summaryrefslogtreecommitdiff
path: root/drivers/net
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/Makefile1
-rw-r--r--drivers/net/caif/Kconfig9
-rw-r--r--drivers/net/caif/Makefile4
-rw-r--r--drivers/net/caif/tegra_caif_sspi.c426
-rw-r--r--drivers/net/usb/Kconfig8
-rw-r--r--drivers/net/usb/Makefile1
-rw-r--r--drivers/net/usb/cdc_ether.c20
-rw-r--r--drivers/net/usb/raw_ip_net.c735
-rw-r--r--drivers/net/usb/smsc95xx.c5
9 files changed, 1209 insertions, 0 deletions
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 6d472016d207..e4a90ac66d09 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -285,6 +285,7 @@ obj-$(CONFIG_USB_USBNET) += usb/
obj-$(CONFIG_USB_ZD1201) += usb/
obj-$(CONFIG_USB_IPHETH) += usb/
obj-$(CONFIG_USB_CDC_PHONET) += usb/
+obj-$(CONFIG_USB_NET_RAW_IP) += usb/
obj-$(CONFIG_WLAN) += wireless/
obj-$(CONFIG_NET_TULIP) += tulip/
diff --git a/drivers/net/caif/Kconfig b/drivers/net/caif/Kconfig
index abf4d7a9dcce..3013a74bbfdd 100644
--- a/drivers/net/caif/Kconfig
+++ b/drivers/net/caif/Kconfig
@@ -47,3 +47,12 @@ config CAIF_HSI
The caif low level driver for CAIF over HSI.
Be aware that if you enable this then you also need to
enable a low-level HSI driver.
+
+config TEGRA_SPI_CAIF
+ tristate "TEGRA specific CAIF SPI transport driver for slave interface"
+ depends on CAIF_SPI_SLAVE && TEGRA_SPI_SLAVE
+ default n
+ ---help---
+ The CAIF Link layer SPI Protocol driver for Tegra Slave SPI interface.
+ This driver implements a platform driver to accommodate for a
+ Tegra Slave SPI device.
diff --git a/drivers/net/caif/Makefile b/drivers/net/caif/Makefile
index 91dff861560f..f30752565b33 100644
--- a/drivers/net/caif/Makefile
+++ b/drivers/net/caif/Makefile
@@ -13,3 +13,7 @@ obj-$(CONFIG_CAIF_SHM) += caif_shm.o
# HSI interface
obj-$(CONFIG_CAIF_HSI) += caif_hsi.o
+
+# Tegra specific SPI slave physical interfaces module
+tegra_cfspi_slave-objs := tegra_caif_sspi.o
+obj-$(CONFIG_TEGRA_SPI_CAIF) += tegra_cfspi_slave.o
diff --git a/drivers/net/caif/tegra_caif_sspi.c b/drivers/net/caif/tegra_caif_sspi.c
new file mode 100644
index 000000000000..c2c15c18f2e4
--- /dev/null
+++ b/drivers/net/caif/tegra_caif_sspi.c
@@ -0,0 +1,426 @@
+/*
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/wait.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+#include <linux/tegra_caif.h>
+#include <mach/spi.h>
+#include <net/caif/caif_spi.h>
+
+MODULE_LICENSE("GPL");
+
+#define SPI_CAIF_PAD_TRANSACTION_SIZE(x) \
+ (((x) > 4) ? ((((x) + 15) / 16) * 16) : (x))
+
+struct sspi_struct {
+ struct cfspi_dev sdev;
+ struct cfspi_xfer *xfer;
+};
+
+static struct sspi_struct slave;
+static struct platform_device slave_device;
+static struct spi_device *tegra_caif_spi_slave_device;
+int tegra_caif_sspi_gpio_spi_int;
+int tegra_caif_sspi_gpio_spi_ss;
+int tegra_caif_sspi_gpio_reset;
+int tegra_caif_sspi_gpio_power;
+int tegra_caif_sspi_gpio_awr;
+int tegra_caif_sspi_gpio_cwr;
+
+
+static int __devinit tegra_caif_spi_slave_probe(struct spi_device *spi);
+
+static int tegra_caif_spi_slave_remove(struct spi_device *spi)
+{
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int tegra_caif_spi_slave_suspend(struct spi_device *spi
+ , pm_message_t mesg)
+{
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+#ifdef CONFIG_PM
+static int tegra_caif_spi_slave_resume(struct spi_device *spi)
+{
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static struct spi_driver tegra_caif_spi_slave_driver = {
+ .driver = {
+ .name = "baseband_spi_slave0.0",
+ .owner = THIS_MODULE,
+ },
+ .probe = tegra_caif_spi_slave_probe,
+ .remove = __devexit_p(tegra_caif_spi_slave_remove),
+#ifdef CONFIG_PM
+ .suspend = tegra_caif_spi_slave_suspend,
+ .resume = tegra_caif_spi_slave_resume,
+#endif /* CONFIG_PM */
+};
+
+void tegra_caif_modem_power(int on)
+{
+ static int u3xx_on;
+ int err;
+ int cnt = 0;
+ int val = 0;
+
+ if (u3xx_on == on)
+ return;
+ u3xx_on = on;
+
+ if (u3xx_on) {
+ /* turn on u3xx modem */
+ err = gpio_request(tegra_caif_sspi_gpio_reset
+ , "caif_sspi_reset");
+ if (err < 0)
+ goto err1;
+
+ err = gpio_request(tegra_caif_sspi_gpio_power
+ , "caif_sspi_power");
+ if (err < 0)
+ goto err2;
+
+ err = gpio_request(tegra_caif_sspi_gpio_awr
+ , "caif_sspi_awr");
+ if (err < 0)
+ goto err3;
+
+ err = gpio_request(tegra_caif_sspi_gpio_cwr
+ , "caif_sspi_cwr");
+ if (err < 0)
+ goto err4;
+
+ err = gpio_direction_output(tegra_caif_sspi_gpio_reset
+ , 0 /* asserted */);
+ if (err < 0)
+ goto err5;
+
+ err = gpio_direction_output(tegra_caif_sspi_gpio_power
+ , 0 /* off */);
+ if (err < 0)
+ goto err6;
+
+ err = gpio_direction_output(tegra_caif_sspi_gpio_awr
+ , 0);
+ if (err < 0)
+ goto err7;
+
+ err = gpio_direction_input(tegra_caif_sspi_gpio_cwr);
+ if (err < 0)
+ goto err8;
+
+ gpio_set_value(tegra_caif_sspi_gpio_power, 0);
+ gpio_set_value(tegra_caif_sspi_gpio_reset, 0);
+
+ msleep(800);
+
+ /* pulse modem power on for 300 ms */
+ gpio_set_value(tegra_caif_sspi_gpio_reset
+ , 1 /* deasserted */);
+ msleep(300);
+ gpio_set_value(tegra_caif_sspi_gpio_power, 1);
+ msleep(300);
+ gpio_set_value(tegra_caif_sspi_gpio_power, 0);
+ msleep(100);
+
+ /* set awr high */
+ gpio_set_value(tegra_caif_sspi_gpio_awr, 1);
+ val = gpio_get_value(tegra_caif_sspi_gpio_cwr);
+ while (!val) {
+ /* wait for cwr to go high */
+ val = gpio_get_value(tegra_caif_sspi_gpio_cwr);
+ pr_info(".");
+ msleep(100);
+ cnt++;
+ if (cnt > 200) {
+ pr_err("\nWaiting for CWR timed out - ERROR\n");
+ break;
+ }
+ }
+ }
+ return;
+err8:
+err7:
+err6:
+err5:
+ gpio_free(tegra_caif_sspi_gpio_cwr);
+err4:
+ gpio_free(tegra_caif_sspi_gpio_awr);
+err3:
+ gpio_free(tegra_caif_sspi_gpio_power);
+err2:
+ gpio_free(tegra_caif_sspi_gpio_reset);
+err1:
+ return;
+}
+
+static irqreturn_t sspi_irq(int irq, void *arg)
+{
+ /* You only need to trigger on an edge to the active state of the
+ * SS signal. Once a edge is detected, the ss_cb() function should
+ * be called with the parameter assert set to true. It is OK
+ * (and even advised) to call the ss_cb() function in IRQ context
+ * in order not to add any delay.
+ */
+ int val;
+ struct cfspi_dev *sdev = (struct cfspi_dev *)arg;
+ val = gpio_get_value(tegra_caif_sspi_gpio_spi_ss);
+ if (val)
+ return IRQ_HANDLED;
+ sdev->ifc->ss_cb(true, sdev->ifc);
+ return IRQ_HANDLED;
+}
+
+static int sspi_callback(void *arg)
+{
+ /* for each spi_sync() call
+ * - sspi_callback() called before spi transfer
+ * - sspi_complete() called after spi transfer
+ */
+
+ /* set master interrupt gpio pin active (tells master to
+ * start spi clock)
+ */
+ udelay(MIN_TRANSITION_TIME_USEC);
+ gpio_set_value(tegra_caif_sspi_gpio_spi_int, 1);
+ return 0;
+}
+
+static void sspi_complete(void *context)
+{
+ /* Normally the DMA or the SPI framework will call you back
+ * in something similar to this. The only thing you need to
+ * do is to call the xfer_done_cb() function, providing the pointer
+ * to the CAIF SPI interface. It is OK to call this function
+ * from IRQ context.
+ */
+
+ struct cfspi_dev *sdev = (struct cfspi_dev *)context;
+ sdev->ifc->xfer_done_cb(sdev->ifc);
+}
+
+static void swap_byte(unsigned char *buf, unsigned int bufsiz)
+{
+ unsigned int i;
+ unsigned char tmp;
+ for (i = 0; i < bufsiz; i += 2) {
+ tmp = buf[i];
+ buf[i] = buf[i+1];
+ buf[i+1] = tmp;
+ }
+}
+
+static int sspi_init_xfer(struct cfspi_xfer *xfer, struct cfspi_dev *dev)
+{
+ /* Store transfer info. For a normal implementation you should
+ * set up your DMA here and make sure that you are ready to
+ * receive the data from the master SPI.
+ */
+
+ struct sspi_struct *sspi = (struct sspi_struct *)dev->priv;
+ struct spi_message m;
+ struct spi_transfer t;
+ int err;
+
+ sspi->xfer = xfer;
+
+ if (!tegra_caif_spi_slave_device)
+ return -ENODEV;
+
+ err = spi_tegra_register_callback(tegra_caif_spi_slave_device,
+ sspi_callback, sspi);
+ if (err < 0) {
+ pr_err("\nspi_tegra_register_callback() failed\n");
+ return -ENODEV;
+ }
+ memset(&t, 0, sizeof(t));
+ t.tx_buf = xfer->va_tx;
+ swap_byte(xfer->va_tx, xfer->tx_dma_len);
+ t.rx_buf = xfer->va_rx;
+ t.len = max(xfer->tx_dma_len, xfer->rx_dma_len);
+ t.len = SPI_CAIF_PAD_TRANSACTION_SIZE(t.len);
+ t.bits_per_word = 16;
+ /* SPI controller clock should be 4 times the spi_clk */
+ t.speed_hz = (SPI_MASTER_CLK_MHZ * 4 * 1000000);
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+
+ dmb();
+ err = spi_sync(tegra_caif_spi_slave_device, &m);
+ dmb();
+ swap_byte(xfer->va_tx, xfer->tx_dma_len);
+ swap_byte(xfer->va_rx, xfer->rx_dma_len);
+ sspi_complete(&sspi->sdev);
+ if (err < 0) {
+ pr_err("spi_init_xfer - spi_sync() err %d\n", err);
+ return err;
+ }
+ return 0;
+}
+
+void sspi_sig_xfer(bool xfer, struct cfspi_dev *dev)
+{
+ /* If xfer is true then you should assert the SPI_INT to indicate to
+ * the master that you are ready to recieve the data from the master
+ * SPI. If xfer is false then you should de-assert SPI_INT to indicate
+ * that the transfer is done.
+ */
+ if (xfer)
+ gpio_set_value(tegra_caif_sspi_gpio_spi_int, 1);
+ else
+ gpio_set_value(tegra_caif_sspi_gpio_spi_int, 0);
+}
+
+static void sspi_release(struct device *dev)
+{
+ /*
+ * Here you should release your SPI device resources.
+ */
+}
+
+static int __init sspi_init(void)
+{
+ /* Here you should initialize your SPI device by providing the
+ * necessary functions, clock speed, name and private data. Once
+ * done, you can register your device with the
+ * platform_device_register() function. This function will return
+ * with the CAIF SPI interface initialized. This is probably also
+ * the place where you should set up your GPIOs, interrupts and SPI
+ * resources.
+ */
+
+ int res = 0;
+
+ /* Register Tegra SPI protocol driver. */
+ res = spi_register_driver(&tegra_caif_spi_slave_driver);
+ if (res < 0)
+ return res;
+
+ /* Initialize slave device. */
+ slave.sdev.init_xfer = sspi_init_xfer;
+ slave.sdev.sig_xfer = sspi_sig_xfer;
+ slave.sdev.clk_mhz = SPI_MASTER_CLK_MHZ;
+ slave.sdev.priv = &slave;
+ slave.sdev.name = "spi_sspi";
+ slave_device.dev.release = sspi_release;
+
+ /* Initialize platform device. */
+ slave_device.name = "cfspi_sspi";
+ slave_device.dev.platform_data = &slave.sdev;
+
+ /* Register platform device. */
+ res = platform_device_register(&slave_device);
+ if (res)
+ return -ENODEV;
+
+ return res;
+}
+
+static void __exit sspi_exit(void)
+{
+ /* Delete platfrom device. */
+ platform_device_del(&slave_device);
+
+ /* Free Tegra SPI protocol driver. */
+ spi_unregister_driver(&tegra_caif_spi_slave_driver);
+
+ /* Free Tegra GPIO interrupts. */
+ disable_irq(gpio_to_irq(tegra_caif_sspi_gpio_spi_ss));
+ free_irq(gpio_to_irq(tegra_caif_sspi_gpio_spi_ss), &slave_device);
+
+ /* Free Tegra GPIOs. */
+ gpio_free(tegra_caif_sspi_gpio_spi_ss);
+ gpio_free(tegra_caif_sspi_gpio_spi_int);
+}
+
+static int __devinit tegra_caif_spi_slave_probe(struct spi_device *spi)
+{
+ struct tegra_caif_platform_data *pdata;
+ int res;
+
+ if (!spi)
+ return -ENODEV;
+
+ pdata = spi->dev.platform_data;
+ if (!pdata)
+ return -ENODEV;
+
+ tegra_caif_sspi_gpio_spi_int = pdata->spi_int;
+ tegra_caif_sspi_gpio_spi_ss = pdata->spi_ss;
+ tegra_caif_sspi_gpio_reset = pdata->reset;
+ tegra_caif_sspi_gpio_power = pdata->power;
+ tegra_caif_sspi_gpio_awr = pdata->awr;
+ tegra_caif_sspi_gpio_cwr = pdata->cwr;
+
+ tegra_caif_spi_slave_device = spi;
+
+ /* Initialize Tegra GPIOs. */
+ res = gpio_request(tegra_caif_sspi_gpio_spi_int, "caif_sspi_spi_int");
+ if (res < 0)
+ goto err1;
+
+ res = gpio_request(tegra_caif_sspi_gpio_spi_ss, "caif_sspi_ss");
+ if (res < 0)
+ goto err2;
+
+ res = gpio_direction_output(tegra_caif_sspi_gpio_spi_int, 0);
+ if (res < 0)
+ goto err3;
+
+ res = gpio_direction_input(tegra_caif_sspi_gpio_spi_ss);
+ if (res < 0)
+ goto err4;
+
+ tegra_caif_modem_power(1);
+ msleep(2000);
+
+ /* Initialize Tegra GPIO interrupts. */
+ res = request_irq(gpio_to_irq(tegra_caif_sspi_gpio_spi_ss),
+ sspi_irq, IRQF_TRIGGER_FALLING, "caif_sspi_ss_irq",
+ &slave.sdev);
+ if (res < 0)
+ goto err5;
+
+ return 0;
+err5:
+ free_irq(gpio_to_irq(tegra_caif_sspi_gpio_spi_ss), &slave_device);
+err4:
+err3:
+ gpio_free(tegra_caif_sspi_gpio_spi_ss);
+err2:
+ gpio_free(tegra_caif_sspi_gpio_spi_int);
+err1:
+ return res;
+}
+
+module_init(sspi_init);
+module_exit(sspi_exit);
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index 84d4608153c9..7f0818038aa3 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -458,4 +458,12 @@ config USB_VL600
http://ubuntuforums.org/showpost.php?p=10589647&postcount=17
+config USB_NET_RAW_IP
+ tristate "RAW-IP Driver for XMM6260 modems"
+ help
+ Choose this option if you have a XMM6260 modem device with RAW-IP suport.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rmnet.
+
endmenu
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index c203fa21f6b1..ea9665cb8492 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_RAW_IP) += raw_ip_net.o
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index c924ea2bce07..4fba62acf757 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -570,6 +570,26 @@ static const struct usb_device_id products [] = {
.driver_info = (unsigned long)&wwan_info,
},
+/* PH450 */
+{
+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+ | USB_DEVICE_ID_MATCH_DEVICE,
+ USB_DEVICE(0x1983,0x0310),
+ .driver_info = (unsigned long)&wwan_info,
+}, {
+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+ | USB_DEVICE_ID_MATCH_DEVICE,
+ USB_DEVICE(0x1983,0x0321),
+ .driver_info = (unsigned long)&wwan_info,
+},
+
+/* Tango module */
+{
+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+ | USB_DEVICE_ID_MATCH_DEVICE,
+ USB_DEVICE(0x0489,0xE03A),
+ .driver_info = (unsigned long)&wwan_info,
+},
/*
* WHITELIST!!!
*
diff --git a/drivers/net/usb/raw_ip_net.c b/drivers/net/usb/raw_ip_net.c
new file mode 100644
index 000000000000..899278b1d2eb
--- /dev/null
+++ b/drivers/net/usb/raw_ip_net.c
@@ -0,0 +1,735 @@
+/*
+ * raw_ip_net.c
+ *
+ * USB network driver for RAW-IP modems.
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/etherdevice.h>
+#include <linux/usb.h>
+
+#define BASEBAND_USB_NET_DEV_NAME "rmnet%d"
+
+/* ethernet packet ethertype for IP packets */
+#define NET_IP_ETHERTYPE 0x08, 0x00
+
+#define TX_TIMEOUT 10
+
+#ifndef USB_NET_BUFSIZ
+#define USB_NET_BUFSIZ 8192
+#endif /* USB_NET_BUFSIZ */
+
+/* maximum interface number supported */
+#define MAX_INTFS 3
+
+MODULE_LICENSE("GPL");
+
+int g_i;
+
+int max_intfs = MAX_INTFS;
+unsigned long usb_net_raw_ip_vid = 0x1519;
+unsigned long usb_net_raw_ip_pid = 0x0020;
+unsigned long usb_net_raw_ip_intf[MAX_INTFS] = { 0x03, 0x05, 0x07 };
+unsigned long usb_net_raw_ip_rx_debug;
+unsigned long usb_net_raw_ip_tx_debug;
+
+module_param(max_intfs, int, 0644);
+MODULE_PARM_DESC(max_intfs, "usb net (raw-ip) - max. interfaces supported");
+module_param(usb_net_raw_ip_vid, ulong, 0644);
+MODULE_PARM_DESC(usb_net_raw_ip_vid, "usb net (raw-ip) - USB VID");
+module_param(usb_net_raw_ip_pid, ulong, 0644);
+MODULE_PARM_DESC(usb_net_raw_ip_pid, "usb net (raw-ip) - USB PID");
+module_param(usb_net_raw_ip_rx_debug, ulong, 0644);
+MODULE_PARM_DESC(usb_net_raw_ip_rx_debug, "usb net (raw-ip) - rx debug");
+module_param(usb_net_raw_ip_tx_debug, ulong, 0644);
+MODULE_PARM_DESC(usb_net_raw_ip_tx_debug, "usb net (raw-ip) - tx debug");
+
+struct baseband_usb {
+ int baseband_index;
+ struct {
+ struct usb_driver *driver;
+ struct usb_device *device;
+ struct usb_interface *interface;
+ struct {
+ struct {
+ unsigned int in;
+ unsigned int out;
+ } isoch, bulk, interrupt;
+ } pipe;
+ /* currently active rx urb */
+ struct urb *rx_urb;
+ /* currently active tx urb */
+ struct urb *tx_urb;
+ } usb;
+};
+
+static struct baseband_usb *baseband_usb_net[MAX_INTFS] = { 0, 0, 0};
+
+static struct net_device *usb_net_raw_ip_dev[MAX_INTFS] = { 0, 0, 0};
+
+static unsigned int g_usb_interface_index[MAX_INTFS];
+static struct usb_interface *g_usb_interface[MAX_INTFS];
+
+static int usb_net_raw_ip_rx_urb_submit(struct baseband_usb *usb);
+static void usb_net_raw_ip_rx_urb_comp(struct urb *urb);
+static void usb_net_raw_ip_tx_urb_comp(struct urb *urb);
+
+static int baseband_usb_driver_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ int i = g_i;
+
+ pr_debug("%s(%d) { intf %p id %p\n", __func__, __LINE__, intf, id);
+
+ pr_debug("i %d\n", i);
+
+ pr_debug("intf->cur_altsetting->desc.bInterfaceNumber %02x\n",
+ intf->cur_altsetting->desc.bInterfaceNumber);
+ pr_debug("intf->cur_altsetting->desc.bAlternateSetting %02x\n",
+ intf->cur_altsetting->desc.bAlternateSetting);
+ pr_debug("intf->cur_altsetting->desc.bNumEndpoints %02x\n",
+ intf->cur_altsetting->desc.bNumEndpoints);
+ pr_debug("intf->cur_altsetting->desc.bInterfaceClass %02x\n",
+ intf->cur_altsetting->desc.bInterfaceClass);
+ pr_debug("intf->cur_altsetting->desc.bInterfaceSubClass %02x\n",
+ intf->cur_altsetting->desc.bInterfaceSubClass);
+ pr_debug("intf->cur_altsetting->desc.bInterfaceProtocol %02x\n",
+ intf->cur_altsetting->desc.bInterfaceProtocol);
+ pr_debug("intf->cur_altsetting->desc.iInterface %02x\n",
+ intf->cur_altsetting->desc.iInterface);
+
+ if (g_usb_interface_index[i] !=
+ intf->cur_altsetting->desc.bInterfaceNumber) {
+ pr_debug("%s(%d) } -ENODEV\n", __func__, __LINE__);
+ return -ENODEV;
+ } else {
+ g_usb_interface[i] = intf;
+ }
+
+ pr_debug("%s(%d) }\n", __func__, __LINE__);
+ return 0;
+}
+
+static void baseband_usb_driver_disconnect(struct usb_interface *intf)
+{
+ pr_debug("%s intf %p\n", __func__, intf);
+}
+
+#ifdef CONFIG_PM
+static int baseband_usb_driver_suspend(struct usb_interface *intf,
+ pm_message_t message)
+{
+ int i;
+
+ pr_debug("%s intf %p\n", __func__, intf);
+
+ for (i = 0; i < max_intfs; i++) {
+ pr_debug("[%d]\n", i);
+ if (!baseband_usb_net[i])
+ continue;
+ if (baseband_usb_net[i]->usb.interface != intf) {
+ pr_debug("%p != %p\n",
+ baseband_usb_net[i]->usb.interface, intf);
+ continue;
+ }
+ if (!baseband_usb_net[i]->usb.rx_urb) {
+ pr_debug("rx_usb already killed\n");
+ continue;
+ }
+ /* kill usb rx */
+ usb_kill_urb(baseband_usb_net[i]->usb.rx_urb);
+ baseband_usb_net[i]->usb.rx_urb = (struct urb *) 0;
+ }
+
+ return 0;
+}
+
+static int baseband_usb_driver_resume(struct usb_interface *intf)
+{
+ int i, err;
+
+ pr_debug("%s intf %p\n", __func__, intf);
+
+ for (i = 0; i < max_intfs; i++) {
+ pr_debug("[%d]\n", i);
+ if (!baseband_usb_net[i])
+ continue;
+ if (baseband_usb_net[i]->usb.interface != intf) {
+ pr_debug("%p != %p\n",
+ baseband_usb_net[i]->usb.interface, intf);
+ continue;
+ }
+ if (baseband_usb_net[i]->usb.rx_urb) {
+ pr_debug("rx_usb already exists\n");
+ continue;
+ }
+ /* start usb rx */
+ err = usb_net_raw_ip_rx_urb_submit(baseband_usb_net[i]);
+ if (err < 0) {
+ pr_err("submit rx failed - err %d\n", err);
+ continue;
+ }
+ }
+
+ return 0;
+}
+static int baseband_usb_driver_reset_resume(struct usb_interface *intf)
+{
+ pr_debug("%s intf %p\n", __func__, intf);
+ return baseband_usb_driver_resume(intf);
+}
+#endif /* CONFIG_PM */
+
+static struct usb_device_id baseband_usb_driver_id_table[MAX_INTFS][2];
+
+static char baseband_usb_driver_name[MAX_INTFS][32];
+
+static struct usb_driver baseband_usb_driver[MAX_INTFS] = {
+ {
+ .name = baseband_usb_driver_name[0],
+ .probe = baseband_usb_driver_probe,
+ .disconnect = baseband_usb_driver_disconnect,
+ .id_table = baseband_usb_driver_id_table[0],
+#ifdef CONFIG_PM
+ .suspend = baseband_usb_driver_suspend,
+ .resume = baseband_usb_driver_resume,
+ .reset_resume = baseband_usb_driver_reset_resume,
+ .supports_autosuspend = 1,
+#endif
+ },
+ {
+ .name = baseband_usb_driver_name[1],
+ .probe = baseband_usb_driver_probe,
+ .disconnect = baseband_usb_driver_disconnect,
+ .id_table = baseband_usb_driver_id_table[1],
+#ifdef CONFIG_PM
+ .suspend = baseband_usb_driver_suspend,
+ .resume = baseband_usb_driver_resume,
+ .reset_resume = baseband_usb_driver_reset_resume,
+ .supports_autosuspend = 1,
+#endif
+ },
+ {
+ .name = baseband_usb_driver_name[2],
+ .probe = baseband_usb_driver_probe,
+ .disconnect = baseband_usb_driver_disconnect,
+ .id_table = baseband_usb_driver_id_table[2],
+#ifdef CONFIG_PM
+ .suspend = baseband_usb_driver_suspend,
+ .resume = baseband_usb_driver_resume,
+ .reset_resume = baseband_usb_driver_reset_resume,
+ .supports_autosuspend = 1,
+#endif
+ },
+};
+
+static void find_usb_pipe(struct baseband_usb *usb)
+{
+ struct usb_device *usbdev = usb->usb.device;
+ struct usb_interface *intf = usb->usb.interface;
+ unsigned char numendpoint = intf->cur_altsetting->desc.bNumEndpoints;
+ struct usb_host_endpoint *endpoint = intf->cur_altsetting->endpoint;
+ unsigned char n;
+
+ for (n = 0; n < numendpoint; n++) {
+ if (usb_endpoint_is_isoc_in(&endpoint[n].desc)) {
+ pr_debug("endpoint[%d] isochronous in\n", n);
+ usb->usb.pipe.isoch.in = usb_rcvisocpipe(usbdev,
+ endpoint[n].desc.bEndpointAddress);
+ } else if (usb_endpoint_is_isoc_out(&endpoint[n].desc)) {
+ pr_debug("endpoint[%d] isochronous out\n", n);
+ usb->usb.pipe.isoch.out = usb_sndisocpipe(usbdev,
+ endpoint[n].desc.bEndpointAddress);
+ } else if (usb_endpoint_is_bulk_in(&endpoint[n].desc)) {
+ pr_debug("endpoint[%d] bulk in\n", n);
+ usb->usb.pipe.bulk.in = usb_rcvbulkpipe(usbdev,
+ endpoint[n].desc.bEndpointAddress);
+ } else if (usb_endpoint_is_bulk_out(&endpoint[n].desc)) {
+ pr_debug("endpoint[%d] bulk out\n", n);
+ usb->usb.pipe.bulk.out = usb_sndbulkpipe(usbdev,
+ endpoint[n].desc.bEndpointAddress);
+ } else if (usb_endpoint_is_int_in(&endpoint[n].desc)) {
+ pr_debug("endpoint[%d] interrupt in\n", n);
+ usb->usb.pipe.interrupt.in = usb_rcvintpipe(usbdev,
+ endpoint[n].desc.bEndpointAddress);
+ } else if (usb_endpoint_is_int_out(&endpoint[n].desc)) {
+ pr_debug("endpoint[%d] interrupt out\n", n);
+ usb->usb.pipe.interrupt.out = usb_sndintpipe(usbdev,
+ endpoint[n].desc.bEndpointAddress);
+ } else {
+ pr_debug("endpoint[%d] skipped\n", n);
+ }
+ }
+}
+
+void baseband_usb_close(struct baseband_usb *usb);
+
+struct baseband_usb *baseband_usb_open(int index,
+ unsigned int vid,
+ unsigned int pid,
+ unsigned int intf)
+{
+ struct baseband_usb *usb;
+ int err;
+
+ pr_debug("baseband_usb_open {\n");
+
+ /* allocate baseband usb structure */
+ usb = kzalloc(sizeof(struct baseband_usb),
+ GFP_KERNEL);
+ if (!usb)
+ return (struct baseband_usb *) 0;
+
+ /* open usb driver */
+ sprintf(baseband_usb_driver_name[index],
+ "baseband_usb_%x_%x_%x",
+ vid, pid, intf);
+ baseband_usb_driver_id_table[index][0].match_flags =
+ USB_DEVICE_ID_MATCH_DEVICE;
+ baseband_usb_driver_id_table[index][0].idVendor = vid;
+ baseband_usb_driver_id_table[index][0].idProduct = pid;
+ g_usb_interface_index[index] = intf;
+ g_usb_interface[index] = (struct usb_interface *) 0;
+ err = usb_register(&baseband_usb_driver[index]);
+ if (err < 0) {
+ pr_err("cannot open usb driver - err %d\n", err);
+ goto error_exit;
+ }
+ usb->baseband_index = index;
+ usb->usb.driver = &baseband_usb_driver[index];
+ if (!g_usb_interface[index]) {
+ pr_err("cannot open usb driver - !g_usb_interface[%d]\n",
+ index);
+ goto error_exit;
+ }
+ usb->usb.device = interface_to_usbdev(g_usb_interface[index]);
+ usb->usb.interface = g_usb_interface[index];
+ find_usb_pipe(usb);
+ usb->usb.rx_urb = (struct urb *) 0;
+ usb->usb.tx_urb = (struct urb *) 0;
+ g_usb_interface_index[index] = ~0U;
+ g_usb_interface[index] = (struct usb_interface *) 0;
+ pr_debug("usb->usb.driver->name %s\n", usb->usb.driver->name);
+ pr_debug("usb->usb.device %p\n", usb->usb.device);
+ pr_debug("usb->usb.interface %p\n", usb->usb.interface);
+ pr_debug("usb->usb.pipe.isoch.in %x\n", usb->usb.pipe.isoch.in);
+ pr_debug("usb->usb.pipe.isoch.out %x\n", usb->usb.pipe.isoch.out);
+ pr_debug("usb->usb.pipe.bulk.in %x\n", usb->usb.pipe.bulk.in);
+ pr_debug("usb->usb.pipe.bulk.out %x\n", usb->usb.pipe.bulk.out);
+ pr_debug("usb->usb.pipe.interrupt.in %x\n", usb->usb.pipe.interrupt.in);
+ pr_debug("usb->usb.pipe.interrupt.out %x\n",
+ usb->usb.pipe.interrupt.out);
+
+ pr_debug("baseband_usb_open }\n");
+ return usb;
+
+error_exit:
+ return (struct baseband_usb *) 0;
+}
+
+void baseband_usb_close(struct baseband_usb *usb)
+{
+ pr_debug("baseband_usb_close {\n");
+
+ /* check input */
+ if (!usb)
+ return;
+
+ /* close usb driver */
+ if (usb->usb.driver) {
+ pr_debug("close usb driver {\n");
+ usb_deregister(usb->usb.driver);
+ usb->usb.driver = (struct usb_driver *) 0;
+ pr_debug("close usb driver }\n");
+ }
+
+ /* free baseband usb structure */
+ kfree(usb);
+
+ pr_debug("baseband_usb_close }\n");
+}
+
+static int baseband_usb_netdev_init(struct net_device *dev)
+{
+ pr_debug("baseband_usb_netdev_init\n");
+ return 0;
+}
+
+static void baseband_usb_netdev_uninit(struct net_device *dev)
+{
+ pr_debug("baseband_usb_netdev_uninit\n");
+}
+
+static int baseband_usb_netdev_open(struct net_device *dev)
+{
+ pr_debug("baseband_usb_netdev_open\n");
+ netif_start_queue(dev);
+ return 0;
+}
+
+static int baseband_usb_netdev_stop(struct net_device *dev)
+{
+ pr_debug("baseband_usb_netdev_stop\n");
+ netif_stop_queue(dev);
+ return 0;
+}
+
+static netdev_tx_t baseband_usb_netdev_start_xmit(
+ struct sk_buff *skb, struct net_device *dev)
+{
+ int i = 0;
+ struct baseband_usb *usb = baseband_usb_net[i];
+ struct urb *urb;
+ unsigned char *buf;
+ int err;
+
+ pr_debug("baseband_usb_netdev_start_xmit\n");
+
+ /* check input */
+ if (!skb) {
+ pr_err("no skb\n");
+ return -EINVAL;
+ }
+
+ /* allocate urb */
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ pr_err("usb_alloc_urb() failed\n");
+ kfree_skb(skb);
+ return -ENOMEM;
+ }
+ buf = kzalloc(skb->len - 14, GFP_ATOMIC);
+ if (!buf) {
+ pr_err("usb buffer kzalloc() failed\n");
+ usb_free_urb(urb);
+ kfree_skb(skb);
+ return -ENOMEM;
+ }
+ err = skb_copy_bits(skb, 14, buf, skb->len - 14);
+ if (err < 0) {
+ pr_err("skb_copy_bits() failed - %d\n", err);
+ kfree(buf);
+ usb_free_urb(urb);
+ kfree_skb(skb);
+ return err;
+ }
+ usb_fill_bulk_urb(urb, usb->usb.device, usb->usb.pipe.bulk.out,
+ buf, skb->len - 14,
+ usb_net_raw_ip_tx_urb_comp,
+ usb);
+ urb->transfer_flags = URB_ZERO_PACKET;
+
+ /* autoresume before tx */
+ err = usb_autopm_get_interface(usb->usb.interface);
+ if (err < 0) {
+ pr_err("%s: usb_autopm_get_interface(%p) failed %d\n",
+ __func__, usb->usb.interface, err);
+ kfree(urb->transfer_buffer);
+ usb_free_urb(urb);
+ kfree_skb(skb);
+ return err;
+ }
+
+ /* submit tx urb */
+ usb_mark_last_busy(usb->usb.device);
+ usb->usb.tx_urb = urb;
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ pr_err("usb_submit_urb() failed - err %d\n", err);
+ usb_autopm_put_interface(usb->usb.interface);
+ usb->usb.tx_urb = (struct urb *) 0;
+ kfree(urb->transfer_buffer);
+ usb_free_urb(urb);
+ kfree_skb(skb);
+ return err;
+ }
+
+ /* free skb */
+ consume_skb(skb);
+
+ return NETDEV_TX_OK;
+}
+
+static struct net_device_ops usb_net_raw_ip_ops = {
+ .ndo_init = baseband_usb_netdev_init,
+ .ndo_uninit = baseband_usb_netdev_uninit,
+ .ndo_open = baseband_usb_netdev_open,
+ .ndo_stop = baseband_usb_netdev_stop,
+ .ndo_start_xmit = baseband_usb_netdev_start_xmit,
+};
+
+static int usb_net_raw_ip_rx_urb_submit(struct baseband_usb *usb)
+{
+ struct urb *urb;
+ void *buf;
+ int err;
+
+ pr_debug("usb_net_raw_ip_rx_urb_submit { usb %p\n", usb);
+
+ /* check input */
+ if (usb->usb.rx_urb) {
+ pr_err("previous urb still active\n");
+ return -1;
+ }
+
+ /* allocate rx urb */
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ pr_err("usb_alloc_urb() failed\n");
+ return -ENOMEM;
+ }
+ buf = kzalloc(USB_NET_BUFSIZ, GFP_ATOMIC);
+ if (!buf) {
+ pr_err("usb buffer kzalloc() failed\n");
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+ usb_fill_bulk_urb(urb, usb->usb.device, usb->usb.pipe.bulk.in,
+ buf, USB_NET_BUFSIZ,
+ usb_net_raw_ip_rx_urb_comp,
+ usb);
+ urb->transfer_flags = 0;
+
+ /* submit rx urb */
+ usb_mark_last_busy(usb->usb.device);
+ usb->usb.rx_urb = urb;
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ pr_err("usb_submit_urb() failed - err %d\n", err);
+ usb->usb.rx_urb = (struct urb *) 0;
+ kfree(urb->transfer_buffer);
+ usb_free_urb(urb);
+ return err;
+ }
+
+ pr_debug("usb_net_raw_ip_rx_urb_submit }\n");
+ return err;
+}
+
+static void usb_net_raw_ip_rx_urb_comp(struct urb *urb)
+{
+ struct baseband_usb *usb = (struct baseband_usb *) urb->context;
+ int i = usb->baseband_index;
+ struct sk_buff *skb;
+ unsigned char *dst;
+ unsigned char ethernet_header[14] = {
+ /* Destination MAC */
+ 0x00, 0x00,
+ 0x00, 0x00,
+ 0x00, 0x00,
+ /* Source MAC */
+ 0x00, 0x00,
+ 0x00, 0x00,
+ 0x00, 0x00,
+ /* EtherType */
+ NET_IP_ETHERTYPE,
+ };
+
+ pr_debug("usb_net_raw_ip_rx_urb_comp { urb %p\n", urb);
+
+ /* check input */
+ if (!urb) {
+ pr_err("no urb\n");
+ return;
+ }
+ if (urb->status == -ENOENT) {
+ pr_info("rx urb killed\n");
+ return;
+ }
+ if (urb->status) {
+ pr_info("rx urb status %d\n", urb->status);
+ }
+
+ /* put rx urb data in rx buffer */
+ if (urb->actual_length) {
+ pr_debug("usb_net_raw_ip_rx_urb_comp - "
+ "urb->actual_length %d\n", urb->actual_length);
+ /* allocate skb with space for
+ * - dummy ethernet header
+ * - rx IP packet from modem
+ */
+ skb = netdev_alloc_skb(usb_net_raw_ip_dev[i],
+ NET_IP_ALIGN + 14 + urb->actual_length);
+ if (skb) {
+ /* generate a dummy ethernet header
+ * since modem sends IP packets without
+ * any ethernet headers
+ */
+ memcpy(ethernet_header + 0,
+ usb_net_raw_ip_dev[i]->dev_addr, 6);
+ memcpy(ethernet_header + 6,
+ "0x01\0x02\0x03\0x04\0x05\0x06", 6);
+ /* fill skb with
+ * - dummy ethernet header
+ * - rx IP packet from modem
+ */
+ skb_reserve(skb, NET_IP_ALIGN);
+ dst = skb_put(skb, 14);
+ memcpy(dst, ethernet_header, 14);
+ dst = skb_put(skb, urb->actual_length);
+ memcpy(dst, urb->transfer_buffer, urb->actual_length);
+ skb->protocol = eth_type_trans(skb,
+ usb_net_raw_ip_dev[i]);
+ /* pass skb to network stack */
+ if (netif_rx(skb) < 0) {
+ pr_err("usb_net_raw_ip_rx_urb_comp_work - "
+ "netif_rx(%p) failed\n", skb);
+ kfree_skb(skb);
+ }
+ } else {
+ pr_err("usb_net_raw_ip_rx_urb_comp_work - "
+ "netdev_alloc_skb() failed\n");
+ }
+ }
+
+ /* free rx urb */
+ if (urb->transfer_buffer) {
+ kfree(urb->transfer_buffer);
+ urb->transfer_buffer = (void *) 0;
+ }
+ usb_free_urb(urb);
+ usb->usb.rx_urb = (struct urb *) 0;
+
+ /* submit next rx urb */
+ usb_net_raw_ip_rx_urb_submit(usb);
+
+ pr_debug("usb_net_raw_ip_rx_urb_comp }\n");
+}
+
+static void usb_net_raw_ip_tx_urb_comp(struct urb *urb)
+{
+ struct baseband_usb *usb = (struct baseband_usb *) urb->context;
+
+ pr_debug("usb_net_raw_ip_tx_urb_comp {\n");
+
+ /* free tx urb */
+ if (urb->transfer_buffer) {
+ kfree(urb->transfer_buffer);
+ urb->transfer_buffer = (void *) 0;
+ }
+ usb_free_urb(urb);
+ usb->usb.tx_urb = (struct urb *) 0;
+
+ /* autosuspend after tx completed */
+ usb_autopm_put_interface_async(usb->usb.interface);
+
+ pr_debug("usb_net_raw_ip_tx_urb_comp }\n");
+}
+
+static int usb_net_raw_ip_init(void)
+{
+ int i;
+ int err;
+
+ pr_debug("usb_net_raw_ip_init {\n");
+
+ /* create multiple raw-ip network devices */
+ for (i = 0; i < max_intfs; i++) {
+ /* open baseband usb */
+ g_i = i;
+ baseband_usb_net[i] = baseband_usb_open(i, usb_net_raw_ip_vid,
+ usb_net_raw_ip_pid, usb_net_raw_ip_intf[i]);
+ if (!baseband_usb_net[i]) {
+ pr_err("cannot open baseband usb net\n");
+ err = -1;
+ goto error_exit;
+ }
+ /* register network device */
+ usb_net_raw_ip_dev[i] = alloc_netdev(0,
+ BASEBAND_USB_NET_DEV_NAME,
+ ether_setup);
+ if (!usb_net_raw_ip_dev[i]) {
+ pr_err("alloc_netdev() failed\n");
+ err = -ENOMEM;
+ goto error_exit;
+ }
+ usb_net_raw_ip_dev[i]->netdev_ops = &usb_net_raw_ip_ops;
+ usb_net_raw_ip_dev[i]->watchdog_timeo = TX_TIMEOUT;
+ random_ether_addr(usb_net_raw_ip_dev[i]->dev_addr);
+ err = register_netdev(usb_net_raw_ip_dev[i]);
+ if (err < 0) {
+ pr_err("cannot register network device - %d\n", err);
+ goto error_exit;
+ }
+ pr_debug("registered baseband usb network device"
+ " - dev %p name %s\n", usb_net_raw_ip_dev[i],
+ BASEBAND_USB_NET_DEV_NAME);
+ /* start usb rx */
+ err = usb_net_raw_ip_rx_urb_submit(baseband_usb_net[i]);
+ if (err < 0) {
+ pr_err("submit rx failed - err %d\n", err);
+ goto error_exit;
+ }
+ }
+
+ pr_debug("usb_net_raw_ip_init }\n");
+ return 0;
+
+error_exit:
+ /* destroy multiple raw-ip network devices */
+ for (i = 0; i < max_intfs; i++) {
+ /* unregister network device */
+ if (usb_net_raw_ip_dev[i]) {
+ unregister_netdev(usb_net_raw_ip_dev[i]);
+ free_netdev(usb_net_raw_ip_dev[i]);
+ usb_net_raw_ip_dev[i] = (struct net_device *) 0;
+ }
+ /* close baseband usb */
+ if (baseband_usb_net[i]) {
+ baseband_usb_close(baseband_usb_net[i]);
+ baseband_usb_net[i] = (struct baseband_usb *) 0;
+ }
+ }
+
+ return err;
+}
+
+static void usb_net_raw_ip_exit(void)
+{
+ int i;
+
+ pr_debug("usb_net_raw_ip_exit {\n");
+
+ /* destroy multiple raw-ip network devices */
+ for (i = 0; i < max_intfs; i++) {
+ /* unregister network device */
+ if (usb_net_raw_ip_dev[i]) {
+ unregister_netdev(usb_net_raw_ip_dev[i]);
+ free_netdev(usb_net_raw_ip_dev[i]);
+ usb_net_raw_ip_dev[i] = (struct net_device *) 0;
+ }
+ /* close baseband usb */
+ if (baseband_usb_net[i]) {
+ baseband_usb_close(baseband_usb_net[i]);
+ baseband_usb_net[i] = (struct baseband_usb *) 0;
+ }
+ }
+
+ pr_debug("usb_net_raw_ip_exit }\n");
+}
+
+module_init(usb_net_raw_ip_init)
+module_exit(usb_net_raw_ip_exit)
+
diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c
index f74f3ce71526..7b4687974987 100644
--- a/drivers/net/usb/smsc95xx.c
+++ b/drivers/net/usb/smsc95xx.c
@@ -1284,6 +1284,11 @@ static const struct usb_device_id products[] = {
USB_DEVICE(0x0424, 0x9E08),
.driver_info = (unsigned long) &smsc95xx_info,
},
+ {
+ /* SMSC89530 USB Ethernet Device on Automotive VCM */
+ USB_DEVICE(0x0424, 0x9E08),
+ .driver_info = (unsigned long) &smsc95xx_info,
+ },
{ }, /* END */
};
MODULE_DEVICE_TABLE(usb, products);