From dad957d7386873c600a365a9d0dcc7cc73f36883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Mon, 22 Oct 2012 10:56:28 +0000 Subject: net: cdc_ncm: workaround NTB input size firmware bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some devices do not support the 8 byte variants of the NTB input size control messages despite announcing such support in their NCM or MBIM functional descriptor. According to the NCM specification, all devices must support the 4 byte variant regardless of whether or not the flag is set: If bit D5 is set in the bmNetworkCapabilities field of function’s NCM Functional Descriptor, the host may set wLength either to 4 or to 8. If wLength is 4, the function shall assume that wNtbInMaxDatagrams is to be set to zero. If wLength is 8, then the function shall use the provided value as the limit. The function shall return an error response (a STALL PID) if wLength is set to any other value. We do not set wNtbInMaxDatagrams in any case, so we can just as well unconditionally use the 4 byte variant without losing any functionality. This works around the known firmware bug, and simplifies the code considerably. Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ncm.c | 46 +++++++++++++--------------------------------- 1 file changed, 13 insertions(+), 33 deletions(-) (limited to 'drivers/net/usb/cdc_ncm.c') diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 4cd582a4f625..32bdf542be1c 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -215,41 +215,21 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx) /* inform device about NTB input size changes */ if (ctx->rx_max != le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize)) { + __le32 *dwNtbInMaxSize; - if (flags & USB_CDC_NCM_NCAP_NTB_INPUT_SIZE) { - struct usb_cdc_ncm_ndp_input_size *ndp_in_sz; - - ndp_in_sz = kzalloc(sizeof(*ndp_in_sz), GFP_KERNEL); - if (!ndp_in_sz) { - err = -ENOMEM; - goto size_err; - } - - err = usb_control_msg(ctx->udev, - usb_sndctrlpipe(ctx->udev, 0), - USB_CDC_SET_NTB_INPUT_SIZE, - USB_TYPE_CLASS | USB_DIR_OUT - | USB_RECIP_INTERFACE, - 0, iface_no, ndp_in_sz, 8, 1000); - kfree(ndp_in_sz); - } else { - __le32 *dwNtbInMaxSize; - dwNtbInMaxSize = kzalloc(sizeof(*dwNtbInMaxSize), - GFP_KERNEL); - if (!dwNtbInMaxSize) { - err = -ENOMEM; - goto size_err; - } - *dwNtbInMaxSize = cpu_to_le32(ctx->rx_max); - - err = usb_control_msg(ctx->udev, - usb_sndctrlpipe(ctx->udev, 0), - USB_CDC_SET_NTB_INPUT_SIZE, - USB_TYPE_CLASS | USB_DIR_OUT - | USB_RECIP_INTERFACE, - 0, iface_no, dwNtbInMaxSize, 4, 1000); - kfree(dwNtbInMaxSize); + dwNtbInMaxSize = kzalloc(sizeof(*dwNtbInMaxSize), GFP_KERNEL); + if (!dwNtbInMaxSize) { + err = -ENOMEM; + goto size_err; } + *dwNtbInMaxSize = cpu_to_le32(ctx->rx_max); + err = usb_control_msg(ctx->udev, + usb_sndctrlpipe(ctx->udev, 0), + USB_CDC_SET_NTB_INPUT_SIZE, + USB_TYPE_CLASS | USB_DIR_OUT + | USB_RECIP_INTERFACE, + 0, iface_no, dwNtbInMaxSize, 4, 1000); + kfree(dwNtbInMaxSize); size_err: if (err < 0) pr_debug("Setting NTB Input Size failed\n"); -- cgit v1.2.3 From 2d1c4310774156204c3faff0db583337caeffcc4 Mon Sep 17 00:00:00 2001 From: Greg Suarez Date: Mon, 22 Oct 2012 10:56:30 +0000 Subject: net: cdc_ncm: adding MBIM support to ncm_setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MBIM and NCM are very similar, so we can reuse most of the setup and bind logic in cdc_ncm for CDC MBIM devices. Handle a few minor differences in ncm_setup. Signed-off-by: Greg Suarez Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ncm.c | 50 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 15 deletions(-) (limited to 'drivers/net/usb/cdc_ncm.c') diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 32bdf542be1c..77c6728d875f 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -64,6 +64,9 @@ /* Minimum value for MaxDatagramSize, ch. 6.2.9 */ #define CDC_NCM_MIN_DATAGRAM_SIZE 1514 /* bytes */ +/* Minimum value for MaxDatagramSize, ch. 8.1.3 */ +#define CDC_MBIM_MIN_DATAGRAM_SIZE 2048 /* bytes */ + #define CDC_NCM_MIN_TX_PKT 512 /* bytes */ /* Default value for MaxDatagramSize */ @@ -98,6 +101,7 @@ struct cdc_ncm_ctx { 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_ether_desc *ether_desc; @@ -158,7 +162,10 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx) u8 flags; u8 iface_no; int err; + int eth_hlen; u16 ntb_fmt_supported; + u32 min_dgram_size; + u32 min_hdr_size; iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber; @@ -184,10 +191,19 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx) ctx->tx_max_datagrams = le16_to_cpu(ctx->ncm_parm.wNtbOutMaxDatagrams); ntb_fmt_supported = le16_to_cpu(ctx->ncm_parm.bmNtbFormatsSupported); - if (ctx->func_desc != NULL) + 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 + } else { flags = 0; + } pr_debug("dwNtbInMaxSize=%u dwNtbOutMaxSize=%u " "wNdpOutPayloadRemainder=%u wNdpOutDivisor=%u " @@ -237,7 +253,7 @@ size_err: /* verify maximum size of transmitted NTB in bytes */ if ((ctx->tx_max < - (CDC_NCM_MIN_HDR_SIZE + CDC_NCM_MIN_DATAGRAM_SIZE)) || + (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); @@ -279,8 +295,8 @@ size_err: } /* adjust TX-remainder according to NCM specification. */ - ctx->tx_remainder = ((ctx->tx_remainder - ETH_HLEN) & - (ctx->tx_modulus - 1)); + ctx->tx_remainder = ((ctx->tx_remainder - eth_hlen) & + (ctx->tx_modulus - 1)); /* additional configuration */ @@ -307,12 +323,18 @@ size_err: pr_debug("Setting NTB format to 16-bit failed\n"); } - ctx->max_datagram_size = CDC_NCM_MIN_DATAGRAM_SIZE; + ctx->max_datagram_size = min_dgram_size; /* set Max Datagram Size (MTU) */ if (flags & USB_CDC_NCM_NCAP_MAX_DATAGRAM_SIZE) { __le16 *max_datagram_size; - u16 eth_max_sz = le16_to_cpu(ctx->ether_desc->wMaxSegmentSize); + 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; max_datagram_size = kzalloc(sizeof(*max_datagram_size), GFP_KERNEL); @@ -329,7 +351,7 @@ size_err: 2, 1000); if (err < 0) { pr_debug("GET_MAX_DATAGRAM_SIZE failed, use size=%u\n", - CDC_NCM_MIN_DATAGRAM_SIZE); + min_dgram_size); } else { ctx->max_datagram_size = le16_to_cpu(*max_datagram_size); @@ -338,12 +360,10 @@ size_err: 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; + ctx->max_datagram_size = CDC_NCM_MAX_DATAGRAM_SIZE; - if (ctx->max_datagram_size < CDC_NCM_MIN_DATAGRAM_SIZE) - ctx->max_datagram_size = - CDC_NCM_MIN_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 != @@ -364,8 +384,8 @@ size_err: } max_dgram_err: - if (ctx->netdev->mtu != (ctx->max_datagram_size - ETH_HLEN)) - ctx->netdev->mtu = ctx->max_datagram_size - ETH_HLEN; + if (ctx->netdev->mtu != (ctx->max_datagram_size - eth_hlen)) + ctx->netdev->mtu = ctx->max_datagram_size - eth_hlen; return 0; } -- cgit v1.2.3 From 38396e4c2984bf87283c2cda2c6aefa5876639dd Mon Sep 17 00:00:00 2001 From: Greg Suarez Date: Mon, 22 Oct 2012 10:56:31 +0000 Subject: net: cdc_ncm: refactor bind preparing for MBIM support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NCM and MBIM can share most of the bind function. Split out the shareable part and add MBIM functional descriptor parsing. Signed-off-by: Greg Suarez Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ncm.c | 47 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 14 deletions(-) (limited to 'drivers/net/usb/cdc_ncm.c') diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 77c6728d875f..643e643392fe 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -451,7 +451,7 @@ static const struct ethtool_ops cdc_ncm_ethtool_ops = { .nway_reset = usbnet_nway_reset, }; -static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf) +static int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting) { struct cdc_ncm_ctx *ctx; struct usb_driver *driver; @@ -525,6 +525,13 @@ static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf) ctx->func_desc = (const struct usb_cdc_ncm_desc *)buf; break; + case USB_CDC_MBIM_TYPE: + if (buf[0] < sizeof(*(ctx->mbim_desc))) + break; + + ctx->mbim_desc = (const struct usb_cdc_mbim_desc *)buf; + break; + default: break; } @@ -537,7 +544,7 @@ advance: /* check if we got everything */ if ((ctx->control == NULL) || (ctx->data == NULL) || - (ctx->ether_desc == NULL) || (ctx->control != intf)) + ((!ctx->mbim_desc) && ((ctx->ether_desc == NULL) || (ctx->control != intf)))) goto error; /* claim interfaces, if any */ @@ -557,7 +564,7 @@ advance: goto error2; /* configure data interface */ - temp = usb_set_interface(dev->udev, iface_no, 1); + temp = usb_set_interface(dev->udev, iface_no, data_altsetting); if (temp) goto error2; @@ -574,11 +581,13 @@ advance: usb_set_intfdata(ctx->control, dev); usb_set_intfdata(ctx->intf, dev); - temp = usbnet_get_ethernet_addr(dev, ctx->ether_desc->iMACAddress); - if (temp) - goto error2; + if (ctx->ether_desc) { + temp = usbnet_get_ethernet_addr(dev, ctx->ether_desc->iMACAddress); + if (temp) + goto error2; + dev_info(&dev->udev->dev, "MAC-Address: %pM\n", dev->net->dev_addr); + } - dev_info(&dev->udev->dev, "MAC-Address: %pM\n", dev->net->dev_addr); dev->in = usb_rcvbulkpipe(dev->udev, ctx->in_ep->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); @@ -587,13 +596,6 @@ advance: dev->status = ctx->status_ep; dev->rx_urb_size = ctx->rx_max; - /* - * We should get an event when network connection is "connected" or - * "disconnected". Set network connection in "disconnected" state - * (carrier is OFF) during attach, so the IP network stack does not - * start IPv6 negotiation and more. - */ - netif_carrier_off(dev->net); ctx->tx_speed = ctx->rx_speed = 0; return 0; @@ -639,6 +641,23 @@ static void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf) cdc_ncm_free(ctx); } +static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf) +{ + int ret; + + /* NCM data altsetting is always 1 */ + ret = cdc_ncm_bind_common(dev, intf, 1); + + /* + * We should get an event when network connection is "connected" or + * "disconnected". Set network connection in "disconnected" state + * (carrier is OFF) during attach, so the IP network stack does not + * start IPv6 negotiation and more. + */ + netif_carrier_off(dev->net); + return ret; +} + static void cdc_ncm_zero_fill(u8 *ptr, u32 first, u32 end, u32 max) { if (first >= max) -- cgit v1.2.3 From 75d67d354f7c9bcaaee3dd30a627d8e34d068205 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Mon, 22 Oct 2012 10:56:32 +0000 Subject: net: cdc_ncm: process chained NDPs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The NCM 1.0 spefication makes provisions for linking more than one NDP into a single NTB. This is important for MBIM support, where these NDPs might be of different types. Following the chain of NDPs is also correct for NCM, and will not change anything in the common case where there is only one NDP Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ncm.c | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) (limited to 'drivers/net/usb/cdc_ncm.c') diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 643e643392fe..34069346544f 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -977,6 +977,8 @@ static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) struct usb_cdc_ncm_nth16 *nth16; struct usb_cdc_ncm_ndp16 *ndp16; struct usb_cdc_ncm_dpe16 *dpe16; + int ndpoffset; + int loopcount = 50; /* arbitrary max preventing infinite loop */ if (ctx == NULL) goto error; @@ -1010,25 +1012,24 @@ static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) } ctx->rx_seq = le16_to_cpu(nth16->wSequence); - len = le16_to_cpu(nth16->wNdpIndex); - if ((len + sizeof(struct usb_cdc_ncm_ndp16)) > skb_in->len) { - pr_debug("invalid DPT16 index <%u>\n", - le16_to_cpu(nth16->wNdpIndex)); + ndpoffset = le16_to_cpu(nth16->wNdpIndex); +next_ndp: + if ((ndpoffset + sizeof(struct usb_cdc_ncm_ndp16)) > skb_in->len) { + pr_debug("invalid NDP offset <%u>\n", ndpoffset); goto error; } - - ndp16 = (struct usb_cdc_ncm_ndp16 *)(((u8 *)skb_in->data) + len); + 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)); - goto error; + goto err_ndp; } if (le16_to_cpu(ndp16->wLength) < USB_CDC_NCM_NDP16_LENGTH_MIN) { pr_debug("invalid DPT16 length <%u>\n", le32_to_cpu(ndp16->dwSignature)); - goto error; + goto err_ndp; } nframes = ((le16_to_cpu(ndp16->wLength) - @@ -1036,15 +1037,15 @@ static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) sizeof(struct usb_cdc_ncm_dpe16)); nframes--; /* we process NDP entries except for the last one */ - len += sizeof(struct usb_cdc_ncm_ndp16); + ndpoffset += sizeof(struct usb_cdc_ncm_ndp16); - if ((len + nframes * (sizeof(struct usb_cdc_ncm_dpe16))) > + if ((ndpoffset + nframes * (sizeof(struct usb_cdc_ncm_dpe16))) > skb_in->len) { pr_debug("Invalid nframes = %d\n", nframes); - goto error; + goto err_ndp; } - dpe16 = (struct usb_cdc_ncm_dpe16 *)(((u8 *)skb_in->data) + len); + dpe16 = (struct usb_cdc_ncm_dpe16 *)(skb_in->data + ndpoffset); for (x = 0; x < nframes; x++, dpe16++) { offset = le16_to_cpu(dpe16->wDatagramIndex); @@ -1056,7 +1057,7 @@ static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) */ if ((offset == 0) || (len == 0)) { if (!x) - goto error; /* empty NTB */ + goto err_ndp; /* empty NTB */ break; } @@ -1067,7 +1068,7 @@ static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) "offset[%u]=%u, length=%u, skb=%p\n", x, offset, len, skb_in); if (!x) - goto error; + goto err_ndp; break; } else { @@ -1080,6 +1081,12 @@ static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) usbnet_skb_return(dev, skb); } } +err_ndp: + /* are there more NDPs to process? */ + ndpoffset = le16_to_cpu(ndp16->wNextNdpIndex); + if (ndpoffset && loopcount--) + goto next_ndp; + return 1; error: return 0; -- cgit v1.2.3 From ff06ab13a4ccae4acb44a2d4e3ece367b616ab50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Mon, 22 Oct 2012 10:56:33 +0000 Subject: net: cdc_ncm: splitting rx_fixup for code reuse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Verifying and handling received MBIM and NCM frames will need to be different in three areas: - verifying the NDP signature - checking valid datagram length - datagram header manipulation This makes it inconvenient to share rx_fixup in whole. But some verification parts are common. Split these out in separate functions. Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ncm.c | 83 +++++++++++++++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 28 deletions(-) (limited to 'drivers/net/usb/cdc_ncm.c') diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 34069346544f..6688a15e044d 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -966,19 +966,12 @@ error: return NULL; } -static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) +/* verify NTB header and return offset of first NDP, or negative error */ +static int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in) { - struct sk_buff *skb; - struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; - int len; - int nframes; - int x; - int offset; struct usb_cdc_ncm_nth16 *nth16; - struct usb_cdc_ncm_ndp16 *ndp16; - struct usb_cdc_ncm_dpe16 *dpe16; - int ndpoffset; - int loopcount = 50; /* arbitrary max preventing infinite loop */ + int len; + int ret = -EINVAL; if (ctx == NULL) goto error; @@ -1012,40 +1005,74 @@ static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) } ctx->rx_seq = le16_to_cpu(nth16->wSequence); - ndpoffset = le16_to_cpu(nth16->wNdpIndex); -next_ndp: + ret = le16_to_cpu(nth16->wNdpIndex); +error: + return ret; +} + +/* verify NDP header and return number of datagrams, or negative error */ +static int cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset) +{ + 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); goto error; } 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)); - goto err_ndp; - } - if (le16_to_cpu(ndp16->wLength) < USB_CDC_NCM_NDP16_LENGTH_MIN) { pr_debug("invalid DPT16 length <%u>\n", le32_to_cpu(ndp16->dwSignature)); - goto err_ndp; + goto error; } - nframes = ((le16_to_cpu(ndp16->wLength) - + ret = ((le16_to_cpu(ndp16->wLength) - sizeof(struct usb_cdc_ncm_ndp16)) / sizeof(struct usb_cdc_ncm_dpe16)); - nframes--; /* we process NDP entries except for the last one */ - - ndpoffset += sizeof(struct usb_cdc_ncm_ndp16); + ret--; /* we process NDP entries except for the last one */ - if ((ndpoffset + nframes * (sizeof(struct usb_cdc_ncm_dpe16))) > + if ((sizeof(struct usb_cdc_ncm_ndp16) + ret * (sizeof(struct usb_cdc_ncm_dpe16))) > skb_in->len) { - pr_debug("Invalid nframes = %d\n", nframes); - goto err_ndp; + pr_debug("Invalid nframes = %d\n", ret); + ret = -EINVAL; } - dpe16 = (struct usb_cdc_ncm_dpe16 *)(skb_in->data + ndpoffset); +error: + return ret; +} + +static 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]; + int len; + int nframes; + int x; + int offset; + struct usb_cdc_ncm_ndp16 *ndp16; + struct usb_cdc_ncm_dpe16 *dpe16; + int ndpoffset; + int loopcount = 50; /* arbitrary max preventing infinite loop */ + + ndpoffset = cdc_ncm_rx_verify_nth16(ctx, skb_in); + if (ndpoffset < 0) + goto error; + +next_ndp: + nframes = cdc_ncm_rx_verify_ndp16(skb_in, ndpoffset); + if (nframes < 0) + goto error; + + 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)); + goto err_ndp; + } + dpe16 = ndp16->dpe16; for (x = 0; x < nframes; x++, dpe16++) { offset = le16_to_cpu(dpe16->wDatagramIndex); -- cgit v1.2.3 From c78b7c58665748aa775e86e0e49798b9f34a1dc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Mon, 22 Oct 2012 10:56:34 +0000 Subject: net: cdc_ncm: refactoring for tx multiplexing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adding multiplexed NDP support to cdc_ncm_fill_tx_frame, allowing transmissions of multiple independent sessions within the same NTB. Refactoring the code quite a bit to avoid having to store copies of multiple NDPs being prepared for tx. The old code would still reserve enough room for a maximum sized NDP in the skb so we might as well keep them in the skb while they are being prepared. Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ncm.c | 247 +++++++++++++++++++--------------------------- 1 file changed, 102 insertions(+), 145 deletions(-) (limited to 'drivers/net/usb/cdc_ncm.c') diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 6688a15e044d..2dd27346cb39 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -88,14 +88,11 @@ (sizeof(struct usb_cdc_ncm_nth16) + sizeof(struct usb_cdc_ncm_ndp16) + \ (CDC_NCM_DPT_DATAGRAMS_MAX + 1) * sizeof(struct usb_cdc_ncm_dpe16)) -struct cdc_ncm_data { - struct usb_cdc_ncm_nth16 nth16; - struct usb_cdc_ncm_ndp16 ndp16; - struct usb_cdc_ncm_dpe16 dpe16[CDC_NCM_DPT_DATAGRAMS_MAX + 1]; -}; +#define CDC_NCM_NDP_SIZE \ + (sizeof(struct usb_cdc_ncm_ndp16) + \ + (CDC_NCM_DPT_DATAGRAMS_MAX + 1) * sizeof(struct usb_cdc_ncm_dpe16)) struct cdc_ncm_ctx { - struct cdc_ncm_data tx_ncm; struct usb_cdc_ncm_ntb_parameters ncm_parm; struct hrtimer tx_timer; struct tasklet_struct bh; @@ -117,13 +114,12 @@ struct cdc_ncm_ctx { struct sk_buff *tx_curr_skb; struct sk_buff *tx_rem_skb; + __le32 tx_rem_sign; spinlock_t mtx; atomic_t stop; u32 tx_timer_pending; - u32 tx_curr_offset; - u32 tx_curr_last_offset; u32 tx_curr_frame_num; u32 rx_speed; u32 tx_speed; @@ -658,51 +654,75 @@ static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf) return ret; } -static void cdc_ncm_zero_fill(u8 *ptr, u32 first, u32 end, u32 max) +static void cdc_ncm_align_tail(struct sk_buff *skb, size_t modulus, size_t remainder, size_t max) { - if (first >= max) - return; - if (first >= end) - return; - if (end > max) - end = max; - memset(ptr + first, 0, end - first); + size_t align = ALIGN(skb->len, modulus) - skb->len + remainder; + + if (skb->len + align > max) + align = max - skb->len; + if (align && skb_tailroom(skb) >= align) + memset(skb_put(skb, align), 0, align); +} + +/* return a pointer to a valid struct usb_cdc_ncm_ndp16 of type sign, possibly + * allocating a new one within skb + */ +static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign, size_t reserve) +{ + struct usb_cdc_ncm_ndp16 *ndp16 = NULL; + struct usb_cdc_ncm_nth16 *nth16 = (void *)skb->data; + size_t ndpoffset = le16_to_cpu(nth16->wNdpIndex); + + /* follow the chain of NDPs, looking for a match */ + while (ndpoffset) { + ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb->data + ndpoffset); + if (ndp16->dwSignature == sign) + return ndp16; + ndpoffset = le16_to_cpu(ndp16->wNextNdpIndex); + } + + /* align new NDP */ + 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) + return NULL; + + /* link to it */ + if (ndp16) + ndp16->wNextNdpIndex = cpu_to_le16(skb->len); + else + 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->dwSignature = sign; + ndp16->wLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_ndp16) + sizeof(struct usb_cdc_ncm_dpe16)); + return ndp16; } static struct sk_buff * -cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb) +cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign) { + struct usb_cdc_ncm_nth16 *nth16; + struct usb_cdc_ncm_ndp16 *ndp16; struct sk_buff *skb_out; - u32 rem; - u32 offset; - u32 last_offset; - u16 n = 0, index; + u16 n = 0, index, ndplen; u8 ready2send = 0; /* if there is a remaining skb, it gets priority */ - if (skb != NULL) + if (skb != NULL) { swap(skb, ctx->tx_rem_skb); - else + swap(sign, ctx->tx_rem_sign); + } else { ready2send = 1; - - /* - * +----------------+ - * | skb_out | - * +----------------+ - * ^ offset - * ^ last_offset - */ + } /* check if we are resuming an OUT skb */ - if (ctx->tx_curr_skb != NULL) { - /* pop variables */ - skb_out = ctx->tx_curr_skb; - offset = ctx->tx_curr_offset; - last_offset = ctx->tx_curr_last_offset; - n = ctx->tx_curr_frame_num; + skb_out = ctx->tx_curr_skb; - } else { - /* reset variables */ + /* allocate a new OUT skb */ + if (!skb_out) { skb_out = alloc_skb((ctx->tx_max + 1), GFP_ATOMIC); if (skb_out == NULL) { if (skb != NULL) { @@ -711,35 +731,21 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb) } goto exit_no_skb; } + /* fill out the initial 16-bit NTB header */ + nth16 = (struct usb_cdc_ncm_nth16 *)memset(skb_put(skb_out, sizeof(struct usb_cdc_ncm_nth16)), 0, sizeof(struct usb_cdc_ncm_nth16)); + nth16->dwSignature = cpu_to_le32(USB_CDC_NCM_NTH16_SIGN); + nth16->wHeaderLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_nth16)); + nth16->wSequence = cpu_to_le16(ctx->tx_seq++); - /* make room for NTH and NDP */ - offset = ALIGN(sizeof(struct usb_cdc_ncm_nth16), - ctx->tx_ndp_modulus) + - sizeof(struct usb_cdc_ncm_ndp16) + - (ctx->tx_max_datagrams + 1) * - sizeof(struct usb_cdc_ncm_dpe16); - - /* store last valid offset before alignment */ - last_offset = offset; - /* align first Datagram offset correctly */ - offset = ALIGN(offset, ctx->tx_modulus) + ctx->tx_remainder; - /* zero buffer till the first IP datagram */ - cdc_ncm_zero_fill(skb_out->data, 0, offset, offset); - n = 0; + /* count total number of frames in this NTB */ ctx->tx_curr_frame_num = 0; } - for (; n < ctx->tx_max_datagrams; n++) { - /* check if end of transmit buffer is reached */ - if (offset >= ctx->tx_max) { - ready2send = 1; - break; - } - /* compute maximum buffer size */ - rem = ctx->tx_max - offset; - + for (n = ctx->tx_curr_frame_num; n < ctx->tx_max_datagrams; n++) { + /* send any remaining skb first */ if (skb == NULL) { skb = ctx->tx_rem_skb; + sign = ctx->tx_rem_sign; ctx->tx_rem_skb = NULL; /* check for end of skb */ @@ -747,7 +753,14 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb) break; } - if (skb->len > rem) { + /* get the appropriate NDP for this skb */ + ndp16 = cdc_ncm_ndp(ctx, skb_out, sign, skb->len + ctx->tx_modulus + ctx->tx_remainder); + + /* align beginning of next frame */ + cdc_ncm_align_tail(skb_out, ctx->tx_modulus, ctx->tx_remainder, ctx->tx_max); + + /* check if we had enough room left for both NDP and frame */ + if (!ndp16 || skb_out->len + skb->len > ctx->tx_max) { if (n == 0) { /* won't fit, MTU problem? */ dev_kfree_skb_any(skb); @@ -760,31 +773,30 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb) ctx->netdev->stats.tx_dropped++; } ctx->tx_rem_skb = skb; + ctx->tx_rem_sign = sign; skb = NULL; ready2send = 1; } break; } - memcpy(((u8 *)skb_out->data) + offset, skb->data, skb->len); - - ctx->tx_ncm.dpe16[n].wDatagramLength = cpu_to_le16(skb->len); - ctx->tx_ncm.dpe16[n].wDatagramIndex = cpu_to_le16(offset); - - /* update offset */ - offset += skb->len; + /* calculate frame number withing this NDP */ + ndplen = le16_to_cpu(ndp16->wLength); + index = (ndplen - sizeof(struct usb_cdc_ncm_ndp16)) / sizeof(struct usb_cdc_ncm_dpe16) - 1; - /* store last valid offset before alignment */ - last_offset = offset; - - /* align offset correctly */ - offset = ALIGN(offset, ctx->tx_modulus) + ctx->tx_remainder; - - /* zero padding */ - cdc_ncm_zero_fill(skb_out->data, last_offset, offset, - ctx->tx_max); + /* OK, add this skb */ + ndp16->dpe16[index].wDatagramLength = cpu_to_le16(skb->len); + 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); dev_kfree_skb_any(skb); skb = NULL; + + /* send now if this NDP is full */ + if (index >= CDC_NCM_DPT_DATAGRAMS_MAX) { + ready2send = 1; + break; + } } /* free up any dangling skb */ @@ -800,16 +812,12 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb) /* wait for more frames */ /* push variables */ ctx->tx_curr_skb = skb_out; - ctx->tx_curr_offset = offset; - ctx->tx_curr_last_offset = last_offset; goto exit_no_skb; } else if ((n < ctx->tx_max_datagrams) && (ready2send == 0)) { /* wait for more frames */ /* push variables */ ctx->tx_curr_skb = skb_out; - ctx->tx_curr_offset = offset; - ctx->tx_curr_last_offset = last_offset; /* set the pending count */ if (n < CDC_NCM_RESTART_TIMER_DATAGRAM_CNT) ctx->tx_timer_pending = CDC_NCM_TIMER_PENDING_CNT; @@ -820,75 +828,24 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb) /* variables will be reset at next call */ } - /* check for overflow */ - if (last_offset > ctx->tx_max) - last_offset = ctx->tx_max; - - /* revert offset */ - offset = last_offset; - /* * 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 (offset > CDC_NCM_MIN_TX_PKT) - offset = ctx->tx_max; - - /* final zero padding */ - cdc_ncm_zero_fill(skb_out->data, last_offset, offset, ctx->tx_max); - - /* store last offset */ - last_offset = offset; - - if (((last_offset < ctx->tx_max) && ((last_offset % - le16_to_cpu(ctx->out_ep->desc.wMaxPacketSize)) == 0)) || - (((last_offset == ctx->tx_max) && ((ctx->tx_max % - le16_to_cpu(ctx->out_ep->desc.wMaxPacketSize)) == 0)) && - (ctx->tx_max < le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize)))) { - /* force short packet */ - *(((u8 *)skb_out->data) + last_offset) = 0; - last_offset++; - } + 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); - /* zero the rest of the DPEs plus the last NULL entry */ - for (; n <= CDC_NCM_DPT_DATAGRAMS_MAX; n++) { - ctx->tx_ncm.dpe16[n].wDatagramLength = 0; - ctx->tx_ncm.dpe16[n].wDatagramIndex = 0; - } - - /* fill out 16-bit NTB header */ - ctx->tx_ncm.nth16.dwSignature = cpu_to_le32(USB_CDC_NCM_NTH16_SIGN); - ctx->tx_ncm.nth16.wHeaderLength = - cpu_to_le16(sizeof(ctx->tx_ncm.nth16)); - ctx->tx_ncm.nth16.wSequence = cpu_to_le16(ctx->tx_seq); - ctx->tx_ncm.nth16.wBlockLength = cpu_to_le16(last_offset); - index = ALIGN(sizeof(struct usb_cdc_ncm_nth16), ctx->tx_ndp_modulus); - ctx->tx_ncm.nth16.wNdpIndex = cpu_to_le16(index); - - memcpy(skb_out->data, &(ctx->tx_ncm.nth16), sizeof(ctx->tx_ncm.nth16)); - ctx->tx_seq++; - - /* fill out 16-bit NDP table */ - ctx->tx_ncm.ndp16.dwSignature = - cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN); - rem = sizeof(ctx->tx_ncm.ndp16) + ((ctx->tx_curr_frame_num + 1) * - sizeof(struct usb_cdc_ncm_dpe16)); - ctx->tx_ncm.ndp16.wLength = cpu_to_le16(rem); - ctx->tx_ncm.ndp16.wNextNdpIndex = 0; /* reserved */ - - memcpy(((u8 *)skb_out->data) + index, - &(ctx->tx_ncm.ndp16), - sizeof(ctx->tx_ncm.ndp16)); - - memcpy(((u8 *)skb_out->data) + index + sizeof(ctx->tx_ncm.ndp16), - &(ctx->tx_ncm.dpe16), - (ctx->tx_curr_frame_num + 1) * - sizeof(struct usb_cdc_ncm_dpe16)); + /* 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)) + *skb_put(skb_out, 1) = 0; /* force short packet */ - /* set frame length */ - skb_put(skb_out, last_offset); + /* set final frame length */ + nth16 = (struct usb_cdc_ncm_nth16 *)skb_out->data; + nth16->wBlockLength = cpu_to_le16(skb_out->len); /* return skb */ ctx->tx_curr_skb = NULL; @@ -955,7 +912,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); + skb_out = cdc_ncm_fill_tx_frame(ctx, skb, cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN)); spin_unlock_bh(&ctx->mtx); return skb_out; -- cgit v1.2.3 From c91ce3b6bff780453283a5882ecd54f267f4682f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Mon, 22 Oct 2012 10:56:35 +0000 Subject: net: cdc_ncm: export shared symbols and definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move symbols and definitons which can be shared with a MBIM driver in a new header. Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ncm.c | 97 ++++++----------------------------------------- 1 file changed, 11 insertions(+), 86 deletions(-) (limited to 'drivers/net/usb/cdc_ncm.c') diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 2dd27346cb39..17b4ba03cb55 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -51,90 +51,10 @@ #include #include #include +#include #define DRIVER_VERSION "14-Mar-2012" -/* CDC NCM subclass 3.2.1 */ -#define USB_CDC_NCM_NDP16_LENGTH_MIN 0x10 - -/* Maximum NTB length */ -#define CDC_NCM_NTB_MAX_SIZE_TX 32768 /* bytes */ -#define CDC_NCM_NTB_MAX_SIZE_RX 32768 /* bytes */ - -/* Minimum value for MaxDatagramSize, ch. 6.2.9 */ -#define CDC_NCM_MIN_DATAGRAM_SIZE 1514 /* bytes */ - -/* Minimum value for MaxDatagramSize, ch. 8.1.3 */ -#define CDC_MBIM_MIN_DATAGRAM_SIZE 2048 /* bytes */ - -#define CDC_NCM_MIN_TX_PKT 512 /* bytes */ - -/* Default value for MaxDatagramSize */ -#define CDC_NCM_MAX_DATAGRAM_SIZE 8192 /* bytes */ - -/* - * Maximum amount of datagrams in NCM Datagram Pointer Table, not counting - * the last NULL entry. - */ -#define CDC_NCM_DPT_DATAGRAMS_MAX 40 - -/* Restart the timer, if amount of datagrams is less than given value */ -#define CDC_NCM_RESTART_TIMER_DATAGRAM_CNT 3 -#define CDC_NCM_TIMER_PENDING_CNT 2 -#define CDC_NCM_TIMER_INTERVAL (400UL * NSEC_PER_USEC) - -/* The following macro defines the minimum header space */ -#define CDC_NCM_MIN_HDR_SIZE \ - (sizeof(struct usb_cdc_ncm_nth16) + sizeof(struct usb_cdc_ncm_ndp16) + \ - (CDC_NCM_DPT_DATAGRAMS_MAX + 1) * sizeof(struct usb_cdc_ncm_dpe16)) - -#define CDC_NCM_NDP_SIZE \ - (sizeof(struct usb_cdc_ncm_ndp16) + \ - (CDC_NCM_DPT_DATAGRAMS_MAX + 1) * sizeof(struct usb_cdc_ncm_dpe16)) - -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_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; - - struct sk_buff *tx_curr_skb; - struct sk_buff *tx_rem_skb; - __le32 tx_rem_sign; - - spinlock_t mtx; - atomic_t stop; - - 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; - u16 tx_max_datagrams; - u16 tx_remainder; - u16 tx_modulus; - u16 tx_ndp_modulus; - u16 tx_seq; - u16 rx_seq; - u16 connected; -}; - static void cdc_ncm_txpath_bh(unsigned long param); 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); @@ -447,7 +367,7 @@ static const struct ethtool_ops cdc_ncm_ethtool_ops = { .nway_reset = usbnet_nway_reset, }; -static int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting) +int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting) { struct cdc_ncm_ctx *ctx; struct usb_driver *driver; @@ -605,8 +525,9 @@ error: dev_info(&dev->udev->dev, "bind() failure\n"); return -ENODEV; } +EXPORT_SYMBOL_GPL(cdc_ncm_bind_common); -static void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf) +void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf) { struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; struct usb_driver *driver = driver_of(intf); @@ -636,6 +557,7 @@ static void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf) usb_set_intfdata(ctx->intf, NULL); cdc_ncm_free(ctx); } +EXPORT_SYMBOL_GPL(cdc_ncm_unbind); static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf) { @@ -701,7 +623,7 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_ return ndp16; } -static struct sk_buff * +struct sk_buff * cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign) { struct usb_cdc_ncm_nth16 *nth16; @@ -858,6 +780,7 @@ exit_no_skb: cdc_ncm_tx_timeout_start(ctx); return NULL; } +EXPORT_SYMBOL_GPL(cdc_ncm_fill_tx_frame); static void cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx) { @@ -924,7 +847,7 @@ error: } /* verify NTB header and return offset of first NDP, or negative error */ -static int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in) +int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in) { struct usb_cdc_ncm_nth16 *nth16; int len; @@ -966,9 +889,10 @@ static int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_ error: return ret; } +EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_nth16); /* verify NDP header and return number of datagrams, or negative error */ -static int cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset) +int cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset) { struct usb_cdc_ncm_ndp16 *ndp16; int ret = -EINVAL; @@ -999,6 +923,7 @@ static int cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset) error: return ret; } +EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_ndp16); static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) { -- cgit v1.2.3 From bd329e1284bc5fa6adaddd2ee9ca15328749a7a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Mon, 22 Oct 2012 10:56:38 +0000 Subject: net: cdc_ncm: do not bind to NCM compatible MBIM devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MBIM specification allows a MBIM device to disguise itself as NCM for backwards compatibility, using additional altsettings with different subclass (control) or protocol (data): C:* #Ifs= 2 Cfg#= 1 Atr=80 MxPwr=100mA I: If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=0d Prot=00 Driver=cdc_mbim E: Ad=81(I) Atr=03(Int.) MxPS= 64 Ivl=7ms I:* If#= 0 Alt= 1 #EPs= 1 Cls=02(comm.) Sub=0e Prot=00 Driver=cdc_mbim E: Ad=81(I) Atr=03(Int.) MxPS= 64 Ivl=7ms I: If#= 1 Alt= 0 #EPs= 0 Cls=0a(data ) Sub=00 Prot=01 Driver=cdc_mbim I: If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=01 Driver=cdc_mbim E: Ad=81(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms I:* If#= 1 Alt= 2 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim E: Ad=81(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms If the MBIM driver is enabled then that should have priority for devices providing such a NCM 1.0 backward compatibility mode. Signed-off-by: Bjørn Mork Signed-off-by: Greg Suarez Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ncm.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'drivers/net/usb/cdc_ncm.c') diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 17b4ba03cb55..0ed03b1ba39a 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -563,6 +563,33 @@ static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf) { int ret; + /* The MBIM spec defines a NCM compatible default altsetting, + * which we may have matched: + * + * "Functions that implement both NCM 1.0 and MBIM (an + * “NCM/MBIM function”) according to this recommendation + * shall provide two alternate settings for the + * Communication Interface. Alternate setting 0, and the + * associated class and endpoint descriptors, shall be + * constructed according to the rules given for the + * Communication Interface in section 5 of [USBNCM10]. + * Alternate setting 1, and the associated class and + * endpoint descriptors, shall be constructed according to + * the rules given in section 6 (USB Device Model) of this + * specification." + * + * Do not bind to such interfaces, allowing cdc_mbim to handle + * them + */ +#if IS_ENABLED(CONFIG_USB_NET_CDC_MBIM) + if ((intf->num_altsetting == 2) && + !usb_set_interface(dev->udev, + intf->cur_altsetting->desc.bInterfaceNumber, + CDC_NCM_COMM_ALTSETTING_MBIM) && + cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting)) + return -ENODEV; +#endif + /* NCM data altsetting is always 1 */ ret = cdc_ncm_bind_common(dev, intf, 1); -- cgit v1.2.3 From 90b8b037f72d482bc8b95c2bbf8aa47d712ee9fb Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Wed, 24 Oct 2012 19:46:56 +0000 Subject: usbnet: cdc-ncm: apply introduced usb command APIs Acked-by: Oliver Neukum Signed-off-by: Ming Lei Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ncm.c | 85 +++++++++++++++++------------------------------ 1 file changed, 31 insertions(+), 54 deletions(-) (limited to 'drivers/net/usb/cdc_ncm.c') diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 0ed03b1ba39a..d0ea41938881 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -82,16 +82,15 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx) 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; - err = usb_control_msg(ctx->udev, - usb_rcvctrlpipe(ctx->udev, 0), - USB_CDC_GET_NTB_PARAMETERS, - USB_TYPE_CLASS | USB_DIR_IN - | USB_RECIP_INTERFACE, - 0, iface_no, &ctx->ncm_parm, - sizeof(ctx->ncm_parm), 10000); + 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)); if (err < 0) { pr_debug("failed GET_NTB_PARAMETERS\n"); return 1; @@ -147,22 +146,12 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx) /* inform device about NTB input size changes */ if (ctx->rx_max != le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize)) { - __le32 *dwNtbInMaxSize; + __le32 dwNtbInMaxSize = cpu_to_le32(ctx->rx_max); - dwNtbInMaxSize = kzalloc(sizeof(*dwNtbInMaxSize), GFP_KERNEL); - if (!dwNtbInMaxSize) { - err = -ENOMEM; - goto size_err; - } - *dwNtbInMaxSize = cpu_to_le32(ctx->rx_max); - err = usb_control_msg(ctx->udev, - usb_sndctrlpipe(ctx->udev, 0), - USB_CDC_SET_NTB_INPUT_SIZE, - USB_TYPE_CLASS | USB_DIR_OUT - | USB_RECIP_INTERFACE, - 0, iface_no, dwNtbInMaxSize, 4, 1000); - kfree(dwNtbInMaxSize); -size_err: + 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"); } @@ -218,23 +207,22 @@ size_err: /* set CRC Mode */ if (flags & USB_CDC_NCM_NCAP_CRC_MODE) { - err = usb_control_msg(ctx->udev, usb_sndctrlpipe(ctx->udev, 0), - USB_CDC_SET_CRC_MODE, - USB_TYPE_CLASS | USB_DIR_OUT - | USB_RECIP_INTERFACE, - USB_CDC_NCM_CRC_NOT_APPENDED, - iface_no, NULL, 0, 1000); + 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 = usb_control_msg(ctx->udev, usb_sndctrlpipe(ctx->udev, 0), - USB_CDC_SET_NTB_FORMAT, USB_TYPE_CLASS - | USB_DIR_OUT | USB_RECIP_INTERFACE, - USB_CDC_NCM_NTB16_FORMAT, - iface_no, NULL, 0, 1000); + 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"); } @@ -243,7 +231,7 @@ size_err: /* set Max Datagram Size (MTU) */ if (flags & USB_CDC_NCM_NCAP_MAX_DATAGRAM_SIZE) { - __le16 *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); @@ -252,25 +240,16 @@ size_err: else goto max_dgram_err; - max_datagram_size = kzalloc(sizeof(*max_datagram_size), - GFP_KERNEL); - if (!max_datagram_size) { - err = -ENOMEM; - goto max_dgram_err; - } - - err = usb_control_msg(ctx->udev, usb_rcvctrlpipe(ctx->udev, 0), - USB_CDC_GET_MAX_DATAGRAM_SIZE, - USB_TYPE_CLASS | USB_DIR_IN - | USB_RECIP_INTERFACE, - 0, iface_no, max_datagram_size, - 2, 1000); + 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); + 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; @@ -283,20 +262,18 @@ size_err: /* if value changed, update device */ if (ctx->max_datagram_size != - le16_to_cpu(*max_datagram_size)) { - err = usb_control_msg(ctx->udev, - usb_sndctrlpipe(ctx->udev, 0), + 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, 1000); + iface_no, &max_datagram_size, + 2); if (err < 0) pr_debug("SET_MAX_DGRAM_SIZE failed\n"); } } - kfree(max_datagram_size); } max_dgram_err: -- cgit v1.2.3 From 1a7c6cc646782b854b036d287611a0659c40fc9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Thu, 25 Oct 2012 21:44:08 +0000 Subject: net: cdc_ncm: big endian fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Probably doesn't matter much since the value is used as a boolean anyway, but it removes the sparse warning: drivers/net/usb/cdc_ncm.c:1090:32: warning: incorrect type in assignment (different base types) drivers/net/usb/cdc_ncm.c:1090:32: expected unsigned short [unsigned] [usertype] connected drivers/net/usb/cdc_ncm.c:1090:32: got restricted __le16 [usertype] wValue Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ncm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/usb/cdc_ncm.c') diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index d0ea41938881..397de6396da6 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -1064,7 +1064,7 @@ 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 = event->wValue; + ctx->connected = le16_to_cpu(event->wValue); printk(KERN_INFO KBUILD_MODNAME ": %s: network connection:" " %sconnected\n", -- cgit v1.2.3 From 7b1e0cba0372c011fc9128a772cf668e70ba9219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Thu, 25 Oct 2012 21:44:09 +0000 Subject: net: cdc_ncm: error path lock fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes the sparse warning drivers/net/usb/cdc_ncm.c:836:9: warning: context imbalance in 'cdc_ncm_txpath_bh' - different lock contexts for basic block Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ncm.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/net/usb/cdc_ncm.c') diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 397de6396da6..ddc7b8880f60 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -819,6 +819,8 @@ static void cdc_ncm_txpath_bh(unsigned long param) netif_tx_lock_bh(ctx->netdev); usbnet_start_xmit(NULL, ctx->netdev); netif_tx_unlock_bh(ctx->netdev); + } else { + spin_unlock_bh(&ctx->mtx); } } -- cgit v1.2.3