xref: /openbmc/linux/drivers/net/usb/cdc_ncm.c (revision 634fef61)
1900d495aSAlexey Orishko /*
2900d495aSAlexey Orishko  * cdc_ncm.c
3900d495aSAlexey Orishko  *
4c84ff1d6SAlexey Orishko  * Copyright (C) ST-Ericsson 2010-2012
5900d495aSAlexey Orishko  * Contact: Alexey Orishko <alexey.orishko@stericsson.com>
6900d495aSAlexey Orishko  * Original author: Hans Petter Selasky <hans.petter.selasky@stericsson.com>
7900d495aSAlexey Orishko  *
8900d495aSAlexey Orishko  * USB Host Driver for Network Control Model (NCM)
922401ff1SEnrico Mioso  * http://www.usb.org/developers/docs/devclass_docs/NCM10_012011.zip
10900d495aSAlexey Orishko  *
11900d495aSAlexey Orishko  * The NCM encoding, decoding and initialization logic
12900d495aSAlexey Orishko  * derives from FreeBSD 8.x. if_cdce.c and if_cdcereg.h
13900d495aSAlexey Orishko  *
14900d495aSAlexey Orishko  * This software is available to you under a choice of one of two
15900d495aSAlexey Orishko  * licenses. You may choose this file to be licensed under the terms
16900d495aSAlexey Orishko  * of the GNU General Public License (GPL) Version 2 or the 2-clause
17900d495aSAlexey Orishko  * BSD license listed below:
18900d495aSAlexey Orishko  *
19900d495aSAlexey Orishko  * Redistribution and use in source and binary forms, with or without
20900d495aSAlexey Orishko  * modification, are permitted provided that the following conditions
21900d495aSAlexey Orishko  * are met:
22900d495aSAlexey Orishko  * 1. Redistributions of source code must retain the above copyright
23900d495aSAlexey Orishko  *    notice, this list of conditions and the following disclaimer.
24900d495aSAlexey Orishko  * 2. Redistributions in binary form must reproduce the above copyright
25900d495aSAlexey Orishko  *    notice, this list of conditions and the following disclaimer in the
26900d495aSAlexey Orishko  *    documentation and/or other materials provided with the distribution.
27900d495aSAlexey Orishko  *
28900d495aSAlexey Orishko  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
29900d495aSAlexey Orishko  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30900d495aSAlexey Orishko  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31900d495aSAlexey Orishko  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
32900d495aSAlexey Orishko  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33900d495aSAlexey Orishko  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34900d495aSAlexey Orishko  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35900d495aSAlexey Orishko  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36900d495aSAlexey Orishko  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37900d495aSAlexey Orishko  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38900d495aSAlexey Orishko  * SUCH DAMAGE.
39900d495aSAlexey Orishko  */
40900d495aSAlexey Orishko 
41900d495aSAlexey Orishko #include <linux/module.h>
42900d495aSAlexey Orishko #include <linux/netdevice.h>
43900d495aSAlexey Orishko #include <linux/ctype.h>
441dfddff5SBjørn Mork #include <linux/etherdevice.h>
45900d495aSAlexey Orishko #include <linux/ethtool.h>
46900d495aSAlexey Orishko #include <linux/workqueue.h>
47900d495aSAlexey Orishko #include <linux/mii.h>
48900d495aSAlexey Orishko #include <linux/crc32.h>
49900d495aSAlexey Orishko #include <linux/usb.h>
50c84ff1d6SAlexey Orishko #include <linux/hrtimer.h>
51900d495aSAlexey Orishko #include <linux/atomic.h>
52900d495aSAlexey Orishko #include <linux/usb/usbnet.h>
53900d495aSAlexey Orishko #include <linux/usb/cdc.h>
54c91ce3b6SBjørn Mork #include <linux/usb/cdc_ncm.h>
55900d495aSAlexey Orishko 
561e8bbe6cSBjørn Mork #if IS_ENABLED(CONFIG_USB_NET_CDC_MBIM)
571e8bbe6cSBjørn Mork static bool prefer_mbim = true;
581e8bbe6cSBjørn Mork #else
591e8bbe6cSBjørn Mork static bool prefer_mbim;
601e8bbe6cSBjørn Mork #endif
611e8bbe6cSBjørn Mork module_param(prefer_mbim, bool, S_IRUGO | S_IWUSR);
621e8bbe6cSBjørn Mork MODULE_PARM_DESC(prefer_mbim, "Prefer MBIM setting on dual NCM/MBIM functions");
631e8bbe6cSBjørn Mork 
64c84ff1d6SAlexey Orishko static void cdc_ncm_txpath_bh(unsigned long param);
65c84ff1d6SAlexey Orishko static void cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx);
66c84ff1d6SAlexey Orishko static enum hrtimer_restart cdc_ncm_tx_timer_cb(struct hrtimer *hr_timer);
67900d495aSAlexey Orishko static struct usb_driver cdc_ncm_driver;
68900d495aSAlexey Orishko 
69beeecd42SBjørn Mork struct cdc_ncm_stats {
70beeecd42SBjørn Mork 	char stat_string[ETH_GSTRING_LEN];
71beeecd42SBjørn Mork 	int sizeof_stat;
72beeecd42SBjørn Mork 	int stat_offset;
73beeecd42SBjørn Mork };
74beeecd42SBjørn Mork 
75beeecd42SBjørn Mork #define CDC_NCM_STAT(str, m) { \
76beeecd42SBjørn Mork 		.stat_string = str, \
77beeecd42SBjørn Mork 		.sizeof_stat = sizeof(((struct cdc_ncm_ctx *)0)->m), \
78beeecd42SBjørn Mork 		.stat_offset = offsetof(struct cdc_ncm_ctx, m) }
79beeecd42SBjørn Mork #define CDC_NCM_SIMPLE_STAT(m)	CDC_NCM_STAT(__stringify(m), m)
80beeecd42SBjørn Mork 
81beeecd42SBjørn Mork static const struct cdc_ncm_stats cdc_ncm_gstrings_stats[] = {
82beeecd42SBjørn Mork 	CDC_NCM_SIMPLE_STAT(tx_reason_ntb_full),
83beeecd42SBjørn Mork 	CDC_NCM_SIMPLE_STAT(tx_reason_ndp_full),
84beeecd42SBjørn Mork 	CDC_NCM_SIMPLE_STAT(tx_reason_timeout),
85beeecd42SBjørn Mork 	CDC_NCM_SIMPLE_STAT(tx_reason_max_datagram),
86beeecd42SBjørn Mork 	CDC_NCM_SIMPLE_STAT(tx_overhead),
87beeecd42SBjørn Mork 	CDC_NCM_SIMPLE_STAT(tx_ntbs),
88beeecd42SBjørn Mork 	CDC_NCM_SIMPLE_STAT(rx_overhead),
89beeecd42SBjørn Mork 	CDC_NCM_SIMPLE_STAT(rx_ntbs),
90beeecd42SBjørn Mork };
91beeecd42SBjørn Mork 
92beeecd42SBjørn Mork static int cdc_ncm_get_sset_count(struct net_device __always_unused *netdev, int sset)
93beeecd42SBjørn Mork {
94beeecd42SBjørn Mork 	switch (sset) {
95beeecd42SBjørn Mork 	case ETH_SS_STATS:
96beeecd42SBjørn Mork 		return ARRAY_SIZE(cdc_ncm_gstrings_stats);
97beeecd42SBjørn Mork 	default:
98beeecd42SBjørn Mork 		return -EOPNOTSUPP;
99beeecd42SBjørn Mork 	}
100beeecd42SBjørn Mork }
101beeecd42SBjørn Mork 
102beeecd42SBjørn Mork static void cdc_ncm_get_ethtool_stats(struct net_device *netdev,
103beeecd42SBjørn Mork 				    struct ethtool_stats __always_unused *stats,
104beeecd42SBjørn Mork 				    u64 *data)
105beeecd42SBjørn Mork {
106beeecd42SBjørn Mork 	struct usbnet *dev = netdev_priv(netdev);
107beeecd42SBjørn Mork 	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
108beeecd42SBjørn Mork 	int i;
109beeecd42SBjørn Mork 	char *p = NULL;
110beeecd42SBjørn Mork 
111beeecd42SBjørn Mork 	for (i = 0; i < ARRAY_SIZE(cdc_ncm_gstrings_stats); i++) {
112beeecd42SBjørn Mork 		p = (char *)ctx + cdc_ncm_gstrings_stats[i].stat_offset;
113beeecd42SBjørn Mork 		data[i] = (cdc_ncm_gstrings_stats[i].sizeof_stat == sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
114beeecd42SBjørn Mork 	}
115beeecd42SBjørn Mork }
116beeecd42SBjørn Mork 
117beeecd42SBjørn Mork static void cdc_ncm_get_strings(struct net_device __always_unused *netdev, u32 stringset, u8 *data)
118beeecd42SBjørn Mork {
119beeecd42SBjørn Mork 	u8 *p = data;
120beeecd42SBjørn Mork 	int i;
121beeecd42SBjørn Mork 
122beeecd42SBjørn Mork 	switch (stringset) {
123beeecd42SBjørn Mork 	case ETH_SS_STATS:
124beeecd42SBjørn Mork 		for (i = 0; i < ARRAY_SIZE(cdc_ncm_gstrings_stats); i++) {
125beeecd42SBjørn Mork 			memcpy(p, cdc_ncm_gstrings_stats[i].stat_string, ETH_GSTRING_LEN);
126beeecd42SBjørn Mork 			p += ETH_GSTRING_LEN;
127beeecd42SBjørn Mork 		}
128beeecd42SBjørn Mork 	}
129beeecd42SBjørn Mork }
130beeecd42SBjørn Mork 
1316c4e548fSBjørn Mork static void cdc_ncm_update_rxtx_max(struct usbnet *dev, u32 new_rx, u32 new_tx);
1326c4e548fSBjørn Mork 
1336c4e548fSBjørn Mork static const struct ethtool_ops cdc_ncm_ethtool_ops = {
1346c4e548fSBjørn Mork 	.get_link          = usbnet_get_link,
1356c4e548fSBjørn Mork 	.nway_reset        = usbnet_nway_reset,
1366c4e548fSBjørn Mork 	.get_drvinfo       = usbnet_get_drvinfo,
1376c4e548fSBjørn Mork 	.get_msglevel      = usbnet_get_msglevel,
1386c4e548fSBjørn Mork 	.set_msglevel      = usbnet_set_msglevel,
1396c4e548fSBjørn Mork 	.get_ts_info       = ethtool_op_get_ts_info,
140beeecd42SBjørn Mork 	.get_sset_count    = cdc_ncm_get_sset_count,
141beeecd42SBjørn Mork 	.get_strings       = cdc_ncm_get_strings,
142beeecd42SBjørn Mork 	.get_ethtool_stats = cdc_ncm_get_ethtool_stats,
143d0b3ab30SPhilippe Reynes 	.get_link_ksettings      = usbnet_get_link_ksettings,
144d0b3ab30SPhilippe Reynes 	.set_link_ksettings      = usbnet_set_link_ksettings,
1456c4e548fSBjørn Mork };
1466c4e548fSBjørn Mork 
147289507d3SBjørn Mork static u32 cdc_ncm_check_rx_max(struct usbnet *dev, u32 new_rx)
1485aa73d5dSBjørn Mork {
1495aa73d5dSBjørn Mork 	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
1505aa73d5dSBjørn Mork 	u32 val, max, min;
1515aa73d5dSBjørn Mork 
1525aa73d5dSBjørn Mork 	/* clamp new_rx to sane values */
1535aa73d5dSBjørn Mork 	min = USB_CDC_NCM_NTB_MIN_IN_SIZE;
1545aa73d5dSBjørn Mork 	max = min_t(u32, CDC_NCM_NTB_MAX_SIZE_RX, le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize));
1555aa73d5dSBjørn Mork 
1565aa73d5dSBjørn Mork 	/* dwNtbInMaxSize spec violation? Use MIN size for both limits */
1575aa73d5dSBjørn Mork 	if (max < min) {
1585aa73d5dSBjørn Mork 		dev_warn(&dev->intf->dev, "dwNtbInMaxSize=%u is too small. Using %u\n",
1595aa73d5dSBjørn Mork 			 le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize), min);
1605aa73d5dSBjørn Mork 		max = min;
1615aa73d5dSBjørn Mork 	}
1625aa73d5dSBjørn Mork 
1635aa73d5dSBjørn Mork 	val = clamp_t(u32, new_rx, min, max);
164289507d3SBjørn Mork 	if (val != new_rx)
165289507d3SBjørn Mork 		dev_dbg(&dev->intf->dev, "rx_max must be in the [%u, %u] range\n", min, max);
166289507d3SBjørn Mork 
167289507d3SBjørn Mork 	return val;
1685aa73d5dSBjørn Mork }
1695aa73d5dSBjørn Mork 
170289507d3SBjørn Mork static u32 cdc_ncm_check_tx_max(struct usbnet *dev, u32 new_tx)
171289507d3SBjørn Mork {
172289507d3SBjørn Mork 	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
173289507d3SBjørn Mork 	u32 val, max, min;
174289507d3SBjørn Mork 
175289507d3SBjørn Mork 	/* clamp new_tx to sane values */
176289507d3SBjørn Mork 	min = ctx->max_datagram_size + ctx->max_ndp_size + sizeof(struct usb_cdc_ncm_nth16);
177289507d3SBjørn Mork 	max = min_t(u32, CDC_NCM_NTB_MAX_SIZE_TX, le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize));
178289507d3SBjørn Mork 
179289507d3SBjørn Mork 	/* some devices set dwNtbOutMaxSize too low for the above default */
180289507d3SBjørn Mork 	min = min(min, max);
181289507d3SBjørn Mork 
182289507d3SBjørn Mork 	val = clamp_t(u32, new_tx, min, max);
183289507d3SBjørn Mork 	if (val != new_tx)
184289507d3SBjørn Mork 		dev_dbg(&dev->intf->dev, "tx_max must be in the [%u, %u] range\n", min, max);
185289507d3SBjørn Mork 
186289507d3SBjørn Mork 	return val;
187289507d3SBjørn Mork }
188289507d3SBjørn Mork 
18939eb7e0eSBjørn Mork static ssize_t cdc_ncm_show_min_tx_pkt(struct device *d, struct device_attribute *attr, char *buf)
19039eb7e0eSBjørn Mork {
19139eb7e0eSBjørn Mork 	struct usbnet *dev = netdev_priv(to_net_dev(d));
19239eb7e0eSBjørn Mork 	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
19339eb7e0eSBjørn Mork 
19439eb7e0eSBjørn Mork 	return sprintf(buf, "%u\n", ctx->min_tx_pkt);
19539eb7e0eSBjørn Mork }
19639eb7e0eSBjørn Mork 
197289507d3SBjørn Mork static ssize_t cdc_ncm_show_rx_max(struct device *d, struct device_attribute *attr, char *buf)
198289507d3SBjørn Mork {
199289507d3SBjørn Mork 	struct usbnet *dev = netdev_priv(to_net_dev(d));
200289507d3SBjørn Mork 	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
201289507d3SBjørn Mork 
202289507d3SBjørn Mork 	return sprintf(buf, "%u\n", ctx->rx_max);
203289507d3SBjørn Mork }
204289507d3SBjørn Mork 
205289507d3SBjørn Mork static ssize_t cdc_ncm_show_tx_max(struct device *d, struct device_attribute *attr, char *buf)
206289507d3SBjørn Mork {
207289507d3SBjørn Mork 	struct usbnet *dev = netdev_priv(to_net_dev(d));
208289507d3SBjørn Mork 	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
209289507d3SBjørn Mork 
210289507d3SBjørn Mork 	return sprintf(buf, "%u\n", ctx->tx_max);
211289507d3SBjørn Mork }
212289507d3SBjørn Mork 
213289507d3SBjørn Mork static ssize_t cdc_ncm_show_tx_timer_usecs(struct device *d, struct device_attribute *attr, char *buf)
214289507d3SBjørn Mork {
215289507d3SBjørn Mork 	struct usbnet *dev = netdev_priv(to_net_dev(d));
216289507d3SBjørn Mork 	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
217289507d3SBjørn Mork 
218289507d3SBjørn Mork 	return sprintf(buf, "%u\n", ctx->timer_interval / (u32)NSEC_PER_USEC);
219289507d3SBjørn Mork }
220289507d3SBjørn Mork 
22139eb7e0eSBjørn Mork static ssize_t cdc_ncm_store_min_tx_pkt(struct device *d,  struct device_attribute *attr, const char *buf, size_t len)
22239eb7e0eSBjørn Mork {
22339eb7e0eSBjørn Mork 	struct usbnet *dev = netdev_priv(to_net_dev(d));
22439eb7e0eSBjørn Mork 	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
22539eb7e0eSBjørn Mork 	unsigned long val;
22639eb7e0eSBjørn Mork 
22739eb7e0eSBjørn Mork 	/* no need to restrict values - anything from 0 to infinity is OK */
22839eb7e0eSBjørn Mork 	if (kstrtoul(buf, 0, &val))
22939eb7e0eSBjørn Mork 		return -EINVAL;
23039eb7e0eSBjørn Mork 
23139eb7e0eSBjørn Mork 	ctx->min_tx_pkt = val;
23239eb7e0eSBjørn Mork 	return len;
23339eb7e0eSBjørn Mork }
23439eb7e0eSBjørn Mork 
235289507d3SBjørn Mork static ssize_t cdc_ncm_store_rx_max(struct device *d,  struct device_attribute *attr, const char *buf, size_t len)
236289507d3SBjørn Mork {
237289507d3SBjørn Mork 	struct usbnet *dev = netdev_priv(to_net_dev(d));
238289507d3SBjørn Mork 	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
239289507d3SBjørn Mork 	unsigned long val;
240289507d3SBjørn Mork 
241289507d3SBjørn Mork 	if (kstrtoul(buf, 0, &val) || cdc_ncm_check_rx_max(dev, val) != val)
242289507d3SBjørn Mork 		return -EINVAL;
243289507d3SBjørn Mork 
244289507d3SBjørn Mork 	cdc_ncm_update_rxtx_max(dev, val, ctx->tx_max);
245289507d3SBjørn Mork 	return len;
246289507d3SBjørn Mork }
247289507d3SBjørn Mork 
248289507d3SBjørn Mork static ssize_t cdc_ncm_store_tx_max(struct device *d,  struct device_attribute *attr, const char *buf, size_t len)
249289507d3SBjørn Mork {
250289507d3SBjørn Mork 	struct usbnet *dev = netdev_priv(to_net_dev(d));
251289507d3SBjørn Mork 	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
252289507d3SBjørn Mork 	unsigned long val;
253289507d3SBjørn Mork 
254289507d3SBjørn Mork 	if (kstrtoul(buf, 0, &val) || cdc_ncm_check_tx_max(dev, val) != val)
255289507d3SBjørn Mork 		return -EINVAL;
256289507d3SBjørn Mork 
257289507d3SBjørn Mork 	cdc_ncm_update_rxtx_max(dev, ctx->rx_max, val);
258289507d3SBjørn Mork 	return len;
259289507d3SBjørn Mork }
260289507d3SBjørn Mork 
261289507d3SBjørn Mork static ssize_t cdc_ncm_store_tx_timer_usecs(struct device *d,  struct device_attribute *attr, const char *buf, size_t len)
262289507d3SBjørn Mork {
263289507d3SBjørn Mork 	struct usbnet *dev = netdev_priv(to_net_dev(d));
264289507d3SBjørn Mork 	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
265289507d3SBjørn Mork 	ssize_t ret;
266289507d3SBjørn Mork 	unsigned long val;
267289507d3SBjørn Mork 
268289507d3SBjørn Mork 	ret = kstrtoul(buf, 0, &val);
269289507d3SBjørn Mork 	if (ret)
270289507d3SBjørn Mork 		return ret;
271289507d3SBjørn Mork 	if (val && (val < CDC_NCM_TIMER_INTERVAL_MIN || val > CDC_NCM_TIMER_INTERVAL_MAX))
272289507d3SBjørn Mork 		return -EINVAL;
273289507d3SBjørn Mork 
274289507d3SBjørn Mork 	spin_lock_bh(&ctx->mtx);
275289507d3SBjørn Mork 	ctx->timer_interval = val * NSEC_PER_USEC;
276289507d3SBjørn Mork 	if (!ctx->timer_interval)
277289507d3SBjørn Mork 		ctx->tx_timer_pending = 0;
278289507d3SBjørn Mork 	spin_unlock_bh(&ctx->mtx);
279289507d3SBjørn Mork 	return len;
280289507d3SBjørn Mork }
281289507d3SBjørn Mork 
28239eb7e0eSBjørn Mork static DEVICE_ATTR(min_tx_pkt, S_IRUGO | S_IWUSR, cdc_ncm_show_min_tx_pkt, cdc_ncm_store_min_tx_pkt);
283289507d3SBjørn Mork static DEVICE_ATTR(rx_max, S_IRUGO | S_IWUSR, cdc_ncm_show_rx_max, cdc_ncm_store_rx_max);
284289507d3SBjørn Mork static DEVICE_ATTR(tx_max, S_IRUGO | S_IWUSR, cdc_ncm_show_tx_max, cdc_ncm_store_tx_max);
285289507d3SBjørn Mork static DEVICE_ATTR(tx_timer_usecs, S_IRUGO | S_IWUSR, cdc_ncm_show_tx_timer_usecs, cdc_ncm_store_tx_timer_usecs);
286289507d3SBjørn Mork 
287404814afSBjørn Mork static ssize_t ndp_to_end_show(struct device *d, struct device_attribute *attr, char *buf)
288404814afSBjørn Mork {
289404814afSBjørn Mork 	struct usbnet *dev = netdev_priv(to_net_dev(d));
290404814afSBjørn Mork 	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
291404814afSBjørn Mork 
292404814afSBjørn Mork 	return sprintf(buf, "%c\n", ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END ? 'Y' : 'N');
293404814afSBjørn Mork }
294404814afSBjørn Mork 
295404814afSBjørn Mork static ssize_t ndp_to_end_store(struct device *d,  struct device_attribute *attr, const char *buf, size_t len)
296404814afSBjørn Mork {
297404814afSBjørn Mork 	struct usbnet *dev = netdev_priv(to_net_dev(d));
298404814afSBjørn Mork 	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
299404814afSBjørn Mork 	bool enable;
300404814afSBjørn Mork 
301404814afSBjørn Mork 	if (strtobool(buf, &enable))
302404814afSBjørn Mork 		return -EINVAL;
303404814afSBjørn Mork 
304404814afSBjørn Mork 	/* no change? */
305404814afSBjørn Mork 	if (enable == (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END))
306404814afSBjørn Mork 		return len;
307404814afSBjørn Mork 
308404814afSBjørn Mork 	if (enable && !ctx->delayed_ndp16) {
309404814afSBjørn Mork 		ctx->delayed_ndp16 = kzalloc(ctx->max_ndp_size, GFP_KERNEL);
310404814afSBjørn Mork 		if (!ctx->delayed_ndp16)
311404814afSBjørn Mork 			return -ENOMEM;
312404814afSBjørn Mork 	}
313404814afSBjørn Mork 
314404814afSBjørn Mork 	/* flush pending data before changing flag */
315404814afSBjørn Mork 	netif_tx_lock_bh(dev->net);
316404814afSBjørn Mork 	usbnet_start_xmit(NULL, dev->net);
317404814afSBjørn Mork 	spin_lock_bh(&ctx->mtx);
318404814afSBjørn Mork 	if (enable)
319404814afSBjørn Mork 		ctx->drvflags |= CDC_NCM_FLAG_NDP_TO_END;
320404814afSBjørn Mork 	else
321404814afSBjørn Mork 		ctx->drvflags &= ~CDC_NCM_FLAG_NDP_TO_END;
322404814afSBjørn Mork 	spin_unlock_bh(&ctx->mtx);
323404814afSBjørn Mork 	netif_tx_unlock_bh(dev->net);
324404814afSBjørn Mork 
325404814afSBjørn Mork 	return len;
326404814afSBjørn Mork }
327404814afSBjørn Mork static DEVICE_ATTR_RW(ndp_to_end);
328404814afSBjørn Mork 
329871578c9SBjørn Mork #define NCM_PARM_ATTR(name, format, tocpu)				\
330871578c9SBjørn Mork static ssize_t cdc_ncm_show_##name(struct device *d, struct device_attribute *attr, char *buf) \
331871578c9SBjørn Mork { \
332871578c9SBjørn Mork 	struct usbnet *dev = netdev_priv(to_net_dev(d)); \
333871578c9SBjørn Mork 	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; \
334871578c9SBjørn Mork 	return sprintf(buf, format "\n", tocpu(ctx->ncm_parm.name));	\
335871578c9SBjørn Mork } \
336871578c9SBjørn Mork static DEVICE_ATTR(name, S_IRUGO, cdc_ncm_show_##name, NULL)
337871578c9SBjørn Mork 
338871578c9SBjørn Mork NCM_PARM_ATTR(bmNtbFormatsSupported, "0x%04x", le16_to_cpu);
339871578c9SBjørn Mork NCM_PARM_ATTR(dwNtbInMaxSize, "%u", le32_to_cpu);
340871578c9SBjørn Mork NCM_PARM_ATTR(wNdpInDivisor, "%u", le16_to_cpu);
341871578c9SBjørn Mork NCM_PARM_ATTR(wNdpInPayloadRemainder, "%u", le16_to_cpu);
342871578c9SBjørn Mork NCM_PARM_ATTR(wNdpInAlignment, "%u", le16_to_cpu);
343871578c9SBjørn Mork NCM_PARM_ATTR(dwNtbOutMaxSize, "%u", le32_to_cpu);
344871578c9SBjørn Mork NCM_PARM_ATTR(wNdpOutDivisor, "%u", le16_to_cpu);
345871578c9SBjørn Mork NCM_PARM_ATTR(wNdpOutPayloadRemainder, "%u", le16_to_cpu);
346871578c9SBjørn Mork NCM_PARM_ATTR(wNdpOutAlignment, "%u", le16_to_cpu);
347871578c9SBjørn Mork NCM_PARM_ATTR(wNtbOutMaxDatagrams, "%u", le16_to_cpu);
348871578c9SBjørn Mork 
349289507d3SBjørn Mork static struct attribute *cdc_ncm_sysfs_attrs[] = {
35039eb7e0eSBjørn Mork 	&dev_attr_min_tx_pkt.attr,
351404814afSBjørn Mork 	&dev_attr_ndp_to_end.attr,
352289507d3SBjørn Mork 	&dev_attr_rx_max.attr,
353289507d3SBjørn Mork 	&dev_attr_tx_max.attr,
354289507d3SBjørn Mork 	&dev_attr_tx_timer_usecs.attr,
355871578c9SBjørn Mork 	&dev_attr_bmNtbFormatsSupported.attr,
356871578c9SBjørn Mork 	&dev_attr_dwNtbInMaxSize.attr,
357871578c9SBjørn Mork 	&dev_attr_wNdpInDivisor.attr,
358871578c9SBjørn Mork 	&dev_attr_wNdpInPayloadRemainder.attr,
359871578c9SBjørn Mork 	&dev_attr_wNdpInAlignment.attr,
360871578c9SBjørn Mork 	&dev_attr_dwNtbOutMaxSize.attr,
361871578c9SBjørn Mork 	&dev_attr_wNdpOutDivisor.attr,
362871578c9SBjørn Mork 	&dev_attr_wNdpOutPayloadRemainder.attr,
363871578c9SBjørn Mork 	&dev_attr_wNdpOutAlignment.attr,
364871578c9SBjørn Mork 	&dev_attr_wNtbOutMaxDatagrams.attr,
365289507d3SBjørn Mork 	NULL,
366289507d3SBjørn Mork };
367289507d3SBjørn Mork 
368289507d3SBjørn Mork static struct attribute_group cdc_ncm_sysfs_attr_group = {
369289507d3SBjørn Mork 	.name = "cdc_ncm",
370289507d3SBjørn Mork 	.attrs = cdc_ncm_sysfs_attrs,
371289507d3SBjørn Mork };
372289507d3SBjørn Mork 
373289507d3SBjørn Mork /* handle rx_max and tx_max changes */
374289507d3SBjørn Mork static void cdc_ncm_update_rxtx_max(struct usbnet *dev, u32 new_rx, u32 new_tx)
375289507d3SBjørn Mork {
376289507d3SBjørn Mork 	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
377289507d3SBjørn Mork 	u8 iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber;
378289507d3SBjørn Mork 	u32 val;
379289507d3SBjørn Mork 
380289507d3SBjørn Mork 	val = cdc_ncm_check_rx_max(dev, new_rx);
381289507d3SBjørn Mork 
3825aa73d5dSBjørn Mork 	/* inform device about NTB input size changes */
3835aa73d5dSBjørn Mork 	if (val != ctx->rx_max) {
3845aa73d5dSBjørn Mork 		__le32 dwNtbInMaxSize = cpu_to_le32(val);
3855aa73d5dSBjørn Mork 
3865aa73d5dSBjørn Mork 		dev_info(&dev->intf->dev, "setting rx_max = %u\n", val);
38768864abfSBjørn Mork 
38868864abfSBjørn Mork 		/* tell device to use new size */
3895aa73d5dSBjørn Mork 		if (usbnet_write_cmd(dev, USB_CDC_SET_NTB_INPUT_SIZE,
3905aa73d5dSBjørn Mork 				     USB_TYPE_CLASS | USB_DIR_OUT
3915aa73d5dSBjørn Mork 				     | USB_RECIP_INTERFACE,
3925aa73d5dSBjørn Mork 				     0, iface_no, &dwNtbInMaxSize, 4) < 0)
3935aa73d5dSBjørn Mork 			dev_dbg(&dev->intf->dev, "Setting NTB Input Size failed\n");
3945aa73d5dSBjørn Mork 		else
3955aa73d5dSBjørn Mork 			ctx->rx_max = val;
3965aa73d5dSBjørn Mork 	}
3975aa73d5dSBjørn Mork 
398f42763dbSBjørn Mork 	/* usbnet use these values for sizing rx queues */
399f42763dbSBjørn Mork 	if (dev->rx_urb_size != ctx->rx_max) {
400f42763dbSBjørn Mork 		dev->rx_urb_size = ctx->rx_max;
401f42763dbSBjørn Mork 		if (netif_running(dev->net))
402f42763dbSBjørn Mork 			usbnet_unlink_rx_urbs(dev);
403f42763dbSBjørn Mork 	}
404f42763dbSBjørn Mork 
405289507d3SBjørn Mork 	val = cdc_ncm_check_tx_max(dev, new_tx);
4065aa73d5dSBjørn Mork 	if (val != ctx->tx_max)
4075aa73d5dSBjørn Mork 		dev_info(&dev->intf->dev, "setting tx_max = %u\n", val);
40808c74fc9SBjørn Mork 
40908c74fc9SBjørn Mork 	/* Adding a pad byte here if necessary simplifies the handling
41008c74fc9SBjørn Mork 	 * in cdc_ncm_fill_tx_frame, making tx_max always represent
41108c74fc9SBjørn Mork 	 * the real skb max size.
41208c74fc9SBjørn Mork 	 *
41308c74fc9SBjørn Mork 	 * We cannot use dev->maxpacket here because this is called from
41408c74fc9SBjørn Mork 	 * .bind which is called before usbnet sets up dev->maxpacket
41508c74fc9SBjørn Mork 	 */
41668864abfSBjørn Mork 	if (val != le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize) &&
41768864abfSBjørn Mork 	    val % usb_maxpacket(dev->udev, dev->out, 1) == 0)
41868864abfSBjørn Mork 		val++;
41908c74fc9SBjørn Mork 
42068864abfSBjørn Mork 	/* we might need to flush any pending tx buffers if running */
42168864abfSBjørn Mork 	if (netif_running(dev->net) && val > ctx->tx_max) {
42268864abfSBjørn Mork 		netif_tx_lock_bh(dev->net);
42368864abfSBjørn Mork 		usbnet_start_xmit(NULL, dev->net);
4241ba5d0ffSBjørn Mork 		/* make sure tx_curr_skb is reallocated if it was empty */
4251ba5d0ffSBjørn Mork 		if (ctx->tx_curr_skb) {
4261ba5d0ffSBjørn Mork 			dev_kfree_skb_any(ctx->tx_curr_skb);
4271ba5d0ffSBjørn Mork 			ctx->tx_curr_skb = NULL;
4281ba5d0ffSBjørn Mork 		}
42968864abfSBjørn Mork 		ctx->tx_max = val;
43068864abfSBjørn Mork 		netif_tx_unlock_bh(dev->net);
43168864abfSBjørn Mork 	} else {
43268864abfSBjørn Mork 		ctx->tx_max = val;
43368864abfSBjørn Mork 	}
43468864abfSBjørn Mork 
43508c74fc9SBjørn Mork 	dev->hard_mtu = ctx->tx_max;
43668864abfSBjørn Mork 
43768864abfSBjørn Mork 	/* max qlen depend on hard_mtu and rx_urb_size */
43868864abfSBjørn Mork 	usbnet_update_max_qlen(dev);
43943e4c6dfSBjørn Mork 
44043e4c6dfSBjørn Mork 	/* never pad more than 3 full USB packets per transfer */
44143e4c6dfSBjørn Mork 	ctx->min_tx_pkt = clamp_t(u16, ctx->tx_max - 3 * usb_maxpacket(dev->udev, dev->out, 1),
44243e4c6dfSBjørn Mork 				  CDC_NCM_MIN_TX_PKT, ctx->tx_max);
4435aa73d5dSBjørn Mork }
4445aa73d5dSBjørn Mork 
445f8afb73dSBjørn Mork /* helpers for NCM and MBIM differences */
446f8afb73dSBjørn Mork static u8 cdc_ncm_flags(struct usbnet *dev)
447900d495aSAlexey Orishko {
448bed6f762SBjørn Mork 	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
449900d495aSAlexey Orishko 
450f8afb73dSBjørn Mork 	if (cdc_ncm_comm_intf_is_mbim(dev->intf->cur_altsetting) && ctx->mbim_desc)
451f8afb73dSBjørn Mork 		return ctx->mbim_desc->bmNetworkCapabilities;
452f8afb73dSBjørn Mork 	if (ctx->func_desc)
453f8afb73dSBjørn Mork 		return ctx->func_desc->bmNetworkCapabilities;
454f8afb73dSBjørn Mork 	return 0;
455f8afb73dSBjørn Mork }
456f8afb73dSBjørn Mork 
457f8afb73dSBjørn Mork static int cdc_ncm_eth_hlen(struct usbnet *dev)
458f8afb73dSBjørn Mork {
459f8afb73dSBjørn Mork 	if (cdc_ncm_comm_intf_is_mbim(dev->intf->cur_altsetting))
460f8afb73dSBjørn Mork 		return 0;
461f8afb73dSBjørn Mork 	return ETH_HLEN;
462f8afb73dSBjørn Mork }
463f8afb73dSBjørn Mork 
464f8afb73dSBjørn Mork static u32 cdc_ncm_min_dgram_size(struct usbnet *dev)
465f8afb73dSBjørn Mork {
466f8afb73dSBjørn Mork 	if (cdc_ncm_comm_intf_is_mbim(dev->intf->cur_altsetting))
467f8afb73dSBjørn Mork 		return CDC_MBIM_MIN_DATAGRAM_SIZE;
468f8afb73dSBjørn Mork 	return CDC_NCM_MIN_DATAGRAM_SIZE;
469f8afb73dSBjørn Mork }
470f8afb73dSBjørn Mork 
471f8afb73dSBjørn Mork static u32 cdc_ncm_max_dgram_size(struct usbnet *dev)
472f8afb73dSBjørn Mork {
473f8afb73dSBjørn Mork 	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
474f8afb73dSBjørn Mork 
475f8afb73dSBjørn Mork 	if (cdc_ncm_comm_intf_is_mbim(dev->intf->cur_altsetting) && ctx->mbim_desc)
476f8afb73dSBjørn Mork 		return le16_to_cpu(ctx->mbim_desc->wMaxSegmentSize);
477f8afb73dSBjørn Mork 	if (ctx->ether_desc)
478f8afb73dSBjørn Mork 		return le16_to_cpu(ctx->ether_desc->wMaxSegmentSize);
479f8afb73dSBjørn Mork 	return CDC_NCM_MAX_DATAGRAM_SIZE;
480f8afb73dSBjørn Mork }
481f8afb73dSBjørn Mork 
482f8afb73dSBjørn Mork /* initial one-time device setup.  MUST be called with the data interface
483f8afb73dSBjørn Mork  * in altsetting 0
484f8afb73dSBjørn Mork  */
485f8afb73dSBjørn Mork static int cdc_ncm_init(struct usbnet *dev)
486f8afb73dSBjørn Mork {
487f8afb73dSBjørn Mork 	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
488f8afb73dSBjørn Mork 	u8 iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber;
489f8afb73dSBjørn Mork 	int err;
490900d495aSAlexey Orishko 
49190b8b037SMing Lei 	err = usbnet_read_cmd(dev, USB_CDC_GET_NTB_PARAMETERS,
49236c35416SGiuseppe Scrivano 			      USB_TYPE_CLASS | USB_DIR_IN
49336c35416SGiuseppe Scrivano 			      |USB_RECIP_INTERFACE,
494ff0992e9SBjørn Mork 			      0, iface_no, &ctx->ncm_parm,
495ff0992e9SBjørn Mork 			      sizeof(ctx->ncm_parm));
49636c35416SGiuseppe Scrivano 	if (err < 0) {
49759ede316SBjørn Mork 		dev_err(&dev->intf->dev, "failed GET_NTB_PARAMETERS\n");
49859ede316SBjørn Mork 		return err; /* GET_NTB_PARAMETERS is required */
499900d495aSAlexey Orishko 	}
500900d495aSAlexey Orishko 
501f8afb73dSBjørn Mork 	/* set CRC Mode */
502f8afb73dSBjørn Mork 	if (cdc_ncm_flags(dev) & USB_CDC_NCM_NCAP_CRC_MODE) {
503f8afb73dSBjørn Mork 		dev_dbg(&dev->intf->dev, "Setting CRC mode off\n");
504f8afb73dSBjørn Mork 		err = usbnet_write_cmd(dev, USB_CDC_SET_CRC_MODE,
505f8afb73dSBjørn Mork 				       USB_TYPE_CLASS | USB_DIR_OUT
506f8afb73dSBjørn Mork 				       | USB_RECIP_INTERFACE,
507f8afb73dSBjørn Mork 				       USB_CDC_NCM_CRC_NOT_APPENDED,
508f8afb73dSBjørn Mork 				       iface_no, NULL, 0);
509f8afb73dSBjørn Mork 		if (err < 0)
510f8afb73dSBjørn Mork 			dev_err(&dev->intf->dev, "SET_CRC_MODE failed\n");
511f8afb73dSBjørn Mork 	}
512f8afb73dSBjørn Mork 
513f8afb73dSBjørn Mork 	/* set NTB format, if both formats are supported.
514f8afb73dSBjørn Mork 	 *
515f8afb73dSBjørn Mork 	 * "The host shall only send this command while the NCM Data
516f8afb73dSBjørn Mork 	 *  Interface is in alternate setting 0."
517f8afb73dSBjørn Mork 	 */
518d22adbfbSDan Carpenter 	if (le16_to_cpu(ctx->ncm_parm.bmNtbFormatsSupported) &
519d22adbfbSDan Carpenter 						USB_CDC_NCM_NTB32_SUPPORTED) {
520f8afb73dSBjørn Mork 		dev_dbg(&dev->intf->dev, "Setting NTB format to 16-bit\n");
521f8afb73dSBjørn Mork 		err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_FORMAT,
522f8afb73dSBjørn Mork 				       USB_TYPE_CLASS | USB_DIR_OUT
523f8afb73dSBjørn Mork 				       | USB_RECIP_INTERFACE,
524f8afb73dSBjørn Mork 				       USB_CDC_NCM_NTB16_FORMAT,
525f8afb73dSBjørn Mork 				       iface_no, NULL, 0);
526f8afb73dSBjørn Mork 		if (err < 0)
527f8afb73dSBjørn Mork 			dev_err(&dev->intf->dev, "SET_NTB_FORMAT failed\n");
528f8afb73dSBjørn Mork 	}
529f8afb73dSBjørn Mork 
530f8afb73dSBjørn Mork 	/* set initial device values */
531ff0992e9SBjørn Mork 	ctx->rx_max = le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize);
532ff0992e9SBjørn Mork 	ctx->tx_max = le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize);
533ff0992e9SBjørn Mork 	ctx->tx_remainder = le16_to_cpu(ctx->ncm_parm.wNdpOutPayloadRemainder);
534ff0992e9SBjørn Mork 	ctx->tx_modulus = le16_to_cpu(ctx->ncm_parm.wNdpOutDivisor);
535ff0992e9SBjørn Mork 	ctx->tx_ndp_modulus = le16_to_cpu(ctx->ncm_parm.wNdpOutAlignment);
53684e77a8bSAlexey Orishko 	/* devices prior to NCM Errata shall set this field to zero */
537ff0992e9SBjørn Mork 	ctx->tx_max_datagrams = le16_to_cpu(ctx->ncm_parm.wNtbOutMaxDatagrams);
53847175e5fSBjørn Mork 
539ae223cd4SBjørn Mork 	dev_dbg(&dev->intf->dev,
540ae223cd4SBjørn Mork 		"dwNtbInMaxSize=%u dwNtbOutMaxSize=%u wNdpOutPayloadRemainder=%u wNdpOutDivisor=%u wNdpOutAlignment=%u wNtbOutMaxDatagrams=%u flags=0x%x\n",
541900d495aSAlexey Orishko 		ctx->rx_max, ctx->tx_max, ctx->tx_remainder, ctx->tx_modulus,
542f8afb73dSBjørn Mork 		ctx->tx_ndp_modulus, ctx->tx_max_datagrams, cdc_ncm_flags(dev));
543900d495aSAlexey Orishko 
54484e77a8bSAlexey Orishko 	/* max count of tx datagrams */
54584e77a8bSAlexey Orishko 	if ((ctx->tx_max_datagrams == 0) ||
54684e77a8bSAlexey Orishko 			(ctx->tx_max_datagrams > CDC_NCM_DPT_DATAGRAMS_MAX))
547900d495aSAlexey Orishko 		ctx->tx_max_datagrams = CDC_NCM_DPT_DATAGRAMS_MAX;
548900d495aSAlexey Orishko 
54970559b89SBjørn Mork 	/* set up maximum NDP size */
55070559b89SBjørn Mork 	ctx->max_ndp_size = sizeof(struct usb_cdc_ncm_ndp16) + (ctx->tx_max_datagrams + 1) * sizeof(struct usb_cdc_ncm_dpe16);
55170559b89SBjørn Mork 
5526c4e548fSBjørn Mork 	/* initial coalescing timer interval */
5536c4e548fSBjørn Mork 	ctx->timer_interval = CDC_NCM_TIMER_INTERVAL_USEC * NSEC_PER_USEC;
5546c4e548fSBjørn Mork 
555f8afb73dSBjørn Mork 	return 0;
556f8afb73dSBjørn Mork }
557f8afb73dSBjørn Mork 
558f8afb73dSBjørn Mork /* set a new max datagram size */
559f8afb73dSBjørn Mork static void cdc_ncm_set_dgram_size(struct usbnet *dev, int new_size)
560f8afb73dSBjørn Mork {
561f8afb73dSBjørn Mork 	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
562f8afb73dSBjørn Mork 	u8 iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber;
563f8afb73dSBjørn Mork 	__le16 max_datagram_size;
564f8afb73dSBjørn Mork 	u16 mbim_mtu;
565f8afb73dSBjørn Mork 	int err;
566f8afb73dSBjørn Mork 
567f8afb73dSBjørn Mork 	/* set default based on descriptors */
568f8afb73dSBjørn Mork 	ctx->max_datagram_size = clamp_t(u32, new_size,
569f8afb73dSBjørn Mork 					 cdc_ncm_min_dgram_size(dev),
570f8afb73dSBjørn Mork 					 CDC_NCM_MAX_DATAGRAM_SIZE);
571f8afb73dSBjørn Mork 
572f8afb73dSBjørn Mork 	/* inform the device about the selected Max Datagram Size? */
573f8afb73dSBjørn Mork 	if (!(cdc_ncm_flags(dev) & USB_CDC_NCM_NCAP_MAX_DATAGRAM_SIZE))
574f8afb73dSBjørn Mork 		goto out;
575f8afb73dSBjørn Mork 
576f8afb73dSBjørn Mork 	/* read current mtu value from device */
577f8afb73dSBjørn Mork 	err = usbnet_read_cmd(dev, USB_CDC_GET_MAX_DATAGRAM_SIZE,
578f8afb73dSBjørn Mork 			      USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE,
579f8afb73dSBjørn Mork 			      0, iface_no, &max_datagram_size, 2);
580f8afb73dSBjørn Mork 	if (err < 0) {
581f8afb73dSBjørn Mork 		dev_dbg(&dev->intf->dev, "GET_MAX_DATAGRAM_SIZE failed\n");
582f8afb73dSBjørn Mork 		goto out;
583f8afb73dSBjørn Mork 	}
584f8afb73dSBjørn Mork 
585f8afb73dSBjørn Mork 	if (le16_to_cpu(max_datagram_size) == ctx->max_datagram_size)
586f8afb73dSBjørn Mork 		goto out;
587f8afb73dSBjørn Mork 
588f8afb73dSBjørn Mork 	max_datagram_size = cpu_to_le16(ctx->max_datagram_size);
589f8afb73dSBjørn Mork 	err = usbnet_write_cmd(dev, USB_CDC_SET_MAX_DATAGRAM_SIZE,
590f8afb73dSBjørn Mork 			       USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE,
591f8afb73dSBjørn Mork 			       0, iface_no, &max_datagram_size, 2);
592f8afb73dSBjørn Mork 	if (err < 0)
593f8afb73dSBjørn Mork 		dev_dbg(&dev->intf->dev, "SET_MAX_DATAGRAM_SIZE failed\n");
594f8afb73dSBjørn Mork 
595f8afb73dSBjørn Mork out:
596f8afb73dSBjørn Mork 	/* set MTU to max supported by the device if necessary */
597f8afb73dSBjørn Mork 	dev->net->mtu = min_t(int, dev->net->mtu, ctx->max_datagram_size - cdc_ncm_eth_hlen(dev));
598f8afb73dSBjørn Mork 
599f8afb73dSBjørn Mork 	/* do not exceed operater preferred MTU */
600f8afb73dSBjørn Mork 	if (ctx->mbim_extended_desc) {
601f8afb73dSBjørn Mork 		mbim_mtu = le16_to_cpu(ctx->mbim_extended_desc->wMTU);
602f8afb73dSBjørn Mork 		if (mbim_mtu != 0 && mbim_mtu < dev->net->mtu)
603f8afb73dSBjørn Mork 			dev->net->mtu = mbim_mtu;
604f8afb73dSBjørn Mork 	}
605f8afb73dSBjørn Mork }
606f8afb73dSBjørn Mork 
607f8afb73dSBjørn Mork static void cdc_ncm_fix_modulus(struct usbnet *dev)
608f8afb73dSBjørn Mork {
609f8afb73dSBjørn Mork 	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
610f8afb73dSBjørn Mork 	u32 val;
611900d495aSAlexey Orishko 
612900d495aSAlexey Orishko 	/*
613900d495aSAlexey Orishko 	 * verify that the structure alignment is:
614900d495aSAlexey Orishko 	 * - power of two
615900d495aSAlexey Orishko 	 * - not greater than the maximum transmit length
616900d495aSAlexey Orishko 	 * - not less than four bytes
617900d495aSAlexey Orishko 	 */
618900d495aSAlexey Orishko 	val = ctx->tx_ndp_modulus;
619900d495aSAlexey Orishko 
620900d495aSAlexey Orishko 	if ((val < USB_CDC_NCM_NDP_ALIGN_MIN_SIZE) ||
621900d495aSAlexey Orishko 	    (val != ((-val) & val)) || (val >= ctx->tx_max)) {
622ae223cd4SBjørn Mork 		dev_dbg(&dev->intf->dev, "Using default alignment: 4 bytes\n");
623900d495aSAlexey Orishko 		ctx->tx_ndp_modulus = USB_CDC_NCM_NDP_ALIGN_MIN_SIZE;
624900d495aSAlexey Orishko 	}
625900d495aSAlexey Orishko 
626900d495aSAlexey Orishko 	/*
627900d495aSAlexey Orishko 	 * verify that the payload alignment is:
628900d495aSAlexey Orishko 	 * - power of two
629900d495aSAlexey Orishko 	 * - not greater than the maximum transmit length
630900d495aSAlexey Orishko 	 * - not less than four bytes
631900d495aSAlexey Orishko 	 */
632900d495aSAlexey Orishko 	val = ctx->tx_modulus;
633900d495aSAlexey Orishko 
634900d495aSAlexey Orishko 	if ((val < USB_CDC_NCM_NDP_ALIGN_MIN_SIZE) ||
635900d495aSAlexey Orishko 	    (val != ((-val) & val)) || (val >= ctx->tx_max)) {
636ae223cd4SBjørn Mork 		dev_dbg(&dev->intf->dev, "Using default transmit modulus: 4 bytes\n");
637900d495aSAlexey Orishko 		ctx->tx_modulus = USB_CDC_NCM_NDP_ALIGN_MIN_SIZE;
638900d495aSAlexey Orishko 	}
639900d495aSAlexey Orishko 
640900d495aSAlexey Orishko 	/* verify the payload remainder */
641900d495aSAlexey Orishko 	if (ctx->tx_remainder >= ctx->tx_modulus) {
642ae223cd4SBjørn Mork 		dev_dbg(&dev->intf->dev, "Using default transmit remainder: 0 bytes\n");
643900d495aSAlexey Orishko 		ctx->tx_remainder = 0;
644900d495aSAlexey Orishko 	}
645900d495aSAlexey Orishko 
646900d495aSAlexey Orishko 	/* adjust TX-remainder according to NCM specification. */
647f8afb73dSBjørn Mork 	ctx->tx_remainder = ((ctx->tx_remainder - cdc_ncm_eth_hlen(dev)) &
648900d495aSAlexey Orishko 			     (ctx->tx_modulus - 1));
64984e77a8bSAlexey Orishko }
650900d495aSAlexey Orishko 
651f8afb73dSBjørn Mork static int cdc_ncm_setup(struct usbnet *dev)
652f8afb73dSBjørn Mork {
653f8afb73dSBjørn Mork 	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
65450f1cb1cSBjørn Mork 	u32 def_rx, def_tx;
65550f1cb1cSBjørn Mork 
65650f1cb1cSBjørn Mork 	/* be conservative when selecting intial buffer size to
65750f1cb1cSBjørn Mork 	 * increase the number of hosts this will work for
65850f1cb1cSBjørn Mork 	 */
65950f1cb1cSBjørn Mork 	def_rx = min_t(u32, CDC_NCM_NTB_DEF_SIZE_RX,
66050f1cb1cSBjørn Mork 		       le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize));
66150f1cb1cSBjørn Mork 	def_tx = min_t(u32, CDC_NCM_NTB_DEF_SIZE_TX,
66250f1cb1cSBjørn Mork 		       le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize));
66384e77a8bSAlexey Orishko 
664f8afb73dSBjørn Mork 	/* clamp rx_max and tx_max and inform device */
66550f1cb1cSBjørn Mork 	cdc_ncm_update_rxtx_max(dev, def_rx, def_tx);
6663f658cdeSAlexey Orishko 
667f8afb73dSBjørn Mork 	/* sanitize the modulus and remainder values */
668f8afb73dSBjørn Mork 	cdc_ncm_fix_modulus(dev);
669900d495aSAlexey Orishko 
670f8afb73dSBjørn Mork 	/* set max datagram size */
671f8afb73dSBjørn Mork 	cdc_ncm_set_dgram_size(dev, cdc_ncm_max_dgram_size(dev));
672900d495aSAlexey Orishko 	return 0;
673900d495aSAlexey Orishko }
674900d495aSAlexey Orishko 
675900d495aSAlexey Orishko static void
676ff1632aaSBjørn Mork cdc_ncm_find_endpoints(struct usbnet *dev, struct usb_interface *intf)
677900d495aSAlexey Orishko {
678ff1632aaSBjørn Mork 	struct usb_host_endpoint *e, *in = NULL, *out = NULL;
679900d495aSAlexey Orishko 	u8 ep;
680900d495aSAlexey Orishko 
681900d495aSAlexey Orishko 	for (ep = 0; ep < intf->cur_altsetting->desc.bNumEndpoints; ep++) {
682900d495aSAlexey Orishko 
683900d495aSAlexey Orishko 		e = intf->cur_altsetting->endpoint + ep;
684900d495aSAlexey Orishko 		switch (e->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
685900d495aSAlexey Orishko 		case USB_ENDPOINT_XFER_INT:
686900d495aSAlexey Orishko 			if (usb_endpoint_dir_in(&e->desc)) {
687ff1632aaSBjørn Mork 				if (!dev->status)
688ff1632aaSBjørn Mork 					dev->status = e;
689900d495aSAlexey Orishko 			}
690900d495aSAlexey Orishko 			break;
691900d495aSAlexey Orishko 
692900d495aSAlexey Orishko 		case USB_ENDPOINT_XFER_BULK:
693900d495aSAlexey Orishko 			if (usb_endpoint_dir_in(&e->desc)) {
694ff1632aaSBjørn Mork 				if (!in)
695ff1632aaSBjørn Mork 					in = e;
696900d495aSAlexey Orishko 			} else {
697ff1632aaSBjørn Mork 				if (!out)
698ff1632aaSBjørn Mork 					out = e;
699900d495aSAlexey Orishko 			}
700900d495aSAlexey Orishko 			break;
701900d495aSAlexey Orishko 
702900d495aSAlexey Orishko 		default:
703900d495aSAlexey Orishko 			break;
704900d495aSAlexey Orishko 		}
705900d495aSAlexey Orishko 	}
706ff1632aaSBjørn Mork 	if (in && !dev->in)
707ff1632aaSBjørn Mork 		dev->in = usb_rcvbulkpipe(dev->udev,
708ff1632aaSBjørn Mork 					  in->desc.bEndpointAddress &
709ff1632aaSBjørn Mork 					  USB_ENDPOINT_NUMBER_MASK);
710ff1632aaSBjørn Mork 	if (out && !dev->out)
711ff1632aaSBjørn Mork 		dev->out = usb_sndbulkpipe(dev->udev,
712ff1632aaSBjørn Mork 					   out->desc.bEndpointAddress &
713ff1632aaSBjørn Mork 					   USB_ENDPOINT_NUMBER_MASK);
714900d495aSAlexey Orishko }
715900d495aSAlexey Orishko 
716900d495aSAlexey Orishko static void cdc_ncm_free(struct cdc_ncm_ctx *ctx)
717900d495aSAlexey Orishko {
718900d495aSAlexey Orishko 	if (ctx == NULL)
719900d495aSAlexey Orishko 		return;
720900d495aSAlexey Orishko 
721900d495aSAlexey Orishko 	if (ctx->tx_rem_skb != NULL) {
722900d495aSAlexey Orishko 		dev_kfree_skb_any(ctx->tx_rem_skb);
723900d495aSAlexey Orishko 		ctx->tx_rem_skb = NULL;
724900d495aSAlexey Orishko 	}
725900d495aSAlexey Orishko 
726900d495aSAlexey Orishko 	if (ctx->tx_curr_skb != NULL) {
727900d495aSAlexey Orishko 		dev_kfree_skb_any(ctx->tx_curr_skb);
728900d495aSAlexey Orishko 		ctx->tx_curr_skb = NULL;
729900d495aSAlexey Orishko 	}
730900d495aSAlexey Orishko 
7314a0e3e98SEnrico Mioso 	kfree(ctx->delayed_ndp16);
7324a0e3e98SEnrico Mioso 
733900d495aSAlexey Orishko 	kfree(ctx);
734900d495aSAlexey Orishko }
735900d495aSAlexey Orishko 
7361dfddff5SBjørn Mork /* we need to override the usbnet change_mtu ndo for two reasons:
7371dfddff5SBjørn Mork  *  - respect the negotiated maximum datagram size
7381dfddff5SBjørn Mork  *  - avoid unwanted changes to rx and tx buffers
7391dfddff5SBjørn Mork  */
7401dfddff5SBjørn Mork int cdc_ncm_change_mtu(struct net_device *net, int new_mtu)
7411dfddff5SBjørn Mork {
7421dfddff5SBjørn Mork 	struct usbnet *dev = netdev_priv(net);
74305a56487SRafal Redzimski 
7441dfddff5SBjørn Mork 	net->mtu = new_mtu;
74505a56487SRafal Redzimski 	cdc_ncm_set_dgram_size(dev, new_mtu + cdc_ncm_eth_hlen(dev));
74605a56487SRafal Redzimski 
7471dfddff5SBjørn Mork 	return 0;
7481dfddff5SBjørn Mork }
7491dfddff5SBjørn Mork EXPORT_SYMBOL_GPL(cdc_ncm_change_mtu);
7501dfddff5SBjørn Mork 
7511dfddff5SBjørn Mork static const struct net_device_ops cdc_ncm_netdev_ops = {
7521dfddff5SBjørn Mork 	.ndo_open	     = usbnet_open,
7531dfddff5SBjørn Mork 	.ndo_stop	     = usbnet_stop,
7541dfddff5SBjørn Mork 	.ndo_start_xmit	     = usbnet_start_xmit,
7551dfddff5SBjørn Mork 	.ndo_tx_timeout	     = usbnet_tx_timeout,
756c8b5d129SGreg Ungerer 	.ndo_get_stats64     = usbnet_get_stats64,
7571dfddff5SBjørn Mork 	.ndo_change_mtu	     = cdc_ncm_change_mtu,
7581dfddff5SBjørn Mork 	.ndo_set_mac_address = eth_mac_addr,
7591dfddff5SBjørn Mork 	.ndo_validate_addr   = eth_validate_addr,
7601dfddff5SBjørn Mork };
7611dfddff5SBjørn Mork 
7624a0e3e98SEnrico Mioso int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting, int drvflags)
763900d495aSAlexey Orishko {
764900d495aSAlexey Orishko 	struct cdc_ncm_ctx *ctx;
765900d495aSAlexey Orishko 	struct usb_driver *driver;
766900d495aSAlexey Orishko 	u8 *buf;
767900d495aSAlexey Orishko 	int len;
768900d495aSAlexey Orishko 	int temp;
769900d495aSAlexey Orishko 	u8 iface_no;
77077b0a099SOliver Neukum 	struct usb_cdc_parsed_header hdr;
771900d495aSAlexey Orishko 
772c796984fSThomas Meyer 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
7738f0923c1SDevendra Naga 	if (!ctx)
7748f0923c1SDevendra Naga 		return -ENOMEM;
775900d495aSAlexey Orishko 
776c84ff1d6SAlexey Orishko 	hrtimer_init(&ctx->tx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
777c84ff1d6SAlexey Orishko 	ctx->tx_timer.function = &cdc_ncm_tx_timer_cb;
778bed6f762SBjørn Mork 	ctx->bh.data = (unsigned long)dev;
779c84ff1d6SAlexey Orishko 	ctx->bh.func = cdc_ncm_txpath_bh;
780c84ff1d6SAlexey Orishko 	atomic_set(&ctx->stop, 0);
781900d495aSAlexey Orishko 	spin_lock_init(&ctx->mtx);
782900d495aSAlexey Orishko 
783900d495aSAlexey Orishko 	/* store ctx pointer in device data field */
784900d495aSAlexey Orishko 	dev->data[0] = (unsigned long)ctx;
785900d495aSAlexey Orishko 
7869fe0234cSBjørn Mork 	/* only the control interface can be successfully probed */
7879fe0234cSBjørn Mork 	ctx->control = intf;
7889fe0234cSBjørn Mork 
789900d495aSAlexey Orishko 	/* get some pointers */
790900d495aSAlexey Orishko 	driver = driver_of(intf);
791900d495aSAlexey Orishko 	buf = intf->cur_altsetting->extra;
792900d495aSAlexey Orishko 	len = intf->cur_altsetting->extralen;
793900d495aSAlexey Orishko 
794900d495aSAlexey Orishko 	/* parse through descriptors associated with control interface */
79577b0a099SOliver Neukum 	cdc_parse_cdc_header(&hdr, intf, buf, len);
796900d495aSAlexey Orishko 
7976527f833SBjørn Mork 	if (hdr.usb_cdc_union_desc)
798900d495aSAlexey Orishko 		ctx->data = usb_ifnum_to_if(dev->udev,
79977b0a099SOliver Neukum 					    hdr.usb_cdc_union_desc->bSlaveInterface0);
80077b0a099SOliver Neukum 	ctx->ether_desc = hdr.usb_cdc_ether_desc;
80177b0a099SOliver Neukum 	ctx->func_desc = hdr.usb_cdc_ncm_desc;
80277b0a099SOliver Neukum 	ctx->mbim_desc = hdr.usb_cdc_mbim_desc;
80377b0a099SOliver Neukum 	ctx->mbim_extended_desc = hdr.usb_cdc_mbim_extended_desc;
804900d495aSAlexey Orishko 
8059992c2e2SBjørn Mork 	/* some buggy devices have an IAD but no CDC Union */
8066527f833SBjørn Mork 	if (!hdr.usb_cdc_union_desc && intf->intf_assoc && intf->intf_assoc->bInterfaceCount == 2) {
80756a666dcSBjørn Mork 		ctx->data = usb_ifnum_to_if(dev->udev, intf->cur_altsetting->desc.bInterfaceNumber + 1);
80856a666dcSBjørn Mork 		dev_dbg(&intf->dev, "CDC Union missing - got slave from IAD\n");
8099992c2e2SBjørn Mork 	}
8109992c2e2SBjørn Mork 
811900d495aSAlexey Orishko 	/* check if we got everything */
812f8afb73dSBjørn Mork 	if (!ctx->data) {
813f8afb73dSBjørn Mork 		dev_dbg(&intf->dev, "CDC Union missing and no IAD found\n");
814900d495aSAlexey Orishko 		goto error;
815296e81f8SBjørn Mork 	}
816f8afb73dSBjørn Mork 	if (cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting)) {
817f8afb73dSBjørn Mork 		if (!ctx->mbim_desc) {
818f8afb73dSBjørn Mork 			dev_dbg(&intf->dev, "MBIM functional descriptor missing\n");
819f8afb73dSBjørn Mork 			goto error;
820f8afb73dSBjørn Mork 		}
821f8afb73dSBjørn Mork 	} else {
822f8afb73dSBjørn Mork 		if (!ctx->ether_desc || !ctx->func_desc) {
823f8afb73dSBjørn Mork 			dev_dbg(&intf->dev, "NCM or ECM functional descriptors missing\n");
824f8afb73dSBjørn Mork 			goto error;
825f8afb73dSBjørn Mork 		}
826f8afb73dSBjørn Mork 	}
827900d495aSAlexey Orishko 
828bbc8d922SBjørn Mork 	/* claim data interface, if different from control */
829bbc8d922SBjørn Mork 	if (ctx->data != ctx->control) {
830900d495aSAlexey Orishko 		temp = usb_driver_claim_interface(driver, ctx->data, dev);
831296e81f8SBjørn Mork 		if (temp) {
832296e81f8SBjørn Mork 			dev_dbg(&intf->dev, "failed to claim data intf\n");
833900d495aSAlexey Orishko 			goto error;
834bbc8d922SBjørn Mork 		}
835296e81f8SBjørn Mork 	}
836900d495aSAlexey Orishko 
837900d495aSAlexey Orishko 	iface_no = ctx->data->cur_altsetting->desc.bInterfaceNumber;
838900d495aSAlexey Orishko 
8397b8076ceSDaniele Palmas 	/* Device-specific flags */
8407b8076ceSDaniele Palmas 	ctx->drvflags = drvflags;
8417b8076ceSDaniele Palmas 
84248906f62SBjørn Mork 	/* Reset data interface. Some devices will not reset properly
84348906f62SBjørn Mork 	 * unless they are configured first.  Toggle the altsetting to
8447b8076ceSDaniele Palmas 	 * force a reset.
8457b8076ceSDaniele Palmas 	 * Some other devices do not work properly with this procedure
8467b8076ceSDaniele Palmas 	 * that can be avoided using quirk CDC_MBIM_FLAG_AVOID_ALTSETTING_TOGGLE
84748906f62SBjørn Mork 	 */
8487b8076ceSDaniele Palmas 	if (!(ctx->drvflags & CDC_MBIM_FLAG_AVOID_ALTSETTING_TOGGLE))
84948906f62SBjørn Mork 		usb_set_interface(dev->udev, iface_no, data_altsetting);
8507b8076ceSDaniele Palmas 
851900d495aSAlexey Orishko 	temp = usb_set_interface(dev->udev, iface_no, 0);
852296e81f8SBjørn Mork 	if (temp) {
853296e81f8SBjørn Mork 		dev_dbg(&intf->dev, "set interface failed\n");
85419694ac8SAlexey Orishko 		goto error2;
855296e81f8SBjørn Mork 	}
856900d495aSAlexey Orishko 
85708c74fc9SBjørn Mork 	/* initialize basic device settings */
85808c74fc9SBjørn Mork 	if (cdc_ncm_init(dev))
859ff0992e9SBjørn Mork 		goto error2;
860ff0992e9SBjørn Mork 
861c086e709SBjørn Mork 	/* Some firmwares need a pause here or they will silently fail
862c086e709SBjørn Mork 	 * to set up the interface properly.  This value was decided
863c086e709SBjørn Mork 	 * empirically on a Sierra Wireless MC7455 running 02.08.02.00
864c086e709SBjørn Mork 	 * firmware.
865c086e709SBjørn Mork 	 */
866c086e709SBjørn Mork 	usleep_range(10000, 20000);
867c086e709SBjørn Mork 
868900d495aSAlexey Orishko 	/* configure data interface */
86938396e4cSGreg Suarez 	temp = usb_set_interface(dev->udev, iface_no, data_altsetting);
870296e81f8SBjørn Mork 	if (temp) {
871296e81f8SBjørn Mork 		dev_dbg(&intf->dev, "set interface failed\n");
87219694ac8SAlexey Orishko 		goto error2;
873296e81f8SBjørn Mork 	}
874900d495aSAlexey Orishko 
875ff1632aaSBjørn Mork 	cdc_ncm_find_endpoints(dev, ctx->data);
876ff1632aaSBjørn Mork 	cdc_ncm_find_endpoints(dev, ctx->control);
877296e81f8SBjørn Mork 	if (!dev->in || !dev->out || !dev->status) {
878296e81f8SBjørn Mork 		dev_dbg(&intf->dev, "failed to collect endpoints\n");
87919694ac8SAlexey Orishko 		goto error2;
880296e81f8SBjørn Mork 	}
881900d495aSAlexey Orishko 
882900d495aSAlexey Orishko 	usb_set_intfdata(ctx->data, dev);
883900d495aSAlexey Orishko 	usb_set_intfdata(ctx->control, dev);
884900d495aSAlexey Orishko 
88538396e4cSGreg Suarez 	if (ctx->ether_desc) {
886900d495aSAlexey Orishko 		temp = usbnet_get_ethernet_addr(dev, ctx->ether_desc->iMACAddress);
887296e81f8SBjørn Mork 		if (temp) {
888296e81f8SBjørn Mork 			dev_dbg(&intf->dev, "failed to get mac address\n");
88919694ac8SAlexey Orishko 			goto error2;
890296e81f8SBjørn Mork 		}
891296e81f8SBjørn Mork 		dev_info(&intf->dev, "MAC-Address: %pM\n", dev->net->dev_addr);
89238396e4cSGreg Suarez 	}
89338396e4cSGreg Suarez 
89408c74fc9SBjørn Mork 	/* finish setting up the device specific data */
89508c74fc9SBjørn Mork 	cdc_ncm_setup(dev);
896ff0992e9SBjørn Mork 
8974a0e3e98SEnrico Mioso 	/* Allocate the delayed NDP if needed. */
8984a0e3e98SEnrico Mioso 	if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END) {
8994a0e3e98SEnrico Mioso 		ctx->delayed_ndp16 = kzalloc(ctx->max_ndp_size, GFP_KERNEL);
9004a0e3e98SEnrico Mioso 		if (!ctx->delayed_ndp16)
9014a0e3e98SEnrico Mioso 			goto error2;
9024a0e3e98SEnrico Mioso 		dev_info(&intf->dev, "NDP will be placed at end of frame for this device.");
9034a0e3e98SEnrico Mioso 	}
9044a0e3e98SEnrico Mioso 
9056c4e548fSBjørn Mork 	/* override ethtool_ops */
9066c4e548fSBjørn Mork 	dev->net->ethtool_ops = &cdc_ncm_ethtool_ops;
9076c4e548fSBjørn Mork 
908289507d3SBjørn Mork 	/* add our sysfs attrs */
909289507d3SBjørn Mork 	dev->net->sysfs_groups[0] = &cdc_ncm_sysfs_attr_group;
910289507d3SBjørn Mork 
9111dfddff5SBjørn Mork 	/* must handle MTU changes */
9121dfddff5SBjørn Mork 	dev->net->netdev_ops = &cdc_ncm_netdev_ops;
913f77f0aeeSJarod Wilson 	dev->net->max_mtu = cdc_ncm_max_dgram_size(dev) - cdc_ncm_eth_hlen(dev);
9141dfddff5SBjørn Mork 
915900d495aSAlexey Orishko 	return 0;
916900d495aSAlexey Orishko 
91719694ac8SAlexey Orishko error2:
91819694ac8SAlexey Orishko 	usb_set_intfdata(ctx->control, NULL);
91919694ac8SAlexey Orishko 	usb_set_intfdata(ctx->data, NULL);
9206b4ef602SBjørn Mork 	if (ctx->data != ctx->control)
92119694ac8SAlexey Orishko 		usb_driver_release_interface(driver, ctx->data);
922900d495aSAlexey Orishko error:
923900d495aSAlexey Orishko 	cdc_ncm_free((struct cdc_ncm_ctx *)dev->data[0]);
924900d495aSAlexey Orishko 	dev->data[0] = 0;
925296e81f8SBjørn Mork 	dev_info(&intf->dev, "bind() failure\n");
926900d495aSAlexey Orishko 	return -ENODEV;
927900d495aSAlexey Orishko }
928c91ce3b6SBjørn Mork EXPORT_SYMBOL_GPL(cdc_ncm_bind_common);
929900d495aSAlexey Orishko 
930c91ce3b6SBjørn Mork void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf)
931900d495aSAlexey Orishko {
932900d495aSAlexey Orishko 	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
93319694ac8SAlexey Orishko 	struct usb_driver *driver = driver_of(intf);
934900d495aSAlexey Orishko 
935900d495aSAlexey Orishko 	if (ctx == NULL)
936900d495aSAlexey Orishko 		return;		/* no setup */
937900d495aSAlexey Orishko 
938c84ff1d6SAlexey Orishko 	atomic_set(&ctx->stop, 1);
939c84ff1d6SAlexey Orishko 
940c84ff1d6SAlexey Orishko 	if (hrtimer_active(&ctx->tx_timer))
941c84ff1d6SAlexey Orishko 		hrtimer_cancel(&ctx->tx_timer);
942c84ff1d6SAlexey Orishko 
943c84ff1d6SAlexey Orishko 	tasklet_kill(&ctx->bh);
944c84ff1d6SAlexey Orishko 
945bbc8d922SBjørn Mork 	/* handle devices with combined control and data interface */
946bbc8d922SBjørn Mork 	if (ctx->control == ctx->data)
947bbc8d922SBjørn Mork 		ctx->data = NULL;
948bbc8d922SBjørn Mork 
94919694ac8SAlexey Orishko 	/* disconnect master --> disconnect slave */
95019694ac8SAlexey Orishko 	if (intf == ctx->control && ctx->data) {
951900d495aSAlexey Orishko 		usb_set_intfdata(ctx->data, NULL);
952900d495aSAlexey Orishko 		usb_driver_release_interface(driver, ctx->data);
95319694ac8SAlexey Orishko 		ctx->data = NULL;
954900d495aSAlexey Orishko 
95519694ac8SAlexey Orishko 	} else if (intf == ctx->data && ctx->control) {
95619694ac8SAlexey Orishko 		usb_set_intfdata(ctx->control, NULL);
957900d495aSAlexey Orishko 		usb_driver_release_interface(driver, ctx->control);
95819694ac8SAlexey Orishko 		ctx->control = NULL;
959900d495aSAlexey Orishko 	}
960900d495aSAlexey Orishko 
9613e515665SBjørn Mork 	usb_set_intfdata(intf, NULL);
962900d495aSAlexey Orishko 	cdc_ncm_free(ctx);
963900d495aSAlexey Orishko }
964c91ce3b6SBjørn Mork EXPORT_SYMBOL_GPL(cdc_ncm_unbind);
965900d495aSAlexey Orishko 
96650a0ffafSBjørn Mork /* Return the number of the MBIM control interface altsetting iff it
96750a0ffafSBjørn Mork  * is preferred and available,
9681e8bbe6cSBjørn Mork  */
96950a0ffafSBjørn Mork u8 cdc_ncm_select_altsetting(struct usb_interface *intf)
97038396e4cSGreg Suarez {
9711e8bbe6cSBjørn Mork 	struct usb_host_interface *alt;
97238396e4cSGreg Suarez 
973bd329e12SBjørn Mork 	/* The MBIM spec defines a NCM compatible default altsetting,
974bd329e12SBjørn Mork 	 * which we may have matched:
975bd329e12SBjørn Mork 	 *
976bd329e12SBjørn Mork 	 *  "Functions that implement both NCM 1.0 and MBIM (an
977bd329e12SBjørn Mork 	 *   “NCM/MBIM function”) according to this recommendation
978bd329e12SBjørn Mork 	 *   shall provide two alternate settings for the
979bd329e12SBjørn Mork 	 *   Communication Interface.  Alternate setting 0, and the
980bd329e12SBjørn Mork 	 *   associated class and endpoint descriptors, shall be
981bd329e12SBjørn Mork 	 *   constructed according to the rules given for the
982bd329e12SBjørn Mork 	 *   Communication Interface in section 5 of [USBNCM10].
983bd329e12SBjørn Mork 	 *   Alternate setting 1, and the associated class and
984bd329e12SBjørn Mork 	 *   endpoint descriptors, shall be constructed according to
985bd329e12SBjørn Mork 	 *   the rules given in section 6 (USB Device Model) of this
986bd329e12SBjørn Mork 	 *   specification."
987bd329e12SBjørn Mork 	 */
98850a0ffafSBjørn Mork 	if (intf->num_altsetting < 2)
98950a0ffafSBjørn Mork 		return intf->cur_altsetting->desc.bAlternateSetting;
99050a0ffafSBjørn Mork 
99150a0ffafSBjørn Mork 	if (prefer_mbim) {
9921e8bbe6cSBjørn Mork 		alt = usb_altnum_to_altsetting(intf, CDC_NCM_COMM_ALTSETTING_MBIM);
99350a0ffafSBjørn Mork 		if (alt && cdc_ncm_comm_intf_is_mbim(alt))
99450a0ffafSBjørn Mork 			return CDC_NCM_COMM_ALTSETTING_MBIM;
9951e8bbe6cSBjørn Mork 	}
99650a0ffafSBjørn Mork 	return CDC_NCM_COMM_ALTSETTING_NCM;
9971e8bbe6cSBjørn Mork }
9981e8bbe6cSBjørn Mork EXPORT_SYMBOL_GPL(cdc_ncm_select_altsetting);
9991e8bbe6cSBjørn Mork 
10001e8bbe6cSBjørn Mork static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)
10011e8bbe6cSBjørn Mork {
10021e8bbe6cSBjørn Mork 	/* MBIM backwards compatible function? */
100350a0ffafSBjørn Mork 	if (cdc_ncm_select_altsetting(intf) != CDC_NCM_COMM_ALTSETTING_NCM)
1004bd329e12SBjørn Mork 		return -ENODEV;
1005bd329e12SBjørn Mork 
10064a0e3e98SEnrico Mioso 	/* The NCM data altsetting is fixed, so we hard-coded it.
10074a0e3e98SEnrico Mioso 	 * Additionally, generic NCM devices are assumed to accept arbitrarily
10084a0e3e98SEnrico Mioso 	 * placed NDP.
10094a0e3e98SEnrico Mioso 	 */
10104d06dd53SBjørn Mork 	return cdc_ncm_bind_common(dev, intf, CDC_NCM_DATA_ALTSETTING_NCM, 0);
101138396e4cSGreg Suarez }
101238396e4cSGreg Suarez 
1013c78b7c58SBjørn Mork static void cdc_ncm_align_tail(struct sk_buff *skb, size_t modulus, size_t remainder, size_t max)
1014900d495aSAlexey Orishko {
1015c78b7c58SBjørn Mork 	size_t align = ALIGN(skb->len, modulus) - skb->len + remainder;
1016c78b7c58SBjørn Mork 
1017c78b7c58SBjørn Mork 	if (skb->len + align > max)
1018c78b7c58SBjørn Mork 		align = max - skb->len;
1019c78b7c58SBjørn Mork 	if (align && skb_tailroom(skb) >= align)
1020b080db58SJohannes Berg 		skb_put_zero(skb, align);
1021c78b7c58SBjørn Mork }
1022c78b7c58SBjørn Mork 
1023c78b7c58SBjørn Mork /* return a pointer to a valid struct usb_cdc_ncm_ndp16 of type sign, possibly
1024c78b7c58SBjørn Mork  * allocating a new one within skb
1025c78b7c58SBjørn Mork  */
1026c78b7c58SBjørn Mork static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign, size_t reserve)
1027c78b7c58SBjørn Mork {
1028c78b7c58SBjørn Mork 	struct usb_cdc_ncm_ndp16 *ndp16 = NULL;
1029c78b7c58SBjørn Mork 	struct usb_cdc_ncm_nth16 *nth16 = (void *)skb->data;
1030c78b7c58SBjørn Mork 	size_t ndpoffset = le16_to_cpu(nth16->wNdpIndex);
1031c78b7c58SBjørn Mork 
10324a0e3e98SEnrico Mioso 	/* If NDP should be moved to the end of the NCM package, we can't follow the
10334a0e3e98SEnrico Mioso 	* NTH16 header as we would normally do. NDP isn't written to the SKB yet, and
10344a0e3e98SEnrico Mioso 	* the wNdpIndex field in the header is actually not consistent with reality. It will be later.
10354a0e3e98SEnrico Mioso 	*/
1036f8c0cfa5SBjørn Mork 	if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END) {
10374a0e3e98SEnrico Mioso 		if (ctx->delayed_ndp16->dwSignature == sign)
10384a0e3e98SEnrico Mioso 			return ctx->delayed_ndp16;
10394a0e3e98SEnrico Mioso 
1040f8c0cfa5SBjørn Mork 		/* We can only push a single NDP to the end. Return
1041f8c0cfa5SBjørn Mork 		 * NULL to send what we've already got and queue this
1042f8c0cfa5SBjørn Mork 		 * skb for later.
1043f8c0cfa5SBjørn Mork 		 */
1044f8c0cfa5SBjørn Mork 		else if (ctx->delayed_ndp16->dwSignature)
1045f8c0cfa5SBjørn Mork 			return NULL;
1046f8c0cfa5SBjørn Mork 	}
1047f8c0cfa5SBjørn Mork 
1048c78b7c58SBjørn Mork 	/* follow the chain of NDPs, looking for a match */
1049c78b7c58SBjørn Mork 	while (ndpoffset) {
1050c78b7c58SBjørn Mork 		ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb->data + ndpoffset);
1051c78b7c58SBjørn Mork 		if  (ndp16->dwSignature == sign)
1052c78b7c58SBjørn Mork 			return ndp16;
1053c78b7c58SBjørn Mork 		ndpoffset = le16_to_cpu(ndp16->wNextNdpIndex);
1054c78b7c58SBjørn Mork 	}
1055c78b7c58SBjørn Mork 
1056c78b7c58SBjørn Mork 	/* align new NDP */
10574a0e3e98SEnrico Mioso 	if (!(ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END))
1058c78b7c58SBjørn Mork 		cdc_ncm_align_tail(skb, ctx->tx_ndp_modulus, 0, ctx->tx_max);
1059c78b7c58SBjørn Mork 
1060c78b7c58SBjørn Mork 	/* verify that there is room for the NDP and the datagram (reserve) */
106170559b89SBjørn Mork 	if ((ctx->tx_max - skb->len - reserve) < ctx->max_ndp_size)
1062c78b7c58SBjørn Mork 		return NULL;
1063c78b7c58SBjørn Mork 
1064c78b7c58SBjørn Mork 	/* link to it */
1065c78b7c58SBjørn Mork 	if (ndp16)
1066c78b7c58SBjørn Mork 		ndp16->wNextNdpIndex = cpu_to_le16(skb->len);
1067c78b7c58SBjørn Mork 	else
1068c78b7c58SBjørn Mork 		nth16->wNdpIndex = cpu_to_le16(skb->len);
1069c78b7c58SBjørn Mork 
1070c78b7c58SBjørn Mork 	/* push a new empty NDP */
10714a0e3e98SEnrico Mioso 	if (!(ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END))
107270559b89SBjørn Mork 		ndp16 = (struct usb_cdc_ncm_ndp16 *)memset(skb_put(skb, ctx->max_ndp_size), 0, ctx->max_ndp_size);
10734a0e3e98SEnrico Mioso 	else
10744a0e3e98SEnrico Mioso 		ndp16 = ctx->delayed_ndp16;
10754a0e3e98SEnrico Mioso 
1076c78b7c58SBjørn Mork 	ndp16->dwSignature = sign;
1077c78b7c58SBjørn Mork 	ndp16->wLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_ndp16) + sizeof(struct usb_cdc_ncm_dpe16));
1078c78b7c58SBjørn Mork 	return ndp16;
1079900d495aSAlexey Orishko }
1080900d495aSAlexey Orishko 
1081c91ce3b6SBjørn Mork struct sk_buff *
1082bed6f762SBjørn Mork cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
1083900d495aSAlexey Orishko {
1084bed6f762SBjørn Mork 	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
1085c78b7c58SBjørn Mork 	struct usb_cdc_ncm_nth16 *nth16;
1086c78b7c58SBjørn Mork 	struct usb_cdc_ncm_ndp16 *ndp16;
1087900d495aSAlexey Orishko 	struct sk_buff *skb_out;
1088c78b7c58SBjørn Mork 	u16 n = 0, index, ndplen;
108984e77a8bSAlexey Orishko 	u8 ready2send = 0;
10904a0e3e98SEnrico Mioso 	u32 delayed_ndp_size;
1091aeca3a77SJim Baxter 	size_t padding_count;
10924a0e3e98SEnrico Mioso 
10934a0e3e98SEnrico Mioso 	/* When our NDP gets written in cdc_ncm_ndp(), then skb_out->len gets updated
10944a0e3e98SEnrico Mioso 	 * accordingly. Otherwise, we should check here.
10954a0e3e98SEnrico Mioso 	 */
10964a0e3e98SEnrico Mioso 	if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END)
10974a0e3e98SEnrico Mioso 		delayed_ndp_size = ctx->max_ndp_size;
10984a0e3e98SEnrico Mioso 	else
10994a0e3e98SEnrico Mioso 		delayed_ndp_size = 0;
1100900d495aSAlexey Orishko 
1101900d495aSAlexey Orishko 	/* if there is a remaining skb, it gets priority */
1102c78b7c58SBjørn Mork 	if (skb != NULL) {
1103900d495aSAlexey Orishko 		swap(skb, ctx->tx_rem_skb);
1104c78b7c58SBjørn Mork 		swap(sign, ctx->tx_rem_sign);
1105c78b7c58SBjørn Mork 	} else {
110684e77a8bSAlexey Orishko 		ready2send = 1;
1107c78b7c58SBjørn Mork 	}
1108900d495aSAlexey Orishko 
1109900d495aSAlexey Orishko 	/* check if we are resuming an OUT skb */
1110900d495aSAlexey Orishko 	skb_out = ctx->tx_curr_skb;
1111900d495aSAlexey Orishko 
1112c78b7c58SBjørn Mork 	/* allocate a new OUT skb */
1113c78b7c58SBjørn Mork 	if (!skb_out) {
111420572226SBjørn Mork 		skb_out = alloc_skb(ctx->tx_max, GFP_ATOMIC);
1115900d495aSAlexey Orishko 		if (skb_out == NULL) {
1116900d495aSAlexey Orishko 			if (skb != NULL) {
1117900d495aSAlexey Orishko 				dev_kfree_skb_any(skb);
1118bed6f762SBjørn Mork 				dev->net->stats.tx_dropped++;
1119900d495aSAlexey Orishko 			}
1120900d495aSAlexey Orishko 			goto exit_no_skb;
1121900d495aSAlexey Orishko 		}
1122c78b7c58SBjørn Mork 		/* fill out the initial 16-bit NTB header */
1123c78b7c58SBjørn Mork 		nth16 = (struct usb_cdc_ncm_nth16 *)memset(skb_put(skb_out, sizeof(struct usb_cdc_ncm_nth16)), 0, sizeof(struct usb_cdc_ncm_nth16));
1124c78b7c58SBjørn Mork 		nth16->dwSignature = cpu_to_le32(USB_CDC_NCM_NTH16_SIGN);
1125c78b7c58SBjørn Mork 		nth16->wHeaderLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_nth16));
1126c78b7c58SBjørn Mork 		nth16->wSequence = cpu_to_le16(ctx->tx_seq++);
1127900d495aSAlexey Orishko 
1128c78b7c58SBjørn Mork 		/* count total number of frames in this NTB */
1129900d495aSAlexey Orishko 		ctx->tx_curr_frame_num = 0;
1130beeecd42SBjørn Mork 
1131beeecd42SBjørn Mork 		/* recent payload counter for this skb_out */
1132beeecd42SBjørn Mork 		ctx->tx_curr_frame_payload = 0;
1133900d495aSAlexey Orishko 	}
1134900d495aSAlexey Orishko 
1135c78b7c58SBjørn Mork 	for (n = ctx->tx_curr_frame_num; n < ctx->tx_max_datagrams; n++) {
1136c78b7c58SBjørn Mork 		/* send any remaining skb first */
1137900d495aSAlexey Orishko 		if (skb == NULL) {
1138900d495aSAlexey Orishko 			skb = ctx->tx_rem_skb;
1139c78b7c58SBjørn Mork 			sign = ctx->tx_rem_sign;
1140900d495aSAlexey Orishko 			ctx->tx_rem_skb = NULL;
1141900d495aSAlexey Orishko 
1142900d495aSAlexey Orishko 			/* check for end of skb */
1143900d495aSAlexey Orishko 			if (skb == NULL)
1144900d495aSAlexey Orishko 				break;
1145900d495aSAlexey Orishko 		}
1146900d495aSAlexey Orishko 
1147c78b7c58SBjørn Mork 		/* get the appropriate NDP for this skb */
1148c78b7c58SBjørn Mork 		ndp16 = cdc_ncm_ndp(ctx, skb_out, sign, skb->len + ctx->tx_modulus + ctx->tx_remainder);
1149c78b7c58SBjørn Mork 
1150c78b7c58SBjørn Mork 		/* align beginning of next frame */
1151c78b7c58SBjørn Mork 		cdc_ncm_align_tail(skb_out,  ctx->tx_modulus, ctx->tx_remainder, ctx->tx_max);
1152c78b7c58SBjørn Mork 
1153c78b7c58SBjørn Mork 		/* check if we had enough room left for both NDP and frame */
11544a0e3e98SEnrico Mioso 		if (!ndp16 || skb_out->len + skb->len + delayed_ndp_size > ctx->tx_max) {
1155900d495aSAlexey Orishko 			if (n == 0) {
1156900d495aSAlexey Orishko 				/* won't fit, MTU problem? */
1157900d495aSAlexey Orishko 				dev_kfree_skb_any(skb);
1158900d495aSAlexey Orishko 				skb = NULL;
1159bed6f762SBjørn Mork 				dev->net->stats.tx_dropped++;
1160900d495aSAlexey Orishko 			} else {
1161900d495aSAlexey Orishko 				/* no room for skb - store for later */
1162900d495aSAlexey Orishko 				if (ctx->tx_rem_skb != NULL) {
1163900d495aSAlexey Orishko 					dev_kfree_skb_any(ctx->tx_rem_skb);
1164bed6f762SBjørn Mork 					dev->net->stats.tx_dropped++;
1165900d495aSAlexey Orishko 				}
1166900d495aSAlexey Orishko 				ctx->tx_rem_skb = skb;
1167c78b7c58SBjørn Mork 				ctx->tx_rem_sign = sign;
1168900d495aSAlexey Orishko 				skb = NULL;
116984e77a8bSAlexey Orishko 				ready2send = 1;
1170beeecd42SBjørn Mork 				ctx->tx_reason_ntb_full++;	/* count reason for transmitting */
1171900d495aSAlexey Orishko 			}
1172900d495aSAlexey Orishko 			break;
1173900d495aSAlexey Orishko 		}
1174900d495aSAlexey Orishko 
1175c78b7c58SBjørn Mork 		/* calculate frame number withing this NDP */
1176c78b7c58SBjørn Mork 		ndplen = le16_to_cpu(ndp16->wLength);
1177c78b7c58SBjørn Mork 		index = (ndplen - sizeof(struct usb_cdc_ncm_ndp16)) / sizeof(struct usb_cdc_ncm_dpe16) - 1;
1178900d495aSAlexey Orishko 
1179c78b7c58SBjørn Mork 		/* OK, add this skb */
1180c78b7c58SBjørn Mork 		ndp16->dpe16[index].wDatagramLength = cpu_to_le16(skb->len);
1181c78b7c58SBjørn Mork 		ndp16->dpe16[index].wDatagramIndex = cpu_to_le16(skb_out->len);
1182c78b7c58SBjørn Mork 		ndp16->wLength = cpu_to_le16(ndplen + sizeof(struct usb_cdc_ncm_dpe16));
118359ae1d12SJohannes Berg 		skb_put_data(skb_out, skb->data, skb->len);
1184beeecd42SBjørn Mork 		ctx->tx_curr_frame_payload += skb->len;	/* count real tx payload data */
1185900d495aSAlexey Orishko 		dev_kfree_skb_any(skb);
1186900d495aSAlexey Orishko 		skb = NULL;
1187c78b7c58SBjørn Mork 
1188c78b7c58SBjørn Mork 		/* send now if this NDP is full */
1189c78b7c58SBjørn Mork 		if (index >= CDC_NCM_DPT_DATAGRAMS_MAX) {
1190c78b7c58SBjørn Mork 			ready2send = 1;
1191beeecd42SBjørn Mork 			ctx->tx_reason_ndp_full++;	/* count reason for transmitting */
1192c78b7c58SBjørn Mork 			break;
1193c78b7c58SBjørn Mork 		}
1194900d495aSAlexey Orishko 	}
1195900d495aSAlexey Orishko 
1196900d495aSAlexey Orishko 	/* free up any dangling skb */
1197900d495aSAlexey Orishko 	if (skb != NULL) {
1198900d495aSAlexey Orishko 		dev_kfree_skb_any(skb);
1199900d495aSAlexey Orishko 		skb = NULL;
1200bed6f762SBjørn Mork 		dev->net->stats.tx_dropped++;
1201900d495aSAlexey Orishko 	}
1202900d495aSAlexey Orishko 
1203900d495aSAlexey Orishko 	ctx->tx_curr_frame_num = n;
1204900d495aSAlexey Orishko 
1205900d495aSAlexey Orishko 	if (n == 0) {
1206900d495aSAlexey Orishko 		/* wait for more frames */
1207900d495aSAlexey Orishko 		/* push variables */
1208900d495aSAlexey Orishko 		ctx->tx_curr_skb = skb_out;
1209900d495aSAlexey Orishko 		goto exit_no_skb;
1210900d495aSAlexey Orishko 
12116c4e548fSBjørn Mork 	} else if ((n < ctx->tx_max_datagrams) && (ready2send == 0) && (ctx->timer_interval > 0)) {
1212900d495aSAlexey Orishko 		/* wait for more frames */
1213900d495aSAlexey Orishko 		/* push variables */
1214900d495aSAlexey Orishko 		ctx->tx_curr_skb = skb_out;
1215900d495aSAlexey Orishko 		/* set the pending count */
1216900d495aSAlexey Orishko 		if (n < CDC_NCM_RESTART_TIMER_DATAGRAM_CNT)
1217c84ff1d6SAlexey Orishko 			ctx->tx_timer_pending = CDC_NCM_TIMER_PENDING_CNT;
1218900d495aSAlexey Orishko 		goto exit_no_skb;
1219900d495aSAlexey Orishko 
1220900d495aSAlexey Orishko 	} else {
1221beeecd42SBjørn Mork 		if (n == ctx->tx_max_datagrams)
1222beeecd42SBjørn Mork 			ctx->tx_reason_max_datagram++;	/* count reason for transmitting */
1223900d495aSAlexey Orishko 		/* frame goes out */
1224900d495aSAlexey Orishko 		/* variables will be reset at next call */
1225900d495aSAlexey Orishko 	}
1226900d495aSAlexey Orishko 
12274a0e3e98SEnrico Mioso 	/* If requested, put NDP at end of frame. */
12284a0e3e98SEnrico Mioso 	if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END) {
12294a0e3e98SEnrico Mioso 		nth16 = (struct usb_cdc_ncm_nth16 *)skb_out->data;
12304a0e3e98SEnrico Mioso 		cdc_ncm_align_tail(skb_out, ctx->tx_ndp_modulus, 0, ctx->tx_max);
12314a0e3e98SEnrico Mioso 		nth16->wNdpIndex = cpu_to_le16(skb_out->len);
123259ae1d12SJohannes Berg 		skb_put_data(skb_out, ctx->delayed_ndp16, ctx->max_ndp_size);
12334a0e3e98SEnrico Mioso 
12344a0e3e98SEnrico Mioso 		/* Zero out delayed NDP - signature checking will naturally fail. */
12354a0e3e98SEnrico Mioso 		ndp16 = memset(ctx->delayed_ndp16, 0, ctx->max_ndp_size);
12364a0e3e98SEnrico Mioso 	}
12374a0e3e98SEnrico Mioso 
123843e4c6dfSBjørn Mork 	/* If collected data size is less or equal ctx->min_tx_pkt
123920572226SBjørn Mork 	 * bytes, we send buffers as it is. If we get more data, it
124020572226SBjørn Mork 	 * would be more efficient for USB HS mobile device with DMA
124120572226SBjørn Mork 	 * engine to receive a full size NTB, than canceling DMA
124220572226SBjørn Mork 	 * transfer and receiving a short packet.
12434d619f62SBjørn Mork 	 *
12444d619f62SBjørn Mork 	 * This optimization support is pointless if we end up sending
12454d619f62SBjørn Mork 	 * a ZLP after full sized NTBs.
1246900d495aSAlexey Orishko 	 */
12474d619f62SBjørn Mork 	if (!(dev->driver_info->flags & FLAG_SEND_ZLP) &&
1248aeca3a77SJim Baxter 	    skb_out->len > ctx->min_tx_pkt) {
1249aeca3a77SJim Baxter 		padding_count = ctx->tx_max - skb_out->len;
1250b080db58SJohannes Berg 		skb_put_zero(skb_out, padding_count);
1251aeca3a77SJim Baxter 	} else if (skb_out->len < ctx->tx_max &&
1252aeca3a77SJim Baxter 		   (skb_out->len % dev->maxpacket) == 0) {
1253634fef61SJohannes Berg 		skb_put_u8(skb_out, 0);	/* force short packet */
1254aeca3a77SJim Baxter 	}
1255900d495aSAlexey Orishko 
1256c78b7c58SBjørn Mork 	/* set final frame length */
1257c78b7c58SBjørn Mork 	nth16 = (struct usb_cdc_ncm_nth16 *)skb_out->data;
1258c78b7c58SBjørn Mork 	nth16->wBlockLength = cpu_to_le16(skb_out->len);
1259900d495aSAlexey Orishko 
1260900d495aSAlexey Orishko 	/* return skb */
1261900d495aSAlexey Orishko 	ctx->tx_curr_skb = NULL;
1262beeecd42SBjørn Mork 
1263beeecd42SBjørn Mork 	/* keep private stats: framing overhead and number of NTBs */
1264beeecd42SBjørn Mork 	ctx->tx_overhead += skb_out->len - ctx->tx_curr_frame_payload;
1265beeecd42SBjørn Mork 	ctx->tx_ntbs++;
1266beeecd42SBjørn Mork 
12677a1e890eSBen Hutchings 	/* usbnet will count all the framing overhead by default.
1268beeecd42SBjørn Mork 	 * Adjust the stats so that the tx_bytes counter show real
1269beeecd42SBjørn Mork 	 * payload data instead.
1270beeecd42SBjørn Mork 	 */
12717a1e890eSBen Hutchings 	usbnet_set_skb_tx_stats(skb_out, n,
127244f6731dSBjørn Mork 				(long)ctx->tx_curr_frame_payload - skb_out->len);
12731e9e39f4SBen Hutchings 
1274900d495aSAlexey Orishko 	return skb_out;
1275900d495aSAlexey Orishko 
1276900d495aSAlexey Orishko exit_no_skb:
1277046c6594SBjørn Mork 	/* Start timer, if there is a remaining non-empty skb */
1278046c6594SBjørn Mork 	if (ctx->tx_curr_skb != NULL && n > 0)
1279c84ff1d6SAlexey Orishko 		cdc_ncm_tx_timeout_start(ctx);
1280900d495aSAlexey Orishko 	return NULL;
1281900d495aSAlexey Orishko }
1282c91ce3b6SBjørn Mork EXPORT_SYMBOL_GPL(cdc_ncm_fill_tx_frame);
1283900d495aSAlexey Orishko 
1284900d495aSAlexey Orishko static void cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx)
1285900d495aSAlexey Orishko {
1286900d495aSAlexey Orishko 	/* start timer, if not already started */
1287c84ff1d6SAlexey Orishko 	if (!(hrtimer_active(&ctx->tx_timer) || atomic_read(&ctx->stop)))
1288c84ff1d6SAlexey Orishko 		hrtimer_start(&ctx->tx_timer,
12898b0e1953SThomas Gleixner 				ctx->timer_interval,
1290c84ff1d6SAlexey Orishko 				HRTIMER_MODE_REL);
1291900d495aSAlexey Orishko }
1292900d495aSAlexey Orishko 
1293c84ff1d6SAlexey Orishko static enum hrtimer_restart cdc_ncm_tx_timer_cb(struct hrtimer *timer)
1294900d495aSAlexey Orishko {
1295c84ff1d6SAlexey Orishko 	struct cdc_ncm_ctx *ctx =
1296c84ff1d6SAlexey Orishko 			container_of(timer, struct cdc_ncm_ctx, tx_timer);
1297900d495aSAlexey Orishko 
1298c84ff1d6SAlexey Orishko 	if (!atomic_read(&ctx->stop))
1299c84ff1d6SAlexey Orishko 		tasklet_schedule(&ctx->bh);
1300c84ff1d6SAlexey Orishko 	return HRTIMER_NORESTART;
1301c84ff1d6SAlexey Orishko }
1302c84ff1d6SAlexey Orishko 
1303c84ff1d6SAlexey Orishko static void cdc_ncm_txpath_bh(unsigned long param)
1304c84ff1d6SAlexey Orishko {
1305bed6f762SBjørn Mork 	struct usbnet *dev = (struct usbnet *)param;
1306bed6f762SBjørn Mork 	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
1307c84ff1d6SAlexey Orishko 
1308c84ff1d6SAlexey Orishko 	spin_lock_bh(&ctx->mtx);
1309900d495aSAlexey Orishko 	if (ctx->tx_timer_pending != 0) {
1310900d495aSAlexey Orishko 		ctx->tx_timer_pending--;
1311900d495aSAlexey Orishko 		cdc_ncm_tx_timeout_start(ctx);
1312c84ff1d6SAlexey Orishko 		spin_unlock_bh(&ctx->mtx);
1313bed6f762SBjørn Mork 	} else if (dev->net != NULL) {
1314beeecd42SBjørn Mork 		ctx->tx_reason_timeout++;	/* count reason for transmitting */
1315c84ff1d6SAlexey Orishko 		spin_unlock_bh(&ctx->mtx);
1316bed6f762SBjørn Mork 		netif_tx_lock_bh(dev->net);
1317bed6f762SBjørn Mork 		usbnet_start_xmit(NULL, dev->net);
1318bed6f762SBjørn Mork 		netif_tx_unlock_bh(dev->net);
13197b1e0cbaSBjørn Mork 	} else {
13207b1e0cbaSBjørn Mork 		spin_unlock_bh(&ctx->mtx);
1321900d495aSAlexey Orishko 	}
1322f742aa8aSAlexey Orishko }
1323900d495aSAlexey Orishko 
13242f69702cSEnrico Mioso struct sk_buff *
1325900d495aSAlexey Orishko cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
1326900d495aSAlexey Orishko {
1327900d495aSAlexey Orishko 	struct sk_buff *skb_out;
1328900d495aSAlexey Orishko 	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
1329900d495aSAlexey Orishko 
1330900d495aSAlexey Orishko 	/*
1331900d495aSAlexey Orishko 	 * The Ethernet API we are using does not support transmitting
1332900d495aSAlexey Orishko 	 * multiple Ethernet frames in a single call. This driver will
1333900d495aSAlexey Orishko 	 * accumulate multiple Ethernet frames and send out a larger
1334900d495aSAlexey Orishko 	 * USB frame when the USB buffer is full or when a single jiffies
1335900d495aSAlexey Orishko 	 * timeout happens.
1336900d495aSAlexey Orishko 	 */
1337900d495aSAlexey Orishko 	if (ctx == NULL)
1338900d495aSAlexey Orishko 		goto error;
1339900d495aSAlexey Orishko 
1340c84ff1d6SAlexey Orishko 	spin_lock_bh(&ctx->mtx);
1341bed6f762SBjørn Mork 	skb_out = cdc_ncm_fill_tx_frame(dev, skb, cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN));
1342c84ff1d6SAlexey Orishko 	spin_unlock_bh(&ctx->mtx);
1343900d495aSAlexey Orishko 	return skb_out;
1344900d495aSAlexey Orishko 
1345900d495aSAlexey Orishko error:
1346900d495aSAlexey Orishko 	if (skb != NULL)
1347900d495aSAlexey Orishko 		dev_kfree_skb_any(skb);
1348900d495aSAlexey Orishko 
1349900d495aSAlexey Orishko 	return NULL;
1350900d495aSAlexey Orishko }
13512f69702cSEnrico Mioso EXPORT_SYMBOL_GPL(cdc_ncm_tx_fixup);
1352900d495aSAlexey Orishko 
1353ff06ab13SBjørn Mork /* verify NTB header and return offset of first NDP, or negative error */
1354c91ce3b6SBjørn Mork int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in)
1355900d495aSAlexey Orishko {
1356ae223cd4SBjørn Mork 	struct usbnet *dev = netdev_priv(skb_in->dev);
1357d5ddb4a5SAlexey Orishko 	struct usb_cdc_ncm_nth16 *nth16;
1358ff06ab13SBjørn Mork 	int len;
1359ff06ab13SBjørn Mork 	int ret = -EINVAL;
1360900d495aSAlexey Orishko 
1361900d495aSAlexey Orishko 	if (ctx == NULL)
1362900d495aSAlexey Orishko 		goto error;
1363900d495aSAlexey Orishko 
1364d5ddb4a5SAlexey Orishko 	if (skb_in->len < (sizeof(struct usb_cdc_ncm_nth16) +
1365d5ddb4a5SAlexey Orishko 					sizeof(struct usb_cdc_ncm_ndp16))) {
1366ae223cd4SBjørn Mork 		netif_dbg(dev, rx_err, dev->net, "frame too short\n");
1367900d495aSAlexey Orishko 		goto error;
1368900d495aSAlexey Orishko 	}
1369900d495aSAlexey Orishko 
1370d5ddb4a5SAlexey Orishko 	nth16 = (struct usb_cdc_ncm_nth16 *)skb_in->data;
1371900d495aSAlexey Orishko 
1372986e10d6SBjørn Mork 	if (nth16->dwSignature != cpu_to_le32(USB_CDC_NCM_NTH16_SIGN)) {
13735448d75fSBjørn Mork 		netif_dbg(dev, rx_err, dev->net,
13745448d75fSBjørn Mork 			  "invalid NTH16 signature <%#010x>\n",
1375d5ddb4a5SAlexey Orishko 			  le32_to_cpu(nth16->dwSignature));
1376900d495aSAlexey Orishko 		goto error;
1377900d495aSAlexey Orishko 	}
1378900d495aSAlexey Orishko 
1379d5ddb4a5SAlexey Orishko 	len = le16_to_cpu(nth16->wBlockLength);
1380d5ddb4a5SAlexey Orishko 	if (len > ctx->rx_max) {
1381ae223cd4SBjørn Mork 		netif_dbg(dev, rx_err, dev->net,
1382ae223cd4SBjørn Mork 			  "unsupported NTB block length %u/%u\n", len,
1383d5ddb4a5SAlexey Orishko 			  ctx->rx_max);
1384900d495aSAlexey Orishko 		goto error;
1385900d495aSAlexey Orishko 	}
1386900d495aSAlexey Orishko 
1387d5ddb4a5SAlexey Orishko 	if ((ctx->rx_seq + 1) != le16_to_cpu(nth16->wSequence) &&
1388d5ddb4a5SAlexey Orishko 	    (ctx->rx_seq || le16_to_cpu(nth16->wSequence)) &&
1389d5ddb4a5SAlexey Orishko 	    !((ctx->rx_seq == 0xffff) && !le16_to_cpu(nth16->wSequence))) {
1390ae223cd4SBjørn Mork 		netif_dbg(dev, rx_err, dev->net,
1391ae223cd4SBjørn Mork 			  "sequence number glitch prev=%d curr=%d\n",
1392d5ddb4a5SAlexey Orishko 			  ctx->rx_seq, le16_to_cpu(nth16->wSequence));
1393d5ddb4a5SAlexey Orishko 	}
1394d5ddb4a5SAlexey Orishko 	ctx->rx_seq = le16_to_cpu(nth16->wSequence);
1395d5ddb4a5SAlexey Orishko 
1396ff06ab13SBjørn Mork 	ret = le16_to_cpu(nth16->wNdpIndex);
1397ff06ab13SBjørn Mork error:
1398ff06ab13SBjørn Mork 	return ret;
1399ff06ab13SBjørn Mork }
1400c91ce3b6SBjørn Mork EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_nth16);
1401ff06ab13SBjørn Mork 
1402ff06ab13SBjørn Mork /* verify NDP header and return number of datagrams, or negative error */
1403c91ce3b6SBjørn Mork int cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset)
1404ff06ab13SBjørn Mork {
1405ae223cd4SBjørn Mork 	struct usbnet *dev = netdev_priv(skb_in->dev);
1406ff06ab13SBjørn Mork 	struct usb_cdc_ncm_ndp16 *ndp16;
1407ff06ab13SBjørn Mork 	int ret = -EINVAL;
1408ff06ab13SBjørn Mork 
140975d67d35SBjørn Mork 	if ((ndpoffset + sizeof(struct usb_cdc_ncm_ndp16)) > skb_in->len) {
1410ae223cd4SBjørn Mork 		netif_dbg(dev, rx_err, dev->net, "invalid NDP offset  <%u>\n",
1411ae223cd4SBjørn Mork 			  ndpoffset);
1412900d495aSAlexey Orishko 		goto error;
1413900d495aSAlexey Orishko 	}
141475d67d35SBjørn Mork 	ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb_in->data + ndpoffset);
1415900d495aSAlexey Orishko 
1416ff06ab13SBjørn Mork 	if (le16_to_cpu(ndp16->wLength) < USB_CDC_NCM_NDP16_LENGTH_MIN) {
1417ae223cd4SBjørn Mork 		netif_dbg(dev, rx_err, dev->net, "invalid DPT16 length <%u>\n",
1418a26fd05dSBjørn Mork 			  le16_to_cpu(ndp16->wLength));
1419ff06ab13SBjørn Mork 		goto error;
1420ff06ab13SBjørn Mork 	}
1421ff06ab13SBjørn Mork 
1422ff06ab13SBjørn Mork 	ret = ((le16_to_cpu(ndp16->wLength) -
1423ff06ab13SBjørn Mork 					sizeof(struct usb_cdc_ncm_ndp16)) /
1424ff06ab13SBjørn Mork 					sizeof(struct usb_cdc_ncm_dpe16));
1425ff06ab13SBjørn Mork 	ret--; /* we process NDP entries except for the last one */
1426ff06ab13SBjørn Mork 
1427ae223cd4SBjørn Mork 	if ((sizeof(struct usb_cdc_ncm_ndp16) +
1428ae223cd4SBjørn Mork 	     ret * (sizeof(struct usb_cdc_ncm_dpe16))) > skb_in->len) {
1429ae223cd4SBjørn Mork 		netif_dbg(dev, rx_err, dev->net, "Invalid nframes = %d\n", ret);
1430ff06ab13SBjørn Mork 		ret = -EINVAL;
1431ff06ab13SBjørn Mork 	}
1432ff06ab13SBjørn Mork 
1433ff06ab13SBjørn Mork error:
1434ff06ab13SBjørn Mork 	return ret;
1435ff06ab13SBjørn Mork }
1436c91ce3b6SBjørn Mork EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_ndp16);
1437ff06ab13SBjørn Mork 
14382f69702cSEnrico Mioso int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
1439ff06ab13SBjørn Mork {
1440ff06ab13SBjørn Mork 	struct sk_buff *skb;
1441ff06ab13SBjørn Mork 	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
1442ff06ab13SBjørn Mork 	int len;
1443ff06ab13SBjørn Mork 	int nframes;
1444ff06ab13SBjørn Mork 	int x;
1445ff06ab13SBjørn Mork 	int offset;
1446ff06ab13SBjørn Mork 	struct usb_cdc_ncm_ndp16 *ndp16;
1447ff06ab13SBjørn Mork 	struct usb_cdc_ncm_dpe16 *dpe16;
1448ff06ab13SBjørn Mork 	int ndpoffset;
1449ff06ab13SBjørn Mork 	int loopcount = 50; /* arbitrary max preventing infinite loop */
1450beeecd42SBjørn Mork 	u32 payload = 0;
1451ff06ab13SBjørn Mork 
1452ff06ab13SBjørn Mork 	ndpoffset = cdc_ncm_rx_verify_nth16(ctx, skb_in);
1453ff06ab13SBjørn Mork 	if (ndpoffset < 0)
1454ff06ab13SBjørn Mork 		goto error;
1455ff06ab13SBjørn Mork 
1456ff06ab13SBjørn Mork next_ndp:
1457ff06ab13SBjørn Mork 	nframes = cdc_ncm_rx_verify_ndp16(skb_in, ndpoffset);
1458ff06ab13SBjørn Mork 	if (nframes < 0)
1459ff06ab13SBjørn Mork 		goto error;
1460ff06ab13SBjørn Mork 
1461ff06ab13SBjørn Mork 	ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb_in->data + ndpoffset);
1462ff06ab13SBjørn Mork 
1463986e10d6SBjørn Mork 	if (ndp16->dwSignature != cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN)) {
14645448d75fSBjørn Mork 		netif_dbg(dev, rx_err, dev->net,
14655448d75fSBjørn Mork 			  "invalid DPT16 signature <%#010x>\n",
1466d5ddb4a5SAlexey Orishko 			  le32_to_cpu(ndp16->dwSignature));
146775d67d35SBjørn Mork 		goto err_ndp;
1468900d495aSAlexey Orishko 	}
1469ff06ab13SBjørn Mork 	dpe16 = ndp16->dpe16;
1470900d495aSAlexey Orishko 
1471d5ddb4a5SAlexey Orishko 	for (x = 0; x < nframes; x++, dpe16++) {
1472d5ddb4a5SAlexey Orishko 		offset = le16_to_cpu(dpe16->wDatagramIndex);
1473d5ddb4a5SAlexey Orishko 		len = le16_to_cpu(dpe16->wDatagramLength);
1474900d495aSAlexey Orishko 
1475900d495aSAlexey Orishko 		/*
1476900d495aSAlexey Orishko 		 * CDC NCM ch. 3.7
1477900d495aSAlexey Orishko 		 * All entries after first NULL entry are to be ignored
1478900d495aSAlexey Orishko 		 */
1479d5ddb4a5SAlexey Orishko 		if ((offset == 0) || (len == 0)) {
1480900d495aSAlexey Orishko 			if (!x)
148175d67d35SBjørn Mork 				goto err_ndp; /* empty NTB */
1482900d495aSAlexey Orishko 			break;
1483900d495aSAlexey Orishko 		}
1484900d495aSAlexey Orishko 
1485900d495aSAlexey Orishko 		/* sanity checking */
1486d5ddb4a5SAlexey Orishko 		if (((offset + len) > skb_in->len) ||
1487d5ddb4a5SAlexey Orishko 				(len > ctx->rx_max) || (len < ETH_HLEN)) {
1488ae223cd4SBjørn Mork 			netif_dbg(dev, rx_err, dev->net,
1489ae223cd4SBjørn Mork 				  "invalid frame detected (ignored) offset[%u]=%u, length=%u, skb=%p\n",
1490d5ddb4a5SAlexey Orishko 				  x, offset, len, skb_in);
1491900d495aSAlexey Orishko 			if (!x)
149275d67d35SBjørn Mork 				goto err_ndp;
1493900d495aSAlexey Orishko 			break;
1494900d495aSAlexey Orishko 
1495900d495aSAlexey Orishko 		} else {
14961e2c6117SBjørn Mork 			/* create a fresh copy to reduce truesize */
14971e2c6117SBjørn Mork 			skb = netdev_alloc_skb_ip_align(dev->net,  len);
14989e56790aSJesper Juhl 			if (!skb)
14999e56790aSJesper Juhl 				goto error;
150059ae1d12SJohannes Berg 			skb_put_data(skb, skb_in->data + offset, len);
1501900d495aSAlexey Orishko 			usbnet_skb_return(dev, skb);
1502beeecd42SBjørn Mork 			payload += len;	/* count payload bytes in this NTB */
1503900d495aSAlexey Orishko 		}
1504900d495aSAlexey Orishko 	}
150575d67d35SBjørn Mork err_ndp:
150675d67d35SBjørn Mork 	/* are there more NDPs to process? */
150775d67d35SBjørn Mork 	ndpoffset = le16_to_cpu(ndp16->wNextNdpIndex);
150875d67d35SBjørn Mork 	if (ndpoffset && loopcount--)
150975d67d35SBjørn Mork 		goto next_ndp;
151075d67d35SBjørn Mork 
1511beeecd42SBjørn Mork 	/* update stats */
1512beeecd42SBjørn Mork 	ctx->rx_overhead += skb_in->len - payload;
1513beeecd42SBjørn Mork 	ctx->rx_ntbs++;
1514beeecd42SBjørn Mork 
1515900d495aSAlexey Orishko 	return 1;
1516900d495aSAlexey Orishko error:
1517900d495aSAlexey Orishko 	return 0;
1518900d495aSAlexey Orishko }
15192f69702cSEnrico Mioso EXPORT_SYMBOL_GPL(cdc_ncm_rx_fixup);
1520900d495aSAlexey Orishko 
1521900d495aSAlexey Orishko static void
1522bed6f762SBjørn Mork cdc_ncm_speed_change(struct usbnet *dev,
152384e77a8bSAlexey Orishko 		     struct usb_cdc_speed_change *data)
1524900d495aSAlexey Orishko {
152584e77a8bSAlexey Orishko 	uint32_t rx_speed = le32_to_cpu(data->DLBitRRate);
152684e77a8bSAlexey Orishko 	uint32_t tx_speed = le32_to_cpu(data->ULBitRate);
1527900d495aSAlexey Orishko 
1528900d495aSAlexey Orishko 	/*
1529900d495aSAlexey Orishko 	 * Currently the USB-NET API does not support reporting the actual
1530900d495aSAlexey Orishko 	 * device speed. Do print it instead.
1531900d495aSAlexey Orishko 	 */
1532900d495aSAlexey Orishko 	if ((tx_speed > 1000000) && (rx_speed > 1000000)) {
1533ae223cd4SBjørn Mork 		netif_info(dev, link, dev->net,
1534ae223cd4SBjørn Mork 			   "%u mbit/s downlink %u mbit/s uplink\n",
1535900d495aSAlexey Orishko 			   (unsigned int)(rx_speed / 1000000U),
1536900d495aSAlexey Orishko 			   (unsigned int)(tx_speed / 1000000U));
1537900d495aSAlexey Orishko 	} else {
1538ae223cd4SBjørn Mork 		netif_info(dev, link, dev->net,
1539ae223cd4SBjørn Mork 			   "%u kbit/s downlink %u kbit/s uplink\n",
1540900d495aSAlexey Orishko 			   (unsigned int)(rx_speed / 1000U),
1541900d495aSAlexey Orishko 			   (unsigned int)(tx_speed / 1000U));
1542900d495aSAlexey Orishko 	}
1543900d495aSAlexey Orishko }
1544900d495aSAlexey Orishko 
1545900d495aSAlexey Orishko static void cdc_ncm_status(struct usbnet *dev, struct urb *urb)
1546900d495aSAlexey Orishko {
1547900d495aSAlexey Orishko 	struct cdc_ncm_ctx *ctx;
1548900d495aSAlexey Orishko 	struct usb_cdc_notification *event;
1549900d495aSAlexey Orishko 
1550900d495aSAlexey Orishko 	ctx = (struct cdc_ncm_ctx *)dev->data[0];
1551900d495aSAlexey Orishko 
1552900d495aSAlexey Orishko 	if (urb->actual_length < sizeof(*event))
1553900d495aSAlexey Orishko 		return;
1554900d495aSAlexey Orishko 
1555900d495aSAlexey Orishko 	/* test for split data in 8-byte chunks */
1556900d495aSAlexey Orishko 	if (test_and_clear_bit(EVENT_STS_SPLIT, &dev->flags)) {
1557bed6f762SBjørn Mork 		cdc_ncm_speed_change(dev,
155884e77a8bSAlexey Orishko 		      (struct usb_cdc_speed_change *)urb->transfer_buffer);
1559900d495aSAlexey Orishko 		return;
1560900d495aSAlexey Orishko 	}
1561900d495aSAlexey Orishko 
1562900d495aSAlexey Orishko 	event = urb->transfer_buffer;
1563900d495aSAlexey Orishko 
1564900d495aSAlexey Orishko 	switch (event->bNotificationType) {
1565900d495aSAlexey Orishko 	case USB_CDC_NOTIFY_NETWORK_CONNECTION:
1566900d495aSAlexey Orishko 		/*
1567900d495aSAlexey Orishko 		 * According to the CDC NCM specification ch.7.1
1568900d495aSAlexey Orishko 		 * USB_CDC_NOTIFY_NETWORK_CONNECTION notification shall be
1569900d495aSAlexey Orishko 		 * sent by device after USB_CDC_NOTIFY_SPEED_CHANGE.
1570900d495aSAlexey Orishko 		 */
1571ae223cd4SBjørn Mork 		netif_info(dev, link, dev->net,
1572ae223cd4SBjørn Mork 			   "network connection: %sconnected\n",
1573fa83dbeeSBjørn Mork 			   !!event->wValue ? "" : "dis");
1574fa83dbeeSBjørn Mork 		usbnet_link_change(dev, !!event->wValue, 0);
1575900d495aSAlexey Orishko 		break;
1576900d495aSAlexey Orishko 
1577900d495aSAlexey Orishko 	case USB_CDC_NOTIFY_SPEED_CHANGE:
157884e77a8bSAlexey Orishko 		if (urb->actual_length < (sizeof(*event) +
157984e77a8bSAlexey Orishko 					sizeof(struct usb_cdc_speed_change)))
1580900d495aSAlexey Orishko 			set_bit(EVENT_STS_SPLIT, &dev->flags);
1581900d495aSAlexey Orishko 		else
1582bed6f762SBjørn Mork 			cdc_ncm_speed_change(dev,
158384e77a8bSAlexey Orishko 					     (struct usb_cdc_speed_change *)&event[1]);
1584900d495aSAlexey Orishko 		break;
1585900d495aSAlexey Orishko 
1586900d495aSAlexey Orishko 	default:
158767a36606SBjørn Mork 		dev_dbg(&dev->udev->dev,
158867a36606SBjørn Mork 			"NCM: unexpected notification 0x%02x!\n",
158967a36606SBjørn Mork 			event->bNotificationType);
1590900d495aSAlexey Orishko 		break;
1591900d495aSAlexey Orishko 	}
1592900d495aSAlexey Orishko }
1593900d495aSAlexey Orishko 
1594900d495aSAlexey Orishko static const struct driver_info cdc_ncm_info = {
1595900d495aSAlexey Orishko 	.description = "CDC NCM",
15964d06dd53SBjørn Mork 	.flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT | FLAG_MULTI_PACKET
15974d06dd53SBjørn Mork 			| FLAG_LINK_INTR,
1598900d495aSAlexey Orishko 	.bind = cdc_ncm_bind,
1599900d495aSAlexey Orishko 	.unbind = cdc_ncm_unbind,
1600a5e40708SOliver Neukum 	.manage_power = usbnet_manage_power,
1601900d495aSAlexey Orishko 	.status = cdc_ncm_status,
1602900d495aSAlexey Orishko 	.rx_fixup = cdc_ncm_rx_fixup,
1603900d495aSAlexey Orishko 	.tx_fixup = cdc_ncm_tx_fixup,
1604900d495aSAlexey Orishko };
1605900d495aSAlexey Orishko 
1606f94898eaSDan Williams /* Same as cdc_ncm_info, but with FLAG_WWAN */
1607f94898eaSDan Williams static const struct driver_info wwan_info = {
1608f94898eaSDan Williams 	.description = "Mobile Broadband Network Device",
1609f94898eaSDan Williams 	.flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT | FLAG_MULTI_PACKET
16104d06dd53SBjørn Mork 			| FLAG_LINK_INTR | FLAG_WWAN,
1611f94898eaSDan Williams 	.bind = cdc_ncm_bind,
1612f94898eaSDan Williams 	.unbind = cdc_ncm_unbind,
1613a5e40708SOliver Neukum 	.manage_power = usbnet_manage_power,
1614f94898eaSDan Williams 	.status = cdc_ncm_status,
1615f94898eaSDan Williams 	.rx_fixup = cdc_ncm_rx_fixup,
1616f94898eaSDan Williams 	.tx_fixup = cdc_ncm_tx_fixup,
1617f94898eaSDan Williams };
1618f94898eaSDan Williams 
16192f62d5aaSWei Shuai /* Same as wwan_info, but with FLAG_NOARP  */
16202f62d5aaSWei Shuai static const struct driver_info wwan_noarp_info = {
16212f62d5aaSWei Shuai 	.description = "Mobile Broadband Network Device (NO ARP)",
16222f62d5aaSWei Shuai 	.flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT | FLAG_MULTI_PACKET
16234d06dd53SBjørn Mork 			| FLAG_LINK_INTR | FLAG_WWAN | FLAG_NOARP,
16242f62d5aaSWei Shuai 	.bind = cdc_ncm_bind,
16252f62d5aaSWei Shuai 	.unbind = cdc_ncm_unbind,
16262f62d5aaSWei Shuai 	.manage_power = usbnet_manage_power,
16272f62d5aaSWei Shuai 	.status = cdc_ncm_status,
16282f62d5aaSWei Shuai 	.rx_fixup = cdc_ncm_rx_fixup,
16292f62d5aaSWei Shuai 	.tx_fixup = cdc_ncm_tx_fixup,
16302f62d5aaSWei Shuai };
16312f62d5aaSWei Shuai 
1632f94898eaSDan Williams static const struct usb_device_id cdc_devs[] = {
1633f94898eaSDan Williams 	/* Ericsson MBM devices like F5521gw */
1634f94898eaSDan Williams 	{ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
1635f94898eaSDan Williams 		| USB_DEVICE_ID_MATCH_VENDOR,
1636f94898eaSDan Williams 	  .idVendor = 0x0bdb,
1637f94898eaSDan Williams 	  .bInterfaceClass = USB_CLASS_COMM,
1638f94898eaSDan Williams 	  .bInterfaceSubClass = USB_CDC_SUBCLASS_NCM,
1639f94898eaSDan Williams 	  .bInterfaceProtocol = USB_CDC_PROTO_NONE,
1640f94898eaSDan Williams 	  .driver_info = (unsigned long) &wwan_info,
1641f94898eaSDan Williams 	},
1642f94898eaSDan Williams 
164379f42232SDaniele Palmas 	/* Telit LE910 V2 */
164479f42232SDaniele Palmas 	{ USB_DEVICE_AND_INTERFACE_INFO(0x1bc7, 0x0036,
164579f42232SDaniele Palmas 		USB_CLASS_COMM,
164679f42232SDaniele Palmas 		USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE),
164779f42232SDaniele Palmas 	  .driver_info = (unsigned long)&wwan_noarp_info,
164879f42232SDaniele Palmas 	},
164979f42232SDaniele Palmas 
1650670c0d62SDaniele Palmas 	/* DW5812 LTE Verizon Mobile Broadband Card
1651670c0d62SDaniele Palmas 	 * Unlike DW5550 this device requires FLAG_NOARP
1652670c0d62SDaniele Palmas 	 */
1653670c0d62SDaniele Palmas 	{ USB_DEVICE_AND_INTERFACE_INFO(0x413c, 0x81bb,
1654670c0d62SDaniele Palmas 		USB_CLASS_COMM,
1655670c0d62SDaniele Palmas 		USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE),
1656670c0d62SDaniele Palmas 	  .driver_info = (unsigned long)&wwan_noarp_info,
1657670c0d62SDaniele Palmas 	},
1658670c0d62SDaniele Palmas 
1659fb83d5f2SDaniele Palmas 	/* DW5813 LTE AT&T Mobile Broadband Card
1660fb83d5f2SDaniele Palmas 	 * Unlike DW5550 this device requires FLAG_NOARP
1661fb83d5f2SDaniele Palmas 	 */
1662fb83d5f2SDaniele Palmas 	{ USB_DEVICE_AND_INTERFACE_INFO(0x413c, 0x81bc,
1663fb83d5f2SDaniele Palmas 		USB_CLASS_COMM,
1664fb83d5f2SDaniele Palmas 		USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE),
1665fb83d5f2SDaniele Palmas 	  .driver_info = (unsigned long)&wwan_noarp_info,
1666fb83d5f2SDaniele Palmas 	},
1667fb83d5f2SDaniele Palmas 
1668f3a1ef9cSPeter Meiser 	/* Dell branded MBM devices like DW5550 */
1669f3a1ef9cSPeter Meiser 	{ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
1670f3a1ef9cSPeter Meiser 		| USB_DEVICE_ID_MATCH_VENDOR,
1671f3a1ef9cSPeter Meiser 	  .idVendor = 0x413c,
1672f3a1ef9cSPeter Meiser 	  .bInterfaceClass = USB_CLASS_COMM,
1673f3a1ef9cSPeter Meiser 	  .bInterfaceSubClass = USB_CDC_SUBCLASS_NCM,
1674f3a1ef9cSPeter Meiser 	  .bInterfaceProtocol = USB_CDC_PROTO_NONE,
1675f3a1ef9cSPeter Meiser 	  .driver_info = (unsigned long) &wwan_info,
1676f3a1ef9cSPeter Meiser 	},
1677f3a1ef9cSPeter Meiser 
1678f3a1ef9cSPeter Meiser 	/* Toshiba branded MBM devices */
1679f3a1ef9cSPeter Meiser 	{ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
1680f3a1ef9cSPeter Meiser 		| USB_DEVICE_ID_MATCH_VENDOR,
1681f3a1ef9cSPeter Meiser 	  .idVendor = 0x0930,
1682f3a1ef9cSPeter Meiser 	  .bInterfaceClass = USB_CLASS_COMM,
1683f3a1ef9cSPeter Meiser 	  .bInterfaceSubClass = USB_CDC_SUBCLASS_NCM,
1684f3a1ef9cSPeter Meiser 	  .bInterfaceProtocol = USB_CDC_PROTO_NONE,
1685f3a1ef9cSPeter Meiser 	  .driver_info = (unsigned long) &wwan_info,
1686f3a1ef9cSPeter Meiser 	},
1687f3a1ef9cSPeter Meiser 
16881f84eab4SBjørn Mork 	/* tag Huawei devices as wwan */
16891f84eab4SBjørn Mork 	{ USB_VENDOR_AND_INTERFACE_INFO(0x12d1,
16901f84eab4SBjørn Mork 					USB_CLASS_COMM,
16911f84eab4SBjørn Mork 					USB_CDC_SUBCLASS_NCM,
16921f84eab4SBjørn Mork 					USB_CDC_PROTO_NONE),
16931f84eab4SBjørn Mork 	  .driver_info = (unsigned long)&wwan_info,
16941f84eab4SBjørn Mork 	},
16951f84eab4SBjørn Mork 
16962f62d5aaSWei Shuai 	/* Infineon(now Intel) HSPA Modem platform */
16972f62d5aaSWei Shuai 	{ USB_DEVICE_AND_INTERFACE_INFO(0x1519, 0x0443,
16982f62d5aaSWei Shuai 		USB_CLASS_COMM,
16992f62d5aaSWei Shuai 		USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE),
17002f62d5aaSWei Shuai 	  .driver_info = (unsigned long)&wwan_noarp_info,
17012f62d5aaSWei Shuai 	},
17022f62d5aaSWei Shuai 
1703f94898eaSDan Williams 	/* Generic CDC-NCM devices */
1704f94898eaSDan Williams 	{ USB_INTERFACE_INFO(USB_CLASS_COMM,
1705f94898eaSDan Williams 		USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE),
1706f94898eaSDan Williams 		.driver_info = (unsigned long)&cdc_ncm_info,
1707f94898eaSDan Williams 	},
1708f94898eaSDan Williams 	{
1709f94898eaSDan Williams 	},
1710f94898eaSDan Williams };
1711f94898eaSDan Williams MODULE_DEVICE_TABLE(usb, cdc_devs);
1712f94898eaSDan Williams 
1713900d495aSAlexey Orishko static struct usb_driver cdc_ncm_driver = {
1714900d495aSAlexey Orishko 	.name = "cdc_ncm",
1715900d495aSAlexey Orishko 	.id_table = cdc_devs,
1716085e50e1SBjørn Mork 	.probe = usbnet_probe,
1717085e50e1SBjørn Mork 	.disconnect = usbnet_disconnect,
1718900d495aSAlexey Orishko 	.suspend = usbnet_suspend,
1719900d495aSAlexey Orishko 	.resume = usbnet_resume,
172085e3c65fSStefan Metzmacher 	.reset_resume =	usbnet_resume,
1721900d495aSAlexey Orishko 	.supports_autosuspend = 1,
1722e1f12eb6SSarah Sharp 	.disable_hub_initiated_lpm = 1,
1723900d495aSAlexey Orishko };
1724900d495aSAlexey Orishko 
1725d632eb1bSGreg Kroah-Hartman module_usb_driver(cdc_ncm_driver);
1726900d495aSAlexey Orishko 
1727900d495aSAlexey Orishko MODULE_AUTHOR("Hans Petter Selasky");
1728900d495aSAlexey Orishko MODULE_DESCRIPTION("USB CDC NCM host driver");
1729900d495aSAlexey Orishko MODULE_LICENSE("Dual BSD/GPL");
1730