18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * cdc_ncm.c
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) ST-Ericsson 2010-2012
58c2ecf20Sopenharmony_ci * Contact: Alexey Orishko <alexey.orishko@stericsson.com>
68c2ecf20Sopenharmony_ci * Original author: Hans Petter Selasky <hans.petter.selasky@stericsson.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * USB Host Driver for Network Control Model (NCM)
98c2ecf20Sopenharmony_ci * http://www.usb.org/developers/docs/devclass_docs/NCM10_012011.zip
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * The NCM encoding, decoding and initialization logic
128c2ecf20Sopenharmony_ci * derives from FreeBSD 8.x. if_cdce.c and if_cdcereg.h
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * This software is available to you under a choice of one of two
158c2ecf20Sopenharmony_ci * licenses. You may choose this file to be licensed under the terms
168c2ecf20Sopenharmony_ci * of the GNU General Public License (GPL) Version 2 or the 2-clause
178c2ecf20Sopenharmony_ci * BSD license listed below:
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
208c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions
218c2ecf20Sopenharmony_ci * are met:
228c2ecf20Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright
238c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer.
248c2ecf20Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright
258c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer in the
268c2ecf20Sopenharmony_ci *    documentation and/or other materials provided with the distribution.
278c2ecf20Sopenharmony_ci *
288c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
298c2ecf20Sopenharmony_ci * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
308c2ecf20Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
318c2ecf20Sopenharmony_ci * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
328c2ecf20Sopenharmony_ci * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
338c2ecf20Sopenharmony_ci * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
348c2ecf20Sopenharmony_ci * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
358c2ecf20Sopenharmony_ci * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
368c2ecf20Sopenharmony_ci * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
378c2ecf20Sopenharmony_ci * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
388c2ecf20Sopenharmony_ci * SUCH DAMAGE.
398c2ecf20Sopenharmony_ci */
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#include <linux/module.h>
428c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
438c2ecf20Sopenharmony_ci#include <linux/ctype.h>
448c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
458c2ecf20Sopenharmony_ci#include <linux/ethtool.h>
468c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
478c2ecf20Sopenharmony_ci#include <linux/mii.h>
488c2ecf20Sopenharmony_ci#include <linux/crc32.h>
498c2ecf20Sopenharmony_ci#include <linux/usb.h>
508c2ecf20Sopenharmony_ci#include <linux/hrtimer.h>
518c2ecf20Sopenharmony_ci#include <linux/atomic.h>
528c2ecf20Sopenharmony_ci#include <linux/usb/usbnet.h>
538c2ecf20Sopenharmony_ci#include <linux/usb/cdc.h>
548c2ecf20Sopenharmony_ci#include <linux/usb/cdc_ncm.h>
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_USB_NET_CDC_MBIM)
578c2ecf20Sopenharmony_cistatic bool prefer_mbim = true;
588c2ecf20Sopenharmony_ci#else
598c2ecf20Sopenharmony_cistatic bool prefer_mbim;
608c2ecf20Sopenharmony_ci#endif
618c2ecf20Sopenharmony_cimodule_param(prefer_mbim, bool, 0644);
628c2ecf20Sopenharmony_ciMODULE_PARM_DESC(prefer_mbim, "Prefer MBIM setting on dual NCM/MBIM functions");
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic void cdc_ncm_txpath_bh(unsigned long param);
658c2ecf20Sopenharmony_cistatic void cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx);
668c2ecf20Sopenharmony_cistatic enum hrtimer_restart cdc_ncm_tx_timer_cb(struct hrtimer *hr_timer);
678c2ecf20Sopenharmony_cistatic struct usb_driver cdc_ncm_driver;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistruct cdc_ncm_stats {
708c2ecf20Sopenharmony_ci	char stat_string[ETH_GSTRING_LEN];
718c2ecf20Sopenharmony_ci	int sizeof_stat;
728c2ecf20Sopenharmony_ci	int stat_offset;
738c2ecf20Sopenharmony_ci};
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci#define CDC_NCM_STAT(str, m) { \
768c2ecf20Sopenharmony_ci		.stat_string = str, \
778c2ecf20Sopenharmony_ci		.sizeof_stat = sizeof(((struct cdc_ncm_ctx *)0)->m), \
788c2ecf20Sopenharmony_ci		.stat_offset = offsetof(struct cdc_ncm_ctx, m) }
798c2ecf20Sopenharmony_ci#define CDC_NCM_SIMPLE_STAT(m)	CDC_NCM_STAT(__stringify(m), m)
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic const struct cdc_ncm_stats cdc_ncm_gstrings_stats[] = {
828c2ecf20Sopenharmony_ci	CDC_NCM_SIMPLE_STAT(tx_reason_ntb_full),
838c2ecf20Sopenharmony_ci	CDC_NCM_SIMPLE_STAT(tx_reason_ndp_full),
848c2ecf20Sopenharmony_ci	CDC_NCM_SIMPLE_STAT(tx_reason_timeout),
858c2ecf20Sopenharmony_ci	CDC_NCM_SIMPLE_STAT(tx_reason_max_datagram),
868c2ecf20Sopenharmony_ci	CDC_NCM_SIMPLE_STAT(tx_overhead),
878c2ecf20Sopenharmony_ci	CDC_NCM_SIMPLE_STAT(tx_ntbs),
888c2ecf20Sopenharmony_ci	CDC_NCM_SIMPLE_STAT(rx_overhead),
898c2ecf20Sopenharmony_ci	CDC_NCM_SIMPLE_STAT(rx_ntbs),
908c2ecf20Sopenharmony_ci};
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci#define CDC_NCM_LOW_MEM_MAX_CNT 10
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic int cdc_ncm_get_sset_count(struct net_device __always_unused *netdev, int sset)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	switch (sset) {
978c2ecf20Sopenharmony_ci	case ETH_SS_STATS:
988c2ecf20Sopenharmony_ci		return ARRAY_SIZE(cdc_ncm_gstrings_stats);
998c2ecf20Sopenharmony_ci	default:
1008c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic void cdc_ncm_get_ethtool_stats(struct net_device *netdev,
1058c2ecf20Sopenharmony_ci				    struct ethtool_stats __always_unused *stats,
1068c2ecf20Sopenharmony_ci				    u64 *data)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct usbnet *dev = netdev_priv(netdev);
1098c2ecf20Sopenharmony_ci	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
1108c2ecf20Sopenharmony_ci	int i;
1118c2ecf20Sopenharmony_ci	char *p = NULL;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(cdc_ncm_gstrings_stats); i++) {
1148c2ecf20Sopenharmony_ci		p = (char *)ctx + cdc_ncm_gstrings_stats[i].stat_offset;
1158c2ecf20Sopenharmony_ci		data[i] = (cdc_ncm_gstrings_stats[i].sizeof_stat == sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic void cdc_ncm_get_strings(struct net_device __always_unused *netdev, u32 stringset, u8 *data)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	u8 *p = data;
1228c2ecf20Sopenharmony_ci	int i;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	switch (stringset) {
1258c2ecf20Sopenharmony_ci	case ETH_SS_STATS:
1268c2ecf20Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(cdc_ncm_gstrings_stats); i++) {
1278c2ecf20Sopenharmony_ci			memcpy(p, cdc_ncm_gstrings_stats[i].stat_string, ETH_GSTRING_LEN);
1288c2ecf20Sopenharmony_ci			p += ETH_GSTRING_LEN;
1298c2ecf20Sopenharmony_ci		}
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic void cdc_ncm_update_rxtx_max(struct usbnet *dev, u32 new_rx, u32 new_tx);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic const struct ethtool_ops cdc_ncm_ethtool_ops = {
1368c2ecf20Sopenharmony_ci	.get_link          = usbnet_get_link,
1378c2ecf20Sopenharmony_ci	.nway_reset        = usbnet_nway_reset,
1388c2ecf20Sopenharmony_ci	.get_drvinfo       = usbnet_get_drvinfo,
1398c2ecf20Sopenharmony_ci	.get_msglevel      = usbnet_get_msglevel,
1408c2ecf20Sopenharmony_ci	.set_msglevel      = usbnet_set_msglevel,
1418c2ecf20Sopenharmony_ci	.get_ts_info       = ethtool_op_get_ts_info,
1428c2ecf20Sopenharmony_ci	.get_sset_count    = cdc_ncm_get_sset_count,
1438c2ecf20Sopenharmony_ci	.get_strings       = cdc_ncm_get_strings,
1448c2ecf20Sopenharmony_ci	.get_ethtool_stats = cdc_ncm_get_ethtool_stats,
1458c2ecf20Sopenharmony_ci	.get_link_ksettings      = usbnet_get_link_ksettings,
1468c2ecf20Sopenharmony_ci	.set_link_ksettings      = usbnet_set_link_ksettings,
1478c2ecf20Sopenharmony_ci};
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic u32 cdc_ncm_check_rx_max(struct usbnet *dev, u32 new_rx)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
1528c2ecf20Sopenharmony_ci	u32 val, max, min;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	/* clamp new_rx to sane values */
1558c2ecf20Sopenharmony_ci	min = USB_CDC_NCM_NTB_MIN_IN_SIZE;
1568c2ecf20Sopenharmony_ci	max = min_t(u32, CDC_NCM_NTB_MAX_SIZE_RX, le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize));
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	/* dwNtbInMaxSize spec violation? Use MIN size for both limits */
1598c2ecf20Sopenharmony_ci	if (max < min) {
1608c2ecf20Sopenharmony_ci		dev_warn(&dev->intf->dev, "dwNtbInMaxSize=%u is too small. Using %u\n",
1618c2ecf20Sopenharmony_ci			 le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize), min);
1628c2ecf20Sopenharmony_ci		max = min;
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	val = clamp_t(u32, new_rx, min, max);
1668c2ecf20Sopenharmony_ci	if (val != new_rx)
1678c2ecf20Sopenharmony_ci		dev_dbg(&dev->intf->dev, "rx_max must be in the [%u, %u] range\n", min, max);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	return val;
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic u32 cdc_ncm_check_tx_max(struct usbnet *dev, u32 new_tx)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
1758c2ecf20Sopenharmony_ci	u32 val, max, min;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	/* clamp new_tx to sane values */
1788c2ecf20Sopenharmony_ci	if (ctx->is_ndp16)
1798c2ecf20Sopenharmony_ci		min = ctx->max_datagram_size + ctx->max_ndp_size + sizeof(struct usb_cdc_ncm_nth16);
1808c2ecf20Sopenharmony_ci	else
1818c2ecf20Sopenharmony_ci		min = ctx->max_datagram_size + ctx->max_ndp_size + sizeof(struct usb_cdc_ncm_nth32);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	if (le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize) == 0)
1848c2ecf20Sopenharmony_ci		max = CDC_NCM_NTB_MAX_SIZE_TX; /* dwNtbOutMaxSize not set */
1858c2ecf20Sopenharmony_ci	else
1868c2ecf20Sopenharmony_ci		max = clamp_t(u32, le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize),
1878c2ecf20Sopenharmony_ci			      USB_CDC_NCM_NTB_MIN_OUT_SIZE,
1888c2ecf20Sopenharmony_ci			      CDC_NCM_NTB_MAX_SIZE_TX);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	/* some devices set dwNtbOutMaxSize too low for the above default */
1918c2ecf20Sopenharmony_ci	min = min(min, max);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	val = clamp_t(u32, new_tx, min, max);
1948c2ecf20Sopenharmony_ci	if (val != new_tx)
1958c2ecf20Sopenharmony_ci		dev_dbg(&dev->intf->dev, "tx_max must be in the [%u, %u] range\n", min, max);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	return val;
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic ssize_t cdc_ncm_show_min_tx_pkt(struct device *d, struct device_attribute *attr, char *buf)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	struct usbnet *dev = netdev_priv(to_net_dev(d));
2038c2ecf20Sopenharmony_ci	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", ctx->min_tx_pkt);
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic ssize_t cdc_ncm_show_rx_max(struct device *d, struct device_attribute *attr, char *buf)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	struct usbnet *dev = netdev_priv(to_net_dev(d));
2118c2ecf20Sopenharmony_ci	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", ctx->rx_max);
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cistatic ssize_t cdc_ncm_show_tx_max(struct device *d, struct device_attribute *attr, char *buf)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	struct usbnet *dev = netdev_priv(to_net_dev(d));
2198c2ecf20Sopenharmony_ci	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", ctx->tx_max);
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic ssize_t cdc_ncm_show_tx_timer_usecs(struct device *d, struct device_attribute *attr, char *buf)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	struct usbnet *dev = netdev_priv(to_net_dev(d));
2278c2ecf20Sopenharmony_ci	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", ctx->timer_interval / (u32)NSEC_PER_USEC);
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic ssize_t cdc_ncm_store_min_tx_pkt(struct device *d,  struct device_attribute *attr, const char *buf, size_t len)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	struct usbnet *dev = netdev_priv(to_net_dev(d));
2358c2ecf20Sopenharmony_ci	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
2368c2ecf20Sopenharmony_ci	unsigned long val;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	/* no need to restrict values - anything from 0 to infinity is OK */
2398c2ecf20Sopenharmony_ci	if (kstrtoul(buf, 0, &val))
2408c2ecf20Sopenharmony_ci		return -EINVAL;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	ctx->min_tx_pkt = val;
2438c2ecf20Sopenharmony_ci	return len;
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic ssize_t cdc_ncm_store_rx_max(struct device *d,  struct device_attribute *attr, const char *buf, size_t len)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	struct usbnet *dev = netdev_priv(to_net_dev(d));
2498c2ecf20Sopenharmony_ci	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
2508c2ecf20Sopenharmony_ci	unsigned long val;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	if (kstrtoul(buf, 0, &val) || cdc_ncm_check_rx_max(dev, val) != val)
2538c2ecf20Sopenharmony_ci		return -EINVAL;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	cdc_ncm_update_rxtx_max(dev, val, ctx->tx_max);
2568c2ecf20Sopenharmony_ci	return len;
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic ssize_t cdc_ncm_store_tx_max(struct device *d,  struct device_attribute *attr, const char *buf, size_t len)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	struct usbnet *dev = netdev_priv(to_net_dev(d));
2628c2ecf20Sopenharmony_ci	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
2638c2ecf20Sopenharmony_ci	unsigned long val;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	if (kstrtoul(buf, 0, &val) || cdc_ncm_check_tx_max(dev, val) != val)
2668c2ecf20Sopenharmony_ci		return -EINVAL;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	cdc_ncm_update_rxtx_max(dev, ctx->rx_max, val);
2698c2ecf20Sopenharmony_ci	return len;
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cistatic ssize_t cdc_ncm_store_tx_timer_usecs(struct device *d,  struct device_attribute *attr, const char *buf, size_t len)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	struct usbnet *dev = netdev_priv(to_net_dev(d));
2758c2ecf20Sopenharmony_ci	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
2768c2ecf20Sopenharmony_ci	ssize_t ret;
2778c2ecf20Sopenharmony_ci	unsigned long val;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	ret = kstrtoul(buf, 0, &val);
2808c2ecf20Sopenharmony_ci	if (ret)
2818c2ecf20Sopenharmony_ci		return ret;
2828c2ecf20Sopenharmony_ci	if (val && (val < CDC_NCM_TIMER_INTERVAL_MIN || val > CDC_NCM_TIMER_INTERVAL_MAX))
2838c2ecf20Sopenharmony_ci		return -EINVAL;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	spin_lock_bh(&ctx->mtx);
2868c2ecf20Sopenharmony_ci	ctx->timer_interval = val * NSEC_PER_USEC;
2878c2ecf20Sopenharmony_ci	if (!ctx->timer_interval)
2888c2ecf20Sopenharmony_ci		ctx->tx_timer_pending = 0;
2898c2ecf20Sopenharmony_ci	spin_unlock_bh(&ctx->mtx);
2908c2ecf20Sopenharmony_ci	return len;
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic DEVICE_ATTR(min_tx_pkt, 0644, cdc_ncm_show_min_tx_pkt, cdc_ncm_store_min_tx_pkt);
2948c2ecf20Sopenharmony_cistatic DEVICE_ATTR(rx_max, 0644, cdc_ncm_show_rx_max, cdc_ncm_store_rx_max);
2958c2ecf20Sopenharmony_cistatic DEVICE_ATTR(tx_max, 0644, cdc_ncm_show_tx_max, cdc_ncm_store_tx_max);
2968c2ecf20Sopenharmony_cistatic DEVICE_ATTR(tx_timer_usecs, 0644, cdc_ncm_show_tx_timer_usecs, cdc_ncm_store_tx_timer_usecs);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic ssize_t ndp_to_end_show(struct device *d, struct device_attribute *attr, char *buf)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	struct usbnet *dev = netdev_priv(to_net_dev(d));
3018c2ecf20Sopenharmony_ci	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	return sprintf(buf, "%c\n", ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END ? 'Y' : 'N');
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic ssize_t ndp_to_end_store(struct device *d,  struct device_attribute *attr, const char *buf, size_t len)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	struct usbnet *dev = netdev_priv(to_net_dev(d));
3098c2ecf20Sopenharmony_ci	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
3108c2ecf20Sopenharmony_ci	bool enable;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	if (strtobool(buf, &enable))
3138c2ecf20Sopenharmony_ci		return -EINVAL;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	/* no change? */
3168c2ecf20Sopenharmony_ci	if (enable == (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END))
3178c2ecf20Sopenharmony_ci		return len;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	if (enable) {
3208c2ecf20Sopenharmony_ci		if (ctx->is_ndp16 && !ctx->delayed_ndp16) {
3218c2ecf20Sopenharmony_ci			ctx->delayed_ndp16 = kzalloc(ctx->max_ndp_size, GFP_KERNEL);
3228c2ecf20Sopenharmony_ci			if (!ctx->delayed_ndp16)
3238c2ecf20Sopenharmony_ci				return -ENOMEM;
3248c2ecf20Sopenharmony_ci		}
3258c2ecf20Sopenharmony_ci		if (!ctx->is_ndp16 && !ctx->delayed_ndp32) {
3268c2ecf20Sopenharmony_ci			ctx->delayed_ndp32 = kzalloc(ctx->max_ndp_size, GFP_KERNEL);
3278c2ecf20Sopenharmony_ci			if (!ctx->delayed_ndp32)
3288c2ecf20Sopenharmony_ci				return -ENOMEM;
3298c2ecf20Sopenharmony_ci		}
3308c2ecf20Sopenharmony_ci	}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	/* flush pending data before changing flag */
3338c2ecf20Sopenharmony_ci	netif_tx_lock_bh(dev->net);
3348c2ecf20Sopenharmony_ci	usbnet_start_xmit(NULL, dev->net);
3358c2ecf20Sopenharmony_ci	spin_lock_bh(&ctx->mtx);
3368c2ecf20Sopenharmony_ci	if (enable)
3378c2ecf20Sopenharmony_ci		ctx->drvflags |= CDC_NCM_FLAG_NDP_TO_END;
3388c2ecf20Sopenharmony_ci	else
3398c2ecf20Sopenharmony_ci		ctx->drvflags &= ~CDC_NCM_FLAG_NDP_TO_END;
3408c2ecf20Sopenharmony_ci	spin_unlock_bh(&ctx->mtx);
3418c2ecf20Sopenharmony_ci	netif_tx_unlock_bh(dev->net);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	return len;
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(ndp_to_end);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci#define NCM_PARM_ATTR(name, format, tocpu)				\
3488c2ecf20Sopenharmony_cistatic ssize_t cdc_ncm_show_##name(struct device *d, struct device_attribute *attr, char *buf) \
3498c2ecf20Sopenharmony_ci{ \
3508c2ecf20Sopenharmony_ci	struct usbnet *dev = netdev_priv(to_net_dev(d)); \
3518c2ecf20Sopenharmony_ci	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; \
3528c2ecf20Sopenharmony_ci	return sprintf(buf, format "\n", tocpu(ctx->ncm_parm.name));	\
3538c2ecf20Sopenharmony_ci} \
3548c2ecf20Sopenharmony_cistatic DEVICE_ATTR(name, 0444, cdc_ncm_show_##name, NULL)
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ciNCM_PARM_ATTR(bmNtbFormatsSupported, "0x%04x", le16_to_cpu);
3578c2ecf20Sopenharmony_ciNCM_PARM_ATTR(dwNtbInMaxSize, "%u", le32_to_cpu);
3588c2ecf20Sopenharmony_ciNCM_PARM_ATTR(wNdpInDivisor, "%u", le16_to_cpu);
3598c2ecf20Sopenharmony_ciNCM_PARM_ATTR(wNdpInPayloadRemainder, "%u", le16_to_cpu);
3608c2ecf20Sopenharmony_ciNCM_PARM_ATTR(wNdpInAlignment, "%u", le16_to_cpu);
3618c2ecf20Sopenharmony_ciNCM_PARM_ATTR(dwNtbOutMaxSize, "%u", le32_to_cpu);
3628c2ecf20Sopenharmony_ciNCM_PARM_ATTR(wNdpOutDivisor, "%u", le16_to_cpu);
3638c2ecf20Sopenharmony_ciNCM_PARM_ATTR(wNdpOutPayloadRemainder, "%u", le16_to_cpu);
3648c2ecf20Sopenharmony_ciNCM_PARM_ATTR(wNdpOutAlignment, "%u", le16_to_cpu);
3658c2ecf20Sopenharmony_ciNCM_PARM_ATTR(wNtbOutMaxDatagrams, "%u", le16_to_cpu);
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cistatic struct attribute *cdc_ncm_sysfs_attrs[] = {
3688c2ecf20Sopenharmony_ci	&dev_attr_min_tx_pkt.attr,
3698c2ecf20Sopenharmony_ci	&dev_attr_ndp_to_end.attr,
3708c2ecf20Sopenharmony_ci	&dev_attr_rx_max.attr,
3718c2ecf20Sopenharmony_ci	&dev_attr_tx_max.attr,
3728c2ecf20Sopenharmony_ci	&dev_attr_tx_timer_usecs.attr,
3738c2ecf20Sopenharmony_ci	&dev_attr_bmNtbFormatsSupported.attr,
3748c2ecf20Sopenharmony_ci	&dev_attr_dwNtbInMaxSize.attr,
3758c2ecf20Sopenharmony_ci	&dev_attr_wNdpInDivisor.attr,
3768c2ecf20Sopenharmony_ci	&dev_attr_wNdpInPayloadRemainder.attr,
3778c2ecf20Sopenharmony_ci	&dev_attr_wNdpInAlignment.attr,
3788c2ecf20Sopenharmony_ci	&dev_attr_dwNtbOutMaxSize.attr,
3798c2ecf20Sopenharmony_ci	&dev_attr_wNdpOutDivisor.attr,
3808c2ecf20Sopenharmony_ci	&dev_attr_wNdpOutPayloadRemainder.attr,
3818c2ecf20Sopenharmony_ci	&dev_attr_wNdpOutAlignment.attr,
3828c2ecf20Sopenharmony_ci	&dev_attr_wNtbOutMaxDatagrams.attr,
3838c2ecf20Sopenharmony_ci	NULL,
3848c2ecf20Sopenharmony_ci};
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_cistatic const struct attribute_group cdc_ncm_sysfs_attr_group = {
3878c2ecf20Sopenharmony_ci	.name = "cdc_ncm",
3888c2ecf20Sopenharmony_ci	.attrs = cdc_ncm_sysfs_attrs,
3898c2ecf20Sopenharmony_ci};
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci/* handle rx_max and tx_max changes */
3928c2ecf20Sopenharmony_cistatic void cdc_ncm_update_rxtx_max(struct usbnet *dev, u32 new_rx, u32 new_tx)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
3958c2ecf20Sopenharmony_ci	u8 iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber;
3968c2ecf20Sopenharmony_ci	u32 val;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	val = cdc_ncm_check_rx_max(dev, new_rx);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	/* inform device about NTB input size changes */
4018c2ecf20Sopenharmony_ci	if (val != ctx->rx_max) {
4028c2ecf20Sopenharmony_ci		__le32 dwNtbInMaxSize = cpu_to_le32(val);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci		dev_info(&dev->intf->dev, "setting rx_max = %u\n", val);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci		/* tell device to use new size */
4078c2ecf20Sopenharmony_ci		if (usbnet_write_cmd(dev, USB_CDC_SET_NTB_INPUT_SIZE,
4088c2ecf20Sopenharmony_ci				     USB_TYPE_CLASS | USB_DIR_OUT
4098c2ecf20Sopenharmony_ci				     | USB_RECIP_INTERFACE,
4108c2ecf20Sopenharmony_ci				     0, iface_no, &dwNtbInMaxSize, 4) < 0)
4118c2ecf20Sopenharmony_ci			dev_dbg(&dev->intf->dev, "Setting NTB Input Size failed\n");
4128c2ecf20Sopenharmony_ci		else
4138c2ecf20Sopenharmony_ci			ctx->rx_max = val;
4148c2ecf20Sopenharmony_ci	}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	/* usbnet use these values for sizing rx queues */
4178c2ecf20Sopenharmony_ci	if (dev->rx_urb_size != ctx->rx_max) {
4188c2ecf20Sopenharmony_ci		dev->rx_urb_size = ctx->rx_max;
4198c2ecf20Sopenharmony_ci		if (netif_running(dev->net))
4208c2ecf20Sopenharmony_ci			usbnet_unlink_rx_urbs(dev);
4218c2ecf20Sopenharmony_ci	}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	val = cdc_ncm_check_tx_max(dev, new_tx);
4248c2ecf20Sopenharmony_ci	if (val != ctx->tx_max)
4258c2ecf20Sopenharmony_ci		dev_info(&dev->intf->dev, "setting tx_max = %u\n", val);
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	/* Adding a pad byte here if necessary simplifies the handling
4288c2ecf20Sopenharmony_ci	 * in cdc_ncm_fill_tx_frame, making tx_max always represent
4298c2ecf20Sopenharmony_ci	 * the real skb max size.
4308c2ecf20Sopenharmony_ci	 *
4318c2ecf20Sopenharmony_ci	 * We cannot use dev->maxpacket here because this is called from
4328c2ecf20Sopenharmony_ci	 * .bind which is called before usbnet sets up dev->maxpacket
4338c2ecf20Sopenharmony_ci	 */
4348c2ecf20Sopenharmony_ci	if (val != le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize) &&
4358c2ecf20Sopenharmony_ci	    val % usb_maxpacket(dev->udev, dev->out, 1) == 0)
4368c2ecf20Sopenharmony_ci		val++;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	/* we might need to flush any pending tx buffers if running */
4398c2ecf20Sopenharmony_ci	if (netif_running(dev->net) && val > ctx->tx_max) {
4408c2ecf20Sopenharmony_ci		netif_tx_lock_bh(dev->net);
4418c2ecf20Sopenharmony_ci		usbnet_start_xmit(NULL, dev->net);
4428c2ecf20Sopenharmony_ci		/* make sure tx_curr_skb is reallocated if it was empty */
4438c2ecf20Sopenharmony_ci		if (ctx->tx_curr_skb) {
4448c2ecf20Sopenharmony_ci			dev_kfree_skb_any(ctx->tx_curr_skb);
4458c2ecf20Sopenharmony_ci			ctx->tx_curr_skb = NULL;
4468c2ecf20Sopenharmony_ci		}
4478c2ecf20Sopenharmony_ci		ctx->tx_max = val;
4488c2ecf20Sopenharmony_ci		netif_tx_unlock_bh(dev->net);
4498c2ecf20Sopenharmony_ci	} else {
4508c2ecf20Sopenharmony_ci		ctx->tx_max = val;
4518c2ecf20Sopenharmony_ci	}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	dev->hard_mtu = ctx->tx_max;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	/* max qlen depend on hard_mtu and rx_urb_size */
4568c2ecf20Sopenharmony_ci	usbnet_update_max_qlen(dev);
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	/* never pad more than 3 full USB packets per transfer */
4598c2ecf20Sopenharmony_ci	ctx->min_tx_pkt = clamp_t(u16, ctx->tx_max - 3 * usb_maxpacket(dev->udev, dev->out, 1),
4608c2ecf20Sopenharmony_ci				  CDC_NCM_MIN_TX_PKT, ctx->tx_max);
4618c2ecf20Sopenharmony_ci}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci/* helpers for NCM and MBIM differences */
4648c2ecf20Sopenharmony_cistatic u8 cdc_ncm_flags(struct usbnet *dev)
4658c2ecf20Sopenharmony_ci{
4668c2ecf20Sopenharmony_ci	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	if (cdc_ncm_comm_intf_is_mbim(dev->intf->cur_altsetting) && ctx->mbim_desc)
4698c2ecf20Sopenharmony_ci		return ctx->mbim_desc->bmNetworkCapabilities;
4708c2ecf20Sopenharmony_ci	if (ctx->func_desc)
4718c2ecf20Sopenharmony_ci		return ctx->func_desc->bmNetworkCapabilities;
4728c2ecf20Sopenharmony_ci	return 0;
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_cistatic int cdc_ncm_eth_hlen(struct usbnet *dev)
4768c2ecf20Sopenharmony_ci{
4778c2ecf20Sopenharmony_ci	if (cdc_ncm_comm_intf_is_mbim(dev->intf->cur_altsetting))
4788c2ecf20Sopenharmony_ci		return 0;
4798c2ecf20Sopenharmony_ci	return ETH_HLEN;
4808c2ecf20Sopenharmony_ci}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_cistatic u32 cdc_ncm_min_dgram_size(struct usbnet *dev)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	if (cdc_ncm_comm_intf_is_mbim(dev->intf->cur_altsetting))
4858c2ecf20Sopenharmony_ci		return CDC_MBIM_MIN_DATAGRAM_SIZE;
4868c2ecf20Sopenharmony_ci	return CDC_NCM_MIN_DATAGRAM_SIZE;
4878c2ecf20Sopenharmony_ci}
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_cistatic u32 cdc_ncm_max_dgram_size(struct usbnet *dev)
4908c2ecf20Sopenharmony_ci{
4918c2ecf20Sopenharmony_ci	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	if (cdc_ncm_comm_intf_is_mbim(dev->intf->cur_altsetting) && ctx->mbim_desc)
4948c2ecf20Sopenharmony_ci		return le16_to_cpu(ctx->mbim_desc->wMaxSegmentSize);
4958c2ecf20Sopenharmony_ci	if (ctx->ether_desc)
4968c2ecf20Sopenharmony_ci		return le16_to_cpu(ctx->ether_desc->wMaxSegmentSize);
4978c2ecf20Sopenharmony_ci	return CDC_NCM_MAX_DATAGRAM_SIZE;
4988c2ecf20Sopenharmony_ci}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci/* initial one-time device setup.  MUST be called with the data interface
5018c2ecf20Sopenharmony_ci * in altsetting 0
5028c2ecf20Sopenharmony_ci */
5038c2ecf20Sopenharmony_cistatic int cdc_ncm_init(struct usbnet *dev)
5048c2ecf20Sopenharmony_ci{
5058c2ecf20Sopenharmony_ci	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
5068c2ecf20Sopenharmony_ci	u8 iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber;
5078c2ecf20Sopenharmony_ci	int err;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	err = usbnet_read_cmd(dev, USB_CDC_GET_NTB_PARAMETERS,
5108c2ecf20Sopenharmony_ci			      USB_TYPE_CLASS | USB_DIR_IN
5118c2ecf20Sopenharmony_ci			      |USB_RECIP_INTERFACE,
5128c2ecf20Sopenharmony_ci			      0, iface_no, &ctx->ncm_parm,
5138c2ecf20Sopenharmony_ci			      sizeof(ctx->ncm_parm));
5148c2ecf20Sopenharmony_ci	if (err < 0) {
5158c2ecf20Sopenharmony_ci		dev_err(&dev->intf->dev, "failed GET_NTB_PARAMETERS\n");
5168c2ecf20Sopenharmony_ci		return err; /* GET_NTB_PARAMETERS is required */
5178c2ecf20Sopenharmony_ci	}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	/* set CRC Mode */
5208c2ecf20Sopenharmony_ci	if (cdc_ncm_flags(dev) & USB_CDC_NCM_NCAP_CRC_MODE) {
5218c2ecf20Sopenharmony_ci		dev_dbg(&dev->intf->dev, "Setting CRC mode off\n");
5228c2ecf20Sopenharmony_ci		err = usbnet_write_cmd(dev, USB_CDC_SET_CRC_MODE,
5238c2ecf20Sopenharmony_ci				       USB_TYPE_CLASS | USB_DIR_OUT
5248c2ecf20Sopenharmony_ci				       | USB_RECIP_INTERFACE,
5258c2ecf20Sopenharmony_ci				       USB_CDC_NCM_CRC_NOT_APPENDED,
5268c2ecf20Sopenharmony_ci				       iface_no, NULL, 0);
5278c2ecf20Sopenharmony_ci		if (err < 0)
5288c2ecf20Sopenharmony_ci			dev_err(&dev->intf->dev, "SET_CRC_MODE failed\n");
5298c2ecf20Sopenharmony_ci	}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	/* use ndp16 by default */
5328c2ecf20Sopenharmony_ci	ctx->is_ndp16 = 1;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	/* set NTB format, if both formats are supported.
5358c2ecf20Sopenharmony_ci	 *
5368c2ecf20Sopenharmony_ci	 * "The host shall only send this command while the NCM Data
5378c2ecf20Sopenharmony_ci	 *  Interface is in alternate setting 0."
5388c2ecf20Sopenharmony_ci	 */
5398c2ecf20Sopenharmony_ci	if (le16_to_cpu(ctx->ncm_parm.bmNtbFormatsSupported) &
5408c2ecf20Sopenharmony_ci						USB_CDC_NCM_NTB32_SUPPORTED) {
5418c2ecf20Sopenharmony_ci		if (ctx->drvflags & CDC_NCM_FLAG_PREFER_NTB32) {
5428c2ecf20Sopenharmony_ci			ctx->is_ndp16 = 0;
5438c2ecf20Sopenharmony_ci			dev_dbg(&dev->intf->dev, "Setting NTB format to 32-bit\n");
5448c2ecf20Sopenharmony_ci			err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_FORMAT,
5458c2ecf20Sopenharmony_ci					       USB_TYPE_CLASS | USB_DIR_OUT
5468c2ecf20Sopenharmony_ci					       | USB_RECIP_INTERFACE,
5478c2ecf20Sopenharmony_ci					       USB_CDC_NCM_NTB32_FORMAT,
5488c2ecf20Sopenharmony_ci					       iface_no, NULL, 0);
5498c2ecf20Sopenharmony_ci		} else {
5508c2ecf20Sopenharmony_ci			ctx->is_ndp16 = 1;
5518c2ecf20Sopenharmony_ci			dev_dbg(&dev->intf->dev, "Setting NTB format to 16-bit\n");
5528c2ecf20Sopenharmony_ci			err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_FORMAT,
5538c2ecf20Sopenharmony_ci					       USB_TYPE_CLASS | USB_DIR_OUT
5548c2ecf20Sopenharmony_ci					       | USB_RECIP_INTERFACE,
5558c2ecf20Sopenharmony_ci					       USB_CDC_NCM_NTB16_FORMAT,
5568c2ecf20Sopenharmony_ci					       iface_no, NULL, 0);
5578c2ecf20Sopenharmony_ci		}
5588c2ecf20Sopenharmony_ci		if (err < 0) {
5598c2ecf20Sopenharmony_ci			ctx->is_ndp16 = 1;
5608c2ecf20Sopenharmony_ci			dev_err(&dev->intf->dev, "SET_NTB_FORMAT failed\n");
5618c2ecf20Sopenharmony_ci		}
5628c2ecf20Sopenharmony_ci	}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	/* set initial device values */
5658c2ecf20Sopenharmony_ci	ctx->rx_max = le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize);
5668c2ecf20Sopenharmony_ci	ctx->tx_max = le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize);
5678c2ecf20Sopenharmony_ci	ctx->tx_remainder = le16_to_cpu(ctx->ncm_parm.wNdpOutPayloadRemainder);
5688c2ecf20Sopenharmony_ci	ctx->tx_modulus = le16_to_cpu(ctx->ncm_parm.wNdpOutDivisor);
5698c2ecf20Sopenharmony_ci	ctx->tx_ndp_modulus = le16_to_cpu(ctx->ncm_parm.wNdpOutAlignment);
5708c2ecf20Sopenharmony_ci	/* devices prior to NCM Errata shall set this field to zero */
5718c2ecf20Sopenharmony_ci	ctx->tx_max_datagrams = le16_to_cpu(ctx->ncm_parm.wNtbOutMaxDatagrams);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	dev_dbg(&dev->intf->dev,
5748c2ecf20Sopenharmony_ci		"dwNtbInMaxSize=%u dwNtbOutMaxSize=%u wNdpOutPayloadRemainder=%u wNdpOutDivisor=%u wNdpOutAlignment=%u wNtbOutMaxDatagrams=%u flags=0x%x\n",
5758c2ecf20Sopenharmony_ci		ctx->rx_max, ctx->tx_max, ctx->tx_remainder, ctx->tx_modulus,
5768c2ecf20Sopenharmony_ci		ctx->tx_ndp_modulus, ctx->tx_max_datagrams, cdc_ncm_flags(dev));
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	/* max count of tx datagrams */
5798c2ecf20Sopenharmony_ci	if ((ctx->tx_max_datagrams == 0) ||
5808c2ecf20Sopenharmony_ci			(ctx->tx_max_datagrams > CDC_NCM_DPT_DATAGRAMS_MAX))
5818c2ecf20Sopenharmony_ci		ctx->tx_max_datagrams = CDC_NCM_DPT_DATAGRAMS_MAX;
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	/* set up maximum NDP size */
5848c2ecf20Sopenharmony_ci	if (ctx->is_ndp16)
5858c2ecf20Sopenharmony_ci		ctx->max_ndp_size = sizeof(struct usb_cdc_ncm_ndp16) + (ctx->tx_max_datagrams + 1) * sizeof(struct usb_cdc_ncm_dpe16);
5868c2ecf20Sopenharmony_ci	else
5878c2ecf20Sopenharmony_ci		ctx->max_ndp_size = sizeof(struct usb_cdc_ncm_ndp32) + (ctx->tx_max_datagrams + 1) * sizeof(struct usb_cdc_ncm_dpe32);
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	/* initial coalescing timer interval */
5908c2ecf20Sopenharmony_ci	ctx->timer_interval = CDC_NCM_TIMER_INTERVAL_USEC * NSEC_PER_USEC;
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	return 0;
5938c2ecf20Sopenharmony_ci}
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci/* set a new max datagram size */
5968c2ecf20Sopenharmony_cistatic void cdc_ncm_set_dgram_size(struct usbnet *dev, int new_size)
5978c2ecf20Sopenharmony_ci{
5988c2ecf20Sopenharmony_ci	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
5998c2ecf20Sopenharmony_ci	u8 iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber;
6008c2ecf20Sopenharmony_ci	__le16 max_datagram_size;
6018c2ecf20Sopenharmony_ci	u16 mbim_mtu;
6028c2ecf20Sopenharmony_ci	int err;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	/* set default based on descriptors */
6058c2ecf20Sopenharmony_ci	ctx->max_datagram_size = clamp_t(u32, new_size,
6068c2ecf20Sopenharmony_ci					 cdc_ncm_min_dgram_size(dev),
6078c2ecf20Sopenharmony_ci					 CDC_NCM_MAX_DATAGRAM_SIZE);
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	/* inform the device about the selected Max Datagram Size? */
6108c2ecf20Sopenharmony_ci	if (!(cdc_ncm_flags(dev) & USB_CDC_NCM_NCAP_MAX_DATAGRAM_SIZE))
6118c2ecf20Sopenharmony_ci		goto out;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	/* read current mtu value from device */
6148c2ecf20Sopenharmony_ci	err = usbnet_read_cmd(dev, USB_CDC_GET_MAX_DATAGRAM_SIZE,
6158c2ecf20Sopenharmony_ci			      USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE,
6168c2ecf20Sopenharmony_ci			      0, iface_no, &max_datagram_size, sizeof(max_datagram_size));
6178c2ecf20Sopenharmony_ci	if (err != sizeof(max_datagram_size)) {
6188c2ecf20Sopenharmony_ci		dev_dbg(&dev->intf->dev, "GET_MAX_DATAGRAM_SIZE failed\n");
6198c2ecf20Sopenharmony_ci		goto out;
6208c2ecf20Sopenharmony_ci	}
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	if (le16_to_cpu(max_datagram_size) == ctx->max_datagram_size)
6238c2ecf20Sopenharmony_ci		goto out;
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	max_datagram_size = cpu_to_le16(ctx->max_datagram_size);
6268c2ecf20Sopenharmony_ci	err = usbnet_write_cmd(dev, USB_CDC_SET_MAX_DATAGRAM_SIZE,
6278c2ecf20Sopenharmony_ci			       USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE,
6288c2ecf20Sopenharmony_ci			       0, iface_no, &max_datagram_size, sizeof(max_datagram_size));
6298c2ecf20Sopenharmony_ci	if (err < 0)
6308c2ecf20Sopenharmony_ci		dev_dbg(&dev->intf->dev, "SET_MAX_DATAGRAM_SIZE failed\n");
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ciout:
6338c2ecf20Sopenharmony_ci	/* set MTU to max supported by the device if necessary */
6348c2ecf20Sopenharmony_ci	dev->net->mtu = min_t(int, dev->net->mtu, ctx->max_datagram_size - cdc_ncm_eth_hlen(dev));
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	/* do not exceed operater preferred MTU */
6378c2ecf20Sopenharmony_ci	if (ctx->mbim_extended_desc) {
6388c2ecf20Sopenharmony_ci		mbim_mtu = le16_to_cpu(ctx->mbim_extended_desc->wMTU);
6398c2ecf20Sopenharmony_ci		if (mbim_mtu != 0 && mbim_mtu < dev->net->mtu)
6408c2ecf20Sopenharmony_ci			dev->net->mtu = mbim_mtu;
6418c2ecf20Sopenharmony_ci	}
6428c2ecf20Sopenharmony_ci}
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_cistatic void cdc_ncm_fix_modulus(struct usbnet *dev)
6458c2ecf20Sopenharmony_ci{
6468c2ecf20Sopenharmony_ci	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
6478c2ecf20Sopenharmony_ci	u32 val;
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	/*
6508c2ecf20Sopenharmony_ci	 * verify that the structure alignment is:
6518c2ecf20Sopenharmony_ci	 * - power of two
6528c2ecf20Sopenharmony_ci	 * - not greater than the maximum transmit length
6538c2ecf20Sopenharmony_ci	 * - not less than four bytes
6548c2ecf20Sopenharmony_ci	 */
6558c2ecf20Sopenharmony_ci	val = ctx->tx_ndp_modulus;
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	if ((val < USB_CDC_NCM_NDP_ALIGN_MIN_SIZE) ||
6588c2ecf20Sopenharmony_ci	    (val != ((-val) & val)) || (val >= ctx->tx_max)) {
6598c2ecf20Sopenharmony_ci		dev_dbg(&dev->intf->dev, "Using default alignment: 4 bytes\n");
6608c2ecf20Sopenharmony_ci		ctx->tx_ndp_modulus = USB_CDC_NCM_NDP_ALIGN_MIN_SIZE;
6618c2ecf20Sopenharmony_ci	}
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	/*
6648c2ecf20Sopenharmony_ci	 * verify that the payload alignment is:
6658c2ecf20Sopenharmony_ci	 * - power of two
6668c2ecf20Sopenharmony_ci	 * - not greater than the maximum transmit length
6678c2ecf20Sopenharmony_ci	 * - not less than four bytes
6688c2ecf20Sopenharmony_ci	 */
6698c2ecf20Sopenharmony_ci	val = ctx->tx_modulus;
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	if ((val < USB_CDC_NCM_NDP_ALIGN_MIN_SIZE) ||
6728c2ecf20Sopenharmony_ci	    (val != ((-val) & val)) || (val >= ctx->tx_max)) {
6738c2ecf20Sopenharmony_ci		dev_dbg(&dev->intf->dev, "Using default transmit modulus: 4 bytes\n");
6748c2ecf20Sopenharmony_ci		ctx->tx_modulus = USB_CDC_NCM_NDP_ALIGN_MIN_SIZE;
6758c2ecf20Sopenharmony_ci	}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	/* verify the payload remainder */
6788c2ecf20Sopenharmony_ci	if (ctx->tx_remainder >= ctx->tx_modulus) {
6798c2ecf20Sopenharmony_ci		dev_dbg(&dev->intf->dev, "Using default transmit remainder: 0 bytes\n");
6808c2ecf20Sopenharmony_ci		ctx->tx_remainder = 0;
6818c2ecf20Sopenharmony_ci	}
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	/* adjust TX-remainder according to NCM specification. */
6848c2ecf20Sopenharmony_ci	ctx->tx_remainder = ((ctx->tx_remainder - cdc_ncm_eth_hlen(dev)) &
6858c2ecf20Sopenharmony_ci			     (ctx->tx_modulus - 1));
6868c2ecf20Sopenharmony_ci}
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_cistatic int cdc_ncm_setup(struct usbnet *dev)
6898c2ecf20Sopenharmony_ci{
6908c2ecf20Sopenharmony_ci	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
6918c2ecf20Sopenharmony_ci	u32 def_rx, def_tx;
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	/* be conservative when selecting intial buffer size to
6948c2ecf20Sopenharmony_ci	 * increase the number of hosts this will work for
6958c2ecf20Sopenharmony_ci	 */
6968c2ecf20Sopenharmony_ci	def_rx = min_t(u32, CDC_NCM_NTB_DEF_SIZE_RX,
6978c2ecf20Sopenharmony_ci		       le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize));
6988c2ecf20Sopenharmony_ci	def_tx = min_t(u32, CDC_NCM_NTB_DEF_SIZE_TX,
6998c2ecf20Sopenharmony_ci		       le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize));
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	/* clamp rx_max and tx_max and inform device */
7028c2ecf20Sopenharmony_ci	cdc_ncm_update_rxtx_max(dev, def_rx, def_tx);
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	/* sanitize the modulus and remainder values */
7058c2ecf20Sopenharmony_ci	cdc_ncm_fix_modulus(dev);
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	/* set max datagram size */
7088c2ecf20Sopenharmony_ci	cdc_ncm_set_dgram_size(dev, cdc_ncm_max_dgram_size(dev));
7098c2ecf20Sopenharmony_ci	return 0;
7108c2ecf20Sopenharmony_ci}
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_cistatic void
7138c2ecf20Sopenharmony_cicdc_ncm_find_endpoints(struct usbnet *dev, struct usb_interface *intf)
7148c2ecf20Sopenharmony_ci{
7158c2ecf20Sopenharmony_ci	struct usb_host_endpoint *e, *in = NULL, *out = NULL;
7168c2ecf20Sopenharmony_ci	u8 ep;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	for (ep = 0; ep < intf->cur_altsetting->desc.bNumEndpoints; ep++) {
7198c2ecf20Sopenharmony_ci		e = intf->cur_altsetting->endpoint + ep;
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci		/* ignore endpoints which cannot transfer data */
7228c2ecf20Sopenharmony_ci		if (!usb_endpoint_maxp(&e->desc))
7238c2ecf20Sopenharmony_ci			continue;
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci		switch (e->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
7268c2ecf20Sopenharmony_ci		case USB_ENDPOINT_XFER_INT:
7278c2ecf20Sopenharmony_ci			if (usb_endpoint_dir_in(&e->desc)) {
7288c2ecf20Sopenharmony_ci				if (!dev->status)
7298c2ecf20Sopenharmony_ci					dev->status = e;
7308c2ecf20Sopenharmony_ci			}
7318c2ecf20Sopenharmony_ci			break;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci		case USB_ENDPOINT_XFER_BULK:
7348c2ecf20Sopenharmony_ci			if (usb_endpoint_dir_in(&e->desc)) {
7358c2ecf20Sopenharmony_ci				if (!in)
7368c2ecf20Sopenharmony_ci					in = e;
7378c2ecf20Sopenharmony_ci			} else {
7388c2ecf20Sopenharmony_ci				if (!out)
7398c2ecf20Sopenharmony_ci					out = e;
7408c2ecf20Sopenharmony_ci			}
7418c2ecf20Sopenharmony_ci			break;
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci		default:
7448c2ecf20Sopenharmony_ci			break;
7458c2ecf20Sopenharmony_ci		}
7468c2ecf20Sopenharmony_ci	}
7478c2ecf20Sopenharmony_ci	if (in && !dev->in)
7488c2ecf20Sopenharmony_ci		dev->in = usb_rcvbulkpipe(dev->udev,
7498c2ecf20Sopenharmony_ci					  in->desc.bEndpointAddress &
7508c2ecf20Sopenharmony_ci					  USB_ENDPOINT_NUMBER_MASK);
7518c2ecf20Sopenharmony_ci	if (out && !dev->out)
7528c2ecf20Sopenharmony_ci		dev->out = usb_sndbulkpipe(dev->udev,
7538c2ecf20Sopenharmony_ci					   out->desc.bEndpointAddress &
7548c2ecf20Sopenharmony_ci					   USB_ENDPOINT_NUMBER_MASK);
7558c2ecf20Sopenharmony_ci}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_cistatic void cdc_ncm_free(struct cdc_ncm_ctx *ctx)
7588c2ecf20Sopenharmony_ci{
7598c2ecf20Sopenharmony_ci	if (ctx == NULL)
7608c2ecf20Sopenharmony_ci		return;
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	if (ctx->tx_rem_skb != NULL) {
7638c2ecf20Sopenharmony_ci		dev_kfree_skb_any(ctx->tx_rem_skb);
7648c2ecf20Sopenharmony_ci		ctx->tx_rem_skb = NULL;
7658c2ecf20Sopenharmony_ci	}
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	if (ctx->tx_curr_skb != NULL) {
7688c2ecf20Sopenharmony_ci		dev_kfree_skb_any(ctx->tx_curr_skb);
7698c2ecf20Sopenharmony_ci		ctx->tx_curr_skb = NULL;
7708c2ecf20Sopenharmony_ci	}
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	if (ctx->is_ndp16)
7738c2ecf20Sopenharmony_ci		kfree(ctx->delayed_ndp16);
7748c2ecf20Sopenharmony_ci	else
7758c2ecf20Sopenharmony_ci		kfree(ctx->delayed_ndp32);
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	kfree(ctx);
7788c2ecf20Sopenharmony_ci}
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci/* we need to override the usbnet change_mtu ndo for two reasons:
7818c2ecf20Sopenharmony_ci *  - respect the negotiated maximum datagram size
7828c2ecf20Sopenharmony_ci *  - avoid unwanted changes to rx and tx buffers
7838c2ecf20Sopenharmony_ci */
7848c2ecf20Sopenharmony_ciint cdc_ncm_change_mtu(struct net_device *net, int new_mtu)
7858c2ecf20Sopenharmony_ci{
7868c2ecf20Sopenharmony_ci	struct usbnet *dev = netdev_priv(net);
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	net->mtu = new_mtu;
7898c2ecf20Sopenharmony_ci	cdc_ncm_set_dgram_size(dev, new_mtu + cdc_ncm_eth_hlen(dev));
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	return 0;
7928c2ecf20Sopenharmony_ci}
7938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cdc_ncm_change_mtu);
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_cistatic const struct net_device_ops cdc_ncm_netdev_ops = {
7968c2ecf20Sopenharmony_ci	.ndo_open	     = usbnet_open,
7978c2ecf20Sopenharmony_ci	.ndo_stop	     = usbnet_stop,
7988c2ecf20Sopenharmony_ci	.ndo_start_xmit	     = usbnet_start_xmit,
7998c2ecf20Sopenharmony_ci	.ndo_tx_timeout	     = usbnet_tx_timeout,
8008c2ecf20Sopenharmony_ci	.ndo_set_rx_mode     = usbnet_set_rx_mode,
8018c2ecf20Sopenharmony_ci	.ndo_get_stats64     = usbnet_get_stats64,
8028c2ecf20Sopenharmony_ci	.ndo_change_mtu	     = cdc_ncm_change_mtu,
8038c2ecf20Sopenharmony_ci	.ndo_set_mac_address = eth_mac_addr,
8048c2ecf20Sopenharmony_ci	.ndo_validate_addr   = eth_validate_addr,
8058c2ecf20Sopenharmony_ci};
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ciint cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting, int drvflags)
8088c2ecf20Sopenharmony_ci{
8098c2ecf20Sopenharmony_ci	struct cdc_ncm_ctx *ctx;
8108c2ecf20Sopenharmony_ci	struct usb_driver *driver;
8118c2ecf20Sopenharmony_ci	u8 *buf;
8128c2ecf20Sopenharmony_ci	int len;
8138c2ecf20Sopenharmony_ci	int temp;
8148c2ecf20Sopenharmony_ci	u8 iface_no;
8158c2ecf20Sopenharmony_ci	struct usb_cdc_parsed_header hdr;
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
8188c2ecf20Sopenharmony_ci	if (!ctx)
8198c2ecf20Sopenharmony_ci		return -ENOMEM;
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	hrtimer_init(&ctx->tx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
8228c2ecf20Sopenharmony_ci	ctx->tx_timer.function = &cdc_ncm_tx_timer_cb;
8238c2ecf20Sopenharmony_ci	tasklet_init(&ctx->bh, cdc_ncm_txpath_bh, (unsigned long)dev);
8248c2ecf20Sopenharmony_ci	atomic_set(&ctx->stop, 0);
8258c2ecf20Sopenharmony_ci	spin_lock_init(&ctx->mtx);
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	/* store ctx pointer in device data field */
8288c2ecf20Sopenharmony_ci	dev->data[0] = (unsigned long)ctx;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	/* only the control interface can be successfully probed */
8318c2ecf20Sopenharmony_ci	ctx->control = intf;
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	/* get some pointers */
8348c2ecf20Sopenharmony_ci	driver = driver_of(intf);
8358c2ecf20Sopenharmony_ci	buf = intf->cur_altsetting->extra;
8368c2ecf20Sopenharmony_ci	len = intf->cur_altsetting->extralen;
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	/* parse through descriptors associated with control interface */
8398c2ecf20Sopenharmony_ci	cdc_parse_cdc_header(&hdr, intf, buf, len);
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	if (hdr.usb_cdc_union_desc)
8428c2ecf20Sopenharmony_ci		ctx->data = usb_ifnum_to_if(dev->udev,
8438c2ecf20Sopenharmony_ci					    hdr.usb_cdc_union_desc->bSlaveInterface0);
8448c2ecf20Sopenharmony_ci	ctx->ether_desc = hdr.usb_cdc_ether_desc;
8458c2ecf20Sopenharmony_ci	ctx->func_desc = hdr.usb_cdc_ncm_desc;
8468c2ecf20Sopenharmony_ci	ctx->mbim_desc = hdr.usb_cdc_mbim_desc;
8478c2ecf20Sopenharmony_ci	ctx->mbim_extended_desc = hdr.usb_cdc_mbim_extended_desc;
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	/* some buggy devices have an IAD but no CDC Union */
8508c2ecf20Sopenharmony_ci	if (!hdr.usb_cdc_union_desc && intf->intf_assoc && intf->intf_assoc->bInterfaceCount == 2) {
8518c2ecf20Sopenharmony_ci		ctx->data = usb_ifnum_to_if(dev->udev, intf->cur_altsetting->desc.bInterfaceNumber + 1);
8528c2ecf20Sopenharmony_ci		dev_dbg(&intf->dev, "CDC Union missing - got slave from IAD\n");
8538c2ecf20Sopenharmony_ci	}
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	/* check if we got everything */
8568c2ecf20Sopenharmony_ci	if (!ctx->data) {
8578c2ecf20Sopenharmony_ci		dev_dbg(&intf->dev, "CDC Union missing and no IAD found\n");
8588c2ecf20Sopenharmony_ci		goto error;
8598c2ecf20Sopenharmony_ci	}
8608c2ecf20Sopenharmony_ci	if (cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting)) {
8618c2ecf20Sopenharmony_ci		if (!ctx->mbim_desc) {
8628c2ecf20Sopenharmony_ci			dev_dbg(&intf->dev, "MBIM functional descriptor missing\n");
8638c2ecf20Sopenharmony_ci			goto error;
8648c2ecf20Sopenharmony_ci		}
8658c2ecf20Sopenharmony_ci	} else {
8668c2ecf20Sopenharmony_ci		if (!ctx->ether_desc || !ctx->func_desc) {
8678c2ecf20Sopenharmony_ci			dev_dbg(&intf->dev, "NCM or ECM functional descriptors missing\n");
8688c2ecf20Sopenharmony_ci			goto error;
8698c2ecf20Sopenharmony_ci		}
8708c2ecf20Sopenharmony_ci	}
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	/* claim data interface, if different from control */
8738c2ecf20Sopenharmony_ci	if (ctx->data != ctx->control) {
8748c2ecf20Sopenharmony_ci		temp = usb_driver_claim_interface(driver, ctx->data, dev);
8758c2ecf20Sopenharmony_ci		if (temp) {
8768c2ecf20Sopenharmony_ci			dev_dbg(&intf->dev, "failed to claim data intf\n");
8778c2ecf20Sopenharmony_ci			goto error;
8788c2ecf20Sopenharmony_ci		}
8798c2ecf20Sopenharmony_ci	}
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	iface_no = ctx->data->cur_altsetting->desc.bInterfaceNumber;
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	/* Device-specific flags */
8848c2ecf20Sopenharmony_ci	ctx->drvflags = drvflags;
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	/* Reset data interface. Some devices will not reset properly
8878c2ecf20Sopenharmony_ci	 * unless they are configured first.  Toggle the altsetting to
8888c2ecf20Sopenharmony_ci	 * force a reset.
8898c2ecf20Sopenharmony_ci	 * Some other devices do not work properly with this procedure
8908c2ecf20Sopenharmony_ci	 * that can be avoided using quirk CDC_MBIM_FLAG_AVOID_ALTSETTING_TOGGLE
8918c2ecf20Sopenharmony_ci	 */
8928c2ecf20Sopenharmony_ci	if (!(ctx->drvflags & CDC_MBIM_FLAG_AVOID_ALTSETTING_TOGGLE))
8938c2ecf20Sopenharmony_ci		usb_set_interface(dev->udev, iface_no, data_altsetting);
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	temp = usb_set_interface(dev->udev, iface_no, 0);
8968c2ecf20Sopenharmony_ci	if (temp) {
8978c2ecf20Sopenharmony_ci		dev_dbg(&intf->dev, "set interface failed\n");
8988c2ecf20Sopenharmony_ci		goto error2;
8998c2ecf20Sopenharmony_ci	}
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	/* initialize basic device settings */
9028c2ecf20Sopenharmony_ci	if (cdc_ncm_init(dev))
9038c2ecf20Sopenharmony_ci		goto error2;
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	/* Some firmwares need a pause here or they will silently fail
9068c2ecf20Sopenharmony_ci	 * to set up the interface properly.  This value was decided
9078c2ecf20Sopenharmony_ci	 * empirically on a Sierra Wireless MC7455 running 02.08.02.00
9088c2ecf20Sopenharmony_ci	 * firmware.
9098c2ecf20Sopenharmony_ci	 */
9108c2ecf20Sopenharmony_ci	usleep_range(10000, 20000);
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	/* configure data interface */
9138c2ecf20Sopenharmony_ci	temp = usb_set_interface(dev->udev, iface_no, data_altsetting);
9148c2ecf20Sopenharmony_ci	if (temp) {
9158c2ecf20Sopenharmony_ci		dev_dbg(&intf->dev, "set interface failed\n");
9168c2ecf20Sopenharmony_ci		goto error2;
9178c2ecf20Sopenharmony_ci	}
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	cdc_ncm_find_endpoints(dev, ctx->data);
9208c2ecf20Sopenharmony_ci	cdc_ncm_find_endpoints(dev, ctx->control);
9218c2ecf20Sopenharmony_ci	if (!dev->in || !dev->out || !dev->status) {
9228c2ecf20Sopenharmony_ci		dev_dbg(&intf->dev, "failed to collect endpoints\n");
9238c2ecf20Sopenharmony_ci		goto error2;
9248c2ecf20Sopenharmony_ci	}
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	usb_set_intfdata(ctx->data, dev);
9278c2ecf20Sopenharmony_ci	usb_set_intfdata(ctx->control, dev);
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	if (ctx->ether_desc) {
9308c2ecf20Sopenharmony_ci		temp = usbnet_get_ethernet_addr(dev, ctx->ether_desc->iMACAddress);
9318c2ecf20Sopenharmony_ci		if (temp) {
9328c2ecf20Sopenharmony_ci			dev_dbg(&intf->dev, "failed to get mac address\n");
9338c2ecf20Sopenharmony_ci			goto error2;
9348c2ecf20Sopenharmony_ci		}
9358c2ecf20Sopenharmony_ci		dev_info(&intf->dev, "MAC-Address: %pM\n", dev->net->dev_addr);
9368c2ecf20Sopenharmony_ci	}
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	/* finish setting up the device specific data */
9398c2ecf20Sopenharmony_ci	cdc_ncm_setup(dev);
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci	/* Allocate the delayed NDP if needed. */
9428c2ecf20Sopenharmony_ci	if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END) {
9438c2ecf20Sopenharmony_ci		if (ctx->is_ndp16) {
9448c2ecf20Sopenharmony_ci			ctx->delayed_ndp16 = kzalloc(ctx->max_ndp_size, GFP_KERNEL);
9458c2ecf20Sopenharmony_ci			if (!ctx->delayed_ndp16)
9468c2ecf20Sopenharmony_ci				goto error2;
9478c2ecf20Sopenharmony_ci		} else {
9488c2ecf20Sopenharmony_ci			ctx->delayed_ndp32 = kzalloc(ctx->max_ndp_size, GFP_KERNEL);
9498c2ecf20Sopenharmony_ci			if (!ctx->delayed_ndp32)
9508c2ecf20Sopenharmony_ci				goto error2;
9518c2ecf20Sopenharmony_ci		}
9528c2ecf20Sopenharmony_ci		dev_info(&intf->dev, "NDP will be placed at end of frame for this device.");
9538c2ecf20Sopenharmony_ci	}
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	/* override ethtool_ops */
9568c2ecf20Sopenharmony_ci	dev->net->ethtool_ops = &cdc_ncm_ethtool_ops;
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci	/* add our sysfs attrs */
9598c2ecf20Sopenharmony_ci	dev->net->sysfs_groups[0] = &cdc_ncm_sysfs_attr_group;
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	/* must handle MTU changes */
9628c2ecf20Sopenharmony_ci	dev->net->netdev_ops = &cdc_ncm_netdev_ops;
9638c2ecf20Sopenharmony_ci	dev->net->max_mtu = cdc_ncm_max_dgram_size(dev) - cdc_ncm_eth_hlen(dev);
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	return 0;
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_cierror2:
9688c2ecf20Sopenharmony_ci	usb_set_intfdata(ctx->control, NULL);
9698c2ecf20Sopenharmony_ci	usb_set_intfdata(ctx->data, NULL);
9708c2ecf20Sopenharmony_ci	if (ctx->data != ctx->control)
9718c2ecf20Sopenharmony_ci		usb_driver_release_interface(driver, ctx->data);
9728c2ecf20Sopenharmony_cierror:
9738c2ecf20Sopenharmony_ci	cdc_ncm_free((struct cdc_ncm_ctx *)dev->data[0]);
9748c2ecf20Sopenharmony_ci	dev->data[0] = 0;
9758c2ecf20Sopenharmony_ci	dev_info(&intf->dev, "bind() failure\n");
9768c2ecf20Sopenharmony_ci	return -ENODEV;
9778c2ecf20Sopenharmony_ci}
9788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cdc_ncm_bind_common);
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_civoid cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf)
9818c2ecf20Sopenharmony_ci{
9828c2ecf20Sopenharmony_ci	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
9838c2ecf20Sopenharmony_ci	struct usb_driver *driver = driver_of(intf);
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	if (ctx == NULL)
9868c2ecf20Sopenharmony_ci		return;		/* no setup */
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci	atomic_set(&ctx->stop, 1);
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	hrtimer_cancel(&ctx->tx_timer);
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci	tasklet_kill(&ctx->bh);
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	/* handle devices with combined control and data interface */
9958c2ecf20Sopenharmony_ci	if (ctx->control == ctx->data)
9968c2ecf20Sopenharmony_ci		ctx->data = NULL;
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci	/* disconnect master --> disconnect slave */
9998c2ecf20Sopenharmony_ci	if (intf == ctx->control && ctx->data) {
10008c2ecf20Sopenharmony_ci		usb_set_intfdata(ctx->data, NULL);
10018c2ecf20Sopenharmony_ci		usb_driver_release_interface(driver, ctx->data);
10028c2ecf20Sopenharmony_ci		ctx->data = NULL;
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci	} else if (intf == ctx->data && ctx->control) {
10058c2ecf20Sopenharmony_ci		usb_set_intfdata(ctx->control, NULL);
10068c2ecf20Sopenharmony_ci		usb_driver_release_interface(driver, ctx->control);
10078c2ecf20Sopenharmony_ci		ctx->control = NULL;
10088c2ecf20Sopenharmony_ci	}
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	usb_set_intfdata(intf, NULL);
10118c2ecf20Sopenharmony_ci	cdc_ncm_free(ctx);
10128c2ecf20Sopenharmony_ci}
10138c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cdc_ncm_unbind);
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci/* Return the number of the MBIM control interface altsetting iff it
10168c2ecf20Sopenharmony_ci * is preferred and available,
10178c2ecf20Sopenharmony_ci */
10188c2ecf20Sopenharmony_ciu8 cdc_ncm_select_altsetting(struct usb_interface *intf)
10198c2ecf20Sopenharmony_ci{
10208c2ecf20Sopenharmony_ci	struct usb_host_interface *alt;
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	/* The MBIM spec defines a NCM compatible default altsetting,
10238c2ecf20Sopenharmony_ci	 * which we may have matched:
10248c2ecf20Sopenharmony_ci	 *
10258c2ecf20Sopenharmony_ci	 *  "Functions that implement both NCM 1.0 and MBIM (an
10268c2ecf20Sopenharmony_ci	 *   “NCM/MBIM function”) according to this recommendation
10278c2ecf20Sopenharmony_ci	 *   shall provide two alternate settings for the
10288c2ecf20Sopenharmony_ci	 *   Communication Interface.  Alternate setting 0, and the
10298c2ecf20Sopenharmony_ci	 *   associated class and endpoint descriptors, shall be
10308c2ecf20Sopenharmony_ci	 *   constructed according to the rules given for the
10318c2ecf20Sopenharmony_ci	 *   Communication Interface in section 5 of [USBNCM10].
10328c2ecf20Sopenharmony_ci	 *   Alternate setting 1, and the associated class and
10338c2ecf20Sopenharmony_ci	 *   endpoint descriptors, shall be constructed according to
10348c2ecf20Sopenharmony_ci	 *   the rules given in section 6 (USB Device Model) of this
10358c2ecf20Sopenharmony_ci	 *   specification."
10368c2ecf20Sopenharmony_ci	 */
10378c2ecf20Sopenharmony_ci	if (intf->num_altsetting < 2)
10388c2ecf20Sopenharmony_ci		return intf->cur_altsetting->desc.bAlternateSetting;
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci	if (prefer_mbim) {
10418c2ecf20Sopenharmony_ci		alt = usb_altnum_to_altsetting(intf, CDC_NCM_COMM_ALTSETTING_MBIM);
10428c2ecf20Sopenharmony_ci		if (alt && cdc_ncm_comm_intf_is_mbim(alt))
10438c2ecf20Sopenharmony_ci			return CDC_NCM_COMM_ALTSETTING_MBIM;
10448c2ecf20Sopenharmony_ci	}
10458c2ecf20Sopenharmony_ci	return CDC_NCM_COMM_ALTSETTING_NCM;
10468c2ecf20Sopenharmony_ci}
10478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cdc_ncm_select_altsetting);
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_cistatic int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)
10508c2ecf20Sopenharmony_ci{
10518c2ecf20Sopenharmony_ci	/* MBIM backwards compatible function? */
10528c2ecf20Sopenharmony_ci	if (cdc_ncm_select_altsetting(intf) != CDC_NCM_COMM_ALTSETTING_NCM)
10538c2ecf20Sopenharmony_ci		return -ENODEV;
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci	/* The NCM data altsetting is fixed, so we hard-coded it.
10568c2ecf20Sopenharmony_ci	 * Additionally, generic NCM devices are assumed to accept arbitrarily
10578c2ecf20Sopenharmony_ci	 * placed NDP.
10588c2ecf20Sopenharmony_ci	 */
10598c2ecf20Sopenharmony_ci	return cdc_ncm_bind_common(dev, intf, CDC_NCM_DATA_ALTSETTING_NCM, 0);
10608c2ecf20Sopenharmony_ci}
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_cistatic void cdc_ncm_align_tail(struct sk_buff *skb, size_t modulus, size_t remainder, size_t max)
10638c2ecf20Sopenharmony_ci{
10648c2ecf20Sopenharmony_ci	size_t align = ALIGN(skb->len, modulus) - skb->len + remainder;
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	if (skb->len + align > max)
10678c2ecf20Sopenharmony_ci		align = max - skb->len;
10688c2ecf20Sopenharmony_ci	if (align && skb_tailroom(skb) >= align)
10698c2ecf20Sopenharmony_ci		skb_put_zero(skb, align);
10708c2ecf20Sopenharmony_ci}
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci/* return a pointer to a valid struct usb_cdc_ncm_ndp16 of type sign, possibly
10738c2ecf20Sopenharmony_ci * allocating a new one within skb
10748c2ecf20Sopenharmony_ci */
10758c2ecf20Sopenharmony_cistatic struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign, size_t reserve)
10768c2ecf20Sopenharmony_ci{
10778c2ecf20Sopenharmony_ci	struct usb_cdc_ncm_ndp16 *ndp16 = NULL;
10788c2ecf20Sopenharmony_ci	struct usb_cdc_ncm_nth16 *nth16 = (void *)skb->data;
10798c2ecf20Sopenharmony_ci	size_t ndpoffset = le16_to_cpu(nth16->wNdpIndex);
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	/* If NDP should be moved to the end of the NCM package, we can't follow the
10828c2ecf20Sopenharmony_ci	* NTH16 header as we would normally do. NDP isn't written to the SKB yet, and
10838c2ecf20Sopenharmony_ci	* the wNdpIndex field in the header is actually not consistent with reality. It will be later.
10848c2ecf20Sopenharmony_ci	*/
10858c2ecf20Sopenharmony_ci	if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END) {
10868c2ecf20Sopenharmony_ci		if (ctx->delayed_ndp16->dwSignature == sign)
10878c2ecf20Sopenharmony_ci			return ctx->delayed_ndp16;
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci		/* We can only push a single NDP to the end. Return
10908c2ecf20Sopenharmony_ci		 * NULL to send what we've already got and queue this
10918c2ecf20Sopenharmony_ci		 * skb for later.
10928c2ecf20Sopenharmony_ci		 */
10938c2ecf20Sopenharmony_ci		else if (ctx->delayed_ndp16->dwSignature)
10948c2ecf20Sopenharmony_ci			return NULL;
10958c2ecf20Sopenharmony_ci	}
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci	/* follow the chain of NDPs, looking for a match */
10988c2ecf20Sopenharmony_ci	while (ndpoffset) {
10998c2ecf20Sopenharmony_ci		ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb->data + ndpoffset);
11008c2ecf20Sopenharmony_ci		if  (ndp16->dwSignature == sign)
11018c2ecf20Sopenharmony_ci			return ndp16;
11028c2ecf20Sopenharmony_ci		ndpoffset = le16_to_cpu(ndp16->wNextNdpIndex);
11038c2ecf20Sopenharmony_ci	}
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	/* align new NDP */
11068c2ecf20Sopenharmony_ci	if (!(ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END))
11078c2ecf20Sopenharmony_ci		cdc_ncm_align_tail(skb, ctx->tx_ndp_modulus, 0, ctx->tx_curr_size);
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_ci	/* verify that there is room for the NDP and the datagram (reserve) */
11108c2ecf20Sopenharmony_ci	if ((ctx->tx_curr_size - skb->len - reserve) < ctx->max_ndp_size)
11118c2ecf20Sopenharmony_ci		return NULL;
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	/* link to it */
11148c2ecf20Sopenharmony_ci	if (ndp16)
11158c2ecf20Sopenharmony_ci		ndp16->wNextNdpIndex = cpu_to_le16(skb->len);
11168c2ecf20Sopenharmony_ci	else
11178c2ecf20Sopenharmony_ci		nth16->wNdpIndex = cpu_to_le16(skb->len);
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci	/* push a new empty NDP */
11208c2ecf20Sopenharmony_ci	if (!(ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END))
11218c2ecf20Sopenharmony_ci		ndp16 = skb_put_zero(skb, ctx->max_ndp_size);
11228c2ecf20Sopenharmony_ci	else
11238c2ecf20Sopenharmony_ci		ndp16 = ctx->delayed_ndp16;
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	ndp16->dwSignature = sign;
11268c2ecf20Sopenharmony_ci	ndp16->wLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_ndp16) + sizeof(struct usb_cdc_ncm_dpe16));
11278c2ecf20Sopenharmony_ci	return ndp16;
11288c2ecf20Sopenharmony_ci}
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_cistatic struct usb_cdc_ncm_ndp32 *cdc_ncm_ndp32(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign, size_t reserve)
11318c2ecf20Sopenharmony_ci{
11328c2ecf20Sopenharmony_ci	struct usb_cdc_ncm_ndp32 *ndp32 = NULL;
11338c2ecf20Sopenharmony_ci	struct usb_cdc_ncm_nth32 *nth32 = (void *)skb->data;
11348c2ecf20Sopenharmony_ci	size_t ndpoffset = le32_to_cpu(nth32->dwNdpIndex);
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	/* If NDP should be moved to the end of the NCM package, we can't follow the
11378c2ecf20Sopenharmony_ci	 * NTH32 header as we would normally do. NDP isn't written to the SKB yet, and
11388c2ecf20Sopenharmony_ci	 * the wNdpIndex field in the header is actually not consistent with reality. It will be later.
11398c2ecf20Sopenharmony_ci	 */
11408c2ecf20Sopenharmony_ci	if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END) {
11418c2ecf20Sopenharmony_ci		if (ctx->delayed_ndp32->dwSignature == sign)
11428c2ecf20Sopenharmony_ci			return ctx->delayed_ndp32;
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci		/* We can only push a single NDP to the end. Return
11458c2ecf20Sopenharmony_ci		 * NULL to send what we've already got and queue this
11468c2ecf20Sopenharmony_ci		 * skb for later.
11478c2ecf20Sopenharmony_ci		 */
11488c2ecf20Sopenharmony_ci		else if (ctx->delayed_ndp32->dwSignature)
11498c2ecf20Sopenharmony_ci			return NULL;
11508c2ecf20Sopenharmony_ci	}
11518c2ecf20Sopenharmony_ci
11528c2ecf20Sopenharmony_ci	/* follow the chain of NDPs, looking for a match */
11538c2ecf20Sopenharmony_ci	while (ndpoffset) {
11548c2ecf20Sopenharmony_ci		ndp32 = (struct usb_cdc_ncm_ndp32 *)(skb->data + ndpoffset);
11558c2ecf20Sopenharmony_ci		if  (ndp32->dwSignature == sign)
11568c2ecf20Sopenharmony_ci			return ndp32;
11578c2ecf20Sopenharmony_ci		ndpoffset = le32_to_cpu(ndp32->dwNextNdpIndex);
11588c2ecf20Sopenharmony_ci	}
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci	/* align new NDP */
11618c2ecf20Sopenharmony_ci	if (!(ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END))
11628c2ecf20Sopenharmony_ci		cdc_ncm_align_tail(skb, ctx->tx_ndp_modulus, 0, ctx->tx_curr_size);
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_ci	/* verify that there is room for the NDP and the datagram (reserve) */
11658c2ecf20Sopenharmony_ci	if ((ctx->tx_curr_size - skb->len - reserve) < ctx->max_ndp_size)
11668c2ecf20Sopenharmony_ci		return NULL;
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	/* link to it */
11698c2ecf20Sopenharmony_ci	if (ndp32)
11708c2ecf20Sopenharmony_ci		ndp32->dwNextNdpIndex = cpu_to_le32(skb->len);
11718c2ecf20Sopenharmony_ci	else
11728c2ecf20Sopenharmony_ci		nth32->dwNdpIndex = cpu_to_le32(skb->len);
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	/* push a new empty NDP */
11758c2ecf20Sopenharmony_ci	if (!(ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END))
11768c2ecf20Sopenharmony_ci		ndp32 = skb_put_zero(skb, ctx->max_ndp_size);
11778c2ecf20Sopenharmony_ci	else
11788c2ecf20Sopenharmony_ci		ndp32 = ctx->delayed_ndp32;
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci	ndp32->dwSignature = sign;
11818c2ecf20Sopenharmony_ci	ndp32->wLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_ndp32) + sizeof(struct usb_cdc_ncm_dpe32));
11828c2ecf20Sopenharmony_ci	return ndp32;
11838c2ecf20Sopenharmony_ci}
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_cistruct sk_buff *
11868c2ecf20Sopenharmony_cicdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
11878c2ecf20Sopenharmony_ci{
11888c2ecf20Sopenharmony_ci	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
11898c2ecf20Sopenharmony_ci	union {
11908c2ecf20Sopenharmony_ci		struct usb_cdc_ncm_nth16 *nth16;
11918c2ecf20Sopenharmony_ci		struct usb_cdc_ncm_nth32 *nth32;
11928c2ecf20Sopenharmony_ci	} nth;
11938c2ecf20Sopenharmony_ci	union {
11948c2ecf20Sopenharmony_ci		struct usb_cdc_ncm_ndp16 *ndp16;
11958c2ecf20Sopenharmony_ci		struct usb_cdc_ncm_ndp32 *ndp32;
11968c2ecf20Sopenharmony_ci	} ndp;
11978c2ecf20Sopenharmony_ci	struct sk_buff *skb_out;
11988c2ecf20Sopenharmony_ci	u16 n = 0, index, ndplen;
11998c2ecf20Sopenharmony_ci	u8 ready2send = 0;
12008c2ecf20Sopenharmony_ci	u32 delayed_ndp_size;
12018c2ecf20Sopenharmony_ci	size_t padding_count;
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	/* When our NDP gets written in cdc_ncm_ndp(), then skb_out->len gets updated
12048c2ecf20Sopenharmony_ci	 * accordingly. Otherwise, we should check here.
12058c2ecf20Sopenharmony_ci	 */
12068c2ecf20Sopenharmony_ci	if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END)
12078c2ecf20Sopenharmony_ci		delayed_ndp_size = ctx->max_ndp_size +
12088c2ecf20Sopenharmony_ci			max_t(u32,
12098c2ecf20Sopenharmony_ci			      ctx->tx_ndp_modulus,
12108c2ecf20Sopenharmony_ci			      ctx->tx_modulus + ctx->tx_remainder) - 1;
12118c2ecf20Sopenharmony_ci	else
12128c2ecf20Sopenharmony_ci		delayed_ndp_size = 0;
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_ci	/* if there is a remaining skb, it gets priority */
12158c2ecf20Sopenharmony_ci	if (skb != NULL) {
12168c2ecf20Sopenharmony_ci		swap(skb, ctx->tx_rem_skb);
12178c2ecf20Sopenharmony_ci		swap(sign, ctx->tx_rem_sign);
12188c2ecf20Sopenharmony_ci	} else {
12198c2ecf20Sopenharmony_ci		ready2send = 1;
12208c2ecf20Sopenharmony_ci	}
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci	/* check if we are resuming an OUT skb */
12238c2ecf20Sopenharmony_ci	skb_out = ctx->tx_curr_skb;
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ci	/* allocate a new OUT skb */
12268c2ecf20Sopenharmony_ci	if (!skb_out) {
12278c2ecf20Sopenharmony_ci		if (ctx->tx_low_mem_val == 0) {
12288c2ecf20Sopenharmony_ci			ctx->tx_curr_size = ctx->tx_max;
12298c2ecf20Sopenharmony_ci			skb_out = alloc_skb(ctx->tx_curr_size, GFP_ATOMIC);
12308c2ecf20Sopenharmony_ci			/* If the memory allocation fails we will wait longer
12318c2ecf20Sopenharmony_ci			 * each time before attempting another full size
12328c2ecf20Sopenharmony_ci			 * allocation again to not overload the system
12338c2ecf20Sopenharmony_ci			 * further.
12348c2ecf20Sopenharmony_ci			 */
12358c2ecf20Sopenharmony_ci			if (skb_out == NULL) {
12368c2ecf20Sopenharmony_ci				/* If even the smallest allocation fails, abort. */
12378c2ecf20Sopenharmony_ci				if (ctx->tx_curr_size == USB_CDC_NCM_NTB_MIN_OUT_SIZE)
12388c2ecf20Sopenharmony_ci					goto alloc_failed;
12398c2ecf20Sopenharmony_ci				ctx->tx_low_mem_max_cnt = min(ctx->tx_low_mem_max_cnt + 1,
12408c2ecf20Sopenharmony_ci							      (unsigned)CDC_NCM_LOW_MEM_MAX_CNT);
12418c2ecf20Sopenharmony_ci				ctx->tx_low_mem_val = ctx->tx_low_mem_max_cnt;
12428c2ecf20Sopenharmony_ci			}
12438c2ecf20Sopenharmony_ci		}
12448c2ecf20Sopenharmony_ci		if (skb_out == NULL) {
12458c2ecf20Sopenharmony_ci			/* See if a very small allocation is possible.
12468c2ecf20Sopenharmony_ci			 * We will send this packet immediately and hope
12478c2ecf20Sopenharmony_ci			 * that there is more memory available later.
12488c2ecf20Sopenharmony_ci			 */
12498c2ecf20Sopenharmony_ci			if (skb)
12508c2ecf20Sopenharmony_ci				ctx->tx_curr_size = max(skb->len,
12518c2ecf20Sopenharmony_ci					(u32)USB_CDC_NCM_NTB_MIN_OUT_SIZE);
12528c2ecf20Sopenharmony_ci			else
12538c2ecf20Sopenharmony_ci				ctx->tx_curr_size = USB_CDC_NCM_NTB_MIN_OUT_SIZE;
12548c2ecf20Sopenharmony_ci			skb_out = alloc_skb(ctx->tx_curr_size, GFP_ATOMIC);
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci			/* No allocation possible so we will abort */
12578c2ecf20Sopenharmony_ci			if (!skb_out)
12588c2ecf20Sopenharmony_ci				goto alloc_failed;
12598c2ecf20Sopenharmony_ci			ctx->tx_low_mem_val--;
12608c2ecf20Sopenharmony_ci		}
12618c2ecf20Sopenharmony_ci		if (ctx->is_ndp16) {
12628c2ecf20Sopenharmony_ci			/* fill out the initial 16-bit NTB header */
12638c2ecf20Sopenharmony_ci			nth.nth16 = skb_put_zero(skb_out, sizeof(struct usb_cdc_ncm_nth16));
12648c2ecf20Sopenharmony_ci			nth.nth16->dwSignature = cpu_to_le32(USB_CDC_NCM_NTH16_SIGN);
12658c2ecf20Sopenharmony_ci			nth.nth16->wHeaderLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_nth16));
12668c2ecf20Sopenharmony_ci			nth.nth16->wSequence = cpu_to_le16(ctx->tx_seq++);
12678c2ecf20Sopenharmony_ci		} else {
12688c2ecf20Sopenharmony_ci			/* fill out the initial 32-bit NTB header */
12698c2ecf20Sopenharmony_ci			nth.nth32 = skb_put_zero(skb_out, sizeof(struct usb_cdc_ncm_nth32));
12708c2ecf20Sopenharmony_ci			nth.nth32->dwSignature = cpu_to_le32(USB_CDC_NCM_NTH32_SIGN);
12718c2ecf20Sopenharmony_ci			nth.nth32->wHeaderLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_nth32));
12728c2ecf20Sopenharmony_ci			nth.nth32->wSequence = cpu_to_le16(ctx->tx_seq++);
12738c2ecf20Sopenharmony_ci		}
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci		/* count total number of frames in this NTB */
12768c2ecf20Sopenharmony_ci		ctx->tx_curr_frame_num = 0;
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci		/* recent payload counter for this skb_out */
12798c2ecf20Sopenharmony_ci		ctx->tx_curr_frame_payload = 0;
12808c2ecf20Sopenharmony_ci	}
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci	for (n = ctx->tx_curr_frame_num; n < ctx->tx_max_datagrams; n++) {
12838c2ecf20Sopenharmony_ci		/* send any remaining skb first */
12848c2ecf20Sopenharmony_ci		if (skb == NULL) {
12858c2ecf20Sopenharmony_ci			skb = ctx->tx_rem_skb;
12868c2ecf20Sopenharmony_ci			sign = ctx->tx_rem_sign;
12878c2ecf20Sopenharmony_ci			ctx->tx_rem_skb = NULL;
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci			/* check for end of skb */
12908c2ecf20Sopenharmony_ci			if (skb == NULL)
12918c2ecf20Sopenharmony_ci				break;
12928c2ecf20Sopenharmony_ci		}
12938c2ecf20Sopenharmony_ci
12948c2ecf20Sopenharmony_ci		/* get the appropriate NDP for this skb */
12958c2ecf20Sopenharmony_ci		if (ctx->is_ndp16)
12968c2ecf20Sopenharmony_ci			ndp.ndp16 = cdc_ncm_ndp16(ctx, skb_out, sign, skb->len + ctx->tx_modulus + ctx->tx_remainder);
12978c2ecf20Sopenharmony_ci		else
12988c2ecf20Sopenharmony_ci			ndp.ndp32 = cdc_ncm_ndp32(ctx, skb_out, sign, skb->len + ctx->tx_modulus + ctx->tx_remainder);
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci		/* align beginning of next frame */
13018c2ecf20Sopenharmony_ci		cdc_ncm_align_tail(skb_out,  ctx->tx_modulus, ctx->tx_remainder, ctx->tx_curr_size);
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_ci		/* check if we had enough room left for both NDP and frame */
13048c2ecf20Sopenharmony_ci		if ((ctx->is_ndp16 && !ndp.ndp16) || (!ctx->is_ndp16 && !ndp.ndp32) ||
13058c2ecf20Sopenharmony_ci		    skb_out->len + skb->len + delayed_ndp_size > ctx->tx_curr_size) {
13068c2ecf20Sopenharmony_ci			if (n == 0) {
13078c2ecf20Sopenharmony_ci				/* won't fit, MTU problem? */
13088c2ecf20Sopenharmony_ci				dev_kfree_skb_any(skb);
13098c2ecf20Sopenharmony_ci				skb = NULL;
13108c2ecf20Sopenharmony_ci				dev->net->stats.tx_dropped++;
13118c2ecf20Sopenharmony_ci			} else {
13128c2ecf20Sopenharmony_ci				/* no room for skb - store for later */
13138c2ecf20Sopenharmony_ci				if (ctx->tx_rem_skb != NULL) {
13148c2ecf20Sopenharmony_ci					dev_kfree_skb_any(ctx->tx_rem_skb);
13158c2ecf20Sopenharmony_ci					dev->net->stats.tx_dropped++;
13168c2ecf20Sopenharmony_ci				}
13178c2ecf20Sopenharmony_ci				ctx->tx_rem_skb = skb;
13188c2ecf20Sopenharmony_ci				ctx->tx_rem_sign = sign;
13198c2ecf20Sopenharmony_ci				skb = NULL;
13208c2ecf20Sopenharmony_ci				ready2send = 1;
13218c2ecf20Sopenharmony_ci				ctx->tx_reason_ntb_full++;	/* count reason for transmitting */
13228c2ecf20Sopenharmony_ci			}
13238c2ecf20Sopenharmony_ci			break;
13248c2ecf20Sopenharmony_ci		}
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci		/* calculate frame number withing this NDP */
13278c2ecf20Sopenharmony_ci		if (ctx->is_ndp16) {
13288c2ecf20Sopenharmony_ci			ndplen = le16_to_cpu(ndp.ndp16->wLength);
13298c2ecf20Sopenharmony_ci			index = (ndplen - sizeof(struct usb_cdc_ncm_ndp16)) / sizeof(struct usb_cdc_ncm_dpe16) - 1;
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci			/* OK, add this skb */
13328c2ecf20Sopenharmony_ci			ndp.ndp16->dpe16[index].wDatagramLength = cpu_to_le16(skb->len);
13338c2ecf20Sopenharmony_ci			ndp.ndp16->dpe16[index].wDatagramIndex = cpu_to_le16(skb_out->len);
13348c2ecf20Sopenharmony_ci			ndp.ndp16->wLength = cpu_to_le16(ndplen + sizeof(struct usb_cdc_ncm_dpe16));
13358c2ecf20Sopenharmony_ci		} else {
13368c2ecf20Sopenharmony_ci			ndplen = le16_to_cpu(ndp.ndp32->wLength);
13378c2ecf20Sopenharmony_ci			index = (ndplen - sizeof(struct usb_cdc_ncm_ndp32)) / sizeof(struct usb_cdc_ncm_dpe32) - 1;
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ci			ndp.ndp32->dpe32[index].dwDatagramLength = cpu_to_le32(skb->len);
13408c2ecf20Sopenharmony_ci			ndp.ndp32->dpe32[index].dwDatagramIndex = cpu_to_le32(skb_out->len);
13418c2ecf20Sopenharmony_ci			ndp.ndp32->wLength = cpu_to_le16(ndplen + sizeof(struct usb_cdc_ncm_dpe32));
13428c2ecf20Sopenharmony_ci		}
13438c2ecf20Sopenharmony_ci		skb_put_data(skb_out, skb->data, skb->len);
13448c2ecf20Sopenharmony_ci		ctx->tx_curr_frame_payload += skb->len;	/* count real tx payload data */
13458c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb);
13468c2ecf20Sopenharmony_ci		skb = NULL;
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci		/* send now if this NDP is full */
13498c2ecf20Sopenharmony_ci		if (index >= CDC_NCM_DPT_DATAGRAMS_MAX) {
13508c2ecf20Sopenharmony_ci			ready2send = 1;
13518c2ecf20Sopenharmony_ci			ctx->tx_reason_ndp_full++;	/* count reason for transmitting */
13528c2ecf20Sopenharmony_ci			break;
13538c2ecf20Sopenharmony_ci		}
13548c2ecf20Sopenharmony_ci	}
13558c2ecf20Sopenharmony_ci
13568c2ecf20Sopenharmony_ci	/* free up any dangling skb */
13578c2ecf20Sopenharmony_ci	if (skb != NULL) {
13588c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb);
13598c2ecf20Sopenharmony_ci		skb = NULL;
13608c2ecf20Sopenharmony_ci		dev->net->stats.tx_dropped++;
13618c2ecf20Sopenharmony_ci	}
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci	ctx->tx_curr_frame_num = n;
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_ci	if (n == 0) {
13668c2ecf20Sopenharmony_ci		/* wait for more frames */
13678c2ecf20Sopenharmony_ci		/* push variables */
13688c2ecf20Sopenharmony_ci		ctx->tx_curr_skb = skb_out;
13698c2ecf20Sopenharmony_ci		goto exit_no_skb;
13708c2ecf20Sopenharmony_ci
13718c2ecf20Sopenharmony_ci	} else if ((n < ctx->tx_max_datagrams) && (ready2send == 0) && (ctx->timer_interval > 0)) {
13728c2ecf20Sopenharmony_ci		/* wait for more frames */
13738c2ecf20Sopenharmony_ci		/* push variables */
13748c2ecf20Sopenharmony_ci		ctx->tx_curr_skb = skb_out;
13758c2ecf20Sopenharmony_ci		/* set the pending count */
13768c2ecf20Sopenharmony_ci		if (n < CDC_NCM_RESTART_TIMER_DATAGRAM_CNT)
13778c2ecf20Sopenharmony_ci			ctx->tx_timer_pending = CDC_NCM_TIMER_PENDING_CNT;
13788c2ecf20Sopenharmony_ci		goto exit_no_skb;
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci	} else {
13818c2ecf20Sopenharmony_ci		if (n == ctx->tx_max_datagrams)
13828c2ecf20Sopenharmony_ci			ctx->tx_reason_max_datagram++;	/* count reason for transmitting */
13838c2ecf20Sopenharmony_ci		/* frame goes out */
13848c2ecf20Sopenharmony_ci		/* variables will be reset at next call */
13858c2ecf20Sopenharmony_ci	}
13868c2ecf20Sopenharmony_ci
13878c2ecf20Sopenharmony_ci	/* If requested, put NDP at end of frame. */
13888c2ecf20Sopenharmony_ci	if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END) {
13898c2ecf20Sopenharmony_ci		if (ctx->is_ndp16) {
13908c2ecf20Sopenharmony_ci			nth.nth16 = (struct usb_cdc_ncm_nth16 *)skb_out->data;
13918c2ecf20Sopenharmony_ci			cdc_ncm_align_tail(skb_out, ctx->tx_ndp_modulus, 0, ctx->tx_curr_size - ctx->max_ndp_size);
13928c2ecf20Sopenharmony_ci			nth.nth16->wNdpIndex = cpu_to_le16(skb_out->len);
13938c2ecf20Sopenharmony_ci			skb_put_data(skb_out, ctx->delayed_ndp16, ctx->max_ndp_size);
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_ci			/* Zero out delayed NDP - signature checking will naturally fail. */
13968c2ecf20Sopenharmony_ci			ndp.ndp16 = memset(ctx->delayed_ndp16, 0, ctx->max_ndp_size);
13978c2ecf20Sopenharmony_ci		} else {
13988c2ecf20Sopenharmony_ci			nth.nth32 = (struct usb_cdc_ncm_nth32 *)skb_out->data;
13998c2ecf20Sopenharmony_ci			cdc_ncm_align_tail(skb_out, ctx->tx_ndp_modulus, 0, ctx->tx_curr_size - ctx->max_ndp_size);
14008c2ecf20Sopenharmony_ci			nth.nth32->dwNdpIndex = cpu_to_le32(skb_out->len);
14018c2ecf20Sopenharmony_ci			skb_put_data(skb_out, ctx->delayed_ndp32, ctx->max_ndp_size);
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci			ndp.ndp32 = memset(ctx->delayed_ndp32, 0, ctx->max_ndp_size);
14048c2ecf20Sopenharmony_ci		}
14058c2ecf20Sopenharmony_ci	}
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_ci	/* If collected data size is less or equal ctx->min_tx_pkt
14088c2ecf20Sopenharmony_ci	 * bytes, we send buffers as it is. If we get more data, it
14098c2ecf20Sopenharmony_ci	 * would be more efficient for USB HS mobile device with DMA
14108c2ecf20Sopenharmony_ci	 * engine to receive a full size NTB, than canceling DMA
14118c2ecf20Sopenharmony_ci	 * transfer and receiving a short packet.
14128c2ecf20Sopenharmony_ci	 *
14138c2ecf20Sopenharmony_ci	 * This optimization support is pointless if we end up sending
14148c2ecf20Sopenharmony_ci	 * a ZLP after full sized NTBs.
14158c2ecf20Sopenharmony_ci	 */
14168c2ecf20Sopenharmony_ci	if (!(dev->driver_info->flags & FLAG_SEND_ZLP) &&
14178c2ecf20Sopenharmony_ci	    skb_out->len > ctx->min_tx_pkt) {
14188c2ecf20Sopenharmony_ci		padding_count = ctx->tx_curr_size - skb_out->len;
14198c2ecf20Sopenharmony_ci		if (!WARN_ON(padding_count > ctx->tx_curr_size))
14208c2ecf20Sopenharmony_ci			skb_put_zero(skb_out, padding_count);
14218c2ecf20Sopenharmony_ci	} else if (skb_out->len < ctx->tx_curr_size &&
14228c2ecf20Sopenharmony_ci		   (skb_out->len % dev->maxpacket) == 0) {
14238c2ecf20Sopenharmony_ci		skb_put_u8(skb_out, 0);	/* force short packet */
14248c2ecf20Sopenharmony_ci	}
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_ci	/* set final frame length */
14278c2ecf20Sopenharmony_ci	if (ctx->is_ndp16) {
14288c2ecf20Sopenharmony_ci		nth.nth16 = (struct usb_cdc_ncm_nth16 *)skb_out->data;
14298c2ecf20Sopenharmony_ci		nth.nth16->wBlockLength = cpu_to_le16(skb_out->len);
14308c2ecf20Sopenharmony_ci	} else {
14318c2ecf20Sopenharmony_ci		nth.nth32 = (struct usb_cdc_ncm_nth32 *)skb_out->data;
14328c2ecf20Sopenharmony_ci		nth.nth32->dwBlockLength = cpu_to_le32(skb_out->len);
14338c2ecf20Sopenharmony_ci	}
14348c2ecf20Sopenharmony_ci
14358c2ecf20Sopenharmony_ci	/* return skb */
14368c2ecf20Sopenharmony_ci	ctx->tx_curr_skb = NULL;
14378c2ecf20Sopenharmony_ci
14388c2ecf20Sopenharmony_ci	/* keep private stats: framing overhead and number of NTBs */
14398c2ecf20Sopenharmony_ci	ctx->tx_overhead += skb_out->len - ctx->tx_curr_frame_payload;
14408c2ecf20Sopenharmony_ci	ctx->tx_ntbs++;
14418c2ecf20Sopenharmony_ci
14428c2ecf20Sopenharmony_ci	/* usbnet will count all the framing overhead by default.
14438c2ecf20Sopenharmony_ci	 * Adjust the stats so that the tx_bytes counter show real
14448c2ecf20Sopenharmony_ci	 * payload data instead.
14458c2ecf20Sopenharmony_ci	 */
14468c2ecf20Sopenharmony_ci	usbnet_set_skb_tx_stats(skb_out, n,
14478c2ecf20Sopenharmony_ci				(long)ctx->tx_curr_frame_payload - skb_out->len);
14488c2ecf20Sopenharmony_ci
14498c2ecf20Sopenharmony_ci	return skb_out;
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_cialloc_failed:
14528c2ecf20Sopenharmony_ci	if (skb) {
14538c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb);
14548c2ecf20Sopenharmony_ci		dev->net->stats.tx_dropped++;
14558c2ecf20Sopenharmony_ci	}
14568c2ecf20Sopenharmony_ciexit_no_skb:
14578c2ecf20Sopenharmony_ci	/* Start timer, if there is a remaining non-empty skb */
14588c2ecf20Sopenharmony_ci	if (ctx->tx_curr_skb != NULL && n > 0)
14598c2ecf20Sopenharmony_ci		cdc_ncm_tx_timeout_start(ctx);
14608c2ecf20Sopenharmony_ci	return NULL;
14618c2ecf20Sopenharmony_ci}
14628c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cdc_ncm_fill_tx_frame);
14638c2ecf20Sopenharmony_ci
14648c2ecf20Sopenharmony_cistatic void cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx)
14658c2ecf20Sopenharmony_ci{
14668c2ecf20Sopenharmony_ci	/* start timer, if not already started */
14678c2ecf20Sopenharmony_ci	if (!(hrtimer_active(&ctx->tx_timer) || atomic_read(&ctx->stop)))
14688c2ecf20Sopenharmony_ci		hrtimer_start(&ctx->tx_timer,
14698c2ecf20Sopenharmony_ci				ctx->timer_interval,
14708c2ecf20Sopenharmony_ci				HRTIMER_MODE_REL);
14718c2ecf20Sopenharmony_ci}
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_cistatic enum hrtimer_restart cdc_ncm_tx_timer_cb(struct hrtimer *timer)
14748c2ecf20Sopenharmony_ci{
14758c2ecf20Sopenharmony_ci	struct cdc_ncm_ctx *ctx =
14768c2ecf20Sopenharmony_ci			container_of(timer, struct cdc_ncm_ctx, tx_timer);
14778c2ecf20Sopenharmony_ci
14788c2ecf20Sopenharmony_ci	if (!atomic_read(&ctx->stop))
14798c2ecf20Sopenharmony_ci		tasklet_schedule(&ctx->bh);
14808c2ecf20Sopenharmony_ci	return HRTIMER_NORESTART;
14818c2ecf20Sopenharmony_ci}
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_cistatic void cdc_ncm_txpath_bh(unsigned long param)
14848c2ecf20Sopenharmony_ci{
14858c2ecf20Sopenharmony_ci	struct usbnet *dev = (struct usbnet *)param;
14868c2ecf20Sopenharmony_ci	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci	spin_lock_bh(&ctx->mtx);
14898c2ecf20Sopenharmony_ci	if (ctx->tx_timer_pending != 0) {
14908c2ecf20Sopenharmony_ci		ctx->tx_timer_pending--;
14918c2ecf20Sopenharmony_ci		cdc_ncm_tx_timeout_start(ctx);
14928c2ecf20Sopenharmony_ci		spin_unlock_bh(&ctx->mtx);
14938c2ecf20Sopenharmony_ci	} else if (dev->net != NULL) {
14948c2ecf20Sopenharmony_ci		ctx->tx_reason_timeout++;	/* count reason for transmitting */
14958c2ecf20Sopenharmony_ci		spin_unlock_bh(&ctx->mtx);
14968c2ecf20Sopenharmony_ci		netif_tx_lock_bh(dev->net);
14978c2ecf20Sopenharmony_ci		usbnet_start_xmit(NULL, dev->net);
14988c2ecf20Sopenharmony_ci		netif_tx_unlock_bh(dev->net);
14998c2ecf20Sopenharmony_ci	} else {
15008c2ecf20Sopenharmony_ci		spin_unlock_bh(&ctx->mtx);
15018c2ecf20Sopenharmony_ci	}
15028c2ecf20Sopenharmony_ci}
15038c2ecf20Sopenharmony_ci
15048c2ecf20Sopenharmony_cistruct sk_buff *
15058c2ecf20Sopenharmony_cicdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
15068c2ecf20Sopenharmony_ci{
15078c2ecf20Sopenharmony_ci	struct sk_buff *skb_out;
15088c2ecf20Sopenharmony_ci	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
15098c2ecf20Sopenharmony_ci
15108c2ecf20Sopenharmony_ci	/*
15118c2ecf20Sopenharmony_ci	 * The Ethernet API we are using does not support transmitting
15128c2ecf20Sopenharmony_ci	 * multiple Ethernet frames in a single call. This driver will
15138c2ecf20Sopenharmony_ci	 * accumulate multiple Ethernet frames and send out a larger
15148c2ecf20Sopenharmony_ci	 * USB frame when the USB buffer is full or when a single jiffies
15158c2ecf20Sopenharmony_ci	 * timeout happens.
15168c2ecf20Sopenharmony_ci	 */
15178c2ecf20Sopenharmony_ci	if (ctx == NULL)
15188c2ecf20Sopenharmony_ci		goto error;
15198c2ecf20Sopenharmony_ci
15208c2ecf20Sopenharmony_ci	spin_lock_bh(&ctx->mtx);
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_ci	if (ctx->is_ndp16)
15238c2ecf20Sopenharmony_ci		skb_out = cdc_ncm_fill_tx_frame(dev, skb, cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN));
15248c2ecf20Sopenharmony_ci	else
15258c2ecf20Sopenharmony_ci		skb_out = cdc_ncm_fill_tx_frame(dev, skb, cpu_to_le32(USB_CDC_NCM_NDP32_NOCRC_SIGN));
15268c2ecf20Sopenharmony_ci
15278c2ecf20Sopenharmony_ci	spin_unlock_bh(&ctx->mtx);
15288c2ecf20Sopenharmony_ci	return skb_out;
15298c2ecf20Sopenharmony_ci
15308c2ecf20Sopenharmony_cierror:
15318c2ecf20Sopenharmony_ci	if (skb != NULL)
15328c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb);
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_ci	return NULL;
15358c2ecf20Sopenharmony_ci}
15368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cdc_ncm_tx_fixup);
15378c2ecf20Sopenharmony_ci
15388c2ecf20Sopenharmony_ci/* verify NTB header and return offset of first NDP, or negative error */
15398c2ecf20Sopenharmony_ciint cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in)
15408c2ecf20Sopenharmony_ci{
15418c2ecf20Sopenharmony_ci	struct usbnet *dev = netdev_priv(skb_in->dev);
15428c2ecf20Sopenharmony_ci	struct usb_cdc_ncm_nth16 *nth16;
15438c2ecf20Sopenharmony_ci	int len;
15448c2ecf20Sopenharmony_ci	int ret = -EINVAL;
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci	if (ctx == NULL)
15478c2ecf20Sopenharmony_ci		goto error;
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_ci	if (skb_in->len < (sizeof(struct usb_cdc_ncm_nth16) +
15508c2ecf20Sopenharmony_ci					sizeof(struct usb_cdc_ncm_ndp16))) {
15518c2ecf20Sopenharmony_ci		netif_dbg(dev, rx_err, dev->net, "frame too short\n");
15528c2ecf20Sopenharmony_ci		goto error;
15538c2ecf20Sopenharmony_ci	}
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_ci	nth16 = (struct usb_cdc_ncm_nth16 *)skb_in->data;
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci	if (nth16->dwSignature != cpu_to_le32(USB_CDC_NCM_NTH16_SIGN)) {
15588c2ecf20Sopenharmony_ci		netif_dbg(dev, rx_err, dev->net,
15598c2ecf20Sopenharmony_ci			  "invalid NTH16 signature <%#010x>\n",
15608c2ecf20Sopenharmony_ci			  le32_to_cpu(nth16->dwSignature));
15618c2ecf20Sopenharmony_ci		goto error;
15628c2ecf20Sopenharmony_ci	}
15638c2ecf20Sopenharmony_ci
15648c2ecf20Sopenharmony_ci	len = le16_to_cpu(nth16->wBlockLength);
15658c2ecf20Sopenharmony_ci	if (len > ctx->rx_max) {
15668c2ecf20Sopenharmony_ci		netif_dbg(dev, rx_err, dev->net,
15678c2ecf20Sopenharmony_ci			  "unsupported NTB block length %u/%u\n", len,
15688c2ecf20Sopenharmony_ci			  ctx->rx_max);
15698c2ecf20Sopenharmony_ci		goto error;
15708c2ecf20Sopenharmony_ci	}
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_ci	if ((ctx->rx_seq + 1) != le16_to_cpu(nth16->wSequence) &&
15738c2ecf20Sopenharmony_ci	    (ctx->rx_seq || le16_to_cpu(nth16->wSequence)) &&
15748c2ecf20Sopenharmony_ci	    !((ctx->rx_seq == 0xffff) && !le16_to_cpu(nth16->wSequence))) {
15758c2ecf20Sopenharmony_ci		netif_dbg(dev, rx_err, dev->net,
15768c2ecf20Sopenharmony_ci			  "sequence number glitch prev=%d curr=%d\n",
15778c2ecf20Sopenharmony_ci			  ctx->rx_seq, le16_to_cpu(nth16->wSequence));
15788c2ecf20Sopenharmony_ci	}
15798c2ecf20Sopenharmony_ci	ctx->rx_seq = le16_to_cpu(nth16->wSequence);
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_ci	ret = le16_to_cpu(nth16->wNdpIndex);
15828c2ecf20Sopenharmony_cierror:
15838c2ecf20Sopenharmony_ci	return ret;
15848c2ecf20Sopenharmony_ci}
15858c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_nth16);
15868c2ecf20Sopenharmony_ci
15878c2ecf20Sopenharmony_ciint cdc_ncm_rx_verify_nth32(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in)
15888c2ecf20Sopenharmony_ci{
15898c2ecf20Sopenharmony_ci	struct usbnet *dev = netdev_priv(skb_in->dev);
15908c2ecf20Sopenharmony_ci	struct usb_cdc_ncm_nth32 *nth32;
15918c2ecf20Sopenharmony_ci	int len;
15928c2ecf20Sopenharmony_ci	int ret = -EINVAL;
15938c2ecf20Sopenharmony_ci
15948c2ecf20Sopenharmony_ci	if (ctx == NULL)
15958c2ecf20Sopenharmony_ci		goto error;
15968c2ecf20Sopenharmony_ci
15978c2ecf20Sopenharmony_ci	if (skb_in->len < (sizeof(struct usb_cdc_ncm_nth32) +
15988c2ecf20Sopenharmony_ci					sizeof(struct usb_cdc_ncm_ndp32))) {
15998c2ecf20Sopenharmony_ci		netif_dbg(dev, rx_err, dev->net, "frame too short\n");
16008c2ecf20Sopenharmony_ci		goto error;
16018c2ecf20Sopenharmony_ci	}
16028c2ecf20Sopenharmony_ci
16038c2ecf20Sopenharmony_ci	nth32 = (struct usb_cdc_ncm_nth32 *)skb_in->data;
16048c2ecf20Sopenharmony_ci
16058c2ecf20Sopenharmony_ci	if (nth32->dwSignature != cpu_to_le32(USB_CDC_NCM_NTH32_SIGN)) {
16068c2ecf20Sopenharmony_ci		netif_dbg(dev, rx_err, dev->net,
16078c2ecf20Sopenharmony_ci			  "invalid NTH32 signature <%#010x>\n",
16088c2ecf20Sopenharmony_ci			  le32_to_cpu(nth32->dwSignature));
16098c2ecf20Sopenharmony_ci		goto error;
16108c2ecf20Sopenharmony_ci	}
16118c2ecf20Sopenharmony_ci
16128c2ecf20Sopenharmony_ci	len = le32_to_cpu(nth32->dwBlockLength);
16138c2ecf20Sopenharmony_ci	if (len > ctx->rx_max) {
16148c2ecf20Sopenharmony_ci		netif_dbg(dev, rx_err, dev->net,
16158c2ecf20Sopenharmony_ci			  "unsupported NTB block length %u/%u\n", len,
16168c2ecf20Sopenharmony_ci			  ctx->rx_max);
16178c2ecf20Sopenharmony_ci		goto error;
16188c2ecf20Sopenharmony_ci	}
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_ci	if ((ctx->rx_seq + 1) != le16_to_cpu(nth32->wSequence) &&
16218c2ecf20Sopenharmony_ci	    (ctx->rx_seq || le16_to_cpu(nth32->wSequence)) &&
16228c2ecf20Sopenharmony_ci	    !((ctx->rx_seq == 0xffff) && !le16_to_cpu(nth32->wSequence))) {
16238c2ecf20Sopenharmony_ci		netif_dbg(dev, rx_err, dev->net,
16248c2ecf20Sopenharmony_ci			  "sequence number glitch prev=%d curr=%d\n",
16258c2ecf20Sopenharmony_ci			  ctx->rx_seq, le16_to_cpu(nth32->wSequence));
16268c2ecf20Sopenharmony_ci	}
16278c2ecf20Sopenharmony_ci	ctx->rx_seq = le16_to_cpu(nth32->wSequence);
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_ci	ret = le32_to_cpu(nth32->dwNdpIndex);
16308c2ecf20Sopenharmony_cierror:
16318c2ecf20Sopenharmony_ci	return ret;
16328c2ecf20Sopenharmony_ci}
16338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_nth32);
16348c2ecf20Sopenharmony_ci
16358c2ecf20Sopenharmony_ci/* verify NDP header and return number of datagrams, or negative error */
16368c2ecf20Sopenharmony_ciint cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset)
16378c2ecf20Sopenharmony_ci{
16388c2ecf20Sopenharmony_ci	struct usbnet *dev = netdev_priv(skb_in->dev);
16398c2ecf20Sopenharmony_ci	struct usb_cdc_ncm_ndp16 *ndp16;
16408c2ecf20Sopenharmony_ci	int ret = -EINVAL;
16418c2ecf20Sopenharmony_ci
16428c2ecf20Sopenharmony_ci	if ((ndpoffset + sizeof(struct usb_cdc_ncm_ndp16)) > skb_in->len) {
16438c2ecf20Sopenharmony_ci		netif_dbg(dev, rx_err, dev->net, "invalid NDP offset  <%u>\n",
16448c2ecf20Sopenharmony_ci			  ndpoffset);
16458c2ecf20Sopenharmony_ci		goto error;
16468c2ecf20Sopenharmony_ci	}
16478c2ecf20Sopenharmony_ci	ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb_in->data + ndpoffset);
16488c2ecf20Sopenharmony_ci
16498c2ecf20Sopenharmony_ci	if (le16_to_cpu(ndp16->wLength) < USB_CDC_NCM_NDP16_LENGTH_MIN) {
16508c2ecf20Sopenharmony_ci		netif_dbg(dev, rx_err, dev->net, "invalid DPT16 length <%u>\n",
16518c2ecf20Sopenharmony_ci			  le16_to_cpu(ndp16->wLength));
16528c2ecf20Sopenharmony_ci		goto error;
16538c2ecf20Sopenharmony_ci	}
16548c2ecf20Sopenharmony_ci
16558c2ecf20Sopenharmony_ci	ret = ((le16_to_cpu(ndp16->wLength) -
16568c2ecf20Sopenharmony_ci					sizeof(struct usb_cdc_ncm_ndp16)) /
16578c2ecf20Sopenharmony_ci					sizeof(struct usb_cdc_ncm_dpe16));
16588c2ecf20Sopenharmony_ci	ret--; /* we process NDP entries except for the last one */
16598c2ecf20Sopenharmony_ci
16608c2ecf20Sopenharmony_ci	if ((sizeof(struct usb_cdc_ncm_ndp16) +
16618c2ecf20Sopenharmony_ci	     ret * (sizeof(struct usb_cdc_ncm_dpe16))) > skb_in->len) {
16628c2ecf20Sopenharmony_ci		netif_dbg(dev, rx_err, dev->net, "Invalid nframes = %d\n", ret);
16638c2ecf20Sopenharmony_ci		ret = -EINVAL;
16648c2ecf20Sopenharmony_ci	}
16658c2ecf20Sopenharmony_ci
16668c2ecf20Sopenharmony_cierror:
16678c2ecf20Sopenharmony_ci	return ret;
16688c2ecf20Sopenharmony_ci}
16698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_ndp16);
16708c2ecf20Sopenharmony_ci
16718c2ecf20Sopenharmony_ci/* verify NDP header and return number of datagrams, or negative error */
16728c2ecf20Sopenharmony_ciint cdc_ncm_rx_verify_ndp32(struct sk_buff *skb_in, int ndpoffset)
16738c2ecf20Sopenharmony_ci{
16748c2ecf20Sopenharmony_ci	struct usbnet *dev = netdev_priv(skb_in->dev);
16758c2ecf20Sopenharmony_ci	struct usb_cdc_ncm_ndp32 *ndp32;
16768c2ecf20Sopenharmony_ci	int ret = -EINVAL;
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_ci	if ((ndpoffset + sizeof(struct usb_cdc_ncm_ndp32)) > skb_in->len) {
16798c2ecf20Sopenharmony_ci		netif_dbg(dev, rx_err, dev->net, "invalid NDP offset  <%u>\n",
16808c2ecf20Sopenharmony_ci			  ndpoffset);
16818c2ecf20Sopenharmony_ci		goto error;
16828c2ecf20Sopenharmony_ci	}
16838c2ecf20Sopenharmony_ci	ndp32 = (struct usb_cdc_ncm_ndp32 *)(skb_in->data + ndpoffset);
16848c2ecf20Sopenharmony_ci
16858c2ecf20Sopenharmony_ci	if (le16_to_cpu(ndp32->wLength) < USB_CDC_NCM_NDP32_LENGTH_MIN) {
16868c2ecf20Sopenharmony_ci		netif_dbg(dev, rx_err, dev->net, "invalid DPT32 length <%u>\n",
16878c2ecf20Sopenharmony_ci			  le16_to_cpu(ndp32->wLength));
16888c2ecf20Sopenharmony_ci		goto error;
16898c2ecf20Sopenharmony_ci	}
16908c2ecf20Sopenharmony_ci
16918c2ecf20Sopenharmony_ci	ret = ((le16_to_cpu(ndp32->wLength) -
16928c2ecf20Sopenharmony_ci					sizeof(struct usb_cdc_ncm_ndp32)) /
16938c2ecf20Sopenharmony_ci					sizeof(struct usb_cdc_ncm_dpe32));
16948c2ecf20Sopenharmony_ci	ret--; /* we process NDP entries except for the last one */
16958c2ecf20Sopenharmony_ci
16968c2ecf20Sopenharmony_ci	if ((sizeof(struct usb_cdc_ncm_ndp32) +
16978c2ecf20Sopenharmony_ci	     ret * (sizeof(struct usb_cdc_ncm_dpe32))) > skb_in->len) {
16988c2ecf20Sopenharmony_ci		netif_dbg(dev, rx_err, dev->net, "Invalid nframes = %d\n", ret);
16998c2ecf20Sopenharmony_ci		ret = -EINVAL;
17008c2ecf20Sopenharmony_ci	}
17018c2ecf20Sopenharmony_ci
17028c2ecf20Sopenharmony_cierror:
17038c2ecf20Sopenharmony_ci	return ret;
17048c2ecf20Sopenharmony_ci}
17058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_ndp32);
17068c2ecf20Sopenharmony_ci
17078c2ecf20Sopenharmony_ciint cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
17088c2ecf20Sopenharmony_ci{
17098c2ecf20Sopenharmony_ci	struct sk_buff *skb;
17108c2ecf20Sopenharmony_ci	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
17118c2ecf20Sopenharmony_ci	unsigned int len;
17128c2ecf20Sopenharmony_ci	int nframes;
17138c2ecf20Sopenharmony_ci	int x;
17148c2ecf20Sopenharmony_ci	unsigned int offset;
17158c2ecf20Sopenharmony_ci	union {
17168c2ecf20Sopenharmony_ci		struct usb_cdc_ncm_ndp16 *ndp16;
17178c2ecf20Sopenharmony_ci		struct usb_cdc_ncm_ndp32 *ndp32;
17188c2ecf20Sopenharmony_ci	} ndp;
17198c2ecf20Sopenharmony_ci	union {
17208c2ecf20Sopenharmony_ci		struct usb_cdc_ncm_dpe16 *dpe16;
17218c2ecf20Sopenharmony_ci		struct usb_cdc_ncm_dpe32 *dpe32;
17228c2ecf20Sopenharmony_ci	} dpe;
17238c2ecf20Sopenharmony_ci
17248c2ecf20Sopenharmony_ci	int ndpoffset;
17258c2ecf20Sopenharmony_ci	int loopcount = 50; /* arbitrary max preventing infinite loop */
17268c2ecf20Sopenharmony_ci	u32 payload = 0;
17278c2ecf20Sopenharmony_ci
17288c2ecf20Sopenharmony_ci	if (ctx->is_ndp16)
17298c2ecf20Sopenharmony_ci		ndpoffset = cdc_ncm_rx_verify_nth16(ctx, skb_in);
17308c2ecf20Sopenharmony_ci	else
17318c2ecf20Sopenharmony_ci		ndpoffset = cdc_ncm_rx_verify_nth32(ctx, skb_in);
17328c2ecf20Sopenharmony_ci
17338c2ecf20Sopenharmony_ci	if (ndpoffset < 0)
17348c2ecf20Sopenharmony_ci		goto error;
17358c2ecf20Sopenharmony_ci
17368c2ecf20Sopenharmony_cinext_ndp:
17378c2ecf20Sopenharmony_ci	if (ctx->is_ndp16) {
17388c2ecf20Sopenharmony_ci		nframes = cdc_ncm_rx_verify_ndp16(skb_in, ndpoffset);
17398c2ecf20Sopenharmony_ci		if (nframes < 0)
17408c2ecf20Sopenharmony_ci			goto error;
17418c2ecf20Sopenharmony_ci
17428c2ecf20Sopenharmony_ci		ndp.ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb_in->data + ndpoffset);
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_ci		if (ndp.ndp16->dwSignature != cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN)) {
17458c2ecf20Sopenharmony_ci			netif_dbg(dev, rx_err, dev->net,
17468c2ecf20Sopenharmony_ci				  "invalid DPT16 signature <%#010x>\n",
17478c2ecf20Sopenharmony_ci				  le32_to_cpu(ndp.ndp16->dwSignature));
17488c2ecf20Sopenharmony_ci			goto err_ndp;
17498c2ecf20Sopenharmony_ci		}
17508c2ecf20Sopenharmony_ci		dpe.dpe16 = ndp.ndp16->dpe16;
17518c2ecf20Sopenharmony_ci	} else {
17528c2ecf20Sopenharmony_ci		nframes = cdc_ncm_rx_verify_ndp32(skb_in, ndpoffset);
17538c2ecf20Sopenharmony_ci		if (nframes < 0)
17548c2ecf20Sopenharmony_ci			goto error;
17558c2ecf20Sopenharmony_ci
17568c2ecf20Sopenharmony_ci		ndp.ndp32 = (struct usb_cdc_ncm_ndp32 *)(skb_in->data + ndpoffset);
17578c2ecf20Sopenharmony_ci
17588c2ecf20Sopenharmony_ci		if (ndp.ndp32->dwSignature != cpu_to_le32(USB_CDC_NCM_NDP32_NOCRC_SIGN)) {
17598c2ecf20Sopenharmony_ci			netif_dbg(dev, rx_err, dev->net,
17608c2ecf20Sopenharmony_ci				  "invalid DPT32 signature <%#010x>\n",
17618c2ecf20Sopenharmony_ci				  le32_to_cpu(ndp.ndp32->dwSignature));
17628c2ecf20Sopenharmony_ci			goto err_ndp;
17638c2ecf20Sopenharmony_ci		}
17648c2ecf20Sopenharmony_ci		dpe.dpe32 = ndp.ndp32->dpe32;
17658c2ecf20Sopenharmony_ci	}
17668c2ecf20Sopenharmony_ci
17678c2ecf20Sopenharmony_ci	for (x = 0; x < nframes; x++) {
17688c2ecf20Sopenharmony_ci		if (ctx->is_ndp16) {
17698c2ecf20Sopenharmony_ci			offset = le16_to_cpu(dpe.dpe16->wDatagramIndex);
17708c2ecf20Sopenharmony_ci			len = le16_to_cpu(dpe.dpe16->wDatagramLength);
17718c2ecf20Sopenharmony_ci		} else {
17728c2ecf20Sopenharmony_ci			offset = le32_to_cpu(dpe.dpe32->dwDatagramIndex);
17738c2ecf20Sopenharmony_ci			len = le32_to_cpu(dpe.dpe32->dwDatagramLength);
17748c2ecf20Sopenharmony_ci		}
17758c2ecf20Sopenharmony_ci
17768c2ecf20Sopenharmony_ci		/*
17778c2ecf20Sopenharmony_ci		 * CDC NCM ch. 3.7
17788c2ecf20Sopenharmony_ci		 * All entries after first NULL entry are to be ignored
17798c2ecf20Sopenharmony_ci		 */
17808c2ecf20Sopenharmony_ci		if ((offset == 0) || (len == 0)) {
17818c2ecf20Sopenharmony_ci			if (!x)
17828c2ecf20Sopenharmony_ci				goto err_ndp; /* empty NTB */
17838c2ecf20Sopenharmony_ci			break;
17848c2ecf20Sopenharmony_ci		}
17858c2ecf20Sopenharmony_ci
17868c2ecf20Sopenharmony_ci		/* sanity checking - watch out for integer wrap*/
17878c2ecf20Sopenharmony_ci		if ((offset > skb_in->len) || (len > skb_in->len - offset) ||
17888c2ecf20Sopenharmony_ci				(len > ctx->rx_max) || (len < ETH_HLEN)) {
17898c2ecf20Sopenharmony_ci			netif_dbg(dev, rx_err, dev->net,
17908c2ecf20Sopenharmony_ci				  "invalid frame detected (ignored) offset[%u]=%u, length=%u, skb=%p\n",
17918c2ecf20Sopenharmony_ci				  x, offset, len, skb_in);
17928c2ecf20Sopenharmony_ci			if (!x)
17938c2ecf20Sopenharmony_ci				goto err_ndp;
17948c2ecf20Sopenharmony_ci			break;
17958c2ecf20Sopenharmony_ci
17968c2ecf20Sopenharmony_ci		} else {
17978c2ecf20Sopenharmony_ci			/* create a fresh copy to reduce truesize */
17988c2ecf20Sopenharmony_ci			skb = netdev_alloc_skb_ip_align(dev->net,  len);
17998c2ecf20Sopenharmony_ci			if (!skb)
18008c2ecf20Sopenharmony_ci				goto error;
18018c2ecf20Sopenharmony_ci			skb_put_data(skb, skb_in->data + offset, len);
18028c2ecf20Sopenharmony_ci			usbnet_skb_return(dev, skb);
18038c2ecf20Sopenharmony_ci			payload += len;	/* count payload bytes in this NTB */
18048c2ecf20Sopenharmony_ci		}
18058c2ecf20Sopenharmony_ci
18068c2ecf20Sopenharmony_ci		if (ctx->is_ndp16)
18078c2ecf20Sopenharmony_ci			dpe.dpe16++;
18088c2ecf20Sopenharmony_ci		else
18098c2ecf20Sopenharmony_ci			dpe.dpe32++;
18108c2ecf20Sopenharmony_ci	}
18118c2ecf20Sopenharmony_cierr_ndp:
18128c2ecf20Sopenharmony_ci	/* are there more NDPs to process? */
18138c2ecf20Sopenharmony_ci	if (ctx->is_ndp16)
18148c2ecf20Sopenharmony_ci		ndpoffset = le16_to_cpu(ndp.ndp16->wNextNdpIndex);
18158c2ecf20Sopenharmony_ci	else
18168c2ecf20Sopenharmony_ci		ndpoffset = le32_to_cpu(ndp.ndp32->dwNextNdpIndex);
18178c2ecf20Sopenharmony_ci
18188c2ecf20Sopenharmony_ci	if (ndpoffset && loopcount--)
18198c2ecf20Sopenharmony_ci		goto next_ndp;
18208c2ecf20Sopenharmony_ci
18218c2ecf20Sopenharmony_ci	/* update stats */
18228c2ecf20Sopenharmony_ci	ctx->rx_overhead += skb_in->len - payload;
18238c2ecf20Sopenharmony_ci	ctx->rx_ntbs++;
18248c2ecf20Sopenharmony_ci
18258c2ecf20Sopenharmony_ci	return 1;
18268c2ecf20Sopenharmony_cierror:
18278c2ecf20Sopenharmony_ci	return 0;
18288c2ecf20Sopenharmony_ci}
18298c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cdc_ncm_rx_fixup);
18308c2ecf20Sopenharmony_ci
18318c2ecf20Sopenharmony_cistatic void
18328c2ecf20Sopenharmony_cicdc_ncm_speed_change(struct usbnet *dev,
18338c2ecf20Sopenharmony_ci		     struct usb_cdc_speed_change *data)
18348c2ecf20Sopenharmony_ci{
18358c2ecf20Sopenharmony_ci	uint32_t rx_speed = le32_to_cpu(data->DLBitRRate);
18368c2ecf20Sopenharmony_ci	uint32_t tx_speed = le32_to_cpu(data->ULBitRate);
18378c2ecf20Sopenharmony_ci
18388c2ecf20Sopenharmony_ci	/* if the speed hasn't changed, don't report it.
18398c2ecf20Sopenharmony_ci	 * RTL8156 shipped before 2021 sends notification about every 32ms.
18408c2ecf20Sopenharmony_ci	 */
18418c2ecf20Sopenharmony_ci	if (dev->rx_speed == rx_speed && dev->tx_speed == tx_speed)
18428c2ecf20Sopenharmony_ci		return;
18438c2ecf20Sopenharmony_ci
18448c2ecf20Sopenharmony_ci	dev->rx_speed = rx_speed;
18458c2ecf20Sopenharmony_ci	dev->tx_speed = tx_speed;
18468c2ecf20Sopenharmony_ci
18478c2ecf20Sopenharmony_ci	/*
18488c2ecf20Sopenharmony_ci	 * Currently the USB-NET API does not support reporting the actual
18498c2ecf20Sopenharmony_ci	 * device speed. Do print it instead.
18508c2ecf20Sopenharmony_ci	 */
18518c2ecf20Sopenharmony_ci	if ((tx_speed > 1000000) && (rx_speed > 1000000)) {
18528c2ecf20Sopenharmony_ci		netif_info(dev, link, dev->net,
18538c2ecf20Sopenharmony_ci			   "%u mbit/s downlink %u mbit/s uplink\n",
18548c2ecf20Sopenharmony_ci			   (unsigned int)(rx_speed / 1000000U),
18558c2ecf20Sopenharmony_ci			   (unsigned int)(tx_speed / 1000000U));
18568c2ecf20Sopenharmony_ci	} else {
18578c2ecf20Sopenharmony_ci		netif_info(dev, link, dev->net,
18588c2ecf20Sopenharmony_ci			   "%u kbit/s downlink %u kbit/s uplink\n",
18598c2ecf20Sopenharmony_ci			   (unsigned int)(rx_speed / 1000U),
18608c2ecf20Sopenharmony_ci			   (unsigned int)(tx_speed / 1000U));
18618c2ecf20Sopenharmony_ci	}
18628c2ecf20Sopenharmony_ci}
18638c2ecf20Sopenharmony_ci
18648c2ecf20Sopenharmony_cistatic void cdc_ncm_status(struct usbnet *dev, struct urb *urb)
18658c2ecf20Sopenharmony_ci{
18668c2ecf20Sopenharmony_ci	struct usb_cdc_notification *event;
18678c2ecf20Sopenharmony_ci
18688c2ecf20Sopenharmony_ci	if (urb->actual_length < sizeof(*event))
18698c2ecf20Sopenharmony_ci		return;
18708c2ecf20Sopenharmony_ci
18718c2ecf20Sopenharmony_ci	/* test for split data in 8-byte chunks */
18728c2ecf20Sopenharmony_ci	if (test_and_clear_bit(EVENT_STS_SPLIT, &dev->flags)) {
18738c2ecf20Sopenharmony_ci		cdc_ncm_speed_change(dev,
18748c2ecf20Sopenharmony_ci		      (struct usb_cdc_speed_change *)urb->transfer_buffer);
18758c2ecf20Sopenharmony_ci		return;
18768c2ecf20Sopenharmony_ci	}
18778c2ecf20Sopenharmony_ci
18788c2ecf20Sopenharmony_ci	event = urb->transfer_buffer;
18798c2ecf20Sopenharmony_ci
18808c2ecf20Sopenharmony_ci	switch (event->bNotificationType) {
18818c2ecf20Sopenharmony_ci	case USB_CDC_NOTIFY_NETWORK_CONNECTION:
18828c2ecf20Sopenharmony_ci		/*
18838c2ecf20Sopenharmony_ci		 * According to the CDC NCM specification ch.7.1
18848c2ecf20Sopenharmony_ci		 * USB_CDC_NOTIFY_NETWORK_CONNECTION notification shall be
18858c2ecf20Sopenharmony_ci		 * sent by device after USB_CDC_NOTIFY_SPEED_CHANGE.
18868c2ecf20Sopenharmony_ci		 */
18878c2ecf20Sopenharmony_ci		if (netif_carrier_ok(dev->net) != !!event->wValue)
18888c2ecf20Sopenharmony_ci			usbnet_link_change(dev, !!event->wValue, 0);
18898c2ecf20Sopenharmony_ci		break;
18908c2ecf20Sopenharmony_ci
18918c2ecf20Sopenharmony_ci	case USB_CDC_NOTIFY_SPEED_CHANGE:
18928c2ecf20Sopenharmony_ci		if (urb->actual_length < (sizeof(*event) +
18938c2ecf20Sopenharmony_ci					sizeof(struct usb_cdc_speed_change)))
18948c2ecf20Sopenharmony_ci			set_bit(EVENT_STS_SPLIT, &dev->flags);
18958c2ecf20Sopenharmony_ci		else
18968c2ecf20Sopenharmony_ci			cdc_ncm_speed_change(dev,
18978c2ecf20Sopenharmony_ci					     (struct usb_cdc_speed_change *)&event[1]);
18988c2ecf20Sopenharmony_ci		break;
18998c2ecf20Sopenharmony_ci
19008c2ecf20Sopenharmony_ci	default:
19018c2ecf20Sopenharmony_ci		dev_dbg(&dev->udev->dev,
19028c2ecf20Sopenharmony_ci			"NCM: unexpected notification 0x%02x!\n",
19038c2ecf20Sopenharmony_ci			event->bNotificationType);
19048c2ecf20Sopenharmony_ci		break;
19058c2ecf20Sopenharmony_ci	}
19068c2ecf20Sopenharmony_ci}
19078c2ecf20Sopenharmony_ci
19088c2ecf20Sopenharmony_cistatic const struct driver_info cdc_ncm_info = {
19098c2ecf20Sopenharmony_ci	.description = "CDC NCM",
19108c2ecf20Sopenharmony_ci	.flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT | FLAG_MULTI_PACKET
19118c2ecf20Sopenharmony_ci			| FLAG_LINK_INTR | FLAG_ETHER,
19128c2ecf20Sopenharmony_ci	.bind = cdc_ncm_bind,
19138c2ecf20Sopenharmony_ci	.unbind = cdc_ncm_unbind,
19148c2ecf20Sopenharmony_ci	.manage_power = usbnet_manage_power,
19158c2ecf20Sopenharmony_ci	.status = cdc_ncm_status,
19168c2ecf20Sopenharmony_ci	.rx_fixup = cdc_ncm_rx_fixup,
19178c2ecf20Sopenharmony_ci	.tx_fixup = cdc_ncm_tx_fixup,
19188c2ecf20Sopenharmony_ci	.set_rx_mode = usbnet_cdc_update_filter,
19198c2ecf20Sopenharmony_ci};
19208c2ecf20Sopenharmony_ci
19218c2ecf20Sopenharmony_ci/* Same as cdc_ncm_info, but with FLAG_WWAN */
19228c2ecf20Sopenharmony_cistatic const struct driver_info wwan_info = {
19238c2ecf20Sopenharmony_ci	.description = "Mobile Broadband Network Device",
19248c2ecf20Sopenharmony_ci	.flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT | FLAG_MULTI_PACKET
19258c2ecf20Sopenharmony_ci			| FLAG_LINK_INTR | FLAG_WWAN,
19268c2ecf20Sopenharmony_ci	.bind = cdc_ncm_bind,
19278c2ecf20Sopenharmony_ci	.unbind = cdc_ncm_unbind,
19288c2ecf20Sopenharmony_ci	.manage_power = usbnet_manage_power,
19298c2ecf20Sopenharmony_ci	.status = cdc_ncm_status,
19308c2ecf20Sopenharmony_ci	.rx_fixup = cdc_ncm_rx_fixup,
19318c2ecf20Sopenharmony_ci	.tx_fixup = cdc_ncm_tx_fixup,
19328c2ecf20Sopenharmony_ci	.set_rx_mode = usbnet_cdc_update_filter,
19338c2ecf20Sopenharmony_ci};
19348c2ecf20Sopenharmony_ci
19358c2ecf20Sopenharmony_ci/* Same as wwan_info, but with FLAG_NOARP  */
19368c2ecf20Sopenharmony_cistatic const struct driver_info wwan_noarp_info = {
19378c2ecf20Sopenharmony_ci	.description = "Mobile Broadband Network Device (NO ARP)",
19388c2ecf20Sopenharmony_ci	.flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT | FLAG_MULTI_PACKET
19398c2ecf20Sopenharmony_ci			| FLAG_LINK_INTR | FLAG_WWAN | FLAG_NOARP,
19408c2ecf20Sopenharmony_ci	.bind = cdc_ncm_bind,
19418c2ecf20Sopenharmony_ci	.unbind = cdc_ncm_unbind,
19428c2ecf20Sopenharmony_ci	.manage_power = usbnet_manage_power,
19438c2ecf20Sopenharmony_ci	.status = cdc_ncm_status,
19448c2ecf20Sopenharmony_ci	.rx_fixup = cdc_ncm_rx_fixup,
19458c2ecf20Sopenharmony_ci	.tx_fixup = cdc_ncm_tx_fixup,
19468c2ecf20Sopenharmony_ci	.set_rx_mode = usbnet_cdc_update_filter,
19478c2ecf20Sopenharmony_ci};
19488c2ecf20Sopenharmony_ci
19498c2ecf20Sopenharmony_cistatic const struct usb_device_id cdc_devs[] = {
19508c2ecf20Sopenharmony_ci	/* Ericsson MBM devices like F5521gw */
19518c2ecf20Sopenharmony_ci	{ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
19528c2ecf20Sopenharmony_ci		| USB_DEVICE_ID_MATCH_VENDOR,
19538c2ecf20Sopenharmony_ci	  .idVendor = 0x0bdb,
19548c2ecf20Sopenharmony_ci	  .bInterfaceClass = USB_CLASS_COMM,
19558c2ecf20Sopenharmony_ci	  .bInterfaceSubClass = USB_CDC_SUBCLASS_NCM,
19568c2ecf20Sopenharmony_ci	  .bInterfaceProtocol = USB_CDC_PROTO_NONE,
19578c2ecf20Sopenharmony_ci	  .driver_info = (unsigned long) &wwan_info,
19588c2ecf20Sopenharmony_ci	},
19598c2ecf20Sopenharmony_ci
19608c2ecf20Sopenharmony_ci	/* Telit LE910 V2 */
19618c2ecf20Sopenharmony_ci	{ USB_DEVICE_AND_INTERFACE_INFO(0x1bc7, 0x0036,
19628c2ecf20Sopenharmony_ci		USB_CLASS_COMM,
19638c2ecf20Sopenharmony_ci		USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE),
19648c2ecf20Sopenharmony_ci	  .driver_info = (unsigned long)&wwan_noarp_info,
19658c2ecf20Sopenharmony_ci	},
19668c2ecf20Sopenharmony_ci
19678c2ecf20Sopenharmony_ci	/* DW5812 LTE Verizon Mobile Broadband Card
19688c2ecf20Sopenharmony_ci	 * Unlike DW5550 this device requires FLAG_NOARP
19698c2ecf20Sopenharmony_ci	 */
19708c2ecf20Sopenharmony_ci	{ USB_DEVICE_AND_INTERFACE_INFO(0x413c, 0x81bb,
19718c2ecf20Sopenharmony_ci		USB_CLASS_COMM,
19728c2ecf20Sopenharmony_ci		USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE),
19738c2ecf20Sopenharmony_ci	  .driver_info = (unsigned long)&wwan_noarp_info,
19748c2ecf20Sopenharmony_ci	},
19758c2ecf20Sopenharmony_ci
19768c2ecf20Sopenharmony_ci	/* DW5813 LTE AT&T Mobile Broadband Card
19778c2ecf20Sopenharmony_ci	 * Unlike DW5550 this device requires FLAG_NOARP
19788c2ecf20Sopenharmony_ci	 */
19798c2ecf20Sopenharmony_ci	{ USB_DEVICE_AND_INTERFACE_INFO(0x413c, 0x81bc,
19808c2ecf20Sopenharmony_ci		USB_CLASS_COMM,
19818c2ecf20Sopenharmony_ci		USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE),
19828c2ecf20Sopenharmony_ci	  .driver_info = (unsigned long)&wwan_noarp_info,
19838c2ecf20Sopenharmony_ci	},
19848c2ecf20Sopenharmony_ci
19858c2ecf20Sopenharmony_ci	/* Dell branded MBM devices like DW5550 */
19868c2ecf20Sopenharmony_ci	{ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
19878c2ecf20Sopenharmony_ci		| USB_DEVICE_ID_MATCH_VENDOR,
19888c2ecf20Sopenharmony_ci	  .idVendor = 0x413c,
19898c2ecf20Sopenharmony_ci	  .bInterfaceClass = USB_CLASS_COMM,
19908c2ecf20Sopenharmony_ci	  .bInterfaceSubClass = USB_CDC_SUBCLASS_NCM,
19918c2ecf20Sopenharmony_ci	  .bInterfaceProtocol = USB_CDC_PROTO_NONE,
19928c2ecf20Sopenharmony_ci	  .driver_info = (unsigned long) &wwan_info,
19938c2ecf20Sopenharmony_ci	},
19948c2ecf20Sopenharmony_ci
19958c2ecf20Sopenharmony_ci	/* Toshiba branded MBM devices */
19968c2ecf20Sopenharmony_ci	{ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
19978c2ecf20Sopenharmony_ci		| USB_DEVICE_ID_MATCH_VENDOR,
19988c2ecf20Sopenharmony_ci	  .idVendor = 0x0930,
19998c2ecf20Sopenharmony_ci	  .bInterfaceClass = USB_CLASS_COMM,
20008c2ecf20Sopenharmony_ci	  .bInterfaceSubClass = USB_CDC_SUBCLASS_NCM,
20018c2ecf20Sopenharmony_ci	  .bInterfaceProtocol = USB_CDC_PROTO_NONE,
20028c2ecf20Sopenharmony_ci	  .driver_info = (unsigned long) &wwan_info,
20038c2ecf20Sopenharmony_ci	},
20048c2ecf20Sopenharmony_ci
20058c2ecf20Sopenharmony_ci	/* tag Huawei devices as wwan */
20068c2ecf20Sopenharmony_ci	{ USB_VENDOR_AND_INTERFACE_INFO(0x12d1,
20078c2ecf20Sopenharmony_ci					USB_CLASS_COMM,
20088c2ecf20Sopenharmony_ci					USB_CDC_SUBCLASS_NCM,
20098c2ecf20Sopenharmony_ci					USB_CDC_PROTO_NONE),
20108c2ecf20Sopenharmony_ci	  .driver_info = (unsigned long)&wwan_info,
20118c2ecf20Sopenharmony_ci	},
20128c2ecf20Sopenharmony_ci
20138c2ecf20Sopenharmony_ci	/* Infineon(now Intel) HSPA Modem platform */
20148c2ecf20Sopenharmony_ci	{ USB_DEVICE_AND_INTERFACE_INFO(0x1519, 0x0443,
20158c2ecf20Sopenharmony_ci		USB_CLASS_COMM,
20168c2ecf20Sopenharmony_ci		USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE),
20178c2ecf20Sopenharmony_ci	  .driver_info = (unsigned long)&wwan_noarp_info,
20188c2ecf20Sopenharmony_ci	},
20198c2ecf20Sopenharmony_ci
20208c2ecf20Sopenharmony_ci	/* u-blox TOBY-L4 */
20218c2ecf20Sopenharmony_ci	{ USB_DEVICE_AND_INTERFACE_INFO(0x1546, 0x1010,
20228c2ecf20Sopenharmony_ci		USB_CLASS_COMM,
20238c2ecf20Sopenharmony_ci		USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE),
20248c2ecf20Sopenharmony_ci	  .driver_info = (unsigned long)&wwan_info,
20258c2ecf20Sopenharmony_ci	},
20268c2ecf20Sopenharmony_ci
20278c2ecf20Sopenharmony_ci	/* Generic CDC-NCM devices */
20288c2ecf20Sopenharmony_ci	{ USB_INTERFACE_INFO(USB_CLASS_COMM,
20298c2ecf20Sopenharmony_ci		USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE),
20308c2ecf20Sopenharmony_ci		.driver_info = (unsigned long)&cdc_ncm_info,
20318c2ecf20Sopenharmony_ci	},
20328c2ecf20Sopenharmony_ci	{
20338c2ecf20Sopenharmony_ci	},
20348c2ecf20Sopenharmony_ci};
20358c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, cdc_devs);
20368c2ecf20Sopenharmony_ci
20378c2ecf20Sopenharmony_cistatic struct usb_driver cdc_ncm_driver = {
20388c2ecf20Sopenharmony_ci	.name = "cdc_ncm",
20398c2ecf20Sopenharmony_ci	.id_table = cdc_devs,
20408c2ecf20Sopenharmony_ci	.probe = usbnet_probe,
20418c2ecf20Sopenharmony_ci	.disconnect = usbnet_disconnect,
20428c2ecf20Sopenharmony_ci	.suspend = usbnet_suspend,
20438c2ecf20Sopenharmony_ci	.resume = usbnet_resume,
20448c2ecf20Sopenharmony_ci	.reset_resume =	usbnet_resume,
20458c2ecf20Sopenharmony_ci	.supports_autosuspend = 1,
20468c2ecf20Sopenharmony_ci	.disable_hub_initiated_lpm = 1,
20478c2ecf20Sopenharmony_ci};
20488c2ecf20Sopenharmony_ci
20498c2ecf20Sopenharmony_cimodule_usb_driver(cdc_ncm_driver);
20508c2ecf20Sopenharmony_ci
20518c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hans Petter Selasky");
20528c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("USB CDC NCM host driver");
20538c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
2054