Merge branch 'cdc_ncm'

Bjørn Mork says:

====================
cdc_ncm: many small and mostly trivial fixes

This series ended up longer than expected, and it is still not
complete. There is more to come when time allows...

Most changes are trivial. Notable non-trivial changes are
 - removed filtering of identical speed notifications
 - tx_max calulation is changed to count the pad byte if
   necessary, and respect the device limit as an absolute
   upper limit even if it is too low according to the spec
 - remove the bug preventing SET_MAX_DATAGRAM_SIZE from having
   any effect
 - drop the pad-to-max if ZLPs are enabled
 - the driver specific VERSION is dropped
 - dev->hard_mtu is set to tx_max instead of max_datagram_size
   causing usbnet to calculate the qlen based on the real max
   size of tx skbs

This series has been tested, along with the previously posted
cdc_mbim series, on the NCM and MBIM devices I have:
 - Ericsson F5521gw (NCM)
 - Huawei E367 (MBIM)
 - D-Link DWM-156 A7 (MBIM w/ too low dwNtb{In,Out}MaxSize bug)
 - Sierra Wireless MC7710 (MBIM w/ ZLP and CDC Union bugs)

Apart from the D-Link modem dropping a lot less oversized
frames with the fix dedicated to it, there are no end user
noticable functional changes as a result of this series.  But
all the non-trivial changes I listed above are of course
detectable by users looking at that specific area (except maybe
the removed speed notification, which requires a device sending
duplicates to be noticable - I don't have any such device).
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2013-11-02 02:02:19 -04:00
commit f99f4f8b09
3 changed files with 233 additions and 289 deletions

View file

@ -175,7 +175,7 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb
}
spin_lock_bh(&ctx->mtx);
skb_out = cdc_ncm_fill_tx_frame(ctx, skb, sign);
skb_out = cdc_ncm_fill_tx_frame(dev, skb, sign);
spin_unlock_bh(&ctx->mtx);
return skb_out;

View file

@ -53,8 +53,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,71 +66,67 @@ 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 usbnet *dev = netdev_priv(net);
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));
}
static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)
static u8 cdc_ncm_setup(struct usbnet *dev)
{
struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
struct usb_cdc_ncm_ntb_parameters ncm_parm;
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);
__le16 max_datagram_size;
iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber;
err = usbnet_read_cmd(dev, USB_CDC_GET_NTB_PARAMETERS,
USB_TYPE_CLASS | USB_DIR_IN
|USB_RECIP_INTERFACE,
0, iface_no, &ctx->ncm_parm,
sizeof(ctx->ncm_parm));
0, iface_no, &ncm_parm,
sizeof(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 */
}
/* read correct set of parameters according to device mode */
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);
ctx->tx_modulus = le16_to_cpu(ctx->ncm_parm.wNdpOutDivisor);
ctx->tx_ndp_modulus = le16_to_cpu(ctx->ncm_parm.wNdpOutAlignment);
ctx->rx_max = le32_to_cpu(ncm_parm.dwNtbInMaxSize);
ctx->tx_max = le32_to_cpu(ncm_parm.dwNtbOutMaxSize);
ctx->tx_remainder = le16_to_cpu(ncm_parm.wNdpOutPayloadRemainder);
ctx->tx_modulus = le16_to_cpu(ncm_parm.wNdpOutDivisor);
ctx->tx_ndp_modulus = le16_to_cpu(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);
ctx->tx_max_datagrams = le16_to_cpu(ncm_parm.wNtbOutMaxDatagrams);
ntb_fmt_supported = le16_to_cpu(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;
/* there are some minor differences in NCM and MBIM defaults */
if (cdc_ncm_comm_intf_is_mbim(ctx->control->cur_altsetting)) {
if (!ctx->mbim_desc)
return -EINVAL;
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;
flags = ctx->mbim_desc->bmNetworkCapabilities;
ctx->max_datagram_size = le16_to_cpu(ctx->mbim_desc->wMaxSegmentSize);
if (ctx->max_datagram_size < CDC_MBIM_MIN_DATAGRAM_SIZE)
ctx->max_datagram_size = CDC_MBIM_MIN_DATAGRAM_SIZE;
} else {
flags = 0;
if (!ctx->func_desc)
return -EINVAL;
eth_hlen = ETH_HLEN;
flags = ctx->func_desc->bmNetworkCapabilities;
ctx->max_datagram_size = le16_to_cpu(ctx->ether_desc->wMaxSegmentSize);
if (ctx->max_datagram_size < CDC_NCM_MIN_DATAGRAM_SIZE)
ctx->max_datagram_size = CDC_NCM_MIN_DATAGRAM_SIZE;
}
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);
/* common absolute max for NCM and MBIM */
if (ctx->max_datagram_size > CDC_NCM_MAX_DATAGRAM_SIZE)
ctx->max_datagram_size = CDC_NCM_MAX_DATAGRAM_SIZE;
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, flags);
/* max count of tx datagrams */
if ((ctx->tx_max_datagrams == 0) ||
@ -141,19 +135,19 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)
/* 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);
dev_dbg(&dev->intf->dev, "Using min receive length=%d\n",
USB_CDC_NCM_NTB_MIN_IN_SIZE);
ctx->rx_max = USB_CDC_NCM_NTB_MIN_IN_SIZE;
}
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);
dev_dbg(&dev->intf->dev, "Using default maximum receive length=%d\n",
CDC_NCM_NTB_MAX_SIZE_RX);
ctx->rx_max = CDC_NCM_NTB_MAX_SIZE_RX;
}
/* inform device about NTB input size changes */
if (ctx->rx_max != le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize)) {
if (ctx->rx_max != le32_to_cpu(ncm_parm.dwNtbInMaxSize)) {
__le32 dwNtbInMaxSize = cpu_to_le32(ctx->rx_max);
err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_INPUT_SIZE,
@ -161,16 +155,22 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)
| USB_RECIP_INTERFACE,
0, iface_no, &dwNtbInMaxSize, 4);
if (err < 0)
pr_debug("Setting NTB Input Size failed\n");
dev_dbg(&dev->intf->dev, "Setting NTB Input Size failed\n");
}
/* 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);
if (ctx->tx_max > CDC_NCM_NTB_MAX_SIZE_TX) {
dev_dbg(&dev->intf->dev, "Using default maximum transmit length=%d\n",
CDC_NCM_NTB_MAX_SIZE_TX);
ctx->tx_max = CDC_NCM_NTB_MAX_SIZE_TX;
/* Adding a pad byte here simplifies the handling in
* cdc_ncm_fill_tx_frame, by making tx_max always
* represent the real skb max size.
*/
if (ctx->tx_max % usb_maxpacket(dev->udev, dev->out, 1) == 0)
ctx->tx_max++;
}
/*
@ -183,7 +183,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,13 +197,13 @@ 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;
}
@ -221,7 +221,7 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)
USB_CDC_NCM_CRC_NOT_APPENDED,
iface_no, NULL, 0);
if (err < 0)
pr_debug("Setting CRC mode off failed\n");
dev_dbg(&dev->intf->dev, "Setting CRC mode off failed\n");
}
/* set NTB format, if both formats are supported */
@ -232,69 +232,43 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)
USB_CDC_NCM_NTB16_FORMAT,
iface_no, NULL, 0);
if (err < 0)
pr_debug("Setting NTB format to 16-bit failed\n");
dev_dbg(&dev->intf->dev, "Setting NTB format to 16-bit failed\n");
}
ctx->max_datagram_size = min_dgram_size;
/* inform the device about the selected Max Datagram Size */
if (!(flags & USB_CDC_NCM_NCAP_MAX_DATAGRAM_SIZE))
goto out;
/* 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");
}
}
/* 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;
}
max_dgram_err:
if (ctx->netdev->mtu != (ctx->max_datagram_size - eth_hlen))
ctx->netdev->mtu = ctx->max_datagram_size - eth_hlen;
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 */
if (dev->net->mtu > ctx->max_datagram_size - eth_hlen)
dev->net->mtu = ctx->max_datagram_size - eth_hlen;
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 +277,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 +296,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 +324,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 +340,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 +364,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 +384,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:
@ -444,69 +411,71 @@ 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 || (!ctx->mbim_desc && !ctx->ether_desc)) {
dev_dbg(&intf->dev, "CDC 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)
goto error2;
/* initialize data interface */
if (cdc_ncm_setup(ctx))
if (temp) {
dev_dbg(&intf->dev, "set interface failed\n");
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;
/* initialize data interface */
if (cdc_ncm_setup(dev)) {
dev_dbg(&intf->dev, "cdc_ncm_setup() failed\n");
goto error2;
}
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);
}
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;
/* usbnet use these values for sizing tx/rx queues */
dev->hard_mtu = ctx->tx_max;
dev->rx_urb_size = ctx->rx_max;
ctx->tx_speed = ctx->rx_speed = 0;
return 0;
error2:
@ -517,7 +486,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,7 +522,7 @@ 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);
@ -662,8 +631,9 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_
}
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 +653,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;
}
@ -725,12 +695,12 @@ 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;
@ -763,7 +733,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;
@ -788,19 +758,20 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign)
/* 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 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.
*
* 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 > CDC_NCM_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 % dev->maxpacket) == 0)
*skb_put(skb_out, 1) = 0; /* force short packet */
/* set final frame length */
@ -809,7 +780,7 @@ 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;
return skb_out;
exit_no_skb:
@ -841,18 +812,19 @@ 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) {
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);
}
@ -875,7 +847,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;
@ -889,6 +861,7 @@ error:
/* 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 +871,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 +910,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 +932,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;
}
@ -989,9 +967,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,9 +992,9 @@ 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;
@ -1042,7 +1021,7 @@ error:
}
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 +1031,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 +1056,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;
}
@ -1101,14 +1071,10 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb)
* 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");
netif_info(dev, link, dev->net,
"network connection: %sconnected\n",
ctx->connected ? "" : "dis");
usbnet_link_change(dev, ctx->connected, 0);
if (!ctx->connected)
ctx->tx_speed = ctx->rx_speed = 0;
break;
case USB_CDC_NOTIFY_SPEED_CHANGE:
@ -1116,8 +1082,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:
@ -1139,22 +1105,6 @@ static int cdc_ncm_check_connect(struct usbnet *dev)
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,
@ -1265,8 +1215,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,

View file

@ -36,6 +36,9 @@
* SUCH DAMAGE.
*/
#ifndef __LINUX_USB_CDC_NCM_H
#define __LINUX_USB_CDC_NCM_H
#define CDC_NCM_COMM_ALTSETTING_NCM 0
#define CDC_NCM_COMM_ALTSETTING_MBIM 1
@ -85,22 +88,13 @@
#define cdc_ncm_data_intf_is_mbim(x) ((x)->desc.bInterfaceProtocol == USB_CDC_MBIM_PROTO_NTB)
struct cdc_ncm_ctx {
struct usb_cdc_ncm_ntb_parameters ncm_parm;
struct hrtimer tx_timer;
struct tasklet_struct bh;
const struct usb_cdc_ncm_desc *func_desc;
const struct usb_cdc_mbim_desc *mbim_desc;
const struct usb_cdc_header_desc *header_desc;
const struct usb_cdc_union_desc *union_desc;
const struct usb_cdc_mbim_desc *mbim_desc;
const struct usb_cdc_ether_desc *ether_desc;
struct net_device *netdev;
struct usb_device *udev;
struct usb_host_endpoint *in_ep;
struct usb_host_endpoint *out_ep;
struct usb_host_endpoint *status_ep;
struct usb_interface *intf;
struct usb_interface *control;
struct usb_interface *data;
@ -113,8 +107,6 @@ struct cdc_ncm_ctx {
u32 tx_timer_pending;
u32 tx_curr_frame_num;
u32 rx_speed;
u32 tx_speed;
u32 rx_max;
u32 tx_max;
u32 max_datagram_size;
@ -127,9 +119,11 @@ struct cdc_ncm_ctx {
u16 connected;
};
extern u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf);
extern int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting);
extern void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf);
extern struct sk_buff *cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign);
extern int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in);
extern int cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset);
u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf);
int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting);
void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf);
struct sk_buff *cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign);
int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in);
int cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset);
#endif /* __LINUX_USB_CDC_NCM_H */