aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/net/usb/Kconfig15
-rw-r--r--drivers/net/usb/Makefile1
-rw-r--r--drivers/net/usb/cdc_ncm.c17
-rw-r--r--drivers/net/usb/huawei_cdc_ncm.c230
-rw-r--r--include/linux/usb/cdc_ncm.h3
5 files changed, 253 insertions, 13 deletions
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index 40db31233313..85e4a01670f0 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -242,6 +242,21 @@ config USB_NET_CDC_NCM
242 * ST-Ericsson M343 HSPA Mobile Broadband Modem (reference design) 242 * ST-Ericsson M343 HSPA Mobile Broadband Modem (reference design)
243 * Ericsson F5521gw Mobile Broadband Module 243 * Ericsson F5521gw Mobile Broadband Module
244 244
245config USB_NET_HUAWEI_CDC_NCM
246 tristate "Huawei NCM embedded AT channel support"
247 depends on USB_USBNET
248 select USB_WDM
249 select USB_NET_CDC_NCM
250 help
251 This driver supports huawei-style NCM devices, that use NCM as a
252 transport for other protocols, usually an embedded AT channel.
253 Good examples are:
254 * Huawei E3131
255 * Huawei E3251
256
257 To compile this driver as a module, choose M here: the module will be
258 called huawei_cdc_ncm.ko.
259
245config USB_NET_CDC_MBIM 260config USB_NET_CDC_MBIM
246 tristate "CDC MBIM support" 261 tristate "CDC MBIM support"
247 depends on USB_USBNET 262 depends on USB_USBNET
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index 8b342cf992fd..b17b5e88bbaf 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_USB_IPHETH) += ipheth.o
32obj-$(CONFIG_USB_SIERRA_NET) += sierra_net.o 32obj-$(CONFIG_USB_SIERRA_NET) += sierra_net.o
33obj-$(CONFIG_USB_NET_CX82310_ETH) += cx82310_eth.o 33obj-$(CONFIG_USB_NET_CX82310_ETH) += cx82310_eth.o
34obj-$(CONFIG_USB_NET_CDC_NCM) += cdc_ncm.o 34obj-$(CONFIG_USB_NET_CDC_NCM) += cdc_ncm.o
35obj-$(CONFIG_USB_NET_HUAWEI_CDC_NCM) += huawei_cdc_ncm.o
35obj-$(CONFIG_USB_VL600) += lg-vl600.o 36obj-$(CONFIG_USB_VL600) += lg-vl600.o
36obj-$(CONFIG_USB_NET_QMI_WWAN) += qmi_wwan.o 37obj-$(CONFIG_USB_NET_QMI_WWAN) += qmi_wwan.o
37obj-$(CONFIG_USB_NET_CDC_MBIM) += cdc_mbim.o 38obj-$(CONFIG_USB_NET_CDC_MBIM) += cdc_mbim.o
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index 11c703337577..f74786aa37be 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -830,7 +830,7 @@ static void cdc_ncm_txpath_bh(unsigned long param)
830 } 830 }
831} 831}
832 832
833static struct sk_buff * 833struct sk_buff *
834cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) 834cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
835{ 835{
836 struct sk_buff *skb_out; 836 struct sk_buff *skb_out;
@@ -857,6 +857,7 @@ error:
857 857
858 return NULL; 858 return NULL;
859} 859}
860EXPORT_SYMBOL_GPL(cdc_ncm_tx_fixup);
860 861
861/* verify NTB header and return offset of first NDP, or negative error */ 862/* verify NTB header and return offset of first NDP, or negative error */
862int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in) 863int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in)
@@ -943,7 +944,7 @@ error:
943} 944}
944EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_ndp16); 945EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_ndp16);
945 946
946static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) 947int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
947{ 948{
948 struct sk_buff *skb; 949 struct sk_buff *skb;
949 struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; 950 struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
@@ -1019,6 +1020,7 @@ err_ndp:
1019error: 1020error:
1020 return 0; 1021 return 0;
1021} 1022}
1023EXPORT_SYMBOL_GPL(cdc_ncm_rx_fixup);
1022 1024
1023static void 1025static void
1024cdc_ncm_speed_change(struct usbnet *dev, 1026cdc_ncm_speed_change(struct usbnet *dev,
@@ -1184,17 +1186,6 @@ static const struct usb_device_id cdc_devs[] = {
1184 .driver_info = (unsigned long)&wwan_info, 1186 .driver_info = (unsigned long)&wwan_info,
1185 }, 1187 },
1186 1188
1187 /* Huawei NCM devices disguised as vendor specific */
1188 { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x16),
1189 .driver_info = (unsigned long)&wwan_info,
1190 },
1191 { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x46),
1192 .driver_info = (unsigned long)&wwan_info,
1193 },
1194 { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x76),
1195 .driver_info = (unsigned long)&wwan_info,
1196 },
1197
1198 /* Infineon(now Intel) HSPA Modem platform */ 1189 /* Infineon(now Intel) HSPA Modem platform */
1199 { USB_DEVICE_AND_INTERFACE_INFO(0x1519, 0x0443, 1190 { USB_DEVICE_AND_INTERFACE_INFO(0x1519, 0x0443,
1200 USB_CLASS_COMM, 1191 USB_CLASS_COMM,
diff --git a/drivers/net/usb/huawei_cdc_ncm.c b/drivers/net/usb/huawei_cdc_ncm.c
new file mode 100644
index 000000000000..312178d7b698
--- /dev/null
+++ b/drivers/net/usb/huawei_cdc_ncm.c
@@ -0,0 +1,230 @@
1/* huawei_cdc_ncm.c - handles Huawei devices using the CDC NCM protocol as
2 * transport layer.
3 * Copyright (C) 2013 Enrico Mioso <mrkiko.rs@gmail.com>
4 *
5 *
6 * ABSTRACT:
7 * This driver handles devices resembling the CDC NCM standard, but
8 * encapsulating another protocol inside it. An example are some Huawei 3G
9 * devices, exposing an embedded AT channel where you can set up the NCM
10 * connection.
11 * This code has been heavily inspired by the cdc_mbim.c driver, which is
12 * Copyright (c) 2012 Smith Micro Software, Inc.
13 * Copyright (c) 2012 Bjørn Mork <bjorn@mork.no>
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * version 2 as published by the Free Software Foundation.
18 */
19
20#include <linux/module.h>
21#include <linux/netdevice.h>
22#include <linux/ethtool.h>
23#include <linux/if_vlan.h>
24#include <linux/ip.h>
25#include <linux/mii.h>
26#include <linux/usb.h>
27#include <linux/usb/cdc.h>
28#include <linux/usb/usbnet.h>
29#include <linux/usb/cdc-wdm.h>
30#include <linux/usb/cdc_ncm.h>
31
32/* Driver data */
33struct huawei_cdc_ncm_state {
34 struct cdc_ncm_ctx *ctx;
35 atomic_t pmcount;
36 struct usb_driver *subdriver;
37 struct usb_interface *control;
38 struct usb_interface *data;
39};
40
41static int huawei_cdc_ncm_manage_power(struct usbnet *usbnet_dev, int on)
42{
43 struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data;
44 int rv;
45
46 if ((on && atomic_add_return(1, &drvstate->pmcount) == 1) ||
47 (!on && atomic_dec_and_test(&drvstate->pmcount))) {
48 rv = usb_autopm_get_interface(usbnet_dev->intf);
49 usbnet_dev->intf->needs_remote_wakeup = on;
50 if (!rv)
51 usb_autopm_put_interface(usbnet_dev->intf);
52 }
53 return 0;
54}
55
56static int huawei_cdc_ncm_wdm_manage_power(struct usb_interface *intf,
57 int status)
58{
59 struct usbnet *usbnet_dev = usb_get_intfdata(intf);
60
61 /* can be called while disconnecting */
62 if (!usbnet_dev)
63 return 0;
64
65 return huawei_cdc_ncm_manage_power(usbnet_dev, status);
66}
67
68
69static int huawei_cdc_ncm_bind(struct usbnet *usbnet_dev,
70 struct usb_interface *intf)
71{
72 struct cdc_ncm_ctx *ctx;
73 struct usb_driver *subdriver = ERR_PTR(-ENODEV);
74 int ret = -ENODEV;
75 struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data;
76
77 /* altsetting should always be 1 for NCM devices - so we hard-coded
78 * it here
79 */
80 ret = cdc_ncm_bind_common(usbnet_dev, intf, 1);
81 if (ret)
82 goto err;
83
84 ctx = drvstate->ctx;
85
86 if (usbnet_dev->status)
87 /* CDC-WMC r1.1 requires wMaxCommand to be "at least 256
88 * decimal (0x100)"
89 */
90 subdriver = usb_cdc_wdm_register(ctx->control,
91 &usbnet_dev->status->desc,
92 256, /* wMaxCommand */
93 huawei_cdc_ncm_wdm_manage_power);
94 if (IS_ERR(subdriver)) {
95 ret = PTR_ERR(subdriver);
96 cdc_ncm_unbind(usbnet_dev, intf);
97 goto err;
98 }
99
100 /* Prevent usbnet from using the status descriptor */
101 usbnet_dev->status = NULL;
102
103 drvstate->subdriver = subdriver;
104
105err:
106 return ret;
107}
108
109static void huawei_cdc_ncm_unbind(struct usbnet *usbnet_dev,
110 struct usb_interface *intf)
111{
112 struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data;
113 struct cdc_ncm_ctx *ctx = drvstate->ctx;
114
115 if (drvstate->subdriver && drvstate->subdriver->disconnect)
116 drvstate->subdriver->disconnect(ctx->control);
117 drvstate->subdriver = NULL;
118
119 cdc_ncm_unbind(usbnet_dev, intf);
120}
121
122static int huawei_cdc_ncm_suspend(struct usb_interface *intf,
123 pm_message_t message)
124{
125 int ret = 0;
126 struct usbnet *usbnet_dev = usb_get_intfdata(intf);
127 struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data;
128 struct cdc_ncm_ctx *ctx = drvstate->ctx;
129
130 if (ctx == NULL) {
131 ret = -ENODEV;
132 goto error;
133 }
134
135 ret = usbnet_suspend(intf, message);
136 if (ret < 0)
137 goto error;
138
139 if (intf == ctx->control &&
140 drvstate->subdriver &&
141 drvstate->subdriver->suspend)
142 ret = drvstate->subdriver->suspend(intf, message);
143 if (ret < 0)
144 usbnet_resume(intf);
145
146error:
147 return ret;
148}
149
150static int huawei_cdc_ncm_resume(struct usb_interface *intf)
151{
152 int ret = 0;
153 struct usbnet *usbnet_dev = usb_get_intfdata(intf);
154 struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data;
155 bool callsub;
156 struct cdc_ncm_ctx *ctx = drvstate->ctx;
157
158 /* should we call subdriver's resume function? */
159 callsub =
160 (intf == ctx->control &&
161 drvstate->subdriver &&
162 drvstate->subdriver->resume);
163
164 if (callsub)
165 ret = drvstate->subdriver->resume(intf);
166 if (ret < 0)
167 goto err;
168 ret = usbnet_resume(intf);
169 if (ret < 0 && callsub)
170 drvstate->subdriver->suspend(intf, PMSG_SUSPEND);
171err:
172 return ret;
173}
174
175static int huawei_cdc_ncm_check_connect(struct usbnet *usbnet_dev)
176{
177 struct cdc_ncm_ctx *ctx;
178
179 ctx = (struct cdc_ncm_ctx *)usbnet_dev->data[0];
180
181 if (ctx == NULL)
182 return 1; /* disconnected */
183
184 return !ctx->connected;
185}
186
187static const struct driver_info huawei_cdc_ncm_info = {
188 .description = "Huawei CDC NCM device",
189 .flags = FLAG_NO_SETINT | FLAG_MULTI_PACKET | FLAG_WWAN,
190 .bind = huawei_cdc_ncm_bind,
191 .unbind = huawei_cdc_ncm_unbind,
192 .check_connect = huawei_cdc_ncm_check_connect,
193 .manage_power = huawei_cdc_ncm_manage_power,
194 .rx_fixup = cdc_ncm_rx_fixup,
195 .tx_fixup = cdc_ncm_tx_fixup,
196};
197
198static const struct usb_device_id huawei_cdc_ncm_devs[] = {
199 /* Huawei NCM devices disguised as vendor specific */
200 { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x16),
201 .driver_info = (unsigned long)&huawei_cdc_ncm_info,
202 },
203 { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x46),
204 .driver_info = (unsigned long)&huawei_cdc_ncm_info,
205 },
206 { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x76),
207 .driver_info = (unsigned long)&huawei_cdc_ncm_info,
208 },
209
210 /* Terminating entry */
211 {
212 },
213};
214MODULE_DEVICE_TABLE(usb, huawei_cdc_ncm_devs);
215
216static struct usb_driver huawei_cdc_ncm_driver = {
217 .name = "huawei_cdc_ncm",
218 .id_table = huawei_cdc_ncm_devs,
219 .probe = usbnet_probe,
220 .disconnect = usbnet_disconnect,
221 .suspend = huawei_cdc_ncm_suspend,
222 .resume = huawei_cdc_ncm_resume,
223 .reset_resume = huawei_cdc_ncm_resume,
224 .supports_autosuspend = 1,
225 .disable_hub_initiated_lpm = 1,
226};
227module_usb_driver(huawei_cdc_ncm_driver);
228MODULE_AUTHOR("Enrico Mioso <mrkiko.rs@gmail.com>");
229MODULE_DESCRIPTION("USB CDC NCM host driver with encapsulated protocol support");
230MODULE_LICENSE("GPL");
diff --git a/include/linux/usb/cdc_ncm.h b/include/linux/usb/cdc_ncm.h
index 2300f7492927..c3fa80745996 100644
--- a/include/linux/usb/cdc_ncm.h
+++ b/include/linux/usb/cdc_ncm.h
@@ -125,5 +125,8 @@ void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf);
125struct sk_buff *cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign); 125struct sk_buff *cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign);
126int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in); 126int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in);
127int cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset); 127int cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset);
128struct sk_buff *
129cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags);
130int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in);
128 131
129#endif /* __LINUX_USB_CDC_NCM_H */ 132#endif /* __LINUX_USB_CDC_NCM_H */