diff options
Diffstat (limited to 'drivers/net/usb')
-rw-r--r-- | drivers/net/usb/Kconfig | 23 | ||||
-rw-r--r-- | drivers/net/usb/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/usb/asix.c | 92 | ||||
-rw-r--r-- | drivers/net/usb/cdc_ncm.c | 236 | ||||
-rw-r--r-- | drivers/net/usb/hso.c | 2 | ||||
-rw-r--r-- | drivers/net/usb/kaweth.c | 8 | ||||
-rw-r--r-- | drivers/net/usb/mcs7830.c | 2 | ||||
-rw-r--r-- | drivers/net/usb/pegasus.c | 4 | ||||
-rw-r--r-- | drivers/net/usb/qmi_wwan.c | 478 | ||||
-rw-r--r-- | drivers/net/usb/rtl8150.c | 4 | ||||
-rw-r--r-- | drivers/net/usb/smsc75xx.c | 2 | ||||
-rw-r--r-- | drivers/net/usb/smsc95xx.c | 2 | ||||
-rw-r--r-- | drivers/net/usb/usbnet.c | 21 |
13 files changed, 661 insertions, 214 deletions
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index 23357612793..833e32f8d63 100644 --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -398,6 +398,28 @@ 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 + select USB_WDM + 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 +483,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 c203fa21f6b..a2e2d72c52a 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/asix.c b/drivers/net/usb/asix.c index 8e84f5bdd6c..5ee032cafad 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); + skb_put(ax_skb, size); + memcpy(ax_skb->data, skb->data + offset, size); + usbnet_skb_return(dev, ax_skb); - if (skb->len < sizeof(header)) - break; - - 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, }; @@ -1599,6 +1551,10 @@ static const struct usb_device_id products [] = { USB_DEVICE (0x6189, 0x182d), .driver_info = (unsigned long) &ax8817x_info, }, { + // Sitecom LN-031 "USB 2.0 10/100/1000 Ethernet adapter" + USB_DEVICE (0x0df6, 0x0056), + .driver_info = (unsigned long) &ax88178_info, +}, { // corega FEther USB2-TX USB_DEVICE (0x07aa, 0x0017), .driver_info = (unsigned long) &ax8817x_info, diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 3a539a9cac5..7adc9f6b0ea 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 <alexey.orishko@stericsson.com> * Original author: Hans Petter Selasky <hans.petter.selasky@stericsson.com> * @@ -47,20 +47,19 @@ #include <linux/mii.h> #include <linux/crc32.h> #include <linux/usb.h> -#include <linux/timer.h> -#include <linux/spinlock.h> +#include <linux/hrtimer.h> #include <linux/atomic.h> #include <linux/usb/usbnet.h> #include <linux/usb/cdc.h> -#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 /* 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 */ @@ -68,19 +67,18 @@ #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. + * the last NULL entry. */ -#define CDC_NCM_DPT_DATAGRAMS_MAX 32 - -/* Maximum amount of IN datagrams in NTB */ -#define CDC_NCM_DPT_DATAGRAMS_IN_MAX 0 /* unlimited */ +#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 \ @@ -94,10 +92,10 @@ 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 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 +115,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; @@ -132,10 +131,13 @@ struct cdc_ncm_ctx { u16 tx_modulus; u16 tx_ndp_modulus; u16 tx_seq; + u16 rx_seq; 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; @@ -361,27 +363,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 @@ -389,14 +389,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; @@ -441,8 +441,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 +467,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; @@ -579,11 +581,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); @@ -621,6 +619,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); @@ -791,7 +796,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 { @@ -871,44 +876,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); } } @@ -917,7 +927,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 @@ -929,19 +938,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: @@ -954,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); - - temp += sizeof(ctx->rx_ncm.ndp16); + len += sizeof(struct usb_cdc_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; @@ -1064,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); } } diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index e1324b4a0f6..2d2a6882ba3 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -3313,7 +3313,6 @@ static int __init hso_init(void) /* fill in all needed values */ tty_drv->magic = TTY_DRIVER_MAGIC; - tty_drv->owner = THIS_MODULE; tty_drv->driver_name = driver_name; tty_drv->name = tty_filename; @@ -3322,7 +3321,6 @@ static int __init hso_init(void) tty_drv->major = tty_major; tty_drv->minor_start = 0; - tty_drv->num = HSO_SERIAL_TTY_MINORS; tty_drv->type = TTY_DRIVER_TYPE_SERIAL; tty_drv->subtype = SERIAL_TYPE_NORMAL; tty_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; diff --git a/drivers/net/usb/kaweth.c b/drivers/net/usb/kaweth.c index d034d9c4254..df2a2cf35a9 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, diff --git a/drivers/net/usb/mcs7830.c b/drivers/net/usb/mcs7830.c index a29aa9cf9f6..c434b6ba033 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); diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c index 5d99b8cacd7..75239309232 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/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c new file mode 100644 index 00000000000..aac68f5195c --- /dev/null +++ b/drivers/net/usb/qmi_wwan.c @@ -0,0 +1,478 @@ +/* + * Copyright (c) 2012 Bjørn Mork <bjorn@mork.no> + * + * 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 <linux/module.h> +#include <linux/netdevice.h> +#include <linux/ethtool.h> +#include <linux/mii.h> +#include <linux/usb.h> +#include <linux/usb/cdc.h> +#include <linux/usb/usbnet.h> +#include <linux/usb/cdc-wdm.h> + +/* 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; + atomic_t *pmcount = (void *)&dev->data[1]; + + atomic_set(pmcount, 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; +} + +/* using a counter to merge subdriver requests with our own into a combined state */ +static int qmi_wwan_manage_power(struct usbnet *dev, int on) +{ + atomic_t *pmcount = (void *)&dev->data[1]; + int rv = 0; + + dev_dbg(&dev->intf->dev, "%s() pmcount=%d, on=%d\n", __func__, atomic_read(pmcount), on); + + if ((on && atomic_add_return(1, pmcount) == 1) || (!on && atomic_dec_and_test(pmcount))) { + /* need autopm_get/put here to ensure the usbcore sees the new value */ + rv = usb_autopm_get_interface(dev->intf); + if (rv < 0) + goto err; + dev->intf->needs_remote_wakeup = on; + usb_autopm_put_interface(dev->intf); + } +err: + return rv; +} + +static int qmi_wwan_cdc_wdm_manage_power(struct usb_interface *intf, int on) +{ + struct usbnet *dev = usb_get_intfdata(intf); + return qmi_wwan_manage_power(dev, on); +} + +/* Some devices combine the "control" and "data" functions into a + * single interface with all three endpoints: interrupt + bulk in and + * out + * + * Setting up cdc-wdm as a subdriver owning the interrupt endpoint + * will let it provide userspace access to the encapsulated QMI + * protocol without interfering with the usbnet operations. + */ +static int qmi_wwan_bind_shared(struct usbnet *dev, struct usb_interface *intf) +{ + int rv; + struct usb_driver *subdriver = NULL; + atomic_t *pmcount = (void *)&dev->data[1]; + + /* ZTE makes devices where the interface descriptors and endpoint + * configurations of two or more interfaces are identical, even + * though the functions are completely different. If set, then + * driver_info->data is a bitmap of acceptable interface numbers + * allowing us to bind to one such interface without binding to + * all of them + */ + if (dev->driver_info->data && + !test_bit(intf->cur_altsetting->desc.bInterfaceNumber, &dev->driver_info->data)) { + dev_info(&intf->dev, "not on our whitelist - ignored"); + rv = -ENODEV; + goto err; + } + + atomic_set(pmcount, 0); + + /* collect all three endpoints */ + rv = usbnet_get_endpoints(dev, intf); + if (rv < 0) + goto err; + + /* require interrupt endpoint for subdriver */ + if (!dev->status) { + rv = -EINVAL; + goto err; + } + + subdriver = usb_cdc_wdm_register(intf, &dev->status->desc, 512, &qmi_wwan_cdc_wdm_manage_power); + if (IS_ERR(subdriver)) { + rv = PTR_ERR(subdriver); + goto err; + } + + /* can't let usbnet use the interrupt endpoint */ + dev->status = NULL; + + /* save subdriver struct for suspend/resume wrappers */ + dev->data[0] = (unsigned long)subdriver; + +err: + return rv; +} + +/* Gobi devices uses identical class/protocol codes for all interfaces regardless + * of function. Some of these are CDC ACM like and have the exact same endpoints + * we are looking for. This leaves two possible strategies for identifying the + * correct interface: + * a) hardcoding interface number, or + * b) use the fact that the wwan interface is the only one lacking additional + * (CDC functional) descriptors + * + * Let's see if we can get away with the generic b) solution. + */ +static int qmi_wwan_bind_gobi(struct usbnet *dev, struct usb_interface *intf) +{ + int rv = -EINVAL; + + /* ignore any interface with additional descriptors */ + if (intf->cur_altsetting->extralen) + goto err; + + rv = qmi_wwan_bind_shared(dev, intf); +err: + return rv; +} + +static void qmi_wwan_unbind_shared(struct usbnet *dev, struct usb_interface *intf) +{ + struct usb_driver *subdriver = (void *)dev->data[0]; + + if (subdriver && subdriver->disconnect) + subdriver->disconnect(intf); + + dev->data[0] = (unsigned long)NULL; +} + +/* suspend/resume wrappers calling both usbnet and the cdc-wdm + * subdriver if present. + * + * NOTE: cdc-wdm also supports pre/post_reset, but we cannot provide + * wrappers for those without adding usbnet reset support first. + */ +static int qmi_wwan_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct usbnet *dev = usb_get_intfdata(intf); + struct usb_driver *subdriver = (void *)dev->data[0]; + int ret; + + ret = usbnet_suspend(intf, message); + if (ret < 0) + goto err; + + if (subdriver && subdriver->suspend) + ret = subdriver->suspend(intf, message); + if (ret < 0) + usbnet_resume(intf); +err: + return ret; +} + +static int qmi_wwan_resume(struct usb_interface *intf) +{ + struct usbnet *dev = usb_get_intfdata(intf); + struct usb_driver *subdriver = (void *)dev->data[0]; + int ret = 0; + + if (subdriver && subdriver->resume) + ret = subdriver->resume(intf); + if (ret < 0) + goto err; + ret = usbnet_resume(intf); + if (ret < 0 && subdriver && subdriver->resume && subdriver->suspend) + subdriver->suspend(intf, PMSG_SUSPEND); +err: + return ret; +} + + +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, +}; + +static const struct driver_info qmi_wwan_shared = { + .description = "QMI speaking wwan device with combined interface", + .flags = FLAG_WWAN, + .bind = qmi_wwan_bind_shared, + .unbind = qmi_wwan_unbind_shared, + .manage_power = qmi_wwan_manage_power, +}; + +static const struct driver_info qmi_wwan_gobi = { + .description = "Qualcomm Gobi wwan/QMI device", + .flags = FLAG_WWAN, + .bind = qmi_wwan_bind_gobi, + .unbind = qmi_wwan_unbind_shared, + .manage_power = qmi_wwan_manage_power, +}; + +/* ZTE suck at making USB descriptors */ +static const struct driver_info qmi_wwan_force_int4 = { + .description = "Qualcomm Gobi wwan/QMI device", + .flags = FLAG_WWAN, + .bind = qmi_wwan_bind_gobi, + .unbind = qmi_wwan_unbind_shared, + .manage_power = qmi_wwan_manage_power, + .data = BIT(4), /* interface whitelist bitmap */ +}; + + +#define HUAWEI_VENDOR_ID 0x12D1 +#define QMI_GOBI_DEVICE(vend, prod) \ + USB_DEVICE(vend, prod), \ + .driver_info = (unsigned long)&qmi_wwan_gobi + +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, + }, + { /* Huawei E392, E398 and possibly others in "Windows mode" + * using a combined control and data interface without any CDC + * functional descriptors + */ + .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 = 17, + .driver_info = (unsigned long)&qmi_wwan_shared, + }, + { /* Pantech UML290 */ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x106c, + .idProduct = 0x3718, + .bInterfaceClass = 0xff, + .bInterfaceSubClass = 0xf0, + .bInterfaceProtocol = 0xff, + .driver_info = (unsigned long)&qmi_wwan_shared, + }, + { /* ZTE MF820D */ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x19d2, + .idProduct = 0x0167, + .bInterfaceClass = 0xff, + .bInterfaceSubClass = 0xff, + .bInterfaceProtocol = 0xff, + .driver_info = (unsigned long)&qmi_wwan_force_int4, + }, + {QMI_GOBI_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */ + {QMI_GOBI_DEVICE(0x03f0, 0x1f1d)}, /* HP un2400 Gobi Modem Device */ + {QMI_GOBI_DEVICE(0x03f0, 0x371d)}, /* HP un2430 Mobile Broadband Module */ + {QMI_GOBI_DEVICE(0x04da, 0x250d)}, /* Panasonic Gobi Modem device */ + {QMI_GOBI_DEVICE(0x413c, 0x8172)}, /* Dell Gobi Modem device */ + {QMI_GOBI_DEVICE(0x1410, 0xa001)}, /* Novatel Gobi Modem device */ + {QMI_GOBI_DEVICE(0x0b05, 0x1776)}, /* Asus Gobi Modem device */ + {QMI_GOBI_DEVICE(0x19d2, 0xfff3)}, /* ONDA Gobi Modem device */ + {QMI_GOBI_DEVICE(0x05c6, 0x9001)}, /* Generic Gobi Modem device */ + {QMI_GOBI_DEVICE(0x05c6, 0x9002)}, /* Generic Gobi Modem device */ + {QMI_GOBI_DEVICE(0x05c6, 0x9202)}, /* Generic Gobi Modem device */ + {QMI_GOBI_DEVICE(0x05c6, 0x9203)}, /* Generic Gobi Modem device */ + {QMI_GOBI_DEVICE(0x05c6, 0x9222)}, /* Generic Gobi Modem device */ + {QMI_GOBI_DEVICE(0x05c6, 0x9009)}, /* Generic Gobi Modem device */ + {QMI_GOBI_DEVICE(0x413c, 0x8186)}, /* Dell Gobi 2000 Modem device (N0218, VU936) */ + {QMI_GOBI_DEVICE(0x05c6, 0x920b)}, /* Generic Gobi 2000 Modem device */ + {QMI_GOBI_DEVICE(0x05c6, 0x9225)}, /* Sony Gobi 2000 Modem device (N0279, VU730) */ + {QMI_GOBI_DEVICE(0x05c6, 0x9245)}, /* Samsung Gobi 2000 Modem device (VL176) */ + {QMI_GOBI_DEVICE(0x03f0, 0x251d)}, /* HP Gobi 2000 Modem device (VP412) */ + {QMI_GOBI_DEVICE(0x05c6, 0x9215)}, /* Acer Gobi 2000 Modem device (VP413) */ + {QMI_GOBI_DEVICE(0x05c6, 0x9265)}, /* Asus Gobi 2000 Modem device (VR305) */ + {QMI_GOBI_DEVICE(0x05c6, 0x9235)}, /* Top Global Gobi 2000 Modem device (VR306) */ + {QMI_GOBI_DEVICE(0x05c6, 0x9275)}, /* iRex Technologies Gobi 2000 Modem device (VR307) */ + {QMI_GOBI_DEVICE(0x1199, 0x9001)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */ + {QMI_GOBI_DEVICE(0x1199, 0x9002)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */ + {QMI_GOBI_DEVICE(0x1199, 0x9003)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */ + {QMI_GOBI_DEVICE(0x1199, 0x9004)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */ + {QMI_GOBI_DEVICE(0x1199, 0x9005)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */ + {QMI_GOBI_DEVICE(0x1199, 0x9006)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */ + {QMI_GOBI_DEVICE(0x1199, 0x9007)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */ + {QMI_GOBI_DEVICE(0x1199, 0x9008)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */ + {QMI_GOBI_DEVICE(0x1199, 0x9009)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */ + {QMI_GOBI_DEVICE(0x1199, 0x900a)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */ + {QMI_GOBI_DEVICE(0x1199, 0x9011)}, /* Sierra Wireless Gobi 2000 Modem device (MC8305) */ + {QMI_GOBI_DEVICE(0x16d8, 0x8002)}, /* CMDTech Gobi 2000 Modem device (VU922) */ + {QMI_GOBI_DEVICE(0x05c6, 0x9205)}, /* Gobi 2000 Modem device */ + {QMI_GOBI_DEVICE(0x1199, 0x9013)}, /* Sierra Wireless Gobi 3000 Modem device (MC8355) */ + { } /* 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 = qmi_wwan_suspend, + .resume = qmi_wwan_resume, + .reset_resume = qmi_wwan_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 <bjorn@mork.no>"); +MODULE_DESCRIPTION("Qualcomm MSM Interface (QMI) WWAN driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/usb/rtl8150.c b/drivers/net/usb/rtl8150.c index 0710b4ca925..6dda2fe5b15 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/smsc75xx.c b/drivers/net/usb/smsc75xx.c index 3b017bbd2a2..187d01ccb97 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 d45520e6dd4..5f19f84d349 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"); } diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 81b96e30375..b7b3f5b0d40 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -328,13 +328,13 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags) unsigned long lockflags; size_t size = dev->rx_urb_size; - if ((skb = alloc_skb (size + NET_IP_ALIGN, flags)) == NULL) { + skb = __netdev_alloc_skb_ip_align(dev->net, size, flags); + if (!skb) { netif_dbg(dev, rx_err, dev->net, "no rx skb\n"); usbnet_defer_kevent (dev, EVENT_RX_MEMORY); usb_free_urb (urb); return -ENOMEM; } - skb_reserve (skb, NET_IP_ALIGN); entry = (struct skb_data *) skb->cb; entry->urb = urb; @@ -493,6 +493,7 @@ block: if (netif_running (dev->net) && !test_bit (EVENT_RX_HALT, &dev->flags)) { rx_submit (dev, urb, GFP_ATOMIC); + usb_mark_last_busy(dev->udev); return; } usb_free_urb (urb); @@ -589,6 +590,14 @@ static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q) entry = (struct skb_data *) skb->cb; urb = entry->urb; + /* + * Get reference count of the URB to avoid it to be + * freed during usb_unlink_urb, which may trigger + * use-after-free problem inside usb_unlink_urb since + * usb_unlink_urb is always racing with .complete + * handler(include defer_bh). + */ + usb_get_urb(urb); spin_unlock_irqrestore(&q->lock, flags); // during some PM-driven resume scenarios, // these (async) unlinks complete immediately @@ -597,6 +606,7 @@ static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q) netdev_dbg(dev->net, "unlink urb err, %d\n", retval); else count++; + usb_put_urb(urb); spin_lock_irqsave(&q->lock, flags); } spin_unlock_irqrestore (&q->lock, flags); @@ -1028,7 +1038,6 @@ static void tx_complete (struct urb *urb) } usb_autopm_put_interface_async(dev->intf); - urb->dev = NULL; entry->state = tx_done; defer_bh(dev, skb, &dev->txq); } @@ -1336,10 +1345,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); @@ -1537,7 +1544,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); } } |