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