diff options
Diffstat (limited to 'drivers/net/usb/cdc_ncm.c')
| -rw-r--r-- | drivers/net/usb/cdc_ncm.c | 1057 | 
1 files changed, 696 insertions, 361 deletions
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 43afde8f48d..80a844e0ae0 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -39,7 +39,6 @@   */  #include <linux/module.h> -#include <linux/init.h>  #include <linux/netdevice.h>  #include <linux/ctype.h>  #include <linux/ethtool.h> @@ -53,8 +52,6 @@  #include <linux/usb/cdc.h>  #include <linux/usb/cdc_ncm.h> -#define	DRIVER_VERSION				"14-Mar-2012" -  #if IS_ENABLED(CONFIG_USB_NET_CDC_MBIM)  static bool prefer_mbim = true;  #else @@ -68,31 +65,384 @@ static void cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx);  static enum hrtimer_restart cdc_ncm_tx_timer_cb(struct hrtimer *hr_timer);  static struct usb_driver cdc_ncm_driver; -static void -cdc_ncm_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) +struct cdc_ncm_stats { +	char stat_string[ETH_GSTRING_LEN]; +	int sizeof_stat; +	int stat_offset; +}; + +#define CDC_NCM_STAT(str, m) { \ +		.stat_string = str, \ +		.sizeof_stat = sizeof(((struct cdc_ncm_ctx *)0)->m), \ +		.stat_offset = offsetof(struct cdc_ncm_ctx, m) } +#define CDC_NCM_SIMPLE_STAT(m)	CDC_NCM_STAT(__stringify(m), m) + +static const struct cdc_ncm_stats cdc_ncm_gstrings_stats[] = { +	CDC_NCM_SIMPLE_STAT(tx_reason_ntb_full), +	CDC_NCM_SIMPLE_STAT(tx_reason_ndp_full), +	CDC_NCM_SIMPLE_STAT(tx_reason_timeout), +	CDC_NCM_SIMPLE_STAT(tx_reason_max_datagram), +	CDC_NCM_SIMPLE_STAT(tx_overhead), +	CDC_NCM_SIMPLE_STAT(tx_ntbs), +	CDC_NCM_SIMPLE_STAT(rx_overhead), +	CDC_NCM_SIMPLE_STAT(rx_ntbs), +}; + +static int cdc_ncm_get_sset_count(struct net_device __always_unused *netdev, int sset) +{ +	switch (sset) { +	case ETH_SS_STATS: +		return ARRAY_SIZE(cdc_ncm_gstrings_stats); +	default: +		return -EOPNOTSUPP; +	} +} + +static void cdc_ncm_get_ethtool_stats(struct net_device *netdev, +				    struct ethtool_stats __always_unused *stats, +				    u64 *data) +{ +	struct usbnet *dev = netdev_priv(netdev); +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; +	int i; +	char *p = NULL; + +	for (i = 0; i < ARRAY_SIZE(cdc_ncm_gstrings_stats); i++) { +		p = (char *)ctx + cdc_ncm_gstrings_stats[i].stat_offset; +		data[i] = (cdc_ncm_gstrings_stats[i].sizeof_stat == sizeof(u64)) ? *(u64 *)p : *(u32 *)p; +	} +} + +static void cdc_ncm_get_strings(struct net_device __always_unused *netdev, u32 stringset, u8 *data) +{ +	u8 *p = data; +	int i; + +	switch (stringset) { +	case ETH_SS_STATS: +		for (i = 0; i < ARRAY_SIZE(cdc_ncm_gstrings_stats); i++) { +			memcpy(p, cdc_ncm_gstrings_stats[i].stat_string, ETH_GSTRING_LEN); +			p += ETH_GSTRING_LEN; +		} +	} +} + +static void cdc_ncm_update_rxtx_max(struct usbnet *dev, u32 new_rx, u32 new_tx); + +static const struct ethtool_ops cdc_ncm_ethtool_ops = { +	.get_settings      = usbnet_get_settings, +	.set_settings      = usbnet_set_settings, +	.get_link          = usbnet_get_link, +	.nway_reset        = usbnet_nway_reset, +	.get_drvinfo       = usbnet_get_drvinfo, +	.get_msglevel      = usbnet_get_msglevel, +	.set_msglevel      = usbnet_set_msglevel, +	.get_ts_info       = ethtool_op_get_ts_info, +	.get_sset_count    = cdc_ncm_get_sset_count, +	.get_strings       = cdc_ncm_get_strings, +	.get_ethtool_stats = cdc_ncm_get_ethtool_stats, +}; + +static u32 cdc_ncm_check_rx_max(struct usbnet *dev, u32 new_rx) +{ +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; +	u32 val, max, min; + +	/* clamp new_rx to sane values */ +	min = USB_CDC_NCM_NTB_MIN_IN_SIZE; +	max = min_t(u32, CDC_NCM_NTB_MAX_SIZE_RX, le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize)); + +	/* dwNtbInMaxSize spec violation? Use MIN size for both limits */ +	if (max < min) { +		dev_warn(&dev->intf->dev, "dwNtbInMaxSize=%u is too small. Using %u\n", +			 le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize), min); +		max = min; +	} + +	val = clamp_t(u32, new_rx, min, max); +	if (val != new_rx) +		dev_dbg(&dev->intf->dev, "rx_max must be in the [%u, %u] range\n", min, max); + +	return val; +} + +static u32 cdc_ncm_check_tx_max(struct usbnet *dev, u32 new_tx) +{ +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; +	u32 val, max, min; + +	/* clamp new_tx to sane values */ +	min = ctx->max_datagram_size + ctx->max_ndp_size + sizeof(struct usb_cdc_ncm_nth16); +	max = min_t(u32, CDC_NCM_NTB_MAX_SIZE_TX, le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize)); + +	/* some devices set dwNtbOutMaxSize too low for the above default */ +	min = min(min, max); + +	val = clamp_t(u32, new_tx, min, max); +	if (val != new_tx) +		dev_dbg(&dev->intf->dev, "tx_max must be in the [%u, %u] range\n", min, max); + +	return val; +} + +static ssize_t cdc_ncm_show_min_tx_pkt(struct device *d, struct device_attribute *attr, char *buf) +{ +	struct usbnet *dev = netdev_priv(to_net_dev(d)); +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + +	return sprintf(buf, "%u\n", ctx->min_tx_pkt); +} + +static ssize_t cdc_ncm_show_rx_max(struct device *d, struct device_attribute *attr, char *buf) +{ +	struct usbnet *dev = netdev_priv(to_net_dev(d)); +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + +	return sprintf(buf, "%u\n", ctx->rx_max); +} + +static ssize_t cdc_ncm_show_tx_max(struct device *d, struct device_attribute *attr, char *buf) +{ +	struct usbnet *dev = netdev_priv(to_net_dev(d)); +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + +	return sprintf(buf, "%u\n", ctx->tx_max); +} + +static ssize_t cdc_ncm_show_tx_timer_usecs(struct device *d, struct device_attribute *attr, char *buf) +{ +	struct usbnet *dev = netdev_priv(to_net_dev(d)); +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + +	return sprintf(buf, "%u\n", ctx->timer_interval / (u32)NSEC_PER_USEC); +} + +static ssize_t cdc_ncm_store_min_tx_pkt(struct device *d,  struct device_attribute *attr, const char *buf, size_t len) +{ +	struct usbnet *dev = netdev_priv(to_net_dev(d)); +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; +	unsigned long val; + +	/* no need to restrict values - anything from 0 to infinity is OK */ +	if (kstrtoul(buf, 0, &val)) +		return -EINVAL; + +	ctx->min_tx_pkt = val; +	return len; +} + +static ssize_t cdc_ncm_store_rx_max(struct device *d,  struct device_attribute *attr, const char *buf, size_t len)  { -	struct usbnet *dev = netdev_priv(net); +	struct usbnet *dev = netdev_priv(to_net_dev(d)); +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; +	unsigned long val; + +	if (kstrtoul(buf, 0, &val) || cdc_ncm_check_rx_max(dev, val) != val) +		return -EINVAL; + +	cdc_ncm_update_rxtx_max(dev, val, ctx->tx_max); +	return len; +} + +static ssize_t cdc_ncm_store_tx_max(struct device *d,  struct device_attribute *attr, const char *buf, size_t len) +{ +	struct usbnet *dev = netdev_priv(to_net_dev(d)); +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; +	unsigned long val; + +	if (kstrtoul(buf, 0, &val) || cdc_ncm_check_tx_max(dev, val) != val) +		return -EINVAL; -	strlcpy(info->driver, dev->driver_name, sizeof(info->driver)); -	strlcpy(info->version, DRIVER_VERSION, sizeof(info->version)); -	strlcpy(info->fw_version, dev->driver_info->description, -		sizeof(info->fw_version)); -	usb_make_path(dev->udev, info->bus_info, sizeof(info->bus_info)); +	cdc_ncm_update_rxtx_max(dev, ctx->rx_max, val); +	return len; +} + +static ssize_t cdc_ncm_store_tx_timer_usecs(struct device *d,  struct device_attribute *attr, const char *buf, size_t len) +{ +	struct usbnet *dev = netdev_priv(to_net_dev(d)); +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; +	ssize_t ret; +	unsigned long val; + +	ret = kstrtoul(buf, 0, &val); +	if (ret) +		return ret; +	if (val && (val < CDC_NCM_TIMER_INTERVAL_MIN || val > CDC_NCM_TIMER_INTERVAL_MAX)) +		return -EINVAL; + +	spin_lock_bh(&ctx->mtx); +	ctx->timer_interval = val * NSEC_PER_USEC; +	if (!ctx->timer_interval) +		ctx->tx_timer_pending = 0; +	spin_unlock_bh(&ctx->mtx); +	return len;  } -static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx) +static DEVICE_ATTR(min_tx_pkt, S_IRUGO | S_IWUSR, cdc_ncm_show_min_tx_pkt, cdc_ncm_store_min_tx_pkt); +static DEVICE_ATTR(rx_max, S_IRUGO | S_IWUSR, cdc_ncm_show_rx_max, cdc_ncm_store_rx_max); +static DEVICE_ATTR(tx_max, S_IRUGO | S_IWUSR, cdc_ncm_show_tx_max, cdc_ncm_store_tx_max); +static DEVICE_ATTR(tx_timer_usecs, S_IRUGO | S_IWUSR, cdc_ncm_show_tx_timer_usecs, cdc_ncm_store_tx_timer_usecs); + +#define NCM_PARM_ATTR(name, format, tocpu)				\ +static ssize_t cdc_ncm_show_##name(struct device *d, struct device_attribute *attr, char *buf) \ +{ \ +	struct usbnet *dev = netdev_priv(to_net_dev(d)); \ +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; \ +	return sprintf(buf, format "\n", tocpu(ctx->ncm_parm.name));	\ +} \ +static DEVICE_ATTR(name, S_IRUGO, cdc_ncm_show_##name, NULL) + +NCM_PARM_ATTR(bmNtbFormatsSupported, "0x%04x", le16_to_cpu); +NCM_PARM_ATTR(dwNtbInMaxSize, "%u", le32_to_cpu); +NCM_PARM_ATTR(wNdpInDivisor, "%u", le16_to_cpu); +NCM_PARM_ATTR(wNdpInPayloadRemainder, "%u", le16_to_cpu); +NCM_PARM_ATTR(wNdpInAlignment, "%u", le16_to_cpu); +NCM_PARM_ATTR(dwNtbOutMaxSize, "%u", le32_to_cpu); +NCM_PARM_ATTR(wNdpOutDivisor, "%u", le16_to_cpu); +NCM_PARM_ATTR(wNdpOutPayloadRemainder, "%u", le16_to_cpu); +NCM_PARM_ATTR(wNdpOutAlignment, "%u", le16_to_cpu); +NCM_PARM_ATTR(wNtbOutMaxDatagrams, "%u", le16_to_cpu); + +static struct attribute *cdc_ncm_sysfs_attrs[] = { +	&dev_attr_min_tx_pkt.attr, +	&dev_attr_rx_max.attr, +	&dev_attr_tx_max.attr, +	&dev_attr_tx_timer_usecs.attr, +	&dev_attr_bmNtbFormatsSupported.attr, +	&dev_attr_dwNtbInMaxSize.attr, +	&dev_attr_wNdpInDivisor.attr, +	&dev_attr_wNdpInPayloadRemainder.attr, +	&dev_attr_wNdpInAlignment.attr, +	&dev_attr_dwNtbOutMaxSize.attr, +	&dev_attr_wNdpOutDivisor.attr, +	&dev_attr_wNdpOutPayloadRemainder.attr, +	&dev_attr_wNdpOutAlignment.attr, +	&dev_attr_wNtbOutMaxDatagrams.attr, +	NULL, +}; + +static struct attribute_group cdc_ncm_sysfs_attr_group = { +	.name = "cdc_ncm", +	.attrs = cdc_ncm_sysfs_attrs, +}; + +/* handle rx_max and tx_max changes */ +static void cdc_ncm_update_rxtx_max(struct usbnet *dev, u32 new_rx, u32 new_tx)  { +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; +	u8 iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber;  	u32 val; -	u8 flags; -	u8 iface_no; -	int err; -	int eth_hlen; -	u16 ntb_fmt_supported; -	u32 min_dgram_size; -	u32 min_hdr_size; -	struct usbnet *dev = netdev_priv(ctx->netdev); -	iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber; +	val = cdc_ncm_check_rx_max(dev, new_rx); + +	/* inform device about NTB input size changes */ +	if (val != ctx->rx_max) { +		__le32 dwNtbInMaxSize = cpu_to_le32(val); + +		dev_info(&dev->intf->dev, "setting rx_max = %u\n", val); + +		/* tell device to use new size */ +		if (usbnet_write_cmd(dev, USB_CDC_SET_NTB_INPUT_SIZE, +				     USB_TYPE_CLASS | USB_DIR_OUT +				     | USB_RECIP_INTERFACE, +				     0, iface_no, &dwNtbInMaxSize, 4) < 0) +			dev_dbg(&dev->intf->dev, "Setting NTB Input Size failed\n"); +		else +			ctx->rx_max = val; +	} + +	/* usbnet use these values for sizing rx queues */ +	if (dev->rx_urb_size != ctx->rx_max) { +		dev->rx_urb_size = ctx->rx_max; +		if (netif_running(dev->net)) +			usbnet_unlink_rx_urbs(dev); +	} + +	val = cdc_ncm_check_tx_max(dev, new_tx); +	if (val != ctx->tx_max) +		dev_info(&dev->intf->dev, "setting tx_max = %u\n", val); + +	/* Adding a pad byte here if necessary simplifies the handling +	 * in cdc_ncm_fill_tx_frame, making tx_max always represent +	 * the real skb max size. +	 * +	 * We cannot use dev->maxpacket here because this is called from +	 * .bind which is called before usbnet sets up dev->maxpacket +	 */ +	if (val != le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize) && +	    val % usb_maxpacket(dev->udev, dev->out, 1) == 0) +		val++; + +	/* we might need to flush any pending tx buffers if running */ +	if (netif_running(dev->net) && val > ctx->tx_max) { +		netif_tx_lock_bh(dev->net); +		usbnet_start_xmit(NULL, dev->net); +		/* make sure tx_curr_skb is reallocated if it was empty */ +		if (ctx->tx_curr_skb) { +			dev_kfree_skb_any(ctx->tx_curr_skb); +			ctx->tx_curr_skb = NULL; +		} +		ctx->tx_max = val; +		netif_tx_unlock_bh(dev->net); +	} else { +		ctx->tx_max = val; +	} + +	dev->hard_mtu = ctx->tx_max; + +	/* max qlen depend on hard_mtu and rx_urb_size */ +	usbnet_update_max_qlen(dev); + +	/* never pad more than 3 full USB packets per transfer */ +	ctx->min_tx_pkt = clamp_t(u16, ctx->tx_max - 3 * usb_maxpacket(dev->udev, dev->out, 1), +				  CDC_NCM_MIN_TX_PKT, ctx->tx_max); +} + +/* helpers for NCM and MBIM differences */ +static u8 cdc_ncm_flags(struct usbnet *dev) +{ +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + +	if (cdc_ncm_comm_intf_is_mbim(dev->intf->cur_altsetting) && ctx->mbim_desc) +		return ctx->mbim_desc->bmNetworkCapabilities; +	if (ctx->func_desc) +		return ctx->func_desc->bmNetworkCapabilities; +	return 0; +} + +static int cdc_ncm_eth_hlen(struct usbnet *dev) +{ +	if (cdc_ncm_comm_intf_is_mbim(dev->intf->cur_altsetting)) +		return 0; +	return ETH_HLEN; +} + +static u32 cdc_ncm_min_dgram_size(struct usbnet *dev) +{ +	if (cdc_ncm_comm_intf_is_mbim(dev->intf->cur_altsetting)) +		return CDC_MBIM_MIN_DATAGRAM_SIZE; +	return CDC_NCM_MIN_DATAGRAM_SIZE; +} + +static u32 cdc_ncm_max_dgram_size(struct usbnet *dev) +{ +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + +	if (cdc_ncm_comm_intf_is_mbim(dev->intf->cur_altsetting) && ctx->mbim_desc) +		return le16_to_cpu(ctx->mbim_desc->wMaxSegmentSize); +	if (ctx->ether_desc) +		return le16_to_cpu(ctx->ether_desc->wMaxSegmentSize); +	return CDC_NCM_MAX_DATAGRAM_SIZE; +} + +/* initial one-time device setup.  MUST be called with the data interface + * in altsetting 0 + */ +static int cdc_ncm_init(struct usbnet *dev) +{ +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; +	u8 iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber; +	int err;  	err = usbnet_read_cmd(dev, USB_CDC_GET_NTB_PARAMETERS,  			      USB_TYPE_CLASS | USB_DIR_IN @@ -100,11 +450,40 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)  			      0, iface_no, &ctx->ncm_parm,  			      sizeof(ctx->ncm_parm));  	if (err < 0) { -		pr_debug("failed GET_NTB_PARAMETERS\n"); -		return 1; +		dev_err(&dev->intf->dev, "failed GET_NTB_PARAMETERS\n"); +		return err; /* GET_NTB_PARAMETERS is required */ +	} + +	/* set CRC Mode */ +	if (cdc_ncm_flags(dev) & USB_CDC_NCM_NCAP_CRC_MODE) { +		dev_dbg(&dev->intf->dev, "Setting CRC mode off\n"); +		err = usbnet_write_cmd(dev, USB_CDC_SET_CRC_MODE, +				       USB_TYPE_CLASS | USB_DIR_OUT +				       | USB_RECIP_INTERFACE, +				       USB_CDC_NCM_CRC_NOT_APPENDED, +				       iface_no, NULL, 0); +		if (err < 0) +			dev_err(&dev->intf->dev, "SET_CRC_MODE failed\n"); +	} + +	/* set NTB format, if both formats are supported. +	 * +	 * "The host shall only send this command while the NCM Data +	 *  Interface is in alternate setting 0." +	 */ +	if (le16_to_cpu(ctx->ncm_parm.bmNtbFormatsSupported) & +						USB_CDC_NCM_NTB32_SUPPORTED) { +		dev_dbg(&dev->intf->dev, "Setting NTB format to 16-bit\n"); +		err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_FORMAT, +				       USB_TYPE_CLASS | USB_DIR_OUT +				       | USB_RECIP_INTERFACE, +				       USB_CDC_NCM_NTB16_FORMAT, +				       iface_no, NULL, 0); +		if (err < 0) +			dev_err(&dev->intf->dev, "SET_NTB_FORMAT failed\n");  	} -	/* read correct set of parameters according to device mode */ +	/* set initial device values */  	ctx->rx_max = le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize);  	ctx->tx_max = le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize);  	ctx->tx_remainder = le16_to_cpu(ctx->ncm_parm.wNdpOutPayloadRemainder); @@ -112,66 +491,79 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)  	ctx->tx_ndp_modulus = le16_to_cpu(ctx->ncm_parm.wNdpOutAlignment);  	/* devices prior to NCM Errata shall set this field to zero */  	ctx->tx_max_datagrams = le16_to_cpu(ctx->ncm_parm.wNtbOutMaxDatagrams); -	ntb_fmt_supported = le16_to_cpu(ctx->ncm_parm.bmNtbFormatsSupported); - -	eth_hlen = ETH_HLEN; -	min_dgram_size = CDC_NCM_MIN_DATAGRAM_SIZE; -	min_hdr_size = CDC_NCM_MIN_HDR_SIZE; -	if (ctx->mbim_desc != NULL) { -		flags = ctx->mbim_desc->bmNetworkCapabilities; -		eth_hlen = 0; -		min_dgram_size = CDC_MBIM_MIN_DATAGRAM_SIZE; -		min_hdr_size = 0; -	} else if (ctx->func_desc != NULL) { -		flags = ctx->func_desc->bmNetworkCapabilities; -	} else { -		flags = 0; -	} -	pr_debug("dwNtbInMaxSize=%u dwNtbOutMaxSize=%u " -		 "wNdpOutPayloadRemainder=%u wNdpOutDivisor=%u " -		 "wNdpOutAlignment=%u wNtbOutMaxDatagrams=%u flags=0x%x\n", -		 ctx->rx_max, ctx->tx_max, ctx->tx_remainder, ctx->tx_modulus, -		 ctx->tx_ndp_modulus, ctx->tx_max_datagrams, flags); +	dev_dbg(&dev->intf->dev, +		"dwNtbInMaxSize=%u dwNtbOutMaxSize=%u wNdpOutPayloadRemainder=%u wNdpOutDivisor=%u wNdpOutAlignment=%u wNtbOutMaxDatagrams=%u flags=0x%x\n", +		ctx->rx_max, ctx->tx_max, ctx->tx_remainder, ctx->tx_modulus, +		ctx->tx_ndp_modulus, ctx->tx_max_datagrams, cdc_ncm_flags(dev));  	/* max count of tx datagrams */  	if ((ctx->tx_max_datagrams == 0) ||  			(ctx->tx_max_datagrams > CDC_NCM_DPT_DATAGRAMS_MAX))  		ctx->tx_max_datagrams = CDC_NCM_DPT_DATAGRAMS_MAX; -	/* verify maximum size of received NTB in bytes */ -	if (ctx->rx_max < USB_CDC_NCM_NTB_MIN_IN_SIZE) { -		pr_debug("Using min receive length=%d\n", -						USB_CDC_NCM_NTB_MIN_IN_SIZE); -		ctx->rx_max = USB_CDC_NCM_NTB_MIN_IN_SIZE; -	} +	/* set up maximum NDP size */ +	ctx->max_ndp_size = sizeof(struct usb_cdc_ncm_ndp16) + (ctx->tx_max_datagrams + 1) * sizeof(struct usb_cdc_ncm_dpe16); -	if (ctx->rx_max > CDC_NCM_NTB_MAX_SIZE_RX) { -		pr_debug("Using default maximum receive length=%d\n", -						CDC_NCM_NTB_MAX_SIZE_RX); -		ctx->rx_max = CDC_NCM_NTB_MAX_SIZE_RX; -	} +	/* initial coalescing timer interval */ +	ctx->timer_interval = CDC_NCM_TIMER_INTERVAL_USEC * NSEC_PER_USEC; -	/* inform device about NTB input size changes */ -	if (ctx->rx_max != le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize)) { -		__le32 dwNtbInMaxSize = cpu_to_le32(ctx->rx_max); +	return 0; +} -		err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_INPUT_SIZE, -				       USB_TYPE_CLASS | USB_DIR_OUT -				       | USB_RECIP_INTERFACE, -				       0, iface_no, &dwNtbInMaxSize, 4); -		if (err < 0) -			pr_debug("Setting NTB Input Size failed\n"); +/* set a new max datagram size */ +static void cdc_ncm_set_dgram_size(struct usbnet *dev, int new_size) +{ +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; +	u8 iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber; +	__le16 max_datagram_size; +	u16 mbim_mtu; +	int err; + +	/* set default based on descriptors */ +	ctx->max_datagram_size = clamp_t(u32, new_size, +					 cdc_ncm_min_dgram_size(dev), +					 CDC_NCM_MAX_DATAGRAM_SIZE); + +	/* inform the device about the selected Max Datagram Size? */ +	if (!(cdc_ncm_flags(dev) & USB_CDC_NCM_NCAP_MAX_DATAGRAM_SIZE)) +		goto out; + +	/* read current mtu value from device */ +	err = usbnet_read_cmd(dev, USB_CDC_GET_MAX_DATAGRAM_SIZE, +			      USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE, +			      0, iface_no, &max_datagram_size, 2); +	if (err < 0) { +		dev_dbg(&dev->intf->dev, "GET_MAX_DATAGRAM_SIZE failed\n"); +		goto out;  	} -	/* verify maximum size of transmitted NTB in bytes */ -	if ((ctx->tx_max < -	    (min_hdr_size + min_dgram_size)) || -	    (ctx->tx_max > CDC_NCM_NTB_MAX_SIZE_TX)) { -		pr_debug("Using default maximum transmit length=%d\n", -						CDC_NCM_NTB_MAX_SIZE_TX); -		ctx->tx_max = CDC_NCM_NTB_MAX_SIZE_TX; +	if (le16_to_cpu(max_datagram_size) == ctx->max_datagram_size) +		goto out; + +	max_datagram_size = cpu_to_le16(ctx->max_datagram_size); +	err = usbnet_write_cmd(dev, USB_CDC_SET_MAX_DATAGRAM_SIZE, +			       USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE, +			       0, iface_no, &max_datagram_size, 2); +	if (err < 0) +		dev_dbg(&dev->intf->dev, "SET_MAX_DATAGRAM_SIZE failed\n"); + +out: +	/* set MTU to max supported by the device if necessary */ +	dev->net->mtu = min_t(int, dev->net->mtu, ctx->max_datagram_size - cdc_ncm_eth_hlen(dev)); + +	/* do not exceed operater preferred MTU */ +	if (ctx->mbim_extended_desc) { +		mbim_mtu = le16_to_cpu(ctx->mbim_extended_desc->wMTU); +		if (mbim_mtu != 0 && mbim_mtu < dev->net->mtu) +			dev->net->mtu = mbim_mtu;  	} +} + +static void cdc_ncm_fix_modulus(struct usbnet *dev) +{ +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; +	u32 val;  	/*  	 * verify that the structure alignment is: @@ -183,7 +575,7 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)  	if ((val < USB_CDC_NCM_NDP_ALIGN_MIN_SIZE) ||  	    (val != ((-val) & val)) || (val >= ctx->tx_max)) { -		pr_debug("Using default alignment: 4 bytes\n"); +		dev_dbg(&dev->intf->dev, "Using default alignment: 4 bytes\n");  		ctx->tx_ndp_modulus = USB_CDC_NCM_NDP_ALIGN_MIN_SIZE;  	} @@ -197,104 +589,49 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)  	if ((val < USB_CDC_NCM_NDP_ALIGN_MIN_SIZE) ||  	    (val != ((-val) & val)) || (val >= ctx->tx_max)) { -		pr_debug("Using default transmit modulus: 4 bytes\n"); +		dev_dbg(&dev->intf->dev, "Using default transmit modulus: 4 bytes\n");  		ctx->tx_modulus = USB_CDC_NCM_NDP_ALIGN_MIN_SIZE;  	}  	/* verify the payload remainder */  	if (ctx->tx_remainder >= ctx->tx_modulus) { -		pr_debug("Using default transmit remainder: 0 bytes\n"); +		dev_dbg(&dev->intf->dev, "Using default transmit remainder: 0 bytes\n");  		ctx->tx_remainder = 0;  	}  	/* adjust TX-remainder according to NCM specification. */ -	ctx->tx_remainder = ((ctx->tx_remainder - eth_hlen) & +	ctx->tx_remainder = ((ctx->tx_remainder - cdc_ncm_eth_hlen(dev)) &  			     (ctx->tx_modulus - 1)); +} -	/* additional configuration */ - -	/* set CRC Mode */ -	if (flags & USB_CDC_NCM_NCAP_CRC_MODE) { -		err = usbnet_write_cmd(dev, USB_CDC_SET_CRC_MODE, -				       USB_TYPE_CLASS | USB_DIR_OUT -				       | USB_RECIP_INTERFACE, -				       USB_CDC_NCM_CRC_NOT_APPENDED, -				       iface_no, NULL, 0); -		if (err < 0) -			pr_debug("Setting CRC mode off failed\n"); -	} - -	/* set NTB format, if both formats are supported */ -	if (ntb_fmt_supported & USB_CDC_NCM_NTH32_SIGN) { -		err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_FORMAT, -				       USB_TYPE_CLASS | USB_DIR_OUT -				       | USB_RECIP_INTERFACE, -				       USB_CDC_NCM_NTB16_FORMAT, -				       iface_no, NULL, 0); -		if (err < 0) -			pr_debug("Setting NTB format to 16-bit failed\n"); -	} +static int cdc_ncm_setup(struct usbnet *dev) +{ +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; +	u32 def_rx, def_tx; -	ctx->max_datagram_size = min_dgram_size; +	/* be conservative when selecting intial buffer size to +	 * increase the number of hosts this will work for +	 */ +	def_rx = min_t(u32, CDC_NCM_NTB_DEF_SIZE_RX, +		       le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize)); +	def_tx = min_t(u32, CDC_NCM_NTB_DEF_SIZE_TX, +		       le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize)); -	/* set Max Datagram Size (MTU) */ -	if (flags & USB_CDC_NCM_NCAP_MAX_DATAGRAM_SIZE) { -		__le16 max_datagram_size; -		u16 eth_max_sz; -		if (ctx->ether_desc != NULL) -			eth_max_sz = le16_to_cpu(ctx->ether_desc->wMaxSegmentSize); -		else if (ctx->mbim_desc != NULL) -			eth_max_sz = le16_to_cpu(ctx->mbim_desc->wMaxSegmentSize); -		else -			goto max_dgram_err; - -		err = usbnet_read_cmd(dev, USB_CDC_GET_MAX_DATAGRAM_SIZE, -				      USB_TYPE_CLASS | USB_DIR_IN -				      | USB_RECIP_INTERFACE, -				      0, iface_no, &max_datagram_size, 2); -		if (err < 0) { -			pr_debug("GET_MAX_DATAGRAM_SIZE failed, use size=%u\n", -				 min_dgram_size); -		} else { -			ctx->max_datagram_size = -				le16_to_cpu(max_datagram_size); -			/* Check Eth descriptor value */ -			if (ctx->max_datagram_size > eth_max_sz) -					ctx->max_datagram_size = eth_max_sz; - -			if (ctx->max_datagram_size > CDC_NCM_MAX_DATAGRAM_SIZE) -				ctx->max_datagram_size = CDC_NCM_MAX_DATAGRAM_SIZE; - -			if (ctx->max_datagram_size < min_dgram_size) -				ctx->max_datagram_size = min_dgram_size; - -			/* if value changed, update device */ -			if (ctx->max_datagram_size != -					le16_to_cpu(max_datagram_size)) { -				err = usbnet_write_cmd(dev, -						USB_CDC_SET_MAX_DATAGRAM_SIZE, -						USB_TYPE_CLASS | USB_DIR_OUT -						 | USB_RECIP_INTERFACE, -						0, -						iface_no, &max_datagram_size, -						2); -				if (err < 0) -					pr_debug("SET_MAX_DGRAM_SIZE failed\n"); -			} -		} -	} +	/* clamp rx_max and tx_max and inform device */ +	cdc_ncm_update_rxtx_max(dev, def_rx, def_tx); -max_dgram_err: -	if (ctx->netdev->mtu != (ctx->max_datagram_size - eth_hlen)) -		ctx->netdev->mtu = ctx->max_datagram_size - eth_hlen; +	/* sanitize the modulus and remainder values */ +	cdc_ncm_fix_modulus(dev); +	/* set max datagram size */ +	cdc_ncm_set_dgram_size(dev, cdc_ncm_max_dgram_size(dev));  	return 0;  }  static void -cdc_ncm_find_endpoints(struct cdc_ncm_ctx *ctx, struct usb_interface *intf) +cdc_ncm_find_endpoints(struct usbnet *dev, struct usb_interface *intf)  { -	struct usb_host_endpoint *e; +	struct usb_host_endpoint *e, *in = NULL, *out = NULL;  	u8 ep;  	for (ep = 0; ep < intf->cur_altsetting->desc.bNumEndpoints; ep++) { @@ -303,18 +640,18 @@ cdc_ncm_find_endpoints(struct cdc_ncm_ctx *ctx, struct usb_interface *intf)  		switch (e->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {  		case USB_ENDPOINT_XFER_INT:  			if (usb_endpoint_dir_in(&e->desc)) { -				if (ctx->status_ep == NULL) -					ctx->status_ep = e; +				if (!dev->status) +					dev->status = e;  			}  			break;  		case USB_ENDPOINT_XFER_BULK:  			if (usb_endpoint_dir_in(&e->desc)) { -				if (ctx->in_ep == NULL) -					ctx->in_ep = e; +				if (!in) +					in = e;  			} else { -				if (ctx->out_ep == NULL) -					ctx->out_ep = e; +				if (!out) +					out = e;  			}  			break; @@ -322,6 +659,14 @@ cdc_ncm_find_endpoints(struct cdc_ncm_ctx *ctx, struct usb_interface *intf)  			break;  		}  	} +	if (in && !dev->in) +		dev->in = usb_rcvbulkpipe(dev->udev, +					  in->desc.bEndpointAddress & +					  USB_ENDPOINT_NUMBER_MASK); +	if (out && !dev->out) +		dev->out = usb_sndbulkpipe(dev->udev, +					   out->desc.bEndpointAddress & +					   USB_ENDPOINT_NUMBER_MASK);  }  static void cdc_ncm_free(struct cdc_ncm_ctx *ctx) @@ -342,18 +687,9 @@ static void cdc_ncm_free(struct cdc_ncm_ctx *ctx)  	kfree(ctx);  } -static const struct ethtool_ops cdc_ncm_ethtool_ops = { -	.get_drvinfo = cdc_ncm_get_drvinfo, -	.get_link = usbnet_get_link, -	.get_msglevel = usbnet_get_msglevel, -	.set_msglevel = usbnet_set_msglevel, -	.get_settings = usbnet_get_settings, -	.set_settings = usbnet_set_settings, -	.nway_reset = usbnet_nway_reset, -}; -  int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting)  { +	const struct usb_cdc_union_desc *union_desc = NULL;  	struct cdc_ncm_ctx *ctx;  	struct usb_driver *driver;  	u8 *buf; @@ -367,23 +703,22 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_  	hrtimer_init(&ctx->tx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);  	ctx->tx_timer.function = &cdc_ncm_tx_timer_cb; -	ctx->bh.data = (unsigned long)ctx; +	ctx->bh.data = (unsigned long)dev;  	ctx->bh.func = cdc_ncm_txpath_bh;  	atomic_set(&ctx->stop, 0);  	spin_lock_init(&ctx->mtx); -	ctx->netdev = dev->net;  	/* store ctx pointer in device data field */  	dev->data[0] = (unsigned long)ctx; +	/* only the control interface can be successfully probed */ +	ctx->control = intf; +  	/* get some pointers */  	driver = driver_of(intf);  	buf = intf->cur_altsetting->extra;  	len = intf->cur_altsetting->extralen; -	ctx->udev = dev->udev; -	ctx->intf = intf; -  	/* parse through descriptors associated with control interface */  	while ((len > 0) && (buf[0] > 2) && (buf[0] <= len)) { @@ -392,16 +727,18 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_  		switch (buf[2]) {  		case USB_CDC_UNION_TYPE: -			if (buf[0] < sizeof(*(ctx->union_desc))) +			if (buf[0] < sizeof(*union_desc))  				break; -			ctx->union_desc = -					(const struct usb_cdc_union_desc *)buf; - -			ctx->control = usb_ifnum_to_if(dev->udev, -					ctx->union_desc->bMasterInterface0); +			union_desc = (const struct usb_cdc_union_desc *)buf; +			/* the master must be the interface we are probing */ +			if (intf->cur_altsetting->desc.bInterfaceNumber != +			    union_desc->bMasterInterface0) { +				dev_dbg(&intf->dev, "bogus CDC Union\n"); +				goto error; +			}  			ctx->data = usb_ifnum_to_if(dev->udev, -					ctx->union_desc->bSlaveInterface0); +						    union_desc->bSlaveInterface0);  			break;  		case USB_CDC_ETHERNET_TYPE: @@ -410,13 +747,6 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_  			ctx->ether_desc =  					(const struct usb_cdc_ether_desc *)buf; -			dev->hard_mtu = -				le16_to_cpu(ctx->ether_desc->wMaxSegmentSize); - -			if (dev->hard_mtu < CDC_NCM_MIN_DATAGRAM_SIZE) -				dev->hard_mtu =	CDC_NCM_MIN_DATAGRAM_SIZE; -			else if (dev->hard_mtu > CDC_NCM_MAX_DATAGRAM_SIZE) -				dev->hard_mtu =	CDC_NCM_MAX_DATAGRAM_SIZE;  			break;  		case USB_CDC_NCM_TYPE: @@ -433,6 +763,14 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_  			ctx->mbim_desc = (const struct usb_cdc_mbim_desc *)buf;  			break; +		case USB_CDC_MBIM_EXTENDED_TYPE: +			if (buf[0] < sizeof(*(ctx->mbim_extended_desc))) +				break; + +			ctx->mbim_extended_desc = +				(const struct usb_cdc_mbim_extended_desc *)buf; +			break; +  		default:  			break;  		} @@ -444,69 +782,85 @@ advance:  	}  	/* some buggy devices have an IAD but no CDC Union */ -	if (!ctx->union_desc && intf->intf_assoc && intf->intf_assoc->bInterfaceCount == 2) { -		ctx->control = intf; +	if (!union_desc && intf->intf_assoc && intf->intf_assoc->bInterfaceCount == 2) {  		ctx->data = usb_ifnum_to_if(dev->udev, intf->cur_altsetting->desc.bInterfaceNumber + 1);  		dev_dbg(&intf->dev, "CDC Union missing - got slave from IAD\n");  	}  	/* check if we got everything */ -	if ((ctx->control == NULL) || (ctx->data == NULL) || -	    ((!ctx->mbim_desc) && ((ctx->ether_desc == NULL) || (ctx->control != intf)))) +	if (!ctx->data) { +		dev_dbg(&intf->dev, "CDC Union missing and no IAD found\n");  		goto error; +	} +	if (cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting)) { +		if (!ctx->mbim_desc) { +			dev_dbg(&intf->dev, "MBIM functional descriptor missing\n"); +			goto error; +		} +	} else { +		if (!ctx->ether_desc || !ctx->func_desc) { +			dev_dbg(&intf->dev, "NCM or ECM functional descriptors missing\n"); +			goto error; +		} +	}  	/* claim data interface, if different from control */  	if (ctx->data != ctx->control) {  		temp = usb_driver_claim_interface(driver, ctx->data, dev); -		if (temp) +		if (temp) { +			dev_dbg(&intf->dev, "failed to claim data intf\n");  			goto error; +		}  	}  	iface_no = ctx->data->cur_altsetting->desc.bInterfaceNumber;  	/* reset data interface */  	temp = usb_set_interface(dev->udev, iface_no, 0); -	if (temp) +	if (temp) { +		dev_dbg(&intf->dev, "set interface failed\n");  		goto error2; +	} -	/* initialize data interface */ -	if (cdc_ncm_setup(ctx)) +	/* initialize basic device settings */ +	if (cdc_ncm_init(dev))  		goto error2;  	/* configure data interface */  	temp = usb_set_interface(dev->udev, iface_no, data_altsetting); -	if (temp) +	if (temp) { +		dev_dbg(&intf->dev, "set interface failed\n");  		goto error2; +	} -	cdc_ncm_find_endpoints(ctx, ctx->data); -	cdc_ncm_find_endpoints(ctx, ctx->control); - -	if ((ctx->in_ep == NULL) || (ctx->out_ep == NULL) || -	    (ctx->status_ep == NULL)) +	cdc_ncm_find_endpoints(dev, ctx->data); +	cdc_ncm_find_endpoints(dev, ctx->control); +	if (!dev->in || !dev->out || !dev->status) { +		dev_dbg(&intf->dev, "failed to collect endpoints\n");  		goto error2; - -	dev->net->ethtool_ops = &cdc_ncm_ethtool_ops; +	}  	usb_set_intfdata(ctx->data, dev);  	usb_set_intfdata(ctx->control, dev); -	usb_set_intfdata(ctx->intf, dev);  	if (ctx->ether_desc) {  		temp = usbnet_get_ethernet_addr(dev, ctx->ether_desc->iMACAddress); -		if (temp) +		if (temp) { +			dev_dbg(&intf->dev, "failed to get mac address\n");  			goto error2; -		dev_info(&dev->udev->dev, "MAC-Address: %pM\n", dev->net->dev_addr); +		} +		dev_info(&intf->dev, "MAC-Address: %pM\n", dev->net->dev_addr);  	} +	/* finish setting up the device specific data */ +	cdc_ncm_setup(dev); -	dev->in = usb_rcvbulkpipe(dev->udev, -		ctx->in_ep->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); -	dev->out = usb_sndbulkpipe(dev->udev, -		ctx->out_ep->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); -	dev->status = ctx->status_ep; -	dev->rx_urb_size = ctx->rx_max; +	/* override ethtool_ops */ +	dev->net->ethtool_ops = &cdc_ncm_ethtool_ops; + +	/* add our sysfs attrs */ +	dev->net->sysfs_groups[0] = &cdc_ncm_sysfs_attr_group; -	ctx->tx_speed = ctx->rx_speed = 0;  	return 0;  error2: @@ -517,7 +871,7 @@ error2:  error:  	cdc_ncm_free((struct cdc_ncm_ctx *)dev->data[0]);  	dev->data[0] = 0; -	dev_info(&dev->udev->dev, "bind() failure\n"); +	dev_info(&intf->dev, "bind() failure\n");  	return -ENODEV;  }  EXPORT_SYMBOL_GPL(cdc_ncm_bind_common); @@ -553,15 +907,15 @@ void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf)  		ctx->control = NULL;  	} -	usb_set_intfdata(ctx->intf, NULL); +	usb_set_intfdata(intf, NULL);  	cdc_ncm_free(ctx);  }  EXPORT_SYMBOL_GPL(cdc_ncm_unbind); -/* Select the MBIM altsetting iff it is preferred and available, - * returning the number of the corresponding data interface altsetting +/* Return the number of the MBIM control interface altsetting iff it + * is preferred and available,   */ -u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf) +u8 cdc_ncm_select_altsetting(struct usb_interface *intf)  {  	struct usb_host_interface *alt; @@ -580,15 +934,15 @@ u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf)  	 *   the rules given in section 6 (USB Device Model) of this  	 *   specification."  	 */ -	if (prefer_mbim && intf->num_altsetting == 2) { +	if (intf->num_altsetting < 2) +		return intf->cur_altsetting->desc.bAlternateSetting; + +	if (prefer_mbim) {  		alt = usb_altnum_to_altsetting(intf, CDC_NCM_COMM_ALTSETTING_MBIM); -		if (alt && cdc_ncm_comm_intf_is_mbim(alt) && -		    !usb_set_interface(dev->udev, -				       intf->cur_altsetting->desc.bInterfaceNumber, -				       CDC_NCM_COMM_ALTSETTING_MBIM)) -			return CDC_NCM_DATA_ALTSETTING_MBIM; +		if (alt && cdc_ncm_comm_intf_is_mbim(alt)) +			return CDC_NCM_COMM_ALTSETTING_MBIM;  	} -	return CDC_NCM_DATA_ALTSETTING_NCM; +	return CDC_NCM_COMM_ALTSETTING_NCM;  }  EXPORT_SYMBOL_GPL(cdc_ncm_select_altsetting); @@ -597,12 +951,11 @@ static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)  	int ret;  	/* MBIM backwards compatible function? */ -	cdc_ncm_select_altsetting(dev, intf); -	if (cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting)) +	if (cdc_ncm_select_altsetting(intf) != CDC_NCM_COMM_ALTSETTING_NCM)  		return -ENODEV; -	/* NCM data altsetting is always 1 */ -	ret = cdc_ncm_bind_common(dev, intf, 1); +	/* The NCM data altsetting is fixed */ +	ret = cdc_ncm_bind_common(dev, intf, CDC_NCM_DATA_ALTSETTING_NCM);  	/*  	 * We should get an event when network connection is "connected" or @@ -645,7 +998,7 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_  	cdc_ncm_align_tail(skb, ctx->tx_ndp_modulus, 0, ctx->tx_max);  	/* verify that there is room for the NDP and the datagram (reserve) */ -	if ((ctx->tx_max - skb->len - reserve) < CDC_NCM_NDP_SIZE) +	if ((ctx->tx_max - skb->len - reserve) < ctx->max_ndp_size)  		return NULL;  	/* link to it */ @@ -655,15 +1008,16 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_  		nth16->wNdpIndex = cpu_to_le16(skb->len);  	/* push a new empty NDP */ -	ndp16 = (struct usb_cdc_ncm_ndp16 *)memset(skb_put(skb, CDC_NCM_NDP_SIZE), 0, CDC_NCM_NDP_SIZE); +	ndp16 = (struct usb_cdc_ncm_ndp16 *)memset(skb_put(skb, ctx->max_ndp_size), 0, ctx->max_ndp_size);  	ndp16->dwSignature = sign;  	ndp16->wLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_ndp16) + sizeof(struct usb_cdc_ncm_dpe16));  	return ndp16;  }  struct sk_buff * -cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign) +cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)  { +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];  	struct usb_cdc_ncm_nth16 *nth16;  	struct usb_cdc_ncm_ndp16 *ndp16;  	struct sk_buff *skb_out; @@ -683,11 +1037,11 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign)  	/* allocate a new OUT skb */  	if (!skb_out) { -		skb_out = alloc_skb((ctx->tx_max + 1), GFP_ATOMIC); +		skb_out = alloc_skb(ctx->tx_max, GFP_ATOMIC);  		if (skb_out == NULL) {  			if (skb != NULL) {  				dev_kfree_skb_any(skb); -				ctx->netdev->stats.tx_dropped++; +				dev->net->stats.tx_dropped++;  			}  			goto exit_no_skb;  		} @@ -699,6 +1053,9 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign)  		/* count total number of frames in this NTB */  		ctx->tx_curr_frame_num = 0; + +		/* recent payload counter for this skb_out */ +		ctx->tx_curr_frame_payload = 0;  	}  	for (n = ctx->tx_curr_frame_num; n < ctx->tx_max_datagrams; n++) { @@ -725,17 +1082,18 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign)  				/* won't fit, MTU problem? */  				dev_kfree_skb_any(skb);  				skb = NULL; -				ctx->netdev->stats.tx_dropped++; +				dev->net->stats.tx_dropped++;  			} else {  				/* no room for skb - store for later */  				if (ctx->tx_rem_skb != NULL) {  					dev_kfree_skb_any(ctx->tx_rem_skb); -					ctx->netdev->stats.tx_dropped++; +					dev->net->stats.tx_dropped++;  				}  				ctx->tx_rem_skb = skb;  				ctx->tx_rem_sign = sign;  				skb = NULL;  				ready2send = 1; +				ctx->tx_reason_ntb_full++;	/* count reason for transmitting */  			}  			break;  		} @@ -749,12 +1107,14 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign)  		ndp16->dpe16[index].wDatagramIndex = cpu_to_le16(skb_out->len);  		ndp16->wLength = cpu_to_le16(ndplen + sizeof(struct usb_cdc_ncm_dpe16));  		memcpy(skb_put(skb_out, skb->len), skb->data, skb->len); +		ctx->tx_curr_frame_payload += skb->len;	/* count real tx payload data */  		dev_kfree_skb_any(skb);  		skb = NULL;  		/* send now if this NDP is full */  		if (index >= CDC_NCM_DPT_DATAGRAMS_MAX) {  			ready2send = 1; +			ctx->tx_reason_ndp_full++;	/* count reason for transmitting */  			break;  		}  	} @@ -763,7 +1123,7 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign)  	if (skb != NULL) {  		dev_kfree_skb_any(skb);  		skb = NULL; -		ctx->netdev->stats.tx_dropped++; +		dev->net->stats.tx_dropped++;  	}  	ctx->tx_curr_frame_num = n; @@ -774,7 +1134,7 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign)  		ctx->tx_curr_skb = skb_out;  		goto exit_no_skb; -	} else if ((n < ctx->tx_max_datagrams) && (ready2send == 0)) { +	} else if ((n < ctx->tx_max_datagrams) && (ready2send == 0) && (ctx->timer_interval > 0)) {  		/* wait for more frames */  		/* push variables */  		ctx->tx_curr_skb = skb_out; @@ -784,23 +1144,26 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign)  		goto exit_no_skb;  	} else { +		if (n == ctx->tx_max_datagrams) +			ctx->tx_reason_max_datagram++;	/* count reason for transmitting */  		/* frame goes out */  		/* variables will be reset at next call */  	} -	/* -	 * If collected data size is less or equal CDC_NCM_MIN_TX_PKT bytes, -	 * we send buffers as it is. If we get more data, it would be more -	 * efficient for USB HS mobile device with DMA engine to receive a full -	 * size NTB, than canceling DMA transfer and receiving a short packet. +	/* If collected data size is less or equal ctx->min_tx_pkt +	 * bytes, we send buffers as it is. If we get more data, it +	 * would be more efficient for USB HS mobile device with DMA +	 * engine to receive a full size NTB, than canceling DMA +	 * transfer and receiving a short packet. +	 * +	 * This optimization support is pointless if we end up sending +	 * a ZLP after full sized NTBs.  	 */ -	if (skb_out->len > CDC_NCM_MIN_TX_PKT) -		/* final zero padding */ -		memset(skb_put(skb_out, ctx->tx_max - skb_out->len), 0, ctx->tx_max - skb_out->len); - -	/* do we need to prevent a ZLP? */ -	if (((skb_out->len % le16_to_cpu(ctx->out_ep->desc.wMaxPacketSize)) == 0) && -	    (skb_out->len < le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize)) && skb_tailroom(skb_out)) +	if (!(dev->driver_info->flags & FLAG_SEND_ZLP) && +	    skb_out->len > ctx->min_tx_pkt) +		memset(skb_put(skb_out, ctx->tx_max - skb_out->len), 0, +		       ctx->tx_max - skb_out->len); +	else if (skb_out->len < ctx->tx_max && (skb_out->len % dev->maxpacket) == 0)  		*skb_put(skb_out, 1) = 0;	/* force short packet */  	/* set final frame length */ @@ -809,12 +1172,23 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign)  	/* return skb */  	ctx->tx_curr_skb = NULL; -	ctx->netdev->stats.tx_packets += ctx->tx_curr_frame_num; +	dev->net->stats.tx_packets += ctx->tx_curr_frame_num; + +	/* keep private stats: framing overhead and number of NTBs */ +	ctx->tx_overhead += skb_out->len - ctx->tx_curr_frame_payload; +	ctx->tx_ntbs++; + +	/* usbnet has already counted all the framing overhead. +	 * Adjust the stats so that the tx_bytes counter show real +	 * payload data instead. +	 */ +	dev->net->stats.tx_bytes -= skb_out->len - ctx->tx_curr_frame_payload; +  	return skb_out;  exit_no_skb: -	/* Start timer, if there is a remaining skb */ -	if (ctx->tx_curr_skb != NULL) +	/* Start timer, if there is a remaining non-empty skb */ +	if (ctx->tx_curr_skb != NULL && n > 0)  		cdc_ncm_tx_timeout_start(ctx);  	return NULL;  } @@ -825,7 +1199,7 @@ static void cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx)  	/* start timer, if not already started */  	if (!(hrtimer_active(&ctx->tx_timer) || atomic_read(&ctx->stop)))  		hrtimer_start(&ctx->tx_timer, -				ktime_set(0, CDC_NCM_TIMER_INTERVAL), +				ktime_set(0, ctx->timer_interval),  				HRTIMER_MODE_REL);  } @@ -841,24 +1215,26 @@ static enum hrtimer_restart cdc_ncm_tx_timer_cb(struct hrtimer *timer)  static void cdc_ncm_txpath_bh(unsigned long param)  { -	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)param; +	struct usbnet *dev = (struct usbnet *)param; +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];  	spin_lock_bh(&ctx->mtx);  	if (ctx->tx_timer_pending != 0) {  		ctx->tx_timer_pending--;  		cdc_ncm_tx_timeout_start(ctx);  		spin_unlock_bh(&ctx->mtx); -	} else if (ctx->netdev != NULL) { +	} else if (dev->net != NULL) { +		ctx->tx_reason_timeout++;	/* count reason for transmitting */  		spin_unlock_bh(&ctx->mtx); -		netif_tx_lock_bh(ctx->netdev); -		usbnet_start_xmit(NULL, ctx->netdev); -		netif_tx_unlock_bh(ctx->netdev); +		netif_tx_lock_bh(dev->net); +		usbnet_start_xmit(NULL, dev->net); +		netif_tx_unlock_bh(dev->net);  	} else {  		spin_unlock_bh(&ctx->mtx);  	}  } -static struct sk_buff * +struct sk_buff *  cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)  {  	struct sk_buff *skb_out; @@ -875,7 +1251,7 @@ cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)  		goto error;  	spin_lock_bh(&ctx->mtx); -	skb_out = cdc_ncm_fill_tx_frame(ctx, skb, cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN)); +	skb_out = cdc_ncm_fill_tx_frame(dev, skb, cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN));  	spin_unlock_bh(&ctx->mtx);  	return skb_out; @@ -885,10 +1261,12 @@ error:  	return NULL;  } +EXPORT_SYMBOL_GPL(cdc_ncm_tx_fixup);  /* verify NTB header and return offset of first NDP, or negative error */  int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in)  { +	struct usbnet *dev = netdev_priv(skb_in->dev);  	struct usb_cdc_ncm_nth16 *nth16;  	int len;  	int ret = -EINVAL; @@ -898,30 +1276,33 @@ int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in)  	if (skb_in->len < (sizeof(struct usb_cdc_ncm_nth16) +  					sizeof(struct usb_cdc_ncm_ndp16))) { -		pr_debug("frame too short\n"); +		netif_dbg(dev, rx_err, dev->net, "frame too short\n");  		goto error;  	}  	nth16 = (struct usb_cdc_ncm_nth16 *)skb_in->data; -	if (le32_to_cpu(nth16->dwSignature) != USB_CDC_NCM_NTH16_SIGN) { -		pr_debug("invalid NTH16 signature <%u>\n", -					le32_to_cpu(nth16->dwSignature)); +	if (nth16->dwSignature != cpu_to_le32(USB_CDC_NCM_NTH16_SIGN)) { +		netif_dbg(dev, rx_err, dev->net, +			  "invalid NTH16 signature <%#010x>\n", +			  le32_to_cpu(nth16->dwSignature));  		goto error;  	}  	len = le16_to_cpu(nth16->wBlockLength);  	if (len > ctx->rx_max) { -		pr_debug("unsupported NTB block length %u/%u\n", len, -								ctx->rx_max); +		netif_dbg(dev, rx_err, dev->net, +			  "unsupported NTB block length %u/%u\n", len, +			  ctx->rx_max);  		goto error;  	}  	if ((ctx->rx_seq + 1) != le16_to_cpu(nth16->wSequence) && -		(ctx->rx_seq || le16_to_cpu(nth16->wSequence)) && -		!((ctx->rx_seq == 0xffff) && !le16_to_cpu(nth16->wSequence))) { -		pr_debug("sequence number glitch prev=%d curr=%d\n", -				ctx->rx_seq, le16_to_cpu(nth16->wSequence)); +	    (ctx->rx_seq || le16_to_cpu(nth16->wSequence)) && +	    !((ctx->rx_seq == 0xffff) && !le16_to_cpu(nth16->wSequence))) { +		netif_dbg(dev, rx_err, dev->net, +			  "sequence number glitch prev=%d curr=%d\n", +			  ctx->rx_seq, le16_to_cpu(nth16->wSequence));  	}  	ctx->rx_seq = le16_to_cpu(nth16->wSequence); @@ -934,18 +1315,20 @@ EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_nth16);  /* verify NDP header and return number of datagrams, or negative error */  int cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset)  { +	struct usbnet *dev = netdev_priv(skb_in->dev);  	struct usb_cdc_ncm_ndp16 *ndp16;  	int ret = -EINVAL;  	if ((ndpoffset + sizeof(struct usb_cdc_ncm_ndp16)) > skb_in->len) { -		pr_debug("invalid NDP offset  <%u>\n", ndpoffset); +		netif_dbg(dev, rx_err, dev->net, "invalid NDP offset  <%u>\n", +			  ndpoffset);  		goto error;  	}  	ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb_in->data + ndpoffset);  	if (le16_to_cpu(ndp16->wLength) < USB_CDC_NCM_NDP16_LENGTH_MIN) { -		pr_debug("invalid DPT16 length <%u>\n", -					le32_to_cpu(ndp16->dwSignature)); +		netif_dbg(dev, rx_err, dev->net, "invalid DPT16 length <%u>\n", +			  le16_to_cpu(ndp16->wLength));  		goto error;  	} @@ -954,9 +1337,9 @@ int cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset)  					sizeof(struct usb_cdc_ncm_dpe16));  	ret--; /* we process NDP entries except for the last one */ -	if ((sizeof(struct usb_cdc_ncm_ndp16) + ret * (sizeof(struct usb_cdc_ncm_dpe16))) > -								skb_in->len) { -		pr_debug("Invalid nframes = %d\n", ret); +	if ((sizeof(struct usb_cdc_ncm_ndp16) + +	     ret * (sizeof(struct usb_cdc_ncm_dpe16))) > skb_in->len) { +		netif_dbg(dev, rx_err, dev->net, "Invalid nframes = %d\n", ret);  		ret = -EINVAL;  	} @@ -965,7 +1348,7 @@ error:  }  EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_ndp16); -static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) +int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)  {  	struct sk_buff *skb;  	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; @@ -977,6 +1360,7 @@ static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)  	struct usb_cdc_ncm_dpe16 *dpe16;  	int ndpoffset;  	int loopcount = 50; /* arbitrary max preventing infinite loop */ +	u32 payload = 0;  	ndpoffset = cdc_ncm_rx_verify_nth16(ctx, skb_in);  	if (ndpoffset < 0) @@ -989,9 +1373,10 @@ next_ndp:  	ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb_in->data + ndpoffset); -	if (le32_to_cpu(ndp16->dwSignature) != USB_CDC_NCM_NDP16_NOCRC_SIGN) { -		pr_debug("invalid DPT16 signature <%u>\n", -			 le32_to_cpu(ndp16->dwSignature)); +	if (ndp16->dwSignature != cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN)) { +		netif_dbg(dev, rx_err, dev->net, +			  "invalid DPT16 signature <%#010x>\n", +			  le32_to_cpu(ndp16->dwSignature));  		goto err_ndp;  	}  	dpe16 = ndp16->dpe16; @@ -1013,21 +1398,21 @@ next_ndp:  		/* sanity checking */  		if (((offset + len) > skb_in->len) ||  				(len > ctx->rx_max) || (len < ETH_HLEN)) { -			pr_debug("invalid frame detected (ignored)" -					"offset[%u]=%u, length=%u, skb=%p\n", -					x, offset, len, skb_in); +			netif_dbg(dev, rx_err, dev->net, +				  "invalid frame detected (ignored) offset[%u]=%u, length=%u, skb=%p\n", +				  x, offset, len, skb_in);  			if (!x)  				goto err_ndp;  			break;  		} else { -			skb = skb_clone(skb_in, GFP_ATOMIC); +			/* create a fresh copy to reduce truesize */ +			skb = netdev_alloc_skb_ip_align(dev->net,  len);  			if (!skb)  				goto error; -			skb->len = len; -			skb->data = ((u8 *)skb_in->data) + offset; -			skb_set_tail_pointer(skb, len); +			memcpy(skb_put(skb, len), skb_in->data + offset, len);  			usbnet_skb_return(dev, skb); +			payload += len;	/* count payload bytes in this NTB */  		}  	}  err_ndp: @@ -1036,13 +1421,18 @@ err_ndp:  	if (ndpoffset && loopcount--)  		goto next_ndp; +	/* update stats */ +	ctx->rx_overhead += skb_in->len - payload; +	ctx->rx_ntbs++; +  	return 1;  error:  	return 0;  } +EXPORT_SYMBOL_GPL(cdc_ncm_rx_fixup);  static void -cdc_ncm_speed_change(struct cdc_ncm_ctx *ctx, +cdc_ncm_speed_change(struct usbnet *dev,  		     struct usb_cdc_speed_change *data)  {  	uint32_t rx_speed = le32_to_cpu(data->DLBitRRate); @@ -1052,25 +1442,16 @@ cdc_ncm_speed_change(struct cdc_ncm_ctx *ctx,  	 * Currently the USB-NET API does not support reporting the actual  	 * device speed. Do print it instead.  	 */ -	if ((tx_speed != ctx->tx_speed) || (rx_speed != ctx->rx_speed)) { -		ctx->tx_speed = tx_speed; -		ctx->rx_speed = rx_speed; - -		if ((tx_speed > 1000000) && (rx_speed > 1000000)) { -			printk(KERN_INFO KBUILD_MODNAME -				": %s: %u mbit/s downlink " -				"%u mbit/s uplink\n", -				ctx->netdev->name, -				(unsigned int)(rx_speed / 1000000U), -				(unsigned int)(tx_speed / 1000000U)); -		} else { -			printk(KERN_INFO KBUILD_MODNAME -				": %s: %u kbit/s downlink " -				"%u kbit/s uplink\n", -				ctx->netdev->name, -				(unsigned int)(rx_speed / 1000U), -				(unsigned int)(tx_speed / 1000U)); -		} +	if ((tx_speed > 1000000) && (rx_speed > 1000000)) { +		netif_info(dev, link, dev->net, +			   "%u mbit/s downlink %u mbit/s uplink\n", +			   (unsigned int)(rx_speed / 1000000U), +			   (unsigned int)(tx_speed / 1000000U)); +	} else { +		netif_info(dev, link, dev->net, +			   "%u kbit/s downlink %u kbit/s uplink\n", +			   (unsigned int)(rx_speed / 1000U), +			   (unsigned int)(tx_speed / 1000U));  	}  } @@ -1086,7 +1467,7 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb)  	/* test for split data in 8-byte chunks */  	if (test_and_clear_bit(EVENT_STS_SPLIT, &dev->flags)) { -		cdc_ncm_speed_change(ctx, +		cdc_ncm_speed_change(dev,  		      (struct usb_cdc_speed_change *)urb->transfer_buffer);  		return;  	} @@ -1100,15 +1481,10 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb)  		 * USB_CDC_NOTIFY_NETWORK_CONNECTION notification shall be  		 * sent by device after USB_CDC_NOTIFY_SPEED_CHANGE.  		 */ -		ctx->connected = le16_to_cpu(event->wValue); - -		printk(KERN_INFO KBUILD_MODNAME ": %s: network connection:" -			" %sconnected\n", -			ctx->netdev->name, ctx->connected ? "" : "dis"); - -		usbnet_link_change(dev, ctx->connected, 0); -		if (!ctx->connected) -			ctx->tx_speed = ctx->rx_speed = 0; +		netif_info(dev, link, dev->net, +			   "network connection: %sconnected\n", +			   !!event->wValue ? "" : "dis"); +		usbnet_link_change(dev, !!event->wValue, 0);  		break;  	case USB_CDC_NOTIFY_SPEED_CHANGE: @@ -1116,8 +1492,8 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb)  					sizeof(struct usb_cdc_speed_change)))  			set_bit(EVENT_STS_SPLIT, &dev->flags);  		else -			cdc_ncm_speed_change(ctx, -				(struct usb_cdc_speed_change *) &event[1]); +			cdc_ncm_speed_change(dev, +					     (struct usb_cdc_speed_change *)&event[1]);  		break;  	default: @@ -1128,39 +1504,11 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb)  	}  } -static int cdc_ncm_check_connect(struct usbnet *dev) -{ -	struct cdc_ncm_ctx *ctx; - -	ctx = (struct cdc_ncm_ctx *)dev->data[0]; -	if (ctx == NULL) -		return 1;	/* disconnected */ - -	return !ctx->connected; -} - -static int -cdc_ncm_probe(struct usb_interface *udev, const struct usb_device_id *prod) -{ -	return usbnet_probe(udev, prod); -} - -static void cdc_ncm_disconnect(struct usb_interface *intf) -{ -	struct usbnet *dev = usb_get_intfdata(intf); - -	if (dev == NULL) -		return;		/* already disconnected */ - -	usbnet_disconnect(intf); -} -  static const struct driver_info cdc_ncm_info = {  	.description = "CDC NCM",  	.flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT | FLAG_MULTI_PACKET,  	.bind = cdc_ncm_bind,  	.unbind = cdc_ncm_unbind, -	.check_connect = cdc_ncm_check_connect,  	.manage_power = usbnet_manage_power,  	.status = cdc_ncm_status,  	.rx_fixup = cdc_ncm_rx_fixup, @@ -1174,7 +1522,6 @@ static const struct driver_info wwan_info = {  			| FLAG_WWAN,  	.bind = cdc_ncm_bind,  	.unbind = cdc_ncm_unbind, -	.check_connect = cdc_ncm_check_connect,  	.manage_power = usbnet_manage_power,  	.status = cdc_ncm_status,  	.rx_fixup = cdc_ncm_rx_fixup, @@ -1188,7 +1535,6 @@ static const struct driver_info wwan_noarp_info = {  			| FLAG_WWAN | FLAG_NOARP,  	.bind = cdc_ncm_bind,  	.unbind = cdc_ncm_unbind, -	.check_connect = cdc_ncm_check_connect,  	.manage_power = usbnet_manage_power,  	.status = cdc_ncm_status,  	.rx_fixup = cdc_ncm_rx_fixup, @@ -1234,17 +1580,6 @@ static const struct usb_device_id cdc_devs[] = {  	  .driver_info = (unsigned long)&wwan_info,  	}, -	/* Huawei NCM devices disguised as vendor specific */ -	{ USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x16), -	  .driver_info = (unsigned long)&wwan_info, -	}, -	{ USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x46), -	  .driver_info = (unsigned long)&wwan_info, -	}, -	{ USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x76), -	  .driver_info = (unsigned long)&wwan_info, -	}, -  	/* Infineon(now Intel) HSPA Modem platform */  	{ USB_DEVICE_AND_INTERFACE_INFO(0x1519, 0x0443,  		USB_CLASS_COMM, @@ -1265,8 +1600,8 @@ MODULE_DEVICE_TABLE(usb, cdc_devs);  static struct usb_driver cdc_ncm_driver = {  	.name = "cdc_ncm",  	.id_table = cdc_devs, -	.probe = cdc_ncm_probe, -	.disconnect = cdc_ncm_disconnect, +	.probe = usbnet_probe, +	.disconnect = usbnet_disconnect,  	.suspend = usbnet_suspend,  	.resume = usbnet_resume,  	.reset_resume =	usbnet_resume,  | 
