xref: /linux/drivers/net/usb/cdc_mbim.c (revision b889fcf63cb62e7fdb7816565e28f44dbe4a76a5)
1 /*
2  * Copyright (c) 2012  Smith Micro Software, Inc.
3  * Copyright (c) 2012  Bjørn Mork <bjorn@mork.no>
4  *
5  * This driver is based on and reuse most of cdc_ncm, which is
6  * Copyright (C) ST-Ericsson 2010-2012
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * version 2 as published by the Free Software Foundation.
11  */
12 
13 #include <linux/module.h>
14 #include <linux/netdevice.h>
15 #include <linux/ethtool.h>
16 #include <linux/if_vlan.h>
17 #include <linux/ip.h>
18 #include <linux/mii.h>
19 #include <linux/usb.h>
20 #include <linux/usb/cdc.h>
21 #include <linux/usb/usbnet.h>
22 #include <linux/usb/cdc-wdm.h>
23 #include <linux/usb/cdc_ncm.h>
24 
25 /* driver specific data - must match cdc_ncm usage */
26 struct cdc_mbim_state {
27 	struct cdc_ncm_ctx *ctx;
28 	atomic_t pmcount;
29 	struct usb_driver *subdriver;
30 	struct usb_interface *control;
31 	struct usb_interface *data;
32 };
33 
34 /* using a counter to merge subdriver requests with our own into a combined state */
35 static int cdc_mbim_manage_power(struct usbnet *dev, int on)
36 {
37 	struct cdc_mbim_state *info = (void *)&dev->data;
38 	int rv = 0;
39 
40 	dev_dbg(&dev->intf->dev, "%s() pmcount=%d, on=%d\n", __func__, atomic_read(&info->pmcount), on);
41 
42 	if ((on && atomic_add_return(1, &info->pmcount) == 1) || (!on && atomic_dec_and_test(&info->pmcount))) {
43 		/* need autopm_get/put here to ensure the usbcore sees the new value */
44 		rv = usb_autopm_get_interface(dev->intf);
45 		if (rv < 0)
46 			goto err;
47 		dev->intf->needs_remote_wakeup = on;
48 		usb_autopm_put_interface(dev->intf);
49 	}
50 err:
51 	return rv;
52 }
53 
54 static int cdc_mbim_wdm_manage_power(struct usb_interface *intf, int status)
55 {
56 	struct usbnet *dev = usb_get_intfdata(intf);
57 
58 	/* can be called while disconnecting */
59 	if (!dev)
60 		return 0;
61 
62 	return cdc_mbim_manage_power(dev, status);
63 }
64 
65 
66 static int cdc_mbim_bind(struct usbnet *dev, struct usb_interface *intf)
67 {
68 	struct cdc_ncm_ctx *ctx;
69 	struct usb_driver *subdriver = ERR_PTR(-ENODEV);
70 	int ret = -ENODEV;
71 	u8 data_altsetting = CDC_NCM_DATA_ALTSETTING_NCM;
72 	struct cdc_mbim_state *info = (void *)&dev->data;
73 
74 	/* see if interface supports MBIM alternate setting */
75 	if (intf->num_altsetting == 2) {
76 		if (!cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting))
77 			usb_set_interface(dev->udev,
78 					  intf->cur_altsetting->desc.bInterfaceNumber,
79 					  CDC_NCM_COMM_ALTSETTING_MBIM);
80 		data_altsetting = CDC_NCM_DATA_ALTSETTING_MBIM;
81 	}
82 
83 	/* Probably NCM, defer for cdc_ncm_bind */
84 	if (!cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting))
85 		goto err;
86 
87 	ret = cdc_ncm_bind_common(dev, intf, data_altsetting);
88 	if (ret)
89 		goto err;
90 
91 	ctx = info->ctx;
92 
93 	/* The MBIM descriptor and the status endpoint are required */
94 	if (ctx->mbim_desc && dev->status)
95 		subdriver = usb_cdc_wdm_register(ctx->control,
96 						 &dev->status->desc,
97 						 le16_to_cpu(ctx->mbim_desc->wMaxControlMessage),
98 						 cdc_mbim_wdm_manage_power);
99 	if (IS_ERR(subdriver)) {
100 		ret = PTR_ERR(subdriver);
101 		cdc_ncm_unbind(dev, intf);
102 		goto err;
103 	}
104 
105 	/* can't let usbnet use the interrupt endpoint */
106 	dev->status = NULL;
107 	info->subdriver = subdriver;
108 
109 	/* MBIM cannot do ARP */
110 	dev->net->flags |= IFF_NOARP;
111 
112 	/* no need to put the VLAN tci in the packet headers */
113 	dev->net->features |= NETIF_F_HW_VLAN_TX;
114 err:
115 	return ret;
116 }
117 
118 static void cdc_mbim_unbind(struct usbnet *dev, struct usb_interface *intf)
119 {
120 	struct cdc_mbim_state *info = (void *)&dev->data;
121 	struct cdc_ncm_ctx *ctx = info->ctx;
122 
123 	/* disconnect subdriver from control interface */
124 	if (info->subdriver && info->subdriver->disconnect)
125 		info->subdriver->disconnect(ctx->control);
126 	info->subdriver = NULL;
127 
128 	/* let NCM unbind clean up both control and data interface */
129 	cdc_ncm_unbind(dev, intf);
130 }
131 
132 
133 static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
134 {
135 	struct sk_buff *skb_out;
136 	struct cdc_mbim_state *info = (void *)&dev->data;
137 	struct cdc_ncm_ctx *ctx = info->ctx;
138 	__le32 sign = cpu_to_le32(USB_CDC_MBIM_NDP16_IPS_SIGN);
139 	u16 tci = 0;
140 	u8 *c;
141 
142 	if (!ctx)
143 		goto error;
144 
145 	if (skb) {
146 		if (skb->len <= sizeof(ETH_HLEN))
147 			goto error;
148 
149 		/* mapping VLANs to MBIM sessions:
150 		 *   no tag     => IPS session <0>
151 		 *   1 - 255    => IPS session <vlanid>
152 		 *   256 - 511  => DSS session <vlanid - 256>
153 		 *   512 - 4095 => unsupported, drop
154 		 */
155 		vlan_get_tag(skb, &tci);
156 
157 		switch (tci & 0x0f00) {
158 		case 0x0000: /* VLAN ID 0 - 255 */
159 			/* verify that datagram is IPv4 or IPv6 */
160 			skb_reset_mac_header(skb);
161 			switch (eth_hdr(skb)->h_proto) {
162 			case htons(ETH_P_IP):
163 			case htons(ETH_P_IPV6):
164 				break;
165 			default:
166 				goto error;
167 			}
168 			c = (u8 *)&sign;
169 			c[3] = tci;
170 			break;
171 		case 0x0100: /* VLAN ID 256 - 511 */
172 			sign = cpu_to_le32(USB_CDC_MBIM_NDP16_DSS_SIGN);
173 			c = (u8 *)&sign;
174 			c[3] = tci;
175 			break;
176 		default:
177 			netif_err(dev, tx_err, dev->net,
178 				  "unsupported tci=0x%04x\n", tci);
179 			goto error;
180 		}
181 		skb_pull(skb, ETH_HLEN);
182 	}
183 
184 	spin_lock_bh(&ctx->mtx);
185 	skb_out = cdc_ncm_fill_tx_frame(ctx, skb, sign);
186 	spin_unlock_bh(&ctx->mtx);
187 	return skb_out;
188 
189 error:
190 	if (skb)
191 		dev_kfree_skb_any(skb);
192 
193 	return NULL;
194 }
195 
196 static struct sk_buff *cdc_mbim_process_dgram(struct usbnet *dev, u8 *buf, size_t len, u16 tci)
197 {
198 	__be16 proto = htons(ETH_P_802_3);
199 	struct sk_buff *skb = NULL;
200 
201 	if (tci < 256) { /* IPS session? */
202 		if (len < sizeof(struct iphdr))
203 			goto err;
204 
205 		switch (*buf & 0xf0) {
206 		case 0x40:
207 			proto = htons(ETH_P_IP);
208 			break;
209 		case 0x60:
210 			proto = htons(ETH_P_IPV6);
211 			break;
212 		default:
213 			goto err;
214 		}
215 	}
216 
217 	skb = netdev_alloc_skb_ip_align(dev->net,  len + ETH_HLEN);
218 	if (!skb)
219 		goto err;
220 
221 	/* add an ethernet header */
222 	skb_put(skb, ETH_HLEN);
223 	skb_reset_mac_header(skb);
224 	eth_hdr(skb)->h_proto = proto;
225 	memset(eth_hdr(skb)->h_source, 0, ETH_ALEN);
226 	memcpy(eth_hdr(skb)->h_dest, dev->net->dev_addr, ETH_ALEN);
227 
228 	/* add datagram */
229 	memcpy(skb_put(skb, len), buf, len);
230 
231 	/* map MBIM session to VLAN */
232 	if (tci)
233 		vlan_put_tag(skb, tci);
234 err:
235 	return skb;
236 }
237 
238 static int cdc_mbim_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
239 {
240 	struct sk_buff *skb;
241 	struct cdc_mbim_state *info = (void *)&dev->data;
242 	struct cdc_ncm_ctx *ctx = info->ctx;
243 	int len;
244 	int nframes;
245 	int x;
246 	int offset;
247 	struct usb_cdc_ncm_ndp16 *ndp16;
248 	struct usb_cdc_ncm_dpe16 *dpe16;
249 	int ndpoffset;
250 	int loopcount = 50; /* arbitrary max preventing infinite loop */
251 	u8 *c;
252 	u16 tci;
253 
254 	ndpoffset = cdc_ncm_rx_verify_nth16(ctx, skb_in);
255 	if (ndpoffset < 0)
256 		goto error;
257 
258 next_ndp:
259 	nframes = cdc_ncm_rx_verify_ndp16(skb_in, ndpoffset);
260 	if (nframes < 0)
261 		goto error;
262 
263 	ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb_in->data + ndpoffset);
264 
265 	switch (ndp16->dwSignature & cpu_to_le32(0x00ffffff)) {
266 	case cpu_to_le32(USB_CDC_MBIM_NDP16_IPS_SIGN):
267 		c = (u8 *)&ndp16->dwSignature;
268 		tci = c[3];
269 		break;
270 	case cpu_to_le32(USB_CDC_MBIM_NDP16_DSS_SIGN):
271 		c = (u8 *)&ndp16->dwSignature;
272 		tci = c[3] + 256;
273 		break;
274 	default:
275 		netif_dbg(dev, rx_err, dev->net,
276 			  "unsupported NDP signature <0x%08x>\n",
277 			  le32_to_cpu(ndp16->dwSignature));
278 		goto err_ndp;
279 
280 	}
281 
282 	dpe16 = ndp16->dpe16;
283 	for (x = 0; x < nframes; x++, dpe16++) {
284 		offset = le16_to_cpu(dpe16->wDatagramIndex);
285 		len = le16_to_cpu(dpe16->wDatagramLength);
286 
287 		/*
288 		 * CDC NCM ch. 3.7
289 		 * All entries after first NULL entry are to be ignored
290 		 */
291 		if ((offset == 0) || (len == 0)) {
292 			if (!x)
293 				goto err_ndp; /* empty NTB */
294 			break;
295 		}
296 
297 		/* sanity checking */
298 		if (((offset + len) > skb_in->len) || (len > ctx->rx_max)) {
299 			netif_dbg(dev, rx_err, dev->net,
300 				  "invalid frame detected (ignored) offset[%u]=%u, length=%u, skb=%p\n",
301 				  x, offset, len, skb_in);
302 			if (!x)
303 				goto err_ndp;
304 			break;
305 		} else {
306 			skb = cdc_mbim_process_dgram(dev, skb_in->data + offset, len, tci);
307 			if (!skb)
308 				goto error;
309 			usbnet_skb_return(dev, skb);
310 		}
311 	}
312 err_ndp:
313 	/* are there more NDPs to process? */
314 	ndpoffset = le16_to_cpu(ndp16->wNextNdpIndex);
315 	if (ndpoffset && loopcount--)
316 		goto next_ndp;
317 
318 	return 1;
319 error:
320 	return 0;
321 }
322 
323 static int cdc_mbim_suspend(struct usb_interface *intf, pm_message_t message)
324 {
325 	int ret = 0;
326 	struct usbnet *dev = usb_get_intfdata(intf);
327 	struct cdc_mbim_state *info = (void *)&dev->data;
328 	struct cdc_ncm_ctx *ctx = info->ctx;
329 
330 	if (ctx == NULL) {
331 		ret = -1;
332 		goto error;
333 	}
334 
335 	ret = usbnet_suspend(intf, message);
336 	if (ret < 0)
337 		goto error;
338 
339 	if (intf == ctx->control && info->subdriver && info->subdriver->suspend)
340 		ret = info->subdriver->suspend(intf, message);
341 	if (ret < 0)
342 		usbnet_resume(intf);
343 
344 error:
345 	return ret;
346 }
347 
348 static int cdc_mbim_resume(struct usb_interface *intf)
349 {
350 	int  ret = 0;
351 	struct usbnet *dev = usb_get_intfdata(intf);
352 	struct cdc_mbim_state *info = (void *)&dev->data;
353 	struct cdc_ncm_ctx *ctx = info->ctx;
354 	bool callsub = (intf == ctx->control && info->subdriver && info->subdriver->resume);
355 
356 	if (callsub)
357 		ret = info->subdriver->resume(intf);
358 	if (ret < 0)
359 		goto err;
360 	ret = usbnet_resume(intf);
361 	if (ret < 0 && callsub && info->subdriver->suspend)
362 		info->subdriver->suspend(intf, PMSG_SUSPEND);
363 err:
364 	return ret;
365 }
366 
367 static const struct driver_info cdc_mbim_info = {
368 	.description = "CDC MBIM",
369 	.flags = FLAG_NO_SETINT | FLAG_MULTI_PACKET | FLAG_WWAN,
370 	.bind = cdc_mbim_bind,
371 	.unbind = cdc_mbim_unbind,
372 	.manage_power = cdc_mbim_manage_power,
373 	.rx_fixup = cdc_mbim_rx_fixup,
374 	.tx_fixup = cdc_mbim_tx_fixup,
375 };
376 
377 static const struct usb_device_id mbim_devs[] = {
378 	/* This duplicate NCM entry is intentional. MBIM devices can
379 	 * be disguised as NCM by default, and this is necessary to
380 	 * allow us to bind the correct driver_info to such devices.
381 	 *
382 	 * bind() will sort out this for us, selecting the correct
383 	 * entry and reject the other
384 	 */
385 	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE),
386 	  .driver_info = (unsigned long)&cdc_mbim_info,
387 	},
388 	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE),
389 	  .driver_info = (unsigned long)&cdc_mbim_info,
390 	},
391 	{
392 	},
393 };
394 MODULE_DEVICE_TABLE(usb, mbim_devs);
395 
396 static struct usb_driver cdc_mbim_driver = {
397 	.name = "cdc_mbim",
398 	.id_table = mbim_devs,
399 	.probe = usbnet_probe,
400 	.disconnect = usbnet_disconnect,
401 	.suspend = cdc_mbim_suspend,
402 	.resume = cdc_mbim_resume,
403 	.reset_resume =	cdc_mbim_resume,
404 	.supports_autosuspend = 1,
405 	.disable_hub_initiated_lpm = 1,
406 };
407 module_usb_driver(cdc_mbim_driver);
408 
409 MODULE_AUTHOR("Greg Suarez <gsuarez@smithmicro.com>");
410 MODULE_AUTHOR("Bjørn Mork <bjorn@mork.no>");
411 MODULE_DESCRIPTION("USB CDC MBIM host driver");
412 MODULE_LICENSE("GPL");
413