diff --git a/package/kernel/ath10k-ct/Makefile b/package/kernel/ath10k-ct/Makefile index f88513cb2..be5b38882 100644 --- a/package/kernel/ath10k-ct/Makefile +++ b/package/kernel/ath10k-ct/Makefile @@ -29,7 +29,7 @@ include $(INCLUDE_DIR)/package.mk define KernelPackage/ath10k-ct SUBMENU:=Wireless Drivers TITLE:=ath10k-ct driver optimized for CT ath10k firmware - DEPENDS:=+kmod-mac80211 +kmod-ath +@DRIVER_11N_SUPPORT +@DRIVER_11AC_SUPPORT @PCI_SUPPORT +kmod-hwmon-core + DEPENDS:=@(arm||aarch64||mips||mipsel) +kmod-mac80211 +kmod-ath +@DRIVER_11N_SUPPORT +@DRIVER_11AC_SUPPORT @PCI_SUPPORT +kmod-hwmon-core FILES:=\ $(PKG_BUILD_DIR)/ath10k$(CT_KVER)/ath10k_pci.ko \ $(PKG_BUILD_DIR)/ath10k$(CT_KVER)/ath10k_core.ko diff --git a/package/kernel/mac80211/patches/build/990-add_kernel_6.6_support.patch b/package/kernel/mac80211/patches/build/990-add_kernel_6.6_support.patch index 2ced0316d..33ed7360c 100644 --- a/package/kernel/mac80211/patches/build/990-add_kernel_6.6_support.patch +++ b/package/kernel/mac80211/patches/build/990-add_kernel_6.6_support.patch @@ -111,3 +111,17 @@ u8 rate_idx; u8 nss; u8 rx_flags; +--- a/drivers/net/wireless/mac80211_hwsim.c ++++ b/drivers/net/wireless/mac80211_hwsim.c +@@ -5750,7 +5750,11 @@ + if (err) + goto out_exit_netlink; + ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)) ++ hwsim_class = class_create("mac80211_hwsim"); ++#else + hwsim_class = class_create(THIS_MODULE, "mac80211_hwsim"); ++#endif + if (IS_ERR(hwsim_class)) { + err = PTR_ERR(hwsim_class); + goto out_exit_virtio; diff --git a/package/kernel/xr-usb-serial/Makefile b/package/kernel/xr-usb-serial/Makefile deleted file mode 100644 index 963bb926c..000000000 --- a/package/kernel/xr-usb-serial/Makefile +++ /dev/null @@ -1,32 +0,0 @@ -# -# Copyright (C) Chen Yijun -# -# This is free software, licensed under the GNU General Public License v2. -# See /LICENSE for more information. -# - -include $(TOPDIR)/rules.mk -include $(INCLUDE_DIR)/kernel.mk - -PKG_NAME:=xr-usb-serial -PKG_RELEASE:=1 - -include $(INCLUDE_DIR)/package.mk - -define KernelPackage/$(PKG_NAME) - SUBMENU:=USB Support - DEPENDS:=@GPIO_SUPPORT +kmod-usb-serial +kmod-usb2 - TITLE:=Driver for XR21V141x (used in HUAWEI UPS) - AUTOLOAD:=$(call AutoLoad,30,xr_usb_serial_common,1) - FILES:=$(PKG_BUILD_DIR)/xr_usb_serial_common.ko -endef - -define Build/Compile - $(MAKE) -C "$(LINUX_DIR)" \ - $(KERNEL_MAKE_FLAGS) \ - M="$(PKG_BUILD_DIR)" \ - EXTRA_CFLAGS="$(BUILDFLAGS)" \ - modules -endef - -$(eval $(call KernelPackage,$(PKG_NAME))) diff --git a/package/kernel/xr-usb-serial/src/Makefile b/package/kernel/xr-usb-serial/src/Makefile deleted file mode 100644 index 4d836ddf8..000000000 --- a/package/kernel/xr-usb-serial/src/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# -# Copyright (C) Chen Yijun -# -# This is free software, licensed under the GNU General Public License v2. -# See /LICENSE for more information. -# - -obj-m := xr_usb_serial_common.o - -EXTRA_CFLAGS := -DDEBUG=0 diff --git a/package/kernel/xr-usb-serial/src/xr_usb_serial_common.c b/package/kernel/xr-usb-serial/src/xr_usb_serial_common.c deleted file mode 100644 index 61469db4f..000000000 --- a/package/kernel/xr-usb-serial/src/xr_usb_serial_common.c +++ /dev/null @@ -1,2086 +0,0 @@ -/* - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -/* - * - * This driver will work with any USB UART function in these Exar devices: - * XR21V1410/1412/1414 - * XR21B1411 - * XR21B1420/1422/1424 - * XR22801/802/804 - * - * The driver has been tested on various kernel versions from 3.6.x to 5.11.x. - * This driver may work on newer versions as well. There is a different driver available - * from www.exar.com that will work with kernel versions 2.6.18 to 3.4.x. - * - * ChangeLog: - * Version 1B - Initial released version. - * Version 1C - Add 9-bit mode support - * Version 1D - GPIO support & Fixes. Check Readme for details. - */ - -//#undef DEBUG -#undef VERBOSE_DEBUG - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "linux/version.h" -#include -#include -#include -#include - -#ifdef CONFIG_COMPAT -#include -#endif - -#include "xr_usb_serial_common.h" -#include "xr_usb_serial_ioctl.h" - -#define DRIVER_AUTHOR "" -#define DRIVER_DESC "Exar/MxL USB UART (serial port) driver version 1D" - -static struct usb_driver xr_usb_serial_driver; -static struct tty_driver *xr_usb_serial_tty_driver; -static struct xr_usb_serial *xr_usb_serial_table[XR_USB_SERIAL_TTY_MINORS]; - -static DEFINE_MUTEX(xr_usb_serial_table_lock); - -/* - * xr_usb_serial_table accessors - */ - -/* - * Look up an XR_USB_SERIAL structure by index. If found and not disconnected, increment - * its refcount and return it with its mutex held. - */ -static struct xr_usb_serial *xr_usb_serial_get_by_index(unsigned index) -{ - struct xr_usb_serial *xr_usb_serial; - - mutex_lock(&xr_usb_serial_table_lock); - xr_usb_serial = xr_usb_serial_table[index]; - if (xr_usb_serial) { - mutex_lock(&xr_usb_serial->mutex); - if (xr_usb_serial->disconnected) { - mutex_unlock(&xr_usb_serial->mutex); - xr_usb_serial = NULL; - } else { - tty_port_get(&xr_usb_serial->port); - mutex_unlock(&xr_usb_serial->mutex); - } - } - mutex_unlock(&xr_usb_serial_table_lock); - return xr_usb_serial; -} - -/* - * Try to find an available minor number and if found, associate it with 'xr_usb_serial'. - */ -static int xr_usb_serial_alloc_minor(struct xr_usb_serial *xr_usb_serial) -{ - int minor; - - mutex_lock(&xr_usb_serial_table_lock); - for (minor = 0; minor < XR_USB_SERIAL_TTY_MINORS; minor++) { - if (!xr_usb_serial_table[minor]) { - xr_usb_serial_table[minor] = xr_usb_serial; - break; - } - } - mutex_unlock(&xr_usb_serial_table_lock); - - return minor; -} - -/* Release the minor number associated with 'xr_usb_serial'. */ -static void xr_usb_serial_release_minor(struct xr_usb_serial *xr_usb_serial) -{ - mutex_lock(&xr_usb_serial_table_lock); - xr_usb_serial_table[xr_usb_serial->minor] = NULL; - mutex_unlock(&xr_usb_serial_table_lock); -} - -/* - * Functions for XR_USB_SERIAL control messages. - */ - -static int xr_usb_serial_ctrl_msg(struct xr_usb_serial *xr_usb_serial, int request, int value, - void *buf, int len) -{ - int retval = usb_control_msg(xr_usb_serial->dev, usb_sndctrlpipe(xr_usb_serial->dev, 0), - request, USB_RT_XR_USB_SERIAL, value, - xr_usb_serial->control->altsetting[0].desc.bInterfaceNumber, - buf, len, 5000); - dev_dbg(&xr_usb_serial->control->dev, - "%s - rq 0x%02x, val %#x, len %#x, result %d\n", - __func__, request, value, len, retval); - return retval < 0 ? retval : 0; -} - -#include "xr_usb_serial_hal.c" - -/* - * Write buffer management. - * All of these assume proper locks taken by the caller. - */ - -static int xr_usb_serial_wb_alloc(struct xr_usb_serial *xr_usb_serial) -{ - int i, wbn; - struct xr_usb_serial_wb *wb; - - wbn = 0; - i = 0; - for (;;) { - wb = &xr_usb_serial->wb[wbn]; - if (!wb->use) { - wb->use = 1; - return wbn; - } - wbn = (wbn + 1) % XR_USB_SERIAL_NW; - if (++i >= XR_USB_SERIAL_NW) - return -1; - } -} - -static int xr_usb_serial_wb_is_avail(struct xr_usb_serial *xr_usb_serial) -{ - int i, n; - unsigned long flags; - - n = XR_USB_SERIAL_NW; - spin_lock_irqsave(&xr_usb_serial->write_lock, flags); - for (i = 0; i < XR_USB_SERIAL_NW; i++) - n -= xr_usb_serial->wb[i].use; - spin_unlock_irqrestore(&xr_usb_serial->write_lock, flags); - return n; -} - -/* - * Finish write. Caller must hold xr_usb_serial->write_lock - */ -static void xr_usb_serial_write_done(struct xr_usb_serial *xr_usb_serial, struct xr_usb_serial_wb *wb) -{ - wb->use = 0; - xr_usb_serial->transmitting--; - usb_autopm_put_interface_async(xr_usb_serial->control); -} - -/* - * Poke write. - * - * the caller is responsible for locking - */ - -static int xr_usb_serial_start_wb(struct xr_usb_serial *xr_usb_serial, struct xr_usb_serial_wb *wb) -{ - int rc; - - xr_usb_serial->transmitting++; - - wb->urb->transfer_buffer = wb->buf; - wb->urb->transfer_dma = wb->dmah; - wb->urb->transfer_buffer_length = wb->len; - wb->urb->dev = xr_usb_serial->dev; - - rc = usb_submit_urb(wb->urb, GFP_ATOMIC); - if (rc < 0) { - dev_err(&xr_usb_serial->data->dev, - "%s - usb_submit_urb(write bulk) failed: %d\n", - __func__, rc); - xr_usb_serial_write_done(xr_usb_serial, wb); - } - return rc; -} - -/* - * attributes exported through sysfs - */ -static ssize_t show_caps -(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct usb_interface *intf = to_usb_interface(dev); - struct xr_usb_serial *xr_usb_serial = usb_get_intfdata(intf); - - return sprintf(buf, "%d", xr_usb_serial->ctrl_caps); -} -static DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL); - -static ssize_t show_country_codes -(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct usb_interface *intf = to_usb_interface(dev); - struct xr_usb_serial *xr_usb_serial = usb_get_intfdata(intf); - - memcpy(buf, xr_usb_serial->country_codes, xr_usb_serial->country_code_size); - return xr_usb_serial->country_code_size; -} - -static DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL); - -static ssize_t show_country_rel_date -(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct usb_interface *intf = to_usb_interface(dev); - struct xr_usb_serial *xr_usb_serial = usb_get_intfdata(intf); - - return sprintf(buf, "%d", xr_usb_serial->country_rel_date); -} - -static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL); -/* - * Interrupt handlers for various XR_USB_SERIAL device responses - */ - -/* control interface reports status changes with "interrupt" transfers */ -static void xr_usb_serial_ctrl_irq(struct urb *urb) -{ - struct xr_usb_serial *xr_usb_serial = urb->context; - struct usb_cdc_notification *dr = urb->transfer_buffer; -#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 9, 0) -#else - struct tty_struct *tty; -#endif - unsigned char *data; - int newctrl; - int retval; - int status = urb->status; - int i; - unsigned char *p; - - switch (status) { - case 0: - p = (unsigned char *)(urb->transfer_buffer); - for(i=0;iactual_length;i++) - { - dev_dbg(&xr_usb_serial->control->dev,"0x%02x\n",p[i]); - } - /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dev_dbg(&xr_usb_serial->control->dev, - "%s - urb shutting down with status: %d\n", - __func__, status); - return; - default: - dev_dbg(&xr_usb_serial->control->dev, - "%s - nonzero urb status received: %d\n", - __func__, status); - goto exit; - } - - usb_mark_last_busy(xr_usb_serial->dev); - - data = (unsigned char *)(dr + 1); - switch (dr->bNotificationType) { - case USB_CDC_NOTIFY_NETWORK_CONNECTION: - dev_dbg(&xr_usb_serial->control->dev, "%s - network connection: %d\n", - __func__, dr->wValue); - break; - - case USB_CDC_NOTIFY_SERIAL_STATE: -#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 9, 0) - newctrl = get_unaligned_le16(data); - if (!xr_usb_serial->clocal && (xr_usb_serial->ctrlin & ~newctrl & XR_USB_SERIAL_CTRL_DCD)) { - dev_dbg(&xr_usb_serial->control->dev, "%s - calling hangup\n", - __func__); - tty_port_tty_hangup(&xr_usb_serial->port, false); - } -#else - tty = tty_port_tty_get(&xr_usb_serial->port); - newctrl = get_unaligned_le16(data); - if (tty) - { - if (!xr_usb_serial->clocal && - (xr_usb_serial->ctrlin & ~newctrl & XR_USB_SERIAL_CTRL_DCD)) { - dev_dbg(&xr_usb_serial->control->dev, - "%s - calling hangup\n", __func__); - tty_hangup(tty); - } - tty_kref_put(tty); - } -#endif - xr_usb_serial->ctrlin = newctrl; - - dev_dbg(&xr_usb_serial->control->dev, - "%s - input control lines: dcd%c dsr%c break%c " - "ring%c framing%c parity%c overrun%c\n", - __func__, - xr_usb_serial->ctrlin & XR_USB_SERIAL_CTRL_DCD ? '+' : '-', - xr_usb_serial->ctrlin & XR_USB_SERIAL_CTRL_DSR ? '+' : '-', - xr_usb_serial->ctrlin & XR_USB_SERIAL_CTRL_BRK ? '+' : '-', - xr_usb_serial->ctrlin & XR_USB_SERIAL_CTRL_RI ? '+' : '-', - xr_usb_serial->ctrlin & XR_USB_SERIAL_CTRL_FRAMING ? '+' : '-', - xr_usb_serial->ctrlin & XR_USB_SERIAL_CTRL_PARITY ? '+' : '-', - xr_usb_serial->ctrlin & XR_USB_SERIAL_CTRL_OVERRUN ? '+' : '-'); - break; - - default: - dev_dbg(&xr_usb_serial->control->dev, - "%s - unknown notification %d received: index %d " - "len %d data0 %d data1 %d\n", - __func__, - dr->bNotificationType, dr->wIndex, - dr->wLength, data[0], data[1]); - break; - } -exit: - retval = usb_submit_urb(urb, GFP_ATOMIC); - if (retval) - dev_err(&xr_usb_serial->control->dev, "%s - usb_submit_urb failed: %d\n", - __func__, retval); -} - -static int xr_usb_serial_submit_read_urb(struct xr_usb_serial *xr_usb_serial, int index, gfp_t mem_flags) -{ - int res; - - if (!test_and_clear_bit(index, &xr_usb_serial->read_urbs_free)) - return 0; - - dev_vdbg(&xr_usb_serial->data->dev, "%s - urb %d\n", __func__, index); - - res = usb_submit_urb(xr_usb_serial->read_urbs[index], mem_flags); - if (res) { - if (res != -EPERM) { - dev_err(&xr_usb_serial->data->dev, - "%s - usb_submit_urb failed: %d\n", - __func__, res); - } - set_bit(index, &xr_usb_serial->read_urbs_free); - return res; - } - - return 0; -} - -static int xr_usb_serial_submit_read_urbs(struct xr_usb_serial *xr_usb_serial, gfp_t mem_flags) -{ - int res; - int i; - - for (i = 0; i < xr_usb_serial->rx_buflimit; ++i) { - res = xr_usb_serial_submit_read_urb(xr_usb_serial, i, mem_flags); - if (res) - return res; - } - - return 0; -} -static void xr_usb_serial_process_read_urb(struct xr_usb_serial *xr_usb_serial, struct urb *urb) -{ -#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 9, 0) -#else - struct tty_struct *tty; -#endif - int preciseflags = xr_usb_serial->preciseflags; - int have_extra_byte; - int length; - - if (!urb->actual_length) - return; - - if (preciseflags) - { - char *dp = urb->transfer_buffer; - int i, ch, ch_flags; - - length = urb->actual_length; - length = length + (xr_usb_serial->have_extra_byte ? 1 : 0); - have_extra_byte = (preciseflags && (length & 1)); - length = (preciseflags) ? (length / 2) : length; - for (i = 0; i < length; ++i) - { - char tty_flag; - if (i == 0) - { - if (xr_usb_serial->have_extra_byte) - { - ch = xr_usb_serial->extra_byte; - } - else - { - ch = *dp++; - } - } - else - { - ch = *dp++; - } - ch_flags = *dp++; - if (ch_flags & RAMCTL_BUFFER_PARITY) - tty_flag = TTY_PARITY; - else if (ch_flags & RAMCTL_BUFFER_BREAK) - tty_flag = TTY_BREAK; - else if (ch_flags & RAMCTL_BUFFER_FRAME) - tty_flag = TTY_FRAME; - else if (ch_flags & RAMCTL_BUFFER_OVERRUN) - tty_flag = TTY_OVERRUN; - else - tty_flag = TTY_NORMAL; - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) - tty_insert_flip_char(&xr_usb_serial->port, ch, tty_flag); - tty_flip_buffer_push(&xr_usb_serial->port); -#else - tty = tty_port_tty_get(&xr_usb_serial->port); - if (!tty) - return; - tty_insert_flip_char(&xr_usb_serial->port, ch, tty_flag); - tty_flip_buffer_push(tty); - tty_kref_put(tty); -#endif - } - } - else - { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) - tty_insert_flip_string(&xr_usb_serial->port, urb->transfer_buffer, - urb->actual_length); - tty_flip_buffer_push(&xr_usb_serial->port); -#else - tty = tty_port_tty_get(&xr_usb_serial->port); - if (!tty) - return; - tty_insert_flip_string(tty, urb->transfer_buffer, urb->actual_length); - tty_flip_buffer_push(tty); - tty_kref_put(tty); -#endif - } -} - -static void xr_usb_serial_read_bulk_callback(struct urb *urb) -{ - struct xr_usb_serial_rb *rb = urb->context; - struct xr_usb_serial *xr_usb_serial = rb->instance; - unsigned long flags; - - dev_vdbg(&xr_usb_serial->data->dev, "%s - urb %d, len %d\n", __func__, - rb->index, urb->actual_length); - set_bit(rb->index, &xr_usb_serial->read_urbs_free); - - if (!xr_usb_serial->dev) { - dev_dbg(&xr_usb_serial->data->dev, "%s - disconnected\n", __func__); - return; - } - usb_mark_last_busy(xr_usb_serial->dev); - - if (urb->status) { - dev_dbg(&xr_usb_serial->data->dev, "%s - non-zero urb status: %d\n", - __func__, urb->status); - //return; - } - xr_usb_serial_process_read_urb(xr_usb_serial, urb); - - /* throttle device if requested by tty */ - spin_lock_irqsave(&xr_usb_serial->read_lock, flags); - xr_usb_serial->throttled = xr_usb_serial->throttle_req; - if (!xr_usb_serial->throttled && !xr_usb_serial->susp_count) { - spin_unlock_irqrestore(&xr_usb_serial->read_lock, flags); - xr_usb_serial_submit_read_urb(xr_usb_serial, rb->index, GFP_ATOMIC); - } else { - spin_unlock_irqrestore(&xr_usb_serial->read_lock, flags); - } -} - -/* data interface wrote those outgoing bytes */ -static void xr_usb_serial_write_bulk(struct urb *urb) -{ - struct xr_usb_serial_wb *wb = urb->context; - struct xr_usb_serial *xr_usb_serial = wb->instance; - unsigned long flags; - - if (urb->status || (urb->actual_length != urb->transfer_buffer_length)) - dev_vdbg(&xr_usb_serial->data->dev, "%s - len %d/%d, status %d\n", - __func__, - urb->actual_length, - urb->transfer_buffer_length, - urb->status); - - spin_lock_irqsave(&xr_usb_serial->write_lock, flags); - xr_usb_serial_write_done(xr_usb_serial, wb); - spin_unlock_irqrestore(&xr_usb_serial->write_lock, flags); - schedule_work(&xr_usb_serial->work); -} - -static void xr_usb_serial_softint(struct work_struct *work) -{ - struct xr_usb_serial *xr_usb_serial = container_of(work, struct xr_usb_serial, work); -#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 9, 0) -#else - struct tty_struct *tty; -#endif - - dev_vdbg(&xr_usb_serial->data->dev, "%s\n", __func__); -#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 9, 0) - tty_port_tty_wakeup(&xr_usb_serial->port); -#else - tty = tty_port_tty_get(&xr_usb_serial->port); - if (!tty) - return; - tty_wakeup(tty); - tty_kref_put(tty); -#endif -} - -/* - * TTY handlers - */ - -static int xr_usb_serial_tty_install(struct tty_driver *driver, struct tty_struct *tty) -{ - struct xr_usb_serial *xr_usb_serial; - int retval; - - dev_dbg(tty->dev, "%s\n", __func__); - - xr_usb_serial = xr_usb_serial_get_by_index(tty->index); - if (!xr_usb_serial) - return -ENODEV; - - retval = tty_standard_install(driver, tty); - if (retval) - goto error_init_termios; - - tty->driver_data = xr_usb_serial; - - return 0; - -error_init_termios: - tty_port_put(&xr_usb_serial->port); - return retval; -} - -static int xr_usb_serial_tty_open(struct tty_struct *tty, struct file *filp) -{ - struct xr_usb_serial *xr_usb_serial = tty->driver_data; - int result; - - result = xr_usb_serial_fifo_reset(xr_usb_serial); - dev_dbg(tty->dev, "%s\n", __func__); - - return tty_port_open(&xr_usb_serial->port, tty, filp); -} - -static int xr_usb_serial_port_activate(struct tty_port *port, struct tty_struct *tty) -{ - struct xr_usb_serial *xr_usb_serial = container_of(port, struct xr_usb_serial, port); - int retval = -ENODEV; - - dev_dbg(&xr_usb_serial->control->dev, "%s\n", __func__); - - mutex_lock(&xr_usb_serial->mutex); - if (xr_usb_serial->disconnected) - goto disconnected; - - retval = usb_autopm_get_interface(xr_usb_serial->control); - if (retval) - goto error_get_interface; - - /* - * FIXME: Why do we need this? Allocating 64K of physically contiguous - * memory is really nasty... - */ - set_bit(TTY_NO_WRITE_SPLIT, &tty->flags); - xr_usb_serial->control->needs_remote_wakeup = 1; - - xr_usb_serial->ctrlurb->dev = xr_usb_serial->dev; - if (usb_submit_urb(xr_usb_serial->ctrlurb, GFP_KERNEL)) { - dev_err(&xr_usb_serial->control->dev, - "%s - usb_submit_urb(ctrl irq) failed\n", __func__); - goto error_submit_urb; - } - - xr_usb_serial->ctrlout = XR_USB_SERIAL_CTRL_DTR | XR_USB_SERIAL_CTRL_RTS; - if (xr_usb_serial_set_control(xr_usb_serial, xr_usb_serial->ctrlout) < 0 && - (xr_usb_serial->ctrl_caps & USB_CDC_CAP_LINE)) - goto error_set_control; - - usb_autopm_put_interface(xr_usb_serial->control); - - /* - * Unthrottle device in case the TTY was closed while throttled. - */ - spin_lock_irq(&xr_usb_serial->read_lock); - xr_usb_serial->throttled = 0; - xr_usb_serial->throttle_req = 0; - spin_unlock_irq(&xr_usb_serial->read_lock); - - if (xr_usb_serial_submit_read_urbs(xr_usb_serial, GFP_KERNEL)) - goto error_submit_read_urbs; - - mutex_unlock(&xr_usb_serial->mutex); - - return 0; - -error_submit_read_urbs: - xr_usb_serial->ctrlout = 0; - xr_usb_serial_set_control(xr_usb_serial, xr_usb_serial->ctrlout); -error_set_control: - usb_kill_urb(xr_usb_serial->ctrlurb); -error_submit_urb: - usb_autopm_put_interface(xr_usb_serial->control); -error_get_interface: -disconnected: - mutex_unlock(&xr_usb_serial->mutex); - return retval; -} - -static void xr_usb_serial_port_destruct(struct tty_port *port) -{ - struct xr_usb_serial *xr_usb_serial = container_of(port, struct xr_usb_serial, port); - - dev_dbg(&xr_usb_serial->control->dev, "%s\n", __func__); -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0) - tty_unregister_device(xr_usb_serial_tty_driver, xr_usb_serial->minor); -#endif -#ifdef CONFIG_GPIOLIB - if (xr_usb_serial->rv_gpio_created == 0) - gpiochip_remove(&xr_usb_serial->xr_gpio); -#endif - xr_usb_serial_release_minor(xr_usb_serial); - usb_put_intf(xr_usb_serial->control); - kfree(xr_usb_serial->country_codes); - kfree(xr_usb_serial); -} - -static void xr_usb_serial_port_shutdown(struct tty_port *port) -{ - struct xr_usb_serial *xr_usb_serial = container_of(port, struct xr_usb_serial, port); - int i; - - dev_dbg(&xr_usb_serial->control->dev, "%s\n", __func__); - - mutex_lock(&xr_usb_serial->mutex); - if (!xr_usb_serial->disconnected) { - usb_autopm_get_interface(xr_usb_serial->control); - xr_usb_serial_set_control(xr_usb_serial, xr_usb_serial->ctrlout = 0); - usb_kill_urb(xr_usb_serial->ctrlurb); - for (i = 0; i < XR_USB_SERIAL_NW; i++) - usb_kill_urb(xr_usb_serial->wb[i].urb); - for (i = 0; i < xr_usb_serial->rx_buflimit; i++) - usb_kill_urb(xr_usb_serial->read_urbs[i]); - xr_usb_serial->control->needs_remote_wakeup = 0; - usb_autopm_put_interface(xr_usb_serial->control); - } - mutex_unlock(&xr_usb_serial->mutex); -} - -static void xr_usb_serial_tty_cleanup(struct tty_struct *tty) -{ - struct xr_usb_serial *xr_usb_serial = tty->driver_data; - dev_dbg(&xr_usb_serial->control->dev, "%s\n", __func__); - tty_port_put(&xr_usb_serial->port); -} - -static void xr_usb_serial_tty_hangup(struct tty_struct *tty) -{ - struct xr_usb_serial *xr_usb_serial = tty->driver_data; - dev_dbg(&xr_usb_serial->control->dev, "%s\n", __func__); - tty_port_hangup(&xr_usb_serial->port); -} - -static void xr_usb_serial_tty_close(struct tty_struct *tty, struct file *filp) -{ - struct xr_usb_serial *xr_usb_serial = tty->driver_data; - dev_dbg(&xr_usb_serial->control->dev, "%s\n", __func__); - tty_port_close(&xr_usb_serial->port, tty, filp); -} - -static int xr_usb_serial_tty_write(struct tty_struct *tty, - const unsigned char *buf, int count) -{ - struct xr_usb_serial *xr_usb_serial = tty->driver_data; - int stat; - unsigned long flags; - int wbn; - struct xr_usb_serial_wb *wb; - - if (!count) - return 0; - - //dev_vdbg(&xr_usb_serial->data->dev, "%s - count %d\n", __func__, count); - - spin_lock_irqsave(&xr_usb_serial->write_lock, flags); - wbn = xr_usb_serial_wb_alloc(xr_usb_serial); - if (wbn < 0) { - spin_unlock_irqrestore(&xr_usb_serial->write_lock, flags); - return 0; - } - wb = &xr_usb_serial->wb[wbn]; - - if (!xr_usb_serial->dev) { - wb->use = 0; - spin_unlock_irqrestore(&xr_usb_serial->write_lock, flags); - return -ENODEV; - } - - count = (count > xr_usb_serial->writesize) ? xr_usb_serial->writesize : count; - //dev_vdbg(&xr_usb_serial->data->dev, "%s - write %d\n", __func__, count); - memcpy(wb->buf, buf, count); - wb->len = count; - - usb_autopm_get_interface_async(xr_usb_serial->control); - if (xr_usb_serial->susp_count) { - if (!xr_usb_serial->delayed_wb) - xr_usb_serial->delayed_wb = wb; - else - usb_autopm_put_interface_async(xr_usb_serial->control); - spin_unlock_irqrestore(&xr_usb_serial->write_lock, flags); - return count; /* A white lie */ - } - usb_mark_last_busy(xr_usb_serial->dev); - - stat = xr_usb_serial_start_wb(xr_usb_serial, wb); - spin_unlock_irqrestore(&xr_usb_serial->write_lock, flags); - - if (stat < 0) - return stat; - return count; -} - -static int xr_usb_serial_tty_write_room(struct tty_struct *tty) -{ - struct xr_usb_serial *xr_usb_serial = tty->driver_data; - /* - * Do not let the line discipline to know that we have a reserve, - * or it might get too enthusiastic. - */ - return xr_usb_serial_wb_is_avail(xr_usb_serial) ? xr_usb_serial->writesize : 0; -} - -static int xr_usb_serial_tty_chars_in_buffer(struct tty_struct *tty) -{ - struct xr_usb_serial *xr_usb_serial = tty->driver_data; - /* - * if the device was unplugged then any remaining characters fell out - * of the connector ;) - */ - if (xr_usb_serial->disconnected) - return 0; - /* - * This is inaccurate (overcounts), but it works. - */ - return (XR_USB_SERIAL_NW - xr_usb_serial_wb_is_avail(xr_usb_serial)) * xr_usb_serial->writesize; -} - -static void xr_usb_serial_tty_throttle(struct tty_struct *tty) -{ - struct xr_usb_serial *xr_usb_serial = tty->driver_data; - - spin_lock_irq(&xr_usb_serial->read_lock); - xr_usb_serial->throttle_req = 1; - spin_unlock_irq(&xr_usb_serial->read_lock); -} - -static void xr_usb_serial_tty_unthrottle(struct tty_struct *tty) -{ - struct xr_usb_serial *xr_usb_serial = tty->driver_data; - unsigned int was_throttled; - - spin_lock_irq(&xr_usb_serial->read_lock); - was_throttled = xr_usb_serial->throttled; - xr_usb_serial->throttled = 0; - xr_usb_serial->throttle_req = 0; - spin_unlock_irq(&xr_usb_serial->read_lock); - - if (was_throttled) - xr_usb_serial_submit_read_urbs(xr_usb_serial, GFP_KERNEL); -} - -static int xr_usb_serial_tty_break_ctl(struct tty_struct *tty, int state) -{ - struct xr_usb_serial *xr_usb_serial = tty->driver_data; - int retval; - - retval = xr_usb_serial_send_break(xr_usb_serial, state ? 0xffff : 0); - if (retval < 0) - dev_err(&xr_usb_serial->control->dev, "%s - send break failed\n", - __func__); - return retval; -} - -static int xr_usb_serial_tty_tiocmget(struct tty_struct *tty) -{ - struct xr_usb_serial *xr_usb_serial = tty->driver_data; - //dev_dbg(&xr_usb_serial->control->dev, "xr_usb_serial_tty_tiocmget\n"); - return xr_usb_serial_tiocmget(xr_usb_serial); -} - -static int xr_usb_serial_tty_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - struct xr_usb_serial *xr_usb_serial = tty->driver_data; - //dev_dbg(&xr_usb_serial->control->dev, "xr_usb_serial_tty_tiocmset set=0x%x clear=0x%x\n",set,clear); - return xr_usb_serial_tiocmset(xr_usb_serial,set,clear); -} - -static int get_serial_info(struct xr_usb_serial *xr_usb_serial, struct serial_struct __user *info) -{ - struct serial_struct tmp; - - if (!info) - return -EINVAL; - - memset(&tmp, 0, sizeof(tmp)); - tmp.flags = ASYNC_LOW_LATENCY; - tmp.xmit_fifo_size = xr_usb_serial->writesize; - tmp.baud_base = le32_to_cpu(xr_usb_serial->line.dwDTERate); - tmp.close_delay = xr_usb_serial->port.close_delay / 10; - tmp.closing_wait = xr_usb_serial->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ? - ASYNC_CLOSING_WAIT_NONE : - xr_usb_serial->port.closing_wait / 10; - - if (copy_to_user(info, &tmp, sizeof(tmp))) - return -EFAULT; - else - return 0; -} - -static int set_serial_info(struct xr_usb_serial *xr_usb_serial, - struct serial_struct __user *newinfo) -{ - struct serial_struct new_serial; - unsigned int closing_wait, close_delay; - int retval = 0; - - if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) - return -EFAULT; - - close_delay = new_serial.close_delay * 10; - closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ? - ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10; - - mutex_lock(&xr_usb_serial->port.mutex); - - if (!capable(CAP_SYS_ADMIN)) { - if ((close_delay != xr_usb_serial->port.close_delay) || - (closing_wait != xr_usb_serial->port.closing_wait)) - retval = -EPERM; - else - retval = -EOPNOTSUPP; - } else { - xr_usb_serial->port.close_delay = close_delay; - xr_usb_serial->port.closing_wait = closing_wait; - } - - mutex_unlock(&xr_usb_serial->port.mutex); - return retval; -} - -static int xr_usb_serial_tty_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - struct xr_usb_serial *xr_usb_serial = tty->driver_data; - int rv = -ENOIOCTLCMD; - unsigned int channel, reg, val,preciseflags; - int baud_rate = 0; - struct usb_cdc_line_coding newline; - short *data; - switch (cmd) { - case TIOCGSERIAL: /* gets serial port data */ - rv = get_serial_info(xr_usb_serial, (struct serial_struct __user *) arg); - break; - case TIOCSSERIAL: - rv = set_serial_info(xr_usb_serial, (struct serial_struct __user *) arg); - break; - case XR_USB_SERIAL_GET_REG: - if (get_user(channel, (int __user *)arg)) - return -EFAULT; - if (get_user(reg, (int __user *)(arg + sizeof(int)))) - return -EFAULT; - - data = kmalloc(2, GFP_KERNEL); - if (data == NULL) { - dev_err(&xr_usb_serial->control->dev, "%s - Cannot allocate USB buffer.\n", __func__); - return -ENOMEM; - } - - if (channel == -1) - { - rv = xr_usb_serial_get_reg(xr_usb_serial,reg, data); - } - else - { - rv = xr_usb_serial_get_reg_ext(xr_usb_serial,channel,reg, data); - } - if (rv < 0) - { - dev_err(&xr_usb_serial->control->dev, "Cannot get register (%d)\n", rv); - kfree(data); - return -EFAULT; - } - if (put_user(le16_to_cpu(*data), (int __user *)(arg + 2 * sizeof(int)))) - { - dev_err(&xr_usb_serial->control->dev, "Cannot put user result\n"); - kfree(data); - return -EFAULT; - } - rv = 0; - kfree(data); - break; - case XR_USB_SERIAL_SET_REG: - if (get_user(channel, (int __user *)arg)) - return -EFAULT; - if (get_user(reg, (int __user *)(arg + sizeof(int)))) - return -EFAULT; - if (get_user(val, (int __user *)(arg + 2 * sizeof(int)))) - return -EFAULT; - - if (channel == -1) - { - rv = xr_usb_serial_set_reg(xr_usb_serial,reg, val); - } - else - { - rv = xr_usb_serial_set_reg_ext(xr_usb_serial,channel,reg, val); - } - if (rv < 0) - return -EFAULT; - rv = 0; - break; - case XR_USB_SERIAL_LOOPBACK: - if (get_user(channel, (int __user *)arg)) - return -EFAULT; - if (channel == -1) - channel = xr_usb_serial->channel; - rv = xr_usb_serial_set_loopback(xr_usb_serial,channel); - if (rv < 0) - return -EFAULT; - rv = 0; - break; - case XR_USB_SERIAL_SET_GPIO_MODE_REG: - xr_usb_serial_disable(xr_usb_serial); - if (get_user(channel, (int __user *)arg)) - return -EFAULT; - if (get_user(val, (int __user *)(arg + sizeof(int)))) - return -EFAULT; - if (channel == -1) - { - //block = portdata->block; - rv = xr_usb_serial_set_reg(xr_usb_serial,xr_usb_serial->reg_map.uart_gpio_mode_addr, val); - } - else - { - rv = xr_usb_serial_set_reg_ext(xr_usb_serial,channel,xr_usb_serial->reg_map.uart_gpio_mode_addr, val); - } - - dev_dbg(&xr_usb_serial->control->dev, "XR_USB_SERIAL_SET_GPIO_MODE_REG 0x%x val:0x%x \n", xr_usb_serial->reg_map.uart_gpio_mode_addr,val); - xr_usb_serial_enable(xr_usb_serial); - if (rv < 0) - return -EFAULT; - break; - case XR_USB_SERIAL_GET_GPIO_MODE_REG: - xr_usb_serial_disable(xr_usb_serial); - if (get_user(channel, (int __user *)arg)) - return -EFAULT; - - data = kmalloc(2, GFP_KERNEL); - if (data == NULL) { - dev_err(&xr_usb_serial->control->dev, "%s - Cannot allocate USB buffer.\n", __func__); - return -ENOMEM; - } - - if (channel == -1) - { - rv = xr_usb_serial_get_reg(xr_usb_serial,xr_usb_serial->reg_map.uart_gpio_mode_addr, data); - } - else - { - rv = xr_usb_serial_get_reg_ext(xr_usb_serial,channel,xr_usb_serial->reg_map.uart_gpio_mode_addr,data); - } - - xr_usb_serial_enable(xr_usb_serial); - - dev_dbg(&xr_usb_serial->control->dev, "XR_USB_SERIAL_GET_GPIO_MODE_REG 0x%x val:0x%x \n", xr_usb_serial->reg_map.uart_gpio_mode_addr,*data); - - if (rv < 0 ) { - dev_err(&xr_usb_serial->control->dev, "Cannot get register (%d) channel=%d \n", rv,channel); - kfree(data); - return -EFAULT; - } - - if (put_user(data[0], (int __user *)(arg + sizeof(int)))) { - dev_err(&xr_usb_serial->control->dev, "Cannot put user result\n"); - kfree(data); - return -EFAULT; - } - - kfree(data); - break; - case XRIOC_SET_ANY_BAUD_RATE: - if (get_user(baud_rate, (int __user *)arg)) { - dev_dbg(&xr_usb_serial->control->dev, "get_user errot \n"); - return -EFAULT; - } - xr_usb_serial->line.dwDTERate = baud_rate; - memcpy(&newline,&(xr_usb_serial->line),sizeof(struct usb_cdc_line_coding)); - xr_usb_serial_disable(xr_usb_serial); - rv = xr_usb_serial_set_line(xr_usb_serial,&newline); - xr_usb_serial_enable(xr_usb_serial); - dev_dbg(&xr_usb_serial->control->dev, "XRIOC_SET_ANY_BAUD_RATE set baud_rate:%d ret=%d\n", baud_rate,rv); - break; - case XRIOC_SET_PRECISE_FLAGS: - preciseflags = arg; - dev_dbg(&xr_usb_serial->control->dev, "%s VIOC_SET_PRECISE_FLAGS %d\n", __func__, preciseflags); - xr_usb_serial_disable(xr_usb_serial); - if (preciseflags) - { - xr_usb_serial->preciseflags = 1; - } - else - { - xr_usb_serial->preciseflags = 0; - } - xr_usb_serial_set_wide_mode(xr_usb_serial,xr_usb_serial->preciseflags); - xr_usb_serial_enable(xr_usb_serial); - break; - } - - return rv; -} - -#ifdef CONFIG_COMPAT -static long xr_usb_serial_tty_compat_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - void __user *up = compat_ptr(arg); - - switch (cmd) { - case TIOCGSERIAL: /* gets serial port data */ - case TIOCSSERIAL: - case XR_USB_SERIAL_GET_REG: - case XR_USB_SERIAL_SET_REG: - case XR_USB_SERIAL_LOOPBACK: - case XR_USB_SERIAL_SET_GPIO_MODE_REG: - case XR_USB_SERIAL_GET_GPIO_MODE_REG: - case XRIOC_SET_ANY_BAUD_RATE: - case XRIOC_SET_PRECISE_FLAGS: - return xr_usb_serial_tty_ioctl(tty, cmd, arg); - - /* - * the rest has a compatible data structure behind arg, - * but we have to convert it to a proper 64 bit pointer. - */ - default: - return xr_usb_serial_tty_ioctl(tty, cmd, (unsigned long)up); - } -} -#endif - -static void xr_usb_serial_tty_set_termios(struct tty_struct *tty, - struct ktermios *termios_old) -{ - struct xr_usb_serial *xr_usb_serial = tty->driver_data; -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0) - struct ktermios *termios = tty->termios; -#else - struct ktermios *termios = &tty->termios; -#endif - unsigned int cflag = termios->c_cflag; - struct usb_cdc_line_coding newline; - int newctrl = xr_usb_serial->ctrlout; - xr_usb_serial_disable(xr_usb_serial); - newline.dwDTERate = cpu_to_le32(tty_get_baud_rate(tty)); - newline.bCharFormat = termios->c_cflag & CSTOPB ? 1 : 0; - newline.bParityType = termios->c_cflag & PARENB ? - (termios->c_cflag & PARODD ? 1 : 2) + - (termios->c_cflag & CMSPAR ? 2 : 0) : 0; - xr_usb_serial->trans9 = 0; - switch (termios->c_cflag & CSIZE) { - case CS5:/*using CS5 replace of the 9 bit data mode*/ - newline.bDataBits = 9; - xr_usb_serial->trans9 =1; - break; - case CS6: - newline.bDataBits = 6; - break; - case CS7: - newline.bDataBits = 7; - break; - case CS8: - default: - newline.bDataBits = 8; - break; - } - /* FIXME: Needs to clear unsupported bits in the termios */ - xr_usb_serial->clocal = ((termios->c_cflag & CLOCAL) != 0); - - if (!newline.dwDTERate) { - newline.dwDTERate = xr_usb_serial->line.dwDTERate; - newctrl &= ~XR_USB_SERIAL_CTRL_DTR; - } else - newctrl |= XR_USB_SERIAL_CTRL_DTR; - - if (newctrl != xr_usb_serial->ctrlout) - xr_usb_serial_set_control(xr_usb_serial, xr_usb_serial->ctrlout = newctrl); - - if((cflag & CRTSCTS) != (termios_old->c_cflag & CRTSCTS)) - { - /* Set the serial flow mode only when needed */ - xr_usb_serial_set_flow_mode(xr_usb_serial,tty,cflag); - } - - if (xr_usb_serial->trans9) - { - /* Turn on wide mode if we're 9-bit transparent. */ - xr_usb_serial_set_wide_mode(xr_usb_serial,1); - } - else if (!xr_usb_serial->preciseflags) - { - xr_usb_serial_set_wide_mode(xr_usb_serial,0); - } - - if (memcmp(&xr_usb_serial->line, &newline, sizeof newline)) - { - memcpy(&xr_usb_serial->line, &newline, sizeof newline); - dev_dbg(&xr_usb_serial->control->dev, "%s - set line: %d %d %d %d\n", - __func__, - le32_to_cpu(newline.dwDTERate), - newline.bCharFormat, newline.bParityType, - newline.bDataBits); - xr_usb_serial_set_line(xr_usb_serial, &xr_usb_serial->line); - } - xr_usb_serial_enable(xr_usb_serial); -} - -static const struct tty_port_operations xr_usb_serial_port_ops = { - .shutdown = xr_usb_serial_port_shutdown, - .activate = xr_usb_serial_port_activate, - .destruct = xr_usb_serial_port_destruct, -}; - -/* - * USB probe and disconnect routines. - */ - -/* Little helpers: write/read buffers free */ -static void xr_usb_serial_write_buffers_free(struct xr_usb_serial *xr_usb_serial) -{ - int i; - struct xr_usb_serial_wb *wb; - struct usb_device *usb_dev = interface_to_usbdev(xr_usb_serial->control); - - for (wb = &xr_usb_serial->wb[0], i = 0; i < XR_USB_SERIAL_NW; i++, wb++) - usb_free_coherent(usb_dev, xr_usb_serial->writesize, wb->buf, wb->dmah); -} - -static void xr_usb_serial_read_buffers_free(struct xr_usb_serial *xr_usb_serial) -{ - struct usb_device *usb_dev = interface_to_usbdev(xr_usb_serial->control); - int i; - - for (i = 0; i < xr_usb_serial->rx_buflimit; i++) - usb_free_coherent(usb_dev, xr_usb_serial->readsize, - xr_usb_serial->read_buffers[i].base, xr_usb_serial->read_buffers[i].dma); -} - -/* Little helper: write buffers allocate */ -static int xr_usb_serial_write_buffers_alloc(struct xr_usb_serial *xr_usb_serial) -{ - int i; - struct xr_usb_serial_wb *wb; - - for (wb = &xr_usb_serial->wb[0], i = 0; i < XR_USB_SERIAL_NW; i++, wb++) { - wb->buf = usb_alloc_coherent(xr_usb_serial->dev, xr_usb_serial->writesize, GFP_KERNEL, - &wb->dmah); - if (!wb->buf) { - while (i != 0) { - --i; - --wb; - usb_free_coherent(xr_usb_serial->dev, xr_usb_serial->writesize, - wb->buf, wb->dmah); - } - return -ENOMEM; - } - } - return 0; -} - -#ifdef CONFIG_GPIOLIB -static int xr_usb_gpio_get(struct gpio_chip *chip, unsigned int offset) -{ - struct xr_usb_serial *xr_usb_serial = container_of(chip, struct xr_usb_serial, xr_gpio); - int rv; - short gpio_status; - - rv = xr_usb_serial_get_reg(xr_usb_serial, xr_usb_serial->reg_map.uart_gpio_status_addr, - &gpio_status); - if (gpio_status&(1 << offset)) - return 1; - else - return 0; -} - -static void xr_usb_gpio_set(struct gpio_chip *chip, unsigned int offset, int val) -{ - struct xr_usb_serial *xr_usb_serial = container_of(chip, struct xr_usb_serial, xr_gpio); - int rv, tmp; - - tmp = 1 << offset; - if (val) - rv = xr_usb_serial_set_reg(xr_usb_serial, xr_usb_serial->reg_map.uart_gpio_set_addr, tmp); - else - rv = xr_usb_serial_set_reg(xr_usb_serial, xr_usb_serial->reg_map.uart_gpio_clr_addr, tmp); -} - -static int xr_usb_gpio_dir_input(struct gpio_chip *chip, unsigned int offset) -{ - int rv; - short dir_value; - struct xr_usb_serial *xr_usb_serial = container_of(chip, struct xr_usb_serial, xr_gpio); - - rv = xr_usb_serial_get_reg(xr_usb_serial, xr_usb_serial->reg_map.uart_gpio_dir_addr, &dir_value); - dir_value &= ~(1 << offset); - rv = xr_usb_serial_set_reg(xr_usb_serial, xr_usb_serial->reg_map.uart_gpio_dir_addr, (int)dir_value); - return 0; -} - -static int xr_usb_gpio_dir_output(struct gpio_chip *chip, - unsigned int offset, int val) -{ - int rv; - short tmp; - struct xr_usb_serial *xr_usb_serial = container_of(chip, struct xr_usb_serial, xr_gpio); - - rv = xr_usb_serial_get_reg(xr_usb_serial, xr_usb_serial->reg_map.uart_gpio_dir_addr, &tmp); - printk ("gpio_dir_output, offset = %d\n", offset); - printk ("gpio_dir_output before set_reg = 0x%02x\n", tmp); - tmp |= (1 << offset); - rv = xr_usb_serial_set_reg(xr_usb_serial, xr_usb_serial->reg_map.uart_gpio_dir_addr, (int)tmp); - rv = xr_usb_serial_get_reg(xr_usb_serial, xr_usb_serial->reg_map.uart_gpio_dir_addr, &tmp); - printk ("gpio_dir_output after set_reg = 0x%02x\n", tmp); - - if (offset > 7) { - rv = xr_usb_serial_get_reg(xr_usb_serial, xr_usb_serial->reg_map.uart_gpio_mode_addr, &tmp); - tmp &= ~(1 << offset); - rv = xr_usb_serial_set_reg(xr_usb_serial, xr_usb_serial->reg_map.uart_gpio_mode_addr, - (int)tmp); - } - - return 0; -} -#endif - -static int xr_usb_serial_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_cdc_union_desc *union_header = NULL; - struct usb_cdc_country_functional_desc *cfd = NULL; - unsigned char *buffer = intf->altsetting->extra; - int buflen = intf->altsetting->extralen; - struct usb_interface *control_interface; - struct usb_interface *data_interface; - struct usb_endpoint_descriptor *epctrl = NULL; - struct usb_endpoint_descriptor *epread = NULL; - struct usb_endpoint_descriptor *epwrite = NULL; - struct usb_device *usb_dev = interface_to_usbdev(intf); - struct xr_usb_serial *xr_usb_serial; - int minor; - int ctrlsize, readsize; - u8 *buf; - u8 ac_management_function = 0; - u8 call_management_function = 0; - int call_interface_num = -1; - int data_interface_num = -1; - unsigned long quirks; - int num_rx_buf; - int i; - int combined_interfaces = 0; - struct device *tty_dev; - int rv = -ENOMEM; -#ifdef CONFIG_GPIOLIB - int gpiochip_base; -#endif - - /* normal quirks */ - quirks = (unsigned long)id->driver_info; - - if (quirks == IGNORE_DEVICE) - return -ENODEV; - - num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : XR_USB_SERIAL_NR; - - dev_dbg(&intf->dev, "USB_device_id idVendor:%04x, idProduct %04x\n",id->idVendor,id->idProduct); - - /* handle quirks deadly to normal probing*/ - if (quirks == NO_UNION_NORMAL) { - data_interface = usb_ifnum_to_if(usb_dev, 1); - control_interface = usb_ifnum_to_if(usb_dev, 0); - goto skip_normal_probe; - } - - /* normal probing*/ - if (!buffer) { - dev_err(&intf->dev, "Weird descriptor references\n"); - return -EINVAL; - } - - if (!buflen) { - if (intf->cur_altsetting->endpoint && - intf->cur_altsetting->endpoint->extralen && - intf->cur_altsetting->endpoint->extra) { - dev_dbg(&intf->dev, - "Seeking extra descriptors on endpoint\n"); - buflen = intf->cur_altsetting->endpoint->extralen; - buffer = intf->cur_altsetting->endpoint->extra; - } else { - dev_err(&intf->dev, - "Zero length descriptor references\n"); - return -EINVAL; - } - } - - while (buflen > 0) { - if (buffer[1] != USB_DT_CS_INTERFACE) { - dev_err(&intf->dev, "skipping garbage\n"); - goto next_desc; - } - - switch (buffer[2]) { - case USB_CDC_UNION_TYPE: /* we've found it */ - if (union_header) { - dev_err(&intf->dev, "More than one " - "union descriptor, skipping ...\n"); - goto next_desc; - } - union_header = (struct usb_cdc_union_desc *)buffer; - break; - case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/ - cfd = (struct usb_cdc_country_functional_desc *)buffer; - break; - case USB_CDC_HEADER_TYPE: /* maybe check version */ - break; /* for now we ignore it */ - case USB_CDC_ACM_TYPE: - ac_management_function = buffer[3]; - break; - case USB_CDC_CALL_MANAGEMENT_TYPE: - call_management_function = buffer[3]; - call_interface_num = buffer[4]; - if ((quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3) - dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n"); - break; - default: - /* there are LOTS more CDC descriptors that - * could legitimately be found here. - */ - dev_dbg(&intf->dev, "Ignoring descriptor: " - "type %02x, length %d\n", - buffer[2], buffer[0]); - break; - } -next_desc: - buflen -= buffer[0]; - buffer += buffer[0]; - } - - if (!union_header) { - if (call_interface_num > 0) { - dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n"); - /* quirks for Droids MuIn LCD */ - if (quirks & NO_DATA_INTERFACE) - data_interface = usb_ifnum_to_if(usb_dev, 0); - else - data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num)); - control_interface = intf; - } else { - if (intf->cur_altsetting->desc.bNumEndpoints != 3) { - dev_dbg(&intf->dev,"No union descriptor, giving up\n"); - return -ENODEV; - } else { - dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n"); - combined_interfaces = 1; - control_interface = data_interface = intf; - goto look_for_collapsed_interface; - } - } - } else { - control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0); - data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0)); - if (!control_interface || !data_interface) { - dev_dbg(&intf->dev, "no interfaces\n"); - return -ENODEV; - } - } - - if (data_interface_num != call_interface_num) - dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n"); - - if (control_interface == data_interface) { - /* some broken devices designed for windows work this way */ - dev_warn(&intf->dev,"Control and data interfaces are not separated!\n"); - combined_interfaces = 1; - /* a popular other OS doesn't use it */ - quirks |= NO_CAP_LINE; - if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) { - dev_err(&intf->dev, "This needs exactly 3 endpoints\n"); - return -EINVAL; - } -look_for_collapsed_interface: - for (i = 0; i < 3; i++) { - struct usb_endpoint_descriptor *ep; - ep = &data_interface->cur_altsetting->endpoint[i].desc; - - if (usb_endpoint_is_int_in(ep)) - epctrl = ep; - else if (usb_endpoint_is_bulk_out(ep)) - epwrite = ep; - else if (usb_endpoint_is_bulk_in(ep)) - epread = ep; - else - return -EINVAL; - } - if (!epctrl || !epread || !epwrite) - return -ENODEV; - else - goto made_compressed_probe; - } - -skip_normal_probe: - - /*workaround for switched interfaces */ - if (data_interface->cur_altsetting->desc.bInterfaceClass - != CDC_DATA_INTERFACE_TYPE) { - if (control_interface->cur_altsetting->desc.bInterfaceClass - == CDC_DATA_INTERFACE_TYPE) { - struct usb_interface *t; - dev_dbg(&intf->dev, - "Your device has switched interfaces.\n"); - t = control_interface; - control_interface = data_interface; - data_interface = t; - } else { - return -EINVAL; - } - } - - /* Accept probe requests only for the control interface */ - if (!combined_interfaces && intf != control_interface) - return -ENODEV; - - if (!combined_interfaces && usb_interface_claimed(data_interface)) { - /* valid in this context */ - dev_dbg(&intf->dev, "The data interface isn't available\n"); - return -EBUSY; - } - - - if (data_interface->cur_altsetting->desc.bNumEndpoints < 2 || - control_interface->cur_altsetting->desc.bNumEndpoints == 0) - return -EINVAL; - - epctrl = &control_interface->cur_altsetting->endpoint[0].desc; - epread = &data_interface->cur_altsetting->endpoint[0].desc; - epwrite = &data_interface->cur_altsetting->endpoint[1].desc; - - - /* workaround for switched endpoints */ - if (!usb_endpoint_dir_in(epread)) { - /* descriptors are swapped */ - struct usb_endpoint_descriptor *t; - dev_dbg(&intf->dev, - "The data interface has switched endpoints\n"); - t = epread; - epread = epwrite; - epwrite = t; - } -made_compressed_probe: - dev_dbg(&intf->dev, "interfaces are valid\n"); - - xr_usb_serial = kzalloc(sizeof(struct xr_usb_serial), GFP_KERNEL); - if (xr_usb_serial == NULL) { - dev_err(&intf->dev, "out of memory (xr_usb_serial kzalloc)\n"); - goto alloc_fail; - } - - minor = xr_usb_serial_alloc_minor(xr_usb_serial); - if (minor == XR_USB_SERIAL_TTY_MINORS) { - dev_err(&intf->dev, "no more free xr_usb_serial devices\n"); - kfree(xr_usb_serial); - return -ENODEV; - } - - ctrlsize = usb_endpoint_maxp(epctrl); - readsize = usb_endpoint_maxp(epread) * - (quirks == SINGLE_RX_URB ? 1 : 2); - xr_usb_serial->combined_interfaces = combined_interfaces; - xr_usb_serial->writesize = usb_endpoint_maxp(epwrite) * 20; - xr_usb_serial->control = control_interface; - xr_usb_serial->data = data_interface; - xr_usb_serial->minor = minor; - xr_usb_serial->dev = usb_dev; - xr_usb_serial->ctrl_caps = ac_management_function; - if (quirks & NO_CAP_LINE) - xr_usb_serial->ctrl_caps &= ~USB_CDC_CAP_LINE; - xr_usb_serial->ctrlsize = ctrlsize; - xr_usb_serial->readsize = readsize; - xr_usb_serial->rx_buflimit = num_rx_buf; - INIT_WORK(&xr_usb_serial->work, xr_usb_serial_softint); - spin_lock_init(&xr_usb_serial->write_lock); - spin_lock_init(&xr_usb_serial->read_lock); - mutex_init(&xr_usb_serial->mutex); - xr_usb_serial->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress); - xr_usb_serial->is_int_ep = usb_endpoint_xfer_int(epread); - if (xr_usb_serial->is_int_ep) - xr_usb_serial->bInterval = epread->bInterval; - tty_port_init(&xr_usb_serial->port); - xr_usb_serial->port.ops = &xr_usb_serial_port_ops; - xr_usb_serial->DeviceVendor = id->idVendor; - xr_usb_serial->DeviceProduct = id->idProduct; -#if 0 - if((xr_usb_serial->DeviceProduct&0xfff0) == 0x1410) - {//map the serial port A B C D to blocknum 0 1 2 3 for the xr21v141x device - xr_usb_serial->channel = epwrite->bEndpointAddress - 1; - } - else if((xr_usb_serial->DeviceProduct&0xfff0) == 0x1420) - {//map the serial port A B C D to blocknum 0 2 4 6 for the xr21B142x device - xr_usb_serial->channel = (epwrite->bEndpointAddress - 4)*2; - } - else - { - xr_usb_serial->channel = epwrite->bEndpointAddress; - } -#else - xr_usb_serial->channel = epwrite->bEndpointAddress; - dev_dbg(&intf->dev, "epwrite->bEndpointAddress =%d\n",epwrite->bEndpointAddress); -#endif - buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &xr_usb_serial->ctrl_dma); - if (!buf) { - dev_err(&intf->dev, "out of memory (ctrl buffer alloc)\n"); - goto alloc_fail2; - } - xr_usb_serial->ctrl_buffer = buf; - - if (xr_usb_serial_write_buffers_alloc(xr_usb_serial) < 0) { - dev_err(&intf->dev, "out of memory (write buffer alloc)\n"); - goto alloc_fail4; - } - - xr_usb_serial->ctrlurb = usb_alloc_urb(0, GFP_KERNEL); - if (!xr_usb_serial->ctrlurb) { - dev_err(&intf->dev, "out of memory (ctrlurb kmalloc)\n"); - goto alloc_fail5; - } - for (i = 0; i < num_rx_buf; i++) { - struct xr_usb_serial_rb *rb = &(xr_usb_serial->read_buffers[i]); - struct urb *urb; - - rb->base = usb_alloc_coherent(xr_usb_serial->dev, readsize, GFP_KERNEL, - &rb->dma); - if (!rb->base) { - dev_err(&intf->dev, "out of memory " - "(read bufs usb_alloc_coherent)\n"); - goto alloc_fail6; - } - rb->index = i; - rb->instance = xr_usb_serial; - - urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) { - dev_err(&intf->dev, - "out of memory (read urbs usb_alloc_urb)\n"); - goto alloc_fail6; - } - urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - urb->transfer_dma = rb->dma; - if (xr_usb_serial->is_int_ep) { - usb_fill_int_urb(urb, xr_usb_serial->dev, - xr_usb_serial->rx_endpoint, - rb->base, - xr_usb_serial->readsize, - xr_usb_serial_read_bulk_callback, rb, - xr_usb_serial->bInterval); - } else { - usb_fill_bulk_urb(urb, xr_usb_serial->dev, - xr_usb_serial->rx_endpoint, - rb->base, - xr_usb_serial->readsize, - xr_usb_serial_read_bulk_callback, rb); - } - - xr_usb_serial->read_urbs[i] = urb; - __set_bit(i, &xr_usb_serial->read_urbs_free); - } - for (i = 0; i < XR_USB_SERIAL_NW; i++) { - struct xr_usb_serial_wb *snd = &(xr_usb_serial->wb[i]); - - snd->urb = usb_alloc_urb(0, GFP_KERNEL); - if (snd->urb == NULL) { - dev_err(&intf->dev, - "out of memory (write urbs usb_alloc_urb)\n"); - goto alloc_fail7; - } - - if (usb_endpoint_xfer_int(epwrite)) - usb_fill_int_urb(snd->urb, usb_dev, -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0) - usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), -#else - usb_sndintpipe(usb_dev, epwrite->bEndpointAddress), -#endif - NULL, xr_usb_serial->writesize, xr_usb_serial_write_bulk, snd, epwrite->bInterval); - else - usb_fill_bulk_urb(snd->urb, usb_dev, - usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), - NULL, xr_usb_serial->writesize, xr_usb_serial_write_bulk, snd); - snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - snd->instance = xr_usb_serial; - } - - usb_set_intfdata(intf, xr_usb_serial); - - i = device_create_file(&intf->dev, &dev_attr_bmCapabilities); - if (i < 0) - goto alloc_fail7; - - if (cfd) { /* export the country data */ - xr_usb_serial->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL); - if (!xr_usb_serial->country_codes) - goto skip_countries; - xr_usb_serial->country_code_size = cfd->bLength - 4; - memcpy(xr_usb_serial->country_codes, (u8 *)&cfd->wCountyCode0, - cfd->bLength - 4); - xr_usb_serial->country_rel_date = cfd->iCountryCodeRelDate; - - i = device_create_file(&intf->dev, &dev_attr_wCountryCodes); - if (i < 0) { - kfree(xr_usb_serial->country_codes); - xr_usb_serial->country_codes = NULL; - xr_usb_serial->country_code_size = 0; - goto skip_countries; - } - - i = device_create_file(&intf->dev, - &dev_attr_iCountryCodeRelDate); - if (i < 0) { - device_remove_file(&intf->dev, &dev_attr_wCountryCodes); - kfree(xr_usb_serial->country_codes); - xr_usb_serial->country_codes = NULL; - xr_usb_serial->country_code_size = 0; - goto skip_countries; - } - } - -skip_countries: - usb_fill_int_urb(xr_usb_serial->ctrlurb, usb_dev, - usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress), - xr_usb_serial->ctrl_buffer, ctrlsize, xr_usb_serial_ctrl_irq, xr_usb_serial, - /* works around buggy devices */ - epctrl->bInterval ? epctrl->bInterval : 0xff); - xr_usb_serial->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - xr_usb_serial->ctrlurb->transfer_dma = xr_usb_serial->ctrl_dma; - - dev_info(&intf->dev, "ttyXR_USB_SERIAL%d: USB XR_USB_SERIAL device\n", minor); - - xr_usb_serial_pre_setup(xr_usb_serial); - - xr_usb_serial_set_control(xr_usb_serial, xr_usb_serial->ctrlout); - - xr_usb_serial->line.dwDTERate = cpu_to_le32(9600); - xr_usb_serial->line.bDataBits = 8; - xr_usb_serial_set_line(xr_usb_serial, &xr_usb_serial->line); - - usb_driver_claim_interface(&xr_usb_serial_driver, data_interface, xr_usb_serial); - usb_set_intfdata(data_interface, xr_usb_serial); - - usb_get_intf(control_interface); -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0) - tty_register_device(xr_usb_serial_tty_driver, minor, &control_interface->dev); -#else - tty_dev = tty_port_register_device(&xr_usb_serial->port, xr_usb_serial_tty_driver, minor, - &control_interface->dev); - if (IS_ERR(tty_dev)) { - rv = PTR_ERR(tty_dev); - goto alloc_fail8; - } -#endif - -#ifdef CONFIG_GPIOLIB - /* Setup GPIO cotroller */ - gpiochip_base = 0; - - xr_usb_serial->xr_gpio.owner = THIS_MODULE; - xr_usb_serial->xr_gpio.label = dev_name(&control_interface->dev); - xr_usb_serial->xr_gpio.direction_input = xr_usb_gpio_dir_input; - xr_usb_serial->xr_gpio.get = xr_usb_gpio_get; - xr_usb_serial->xr_gpio.direction_output = xr_usb_gpio_dir_output; - xr_usb_serial->xr_gpio.set = xr_usb_gpio_set; - xr_usb_serial->xr_gpio.base = gpiochip_base; - xr_usb_serial->xr_gpio.ngpio = 10; - xr_usb_serial->xr_gpio.can_sleep = 1; - - rv = gpiochip_add(&xr_usb_serial->xr_gpio); - - if (rv != 0) { - // gpiochip numbers not available, start from 0 - xr_usb_serial->xr_gpio.base = 0; - } - - while (rv != 0) { - xr_usb_serial->xr_gpio.base += 10; - - if (xr_usb_serial->xr_gpio.base > 502) { - // max gpio number = 512 - // we ran out of gpios?? - break; - } - rv = gpiochip_add(&xr_usb_serial->xr_gpio); - } - xr_usb_serial->rv_gpio_created = rv; - if (rv == 0) { - dev_dbg(&xr_usb_serial->control->dev, "gpiochip%d added", - xr_usb_serial->xr_gpio.base); - } else { - dev_dbg(&xr_usb_serial->control->dev, "failed to add gpiochip\n"); - } - -#endif - - return 0; -alloc_fail8: - if (xr_usb_serial->country_codes) { - device_remove_file(&xr_usb_serial->control->dev, - &dev_attr_wCountryCodes); - device_remove_file(&xr_usb_serial->control->dev, - &dev_attr_iCountryCodeRelDate); - } - device_remove_file(&xr_usb_serial->control->dev, &dev_attr_bmCapabilities); -alloc_fail7: - usb_set_intfdata(intf, NULL); - for (i = 0; i < XR_USB_SERIAL_NW; i++) - usb_free_urb(xr_usb_serial->wb[i].urb); -alloc_fail6: - for (i = 0; i < num_rx_buf; i++) - usb_free_urb(xr_usb_serial->read_urbs[i]); - xr_usb_serial_read_buffers_free(xr_usb_serial); - usb_free_urb(xr_usb_serial->ctrlurb); -alloc_fail5: - xr_usb_serial_write_buffers_free(xr_usb_serial); -alloc_fail4: - usb_free_coherent(usb_dev, ctrlsize, xr_usb_serial->ctrl_buffer, xr_usb_serial->ctrl_dma); -alloc_fail2: - xr_usb_serial_release_minor(xr_usb_serial); - kfree(xr_usb_serial); -alloc_fail: - return rv; -} - -static void stop_data_traffic(struct xr_usb_serial *xr_usb_serial) -{ - int i; - - dev_dbg(&xr_usb_serial->control->dev, "%s\n", __func__); - - usb_kill_urb(xr_usb_serial->ctrlurb); - for (i = 0; i < XR_USB_SERIAL_NW; i++) - usb_kill_urb(xr_usb_serial->wb[i].urb); - for (i = 0; i < xr_usb_serial->rx_buflimit; i++) - usb_kill_urb(xr_usb_serial->read_urbs[i]); - - cancel_work_sync(&xr_usb_serial->work); -} - -static void xr_usb_serial_disconnect(struct usb_interface *intf) -{ - struct xr_usb_serial *xr_usb_serial = usb_get_intfdata(intf); - struct usb_device *usb_dev = interface_to_usbdev(intf); - struct tty_struct *tty; - int i; - - dev_dbg(&intf->dev, "%s\n", __func__); - - /* sibling interface is already cleaning up */ - if (!xr_usb_serial) - return; - - mutex_lock(&xr_usb_serial->mutex); - xr_usb_serial->disconnected = true; - if (xr_usb_serial->country_codes) { - device_remove_file(&xr_usb_serial->control->dev, - &dev_attr_wCountryCodes); - device_remove_file(&xr_usb_serial->control->dev, - &dev_attr_iCountryCodeRelDate); - } - device_remove_file(&xr_usb_serial->control->dev, &dev_attr_bmCapabilities); - usb_set_intfdata(xr_usb_serial->control, NULL); - usb_set_intfdata(xr_usb_serial->data, NULL); - mutex_unlock(&xr_usb_serial->mutex); - - tty = tty_port_tty_get(&xr_usb_serial->port); - if (tty) { - tty_vhangup(tty); - tty_kref_put(tty); - } - stop_data_traffic(xr_usb_serial); -#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 0) - tty_unregister_device(xr_usb_serial_tty_driver, xr_usb_serial->minor); -#endif - - usb_free_urb(xr_usb_serial->ctrlurb); - for (i = 0; i < XR_USB_SERIAL_NW; i++) - usb_free_urb(xr_usb_serial->wb[i].urb); - for (i = 0; i < xr_usb_serial->rx_buflimit; i++) - usb_free_urb(xr_usb_serial->read_urbs[i]); - xr_usb_serial_write_buffers_free(xr_usb_serial); - usb_free_coherent(usb_dev, xr_usb_serial->ctrlsize, xr_usb_serial->ctrl_buffer, xr_usb_serial->ctrl_dma); - xr_usb_serial_read_buffers_free(xr_usb_serial); - - if (!xr_usb_serial->combined_interfaces) - usb_driver_release_interface(&xr_usb_serial_driver, intf == xr_usb_serial->control ? - xr_usb_serial->data : xr_usb_serial->control); - - tty_port_put(&xr_usb_serial->port); -} - -#ifdef CONFIG_PM -static int xr_usb_serial_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct xr_usb_serial *xr_usb_serial = usb_get_intfdata(intf); - int cnt; - - if (PMSG_IS_AUTO(message)) { - int b; - - spin_lock_irq(&xr_usb_serial->write_lock); - b = xr_usb_serial->transmitting; - spin_unlock_irq(&xr_usb_serial->write_lock); - if (b) - return -EBUSY; - } - - spin_lock_irq(&xr_usb_serial->read_lock); - spin_lock(&xr_usb_serial->write_lock); - cnt = xr_usb_serial->susp_count++; - spin_unlock(&xr_usb_serial->write_lock); - spin_unlock_irq(&xr_usb_serial->read_lock); - - if (cnt) - return 0; - -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 2) - if (test_bit(ASYNCB_INITIALIZED, &xr_usb_serial->port.flags)) -#endif - stop_data_traffic(xr_usb_serial); - - return 0; -} - -static int xr_usb_serial_resume(struct usb_interface *intf) -{ - struct xr_usb_serial *xr_usb_serial = usb_get_intfdata(intf); - struct xr_usb_serial_wb *wb; - int rv = 0; - int cnt; - - spin_lock_irq(&xr_usb_serial->read_lock); - xr_usb_serial->susp_count -= 1; - cnt = xr_usb_serial->susp_count; - spin_unlock_irq(&xr_usb_serial->read_lock); - - if (cnt) - return 0; - -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0) - if (test_bit(ASYNCB_INITIALIZED, &xr_usb_serial->port.flags)) { -#else - if (tty_port_initialized(&xr_usb_serial->port)) { -#endif - rv = usb_submit_urb(xr_usb_serial->ctrlurb, GFP_NOIO); - - spin_lock_irq(&xr_usb_serial->write_lock); - if (xr_usb_serial->delayed_wb) { - wb = xr_usb_serial->delayed_wb; - xr_usb_serial->delayed_wb = NULL; - spin_unlock_irq(&xr_usb_serial->write_lock); - xr_usb_serial_start_wb(xr_usb_serial, wb); - } else { - spin_unlock_irq(&xr_usb_serial->write_lock); - } - - /* - * delayed error checking because we must - * do the write path at all cost - */ - if (rv < 0) - goto err_out; - - rv = xr_usb_serial_submit_read_urbs(xr_usb_serial, GFP_NOIO); - } - -err_out: - return rv; -} - -static int xr_usb_serial_reset_resume(struct usb_interface *intf) -{ - struct xr_usb_serial *xr_usb_serial = usb_get_intfdata(intf); -#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 9, 0) -#else - struct tty_struct *tty; -#endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0) - if (test_bit(ASYNCB_INITIALIZED, &xr_usb_serial->port.flags)){ -#else - if (tty_port_initialized(&xr_usb_serial->port)) { -#endif -#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 9, 0) - tty_port_tty_hangup(&xr_usb_serial->port, false); -#else - tty = tty_port_tty_get(&xr_usb_serial->port); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } -#endif - } - return xr_usb_serial_resume(intf); -} - -#endif /* CONFIG_PM */ - -/* - * USB driver structure. - */ -static const struct usb_device_id xr_usb_serial_ids[] = { - { USB_DEVICE(0x04e2, 0x1410)}, - { USB_DEVICE(0x04e2, 0x1411)}, - { USB_DEVICE(0x04e2, 0x1412)}, - { USB_DEVICE(0x04e2, 0x1414)}, - { USB_DEVICE(0x04e2, 0x1420)}, - { USB_DEVICE(0x04e2, 0x1421)}, - { USB_DEVICE(0x04e2, 0x1422)}, - { USB_DEVICE(0x04e2, 0x1424)}, - { USB_DEVICE(0x04e2, 0x1400)}, - { USB_DEVICE(0x04e2, 0x1401)}, - { USB_DEVICE(0x04e2, 0x1402)}, - { USB_DEVICE(0x04e2, 0x1403)}, - { } -}; - -MODULE_DEVICE_TABLE(usb, xr_usb_serial_ids); - -static struct usb_driver xr_usb_serial_driver = { - .name = "cdc_xr_usb_serial", - .probe = xr_usb_serial_probe, - .disconnect = xr_usb_serial_disconnect, -#ifdef CONFIG_PM - .suspend = xr_usb_serial_suspend, - .resume = xr_usb_serial_resume, - .reset_resume = xr_usb_serial_reset_resume, -#endif - .id_table = xr_usb_serial_ids, -#ifdef CONFIG_PM - .supports_autosuspend = 1, -#endif - .disable_hub_initiated_lpm = 1, -}; - -/* - * TTY driver structures. - */ - -static const struct tty_operations xr_usb_serial_ops = { - .install = xr_usb_serial_tty_install, - .open = xr_usb_serial_tty_open, - .close = xr_usb_serial_tty_close, - .cleanup = xr_usb_serial_tty_cleanup, - .hangup = xr_usb_serial_tty_hangup, - .write = xr_usb_serial_tty_write, - .write_room = xr_usb_serial_tty_write_room, -#ifdef CONFIG_COMPAT - .compat_ioctl = xr_usb_serial_tty_compat_ioctl, -#endif - .ioctl = xr_usb_serial_tty_ioctl, - .throttle = xr_usb_serial_tty_throttle, - .unthrottle = xr_usb_serial_tty_unthrottle, - .chars_in_buffer = xr_usb_serial_tty_chars_in_buffer, - .break_ctl = xr_usb_serial_tty_break_ctl, - .set_termios = xr_usb_serial_tty_set_termios, - .tiocmget = xr_usb_serial_tty_tiocmget, - .tiocmset = xr_usb_serial_tty_tiocmset, -}; - -/* - * Init / exit. - */ - -static int __init xr_usb_serial_init(void) -{ - int retval; - xr_usb_serial_tty_driver = alloc_tty_driver(XR_USB_SERIAL_TTY_MINORS); - if (!xr_usb_serial_tty_driver) - return -ENOMEM; - xr_usb_serial_tty_driver->driver_name = "xr_usb_serial", - xr_usb_serial_tty_driver->name = "ttyXRUSB", - xr_usb_serial_tty_driver->major = XR_USB_SERIAL_TTY_MAJOR, - xr_usb_serial_tty_driver->minor_start = 0, - xr_usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL, - xr_usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL, - xr_usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; - xr_usb_serial_tty_driver->init_termios = tty_std_termios; - xr_usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | - HUPCL | CLOCAL; - tty_set_operations(xr_usb_serial_tty_driver, &xr_usb_serial_ops); - - retval = tty_register_driver(xr_usb_serial_tty_driver); - if (retval) { - put_tty_driver(xr_usb_serial_tty_driver); - return retval; - } - - retval = usb_register(&xr_usb_serial_driver); - if (retval) { - tty_unregister_driver(xr_usb_serial_tty_driver); - put_tty_driver(xr_usb_serial_tty_driver); - return retval; - } - - printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n"); - - return 0; -} - -static void __exit xr_usb_serial_exit(void) -{ - usb_deregister(&xr_usb_serial_driver); - tty_unregister_driver(xr_usb_serial_tty_driver); - put_tty_driver(xr_usb_serial_tty_driver); -} - -module_init(xr_usb_serial_init); -module_exit(xr_usb_serial_exit); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_CHARDEV_MAJOR(XR_USB_SERIAL_TTY_MAJOR); diff --git a/package/kernel/xr-usb-serial/src/xr_usb_serial_common.h b/package/kernel/xr-usb-serial/src/xr_usb_serial_common.h deleted file mode 100644 index efcbba8ce..000000000 --- a/package/kernel/xr-usb-serial/src/xr_usb_serial_common.h +++ /dev/null @@ -1,197 +0,0 @@ -/* - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/* - * CMSPAR, some architectures can't have space and mark parity. - */ - -#ifndef CMSPAR -#define CMSPAR 0 -#endif - -/* - * Major and minor numbers. - */ - -#define XR_USB_SERIAL_TTY_MAJOR 266 -#define XR_USB_SERIAL_TTY_MINORS 32 - -/* - * Requests. - */ - -#define USB_RT_XR_USB_SERIAL (USB_TYPE_CLASS | USB_RECIP_INTERFACE) - -/* - * Output control lines. - */ - -#define XR_USB_SERIAL_CTRL_DTR 0x01 -#define XR_USB_SERIAL_CTRL_RTS 0x02 - -/* - * Input control lines and line errors. - */ - -#define XR_USB_SERIAL_CTRL_DCD 0x01 -#define XR_USB_SERIAL_CTRL_DSR 0x02 -#define XR_USB_SERIAL_CTRL_BRK 0x04 -#define XR_USB_SERIAL_CTRL_RI 0x08 - -#define XR_USB_SERIAL_CTRL_FRAMING 0x10 -#define XR_USB_SERIAL_CTRL_PARITY 0x20 -#define XR_USB_SERIAL_CTRL_OVERRUN 0x40 - -/* - * Internal driver structures. - */ - -/* - * The only reason to have several buffers is to accommodate assumptions - * in line disciplines. They ask for empty space amount, receive our URB size, - * and proceed to issue several 1-character writes, assuming they will fit. - * The very first write takes a complete URB. Fortunately, this only happens - * when processing onlcr, so we only need 2 buffers. These values must be - * powers of 2. - */ -#define XR_USB_SERIAL_NW 16 -#define XR_USB_SERIAL_NR 16 - -#define RAMCTL_BUFFER_PARITY 0x1 -#define RAMCTL_BUFFER_BREAK 0x2 -#define RAMCTL_BUFFER_FRAME 0x4 -#define RAMCTL_BUFFER_OVERRUN 0x8 - -struct xr_usb_serial_wb { - unsigned char *buf; - dma_addr_t dmah; - int len; - int use; - struct urb *urb; - struct xr_usb_serial *instance; -}; - -struct xr_usb_serial_rb { - int size; - unsigned char *base; - dma_addr_t dma; - int index; - struct xr_usb_serial *instance; -}; - -struct reg_addr_map { - unsigned int uart_enable_addr; - unsigned int uart_format_addr; - unsigned int uart_flow_addr; - unsigned int uart_loopback_addr; - unsigned int uart_xon_char_addr; - unsigned int uart_xoff_char_addr; - unsigned int uart_gpio_mode_addr; - unsigned int uart_gpio_dir_addr; - unsigned int uart_gpio_set_addr; - unsigned int uart_gpio_clr_addr; - unsigned int uart_gpio_status_addr; - unsigned int tx_break_addr; - unsigned int uart_custom_driver; - unsigned int uart_low_latency; -}; - -struct xr_usb_serial { - struct usb_device *dev; /* the corresponding usb device */ - struct usb_interface *control; /* control interface */ - struct usb_interface *data; /* data interface */ - struct tty_port port; /* our tty port data */ - struct urb *ctrlurb; /* urbs */ - u8 *ctrl_buffer; /* buffers of urbs */ - dma_addr_t ctrl_dma; /* dma handles of buffers */ - u8 *country_codes; /* country codes from device */ - unsigned int country_code_size; /* size of this buffer */ - unsigned int country_rel_date; /* release date of version */ - struct xr_usb_serial_wb wb[XR_USB_SERIAL_NW]; - unsigned long read_urbs_free; - struct urb *read_urbs[XR_USB_SERIAL_NR]; - struct xr_usb_serial_rb read_buffers[XR_USB_SERIAL_NR]; - int rx_buflimit; - int rx_endpoint; - spinlock_t read_lock; - int write_used; /* number of non-empty write buffers */ - int transmitting; - spinlock_t write_lock; - struct mutex mutex; - bool disconnected; - struct usb_cdc_line_coding line; /* bits, stop, parity */ - struct work_struct work; /* work queue entry for line discipline waking up */ - unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */ - unsigned int ctrlout; /* output control lines (DTR, RTS) */ - unsigned int writesize; /* max packet size for the output bulk endpoint */ - unsigned int readsize,ctrlsize; /* buffer sizes for freeing */ - unsigned int minor; /* xr_usb_serial minor number */ - unsigned char clocal; /* termios CLOCAL */ - unsigned int ctrl_caps; /* control capabilities from the class specific header */ - unsigned int susp_count; /* number of suspended interfaces */ - unsigned int combined_interfaces:1; /* control and data collapsed */ - unsigned int is_int_ep:1; /* interrupt endpoints contrary to spec used */ - unsigned int throttled:1; /* actually throttled */ - unsigned int throttle_req:1; /* throttle requested */ - u8 bInterval; - struct xr_usb_serial_wb *delayed_wb; /* write queued for a device about to be woken */ - unsigned int channel; - int preciseflags; /* USB: wide mode, TTY: flags per character */ - int trans9; /* USB: wide mode, serial 9N1 */ - int have_extra_byte; - int extra_byte; - - unsigned short DeviceVendor; - unsigned short DeviceProduct; - struct reg_addr_map reg_map; -#ifdef CONFIG_GPIOLIB - struct gpio_chip xr_gpio; - int rv_gpio_created; -#endif -}; - -#define CDC_DATA_INTERFACE_TYPE 0x0a - -/* constants describing various quirks and errors */ -#define NO_UNION_NORMAL 1 -#define SINGLE_RX_URB 2 -#define NO_CAP_LINE 4 -#define NOT_A_MODEM 8 -#define NO_DATA_INTERFACE 16 -#define IGNORE_DEVICE 32 - - -#define UART_ENABLE_TX 1 -#define UART_ENABLE_RX 2 - -#define UART_GPIO_CLR_DTR 0x8 -#define UART_GPIO_SET_DTR 0x8 -#define UART_GPIO_CLR_RTS 0x20 -#define UART_GPIO_SET_RTS 0x20 - -#define LOOPBACK_ENABLE_TX_RX 1 -#define LOOPBACK_ENABLE_RTS_CTS 2 -#define LOOPBACK_ENABLE_DTR_DSR 4 - -#define UART_FLOW_MODE_NONE 0x0 -#define UART_FLOW_MODE_HW 0x1 -#define UART_FLOW_MODE_SW 0x2 - -#define UART_GPIO_MODE_SEL_GPIO 0x0 -#define UART_GPIO_MODE_SEL_RTS_CTS 0x1 - -#define XR2280x_FUNC_MGR_OFFSET 0x40 - diff --git a/package/kernel/xr-usb-serial/src/xr_usb_serial_hal.c b/package/kernel/xr-usb-serial/src/xr_usb_serial_hal.c deleted file mode 100644 index 26f5b8a36..000000000 --- a/package/kernel/xr-usb-serial/src/xr_usb_serial_hal.c +++ /dev/null @@ -1,821 +0,0 @@ -/* - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define XR_SET_MAP_XR2280X 5 -#define XR_GET_MAP_XR2280X 5 - -#define XR_SET_MAP_XR21B142X 0 -#define XR_GET_MAP_XR21B142X 0 - -#define XR_SET_MAP_XR21V141X 0 -#define XR_GET_MAP_XR21V141X 1 - -#define XR_SET_MAP_XR21B1411 0 -#define XR_GET_MAP_XR21B1411 1 - - -int xr_usb_serial_set_reg(struct xr_usb_serial *xr_usb_serial,int regnum, int value) -{ - int result; - int channel = 0; - //dev_info(&xr_usb_serial->control->dev, "%s Channel:%d 0x%02x = 0x%02x\n", __func__,channel,regnum, value); - if((xr_usb_serial->DeviceProduct&0xfff0) == 0x1400) - { - int XR2280xaddr = XR2280x_FUNC_MGR_OFFSET + regnum; - result = usb_control_msg(xr_usb_serial->dev, /* usb device */ - usb_sndctrlpipe(xr_usb_serial->dev, 0), /* endpoint pipe */ - XR_SET_MAP_XR2280X, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR, /* request_type */ - value, /* request value */ - XR2280xaddr, /* index */ - NULL, /* data */ - 0, /* size */ - 5000); /* timeout */ - } - else if((xr_usb_serial->DeviceProduct == 0x1410) || - (xr_usb_serial->DeviceProduct == 0x1412) || - (xr_usb_serial->DeviceProduct == 0x1414)) - { - if(xr_usb_serial->channel) - channel = xr_usb_serial->channel - 1; - result = usb_control_msg(xr_usb_serial->dev, /* usb device */ - usb_sndctrlpipe(xr_usb_serial->dev, 0), /* endpoint pipe */ - XR_SET_MAP_XR21V141X, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR, /* request_type */ - value, /* request value */ - regnum | (channel << 8), /* index */ - NULL, /* data */ - 0, /* size */ - 5000); /* timeout */ - } - else if(xr_usb_serial->DeviceProduct == 0x1411) - { - result = usb_control_msg(xr_usb_serial->dev, /* usb device */ - usb_sndctrlpipe(xr_usb_serial->dev, 0), /* endpoint pipe */ - XR_SET_MAP_XR21B1411, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR, /* request_type */ - value, /* request value */ - regnum , /* index */ - NULL, /* data */ - 0, /* size */ - 5000); /* timeout */ - } - else if((xr_usb_serial->DeviceProduct == 0x1420)|| - (xr_usb_serial->DeviceProduct == 0x1422)|| - (xr_usb_serial->DeviceProduct == 0x1424)) - { - channel = (xr_usb_serial->channel - 4)*2; - result = usb_control_msg(xr_usb_serial->dev, /* usb device */ - usb_sndctrlpipe(xr_usb_serial->dev, 0), /* endpoint pipe */ - XR_SET_MAP_XR21B142X, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR | 1, /* request_type */ - value, /* request value */ - regnum | (channel << 8), /* index */ - NULL, /* data */ - 0, /* size */ - 5000); /* timeout */ - } - else - { - result = -1; - } - if(result < 0) - dev_err(&xr_usb_serial->control->dev, "%s Error:%d\n", __func__,result); - return result; -} - -int xr_usb_serial_set_reg_ext(struct xr_usb_serial *xr_usb_serial,int channel,int regnum, int value) -{ - int result; - int XR2280xaddr = XR2280x_FUNC_MGR_OFFSET + regnum; - //dev_info(&xr_usb_serial->control->dev, "%s channel:%d 0x%02x = 0x%02x\n", __func__,channel,regnum, value); - if((xr_usb_serial->DeviceProduct&0xfff0) == 0x1400) - { - result = usb_control_msg(xr_usb_serial->dev, /* usb device */ - usb_sndctrlpipe(xr_usb_serial->dev, 0), /* endpoint pipe */ - XR_SET_MAP_XR2280X, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR, /* request_type */ - value, /* request value */ - XR2280xaddr, /* index */ - NULL, /* data */ - 0, /* size */ - 5000); /* timeout */ - } - else if((xr_usb_serial->DeviceProduct == 0x1410) || - (xr_usb_serial->DeviceProduct == 0x1412) || - (xr_usb_serial->DeviceProduct == 0x1414)) - { - result = usb_control_msg(xr_usb_serial->dev, /* usb device */ - usb_sndctrlpipe(xr_usb_serial->dev, 0), /* endpoint pipe */ - XR_SET_MAP_XR21V141X, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR, /* request_type */ - value, /* request value */ - regnum | (channel << 8), /* index */ - NULL, /* data */ - 0, /* size */ - 5000); /* timeout */ - } - else if(xr_usb_serial->DeviceProduct == 0x1411) - { - result = usb_control_msg(xr_usb_serial->dev, /* usb device */ - usb_sndctrlpipe(xr_usb_serial->dev, 0), /* endpoint pipe */ - XR_SET_MAP_XR21B1411, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR , /* request_type */ - value, /* request value */ - regnum , /* index */ - NULL, /* data */ - 0, /* size */ - 5000); /* timeout */ - } - else if((xr_usb_serial->DeviceProduct == 0x1420)|| - (xr_usb_serial->DeviceProduct == 0x1422)|| - (xr_usb_serial->DeviceProduct == 0x1424)) - { - result = usb_control_msg(xr_usb_serial->dev, /* usb device */ - usb_sndctrlpipe(xr_usb_serial->dev, 0), /* endpoint pipe */ - XR_SET_MAP_XR21B142X, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR | 1, /* request_type */ - value, /* request value */ - regnum | (channel << 8), /* index */ - NULL, /* data */ - 0, /* size */ - 5000); /* timeout */ - } - else - { - result = -1; - } - if(result < 0) - dev_err(&xr_usb_serial->control->dev, "%s Error:%d\n", __func__,result); - return result; -} - -int xr_usb_serial_get_reg(struct xr_usb_serial *xr_usb_serial,int regnum, short *value) -{ - int result; - int channel = 0; - short *dmadata; - - /* Use dynamic memory instead of statically allocated buffer - This is to avoid Error -11 (EAGAIN) for Kernel no longer accept static allocated buffer after 4.9 */ - dmadata = kmalloc(2, GFP_KERNEL); - - if (!dmadata) - { - dev_err(&xr_usb_serial->control->dev, "[func:%s,line:%d] error no memory allocated!! \n", __func__,__LINE__); - return -ENOMEM; - } - - if((xr_usb_serial->DeviceProduct&0xfff0) == 0x1400) - { - int XR2280xaddr = XR2280x_FUNC_MGR_OFFSET + regnum; - result = usb_control_msg(xr_usb_serial->dev, /* usb device */ - usb_rcvctrlpipe(xr_usb_serial->dev, 0), /* endpoint pipe */ - XR_GET_MAP_XR2280X, /* request */ - USB_DIR_IN | USB_TYPE_VENDOR , /* request_type */ - 0, /* request value */ - XR2280xaddr, /* index */ - dmadata, /* data */ - 2, /* size */ - 5000); /* timeout */ - memcpy(value, dmadata, 2); - } - else if((xr_usb_serial->DeviceProduct == 0x1410) || - (xr_usb_serial->DeviceProduct == 0x1412) || - (xr_usb_serial->DeviceProduct == 0x1414)) - { - if(xr_usb_serial->channel) - channel = xr_usb_serial->channel -1; - result = usb_control_msg(xr_usb_serial->dev, /* usb device */ - usb_rcvctrlpipe(xr_usb_serial->dev, 0), /* endpoint pipe */ - XR_GET_MAP_XR21V141X, /* request */ - USB_DIR_IN | USB_TYPE_VENDOR, /* request_type */ - 0, /* request value */ - regnum | (channel << 8), /* index */ - dmadata, /* data */ - 1, /* size */ - 5000); /* timeout */ - memcpy(value, dmadata, 1); - } - else if(xr_usb_serial->DeviceProduct == 0x1411) - { - result = usb_control_msg(xr_usb_serial->dev, /* usb device */ - usb_rcvctrlpipe(xr_usb_serial->dev, 0), /* endpoint pipe */ - XR_GET_MAP_XR21B1411, /* request */ - USB_DIR_IN | USB_TYPE_VENDOR, /* request_type */ - 0, /* request value */ - regnum, /* index */ - dmadata, /* data */ - 2, /* size */ - 5000); /* timeout */ - memcpy(value, dmadata, 2); - } - else if((xr_usb_serial->DeviceProduct == 0x1420)|| - (xr_usb_serial->DeviceProduct == 0x1422)|| - (xr_usb_serial->DeviceProduct == 0x1424)) - { - channel = (xr_usb_serial->channel -4)*2; - result = usb_control_msg(xr_usb_serial->dev, /* usb device */ - usb_rcvctrlpipe(xr_usb_serial->dev, 0), /* endpoint pipe */ - XR_GET_MAP_XR21B142X, /* request */ - USB_DIR_IN | USB_TYPE_VENDOR | 1, /* request_type */ - 0, /* request value */ - regnum | (channel << 8), /* index */ - dmadata, /* data */ - 2, /* size */ - 5000); /* timeout */ - memcpy(value, dmadata, 2); - } - else - { - result = -1; - } - - if(result < 0) - dev_err(&xr_usb_serial->control->dev, "%s channel:%d Reg 0x%x Error:%d\n", __func__,channel,regnum,result); - //else - //dev_info(&xr_usb_serial->control->dev, "%s channel:%d 0x%x = 0x%04x\n", __func__,channel,regnum, *value); - - kfree(dmadata); - return result; -} - -int xr_usb_serial_get_reg_ext(struct xr_usb_serial *xr_usb_serial,int channel,int regnum, short *value) -{ - int result; - int XR2280xaddr = XR2280x_FUNC_MGR_OFFSET + regnum; - short *dmadata; - - /* Use dynamic memory instead of statically allocated buffer - This is to avoid Error -11 (EAGAIN) for Kernel no longer accept static allocated buffer after 4.9 */ - dmadata = kmalloc(2, GFP_KERNEL); - - if (!dmadata) - { - dev_err(&xr_usb_serial->control->dev, "[func:%s,line:%d] error no memory allocated!! \n", __func__,__LINE__); - return -ENOMEM; - } - - if((xr_usb_serial->DeviceProduct&0xfff0) == 0x1400) - { - result = usb_control_msg(xr_usb_serial->dev, /* usb device */ - usb_rcvctrlpipe(xr_usb_serial->dev, 0), /* endpoint pipe */ - XR_GET_MAP_XR2280X, /* request */ - USB_DIR_IN | USB_TYPE_VENDOR , /* request_type */ - 0, /* request value */ - XR2280xaddr, /* index */ - dmadata, /* data */ - 2, /* size */ - 5000); /* timeout */ - memcpy(value, dmadata, 2); - } - else if((xr_usb_serial->DeviceProduct == 0x1410) || - (xr_usb_serial->DeviceProduct == 0x1412) || - (xr_usb_serial->DeviceProduct == 0x1414)) - { - result = usb_control_msg(xr_usb_serial->dev, /* usb device */ - usb_rcvctrlpipe(xr_usb_serial->dev, 0), /* endpoint pipe */ - XR_GET_MAP_XR21V141X, /* request */ - USB_DIR_IN | USB_TYPE_VENDOR, /* request_type */ - 0, /* request value */ - regnum | (channel << 8), /* index */ - dmadata, /* data */ - 1, /* size */ - 5000); /* timeout */ - memcpy(value, dmadata, 1); - } - else if(xr_usb_serial->DeviceProduct == 0x1411) - { - result = usb_control_msg(xr_usb_serial->dev, /* usb device */ - usb_rcvctrlpipe(xr_usb_serial->dev, 0), /* endpoint pipe */ - XR_GET_MAP_XR21B1411, /* request */ - USB_DIR_IN | USB_TYPE_VENDOR , /* request_type */ - 0, /* request value */ - regnum | (channel << 8), /* index */ - dmadata, /* data */ - 2, /* size */ - 5000); /* timeout */ - memcpy(value, dmadata, 2); - } - else if((xr_usb_serial->DeviceProduct == 0x1420)|| - (xr_usb_serial->DeviceProduct == 0x1422)|| - (xr_usb_serial->DeviceProduct == 0x1424)) - { - result = usb_control_msg(xr_usb_serial->dev, /* usb device */ - usb_rcvctrlpipe(xr_usb_serial->dev, 0), /* endpoint pipe */ - XR_GET_MAP_XR21B142X, /* request */ - USB_DIR_IN | USB_TYPE_VENDOR | 1, /* request_type */ - 0, /* request value */ - regnum | (channel << 8), /* index */ - dmadata, /* data */ - 2, /* size */ - 5000); /* timeout */ - memcpy(value, dmadata, 2); - } - else - { - result = -1; - } - - if(result < 0) - dev_err(&xr_usb_serial->control->dev, "%s Error:%d\n", __func__,result); - //else - //dev_info(&xr_usb_serial->control->dev, "%s channel:%d 0x%x = 0x%04x\n", __func__,channel,regnum, *value); - - kfree(dmadata); - return result; -} - -struct xr21v141x_baud_rate -{ - unsigned int tx; - unsigned int rx0; - unsigned int rx1; -}; - -static struct xr21v141x_baud_rate xr21v141x_baud_rates[] = { - { 0x000, 0x000, 0x000 }, - { 0x000, 0x000, 0x000 }, - { 0x100, 0x000, 0x100 }, - { 0x020, 0x400, 0x020 }, - { 0x010, 0x100, 0x010 }, - { 0x208, 0x040, 0x208 }, - { 0x104, 0x820, 0x108 }, - { 0x844, 0x210, 0x884 }, - { 0x444, 0x110, 0x444 }, - { 0x122, 0x888, 0x224 }, - { 0x912, 0x448, 0x924 }, - { 0x492, 0x248, 0x492 }, - { 0x252, 0x928, 0x292 }, - { 0X94A, 0X4A4, 0XA52 }, - { 0X52A, 0XAA4, 0X54A }, - { 0XAAA, 0x954, 0X4AA }, - { 0XAAA, 0x554, 0XAAA }, - { 0x555, 0XAD4, 0X5AA }, - { 0XB55, 0XAB4, 0X55A }, - { 0X6B5, 0X5AC, 0XB56 }, - { 0X5B5, 0XD6C, 0X6D6 }, - { 0XB6D, 0XB6A, 0XDB6 }, - { 0X76D, 0X6DA, 0XBB6 }, - { 0XEDD, 0XDDA, 0X76E }, - { 0XDDD, 0XBBA, 0XEEE }, - { 0X7BB, 0XF7A, 0XDDE }, - { 0XF7B, 0XEF6, 0X7DE }, - { 0XDF7, 0XBF6, 0XF7E }, - { 0X7F7, 0XFEE, 0XEFE }, - { 0XFDF, 0XFBE, 0X7FE }, - { 0XF7F, 0XEFE, 0XFFE }, - { 0XFFF, 0XFFE, 0XFFD }, -}; -#define UART_CLOCK_DIVISOR_0 0x004 -#define UART_CLOCK_DIVISOR_1 0x005 -#define UART_CLOCK_DIVISOR_2 0x006 -#define UART_TX_CLOCK_MASK_0 0x007 -#define UART_TX_CLOCK_MASK_1 0x008 -#define UART_RX_CLOCK_MASK_0 0x009 -#define UART_RX_CLOCK_MASK_1 0x00a - -static int xr21v141x_set_baud_rate(struct xr_usb_serial *xr_usb_serial, unsigned int rate) -{ - unsigned int divisor = 48000000 / rate; - unsigned int i = ((32 * 48000000) / rate) & 0x1f; - unsigned int tx_mask = xr21v141x_baud_rates[i].tx; - unsigned int rx_mask = (divisor & 1) ? xr21v141x_baud_rates[i].rx1 : xr21v141x_baud_rates[i].rx0; - - //dev_info(&xr_usb_serial->control->dev, "Setting baud rate to %d: i=%u div=%u tx=%03x rx=%03x\n", rate, i, divisor, tx_mask, rx_mask); - - xr_usb_serial_set_reg(xr_usb_serial,UART_CLOCK_DIVISOR_0, (divisor >> 0) & 0xff); - xr_usb_serial_set_reg(xr_usb_serial,UART_CLOCK_DIVISOR_1, (divisor >> 8) & 0xff); - xr_usb_serial_set_reg(xr_usb_serial,UART_CLOCK_DIVISOR_2, (divisor >> 16) & 0xff); - xr_usb_serial_set_reg(xr_usb_serial,UART_TX_CLOCK_MASK_0, (tx_mask >> 0) & 0xff); - xr_usb_serial_set_reg(xr_usb_serial,UART_TX_CLOCK_MASK_1, (tx_mask >> 8) & 0xff); - xr_usb_serial_set_reg(xr_usb_serial,UART_RX_CLOCK_MASK_0, (rx_mask >> 0) & 0xff); - xr_usb_serial_set_reg(xr_usb_serial,UART_RX_CLOCK_MASK_1, (rx_mask >> 8) & 0xff); - - return 0; -} -/* devices aren't required to support these requests. - * the cdc xr_usb_serial descriptor tells whether they do... - */ -int xr_usb_serial_set_control(struct xr_usb_serial *xr_usb_serial, unsigned int control) -{ - int ret = 0; - - if((xr_usb_serial->DeviceProduct == 0x1410) || - (xr_usb_serial->DeviceProduct == 0x1412) || - (xr_usb_serial->DeviceProduct == 0x1414)) - { - if (control & XR_USB_SERIAL_CTRL_DTR) - xr_usb_serial_set_reg(xr_usb_serial,xr_usb_serial->reg_map.uart_gpio_clr_addr, 0x08); - else - xr_usb_serial_set_reg(xr_usb_serial,xr_usb_serial->reg_map.uart_gpio_set_addr, 0x08); - - if (control & XR_USB_SERIAL_CTRL_RTS) - xr_usb_serial_set_reg(xr_usb_serial,xr_usb_serial->reg_map.uart_gpio_clr_addr, 0x20); - else - xr_usb_serial_set_reg(xr_usb_serial,xr_usb_serial->reg_map.uart_gpio_set_addr, 0x20); - } - else - { - ret = xr_usb_serial_ctrl_msg(xr_usb_serial, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0); - } - - return ret; -} - -int xr_usb_serial_set_line(struct xr_usb_serial *xr_usb_serial, struct usb_cdc_line_coding* line) -{ - int ret = 0; - unsigned int format_size; - unsigned int format_parity; - unsigned int format_stop; - if((xr_usb_serial->DeviceProduct == 0x1410) || - (xr_usb_serial->DeviceProduct == 0x1412) || - (xr_usb_serial->DeviceProduct == 0x1414)) - { - xr21v141x_set_baud_rate(xr_usb_serial,le32_to_cpu(line->dwDTERate)); - format_size = line->bDataBits; - format_parity = line->bParityType; - format_stop = line->bCharFormat; - xr_usb_serial_set_reg(xr_usb_serial, - xr_usb_serial->reg_map.uart_format_addr, - (format_size << 0) | (format_parity << 4) | (format_stop << 7) ); - } - else - { - ret = xr_usb_serial_ctrl_msg(xr_usb_serial, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line)); - } - return ret; -} - -int xr_usb_serial_set_flow_mode(struct xr_usb_serial *xr_usb_serial, struct tty_struct *tty, unsigned int cflag) -{ - unsigned int flow; - unsigned int gpio_mode; - - if (cflag & CRTSCTS) - { - //dev_dbg(&xr_usb_serial->control->dev, "xr_usb_serial_set_flow_mode:hardware\n"); - flow = UART_FLOW_MODE_HW; - gpio_mode = UART_GPIO_MODE_SEL_RTS_CTS; - } - else if (I_IXOFF(tty) || I_IXON(tty)) - { - unsigned char start_char = START_CHAR(tty); - unsigned char stop_char = STOP_CHAR(tty); - //dev_dbg(&xr_usb_serial->control->dev, "xr_usb_serial_set_flow_mode:software\n"); - flow = UART_FLOW_MODE_SW; - gpio_mode = UART_GPIO_MODE_SEL_GPIO; - - xr_usb_serial_set_reg(xr_usb_serial, xr_usb_serial->reg_map.uart_xon_char_addr, start_char); - xr_usb_serial_set_reg(xr_usb_serial, xr_usb_serial->reg_map.uart_xoff_char_addr, stop_char); - } - else - { - //dev_dbg(&xr_usb_serial->control->dev, "xr_usb_serial_set_flow_mode:none\n"); - flow = UART_FLOW_MODE_NONE; - gpio_mode = UART_GPIO_MODE_SEL_GPIO; - } - - if((xr_usb_serial->DeviceProduct == 0x1420)|| - (xr_usb_serial->DeviceProduct == 0x1422)|| - (xr_usb_serial->DeviceProduct == 0x1424)) - { - //Add support for the TXT and RXT function for 0x1420, 0x1422, 0x1424, by setting GPIO_MODE [9:8] = '11' - gpio_mode |= 0x300; - } - - xr_usb_serial_set_reg(xr_usb_serial, xr_usb_serial->reg_map.uart_flow_addr, flow); - xr_usb_serial_set_reg(xr_usb_serial, xr_usb_serial->reg_map.uart_gpio_mode_addr, gpio_mode); - return 0; -} - -int xr_usb_serial_send_break(struct xr_usb_serial *xr_usb_serial, int state) -{ - int ret = 0; - if((xr_usb_serial->DeviceProduct == 0x1410)|| - (xr_usb_serial->DeviceProduct == 0x1412)|| - (xr_usb_serial->DeviceProduct == 0x1414)) - { - if(state) - ret = xr_usb_serial_set_reg(xr_usb_serial,xr_usb_serial->reg_map.tx_break_addr,0xffff); - else - ret = xr_usb_serial_set_reg(xr_usb_serial,xr_usb_serial->reg_map.tx_break_addr,0); - } - else - { - ret = xr_usb_serial_ctrl_msg(xr_usb_serial, USB_CDC_REQ_SEND_BREAK, state, NULL, 0); - } - return ret; -} - -#define URM_REG_BLOCK 4 -#define URM_ENABLE_BASE 0x010 -#define URM_ENABLE_0_TX 0x001 -#define URM_ENABLE_0_RX 0x002 -#define URM_RESET_RX_FIFO_BASE 0x018 -#define URM_RESET_TX_FIFO_BASE 0x01C - -int xr_usb_serial_enable(struct xr_usb_serial *xr_usb_serial) -{ - int ret = 0; - int channel = xr_usb_serial->channel; - //dev_info(&xr_usb_serial->control->dev, "xr_usb_serial_enable channel=%d\n",channel); - if(channel) - channel--; - if((xr_usb_serial->DeviceProduct == 0x1410)|| - (xr_usb_serial->DeviceProduct == 0x1412)|| - (xr_usb_serial->DeviceProduct == 0x1414)) - { - ret = xr_usb_serial_set_reg_ext(xr_usb_serial,URM_REG_BLOCK,URM_ENABLE_BASE + channel,URM_ENABLE_0_TX); - ret = xr_usb_serial_set_reg(xr_usb_serial,xr_usb_serial->reg_map.uart_enable_addr,UART_ENABLE_TX | UART_ENABLE_RX); - ret = xr_usb_serial_set_reg_ext(xr_usb_serial,URM_REG_BLOCK,URM_ENABLE_BASE + channel,URM_ENABLE_0_TX | URM_ENABLE_0_RX); - } - else - { - ret = xr_usb_serial_set_reg(xr_usb_serial,xr_usb_serial->reg_map.uart_enable_addr,UART_ENABLE_TX | UART_ENABLE_RX); - } - - return ret; -} - -int xr_usb_serial_fifo_reset(struct xr_usb_serial *xr_usb_serial) -{ - int ret = 0; - int channel = xr_usb_serial->channel; - - if(channel) channel--; - if((xr_usb_serial->DeviceProduct == 0x1410)|| - (xr_usb_serial->DeviceProduct == 0x1412)|| - (xr_usb_serial->DeviceProduct == 0x1414)) - { - ret = xr_usb_serial_set_reg_ext(xr_usb_serial,URM_REG_BLOCK,URM_RESET_RX_FIFO_BASE + channel,0xff); - ret |= xr_usb_serial_set_reg_ext(xr_usb_serial,URM_REG_BLOCK,URM_RESET_TX_FIFO_BASE + channel,0xff); - } - return ret; -} - -int xr_usb_serial_disable(struct xr_usb_serial *xr_usb_serial) -{ - int ret = 0; - int channel = xr_usb_serial->channel; - //dev_info(&xr_usb_serial->control->dev, "xr_usb_serial_disable channel=%d\n",channel); - if(channel) channel--; - ret = xr_usb_serial_set_reg(xr_usb_serial,xr_usb_serial->reg_map.uart_enable_addr,0); - if((xr_usb_serial->DeviceProduct == 0x1410)|| - (xr_usb_serial->DeviceProduct == 0x1412)|| - (xr_usb_serial->DeviceProduct == 0x1414)) - { - ret = xr_usb_serial_set_reg_ext(xr_usb_serial,URM_REG_BLOCK,URM_ENABLE_BASE + channel,URM_ENABLE_0_TX); - } - - return ret; -} - -int xr_usb_serial_set_loopback(struct xr_usb_serial *xr_usb_serial, int channel) -{ - int ret = 0; - xr_usb_serial_disable(xr_usb_serial); - - if((xr_usb_serial->DeviceProduct == 0x1410) || - (xr_usb_serial->DeviceProduct == 0x1412) || - (xr_usb_serial->DeviceProduct == 0x1414)) - { - switch (channel) - { - case 0: - ret = xr_usb_serial_set_reg_ext(xr_usb_serial,channel, - xr_usb_serial->reg_map.uart_loopback_addr,0x40); - break; - case 1: - ret = xr_usb_serial_set_reg_ext(xr_usb_serial,channel, - xr_usb_serial->reg_map.uart_loopback_addr,0x41); - break; - case 2: - ret = xr_usb_serial_set_reg_ext(xr_usb_serial,channel, - xr_usb_serial->reg_map.uart_loopback_addr,0x42); - break; - case 3: - ret = xr_usb_serial_set_reg_ext(xr_usb_serial,channel, - xr_usb_serial->reg_map.uart_loopback_addr,0x43); - break; - default: - break; - } - } - else if((xr_usb_serial->DeviceProduct == 0x1420)|| - (xr_usb_serial->DeviceProduct == 0x1422)|| - (xr_usb_serial->DeviceProduct == 0x1424)) - { - ret = xr_usb_serial_set_reg_ext(xr_usb_serial,channel, - xr_usb_serial->reg_map.uart_loopback_addr,0x07); - } - xr_usb_serial_enable(xr_usb_serial); - return ret; -} - -#define XR21V1414_WIDE_MODE_OFFSET 3 -#define XR21B142X_WIDE_MODE_TX_OFFSET 0x42 -#define XR21B142X_WIDE_MODE_RX_OFFSET 0x45 -/* XR2280x_FUNC_MGR_OFFSET will be included in xr_usb_serial_set_reg */ -#define XR21B140X_WIDE_MODE_TX_OFFSET 0x22 -#define XR21B140X_WIDE_MODE_RX_OFFSET 0x25 -int xr_usb_serial_set_wide_mode(struct xr_usb_serial *xr_usb_serial, int preciseflags) -{ - int ret = 0; - int channel = xr_usb_serial->channel; - xr_usb_serial_disable(xr_usb_serial); - if((xr_usb_serial->DeviceProduct&0xfff0) == 0x1400) - { - xr_usb_serial_set_reg(xr_usb_serial, XR21B140X_WIDE_MODE_TX_OFFSET, preciseflags); - xr_usb_serial_set_reg(xr_usb_serial, XR21B140X_WIDE_MODE_RX_OFFSET, preciseflags); - } - else if((xr_usb_serial->DeviceProduct == 0x1410)|| - (xr_usb_serial->DeviceProduct == 0x1412)|| - (xr_usb_serial->DeviceProduct == 0x1414)) - { - if(channel) channel--; - xr_usb_serial_set_reg_ext(xr_usb_serial, 0x66, channel*8 + XR21V1414_WIDE_MODE_OFFSET, preciseflags); - } - else if(xr_usb_serial->DeviceProduct == 0x1411) - { - xr_usb_serial_set_reg(xr_usb_serial,0xd02, preciseflags); - } - else if((xr_usb_serial->DeviceProduct == 0x1420)|| - (xr_usb_serial->DeviceProduct == 0x1422)|| - (xr_usb_serial->DeviceProduct == 0x1424)) - { - xr_usb_serial_set_reg(xr_usb_serial, XR21B142X_WIDE_MODE_TX_OFFSET, preciseflags); - xr_usb_serial_set_reg(xr_usb_serial, XR21B142X_WIDE_MODE_RX_OFFSET, preciseflags); - } - xr_usb_serial_enable(xr_usb_serial); - return ret; -} - -static int xr_usb_serial_tiocmget(struct xr_usb_serial *xr_usb_serial) -{ - short data; - int result; - result = xr_usb_serial_get_reg(xr_usb_serial,xr_usb_serial->reg_map.uart_gpio_status_addr, &data); - //dev_info(&xr_usb_serial->control->dev, "xr_usb_serial_tiocmget uart_gpio_status_addr:0x%04x\n",data); - if (result) - return ((data & 0x8) ? 0: TIOCM_DTR) | ((data & 0x20) ? 0:TIOCM_RTS ) | ((data & 0x4) ? 0:TIOCM_DSR) | ((data & 0x1) ? 0 : TIOCM_RI) | ((data & 0x2) ? 0:TIOCM_CD) | ((data & 0x10) ? 0 : TIOCM_CTS); - else - return -EFAULT; -} - -static int xr_usb_serial_tiocmset(struct xr_usb_serial *xr_usb_serial, - unsigned int set, unsigned int clear) -{ - unsigned int newctrl = 0; - newctrl = xr_usb_serial->ctrlout; - - set = (set & TIOCM_DTR ? XR_USB_SERIAL_CTRL_DTR : 0) | (set & TIOCM_RTS ? XR_USB_SERIAL_CTRL_RTS : 0); - - clear = (clear & TIOCM_DTR ? XR_USB_SERIAL_CTRL_DTR : 0) | (clear & TIOCM_RTS ? XR_USB_SERIAL_CTRL_RTS : 0); - - newctrl = (newctrl & ~clear) | set; - - if (xr_usb_serial->ctrlout == newctrl) - return 0; - - xr_usb_serial->ctrlout = newctrl; - - if (newctrl & XR_USB_SERIAL_CTRL_DTR) - xr_usb_serial_set_reg(xr_usb_serial, xr_usb_serial->reg_map.uart_gpio_clr_addr, 0x08); - else - xr_usb_serial_set_reg(xr_usb_serial, xr_usb_serial->reg_map.uart_gpio_set_addr, 0x08); - - if (newctrl & XR_USB_SERIAL_CTRL_RTS) - xr_usb_serial_set_reg(xr_usb_serial, xr_usb_serial->reg_map.uart_gpio_clr_addr, 0x20); - else - xr_usb_serial_set_reg(xr_usb_serial, xr_usb_serial->reg_map.uart_gpio_set_addr, 0x20); - - return 0; -} - -static struct reg_addr_map xr21b140x_reg_map; -static struct reg_addr_map xr21b1411_reg_map; -static struct reg_addr_map xr21v141x_reg_map; -static struct reg_addr_map xr21b142x_reg_map; - -static void init_xr21b140x_reg_map(void) -{ - xr21b140x_reg_map.uart_enable_addr = 0x00; - xr21b140x_reg_map.uart_format_addr = 0x05; - xr21b140x_reg_map.uart_flow_addr = 0x06; - xr21b140x_reg_map.uart_loopback_addr = 0x16; - xr21b140x_reg_map.uart_xon_char_addr = 0x07; - xr21b140x_reg_map.uart_xoff_char_addr = 0x08; - xr21b140x_reg_map.uart_gpio_mode_addr = 0x0c; - xr21b140x_reg_map.uart_gpio_dir_addr = 0x0d; - xr21b140x_reg_map.uart_gpio_set_addr = 0x0e; - xr21b140x_reg_map.uart_gpio_clr_addr = 0x0f; - xr21b140x_reg_map.uart_gpio_status_addr = 0x10; - xr21b140x_reg_map.tx_break_addr = 0x0a; - xr21b140x_reg_map.uart_custom_driver = 0x41; -} - -static void init_xr21b1411_reg_map(void) -{ - xr21b1411_reg_map.uart_enable_addr = 0xc00; - xr21b1411_reg_map.uart_flow_addr = 0xc06; - xr21b1411_reg_map.uart_loopback_addr = 0xc16; - xr21b1411_reg_map.uart_xon_char_addr = 0xc07; - xr21b1411_reg_map.uart_xoff_char_addr = 0xc08; - xr21b1411_reg_map.uart_gpio_mode_addr = 0xc0c; - xr21b1411_reg_map.uart_gpio_dir_addr = 0xc0d; - xr21b1411_reg_map.uart_gpio_set_addr = 0xc0e; - xr21b1411_reg_map.uart_gpio_clr_addr = 0xc0f; - xr21b1411_reg_map.uart_gpio_status_addr = 0xc10; - xr21b1411_reg_map.tx_break_addr = 0xc0a; - xr21b1411_reg_map.uart_custom_driver = 0x20d; -} - -static void init_xr21v141x_reg_map(void) -{ - xr21v141x_reg_map.uart_enable_addr = 0x03; - xr21v141x_reg_map.uart_format_addr = 0x0b; - xr21v141x_reg_map.uart_flow_addr = 0x0c; - xr21v141x_reg_map.uart_loopback_addr = 0x12; - xr21v141x_reg_map.uart_xon_char_addr = 0x10; - xr21v141x_reg_map.uart_xoff_char_addr = 0x11; - xr21v141x_reg_map.uart_gpio_mode_addr = 0x1a; - xr21v141x_reg_map.uart_gpio_dir_addr = 0x1b; - xr21v141x_reg_map.uart_gpio_set_addr = 0x1d; - xr21v141x_reg_map.uart_gpio_clr_addr = 0x1e; - xr21v141x_reg_map.uart_gpio_status_addr = 0x1f; - xr21v141x_reg_map.tx_break_addr = 0x14; -} - -static void init_xr21b142x_reg_map(void) -{ - xr21b142x_reg_map.uart_enable_addr = 0x00; - xr21b142x_reg_map.uart_flow_addr = 0x06; - xr21b142x_reg_map.uart_loopback_addr = 0x16; - xr21b142x_reg_map.uart_xon_char_addr = 0x07; - xr21b142x_reg_map.uart_xoff_char_addr = 0x08; - xr21b142x_reg_map.uart_gpio_mode_addr = 0x0c; - xr21b142x_reg_map.uart_gpio_dir_addr = 0x0d; - xr21b142x_reg_map.uart_gpio_set_addr = 0x0e; - xr21b142x_reg_map.uart_gpio_clr_addr = 0x0f; - xr21b142x_reg_map.uart_gpio_status_addr = 0x10; - xr21b142x_reg_map.tx_break_addr = 0x0a; - xr21b142x_reg_map.uart_custom_driver = 0x60; - xr21b142x_reg_map.uart_low_latency = 0x46; -} - -int xr_usb_serial_pre_setup(struct xr_usb_serial *xr_usb_serial) -{ - int ret = 0; - - init_xr21b140x_reg_map(); - init_xr21b1411_reg_map(); - init_xr21v141x_reg_map(); - init_xr21b142x_reg_map(); - if((xr_usb_serial->DeviceProduct&0xfff0) == 0x1400) - { - memcpy(&(xr_usb_serial->reg_map),&xr21b140x_reg_map,sizeof(struct reg_addr_map)); - } - else if(xr_usb_serial->DeviceProduct == 0x1411) - { - memcpy(&(xr_usb_serial->reg_map),&xr21b1411_reg_map,sizeof(struct reg_addr_map)); - } - else if((xr_usb_serial->DeviceProduct == 0x1410)|| - (xr_usb_serial->DeviceProduct == 0x1412)|| - (xr_usb_serial->DeviceProduct == 0x1414)) - { - memcpy(&(xr_usb_serial->reg_map),&xr21v141x_reg_map,sizeof(struct reg_addr_map)); - } - else if((xr_usb_serial->DeviceProduct == 0x1420)|| - (xr_usb_serial->DeviceProduct == 0x1422)|| - (xr_usb_serial->DeviceProduct == 0x1424)) - { - memcpy(&(xr_usb_serial->reg_map),&xr21b142x_reg_map,sizeof(struct reg_addr_map)); - } - else - { - ret = -1; - } - if(xr_usb_serial->reg_map.uart_custom_driver) - xr_usb_serial_set_reg(xr_usb_serial, xr_usb_serial->reg_map.uart_custom_driver, 1); - - xr_usb_serial_set_reg(xr_usb_serial, xr_usb_serial->reg_map.uart_gpio_mode_addr, 0); - xr_usb_serial_set_reg(xr_usb_serial, xr_usb_serial->reg_map.uart_gpio_dir_addr, 0x28); - xr_usb_serial_set_reg(xr_usb_serial, xr_usb_serial->reg_map.uart_gpio_set_addr, UART_GPIO_SET_DTR | UART_GPIO_SET_RTS); - - return ret; -} diff --git a/package/kernel/xr-usb-serial/src/xr_usb_serial_ioctl.h b/package/kernel/xr-usb-serial/src/xr_usb_serial_ioctl.h deleted file mode 100644 index eb7cd4b7e..000000000 --- a/package/kernel/xr-usb-serial/src/xr_usb_serial_ioctl.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include - -#define XR_USB_SERIAL_IOC_MAGIC 'v' - -#define XR_USB_SERIAL_GET_REG _IOWR(XR_USB_SERIAL_IOC_MAGIC, 1, int) -#define XR_USB_SERIAL_SET_REG _IOWR(XR_USB_SERIAL_IOC_MAGIC, 2, int) -#define XR_USB_SERIAL_SET_ADDRESS_MATCH _IO(XR_USB_SERIAL_IOC_MAGIC, 3) -#define XR_USB_SERIAL_SET_PRECISE_FLAGS _IO(XR_USB_SERIAL_IOC_MAGIC, 4) -#define XR_USB_SERIAL_TEST_MODE _IO(XR_USB_SERIAL_IOC_MAGIC, 5) -#define XR_USB_SERIAL_LOOPBACK _IO(XR_USB_SERIAL_IOC_MAGIC, 6) -#define XR_USB_SERIAL_SET_GPIO_MODE_REG _IO(XR_USB_SERIAL_IOC_MAGIC, 9) -#define XR_USB_SERIAL_GET_GPIO_MODE_REG _IO(XR_USB_SERIAL_IOC_MAGIC, 10) -#define XRIOC_SET_ANY_BAUD_RATE _IO(XR_USB_SERIAL_IOC_MAGIC, 11) -#define XRIOC_SET_PRECISE_FLAGS _IO(XR_USB_SERIAL_IOC_MAGIC, 12) - -#define VZ_ADDRESS_UNICAST_S 0 -#define VZ_ADDRESS_BROADCAST_S 8 -#define VZ_ADDRESS_MATCH(U, B) (0x8000000 | ((B) << VZ_ADDRESS_BROADCAST_S) | ((U) << VZ_ADDRESS_UNICAST_S)) -#define VZ_ADDRESS_MATCH_DISABLE 0 diff --git a/package/wwan/app/luci-app-gobinetmodem/Makefile b/package/wwan/app/luci-app-gobinetmodem/Makefile deleted file mode 100644 index 634cff7d0..000000000 --- a/package/wwan/app/luci-app-gobinetmodem/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright (C) 2015 OpenWrt.org -# -# This is free software, licensed under the GNU General Public License v2. -# See /LICENSE for more information. -# - -include $(TOPDIR)/rules.mk - -LUCI_TITLE:=Modem Server -LUCI_DEPENDS:=+luci-compat +quectel-CM-5G +kmod-gobinet \ - +kmod-usb-net-cdc-ether +kmod-usb-net-qmi-wwan \ - +kmod-usb-net-rndis +kmod-usb-serial-option - -include $(TOPDIR)/feeds/luci/luci.mk - -# call BuildPackage - OpenWrt buildroot signature diff --git a/package/wwan/app/luci-app-gobinetmodem/luasrc/controller/gobinetmodem.lua b/package/wwan/app/luci-app-gobinetmodem/luasrc/controller/gobinetmodem.lua deleted file mode 100644 index 5b6de85db..000000000 --- a/package/wwan/app/luci-app-gobinetmodem/luasrc/controller/gobinetmodem.lua +++ /dev/null @@ -1,9 +0,0 @@ -module("luci.controller.gobinetmodem", package.seeall) - -function index() - if not nixio.fs.access("/etc/config/gobinetmodem") then - return - end - - entry({"admin", "network", "gobinetmodem"}, cbi("gobinetmodem"), _("Gobinet Modem Server"), 80).dependent = false -end diff --git a/package/wwan/app/luci-app-gobinetmodem/luasrc/model/cbi/gobinetmodem.lua b/package/wwan/app/luci-app-gobinetmodem/luasrc/model/cbi/gobinetmodem.lua deleted file mode 100644 index 784130f16..000000000 --- a/package/wwan/app/luci-app-gobinetmodem/luasrc/model/cbi/gobinetmodem.lua +++ /dev/null @@ -1,39 +0,0 @@ --- Copyright 2016 David Thornley --- Licensed to the public under the Apache License 2.0. - -mp = Map("gobinetmodem") -mp.title = translate("gobinet Modem Server") -mp.description = translate("Modem Server For OpenWrt") - -s = mp:section(TypedSection, "service", "Base Setting") -s.anonymous = true - -enabled = s:option(Flag, "enabled", translate("Enable")) -enabled.default = 0 -enabled.rmempty = false - -apn = s:option(Value, "apn", translate("APN")) -apn.rmempty = true - -pincode = s:option(Value, "pincode", translate("PIN")) -pincode.rmempty = true - -username = s:option(Value, "username", translate("PAP/CHAP username")) -username.rmempty = true - -password = s:option(Value, "password", translate("PAP/CHAP password")) -password.rmempty = true - -auth = s:option(Value, "auth", translate("Authentication Type")) -auth.rmempty = true -auth:value("", translate("-- Please choose --")) -auth:value("both", "PAP/CHAP (both)") -auth:value("pap", "PAP") -auth:value("chap", "CHAP") -auth:value("none", "NONE") - -tool = s:option(Value, "tool", translate("Tools")) -tool:value("quectel-CM", "quectel-CM") -tool.rmempty = true - -return mp diff --git a/package/wwan/app/luci-app-gobinetmodem/po/zh-cn/usbmodem.po b/package/wwan/app/luci-app-gobinetmodem/po/zh-cn/usbmodem.po deleted file mode 100644 index df1f6df8e..000000000 --- a/package/wwan/app/luci-app-gobinetmodem/po/zh-cn/usbmodem.po +++ /dev/null @@ -1,24 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: \n" -"POT-Creation-Date: \n" -"PO-Revision-Date: \n" -"Last-Translator: dingpengyu \n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Language: zh_CN\n" -"X-Generator: Poedit 2.3.1\n" - -msgid "Base Setting" -msgstr "基本设置" - -msgid "gobinet Modem Server" -msgstr "gobinet移å¨ç½‘络拨å·æœå¡" - -msgid "Modem Server For OpenWrt" -msgstr "OpenWrt移å¨ç½‘络拨å·æœå¡" - -msgid "Tools" -msgstr "拨å·å·¥å…·" diff --git a/package/wwan/app/luci-app-gobinetmodem/root/etc/config/gobinetmodem b/package/wwan/app/luci-app-gobinetmodem/root/etc/config/gobinetmodem deleted file mode 100644 index 05fad9b41..000000000 --- a/package/wwan/app/luci-app-gobinetmodem/root/etc/config/gobinetmodem +++ /dev/null @@ -1,4 +0,0 @@ -config service - option tool 'quectel-CM' - option enabled '0' - diff --git a/package/wwan/app/luci-app-gobinetmodem/root/etc/init.d/gobinetmodem b/package/wwan/app/luci-app-gobinetmodem/root/etc/init.d/gobinetmodem deleted file mode 100755 index 953ee96c6..000000000 --- a/package/wwan/app/luci-app-gobinetmodem/root/etc/init.d/gobinetmodem +++ /dev/null @@ -1,80 +0,0 @@ -#!/bin/sh /etc/rc.common -# Copyright (C) 2006-2014 OpenWrt.org - -START=99 -STOP=16 -USE_PROCD=1 -#使用procdå¯å¨ - -run_4g() -{ - local enabled - config_get_bool enabled $1 enabled - - echo "run 4G" >> /tmp/log4g - - if [ "$enabled" = "1" ]; then - local user - local password - local apn - local auth - local pincode - local device - local tool - - # echo "enable 4G" >> /tmp/log4g - config_get user $1 user - config_get password $1 password - config_get apn $1 apn - config_get auth $1 auth - config_get pincode $1 pincode - config_get device $1 device - config_get tool $1 tool - config_get tty $1 tty - config_get atcmd $1 atcmd - - devname="$(basename "$device")" - devpath="$(readlink -f /sys/class/usbmisc/$devname/device/)" - ifname="$( ls "$devpath"/net )" - - if [ "$tool" = "at" ];then - at_tool "$atcmd" -d $tty - else - procd_open_instance - #创建一个å®ä¾‹ï¼Œ 在procd看æ¥ä¸€ä¸ªåº”用程åºå¯ä»¥å¤ä¸ªå®\E4\BE? - #ubus call service list å¯ä»¥æŸ¥çœ‹å®ä¾‹ - procd_set_param command $tool -i $ifname -s $apn - if [ "$password" != "" ];then - procd_append_param command $user $password $auth - fi - if [ "$pincode" != "" ]; then - procd_append_param command -p $pincode - fi - # procd_append_param command -f /tmp/4g.log - procd_set_param respawn - echo "quectel-CM has started." - procd_close_instance - #关闭å®ä¾‹ - fi - - fi -} - - -service_triggers() -{ - procd_add_reload_trigger "gobinetmodem" -} - -start_service() { - config_load gobinetmodem - config_foreach run_4g service -} - -stop_service() -{ - echo "4G stop" >> /tmp/log4g - killall quectel-CM - echo "quectel-CM has stoped." -} - diff --git a/package/wwan/app/luci-app-gobinetmodem/root/etc/uci-defaults/luci-gobinetmodem b/package/wwan/app/luci-app-gobinetmodem/root/etc/uci-defaults/luci-gobinetmodem deleted file mode 100755 index b4b7674e3..000000000 --- a/package/wwan/app/luci-app-gobinetmodem/root/etc/uci-defaults/luci-gobinetmodem +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh - -uci -q batch <<-EOF >/dev/null - delete ucitrack.@gobinetmodem[-1] - add ucitrack gobinetmodem - set ucitrack.@gobinetmodem[-1].init=gobinetmodem - commit ucitrack -EOF - -rm -f /tmp/luci-indexcache -exit 0 diff --git a/package/wwan/driver/quectel_Gobinet/Makefile b/package/wwan/driver/quectel_Gobinet/Makefile deleted file mode 100755 index abdba443e..000000000 --- a/package/wwan/driver/quectel_Gobinet/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -# -# Copyright (C) 2015 OpenWrt.org -# -# This is free software, licensed under the GNU General Public License v2. -# See /LICENSE for more information. -# - -include $(TOPDIR)/rules.mk - -PKG_NAME:=gobinet -PKG_VERSION:=1.6.3 -PKG_RELEASE:=1 - -include $(INCLUDE_DIR)/kernel.mk -include $(INCLUDE_DIR)/package.mk - -define KernelPackage/gobinet - SUBMENU:=WWAN Support - TITLE:=Quectel Linux USB Gobinet Driver - DEPENDS:=+kmod-usb-net - FILES:=$(PKG_BUILD_DIR)/GobiNet.ko - AUTOLOAD:=$(call AutoLoad,81,GobiNet) -endef - -define KernelPackage/gobinet/description - Quectel Linux USB gobinet Driver -endef - -MAKE_OPTS:= \ - ARCH="$(LINUX_KARCH)" \ - CROSS_COMPILE="$(TARGET_CROSS)" \ - CXXFLAGS="$(TARGET_CXXFLAGS)" \ - M="$(PKG_BUILD_DIR)" \ - $(EXTRA_KCONFIG) - -define Build/Prepare - mkdir -p $(PKG_BUILD_DIR) - $(CP) ./src/* $(PKG_BUILD_DIR)/ -endef - -define Build/Compile - $(MAKE) -C "$(LINUX_DIR)" \ - $(MAKE_OPTS) \ - modules -endef - -$(eval $(call KernelPackage,gobinet)) diff --git a/package/wwan/driver/quectel_Gobinet/src/GobiUSBNet.c b/package/wwan/driver/quectel_Gobinet/src/GobiUSBNet.c deleted file mode 100644 index 689c1733a..000000000 --- a/package/wwan/driver/quectel_Gobinet/src/GobiUSBNet.c +++ /dev/null @@ -1,3202 +0,0 @@ -/*=========================================================================== -FILE: - GobiUSBNet.c - -DESCRIPTION: - Qualcomm USB Network device for Gobi 3000 - -FUNCTIONS: - GobiNetSuspend - GobiNetResume - GobiNetDriverBind - GobiNetDriverUnbind - GobiUSBNetURBCallback - GobiUSBNetTXTimeout - GobiUSBNetAutoPMThread - GobiUSBNetStartXmit - GobiUSBNetOpen - GobiUSBNetStop - GobiUSBNetProbe - GobiUSBNetModInit - GobiUSBNetModExit - -Copyright (c) 2011, Code Aurora Forum. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Code Aurora Forum nor - the names of its contributors may be used to endorse or promote - products derived from this software without specific prior written - permission. - - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -===========================================================================*/ - -//--------------------------------------------------------------------------- -// Include Files -//--------------------------------------------------------------------------- - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#if LINUX_VERSION_CODE > KERNEL_VERSION(3,16,0) //8b094cd03b4a3793220d8d8d86a173bfea8c285b -#include -#else -#define timespec64 timespec -#define ktime_get_ts64 ktime_get_ts -#define timespec64_sub timespec_sub -#endif - -#include "Structs.h" -#include "QMIDevice.h" -#include "QMI.h" - -#ifndef ETH_P_MAP -#define ETH_P_MAP 0xDA1A -#endif - -#if (ETH_P_MAP == 0x00F9) -#undef ETH_P_MAP -#define ETH_P_MAP 0xDA1A -#endif - -//----------------------------------------------------------------------------- -// Definitions -//----------------------------------------------------------------------------- - -// Version Information -//add new module or new feature, increase major version. fix bug, increase minor version -#define VERSION_NUMBER "V1.6.3" -#define DRIVER_VERSION "Quectel_Linux&Android_GobiNet_Driver_"VERSION_NUMBER -#define DRIVER_AUTHOR "Qualcomm Innovation Center" -#define DRIVER_DESC "GobiNet" -static const char driver_name[] = "GobiNet"; - -// Debug flag -int quec_debug = 0; - -// Allow user interrupts -//int interruptible = 1; - -// Number of IP packets which may be queued up for transmit -static int txQueueLength = 100; - -// Class should be created during module init, so needs to be global -static struct class * gpClass; - -static const unsigned char ec20_mac[ETH_ALEN] = {0x02, 0x50, 0xf3, 0x00, 0x00, 0x00}; -static const unsigned char default_modem_addr[ETH_ALEN] = {0x02, 0x50, 0xf3, 0x00, 0x00, 0x00}; -static const unsigned char node_id[ETH_ALEN] = {0x02, 0x50, 0xf4, 0x00, 0x00, 0x00}; -//static const u8 broadcast_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - -//setup data call by "AT$QCRMCALL=1,1" -static uint __read_mostly qcrmcall_mode = 0; -module_param( qcrmcall_mode, uint, S_IRUGO | S_IWUSR ); - -static struct sk_buff * ether_to_ip_fixup(struct net_device *dev, struct sk_buff *skb) { - const struct ethhdr *ehdr; - - skb_reset_mac_header(skb); - ehdr = eth_hdr(skb); - - if (ehdr->h_proto == htons(ETH_P_IP)) { - if (unlikely(skb->len <= (sizeof(struct ethhdr) + sizeof(struct iphdr)))) { - goto drop_skb; - } - } - else if (ehdr->h_proto == htons(ETH_P_IPV6)) { - if (unlikely(skb->len <= (sizeof(struct ethhdr) + sizeof(struct ipv6hdr)))) { - goto drop_skb; - } - } - else { - DBG("%s skb h_proto is %04x\n", dev->name, ntohs(ehdr->h_proto)); - goto drop_skb; - } - - if (unlikely(skb_pull(skb, ETH_HLEN))) - return skb; - -drop_skb: - return NULL; -} - -//#define QUECTEL_REMOVE_TX_ZLP -#define USB_CDC_SET_REMOVE_TX_ZLP_COMMAND 0x5D - -//#define QUECTEL_WWAN_MULTI_PACKAGES - -#ifdef QUECTEL_WWAN_MULTI_PACKAGES -static uint __read_mostly rx_packets = 10; -module_param( rx_packets, uint, S_IRUGO | S_IWUSR ); - -#define USB_CDC_SET_MULTI_PACKAGE_COMMAND (0x5C) -#define QUEC_NET_MSG_SPEC (0x80) -#define QUEC_NET_MSG_ID_IP_DATA (0x00) - -struct multi_package_config { - __le32 enable; - __le32 package_max_len; - __le32 package_max_count_in_queue; - __le32 timeout; -} __packed; - -struct quec_net_package_header { - unsigned char msg_spec; - unsigned char msg_id; - unsigned short payload_len; - unsigned char reserve[16]; -} __packed; -#endif - -#ifdef QUECTEL_WWAN_QMAP -/* - Quectel_WCDMA<E_Linux_USB_Driver_User_Guide_V1.9.pdf - 5.6. Test QMAP on GobiNet or QMI WWAN - 0 - no QMAP - 1 - QMAP (Aggregation protocol) - X - QMAP (Multiplexing and Aggregation protocol) -*/ -static uint __read_mostly qmap_mode = 0; -module_param( qmap_mode, uint, S_IRUGO | S_IWUSR ); - -struct qmap_hdr { - u8 cd_rsvd_pad; - u8 mux_id; - u16 pkt_len; -} __packed; - -enum rmnet_map_v5_header_type { - RMNET_MAP_HEADER_TYPE_UNKNOWN, - RMNET_MAP_HEADER_TYPE_COALESCING = 0x1, - RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD = 0x2, - RMNET_MAP_HEADER_TYPE_ENUM_LENGTH -}; - -/* Main QMAP header */ -struct rmnet_map_header { -#if defined(__LITTLE_ENDIAN_BITFIELD) - u8 pad_len:6; - u8 next_hdr:1; - u8 cd_bit:1; -#elif defined (__BIG_ENDIAN_BITFIELD) - u8 cd_bit:1; - u8 next_hdr:1; - u8 pad_len:6; -#else -#error "Please fix " -#endif - u8 mux_id; - __be16 pkt_len; -} __aligned(1); - -/* QMAP v5 headers */ -struct rmnet_map_v5_csum_header { -#if defined(__LITTLE_ENDIAN_BITFIELD) - u8 next_hdr:1; - u8 header_type:7; - u8 hw_reserved:7; - u8 csum_valid_required:1; -#elif defined (__BIG_ENDIAN_BITFIELD) - u8 header_type:7; - u8 next_hdr:1; - u8 csum_valid_required:1; - u8 hw_reserved:7; -#else -#error "Please fix " -#endif - __be16 reserved; -} __aligned(1); - -struct qmap_priv { - struct net_device *real_dev; - struct net_device *self_dev; - uint qmap_version; - uint offset_id; - uint mux_id; - uint link_state; - -#if defined(QUECTEL_UL_DATA_AGG) - /* QMIWDS_ADMIN_SET_DATA_FORMAT_RESP TLV_0x17 and TLV_0x18 */ - uint ul_data_aggregation_max_datagrams; //UplinkDataAggregationMaxDatagramsTlv - uint ul_data_aggregation_max_size; //UplinkDataAggregationMaxSizeTlv - uint dl_minimum_padding; //0x1A - - spinlock_t agg_lock; - struct sk_buff *agg_skb; - unsigned agg_count; - struct timespec64 agg_time; - struct hrtimer agg_hrtimer; - struct work_struct agg_wq; -#endif - -#ifdef QUECTEL_BRIDGE_MODE - int m_bridge_mode; - uint m_bridge_ipv4; - unsigned char mHostMAC[6]; -#endif -}; - -#ifdef QUECTEL_BRIDGE_MODE -static int is_qmap_netdev(const struct net_device *netdev); -#endif - -#endif - -#ifdef QUECTEL_BRIDGE_MODE -static int __read_mostly bridge_mode = 0/*|BIT(1)*/; -module_param( bridge_mode, int, S_IRUGO | S_IWUSR ); - -static int bridge_arp_reply(struct net_device *net, struct sk_buff *skb, uint bridge_ipv4) { - struct arphdr *parp; - u8 *arpptr, *sha; - u8 sip[4], tip[4], ipv4[4]; - struct sk_buff *reply = NULL; - - ipv4[0] = (bridge_ipv4 >> 24) & 0xFF; - ipv4[1] = (bridge_ipv4 >> 16) & 0xFF; - ipv4[2] = (bridge_ipv4 >> 8) & 0xFF; - ipv4[3] = (bridge_ipv4 >> 0) & 0xFF; - - parp = arp_hdr(skb); - - if (parp->ar_hrd == htons(ARPHRD_ETHER) && parp->ar_pro == htons(ETH_P_IP) - && parp->ar_op == htons(ARPOP_REQUEST) && parp->ar_hln == 6 && parp->ar_pln == 4) { - arpptr = (u8 *)parp + sizeof(struct arphdr); - sha = arpptr; - arpptr += net->addr_len; /* sha */ - memcpy(sip, arpptr, sizeof(sip)); - arpptr += sizeof(sip); - arpptr += net->addr_len; /* tha */ - memcpy(tip, arpptr, sizeof(tip)); - - pr_info("%s sip = %d.%d.%d.%d, tip=%d.%d.%d.%d, ipv4=%d.%d.%d.%d\n", netdev_name(net), - sip[0], sip[1], sip[2], sip[3], tip[0], tip[1], tip[2], tip[3], ipv4[0], ipv4[1], ipv4[2], ipv4[3]); - //wwan0 sip = 10.151.137.255, tip=10.151.138.0, ipv4=10.151.137.255 - if (tip[0] == ipv4[0] && tip[1] == ipv4[1] && (tip[2]&0xFC) == (ipv4[2]&0xFC) && tip[3] != ipv4[3]) - reply = arp_create(ARPOP_REPLY, ETH_P_ARP, *((__be32 *)sip), net, *((__be32 *)tip), sha, ec20_mac, sha); - - if (reply) { - skb_reset_mac_header(reply); - __skb_pull(reply, skb_network_offset(reply)); - reply->ip_summed = CHECKSUM_UNNECESSARY; - reply->pkt_type = PACKET_HOST; - - netif_rx_ni(reply); - } - return 1; - } - - return 0; -} - -static struct sk_buff *bridge_mode_tx_fixup(struct net_device *net, struct sk_buff *skb, uint bridge_ipv4, unsigned char *bridge_mac) { - struct ethhdr *ehdr; - const struct iphdr *iph; - - skb_reset_mac_header(skb); - ehdr = eth_hdr(skb); - - if (ehdr->h_proto == htons(ETH_P_ARP)) { - if (bridge_ipv4) - bridge_arp_reply(net, skb, bridge_ipv4); - return NULL; - } - - iph = ip_hdr(skb); - //DBG("iphdr: "); - //PrintHex((void *)iph, sizeof(struct iphdr)); - -// 1 0.000000000 0.0.0.0 255.255.255.255 DHCP 362 DHCP Request - Transaction ID 0xe7643ad7 - if (ehdr->h_proto == htons(ETH_P_IP) && iph->protocol == IPPROTO_UDP && iph->saddr == 0x00000000 && iph->daddr == 0xFFFFFFFF) { - //if (udp_hdr(skb)->dest == htons(67)) //DHCP Request - { - memcpy(bridge_mac, ehdr->h_source, ETH_ALEN); - pr_info("%s PC Mac Address: %02x:%02x:%02x:%02x:%02x:%02x\n", netdev_name(net), - bridge_mac[0], bridge_mac[1], bridge_mac[2], bridge_mac[3], bridge_mac[4], bridge_mac[5]); - } - } - - if (memcmp(ehdr->h_source, bridge_mac, ETH_ALEN)) { - return NULL; - } - - return skb; -} - -static void bridge_mode_rx_fixup(sGobiUSBNet *pQmapDev, struct net_device *net, struct sk_buff *skb) { - uint bridge_mode = 0; - unsigned char *bridge_mac; - - if (pQmapDev->qmap_mode > 1) { - struct qmap_priv *priv = netdev_priv(net); - bridge_mode = priv->m_bridge_mode; - bridge_mac = priv->mHostMAC; - } - else { - bridge_mode = pQmapDev->m_bridge_mode; - bridge_mac = pQmapDev->mHostMAC; - } - - if (bridge_mode) - memcpy(eth_hdr(skb)->h_dest, bridge_mac, ETH_ALEN); - else - memcpy(eth_hdr(skb)->h_dest, net->dev_addr, ETH_ALEN); -} - -static ssize_t bridge_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct net_device *pNet = to_net_dev(dev); - uint bridge_mode = 0; - - if (is_qmap_netdev(pNet)) { - struct qmap_priv *priv = netdev_priv(pNet); - bridge_mode = priv->m_bridge_mode; - } - else { - struct usbnet * pDev = netdev_priv( pNet ); - sGobiUSBNet * pGobiDev = (sGobiUSBNet *)pDev->data[0]; - bridge_mode = pGobiDev->m_bridge_mode; - } - - return snprintf(buf, PAGE_SIZE, "%d\n", bridge_mode); -} - -static ssize_t bridge_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct net_device *pNet = to_net_dev(dev); - uint old_mode = 0; - uint bridge_mode = simple_strtoul(buf, NULL, 0); - - if (pNet->type != ARPHRD_ETHER) { - return count; - } - - if (is_qmap_netdev(pNet)) { - struct qmap_priv *priv = netdev_priv(pNet); - - old_mode = priv->m_bridge_mode; - priv->m_bridge_mode = bridge_mode; - } - else { - struct usbnet * pDev = netdev_priv( pNet ); - sGobiUSBNet * pGobiDev = (sGobiUSBNet *)pDev->data[0]; - - old_mode = pGobiDev->m_bridge_mode; - pGobiDev->m_bridge_mode = bridge_mode; - } - - if (old_mode != bridge_mode) - dev_info(dev, "bridge_mode change to 0x%x\n", bridge_mode); - - return count; -} - -static ssize_t bridge_ipv4_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct net_device *pNet = to_net_dev(dev); - unsigned int bridge_ipv4 = 0; - unsigned char ipv4[4]; - - if (is_qmap_netdev(pNet)) { - struct qmap_priv *priv = netdev_priv(pNet); - bridge_ipv4 = priv->m_bridge_ipv4; - } - else { - struct usbnet * pDev = netdev_priv( pNet ); - sGobiUSBNet * pGobiDev = (sGobiUSBNet *)pDev->data[0]; - bridge_ipv4 = pGobiDev->m_bridge_ipv4; - } - - ipv4[0] = (bridge_ipv4 >> 24) & 0xFF; - ipv4[1] = (bridge_ipv4 >> 16) & 0xFF; - ipv4[2] = (bridge_ipv4 >> 8) & 0xFF; - ipv4[3] = (bridge_ipv4 >> 0) & 0xFF; - - return snprintf(buf, PAGE_SIZE, "%d.%d.%d.%d\n", ipv4[0], ipv4[1], ipv4[2], ipv4[3]); -} - -static ssize_t bridge_ipv4_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct net_device *pNet = to_net_dev(dev); - - if (is_qmap_netdev(pNet)) { - struct qmap_priv *priv = netdev_priv(pNet); - priv->m_bridge_ipv4 = simple_strtoul(buf, NULL, 16); - } - else { - struct usbnet * pDev = netdev_priv( pNet ); - sGobiUSBNet * pGobiDev = (sGobiUSBNet *)pDev->data[0]; - pGobiDev->m_bridge_ipv4 = simple_strtoul(buf, NULL, 16); - } - - return count; -} - -static DEVICE_ATTR(bridge_mode, S_IWUSR | S_IRUGO, bridge_mode_show, bridge_mode_store); -static DEVICE_ATTR(bridge_ipv4, S_IWUSR | S_IRUGO, bridge_ipv4_show, bridge_ipv4_store); - -static struct attribute *qmi_qmap_sysfs_attrs[] = { - &dev_attr_bridge_mode.attr, - &dev_attr_bridge_ipv4.attr, - NULL, -}; - -static struct attribute_group qmi_qmap_sysfs_attr_group = { - .attrs = qmi_qmap_sysfs_attrs, -}; -#endif - -#ifdef QUECTEL_WWAN_QMAP -static sGobiUSBNet * net_to_qmap(struct net_device *dev) { - struct usbnet *usbnet = netdev_priv(dev); - sGobiUSBNet * pGobiDev = (sGobiUSBNet *)usbnet->data[0]; - - return pGobiDev; -} - -static struct sk_buff * add_qhdr(struct sk_buff *skb, u8 mux_id) { - struct qmap_hdr *qhdr; - int pad = 0; - - pad = skb->len%4; - if (pad) { - pad = 4 - pad; - if (skb_tailroom(skb) < pad) { - printk("skb_tailroom small!\n"); - pad = 0; - } - if (pad) - __skb_put(skb, pad); - } - - qhdr = (struct qmap_hdr *)skb_push(skb, sizeof(struct qmap_hdr)); - qhdr->cd_rsvd_pad = pad; - qhdr->mux_id = mux_id; - qhdr->pkt_len = cpu_to_be16(skb->len - sizeof(struct qmap_hdr)); - - return skb; -} - -static struct sk_buff * add_qhdr_v5(struct sk_buff *skb, u8 mux_id) { - struct rmnet_map_header *map_header; - struct rmnet_map_v5_csum_header *ul_header; - u32 padding, map_datalen; - - map_datalen = skb->len; - padding = map_datalen%4; - if (padding) { - padding = 4 - padding; - if (skb_tailroom(skb) < padding) { - printk("skb_tailroom small!\n"); - padding = 0; - } - if (padding) - __skb_put(skb, padding); - } - - map_header = (struct rmnet_map_header *)skb_push(skb, (sizeof(struct rmnet_map_header) + sizeof(struct rmnet_map_v5_csum_header))); - map_header->cd_bit = 0; - map_header->next_hdr = 1; - map_header->pad_len = padding; - map_header->mux_id = mux_id; - map_header->pkt_len = htons(map_datalen + padding); - - ul_header = (struct rmnet_map_v5_csum_header *)(map_header + 1); - memset(ul_header, 0, sizeof(*ul_header)); - ul_header->header_type = RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD; - if (skb->ip_summed == CHECKSUM_PARTIAL) { -#if 0 //TODO - skb->ip_summed = CHECKSUM_NONE; - /* Ask for checksum offloading */ - ul_header->csum_valid_required = 1; -#endif - } - - return skb; -} - -static void rmnet_usb_tx_wake_queue(unsigned long data) { - sGobiUSBNet *pQmapDev = (void *)data; - int i; - - for (i = 0; i < pQmapDev->qmap_mode; i++) { - struct net_device *qmap_net = pQmapDev->mpQmapNetDev[i]; - if (qmap_net) { - if (netif_queue_stopped(qmap_net) && !netif_queue_stopped(pQmapDev->mpNetDev->net)) { - netif_wake_queue(qmap_net); - } - } - } -} - -static void rmnet_usb_tx_skb_destructor(struct sk_buff *skb) { - sGobiUSBNet *pQmapDev = net_to_qmap(skb->dev); - int i; - - for (i = 0; i < pQmapDev->qmap_mode; i++) { - struct net_device *qmap_net = pQmapDev->mpQmapNetDev[i]; - - if (qmap_net) { - if (netif_queue_stopped(qmap_net)) { - tasklet_schedule(&pQmapDev->txq); - break; - } - } - } -} - -static void rmnet_vnd_update_rx_stats(struct net_device *net, - unsigned rx_packets, unsigned rx_bytes) { - net->stats.rx_packets += rx_packets; - net->stats.rx_bytes += rx_bytes; -} - -static void rmnet_vnd_update_tx_stats(struct net_device *net, - unsigned tx_packets, unsigned tx_bytes) { - net->stats.tx_packets += tx_packets; - net->stats.tx_bytes += tx_bytes; -} - -#if defined(QUECTEL_UL_DATA_AGG) -static long agg_time_limit __read_mostly = 1000000L; //reduce this time, can get better TPUT performance, but will increase USB interrupts -module_param(agg_time_limit, long, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(agg_time_limit, "Maximum time packets sit in the agg buf"); - -static long agg_bypass_time __read_mostly = 10000000L; -module_param(agg_bypass_time, long, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(agg_bypass_time, "Skip agg when apart spaced more than this"); - -static int rmnet_usb_tx_agg_skip(struct sk_buff *skb, int offset) -{ - u8 *packet_start = skb->data + offset; - int ready2send = 0; - - if (skb->protocol == htons(ETH_P_IP)) { - struct iphdr *ip4h = (struct iphdr *)(packet_start); - - if (ip4h->protocol == IPPROTO_TCP) { - const struct tcphdr *th = (const struct tcphdr *)(packet_start + sizeof(struct iphdr)); - if (th->psh) { - ready2send = 1; - } - } - else if (ip4h->protocol == IPPROTO_ICMP) - ready2send = 1; - - } else if (skb->protocol == htons(ETH_P_IPV6)) { - struct ipv6hdr *ip6h = (struct ipv6hdr *)(packet_start); - - if (ip6h->nexthdr == NEXTHDR_TCP) { - const struct tcphdr *th = (const struct tcphdr *)(packet_start + sizeof(struct ipv6hdr)); - if (th->psh) { - ready2send = 1; - } - } else if (ip6h->nexthdr == NEXTHDR_ICMP) { - ready2send = 1; - } else if (ip6h->nexthdr == NEXTHDR_FRAGMENT) { - struct frag_hdr *frag; - - frag = (struct frag_hdr *)(packet_start - + sizeof(struct ipv6hdr)); - if (frag->nexthdr == IPPROTO_ICMPV6) - ready2send = 1; - } - } - - return ready2send; -} - -static void rmnet_usb_tx_agg_work(struct work_struct *work) -{ - struct qmap_priv *priv = - container_of(work, struct qmap_priv, agg_wq); - struct sk_buff *skb = NULL; - unsigned long flags; - - spin_lock_irqsave(&priv->agg_lock, flags); - if (likely(priv->agg_skb)) { - skb = priv->agg_skb; - priv->agg_skb = NULL; - priv->agg_count = 0; - skb->protocol = htons(ETH_P_MAP); - skb->dev = priv->real_dev; - ktime_get_ts64(&priv->agg_time); - } - spin_unlock_irqrestore(&priv->agg_lock, flags); - - if (skb) { - int err = dev_queue_xmit(skb); - if (err != NET_XMIT_SUCCESS) { - priv->self_dev->stats.tx_errors++; - } - } -} - -static enum hrtimer_restart rmnet_usb_tx_agg_timer_cb(struct hrtimer *timer) -{ - struct qmap_priv *priv = - container_of(timer, struct qmap_priv, agg_hrtimer); - - schedule_work(&priv->agg_wq); - return HRTIMER_NORESTART; -} - -static int rmnet_usb_tx_agg(struct sk_buff *skb, struct qmap_priv *priv) { - int ready2send = 0; - int xmit_more = 0; - struct timespec64 diff, now; - struct sk_buff *agg_skb = NULL; - unsigned long flags; - int err; - struct net_device *pNet = priv->self_dev; - -#if LINUX_VERSION_CODE < KERNEL_VERSION(5,1,0) //6b16f9ee89b8d5709f24bc3ac89ae8b5452c0d7c -#if LINUX_VERSION_CODE > KERNEL_VERSION(3,16,0) - xmit_more = skb->xmit_more; -#endif -#else - xmit_more = netdev_xmit_more(); -#endif - - rmnet_vnd_update_tx_stats(pNet, 1, skb->len); - - if (priv->ul_data_aggregation_max_datagrams == 1) { - skb->protocol = htons(ETH_P_MAP); - skb->dev = priv->real_dev; - if (!skb->destructor) - skb->destructor = rmnet_usb_tx_skb_destructor; - err = dev_queue_xmit(skb); - if (err != NET_XMIT_SUCCESS) - pNet->stats.tx_errors++; - return NET_XMIT_SUCCESS; - } - -new_packet: - spin_lock_irqsave(&priv->agg_lock, flags); - agg_skb = NULL; - ready2send = 0; - ktime_get_ts64(&now); - diff = timespec64_sub(now, priv->agg_time); - - if (priv->agg_skb) { - if ((priv->agg_skb->len + skb->len) < priv->ul_data_aggregation_max_size) { - memcpy(skb_put(priv->agg_skb, skb->len), skb->data, skb->len); - priv->agg_count++; - - if (diff.tv_sec > 0 || diff.tv_nsec > agg_time_limit) { - ready2send = 1; - } - else if (priv->agg_count == priv->ul_data_aggregation_max_datagrams) { - ready2send = 1; - } - else if (xmit_more == 0) { - struct rmnet_map_header *map_header = (struct rmnet_map_header *)skb->data; - size_t offset = sizeof(struct rmnet_map_header); - if (map_header->next_hdr) - offset += sizeof(struct rmnet_map_v5_csum_header); - - ready2send = rmnet_usb_tx_agg_skip(skb, offset); - } - - dev_kfree_skb_any(skb); - skb = NULL; - } - else { - ready2send = 1; - } - - if (ready2send) { - agg_skb = priv->agg_skb; - priv->agg_skb = NULL; - priv->agg_count = 0; - } - } - else if (skb) { - if (diff.tv_sec > 0 || diff.tv_nsec > agg_bypass_time) { - ready2send = 1; - } - else if (xmit_more == 0) { - struct rmnet_map_header *map_header = (struct rmnet_map_header *)skb->data; - size_t offset = sizeof(struct rmnet_map_header); - if (map_header->next_hdr) - offset += sizeof(struct rmnet_map_v5_csum_header); - - ready2send = rmnet_usb_tx_agg_skip(skb, offset); - } - - if (ready2send == 0) { - priv->agg_skb = alloc_skb(priv->ul_data_aggregation_max_size, GFP_ATOMIC); - if (priv->agg_skb) { - memcpy(skb_put(priv->agg_skb, skb->len), skb->data, skb->len); - priv->agg_count++; - dev_kfree_skb_any(skb); - skb = NULL; - } - else { - ready2send = 1; - } - } - - if (ready2send) { - agg_skb = skb; - skb = NULL; - } - } - - if (ready2send) { - priv->agg_time = now; - } - spin_unlock_irqrestore(&priv->agg_lock, flags); - - if (agg_skb) { - agg_skb->protocol = htons(ETH_P_MAP); - agg_skb->dev = priv->real_dev; - if (!agg_skb->destructor) - agg_skb->destructor = rmnet_usb_tx_skb_destructor; - err = dev_queue_xmit(agg_skb); - if (err != NET_XMIT_SUCCESS) { - pNet->stats.tx_errors++; - } - } - - if (skb) { - goto new_packet; - } - - if (priv->agg_skb) { - if (!hrtimer_is_queued(&priv->agg_hrtimer)) - hrtimer_start(&priv->agg_hrtimer, ns_to_ktime(NSEC_PER_MSEC * 2), HRTIMER_MODE_REL); - } - - return NET_XMIT_SUCCESS; -} -#endif - -static int qmap_open(struct net_device *dev) -{ - struct qmap_priv *priv = netdev_priv(dev); - sGobiUSBNet * pGobiDev = net_to_qmap(priv->real_dev); - - if (!(priv->real_dev->flags & IFF_UP)) - return -ENETDOWN; - - if (!pGobiDev->mbQMIReady) - return -ENETDOWN; - -#if defined(QUECTEL_UL_DATA_AGG) - if (priv->ul_data_aggregation_max_datagrams == 1 && pGobiDev->agg_ctx.ul_data_aggregation_max_datagrams > 1) { - priv->ul_data_aggregation_max_datagrams = pGobiDev->agg_ctx.ul_data_aggregation_max_datagrams; - priv->ul_data_aggregation_max_size = pGobiDev->agg_ctx.ul_data_aggregation_max_size; - priv->dl_minimum_padding = pGobiDev->agg_ctx.dl_minimum_padding; - } -#endif - - if (netif_carrier_ok(priv->real_dev) && priv->link_state) - netif_carrier_on(dev); - - if (netif_carrier_ok(dev)) { - if (netif_queue_stopped(dev) && !netif_queue_stopped(priv->real_dev)) - netif_wake_queue(dev); - } - - return 0; -} - -static int qmap_stop(struct net_device *pNet) -{ - netif_carrier_off(pNet); - return 0; -} - -static int qmap_start_xmit(struct sk_buff *skb, struct net_device *pNet) -{ - int err; - struct qmap_priv *priv = netdev_priv(pNet); - - if (netif_queue_stopped(priv->real_dev)) { - //printk(KERN_DEBUG "s\n"); - netif_stop_queue(pNet); - return NETDEV_TX_BUSY; - } - - if (pNet->type == ARPHRD_ETHER) { -#ifdef QUECTEL_BRIDGE_MODE - if (priv->m_bridge_mode && bridge_mode_tx_fixup(pNet, skb, priv->m_bridge_ipv4, priv->mHostMAC) == NULL) { - dev_kfree_skb_any (skb); - return NETDEV_TX_OK; - } -#endif - - if (ether_to_ip_fixup(pNet, skb) == NULL) { - dev_kfree_skb_any (skb); - return NETDEV_TX_OK; - } - } - - if (priv->qmap_version == 5) { - add_qhdr(skb, priv->mux_id); - } - else if (priv->qmap_version == 9) { - add_qhdr_v5(skb, priv->mux_id); - } - else { - dev_kfree_skb_any (skb); - return NETDEV_TX_OK; - } - -#if defined(QUECTEL_UL_DATA_AGG) - err = rmnet_usb_tx_agg(skb, priv); -#else - skb->protocol = htons(ETH_P_MAP); - skb->dev = priv->real_dev; - if (!skb->destructor) - skb->destructor = rmnet_usb_tx_skb_destructor; - err = dev_queue_xmit(skb); -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,14 )) - if (err == NET_XMIT_SUCCESS) { - rmnet_vnd_update_tx_stats(pNet, 1, skb->len); - } else { - pNet->stats.tx_errors++; - } -#endif -#endif - - return err; -} - -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,29 )) -#else -static const struct net_device_ops qmap_netdev_ops = { - .ndo_open = qmap_open, - .ndo_stop = qmap_stop, - .ndo_start_xmit = qmap_start_xmit, -}; -#endif - -#ifdef QUECTEL_BRIDGE_MODE -static int is_qmap_netdev(const struct net_device *netdev) { -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,29 )) - return netdev->open == qmap_open; -#else - return netdev->netdev_ops == &qmap_netdev_ops; -#endif -} -#endif - -static int qmap_register_device(sGobiUSBNet * pDev, u8 offset_id) -{ - struct net_device *real_dev = pDev->mpNetDev->net; - struct net_device *qmap_net; - struct qmap_priv *priv; - int err; - - qmap_net = alloc_etherdev(sizeof(*priv)); - if (!qmap_net) - return -ENOBUFS; - - SET_NETDEV_DEV(qmap_net, &real_dev->dev); - priv = netdev_priv(qmap_net); - priv->offset_id = offset_id; - priv->mux_id = QUECTEL_QMAP_MUX_ID + offset_id; - priv->qmap_version = pDev->qmap_version; - priv->real_dev = real_dev; - priv->self_dev = qmap_net; - -#if defined(QUECTEL_UL_DATA_AGG) - priv->ul_data_aggregation_max_datagrams = 1; - priv->ul_data_aggregation_max_size = 2048; - priv->dl_minimum_padding = 0; - priv->agg_skb = NULL; - priv->agg_count = 0; - hrtimer_init(&priv->agg_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - priv->agg_hrtimer.function = rmnet_usb_tx_agg_timer_cb; - INIT_WORK(&priv->agg_wq, rmnet_usb_tx_agg_work); - ktime_get_ts64(&priv->agg_time); - spin_lock_init(&priv->agg_lock); -#endif - - sprintf(qmap_net->name, "%s.%d", real_dev->name, offset_id + 1); -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,29 )) - qmap_net->open = qmap_open; - qmap_net->stop = qmap_stop; - qmap_net->hard_start_xmit = qmap_start_xmit; -#else - qmap_net->netdev_ops = &qmap_netdev_ops; -#endif - memcpy (qmap_net->dev_addr, real_dev->dev_addr, ETH_ALEN); - -#ifdef QUECTEL_BRIDGE_MODE - priv->m_bridge_mode = !!(pDev->m_bridge_mode & BIT(offset_id)); - qmap_net->sysfs_groups[0] = &qmi_qmap_sysfs_attr_group; -#endif - - err = register_netdev(qmap_net); - if (err < 0) { - INFO("register_netdev(%s), err=%d\n", qmap_net->name, err); - goto out_free_newdev; - } - netif_device_attach (qmap_net); - - pDev->mpQmapNetDev[offset_id] = qmap_net; - qmap_net->flags |= IFF_NOARP; - qmap_net->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); - - INFO("%s\n", qmap_net->name); - - return 0; - -out_free_newdev: - free_netdev(qmap_net); - return err; -} - -static void qmap_unregister_device(sGobiUSBNet * pDev, u8 offset_id) { - struct net_device *qmap_net; -#if defined(QUECTEL_UL_DATA_AGG) - struct qmap_priv *priv; - unsigned long flags; -#endif - - qmap_net = pDev->mpQmapNetDev[offset_id]; - if (qmap_net == NULL) - return; - - netif_carrier_off(qmap_net); - netif_stop_queue(qmap_net); - -#if defined(QUECTEL_UL_DATA_AGG) - priv = netdev_priv(qmap_net); - hrtimer_cancel(&priv->agg_hrtimer); - cancel_work_sync(&priv->agg_wq); - spin_lock_irqsave(&priv->agg_lock, flags); - if (priv->agg_skb) { - kfree_skb(priv->agg_skb); - } - spin_unlock_irqrestore(&priv->agg_lock, flags); -#endif - - unregister_netdev(qmap_net); - free_netdev(qmap_net); -} - -static ssize_t qmap_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct net_device *pNet = to_net_dev(dev); - struct usbnet * pDev = netdev_priv( pNet ); - sGobiUSBNet * pGobiDev = (sGobiUSBNet *)pDev->data[0]; - - return snprintf(buf, PAGE_SIZE, "%d\n", pGobiDev->qmap_mode); -} - -static DEVICE_ATTR(qmap_mode, S_IRUGO, qmap_mode_show, NULL); - -static ssize_t qmap_size_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct net_device *pNet = to_net_dev(dev); - struct usbnet * pDev = netdev_priv( pNet ); - sGobiUSBNet * pGobiDev = (sGobiUSBNet *)pDev->data[0]; - - return snprintf(buf, PAGE_SIZE, "%d\n", pGobiDev->qmap_size); -} - -static DEVICE_ATTR(qmap_size, S_IRUGO, qmap_size_show, NULL); - -static ssize_t link_state_show(struct device *dev, struct device_attribute *attr, char *buf) { - sGobiUSBNet *pQmapDev = net_to_qmap(to_net_dev(dev)); - - return snprintf(buf, PAGE_SIZE, "0x%x\n", pQmapDev->link_state); -} - -static ssize_t link_state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct net_device *netdev = to_net_dev(dev); - sGobiUSBNet *pQmapDev = net_to_qmap(to_net_dev(dev)); - unsigned qmap_mode = pQmapDev->qmap_mode; - unsigned link_state = 0; - unsigned old_link = pQmapDev->link_state; - uint offset_id = 0; - - link_state = simple_strtoul(buf, NULL, 0); - if (qmap_mode == 1) { - pQmapDev->link_state = !!link_state; - } - else if (qmap_mode > 1) { - offset_id = ((link_state&0x7F) - 1); - - if (offset_id >= qmap_mode) { - dev_info(dev, "%s offset_id is %d. but qmap_mode is %d\n", __func__, offset_id, pQmapDev->qmap_mode); - return count; - } - - if (link_state&0x80) - pQmapDev->link_state &= ~(1 << offset_id); - else - pQmapDev->link_state |= (1 << offset_id); - } - - if (old_link != pQmapDev->link_state) { - struct net_device *qmap_net = pQmapDev->mpQmapNetDev[offset_id]; - - if (pQmapDev->link_state) { - netif_carrier_on(netdev); - } else { - netif_carrier_off(netdev); - } - - if (qmap_net && qmap_net != netdev) { - struct qmap_priv *priv = netdev_priv(qmap_net); - - priv->link_state = !!(pQmapDev->link_state & (1 << offset_id)); - if (priv->link_state) { - netif_carrier_on(qmap_net); - if (netif_queue_stopped(qmap_net) && !netif_queue_stopped(priv->real_dev)) - netif_wake_queue(qmap_net); - } - else - netif_carrier_off(qmap_net); - } - } - - if (old_link != pQmapDev->link_state) - dev_info(dev, "link_state 0x%x -> 0x%x\n", old_link, pQmapDev->link_state); - - return count; -} - -static DEVICE_ATTR(link_state, S_IWUSR | S_IRUGO, link_state_show, link_state_store); -#endif - -static struct attribute *gobinet_sysfs_attrs[] = { -#ifdef QUECTEL_BRIDGE_MODE - &dev_attr_bridge_mode.attr, - &dev_attr_bridge_ipv4.attr, -#endif -#ifdef QUECTEL_WWAN_QMAP - &dev_attr_qmap_mode.attr, - &dev_attr_qmap_size.attr, - &dev_attr_link_state.attr, -#endif - NULL, -}; - -static struct attribute_group gobinet_sysfs_attr_group = { - .attrs = gobinet_sysfs_attrs, -}; - -#if defined(QUECTEL_WWAN_QMAP) -typedef struct { - unsigned int size; - unsigned int rx_urb_size; - unsigned int ep_type; - unsigned int iface_id; - unsigned int qmap_mode; - unsigned int qmap_version; - unsigned int dl_minimum_padding; - char ifname[8][16]; - unsigned char mux_id[8]; -} RMNET_INFO; - -static void rmnet_info_set(struct sGobiUSBNet *pQmapDev, RMNET_INFO *rmnet_info) -{ - int i; - - memset(rmnet_info, 0, sizeof(*rmnet_info)); - rmnet_info->size = sizeof(RMNET_INFO); - rmnet_info->rx_urb_size = pQmapDev->qmap_size; - rmnet_info->ep_type = 2; //DATA_EP_TYPE_HSUSB - rmnet_info->iface_id = 4; - rmnet_info->qmap_mode = pQmapDev->qmap_mode; - rmnet_info->qmap_version = pQmapDev->qmap_version; - rmnet_info->dl_minimum_padding = 0; - - for (i = 0; i < pQmapDev->qmap_mode; i++) { - struct net_device *qmap_net = pQmapDev->mpQmapNetDev[i]; - - if (!qmap_net) - break; - - strcpy(rmnet_info->ifname[i], qmap_net->name); - rmnet_info->mux_id[i] = QUECTEL_QMAP_MUX_ID; - if (pQmapDev->qmap_mode > 1) { - struct qmap_priv *priv = netdev_priv(qmap_net); - - rmnet_info->mux_id[i] = priv->mux_id; - } - } -} - -static int qmap_ndo_do_ioctl(struct net_device *dev,struct ifreq *ifr, int cmd) { - int rc = -EOPNOTSUPP; - uint link_state = 0; - sGobiUSBNet *pQmapDev = net_to_qmap(dev); - - atomic_inc(&pQmapDev->refcount); - if (!pQmapDev->mbQMIReady) { - if (wait_for_completion_interruptible_timeout(&pQmapDev->mQMIReadyCompletion, 15*HZ) <= 0) { - if (atomic_dec_and_test(&pQmapDev->refcount)) { - kfree( pQmapDev ); - } - return -ETIMEDOUT; - } - } - atomic_dec(&pQmapDev->refcount); - - switch (cmd) { - case 0x89F1: //SIOCDEVPRIVATE - rc = copy_from_user(&link_state, ifr->ifr_ifru.ifru_data, sizeof(link_state)); - if (!rc) { - char buf[32]; - snprintf(buf, sizeof(buf), "%u", link_state); - link_state_store(&dev->dev, NULL, buf, strlen(buf)); - } - break; - - case 0x89F2: //SIOCDEVPRIVATE - rc = 0; - break; - - case 0x89F3: //SIOCDEVPRIVATE - if (pQmapDev->qmap_mode) { - RMNET_INFO rmnet_info; - - rmnet_info_set(pQmapDev, &rmnet_info); - rc = copy_to_user(ifr->ifr_ifru.ifru_data, &rmnet_info, sizeof(rmnet_info)); - } - break; - - default: - break; - } - - return rc; -} -#endif - -#ifdef CONFIG_PM -/*=========================================================================== -METHOD: - GobiNetSuspend (Public Method) - -DESCRIPTION: - Stops QMI traffic while device is suspended - -PARAMETERS - pIntf [ I ] - Pointer to interface - powerEvent [ I ] - Power management event - -RETURN VALUE: - int - 0 for success - negative errno for failure -===========================================================================*/ -static int GobiNetSuspend( - struct usb_interface * pIntf, - pm_message_t powerEvent ) -{ - struct usbnet * pDev; - sGobiUSBNet * pGobiDev; - - if (pIntf == 0) - { - return -ENOMEM; - } - -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,23 )) - pDev = usb_get_intfdata( pIntf ); -#else - pDev = (struct usbnet *)pIntf->dev.platform_data; -#endif - - if (pDev == NULL || pDev->net == NULL) - { - DBG( "failed to get netdevice\n" ); - return -ENXIO; - } - - pGobiDev = (sGobiUSBNet *)pDev->data[0]; - if (pGobiDev == NULL) - { - DBG( "failed to get QMIDevice\n" ); - return -ENXIO; - } - - if (pGobiDev->mbQMISyncIng) - { - DBG( "QMI sync ing\n" ); - return -EBUSY; - } - - // Is this autosuspend or system suspend? - // do we allow remote wakeup? -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,33 )) -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,18 )) - if (pDev->udev->auto_pm == 0) -#else - if (1) -#endif -#else - if ((powerEvent.event & PM_EVENT_AUTO) == 0) -#endif - { - DBG( "device suspended to power level %d\n", - powerEvent.event ); - GobiSetDownReason( pGobiDev, DRIVER_SUSPENDED ); - } - else - { - DBG( "device autosuspend\n" ); - } - - if (powerEvent.event & PM_EVENT_SUSPEND) - { - // Stop QMI read callbacks - if (pGobiDev->m_qcrmcall_mode) { - } else { - KillRead( pGobiDev ); - } -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,22 )) - pDev->udev->reset_resume = 0; -#endif - - // Store power state to avoid duplicate resumes - pIntf->dev.power.power_state.event = powerEvent.event; - } - else - { - // Other power modes cause QMI connection to be lost -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,22 )) - pDev->udev->reset_resume = 1; -#endif - } - - // Run usbnet's suspend function - return usbnet_suspend( pIntf, powerEvent ); -} -int QuecGobiNetSuspend(struct usb_interface *pIntf, pm_message_t powerEvent ) { - return GobiNetSuspend(pIntf, powerEvent); -} - -/*=========================================================================== -METHOD: - GobiNetResume (Public Method) - -DESCRIPTION: - Resume QMI traffic or recreate QMI device - -PARAMETERS - pIntf [ I ] - Pointer to interface - -RETURN VALUE: - int - 0 for success - negative errno for failure -===========================================================================*/ -static int GobiNetResume( struct usb_interface * pIntf ) -{ - struct usbnet * pDev; - sGobiUSBNet * pGobiDev; - int nRet; - int oldPowerState; - - if (pIntf == 0) - { - return -ENOMEM; - } - -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,23 )) - pDev = usb_get_intfdata( pIntf ); -#else - pDev = (struct usbnet *)pIntf->dev.platform_data; -#endif - - if (pDev == NULL || pDev->net == NULL) - { - DBG( "failed to get netdevice\n" ); - return -ENXIO; - } - - pGobiDev = (sGobiUSBNet *)pDev->data[0]; - if (pGobiDev == NULL) - { - DBG( "failed to get QMIDevice\n" ); - return -ENXIO; - } - - oldPowerState = pIntf->dev.power.power_state.event; - pIntf->dev.power.power_state.event = PM_EVENT_ON; - DBG( "resuming from power mode %d\n", oldPowerState ); - - if (oldPowerState & PM_EVENT_SUSPEND) - { - // It doesn't matter if this is autoresume or system resume - GobiClearDownReason( pGobiDev, DRIVER_SUSPENDED ); - - nRet = usbnet_resume( pIntf ); - if (nRet != 0) - { - DBG( "usbnet_resume error %d\n", nRet ); - return nRet; - } - - // Restart QMI read callbacks - if (pGobiDev->m_qcrmcall_mode) { - nRet = 0; - } else { - nRet = StartRead( pGobiDev ); - } - if (nRet != 0) - { - DBG( "StartRead error %d\n", nRet ); - return nRet; - } - -#ifdef CONFIG_PM - #if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,29 )) - // Kick Auto PM thread to process any queued URBs - complete( &pGobiDev->mAutoPM.mThreadDoWork ); - #endif -#endif /* CONFIG_PM */ - -#if defined(QUECTEL_WWAN_QMAP) - if ((!netif_queue_stopped(pDev->net)) && (pGobiDev->qmap_mode > 1)) { - rmnet_usb_tx_wake_queue((unsigned long )pGobiDev); - } -#endif - } - else - { - DBG( "nothing to resume\n" ); - return 0; - } - - return nRet; -} -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,27 )) -static int GobiNetResetResume( struct usb_interface * pIntf ) -{ - INFO( "device do not support reset_resume\n" ); - pIntf->needs_binding = 1; - - return -EOPNOTSUPP; -} -#endif -#endif /* CONFIG_PM */ - -static void ql_net_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) -{ - usbnet_get_drvinfo(net, info); - /* Inherit standard device info */ - strlcpy(info->driver, driver_name, sizeof(info->driver)); - strlcpy(info->version, VERSION_NUMBER, sizeof(info->version)); -} - -static struct ethtool_ops ql_net_ethtool_ops; - -/*=========================================================================== -METHOD: - GobiNetDriverBind (Public Method) - -DESCRIPTION: - Setup in and out pipes - -PARAMETERS - pDev [ I ] - Pointer to usbnet device - pIntf [ I ] - Pointer to interface - -RETURN VALUE: - int - 0 for success - Negative errno for error -===========================================================================*/ -static int GobiNetDriverBind( - struct usbnet * pDev, - struct usb_interface * pIntf ) -{ - int numEndpoints; - int endpointIndex; - struct usb_host_endpoint * pEndpoint = NULL; - struct usb_host_endpoint * pIn = NULL; - struct usb_host_endpoint * pOut = NULL; - - // Verify one altsetting - if (pIntf->num_altsetting != 1) - { - DBG( "invalid num_altsetting %u\n", pIntf->num_altsetting ); - return -ENODEV; - } - - // Verify correct interface (4 for UC20) - if ( !test_bit(pIntf->cur_altsetting->desc.bInterfaceNumber, &pDev->driver_info->data)) - { - DBG( "invalid interface %d\n", - pIntf->cur_altsetting->desc.bInterfaceNumber ); - return -ENODEV; - } - - if ( pIntf->cur_altsetting->desc.bInterfaceClass != 0xff) - { - struct usb_interface_descriptor *desc = &pIntf->cur_altsetting->desc; - const char *qcfg_usbnet = "UNKNOW"; - - if (desc->bInterfaceClass == 2 && desc->bInterfaceSubClass == 0x0e) { - qcfg_usbnet = "MBIM"; - } else if (desc->bInterfaceClass == 2 && desc->bInterfaceSubClass == 0x06) { - qcfg_usbnet = "ECM"; - } else if (desc->bInterfaceClass == 0xe0 && desc->bInterfaceSubClass == 1 && desc->bInterfaceProtocol == 3) { - qcfg_usbnet = "RNDIS"; - } - - INFO( "usbnet is %s not NDIS/RMNET!\n", qcfg_usbnet); - - return -ENODEV; - } - - // Collect In and Out endpoints - numEndpoints = pIntf->cur_altsetting->desc.bNumEndpoints; - for (endpointIndex = 0; endpointIndex < numEndpoints; endpointIndex++) - { - pEndpoint = pIntf->cur_altsetting->endpoint + endpointIndex; - if (pEndpoint == NULL) - { - DBG( "invalid endpoint %u\n", endpointIndex ); - return -ENODEV; - } - - if (usb_endpoint_dir_in( &pEndpoint->desc ) == true - && usb_endpoint_xfer_int( &pEndpoint->desc ) == false) - { - pIn = pEndpoint; - } - else if (usb_endpoint_dir_out( &pEndpoint->desc ) == true) - { - pOut = pEndpoint; - } - } - - if (pIn == NULL || pOut == NULL) - { - DBG( "invalid endpoints\n" ); - return -ENODEV; - } - - if (usb_set_interface( pDev->udev, - pIntf->cur_altsetting->desc.bInterfaceNumber, - 0 ) != 0) - { - DBG( "unable to set interface\n" ); - return -ENODEV; - } - - pDev->in = usb_rcvbulkpipe( pDev->udev, - pIn->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK ); - pDev->out = usb_sndbulkpipe( pDev->udev, - pOut->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK ); - -#if defined(QUECTEL_WWAN_MULTI_PACKAGES) - if (rx_packets && pDev->udev->descriptor.idVendor == cpu_to_le16(0x2C7C)) { - struct multi_package_config rx_config = { - .enable = cpu_to_le32(1), - .package_max_len = cpu_to_le32((1500 + sizeof(struct quec_net_package_header)) * rx_packets), - .package_max_count_in_queue = cpu_to_le32(rx_packets), - .timeout = cpu_to_le32(10*1000), //10ms - }; - int ret = 0; - - ret = usb_control_msg( - interface_to_usbdev(pIntf), - usb_sndctrlpipe(interface_to_usbdev(pIntf), 0), - USB_CDC_SET_MULTI_PACKAGE_COMMAND, - 0x21, //USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE - 1, - pIntf->cur_altsetting->desc.bInterfaceNumber, - &rx_config, sizeof(rx_config), 100); - - DBG( "Quectel EC21&EC25 rx_packets=%d, ret=%d\n", rx_packets, ret); - if (ret == sizeof(rx_config)) { - pDev->rx_urb_size = le32_to_cpu(rx_config.package_max_len); - } else { - rx_packets = 0; - } - } -#endif - -#if 1 //def DATA_MODE_RP - /* make MAC addr easily distinguishable from an IP header */ - if ((pDev->net->dev_addr[0] & 0xd0) == 0x40) { - /*clear this bit wil make usbnet apdater named as usbX(instead if ethX)*/ - pDev->net->dev_addr[0] |= 0x02; /* set local assignment bit */ - pDev->net->dev_addr[0] &= 0xbf; /* clear "IP" bit */ - } - memcpy (pDev->net->dev_addr, node_id, sizeof node_id); - pDev->net->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); - pDev->net->features |= (NETIF_F_VLAN_CHALLENGED); -#endif - - ql_net_ethtool_ops = *pDev->net->ethtool_ops; - ql_net_ethtool_ops.get_drvinfo = ql_net_get_drvinfo; - pDev->net->ethtool_ops = &ql_net_ethtool_ops; - - DBG( "in %x, out %x\n", - pIn->desc.bEndpointAddress, - pOut->desc.bEndpointAddress ); - - // In later versions of the kernel, usbnet helps with this -#if (LINUX_VERSION_CODE <= KERNEL_VERSION( 2,6,23 )) - pIntf->dev.platform_data = (void *)pDev; -#endif - - if (qcrmcall_mode == 0 && pDev->net->sysfs_groups[0] == NULL && gobinet_sysfs_attr_group.attrs[0] != NULL) { -#if (LINUX_VERSION_CODE <= KERNEL_VERSION( 2,6,32)) //see commit 0c509a6c9393b27a8c5a01acd4a72616206cfc24 - pDev->net->sysfs_groups[1] = &gobinet_sysfs_attr_group; //see netdev_register_sysfs() -#else - pDev->net->sysfs_groups[0] = &gobinet_sysfs_attr_group; -#endif - } - - if (!pDev->rx_urb_size) { -//to advoid module report mtu 1460, but rx 1500 bytes IP packets, and cause the customer's system crash -//next setting can make usbnet.c:usbnet_change_mtu() do not modify rx_urb_size according to mtu - pDev->rx_urb_size = ETH_DATA_LEN + ETH_HLEN + 6; - } - - return 0; -} - -/*=========================================================================== -METHOD: - GobiNetDriverUnbind (Public Method) - -DESCRIPTION: - Deregisters QMI device (Registration happened in the probe function) - -PARAMETERS - pDev [ I ] - Pointer to usbnet device - pIntfUnused [ I ] - Pointer to interface - -RETURN VALUE: - None -===========================================================================*/ -static void GobiNetDriverUnbind( - struct usbnet * pDev, - struct usb_interface * pIntf) -{ - sGobiUSBNet * pGobiDev = (sGobiUSBNet *)pDev->data[0]; - - // Should already be down, but just in case... - netif_carrier_off( pDev->net ); - - if (pGobiDev->m_qcrmcall_mode) { - } else { - DeregisterQMIDevice( pGobiDev ); - } - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION( 2,6,29 )) - kfree( pDev->net->netdev_ops ); - pDev->net->netdev_ops = NULL; -#endif - -#if (LINUX_VERSION_CODE <= KERNEL_VERSION( 2,6,23 )) - pIntf->dev.platform_data = NULL; -#endif - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION( 2,6,19 )) - pIntf->needs_remote_wakeup = 0; -#endif - - if (atomic_dec_and_test(&pGobiDev->refcount)) - kfree( pGobiDev ); - else - INFO("memory leak!\n"); -} - -#if 1 //def DATA_MODE_RP - -#if defined(QUECTEL_WWAN_QMAP) -static void _rmnet_usb_rx_handler(struct usbnet *dev, struct sk_buff *skb_in) -{ - sGobiUSBNet * pQmapDev = (sGobiUSBNet *)dev->data[0]; - struct sk_buff *qmap_skb; - struct sk_buff_head skb_chain; - uint dl_minimum_padding = 0; - -#if defined(QUECTEL_UL_DATA_AGG) - if (pQmapDev->qmap_version == 9) - dl_minimum_padding = pQmapDev->agg_ctx.dl_minimum_padding; -#endif - - __skb_queue_head_init(&skb_chain); - - while (skb_in->len > sizeof(struct qmap_hdr)) { - struct rmnet_map_header *map_header = (struct rmnet_map_header *)skb_in->data; - struct rmnet_map_v5_csum_header *ul_header = NULL; - size_t hdr_size = sizeof(struct rmnet_map_header); - struct net_device *qmap_net; - int pkt_len = ntohs(map_header->pkt_len); - int skb_len; - __be16 protocol; - int mux_id; - - if (map_header->next_hdr) { - ul_header = (struct rmnet_map_v5_csum_header *)(map_header + 1); - hdr_size += sizeof(struct rmnet_map_v5_csum_header); - } - - skb_len = pkt_len - (map_header->pad_len&0x3F); - skb_len -= dl_minimum_padding; - if (skb_len > 1500) { - dev_info(&dev->net->dev, "drop skb_len=%x larger than 1500\n", skb_len); - goto error_pkt; - } - - if (skb_in->len < (pkt_len + hdr_size)) { - dev_info(&dev->net->dev, "drop qmap unknow pkt, len=%d, pkt_len=%d\n", skb_in->len, pkt_len); - goto error_pkt; - } - - if (map_header->cd_bit) { - dev_info(&dev->net->dev, "skip qmap command packet\n"); - goto skip_pkt; - } - - switch (skb_in->data[hdr_size] & 0xf0) { - case 0x40: - protocol = htons(ETH_P_IP); - break; - case 0x60: - protocol = htons(ETH_P_IPV6); - break; - default: - dev_info(&dev->net->dev, "unknow skb->protocol %02x\n", skb_in->data[hdr_size]); - goto error_pkt; - } - - mux_id = map_header->mux_id - QUECTEL_QMAP_MUX_ID; - if (mux_id >= pQmapDev->qmap_mode) { - dev_info(&dev->net->dev, "drop qmap unknow mux_id %x\n", map_header->mux_id); - goto error_pkt; - } - - qmap_net = pQmapDev->mpQmapNetDev[mux_id]; - - if (qmap_net == NULL) { - dev_info(&dev->net->dev, "drop qmap unknow mux_id %x\n", map_header->mux_id); - goto skip_pkt; - } - - qmap_skb = netdev_alloc_skb(qmap_net, skb_len); - if (qmap_skb) { - skb_put(qmap_skb, skb_len); - memcpy(qmap_skb->data, skb_in->data + hdr_size, skb_len); - } - - if (qmap_skb == NULL) { - dev_info(&dev->net->dev, "fail to alloc skb, pkt_len = %d\n", skb_len); - goto error_pkt; - } - - skb_reset_transport_header(qmap_skb); - skb_reset_network_header(qmap_skb); - qmap_skb->pkt_type = PACKET_HOST; - skb_set_mac_header(qmap_skb, 0); - qmap_skb->protocol = protocol; - - if (ul_header && ul_header->header_type == RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD - && ul_header->csum_valid_required) { -#if 0 //TODO - qmap_skb->ip_summed = CHECKSUM_UNNECESSARY; -#endif - } - - if (qmap_skb->dev->type == ARPHRD_ETHER) { - skb_push(qmap_skb, ETH_HLEN); - skb_reset_mac_header(qmap_skb); - memcpy(eth_hdr(qmap_skb)->h_source, default_modem_addr, ETH_ALEN); - memcpy(eth_hdr(qmap_skb)->h_dest, qmap_net->dev_addr, ETH_ALEN); - eth_hdr(qmap_skb)->h_proto = protocol; -#ifdef QUECTEL_BRIDGE_MODE - bridge_mode_rx_fixup(pQmapDev, qmap_net, qmap_skb); -#endif - } - - __skb_queue_tail(&skb_chain, qmap_skb); - -skip_pkt: - skb_pull(skb_in, pkt_len + hdr_size); - } - -error_pkt: - while ((qmap_skb = __skb_dequeue (&skb_chain))) { - if (qmap_skb->dev != dev->net) { - if (qmap_skb->dev->type == ARPHRD_ETHER) - __skb_pull(qmap_skb, ETH_HLEN); - rmnet_vnd_update_rx_stats(qmap_skb->dev, 1, qmap_skb->len); - netif_rx(qmap_skb); - } - else { - qmap_skb->protocol = 0; - usbnet_skb_return(dev, qmap_skb); - } - } -} - -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,35 )) //ab95bfe01f9872459c8678572ccadbf646badad0 -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,39 )) //8a4eb5734e8d1dc60a8c28576bbbdfdcc643626d -static struct sk_buff* rmnet_usb_rx_handler(struct sk_buff *skb) -{ - struct usbnet *dev; - - if (!skb) - goto done; - - //printk("%s skb=%p, protocol=%x, len=%d\n", __func__, skb, skb->protocol, skb->len); - - if (skb->pkt_type == PACKET_LOOPBACK) - return skb; - - if (skb->protocol != htons(ETH_P_MAP)) { - WARN_ON(1); - return skb; - } - - dev = netdev_priv(skb->dev); - - if (dev == NULL) { - WARN_ON(1); - return skb; - } - - _rmnet_usb_rx_handler(dev, skb); - consume_skb(skb); - -done: - return NULL; -} -#else -static rx_handler_result_t rmnet_usb_rx_handler(struct sk_buff **pskb) -{ - struct sk_buff *skb = *pskb; - struct usbnet *dev; - - if (!skb) - goto done; - - //printk("%s skb=%p, protocol=%x, len=%d\n", __func__, skb, skb->protocol, skb->len); - - if (skb->pkt_type == PACKET_LOOPBACK) - return RX_HANDLER_PASS; - - if (skb->protocol != htons(ETH_P_MAP)) { - WARN_ON(1); - return RX_HANDLER_PASS; - } - - dev = netdev_priv(skb->dev); - - if (dev == NULL) { - WARN_ON(1); - return RX_HANDLER_PASS; - } - - _rmnet_usb_rx_handler(dev, skb); - consume_skb(skb); - -done: - return RX_HANDLER_CONSUMED; -} -#endif -#endif -#endif -/*=========================================================================== -METHOD: - GobiNetDriverTxFixup (Public Method) - -DESCRIPTION: - Handling data format mode on transmit path - -PARAMETERS - pDev [ I ] - Pointer to usbnet device - pSKB [ I ] - Pointer to transmit packet buffer - flags [ I ] - os flags - -RETURN VALUE: - None -===========================================================================*/ -static struct sk_buff *GobiNetDriverTxFixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) -{ - sGobiUSBNet * pGobiDev = (sGobiUSBNet *)dev->data[0]; - - if (!pGobiDev) { - DBG( "failed to get QMIDevice\n" ); - dev_kfree_skb_any(skb); - return NULL; - } - - if (unlikely(!skb)) { - return NULL; - } - - if (!pGobiDev->mbRawIPMode) - return skb; - -#ifdef QUECTEL_WWAN_QMAP - if (pGobiDev->qmap_mode > 1) { - if (skb->protocol == htons(ETH_P_MAP)) - return skb; - - goto drop_skb; - } - else if (pGobiDev->qmap_mode == 1) { - if (unlikely(!pGobiDev->link_state)) { - dev_info(&dev->net->dev, "link_state 0x%x, drop skb, len = %u\n", pGobiDev->link_state, skb->len); - goto drop_skb; - } - - if (dev->net->type == ARPHRD_ETHER) { -#ifdef QUECTEL_BRIDGE_MODE - if (pGobiDev->m_bridge_mode && bridge_mode_tx_fixup(dev->net, skb, pGobiDev->m_bridge_ipv4, pGobiDev->mHostMAC) == NULL) { - goto drop_skb; - } -#endif - - if (ether_to_ip_fixup(dev->net, skb) == NULL) - goto drop_skb; - } - - if (pGobiDev->qmap_version == 5) { - add_qhdr(skb, QUECTEL_QMAP_MUX_ID); - } - else if (pGobiDev->qmap_version == 9) { - add_qhdr_v5(skb, QUECTEL_QMAP_MUX_ID); - } - else { - goto drop_skb; - } - - return skb; - } -#endif - -#ifdef QUECTEL_BRIDGE_MODE - if (pGobiDev->m_bridge_mode && bridge_mode_tx_fixup(dev->net, skb, pGobiDev->m_bridge_ipv4, pGobiDev->mHostMAC) == NULL) { - goto drop_skb; - } -#endif - - // Skip Ethernet header from message - if (likely(ether_to_ip_fixup(dev->net, skb))) { - return skb; - } - else { -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,22 )) - dev_err(&dev->intf->dev, "Packet Dropped "); -#elif (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,18 )) - dev_err(dev->net->dev.parent, "Packet Dropped "); -#else - INFO("Packet Dropped "); -#endif - } - -#if defined(QUECTEL_WWAN_QMAP) -drop_skb: -#endif -#if (LINUX_VERSION_CODE <= KERNEL_VERSION( 2,6,24 )) && defined(CONFIG_X86_32) - INFO("dev_kfree_skb_any() will make kernel panic on CentOS!\n"); - quec_debug=1;PrintHex(skb->data, 32);quec_debug=0; -#else - // Filter the packet out, release it - dev_kfree_skb_any(skb); -#endif - - return NULL; -} - -#if defined(QUECTEL_WWAN_MULTI_PACKAGES) -static int GobiNetDriverRxPktsFixup(struct usbnet *dev, struct sk_buff *skb) -{ - sGobiUSBNet * pGobiDev = (sGobiUSBNet *)dev->data[0]; - - if (!pGobiDev->mbRawIPMode) - return 1; - - /* This check is no longer done by usbnet */ - if (skb->len < dev->net->hard_header_len) - return 0; - - if (!rx_packets) { - return GobiNetDriverRxFixup(dev, skb); - } - - while (likely(skb->len)) { - struct sk_buff* new_skb; - struct quec_net_package_header package_header; - - if (skb->len < sizeof(package_header)) - return 0; - - memcpy(&package_header, skb->data, sizeof(package_header)); - package_header.payload_len = be16_to_cpu(package_header.payload_len); - - if (package_header.msg_spec != QUEC_NET_MSG_SPEC || package_header.msg_id != QUEC_NET_MSG_ID_IP_DATA) - return 0; - - if (skb->len < (package_header.payload_len + sizeof(package_header))) - return 0; - - skb_pull(skb, sizeof(package_header)); - - if (skb->len == package_header.payload_len) - return GobiNetDriverRxFixup(dev, skb); - - new_skb = skb_clone(skb, GFP_ATOMIC); - if (new_skb) { - skb_trim(new_skb, package_header.payload_len); - if (GobiNetDriverRxFixup(dev, new_skb)) - usbnet_skb_return(dev, new_skb); - else - return 0; - } - - skb_pull(skb, package_header.payload_len); - } - - return 0; -} -#endif - -#ifdef QUECTEL_WWAN_QMAP -static int GobiNetDriverRxQmapFixup(struct usbnet *dev, struct sk_buff *skb) -{ -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,35 )) //ab95bfe01f9872459c8678572ccadbf646badad0 - rx_handler_func_t *rx_handler; - -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,3,1 )) //7bdd402706cf26bfef9050dfee3f229b7f33ee4f - if (skb->dev == NULL) { - skb->dev = dev->net; - } -#endif - rx_handler = rcu_dereference(skb->dev->rx_handler); - - if (rx_handler == rmnet_usb_rx_handler) { -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,3,1 )) //7bdd402706cf26bfef9050dfee3f229b7f33ee4f - unsigned headroom = skb_headroom(skb); - if (headroom < ETH_HLEN) { - unsigned tailroom = skb_tailroom(skb); - if ((tailroom + headroom) >= ETH_HLEN) { - unsigned moveroom = ETH_HLEN - headroom; - memmove(skb->data + moveroom ,skb->data, skb->len); - skb->data += moveroom; - skb->tail += moveroom; - #ifdef WARN_ONCE - WARN_ONCE(1, "It is better reserve headroom in usbnet.c:rx_submit()!\n"); - #endif - } - } -#endif - - if (dev->net->type == ARPHRD_ETHER && skb_headroom(skb) >= ETH_HLEN) { - //usbnet.c rx_process() usbnet_skb_return() eth_type_trans() - skb_push(skb, ETH_HLEN); - skb_reset_mac_header(skb); - memcpy(eth_hdr(skb)->h_source, default_modem_addr, ETH_ALEN); - memcpy(eth_hdr(skb)->h_dest, dev->net->dev_addr, ETH_ALEN); - eth_hdr(skb)->h_proto = htons(ETH_P_MAP); - - return 1; - } - -#ifdef WARN_ONCE - WARN_ONCE(1, "skb_headroom < ETH_HLEN\n"); -#endif - return 0; - } -#endif - - _rmnet_usb_rx_handler(dev, skb); - return 0; -} -#endif -/*=========================================================================== -METHOD: - GobiNetDriverRxFixup (Public Method) - -DESCRIPTION: - Handling data format mode on receive path - -PARAMETERS - pDev [ I ] - Pointer to usbnet device - pSKB [ I ] - Pointer to received packet buffer - -RETURN VALUE: - None -===========================================================================*/ -static int GobiNetDriverRxFixup(struct usbnet *dev, struct sk_buff *skb) -{ - __be16 proto; - sGobiUSBNet * pGobiDev = (sGobiUSBNet *)dev->data[0]; - - if (!pGobiDev->mbRawIPMode) - return 1; - - /* This check is no longer done by usbnet */ - if (skb->len < dev->net->hard_header_len) - return 0; - -#ifdef QUECTEL_WWAN_QMAP - if (pGobiDev->qmap_mode) { - return GobiNetDriverRxQmapFixup(dev, skb); - } -#endif - - switch (skb->data[0] & 0xf0) { - case 0x40: - proto = htons(ETH_P_IP); - break; - case 0x60: - proto = htons(ETH_P_IPV6); - break; - case 0x00: - if (is_multicast_ether_addr(skb->data)) - return 1; - /* possibly bogus destination - rewrite just in case */ - skb_reset_mac_header(skb); - goto fix_dest; - default: - /* pass along other packets without modifications */ - return 1; - } - if (skb_headroom(skb) < ETH_HLEN && pskb_expand_head(skb, ETH_HLEN, 0, GFP_ATOMIC)) { - DBG("%s: couldn't pskb_expand_head\n", __func__); - return 0; - } - skb_push(skb, ETH_HLEN); - skb_reset_mac_header(skb); - eth_hdr(skb)->h_proto = proto; - memcpy(eth_hdr(skb)->h_source, ec20_mac, ETH_ALEN); -fix_dest: -#ifdef QUECTEL_BRIDGE_MODE - bridge_mode_rx_fixup(pGobiDev, dev->net, skb); -#else - memcpy(eth_hdr(skb)->h_dest, dev->net->dev_addr, ETH_ALEN); -#endif - -#ifdef QUECTEL_BRIDGE_MODE -#if 0 - if (pGobiDev->m_bridge_mode) { - struct ethhdr *ehdr = eth_hdr(skb); -quec_debug = 1; - DBG(": "); - PrintHex(ehdr, sizeof(struct ethhdr)); -quec_debug = 0; - } -#endif -#endif - - return 1; -} -#endif - -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,29 )) -#ifdef CONFIG_PM -/*=========================================================================== -METHOD: - GobiUSBNetURBCallback (Public Method) - -DESCRIPTION: - Write is complete, cleanup and signal that we're ready for next packet - -PARAMETERS - pURB [ I ] - Pointer to sAutoPM struct - -RETURN VALUE: - None -===========================================================================*/ -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,18 )) -void GobiUSBNetURBCallback( struct urb * pURB ) -#else -void GobiUSBNetURBCallback(struct urb *pURB, struct pt_regs *regs) -#endif -{ - unsigned long activeURBflags; - sAutoPM * pAutoPM = (sAutoPM *)pURB->context; - if (pAutoPM == NULL) - { - // Should never happen - DBG( "bad context\n" ); - return; - } - - if (pURB->status != 0) - { - // Note that in case of an error, the behaviour is no different - DBG( "urb finished with error %d\n", pURB->status ); - } - - // Remove activeURB (memory to be freed later) - spin_lock_irqsave( &pAutoPM->mActiveURBLock, activeURBflags ); - - // EAGAIN used to signify callback is done - pAutoPM->mpActiveURB = ERR_PTR( -EAGAIN ); - - spin_unlock_irqrestore( &pAutoPM->mActiveURBLock, activeURBflags ); - - complete( &pAutoPM->mThreadDoWork ); - -#ifdef URB_FREE_BUFFER_BY_SELF - if (pURB->transfer_flags & URB_FREE_BUFFER) - kfree(pURB->transfer_buffer); -#endif - usb_free_urb( pURB ); -} - -/*=========================================================================== -METHOD: - GobiUSBNetTXTimeout (Public Method) - -DESCRIPTION: - Timeout declared by the net driver. Stop all transfers - -PARAMETERS - pNet [ I ] - Pointer to net device - -RETURN VALUE: - None -===========================================================================*/ -void GobiUSBNetTXTimeout( struct net_device * pNet ) -{ - struct sGobiUSBNet * pGobiDev; - sAutoPM * pAutoPM; - sURBList * pURBListEntry; - unsigned long activeURBflags, URBListFlags; - struct usbnet * pDev = netdev_priv( pNet ); - struct urb * pURB; - - if (pDev == NULL || pDev->net == NULL) - { - DBG( "failed to get usbnet device\n" ); - return; - } - - pGobiDev = (sGobiUSBNet *)pDev->data[0]; - if (pGobiDev == NULL) - { - DBG( "failed to get QMIDevice\n" ); - return; - } - pAutoPM = &pGobiDev->mAutoPM; - - DBG( "\n" ); - - // Grab a pointer to active URB - spin_lock_irqsave( &pAutoPM->mActiveURBLock, activeURBflags ); - pURB = pAutoPM->mpActiveURB; - spin_unlock_irqrestore( &pAutoPM->mActiveURBLock, activeURBflags ); - // Stop active URB - if (pURB != NULL) - { - usb_kill_urb( pURB ); - } - - // Cleanup URB List - spin_lock_irqsave( &pAutoPM->mURBListLock, URBListFlags ); - - pURBListEntry = pAutoPM->mpURBList; - while (pURBListEntry != NULL) - { - pAutoPM->mpURBList = pAutoPM->mpURBList->mpNext; - atomic_dec( &pAutoPM->mURBListLen ); - usb_free_urb( pURBListEntry->mpURB ); - kfree( pURBListEntry ); - pURBListEntry = pAutoPM->mpURBList; - } - - spin_unlock_irqrestore( &pAutoPM->mURBListLock, URBListFlags ); - - complete( &pAutoPM->mThreadDoWork ); - - return; -} - -/*=========================================================================== -METHOD: - GobiUSBNetAutoPMThread (Public Method) - -DESCRIPTION: - Handle device Auto PM state asynchronously - Handle network packet transmission asynchronously - -PARAMETERS - pData [ I ] - Pointer to sAutoPM struct - -RETURN VALUE: - int - 0 for success - Negative errno for error -===========================================================================*/ -static int GobiUSBNetAutoPMThread( void * pData ) -{ - unsigned long activeURBflags, URBListFlags; - sURBList * pURBListEntry; - int status; - struct usb_device * pUdev; - sAutoPM * pAutoPM = (sAutoPM *)pData; - struct urb * pURB; - - if (pAutoPM == NULL) - { - DBG( "passed null pointer\n" ); - return -EINVAL; - } - - pUdev = interface_to_usbdev( pAutoPM->mpIntf ); - - DBG( "traffic thread started\n" ); - - while (pAutoPM->mbExit == false) - { - // Wait for someone to poke us - wait_for_completion_interruptible( &pAutoPM->mThreadDoWork ); - - // Time to exit? - if (pAutoPM->mbExit == true) - { - // Stop activeURB - spin_lock_irqsave( &pAutoPM->mActiveURBLock, activeURBflags ); - pURB = pAutoPM->mpActiveURB; - spin_unlock_irqrestore( &pAutoPM->mActiveURBLock, activeURBflags ); - - // EAGAIN used to signify callback is done - if (IS_ERR( pAutoPM->mpActiveURB ) - && PTR_ERR( pAutoPM->mpActiveURB ) == -EAGAIN ) - { - pURB = NULL; - } - - if (pURB != NULL) - { - usb_kill_urb( pURB ); - } - // Will be freed in callback function - - // Cleanup URB List - spin_lock_irqsave( &pAutoPM->mURBListLock, URBListFlags ); - - pURBListEntry = pAutoPM->mpURBList; - while (pURBListEntry != NULL) - { - pAutoPM->mpURBList = pAutoPM->mpURBList->mpNext; - atomic_dec( &pAutoPM->mURBListLen ); - usb_free_urb( pURBListEntry->mpURB ); - kfree( pURBListEntry ); - pURBListEntry = pAutoPM->mpURBList; - } - - spin_unlock_irqrestore( &pAutoPM->mURBListLock, URBListFlags ); - - break; - } - - // Is our URB active? - spin_lock_irqsave( &pAutoPM->mActiveURBLock, activeURBflags ); - - // EAGAIN used to signify callback is done - if (IS_ERR( pAutoPM->mpActiveURB ) - && PTR_ERR( pAutoPM->mpActiveURB ) == -EAGAIN ) - { - pAutoPM->mpActiveURB = NULL; - - // Restore IRQs so task can sleep - spin_unlock_irqrestore( &pAutoPM->mActiveURBLock, activeURBflags ); - - // URB is done, decrement the Auto PM usage count - usb_autopm_put_interface( pAutoPM->mpIntf ); - - // Lock ActiveURB again - spin_lock_irqsave( &pAutoPM->mActiveURBLock, activeURBflags ); - } - - if (pAutoPM->mpActiveURB != NULL) - { - // There is already a URB active, go back to sleep - spin_unlock_irqrestore( &pAutoPM->mActiveURBLock, activeURBflags ); - continue; - } - - // Is there a URB waiting to be submitted? - spin_lock_irqsave( &pAutoPM->mURBListLock, URBListFlags ); - if (pAutoPM->mpURBList == NULL) - { - // No more URBs to submit, go back to sleep - spin_unlock_irqrestore( &pAutoPM->mURBListLock, URBListFlags ); - spin_unlock_irqrestore( &pAutoPM->mActiveURBLock, activeURBflags ); - continue; - } - - // Pop an element - pURBListEntry = pAutoPM->mpURBList; - pAutoPM->mpURBList = pAutoPM->mpURBList->mpNext; - atomic_dec( &pAutoPM->mURBListLen ); - spin_unlock_irqrestore( &pAutoPM->mURBListLock, URBListFlags ); - - // Set ActiveURB - pAutoPM->mpActiveURB = pURBListEntry->mpURB; - spin_unlock_irqrestore( &pAutoPM->mActiveURBLock, activeURBflags ); - - // Tell autopm core we need device woken up - status = usb_autopm_get_interface( pAutoPM->mpIntf ); - if (status < 0) - { - DBG( "unable to autoresume interface: %d\n", status ); - - // likely caused by device going from autosuspend -> full suspend - if (status == -EPERM) - { -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,33 )) -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,18 )) - pUdev->auto_pm = 0; -#else - pUdev = pUdev; -#endif -#endif - GobiNetSuspend( pAutoPM->mpIntf, PMSG_SUSPEND ); - } - - // Add pURBListEntry back onto pAutoPM->mpURBList - spin_lock_irqsave( &pAutoPM->mURBListLock, URBListFlags ); - pURBListEntry->mpNext = pAutoPM->mpURBList; - pAutoPM->mpURBList = pURBListEntry; - atomic_inc( &pAutoPM->mURBListLen ); - spin_unlock_irqrestore( &pAutoPM->mURBListLock, URBListFlags ); - - spin_lock_irqsave( &pAutoPM->mActiveURBLock, activeURBflags ); - pAutoPM->mpActiveURB = NULL; - spin_unlock_irqrestore( &pAutoPM->mActiveURBLock, activeURBflags ); - - // Go back to sleep - continue; - } - - // Submit URB - status = usb_submit_urb( pAutoPM->mpActiveURB, GFP_KERNEL ); - if (status < 0) - { - // Could happen for a number of reasons - DBG( "Failed to submit URB: %d. Packet dropped\n", status ); - spin_lock_irqsave( &pAutoPM->mActiveURBLock, activeURBflags ); - usb_free_urb( pAutoPM->mpActiveURB ); - pAutoPM->mpActiveURB = NULL; - spin_unlock_irqrestore( &pAutoPM->mActiveURBLock, activeURBflags ); - usb_autopm_put_interface( pAutoPM->mpIntf ); - - // Loop again - complete( &pAutoPM->mThreadDoWork ); - } - - kfree( pURBListEntry ); - } - - DBG( "traffic thread exiting\n" ); - pAutoPM->mpThread = NULL; - return 0; -} - -/*=========================================================================== -METHOD: - GobiUSBNetStartXmit (Public Method) - -DESCRIPTION: - Convert sk_buff to usb URB and queue for transmit - -PARAMETERS - pNet [ I ] - Pointer to net device - -RETURN VALUE: - NETDEV_TX_OK on success - NETDEV_TX_BUSY on error -===========================================================================*/ -int GobiUSBNetStartXmit( - struct sk_buff * pSKB, - struct net_device * pNet ) -{ - unsigned long URBListFlags; - struct sGobiUSBNet * pGobiDev; - sAutoPM * pAutoPM; - sURBList * pURBListEntry, ** ppURBListEnd; - void * pURBData; - struct usbnet * pDev = netdev_priv( pNet ); - - //DBG( "\n" ); - - if (pDev == NULL || pDev->net == NULL) - { - DBG( "failed to get usbnet device\n" ); - return NETDEV_TX_BUSY; - } - - pGobiDev = (sGobiUSBNet *)pDev->data[0]; - if (pGobiDev == NULL) - { - DBG( "failed to get QMIDevice\n" ); - return NETDEV_TX_BUSY; - } - pAutoPM = &pGobiDev->mAutoPM; - - if( NULL == pSKB ) - { - DBG( "Buffer is NULL \n" ); - return NETDEV_TX_BUSY; - } - - if (GobiTestDownReason( pGobiDev, DRIVER_SUSPENDED )) - { - // Should not happen - DBG( "device is suspended\n" ); - dump_stack(); - return NETDEV_TX_BUSY; - } - - if (GobiTestDownReason( pGobiDev, NO_NDIS_CONNECTION )) - { - //netif_carrier_off( pGobiDev->mpNetDev->net ); - //DBG( "device is disconnected\n" ); - //dump_stack(); - return NETDEV_TX_BUSY; - } - - // Convert the sk_buff into a URB - - // Check if buffer is full - if ( atomic_read( &pAutoPM->mURBListLen ) >= txQueueLength) - { - DBG( "not scheduling request, buffer is full\n" ); - return NETDEV_TX_BUSY; - } - - // Allocate URBListEntry - pURBListEntry = kmalloc( sizeof( sURBList ), GFP_ATOMIC ); - if (pURBListEntry == NULL) - { - DBG( "unable to allocate URBList memory\n" ); - return NETDEV_TX_BUSY; - } - pURBListEntry->mpNext = NULL; - - // Allocate URB - pURBListEntry->mpURB = usb_alloc_urb( 0, GFP_ATOMIC ); - if (pURBListEntry->mpURB == NULL) - { - DBG( "unable to allocate URB\n" ); - // release all memory allocated by now - if (pURBListEntry) - kfree( pURBListEntry ); - return NETDEV_TX_BUSY; - } - -#if 1 //def DATA_MODE_RP - GobiNetDriverTxFixup(pDev, pSKB, GFP_ATOMIC); -#endif - - // Allocate URB transfer_buffer - pURBData = kmalloc( pSKB->len, GFP_ATOMIC ); - if (pURBData == NULL) - { - DBG( "unable to allocate URB data\n" ); - // release all memory allocated by now - if (pURBListEntry) - { - usb_free_urb( pURBListEntry->mpURB ); - kfree( pURBListEntry ); - } - return NETDEV_TX_BUSY; - } - // Fill with SKB's data - memcpy( pURBData, pSKB->data, pSKB->len ); - - usb_fill_bulk_urb( pURBListEntry->mpURB, - pGobiDev->mpNetDev->udev, - pGobiDev->mpNetDev->out, - pURBData, - pSKB->len, - GobiUSBNetURBCallback, - pAutoPM ); - - /* Handle the need to send a zero length packet and release the - * transfer buffer - */ - pURBListEntry->mpURB->transfer_flags |= (URB_ZERO_PACKET | URB_FREE_BUFFER); - - // Aquire lock on URBList - spin_lock_irqsave( &pAutoPM->mURBListLock, URBListFlags ); - - // Add URB to end of list - ppURBListEnd = &pAutoPM->mpURBList; - while ((*ppURBListEnd) != NULL) - { - ppURBListEnd = &(*ppURBListEnd)->mpNext; - } - *ppURBListEnd = pURBListEntry; - atomic_inc( &pAutoPM->mURBListLen ); - - spin_unlock_irqrestore( &pAutoPM->mURBListLock, URBListFlags ); - - complete( &pAutoPM->mThreadDoWork ); - - // Start transfer timer - pNet->trans_start = jiffies; - // Free SKB - if (pSKB) - dev_kfree_skb_any( pSKB ); - - return NETDEV_TX_OK; -} -#endif -static int (*local_usbnet_start_xmit) (struct sk_buff *skb, struct net_device *net); -#endif - -static int GobiUSBNetStartXmit2( struct sk_buff *pSKB, struct net_device *pNet ){ - struct sGobiUSBNet * pGobiDev; - struct usbnet * pDev = netdev_priv( pNet ); - - //DBG( "\n" ); - - if (pDev == NULL || pDev->net == NULL) - { - DBG( "failed to get usbnet device\n" ); - return NETDEV_TX_BUSY; - } - - pGobiDev = (sGobiUSBNet *)pDev->data[0]; - if (pGobiDev == NULL) - { - DBG( "failed to get QMIDevice\n" ); - return NETDEV_TX_BUSY; - } - - if( NULL == pSKB ) - { - DBG( "Buffer is NULL \n" ); - return NETDEV_TX_BUSY; - } - - if (GobiTestDownReason( pGobiDev, DRIVER_SUSPENDED )) - { - // Should not happen - DBG( "device is suspended\n" ); - dump_stack(); - return NETDEV_TX_BUSY; - } - - if (GobiTestDownReason( pGobiDev, NO_NDIS_CONNECTION )) - { - //netif_carrier_off( pGobiDev->mpNetDev->net ); - //DBG( "device is disconnected\n" ); - //dump_stack(); - return NETDEV_TX_BUSY; - } - -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,29 )) - return local_usbnet_start_xmit(pSKB, pNet); -#else - return usbnet_start_xmit(pSKB, pNet); -#endif -} - -/*=========================================================================== -METHOD: - GobiUSBNetOpen (Public Method) - -DESCRIPTION: - Wrapper to usbnet_open, correctly handling autosuspend - Start AutoPM thread (if CONFIG_PM is defined) - -PARAMETERS - pNet [ I ] - Pointer to net device - -RETURN VALUE: - int - 0 for success - Negative errno for error -===========================================================================*/ -static int GobiUSBNetOpen( struct net_device * pNet ) -{ - int status = 0; - struct sGobiUSBNet * pGobiDev; - struct usbnet * pDev = netdev_priv( pNet ); - - if (pDev == NULL) - { - DBG( "failed to get usbnet device\n" ); - return -ENXIO; - } - - pGobiDev = (sGobiUSBNet *)pDev->data[0]; - if (pGobiDev == NULL) - { - DBG( "failed to get QMIDevice\n" ); - return -ENXIO; - } - - DBG( "\n" ); - -#ifdef CONFIG_PM - #if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,29 )) - // Start the AutoPM thread - pGobiDev->mAutoPM.mpIntf = pGobiDev->mpIntf; - pGobiDev->mAutoPM.mbExit = false; - pGobiDev->mAutoPM.mpURBList = NULL; - pGobiDev->mAutoPM.mpActiveURB = NULL; - spin_lock_init( &pGobiDev->mAutoPM.mURBListLock ); - spin_lock_init( &pGobiDev->mAutoPM.mActiveURBLock ); - atomic_set( &pGobiDev->mAutoPM.mURBListLen, 0 ); - init_completion( &pGobiDev->mAutoPM.mThreadDoWork ); - - pGobiDev->mAutoPM.mpThread = kthread_run( GobiUSBNetAutoPMThread, - &pGobiDev->mAutoPM, - "GobiUSBNetAutoPMThread" ); - if (IS_ERR( pGobiDev->mAutoPM.mpThread )) - { - DBG( "AutoPM thread creation error\n" ); - return PTR_ERR( pGobiDev->mAutoPM.mpThread ); - } - #endif -#endif /* CONFIG_PM */ - - // Allow traffic - GobiClearDownReason( pGobiDev, NET_IFACE_STOPPED ); - - // Pass to usbnet_open if defined - if (pGobiDev->mpUSBNetOpen != NULL) - { - status = pGobiDev->mpUSBNetOpen( pNet ); -#ifdef CONFIG_PM - // If usbnet_open was successful enable Auto PM - if (status == 0) - { -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,33 )) - usb_autopm_enable( pGobiDev->mpIntf ); -#else - usb_autopm_put_interface( pGobiDev->mpIntf ); -#endif - } -#endif /* CONFIG_PM */ - } - else - { - DBG( "no USBNetOpen defined\n" ); - } - - return status; -} - -/*=========================================================================== -METHOD: - GobiUSBNetStop (Public Method) - -DESCRIPTION: - Wrapper to usbnet_stop, correctly handling autosuspend - Stop AutoPM thread (if CONFIG_PM is defined) - -PARAMETERS - pNet [ I ] - Pointer to net device - -RETURN VALUE: - int - 0 for success - Negative errno for error -===========================================================================*/ -static int GobiUSBNetStop( struct net_device * pNet ) -{ - struct sGobiUSBNet * pGobiDev; - struct usbnet * pDev = netdev_priv( pNet ); - - if (pDev == NULL || pDev->net == NULL) - { - DBG( "failed to get netdevice\n" ); - return -ENXIO; - } - - pGobiDev = (sGobiUSBNet *)pDev->data[0]; - if (pGobiDev == NULL) - { - DBG( "failed to get QMIDevice\n" ); - return -ENXIO; - } - - // Stop traffic - GobiSetDownReason( pGobiDev, NET_IFACE_STOPPED ); - -#ifdef CONFIG_PM - #if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,29 )) - // Tell traffic thread to exit - pGobiDev->mAutoPM.mbExit = true; - complete( &pGobiDev->mAutoPM.mThreadDoWork ); - - // Wait for it to exit - while( pGobiDev->mAutoPM.mpThread != NULL ) - { - msleep( 100 ); - } - DBG( "thread stopped\n" ); - #endif -#endif /* CONFIG_PM */ - - // Pass to usbnet_stop, if defined - if (pGobiDev->mpUSBNetStop != NULL) - { - return pGobiDev->mpUSBNetStop( pNet ); - } - else - { - return 0; - } -} - -static int GobiNetDriver_check_connect(struct usbnet *pDev) { - int status = 0; - struct sGobiUSBNet * pGobiDev = NULL; - - while (status++ < 10) { - pGobiDev = (sGobiUSBNet *)pDev->data[0]; - if (pGobiDev && pGobiDev->mbProbeDone) - break; - msleep(1); - } - - return 0; -} - -/*=========================================================================*/ -// Struct driver_info -/*=========================================================================*/ -static struct driver_info GobiNetInfo = -{ - .description = "GobiNet Ethernet Device", -#if 1//def CONFIG_ANDROID -#if defined(QUECTEL_WWAN_QMAP) && defined(FLAG_RX_ASSEMBLE) - .flags = FLAG_RX_ASSEMBLE, //usb0 -#endif -#else -#if defined(QUECTEL_WWAN_QMAP) && defined(FLAG_RX_ASSEMBLE) - .flags = FLAG_ETHER | FLAG_RX_ASSEMBLE, -#else - .flags = FLAG_ETHER, -#endif -#endif - .bind = GobiNetDriverBind, - .unbind = GobiNetDriverUnbind, -#if 1 //def DATA_MODE_RP -#if defined(QUECTEL_WWAN_MULTI_PACKAGES) - .rx_fixup = GobiNetDriverRxPktsFixup, -#else - .rx_fixup = GobiNetDriverRxFixup, -#endif - .tx_fixup = GobiNetDriverTxFixup, -#endif - .check_connect = GobiNetDriver_check_connect, - .data = (1 << 4), -}; - -/*=========================================================================*/ -// Qualcomm Gobi 3000 VID/PIDs -/*=========================================================================*/ -#define GOBI_FIXED_INTF(vend, prod) \ - { \ - USB_DEVICE( vend, prod ), \ - .driver_info = (unsigned long)&GobiNetInfo, \ - } -static const struct usb_device_id QuecGobiVIDPIDTable [] = -{ - GOBI_FIXED_INTF( 0x05c6, 0x9003 ), // Quectel UC20 - GOBI_FIXED_INTF( 0x05c6, 0x9215 ), // Quectel EC20 (MDM9215) - GOBI_FIXED_INTF( 0x2c7c, 0x0125 ), // Quectel EC20 (MDM9X07)/EC25/EG25 - GOBI_FIXED_INTF( 0x2c7c, 0x0121 ), // Quectel EC21 - GOBI_FIXED_INTF( 0x2c7c, 0x0306 ), // Quectel EP06 - GOBI_FIXED_INTF( 0x2c7c, 0x030B ), // Quectel EG065K,SDX12 - GOBI_FIXED_INTF( 0x2c7c, 0x0435 ), // Quectel AG35 - GOBI_FIXED_INTF( 0x2c7c, 0x0296 ), // Quectel BG96 - GOBI_FIXED_INTF( 0x2c7c, 0x0191 ), // Quectel EG91 - GOBI_FIXED_INTF( 0x2c7c, 0x0195 ), // Quectel EG95 - GOBI_FIXED_INTF( 0x2c7c, 0x0512 ), // Quectel EG12/EP12/EM12/EG16/EG18,SDx20 - GOBI_FIXED_INTF( 0x2c7c, 0x0620 ), // Quectel EG20,SDx24 - GOBI_FIXED_INTF( 0x2c7c, 0x0800 ), // Quectel RG500Q,RM500Q,RM510Q,SDX55 - GOBI_FIXED_INTF( 0x2c7c, 0x0801 ), // Quectel RG520Q,RM520Q,SG520Q,SDX6X - //Terminating entry - { } -}; - -MODULE_DEVICE_TABLE( usb, QuecGobiVIDPIDTable ); - -/*=========================================================================== -METHOD: - GobiUSBNetProbe (Public Method) - -DESCRIPTION: - Run usbnet_probe - Setup QMI device - -PARAMETERS - pIntf [ I ] - Pointer to interface - pVIDPIDs [ I ] - Pointer to VID/PID table - -RETURN VALUE: - int - 0 for success - Negative errno for error -===========================================================================*/ -static int GobiUSBNetProbe( - struct usb_interface * pIntf, - const struct usb_device_id * pVIDPIDs ) -{ - int status; - struct usbnet * pDev; - sGobiUSBNet * pGobiDev; -#if (LINUX_VERSION_CODE >= KERNEL_VERSION( 2,6,29 )) - struct net_device_ops * pNetDevOps; -#endif - - status = usbnet_probe( pIntf, pVIDPIDs ); - if (status < 0) - { - DBG( "usbnet_probe failed %d\n", status ); - return status; - } - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION( 2,6,19 )) - pIntf->needs_remote_wakeup = 1; -#endif - -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,23 )) - pDev = usb_get_intfdata( pIntf ); -#else - pDev = (struct usbnet *)pIntf->dev.platform_data; -#endif - - if (pDev == NULL || pDev->net == NULL) - { - DBG( "failed to get netdevice\n" ); - usbnet_disconnect( pIntf ); - return -ENXIO; - } - - pGobiDev = kzalloc( sizeof( sGobiUSBNet ), GFP_KERNEL ); - if (pGobiDev == NULL) - { - DBG( "fail to allocate device buffers" ); - usbnet_disconnect( pIntf ); - return -ENOMEM; - } - - atomic_set(&pGobiDev->refcount, 1); - - pDev->data[0] = (unsigned long)pGobiDev; - - pGobiDev->mpNetDev = pDev; - - // Clearing endpoint halt is a magic handshake that brings - // the device out of low power (airplane) mode - usb_clear_halt( pGobiDev->mpNetDev->udev, pDev->out ); - - // Overload PM related network functions -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,29 )) - pGobiDev->mpUSBNetOpen = pDev->net->open; - pDev->net->open = GobiUSBNetOpen; - pGobiDev->mpUSBNetStop = pDev->net->stop; - pDev->net->stop = GobiUSBNetStop; -#if defined(CONFIG_PM) && (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,14 )) - pDev->net->hard_start_xmit = GobiUSBNetStartXmit; - pDev->net->tx_timeout = GobiUSBNetTXTimeout; -#else //quectel donot send dhcp request before ndis connect for uc20 - local_usbnet_start_xmit = pDev->net->hard_start_xmit; - pDev->net->hard_start_xmit = GobiUSBNetStartXmit2; -#endif -#else - pNetDevOps = kmalloc( sizeof( struct net_device_ops ), GFP_KERNEL ); - if (pNetDevOps == NULL) - { - DBG( "falied to allocate net device ops" ); - usbnet_disconnect( pIntf ); - return -ENOMEM; - } - memcpy( pNetDevOps, pDev->net->netdev_ops, sizeof( struct net_device_ops ) ); - - pGobiDev->mpUSBNetOpen = pNetDevOps->ndo_open; - pNetDevOps->ndo_open = GobiUSBNetOpen; - pGobiDev->mpUSBNetStop = pNetDevOps->ndo_stop; - pNetDevOps->ndo_stop = GobiUSBNetStop; -#if 1 //quectel donot send dhcp request before ndis connect for uc20 - pNetDevOps->ndo_start_xmit = GobiUSBNetStartXmit2; -#else - pNetDevOps->ndo_start_xmit = usbnet_start_xmit; -#endif - pNetDevOps->ndo_tx_timeout = usbnet_tx_timeout; - -#if defined(QUECTEL_WWAN_QMAP) - pNetDevOps->ndo_do_ioctl = qmap_ndo_do_ioctl; -#endif - - pDev->net->netdev_ops = pNetDevOps; -#endif - -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,31 )) - memset( &(pGobiDev->mpNetDev->stats), 0, sizeof( struct net_device_stats ) ); -#else - memset( &(pGobiDev->mpNetDev->net->stats), 0, sizeof( struct net_device_stats ) ); -#endif - - pGobiDev->mpIntf = pIntf; - memset( &(pGobiDev->mMEID), '0', 14 ); - - DBG( "Mac Address:\n" ); - PrintHex( &pGobiDev->mpNetDev->net->dev_addr[0], 6 ); - - pGobiDev->mbQMIValid = false; - memset( &pGobiDev->mQMIDev, 0, sizeof( sQMIDev ) ); - pGobiDev->mQMIDev.mbCdevIsInitialized = false; - - pGobiDev->mQMIDev.mpDevClass = gpClass; - -#ifdef CONFIG_PM - #if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,29 )) - init_completion( &pGobiDev->mAutoPM.mThreadDoWork ); - #endif -#endif /* CONFIG_PM */ - spin_lock_init( &pGobiDev->mQMIDev.mClientMemLock ); - - // Default to device down - pGobiDev->mDownReason = 0; - -//#if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,11,0 )) - GobiSetDownReason( pGobiDev, NO_NDIS_CONNECTION ); - GobiSetDownReason( pGobiDev, NET_IFACE_STOPPED ); -//#endif - - // Register QMI - pGobiDev->mbMdm9x07 |= (pDev->udev->descriptor.idVendor == cpu_to_le16(0x2c7c)); - pGobiDev->mbMdm9x06 |= (pDev->udev->descriptor.idVendor == cpu_to_le16(0x2c7c) && pDev->udev->descriptor.idProduct == cpu_to_le16(0x0296)); - pGobiDev->mbRawIPMode = pGobiDev->mbMdm9x07; - if ( pGobiDev->mbRawIPMode) - pGobiDev->mpNetDev->net->flags |= IFF_NOARP; -#ifdef QUECTEL_BRIDGE_MODE - memcpy(pGobiDev->mHostMAC, pDev->net->dev_addr, 6); - pGobiDev->m_bridge_mode = bridge_mode; -#endif - -#ifdef QUECTEL_REMOVE_TX_ZLP - { - struct remove_tx_zlp_config { - __le32 enable; - } __packed; - - struct remove_tx_zlp_config cfg; - cfg.enable = cpu_to_le32(1); //1-enable 0-disable - - usb_control_msg( - interface_to_usbdev(pIntf), - usb_sndctrlpipe(interface_to_usbdev(pIntf), 0), - USB_CDC_SET_REMOVE_TX_ZLP_COMMAND, - 0x21, //USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE - 0, - pIntf->cur_altsetting->desc.bInterfaceNumber, - &cfg, sizeof(cfg), 100); - } -#endif - - pGobiDev->m_qcrmcall_mode = qcrmcall_mode; - - if (pGobiDev->m_qcrmcall_mode) { - INFO("AT$QCRMCALL MODE!"); - - GobiClearDownReason( pGobiDev, NO_NDIS_CONNECTION ); - usb_control_msg( - interface_to_usbdev(pIntf), - usb_sndctrlpipe(interface_to_usbdev(pIntf), 0), - 0x22, //USB_CDC_REQ_SET_CONTROL_LINE_STATE - 0x21, //USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE - 1, //active CDC DTR - pIntf->cur_altsetting->desc.bInterfaceNumber, - NULL, 0, 100); - status = 0; - } - else { -#if defined(QUECTEL_WWAN_QMAP) - if (pGobiDev->mbRawIPMode) { - unsigned idProduct = le16_to_cpu(pDev->udev->descriptor.idProduct); - - pGobiDev->qmap_mode = qmap_mode; - if (pGobiDev->qmap_mode == 0) { - if (idProduct == 0x0800 || idProduct == 0x0801) { - pGobiDev->qmap_mode = 1; - } - } - - pGobiDev->qmap_version = 5; - if (idProduct == 0x0800 || idProduct == 0x0801) { - pGobiDev->qmap_version = 9; - } - } - - if (pGobiDev->qmap_mode) { - netif_carrier_off(pDev->net); - } - - if (pGobiDev->qmap_mode > 1) { -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,35 )) //ab95bfe01f9872459c8678572ccadbf646badad0 - rtnl_lock(); - netdev_rx_handler_register(pDev->net, rmnet_usb_rx_handler, NULL); - rtnl_unlock(); -#endif - } - -#if defined(QUECTEL_UL_DATA_AGG) - if (pGobiDev->qmap_mode) { - struct ul_agg_ctx *agg_ctx = &pGobiDev->agg_ctx; - - agg_ctx->ul_data_aggregation_max_datagrams = 1; - agg_ctx->ul_data_aggregation_max_size = 2048; - agg_ctx->dl_minimum_padding = 0; - } -#endif -#endif - status = RegisterQMIDevice( pGobiDev ); - } - - if (status != 0) - { - // usbnet_disconnect() will call GobiNetDriverUnbind() which will call - // DeregisterQMIDevice() to clean up any partially created QMI device - usbnet_disconnect( pIntf ); - return status; - } - -#if defined(QUECTEL_WWAN_QMAP) - tasklet_init(&pGobiDev->txq, rmnet_usb_tx_wake_queue, (unsigned long)pGobiDev); - - if (pGobiDev->qmap_mode > 1) { - unsigned i; - - for (i = 0; i < pGobiDev->qmap_mode; i++) { - qmap_register_device(pGobiDev, i); - } - } else { - pGobiDev->mpQmapNetDev[0] = pDev->net; - } -#endif - - pGobiDev->mbProbeDone = 1; - // Success - return 0; -} - -static void GobiUSBNetDisconnect (struct usb_interface *intf) { -#if defined(QUECTEL_WWAN_QMAP) - struct usbnet *pDev = usb_get_intfdata(intf); - sGobiUSBNet * pGobiDev = (sGobiUSBNet *)pDev->data[0]; - unsigned i; - - if (pGobiDev->qmap_mode > 1) { - for (i = 0; i < pGobiDev->qmap_mode; i++) { - qmap_unregister_device(pGobiDev, i); - } - - } - - tasklet_kill(&pGobiDev->txq); -#endif - - usbnet_disconnect(intf); -} - -static struct usb_driver GobiNet = -{ - .name = "GobiNet", - .id_table = QuecGobiVIDPIDTable, - .probe = GobiUSBNetProbe, - .disconnect = GobiUSBNetDisconnect, -#ifdef CONFIG_PM - .suspend = GobiNetSuspend, - .resume = GobiNetResume, -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,27 )) - .reset_resume = GobiNetResetResume, -#endif -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,18 )) - .supports_autosuspend = true, -#endif -#endif /* CONFIG_PM */ -}; - -/*=========================================================================== -METHOD: - GobiUSBNetModInit (Public Method) - -DESCRIPTION: - Initialize module - Create device class - Register out usb_driver struct - -RETURN VALUE: - int - 0 for success - Negative errno for error -===========================================================================*/ -static int __init GobiUSBNetModInit( void ) -{ - gpClass = class_create( THIS_MODULE, "GobiQMI" ); - if (IS_ERR( gpClass ) == true) - { - DBG( "error at class_create %ld\n", PTR_ERR( gpClass ) ); - return -ENOMEM; - } - - // This will be shown whenever driver is loaded - printk( KERN_INFO "%s: %s\n", DRIVER_DESC, DRIVER_VERSION ); - - return usb_register( &GobiNet ); -} -module_init( GobiUSBNetModInit ); - -/*=========================================================================== -METHOD: - GobiUSBNetModExit (Public Method) - -DESCRIPTION: - Deregister module - Destroy device class - -RETURN VALUE: - void -===========================================================================*/ -static void __exit GobiUSBNetModExit( void ) -{ - usb_deregister( &GobiNet ); - - class_destroy( gpClass ); -} -module_exit( GobiUSBNetModExit ); - -MODULE_VERSION( DRIVER_VERSION ); -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); -MODULE_LICENSE("Dual BSD/GPL"); - -#ifdef bool -#undef bool -#endif - -module_param_named( debug, quec_debug, int, S_IRUGO | S_IWUSR ); -MODULE_PARM_DESC( debug, "Debuging enabled or not" ); - -//module_param_named( interruptible, Quecinterruptible, int, S_IRUGO | S_IWUSR ); -//MODULE_PARM_DESC( interruptible, "Listen for and return on user interrupt" ); -module_param( txQueueLength, int, S_IRUGO | S_IWUSR ); -MODULE_PARM_DESC( txQueueLength, - "Number of IP packets which may be queued up for transmit" ); - diff --git a/package/wwan/driver/quectel_Gobinet/src/Makefile b/package/wwan/driver/quectel_Gobinet/src/Makefile deleted file mode 100644 index 7f0deb0e9..000000000 --- a/package/wwan/driver/quectel_Gobinet/src/Makefile +++ /dev/null @@ -1,43 +0,0 @@ -obj-m := GobiNet.o -GobiNet-objs := GobiUSBNet.o QMIDevice.o QMI.o - -PWD := $(shell pwd) -OUTPUTDIR=/lib/modules/`uname -r`/kernel/drivers/net/usb/ - -ifeq ($(ARCH),) -ARCH := $(shell uname -m) -endif -ifeq ($(CROSS_COMPILE),) -CROSS_COMPILE := -endif -ifeq ($(KDIR),) -KDIR := /lib/modules/$(shell uname -r)/build -ifeq ($(ARCH),i686) -ifeq ($(wildcard $KDIR/arch/$ARCH),) -ARCH=i386 -endif -endif -endif - -$(shell rm -rf usbnet.h) -ifneq ($(wildcard $(KDIR)/drivers/usb/net/usbnet.h),) -$(shell ln -s $(KDIR)/drivers/usb/net/usbnet.h usbnet.h) -endif -ifneq ($(wildcard $(KDIR)/drivers/net/usb/usbnet.h),) -$(shell ln -s $(KDIR)/drivers/net/usb/usbnet.h usbnet.h) -endif - -default: - ln -sf makefile Makefile - $(MAKE) ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE} -C $(KDIR) M=$(PWD) modules - -install: default - mkdir -p $(OUTPUTDIR) - cp -f GobiNet.ko $(OUTPUTDIR) - depmod - modprobe -r GobiNet - modprobe GobiNet - -clean: - rm -rf Makefile usbnet.h - rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module.* modules.order diff --git a/package/wwan/driver/quectel_Gobinet/src/QMI.c b/package/wwan/driver/quectel_Gobinet/src/QMI.c deleted file mode 100644 index 644699b1a..000000000 --- a/package/wwan/driver/quectel_Gobinet/src/QMI.c +++ /dev/null @@ -1,1521 +0,0 @@ -#ifdef __QUEC_INCLUDE_QMI_C__ -/*=========================================================================== -FILE: - QMI.c - -DESCRIPTION: - Qualcomm QMI driver code - -FUNCTIONS: - Generic QMUX functions - ParseQMUX - FillQMUX - - Generic QMI functions - GetTLV - ValidQMIMessage - GetQMIMessageID - - Fill Buffers with QMI requests - QMICTLGetClientIDReq - QMICTLReleaseClientIDReq - QMICTLReadyReq - QMIWDSSetEventReportReq - QMIWDSGetPKGSRVCStatusReq - QMIDMSGetMEIDReq - QMIWDASetDataFormatReq - QMICTLSetDataFormatReq - QMICTLSyncReq - - Parse data from QMI responses - QMICTLGetClientIDResp - QMICTLReleaseClientIDResp - QMIWDSEventResp - QMIDMSGetMEIDResp - QMIWDASetDataFormatResp - QMICTLSyncResp - -Copyright (c) 2011, Code Aurora Forum. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Code Aurora Forum nor - the names of its contributors may be used to endorse or promote - products derived from this software without specific prior written - permission. - - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -===========================================================================*/ - -//--------------------------------------------------------------------------- -// Include Files -//--------------------------------------------------------------------------- -#include -#include -#include "Structs.h" -#include "QMI.h" - -/*=========================================================================*/ -// Get sizes of buffers needed by QMI requests -/*=========================================================================*/ - -/*=========================================================================== -METHOD: - QMUXHeaderSize (Public Method) - -DESCRIPTION: - Get size of buffer needed for QMUX - -RETURN VALUE: - u16 - size of buffer -===========================================================================*/ -static u16 QMUXHeaderSize( void ) -{ - return sizeof( sQMUX ); -} - -/*=========================================================================== -METHOD: - QMICTLGetClientIDReqSize (Public Method) - -DESCRIPTION: - Get size of buffer needed for QMUX + QMICTLGetClientIDReq - -RETURN VALUE: - u16 - size of buffer -===========================================================================*/ -static u16 QMICTLGetClientIDReqSize( void ) -{ - return sizeof( sQMUX ) + 10; -} - -/*=========================================================================== -METHOD: - QMICTLReleaseClientIDReqSize (Public Method) - -DESCRIPTION: - Get size of buffer needed for QMUX + QMICTLReleaseClientIDReq - -RETURN VALUE: - u16 - size of header -===========================================================================*/ -static u16 QMICTLReleaseClientIDReqSize( void ) -{ - return sizeof( sQMUX ) + 11; -} - -/*=========================================================================== -METHOD: - QMICTLReadyReqSize (Public Method) - -DESCRIPTION: - Get size of buffer needed for QMUX + QMICTLReadyReq - -RETURN VALUE: - u16 - size of buffer -===========================================================================*/ -static u16 QMICTLReadyReqSize( void ) -{ - return sizeof( sQMUX ) + 6; -} - -/*=========================================================================== -METHOD: - QMIWDSSetEventReportReqSize (Public Method) - -DESCRIPTION: - Get size of buffer needed for QMUX + QMIWDSSetEventReportReq - -RETURN VALUE: - u16 - size of buffer -===========================================================================*/ -static u16 QMIWDSSetEventReportReqSize( void ) -{ - return sizeof( sQMUX ) + 15; -} - -/*=========================================================================== -METHOD: - QMIWDSGetPKGSRVCStatusReqSize (Public Method) - -DESCRIPTION: - Get size of buffer needed for QMUX + QMIWDSGetPKGSRVCStatusReq - -RETURN VALUE: - u16 - size of buffer -===========================================================================*/ -static u16 QMIWDSGetPKGSRVCStatusReqSize( void ) -{ - return sizeof( sQMUX ) + 7; -} - -/*=========================================================================== -METHOD: - QMIDMSGetMEIDReqSize (Public Method) - -DESCRIPTION: - Get size of buffer needed for QMUX + QMIDMSGetMEIDReq - -RETURN VALUE: - u16 - size of buffer -===========================================================================*/ -static u16 QMIDMSGetMEIDReqSize( void ) -{ - return sizeof( sQMUX ) + 7; -} - -struct QMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS -{ - u8 TLVType; - u16 TLVLength; - u8 QOSSetting; -} __packed; - -struct QMIWDS_ADMIN_SET_DATA_FORMAT_TLV -{ - u8 TLVType; - u16 TLVLength; - u32 Value; -} __packed; - -struct QMIWDS_ENDPOINT_TLV -{ - u8 TLVType; - u16 TLVLength; - u32 ep_type; - u32 iface_id; -} __packed; - -struct QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG -{ - u8 CtlFlags; // 0: single QMUX Msg; 1: - u16 TransactionId; - u16 Type; - u16 Length; - struct QMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS QosDataFormatTlv; - struct QMIWDS_ADMIN_SET_DATA_FORMAT_TLV UnderlyingLinkLayerProtocolTlv; - struct QMIWDS_ADMIN_SET_DATA_FORMAT_TLV UplinkDataAggregationProtocolTlv; - struct QMIWDS_ADMIN_SET_DATA_FORMAT_TLV DownlinkDataAggregationProtocolTlv; - struct QMIWDS_ADMIN_SET_DATA_FORMAT_TLV DownlinkDataAggregationMaxDatagramsTlv; - struct QMIWDS_ADMIN_SET_DATA_FORMAT_TLV DownlinkDataAggregationMaxSizeTlv; - struct QMIWDS_ENDPOINT_TLV epTlv; - struct QMIWDS_ADMIN_SET_DATA_FORMAT_TLV dl_minimum_padding; - struct QMIWDS_ADMIN_SET_DATA_FORMAT_TLV UplinkDataAggregationMaxDatagramsTlv; - struct QMIWDS_ADMIN_SET_DATA_FORMAT_TLV UplinkDataAggregationMaxSizeTlv; -} __packed; - -/*=========================================================================== -METHOD: - QMIWDASetDataFormatReqSize (Public Method) - -DESCRIPTION: - Get size of buffer needed for QMUX + QMIWDASetDataFormatReq - -RETURN VALUE: - u16 - size of buffer -===========================================================================*/ -static u16 QMIWDASetDataFormatReqSize( int qmap_version ) -{ -if (qmap_version) - return sizeof( sQMUX ) + sizeof(struct QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG); -else - return sizeof( sQMUX ) + 18; -} - -/*=========================================================================== -METHOD: - QMICTLSyncReqSize (Public Method) - -DESCRIPTION: - Get size of buffer needed for QMUX + QMICTLSyncReq - -RETURN VALUE: - u16 - size of buffer -===========================================================================*/ -static u16 QMICTLSyncReqSize( void ) -{ - return sizeof( sQMUX ) + 6; -} - -/*=========================================================================*/ -// Generic QMUX functions -/*=========================================================================*/ - -/*=========================================================================== -METHOD: - ParseQMUX (Public Method) - -DESCRIPTION: - Remove QMUX headers from a buffer - -PARAMETERS - pClientID [ O ] - On success, will point to Client ID - pBuffer [ I ] - Full Message passed in - buffSize [ I ] - Size of pBuffer - -RETURN VALUE: - int - Positive for size of QMUX header - Negative errno for error -===========================================================================*/ -static int ParseQMUX( - u16 * pClientID, - void * pBuffer, - u16 buffSize ) -{ - sQMUX * pQMUXHeader; - - if (pBuffer == 0 || buffSize < 12) - { - return -ENOMEM; - } - - // QMUX Header - pQMUXHeader = (sQMUX *)pBuffer; - - if (pQMUXHeader->mTF != 1 - || le16_to_cpu(get_unaligned(&pQMUXHeader->mLength)) != buffSize - 1 - || pQMUXHeader->mCtrlFlag != 0x80 ) - { - return -EINVAL; - } - - // Client ID - *pClientID = (pQMUXHeader->mQMIClientID << 8) + pQMUXHeader->mQMIService; - - return sizeof( sQMUX ); -} - -/*=========================================================================== -METHOD: - FillQMUX (Public Method) - -DESCRIPTION: - Fill buffer with QMUX headers - -PARAMETERS - clientID [ I ] - Client ID - pBuffer [ O ] - Buffer to be filled - buffSize [ I ] - Size of pBuffer (must be at least 6) - -RETURN VALUE: - int - 0 for success - Negative errno for error -===========================================================================*/ -static int FillQMUX( - u16 clientID, - void * pBuffer, - u16 buffSize ) -{ - sQMUX * pQMUXHeader; - - if (pBuffer == 0 || buffSize < sizeof( sQMUX )) - { - return -ENOMEM; - } - - // QMUX Header - pQMUXHeader = (sQMUX *)pBuffer; - - pQMUXHeader->mTF = 1; - put_unaligned(cpu_to_le16(buffSize - 1), &pQMUXHeader->mLength); - //DBG("pQMUXHeader->mLength = 0x%x, buffSize - 1 = 0x%x\n",pQMUXHeader->mLength, buffSize - 1); - pQMUXHeader->mCtrlFlag = 0; - - // Service and Client ID - pQMUXHeader->mQMIService = clientID & 0xff; - pQMUXHeader->mQMIClientID = clientID >> 8; - - return 0; -} - -/*=========================================================================*/ -// Generic QMI functions -/*=========================================================================*/ - -/*=========================================================================== -METHOD: - GetTLV (Public Method) - -DESCRIPTION: - Get data buffer of a specified TLV from a QMI message - - QMI Message shall NOT include SDU - -PARAMETERS - pQMIMessage [ I ] - QMI Message buffer - messageLen [ I ] - Size of QMI Message buffer - type [ I ] - Desired Type - pOutDataBuf [ O ] - Buffer to be filled with TLV - messageLen [ I ] - Size of QMI Message buffer - -RETURN VALUE: - u16 - Size of TLV for success - Negative errno for error -===========================================================================*/ -static int GetTLV( - void * pQMIMessage, - u16 messageLen, - u8 type, - void * pOutDataBuf, - u16 bufferLen ) -{ - u16 pos; - u16 tlvSize = 0; - u16 cpyCount; - - if (pQMIMessage == 0 || pOutDataBuf == 0) - { - return -ENOMEM; - } - - for (pos = 4; - pos + 3 < messageLen; - pos += tlvSize + 3) - { - tlvSize = le16_to_cpu( get_unaligned(((u16 *)(pQMIMessage + pos + 1) )) ); - if (*(u8 *)(pQMIMessage + pos) == type) - { - if (bufferLen < tlvSize) - { - return -ENOMEM; - } - - for (cpyCount = 0; cpyCount < tlvSize; cpyCount++) - { - *((char*)(pOutDataBuf + cpyCount)) = *((char*)(pQMIMessage + pos + 3 + cpyCount)); - } - - return tlvSize; - } - } - - return -ENOMSG; -} - -/*=========================================================================== -METHOD: - ValidQMIMessage (Public Method) - -DESCRIPTION: - Check mandatory TLV in a QMI message - - QMI Message shall NOT include SDU - -PARAMETERS - pQMIMessage [ I ] - QMI Message buffer - messageLen [ I ] - Size of QMI Message buffer - -RETURN VALUE: - int - 0 for success (no error) - Negative errno for error - Positive for QMI error code -===========================================================================*/ -static int ValidQMIMessage( - void * pQMIMessage, - u16 messageLen ) -{ - char mandTLV[4]; - - if (GetTLV( pQMIMessage, messageLen, 2, &mandTLV[0], 4 ) == 4) - { - // Found TLV - if (*(u16 *)&mandTLV[0] != 0) - { - return le16_to_cpu( get_unaligned(&mandTLV[2]) ); - } - else - { - return 0; - } - } - else - { - return -ENOMSG; - } -} - -/*=========================================================================== -METHOD: - GetQMIMessageID (Public Method) - -DESCRIPTION: - Get the message ID of a QMI message - - QMI Message shall NOT include SDU - -PARAMETERS - pQMIMessage [ I ] - QMI Message buffer - messageLen [ I ] - Size of QMI Message buffer - -RETURN VALUE: - int - Positive for message ID - Negative errno for error -===========================================================================*/ -static int GetQMIMessageID( - void * pQMIMessage, - u16 messageLen ) -{ - if (messageLen < 2) - { - return -ENODATA; - } - else - { - return le16_to_cpu( get_unaligned((u16 *)pQMIMessage) ); - } -} - -/*=========================================================================*/ -// Fill Buffers with QMI requests -/*=========================================================================*/ - -/*=========================================================================== -METHOD: - QMICTLGetClientIDReq (Public Method) - -DESCRIPTION: - Fill buffer with QMI CTL Get Client ID Request - -PARAMETERS - pBuffer [ 0 ] - Buffer to be filled - buffSize [ I ] - Size of pBuffer - transactionID [ I ] - Transaction ID - serviceType [ I ] - Service type requested - -RETURN VALUE: - int - Positive for resulting size of pBuffer - Negative errno for error -===========================================================================*/ -static int QMICTLGetClientIDReq( - void * pBuffer, - u16 buffSize, - u8 transactionID, - u8 serviceType ) -{ - if (pBuffer == 0 || buffSize < QMICTLGetClientIDReqSize() ) - { - return -ENOMEM; - } - - // QMI CTL GET CLIENT ID - // Request - *(u8 *)(pBuffer + sizeof( sQMUX ))= 0x00; - // Transaction ID - *(u8 *)(pBuffer + sizeof( sQMUX ) + 1) = transactionID; - // Message ID - put_unaligned(cpu_to_le16(0x0022), (u16 *)(pBuffer + sizeof( sQMUX ) + 2)); - // Size of TLV's - put_unaligned(cpu_to_le16(0x0004), (u16 *)(pBuffer + sizeof( sQMUX ) + 4)); - // QMI Service Type - *(u8 *)(pBuffer + sizeof( sQMUX ) + 6) = 0x01; - // Size - put_unaligned(cpu_to_le16(0x0001), (u16 *)(pBuffer + sizeof( sQMUX ) + 7)); - // QMI svc type - *(u8 *)(pBuffer + sizeof( sQMUX ) + 9) = serviceType; - - // success - return sizeof( sQMUX ) + 10; -} - -/*=========================================================================== -METHOD: - QMICTLReleaseClientIDReq (Public Method) - -DESCRIPTION: - Fill buffer with QMI CTL Release Client ID Request - -PARAMETERS - pBuffer [ 0 ] - Buffer to be filled - buffSize [ I ] - Size of pBuffer - transactionID [ I ] - Transaction ID - clientID [ I ] - Service type requested - -RETURN VALUE: - int - Positive for resulting size of pBuffer - Negative errno for error -===========================================================================*/ -static int QMICTLReleaseClientIDReq( - void * pBuffer, - u16 buffSize, - u8 transactionID, - u16 clientID ) -{ - if (pBuffer == 0 || buffSize < QMICTLReleaseClientIDReqSize() ) - { - return -ENOMEM; - } - - DBG( "buffSize: 0x%x, transactionID: 0x%x, clientID: 0x%x,\n", - buffSize, transactionID, clientID ); - - // QMI CTL RELEASE CLIENT ID REQ - // Request - *(u8 *)(pBuffer + sizeof( sQMUX )) = 0x00; - // Transaction ID - *(u8 *)(pBuffer + sizeof( sQMUX ) + 1 ) = transactionID; - // Message ID - put_unaligned( cpu_to_le16(0x0023), (u16 *)(pBuffer + sizeof( sQMUX ) + 2) ); - // Size of TLV's - put_unaligned( cpu_to_le16(0x0005), (u16 *)(pBuffer + sizeof( sQMUX ) + 4) ); - // Release client ID - *(u8 *)(pBuffer + sizeof( sQMUX ) + 6) = 0x01; - // Size - put_unaligned( cpu_to_le16(0x0002), (u16 *)(pBuffer + sizeof( sQMUX ) + 7)); - // QMI svs type / Client ID - put_unaligned(cpu_to_le16(clientID), (u16 *)(pBuffer + sizeof( sQMUX ) + 9)); - - // success - return sizeof( sQMUX ) + 11; -} - -/*=========================================================================== -METHOD: - QMICTLReadyReq (Public Method) - -DESCRIPTION: - Fill buffer with QMI CTL Get Version Info Request - -PARAMETERS - pBuffer [ 0 ] - Buffer to be filled - buffSize [ I ] - Size of pBuffer - transactionID [ I ] - Transaction ID - -RETURN VALUE: - int - Positive for resulting size of pBuffer - Negative errno for error -===========================================================================*/ -static int QMICTLReadyReq( - void * pBuffer, - u16 buffSize, - u8 transactionID ) -{ - if (pBuffer == 0 || buffSize < QMICTLReadyReqSize() ) - { - return -ENOMEM; - } - - DBG("buffSize: 0x%x, transactionID: 0x%x\n", buffSize, transactionID); - - // QMI CTL GET VERSION INFO REQ - // Request - *(u8 *)(pBuffer + sizeof( sQMUX )) = 0x00; - // Transaction ID - *(u8 *)(pBuffer + sizeof( sQMUX ) + 1) = transactionID; - // Message ID - put_unaligned( cpu_to_le16(0x0021), (u16 *)(pBuffer + sizeof( sQMUX ) + 2) ); - // Size of TLV's - put_unaligned( cpu_to_le16(0x0000), (u16 *)(pBuffer + sizeof( sQMUX ) + 4) ); - - // success - return sizeof( sQMUX ) + 6; -} - -/*=========================================================================== -METHOD: - QMIWDSSetEventReportReq (Public Method) - -DESCRIPTION: - Fill buffer with QMI WDS Set Event Report Request - -PARAMETERS - pBuffer [ 0 ] - Buffer to be filled - buffSize [ I ] - Size of pBuffer - transactionID [ I ] - Transaction ID - -RETURN VALUE: - int - Positive for resulting size of pBuffer - Negative errno for error -===========================================================================*/ -static int QMIWDSSetEventReportReq( - void * pBuffer, - u16 buffSize, - u16 transactionID ) -{ - if (pBuffer == 0 || buffSize < QMIWDSSetEventReportReqSize() ) - { - return -ENOMEM; - } - - // QMI WDS SET EVENT REPORT REQ - // Request - *(u8 *)(pBuffer + sizeof( sQMUX )) = 0x00; - // Transaction ID - put_unaligned( cpu_to_le16(transactionID), (u16 *)(pBuffer + sizeof( sQMUX ) + 1)); - // Message ID - put_unaligned( cpu_to_le16(0x0001), (u16 *)(pBuffer + sizeof( sQMUX ) + 3)); - // Size of TLV's - put_unaligned(cpu_to_le16(0x0008), (u16 *)(pBuffer + sizeof( sQMUX ) + 5)); - // Report channel rate TLV - *(u8 *)(pBuffer + sizeof( sQMUX ) + 7) = 0x11; - // Size - put_unaligned( cpu_to_le16(0x0005), (u16 *)(pBuffer + sizeof( sQMUX ) + 8)); - // Stats period - *(u8 *)(pBuffer + sizeof( sQMUX ) + 10) = 0x01; - // Stats mask - put_unaligned( cpu_to_le32(0x000000ff), (u32 *)(pBuffer + sizeof( sQMUX ) + 11) ); - - // success - return sizeof( sQMUX ) + 15; -} - -/*=========================================================================== -METHOD: - QMIWDSGetPKGSRVCStatusReq (Public Method) - -DESCRIPTION: - Fill buffer with QMI WDS Get PKG SRVC Status Request - -PARAMETERS - pBuffer [ 0 ] - Buffer to be filled - buffSize [ I ] - Size of pBuffer - transactionID [ I ] - Transaction ID - -RETURN VALUE: - int - Positive for resulting size of pBuffer - Negative errno for error -===========================================================================*/ -static int QMIWDSGetPKGSRVCStatusReq( - void * pBuffer, - u16 buffSize, - u16 transactionID ) -{ - if (pBuffer == 0 || buffSize < QMIWDSGetPKGSRVCStatusReqSize() ) - { - return -ENOMEM; - } - - // QMI WDS Get PKG SRVC Status REQ - // Request - *(u8 *)(pBuffer + sizeof( sQMUX )) = 0x00; - // Transaction ID - put_unaligned(cpu_to_le16(transactionID), (u16 *)(pBuffer + sizeof( sQMUX ) + 1)); - // Message ID - put_unaligned(cpu_to_le16(0x0022), (u16 *)(pBuffer + sizeof( sQMUX ) + 3)); - // Size of TLV's - put_unaligned(cpu_to_le16(0x0000), (u16 *)(pBuffer + sizeof( sQMUX ) + 5)); - - // success - return sizeof( sQMUX ) + 7; -} - -#if 0 -static u16 QMIWDSSetQMUXBindMuxDataPortSize( void ) -{ - return sizeof( sQMUX ) + 29; -} - -static u16 QMIWDSSetQMUXBindMuxDataPortReq( - void * pBuffer, - u16 buffSize, - u8 MuxId, - u16 transactionID ) -{ - if (pBuffer == 0 || buffSize < QMIWDSSetQMUXBindMuxDataPortSize() ) - { - return -ENOMEM; - } - - // QMI WDS Set QMUX Bind Mux Data Port REQ - // Request - *(u8 *)(pBuffer + sizeof( sQMUX )) = 0x00; - // Transaction ID - put_unaligned(cpu_to_le16(transactionID), (u16 *)(pBuffer + sizeof( sQMUX ) + 1)); - // Message ID - put_unaligned(cpu_to_le16(0x00a2), (u16 *)(pBuffer + sizeof( sQMUX ) + 3)); - // Size of TLV's - put_unaligned(cpu_to_le16(0x0016), (u16 *)(pBuffer + sizeof( sQMUX ) + 5)); - - *(u8 *)(pBuffer + sizeof( sQMUX ) + 7) = 0x10; - put_unaligned(cpu_to_le16(0x08), (u16 *)(pBuffer + sizeof( sQMUX ) + 8)); - put_unaligned(cpu_to_le32(0x02), (u32 *)(pBuffer + sizeof( sQMUX ) + 10)); // ep_type - put_unaligned(cpu_to_le32(0x04), (u32 *)(pBuffer + sizeof( sQMUX ) + 14)); // iface_id - - *(u8 *)(pBuffer + sizeof( sQMUX ) + 18) = 0x11; - put_unaligned(cpu_to_le16(0x01), (u16 *)(pBuffer + sizeof( sQMUX ) + 19)); - *(u8 *)(pBuffer + sizeof( sQMUX ) + 21) = MuxId; // MuxId - - *(u8 *)(pBuffer + sizeof( sQMUX ) + 22) = 0x13; - put_unaligned(cpu_to_le16(0x04), (u16 *)(pBuffer + sizeof( sQMUX ) + 23)); - put_unaligned(cpu_to_le32(0x01), (u32 *)(pBuffer + sizeof( sQMUX ) + 25)); - - // success - return sizeof( sQMUX ) + 29; -} -#endif - -/*=========================================================================== -METHOD: - QMIDMSGetMEIDReq (Public Method) - -DESCRIPTION: - Fill buffer with QMI DMS Get Serial Numbers Request - -PARAMETERS - pBuffer [ 0 ] - Buffer to be filled - buffSize [ I ] - Size of pBuffer - transactionID [ I ] - Transaction ID - -RETURN VALUE: - int - Positive for resulting size of pBuffer - Negative errno for error -===========================================================================*/ -static int QMIDMSGetMEIDReq( - void * pBuffer, - u16 buffSize, - u16 transactionID ) -{ - if (pBuffer == 0 || buffSize < QMIDMSGetMEIDReqSize() ) - { - return -ENOMEM; - } - - // QMI DMS GET SERIAL NUMBERS REQ - // Request - *(u8 *)(pBuffer + sizeof( sQMUX )) = 0x00; - // Transaction ID - put_unaligned( cpu_to_le16(transactionID), (u16 *)(pBuffer + sizeof( sQMUX ) + 1) ); - // Message ID - put_unaligned( cpu_to_le16(0x0025), (u16 *)(pBuffer + sizeof( sQMUX ) + 3) ); - // Size of TLV's - put_unaligned( cpu_to_le16(0x0000), (u16 *)(pBuffer + sizeof( sQMUX ) + 5)); - - // success - return sizeof( sQMUX ) + 7; -} - -/*=========================================================================== -METHOD: - QMIWDASetDataFormatReq (Public Method) - -DESCRIPTION: - Fill buffer with QMI WDA Set Data Format Request - -PARAMETERS - pBuffer [ 0 ] - Buffer to be filled - buffSize [ I ] - Size of pBuffer - transactionID [ I ] - Transaction ID - -RETURN VALUE: - int - Positive for resulting size of pBuffer - Negative errno for error -===========================================================================*/ -static int QMIWDASetDataFormatReq( - void * pBuffer, - u16 buffSize, - bool bRawIPMode, int qmap_version, u32 rx_size, - u16 transactionID ) -{ -if (qmap_version) { - struct QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG *pMUXMsg = (struct QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG *)(pBuffer + sizeof( sQMUX )); - - pMUXMsg->CtlFlags = 0x00; - put_unaligned( cpu_to_le16(transactionID), &pMUXMsg->TransactionId); - put_unaligned( cpu_to_le16(0x0020), &pMUXMsg->Type); - put_unaligned( cpu_to_le16(sizeof( struct QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG) - 7), &pMUXMsg->Length); - - //Indicates whether the Quality of Service(QOS) data format is used by the client. - pMUXMsg->QosDataFormatTlv.TLVType = 0x10; - pMUXMsg->QosDataFormatTlv.TLVLength = cpu_to_le16(0x0001); - pMUXMsg->QosDataFormatTlv.QOSSetting = 0; /* no-QOS header */ -//Underlying Link Layer Protocol - pMUXMsg->UnderlyingLinkLayerProtocolTlv.TLVType = 0x11; - pMUXMsg->UnderlyingLinkLayerProtocolTlv.TLVLength = cpu_to_le16(4); - pMUXMsg->UnderlyingLinkLayerProtocolTlv.Value = cpu_to_le32(0x02); /* Set Ethernet mode */ -//Uplink (UL) data aggregation protocol to be used for uplink data transfer. - pMUXMsg->UplinkDataAggregationProtocolTlv.TLVType = 0x12; - pMUXMsg->UplinkDataAggregationProtocolTlv.TLVLength = cpu_to_le16(4); - pMUXMsg->UplinkDataAggregationProtocolTlv.Value = cpu_to_le32(qmap_version); //UL QMAP is enabled -//Downlink (DL) data aggregation protocol to be used for downlink data transfer - pMUXMsg->DownlinkDataAggregationProtocolTlv.TLVType = 0x13; - pMUXMsg->DownlinkDataAggregationProtocolTlv.TLVLength = cpu_to_le16(4); - pMUXMsg->DownlinkDataAggregationProtocolTlv.Value = cpu_to_le32(qmap_version); //UL QMAP is enabled -//Maximum number of datagrams in a single aggregated packet on downlink - pMUXMsg->DownlinkDataAggregationMaxDatagramsTlv.TLVType = 0x15; - pMUXMsg->DownlinkDataAggregationMaxDatagramsTlv.TLVLength = cpu_to_le16(4); - pMUXMsg->DownlinkDataAggregationMaxDatagramsTlv.Value = cpu_to_le32(rx_size/1024); -//Maximum size in bytes of a single aggregated packet allowed on downlink - pMUXMsg->DownlinkDataAggregationMaxSizeTlv.TLVType = 0x16; - pMUXMsg->DownlinkDataAggregationMaxSizeTlv.TLVLength = cpu_to_le16(4); - pMUXMsg->DownlinkDataAggregationMaxSizeTlv.Value = cpu_to_le32(rx_size); -//Peripheral End Point ID - pMUXMsg->epTlv.TLVType = 0x17; - pMUXMsg->epTlv.TLVLength = cpu_to_le16(8); - pMUXMsg->epTlv.ep_type = cpu_to_le32(0x02); // DATA_EP_TYPE_BAM_DMUX - pMUXMsg->epTlv.iface_id = cpu_to_le32(0x04); -//Specifies the minimum padding bytes to be added in between aggregated downlink QMAP packets. - pMUXMsg->dl_minimum_padding.TLVType = 0x19; - pMUXMsg->dl_minimum_padding.TLVLength = cpu_to_le16(4); - pMUXMsg->dl_minimum_padding.Value = cpu_to_le32(0); -//Maximum number of datagrams in a single aggregated packet on uplink - pMUXMsg->UplinkDataAggregationMaxDatagramsTlv.TLVType = 27; - pMUXMsg->UplinkDataAggregationMaxDatagramsTlv.TLVLength = cpu_to_le16(4); - pMUXMsg->UplinkDataAggregationMaxDatagramsTlv.Value = cpu_to_le32(11); -//Maximum size in bytes of a single aggregated packet allowed on uplink - pMUXMsg->UplinkDataAggregationMaxSizeTlv.TLVType = 28; - pMUXMsg->UplinkDataAggregationMaxSizeTlv.TLVLength = cpu_to_le16(4); - pMUXMsg->UplinkDataAggregationMaxSizeTlv.Value = cpu_to_le32(8*1024); -} -else { - if (pBuffer == 0 || buffSize < QMIWDASetDataFormatReqSize(qmap_version) ) - { - return -ENOMEM; - } - - // QMI WDA SET DATA FORMAT REQ - // Request - *(u8 *)(pBuffer + sizeof( sQMUX )) = 0x00; - - // Transaction ID - put_unaligned( cpu_to_le16(transactionID), (u16 *)(pBuffer + sizeof( sQMUX ) + 1) ); - - // Message ID - put_unaligned( cpu_to_le16(0x0020), (u16 *)(pBuffer + sizeof( sQMUX ) + 3) ); - - // Size of TLV's - put_unaligned( cpu_to_le16(0x000b), (u16 *)(pBuffer + sizeof( sQMUX ) + 5)); - - /* TLVType QOS Data Format 1 byte */ - *(u8 *)(pBuffer + sizeof( sQMUX ) + 7) = 0x10; // type data format - - /* TLVLength 2 bytes - see spec */ - put_unaligned( cpu_to_le16(0x0001), (u16 *)(pBuffer + sizeof( sQMUX ) + 8)); - - /* DataFormat: 0-default; 1-QoS hdr present 2 bytes */ -#ifdef QOS_MODE - *(u8 *)(pBuffer + sizeof( sQMUX ) + 10) = 1; /* QOS header */ -#else - *(u8 *)(pBuffer + sizeof( sQMUX ) + 10) = 0; /* no-QOS header */ -#endif - - /* TLVType Link-Layer Protocol (Optional) 1 byte */ - *(u8 *)(pBuffer + sizeof( sQMUX ) + 11) = 0x11; - - /* TLVLength 2 bytes */ - put_unaligned( cpu_to_le16(0x0004), (u16 *)(pBuffer + sizeof( sQMUX ) + 12)); - - /* LinkProt: 0x1 - ETH; 0x2 - rawIP 4 bytes */ -if (bRawIPMode) { //#ifdef DATA_MODE_RP - /* Set RawIP mode */ - put_unaligned( cpu_to_le32(0x00000002), (u32 *)(pBuffer + sizeof( sQMUX ) + 14)); - DBG("Request RawIP Data Format\n"); -} else { //#else - /* Set Ethernet mode */ - put_unaligned( cpu_to_le32(0x00000001), (u32 *)(pBuffer + sizeof( sQMUX ) + 14)); - DBG("Request Ethernet Data Format\n"); -} //#endif - -} - - // success - return QMIWDASetDataFormatReqSize(qmap_version); -} - -#if 0 -static int QMIWDASetDataQmapReq( - void * pBuffer, - u16 buffSize, - u16 transactionID ) -{ - // QMI WDA SET DATA FORMAT REQ - // Request - *(u8 *)(pBuffer + sizeof( sQMUX )) = 0x00; - - // Transaction ID - put_unaligned( cpu_to_le16(transactionID), (u16 *)(pBuffer + sizeof( sQMUX ) + 1) ); - - // Message ID - put_unaligned( cpu_to_le16(0x002B), (u16 *)(pBuffer + sizeof( sQMUX ) + 3) ); - - // Size of TLV's - put_unaligned( cpu_to_le16(0x0004), (u16 *)(pBuffer + sizeof( sQMUX ) + 5)); - - /* TLVType QMAP In-Band Flow Control 1 byte */ - *(u8 *)(pBuffer + sizeof( sQMUX ) + 7) = 0x10; - put_unaligned( cpu_to_le16(0x0001), (u16 *)(pBuffer + sizeof( sQMUX ) + 8)); - *(u8 *)(pBuffer + sizeof( sQMUX ) + 10) = 0x01; - - // success - return ( sizeof( sQMUX ) + 11); -} -#endif - -#if 0 -/*=========================================================================== -METHOD: - QMICTLSetDataFormatReqSize (Public Method) - -DESCRIPTION: - Get size of buffer needed for QMUX + QMICTLSetDataFormatReq - -RETURN VALUE: - u16 - size of buffer -===========================================================================*/ -static u16 QMICTLSetDataFormatReqSize( void ) -{ - return sizeof( sQMUX ) + 15; -} - -/*=========================================================================== -METHOD: - QMICTLSetDataFormatReq (Public Method) - -DESCRIPTION: - Fill buffer with QMI CTL Set Data Format Request - -PARAMETERS - pBuffer [ 0 ] - Buffer to be filled - buffSize [ I ] - Size of pBuffer - transactionID [ I ] - Transaction ID - -RETURN VALUE: - int - Positive for resulting size of pBuffer - Negative errno for error -===========================================================================*/ -static int QMICTLSetDataFormatReq( - void * pBuffer, - u16 buffSize, - u8 transactionID ) -{ - if (pBuffer == 0 || buffSize < QMICTLSetDataFormatReqSize() ) - { - return -ENOMEM; - } - - /* QMI CTL Set Data Format Request */ - /* Request */ - *(u8 *)(pBuffer + sizeof( sQMUX )) = 0x00; // QMICTL_FLAG_REQUEST - - /* Transaction ID 1 byte */ - *(u8 *)(pBuffer + sizeof( sQMUX ) + 1) = transactionID; /* 1 byte as in spec */ - - /* QMICTLType 2 bytes */ - put_unaligned( cpu_to_le16(0x0026), (u16 *)(pBuffer + sizeof( sQMUX ) + 2)); - - /* Length 2 bytes of 2 TLVs each - see spec */ - put_unaligned( cpu_to_le16(0x0009), (u16 *)(pBuffer + sizeof( sQMUX ) + 4)); - - /* TLVType Data Format (Mandatory) 1 byte */ - *(u8 *)(pBuffer + sizeof( sQMUX ) + 6) = 0x01; // type data format - - /* TLVLength 2 bytes - see spec */ - put_unaligned( cpu_to_le16(0x0001), (u16 *)(pBuffer + sizeof( sQMUX ) + 7)); - - /* DataFormat: 0-default; 1-QoS hdr present 2 bytes */ -#ifdef QOS_MODE - *(u8 *)(pBuffer + sizeof( sQMUX ) + 9) = 1; /* QOS header */ -#else - *(u8 *)(pBuffer + sizeof( sQMUX ) + 9) = 0; /* no-QOS header */ -#endif - - /* TLVType Link-Layer Protocol (Optional) 1 byte */ - *(u8 *)(pBuffer + sizeof( sQMUX ) + 10) = TLV_TYPE_LINK_PROTO; - - /* TLVLength 2 bytes */ - put_unaligned( cpu_to_le16(0x0002), (u16 *)(pBuffer + sizeof( sQMUX ) + 11)); - - /* LinkProt: 0x1 - ETH; 0x2 - rawIP 2 bytes */ -#ifdef DATA_MODE_RP - /* Set RawIP mode */ - put_unaligned( cpu_to_le16(0x0002), (u16 *)(pBuffer + sizeof( sQMUX ) + 13)); - DBG("Request RawIP Data Format\n"); -#else - /* Set Ethernet mode */ - put_unaligned( cpu_to_le16(0x0001), (u16 *)(pBuffer + sizeof( sQMUX ) + 13)); - DBG("Request Ethernet Data Format\n"); -#endif - - /* success */ - return sizeof( sQMUX ) + 15; - -} -#endif - -/*=========================================================================== -METHOD: - QMICTLSyncReq (Public Method) - -DESCRIPTION: - Fill buffer with QMI CTL Sync Request - -PARAMETERS - pBuffer [ 0 ] - Buffer to be filled - buffSize [ I ] - Size of pBuffer - transactionID [ I ] - Transaction ID - -RETURN VALUE: - int - Positive for resulting size of pBuffer - Negative errno for error -===========================================================================*/ -static int QMICTLSyncReq( - void * pBuffer, - u16 buffSize, - u16 transactionID ) -{ - if (pBuffer == 0 || buffSize < QMICTLSyncReqSize() ) - { - return -ENOMEM; - } - - // Request - *(u8 *)(pBuffer + sizeof( sQMUX )) = 0x00; - // Transaction ID - *(u8 *)(pBuffer + sizeof( sQMUX ) + 1) = transactionID; - // Message ID - put_unaligned( cpu_to_le16(0x0027), (u16 *)(pBuffer + sizeof( sQMUX ) + 2) ); - // Size of TLV's - put_unaligned( cpu_to_le16(0x0000), (u16 *)(pBuffer + sizeof( sQMUX ) + 4) ); - - // success - return sizeof( sQMUX ) + 6; -} - -/*=========================================================================*/ -// Parse data from QMI responses -/*=========================================================================*/ - -/*=========================================================================== -METHOD: - QMICTLGetClientIDResp (Public Method) - -DESCRIPTION: - Parse the QMI CTL Get Client ID Resp - -PARAMETERS - pBuffer [ I ] - Buffer to be parsed - buffSize [ I ] - Size of pBuffer - pClientID [ 0 ] - Recieved client ID - -RETURN VALUE: - int - 0 for success - Negative errno for error -===========================================================================*/ -static int QMICTLGetClientIDResp( - void * pBuffer, - u16 buffSize, - u16 * pClientID ) -{ - int result; - - // Ignore QMUX and SDU - // QMI CTL SDU is 2 bytes, not 3 - u8 offset = sizeof( sQMUX ) + 2; - - if (pBuffer == 0 || buffSize < offset) - { - return -ENOMEM; - } - - pBuffer = pBuffer + offset; - buffSize -= offset; - - result = GetQMIMessageID( pBuffer, buffSize ); - if (result != 0x22) - { - return -EFAULT; - } - - result = ValidQMIMessage( pBuffer, buffSize ); - if (result != 0) - { - return -EFAULT; - } - - result = GetTLV( pBuffer, buffSize, 0x01, pClientID, 2 ); - if (result != 2) - { - return -EFAULT; - } - - return 0; -} - -/*=========================================================================== -METHOD: - QMICTLReleaseClientIDResp (Public Method) - -DESCRIPTION: - Verify the QMI CTL Release Client ID Resp is valid - -PARAMETERS - pBuffer [ I ] - Buffer to be parsed - buffSize [ I ] - Size of pBuffer - -RETURN VALUE: - int - 0 for success - Negative errno for error -===========================================================================*/ -static int QMICTLReleaseClientIDResp( - void * pBuffer, - u16 buffSize ) -{ - int result; - - // Ignore QMUX and SDU - // QMI CTL SDU is 2 bytes, not 3 - u8 offset = sizeof( sQMUX ) + 2; - - if (pBuffer == 0 || buffSize < offset) - { - return -ENOMEM; - } - - pBuffer = pBuffer + offset; - buffSize -= offset; - - result = GetQMIMessageID( pBuffer, buffSize ); - if (result != 0x23) - { - return -EFAULT; - } - - result = ValidQMIMessage( pBuffer, buffSize ); - if (result != 0) - { - return -EFAULT; - } - - return 0; -} - -/*=========================================================================== -METHOD: - QMIWDSEventResp (Public Method) - -DESCRIPTION: - Parse the QMI WDS Set Event Report Resp/Indication or - QMI WDS Get PKG SRVC Status Resp/Indication - - Return parameters will only be updated if value was received - -PARAMETERS - pBuffer [ I ] - Buffer to be parsed - buffSize [ I ] - Size of pBuffer - pTXOk [ O ] - Number of transmitted packets without errors - pRXOk [ O ] - Number of recieved packets without errors - pTXErr [ O ] - Number of transmitted packets with framing errors - pRXErr [ O ] - Number of recieved packets with framing errors - pTXOfl [ O ] - Number of transmitted packets dropped due to overflow - pRXOfl [ O ] - Number of recieved packets dropped due to overflow - pTXBytesOk [ O ] - Number of transmitted bytes without errors - pRXBytesOk [ O ] - Number of recieved bytes without errors - pbLinkState [ 0 ] - Is the link active? - pbReconfigure [ 0 ] - Must interface be reconfigured? (reset IP address) - -RETURN VALUE: - int - 0 for success - Negative errno for error -===========================================================================*/ -static int QMIWDSEventResp( - void * pBuffer, - u16 buffSize, - u32 * pTXOk, - u32 * pRXOk, - u32 * pTXErr, - u32 * pRXErr, - u32 * pTXOfl, - u32 * pRXOfl, - u64 * pTXBytesOk, - u64 * pRXBytesOk, - bool * pbLinkState, - bool * pbReconfigure ) -{ - int result; - u8 pktStatusRead[2]; - - // Ignore QMUX and SDU - u8 offset = sizeof( sQMUX ) + 3; - - if (pBuffer == 0 - || buffSize < offset - || pTXOk == 0 - || pRXOk == 0 - || pTXErr == 0 - || pRXErr == 0 - || pTXOfl == 0 - || pRXOfl == 0 - || pTXBytesOk == 0 - || pRXBytesOk == 0 - || pbLinkState == 0 - || pbReconfigure == 0 ) - { - return -ENOMEM; - } - - pBuffer = pBuffer + offset; - buffSize -= offset; - - // Note: Indications. No Mandatory TLV required - - result = GetQMIMessageID( pBuffer, buffSize ); - // QMI WDS Set Event Report Resp - if (result == 0x01) - { - // TLV's are not mandatory - GetTLV( pBuffer, buffSize, 0x10, (void*)pTXOk, 4 ); - put_unaligned( le32_to_cpu(*pTXOk), pTXOk); - GetTLV( pBuffer, buffSize, 0x11, (void*)pRXOk, 4 ); - put_unaligned( le32_to_cpu(*pRXOk), pRXOk); - GetTLV( pBuffer, buffSize, 0x12, (void*)pTXErr, 4 ); - put_unaligned( le32_to_cpu(*pTXErr), pTXErr); - GetTLV( pBuffer, buffSize, 0x13, (void*)pRXErr, 4 ); - put_unaligned( le32_to_cpu(*pRXErr), pRXErr); - GetTLV( pBuffer, buffSize, 0x14, (void*)pTXOfl, 4 ); - put_unaligned( le32_to_cpu(*pTXOfl), pTXOfl); - GetTLV( pBuffer, buffSize, 0x15, (void*)pRXOfl, 4 ); - put_unaligned( le32_to_cpu(*pRXOfl), pRXOfl); - GetTLV( pBuffer, buffSize, 0x19, (void*)pTXBytesOk, 8 ); - put_unaligned( le64_to_cpu(*pTXBytesOk), pTXBytesOk); - GetTLV( pBuffer, buffSize, 0x1A, (void*)pRXBytesOk, 8 ); - put_unaligned( le64_to_cpu(*pRXBytesOk), pRXBytesOk); - } - // QMI WDS Get PKG SRVC Status Resp - else if (result == 0x22) - { - result = GetTLV( pBuffer, buffSize, 0x01, &pktStatusRead[0], 2 ); - // 1 or 2 bytes may be received - if (result >= 1) - { - if (pktStatusRead[0] == 0x02) - { - *pbLinkState = true; - } - else - { - *pbLinkState = false; - } - } - if (result == 2) - { - if (pktStatusRead[1] == 0x01) - { - *pbReconfigure = true; - } - else - { - *pbReconfigure = false; - } - } - - if (result < 0) - { - return result; - } - } - else - { - return -EFAULT; - } - - return 0; -} - -/*=========================================================================== -METHOD: - QMIDMSGetMEIDResp (Public Method) - -DESCRIPTION: - Parse the QMI DMS Get Serial Numbers Resp - -PARAMETERS - pBuffer [ I ] - Buffer to be parsed - buffSize [ I ] - Size of pBuffer - pMEID [ O ] - Device MEID - meidSize [ I ] - Size of MEID buffer (at least 14) - -RETURN VALUE: - int - 0 for success - Negative errno for error -===========================================================================*/ -static int QMIDMSGetMEIDResp( - void * pBuffer, - u16 buffSize, - char * pMEID, - int meidSize ) -{ - int result; - - // Ignore QMUX and SDU - u8 offset = sizeof( sQMUX ) + 3; - - if (pBuffer == 0 || buffSize < offset || meidSize < 14) - { - return -ENOMEM; - } - - pBuffer = pBuffer + offset; - buffSize -= offset; - - result = GetQMIMessageID( pBuffer, buffSize ); - if (result != 0x25) - { - return -EFAULT; - } - - result = ValidQMIMessage( pBuffer, buffSize ); - if (result != 0) - { - return -EFAULT; - } - - result = GetTLV( pBuffer, buffSize, 0x12, (void*)pMEID, 14 ); - if (result != 14) - { - return -EFAULT; - } - - return 0; -} - -/*=========================================================================== -METHOD: - QMIWDASetDataFormatResp (Public Method) - -DESCRIPTION: - Parse the QMI WDA Set Data Format Response - -PARAMETERS - pBuffer [ I ] - Buffer to be parsed - buffSize [ I ] - Size of pBuffer - -RETURN VALUE: - int - 0 for success - Negative errno for error -===========================================================================*/ -static int QMIWDASetDataFormatResp( - void * pBuffer, - u16 buffSize, bool bRawIPMode, int *qmap_version, int *rx_size, int *tx_size, QMAP_SETTING *set) -{ - - int result; - - u8 pktLinkProtocol[4]; - - // Ignore QMUX and SDU - // QMI SDU is 3 bytes - u8 offset = sizeof( sQMUX ) + 3; - - if (pBuffer == 0 || buffSize < offset) - { - return -ENOMEM; - } - - pBuffer = pBuffer + offset; - buffSize -= offset; - - result = GetQMIMessageID( pBuffer, buffSize ); - if (result != 0x20) - { - return -EFAULT; - } - - /* Check response message result TLV */ - result = ValidQMIMessage( pBuffer, buffSize ); - if (result != 0) - { - DBG("EFAULT: Data Format Mode Bad Response\n"); -// return -EFAULT; - return 0; - } - - /* Check response message link protocol */ - result = GetTLV( pBuffer, buffSize, 0x11, - &pktLinkProtocol[0], 4); - if (result != 4) - { - DBG("EFAULT: Wrong TLV format\n"); - return 0; - } - -if (bRawIPMode) { ////#ifdef DATA_MODE_RP - if (pktLinkProtocol[0] != 2) - { - DBG("EFAULT: Data Format Cannot be set to RawIP Mode\n"); - return pktLinkProtocol[0]; - } - DBG("Data Format Set to RawIP\n"); -} else { ////#else - if (pktLinkProtocol[0] != 1) - { - DBG("EFAULT: Data Format Cannot be set to Ethernet Mode\n"); - return pktLinkProtocol[0]; - } - DBG("Data Format Set to Ethernet Mode \n"); -} //#endif - - GetTLV( pBuffer, buffSize, 0x12, qmap_version, 4); - if (le32_to_cpu(*qmap_version)) - GetTLV( pBuffer, buffSize, 0x13, qmap_version, 4); - - GetTLV( pBuffer, buffSize, 0x16, rx_size, 4); - GetTLV( pBuffer, buffSize, 0x18, tx_size, 4); - - if (set) { - GetTLV( pBuffer, buffSize, 0x15, &set->dl_data_aggregation_max_datagrams, 4); - GetTLV( pBuffer, buffSize, 0x16, &set->dl_data_aggregation_max_size, 4); - GetTLV( pBuffer, buffSize, 0x17, &set->ul_data_aggregation_max_datagrams, 4); - GetTLV( pBuffer, buffSize, 0x18, &set->ul_data_aggregation_max_size, 4); - GetTLV( pBuffer, buffSize, 0x1a, &set->dl_minimum_padding, 4); - } - - return pktLinkProtocol[0]; -} - -/*=========================================================================== -METHOD: - QMICTLSyncResp (Public Method) - -DESCRIPTION: - Validate the QMI CTL Sync Response - -PARAMETERS - pBuffer [ I ] - Buffer to be parsed - buffSize [ I ] - Size of pBuffer - -RETURN VALUE: - int - 0 for success - Negative errno for error -===========================================================================*/ -static int QMICTLSyncResp( - void *pBuffer, - u16 buffSize ) -{ - int result; - - // Ignore QMUX (2 bytes for QMI CTL) and SDU - u8 offset = sizeof( sQMUX ) + 2; - - if (pBuffer == 0 || buffSize < offset) - { - return -ENOMEM; - } - - pBuffer = pBuffer + offset; - buffSize -= offset; - - result = GetQMIMessageID( pBuffer, buffSize ); - if (result != 0x27) - { - return -EFAULT; - } - - result = ValidQMIMessage( pBuffer, buffSize ); - - return result; -} -#endif diff --git a/package/wwan/driver/quectel_Gobinet/src/QMI.h b/package/wwan/driver/quectel_Gobinet/src/QMI.h deleted file mode 100644 index 284bf7998..000000000 --- a/package/wwan/driver/quectel_Gobinet/src/QMI.h +++ /dev/null @@ -1,337 +0,0 @@ -/*=========================================================================== -FILE: - QMI.h - -DESCRIPTION: - Qualcomm QMI driver header - -FUNCTIONS: - Generic QMUX functions - ParseQMUX - FillQMUX - - Generic QMI functions - GetTLV - ValidQMIMessage - GetQMIMessageID - - Get sizes of buffers needed by QMI requests - QMUXHeaderSize - QMICTLGetClientIDReqSize - QMICTLReleaseClientIDReqSize - QMICTLReadyReqSize - QMIWDSSetEventReportReqSize - QMIWDSGetPKGSRVCStatusReqSize - QMIDMSGetMEIDReqSize - QMICTLSyncReqSize - - Fill Buffers with QMI requests - QMICTLGetClientIDReq - QMICTLReleaseClientIDReq - QMICTLReadyReq - QMIWDSSetEventReportReq - QMIWDSGetPKGSRVCStatusReq - QMIDMSGetMEIDReq - QMICTLSetDataFormatReq - QMICTLSyncReq - - Parse data from QMI responses - QMICTLGetClientIDResp - QMICTLReleaseClientIDResp - QMIWDSEventResp - QMIDMSGetMEIDResp - -Copyright (c) 2011, Code Aurora Forum. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Code Aurora Forum nor - the names of its contributors may be used to endorse or promote - products derived from this software without specific prior written - permission. - - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -===========================================================================*/ - -#pragma once - -/*=========================================================================*/ -// Definitions -/*=========================================================================*/ - -extern int quec_debug; -// DBG macro -#define DBG( format, arg... ) do { \ - if (quec_debug == 1)\ - { \ - printk( KERN_INFO "GobiNet::%s " format, __FUNCTION__, ## arg ); \ - } }while(0) - -#if 0 -#define VDBG( format, arg... ) do { \ - if (debug == 1)\ - { \ - printk( KERN_INFO "GobiNet::%s " format, __FUNCTION__, ## arg ); \ - } } while(0) -#else -#define VDBG( format, arg... ) do { } while(0) -#endif - -#define INFO( format, arg... ) do { \ - printk( KERN_INFO "GobiNet::%s " format, __FUNCTION__, ## arg ); \ - }while(0) - -// QMI Service Types -#define QMICTL 0 -#define QMIWDS 1 -#define QMIDMS 2 -#define QMINAS 3 -#define QMIUIM 11 -#define QMIWDA 0x1A - -#define u8 unsigned char -#define u16 unsigned short -#define u32 unsigned int -#define u64 unsigned long long - -#define bool u8 -#define true 1 -#define false 0 - -#define ENOMEM 12 -#define EFAULT 14 -#define EINVAL 22 -#ifndef ENOMSG -#define ENOMSG 42 -#endif -#define ENODATA 61 - -#define TLV_TYPE_LINK_PROTO 0x10 - -/*=========================================================================*/ -// Struct sQMUX -// -// Structure that defines a QMUX header -/*=========================================================================*/ -typedef struct sQMUX -{ - /* T\F, always 1 */ - u8 mTF; - - /* Size of message */ - u16 mLength; - - /* Control flag */ - u8 mCtrlFlag; - - /* Service Type */ - u8 mQMIService; - - /* Client ID */ - u8 mQMIClientID; - -}__attribute__((__packed__)) sQMUX; - -#if 0 -/*=========================================================================*/ -// Generic QMUX functions -/*=========================================================================*/ - -// Remove QMUX headers from a buffer -int ParseQMUX( - u16 * pClientID, - void * pBuffer, - u16 buffSize ); - -// Fill buffer with QMUX headers -int FillQMUX( - u16 clientID, - void * pBuffer, - u16 buffSize ); - -/*=========================================================================*/ -// Generic QMI functions -/*=========================================================================*/ - -// Get data buffer of a specified TLV from a QMI message -int GetTLV( - void * pQMIMessage, - u16 messageLen, - u8 type, - void * pOutDataBuf, - u16 bufferLen ); - -// Check mandatory TLV in a QMI message -int ValidQMIMessage( - void * pQMIMessage, - u16 messageLen ); - -// Get the message ID of a QMI message -int GetQMIMessageID( - void * pQMIMessage, - u16 messageLen ); - -/*=========================================================================*/ -// Get sizes of buffers needed by QMI requests -/*=========================================================================*/ - -// Get size of buffer needed for QMUX -u16 QMUXHeaderSize( void ); - -// Get size of buffer needed for QMUX + QMICTLGetClientIDReq -u16 QMICTLGetClientIDReqSize( void ); - -// Get size of buffer needed for QMUX + QMICTLReleaseClientIDReq -u16 QMICTLReleaseClientIDReqSize( void ); - -// Get size of buffer needed for QMUX + QMICTLReadyReq -u16 QMICTLReadyReqSize( void ); - -// Get size of buffer needed for QMUX + QMIWDSSetEventReportReq -u16 QMIWDSSetEventReportReqSize( void ); - -// Get size of buffer needed for QMUX + QMIWDSGetPKGSRVCStatusReq -u16 QMIWDSGetPKGSRVCStatusReqSize( void ); - -u16 QMIWDSSetQMUXBindMuxDataPortSize( void ); - -// Get size of buffer needed for QMUX + QMIDMSGetMEIDReq -u16 QMIDMSGetMEIDReqSize( void ); - -// Get size of buffer needed for QMUX + QMIWDASetDataFormatReq -u16 QMIWDASetDataFormatReqSize( int qmap_mode ); - -// Get size of buffer needed for QMUX + QMICTLSyncReq -u16 QMICTLSyncReqSize( void ); - -/*=========================================================================*/ -// Fill Buffers with QMI requests -/*=========================================================================*/ - -// Fill buffer with QMI CTL Get Client ID Request -int QMICTLGetClientIDReq( - void * pBuffer, - u16 buffSize, - u8 transactionID, - u8 serviceType ); - -// Fill buffer with QMI CTL Release Client ID Request -int QMICTLReleaseClientIDReq( - void * pBuffer, - u16 buffSize, - u8 transactionID, - u16 clientID ); - -// Fill buffer with QMI CTL Get Version Info Request -int QMICTLReadyReq( - void * pBuffer, - u16 buffSize, - u8 transactionID ); - -// Fill buffer with QMI WDS Set Event Report Request -int QMIWDSSetEventReportReq( - void * pBuffer, - u16 buffSize, - u16 transactionID ); - -// Fill buffer with QMI WDS Get PKG SRVC Status Request -int QMIWDSGetPKGSRVCStatusReq( - void * pBuffer, - u16 buffSize, - u16 transactionID ); - -u16 QMIWDSSetQMUXBindMuxDataPortReq( - void * pBuffer, - u16 buffSize, - u8 MuxId, - u16 transactionID ); - -// Fill buffer with QMI DMS Get Serial Numbers Request -int QMIDMSGetMEIDReq( - void * pBuffer, - u16 buffSize, - u16 transactionID ); - -// Fill buffer with QMI WDA Set Data Format Request -int QMIWDASetDataFormatReq( - void * pBuffer, - u16 buffSize, - bool bRawIPMode, int qmap_mode, u32 rx_size, - u16 transactionID ); - -#if 0 -int QMIWDASetDataQmapReq( - void * pBuffer, - u16 buffSize, - u16 transactionID ); -#endif - -int QMICTLSyncReq( - void * pBuffer, - u16 buffSize, - u16 transactionID ); - -/*=========================================================================*/ -// Parse data from QMI responses -/*=========================================================================*/ - -// Parse the QMI CTL Get Client ID Resp -int QMICTLGetClientIDResp( - void * pBuffer, - u16 buffSize, - u16 * pClientID ); - -// Verify the QMI CTL Release Client ID Resp is valid -int QMICTLReleaseClientIDResp( - void * pBuffer, - u16 buffSize ); - -// Parse the QMI WDS Set Event Report Resp/Indication or -// QMI WDS Get PKG SRVC Status Resp/Indication -int QMIWDSEventResp( - void * pBuffer, - u16 buffSize, - u32 * pTXOk, - u32 * pRXOk, - u32 * pTXErr, - u32 * pRXErr, - u32 * pTXOfl, - u32 * pRXOfl, - u64 * pTXBytesOk, - u64 * pRXBytesOk, - bool * pbLinkState, - bool * pbReconfigure ); - -// Parse the QMI DMS Get Serial Numbers Resp -int QMIDMSGetMEIDResp( - void * pBuffer, - u16 buffSize, - char * pMEID, - int meidSize ); - -// Parse the QMI DMS Get Serial Numbers Resp -int QMIWDASetDataFormatResp( - void * pBuffer, - u16 buffSize, bool bRawIPMode, int *qmap_enabled, int *rx_size, int *tx_size); - -// Pasre the QMI CTL Sync Response -int QMICTLSyncResp( - void *pBuffer, - u16 buffSize ); -#endif diff --git a/package/wwan/driver/quectel_Gobinet/src/QMIDevice.c b/package/wwan/driver/quectel_Gobinet/src/QMIDevice.c deleted file mode 100644 index 5d907f50a..000000000 --- a/package/wwan/driver/quectel_Gobinet/src/QMIDevice.c +++ /dev/null @@ -1,4363 +0,0 @@ -/*=========================================================================== -FILE: - QMIDevice.c - -DESCRIPTION: - Functions related to the QMI interface device - -FUNCTIONS: - Generic functions - IsDeviceValid - PrintHex - GobiSetDownReason - GobiClearDownReason - GobiTestDownReason - - Driver level asynchronous read functions - ResubmitIntURB - ReadCallback - IntCallback - StartRead - KillRead - - Internal read/write functions - ReadAsync - UpSem - ReadSync - WriteSyncCallback - WriteSync - - Internal memory management functions - GetClientID - ReleaseClientID - FindClientMem - AddToReadMemList - PopFromReadMemList - AddToNotifyList - NotifyAndPopNotifyList - AddToURBList - PopFromURBList - - Internal userspace wrapper functions - UserspaceunlockedIOCTL - - Userspace wrappers - UserspaceOpen - UserspaceIOCTL - UserspaceClose - UserspaceRead - UserspaceWrite - UserspacePoll - - Initializer and destructor - RegisterQMIDevice - DeregisterQMIDevice - - Driver level client management - QMIReady - QMIWDSCallback - SetupQMIWDSCallback - QMIDMSGetMEID - -Copyright (c) 2011, Code Aurora Forum. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Code Aurora Forum nor - the names of its contributors may be used to endorse or promote - products derived from this software without specific prior written - permission. - - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -===========================================================================*/ - -//--------------------------------------------------------------------------- -// Include Files -//--------------------------------------------------------------------------- -#include -#include -#include -#include - -//----------------------------------------------------------------------------- -// Definitions -//----------------------------------------------------------------------------- - -#define __QUEC_INCLUDE_QMI_C__ -#include "QMI.c" -#define __QUECTEL_INTER__ -#include "QMIDevice.h" - -#if (LINUX_VERSION_CODE <= KERNEL_VERSION( 2,6,22 )) -static int s_interval; -#endif - -#if (LINUX_VERSION_CODE <= KERNEL_VERSION( 2,6,14 )) -#include -static char devfs_name[32]; -static int device_create(struct class *class, struct device *parent, dev_t devt, const char *fmt, ...) -{ - va_list vargs; - struct class_device *class_dev; - int err; - - va_start(vargs, fmt); - vsnprintf(devfs_name, sizeof(devfs_name), fmt, vargs); - va_end(vargs); - - class_dev = class_device_create(class, devt, parent, "%s", devfs_name); - if (IS_ERR(class_dev)) { - err = PTR_ERR(class_dev); - goto out; - } - - err = devfs_mk_cdev(devt, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP, devfs_name); - if (err) { - class_device_destroy(class, devt); - goto out; - } - - return 0; - -out: - return err; -} - -static void device_destroy(struct class *class, dev_t devt) -{ - class_device_destroy(class, devt); - devfs_remove(devfs_name); -} -#endif - -#ifdef CONFIG_PM -// Prototype to GobiNetSuspend function -int QuecGobiNetSuspend( - struct usb_interface * pIntf, - pm_message_t powerEvent ); -#endif /* CONFIG_PM */ - -// IOCTL to generate a client ID for this service type -#define IOCTL_QMI_GET_SERVICE_FILE 0x8BE0 + 1 - -// IOCTL to get the VIDPID of the device -#define IOCTL_QMI_GET_DEVICE_VIDPID 0x8BE0 + 2 - -// IOCTL to get the MEID of the device -#define IOCTL_QMI_GET_DEVICE_MEID 0x8BE0 + 3 - -#define IOCTL_QMI_RELEASE_SERVICE_FILE_IOCTL (0x8BE0 + 4) - -// CDC GET_ENCAPSULATED_RESPONSE packet -#define CDC_GET_ENCAPSULATED_RESPONSE_LE 0x01A1ll -#define CDC_GET_ENCAPSULATED_RESPONSE_BE 0xA101000000000000ll -/* The following masks filter the common part of the encapsulated response - * packet value for Gobi and QMI devices, ie. ignore usb interface number - */ -#define CDC_RSP_MASK_BE 0xFFFFFFFF00FFFFFFll -#define CDC_RSP_MASK_LE 0xFFFFFFE0FFFFFFFFll - -static const int i = 1; -#define is_bigendian() ( (*(char*)&i) == 0 ) -#define CDC_GET_ENCAPSULATED_RESPONSE(pcdcrsp, pmask)\ -{\ - *pcdcrsp = is_bigendian() ? CDC_GET_ENCAPSULATED_RESPONSE_BE \ - : CDC_GET_ENCAPSULATED_RESPONSE_LE ; \ - *pmask = is_bigendian() ? CDC_RSP_MASK_BE \ - : CDC_RSP_MASK_LE; \ -} - -// CDC CONNECTION_SPEED_CHANGE indication packet -#define CDC_CONNECTION_SPEED_CHANGE_LE 0x2AA1ll -#define CDC_CONNECTION_SPEED_CHANGE_BE 0xA12A000000000000ll -/* The following masks filter the common part of the connection speed change - * packet value for Gobi and QMI devices - */ -#define CDC_CONNSPD_MASK_BE 0xFFFFFFFFFFFF7FFFll -#define CDC_CONNSPD_MASK_LE 0XFFF7FFFFFFFFFFFFll -#define CDC_GET_CONNECTION_SPEED_CHANGE(pcdccscp, pmask)\ -{\ - *pcdccscp = is_bigendian() ? CDC_CONNECTION_SPEED_CHANGE_BE \ - : CDC_CONNECTION_SPEED_CHANGE_LE ; \ - *pmask = is_bigendian() ? CDC_CONNSPD_MASK_BE \ - : CDC_CONNSPD_MASK_LE; \ -} - -#define SET_CONTROL_LINE_STATE_REQUEST_TYPE 0x21 -#define SET_CONTROL_LINE_STATE_REQUEST 0x22 -#define CONTROL_DTR 0x01 -#define CONTROL_RTS 0x02 - -/*=========================================================================*/ -// UserspaceQMIFops -// QMI device's userspace file operations -/*=========================================================================*/ -static struct file_operations UserspaceQMIFops = -{ - .owner = THIS_MODULE, - .read = UserspaceRead, - .write = UserspaceWrite, -#ifdef CONFIG_COMPAT - .compat_ioctl = UserspaceunlockedIOCTL, -#endif -#if (LINUX_VERSION_CODE >= KERNEL_VERSION( 2,6,36 )) - .unlocked_ioctl = UserspaceunlockedIOCTL, -#else - .ioctl = UserspaceIOCTL, -#endif - .open = UserspaceOpen, -#ifdef quectel_no_for_each_process - .release = UserspaceClose, -#else - .flush = UserspaceClose, -#endif - .poll = UserspacePoll, -}; - -/*=========================================================================*/ -// Generic functions -/*=========================================================================*/ -static u8 QMIXactionIDGet( sGobiUSBNet *pDev) -{ - u8 transactionID; - - if( 0 == (transactionID = atomic_add_return( 1, &pDev->mQMIDev.mQMICTLTransactionID)) ) - { - transactionID = atomic_add_return( 1, &pDev->mQMIDev.mQMICTLTransactionID ); - } - -#if 1 //free these ununsed qmi response, or when these transactionID re-used, they will be regarded as qmi response of the qmi request that have same transactionID - if (transactionID) { - unsigned long flags; - void * pReadBuffer; - u16 readBufferSize; - - spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags ); - while (PopFromReadMemList( pDev, - QMICTL, - transactionID, - &pReadBuffer, - &readBufferSize ) == true) - { - kfree( pReadBuffer ); - } - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); - } -#endif - - return transactionID; -} - -static struct usb_endpoint_descriptor *GetEndpoint( - struct usb_interface *pintf, - int type, - int dir ) -{ - int i; - struct usb_host_interface *iface = pintf->cur_altsetting; - struct usb_endpoint_descriptor *pendp; - - for( i = 0; i < iface->desc.bNumEndpoints; i++) - { - pendp = &iface->endpoint[i].desc; - if( ((pendp->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == dir) - && - (usb_endpoint_type(pendp) == type) ) - { - return pendp; - } - } - - return NULL; -} - -/*=========================================================================== -METHOD: - IsDeviceValid (Public Method) - -DESCRIPTION: - Basic test to see if device memory is valid - -PARAMETERS: - pDev [ I ] - Device specific memory - -RETURN VALUE: - bool -===========================================================================*/ -static bool IsDeviceValid( sGobiUSBNet * pDev ) -{ - if (pDev == NULL) - { - return false; - } - - if (pDev->mbQMIValid == false) - { - return false; - } - - return true; -} - -/*=========================================================================== -METHOD: - PrintHex (Public Method) - -DESCRIPTION: - Print Hex data, for debug purposes - -PARAMETERS: - pBuffer [ I ] - Data buffer - bufSize [ I ] - Size of data buffer - -RETURN VALUE: - None -===========================================================================*/ -void QuecPrintHex( - void * pBuffer, - u16 bufSize ) -{ - char * pPrintBuf; - u16 pos; - int status; - - if (quec_debug != 1) - { - return; - } - - pPrintBuf = kmalloc( bufSize * 3 + 1, GFP_ATOMIC ); - if (pPrintBuf == NULL) - { - DBG( "Unable to allocate buffer\n" ); - return; - } - memset( pPrintBuf, 0 , bufSize * 3 + 1 ); - - for (pos = 0; pos < bufSize; pos++) - { - status = snprintf( (pPrintBuf + (pos * 3)), - 4, - "%02X ", - *(u8 *)(pBuffer + pos) ); - if (status != 3) - { - DBG( "snprintf error %d\n", status ); - kfree( pPrintBuf ); - return; - } - } - - DBG( " : %s\n", pPrintBuf ); - - kfree( pPrintBuf ); - pPrintBuf = NULL; - return; -} - -/*=========================================================================== -METHOD: - GobiSetDownReason (Public Method) - -DESCRIPTION: - Sets mDownReason and turns carrier off - -PARAMETERS - pDev [ I ] - Device specific memory - reason [ I ] - Reason device is down - -RETURN VALUE: - None -===========================================================================*/ -void QuecGobiSetDownReason( - sGobiUSBNet * pDev, - u8 reason ) -{ - DBG("%s reason=%d, mDownReason=%x\n", __func__, reason, (unsigned)pDev->mDownReason); - -#ifdef QUECTEL_WWAN_QMAP - if (reason == NO_NDIS_CONNECTION) - return; -#endif - - set_bit( reason, &pDev->mDownReason ); - - netif_carrier_off( pDev->mpNetDev->net ); -} - -/*=========================================================================== -METHOD: - GobiClearDownReason (Public Method) - -DESCRIPTION: - Clear mDownReason and may turn carrier on - -PARAMETERS - pDev [ I ] - Device specific memory - reason [ I ] - Reason device is no longer down - -RETURN VALUE: - None -===========================================================================*/ -void QuecGobiClearDownReason( - sGobiUSBNet * pDev, - u8 reason ) -{ - clear_bit( reason, &pDev->mDownReason ); - - DBG("%s reason=%d, mDownReason=%x\n", __func__, reason, (unsigned)pDev->mDownReason); -#if 0 //(LINUX_VERSION_CODE >= KERNEL_VERSION( 3,11,0 )) - netif_carrier_on( pDev->mpNetDev->net ); -#else - if (pDev->mDownReason == 0) - { -#ifdef QUECTEL_WWAN_QMAP - if (pDev->qmap_mode && !pDev->link_state) - ; - else -#endif - netif_carrier_on( pDev->mpNetDev->net ); - } -#endif -} - -/*=========================================================================== -METHOD: - GobiTestDownReason (Public Method) - -DESCRIPTION: - Test mDownReason and returns whether reason is set - -PARAMETERS - pDev [ I ] - Device specific memory - reason [ I ] - Reason device is down - -RETURN VALUE: - bool -===========================================================================*/ -bool QuecGobiTestDownReason( - sGobiUSBNet * pDev, - u8 reason ) -{ - return test_bit( reason, &pDev->mDownReason ); -} - -/*=========================================================================*/ -// Driver level asynchronous read functions -/*=========================================================================*/ - -/*=========================================================================== -METHOD: - ResubmitIntURB (Public Method) - -DESCRIPTION: - Resubmit interrupt URB, re-using same values - -PARAMETERS - pIntURB [ I ] - Interrupt URB - -RETURN VALUE: - int - 0 for success - negative errno for failure -===========================================================================*/ -static int ResubmitIntURB( struct urb * pIntURB ) -{ - int status; - int interval; - - // Sanity test - if ( (pIntURB == NULL) - || (pIntURB->dev == NULL) ) - { - return -EINVAL; - } - - // Interval needs reset after every URB completion -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,22 )) - interval = max((int)(pIntURB->ep->desc.bInterval), - (pIntURB->dev->speed == USB_SPEED_HIGH) ? 7 : 3); -#else - interval = s_interval; -#endif - - // Reschedule interrupt URB - usb_fill_int_urb( pIntURB, - pIntURB->dev, - pIntURB->pipe, - pIntURB->transfer_buffer, - pIntURB->transfer_buffer_length, - pIntURB->complete, - pIntURB->context, - interval ); - status = usb_submit_urb( pIntURB, GFP_ATOMIC ); - if (status != 0) - { - DBG( "Error re-submitting Int URB %d\n", status ); - } - - return status; -} - - -#ifdef QUECTEL_QMI_MERGE -static int MergeRecQmiMsg( sQMIDev * pQMIDev, struct urb * pReadURB ) -{ - sQMIMsgHeader * mHeader; - sQMIMsgPacket * mPacket; - - DBG( "%s called \n", __func__ ); - mPacket = pQMIDev->mpQmiMsgPacket; - - if(pReadURB->actual_length < sizeof(sQMIMsgHeader)) - { - return -1; - } - - mHeader = (sQMIMsgHeader *)pReadURB->transfer_buffer; - if(le16_to_cpu(mHeader->idenity) != MERGE_PACKET_IDENTITY || le16_to_cpu(mHeader->version) != MERGE_PACKET_VERSION || le16_to_cpu(mHeader->cur_len) > le16_to_cpu(mHeader->total_len)) - return -1; - - if(le16_to_cpu(mHeader->cur_len) == le16_to_cpu(mHeader->total_len)) { - mPacket->len = le16_to_cpu(mHeader->total_len); - memcpy(pReadURB->transfer_buffer, pReadURB->transfer_buffer + sizeof(sQMIMsgHeader), mPacket->len); - pReadURB->actual_length = mPacket->len; - mPacket->len = 0; - - return 0; - } - - memcpy(mPacket->buf + mPacket->len, pReadURB->transfer_buffer + sizeof(sQMIMsgHeader), le16_to_cpu(mHeader->cur_len)); - mPacket->len += le16_to_cpu(mHeader->cur_len); - - if (le16_to_cpu(mHeader->cur_len) < MERGE_PACKET_MAX_PAYLOAD_SIZE || mPacket->len >= le16_to_cpu(mHeader->total_len)) { - memcpy(pReadURB->transfer_buffer, mPacket->buf, mPacket->len); - pReadURB->actual_length = mPacket->len; - mPacket->len = 0; - return 0; - } - - return -1; -} -#endif - -/*=========================================================================== -METHOD: - ReadCallback (Public Method) - -DESCRIPTION: - Put the data in storage and notify anyone waiting for data - -PARAMETERS - pReadURB [ I ] - URB this callback is run for - -RETURN VALUE: - None -===========================================================================*/ -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,18 )) -static void ReadCallback( struct urb * pReadURB ) -#else -static void ReadCallback(struct urb *pReadURB, struct pt_regs *regs) -#endif -{ - int result; - u16 clientID; - sClientMemList * pClientMem; - void * pData; - void * pDataCopy; - u16 dataSize; - sGobiUSBNet * pDev; - unsigned long flags; - u16 transactionID; - - if (pReadURB == NULL) - { - DBG( "bad read URB\n" ); - return; - } - - pDev = pReadURB->context; - if (IsDeviceValid( pDev ) == false) - { - DBG( "Invalid device!\n" ); - return; - } - -#ifdef READ_QMI_URB_ERROR - del_timer(&pDev->mQMIDev.mReadUrbTimer); - if ((pReadURB->status == -ECONNRESET) && (pReadURB->actual_length > 0)) - pReadURB->status = 0; -#endif - - if (pReadURB->status != 0) - { - DBG( "Read status = %d\n", pReadURB->status ); - - // Resubmit the interrupt URB - ResubmitIntURB( pDev->mQMIDev.mpIntURB ); - - return; - } - DBG( "Read %d bytes\n", pReadURB->actual_length ); - -#ifdef QUECTEL_QMI_MERGE - if(MergeRecQmiMsg(&pDev->mQMIDev, pReadURB)) - { - DBG( "not a full packet, read again\n"); - // Resubmit the interrupt URB - ResubmitIntURB( pDev->mQMIDev.mpIntURB ); - return; - } -#endif - - pData = pReadURB->transfer_buffer; - dataSize = pReadURB->actual_length; - - PrintHex( pData, dataSize ); - -#ifdef READ_QMI_URB_ERROR - if (dataSize < (le16_to_cpu(get_unaligned((u16*)(pData + 1))) + 1)) { - dataSize = (le16_to_cpu(get_unaligned((u16*)(pData + 1))) + 1); - memset(pReadURB->transfer_buffer + pReadURB->actual_length, 0x00, dataSize - pReadURB->actual_length); - INFO( "Read %d / %d bytes\n", pReadURB->actual_length, dataSize); - } -#endif - - result = ParseQMUX( &clientID, - pData, - dataSize ); - if (result < 0) - { - DBG( "Read error parsing QMUX %d\n", result ); - - // Resubmit the interrupt URB - ResubmitIntURB( pDev->mQMIDev.mpIntURB ); - - return; - } - - // Grab transaction ID - - // Data large enough? - if (dataSize < result + 3) - { - DBG( "Data buffer too small to parse\n" ); - - // Resubmit the interrupt URB - ResubmitIntURB( pDev->mQMIDev.mpIntURB ); - - return; - } - - // Transaction ID size is 1 for QMICTL, 2 for others - if (clientID == QMICTL) - { - transactionID = *(u8*)(pData + result + 1); - } - else - { - transactionID = le16_to_cpu( get_unaligned((u16*)(pData + result + 1)) ); - } - - // Critical section - spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags ); - - // Find memory storage for this service and Client ID - // Not using FindClientMem because it can't handle broadcasts - pClientMem = pDev->mQMIDev.mpClientMemList; - - while (pClientMem != NULL) - { - if (pClientMem->mClientID == clientID - || (pClientMem->mClientID | 0xff00) == clientID) - { - // Make copy of pData - pDataCopy = kmalloc( dataSize, GFP_ATOMIC ); - if (pDataCopy == NULL) - { - DBG( "Error allocating client data memory\n" ); - - // End critical section - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); - - // Resubmit the interrupt URB - ResubmitIntURB( pDev->mQMIDev.mpIntURB ); - - return; - } - - memcpy( pDataCopy, pData, dataSize ); - - if (AddToReadMemList( pDev, - pClientMem->mClientID, - transactionID, - pDataCopy, - dataSize ) == false) - { - DBG( "Error allocating pReadMemListEntry " - "read will be discarded\n" ); - kfree( pDataCopy ); - - // End critical section - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); - - // Resubmit the interrupt URB - ResubmitIntURB( pDev->mQMIDev.mpIntURB ); - - return; - } - - // Success - VDBG( "Creating new readListEntry for client 0x%04X, TID %x\n", - clientID, - transactionID ); - - // Notify this client data exists - NotifyAndPopNotifyList( pDev, - pClientMem->mClientID, - transactionID ); - - // Possibly notify poll() that data exists - wake_up_interruptible_sync( &pClientMem->mWaitQueue ); - - // Not a broadcast - if (clientID >> 8 != 0xff) - { - break; - } - } - - // Next element - pClientMem = pClientMem->mpNext; - } - - // End critical section - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); - - // Resubmit the interrupt URB - ResubmitIntURB( pDev->mQMIDev.mpIntURB ); -} - -/*=========================================================================== -METHOD: - IntCallback (Public Method) - -DESCRIPTION: - Data is available, fire off a read URB - -PARAMETERS - pIntURB [ I ] - URB this callback is run for - -RETURN VALUE: - None -===========================================================================*/ -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,18 )) -static void IntCallback( struct urb * pIntURB ) -{ -#else -static void IntCallback(struct urb *pIntURB, struct pt_regs *regs) -{ -#endif - int status; - struct usb_cdc_notification *dr; - - sGobiUSBNet * pDev = (sGobiUSBNet *)pIntURB->context; - dr = (struct usb_cdc_notification *)pDev->mQMIDev.mpIntBuffer; - - if (IsDeviceValid( pDev ) == false) - { - DBG( "Invalid device!\n" ); - return; - } - - // Verify this was a normal interrupt - if (pIntURB->status != 0) - { - DBG( "IntCallback: Int status = %d\n", pIntURB->status ); - - // Ignore EOVERFLOW errors - if (pIntURB->status != -EOVERFLOW) - { - // Read 'thread' dies here - return; - } - } - else - { - //TODO cast transfer_buffer to struct usb_cdc_notification - - VDBG( "IntCallback: Encapsulated Response = 0x%llx\n", - (*(u64*)pIntURB->transfer_buffer)); - - switch (dr->bNotificationType) { - case USB_CDC_NOTIFY_RESPONSE_AVAILABLE: //0x01 - { - // Time to read - usb_fill_control_urb( pDev->mQMIDev.mpReadURB, - pDev->mpNetDev->udev, - usb_rcvctrlpipe( pDev->mpNetDev->udev, 0 ), - (unsigned char *)pDev->mQMIDev.mpReadSetupPacket, - pDev->mQMIDev.mpReadBuffer, - DEFAULT_READ_URB_LENGTH, - ReadCallback, - pDev ); - #ifdef READ_QMI_URB_ERROR - mod_timer( &pDev->mQMIDev.mReadUrbTimer, jiffies + msecs_to_jiffies(300) ); - #endif - status = usb_submit_urb( pDev->mQMIDev.mpReadURB, GFP_ATOMIC ); - if (status != 0) - { - DBG("Error submitting Read URB %d\n", status); - // Resubmit the interrupt urb - ResubmitIntURB(pIntURB); - return; - } - - // Int URB will be resubmitted during ReadCallback - return; - } - case USB_CDC_NOTIFY_SPEED_CHANGE: //0x2a - { - DBG( "IntCallback: Connection Speed Change = 0x%llx\n", - (*(u64*)pIntURB->transfer_buffer)); - - // if upstream or downstream is 0, stop traffic. Otherwise resume it - if ((*(u32*)(pIntURB->transfer_buffer + 8) == 0) - || (*(u32*)(pIntURB->transfer_buffer + 12) == 0)) - { - GobiSetDownReason( pDev, CDC_CONNECTION_SPEED ); - DBG( "traffic stopping due to CONNECTION_SPEED_CHANGE\n" ); - } - else - { - GobiClearDownReason( pDev, CDC_CONNECTION_SPEED ); - DBG( "resuming traffic due to CONNECTION_SPEED_CHANGE\n" ); - } - } - break; - default: - { - DBG( "ignoring invalid interrupt in packet\n" ); - PrintHex( pIntURB->transfer_buffer, pIntURB->actual_length ); - } - } - - // Resubmit the interrupt urb - ResubmitIntURB( pIntURB ); - - return; - } -} - -#ifdef READ_QMI_URB_ERROR -static void ReadUrbTimerFunc( struct urb * pReadURB ) -{ - int result; - - INFO( "%s called (%ld).\n", __func__, jiffies ); - - if ((pReadURB != NULL) && (pReadURB->status == -EINPROGRESS)) - { - // Asynchronously unlink URB. On success, -EINPROGRESS will be returned, - // URB status will be set to -ECONNRESET, and ReadCallback() executed - result = usb_unlink_urb( pReadURB ); - INFO( "%s called usb_unlink_urb, result = %d\n", __func__, result); - } -} -#endif - -/*=========================================================================== -METHOD: - StartRead (Public Method) - -DESCRIPTION: - Start continuous read "thread" (callback driven) - - Note: In case of error, KillRead() should be run - to remove urbs and clean up memory. - -PARAMETERS: - pDev [ I ] - Device specific memory - -RETURN VALUE: - int - 0 for success - negative errno for failure -===========================================================================*/ -int QuecStartRead( sGobiUSBNet * pDev ) -{ - int interval; - struct usb_endpoint_descriptor *pendp; - - if (IsDeviceValid( pDev ) == false) - { - DBG( "Invalid device!\n" ); - return -ENXIO; - } - - // Allocate URB buffers - pDev->mQMIDev.mpReadURB = usb_alloc_urb( 0, GFP_KERNEL ); - if (pDev->mQMIDev.mpReadURB == NULL) - { - DBG( "Error allocating read urb\n" ); - return -ENOMEM; - } - -#ifdef READ_QMI_URB_ERROR - setup_timer( &pDev->mQMIDev.mReadUrbTimer, (void*)ReadUrbTimerFunc, (unsigned long)pDev->mQMIDev.mpReadURB ); -#endif - - pDev->mQMIDev.mpIntURB = usb_alloc_urb( 0, GFP_KERNEL ); - if (pDev->mQMIDev.mpIntURB == NULL) - { - DBG( "Error allocating int urb\n" ); - usb_free_urb( pDev->mQMIDev.mpReadURB ); - pDev->mQMIDev.mpReadURB = NULL; - return -ENOMEM; - } - - // Create data buffers - pDev->mQMIDev.mpReadBuffer = kmalloc( DEFAULT_READ_URB_LENGTH, GFP_KERNEL ); - if (pDev->mQMIDev.mpReadBuffer == NULL) - { - DBG( "Error allocating read buffer\n" ); - usb_free_urb( pDev->mQMIDev.mpIntURB ); - pDev->mQMIDev.mpIntURB = NULL; - usb_free_urb( pDev->mQMIDev.mpReadURB ); - pDev->mQMIDev.mpReadURB = NULL; - return -ENOMEM; - } - - pDev->mQMIDev.mpIntBuffer = kmalloc( 64, GFP_KERNEL ); - if (pDev->mQMIDev.mpIntBuffer == NULL) - { - DBG( "Error allocating int buffer\n" ); - kfree( pDev->mQMIDev.mpReadBuffer ); - pDev->mQMIDev.mpReadBuffer = NULL; - usb_free_urb( pDev->mQMIDev.mpIntURB ); - pDev->mQMIDev.mpIntURB = NULL; - usb_free_urb( pDev->mQMIDev.mpReadURB ); - pDev->mQMIDev.mpReadURB = NULL; - return -ENOMEM; - } - - pDev->mQMIDev.mpReadSetupPacket = kmalloc( sizeof( sURBSetupPacket ), - GFP_KERNEL ); - if (pDev->mQMIDev.mpReadSetupPacket == NULL) - { - DBG( "Error allocating setup packet buffer\n" ); - kfree( pDev->mQMIDev.mpIntBuffer ); - pDev->mQMIDev.mpIntBuffer = NULL; - kfree( pDev->mQMIDev.mpReadBuffer ); - pDev->mQMIDev.mpReadBuffer = NULL; - usb_free_urb( pDev->mQMIDev.mpIntURB ); - pDev->mQMIDev.mpIntURB = NULL; - usb_free_urb( pDev->mQMIDev.mpReadURB ); - pDev->mQMIDev.mpReadURB = NULL; - return -ENOMEM; - } - - // CDC Get Encapsulated Response packet - pDev->mQMIDev.mpReadSetupPacket->mRequestType = 0xA1; - pDev->mQMIDev.mpReadSetupPacket->mRequestCode = 1; - pDev->mQMIDev.mpReadSetupPacket->mValue = 0; - pDev->mQMIDev.mpReadSetupPacket->mIndex = - cpu_to_le16(pDev->mpIntf->cur_altsetting->desc.bInterfaceNumber); /* interface number */ - pDev->mQMIDev.mpReadSetupPacket->mLength = cpu_to_le16(DEFAULT_READ_URB_LENGTH); - - pendp = GetEndpoint(pDev->mpIntf, USB_ENDPOINT_XFER_INT, USB_DIR_IN); - if (pendp == NULL) - { - DBG( "Invalid interrupt endpoint!\n" ); - kfree(pDev->mQMIDev.mpReadSetupPacket); - pDev->mQMIDev.mpReadSetupPacket = NULL; - kfree( pDev->mQMIDev.mpIntBuffer ); - pDev->mQMIDev.mpIntBuffer = NULL; - kfree( pDev->mQMIDev.mpReadBuffer ); - pDev->mQMIDev.mpReadBuffer = NULL; - usb_free_urb( pDev->mQMIDev.mpIntURB ); - pDev->mQMIDev.mpIntURB = NULL; - usb_free_urb( pDev->mQMIDev.mpReadURB ); - pDev->mQMIDev.mpReadURB = NULL; - return -ENXIO; - } - -#ifdef QUECTEL_QMI_MERGE - pDev->mQMIDev.mpQmiMsgPacket = kmalloc( sizeof(sQMIMsgPacket), GFP_KERNEL ); - if (pDev->mQMIDev.mpQmiMsgPacket == NULL) - { - DBG( "Error allocating qmi msg merge packet buffer!\n" ); - kfree(pDev->mQMIDev.mpReadSetupPacket); - pDev->mQMIDev.mpReadSetupPacket = NULL; - kfree( pDev->mQMIDev.mpIntBuffer ); - pDev->mQMIDev.mpIntBuffer = NULL; - kfree( pDev->mQMIDev.mpReadBuffer ); - pDev->mQMIDev.mpReadBuffer = NULL; - usb_free_urb( pDev->mQMIDev.mpIntURB ); - pDev->mQMIDev.mpIntURB = NULL; - usb_free_urb( pDev->mQMIDev.mpReadURB ); - pDev->mQMIDev.mpReadURB = NULL; - return -ENOMEM; - } -#endif - - // Interval needs reset after every URB completion - interval = max((int)(pendp->bInterval), - (pDev->mpNetDev->udev->speed == USB_SPEED_HIGH) ? 7 : 3); -#if (LINUX_VERSION_CODE <= KERNEL_VERSION( 2,6,22 )) - s_interval = interval; -#endif - - // Schedule interrupt URB - usb_fill_int_urb( pDev->mQMIDev.mpIntURB, - pDev->mpNetDev->udev, - /* QMI interrupt endpoint for the following - * interface configuration: DM, NMEA, MDM, NET - */ - usb_rcvintpipe( pDev->mpNetDev->udev, - pendp->bEndpointAddress), - pDev->mQMIDev.mpIntBuffer, - min((int)le16_to_cpu(pendp->wMaxPacketSize), 64), - IntCallback, - pDev, - interval ); - return usb_submit_urb( pDev->mQMIDev.mpIntURB, GFP_KERNEL ); -} - -/*=========================================================================== -METHOD: - KillRead (Public Method) - -DESCRIPTION: - Kill continuous read "thread" - -PARAMETERS: - pDev [ I ] - Device specific memory - -RETURN VALUE: - None -===========================================================================*/ -void QuecKillRead( sGobiUSBNet * pDev ) -{ - // Stop reading - if (pDev->mQMIDev.mpReadURB != NULL) - { - DBG( "Killng read URB\n" ); - usb_kill_urb( pDev->mQMIDev.mpReadURB ); - } - - if (pDev->mQMIDev.mpIntURB != NULL) - { - DBG( "Killng int URB\n" ); - usb_kill_urb( pDev->mQMIDev.mpIntURB ); - } - - // Release buffers - kfree( pDev->mQMIDev.mpReadSetupPacket ); - pDev->mQMIDev.mpReadSetupPacket = NULL; - kfree( pDev->mQMIDev.mpReadBuffer ); - pDev->mQMIDev.mpReadBuffer = NULL; - kfree( pDev->mQMIDev.mpIntBuffer ); - pDev->mQMIDev.mpIntBuffer = NULL; - - // Release URB's - usb_free_urb( pDev->mQMIDev.mpReadURB ); - pDev->mQMIDev.mpReadURB = NULL; - usb_free_urb( pDev->mQMIDev.mpIntURB ); - pDev->mQMIDev.mpIntURB = NULL; - -#ifdef QUECTEL_QMI_MERGE - kfree( pDev->mQMIDev.mpQmiMsgPacket ); - pDev->mQMIDev.mpQmiMsgPacket = NULL; -#endif -} - -/*=========================================================================*/ -// Internal read/write functions -/*=========================================================================*/ - -/*=========================================================================== -METHOD: - ReadAsync (Public Method) - -DESCRIPTION: - Start asynchronous read - NOTE: Reading client's data store, not device - -PARAMETERS: - pDev [ I ] - Device specific memory - clientID [ I ] - Requester's client ID - transactionID [ I ] - Transaction ID or 0 for any - pCallback [ I ] - Callback to be executed when data is available - pData [ I ] - Data buffer that willl be passed (unmodified) - to callback - -RETURN VALUE: - int - 0 for success - negative errno for failure -===========================================================================*/ -static int ReadAsync( - sGobiUSBNet * pDev, - u16 clientID, - u16 transactionID, - void (*pCallback)(sGobiUSBNet*, u16, void *), - void * pData ) -{ - sClientMemList * pClientMem; - sReadMemList ** ppReadMemList; - - unsigned long flags; - - if (IsDeviceValid( pDev ) == false) - { - DBG( "Invalid device!\n" ); - return -ENXIO; - } - - // Critical section - spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags ); - - // Find memory storage for this client ID - pClientMem = FindClientMem( pDev, clientID ); - if (pClientMem == NULL) - { - DBG( "Could not find matching client ID 0x%04X\n", - clientID ); - - // End critical section - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); - return -ENXIO; - } - - ppReadMemList = &(pClientMem->mpList); - - // Does data already exist? - while (*ppReadMemList != NULL) - { - // Is this element our data? - if (transactionID == 0 - || transactionID == (*ppReadMemList)->mTransactionID) - { - // End critical section - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); - - // Run our own callback - pCallback( pDev, clientID, pData ); - - return 0; - } - - // Next - ppReadMemList = &(*ppReadMemList)->mpNext; - } - - // Data not found, add ourself to list of waiters - if (AddToNotifyList( pDev, - clientID, - transactionID, - pCallback, - pData ) == false) - { - DBG( "Unable to register for notification\n" ); - } - - // End critical section - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); - - // Success - return 0; -} - -/*=========================================================================== -METHOD: - UpSem (Public Method) - -DESCRIPTION: - Notification function for synchronous read - -PARAMETERS: - pDev [ I ] - Device specific memory - clientID [ I ] - Requester's client ID - pData [ I ] - Buffer that holds semaphore to be up()-ed - -RETURN VALUE: - None -===========================================================================*/ -#define QUEC_SEM_MAGIC 0x12345678 -struct QuecSem { - struct semaphore readSem; - int magic; -}; - -static void UpSem( - sGobiUSBNet * pDev, - u16 clientID, - void * pData ) -{ - struct QuecSem *pSem = (struct QuecSem *)pData; - - VDBG( "0x%04X\n", clientID ); - - if (pSem->magic == QUEC_SEM_MAGIC) - up( &(pSem->readSem) ); - else - kfree(pSem); - return; -} - -/*=========================================================================== -METHOD: - ReadSync (Public Method) - -DESCRIPTION: - Start synchronous read - NOTE: Reading client's data store, not device - -PARAMETERS: - pDev [ I ] - Device specific memory - ppOutBuffer [I/O] - On success, will be filled with a - pointer to read buffer - clientID [ I ] - Requester's client ID - transactionID [ I ] - Transaction ID or 0 for any - -RETURN VALUE: - int - size of data read for success - negative errno for failure -===========================================================================*/ -static int ReadSync( - sGobiUSBNet * pDev, - void ** ppOutBuffer, - u16 clientID, - u16 transactionID ) -{ - int result; - sClientMemList * pClientMem; - sNotifyList ** ppNotifyList, * pDelNotifyListEntry; - struct QuecSem readSem; - void * pData; - unsigned long flags; - u16 dataSize; - - if (IsDeviceValid( pDev ) == false) - { - DBG( "Invalid device!\n" ); - return -ENXIO; - } - - // Critical section - spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags ); - - // Find memory storage for this Client ID - pClientMem = FindClientMem( pDev, clientID ); - if (pClientMem == NULL) - { - DBG( "Could not find matching client ID 0x%04X\n", - clientID ); - - // End critical section - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); - return -ENXIO; - } - - // Note: in cases where read is interrupted, - // this will verify client is still valid - while (PopFromReadMemList( pDev, - clientID, - transactionID, - &pData, - &dataSize ) == false) - { - // Data does not yet exist, wait - sema_init( &readSem.readSem, 0 ); - readSem.magic = QUEC_SEM_MAGIC; - - // Add ourself to list of waiters - if (AddToNotifyList( pDev, - clientID, - transactionID, - UpSem, - &readSem ) == false) - { - DBG( "unable to register for notification\n" ); - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); - return -EFAULT; - } - - // End critical section while we block - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); - - // Wait for notification - result = down_interruptible( &readSem.readSem ); - //if (result) INFO("down_interruptible = %d\n", result); - if (result == -EINTR) { - result = down_timeout(&readSem.readSem, msecs_to_jiffies(200)); - //if (result) INFO("down_timeout = %d\n", result); - } - if (result != 0) - { - DBG( "Down Timeout %d\n", result ); - - // readSem will fall out of scope, - // remove from notify list so it's not referenced - spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags ); - ppNotifyList = &(pClientMem->mpReadNotifyList); - pDelNotifyListEntry = NULL; - - // Find and delete matching entry - while (*ppNotifyList != NULL) - { - if ((*ppNotifyList)->mpData == &readSem) - { - pDelNotifyListEntry = *ppNotifyList; - *ppNotifyList = (*ppNotifyList)->mpNext; - kfree( pDelNotifyListEntry ); - break; - } - - // Next - ppNotifyList = &(*ppNotifyList)->mpNext; - } - - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); - return -EINTR; - } - - // Verify device is still valid - if (IsDeviceValid( pDev ) == false) - { - DBG( "Invalid device!\n" ); - return -ENXIO; - } - - // Restart critical section and continue loop - spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags ); - } - - // End Critical section - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); - - // Success - *ppOutBuffer = pData; - - return dataSize; -} - -/*=========================================================================== -METHOD: - WriteSyncCallback (Public Method) - -DESCRIPTION: - Write callback - -PARAMETERS - pWriteURB [ I ] - URB this callback is run for - -RETURN VALUE: - None -===========================================================================*/ -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,18 )) -static void WriteSyncCallback( struct urb * pWriteURB ) -#else -static void WriteSyncCallback(struct urb *pWriteURB, struct pt_regs *regs) -#endif -{ - if (pWriteURB == NULL) - { - DBG( "null urb\n" ); - return; - } - - DBG( "Write status/size %d/%d\n", - pWriteURB->status, - pWriteURB->actual_length ); - - // Notify that write has completed by up()-ing semeaphore - up( (struct semaphore * )pWriteURB->context ); - - return; -} - -/*=========================================================================== -METHOD: - WriteSync (Public Method) - -DESCRIPTION: - Start synchronous write - -PARAMETERS: - pDev [ I ] - Device specific memory - pWriteBuffer [ I ] - Data to be written - writeBufferSize [ I ] - Size of data to be written - clientID [ I ] - Client ID of requester - -RETURN VALUE: - int - write size (includes QMUX) - negative errno for failure -===========================================================================*/ -static int WriteSync( - sGobiUSBNet * pDev, - char * pWriteBuffer, - int writeBufferSize, - u16 clientID ) -{ - int result; - struct semaphore writeSem; - struct urb * pWriteURB; - sURBSetupPacket *writeSetup; - unsigned long flags; - - if (IsDeviceValid( pDev ) == false) - { - DBG( "Invalid device!\n" ); - return -ENXIO; - } - - pWriteURB = usb_alloc_urb( 0, GFP_KERNEL ); - if (pWriteURB == NULL) - { - DBG( "URB mem error\n" ); - return -ENOMEM; - } - - // Fill writeBuffer with QMUX - result = FillQMUX( clientID, pWriteBuffer, writeBufferSize ); - if (result < 0) - { - usb_free_urb( pWriteURB ); - return result; - } - - // CDC Send Encapsulated Request packet - writeSetup = kmalloc(sizeof(sURBSetupPacket), GFP_KERNEL); - writeSetup->mRequestType = 0x21; - writeSetup->mRequestCode = 0; - writeSetup->mValue = 0; - writeSetup->mIndex = cpu_to_le16(pDev->mpIntf->cur_altsetting->desc.bInterfaceNumber); - writeSetup->mLength = cpu_to_le16(writeBufferSize); - - // Create URB - usb_fill_control_urb( pWriteURB, - pDev->mpNetDev->udev, - usb_sndctrlpipe( pDev->mpNetDev->udev, 0 ), - (unsigned char *)writeSetup, - (void*)pWriteBuffer, - writeBufferSize, - NULL, - pDev ); - - DBG( "Actual Write:\n" ); - PrintHex( pWriteBuffer, writeBufferSize ); - - sema_init( &writeSem, 0 ); - - pWriteURB->complete = WriteSyncCallback; - pWriteURB->context = &writeSem; - - // Wake device - result = usb_autopm_get_interface( pDev->mpIntf ); - if (result < 0) - { - DBG( "unable to resume interface: %d\n", result ); - - // Likely caused by device going from autosuspend -> full suspend - if (result == -EPERM) - { -#ifdef CONFIG_PM -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,33 )) -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,18 )) - pDev->mpNetDev->udev->auto_pm = 0; -#endif -#endif - QuecGobiNetSuspend( pDev->mpIntf, PMSG_SUSPEND ); -#endif /* CONFIG_PM */ - } - usb_free_urb( pWriteURB ); - kfree(writeSetup); - - return result; - } - - // Critical section - spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags ); - - if (AddToURBList( pDev, clientID, pWriteURB ) == false) - { - usb_free_urb( pWriteURB ); - kfree(writeSetup); - - // End critical section - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); - usb_autopm_put_interface( pDev->mpIntf ); - return -EINVAL; - } - - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); - result = usb_submit_urb( pWriteURB, GFP_KERNEL ); - spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags ); - - if (result < 0) - { - DBG( "submit URB error %d\n", result ); - - // Get URB back so we can destroy it - if (PopFromURBList( pDev, clientID ) != pWriteURB) - { - // This shouldn't happen - DBG( "Didn't get write URB back\n" ); - //advoid ReleaseClientID() free again (no PopFromURBList) - } - else - { - usb_free_urb( pWriteURB ); - kfree(writeSetup); - } - - // End critical section - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); - usb_autopm_put_interface( pDev->mpIntf ); - return result; - } - - // End critical section while we block - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); - - // Wait for write to finish - if (1 != 0) //(interruptible != 0) - { - // Allow user interrupts - result = down_interruptible( &writeSem ); - //if (result) INFO("down_interruptible = %d\n", result); - if (result == -EINTR) { - result = down_timeout(&writeSem, msecs_to_jiffies(200)); - //if (result) INFO("down_interruptible = %d\n", result); - } - } - else - { - // Ignore user interrupts - result = 0; - down( &writeSem ); - } - - // Write is done, release device - usb_autopm_put_interface( pDev->mpIntf ); - - // Verify device is still valid - if (IsDeviceValid( pDev ) == false) - { - DBG( "Invalid device!\n" ); - - usb_kill_urb( pWriteURB ); -#if 0 //advoid ReleaseClientID() free again (no PopFromURBList) - usb_free_urb( pWriteURB ); - kfree(writeSetup); -#endif - return -ENXIO; - } - - // Restart critical section - spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags ); - - // Get URB back so we can destroy it - if (PopFromURBList( pDev, clientID ) != pWriteURB) - { - // This shouldn't happen - DBG( "Didn't get write URB back\n" ); - - // End critical section - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); - usb_kill_urb( pWriteURB ); -#if 0 //advoid ReleaseClientID() free again (fail PopFromURBList) - usb_free_urb( pWriteURB ); - kfree(writeSetup); -#endif - return -EINVAL; - } - - // End critical section - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); - - if (result == 0) - { - // Write is finished - if (pWriteURB->status == 0) - { - // Return number of bytes that were supposed to have been written, - // not size of QMI request - result = writeBufferSize; - } - else - { - DBG( "bad status = %d\n", pWriteURB->status ); - - // Return error value - result = pWriteURB->status; - } - } - else - { - // We have been forcibly interrupted - DBG( "Interrupted %d !!!\n", result ); - DBG( "Device may be in bad state and need reset !!!\n" ); - - // URB has not finished - usb_kill_urb( pWriteURB ); - } - - usb_free_urb( pWriteURB ); - kfree(writeSetup); - - return result; -} - -/*=========================================================================*/ -// Internal memory management functions -/*=========================================================================*/ - -/*=========================================================================== -METHOD: - GetClientID (Public Method) - -DESCRIPTION: - Request a QMI client for the input service type and initialize memory - structure - -PARAMETERS: - pDev [ I ] - Device specific memory - serviceType [ I ] - Desired QMI service type - -RETURN VALUE: - int - Client ID for success (positive) - Negative errno for error -===========================================================================*/ -static int GetClientID( - sGobiUSBNet * pDev, - u8 serviceType ) -{ - u16 clientID; - sClientMemList ** ppClientMem; - int result; - void * pWriteBuffer; - u16 writeBufferSize; - void * pReadBuffer; - u16 readBufferSize; - unsigned long flags; - u8 transactionID; - - if (IsDeviceValid( pDev ) == false) - { - DBG( "Invalid device!\n" ); - return -ENXIO; - } - - // Run QMI request to be asigned a Client ID - if (serviceType != 0) - { - writeBufferSize = QMICTLGetClientIDReqSize(); - pWriteBuffer = kmalloc( writeBufferSize, GFP_KERNEL ); - if (pWriteBuffer == NULL) - { - return -ENOMEM; - } - - transactionID = QMIXactionIDGet( pDev ); - - result = QMICTLGetClientIDReq( pWriteBuffer, - writeBufferSize, - transactionID, - serviceType ); - if (result < 0) - { - kfree( pWriteBuffer ); - return result; - } - - - result = WriteSync( pDev, - pWriteBuffer, - writeBufferSize, - QMICTL ); - kfree( pWriteBuffer ); - - if (result < 0) - { - return result; - } - - result = ReadSync( pDev, - &pReadBuffer, - QMICTL, - transactionID ); - if (result < 0) - { - DBG( "bad read data %d\n", result ); - return result; - } - readBufferSize = result; - - result = QMICTLGetClientIDResp( pReadBuffer, - readBufferSize, - &clientID ); - - /* Upon return from QMICTLGetClientIDResp, clientID - * low address contains the Service Number (SN), and - * clientID high address contains Client Number (CN) - * For the ReadCallback to function correctly,we swap - * the SN and CN on a Big Endian architecture. - */ - clientID = le16_to_cpu(clientID); - - kfree( pReadBuffer ); - - if (result < 0) - { - return result; - } - } - else - { - // QMI CTL will always have client ID 0 - clientID = 0; - } - - // Critical section - spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags ); - - // Verify client is not already allocated - if (FindClientMem( pDev, clientID ) != NULL) - { - DBG( "Client memory already exists\n" ); - - // End Critical section - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); - return -ETOOMANYREFS; - } - - // Go to last entry in client mem list - ppClientMem = &pDev->mQMIDev.mpClientMemList; - while (*ppClientMem != NULL) - { - ppClientMem = &(*ppClientMem)->mpNext; - } - - // Create locations for read to place data into - *ppClientMem = kmalloc( sizeof( sClientMemList ), GFP_ATOMIC ); - if (*ppClientMem == NULL) - { - DBG( "Error allocating read list\n" ); - - // End critical section - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); - return -ENOMEM; - } - - (*ppClientMem)->mClientID = clientID; - (*ppClientMem)->mpList = NULL; - (*ppClientMem)->mpReadNotifyList = NULL; - (*ppClientMem)->mpURBList = NULL; - (*ppClientMem)->mpNext = NULL; - - // Initialize workqueue for poll() - init_waitqueue_head( &(*ppClientMem)->mWaitQueue ); - - // End Critical section - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); - - return (int)( (*ppClientMem)->mClientID ); -} - -/*=========================================================================== -METHOD: - ReleaseClientID (Public Method) - -DESCRIPTION: - Release QMI client and free memory - -PARAMETERS: - pDev [ I ] - Device specific memory - clientID [ I ] - Requester's client ID - -RETURN VALUE: - None -===========================================================================*/ -static void ReleaseClientID( - sGobiUSBNet * pDev, - u16 clientID ) -{ - int result; - sClientMemList ** ppDelClientMem; - sClientMemList * pNextClientMem; - struct urb * pDelURB; - void * pDelData; - u16 dataSize; - void * pWriteBuffer; - u16 writeBufferSize; - void * pReadBuffer; - u16 readBufferSize; - unsigned long flags; - u8 transactionID; - - // Is device is still valid? - if (IsDeviceValid( pDev ) == false) - { - DBG( "invalid device\n" ); - return; - } - - DBG( "releasing 0x%04X\n", clientID ); - - // Run QMI ReleaseClientID if this isn't QMICTL - if (clientID != QMICTL && pDev->mpNetDev->udev->state) - { - // Note: all errors are non fatal, as we always want to delete - // client memory in latter part of function - - writeBufferSize = QMICTLReleaseClientIDReqSize(); - pWriteBuffer = kmalloc( writeBufferSize, GFP_KERNEL ); - if (pWriteBuffer == NULL) - { - DBG( "memory error\n" ); - } - else - { - transactionID = QMIXactionIDGet( pDev ); - - result = QMICTLReleaseClientIDReq( pWriteBuffer, - writeBufferSize, - transactionID, - clientID ); - if (result < 0) - { - kfree( pWriteBuffer ); - DBG( "error %d filling req buffer\n", result ); - } - else - { - result = WriteSync( pDev, - pWriteBuffer, - writeBufferSize, - QMICTL ); - kfree( pWriteBuffer ); - - if (result < 0) - { - DBG( "bad write status %d\n", result ); - } - else - { - result = ReadSync( pDev, - &pReadBuffer, - QMICTL, - transactionID ); - if (result < 0) - { - DBG( "bad read status %d\n", result ); - } - else - { - readBufferSize = result; - - result = QMICTLReleaseClientIDResp( pReadBuffer, - readBufferSize ); - kfree( pReadBuffer ); - - if (result < 0) - { - DBG( "error %d parsing response\n", result ); - } - } - } - } - } - } - - // Cleaning up client memory - - // Critical section - spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags ); - - // Can't use FindClientMem, I need to keep pointer of previous - ppDelClientMem = &pDev->mQMIDev.mpClientMemList; - while (*ppDelClientMem != NULL) - { - if ((*ppDelClientMem)->mClientID == clientID) - { - pNextClientMem = (*ppDelClientMem)->mpNext; - - // Notify all clients - while (NotifyAndPopNotifyList( pDev, - clientID, - 0 ) == true ); - - // Kill and free all URB's - pDelURB = PopFromURBList( pDev, clientID ); - while (pDelURB != NULL) - { - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); - usb_kill_urb( pDelURB ); - usb_free_urb( pDelURB ); - spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags ); - pDelURB = PopFromURBList( pDev, clientID ); - } - - // Free any unread data - while (PopFromReadMemList( pDev, - clientID, - 0, - &pDelData, - &dataSize ) == true ) - { - kfree( pDelData ); - } - - // Delete client Mem - if (!waitqueue_active( &(*ppDelClientMem)->mWaitQueue)) - kfree( *ppDelClientMem ); - else - INFO("memory leak!\n"); - - // Overwrite the pointer that was to this client mem - *ppDelClientMem = pNextClientMem; - } - else - { - // I now point to (a pointer of ((the node I was at)'s mpNext)) - ppDelClientMem = &(*ppDelClientMem)->mpNext; - } - } - - // End Critical section - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); - - return; -} - -/*=========================================================================== -METHOD: - FindClientMem (Public Method) - -DESCRIPTION: - Find this client's memory - - Caller MUST have lock on mClientMemLock - -PARAMETERS: - pDev [ I ] - Device specific memory - clientID [ I ] - Requester's client ID - -RETURN VALUE: - sClientMemList - Pointer to requested sClientMemList for success - NULL for error -===========================================================================*/ -static sClientMemList * FindClientMem( - sGobiUSBNet * pDev, - u16 clientID ) -{ - sClientMemList * pClientMem; - - if (IsDeviceValid( pDev ) == false) - { - DBG( "Invalid device\n" ); - return NULL; - } - -#ifdef CONFIG_SMP - // Verify Lock - if (spin_is_locked( &pDev->mQMIDev.mClientMemLock ) == 0) - { - DBG( "unlocked\n" ); - BUG(); - } -#endif - - pClientMem = pDev->mQMIDev.mpClientMemList; - while (pClientMem != NULL) - { - if (pClientMem->mClientID == clientID) - { - // Success - VDBG("Found client's 0x%x memory\n", clientID); - return pClientMem; - } - - pClientMem = pClientMem->mpNext; - } - - DBG( "Could not find client mem 0x%04X\n", clientID ); - return NULL; -} - -/*=========================================================================== -METHOD: - AddToReadMemList (Public Method) - -DESCRIPTION: - Add Data to this client's ReadMem list - - Caller MUST have lock on mClientMemLock - -PARAMETERS: - pDev [ I ] - Device specific memory - clientID [ I ] - Requester's client ID - transactionID [ I ] - Transaction ID or 0 for any - pData [ I ] - Data to add - dataSize [ I ] - Size of data to add - -RETURN VALUE: - bool -===========================================================================*/ -static bool AddToReadMemList( - sGobiUSBNet * pDev, - u16 clientID, - u16 transactionID, - void * pData, - u16 dataSize ) -{ - sClientMemList * pClientMem; - sReadMemList ** ppThisReadMemList; - -#ifdef CONFIG_SMP - // Verify Lock - if (spin_is_locked( &pDev->mQMIDev.mClientMemLock ) == 0) - { - DBG( "unlocked\n" ); - BUG(); - } -#endif - - // Get this client's memory location - pClientMem = FindClientMem( pDev, clientID ); - if (pClientMem == NULL) - { - DBG( "Could not find this client's memory 0x%04X\n", - clientID ); - - return false; - } - - // Go to last ReadMemList entry - ppThisReadMemList = &pClientMem->mpList; - while (*ppThisReadMemList != NULL) - { - ppThisReadMemList = &(*ppThisReadMemList)->mpNext; - } - - *ppThisReadMemList = kmalloc( sizeof( sReadMemList ), GFP_ATOMIC ); - if (*ppThisReadMemList == NULL) - { - DBG( "Mem error\n" ); - - return false; - } - - (*ppThisReadMemList)->mpNext = NULL; - (*ppThisReadMemList)->mpData = pData; - (*ppThisReadMemList)->mDataSize = dataSize; - (*ppThisReadMemList)->mTransactionID = transactionID; - - return true; -} - -/*=========================================================================== -METHOD: - PopFromReadMemList (Public Method) - -DESCRIPTION: - Remove data from this client's ReadMem list if it matches - the specified transaction ID. - - Caller MUST have lock on mClientMemLock - -PARAMETERS: - pDev [ I ] - Device specific memory - clientID [ I ] - Requester's client ID - transactionID [ I ] - Transaction ID or 0 for any - ppData [I/O] - On success, will be filled with a - pointer to read buffer - pDataSize [I/O] - On succces, will be filled with the - read buffer's size - -RETURN VALUE: - bool -===========================================================================*/ -static bool PopFromReadMemList( - sGobiUSBNet * pDev, - u16 clientID, - u16 transactionID, - void ** ppData, - u16 * pDataSize ) -{ - sClientMemList * pClientMem; - sReadMemList * pDelReadMemList, ** ppReadMemList; - -#ifdef CONFIG_SMP - // Verify Lock - if (spin_is_locked( &pDev->mQMIDev.mClientMemLock ) == 0) - { - DBG( "unlocked\n" ); - BUG(); - } -#endif - - // Get this client's memory location - pClientMem = FindClientMem( pDev, clientID ); - if (pClientMem == NULL) - { - DBG( "Could not find this client's memory 0x%04X\n", - clientID ); - - return false; - } - - ppReadMemList = &(pClientMem->mpList); - pDelReadMemList = NULL; - - // Find first message that matches this transaction ID - while (*ppReadMemList != NULL) - { - // Do we care about transaction ID? - if (transactionID == 0 - || transactionID == (*ppReadMemList)->mTransactionID ) - { - pDelReadMemList = *ppReadMemList; - VDBG( "*ppReadMemList = 0x%p pDelReadMemList = 0x%p\n", - *ppReadMemList, pDelReadMemList ); - break; - } - - VDBG( "skipping 0x%04X data TID = %x\n", clientID, (*ppReadMemList)->mTransactionID ); - - // Next - ppReadMemList = &(*ppReadMemList)->mpNext; - } - VDBG( "*ppReadMemList = 0x%p pDelReadMemList = 0x%p\n", - *ppReadMemList, pDelReadMemList ); - if (pDelReadMemList != NULL) - { - *ppReadMemList = (*ppReadMemList)->mpNext; - - // Copy to output - *ppData = pDelReadMemList->mpData; - *pDataSize = pDelReadMemList->mDataSize; - VDBG( "*ppData = 0x%p pDataSize = %u\n", - *ppData, *pDataSize ); - - // Free memory - kfree( pDelReadMemList ); - - return true; - } - else - { - DBG( "No read memory to pop, Client 0x%04X, TID = %x\n", - clientID, - transactionID ); - return false; - } -} - -/*=========================================================================== -METHOD: - AddToNotifyList (Public Method) - -DESCRIPTION: - Add Notify entry to this client's notify List - - Caller MUST have lock on mClientMemLock - -PARAMETERS: - pDev [ I ] - Device specific memory - clientID [ I ] - Requester's client ID - transactionID [ I ] - Transaction ID or 0 for any - pNotifyFunct [ I ] - Callback function to be run when data is available - pData [ I ] - Data buffer that willl be passed (unmodified) - to callback - -RETURN VALUE: - bool -===========================================================================*/ -static bool AddToNotifyList( - sGobiUSBNet * pDev, - u16 clientID, - u16 transactionID, - void (* pNotifyFunct)(sGobiUSBNet *, u16, void *), - void * pData ) -{ - sClientMemList * pClientMem; - sNotifyList ** ppThisNotifyList; - -#ifdef CONFIG_SMP - // Verify Lock - if (spin_is_locked( &pDev->mQMIDev.mClientMemLock ) == 0) - { - DBG( "unlocked\n" ); - BUG(); - } -#endif - - // Get this client's memory location - pClientMem = FindClientMem( pDev, clientID ); - if (pClientMem == NULL) - { - DBG( "Could not find this client's memory 0x%04X\n", clientID ); - return false; - } - - // Go to last URBList entry - ppThisNotifyList = &pClientMem->mpReadNotifyList; - while (*ppThisNotifyList != NULL) - { - ppThisNotifyList = &(*ppThisNotifyList)->mpNext; - } - - *ppThisNotifyList = kmalloc( sizeof( sNotifyList ), GFP_ATOMIC ); - if (*ppThisNotifyList == NULL) - { - DBG( "Mem error\n" ); - return false; - } - - (*ppThisNotifyList)->mpNext = NULL; - (*ppThisNotifyList)->mpNotifyFunct = pNotifyFunct; - (*ppThisNotifyList)->mpData = pData; - (*ppThisNotifyList)->mTransactionID = transactionID; - - return true; -} - -/*=========================================================================== -METHOD: - NotifyAndPopNotifyList (Public Method) - -DESCRIPTION: - Remove first Notify entry from this client's notify list - and Run function - - Caller MUST have lock on mClientMemLock - -PARAMETERS: - pDev [ I ] - Device specific memory - clientID [ I ] - Requester's client ID - transactionID [ I ] - Transaction ID or 0 for any - -RETURN VALUE: - bool -===========================================================================*/ -static bool NotifyAndPopNotifyList( - sGobiUSBNet * pDev, - u16 clientID, - u16 transactionID ) -{ - sClientMemList * pClientMem; - sNotifyList * pDelNotifyList, ** ppNotifyList; - -#ifdef CONFIG_SMP - // Verify Lock - if (spin_is_locked( &pDev->mQMIDev.mClientMemLock ) == 0) - { - DBG( "unlocked\n" ); - BUG(); - } -#endif - - // Get this client's memory location - pClientMem = FindClientMem( pDev, clientID ); - if (pClientMem == NULL) - { - DBG( "Could not find this client's memory 0x%04X\n", clientID ); - return false; - } - - ppNotifyList = &(pClientMem->mpReadNotifyList); - pDelNotifyList = NULL; - - // Remove from list - while (*ppNotifyList != NULL) - { - // Do we care about transaction ID? - if (transactionID == 0 - || (*ppNotifyList)->mTransactionID == 0 - || transactionID == (*ppNotifyList)->mTransactionID) - { - pDelNotifyList = *ppNotifyList; - break; - } - - DBG( "skipping data TID = %x\n", (*ppNotifyList)->mTransactionID ); - - // next - ppNotifyList = &(*ppNotifyList)->mpNext; - } - - if (pDelNotifyList != NULL) - { - // Remove element - *ppNotifyList = (*ppNotifyList)->mpNext; - - // Run notification function - if (pDelNotifyList->mpNotifyFunct != NULL) - { - // Unlock for callback - spin_unlock( &pDev->mQMIDev.mClientMemLock ); - - pDelNotifyList->mpNotifyFunct( pDev, - clientID, - pDelNotifyList->mpData ); - - // Restore lock - spin_lock( &pDev->mQMIDev.mClientMemLock ); - } - - // Delete memory - kfree( pDelNotifyList ); - - return true; - } - else - { - DBG( "no one to notify for TID %x\n", transactionID ); - - return false; - } -} - -/*=========================================================================== -METHOD: - AddToURBList (Public Method) - -DESCRIPTION: - Add URB to this client's URB list - - Caller MUST have lock on mClientMemLock - -PARAMETERS: - pDev [ I ] - Device specific memory - clientID [ I ] - Requester's client ID - pURB [ I ] - URB to be added - -RETURN VALUE: - bool -===========================================================================*/ -static bool AddToURBList( - sGobiUSBNet * pDev, - u16 clientID, - struct urb * pURB ) -{ - sClientMemList * pClientMem; - sURBList ** ppThisURBList; - -#ifdef CONFIG_SMP - // Verify Lock - if (spin_is_locked( &pDev->mQMIDev.mClientMemLock ) == 0) - { - DBG( "unlocked\n" ); - BUG(); - } -#endif - - // Get this client's memory location - pClientMem = FindClientMem( pDev, clientID ); - if (pClientMem == NULL) - { - DBG( "Could not find this client's memory 0x%04X\n", clientID ); - return false; - } - - // Go to last URBList entry - ppThisURBList = &pClientMem->mpURBList; - while (*ppThisURBList != NULL) - { - ppThisURBList = &(*ppThisURBList)->mpNext; - } - - *ppThisURBList = kmalloc( sizeof( sURBList ), GFP_ATOMIC ); - if (*ppThisURBList == NULL) - { - DBG( "Mem error\n" ); - return false; - } - - (*ppThisURBList)->mpNext = NULL; - (*ppThisURBList)->mpURB = pURB; - - return true; -} - -/*=========================================================================== -METHOD: - PopFromURBList (Public Method) - -DESCRIPTION: - Remove URB from this client's URB list - - Caller MUST have lock on mClientMemLock - -PARAMETERS: - pDev [ I ] - Device specific memory - clientID [ I ] - Requester's client ID - -RETURN VALUE: - struct urb - Pointer to requested client's URB - NULL for error -===========================================================================*/ -static struct urb * PopFromURBList( - sGobiUSBNet * pDev, - u16 clientID ) -{ - sClientMemList * pClientMem; - sURBList * pDelURBList; - struct urb * pURB; - -#ifdef CONFIG_SMP - // Verify Lock - if (spin_is_locked( &pDev->mQMIDev.mClientMemLock ) == 0) - { - DBG( "unlocked\n" ); - BUG(); - } -#endif - - // Get this client's memory location - pClientMem = FindClientMem( pDev, clientID ); - if (pClientMem == NULL) - { - DBG( "Could not find this client's memory 0x%04X\n", clientID ); - return NULL; - } - - // Remove from list - if (pClientMem->mpURBList != NULL) - { - pDelURBList = pClientMem->mpURBList; - pClientMem->mpURBList = pClientMem->mpURBList->mpNext; - - // Copy to output - pURB = pDelURBList->mpURB; - - // Delete memory - kfree( pDelURBList ); - - return pURB; - } - else - { - DBG( "No URB's to pop\n" ); - - return NULL; - } -} - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION( 3,19,0 )) -#ifndef f_dentry -#define f_dentry f_path.dentry -#endif -#endif - -/*=========================================================================*/ -// Internal userspace wrappers -/*=========================================================================*/ - -/*=========================================================================== -METHOD: - UserspaceunlockedIOCTL (Public Method) - -DESCRIPTION: - Internal wrapper for Userspace IOCTL interface - -PARAMETERS - pFilp [ I ] - userspace file descriptor - cmd [ I ] - IOCTL command - arg [ I ] - IOCTL argument - -RETURN VALUE: - long - 0 for success - Negative errno for failure -===========================================================================*/ -static long UserspaceunlockedIOCTL( - struct file * pFilp, - unsigned int cmd, - unsigned long arg ) -{ - int result; - u32 devVIDPID; - - sQMIFilpStorage * pFilpData = (sQMIFilpStorage *)pFilp->private_data; - - if (pFilpData == NULL) - { - DBG( "Bad file data\n" ); - return -EBADF; - } - - if (IsDeviceValid( pFilpData->mpDev ) == false) - { - DBG( "Invalid device! Updating f_ops\n" ); - pFilp->f_op = pFilp->f_dentry->d_inode->i_fop; - return -ENXIO; - } - - switch (cmd) - { - case IOCTL_QMI_GET_SERVICE_FILE: - DBG( "Setting up QMI for service %lu\n", arg ); - if ((u8)arg == 0) - { - DBG( "Cannot use QMICTL from userspace\n" ); - return -EINVAL; - } - - // Connection is already setup - if (pFilpData->mClientID != (u16)-1) - { - DBG( "Close the current connection before opening a new one\n" ); - return -EBADR; - } - - result = GetClientID( pFilpData->mpDev, (u8)arg ); -// it seems QMIWDA only allow one client, if the last quectel-CM donot realese it (killed by SIGKILL). -// can force release it at here -#if 1 - if (result < 0 && (u8)arg == QMIWDA) - { - ReleaseClientID( pFilpData->mpDev, QMIWDA | (1 << 8) ); - result = GetClientID( pFilpData->mpDev, (u8)arg ); - } -#endif - if (result < 0) - { - return result; - } - pFilpData->mClientID = (u16)result; - DBG("pFilpData->mClientID = 0x%x\n", pFilpData->mClientID ); - return 0; - break; - - - case IOCTL_QMI_GET_DEVICE_VIDPID: - if (arg == 0) - { - DBG( "Bad VIDPID buffer\n" ); - return -EINVAL; - } - - // Extra verification - if (pFilpData->mpDev->mpNetDev == 0) - { - DBG( "Bad mpNetDev\n" ); - return -ENOMEM; - } - if (pFilpData->mpDev->mpNetDev->udev == 0) - { - DBG( "Bad udev\n" ); - return -ENOMEM; - } - - devVIDPID = ((le16_to_cpu( pFilpData->mpDev->mpNetDev->udev->descriptor.idVendor ) << 16) - + le16_to_cpu( pFilpData->mpDev->mpNetDev->udev->descriptor.idProduct ) ); - - result = copy_to_user( (unsigned int *)arg, &devVIDPID, 4 ); - if (result != 0) - { - DBG( "Copy to userspace failure %d\n", result ); - } - - return result; - - break; - - case IOCTL_QMI_GET_DEVICE_MEID: - if (arg == 0) - { - DBG( "Bad MEID buffer\n" ); - return -EINVAL; - } - - result = copy_to_user( (unsigned int *)arg, &pFilpData->mpDev->mMEID[0], 14 ); - if (result != 0) - { - DBG( "Copy to userspace failure %d\n", result ); - } - - return result; - - break; - - default: - return -EBADRQC; - } -} - -/*=========================================================================*/ -// Userspace wrappers -/*=========================================================================*/ - -/*=========================================================================== -METHOD: - UserspaceOpen (Public Method) - -DESCRIPTION: - Userspace open - IOCTL must be called before reads or writes - -PARAMETERS - pInode [ I ] - kernel file descriptor - pFilp [ I ] - userspace file descriptor - -RETURN VALUE: - int - 0 for success - Negative errno for failure -===========================================================================*/ -static int UserspaceOpen( - struct inode * pInode, - struct file * pFilp ) -{ - sQMIFilpStorage * pFilpData; - - // Optain device pointer from pInode - sQMIDev * pQMIDev = container_of( pInode->i_cdev, - sQMIDev, - mCdev ); - sGobiUSBNet * pDev = container_of( pQMIDev, - sGobiUSBNet, - mQMIDev ); - - if (pDev->mbMdm9x07) - { - atomic_inc(&pDev->refcount); - if (!pDev->mbQMIReady) { - if (wait_for_completion_interruptible_timeout(&pDev->mQMIReadyCompletion, 15*HZ) <= 0) { - if (atomic_dec_and_test(&pDev->refcount)) { - kfree( pDev ); - } - return -ETIMEDOUT; - } - } - atomic_dec(&pDev->refcount); - } - - if (IsDeviceValid( pDev ) == false) - { - DBG( "Invalid device\n" ); - return -ENXIO; - } - - // Setup data in pFilp->private_data - pFilp->private_data = kmalloc( sizeof( sQMIFilpStorage ), GFP_KERNEL ); - if (pFilp->private_data == NULL) - { - DBG( "Mem error\n" ); - return -ENOMEM; - } - - pFilpData = (sQMIFilpStorage *)pFilp->private_data; - pFilpData->mClientID = (u16)-1; - pFilpData->mpDev = pDev; - atomic_inc(&pFilpData->mpDev->refcount); - - return 0; -} - -/*=========================================================================== -METHOD: - UserspaceIOCTL (Public Method) - -DESCRIPTION: - Userspace IOCTL functions - -PARAMETERS - pUnusedInode [ I ] - (unused) kernel file descriptor - pFilp [ I ] - userspace file descriptor - cmd [ I ] - IOCTL command - arg [ I ] - IOCTL argument - -RETURN VALUE: - int - 0 for success - Negative errno for failure -===========================================================================*/ -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,36 )) -static int UserspaceIOCTL( - struct inode * pUnusedInode, - struct file * pFilp, - unsigned int cmd, - unsigned long arg ) -{ - // call the internal wrapper function - return (int)UserspaceunlockedIOCTL( pFilp, cmd, arg ); -} -#endif - -#ifdef quectel_no_for_each_process -static int UserspaceClose( - struct inode * pInode, - struct file * pFilp ) -{ - sQMIFilpStorage * pFilpData = (sQMIFilpStorage *)pFilp->private_data; - - if (pFilpData == NULL) - { - DBG( "bad file data\n" ); - return -EBADF; - } - - atomic_dec(&pFilpData->mpDev->refcount); - - if (IsDeviceValid( pFilpData->mpDev ) == false) - { - return -ENXIO; - } - - DBG( "0x%04X\n", pFilpData->mClientID ); - - // Disable pFilpData so they can't keep sending read or write - // should this function hang - // Note: memory pointer is still saved in pFilpData to be deleted later - pFilp->private_data = NULL; - - if (pFilpData->mClientID != (u16)-1) - { - if (pFilpData->mpDev->mbDeregisterQMIDevice) - pFilpData->mClientID = (u16)-1; //DeregisterQMIDevice() will release this ClientID - else - ReleaseClientID( pFilpData->mpDev, - pFilpData->mClientID ); - } - - kfree( pFilpData ); - return 0; -} -#else -/*=========================================================================== -METHOD: - UserspaceClose (Public Method) - -DESCRIPTION: - Userspace close - Release client ID and free memory - -PARAMETERS - pFilp [ I ] - userspace file descriptor - unusedFileTable [ I ] - (unused) file table - -RETURN VALUE: - int - 0 for success - Negative errno for failure -===========================================================================*/ -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,14 )) -int UserspaceClose( - struct file * pFilp, - fl_owner_t unusedFileTable ) -#else -int UserspaceClose( struct file * pFilp ) -#endif -{ - sQMIFilpStorage * pFilpData = (sQMIFilpStorage *)pFilp->private_data; - struct task_struct * pEachTask; - struct fdtable * pFDT; - int count = 0; - int used = 0; - unsigned long flags; - - if (pFilpData == NULL) - { - DBG( "bad file data\n" ); - return -EBADF; - } - - // Fallthough. If f_count == 1 no need to do more checks -#if (LINUX_VERSION_CODE <= KERNEL_VERSION( 2,6,24 )) - if (atomic_read( &pFilp->f_count ) != 1) -#else - if (atomic_long_read( &pFilp->f_count ) != 1) -#endif - { - rcu_read_lock(); - for_each_process( pEachTask ) - { - task_lock(pEachTask); - if (pEachTask == NULL || pEachTask->files == NULL) - { - // Some tasks may not have files (e.g. Xsession) - task_unlock(pEachTask); - continue; - } - spin_lock_irqsave( &pEachTask->files->file_lock, flags ); - task_unlock(pEachTask); //kernel/exit.c:do_exit() -> fs/file.c:exit_files() - pFDT = files_fdtable( pEachTask->files ); - for (count = 0; count < pFDT->max_fds; count++) - { - // Before this function was called, this file was removed - // from our task's file table so if we find it in a file - // table then it is being used by another task - if (pFDT->fd[count] == pFilp) - { - used++; - break; - } - } - spin_unlock_irqrestore( &pEachTask->files->file_lock, flags ); - } - rcu_read_unlock(); - - if (used > 0) - { - DBG( "not closing, as this FD is open by %d other process\n", used ); - return 0; - } - } - - if (IsDeviceValid( pFilpData->mpDev ) == false) - { - DBG( "Invalid device! Updating f_ops\n" ); - pFilp->f_op = pFilp->f_dentry->d_inode->i_fop; - return -ENXIO; - } - - DBG( "0x%04X\n", pFilpData->mClientID ); - - // Disable pFilpData so they can't keep sending read or write - // should this function hang - // Note: memory pointer is still saved in pFilpData to be deleted later - pFilp->private_data = NULL; - - if (pFilpData->mClientID != (u16)-1) - { - if (pFilpData->mpDev->mbDeregisterQMIDevice) - pFilpData->mClientID = (u16)-1; //DeregisterQMIDevice() will release this ClientID - else - ReleaseClientID( pFilpData->mpDev, - pFilpData->mClientID ); - } - atomic_dec(&pFilpData->mpDev->refcount); - - kfree( pFilpData ); - return 0; -} -#endif - -/*=========================================================================== -METHOD: - UserspaceRead (Public Method) - -DESCRIPTION: - Userspace read (synchronous) - -PARAMETERS - pFilp [ I ] - userspace file descriptor - pBuf [ I ] - read buffer - size [ I ] - size of read buffer - pUnusedFpos [ I ] - (unused) file position - -RETURN VALUE: - ssize_t - Number of bytes read for success - Negative errno for failure -===========================================================================*/ -static ssize_t UserspaceRead( - struct file * pFilp, - char __user * pBuf, - size_t size, - loff_t * pUnusedFpos ) -{ - int result; - void * pReadData = NULL; - void * pSmallReadData; - sQMIFilpStorage * pFilpData = (sQMIFilpStorage *)pFilp->private_data; - - if (pFilpData == NULL) - { - DBG( "Bad file data\n" ); - return -EBADF; - } - - if (IsDeviceValid( pFilpData->mpDev ) == false) - { - DBG( "Invalid device! Updating f_ops\n" ); - pFilp->f_op = pFilp->f_dentry->d_inode->i_fop; - return -ENXIO; - } - - if (pFilpData->mClientID == (u16)-1) - { - DBG( "Client ID must be set before reading 0x%04X\n", - pFilpData->mClientID ); - return -EBADR; - } - - // Perform synchronous read - result = ReadSync( pFilpData->mpDev, - &pReadData, - pFilpData->mClientID, - 0 ); - if (result <= 0) - { - return result; - } - - // Discard QMUX header - result -= QMUXHeaderSize(); - pSmallReadData = pReadData + QMUXHeaderSize(); - - if (result > size) - { - DBG( "Read data is too large for amount user has requested\n" ); - kfree( pReadData ); - return -EOVERFLOW; - } - - DBG( "pBuf = 0x%p pSmallReadData = 0x%p, result = %d", - pBuf, pSmallReadData, result ); - - if (copy_to_user( pBuf, pSmallReadData, result ) != 0) - { - DBG( "Error copying read data to user\n" ); - result = -EFAULT; - } - - // Reader is responsible for freeing read buffer - kfree( pReadData ); - - return result; -} - -/*=========================================================================== -METHOD: - UserspaceWrite (Public Method) - -DESCRIPTION: - Userspace write (synchronous) - -PARAMETERS - pFilp [ I ] - userspace file descriptor - pBuf [ I ] - write buffer - size [ I ] - size of write buffer - pUnusedFpos [ I ] - (unused) file position - -RETURN VALUE: - ssize_t - Number of bytes read for success - Negative errno for failure -===========================================================================*/ -static ssize_t UserspaceWrite( - struct file * pFilp, - const char __user * pBuf, - size_t size, - loff_t * pUnusedFpos ) -{ - int status; - void * pWriteBuffer; - sQMIFilpStorage * pFilpData = (sQMIFilpStorage *)pFilp->private_data; - - if (pFilpData == NULL) - { - DBG( "Bad file data\n" ); - return -EBADF; - } - - if (IsDeviceValid( pFilpData->mpDev ) == false) - { - DBG( "Invalid device! Updating f_ops\n" ); - pFilp->f_op = pFilp->f_dentry->d_inode->i_fop; - return -ENXIO; - } - - if (pFilpData->mClientID == (u16)-1) - { - DBG( "Client ID must be set before writing 0x%04X\n", - pFilpData->mClientID ); - return -EBADR; - } - - // Copy data from user to kernel space - pWriteBuffer = kmalloc( size + QMUXHeaderSize(), GFP_KERNEL ); - if (pWriteBuffer == NULL) - { - return -ENOMEM; - } - status = copy_from_user( pWriteBuffer + QMUXHeaderSize(), pBuf, size ); - if (status != 0) - { - DBG( "Unable to copy data from userspace %d\n", status ); - kfree( pWriteBuffer ); - return status; - } - - status = WriteSync( pFilpData->mpDev, - pWriteBuffer, - size + QMUXHeaderSize(), - pFilpData->mClientID ); - - kfree( pWriteBuffer ); - - // On success, return requested size, not full QMI reqest size - if (status == size + QMUXHeaderSize()) - { - return size; - } - else - { - return status; - } -} - -/*=========================================================================== -METHOD: - UserspacePoll (Public Method) - -DESCRIPTION: - Used to determine if read/write operations are possible without blocking - -PARAMETERS - pFilp [ I ] - userspace file descriptor - pPollTable [I/O] - Wait object to notify the kernel when data - is ready - -RETURN VALUE: - unsigned int - bitmask of what operations can be done immediately -===========================================================================*/ -static unsigned int UserspacePoll( - struct file * pFilp, - struct poll_table_struct * pPollTable ) -{ - sQMIFilpStorage * pFilpData = (sQMIFilpStorage *)pFilp->private_data; - sClientMemList * pClientMem; - unsigned long flags; - - // Always ready to write - unsigned long status = POLLOUT | POLLWRNORM; - - if (pFilpData == NULL) - { - DBG( "Bad file data\n" ); - return POLLERR; - } - - if (IsDeviceValid( pFilpData->mpDev ) == false) - { - DBG( "Invalid device! Updating f_ops\n" ); - pFilp->f_op = pFilp->f_dentry->d_inode->i_fop; - return POLLERR; - } - - if (pFilpData->mpDev->mbDeregisterQMIDevice) - { - DBG( "DeregisterQMIDevice ing\n" ); - return POLLHUP | POLLERR; - } - - if (pFilpData->mClientID == (u16)-1) - { - DBG( "Client ID must be set before polling 0x%04X\n", - pFilpData->mClientID ); - return POLLERR; - } - - // Critical section - spin_lock_irqsave( &pFilpData->mpDev->mQMIDev.mClientMemLock, flags ); - - // Get this client's memory location - pClientMem = FindClientMem( pFilpData->mpDev, - pFilpData->mClientID ); - if (pClientMem == NULL) - { - DBG( "Could not find this client's memory 0x%04X\n", - pFilpData->mClientID ); - - spin_unlock_irqrestore( &pFilpData->mpDev->mQMIDev.mClientMemLock, - flags ); - return POLLERR; - } - - poll_wait( pFilp, &pClientMem->mWaitQueue, pPollTable ); - - if (pClientMem->mpList != NULL) - { - status |= POLLIN | POLLRDNORM; - } - - // End critical section - spin_unlock_irqrestore( &pFilpData->mpDev->mQMIDev.mClientMemLock, flags ); - - // Always ready to write - return (status | POLLOUT | POLLWRNORM); -} - -/*=========================================================================*/ -// Initializer and destructor -/*=========================================================================*/ -static int QMICTLSyncProc(sGobiUSBNet *pDev) -{ - void *pWriteBuffer; - void *pReadBuffer; - int result; - u16 writeBufferSize; - u16 readBufferSize; - u8 transactionID; - unsigned long flags; - - if (IsDeviceValid( pDev ) == false) - { - DBG( "Invalid device\n" ); - return -EFAULT; - } - - writeBufferSize= QMICTLSyncReqSize(); - pWriteBuffer = kmalloc( writeBufferSize, GFP_KERNEL ); - if (pWriteBuffer == NULL) - { - return -ENOMEM; - } - - transactionID = QMIXactionIDGet(pDev); - - /* send a QMI_CTL_SYNC_REQ (0x0027) */ - result = QMICTLSyncReq( pWriteBuffer, - writeBufferSize, - transactionID ); - if (result < 0) - { - kfree( pWriteBuffer ); - return result; - } - - result = WriteSync( pDev, - pWriteBuffer, - writeBufferSize, - QMICTL ); - - if (result < 0) - { - kfree( pWriteBuffer ); - return result; - } - - // QMI CTL Sync Response - result = ReadSync( pDev, - &pReadBuffer, - QMICTL, - transactionID ); - if (result < 0) - { - return result; - } - - result = QMICTLSyncResp( pReadBuffer, - (u16)result ); - - kfree( pReadBuffer ); - - if (result < 0) /* need to re-sync */ - { - DBG( "sync response error code %d\n", result ); - /* start timer and wait for the response */ - /* process response */ - return result; - } - -#if 1 //free these ununsed qmi response, or when these transactionID re-used, they will be regarded as qmi response of the qmi request that have same transactionID - // Enter critical section - spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags ); - - // Free any unread data - while (PopFromReadMemList( pDev, QMICTL, 0, &pReadBuffer, &readBufferSize) == true) { - kfree( pReadBuffer ); - } - - // End critical section - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); -#endif - - // Success - return 0; -} - -static int qmi_sync_thread(void *data) { - sGobiUSBNet * pDev = (sGobiUSBNet *)data; - int result = 0; - -#if 1 - // Device is not ready for QMI connections right away - // Wait up to 30 seconds before failing - if (QMIReady( pDev, 30000 ) == false) - { - DBG( "Device unresponsive to QMI\n" ); - goto __qmi_sync_finished; - } - - // Initiate QMI CTL Sync Procedure - DBG( "Sending QMI CTL Sync Request\n" ); - result = QMICTLSyncProc(pDev); - if (result != 0) - { - DBG( "QMI CTL Sync Procedure Error\n" ); - goto __qmi_sync_finished; - } - else - { - DBG( "QMI CTL Sync Procedure Successful\n" ); - } - -#if defined(QUECTEL_WWAN_QMAP) -if (pDev->qmap_mode) { - // Setup Data Format - result = QMIWDASetDataFormat (pDev, pDev->qmap_mode, &pDev->qmap_size); - if (result != 0) - { - goto __qmi_sync_finished; - } - pDev->mpNetDev->rx_urb_size = pDev->qmap_size; -} -#endif - - // Setup WDS callback - result = SetupQMIWDSCallback( pDev ); - if (result != 0) - { - goto __qmi_sync_finished; - } - - // Fill MEID for device - result = QMIDMSGetMEID( pDev ); - if (result != 0) - { - goto __qmi_sync_finished; - } -#endif - -__qmi_sync_finished: - pDev->mbQMIReady = true; - complete_all(&pDev->mQMIReadyCompletion); - pDev->mbQMISyncIng = false; - if (atomic_dec_and_test(&pDev->refcount)) { - kfree( pDev ); - } - return result; -} - -/*=========================================================================== -METHOD: - RegisterQMIDevice (Public Method) - -DESCRIPTION: - QMI Device initialization function - -PARAMETERS: - pDev [ I ] - Device specific memory - -RETURN VALUE: - int - 0 for success - Negative errno for failure -===========================================================================*/ -int RegisterQMIDevice( sGobiUSBNet * pDev ) -{ - int result; - int GobiQMIIndex = 0; - dev_t devno; - char * pDevName; - - if (pDev->mQMIDev.mbCdevIsInitialized == true) - { - // Should never happen, but always better to check - DBG( "device already exists\n" ); - return -EEXIST; - } - - pDev->mbQMIValid = true; - pDev->mbDeregisterQMIDevice = false; - - // Set up for QMICTL - // (does not send QMI message, just sets up memory) - result = GetClientID( pDev, QMICTL ); - if (result != 0) - { - pDev->mbQMIValid = false; - return result; - } - atomic_set( &pDev->mQMIDev.mQMICTLTransactionID, 1 ); - - // Start Async reading - result = StartRead( pDev ); - if (result != 0) - { - pDev->mbQMIValid = false; - return result; - } - - if (pDev->mbMdm9x07) - { - usb_control_msg( pDev->mpNetDev->udev, - usb_sndctrlpipe( pDev->mpNetDev->udev, 0 ), - SET_CONTROL_LINE_STATE_REQUEST, - SET_CONTROL_LINE_STATE_REQUEST_TYPE, - CONTROL_DTR, - /* USB interface number to receive control message */ - pDev->mpIntf->cur_altsetting->desc.bInterfaceNumber, - NULL, - 0, - 100 ); - } - - //for EC21&25, must wait about 15 seconds to wait QMI ready. it is too long for driver probe(will block other drivers probe). - if (pDev->mbMdm9x07) - { - struct task_struct *qmi_sync_task; - atomic_inc(&pDev->refcount); - init_completion(&pDev->mQMIReadyCompletion); - pDev->mbQMIReady = false; - pDev->mbQMISyncIng = true; - qmi_sync_task = kthread_run(qmi_sync_thread, (void *)pDev, "qmi_sync/%d", pDev->mpNetDev->udev->devnum); - if (IS_ERR(qmi_sync_task)) { - pDev->mbQMISyncIng = false; - atomic_dec(&pDev->refcount); - DBG( "Create qmi_sync_thread fail\n" ); - return PTR_ERR(qmi_sync_task); - } - goto __register_chardev_qccmi; - } - - // Device is not ready for QMI connections right away - // Wait up to 30 seconds before failing - if (QMIReady( pDev, 30000 ) == false) - { - DBG( "Device unresponsive to QMI\n" ); - return -ETIMEDOUT; - } - - // Initiate QMI CTL Sync Procedure - DBG( "Sending QMI CTL Sync Request\n" ); - result = QMICTLSyncProc(pDev); - if (result != 0) - { - DBG( "QMI CTL Sync Procedure Error\n" ); - return result; - } - else - { - DBG( "QMI CTL Sync Procedure Successful\n" ); - } - - // Setup Data Format -#if defined(QUECTEL_WWAN_QMAP) - result = QMIWDASetDataFormat (pDev, pDev->qmap_mode, NULL); -#else - result = QMIWDASetDataFormat (pDev, 0, NULL); -#endif - if (result != 0) - { - return result; - } - - // Setup WDS callback - result = SetupQMIWDSCallback( pDev ); - if (result != 0) - { - return result; - } - - // Fill MEID for device - result = QMIDMSGetMEID( pDev ); - if (result != 0) - { - return result; - } - -__register_chardev_qccmi: - // allocate and fill devno with numbers - result = alloc_chrdev_region( &devno, 0, 1, "qcqmi" ); - if (result < 0) - { - return result; - } - - // Create cdev - cdev_init( &pDev->mQMIDev.mCdev, &UserspaceQMIFops ); - pDev->mQMIDev.mCdev.owner = THIS_MODULE; - pDev->mQMIDev.mCdev.ops = &UserspaceQMIFops; - pDev->mQMIDev.mbCdevIsInitialized = true; - - result = cdev_add( &pDev->mQMIDev.mCdev, devno, 1 ); - if (result != 0) - { - DBG( "error adding cdev\n" ); - return result; - } - - // Match interface number (usb# or eth#) - if (!!(pDevName = strstr( pDev->mpNetDev->net->name, "eth" ))) { - pDevName += strlen( "eth" ); - } else if (!!(pDevName = strstr( pDev->mpNetDev->net->name, "usb" ))) { - pDevName += strlen( "usb" ); -#if 1 //openWRT like use ppp# or lte# - } else if (!!(pDevName = strstr( pDev->mpNetDev->net->name, "ppp" ))) { - pDevName += strlen( "ppp" ); - } else if (!!(pDevName = strstr( pDev->mpNetDev->net->name, "lte" ))) { - pDevName += strlen( "lte" ); -#endif - } else { - DBG( "Bad net name: %s\n", pDev->mpNetDev->net->name ); - return -ENXIO; - } - GobiQMIIndex = simple_strtoul( pDevName, NULL, 10 ); - if (GobiQMIIndex < 0) - { - DBG( "Bad minor number\n" ); - return -ENXIO; - } - - // Always print this output - printk( KERN_INFO "creating qcqmi%d\n", - GobiQMIIndex ); - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION( 2,6,27 )) - // kernel 2.6.27 added a new fourth parameter to device_create - // void * drvdata : the data to be added to the device for callbacks - device_create( pDev->mQMIDev.mpDevClass, - &pDev->mpIntf->dev, - devno, - NULL, - "qcqmi%d", - GobiQMIIndex ); -#else - device_create( pDev->mQMIDev.mpDevClass, - &pDev->mpIntf->dev, - devno, - "qcqmi%d", - GobiQMIIndex ); -#endif - - pDev->mQMIDev.mDevNum = devno; - - // Success - return 0; -} - -/*=========================================================================== -METHOD: - DeregisterQMIDevice (Public Method) - -DESCRIPTION: - QMI Device cleanup function - - NOTE: When this function is run the device is no longer valid - -PARAMETERS: - pDev [ I ] - Device specific memory - -RETURN VALUE: - None -===========================================================================*/ -void DeregisterQMIDevice( sGobiUSBNet * pDev ) -{ -#ifndef quectel_no_for_each_process - struct inode * pOpenInode; - struct list_head * pInodeList; - struct task_struct * pEachTask; - struct fdtable * pFDT; - struct file * pFilp; - int count = 0; -#endif - unsigned long flags; - int tries; - int result; - - // Should never happen, but check anyway - if (IsDeviceValid( pDev ) == false) - { - DBG( "wrong device\n" ); - return; - } - - pDev->mbDeregisterQMIDevice = true; - - for (tries = 0; tries < 3000; tries += 10) { - if (pDev->mbQMISyncIng == false) - break; - msleep(10); - } - - if (pDev->mbQMISyncIng) { - DBG( "QMI sync ing\n" ); - } - - // Release all clients - spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags ); - while (pDev->mQMIDev.mpClientMemList != NULL) - { - u16 mClientID = pDev->mQMIDev.mpClientMemList->mClientID; - if (waitqueue_active(&pDev->mQMIDev.mpClientMemList->mWaitQueue)) { - DBG("WaitQueue 0x%04X\n", mClientID); - wake_up_interruptible_sync( &pDev->mQMIDev.mpClientMemList->mWaitQueue ); - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); - msleep(10); - spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags ); - continue; - } - - DBG( "release 0x%04X\n", pDev->mQMIDev.mpClientMemList->mClientID ); - - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); - ReleaseClientID( pDev, mClientID ); - // NOTE: pDev->mQMIDev.mpClientMemList will - // be updated in ReleaseClientID() - spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags ); - } - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); - - // Stop all reads - KillRead( pDev ); - - pDev->mbQMIValid = false; - - if (pDev->mQMIDev.mbCdevIsInitialized == false) - { - return; - } - -#ifndef quectel_no_for_each_process - // Find each open file handle, and manually close it - - // Generally there will only be only one inode, but more are possible - list_for_each( pInodeList, &pDev->mQMIDev.mCdev.list ) - { - // Get the inode - pOpenInode = container_of( pInodeList, struct inode, i_devices ); - if (pOpenInode != NULL && (IS_ERR( pOpenInode ) == false)) - { - // Look for this inode in each task - - rcu_read_lock(); - for_each_process( pEachTask ) - { - task_lock(pEachTask); - if (pEachTask == NULL || pEachTask->files == NULL) - { - // Some tasks may not have files (e.g. Xsession) - task_unlock(pEachTask); - continue; - } - // For each file this task has open, check if it's referencing - // our inode. - spin_lock_irqsave( &pEachTask->files->file_lock, flags ); - task_unlock(pEachTask); //kernel/exit.c:do_exit() -> fs/file.c:exit_files() - pFDT = files_fdtable( pEachTask->files ); - for (count = 0; count < pFDT->max_fds; count++) - { - pFilp = pFDT->fd[count]; - if (pFilp != NULL && pFilp->f_dentry != NULL) - { - if (pFilp->f_dentry->d_inode == pOpenInode) - { - // Close this file handle - rcu_assign_pointer( pFDT->fd[count], NULL ); - spin_unlock_irqrestore( &pEachTask->files->file_lock, flags ); - - DBG( "forcing close of open file handle\n" ); - filp_close( pFilp, pEachTask->files ); - - spin_lock_irqsave( &pEachTask->files->file_lock, flags ); - } - } - } - spin_unlock_irqrestore( &pEachTask->files->file_lock, flags ); - } - rcu_read_unlock(); - } - } -#endif - -if (pDev->mpNetDev->udev->state) { - // Send SetControlLineState request (USB_CDC) - result = usb_control_msg( pDev->mpNetDev->udev, - usb_sndctrlpipe( pDev->mpNetDev->udev, 0 ), - SET_CONTROL_LINE_STATE_REQUEST, - SET_CONTROL_LINE_STATE_REQUEST_TYPE, - 0, // DTR not present - /* USB interface number to receive control message */ - pDev->mpIntf->cur_altsetting->desc.bInterfaceNumber, - NULL, - 0, - 100 ); - if (result < 0) - { - DBG( "Bad SetControlLineState status %d\n", result ); - } -} - - // Remove device (so no more calls can be made by users) - if (IS_ERR( pDev->mQMIDev.mpDevClass ) == false) - { - device_destroy( pDev->mQMIDev.mpDevClass, - pDev->mQMIDev.mDevNum ); - } - - // Hold onto cdev memory location until everyone is through using it. - // Timeout after 30 seconds (10 ms interval). Timeout should never happen, - // but exists to prevent an infinate loop just in case. - for (tries = 0; tries < 30 * 100; tries++) - { -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 4,11,0 )) - int ref = atomic_read( &pDev->mQMIDev.mCdev.kobj.kref.refcount ); -#else - int ref = kref_read( &pDev->mQMIDev.mCdev.kobj.kref ); -#endif - if (ref > 1) - { - DBG( "cdev in use by %d tasks\n", ref - 1 ); - if (tries > 10) - INFO( "cdev in use by %d tasks\n", ref - 1 ); - msleep( 10 ); - } - else - { - break; - } - } - - cdev_del( &pDev->mQMIDev.mCdev ); - - unregister_chrdev_region( pDev->mQMIDev.mDevNum, 1 ); - - return; -} - -/*=========================================================================*/ -// Driver level client management -/*=========================================================================*/ - -/*=========================================================================== -METHOD: - QMIReady (Public Method) - -DESCRIPTION: - Send QMI CTL GET VERSION INFO REQ and SET DATA FORMAT REQ - Wait for response or timeout - -PARAMETERS: - pDev [ I ] - Device specific memory - timeout [ I ] - Milliseconds to wait for response - -RETURN VALUE: - bool -===========================================================================*/ -static bool QMIReady( - sGobiUSBNet * pDev, - u16 timeout ) -{ - int result; - void * pWriteBuffer; - u16 writeBufferSize; - void * pReadBuffer; - u16 readBufferSize; - u16 curTime; - unsigned long flags; - u8 transactionID; - u16 interval = 2000; - - if (IsDeviceValid( pDev ) == false) - { - DBG( "Invalid device\n" ); - return false; - } - - writeBufferSize = QMICTLReadyReqSize(); - pWriteBuffer = kmalloc( writeBufferSize, GFP_KERNEL ); - if (pWriteBuffer == NULL) - { - return false; - } - - // An implimentation of down_timeout has not been agreed on, - // so it's been added and removed from the kernel several times. - // We're just going to ignore it and poll the semaphore. - - // Send a write every 1000 ms and see if we get a response - for (curTime = 0; curTime < timeout; curTime += interval) - { - // Start read - struct QuecSem *readSem = kmalloc(sizeof(struct QuecSem ), GFP_KERNEL); - readSem->magic = QUEC_SEM_MAGIC; - sema_init( &readSem->readSem, 0 ); - - transactionID = QMIXactionIDGet( pDev ); - - result = ReadAsync( pDev, QMICTL, transactionID, UpSem, readSem ); - if (result != 0) - { - kfree( pWriteBuffer ); - return false; - } - - // Fill buffer - result = QMICTLReadyReq( pWriteBuffer, - writeBufferSize, - transactionID ); - if (result < 0) - { - kfree( pWriteBuffer ); - return false; - } - - // Disregard status. On errors, just try again - result = WriteSync( pDev, - pWriteBuffer, - writeBufferSize, - QMICTL ); - - if (result < 0) //maybe caused by usb disconnect - { - kfree( pWriteBuffer ); - return false; - } - -#if 1 - if (down_timeout( &readSem->readSem, msecs_to_jiffies(interval) ) == 0) -#else - msleep( interval ); - if (down_trylock( &readSem->readSem ) == 0) -#endif - { - kfree(readSem); - // Enter critical section - spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags ); - - // Pop the read data - if (PopFromReadMemList( pDev, - QMICTL, - transactionID, - &pReadBuffer, - &readBufferSize ) == true) - { - // Success - - // End critical section - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); - - // We don't care about the result - kfree( pReadBuffer ); - - break; - } - else - { - // Read mismatch/failure, unlock and continue - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); - } - } - else - { - readSem->magic = 0; - // Enter critical section - spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags ); - - // Timeout, remove the async read - NotifyAndPopNotifyList( pDev, QMICTL, transactionID ); - - // End critical section - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); - } - - if (pDev->mbDeregisterQMIDevice) - { - kfree( pWriteBuffer ); - return false; - } - } - - kfree( pWriteBuffer ); - - // Did we time out? - if (curTime >= timeout) - { - return false; - } - - DBG( "QMI Ready after %u milliseconds\n", curTime ); - - // Success - return true; -} - -/*=========================================================================== -METHOD: - QMIWDSCallback (Public Method) - -DESCRIPTION: - QMI WDS callback function - Update net stats or link state - -PARAMETERS: - pDev [ I ] - Device specific memory - clientID [ I ] - Client ID - pData [ I ] - Callback data (unused) - -RETURN VALUE: - None -===========================================================================*/ -static void QMIWDSCallback( - sGobiUSBNet * pDev, - u16 clientID, - void * pData ) -{ - bool bRet; - int result; - void * pReadBuffer; - u16 readBufferSize; - -#if 0 -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,31 )) - struct net_device_stats * pStats = &(pDev->mpNetDev->stats); -#else - struct net_device_stats * pStats = &(pDev->mpNetDev->net->stats); -#endif -#endif - - u32 TXOk = (u32)-1; - u32 RXOk = (u32)-1; - u32 TXErr = (u32)-1; - u32 RXErr = (u32)-1; - u32 TXOfl = (u32)-1; - u32 RXOfl = (u32)-1; - u64 TXBytesOk = (u64)-1; - u64 RXBytesOk = (u64)-1; - bool bLinkState; - bool bReconfigure; - unsigned long flags; - - if (IsDeviceValid( pDev ) == false) - { - DBG( "Invalid device\n" ); - return; - } - - // Critical section - spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags ); - - bRet = PopFromReadMemList( pDev, - clientID, - 0, - &pReadBuffer, - &readBufferSize ); - - // End critical section - spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); - - if (bRet == false) - { - DBG( "WDS callback failed to get data\n" ); - return; - } - - // Default values - bLinkState = ! GobiTestDownReason( pDev, NO_NDIS_CONNECTION ); - bReconfigure = false; - - result = QMIWDSEventResp( pReadBuffer, - readBufferSize, - &TXOk, - &RXOk, - &TXErr, - &RXErr, - &TXOfl, - &RXOfl, - &TXBytesOk, - &RXBytesOk, - &bLinkState, - &bReconfigure ); - if (result < 0) - { - DBG( "bad WDS packet\n" ); - } - else - { -#if 0 //usbbet.c will do this job - // Fill in new values, ignore max values - if (TXOfl != (u32)-1) - { - pStats->tx_fifo_errors = TXOfl; - } - - if (RXOfl != (u32)-1) - { - pStats->rx_fifo_errors = RXOfl; - } - - if (TXErr != (u32)-1) - { - pStats->tx_errors = TXErr; - } - - if (RXErr != (u32)-1) - { - pStats->rx_errors = RXErr; - } - - if (TXOk != (u32)-1) - { - pStats->tx_packets = TXOk + pStats->tx_errors; - } - - if (RXOk != (u32)-1) - { - pStats->rx_packets = RXOk + pStats->rx_errors; - } - - if (TXBytesOk != (u64)-1) - { - pStats->tx_bytes = TXBytesOk; - } - - if (RXBytesOk != (u64)-1) - { - pStats->rx_bytes = RXBytesOk; - } -#endif - - if (bReconfigure == true) - { - DBG( "Net device link reset\n" ); - GobiSetDownReason( pDev, NO_NDIS_CONNECTION ); - GobiClearDownReason( pDev, NO_NDIS_CONNECTION ); - } - else - { - if (bLinkState == true) - { - if (GobiTestDownReason( pDev, NO_NDIS_CONNECTION )) { - DBG( "Net device link is connected\n" ); - GobiClearDownReason( pDev, NO_NDIS_CONNECTION ); - } - } - else - { - if (!GobiTestDownReason( pDev, NO_NDIS_CONNECTION )) { - DBG( "Net device link is disconnected\n" ); - GobiSetDownReason( pDev, NO_NDIS_CONNECTION ); - } - } - } - } - - kfree( pReadBuffer ); - - // Setup next read - result = ReadAsync( pDev, - clientID, - 0, - QMIWDSCallback, - pData ); - if (result != 0) - { - DBG( "unable to setup next async read\n" ); - } - - return; -} - -/*=========================================================================== -METHOD: - SetupQMIWDSCallback (Public Method) - -DESCRIPTION: - Request client and fire off reqests and start async read for - QMI WDS callback - -PARAMETERS: - pDev [ I ] - Device specific memory - -RETURN VALUE: - int - 0 for success - Negative errno for failure -===========================================================================*/ -static int SetupQMIWDSCallback( sGobiUSBNet * pDev ) -{ - int result; - void * pWriteBuffer; - u16 writeBufferSize; - u16 WDSClientID; - - if (IsDeviceValid( pDev ) == false) - { - DBG( "Invalid device\n" ); - return -EFAULT; - } - - result = GetClientID( pDev, QMIWDS ); - if (result < 0) - { - return result; - } - WDSClientID = result; - -#if 0 // add for "AT$QCRMCALL=1,1", be careful: donot enable these codes if use quectel-CM, or cannot obtain IP by udhcpc - if (pDev->mbMdm9x07) - { - void * pReadBuffer; - u16 readBufferSize; - - writeBufferSize = QMIWDSSetQMUXBindMuxDataPortSize(); - pWriteBuffer = kmalloc( writeBufferSize, GFP_KERNEL ); - if (pWriteBuffer == NULL) - { - return -ENOMEM; - } - - result = QMIWDSSetQMUXBindMuxDataPortReq( pWriteBuffer, - writeBufferSize, - 0x81, - 3 ); - if (result < 0) - { - kfree( pWriteBuffer ); - return result; - } - - result = WriteSync( pDev, - pWriteBuffer, - writeBufferSize, - WDSClientID ); - kfree( pWriteBuffer ); - - if (result < 0) - { - return result; - } - - result = ReadSync( pDev, - &pReadBuffer, - WDSClientID, - 3 ); - if (result < 0) - { - return result; - } - readBufferSize = result; - - kfree( pReadBuffer ); - } -#endif - - // QMI WDS Set Event Report - writeBufferSize = QMIWDSSetEventReportReqSize(); - pWriteBuffer = kmalloc( writeBufferSize, GFP_KERNEL ); - if (pWriteBuffer == NULL) - { - return -ENOMEM; - } - - result = QMIWDSSetEventReportReq( pWriteBuffer, - writeBufferSize, - 1 ); - if (result < 0) - { - kfree( pWriteBuffer ); - return result; - } - - result = WriteSync( pDev, - pWriteBuffer, - writeBufferSize, - WDSClientID ); - kfree( pWriteBuffer ); - - if (result < 0) - { - return result; - } - - // QMI WDS Get PKG SRVC Status - writeBufferSize = QMIWDSGetPKGSRVCStatusReqSize(); - pWriteBuffer = kmalloc( writeBufferSize, GFP_KERNEL ); - if (pWriteBuffer == NULL) - { - return -ENOMEM; - } - - result = QMIWDSGetPKGSRVCStatusReq( pWriteBuffer, - writeBufferSize, - 2 ); - if (result < 0) - { - kfree( pWriteBuffer ); - return result; - } - - result = WriteSync( pDev, - pWriteBuffer, - writeBufferSize, - WDSClientID ); - kfree( pWriteBuffer ); - - if (result < 0) - { - return result; - } - - // Setup asnyc read callback - result = ReadAsync( pDev, - WDSClientID, - 0, - QMIWDSCallback, - NULL ); - if (result != 0) - { - DBG( "unable to setup async read\n" ); - return result; - } - - // Send SetControlLineState request (USB_CDC) - // Required for Autoconnect - result = usb_control_msg( pDev->mpNetDev->udev, - usb_sndctrlpipe( pDev->mpNetDev->udev, 0 ), - SET_CONTROL_LINE_STATE_REQUEST, - SET_CONTROL_LINE_STATE_REQUEST_TYPE, - CONTROL_DTR, - /* USB interface number to receive control message */ - pDev->mpIntf->cur_altsetting->desc.bInterfaceNumber, - NULL, - 0, - 100 ); - if (result < 0) - { - DBG( "Bad SetControlLineState status %d\n", result ); - return result; - } - - return 0; -} - -/*=========================================================================== -METHOD: - QMIDMSGetMEID (Public Method) - -DESCRIPTION: - Register DMS client - send MEID req and parse response - Release DMS client - -PARAMETERS: - pDev [ I ] - Device specific memory - -RETURN VALUE: - None -===========================================================================*/ -static int QMIDMSGetMEID( sGobiUSBNet * pDev ) -{ - int result; - void * pWriteBuffer; - u16 writeBufferSize; - void * pReadBuffer; - u16 readBufferSize; - u16 DMSClientID; - - if (IsDeviceValid( pDev ) == false) - { - DBG( "Invalid device\n" ); - return -EFAULT; - } - - result = GetClientID( pDev, QMIDMS ); - if (result < 0) - { - return result; - } - DMSClientID = result; - - // QMI DMS Get Serial numbers Req - writeBufferSize = QMIDMSGetMEIDReqSize(); - pWriteBuffer = kmalloc( writeBufferSize, GFP_KERNEL ); - if (pWriteBuffer == NULL) - { - return -ENOMEM; - } - - result = QMIDMSGetMEIDReq( pWriteBuffer, - writeBufferSize, - 1 ); - if (result < 0) - { - kfree( pWriteBuffer ); - return result; - } - - result = WriteSync( pDev, - pWriteBuffer, - writeBufferSize, - DMSClientID ); - kfree( pWriteBuffer ); - - if (result < 0) - { - return result; - } - - // QMI DMS Get Serial numbers Resp - result = ReadSync( pDev, - &pReadBuffer, - DMSClientID, - 1 ); - if (result < 0) - { - return result; - } - readBufferSize = result; - - result = QMIDMSGetMEIDResp( pReadBuffer, - readBufferSize, - &pDev->mMEID[0], - 14 ); - kfree( pReadBuffer ); - - if (result < 0) - { - DBG( "bad get MEID resp\n" ); - - // Non fatal error, device did not return any MEID - // Fill with 0's - memset( &pDev->mMEID[0], '0', 14 ); - } - - ReleaseClientID( pDev, DMSClientID ); - - // Success - return 0; -} - -/*=========================================================================== -METHOD: - QMIWDASetDataFormat (Public Method) - -DESCRIPTION: - Register WDA client - send Data format request and parse response - Release WDA client - -PARAMETERS: - pDev [ I ] - Device specific memory - -RETURN VALUE: - None -===========================================================================*/ -static int QMIWDASetDataFormat( sGobiUSBNet * pDev, int qmap_mode, int *rx_urb_size ) -{ - int result; - void * pWriteBuffer; - u16 writeBufferSize; - void * pReadBuffer; - u16 readBufferSize; - u16 WDAClientID; - - DBG("\n"); - - if (IsDeviceValid( pDev ) == false) - { - DBG( "Invalid device\n" ); - return -EFAULT; - } - - result = GetClientID( pDev, QMIWDA ); - if (result < 0) - { - return result; - } - WDAClientID = result; - - // QMI WDA Set Data Format Request - writeBufferSize = QMIWDASetDataFormatReqSize(qmap_mode); - pWriteBuffer = kmalloc( writeBufferSize, GFP_KERNEL ); - if (pWriteBuffer == NULL) - { - return -ENOMEM; - } - - result = QMIWDASetDataFormatReq( pWriteBuffer, - writeBufferSize, pDev->mbRawIPMode, - qmap_mode ? pDev->qmap_version : 0, (31*1024), - 1 ); - - if (result < 0) - { - kfree( pWriteBuffer ); - return result; - } - - result = WriteSync( pDev, - pWriteBuffer, - writeBufferSize, - WDAClientID ); - kfree( pWriteBuffer ); - - if (result < 0) - { - return result; - } - - // QMI DMS Get Serial numbers Resp - result = ReadSync( pDev, - &pReadBuffer, - WDAClientID, - 1 ); - if (result < 0) - { - return result; - } - readBufferSize = result; - -if (qmap_mode && rx_urb_size) { - int qmap_version = 0, rx_size = 0, tx_size = 0; - result = QMIWDASetDataFormatResp( pReadBuffer, - readBufferSize, pDev->mbRawIPMode, &qmap_version, &rx_size, &tx_size, &pDev->qmap_settings); - INFO( "qmap settings qmap_version=%d, rx_size=%d, tx_size=%d\n", - le32_to_cpu(qmap_version), le32_to_cpu(rx_size), le32_to_cpu(tx_size)); - - if (le32_to_cpu(qmap_version)) { -#if defined(QUECTEL_UL_DATA_AGG) - struct ul_agg_ctx *ctx = &pDev->agg_ctx; - - if (le32_to_cpu(pDev->qmap_settings.ul_data_aggregation_max_datagrams) > 1) { - ctx->ul_data_aggregation_max_size = le32_to_cpu(pDev->qmap_settings.ul_data_aggregation_max_size); - ctx->ul_data_aggregation_max_datagrams = le32_to_cpu(pDev->qmap_settings.ul_data_aggregation_max_datagrams); - ctx->dl_minimum_padding = le32_to_cpu(pDev->qmap_settings.dl_minimum_padding); - } - INFO( "qmap settings ul_data_aggregation_max_size=%d, ul_data_aggregation_max_datagrams=%d\n", - ctx->ul_data_aggregation_max_size, ctx->ul_data_aggregation_max_datagrams); - if (ctx->ul_data_aggregation_max_datagrams > 11) - ctx->ul_data_aggregation_max_datagrams = 11; -#endif - *rx_urb_size = le32_to_cpu(rx_size); - } else { - *rx_urb_size = 0; - result = -EFAULT; - } -} else { - int qmap_enabled = 0, rx_size = 0, tx_size = 0; - result = QMIWDASetDataFormatResp( pReadBuffer, - readBufferSize, pDev->mbRawIPMode, &qmap_enabled, &rx_size, &tx_size, NULL); -} - - kfree( pReadBuffer ); - - if (result < 0) - { - DBG( "Data Format Cannot be set\n" ); - } - - ReleaseClientID( pDev, WDAClientID ); - - // Success - return 0; -} - -int QuecQMIWDASetDataFormat( sGobiUSBNet * pDev, int qmap_mode, int *rx_urb_size ) { - return QMIWDASetDataFormat(pDev, qmap_mode, rx_urb_size); -} diff --git a/package/wwan/driver/quectel_Gobinet/src/QMIDevice.h b/package/wwan/driver/quectel_Gobinet/src/QMIDevice.h deleted file mode 100644 index 93984fa08..000000000 --- a/package/wwan/driver/quectel_Gobinet/src/QMIDevice.h +++ /dev/null @@ -1,368 +0,0 @@ -/*=========================================================================== -FILE: - QMIDevice.h - -DESCRIPTION: - Functions related to the QMI interface device - -FUNCTIONS: - Generic functions - IsDeviceValid - PrintHex - GobiSetDownReason - GobiClearDownReason - GobiTestDownReason - - Driver level asynchronous read functions - ResubmitIntURB - ReadCallback - IntCallback - StartRead - KillRead - - Internal read/write functions - ReadAsync - UpSem - ReadSync - WriteSyncCallback - WriteSync - - Internal memory management functions - GetClientID - ReleaseClientID - FindClientMem - AddToReadMemList - PopFromReadMemList - AddToNotifyList - NotifyAndPopNotifyList - AddToURBList - PopFromURBList - - Internal userspace wrapper functions - UserspaceunlockedIOCTL - - Userspace wrappers - UserspaceOpen - UserspaceIOCTL - UserspaceClose - UserspaceRead - UserspaceWrite - UserspacePoll - - Initializer and destructor - RegisterQMIDevice - DeregisterQMIDevice - - Driver level client management - QMIReady - QMIWDSCallback - SetupQMIWDSCallback - QMIDMSGetMEID - -Copyright (c) 2011, Code Aurora Forum. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Code Aurora Forum nor - the names of its contributors may be used to endorse or promote - products derived from this software without specific prior written - permission. - - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -===========================================================================*/ - -//--------------------------------------------------------------------------- -// Pragmas -//--------------------------------------------------------------------------- -#pragma once - -//--------------------------------------------------------------------------- -// Include Files -//--------------------------------------------------------------------------- -#include "Structs.h" -#include "QMI.h" - -/*=========================================================================*/ -// Generic functions -/*=========================================================================*/ - -#ifdef __QUECTEL_INTER__ - -// Basic test to see if device memory is valid -static bool IsDeviceValid( sGobiUSBNet * pDev ); - -/*=========================================================================*/ -// Driver level asynchronous read functions -/*=========================================================================*/ - -// Resubmit interrupt URB, re-using same values -static int ResubmitIntURB( struct urb * pIntURB ); - -// Read callback -// Put the data in storage and notify anyone waiting for data -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,18 )) -static void ReadCallback( struct urb * pReadURB ); -#else -static void ReadCallback(struct urb *pReadURB, struct pt_regs *regs); -#endif - -// Inturrupt callback -// Data is available, start a read URB -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,18 )) -static void IntCallback( struct urb * pIntURB ); -#else -static void IntCallback(struct urb *pIntURB, struct pt_regs *regs); -#endif - -/*=========================================================================*/ -// Internal read/write functions -/*=========================================================================*/ - -// Start asynchronous read -// Reading client's data store, not device -static int ReadAsync( - sGobiUSBNet * pDev, - u16 clientID, - u16 transactionID, - void (*pCallback)(sGobiUSBNet *, u16, void *), - void * pData ); - -// Notification function for synchronous read -static void UpSem( - sGobiUSBNet * pDev, - u16 clientID, - void * pData ); - -// Start synchronous read -// Reading client's data store, not device -static int ReadSync( - sGobiUSBNet * pDev, - void ** ppOutBuffer, - u16 clientID, - u16 transactionID ); - -// Write callback -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,18 )) -static void WriteSyncCallback( struct urb * pWriteURB ); -#else -static void WriteSyncCallback(struct urb *pWriteURB, struct pt_regs *regs); -#endif - -// Start synchronous write -static int WriteSync( - sGobiUSBNet * pDev, - char * pInWriteBuffer, - int size, - u16 clientID ); - -/*=========================================================================*/ -// Internal memory management functions -/*=========================================================================*/ - -// Create client and allocate memory -static int GetClientID( - sGobiUSBNet * pDev, - u8 serviceType ); - -// Release client and free memory -static void ReleaseClientID( - sGobiUSBNet * pDev, - u16 clientID ); - -// Find this client's memory -static sClientMemList * FindClientMem( - sGobiUSBNet * pDev, - u16 clientID ); - -// Add Data to this client's ReadMem list -static bool AddToReadMemList( - sGobiUSBNet * pDev, - u16 clientID, - u16 transactionID, - void * pData, - u16 dataSize ); - -// Remove data from this client's ReadMem list if it matches -// the specified transaction ID. -static bool PopFromReadMemList( - sGobiUSBNet * pDev, - u16 clientID, - u16 transactionID, - void ** ppData, - u16 * pDataSize ); - -// Add Notify entry to this client's notify List -static bool AddToNotifyList( - sGobiUSBNet * pDev, - u16 clientID, - u16 transactionID, - void (* pNotifyFunct)(sGobiUSBNet *, u16, void *), - void * pData ); - -// Remove first Notify entry from this client's notify list -// and Run function -static bool NotifyAndPopNotifyList( - sGobiUSBNet * pDev, - u16 clientID, - u16 transactionID ); - -// Add URB to this client's URB list -static bool AddToURBList( - sGobiUSBNet * pDev, - u16 clientID, - struct urb * pURB ); - -// Remove URB from this client's URB list -static struct urb * PopFromURBList( - sGobiUSBNet * pDev, - u16 clientID ); - -/*=========================================================================*/ -// Internal userspace wrappers -/*=========================================================================*/ - -// Userspace unlocked ioctl -static long UserspaceunlockedIOCTL( - struct file * pFilp, - unsigned int cmd, - unsigned long arg ); - -/*=========================================================================*/ -// Userspace wrappers -/*=========================================================================*/ - -// Userspace open -static int UserspaceOpen( - struct inode * pInode, - struct file * pFilp ); - -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,36 )) -// Userspace ioctl -static int UserspaceIOCTL( - struct inode * pUnusedInode, - struct file * pFilp, - unsigned int cmd, - unsigned long arg ); -#endif - -// Userspace close -#define quectel_no_for_each_process -#ifdef quectel_no_for_each_process -static int UserspaceClose( - struct inode * pInode, - struct file * pFilp ); -#else -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,14 )) -static int UserspaceClose( - struct file * pFilp, - fl_owner_t unusedFileTable ); -#else -static int UserspaceClose( struct file * pFilp ); -#endif -#endif - -// Userspace read (synchronous) -static ssize_t UserspaceRead( - struct file * pFilp, - char __user * pBuf, - size_t size, - loff_t * pUnusedFpos ); - -// Userspace write (synchronous) -static ssize_t UserspaceWrite( - struct file * pFilp, - const char __user * pBuf, - size_t size, - loff_t * pUnusedFpos ); - -static unsigned int UserspacePoll( - struct file * pFilp, - struct poll_table_struct * pPollTable ); - -/*=========================================================================*/ -// Driver level client management -/*=========================================================================*/ - -// Check if QMI is ready for use -static bool QMIReady( - sGobiUSBNet * pDev, - u16 timeout ); - -// QMI WDS callback function -static void QMIWDSCallback( - sGobiUSBNet * pDev, - u16 clientID, - void * pData ); - -// Fire off reqests and start async read for QMI WDS callback -static int SetupQMIWDSCallback( sGobiUSBNet * pDev ); - -// Register client, send req and parse MEID response, release client -static int QMIDMSGetMEID( sGobiUSBNet * pDev ); - -// Register client, send req and parse Data format response, release client -static int QMIWDASetDataFormat( sGobiUSBNet * pDev, int qmap_mode, int *rx_urb_size ); -#endif - -// Print Hex data, for debug purposes -void QuecPrintHex( - void * pBuffer, - u16 bufSize ); - -// Sets mDownReason and turns carrier off -void QuecGobiSetDownReason( - sGobiUSBNet * pDev, - u8 reason ); - -// Clear mDownReason and may turn carrier on -void QuecGobiClearDownReason( - sGobiUSBNet * pDev, - u8 reason ); - -// Tests mDownReason and returns whether reason is set -bool QuecGobiTestDownReason( - sGobiUSBNet * pDev, - u8 reason ); - -// Start continuous read "thread" - int QuecStartRead( sGobiUSBNet * pDev ); - -// Kill continuous read "thread" - void QuecKillRead( sGobiUSBNet * pDev ); - -/*=========================================================================*/ -// Initializer and destructor -/*=========================================================================*/ - -// QMI Device initialization function -int QuecRegisterQMIDevice( sGobiUSBNet * pDev ); - -// QMI Device cleanup function -void QuecDeregisterQMIDevice( sGobiUSBNet * pDev ); - -int QuecQMIWDASetDataFormat( sGobiUSBNet * pDev, int qmap_mode, int *rx_urb_size ); - -#define PrintHex QuecPrintHex -#define GobiSetDownReason QuecGobiSetDownReason -#define GobiClearDownReason QuecGobiClearDownReason -#define GobiTestDownReason QuecGobiTestDownReason -#define StartRead QuecStartRead -#define KillRead QuecKillRead -#define RegisterQMIDevice QuecRegisterQMIDevice -#define DeregisterQMIDevice QuecDeregisterQMIDevice diff --git a/package/wwan/driver/quectel_Gobinet/src/Readme.txt b/package/wwan/driver/quectel_Gobinet/src/Readme.txt deleted file mode 100644 index 0df201a89..000000000 --- a/package/wwan/driver/quectel_Gobinet/src/Readme.txt +++ /dev/null @@ -1,78 +0,0 @@ -Gobi3000 network driver 2011-07-29-1026 - -This readme covers important information concerning -the Gobi Net driver. - -Table of Contents - -1. What's new in this release -2. Known issues -3. Known platform issues - - -------------------------------------------------------------------------------- - -1. WHAT'S NEW - -This Release (Gobi3000 network driver 2011-07-29-1026) -a. Signal the device to leave low power mode on enumeration -b. Add "txQueueLength" parameter, which will set the Tx Queue Length -c. Send SetControlLineState message during driver/device removal -d. Change to new date-based versioning scheme - -Prior Release (Gobi3000 network driver 1.0.60) 06/29/2011 -a. Add UserspacePoll() function, to support select() -b. Fix possible deadlock on GobiUSBNetTXTimeout() -c. Fix memory leak on data transmission - -Prior Release (Gobi3000 network driver 1.0.50) 05/18/2011 -a. Add support for kernels up to 2.6.38 -b. Add support for dynamic interface binding - -Prior Release (Gobi3000 network driver 1.0.40) 02/28/2011 -a. In cases of QMI read errors, discard the error and continue reading. -b. Add "interruptible" parameter, which may be disabled for debugging purposes. - -Prior Release (Gobi3000 network driver 1.0.30) 01/05/2011 -a. Fix rare kernel PANIC if a process terminates while file handle close - or device removal is in progress. - -Prior Release (Gobi3000 network driver 1.0.20) 11/01/2010 -a. Fix possible kernel WARNING if device removed before QCWWANDisconnect(). -b. Fix multiple memory leaks in error cases. - -Prior Release (Gobi3000 network driver 1.0.10) 09/17/2010 -a. Initial release - -------------------------------------------------------------------------------- - -2. KNOWN ISSUES - -No known issues. - -------------------------------------------------------------------------------- - -3. KNOWN PLATFORM ISSUES - -a. Enabling autosuspend: - Autosuspend is supported by the Gobi3000 module and its drivers, - but by default it is not enabled by the open source kernel. As such, - the Gobi3000 module will not enter autosuspend unless the - user specifically turns on autosuspend with the command: - echo auto > /sys/bus/usb/devices/.../power/level -b. Ksoftirq using 100% CPU: - There is a known issue with the open source usbnet driver that can - result in infinite software interrupts. The fix for this is to test - (in the usbnet_bh() function) if the usb_device can submit URBs before - attempting to submit the response URB buffers. -c. NetworkManager does not recognize connection after resume: - After resuming from sleep/hibernate, NetworkManager may not recognize new - network connections by the Gobi device. This is a system issue not specific - to the Gobi device, which may result in dhcp not being run and the default - route not being updated. One way to fix this is to simply restart the - NetworkManager service. - -------------------------------------------------------------------------------- - - - diff --git a/package/wwan/driver/quectel_Gobinet/src/ReleaseNote.txt b/package/wwan/driver/quectel_Gobinet/src/ReleaseNote.txt deleted file mode 100644 index c3015e844..000000000 --- a/package/wwan/driver/quectel_Gobinet/src/ReleaseNote.txt +++ /dev/null @@ -1,166 +0,0 @@ -Release Notes - -[V1.6.3] -Date: 9/26/2021 -enhancement: - 1. change version to 1.6.3 -fix: - -[V1.6.2.16] -Date: 9/17/2021 -enhancement: -fix: - 1. add sdx6x platform support - -[V1.6.2.15] -Date: 3/23/2021 -enhancement: -fix: - 1. add sdx12 platform support - -[V1.6.2.14] -Date: 3/18/2021 -enhancement: -fix: - 1. fix kasam: use-after-free when do modem reboot stress test - 2. wait qmi_sync_thread() finish in DeregisterQMIDevice(), usb will disconnect when driver is still in qmi_sync_thread() - -[V1.6.2.13] -Date: 12/31/2020 -enhancement: -fix: - 1. fix quectel-CM open error when driver is still in qmi_sync_thread() but SOC enter sleep. - -[V1.6.2.12] -Date: 12/31/2020 -enhancement: -fix: - 1. for multi-pdn-call, can not ping when usb resume for usb suspend state. - -[V1.6.2.11] -Date: 11/7/2020 -enhancement: - 1. support QUECTEL_QMI_MERGE, for some SOC, control endpoint only support read max 64 bytes QMI. - for QMI that size > 64, we need read serval times, and merge. -fix: - -[V1.6.2.10] -Date: 9/15/2020 -enhancement: -fix: - 1. for X55, fix panic on kernel V2.6 ~ V3.2 - -[V1.6.2.9] -Date: 7/24/2020 -enhancement: -fix: - 1. for X55, fix errors on Big Endian SOC. - -[V1.6.2.8] -Date: 7/2/2020 -enhancement: - 1. support QMAPV5, UL AGG (porting from qmi_wwan_q) -fix: - 1. fix errors kernel V2.6 . - -[V1.6.2.7] -Date: 6/9/2020 -enhancement: -fix: - 1. when send qmi ctl request, clear qmi ctl response which's TID is same - -[V1.6.2.6] -Date: 5/19/2020 -enhancement: - 1. support bridge mode for multi-pdn-call -fix: - -[V1.6.2.5] -Date: 4/26/2020 -enhancement: - 1. fix netcard name as usbX (from ethX) -fix: - -...... - -[Quectel_WCDMA<E_Linux&Android_GobiNet_Driver_V1.5.0] -Date: 2018/04/17 -enhancement:: -1. support EG20&RG500 -2. fix set rx_urb_size as 1520. do not change accroding to MTU - -[Quectel_WCDMA<E_Linux&Android_GobiNet_Driver_V1.4.3] -Date: 2018/04/16 -enhancement:: -1. increase QMAP's rx_urb_size to 32KB - -[Quectel_WCDMA<E_Linux&Android_GobiNet_Driver_V1.4.2] -Date: 2018/04/03 -bug fix: -1. fix qmi client can not be released when quectel-CM killed by ¡®kill -9¡¯ - -[Quectel_WCDMA<E_Linux&Android_GobiNet_Driver_V1.4.1] -Date: 2018/02/20 -bug fix: -1. fix a compiler error on Kernel lager than 4.11 - -[Quectel_WCDMA<E_Linux&Android_GobiNet_Driver_V1.4.0] -Date: 2018/12/17 -bug fix: -1. fix a USB DMA error when built as GobiNet.ko on Kernel lager than 4.15 - -[Quectel_WCDMA<E_Linux&Android_GobiNet_Driver_V1.3.8] -[Quectel_WCDMA<E_Linux&Android_GobiNet_Driver_V1.3.7] -Date: 2018/09/25 -enhancement: -1. check skb length in tx_fixup functions. -2. when QMAP enabled, set FLAG_RX_ASSEMBLE to advoid 'RX errors' of ifconfig - -[Quectel_WCDMA<E_Linux&Android_GobiNet_Driver_V1.3.6] -Date: 2018/09/11 -enhancement: -1. support EG12 EM12 -2. optimization QMAP source code -3. fix compile errors and warnnings on kernel version 4.15 - -[Quectel_WCDMA<E_Linux&Android_GobiNet_Driver_V1.3.5] -Date: 2018/05/12 -enhancement: -1. provide two method to enable QMAP function. - 1.1 set module parameters 'qmap_mode' to X(1~4) to enable QMAP. - 1.2 ifconfig usb0 down, then 'echo X > /sys/class/usbX/qmap_mode' to enable QMAP - for above two method, X(1) used to enable 'IP Aggregation' and X(2~4) to enable 'IP Mux' -2. support bridge mode, also provide two method to enable bridge mode. - 2.1 set module parameters 'bridge_mode' to 1 to enable bridge mode. - 2.2 'echo 1 > /sys/class/usbX/bridge_mode' to enable bridge mode. - bridge mode setups: - brctl addbr br0; brctl addif br0 eth0; brctl addif usb0; ./quectel-CM; ifconfig br0 up; ifconfig eth0 up - then connect eth0 to PC by ethernet cable. and PC run DHCP tool to obtain network public IP address. - - 'WCDMA<E_QConnectManager_Linux&Android_V1.1.40' and later version is required to use QMAP and bridge mode. - -[Quectel_WCDMA<E_Linux&Android_GobiNet_Driver_V1.3.4] -Date: 2018/05/07 -enhancement: -1. support use 'AT$QCRMCALL=1,1' to setup data call. - when use 'AT$QCRMCALL=1,1', must set module parameters 'qcrmcall_mode' to 1, - and GobiNet Driver will do not tx&rx QMI. - -[Quectel_WCDMA<E_Linux&Android_GobiNet_Driver_V1.3.3] -Date: 2018/04/04 -optimization: -1. optimization QMAP source code - -[Quectel_WCDMA<E_Linux&Android_GobiNet_Driver_V1.3.2] -Date: 2018/03/23 -enhancement: -1. support Qualcomm Mux and Aggregation Protocol (QMAP) - 1.1 IP Mux: GobiNet Driver register multiple netcards, one netcards corresponding to one PDP. - and GobiNet Driver will tx/rx multiple IP packets maybe belong to different PDPs in one URB. - 1.2 IP Aggregation: GobiNet Driver will rx multiple IP packets in one URB, used to increase throughput theoretically by reducing the number of usb interrupts. - the max rx URB size of MDM9x07 is 4KB, the max rx URB size of MDM9x40&SDX20 is 16KB - -[Quectel_WCDMA<E_Linux&Android_GobiNet_Driver_V1.3.1] -Date: 2017/11/20 -enhancement: -1. support BG96 diff --git a/package/wwan/driver/quectel_Gobinet/src/Structs.h b/package/wwan/driver/quectel_Gobinet/src/Structs.h deleted file mode 100644 index d5a78e7af..000000000 --- a/package/wwan/driver/quectel_Gobinet/src/Structs.h +++ /dev/null @@ -1,529 +0,0 @@ -/*=========================================================================== -FILE: - Structs.h - -DESCRIPTION: - Declaration of structures used by the Qualcomm Linux USB Network driver - -FUNCTIONS: - none - -Copyright (c) 2011, Code Aurora Forum. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Code Aurora Forum nor - the names of its contributors may be used to endorse or promote - products derived from this software without specific prior written - permission. - - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -===========================================================================*/ - -//--------------------------------------------------------------------------- -// Pragmas -//--------------------------------------------------------------------------- -#pragma once - -//--------------------------------------------------------------------------- -// Include Files -//--------------------------------------------------------------------------- -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define QUECTEL_WWAN_QMAP 4 //MAX is 7 -#ifdef QUECTEL_WWAN_QMAP -#define QUECTEL_QMAP_MUX_ID 0x81 -#endif - -//#define QUECTEL_QMI_MERGE - -#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) -#define QUECTEL_BRIDGE_MODE -#endif - -#if (LINUX_VERSION_CODE <= KERNEL_VERSION( 2,6,21 )) -static inline void skb_reset_mac_header(struct sk_buff *skb) -{ - skb->mac.raw = skb->data; -} -#endif - -#if (LINUX_VERSION_CODE <= KERNEL_VERSION( 2,6,22 )) -#define bool u8 -#ifndef URB_FREE_BUFFER -#define URB_FREE_BUFFER_BY_SELF //usb_free_urb will not free, should free by self -#define URB_FREE_BUFFER 0x0100 /* Free transfer buffer with the URB */ -#endif - -/** - * usb_endpoint_type - get the endpoint's transfer type - * @epd: endpoint to be checked - * - * Returns one of USB_ENDPOINT_XFER_{CONTROL, ISOC, BULK, INT} according - * to @epd's transfer type. - */ -static inline int usb_endpoint_type(const struct usb_endpoint_descriptor *epd) -{ - return epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; -} -#endif - -#if (LINUX_VERSION_CODE <= KERNEL_VERSION( 2,6,18 )) -/** - * usb_endpoint_dir_in - check if the endpoint has IN direction - * @epd: endpoint to be checked - * - * Returns true if the endpoint is of type IN, otherwise it returns false. - */ -static inline int usb_endpoint_dir_in(const struct usb_endpoint_descriptor *epd) -{ - return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN); -} - -/** - * usb_endpoint_dir_out - check if the endpoint has OUT direction - * @epd: endpoint to be checked - * - * Returns true if the endpoint is of type OUT, otherwise it returns false. - */ -static inline int usb_endpoint_dir_out( - const struct usb_endpoint_descriptor *epd) -{ - return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT); -} - -/** - * usb_endpoint_xfer_int - check if the endpoint has interrupt transfer type - * @epd: endpoint to be checked - * - * Returns true if the endpoint is of type interrupt, otherwise it returns - * false. - */ -static inline int usb_endpoint_xfer_int( - const struct usb_endpoint_descriptor *epd) -{ - return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == - USB_ENDPOINT_XFER_INT); -} - -static inline int usb_autopm_set_interface(struct usb_interface *intf) -{ return 0; } - -static inline int usb_autopm_get_interface(struct usb_interface *intf) -{ return 0; } - -static inline int usb_autopm_get_interface_async(struct usb_interface *intf) -{ return 0; } - -static inline void usb_autopm_put_interface(struct usb_interface *intf) -{ } -static inline void usb_autopm_put_interface_async(struct usb_interface *intf) -{ } -static inline void usb_autopm_enable(struct usb_interface *intf) -{ } -static inline void usb_autopm_disable(struct usb_interface *intf) -{ } -static inline void usb_mark_last_busy(struct usb_device *udev) -{ } -#endif - -#if (LINUX_VERSION_CODE <= KERNEL_VERSION( 2,6,24 )) - #include "usbnet.h" -#else - #include -#endif - -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,25 )) - #include -#else - #include -#endif - -// Used in recursion, defined later below -struct sGobiUSBNet; - - -#if defined(QUECTEL_WWAN_QMAP) -#define QUECTEL_UL_DATA_AGG 1 - -#if defined(QUECTEL_UL_DATA_AGG) -struct ul_agg_ctx { - /* QMIWDS_ADMIN_SET_DATA_FORMAT_RESP TLV_0x17 and TLV_0x18 */ - uint ul_data_aggregation_max_datagrams; //UplinkDataAggregationMaxDatagramsTlv - uint ul_data_aggregation_max_size; //UplinkDataAggregationMaxSizeTlv - uint dl_minimum_padding; -}; -#endif -#endif - -/*=========================================================================*/ -// Struct sReadMemList -// -// Structure that defines an entry in a Read Memory linked list -/*=========================================================================*/ -typedef struct sReadMemList -{ - /* Data buffer */ - void * mpData; - - /* Transaction ID */ - u16 mTransactionID; - - /* Size of data buffer */ - u16 mDataSize; - - /* Next entry in linked list */ - struct sReadMemList * mpNext; - -} sReadMemList; - -/*=========================================================================*/ -// Struct sNotifyList -// -// Structure that defines an entry in a Notification linked list -/*=========================================================================*/ -typedef struct sNotifyList -{ - /* Function to be run when data becomes available */ - void (* mpNotifyFunct)(struct sGobiUSBNet *, u16, void *); - - /* Transaction ID */ - u16 mTransactionID; - - /* Data to provide as parameter to mpNotifyFunct */ - void * mpData; - - /* Next entry in linked list */ - struct sNotifyList * mpNext; - -} sNotifyList; - -/*=========================================================================*/ -// Struct sURBList -// -// Structure that defines an entry in a URB linked list -/*=========================================================================*/ -typedef struct sURBList -{ - /* The current URB */ - struct urb * mpURB; - - /* Next entry in linked list */ - struct sURBList * mpNext; - -} sURBList; - -/*=========================================================================*/ -// Struct sClientMemList -// -// Structure that defines an entry in a Client Memory linked list -// Stores data specific to a Service Type and Client ID -/*=========================================================================*/ -typedef struct sClientMemList -{ - /* Client ID for this Client */ - u16 mClientID; - - /* Linked list of Read entries */ - /* Stores data read from device before sending to client */ - sReadMemList * mpList; - - /* Linked list of Notification entries */ - /* Stores notification functions to be run as data becomes - available or the device is removed */ - sNotifyList * mpReadNotifyList; - - /* Linked list of URB entries */ - /* Stores pointers to outstanding URBs which need canceled - when the client is deregistered or the device is removed */ - sURBList * mpURBList; - - /* Next entry in linked list */ - struct sClientMemList * mpNext; - - /* Wait queue object for poll() */ - wait_queue_head_t mWaitQueue; - -} sClientMemList; - -/*=========================================================================*/ -// Struct sURBSetupPacket -// -// Structure that defines a USB Setup packet for Control URBs -// Taken from USB CDC specifications -/*=========================================================================*/ -typedef struct sURBSetupPacket -{ - /* Request type */ - u8 mRequestType; - - /* Request code */ - u8 mRequestCode; - - /* Value */ - u16 mValue; - - /* Index */ - u16 mIndex; - - /* Length of Control URB */ - u16 mLength; - -} sURBSetupPacket; - -// Common value for sURBSetupPacket.mLength -#define DEFAULT_READ_URB_LENGTH 0x1000 - -#ifdef QUECTEL_QMI_MERGE -#define MERGE_PACKET_IDENTITY 0x2c7c -#define MERGE_PACKET_VERSION 0x0001 -#define MERGE_PACKET_MAX_PAYLOAD_SIZE 56 -typedef struct sQMIMsgHeader { - u16 idenity; - u16 version; - u16 cur_len; - u16 total_len; -} sQMIMsgHeader; - -typedef struct sQMIMsgPacket { - sQMIMsgHeader header; - u16 len; - char buf[DEFAULT_READ_URB_LENGTH]; -} sQMIMsgPacket; -#endif - -#ifdef CONFIG_PM -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,29 )) -/*=========================================================================*/ -// Struct sAutoPM -// -// Structure used to manage AutoPM thread which determines whether the -// device is in use or may enter autosuspend. Also submits net -// transmissions asynchronously. -/*=========================================================================*/ -typedef struct sAutoPM -{ - /* Thread for atomic autopm function */ - struct task_struct * mpThread; - - /* Signal for completion when it's time for the thread to work */ - struct completion mThreadDoWork; - - /* Time to exit? */ - bool mbExit; - - /* List of URB's queued to be sent to the device */ - sURBList * mpURBList; - - /* URB list lock (for adding and removing elements) */ - spinlock_t mURBListLock; - - /* Length of the URB list */ - atomic_t mURBListLen; - - /* Active URB */ - struct urb * mpActiveURB; - - /* Active URB lock (for adding and removing elements) */ - spinlock_t mActiveURBLock; - - /* Duplicate pointer to USB device interface */ - struct usb_interface * mpIntf; - -} sAutoPM; -#endif -#endif /* CONFIG_PM */ - -/*=========================================================================*/ -// Struct sQMIDev -// -// Structure that defines the data for the QMI device -/*=========================================================================*/ -typedef struct sQMIDev -{ - /* Device number */ - dev_t mDevNum; - - /* Device class */ - struct class * mpDevClass; - - /* cdev struct */ - struct cdev mCdev; - - /* is mCdev initialized? */ - bool mbCdevIsInitialized; - - /* Pointer to read URB */ - struct urb * mpReadURB; - -//#define READ_QMI_URB_ERROR -#ifdef READ_QMI_URB_ERROR - struct timer_list mReadUrbTimer; -#endif - -#ifdef QUECTEL_QMI_MERGE - sQMIMsgPacket * mpQmiMsgPacket; -#endif - - /* Read setup packet */ - sURBSetupPacket * mpReadSetupPacket; - - /* Read buffer attached to current read URB */ - void * mpReadBuffer; - - /* Inturrupt URB */ - /* Used to asynchronously notify when read data is available */ - struct urb * mpIntURB; - - /* Buffer used by Inturrupt URB */ - void * mpIntBuffer; - - /* Pointer to memory linked list for all clients */ - sClientMemList * mpClientMemList; - - /* Spinlock for client Memory entries */ - spinlock_t mClientMemLock; - - /* Transaction ID associated with QMICTL "client" */ - atomic_t mQMICTLTransactionID; - -} sQMIDev; - -typedef struct { - u32 qmap_enabled; - u32 dl_data_aggregation_max_datagrams; - u32 dl_data_aggregation_max_size ; - u32 ul_data_aggregation_max_datagrams; - u32 ul_data_aggregation_max_size; - u32 dl_minimum_padding; -} QMAP_SETTING; - -/*=========================================================================*/ -// Struct sGobiUSBNet -// -// Structure that defines the data associated with the Qualcomm USB device -/*=========================================================================*/ -typedef struct sGobiUSBNet -{ - atomic_t refcount; - - /* Net device structure */ - struct usbnet * mpNetDev; -#ifdef QUECTEL_WWAN_QMAP - unsigned link_state; - int qmap_mode; - int qmap_size; - int qmap_version; - struct net_device *mpQmapNetDev[QUECTEL_WWAN_QMAP]; - struct tasklet_struct txq; - - QMAP_SETTING qmap_settings; -#if defined(QUECTEL_UL_DATA_AGG) - struct ul_agg_ctx agg_ctx; -#endif - -#ifdef QUECTEL_BRIDGE_MODE - int m_qmap_bridge_mode[QUECTEL_WWAN_QMAP]; -#endif -#endif - -#if 1 //def DATA_MODE_RP - bool mbMdm9x07; - bool mbMdm9x06; //for BG96 - /* QMI "device" work in IP Mode or ETH Mode */ - bool mbRawIPMode; -#ifdef QUECTEL_BRIDGE_MODE - int m_bridge_mode; - uint m_bridge_ipv4; - unsigned char mHostMAC[6]; -#endif - int m_qcrmcall_mode; -#endif - - struct completion mQMIReadyCompletion; - bool mbQMIReady; - bool mbProbeDone; - bool mbQMISyncIng; - - /* Usb device interface */ - struct usb_interface * mpIntf; - - /* Pointers to usbnet_open and usbnet_stop functions */ - int (* mpUSBNetOpen)(struct net_device *); - int (* mpUSBNetStop)(struct net_device *); - - /* Reason(s) why interface is down */ - /* Used by Gobi*DownReason */ - unsigned long mDownReason; -#define NO_NDIS_CONNECTION 0 -#define CDC_CONNECTION_SPEED 1 -#define DRIVER_SUSPENDED 2 -#define NET_IFACE_STOPPED 3 - - /* QMI "device" status */ - bool mbQMIValid; - - bool mbDeregisterQMIDevice; - - /* QMI "device" memory */ - sQMIDev mQMIDev; - - /* Device MEID */ - char mMEID[14]; - struct hrtimer timer; - struct tasklet_struct bh; - unsigned long - pending_num : 8, - pending_size : 16; - struct sk_buff *pending_pool[16]; - -#ifdef CONFIG_PM - #if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,29 )) - /* AutoPM thread */ - sAutoPM mAutoPM; -#endif -#endif /* CONFIG_PM */ -} sGobiUSBNet; - -/*=========================================================================*/ -// Struct sQMIFilpStorage -// -// Structure that defines the storage each file handle contains -// Relates the file handle to a client -/*=========================================================================*/ -typedef struct sQMIFilpStorage -{ - /* Client ID */ - u16 mClientID; - - /* Device pointer */ - sGobiUSBNet * mpDev; - -} sQMIFilpStorage; -