]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - android-sdk/kernel-video.git/blob - drivers/net/usb/cdc_mbim.c
Merge branch 'drm-nouveau-fixes-3.8' of git://anongit.freedesktop.org/git/nouveau...
[android-sdk/kernel-video.git] / drivers / net / usb / cdc_mbim.c
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  */
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>
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 };
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;
40         dev_dbg(&dev->intf->dev, "%s() pmcount=%d, on=%d\n", __func__, atomic_read(&info->pmcount), on);
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 }
54 static int cdc_mbim_wdm_manage_power(struct usb_interface *intf, int status)
55 {
56         struct usbnet *dev = usb_get_intfdata(intf);
58         /* can be called while disconnecting */
59         if (!dev)
60                 return 0;
62         return cdc_mbim_manage_power(dev, status);
63 }
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;
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         }
83         /* Probably NCM, defer for cdc_ncm_bind */
84         if (!cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting))
85                 goto err;
87         ret = cdc_ncm_bind_common(dev, intf, data_altsetting);
88         if (ret)
89                 goto err;
91         ctx = info->ctx;
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         }
105         /* can't let usbnet use the interrupt endpoint */
106         dev->status = NULL;
107         info->subdriver = subdriver;
109         /* MBIM cannot do ARP */
110         dev->net->flags |= IFF_NOARP;
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;
118 static void cdc_mbim_unbind(struct usbnet *dev, struct usb_interface *intf)
120         struct cdc_mbim_state *info = (void *)&dev->data;
121         struct cdc_ncm_ctx *ctx = info->ctx;
123         /* disconnect subdriver from control interface */
124         if (info->subdriver && info->subdriver->disconnect)
125                 info->subdriver->disconnect(ctx->control);
126         info->subdriver = NULL;
128         /* let NCM unbind clean up both control and data interface */
129         cdc_ncm_unbind(dev, intf);
133 static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
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;
142         if (!ctx)
143                 goto error;
145         if (skb) {
146                 if (skb->len <= sizeof(ETH_HLEN))
147                         goto error;
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);
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         }
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;
189 error:
190         if (skb)
191                 dev_kfree_skb_any(skb);
193         return NULL;
196 static struct sk_buff *cdc_mbim_process_dgram(struct usbnet *dev, u8 *buf, size_t len, u16 tci)
198         __be16 proto = htons(ETH_P_802_3);
199         struct sk_buff *skb = NULL;
201         if (tci < 256) { /* IPS session? */
202                 if (len < sizeof(struct iphdr))
203                         goto err;
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         }
217         skb = netdev_alloc_skb_ip_align(dev->net,  len + ETH_HLEN);
218         if (!skb)
219                 goto err;
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);
228         /* add datagram */
229         memcpy(skb_put(skb, len), buf, len);
231         /* map MBIM session to VLAN */
232         if (tci)
233                 vlan_put_tag(skb, tci);
234 err:
235         return skb;
238 static int cdc_mbim_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
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;
254         ndpoffset = cdc_ncm_rx_verify_nth16(ctx, skb_in);
255         if (ndpoffset < 0)
256                 goto error;
258 next_ndp:
259         nframes = cdc_ncm_rx_verify_ndp16(skb_in, ndpoffset);
260         if (nframes < 0)
261                 goto error;
263         ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb_in->data + ndpoffset);
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;
280         }
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);
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                 }
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;
318         return 1;
319 error:
320         return 0;
323 static int cdc_mbim_suspend(struct usb_interface *intf, pm_message_t message)
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;
330         if (ctx == NULL) {
331                 ret = -1;
332                 goto error;
333         }
335         ret = usbnet_suspend(intf, message);
336         if (ret < 0)
337                 goto error;
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);
344 error:
345         return ret;
348 static int cdc_mbim_resume(struct usb_interface *intf)
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);
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;
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 };
377 /* MBIM and NCM devices should not need a ZLP after NTBs with
378  * dwNtbOutMaxSize length. This driver_info is for the exceptional
379  * devices requiring it anyway, allowing them to be supported without
380  * forcing the performance penalty on all the sane devices.
381  */
382 static const struct driver_info cdc_mbim_info_zlp = {
383         .description = "CDC MBIM",
384         .flags = FLAG_NO_SETINT | FLAG_MULTI_PACKET | FLAG_WWAN | FLAG_SEND_ZLP,
385         .bind = cdc_mbim_bind,
386         .unbind = cdc_mbim_unbind,
387         .manage_power = cdc_mbim_manage_power,
388         .rx_fixup = cdc_mbim_rx_fixup,
389         .tx_fixup = cdc_mbim_tx_fixup,
390 };
392 static const struct usb_device_id mbim_devs[] = {
393         /* This duplicate NCM entry is intentional. MBIM devices can
394          * be disguised as NCM by default, and this is necessary to
395          * allow us to bind the correct driver_info to such devices.
396          *
397          * bind() will sort out this for us, selecting the correct
398          * entry and reject the other
399          */
400         { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE),
401           .driver_info = (unsigned long)&cdc_mbim_info,
402         },
403         /* Sierra Wireless MC7710 need ZLPs */
404         { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x68a2, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE),
405           .driver_info = (unsigned long)&cdc_mbim_info_zlp,
406         },
407         { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE),
408           .driver_info = (unsigned long)&cdc_mbim_info,
409         },
410         {
411         },
412 };
413 MODULE_DEVICE_TABLE(usb, mbim_devs);
415 static struct usb_driver cdc_mbim_driver = {
416         .name = "cdc_mbim",
417         .id_table = mbim_devs,
418         .probe = usbnet_probe,
419         .disconnect = usbnet_disconnect,
420         .suspend = cdc_mbim_suspend,
421         .resume = cdc_mbim_resume,
422         .reset_resume = cdc_mbim_resume,
423         .supports_autosuspend = 1,
424         .disable_hub_initiated_lpm = 1,
425 };
426 module_usb_driver(cdc_mbim_driver);
428 MODULE_AUTHOR("Greg Suarez <gsuarez@smithmicro.com>");
429 MODULE_AUTHOR("Bjørn Mork <bjorn@mork.no>");
430 MODULE_DESCRIPTION("USB CDC MBIM host driver");
431 MODULE_LICENSE("GPL");