summaryrefslogtreecommitdiffstats
path: root/drivers/net/usb
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/usb')
-rw-r--r--drivers/net/usb/Kconfig22
-rw-r--r--drivers/net/usb/Makefile1
-rw-r--r--drivers/net/usb/asix.c31
-rw-r--r--drivers/net/usb/catc.c17
-rw-r--r--drivers/net/usb/cdc-phonet.c13
-rw-r--r--drivers/net/usb/cdc_eem.c13
-rw-r--r--drivers/net/usb/cdc_ether.c19
-rw-r--r--drivers/net/usb/cdc_ncm.c19
-rw-r--r--drivers/net/usb/cdc_subset.c12
-rw-r--r--drivers/net/usb/cx82310_eth.c12
-rw-r--r--drivers/net/usb/dm9601.c13
-rw-r--r--drivers/net/usb/gl620a.c12
-rw-r--r--drivers/net/usb/int51x1.c12
-rw-r--r--drivers/net/usb/ipheth.c22
-rw-r--r--drivers/net/usb/kalmia.c12
-rw-r--r--drivers/net/usb/kaweth.c30
-rw-r--r--drivers/net/usb/lg-vl600.c12
-rw-r--r--drivers/net/usb/mcs7830.c12
-rw-r--r--drivers/net/usb/net1080.c12
-rw-r--r--drivers/net/usb/plusb.c12
-rw-r--r--drivers/net/usb/qmi_wwan.c228
-rw-r--r--drivers/net/usb/rndis_host.c12
-rw-r--r--drivers/net/usb/rtl8150.c15
-rw-r--r--drivers/net/usb/sierra_net.c21
-rw-r--r--drivers/net/usb/smsc75xx.c12
-rw-r--r--drivers/net/usb/smsc95xx.c12
-rw-r--r--drivers/net/usb/zaurus.c12
27 files changed, 302 insertions, 318 deletions
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index 23357612793..4bad899fb38 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 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 dbdca225b84..8e84f5bdd6c 100644
--- a/drivers/net/usb/asix.c
+++ b/drivers/net/usb/asix.c
@@ -376,7 +376,7 @@ static int asix_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
skb_pull(skb, (size + 1) & 0xfffe);
- if (skb->len == 0)
+ if (skb->len < sizeof(header))
break;
head = (u8 *) skb->data;
@@ -978,6 +978,7 @@ static int ax88772_link_reset(struct usbnet *dev)
static int ax88772_reset(struct usbnet *dev)
{
+ struct asix_data *data = (struct asix_data *)&dev->data;
int ret, embd_phy;
u16 rx_ctl;
@@ -1055,6 +1056,13 @@ static int ax88772_reset(struct usbnet *dev)
goto out;
}
+ /* Rewrite MAC address */
+ memcpy(data->mac_addr, dev->net->dev_addr, ETH_ALEN);
+ ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN,
+ data->mac_addr);
+ if (ret < 0)
+ goto out;
+
/* Set RX_CTL to default values with 2k buffer, and enable cactus */
ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL);
if (ret < 0)
@@ -1152,7 +1160,7 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
return 0;
}
-static struct ethtool_ops ax88178_ethtool_ops = {
+static const struct ethtool_ops ax88178_ethtool_ops = {
.get_drvinfo = asix_get_drvinfo,
.get_link = asix_get_link,
.get_msglevel = usbnet_get_msglevel,
@@ -1320,6 +1328,13 @@ static int ax88178_reset(struct usbnet *dev)
if (ret < 0)
return ret;
+ /* Rewrite MAC address */
+ memcpy(data->mac_addr, dev->net->dev_addr, ETH_ALEN);
+ ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN,
+ data->mac_addr);
+ if (ret < 0)
+ return ret;
+
ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL);
if (ret < 0)
return ret;
@@ -1678,17 +1693,7 @@ static struct usb_driver asix_driver = {
.supports_autosuspend = 1,
};
-static int __init asix_init(void)
-{
- return usb_register(&asix_driver);
-}
-module_init(asix_init);
-
-static void __exit asix_exit(void)
-{
- usb_deregister(&asix_driver);
-}
-module_exit(asix_exit);
+module_usb_driver(asix_driver);
MODULE_AUTHOR("David Hollis");
MODULE_VERSION(DRIVER_VERSION);
diff --git a/drivers/net/usb/catc.c b/drivers/net/usb/catc.c
index a68272c9338..182cfb4aeb1 100644
--- a/drivers/net/usb/catc.c
+++ b/drivers/net/usb/catc.c
@@ -949,19 +949,4 @@ static struct usb_driver catc_driver = {
.id_table = catc_id_table,
};
-static int __init catc_init(void)
-{
- int result = usb_register(&catc_driver);
- if (result == 0)
- printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
- DRIVER_DESC "\n");
- return result;
-}
-
-static void __exit catc_exit(void)
-{
- usb_deregister(&catc_driver);
-}
-
-module_init(catc_init);
-module_exit(catc_exit);
+module_usb_driver(catc_driver);
diff --git a/drivers/net/usb/cdc-phonet.c b/drivers/net/usb/cdc-phonet.c
index 331e44056f5..790cbdea739 100644
--- a/drivers/net/usb/cdc-phonet.c
+++ b/drivers/net/usb/cdc-phonet.c
@@ -457,18 +457,7 @@ static struct usb_driver usbpn_driver = {
.id_table = usbpn_ids,
};
-static int __init usbpn_init(void)
-{
- return usb_register(&usbpn_driver);
-}
-
-static void __exit usbpn_exit(void)
-{
- usb_deregister(&usbpn_driver);
-}
-
-module_init(usbpn_init);
-module_exit(usbpn_exit);
+module_usb_driver(usbpn_driver);
MODULE_AUTHOR("Remi Denis-Courmont");
MODULE_DESCRIPTION("USB CDC Phonet host interface");
diff --git a/drivers/net/usb/cdc_eem.c b/drivers/net/usb/cdc_eem.c
index 882f53f708d..439690be519 100644
--- a/drivers/net/usb/cdc_eem.c
+++ b/drivers/net/usb/cdc_eem.c
@@ -369,18 +369,7 @@ static struct usb_driver eem_driver = {
.resume = usbnet_resume,
};
-
-static int __init eem_init(void)
-{
- return usb_register(&eem_driver);
-}
-module_init(eem_init);
-
-static void __exit eem_exit(void)
-{
- usb_deregister(&eem_driver);
-}
-module_exit(eem_exit);
+module_usb_driver(eem_driver);
MODULE_AUTHOR("Omar Laazimani <omar.oberthur@gmail.com>");
MODULE_DESCRIPTION("USB CDC EEM");
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index 99ed6eb4dfa..41a61efc331 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -425,6 +425,9 @@ int usbnet_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
int status;
struct cdc_state *info = (void *) &dev->data;
+ BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data)
+ < sizeof(struct cdc_state)));
+
status = usbnet_generic_cdc_bind(dev, intf);
if (status < 0)
return status;
@@ -615,21 +618,7 @@ static struct usb_driver cdc_driver = {
.supports_autosuspend = 1,
};
-
-static int __init cdc_init(void)
-{
- BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data)
- < sizeof(struct cdc_state)));
-
- return usb_register(&cdc_driver);
-}
-module_init(cdc_init);
-
-static void __exit cdc_exit(void)
-{
- usb_deregister(&cdc_driver);
-}
-module_exit(cdc_exit);
+module_usb_driver(cdc_driver);
MODULE_AUTHOR("David Brownell");
MODULE_DESCRIPTION("USB CDC Ethernet devices");
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index 009dd0f1853..3a539a9cac5 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -138,7 +138,7 @@ struct cdc_ncm_ctx {
static void cdc_ncm_tx_timeout(unsigned long arg);
static const struct driver_info cdc_ncm_info;
static struct usb_driver cdc_ncm_driver;
-static struct ethtool_ops cdc_ncm_ethtool_ops;
+static const struct ethtool_ops cdc_ncm_ethtool_ops;
static const struct usb_device_id cdc_devs[] = {
{ USB_INTERFACE_INFO(USB_CLASS_COMM,
@@ -1220,7 +1220,7 @@ static struct usb_driver cdc_ncm_driver = {
.supports_autosuspend = 1,
};
-static struct ethtool_ops cdc_ncm_ethtool_ops = {
+static const struct ethtool_ops cdc_ncm_ethtool_ops = {
.get_drvinfo = cdc_ncm_get_drvinfo,
.get_link = usbnet_get_link,
.get_msglevel = usbnet_get_msglevel,
@@ -1230,20 +1230,7 @@ static struct ethtool_ops cdc_ncm_ethtool_ops = {
.nway_reset = usbnet_nway_reset,
};
-static int __init cdc_ncm_init(void)
-{
- printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION "\n");
- return usb_register(&cdc_ncm_driver);
-}
-
-module_init(cdc_ncm_init);
-
-static void __exit cdc_ncm_exit(void)
-{
- usb_deregister(&cdc_ncm_driver);
-}
-
-module_exit(cdc_ncm_exit);
+module_usb_driver(cdc_ncm_driver);
MODULE_AUTHOR("Hans Petter Selasky");
MODULE_DESCRIPTION("USB CDC NCM host driver");
diff --git a/drivers/net/usb/cdc_subset.c b/drivers/net/usb/cdc_subset.c
index fc5f13d47ad..b403d934e4e 100644
--- a/drivers/net/usb/cdc_subset.c
+++ b/drivers/net/usb/cdc_subset.c
@@ -338,17 +338,7 @@ static struct usb_driver cdc_subset_driver = {
.id_table = products,
};
-static int __init cdc_subset_init(void)
-{
- return usb_register(&cdc_subset_driver);
-}
-module_init(cdc_subset_init);
-
-static void __exit cdc_subset_exit(void)
-{
- usb_deregister(&cdc_subset_driver);
-}
-module_exit(cdc_subset_exit);
+module_usb_driver(cdc_subset_driver);
MODULE_AUTHOR("David Brownell");
MODULE_DESCRIPTION("Simple 'CDC Subset' USB networking links");
diff --git a/drivers/net/usb/cx82310_eth.c b/drivers/net/usb/cx82310_eth.c
index 8969f124c18..0e0531356e6 100644
--- a/drivers/net/usb/cx82310_eth.c
+++ b/drivers/net/usb/cx82310_eth.c
@@ -329,17 +329,7 @@ static struct usb_driver cx82310_driver = {
.resume = usbnet_resume,
};
-static int __init cx82310_init(void)
-{
- return usb_register(&cx82310_driver);
-}
-module_init(cx82310_init);
-
-static void __exit cx82310_exit(void)
-{
- usb_deregister(&cx82310_driver);
-}
-module_exit(cx82310_exit);
+module_usb_driver(cx82310_driver);
MODULE_AUTHOR("Ondrej Zary");
MODULE_DESCRIPTION("Conexant CX82310-based ADSL router USB ethernet driver");
diff --git a/drivers/net/usb/dm9601.c b/drivers/net/usb/dm9601.c
index fbc0e4def76..b97226318ea 100644
--- a/drivers/net/usb/dm9601.c
+++ b/drivers/net/usb/dm9601.c
@@ -672,18 +672,7 @@ static struct usb_driver dm9601_driver = {
.resume = usbnet_resume,
};
-static int __init dm9601_init(void)
-{
- return usb_register(&dm9601_driver);
-}
-
-static void __exit dm9601_exit(void)
-{
- usb_deregister(&dm9601_driver);
-}
-
-module_init(dm9601_init);
-module_exit(dm9601_exit);
+module_usb_driver(dm9601_driver);
MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>");
MODULE_DESCRIPTION("Davicom DM9601 USB 1.1 ethernet devices");
diff --git a/drivers/net/usb/gl620a.c b/drivers/net/usb/gl620a.c
index c4cfd1dea88..38266bdae26 100644
--- a/drivers/net/usb/gl620a.c
+++ b/drivers/net/usb/gl620a.c
@@ -227,17 +227,7 @@ static struct usb_driver gl620a_driver = {
.resume = usbnet_resume,
};
-static int __init usbnet_init(void)
-{
- return usb_register(&gl620a_driver);
-}
-module_init(usbnet_init);
-
-static void __exit usbnet_exit(void)
-{
- usb_deregister(&gl620a_driver);
-}
-module_exit(usbnet_exit);
+module_usb_driver(gl620a_driver);
MODULE_AUTHOR("Jiun-Jie Huang");
MODULE_DESCRIPTION("GL620-USB-A Host-to-Host Link cables");
diff --git a/drivers/net/usb/int51x1.c b/drivers/net/usb/int51x1.c
index 131ac6c172f..12a22a453ff 100644
--- a/drivers/net/usb/int51x1.c
+++ b/drivers/net/usb/int51x1.c
@@ -238,17 +238,7 @@ static struct usb_driver int51x1_driver = {
.resume = usbnet_resume,
};
-static int __init int51x1_init(void)
-{
- return usb_register(&int51x1_driver);
-}
-module_init(int51x1_init);
-
-static void __exit int51x1_exit(void)
-{
- usb_deregister(&int51x1_driver);
-}
-module_exit(int51x1_exit);
+module_usb_driver(int51x1_driver);
MODULE_AUTHOR("Peter Holik");
MODULE_DESCRIPTION("Intellon usb powerline adapter");
diff --git a/drivers/net/usb/ipheth.c b/drivers/net/usb/ipheth.c
index 13c1f044b40..e84662db51c 100644
--- a/drivers/net/usb/ipheth.c
+++ b/drivers/net/usb/ipheth.c
@@ -420,7 +420,7 @@ static u32 ipheth_ethtool_op_get_link(struct net_device *net)
return netif_carrier_ok(dev->net);
}
-static struct ethtool_ops ops = {
+static const struct ethtool_ops ops = {
.get_link = ipheth_ethtool_op_get_link
};
@@ -543,25 +543,7 @@ static struct usb_driver ipheth_driver = {
.id_table = ipheth_table,
};
-static int __init ipheth_init(void)
-{
- int retval;
-
- retval = usb_register(&ipheth_driver);
- if (retval) {
- err("usb_register failed: %d", retval);
- return retval;
- }
- return 0;
-}
-
-static void __exit ipheth_exit(void)
-{
- usb_deregister(&ipheth_driver);
-}
-
-module_init(ipheth_init);
-module_exit(ipheth_exit);
+module_usb_driver(ipheth_driver);
MODULE_AUTHOR("Diego Giagio <diego@giagio.com>");
MODULE_DESCRIPTION("Apple iPhone USB Ethernet driver");
diff --git a/drivers/net/usb/kalmia.c b/drivers/net/usb/kalmia.c
index 5a6d0f88f43..7562649b3d6 100644
--- a/drivers/net/usb/kalmia.c
+++ b/drivers/net/usb/kalmia.c
@@ -375,17 +375,7 @@ static struct usb_driver kalmia_driver = {
.resume = usbnet_resume
};
-static int __init kalmia_init(void)
-{
- return usb_register(&kalmia_driver);
-}
-module_init( kalmia_init);
-
-static void __exit kalmia_exit(void)
-{
- usb_deregister(&kalmia_driver);
-}
-module_exit( kalmia_exit);
+module_usb_driver(kalmia_driver);
MODULE_AUTHOR("Marius Bjoernstad Kotsbak <marius@kotsbak.com>");
MODULE_DESCRIPTION("Samsung Kalmia USB network driver");
diff --git a/drivers/net/usb/kaweth.c b/drivers/net/usb/kaweth.c
index 582ca2dfa5f..d034d9c4254 100644
--- a/drivers/net/usb/kaweth.c
+++ b/drivers/net/usb/kaweth.c
@@ -1324,32 +1324,4 @@ static int kaweth_internal_control_msg(struct usb_device *usb_dev,
}
}
-
-/****************************************************************
- * kaweth_init
- ****************************************************************/
-static int __init kaweth_init(void)
-{
- dbg("Driver loading");
- return usb_register(&kaweth_driver);
-}
-
-/****************************************************************
- * kaweth_exit
- ****************************************************************/
-static void __exit kaweth_exit(void)
-{
- usb_deregister(&kaweth_driver);
-}
-
-module_init(kaweth_init);
-module_exit(kaweth_exit);
-
-
-
-
-
-
-
-
-
+module_usb_driver(kaweth_driver);
diff --git a/drivers/net/usb/lg-vl600.c b/drivers/net/usb/lg-vl600.c
index 9c26c6390d6..45a981fde43 100644
--- a/drivers/net/usb/lg-vl600.c
+++ b/drivers/net/usb/lg-vl600.c
@@ -346,17 +346,7 @@ static struct usb_driver lg_vl600_driver = {
.resume = usbnet_resume,
};
-static int __init vl600_init(void)
-{
- return usb_register(&lg_vl600_driver);
-}
-module_init(vl600_init);
-
-static void __exit vl600_exit(void)
-{
- usb_deregister(&lg_vl600_driver);
-}
-module_exit(vl600_exit);
+module_usb_driver(lg_vl600_driver);
MODULE_AUTHOR("Anrzej Zaborowski");
MODULE_DESCRIPTION("LG-VL600 modem's ethernet link");
diff --git a/drivers/net/usb/mcs7830.c b/drivers/net/usb/mcs7830.c
index db2cb74bf85..a29aa9cf9f6 100644
--- a/drivers/net/usb/mcs7830.c
+++ b/drivers/net/usb/mcs7830.c
@@ -692,17 +692,7 @@ static struct usb_driver mcs7830_driver = {
.reset_resume = mcs7830_reset_resume,
};
-static int __init mcs7830_init(void)
-{
- return usb_register(&mcs7830_driver);
-}
-module_init(mcs7830_init);
-
-static void __exit mcs7830_exit(void)
-{
- usb_deregister(&mcs7830_driver);
-}
-module_exit(mcs7830_exit);
+module_usb_driver(mcs7830_driver);
MODULE_DESCRIPTION("USB to network adapter MCS7830)");
MODULE_LICENSE("GPL");
diff --git a/drivers/net/usb/net1080.c b/drivers/net/usb/net1080.c
index 01db4602a39..83f965cb69e 100644
--- a/drivers/net/usb/net1080.c
+++ b/drivers/net/usb/net1080.c
@@ -589,17 +589,7 @@ static struct usb_driver net1080_driver = {
.resume = usbnet_resume,
};
-static int __init net1080_init(void)
-{
- return usb_register(&net1080_driver);
-}
-module_init(net1080_init);
-
-static void __exit net1080_exit(void)
-{
- usb_deregister(&net1080_driver);
-}
-module_exit(net1080_exit);
+module_usb_driver(net1080_driver);
MODULE_AUTHOR("David Brownell");
MODULE_DESCRIPTION("NetChip 1080 based USB Host-to-Host Links");
diff --git a/drivers/net/usb/plusb.c b/drivers/net/usb/plusb.c
index 217aec8a768..b2b035e2997 100644
--- a/drivers/net/usb/plusb.c
+++ b/drivers/net/usb/plusb.c
@@ -154,17 +154,7 @@ static struct usb_driver plusb_driver = {
.resume = usbnet_resume,
};
-static int __init plusb_init(void)
-{
- return usb_register(&plusb_driver);
-}
-module_init(plusb_init);
-
-static void __exit plusb_exit(void)
-{
- usb_deregister(&plusb_driver);
-}
-module_exit(plusb_exit);
+module_usb_driver(plusb_driver);
MODULE_AUTHOR("David Brownell");
MODULE_DESCRIPTION("Prolific PL-2301/2302/25A1 USB Host to Host Link Driver");
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
new file mode 100644
index 00000000000..739e6de7abc
--- /dev/null
+++ b/drivers/net/usb/qmi_wwan.c
@@ -0,0 +1,228 @@
+/*
+ * 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>
+
+/* 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 <bjorn@mork.no>");
+MODULE_DESCRIPTION("Qualcomm MSM Interface (QMI) WWAN driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
index 255d6a424a6..c8f1b5b3aff 100644
--- a/drivers/net/usb/rndis_host.c
+++ b/drivers/net/usb/rndis_host.c
@@ -635,17 +635,7 @@ static struct usb_driver rndis_driver = {
.resume = usbnet_resume,
};
-static int __init rndis_init(void)
-{
- return usb_register(&rndis_driver);
-}
-module_init(rndis_init);
-
-static void __exit rndis_exit(void)
-{
- usb_deregister(&rndis_driver);
-}
-module_exit(rndis_exit);
+module_usb_driver(rndis_driver);
MODULE_AUTHOR("David Brownell");
MODULE_DESCRIPTION("USB Host side RNDIS driver");
diff --git a/drivers/net/usb/rtl8150.c b/drivers/net/usb/rtl8150.c
index bf8c84d0adf..0710b4ca925 100644
--- a/drivers/net/usb/rtl8150.c
+++ b/drivers/net/usb/rtl8150.c
@@ -978,20 +978,7 @@ static struct usb_driver rtl8150_driver = {
.resume = rtl8150_resume
};
-static int __init usb_rtl8150_init(void)
-{
- printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
- DRIVER_DESC "\n");
- return usb_register(&rtl8150_driver);
-}
-
-static void __exit usb_rtl8150_exit(void)
-{
- usb_deregister(&rtl8150_driver);
-}
-
-module_init(usb_rtl8150_init);
-module_exit(usb_rtl8150_exit);
+module_usb_driver(rtl8150_driver);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/net/usb/sierra_net.c b/drivers/net/usb/sierra_net.c
index ed1b4321058..b59cf20c781 100644
--- a/drivers/net/usb/sierra_net.c
+++ b/drivers/net/usb/sierra_net.c
@@ -618,7 +618,7 @@ static u32 sierra_net_get_link(struct net_device *net)
return sierra_net_get_private(dev)->link_up && netif_running(net);
}
-static struct ethtool_ops sierra_net_ethtool_ops = {
+static const struct ethtool_ops sierra_net_ethtool_ops = {
.get_drvinfo = sierra_net_get_drvinfo,
.get_link = sierra_net_get_link,
.get_msglevel = usbnet_get_msglevel,
@@ -900,6 +900,9 @@ struct sk_buff *sierra_net_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
u16 len;
bool need_tail;
+ BUILD_BUG_ON(FIELD_SIZEOF(struct usbnet, data)
+ < sizeof(struct cdc_state));
+
dev_dbg(&dev->udev->dev, "%s", __func__);
if (priv->link_up && check_ethip_packet(skb, dev) && is_ip(skb)) {
/* enough head room as is? */
@@ -981,21 +984,7 @@ static struct usb_driver sierra_net_driver = {
.no_dynamic_id = 1,
};
-static int __init sierra_net_init(void)
-{
- BUILD_BUG_ON(FIELD_SIZEOF(struct usbnet, data)
- < sizeof(struct cdc_state));
-
- return usb_register(&sierra_net_driver);
-}
-
-static void __exit sierra_net_exit(void)
-{
- usb_deregister(&sierra_net_driver);
-}
-
-module_exit(sierra_net_exit);
-module_init(sierra_net_init);
+module_usb_driver(sierra_net_driver);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c
index 0d5da82f0ff..3b017bbd2a2 100644
--- a/drivers/net/usb/smsc75xx.c
+++ b/drivers/net/usb/smsc75xx.c
@@ -1238,17 +1238,7 @@ static struct usb_driver smsc75xx_driver = {
.disconnect = usbnet_disconnect,
};
-static int __init smsc75xx_init(void)
-{
- return usb_register(&smsc75xx_driver);
-}
-module_init(smsc75xx_init);
-
-static void __exit smsc75xx_exit(void)
-{
- usb_deregister(&smsc75xx_driver);
-}
-module_exit(smsc75xx_exit);
+module_usb_driver(smsc75xx_driver);
MODULE_AUTHOR("Nancy Lin");
MODULE_AUTHOR("Steve Glendinning <steve.glendinning@smsc.com>");
diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c
index db217ad66f2..d45520e6dd4 100644
--- a/drivers/net/usb/smsc95xx.c
+++ b/drivers/net/usb/smsc95xx.c
@@ -1298,17 +1298,7 @@ static struct usb_driver smsc95xx_driver = {
.disconnect = usbnet_disconnect,
};
-static int __init smsc95xx_init(void)
-{
- return usb_register(&smsc95xx_driver);
-}
-module_init(smsc95xx_init);
-
-static void __exit smsc95xx_exit(void)
-{
- usb_deregister(&smsc95xx_driver);
-}
-module_exit(smsc95xx_exit);
+module_usb_driver(smsc95xx_driver);
MODULE_AUTHOR("Nancy Lin");
MODULE_AUTHOR("Steve Glendinning <steve.glendinning@smsc.com>");
diff --git a/drivers/net/usb/zaurus.c b/drivers/net/usb/zaurus.c
index 1a2234c2051..f701d412708 100644
--- a/drivers/net/usb/zaurus.c
+++ b/drivers/net/usb/zaurus.c
@@ -362,17 +362,7 @@ static struct usb_driver zaurus_driver = {
.resume = usbnet_resume,
};
-static int __init zaurus_init(void)
-{
- return usb_register(&zaurus_driver);
-}
-module_init(zaurus_init);
-
-static void __exit zaurus_exit(void)
-{
- usb_deregister(&zaurus_driver);
-}
-module_exit(zaurus_exit);
+module_usb_driver(zaurus_driver);
MODULE_AUTHOR("Pavel Machek, David Brownell");
MODULE_DESCRIPTION("Sharp Zaurus PDA, and compatible products");