diff options
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/caif/Kconfig | 9 | ||||
-rw-r--r-- | drivers/net/caif/Makefile | 4 | ||||
-rw-r--r-- | drivers/net/caif/tegra_caif_sspi.c | 426 | ||||
-rw-r--r-- | drivers/net/usb/Kconfig | 8 | ||||
-rw-r--r-- | drivers/net/usb/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/usb/cdc_ether.c | 20 | ||||
-rw-r--r-- | drivers/net/usb/raw_ip_net.c | 735 | ||||
-rw-r--r-- | drivers/net/usb/smsc95xx.c | 5 |
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); |