From 9b28ecd66b391349d3492574500f978566195454 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Thu, 19 Jan 2012 15:37:22 +0000 Subject: net: usb: qmi_wwan: New driver for Huawei QMI based WWAN devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some WWAN LTE/3G devices based on chipsets from Qualcomm provide near standard CDC ECM interfaces in addition to the usual serial interfaces. The Huawei E392/E398 are examples of such devices. These typically cannot be fully configured using AT commands over a serial interface. It is necessary to speak the proprietary Qualcomm MSM Interface (QMI) protocol to the device to enable the ethernet proxy functionality. The devices embed the QMI protocol in CDC on the control interface, using standard CDC commands and notifications. The do not otherwise use CDC commands for the ethernet function. This driver does therefore not need access to any other aspects of the control interface than the descriptors attached to it. Another driver, cdc-wdm, will provide userspace access to the QMI protocol independently of this driver. To facilitate this, this driver avoids binding to the control interface, and uses only the associated data interface after parsing the common CDC functional descriptors on the control interface. You will want both the cdc-wdm and option drivers as companions to this driver, to have full access to all interfaces and protocols exported by the device. Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/Kconfig | 22 +++++ drivers/net/usb/Makefile | 1 + drivers/net/usb/qmi_wwan.c | 228 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 251 insertions(+) create mode 100644 drivers/net/usb/qmi_wwan.c (limited to 'drivers/net/usb') diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index 233576127934..4bad899fb38f 100644 --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -398,6 +398,27 @@ config USB_NET_KALMIA To compile this driver as a module, choose M here: the module will be called kalmia. +config USB_NET_QMI_WWAN + tristate "QMI WWAN driver for Qualcomm MSM based 3G and LTE modems" + depends on USB_USBNET + help + Support WWAN LTE/3G devices based on Qualcomm Mobile Data Modem + (MDM) chipsets. Examples of such devices are + * Huawei E392/E398 + + This driver will only drive the ethernet part of the chips. + The devices require additional configuration to be usable. + Multiple management interfaces with linux drivers are + available: + + * option: AT commands on /dev/ttyUSBx + * cdc-wdm: Qualcomm MSM Interface (QMI) protocol on /dev/cdc-wdmx + + A modem manager with support for QMI is recommended. + + To compile this driver as a module, choose M here: the + module will be called qmi_wwan. + config USB_HSO tristate "Option USB High Speed Mobile Devices" depends on USB && RFKILL @@ -461,4 +482,5 @@ config USB_VL600 http://ubuntuforums.org/showpost.php?p=10589647&postcount=17 + endmenu diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile index c203fa21f6b1..a2e2d72c52a0 100644 --- a/drivers/net/usb/Makefile +++ b/drivers/net/usb/Makefile @@ -29,4 +29,5 @@ obj-$(CONFIG_USB_SIERRA_NET) += sierra_net.o obj-$(CONFIG_USB_NET_CX82310_ETH) += cx82310_eth.o obj-$(CONFIG_USB_NET_CDC_NCM) += cdc_ncm.o obj-$(CONFIG_USB_VL600) += lg-vl600.o +obj-$(CONFIG_USB_NET_QMI_WWAN) += qmi_wwan.o diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c new file mode 100644 index 000000000000..739e6de7abcb --- /dev/null +++ b/drivers/net/usb/qmi_wwan.c @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2012 Bjørn Mork + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* The name of the CDC Device Management driver */ +#define DM_DRIVER "cdc_wdm" + +/* + * This driver supports wwan (3G/LTE/?) devices using a vendor + * specific management protocol called Qualcomm MSM Interface (QMI) - + * in addition to the more common AT commands over serial interface + * management + * + * QMI is wrapped in CDC, using CDC encapsulated commands on the + * control ("master") interface of a two-interface CDC Union + * resembling standard CDC ECM. The devices do not use the control + * interface for any other CDC messages. Most likely because the + * management protocol is used in place of the standard CDC + * notifications NOTIFY_NETWORK_CONNECTION and NOTIFY_SPEED_CHANGE + * + * Handling a protocol like QMI is out of the scope for any driver. + * It can be exported as a character device using the cdc-wdm driver, + * which will enable userspace applications ("modem managers") to + * handle it. This may be required to use the network interface + * provided by the driver. + * + * These devices may alternatively/additionally be configured using AT + * commands on any of the serial interfaces driven by the option driver + * + * This driver binds only to the data ("slave") interface to enable + * the cdc-wdm driver to bind to the control interface. It still + * parses the CDC functional descriptors on the control interface to + * a) verify that this is indeed a handled interface (CDC Union + * header lists it as slave) + * b) get MAC address and other ethernet config from the CDC Ethernet + * header + * c) enable user bind requests against the control interface, which + * is the common way to bind to CDC Ethernet Control Model type + * interfaces + * d) provide a hint to the user about which interface is the + * corresponding management interface + */ + +static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) +{ + int status = -1; + struct usb_interface *control = NULL; + u8 *buf = intf->cur_altsetting->extra; + int len = intf->cur_altsetting->extralen; + struct usb_interface_descriptor *desc = &intf->cur_altsetting->desc; + struct usb_cdc_union_desc *cdc_union = NULL; + struct usb_cdc_ether_desc *cdc_ether = NULL; + u32 required = 1 << USB_CDC_HEADER_TYPE | 1 << USB_CDC_UNION_TYPE; + u32 found = 0; + + /* + * assume a data interface has no additional descriptors and + * that the control and data interface are numbered + * consecutively - this holds for the Huawei device at least + */ + if (len == 0 && desc->bInterfaceNumber > 0) { + control = usb_ifnum_to_if(dev->udev, desc->bInterfaceNumber - 1); + if (!control) + goto err; + + buf = control->cur_altsetting->extra; + len = control->cur_altsetting->extralen; + dev_dbg(&intf->dev, "guessing \"control\" => %s, \"data\" => this\n", + dev_name(&control->dev)); + } + + while (len > 3) { + struct usb_descriptor_header *h = (void *)buf; + + /* ignore any misplaced descriptors */ + if (h->bDescriptorType != USB_DT_CS_INTERFACE) + goto next_desc; + + /* buf[2] is CDC descriptor subtype */ + switch (buf[2]) { + case USB_CDC_HEADER_TYPE: + if (found & 1 << USB_CDC_HEADER_TYPE) { + dev_dbg(&intf->dev, "extra CDC header\n"); + goto err; + } + if (h->bLength != sizeof(struct usb_cdc_header_desc)) { + dev_dbg(&intf->dev, "CDC header len %u\n", h->bLength); + goto err; + } + break; + case USB_CDC_UNION_TYPE: + if (found & 1 << USB_CDC_UNION_TYPE) { + dev_dbg(&intf->dev, "extra CDC union\n"); + goto err; + } + if (h->bLength != sizeof(struct usb_cdc_union_desc)) { + dev_dbg(&intf->dev, "CDC union len %u\n", h->bLength); + goto err; + } + cdc_union = (struct usb_cdc_union_desc *)buf; + break; + case USB_CDC_ETHERNET_TYPE: + if (found & 1 << USB_CDC_ETHERNET_TYPE) { + dev_dbg(&intf->dev, "extra CDC ether\n"); + goto err; + } + if (h->bLength != sizeof(struct usb_cdc_ether_desc)) { + dev_dbg(&intf->dev, "CDC ether len %u\n", h->bLength); + goto err; + } + cdc_ether = (struct usb_cdc_ether_desc *)buf; + break; + } + + /* + * Remember which CDC functional descriptors we've seen. Works + * for all types we care about, of which USB_CDC_ETHERNET_TYPE + * (0x0f) is the highest numbered + */ + if (buf[2] < 32) + found |= 1 << buf[2]; + +next_desc: + len -= h->bLength; + buf += h->bLength; + } + + /* did we find all the required ones? */ + if ((found & required) != required) { + dev_err(&intf->dev, "CDC functional descriptors missing\n"); + goto err; + } + + /* give the user a helpful hint if trying to bind to the wrong interface */ + if (cdc_union && desc->bInterfaceNumber == cdc_union->bMasterInterface0) { + dev_err(&intf->dev, "leaving \"control\" interface for " DM_DRIVER " - try binding to %s instead!\n", + dev_name(&usb_ifnum_to_if(dev->udev, cdc_union->bSlaveInterface0)->dev)); + goto err; + } + + /* errors aren't fatal - we can live with the dynamic address */ + if (cdc_ether) { + dev->hard_mtu = le16_to_cpu(cdc_ether->wMaxSegmentSize); + usbnet_get_ethernet_addr(dev, cdc_ether->iMACAddress); + } + + /* success! point the user to the management interface */ + if (control) + dev_info(&intf->dev, "Use \"" DM_DRIVER "\" for QMI interface %s\n", + dev_name(&control->dev)); + + /* XXX: add a sysfs symlink somewhere to help management applications find it? */ + + /* collect bulk endpoints now that we know intf == "data" interface */ + status = usbnet_get_endpoints(dev, intf); + +err: + return status; +} + +/* stolen from cdc_ether.c */ +static int qmi_wwan_manage_power(struct usbnet *dev, int on) +{ + dev->intf->needs_remote_wakeup = on; + return 0; +} + +static const struct driver_info qmi_wwan_info = { + .description = "QMI speaking wwan device", + .flags = FLAG_WWAN, + .bind = qmi_wwan_bind, + .manage_power = qmi_wwan_manage_power, +}; + +#define HUAWEI_VENDOR_ID 0x12D1 + +static const struct usb_device_id products[] = { +{ + /* Huawei E392, E398 and possibly others sharing both device id and more... */ + .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = HUAWEI_VENDOR_ID, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 8, /* NOTE: This is the *slave* interface of the CDC Union! */ + .driver_info = (unsigned long)&qmi_wwan_info, +}, { +}, /* END */ +}; +MODULE_DEVICE_TABLE(usb, products); + +static struct usb_driver qmi_wwan_driver = { + .name = "qmi_wwan", + .id_table = products, + .probe = usbnet_probe, + .disconnect = usbnet_disconnect, + .suspend = usbnet_suspend, + .resume = usbnet_resume, + .reset_resume = usbnet_resume, + .supports_autosuspend = 1, +}; + +static int __init qmi_wwan_init(void) +{ + return usb_register(&qmi_wwan_driver); +} +module_init(qmi_wwan_init); + +static void __exit qmi_wwan_exit(void) +{ + usb_deregister(&qmi_wwan_driver); +} +module_exit(qmi_wwan_exit); + +MODULE_AUTHOR("Bjørn Mork "); +MODULE_DESCRIPTION("Qualcomm MSM Interface (QMI) WWAN driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.2 From 41de8d4cff21a2e81e3d9ff66f5f7c903f9c3ab1 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sun, 29 Jan 2012 13:47:52 +0000 Subject: drivers/net: Remove alloc_etherdev error messages alloc_etherdev has a generic OOM/unable to alloc message. Remove the duplicative messages after alloc_etherdev calls. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- drivers/net/usb/pegasus.c | 4 +--- drivers/net/usb/rtl8150.c | 4 +--- drivers/net/usb/usbnet.c | 4 +--- 3 files changed, 3 insertions(+), 9 deletions(-) (limited to 'drivers/net/usb') diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c index 5d99b8cacd7d..752393092325 100644 --- a/drivers/net/usb/pegasus.c +++ b/drivers/net/usb/pegasus.c @@ -1332,10 +1332,8 @@ static int pegasus_probe(struct usb_interface *intf, usb_get_dev(dev); net = alloc_etherdev(sizeof(struct pegasus)); - if (!net) { - dev_err(&intf->dev, "can't allocate %s\n", "device"); + if (!net) goto out; - } pegasus = netdev_priv(net); pegasus->dev_index = dev_index; diff --git a/drivers/net/usb/rtl8150.c b/drivers/net/usb/rtl8150.c index 0710b4ca9252..6dda2fe5b15b 100644 --- a/drivers/net/usb/rtl8150.c +++ b/drivers/net/usb/rtl8150.c @@ -894,10 +894,8 @@ static int rtl8150_probe(struct usb_interface *intf, struct net_device *netdev; netdev = alloc_etherdev(sizeof(rtl8150_t)); - if (!netdev) { - err("Out of memory"); + if (!netdev) return -ENOMEM; - } dev = netdev_priv(netdev); diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index fae0fbd8bc88..b924f46c963c 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -1334,10 +1334,8 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) // set up our own records net = alloc_etherdev(sizeof(*dev)); - if (!net) { - dbg ("can't kmalloc dev"); + if (!net) goto out; - } /* netdev_printk() needs this so do it as early as possible */ SET_NETDEV_DEV(net, &udev->dev); -- cgit v1.2.2 From f2cedb63df14342ad40a8b5b324fc5d94a60b665 Mon Sep 17 00:00:00 2001 From: Danny Kukawka Date: Wed, 15 Feb 2012 06:45:39 +0000 Subject: net: replace random_ether_addr() with eth_hw_addr_random() Replace usage of random_ether_addr() with eth_hw_addr_random() to set addr_assign_type correctly to NET_ADDR_RANDOM. Change the trivial cases. v2: adapt to renamed eth_hw_addr_random() Signed-off-by: Danny Kukawka Signed-off-by: David S. Miller --- drivers/net/usb/smsc75xx.c | 2 +- drivers/net/usb/smsc95xx.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/usb') diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c index 3b017bbd2a22..187d01ccb973 100644 --- a/drivers/net/usb/smsc75xx.c +++ b/drivers/net/usb/smsc75xx.c @@ -615,7 +615,7 @@ static void smsc75xx_init_mac_address(struct usbnet *dev) } /* no eeprom, or eeprom values are invalid. generate random MAC */ - random_ether_addr(dev->net->dev_addr); + eth_hw_addr_random(dev->net); netif_dbg(dev, ifup, dev->net, "MAC address set to random_ether_addr"); } diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index d45520e6dd48..5f19f84d3494 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -614,7 +614,7 @@ static void smsc95xx_init_mac_address(struct usbnet *dev) } /* no eeprom, or eeprom values are invalid. generate random MAC */ - random_ether_addr(dev->net->dev_addr); + eth_hw_addr_random(dev->net); netif_dbg(dev, ifup, dev->net, "MAC address set to random_ether_addr\n"); } -- cgit v1.2.2 From 15b8f4cf8ea6654c5642358c70b6e84909d99038 Mon Sep 17 00:00:00 2001 From: Danny Kukawka Date: Tue, 21 Feb 2012 02:07:50 +0000 Subject: mcs7830: unify return value of .ndo_set_mac_address if address is invalid Unify return value of .ndo_set_mac_address if the given address isn't valid. Return -EADDRNOTAVAIL as eth_mac_addr() already does if is_valid_ether_addr() fails. Signed-off-by: Danny Kukawka Signed-off-by: David S. Miller --- drivers/net/usb/mcs7830.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/usb') diff --git a/drivers/net/usb/mcs7830.c b/drivers/net/usb/mcs7830.c index a29aa9cf9f6e..c434b6ba0337 100644 --- a/drivers/net/usb/mcs7830.c +++ b/drivers/net/usb/mcs7830.c @@ -239,7 +239,7 @@ static int mcs7830_set_mac_address(struct net_device *netdev, void *p) return -EBUSY; if (!is_valid_ether_addr(addr->sa_data)) - return -EINVAL; + return -EADDRNOTAVAIL; ret = mcs7830_hif_set_mac_address(dev, addr->sa_data); -- cgit v1.2.2 From 517cd81c0a5b5d8fa4887490ba5cd7c64e7b5e00 Mon Sep 17 00:00:00 2001 From: Danny Kukawka Date: Fri, 24 Feb 2012 03:45:59 +0000 Subject: usb/cdc_ncm: print MAC via printk format specifier Print MAC/dev_addr via printk extended format specifier %pM instead of custom code. Signed-off-by: Danny Kukawka Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ncm.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers/net/usb') diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 3a539a9cac54..81f86b8ea551 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -579,11 +579,7 @@ advance: if (temp) goto error2; - dev_info(&dev->udev->dev, "MAC-Address: " - "0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x\n", - dev->net->dev_addr[0], dev->net->dev_addr[1], - dev->net->dev_addr[2], dev->net->dev_addr[3], - dev->net->dev_addr[4], dev->net->dev_addr[5]); + 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); -- cgit v1.2.2 From 6ac8f069776c4e678448dabdd9bb2dd25dea3186 Mon Sep 17 00:00:00 2001 From: Danny Kukawka Date: Fri, 24 Feb 2012 03:46:00 +0000 Subject: usb/kaweth: print MAC via printk format specifier Print MAC/dev_addr via printk extended format specifier %pM instead of custom code. Signed-off-by: Danny Kukawka Acked-by: Oliver Neukum Signed-off-by: David S. Miller --- drivers/net/usb/kaweth.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'drivers/net/usb') diff --git a/drivers/net/usb/kaweth.c b/drivers/net/usb/kaweth.c index d034d9c42548..df2a2cf35a99 100644 --- a/drivers/net/usb/kaweth.c +++ b/drivers/net/usb/kaweth.c @@ -1098,13 +1098,7 @@ err_fw: dev_info(&intf->dev, "Statistics collection: %x\n", kaweth->configuration.statistics_mask); dev_info(&intf->dev, "Multicast filter limit: %x\n", kaweth->configuration.max_multicast_filters & ((1 << 15) - 1)); dev_info(&intf->dev, "MTU: %d\n", le16_to_cpu(kaweth->configuration.segment_size)); - dev_info(&intf->dev, "Read MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n", - (int)kaweth->configuration.hw_addr[0], - (int)kaweth->configuration.hw_addr[1], - (int)kaweth->configuration.hw_addr[2], - (int)kaweth->configuration.hw_addr[3], - (int)kaweth->configuration.hw_addr[4], - (int)kaweth->configuration.hw_addr[5]); + dev_info(&intf->dev, "Read MAC address %pM\n", kaweth->configuration.hw_addr); if(!memcmp(&kaweth->configuration.hw_addr, &bcast_addr, -- cgit v1.2.2 From a9e0aca4b37885b5599e52211f098bd7f565e749 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 14 Mar 2012 20:18:32 +0000 Subject: asix: asix_rx_fixup surgery to reduce skb truesizes asix_rx_fixup() is complex, and does some unnecessary memory copies (at least on x86 where NET_IP_ALIGN is 0) Also, it tends to provide skbs with a big truesize (4096+256 with MTU=1500) to upper stack, so incoming trafic consume a lot of memory and I noticed early packet drops because we hit socket rcvbuf too fast. Switch to a different strategy, using copybreak so that we provide nice skbs to upper stack (including the NET_SKB_PAD to avoid future head reallocations in some paths) With this patch, I no longer see packets drops or tcp collapses on various tcp workload with a AX88772 adapter. Signed-off-by: Eric Dumazet Cc: Aurelien Jacobs Cc: Greg Kroah-Hartman Cc: Trond Wuellner Cc: Grant Grundler Cc: Paul Stewart Reviewed-by: Grant Grundler Reviewed-by: Grant Grundler Signed-off-by: David S. Miller --- drivers/net/usb/asix.c | 88 ++++++++++++-------------------------------------- 1 file changed, 20 insertions(+), 68 deletions(-) (limited to 'drivers/net/usb') diff --git a/drivers/net/usb/asix.c b/drivers/net/usb/asix.c index 8e84f5bdd6ca..25fe1838d886 100644 --- a/drivers/net/usb/asix.c +++ b/drivers/net/usb/asix.c @@ -305,88 +305,40 @@ asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index, static int asix_rx_fixup(struct usbnet *dev, struct sk_buff *skb) { - u8 *head; - u32 header; - char *packet; - struct sk_buff *ax_skb; - u16 size; + int offset = 0; - head = (u8 *) skb->data; - memcpy(&header, head, sizeof(header)); - le32_to_cpus(&header); - packet = head + sizeof(header); + while (offset + sizeof(u32) < skb->len) { + struct sk_buff *ax_skb; + u16 size; + u32 header = get_unaligned_le32(skb->data + offset); - skb_pull(skb, 4); - - while (skb->len > 0) { - if ((header & 0x07ff) != ((~header >> 16) & 0x07ff)) - netdev_err(dev->net, "asix_rx_fixup() Bad Header Length\n"); + offset += sizeof(u32); /* get the packet length */ - size = (u16) (header & 0x000007ff); - - if ((skb->len) - ((size + 1) & 0xfffe) == 0) { - u8 alignment = (unsigned long)skb->data & 0x3; - if (alignment != 0x2) { - /* - * not 16bit aligned so use the room provided by - * the 32 bit header to align the data - * - * note we want 16bit alignment as MAC header is - * 14bytes thus ip header will be aligned on - * 32bit boundary so accessing ipheader elements - * using a cast to struct ip header wont cause - * an unaligned accesses. - */ - u8 realignment = (alignment + 2) & 0x3; - memmove(skb->data - realignment, - skb->data, - size); - skb->data -= realignment; - skb_set_tail_pointer(skb, size); - } - return 2; + size = (u16) (header & 0x7ff); + if (size != ((~header >> 16) & 0x07ff)) { + netdev_err(dev->net, "asix_rx_fixup() Bad Header Length\n"); + return 0; } - if (size > dev->net->mtu + ETH_HLEN) { + if ((size > dev->net->mtu + ETH_HLEN) || + (size + offset > skb->len)) { netdev_err(dev->net, "asix_rx_fixup() Bad RX Length %d\n", size); return 0; } - ax_skb = skb_clone(skb, GFP_ATOMIC); - if (ax_skb) { - u8 alignment = (unsigned long)packet & 0x3; - ax_skb->len = size; - - if (alignment != 0x2) { - /* - * not 16bit aligned use the room provided by - * the 32 bit header to align the data - */ - u8 realignment = (alignment + 2) & 0x3; - memmove(packet - realignment, packet, size); - packet -= realignment; - } - ax_skb->data = packet; - skb_set_tail_pointer(ax_skb, size); - usbnet_skb_return(dev, ax_skb); - } else { + ax_skb = netdev_alloc_skb_ip_align(dev->net, size); + if (!ax_skb) return 0; - } - - skb_pull(skb, (size + 1) & 0xfffe); - if (skb->len < sizeof(header)) - break; + skb_put(ax_skb, size); + memcpy(ax_skb->data, skb->data + offset, size); + usbnet_skb_return(dev, ax_skb); - head = (u8 *) skb->data; - memcpy(&header, head, sizeof(header)); - le32_to_cpus(&header); - packet = head + sizeof(header); - skb_pull(skb, 4); + offset += (size + 1) & 0xfffe; } - if (skb->len < 0) { + if (skb->len != offset) { netdev_err(dev->net, "asix_rx_fixup() Bad SKB Length %d\n", skb->len); return 0; @@ -1541,7 +1493,7 @@ static const struct driver_info ax88772_info = { .status = asix_status, .link_reset = ax88772_link_reset, .reset = ax88772_reset, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR, + .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | FLAG_MULTI_PACKET, .rx_fixup = asix_rx_fixup, .tx_fixup = asix_tx_fixup, }; -- cgit v1.2.2 From c84ff1d6dff38058a5bd20cb633d13a0f685788a Mon Sep 17 00:00:00 2001 From: Alexey Orishko Date: Wed, 14 Mar 2012 11:26:10 +0000 Subject: cdc_ncm: reduce driver latency in the data path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes: - use high resolution timer latency became approx. 10-15 times less than before - use taklet for sending remaining data in tx path Tested on Intel/ARM. Reviewed-by: Sjur Brændeland Tested-by: Dmitry Tarnyagin Signed-off-by: Alexey Orishko Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ncm.c | 94 +++++++++++++++++++++++++---------------------- 1 file changed, 51 insertions(+), 43 deletions(-) (limited to 'drivers/net/usb') diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 81f86b8ea551..a8e2a62c2958 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -1,7 +1,7 @@ /* * cdc_ncm.c * - * Copyright (C) ST-Ericsson 2010-2011 + * Copyright (C) ST-Ericsson 2010-2012 * Contact: Alexey Orishko * Original author: Hans Petter Selasky * @@ -47,13 +47,12 @@ #include #include #include -#include -#include +#include #include #include #include -#define DRIVER_VERSION "04-Aug-2011" +#define DRIVER_VERSION "14-Mar-2012" /* CDC NCM subclass 3.2.1 */ #define USB_CDC_NCM_NDP16_LENGTH_MIN 0x10 @@ -81,6 +80,8 @@ /* 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 \ @@ -97,7 +98,8 @@ struct cdc_ncm_ctx { struct cdc_ncm_data rx_ncm; struct cdc_ncm_data tx_ncm; struct usb_cdc_ncm_ntb_parameters ncm_parm; - struct timer_list tx_timer; + struct hrtimer tx_timer; + struct tasklet_struct bh; const struct usb_cdc_ncm_desc *func_desc; const struct usb_cdc_header_desc *header_desc; @@ -117,6 +119,7 @@ struct cdc_ncm_ctx { struct sk_buff *tx_rem_skb; spinlock_t mtx; + atomic_t stop; u32 tx_timer_pending; u32 tx_curr_offset; @@ -135,7 +138,9 @@ struct cdc_ncm_ctx { u16 connected; }; -static void cdc_ncm_tx_timeout(unsigned long arg); +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); static const struct driver_info cdc_ncm_info; static struct usb_driver cdc_ncm_driver; static const struct ethtool_ops cdc_ncm_ethtool_ops; @@ -441,8 +446,6 @@ static void cdc_ncm_free(struct cdc_ncm_ctx *ctx) if (ctx == NULL) return; - del_timer_sync(&ctx->tx_timer); - if (ctx->tx_rem_skb != NULL) { dev_kfree_skb_any(ctx->tx_rem_skb); ctx->tx_rem_skb = NULL; @@ -469,7 +472,11 @@ static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf) if (ctx == NULL) return -ENODEV; - init_timer(&ctx->tx_timer); + 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.func = cdc_ncm_txpath_bh; + atomic_set(&ctx->stop, 0); spin_lock_init(&ctx->mtx); ctx->netdev = dev->net; @@ -617,6 +624,13 @@ static void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf) if (ctx == NULL) return; /* no setup */ + atomic_set(&ctx->stop, 1); + + if (hrtimer_active(&ctx->tx_timer)) + hrtimer_cancel(&ctx->tx_timer); + + tasklet_kill(&ctx->bh); + /* disconnect master --> disconnect slave */ if (intf == ctx->control && ctx->data) { usb_set_intfdata(ctx->data, NULL); @@ -787,7 +801,7 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb) ctx->tx_curr_last_offset = last_offset; /* set the pending count */ if (n < CDC_NCM_RESTART_TIMER_DATAGRAM_CNT) - ctx->tx_timer_pending = 2; + ctx->tx_timer_pending = CDC_NCM_TIMER_PENDING_CNT; goto exit_no_skb; } else { @@ -867,44 +881,49 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb) /* return skb */ ctx->tx_curr_skb = NULL; + ctx->netdev->stats.tx_packets += ctx->tx_curr_frame_num; return skb_out; exit_no_skb: + /* Start timer, if there is a remaining skb */ + if (ctx->tx_curr_skb != NULL) + cdc_ncm_tx_timeout_start(ctx); return NULL; } static void cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx) { /* start timer, if not already started */ - if (timer_pending(&ctx->tx_timer) == 0) { - ctx->tx_timer.function = &cdc_ncm_tx_timeout; - ctx->tx_timer.data = (unsigned long)ctx; - ctx->tx_timer.expires = jiffies + ((HZ + 999) / 1000); - add_timer(&ctx->tx_timer); - } + if (!(hrtimer_active(&ctx->tx_timer) || atomic_read(&ctx->stop))) + hrtimer_start(&ctx->tx_timer, + ktime_set(0, CDC_NCM_TIMER_INTERVAL), + HRTIMER_MODE_REL); } -static void cdc_ncm_tx_timeout(unsigned long arg) +static enum hrtimer_restart cdc_ncm_tx_timer_cb(struct hrtimer *timer) { - struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)arg; - u8 restart; + struct cdc_ncm_ctx *ctx = + container_of(timer, struct cdc_ncm_ctx, tx_timer); - spin_lock(&ctx->mtx); - if (ctx->tx_timer_pending != 0) { - ctx->tx_timer_pending--; - restart = 1; - } else { - restart = 0; - } + if (!atomic_read(&ctx->stop)) + tasklet_schedule(&ctx->bh); + return HRTIMER_NORESTART; +} - spin_unlock(&ctx->mtx); +static void cdc_ncm_txpath_bh(unsigned long param) +{ + struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)param; - if (restart) { - spin_lock(&ctx->mtx); + spin_lock_bh(&ctx->mtx); + if (ctx->tx_timer_pending != 0) { + ctx->tx_timer_pending--; cdc_ncm_tx_timeout_start(ctx); - spin_unlock(&ctx->mtx); + spin_unlock_bh(&ctx->mtx); } else if (ctx->netdev != NULL) { + spin_unlock_bh(&ctx->mtx); + netif_tx_lock_bh(ctx->netdev); usbnet_start_xmit(NULL, ctx->netdev); + netif_tx_unlock_bh(ctx->netdev); } } @@ -913,7 +932,6 @@ cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) { struct sk_buff *skb_out; struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; - u8 need_timer = 0; /* * The Ethernet API we are using does not support transmitting @@ -925,19 +943,9 @@ cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) if (ctx == NULL) goto error; - spin_lock(&ctx->mtx); + spin_lock_bh(&ctx->mtx); skb_out = cdc_ncm_fill_tx_frame(ctx, skb); - if (ctx->tx_curr_skb != NULL) - need_timer = 1; - - /* Start timer, if there is a remaining skb */ - if (need_timer) - cdc_ncm_tx_timeout_start(ctx); - - if (skb_out) - dev->net->stats.tx_packets += ctx->tx_curr_frame_num; - - spin_unlock(&ctx->mtx); + spin_unlock_bh(&ctx->mtx); return skb_out; error: -- cgit v1.2.2 From 3f658cde9401bd45429ee720cf2c69f0bc5547b9 Mon Sep 17 00:00:00 2001 From: Alexey Orishko Date: Wed, 14 Mar 2012 11:26:11 +0000 Subject: cdc_ncm: fix MTU and max_datagram_size handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes/fixes: - inform device if max_datagram_size was changed by host - max_datagram_size can't be bigger MTU in ETH func descr - fix constants definitions to enable running CAIF service over NCM Tested on Intel/ARM. Reviewed-by: Sjur Brændeland Tested-by: Dmitry Tarnyagin Signed-off-by: Alexey Orishko Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ncm.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) (limited to 'drivers/net/usb') diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index a8e2a62c2958..f8f194658412 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -58,8 +58,8 @@ #define USB_CDC_NCM_NDP16_LENGTH_MIN 0x10 /* Maximum NTB length */ -#define CDC_NCM_NTB_MAX_SIZE_TX 16384 /* bytes */ -#define CDC_NCM_NTB_MAX_SIZE_RX 16384 /* bytes */ +#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 */ @@ -67,13 +67,13 @@ #define CDC_NCM_MIN_TX_PKT 512 /* bytes */ /* Default value for MaxDatagramSize */ -#define CDC_NCM_MAX_DATAGRAM_SIZE 2048 /* bytes */ +#define CDC_NCM_MAX_DATAGRAM_SIZE 8192 /* bytes */ /* * Maximum amount of datagrams in NCM Datagram Pointer Table, not counting * the last NULL entry. Any additional datagrams in NTB would be discarded. */ -#define CDC_NCM_DPT_DATAGRAMS_MAX 32 +#define CDC_NCM_DPT_DATAGRAMS_MAX 40 /* Maximum amount of IN datagrams in NTB */ #define CDC_NCM_DPT_DATAGRAMS_IN_MAX 0 /* unlimited */ @@ -366,27 +366,25 @@ size_err: if (err < 0) { pr_debug("GET_MAX_DATAGRAM_SIZE failed, use size=%u\n", CDC_NCM_MIN_DATAGRAM_SIZE); - kfree(max_datagram_size); } else { ctx->max_datagram_size = le16_to_cpu(*max_datagram_size); /* Check Eth descriptor value */ - if (eth_max_sz < CDC_NCM_MAX_DATAGRAM_SIZE) { - if (ctx->max_datagram_size > eth_max_sz) + if (ctx->max_datagram_size > eth_max_sz) ctx->max_datagram_size = eth_max_sz; - } else { - if (ctx->max_datagram_size > - CDC_NCM_MAX_DATAGRAM_SIZE) - ctx->max_datagram_size = + + if (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 value changed, update device */ - err = usb_control_msg(ctx->udev, + if (ctx->max_datagram_size != + le16_to_cpu(*max_datagram_size)) { + err = usb_control_msg(ctx->udev, usb_sndctrlpipe(ctx->udev, 0), USB_CDC_SET_MAX_DATAGRAM_SIZE, USB_TYPE_CLASS | USB_DIR_OUT @@ -394,14 +392,14 @@ size_err: 0, iface_no, max_datagram_size, 2, 1000); - kfree(max_datagram_size); -max_dgram_err: - if (err < 0) - pr_debug("SET_MAX_DATAGRAM_SIZE failed\n"); + if (err < 0) + pr_debug("SET_MAX_DGRAM_SIZE failed\n"); + } } - + kfree(max_datagram_size); } +max_dgram_err: if (ctx->netdev->mtu != (ctx->max_datagram_size - ETH_HLEN)) ctx->netdev->mtu = ctx->max_datagram_size - ETH_HLEN; -- cgit v1.2.2 From d5ddb4a59ed43b4c569b4efa8b508d50ef140cc6 Mon Sep 17 00:00:00 2001 From: Alexey Orishko Date: Wed, 14 Mar 2012 11:26:12 +0000 Subject: cdc_ncm: avoid discarding datagrams in rx path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes: - removed a limit for amount of datagrams for IN NTB - using pointer to traverse NTB in rx_fixup() - renamed "temp" to "len" in rx_fixup() - do NTB sequence number check in rx path Tested on Intel/ARM. Reviewed-by: Sjur Brændeland Tested-by: Dmitry Tarnyagin Signed-off-by: Alexey Orishko Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ncm.c | 102 +++++++++++++++++++++------------------------- 1 file changed, 47 insertions(+), 55 deletions(-) (limited to 'drivers/net/usb') diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index f8f194658412..7adc9f6b0ea1 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -71,13 +71,10 @@ /* * Maximum amount of datagrams in NCM Datagram Pointer Table, not counting - * the last NULL entry. Any additional datagrams in NTB would be discarded. + * the last NULL entry. */ #define CDC_NCM_DPT_DATAGRAMS_MAX 40 -/* Maximum amount of IN datagrams in NTB */ -#define CDC_NCM_DPT_DATAGRAMS_IN_MAX 0 /* unlimited */ - /* 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 @@ -95,7 +92,6 @@ struct cdc_ncm_data { }; struct cdc_ncm_ctx { - struct cdc_ncm_data rx_ncm; struct cdc_ncm_data tx_ncm; struct usb_cdc_ncm_ntb_parameters ncm_parm; struct hrtimer tx_timer; @@ -135,6 +131,7 @@ struct cdc_ncm_ctx { u16 tx_modulus; u16 tx_ndp_modulus; u16 tx_seq; + u16 rx_seq; u16 connected; }; @@ -956,108 +953,103 @@ error: static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) { struct sk_buff *skb; - struct cdc_ncm_ctx *ctx; - int sumlen; - int actlen; - int temp; + 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; - ctx = (struct cdc_ncm_ctx *)dev->data[0]; if (ctx == NULL) goto error; - actlen = skb_in->len; - sumlen = CDC_NCM_NTB_MAX_SIZE_RX; - - if (actlen < (sizeof(ctx->rx_ncm.nth16) + sizeof(ctx->rx_ncm.ndp16))) { + if (skb_in->len < (sizeof(struct usb_cdc_ncm_nth16) + + sizeof(struct usb_cdc_ncm_ndp16))) { pr_debug("frame too short\n"); goto error; } - memcpy(&(ctx->rx_ncm.nth16), ((u8 *)skb_in->data), - sizeof(ctx->rx_ncm.nth16)); + nth16 = (struct usb_cdc_ncm_nth16 *)skb_in->data; - if (le32_to_cpu(ctx->rx_ncm.nth16.dwSignature) != - USB_CDC_NCM_NTH16_SIGN) { + if (le32_to_cpu(nth16->dwSignature) != USB_CDC_NCM_NTH16_SIGN) { pr_debug("invalid NTH16 signature <%u>\n", - le32_to_cpu(ctx->rx_ncm.nth16.dwSignature)); + le32_to_cpu(nth16->dwSignature)); goto error; } - temp = le16_to_cpu(ctx->rx_ncm.nth16.wBlockLength); - if (temp > sumlen) { - pr_debug("unsupported NTB block length %u/%u\n", temp, sumlen); + len = le16_to_cpu(nth16->wBlockLength); + if (len > ctx->rx_max) { + pr_debug("unsupported NTB block length %u/%u\n", len, + ctx->rx_max); goto error; } - temp = le16_to_cpu(ctx->rx_ncm.nth16.wNdpIndex); - if ((temp + sizeof(ctx->rx_ncm.ndp16)) > actlen) { - pr_debug("invalid DPT16 index\n"); + 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); + + 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)); goto error; } - memcpy(&(ctx->rx_ncm.ndp16), ((u8 *)skb_in->data) + temp, - sizeof(ctx->rx_ncm.ndp16)); + ndp16 = (struct usb_cdc_ncm_ndp16 *)(((u8 *)skb_in->data) + len); - if (le32_to_cpu(ctx->rx_ncm.ndp16.dwSignature) != - USB_CDC_NCM_NDP16_NOCRC_SIGN) { + if (le32_to_cpu(ndp16->dwSignature) != USB_CDC_NCM_NDP16_NOCRC_SIGN) { pr_debug("invalid DPT16 signature <%u>\n", - le32_to_cpu(ctx->rx_ncm.ndp16.dwSignature)); + le32_to_cpu(ndp16->dwSignature)); goto error; } - if (le16_to_cpu(ctx->rx_ncm.ndp16.wLength) < - USB_CDC_NCM_NDP16_LENGTH_MIN) { + if (le16_to_cpu(ndp16->wLength) < USB_CDC_NCM_NDP16_LENGTH_MIN) { pr_debug("invalid DPT16 length <%u>\n", - le32_to_cpu(ctx->rx_ncm.ndp16.dwSignature)); + le32_to_cpu(ndp16->dwSignature)); goto error; } - nframes = ((le16_to_cpu(ctx->rx_ncm.ndp16.wLength) - + nframes = ((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 */ - pr_debug("nframes = %u\n", nframes); + len += sizeof(struct usb_cdc_ncm_ndp16); - temp += sizeof(ctx->rx_ncm.ndp16); - - if ((temp + nframes * (sizeof(struct usb_cdc_ncm_dpe16))) > actlen) { + if ((len + nframes * (sizeof(struct usb_cdc_ncm_dpe16))) > + skb_in->len) { pr_debug("Invalid nframes = %d\n", nframes); goto error; } - if (nframes > CDC_NCM_DPT_DATAGRAMS_MAX) { - pr_debug("Truncating number of frames from %u to %u\n", - nframes, CDC_NCM_DPT_DATAGRAMS_MAX); - nframes = CDC_NCM_DPT_DATAGRAMS_MAX; - } - - memcpy(&(ctx->rx_ncm.dpe16), ((u8 *)skb_in->data) + temp, - nframes * (sizeof(struct usb_cdc_ncm_dpe16))); + dpe16 = (struct usb_cdc_ncm_dpe16 *)(((u8 *)skb_in->data) + len); - for (x = 0; x < nframes; x++) { - offset = le16_to_cpu(ctx->rx_ncm.dpe16[x].wDatagramIndex); - temp = le16_to_cpu(ctx->rx_ncm.dpe16[x].wDatagramLength); + for (x = 0; x < nframes; x++, dpe16++) { + offset = le16_to_cpu(dpe16->wDatagramIndex); + len = le16_to_cpu(dpe16->wDatagramLength); /* * CDC NCM ch. 3.7 * All entries after first NULL entry are to be ignored */ - if ((offset == 0) || (temp == 0)) { + if ((offset == 0) || (len == 0)) { if (!x) goto error; /* empty NTB */ break; } /* sanity checking */ - if (((offset + temp) > actlen) || - (temp > CDC_NCM_MAX_DATAGRAM_SIZE) || (temp < ETH_HLEN)) { + 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, temp, skb_in); + x, offset, len, skb_in); if (!x) goto error; break; @@ -1066,9 +1058,9 @@ static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) skb = skb_clone(skb_in, GFP_ATOMIC); if (!skb) goto error; - skb->len = temp; + skb->len = len; skb->data = ((u8 *)skb_in->data) + offset; - skb_set_tail_pointer(skb, temp); + skb_set_tail_pointer(skb, len); usbnet_skb_return(dev, skb); } } -- cgit v1.2.2 From 1aa9bc5b2f4cf8c48944fb9a607bf1dd674e2c10 Mon Sep 17 00:00:00 2001 From: Alexey Orishko Date: Wed, 14 Mar 2012 04:00:24 +0000 Subject: usbnet: use netif_tx_wake_queue instead of netif_start_queue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If host is going to autosuspend function with two interfaces and if IP packet has arrived in-between of two usbnet_suspend() callbacks, i.e usbnet_resume() is called in-between, tx data flow is stopped. When autosuspend timer expires and device is put to autosuspend again, tx queue is waked up and data can be sent again. This behavior might be repeated several times in a row. Tested on Intel/ARM. Reviewed-by: Sjur Brændeland Tested-by: Dmitry Tarnyagin Signed-off-by: Alexey Orishko Acked-by: Oliver Neukum Signed-off-by: David S. Miller --- drivers/net/usb/usbnet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/usb') diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 83dcc530618e..5394b4eb00b0 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -1535,7 +1535,7 @@ int usbnet_resume (struct usb_interface *intf) if (test_bit(EVENT_DEV_OPEN, &dev->flags)) { if (!(dev->txq.qlen >= TX_QLEN(dev))) - netif_start_queue(dev->net); + netif_tx_wake_all_queues(dev->net); tasklet_schedule (&dev->bh); } } -- cgit v1.2.2 From 74cba4a85e92650f29be0e2d2ba1c282002d1168 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Mon, 19 Mar 2012 07:48:29 +0000 Subject: net: qmi_wwan: fix build error due to cdc-wdm dependecy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: drivers/built-in.o: In function `qmi_wwan_bind_shared': qmi_wwan.c:(.text+0x25b686): undefined reference to `usb_cdc_wdm_register' make[1]: *** [.tmp_vmlinux1] Error 1 Reported-by: Randy Dunlap Signed-off-by: Bjørn Mork Acked-by: Randy Dunlap Signed-off-by: David S. Miller --- drivers/net/usb/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/usb') diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index 4bad899fb38f..833e32f8d63b 100644 --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -401,6 +401,7 @@ config USB_NET_KALMIA config USB_NET_QMI_WWAN tristate "QMI WWAN driver for Qualcomm MSM based 3G and LTE modems" depends on USB_USBNET + select USB_WDM help Support WWAN LTE/3G devices based on Qualcomm Mobile Data Modem (MDM) chipsets. Examples of such devices are -- cgit v1.2.2