From: Michael Tremer Date: Thu, 8 Apr 2021 10:23:56 +0000 (+0000) Subject: Drop backports X-Git-Url: http://git.ipfire.org/?p=people%2Fpmueller%2Fipfire-2.x.git;a=commitdiff_plain;h=4330bf93be4700565612e33bfa994b4de9fb45c6 Drop backports These are some old drivers that we used to pull in from more recent kernels. Signed-off-by: Michael Tremer --- diff --git a/lfs/backports b/lfs/backports deleted file mode 100644 index 0ad9b0c5d5..0000000000 --- a/lfs/backports +++ /dev/null @@ -1,148 +0,0 @@ -############################################################################### -# # -# IPFire.org - A linux based firewall # -# Copyright (C) 2007-2018 IPFire Team # -# # -# 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 3 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, see . # -# # -############################################################################### - -############################################################################### -# Definitions -############################################################################### - -include Config - -VERSUFIX = ipfire$(KCFG) - -VER = 4.2.6-1 - -THISAPP = backports-$(VER) -DL_FILE = $(THISAPP).tar.xz -DL_FROM = $(URL_IPFIRE) -DIR_APP = $(DIR_SRC)/$(THISAPP) -TARGET = $(DIR_INFO)/$(THISAPP)-kmod-$(KVER)-$(VERSUFIX) - -#ifeq "$(BUILD_PLATFORM)" "arm" -CFLAGS += -fno-PIC -#endif - -############################################################################### -# Top-level Rules -############################################################################### - -objects = $(DL_FILE) - -$(DL_FILE) = $(DL_FROM)/$(DL_FILE) - -$(DL_FILE)_MD5 = 3f978eb56473d9289cf21ebbcb5aa80b - -install : $(TARGET) - -check : $(patsubst %,$(DIR_CHK)/%,$(objects)) - -download :$(patsubst %,$(DIR_DL)/%,$(objects)) - -md5 : $(subst %,%_MD5,$(objects)) - -dist: - $(PAK) - -############################################################################### -# Downloading, checking, md5sum -############################################################################### - -$(patsubst %,$(DIR_CHK)/%,$(objects)) : - @$(CHECK) - -$(patsubst %,$(DIR_DL)/%,$(objects)) : - @$(LOAD) - -$(subst %,%_MD5,$(objects)) : - @$(MD5) - -############################################################################### -# Installation Details -############################################################################### - -$(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects)) - @$(PREBUILD) - - # remove replaced kernel modules - rm -rf /lib/modules/$(KVER)-$(VERSUFIX)/kernel/drivers/net/wireless/rtl818x/rtl8180/rtl8180.ko - rm -rf /lib/modules/$(KVER)-$(VERSUFIX)/kernel/drivers/media - - @rm -rf $(DIR_APP) && cd $(DIR_SRC) && tar Jxf $(DIR_DL)/$(DL_FILE) - cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/backports-3.18.1-1-ipfire-build.patch - cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/backports-4.2.6-1-grsecurity.patch - cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/backports-4.2.6-1-add_usbnet_modules.patch -ifeq "$(BUILD_ARCH)" "x86_64" - cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/backports-3.18.1-1_no_dma_sgtable_on_x86_64.patch -endif - - # DVB patches - cd $(DIR_APP) && patch -Np2 < $(DIR_SRC)/src/patches/v4l-dvb_fix_tua6034_pll.patch - - # Wlan patches - cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/compat-drivers-3.8.3-ath_ignore_eeprom_regdomain.patch - cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/linux-3.14.22-iwlwifi-noibss_only_on_radar_chan.patch - cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/linux-3.10.37-rt2800usb_add_dlink_dwa137_usbid.patch - cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/backports-3.18.1-1_rt2x00usb_suppress_queue_warnings.patch - cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/backports-4.2.6-1-mt7601_upstream_fixes.patch - cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/backports-3.18.1-1_add_libertas_uap.patch - cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/backports-4.2.6-1_ath10k_remove_logspam.patch - cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/backports-4.2.6-1-ath9k_add_missing_mask_init.patch - - # smsc mac address patch for pandaboard and raspberry pi -ifeq "$(KCFG)" "-multi" - cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/linux-3.10-smsc95xx-add_mac_addr_param.patch -endif -ifeq "$(KCFG)" "-rpi" - cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/linux-3.10-smsc95xx-add_mac_addr_param.patch -endif - - # Patches form stable linux updates - cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/backports/backports-linux-upstream-1.patch - cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/backports/backports-linux-upstream-2.patch - cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/backports/backports-linux-upstream-3.patch - - # generate config - cd $(DIR_APP) && make KLIB=/lib/modules/$(KVER)-$(VERSUFIX)/ allmodconfig - - # Disable some settings - cd $(DIR_APP) && sed -i -e "s/CPTCFG_CFG80211_DEVELOPER_WARNINGS=y/# CPTCFG_CFG80211_DEVELOPER_WARNINGS is not set/g" .config - cd $(DIR_APP) && sed -i -e "s/CPTCFG_CFG80211_INTERNAL_REGDB=y/# CPTCFG_CFG80211_INTERNAL_REGDB is not set/g" .config - cd $(DIR_APP) && sed -i -e "s/CPTCFG_CFG80211_DEFAULT_PS=y/# CPTCFG_CFG80211_DEFAULT_PS is not set/g" .config - cd $(DIR_APP) && sed -i -e "s/CPTCFG_NFC=m/# CPTCFG_NFC is not set/g" .config - -ifeq "$(BUILD_ARCH)" "x86_64" - cd $(DIR_APP) && sed -i -e "s/CPTCFG_LIBERTAS_UAP=m/# CPTCFG_LIBERTAS_UAP is not set/g" .config -endif - - # Disable some modules (build fail/missing symbols) - cd $(DIR_APP) && sed -i -e "s/CPTCFG_VIDEO_AM437X_VPFE=m/# CPTCFG_VIDEO_AM437X_VPFE is not set/g" .config - cd $(DIR_APP) && sed -i -e "s/CPTCFG_VIDEO_VIA_CAMERA=m/# CPTCFG_VIDEO_VIA_CAMERA is not set/g" .config - cd $(DIR_APP) && sed -i -e "s/CPTCFG_VIDEO_XILINX=m/# CPTCFG_VIDEO_XILINX is not set/g" .config - cd $(DIR_APP) && sed -i -e "s/CPTCFG_IR_SUNXI=m/# CPTCFG_IR_SUNXI is not set/g" .config - cd $(DIR_APP) && sed -i -e "s/CPTCFG_BT_6LOWPAN=m/# CPTCFG_BT_6LOWPAN is not set/g" .config - - # Disable DEBUG - cd $(DIR_APP) && sed -i -e "s/.*DEBUG=y/# & is not set/g" .config - cd $(DIR_APP) && sed -i -e "s/.*DEBUGFS=y/# & is not set/g" .config - cd $(DIR_APP) && sed -i -e "s/=y is not set/ is not set/g" .config - - cd $(DIR_APP) && make $(MAKETUNING) KLIB=/lib/modules/$(KVER)-$(VERSUFIX)/ install - - @rm -rf $(DIR_APP) - @$(POSTBUILD) diff --git a/make.sh b/make.sh index 997caa92cd..829d5da97d 100755 --- a/make.sh +++ b/make.sh @@ -1196,7 +1196,6 @@ buildipfire() { case "${BUILD_ARCH}" in x86_64|aarch64) lfsmake2 linux KCFG="" -# lfsmake2 backports KCFG="" # lfsmake2 e1000e KCFG="" # lfsmake2 igb KCFG="" # lfsmake2 ixgbe KCFG="" @@ -1206,7 +1205,6 @@ buildipfire() { i586) # x86 kernel build lfsmake2 linux KCFG="" -# lfsmake2 backports KCFG="" # lfsmake2 e1000e KCFG="" # lfsmake2 igb KCFG="" # lfsmake2 ixgbe KCFG="" @@ -1217,7 +1215,6 @@ buildipfire() { armv5tel) # arm multi platform (Panda, Wandboard ...) kernel build lfsmake2 linux KCFG="-multi" -# lfsmake2 backports KCFG="-multi" # lfsmake2 e1000e KCFG="-multi" # lfsmake2 igb KCFG="-multi" # lfsmake2 ixgbe KCFG="-multi" diff --git a/src/patches/backports-3.18.1-1-ipfire-build.patch b/src/patches/backports-3.18.1-1-ipfire-build.patch deleted file mode 100644 index 0b2998e1c2..0000000000 --- a/src/patches/backports-3.18.1-1-ipfire-build.patch +++ /dev/null @@ -1,34 +0,0 @@ -diff -Naur backports-3.18.1-1.org/Makefile backports-3.18.1-1/Makefile ---- backports-3.18.1-1.org/Makefile 2014-12-21 22:37:13.000000000 +0100 -+++ backports-3.18.1-1/Makefile 2014-12-29 15:51:19.358111370 +0100 -@@ -8,9 +8,9 @@ - SHELL := /bin/bash - BACKPORT_PWD := $(shell pwd) - --KMODDIR ?= updates -+KMODDIR ?= kernel - ifneq ($(origin KLIB), undefined) --KMODPATH_ARG := "INSTALL_MOD_PATH=$(KLIB)" -+KMODPATH_ARG := - else - KLIB := /lib/modules/$(shell uname -r)/ - KMODPATH_ARG := -diff -Naur backports-3.18.1-1.org/Makefile.real backports-3.18.1-1/Makefile.real ---- backports-3.18.1-1.org/Makefile.real 2014-12-21 22:37:13.000000000 +0100 -+++ backports-3.18.1-1/Makefile.real 2014-12-29 15:51:40.934780933 +0100 -@@ -92,11 +92,11 @@ - @$(MAKE) -C $(KLIB_BUILD) M=$(BACKPORT_PWD) \ - INSTALL_MOD_DIR=$(KMODDIR) $(KMODPATH_ARG) \ - modules_install -- @./scripts/blacklist.sh $(KLIB)/ $(KLIB)/$(KMODDIR) -+# @./scripts/blacklist.sh $(KLIB)/ $(KLIB)/$(KMODDIR) - @./scripts/compress_modules.sh $(KLIB)/$(KMODDIR) -- @./scripts/check_depmod.sh -- @/sbin/depmod -a -- @./scripts/update-initramfs.sh $(KLIB) -+# @./scripts/check_depmod.sh -+# @/sbin/depmod -a -+# @./scripts/update-initramfs.sh $(KLIB) - @echo - @echo Your backported driver modules should be installed now. - @echo Reboot. diff --git a/src/patches/backports-3.18.1-1_add_libertas_uap.patch b/src/patches/backports-3.18.1-1_add_libertas_uap.patch deleted file mode 100644 index 9a5b01befa..0000000000 --- a/src/patches/backports-3.18.1-1_add_libertas_uap.patch +++ /dev/null @@ -1,5058 +0,0 @@ -diff -Naur backports-3.18.1-1.org/drivers/net/wireless/Kconfig backports-3.18.1-1/drivers/net/wireless/Kconfig ---- backports-3.18.1-1.org/drivers/net/wireless/Kconfig 2014-12-21 22:37:15.000000000 +0100 -+++ backports-3.18.1-1/drivers/net/wireless/Kconfig 2014-12-29 20:37:43.945764119 +0100 -@@ -55,6 +55,14 @@ - ---help--- - A driver for Marvell Libertas 8388 USB devices using thinfirm. - -+config LIBERTAS_UAP -+ tristate "Marvell 8xxx Libertas UAP" -+ depends on MAC80211 -+ depends on MMC -+ select FW_LOADER -+ ---help--- -+ Driver for Marvell Libertas 8xxx micro AP. -+ - config AIRO - depends on n - tristate "Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards" -diff -Naur backports-3.18.1-1.org/drivers/net/wireless/libertas_uap/Makefile backports-3.18.1-1/drivers/net/wireless/libertas_uap/Makefile ---- backports-3.18.1-1.org/drivers/net/wireless/libertas_uap/Makefile 1970-01-01 01:00:00.000000000 +0100 -+++ backports-3.18.1-1/drivers/net/wireless/libertas_uap/Makefile 2014-12-29 20:41:50.975778546 +0100 -@@ -0,0 +1,6 @@ -+obj-$(CPTCFG_LIBERTAS_UAP) += uap8xxx.o -+ -+uap8xxx-y += uap_main.o uap_sdio_mmc.o -+uap8xxx-$(CPTCFG_PROC_FS) += uap_proc.o uap_debug.o -+ -+EXTRA_CFLAGS += -DFPNUM='"52"' -DPXA3XX_DMA_ALIGN -DDEBUG_LEVEL1 -diff -Naur backports-3.18.1-1.org/drivers/net/wireless/libertas_uap/uap_debug.c backports-3.18.1-1/drivers/net/wireless/libertas_uap/uap_debug.c ---- backports-3.18.1-1.org/drivers/net/wireless/libertas_uap/uap_debug.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-3.18.1-1/drivers/net/wireless/libertas_uap/uap_debug.c 2014-12-29 20:37:43.949097590 +0100 -@@ -0,0 +1,260 @@ -+/** @file uap_debug.c -+ * @brief This file contains functions for debug proc file. -+ * -+ * Copyright (C) 2008-2009, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available along with the File in the gpl.txt file or by writing to -+ * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -+ * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ * -+ */ -+#ifdef CONFIG_PROC_FS -+#include "uap_headers.h" -+ -+/******************************************************** -+ Local Variables -+********************************************************/ -+ -+#define item_size(n) (sizeof ((uap_adapter *)0)->n) -+#define item_addr(n) ((u32) &((uap_adapter *)0)->n) -+ -+#define item_dbg_size(n) (sizeof (((uap_adapter *)0)->dbg.n)) -+#define item_dbg_addr(n) ((u32) &(((uap_adapter *)0)->dbg.n)) -+ -+#define item_dev_size(n) (sizeof ((uap_dev_t *)0)->n) -+#define item_dev_addr(n) ((u32) &((uap_dev_t *)0)->n) -+ -+/** MicroAp device offset */ -+#define OFFSET_UAP_DEV 0x01 -+/** Bluetooth adapter offset */ -+#define OFFSET_UAP_ADAPTER 0x02 -+ -+struct debug_data -+{ -+ /** Name */ -+ char name[32]; -+ /** Size */ -+ u32 size; -+ /** Address */ -+ u32 addr; -+ /** Offset */ -+ u32 offset; -+ /** Flag */ -+ u32 flag; -+}; -+ -+/* To debug any member of uap_adapter, simply add one line here. -+ */ -+static struct debug_data items[] = { -+ {"cmd_sent", item_dev_size(cmd_sent), 0, item_dev_addr(cmd_sent), -+ OFFSET_UAP_DEV}, -+ {"data_sent", item_dev_size(data_sent), 0, item_dev_addr(data_sent), -+ OFFSET_UAP_DEV}, -+ {"IntCounter", item_size(IntCounter), 0, item_addr(IntCounter), -+ OFFSET_UAP_ADAPTER}, -+ {"cmd_pending", item_size(cmd_pending), 0, item_addr(cmd_pending), -+ OFFSET_UAP_ADAPTER}, -+ {"num_cmd_h2c_fail", item_dbg_size(num_cmd_host_to_card_failure), 0, -+ item_dbg_addr(num_cmd_host_to_card_failure), OFFSET_UAP_ADAPTER}, -+ {"num_tx_h2c_fail", item_dbg_size(num_tx_host_to_card_failure), 0, -+ item_dbg_addr(num_tx_host_to_card_failure), OFFSET_UAP_ADAPTER}, -+ {"psmode", item_size(psmode), 0, item_addr(psmode), OFFSET_UAP_ADAPTER}, -+ {"ps_state", item_size(ps_state), 0, item_addr(ps_state), -+ OFFSET_UAP_ADAPTER}, -+#ifdef DEBUG_LEVEL1 -+ {"drvdbg", sizeof(drvdbg), (u32) & drvdbg, 0, 0} -+#endif -+}; -+ -+static int num_of_items = sizeof(items) / sizeof(items[0]); -+ -+/******************************************************** -+ Global Variables -+********************************************************/ -+ -+/******************************************************** -+ Local Functions -+********************************************************/ -+/** -+ * @brief proc read function -+ * -+ * @param page pointer to buffer -+ * @param s read data starting position -+ * @param off offset -+ * @param cnt counter -+ * @param eof end of file flag -+ * @param data data to output -+ * @return number of output data -+ */ -+static int uap_debug_proc_show(struct seq_file *s, void *data) { -+ int val = 0; -+ int i; -+ -+ struct debug_data *d = (struct debug_data *)s->private; -+ -+ if (MODULE_GET == 0) -+ return UAP_STATUS_FAILURE; -+ -+ for (i = 0; i < num_of_items; i++) { -+ if (d[i].size == 1) -+ val = *((u8 *) d[i].addr); -+ else if (d[i].size == 2) -+ val = *((u16 *) d[i].addr); -+ else if (d[i].size == 4) -+ val = *((u32 *) d[i].addr); -+ -+ seq_printf(s, "%s=%d\n", d[i].name, val); -+ } -+ MODULE_PUT; -+ return 0; -+} -+ -+static int uap_debug_proc_open(struct inode* inode, struct file* file) { -+ return single_open(file, uap_debug_proc_show, PDE_DATA(inode)); -+} -+ -+/** -+ * @brief proc write function -+ * -+ * @param f file pointer -+ * @param buf pointer to data buffer -+ * @param cnt data number to write -+ * @param data data to write -+ * @return number of data -+ */ -+static ssize_t uap_debug_proc_write(struct file *f, const char __user *buf, size_t cnt, loff_t *data) { -+ int r, i; -+ char *pdata; -+ char *p; -+ char *p0; -+ char *p1; -+ char *p2; -+ struct debug_data *d = (struct debug_data *)PDE_DATA(file_inode(f)); -+ -+ if (MODULE_GET == 0) -+ return UAP_STATUS_FAILURE; -+ -+ pdata = (char *) kmalloc(cnt, GFP_KERNEL); -+ if (pdata == NULL) { -+ MODULE_PUT; -+ return 0; -+ } -+ -+ if (copy_from_user(pdata, buf, cnt)) { -+ PRINTM(INFO, "Copy from user failed\n"); -+ kfree(pdata); -+ MODULE_PUT; -+ return 0; -+ } -+ -+ p0 = pdata; -+ for (i = 0; i < num_of_items; i++) { -+ do { -+ p = strstr(p0, d[i].name); -+ if (p == NULL) -+ break; -+ p1 = strchr(p, '\n'); -+ if (p1 == NULL) -+ break; -+ p0 = p1++; -+ p2 = strchr(p, '='); -+ if (!p2) -+ break; -+ p2++; -+ r = string_to_number(p2); -+ if (d[i].size == 1) -+ *((u8 *) d[i].addr) = (u8) r; -+ else if (d[i].size == 2) -+ *((u16 *) d[i].addr) = (u16) r; -+ else if (d[i].size == 4) -+ *((u32 *) d[i].addr) = (u32) r; -+ break; -+ } while (TRUE); -+ } -+ kfree(pdata); -+#ifdef DEBUG_LEVEL1 -+ printk(KERN_ALERT "drvdbg = 0x%x\n", drvdbg); -+ printk(KERN_ALERT "INFO (%08lx) %s\n", DBG_INFO, -+ (drvdbg & DBG_INFO) ? "X" : ""); -+ printk(KERN_ALERT "WARN (%08lx) %s\n", DBG_WARN, -+ (drvdbg & DBG_WARN) ? "X" : ""); -+ printk(KERN_ALERT "ENTRY (%08lx) %s\n", DBG_ENTRY, -+ (drvdbg & DBG_ENTRY) ? "X" : ""); -+ printk(KERN_ALERT "CMD_D (%08lx) %s\n", DBG_CMD_D, -+ (drvdbg & DBG_CMD_D) ? "X" : ""); -+ printk(KERN_ALERT "DAT_D (%08lx) %s\n", DBG_DAT_D, -+ (drvdbg & DBG_DAT_D) ? "X" : ""); -+ printk(KERN_ALERT "CMND (%08lx) %s\n", DBG_CMND, -+ (drvdbg & DBG_CMND) ? "X" : ""); -+ printk(KERN_ALERT "DATA (%08lx) %s\n", DBG_DATA, -+ (drvdbg & DBG_DATA) ? "X" : ""); -+ printk(KERN_ALERT "ERROR (%08lx) %s\n", DBG_ERROR, -+ (drvdbg & DBG_ERROR) ? "X" : ""); -+ printk(KERN_ALERT "FATAL (%08lx) %s\n", DBG_FATAL, -+ (drvdbg & DBG_FATAL) ? "X" : ""); -+ printk(KERN_ALERT "MSG (%08lx) %s\n", DBG_MSG, -+ (drvdbg & DBG_MSG) ? "X" : ""); -+#endif -+ MODULE_PUT; -+ return cnt; -+} -+ -+static const struct file_operations uap_debug_proc_fops = { -+ .owner = THIS_MODULE, -+ .open = uap_debug_proc_open, -+ .read = seq_read, -+ .llseek = seq_lseek, -+ .release = single_release, -+ .write = uap_debug_proc_write, -+}; -+ -+/******************************************************** -+ Global Functions -+********************************************************/ -+/** -+ * @brief create debug proc file -+ * -+ * @param priv pointer uap_private -+ * @param dev pointer net_device -+ * @return N/A -+ */ -+void -+uap_debug_entry(uap_private * priv, struct net_device *dev) -+{ -+ int i; -+ -+ if (priv->proc_entry == NULL) -+ return; -+ -+ for (i = 0; i < num_of_items; i++) { -+ if (items[i].flag & OFFSET_UAP_ADAPTER) -+ items[i].addr = items[i].offset + (u32) priv->adapter; -+ if (items[i].flag & OFFSET_UAP_DEV) -+ items[i].addr = items[i].offset + (u32) & priv->uap_dev; -+ } -+ proc_create_data("debug", 0644, priv->proc_entry, &uap_debug_proc_fops, -+ &items[0]); -+} -+ -+/** -+ * @brief remove proc file -+ * -+ * @param priv pointer uap_private -+ * @return N/A -+ */ -+void -+uap_debug_remove(uap_private * priv) -+{ -+ remove_proc_entry("debug", priv->proc_entry); -+} -+ -+#endif -diff -Naur backports-3.18.1-1.org/drivers/net/wireless/libertas_uap/uap_drv.h backports-3.18.1-1/drivers/net/wireless/libertas_uap/uap_drv.h ---- backports-3.18.1-1.org/drivers/net/wireless/libertas_uap/uap_drv.h 1970-01-01 01:00:00.000000000 +0100 -+++ backports-3.18.1-1/drivers/net/wireless/libertas_uap/uap_drv.h 2014-12-29 20:37:43.949097590 +0100 -@@ -0,0 +1,667 @@ -+/** @file uap_drv.h -+ * @brief This file contains Linux OS related definitions and -+ * declarations, uAP driver -+ * -+ * Copyright (C) 2008-2009, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available along with the File in the gpl.txt file or by writing to -+ * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -+ * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ * -+ */ -+ -+#ifndef _UAP_DRV_H -+#define _UAP_DRV_H -+ -+/** Driver release version */ -+#define DRIVER_VERSION "26146" -+ -+/** True */ -+#ifndef TRUE -+#define TRUE 1 -+#endif -+/** False */ -+#ifndef FALSE -+#define FALSE 0 -+#endif -+ -+/** Bit definitions */ -+#ifndef BIT -+#define BIT(x) (1UL << (x)) -+#endif -+ -+/** Dma addresses are 32-bits wide. */ -+#ifndef __ATTRIB_ALIGN__ -+#define __ATTRIB_ALIGN__ __attribute__((aligned(4))) -+#endif -+ -+/** attribute pack */ -+#ifndef __ATTRIB_PACK__ -+#define __ATTRIB_PACK__ __attribute__ ((packed)) -+#endif -+ -+/** Debug Macro definition*/ -+#ifdef DEBUG_LEVEL1 -+ -+extern u32 drvdbg; -+ -+/** Debug message control bit definition for drvdbg */ -+/** Debug message */ -+#define DBG_MSG BIT(0) -+/** Debug fatal message */ -+#define DBG_FATAL BIT(1) -+/** Debug error message */ -+#define DBG_ERROR BIT(2) -+/** Debug data message */ -+#define DBG_DATA BIT(3) -+/** Debug command message */ -+#define DBG_CMND BIT(4) -+ -+/** Debug data */ -+#define DBG_DAT_D BIT(16) -+/** Debug command */ -+#define DBG_CMD_D BIT(17) -+ -+/** Debug entry */ -+#define DBG_ENTRY BIT(28) -+/** Debug warning */ -+#define DBG_WARN BIT(29) -+/** Debug info */ -+#define DBG_INFO BIT(30) -+ -+/** Print info */ -+#define PRINTM_INFO(msg...) {if (drvdbg & DBG_INFO) printk(KERN_DEBUG msg);} -+/** Print warn message */ -+#define PRINTM_WARN(msg...) {if (drvdbg & DBG_WARN) printk(KERN_DEBUG msg);} -+/** Print entry */ -+#define PRINTM_ENTRY(msg...) {if (drvdbg & DBG_ENTRY) printk(KERN_DEBUG msg);} -+/** Print cmd_d */ -+#define PRINTM_CMD_D(msg...) {if (drvdbg & DBG_CMD_D) printk(KERN_DEBUG msg);} -+/** Print data_d */ -+#define PRINTM_DAT_D(msg...) {if (drvdbg & DBG_DAT_D) printk(KERN_DEBUG msg);} -+/** Print command */ -+#define PRINTM_CMND(msg...) {if (drvdbg & DBG_CMND) printk(KERN_DEBUG msg);} -+/** Print data */ -+#define PRINTM_DATA(msg...) {if (drvdbg & DBG_DATA) printk(KERN_DEBUG msg);} -+/** Print error message */ -+#define PRINTM_ERROR(msg...) {if (drvdbg & DBG_ERROR) printk(KERN_DEBUG msg);} -+/** Print fatal message */ -+#define PRINTM_FATAL(msg...) {if (drvdbg & DBG_FATAL) printk(KERN_DEBUG msg);} -+/** Print message */ -+#define PRINTM_MSG(msg...) {if (drvdbg & DBG_MSG) printk(KERN_ALERT msg);} -+/** Print level */ -+#define PRINTM(level,msg...) PRINTM_##level(msg) -+ -+#else -+ -+#define PRINTM(level,msg...) do {} while (0) -+ -+#endif /* DEBUG_LEVEL1 */ -+ -+/** Wait until a condition becomes true */ -+#define ASSERT(cond) \ -+do { \ -+ if (!(cond)) \ -+ PRINTM(INFO, "ASSERT: %s, %s:%i\n", \ -+ __FUNCTION__, __FILE__, __LINE__); \ -+} while(0) -+ -+/** Log enrty point for debugging */ -+#define ENTER() PRINTM(ENTRY, "Enter: %s, %s:%i\n", __FUNCTION__, \ -+ __FILE__, __LINE__) -+/** Log exit point for debugging */ -+#define LEAVE() PRINTM(ENTRY, "Leave: %s, %s:%i\n", __FUNCTION__, \ -+ __FILE__, __LINE__) -+ -+#ifdef DEBUG_LEVEL1 -+/** Dump buffer length */ -+#define DBG_DUMP_BUF_LEN 64 -+/** Maximum dump per line */ -+#define MAX_DUMP_PER_LINE 16 -+/** Data dump length */ -+#define DATA_DUMP_LEN 32 -+ -+static inline void -+hexdump(char *prompt, u8 * buf, int len) -+{ -+ int i; -+ char dbgdumpbuf[DBG_DUMP_BUF_LEN]; -+ char *ptr = dbgdumpbuf; -+ -+ printk(KERN_DEBUG "%s:\n", prompt); -+ for (i = 1; i <= len; i++) { -+ ptr += sprintf(ptr, "%02x ", *buf); -+ buf++; -+ if (i % MAX_DUMP_PER_LINE == 0) { -+ *ptr = 0; -+ printk(KERN_DEBUG "%s\n", dbgdumpbuf); -+ ptr = dbgdumpbuf; -+ } -+ } -+ if (len % MAX_DUMP_PER_LINE) { -+ *ptr = 0; -+ printk(KERN_DEBUG "%s\n", dbgdumpbuf); -+ } -+} -+ -+/** Debug command */ -+#define DBG_HEXDUMP_CMD_D(x,y,z) {if (drvdbg & DBG_CMD_D) hexdump(x,y,z);} -+/** Debug data */ -+#define DBG_HEXDUMP_DAT_D(x,y,z) {if (drvdbg & DBG_DAT_D) hexdump(x,y,z);} -+/** Debug hexdump */ -+#define DBG_HEXDUMP(level,x,y,z) DBG_HEXDUMP_##level(x,y,z) -+/** hexdump */ -+#define HEXDUMP(x,y,z) {if (drvdbg & DBG_INFO) hexdump(x,y,z);} -+#else -+/** Do nothing since debugging is not turned on */ -+#define DBG_HEXDUMP(level,x,y,z) do {} while (0) -+/** Do nothing since debugging is not turned on */ -+#define HEXDUMP(x,y,z) do {} while (0) -+#endif -+ -+/** -+ * Typedefs -+ */ -+/** Unsigned char */ -+typedef u8 BOOLEAN; -+ -+/* -+ * OS macro definitions -+ */ -+/** OS macro to get time */ -+#define os_time_get() jiffies -+ -+/** OS macro to update transfer start time */ -+#define UpdateTransStart(dev) { \ -+ dev->trans_start = jiffies; \ -+} -+ -+/** Try to get a reference to the module */ -+#define MODULE_GET try_module_get(THIS_MODULE) -+/** Decrease module reference count */ -+#define MODULE_PUT module_put(THIS_MODULE) -+ -+/** OS macro to initialize semaphore */ -+#define OS_INIT_SEMAPHORE(x) sema_init(x,1) -+/** OS macro to acquire blocking semaphore */ -+#define OS_ACQ_SEMAPHORE_BLOCK(x) down_interruptible(x) -+/** OS macro to acquire non-blocking semaphore */ -+#define OS_ACQ_SEMAPHORE_NOBLOCK(x) down_trylock(x) -+/** OS macro to release semaphore */ -+#define OS_REL_SEMAPHORE(x) up(x) -+ -+static inline void -+os_sched_timeout(u32 millisec) -+{ -+ set_current_state(TASK_INTERRUPTIBLE); -+ schedule_timeout((millisec * HZ) / 1000); -+} -+ -+/** Maximum size of ethernet packet */ -+#define MRVDRV_MAXIMUM_ETH_PACKET_SIZE 1514 -+ -+/** Maximum size of multicast list */ -+#define MRVDRV_MAX_MULTICAST_LIST_SIZE 32 -+ -+/** Find minimum */ -+#ifndef MIN -+#define MIN(a,b) ((a) < (b) ? (a) : (b)) -+#endif -+ -+/** Find maximum */ -+#ifndef MAX -+#define MAX(a,b) ((a) > (b) ? (a) : (b)) -+#endif -+ -+/** Find number of elements */ -+#ifndef NELEMENTS -+#define NELEMENTS(x) (sizeof(x)/sizeof(x[0])) -+#endif -+ -+/** Buffer Constants */ -+ -+/** Size of command buffer */ -+#define MRVDRV_SIZE_OF_CMD_BUFFER (2 * 1024) -+ -+/** Length of device length */ -+#define DEV_NAME_LEN 32 -+ -+/** Length of ethernet address */ -+#ifndef ETH_ALEN -+#define ETH_ALEN 6 -+#endif -+ -+/** Default watchdog timeout */ -+#define MRVDRV_DEFAULT_WATCHDOG_TIMEOUT (2 * HZ) -+ -+/** Success */ -+#define UAP_STATUS_SUCCESS (0) -+/** Failure */ -+#define UAP_STATUS_FAILURE (-1) -+/** Not accepted */ -+#define UAP_STATUS_NOT_ACCEPTED (-2) -+ -+/** Max loop count (* 100ms) for waiting device ready at init time */ -+#define MAX_WAIT_DEVICE_READY_COUNT 50 -+ -+/** Tx high watermark. Stop Tx queue after this is crossed */ -+#define TX_HIGH_WATERMARK 4 -+/** Tx low watermark. Restart Tx queue after this is crossed */ -+#define TX_LOW_WATERMARK 2 -+ -+/** Netlink protocol number */ -+#define NETLINK_MARVELL (MAX_LINKS - 1) -+/** Netlink maximum payload size */ -+#define NL_MAX_PAYLOAD 1024 -+/** Netlink multicast group number */ -+#define NL_MULTICAST_GROUP 1 -+ -+/** 20 seconds */ -+#define MRVDRV_TIMER_20S 20000 -+ -+/** Host Command option for wait till Send */ -+#define HostCmd_OPTION_WAITFORSEND 0x0001 -+/** Host Command option for wait for RSP */ -+#define HostCmd_OPTION_WAITFORRSP 0x0002 -+/** Host Command option for wait for RSP or Timeout */ -+#define HostCmd_OPTION_WAITFORRSP_TIMEOUT 0x0003 -+/** Host Command option for wait for RSP of sleep confirm */ -+#define HostCmd_OPTION_WAITFORRSP_SLEEPCONFIRM 0x0004 -+ -+/** Sleep until a condition gets true or a timeout elapses */ -+#define os_wait_interruptible_timeout(waitq, cond, timeout) \ -+ wait_event_interruptible_timeout(waitq, cond, ((timeout) * HZ / 1000)) -+ -+/** Private command ID to Host command */ -+#define UAPHOSTCMD (SIOCDEVPRIVATE + 1) -+ -+/** Private command ID to Power Mode */ -+#define UAP_POWER_MODE (SIOCDEVPRIVATE + 3) -+/** sleep_param */ -+typedef struct _ps_sleep_param -+{ -+ /** control bitmap */ -+ u32 ctrl_bitmap; -+ /** minimum sleep period (micro second) */ -+ u32 min_sleep; -+ /** maximum sleep period (micro second) */ -+ u32 max_sleep; -+} ps_sleep_param; -+ -+/** inactivity sleep_param */ -+typedef struct _inact_sleep_param -+{ -+ /** inactivity timeout (micro second) */ -+ u32 inactivity_to; -+ /** miniumu awake period (micro second) */ -+ u32 min_awake; -+ /** maximum awake period (micro second) */ -+ u32 max_awake; -+} inact_sleep_param; -+ -+/** flag for ps mode */ -+#define PS_FLAG_PS_MODE 1 -+/** flag for sleep param */ -+#define PS_FLAG_SLEEP_PARAM 2 -+/** flag for inactivity sleep param */ -+#define PS_FLAG_INACT_SLEEP_PARAM 4 -+ -+/** Disable power mode */ -+#define PS_MODE_DISABLE 0 -+/** Enable periodic dtim ps */ -+#define PS_MODE_PERIODIC_DTIM 1 -+/** Enable inactivity ps */ -+#define PS_MODE_INACTIVITY 2 -+ -+/** sleep parameter */ -+#define SLEEP_PARAMETER 1 -+/** inactivity sleep parameter */ -+#define INACTIVITY_SLEEP_PARAMETER 2 -+/** ps_mgmt */ -+typedef struct _ps_mgmt -+{ -+ /** flags for valid field */ -+ u16 flags; -+ /** power mode */ -+ u16 ps_mode; -+ /** sleep param */ -+ ps_sleep_param sleep_param; -+ /** inactivity sleep param */ -+ inact_sleep_param inact_param; -+} ps_mgmt; -+ -+/** Semaphore structure */ -+typedef struct semaphore SEMAPHORE; -+ -+/** Global Varibale Declaration */ -+/** Private data structure of the device */ -+typedef struct _uap_private uap_private; -+/** Adapter data structure of the device */ -+typedef struct _uap_adapter uap_adapter; -+/** private structure */ -+extern uap_private *uappriv; -+ -+/** ENUM definition*/ -+ -+/** Hardware status codes */ -+typedef enum _HARDWARE_STATUS -+{ -+ HWReady, -+ HWInitializing, -+ HWReset, -+ HWClosing, -+ HWNotReady -+} HARDWARE_STATUS; -+ -+/** info for debug purpose */ -+typedef struct _uap_dbg -+{ -+ /** Number of host to card command failures */ -+ u32 num_cmd_host_to_card_failure; -+ /** Number of host to card Tx failures */ -+ u32 num_tx_host_to_card_failure; -+} uap_dbg; -+ -+/** Set thread state */ -+#define OS_SET_THREAD_STATE(x) set_current_state(x) -+ -+typedef struct -+{ -+ /** Task */ -+ struct task_struct *task; -+ /** Queue */ -+ wait_queue_head_t waitQ; -+ /** PID */ -+ pid_t pid; -+ /** Private structure */ -+ void *priv; -+} uap_thread; -+ -+static inline void -+uap_activate_thread(uap_thread * thr) -+{ -+ /** Record the thread pid */ -+ thr->pid = current->pid; -+ -+ /** Initialize the wait queue */ -+ init_waitqueue_head(&thr->waitQ); -+} -+ -+static inline void -+uap_deactivate_thread(uap_thread * thr) -+{ -+ thr->pid = 0; -+ return; -+} -+ -+static inline void -+uap_create_thread(int (*uapfunc) (void *), uap_thread * thr, char *name) -+{ -+ thr->task = kthread_run(uapfunc, thr, "%s", name); -+} -+ -+static inline int -+uap_terminate_thread(uap_thread * thr) -+{ -+ /* Check if the thread is active or not */ -+ if (!thr->pid) -+ return -1; -+ kthread_stop(thr->task); -+ return 0; -+} -+ -+/** Data structure for the Marvell uAP device */ -+typedef struct _uap_dev -+{ -+ /** device name */ -+ char name[DEV_NAME_LEN]; -+ /** card pointer */ -+ void *card; -+ /** IO port */ -+ u32 ioport; -+ /** Rx unit */ -+ u8 rx_unit; -+ /** Data sent: -+ TRUE - Data is sent to fw, no Tx Done received -+ FALSE - Tx done received for previous Tx */ -+ BOOLEAN data_sent; -+ /** CMD sent: -+ TRUE - CMD is sent to fw, no CMD Done received -+ FALSE - CMD done received for previous CMD */ -+ BOOLEAN cmd_sent; -+ /** netdev pointer */ -+ struct net_device *netdev; -+} uap_dev_t, *puap_dev_t; -+ -+/** Private structure for the MV device */ -+struct _uap_private -+{ -+ /** Device open */ -+ int open; -+ -+ /** Device adapter structure */ -+ uap_adapter *adapter; -+ /** Device structure */ -+ uap_dev_t uap_dev; -+ -+ /** Net device statistics structure */ -+ struct net_device_stats stats; -+ -+ /** Number of Tx timeouts */ -+ u32 num_tx_timeout; -+ -+ /** Media connection status */ -+ BOOLEAN MediaConnected; -+ -+#ifdef CONFIG_PROC_FS -+ struct proc_dir_entry *proc_uap; -+ struct proc_dir_entry *proc_entry; -+#endif /* CONFIG_PROC_FS */ -+ -+ /** Firmware helper */ -+ const struct firmware *fw_helper; -+ /** Firmware */ -+ const struct firmware *firmware; -+ /** Hotplug device */ -+ struct device *hotplug_device; -+ /** thread to service interrupts */ -+ uap_thread MainThread; -+ /** Driver lock */ -+ spinlock_t driver_lock; -+ /** Driver lock flags */ -+ ulong driver_flags; -+ -+}; -+ -+/** PS_CMD_ConfirmSleep */ -+typedef struct _PS_CMD_ConfirmSleep -+{ -+ /** SDIO Length */ -+ u16 SDLen; -+ /** SDIO Type */ -+ u16 SDType; -+ /** Command */ -+ u16 Command; -+ /** Size */ -+ u16 Size; -+ /** Sequence number */ -+ u16 SeqNum; -+ /** Result */ -+ u16 Result; -+} __ATTRIB_PACK__ PS_CMD_ConfirmSleep, *PPS_CMD_ConfirmSleep; -+ -+/** Wlan Adapter data structure*/ -+struct _uap_adapter -+{ -+ /** Power save confirm sleep command */ -+ PS_CMD_ConfirmSleep PSConfirmSleep; -+ /** Device status */ -+ HARDWARE_STATUS HardwareStatus; -+ /** Interrupt counter */ -+ u32 IntCounter; -+ /** Tx packet queue */ -+ struct sk_buff_head tx_queue; -+ /** Cmd packet queue */ -+ struct sk_buff_head cmd_queue; -+ /** Command sequence number */ -+ u16 SeqNum; -+ /** Command buffer */ -+ u8 *CmdBuf; -+ /** cmd pending flag */ -+ u8 cmd_pending; -+ /** cmd wait option */ -+ u8 cmd_wait_option; -+ /** Command buffer length */ -+ u32 CmdSize; -+ /** Command wait queue */ -+ wait_queue_head_t cmdwait_q __ATTRIB_ALIGN__; -+ /** Command wait queue state flag */ -+ u8 CmdWaitQWoken; -+ /** PnP support */ -+ BOOLEAN SurpriseRemoved; -+ /** Debug */ -+ uap_dbg dbg; -+ /** Netlink kernel socket */ -+ struct sock *nl_sk; -+ /** Semaphore for CMD */ -+ SEMAPHORE CmdSem; -+ /** Power Save mode */ -+ u8 psmode; -+ /** Power Save state */ -+ u8 ps_state; -+ /** Number of wakeup tries */ -+ u32 WakeupTries; -+}; -+ -+static inline int -+os_upload_rx_packet(uap_private * priv, struct sk_buff *skb) -+{ -+ skb->dev = priv->uap_dev.netdev; -+ skb->protocol = eth_type_trans(skb, priv->uap_dev.netdev); -+ skb->ip_summed = CHECKSUM_UNNECESSARY; -+ if (in_interrupt()) -+ netif_rx(skb); -+ else -+ netif_rx_ni(skb); -+ return 0; -+} -+ -+/* -+ * netif carrier_on/off and start(wake)/stop_queue handling -+ */ -+static inline void -+os_carrier_on(uap_private * priv) -+{ -+ if (!netif_carrier_ok(priv->uap_dev.netdev) && -+ (priv->MediaConnected == TRUE)) { -+ netif_carrier_on(priv->uap_dev.netdev); -+ } -+} -+ -+static inline void -+os_carrier_off(uap_private * priv) -+{ -+ if (netif_carrier_ok(priv->uap_dev.netdev)) { -+ netif_carrier_off(priv->uap_dev.netdev); -+ } -+} -+ -+static inline void -+os_start_queue(uap_private * priv) -+{ -+ if (netif_queue_stopped(priv->uap_dev.netdev) && -+ (priv->MediaConnected == TRUE)) { -+ netif_wake_queue(priv->uap_dev.netdev); -+ } -+} -+ -+static inline void -+os_stop_queue(uap_private * priv) -+{ -+ if (!netif_queue_stopped(priv->uap_dev.netdev)) { -+ netif_stop_queue(priv->uap_dev.netdev); -+ } -+} -+ -+/** Interface specific header */ -+#define INTF_HEADER_LEN 4 -+ -+/** headroom alignment for tx packet */ -+#define HEADER_ALIGNMENT 8 -+ -+/** The number of times to try when polling for status bits */ -+#define MAX_POLL_TRIES 100 -+ -+/** Length of SNAP header */ -+#define MRVDRV_SNAP_HEADER_LEN 8 -+ -+/** Extra length of Tx packet buffer */ -+#define EXTRA_LEN 36 -+ -+/** Buffer size for ethernet Tx packets */ -+#define MRVDRV_ETH_TX_PACKET_BUFFER_SIZE \ -+ (ETH_FRAME_LEN + sizeof(TxPD) + EXTRA_LEN) -+ -+/** Buffer size for ethernet Rx packets */ -+#define MRVDRV_ETH_RX_PACKET_BUFFER_SIZE \ -+ (ETH_FRAME_LEN + sizeof(RxPD) \ -+ + MRVDRV_SNAP_HEADER_LEN + EXTRA_LEN) -+ -+/** Packet type: data, command & event */ -+typedef enum _mv_type -+{ -+ MV_TYPE_DAT = 0, -+ MV_TYPE_CMD = 1, -+ MV_TYPE_EVENT = 3 -+} mv_type; -+ -+/** Disable interrupt */ -+#define OS_INT_DISABLE spin_lock_irqsave(&priv->driver_lock, priv->driver_flags) -+/** Enable interrupt */ -+#define OS_INT_RESTORE spin_unlock_irqrestore(&priv->driver_lock, priv->driver_flags) -+ -+int uap_process_rx_packet(uap_private * priv, struct sk_buff *skb); -+void uap_interrupt(uap_private * priv); -+uap_private *uap_add_card(void *card); -+int uap_remove_card(void *card); -+int uap_process_event(uap_private * priv, u8 * payload, uint len); -+int uap_soft_reset(uap_private * priv); -+int uap_process_sleep_confirm_resp(uap_private * priv, u8 * resp, int resp_len); -+ -+#ifdef CONFIG_PROC_FS -+/** The proc fs interface */ -+void uap_proc_entry(uap_private * priv, struct net_device *dev); -+void uap_proc_remove(uap_private * priv); -+int string_to_number(char *s); -+void uap_debug_entry(uap_private * priv, struct net_device *dev); -+void uap_debug_remove(uap_private * priv); -+#endif /* CONFIG_PROC_FS */ -+ -+int sbi_register(void); -+ -+void sbi_unregister(void); -+int sbi_register_dev(uap_private * priv); -+int sbi_unregister_dev(uap_private * priv); -+int sbi_prog_fw_w_helper(uap_private *); -+ -+int sbi_host_to_card(uap_private * priv, u8 * payload, u16 nb); -+int sbi_enable_host_int(uap_private * priv); -+int sbi_disable_host_int(uap_private * priv); -+ -+int sbi_get_int_status(uap_private * priv, u8 * ireg); -+/** Check firmware status */ -+int sbi_check_fw_status(uap_private *, int); -+int sbi_prog_helper(uap_private *); -+ -+int sbi_wakeup_firmware(uap_private * priv); -+ -+#endif /* _UAP_DRV_H */ -diff -Naur backports-3.18.1-1.org/drivers/net/wireless/libertas_uap/uap_fw.h backports-3.18.1-1/drivers/net/wireless/libertas_uap/uap_fw.h ---- backports-3.18.1-1.org/drivers/net/wireless/libertas_uap/uap_fw.h 1970-01-01 01:00:00.000000000 +0100 -+++ backports-3.18.1-1/drivers/net/wireless/libertas_uap/uap_fw.h 2014-12-29 20:37:43.949097590 +0100 -@@ -0,0 +1,359 @@ -+/** @file uap_fw.h -+ * -+ * @brief This file contains firmware specific defines. -+ * -+ * Copyright (C) 2008-2009, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available along with the File in the gpl.txt file or by writing to -+ * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -+ * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ * -+ */ -+/******************************************************** -+Change log: -+ 02/26/08: Initial creation -+********************************************************/ -+ -+#ifndef _UAP_FW_H -+#define _UAP_FW_H -+ -+/** uap upload size */ -+#define UAP_UPLD_SIZE 2312 -+/** Packet type Micro AP */ -+#define PKT_TYPE_MICROAP 1 -+/** Packet type client */ -+#define PKT_TYPE_CLIENT 0 -+ -+/** TxPD descriptor */ -+typedef struct _TxPD -+{ -+ /** Bss Type */ -+ u8 BssType; -+ /** Bss num */ -+ u8 BssNum; -+ /** Tx packet length */ -+ u16 TxPktLength; -+ /** Tx packet offset */ -+ u16 TxPktOffset; -+ /** Tx packet type */ -+ u16 TxPktType; -+ /** Tx Control */ -+ u32 TxControl; -+ /** reserved */ -+ u32 reserved[2]; -+} __ATTRIB_PACK__ TxPD, *PTxPD; -+ -+/** RxPD Descriptor */ -+typedef struct _RxPD -+{ -+ /** Bss Type */ -+ u8 BssType; -+ /** Bss Num */ -+ u8 BssNum; -+ /** Tx packet length */ -+ u16 RxPktLength; -+ /** Tx packet offset */ -+ u16 RxPktOffset; -+} __ATTRIB_PACK__ RxPD, *PRxPD; -+ -+#ifdef BIG_ENDIAN -+/** Convert from 16 bit little endian format to CPU format */ -+#define uap_le16_to_cpu(x) le16_to_cpu(x) -+/** Convert from 32 bit little endian format to CPU format */ -+#define uap_le32_to_cpu(x) le32_to_cpu(x) -+/** Convert from 64 bit little endian format to CPU format */ -+#define uap_le64_to_cpu(x) le64_to_cpu(x) -+/** Convert to 16 bit little endian format from CPU format */ -+#define uap_cpu_to_le16(x) cpu_to_le16(x) -+/** Convert to 32 bit little endian format from CPU format */ -+#define uap_cpu_to_le32(x) cpu_to_le32(x) -+/** Convert to 64 bit little endian format from CPU format */ -+#define uap_cpu_to_le64(x) cpu_to_le64(x) -+ -+/** Convert TxPD to little endian format from CPU format */ -+#define endian_convert_TxPD(x); \ -+ { \ -+ (x)->TxPktLength = uap_cpu_to_le16((x)->TxPktLength); \ -+ (x)->TxPktOffset = uap_cpu_to_le32((x)->TxPktOffset); \ -+ (x)->TxControl = uap_cpu_to_le32((x)->TxControl); \ -+ (x)->TxPktType = uap_cpu_to_le32((x)->TxPktType); \ -+ } -+ -+/** Convert RxPD from little endian format to CPU format */ -+#define endian_convert_RxPD(x); \ -+ { \ -+ (x)->RxPktLength = uap_le16_to_cpu((x)->RxPktLength); \ -+ (x)->RxPktOffset = uap_le32_to_cpu((x)->RxPktOffset); \ -+ } -+#else /* BIG_ENDIAN */ -+/** Do nothing */ -+#define uap_le16_to_cpu(x) x -+/** Do nothing */ -+#define uap_le32_to_cpu(x) x -+/** Do nothing */ -+#define uap_le64_to_cpu(x) x -+/** Do nothing */ -+#define uap_cpu_to_le16(x) x -+/** Do nothing */ -+#define uap_cpu_to_le32(x) x -+/** Do nothing */ -+#define uap_cpu_to_le64(x) x -+ -+/** Do nothing */ -+#define endian_convert_TxPD(x) -+/** Do nothing */ -+#define endian_convert_RxPD(x) -+#endif /* BIG_ENDIAN */ -+ -+/** Host Command ID : Function initialization */ -+#define HostCmd_CMD_FUNC_INIT 0x00a9 -+/** Host Command ID : Function shutdown */ -+#define HostCmd_CMD_FUNC_SHUTDOWN 0x00aa -+ -+/** Host Command id: SYS_INFO */ -+#define HOST_CMD_APCMD_SYS_INFO 0x00ae -+/** Host Command id: SYS_RESET */ -+#define HOST_CMD_APCMD_SYS_RESET 0x00af -+/** Host Command id: SYS_CONFIGURE */ -+#define HOST_CMD_APCMD_SYS_CONFIGURE 0x00b0 -+/** Host Command id: BSS_START */ -+#define HOST_CMD_APCMD_BSS_START 0x00b1 -+/** Host Command id: SYS_STOP */ -+#define HOST_CMD_APCMD_BSS_STOP 0x00b2 -+/** Host Command id: STA_LIST */ -+#define HOST_CMD_APCMD_STA_LIST 0x00b3 -+/** Host Command id: STA_FILTER_TABLE */ -+#define HOST_CMD_APCMD_STA_FILTER_TABLE 0x00b4 -+/** Host Command id: STA_DEAUTH */ -+#define HOST_CMD_APCMD_STA_DEAUTH 0x00b5 -+/** Host Command id: SOFT_RESET */ -+#define HOST_CMD_APCMD_SOFT_RESET 0x00d5 -+/** Host Command id: POWER_MGMT_EXT */ -+#define HOST_CMD_POWER_MGMT_EXT 0x00ef -+/** Host Command id: SLEEP_CONFIRM*/ -+#define HOST_CMD_SLEEP_CONFIRM 0x00d8 -+ -+/** TLV type : SSID */ -+#define TLV_TYPE_SSID 0x0000 -+/** TLV type : Rates */ -+#define TLV_TYPE_RATES 0x0001 -+/** TLV type : PHY DS */ -+#define TLV_TYPE_PHY_DS 0x0003 -+ -+/** TLV Id : Base id */ -+#define PROPRIETARY_TLV_BASE_ID 0x0100 -+/** TLV Id : AP_MAC_ADDRESS */ -+#define MRVL_AP_MAC_ADDRESS_TLV_ID (PROPRIETARY_TLV_BASE_ID + 43) -+/** TLV Id : Beacon period */ -+#define MRVL_BEACON_PERIOD_TLV_ID (PROPRIETARY_TLV_BASE_ID + 44) -+/** TLV Id : Dtim period */ -+#define MRVL_DTIM_PERIOD_TLV_ID (PROPRIETARY_TLV_BASE_ID + 45) -+/** TLV Id : Basic rates */ -+#define MRVL_BASIC_RATES_TLV_ID (PROPRIETARY_TLV_BASE_ID + 46) -+/** TLV Id : Tx Power */ -+#define MRVL_TX_POWER_TLV_ID (PROPRIETARY_TLV_BASE_ID + 47) -+/** TLV Id : Broadcast SSID control */ -+#define MRVL_BCAST_SSID_CTL_TLV_ID (PROPRIETARY_TLV_BASE_ID + 48) -+/** TLV Id : Preamble control */ -+#define MRVL_PREAMBLE_CTL_TLV_ID (PROPRIETARY_TLV_BASE_ID + 49) -+/** TLV Id : Antenna control */ -+#define MRVL_ANTENNA_CTL_TLV_ID (PROPRIETARY_TLV_BASE_ID + 50) -+/** TLV Id : RTS threshold */ -+#define MRVL_RTS_THRESHOLD_TLV_ID (PROPRIETARY_TLV_BASE_ID + 51) -+/** TLV Id : Radio control */ -+#define MRVL_RADIO_CTL_TLV_ID (PROPRIETARY_TLV_BASE_ID + 52) -+/** TLV Id : TX data rate */ -+#define MRVL_TX_DATA_RATE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 53) -+/** TLV Id : Packet forward control */ -+#define MRVL_PKT_FWD_CTL_TLV_ID (PROPRIETARY_TLV_BASE_ID + 54) -+/** TLV Id : STA info */ -+#define MRVL_STA_INFO_TLV_ID (PROPRIETARY_TLV_BASE_ID + 55) -+/** TLV Id : STA MAC address filter */ -+#define MRVL_STA_MAC_ADDR_FILTER_TLV_ID (PROPRIETARY_TLV_BASE_ID + 56) -+/** TLV Id : STA ageout timer */ -+#define MRVL_STA_AGEOUT_TIMER_TLV_ID (PROPRIETARY_TLV_BASE_ID + 57) -+/** TLV Id : Security config */ -+#define MRVL_SECURITY_CFG_TLV_ID (PROPRIETARY_TLV_BASE_ID + 58) -+/** TLV Id : WEP KEY */ -+#define MRVL_WEP_KEY_TLV_ID (PROPRIETARY_TLV_BASE_ID + 59) -+/** TLV Id : WPA Passphrase */ -+#define MRVL_WPA_PASSPHRASE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 60) -+ -+/** Action get */ -+#define ACTION_GET 0 -+/** Action set */ -+#define ACTION_SET 1 -+/** Length of ethernet address */ -+#ifndef ETH_ALEN -+#define ETH_ALEN 6 -+#endif -+ -+/** HostCmd_DS_GEN */ -+typedef struct -+{ -+ /** Command */ -+ u16 Command; -+ /** Size */ -+ u16 Size; -+ /** Sequence number */ -+ u16 SeqNum; -+ /** Result */ -+ u16 Result; -+} __ATTRIB_PACK__ HostCmd_DS_GEN; -+ -+/** Size of HostCmd_DS_GEN */ -+#define S_DS_GEN sizeof(HostCmd_DS_GEN) -+ -+/** _HostCmd_HEADER*/ -+typedef struct -+{ -+ /** Command Header : Command */ -+ u16 Command; -+ /** Command Header : Size */ -+ u16 Size; -+} __ATTRIB_PACK__ HostCmd_HEADER; -+ -+/** HostCmd_SYS_CONFIG */ -+typedef struct _HostCmd_SYS_CONFIG -+{ -+ /** CMD Action GET/SET*/ -+ u16 Action; -+ /** Tlv buffer */ -+ u8 TlvBuffer[0]; -+} __ATTRIB_PACK__ HostCmd_SYS_CONFIG; -+ -+/** HostCmd_DS_POWER_MGMT_EXT */ -+typedef struct _HostCmd_DS_POWER_MGMT_EXT -+{ -+ /** CMD Action Get/Set*/ -+ u16 action; -+ /** power mode */ -+ u16 power_mode; -+} __ATTRIB_PACK__ HostCmd_DS_POWER_MGMT_EXT; -+ -+/** _HostCmd_DS_COMMAND*/ -+typedef struct _HostCmd_DS_COMMAND -+{ -+ -+ /** Command Header : Command */ -+ u16 Command; -+ /** Command Header : Size */ -+ u16 Size; -+ /** Command Header : Sequence number */ -+ u16 SeqNum; -+ /** Command Header : Result */ -+ u16 Result; -+ /** Command Body */ -+ union -+ { -+ HostCmd_SYS_CONFIG sys_config; -+ HostCmd_DS_POWER_MGMT_EXT pm_cfg; -+ -+ } params; -+} __ATTRIB_PACK__ HostCmd_DS_COMMAND; -+ -+/** MrvlIEtypesHeader_*/ -+typedef struct _MrvlIEtypesHeader -+{ -+ /** Header type */ -+ u16 Type; -+ /** Header length */ -+ u16 Len; -+} __ATTRIB_PACK__ MrvlIEtypesHeader_t; -+ -+/** MrvlIEtypes_Data_t */ -+typedef struct _MrvlIEtypes_Data_t -+{ -+ /** Header */ -+ MrvlIEtypesHeader_t Header; -+ /** Data */ -+ u8 Data[1]; -+} __ATTRIB_PACK__ MrvlIEtypes_Data_t; -+ -+/** MrvlIEtypes_ChanListParamSet_t */ -+typedef struct _MrvlIEtypes_MacAddr_t -+{ -+ /** Header */ -+ MrvlIEtypesHeader_t Header; -+ /** AP MAC address */ -+ u8 ApMacAddr[ETH_ALEN]; -+} __ATTRIB_PACK__ MrvlIEtypes_MacAddr_t; -+ -+/** Event ID: BSS started */ -+#define MICRO_AP_EV_ID_BSS_START 46 -+ -+/** Event ID: BSS idle event */ -+#define MICRO_AP_EV_BSS_IDLE 67 -+ -+/** Event ID: BSS active event */ -+#define MICRO_AP_EV_BSS_ACTIVE 68 -+ -+/** Event ID: PS_AWAKE */ -+#define EVENT_PS_AWAKE 0x0a -+ -+/** Event ID: PS_SLEEP */ -+#define EVENT_PS_SLEEP 0x0b -+ -+/** PS_STATE */ -+typedef enum _PS_STATE -+{ -+ PS_STATE_AWAKE, -+ PS_STATE_PRE_SLEEP, -+ PS_STATE_SLEEP -+} PS_STATE; -+ -+/** TLV type: AP Sleep param */ -+#define TLV_TYPE_AP_SLEEP_PARAM (PROPRIETARY_TLV_BASE_ID + 106) -+/** TLV type: AP Inactivity Sleep param */ -+#define TLV_TYPE_AP_INACT_SLEEP_PARAM (PROPRIETARY_TLV_BASE_ID + 107) -+ -+/** MrvlIEtypes_sleep_param_t */ -+typedef struct _MrvlIEtypes_sleep_param_t -+{ -+ /** Header */ -+ MrvlIEtypesHeader_t header; -+ /** control bitmap */ -+ u32 ctrl_bitmap; -+ /** min_sleep */ -+ u32 min_sleep; -+ /** max_sleep */ -+ u32 max_sleep; -+} __ATTRIB_PACK__ MrvlIEtypes_sleep_param_t; -+ -+/** MrvlIEtypes_inact_sleep_param_t */ -+typedef struct _MrvlIEtypes_inact_sleep_param_t -+{ -+ /** Header */ -+ MrvlIEtypesHeader_t header; -+ /** inactivity timeout */ -+ u32 inactivity_to; -+ /** min_awake */ -+ u32 min_awake; -+ /** max_awake */ -+ u32 max_awake; -+} __ATTRIB_PACK__ MrvlIEtypes_inact_sleep_param_t; -+ -+/** AP_Event */ -+typedef struct _AP_Event -+{ -+ /** Event ID */ -+ u32 EventId; -+ /* -+ * Reserved for STA_ASSOCIATED event and contains -+ * status information for the MIC_COUNTERMEASURES event. -+ */ -+ /** Reserved/status */ -+ u16 status; -+ /** AP MAC address */ -+ u8 MacAddr[ETH_ALEN]; -+} __ATTRIB_PACK__ AP_Event; -+#endif /* _UAP_FW_H */ -diff -Naur backports-3.18.1-1.org/drivers/net/wireless/libertas_uap/uap_headers.h backports-3.18.1-1/drivers/net/wireless/libertas_uap/uap_headers.h ---- backports-3.18.1-1.org/drivers/net/wireless/libertas_uap/uap_headers.h 1970-01-01 01:00:00.000000000 +0100 -+++ backports-3.18.1-1/drivers/net/wireless/libertas_uap/uap_headers.h 2014-12-29 20:37:43.949097590 +0100 -@@ -0,0 +1,64 @@ -+/** @file uap_headers.h -+ * -+ * @brief This file contains all the necessary include file. -+ * -+ * Copyright (C) 2008-2009, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available along with the File in the gpl.txt file or by writing to -+ * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -+ * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ * -+ */ -+#ifndef _UAP_HEADERS_H -+#define _UAP_HEADERS_H -+ -+/* Linux header files */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) -+#include -+#else -+#include -+#endif -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) -+#include -+#endif -+ -+/* Net header files */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "uap_drv.h" -+#include "uap_fw.h" -+ -+#include -+#include -+#include -+#include -+#include "uap_sdio_mmc.h" -+ -+#endif /* _UAP_HEADERS_H */ -diff -Naur backports-3.18.1-1.org/drivers/net/wireless/libertas_uap/uap_main.c backports-3.18.1-1/drivers/net/wireless/libertas_uap/uap_main.c ---- backports-3.18.1-1.org/drivers/net/wireless/libertas_uap/uap_main.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-3.18.1-1/drivers/net/wireless/libertas_uap/uap_main.c 2014-12-29 20:37:43.952431125 +0100 -@@ -0,0 +1,1817 @@ -+/** @file uap_main.c -+ * @brief This file contains the major functions in uAP -+ * driver. It includes init, exit etc.. -+ * This file also contains the initialization for SW, -+ * FW and HW -+ * -+ * Copyright (C) 2008-2009, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available along with the File in the gpl.txt file or by writing to -+ * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -+ * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ * -+ */ -+/** -+ * @mainpage uAP Linux Driver -+ * -+ * @section overview_sec Overview -+ * -+ * This is Linux reference driver for Marvell uAP. -+ * -+ * @section copyright_sec Copyright -+ * -+ * Copyright (C) 2008, Marvell International Ltd. -+ * -+ */ -+ -+#include "uap_headers.h" -+ -+/** -+ * the global variable of a pointer to uap_private -+ * structure variable -+ */ -+uap_private *uappriv = NULL; -+#ifdef DEBUG_LEVEL1 -+#define DEFAULT_DEBUG_MASK (DBG_MSG | DBG_FATAL | DBG_ERROR) -+u32 drvdbg = DEFAULT_DEBUG_MASK; -+#endif -+/** Helper name */ -+char *helper_name = NULL; -+/** Firmware name */ -+char *fw_name = NULL; -+ -+/** Semaphore for add/remove card */ -+SEMAPHORE AddRemoveCardSem; -+ -+/******************************************************** -+ Local Functions -+********************************************************/ -+/** -+ * @brief This function send sleep confirm command to firmware -+ * -+ * @param priv A pointer to uap_private structure -+ * @return UAP_STATUS_SUCCESS for success otherwise UAP_STATUS_FAILURE -+ */ -+static int -+uap_dnld_sleep_confirm_cmd(uap_private * priv) -+{ -+ uap_adapter *Adapter = priv->adapter; -+ int ret = UAP_STATUS_SUCCESS; -+ ENTER(); -+ PRINTM(CMND, "Sleep confirm\n"); -+ Adapter->cmd_pending = TRUE; -+ Adapter->cmd_wait_option = HostCmd_OPTION_WAITFORRSP_SLEEPCONFIRM; -+ ret = -+ sbi_host_to_card(priv, (u8 *) & Adapter->PSConfirmSleep, -+ sizeof(PS_CMD_ConfirmSleep)); -+ if (ret != UAP_STATUS_SUCCESS) { -+ Adapter->ps_state = PS_STATE_AWAKE; -+ Adapter->cmd_pending = FALSE; -+ Adapter->cmd_wait_option = FALSE; -+ } -+ LEAVE(); -+ return ret; -+} -+ -+/** -+ * @brief This function process sleep confirm resp from firmware -+ * -+ * @param priv A pointer to uap_private structure -+ * @param resp A pointer to resp buf -+ * @param resp_len resp buf len -+ * @return UAP_STATUS_SUCCESS for success otherwise UAP_STATUS_FAILURE -+ */ -+int -+uap_process_sleep_confirm_resp(uap_private * priv, u8 * resp, int resp_len) -+{ -+ int ret = UAP_STATUS_SUCCESS; -+ HostCmd_DS_COMMAND *cmd; -+ uap_adapter *Adapter = priv->adapter; -+ ENTER(); -+ PRINTM(CMND, "Sleep confirm resp\n"); -+ if (!resp_len) { -+ PRINTM(ERROR, "Cmd Size is 0\n"); -+ ret = -EFAULT; -+ goto done; -+ } -+ cmd = (HostCmd_DS_COMMAND *) resp; -+ cmd->Result = uap_le16_to_cpu(cmd->Result); -+ if (cmd->Result != UAP_STATUS_SUCCESS) { -+ PRINTM(ERROR, "HOST_CMD_APCMD_PS_SLEEP_CONFIRM fail=%x\n", cmd->Result); -+ ret = -EFAULT; -+ } -+ done: -+ if (ret == UAP_STATUS_SUCCESS) -+ Adapter->ps_state = PS_STATE_SLEEP; -+ else -+ Adapter->ps_state = PS_STATE_AWAKE; -+ LEAVE(); -+ return ret; -+} -+ -+/** -+ * @brief This function checks condition and prepares to -+ * send sleep confirm command to firmware if OK. -+ * -+ * @param priv A pointer to uap_private structure -+ * @return n/a -+ */ -+static void -+uap_ps_cond_check(uap_private * priv) -+{ -+ uap_adapter *Adapter = priv->adapter; -+ -+ ENTER(); -+ if (!priv->uap_dev.cmd_sent && -+ !Adapter->cmd_pending && !Adapter->IntCounter) { -+ uap_dnld_sleep_confirm_cmd(priv); -+ } else { -+ PRINTM(INFO, "Delay Sleep Confirm (%s%s%s)\n", -+ (priv->uap_dev.cmd_sent) ? "D" : "", -+ (Adapter->cmd_pending) ? "C" : "", -+ (Adapter->IntCounter) ? "I" : ""); -+ } -+ LEAVE(); -+} -+ -+/** -+ * @brief This function add cmd to cmdQ and waiting for response -+ * -+ * @param priv A pointer to uap_private structure -+ * @param skb A pointer to the skb for process -+ * @param wait_option Wait option -+ * @return UAP_STATUS_SUCCESS for success otherwise UAP_STATUS_FAILURE -+ */ -+static int -+uap_process_cmd(uap_private * priv, struct sk_buff *skb, u8 wait_option) -+{ -+ uap_adapter *Adapter = priv->adapter; -+ int ret = UAP_STATUS_SUCCESS; -+ HostCmd_DS_COMMAND *cmd; -+ u8 *headptr; -+ ENTER(); -+ if (Adapter->HardwareStatus != HWReady) { -+ PRINTM(ERROR, "Hw not ready, uap_process_cmd\n"); -+ kfree(skb); -+ LEAVE(); -+ return -EFAULT; -+ } -+ skb->cb[0] = wait_option; -+ headptr = skb->data; -+ *(u16 *) & headptr[0] = uap_cpu_to_le16(skb->len); -+ *(u16 *) & headptr[2] = uap_cpu_to_le16(MV_TYPE_CMD); -+ cmd = (HostCmd_DS_COMMAND *) (skb->data + INTF_HEADER_LEN); -+ Adapter->SeqNum++; -+ cmd->SeqNum = uap_cpu_to_le16(Adapter->SeqNum); -+ PRINTM(CMND, "process_cmd: %x\n", cmd->Command); -+ DBG_HEXDUMP(CMD_D, "process_cmd", (u8 *) cmd, cmd->Size); -+ if (!wait_option) { -+ skb_queue_tail(&priv->adapter->cmd_queue, skb); -+ wake_up_interruptible(&priv->MainThread.waitQ); -+ LEAVE(); -+ return ret; -+ } -+ if (OS_ACQ_SEMAPHORE_BLOCK(&Adapter->CmdSem)) { -+ PRINTM(ERROR, "Acquire semaphore error, uap_prepare_cmd\n"); -+ kfree(skb); -+ LEAVE(); -+ return -EBUSY; -+ } -+ skb_queue_tail(&priv->adapter->cmd_queue, skb); -+ Adapter->CmdWaitQWoken = FALSE; -+ wake_up_interruptible(&priv->MainThread.waitQ); -+ /* Sleep until response is generated by FW */ -+ if (wait_option == HostCmd_OPTION_WAITFORRSP_TIMEOUT) { -+ if (!os_wait_interruptible_timeout -+ (Adapter->cmdwait_q, Adapter->CmdWaitQWoken, MRVDRV_TIMER_20S)) { -+ PRINTM(ERROR, "Cmd timeout\n"); -+ Adapter->cmd_pending = FALSE; -+ ret = -EFAULT; -+ } -+ } else -+ wait_event_interruptible(Adapter->cmdwait_q, Adapter->CmdWaitQWoken); -+ OS_REL_SEMAPHORE(&Adapter->CmdSem); -+ LEAVE(); -+ return ret; -+} -+ -+/** -+ * @brief Inspect the response buffer for pointers to expected TLVs -+ * -+ * -+ * @param pTlv Pointer to the start of the TLV buffer to parse -+ * @param tlvBufSize Size of the TLV buffer -+ * @param reqTlvType request tlv's tlvtype -+ * @param ppTlv Output parameter: Pointer to the request TLV if found -+ * -+ * @return void -+ */ -+static void -+uap_get_tlv_ptrs(MrvlIEtypes_Data_t * pTlv, int tlvBufSize, -+ u16 reqTlvType, MrvlIEtypes_Data_t ** ppTlv) -+{ -+ MrvlIEtypes_Data_t *pCurrentTlv; -+ int tlvBufLeft; -+ u16 tlvType; -+ u16 tlvLen; -+ -+ ENTER(); -+ pCurrentTlv = pTlv; -+ tlvBufLeft = tlvBufSize; -+ *ppTlv = NULL; -+ PRINTM(INFO, "uap_get_tlv: tlvBufSize = %d, reqTlvType=%x\n", tlvBufSize, -+ reqTlvType); -+ while (tlvBufLeft >= sizeof(MrvlIEtypesHeader_t)) { -+ tlvType = uap_le16_to_cpu(pCurrentTlv->Header.Type); -+ tlvLen = uap_le16_to_cpu(pCurrentTlv->Header.Len); -+ if (reqTlvType == tlvType) -+ *ppTlv = (MrvlIEtypes_Data_t *) pCurrentTlv; -+ if (*ppTlv) { -+ HEXDUMP("TLV Buf", (u8 *) * ppTlv, tlvLen); -+ break; -+ } -+ tlvBufLeft -= (sizeof(pTlv->Header) + tlvLen); -+ pCurrentTlv = (MrvlIEtypes_Data_t *) (pCurrentTlv->Data + tlvLen); -+ } /* while */ -+ LEAVE(); -+} -+ -+/** -+ * @brief This function get mac -+ * -+ * @param priv A pointer to uap_private structure -+ * @return UAP_STATUS_SUCCESS on success, otherwise failure code -+ */ -+static int -+uap_get_mac_address(uap_private * priv) -+{ -+ int ret = UAP_STATUS_SUCCESS; -+ u32 CmdSize; -+ HostCmd_DS_COMMAND *cmd; -+ uap_adapter *Adapter = priv->adapter; -+ struct sk_buff *skb; -+ MrvlIEtypes_MacAddr_t *pMacAddrTlv; -+ MrvlIEtypes_Data_t *pTlv; -+ u16 tlvBufSize; -+ ENTER(); -+ skb = dev_alloc_skb(MRVDRV_SIZE_OF_CMD_BUFFER); -+ if (!skb) { -+ PRINTM(ERROR, "No free skb\n"); -+ ret = -ENOMEM; -+ goto done; -+ } -+ CmdSize = -+ S_DS_GEN + sizeof(HostCmd_SYS_CONFIG) + sizeof(MrvlIEtypes_MacAddr_t); -+ cmd = (HostCmd_DS_COMMAND *) (skb->data + INTF_HEADER_LEN); -+ cmd->Command = uap_cpu_to_le16(HOST_CMD_APCMD_SYS_CONFIGURE); -+ cmd->Size = uap_cpu_to_le16(CmdSize); -+ cmd->params.sys_config.Action = uap_cpu_to_le16(ACTION_GET); -+ pMacAddrTlv = -+ (MrvlIEtypes_MacAddr_t *) (skb->data + INTF_HEADER_LEN + S_DS_GEN + -+ sizeof(HostCmd_SYS_CONFIG)); -+ pMacAddrTlv->Header.Type = uap_cpu_to_le16(MRVL_AP_MAC_ADDRESS_TLV_ID); -+ pMacAddrTlv->Header.Len = uap_cpu_to_le16(ETH_ALEN); -+ skb_put(skb, CmdSize + INTF_HEADER_LEN); -+ if (UAP_STATUS_SUCCESS != -+ uap_process_cmd(priv, skb, HostCmd_OPTION_WAITFORRSP_TIMEOUT)) { -+ PRINTM(ERROR, "Fail to process cmd SYS_CONFIGURE Query\n"); -+ ret = -EFAULT; -+ goto done; -+ } -+ if (!Adapter->CmdSize) { -+ PRINTM(ERROR, "Cmd Size is 0\n"); -+ ret = -EFAULT; -+ goto done; -+ } -+ cmd = (HostCmd_DS_COMMAND *) Adapter->CmdBuf; -+ cmd->Result = uap_le16_to_cpu(cmd->Result); -+ if (cmd->Result != UAP_STATUS_SUCCESS) { -+ PRINTM(ERROR, "uap_get_mac_address fail=%x\n", cmd->Result); -+ ret = -EFAULT; -+ goto done; -+ } -+ pTlv = -+ (MrvlIEtypes_Data_t *) (Adapter->CmdBuf + S_DS_GEN + -+ sizeof(HostCmd_SYS_CONFIG)); -+ tlvBufSize = Adapter->CmdSize - S_DS_GEN - sizeof(HostCmd_SYS_CONFIG); -+ uap_get_tlv_ptrs(pTlv, tlvBufSize, MRVL_AP_MAC_ADDRESS_TLV_ID, -+ (MrvlIEtypes_Data_t **) & pMacAddrTlv); -+ if (pMacAddrTlv) { -+ memcpy(priv->uap_dev.netdev->dev_addr, pMacAddrTlv->ApMacAddr, -+ ETH_ALEN); -+ HEXDUMP("Original MAC addr", priv->uap_dev.netdev->dev_addr, ETH_ALEN); -+ } -+ done: -+ LEAVE(); -+ return ret; -+} -+ -+/** -+ * @brief This function checks the conditions and sends packet to device -+ * -+ * @param priv A pointer to uap_private structure -+ * @param skb A pointer to the skb for process -+ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE -+ */ -+static int -+uap_process_tx(uap_private * priv, struct sk_buff *skb) -+{ -+ uap_adapter *Adapter = priv->adapter; -+ int ret = UAP_STATUS_SUCCESS; -+ TxPD *pLocalTxPD; -+ u8 *headptr; -+ struct sk_buff *newskb; -+ int newheadlen; -+ ENTER(); -+ ASSERT(skb); -+ if (!skb) { -+ LEAVE(); -+ return UAP_STATUS_FAILURE; -+ } -+ if (skb_headroom(skb) < (sizeof(TxPD) + INTF_HEADER_LEN + HEADER_ALIGNMENT)) { -+ newheadlen = sizeof(TxPD) + INTF_HEADER_LEN + HEADER_ALIGNMENT; -+ PRINTM(WARN, "Tx: Insufficient skb headroom %d\n", skb_headroom(skb)); -+ /* Insufficient skb headroom - allocate a new skb */ -+ newskb = skb_realloc_headroom(skb, newheadlen); -+ if (unlikely(newskb == NULL)) { -+ PRINTM(ERROR, "Tx: Cannot allocate skb\n"); -+ ret = UAP_STATUS_FAILURE; -+ goto done; -+ } -+ kfree_skb(skb); -+ skb = newskb; -+ PRINTM(INFO, "new skb headroom %d\n", skb_headroom(skb)); -+ } -+ /* headptr should be aligned */ -+ headptr = skb->data - sizeof(TxPD) - INTF_HEADER_LEN; -+ headptr = (u8 *) ((u32) headptr & ~((u32) (HEADER_ALIGNMENT - 1))); -+ -+ pLocalTxPD = (TxPD *) (headptr + INTF_HEADER_LEN); -+ memset(pLocalTxPD, 0, sizeof(TxPD)); -+ pLocalTxPD->BssType = PKT_TYPE_MICROAP; -+ pLocalTxPD->TxPktLength = skb->len; -+ /* offset of actual data */ -+ pLocalTxPD->TxPktOffset = (long) skb->data - (long) pLocalTxPD; -+ endian_convert_TxPD(pLocalTxPD); -+ *(u16 *) & headptr[0] = -+ uap_cpu_to_le16(skb->len + ((long) skb->data - (long) headptr)); -+ *(u16 *) & headptr[2] = uap_cpu_to_le16(MV_TYPE_DAT); -+ ret = -+ sbi_host_to_card(priv, headptr, -+ skb->len + ((long) skb->data - (long) headptr)); -+ if (ret) { -+ PRINTM(ERROR, "uap_process_tx Error: sbi_host_to_card failed: 0x%X\n", -+ ret); -+ Adapter->dbg.num_tx_host_to_card_failure++; -+ goto done; -+ } -+ PRINTM(DATA, "Data => FW\n"); -+ DBG_HEXDUMP(DAT_D, "Tx", headptr, -+ MIN(skb->len + sizeof(TxPD), DATA_DUMP_LEN)); -+ done: -+ /* Freed skb */ -+ kfree_skb(skb); -+ LEAVE(); -+ return ret; -+} -+ -+static struct netlink_kernel_cfg cfg = { -+ .groups = NL_MULTICAST_GROUP, -+}; -+ -+/** -+ * @brief This function initializes the adapter structure -+ * and set default value to the member of adapter. -+ * -+ * @param priv A pointer to uap_private structure -+ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE -+ */ -+static int -+uap_init_sw(uap_private * priv) -+{ -+ uap_adapter *Adapter = priv->adapter; -+ -+ ENTER(); -+ -+ if (!(Adapter->CmdBuf = kmalloc(MRVDRV_SIZE_OF_CMD_BUFFER, GFP_KERNEL))) { -+ PRINTM(INFO, "Failed to allocate command buffer!\n"); -+ LEAVE(); -+ return UAP_STATUS_FAILURE; -+ } -+ -+ Adapter->cmd_pending = FALSE; -+ Adapter->CmdWaitQWoken = FALSE; -+ Adapter->ps_state = PS_STATE_AWAKE; -+ Adapter->WakeupTries = 0; -+ -+ memset(&Adapter->PSConfirmSleep, 0, sizeof(PS_CMD_ConfirmSleep)); -+ /** SDIO header */ -+ Adapter->PSConfirmSleep.SDLen = -+ uap_cpu_to_le16(sizeof(PS_CMD_ConfirmSleep)); -+ Adapter->PSConfirmSleep.SDType = uap_cpu_to_le16(MV_TYPE_CMD); -+ Adapter->PSConfirmSleep.SeqNum = 0; -+ Adapter->PSConfirmSleep.Command = uap_cpu_to_le16(HOST_CMD_SLEEP_CONFIRM); -+ Adapter->PSConfirmSleep.Size = uap_cpu_to_le16(sizeof(HostCmd_DS_GEN)); -+ Adapter->PSConfirmSleep.Result = 0; -+ -+ init_waitqueue_head(&Adapter->cmdwait_q); -+ OS_INIT_SEMAPHORE(&Adapter->CmdSem); -+ -+ skb_queue_head_init(&Adapter->tx_queue); -+ skb_queue_head_init(&Adapter->cmd_queue); -+ -+ /* Status variable */ -+ Adapter->HardwareStatus = HWInitializing; -+ -+ /* PnP support */ -+ Adapter->SurpriseRemoved = FALSE; -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) -+ Adapter->nl_sk = netlink_kernel_create(NETLINK_MARVELL, -+ NL_MULTICAST_GROUP, NULL, -+ THIS_MODULE); -+#else -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) -+ Adapter->nl_sk = netlink_kernel_create(NETLINK_MARVELL, -+ NL_MULTICAST_GROUP, NULL, NULL, -+ THIS_MODULE); -+#else -+ Adapter->nl_sk = netlink_kernel_create(&init_net, NETLINK_MARVELL, &cfg); -+#endif -+#endif -+ if (!Adapter->nl_sk) { -+ PRINTM(ERROR, -+ "Could not initialize netlink event passing mechanism!\n"); -+ } -+ LEAVE(); -+ return UAP_STATUS_SUCCESS; -+} -+ -+/** -+ * @brief This function sends FUNC_INIT command to firmware -+ * -+ * @param priv A pointer to uap_private structure -+ * @return UAP_STATUS_SUCCESS on success, otherwise failure code -+ */ -+static int -+uap_func_init(uap_private * priv) -+{ -+ int ret = UAP_STATUS_SUCCESS; -+ u32 CmdSize; -+ HostCmd_DS_GEN *cmd; -+ uap_adapter *Adapter = priv->adapter; -+ struct sk_buff *skb; -+ ENTER(); -+ if (Adapter->HardwareStatus != HWReady) { -+ PRINTM(ERROR, "uap_func_init:Hardware is not ready!\n"); -+ ret = -EFAULT; -+ goto done; -+ } -+ skb = dev_alloc_skb(MRVDRV_SIZE_OF_CMD_BUFFER); -+ if (!skb) { -+ PRINTM(ERROR, "No free skb\n"); -+ ret = -ENOMEM; -+ goto done; -+ } -+ CmdSize = sizeof(HostCmd_DS_GEN); -+ cmd = (HostCmd_DS_GEN *) (skb->data + INTF_HEADER_LEN); -+ cmd->Command = uap_cpu_to_le16(HostCmd_CMD_FUNC_INIT); -+ cmd->Size = uap_cpu_to_le16(CmdSize); -+ skb_put(skb, CmdSize + INTF_HEADER_LEN); -+ PRINTM(CMND, "HostCmd_CMD_FUNC_INIT\n"); -+ if (UAP_STATUS_SUCCESS != -+ uap_process_cmd(priv, skb, HostCmd_OPTION_WAITFORRSP_TIMEOUT)) { -+ PRINTM(ERROR, "Fail to process cmd HostCmd_CMD_FUNC_INIT\n"); -+ ret = -EFAULT; -+ goto done; -+ } -+ done: -+ LEAVE(); -+ return ret; -+} -+ -+/** -+ * @brief This function sends FUNC_SHUTDOWN command to firmware -+ * -+ * @param priv A pointer to uap_private structure -+ * @return UAP_STATUS_SUCCESS on success, otherwise failure code -+ */ -+static int __exit -+uap_func_shutdown(uap_private * priv) -+{ -+ int ret = UAP_STATUS_SUCCESS; -+ u32 CmdSize; -+ HostCmd_DS_GEN *cmd; -+ uap_adapter *Adapter = priv->adapter; -+ struct sk_buff *skb; -+ ENTER(); -+ if (Adapter->HardwareStatus != HWReady) { -+ PRINTM(ERROR, "uap_func_shutdown:Hardware is not ready!\n"); -+ ret = -EFAULT; -+ goto done; -+ } -+ skb = dev_alloc_skb(MRVDRV_SIZE_OF_CMD_BUFFER); -+ if (!skb) { -+ PRINTM(ERROR, "No free skb\n"); -+ ret = -ENOMEM; -+ goto done; -+ } -+ CmdSize = sizeof(HostCmd_DS_GEN); -+ cmd = (HostCmd_DS_GEN *) (skb->data + INTF_HEADER_LEN); -+ cmd->Command = uap_cpu_to_le16(HostCmd_CMD_FUNC_SHUTDOWN); -+ cmd->Size = uap_cpu_to_le16(CmdSize); -+ skb_put(skb, CmdSize + INTF_HEADER_LEN); -+ PRINTM(CMND, "HostCmd_CMD_FUNC_SHUTDOWN\n"); -+ if (UAP_STATUS_SUCCESS != -+ uap_process_cmd(priv, skb, HostCmd_OPTION_WAITFORRSP_TIMEOUT)) { -+ PRINTM(ERROR, "Fail to process cmd HostCmd_CMD_FUNC_SHUTDOWN\n"); -+ ret = -EFAULT; -+ goto done; -+ } -+ done: -+ LEAVE(); -+ return ret; -+} -+ -+/** -+ * @brief This function initializes firmware -+ * -+ * @param priv A pointer to uap_private structure -+ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE -+ */ -+static int -+uap_init_fw(uap_private * priv) -+{ -+ int ret = UAP_STATUS_SUCCESS; -+ ENTER(); -+ sbi_disable_host_int(priv); -+ /* Check if firmware is already running */ -+ if (sbi_check_fw_status(priv, 1) == UAP_STATUS_SUCCESS) { -+ PRINTM(MSG, "UAP FW already running! Skip FW download\n"); -+ } else { -+ if ((ret = request_firmware(&priv->fw_helper, helper_name, -+ priv->hotplug_device)) < 0) { -+ PRINTM(FATAL, -+ "request_firmware() failed (helper), error code = %#x\n", -+ ret); -+ goto done; -+ } -+ -+ /* Download the helper */ -+ ret = sbi_prog_helper(priv); -+ -+ if (ret) { -+ PRINTM(FATAL, -+ "Bootloader in invalid state! Helper download failed!\n"); -+ ret = UAP_STATUS_FAILURE; -+ goto done; -+ } -+ if ((ret = request_firmware(&priv->firmware, fw_name, -+ priv->hotplug_device)) < 0) { -+ PRINTM(FATAL, "request_firmware() failed, error code = %#x\n", ret); -+ goto done; -+ } -+ -+ /* Download the main firmware via the helper firmware */ -+ if (sbi_prog_fw_w_helper(priv)) { -+ PRINTM(FATAL, "UAP FW download failed!\n"); -+ ret = UAP_STATUS_FAILURE; -+ goto done; -+ } -+ /* Check if the firmware is downloaded successfully or not */ -+ if (sbi_check_fw_status(priv, MAX_FIRMWARE_POLL_TRIES) == -+ UAP_STATUS_FAILURE) { -+ PRINTM(FATAL, "FW failed to be active in time!\n"); -+ ret = UAP_STATUS_FAILURE; -+ goto done; -+ } -+ PRINTM(MSG, "UAP FW is active\n"); -+ } -+ sbi_enable_host_int(priv); -+ priv->adapter->HardwareStatus = HWReady; -+ if (uap_func_init(priv) != UAP_STATUS_SUCCESS) { -+ ret = UAP_STATUS_FAILURE; -+ goto done; -+ } -+ done: -+ if (priv->fw_helper) -+ release_firmware(priv->fw_helper); -+ if (priv->firmware) -+ release_firmware(priv->firmware); -+ LEAVE(); -+ return ret; -+ -+} -+ -+/** -+ * @brief This function frees the structure of adapter -+ * -+ * @param priv A pointer to uap_private structure -+ * @return n/a -+ */ -+static void -+uap_free_adapter(uap_private * priv) -+{ -+ uap_adapter *Adapter = priv->adapter; -+ -+ ENTER(); -+ -+ if (Adapter) { -+ if ((Adapter->nl_sk) && ((Adapter->nl_sk)->sk_socket)) { -+ sock_release((Adapter->nl_sk)->sk_socket); -+ Adapter->nl_sk = NULL; -+ } -+ if (Adapter->CmdBuf) -+ kfree(Adapter->CmdBuf); -+ skb_queue_purge(&priv->adapter->tx_queue); -+ skb_queue_purge(&priv->adapter->cmd_queue); -+ /* Free the adapter object itself */ -+ kfree(Adapter); -+ priv->adapter = NULL; -+ } -+ -+ LEAVE(); -+} -+ -+/** -+ * @brief This function handles the major job in uap driver. -+ * it handles the event generated by firmware, rx data received -+ * from firmware and tx data sent from kernel. -+ * -+ * @param data A pointer to uap_thread structure -+ * @return BT_STATUS_SUCCESS -+ */ -+static int -+uap_service_main_thread(void *data) -+{ -+ uap_thread *thread = data; -+ uap_private *priv = thread->priv; -+ uap_adapter *Adapter = priv->adapter; -+ wait_queue_t wait; -+ u8 ireg = 0; -+ struct sk_buff *skb; -+ ENTER(); -+ uap_activate_thread(thread); -+ init_waitqueue_entry(&wait, current); -+ current->flags |= PF_NOFREEZE; -+ -+ for (;;) { -+ add_wait_queue(&thread->waitQ, &wait); -+ OS_SET_THREAD_STATE(TASK_INTERRUPTIBLE); -+ if ((Adapter->WakeupTries) || -+ (!Adapter->IntCounter && Adapter->ps_state == PS_STATE_PRE_SLEEP) || -+ (!priv->adapter->IntCounter -+ && (priv->uap_dev.data_sent || -+ skb_queue_empty(&priv->adapter->tx_queue)) -+ && (priv->uap_dev.cmd_sent || Adapter->cmd_pending || -+ skb_queue_empty(&priv->adapter->cmd_queue)) -+ )) { -+ PRINTM(INFO, "Main: Thread sleeping...\n"); -+ schedule(); -+ } -+ OS_SET_THREAD_STATE(TASK_RUNNING); -+ remove_wait_queue(&thread->waitQ, &wait); -+ if (kthread_should_stop() || Adapter->SurpriseRemoved) { -+ PRINTM(INFO, "main-thread: break from main thread: " -+ "SurpriseRemoved=0x%x\n", Adapter->SurpriseRemoved); -+ /* Cancel pending command */ -+ if (Adapter->cmd_pending == TRUE) { -+ /* Wake up cmd Q */ -+ Adapter->CmdWaitQWoken = TRUE; -+ wake_up_interruptible(&Adapter->cmdwait_q); -+ } -+ break; -+ } -+ -+ PRINTM(INFO, "Main: Thread waking up...\n"); -+ if (priv->adapter->IntCounter) { -+ OS_INT_DISABLE; -+ Adapter->IntCounter = 0; -+ OS_INT_RESTORE; -+ sbi_get_int_status(priv, &ireg); -+ } else if ((priv->adapter->ps_state == PS_STATE_SLEEP) && -+ (!skb_queue_empty(&priv->adapter->cmd_queue) || -+ !skb_queue_empty(&priv->adapter->tx_queue))) { -+ priv->adapter->WakeupTries++; -+ PRINTM(CMND, "%lu : Wakeup device...\n", os_time_get()); -+ sbi_wakeup_firmware(priv); -+ continue; -+ } -+ if (Adapter->ps_state == PS_STATE_PRE_SLEEP) -+ uap_ps_cond_check(priv); -+ -+ /* The PS state is changed during processing of Sleep Request event -+ above */ -+ if ((Adapter->ps_state == PS_STATE_SLEEP) || -+ (Adapter->ps_state == PS_STATE_PRE_SLEEP)) -+ continue; -+ /* Execute the next command */ -+ if (!priv->uap_dev.cmd_sent && !Adapter->cmd_pending && -+ (Adapter->HardwareStatus == HWReady)) { -+ if (!skb_queue_empty(&priv->adapter->cmd_queue)) { -+ skb = skb_dequeue(&priv->adapter->cmd_queue); -+ if (skb) { -+ Adapter->CmdSize = 0; -+ Adapter->cmd_pending = TRUE; -+ Adapter->cmd_wait_option = skb->cb[0]; -+ if (sbi_host_to_card(priv, skb->data, skb->len)) { -+ PRINTM(ERROR, "Cmd:sbi_host_to_card failed!\n"); -+ Adapter->cmd_pending = FALSE; -+ Adapter->dbg.num_cmd_host_to_card_failure++; -+ /* Wake up cmd Q */ -+ Adapter->CmdWaitQWoken = TRUE; -+ wake_up_interruptible(&Adapter->cmdwait_q); -+ } else { -+ if (Adapter->cmd_wait_option == -+ HostCmd_OPTION_WAITFORSEND) { -+ /* Wake up cmd Q */ -+ Adapter->CmdWaitQWoken = TRUE; -+ wake_up_interruptible(&Adapter->cmdwait_q); -+ Adapter->cmd_wait_option = FALSE; -+ } -+ } -+ kfree_skb(skb); -+ } -+ } -+ } -+ if (!priv->uap_dev.data_sent && (Adapter->HardwareStatus == HWReady)) { -+ if (!skb_queue_empty(&priv->adapter->tx_queue)) { -+ skb = skb_dequeue(&priv->adapter->tx_queue); -+ if (skb) { -+ if (uap_process_tx(priv, skb)) { -+ priv->stats.tx_dropped++; -+ priv->stats.tx_errors++; -+ os_start_queue(priv); -+ } else { -+ priv->stats.tx_packets++; -+ priv->stats.tx_bytes += skb->len; -+ } -+ -+ } -+ } -+ } -+ } -+ uap_deactivate_thread(thread); -+ LEAVE(); -+ return UAP_STATUS_SUCCESS; -+} -+ -+/** -+ * @brief uap hostcmd ioctl handler -+ * -+ * @param dev A pointer to net_device structure -+ * @param req A pointer to ifreq structure -+ * @return UAP_STATUS_SUCCESS --success, otherwise fail -+ */ -+/********* format of ifr_data *************/ -+/* buf_len + Hostcmd_body */ -+/* buf_len: 4 bytes */ -+/* the length of the buf which */ -+/* can be used to return data */ -+/* to application */ -+/* Hostcmd_body */ -+/*******************************************/ -+static int -+uap_hostcmd_ioctl(struct net_device *dev, struct ifreq *req) -+{ -+ u32 buf_len; -+ HostCmd_HEADER head; -+ uap_private *priv = (uap_private *) netdev_priv(dev); -+ uap_adapter *Adapter = priv->adapter; -+ int ret = UAP_STATUS_SUCCESS; -+ struct sk_buff *skb; -+ -+ ENTER(); -+ -+ /* Sanity check */ -+ if (req->ifr_data == NULL) { -+ PRINTM(ERROR, "uap_hostcmd_ioctl() corrupt data\n"); -+ LEAVE(); -+ return -EFAULT; -+ } -+ if (copy_from_user(&buf_len, req->ifr_data, sizeof(buf_len))) { -+ PRINTM(ERROR, "Copy from user failed\n"); -+ LEAVE(); -+ return -EFAULT; -+ } -+ memset(&head, 0, sizeof(HostCmd_HEADER)); -+ /* Get the command size from user space */ -+ if (copy_from_user -+ (&head, req->ifr_data + sizeof(buf_len), sizeof(HostCmd_HEADER))) { -+ PRINTM(ERROR, "Copy from user failed\n"); -+ LEAVE(); -+ return -EFAULT; -+ } -+ head.Size = uap_le16_to_cpu(head.Size); -+ if (head.Size > MRVDRV_SIZE_OF_CMD_BUFFER) { -+ PRINTM(ERROR, "CmdSize too big=%d\n", head.Size); -+ LEAVE(); -+ return -EFAULT; -+ } -+ PRINTM(CMND, "ioctl: hostcmd=%x, size=%d,buf_len=%d\n", head.Command, -+ head.Size, buf_len); -+ skb = dev_alloc_skb(head.Size + INTF_HEADER_LEN); -+ if (!skb) { -+ PRINTM(ERROR, "No free skb\n"); -+ LEAVE(); -+ return -ENOMEM; -+ } -+ -+ /* Get the command from user space */ -+ if (copy_from_user -+ (skb->data + INTF_HEADER_LEN, req->ifr_data + sizeof(buf_len), -+ head.Size)) { -+ PRINTM(ERROR, "Copy from user failed\n"); -+ LEAVE(); -+ return -EFAULT; -+ } -+ skb_put(skb, head.Size + INTF_HEADER_LEN); -+ if (UAP_STATUS_SUCCESS != -+ uap_process_cmd(priv, skb, HostCmd_OPTION_WAITFORRSP)) { -+ PRINTM(ERROR, "Fail to process cmd\n"); -+ LEAVE(); -+ return -EFAULT; -+ } -+ if (!Adapter->CmdSize) { -+ PRINTM(ERROR, "Cmd Size is 0\n"); -+ LEAVE(); -+ return -EFAULT; -+ } -+ if (Adapter->CmdSize > buf_len) { -+ PRINTM(ERROR, "buf_len is too small\n"); -+ LEAVE(); -+ return -EFAULT; -+ } -+ /* Copy to user */ -+ if (copy_to_user -+ (req->ifr_data + sizeof(buf_len), Adapter->CmdBuf, Adapter->CmdSize)) { -+ PRINTM(ERROR, "Copy to user failed!\n"); -+ LEAVE(); -+ return -EFAULT; -+ } -+ LEAVE(); -+ return ret; -+} -+ -+/** -+ * @brief uap power mode ioctl handler -+ * -+ * @param dev A pointer to net_device structure -+ * @param req A pointer to ifreq structure -+ * @return UAP_STATUS_SUCCESS --success, otherwise fail -+ */ -+static int -+uap_power_mode_ioctl(struct net_device *dev, struct ifreq *req) -+{ -+ ps_mgmt pm_cfg; -+ int ret = UAP_STATUS_SUCCESS; -+ uap_private *priv = (uap_private *) netdev_priv(dev); -+ uap_adapter *Adapter = priv->adapter; -+ struct sk_buff *skb = NULL; -+ HostCmd_DS_COMMAND *cmd; -+ u32 CmdSize; -+ u8 *tlv = NULL; -+ MrvlIEtypes_sleep_param_t *sleep_tlv = NULL; -+ MrvlIEtypes_inact_sleep_param_t *inact_tlv = NULL; -+ u16 tlv_buf_left = 0; -+ MrvlIEtypesHeader_t *tlvbuf = NULL; -+ u16 tlv_type = 0; -+ u16 tlv_len = 0; -+ -+ ENTER(); -+ -+ /* Sanity check */ -+ if (req->ifr_data == NULL) { -+ PRINTM(ERROR, "uap_power_mode_ioctl() corrupt data\n"); -+ LEAVE(); -+ return -EFAULT; -+ } -+ -+ memset(&pm_cfg, 0, sizeof(ps_mgmt)); -+ if (copy_from_user(&pm_cfg, req->ifr_data, sizeof(ps_mgmt))) { -+ PRINTM(ERROR, "Copy from user failed\n"); -+ LEAVE(); -+ return -EFAULT; -+ } -+ PRINTM(CMND, -+ "ioctl power: flag=0x%x ps_mode=%d ctrl_bitmap=%d min_sleep=%d max_sleep=%d " -+ "inact_to=%d min_awake=%d max_awake=%d\n", pm_cfg.flags, -+ (int) pm_cfg.ps_mode, (int) pm_cfg.sleep_param.ctrl_bitmap, -+ (int) pm_cfg.sleep_param.min_sleep, -+ (int) pm_cfg.sleep_param.max_sleep, -+ (int) pm_cfg.inact_param.inactivity_to, -+ (int) pm_cfg.inact_param.min_awake, -+ (int) pm_cfg.inact_param.max_awake); -+ -+ if (pm_cfg. -+ flags & ~(PS_FLAG_PS_MODE | PS_FLAG_SLEEP_PARAM | -+ PS_FLAG_INACT_SLEEP_PARAM)) { -+ PRINTM(ERROR, "Invalid parameter: flags = 0x%x\n", pm_cfg.flags); -+ ret = -EINVAL; -+ goto done; -+ } -+ if (pm_cfg.ps_mode > PS_MODE_INACTIVITY) { -+ PRINTM(ERROR, "Invalid parameter: ps_mode = %d\n", (int) pm_cfg.flags); -+ ret = -EINVAL; -+ goto done; -+ } -+ -+ skb = dev_alloc_skb(MRVDRV_SIZE_OF_CMD_BUFFER); -+ if (!skb) { -+ PRINTM(INFO, "No free skb\n"); -+ ret = -ENOMEM; -+ goto done; -+ } -+ -+ CmdSize = S_DS_GEN + sizeof(HostCmd_DS_POWER_MGMT_EXT); -+ -+ cmd = (HostCmd_DS_COMMAND *) (skb->data + INTF_HEADER_LEN); -+ cmd->Command = uap_cpu_to_le16(HOST_CMD_POWER_MGMT_EXT); -+ if (!pm_cfg.flags) { -+ cmd->params.pm_cfg.action = uap_cpu_to_le16(ACTION_GET); -+ } else { -+ cmd->params.pm_cfg.action = uap_cpu_to_le16(ACTION_SET); -+ cmd->params.pm_cfg.power_mode = uap_cpu_to_le16(pm_cfg.ps_mode); -+ tlv = (u8 *) & cmd->params.pm_cfg + sizeof(HostCmd_DS_POWER_MGMT_EXT); -+ -+ if ((pm_cfg.ps_mode) && (pm_cfg.flags & PS_FLAG_SLEEP_PARAM)) { -+ sleep_tlv = (MrvlIEtypes_sleep_param_t *) tlv; -+ sleep_tlv->header.Type = uap_cpu_to_le16(TLV_TYPE_AP_SLEEP_PARAM); -+ sleep_tlv->header.Len = -+ uap_cpu_to_le16(sizeof(MrvlIEtypes_sleep_param_t) - -+ sizeof(MrvlIEtypesHeader_t)); -+ sleep_tlv->ctrl_bitmap = -+ uap_cpu_to_le32(pm_cfg.sleep_param.ctrl_bitmap); -+ sleep_tlv->min_sleep = -+ uap_cpu_to_le32(pm_cfg.sleep_param.min_sleep); -+ sleep_tlv->max_sleep = -+ uap_cpu_to_le32(pm_cfg.sleep_param.max_sleep); -+ CmdSize += sizeof(MrvlIEtypes_sleep_param_t); -+ tlv += sizeof(MrvlIEtypes_sleep_param_t); -+ } -+ if ((pm_cfg.ps_mode == PS_MODE_INACTIVITY) && -+ (pm_cfg.flags & PS_FLAG_INACT_SLEEP_PARAM)) { -+ inact_tlv = (MrvlIEtypes_inact_sleep_param_t *) tlv; -+ inact_tlv->header.Type = -+ uap_cpu_to_le16(TLV_TYPE_AP_INACT_SLEEP_PARAM); -+ inact_tlv->header.Len = -+ uap_cpu_to_le16(sizeof(MrvlIEtypes_inact_sleep_param_t) - -+ sizeof(MrvlIEtypesHeader_t)); -+ inact_tlv->inactivity_to = -+ uap_cpu_to_le32(pm_cfg.inact_param.inactivity_to); -+ inact_tlv->min_awake = -+ uap_cpu_to_le32(pm_cfg.inact_param.min_awake); -+ inact_tlv->max_awake = -+ uap_cpu_to_le32(pm_cfg.inact_param.max_awake); -+ CmdSize += sizeof(MrvlIEtypes_inact_sleep_param_t); -+ tlv += sizeof(MrvlIEtypes_inact_sleep_param_t); -+ } -+ } -+ cmd->Size = uap_cpu_to_le16(CmdSize); -+ skb_put(skb, CmdSize + INTF_HEADER_LEN); -+ if (UAP_STATUS_SUCCESS != -+ uap_process_cmd(priv, skb, HostCmd_OPTION_WAITFORRSP)) { -+ PRINTM(ERROR, "Fail to process cmd POWER_MODE\n"); -+ ret = -EFAULT; -+ goto done; -+ } -+ if (!Adapter->CmdSize) { -+ PRINTM(ERROR, "Cmd Size is 0\n"); -+ ret = -EFAULT; -+ goto done; -+ } -+ cmd = (HostCmd_DS_COMMAND *) Adapter->CmdBuf; -+ cmd->Result = uap_le16_to_cpu(cmd->Result); -+ if (cmd->Result != UAP_STATUS_SUCCESS) { -+ PRINTM(ERROR, "HOST_CMD_APCMD_POWER_MODE fail=%x\n", cmd->Result); -+ ret = -EFAULT; -+ goto done; -+ } -+ if (pm_cfg.flags) { -+ Adapter->psmode = uap_le16_to_cpu(cmd->params.pm_cfg.power_mode); -+ } else { -+ pm_cfg.flags = PS_FLAG_PS_MODE; -+ pm_cfg.ps_mode = uap_le16_to_cpu(cmd->params.pm_cfg.power_mode); -+ tlv_buf_left = -+ cmd->Size - (sizeof(HostCmd_DS_POWER_MGMT_EXT) + S_DS_GEN); -+ tlvbuf = -+ (MrvlIEtypesHeader_t *) ((u8 *) & cmd->params.pm_cfg + -+ sizeof(HostCmd_DS_POWER_MGMT_EXT)); -+ while (tlv_buf_left >= sizeof(MrvlIEtypesHeader_t)) { -+ tlv_type = uap_le16_to_cpu(tlvbuf->Type); -+ tlv_len = uap_le16_to_cpu(tlvbuf->Len); -+ switch (tlv_type) { -+ case TLV_TYPE_AP_SLEEP_PARAM: -+ sleep_tlv = (MrvlIEtypes_sleep_param_t *) tlvbuf; -+ pm_cfg.flags |= PS_FLAG_SLEEP_PARAM; -+ pm_cfg.sleep_param.ctrl_bitmap = -+ uap_le32_to_cpu(sleep_tlv->ctrl_bitmap); -+ pm_cfg.sleep_param.min_sleep = -+ uap_le32_to_cpu(sleep_tlv->min_sleep); -+ pm_cfg.sleep_param.max_sleep = -+ uap_le32_to_cpu(sleep_tlv->max_sleep); -+ break; -+ case TLV_TYPE_AP_INACT_SLEEP_PARAM: -+ inact_tlv = (MrvlIEtypes_inact_sleep_param_t *) tlvbuf; -+ pm_cfg.flags |= PS_FLAG_INACT_SLEEP_PARAM; -+ pm_cfg.inact_param.inactivity_to = -+ uap_le32_to_cpu(inact_tlv->inactivity_to); -+ pm_cfg.inact_param.min_awake = -+ uap_le32_to_cpu(inact_tlv->min_awake); -+ pm_cfg.inact_param.max_awake = -+ uap_le32_to_cpu(inact_tlv->max_awake); -+ break; -+ } -+ tlv_buf_left -= tlv_len + sizeof(MrvlIEtypesHeader_t); -+ tlvbuf = -+ (MrvlIEtypesHeader_t *) ((u8 *) tlvbuf + tlv_len + -+ sizeof(MrvlIEtypesHeader_t)); -+ } -+ /* Copy to user */ -+ if (copy_to_user(req->ifr_data, &pm_cfg, sizeof(ps_mgmt))) { -+ PRINTM(ERROR, "Copy to user failed!\n"); -+ LEAVE(); -+ return -EFAULT; -+ } -+ } -+ done: -+ LEAVE(); -+ return ret; -+} -+ -+/** -+ * @brief This function send bss_stop command to firmware -+ * -+ * @param priv A pointer to uap_private structure -+ * @return UAP_STATUS_SUCCESS on success, otherwise failure code -+ */ -+static int -+uap_bss_stop(uap_private * priv) -+{ -+ int ret = UAP_STATUS_SUCCESS; -+ u32 CmdSize; -+ HostCmd_DS_GEN *cmd; -+ uap_adapter *Adapter = priv->adapter; -+ struct sk_buff *skb; -+ ENTER(); -+ if (Adapter->HardwareStatus != HWReady) { -+ PRINTM(ERROR, "uap_bss_stop:Hardware is not ready!\n"); -+ ret = -EFAULT; -+ goto done; -+ } -+ skb = dev_alloc_skb(MRVDRV_SIZE_OF_CMD_BUFFER); -+ if (!skb) { -+ PRINTM(ERROR, "No free skb\n"); -+ ret = -ENOMEM; -+ goto done; -+ } -+ CmdSize = sizeof(HostCmd_DS_GEN); -+ cmd = (HostCmd_DS_GEN *) (skb->data + INTF_HEADER_LEN); -+ cmd->Command = uap_cpu_to_le16(HOST_CMD_APCMD_BSS_STOP); -+ cmd->Size = uap_cpu_to_le16(CmdSize); -+ skb_put(skb, CmdSize + INTF_HEADER_LEN); -+ PRINTM(CMND, "APCMD_BSS_STOP\n"); -+ if (UAP_STATUS_SUCCESS != -+ uap_process_cmd(priv, skb, HostCmd_OPTION_WAITFORRSP_TIMEOUT)) { -+ PRINTM(ERROR, "Fail to process cmd BSS_STOP\n"); -+ ret = -EFAULT; -+ goto done; -+ } -+ done: -+ LEAVE(); -+ return ret; -+} -+ -+/******************************************************** -+ Global Functions -+********************************************************/ -+/** -+ * @brief This function send soft_reset command to firmware -+ * -+ * @param priv A pointer to uap_private structure -+ * @return UAP_STATUS_SUCCESS on success, otherwise failure code -+ */ -+int -+uap_soft_reset(uap_private * priv) -+{ -+ int ret = UAP_STATUS_SUCCESS; -+ u32 CmdSize; -+ HostCmd_DS_GEN *cmd; -+ uap_adapter *Adapter = priv->adapter; -+ struct sk_buff *skb; -+ ENTER(); -+ ret = uap_bss_stop(priv); -+ if (ret != UAP_STATUS_SUCCESS) -+ goto done; -+ skb = dev_alloc_skb(MRVDRV_SIZE_OF_CMD_BUFFER); -+ if (!skb) { -+ PRINTM(ERROR, "No free skb\n"); -+ ret = -ENOMEM; -+ goto done; -+ } -+ CmdSize = sizeof(HostCmd_DS_GEN); -+ cmd = (HostCmd_DS_GEN *) (skb->data + INTF_HEADER_LEN); -+ cmd->Command = uap_cpu_to_le16(HOST_CMD_APCMD_SOFT_RESET); -+ cmd->Size = uap_cpu_to_le16(CmdSize); -+ skb_put(skb, CmdSize + INTF_HEADER_LEN); -+ PRINTM(CMND, "APCMD_SOFT_RESET\n"); -+ if (UAP_STATUS_SUCCESS != -+ uap_process_cmd(priv, skb, HostCmd_OPTION_WAITFORSEND)) { -+ PRINTM(ERROR, "Fail to process cmd SOFT_RESET\n"); -+ ret = -EFAULT; -+ goto done; -+ } -+ Adapter->SurpriseRemoved = TRUE; -+ /* delay to allow hardware complete reset */ -+ os_sched_timeout(5); -+ if (priv->MediaConnected == TRUE) { -+ os_stop_queue(priv); -+ os_carrier_off(priv); -+ priv->MediaConnected = FALSE; -+ } -+ Adapter->CmdSize = 0; -+ Adapter->CmdWaitQWoken = TRUE; -+ wake_up_interruptible(&Adapter->cmdwait_q); -+ skb_queue_purge(&priv->adapter->tx_queue); -+ skb_queue_purge(&priv->adapter->cmd_queue); -+ done: -+ LEAVE(); -+ return ret; -+} -+ -+/** -+ * @brief This function processes received packet and forwards it -+ * to kernel/upper layer -+ * -+ * @param priv A pointer to uap_private -+ * @param skb A pointer to skb which includes the received packet -+ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE -+ */ -+int -+uap_process_rx_packet(uap_private * priv, struct sk_buff *skb) -+{ -+ int ret = UAP_STATUS_SUCCESS; -+ RxPD *pRxPD; -+ ENTER(); -+ priv->adapter->ps_state = PS_STATE_AWAKE; -+ pRxPD = (RxPD *) skb->data; -+ endian_convert_RxPD(pRxPD); -+ DBG_HEXDUMP(DAT_D, "Rx", skb->data, MIN(skb->len, DATA_DUMP_LEN)); -+ skb_pull(skb, pRxPD->RxPktOffset); -+ priv->stats.rx_packets++; -+ priv->stats.rx_bytes += skb->len; -+ os_upload_rx_packet(priv, skb); -+ LEAVE(); -+ return ret; -+} -+ -+/** -+ * @brief This function opens the network device -+ * -+ * @param dev A pointer to net_device structure -+ * @return UAP_STATUS_SUCCESS -+ */ -+static int -+uap_open(struct net_device *dev) -+{ -+ uap_private *priv = (uap_private *) (uap_private *) netdev_priv(dev); -+ uap_adapter *Adapter = priv->adapter; -+ int i = 0; -+ -+ ENTER(); -+ -+ /* On some systems the device open handler will be called before HW ready. */ -+ /* Use the following flag check and wait function to work around the issue. */ -+ while ((Adapter->HardwareStatus != HWReady) && -+ (i < MAX_WAIT_DEVICE_READY_COUNT)) { -+ i++; -+ os_sched_timeout(100); -+ } -+ if (i >= MAX_WAIT_DEVICE_READY_COUNT) { -+ PRINTM(FATAL, "HW not ready, uap_open() return failure\n"); -+ LEAVE(); -+ return UAP_STATUS_FAILURE; -+ } -+ -+ if (MODULE_GET == 0) -+ return UAP_STATUS_FAILURE; -+ -+ priv->open = TRUE; -+ if (priv->MediaConnected == TRUE) { -+ os_carrier_on(priv); -+ os_start_queue(priv); -+ } else { -+ os_stop_queue(priv); -+ os_carrier_off(priv); -+ } -+ LEAVE(); -+ return UAP_STATUS_SUCCESS; -+} -+ -+/** -+ * @brief This function closes the network device -+ * -+ * @param dev A pointer to net_device structure -+ * @return UAP_STATUS_SUCCESS -+ */ -+static int -+uap_close(struct net_device *dev) -+{ -+ uap_private *priv = (uap_private *) netdev_priv(dev); -+ -+ ENTER(); -+ skb_queue_purge(&priv->adapter->tx_queue); -+ os_stop_queue(priv); -+ os_carrier_off(priv); -+ -+ MODULE_PUT; -+ priv->open = FALSE; -+ LEAVE(); -+ return UAP_STATUS_SUCCESS; -+} -+ -+/** -+ * @brief This function returns the network statistics -+ * -+ * @param dev A pointer to uap_private structure -+ * @return A pointer to net_device_stats structure -+ */ -+static struct net_device_stats * -+uap_get_stats(struct net_device *dev) -+{ -+ uap_private *priv = (uap_private *) netdev_priv(dev); -+ -+ return &priv->stats; -+} -+ -+/** -+ * @brief This function sets the MAC address to firmware. -+ * -+ * @param dev A pointer to uap_private structure -+ * @param addr MAC address to set -+ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE -+ */ -+static int -+uap_set_mac_address(struct net_device *dev, void *addr) -+{ -+ int ret = UAP_STATUS_SUCCESS; -+ uap_private *priv = (uap_private *) netdev_priv(dev); -+ struct sockaddr *pHwAddr = (struct sockaddr *) addr; -+ u32 CmdSize; -+ HostCmd_DS_COMMAND *cmd; -+ MrvlIEtypes_MacAddr_t *pMacAddrTlv; -+ uap_adapter *Adapter = priv->adapter; -+ struct sk_buff *skb; -+ -+ ENTER(); -+ -+ /* Dump MAC address */ -+ DBG_HEXDUMP(CMD_D, "Original MAC addr", dev->dev_addr, ETH_ALEN); -+ DBG_HEXDUMP(CMD_D, "New MAC addr", pHwAddr->sa_data, ETH_ALEN); -+ if (priv->open && (priv->MediaConnected == TRUE)) { -+ os_carrier_on(priv); -+ os_start_queue(priv); -+ } -+ skb = dev_alloc_skb(MRVDRV_SIZE_OF_CMD_BUFFER); -+ if (!skb) { -+ PRINTM(ERROR, "No free skb\n"); -+ LEAVE(); -+ return -ENOMEM; -+ } -+ CmdSize = -+ S_DS_GEN + sizeof(HostCmd_SYS_CONFIG) + sizeof(MrvlIEtypes_MacAddr_t); -+ cmd = (HostCmd_DS_COMMAND *) (skb->data + INTF_HEADER_LEN); -+ cmd->Command = uap_cpu_to_le16(HOST_CMD_APCMD_SYS_CONFIGURE); -+ cmd->Size = uap_cpu_to_le16(CmdSize); -+ cmd->params.sys_config.Action = uap_cpu_to_le16(ACTION_SET); -+ pMacAddrTlv = -+ (MrvlIEtypes_MacAddr_t *) ((u8 *) cmd + S_DS_GEN + -+ sizeof(HostCmd_SYS_CONFIG)); -+ pMacAddrTlv->Header.Type = uap_cpu_to_le16(MRVL_AP_MAC_ADDRESS_TLV_ID); -+ pMacAddrTlv->Header.Len = uap_cpu_to_le16(ETH_ALEN); -+ memcpy(pMacAddrTlv->ApMacAddr, pHwAddr->sa_data, ETH_ALEN); -+ skb_put(skb, CmdSize + INTF_HEADER_LEN); -+ PRINTM(CMND, "set_mac_address\n"); -+ if (UAP_STATUS_SUCCESS != -+ uap_process_cmd(priv, skb, HostCmd_OPTION_WAITFORRSP_TIMEOUT)) { -+ PRINTM(ERROR, "Fail to set mac address\n"); -+ LEAVE(); -+ return -EFAULT; -+ } -+ if (!Adapter->CmdSize) { -+ PRINTM(ERROR, "Cmd Size is 0\n"); -+ LEAVE(); -+ return -EFAULT; -+ } -+ cmd = (HostCmd_DS_COMMAND *) Adapter->CmdBuf; -+ cmd->Result = uap_cpu_to_le16(cmd->Result); -+ if (cmd->Result != UAP_STATUS_SUCCESS) { -+ PRINTM(ERROR, "set mac addrress fail,cmd result=%x\n", cmd->Result); -+ ret = -EFAULT; -+ } else -+ memcpy(dev->dev_addr, pHwAddr->sa_data, ETH_ALEN); -+ LEAVE(); -+ return ret; -+} -+ -+/** -+ * @brief This function handles the timeout of packet -+ * transmission -+ * -+ * @param dev A pointer to net_device structure -+ * @return n/a -+ */ -+static void -+uap_tx_timeout(struct net_device *dev) -+{ -+ uap_private *priv = (uap_private *) netdev_priv(dev); -+ -+ ENTER(); -+ -+ PRINTM(DATA, "Tx timeout\n"); -+ UpdateTransStart(dev); -+ priv->num_tx_timeout++; -+ priv->adapter->IntCounter++; -+ wake_up_interruptible(&priv->MainThread.waitQ); -+ -+ LEAVE(); -+} -+ -+/** -+ * @brief This function handles packet transmission -+ * -+ * @param skb A pointer to sk_buff structure -+ * @param dev A pointer to net_device structure -+ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE -+ */ -+static int -+uap_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) -+{ -+ uap_private *priv = (uap_private *) netdev_priv(dev); -+ int ret = UAP_STATUS_SUCCESS; -+ -+ ENTER(); -+ PRINTM(DATA, "Data <= kernel\n"); -+ DBG_HEXDUMP(DAT_D, "Tx", skb->data, MIN(skb->len, DATA_DUMP_LEN)); -+ /* skb sanity check */ -+ if (!skb->len || (skb->len > MRVDRV_MAXIMUM_ETH_PACKET_SIZE)) { -+ PRINTM(ERROR, "Tx Error: Bad skb length %d : %d\n", skb->len, -+ MRVDRV_MAXIMUM_ETH_PACKET_SIZE); -+ priv->stats.tx_dropped++; -+ kfree(skb); -+ goto done; -+ } -+ skb_queue_tail(&priv->adapter->tx_queue, skb); -+ wake_up_interruptible(&priv->MainThread.waitQ); -+ if (skb_queue_len(&priv->adapter->tx_queue) > TX_HIGH_WATERMARK) { -+ UpdateTransStart(dev); -+ os_stop_queue(priv); -+ } -+ done: -+ LEAVE(); -+ return ret; -+} -+ -+/** -+ * @brief ioctl function - entry point -+ * -+ * @param dev A pointer to net_device structure -+ * @param req A pointer to ifreq structure -+ * @param cmd command -+ * @return UAP_STATUS_SUCCESS--success, otherwise fail -+ */ -+static int -+uap_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd) -+{ -+ int ret = UAP_STATUS_SUCCESS; -+ -+ ENTER(); -+ -+ PRINTM(CMND, "uap_do_ioctl: ioctl cmd = 0x%x\n", cmd); -+ -+ switch (cmd) { -+ case UAPHOSTCMD: -+ ret = uap_hostcmd_ioctl(dev, req); -+ break; -+ case UAP_POWER_MODE: -+ ret = uap_power_mode_ioctl(dev, req); -+ break; -+ default: -+ ret = -EINVAL; -+ break; -+ } -+ -+ LEAVE(); -+ return ret; -+} -+ -+/** -+ * @brief This function handles events generated by firmware -+ * -+ * @param priv A pointer to uap_private structure -+ * @param payload A pointer to payload buffer -+ * @param len Length of the payload -+ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE -+ */ -+int -+uap_process_event(uap_private * priv, u8 * payload, uint len) -+{ -+ int ret = UAP_STATUS_SUCCESS; -+ uap_adapter *Adapter = priv->adapter; -+ struct sk_buff *skb = NULL; -+ struct nlmsghdr *nlh = NULL; -+ struct sock *sk = Adapter->nl_sk; -+ AP_Event *pEvent; -+ -+ ENTER(); -+ Adapter->ps_state = PS_STATE_AWAKE; -+ if (len > NL_MAX_PAYLOAD) { -+ PRINTM(ERROR, "event size is too big!!! len=%d\n", len); -+ ret = UAP_STATUS_FAILURE; -+ goto done; -+ } -+ pEvent = (AP_Event *) payload; -+ PRINTM(CMND, "Event: %d\n", pEvent->EventId); -+ switch (pEvent->EventId) { -+ case MICRO_AP_EV_ID_BSS_START: -+ memcpy(priv->uap_dev.netdev->dev_addr, pEvent->MacAddr, ETH_ALEN); -+ DBG_HEXDUMP(CMD_D, "BSS MAC addr", priv->uap_dev.netdev->dev_addr, -+ ETH_ALEN); -+ break; -+ case MICRO_AP_EV_BSS_ACTIVE: -+ // carrier on -+ priv->MediaConnected = TRUE; -+ os_carrier_on(priv); -+ os_start_queue(priv); -+ break; -+ case MICRO_AP_EV_BSS_IDLE: -+ os_stop_queue(priv); -+ os_carrier_off(priv); -+ priv->MediaConnected = FALSE; -+ break; -+ case EVENT_PS_AWAKE: -+ PRINTM(CMND, "UAP: PS_AWAKE\n"); -+ Adapter->ps_state = PS_STATE_AWAKE; -+ Adapter->WakeupTries = 0; -+ break; -+ case EVENT_PS_SLEEP: -+ PRINTM(CMND, "UAP: PS_SLEEP\n"); -+ Adapter->ps_state = PS_STATE_PRE_SLEEP; -+ break; -+ default: -+ break; -+ } -+ if ((pEvent->EventId == EVENT_PS_AWAKE) || -+ (pEvent->EventId == EVENT_PS_SLEEP)) -+ goto done; -+ if (sk) { -+ /* Allocate skb */ -+ if (!(skb = alloc_skb(NLMSG_SPACE(NL_MAX_PAYLOAD), GFP_ATOMIC))) { -+ PRINTM(ERROR, "Could not allocate skb for netlink.\n"); -+ ret = UAP_STATUS_FAILURE; -+ goto done; -+ } -+ nlh = (struct nlmsghdr *) skb->data; -+ nlh->nlmsg_len = NLMSG_SPACE(len); -+ -+ /* From kernel */ -+ nlh->nlmsg_pid = 0; -+ nlh->nlmsg_flags = 0; -+ -+ /* Data */ -+ skb_put(skb, nlh->nlmsg_len); -+ memcpy(NLMSG_DATA(nlh), payload, len); -+ -+ /* From Kernel */ -+ NETLINK_CB(skb).portid = 0; -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) -+ /* Multicast message */ -+ NETLINK_CB(skb).dst_pid = 0; -+#endif -+ -+ /* Multicast group number */ -+ NETLINK_CB(skb).dst_group = NL_MULTICAST_GROUP; -+ -+ /* Send message */ -+ netlink_broadcast(sk, skb, 0, NL_MULTICAST_GROUP, GFP_KERNEL); -+ -+ ret = UAP_STATUS_SUCCESS; -+ } else { -+ PRINTM(ERROR, "Could not send event through NETLINK. Link down.\n"); -+ ret = UAP_STATUS_FAILURE; -+ } -+ done: -+ LEAVE(); -+ return ret; -+} -+ -+/** -+ * @brief This function handles the interrupt. it will change PS -+ * state if applicable. it will wake up main_thread to handle -+ * the interrupt event as well. -+ * -+ * @param priv A pointer to uap_private structure -+ * @return n/a -+ */ -+void -+uap_interrupt(uap_private * priv) -+{ -+ ENTER(); -+ priv->adapter->IntCounter++; -+ priv->adapter->WakeupTries = 0; -+ PRINTM(INFO, "*\n"); -+ wake_up_interruptible(&priv->MainThread.waitQ); -+ -+ LEAVE(); -+ -+} -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29) -+/** Network device handlers */ -+static const struct net_device_ops uap_netdev_ops = { -+ .ndo_open = uap_open, -+ .ndo_start_xmit = uap_hard_start_xmit, -+ .ndo_stop = uap_close, -+ .ndo_do_ioctl = uap_do_ioctl, -+ .ndo_set_mac_address = uap_set_mac_address, -+ .ndo_tx_timeout = uap_tx_timeout, -+ .ndo_get_stats = uap_get_stats, -+}; -+#endif -+ -+/** -+ * @brief This function adds the card. it will probe the -+ * card, allocate the uap_priv and initialize the device. -+ * -+ * @param card A pointer to card -+ * @return A pointer to uap_private structure -+ */ -+uap_private * -+uap_add_card(void *card) -+{ -+ struct net_device *dev = NULL; -+ uap_private *priv = NULL; -+ -+ ENTER(); -+ -+ if (OS_ACQ_SEMAPHORE_BLOCK(&AddRemoveCardSem)) -+ goto exit_sem_err; -+ -+ /* Allocate an Ethernet device */ -+ if (!(dev = alloc_etherdev(sizeof(uap_private)))) { -+ PRINTM(FATAL, "Init ethernet device failed!\n"); -+ goto error; -+ } -+ priv = (uap_private *) netdev_priv(dev); -+ -+ /* Allocate name */ -+ if (dev_alloc_name(dev, "uap%d") < 0) { -+ PRINTM(ERROR, "Could not allocate device name!\n"); -+ goto error; -+ } -+ -+ /* Allocate buffer for uap_adapter */ -+ if (!(priv->adapter = kmalloc(sizeof(uap_adapter), GFP_KERNEL))) { -+ PRINTM(FATAL, "Allocate buffer for uap_adapter failed!\n"); -+ goto error; -+ } -+ memset(priv->adapter, 0, sizeof(uap_adapter)); -+ -+ priv->uap_dev.netdev = dev; -+ priv->uap_dev.card = card; -+ priv->MediaConnected = FALSE; -+ uappriv = priv; -+ ((struct sdio_mmc_card *) card)->priv = priv; -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) -+ SET_MODULE_OWNER(dev); -+#endif -+ -+ /* Setup the OS Interface to our functions */ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29) -+ dev->open = uap_open; -+ dev->stop = uap_close; -+ dev->hard_start_xmit = uap_hard_start_xmit; -+ dev->tx_timeout = uap_tx_timeout; -+ dev->get_stats = uap_get_stats; -+ dev->do_ioctl = uap_do_ioctl; -+ dev->set_mac_address = uap_set_mac_address; -+ dev->set_multicast_list = uap_set_multicast_list; -+#else -+ dev->netdev_ops = &uap_netdev_ops; -+#endif -+ dev->watchdog_timeo = MRVDRV_DEFAULT_WATCHDOG_TIMEOUT; -+ dev->hard_header_len += sizeof(TxPD) + INTF_HEADER_LEN; -+ dev->hard_header_len += HEADER_ALIGNMENT; -+#define NETIF_F_DYNALLOC 16 -+ dev->features |= NETIF_F_DYNALLOC; -+ dev->flags |= IFF_BROADCAST | IFF_MULTICAST; -+ -+ /* Init SW */ -+ if (uap_init_sw(priv)) { -+ PRINTM(FATAL, "Software Init Failed\n"); -+ goto error; -+ } -+ -+ PRINTM(INFO, "Starting kthread...\n"); -+ priv->MainThread.priv = priv; -+ spin_lock_init(&priv->driver_lock); -+ uap_create_thread(uap_service_main_thread, &priv->MainThread, -+ "uap_main_service"); -+ while (priv->MainThread.pid == 0) { -+ os_sched_timeout(2); -+ } -+ -+ /* Register the device */ -+ if (sbi_register_dev(priv) < 0) { -+ PRINTM(FATAL, "Failed to register uap device!\n"); -+ goto err_registerdev; -+ } -+#ifdef FW_DNLD_NEEDED -+ SET_NETDEV_DEV(dev, priv->hotplug_device); -+#endif -+ -+ /* Init FW and HW */ -+ if (uap_init_fw(priv)) { -+ PRINTM(FATAL, "Firmware Init Failed\n"); -+ goto err_init_fw; -+ } -+ -+ priv->uap_dev.cmd_sent = FALSE; -+ priv->uap_dev.data_sent = FALSE; -+ -+ /* Get mac address from firmware */ -+ if (uap_get_mac_address(priv)) { -+ PRINTM(FATAL, "Fail to get mac address\n"); -+ goto err_init_fw; -+ } -+ /* Register network device */ -+ if (register_netdev(dev)) { -+ printk(KERN_ERR "Cannot register network device!\n"); -+ goto err_init_fw; -+ } -+#ifdef CONFIG_PROC_FS -+ uap_proc_entry(priv, dev); -+ uap_debug_entry(priv, dev); -+#endif /* CPNFIG_PROC_FS */ -+ OS_REL_SEMAPHORE(&AddRemoveCardSem); -+ -+ LEAVE(); -+ return priv; -+ err_init_fw: -+ sbi_unregister_dev(priv); -+ err_registerdev: -+ ((struct sdio_mmc_card *) card)->priv = NULL; -+ /* Stop the thread servicing the interrupts */ -+ priv->adapter->SurpriseRemoved = TRUE; -+ wake_up_interruptible(&priv->MainThread.waitQ); -+ while (priv->MainThread.pid) { -+ os_sched_timeout(1); -+ } -+ error: -+ if (dev) { -+ if (dev->reg_state == NETREG_REGISTERED) -+ unregister_netdev(dev); -+ if (priv->adapter) -+ uap_free_adapter(priv); -+ free_netdev(dev); -+ uappriv = NULL; -+ } -+ OS_REL_SEMAPHORE(&AddRemoveCardSem); -+ exit_sem_err: -+ LEAVE(); -+ return NULL; -+} -+ -+/** -+ * @brief This function removes the card. -+ * -+ * @param card A pointer to card -+ * @return UAP_STATUS_SUCCESS -+ */ -+int -+uap_remove_card(void *card) -+{ -+ uap_private *priv = uappriv; -+ uap_adapter *Adapter; -+ struct net_device *dev; -+ -+ ENTER(); -+ -+ if (OS_ACQ_SEMAPHORE_BLOCK(&AddRemoveCardSem)) -+ goto exit_sem_err; -+ -+ if (!priv || !(Adapter = priv->adapter)) { -+ goto exit_remove; -+ } -+ Adapter->SurpriseRemoved = TRUE; -+ if (Adapter->cmd_pending == TRUE) { -+ /* Wake up cmd Q */ -+ Adapter->CmdWaitQWoken = TRUE; -+ wake_up_interruptible(&Adapter->cmdwait_q); -+ } -+ dev = priv->uap_dev.netdev; -+ if (priv->MediaConnected == TRUE) { -+ os_stop_queue(priv); -+ os_carrier_off(priv); -+ priv->MediaConnected = FALSE; -+ } -+ Adapter->CmdSize = 0; -+ Adapter->CmdWaitQWoken = TRUE; -+ wake_up_interruptible(&Adapter->cmdwait_q); -+ skb_queue_purge(&priv->adapter->tx_queue); -+ skb_queue_purge(&priv->adapter->cmd_queue); -+ -+ /* Disable interrupts on the card */ -+ sbi_disable_host_int(priv); -+ PRINTM(INFO, "netdev_finish_unregister: %s%s.\n", dev->name, -+ (dev->features & NETIF_F_DYNALLOC) ? "" : ", old style"); -+ unregister_netdev(dev); -+ PRINTM(INFO, "Unregister finish\n"); -+ wake_up_interruptible(&priv->MainThread.waitQ); -+ while (priv->MainThread.pid) { -+ os_sched_timeout(1); -+ } -+ -+ if ((Adapter->nl_sk) && ((Adapter->nl_sk)->sk_socket)) { -+ sock_release((Adapter->nl_sk)->sk_socket); -+ Adapter->nl_sk = NULL; -+ } -+#ifdef CONFIG_PROC_FS -+ uap_debug_remove(priv); -+ uap_proc_remove(priv); -+#endif -+ sbi_unregister_dev(priv); -+ PRINTM(INFO, "Free Adapter\n"); -+ uap_free_adapter(priv); -+ priv->uap_dev.netdev = NULL; -+ free_netdev(dev); -+ uappriv = NULL; -+ -+ exit_remove: -+ OS_REL_SEMAPHORE(&AddRemoveCardSem); -+ exit_sem_err: -+ LEAVE(); -+ return UAP_STATUS_SUCCESS; -+} -+ -+/** -+ * @brief This function initializes module. -+ * -+ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE -+ */ -+static int __init -+uap_init_module(void) -+{ -+ int ret = UAP_STATUS_SUCCESS; -+ ENTER(); -+ -+ OS_INIT_SEMAPHORE(&AddRemoveCardSem); -+ ret = sbi_register(); -+ LEAVE(); -+ return ret; -+} -+ -+/** -+ * @brief This function cleans module -+ * -+ * @return n/a -+ */ -+static void __exit -+uap_cleanup_module(void) -+{ -+ ENTER(); -+ -+ if (OS_ACQ_SEMAPHORE_BLOCK(&AddRemoveCardSem)) -+ goto exit_sem_err; -+ -+ if ((uappriv) && (uappriv->adapter)) { -+ uap_func_shutdown(uappriv); -+ } -+ OS_REL_SEMAPHORE(&AddRemoveCardSem); -+ exit_sem_err: -+ sbi_unregister(); -+ LEAVE(); -+} -+ -+module_init(uap_init_module); -+module_exit(uap_cleanup_module); -+module_param(helper_name, charp, 0); -+MODULE_PARM_DESC(helper_name, "Helper name"); -+module_param(fw_name, charp, 0); -+MODULE_PARM_DESC(fw_name, "Firmware name"); -+ -+MODULE_DESCRIPTION("M-UAP Driver"); -+MODULE_AUTHOR("Marvell International Ltd."); -+MODULE_VERSION(DRIVER_VERSION); -+MODULE_LICENSE("GPL"); -diff -Naur backports-3.18.1-1.org/drivers/net/wireless/libertas_uap/uap_proc.c backports-3.18.1-1/drivers/net/wireless/libertas_uap/uap_proc.c ---- backports-3.18.1-1.org/drivers/net/wireless/libertas_uap/uap_proc.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-3.18.1-1/drivers/net/wireless/libertas_uap/uap_proc.c 2014-12-29 20:37:43.952431125 +0100 -@@ -0,0 +1,258 @@ -+/** @file uap_proc.c -+ * @brief This file contains functions for proc file. -+ * -+ * Copyright (C) 2008-2009, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available along with the File in the gpl.txt file or by writing to -+ * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -+ * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ * -+ */ -+#ifdef CONFIG_PROC_FS -+#include -+#include -+#include "uap_headers.h" -+ -+/** /proc directory root */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) -+#define PROC_DIR NULL -+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) -+#define PROC_DIR &proc_root -+#else -+#define PROC_DIR proc_net -+#endif -+ -+/******************************************************** -+ Local Variables -+********************************************************/ -+ -+/******************************************************** -+ Global Variables -+********************************************************/ -+ -+/******************************************************** -+ Local Functions -+********************************************************/ -+ -+static int uap_info_proc_show(struct seq_file *s, void *data) { -+ int i; -+ struct net_device *netdev = (struct net_device*)s->private; -+ struct netdev_hw_addr *ha; -+ uap_private *priv = (uap_private *) netdev_priv(netdev); -+ -+ seq_printf(s, "driver_name = " "\"uap\"\n"); -+ seq_printf(s, "driver_version = %s-(FP%s)", DRIVER_VERSION, FPNUM); -+ seq_printf(s, "\nInterfaceName=\"%s\"\n", netdev->name); -+ -+ seq_printf(s, "State=\"%s\"\n", -+ ((priv->MediaConnected == -+ FALSE) ? "Disconnected" : "Connected")); -+ seq_printf(s, "MACAddress=\"%02x:%02x:%02x:%02x:%02x:%02x\"\n", -+ netdev->dev_addr[0], netdev->dev_addr[1], netdev->dev_addr[2], -+ netdev->dev_addr[3], netdev->dev_addr[4], netdev->dev_addr[5]); -+ i = 0; -+ netdev_for_each_mc_addr(ha, netdev) { -+ ++i; -+ } -+ seq_printf(s, "MCCount=\"%d\"\n", i); -+ -+ /* -+ * Put out the multicast list -+ */ -+ i = 0; -+ netdev_for_each_mc_addr(ha, netdev) { -+ seq_printf(s, -+ "MCAddr[%d]=\"%02x:%02x:%02x:%02x:%02x:%02x\"\n", -+ i++, -+ ha->addr[0], ha->addr[1], -+ ha->addr[2], ha->addr[3], -+ ha->addr[4], ha->addr[5]); -+ } -+ -+ seq_printf(s, "num_tx_bytes = %lu\n", priv->stats.tx_bytes); -+ seq_printf(s, "num_rx_bytes = %lu\n", priv->stats.rx_bytes); -+ seq_printf(s, "num_tx_pkts = %lu\n", priv->stats.tx_packets); -+ seq_printf(s, "num_rx_pkts = %lu\n", priv->stats.rx_packets); -+ seq_printf(s, "num_tx_pkts_dropped = %lu\n", priv->stats.tx_dropped); -+ seq_printf(s, "num_rx_pkts_dropped = %lu\n", priv->stats.rx_dropped); -+ seq_printf(s, "num_tx_pkts_err = %lu\n", priv->stats.tx_errors); -+ seq_printf(s, "num_rx_pkts_err = %lu\n", priv->stats.rx_errors); -+ seq_printf(s, "num_tx_timeout = %u\n", priv->num_tx_timeout); -+ seq_printf(s, "carrier %s\n", -+ ((netif_carrier_ok(priv->uap_dev.netdev)) ? "on" : "off")); -+ seq_printf(s, "tx queue %s\n", -+ ((netif_queue_stopped(priv->uap_dev.netdev)) ? "stopped" : -+ "started")); -+ -+ return 0; -+} -+ -+static int uap_info_proc_open(struct inode *inode, struct file *file) { -+ return single_open(file, uap_info_proc_show, PDE_DATA(inode)); -+} -+ -+static int uap_hwstatus_proc_show(struct seq_file *s, void *data) { -+ struct net_device *netdev = (struct net_device*)s->private; -+ uap_private *priv = (uap_private *) netdev_priv(netdev); -+ -+ MODULE_GET; -+ seq_printf(s, "%d\n", priv->adapter->HardwareStatus); -+ MODULE_PUT; -+ -+ return 0; -+} -+ -+static int uap_hwstatus_proc_open(struct inode *inode, struct file *file) { -+ return single_open(file, uap_hwstatus_proc_show, PDE_DATA(inode)); -+} -+ -+static ssize_t uap_hwstatus_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *data) { -+ struct net_device *netdev = (struct net_device *)PDE_DATA(file_inode(file)); -+ uap_private *priv = (uap_private *) netdev_priv(netdev); -+ -+ int hwstatus; -+ char value[10]; -+ -+ if (count > sizeof(value)) -+ return count; -+ -+ if (copy_from_user(&value, buffer, count)) -+ return -EFAULT; -+ -+ hwstatus = string_to_number(value); -+ switch (hwstatus) { -+ case HWReset: -+ PRINTM(MSG, "reset hw\n"); -+ uap_soft_reset(priv); -+ priv->adapter->HardwareStatus = HWReset; -+ break; -+ default: -+ break; -+ } -+ -+ MODULE_PUT; -+ return count; -+} -+ -+static const struct file_operations uap_info_proc_fops = { -+ .owner = THIS_MODULE, -+ .open = uap_info_proc_open, -+ .read = seq_read, -+ .llseek = seq_lseek, -+ .release = single_release, -+}; -+ -+static const struct file_operations uap_hwstatus_proc_fops = { -+ .owner = THIS_MODULE, -+ .open = uap_hwstatus_proc_open, -+ .read = seq_read, -+ .llseek = seq_lseek, -+ .release = single_release, -+ .write = uap_hwstatus_proc_write, -+}; -+ -+/******************************************************** -+ Global Functions -+********************************************************/ -+/** -+ * @brief create uap proc file -+ * -+ * @param priv pointer uap_private -+ * @param dev pointer net_device -+ * @return N/A -+ */ -+void -+uap_proc_entry(uap_private * priv, struct net_device *dev) -+{ -+ PRINTM(INFO, "Creating Proc Interface\n"); -+ /* Check if uap directory already exists */ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) -+ struct proc_dir_entry *r = PROC_DIR; -+ -+ for (r = r->subdir; r; r = r->next) { -+ if (r->namelen && !strcmp("uap", r->name)) { -+ /* Directory exists */ -+ PRINTM(WARN, "proc directory already exists!\n"); -+ priv->proc_uap = r; -+ break; -+ } -+ } -+#endif -+ if (!priv->proc_uap) { -+ priv->proc_uap = proc_mkdir("uap", PROC_DIR); -+ if (!priv->proc_uap) -+ return; -+ } -+ priv->proc_entry = proc_mkdir(dev->name, priv->proc_uap); -+ -+ if (priv->proc_entry) { -+ proc_create_data("info", 0644, priv->proc_entry, &uap_info_proc_fops, dev); -+ proc_create_data("hwinfo", 0644, priv->proc_entry, &uap_hwstatus_proc_fops, dev); -+ } -+} -+ -+/** -+ * @brief remove proc file -+ * -+ * @param priv pointer uap_private -+ * @return N/A -+ */ -+void -+uap_proc_remove(uap_private * priv) -+{ -+ if (priv->proc_uap) { -+ if (priv->proc_entry) { -+ remove_proc_entry("info", priv->proc_entry); -+ remove_proc_entry("hwstatus", priv->proc_entry); -+ } -+ remove_proc_entry(priv->uap_dev.netdev->name, priv->proc_uap); -+ } -+} -+ -+/** -+ * @brief convert string to number -+ * -+ * @param s pointer to numbered string -+ * @return converted number from string s -+ */ -+int -+string_to_number(char *s) -+{ -+ int r = 0; -+ int base = 0; -+ int pn = 1; -+ -+ if (strncmp(s, "-", 1) == 0) { -+ pn = -1; -+ s++; -+ } -+ if ((strncmp(s, "0x", 2) == 0) || (strncmp(s, "0X", 2) == 0)) { -+ base = 16; -+ s += 2; -+ } else -+ base = 10; -+ -+ for (s = s; *s != 0; s++) { -+ if ((*s >= '0') && (*s <= '9')) -+ r = (r * base) + (*s - '0'); -+ else if ((*s >= 'A') && (*s <= 'F')) -+ r = (r * base) + (*s - 'A' + 10); -+ else if ((*s >= 'a') && (*s <= 'f')) -+ r = (r * base) + (*s - 'a' + 10); -+ else -+ break; -+ } -+ -+ return (r * pn); -+} -+ -+#endif -diff -Naur backports-3.18.1-1.org/drivers/net/wireless/libertas_uap/uap_sdio_mmc.c backports-3.18.1-1/drivers/net/wireless/libertas_uap/uap_sdio_mmc.c ---- backports-3.18.1-1.org/drivers/net/wireless/libertas_uap/uap_sdio_mmc.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-3.18.1-1/drivers/net/wireless/libertas_uap/uap_sdio_mmc.c 2014-12-29 20:37:43.955764567 +0100 -@@ -0,0 +1,1428 @@ -+/** @file uap_sdio_mmc.c -+ * @brief This file contains SDIO IF (interface) module -+ * related functions. -+ * -+ * Copyright (C) 2007-2009, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available along with the File in the gpl.txt file or by writing to -+ * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -+ * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ * -+ */ -+/**************************************************** -+Change log: -+****************************************************/ -+ -+#include "uap_sdio_mmc.h" -+ -+#include -+ -+/** define SDIO block size */ -+/* We support up to 480-byte block size due to FW buffer limitation. */ -+#define SD_BLOCK_SIZE 256 -+ -+/** define allocated buffer size */ -+#define ALLOC_BUF_SIZE (((MAX(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, \ -+ MRVDRV_SIZE_OF_CMD_BUFFER) + INTF_HEADER_LEN \ -+ + SD_BLOCK_SIZE - 1) / SD_BLOCK_SIZE) * SD_BLOCK_SIZE) -+ -+/** Max retry number of CMD53 write */ -+#define MAX_WRITE_IOMEM_RETRY 2 -+ -+/******************************************************** -+ Local Variables -+********************************************************/ -+ -+/** SDIO Rx unit */ -+static u8 sdio_rx_unit = 0; -+ -+/**Interrupt status */ -+static u8 sd_ireg = 0; -+/******************************************************** -+ Global Variables -+********************************************************/ -+extern u8 *helper_name; -+extern u8 *fw_name; -+/** Default helper name */ -+#define DEFAULT_HELPER_NAME "mrvl/helper_sd.bin" -+/** Default firmware name */ -+#define DEFAULT_FW_NAME "mrvl/sd8688_ap.bin" -+ -+/******************************************************** -+ Local Functions -+********************************************************/ -+/** -+ * @brief This function reads the IO register. -+ * -+ * @param priv A pointer to uap_private structure -+ * @param reg register to be read -+ * @param dat A pointer to variable that keeps returned value -+ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE -+ */ -+static int -+sbi_read_ioreg(uap_private * priv, u32 reg, u8 * dat) -+{ -+ struct sdio_mmc_card *card; -+ int ret = UAP_STATUS_FAILURE; -+ -+ ENTER(); -+ -+ card = priv->uap_dev.card; -+ if (!card || !card->func) { -+ PRINTM(ERROR, "sbi_read_ioreg(): card or function is NULL!\n"); -+ goto done; -+ } -+ -+ *dat = sdio_readb(card->func, reg, &ret); -+ if (ret) { -+ PRINTM(ERROR, "sbi_read_ioreg(): sdio_readb failed! ret=%d\n", ret); -+ goto done; -+ } -+ -+ PRINTM(INFO, "sbi_read_ioreg() priv=%p func=%d reg=%#x dat=%#x\n", priv, -+ card->func->num, reg, *dat); -+ -+ done: -+ LEAVE(); -+ return ret; -+} -+ -+/** -+ * @brief This function writes the IO register. -+ * -+ * @param priv A pointer to uap_private structure -+ * @param reg register to be written -+ * @param dat the value to be written -+ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE -+ */ -+static int -+sbi_write_ioreg(uap_private * priv, u32 reg, u8 dat) -+{ -+ struct sdio_mmc_card *card; -+ int ret = UAP_STATUS_FAILURE; -+ -+ ENTER(); -+ -+ card = priv->uap_dev.card; -+ if (!card || !card->func) { -+ PRINTM(ERROR, "sbi_write_ioreg(): card or function is NULL!\n"); -+ goto done; -+ } -+ -+ PRINTM(INFO, "sbi_write_ioreg() priv=%p func=%d reg=%#x dat=%#x\n", priv, -+ card->func->num, reg, dat); -+ -+ sdio_writeb(card->func, dat, reg, &ret); -+ if (ret) { -+ PRINTM(ERROR, "sbi_write_ioreg(): sdio_readb failed! ret=%d\n", ret); -+ goto done; -+ } -+ -+ done: -+ LEAVE(); -+ return ret; -+} -+ -+/** -+ * @brief This function get rx_unit value -+ * -+ * @param priv A pointer to uap_private structure -+ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE -+ */ -+static int -+sd_get_rx_unit(uap_private * priv) -+{ -+ int ret = UAP_STATUS_SUCCESS; -+ u8 reg; -+ -+ ENTER(); -+ -+ ret = sbi_read_ioreg(priv, CARD_RX_UNIT_REG, ®); -+ if (ret == UAP_STATUS_SUCCESS) -+ sdio_rx_unit = reg; -+ -+ LEAVE(); -+ return ret; -+} -+ -+/** -+ * @brief This function reads rx length -+ * -+ * @param priv A pointer to uap_private structure -+ * @param dat A pointer to keep returned data -+ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE -+ */ -+static int -+sd_read_rx_len(uap_private * priv, u16 * dat) -+{ -+ int ret = UAP_STATUS_SUCCESS; -+ u8 reg; -+ -+ ENTER(); -+ -+ ret = sbi_read_ioreg(priv, CARD_RX_LEN_REG, ®); -+ if (ret == UAP_STATUS_SUCCESS) -+ *dat = (u16) reg << sdio_rx_unit; -+ -+ LEAVE(); -+ return ret; -+} -+ -+/** -+ * @brief This function reads fw status registers -+ * -+ * @param priv A pointer to uap_private structure -+ * @param dat A pointer to keep returned data -+ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE -+ */ -+static int -+sd_read_firmware_status(uap_private * priv, u16 * dat) -+{ -+ int ret = UAP_STATUS_SUCCESS; -+ u8 fws0; -+ u8 fws1; -+ -+ ENTER(); -+ -+ ret = sbi_read_ioreg(priv, CARD_FW_STATUS0_REG, &fws0); -+ if (ret < 0) { -+ LEAVE(); -+ return UAP_STATUS_FAILURE; -+ } -+ -+ ret = sbi_read_ioreg(priv, CARD_FW_STATUS1_REG, &fws1); -+ if (ret < 0) { -+ LEAVE(); -+ return UAP_STATUS_FAILURE; -+ } -+ -+ *dat = (((u16) fws1) << 8) | fws0; -+ -+ LEAVE(); -+ return UAP_STATUS_SUCCESS; -+} -+ -+/** -+ * @brief This function polls the card status register. -+ * -+ * @param priv A pointer to uap_private structure -+ * @param bits the bit mask -+ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE -+ */ -+static int -+mv_sdio_poll_card_status(uap_private * priv, u8 bits) -+{ -+ int tries; -+ u8 cs; -+ -+ ENTER(); -+ -+ for (tries = 0; tries < MAX_POLL_TRIES; tries++) { -+ if (sbi_read_ioreg(priv, CARD_STATUS_REG, &cs) < 0) -+ break; -+ else if ((cs & bits) == bits) { -+ LEAVE(); -+ return UAP_STATUS_SUCCESS; -+ } -+ udelay(10); -+ } -+ -+ PRINTM(WARN, "mv_sdio_poll_card_status failed, tries = %d\n", tries); -+ -+ LEAVE(); -+ return UAP_STATUS_FAILURE; -+} -+ -+/** -+ * @brief This function set the sdio bus width. -+ * -+ * @param priv A pointer to uap_private structure -+ * @param mode 1--1 bit mode, 4--4 bit mode -+ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE -+ */ -+#if 0 -+static int -+sdio_set_bus_width(uap_private * priv, u8 mode) -+{ -+ ENTER(); -+ LEAVE(); -+ return UAP_STATUS_SUCCESS; -+} -+#endif -+ -+/** -+ * @brief This function reads data from the card. -+ * -+ * @param priv A pointer to uap_private structure -+ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE -+ */ -+static int -+sd_card_to_host(uap_private * priv) -+{ -+ int ret = UAP_STATUS_SUCCESS; -+ u16 buf_len = 0; -+ int buf_block_len; -+ int blksz; -+ struct sk_buff *skb = NULL; -+ u16 type; -+ u8 *payload = NULL; -+ struct sdio_mmc_card *card = priv->uap_dev.card; -+ -+ ENTER(); -+ -+ if (!card || !card->func) { -+ PRINTM(ERROR, "card or function is NULL!\n"); -+ ret = UAP_STATUS_FAILURE; -+ goto exit; -+ } -+ -+ /* Read the length of data to be transferred */ -+ ret = sd_read_rx_len(priv, &buf_len); -+ if (ret < 0) { -+ PRINTM(ERROR, "card_to_host, read scratch reg failed\n"); -+ ret = UAP_STATUS_FAILURE; -+ goto exit; -+ } -+ -+ /* Allocate buffer */ -+ blksz = SD_BLOCK_SIZE; -+ buf_block_len = (buf_len + blksz - 1) / blksz; -+ if (buf_len <= INTF_HEADER_LEN || (buf_block_len * blksz) > ALLOC_BUF_SIZE) { -+ PRINTM(ERROR, "card_to_host, invalid packet length: %d\n", buf_len); -+ ret = UAP_STATUS_FAILURE; -+ goto exit; -+ } -+#ifdef PXA3XX_DMA_ALIGN -+ skb = dev_alloc_skb(buf_block_len * blksz + PXA3XX_DMA_ALIGNMENT); -+#else -+ skb = dev_alloc_skb(buf_block_len * blksz); -+#endif -+ if (skb == NULL) { -+ PRINTM(WARN, "No free skb\n"); -+ goto exit; -+ } -+#ifdef PXA3XX_DMA_ALIGN -+ if ((u32) skb->data & (PXA3XX_DMA_ALIGNMENT - 1)) { -+ skb_put(skb, (u32) skb->data & (PXA3XX_DMA_ALIGNMENT - 1)); -+ skb_pull(skb, (u32) skb->data & (PXA3XX_DMA_ALIGNMENT - 1)); -+ } -+#endif /* PXA3XX_DMA_ALIGN */ -+ -+ payload = skb->tail; -+ ret = sdio_readsb(card->func, payload, priv->uap_dev.ioport, -+ buf_block_len * blksz); -+ if (ret < 0) { -+ PRINTM(ERROR, "card_to_host, read iomem failed: %d\n", ret); -+ ret = UAP_STATUS_FAILURE; -+ goto exit; -+ } -+ HEXDUMP("SDIO Blk Rd", payload, blksz * buf_block_len); -+ /* -+ * This is SDIO specific header -+ * u16 length, -+ * u16 type (MV_TYPE_DAT = 0, MV_TYPE_CMD = 1, MV_TYPE_EVENT = 3) -+ */ -+ buf_len = uap_le16_to_cpu(*(u16 *) & payload[0]); -+ type = uap_le16_to_cpu(*(u16 *) & payload[2]); -+ switch (type) { -+ case MV_TYPE_EVENT: -+ skb_put(skb, buf_len); -+ skb_pull(skb, INTF_HEADER_LEN); -+ uap_process_event(priv, skb->data, skb->len); -+ kfree_skb(skb); -+ skb = NULL; -+ break; -+ case MV_TYPE_CMD: -+ skb_put(skb, buf_len); -+ skb_pull(skb, INTF_HEADER_LEN); -+ priv->adapter->cmd_pending = FALSE; -+ if (priv->adapter->cmd_wait_option == -+ HostCmd_OPTION_WAITFORRSP_SLEEPCONFIRM) { -+ priv->adapter->cmd_wait_option = FALSE; -+ uap_process_sleep_confirm_resp(priv, skb->data, skb->len); -+ } else if (priv->adapter->cmd_wait_option) { -+ memcpy(priv->adapter->CmdBuf, skb->data, skb->len); -+ priv->adapter->CmdSize = skb->len; -+ priv->adapter->cmd_wait_option = FALSE; -+ priv->adapter->CmdWaitQWoken = TRUE; -+ wake_up_interruptible(&priv->adapter->cmdwait_q); -+ } -+ kfree_skb(skb); -+ skb = NULL; -+ break; -+ case MV_TYPE_DAT: -+ skb_put(skb, buf_len); -+ skb_pull(skb, INTF_HEADER_LEN); -+ uap_process_rx_packet(priv, skb); -+ break; -+ default: -+ priv->stats.rx_errors++; -+ priv->stats.rx_dropped++; -+ /* Driver specified event and command resp should be handle here */ -+ PRINTM(INFO, "Unknown PKT type:%d\n", type); -+ kfree_skb(skb); -+ skb = NULL; -+ break; -+ } -+ exit: -+ if (ret) { -+ if (skb) -+ kfree_skb(skb); -+ } -+ -+ LEAVE(); -+ return ret; -+} -+ -+/** -+ * @brief This function enables the host interrupts mask -+ * -+ * @param priv A pointer to uap_private structure -+ * @param mask the interrupt mask -+ * @return UAP_STATUS_SUCCESS -+ */ -+static int -+enable_host_int_mask(uap_private * priv, u8 mask) -+{ -+ int ret = UAP_STATUS_SUCCESS; -+ -+ ENTER(); -+ -+ /* Simply write the mask to the register */ -+ ret = sbi_write_ioreg(priv, HOST_INT_MASK_REG, mask); -+ -+ if (ret) { -+ PRINTM(WARN, "Unable to enable the host interrupt!\n"); -+ ret = UAP_STATUS_FAILURE; -+ } -+ -+ LEAVE(); -+ return ret; -+} -+ -+/** @brief This function disables the host interrupts mask. -+ * -+ * @param priv A pointer to uap_private structure -+ * @param mask the interrupt mask -+ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE -+ */ -+static int -+disable_host_int_mask(uap_private * priv, u8 mask) -+{ -+ int ret = UAP_STATUS_SUCCESS; -+ u8 host_int_mask; -+ -+ ENTER(); -+ -+ /* Read back the host_int_mask register */ -+ ret = sbi_read_ioreg(priv, HOST_INT_MASK_REG, &host_int_mask); -+ if (ret) { -+ ret = UAP_STATUS_FAILURE; -+ goto done; -+ } -+ -+ /* Update with the mask and write back to the register */ -+ host_int_mask &= ~mask; -+ ret = sbi_write_ioreg(priv, HOST_INT_MASK_REG, host_int_mask); -+ if (ret < 0) { -+ PRINTM(WARN, "Unable to diable the host interrupt!\n"); -+ ret = UAP_STATUS_FAILURE; -+ goto done; -+ } -+ -+ done: -+ LEAVE(); -+ return ret; -+} -+ -+/******************************************************** -+ Global Functions -+********************************************************/ -+ -+/** -+ * @brief This function handles the interrupt. -+ * -+ * @param func A pointer to sdio_func structure. -+ * @return n/a -+ */ -+static void -+sbi_interrupt(struct sdio_func *func) -+{ -+ struct sdio_mmc_card *card; -+ uap_private *priv; -+ u8 ireg = 0; -+ int ret = UAP_STATUS_SUCCESS; -+ -+ ENTER(); -+ -+ card = sdio_get_drvdata(func); -+ if (!card || !card->priv) { -+ PRINTM(MSG, "%s: sbi_interrupt(%p) card or priv is NULL, card=%p\n", -+ __FUNCTION__, func, card); -+ LEAVE(); -+ return; -+ } -+ priv = card->priv; -+#ifdef FW_WAKEUP_TIME -+ if ((priv->adapter->wt_pwrup_sending != 0L) && -+ (priv->adapter->wt_int == 0L)) -+ priv->adapter->wt_int = get_utimeofday(); -+#endif -+ -+ ireg = sdio_readb(card->func, HOST_INTSTATUS_REG, &ret); -+ if (ret) { -+ PRINTM(WARN, "sdio_read_ioreg: read int status register failed\n"); -+ goto done; -+ } -+ if (ireg != 0) { -+ /* -+ * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS -+ * Clear the interrupt status register and re-enable the interrupt -+ */ -+ PRINTM(INFO, "sdio_ireg = 0x%x\n", ireg); -+ sdio_writeb(card->func, -+ ~(ireg) & (DN_LD_HOST_INT_STATUS | UP_LD_HOST_INT_STATUS), -+ HOST_INTSTATUS_REG, &ret); -+ if (ret) { -+ PRINTM(WARN, -+ "sdio_write_ioreg: clear int status register failed\n"); -+ goto done; -+ } -+ } -+ OS_INT_DISABLE; -+ sd_ireg |= ireg; -+ OS_INT_RESTORE; -+ -+ uap_interrupt(priv); -+ done: -+ LEAVE(); -+} -+ -+/** -+ * @brief This function probe the card -+ * -+ * @param func A pointer to sdio_func structure -+ * @param id A pointer to structure sd_device_id -+ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE -+ */ -+static int -+uap_probe(struct sdio_func *func, const struct sdio_device_id *id) -+{ -+ int ret = UAP_STATUS_FAILURE; -+ struct sdio_mmc_card *card = NULL; -+ -+ ENTER(); -+ -+ PRINTM(MSG, "%s: vendor=0x%4.04X device=0x%4.04X class=%d function=%d\n", -+ __FUNCTION__, func->vendor, func->device, func->class, func->num); -+ -+ card = kzalloc(sizeof(struct sdio_mmc_card), GFP_KERNEL); -+ if (!card) { -+ ret = -ENOMEM; -+ goto done; -+ } -+ -+ card->func = func; -+ -+ if (!uap_add_card(card)) { -+ PRINTM(ERROR, "%s: uap_add_callback failed\n", __FUNCTION__); -+ kfree(card); -+ ret = UAP_STATUS_FAILURE; -+ goto done; -+ } -+ -+ ret = UAP_STATUS_SUCCESS; -+ -+ done: -+ LEAVE(); -+ return ret; -+} -+ -+/** -+ * @brief This function removes the card -+ * -+ * @param func A pointer to sdio_func structure -+ * @return N/A -+ */ -+static void -+uap_remove(struct sdio_func *func) -+{ -+ struct sdio_mmc_card *card; -+ -+ ENTER(); -+ -+ if (func) { -+ card = sdio_get_drvdata(func); -+ if (card) { -+ uap_remove_card(card); -+ kfree(card); -+ } -+ } -+ -+ LEAVE(); -+} -+ -+#ifdef CONFIG_PM -+/** -+ * @brief This function handles client driver suspend -+ * -+ * @param func A pointer to sdio_func structure -+ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE -+ */ -+int -+uap_suspend(struct sdio_func *func) -+{ -+ ENTER(); -+ LEAVE(); -+ return 0; -+} -+ -+/** -+ * @brief This function handles client driver resume -+ * -+ * @param func A pointer to sdio_func structure -+ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE -+ */ -+int -+uap_resume(struct sdio_func *func) -+{ -+ ENTER(); -+ LEAVE(); -+ return 0; -+} -+#endif -+ -+/** Device ID for SD8688 */ -+#define SD_DEVICE_ID_8688_UAP 0x9104 -+/** UAP IDs */ -+static const struct sdio_device_id uap_ids[] = { -+ {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SD_DEVICE_ID_8688_UAP)}, -+ {}, -+}; -+ -+MODULE_DEVICE_TABLE(sdio, uap_ids); -+ -+static struct sdio_driver uap_sdio = { -+ .name = "uap_sdio", -+ .id_table = uap_ids, -+ .probe = uap_probe, -+ .remove = uap_remove, -+#ifdef CONFIG_PM -+/* .suspend = uap_suspend, */ -+/* .resume = uap_resume, */ -+#endif -+ -+}; -+ -+/** -+ * @brief This function registers the IF module in bus driver. -+ * -+ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE -+ */ -+int __init -+sbi_register() -+{ -+ int ret = UAP_STATUS_SUCCESS; -+ -+ ENTER(); -+ -+ /* SDIO Driver Registration */ -+ if (sdio_register_driver(&uap_sdio) != 0) { -+ PRINTM(FATAL, "SDIO Driver Registration Failed \n"); -+ ret = UAP_STATUS_FAILURE; -+ } -+ -+ LEAVE(); -+ return ret; -+} -+ -+/** -+ * @brief This function de-registers the IF module in bus driver. -+ * -+ * @return n/a -+ */ -+void __exit -+sbi_unregister(void) -+{ -+ ENTER(); -+ -+ /* SDIO Driver Unregistration */ -+ sdio_unregister_driver(&uap_sdio); -+ -+ LEAVE(); -+} -+ -+/** -+ * @brief This function checks the interrupt status and handle it accordingly. -+ * -+ * @param priv A pointer to uap_private structure -+ * @param ireg A pointer to variable that keeps returned value -+ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE -+ */ -+int -+sbi_get_int_status(uap_private * priv, u8 * ireg) -+{ -+ int ret = UAP_STATUS_SUCCESS; -+ u8 sdio_ireg = 0; -+ struct sdio_mmc_card *card = priv->uap_dev.card; -+ -+ ENTER(); -+ -+ *ireg = 0; -+ OS_INT_DISABLE; -+ sdio_ireg = sd_ireg; -+ sd_ireg = 0; -+ OS_INT_RESTORE; -+ -+ sdio_claim_host(card->func); -+ -+ if (sdio_ireg & DN_LD_HOST_INT_STATUS) { /* tx_done INT */ -+ if (!priv->uap_dev.cmd_sent) { /* tx_done already received */ -+ PRINTM(INFO, -+ "warning: tx_done already received: tx_dnld_rdy=0x%x int status=0x%x\n", -+ priv->uap_dev.cmd_sent, sdio_ireg); -+ } else { -+ priv->uap_dev.cmd_sent = FALSE; -+ priv->uap_dev.data_sent = FALSE; -+ if ( (priv->uap_dev.netdev->reg_state == NETREG_REGISTERED) && (skb_queue_len(&priv->adapter->tx_queue) < TX_LOW_WATERMARK)) { -+ os_start_queue(priv); -+ } -+ } -+ } -+ if (sdio_ireg & UP_LD_HOST_INT_STATUS) { -+ sd_card_to_host(priv); -+ } -+ -+ *ireg = sdio_ireg; -+ ret = UAP_STATUS_SUCCESS; -+ sdio_release_host(card->func); -+ -+ LEAVE(); -+ return ret; -+} -+ -+/** -+ * @brief This function disables the host interrupts. -+ * -+ * @param priv A pointer to uap_private structure -+ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE -+ */ -+int -+sbi_disable_host_int(uap_private * priv) -+{ -+ struct sdio_mmc_card *card = priv->uap_dev.card; -+ int ret; -+ -+ ENTER(); -+ -+ sdio_claim_host(card->func); -+ ret = disable_host_int_mask(priv, HIM_DISABLE); -+ sdio_release_host(card->func); -+ -+ LEAVE(); -+ return ret; -+} -+ -+/** -+ * @brief This function enables the host interrupts. -+ * -+ * @param priv A pointer to uap_private structure -+ * @return UAP_STATUS_SUCCESS -+ */ -+int -+sbi_enable_host_int(uap_private * priv) -+{ -+ struct sdio_mmc_card *card = priv->uap_dev.card; -+ int ret; -+ -+ ENTER(); -+ -+ sdio_claim_host(card->func); -+ ret = enable_host_int_mask(priv, HIM_ENABLE); -+ sdio_release_host(card->func); -+ -+ LEAVE(); -+ return ret; -+} -+ -+/** -+ * @brief This function de-registers the device. -+ * -+ * @param priv A pointer to uap_private structure -+ * @return UAP_STATUS_SUCCESS -+ */ -+int -+sbi_unregister_dev(uap_private * priv) -+{ -+ struct sdio_mmc_card *card = priv->uap_dev.card; -+ -+ ENTER(); -+ -+ if (!card || !card->func) { -+ PRINTM(ERROR, "Error: card or function is NULL!\n"); -+ goto done; -+ } -+ -+ sdio_claim_host(card->func); -+ sdio_release_irq(card->func); -+ sdio_disable_func(card->func); -+ sdio_release_host(card->func); -+ -+ sdio_set_drvdata(card->func, NULL); -+ -+ done: -+ LEAVE(); -+ return UAP_STATUS_SUCCESS; -+} -+ -+/** -+ * @brief This function registers the device. -+ * -+ * @param priv A pointer to uap_private structure -+ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE -+ */ -+int -+sbi_register_dev(uap_private * priv) -+{ -+ int ret = UAP_STATUS_FAILURE; -+ u8 reg; -+ struct sdio_mmc_card *card = priv->uap_dev.card; -+ struct sdio_func *func; -+ -+ ENTER(); -+ -+ if (!card || !card->func) { -+ PRINTM(ERROR, "Error: card or function is NULL!\n"); -+ goto done; -+ } -+ -+ func = card->func; -+ -+ /* Initialize the private structure */ -+ priv->uap_dev.ioport = 0; -+ -+ sdio_claim_host(func); -+ -+ ret = sdio_enable_func(func); -+ if (ret) { -+ PRINTM(FATAL, "sdio_enable_func() failed: ret=%d\n", ret); -+ goto release_host; -+ } -+ -+ ret = sdio_claim_irq(func, sbi_interrupt); -+ if (ret) { -+ PRINTM(FATAL, "sdio_claim_irq failed: ret=%d\n", ret); -+ goto disable_func; -+ } -+ -+ /* Read the IO port */ -+ ret = sbi_read_ioreg(priv, IO_PORT_0_REG, ®); -+ if (ret) -+ goto release_irq; -+ else -+ priv->uap_dev.ioport |= reg; -+ -+ ret = sbi_read_ioreg(priv, IO_PORT_1_REG, ®); -+ if (ret) -+ goto release_irq; -+ else -+ priv->uap_dev.ioport |= (reg << 8); -+ -+ ret = sbi_read_ioreg(priv, IO_PORT_2_REG, ®); -+ if (ret) -+ goto release_irq; -+ else -+ priv->uap_dev.ioport |= (reg << 16); -+ -+ PRINTM(INFO, "SDIO FUNC #%d IO port: 0x%x\n", func->num, -+ priv->uap_dev.ioport); -+ -+ ret = sdio_set_block_size(card->func, SD_BLOCK_SIZE); -+ if (ret) { -+ PRINTM(ERROR, "%s: cannot set SDIO block size\n", __FUNCTION__); -+ ret = UAP_STATUS_FAILURE; -+ goto release_irq; -+ } -+ priv->hotplug_device = &func->dev; -+ -+ if (helper_name == NULL) { -+ helper_name = DEFAULT_HELPER_NAME; -+ } -+ if (fw_name == NULL) { -+ fw_name = DEFAULT_FW_NAME; -+ } -+ sdio_release_host(func); -+ -+ sdio_set_drvdata(func, card); -+ -+ ret = UAP_STATUS_SUCCESS; -+ goto done; -+ -+ release_irq: -+ sdio_release_irq(func); -+ disable_func: -+ sdio_disable_func(func); -+ release_host: -+ sdio_release_host(func); -+ -+ done: -+ LEAVE(); -+ return ret; -+} -+ -+/** -+ * @brief This function sends data to the card. -+ * -+ * @param priv A pointer to uap_private structure -+ * @param payload A pointer to the data/cmd buffer -+ * @param nb the length of data/cmd -+ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE -+ */ -+int -+sbi_host_to_card(uap_private * priv, u8 * payload, u16 nb) -+{ -+ struct sdio_mmc_card *card = priv->uap_dev.card; -+ int ret = UAP_STATUS_SUCCESS; -+ int buf_block_len; -+ int blksz; -+ int i = 0; -+ u8 *buf = NULL; -+#ifdef PXA3XX_DMA_ALIGN -+ void *tmpbuf = NULL; -+ int tmpbufsz; -+#endif -+ -+ ENTER(); -+ -+ if (!card || !card->func) { -+ PRINTM(ERROR, "card or function is NULL!\n"); -+ LEAVE(); -+ return UAP_STATUS_FAILURE; -+ } -+ buf = payload; -+#ifdef PXA3XX_DMA_ALIGN -+ if ((u32) payload & (PXA3XX_DMA_ALIGNMENT - 1)) { -+ tmpbufsz = ALIGN_SZ(nb, PXA3XX_DMA_ALIGNMENT); -+ tmpbuf = kmalloc(tmpbufsz, GFP_KERNEL); -+ memset(tmpbuf, 0, tmpbufsz); -+ /* Ensure 8-byte aligned CMD buffer */ -+ buf = (u8 *) ALIGN_ADDR(tmpbuf, PXA3XX_DMA_ALIGNMENT); -+ memcpy(buf, payload, nb); -+ } -+#endif -+ /* Allocate buffer and copy payload */ -+ blksz = SD_BLOCK_SIZE; -+ buf_block_len = (nb + blksz - 1) / blksz; -+ sdio_claim_host(card->func); -+#define MAX_WRITE_IOMEM_RETRY 2 -+ priv->uap_dev.cmd_sent = TRUE; -+ priv->uap_dev.data_sent = TRUE; -+ do { -+ /* Transfer data to card */ -+ ret = sdio_writesb(card->func, priv->uap_dev.ioport, buf, -+ buf_block_len * blksz); -+ if (ret < 0) { -+ i++; -+ PRINTM(ERROR, "host_to_card, write iomem (%d) failed: %d\n", i, -+ ret); -+ ret = UAP_STATUS_FAILURE; -+ if (i > MAX_WRITE_IOMEM_RETRY) -+ goto exit; -+ } else { -+ HEXDUMP("SDIO Blk Wr", payload, nb); -+ } -+ } while (ret == UAP_STATUS_FAILURE); -+ exit: -+ sdio_release_host(card->func); -+#ifdef PXA3XX_DMA_ALIGN -+ if (tmpbuf) -+ kfree(tmpbuf); -+#endif -+ if (ret == UAP_STATUS_FAILURE) { -+ priv->uap_dev.cmd_sent = FALSE; -+ priv->uap_dev.data_sent = FALSE; -+ } -+ LEAVE(); -+ return ret; -+} -+ -+/** -+ * @brief This function reads CIS information. -+ * -+ * @param priv A pointer to uap_private structure -+ * @param cisinfo A pointer to CIS information output buffer -+ * @param cislen A pointer to length of CIS info output buffer -+ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE -+ */ -+#if 0 -+static int -+sbi_get_cis_info(uap_private * priv, void *cisinfo, int *cislen) -+{ -+#define CIS_PTR (0x8000) -+ struct sdio_mmc_card *card = priv->uap_dev.card; -+ unsigned int i, cis_ptr = CIS_PTR; -+ int ret = UAP_STATUS_FAILURE; -+ -+ ENTER(); -+ -+ if (!card || !card->func) { -+ PRINTM(ERROR, "sbi_get_cis_info(): card or function is NULL!\n"); -+ goto exit; -+ } -+#define MAX_SDIO_CIS_INFO_LEN (256) -+ if (!cisinfo || (*cislen < MAX_SDIO_CIS_INFO_LEN)) { -+ PRINTM(WARN, "ERROR! get_cis_info: insufficient buffer passed\n"); -+ goto exit; -+ } -+ -+ *cislen = MAX_SDIO_CIS_INFO_LEN; -+ -+ sdio_claim_host(card->func); -+ -+ PRINTM(INFO, "cis_ptr=%#x\n", cis_ptr); -+ -+ /* Read the Tuple Data */ -+ for (i = 0; i < *cislen; i++) { -+ ((unsigned char *) cisinfo)[i] = -+ sdio_readb(card->func, cis_ptr + i, &ret); -+ if (ret) { -+ PRINTM(WARN, "get_cis_info error: ret=%d\n", ret); -+ ret = UAP_STATUS_FAILURE; -+ goto done; -+ } -+ PRINTM(INFO, "cisinfo[%d]=%#x\n", i, ((unsigned char *) cisinfo)[i]); -+ } -+ -+ done: -+ sdio_release_host(card->func); -+ exit: -+ LEAVE(); -+ return ret; -+} -+#endif -+/** -+ * @brief This function downloads helper image to the card. -+ * -+ * @param priv A pointer to uap_private structure -+ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE -+ */ -+int -+sbi_prog_helper(uap_private * priv) -+{ -+ struct sdio_mmc_card *card = priv->uap_dev.card; -+ u8 *helper = NULL; -+ int helperlen; -+ int ret = UAP_STATUS_SUCCESS; -+ void *tmphlprbuf = NULL; -+ int tmphlprbufsz; -+ u8 *hlprbuf; -+ int hlprblknow; -+ u32 tx_len; -+#ifdef FW_DOWNLOAD_SPEED -+ u32 tv1, tv2; -+#endif -+ -+ ENTER(); -+ -+ if (!card || !card->func) { -+ PRINTM(ERROR, "sbi_prog_helper(): card or function is NULL!\n"); -+ goto done; -+ } -+ -+ if (priv->fw_helper) { -+ helper = (u8 *) priv->fw_helper->data; -+ helperlen = priv->fw_helper->size; -+ } else { -+ PRINTM(MSG, "No helper image found! Terminating download.\n"); -+ LEAVE(); -+ return UAP_STATUS_FAILURE; -+ } -+ -+ PRINTM(INFO, "Downloading helper image (%d bytes), block size %d bytes\n", -+ helperlen, SD_BLOCK_SIZE); -+ -+#ifdef FW_DOWNLOAD_SPEED -+ tv1 = get_utimeofday(); -+#endif -+ -+#ifdef PXA3XX_DMA_ALIGN -+ tmphlprbufsz = ALIGN_SZ(UAP_UPLD_SIZE, PXA3XX_DMA_ALIGNMENT); -+#else /* !PXA3XX_DMA_ALIGN */ -+ tmphlprbufsz = UAP_UPLD_SIZE; -+#endif /* !PXA3XX_DMA_ALIGN */ -+ tmphlprbuf = kmalloc(tmphlprbufsz, GFP_KERNEL); -+ if (!tmphlprbuf) { -+ PRINTM(ERROR, -+ "Unable to allocate buffer for helper. Terminating download\n"); -+ ret = UAP_STATUS_FAILURE; -+ goto done; -+ } -+ memset(tmphlprbuf, 0, tmphlprbufsz); -+#ifdef PXA3XX_DMA_ALIGN -+ hlprbuf = (u8 *) ALIGN_ADDR(tmphlprbuf, PXA3XX_DMA_ALIGNMENT); -+#else /* !PXA3XX_DMA_ALIGN */ -+ hlprbuf = (u8 *) tmphlprbuf; -+#endif /* !PXA3XX_DMA_ALIGN */ -+ -+ sdio_claim_host(card->func); -+ -+ /* Perform helper data transfer */ -+ tx_len = (FIRMWARE_TRANSFER_NBLOCK * SD_BLOCK_SIZE) - INTF_HEADER_LEN; -+ hlprblknow = 0; -+ do { -+ /* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY bits */ -+ ret = mv_sdio_poll_card_status(priv, CARD_IO_READY | DN_LD_CARD_RDY); -+ if (ret < 0) { -+ PRINTM(FATAL, "Helper download poll status timeout @ %d\n", -+ hlprblknow); -+ goto done; -+ } -+ -+ /* More data? */ -+ if (hlprblknow >= helperlen) -+ break; -+ -+ /* Set blocksize to transfer - checking for last block */ -+ if (helperlen - hlprblknow < tx_len) -+ tx_len = helperlen - hlprblknow; -+ -+ /* Set length to the 4-byte header */ -+ *(u32 *) hlprbuf = uap_cpu_to_le32(tx_len); -+ -+ /* Copy payload to buffer */ -+ memcpy(&hlprbuf[INTF_HEADER_LEN], &helper[hlprblknow], tx_len); -+ -+ PRINTM(INFO, "."); -+ -+ /* Send data */ -+ ret = sdio_writesb(card->func, priv->uap_dev.ioport, -+ hlprbuf, FIRMWARE_TRANSFER_NBLOCK * SD_BLOCK_SIZE); -+ -+ if (ret < 0) { -+ PRINTM(FATAL, "IO error during helper download @ %d\n", hlprblknow); -+ goto done; -+ } -+ -+ hlprblknow += tx_len; -+ } while (TRUE); -+ -+#ifdef FW_DOWNLOAD_SPEED -+ tv2 = get_utimeofday(); -+ PRINTM(INFO, "helper: %ld.%03ld.%03ld ", tv1 / 1000000, -+ (tv1 % 1000000) / 1000, tv1 % 1000); -+ PRINTM(INFO, " -> %ld.%03ld.%03ld ", tv2 / 1000000, (tv2 % 1000000) / 1000, -+ tv2 % 1000); -+ tv2 -= tv1; -+ PRINTM(INFO, " == %ld.%03ld.%03ld\n", tv2 / 1000000, (tv2 % 1000000) / 1000, -+ tv2 % 1000); -+#endif -+ -+ /* Write last EOF data */ -+ PRINTM(INFO, "\nTransferring helper image EOF block\n"); -+ memset(hlprbuf, 0x0, SD_BLOCK_SIZE); -+ ret = sdio_writesb(card->func, priv->uap_dev.ioport, -+ hlprbuf, SD_BLOCK_SIZE); -+ -+ if (ret < 0) { -+ PRINTM(FATAL, "IO error in writing helper image EOF block\n"); -+ goto done; -+ } -+ -+ ret = UAP_STATUS_SUCCESS; -+ -+ done: -+ sdio_release_host(card->func); -+ if (tmphlprbuf) -+ kfree(tmphlprbuf); -+ -+ LEAVE(); -+ return ret; -+} -+ -+/** -+ * @brief This function downloads firmware image to the card. -+ * -+ * @param priv A pointer to uap_private structure -+ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE -+ */ -+int -+sbi_prog_fw_w_helper(uap_private * priv) -+{ -+ struct sdio_mmc_card *card = priv->uap_dev.card; -+ u8 *firmware = NULL; -+ int firmwarelen; -+ u8 base0; -+ u8 base1; -+ int ret = UAP_STATUS_SUCCESS; -+ int offset; -+ void *tmpfwbuf = NULL; -+ int tmpfwbufsz; -+ u8 *fwbuf; -+ u16 len; -+ int txlen = 0; -+ int tx_blocks = 0; -+ int i = 0; -+ int tries = 0; -+#ifdef FW_DOWNLOAD_SPEED -+ u32 tv1, tv2; -+#endif -+ -+ ENTER(); -+ -+ if (!card || !card->func) { -+ PRINTM(ERROR, "sbi_prog_fw_w_helper(): card or function is NULL!\n"); -+ goto done; -+ } -+ -+ if (priv->firmware) { -+ firmware = (u8 *) priv->firmware->data; -+ firmwarelen = priv->firmware->size; -+ } else { -+ PRINTM(MSG, "No firmware image found! Terminating download.\n"); -+ LEAVE(); -+ return UAP_STATUS_FAILURE; -+ } -+ -+ PRINTM(INFO, "Downloading FW image (%d bytes)\n", firmwarelen); -+ -+#ifdef FW_DOWNLOAD_SPEED -+ tv1 = get_utimeofday(); -+#endif -+ -+#ifdef PXA3XX_DMA_ALIGN -+ tmpfwbufsz = ALIGN_SZ(UAP_UPLD_SIZE, PXA3XX_DMA_ALIGNMENT); -+#else /* PXA3XX_DMA_ALIGN */ -+ tmpfwbufsz = UAP_UPLD_SIZE; -+#endif /* PXA3XX_DMA_ALIGN */ -+ tmpfwbuf = kmalloc(tmpfwbufsz, GFP_KERNEL); -+ if (!tmpfwbuf) { -+ PRINTM(ERROR, -+ "Unable to allocate buffer for firmware. Terminating download.\n"); -+ ret = UAP_STATUS_FAILURE; -+ goto done; -+ } -+ memset(tmpfwbuf, 0, tmpfwbufsz); -+#ifdef PXA3XX_DMA_ALIGN -+ /* Ensure 8-byte aligned firmware buffer */ -+ fwbuf = (u8 *) ALIGN_ADDR(tmpfwbuf, PXA3XX_DMA_ALIGNMENT); -+#else /* PXA3XX_DMA_ALIGN */ -+ fwbuf = (u8 *) tmpfwbuf; -+#endif /* PXA3XX_DMA_ALIGN */ -+ -+ sdio_claim_host(card->func); -+ -+ /* Perform firmware data transfer */ -+ offset = 0; -+ do { -+ /* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY bits */ -+ ret = mv_sdio_poll_card_status(priv, CARD_IO_READY | DN_LD_CARD_RDY); -+ if (ret < 0) { -+ PRINTM(FATAL, "FW download with helper poll status timeout @ %d\n", -+ offset); -+ goto done; -+ } -+ -+ /* More data? */ -+ if (offset >= firmwarelen) -+ break; -+ -+ for (tries = 0; tries < MAX_POLL_TRIES; tries++) { -+ if ((ret = sbi_read_ioreg(priv, HOST_F1_RD_BASE_0, &base0)) < 0) { -+ PRINTM(WARN, "Dev BASE0 register read failed:" -+ " base0=0x%04X(%d). Terminating download.\n", base0, -+ base0); -+ ret = UAP_STATUS_FAILURE; -+ goto done; -+ } -+ if ((ret = sbi_read_ioreg(priv, HOST_F1_RD_BASE_1, &base1)) < 0) { -+ PRINTM(WARN, "Dev BASE1 register read failed:" -+ " base1=0x%04X(%d). Terminating download.\n", base1, -+ base1); -+ ret = UAP_STATUS_FAILURE; -+ goto done; -+ } -+ len = (((u16) base1) << 8) | base0; -+ -+ /* For SD8688 wait until the length is not 0, 1 or 2 before -+ downloading the first FW block, since BOOT code writes the -+ register to indicate the helper/FW download winner, the value -+ could be 1 or 2 (Func1 or Func2). */ -+ if ((len && offset) || (len > 2)) -+ break; -+ udelay(10); -+ } -+ -+ if (len == 0) -+ break; -+ else if (len > UAP_UPLD_SIZE) { -+ PRINTM(FATAL, "FW download failure @ %d, invalid length %d\n", -+ offset, len); -+ ret = UAP_STATUS_FAILURE; -+ goto done; -+ } -+ -+ txlen = len; -+ -+ if (len & BIT(0)) { -+ i++; -+ if (i > MAX_WRITE_IOMEM_RETRY) { -+ PRINTM(FATAL, -+ "FW download failure @ %d, over max retry count\n", -+ offset); -+ ret = UAP_STATUS_FAILURE; -+ goto done; -+ } -+ PRINTM(ERROR, "FW CRC error indicated by the helper:" -+ " len = 0x%04X, txlen = %d\n", len, txlen); -+ len &= ~BIT(0); -+ /* Setting this to 0 to resend from same offset */ -+ txlen = 0; -+ } else { -+ i = 0; -+ -+ /* Set blocksize to transfer - checking for last block */ -+ if (firmwarelen - offset < txlen) { -+ txlen = firmwarelen - offset; -+ } -+ PRINTM(INFO, "."); -+ -+ tx_blocks = (txlen + SD_BLOCK_SIZE - 1) / SD_BLOCK_SIZE; -+ -+ /* Copy payload to buffer */ -+ memcpy(fwbuf, &firmware[offset], txlen); -+ } -+ -+ /* Send data */ -+ ret = sdio_writesb(card->func, priv->uap_dev.ioport, -+ fwbuf, tx_blocks * SD_BLOCK_SIZE); -+ -+ if (ret < 0) { -+ PRINTM(ERROR, "FW download, write iomem (%d) failed @ %d\n", i, -+ offset); -+ if (sbi_write_ioreg(priv, CONFIGURATION_REG, 0x04) < 0) { -+ PRINTM(ERROR, "write ioreg failed (CFG)\n"); -+ } -+ } -+ -+ offset += txlen; -+ } while (TRUE); -+ -+ PRINTM(INFO, "\nFW download over, size %d bytes\n", offset); -+ -+ ret = UAP_STATUS_SUCCESS; -+ done: -+#ifdef FW_DOWNLOAD_SPEED -+ tv2 = get_utimeofday(); -+ PRINTM(INFO, "FW: %ld.%03ld.%03ld ", tv1 / 1000000, -+ (tv1 % 1000000) / 1000, tv1 % 1000); -+ PRINTM(INFO, " -> %ld.%03ld.%03ld ", tv2 / 1000000, -+ (tv2 % 1000000) / 1000, tv2 % 1000); -+ tv2 -= tv1; -+ PRINTM(INFO, " == %ld.%03ld.%03ld\n", tv2 / 1000000, -+ (tv2 % 1000000) / 1000, tv2 % 1000); -+#endif -+ sdio_release_host(card->func); -+ if (tmpfwbuf) -+ kfree(tmpfwbuf); -+ -+ LEAVE(); -+ return ret; -+} -+ -+/** -+ * @brief This function checks if the firmware is ready to accept -+ * command or not. -+ * -+ * @param priv A pointer to uap_private structure -+ * @param pollnum Poll number -+ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE -+ */ -+int -+sbi_check_fw_status(uap_private * priv, int pollnum) -+{ -+ struct sdio_mmc_card *card = priv->uap_dev.card; -+ int ret = UAP_STATUS_SUCCESS; -+ u16 firmwarestat; -+ int tries; -+ -+ ENTER(); -+ -+ sdio_claim_host(card->func); -+ -+ /* Wait for firmware initialization event */ -+ for (tries = 0; tries < pollnum; tries++) { -+ if ((ret = sd_read_firmware_status(priv, &firmwarestat)) < 0) -+ continue; -+ if (firmwarestat == FIRMWARE_READY) { -+ ret = UAP_STATUS_SUCCESS; -+ break; -+ } else { -+ mdelay(10); -+ ret = UAP_STATUS_FAILURE; -+ } -+ } -+ -+ if (ret < 0) -+ goto done; -+ -+ ret = UAP_STATUS_SUCCESS; -+ sd_get_rx_unit(priv); -+ -+ done: -+ sdio_release_host(card->func); -+ -+ LEAVE(); -+ return ret; -+} -+ -+/** -+ * @brief This function set bus clock on/off -+ * -+ * @param priv A pointer to uap_private structure -+ * @param option TRUE--on , FALSE--off -+ * @return UAP_STATUS_SUCCESS -+ */ -+#if 0 -+static int -+sbi_set_bus_clock(uap_private * priv, u8 option) -+{ -+ ENTER(); -+ LEAVE(); -+ return UAP_STATUS_SUCCESS; -+} -+#endif -+ -+/** -+ * @brief This function wakeup firmware -+ * -+ * @param priv A pointer to uap_private structure -+ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE -+ */ -+int -+sbi_wakeup_firmware(uap_private * priv) -+{ -+ struct sdio_mmc_card *card = priv->uap_dev.card; -+ int ret = UAP_STATUS_SUCCESS; -+ -+ ENTER(); -+ -+ if (!card || !card->func) { -+ PRINTM(ERROR, "card or function is NULL!\n"); -+ LEAVE(); -+ return UAP_STATUS_FAILURE; -+ } -+ sdio_claim_host(card->func); -+ sdio_writeb(card->func, HOST_POWER_UP, CONFIGURATION_REG, &ret); -+ sdio_release_host(card->func); -+ LEAVE(); -+ return ret; -+} -diff -Naur backports-3.18.1-1.org/drivers/net/wireless/libertas_uap/uap_sdio_mmc.h backports-3.18.1-1/drivers/net/wireless/libertas_uap/uap_sdio_mmc.h ---- backports-3.18.1-1.org/drivers/net/wireless/libertas_uap/uap_sdio_mmc.h 1970-01-01 01:00:00.000000000 +0100 -+++ backports-3.18.1-1/drivers/net/wireless/libertas_uap/uap_sdio_mmc.h 2014-12-29 20:37:43.955764567 +0100 -@@ -0,0 +1,136 @@ -+/** @file uap_sdio_mmc.h -+ * @brief This file contains SDIO IF (interface) module -+ * related macros, enum, and structure. -+ * -+ * Copyright (C) 2007-2009, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available along with the File in the gpl.txt file or by writing to -+ * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -+ * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ * -+ */ -+/**************************************************** -+Change log: -+ 10/10/07: initial version -+****************************************************/ -+ -+#ifndef _UAP_SDIO_MMC_H -+#define _UAP_SDIO_MMC_H -+ -+#include -+#include -+#include -+#include -+ -+#include "uap_headers.h" -+ -+/** The number of times to try when waiting for downloaded firmware to -+ become active. (polling the scratch register). */ -+#define MAX_FIRMWARE_POLL_TRIES 100 -+ -+/** Firmware ready */ -+#define FIRMWARE_READY 0xfedc -+ -+/** Number of firmware blocks to transfer */ -+#define FIRMWARE_TRANSFER_NBLOCK 2 -+ -+/* Host Control Registers */ -+/** Host Control Registers : I/O port 0 */ -+#define IO_PORT_0_REG 0x00 -+/** Host Control Registers : I/O port 1 */ -+#define IO_PORT_1_REG 0x01 -+/** Host Control Registers : I/O port 2 */ -+#define IO_PORT_2_REG 0x02 -+ -+/** Host Control Registers : Configuration */ -+#define CONFIGURATION_REG 0x03 -+/** Host Control Registers : Host without Command 53 finish host */ -+#define HOST_WO_CMD53_FINISH_HOST (0x1U << 2) -+/** Host Control Registers : Host power up */ -+#define HOST_POWER_UP (0x1U << 1) -+/** Host Control Registers : Host power down */ -+#define HOST_POWER_DOWN (0x1U << 0) -+ -+/** Host Control Registers : Host interrupt mask */ -+#define HOST_INT_MASK_REG 0x04 -+/** Host Control Registers : Upload host interrupt mask */ -+#define UP_LD_HOST_INT_MASK (0x1U) -+/** Host Control Registers : Download host interrupt mask */ -+#define DN_LD_HOST_INT_MASK (0x2U) -+/** Enable Host interrupt mask */ -+#define HIM_ENABLE (UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK) -+/** Disable Host interrupt mask */ -+#define HIM_DISABLE 0xff -+ -+/** Host Control Registers : Host interrupt status */ -+#define HOST_INTSTATUS_REG 0x05 -+/** Host Control Registers : Upload host interrupt status */ -+#define UP_LD_HOST_INT_STATUS (0x1U) -+/** Host Control Registers : Download host interrupt status */ -+#define DN_LD_HOST_INT_STATUS (0x2U) -+ -+/** Host F1 read base 0 */ -+#define HOST_F1_RD_BASE_0 0x10 -+/** Host F1 read base 1 */ -+#define HOST_F1_RD_BASE_1 0x11 -+ -+/** Card Control Registers : Card status register */ -+#define CARD_STATUS_REG 0x20 -+/** Card Control Registers : Card I/O ready */ -+#define CARD_IO_READY (0x1U << 3) -+/** Card Control Registers : CIS card ready */ -+#define CIS_CARD_RDY (0x1U << 2) -+/** Card Control Registers : Upload card ready */ -+#define UP_LD_CARD_RDY (0x1U << 1) -+/** Card Control Registers : Download card ready */ -+#define DN_LD_CARD_RDY (0x1U << 0) -+ -+/** Card Control Registers : Card OCR 0 register */ -+#define CARD_OCR_0_REG 0x34 -+/** Card Control Registers : Card OCR 1 register */ -+#define CARD_OCR_1_REG 0x35 -+ -+/** Firmware status 0 register */ -+#define CARD_FW_STATUS0_REG 0x40 -+/** Firmware status 1 register */ -+#define CARD_FW_STATUS1_REG 0x41 -+/** Rx length register */ -+#define CARD_RX_LEN_REG 0x42 -+/** Rx unit register */ -+#define CARD_RX_UNIT_REG 0x43 -+ -+/** Chip Id Register 0 */ -+#define CARD_CHIP_ID_0_REG 0x801c -+/** Chip Id Register 1 */ -+#define CARD_CHIP_ID_1_REG 0x801d -+ -+#ifdef PXA3XX_DMA_ALIGN -+/** DMA alignment value for PXA3XX platforms */ -+#define PXA3XX_DMA_ALIGNMENT 8 -+/** Macros for Data Alignment : size */ -+#define ALIGN_SZ(p, a) \ -+ (((p) + ((a) - 1)) & ~((a) - 1)) -+ -+/** Macros for Data Alignment : address */ -+#define ALIGN_ADDR(p, a) \ -+ ((((u32)(p)) + (((u32)(a)) - 1)) & ~(((u32)(a)) - 1)) -+#endif /* PXA3XX_DMA_ALIGN */ -+ -+struct sdio_mmc_card -+{ -+ /** sdio_func structure pointer */ -+ struct sdio_func *func; -+ /** uap_private structure pointer */ -+ uap_private *priv; -+}; -+ -+#endif /* _UAP_SDIO_MMC_H */ -diff -Naur backports-3.18.1-1.org/drivers/net/wireless/Makefile backports-3.18.1-1/drivers/net/wireless/Makefile ---- backports-3.18.1-1.org/drivers/net/wireless/Makefile 2014-12-21 22:37:15.000000000 +0100 -+++ backports-3.18.1-1/drivers/net/wireless/Makefile 2014-12-29 20:40:33.632440784 +0100 -@@ -60,3 +60,5 @@ - - obj-$(CPTCFG_CW1200) += cw1200/ - obj-$(CPTCFG_RSI_91X) += rsi/ -+ -+obj-$(CPTCFG_LIBERTAS_UAP) += libertas_uap/ diff --git a/src/patches/backports-3.18.1-1_no_dma_sgtable_on_x86_64.patch b/src/patches/backports-3.18.1-1_no_dma_sgtable_on_x86_64.patch deleted file mode 100644 index 5a2d04e7ce..0000000000 --- a/src/patches/backports-3.18.1-1_no_dma_sgtable_on_x86_64.patch +++ /dev/null @@ -1,26 +0,0 @@ -diff -Naur backports-4.1.1-1.org/compat/dma-shared-helpers.c backports-4.1.1-1/compat/dma-shared-helpers.c ---- backports-4.1.1-1.org/compat/dma-shared-helpers.c 2015-07-01 23:10:37.000000000 +0200 -+++ backports-4.1.1-1/compat/dma-shared-helpers.c 2015-09-25 13:29:14.006762269 +0200 -@@ -20,22 +20,3 @@ - #endif /* LINUX_VERSION_CODE <= KERNEL_VERSION(3,6,0) */ - #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0) */ - --#if RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,0) --/* -- * Create scatter-list for the already allocated DMA buffer. -- */ --int dma_common_get_sgtable(struct device *dev, struct sg_table *sgt, -- void *cpu_addr, dma_addr_t handle, size_t size) --{ -- struct page *page = virt_to_page(cpu_addr); -- int ret; -- -- ret = sg_alloc_table(sgt, 1, GFP_KERNEL); -- if (unlikely(ret)) -- return ret; -- -- sg_set_page(sgt->sgl, page, PAGE_ALIGN(size), 0); -- return 0; --} --EXPORT_SYMBOL_GPL(dma_common_get_sgtable); --#endif /* RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,0) */ diff --git a/src/patches/backports-3.18.1-1_rt2x00usb_suppress_queue_warnings.patch b/src/patches/backports-3.18.1-1_rt2x00usb_suppress_queue_warnings.patch deleted file mode 100644 index 3f9308e38f..0000000000 --- a/src/patches/backports-3.18.1-1_rt2x00usb_suppress_queue_warnings.patch +++ /dev/null @@ -1,42 +0,0 @@ -diff -Naur backports-3.18.1-1.org/drivers/net/wireless/rt2x00/rt2800usb.c backports-3.18.1-1/drivers/net/wireless/rt2x00/rt2800usb.c ---- backports-3.18.1-1.org/drivers/net/wireless/rt2x00/rt2800usb.c 2014-12-21 22:37:14.000000000 +0100 -+++ backports-3.18.1-1/drivers/net/wireless/rt2x00/rt2800usb.c 2015-04-07 11:44:16.647963570 +0200 -@@ -444,7 +444,7 @@ - - rt2x00usb_register_read(rt2x00dev, TXRXQ_PCNT, ®); - if (rt2x00_get_field32(reg, TXRXQ_PCNT_TX0Q)) { -- rt2x00_warn(rt2x00dev, "TX HW queue 0 timed out, invoke forced kick\n"); -+ rt2x00_dbg(rt2x00dev, "TX HW queue 0 timed out, invoke forced kick\n"); - - rt2x00usb_register_write(rt2x00dev, PBF_CFG, 0xf40012); - -@@ -459,7 +459,7 @@ - - rt2x00usb_register_read(rt2x00dev, TXRXQ_PCNT, ®); - if (rt2x00_get_field32(reg, TXRXQ_PCNT_TX1Q)) { -- rt2x00_warn(rt2x00dev, "TX HW queue 1 timed out, invoke forced kick\n"); -+ rt2x00_dbg(rt2x00dev, "TX HW queue 1 timed out, invoke forced kick\n"); - - rt2x00usb_register_write(rt2x00dev, PBF_CFG, 0xf4000a); - -@@ -609,7 +609,7 @@ - - if (unlikely(test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || - !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags))) { -- rt2x00_warn(rt2x00dev, "Data pending for entry %u in queue %u\n", -+ rt2x00_dbg(rt2x00dev, "Data pending for entry %u in queue %u\n", - entry->entry_idx, qid); - break; - } -diff -Naur backports-3.18.1-1.org/drivers/net/wireless/rt2x00/rt2x00usb.c backports-3.18.1-1/drivers/net/wireless/rt2x00/rt2x00usb.c ---- backports-3.18.1-1.org/drivers/net/wireless/rt2x00/rt2x00usb.c 2014-12-21 22:37:14.000000000 +0100 -+++ backports-3.18.1-1/drivers/net/wireless/rt2x00/rt2x00usb.c 2015-04-07 11:42:41.723492892 +0200 -@@ -524,7 +524,7 @@ - - static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue) - { -- rt2x00_warn(queue->rt2x00dev, "TX queue %d DMA timed out, invoke forced forced reset\n", -+ rt2x00_dbg(queue->rt2x00dev, "TX queue %d DMA timed out, invoke forced reset\n", - queue->qid); - - rt2x00queue_stop_queue(queue); diff --git a/src/patches/backports-4.2.6-1-add_usbnet_modules.patch b/src/patches/backports-4.2.6-1-add_usbnet_modules.patch deleted file mode 100644 index 7ee228d6d6..0000000000 --- a/src/patches/backports-4.2.6-1-add_usbnet_modules.patch +++ /dev/null @@ -1,29007 +0,0 @@ -diff -Naur backports-4.2.6-1.org/drivers/net/usb/asix_common.c backports-4.2.6-1/drivers/net/usb/asix_common.c ---- backports-4.2.6-1.org/drivers/net/usb/asix_common.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/asix_common.c 2016-06-28 14:35:17.965307221 +0200 -@@ -0,0 +1,584 @@ -+/* -+ * ASIX AX8817X based USB 2.0 Ethernet Devices -+ * Copyright (C) 2003-2006 David Hollis -+ * Copyright (C) 2005 Phil Chang -+ * Copyright (C) 2006 James Painter -+ * Copyright (c) 2002-2003 TiVo Inc. -+ * -+ * 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, see . -+ */ -+ -+#include "asix.h" -+ -+int asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, -+ u16 size, void *data) -+{ -+ int ret; -+ ret = usbnet_read_cmd(dev, cmd, -+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, -+ value, index, data, size); -+ -+ if (ret != size && ret >= 0) -+ return -EINVAL; -+ return ret; -+} -+ -+int asix_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, -+ u16 size, void *data) -+{ -+ return usbnet_write_cmd(dev, cmd, -+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, -+ value, index, data, size); -+} -+ -+void asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index, -+ u16 size, void *data) -+{ -+ usbnet_write_cmd_async(dev, cmd, -+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, -+ value, index, data, size); -+} -+ -+int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb, -+ struct asix_rx_fixup_info *rx) -+{ -+ int offset = 0; -+ -+ while (offset + sizeof(u16) <= skb->len) { -+ u16 remaining = 0; -+ unsigned char *data; -+ -+ if (!rx->size) { -+ if ((skb->len - offset == sizeof(u16)) || -+ rx->split_head) { -+ if(!rx->split_head) { -+ rx->header = get_unaligned_le16( -+ skb->data + offset); -+ rx->split_head = true; -+ offset += sizeof(u16); -+ break; -+ } else { -+ rx->header |= (get_unaligned_le16( -+ skb->data + offset) -+ << 16); -+ rx->split_head = false; -+ offset += sizeof(u16); -+ } -+ } else { -+ rx->header = get_unaligned_le32(skb->data + -+ offset); -+ offset += sizeof(u32); -+ } -+ -+ /* get the packet length */ -+ rx->size = (u16) (rx->header & 0x7ff); -+ if (rx->size != ((~rx->header >> 16) & 0x7ff)) { -+ netdev_err(dev->net, "asix_rx_fixup() Bad Header Length 0x%x, offset %d\n", -+ rx->header, offset); -+ rx->size = 0; -+ return 0; -+ } -+ rx->ax_skb = netdev_alloc_skb_ip_align(dev->net, -+ rx->size); -+ if (!rx->ax_skb) -+ return 0; -+ } -+ -+ if (rx->size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) { -+ netdev_err(dev->net, "asix_rx_fixup() Bad RX Length %d\n", -+ rx->size); -+ kfree_skb(rx->ax_skb); -+ rx->ax_skb = NULL; -+ rx->size = 0U; -+ -+ return 0; -+ } -+ -+ if (rx->size > skb->len - offset) { -+ remaining = rx->size - (skb->len - offset); -+ rx->size = skb->len - offset; -+ } -+ -+ data = skb_put(rx->ax_skb, rx->size); -+ memcpy(data, skb->data + offset, rx->size); -+ if (!remaining) -+ usbnet_skb_return(dev, rx->ax_skb); -+ -+ offset += (rx->size + 1) & 0xfffe; -+ rx->size = remaining; -+ } -+ -+ if (skb->len != offset) { -+ netdev_err(dev->net, "asix_rx_fixup() Bad SKB Length %d, %d\n", -+ skb->len, offset); -+ return 0; -+ } -+ -+ return 1; -+} -+ -+int asix_rx_fixup_common(struct usbnet *dev, struct sk_buff *skb) -+{ -+ struct asix_common_private *dp = dev->driver_priv; -+ struct asix_rx_fixup_info *rx = &dp->rx_fixup_info; -+ -+ return asix_rx_fixup_internal(dev, skb, rx); -+} -+ -+struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, -+ gfp_t flags) -+{ -+ int padlen; -+ int headroom = skb_headroom(skb); -+ int tailroom = skb_tailroom(skb); -+ u32 packet_len; -+ u32 padbytes = 0xffff0000; -+ -+ padlen = ((skb->len + 4) & (dev->maxpacket - 1)) ? 0 : 4; -+ -+ /* We need to push 4 bytes in front of frame (packet_len) -+ * and maybe add 4 bytes after the end (if padlen is 4) -+ * -+ * Avoid skb_copy_expand() expensive call, using following rules : -+ * - We are allowed to push 4 bytes in headroom if skb_header_cloned() -+ * is false (and if we have 4 bytes of headroom) -+ * - We are allowed to put 4 bytes at tail if skb_cloned() -+ * is false (and if we have 4 bytes of tailroom) -+ * -+ * TCP packets for example are cloned, but skb_header_release() -+ * was called in tcp stack, allowing us to use headroom for our needs. -+ */ -+ if (!skb_header_cloned(skb) && -+ !(padlen && skb_cloned(skb)) && -+ headroom + tailroom >= 4 + padlen) { -+ /* following should not happen, but better be safe */ -+ if (headroom < 4 || -+ tailroom < padlen) { -+ skb->data = memmove(skb->head + 4, skb->data, skb->len); -+ skb_set_tail_pointer(skb, skb->len); -+ } -+ } else { -+ struct sk_buff *skb2; -+ -+ skb2 = skb_copy_expand(skb, 4, padlen, flags); -+ dev_kfree_skb_any(skb); -+ skb = skb2; -+ if (!skb) -+ return NULL; -+ } -+ -+ packet_len = ((skb->len ^ 0x0000ffff) << 16) + skb->len; -+ skb_push(skb, 4); -+ cpu_to_le32s(&packet_len); -+ skb_copy_to_linear_data(skb, &packet_len, sizeof(packet_len)); -+ -+ if (padlen) { -+ cpu_to_le32s(&padbytes); -+ memcpy(skb_tail_pointer(skb), &padbytes, sizeof(padbytes)); -+ skb_put(skb, sizeof(padbytes)); -+ } -+ -+ usbnet_set_skb_tx_stats(skb, 1, 0); -+ return skb; -+} -+ -+int asix_set_sw_mii(struct usbnet *dev) -+{ -+ int ret; -+ ret = asix_write_cmd(dev, AX_CMD_SET_SW_MII, 0x0000, 0, 0, NULL); -+ if (ret < 0) -+ netdev_err(dev->net, "Failed to enable software MII access\n"); -+ return ret; -+} -+ -+int asix_set_hw_mii(struct usbnet *dev) -+{ -+ int ret; -+ ret = asix_write_cmd(dev, AX_CMD_SET_HW_MII, 0x0000, 0, 0, NULL); -+ if (ret < 0) -+ netdev_err(dev->net, "Failed to enable hardware MII access\n"); -+ return ret; -+} -+ -+int asix_read_phy_addr(struct usbnet *dev, int internal) -+{ -+ int offset = (internal ? 1 : 0); -+ u8 buf[2]; -+ int ret = asix_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf); -+ -+ netdev_dbg(dev->net, "asix_get_phy_addr()\n"); -+ -+ if (ret < 0) { -+ netdev_err(dev->net, "Error reading PHYID register: %02x\n", ret); -+ goto out; -+ } -+ netdev_dbg(dev->net, "asix_get_phy_addr() returning 0x%04x\n", -+ *((__le16 *)buf)); -+ ret = buf[offset]; -+ -+out: -+ return ret; -+} -+ -+int asix_get_phy_addr(struct usbnet *dev) -+{ -+ /* return the address of the internal phy */ -+ return asix_read_phy_addr(dev, 1); -+} -+ -+ -+int asix_sw_reset(struct usbnet *dev, u8 flags) -+{ -+ int ret; -+ -+ ret = asix_write_cmd(dev, AX_CMD_SW_RESET, flags, 0, 0, NULL); -+ if (ret < 0) -+ netdev_err(dev->net, "Failed to send software reset: %02x\n", ret); -+ -+ return ret; -+} -+ -+u16 asix_read_rx_ctl(struct usbnet *dev) -+{ -+ __le16 v; -+ int ret = asix_read_cmd(dev, AX_CMD_READ_RX_CTL, 0, 0, 2, &v); -+ -+ if (ret < 0) { -+ netdev_err(dev->net, "Error reading RX_CTL register: %02x\n", ret); -+ goto out; -+ } -+ ret = le16_to_cpu(v); -+out: -+ return ret; -+} -+ -+int asix_write_rx_ctl(struct usbnet *dev, u16 mode) -+{ -+ int ret; -+ -+ netdev_dbg(dev->net, "asix_write_rx_ctl() - mode = 0x%04x\n", mode); -+ ret = asix_write_cmd(dev, AX_CMD_WRITE_RX_CTL, mode, 0, 0, NULL); -+ if (ret < 0) -+ netdev_err(dev->net, "Failed to write RX_CTL mode to 0x%04x: %02x\n", -+ mode, ret); -+ -+ return ret; -+} -+ -+u16 asix_read_medium_status(struct usbnet *dev) -+{ -+ __le16 v; -+ int ret = asix_read_cmd(dev, AX_CMD_READ_MEDIUM_STATUS, 0, 0, 2, &v); -+ -+ if (ret < 0) { -+ netdev_err(dev->net, "Error reading Medium Status register: %02x\n", -+ ret); -+ return ret; /* TODO: callers not checking for error ret */ -+ } -+ -+ return le16_to_cpu(v); -+ -+} -+ -+int asix_write_medium_mode(struct usbnet *dev, u16 mode) -+{ -+ int ret; -+ -+ netdev_dbg(dev->net, "asix_write_medium_mode() - mode = 0x%04x\n", mode); -+ ret = asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL); -+ if (ret < 0) -+ netdev_err(dev->net, "Failed to write Medium Mode mode to 0x%04x: %02x\n", -+ mode, ret); -+ -+ return ret; -+} -+ -+int asix_write_gpio(struct usbnet *dev, u16 value, int sleep) -+{ -+ int ret; -+ -+ netdev_dbg(dev->net, "asix_write_gpio() - value = 0x%04x\n", value); -+ ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS, value, 0, 0, NULL); -+ if (ret < 0) -+ netdev_err(dev->net, "Failed to write GPIO value 0x%04x: %02x\n", -+ value, ret); -+ -+ if (sleep) -+ msleep(sleep); -+ -+ return ret; -+} -+ -+/* -+ * AX88772 & AX88178 have a 16-bit RX_CTL value -+ */ -+void asix_set_multicast(struct net_device *net) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ struct asix_data *data = (struct asix_data *)&dev->data; -+ u16 rx_ctl = AX_DEFAULT_RX_CTL; -+ -+ if (net->flags & IFF_PROMISC) { -+ rx_ctl |= AX_RX_CTL_PRO; -+ } else if (net->flags & IFF_ALLMULTI || -+ netdev_mc_count(net) > AX_MAX_MCAST) { -+ rx_ctl |= AX_RX_CTL_AMALL; -+ } else if (netdev_mc_empty(net)) { -+ /* just broadcast and directed */ -+ } else { -+ /* We use the 20 byte dev->data -+ * for our 8 byte filter buffer -+ * to avoid allocating memory that -+ * is tricky to free later */ -+ struct netdev_hw_addr *ha; -+ u32 crc_bits; -+ -+ memset(data->multi_filter, 0, AX_MCAST_FILTER_SIZE); -+ -+ /* Build the multicast hash filter. */ -+ netdev_for_each_mc_addr(ha, net) { -+ crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26; -+ data->multi_filter[crc_bits >> 3] |= -+ 1 << (crc_bits & 7); -+ } -+ -+ asix_write_cmd_async(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0, -+ AX_MCAST_FILTER_SIZE, data->multi_filter); -+ -+ rx_ctl |= AX_RX_CTL_AM; -+ } -+ -+ asix_write_cmd_async(dev, AX_CMD_WRITE_RX_CTL, rx_ctl, 0, 0, NULL); -+} -+ -+int asix_mdio_read(struct net_device *netdev, int phy_id, int loc) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ __le16 res; -+ -+ mutex_lock(&dev->phy_mutex); -+ asix_set_sw_mii(dev); -+ asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id, -+ (__u16)loc, 2, &res); -+ asix_set_hw_mii(dev); -+ mutex_unlock(&dev->phy_mutex); -+ -+ netdev_dbg(dev->net, "asix_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n", -+ phy_id, loc, le16_to_cpu(res)); -+ -+ return le16_to_cpu(res); -+} -+ -+void asix_mdio_write(struct net_device *netdev, int phy_id, int loc, int val) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ __le16 res = cpu_to_le16(val); -+ -+ netdev_dbg(dev->net, "asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n", -+ phy_id, loc, val); -+ mutex_lock(&dev->phy_mutex); -+ asix_set_sw_mii(dev); -+ asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, (__u16)loc, 2, &res); -+ asix_set_hw_mii(dev); -+ mutex_unlock(&dev->phy_mutex); -+} -+ -+void asix_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ u8 opt; -+ -+ if (asix_read_cmd(dev, AX_CMD_READ_MONITOR_MODE, 0, 0, 1, &opt) < 0) { -+ wolinfo->supported = 0; -+ wolinfo->wolopts = 0; -+ return; -+ } -+ wolinfo->supported = WAKE_PHY | WAKE_MAGIC; -+ wolinfo->wolopts = 0; -+ if (opt & AX_MONITOR_LINK) -+ wolinfo->wolopts |= WAKE_PHY; -+ if (opt & AX_MONITOR_MAGIC) -+ wolinfo->wolopts |= WAKE_MAGIC; -+} -+ -+int asix_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ u8 opt = 0; -+ -+ if (wolinfo->wolopts & WAKE_PHY) -+ opt |= AX_MONITOR_LINK; -+ if (wolinfo->wolopts & WAKE_MAGIC) -+ opt |= AX_MONITOR_MAGIC; -+ -+ if (asix_write_cmd(dev, AX_CMD_WRITE_MONITOR_MODE, -+ opt, 0, 0, NULL) < 0) -+ return -EINVAL; -+ -+ return 0; -+} -+ -+int asix_get_eeprom_len(struct net_device *net) -+{ -+ return AX_EEPROM_LEN; -+} -+ -+int asix_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, -+ u8 *data) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ u16 *eeprom_buff; -+ int first_word, last_word; -+ int i; -+ -+ if (eeprom->len == 0) -+ return -EINVAL; -+ -+ eeprom->magic = AX_EEPROM_MAGIC; -+ -+ first_word = eeprom->offset >> 1; -+ last_word = (eeprom->offset + eeprom->len - 1) >> 1; -+ -+ eeprom_buff = kmalloc(sizeof(u16) * (last_word - first_word + 1), -+ GFP_KERNEL); -+ if (!eeprom_buff) -+ return -ENOMEM; -+ -+ /* ax8817x returns 2 bytes from eeprom on read */ -+ for (i = first_word; i <= last_word; i++) { -+ if (asix_read_cmd(dev, AX_CMD_READ_EEPROM, i, 0, 2, -+ &(eeprom_buff[i - first_word])) < 0) { -+ kfree(eeprom_buff); -+ return -EIO; -+ } -+ } -+ -+ memcpy(data, (u8 *)eeprom_buff + (eeprom->offset & 1), eeprom->len); -+ kfree(eeprom_buff); -+ return 0; -+} -+ -+int asix_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, -+ u8 *data) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ u16 *eeprom_buff; -+ int first_word, last_word; -+ int i; -+ int ret; -+ -+ netdev_dbg(net, "write EEPROM len %d, offset %d, magic 0x%x\n", -+ eeprom->len, eeprom->offset, eeprom->magic); -+ -+ if (eeprom->len == 0) -+ return -EINVAL; -+ -+ if (eeprom->magic != AX_EEPROM_MAGIC) -+ return -EINVAL; -+ -+ first_word = eeprom->offset >> 1; -+ last_word = (eeprom->offset + eeprom->len - 1) >> 1; -+ -+ eeprom_buff = kmalloc(sizeof(u16) * (last_word - first_word + 1), -+ GFP_KERNEL); -+ if (!eeprom_buff) -+ return -ENOMEM; -+ -+ /* align data to 16 bit boundaries, read the missing data from -+ the EEPROM */ -+ if (eeprom->offset & 1) { -+ ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, first_word, 0, 2, -+ &(eeprom_buff[0])); -+ if (ret < 0) { -+ netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", first_word); -+ goto free; -+ } -+ } -+ -+ if ((eeprom->offset + eeprom->len) & 1) { -+ ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, last_word, 0, 2, -+ &(eeprom_buff[last_word - first_word])); -+ if (ret < 0) { -+ netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", last_word); -+ goto free; -+ } -+ } -+ -+ memcpy((u8 *)eeprom_buff + (eeprom->offset & 1), data, eeprom->len); -+ -+ /* write data to EEPROM */ -+ ret = asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0x0000, 0, 0, NULL); -+ if (ret < 0) { -+ netdev_err(net, "Failed to enable EEPROM write\n"); -+ goto free; -+ } -+ msleep(20); -+ -+ for (i = first_word; i <= last_word; i++) { -+ netdev_dbg(net, "write to EEPROM at offset 0x%02x, data 0x%04x\n", -+ i, eeprom_buff[i - first_word]); -+ ret = asix_write_cmd(dev, AX_CMD_WRITE_EEPROM, i, -+ eeprom_buff[i - first_word], 0, NULL); -+ if (ret < 0) { -+ netdev_err(net, "Failed to write EEPROM at offset 0x%02x.\n", -+ i); -+ goto free; -+ } -+ msleep(20); -+ } -+ -+ ret = asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0x0000, 0, 0, NULL); -+ if (ret < 0) { -+ netdev_err(net, "Failed to disable EEPROM write\n"); -+ goto free; -+ } -+ -+ ret = 0; -+free: -+ kfree(eeprom_buff); -+ return ret; -+} -+ -+void asix_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) -+{ -+ /* Inherit standard device info */ -+ usbnet_get_drvinfo(net, info); -+ strlcpy(info->driver, DRIVER_NAME, sizeof(info->driver)); -+ strlcpy(info->version, DRIVER_VERSION, sizeof(info->version)); -+ info->eedump_len = AX_EEPROM_LEN; -+} -+ -+int asix_set_mac_address(struct net_device *net, void *p) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ struct asix_data *data = (struct asix_data *)&dev->data; -+ struct sockaddr *addr = p; -+ -+ if (netif_running(net)) -+ return -EBUSY; -+ if (!is_valid_ether_addr(addr->sa_data)) -+ return -EADDRNOTAVAIL; -+ -+ memcpy(net->dev_addr, addr->sa_data, ETH_ALEN); -+ -+ /* We use the 20 byte dev->data -+ * for our 6 byte mac buffer -+ * to avoid allocating memory that -+ * is tricky to free later */ -+ memcpy(data->mac_addr, addr->sa_data, ETH_ALEN); -+ asix_write_cmd_async(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN, -+ data->mac_addr); -+ -+ return 0; -+} -diff -Naur backports-4.2.6-1.org/drivers/net/usb/asix_devices.c backports-4.2.6-1/drivers/net/usb/asix_devices.c ---- backports-4.2.6-1.org/drivers/net/usb/asix_devices.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/asix_devices.c 2016-06-28 14:35:17.965307221 +0200 -@@ -0,0 +1,1107 @@ -+/* -+ * ASIX AX8817X based USB 2.0 Ethernet Devices -+ * Copyright (C) 2003-2006 David Hollis -+ * Copyright (C) 2005 Phil Chang -+ * Copyright (C) 2006 James Painter -+ * Copyright (c) 2002-2003 TiVo Inc. -+ * -+ * 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, see . -+ */ -+ -+#include "asix.h" -+ -+#define PHY_MODE_MARVELL 0x0000 -+#define MII_MARVELL_LED_CTRL 0x0018 -+#define MII_MARVELL_STATUS 0x001b -+#define MII_MARVELL_CTRL 0x0014 -+ -+#define MARVELL_LED_MANUAL 0x0019 -+ -+#define MARVELL_STATUS_HWCFG 0x0004 -+ -+#define MARVELL_CTRL_TXDELAY 0x0002 -+#define MARVELL_CTRL_RXDELAY 0x0080 -+ -+#define PHY_MODE_RTL8211CL 0x000C -+ -+struct ax88172_int_data { -+ __le16 res1; -+ u8 link; -+ __le16 res2; -+ u8 status; -+ __le16 res3; -+} __packed; -+ -+static void asix_status(struct usbnet *dev, struct urb *urb) -+{ -+ struct ax88172_int_data *event; -+ int link; -+ -+ if (urb->actual_length < 8) -+ return; -+ -+ event = urb->transfer_buffer; -+ link = event->link & 0x01; -+ if (netif_carrier_ok(dev->net) != link) { -+ usbnet_link_change(dev, link, 1); -+ netdev_dbg(dev->net, "Link Status is: %d\n", link); -+ } -+} -+ -+static void asix_set_netdev_dev_addr(struct usbnet *dev, u8 *addr) -+{ -+ if (is_valid_ether_addr(addr)) { -+ memcpy(dev->net->dev_addr, addr, ETH_ALEN); -+ } else { -+ netdev_info(dev->net, "invalid hw address, using random\n"); -+ eth_hw_addr_random(dev->net); -+ } -+} -+ -+/* Get the PHY Identifier from the PHYSID1 & PHYSID2 MII registers */ -+static u32 asix_get_phyid(struct usbnet *dev) -+{ -+ int phy_reg; -+ u32 phy_id; -+ int i; -+ -+ /* Poll for the rare case the FW or phy isn't ready yet. */ -+ for (i = 0; i < 100; i++) { -+ phy_reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_PHYSID1); -+ if (phy_reg != 0 && phy_reg != 0xFFFF) -+ break; -+ mdelay(1); -+ } -+ -+ if (phy_reg <= 0 || phy_reg == 0xFFFF) -+ return 0; -+ -+ phy_id = (phy_reg & 0xffff) << 16; -+ -+ phy_reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_PHYSID2); -+ if (phy_reg < 0) -+ return 0; -+ -+ phy_id |= (phy_reg & 0xffff); -+ -+ return phy_id; -+} -+ -+static u32 asix_get_link(struct net_device *net) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ -+ return mii_link_ok(&dev->mii); -+} -+ -+static int asix_ioctl (struct net_device *net, struct ifreq *rq, int cmd) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ -+ return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); -+} -+ -+/* We need to override some ethtool_ops so we require our -+ own structure so we don't interfere with other usbnet -+ devices that may be connected at the same time. */ -+static const struct ethtool_ops ax88172_ethtool_ops = { -+ .get_drvinfo = asix_get_drvinfo, -+ .get_link = asix_get_link, -+ .get_msglevel = usbnet_get_msglevel, -+ .set_msglevel = usbnet_set_msglevel, -+ .get_wol = asix_get_wol, -+ .set_wol = asix_set_wol, -+ .get_eeprom_len = asix_get_eeprom_len, -+ .get_eeprom = asix_get_eeprom, -+ .set_eeprom = asix_set_eeprom, -+ .get_settings = usbnet_get_settings, -+ .set_settings = usbnet_set_settings, -+ .nway_reset = usbnet_nway_reset, -+}; -+ -+static void ax88172_set_multicast(struct net_device *net) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ struct asix_data *data = (struct asix_data *)&dev->data; -+ u8 rx_ctl = 0x8c; -+ -+ if (net->flags & IFF_PROMISC) { -+ rx_ctl |= 0x01; -+ } else if (net->flags & IFF_ALLMULTI || -+ netdev_mc_count(net) > AX_MAX_MCAST) { -+ rx_ctl |= 0x02; -+ } else if (netdev_mc_empty(net)) { -+ /* just broadcast and directed */ -+ } else { -+ /* We use the 20 byte dev->data -+ * for our 8 byte filter buffer -+ * to avoid allocating memory that -+ * is tricky to free later */ -+ struct netdev_hw_addr *ha; -+ u32 crc_bits; -+ -+ memset(data->multi_filter, 0, AX_MCAST_FILTER_SIZE); -+ -+ /* Build the multicast hash filter. */ -+ netdev_for_each_mc_addr(ha, net) { -+ crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26; -+ data->multi_filter[crc_bits >> 3] |= -+ 1 << (crc_bits & 7); -+ } -+ -+ asix_write_cmd_async(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0, -+ AX_MCAST_FILTER_SIZE, data->multi_filter); -+ -+ rx_ctl |= 0x10; -+ } -+ -+ asix_write_cmd_async(dev, AX_CMD_WRITE_RX_CTL, rx_ctl, 0, 0, NULL); -+} -+ -+static int ax88172_link_reset(struct usbnet *dev) -+{ -+ u8 mode; -+ struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET }; -+ -+ mii_check_media(&dev->mii, 1, 1); -+ mii_ethtool_gset(&dev->mii, &ecmd); -+ mode = AX88172_MEDIUM_DEFAULT; -+ -+ if (ecmd.duplex != DUPLEX_FULL) -+ mode |= ~AX88172_MEDIUM_FD; -+ -+ netdev_dbg(dev->net, "ax88172_link_reset() speed: %u duplex: %d setting mode to 0x%04x\n", -+ ethtool_cmd_speed(&ecmd), ecmd.duplex, mode); -+ -+ asix_write_medium_mode(dev, mode); -+ -+ return 0; -+} -+ -+static const struct net_device_ops ax88172_netdev_ops = { -+ .ndo_open = usbnet_open, -+ .ndo_stop = usbnet_stop, -+ .ndo_start_xmit = usbnet_start_xmit, -+ .ndo_tx_timeout = usbnet_tx_timeout, -+ .ndo_change_mtu = usbnet_change_mtu, -+ .ndo_set_mac_address = eth_mac_addr, -+ .ndo_validate_addr = eth_validate_addr, -+ .ndo_do_ioctl = asix_ioctl, -+ .ndo_set_rx_mode = ax88172_set_multicast, -+}; -+ -+static int ax88172_bind(struct usbnet *dev, struct usb_interface *intf) -+{ -+ int ret = 0; -+ u8 buf[ETH_ALEN]; -+ int i; -+ unsigned long gpio_bits = dev->driver_info->data; -+ -+ usbnet_get_endpoints(dev,intf); -+ -+ /* Toggle the GPIOs in a manufacturer/model specific way */ -+ for (i = 2; i >= 0; i--) { -+ ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS, -+ (gpio_bits >> (i * 8)) & 0xff, 0, 0, NULL); -+ if (ret < 0) -+ goto out; -+ msleep(5); -+ } -+ -+ ret = asix_write_rx_ctl(dev, 0x80); -+ if (ret < 0) -+ goto out; -+ -+ /* Get the MAC address */ -+ ret = asix_read_cmd(dev, AX88172_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf); -+ if (ret < 0) { -+ netdev_dbg(dev->net, "read AX_CMD_READ_NODE_ID failed: %d\n", -+ ret); -+ goto out; -+ } -+ -+ asix_set_netdev_dev_addr(dev, buf); -+ -+ /* Initialize MII structure */ -+ dev->mii.dev = dev->net; -+ dev->mii.mdio_read = asix_mdio_read; -+ dev->mii.mdio_write = asix_mdio_write; -+ dev->mii.phy_id_mask = 0x3f; -+ dev->mii.reg_num_mask = 0x1f; -+ dev->mii.phy_id = asix_get_phy_addr(dev); -+ -+ dev->net->netdev_ops = &ax88172_netdev_ops; -+ dev->net->ethtool_ops = &ax88172_ethtool_ops; -+ dev->net->needed_headroom = 4; /* cf asix_tx_fixup() */ -+ dev->net->needed_tailroom = 4; /* cf asix_tx_fixup() */ -+ -+ asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET); -+ asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, -+ ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP); -+ mii_nway_restart(&dev->mii); -+ -+ return 0; -+ -+out: -+ return ret; -+} -+ -+static const struct ethtool_ops ax88772_ethtool_ops = { -+ .get_drvinfo = asix_get_drvinfo, -+ .get_link = asix_get_link, -+ .get_msglevel = usbnet_get_msglevel, -+ .set_msglevel = usbnet_set_msglevel, -+ .get_wol = asix_get_wol, -+ .set_wol = asix_set_wol, -+ .get_eeprom_len = asix_get_eeprom_len, -+ .get_eeprom = asix_get_eeprom, -+ .set_eeprom = asix_set_eeprom, -+ .get_settings = usbnet_get_settings, -+ .set_settings = usbnet_set_settings, -+ .nway_reset = usbnet_nway_reset, -+}; -+ -+static int ax88772_link_reset(struct usbnet *dev) -+{ -+ u16 mode; -+ struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET }; -+ -+ mii_check_media(&dev->mii, 1, 1); -+ mii_ethtool_gset(&dev->mii, &ecmd); -+ mode = AX88772_MEDIUM_DEFAULT; -+ -+ if (ethtool_cmd_speed(&ecmd) != SPEED_100) -+ mode &= ~AX_MEDIUM_PS; -+ -+ if (ecmd.duplex != DUPLEX_FULL) -+ mode &= ~AX_MEDIUM_FD; -+ -+ netdev_dbg(dev->net, "ax88772_link_reset() speed: %u duplex: %d setting mode to 0x%04x\n", -+ ethtool_cmd_speed(&ecmd), ecmd.duplex, mode); -+ -+ asix_write_medium_mode(dev, mode); -+ -+ return 0; -+} -+ -+static int ax88772_reset(struct usbnet *dev) -+{ -+ struct asix_data *data = (struct asix_data *)&dev->data; -+ int ret, embd_phy; -+ u16 rx_ctl; -+ -+ ret = asix_write_gpio(dev, -+ AX_GPIO_RSE | AX_GPIO_GPO_2 | AX_GPIO_GPO2EN, 5); -+ if (ret < 0) -+ goto out; -+ -+ embd_phy = ((asix_get_phy_addr(dev) & 0x1f) == 0x10 ? 1 : 0); -+ -+ ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, embd_phy, 0, 0, NULL); -+ if (ret < 0) { -+ netdev_dbg(dev->net, "Select PHY #1 failed: %d\n", ret); -+ goto out; -+ } -+ -+ ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_PRL); -+ if (ret < 0) -+ goto out; -+ -+ msleep(150); -+ -+ ret = asix_sw_reset(dev, AX_SWRESET_CLEAR); -+ if (ret < 0) -+ goto out; -+ -+ msleep(150); -+ -+ if (embd_phy) { -+ ret = asix_sw_reset(dev, AX_SWRESET_IPRL); -+ if (ret < 0) -+ goto out; -+ } else { -+ ret = asix_sw_reset(dev, AX_SWRESET_PRTE); -+ if (ret < 0) -+ goto out; -+ } -+ -+ msleep(150); -+ rx_ctl = asix_read_rx_ctl(dev); -+ netdev_dbg(dev->net, "RX_CTL is 0x%04x after software reset\n", rx_ctl); -+ ret = asix_write_rx_ctl(dev, 0x0000); -+ if (ret < 0) -+ goto out; -+ -+ rx_ctl = asix_read_rx_ctl(dev); -+ netdev_dbg(dev->net, "RX_CTL is 0x%04x setting to 0x0000\n", rx_ctl); -+ -+ ret = asix_sw_reset(dev, AX_SWRESET_PRL); -+ if (ret < 0) -+ goto out; -+ -+ msleep(150); -+ -+ ret = asix_sw_reset(dev, AX_SWRESET_IPRL | AX_SWRESET_PRL); -+ if (ret < 0) -+ goto out; -+ -+ msleep(150); -+ -+ asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET); -+ asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, -+ ADVERTISE_ALL | ADVERTISE_CSMA); -+ mii_nway_restart(&dev->mii); -+ -+ ret = asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT); -+ if (ret < 0) -+ goto out; -+ -+ ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0, -+ AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT, -+ AX88772_IPG2_DEFAULT, 0, NULL); -+ if (ret < 0) { -+ netdev_dbg(dev->net, "Write IPG,IPG1,IPG2 failed: %d\n", ret); -+ goto out; -+ } -+ -+ /* Rewrite MAC address */ -+ memcpy(data->mac_addr, dev->net->dev_addr, ETH_ALEN); -+ ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN, -+ data->mac_addr); -+ if (ret < 0) -+ goto out; -+ -+ /* Set RX_CTL to default values with 2k buffer, and enable cactus */ -+ ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL); -+ if (ret < 0) -+ goto out; -+ -+ rx_ctl = asix_read_rx_ctl(dev); -+ netdev_dbg(dev->net, "RX_CTL is 0x%04x after all initializations\n", -+ rx_ctl); -+ -+ rx_ctl = asix_read_medium_status(dev); -+ netdev_dbg(dev->net, -+ "Medium Status is 0x%04x after all initializations\n", -+ rx_ctl); -+ -+ return 0; -+ -+out: -+ return ret; -+ -+} -+ -+static const struct net_device_ops ax88772_netdev_ops = { -+ .ndo_open = usbnet_open, -+ .ndo_stop = usbnet_stop, -+ .ndo_start_xmit = usbnet_start_xmit, -+ .ndo_tx_timeout = usbnet_tx_timeout, -+ .ndo_change_mtu = usbnet_change_mtu, -+ .ndo_set_mac_address = asix_set_mac_address, -+ .ndo_validate_addr = eth_validate_addr, -+ .ndo_do_ioctl = asix_ioctl, -+ .ndo_set_rx_mode = asix_set_multicast, -+}; -+ -+static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf) -+{ -+ int ret, embd_phy, i; -+ u8 buf[ETH_ALEN]; -+ u32 phyid; -+ -+ usbnet_get_endpoints(dev,intf); -+ -+ /* Get the MAC address */ -+ if (dev->driver_info->data & FLAG_EEPROM_MAC) { -+ for (i = 0; i < (ETH_ALEN >> 1); i++) { -+ ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, 0x04 + i, -+ 0, 2, buf + i * 2); -+ if (ret < 0) -+ break; -+ } -+ } else { -+ ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, -+ 0, 0, ETH_ALEN, buf); -+ } -+ -+ if (ret < 0) { -+ netdev_dbg(dev->net, "Failed to read MAC address: %d\n", ret); -+ return ret; -+ } -+ -+ asix_set_netdev_dev_addr(dev, buf); -+ -+ /* Initialize MII structure */ -+ dev->mii.dev = dev->net; -+ dev->mii.mdio_read = asix_mdio_read; -+ dev->mii.mdio_write = asix_mdio_write; -+ dev->mii.phy_id_mask = 0x1f; -+ dev->mii.reg_num_mask = 0x1f; -+ dev->mii.phy_id = asix_get_phy_addr(dev); -+ -+ dev->net->netdev_ops = &ax88772_netdev_ops; -+ dev->net->ethtool_ops = &ax88772_ethtool_ops; -+ dev->net->needed_headroom = 4; /* cf asix_tx_fixup() */ -+ dev->net->needed_tailroom = 4; /* cf asix_tx_fixup() */ -+ -+ embd_phy = ((dev->mii.phy_id & 0x1f) == 0x10 ? 1 : 0); -+ -+ /* Reset the PHY to normal operation mode */ -+ ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, embd_phy, 0, 0, NULL); -+ if (ret < 0) { -+ netdev_dbg(dev->net, "Select PHY #1 failed: %d\n", ret); -+ return ret; -+ } -+ -+ ax88772_reset(dev); -+ -+ /* Read PHYID register *AFTER* the PHY was reset properly */ -+ phyid = asix_get_phyid(dev); -+ netdev_dbg(dev->net, "PHYID=0x%08x\n", phyid); -+ -+ /* Asix framing packs multiple eth frames into a 2K usb bulk transfer */ -+ if (dev->driver_info->flags & FLAG_FRAMING_AX) { -+ /* hard_mtu is still the default - the device does not support -+ jumbo eth frames */ -+ dev->rx_urb_size = 2048; -+ } -+ -+ dev->driver_priv = kzalloc(sizeof(struct asix_common_private), GFP_KERNEL); -+ if (!dev->driver_priv) -+ return -ENOMEM; -+ -+ return 0; -+} -+ -+static void ax88772_unbind(struct usbnet *dev, struct usb_interface *intf) -+{ -+ kfree(dev->driver_priv); -+} -+ -+static const struct ethtool_ops ax88178_ethtool_ops = { -+ .get_drvinfo = asix_get_drvinfo, -+ .get_link = asix_get_link, -+ .get_msglevel = usbnet_get_msglevel, -+ .set_msglevel = usbnet_set_msglevel, -+ .get_wol = asix_get_wol, -+ .set_wol = asix_set_wol, -+ .get_eeprom_len = asix_get_eeprom_len, -+ .get_eeprom = asix_get_eeprom, -+ .set_eeprom = asix_set_eeprom, -+ .get_settings = usbnet_get_settings, -+ .set_settings = usbnet_set_settings, -+ .nway_reset = usbnet_nway_reset, -+}; -+ -+static int marvell_phy_init(struct usbnet *dev) -+{ -+ struct asix_data *data = (struct asix_data *)&dev->data; -+ u16 reg; -+ -+ netdev_dbg(dev->net, "marvell_phy_init()\n"); -+ -+ reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_MARVELL_STATUS); -+ netdev_dbg(dev->net, "MII_MARVELL_STATUS = 0x%04x\n", reg); -+ -+ asix_mdio_write(dev->net, dev->mii.phy_id, MII_MARVELL_CTRL, -+ MARVELL_CTRL_RXDELAY | MARVELL_CTRL_TXDELAY); -+ -+ if (data->ledmode) { -+ reg = asix_mdio_read(dev->net, dev->mii.phy_id, -+ MII_MARVELL_LED_CTRL); -+ netdev_dbg(dev->net, "MII_MARVELL_LED_CTRL (1) = 0x%04x\n", reg); -+ -+ reg &= 0xf8ff; -+ reg |= (1 + 0x0100); -+ asix_mdio_write(dev->net, dev->mii.phy_id, -+ MII_MARVELL_LED_CTRL, reg); -+ -+ reg = asix_mdio_read(dev->net, dev->mii.phy_id, -+ MII_MARVELL_LED_CTRL); -+ netdev_dbg(dev->net, "MII_MARVELL_LED_CTRL (2) = 0x%04x\n", reg); -+ reg &= 0xfc0f; -+ } -+ -+ return 0; -+} -+ -+static int rtl8211cl_phy_init(struct usbnet *dev) -+{ -+ struct asix_data *data = (struct asix_data *)&dev->data; -+ -+ netdev_dbg(dev->net, "rtl8211cl_phy_init()\n"); -+ -+ asix_mdio_write (dev->net, dev->mii.phy_id, 0x1f, 0x0005); -+ asix_mdio_write (dev->net, dev->mii.phy_id, 0x0c, 0); -+ asix_mdio_write (dev->net, dev->mii.phy_id, 0x01, -+ asix_mdio_read (dev->net, dev->mii.phy_id, 0x01) | 0x0080); -+ asix_mdio_write (dev->net, dev->mii.phy_id, 0x1f, 0); -+ -+ if (data->ledmode == 12) { -+ asix_mdio_write (dev->net, dev->mii.phy_id, 0x1f, 0x0002); -+ asix_mdio_write (dev->net, dev->mii.phy_id, 0x1a, 0x00cb); -+ asix_mdio_write (dev->net, dev->mii.phy_id, 0x1f, 0); -+ } -+ -+ return 0; -+} -+ -+static int marvell_led_status(struct usbnet *dev, u16 speed) -+{ -+ u16 reg = asix_mdio_read(dev->net, dev->mii.phy_id, MARVELL_LED_MANUAL); -+ -+ netdev_dbg(dev->net, "marvell_led_status() read 0x%04x\n", reg); -+ -+ /* Clear out the center LED bits - 0x03F0 */ -+ reg &= 0xfc0f; -+ -+ switch (speed) { -+ case SPEED_1000: -+ reg |= 0x03e0; -+ break; -+ case SPEED_100: -+ reg |= 0x03b0; -+ break; -+ default: -+ reg |= 0x02f0; -+ } -+ -+ netdev_dbg(dev->net, "marvell_led_status() writing 0x%04x\n", reg); -+ asix_mdio_write(dev->net, dev->mii.phy_id, MARVELL_LED_MANUAL, reg); -+ -+ return 0; -+} -+ -+static int ax88178_reset(struct usbnet *dev) -+{ -+ struct asix_data *data = (struct asix_data *)&dev->data; -+ int ret; -+ __le16 eeprom; -+ u8 status; -+ int gpio0 = 0; -+ u32 phyid; -+ -+ asix_read_cmd(dev, AX_CMD_READ_GPIOS, 0, 0, 1, &status); -+ netdev_dbg(dev->net, "GPIO Status: 0x%04x\n", status); -+ -+ asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0, 0, 0, NULL); -+ asix_read_cmd(dev, AX_CMD_READ_EEPROM, 0x0017, 0, 2, &eeprom); -+ asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0, 0, 0, NULL); -+ -+ netdev_dbg(dev->net, "EEPROM index 0x17 is 0x%04x\n", eeprom); -+ -+ if (eeprom == cpu_to_le16(0xffff)) { -+ data->phymode = PHY_MODE_MARVELL; -+ data->ledmode = 0; -+ gpio0 = 1; -+ } else { -+ data->phymode = le16_to_cpu(eeprom) & 0x7F; -+ data->ledmode = le16_to_cpu(eeprom) >> 8; -+ gpio0 = (le16_to_cpu(eeprom) & 0x80) ? 0 : 1; -+ } -+ netdev_dbg(dev->net, "GPIO0: %d, PhyMode: %d\n", gpio0, data->phymode); -+ -+ /* Power up external GigaPHY through AX88178 GPIO pin */ -+ asix_write_gpio(dev, AX_GPIO_RSE | AX_GPIO_GPO_1 | AX_GPIO_GPO1EN, 40); -+ if ((le16_to_cpu(eeprom) >> 8) != 1) { -+ asix_write_gpio(dev, 0x003c, 30); -+ asix_write_gpio(dev, 0x001c, 300); -+ asix_write_gpio(dev, 0x003c, 30); -+ } else { -+ netdev_dbg(dev->net, "gpio phymode == 1 path\n"); -+ asix_write_gpio(dev, AX_GPIO_GPO1EN, 30); -+ asix_write_gpio(dev, AX_GPIO_GPO1EN | AX_GPIO_GPO_1, 30); -+ } -+ -+ /* Read PHYID register *AFTER* powering up PHY */ -+ phyid = asix_get_phyid(dev); -+ netdev_dbg(dev->net, "PHYID=0x%08x\n", phyid); -+ -+ /* Set AX88178 to enable MII/GMII/RGMII interface for external PHY */ -+ asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, 0, 0, 0, NULL); -+ -+ asix_sw_reset(dev, 0); -+ msleep(150); -+ -+ asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD); -+ msleep(150); -+ -+ asix_write_rx_ctl(dev, 0); -+ -+ if (data->phymode == PHY_MODE_MARVELL) { -+ marvell_phy_init(dev); -+ msleep(60); -+ } else if (data->phymode == PHY_MODE_RTL8211CL) -+ rtl8211cl_phy_init(dev); -+ -+ asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, -+ BMCR_RESET | BMCR_ANENABLE); -+ asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, -+ ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP); -+ asix_mdio_write(dev->net, dev->mii.phy_id, MII_CTRL1000, -+ ADVERTISE_1000FULL); -+ -+ mii_nway_restart(&dev->mii); -+ -+ ret = asix_write_medium_mode(dev, AX88178_MEDIUM_DEFAULT); -+ if (ret < 0) -+ return ret; -+ -+ /* Rewrite MAC address */ -+ memcpy(data->mac_addr, dev->net->dev_addr, ETH_ALEN); -+ ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN, -+ data->mac_addr); -+ if (ret < 0) -+ return ret; -+ -+ ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL); -+ if (ret < 0) -+ return ret; -+ -+ return 0; -+} -+ -+static int ax88178_link_reset(struct usbnet *dev) -+{ -+ u16 mode; -+ struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET }; -+ struct asix_data *data = (struct asix_data *)&dev->data; -+ u32 speed; -+ -+ netdev_dbg(dev->net, "ax88178_link_reset()\n"); -+ -+ mii_check_media(&dev->mii, 1, 1); -+ mii_ethtool_gset(&dev->mii, &ecmd); -+ mode = AX88178_MEDIUM_DEFAULT; -+ speed = ethtool_cmd_speed(&ecmd); -+ -+ if (speed == SPEED_1000) -+ mode |= AX_MEDIUM_GM; -+ else if (speed == SPEED_100) -+ mode |= AX_MEDIUM_PS; -+ else -+ mode &= ~(AX_MEDIUM_PS | AX_MEDIUM_GM); -+ -+ mode |= AX_MEDIUM_ENCK; -+ -+ if (ecmd.duplex == DUPLEX_FULL) -+ mode |= AX_MEDIUM_FD; -+ else -+ mode &= ~AX_MEDIUM_FD; -+ -+ netdev_dbg(dev->net, "ax88178_link_reset() speed: %u duplex: %d setting mode to 0x%04x\n", -+ speed, ecmd.duplex, mode); -+ -+ asix_write_medium_mode(dev, mode); -+ -+ if (data->phymode == PHY_MODE_MARVELL && data->ledmode) -+ marvell_led_status(dev, speed); -+ -+ return 0; -+} -+ -+static void ax88178_set_mfb(struct usbnet *dev) -+{ -+ u16 mfb = AX_RX_CTL_MFB_16384; -+ u16 rxctl; -+ u16 medium; -+ int old_rx_urb_size = dev->rx_urb_size; -+ -+ if (dev->hard_mtu < 2048) { -+ dev->rx_urb_size = 2048; -+ mfb = AX_RX_CTL_MFB_2048; -+ } else if (dev->hard_mtu < 4096) { -+ dev->rx_urb_size = 4096; -+ mfb = AX_RX_CTL_MFB_4096; -+ } else if (dev->hard_mtu < 8192) { -+ dev->rx_urb_size = 8192; -+ mfb = AX_RX_CTL_MFB_8192; -+ } else if (dev->hard_mtu < 16384) { -+ dev->rx_urb_size = 16384; -+ mfb = AX_RX_CTL_MFB_16384; -+ } -+ -+ rxctl = asix_read_rx_ctl(dev); -+ asix_write_rx_ctl(dev, (rxctl & ~AX_RX_CTL_MFB_16384) | mfb); -+ -+ medium = asix_read_medium_status(dev); -+ if (dev->net->mtu > 1500) -+ medium |= AX_MEDIUM_JFE; -+ else -+ medium &= ~AX_MEDIUM_JFE; -+ asix_write_medium_mode(dev, medium); -+ -+ if (dev->rx_urb_size > old_rx_urb_size) -+ usbnet_unlink_rx_urbs(dev); -+} -+ -+static int ax88178_change_mtu(struct net_device *net, int new_mtu) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ int ll_mtu = new_mtu + net->hard_header_len + 4; -+ -+ netdev_dbg(dev->net, "ax88178_change_mtu() new_mtu=%d\n", new_mtu); -+ -+ if (new_mtu <= 0 || ll_mtu > 16384) -+ return -EINVAL; -+ -+ if ((ll_mtu % dev->maxpacket) == 0) -+ return -EDOM; -+ -+ net->mtu = new_mtu; -+ dev->hard_mtu = net->mtu + net->hard_header_len; -+ ax88178_set_mfb(dev); -+ -+ /* max qlen depend on hard_mtu and rx_urb_size */ -+ usbnet_update_max_qlen(dev); -+ -+ return 0; -+} -+ -+static const struct net_device_ops ax88178_netdev_ops = { -+ .ndo_open = usbnet_open, -+ .ndo_stop = usbnet_stop, -+ .ndo_start_xmit = usbnet_start_xmit, -+ .ndo_tx_timeout = usbnet_tx_timeout, -+ .ndo_set_mac_address = asix_set_mac_address, -+ .ndo_validate_addr = eth_validate_addr, -+ .ndo_set_rx_mode = asix_set_multicast, -+ .ndo_do_ioctl = asix_ioctl, -+ .ndo_change_mtu = ax88178_change_mtu, -+}; -+ -+static int ax88178_bind(struct usbnet *dev, struct usb_interface *intf) -+{ -+ int ret; -+ u8 buf[ETH_ALEN]; -+ -+ usbnet_get_endpoints(dev,intf); -+ -+ /* Get the MAC address */ -+ ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf); -+ if (ret < 0) { -+ netdev_dbg(dev->net, "Failed to read MAC address: %d\n", ret); -+ return ret; -+ } -+ -+ asix_set_netdev_dev_addr(dev, buf); -+ -+ /* Initialize MII structure */ -+ dev->mii.dev = dev->net; -+ dev->mii.mdio_read = asix_mdio_read; -+ dev->mii.mdio_write = asix_mdio_write; -+ dev->mii.phy_id_mask = 0x1f; -+ dev->mii.reg_num_mask = 0xff; -+ dev->mii.supports_gmii = 1; -+ dev->mii.phy_id = asix_get_phy_addr(dev); -+ -+ dev->net->netdev_ops = &ax88178_netdev_ops; -+ dev->net->ethtool_ops = &ax88178_ethtool_ops; -+ -+ /* Blink LEDS so users know driver saw dongle */ -+ asix_sw_reset(dev, 0); -+ msleep(150); -+ -+ asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD); -+ msleep(150); -+ -+ /* Asix framing packs multiple eth frames into a 2K usb bulk transfer */ -+ if (dev->driver_info->flags & FLAG_FRAMING_AX) { -+ /* hard_mtu is still the default - the device does not support -+ jumbo eth frames */ -+ dev->rx_urb_size = 2048; -+ } -+ -+ dev->driver_priv = kzalloc(sizeof(struct asix_common_private), GFP_KERNEL); -+ if (!dev->driver_priv) -+ return -ENOMEM; -+ -+ return 0; -+} -+ -+static const struct driver_info ax8817x_info = { -+ .description = "ASIX AX8817x USB 2.0 Ethernet", -+ .bind = ax88172_bind, -+ .status = asix_status, -+ .link_reset = ax88172_link_reset, -+ .reset = ax88172_link_reset, -+ .flags = FLAG_ETHER | FLAG_LINK_INTR, -+ .data = 0x00130103, -+}; -+ -+static const struct driver_info dlink_dub_e100_info = { -+ .description = "DLink DUB-E100 USB Ethernet", -+ .bind = ax88172_bind, -+ .status = asix_status, -+ .link_reset = ax88172_link_reset, -+ .reset = ax88172_link_reset, -+ .flags = FLAG_ETHER | FLAG_LINK_INTR, -+ .data = 0x009f9d9f, -+}; -+ -+static const struct driver_info netgear_fa120_info = { -+ .description = "Netgear FA-120 USB Ethernet", -+ .bind = ax88172_bind, -+ .status = asix_status, -+ .link_reset = ax88172_link_reset, -+ .reset = ax88172_link_reset, -+ .flags = FLAG_ETHER | FLAG_LINK_INTR, -+ .data = 0x00130103, -+}; -+ -+static const struct driver_info hawking_uf200_info = { -+ .description = "Hawking UF200 USB Ethernet", -+ .bind = ax88172_bind, -+ .status = asix_status, -+ .link_reset = ax88172_link_reset, -+ .reset = ax88172_link_reset, -+ .flags = FLAG_ETHER | FLAG_LINK_INTR, -+ .data = 0x001f1d1f, -+}; -+ -+static const struct driver_info ax88772_info = { -+ .description = "ASIX AX88772 USB 2.0 Ethernet", -+ .bind = ax88772_bind, -+ .unbind = ax88772_unbind, -+ .status = asix_status, -+ .link_reset = ax88772_link_reset, -+ .reset = ax88772_link_reset, -+ .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | FLAG_MULTI_PACKET, -+ .rx_fixup = asix_rx_fixup_common, -+ .tx_fixup = asix_tx_fixup, -+}; -+ -+static const struct driver_info ax88772b_info = { -+ .description = "ASIX AX88772B USB 2.0 Ethernet", -+ .bind = ax88772_bind, -+ .unbind = ax88772_unbind, -+ .status = asix_status, -+ .link_reset = ax88772_link_reset, -+ .reset = ax88772_reset, -+ .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | -+ FLAG_MULTI_PACKET, -+ .rx_fixup = asix_rx_fixup_common, -+ .tx_fixup = asix_tx_fixup, -+ .data = FLAG_EEPROM_MAC, -+}; -+ -+static const struct driver_info ax88178_info = { -+ .description = "ASIX AX88178 USB 2.0 Ethernet", -+ .bind = ax88178_bind, -+ .unbind = ax88772_unbind, -+ .status = asix_status, -+ .link_reset = ax88178_link_reset, -+ .reset = ax88178_reset, -+ .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | -+ FLAG_MULTI_PACKET, -+ .rx_fixup = asix_rx_fixup_common, -+ .tx_fixup = asix_tx_fixup, -+}; -+ -+/* -+ * USBLINK 20F9 "USB 2.0 LAN" USB ethernet adapter, typically found in -+ * no-name packaging. -+ * USB device strings are: -+ * 1: Manufacturer: USBLINK -+ * 2: Product: HG20F9 USB2.0 -+ * 3: Serial: 000003 -+ * Appears to be compatible with Asix 88772B. -+ */ -+static const struct driver_info hg20f9_info = { -+ .description = "HG20F9 USB 2.0 Ethernet", -+ .bind = ax88772_bind, -+ .unbind = ax88772_unbind, -+ .status = asix_status, -+ .link_reset = ax88772_link_reset, -+ .reset = ax88772_reset, -+ .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | -+ FLAG_MULTI_PACKET, -+ .rx_fixup = asix_rx_fixup_common, -+ .tx_fixup = asix_tx_fixup, -+ .data = FLAG_EEPROM_MAC, -+}; -+ -+static const struct usb_device_id products [] = { -+{ -+ // Linksys USB200M -+ USB_DEVICE (0x077b, 0x2226), -+ .driver_info = (unsigned long) &ax8817x_info, -+}, { -+ // Netgear FA120 -+ USB_DEVICE (0x0846, 0x1040), -+ .driver_info = (unsigned long) &netgear_fa120_info, -+}, { -+ // DLink DUB-E100 -+ USB_DEVICE (0x2001, 0x1a00), -+ .driver_info = (unsigned long) &dlink_dub_e100_info, -+}, { -+ // Intellinet, ST Lab USB Ethernet -+ USB_DEVICE (0x0b95, 0x1720), -+ .driver_info = (unsigned long) &ax8817x_info, -+}, { -+ // Hawking UF200, TrendNet TU2-ET100 -+ USB_DEVICE (0x07b8, 0x420a), -+ .driver_info = (unsigned long) &hawking_uf200_info, -+}, { -+ // Billionton Systems, USB2AR -+ USB_DEVICE (0x08dd, 0x90ff), -+ .driver_info = (unsigned long) &ax8817x_info, -+}, { -+ // ATEN UC210T -+ USB_DEVICE (0x0557, 0x2009), -+ .driver_info = (unsigned long) &ax8817x_info, -+}, { -+ // Buffalo LUA-U2-KTX -+ USB_DEVICE (0x0411, 0x003d), -+ .driver_info = (unsigned long) &ax8817x_info, -+}, { -+ // Buffalo LUA-U2-GT 10/100/1000 -+ USB_DEVICE (0x0411, 0x006e), -+ .driver_info = (unsigned long) &ax88178_info, -+}, { -+ // Sitecom LN-029 "USB 2.0 10/100 Ethernet adapter" -+ USB_DEVICE (0x6189, 0x182d), -+ .driver_info = (unsigned long) &ax8817x_info, -+}, { -+ // Sitecom LN-031 "USB 2.0 10/100/1000 Ethernet adapter" -+ USB_DEVICE (0x0df6, 0x0056), -+ .driver_info = (unsigned long) &ax88178_info, -+}, { -+ // Sitecom LN-028 "USB 2.0 10/100/1000 Ethernet adapter" -+ USB_DEVICE (0x0df6, 0x061c), -+ .driver_info = (unsigned long) &ax88178_info, -+}, { -+ // corega FEther USB2-TX -+ USB_DEVICE (0x07aa, 0x0017), -+ .driver_info = (unsigned long) &ax8817x_info, -+}, { -+ // Surecom EP-1427X-2 -+ USB_DEVICE (0x1189, 0x0893), -+ .driver_info = (unsigned long) &ax8817x_info, -+}, { -+ // goodway corp usb gwusb2e -+ USB_DEVICE (0x1631, 0x6200), -+ .driver_info = (unsigned long) &ax8817x_info, -+}, { -+ // JVC MP-PRX1 Port Replicator -+ USB_DEVICE (0x04f1, 0x3008), -+ .driver_info = (unsigned long) &ax8817x_info, -+}, { -+ // Lenovo U2L100P 10/100 -+ USB_DEVICE (0x17ef, 0x7203), -+ .driver_info = (unsigned long) &ax88772_info, -+}, { -+ // ASIX AX88772B 10/100 -+ USB_DEVICE (0x0b95, 0x772b), -+ .driver_info = (unsigned long) &ax88772b_info, -+}, { -+ // ASIX AX88772 10/100 -+ USB_DEVICE (0x0b95, 0x7720), -+ .driver_info = (unsigned long) &ax88772_info, -+}, { -+ // ASIX AX88178 10/100/1000 -+ USB_DEVICE (0x0b95, 0x1780), -+ .driver_info = (unsigned long) &ax88178_info, -+}, { -+ // Logitec LAN-GTJ/U2A -+ USB_DEVICE (0x0789, 0x0160), -+ .driver_info = (unsigned long) &ax88178_info, -+}, { -+ // Linksys USB200M Rev 2 -+ USB_DEVICE (0x13b1, 0x0018), -+ .driver_info = (unsigned long) &ax88772_info, -+}, { -+ // 0Q0 cable ethernet -+ USB_DEVICE (0x1557, 0x7720), -+ .driver_info = (unsigned long) &ax88772_info, -+}, { -+ // DLink DUB-E100 H/W Ver B1 -+ USB_DEVICE (0x07d1, 0x3c05), -+ .driver_info = (unsigned long) &ax88772_info, -+}, { -+ // DLink DUB-E100 H/W Ver B1 Alternate -+ USB_DEVICE (0x2001, 0x3c05), -+ .driver_info = (unsigned long) &ax88772_info, -+}, { -+ // DLink DUB-E100 H/W Ver C1 -+ USB_DEVICE (0x2001, 0x1a02), -+ .driver_info = (unsigned long) &ax88772_info, -+}, { -+ // Linksys USB1000 -+ USB_DEVICE (0x1737, 0x0039), -+ .driver_info = (unsigned long) &ax88178_info, -+}, { -+ // IO-DATA ETG-US2 -+ USB_DEVICE (0x04bb, 0x0930), -+ .driver_info = (unsigned long) &ax88178_info, -+}, { -+ // Belkin F5D5055 -+ USB_DEVICE(0x050d, 0x5055), -+ .driver_info = (unsigned long) &ax88178_info, -+}, { -+ // Apple USB Ethernet Adapter -+ USB_DEVICE(0x05ac, 0x1402), -+ .driver_info = (unsigned long) &ax88772_info, -+}, { -+ // Cables-to-Go USB Ethernet Adapter -+ USB_DEVICE(0x0b95, 0x772a), -+ .driver_info = (unsigned long) &ax88772_info, -+}, { -+ // ABOCOM for pci -+ USB_DEVICE(0x14ea, 0xab11), -+ .driver_info = (unsigned long) &ax88178_info, -+}, { -+ // ASIX 88772a -+ USB_DEVICE(0x0db0, 0xa877), -+ .driver_info = (unsigned long) &ax88772_info, -+}, { -+ // Asus USB Ethernet Adapter -+ USB_DEVICE (0x0b95, 0x7e2b), -+ .driver_info = (unsigned long) &ax88772_info, -+}, { -+ /* ASIX 88172a demo board */ -+ USB_DEVICE(0x0b95, 0x172a), -+ .driver_info = (unsigned long) &ax88172a_info, -+}, { -+ /* -+ * USBLINK HG20F9 "USB 2.0 LAN" -+ * Appears to have gazumped Linksys's manufacturer ID but -+ * doesn't (yet) conflict with any known Linksys product. -+ */ -+ USB_DEVICE(0x066b, 0x20f9), -+ .driver_info = (unsigned long) &hg20f9_info, -+}, -+ { }, // END -+}; -+MODULE_DEVICE_TABLE(usb, products); -+ -+static struct usb_driver asix_driver = { -+ .name = DRIVER_NAME, -+ .id_table = products, -+ .probe = usbnet_probe, -+ .suspend = usbnet_suspend, -+ .resume = usbnet_resume, -+ .disconnect = usbnet_disconnect, -+ .supports_autosuspend = 1, -+ .disable_hub_initiated_lpm = 1, -+}; -+ -+module_usb_driver(asix_driver); -+ -+MODULE_AUTHOR("David Hollis"); -+MODULE_VERSION(DRIVER_VERSION); -+MODULE_DESCRIPTION("ASIX AX8817X based USB 2.0 Ethernet Devices"); -+MODULE_LICENSE("GPL"); -+ -diff -Naur backports-4.2.6-1.org/drivers/net/usb/asix.h backports-4.2.6-1/drivers/net/usb/asix.h ---- backports-4.2.6-1.org/drivers/net/usb/asix.h 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/asix.h 2016-06-28 14:35:17.965307221 +0200 -@@ -0,0 +1,234 @@ -+/* -+ * ASIX AX8817X based USB 2.0 Ethernet Devices -+ * Copyright (C) 2003-2006 David Hollis -+ * Copyright (C) 2005 Phil Chang -+ * Copyright (C) 2006 James Painter -+ * Copyright (c) 2002-2003 TiVo Inc. -+ * -+ * 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, see . -+ */ -+ -+#ifndef _ASIX_H -+#define _ASIX_H -+ -+// #define DEBUG // error path messages, extra info -+// #define VERBOSE // more; success messages -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define DRIVER_VERSION "22-Dec-2011" -+#define DRIVER_NAME "asix" -+ -+/* ASIX AX8817X based USB 2.0 Ethernet Devices */ -+ -+#define AX_CMD_SET_SW_MII 0x06 -+#define AX_CMD_READ_MII_REG 0x07 -+#define AX_CMD_WRITE_MII_REG 0x08 -+#define AX_CMD_SET_HW_MII 0x0a -+#define AX_CMD_READ_EEPROM 0x0b -+#define AX_CMD_WRITE_EEPROM 0x0c -+#define AX_CMD_WRITE_ENABLE 0x0d -+#define AX_CMD_WRITE_DISABLE 0x0e -+#define AX_CMD_READ_RX_CTL 0x0f -+#define AX_CMD_WRITE_RX_CTL 0x10 -+#define AX_CMD_READ_IPG012 0x11 -+#define AX_CMD_WRITE_IPG0 0x12 -+#define AX_CMD_WRITE_IPG1 0x13 -+#define AX_CMD_READ_NODE_ID 0x13 -+#define AX_CMD_WRITE_NODE_ID 0x14 -+#define AX_CMD_WRITE_IPG2 0x14 -+#define AX_CMD_WRITE_MULTI_FILTER 0x16 -+#define AX88172_CMD_READ_NODE_ID 0x17 -+#define AX_CMD_READ_PHY_ID 0x19 -+#define AX_CMD_READ_MEDIUM_STATUS 0x1a -+#define AX_CMD_WRITE_MEDIUM_MODE 0x1b -+#define AX_CMD_READ_MONITOR_MODE 0x1c -+#define AX_CMD_WRITE_MONITOR_MODE 0x1d -+#define AX_CMD_READ_GPIOS 0x1e -+#define AX_CMD_WRITE_GPIOS 0x1f -+#define AX_CMD_SW_RESET 0x20 -+#define AX_CMD_SW_PHY_STATUS 0x21 -+#define AX_CMD_SW_PHY_SELECT 0x22 -+ -+#define AX_PHY_SELECT_MASK (BIT(3) | BIT(2)) -+#define AX_PHY_SELECT_INTERNAL 0 -+#define AX_PHY_SELECT_EXTERNAL BIT(2) -+ -+#define AX_MONITOR_MODE 0x01 -+#define AX_MONITOR_LINK 0x02 -+#define AX_MONITOR_MAGIC 0x04 -+#define AX_MONITOR_HSFS 0x10 -+ -+/* AX88172 Medium Status Register values */ -+#define AX88172_MEDIUM_FD 0x02 -+#define AX88172_MEDIUM_TX 0x04 -+#define AX88172_MEDIUM_FC 0x10 -+#define AX88172_MEDIUM_DEFAULT \ -+ ( AX88172_MEDIUM_FD | AX88172_MEDIUM_TX | AX88172_MEDIUM_FC ) -+ -+#define AX_MCAST_FILTER_SIZE 8 -+#define AX_MAX_MCAST 64 -+ -+#define AX_SWRESET_CLEAR 0x00 -+#define AX_SWRESET_RR 0x01 -+#define AX_SWRESET_RT 0x02 -+#define AX_SWRESET_PRTE 0x04 -+#define AX_SWRESET_PRL 0x08 -+#define AX_SWRESET_BZ 0x10 -+#define AX_SWRESET_IPRL 0x20 -+#define AX_SWRESET_IPPD 0x40 -+ -+#define AX88772_IPG0_DEFAULT 0x15 -+#define AX88772_IPG1_DEFAULT 0x0c -+#define AX88772_IPG2_DEFAULT 0x12 -+ -+/* AX88772 & AX88178 Medium Mode Register */ -+#define AX_MEDIUM_PF 0x0080 -+#define AX_MEDIUM_JFE 0x0040 -+#define AX_MEDIUM_TFC 0x0020 -+#define AX_MEDIUM_RFC 0x0010 -+#define AX_MEDIUM_ENCK 0x0008 -+#define AX_MEDIUM_AC 0x0004 -+#define AX_MEDIUM_FD 0x0002 -+#define AX_MEDIUM_GM 0x0001 -+#define AX_MEDIUM_SM 0x1000 -+#define AX_MEDIUM_SBP 0x0800 -+#define AX_MEDIUM_PS 0x0200 -+#define AX_MEDIUM_RE 0x0100 -+ -+#define AX88178_MEDIUM_DEFAULT \ -+ (AX_MEDIUM_PS | AX_MEDIUM_FD | AX_MEDIUM_AC | \ -+ AX_MEDIUM_RFC | AX_MEDIUM_TFC | AX_MEDIUM_JFE | \ -+ AX_MEDIUM_RE) -+ -+#define AX88772_MEDIUM_DEFAULT \ -+ (AX_MEDIUM_FD | AX_MEDIUM_RFC | \ -+ AX_MEDIUM_TFC | AX_MEDIUM_PS | \ -+ AX_MEDIUM_AC | AX_MEDIUM_RE) -+ -+/* AX88772 & AX88178 RX_CTL values */ -+#define AX_RX_CTL_SO 0x0080 -+#define AX_RX_CTL_AP 0x0020 -+#define AX_RX_CTL_AM 0x0010 -+#define AX_RX_CTL_AB 0x0008 -+#define AX_RX_CTL_SEP 0x0004 -+#define AX_RX_CTL_AMALL 0x0002 -+#define AX_RX_CTL_PRO 0x0001 -+#define AX_RX_CTL_MFB_2048 0x0000 -+#define AX_RX_CTL_MFB_4096 0x0100 -+#define AX_RX_CTL_MFB_8192 0x0200 -+#define AX_RX_CTL_MFB_16384 0x0300 -+ -+#define AX_DEFAULT_RX_CTL (AX_RX_CTL_SO | AX_RX_CTL_AB) -+ -+/* GPIO 0 .. 2 toggles */ -+#define AX_GPIO_GPO0EN 0x01 /* GPIO0 Output enable */ -+#define AX_GPIO_GPO_0 0x02 /* GPIO0 Output value */ -+#define AX_GPIO_GPO1EN 0x04 /* GPIO1 Output enable */ -+#define AX_GPIO_GPO_1 0x08 /* GPIO1 Output value */ -+#define AX_GPIO_GPO2EN 0x10 /* GPIO2 Output enable */ -+#define AX_GPIO_GPO_2 0x20 /* GPIO2 Output value */ -+#define AX_GPIO_RESERVED 0x40 /* Reserved */ -+#define AX_GPIO_RSE 0x80 /* Reload serial EEPROM */ -+ -+#define AX_EEPROM_MAGIC 0xdeadbeef -+#define AX_EEPROM_LEN 0x200 -+ -+/* This structure cannot exceed sizeof(unsigned long [5]) AKA 20 bytes */ -+struct asix_data { -+ u8 multi_filter[AX_MCAST_FILTER_SIZE]; -+ u8 mac_addr[ETH_ALEN]; -+ u8 phymode; -+ u8 ledmode; -+ u8 res; -+}; -+ -+struct asix_rx_fixup_info { -+ struct sk_buff *ax_skb; -+ u32 header; -+ u16 size; -+ bool split_head; -+}; -+ -+struct asix_common_private { -+ struct asix_rx_fixup_info rx_fixup_info; -+}; -+ -+extern const struct driver_info ax88172a_info; -+ -+/* ASIX specific flags */ -+#define FLAG_EEPROM_MAC (1UL << 0) /* init device MAC from eeprom */ -+ -+int asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, -+ u16 size, void *data); -+ -+int asix_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, -+ u16 size, void *data); -+ -+void asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, -+ u16 index, u16 size, void *data); -+ -+int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb, -+ struct asix_rx_fixup_info *rx); -+int asix_rx_fixup_common(struct usbnet *dev, struct sk_buff *skb); -+ -+struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, -+ gfp_t flags); -+ -+int asix_set_sw_mii(struct usbnet *dev); -+int asix_set_hw_mii(struct usbnet *dev); -+ -+int asix_read_phy_addr(struct usbnet *dev, int internal); -+int asix_get_phy_addr(struct usbnet *dev); -+ -+int asix_sw_reset(struct usbnet *dev, u8 flags); -+ -+u16 asix_read_rx_ctl(struct usbnet *dev); -+int asix_write_rx_ctl(struct usbnet *dev, u16 mode); -+ -+u16 asix_read_medium_status(struct usbnet *dev); -+int asix_write_medium_mode(struct usbnet *dev, u16 mode); -+ -+int asix_write_gpio(struct usbnet *dev, u16 value, int sleep); -+ -+void asix_set_multicast(struct net_device *net); -+ -+int asix_mdio_read(struct net_device *netdev, int phy_id, int loc); -+void asix_mdio_write(struct net_device *netdev, int phy_id, int loc, int val); -+ -+void asix_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo); -+int asix_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo); -+ -+int asix_get_eeprom_len(struct net_device *net); -+int asix_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, -+ u8 *data); -+int asix_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, -+ u8 *data); -+ -+void asix_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info); -+ -+int asix_set_mac_address(struct net_device *net, void *p); -+ -+#endif /* _ASIX_H */ -diff -Naur backports-4.2.6-1.org/drivers/net/usb/ax88172a.c backports-4.2.6-1/drivers/net/usb/ax88172a.c ---- backports-4.2.6-1.org/drivers/net/usb/ax88172a.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/ax88172a.c 2016-06-28 14:35:17.965307221 +0200 -@@ -0,0 +1,422 @@ -+/* -+ * ASIX AX88172A based USB 2.0 Ethernet Devices -+ * Copyright (C) 2012 OMICRON electronics GmbH -+ * -+ * Supports external PHYs via phylib. Based on the driver for the -+ * AX88772. Original copyrights follow: -+ * -+ * Copyright (C) 2003-2006 David Hollis -+ * Copyright (C) 2005 Phil Chang -+ * Copyright (C) 2006 James Painter -+ * Copyright (c) 2002-2003 TiVo Inc. -+ * -+ * 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, see . -+ */ -+ -+#include "asix.h" -+#include -+ -+struct ax88172a_private { -+ struct mii_bus *mdio; -+ struct phy_device *phydev; -+ char phy_name[20]; -+ u16 phy_addr; -+ u16 oldmode; -+ int use_embdphy; -+ struct asix_rx_fixup_info rx_fixup_info; -+}; -+ -+/* MDIO read and write wrappers for phylib */ -+static int asix_mdio_bus_read(struct mii_bus *bus, int phy_id, int regnum) -+{ -+ return asix_mdio_read(((struct usbnet *)bus->priv)->net, phy_id, -+ regnum); -+} -+ -+static int asix_mdio_bus_write(struct mii_bus *bus, int phy_id, int regnum, -+ u16 val) -+{ -+ asix_mdio_write(((struct usbnet *)bus->priv)->net, phy_id, regnum, val); -+ return 0; -+} -+ -+static int ax88172a_ioctl(struct net_device *net, struct ifreq *rq, int cmd) -+{ -+ if (!netif_running(net)) -+ return -EINVAL; -+ -+ if (!net->phydev) -+ return -ENODEV; -+ -+ return phy_mii_ioctl(net->phydev, rq, cmd); -+} -+ -+/* set MAC link settings according to information from phylib */ -+static void ax88172a_adjust_link(struct net_device *netdev) -+{ -+ struct phy_device *phydev = netdev->phydev; -+ struct usbnet *dev = netdev_priv(netdev); -+ struct ax88172a_private *priv = dev->driver_priv; -+ u16 mode = 0; -+ -+ if (phydev->link) { -+ mode = AX88772_MEDIUM_DEFAULT; -+ -+ if (phydev->duplex == DUPLEX_HALF) -+ mode &= ~AX_MEDIUM_FD; -+ -+ if (phydev->speed != SPEED_100) -+ mode &= ~AX_MEDIUM_PS; -+ } -+ -+ if (mode != priv->oldmode) { -+ asix_write_medium_mode(dev, mode); -+ priv->oldmode = mode; -+ netdev_dbg(netdev, "speed %u duplex %d, setting mode to 0x%04x\n", -+ phydev->speed, phydev->duplex, mode); -+ phy_print_status(phydev); -+ } -+} -+ -+static void ax88172a_status(struct usbnet *dev, struct urb *urb) -+{ -+ /* link changes are detected by polling the phy */ -+} -+ -+/* use phylib infrastructure */ -+static int ax88172a_init_mdio(struct usbnet *dev) -+{ -+ struct ax88172a_private *priv = dev->driver_priv; -+ int ret, i; -+ -+ priv->mdio = mdiobus_alloc(); -+ if (!priv->mdio) { -+ netdev_err(dev->net, "Could not allocate MDIO bus\n"); -+ return -ENOMEM; -+ } -+ -+ priv->mdio->priv = (void *)dev; -+ priv->mdio->read = &asix_mdio_bus_read; -+ priv->mdio->write = &asix_mdio_bus_write; -+ priv->mdio->name = "Asix MDIO Bus"; -+ /* mii bus name is usb-- */ -+ snprintf(priv->mdio->id, MII_BUS_ID_SIZE, "usb-%03d:%03d", -+ dev->udev->bus->busnum, dev->udev->devnum); -+ -+ priv->mdio->irq = kzalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); -+ if (!priv->mdio->irq) { -+ ret = -ENOMEM; -+ goto mfree; -+ } -+ for (i = 0; i < PHY_MAX_ADDR; i++) -+ priv->mdio->irq[i] = PHY_POLL; -+ -+ ret = mdiobus_register(priv->mdio); -+ if (ret) { -+ netdev_err(dev->net, "Could not register MDIO bus\n"); -+ goto ifree; -+ } -+ -+ netdev_info(dev->net, "registered mdio bus %s\n", priv->mdio->id); -+ return 0; -+ -+ifree: -+ kfree(priv->mdio->irq); -+mfree: -+ mdiobus_free(priv->mdio); -+ return ret; -+} -+ -+static void ax88172a_remove_mdio(struct usbnet *dev) -+{ -+ struct ax88172a_private *priv = dev->driver_priv; -+ -+ netdev_info(dev->net, "deregistering mdio bus %s\n", priv->mdio->id); -+ mdiobus_unregister(priv->mdio); -+ kfree(priv->mdio->irq); -+ mdiobus_free(priv->mdio); -+} -+ -+static const struct net_device_ops ax88172a_netdev_ops = { -+ .ndo_open = usbnet_open, -+ .ndo_stop = usbnet_stop, -+ .ndo_start_xmit = usbnet_start_xmit, -+ .ndo_tx_timeout = usbnet_tx_timeout, -+ .ndo_change_mtu = usbnet_change_mtu, -+ .ndo_set_mac_address = asix_set_mac_address, -+ .ndo_validate_addr = eth_validate_addr, -+ .ndo_do_ioctl = ax88172a_ioctl, -+ .ndo_set_rx_mode = asix_set_multicast, -+}; -+ -+static int ax88172a_get_settings(struct net_device *net, -+ struct ethtool_cmd *cmd) -+{ -+ if (!net->phydev) -+ return -ENODEV; -+ -+ return phy_ethtool_gset(net->phydev, cmd); -+} -+ -+static int ax88172a_set_settings(struct net_device *net, -+ struct ethtool_cmd *cmd) -+{ -+ if (!net->phydev) -+ return -ENODEV; -+ -+ return phy_ethtool_sset(net->phydev, cmd); -+} -+ -+static int ax88172a_nway_reset(struct net_device *net) -+{ -+ if (!net->phydev) -+ return -ENODEV; -+ -+ return phy_start_aneg(net->phydev); -+} -+ -+static const struct ethtool_ops ax88172a_ethtool_ops = { -+ .get_drvinfo = asix_get_drvinfo, -+ .get_link = usbnet_get_link, -+ .get_msglevel = usbnet_get_msglevel, -+ .set_msglevel = usbnet_set_msglevel, -+ .get_wol = asix_get_wol, -+ .set_wol = asix_set_wol, -+ .get_eeprom_len = asix_get_eeprom_len, -+ .get_eeprom = asix_get_eeprom, -+ .set_eeprom = asix_set_eeprom, -+ .get_settings = ax88172a_get_settings, -+ .set_settings = ax88172a_set_settings, -+ .nway_reset = ax88172a_nway_reset, -+}; -+ -+static int ax88172a_reset_phy(struct usbnet *dev, int embd_phy) -+{ -+ int ret; -+ -+ ret = asix_sw_reset(dev, AX_SWRESET_IPPD); -+ if (ret < 0) -+ goto err; -+ -+ msleep(150); -+ ret = asix_sw_reset(dev, AX_SWRESET_CLEAR); -+ if (ret < 0) -+ goto err; -+ -+ msleep(150); -+ -+ ret = asix_sw_reset(dev, embd_phy ? AX_SWRESET_IPRL : AX_SWRESET_IPPD); -+ if (ret < 0) -+ goto err; -+ -+ return 0; -+ -+err: -+ return ret; -+} -+ -+ -+static int ax88172a_bind(struct usbnet *dev, struct usb_interface *intf) -+{ -+ int ret; -+ u8 buf[ETH_ALEN]; -+ struct ax88172a_private *priv; -+ -+ usbnet_get_endpoints(dev, intf); -+ -+ priv = kzalloc(sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ -+ dev->driver_priv = priv; -+ -+ /* Get the MAC address */ -+ ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf); -+ if (ret < 0) { -+ netdev_err(dev->net, "Failed to read MAC address: %d\n", ret); -+ goto free; -+ } -+ memcpy(dev->net->dev_addr, buf, ETH_ALEN); -+ -+ dev->net->netdev_ops = &ax88172a_netdev_ops; -+ dev->net->ethtool_ops = &ax88172a_ethtool_ops; -+ -+ /* are we using the internal or the external phy? */ -+ ret = asix_read_cmd(dev, AX_CMD_SW_PHY_STATUS, 0, 0, 1, buf); -+ if (ret < 0) { -+ netdev_err(dev->net, "Failed to read software interface selection register: %d\n", -+ ret); -+ goto free; -+ } -+ -+ netdev_dbg(dev->net, "AX_CMD_SW_PHY_STATUS = 0x%02x\n", buf[0]); -+ switch (buf[0] & AX_PHY_SELECT_MASK) { -+ case AX_PHY_SELECT_INTERNAL: -+ netdev_dbg(dev->net, "use internal phy\n"); -+ priv->use_embdphy = 1; -+ break; -+ case AX_PHY_SELECT_EXTERNAL: -+ netdev_dbg(dev->net, "use external phy\n"); -+ priv->use_embdphy = 0; -+ break; -+ default: -+ netdev_err(dev->net, "Interface mode not supported by driver\n"); -+ ret = -ENOTSUPP; -+ goto free; -+ } -+ -+ priv->phy_addr = asix_read_phy_addr(dev, priv->use_embdphy); -+ ax88172a_reset_phy(dev, priv->use_embdphy); -+ -+ /* Asix framing packs multiple eth frames into a 2K usb bulk transfer */ -+ if (dev->driver_info->flags & FLAG_FRAMING_AX) { -+ /* hard_mtu is still the default - the device does not support -+ jumbo eth frames */ -+ dev->rx_urb_size = 2048; -+ } -+ -+ /* init MDIO bus */ -+ ret = ax88172a_init_mdio(dev); -+ if (ret) -+ goto free; -+ -+ return 0; -+ -+free: -+ kfree(priv); -+ return ret; -+} -+ -+static int ax88172a_stop(struct usbnet *dev) -+{ -+ struct ax88172a_private *priv = dev->driver_priv; -+ -+ netdev_dbg(dev->net, "Stopping interface\n"); -+ -+ if (priv->phydev) { -+ netdev_info(dev->net, "Disconnecting from phy %s\n", -+ priv->phy_name); -+ phy_stop(priv->phydev); -+ phy_disconnect(priv->phydev); -+ } -+ -+ return 0; -+} -+ -+static void ax88172a_unbind(struct usbnet *dev, struct usb_interface *intf) -+{ -+ struct ax88172a_private *priv = dev->driver_priv; -+ -+ ax88172a_remove_mdio(dev); -+ kfree(priv); -+} -+ -+static int ax88172a_reset(struct usbnet *dev) -+{ -+ struct asix_data *data = (struct asix_data *)&dev->data; -+ struct ax88172a_private *priv = dev->driver_priv; -+ int ret; -+ u16 rx_ctl; -+ -+ ax88172a_reset_phy(dev, priv->use_embdphy); -+ -+ msleep(150); -+ rx_ctl = asix_read_rx_ctl(dev); -+ netdev_dbg(dev->net, "RX_CTL is 0x%04x after software reset\n", rx_ctl); -+ ret = asix_write_rx_ctl(dev, 0x0000); -+ if (ret < 0) -+ goto out; -+ -+ rx_ctl = asix_read_rx_ctl(dev); -+ netdev_dbg(dev->net, "RX_CTL is 0x%04x setting to 0x0000\n", rx_ctl); -+ -+ msleep(150); -+ -+ ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0, -+ AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT, -+ AX88772_IPG2_DEFAULT, 0, NULL); -+ if (ret < 0) { -+ netdev_err(dev->net, "Write IPG,IPG1,IPG2 failed: %d\n", ret); -+ goto out; -+ } -+ -+ /* Rewrite MAC address */ -+ memcpy(data->mac_addr, dev->net->dev_addr, ETH_ALEN); -+ ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN, -+ data->mac_addr); -+ if (ret < 0) -+ goto out; -+ -+ /* Set RX_CTL to default values with 2k buffer, and enable cactus */ -+ ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL); -+ if (ret < 0) -+ goto out; -+ -+ rx_ctl = asix_read_rx_ctl(dev); -+ netdev_dbg(dev->net, "RX_CTL is 0x%04x after all initializations\n", -+ rx_ctl); -+ -+ rx_ctl = asix_read_medium_status(dev); -+ netdev_dbg(dev->net, "Medium Status is 0x%04x after all initializations\n", -+ rx_ctl); -+ -+ /* Connect to PHY */ -+ snprintf(priv->phy_name, 20, PHY_ID_FMT, -+ priv->mdio->id, priv->phy_addr); -+ -+ priv->phydev = phy_connect(dev->net, priv->phy_name, -+ &ax88172a_adjust_link, -+ PHY_INTERFACE_MODE_MII); -+ if (IS_ERR(priv->phydev)) { -+ netdev_err(dev->net, "Could not connect to PHY device %s\n", -+ priv->phy_name); -+ ret = PTR_ERR(priv->phydev); -+ goto out; -+ } -+ -+ netdev_info(dev->net, "Connected to phy %s\n", priv->phy_name); -+ -+ /* During power-up, the AX88172A set the power down (BMCR_PDOWN) -+ * bit of the PHY. Bring the PHY up again. -+ */ -+ genphy_resume(priv->phydev); -+ phy_start(priv->phydev); -+ -+ return 0; -+ -+out: -+ return ret; -+ -+} -+ -+static int ax88172a_rx_fixup(struct usbnet *dev, struct sk_buff *skb) -+{ -+ struct ax88172a_private *dp = dev->driver_priv; -+ struct asix_rx_fixup_info *rx = &dp->rx_fixup_info; -+ -+ return asix_rx_fixup_internal(dev, skb, rx); -+} -+ -+const struct driver_info ax88172a_info = { -+ .description = "ASIX AX88172A USB 2.0 Ethernet", -+ .bind = ax88172a_bind, -+ .reset = ax88172a_reset, -+ .stop = ax88172a_stop, -+ .unbind = ax88172a_unbind, -+ .status = ax88172a_status, -+ .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | -+ FLAG_MULTI_PACKET, -+ .rx_fixup = ax88172a_rx_fixup, -+ .tx_fixup = asix_tx_fixup, -+}; -diff -Naur backports-4.2.6-1.org/drivers/net/usb/ax88179_178a.c backports-4.2.6-1/drivers/net/usb/ax88179_178a.c ---- backports-4.2.6-1.org/drivers/net/usb/ax88179_178a.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/ax88179_178a.c 2016-06-28 14:35:17.968640554 +0200 -@@ -0,0 +1,1756 @@ -+/* -+ * ASIX AX88179/178A USB 3.0/2.0 to Gigabit Ethernet Devices -+ * -+ * Copyright (C) 2011-2013 ASIX -+ * -+ * 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, see . -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define AX88179_PHY_ID 0x03 -+#define AX_EEPROM_LEN 0x100 -+#define AX88179_EEPROM_MAGIC 0x17900b95 -+#define AX_MCAST_FLTSIZE 8 -+#define AX_MAX_MCAST 64 -+#define AX_INT_PPLS_LINK ((u32)BIT(16)) -+#define AX_RXHDR_L4_TYPE_MASK 0x1c -+#define AX_RXHDR_L4_TYPE_UDP 4 -+#define AX_RXHDR_L4_TYPE_TCP 16 -+#define AX_RXHDR_L3CSUM_ERR 2 -+#define AX_RXHDR_L4CSUM_ERR 1 -+#define AX_RXHDR_CRC_ERR ((u32)BIT(29)) -+#define AX_RXHDR_DROP_ERR ((u32)BIT(31)) -+#define AX_ACCESS_MAC 0x01 -+#define AX_ACCESS_PHY 0x02 -+#define AX_ACCESS_EEPROM 0x04 -+#define AX_ACCESS_EFUS 0x05 -+#define AX_PAUSE_WATERLVL_HIGH 0x54 -+#define AX_PAUSE_WATERLVL_LOW 0x55 -+ -+#define PHYSICAL_LINK_STATUS 0x02 -+ #define AX_USB_SS 0x04 -+ #define AX_USB_HS 0x02 -+ -+#define GENERAL_STATUS 0x03 -+/* Check AX88179 version. UA1:Bit2 = 0, UA2:Bit2 = 1 */ -+ #define AX_SECLD 0x04 -+ -+#define AX_SROM_ADDR 0x07 -+#define AX_SROM_CMD 0x0a -+ #define EEP_RD 0x04 -+ #define EEP_BUSY 0x10 -+ -+#define AX_SROM_DATA_LOW 0x08 -+#define AX_SROM_DATA_HIGH 0x09 -+ -+#define AX_RX_CTL 0x0b -+ #define AX_RX_CTL_DROPCRCERR 0x0100 -+ #define AX_RX_CTL_IPE 0x0200 -+ #define AX_RX_CTL_START 0x0080 -+ #define AX_RX_CTL_AP 0x0020 -+ #define AX_RX_CTL_AM 0x0010 -+ #define AX_RX_CTL_AB 0x0008 -+ #define AX_RX_CTL_AMALL 0x0002 -+ #define AX_RX_CTL_PRO 0x0001 -+ #define AX_RX_CTL_STOP 0x0000 -+ -+#define AX_NODE_ID 0x10 -+#define AX_MULFLTARY 0x16 -+ -+#define AX_MEDIUM_STATUS_MODE 0x22 -+ #define AX_MEDIUM_GIGAMODE 0x01 -+ #define AX_MEDIUM_FULL_DUPLEX 0x02 -+ #define AX_MEDIUM_EN_125MHZ 0x08 -+ #define AX_MEDIUM_RXFLOW_CTRLEN 0x10 -+ #define AX_MEDIUM_TXFLOW_CTRLEN 0x20 -+ #define AX_MEDIUM_RECEIVE_EN 0x100 -+ #define AX_MEDIUM_PS 0x200 -+ #define AX_MEDIUM_JUMBO_EN 0x8040 -+ -+#define AX_MONITOR_MOD 0x24 -+ #define AX_MONITOR_MODE_RWLC 0x02 -+ #define AX_MONITOR_MODE_RWMP 0x04 -+ #define AX_MONITOR_MODE_PMEPOL 0x20 -+ #define AX_MONITOR_MODE_PMETYPE 0x40 -+ -+#define AX_GPIO_CTRL 0x25 -+ #define AX_GPIO_CTRL_GPIO3EN 0x80 -+ #define AX_GPIO_CTRL_GPIO2EN 0x40 -+ #define AX_GPIO_CTRL_GPIO1EN 0x20 -+ -+#define AX_PHYPWR_RSTCTL 0x26 -+ #define AX_PHYPWR_RSTCTL_BZ 0x0010 -+ #define AX_PHYPWR_RSTCTL_IPRL 0x0020 -+ #define AX_PHYPWR_RSTCTL_AT 0x1000 -+ -+#define AX_RX_BULKIN_QCTRL 0x2e -+#define AX_CLK_SELECT 0x33 -+ #define AX_CLK_SELECT_BCS 0x01 -+ #define AX_CLK_SELECT_ACS 0x02 -+ #define AX_CLK_SELECT_ULR 0x08 -+ -+#define AX_RXCOE_CTL 0x34 -+ #define AX_RXCOE_IP 0x01 -+ #define AX_RXCOE_TCP 0x02 -+ #define AX_RXCOE_UDP 0x04 -+ #define AX_RXCOE_TCPV6 0x20 -+ #define AX_RXCOE_UDPV6 0x40 -+ -+#define AX_TXCOE_CTL 0x35 -+ #define AX_TXCOE_IP 0x01 -+ #define AX_TXCOE_TCP 0x02 -+ #define AX_TXCOE_UDP 0x04 -+ #define AX_TXCOE_TCPV6 0x20 -+ #define AX_TXCOE_UDPV6 0x40 -+ -+#define AX_LEDCTRL 0x73 -+ -+#define GMII_PHY_PHYSR 0x11 -+ #define GMII_PHY_PHYSR_SMASK 0xc000 -+ #define GMII_PHY_PHYSR_GIGA 0x8000 -+ #define GMII_PHY_PHYSR_100 0x4000 -+ #define GMII_PHY_PHYSR_FULL 0x2000 -+ #define GMII_PHY_PHYSR_LINK 0x400 -+ -+#define GMII_LED_ACT 0x1a -+ #define GMII_LED_ACTIVE_MASK 0xff8f -+ #define GMII_LED0_ACTIVE BIT(4) -+ #define GMII_LED1_ACTIVE BIT(5) -+ #define GMII_LED2_ACTIVE BIT(6) -+ -+#define GMII_LED_LINK 0x1c -+ #define GMII_LED_LINK_MASK 0xf888 -+ #define GMII_LED0_LINK_10 BIT(0) -+ #define GMII_LED0_LINK_100 BIT(1) -+ #define GMII_LED0_LINK_1000 BIT(2) -+ #define GMII_LED1_LINK_10 BIT(4) -+ #define GMII_LED1_LINK_100 BIT(5) -+ #define GMII_LED1_LINK_1000 BIT(6) -+ #define GMII_LED2_LINK_10 BIT(8) -+ #define GMII_LED2_LINK_100 BIT(9) -+ #define GMII_LED2_LINK_1000 BIT(10) -+ #define LED0_ACTIVE BIT(0) -+ #define LED0_LINK_10 BIT(1) -+ #define LED0_LINK_100 BIT(2) -+ #define LED0_LINK_1000 BIT(3) -+ #define LED0_FD BIT(4) -+ #define LED0_USB3_MASK 0x001f -+ #define LED1_ACTIVE BIT(5) -+ #define LED1_LINK_10 BIT(6) -+ #define LED1_LINK_100 BIT(7) -+ #define LED1_LINK_1000 BIT(8) -+ #define LED1_FD BIT(9) -+ #define LED1_USB3_MASK 0x03e0 -+ #define LED2_ACTIVE BIT(10) -+ #define LED2_LINK_1000 BIT(13) -+ #define LED2_LINK_100 BIT(12) -+ #define LED2_LINK_10 BIT(11) -+ #define LED2_FD BIT(14) -+ #define LED_VALID BIT(15) -+ #define LED2_USB3_MASK 0x7c00 -+ -+#define GMII_PHYPAGE 0x1e -+#define GMII_PHY_PAGE_SELECT 0x1f -+ #define GMII_PHY_PGSEL_EXT 0x0007 -+ #define GMII_PHY_PGSEL_PAGE0 0x0000 -+ #define GMII_PHY_PGSEL_PAGE3 0x0003 -+ #define GMII_PHY_PGSEL_PAGE5 0x0005 -+ -+struct ax88179_data { -+ u8 eee_enabled; -+ u8 eee_active; -+ u16 rxctl; -+ u16 reserved; -+}; -+ -+struct ax88179_int_data { -+ __le32 intdata1; -+ __le32 intdata2; -+}; -+ -+static const struct { -+ unsigned char ctrl, timer_l, timer_h, size, ifg; -+} AX88179_BULKIN_SIZE[] = { -+ {7, 0x4f, 0, 0x12, 0xff}, -+ {7, 0x20, 3, 0x16, 0xff}, -+ {7, 0xae, 7, 0x18, 0xff}, -+ {7, 0xcc, 0x4c, 0x18, 8}, -+}; -+ -+static int __ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, -+ u16 size, void *data, int in_pm) -+{ -+ int ret; -+ int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16); -+ -+ BUG_ON(!dev); -+ -+ if (!in_pm) -+ fn = usbnet_read_cmd; -+ else -+ fn = usbnet_read_cmd_nopm; -+ -+ ret = fn(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, -+ value, index, data, size); -+ -+ if (unlikely(ret < 0)) -+ netdev_warn(dev->net, "Failed to read reg index 0x%04x: %d\n", -+ index, ret); -+ -+ return ret; -+} -+ -+static int __ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, -+ u16 size, void *data, int in_pm) -+{ -+ int ret; -+ int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16); -+ -+ BUG_ON(!dev); -+ -+ if (!in_pm) -+ fn = usbnet_write_cmd; -+ else -+ fn = usbnet_write_cmd_nopm; -+ -+ ret = fn(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, -+ value, index, data, size); -+ -+ if (unlikely(ret < 0)) -+ netdev_warn(dev->net, "Failed to write reg index 0x%04x: %d\n", -+ index, ret); -+ -+ return ret; -+} -+ -+static void ax88179_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, -+ u16 index, u16 size, void *data) -+{ -+ u16 buf; -+ -+ if (2 == size) { -+ buf = *((u16 *)data); -+ cpu_to_le16s(&buf); -+ usbnet_write_cmd_async(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | -+ USB_RECIP_DEVICE, value, index, &buf, -+ size); -+ } else { -+ usbnet_write_cmd_async(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | -+ USB_RECIP_DEVICE, value, index, data, -+ size); -+ } -+} -+ -+static int ax88179_read_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value, -+ u16 index, u16 size, void *data) -+{ -+ int ret; -+ -+ if (2 == size) { -+ u16 buf; -+ ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf, 1); -+ le16_to_cpus(&buf); -+ *((u16 *)data) = buf; -+ } else if (4 == size) { -+ u32 buf; -+ ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf, 1); -+ le32_to_cpus(&buf); -+ *((u32 *)data) = buf; -+ } else { -+ ret = __ax88179_read_cmd(dev, cmd, value, index, size, data, 1); -+ } -+ -+ return ret; -+} -+ -+static int ax88179_write_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value, -+ u16 index, u16 size, void *data) -+{ -+ int ret; -+ -+ if (2 == size) { -+ u16 buf; -+ buf = *((u16 *)data); -+ cpu_to_le16s(&buf); -+ ret = __ax88179_write_cmd(dev, cmd, value, index, -+ size, &buf, 1); -+ } else { -+ ret = __ax88179_write_cmd(dev, cmd, value, index, -+ size, data, 1); -+ } -+ -+ return ret; -+} -+ -+static int ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, -+ u16 size, void *data) -+{ -+ int ret; -+ -+ if (2 == size) { -+ u16 buf; -+ ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf, 0); -+ le16_to_cpus(&buf); -+ *((u16 *)data) = buf; -+ } else if (4 == size) { -+ u32 buf; -+ ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf, 0); -+ le32_to_cpus(&buf); -+ *((u32 *)data) = buf; -+ } else { -+ ret = __ax88179_read_cmd(dev, cmd, value, index, size, data, 0); -+ } -+ -+ return ret; -+} -+ -+static int ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, -+ u16 size, void *data) -+{ -+ int ret; -+ -+ if (2 == size) { -+ u16 buf; -+ buf = *((u16 *)data); -+ cpu_to_le16s(&buf); -+ ret = __ax88179_write_cmd(dev, cmd, value, index, -+ size, &buf, 0); -+ } else { -+ ret = __ax88179_write_cmd(dev, cmd, value, index, -+ size, data, 0); -+ } -+ -+ return ret; -+} -+ -+static void ax88179_status(struct usbnet *dev, struct urb *urb) -+{ -+ struct ax88179_int_data *event; -+ u32 link; -+ -+ if (urb->actual_length < 8) -+ return; -+ -+ event = urb->transfer_buffer; -+ le32_to_cpus((void *)&event->intdata1); -+ -+ link = (((__force u32)event->intdata1) & AX_INT_PPLS_LINK) >> 16; -+ -+ if (netif_carrier_ok(dev->net) != link) { -+ usbnet_link_change(dev, link, 1); -+ netdev_info(dev->net, "ax88179 - Link status is: %d\n", link); -+ } -+} -+ -+static int ax88179_mdio_read(struct net_device *netdev, int phy_id, int loc) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ u16 res; -+ -+ ax88179_read_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, &res); -+ return res; -+} -+ -+static void ax88179_mdio_write(struct net_device *netdev, int phy_id, int loc, -+ int val) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ u16 res = (u16) val; -+ -+ ax88179_write_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, &res); -+} -+ -+static inline int ax88179_phy_mmd_indirect(struct usbnet *dev, u16 prtad, -+ u16 devad) -+{ -+ u16 tmp16; -+ int ret; -+ -+ tmp16 = devad; -+ ret = ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, -+ MII_MMD_CTRL, 2, &tmp16); -+ -+ tmp16 = prtad; -+ ret = ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, -+ MII_MMD_DATA, 2, &tmp16); -+ -+ tmp16 = devad | MII_MMD_CTRL_NOINCR; -+ ret = ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, -+ MII_MMD_CTRL, 2, &tmp16); -+ -+ return ret; -+} -+ -+static int -+ax88179_phy_read_mmd_indirect(struct usbnet *dev, u16 prtad, u16 devad) -+{ -+ int ret; -+ u16 tmp16; -+ -+ ax88179_phy_mmd_indirect(dev, prtad, devad); -+ -+ ret = ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, -+ MII_MMD_DATA, 2, &tmp16); -+ if (ret < 0) -+ return ret; -+ -+ return tmp16; -+} -+ -+static int -+ax88179_phy_write_mmd_indirect(struct usbnet *dev, u16 prtad, u16 devad, -+ u16 data) -+{ -+ int ret; -+ -+ ax88179_phy_mmd_indirect(dev, prtad, devad); -+ -+ ret = ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, -+ MII_MMD_DATA, 2, &data); -+ -+ if (ret < 0) -+ return ret; -+ -+ return 0; -+} -+ -+static int ax88179_suspend(struct usb_interface *intf, pm_message_t message) -+{ -+ struct usbnet *dev = usb_get_intfdata(intf); -+ u16 tmp16; -+ u8 tmp8; -+ -+ usbnet_suspend(intf, message); -+ -+ /* Disable RX path */ -+ ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, -+ 2, 2, &tmp16); -+ tmp16 &= ~AX_MEDIUM_RECEIVE_EN; -+ ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, -+ 2, 2, &tmp16); -+ -+ /* Force bulk-in zero length */ -+ ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, -+ 2, 2, &tmp16); -+ -+ tmp16 |= AX_PHYPWR_RSTCTL_BZ | AX_PHYPWR_RSTCTL_IPRL; -+ ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, -+ 2, 2, &tmp16); -+ -+ /* change clock */ -+ tmp8 = 0; -+ ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8); -+ -+ /* Configure RX control register => stop operation */ -+ tmp16 = AX_RX_CTL_STOP; -+ ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16); -+ -+ return 0; -+} -+ -+/* This function is used to enable the autodetach function. */ -+/* This function is determined by offset 0x43 of EEPROM */ -+static int ax88179_auto_detach(struct usbnet *dev, int in_pm) -+{ -+ u16 tmp16; -+ u8 tmp8; -+ int (*fnr)(struct usbnet *, u8, u16, u16, u16, void *); -+ int (*fnw)(struct usbnet *, u8, u16, u16, u16, void *); -+ -+ if (!in_pm) { -+ fnr = ax88179_read_cmd; -+ fnw = ax88179_write_cmd; -+ } else { -+ fnr = ax88179_read_cmd_nopm; -+ fnw = ax88179_write_cmd_nopm; -+ } -+ -+ if (fnr(dev, AX_ACCESS_EEPROM, 0x43, 1, 2, &tmp16) < 0) -+ return 0; -+ -+ if ((tmp16 == 0xFFFF) || (!(tmp16 & 0x0100))) -+ return 0; -+ -+ /* Enable Auto Detach bit */ -+ tmp8 = 0; -+ fnr(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8); -+ tmp8 |= AX_CLK_SELECT_ULR; -+ fnw(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8); -+ -+ fnr(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16); -+ tmp16 |= AX_PHYPWR_RSTCTL_AT; -+ fnw(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16); -+ -+ return 0; -+} -+ -+static int ax88179_resume(struct usb_interface *intf) -+{ -+ struct usbnet *dev = usb_get_intfdata(intf); -+ u16 tmp16; -+ u8 tmp8; -+ -+ usbnet_link_change(dev, 0, 0); -+ -+ /* Power up ethernet PHY */ -+ tmp16 = 0; -+ ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, -+ 2, 2, &tmp16); -+ udelay(1000); -+ -+ tmp16 = AX_PHYPWR_RSTCTL_IPRL; -+ ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, -+ 2, 2, &tmp16); -+ msleep(200); -+ -+ /* Ethernet PHY Auto Detach*/ -+ ax88179_auto_detach(dev, 1); -+ -+ /* Enable clock */ -+ ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8); -+ tmp8 |= AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS; -+ ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8); -+ msleep(100); -+ -+ /* Configure RX control register => start operation */ -+ tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START | -+ AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB; -+ ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16); -+ -+ return usbnet_resume(intf); -+} -+ -+static void -+ax88179_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ u8 opt; -+ -+ if (ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, -+ 1, 1, &opt) < 0) { -+ wolinfo->supported = 0; -+ wolinfo->wolopts = 0; -+ return; -+ } -+ -+ wolinfo->supported = WAKE_PHY | WAKE_MAGIC; -+ wolinfo->wolopts = 0; -+ if (opt & AX_MONITOR_MODE_RWLC) -+ wolinfo->wolopts |= WAKE_PHY; -+ if (opt & AX_MONITOR_MODE_RWMP) -+ wolinfo->wolopts |= WAKE_MAGIC; -+} -+ -+static int -+ax88179_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ u8 opt = 0; -+ -+ if (wolinfo->wolopts & WAKE_PHY) -+ opt |= AX_MONITOR_MODE_RWLC; -+ if (wolinfo->wolopts & WAKE_MAGIC) -+ opt |= AX_MONITOR_MODE_RWMP; -+ -+ if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, -+ 1, 1, &opt) < 0) -+ return -EINVAL; -+ -+ return 0; -+} -+ -+static int ax88179_get_eeprom_len(struct net_device *net) -+{ -+ return AX_EEPROM_LEN; -+} -+ -+static int -+ax88179_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, -+ u8 *data) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ u16 *eeprom_buff; -+ int first_word, last_word; -+ int i, ret; -+ -+ if (eeprom->len == 0) -+ return -EINVAL; -+ -+ eeprom->magic = AX88179_EEPROM_MAGIC; -+ -+ first_word = eeprom->offset >> 1; -+ last_word = (eeprom->offset + eeprom->len - 1) >> 1; -+ eeprom_buff = kmalloc(sizeof(u16) * (last_word - first_word + 1), -+ GFP_KERNEL); -+ if (!eeprom_buff) -+ return -ENOMEM; -+ -+ /* ax88179/178A returns 2 bytes from eeprom on read */ -+ for (i = first_word; i <= last_word; i++) { -+ ret = __ax88179_read_cmd(dev, AX_ACCESS_EEPROM, i, 1, 2, -+ &eeprom_buff[i - first_word], -+ 0); -+ if (ret < 0) { -+ kfree(eeprom_buff); -+ return -EIO; -+ } -+ } -+ -+ memcpy(data, (u8 *)eeprom_buff + (eeprom->offset & 1), eeprom->len); -+ kfree(eeprom_buff); -+ return 0; -+} -+ -+static int ax88179_get_settings(struct net_device *net, struct ethtool_cmd *cmd) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ return mii_ethtool_gset(&dev->mii, cmd); -+} -+ -+static int ax88179_set_settings(struct net_device *net, struct ethtool_cmd *cmd) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ return mii_ethtool_sset(&dev->mii, cmd); -+} -+ -+static int -+ax88179_ethtool_get_eee(struct usbnet *dev, struct ethtool_eee *data) -+{ -+ int val; -+ -+ /* Get Supported EEE */ -+ val = ax88179_phy_read_mmd_indirect(dev, MDIO_PCS_EEE_ABLE, -+ MDIO_MMD_PCS); -+ if (val < 0) -+ return val; -+ data->supported = mmd_eee_cap_to_ethtool_sup_t(val); -+ -+ /* Get advertisement EEE */ -+ val = ax88179_phy_read_mmd_indirect(dev, MDIO_AN_EEE_ADV, -+ MDIO_MMD_AN); -+ if (val < 0) -+ return val; -+ data->advertised = mmd_eee_adv_to_ethtool_adv_t(val); -+ -+ /* Get LP advertisement EEE */ -+ val = ax88179_phy_read_mmd_indirect(dev, MDIO_AN_EEE_LPABLE, -+ MDIO_MMD_AN); -+ if (val < 0) -+ return val; -+ data->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(val); -+ -+ return 0; -+} -+ -+static int -+ax88179_ethtool_set_eee(struct usbnet *dev, struct ethtool_eee *data) -+{ -+ u16 tmp16 = ethtool_adv_to_mmd_eee_adv_t(data->advertised); -+ -+ return ax88179_phy_write_mmd_indirect(dev, MDIO_AN_EEE_ADV, -+ MDIO_MMD_AN, tmp16); -+} -+ -+static int ax88179_chk_eee(struct usbnet *dev) -+{ -+ struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET }; -+ struct ax88179_data *priv = (struct ax88179_data *)dev->data; -+ -+ mii_ethtool_gset(&dev->mii, &ecmd); -+ -+ if (ecmd.duplex & DUPLEX_FULL) { -+ int eee_lp, eee_cap, eee_adv; -+ u32 lp, cap, adv, supported = 0; -+ -+ eee_cap = ax88179_phy_read_mmd_indirect(dev, -+ MDIO_PCS_EEE_ABLE, -+ MDIO_MMD_PCS); -+ if (eee_cap < 0) { -+ priv->eee_active = 0; -+ return false; -+ } -+ -+ cap = mmd_eee_cap_to_ethtool_sup_t(eee_cap); -+ if (!cap) { -+ priv->eee_active = 0; -+ return false; -+ } -+ -+ eee_lp = ax88179_phy_read_mmd_indirect(dev, -+ MDIO_AN_EEE_LPABLE, -+ MDIO_MMD_AN); -+ if (eee_lp < 0) { -+ priv->eee_active = 0; -+ return false; -+ } -+ -+ eee_adv = ax88179_phy_read_mmd_indirect(dev, -+ MDIO_AN_EEE_ADV, -+ MDIO_MMD_AN); -+ -+ if (eee_adv < 0) { -+ priv->eee_active = 0; -+ return false; -+ } -+ -+ adv = mmd_eee_adv_to_ethtool_adv_t(eee_adv); -+ lp = mmd_eee_adv_to_ethtool_adv_t(eee_lp); -+ supported = (ecmd.speed == SPEED_1000) ? -+ SUPPORTED_1000baseT_Full : -+ SUPPORTED_100baseT_Full; -+ -+ if (!(lp & adv & supported)) { -+ priv->eee_active = 0; -+ return false; -+ } -+ -+ priv->eee_active = 1; -+ return true; -+ } -+ -+ priv->eee_active = 0; -+ return false; -+} -+ -+static void ax88179_disable_eee(struct usbnet *dev) -+{ -+ u16 tmp16; -+ -+ tmp16 = GMII_PHY_PGSEL_PAGE3; -+ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, -+ GMII_PHY_PAGE_SELECT, 2, &tmp16); -+ -+ tmp16 = 0x3246; -+ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, -+ MII_PHYADDR, 2, &tmp16); -+ -+ tmp16 = GMII_PHY_PGSEL_PAGE0; -+ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, -+ GMII_PHY_PAGE_SELECT, 2, &tmp16); -+} -+ -+static void ax88179_enable_eee(struct usbnet *dev) -+{ -+ u16 tmp16; -+ -+ tmp16 = GMII_PHY_PGSEL_PAGE3; -+ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, -+ GMII_PHY_PAGE_SELECT, 2, &tmp16); -+ -+ tmp16 = 0x3247; -+ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, -+ MII_PHYADDR, 2, &tmp16); -+ -+ tmp16 = GMII_PHY_PGSEL_PAGE5; -+ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, -+ GMII_PHY_PAGE_SELECT, 2, &tmp16); -+ -+ tmp16 = 0x0680; -+ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, -+ MII_BMSR, 2, &tmp16); -+ -+ tmp16 = GMII_PHY_PGSEL_PAGE0; -+ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, -+ GMII_PHY_PAGE_SELECT, 2, &tmp16); -+} -+ -+static int ax88179_get_eee(struct net_device *net, struct ethtool_eee *edata) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ struct ax88179_data *priv = (struct ax88179_data *)dev->data; -+ -+ edata->eee_enabled = priv->eee_enabled; -+ edata->eee_active = priv->eee_active; -+ -+ return ax88179_ethtool_get_eee(dev, edata); -+} -+ -+static int ax88179_set_eee(struct net_device *net, struct ethtool_eee *edata) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ struct ax88179_data *priv = (struct ax88179_data *)dev->data; -+ int ret = -EOPNOTSUPP; -+ -+ priv->eee_enabled = edata->eee_enabled; -+ if (!priv->eee_enabled) { -+ ax88179_disable_eee(dev); -+ } else { -+ priv->eee_enabled = ax88179_chk_eee(dev); -+ if (!priv->eee_enabled) -+ return -EOPNOTSUPP; -+ -+ ax88179_enable_eee(dev); -+ } -+ -+ ret = ax88179_ethtool_set_eee(dev, edata); -+ if (ret) -+ return ret; -+ -+ mii_nway_restart(&dev->mii); -+ -+ usbnet_link_change(dev, 0, 0); -+ -+ return ret; -+} -+ -+static int ax88179_ioctl(struct net_device *net, struct ifreq *rq, int cmd) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); -+} -+ -+static const struct ethtool_ops ax88179_ethtool_ops = { -+ .get_link = ethtool_op_get_link, -+ .get_msglevel = usbnet_get_msglevel, -+ .set_msglevel = usbnet_set_msglevel, -+ .get_wol = ax88179_get_wol, -+ .set_wol = ax88179_set_wol, -+ .get_eeprom_len = ax88179_get_eeprom_len, -+ .get_eeprom = ax88179_get_eeprom, -+ .get_settings = ax88179_get_settings, -+ .set_settings = ax88179_set_settings, -+ .get_eee = ax88179_get_eee, -+ .set_eee = ax88179_set_eee, -+ .nway_reset = usbnet_nway_reset, -+}; -+ -+static void ax88179_set_multicast(struct net_device *net) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ struct ax88179_data *data = (struct ax88179_data *)dev->data; -+ u8 *m_filter = ((u8 *)dev->data) + 12; -+ -+ data->rxctl = (AX_RX_CTL_START | AX_RX_CTL_AB | AX_RX_CTL_IPE); -+ -+ if (net->flags & IFF_PROMISC) { -+ data->rxctl |= AX_RX_CTL_PRO; -+ } else if (net->flags & IFF_ALLMULTI || -+ netdev_mc_count(net) > AX_MAX_MCAST) { -+ data->rxctl |= AX_RX_CTL_AMALL; -+ } else if (netdev_mc_empty(net)) { -+ /* just broadcast and directed */ -+ } else { -+ /* We use the 20 byte dev->data for our 8 byte filter buffer -+ * to avoid allocating memory that is tricky to free later -+ */ -+ u32 crc_bits; -+ struct netdev_hw_addr *ha; -+ -+ memset(m_filter, 0, AX_MCAST_FLTSIZE); -+ -+ netdev_for_each_mc_addr(ha, net) { -+ crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26; -+ *(m_filter + (crc_bits >> 3)) |= (1 << (crc_bits & 7)); -+ } -+ -+ ax88179_write_cmd_async(dev, AX_ACCESS_MAC, AX_MULFLTARY, -+ AX_MCAST_FLTSIZE, AX_MCAST_FLTSIZE, -+ m_filter); -+ -+ data->rxctl |= AX_RX_CTL_AM; -+ } -+ -+ ax88179_write_cmd_async(dev, AX_ACCESS_MAC, AX_RX_CTL, -+ 2, 2, &data->rxctl); -+} -+ -+static int -+ax88179_set_features(struct net_device *net, netdev_features_t features) -+{ -+ u8 tmp; -+ struct usbnet *dev = netdev_priv(net); -+ netdev_features_t changed = net->features ^ features; -+ -+ if (changed & NETIF_F_IP_CSUM) { -+ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp); -+ tmp ^= AX_TXCOE_TCP | AX_TXCOE_UDP; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp); -+ } -+ -+ if (changed & NETIF_F_IPV6_CSUM) { -+ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp); -+ tmp ^= AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp); -+ } -+ -+ if (changed & NETIF_F_RXCSUM) { -+ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, &tmp); -+ tmp ^= AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP | -+ AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, &tmp); -+ } -+ -+ return 0; -+} -+ -+static int ax88179_change_mtu(struct net_device *net, int new_mtu) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ u16 tmp16; -+ -+ if (new_mtu <= 0 || new_mtu > 4088) -+ return -EINVAL; -+ -+ net->mtu = new_mtu; -+ dev->hard_mtu = net->mtu + net->hard_header_len; -+ -+ if (net->mtu > 1500) { -+ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, -+ 2, 2, &tmp16); -+ tmp16 |= AX_MEDIUM_JUMBO_EN; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, -+ 2, 2, &tmp16); -+ } else { -+ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, -+ 2, 2, &tmp16); -+ tmp16 &= ~AX_MEDIUM_JUMBO_EN; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, -+ 2, 2, &tmp16); -+ } -+ -+ /* max qlen depend on hard_mtu and rx_urb_size */ -+ usbnet_update_max_qlen(dev); -+ -+ return 0; -+} -+ -+static int ax88179_set_mac_addr(struct net_device *net, void *p) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ struct sockaddr *addr = p; -+ int ret; -+ -+ if (netif_running(net)) -+ return -EBUSY; -+ if (!is_valid_ether_addr(addr->sa_data)) -+ return -EADDRNOTAVAIL; -+ -+ memcpy(net->dev_addr, addr->sa_data, ETH_ALEN); -+ -+ /* Set the MAC address */ -+ ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, -+ ETH_ALEN, net->dev_addr); -+ if (ret < 0) -+ return ret; -+ -+ return 0; -+} -+ -+static const struct net_device_ops ax88179_netdev_ops = { -+ .ndo_open = usbnet_open, -+ .ndo_stop = usbnet_stop, -+ .ndo_start_xmit = usbnet_start_xmit, -+ .ndo_tx_timeout = usbnet_tx_timeout, -+ .ndo_change_mtu = ax88179_change_mtu, -+ .ndo_set_mac_address = ax88179_set_mac_addr, -+ .ndo_validate_addr = eth_validate_addr, -+ .ndo_do_ioctl = ax88179_ioctl, -+ .ndo_set_rx_mode = ax88179_set_multicast, -+ .ndo_set_features = ax88179_set_features, -+}; -+ -+static int ax88179_check_eeprom(struct usbnet *dev) -+{ -+ u8 i, buf, eeprom[20]; -+ u16 csum, delay = HZ / 10; -+ unsigned long jtimeout; -+ -+ /* Read EEPROM content */ -+ for (i = 0; i < 6; i++) { -+ buf = i; -+ if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_ADDR, -+ 1, 1, &buf) < 0) -+ return -EINVAL; -+ -+ buf = EEP_RD; -+ if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD, -+ 1, 1, &buf) < 0) -+ return -EINVAL; -+ -+ jtimeout = jiffies + delay; -+ do { -+ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD, -+ 1, 1, &buf); -+ -+ if (time_after(jiffies, jtimeout)) -+ return -EINVAL; -+ -+ } while (buf & EEP_BUSY); -+ -+ __ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_LOW, -+ 2, 2, &eeprom[i * 2], 0); -+ -+ if ((i == 0) && (eeprom[0] == 0xFF)) -+ return -EINVAL; -+ } -+ -+ csum = eeprom[6] + eeprom[7] + eeprom[8] + eeprom[9]; -+ csum = (csum >> 8) + (csum & 0xff); -+ if ((csum + eeprom[10]) != 0xff) -+ return -EINVAL; -+ -+ return 0; -+} -+ -+static int ax88179_check_efuse(struct usbnet *dev, u16 *ledmode) -+{ -+ u8 i; -+ u8 efuse[64]; -+ u16 csum = 0; -+ -+ if (ax88179_read_cmd(dev, AX_ACCESS_EFUS, 0, 64, 64, efuse) < 0) -+ return -EINVAL; -+ -+ if (*efuse == 0xFF) -+ return -EINVAL; -+ -+ for (i = 0; i < 64; i++) -+ csum = csum + efuse[i]; -+ -+ while (csum > 255) -+ csum = (csum & 0x00FF) + ((csum >> 8) & 0x00FF); -+ -+ if (csum != 0xFF) -+ return -EINVAL; -+ -+ *ledmode = (efuse[51] << 8) | efuse[52]; -+ -+ return 0; -+} -+ -+static int ax88179_convert_old_led(struct usbnet *dev, u16 *ledvalue) -+{ -+ u16 led; -+ -+ /* Loaded the old eFuse LED Mode */ -+ if (ax88179_read_cmd(dev, AX_ACCESS_EEPROM, 0x3C, 1, 2, &led) < 0) -+ return -EINVAL; -+ -+ led >>= 8; -+ switch (led) { -+ case 0xFF: -+ led = LED0_ACTIVE | LED1_LINK_10 | LED1_LINK_100 | -+ LED1_LINK_1000 | LED2_ACTIVE | LED2_LINK_10 | -+ LED2_LINK_100 | LED2_LINK_1000 | LED_VALID; -+ break; -+ case 0xFE: -+ led = LED0_ACTIVE | LED1_LINK_1000 | LED2_LINK_100 | LED_VALID; -+ break; -+ case 0xFD: -+ led = LED0_ACTIVE | LED1_LINK_1000 | LED2_LINK_100 | -+ LED2_LINK_10 | LED_VALID; -+ break; -+ case 0xFC: -+ led = LED0_ACTIVE | LED1_ACTIVE | LED1_LINK_1000 | LED2_ACTIVE | -+ LED2_LINK_100 | LED2_LINK_10 | LED_VALID; -+ break; -+ default: -+ led = LED0_ACTIVE | LED1_LINK_10 | LED1_LINK_100 | -+ LED1_LINK_1000 | LED2_ACTIVE | LED2_LINK_10 | -+ LED2_LINK_100 | LED2_LINK_1000 | LED_VALID; -+ break; -+ } -+ -+ *ledvalue = led; -+ -+ return 0; -+} -+ -+static int ax88179_led_setting(struct usbnet *dev) -+{ -+ u8 ledfd, value = 0; -+ u16 tmp, ledact, ledlink, ledvalue = 0, delay = HZ / 10; -+ unsigned long jtimeout; -+ -+ /* Check AX88179 version. UA1 or UA2*/ -+ ax88179_read_cmd(dev, AX_ACCESS_MAC, GENERAL_STATUS, 1, 1, &value); -+ -+ if (!(value & AX_SECLD)) { /* UA1 */ -+ value = AX_GPIO_CTRL_GPIO3EN | AX_GPIO_CTRL_GPIO2EN | -+ AX_GPIO_CTRL_GPIO1EN; -+ if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_GPIO_CTRL, -+ 1, 1, &value) < 0) -+ return -EINVAL; -+ } -+ -+ /* Check EEPROM */ -+ if (!ax88179_check_eeprom(dev)) { -+ value = 0x42; -+ if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_ADDR, -+ 1, 1, &value) < 0) -+ return -EINVAL; -+ -+ value = EEP_RD; -+ if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD, -+ 1, 1, &value) < 0) -+ return -EINVAL; -+ -+ jtimeout = jiffies + delay; -+ do { -+ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD, -+ 1, 1, &value); -+ -+ if (time_after(jiffies, jtimeout)) -+ return -EINVAL; -+ -+ } while (value & EEP_BUSY); -+ -+ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_HIGH, -+ 1, 1, &value); -+ ledvalue = (value << 8); -+ -+ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_LOW, -+ 1, 1, &value); -+ ledvalue |= value; -+ -+ /* load internal ROM for defaule setting */ -+ if ((ledvalue == 0xFFFF) || ((ledvalue & LED_VALID) == 0)) -+ ax88179_convert_old_led(dev, &ledvalue); -+ -+ } else if (!ax88179_check_efuse(dev, &ledvalue)) { -+ if ((ledvalue == 0xFFFF) || ((ledvalue & LED_VALID) == 0)) -+ ax88179_convert_old_led(dev, &ledvalue); -+ } else { -+ ax88179_convert_old_led(dev, &ledvalue); -+ } -+ -+ tmp = GMII_PHY_PGSEL_EXT; -+ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, -+ GMII_PHY_PAGE_SELECT, 2, &tmp); -+ -+ tmp = 0x2c; -+ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, -+ GMII_PHYPAGE, 2, &tmp); -+ -+ ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, -+ GMII_LED_ACT, 2, &ledact); -+ -+ ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, -+ GMII_LED_LINK, 2, &ledlink); -+ -+ ledact &= GMII_LED_ACTIVE_MASK; -+ ledlink &= GMII_LED_LINK_MASK; -+ -+ if (ledvalue & LED0_ACTIVE) -+ ledact |= GMII_LED0_ACTIVE; -+ -+ if (ledvalue & LED1_ACTIVE) -+ ledact |= GMII_LED1_ACTIVE; -+ -+ if (ledvalue & LED2_ACTIVE) -+ ledact |= GMII_LED2_ACTIVE; -+ -+ if (ledvalue & LED0_LINK_10) -+ ledlink |= GMII_LED0_LINK_10; -+ -+ if (ledvalue & LED1_LINK_10) -+ ledlink |= GMII_LED1_LINK_10; -+ -+ if (ledvalue & LED2_LINK_10) -+ ledlink |= GMII_LED2_LINK_10; -+ -+ if (ledvalue & LED0_LINK_100) -+ ledlink |= GMII_LED0_LINK_100; -+ -+ if (ledvalue & LED1_LINK_100) -+ ledlink |= GMII_LED1_LINK_100; -+ -+ if (ledvalue & LED2_LINK_100) -+ ledlink |= GMII_LED2_LINK_100; -+ -+ if (ledvalue & LED0_LINK_1000) -+ ledlink |= GMII_LED0_LINK_1000; -+ -+ if (ledvalue & LED1_LINK_1000) -+ ledlink |= GMII_LED1_LINK_1000; -+ -+ if (ledvalue & LED2_LINK_1000) -+ ledlink |= GMII_LED2_LINK_1000; -+ -+ tmp = ledact; -+ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, -+ GMII_LED_ACT, 2, &tmp); -+ -+ tmp = ledlink; -+ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, -+ GMII_LED_LINK, 2, &tmp); -+ -+ tmp = GMII_PHY_PGSEL_PAGE0; -+ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, -+ GMII_PHY_PAGE_SELECT, 2, &tmp); -+ -+ /* LED full duplex setting */ -+ ledfd = 0; -+ if (ledvalue & LED0_FD) -+ ledfd |= 0x01; -+ else if ((ledvalue & LED0_USB3_MASK) == 0) -+ ledfd |= 0x02; -+ -+ if (ledvalue & LED1_FD) -+ ledfd |= 0x04; -+ else if ((ledvalue & LED1_USB3_MASK) == 0) -+ ledfd |= 0x08; -+ -+ if (ledvalue & LED2_FD) -+ ledfd |= 0x10; -+ else if ((ledvalue & LED2_USB3_MASK) == 0) -+ ledfd |= 0x20; -+ -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_LEDCTRL, 1, 1, &ledfd); -+ -+ return 0; -+} -+ -+static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf) -+{ -+ u8 buf[5]; -+ u16 *tmp16; -+ u8 *tmp; -+ struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data; -+ struct ethtool_eee eee_data; -+ -+ usbnet_get_endpoints(dev, intf); -+ -+ tmp16 = (u16 *)buf; -+ tmp = (u8 *)buf; -+ -+ memset(ax179_data, 0, sizeof(*ax179_data)); -+ -+ /* Power up ethernet PHY */ -+ *tmp16 = 0; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16); -+ *tmp16 = AX_PHYPWR_RSTCTL_IPRL; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16); -+ msleep(200); -+ -+ *tmp = AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, tmp); -+ msleep(100); -+ -+ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, -+ ETH_ALEN, dev->net->dev_addr); -+ memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN); -+ -+ /* RX bulk configuration */ -+ memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5); -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp); -+ -+ dev->rx_urb_size = 1024 * 20; -+ -+ *tmp = 0x34; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp); -+ -+ *tmp = 0x52; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH, -+ 1, 1, tmp); -+ -+ dev->net->netdev_ops = &ax88179_netdev_ops; -+ dev->net->ethtool_ops = &ax88179_ethtool_ops; -+ dev->net->needed_headroom = 8; -+ -+ /* Initialize MII structure */ -+ dev->mii.dev = dev->net; -+ dev->mii.mdio_read = ax88179_mdio_read; -+ dev->mii.mdio_write = ax88179_mdio_write; -+ dev->mii.phy_id_mask = 0xff; -+ dev->mii.reg_num_mask = 0xff; -+ dev->mii.phy_id = 0x03; -+ dev->mii.supports_gmii = 1; -+ -+ dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | -+ NETIF_F_RXCSUM; -+ -+ dev->net->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | -+ NETIF_F_RXCSUM; -+ -+ /* Enable checksum offload */ -+ *tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP | -+ AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, tmp); -+ -+ *tmp = AX_TXCOE_IP | AX_TXCOE_TCP | AX_TXCOE_UDP | -+ AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, tmp); -+ -+ /* Configure RX control register => start operation */ -+ *tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START | -+ AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, tmp16); -+ -+ *tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL | -+ AX_MONITOR_MODE_RWMP; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, tmp); -+ -+ /* Configure default medium type => giga */ -+ *tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN | -+ AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_FULL_DUPLEX | -+ AX_MEDIUM_GIGAMODE; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, -+ 2, 2, tmp16); -+ -+ ax88179_led_setting(dev); -+ -+ ax179_data->eee_enabled = 0; -+ ax179_data->eee_active = 0; -+ -+ ax88179_disable_eee(dev); -+ -+ ax88179_ethtool_get_eee(dev, &eee_data); -+ eee_data.advertised = 0; -+ ax88179_ethtool_set_eee(dev, &eee_data); -+ -+ /* Restart autoneg */ -+ mii_nway_restart(&dev->mii); -+ -+ usbnet_link_change(dev, 0, 0); -+ -+ return 0; -+} -+ -+static void ax88179_unbind(struct usbnet *dev, struct usb_interface *intf) -+{ -+ u16 tmp16; -+ -+ /* Configure RX control register => stop operation */ -+ tmp16 = AX_RX_CTL_STOP; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16); -+ -+ tmp16 = 0; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp16); -+ -+ /* Power down ethernet PHY */ -+ tmp16 = 0; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16); -+} -+ -+static void -+ax88179_rx_checksum(struct sk_buff *skb, u32 *pkt_hdr) -+{ -+ skb->ip_summed = CHECKSUM_NONE; -+ -+ /* checksum error bit is set */ -+ if ((*pkt_hdr & AX_RXHDR_L3CSUM_ERR) || -+ (*pkt_hdr & AX_RXHDR_L4CSUM_ERR)) -+ return; -+ -+ /* It must be a TCP or UDP packet with a valid checksum */ -+ if (((*pkt_hdr & AX_RXHDR_L4_TYPE_MASK) == AX_RXHDR_L4_TYPE_TCP) || -+ ((*pkt_hdr & AX_RXHDR_L4_TYPE_MASK) == AX_RXHDR_L4_TYPE_UDP)) -+ skb->ip_summed = CHECKSUM_UNNECESSARY; -+} -+ -+static int ax88179_rx_fixup(struct usbnet *dev, struct sk_buff *skb) -+{ -+ struct sk_buff *ax_skb; -+ int pkt_cnt; -+ u32 rx_hdr; -+ u16 hdr_off; -+ u32 *pkt_hdr; -+ -+ /* This check is no longer done by usbnet */ -+ if (skb->len < dev->net->hard_header_len) -+ return 0; -+ -+ skb_trim(skb, skb->len - 4); -+ memcpy(&rx_hdr, skb_tail_pointer(skb), 4); -+ le32_to_cpus(&rx_hdr); -+ -+ pkt_cnt = (u16)rx_hdr; -+ hdr_off = (u16)(rx_hdr >> 16); -+ pkt_hdr = (u32 *)(skb->data + hdr_off); -+ -+ while (pkt_cnt--) { -+ u16 pkt_len; -+ -+ le32_to_cpus(pkt_hdr); -+ pkt_len = (*pkt_hdr >> 16) & 0x1fff; -+ -+ /* Check CRC or runt packet */ -+ if ((*pkt_hdr & AX_RXHDR_CRC_ERR) || -+ (*pkt_hdr & AX_RXHDR_DROP_ERR)) { -+ skb_pull(skb, (pkt_len + 7) & 0xFFF8); -+ pkt_hdr++; -+ continue; -+ } -+ -+ if (pkt_cnt == 0) { -+ /* Skip IP alignment psudo header */ -+ skb_pull(skb, 2); -+ skb->len = pkt_len; -+ skb_set_tail_pointer(skb, pkt_len); -+ skb->truesize = pkt_len + sizeof(struct sk_buff); -+ ax88179_rx_checksum(skb, pkt_hdr); -+ return 1; -+ } -+ -+ ax_skb = skb_clone(skb, GFP_ATOMIC); -+ if (ax_skb) { -+ ax_skb->len = pkt_len; -+ ax_skb->data = skb->data + 2; -+ skb_set_tail_pointer(ax_skb, pkt_len); -+ ax_skb->truesize = pkt_len + sizeof(struct sk_buff); -+ ax88179_rx_checksum(ax_skb, pkt_hdr); -+ usbnet_skb_return(dev, ax_skb); -+ } else { -+ return 0; -+ } -+ -+ skb_pull(skb, (pkt_len + 7) & 0xFFF8); -+ pkt_hdr++; -+ } -+ return 1; -+} -+ -+static struct sk_buff * -+ax88179_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) -+{ -+ u32 tx_hdr1, tx_hdr2; -+ int frame_size = dev->maxpacket; -+ int mss = skb_shinfo(skb)->gso_size; -+ int headroom; -+ -+ tx_hdr1 = skb->len; -+ tx_hdr2 = mss; -+ if (((skb->len + 8) % frame_size) == 0) -+ tx_hdr2 |= 0x80008000; /* Enable padding */ -+ -+ headroom = skb_headroom(skb) - 8; -+ -+ if ((skb_header_cloned(skb) || headroom < 0) && -+ pskb_expand_head(skb, headroom < 0 ? 8 : 0, 0, GFP_ATOMIC)) { -+ dev_kfree_skb_any(skb); -+ return NULL; -+ } -+ -+ skb_push(skb, 4); -+ cpu_to_le32s(&tx_hdr2); -+ skb_copy_to_linear_data(skb, &tx_hdr2, 4); -+ -+ skb_push(skb, 4); -+ cpu_to_le32s(&tx_hdr1); -+ skb_copy_to_linear_data(skb, &tx_hdr1, 4); -+ -+ return skb; -+} -+ -+static int ax88179_link_reset(struct usbnet *dev) -+{ -+ struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data; -+ u8 tmp[5], link_sts; -+ u16 mode, tmp16, delay = HZ / 10; -+ u32 tmp32 = 0x40000000; -+ unsigned long jtimeout; -+ -+ jtimeout = jiffies + delay; -+ while (tmp32 & 0x40000000) { -+ mode = 0; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &mode); -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, -+ &ax179_data->rxctl); -+ -+ /*link up, check the usb device control TX FIFO full or empty*/ -+ ax88179_read_cmd(dev, 0x81, 0x8c, 0, 4, &tmp32); -+ -+ if (time_after(jiffies, jtimeout)) -+ return 0; -+ } -+ -+ mode = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN | -+ AX_MEDIUM_RXFLOW_CTRLEN; -+ -+ ax88179_read_cmd(dev, AX_ACCESS_MAC, PHYSICAL_LINK_STATUS, -+ 1, 1, &link_sts); -+ -+ ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, -+ GMII_PHY_PHYSR, 2, &tmp16); -+ -+ if (!(tmp16 & GMII_PHY_PHYSR_LINK)) { -+ return 0; -+ } else if (GMII_PHY_PHYSR_GIGA == (tmp16 & GMII_PHY_PHYSR_SMASK)) { -+ mode |= AX_MEDIUM_GIGAMODE | AX_MEDIUM_EN_125MHZ; -+ if (dev->net->mtu > 1500) -+ mode |= AX_MEDIUM_JUMBO_EN; -+ -+ if (link_sts & AX_USB_SS) -+ memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5); -+ else if (link_sts & AX_USB_HS) -+ memcpy(tmp, &AX88179_BULKIN_SIZE[1], 5); -+ else -+ memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5); -+ } else if (GMII_PHY_PHYSR_100 == (tmp16 & GMII_PHY_PHYSR_SMASK)) { -+ mode |= AX_MEDIUM_PS; -+ -+ if (link_sts & (AX_USB_SS | AX_USB_HS)) -+ memcpy(tmp, &AX88179_BULKIN_SIZE[2], 5); -+ else -+ memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5); -+ } else { -+ memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5); -+ } -+ -+ /* RX bulk configuration */ -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp); -+ -+ dev->rx_urb_size = (1024 * (tmp[3] + 2)); -+ -+ if (tmp16 & GMII_PHY_PHYSR_FULL) -+ mode |= AX_MEDIUM_FULL_DUPLEX; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, -+ 2, 2, &mode); -+ -+ ax179_data->eee_enabled = ax88179_chk_eee(dev); -+ -+ netif_carrier_on(dev->net); -+ -+ return 0; -+} -+ -+static int ax88179_reset(struct usbnet *dev) -+{ -+ u8 buf[5]; -+ u16 *tmp16; -+ u8 *tmp; -+ struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data; -+ struct ethtool_eee eee_data; -+ -+ tmp16 = (u16 *)buf; -+ tmp = (u8 *)buf; -+ -+ /* Power up ethernet PHY */ -+ *tmp16 = 0; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16); -+ -+ *tmp16 = AX_PHYPWR_RSTCTL_IPRL; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16); -+ msleep(200); -+ -+ *tmp = AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, tmp); -+ msleep(100); -+ -+ /* Ethernet PHY Auto Detach*/ -+ ax88179_auto_detach(dev, 0); -+ -+ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, ETH_ALEN, -+ dev->net->dev_addr); -+ memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN); -+ -+ /* RX bulk configuration */ -+ memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5); -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp); -+ -+ dev->rx_urb_size = 1024 * 20; -+ -+ *tmp = 0x34; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp); -+ -+ *tmp = 0x52; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH, -+ 1, 1, tmp); -+ -+ dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | -+ NETIF_F_RXCSUM; -+ -+ dev->net->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | -+ NETIF_F_RXCSUM; -+ -+ /* Enable checksum offload */ -+ *tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP | -+ AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, tmp); -+ -+ *tmp = AX_TXCOE_IP | AX_TXCOE_TCP | AX_TXCOE_UDP | -+ AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, tmp); -+ -+ /* Configure RX control register => start operation */ -+ *tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START | -+ AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, tmp16); -+ -+ *tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL | -+ AX_MONITOR_MODE_RWMP; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, tmp); -+ -+ /* Configure default medium type => giga */ -+ *tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN | -+ AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_FULL_DUPLEX | -+ AX_MEDIUM_GIGAMODE; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, -+ 2, 2, tmp16); -+ -+ ax88179_led_setting(dev); -+ -+ ax179_data->eee_enabled = 0; -+ ax179_data->eee_active = 0; -+ -+ ax88179_disable_eee(dev); -+ -+ ax88179_ethtool_get_eee(dev, &eee_data); -+ eee_data.advertised = 0; -+ ax88179_ethtool_set_eee(dev, &eee_data); -+ -+ /* Restart autoneg */ -+ mii_nway_restart(&dev->mii); -+ -+ usbnet_link_change(dev, 0, 0); -+ -+ return 0; -+} -+ -+static int ax88179_stop(struct usbnet *dev) -+{ -+ u16 tmp16; -+ -+ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, -+ 2, 2, &tmp16); -+ tmp16 &= ~AX_MEDIUM_RECEIVE_EN; -+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, -+ 2, 2, &tmp16); -+ -+ return 0; -+} -+ -+static const struct driver_info ax88179_info = { -+ .description = "ASIX AX88179 USB 3.0 Gigabit Ethernet", -+ .bind = ax88179_bind, -+ .unbind = ax88179_unbind, -+ .status = ax88179_status, -+ .link_reset = ax88179_link_reset, -+ .reset = ax88179_reset, -+ .stop = ax88179_stop, -+ .flags = FLAG_ETHER | FLAG_FRAMING_AX, -+ .rx_fixup = ax88179_rx_fixup, -+ .tx_fixup = ax88179_tx_fixup, -+}; -+ -+static const struct driver_info ax88178a_info = { -+ .description = "ASIX AX88178A USB 2.0 Gigabit Ethernet", -+ .bind = ax88179_bind, -+ .unbind = ax88179_unbind, -+ .status = ax88179_status, -+ .link_reset = ax88179_link_reset, -+ .reset = ax88179_reset, -+ .stop = ax88179_stop, -+ .flags = FLAG_ETHER | FLAG_FRAMING_AX, -+ .rx_fixup = ax88179_rx_fixup, -+ .tx_fixup = ax88179_tx_fixup, -+}; -+ -+static const struct driver_info dlink_dub1312_info = { -+ .description = "D-Link DUB-1312 USB 3.0 to Gigabit Ethernet Adapter", -+ .bind = ax88179_bind, -+ .unbind = ax88179_unbind, -+ .status = ax88179_status, -+ .link_reset = ax88179_link_reset, -+ .reset = ax88179_reset, -+ .stop = ax88179_stop, -+ .flags = FLAG_ETHER | FLAG_FRAMING_AX, -+ .rx_fixup = ax88179_rx_fixup, -+ .tx_fixup = ax88179_tx_fixup, -+}; -+ -+static const struct driver_info sitecom_info = { -+ .description = "Sitecom USB 3.0 to Gigabit Adapter", -+ .bind = ax88179_bind, -+ .unbind = ax88179_unbind, -+ .status = ax88179_status, -+ .link_reset = ax88179_link_reset, -+ .reset = ax88179_reset, -+ .stop = ax88179_stop, -+ .flags = FLAG_ETHER | FLAG_FRAMING_AX, -+ .rx_fixup = ax88179_rx_fixup, -+ .tx_fixup = ax88179_tx_fixup, -+}; -+ -+static const struct driver_info samsung_info = { -+ .description = "Samsung USB Ethernet Adapter", -+ .bind = ax88179_bind, -+ .unbind = ax88179_unbind, -+ .status = ax88179_status, -+ .link_reset = ax88179_link_reset, -+ .reset = ax88179_reset, -+ .stop = ax88179_stop, -+ .flags = FLAG_ETHER | FLAG_FRAMING_AX, -+ .rx_fixup = ax88179_rx_fixup, -+ .tx_fixup = ax88179_tx_fixup, -+}; -+ -+static const struct driver_info lenovo_info = { -+ .description = "Lenovo OneLinkDock Gigabit LAN", -+ .bind = ax88179_bind, -+ .unbind = ax88179_unbind, -+ .status = ax88179_status, -+ .link_reset = ax88179_link_reset, -+ .reset = ax88179_reset, -+ .stop = ax88179_stop, -+ .flags = FLAG_ETHER | FLAG_FRAMING_AX, -+ .rx_fixup = ax88179_rx_fixup, -+ .tx_fixup = ax88179_tx_fixup, -+}; -+ -+static const struct usb_device_id products[] = { -+{ -+ /* ASIX AX88179 10/100/1000 */ -+ USB_DEVICE(0x0b95, 0x1790), -+ .driver_info = (unsigned long)&ax88179_info, -+}, { -+ /* ASIX AX88178A 10/100/1000 */ -+ USB_DEVICE(0x0b95, 0x178a), -+ .driver_info = (unsigned long)&ax88178a_info, -+}, { -+ /* D-Link DUB-1312 USB 3.0 to Gigabit Ethernet Adapter */ -+ USB_DEVICE(0x2001, 0x4a00), -+ .driver_info = (unsigned long)&dlink_dub1312_info, -+}, { -+ /* Sitecom USB 3.0 to Gigabit Adapter */ -+ USB_DEVICE(0x0df6, 0x0072), -+ .driver_info = (unsigned long)&sitecom_info, -+}, { -+ /* Samsung USB Ethernet Adapter */ -+ USB_DEVICE(0x04e8, 0xa100), -+ .driver_info = (unsigned long)&samsung_info, -+}, { -+ /* Lenovo OneLinkDock Gigabit LAN */ -+ USB_DEVICE(0x17ef, 0x304b), -+ .driver_info = (unsigned long)&lenovo_info, -+}, -+ { }, -+}; -+MODULE_DEVICE_TABLE(usb, products); -+ -+static struct usb_driver ax88179_178a_driver = { -+ .name = "ax88179_178a", -+ .id_table = products, -+ .probe = usbnet_probe, -+ .suspend = ax88179_suspend, -+ .resume = ax88179_resume, -+ .reset_resume = ax88179_resume, -+ .disconnect = usbnet_disconnect, -+ .supports_autosuspend = 1, -+ .disable_hub_initiated_lpm = 1, -+}; -+ -+module_usb_driver(ax88179_178a_driver); -+ -+MODULE_DESCRIPTION("ASIX AX88179/178A based USB 3.0/2.0 Gigabit Ethernet Devices"); -+MODULE_LICENSE("GPL"); -diff -Naur backports-4.2.6-1.org/drivers/net/usb/catc.c backports-4.2.6-1/drivers/net/usb/catc.c ---- backports-4.2.6-1.org/drivers/net/usb/catc.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/catc.c 2016-06-28 14:35:17.975307221 +0200 -@@ -0,0 +1,965 @@ -+/* -+ * Copyright (c) 2001 Vojtech Pavlik -+ * -+ * CATC EL1210A NetMate USB Ethernet driver -+ * -+ * Sponsored by SuSE -+ * -+ * Based on the work of -+ * Donald Becker -+ * -+ * Old chipset support added by Simon Evans 2002 -+ * - adds support for Belkin F5U011 -+ */ -+ -+/* -+ * 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, see . -+ * -+ * Should you need to contact me, the author, you can do so either by -+ * e-mail - mail your message to , or by paper mail: -+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#undef DEBUG -+ -+#include -+ -+/* -+ * Version information. -+ */ -+ -+#define DRIVER_VERSION "v2.8" -+#define DRIVER_AUTHOR "Vojtech Pavlik " -+#define DRIVER_DESC "CATC EL1210A NetMate USB Ethernet driver" -+#define SHORT_DRIVER_DESC "EL1210A NetMate USB Ethernet" -+ -+MODULE_AUTHOR(DRIVER_AUTHOR); -+MODULE_DESCRIPTION(DRIVER_DESC); -+MODULE_LICENSE("GPL"); -+ -+static const char driver_name[] = "catc"; -+ -+/* -+ * Some defines. -+ */ -+ -+#define STATS_UPDATE (HZ) /* Time between stats updates */ -+#define TX_TIMEOUT (5*HZ) /* Max time the queue can be stopped */ -+#define PKT_SZ 1536 /* Max Ethernet packet size */ -+#define RX_MAX_BURST 15 /* Max packets per rx buffer (> 0, < 16) */ -+#define TX_MAX_BURST 15 /* Max full sized packets per tx buffer (> 0) */ -+#define CTRL_QUEUE 16 /* Max control requests in flight (power of two) */ -+#define RX_PKT_SZ 1600 /* Max size of receive packet for F5U011 */ -+ -+/* -+ * Control requests. -+ */ -+ -+enum control_requests { -+ ReadMem = 0xf1, -+ GetMac = 0xf2, -+ Reset = 0xf4, -+ SetMac = 0xf5, -+ SetRxMode = 0xf5, /* F5U011 only */ -+ WriteROM = 0xf8, -+ SetReg = 0xfa, -+ GetReg = 0xfb, -+ WriteMem = 0xfc, -+ ReadROM = 0xfd, -+}; -+ -+/* -+ * Registers. -+ */ -+ -+enum register_offsets { -+ TxBufCount = 0x20, -+ RxBufCount = 0x21, -+ OpModes = 0x22, -+ TxQed = 0x23, -+ RxQed = 0x24, -+ MaxBurst = 0x25, -+ RxUnit = 0x60, -+ EthStatus = 0x61, -+ StationAddr0 = 0x67, -+ EthStats = 0x69, -+ LEDCtrl = 0x81, -+}; -+ -+enum eth_stats { -+ TxSingleColl = 0x00, -+ TxMultiColl = 0x02, -+ TxExcessColl = 0x04, -+ RxFramErr = 0x06, -+}; -+ -+enum op_mode_bits { -+ Op3MemWaits = 0x03, -+ OpLenInclude = 0x08, -+ OpRxMerge = 0x10, -+ OpTxMerge = 0x20, -+ OpWin95bugfix = 0x40, -+ OpLoopback = 0x80, -+}; -+ -+enum rx_filter_bits { -+ RxEnable = 0x01, -+ RxPolarity = 0x02, -+ RxForceOK = 0x04, -+ RxMultiCast = 0x08, -+ RxPromisc = 0x10, -+ AltRxPromisc = 0x20, /* F5U011 uses different bit */ -+}; -+ -+enum led_values { -+ LEDFast = 0x01, -+ LEDSlow = 0x02, -+ LEDFlash = 0x03, -+ LEDPulse = 0x04, -+ LEDLink = 0x08, -+}; -+ -+enum link_status { -+ LinkNoChange = 0, -+ LinkGood = 1, -+ LinkBad = 2 -+}; -+ -+/* -+ * The catc struct. -+ */ -+ -+#define CTRL_RUNNING 0 -+#define RX_RUNNING 1 -+#define TX_RUNNING 2 -+ -+struct catc { -+ struct net_device *netdev; -+ struct usb_device *usbdev; -+ -+ unsigned long flags; -+ -+ unsigned int tx_ptr, tx_idx; -+ unsigned int ctrl_head, ctrl_tail; -+ spinlock_t tx_lock, ctrl_lock; -+ -+ u8 tx_buf[2][TX_MAX_BURST * (PKT_SZ + 2)]; -+ u8 rx_buf[RX_MAX_BURST * (PKT_SZ + 2)]; -+ u8 irq_buf[2]; -+ u8 ctrl_buf[64]; -+ struct usb_ctrlrequest ctrl_dr; -+ -+ struct timer_list timer; -+ u8 stats_buf[8]; -+ u16 stats_vals[4]; -+ unsigned long last_stats; -+ -+ u8 multicast[64]; -+ -+ struct ctrl_queue { -+ u8 dir; -+ u8 request; -+ u16 value; -+ u16 index; -+ void *buf; -+ int len; -+ void (*callback)(struct catc *catc, struct ctrl_queue *q); -+ } ctrl_queue[CTRL_QUEUE]; -+ -+ struct urb *tx_urb, *rx_urb, *irq_urb, *ctrl_urb; -+ -+ u8 is_f5u011; /* Set if device is an F5U011 */ -+ u8 rxmode[2]; /* Used for F5U011 */ -+ atomic_t recq_sz; /* Used for F5U011 - counter of waiting rx packets */ -+}; -+ -+/* -+ * Useful macros. -+ */ -+ -+#define catc_get_mac(catc, mac) catc_ctrl_msg(catc, USB_DIR_IN, GetMac, 0, 0, mac, 6) -+#define catc_reset(catc) catc_ctrl_msg(catc, USB_DIR_OUT, Reset, 0, 0, NULL, 0) -+#define catc_set_reg(catc, reg, val) catc_ctrl_msg(catc, USB_DIR_OUT, SetReg, val, reg, NULL, 0) -+#define catc_get_reg(catc, reg, buf) catc_ctrl_msg(catc, USB_DIR_IN, GetReg, 0, reg, buf, 1) -+#define catc_write_mem(catc, addr, buf, size) catc_ctrl_msg(catc, USB_DIR_OUT, WriteMem, 0, addr, buf, size) -+#define catc_read_mem(catc, addr, buf, size) catc_ctrl_msg(catc, USB_DIR_IN, ReadMem, 0, addr, buf, size) -+ -+#define f5u011_rxmode(catc, rxmode) catc_ctrl_msg(catc, USB_DIR_OUT, SetRxMode, 0, 1, rxmode, 2) -+#define f5u011_rxmode_async(catc, rxmode) catc_ctrl_async(catc, USB_DIR_OUT, SetRxMode, 0, 1, &rxmode, 2, NULL) -+#define f5u011_mchash_async(catc, hash) catc_ctrl_async(catc, USB_DIR_OUT, SetRxMode, 0, 2, &hash, 8, NULL) -+ -+#define catc_set_reg_async(catc, reg, val) catc_ctrl_async(catc, USB_DIR_OUT, SetReg, val, reg, NULL, 0, NULL) -+#define catc_get_reg_async(catc, reg, cb) catc_ctrl_async(catc, USB_DIR_IN, GetReg, 0, reg, NULL, 1, cb) -+#define catc_write_mem_async(catc, addr, buf, size) catc_ctrl_async(catc, USB_DIR_OUT, WriteMem, 0, addr, buf, size, NULL) -+ -+/* -+ * Receive routines. -+ */ -+ -+static void catc_rx_done(struct urb *urb) -+{ -+ struct catc *catc = urb->context; -+ u8 *pkt_start = urb->transfer_buffer; -+ struct sk_buff *skb; -+ int pkt_len, pkt_offset = 0; -+ int status = urb->status; -+ -+ if (!catc->is_f5u011) { -+ clear_bit(RX_RUNNING, &catc->flags); -+ pkt_offset = 2; -+ } -+ -+ if (status) { -+ dev_dbg(&urb->dev->dev, "rx_done, status %d, length %d\n", -+ status, urb->actual_length); -+ return; -+ } -+ -+ do { -+ if(!catc->is_f5u011) { -+ pkt_len = le16_to_cpup((__le16*)pkt_start); -+ if (pkt_len > urb->actual_length) { -+ catc->netdev->stats.rx_length_errors++; -+ catc->netdev->stats.rx_errors++; -+ break; -+ } -+ } else { -+ pkt_len = urb->actual_length; -+ } -+ -+ if (!(skb = dev_alloc_skb(pkt_len))) -+ return; -+ -+ skb_copy_to_linear_data(skb, pkt_start + pkt_offset, pkt_len); -+ skb_put(skb, pkt_len); -+ -+ skb->protocol = eth_type_trans(skb, catc->netdev); -+ netif_rx(skb); -+ -+ catc->netdev->stats.rx_packets++; -+ catc->netdev->stats.rx_bytes += pkt_len; -+ -+ /* F5U011 only does one packet per RX */ -+ if (catc->is_f5u011) -+ break; -+ pkt_start += (((pkt_len + 1) >> 6) + 1) << 6; -+ -+ } while (pkt_start - (u8 *) urb->transfer_buffer < urb->actual_length); -+ -+ if (catc->is_f5u011) { -+ if (atomic_read(&catc->recq_sz)) { -+ int state; -+ atomic_dec(&catc->recq_sz); -+ netdev_dbg(catc->netdev, "getting extra packet\n"); -+ urb->dev = catc->usbdev; -+ if ((state = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { -+ netdev_dbg(catc->netdev, -+ "submit(rx_urb) status %d\n", state); -+ } -+ } else { -+ clear_bit(RX_RUNNING, &catc->flags); -+ } -+ } -+} -+ -+static void catc_irq_done(struct urb *urb) -+{ -+ struct catc *catc = urb->context; -+ u8 *data = urb->transfer_buffer; -+ int status = urb->status; -+ unsigned int hasdata = 0, linksts = LinkNoChange; -+ int res; -+ -+ if (!catc->is_f5u011) { -+ hasdata = data[1] & 0x80; -+ if (data[1] & 0x40) -+ linksts = LinkGood; -+ else if (data[1] & 0x20) -+ linksts = LinkBad; -+ } else { -+ hasdata = (unsigned int)(be16_to_cpup((__be16*)data) & 0x0fff); -+ if (data[0] == 0x90) -+ linksts = LinkGood; -+ else if (data[0] == 0xA0) -+ linksts = LinkBad; -+ } -+ -+ switch (status) { -+ case 0: /* success */ -+ break; -+ case -ECONNRESET: /* unlink */ -+ case -ENOENT: -+ case -ESHUTDOWN: -+ return; -+ /* -EPIPE: should clear the halt */ -+ default: /* error */ -+ dev_dbg(&urb->dev->dev, -+ "irq_done, status %d, data %02x %02x.\n", -+ status, data[0], data[1]); -+ goto resubmit; -+ } -+ -+ if (linksts == LinkGood) { -+ netif_carrier_on(catc->netdev); -+ netdev_dbg(catc->netdev, "link ok\n"); -+ } -+ -+ if (linksts == LinkBad) { -+ netif_carrier_off(catc->netdev); -+ netdev_dbg(catc->netdev, "link bad\n"); -+ } -+ -+ if (hasdata) { -+ if (test_and_set_bit(RX_RUNNING, &catc->flags)) { -+ if (catc->is_f5u011) -+ atomic_inc(&catc->recq_sz); -+ } else { -+ catc->rx_urb->dev = catc->usbdev; -+ if ((res = usb_submit_urb(catc->rx_urb, GFP_ATOMIC)) < 0) { -+ dev_err(&catc->usbdev->dev, -+ "submit(rx_urb) status %d\n", res); -+ } -+ } -+ } -+resubmit: -+ res = usb_submit_urb (urb, GFP_ATOMIC); -+ if (res) -+ dev_err(&catc->usbdev->dev, -+ "can't resubmit intr, %s-%s, status %d\n", -+ catc->usbdev->bus->bus_name, -+ catc->usbdev->devpath, res); -+} -+ -+/* -+ * Transmit routines. -+ */ -+ -+static int catc_tx_run(struct catc *catc) -+{ -+ int status; -+ -+ if (catc->is_f5u011) -+ catc->tx_ptr = (catc->tx_ptr + 63) & ~63; -+ -+ catc->tx_urb->transfer_buffer_length = catc->tx_ptr; -+ catc->tx_urb->transfer_buffer = catc->tx_buf[catc->tx_idx]; -+ catc->tx_urb->dev = catc->usbdev; -+ -+ if ((status = usb_submit_urb(catc->tx_urb, GFP_ATOMIC)) < 0) -+ dev_err(&catc->usbdev->dev, "submit(tx_urb), status %d\n", -+ status); -+ -+ catc->tx_idx = !catc->tx_idx; -+ catc->tx_ptr = 0; -+ -+ catc->netdev->trans_start = jiffies; -+ return status; -+} -+ -+static void catc_tx_done(struct urb *urb) -+{ -+ struct catc *catc = urb->context; -+ unsigned long flags; -+ int r, status = urb->status; -+ -+ if (status == -ECONNRESET) { -+ dev_dbg(&urb->dev->dev, "Tx Reset.\n"); -+ urb->status = 0; -+ catc->netdev->trans_start = jiffies; -+ catc->netdev->stats.tx_errors++; -+ clear_bit(TX_RUNNING, &catc->flags); -+ netif_wake_queue(catc->netdev); -+ return; -+ } -+ -+ if (status) { -+ dev_dbg(&urb->dev->dev, "tx_done, status %d, length %d\n", -+ status, urb->actual_length); -+ return; -+ } -+ -+ spin_lock_irqsave(&catc->tx_lock, flags); -+ -+ if (catc->tx_ptr) { -+ r = catc_tx_run(catc); -+ if (unlikely(r < 0)) -+ clear_bit(TX_RUNNING, &catc->flags); -+ } else { -+ clear_bit(TX_RUNNING, &catc->flags); -+ } -+ -+ netif_wake_queue(catc->netdev); -+ -+ spin_unlock_irqrestore(&catc->tx_lock, flags); -+} -+ -+static netdev_tx_t catc_start_xmit(struct sk_buff *skb, -+ struct net_device *netdev) -+{ -+ struct catc *catc = netdev_priv(netdev); -+ unsigned long flags; -+ int r = 0; -+ char *tx_buf; -+ -+ spin_lock_irqsave(&catc->tx_lock, flags); -+ -+ catc->tx_ptr = (((catc->tx_ptr - 1) >> 6) + 1) << 6; -+ tx_buf = catc->tx_buf[catc->tx_idx] + catc->tx_ptr; -+ if (catc->is_f5u011) -+ *(__be16 *)tx_buf = cpu_to_be16(skb->len); -+ else -+ *(__le16 *)tx_buf = cpu_to_le16(skb->len); -+ skb_copy_from_linear_data(skb, tx_buf + 2, skb->len); -+ catc->tx_ptr += skb->len + 2; -+ -+ if (!test_and_set_bit(TX_RUNNING, &catc->flags)) { -+ r = catc_tx_run(catc); -+ if (r < 0) -+ clear_bit(TX_RUNNING, &catc->flags); -+ } -+ -+ if ((catc->is_f5u011 && catc->tx_ptr) || -+ (catc->tx_ptr >= ((TX_MAX_BURST - 1) * (PKT_SZ + 2)))) -+ netif_stop_queue(netdev); -+ -+ spin_unlock_irqrestore(&catc->tx_lock, flags); -+ -+ if (r >= 0) { -+ catc->netdev->stats.tx_bytes += skb->len; -+ catc->netdev->stats.tx_packets++; -+ } -+ -+ dev_kfree_skb(skb); -+ -+ return NETDEV_TX_OK; -+} -+ -+static void catc_tx_timeout(struct net_device *netdev) -+{ -+ struct catc *catc = netdev_priv(netdev); -+ -+ dev_warn(&netdev->dev, "Transmit timed out.\n"); -+ usb_unlink_urb(catc->tx_urb); -+} -+ -+/* -+ * Control messages. -+ */ -+ -+static int catc_ctrl_msg(struct catc *catc, u8 dir, u8 request, u16 value, u16 index, void *buf, int len) -+{ -+ int retval = usb_control_msg(catc->usbdev, -+ dir ? usb_rcvctrlpipe(catc->usbdev, 0) : usb_sndctrlpipe(catc->usbdev, 0), -+ request, 0x40 | dir, value, index, buf, len, 1000); -+ return retval < 0 ? retval : 0; -+} -+ -+static void catc_ctrl_run(struct catc *catc) -+{ -+ struct ctrl_queue *q = catc->ctrl_queue + catc->ctrl_tail; -+ struct usb_device *usbdev = catc->usbdev; -+ struct urb *urb = catc->ctrl_urb; -+ struct usb_ctrlrequest *dr = &catc->ctrl_dr; -+ int status; -+ -+ dr->bRequest = q->request; -+ dr->bRequestType = 0x40 | q->dir; -+ dr->wValue = cpu_to_le16(q->value); -+ dr->wIndex = cpu_to_le16(q->index); -+ dr->wLength = cpu_to_le16(q->len); -+ -+ urb->pipe = q->dir ? usb_rcvctrlpipe(usbdev, 0) : usb_sndctrlpipe(usbdev, 0); -+ urb->transfer_buffer_length = q->len; -+ urb->transfer_buffer = catc->ctrl_buf; -+ urb->setup_packet = (void *) dr; -+ urb->dev = usbdev; -+ -+ if (!q->dir && q->buf && q->len) -+ memcpy(catc->ctrl_buf, q->buf, q->len); -+ -+ if ((status = usb_submit_urb(catc->ctrl_urb, GFP_ATOMIC))) -+ dev_err(&catc->usbdev->dev, "submit(ctrl_urb) status %d\n", -+ status); -+} -+ -+static void catc_ctrl_done(struct urb *urb) -+{ -+ struct catc *catc = urb->context; -+ struct ctrl_queue *q; -+ unsigned long flags; -+ int status = urb->status; -+ -+ if (status) -+ dev_dbg(&urb->dev->dev, "ctrl_done, status %d, len %d.\n", -+ status, urb->actual_length); -+ -+ spin_lock_irqsave(&catc->ctrl_lock, flags); -+ -+ q = catc->ctrl_queue + catc->ctrl_tail; -+ -+ if (q->dir) { -+ if (q->buf && q->len) -+ memcpy(q->buf, catc->ctrl_buf, q->len); -+ else -+ q->buf = catc->ctrl_buf; -+ } -+ -+ if (q->callback) -+ q->callback(catc, q); -+ -+ catc->ctrl_tail = (catc->ctrl_tail + 1) & (CTRL_QUEUE - 1); -+ -+ if (catc->ctrl_head != catc->ctrl_tail) -+ catc_ctrl_run(catc); -+ else -+ clear_bit(CTRL_RUNNING, &catc->flags); -+ -+ spin_unlock_irqrestore(&catc->ctrl_lock, flags); -+} -+ -+static int catc_ctrl_async(struct catc *catc, u8 dir, u8 request, u16 value, -+ u16 index, void *buf, int len, void (*callback)(struct catc *catc, struct ctrl_queue *q)) -+{ -+ struct ctrl_queue *q; -+ int retval = 0; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&catc->ctrl_lock, flags); -+ -+ q = catc->ctrl_queue + catc->ctrl_head; -+ -+ q->dir = dir; -+ q->request = request; -+ q->value = value; -+ q->index = index; -+ q->buf = buf; -+ q->len = len; -+ q->callback = callback; -+ -+ catc->ctrl_head = (catc->ctrl_head + 1) & (CTRL_QUEUE - 1); -+ -+ if (catc->ctrl_head == catc->ctrl_tail) { -+ dev_err(&catc->usbdev->dev, "ctrl queue full\n"); -+ catc->ctrl_tail = (catc->ctrl_tail + 1) & (CTRL_QUEUE - 1); -+ retval = -1; -+ } -+ -+ if (!test_and_set_bit(CTRL_RUNNING, &catc->flags)) -+ catc_ctrl_run(catc); -+ -+ spin_unlock_irqrestore(&catc->ctrl_lock, flags); -+ -+ return retval; -+} -+ -+/* -+ * Statistics. -+ */ -+ -+static void catc_stats_done(struct catc *catc, struct ctrl_queue *q) -+{ -+ int index = q->index - EthStats; -+ u16 data, last; -+ -+ catc->stats_buf[index] = *((char *)q->buf); -+ -+ if (index & 1) -+ return; -+ -+ data = ((u16)catc->stats_buf[index] << 8) | catc->stats_buf[index + 1]; -+ last = catc->stats_vals[index >> 1]; -+ -+ switch (index) { -+ case TxSingleColl: -+ case TxMultiColl: -+ catc->netdev->stats.collisions += data - last; -+ break; -+ case TxExcessColl: -+ catc->netdev->stats.tx_aborted_errors += data - last; -+ catc->netdev->stats.tx_errors += data - last; -+ break; -+ case RxFramErr: -+ catc->netdev->stats.rx_frame_errors += data - last; -+ catc->netdev->stats.rx_errors += data - last; -+ break; -+ } -+ -+ catc->stats_vals[index >> 1] = data; -+} -+ -+static void catc_stats_timer(unsigned long data) -+{ -+ struct catc *catc = (void *) data; -+ int i; -+ -+ for (i = 0; i < 8; i++) -+ catc_get_reg_async(catc, EthStats + 7 - i, catc_stats_done); -+ -+ mod_timer(&catc->timer, jiffies + STATS_UPDATE); -+} -+ -+/* -+ * Receive modes. Broadcast, Multicast, Promisc. -+ */ -+ -+static void catc_multicast(unsigned char *addr, u8 *multicast) -+{ -+ u32 crc; -+ -+ crc = ether_crc_le(6, addr); -+ multicast[(crc >> 3) & 0x3f] |= 1 << (crc & 7); -+} -+ -+static void catc_set_multicast_list(struct net_device *netdev) -+{ -+ struct catc *catc = netdev_priv(netdev); -+ struct netdev_hw_addr *ha; -+ u8 broadcast[ETH_ALEN]; -+ u8 rx = RxEnable | RxPolarity | RxMultiCast; -+ -+ eth_broadcast_addr(broadcast); -+ memset(catc->multicast, 0, 64); -+ -+ catc_multicast(broadcast, catc->multicast); -+ catc_multicast(netdev->dev_addr, catc->multicast); -+ -+ if (netdev->flags & IFF_PROMISC) { -+ memset(catc->multicast, 0xff, 64); -+ rx |= (!catc->is_f5u011) ? RxPromisc : AltRxPromisc; -+ } -+ -+ if (netdev->flags & IFF_ALLMULTI) { -+ memset(catc->multicast, 0xff, 64); -+ } else { -+ netdev_for_each_mc_addr(ha, netdev) { -+ u32 crc = ether_crc_le(6, ha->addr); -+ if (!catc->is_f5u011) { -+ catc->multicast[(crc >> 3) & 0x3f] |= 1 << (crc & 7); -+ } else { -+ catc->multicast[7-(crc >> 29)] |= 1 << ((crc >> 26) & 7); -+ } -+ } -+ } -+ if (!catc->is_f5u011) { -+ catc_set_reg_async(catc, RxUnit, rx); -+ catc_write_mem_async(catc, 0xfa80, catc->multicast, 64); -+ } else { -+ f5u011_mchash_async(catc, catc->multicast); -+ if (catc->rxmode[0] != rx) { -+ catc->rxmode[0] = rx; -+ netdev_dbg(catc->netdev, -+ "Setting RX mode to %2.2X %2.2X\n", -+ catc->rxmode[0], catc->rxmode[1]); -+ f5u011_rxmode_async(catc, catc->rxmode); -+ } -+ } -+} -+ -+static void catc_get_drvinfo(struct net_device *dev, -+ struct ethtool_drvinfo *info) -+{ -+ struct catc *catc = netdev_priv(dev); -+ strlcpy(info->driver, driver_name, sizeof(info->driver)); -+ strlcpy(info->version, DRIVER_VERSION, sizeof(info->version)); -+ usb_make_path(catc->usbdev, info->bus_info, sizeof(info->bus_info)); -+} -+ -+static int catc_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) -+{ -+ struct catc *catc = netdev_priv(dev); -+ if (!catc->is_f5u011) -+ return -EOPNOTSUPP; -+ -+ cmd->supported = SUPPORTED_10baseT_Half | SUPPORTED_TP; -+ cmd->advertising = ADVERTISED_10baseT_Half | ADVERTISED_TP; -+ ethtool_cmd_speed_set(cmd, SPEED_10); -+ cmd->duplex = DUPLEX_HALF; -+ cmd->port = PORT_TP; -+ cmd->phy_address = 0; -+ cmd->transceiver = XCVR_INTERNAL; -+ cmd->autoneg = AUTONEG_DISABLE; -+ cmd->maxtxpkt = 1; -+ cmd->maxrxpkt = 1; -+ return 0; -+} -+ -+static const struct ethtool_ops ops = { -+ .get_drvinfo = catc_get_drvinfo, -+ .get_settings = catc_get_settings, -+ .get_link = ethtool_op_get_link -+}; -+ -+/* -+ * Open, close. -+ */ -+ -+static int catc_open(struct net_device *netdev) -+{ -+ struct catc *catc = netdev_priv(netdev); -+ int status; -+ -+ catc->irq_urb->dev = catc->usbdev; -+ if ((status = usb_submit_urb(catc->irq_urb, GFP_KERNEL)) < 0) { -+ dev_err(&catc->usbdev->dev, "submit(irq_urb) status %d\n", -+ status); -+ return -1; -+ } -+ -+ netif_start_queue(netdev); -+ -+ if (!catc->is_f5u011) -+ mod_timer(&catc->timer, jiffies + STATS_UPDATE); -+ -+ return 0; -+} -+ -+static int catc_stop(struct net_device *netdev) -+{ -+ struct catc *catc = netdev_priv(netdev); -+ -+ netif_stop_queue(netdev); -+ -+ if (!catc->is_f5u011) -+ del_timer_sync(&catc->timer); -+ -+ usb_kill_urb(catc->rx_urb); -+ usb_kill_urb(catc->tx_urb); -+ usb_kill_urb(catc->irq_urb); -+ usb_kill_urb(catc->ctrl_urb); -+ -+ return 0; -+} -+ -+static const struct net_device_ops catc_netdev_ops = { -+ .ndo_open = catc_open, -+ .ndo_stop = catc_stop, -+ .ndo_start_xmit = catc_start_xmit, -+ -+ .ndo_tx_timeout = catc_tx_timeout, -+ .ndo_set_rx_mode = catc_set_multicast_list, -+ .ndo_change_mtu = eth_change_mtu, -+ .ndo_set_mac_address = eth_mac_addr, -+ .ndo_validate_addr = eth_validate_addr, -+}; -+ -+/* -+ * USB probe, disconnect. -+ */ -+ -+static int catc_probe(struct usb_interface *intf, const struct usb_device_id *id) -+{ -+ struct device *dev = &intf->dev; -+ struct usb_device *usbdev = interface_to_usbdev(intf); -+ struct net_device *netdev; -+ struct catc *catc; -+ u8 broadcast[ETH_ALEN]; -+ int i, pktsz; -+ -+ if (usb_set_interface(usbdev, -+ intf->altsetting->desc.bInterfaceNumber, 1)) { -+ dev_err(dev, "Can't set altsetting 1.\n"); -+ return -EIO; -+ } -+ -+ netdev = alloc_etherdev(sizeof(struct catc)); -+ if (!netdev) -+ return -ENOMEM; -+ -+ catc = netdev_priv(netdev); -+ -+ netdev->netdev_ops = &catc_netdev_ops; -+ netdev->watchdog_timeo = TX_TIMEOUT; -+ netdev->ethtool_ops = &ops; -+ -+ catc->usbdev = usbdev; -+ catc->netdev = netdev; -+ -+ spin_lock_init(&catc->tx_lock); -+ spin_lock_init(&catc->ctrl_lock); -+ -+ init_timer(&catc->timer); -+ catc->timer.data = (long) catc; -+ catc->timer.function = catc_stats_timer; -+ -+ catc->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL); -+ catc->tx_urb = usb_alloc_urb(0, GFP_KERNEL); -+ catc->rx_urb = usb_alloc_urb(0, GFP_KERNEL); -+ catc->irq_urb = usb_alloc_urb(0, GFP_KERNEL); -+ if ((!catc->ctrl_urb) || (!catc->tx_urb) || -+ (!catc->rx_urb) || (!catc->irq_urb)) { -+ dev_err(&intf->dev, "No free urbs available.\n"); -+ usb_free_urb(catc->ctrl_urb); -+ usb_free_urb(catc->tx_urb); -+ usb_free_urb(catc->rx_urb); -+ usb_free_urb(catc->irq_urb); -+ free_netdev(netdev); -+ return -ENOMEM; -+ } -+ -+ /* The F5U011 has the same vendor/product as the netmate but a device version of 0x130 */ -+ if (le16_to_cpu(usbdev->descriptor.idVendor) == 0x0423 && -+ le16_to_cpu(usbdev->descriptor.idProduct) == 0xa && -+ le16_to_cpu(catc->usbdev->descriptor.bcdDevice) == 0x0130) { -+ dev_dbg(dev, "Testing for f5u011\n"); -+ catc->is_f5u011 = 1; -+ atomic_set(&catc->recq_sz, 0); -+ pktsz = RX_PKT_SZ; -+ } else { -+ pktsz = RX_MAX_BURST * (PKT_SZ + 2); -+ } -+ -+ usb_fill_control_urb(catc->ctrl_urb, usbdev, usb_sndctrlpipe(usbdev, 0), -+ NULL, NULL, 0, catc_ctrl_done, catc); -+ -+ usb_fill_bulk_urb(catc->tx_urb, usbdev, usb_sndbulkpipe(usbdev, 1), -+ NULL, 0, catc_tx_done, catc); -+ -+ usb_fill_bulk_urb(catc->rx_urb, usbdev, usb_rcvbulkpipe(usbdev, 1), -+ catc->rx_buf, pktsz, catc_rx_done, catc); -+ -+ usb_fill_int_urb(catc->irq_urb, usbdev, usb_rcvintpipe(usbdev, 2), -+ catc->irq_buf, 2, catc_irq_done, catc, 1); -+ -+ if (!catc->is_f5u011) { -+ dev_dbg(dev, "Checking memory size\n"); -+ -+ i = 0x12345678; -+ catc_write_mem(catc, 0x7a80, &i, 4); -+ i = 0x87654321; -+ catc_write_mem(catc, 0xfa80, &i, 4); -+ catc_read_mem(catc, 0x7a80, &i, 4); -+ -+ switch (i) { -+ case 0x12345678: -+ catc_set_reg(catc, TxBufCount, 8); -+ catc_set_reg(catc, RxBufCount, 32); -+ dev_dbg(dev, "64k Memory\n"); -+ break; -+ default: -+ dev_warn(&intf->dev, -+ "Couldn't detect memory size, assuming 32k\n"); -+ case 0x87654321: -+ catc_set_reg(catc, TxBufCount, 4); -+ catc_set_reg(catc, RxBufCount, 16); -+ dev_dbg(dev, "32k Memory\n"); -+ break; -+ } -+ -+ dev_dbg(dev, "Getting MAC from SEEROM.\n"); -+ -+ catc_get_mac(catc, netdev->dev_addr); -+ -+ dev_dbg(dev, "Setting MAC into registers.\n"); -+ -+ for (i = 0; i < 6; i++) -+ catc_set_reg(catc, StationAddr0 - i, netdev->dev_addr[i]); -+ -+ dev_dbg(dev, "Filling the multicast list.\n"); -+ -+ eth_broadcast_addr(broadcast); -+ catc_multicast(broadcast, catc->multicast); -+ catc_multicast(netdev->dev_addr, catc->multicast); -+ catc_write_mem(catc, 0xfa80, catc->multicast, 64); -+ -+ dev_dbg(dev, "Clearing error counters.\n"); -+ -+ for (i = 0; i < 8; i++) -+ catc_set_reg(catc, EthStats + i, 0); -+ catc->last_stats = jiffies; -+ -+ dev_dbg(dev, "Enabling.\n"); -+ -+ catc_set_reg(catc, MaxBurst, RX_MAX_BURST); -+ catc_set_reg(catc, OpModes, OpTxMerge | OpRxMerge | OpLenInclude | Op3MemWaits); -+ catc_set_reg(catc, LEDCtrl, LEDLink); -+ catc_set_reg(catc, RxUnit, RxEnable | RxPolarity | RxMultiCast); -+ } else { -+ dev_dbg(dev, "Performing reset\n"); -+ catc_reset(catc); -+ catc_get_mac(catc, netdev->dev_addr); -+ -+ dev_dbg(dev, "Setting RX Mode\n"); -+ catc->rxmode[0] = RxEnable | RxPolarity | RxMultiCast; -+ catc->rxmode[1] = 0; -+ f5u011_rxmode(catc, catc->rxmode); -+ } -+ dev_dbg(dev, "Init done.\n"); -+ printk(KERN_INFO "%s: %s USB Ethernet at usb-%s-%s, %pM.\n", -+ netdev->name, (catc->is_f5u011) ? "Belkin F5U011" : "CATC EL1210A NetMate", -+ usbdev->bus->bus_name, usbdev->devpath, netdev->dev_addr); -+ usb_set_intfdata(intf, catc); -+ -+ SET_NETDEV_DEV(netdev, &intf->dev); -+ if (register_netdev(netdev) != 0) { -+ usb_set_intfdata(intf, NULL); -+ usb_free_urb(catc->ctrl_urb); -+ usb_free_urb(catc->tx_urb); -+ usb_free_urb(catc->rx_urb); -+ usb_free_urb(catc->irq_urb); -+ free_netdev(netdev); -+ return -EIO; -+ } -+ return 0; -+} -+ -+static void catc_disconnect(struct usb_interface *intf) -+{ -+ struct catc *catc = usb_get_intfdata(intf); -+ -+ usb_set_intfdata(intf, NULL); -+ if (catc) { -+ unregister_netdev(catc->netdev); -+ usb_free_urb(catc->ctrl_urb); -+ usb_free_urb(catc->tx_urb); -+ usb_free_urb(catc->rx_urb); -+ usb_free_urb(catc->irq_urb); -+ free_netdev(catc->netdev); -+ } -+} -+ -+/* -+ * Module functions and tables. -+ */ -+ -+static struct usb_device_id catc_id_table [] = { -+ { USB_DEVICE(0x0423, 0xa) }, /* CATC Netmate, Belkin F5U011 */ -+ { USB_DEVICE(0x0423, 0xc) }, /* CATC Netmate II, Belkin F5U111 */ -+ { USB_DEVICE(0x08d1, 0x1) }, /* smartBridges smartNIC */ -+ { } -+}; -+ -+MODULE_DEVICE_TABLE(usb, catc_id_table); -+ -+static struct usb_driver catc_driver = { -+ .name = driver_name, -+ .probe = catc_probe, -+ .disconnect = catc_disconnect, -+ .id_table = catc_id_table, -+ .disable_hub_initiated_lpm = 1, -+}; -+ -+module_usb_driver(catc_driver); -diff -Naur backports-4.2.6-1.org/drivers/net/usb/cdc_eem.c backports-4.2.6-1/drivers/net/usb/cdc_eem.c ---- backports-4.2.6-1.org/drivers/net/usb/cdc_eem.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/cdc_eem.c 2016-06-28 14:35:17.975307221 +0200 -@@ -0,0 +1,381 @@ -+/* -+ * USB CDC EEM network interface driver -+ * Copyright (C) 2009 Oberthur Technologies -+ * by Omar Laazimani, Olivier Condemine -+ * -+ * 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, see . -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+ -+/* -+ * This driver is an implementation of the CDC "Ethernet Emulation -+ * Model" (EEM) specification, which encapsulates Ethernet frames -+ * for transport over USB using a simpler USB device model than the -+ * previous CDC "Ethernet Control Model" (ECM, or "CDC Ethernet"). -+ * -+ * For details, see www.usb.org/developers/devclass_docs/CDC_EEM10.pdf -+ * -+ * This version has been tested with GIGAntIC WuaoW SIM Smart Card on 2.6.24, -+ * 2.6.27 and 2.6.30rc2 kernel. -+ * It has also been validated on Openmoko Om 2008.12 (based on 2.6.24 kernel). -+ * build on 23-April-2009 -+ */ -+ -+#define EEM_HEAD 2 /* 2 byte header */ -+ -+/*-------------------------------------------------------------------------*/ -+ -+static void eem_linkcmd_complete(struct urb *urb) -+{ -+ dev_kfree_skb(urb->context); -+ usb_free_urb(urb); -+} -+ -+static void eem_linkcmd(struct usbnet *dev, struct sk_buff *skb) -+{ -+ struct urb *urb; -+ int status; -+ -+ urb = usb_alloc_urb(0, GFP_ATOMIC); -+ if (!urb) -+ goto fail; -+ -+ usb_fill_bulk_urb(urb, dev->udev, dev->out, -+ skb->data, skb->len, eem_linkcmd_complete, skb); -+ -+ status = usb_submit_urb(urb, GFP_ATOMIC); -+ if (status) { -+ usb_free_urb(urb); -+fail: -+ dev_kfree_skb(skb); -+ netdev_warn(dev->net, "link cmd failure\n"); -+ return; -+ } -+} -+ -+static int eem_bind(struct usbnet *dev, struct usb_interface *intf) -+{ -+ int status = 0; -+ -+ status = usbnet_get_endpoints(dev, intf); -+ if (status < 0) { -+ usb_set_intfdata(intf, NULL); -+ usb_driver_release_interface(driver_of(intf), intf); -+ return status; -+ } -+ -+ /* no jumbogram (16K) support for now */ -+ -+ dev->net->hard_header_len += EEM_HEAD + ETH_FCS_LEN + VLAN_HLEN; -+ dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; -+ -+ return 0; -+} -+ -+/* -+ * EEM permits packing multiple Ethernet frames into USB transfers -+ * (a "bundle"), but for TX we don't try to do that. -+ */ -+static struct sk_buff *eem_tx_fixup(struct usbnet *dev, struct sk_buff *skb, -+ gfp_t flags) -+{ -+ struct sk_buff *skb2 = NULL; -+ u16 len = skb->len; -+ u32 crc = 0; -+ int padlen = 0; -+ -+ /* When ((len + EEM_HEAD + ETH_FCS_LEN) % dev->maxpacket) is -+ * zero, stick two bytes of zero length EEM packet on the end. -+ * Else the framework would add invalid single byte padding, -+ * since it can't know whether ZLPs will be handled right by -+ * all the relevant hardware and software. -+ */ -+ if (!((len + EEM_HEAD + ETH_FCS_LEN) % dev->maxpacket)) -+ padlen += 2; -+ -+ if (!skb_cloned(skb)) { -+ int headroom = skb_headroom(skb); -+ int tailroom = skb_tailroom(skb); -+ -+ if ((tailroom >= ETH_FCS_LEN + padlen) && -+ (headroom >= EEM_HEAD)) -+ goto done; -+ -+ if ((headroom + tailroom) -+ > (EEM_HEAD + ETH_FCS_LEN + padlen)) { -+ skb->data = memmove(skb->head + -+ EEM_HEAD, -+ skb->data, -+ skb->len); -+ skb_set_tail_pointer(skb, len); -+ goto done; -+ } -+ } -+ -+ skb2 = skb_copy_expand(skb, EEM_HEAD, ETH_FCS_LEN + padlen, flags); -+ if (!skb2) -+ return NULL; -+ -+ dev_kfree_skb_any(skb); -+ skb = skb2; -+ -+done: -+ /* we don't use the "no Ethernet CRC" option */ -+ crc = crc32_le(~0, skb->data, skb->len); -+ crc = ~crc; -+ -+ put_unaligned_le32(crc, skb_put(skb, 4)); -+ -+ /* EEM packet header format: -+ * b0..13: length of ethernet frame -+ * b14: bmCRC (1 == valid Ethernet CRC) -+ * b15: bmType (0 == data) -+ */ -+ len = skb->len; -+ put_unaligned_le16(BIT(14) | len, skb_push(skb, 2)); -+ -+ /* Bundle a zero length EEM packet if needed */ -+ if (padlen) -+ put_unaligned_le16(0, skb_put(skb, 2)); -+ -+ return skb; -+} -+ -+static int eem_rx_fixup(struct usbnet *dev, struct sk_buff *skb) -+{ -+ /* -+ * Our task here is to strip off framing, leaving skb with one -+ * data frame for the usbnet framework code to process. But we -+ * may have received multiple EEM payloads, or command payloads. -+ * So we must process _everything_ as if it's a header, except -+ * maybe the last data payload -+ * -+ * REVISIT the framework needs updating so that when we consume -+ * all payloads (the last or only message was a command, or a -+ * zero length EEM packet) that is not accounted as an rx_error. -+ */ -+ do { -+ struct sk_buff *skb2 = NULL; -+ u16 header; -+ u16 len = 0; -+ -+ /* incomplete EEM header? */ -+ if (skb->len < EEM_HEAD) -+ return 0; -+ -+ /* -+ * EEM packet header format: -+ * b0..14: EEM type dependent (Data or Command) -+ * b15: bmType -+ */ -+ header = get_unaligned_le16(skb->data); -+ skb_pull(skb, EEM_HEAD); -+ -+ /* -+ * The bmType bit helps to denote when EEM -+ * packet is data or command : -+ * bmType = 0 : EEM data payload -+ * bmType = 1 : EEM (link) command -+ */ -+ if (header & BIT(15)) { -+ u16 bmEEMCmd; -+ -+ /* -+ * EEM (link) command packet: -+ * b0..10: bmEEMCmdParam -+ * b11..13: bmEEMCmd -+ * b14: bmReserved (must be 0) -+ * b15: 1 (EEM command) -+ */ -+ if (header & BIT(14)) { -+ netdev_dbg(dev->net, "reserved command %04x\n", -+ header); -+ continue; -+ } -+ -+ bmEEMCmd = (header >> 11) & 0x7; -+ switch (bmEEMCmd) { -+ -+ /* Responding to echo requests is mandatory. */ -+ case 0: /* Echo command */ -+ len = header & 0x7FF; -+ -+ /* bogus command? */ -+ if (skb->len < len) -+ return 0; -+ -+ skb2 = skb_clone(skb, GFP_ATOMIC); -+ if (unlikely(!skb2)) -+ goto next; -+ skb_trim(skb2, len); -+ put_unaligned_le16(BIT(15) | (1 << 11) | len, -+ skb_push(skb2, 2)); -+ eem_linkcmd(dev, skb2); -+ break; -+ -+ /* -+ * Host may choose to ignore hints. -+ * - suspend: peripheral ready to suspend -+ * - response: suggest N millisec polling -+ * - response complete: suggest N sec polling -+ * -+ * Suspend is reported and maybe heeded. -+ */ -+ case 2: /* Suspend hint */ -+ usbnet_device_suggests_idle(dev); -+ continue; -+ case 3: /* Response hint */ -+ case 4: /* Response complete hint */ -+ continue; -+ -+ /* -+ * Hosts should never receive host-to-peripheral -+ * or reserved command codes; or responses to an -+ * echo command we didn't send. -+ */ -+ case 1: /* Echo response */ -+ case 5: /* Tickle */ -+ default: /* reserved */ -+ netdev_warn(dev->net, -+ "unexpected link command %d\n", -+ bmEEMCmd); -+ continue; -+ } -+ -+ } else { -+ u32 crc, crc2; -+ int is_last; -+ -+ /* zero length EEM packet? */ -+ if (header == 0) -+ continue; -+ -+ /* -+ * EEM data packet header : -+ * b0..13: length of ethernet frame -+ * b14: bmCRC -+ * b15: 0 (EEM data) -+ */ -+ len = header & 0x3FFF; -+ -+ /* bogus EEM payload? */ -+ if (skb->len < len) -+ return 0; -+ -+ /* bogus ethernet frame? */ -+ if (len < (ETH_HLEN + ETH_FCS_LEN)) -+ goto next; -+ -+ /* -+ * Treat the last payload differently: framework -+ * code expects our "fixup" to have stripped off -+ * headers, so "skb" is a data packet (or error). -+ * Else if it's not the last payload, keep "skb" -+ * for further processing. -+ */ -+ is_last = (len == skb->len); -+ if (is_last) -+ skb2 = skb; -+ else { -+ skb2 = skb_clone(skb, GFP_ATOMIC); -+ if (unlikely(!skb2)) -+ return 0; -+ } -+ -+ /* -+ * The bmCRC helps to denote when the CRC field in -+ * the Ethernet frame contains a calculated CRC: -+ * bmCRC = 1 : CRC is calculated -+ * bmCRC = 0 : CRC = 0xDEADBEEF -+ */ -+ if (header & BIT(14)) { -+ crc = get_unaligned_le32(skb2->data -+ + len - ETH_FCS_LEN); -+ crc2 = ~crc32_le(~0, skb2->data, skb2->len -+ - ETH_FCS_LEN); -+ } else { -+ crc = get_unaligned_be32(skb2->data -+ + len - ETH_FCS_LEN); -+ crc2 = 0xdeadbeef; -+ } -+ skb_trim(skb2, len - ETH_FCS_LEN); -+ -+ if (is_last) -+ return crc == crc2; -+ -+ if (unlikely(crc != crc2)) { -+ dev->net->stats.rx_errors++; -+ dev_kfree_skb_any(skb2); -+ } else -+ usbnet_skb_return(dev, skb2); -+ } -+ -+next: -+ skb_pull(skb, len); -+ } while (skb->len); -+ -+ return 1; -+} -+ -+static const struct driver_info eem_info = { -+ .description = "CDC EEM Device", -+ .flags = FLAG_ETHER | FLAG_POINTTOPOINT, -+ .bind = eem_bind, -+ .rx_fixup = eem_rx_fixup, -+ .tx_fixup = eem_tx_fixup, -+}; -+ -+/*-------------------------------------------------------------------------*/ -+ -+static const struct usb_device_id products[] = { -+{ -+ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_EEM, -+ USB_CDC_PROTO_EEM), -+ .driver_info = (unsigned long) &eem_info, -+}, -+{ -+ /* EMPTY == end of list */ -+}, -+}; -+MODULE_DEVICE_TABLE(usb, products); -+ -+static struct usb_driver eem_driver = { -+ .name = "cdc_eem", -+ .id_table = products, -+ .probe = usbnet_probe, -+ .disconnect = usbnet_disconnect, -+ .suspend = usbnet_suspend, -+ .resume = usbnet_resume, -+ .disable_hub_initiated_lpm = 1, -+}; -+ -+module_usb_driver(eem_driver); -+ -+MODULE_AUTHOR("Omar Laazimani "); -+MODULE_DESCRIPTION("USB CDC EEM"); -+MODULE_LICENSE("GPL"); -diff -Naur backports-4.2.6-1.org/drivers/net/usb/cdc-phonet.c backports-4.2.6-1/drivers/net/usb/cdc-phonet.c ---- backports-4.2.6-1.org/drivers/net/usb/cdc-phonet.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/cdc-phonet.c 2016-06-28 14:35:17.975307221 +0200 -@@ -0,0 +1,466 @@ -+/* -+ * phonet.c -- USB CDC Phonet host driver -+ * -+ * Copyright (C) 2008-2009 Nokia Corporation. All rights reserved. -+ * -+ * Author: Rémi Denis-Courmont -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU -+ * General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -+ * 02110-1301 USA -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define PN_MEDIA_USB 0x1B -+ -+static const unsigned rxq_size = 17; -+ -+struct usbpn_dev { -+ struct net_device *dev; -+ -+ struct usb_interface *intf, *data_intf; -+ struct usb_device *usb; -+ unsigned int tx_pipe, rx_pipe; -+ u8 active_setting; -+ u8 disconnected; -+ -+ unsigned tx_queue; -+ spinlock_t tx_lock; -+ -+ spinlock_t rx_lock; -+ struct sk_buff *rx_skb; -+ struct urb *urbs[0]; -+}; -+ -+static void tx_complete(struct urb *req); -+static void rx_complete(struct urb *req); -+ -+/* -+ * Network device callbacks -+ */ -+static netdev_tx_t usbpn_xmit(struct sk_buff *skb, struct net_device *dev) -+{ -+ struct usbpn_dev *pnd = netdev_priv(dev); -+ struct urb *req = NULL; -+ unsigned long flags; -+ int err; -+ -+ if (skb->protocol != htons(ETH_P_PHONET)) -+ goto drop; -+ -+ req = usb_alloc_urb(0, GFP_ATOMIC); -+ if (!req) -+ goto drop; -+ usb_fill_bulk_urb(req, pnd->usb, pnd->tx_pipe, skb->data, skb->len, -+ tx_complete, skb); -+ req->transfer_flags = URB_ZERO_PACKET; -+ err = usb_submit_urb(req, GFP_ATOMIC); -+ if (err) { -+ usb_free_urb(req); -+ goto drop; -+ } -+ -+ spin_lock_irqsave(&pnd->tx_lock, flags); -+ pnd->tx_queue++; -+ if (pnd->tx_queue >= dev->tx_queue_len) -+ netif_stop_queue(dev); -+ spin_unlock_irqrestore(&pnd->tx_lock, flags); -+ return NETDEV_TX_OK; -+ -+drop: -+ dev_kfree_skb(skb); -+ dev->stats.tx_dropped++; -+ return NETDEV_TX_OK; -+} -+ -+static void tx_complete(struct urb *req) -+{ -+ struct sk_buff *skb = req->context; -+ struct net_device *dev = skb->dev; -+ struct usbpn_dev *pnd = netdev_priv(dev); -+ int status = req->status; -+ -+ switch (status) { -+ case 0: -+ dev->stats.tx_bytes += skb->len; -+ break; -+ -+ case -ENOENT: -+ case -ECONNRESET: -+ case -ESHUTDOWN: -+ dev->stats.tx_aborted_errors++; -+ default: -+ dev->stats.tx_errors++; -+ dev_dbg(&dev->dev, "TX error (%d)\n", status); -+ } -+ dev->stats.tx_packets++; -+ -+ spin_lock(&pnd->tx_lock); -+ pnd->tx_queue--; -+ netif_wake_queue(dev); -+ spin_unlock(&pnd->tx_lock); -+ -+ dev_kfree_skb_any(skb); -+ usb_free_urb(req); -+} -+ -+static int rx_submit(struct usbpn_dev *pnd, struct urb *req, gfp_t gfp_flags) -+{ -+ struct net_device *dev = pnd->dev; -+ struct page *page; -+ int err; -+ -+ page = __dev_alloc_page(gfp_flags | __GFP_NOMEMALLOC); -+ if (!page) -+ return -ENOMEM; -+ -+ usb_fill_bulk_urb(req, pnd->usb, pnd->rx_pipe, page_address(page), -+ PAGE_SIZE, rx_complete, dev); -+ req->transfer_flags = 0; -+ err = usb_submit_urb(req, gfp_flags); -+ if (unlikely(err)) { -+ dev_dbg(&dev->dev, "RX submit error (%d)\n", err); -+ put_page(page); -+ } -+ return err; -+} -+ -+static void rx_complete(struct urb *req) -+{ -+ struct net_device *dev = req->context; -+ struct usbpn_dev *pnd = netdev_priv(dev); -+ struct page *page = virt_to_page(req->transfer_buffer); -+ struct sk_buff *skb; -+ unsigned long flags; -+ int status = req->status; -+ -+ switch (status) { -+ case 0: -+ spin_lock_irqsave(&pnd->rx_lock, flags); -+ skb = pnd->rx_skb; -+ if (!skb) { -+ skb = pnd->rx_skb = netdev_alloc_skb(dev, 12); -+ if (likely(skb)) { -+ /* Can't use pskb_pull() on page in IRQ */ -+ memcpy(skb_put(skb, 1), page_address(page), 1); -+ skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, -+ page, 1, req->actual_length, -+ PAGE_SIZE); -+ page = NULL; -+ } -+ } else { -+ skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, -+ page, 0, req->actual_length, -+ PAGE_SIZE); -+ page = NULL; -+ } -+ if (req->actual_length < PAGE_SIZE) -+ pnd->rx_skb = NULL; /* Last fragment */ -+ else -+ skb = NULL; -+ spin_unlock_irqrestore(&pnd->rx_lock, flags); -+ if (skb) { -+ skb->protocol = htons(ETH_P_PHONET); -+ skb_reset_mac_header(skb); -+ __skb_pull(skb, 1); -+ skb->dev = dev; -+ dev->stats.rx_packets++; -+ dev->stats.rx_bytes += skb->len; -+ -+ netif_rx(skb); -+ } -+ goto resubmit; -+ -+ case -ENOENT: -+ case -ECONNRESET: -+ case -ESHUTDOWN: -+ req = NULL; -+ break; -+ -+ case -EOVERFLOW: -+ dev->stats.rx_over_errors++; -+ dev_dbg(&dev->dev, "RX overflow\n"); -+ break; -+ -+ case -EILSEQ: -+ dev->stats.rx_crc_errors++; -+ break; -+ } -+ -+ dev->stats.rx_errors++; -+resubmit: -+ if (page) -+ put_page(page); -+ if (req) -+ rx_submit(pnd, req, GFP_ATOMIC); -+} -+ -+static int usbpn_close(struct net_device *dev); -+ -+static int usbpn_open(struct net_device *dev) -+{ -+ struct usbpn_dev *pnd = netdev_priv(dev); -+ int err; -+ unsigned i; -+ unsigned num = pnd->data_intf->cur_altsetting->desc.bInterfaceNumber; -+ -+ err = usb_set_interface(pnd->usb, num, pnd->active_setting); -+ if (err) -+ return err; -+ -+ for (i = 0; i < rxq_size; i++) { -+ struct urb *req = usb_alloc_urb(0, GFP_KERNEL); -+ -+ if (!req || rx_submit(pnd, req, GFP_KERNEL)) { -+ usb_free_urb(req); -+ usbpn_close(dev); -+ return -ENOMEM; -+ } -+ pnd->urbs[i] = req; -+ } -+ -+ netif_wake_queue(dev); -+ return 0; -+} -+ -+static int usbpn_close(struct net_device *dev) -+{ -+ struct usbpn_dev *pnd = netdev_priv(dev); -+ unsigned i; -+ unsigned num = pnd->data_intf->cur_altsetting->desc.bInterfaceNumber; -+ -+ netif_stop_queue(dev); -+ -+ for (i = 0; i < rxq_size; i++) { -+ struct urb *req = pnd->urbs[i]; -+ -+ if (!req) -+ continue; -+ usb_kill_urb(req); -+ usb_free_urb(req); -+ pnd->urbs[i] = NULL; -+ } -+ -+ return usb_set_interface(pnd->usb, num, !pnd->active_setting); -+} -+ -+static int usbpn_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -+{ -+ struct if_phonet_req *req = (struct if_phonet_req *)ifr; -+ -+ switch (cmd) { -+ case SIOCPNGAUTOCONF: -+ req->ifr_phonet_autoconf.device = PN_DEV_PC; -+ return 0; -+ } -+ return -ENOIOCTLCMD; -+} -+ -+static int usbpn_set_mtu(struct net_device *dev, int new_mtu) -+{ -+ if ((new_mtu < PHONET_MIN_MTU) || (new_mtu > PHONET_MAX_MTU)) -+ return -EINVAL; -+ -+ dev->mtu = new_mtu; -+ return 0; -+} -+ -+static const struct net_device_ops usbpn_ops = { -+ .ndo_open = usbpn_open, -+ .ndo_stop = usbpn_close, -+ .ndo_start_xmit = usbpn_xmit, -+ .ndo_do_ioctl = usbpn_ioctl, -+ .ndo_change_mtu = usbpn_set_mtu, -+}; -+ -+static void usbpn_setup(struct net_device *dev) -+{ -+ dev->features = 0; -+ dev->netdev_ops = &usbpn_ops, -+ dev->header_ops = &phonet_header_ops; -+ dev->type = ARPHRD_PHONET; -+ dev->flags = IFF_POINTOPOINT | IFF_NOARP; -+ dev->mtu = PHONET_MAX_MTU; -+ dev->hard_header_len = 1; -+ dev->dev_addr[0] = PN_MEDIA_USB; -+ dev->addr_len = 1; -+ dev->tx_queue_len = 3; -+ -+ dev->destructor = free_netdev; -+} -+ -+/* -+ * USB driver callbacks -+ */ -+static struct usb_device_id usbpn_ids[] = { -+ { -+ .match_flags = USB_DEVICE_ID_MATCH_VENDOR -+ | USB_DEVICE_ID_MATCH_INT_CLASS -+ | USB_DEVICE_ID_MATCH_INT_SUBCLASS, -+ .idVendor = 0x0421, /* Nokia */ -+ .bInterfaceClass = USB_CLASS_COMM, -+ .bInterfaceSubClass = 0xFE, -+ }, -+ { }, -+}; -+ -+MODULE_DEVICE_TABLE(usb, usbpn_ids); -+ -+static struct usb_driver usbpn_driver; -+ -+static int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *id) -+{ -+ static const char ifname[] = "usbpn%d"; -+ const struct usb_cdc_union_desc *union_header = NULL; -+ const struct usb_host_interface *data_desc; -+ struct usb_interface *data_intf; -+ struct usb_device *usbdev = interface_to_usbdev(intf); -+ struct net_device *dev; -+ struct usbpn_dev *pnd; -+ u8 *data; -+ int phonet = 0; -+ int len, err; -+ -+ data = intf->altsetting->extra; -+ len = intf->altsetting->extralen; -+ while (len >= 3) { -+ u8 dlen = data[0]; -+ if (dlen < 3) -+ return -EINVAL; -+ -+ /* bDescriptorType */ -+ if (data[1] == USB_DT_CS_INTERFACE) { -+ /* bDescriptorSubType */ -+ switch (data[2]) { -+ case USB_CDC_UNION_TYPE: -+ if (union_header || dlen < 5) -+ break; -+ union_header = -+ (struct usb_cdc_union_desc *)data; -+ break; -+ case 0xAB: -+ phonet = 1; -+ break; -+ } -+ } -+ data += dlen; -+ len -= dlen; -+ } -+ -+ if (!union_header || !phonet) -+ return -EINVAL; -+ -+ data_intf = usb_ifnum_to_if(usbdev, union_header->bSlaveInterface0); -+ if (data_intf == NULL) -+ return -ENODEV; -+ /* Data interface has one inactive and one active setting */ -+ if (data_intf->num_altsetting != 2) -+ return -EINVAL; -+ if (data_intf->altsetting[0].desc.bNumEndpoints == 0 && -+ data_intf->altsetting[1].desc.bNumEndpoints == 2) -+ data_desc = data_intf->altsetting + 1; -+ else -+ if (data_intf->altsetting[0].desc.bNumEndpoints == 2 && -+ data_intf->altsetting[1].desc.bNumEndpoints == 0) -+ data_desc = data_intf->altsetting; -+ else -+ return -EINVAL; -+ -+ dev = alloc_netdev(sizeof(*pnd) + sizeof(pnd->urbs[0]) * rxq_size, -+ ifname, NET_NAME_UNKNOWN, usbpn_setup); -+ if (!dev) -+ return -ENOMEM; -+ -+ pnd = netdev_priv(dev); -+ SET_NETDEV_DEV(dev, &intf->dev); -+ -+ pnd->dev = dev; -+ pnd->usb = usbdev; -+ pnd->intf = intf; -+ pnd->data_intf = data_intf; -+ spin_lock_init(&pnd->tx_lock); -+ spin_lock_init(&pnd->rx_lock); -+ /* Endpoints */ -+ if (usb_pipein(data_desc->endpoint[0].desc.bEndpointAddress)) { -+ pnd->rx_pipe = usb_rcvbulkpipe(usbdev, -+ data_desc->endpoint[0].desc.bEndpointAddress); -+ pnd->tx_pipe = usb_sndbulkpipe(usbdev, -+ data_desc->endpoint[1].desc.bEndpointAddress); -+ } else { -+ pnd->rx_pipe = usb_rcvbulkpipe(usbdev, -+ data_desc->endpoint[1].desc.bEndpointAddress); -+ pnd->tx_pipe = usb_sndbulkpipe(usbdev, -+ data_desc->endpoint[0].desc.bEndpointAddress); -+ } -+ pnd->active_setting = data_desc - data_intf->altsetting; -+ -+ err = usb_driver_claim_interface(&usbpn_driver, data_intf, pnd); -+ if (err) -+ goto out; -+ -+ /* Force inactive mode until the network device is brought UP */ -+ usb_set_interface(usbdev, union_header->bSlaveInterface0, -+ !pnd->active_setting); -+ usb_set_intfdata(intf, pnd); -+ -+ err = register_netdev(dev); -+ if (err) { -+ usb_driver_release_interface(&usbpn_driver, data_intf); -+ goto out; -+ } -+ -+ dev_dbg(&dev->dev, "USB CDC Phonet device found\n"); -+ return 0; -+ -+out: -+ usb_set_intfdata(intf, NULL); -+ free_netdev(dev); -+ return err; -+} -+ -+static void usbpn_disconnect(struct usb_interface *intf) -+{ -+ struct usbpn_dev *pnd = usb_get_intfdata(intf); -+ -+ if (pnd->disconnected) -+ return; -+ -+ pnd->disconnected = 1; -+ usb_driver_release_interface(&usbpn_driver, -+ (pnd->intf == intf) ? pnd->data_intf : pnd->intf); -+ unregister_netdev(pnd->dev); -+} -+ -+static struct usb_driver usbpn_driver = { -+ .name = "cdc_phonet", -+ .probe = usbpn_probe, -+ .disconnect = usbpn_disconnect, -+ .id_table = usbpn_ids, -+ .disable_hub_initiated_lpm = 1, -+}; -+ -+module_usb_driver(usbpn_driver); -+ -+MODULE_AUTHOR("Remi Denis-Courmont"); -+MODULE_DESCRIPTION("USB CDC Phonet host interface"); -+MODULE_LICENSE("GPL"); -diff -Naur backports-4.2.6-1.org/drivers/net/usb/cdc_subset.c backports-4.2.6-1/drivers/net/usb/cdc_subset.c ---- backports-4.2.6-1.org/drivers/net/usb/cdc_subset.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/cdc_subset.c 2016-06-28 14:35:17.975307221 +0200 -@@ -0,0 +1,369 @@ -+/* -+ * Simple "CDC Subset" USB Networking Links -+ * Copyright (C) 2000-2005 by David Brownell -+ * -+ * 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, see . -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+ -+/* -+ * This supports simple USB network links that don't require any special -+ * framing or hardware control operations. The protocol used here is a -+ * strict subset of CDC Ethernet, with three basic differences reflecting -+ * the goal that almost any hardware should run it: -+ * -+ * - Minimal runtime control: one interface, no altsettings, and -+ * no vendor or class specific control requests. If a device is -+ * configured, it is allowed to exchange packets with the host. -+ * Fancier models would mean not working on some hardware. -+ * -+ * - Minimal manufacturing control: no IEEE "Organizationally -+ * Unique ID" required, or an EEPROMs to store one. Each host uses -+ * one random "locally assigned" Ethernet address instead, which can -+ * of course be overridden using standard tools like "ifconfig". -+ * (With 2^46 such addresses, same-net collisions are quite rare.) -+ * -+ * - There is no additional framing data for USB. Packets are written -+ * exactly as in CDC Ethernet, starting with an Ethernet header and -+ * terminated by a short packet. However, the host will never send a -+ * zero length packet; some systems can't handle those robustly. -+ * -+ * Anything that can transmit and receive USB bulk packets can implement -+ * this protocol. That includes both smart peripherals and quite a lot -+ * of "host-to-host" USB cables (which embed two devices back-to-back). -+ * -+ * Note that although Linux may use many of those host-to-host links -+ * with this "cdc_subset" framing, that doesn't mean there may not be a -+ * better approach. Handling the "other end unplugs/replugs" scenario -+ * well tends to require chip-specific vendor requests. Also, Windows -+ * peers at the other end of host-to-host cables may expect their own -+ * framing to be used rather than this "cdc_subset" model. -+ */ -+ -+#if defined(CONFIG_USB_EPSON2888) || defined(CONFIG_USB_ARMLINUX) -+/* PDA style devices are always connected if present */ -+static int always_connected (struct usbnet *dev) -+{ -+ return 0; -+} -+#endif -+ -+#ifdef CONFIG_USB_ALI_M5632 -+#define HAVE_HARDWARE -+ -+/*------------------------------------------------------------------------- -+ * -+ * ALi M5632 driver ... does high speed -+ * -+ * NOTE that the MS-Windows drivers for this chip use some funky and -+ * (naturally) undocumented 7-byte prefix to each packet, so this is a -+ * case where we don't currently interoperate. Also, once you unplug -+ * one end of the cable, you need to replug the other end too ... since -+ * chip docs are unavailable, there's no way to reset the relevant state -+ * short of a power cycle. -+ * -+ *-------------------------------------------------------------------------*/ -+ -+static void m5632_recover(struct usbnet *dev) -+{ -+ struct usb_device *udev = dev->udev; -+ struct usb_interface *intf = dev->intf; -+ int r; -+ -+ r = usb_lock_device_for_reset(udev, intf); -+ if (r < 0) -+ return; -+ -+ usb_reset_device(udev); -+ usb_unlock_device(udev); -+} -+ -+static const struct driver_info ali_m5632_info = { -+ .description = "ALi M5632", -+ .flags = FLAG_POINTTOPOINT, -+ .recover = m5632_recover, -+}; -+ -+#endif -+ -+#ifdef CONFIG_USB_AN2720 -+#define HAVE_HARDWARE -+ -+/*------------------------------------------------------------------------- -+ * -+ * AnchorChips 2720 driver ... http://www.cypress.com -+ * -+ * This doesn't seem to have a way to detect whether the peer is -+ * connected, or need any reset handshaking. It's got pretty big -+ * internal buffers (handles most of a frame's worth of data). -+ * Chip data sheets don't describe any vendor control messages. -+ * -+ *-------------------------------------------------------------------------*/ -+ -+static const struct driver_info an2720_info = { -+ .description = "AnchorChips/Cypress 2720", -+ .flags = FLAG_POINTTOPOINT, -+ // no reset available! -+ // no check_connect available! -+ -+ .in = 2, .out = 2, // direction distinguishes these -+}; -+ -+#endif /* CONFIG_USB_AN2720 */ -+ -+ -+#ifdef CONFIG_USB_BELKIN -+#define HAVE_HARDWARE -+ -+/*------------------------------------------------------------------------- -+ * -+ * Belkin F5U104 ... two NetChip 2280 devices + Atmel AVR microcontroller -+ * -+ * ... also two eTEK designs, including one sold as "Advance USBNET" -+ * -+ *-------------------------------------------------------------------------*/ -+ -+static const struct driver_info belkin_info = { -+ .description = "Belkin, eTEK, or compatible", -+ .flags = FLAG_POINTTOPOINT, -+}; -+ -+#endif /* CONFIG_USB_BELKIN */ -+ -+ -+ -+#ifdef CONFIG_USB_EPSON2888 -+#define HAVE_HARDWARE -+ -+/*------------------------------------------------------------------------- -+ * -+ * EPSON USB clients -+ * -+ * This is the same idea as Linux PDAs (below) except the firmware in the -+ * device might not be Tux-powered. Epson provides reference firmware that -+ * implements this interface. Product developers can reuse or modify that -+ * code, such as by using their own product and vendor codes. -+ * -+ * Support was from Juro Bystricky -+ * -+ *-------------------------------------------------------------------------*/ -+ -+static const struct driver_info epson2888_info = { -+ .description = "Epson USB Device", -+ .check_connect = always_connected, -+ .flags = FLAG_POINTTOPOINT, -+ -+ .in = 4, .out = 3, -+}; -+ -+#endif /* CONFIG_USB_EPSON2888 */ -+ -+ -+/*------------------------------------------------------------------------- -+ * -+ * info from Jonathan McDowell -+ * -+ *-------------------------------------------------------------------------*/ -+#ifdef CONFIG_USB_KC2190 -+#define HAVE_HARDWARE -+static const struct driver_info kc2190_info = { -+ .description = "KC Technology KC-190", -+ .flags = FLAG_POINTTOPOINT, -+}; -+#endif /* CONFIG_USB_KC2190 */ -+ -+ -+#ifdef CONFIG_USB_ARMLINUX -+#define HAVE_HARDWARE -+ -+/*------------------------------------------------------------------------- -+ * -+ * Intel's SA-1100 chip integrates basic USB support, and is used -+ * in PDAs like some iPaqs, the Yopy, some Zaurus models, and more. -+ * When they run Linux, arch/arm/mach-sa1100/usb-eth.c may be used to -+ * network using minimal USB framing data. -+ * -+ * This describes the driver currently in standard ARM Linux kernels. -+ * The Zaurus uses a different driver (see later). -+ * -+ * PXA25x and PXA210 use XScale cores (ARM v5TE) with better USB support -+ * and different USB endpoint numbering than the SA1100 devices. The -+ * mach-pxa/usb-eth.c driver re-uses the device ids from mach-sa1100 -+ * so we rely on the endpoint descriptors. -+ * -+ *-------------------------------------------------------------------------*/ -+ -+static const struct driver_info linuxdev_info = { -+ .description = "Linux Device", -+ .check_connect = always_connected, -+ .flags = FLAG_POINTTOPOINT, -+}; -+ -+static const struct driver_info yopy_info = { -+ .description = "Yopy", -+ .check_connect = always_connected, -+ .flags = FLAG_POINTTOPOINT, -+}; -+ -+static const struct driver_info blob_info = { -+ .description = "Boot Loader OBject", -+ .check_connect = always_connected, -+ .flags = FLAG_POINTTOPOINT, -+}; -+ -+#endif /* CONFIG_USB_ARMLINUX */ -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+#ifndef HAVE_HARDWARE -+#warning You need to configure some hardware for this driver -+#endif -+ -+/* -+ * chip vendor names won't normally be on the cables, and -+ * may not be on the device. -+ */ -+ -+static const struct usb_device_id products [] = { -+ -+#ifdef CONFIG_USB_ALI_M5632 -+{ -+ USB_DEVICE (0x0402, 0x5632), // ALi defaults -+ .driver_info = (unsigned long) &ali_m5632_info, -+}, -+{ -+ USB_DEVICE (0x182d,0x207c), // SiteCom CN-124 -+ .driver_info = (unsigned long) &ali_m5632_info, -+}, -+#endif -+ -+#ifdef CONFIG_USB_AN2720 -+{ -+ USB_DEVICE (0x0547, 0x2720), // AnchorChips defaults -+ .driver_info = (unsigned long) &an2720_info, -+}, { -+ USB_DEVICE (0x0547, 0x2727), // Xircom PGUNET -+ .driver_info = (unsigned long) &an2720_info, -+}, -+#endif -+ -+#ifdef CONFIG_USB_BELKIN -+{ -+ USB_DEVICE (0x050d, 0x0004), // Belkin -+ .driver_info = (unsigned long) &belkin_info, -+}, { -+ USB_DEVICE (0x056c, 0x8100), // eTEK -+ .driver_info = (unsigned long) &belkin_info, -+}, { -+ USB_DEVICE (0x0525, 0x9901), // Advance USBNET (eTEK) -+ .driver_info = (unsigned long) &belkin_info, -+}, -+#endif -+ -+#ifdef CONFIG_USB_EPSON2888 -+{ -+ USB_DEVICE (0x0525, 0x2888), // EPSON USB client -+ .driver_info = (unsigned long) &epson2888_info, -+}, -+#endif -+ -+#ifdef CONFIG_USB_KC2190 -+{ -+ USB_DEVICE (0x050f, 0x0190), // KC-190 -+ .driver_info = (unsigned long) &kc2190_info, -+}, -+#endif -+ -+#ifdef CONFIG_USB_ARMLINUX -+/* -+ * SA-1100 using standard ARM Linux kernels, or compatible. -+ * Often used when talking to Linux PDAs (iPaq, Yopy, etc). -+ * The sa-1100 "usb-eth" driver handles the basic framing. -+ * -+ * PXA25x or PXA210 ... these use a "usb-eth" driver much like -+ * the sa1100 one, but hardware uses different endpoint numbers. -+ * -+ * Or the Linux "Ethernet" gadget on hardware that can't talk -+ * CDC Ethernet (e.g., no altsettings), in either of two modes: -+ * - acting just like the old "usb-eth" firmware, though -+ * the implementation is different -+ * - supporting RNDIS as the first/default configuration for -+ * MS-Windows interop; Linux needs to use the other config -+ */ -+{ -+ // 1183 = 0x049F, both used as hex values? -+ // Compaq "Itsy" vendor/product id -+ USB_DEVICE (0x049F, 0x505A), // usb-eth, or compatible -+ .driver_info = (unsigned long) &linuxdev_info, -+}, { -+ USB_DEVICE (0x0E7E, 0x1001), // G.Mate "Yopy" -+ .driver_info = (unsigned long) &yopy_info, -+}, { -+ USB_DEVICE (0x8086, 0x07d3), // "blob" bootloader -+ .driver_info = (unsigned long) &blob_info, -+}, { -+ USB_DEVICE (0x1286, 0x8001), // "blob" bootloader -+ .driver_info = (unsigned long) &blob_info, -+}, { -+ // Linux Ethernet/RNDIS gadget, mostly on PXA, second config -+ // e.g. Gumstix, current OpenZaurus, ... or anything else -+ // that just enables this gadget option. -+ USB_DEVICE (0x0525, 0xa4a2), -+ .driver_info = (unsigned long) &linuxdev_info, -+}, -+#endif -+ -+ { }, // END -+}; -+MODULE_DEVICE_TABLE(usb, products); -+ -+/*-------------------------------------------------------------------------*/ -+static int dummy_prereset(struct usb_interface *intf) -+{ -+ return 0; -+} -+ -+static int dummy_postreset(struct usb_interface *intf) -+{ -+ return 0; -+} -+ -+static struct usb_driver cdc_subset_driver = { -+ .name = "cdc_subset", -+ .probe = usbnet_probe, -+ .suspend = usbnet_suspend, -+ .resume = usbnet_resume, -+ .pre_reset = dummy_prereset, -+ .post_reset = dummy_postreset, -+ .disconnect = usbnet_disconnect, -+ .id_table = products, -+ .disable_hub_initiated_lpm = 1, -+}; -+ -+module_usb_driver(cdc_subset_driver); -+ -+MODULE_AUTHOR("David Brownell"); -+MODULE_DESCRIPTION("Simple 'CDC Subset' USB networking links"); -+MODULE_LICENSE("GPL"); -diff -Naur backports-4.2.6-1.org/drivers/net/usb/cx82310_eth.c backports-4.2.6-1/drivers/net/usb/cx82310_eth.c ---- backports-4.2.6-1.org/drivers/net/usb/cx82310_eth.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/cx82310_eth.c 2016-06-28 14:35:17.978640554 +0200 -@@ -0,0 +1,353 @@ -+/* -+ * Driver for USB ethernet port of Conexant CX82310-based ADSL routers -+ * Copyright (C) 2010 by Ondrej Zary -+ * some parts inspired by the cxacru driver -+ * -+ * 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, see . -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+enum cx82310_cmd { -+ CMD_START = 0x84, /* no effect? */ -+ CMD_STOP = 0x85, /* no effect? */ -+ CMD_GET_STATUS = 0x90, /* returns nothing? */ -+ CMD_GET_MAC_ADDR = 0x91, /* read MAC address */ -+ CMD_GET_LINK_STATUS = 0x92, /* not useful, link is always up */ -+ CMD_ETHERNET_MODE = 0x99, /* unknown, needed during init */ -+}; -+ -+enum cx82310_status { -+ STATUS_UNDEFINED, -+ STATUS_SUCCESS, -+ STATUS_ERROR, -+ STATUS_UNSUPPORTED, -+ STATUS_UNIMPLEMENTED, -+ STATUS_PARAMETER_ERROR, -+ STATUS_DBG_LOOPBACK, -+}; -+ -+#define CMD_PACKET_SIZE 64 -+#define CMD_TIMEOUT 100 -+#define CMD_REPLY_RETRY 5 -+ -+#define CX82310_MTU 1514 -+#define CMD_EP 0x01 -+ -+/* -+ * execute control command -+ * - optionally send some data (command parameters) -+ * - optionally wait for the reply -+ * - optionally read some data from the reply -+ */ -+static int cx82310_cmd(struct usbnet *dev, enum cx82310_cmd cmd, bool reply, -+ u8 *wdata, int wlen, u8 *rdata, int rlen) -+{ -+ int actual_len, retries, ret; -+ struct usb_device *udev = dev->udev; -+ u8 *buf = kzalloc(CMD_PACKET_SIZE, GFP_KERNEL); -+ -+ if (!buf) -+ return -ENOMEM; -+ -+ /* create command packet */ -+ buf[0] = cmd; -+ if (wdata) -+ memcpy(buf + 4, wdata, min_t(int, wlen, CMD_PACKET_SIZE - 4)); -+ -+ /* send command packet */ -+ ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, CMD_EP), buf, -+ CMD_PACKET_SIZE, &actual_len, CMD_TIMEOUT); -+ if (ret < 0) { -+ if (cmd != CMD_GET_LINK_STATUS) -+ dev_err(&dev->udev->dev, "send command %#x: error %d\n", -+ cmd, ret); -+ goto end; -+ } -+ -+ if (reply) { -+ /* wait for reply, retry if it's empty */ -+ for (retries = 0; retries < CMD_REPLY_RETRY; retries++) { -+ ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, CMD_EP), -+ buf, CMD_PACKET_SIZE, &actual_len, -+ CMD_TIMEOUT); -+ if (ret < 0) { -+ if (cmd != CMD_GET_LINK_STATUS) -+ dev_err(&dev->udev->dev, -+ "reply receive error %d\n", -+ ret); -+ goto end; -+ } -+ if (actual_len > 0) -+ break; -+ } -+ if (actual_len == 0) { -+ dev_err(&dev->udev->dev, "no reply to command %#x\n", -+ cmd); -+ ret = -EIO; -+ goto end; -+ } -+ if (buf[0] != cmd) { -+ dev_err(&dev->udev->dev, -+ "got reply to command %#x, expected: %#x\n", -+ buf[0], cmd); -+ ret = -EIO; -+ goto end; -+ } -+ if (buf[1] != STATUS_SUCCESS) { -+ dev_err(&dev->udev->dev, "command %#x failed: %#x\n", -+ cmd, buf[1]); -+ ret = -EIO; -+ goto end; -+ } -+ if (rdata) -+ memcpy(rdata, buf + 4, -+ min_t(int, rlen, CMD_PACKET_SIZE - 4)); -+ } -+end: -+ kfree(buf); -+ return ret; -+} -+ -+#define partial_len data[0] /* length of partial packet data */ -+#define partial_rem data[1] /* remaining (missing) data length */ -+#define partial_data data[2] /* partial packet data */ -+ -+static int cx82310_bind(struct usbnet *dev, struct usb_interface *intf) -+{ -+ int ret; -+ char buf[15]; -+ struct usb_device *udev = dev->udev; -+ u8 link[3]; -+ int timeout = 50; -+ -+ /* avoid ADSL modems - continue only if iProduct is "USB NET CARD" */ -+ if (usb_string(udev, udev->descriptor.iProduct, buf, sizeof(buf)) > 0 -+ && strcmp(buf, "USB NET CARD")) { -+ dev_info(&udev->dev, "ignoring: probably an ADSL modem\n"); -+ return -ENODEV; -+ } -+ -+ ret = usbnet_get_endpoints(dev, intf); -+ if (ret) -+ return ret; -+ -+ /* -+ * this must not include ethernet header as the device can send partial -+ * packets with no header (and sometimes even empty URBs) -+ */ -+ dev->net->hard_header_len = 0; -+ /* we can send at most 1514 bytes of data (+ 2-byte header) per URB */ -+ dev->hard_mtu = CX82310_MTU + 2; -+ /* we can receive URBs up to 4KB from the device */ -+ dev->rx_urb_size = 4096; -+ -+ dev->partial_data = (unsigned long) kmalloc(dev->hard_mtu, GFP_KERNEL); -+ if (!dev->partial_data) -+ return -ENOMEM; -+ -+ /* wait for firmware to become ready (indicated by the link being up) */ -+ while (--timeout) { -+ ret = cx82310_cmd(dev, CMD_GET_LINK_STATUS, true, NULL, 0, -+ link, sizeof(link)); -+ /* the command can time out during boot - it's not an error */ -+ if (!ret && link[0] == 1 && link[2] == 1) -+ break; -+ msleep(500); -+ } -+ if (!timeout) { -+ dev_err(&udev->dev, "firmware not ready in time\n"); -+ return -ETIMEDOUT; -+ } -+ -+ /* enable ethernet mode (?) */ -+ ret = cx82310_cmd(dev, CMD_ETHERNET_MODE, true, "\x01", 1, NULL, 0); -+ if (ret) { -+ dev_err(&udev->dev, "unable to enable ethernet mode: %d\n", -+ ret); -+ goto err; -+ } -+ -+ /* get the MAC address */ -+ ret = cx82310_cmd(dev, CMD_GET_MAC_ADDR, true, NULL, 0, -+ dev->net->dev_addr, ETH_ALEN); -+ if (ret) { -+ dev_err(&udev->dev, "unable to read MAC address: %d\n", ret); -+ goto err; -+ } -+ -+ /* start (does not seem to have any effect?) */ -+ ret = cx82310_cmd(dev, CMD_START, false, NULL, 0, NULL, 0); -+ if (ret) -+ goto err; -+ -+ return 0; -+err: -+ kfree((void *)dev->partial_data); -+ return ret; -+} -+ -+static void cx82310_unbind(struct usbnet *dev, struct usb_interface *intf) -+{ -+ kfree((void *)dev->partial_data); -+} -+ -+/* -+ * RX is NOT easy - we can receive multiple packets per skb, each having 2-byte -+ * packet length at the beginning. -+ * The last packet might be incomplete (when it crosses the 4KB URB size), -+ * continuing in the next skb (without any headers). -+ * If a packet has odd length, there is one extra byte at the end (before next -+ * packet or at the end of the URB). -+ */ -+static int cx82310_rx_fixup(struct usbnet *dev, struct sk_buff *skb) -+{ -+ int len; -+ struct sk_buff *skb2; -+ -+ /* -+ * If the last skb ended with an incomplete packet, this skb contains -+ * end of that packet at the beginning. -+ */ -+ if (dev->partial_rem) { -+ len = dev->partial_len + dev->partial_rem; -+ skb2 = alloc_skb(len, GFP_ATOMIC); -+ if (!skb2) -+ return 0; -+ skb_put(skb2, len); -+ memcpy(skb2->data, (void *)dev->partial_data, -+ dev->partial_len); -+ memcpy(skb2->data + dev->partial_len, skb->data, -+ dev->partial_rem); -+ usbnet_skb_return(dev, skb2); -+ skb_pull(skb, (dev->partial_rem + 1) & ~1); -+ dev->partial_rem = 0; -+ if (skb->len < 2) -+ return 1; -+ } -+ -+ /* a skb can contain multiple packets */ -+ while (skb->len > 1) { -+ /* first two bytes are packet length */ -+ len = skb->data[0] | (skb->data[1] << 8); -+ skb_pull(skb, 2); -+ -+ /* if last packet in the skb, let usbnet to process it */ -+ if (len == skb->len || len + 1 == skb->len) { -+ skb_trim(skb, len); -+ break; -+ } -+ -+ if (len > CX82310_MTU) { -+ dev_err(&dev->udev->dev, "RX packet too long: %d B\n", -+ len); -+ return 0; -+ } -+ -+ /* incomplete packet, save it for the next skb */ -+ if (len > skb->len) { -+ dev->partial_len = skb->len; -+ dev->partial_rem = len - skb->len; -+ memcpy((void *)dev->partial_data, skb->data, -+ dev->partial_len); -+ skb_pull(skb, skb->len); -+ break; -+ } -+ -+ skb2 = alloc_skb(len, GFP_ATOMIC); -+ if (!skb2) -+ return 0; -+ skb_put(skb2, len); -+ memcpy(skb2->data, skb->data, len); -+ /* process the packet */ -+ usbnet_skb_return(dev, skb2); -+ -+ skb_pull(skb, (len + 1) & ~1); -+ } -+ -+ /* let usbnet process the last packet */ -+ return 1; -+} -+ -+/* TX is easy, just add 2 bytes of length at the beginning */ -+static struct sk_buff *cx82310_tx_fixup(struct usbnet *dev, struct sk_buff *skb, -+ gfp_t flags) -+{ -+ int len = skb->len; -+ -+ if (skb_headroom(skb) < 2) { -+ struct sk_buff *skb2 = skb_copy_expand(skb, 2, 0, flags); -+ dev_kfree_skb_any(skb); -+ skb = skb2; -+ if (!skb) -+ return NULL; -+ } -+ skb_push(skb, 2); -+ -+ skb->data[0] = len; -+ skb->data[1] = len >> 8; -+ -+ return skb; -+} -+ -+ -+static const struct driver_info cx82310_info = { -+ .description = "Conexant CX82310 USB ethernet", -+ .flags = FLAG_ETHER, -+ .bind = cx82310_bind, -+ .unbind = cx82310_unbind, -+ .rx_fixup = cx82310_rx_fixup, -+ .tx_fixup = cx82310_tx_fixup, -+}; -+ -+#define USB_DEVICE_CLASS(vend, prod, cl, sc, pr) \ -+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ -+ USB_DEVICE_ID_MATCH_DEV_INFO, \ -+ .idVendor = (vend), \ -+ .idProduct = (prod), \ -+ .bDeviceClass = (cl), \ -+ .bDeviceSubClass = (sc), \ -+ .bDeviceProtocol = (pr) -+ -+static const struct usb_device_id products[] = { -+ { -+ USB_DEVICE_CLASS(0x0572, 0xcb01, 0xff, 0, 0), -+ .driver_info = (unsigned long) &cx82310_info -+ }, -+ { }, -+}; -+MODULE_DEVICE_TABLE(usb, products); -+ -+static struct usb_driver cx82310_driver = { -+ .name = "cx82310_eth", -+ .id_table = products, -+ .probe = usbnet_probe, -+ .disconnect = usbnet_disconnect, -+ .suspend = usbnet_suspend, -+ .resume = usbnet_resume, -+ .disable_hub_initiated_lpm = 1, -+}; -+ -+module_usb_driver(cx82310_driver); -+ -+MODULE_AUTHOR("Ondrej Zary"); -+MODULE_DESCRIPTION("Conexant CX82310-based ADSL router USB ethernet driver"); -+MODULE_LICENSE("GPL"); -diff -Naur backports-4.2.6-1.org/drivers/net/usb/dm9601.c backports-4.2.6-1/drivers/net/usb/dm9601.c ---- backports-4.2.6-1.org/drivers/net/usb/dm9601.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/dm9601.c 2016-06-28 14:35:17.978640554 +0200 -@@ -0,0 +1,647 @@ -+/* -+ * Davicom DM96xx USB 10/100Mbps ethernet devices -+ * -+ * Peter Korsgaard -+ * -+ * This file is licensed under the terms of the GNU General Public License -+ * version 2. This program is licensed "as is" without any warranty of any -+ * kind, whether express or implied. -+ */ -+ -+//#define DEBUG -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* datasheet: -+ http://ptm2.cc.utu.fi/ftp/network/cards/DM9601/From_NET/DM9601-DS-P01-930914.pdf -+*/ -+ -+/* control requests */ -+#define DM_READ_REGS 0x00 -+#define DM_WRITE_REGS 0x01 -+#define DM_READ_MEMS 0x02 -+#define DM_WRITE_REG 0x03 -+#define DM_WRITE_MEMS 0x05 -+#define DM_WRITE_MEM 0x07 -+ -+/* registers */ -+#define DM_NET_CTRL 0x00 -+#define DM_RX_CTRL 0x05 -+#define DM_SHARED_CTRL 0x0b -+#define DM_SHARED_ADDR 0x0c -+#define DM_SHARED_DATA 0x0d /* low + high */ -+#define DM_PHY_ADDR 0x10 /* 6 bytes */ -+#define DM_MCAST_ADDR 0x16 /* 8 bytes */ -+#define DM_GPR_CTRL 0x1e -+#define DM_GPR_DATA 0x1f -+#define DM_CHIP_ID 0x2c -+#define DM_MODE_CTRL 0x91 /* only on dm9620 */ -+ -+/* chip id values */ -+#define ID_DM9601 0 -+#define ID_DM9620 1 -+ -+#define DM_MAX_MCAST 64 -+#define DM_MCAST_SIZE 8 -+#define DM_EEPROM_LEN 256 -+#define DM_TX_OVERHEAD 2 /* 2 byte header */ -+#define DM_RX_OVERHEAD 7 /* 3 byte header + 4 byte crc tail */ -+#define DM_TIMEOUT 1000 -+ -+static int dm_read(struct usbnet *dev, u8 reg, u16 length, void *data) -+{ -+ int err; -+ err = usbnet_read_cmd(dev, DM_READ_REGS, -+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, -+ 0, reg, data, length); -+ if(err != length && err >= 0) -+ err = -EINVAL; -+ return err; -+} -+ -+static int dm_read_reg(struct usbnet *dev, u8 reg, u8 *value) -+{ -+ return dm_read(dev, reg, 1, value); -+} -+ -+static int dm_write(struct usbnet *dev, u8 reg, u16 length, void *data) -+{ -+ int err; -+ err = usbnet_write_cmd(dev, DM_WRITE_REGS, -+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, -+ 0, reg, data, length); -+ -+ if (err >= 0 && err < length) -+ err = -EINVAL; -+ return err; -+} -+ -+static int dm_write_reg(struct usbnet *dev, u8 reg, u8 value) -+{ -+ return usbnet_write_cmd(dev, DM_WRITE_REG, -+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, -+ value, reg, NULL, 0); -+} -+ -+static void dm_write_async(struct usbnet *dev, u8 reg, u16 length, void *data) -+{ -+ usbnet_write_cmd_async(dev, DM_WRITE_REGS, -+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, -+ 0, reg, data, length); -+} -+ -+static void dm_write_reg_async(struct usbnet *dev, u8 reg, u8 value) -+{ -+ usbnet_write_cmd_async(dev, DM_WRITE_REG, -+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, -+ value, reg, NULL, 0); -+} -+ -+static int dm_read_shared_word(struct usbnet *dev, int phy, u8 reg, __le16 *value) -+{ -+ int ret, i; -+ -+ mutex_lock(&dev->phy_mutex); -+ -+ dm_write_reg(dev, DM_SHARED_ADDR, phy ? (reg | 0x40) : reg); -+ dm_write_reg(dev, DM_SHARED_CTRL, phy ? 0xc : 0x4); -+ -+ for (i = 0; i < DM_TIMEOUT; i++) { -+ u8 tmp = 0; -+ -+ udelay(1); -+ ret = dm_read_reg(dev, DM_SHARED_CTRL, &tmp); -+ if (ret < 0) -+ goto out; -+ -+ /* ready */ -+ if ((tmp & 1) == 0) -+ break; -+ } -+ -+ if (i == DM_TIMEOUT) { -+ netdev_err(dev->net, "%s read timed out!\n", phy ? "phy" : "eeprom"); -+ ret = -EIO; -+ goto out; -+ } -+ -+ dm_write_reg(dev, DM_SHARED_CTRL, 0x0); -+ ret = dm_read(dev, DM_SHARED_DATA, 2, value); -+ -+ netdev_dbg(dev->net, "read shared %d 0x%02x returned 0x%04x, %d\n", -+ phy, reg, *value, ret); -+ -+ out: -+ mutex_unlock(&dev->phy_mutex); -+ return ret; -+} -+ -+static int dm_write_shared_word(struct usbnet *dev, int phy, u8 reg, __le16 value) -+{ -+ int ret, i; -+ -+ mutex_lock(&dev->phy_mutex); -+ -+ ret = dm_write(dev, DM_SHARED_DATA, 2, &value); -+ if (ret < 0) -+ goto out; -+ -+ dm_write_reg(dev, DM_SHARED_ADDR, phy ? (reg | 0x40) : reg); -+ dm_write_reg(dev, DM_SHARED_CTRL, phy ? 0x1a : 0x12); -+ -+ for (i = 0; i < DM_TIMEOUT; i++) { -+ u8 tmp = 0; -+ -+ udelay(1); -+ ret = dm_read_reg(dev, DM_SHARED_CTRL, &tmp); -+ if (ret < 0) -+ goto out; -+ -+ /* ready */ -+ if ((tmp & 1) == 0) -+ break; -+ } -+ -+ if (i == DM_TIMEOUT) { -+ netdev_err(dev->net, "%s write timed out!\n", phy ? "phy" : "eeprom"); -+ ret = -EIO; -+ goto out; -+ } -+ -+ dm_write_reg(dev, DM_SHARED_CTRL, 0x0); -+ -+out: -+ mutex_unlock(&dev->phy_mutex); -+ return ret; -+} -+ -+static int dm_read_eeprom_word(struct usbnet *dev, u8 offset, void *value) -+{ -+ return dm_read_shared_word(dev, 0, offset, value); -+} -+ -+ -+ -+static int dm9601_get_eeprom_len(struct net_device *dev) -+{ -+ return DM_EEPROM_LEN; -+} -+ -+static int dm9601_get_eeprom(struct net_device *net, -+ struct ethtool_eeprom *eeprom, u8 * data) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ __le16 *ebuf = (__le16 *) data; -+ int i; -+ -+ /* access is 16bit */ -+ if ((eeprom->offset % 2) || (eeprom->len % 2)) -+ return -EINVAL; -+ -+ for (i = 0; i < eeprom->len / 2; i++) { -+ if (dm_read_eeprom_word(dev, eeprom->offset / 2 + i, -+ &ebuf[i]) < 0) -+ return -EINVAL; -+ } -+ return 0; -+} -+ -+static int dm9601_mdio_read(struct net_device *netdev, int phy_id, int loc) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ -+ __le16 res; -+ -+ if (phy_id) { -+ netdev_dbg(dev->net, "Only internal phy supported\n"); -+ return 0; -+ } -+ -+ dm_read_shared_word(dev, 1, loc, &res); -+ -+ netdev_dbg(dev->net, -+ "dm9601_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n", -+ phy_id, loc, le16_to_cpu(res)); -+ -+ return le16_to_cpu(res); -+} -+ -+static void dm9601_mdio_write(struct net_device *netdev, int phy_id, int loc, -+ int val) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ __le16 res = cpu_to_le16(val); -+ -+ if (phy_id) { -+ netdev_dbg(dev->net, "Only internal phy supported\n"); -+ return; -+ } -+ -+ netdev_dbg(dev->net, "dm9601_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n", -+ phy_id, loc, val); -+ -+ dm_write_shared_word(dev, 1, loc, res); -+} -+ -+static void dm9601_get_drvinfo(struct net_device *net, -+ struct ethtool_drvinfo *info) -+{ -+ /* Inherit standard device info */ -+ usbnet_get_drvinfo(net, info); -+ info->eedump_len = DM_EEPROM_LEN; -+} -+ -+static u32 dm9601_get_link(struct net_device *net) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ -+ return mii_link_ok(&dev->mii); -+} -+ -+static int dm9601_ioctl(struct net_device *net, struct ifreq *rq, int cmd) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ -+ return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); -+} -+ -+static const struct ethtool_ops dm9601_ethtool_ops = { -+ .get_drvinfo = dm9601_get_drvinfo, -+ .get_link = dm9601_get_link, -+ .get_msglevel = usbnet_get_msglevel, -+ .set_msglevel = usbnet_set_msglevel, -+ .get_eeprom_len = dm9601_get_eeprom_len, -+ .get_eeprom = dm9601_get_eeprom, -+ .get_settings = usbnet_get_settings, -+ .set_settings = usbnet_set_settings, -+ .nway_reset = usbnet_nway_reset, -+}; -+ -+static void dm9601_set_multicast(struct net_device *net) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ /* We use the 20 byte dev->data for our 8 byte filter buffer -+ * to avoid allocating memory that is tricky to free later */ -+ u8 *hashes = (u8 *) & dev->data; -+ u8 rx_ctl = 0x31; -+ -+ memset(hashes, 0x00, DM_MCAST_SIZE); -+ hashes[DM_MCAST_SIZE - 1] |= 0x80; /* broadcast address */ -+ -+ if (net->flags & IFF_PROMISC) { -+ rx_ctl |= 0x02; -+ } else if (net->flags & IFF_ALLMULTI || -+ netdev_mc_count(net) > DM_MAX_MCAST) { -+ rx_ctl |= 0x08; -+ } else if (!netdev_mc_empty(net)) { -+ struct netdev_hw_addr *ha; -+ -+ netdev_for_each_mc_addr(ha, net) { -+ u32 crc = ether_crc(ETH_ALEN, ha->addr) >> 26; -+ hashes[crc >> 3] |= 1 << (crc & 0x7); -+ } -+ } -+ -+ dm_write_async(dev, DM_MCAST_ADDR, DM_MCAST_SIZE, hashes); -+ dm_write_reg_async(dev, DM_RX_CTRL, rx_ctl); -+} -+ -+static void __dm9601_set_mac_address(struct usbnet *dev) -+{ -+ dm_write_async(dev, DM_PHY_ADDR, ETH_ALEN, dev->net->dev_addr); -+} -+ -+static int dm9601_set_mac_address(struct net_device *net, void *p) -+{ -+ struct sockaddr *addr = p; -+ struct usbnet *dev = netdev_priv(net); -+ -+ if (!is_valid_ether_addr(addr->sa_data)) { -+ dev_err(&net->dev, "not setting invalid mac address %pM\n", -+ addr->sa_data); -+ return -EINVAL; -+ } -+ -+ memcpy(net->dev_addr, addr->sa_data, net->addr_len); -+ __dm9601_set_mac_address(dev); -+ -+ return 0; -+} -+ -+static const struct net_device_ops dm9601_netdev_ops = { -+ .ndo_open = usbnet_open, -+ .ndo_stop = usbnet_stop, -+ .ndo_start_xmit = usbnet_start_xmit, -+ .ndo_tx_timeout = usbnet_tx_timeout, -+ .ndo_change_mtu = usbnet_change_mtu, -+ .ndo_validate_addr = eth_validate_addr, -+ .ndo_do_ioctl = dm9601_ioctl, -+ .ndo_set_rx_mode = dm9601_set_multicast, -+ .ndo_set_mac_address = dm9601_set_mac_address, -+}; -+ -+static int dm9601_bind(struct usbnet *dev, struct usb_interface *intf) -+{ -+ int ret; -+ u8 mac[ETH_ALEN], id; -+ -+ ret = usbnet_get_endpoints(dev, intf); -+ if (ret) -+ goto out; -+ -+ dev->net->netdev_ops = &dm9601_netdev_ops; -+ dev->net->ethtool_ops = &dm9601_ethtool_ops; -+ dev->net->hard_header_len += DM_TX_OVERHEAD; -+ dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; -+ -+ /* dm9620/21a require room for 4 byte padding, even in dm9601 -+ * mode, so we need +1 to be able to receive full size -+ * ethernet frames. -+ */ -+ dev->rx_urb_size = dev->net->mtu + ETH_HLEN + DM_RX_OVERHEAD + 1; -+ -+ dev->mii.dev = dev->net; -+ dev->mii.mdio_read = dm9601_mdio_read; -+ dev->mii.mdio_write = dm9601_mdio_write; -+ dev->mii.phy_id_mask = 0x1f; -+ dev->mii.reg_num_mask = 0x1f; -+ -+ /* reset */ -+ dm_write_reg(dev, DM_NET_CTRL, 1); -+ udelay(20); -+ -+ /* read MAC */ -+ if (dm_read(dev, DM_PHY_ADDR, ETH_ALEN, mac) < 0) { -+ printk(KERN_ERR "Error reading MAC address\n"); -+ ret = -ENODEV; -+ goto out; -+ } -+ -+ /* -+ * Overwrite the auto-generated address only with good ones. -+ */ -+ if (is_valid_ether_addr(mac)) -+ memcpy(dev->net->dev_addr, mac, ETH_ALEN); -+ else { -+ printk(KERN_WARNING -+ "dm9601: No valid MAC address in EEPROM, using %pM\n", -+ dev->net->dev_addr); -+ __dm9601_set_mac_address(dev); -+ } -+ -+ if (dm_read_reg(dev, DM_CHIP_ID, &id) < 0) { -+ netdev_err(dev->net, "Error reading chip ID\n"); -+ ret = -ENODEV; -+ goto out; -+ } -+ -+ /* put dm9620 devices in dm9601 mode */ -+ if (id == ID_DM9620) { -+ u8 mode; -+ -+ if (dm_read_reg(dev, DM_MODE_CTRL, &mode) < 0) { -+ netdev_err(dev->net, "Error reading MODE_CTRL\n"); -+ ret = -ENODEV; -+ goto out; -+ } -+ dm_write_reg(dev, DM_MODE_CTRL, mode & 0x7f); -+ } -+ -+ /* power up phy */ -+ dm_write_reg(dev, DM_GPR_CTRL, 1); -+ dm_write_reg(dev, DM_GPR_DATA, 0); -+ -+ /* receive broadcast packets */ -+ dm9601_set_multicast(dev->net); -+ -+ dm9601_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET); -+ dm9601_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, -+ ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP); -+ mii_nway_restart(&dev->mii); -+ -+out: -+ return ret; -+} -+ -+static int dm9601_rx_fixup(struct usbnet *dev, struct sk_buff *skb) -+{ -+ u8 status; -+ int len; -+ -+ /* format: -+ b1: rx status -+ b2: packet length (incl crc) low -+ b3: packet length (incl crc) high -+ b4..n-4: packet data -+ bn-3..bn: ethernet crc -+ */ -+ -+ if (unlikely(skb->len < DM_RX_OVERHEAD)) { -+ dev_err(&dev->udev->dev, "unexpected tiny rx frame\n"); -+ return 0; -+ } -+ -+ status = skb->data[0]; -+ len = (skb->data[1] | (skb->data[2] << 8)) - 4; -+ -+ if (unlikely(status & 0xbf)) { -+ if (status & 0x01) dev->net->stats.rx_fifo_errors++; -+ if (status & 0x02) dev->net->stats.rx_crc_errors++; -+ if (status & 0x04) dev->net->stats.rx_frame_errors++; -+ if (status & 0x20) dev->net->stats.rx_missed_errors++; -+ if (status & 0x90) dev->net->stats.rx_length_errors++; -+ return 0; -+ } -+ -+ skb_pull(skb, 3); -+ skb_trim(skb, len); -+ -+ return 1; -+} -+ -+static struct sk_buff *dm9601_tx_fixup(struct usbnet *dev, struct sk_buff *skb, -+ gfp_t flags) -+{ -+ int len, pad; -+ -+ /* format: -+ b1: packet length low -+ b2: packet length high -+ b3..n: packet data -+ */ -+ -+ len = skb->len + DM_TX_OVERHEAD; -+ -+ /* workaround for dm962x errata with tx fifo getting out of -+ * sync if a USB bulk transfer retry happens right after a -+ * packet with odd / maxpacket length by adding up to 3 bytes -+ * padding. -+ */ -+ while ((len & 1) || !(len % dev->maxpacket)) -+ len++; -+ -+ len -= DM_TX_OVERHEAD; /* hw header doesn't count as part of length */ -+ pad = len - skb->len; -+ -+ if (skb_headroom(skb) < DM_TX_OVERHEAD || skb_tailroom(skb) < pad) { -+ struct sk_buff *skb2; -+ -+ skb2 = skb_copy_expand(skb, DM_TX_OVERHEAD, pad, flags); -+ dev_kfree_skb_any(skb); -+ skb = skb2; -+ if (!skb) -+ return NULL; -+ } -+ -+ __skb_push(skb, DM_TX_OVERHEAD); -+ -+ if (pad) { -+ memset(skb->data + skb->len, 0, pad); -+ __skb_put(skb, pad); -+ } -+ -+ skb->data[0] = len; -+ skb->data[1] = len >> 8; -+ -+ return skb; -+} -+ -+static void dm9601_status(struct usbnet *dev, struct urb *urb) -+{ -+ int link; -+ u8 *buf; -+ -+ /* format: -+ b0: net status -+ b1: tx status 1 -+ b2: tx status 2 -+ b3: rx status -+ b4: rx overflow -+ b5: rx count -+ b6: tx count -+ b7: gpr -+ */ -+ -+ if (urb->actual_length < 8) -+ return; -+ -+ buf = urb->transfer_buffer; -+ -+ link = !!(buf[0] & 0x40); -+ if (netif_carrier_ok(dev->net) != link) { -+ usbnet_link_change(dev, link, 1); -+ netdev_dbg(dev->net, "Link Status is: %d\n", link); -+ } -+} -+ -+static int dm9601_link_reset(struct usbnet *dev) -+{ -+ struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET }; -+ -+ mii_check_media(&dev->mii, 1, 1); -+ mii_ethtool_gset(&dev->mii, &ecmd); -+ -+ netdev_dbg(dev->net, "link_reset() speed: %u duplex: %d\n", -+ ethtool_cmd_speed(&ecmd), ecmd.duplex); -+ -+ return 0; -+} -+ -+static const struct driver_info dm9601_info = { -+ .description = "Davicom DM96xx USB 10/100 Ethernet", -+ .flags = FLAG_ETHER | FLAG_LINK_INTR, -+ .bind = dm9601_bind, -+ .rx_fixup = dm9601_rx_fixup, -+ .tx_fixup = dm9601_tx_fixup, -+ .status = dm9601_status, -+ .link_reset = dm9601_link_reset, -+ .reset = dm9601_link_reset, -+}; -+ -+static const struct usb_device_id products[] = { -+ { -+ USB_DEVICE(0x07aa, 0x9601), /* Corega FEther USB-TXC */ -+ .driver_info = (unsigned long)&dm9601_info, -+ }, -+ { -+ USB_DEVICE(0x0a46, 0x9601), /* Davicom USB-100 */ -+ .driver_info = (unsigned long)&dm9601_info, -+ }, -+ { -+ USB_DEVICE(0x0a46, 0x6688), /* ZT6688 USB NIC */ -+ .driver_info = (unsigned long)&dm9601_info, -+ }, -+ { -+ USB_DEVICE(0x0a46, 0x0268), /* ShanTou ST268 USB NIC */ -+ .driver_info = (unsigned long)&dm9601_info, -+ }, -+ { -+ USB_DEVICE(0x0a46, 0x8515), /* ADMtek ADM8515 USB NIC */ -+ .driver_info = (unsigned long)&dm9601_info, -+ }, -+ { -+ USB_DEVICE(0x0a47, 0x9601), /* Hirose USB-100 */ -+ .driver_info = (unsigned long)&dm9601_info, -+ }, -+ { -+ USB_DEVICE(0x0fe6, 0x8101), /* DM9601 USB to Fast Ethernet Adapter */ -+ .driver_info = (unsigned long)&dm9601_info, -+ }, -+ { -+ USB_DEVICE(0x0fe6, 0x9700), /* DM9601 USB to Fast Ethernet Adapter */ -+ .driver_info = (unsigned long)&dm9601_info, -+ }, -+ { -+ USB_DEVICE(0x0a46, 0x9000), /* DM9000E */ -+ .driver_info = (unsigned long)&dm9601_info, -+ }, -+ { -+ USB_DEVICE(0x0a46, 0x9620), /* DM9620 USB to Fast Ethernet Adapter */ -+ .driver_info = (unsigned long)&dm9601_info, -+ }, -+ { -+ USB_DEVICE(0x0a46, 0x9621), /* DM9621A USB to Fast Ethernet Adapter */ -+ .driver_info = (unsigned long)&dm9601_info, -+ }, -+ { -+ USB_DEVICE(0x0a46, 0x9622), /* DM9622 USB to Fast Ethernet Adapter */ -+ .driver_info = (unsigned long)&dm9601_info, -+ }, -+ { -+ USB_DEVICE(0x0a46, 0x0269), /* DM962OA USB to Fast Ethernet Adapter */ -+ .driver_info = (unsigned long)&dm9601_info, -+ }, -+ { -+ USB_DEVICE(0x0a46, 0x1269), /* DM9621A USB to Fast Ethernet Adapter */ -+ .driver_info = (unsigned long)&dm9601_info, -+ }, -+ {}, // END -+}; -+ -+MODULE_DEVICE_TABLE(usb, products); -+ -+static struct usb_driver dm9601_driver = { -+ .name = "dm9601", -+ .id_table = products, -+ .probe = usbnet_probe, -+ .disconnect = usbnet_disconnect, -+ .suspend = usbnet_suspend, -+ .resume = usbnet_resume, -+ .disable_hub_initiated_lpm = 1, -+}; -+ -+module_usb_driver(dm9601_driver); -+ -+MODULE_AUTHOR("Peter Korsgaard "); -+MODULE_DESCRIPTION("Davicom DM96xx USB 10/100 ethernet devices"); -+MODULE_LICENSE("GPL"); -diff -Naur backports-4.2.6-1.org/drivers/net/usb/gl620a.c backports-4.2.6-1/drivers/net/usb/gl620a.c ---- backports-4.2.6-1.org/drivers/net/usb/gl620a.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/gl620a.c 2016-06-28 14:35:17.978640554 +0200 -@@ -0,0 +1,242 @@ -+/* -+ * GeneSys GL620USB-A based links -+ * Copyright (C) 2001 by Jiun-Jie Huang -+ * Copyright (C) 2001 by Stanislav Brabec -+ * -+ * 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, see . -+ */ -+ -+// #define DEBUG // error path messages, extra info -+// #define VERBOSE // more; success messages -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+ -+/* -+ * GeneSys GL620USB-A (www.genesyslogic.com.tw) -+ * -+ * ... should partially interop with the Win32 driver for this hardware. -+ * The GeneSys docs imply there's some NDIS issue motivating this framing. -+ * -+ * Some info from GeneSys: -+ * - GL620USB-A is full duplex; GL620USB is only half duplex for bulk. -+ * (Some cables, like the BAFO-100c, use the half duplex version.) -+ * - For the full duplex model, the low bit of the version code says -+ * which side is which ("left/right"). -+ * - For the half duplex type, a control/interrupt handshake settles -+ * the transfer direction. (That's disabled here, partially coded.) -+ * A control URB would block until other side writes an interrupt. -+ * -+ * Original code from Jiun-Jie Huang -+ * and merged into "usbnet" by Stanislav Brabec . -+ */ -+ -+// control msg write command -+#define GENELINK_CONNECT_WRITE 0xF0 -+// interrupt pipe index -+#define GENELINK_INTERRUPT_PIPE 0x03 -+// interrupt read buffer size -+#define INTERRUPT_BUFSIZE 0x08 -+// interrupt pipe interval value -+#define GENELINK_INTERRUPT_INTERVAL 0x10 -+// max transmit packet number per transmit -+#define GL_MAX_TRANSMIT_PACKETS 32 -+// max packet length -+#define GL_MAX_PACKET_LEN 1514 -+// max receive buffer size -+#define GL_RCV_BUF_SIZE \ -+ (((GL_MAX_PACKET_LEN + 4) * GL_MAX_TRANSMIT_PACKETS) + 4) -+ -+struct gl_packet { -+ __le32 packet_length; -+ char packet_data [1]; -+}; -+ -+struct gl_header { -+ __le32 packet_count; -+ struct gl_packet packets; -+}; -+ -+static int genelink_rx_fixup(struct usbnet *dev, struct sk_buff *skb) -+{ -+ struct gl_header *header; -+ struct gl_packet *packet; -+ struct sk_buff *gl_skb; -+ u32 size; -+ u32 count; -+ -+ /* This check is no longer done by usbnet */ -+ if (skb->len < dev->net->hard_header_len) -+ return 0; -+ -+ header = (struct gl_header *) skb->data; -+ -+ // get the packet count of the received skb -+ count = le32_to_cpu(header->packet_count); -+ if (count > GL_MAX_TRANSMIT_PACKETS) { -+ netdev_dbg(dev->net, -+ "genelink: invalid received packet count %u\n", -+ count); -+ return 0; -+ } -+ -+ // set the current packet pointer to the first packet -+ packet = &header->packets; -+ -+ // decrement the length for the packet count size 4 bytes -+ skb_pull(skb, 4); -+ -+ while (count > 1) { -+ // get the packet length -+ size = le32_to_cpu(packet->packet_length); -+ -+ // this may be a broken packet -+ if (size > GL_MAX_PACKET_LEN) { -+ netdev_dbg(dev->net, "genelink: invalid rx length %d\n", -+ size); -+ return 0; -+ } -+ -+ // allocate the skb for the individual packet -+ gl_skb = alloc_skb(size, GFP_ATOMIC); -+ if (gl_skb) { -+ -+ // copy the packet data to the new skb -+ memcpy(skb_put(gl_skb, size), -+ packet->packet_data, size); -+ usbnet_skb_return(dev, gl_skb); -+ } -+ -+ // advance to the next packet -+ packet = (struct gl_packet *)&packet->packet_data[size]; -+ count--; -+ -+ // shift the data pointer to the next gl_packet -+ skb_pull(skb, size + 4); -+ } -+ -+ // skip the packet length field 4 bytes -+ skb_pull(skb, 4); -+ -+ if (skb->len > GL_MAX_PACKET_LEN) { -+ netdev_dbg(dev->net, "genelink: invalid rx length %d\n", -+ skb->len); -+ return 0; -+ } -+ return 1; -+} -+ -+static struct sk_buff * -+genelink_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) -+{ -+ int padlen; -+ int length = skb->len; -+ int headroom = skb_headroom(skb); -+ int tailroom = skb_tailroom(skb); -+ __le32 *packet_count; -+ __le32 *packet_len; -+ -+ // FIXME: magic numbers, bleech -+ padlen = ((skb->len + (4 + 4*1)) % 64) ? 0 : 1; -+ -+ if ((!skb_cloned(skb)) -+ && ((headroom + tailroom) >= (padlen + (4 + 4*1)))) { -+ if ((headroom < (4 + 4*1)) || (tailroom < padlen)) { -+ skb->data = memmove(skb->head + (4 + 4*1), -+ skb->data, skb->len); -+ skb_set_tail_pointer(skb, skb->len); -+ } -+ } else { -+ struct sk_buff *skb2; -+ skb2 = skb_copy_expand(skb, (4 + 4*1) , padlen, flags); -+ dev_kfree_skb_any(skb); -+ skb = skb2; -+ if (!skb) -+ return NULL; -+ } -+ -+ // attach the packet count to the header -+ packet_count = (__le32 *) skb_push(skb, (4 + 4*1)); -+ packet_len = packet_count + 1; -+ -+ *packet_count = cpu_to_le32(1); -+ *packet_len = cpu_to_le32(length); -+ -+ // add padding byte -+ if ((skb->len % dev->maxpacket) == 0) -+ skb_put(skb, 1); -+ -+ return skb; -+} -+ -+static int genelink_bind(struct usbnet *dev, struct usb_interface *intf) -+{ -+ dev->hard_mtu = GL_RCV_BUF_SIZE; -+ dev->net->hard_header_len += 4; -+ dev->in = usb_rcvbulkpipe(dev->udev, dev->driver_info->in); -+ dev->out = usb_sndbulkpipe(dev->udev, dev->driver_info->out); -+ return 0; -+} -+ -+static const struct driver_info genelink_info = { -+ .description = "Genesys GeneLink", -+ .flags = FLAG_POINTTOPOINT | FLAG_FRAMING_GL | FLAG_NO_SETINT, -+ .bind = genelink_bind, -+ .rx_fixup = genelink_rx_fixup, -+ .tx_fixup = genelink_tx_fixup, -+ -+ .in = 1, .out = 2, -+ -+#ifdef GENELINK_ACK -+ .check_connect =genelink_check_connect, -+#endif -+}; -+ -+static const struct usb_device_id products [] = { -+ -+{ -+ USB_DEVICE(0x05e3, 0x0502), // GL620USB-A -+ .driver_info = (unsigned long) &genelink_info, -+}, -+ /* NOT: USB_DEVICE(0x05e3, 0x0501), // GL620USB -+ * that's half duplex, not currently supported -+ */ -+ { }, // END -+}; -+MODULE_DEVICE_TABLE(usb, products); -+ -+static struct usb_driver gl620a_driver = { -+ .name = "gl620a", -+ .id_table = products, -+ .probe = usbnet_probe, -+ .disconnect = usbnet_disconnect, -+ .suspend = usbnet_suspend, -+ .resume = usbnet_resume, -+ .disable_hub_initiated_lpm = 1, -+}; -+ -+module_usb_driver(gl620a_driver); -+ -+MODULE_AUTHOR("Jiun-Jie Huang"); -+MODULE_DESCRIPTION("GL620-USB-A Host-to-Host Link cables"); -+MODULE_LICENSE("GPL"); -+ -diff -Naur backports-4.2.6-1.org/drivers/net/usb/hso.c backports-4.2.6-1/drivers/net/usb/hso.c ---- backports-4.2.6-1.org/drivers/net/usb/hso.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/hso.c 2016-06-28 14:35:17.981973887 +0200 -@@ -0,0 +1,3322 @@ -+/****************************************************************************** -+ * -+ * Driver for Option High Speed Mobile Devices. -+ * -+ * Copyright (C) 2008 Option International -+ * Filip Aben -+ * Denis Joseph Barrow -+ * Jan Dumon -+ * Copyright (C) 2007 Andrew Bird (Sphere Systems Ltd) -+ * -+ * Copyright (C) 2008 Greg Kroah-Hartman -+ * Copyright (C) 2008 Novell, Inc. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, -+ * USA -+ * -+ * -+ *****************************************************************************/ -+ -+/****************************************************************************** -+ * -+ * Description of the device: -+ * -+ * Interface 0: Contains the IP network interface on the bulk end points. -+ * The multiplexed serial ports are using the interrupt and -+ * control endpoints. -+ * Interrupt contains a bitmap telling which multiplexed -+ * serialport needs servicing. -+ * -+ * Interface 1: Diagnostics port, uses bulk only, do not submit urbs until the -+ * port is opened, as this have a huge impact on the network port -+ * throughput. -+ * -+ * Interface 2: Standard modem interface - circuit switched interface, this -+ * can be used to make a standard ppp connection however it -+ * should not be used in conjunction with the IP network interface -+ * enabled for USB performance reasons i.e. if using this set -+ * ideally disable_net=1. -+ * -+ *****************************************************************************/ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define MOD_AUTHOR "Option Wireless" -+#define MOD_DESCRIPTION "USB High Speed Option driver" -+#define MOD_LICENSE "GPL" -+ -+#define HSO_MAX_NET_DEVICES 10 -+#define HSO__MAX_MTU 2048 -+#define DEFAULT_MTU 1500 -+#define DEFAULT_MRU 1500 -+ -+#define CTRL_URB_RX_SIZE 1024 -+#define CTRL_URB_TX_SIZE 64 -+ -+#define BULK_URB_RX_SIZE 4096 -+#define BULK_URB_TX_SIZE 8192 -+ -+#define MUX_BULK_RX_BUF_SIZE HSO__MAX_MTU -+#define MUX_BULK_TX_BUF_SIZE HSO__MAX_MTU -+#define MUX_BULK_RX_BUF_COUNT 4 -+#define USB_TYPE_OPTION_VENDOR 0x20 -+ -+/* These definitions are used with the struct hso_net flags element */ -+/* - use *_bit operations on it. (bit indices not values.) */ -+#define HSO_NET_RUNNING 0 -+ -+#define HSO_NET_TX_TIMEOUT (HZ*10) -+ -+#define HSO_SERIAL_MAGIC 0x48534f31 -+ -+/* Number of ttys to handle */ -+#define HSO_SERIAL_TTY_MINORS 256 -+ -+#define MAX_RX_URBS 2 -+ -+/*****************************************************************************/ -+/* Debugging functions */ -+/*****************************************************************************/ -+#define D__(lvl_, fmt, arg...) \ -+ do { \ -+ printk(lvl_ "[%d:%s]: " fmt "\n", \ -+ __LINE__, __func__, ## arg); \ -+ } while (0) -+ -+#define D_(lvl, args...) \ -+ do { \ -+ if (lvl & debug) \ -+ D__(KERN_INFO, args); \ -+ } while (0) -+ -+#define D1(args...) D_(0x01, ##args) -+#define D2(args...) D_(0x02, ##args) -+#define D3(args...) D_(0x04, ##args) -+#define D4(args...) D_(0x08, ##args) -+#define D5(args...) D_(0x10, ##args) -+ -+/*****************************************************************************/ -+/* Enumerators */ -+/*****************************************************************************/ -+enum pkt_parse_state { -+ WAIT_IP, -+ WAIT_DATA, -+ WAIT_SYNC -+}; -+ -+/*****************************************************************************/ -+/* Structs */ -+/*****************************************************************************/ -+ -+struct hso_shared_int { -+ struct usb_endpoint_descriptor *intr_endp; -+ void *shared_intr_buf; -+ struct urb *shared_intr_urb; -+ struct usb_device *usb; -+ int use_count; -+ int ref_count; -+ struct mutex shared_int_lock; -+}; -+ -+struct hso_net { -+ struct hso_device *parent; -+ struct net_device *net; -+ struct rfkill *rfkill; -+ char name[24]; -+ -+ struct usb_endpoint_descriptor *in_endp; -+ struct usb_endpoint_descriptor *out_endp; -+ -+ struct urb *mux_bulk_rx_urb_pool[MUX_BULK_RX_BUF_COUNT]; -+ struct urb *mux_bulk_tx_urb; -+ void *mux_bulk_rx_buf_pool[MUX_BULK_RX_BUF_COUNT]; -+ void *mux_bulk_tx_buf; -+ -+ struct sk_buff *skb_rx_buf; -+ struct sk_buff *skb_tx_buf; -+ -+ enum pkt_parse_state rx_parse_state; -+ spinlock_t net_lock; -+ -+ unsigned short rx_buf_size; -+ unsigned short rx_buf_missing; -+ struct iphdr rx_ip_hdr; -+ -+ unsigned long flags; -+}; -+ -+enum rx_ctrl_state{ -+ RX_IDLE, -+ RX_SENT, -+ RX_PENDING -+}; -+ -+#define BM_REQUEST_TYPE (0xa1) -+#define B_NOTIFICATION (0x20) -+#define W_VALUE (0x0) -+#define W_LENGTH (0x2) -+ -+#define B_OVERRUN (0x1<<6) -+#define B_PARITY (0x1<<5) -+#define B_FRAMING (0x1<<4) -+#define B_RING_SIGNAL (0x1<<3) -+#define B_BREAK (0x1<<2) -+#define B_TX_CARRIER (0x1<<1) -+#define B_RX_CARRIER (0x1<<0) -+ -+struct hso_serial_state_notification { -+ u8 bmRequestType; -+ u8 bNotification; -+ u16 wValue; -+ u16 wIndex; -+ u16 wLength; -+ u16 UART_state_bitmap; -+} __packed; -+ -+struct hso_tiocmget { -+ struct mutex mutex; -+ wait_queue_head_t waitq; -+ int intr_completed; -+ struct usb_endpoint_descriptor *endp; -+ struct urb *urb; -+ struct hso_serial_state_notification serial_state_notification; -+ u16 prev_UART_state_bitmap; -+ struct uart_icount icount; -+}; -+ -+ -+struct hso_serial { -+ struct hso_device *parent; -+ int magic; -+ u8 minor; -+ -+ struct hso_shared_int *shared_int; -+ -+ /* rx/tx urb could be either a bulk urb or a control urb depending -+ on which serial port it is used on. */ -+ struct urb *rx_urb[MAX_RX_URBS]; -+ u8 num_rx_urbs; -+ u8 *rx_data[MAX_RX_URBS]; -+ u16 rx_data_length; /* should contain allocated length */ -+ -+ struct urb *tx_urb; -+ u8 *tx_data; -+ u8 *tx_buffer; -+ u16 tx_data_length; /* should contain allocated length */ -+ u16 tx_data_count; -+ u16 tx_buffer_count; -+ struct usb_ctrlrequest ctrl_req_tx; -+ struct usb_ctrlrequest ctrl_req_rx; -+ -+ struct usb_endpoint_descriptor *in_endp; -+ struct usb_endpoint_descriptor *out_endp; -+ -+ enum rx_ctrl_state rx_state; -+ u8 rts_state; -+ u8 dtr_state; -+ unsigned tx_urb_used:1; -+ -+ struct tty_port port; -+ /* from usb_serial_port */ -+ spinlock_t serial_lock; -+ -+ int (*write_data) (struct hso_serial *serial); -+ struct hso_tiocmget *tiocmget; -+ /* Hacks required to get flow control -+ * working on the serial receive buffers -+ * so as not to drop characters on the floor. -+ */ -+ int curr_rx_urb_idx; -+ u8 rx_urb_filled[MAX_RX_URBS]; -+ struct tasklet_struct unthrottle_tasklet; -+}; -+ -+struct hso_device { -+ union { -+ struct hso_serial *dev_serial; -+ struct hso_net *dev_net; -+ } port_data; -+ -+ u32 port_spec; -+ -+ u8 is_active; -+ u8 usb_gone; -+ struct work_struct async_get_intf; -+ struct work_struct async_put_intf; -+ -+ struct usb_device *usb; -+ struct usb_interface *interface; -+ -+ struct device *dev; -+ struct kref ref; -+ struct mutex mutex; -+}; -+ -+/* Type of interface */ -+#define HSO_INTF_MASK 0xFF00 -+#define HSO_INTF_MUX 0x0100 -+#define HSO_INTF_BULK 0x0200 -+ -+/* Type of port */ -+#define HSO_PORT_MASK 0xFF -+#define HSO_PORT_NO_PORT 0x0 -+#define HSO_PORT_CONTROL 0x1 -+#define HSO_PORT_APP 0x2 -+#define HSO_PORT_GPS 0x3 -+#define HSO_PORT_PCSC 0x4 -+#define HSO_PORT_APP2 0x5 -+#define HSO_PORT_GPS_CONTROL 0x6 -+#define HSO_PORT_MSD 0x7 -+#define HSO_PORT_VOICE 0x8 -+#define HSO_PORT_DIAG2 0x9 -+#define HSO_PORT_DIAG 0x10 -+#define HSO_PORT_MODEM 0x11 -+#define HSO_PORT_NETWORK 0x12 -+ -+/* Additional device info */ -+#define HSO_INFO_MASK 0xFF000000 -+#define HSO_INFO_CRC_BUG 0x01000000 -+ -+/*****************************************************************************/ -+/* Prototypes */ -+/*****************************************************************************/ -+/* Serial driver functions */ -+static int hso_serial_tiocmset(struct tty_struct *tty, -+ unsigned int set, unsigned int clear); -+static void ctrl_callback(struct urb *urb); -+static int put_rxbuf_data(struct urb *urb, struct hso_serial *serial); -+static void hso_kick_transmit(struct hso_serial *serial); -+/* Helper functions */ -+static int hso_mux_submit_intr_urb(struct hso_shared_int *mux_int, -+ struct usb_device *usb, gfp_t gfp); -+static void handle_usb_error(int status, const char *function, -+ struct hso_device *hso_dev); -+static struct usb_endpoint_descriptor *hso_get_ep(struct usb_interface *intf, -+ int type, int dir); -+static int hso_get_mux_ports(struct usb_interface *intf, unsigned char *ports); -+static void hso_free_interface(struct usb_interface *intf); -+static int hso_start_serial_device(struct hso_device *hso_dev, gfp_t flags); -+static int hso_stop_serial_device(struct hso_device *hso_dev); -+static int hso_start_net_device(struct hso_device *hso_dev); -+static void hso_free_shared_int(struct hso_shared_int *shared_int); -+static int hso_stop_net_device(struct hso_device *hso_dev); -+static void hso_serial_ref_free(struct kref *ref); -+static void hso_std_serial_read_bulk_callback(struct urb *urb); -+static int hso_mux_serial_read(struct hso_serial *serial); -+static void async_get_intf(struct work_struct *data); -+static void async_put_intf(struct work_struct *data); -+static int hso_put_activity(struct hso_device *hso_dev); -+static int hso_get_activity(struct hso_device *hso_dev); -+static void tiocmget_intr_callback(struct urb *urb); -+/*****************************************************************************/ -+/* Helping functions */ -+/*****************************************************************************/ -+ -+/* #define DEBUG */ -+ -+static inline struct hso_net *dev2net(struct hso_device *hso_dev) -+{ -+ return hso_dev->port_data.dev_net; -+} -+ -+static inline struct hso_serial *dev2ser(struct hso_device *hso_dev) -+{ -+ return hso_dev->port_data.dev_serial; -+} -+ -+/* Debugging functions */ -+#ifdef DEBUG -+static void dbg_dump(int line_count, const char *func_name, unsigned char *buf, -+ unsigned int len) -+{ -+ static char name[255]; -+ -+ sprintf(name, "hso[%d:%s]", line_count, func_name); -+ print_hex_dump_bytes(name, DUMP_PREFIX_NONE, buf, len); -+} -+ -+#define DUMP(buf_, len_) \ -+ dbg_dump(__LINE__, __func__, (unsigned char *)buf_, len_) -+ -+#define DUMP1(buf_, len_) \ -+ do { \ -+ if (0x01 & debug) \ -+ DUMP(buf_, len_); \ -+ } while (0) -+#else -+#define DUMP(buf_, len_) -+#define DUMP1(buf_, len_) -+#endif -+ -+/* module parameters */ -+static int debug; -+static int tty_major; -+static int disable_net; -+ -+/* driver info */ -+static const char driver_name[] = "hso"; -+static const char tty_filename[] = "ttyHS"; -+static const char *version = __FILE__ ": " MOD_AUTHOR; -+/* the usb driver itself (registered in hso_init) */ -+static struct usb_driver hso_driver; -+/* serial structures */ -+static struct tty_driver *tty_drv; -+static struct hso_device *serial_table[HSO_SERIAL_TTY_MINORS]; -+static struct hso_device *network_table[HSO_MAX_NET_DEVICES]; -+static spinlock_t serial_table_lock; -+ -+static const s32 default_port_spec[] = { -+ HSO_INTF_MUX | HSO_PORT_NETWORK, -+ HSO_INTF_BULK | HSO_PORT_DIAG, -+ HSO_INTF_BULK | HSO_PORT_MODEM, -+ 0 -+}; -+ -+static const s32 icon321_port_spec[] = { -+ HSO_INTF_MUX | HSO_PORT_NETWORK, -+ HSO_INTF_BULK | HSO_PORT_DIAG2, -+ HSO_INTF_BULK | HSO_PORT_MODEM, -+ HSO_INTF_BULK | HSO_PORT_DIAG, -+ 0 -+}; -+ -+#define default_port_device(vendor, product) \ -+ USB_DEVICE(vendor, product), \ -+ .driver_info = (kernel_ulong_t)default_port_spec -+ -+#define icon321_port_device(vendor, product) \ -+ USB_DEVICE(vendor, product), \ -+ .driver_info = (kernel_ulong_t)icon321_port_spec -+ -+/* list of devices we support */ -+static const struct usb_device_id hso_ids[] = { -+ {default_port_device(0x0af0, 0x6711)}, -+ {default_port_device(0x0af0, 0x6731)}, -+ {default_port_device(0x0af0, 0x6751)}, -+ {default_port_device(0x0af0, 0x6771)}, -+ {default_port_device(0x0af0, 0x6791)}, -+ {default_port_device(0x0af0, 0x6811)}, -+ {default_port_device(0x0af0, 0x6911)}, -+ {default_port_device(0x0af0, 0x6951)}, -+ {default_port_device(0x0af0, 0x6971)}, -+ {default_port_device(0x0af0, 0x7011)}, -+ {default_port_device(0x0af0, 0x7031)}, -+ {default_port_device(0x0af0, 0x7051)}, -+ {default_port_device(0x0af0, 0x7071)}, -+ {default_port_device(0x0af0, 0x7111)}, -+ {default_port_device(0x0af0, 0x7211)}, -+ {default_port_device(0x0af0, 0x7251)}, -+ {default_port_device(0x0af0, 0x7271)}, -+ {default_port_device(0x0af0, 0x7311)}, -+ {default_port_device(0x0af0, 0xc031)}, /* Icon-Edge */ -+ {icon321_port_device(0x0af0, 0xd013)}, /* Module HSxPA */ -+ {icon321_port_device(0x0af0, 0xd031)}, /* Icon-321 */ -+ {icon321_port_device(0x0af0, 0xd033)}, /* Icon-322 */ -+ {USB_DEVICE(0x0af0, 0x7301)}, /* GE40x */ -+ {USB_DEVICE(0x0af0, 0x7361)}, /* GE40x */ -+ {USB_DEVICE(0x0af0, 0x7381)}, /* GE40x */ -+ {USB_DEVICE(0x0af0, 0x7401)}, /* GI 0401 */ -+ {USB_DEVICE(0x0af0, 0x7501)}, /* GTM 382 */ -+ {USB_DEVICE(0x0af0, 0x7601)}, /* GE40x */ -+ {USB_DEVICE(0x0af0, 0x7701)}, -+ {USB_DEVICE(0x0af0, 0x7706)}, -+ {USB_DEVICE(0x0af0, 0x7801)}, -+ {USB_DEVICE(0x0af0, 0x7901)}, -+ {USB_DEVICE(0x0af0, 0x7A01)}, -+ {USB_DEVICE(0x0af0, 0x7A05)}, -+ {USB_DEVICE(0x0af0, 0x8200)}, -+ {USB_DEVICE(0x0af0, 0x8201)}, -+ {USB_DEVICE(0x0af0, 0x8300)}, -+ {USB_DEVICE(0x0af0, 0x8302)}, -+ {USB_DEVICE(0x0af0, 0x8304)}, -+ {USB_DEVICE(0x0af0, 0x8400)}, -+ {USB_DEVICE(0x0af0, 0x8600)}, -+ {USB_DEVICE(0x0af0, 0x8800)}, -+ {USB_DEVICE(0x0af0, 0x8900)}, -+ {USB_DEVICE(0x0af0, 0x9000)}, -+ {USB_DEVICE(0x0af0, 0x9200)}, /* Option GTM671WFS */ -+ {USB_DEVICE(0x0af0, 0xd035)}, -+ {USB_DEVICE(0x0af0, 0xd055)}, -+ {USB_DEVICE(0x0af0, 0xd155)}, -+ {USB_DEVICE(0x0af0, 0xd255)}, -+ {USB_DEVICE(0x0af0, 0xd057)}, -+ {USB_DEVICE(0x0af0, 0xd157)}, -+ {USB_DEVICE(0x0af0, 0xd257)}, -+ {USB_DEVICE(0x0af0, 0xd357)}, -+ {USB_DEVICE(0x0af0, 0xd058)}, -+ {USB_DEVICE(0x0af0, 0xc100)}, -+ {} -+}; -+MODULE_DEVICE_TABLE(usb, hso_ids); -+ -+/* Sysfs attribute */ -+static ssize_t hso_sysfs_show_porttype(struct device *dev, -+ struct device_attribute *attr, -+ char *buf) -+{ -+ struct hso_device *hso_dev = dev_get_drvdata(dev); -+ char *port_name; -+ -+ if (!hso_dev) -+ return 0; -+ -+ switch (hso_dev->port_spec & HSO_PORT_MASK) { -+ case HSO_PORT_CONTROL: -+ port_name = "Control"; -+ break; -+ case HSO_PORT_APP: -+ port_name = "Application"; -+ break; -+ case HSO_PORT_APP2: -+ port_name = "Application2"; -+ break; -+ case HSO_PORT_GPS: -+ port_name = "GPS"; -+ break; -+ case HSO_PORT_GPS_CONTROL: -+ port_name = "GPS Control"; -+ break; -+ case HSO_PORT_PCSC: -+ port_name = "PCSC"; -+ break; -+ case HSO_PORT_DIAG: -+ port_name = "Diagnostic"; -+ break; -+ case HSO_PORT_DIAG2: -+ port_name = "Diagnostic2"; -+ break; -+ case HSO_PORT_MODEM: -+ port_name = "Modem"; -+ break; -+ case HSO_PORT_NETWORK: -+ port_name = "Network"; -+ break; -+ default: -+ port_name = "Unknown"; -+ break; -+ } -+ -+ return sprintf(buf, "%s\n", port_name); -+} -+static DEVICE_ATTR(hsotype, S_IRUGO, hso_sysfs_show_porttype, NULL); -+ -+static struct attribute *hso_serial_dev_attrs[] = { -+ &dev_attr_hsotype.attr, -+ NULL -+}; -+ -+ATTRIBUTE_GROUPS(hso_serial_dev); -+ -+static int hso_urb_to_index(struct hso_serial *serial, struct urb *urb) -+{ -+ int idx; -+ -+ for (idx = 0; idx < serial->num_rx_urbs; idx++) -+ if (serial->rx_urb[idx] == urb) -+ return idx; -+ dev_err(serial->parent->dev, "hso_urb_to_index failed\n"); -+ return -1; -+} -+ -+/* converts mux value to a port spec value */ -+static u32 hso_mux_to_port(int mux) -+{ -+ u32 result; -+ -+ switch (mux) { -+ case 0x1: -+ result = HSO_PORT_CONTROL; -+ break; -+ case 0x2: -+ result = HSO_PORT_APP; -+ break; -+ case 0x4: -+ result = HSO_PORT_PCSC; -+ break; -+ case 0x8: -+ result = HSO_PORT_GPS; -+ break; -+ case 0x10: -+ result = HSO_PORT_APP2; -+ break; -+ default: -+ result = HSO_PORT_NO_PORT; -+ } -+ return result; -+} -+ -+/* converts port spec value to a mux value */ -+static u32 hso_port_to_mux(int port) -+{ -+ u32 result; -+ -+ switch (port & HSO_PORT_MASK) { -+ case HSO_PORT_CONTROL: -+ result = 0x0; -+ break; -+ case HSO_PORT_APP: -+ result = 0x1; -+ break; -+ case HSO_PORT_PCSC: -+ result = 0x2; -+ break; -+ case HSO_PORT_GPS: -+ result = 0x3; -+ break; -+ case HSO_PORT_APP2: -+ result = 0x4; -+ break; -+ default: -+ result = 0x0; -+ } -+ return result; -+} -+ -+static struct hso_serial *get_serial_by_shared_int_and_type( -+ struct hso_shared_int *shared_int, -+ int mux) -+{ -+ int i, port; -+ -+ port = hso_mux_to_port(mux); -+ -+ for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) { -+ if (serial_table[i] && -+ (dev2ser(serial_table[i])->shared_int == shared_int) && -+ ((serial_table[i]->port_spec & HSO_PORT_MASK) == port)) { -+ return dev2ser(serial_table[i]); -+ } -+ } -+ -+ return NULL; -+} -+ -+static struct hso_serial *get_serial_by_index(unsigned index) -+{ -+ struct hso_serial *serial = NULL; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&serial_table_lock, flags); -+ if (serial_table[index]) -+ serial = dev2ser(serial_table[index]); -+ spin_unlock_irqrestore(&serial_table_lock, flags); -+ -+ return serial; -+} -+ -+static int get_free_serial_index(void) -+{ -+ int index; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&serial_table_lock, flags); -+ for (index = 0; index < HSO_SERIAL_TTY_MINORS; index++) { -+ if (serial_table[index] == NULL) { -+ spin_unlock_irqrestore(&serial_table_lock, flags); -+ return index; -+ } -+ } -+ spin_unlock_irqrestore(&serial_table_lock, flags); -+ -+ printk(KERN_ERR "%s: no free serial devices in table\n", __func__); -+ return -1; -+} -+ -+static void set_serial_by_index(unsigned index, struct hso_serial *serial) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&serial_table_lock, flags); -+ if (serial) -+ serial_table[index] = serial->parent; -+ else -+ serial_table[index] = NULL; -+ spin_unlock_irqrestore(&serial_table_lock, flags); -+} -+ -+static void handle_usb_error(int status, const char *function, -+ struct hso_device *hso_dev) -+{ -+ char *explanation; -+ -+ switch (status) { -+ case -ENODEV: -+ explanation = "no device"; -+ break; -+ case -ENOENT: -+ explanation = "endpoint not enabled"; -+ break; -+ case -EPIPE: -+ explanation = "endpoint stalled"; -+ break; -+ case -ENOSPC: -+ explanation = "not enough bandwidth"; -+ break; -+ case -ESHUTDOWN: -+ explanation = "device disabled"; -+ break; -+ case -EHOSTUNREACH: -+ explanation = "device suspended"; -+ break; -+ case -EINVAL: -+ case -EAGAIN: -+ case -EFBIG: -+ case -EMSGSIZE: -+ explanation = "internal error"; -+ break; -+ case -EILSEQ: -+ case -EPROTO: -+ case -ETIME: -+ case -ETIMEDOUT: -+ explanation = "protocol error"; -+ if (hso_dev) -+ usb_queue_reset_device(hso_dev->interface); -+ break; -+ default: -+ explanation = "unknown status"; -+ break; -+ } -+ -+ /* log a meaningful explanation of an USB status */ -+ D1("%s: received USB status - %s (%d)", function, explanation, status); -+} -+ -+/* Network interface functions */ -+ -+/* called when net interface is brought up by ifconfig */ -+static int hso_net_open(struct net_device *net) -+{ -+ struct hso_net *odev = netdev_priv(net); -+ unsigned long flags = 0; -+ -+ if (!odev) { -+ dev_err(&net->dev, "No net device !\n"); -+ return -ENODEV; -+ } -+ -+ odev->skb_tx_buf = NULL; -+ -+ /* setup environment */ -+ spin_lock_irqsave(&odev->net_lock, flags); -+ odev->rx_parse_state = WAIT_IP; -+ odev->rx_buf_size = 0; -+ odev->rx_buf_missing = sizeof(struct iphdr); -+ spin_unlock_irqrestore(&odev->net_lock, flags); -+ -+ /* We are up and running. */ -+ set_bit(HSO_NET_RUNNING, &odev->flags); -+ hso_start_net_device(odev->parent); -+ -+ /* Tell the kernel we are ready to start receiving from it */ -+ netif_start_queue(net); -+ -+ return 0; -+} -+ -+/* called when interface is brought down by ifconfig */ -+static int hso_net_close(struct net_device *net) -+{ -+ struct hso_net *odev = netdev_priv(net); -+ -+ /* we don't need the queue anymore */ -+ netif_stop_queue(net); -+ /* no longer running */ -+ clear_bit(HSO_NET_RUNNING, &odev->flags); -+ -+ hso_stop_net_device(odev->parent); -+ -+ /* done */ -+ return 0; -+} -+ -+/* USB tells is xmit done, we should start the netqueue again */ -+static void write_bulk_callback(struct urb *urb) -+{ -+ struct hso_net *odev = urb->context; -+ int status = urb->status; -+ -+ /* Sanity check */ -+ if (!odev || !test_bit(HSO_NET_RUNNING, &odev->flags)) { -+ dev_err(&urb->dev->dev, "%s: device not running\n", __func__); -+ return; -+ } -+ -+ /* Do we still have a valid kernel network device? */ -+ if (!netif_device_present(odev->net)) { -+ dev_err(&urb->dev->dev, "%s: net device not present\n", -+ __func__); -+ return; -+ } -+ -+ /* log status, but don't act on it, we don't need to resubmit anything -+ * anyhow */ -+ if (status) -+ handle_usb_error(status, __func__, odev->parent); -+ -+ hso_put_activity(odev->parent); -+ -+ /* Tell the network interface we are ready for another frame */ -+ netif_wake_queue(odev->net); -+} -+ -+/* called by kernel when we need to transmit a packet */ -+static netdev_tx_t hso_net_start_xmit(struct sk_buff *skb, -+ struct net_device *net) -+{ -+ struct hso_net *odev = netdev_priv(net); -+ int result; -+ -+ /* Tell the kernel, "No more frames 'til we are done with this one." */ -+ netif_stop_queue(net); -+ if (hso_get_activity(odev->parent) == -EAGAIN) { -+ odev->skb_tx_buf = skb; -+ return NETDEV_TX_OK; -+ } -+ -+ /* log if asked */ -+ DUMP1(skb->data, skb->len); -+ /* Copy it from kernel memory to OUR memory */ -+ memcpy(odev->mux_bulk_tx_buf, skb->data, skb->len); -+ D1("len: %d/%d", skb->len, MUX_BULK_TX_BUF_SIZE); -+ -+ /* Fill in the URB for shipping it out. */ -+ usb_fill_bulk_urb(odev->mux_bulk_tx_urb, -+ odev->parent->usb, -+ usb_sndbulkpipe(odev->parent->usb, -+ odev->out_endp-> -+ bEndpointAddress & 0x7F), -+ odev->mux_bulk_tx_buf, skb->len, write_bulk_callback, -+ odev); -+ -+ /* Deal with the Zero Length packet problem, I hope */ -+ odev->mux_bulk_tx_urb->transfer_flags |= URB_ZERO_PACKET; -+ -+ /* Send the URB on its merry way. */ -+ result = usb_submit_urb(odev->mux_bulk_tx_urb, GFP_ATOMIC); -+ if (result) { -+ dev_warn(&odev->parent->interface->dev, -+ "failed mux_bulk_tx_urb %d\n", result); -+ net->stats.tx_errors++; -+ netif_start_queue(net); -+ } else { -+ net->stats.tx_packets++; -+ net->stats.tx_bytes += skb->len; -+ } -+ dev_kfree_skb(skb); -+ /* we're done */ -+ return NETDEV_TX_OK; -+} -+ -+static const struct ethtool_ops ops = { -+ .get_link = ethtool_op_get_link -+}; -+ -+/* called when a packet did not ack after watchdogtimeout */ -+static void hso_net_tx_timeout(struct net_device *net) -+{ -+ struct hso_net *odev = netdev_priv(net); -+ -+ if (!odev) -+ return; -+ -+ /* Tell syslog we are hosed. */ -+ dev_warn(&net->dev, "Tx timed out.\n"); -+ -+ /* Tear the waiting frame off the list */ -+ if (odev->mux_bulk_tx_urb && -+ (odev->mux_bulk_tx_urb->status == -EINPROGRESS)) -+ usb_unlink_urb(odev->mux_bulk_tx_urb); -+ -+ /* Update statistics */ -+ net->stats.tx_errors++; -+} -+ -+/* make a real packet from the received USB buffer */ -+static void packetizeRx(struct hso_net *odev, unsigned char *ip_pkt, -+ unsigned int count, unsigned char is_eop) -+{ -+ unsigned short temp_bytes; -+ unsigned short buffer_offset = 0; -+ unsigned short frame_len; -+ unsigned char *tmp_rx_buf; -+ -+ /* log if needed */ -+ D1("Rx %d bytes", count); -+ DUMP(ip_pkt, min(128, (int)count)); -+ -+ while (count) { -+ switch (odev->rx_parse_state) { -+ case WAIT_IP: -+ /* waiting for IP header. */ -+ /* wanted bytes - size of ip header */ -+ temp_bytes = -+ (count < -+ odev->rx_buf_missing) ? count : odev-> -+ rx_buf_missing; -+ -+ memcpy(((unsigned char *)(&odev->rx_ip_hdr)) + -+ odev->rx_buf_size, ip_pkt + buffer_offset, -+ temp_bytes); -+ -+ odev->rx_buf_size += temp_bytes; -+ buffer_offset += temp_bytes; -+ odev->rx_buf_missing -= temp_bytes; -+ count -= temp_bytes; -+ -+ if (!odev->rx_buf_missing) { -+ /* header is complete allocate an sk_buffer and -+ * continue to WAIT_DATA */ -+ frame_len = ntohs(odev->rx_ip_hdr.tot_len); -+ -+ if ((frame_len > DEFAULT_MRU) || -+ (frame_len < sizeof(struct iphdr))) { -+ dev_err(&odev->net->dev, -+ "Invalid frame (%d) length\n", -+ frame_len); -+ odev->rx_parse_state = WAIT_SYNC; -+ continue; -+ } -+ /* Allocate an sk_buff */ -+ odev->skb_rx_buf = netdev_alloc_skb(odev->net, -+ frame_len); -+ if (!odev->skb_rx_buf) { -+ /* We got no receive buffer. */ -+ D1("could not allocate memory"); -+ odev->rx_parse_state = WAIT_SYNC; -+ continue; -+ } -+ -+ /* Copy what we got so far. make room for iphdr -+ * after tail. */ -+ tmp_rx_buf = -+ skb_put(odev->skb_rx_buf, -+ sizeof(struct iphdr)); -+ memcpy(tmp_rx_buf, (char *)&(odev->rx_ip_hdr), -+ sizeof(struct iphdr)); -+ -+ /* ETH_HLEN */ -+ odev->rx_buf_size = sizeof(struct iphdr); -+ -+ /* Filip actually use .tot_len */ -+ odev->rx_buf_missing = -+ frame_len - sizeof(struct iphdr); -+ odev->rx_parse_state = WAIT_DATA; -+ } -+ break; -+ -+ case WAIT_DATA: -+ temp_bytes = (count < odev->rx_buf_missing) -+ ? count : odev->rx_buf_missing; -+ -+ /* Copy the rest of the bytes that are left in the -+ * buffer into the waiting sk_buf. */ -+ /* Make room for temp_bytes after tail. */ -+ tmp_rx_buf = skb_put(odev->skb_rx_buf, temp_bytes); -+ memcpy(tmp_rx_buf, ip_pkt + buffer_offset, temp_bytes); -+ -+ odev->rx_buf_missing -= temp_bytes; -+ count -= temp_bytes; -+ buffer_offset += temp_bytes; -+ odev->rx_buf_size += temp_bytes; -+ if (!odev->rx_buf_missing) { -+ /* Packet is complete. Inject into stack. */ -+ /* We have IP packet here */ -+ odev->skb_rx_buf->protocol = cpu_to_be16(ETH_P_IP); -+ skb_reset_mac_header(odev->skb_rx_buf); -+ -+ /* Ship it off to the kernel */ -+ netif_rx(odev->skb_rx_buf); -+ /* No longer our buffer. */ -+ odev->skb_rx_buf = NULL; -+ -+ /* update out statistics */ -+ odev->net->stats.rx_packets++; -+ -+ odev->net->stats.rx_bytes += odev->rx_buf_size; -+ -+ odev->rx_buf_size = 0; -+ odev->rx_buf_missing = sizeof(struct iphdr); -+ odev->rx_parse_state = WAIT_IP; -+ } -+ break; -+ -+ case WAIT_SYNC: -+ D1(" W_S"); -+ count = 0; -+ break; -+ default: -+ D1(" "); -+ count--; -+ break; -+ } -+ } -+ -+ /* Recovery mechanism for WAIT_SYNC state. */ -+ if (is_eop) { -+ if (odev->rx_parse_state == WAIT_SYNC) { -+ odev->rx_parse_state = WAIT_IP; -+ odev->rx_buf_size = 0; -+ odev->rx_buf_missing = sizeof(struct iphdr); -+ } -+ } -+} -+ -+static void fix_crc_bug(struct urb *urb, __le16 max_packet_size) -+{ -+ static const u8 crc_check[4] = { 0xDE, 0xAD, 0xBE, 0xEF }; -+ u32 rest = urb->actual_length % le16_to_cpu(max_packet_size); -+ -+ if (((rest == 5) || (rest == 6)) && -+ !memcmp(((u8 *)urb->transfer_buffer) + urb->actual_length - 4, -+ crc_check, 4)) { -+ urb->actual_length -= 4; -+ } -+} -+ -+/* Moving data from usb to kernel (in interrupt state) */ -+static void read_bulk_callback(struct urb *urb) -+{ -+ struct hso_net *odev = urb->context; -+ struct net_device *net; -+ int result; -+ int status = urb->status; -+ -+ /* is al ok? (Filip: Who's Al ?) */ -+ if (status) { -+ handle_usb_error(status, __func__, odev->parent); -+ return; -+ } -+ -+ /* Sanity check */ -+ if (!odev || !test_bit(HSO_NET_RUNNING, &odev->flags)) { -+ D1("BULK IN callback but driver is not active!"); -+ return; -+ } -+ usb_mark_last_busy(urb->dev); -+ -+ net = odev->net; -+ -+ if (!netif_device_present(net)) { -+ /* Somebody killed our network interface... */ -+ return; -+ } -+ -+ if (odev->parent->port_spec & HSO_INFO_CRC_BUG) -+ fix_crc_bug(urb, odev->in_endp->wMaxPacketSize); -+ -+ /* do we even have a packet? */ -+ if (urb->actual_length) { -+ /* Handle the IP stream, add header and push it onto network -+ * stack if the packet is complete. */ -+ spin_lock(&odev->net_lock); -+ packetizeRx(odev, urb->transfer_buffer, urb->actual_length, -+ (urb->transfer_buffer_length > -+ urb->actual_length) ? 1 : 0); -+ spin_unlock(&odev->net_lock); -+ } -+ -+ /* We are done with this URB, resubmit it. Prep the USB to wait for -+ * another frame. Reuse same as received. */ -+ usb_fill_bulk_urb(urb, -+ odev->parent->usb, -+ usb_rcvbulkpipe(odev->parent->usb, -+ odev->in_endp-> -+ bEndpointAddress & 0x7F), -+ urb->transfer_buffer, MUX_BULK_RX_BUF_SIZE, -+ read_bulk_callback, odev); -+ -+ /* Give this to the USB subsystem so it can tell us when more data -+ * arrives. */ -+ result = usb_submit_urb(urb, GFP_ATOMIC); -+ if (result) -+ dev_warn(&odev->parent->interface->dev, -+ "%s failed submit mux_bulk_rx_urb %d\n", __func__, -+ result); -+} -+ -+/* Serial driver functions */ -+ -+static void hso_init_termios(struct ktermios *termios) -+{ -+ /* -+ * The default requirements for this device are: -+ */ -+ termios->c_iflag &= -+ ~(IGNBRK /* disable ignore break */ -+ | BRKINT /* disable break causes interrupt */ -+ | PARMRK /* disable mark parity errors */ -+ | ISTRIP /* disable clear high bit of input characters */ -+ | INLCR /* disable translate NL to CR */ -+ | IGNCR /* disable ignore CR */ -+ | ICRNL /* disable translate CR to NL */ -+ | IXON); /* disable enable XON/XOFF flow control */ -+ -+ /* disable postprocess output characters */ -+ termios->c_oflag &= ~OPOST; -+ -+ termios->c_lflag &= -+ ~(ECHO /* disable echo input characters */ -+ | ECHONL /* disable echo new line */ -+ | ICANON /* disable erase, kill, werase, and rprnt -+ special characters */ -+ | ISIG /* disable interrupt, quit, and suspend special -+ characters */ -+ | IEXTEN); /* disable non-POSIX special characters */ -+ -+ termios->c_cflag &= -+ ~(CSIZE /* no size */ -+ | PARENB /* disable parity bit */ -+ | CBAUD /* clear current baud rate */ -+ | CBAUDEX); /* clear current buad rate */ -+ -+ termios->c_cflag |= CS8; /* character size 8 bits */ -+ -+ /* baud rate 115200 */ -+ tty_termios_encode_baud_rate(termios, 115200, 115200); -+} -+ -+static void _hso_serial_set_termios(struct tty_struct *tty, -+ struct ktermios *old) -+{ -+ struct hso_serial *serial = tty->driver_data; -+ -+ if (!serial) { -+ printk(KERN_ERR "%s: no tty structures", __func__); -+ return; -+ } -+ -+ D4("port %d", serial->minor); -+ -+ /* -+ * Fix up unsupported bits -+ */ -+ tty->termios.c_iflag &= ~IXON; /* disable enable XON/XOFF flow control */ -+ -+ tty->termios.c_cflag &= -+ ~(CSIZE /* no size */ -+ | PARENB /* disable parity bit */ -+ | CBAUD /* clear current baud rate */ -+ | CBAUDEX); /* clear current buad rate */ -+ -+ tty->termios.c_cflag |= CS8; /* character size 8 bits */ -+ -+ /* baud rate 115200 */ -+ tty_encode_baud_rate(tty, 115200, 115200); -+} -+ -+static void hso_resubmit_rx_bulk_urb(struct hso_serial *serial, struct urb *urb) -+{ -+ int result; -+ /* We are done with this URB, resubmit it. Prep the USB to wait for -+ * another frame */ -+ usb_fill_bulk_urb(urb, serial->parent->usb, -+ usb_rcvbulkpipe(serial->parent->usb, -+ serial->in_endp-> -+ bEndpointAddress & 0x7F), -+ urb->transfer_buffer, serial->rx_data_length, -+ hso_std_serial_read_bulk_callback, serial); -+ /* Give this to the USB subsystem so it can tell us when more data -+ * arrives. */ -+ result = usb_submit_urb(urb, GFP_ATOMIC); -+ if (result) { -+ dev_err(&urb->dev->dev, "%s failed submit serial rx_urb %d\n", -+ __func__, result); -+ } -+} -+ -+ -+ -+ -+static void put_rxbuf_data_and_resubmit_bulk_urb(struct hso_serial *serial) -+{ -+ int count; -+ struct urb *curr_urb; -+ -+ while (serial->rx_urb_filled[serial->curr_rx_urb_idx]) { -+ curr_urb = serial->rx_urb[serial->curr_rx_urb_idx]; -+ count = put_rxbuf_data(curr_urb, serial); -+ if (count == -1) -+ return; -+ if (count == 0) { -+ serial->curr_rx_urb_idx++; -+ if (serial->curr_rx_urb_idx >= serial->num_rx_urbs) -+ serial->curr_rx_urb_idx = 0; -+ hso_resubmit_rx_bulk_urb(serial, curr_urb); -+ } -+ } -+} -+ -+static void put_rxbuf_data_and_resubmit_ctrl_urb(struct hso_serial *serial) -+{ -+ int count = 0; -+ struct urb *urb; -+ -+ urb = serial->rx_urb[0]; -+ if (atomic_read(&serial->port.count) > 0) { -+ count = put_rxbuf_data(urb, serial); -+ if (count == -1) -+ return; -+ } -+ /* Re issue a read as long as we receive data. */ -+ -+ if (count == 0 && ((urb->actual_length != 0) || -+ (serial->rx_state == RX_PENDING))) { -+ serial->rx_state = RX_SENT; -+ hso_mux_serial_read(serial); -+ } else -+ serial->rx_state = RX_IDLE; -+} -+ -+ -+/* read callback for Diag and CS port */ -+static void hso_std_serial_read_bulk_callback(struct urb *urb) -+{ -+ struct hso_serial *serial = urb->context; -+ int status = urb->status; -+ -+ D4("\n--- Got serial_read_bulk callback %02x ---", status); -+ -+ /* sanity check */ -+ if (!serial) { -+ D1("serial == NULL"); -+ return; -+ } -+ if (status) { -+ handle_usb_error(status, __func__, serial->parent); -+ return; -+ } -+ -+ D1("Actual length = %d\n", urb->actual_length); -+ DUMP1(urb->transfer_buffer, urb->actual_length); -+ -+ /* Anyone listening? */ -+ if (atomic_read(&serial->port.count) == 0) -+ return; -+ -+ if (serial->parent->port_spec & HSO_INFO_CRC_BUG) -+ fix_crc_bug(urb, serial->in_endp->wMaxPacketSize); -+ /* Valid data, handle RX data */ -+ spin_lock(&serial->serial_lock); -+ serial->rx_urb_filled[hso_urb_to_index(serial, urb)] = 1; -+ put_rxbuf_data_and_resubmit_bulk_urb(serial); -+ spin_unlock(&serial->serial_lock); -+} -+ -+/* -+ * This needs to be a tasklet otherwise we will -+ * end up recursively calling this function. -+ */ -+static void hso_unthrottle_tasklet(struct hso_serial *serial) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&serial->serial_lock, flags); -+ if ((serial->parent->port_spec & HSO_INTF_MUX)) -+ put_rxbuf_data_and_resubmit_ctrl_urb(serial); -+ else -+ put_rxbuf_data_and_resubmit_bulk_urb(serial); -+ spin_unlock_irqrestore(&serial->serial_lock, flags); -+} -+ -+static void hso_unthrottle(struct tty_struct *tty) -+{ -+ struct hso_serial *serial = tty->driver_data; -+ -+ tasklet_hi_schedule(&serial->unthrottle_tasklet); -+} -+ -+/* open the requested serial port */ -+static int hso_serial_open(struct tty_struct *tty, struct file *filp) -+{ -+ struct hso_serial *serial = get_serial_by_index(tty->index); -+ int result; -+ -+ /* sanity check */ -+ if (serial == NULL || serial->magic != HSO_SERIAL_MAGIC) { -+ WARN_ON(1); -+ tty->driver_data = NULL; -+ D1("Failed to open port"); -+ return -ENODEV; -+ } -+ -+ mutex_lock(&serial->parent->mutex); -+ result = usb_autopm_get_interface(serial->parent->interface); -+ if (result < 0) -+ goto err_out; -+ -+ D1("Opening %d", serial->minor); -+ -+ /* setup */ -+ tty->driver_data = serial; -+ tty_port_tty_set(&serial->port, tty); -+ -+ /* check for port already opened, if not set the termios */ -+ if (atomic_inc_return(&serial->port.count) == 1) { -+ serial->rx_state = RX_IDLE; -+ /* Force default termio settings */ -+ _hso_serial_set_termios(tty, NULL); -+ tasklet_init(&serial->unthrottle_tasklet, -+ (void (*)(unsigned long))hso_unthrottle_tasklet, -+ (unsigned long)serial); -+ result = hso_start_serial_device(serial->parent, GFP_KERNEL); -+ if (result) { -+ hso_stop_serial_device(serial->parent); -+ atomic_dec(&serial->port.count); -+ } else { -+ kref_get(&serial->parent->ref); -+ } -+ } else { -+ D1("Port was already open"); -+ } -+ -+ usb_autopm_put_interface(serial->parent->interface); -+ -+ /* done */ -+ if (result) -+ hso_serial_tiocmset(tty, TIOCM_RTS | TIOCM_DTR, 0); -+err_out: -+ mutex_unlock(&serial->parent->mutex); -+ return result; -+} -+ -+/* close the requested serial port */ -+static void hso_serial_close(struct tty_struct *tty, struct file *filp) -+{ -+ struct hso_serial *serial = tty->driver_data; -+ u8 usb_gone; -+ -+ D1("Closing serial port"); -+ -+ /* Open failed, no close cleanup required */ -+ if (serial == NULL) -+ return; -+ -+ mutex_lock(&serial->parent->mutex); -+ usb_gone = serial->parent->usb_gone; -+ -+ if (!usb_gone) -+ usb_autopm_get_interface(serial->parent->interface); -+ -+ /* reset the rts and dtr */ -+ /* do the actual close */ -+ atomic_dec(&serial->port.count); -+ -+ if (atomic_read(&serial->port.count) <= 0) { -+ atomic_set(&serial->port.count, 0); -+ tty_port_tty_set(&serial->port, NULL); -+ if (!usb_gone) -+ hso_stop_serial_device(serial->parent); -+ tasklet_kill(&serial->unthrottle_tasklet); -+ } -+ -+ if (!usb_gone) -+ usb_autopm_put_interface(serial->parent->interface); -+ -+ mutex_unlock(&serial->parent->mutex); -+} -+ -+/* close the requested serial port */ -+static int hso_serial_write(struct tty_struct *tty, const unsigned char *buf, -+ int count) -+{ -+ struct hso_serial *serial = tty->driver_data; -+ int space, tx_bytes; -+ unsigned long flags; -+ -+ /* sanity check */ -+ if (serial == NULL) { -+ printk(KERN_ERR "%s: serial is NULL\n", __func__); -+ return -ENODEV; -+ } -+ -+ spin_lock_irqsave(&serial->serial_lock, flags); -+ -+ space = serial->tx_data_length - serial->tx_buffer_count; -+ tx_bytes = (count < space) ? count : space; -+ -+ if (!tx_bytes) -+ goto out; -+ -+ memcpy(serial->tx_buffer + serial->tx_buffer_count, buf, tx_bytes); -+ serial->tx_buffer_count += tx_bytes; -+ -+out: -+ spin_unlock_irqrestore(&serial->serial_lock, flags); -+ -+ hso_kick_transmit(serial); -+ /* done */ -+ return tx_bytes; -+} -+ -+/* how much room is there for writing */ -+static int hso_serial_write_room(struct tty_struct *tty) -+{ -+ struct hso_serial *serial = tty->driver_data; -+ int room; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&serial->serial_lock, flags); -+ room = serial->tx_data_length - serial->tx_buffer_count; -+ spin_unlock_irqrestore(&serial->serial_lock, flags); -+ -+ /* return free room */ -+ return room; -+} -+ -+static void hso_serial_cleanup(struct tty_struct *tty) -+{ -+ struct hso_serial *serial = tty->driver_data; -+ -+ if (!serial) -+ return; -+ -+ kref_put(&serial->parent->ref, hso_serial_ref_free); -+} -+ -+/* setup the term */ -+static void hso_serial_set_termios(struct tty_struct *tty, struct ktermios *old) -+{ -+ struct hso_serial *serial = tty->driver_data; -+ unsigned long flags; -+ -+ if (old) -+ D5("Termios called with: cflags new[%d] - old[%d]", -+ tty->termios.c_cflag, old->c_cflag); -+ -+ /* the actual setup */ -+ spin_lock_irqsave(&serial->serial_lock, flags); -+ if (atomic_read(&serial->port.count)) -+ _hso_serial_set_termios(tty, old); -+ else -+ tty->termios = *old; -+ spin_unlock_irqrestore(&serial->serial_lock, flags); -+ -+ /* done */ -+} -+ -+/* how many characters in the buffer */ -+static int hso_serial_chars_in_buffer(struct tty_struct *tty) -+{ -+ struct hso_serial *serial = tty->driver_data; -+ int chars; -+ unsigned long flags; -+ -+ /* sanity check */ -+ if (serial == NULL) -+ return 0; -+ -+ spin_lock_irqsave(&serial->serial_lock, flags); -+ chars = serial->tx_buffer_count; -+ spin_unlock_irqrestore(&serial->serial_lock, flags); -+ -+ return chars; -+} -+static int tiocmget_submit_urb(struct hso_serial *serial, -+ struct hso_tiocmget *tiocmget, -+ struct usb_device *usb) -+{ -+ int result; -+ -+ if (serial->parent->usb_gone) -+ return -ENODEV; -+ usb_fill_int_urb(tiocmget->urb, usb, -+ usb_rcvintpipe(usb, -+ tiocmget->endp-> -+ bEndpointAddress & 0x7F), -+ &tiocmget->serial_state_notification, -+ sizeof(struct hso_serial_state_notification), -+ tiocmget_intr_callback, serial, -+ tiocmget->endp->bInterval); -+ result = usb_submit_urb(tiocmget->urb, GFP_ATOMIC); -+ if (result) { -+ dev_warn(&usb->dev, "%s usb_submit_urb failed %d\n", __func__, -+ result); -+ } -+ return result; -+ -+} -+ -+static void tiocmget_intr_callback(struct urb *urb) -+{ -+ struct hso_serial *serial = urb->context; -+ struct hso_tiocmget *tiocmget; -+ int status = urb->status; -+ u16 UART_state_bitmap, prev_UART_state_bitmap; -+ struct uart_icount *icount; -+ struct hso_serial_state_notification *serial_state_notification; -+ struct usb_device *usb; -+ struct usb_interface *interface; -+ int if_num; -+ -+ /* Sanity checks */ -+ if (!serial) -+ return; -+ if (status) { -+ handle_usb_error(status, __func__, serial->parent); -+ return; -+ } -+ -+ /* tiocmget is only supported on HSO_PORT_MODEM */ -+ tiocmget = serial->tiocmget; -+ if (!tiocmget) -+ return; -+ BUG_ON((serial->parent->port_spec & HSO_PORT_MASK) != HSO_PORT_MODEM); -+ -+ usb = serial->parent->usb; -+ interface = serial->parent->interface; -+ -+ if_num = interface->cur_altsetting->desc.bInterfaceNumber; -+ -+ /* wIndex should be the USB interface number of the port to which the -+ * notification applies, which should always be the Modem port. -+ */ -+ serial_state_notification = &tiocmget->serial_state_notification; -+ if (serial_state_notification->bmRequestType != BM_REQUEST_TYPE || -+ serial_state_notification->bNotification != B_NOTIFICATION || -+ le16_to_cpu(serial_state_notification->wValue) != W_VALUE || -+ le16_to_cpu(serial_state_notification->wIndex) != if_num || -+ le16_to_cpu(serial_state_notification->wLength) != W_LENGTH) { -+ dev_warn(&usb->dev, -+ "hso received invalid serial state notification\n"); -+ DUMP(serial_state_notification, -+ sizeof(struct hso_serial_state_notification)); -+ } else { -+ -+ UART_state_bitmap = le16_to_cpu(serial_state_notification-> -+ UART_state_bitmap); -+ prev_UART_state_bitmap = tiocmget->prev_UART_state_bitmap; -+ icount = &tiocmget->icount; -+ spin_lock(&serial->serial_lock); -+ if ((UART_state_bitmap & B_OVERRUN) != -+ (prev_UART_state_bitmap & B_OVERRUN)) -+ icount->parity++; -+ if ((UART_state_bitmap & B_PARITY) != -+ (prev_UART_state_bitmap & B_PARITY)) -+ icount->parity++; -+ if ((UART_state_bitmap & B_FRAMING) != -+ (prev_UART_state_bitmap & B_FRAMING)) -+ icount->frame++; -+ if ((UART_state_bitmap & B_RING_SIGNAL) && -+ !(prev_UART_state_bitmap & B_RING_SIGNAL)) -+ icount->rng++; -+ if ((UART_state_bitmap & B_BREAK) != -+ (prev_UART_state_bitmap & B_BREAK)) -+ icount->brk++; -+ if ((UART_state_bitmap & B_TX_CARRIER) != -+ (prev_UART_state_bitmap & B_TX_CARRIER)) -+ icount->dsr++; -+ if ((UART_state_bitmap & B_RX_CARRIER) != -+ (prev_UART_state_bitmap & B_RX_CARRIER)) -+ icount->dcd++; -+ tiocmget->prev_UART_state_bitmap = UART_state_bitmap; -+ spin_unlock(&serial->serial_lock); -+ tiocmget->intr_completed = 1; -+ wake_up_interruptible(&tiocmget->waitq); -+ } -+ memset(serial_state_notification, 0, -+ sizeof(struct hso_serial_state_notification)); -+ tiocmget_submit_urb(serial, -+ tiocmget, -+ serial->parent->usb); -+} -+ -+/* -+ * next few functions largely stolen from drivers/serial/serial_core.c -+ */ -+/* Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change -+ * - mask passed in arg for lines of interest -+ * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) -+ * Caller should use TIOCGICOUNT to see which one it was -+ */ -+static int -+hso_wait_modem_status(struct hso_serial *serial, unsigned long arg) -+{ -+ DECLARE_WAITQUEUE(wait, current); -+ struct uart_icount cprev, cnow; -+ struct hso_tiocmget *tiocmget; -+ int ret; -+ -+ tiocmget = serial->tiocmget; -+ if (!tiocmget) -+ return -ENOENT; -+ /* -+ * note the counters on entry -+ */ -+ spin_lock_irq(&serial->serial_lock); -+ memcpy(&cprev, &tiocmget->icount, sizeof(struct uart_icount)); -+ spin_unlock_irq(&serial->serial_lock); -+ add_wait_queue(&tiocmget->waitq, &wait); -+ for (;;) { -+ spin_lock_irq(&serial->serial_lock); -+ memcpy(&cnow, &tiocmget->icount, sizeof(struct uart_icount)); -+ spin_unlock_irq(&serial->serial_lock); -+ set_current_state(TASK_INTERRUPTIBLE); -+ if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || -+ ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || -+ ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd))) { -+ ret = 0; -+ break; -+ } -+ schedule(); -+ /* see if a signal did it */ -+ if (signal_pending(current)) { -+ ret = -ERESTARTSYS; -+ break; -+ } -+ cprev = cnow; -+ } -+ __set_current_state(TASK_RUNNING); -+ remove_wait_queue(&tiocmget->waitq, &wait); -+ -+ return ret; -+} -+ -+/* -+ * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) -+ * Return: write counters to the user passed counter struct -+ * NB: both 1->0 and 0->1 transitions are counted except for -+ * RI where only 0->1 is counted. -+ */ -+static int hso_get_count(struct tty_struct *tty, -+ struct serial_icounter_struct *icount) -+{ -+ struct uart_icount cnow; -+ struct hso_serial *serial = tty->driver_data; -+ struct hso_tiocmget *tiocmget = serial->tiocmget; -+ -+ memset(icount, 0, sizeof(struct serial_icounter_struct)); -+ -+ if (!tiocmget) -+ return -ENOENT; -+ spin_lock_irq(&serial->serial_lock); -+ memcpy(&cnow, &tiocmget->icount, sizeof(struct uart_icount)); -+ spin_unlock_irq(&serial->serial_lock); -+ -+ icount->cts = cnow.cts; -+ icount->dsr = cnow.dsr; -+ icount->rng = cnow.rng; -+ icount->dcd = cnow.dcd; -+ icount->rx = cnow.rx; -+ icount->tx = cnow.tx; -+ icount->frame = cnow.frame; -+ icount->overrun = cnow.overrun; -+ icount->parity = cnow.parity; -+ icount->brk = cnow.brk; -+ icount->buf_overrun = cnow.buf_overrun; -+ -+ return 0; -+} -+ -+ -+static int hso_serial_tiocmget(struct tty_struct *tty) -+{ -+ int retval; -+ struct hso_serial *serial = tty->driver_data; -+ struct hso_tiocmget *tiocmget; -+ u16 UART_state_bitmap; -+ -+ /* sanity check */ -+ if (!serial) { -+ D1("no tty structures"); -+ return -EINVAL; -+ } -+ spin_lock_irq(&serial->serial_lock); -+ retval = ((serial->rts_state) ? TIOCM_RTS : 0) | -+ ((serial->dtr_state) ? TIOCM_DTR : 0); -+ tiocmget = serial->tiocmget; -+ if (tiocmget) { -+ -+ UART_state_bitmap = le16_to_cpu( -+ tiocmget->prev_UART_state_bitmap); -+ if (UART_state_bitmap & B_RING_SIGNAL) -+ retval |= TIOCM_RNG; -+ if (UART_state_bitmap & B_RX_CARRIER) -+ retval |= TIOCM_CD; -+ if (UART_state_bitmap & B_TX_CARRIER) -+ retval |= TIOCM_DSR; -+ } -+ spin_unlock_irq(&serial->serial_lock); -+ return retval; -+} -+ -+static int hso_serial_tiocmset(struct tty_struct *tty, -+ unsigned int set, unsigned int clear) -+{ -+ int val = 0; -+ unsigned long flags; -+ int if_num; -+ struct hso_serial *serial = tty->driver_data; -+ struct usb_interface *interface; -+ -+ /* sanity check */ -+ if (!serial) { -+ D1("no tty structures"); -+ return -EINVAL; -+ } -+ -+ if ((serial->parent->port_spec & HSO_PORT_MASK) != HSO_PORT_MODEM) -+ return -EINVAL; -+ -+ interface = serial->parent->interface; -+ if_num = interface->cur_altsetting->desc.bInterfaceNumber; -+ -+ spin_lock_irqsave(&serial->serial_lock, flags); -+ if (set & TIOCM_RTS) -+ serial->rts_state = 1; -+ if (set & TIOCM_DTR) -+ serial->dtr_state = 1; -+ -+ if (clear & TIOCM_RTS) -+ serial->rts_state = 0; -+ if (clear & TIOCM_DTR) -+ serial->dtr_state = 0; -+ -+ if (serial->dtr_state) -+ val |= 0x01; -+ if (serial->rts_state) -+ val |= 0x02; -+ -+ spin_unlock_irqrestore(&serial->serial_lock, flags); -+ -+ return usb_control_msg(serial->parent->usb, -+ usb_rcvctrlpipe(serial->parent->usb, 0), 0x22, -+ 0x21, val, if_num, NULL, 0, -+ USB_CTRL_SET_TIMEOUT); -+} -+ -+static int hso_serial_ioctl(struct tty_struct *tty, -+ unsigned int cmd, unsigned long arg) -+{ -+ struct hso_serial *serial = tty->driver_data; -+ int ret = 0; -+ D4("IOCTL cmd: %d, arg: %ld", cmd, arg); -+ -+ if (!serial) -+ return -ENODEV; -+ switch (cmd) { -+ case TIOCMIWAIT: -+ ret = hso_wait_modem_status(serial, arg); -+ break; -+ default: -+ ret = -ENOIOCTLCMD; -+ break; -+ } -+ return ret; -+} -+ -+ -+/* starts a transmit */ -+static void hso_kick_transmit(struct hso_serial *serial) -+{ -+ u8 *temp; -+ unsigned long flags; -+ int res; -+ -+ spin_lock_irqsave(&serial->serial_lock, flags); -+ if (!serial->tx_buffer_count) -+ goto out; -+ -+ if (serial->tx_urb_used) -+ goto out; -+ -+ /* Wakeup USB interface if necessary */ -+ if (hso_get_activity(serial->parent) == -EAGAIN) -+ goto out; -+ -+ /* Switch pointers around to avoid memcpy */ -+ temp = serial->tx_buffer; -+ serial->tx_buffer = serial->tx_data; -+ serial->tx_data = temp; -+ serial->tx_data_count = serial->tx_buffer_count; -+ serial->tx_buffer_count = 0; -+ -+ /* If temp is set, it means we switched buffers */ -+ if (temp && serial->write_data) { -+ res = serial->write_data(serial); -+ if (res >= 0) -+ serial->tx_urb_used = 1; -+ } -+out: -+ spin_unlock_irqrestore(&serial->serial_lock, flags); -+} -+ -+/* make a request (for reading and writing data to muxed serial port) */ -+static int mux_device_request(struct hso_serial *serial, u8 type, u16 port, -+ struct urb *ctrl_urb, -+ struct usb_ctrlrequest *ctrl_req, -+ u8 *ctrl_urb_data, u32 size) -+{ -+ int result; -+ int pipe; -+ -+ /* Sanity check */ -+ if (!serial || !ctrl_urb || !ctrl_req) { -+ printk(KERN_ERR "%s: Wrong arguments\n", __func__); -+ return -EINVAL; -+ } -+ -+ /* initialize */ -+ ctrl_req->wValue = 0; -+ ctrl_req->wIndex = cpu_to_le16(hso_port_to_mux(port)); -+ ctrl_req->wLength = cpu_to_le16(size); -+ -+ if (type == USB_CDC_GET_ENCAPSULATED_RESPONSE) { -+ /* Reading command */ -+ ctrl_req->bRequestType = USB_DIR_IN | -+ USB_TYPE_OPTION_VENDOR | -+ USB_RECIP_INTERFACE; -+ ctrl_req->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE; -+ pipe = usb_rcvctrlpipe(serial->parent->usb, 0); -+ } else { -+ /* Writing command */ -+ ctrl_req->bRequestType = USB_DIR_OUT | -+ USB_TYPE_OPTION_VENDOR | -+ USB_RECIP_INTERFACE; -+ ctrl_req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND; -+ pipe = usb_sndctrlpipe(serial->parent->usb, 0); -+ } -+ /* syslog */ -+ D2("%s command (%02x) len: %d, port: %d", -+ type == USB_CDC_GET_ENCAPSULATED_RESPONSE ? "Read" : "Write", -+ ctrl_req->bRequestType, ctrl_req->wLength, port); -+ -+ /* Load ctrl urb */ -+ ctrl_urb->transfer_flags = 0; -+ usb_fill_control_urb(ctrl_urb, -+ serial->parent->usb, -+ pipe, -+ (u8 *) ctrl_req, -+ ctrl_urb_data, size, ctrl_callback, serial); -+ /* Send it on merry way */ -+ result = usb_submit_urb(ctrl_urb, GFP_ATOMIC); -+ if (result) { -+ dev_err(&ctrl_urb->dev->dev, -+ "%s failed submit ctrl_urb %d type %d\n", __func__, -+ result, type); -+ return result; -+ } -+ -+ /* done */ -+ return size; -+} -+ -+/* called by intr_callback when read occurs */ -+static int hso_mux_serial_read(struct hso_serial *serial) -+{ -+ if (!serial) -+ return -EINVAL; -+ -+ /* clean data */ -+ memset(serial->rx_data[0], 0, CTRL_URB_RX_SIZE); -+ /* make the request */ -+ -+ if (serial->num_rx_urbs != 1) { -+ dev_err(&serial->parent->interface->dev, -+ "ERROR: mux'd reads with multiple buffers " -+ "not possible\n"); -+ return 0; -+ } -+ return mux_device_request(serial, -+ USB_CDC_GET_ENCAPSULATED_RESPONSE, -+ serial->parent->port_spec & HSO_PORT_MASK, -+ serial->rx_urb[0], -+ &serial->ctrl_req_rx, -+ serial->rx_data[0], serial->rx_data_length); -+} -+ -+/* used for muxed serial port callback (muxed serial read) */ -+static void intr_callback(struct urb *urb) -+{ -+ struct hso_shared_int *shared_int = urb->context; -+ struct hso_serial *serial; -+ unsigned char *port_req; -+ int status = urb->status; -+ int i; -+ -+ usb_mark_last_busy(urb->dev); -+ -+ /* sanity check */ -+ if (!shared_int) -+ return; -+ -+ /* status check */ -+ if (status) { -+ handle_usb_error(status, __func__, NULL); -+ return; -+ } -+ D4("\n--- Got intr callback 0x%02X ---", status); -+ -+ /* what request? */ -+ port_req = urb->transfer_buffer; -+ D4(" port_req = 0x%.2X\n", *port_req); -+ /* loop over all muxed ports to find the one sending this */ -+ for (i = 0; i < 8; i++) { -+ /* max 8 channels on MUX */ -+ if (*port_req & (1 << i)) { -+ serial = get_serial_by_shared_int_and_type(shared_int, -+ (1 << i)); -+ if (serial != NULL) { -+ D1("Pending read interrupt on port %d\n", i); -+ spin_lock(&serial->serial_lock); -+ if (serial->rx_state == RX_IDLE && -+ atomic_read(&serial->port.count) > 0) { -+ /* Setup and send a ctrl req read on -+ * port i */ -+ if (!serial->rx_urb_filled[0]) { -+ serial->rx_state = RX_SENT; -+ hso_mux_serial_read(serial); -+ } else -+ serial->rx_state = RX_PENDING; -+ } else { -+ D1("Already a read pending on " -+ "port %d or port not open\n", i); -+ } -+ spin_unlock(&serial->serial_lock); -+ } -+ } -+ } -+ /* Resubmit interrupt urb */ -+ hso_mux_submit_intr_urb(shared_int, urb->dev, GFP_ATOMIC); -+} -+ -+/* called for writing to muxed serial port */ -+static int hso_mux_serial_write_data(struct hso_serial *serial) -+{ -+ if (NULL == serial) -+ return -EINVAL; -+ -+ return mux_device_request(serial, -+ USB_CDC_SEND_ENCAPSULATED_COMMAND, -+ serial->parent->port_spec & HSO_PORT_MASK, -+ serial->tx_urb, -+ &serial->ctrl_req_tx, -+ serial->tx_data, serial->tx_data_count); -+} -+ -+/* write callback for Diag and CS port */ -+static void hso_std_serial_write_bulk_callback(struct urb *urb) -+{ -+ struct hso_serial *serial = urb->context; -+ int status = urb->status; -+ -+ /* sanity check */ -+ if (!serial) { -+ D1("serial == NULL"); -+ return; -+ } -+ -+ spin_lock(&serial->serial_lock); -+ serial->tx_urb_used = 0; -+ spin_unlock(&serial->serial_lock); -+ if (status) { -+ handle_usb_error(status, __func__, serial->parent); -+ return; -+ } -+ hso_put_activity(serial->parent); -+ tty_port_tty_wakeup(&serial->port); -+ hso_kick_transmit(serial); -+ -+ D1(" "); -+} -+ -+/* called for writing diag or CS serial port */ -+static int hso_std_serial_write_data(struct hso_serial *serial) -+{ -+ int count = serial->tx_data_count; -+ int result; -+ -+ usb_fill_bulk_urb(serial->tx_urb, -+ serial->parent->usb, -+ usb_sndbulkpipe(serial->parent->usb, -+ serial->out_endp-> -+ bEndpointAddress & 0x7F), -+ serial->tx_data, serial->tx_data_count, -+ hso_std_serial_write_bulk_callback, serial); -+ -+ result = usb_submit_urb(serial->tx_urb, GFP_ATOMIC); -+ if (result) { -+ dev_warn(&serial->parent->usb->dev, -+ "Failed to submit urb - res %d\n", result); -+ return result; -+ } -+ -+ return count; -+} -+ -+/* callback after read or write on muxed serial port */ -+static void ctrl_callback(struct urb *urb) -+{ -+ struct hso_serial *serial = urb->context; -+ struct usb_ctrlrequest *req; -+ int status = urb->status; -+ -+ /* sanity check */ -+ if (!serial) -+ return; -+ -+ spin_lock(&serial->serial_lock); -+ serial->tx_urb_used = 0; -+ spin_unlock(&serial->serial_lock); -+ if (status) { -+ handle_usb_error(status, __func__, serial->parent); -+ return; -+ } -+ -+ /* what request? */ -+ req = (struct usb_ctrlrequest *)(urb->setup_packet); -+ D4("\n--- Got muxed ctrl callback 0x%02X ---", status); -+ D4("Actual length of urb = %d\n", urb->actual_length); -+ DUMP1(urb->transfer_buffer, urb->actual_length); -+ -+ if (req->bRequestType == -+ (USB_DIR_IN | USB_TYPE_OPTION_VENDOR | USB_RECIP_INTERFACE)) { -+ /* response to a read command */ -+ serial->rx_urb_filled[0] = 1; -+ spin_lock(&serial->serial_lock); -+ put_rxbuf_data_and_resubmit_ctrl_urb(serial); -+ spin_unlock(&serial->serial_lock); -+ } else { -+ hso_put_activity(serial->parent); -+ tty_port_tty_wakeup(&serial->port); -+ /* response to a write command */ -+ hso_kick_transmit(serial); -+ } -+} -+ -+/* handle RX data for serial port */ -+static int put_rxbuf_data(struct urb *urb, struct hso_serial *serial) -+{ -+ struct tty_struct *tty; -+ int count; -+ -+ /* Sanity check */ -+ if (urb == NULL || serial == NULL) { -+ D1("serial = NULL"); -+ return -2; -+ } -+ -+ tty = tty_port_tty_get(&serial->port); -+ -+ if (tty && test_bit(TTY_THROTTLED, &tty->flags)) { -+ tty_kref_put(tty); -+ return -1; -+ } -+ -+ /* Push data to tty */ -+ D1("data to push to tty"); -+ count = tty_buffer_request_room(&serial->port, urb->actual_length); -+ if (count >= urb->actual_length) { -+ tty_insert_flip_string(&serial->port, urb->transfer_buffer, -+ urb->actual_length); -+ tty_flip_buffer_push(&serial->port); -+ } else { -+ dev_warn(&serial->parent->usb->dev, -+ "dropping data, %d bytes lost\n", urb->actual_length); -+ } -+ -+ tty_kref_put(tty); -+ -+ serial->rx_urb_filled[hso_urb_to_index(serial, urb)] = 0; -+ -+ return 0; -+} -+ -+ -+/* Base driver functions */ -+ -+static void hso_log_port(struct hso_device *hso_dev) -+{ -+ char *port_type; -+ char port_dev[20]; -+ -+ switch (hso_dev->port_spec & HSO_PORT_MASK) { -+ case HSO_PORT_CONTROL: -+ port_type = "Control"; -+ break; -+ case HSO_PORT_APP: -+ port_type = "Application"; -+ break; -+ case HSO_PORT_GPS: -+ port_type = "GPS"; -+ break; -+ case HSO_PORT_GPS_CONTROL: -+ port_type = "GPS control"; -+ break; -+ case HSO_PORT_APP2: -+ port_type = "Application2"; -+ break; -+ case HSO_PORT_PCSC: -+ port_type = "PCSC"; -+ break; -+ case HSO_PORT_DIAG: -+ port_type = "Diagnostic"; -+ break; -+ case HSO_PORT_DIAG2: -+ port_type = "Diagnostic2"; -+ break; -+ case HSO_PORT_MODEM: -+ port_type = "Modem"; -+ break; -+ case HSO_PORT_NETWORK: -+ port_type = "Network"; -+ break; -+ default: -+ port_type = "Unknown"; -+ break; -+ } -+ if ((hso_dev->port_spec & HSO_PORT_MASK) == HSO_PORT_NETWORK) { -+ sprintf(port_dev, "%s", dev2net(hso_dev)->net->name); -+ } else -+ sprintf(port_dev, "/dev/%s%d", tty_filename, -+ dev2ser(hso_dev)->minor); -+ -+ dev_dbg(&hso_dev->interface->dev, "HSO: Found %s port %s\n", -+ port_type, port_dev); -+} -+ -+static int hso_start_net_device(struct hso_device *hso_dev) -+{ -+ int i, result = 0; -+ struct hso_net *hso_net = dev2net(hso_dev); -+ -+ if (!hso_net) -+ return -ENODEV; -+ -+ /* send URBs for all read buffers */ -+ for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) { -+ -+ /* Prep a receive URB */ -+ usb_fill_bulk_urb(hso_net->mux_bulk_rx_urb_pool[i], -+ hso_dev->usb, -+ usb_rcvbulkpipe(hso_dev->usb, -+ hso_net->in_endp-> -+ bEndpointAddress & 0x7F), -+ hso_net->mux_bulk_rx_buf_pool[i], -+ MUX_BULK_RX_BUF_SIZE, read_bulk_callback, -+ hso_net); -+ -+ /* Put it out there so the device can send us stuff */ -+ result = usb_submit_urb(hso_net->mux_bulk_rx_urb_pool[i], -+ GFP_NOIO); -+ if (result) -+ dev_warn(&hso_dev->usb->dev, -+ "%s failed mux_bulk_rx_urb[%d] %d\n", __func__, -+ i, result); -+ } -+ -+ return result; -+} -+ -+static int hso_stop_net_device(struct hso_device *hso_dev) -+{ -+ int i; -+ struct hso_net *hso_net = dev2net(hso_dev); -+ -+ if (!hso_net) -+ return -ENODEV; -+ -+ for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) { -+ if (hso_net->mux_bulk_rx_urb_pool[i]) -+ usb_kill_urb(hso_net->mux_bulk_rx_urb_pool[i]); -+ -+ } -+ if (hso_net->mux_bulk_tx_urb) -+ usb_kill_urb(hso_net->mux_bulk_tx_urb); -+ -+ return 0; -+} -+ -+static int hso_start_serial_device(struct hso_device *hso_dev, gfp_t flags) -+{ -+ int i, result = 0; -+ struct hso_serial *serial = dev2ser(hso_dev); -+ -+ if (!serial) -+ return -ENODEV; -+ -+ /* If it is not the MUX port fill in and submit a bulk urb (already -+ * allocated in hso_serial_start) */ -+ if (!(serial->parent->port_spec & HSO_INTF_MUX)) { -+ for (i = 0; i < serial->num_rx_urbs; i++) { -+ usb_fill_bulk_urb(serial->rx_urb[i], -+ serial->parent->usb, -+ usb_rcvbulkpipe(serial->parent->usb, -+ serial->in_endp-> -+ bEndpointAddress & -+ 0x7F), -+ serial->rx_data[i], -+ serial->rx_data_length, -+ hso_std_serial_read_bulk_callback, -+ serial); -+ result = usb_submit_urb(serial->rx_urb[i], flags); -+ if (result) { -+ dev_warn(&serial->parent->usb->dev, -+ "Failed to submit urb - res %d\n", -+ result); -+ break; -+ } -+ } -+ } else { -+ mutex_lock(&serial->shared_int->shared_int_lock); -+ if (!serial->shared_int->use_count) { -+ result = -+ hso_mux_submit_intr_urb(serial->shared_int, -+ hso_dev->usb, flags); -+ } -+ serial->shared_int->use_count++; -+ mutex_unlock(&serial->shared_int->shared_int_lock); -+ } -+ if (serial->tiocmget) -+ tiocmget_submit_urb(serial, -+ serial->tiocmget, -+ serial->parent->usb); -+ return result; -+} -+ -+static int hso_stop_serial_device(struct hso_device *hso_dev) -+{ -+ int i; -+ struct hso_serial *serial = dev2ser(hso_dev); -+ struct hso_tiocmget *tiocmget; -+ -+ if (!serial) -+ return -ENODEV; -+ -+ for (i = 0; i < serial->num_rx_urbs; i++) { -+ if (serial->rx_urb[i]) { -+ usb_kill_urb(serial->rx_urb[i]); -+ serial->rx_urb_filled[i] = 0; -+ } -+ } -+ serial->curr_rx_urb_idx = 0; -+ -+ if (serial->tx_urb) -+ usb_kill_urb(serial->tx_urb); -+ -+ if (serial->shared_int) { -+ mutex_lock(&serial->shared_int->shared_int_lock); -+ if (serial->shared_int->use_count && -+ (--serial->shared_int->use_count == 0)) { -+ struct urb *urb; -+ -+ urb = serial->shared_int->shared_intr_urb; -+ if (urb) -+ usb_kill_urb(urb); -+ } -+ mutex_unlock(&serial->shared_int->shared_int_lock); -+ } -+ tiocmget = serial->tiocmget; -+ if (tiocmget) { -+ wake_up_interruptible(&tiocmget->waitq); -+ usb_kill_urb(tiocmget->urb); -+ } -+ -+ return 0; -+} -+ -+static void hso_serial_tty_unregister(struct hso_serial *serial) -+{ -+ tty_unregister_device(tty_drv, serial->minor); -+} -+ -+static void hso_serial_common_free(struct hso_serial *serial) -+{ -+ int i; -+ -+ for (i = 0; i < serial->num_rx_urbs; i++) { -+ /* unlink and free RX URB */ -+ usb_free_urb(serial->rx_urb[i]); -+ /* free the RX buffer */ -+ kfree(serial->rx_data[i]); -+ } -+ -+ /* unlink and free TX URB */ -+ usb_free_urb(serial->tx_urb); -+ kfree(serial->tx_buffer); -+ kfree(serial->tx_data); -+ tty_port_destroy(&serial->port); -+} -+ -+static int hso_serial_common_create(struct hso_serial *serial, int num_urbs, -+ int rx_size, int tx_size) -+{ -+ struct device *dev; -+ int minor; -+ int i; -+ -+ tty_port_init(&serial->port); -+ -+ minor = get_free_serial_index(); -+ if (minor < 0) -+ goto exit; -+ -+ /* register our minor number */ -+ serial->parent->dev = tty_port_register_device_attr(&serial->port, -+ tty_drv, minor, &serial->parent->interface->dev, -+ serial->parent, hso_serial_dev_groups); -+ dev = serial->parent->dev; -+ -+ /* fill in specific data for later use */ -+ serial->minor = minor; -+ serial->magic = HSO_SERIAL_MAGIC; -+ spin_lock_init(&serial->serial_lock); -+ serial->num_rx_urbs = num_urbs; -+ -+ /* RX, allocate urb and initialize */ -+ -+ /* prepare our RX buffer */ -+ serial->rx_data_length = rx_size; -+ for (i = 0; i < serial->num_rx_urbs; i++) { -+ serial->rx_urb[i] = usb_alloc_urb(0, GFP_KERNEL); -+ if (!serial->rx_urb[i]) { -+ dev_err(dev, "Could not allocate urb?\n"); -+ goto exit; -+ } -+ serial->rx_urb[i]->transfer_buffer = NULL; -+ serial->rx_urb[i]->transfer_buffer_length = 0; -+ serial->rx_data[i] = kzalloc(serial->rx_data_length, -+ GFP_KERNEL); -+ if (!serial->rx_data[i]) -+ goto exit; -+ } -+ -+ /* TX, allocate urb and initialize */ -+ serial->tx_urb = usb_alloc_urb(0, GFP_KERNEL); -+ if (!serial->tx_urb) { -+ dev_err(dev, "Could not allocate urb?\n"); -+ goto exit; -+ } -+ serial->tx_urb->transfer_buffer = NULL; -+ serial->tx_urb->transfer_buffer_length = 0; -+ /* prepare our TX buffer */ -+ serial->tx_data_count = 0; -+ serial->tx_buffer_count = 0; -+ serial->tx_data_length = tx_size; -+ serial->tx_data = kzalloc(serial->tx_data_length, GFP_KERNEL); -+ if (!serial->tx_data) -+ goto exit; -+ -+ serial->tx_buffer = kzalloc(serial->tx_data_length, GFP_KERNEL); -+ if (!serial->tx_buffer) -+ goto exit; -+ -+ return 0; -+exit: -+ hso_serial_tty_unregister(serial); -+ hso_serial_common_free(serial); -+ return -1; -+} -+ -+/* Creates a general hso device */ -+static struct hso_device *hso_create_device(struct usb_interface *intf, -+ int port_spec) -+{ -+ struct hso_device *hso_dev; -+ -+ hso_dev = kzalloc(sizeof(*hso_dev), GFP_ATOMIC); -+ if (!hso_dev) -+ return NULL; -+ -+ hso_dev->port_spec = port_spec; -+ hso_dev->usb = interface_to_usbdev(intf); -+ hso_dev->interface = intf; -+ kref_init(&hso_dev->ref); -+ mutex_init(&hso_dev->mutex); -+ -+ INIT_WORK(&hso_dev->async_get_intf, async_get_intf); -+ INIT_WORK(&hso_dev->async_put_intf, async_put_intf); -+ -+ return hso_dev; -+} -+ -+/* Removes a network device in the network device table */ -+static int remove_net_device(struct hso_device *hso_dev) -+{ -+ int i; -+ -+ for (i = 0; i < HSO_MAX_NET_DEVICES; i++) { -+ if (network_table[i] == hso_dev) { -+ network_table[i] = NULL; -+ break; -+ } -+ } -+ if (i == HSO_MAX_NET_DEVICES) -+ return -1; -+ return 0; -+} -+ -+/* Frees our network device */ -+static void hso_free_net_device(struct hso_device *hso_dev) -+{ -+ int i; -+ struct hso_net *hso_net = dev2net(hso_dev); -+ -+ if (!hso_net) -+ return; -+ -+ remove_net_device(hso_net->parent); -+ -+ if (hso_net->net) -+ unregister_netdev(hso_net->net); -+ -+ /* start freeing */ -+ for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) { -+ usb_free_urb(hso_net->mux_bulk_rx_urb_pool[i]); -+ kfree(hso_net->mux_bulk_rx_buf_pool[i]); -+ hso_net->mux_bulk_rx_buf_pool[i] = NULL; -+ } -+ usb_free_urb(hso_net->mux_bulk_tx_urb); -+ kfree(hso_net->mux_bulk_tx_buf); -+ hso_net->mux_bulk_tx_buf = NULL; -+ -+ if (hso_net->net) -+ free_netdev(hso_net->net); -+ -+ kfree(hso_dev); -+} -+ -+static const struct net_device_ops hso_netdev_ops = { -+ .ndo_open = hso_net_open, -+ .ndo_stop = hso_net_close, -+ .ndo_start_xmit = hso_net_start_xmit, -+ .ndo_tx_timeout = hso_net_tx_timeout, -+}; -+ -+/* initialize the network interface */ -+static void hso_net_init(struct net_device *net) -+{ -+ struct hso_net *hso_net = netdev_priv(net); -+ -+ D1("sizeof hso_net is %d", (int)sizeof(*hso_net)); -+ -+ /* fill in the other fields */ -+ net->netdev_ops = &hso_netdev_ops; -+ net->watchdog_timeo = HSO_NET_TX_TIMEOUT; -+ net->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; -+ net->type = ARPHRD_NONE; -+ net->mtu = DEFAULT_MTU - 14; -+ net->tx_queue_len = 10; -+ net->ethtool_ops = &ops; -+ -+ /* and initialize the semaphore */ -+ spin_lock_init(&hso_net->net_lock); -+} -+ -+/* Adds a network device in the network device table */ -+static int add_net_device(struct hso_device *hso_dev) -+{ -+ int i; -+ -+ for (i = 0; i < HSO_MAX_NET_DEVICES; i++) { -+ if (network_table[i] == NULL) { -+ network_table[i] = hso_dev; -+ break; -+ } -+ } -+ if (i == HSO_MAX_NET_DEVICES) -+ return -1; -+ return 0; -+} -+ -+static int hso_rfkill_set_block(void *data, bool blocked) -+{ -+ struct hso_device *hso_dev = data; -+ int enabled = !blocked; -+ int rv; -+ -+ mutex_lock(&hso_dev->mutex); -+ if (hso_dev->usb_gone) -+ rv = 0; -+ else -+ rv = usb_control_msg(hso_dev->usb, usb_rcvctrlpipe(hso_dev->usb, 0), -+ enabled ? 0x82 : 0x81, 0x40, 0, 0, NULL, 0, -+ USB_CTRL_SET_TIMEOUT); -+ mutex_unlock(&hso_dev->mutex); -+ return rv; -+} -+ -+static const struct rfkill_ops hso_rfkill_ops = { -+ .set_block = hso_rfkill_set_block, -+}; -+ -+/* Creates and sets up everything for rfkill */ -+static void hso_create_rfkill(struct hso_device *hso_dev, -+ struct usb_interface *interface) -+{ -+ struct hso_net *hso_net = dev2net(hso_dev); -+ struct device *dev = &hso_net->net->dev; -+ static u32 rfkill_counter; -+ -+ snprintf(hso_net->name, sizeof(hso_net->name), "hso-%d", -+ rfkill_counter++); -+ -+ hso_net->rfkill = rfkill_alloc(hso_net->name, -+ &interface_to_usbdev(interface)->dev, -+ RFKILL_TYPE_WWAN, -+ &hso_rfkill_ops, hso_dev); -+ if (!hso_net->rfkill) { -+ dev_err(dev, "%s - Out of memory\n", __func__); -+ return; -+ } -+ if (rfkill_register(hso_net->rfkill) < 0) { -+ rfkill_destroy(hso_net->rfkill); -+ hso_net->rfkill = NULL; -+ dev_err(dev, "%s - Failed to register rfkill\n", __func__); -+ return; -+ } -+} -+ -+static struct device_type hso_type = { -+ .name = "wwan", -+}; -+ -+/* Creates our network device */ -+static struct hso_device *hso_create_net_device(struct usb_interface *interface, -+ int port_spec) -+{ -+ int result, i; -+ struct net_device *net; -+ struct hso_net *hso_net; -+ struct hso_device *hso_dev; -+ -+ hso_dev = hso_create_device(interface, port_spec); -+ if (!hso_dev) -+ return NULL; -+ -+ /* allocate our network device, then we can put in our private data */ -+ /* call hso_net_init to do the basic initialization */ -+ net = alloc_netdev(sizeof(struct hso_net), "hso%d", NET_NAME_UNKNOWN, -+ hso_net_init); -+ if (!net) { -+ dev_err(&interface->dev, "Unable to create ethernet device\n"); -+ goto exit; -+ } -+ -+ hso_net = netdev_priv(net); -+ -+ hso_dev->port_data.dev_net = hso_net; -+ hso_net->net = net; -+ hso_net->parent = hso_dev; -+ -+ hso_net->in_endp = hso_get_ep(interface, USB_ENDPOINT_XFER_BULK, -+ USB_DIR_IN); -+ if (!hso_net->in_endp) { -+ dev_err(&interface->dev, "Can't find BULK IN endpoint\n"); -+ goto exit; -+ } -+ hso_net->out_endp = hso_get_ep(interface, USB_ENDPOINT_XFER_BULK, -+ USB_DIR_OUT); -+ if (!hso_net->out_endp) { -+ dev_err(&interface->dev, "Can't find BULK OUT endpoint\n"); -+ goto exit; -+ } -+ SET_NETDEV_DEV(net, &interface->dev); -+ SET_NETDEV_DEVTYPE(net, &hso_type); -+ -+ /* registering our net device */ -+ result = register_netdev(net); -+ if (result) { -+ dev_err(&interface->dev, "Failed to register device\n"); -+ goto exit; -+ } -+ -+ /* start allocating */ -+ for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) { -+ hso_net->mux_bulk_rx_urb_pool[i] = usb_alloc_urb(0, GFP_KERNEL); -+ if (!hso_net->mux_bulk_rx_urb_pool[i]) { -+ dev_err(&interface->dev, "Could not allocate rx urb\n"); -+ goto exit; -+ } -+ hso_net->mux_bulk_rx_buf_pool[i] = kzalloc(MUX_BULK_RX_BUF_SIZE, -+ GFP_KERNEL); -+ if (!hso_net->mux_bulk_rx_buf_pool[i]) -+ goto exit; -+ } -+ hso_net->mux_bulk_tx_urb = usb_alloc_urb(0, GFP_KERNEL); -+ if (!hso_net->mux_bulk_tx_urb) { -+ dev_err(&interface->dev, "Could not allocate tx urb\n"); -+ goto exit; -+ } -+ hso_net->mux_bulk_tx_buf = kzalloc(MUX_BULK_TX_BUF_SIZE, GFP_KERNEL); -+ if (!hso_net->mux_bulk_tx_buf) -+ goto exit; -+ -+ add_net_device(hso_dev); -+ -+ hso_log_port(hso_dev); -+ -+ hso_create_rfkill(hso_dev, interface); -+ -+ return hso_dev; -+exit: -+ hso_free_net_device(hso_dev); -+ return NULL; -+} -+ -+static void hso_free_tiomget(struct hso_serial *serial) -+{ -+ struct hso_tiocmget *tiocmget; -+ if (!serial) -+ return; -+ tiocmget = serial->tiocmget; -+ if (tiocmget) { -+ usb_free_urb(tiocmget->urb); -+ tiocmget->urb = NULL; -+ serial->tiocmget = NULL; -+ kfree(tiocmget); -+ } -+} -+ -+/* Frees an AT channel ( goes for both mux and non-mux ) */ -+static void hso_free_serial_device(struct hso_device *hso_dev) -+{ -+ struct hso_serial *serial = dev2ser(hso_dev); -+ -+ if (!serial) -+ return; -+ -+ hso_serial_common_free(serial); -+ -+ if (serial->shared_int) { -+ mutex_lock(&serial->shared_int->shared_int_lock); -+ if (--serial->shared_int->ref_count == 0) -+ hso_free_shared_int(serial->shared_int); -+ else -+ mutex_unlock(&serial->shared_int->shared_int_lock); -+ } -+ hso_free_tiomget(serial); -+ kfree(serial); -+ kfree(hso_dev); -+} -+ -+/* Creates a bulk AT channel */ -+static struct hso_device *hso_create_bulk_serial_device( -+ struct usb_interface *interface, int port) -+{ -+ struct hso_device *hso_dev; -+ struct hso_serial *serial; -+ int num_urbs; -+ struct hso_tiocmget *tiocmget; -+ -+ hso_dev = hso_create_device(interface, port); -+ if (!hso_dev) -+ return NULL; -+ -+ serial = kzalloc(sizeof(*serial), GFP_KERNEL); -+ if (!serial) -+ goto exit; -+ -+ serial->parent = hso_dev; -+ hso_dev->port_data.dev_serial = serial; -+ -+ if ((port & HSO_PORT_MASK) == HSO_PORT_MODEM) { -+ num_urbs = 2; -+ serial->tiocmget = kzalloc(sizeof(struct hso_tiocmget), -+ GFP_KERNEL); -+ /* it isn't going to break our heart if serial->tiocmget -+ * allocation fails don't bother checking this. -+ */ -+ if (serial->tiocmget) { -+ tiocmget = serial->tiocmget; -+ tiocmget->urb = usb_alloc_urb(0, GFP_KERNEL); -+ if (tiocmget->urb) { -+ mutex_init(&tiocmget->mutex); -+ init_waitqueue_head(&tiocmget->waitq); -+ tiocmget->endp = hso_get_ep( -+ interface, -+ USB_ENDPOINT_XFER_INT, -+ USB_DIR_IN); -+ } else -+ hso_free_tiomget(serial); -+ } -+ } -+ else -+ num_urbs = 1; -+ -+ if (hso_serial_common_create(serial, num_urbs, BULK_URB_RX_SIZE, -+ BULK_URB_TX_SIZE)) -+ goto exit; -+ -+ serial->in_endp = hso_get_ep(interface, USB_ENDPOINT_XFER_BULK, -+ USB_DIR_IN); -+ if (!serial->in_endp) { -+ dev_err(&interface->dev, "Failed to find BULK IN ep\n"); -+ goto exit2; -+ } -+ -+ if (! -+ (serial->out_endp = -+ hso_get_ep(interface, USB_ENDPOINT_XFER_BULK, USB_DIR_OUT))) { -+ dev_err(&interface->dev, "Failed to find BULK IN ep\n"); -+ goto exit2; -+ } -+ -+ serial->write_data = hso_std_serial_write_data; -+ -+ /* and record this serial */ -+ set_serial_by_index(serial->minor, serial); -+ -+ /* setup the proc dirs and files if needed */ -+ hso_log_port(hso_dev); -+ -+ /* done, return it */ -+ return hso_dev; -+ -+exit2: -+ hso_serial_tty_unregister(serial); -+ hso_serial_common_free(serial); -+exit: -+ hso_free_tiomget(serial); -+ kfree(serial); -+ kfree(hso_dev); -+ return NULL; -+} -+ -+/* Creates a multiplexed AT channel */ -+static -+struct hso_device *hso_create_mux_serial_device(struct usb_interface *interface, -+ int port, -+ struct hso_shared_int *mux) -+{ -+ struct hso_device *hso_dev; -+ struct hso_serial *serial; -+ int port_spec; -+ -+ port_spec = HSO_INTF_MUX; -+ port_spec &= ~HSO_PORT_MASK; -+ -+ port_spec |= hso_mux_to_port(port); -+ if ((port_spec & HSO_PORT_MASK) == HSO_PORT_NO_PORT) -+ return NULL; -+ -+ hso_dev = hso_create_device(interface, port_spec); -+ if (!hso_dev) -+ return NULL; -+ -+ serial = kzalloc(sizeof(*serial), GFP_KERNEL); -+ if (!serial) -+ goto exit; -+ -+ hso_dev->port_data.dev_serial = serial; -+ serial->parent = hso_dev; -+ -+ if (hso_serial_common_create -+ (serial, 1, CTRL_URB_RX_SIZE, CTRL_URB_TX_SIZE)) -+ goto exit; -+ -+ serial->tx_data_length--; -+ serial->write_data = hso_mux_serial_write_data; -+ -+ serial->shared_int = mux; -+ mutex_lock(&serial->shared_int->shared_int_lock); -+ serial->shared_int->ref_count++; -+ mutex_unlock(&serial->shared_int->shared_int_lock); -+ -+ /* and record this serial */ -+ set_serial_by_index(serial->minor, serial); -+ -+ /* setup the proc dirs and files if needed */ -+ hso_log_port(hso_dev); -+ -+ /* done, return it */ -+ return hso_dev; -+ -+exit: -+ if (serial) { -+ tty_unregister_device(tty_drv, serial->minor); -+ kfree(serial); -+ } -+ kfree(hso_dev); -+ return NULL; -+ -+} -+ -+static void hso_free_shared_int(struct hso_shared_int *mux) -+{ -+ usb_free_urb(mux->shared_intr_urb); -+ kfree(mux->shared_intr_buf); -+ mutex_unlock(&mux->shared_int_lock); -+ kfree(mux); -+} -+ -+static -+struct hso_shared_int *hso_create_shared_int(struct usb_interface *interface) -+{ -+ struct hso_shared_int *mux = kzalloc(sizeof(*mux), GFP_KERNEL); -+ -+ if (!mux) -+ return NULL; -+ -+ mux->intr_endp = hso_get_ep(interface, USB_ENDPOINT_XFER_INT, -+ USB_DIR_IN); -+ if (!mux->intr_endp) { -+ dev_err(&interface->dev, "Can't find INT IN endpoint\n"); -+ goto exit; -+ } -+ -+ mux->shared_intr_urb = usb_alloc_urb(0, GFP_KERNEL); -+ if (!mux->shared_intr_urb) { -+ dev_err(&interface->dev, "Could not allocate intr urb?\n"); -+ goto exit; -+ } -+ mux->shared_intr_buf = -+ kzalloc(le16_to_cpu(mux->intr_endp->wMaxPacketSize), -+ GFP_KERNEL); -+ if (!mux->shared_intr_buf) -+ goto exit; -+ -+ mutex_init(&mux->shared_int_lock); -+ -+ return mux; -+ -+exit: -+ kfree(mux->shared_intr_buf); -+ usb_free_urb(mux->shared_intr_urb); -+ kfree(mux); -+ return NULL; -+} -+ -+/* Gets the port spec for a certain interface */ -+static int hso_get_config_data(struct usb_interface *interface) -+{ -+ struct usb_device *usbdev = interface_to_usbdev(interface); -+ u8 *config_data = kmalloc(17, GFP_KERNEL); -+ u32 if_num = interface->cur_altsetting->desc.bInterfaceNumber; -+ s32 result; -+ -+ if (!config_data) -+ return -ENOMEM; -+ if (usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), -+ 0x86, 0xC0, 0, 0, config_data, 17, -+ USB_CTRL_SET_TIMEOUT) != 0x11) { -+ kfree(config_data); -+ return -EIO; -+ } -+ -+ switch (config_data[if_num]) { -+ case 0x0: -+ result = 0; -+ break; -+ case 0x1: -+ result = HSO_PORT_DIAG; -+ break; -+ case 0x2: -+ result = HSO_PORT_GPS; -+ break; -+ case 0x3: -+ result = HSO_PORT_GPS_CONTROL; -+ break; -+ case 0x4: -+ result = HSO_PORT_APP; -+ break; -+ case 0x5: -+ result = HSO_PORT_APP2; -+ break; -+ case 0x6: -+ result = HSO_PORT_CONTROL; -+ break; -+ case 0x7: -+ result = HSO_PORT_NETWORK; -+ break; -+ case 0x8: -+ result = HSO_PORT_MODEM; -+ break; -+ case 0x9: -+ result = HSO_PORT_MSD; -+ break; -+ case 0xa: -+ result = HSO_PORT_PCSC; -+ break; -+ case 0xb: -+ result = HSO_PORT_VOICE; -+ break; -+ default: -+ result = 0; -+ } -+ -+ if (result) -+ result |= HSO_INTF_BULK; -+ -+ if (config_data[16] & 0x1) -+ result |= HSO_INFO_CRC_BUG; -+ -+ kfree(config_data); -+ return result; -+} -+ -+/* called once for each interface upon device insertion */ -+static int hso_probe(struct usb_interface *interface, -+ const struct usb_device_id *id) -+{ -+ int mux, i, if_num, port_spec; -+ unsigned char port_mask; -+ struct hso_device *hso_dev = NULL; -+ struct hso_shared_int *shared_int; -+ struct hso_device *tmp_dev = NULL; -+ -+ if (interface->cur_altsetting->desc.bInterfaceClass != 0xFF) { -+ dev_err(&interface->dev, "Not our interface\n"); -+ return -ENODEV; -+ } -+ -+ if_num = interface->cur_altsetting->desc.bInterfaceNumber; -+ -+ /* Get the interface/port specification from either driver_info or from -+ * the device itself */ -+ if (id->driver_info) -+ port_spec = ((u32 *)(id->driver_info))[if_num]; -+ else -+ port_spec = hso_get_config_data(interface); -+ -+ /* Check if we need to switch to alt interfaces prior to port -+ * configuration */ -+ if (interface->num_altsetting > 1) -+ usb_set_interface(interface_to_usbdev(interface), if_num, 1); -+ interface->needs_remote_wakeup = 1; -+ -+ /* Allocate new hso device(s) */ -+ switch (port_spec & HSO_INTF_MASK) { -+ case HSO_INTF_MUX: -+ if ((port_spec & HSO_PORT_MASK) == HSO_PORT_NETWORK) { -+ /* Create the network device */ -+ if (!disable_net) { -+ hso_dev = hso_create_net_device(interface, -+ port_spec); -+ if (!hso_dev) -+ goto exit; -+ tmp_dev = hso_dev; -+ } -+ } -+ -+ if (hso_get_mux_ports(interface, &port_mask)) -+ /* TODO: de-allocate everything */ -+ goto exit; -+ -+ shared_int = hso_create_shared_int(interface); -+ if (!shared_int) -+ goto exit; -+ -+ for (i = 1, mux = 0; i < 0x100; i = i << 1, mux++) { -+ if (port_mask & i) { -+ hso_dev = hso_create_mux_serial_device( -+ interface, i, shared_int); -+ if (!hso_dev) -+ goto exit; -+ } -+ } -+ -+ if (tmp_dev) -+ hso_dev = tmp_dev; -+ break; -+ -+ case HSO_INTF_BULK: -+ /* It's a regular bulk interface */ -+ if ((port_spec & HSO_PORT_MASK) == HSO_PORT_NETWORK) { -+ if (!disable_net) -+ hso_dev = -+ hso_create_net_device(interface, port_spec); -+ } else { -+ hso_dev = -+ hso_create_bulk_serial_device(interface, port_spec); -+ } -+ if (!hso_dev) -+ goto exit; -+ break; -+ default: -+ goto exit; -+ } -+ -+ /* save our data pointer in this device */ -+ usb_set_intfdata(interface, hso_dev); -+ -+ /* done */ -+ return 0; -+exit: -+ hso_free_interface(interface); -+ return -ENODEV; -+} -+ -+/* device removed, cleaning up */ -+static void hso_disconnect(struct usb_interface *interface) -+{ -+ hso_free_interface(interface); -+ -+ /* remove reference of our private data */ -+ usb_set_intfdata(interface, NULL); -+} -+ -+static void async_get_intf(struct work_struct *data) -+{ -+ struct hso_device *hso_dev = -+ container_of(data, struct hso_device, async_get_intf); -+ usb_autopm_get_interface(hso_dev->interface); -+} -+ -+static void async_put_intf(struct work_struct *data) -+{ -+ struct hso_device *hso_dev = -+ container_of(data, struct hso_device, async_put_intf); -+ usb_autopm_put_interface(hso_dev->interface); -+} -+ -+static int hso_get_activity(struct hso_device *hso_dev) -+{ -+ if (hso_dev->usb->state == USB_STATE_SUSPENDED) { -+ if (!hso_dev->is_active) { -+ hso_dev->is_active = 1; -+ schedule_work(&hso_dev->async_get_intf); -+ } -+ } -+ -+ if (hso_dev->usb->state != USB_STATE_CONFIGURED) -+ return -EAGAIN; -+ -+ usb_mark_last_busy(hso_dev->usb); -+ -+ return 0; -+} -+ -+static int hso_put_activity(struct hso_device *hso_dev) -+{ -+ if (hso_dev->usb->state != USB_STATE_SUSPENDED) { -+ if (hso_dev->is_active) { -+ hso_dev->is_active = 0; -+ schedule_work(&hso_dev->async_put_intf); -+ return -EAGAIN; -+ } -+ } -+ hso_dev->is_active = 0; -+ return 0; -+} -+ -+/* called by kernel when we need to suspend device */ -+static int hso_suspend(struct usb_interface *iface, pm_message_t message) -+{ -+ int i, result; -+ -+ /* Stop all serial ports */ -+ for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) { -+ if (serial_table[i] && (serial_table[i]->interface == iface)) { -+ result = hso_stop_serial_device(serial_table[i]); -+ if (result) -+ goto out; -+ } -+ } -+ -+ /* Stop all network ports */ -+ for (i = 0; i < HSO_MAX_NET_DEVICES; i++) { -+ if (network_table[i] && -+ (network_table[i]->interface == iface)) { -+ result = hso_stop_net_device(network_table[i]); -+ if (result) -+ goto out; -+ } -+ } -+ -+out: -+ return 0; -+} -+ -+/* called by kernel when we need to resume device */ -+static int hso_resume(struct usb_interface *iface) -+{ -+ int i, result = 0; -+ struct hso_net *hso_net; -+ -+ /* Start all serial ports */ -+ for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) { -+ if (serial_table[i] && (serial_table[i]->interface == iface)) { -+ if (atomic_read(&dev2ser(serial_table[i])->port.count)) { -+ result = -+ hso_start_serial_device(serial_table[i], GFP_NOIO); -+ hso_kick_transmit(dev2ser(serial_table[i])); -+ if (result) -+ goto out; -+ } -+ } -+ } -+ -+ /* Start all network ports */ -+ for (i = 0; i < HSO_MAX_NET_DEVICES; i++) { -+ if (network_table[i] && -+ (network_table[i]->interface == iface)) { -+ hso_net = dev2net(network_table[i]); -+ if (hso_net->flags & IFF_UP) { -+ /* First transmit any lingering data, -+ then restart the device. */ -+ if (hso_net->skb_tx_buf) { -+ dev_dbg(&iface->dev, -+ "Transmitting" -+ " lingering data\n"); -+ hso_net_start_xmit(hso_net->skb_tx_buf, -+ hso_net->net); -+ hso_net->skb_tx_buf = NULL; -+ } -+ result = hso_start_net_device(network_table[i]); -+ if (result) -+ goto out; -+ } -+ } -+ } -+ -+out: -+ return result; -+} -+ -+static void hso_serial_ref_free(struct kref *ref) -+{ -+ struct hso_device *hso_dev = container_of(ref, struct hso_device, ref); -+ -+ hso_free_serial_device(hso_dev); -+} -+ -+static void hso_free_interface(struct usb_interface *interface) -+{ -+ struct hso_serial *serial; -+ int i; -+ -+ for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) { -+ if (serial_table[i] && -+ (serial_table[i]->interface == interface)) { -+ serial = dev2ser(serial_table[i]); -+ tty_port_tty_hangup(&serial->port, false); -+ mutex_lock(&serial->parent->mutex); -+ serial->parent->usb_gone = 1; -+ mutex_unlock(&serial->parent->mutex); -+ cancel_work_sync(&serial_table[i]->async_put_intf); -+ cancel_work_sync(&serial_table[i]->async_get_intf); -+ hso_serial_tty_unregister(serial); -+ kref_put(&serial_table[i]->ref, hso_serial_ref_free); -+ set_serial_by_index(i, NULL); -+ } -+ } -+ -+ for (i = 0; i < HSO_MAX_NET_DEVICES; i++) { -+ if (network_table[i] && -+ (network_table[i]->interface == interface)) { -+ struct rfkill *rfk = dev2net(network_table[i])->rfkill; -+ /* hso_stop_net_device doesn't stop the net queue since -+ * traffic needs to start it again when suspended */ -+ netif_stop_queue(dev2net(network_table[i])->net); -+ hso_stop_net_device(network_table[i]); -+ cancel_work_sync(&network_table[i]->async_put_intf); -+ cancel_work_sync(&network_table[i]->async_get_intf); -+ if (rfk) { -+ rfkill_unregister(rfk); -+ rfkill_destroy(rfk); -+ } -+ hso_free_net_device(network_table[i]); -+ } -+ } -+} -+ -+/* Helper functions */ -+ -+/* Get the endpoint ! */ -+static struct usb_endpoint_descriptor *hso_get_ep(struct usb_interface *intf, -+ int type, int dir) -+{ -+ int i; -+ struct usb_host_interface *iface = intf->cur_altsetting; -+ struct usb_endpoint_descriptor *endp; -+ -+ for (i = 0; i < iface->desc.bNumEndpoints; i++) { -+ endp = &iface->endpoint[i].desc; -+ if (((endp->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == dir) && -+ (usb_endpoint_type(endp) == type)) -+ return endp; -+ } -+ -+ return NULL; -+} -+ -+/* Get the byte that describes which ports are enabled */ -+static int hso_get_mux_ports(struct usb_interface *intf, unsigned char *ports) -+{ -+ int i; -+ struct usb_host_interface *iface = intf->cur_altsetting; -+ -+ if (iface->extralen == 3) { -+ *ports = iface->extra[2]; -+ return 0; -+ } -+ -+ for (i = 0; i < iface->desc.bNumEndpoints; i++) { -+ if (iface->endpoint[i].extralen == 3) { -+ *ports = iface->endpoint[i].extra[2]; -+ return 0; -+ } -+ } -+ -+ return -1; -+} -+ -+/* interrupt urb needs to be submitted, used for serial read of muxed port */ -+static int hso_mux_submit_intr_urb(struct hso_shared_int *shared_int, -+ struct usb_device *usb, gfp_t gfp) -+{ -+ int result; -+ -+ usb_fill_int_urb(shared_int->shared_intr_urb, usb, -+ usb_rcvintpipe(usb, -+ shared_int->intr_endp->bEndpointAddress & 0x7F), -+ shared_int->shared_intr_buf, -+ 1, -+ intr_callback, shared_int, -+ shared_int->intr_endp->bInterval); -+ -+ result = usb_submit_urb(shared_int->shared_intr_urb, gfp); -+ if (result) -+ dev_warn(&usb->dev, "%s failed mux_intr_urb %d\n", __func__, -+ result); -+ -+ return result; -+} -+ -+/* operations setup of the serial interface */ -+static const struct tty_operations hso_serial_ops = { -+ .open = hso_serial_open, -+ .close = hso_serial_close, -+ .write = hso_serial_write, -+ .write_room = hso_serial_write_room, -+ .cleanup = hso_serial_cleanup, -+ .ioctl = hso_serial_ioctl, -+ .set_termios = hso_serial_set_termios, -+ .chars_in_buffer = hso_serial_chars_in_buffer, -+ .tiocmget = hso_serial_tiocmget, -+ .tiocmset = hso_serial_tiocmset, -+ .get_icount = hso_get_count, -+ .unthrottle = hso_unthrottle -+}; -+ -+static struct usb_driver hso_driver = { -+ .name = driver_name, -+ .probe = hso_probe, -+ .disconnect = hso_disconnect, -+ .id_table = hso_ids, -+ .suspend = hso_suspend, -+ .resume = hso_resume, -+ .reset_resume = hso_resume, -+ .supports_autosuspend = 1, -+ .disable_hub_initiated_lpm = 1, -+}; -+ -+static int __init hso_init(void) -+{ -+ int i; -+ int result; -+ -+ /* put it in the log */ -+ printk(KERN_INFO "hso: %s\n", version); -+ -+ /* Initialise the serial table semaphore and table */ -+ spin_lock_init(&serial_table_lock); -+ for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) -+ serial_table[i] = NULL; -+ -+ /* allocate our driver using the proper amount of supported minors */ -+ tty_drv = alloc_tty_driver(HSO_SERIAL_TTY_MINORS); -+ if (!tty_drv) -+ return -ENOMEM; -+ -+ /* fill in all needed values */ -+ tty_drv->driver_name = driver_name; -+ tty_drv->name = tty_filename; -+ -+ /* if major number is provided as parameter, use that one */ -+ if (tty_major) -+ tty_drv->major = tty_major; -+ -+ tty_drv->minor_start = 0; -+ tty_drv->type = TTY_DRIVER_TYPE_SERIAL; -+ tty_drv->subtype = SERIAL_TYPE_NORMAL; -+ tty_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; -+ tty_drv->init_termios = tty_std_termios; -+ hso_init_termios(&tty_drv->init_termios); -+ tty_set_operations(tty_drv, &hso_serial_ops); -+ -+ /* register the tty driver */ -+ result = tty_register_driver(tty_drv); -+ if (result) { -+ printk(KERN_ERR "%s - tty_register_driver failed(%d)\n", -+ __func__, result); -+ goto err_free_tty; -+ } -+ -+ /* register this module as an usb driver */ -+ result = usb_register(&hso_driver); -+ if (result) { -+ printk(KERN_ERR "Could not register hso driver? error: %d\n", -+ result); -+ goto err_unreg_tty; -+ } -+ -+ /* done */ -+ return 0; -+err_unreg_tty: -+ tty_unregister_driver(tty_drv); -+err_free_tty: -+ put_tty_driver(tty_drv); -+ return result; -+} -+ -+static void __exit hso_exit(void) -+{ -+ printk(KERN_INFO "hso: unloaded\n"); -+ -+ tty_unregister_driver(tty_drv); -+ put_tty_driver(tty_drv); -+ /* deregister the usb driver */ -+ usb_deregister(&hso_driver); -+} -+ -+/* Module definitions */ -+module_init(hso_init); -+module_exit(hso_exit); -+ -+MODULE_AUTHOR(MOD_AUTHOR); -+MODULE_DESCRIPTION(MOD_DESCRIPTION); -+MODULE_LICENSE(MOD_LICENSE); -+ -+/* change the debug level (eg: insmod hso.ko debug=0x04) */ -+MODULE_PARM_DESC(debug, "Level of debug [0x01 | 0x02 | 0x04 | 0x08 | 0x10]"); -+module_param(debug, int, S_IRUGO | S_IWUSR); -+ -+/* set the major tty number (eg: insmod hso.ko tty_major=245) */ -+MODULE_PARM_DESC(tty_major, "Set the major tty number"); -+module_param(tty_major, int, S_IRUGO | S_IWUSR); -+ -+/* disable network interface (eg: insmod hso.ko disable_net=1) */ -+MODULE_PARM_DESC(disable_net, "Disable the network interface"); -+module_param(disable_net, int, S_IRUGO | S_IWUSR); -diff -Naur backports-4.2.6-1.org/drivers/net/usb/huawei_cdc_ncm.c backports-4.2.6-1/drivers/net/usb/huawei_cdc_ncm.c ---- backports-4.2.6-1.org/drivers/net/usb/huawei_cdc_ncm.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/huawei_cdc_ncm.c 2016-06-28 14:35:17.981973887 +0200 -@@ -0,0 +1,224 @@ -+/* huawei_cdc_ncm.c - handles Huawei devices using the CDC NCM protocol as -+ * transport layer. -+ * Copyright (C) 2013 Enrico Mioso -+ * -+ * -+ * ABSTRACT: -+ * This driver handles devices resembling the CDC NCM standard, but -+ * encapsulating another protocol inside it. An example are some Huawei 3G -+ * devices, exposing an embedded AT channel where you can set up the NCM -+ * connection. -+ * This code has been heavily inspired by the cdc_mbim.c driver, which is -+ * Copyright (c) 2012 Smith Micro Software, Inc. -+ * Copyright (c) 2012 Bjørn Mork -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * version 2 as published by the Free Software Foundation. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* Driver data */ -+struct huawei_cdc_ncm_state { -+ struct cdc_ncm_ctx *ctx; -+ atomic_t pmcount; -+ struct usb_driver *subdriver; -+ struct usb_interface *control; -+ struct usb_interface *data; -+}; -+ -+static int huawei_cdc_ncm_manage_power(struct usbnet *usbnet_dev, int on) -+{ -+ struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data; -+ int rv; -+ -+ if ((on && atomic_add_return(1, &drvstate->pmcount) == 1) || -+ (!on && atomic_dec_and_test(&drvstate->pmcount))) { -+ rv = usb_autopm_get_interface(usbnet_dev->intf); -+ usbnet_dev->intf->needs_remote_wakeup = on; -+ if (!rv) -+ usb_autopm_put_interface(usbnet_dev->intf); -+ } -+ return 0; -+} -+ -+static int huawei_cdc_ncm_wdm_manage_power(struct usb_interface *intf, -+ int status) -+{ -+ struct usbnet *usbnet_dev = usb_get_intfdata(intf); -+ -+ /* can be called while disconnecting */ -+ if (!usbnet_dev) -+ return 0; -+ -+ return huawei_cdc_ncm_manage_power(usbnet_dev, status); -+} -+ -+ -+static int huawei_cdc_ncm_bind(struct usbnet *usbnet_dev, -+ struct usb_interface *intf) -+{ -+ struct cdc_ncm_ctx *ctx; -+ struct usb_driver *subdriver = ERR_PTR(-ENODEV); -+ int ret = -ENODEV; -+ struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data; -+ int drvflags = 0; -+ -+ /* altsetting should always be 1 for NCM devices - so we hard-coded -+ * it here. Some huawei devices will need the NDP part of the NCM package to -+ * be at the end of the frame. -+ */ -+ drvflags |= CDC_NCM_FLAG_NDP_TO_END; -+ ret = cdc_ncm_bind_common(usbnet_dev, intf, 1, drvflags); -+ if (ret) -+ goto err; -+ -+ ctx = drvstate->ctx; -+ -+ if (usbnet_dev->status) -+ /* The wMaxCommand buffer must be big enough to hold -+ * any message from the modem. Experience has shown -+ * that some replies are more than 256 bytes long -+ */ -+ subdriver = usb_cdc_wdm_register(ctx->control, -+ &usbnet_dev->status->desc, -+ 1024, /* wMaxCommand */ -+ huawei_cdc_ncm_wdm_manage_power); -+ if (IS_ERR(subdriver)) { -+ ret = PTR_ERR(subdriver); -+ cdc_ncm_unbind(usbnet_dev, intf); -+ goto err; -+ } -+ -+ /* Prevent usbnet from using the status descriptor */ -+ usbnet_dev->status = NULL; -+ -+ drvstate->subdriver = subdriver; -+ -+err: -+ return ret; -+} -+ -+static void huawei_cdc_ncm_unbind(struct usbnet *usbnet_dev, -+ struct usb_interface *intf) -+{ -+ struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data; -+ struct cdc_ncm_ctx *ctx = drvstate->ctx; -+ -+ if (drvstate->subdriver && drvstate->subdriver->disconnect) -+ drvstate->subdriver->disconnect(ctx->control); -+ drvstate->subdriver = NULL; -+ -+ cdc_ncm_unbind(usbnet_dev, intf); -+} -+ -+static int huawei_cdc_ncm_suspend(struct usb_interface *intf, -+ pm_message_t message) -+{ -+ int ret = 0; -+ struct usbnet *usbnet_dev = usb_get_intfdata(intf); -+ struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data; -+ struct cdc_ncm_ctx *ctx = drvstate->ctx; -+ -+ if (ctx == NULL) { -+ ret = -ENODEV; -+ goto error; -+ } -+ -+ ret = usbnet_suspend(intf, message); -+ if (ret < 0) -+ goto error; -+ -+ if (intf == ctx->control && -+ drvstate->subdriver && -+ drvstate->subdriver->suspend) -+ ret = drvstate->subdriver->suspend(intf, message); -+ if (ret < 0) -+ usbnet_resume(intf); -+ -+error: -+ return ret; -+} -+ -+static int huawei_cdc_ncm_resume(struct usb_interface *intf) -+{ -+ int ret = 0; -+ struct usbnet *usbnet_dev = usb_get_intfdata(intf); -+ struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data; -+ bool callsub; -+ struct cdc_ncm_ctx *ctx = drvstate->ctx; -+ -+ /* should we call subdriver's resume function? */ -+ callsub = -+ (intf == ctx->control && -+ drvstate->subdriver && -+ drvstate->subdriver->resume); -+ -+ if (callsub) -+ ret = drvstate->subdriver->resume(intf); -+ if (ret < 0) -+ goto err; -+ ret = usbnet_resume(intf); -+ if (ret < 0 && callsub) -+ drvstate->subdriver->suspend(intf, PMSG_SUSPEND); -+err: -+ return ret; -+} -+ -+static const struct driver_info huawei_cdc_ncm_info = { -+ .description = "Huawei CDC NCM device", -+ .flags = FLAG_NO_SETINT | FLAG_MULTI_PACKET | FLAG_WWAN, -+ .bind = huawei_cdc_ncm_bind, -+ .unbind = huawei_cdc_ncm_unbind, -+ .manage_power = huawei_cdc_ncm_manage_power, -+ .rx_fixup = cdc_ncm_rx_fixup, -+ .tx_fixup = cdc_ncm_tx_fixup, -+}; -+ -+static const struct usb_device_id huawei_cdc_ncm_devs[] = { -+ /* Huawei NCM devices disguised as vendor specific */ -+ { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x16), -+ .driver_info = (unsigned long)&huawei_cdc_ncm_info, -+ }, -+ { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x46), -+ .driver_info = (unsigned long)&huawei_cdc_ncm_info, -+ }, -+ { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x76), -+ .driver_info = (unsigned long)&huawei_cdc_ncm_info, -+ }, -+ { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x03, 0x16), -+ .driver_info = (unsigned long)&huawei_cdc_ncm_info, -+ }, -+ -+ /* Terminating entry */ -+ { -+ }, -+}; -+MODULE_DEVICE_TABLE(usb, huawei_cdc_ncm_devs); -+ -+static struct usb_driver huawei_cdc_ncm_driver = { -+ .name = "huawei_cdc_ncm", -+ .id_table = huawei_cdc_ncm_devs, -+ .probe = usbnet_probe, -+ .disconnect = usbnet_disconnect, -+ .suspend = huawei_cdc_ncm_suspend, -+ .resume = huawei_cdc_ncm_resume, -+ .reset_resume = huawei_cdc_ncm_resume, -+ .supports_autosuspend = 1, -+ .disable_hub_initiated_lpm = 1, -+}; -+module_usb_driver(huawei_cdc_ncm_driver); -+MODULE_AUTHOR("Enrico Mioso "); -+MODULE_DESCRIPTION("USB CDC NCM host driver with encapsulated protocol support"); -+MODULE_LICENSE("GPL"); -diff -Naur backports-4.2.6-1.org/drivers/net/usb/int51x1.c backports-4.2.6-1/drivers/net/usb/int51x1.c ---- backports-4.2.6-1.org/drivers/net/usb/int51x1.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/int51x1.c 2016-06-28 14:35:17.985307220 +0200 -@@ -0,0 +1,199 @@ -+/* -+ * Copyright (c) 2009 Peter Holik -+ * -+ * Intellon usb PLC (Powerline Communications) usb net driver -+ * -+ * http://www.tandel.be/downloads/INT51X1_Datasheet.pdf -+ * -+ * Based on the work of Jan 'RedBully' Seiffert -+ */ -+ -+/* -+ * 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, see . -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define INT51X1_VENDOR_ID 0x09e1 -+#define INT51X1_PRODUCT_ID 0x5121 -+ -+#define INT51X1_HEADER_SIZE 2 /* 2 byte header */ -+ -+#define PACKET_TYPE_PROMISCUOUS (1 << 0) -+#define PACKET_TYPE_ALL_MULTICAST (1 << 1) /* no filter */ -+#define PACKET_TYPE_DIRECTED (1 << 2) -+#define PACKET_TYPE_BROADCAST (1 << 3) -+#define PACKET_TYPE_MULTICAST (1 << 4) /* filtered */ -+ -+#define SET_ETHERNET_PACKET_FILTER 0x43 -+ -+static int int51x1_rx_fixup(struct usbnet *dev, struct sk_buff *skb) -+{ -+ int len; -+ -+ if (!(pskb_may_pull(skb, INT51X1_HEADER_SIZE))) { -+ netdev_err(dev->net, "unexpected tiny rx frame\n"); -+ return 0; -+ } -+ -+ len = le16_to_cpu(*(__le16 *)&skb->data[skb->len - 2]); -+ -+ skb_trim(skb, len); -+ -+ return 1; -+} -+ -+static struct sk_buff *int51x1_tx_fixup(struct usbnet *dev, -+ struct sk_buff *skb, gfp_t flags) -+{ -+ int pack_len = skb->len; -+ int pack_with_header_len = pack_len + INT51X1_HEADER_SIZE; -+ int headroom = skb_headroom(skb); -+ int tailroom = skb_tailroom(skb); -+ int need_tail = 0; -+ __le16 *len; -+ -+ /* if packet and our header is smaler than 64 pad to 64 (+ ZLP) */ -+ if ((pack_with_header_len) < dev->maxpacket) -+ need_tail = dev->maxpacket - pack_with_header_len + 1; -+ /* -+ * usbnet would send a ZLP if packetlength mod urbsize == 0 for us, -+ * but we need to know ourself, because this would add to the length -+ * we send down to the device... -+ */ -+ else if (!(pack_with_header_len % dev->maxpacket)) -+ need_tail = 1; -+ -+ if (!skb_cloned(skb) && -+ (headroom + tailroom >= need_tail + INT51X1_HEADER_SIZE)) { -+ if (headroom < INT51X1_HEADER_SIZE || tailroom < need_tail) { -+ skb->data = memmove(skb->head + INT51X1_HEADER_SIZE, -+ skb->data, skb->len); -+ skb_set_tail_pointer(skb, skb->len); -+ } -+ } else { -+ struct sk_buff *skb2; -+ -+ skb2 = skb_copy_expand(skb, -+ INT51X1_HEADER_SIZE, -+ need_tail, -+ flags); -+ dev_kfree_skb_any(skb); -+ if (!skb2) -+ return NULL; -+ skb = skb2; -+ } -+ -+ pack_len += need_tail; -+ pack_len &= 0x07ff; -+ -+ len = (__le16 *) __skb_push(skb, INT51X1_HEADER_SIZE); -+ *len = cpu_to_le16(pack_len); -+ -+ if(need_tail) -+ memset(__skb_put(skb, need_tail), 0, need_tail); -+ -+ return skb; -+} -+ -+static void int51x1_set_multicast(struct net_device *netdev) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ u16 filter = PACKET_TYPE_DIRECTED | PACKET_TYPE_BROADCAST; -+ -+ if (netdev->flags & IFF_PROMISC) { -+ /* do not expect to see traffic of other PLCs */ -+ filter |= PACKET_TYPE_PROMISCUOUS; -+ netdev_info(dev->net, "promiscuous mode enabled\n"); -+ } else if (!netdev_mc_empty(netdev) || -+ (netdev->flags & IFF_ALLMULTI)) { -+ filter |= PACKET_TYPE_ALL_MULTICAST; -+ netdev_dbg(dev->net, "receive all multicast enabled\n"); -+ } else { -+ /* ~PROMISCUOUS, ~MULTICAST */ -+ netdev_dbg(dev->net, "receive own packets only\n"); -+ } -+ -+ usbnet_write_cmd_async(dev, SET_ETHERNET_PACKET_FILTER, -+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, -+ filter, 0, NULL, 0); -+} -+ -+static const struct net_device_ops int51x1_netdev_ops = { -+ .ndo_open = usbnet_open, -+ .ndo_stop = usbnet_stop, -+ .ndo_start_xmit = usbnet_start_xmit, -+ .ndo_tx_timeout = usbnet_tx_timeout, -+ .ndo_change_mtu = usbnet_change_mtu, -+ .ndo_set_mac_address = eth_mac_addr, -+ .ndo_validate_addr = eth_validate_addr, -+ .ndo_set_rx_mode = int51x1_set_multicast, -+}; -+ -+static int int51x1_bind(struct usbnet *dev, struct usb_interface *intf) -+{ -+ int status = usbnet_get_ethernet_addr(dev, 3); -+ -+ if (status) -+ return status; -+ -+ dev->net->hard_header_len += INT51X1_HEADER_SIZE; -+ dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; -+ dev->net->netdev_ops = &int51x1_netdev_ops; -+ -+ return usbnet_get_endpoints(dev, intf); -+} -+ -+static const struct driver_info int51x1_info = { -+ .description = "Intellon usb powerline adapter", -+ .bind = int51x1_bind, -+ .rx_fixup = int51x1_rx_fixup, -+ .tx_fixup = int51x1_tx_fixup, -+ .in = 1, -+ .out = 2, -+ .flags = FLAG_ETHER, -+}; -+ -+static const struct usb_device_id products[] = { -+ { -+ USB_DEVICE(INT51X1_VENDOR_ID, INT51X1_PRODUCT_ID), -+ .driver_info = (unsigned long) &int51x1_info, -+ }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(usb, products); -+ -+static struct usb_driver int51x1_driver = { -+ .name = "int51x1", -+ .id_table = products, -+ .probe = usbnet_probe, -+ .disconnect = usbnet_disconnect, -+ .suspend = usbnet_suspend, -+ .resume = usbnet_resume, -+ .disable_hub_initiated_lpm = 1, -+}; -+ -+module_usb_driver(int51x1_driver); -+ -+MODULE_AUTHOR("Peter Holik"); -+MODULE_DESCRIPTION("Intellon usb powerline adapter"); -+MODULE_LICENSE("GPL"); -diff -Naur backports-4.2.6-1.org/drivers/net/usb/ipheth.c backports-4.2.6-1/drivers/net/usb/ipheth.c ---- backports-4.2.6-1.org/drivers/net/usb/ipheth.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/ipheth.c 2016-06-28 14:35:17.985307220 +0200 -@@ -0,0 +1,588 @@ -+/* -+ * ipheth.c - Apple iPhone USB Ethernet driver -+ * -+ * Copyright (c) 2009 Diego Giagio -+ * All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * 2. 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. -+ * 3. Neither the name of GIAGIO.COM nor the names of its contributors -+ * may be used to endorse or promote products derived from this software -+ * without specific prior written permission. -+ * -+ * Alternatively, provided that this notice is retained in full, this -+ * software may be distributed under the terms of the GNU General -+ * Public License ("GPL") version 2, in which case the provisions of the -+ * GPL apply INSTEAD OF those given above. -+ * -+ * The provided data structures and external interfaces from this code -+ * are not restricted to be used by modules with a GPL compatible license. -+ * -+ * 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. -+ * -+ * -+ * Attention: iPhone device must be paired, otherwise it won't respond to our -+ * driver. For more info: http://giagio.com/wiki/moin.cgi/iPhoneEthernetDriver -+ * -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define USB_VENDOR_APPLE 0x05ac -+#define USB_PRODUCT_IPHONE 0x1290 -+#define USB_PRODUCT_IPHONE_3G 0x1292 -+#define USB_PRODUCT_IPHONE_3GS 0x1294 -+#define USB_PRODUCT_IPHONE_4 0x1297 -+#define USB_PRODUCT_IPAD 0x129a -+#define USB_PRODUCT_IPAD_2 0x12a2 -+#define USB_PRODUCT_IPAD_3 0x12a6 -+#define USB_PRODUCT_IPAD_MINI 0x12ab -+#define USB_PRODUCT_IPHONE_4_VZW 0x129c -+#define USB_PRODUCT_IPHONE_4S 0x12a0 -+#define USB_PRODUCT_IPHONE_5 0x12a8 -+ -+#define IPHETH_USBINTF_CLASS 255 -+#define IPHETH_USBINTF_SUBCLASS 253 -+#define IPHETH_USBINTF_PROTO 1 -+ -+#define IPHETH_BUF_SIZE 1516 -+#define IPHETH_IP_ALIGN 2 /* padding at front of URB */ -+#define IPHETH_TX_TIMEOUT (5 * HZ) -+ -+#define IPHETH_INTFNUM 2 -+#define IPHETH_ALT_INTFNUM 1 -+ -+#define IPHETH_CTRL_ENDP 0x00 -+#define IPHETH_CTRL_BUF_SIZE 0x40 -+#define IPHETH_CTRL_TIMEOUT (5 * HZ) -+ -+#define IPHETH_CMD_GET_MACADDR 0x00 -+#define IPHETH_CMD_CARRIER_CHECK 0x45 -+ -+#define IPHETH_CARRIER_CHECK_TIMEOUT round_jiffies_relative(1 * HZ) -+#define IPHETH_CARRIER_ON 0x04 -+ -+static struct usb_device_id ipheth_table[] = { -+ { USB_DEVICE_AND_INTERFACE_INFO( -+ USB_VENDOR_APPLE, USB_PRODUCT_IPHONE, -+ IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, -+ IPHETH_USBINTF_PROTO) }, -+ { USB_DEVICE_AND_INTERFACE_INFO( -+ USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_3G, -+ IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, -+ IPHETH_USBINTF_PROTO) }, -+ { USB_DEVICE_AND_INTERFACE_INFO( -+ USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_3GS, -+ IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, -+ IPHETH_USBINTF_PROTO) }, -+ { USB_DEVICE_AND_INTERFACE_INFO( -+ USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_4, -+ IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, -+ IPHETH_USBINTF_PROTO) }, -+ { USB_DEVICE_AND_INTERFACE_INFO( -+ USB_VENDOR_APPLE, USB_PRODUCT_IPAD, -+ IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, -+ IPHETH_USBINTF_PROTO) }, -+ { USB_DEVICE_AND_INTERFACE_INFO( -+ USB_VENDOR_APPLE, USB_PRODUCT_IPAD_2, -+ IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, -+ IPHETH_USBINTF_PROTO) }, -+ { USB_DEVICE_AND_INTERFACE_INFO( -+ USB_VENDOR_APPLE, USB_PRODUCT_IPAD_3, -+ IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, -+ IPHETH_USBINTF_PROTO) }, -+ { USB_DEVICE_AND_INTERFACE_INFO( -+ USB_VENDOR_APPLE, USB_PRODUCT_IPAD_MINI, -+ IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, -+ IPHETH_USBINTF_PROTO) }, -+ { USB_DEVICE_AND_INTERFACE_INFO( -+ USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_4_VZW, -+ IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, -+ IPHETH_USBINTF_PROTO) }, -+ { USB_DEVICE_AND_INTERFACE_INFO( -+ USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_4S, -+ IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, -+ IPHETH_USBINTF_PROTO) }, -+ { USB_DEVICE_AND_INTERFACE_INFO( -+ USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_5, -+ IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, -+ IPHETH_USBINTF_PROTO) }, -+ { } -+}; -+MODULE_DEVICE_TABLE(usb, ipheth_table); -+ -+struct ipheth_device { -+ struct usb_device *udev; -+ struct usb_interface *intf; -+ struct net_device *net; -+ struct sk_buff *tx_skb; -+ struct urb *tx_urb; -+ struct urb *rx_urb; -+ unsigned char *tx_buf; -+ unsigned char *rx_buf; -+ unsigned char *ctrl_buf; -+ u8 bulk_in; -+ u8 bulk_out; -+ struct delayed_work carrier_work; -+}; -+ -+static int ipheth_rx_submit(struct ipheth_device *dev, gfp_t mem_flags); -+ -+static int ipheth_alloc_urbs(struct ipheth_device *iphone) -+{ -+ struct urb *tx_urb = NULL; -+ struct urb *rx_urb = NULL; -+ u8 *tx_buf = NULL; -+ u8 *rx_buf = NULL; -+ -+ tx_urb = usb_alloc_urb(0, GFP_KERNEL); -+ if (tx_urb == NULL) -+ goto error_nomem; -+ -+ rx_urb = usb_alloc_urb(0, GFP_KERNEL); -+ if (rx_urb == NULL) -+ goto free_tx_urb; -+ -+ tx_buf = usb_alloc_coherent(iphone->udev, IPHETH_BUF_SIZE, -+ GFP_KERNEL, &tx_urb->transfer_dma); -+ if (tx_buf == NULL) -+ goto free_rx_urb; -+ -+ rx_buf = usb_alloc_coherent(iphone->udev, IPHETH_BUF_SIZE, -+ GFP_KERNEL, &rx_urb->transfer_dma); -+ if (rx_buf == NULL) -+ goto free_tx_buf; -+ -+ -+ iphone->tx_urb = tx_urb; -+ iphone->rx_urb = rx_urb; -+ iphone->tx_buf = tx_buf; -+ iphone->rx_buf = rx_buf; -+ return 0; -+ -+free_tx_buf: -+ usb_free_coherent(iphone->udev, IPHETH_BUF_SIZE, tx_buf, -+ tx_urb->transfer_dma); -+free_rx_urb: -+ usb_free_urb(rx_urb); -+free_tx_urb: -+ usb_free_urb(tx_urb); -+error_nomem: -+ return -ENOMEM; -+} -+ -+static void ipheth_free_urbs(struct ipheth_device *iphone) -+{ -+ usb_free_coherent(iphone->udev, IPHETH_BUF_SIZE, iphone->rx_buf, -+ iphone->rx_urb->transfer_dma); -+ usb_free_coherent(iphone->udev, IPHETH_BUF_SIZE, iphone->tx_buf, -+ iphone->tx_urb->transfer_dma); -+ usb_free_urb(iphone->rx_urb); -+ usb_free_urb(iphone->tx_urb); -+} -+ -+static void ipheth_kill_urbs(struct ipheth_device *dev) -+{ -+ usb_kill_urb(dev->tx_urb); -+ usb_kill_urb(dev->rx_urb); -+} -+ -+static void ipheth_rcvbulk_callback(struct urb *urb) -+{ -+ struct ipheth_device *dev; -+ struct sk_buff *skb; -+ int status; -+ char *buf; -+ int len; -+ -+ dev = urb->context; -+ if (dev == NULL) -+ return; -+ -+ status = urb->status; -+ switch (status) { -+ case -ENOENT: -+ case -ECONNRESET: -+ case -ESHUTDOWN: -+ return; -+ case 0: -+ break; -+ default: -+ dev_err(&dev->intf->dev, "%s: urb status: %d\n", -+ __func__, status); -+ return; -+ } -+ -+ if (urb->actual_length <= IPHETH_IP_ALIGN) { -+ dev->net->stats.rx_length_errors++; -+ return; -+ } -+ len = urb->actual_length - IPHETH_IP_ALIGN; -+ buf = urb->transfer_buffer + IPHETH_IP_ALIGN; -+ -+ skb = dev_alloc_skb(len); -+ if (!skb) { -+ dev_err(&dev->intf->dev, "%s: dev_alloc_skb: -ENOMEM\n", -+ __func__); -+ dev->net->stats.rx_dropped++; -+ return; -+ } -+ -+ memcpy(skb_put(skb, len), buf, len); -+ skb->dev = dev->net; -+ skb->protocol = eth_type_trans(skb, dev->net); -+ -+ dev->net->stats.rx_packets++; -+ dev->net->stats.rx_bytes += len; -+ -+ netif_rx(skb); -+ ipheth_rx_submit(dev, GFP_ATOMIC); -+} -+ -+static void ipheth_sndbulk_callback(struct urb *urb) -+{ -+ struct ipheth_device *dev; -+ int status = urb->status; -+ -+ dev = urb->context; -+ if (dev == NULL) -+ return; -+ -+ if (status != 0 && -+ status != -ENOENT && -+ status != -ECONNRESET && -+ status != -ESHUTDOWN) -+ dev_err(&dev->intf->dev, "%s: urb status: %d\n", -+ __func__, status); -+ -+ dev_kfree_skb_irq(dev->tx_skb); -+ netif_wake_queue(dev->net); -+} -+ -+static int ipheth_carrier_set(struct ipheth_device *dev) -+{ -+ struct usb_device *udev = dev->udev; -+ int retval; -+ -+ retval = usb_control_msg(udev, -+ usb_rcvctrlpipe(udev, IPHETH_CTRL_ENDP), -+ IPHETH_CMD_CARRIER_CHECK, /* request */ -+ 0xc0, /* request type */ -+ 0x00, /* value */ -+ 0x02, /* index */ -+ dev->ctrl_buf, IPHETH_CTRL_BUF_SIZE, -+ IPHETH_CTRL_TIMEOUT); -+ if (retval < 0) { -+ dev_err(&dev->intf->dev, "%s: usb_control_msg: %d\n", -+ __func__, retval); -+ return retval; -+ } -+ -+ if (dev->ctrl_buf[0] == IPHETH_CARRIER_ON) -+ netif_carrier_on(dev->net); -+ else -+ netif_carrier_off(dev->net); -+ -+ return 0; -+} -+ -+static void ipheth_carrier_check_work(struct work_struct *work) -+{ -+ struct ipheth_device *dev = container_of(work, struct ipheth_device, -+ carrier_work.work); -+ -+ ipheth_carrier_set(dev); -+ schedule_delayed_work(&dev->carrier_work, IPHETH_CARRIER_CHECK_TIMEOUT); -+} -+ -+static int ipheth_get_macaddr(struct ipheth_device *dev) -+{ -+ struct usb_device *udev = dev->udev; -+ struct net_device *net = dev->net; -+ int retval; -+ -+ retval = usb_control_msg(udev, -+ usb_rcvctrlpipe(udev, IPHETH_CTRL_ENDP), -+ IPHETH_CMD_GET_MACADDR, /* request */ -+ 0xc0, /* request type */ -+ 0x00, /* value */ -+ 0x02, /* index */ -+ dev->ctrl_buf, -+ IPHETH_CTRL_BUF_SIZE, -+ IPHETH_CTRL_TIMEOUT); -+ if (retval < 0) { -+ dev_err(&dev->intf->dev, "%s: usb_control_msg: %d\n", -+ __func__, retval); -+ } else if (retval < ETH_ALEN) { -+ dev_err(&dev->intf->dev, -+ "%s: usb_control_msg: short packet: %d bytes\n", -+ __func__, retval); -+ retval = -EINVAL; -+ } else { -+ memcpy(net->dev_addr, dev->ctrl_buf, ETH_ALEN); -+ retval = 0; -+ } -+ -+ return retval; -+} -+ -+static int ipheth_rx_submit(struct ipheth_device *dev, gfp_t mem_flags) -+{ -+ struct usb_device *udev = dev->udev; -+ int retval; -+ -+ usb_fill_bulk_urb(dev->rx_urb, udev, -+ usb_rcvbulkpipe(udev, dev->bulk_in), -+ dev->rx_buf, IPHETH_BUF_SIZE, -+ ipheth_rcvbulk_callback, -+ dev); -+ dev->rx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; -+ -+ retval = usb_submit_urb(dev->rx_urb, mem_flags); -+ if (retval) -+ dev_err(&dev->intf->dev, "%s: usb_submit_urb: %d\n", -+ __func__, retval); -+ return retval; -+} -+ -+static int ipheth_open(struct net_device *net) -+{ -+ struct ipheth_device *dev = netdev_priv(net); -+ struct usb_device *udev = dev->udev; -+ int retval = 0; -+ -+ usb_set_interface(udev, IPHETH_INTFNUM, IPHETH_ALT_INTFNUM); -+ -+ retval = ipheth_carrier_set(dev); -+ if (retval) -+ return retval; -+ -+ retval = ipheth_rx_submit(dev, GFP_KERNEL); -+ if (retval) -+ return retval; -+ -+ schedule_delayed_work(&dev->carrier_work, IPHETH_CARRIER_CHECK_TIMEOUT); -+ netif_start_queue(net); -+ return retval; -+} -+ -+static int ipheth_close(struct net_device *net) -+{ -+ struct ipheth_device *dev = netdev_priv(net); -+ -+ cancel_delayed_work_sync(&dev->carrier_work); -+ netif_stop_queue(net); -+ return 0; -+} -+ -+static int ipheth_tx(struct sk_buff *skb, struct net_device *net) -+{ -+ struct ipheth_device *dev = netdev_priv(net); -+ struct usb_device *udev = dev->udev; -+ int retval; -+ -+ /* Paranoid */ -+ if (skb->len > IPHETH_BUF_SIZE) { -+ WARN(1, "%s: skb too large: %d bytes\n", __func__, skb->len); -+ dev->net->stats.tx_dropped++; -+ dev_kfree_skb_irq(skb); -+ return NETDEV_TX_OK; -+ } -+ -+ memcpy(dev->tx_buf, skb->data, skb->len); -+ if (skb->len < IPHETH_BUF_SIZE) -+ memset(dev->tx_buf + skb->len, 0, IPHETH_BUF_SIZE - skb->len); -+ -+ usb_fill_bulk_urb(dev->tx_urb, udev, -+ usb_sndbulkpipe(udev, dev->bulk_out), -+ dev->tx_buf, IPHETH_BUF_SIZE, -+ ipheth_sndbulk_callback, -+ dev); -+ dev->tx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; -+ -+ retval = usb_submit_urb(dev->tx_urb, GFP_ATOMIC); -+ if (retval) { -+ dev_err(&dev->intf->dev, "%s: usb_submit_urb: %d\n", -+ __func__, retval); -+ dev->net->stats.tx_errors++; -+ dev_kfree_skb_irq(skb); -+ } else { -+ dev->tx_skb = skb; -+ -+ dev->net->stats.tx_packets++; -+ dev->net->stats.tx_bytes += skb->len; -+ netif_stop_queue(net); -+ } -+ -+ return NETDEV_TX_OK; -+} -+ -+static void ipheth_tx_timeout(struct net_device *net) -+{ -+ struct ipheth_device *dev = netdev_priv(net); -+ -+ dev_err(&dev->intf->dev, "%s: TX timeout\n", __func__); -+ dev->net->stats.tx_errors++; -+ usb_unlink_urb(dev->tx_urb); -+} -+ -+static u32 ipheth_ethtool_op_get_link(struct net_device *net) -+{ -+ struct ipheth_device *dev = netdev_priv(net); -+ return netif_carrier_ok(dev->net); -+} -+ -+static const struct ethtool_ops ops = { -+ .get_link = ipheth_ethtool_op_get_link -+}; -+ -+static const struct net_device_ops ipheth_netdev_ops = { -+ .ndo_open = ipheth_open, -+ .ndo_stop = ipheth_close, -+ .ndo_start_xmit = ipheth_tx, -+ .ndo_tx_timeout = ipheth_tx_timeout, -+}; -+ -+static int ipheth_probe(struct usb_interface *intf, -+ const struct usb_device_id *id) -+{ -+ struct usb_device *udev = interface_to_usbdev(intf); -+ struct usb_host_interface *hintf; -+ struct usb_endpoint_descriptor *endp; -+ struct ipheth_device *dev; -+ struct net_device *netdev; -+ int i; -+ int retval; -+ -+ netdev = alloc_etherdev(sizeof(struct ipheth_device)); -+ if (!netdev) -+ return -ENOMEM; -+ -+ netdev->netdev_ops = &ipheth_netdev_ops; -+ netdev->watchdog_timeo = IPHETH_TX_TIMEOUT; -+ strcpy(netdev->name, "eth%d"); -+ -+ dev = netdev_priv(netdev); -+ dev->udev = udev; -+ dev->net = netdev; -+ dev->intf = intf; -+ -+ /* Set up endpoints */ -+ hintf = usb_altnum_to_altsetting(intf, IPHETH_ALT_INTFNUM); -+ if (hintf == NULL) { -+ retval = -ENODEV; -+ dev_err(&intf->dev, "Unable to find alternate settings interface\n"); -+ goto err_endpoints; -+ } -+ -+ for (i = 0; i < hintf->desc.bNumEndpoints; i++) { -+ endp = &hintf->endpoint[i].desc; -+ if (usb_endpoint_is_bulk_in(endp)) -+ dev->bulk_in = endp->bEndpointAddress; -+ else if (usb_endpoint_is_bulk_out(endp)) -+ dev->bulk_out = endp->bEndpointAddress; -+ } -+ if (!(dev->bulk_in && dev->bulk_out)) { -+ retval = -ENODEV; -+ dev_err(&intf->dev, "Unable to find endpoints\n"); -+ goto err_endpoints; -+ } -+ -+ dev->ctrl_buf = kmalloc(IPHETH_CTRL_BUF_SIZE, GFP_KERNEL); -+ if (dev->ctrl_buf == NULL) { -+ retval = -ENOMEM; -+ goto err_alloc_ctrl_buf; -+ } -+ -+ retval = ipheth_get_macaddr(dev); -+ if (retval) -+ goto err_get_macaddr; -+ -+ INIT_DELAYED_WORK(&dev->carrier_work, ipheth_carrier_check_work); -+ -+ retval = ipheth_alloc_urbs(dev); -+ if (retval) { -+ dev_err(&intf->dev, "error allocating urbs: %d\n", retval); -+ goto err_alloc_urbs; -+ } -+ -+ usb_set_intfdata(intf, dev); -+ -+ SET_NETDEV_DEV(netdev, &intf->dev); -+ netdev->ethtool_ops = &ops; -+ -+ retval = register_netdev(netdev); -+ if (retval) { -+ dev_err(&intf->dev, "error registering netdev: %d\n", retval); -+ retval = -EIO; -+ goto err_register_netdev; -+ } -+ -+ dev_info(&intf->dev, "Apple iPhone USB Ethernet device attached\n"); -+ return 0; -+ -+err_register_netdev: -+ ipheth_free_urbs(dev); -+err_alloc_urbs: -+err_get_macaddr: -+err_alloc_ctrl_buf: -+ kfree(dev->ctrl_buf); -+err_endpoints: -+ free_netdev(netdev); -+ return retval; -+} -+ -+static void ipheth_disconnect(struct usb_interface *intf) -+{ -+ struct ipheth_device *dev; -+ -+ dev = usb_get_intfdata(intf); -+ if (dev != NULL) { -+ unregister_netdev(dev->net); -+ ipheth_kill_urbs(dev); -+ ipheth_free_urbs(dev); -+ kfree(dev->ctrl_buf); -+ free_netdev(dev->net); -+ } -+ usb_set_intfdata(intf, NULL); -+ dev_info(&intf->dev, "Apple iPhone USB Ethernet now disconnected\n"); -+} -+ -+static struct usb_driver ipheth_driver = { -+ .name = "ipheth", -+ .probe = ipheth_probe, -+ .disconnect = ipheth_disconnect, -+ .id_table = ipheth_table, -+ .disable_hub_initiated_lpm = 1, -+}; -+ -+module_usb_driver(ipheth_driver); -+ -+MODULE_AUTHOR("Diego Giagio "); -+MODULE_DESCRIPTION("Apple iPhone USB Ethernet driver"); -+MODULE_LICENSE("Dual BSD/GPL"); -diff -Naur backports-4.2.6-1.org/drivers/net/usb/kalmia.c backports-4.2.6-1/drivers/net/usb/kalmia.c ---- backports-4.2.6-1.org/drivers/net/usb/kalmia.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/kalmia.c 2016-06-28 14:35:17.985307220 +0200 -@@ -0,0 +1,366 @@ -+/* -+ * USB network interface driver for Samsung Kalmia based LTE USB modem like the -+ * Samsung GT-B3730 and GT-B3710. -+ * -+ * Copyright (C) 2011 Marius Bjoernstad Kotsbak -+ * -+ * Sponsored by Quicklink Video Distribution Services Ltd. -+ * -+ * Based on the cdc_eem module. -+ * -+ * 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. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* -+ * The Samsung Kalmia based LTE USB modems have a CDC ACM port for modem control -+ * handled by the "option" module and an ethernet data port handled by this -+ * module. -+ * -+ * The stick must first be switched into modem mode by usb_modeswitch -+ * or similar tool. Then the modem gets sent two initialization packets by -+ * this module, which gives the MAC address of the device. User space can then -+ * connect the modem using AT commands through the ACM port and then use -+ * DHCP on the network interface exposed by this module. Network packets are -+ * sent to and from the modem in a proprietary format discovered after watching -+ * the behavior of the windows driver for the modem. -+ * -+ * More information about the use of the modem is available in usb_modeswitch -+ * forum and the project page: -+ * -+ * http://www.draisberghof.de/usb_modeswitch/bb/viewtopic.php?t=465 -+ * https://github.com/mkotsbak/Samsung-GT-B3730-linux-driver -+ */ -+ -+/* #define DEBUG */ -+/* #define VERBOSE */ -+ -+#define KALMIA_HEADER_LENGTH 6 -+#define KALMIA_ALIGN_SIZE 4 -+#define KALMIA_USB_TIMEOUT 10000 -+ -+/*-------------------------------------------------------------------------*/ -+ -+static int -+kalmia_send_init_packet(struct usbnet *dev, u8 *init_msg, u8 init_msg_len, -+ u8 *buffer, u8 expected_len) -+{ -+ int act_len; -+ int status; -+ -+ netdev_dbg(dev->net, "Sending init packet"); -+ -+ status = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 0x02), -+ init_msg, init_msg_len, &act_len, KALMIA_USB_TIMEOUT); -+ if (status != 0) { -+ netdev_err(dev->net, -+ "Error sending init packet. Status %i, length %i\n", -+ status, act_len); -+ return status; -+ } -+ else if (act_len != init_msg_len) { -+ netdev_err(dev->net, -+ "Did not send all of init packet. Bytes sent: %i", -+ act_len); -+ } -+ else { -+ netdev_dbg(dev->net, "Successfully sent init packet."); -+ } -+ -+ status = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, 0x81), -+ buffer, expected_len, &act_len, KALMIA_USB_TIMEOUT); -+ -+ if (status != 0) -+ netdev_err(dev->net, -+ "Error receiving init result. Status %i, length %i\n", -+ status, act_len); -+ else if (act_len != expected_len) -+ netdev_err(dev->net, "Unexpected init result length: %i\n", -+ act_len); -+ -+ return status; -+} -+ -+static int -+kalmia_init_and_get_ethernet_addr(struct usbnet *dev, u8 *ethernet_addr) -+{ -+ static const char init_msg_1[] = -+ { 0x57, 0x50, 0x04, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, -+ 0x00, 0x00 }; -+ static const char init_msg_2[] = -+ { 0x57, 0x50, 0x04, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xf4, -+ 0x00, 0x00 }; -+ static const int buflen = 28; -+ char *usb_buf; -+ int status; -+ -+ usb_buf = kmalloc(buflen, GFP_DMA | GFP_KERNEL); -+ if (!usb_buf) -+ return -ENOMEM; -+ -+ memcpy(usb_buf, init_msg_1, 12); -+ status = kalmia_send_init_packet(dev, usb_buf, sizeof(init_msg_1) -+ / sizeof(init_msg_1[0]), usb_buf, 24); -+ if (status != 0) -+ return status; -+ -+ memcpy(usb_buf, init_msg_2, 12); -+ status = kalmia_send_init_packet(dev, usb_buf, sizeof(init_msg_2) -+ / sizeof(init_msg_2[0]), usb_buf, 28); -+ if (status != 0) -+ return status; -+ -+ memcpy(ethernet_addr, usb_buf + 10, ETH_ALEN); -+ -+ kfree(usb_buf); -+ return status; -+} -+ -+static int -+kalmia_bind(struct usbnet *dev, struct usb_interface *intf) -+{ -+ int status; -+ u8 ethernet_addr[ETH_ALEN]; -+ -+ /* Don't bind to AT command interface */ -+ if (intf->cur_altsetting->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC) -+ return -EINVAL; -+ -+ dev->in = usb_rcvbulkpipe(dev->udev, 0x81 & USB_ENDPOINT_NUMBER_MASK); -+ dev->out = usb_sndbulkpipe(dev->udev, 0x02 & USB_ENDPOINT_NUMBER_MASK); -+ dev->status = NULL; -+ -+ dev->net->hard_header_len += KALMIA_HEADER_LENGTH; -+ dev->hard_mtu = 1400; -+ dev->rx_urb_size = dev->hard_mtu * 10; // Found as optimal after testing -+ -+ status = kalmia_init_and_get_ethernet_addr(dev, ethernet_addr); -+ -+ if (status < 0) { -+ usb_set_intfdata(intf, NULL); -+ usb_driver_release_interface(driver_of(intf), intf); -+ return status; -+ } -+ -+ memcpy(dev->net->dev_addr, ethernet_addr, ETH_ALEN); -+ -+ return status; -+} -+ -+static struct sk_buff * -+kalmia_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) -+{ -+ struct sk_buff *skb2 = NULL; -+ u16 content_len; -+ unsigned char *header_start; -+ unsigned char ether_type_1, ether_type_2; -+ u8 remainder, padlen = 0; -+ -+ if (!skb_cloned(skb)) { -+ int headroom = skb_headroom(skb); -+ int tailroom = skb_tailroom(skb); -+ -+ if ((tailroom >= KALMIA_ALIGN_SIZE) && (headroom -+ >= KALMIA_HEADER_LENGTH)) -+ goto done; -+ -+ if ((headroom + tailroom) > (KALMIA_HEADER_LENGTH -+ + KALMIA_ALIGN_SIZE)) { -+ skb->data = memmove(skb->head + KALMIA_HEADER_LENGTH, -+ skb->data, skb->len); -+ skb_set_tail_pointer(skb, skb->len); -+ goto done; -+ } -+ } -+ -+ skb2 = skb_copy_expand(skb, KALMIA_HEADER_LENGTH, -+ KALMIA_ALIGN_SIZE, flags); -+ if (!skb2) -+ return NULL; -+ -+ dev_kfree_skb_any(skb); -+ skb = skb2; -+ -+done: -+ header_start = skb_push(skb, KALMIA_HEADER_LENGTH); -+ ether_type_1 = header_start[KALMIA_HEADER_LENGTH + 12]; -+ ether_type_2 = header_start[KALMIA_HEADER_LENGTH + 13]; -+ -+ netdev_dbg(dev->net, "Sending etherType: %02x%02x", ether_type_1, -+ ether_type_2); -+ -+ /* According to empiric data for data packages */ -+ header_start[0] = 0x57; -+ header_start[1] = 0x44; -+ content_len = skb->len - KALMIA_HEADER_LENGTH; -+ -+ put_unaligned_le16(content_len, &header_start[2]); -+ header_start[4] = ether_type_1; -+ header_start[5] = ether_type_2; -+ -+ /* Align to 4 bytes by padding with zeros */ -+ remainder = skb->len % KALMIA_ALIGN_SIZE; -+ if (remainder > 0) { -+ padlen = KALMIA_ALIGN_SIZE - remainder; -+ memset(skb_put(skb, padlen), 0, padlen); -+ } -+ -+ netdev_dbg(dev->net, -+ "Sending package with length %i and padding %i. Header: %6phC.", -+ content_len, padlen, header_start); -+ -+ return skb; -+} -+ -+static int -+kalmia_rx_fixup(struct usbnet *dev, struct sk_buff *skb) -+{ -+ /* -+ * Our task here is to strip off framing, leaving skb with one -+ * data frame for the usbnet framework code to process. -+ */ -+ static const u8 HEADER_END_OF_USB_PACKET[] = -+ { 0x57, 0x5a, 0x00, 0x00, 0x08, 0x00 }; -+ static const u8 EXPECTED_UNKNOWN_HEADER_1[] = -+ { 0x57, 0x43, 0x1e, 0x00, 0x15, 0x02 }; -+ static const u8 EXPECTED_UNKNOWN_HEADER_2[] = -+ { 0x57, 0x50, 0x0e, 0x00, 0x00, 0x00 }; -+ int i = 0; -+ -+ /* incomplete header? */ -+ if (skb->len < KALMIA_HEADER_LENGTH) -+ return 0; -+ -+ do { -+ struct sk_buff *skb2 = NULL; -+ u8 *header_start; -+ u16 usb_packet_length, ether_packet_length; -+ int is_last; -+ -+ header_start = skb->data; -+ -+ if (unlikely(header_start[0] != 0x57 || header_start[1] != 0x44)) { -+ if (!memcmp(header_start, EXPECTED_UNKNOWN_HEADER_1, -+ sizeof(EXPECTED_UNKNOWN_HEADER_1)) || !memcmp( -+ header_start, EXPECTED_UNKNOWN_HEADER_2, -+ sizeof(EXPECTED_UNKNOWN_HEADER_2))) { -+ netdev_dbg(dev->net, -+ "Received expected unknown frame header: %6phC. Package length: %i\n", -+ header_start, -+ skb->len - KALMIA_HEADER_LENGTH); -+ } -+ else { -+ netdev_err(dev->net, -+ "Received unknown frame header: %6phC. Package length: %i\n", -+ header_start, -+ skb->len - KALMIA_HEADER_LENGTH); -+ return 0; -+ } -+ } -+ else -+ netdev_dbg(dev->net, -+ "Received header: %6phC. Package length: %i\n", -+ header_start, skb->len - KALMIA_HEADER_LENGTH); -+ -+ /* subtract start header and end header */ -+ usb_packet_length = skb->len - (2 * KALMIA_HEADER_LENGTH); -+ ether_packet_length = get_unaligned_le16(&header_start[2]); -+ skb_pull(skb, KALMIA_HEADER_LENGTH); -+ -+ /* Some small packets misses end marker */ -+ if (usb_packet_length < ether_packet_length) { -+ ether_packet_length = usb_packet_length -+ + KALMIA_HEADER_LENGTH; -+ is_last = true; -+ } -+ else { -+ netdev_dbg(dev->net, "Correct package length #%i", i -+ + 1); -+ -+ is_last = (memcmp(skb->data + ether_packet_length, -+ HEADER_END_OF_USB_PACKET, -+ sizeof(HEADER_END_OF_USB_PACKET)) == 0); -+ if (!is_last) { -+ header_start = skb->data + ether_packet_length; -+ netdev_dbg(dev->net, -+ "End header: %6phC. Package length: %i\n", -+ header_start, -+ skb->len - KALMIA_HEADER_LENGTH); -+ } -+ } -+ -+ if (is_last) { -+ skb2 = skb; -+ } -+ else { -+ skb2 = skb_clone(skb, GFP_ATOMIC); -+ if (unlikely(!skb2)) -+ return 0; -+ } -+ -+ skb_trim(skb2, ether_packet_length); -+ -+ if (is_last) { -+ return 1; -+ } -+ else { -+ usbnet_skb_return(dev, skb2); -+ skb_pull(skb, ether_packet_length); -+ } -+ -+ i++; -+ } -+ while (skb->len); -+ -+ return 1; -+} -+ -+static const struct driver_info kalmia_info = { -+ .description = "Samsung Kalmia LTE USB dongle", -+ .flags = FLAG_WWAN, -+ .bind = kalmia_bind, -+ .rx_fixup = kalmia_rx_fixup, -+ .tx_fixup = kalmia_tx_fixup -+}; -+ -+/*-------------------------------------------------------------------------*/ -+ -+static const struct usb_device_id products[] = { -+ /* The unswitched USB ID, to get the module auto loaded: */ -+ { USB_DEVICE(0x04e8, 0x689a) }, -+ /* The stick swithed into modem (by e.g. usb_modeswitch): */ -+ { USB_DEVICE(0x04e8, 0x6889), -+ .driver_info = (unsigned long) &kalmia_info, }, -+ { /* EMPTY == end of list */} }; -+MODULE_DEVICE_TABLE( usb, products); -+ -+static struct usb_driver kalmia_driver = { -+ .name = "kalmia", -+ .id_table = products, -+ .probe = usbnet_probe, -+ .disconnect = usbnet_disconnect, -+ .suspend = usbnet_suspend, -+ .resume = usbnet_resume, -+ .disable_hub_initiated_lpm = 1, -+}; -+ -+module_usb_driver(kalmia_driver); -+ -+MODULE_AUTHOR("Marius Bjoernstad Kotsbak "); -+MODULE_DESCRIPTION("Samsung Kalmia USB network driver"); -+MODULE_LICENSE("GPL"); -diff -Naur backports-4.2.6-1.org/drivers/net/usb/kaweth.c backports-4.2.6-1/drivers/net/usb/kaweth.c ---- backports-4.2.6-1.org/drivers/net/usb/kaweth.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/kaweth.c 2016-06-28 14:35:17.988640553 +0200 -@@ -0,0 +1,1331 @@ -+/**************************************************************** -+ * -+ * kaweth.c - driver for KL5KUSB101 based USB->Ethernet -+ * -+ * (c) 2000 Interlan Communications -+ * (c) 2000 Stephane Alnet -+ * (C) 2001 Brad Hards -+ * (C) 2002 Oliver Neukum -+ * -+ * Original author: The Zapman -+ * Inspired by, and much credit goes to Michael Rothwell -+ * for the test equipment, help, and patience -+ * Based off of (and with thanks to) Petko Manolov's pegaus.c driver. -+ * Also many thanks to Joel Silverman and Ed Surprenant at Kawasaki -+ * for providing the firmware and driver resources. -+ * -+ * 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, 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, see . -+ * -+ ****************************************************************/ -+ -+/* TODO: -+ * Develop test procedures for USB net interfaces -+ * Run test procedures -+ * Fix bugs from previous two steps -+ * Snoop other OSs for any tricks we're not doing -+ * Reduce arbitrary timeouts -+ * Smart multicast support -+ * Temporary MAC change support -+ * Tunable SOFs parameter - ioctl()? -+ * Ethernet stats collection -+ * Code formatting improvements -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#undef DEBUG -+ -+#define KAWETH_MTU 1514 -+#define KAWETH_BUF_SIZE 1664 -+#define KAWETH_TX_TIMEOUT (5 * HZ) -+#define KAWETH_SCRATCH_SIZE 32 -+#define KAWETH_FIRMWARE_BUF_SIZE 4096 -+#define KAWETH_CONTROL_TIMEOUT (30000) -+ -+#define KAWETH_STATUS_BROKEN 0x0000001 -+#define KAWETH_STATUS_CLOSING 0x0000002 -+#define KAWETH_STATUS_SUSPENDING 0x0000004 -+ -+#define KAWETH_STATUS_BLOCKED (KAWETH_STATUS_CLOSING | KAWETH_STATUS_SUSPENDING) -+ -+#define KAWETH_PACKET_FILTER_PROMISCUOUS 0x01 -+#define KAWETH_PACKET_FILTER_ALL_MULTICAST 0x02 -+#define KAWETH_PACKET_FILTER_DIRECTED 0x04 -+#define KAWETH_PACKET_FILTER_BROADCAST 0x08 -+#define KAWETH_PACKET_FILTER_MULTICAST 0x10 -+ -+/* Table 7 */ -+#define KAWETH_COMMAND_GET_ETHERNET_DESC 0x00 -+#define KAWETH_COMMAND_MULTICAST_FILTERS 0x01 -+#define KAWETH_COMMAND_SET_PACKET_FILTER 0x02 -+#define KAWETH_COMMAND_STATISTICS 0x03 -+#define KAWETH_COMMAND_SET_TEMP_MAC 0x06 -+#define KAWETH_COMMAND_GET_TEMP_MAC 0x07 -+#define KAWETH_COMMAND_SET_URB_SIZE 0x08 -+#define KAWETH_COMMAND_SET_SOFS_WAIT 0x09 -+#define KAWETH_COMMAND_SCAN 0xFF -+ -+#define KAWETH_SOFS_TO_WAIT 0x05 -+ -+#define INTBUFFERSIZE 4 -+ -+#define STATE_OFFSET 0 -+#define STATE_MASK 0x40 -+#define STATE_SHIFT 5 -+ -+#define IS_BLOCKED(s) (s & KAWETH_STATUS_BLOCKED) -+ -+ -+MODULE_AUTHOR("Michael Zappe , Stephane Alnet , Brad Hards and Oliver Neukum "); -+MODULE_DESCRIPTION("KL5USB101 USB Ethernet driver"); -+MODULE_LICENSE("GPL"); -+MODULE_FIRMWARE("kaweth/new_code.bin"); -+MODULE_FIRMWARE("kaweth/new_code_fix.bin"); -+MODULE_FIRMWARE("kaweth/trigger_code.bin"); -+MODULE_FIRMWARE("kaweth/trigger_code_fix.bin"); -+ -+static const char driver_name[] = "kaweth"; -+ -+static int kaweth_probe( -+ struct usb_interface *intf, -+ const struct usb_device_id *id /* from id_table */ -+ ); -+static void kaweth_disconnect(struct usb_interface *intf); -+static int kaweth_internal_control_msg(struct usb_device *usb_dev, -+ unsigned int pipe, -+ struct usb_ctrlrequest *cmd, void *data, -+ int len, int timeout); -+static int kaweth_suspend(struct usb_interface *intf, pm_message_t message); -+static int kaweth_resume(struct usb_interface *intf); -+ -+/**************************************************************** -+ * usb_device_id -+ ****************************************************************/ -+static struct usb_device_id usb_klsi_table[] = { -+ { USB_DEVICE(0x03e8, 0x0008) }, /* AOX Endpoints USB Ethernet */ -+ { USB_DEVICE(0x04bb, 0x0901) }, /* I-O DATA USB-ET/T */ -+ { USB_DEVICE(0x0506, 0x03e8) }, /* 3Com 3C19250 */ -+ { USB_DEVICE(0x0506, 0x11f8) }, /* 3Com 3C460 */ -+ { USB_DEVICE(0x0557, 0x2002) }, /* ATEN USB Ethernet */ -+ { USB_DEVICE(0x0557, 0x4000) }, /* D-Link DSB-650C */ -+ { USB_DEVICE(0x0565, 0x0002) }, /* Peracom Enet */ -+ { USB_DEVICE(0x0565, 0x0003) }, /* Optus@Home UEP1045A */ -+ { USB_DEVICE(0x0565, 0x0005) }, /* Peracom Enet2 */ -+ { USB_DEVICE(0x05e9, 0x0008) }, /* KLSI KL5KUSB101B */ -+ { USB_DEVICE(0x05e9, 0x0009) }, /* KLSI KL5KUSB101B (Board change) */ -+ { USB_DEVICE(0x066b, 0x2202) }, /* Linksys USB10T */ -+ { USB_DEVICE(0x06e1, 0x0008) }, /* ADS USB-10BT */ -+ { USB_DEVICE(0x06e1, 0x0009) }, /* ADS USB-10BT */ -+ { USB_DEVICE(0x0707, 0x0100) }, /* SMC 2202USB */ -+ { USB_DEVICE(0x07aa, 0x0001) }, /* Correga K.K. */ -+ { USB_DEVICE(0x07b8, 0x4000) }, /* D-Link DU-E10 */ -+ { USB_DEVICE(0x07c9, 0xb010) }, /* Allied Telesyn AT-USB10 USB Ethernet Adapter */ -+ { USB_DEVICE(0x0846, 0x1001) }, /* NetGear EA-101 */ -+ { USB_DEVICE(0x0846, 0x1002) }, /* NetGear EA-101 */ -+ { USB_DEVICE(0x085a, 0x0008) }, /* PortGear Ethernet Adapter */ -+ { USB_DEVICE(0x085a, 0x0009) }, /* PortGear Ethernet Adapter */ -+ { USB_DEVICE(0x087d, 0x5704) }, /* Jaton USB Ethernet Device Adapter */ -+ { USB_DEVICE(0x0951, 0x0008) }, /* Kingston Technology USB Ethernet Adapter */ -+ { USB_DEVICE(0x095a, 0x3003) }, /* Portsmith Express Ethernet Adapter */ -+ { USB_DEVICE(0x10bd, 0x1427) }, /* ASANTE USB To Ethernet Adapter */ -+ { USB_DEVICE(0x1342, 0x0204) }, /* Mobility USB-Ethernet Adapter */ -+ { USB_DEVICE(0x13d2, 0x0400) }, /* Shark Pocket Adapter */ -+ { USB_DEVICE(0x1485, 0x0001) }, /* Silicom U2E */ -+ { USB_DEVICE(0x1485, 0x0002) }, /* Psion Dacom Gold Port Ethernet */ -+ { USB_DEVICE(0x1645, 0x0005) }, /* Entrega E45 */ -+ { USB_DEVICE(0x1645, 0x0008) }, /* Entrega USB Ethernet Adapter */ -+ { USB_DEVICE(0x1645, 0x8005) }, /* PortGear Ethernet Adapter */ -+ { USB_DEVICE(0x1668, 0x0323) }, /* Actiontec USB Ethernet */ -+ { USB_DEVICE(0x2001, 0x4000) }, /* D-link DSB-650C */ -+ {} /* Null terminator */ -+}; -+ -+MODULE_DEVICE_TABLE (usb, usb_klsi_table); -+ -+/**************************************************************** -+ * kaweth_driver -+ ****************************************************************/ -+static struct usb_driver kaweth_driver = { -+ .name = driver_name, -+ .probe = kaweth_probe, -+ .disconnect = kaweth_disconnect, -+ .suspend = kaweth_suspend, -+ .resume = kaweth_resume, -+ .id_table = usb_klsi_table, -+ .supports_autosuspend = 1, -+ .disable_hub_initiated_lpm = 1, -+}; -+ -+typedef __u8 eth_addr_t[6]; -+ -+/**************************************************************** -+ * usb_eth_dev -+ ****************************************************************/ -+struct usb_eth_dev { -+ char *name; -+ __u16 vendor; -+ __u16 device; -+ void *pdata; -+}; -+ -+/**************************************************************** -+ * kaweth_ethernet_configuration -+ * Refer Table 8 -+ ****************************************************************/ -+struct kaweth_ethernet_configuration -+{ -+ __u8 size; -+ __u8 reserved1; -+ __u8 reserved2; -+ eth_addr_t hw_addr; -+ __u32 statistics_mask; -+ __le16 segment_size; -+ __u16 max_multicast_filters; -+ __u8 reserved3; -+} __packed; -+ -+/**************************************************************** -+ * kaweth_device -+ ****************************************************************/ -+struct kaweth_device -+{ -+ spinlock_t device_lock; -+ -+ __u32 status; -+ int end; -+ int suspend_lowmem_rx; -+ int suspend_lowmem_ctrl; -+ int linkstate; -+ int opened; -+ struct delayed_work lowmem_work; -+ -+ struct usb_device *dev; -+ struct usb_interface *intf; -+ struct net_device *net; -+ wait_queue_head_t term_wait; -+ -+ struct urb *rx_urb; -+ struct urb *tx_urb; -+ struct urb *irq_urb; -+ -+ dma_addr_t intbufferhandle; -+ __u8 *intbuffer; -+ dma_addr_t rxbufferhandle; -+ __u8 *rx_buf; -+ -+ -+ struct sk_buff *tx_skb; -+ -+ __u8 *firmware_buf; -+ __u8 scratch[KAWETH_SCRATCH_SIZE]; -+ __u16 packet_filter_bitmap; -+ -+ struct kaweth_ethernet_configuration configuration; -+ -+ struct net_device_stats stats; -+}; -+ -+/**************************************************************** -+ * kaweth_control -+ ****************************************************************/ -+static int kaweth_control(struct kaweth_device *kaweth, -+ unsigned int pipe, -+ __u8 request, -+ __u8 requesttype, -+ __u16 value, -+ __u16 index, -+ void *data, -+ __u16 size, -+ int timeout) -+{ -+ struct usb_ctrlrequest *dr; -+ int retval; -+ -+ netdev_dbg(kaweth->net, "kaweth_control()\n"); -+ -+ if(in_interrupt()) { -+ netdev_dbg(kaweth->net, "in_interrupt()\n"); -+ return -EBUSY; -+ } -+ -+ dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC); -+ if (!dr) -+ return -ENOMEM; -+ -+ dr->bRequestType = requesttype; -+ dr->bRequest = request; -+ dr->wValue = cpu_to_le16(value); -+ dr->wIndex = cpu_to_le16(index); -+ dr->wLength = cpu_to_le16(size); -+ -+ retval = kaweth_internal_control_msg(kaweth->dev, -+ pipe, -+ dr, -+ data, -+ size, -+ timeout); -+ -+ kfree(dr); -+ return retval; -+} -+ -+/**************************************************************** -+ * kaweth_read_configuration -+ ****************************************************************/ -+static int kaweth_read_configuration(struct kaweth_device *kaweth) -+{ -+ int retval; -+ -+ netdev_dbg(kaweth->net, "Reading kaweth configuration\n"); -+ -+ retval = kaweth_control(kaweth, -+ usb_rcvctrlpipe(kaweth->dev, 0), -+ KAWETH_COMMAND_GET_ETHERNET_DESC, -+ USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE, -+ 0, -+ 0, -+ (void *)&kaweth->configuration, -+ sizeof(kaweth->configuration), -+ KAWETH_CONTROL_TIMEOUT); -+ -+ return retval; -+} -+ -+/**************************************************************** -+ * kaweth_set_urb_size -+ ****************************************************************/ -+static int kaweth_set_urb_size(struct kaweth_device *kaweth, __u16 urb_size) -+{ -+ int retval; -+ -+ netdev_dbg(kaweth->net, "Setting URB size to %d\n", (unsigned)urb_size); -+ -+ retval = kaweth_control(kaweth, -+ usb_sndctrlpipe(kaweth->dev, 0), -+ KAWETH_COMMAND_SET_URB_SIZE, -+ USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, -+ urb_size, -+ 0, -+ (void *)&kaweth->scratch, -+ 0, -+ KAWETH_CONTROL_TIMEOUT); -+ -+ return retval; -+} -+ -+/**************************************************************** -+ * kaweth_set_sofs_wait -+ ****************************************************************/ -+static int kaweth_set_sofs_wait(struct kaweth_device *kaweth, __u16 sofs_wait) -+{ -+ int retval; -+ -+ netdev_dbg(kaweth->net, "Set SOFS wait to %d\n", (unsigned)sofs_wait); -+ -+ retval = kaweth_control(kaweth, -+ usb_sndctrlpipe(kaweth->dev, 0), -+ KAWETH_COMMAND_SET_SOFS_WAIT, -+ USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, -+ sofs_wait, -+ 0, -+ (void *)&kaweth->scratch, -+ 0, -+ KAWETH_CONTROL_TIMEOUT); -+ -+ return retval; -+} -+ -+/**************************************************************** -+ * kaweth_set_receive_filter -+ ****************************************************************/ -+static int kaweth_set_receive_filter(struct kaweth_device *kaweth, -+ __u16 receive_filter) -+{ -+ int retval; -+ -+ netdev_dbg(kaweth->net, "Set receive filter to %d\n", -+ (unsigned)receive_filter); -+ -+ retval = kaweth_control(kaweth, -+ usb_sndctrlpipe(kaweth->dev, 0), -+ KAWETH_COMMAND_SET_PACKET_FILTER, -+ USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, -+ receive_filter, -+ 0, -+ (void *)&kaweth->scratch, -+ 0, -+ KAWETH_CONTROL_TIMEOUT); -+ -+ return retval; -+} -+ -+/**************************************************************** -+ * kaweth_download_firmware -+ ****************************************************************/ -+static int kaweth_download_firmware(struct kaweth_device *kaweth, -+ const char *fwname, -+ __u8 interrupt, -+ __u8 type) -+{ -+ const struct firmware *fw; -+ int data_len; -+ int ret; -+ -+ ret = request_firmware(&fw, fwname, &kaweth->dev->dev); -+ if (ret) { -+ dev_err(&kaweth->intf->dev, "Firmware request failed\n"); -+ return ret; -+ } -+ -+ if (fw->size > KAWETH_FIRMWARE_BUF_SIZE) { -+ dev_err(&kaweth->intf->dev, "Firmware too big: %zu\n", -+ fw->size); -+ release_firmware(fw); -+ return -ENOSPC; -+ } -+ data_len = fw->size; -+ memcpy(kaweth->firmware_buf, fw->data, fw->size); -+ -+ release_firmware(fw); -+ -+ kaweth->firmware_buf[2] = (data_len & 0xFF) - 7; -+ kaweth->firmware_buf[3] = data_len >> 8; -+ kaweth->firmware_buf[4] = type; -+ kaweth->firmware_buf[5] = interrupt; -+ -+ netdev_dbg(kaweth->net, "High: %i, Low:%i\n", kaweth->firmware_buf[3], -+ kaweth->firmware_buf[2]); -+ -+ netdev_dbg(kaweth->net, -+ "Downloading firmware at %p to kaweth device at %p\n", -+ kaweth->firmware_buf, kaweth); -+ netdev_dbg(kaweth->net, "Firmware length: %d\n", data_len); -+ -+ return kaweth_control(kaweth, -+ usb_sndctrlpipe(kaweth->dev, 0), -+ KAWETH_COMMAND_SCAN, -+ USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, -+ 0, -+ 0, -+ (void *)kaweth->firmware_buf, -+ data_len, -+ KAWETH_CONTROL_TIMEOUT); -+} -+ -+/**************************************************************** -+ * kaweth_trigger_firmware -+ ****************************************************************/ -+static int kaweth_trigger_firmware(struct kaweth_device *kaweth, -+ __u8 interrupt) -+{ -+ kaweth->firmware_buf[0] = 0xB6; -+ kaweth->firmware_buf[1] = 0xC3; -+ kaweth->firmware_buf[2] = 0x01; -+ kaweth->firmware_buf[3] = 0x00; -+ kaweth->firmware_buf[4] = 0x06; -+ kaweth->firmware_buf[5] = interrupt; -+ kaweth->firmware_buf[6] = 0x00; -+ kaweth->firmware_buf[7] = 0x00; -+ -+ netdev_dbg(kaweth->net, "Triggering firmware\n"); -+ -+ return kaweth_control(kaweth, -+ usb_sndctrlpipe(kaweth->dev, 0), -+ KAWETH_COMMAND_SCAN, -+ USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, -+ 0, -+ 0, -+ (void *)kaweth->firmware_buf, -+ 8, -+ KAWETH_CONTROL_TIMEOUT); -+} -+ -+/**************************************************************** -+ * kaweth_reset -+ ****************************************************************/ -+static int kaweth_reset(struct kaweth_device *kaweth) -+{ -+ int result; -+ -+ netdev_dbg(kaweth->net, "kaweth_reset(%p)\n", kaweth); -+ result = usb_reset_configuration(kaweth->dev); -+ mdelay(10); -+ -+ netdev_dbg(kaweth->net, "kaweth_reset() returns %d.\n", result); -+ -+ return result; -+} -+ -+static void kaweth_usb_receive(struct urb *); -+static int kaweth_resubmit_rx_urb(struct kaweth_device *, gfp_t); -+ -+/**************************************************************** -+ int_callback -+*****************************************************************/ -+ -+static void kaweth_resubmit_int_urb(struct kaweth_device *kaweth, gfp_t mf) -+{ -+ int status; -+ -+ status = usb_submit_urb (kaweth->irq_urb, mf); -+ if (unlikely(status == -ENOMEM)) { -+ kaweth->suspend_lowmem_ctrl = 1; -+ schedule_delayed_work(&kaweth->lowmem_work, HZ/4); -+ } else { -+ kaweth->suspend_lowmem_ctrl = 0; -+ } -+ -+ if (status) -+ dev_err(&kaweth->intf->dev, -+ "can't resubmit intr, %s-%s, status %d\n", -+ kaweth->dev->bus->bus_name, -+ kaweth->dev->devpath, status); -+} -+ -+static void int_callback(struct urb *u) -+{ -+ struct kaweth_device *kaweth = u->context; -+ int act_state; -+ int status = u->status; -+ -+ switch (status) { -+ case 0: /* success */ -+ break; -+ case -ECONNRESET: /* unlink */ -+ case -ENOENT: -+ case -ESHUTDOWN: -+ return; -+ /* -EPIPE: should clear the halt */ -+ default: /* error */ -+ goto resubmit; -+ } -+ -+ /* we check the link state to report changes */ -+ if (kaweth->linkstate != (act_state = ( kaweth->intbuffer[STATE_OFFSET] | STATE_MASK) >> STATE_SHIFT)) { -+ if (act_state) -+ netif_carrier_on(kaweth->net); -+ else -+ netif_carrier_off(kaweth->net); -+ -+ kaweth->linkstate = act_state; -+ } -+resubmit: -+ kaweth_resubmit_int_urb(kaweth, GFP_ATOMIC); -+} -+ -+static void kaweth_resubmit_tl(struct work_struct *work) -+{ -+ struct kaweth_device *kaweth = -+ container_of(work, struct kaweth_device, lowmem_work.work); -+ -+ if (IS_BLOCKED(kaweth->status)) -+ return; -+ -+ if (kaweth->suspend_lowmem_rx) -+ kaweth_resubmit_rx_urb(kaweth, GFP_NOIO); -+ -+ if (kaweth->suspend_lowmem_ctrl) -+ kaweth_resubmit_int_urb(kaweth, GFP_NOIO); -+} -+ -+ -+/**************************************************************** -+ * kaweth_resubmit_rx_urb -+ ****************************************************************/ -+static int kaweth_resubmit_rx_urb(struct kaweth_device *kaweth, -+ gfp_t mem_flags) -+{ -+ int result; -+ -+ usb_fill_bulk_urb(kaweth->rx_urb, -+ kaweth->dev, -+ usb_rcvbulkpipe(kaweth->dev, 1), -+ kaweth->rx_buf, -+ KAWETH_BUF_SIZE, -+ kaweth_usb_receive, -+ kaweth); -+ kaweth->rx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; -+ kaweth->rx_urb->transfer_dma = kaweth->rxbufferhandle; -+ -+ if((result = usb_submit_urb(kaweth->rx_urb, mem_flags))) { -+ if (result == -ENOMEM) { -+ kaweth->suspend_lowmem_rx = 1; -+ schedule_delayed_work(&kaweth->lowmem_work, HZ/4); -+ } -+ dev_err(&kaweth->intf->dev, "resubmitting rx_urb %d failed\n", -+ result); -+ } else { -+ kaweth->suspend_lowmem_rx = 0; -+ } -+ -+ return result; -+} -+ -+static void kaweth_async_set_rx_mode(struct kaweth_device *kaweth); -+ -+/**************************************************************** -+ * kaweth_usb_receive -+ ****************************************************************/ -+static void kaweth_usb_receive(struct urb *urb) -+{ -+ struct device *dev = &urb->dev->dev; -+ struct kaweth_device *kaweth = urb->context; -+ struct net_device *net = kaweth->net; -+ int status = urb->status; -+ -+ int count = urb->actual_length; -+ int count2 = urb->transfer_buffer_length; -+ -+ __u16 pkt_len = le16_to_cpup((__le16 *)kaweth->rx_buf); -+ -+ struct sk_buff *skb; -+ -+ if (unlikely(status == -EPIPE)) { -+ kaweth->stats.rx_errors++; -+ kaweth->end = 1; -+ wake_up(&kaweth->term_wait); -+ dev_dbg(dev, "Status was -EPIPE.\n"); -+ return; -+ } -+ if (unlikely(status == -ECONNRESET || status == -ESHUTDOWN)) { -+ /* we are killed - set a flag and wake the disconnect handler */ -+ kaweth->end = 1; -+ wake_up(&kaweth->term_wait); -+ dev_dbg(dev, "Status was -ECONNRESET or -ESHUTDOWN.\n"); -+ return; -+ } -+ if (unlikely(status == -EPROTO || status == -ETIME || -+ status == -EILSEQ)) { -+ kaweth->stats.rx_errors++; -+ dev_dbg(dev, "Status was -EPROTO, -ETIME, or -EILSEQ.\n"); -+ return; -+ } -+ if (unlikely(status == -EOVERFLOW)) { -+ kaweth->stats.rx_errors++; -+ dev_dbg(dev, "Status was -EOVERFLOW.\n"); -+ } -+ spin_lock(&kaweth->device_lock); -+ if (IS_BLOCKED(kaweth->status)) { -+ spin_unlock(&kaweth->device_lock); -+ return; -+ } -+ spin_unlock(&kaweth->device_lock); -+ -+ if(status && status != -EREMOTEIO && count != 1) { -+ dev_err(&kaweth->intf->dev, -+ "%s RX status: %d count: %d packet_len: %d\n", -+ net->name, status, count, (int)pkt_len); -+ kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC); -+ return; -+ } -+ -+ if(kaweth->net && (count > 2)) { -+ if(pkt_len > (count - 2)) { -+ dev_err(&kaweth->intf->dev, -+ "Packet length too long for USB frame (pkt_len: %x, count: %x)\n", -+ pkt_len, count); -+ dev_err(&kaweth->intf->dev, "Packet len & 2047: %x\n", -+ pkt_len & 2047); -+ dev_err(&kaweth->intf->dev, "Count 2: %x\n", count2); -+ kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC); -+ return; -+ } -+ -+ if(!(skb = dev_alloc_skb(pkt_len+2))) { -+ kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC); -+ return; -+ } -+ -+ skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ -+ -+ skb_copy_to_linear_data(skb, kaweth->rx_buf + 2, pkt_len); -+ -+ skb_put(skb, pkt_len); -+ -+ skb->protocol = eth_type_trans(skb, net); -+ -+ netif_rx(skb); -+ -+ kaweth->stats.rx_packets++; -+ kaweth->stats.rx_bytes += pkt_len; -+ } -+ -+ kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC); -+} -+ -+/**************************************************************** -+ * kaweth_open -+ ****************************************************************/ -+static int kaweth_open(struct net_device *net) -+{ -+ struct kaweth_device *kaweth = netdev_priv(net); -+ int res; -+ -+ netdev_dbg(kaweth->net, "Opening network device.\n"); -+ -+ res = usb_autopm_get_interface(kaweth->intf); -+ if (res) { -+ dev_err(&kaweth->intf->dev, "Interface cannot be resumed.\n"); -+ return -EIO; -+ } -+ res = kaweth_resubmit_rx_urb(kaweth, GFP_KERNEL); -+ if (res) -+ goto err_out; -+ -+ usb_fill_int_urb( -+ kaweth->irq_urb, -+ kaweth->dev, -+ usb_rcvintpipe(kaweth->dev, 3), -+ kaweth->intbuffer, -+ INTBUFFERSIZE, -+ int_callback, -+ kaweth, -+ 250); /* overriding the descriptor */ -+ kaweth->irq_urb->transfer_dma = kaweth->intbufferhandle; -+ kaweth->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; -+ -+ res = usb_submit_urb(kaweth->irq_urb, GFP_KERNEL); -+ if (res) { -+ usb_kill_urb(kaweth->rx_urb); -+ goto err_out; -+ } -+ kaweth->opened = 1; -+ -+ netif_start_queue(net); -+ -+ kaweth_async_set_rx_mode(kaweth); -+ return 0; -+ -+err_out: -+ usb_autopm_put_interface(kaweth->intf); -+ return -EIO; -+} -+ -+/**************************************************************** -+ * kaweth_kill_urbs -+ ****************************************************************/ -+static void kaweth_kill_urbs(struct kaweth_device *kaweth) -+{ -+ usb_kill_urb(kaweth->irq_urb); -+ usb_kill_urb(kaweth->rx_urb); -+ usb_kill_urb(kaweth->tx_urb); -+ -+ cancel_delayed_work_sync(&kaweth->lowmem_work); -+ -+ /* a scheduled work may have resubmitted, -+ we hit them again */ -+ usb_kill_urb(kaweth->irq_urb); -+ usb_kill_urb(kaweth->rx_urb); -+} -+ -+/**************************************************************** -+ * kaweth_close -+ ****************************************************************/ -+static int kaweth_close(struct net_device *net) -+{ -+ struct kaweth_device *kaweth = netdev_priv(net); -+ -+ netif_stop_queue(net); -+ kaweth->opened = 0; -+ -+ kaweth->status |= KAWETH_STATUS_CLOSING; -+ -+ kaweth_kill_urbs(kaweth); -+ -+ kaweth->status &= ~KAWETH_STATUS_CLOSING; -+ -+ usb_autopm_put_interface(kaweth->intf); -+ -+ return 0; -+} -+ -+static u32 kaweth_get_link(struct net_device *dev) -+{ -+ struct kaweth_device *kaweth = netdev_priv(dev); -+ -+ return kaweth->linkstate; -+} -+ -+static const struct ethtool_ops ops = { -+ .get_link = kaweth_get_link -+}; -+ -+/**************************************************************** -+ * kaweth_usb_transmit_complete -+ ****************************************************************/ -+static void kaweth_usb_transmit_complete(struct urb *urb) -+{ -+ struct kaweth_device *kaweth = urb->context; -+ struct sk_buff *skb = kaweth->tx_skb; -+ int status = urb->status; -+ -+ if (unlikely(status != 0)) -+ if (status != -ENOENT) -+ dev_dbg(&urb->dev->dev, "%s: TX status %d.\n", -+ kaweth->net->name, status); -+ -+ netif_wake_queue(kaweth->net); -+ dev_kfree_skb_irq(skb); -+} -+ -+/**************************************************************** -+ * kaweth_start_xmit -+ ****************************************************************/ -+static netdev_tx_t kaweth_start_xmit(struct sk_buff *skb, -+ struct net_device *net) -+{ -+ struct kaweth_device *kaweth = netdev_priv(net); -+ __le16 *private_header; -+ -+ int res; -+ -+ spin_lock_irq(&kaweth->device_lock); -+ -+ kaweth_async_set_rx_mode(kaweth); -+ netif_stop_queue(net); -+ if (IS_BLOCKED(kaweth->status)) { -+ goto skip; -+ } -+ -+ /* We now decide whether we can put our special header into the sk_buff */ -+ if (skb_cloned(skb) || skb_headroom(skb) < 2) { -+ /* no such luck - we make our own */ -+ struct sk_buff *copied_skb; -+ copied_skb = skb_copy_expand(skb, 2, 0, GFP_ATOMIC); -+ dev_kfree_skb_irq(skb); -+ skb = copied_skb; -+ if (!copied_skb) { -+ kaweth->stats.tx_errors++; -+ netif_start_queue(net); -+ spin_unlock_irq(&kaweth->device_lock); -+ return NETDEV_TX_OK; -+ } -+ } -+ -+ private_header = (__le16 *)__skb_push(skb, 2); -+ *private_header = cpu_to_le16(skb->len-2); -+ kaweth->tx_skb = skb; -+ -+ usb_fill_bulk_urb(kaweth->tx_urb, -+ kaweth->dev, -+ usb_sndbulkpipe(kaweth->dev, 2), -+ private_header, -+ skb->len, -+ kaweth_usb_transmit_complete, -+ kaweth); -+ kaweth->end = 0; -+ -+ if((res = usb_submit_urb(kaweth->tx_urb, GFP_ATOMIC))) -+ { -+ dev_warn(&net->dev, "kaweth failed tx_urb %d\n", res); -+skip: -+ kaweth->stats.tx_errors++; -+ -+ netif_start_queue(net); -+ dev_kfree_skb_irq(skb); -+ } -+ else -+ { -+ kaweth->stats.tx_packets++; -+ kaweth->stats.tx_bytes += skb->len; -+ } -+ -+ spin_unlock_irq(&kaweth->device_lock); -+ -+ return NETDEV_TX_OK; -+} -+ -+/**************************************************************** -+ * kaweth_set_rx_mode -+ ****************************************************************/ -+static void kaweth_set_rx_mode(struct net_device *net) -+{ -+ struct kaweth_device *kaweth = netdev_priv(net); -+ -+ __u16 packet_filter_bitmap = KAWETH_PACKET_FILTER_DIRECTED | -+ KAWETH_PACKET_FILTER_BROADCAST | -+ KAWETH_PACKET_FILTER_MULTICAST; -+ -+ netdev_dbg(net, "Setting Rx mode to %d\n", packet_filter_bitmap); -+ -+ netif_stop_queue(net); -+ -+ if (net->flags & IFF_PROMISC) { -+ packet_filter_bitmap |= KAWETH_PACKET_FILTER_PROMISCUOUS; -+ } -+ else if (!netdev_mc_empty(net) || (net->flags & IFF_ALLMULTI)) { -+ packet_filter_bitmap |= KAWETH_PACKET_FILTER_ALL_MULTICAST; -+ } -+ -+ kaweth->packet_filter_bitmap = packet_filter_bitmap; -+ netif_wake_queue(net); -+} -+ -+/**************************************************************** -+ * kaweth_async_set_rx_mode -+ ****************************************************************/ -+static void kaweth_async_set_rx_mode(struct kaweth_device *kaweth) -+{ -+ int result; -+ __u16 packet_filter_bitmap = kaweth->packet_filter_bitmap; -+ -+ kaweth->packet_filter_bitmap = 0; -+ if (packet_filter_bitmap == 0) -+ return; -+ -+ if (in_interrupt()) -+ return; -+ -+ result = kaweth_control(kaweth, -+ usb_sndctrlpipe(kaweth->dev, 0), -+ KAWETH_COMMAND_SET_PACKET_FILTER, -+ USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, -+ packet_filter_bitmap, -+ 0, -+ (void *)&kaweth->scratch, -+ 0, -+ KAWETH_CONTROL_TIMEOUT); -+ -+ if(result < 0) { -+ dev_err(&kaweth->intf->dev, "Failed to set Rx mode: %d\n", -+ result); -+ } -+ else { -+ netdev_dbg(kaweth->net, "Set Rx mode to %d\n", -+ packet_filter_bitmap); -+ } -+} -+ -+/**************************************************************** -+ * kaweth_netdev_stats -+ ****************************************************************/ -+static struct net_device_stats *kaweth_netdev_stats(struct net_device *dev) -+{ -+ struct kaweth_device *kaweth = netdev_priv(dev); -+ return &kaweth->stats; -+} -+ -+/**************************************************************** -+ * kaweth_tx_timeout -+ ****************************************************************/ -+static void kaweth_tx_timeout(struct net_device *net) -+{ -+ struct kaweth_device *kaweth = netdev_priv(net); -+ -+ dev_warn(&net->dev, "%s: Tx timed out. Resetting.\n", net->name); -+ kaweth->stats.tx_errors++; -+ net->trans_start = jiffies; -+ -+ usb_unlink_urb(kaweth->tx_urb); -+} -+ -+/**************************************************************** -+ * kaweth_suspend -+ ****************************************************************/ -+static int kaweth_suspend(struct usb_interface *intf, pm_message_t message) -+{ -+ struct kaweth_device *kaweth = usb_get_intfdata(intf); -+ unsigned long flags; -+ -+ dev_dbg(&intf->dev, "Suspending device\n"); -+ spin_lock_irqsave(&kaweth->device_lock, flags); -+ kaweth->status |= KAWETH_STATUS_SUSPENDING; -+ spin_unlock_irqrestore(&kaweth->device_lock, flags); -+ -+ kaweth_kill_urbs(kaweth); -+ return 0; -+} -+ -+/**************************************************************** -+ * kaweth_resume -+ ****************************************************************/ -+static int kaweth_resume(struct usb_interface *intf) -+{ -+ struct kaweth_device *kaweth = usb_get_intfdata(intf); -+ unsigned long flags; -+ -+ dev_dbg(&intf->dev, "Resuming device\n"); -+ spin_lock_irqsave(&kaweth->device_lock, flags); -+ kaweth->status &= ~KAWETH_STATUS_SUSPENDING; -+ spin_unlock_irqrestore(&kaweth->device_lock, flags); -+ -+ if (!kaweth->opened) -+ return 0; -+ kaweth_resubmit_rx_urb(kaweth, GFP_NOIO); -+ kaweth_resubmit_int_urb(kaweth, GFP_NOIO); -+ -+ return 0; -+} -+ -+/**************************************************************** -+ * kaweth_probe -+ ****************************************************************/ -+ -+ -+static const struct net_device_ops kaweth_netdev_ops = { -+ .ndo_open = kaweth_open, -+ .ndo_stop = kaweth_close, -+ .ndo_start_xmit = kaweth_start_xmit, -+ .ndo_tx_timeout = kaweth_tx_timeout, -+ .ndo_set_rx_mode = kaweth_set_rx_mode, -+ .ndo_get_stats = kaweth_netdev_stats, -+ .ndo_change_mtu = eth_change_mtu, -+ .ndo_set_mac_address = eth_mac_addr, -+ .ndo_validate_addr = eth_validate_addr, -+}; -+ -+static int kaweth_probe( -+ struct usb_interface *intf, -+ const struct usb_device_id *id /* from id_table */ -+ ) -+{ -+ struct device *dev = &intf->dev; -+ struct usb_device *udev = interface_to_usbdev(intf); -+ struct kaweth_device *kaweth; -+ struct net_device *netdev; -+ const eth_addr_t bcast_addr = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; -+ int result = 0; -+ -+ dev_dbg(dev, -+ "Kawasaki Device Probe (Device number:%d): 0x%4.4x:0x%4.4x:0x%4.4x\n", -+ udev->devnum, le16_to_cpu(udev->descriptor.idVendor), -+ le16_to_cpu(udev->descriptor.idProduct), -+ le16_to_cpu(udev->descriptor.bcdDevice)); -+ -+ dev_dbg(dev, "Device at %p\n", udev); -+ -+ dev_dbg(dev, "Descriptor length: %x type: %x\n", -+ (int)udev->descriptor.bLength, -+ (int)udev->descriptor.bDescriptorType); -+ -+ netdev = alloc_etherdev(sizeof(*kaweth)); -+ if (!netdev) -+ return -ENOMEM; -+ -+ kaweth = netdev_priv(netdev); -+ kaweth->dev = udev; -+ kaweth->net = netdev; -+ -+ spin_lock_init(&kaweth->device_lock); -+ init_waitqueue_head(&kaweth->term_wait); -+ -+ dev_dbg(dev, "Resetting.\n"); -+ -+ kaweth_reset(kaweth); -+ -+ /* -+ * If high byte of bcdDevice is nonzero, firmware is already -+ * downloaded. Don't try to do it again, or we'll hang the device. -+ */ -+ -+ if (le16_to_cpu(udev->descriptor.bcdDevice) >> 8) { -+ dev_info(dev, "Firmware present in device.\n"); -+ } else { -+ /* Download the firmware */ -+ dev_info(dev, "Downloading firmware...\n"); -+ kaweth->firmware_buf = (__u8 *)__get_free_page(GFP_KERNEL); -+ if ((result = kaweth_download_firmware(kaweth, -+ "kaweth/new_code.bin", -+ 100, -+ 2)) < 0) { -+ dev_err(dev, "Error downloading firmware (%d)\n", -+ result); -+ goto err_fw; -+ } -+ -+ if ((result = kaweth_download_firmware(kaweth, -+ "kaweth/new_code_fix.bin", -+ 100, -+ 3)) < 0) { -+ dev_err(dev, "Error downloading firmware fix (%d)\n", -+ result); -+ goto err_fw; -+ } -+ -+ if ((result = kaweth_download_firmware(kaweth, -+ "kaweth/trigger_code.bin", -+ 126, -+ 2)) < 0) { -+ dev_err(dev, "Error downloading trigger code (%d)\n", -+ result); -+ goto err_fw; -+ -+ } -+ -+ if ((result = kaweth_download_firmware(kaweth, -+ "kaweth/trigger_code_fix.bin", -+ 126, -+ 3)) < 0) { -+ dev_err(dev, "Error downloading trigger code fix (%d)\n", result); -+ goto err_fw; -+ } -+ -+ -+ if ((result = kaweth_trigger_firmware(kaweth, 126)) < 0) { -+ dev_err(dev, "Error triggering firmware (%d)\n", result); -+ goto err_fw; -+ } -+ -+ /* Device will now disappear for a moment... */ -+ dev_info(dev, "Firmware loaded. I'll be back...\n"); -+err_fw: -+ free_page((unsigned long)kaweth->firmware_buf); -+ free_netdev(netdev); -+ return -EIO; -+ } -+ -+ result = kaweth_read_configuration(kaweth); -+ -+ if(result < 0) { -+ dev_err(dev, "Error reading configuration (%d), no net device created\n", result); -+ goto err_free_netdev; -+ } -+ -+ dev_info(dev, "Statistics collection: %x\n", kaweth->configuration.statistics_mask); -+ dev_info(dev, "Multicast filter limit: %x\n", kaweth->configuration.max_multicast_filters & ((1 << 15) - 1)); -+ dev_info(dev, "MTU: %d\n", le16_to_cpu(kaweth->configuration.segment_size)); -+ dev_info(dev, "Read MAC address %pM\n", kaweth->configuration.hw_addr); -+ -+ if(!memcmp(&kaweth->configuration.hw_addr, -+ &bcast_addr, -+ sizeof(bcast_addr))) { -+ dev_err(dev, "Firmware not functioning properly, no net device created\n"); -+ goto err_free_netdev; -+ } -+ -+ if(kaweth_set_urb_size(kaweth, KAWETH_BUF_SIZE) < 0) { -+ dev_dbg(dev, "Error setting URB size\n"); -+ goto err_free_netdev; -+ } -+ -+ if(kaweth_set_sofs_wait(kaweth, KAWETH_SOFS_TO_WAIT) < 0) { -+ dev_err(dev, "Error setting SOFS wait\n"); -+ goto err_free_netdev; -+ } -+ -+ result = kaweth_set_receive_filter(kaweth, -+ KAWETH_PACKET_FILTER_DIRECTED | -+ KAWETH_PACKET_FILTER_BROADCAST | -+ KAWETH_PACKET_FILTER_MULTICAST); -+ -+ if(result < 0) { -+ dev_err(dev, "Error setting receive filter\n"); -+ goto err_free_netdev; -+ } -+ -+ dev_dbg(dev, "Initializing net device.\n"); -+ -+ kaweth->intf = intf; -+ -+ kaweth->tx_urb = usb_alloc_urb(0, GFP_KERNEL); -+ if (!kaweth->tx_urb) -+ goto err_free_netdev; -+ kaweth->rx_urb = usb_alloc_urb(0, GFP_KERNEL); -+ if (!kaweth->rx_urb) -+ goto err_only_tx; -+ kaweth->irq_urb = usb_alloc_urb(0, GFP_KERNEL); -+ if (!kaweth->irq_urb) -+ goto err_tx_and_rx; -+ -+ kaweth->intbuffer = usb_alloc_coherent( kaweth->dev, -+ INTBUFFERSIZE, -+ GFP_KERNEL, -+ &kaweth->intbufferhandle); -+ if (!kaweth->intbuffer) -+ goto err_tx_and_rx_and_irq; -+ kaweth->rx_buf = usb_alloc_coherent( kaweth->dev, -+ KAWETH_BUF_SIZE, -+ GFP_KERNEL, -+ &kaweth->rxbufferhandle); -+ if (!kaweth->rx_buf) -+ goto err_all_but_rxbuf; -+ -+ memcpy(netdev->broadcast, &bcast_addr, sizeof(bcast_addr)); -+ memcpy(netdev->dev_addr, &kaweth->configuration.hw_addr, -+ sizeof(kaweth->configuration.hw_addr)); -+ -+ netdev->netdev_ops = &kaweth_netdev_ops; -+ netdev->watchdog_timeo = KAWETH_TX_TIMEOUT; -+ netdev->mtu = le16_to_cpu(kaweth->configuration.segment_size); -+ netdev->ethtool_ops = &ops; -+ -+ /* kaweth is zeroed as part of alloc_netdev */ -+ INIT_DELAYED_WORK(&kaweth->lowmem_work, kaweth_resubmit_tl); -+ usb_set_intfdata(intf, kaweth); -+ -+#if 0 -+// dma_supported() is deeply broken on almost all architectures -+ if (dma_supported (dev, 0xffffffffffffffffULL)) -+ kaweth->net->features |= NETIF_F_HIGHDMA; -+#endif -+ -+ SET_NETDEV_DEV(netdev, dev); -+ if (register_netdev(netdev) != 0) { -+ dev_err(dev, "Error registering netdev.\n"); -+ goto err_intfdata; -+ } -+ -+ dev_info(dev, "kaweth interface created at %s\n", -+ kaweth->net->name); -+ -+ dev_dbg(dev, "Kaweth probe returning.\n"); -+ -+ return 0; -+ -+err_intfdata: -+ usb_set_intfdata(intf, NULL); -+ usb_free_coherent(kaweth->dev, KAWETH_BUF_SIZE, (void *)kaweth->rx_buf, kaweth->rxbufferhandle); -+err_all_but_rxbuf: -+ usb_free_coherent(kaweth->dev, INTBUFFERSIZE, (void *)kaweth->intbuffer, kaweth->intbufferhandle); -+err_tx_and_rx_and_irq: -+ usb_free_urb(kaweth->irq_urb); -+err_tx_and_rx: -+ usb_free_urb(kaweth->rx_urb); -+err_only_tx: -+ usb_free_urb(kaweth->tx_urb); -+err_free_netdev: -+ free_netdev(netdev); -+ -+ return -EIO; -+} -+ -+/**************************************************************** -+ * kaweth_disconnect -+ ****************************************************************/ -+static void kaweth_disconnect(struct usb_interface *intf) -+{ -+ struct kaweth_device *kaweth = usb_get_intfdata(intf); -+ struct net_device *netdev; -+ -+ dev_info(&intf->dev, "Unregistering\n"); -+ -+ usb_set_intfdata(intf, NULL); -+ if (!kaweth) { -+ dev_warn(&intf->dev, "unregistering non-existent device\n"); -+ return; -+ } -+ netdev = kaweth->net; -+ -+ netdev_dbg(kaweth->net, "Unregistering net device\n"); -+ unregister_netdev(netdev); -+ -+ usb_free_urb(kaweth->rx_urb); -+ usb_free_urb(kaweth->tx_urb); -+ usb_free_urb(kaweth->irq_urb); -+ -+ usb_free_coherent(kaweth->dev, KAWETH_BUF_SIZE, (void *)kaweth->rx_buf, kaweth->rxbufferhandle); -+ usb_free_coherent(kaweth->dev, INTBUFFERSIZE, (void *)kaweth->intbuffer, kaweth->intbufferhandle); -+ -+ free_netdev(netdev); -+} -+ -+ -+// FIXME this completion stuff is a modified clone of -+// an OLD version of some stuff in usb.c ... -+struct usb_api_data { -+ wait_queue_head_t wqh; -+ int done; -+}; -+ -+/*-------------------------------------------------------------------* -+ * completion handler for compatibility wrappers (sync control/bulk) * -+ *-------------------------------------------------------------------*/ -+static void usb_api_blocking_completion(struct urb *urb) -+{ -+ struct usb_api_data *awd = (struct usb_api_data *)urb->context; -+ -+ awd->done=1; -+ wake_up(&awd->wqh); -+} -+ -+/*-------------------------------------------------------------------* -+ * COMPATIBILITY STUFF * -+ *-------------------------------------------------------------------*/ -+ -+// Starts urb and waits for completion or timeout -+static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length) -+{ -+ struct usb_api_data awd; -+ int status; -+ -+ init_waitqueue_head(&awd.wqh); -+ awd.done = 0; -+ -+ urb->context = &awd; -+ status = usb_submit_urb(urb, GFP_ATOMIC); -+ if (status) { -+ // something went wrong -+ usb_free_urb(urb); -+ return status; -+ } -+ -+ if (!wait_event_timeout(awd.wqh, awd.done, timeout)) { -+ // timeout -+ dev_warn(&urb->dev->dev, "usb_control/bulk_msg: timeout\n"); -+ usb_kill_urb(urb); // remove urb safely -+ status = -ETIMEDOUT; -+ } -+ else { -+ status = urb->status; -+ } -+ -+ if (actual_length) { -+ *actual_length = urb->actual_length; -+ } -+ -+ usb_free_urb(urb); -+ return status; -+} -+ -+/*-------------------------------------------------------------------*/ -+// returns status (negative) or length (positive) -+static int kaweth_internal_control_msg(struct usb_device *usb_dev, -+ unsigned int pipe, -+ struct usb_ctrlrequest *cmd, void *data, -+ int len, int timeout) -+{ -+ struct urb *urb; -+ int retv; -+ int length = 0; /* shut up GCC */ -+ -+ urb = usb_alloc_urb(0, GFP_ATOMIC); -+ if (!urb) -+ return -ENOMEM; -+ -+ usb_fill_control_urb(urb, usb_dev, pipe, (unsigned char*)cmd, data, -+ len, usb_api_blocking_completion, NULL); -+ -+ retv = usb_start_wait_urb(urb, timeout, &length); -+ if (retv < 0) { -+ return retv; -+ } -+ else { -+ return length; -+ } -+} -+ -+module_usb_driver(kaweth_driver); -diff -Naur backports-4.2.6-1.org/drivers/net/usb/Kconfig backports-4.2.6-1/drivers/net/usb/Kconfig ---- backports-4.2.6-1.org/drivers/net/usb/Kconfig 2015-11-15 22:19:40.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/Kconfig 2016-06-28 14:35:17.991973886 +0200 -@@ -13,7 +13,6 @@ - if USB_NET_DRIVERS - - config USB_CATC -- depends on n - tristate "USB CATC NetMate-based Ethernet device support" - depends on m - depends on CRC32 -@@ -34,7 +33,6 @@ - module will be called catc. - - config USB_KAWETH -- depends on n - tristate "USB KLSI KL5USB101-based ethernet device support" - depends on m - ---help--- -@@ -75,7 +73,6 @@ - module will be called kaweth. - - config USB_PEGASUS -- depends on n - tristate "USB Pegasus/Pegasus-II based ethernet device support" - depends on m - select BPAUTO_MII -@@ -92,7 +89,6 @@ - module will be called pegasus. - - config USB_RTL8150 -- depends on n - tristate "USB RTL8150 based ethernet device support" - depends on m - select BPAUTO_MII -@@ -105,7 +101,6 @@ - module will be called rtl8150. - - config USB_RTL8152 -- depends on n - tristate "Realtek RTL8152/RTL8153 Based USB Ethernet Adapters" - depends on m - select BPAUTO_MII -@@ -153,7 +148,6 @@ - module will be called usbnet. - - config USB_NET_AX8817X -- depends on n - tristate "ASIX AX88xxx Based USB 2.0 Ethernet Adapters" - depends on m - depends on USB_USBNET -@@ -184,7 +178,6 @@ - what other networking devices you have in use. - - config USB_NET_AX88179_178A -- depends on n - tristate "ASIX AX88179/178A USB 3.0/2.0 to Gigabit Ethernet" - depends on m - depends on USB_USBNET -@@ -233,7 +226,6 @@ - name is used instead. - - config USB_NET_CDC_EEM -- depends on n - tristate "CDC EEM support" - depends on m - depends on USB_USBNET -@@ -269,7 +261,6 @@ - * Ericsson F5521gw Mobile Broadband Module - - config USB_NET_HUAWEI_CDC_NCM -- depends on n - tristate "Huawei NCM embedded AT channel support" - depends on m - depends on USB_USBNET -@@ -305,7 +296,6 @@ - module will be called cdc_mbim. - - config USB_NET_DM9601 -- depends on n - tristate "Davicom DM96xx based USB 10/100 ethernet devices" - depends on m - depends on USB_USBNET -@@ -315,7 +305,6 @@ - based USB 10/100 Ethernet adapters. - - config USB_NET_SR9700 -- depends on n - tristate "CoreChip-sz SR9700 based USB 1.1 10/100 ethernet devices" - depends on m - depends on USB_USBNET -@@ -325,7 +314,6 @@ - 10/100 Ethernet adapters. - - config USB_NET_SR9800 -- depends on n - tristate "CoreChip-sz SR9800 based USB 2.0 10/100 ethernet devices" - depends on m - depends on USB_USBNET -@@ -342,7 +330,6 @@ - module will be called sr9800. - - config USB_NET_SMSC75XX -- depends on n - tristate "SMSC LAN75XX based USB 2.0 gigabit ethernet devices" - depends on m - depends on USB_USBNET -@@ -354,7 +341,6 @@ - Gigabit Ethernet adapters. - - config USB_NET_SMSC95XX -- depends on n - tristate "SMSC LAN95XX based USB 2.0 10/100 ethernet devices" - depends on m - depends on USB_USBNET -@@ -366,7 +352,6 @@ - 10/100 Ethernet adapters. - - config USB_NET_GL620A -- depends on n - tristate "GeneSys GL620USB-A based cables" - depends on m - depends on USB_USBNET -@@ -377,7 +362,6 @@ - Note that the half-duplex "GL620USB" is not supported. - - config USB_NET_NET1080 -- depends on n - tristate "NetChip 1080 based cables (Laplink, ...)" - depends on m - default y -@@ -388,7 +372,6 @@ - optionally with LEDs that indicate traffic - - config USB_NET_PLUSB -- depends on n - tristate "Prolific PL-2301/2302/25A1 based cables" - depends on m - # if the handshake/init/reset problems, from original 'plusb', -@@ -399,7 +382,6 @@ - with one of these chips. - - config USB_NET_MCS7830 -- depends on n - tristate "MosChip MCS7830 based Ethernet adapters" - depends on m - depends on USB_USBNET -@@ -425,7 +407,6 @@ - (and for) Microsoft; it isn't an "Open" ecosystem or market. - - config USB_NET_CDC_SUBSET -- depends on n - tristate "Simple USB Network Links (CDC Ethernet subset)" - depends on m - depends on USB_USBNET -@@ -497,7 +478,6 @@ - with one of these chips. - - config USB_NET_ZAURUS -- depends on n - tristate "Sharp Zaurus (stock ROMs) and compatible" - depends on m - depends on USB_USBNET -@@ -517,7 +497,6 @@ - some cases CDC MDLM) protocol, not "g_ether". - - config USB_NET_CX82310_ETH -- depends on n - tristate "Conexant CX82310 USB ethernet port" - depends on m - depends on USB_USBNET -@@ -527,7 +506,6 @@ - it will not work with ADSL modems (use cxacru driver instead). - - config USB_NET_KALMIA -- depends on n - tristate "Samsung Kalmia based LTE USB modem" - depends on m - depends on USB_USBNET -@@ -562,7 +540,6 @@ - module will be called qmi_wwan. - - config USB_HSO -- depends on n - tristate "Option USB High Speed Mobile Devices" - depends on m - depends on USB && RFKILL && TTY -@@ -575,7 +552,6 @@ - module will be called hso. - - config USB_NET_INT51X1 -- depends on n - tristate "Intellon PLC based usb adapter" - depends on m - depends on USB_USBNET -@@ -585,7 +561,6 @@ - INT51x1/INT5200 chip, like the "devolo dLan duo". - - config USB_CDC_PHONET -- depends on n - tristate "CDC Phonet support" - depends on m - depends on PHONET -@@ -595,7 +570,6 @@ - "PC suite" USB profile. - - config USB_IPHETH -- depends on n - tristate "Apple iPhone USB Ethernet driver" - depends on m - default n -@@ -619,11 +593,10 @@ - module will be called sierra_net. - - config USB_VL600 -- depends on n - tristate "LG VL600 modem dongle" - depends on m - depends on USB_NET_CDCETHER && TTY -- select USB_ACM -+# depends on USB_ACM - help - Select this if you want to use an LG Electronics 4G/LTE usb modem - called VL600. This driver only handles the ethernet -diff -Naur backports-4.2.6-1.org/drivers/net/usb/Kconfig.orig backports-4.2.6-1/drivers/net/usb/Kconfig.orig ---- backports-4.2.6-1.org/drivers/net/usb/Kconfig.orig 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/Kconfig.orig 2016-06-28 14:35:17.991973886 +0200 -@@ -0,0 +1,638 @@ -+# -+# USB Network devices configuration -+# -+comment "Host-side USB support is needed for USB Network Adapter support" -+ depends on !USB && NET -+ -+menuconfig USB_NET_DRIVERS -+ tristate "USB Network Adapters" -+ depends on m -+ default USB if USB -+ depends on USB && NET -+ -+if USB_NET_DRIVERS -+ -+config USB_CATC -+ depends on n -+ tristate "USB CATC NetMate-based Ethernet device support" -+ depends on m -+ depends on CRC32 -+ ---help--- -+ Say Y if you want to use one of the following 10Mbps USB Ethernet -+ device based on the EL1210A chip. Supported devices are: -+ Belkin F5U011 -+ Belkin F5U111 -+ CATC NetMate -+ CATC NetMate II -+ smartBridges smartNIC -+ -+ This driver makes the adapter appear as a normal Ethernet interface, -+ typically on eth0, if it is the only ethernet device, or perhaps on -+ eth1, if you have a PCI or ISA ethernet card installed. -+ -+ To compile this driver as a module, choose M here: the -+ module will be called catc. -+ -+config USB_KAWETH -+ depends on n -+ tristate "USB KLSI KL5USB101-based ethernet device support" -+ depends on m -+ ---help--- -+ Say Y here if you want to use one of the following 10Mbps only -+ USB Ethernet adapters based on the KLSI KL5KUSB101B chipset: -+ 3Com 3C19250 -+ ADS USB-10BT -+ ATEN USB Ethernet -+ ASANTE USB To Ethernet Adapter -+ AOX Endpoints USB Ethernet -+ Correga K.K. -+ D-Link DSB-650C and DU-E10 -+ Entrega / Portgear E45 -+ I-O DATA USB-ET/T -+ Jaton USB Ethernet Device Adapter -+ Kingston Technology USB Ethernet Adapter -+ Linksys USB10T -+ Mobility USB-Ethernet Adapter -+ NetGear EA-101 -+ Peracom Enet and Enet2 -+ Portsmith Express Ethernet Adapter -+ Shark Pocket Adapter -+ SMC 2202USB -+ Sony Vaio port extender -+ -+ This driver is likely to work with most 10Mbps only USB Ethernet -+ adapters, including some "no brand" devices. It does NOT work on -+ SmartBridges smartNIC or on Belkin F5U111 devices - you should use -+ the CATC NetMate driver for those. If you are not sure which one -+ you need, select both, and the correct one should be selected for -+ you. -+ -+ This driver makes the adapter appear as a normal Ethernet interface, -+ typically on eth0, if it is the only ethernet device, or perhaps on -+ eth1, if you have a PCI or ISA ethernet card installed. -+ -+ To compile this driver as a module, choose M here: the -+ module will be called kaweth. -+ -+config USB_PEGASUS -+ depends on n -+ tristate "USB Pegasus/Pegasus-II based ethernet device support" -+ depends on m -+ select BPAUTO_MII -+ ---help--- -+ Say Y here if you know you have Pegasus or Pegasus-II based adapter. -+ If in doubt then look at for the -+ complete list of supported devices. -+ -+ If your particular adapter is not in the list and you are _sure_ it -+ is Pegasus or Pegasus II based then send me -+ vendor and device IDs. -+ -+ To compile this driver as a module, choose M here: the -+ module will be called pegasus. -+ -+config USB_RTL8150 -+ depends on n -+ tristate "USB RTL8150 based ethernet device support" -+ depends on m -+ select BPAUTO_MII -+ help -+ Say Y here if you have RTL8150 based usb-ethernet adapter. -+ Send me any comments you may have. -+ You can also check for updates at . -+ -+ To compile this driver as a module, choose M here: the -+ module will be called rtl8150. -+ -+config USB_RTL8152 -+ depends on n -+ tristate "Realtek RTL8152/RTL8153 Based USB Ethernet Adapters" -+ depends on m -+ select BPAUTO_MII -+ help -+ This option adds support for Realtek RTL8152 based USB 2.0 -+ 10/100 Ethernet adapters and RTL8153 based USB 3.0 10/100/1000 -+ Ethernet adapters. -+ -+ To compile this driver as a module, choose M here: the -+ module will be called r8152. -+ -+config USB_USBNET -+ tristate "Multi-purpose USB Networking Framework" -+ depends on m -+ select BPAUTO_MII -+ ---help--- -+ This driver supports several kinds of network links over USB, -+ with "minidrivers" built around a common network driver core -+ that supports deep queues for efficient transfers. (This gives -+ better performance with small packets and at high speeds). -+ -+ The USB host runs "usbnet", and the other end of the link might be: -+ -+ - Another USB host, when using USB "network" or "data transfer" -+ cables. These are often used to network laptops to PCs, like -+ "Laplink" parallel cables or some motherboards. These rely -+ on specialized chips from many suppliers. -+ -+ - An intelligent USB gadget, perhaps embedding a Linux system. -+ These include PDAs running Linux (iPaq, Yopy, Zaurus, and -+ others), and devices that interoperate using the standard -+ CDC-Ethernet specification (including many cable modems). -+ -+ - Network adapter hardware (like those for 10/100 Ethernet) which -+ uses this driver framework. -+ -+ The link will appear with a name like "usb0", when the link is -+ a two-node link, or "eth0" for most CDC-Ethernet devices. Those -+ two-node links are most easily managed with Ethernet Bridging -+ (CONFIG_BRIDGE) instead of routing. -+ -+ For more information see . -+ -+ To compile this driver as a module, choose M here: the -+ module will be called usbnet. -+ -+config USB_NET_AX8817X -+ depends on n -+ tristate "ASIX AX88xxx Based USB 2.0 Ethernet Adapters" -+ depends on m -+ depends on USB_USBNET -+ depends on CRC32 -+ depends on PHYLIB -+ default y -+ help -+ This option adds support for ASIX AX88xxx based USB 2.0 -+ 10/100 Ethernet adapters. -+ -+ This driver should work with at least the following devices: -+ * Aten UC210T -+ * ASIX AX88172 -+ * Billionton Systems, USB2AR -+ * Buffalo LUA-U2-KTX -+ * Corega FEther USB2-TX -+ * D-Link DUB-E100 -+ * Hawking UF200 -+ * Linksys USB200M -+ * Netgear FA120 -+ * Sitecom LN-029 -+ * Sitecom LN-028 -+ * Intellinet USB 2.0 Ethernet -+ * ST Lab USB 2.0 Ethernet -+ * TrendNet TU2-ET100 -+ -+ This driver creates an interface named "ethX", where X depends on -+ what other networking devices you have in use. -+ -+config USB_NET_AX88179_178A -+ depends on n -+ tristate "ASIX AX88179/178A USB 3.0/2.0 to Gigabit Ethernet" -+ depends on m -+ depends on USB_USBNET -+ depends on CRC32 -+ depends on PHYLIB -+ default y -+ help -+ This option adds support for ASIX AX88179 based USB 3.0/2.0 -+ to Gigabit Ethernet adapters. -+ -+ This driver should work with at least the following devices: -+ * ASIX AX88179 -+ * ASIX AX88178A -+ * Sitcomm LN-032 -+ -+ This driver creates an interface named "ethX", where X depends on -+ what other networking devices you have in use. -+ -+config USB_NET_CDCETHER -+ tristate "CDC Ethernet support (smart devices such as cable modems)" -+ depends on m -+ depends on USB_USBNET -+ default y -+ help -+ This option supports devices conforming to the Communication Device -+ Class (CDC) Ethernet Control Model, a specification that's easy to -+ implement in device firmware. The CDC specifications are available -+ from . -+ -+ CDC Ethernet is an implementation option for DOCSIS cable modems -+ that support USB connectivity, used for non-Microsoft USB hosts. -+ The Linux-USB CDC Ethernet Gadget driver is an open implementation. -+ This driver should work with at least the following devices: -+ -+ * Dell Wireless 5530 HSPA -+ * Ericsson PipeRider (all variants) -+ * Ericsson Mobile Broadband Module (all variants) -+ * Motorola (DM100 and SB4100) -+ * Broadcom Cable Modem (reference design) -+ * Toshiba (PCX1100U and F3507g/F3607gw) -+ * ... -+ -+ This driver creates an interface named "ethX", where X depends on -+ what other networking devices you have in use. However, if the -+ IEEE 802 "local assignment" bit is set in the address, a "usbX" -+ name is used instead. -+ -+config USB_NET_CDC_EEM -+ depends on n -+ tristate "CDC EEM support" -+ depends on m -+ depends on USB_USBNET -+ help -+ This option supports devices conforming to the Communication Device -+ Class (CDC) Ethernet Emulation Model, a specification that's easy to -+ implement in device firmware. The CDC EEM specifications are available -+ from . -+ -+ This driver creates an interface named "ethX", where X depends on -+ what other networking devices you have in use. However, if the -+ IEEE 802 "local assignment" bit is set in the address, a "usbX" -+ name is used instead. -+ -+config USB_NET_CDC_NCM -+ tristate "CDC NCM support" -+ depends on m -+ depends on USB_USBNET -+ default y -+ help -+ This driver provides support for CDC NCM (Network Control Model -+ Device USB Class Specification). The CDC NCM specification is -+ available from . -+ -+ Say "y" to link the driver statically, or "m" to build a -+ dynamically linked module. -+ -+ This driver should work with at least the following devices: -+ * ST-Ericsson M700 LTE FDD/TDD Mobile Broadband Modem (ref. design) -+ * ST-Ericsson M5730 HSPA+ Mobile Broadband Modem (reference design) -+ * ST-Ericsson M570 HSPA+ Mobile Broadband Modem (reference design) -+ * ST-Ericsson M343 HSPA Mobile Broadband Modem (reference design) -+ * Ericsson F5521gw Mobile Broadband Module -+ -+config USB_NET_HUAWEI_CDC_NCM -+ depends on n -+ tristate "Huawei NCM embedded AT channel support" -+ depends on m -+ depends on USB_USBNET -+ select USB_WDM -+ select USB_NET_CDC_NCM -+ help -+ This driver supports huawei-style NCM devices, that use NCM as a -+ transport for other protocols, usually an embedded AT channel. -+ Good examples are: -+ * Huawei E3131 -+ * Huawei E3251 -+ -+ To compile this driver as a module, choose M here: the module will be -+ called huawei_cdc_ncm.ko. -+ -+config USB_NET_CDC_MBIM -+ tristate "CDC MBIM support" -+ depends on m -+ depends on USB_USBNET -+ select USB_WDM -+ select USB_NET_CDC_NCM -+ help -+ This driver provides support for CDC MBIM (Mobile Broadband -+ Interface Model) devices. The CDC MBIM specification is -+ available from . -+ -+ MBIM devices require configuration using the management -+ protocol defined by the MBIM specification. This driver -+ provides unfiltered access to the MBIM control channel -+ through the associated /dev/cdc-wdmx character device. -+ -+ To compile this driver as a module, choose M here: the -+ module will be called cdc_mbim. -+ -+config USB_NET_DM9601 -+ depends on n -+ tristate "Davicom DM96xx based USB 10/100 ethernet devices" -+ depends on m -+ depends on USB_USBNET -+ depends on CRC32 -+ help -+ This option adds support for Davicom DM9601/DM9620/DM9621A -+ based USB 10/100 Ethernet adapters. -+ -+config USB_NET_SR9700 -+ depends on n -+ tristate "CoreChip-sz SR9700 based USB 1.1 10/100 ethernet devices" -+ depends on m -+ depends on USB_USBNET -+ depends on CRC32 -+ help -+ This option adds support for CoreChip-sz SR9700 based USB 1.1 -+ 10/100 Ethernet adapters. -+ -+config USB_NET_SR9800 -+ depends on n -+ tristate "CoreChip-sz SR9800 based USB 2.0 10/100 ethernet devices" -+ depends on m -+ depends on USB_USBNET -+ depends on CRC32 -+ ---help--- -+ Say Y if you want to use one of the following 100Mbps USB Ethernet -+ device based on the CoreChip-sz SR9800 chip. -+ -+ This driver makes the adapter appear as a normal Ethernet interface, -+ typically on eth0, if it is the only ethernet device, or perhaps on -+ eth1, if you have a PCI or ISA ethernet card installed. -+ -+ To compile this driver as a module, choose M here: the -+ module will be called sr9800. -+ -+config USB_NET_SMSC75XX -+ depends on n -+ tristate "SMSC LAN75XX based USB 2.0 gigabit ethernet devices" -+ depends on m -+ depends on USB_USBNET -+ depends on BITREVERSE -+ depends on CRC16 -+ depends on CRC32 -+ help -+ This option adds support for SMSC LAN75XX based USB 2.0 -+ Gigabit Ethernet adapters. -+ -+config USB_NET_SMSC95XX -+ depends on n -+ tristate "SMSC LAN95XX based USB 2.0 10/100 ethernet devices" -+ depends on m -+ depends on USB_USBNET -+ depends on BITREVERSE -+ depends on CRC16 -+ depends on CRC32 -+ help -+ This option adds support for SMSC LAN95XX based USB 2.0 -+ 10/100 Ethernet adapters. -+ -+config USB_NET_GL620A -+ depends on n -+ tristate "GeneSys GL620USB-A based cables" -+ depends on m -+ depends on USB_USBNET -+ help -+ Choose this option if you're using a host-to-host cable, -+ or PC2PC motherboard, with this chip. -+ -+ Note that the half-duplex "GL620USB" is not supported. -+ -+config USB_NET_NET1080 -+ depends on n -+ tristate "NetChip 1080 based cables (Laplink, ...)" -+ depends on m -+ default y -+ depends on USB_USBNET -+ help -+ Choose this option if you're using a host-to-host cable based -+ on this design: one NetChip 1080 chip and supporting logic, -+ optionally with LEDs that indicate traffic -+ -+config USB_NET_PLUSB -+ depends on n -+ tristate "Prolific PL-2301/2302/25A1 based cables" -+ depends on m -+ # if the handshake/init/reset problems, from original 'plusb', -+ # are ever resolved ... then remove "experimental" -+ depends on USB_USBNET -+ help -+ Choose this option if you're using a host-to-host cable -+ with one of these chips. -+ -+config USB_NET_MCS7830 -+ depends on n -+ tristate "MosChip MCS7830 based Ethernet adapters" -+ depends on m -+ depends on USB_USBNET -+ help -+ Choose this option if you're using a 10/100 Ethernet USB2 -+ adapter based on the MosChip 7830 controller. This includes -+ adapters marketed under the DeLOCK brand. -+ -+config USB_NET_RNDIS_HOST -+ tristate "Host for RNDIS and ActiveSync devices" -+ depends on m -+ depends on USB_USBNET -+ select USB_NET_CDCETHER -+ help -+ This option enables hosting "Remote NDIS" USB networking links, -+ as encouraged by Microsoft (instead of CDC Ethernet!) for use in -+ various devices that may only support this protocol. A variant -+ of this protocol (with even less public documentation) seems to -+ be at the root of Microsoft's "ActiveSync" too. -+ -+ Avoid using this protocol unless you have no better options. -+ The protocol specification is incomplete, and is controlled by -+ (and for) Microsoft; it isn't an "Open" ecosystem or market. -+ -+config USB_NET_CDC_SUBSET -+ depends on n -+ tristate "Simple USB Network Links (CDC Ethernet subset)" -+ depends on m -+ depends on USB_USBNET -+ default y -+ help -+ This driver module supports USB network devices that can work -+ without any device-specific information. Select it if you have -+ one of these drivers. -+ -+ Note that while many USB host-to-host cables can work in this mode, -+ that may mean not being able to talk to Win32 systems or more -+ commonly not being able to handle certain events (like replugging -+ the host on the other end) very well. Also, these devices will -+ not generally have permanently assigned Ethernet addresses. -+ -+config USB_ALI_M5632 -+ bool "ALi M5632 based 'USB 2.0 Data Link' cables" -+ depends on USB_NET_CDC_SUBSET -+ help -+ Choose this option if you're using a host-to-host cable -+ based on this design, which supports USB 2.0 high speed. -+ -+config USB_AN2720 -+ bool "AnchorChips 2720 based cables (Xircom PGUNET, ...)" -+ depends on USB_NET_CDC_SUBSET -+ help -+ Choose this option if you're using a host-to-host cable -+ based on this design. Note that AnchorChips is now a -+ Cypress brand. -+ -+config USB_BELKIN -+ bool "eTEK based host-to-host cables (Advance, Belkin, ...)" -+ depends on USB_NET_CDC_SUBSET -+ default y -+ help -+ Choose this option if you're using a host-to-host cable -+ based on this design: two NetChip 2890 chips and an Atmel -+ microcontroller, with LEDs that indicate traffic. -+ -+config USB_ARMLINUX -+ bool "Embedded ARM Linux links (iPaq, ...)" -+ depends on USB_NET_CDC_SUBSET -+ default y -+ help -+ Choose this option to support the "usb-eth" networking driver -+ used by most of the ARM Linux community with device controllers -+ such as the SA-11x0 and PXA-25x UDCs, or the tftp capabilities -+ in some PXA versions of the "blob" boot loader. -+ -+ Linux-based "Gumstix" PXA-25x based systems use this protocol -+ to talk with other Linux systems. -+ -+ Although the ROMs shipped with Sharp Zaurus products use a -+ different link level framing protocol, you can have them use -+ this simpler protocol by installing a different kernel. -+ -+config USB_EPSON2888 -+ bool "Epson 2888 based firmware (DEVELOPMENT)" -+ depends on USB_NET_CDC_SUBSET -+ help -+ Choose this option to support the usb networking links used -+ by some sample firmware from Epson. -+ -+config USB_KC2190 -+ bool "KT Technology KC2190 based cables (InstaNet)" -+ depends on USB_NET_CDC_SUBSET -+ help -+ Choose this option if you're using a host-to-host cable -+ with one of these chips. -+ -+config USB_NET_ZAURUS -+ depends on n -+ tristate "Sharp Zaurus (stock ROMs) and compatible" -+ depends on m -+ depends on USB_USBNET -+ select USB_NET_CDCETHER -+ depends on CRC32 -+ default y -+ help -+ Choose this option to support the usb networking links used by -+ Zaurus models like the SL-5000D, SL-5500, SL-5600, A-300, B-500. -+ This also supports some related device firmware, as used in some -+ PDAs from Olympus and some cell phones from Motorola. -+ -+ If you install an alternate image, such as the Linux 2.6 based -+ versions of OpenZaurus, you should no longer need to support this -+ protocol. Only the "eth-fd" or "net_fd" drivers in these devices -+ really need this non-conformant variant of CDC Ethernet (or in -+ some cases CDC MDLM) protocol, not "g_ether". -+ -+config USB_NET_CX82310_ETH -+ depends on n -+ tristate "Conexant CX82310 USB ethernet port" -+ depends on m -+ depends on USB_USBNET -+ help -+ Choose this option if you're using a Conexant CX82310-based ADSL -+ router with USB ethernet port. This driver is for routers only, -+ it will not work with ADSL modems (use cxacru driver instead). -+ -+config USB_NET_KALMIA -+ depends on n -+ tristate "Samsung Kalmia based LTE USB modem" -+ depends on m -+ depends on USB_USBNET -+ help -+ Choose this option if you have a Samsung Kalmia based USB modem -+ as Samsung GT-B3730. -+ -+ To compile this driver as a module, choose M here: the -+ module will be called kalmia. -+ -+config USB_NET_QMI_WWAN -+ tristate "QMI WWAN driver for Qualcomm MSM based 3G and LTE modems" -+ depends on m -+ depends on USB_USBNET -+ select USB_WDM -+ help -+ Support WWAN LTE/3G devices based on Qualcomm Mobile Data Modem -+ (MDM) chipsets. Examples of such devices are -+ * Huawei E392/E398 -+ -+ This driver will only drive the ethernet part of the chips. -+ The devices require additional configuration to be usable. -+ Multiple management interfaces with linux drivers are -+ available: -+ -+ * option: AT commands on /dev/ttyUSBx -+ * cdc-wdm: Qualcomm MSM Interface (QMI) protocol on /dev/cdc-wdmx -+ -+ A modem manager with support for QMI is recommended. -+ -+ To compile this driver as a module, choose M here: the -+ module will be called qmi_wwan. -+ -+config USB_HSO -+ depends on n -+ tristate "Option USB High Speed Mobile Devices" -+ depends on m -+ depends on USB && RFKILL && TTY -+ default n -+ help -+ Choose this option if you have an Option HSDPA/HSUPA card. -+ These cards support downlink speeds of 7.2Mbps or greater. -+ -+ To compile this driver as a module, choose M here: the -+ module will be called hso. -+ -+config USB_NET_INT51X1 -+ depends on n -+ tristate "Intellon PLC based usb adapter" -+ depends on m -+ depends on USB_USBNET -+ help -+ Choose this option if you're using a 14Mb USB-based PLC -+ (Powerline Communications) solution with an Intellon -+ INT51x1/INT5200 chip, like the "devolo dLan duo". -+ -+config USB_CDC_PHONET -+ depends on n -+ tristate "CDC Phonet support" -+ depends on m -+ depends on PHONET -+ help -+ Choose this option to support the Phonet interface to a Nokia -+ cellular modem, as found on most Nokia handsets with the -+ "PC suite" USB profile. -+ -+config USB_IPHETH -+ depends on n -+ tristate "Apple iPhone USB Ethernet driver" -+ depends on m -+ default n -+ ---help--- -+ Module used to share Internet connection (tethering) from your -+ iPhone (Original, 3G and 3GS) to your system. -+ Note that you need userspace libraries and programs that are needed -+ to pair your device with your system and that understand the iPhone -+ protocol. -+ -+ For more information: http://giagio.com/wiki/moin.cgi/iPhoneEthernetDriver -+ -+config USB_SIERRA_NET -+ tristate "USB-to-WWAN Driver for Sierra Wireless modems" -+ depends on m -+ depends on USB_USBNET -+ help -+ Choose this option if you have a Sierra Wireless USB-to-WWAN device. -+ -+ To compile this driver as a module, choose M here: the -+ module will be called sierra_net. -+ -+config USB_VL600 -+ depends on n -+ tristate "LG VL600 modem dongle" -+ depends on m -+ depends on USB_NET_CDCETHER && TTY -+ select USB_ACM -+ help -+ Select this if you want to use an LG Electronics 4G/LTE usb modem -+ called VL600. This driver only handles the ethernet -+ interface exposed by the modem firmware. To establish a connection -+ you will first need a userspace program that sends the right -+ command to the modem through its CDC ACM port, and most -+ likely also a DHCP client. See this thread about using the -+ 4G modem from Verizon: -+ -+ http://ubuntuforums.org/showpost.php?p=10589647&postcount=17 -+ -+endif # USB_NET_DRIVERS -diff -Naur backports-4.2.6-1.org/drivers/net/usb/lg-vl600.c backports-4.2.6-1/drivers/net/usb/lg-vl600.c ---- backports-4.2.6-1.org/drivers/net/usb/lg-vl600.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/lg-vl600.c 2016-06-28 14:35:17.991973886 +0200 -@@ -0,0 +1,353 @@ -+/* -+ * Ethernet interface part of the LG VL600 LTE modem (4G dongle) -+ * -+ * Copyright (C) 2011 Intel Corporation -+ * Author: Andrzej Zaborowski -+ * -+ * 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, see . -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* -+ * The device has a CDC ACM port for modem control (it claims to be -+ * CDC ACM anyway) and a CDC Ethernet port for actual network data. -+ * It will however ignore data on both ports that is not encapsulated -+ * in a specific way, any data returned is also encapsulated the same -+ * way. The headers don't seem to follow any popular standard. -+ * -+ * This driver adds and strips these headers from the ethernet frames -+ * sent/received from the CDC Ethernet port. The proprietary header -+ * replaces the standard ethernet header in a packet so only actual -+ * ethernet frames are allowed. The headers allow some form of -+ * multiplexing by using non standard values of the .h_proto field. -+ * Windows/Mac drivers do send a couple of such frames to the device -+ * during initialisation, with protocol set to 0x0906 or 0x0b06 and (what -+ * seems to be) a flag in the .dummy_flags. This doesn't seem necessary -+ * for modem operation but can possibly be used for GPS or other funcitons. -+ */ -+ -+struct vl600_frame_hdr { -+ __le32 len; -+ __le32 serial; -+ __le32 pkt_cnt; -+ __le32 dummy_flags; -+ __le32 dummy; -+ __le32 magic; -+} __attribute__((packed)); -+ -+struct vl600_pkt_hdr { -+ __le32 dummy[2]; -+ __le32 len; -+ __be16 h_proto; -+} __attribute__((packed)); -+ -+struct vl600_state { -+ struct sk_buff *current_rx_buf; -+}; -+ -+static int vl600_bind(struct usbnet *dev, struct usb_interface *intf) -+{ -+ int ret; -+ struct vl600_state *s = kzalloc(sizeof(struct vl600_state), GFP_KERNEL); -+ -+ if (!s) -+ return -ENOMEM; -+ -+ ret = usbnet_cdc_bind(dev, intf); -+ if (ret) { -+ kfree(s); -+ return ret; -+ } -+ -+ dev->driver_priv = s; -+ -+ /* ARP packets don't go through, but they're also of no use. The -+ * subnet has only two hosts anyway: us and the gateway / DHCP -+ * server (probably simulated by modem firmware or network operator) -+ * whose address changes everytime we connect to the intarwebz and -+ * who doesn't bother answering ARP requests either. So hardware -+ * addresses have no meaning, the destination and the source of every -+ * packet depend only on whether it is on the IN or OUT endpoint. */ -+ dev->net->flags |= IFF_NOARP; -+ /* IPv6 NDP relies on multicast. Enable it by default. */ -+ dev->net->flags |= IFF_MULTICAST; -+ -+ return ret; -+} -+ -+static void vl600_unbind(struct usbnet *dev, struct usb_interface *intf) -+{ -+ struct vl600_state *s = dev->driver_priv; -+ -+ if (s->current_rx_buf) -+ dev_kfree_skb(s->current_rx_buf); -+ -+ kfree(s); -+ -+ return usbnet_cdc_unbind(dev, intf); -+} -+ -+static int vl600_rx_fixup(struct usbnet *dev, struct sk_buff *skb) -+{ -+ struct vl600_frame_hdr *frame; -+ struct vl600_pkt_hdr *packet; -+ struct ethhdr *ethhdr; -+ int packet_len, count; -+ struct sk_buff *buf = skb; -+ struct sk_buff *clone; -+ struct vl600_state *s = dev->driver_priv; -+ -+ /* Frame lengths are generally 4B multiplies but every couple of -+ * hours there's an odd number of bytes sized yet correct frame, -+ * so don't require this. */ -+ -+ /* Allow a packet (or multiple packets batched together) to be -+ * split across many frames. We don't allow a new batch to -+ * begin in the same frame another one is ending however, and no -+ * leading or trailing pad bytes. */ -+ if (s->current_rx_buf) { -+ frame = (struct vl600_frame_hdr *) s->current_rx_buf->data; -+ if (skb->len + s->current_rx_buf->len > -+ le32_to_cpup(&frame->len)) { -+ netif_err(dev, ifup, dev->net, "Fragment too long\n"); -+ dev->net->stats.rx_length_errors++; -+ goto error; -+ } -+ -+ buf = s->current_rx_buf; -+ memcpy(skb_put(buf, skb->len), skb->data, skb->len); -+ } else if (skb->len < 4) { -+ netif_err(dev, ifup, dev->net, "Frame too short\n"); -+ dev->net->stats.rx_length_errors++; -+ goto error; -+ } -+ -+ frame = (struct vl600_frame_hdr *) buf->data; -+ /* Yes, check that frame->magic == 0x53544448 (or 0x44544d48), -+ * otherwise we may run out of memory w/a bad packet */ -+ if (ntohl(frame->magic) != 0x53544448 && -+ ntohl(frame->magic) != 0x44544d48) -+ goto error; -+ -+ if (buf->len < sizeof(*frame) || -+ buf->len != le32_to_cpup(&frame->len)) { -+ /* Save this fragment for later assembly */ -+ if (s->current_rx_buf) -+ return 0; -+ -+ s->current_rx_buf = skb_copy_expand(skb, 0, -+ le32_to_cpup(&frame->len), GFP_ATOMIC); -+ if (!s->current_rx_buf) { -+ netif_err(dev, ifup, dev->net, "Reserving %i bytes " -+ "for packet assembly failed.\n", -+ le32_to_cpup(&frame->len)); -+ dev->net->stats.rx_errors++; -+ } -+ -+ return 0; -+ } -+ -+ count = le32_to_cpup(&frame->pkt_cnt); -+ -+ skb_pull(buf, sizeof(*frame)); -+ -+ while (count--) { -+ if (buf->len < sizeof(*packet)) { -+ netif_err(dev, ifup, dev->net, "Packet too short\n"); -+ goto error; -+ } -+ -+ packet = (struct vl600_pkt_hdr *) buf->data; -+ packet_len = sizeof(*packet) + le32_to_cpup(&packet->len); -+ if (packet_len > buf->len) { -+ netif_err(dev, ifup, dev->net, -+ "Bad packet length stored in header\n"); -+ goto error; -+ } -+ -+ /* Packet header is same size as the ethernet header -+ * (sizeof(*packet) == sizeof(*ethhdr)), additionally -+ * the h_proto field is in the same place so we just leave it -+ * alone and fill in the remaining fields. -+ */ -+ ethhdr = (struct ethhdr *) skb->data; -+ if (be16_to_cpup(ðhdr->h_proto) == ETH_P_ARP && -+ buf->len > 0x26) { -+ /* Copy the addresses from packet contents */ -+ memcpy(ethhdr->h_source, -+ &buf->data[sizeof(*ethhdr) + 0x8], -+ ETH_ALEN); -+ memcpy(ethhdr->h_dest, -+ &buf->data[sizeof(*ethhdr) + 0x12], -+ ETH_ALEN); -+ } else { -+ eth_zero_addr(ethhdr->h_source); -+ memcpy(ethhdr->h_dest, dev->net->dev_addr, ETH_ALEN); -+ -+ /* Inbound IPv6 packets have an IPv4 ethertype (0x800) -+ * for some reason. Peek at the L3 header to check -+ * for IPv6 packets, and set the ethertype to IPv6 -+ * (0x86dd) so Linux can understand it. -+ */ -+ if ((buf->data[sizeof(*ethhdr)] & 0xf0) == 0x60) -+ ethhdr->h_proto = htons(ETH_P_IPV6); -+ } -+ -+ if (count) { -+ /* Not the last packet in this batch */ -+ clone = skb_clone(buf, GFP_ATOMIC); -+ if (!clone) -+ goto error; -+ -+ skb_trim(clone, packet_len); -+ usbnet_skb_return(dev, clone); -+ -+ skb_pull(buf, (packet_len + 3) & ~3); -+ } else { -+ skb_trim(buf, packet_len); -+ -+ if (s->current_rx_buf) { -+ usbnet_skb_return(dev, buf); -+ s->current_rx_buf = NULL; -+ return 0; -+ } -+ -+ return 1; -+ } -+ } -+ -+error: -+ if (s->current_rx_buf) { -+ dev_kfree_skb_any(s->current_rx_buf); -+ s->current_rx_buf = NULL; -+ } -+ dev->net->stats.rx_errors++; -+ return 0; -+} -+ -+static struct sk_buff *vl600_tx_fixup(struct usbnet *dev, -+ struct sk_buff *skb, gfp_t flags) -+{ -+ struct sk_buff *ret; -+ struct vl600_frame_hdr *frame; -+ struct vl600_pkt_hdr *packet; -+ static uint32_t serial = 1; -+ int orig_len = skb->len - sizeof(struct ethhdr); -+ int full_len = (skb->len + sizeof(struct vl600_frame_hdr) + 3) & ~3; -+ -+ frame = (struct vl600_frame_hdr *) skb->data; -+ if (skb->len > sizeof(*frame) && skb->len == le32_to_cpup(&frame->len)) -+ return skb; /* Already encapsulated? */ -+ -+ if (skb->len < sizeof(struct ethhdr)) -+ /* Drop, device can only deal with ethernet packets */ -+ return NULL; -+ -+ if (!skb_cloned(skb)) { -+ int headroom = skb_headroom(skb); -+ int tailroom = skb_tailroom(skb); -+ -+ if (tailroom >= full_len - skb->len - sizeof(*frame) && -+ headroom >= sizeof(*frame)) -+ /* There's enough head and tail room */ -+ goto encapsulate; -+ -+ if (headroom + tailroom + skb->len >= full_len) { -+ /* There's enough total room, just readjust */ -+ skb->data = memmove(skb->head + sizeof(*frame), -+ skb->data, skb->len); -+ skb_set_tail_pointer(skb, skb->len); -+ goto encapsulate; -+ } -+ } -+ -+ /* Alloc a new skb with the required size */ -+ ret = skb_copy_expand(skb, sizeof(struct vl600_frame_hdr), full_len - -+ skb->len - sizeof(struct vl600_frame_hdr), flags); -+ dev_kfree_skb_any(skb); -+ if (!ret) -+ return ret; -+ skb = ret; -+ -+encapsulate: -+ /* Packet header is same size as ethernet packet header -+ * (sizeof(*packet) == sizeof(struct ethhdr)), additionally the -+ * h_proto field is in the same place so we just leave it alone and -+ * overwrite the remaining fields. -+ */ -+ packet = (struct vl600_pkt_hdr *) skb->data; -+ /* The VL600 wants IPv6 packets to have an IPv4 ethertype -+ * Since this modem only supports IPv4 and IPv6, just set all -+ * frames to 0x0800 (ETH_P_IP) -+ */ -+ packet->h_proto = htons(ETH_P_IP); -+ memset(&packet->dummy, 0, sizeof(packet->dummy)); -+ packet->len = cpu_to_le32(orig_len); -+ -+ frame = (struct vl600_frame_hdr *) skb_push(skb, sizeof(*frame)); -+ memset(frame, 0, sizeof(*frame)); -+ frame->len = cpu_to_le32(full_len); -+ frame->serial = cpu_to_le32(serial++); -+ frame->pkt_cnt = cpu_to_le32(1); -+ -+ if (skb->len < full_len) /* Pad */ -+ skb_put(skb, full_len - skb->len); -+ -+ return skb; -+} -+ -+static const struct driver_info vl600_info = { -+ .description = "LG VL600 modem", -+ .flags = FLAG_RX_ASSEMBLE | FLAG_WWAN, -+ .bind = vl600_bind, -+ .unbind = vl600_unbind, -+ .status = usbnet_cdc_status, -+ .rx_fixup = vl600_rx_fixup, -+ .tx_fixup = vl600_tx_fixup, -+}; -+ -+static const struct usb_device_id products[] = { -+ { -+ USB_DEVICE_AND_INTERFACE_INFO(0x1004, 0x61aa, USB_CLASS_COMM, -+ USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), -+ .driver_info = (unsigned long) &vl600_info, -+ }, -+ {}, /* End */ -+}; -+MODULE_DEVICE_TABLE(usb, products); -+ -+static struct usb_driver lg_vl600_driver = { -+ .name = "lg-vl600", -+ .id_table = products, -+ .probe = usbnet_probe, -+ .disconnect = usbnet_disconnect, -+ .suspend = usbnet_suspend, -+ .resume = usbnet_resume, -+ .disable_hub_initiated_lpm = 1, -+}; -+ -+module_usb_driver(lg_vl600_driver); -+ -+MODULE_AUTHOR("Anrzej Zaborowski"); -+MODULE_DESCRIPTION("LG-VL600 modem's ethernet link"); -+MODULE_LICENSE("GPL"); -diff -Naur backports-4.2.6-1.org/drivers/net/usb/Makefile backports-4.2.6-1/drivers/net/usb/Makefile ---- backports-4.2.6-1.org/drivers/net/usb/Makefile 2015-11-15 22:19:40.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/Makefile 2016-06-28 14:35:17.991973886 +0200 -@@ -1,39 +1,40 @@ - # - # Makefile for USB Network drivers - # --# --#obj-$(CPTCFG_USB_CATC) += catc.o --#obj-$(CPTCFG_USB_KAWETH) += kaweth.o --#obj-$(CPTCFG_USB_PEGASUS) += pegasus.o --#obj-$(CPTCFG_USB_RTL8150) += rtl8150.o --#obj-$(CPTCFG_USB_RTL8152) += r8152.o --#obj-$(CPTCFG_USB_HSO) += hso.o --#obj-$(CPTCFG_USB_NET_AX8817X) += asix.o --#obj-$(CPTCFG_USB_NET_AX88179_178A) += ax88179_178a.o -+ -+obj-$(CPTCFG_USB_CATC) += catc.o -+obj-$(CPTCFG_USB_KAWETH) += kaweth.o -+obj-$(CPTCFG_USB_PEGASUS) += pegasus.o -+obj-$(CPTCFG_USB_RTL8150) += rtl8150.o -+obj-$(CPTCFG_USB_RTL8152) += r8152.o -+obj-$(CPTCFG_USB_HSO) += hso.o -+obj-$(CPTCFG_USB_NET_AX8817X) += asix.o -+asix-y := asix_devices.o asix_common.o ax88172a.o -+obj-$(CPTCFG_USB_NET_AX88179_178A) += ax88179_178a.o - obj-$(CPTCFG_USB_NET_CDCETHER) += cdc_ether.o --#obj-$(CPTCFG_USB_NET_CDC_EEM) += cdc_eem.o --#obj-$(CPTCFG_USB_NET_DM9601) += dm9601.o --#obj-$(CPTCFG_USB_NET_SR9700) += sr9700.o --#obj-$(CPTCFG_USB_NET_SR9800) += sr9800.o --#obj-$(CPTCFG_USB_NET_SMSC75XX) += smsc75xx.o --#obj-$(CPTCFG_USB_NET_SMSC95XX) += smsc95xx.o --#obj-$(CPTCFG_USB_NET_GL620A) += gl620a.o --#obj-$(CPTCFG_USB_NET_NET1080) += net1080.o --#obj-$(CPTCFG_USB_NET_PLUSB) += plusb.o -+obj-$(CPTCFG_USB_NET_CDC_EEM) += cdc_eem.o -+obj-$(CPTCFG_USB_NET_DM9601) += dm9601.o -+obj-$(CPTCFG_USB_NET_SR9700) += sr9700.o -+obj-$(CPTCFG_USB_NET_SR9800) += sr9800.o -+obj-$(CPTCFG_USB_NET_SMSC75XX) += smsc75xx.o -+obj-$(CPTCFG_USB_NET_SMSC95XX) += smsc95xx.o -+obj-$(CPTCFG_USB_NET_GL620A) += gl620a.o -+obj-$(CPTCFG_USB_NET_NET1080) += net1080.o -+obj-$(CPTCFG_USB_NET_PLUSB) += plusb.o - obj-$(CPTCFG_USB_NET_RNDIS_HOST) += rndis_host.o --#obj-$(CPTCFG_USB_NET_CDC_SUBSET) += cdc_subset.o --#obj-$(CPTCFG_USB_NET_ZAURUS) += zaurus.o --#obj-$(CPTCFG_USB_NET_MCS7830) += mcs7830.o -+obj-$(CPTCFG_USB_NET_CDC_SUBSET) += cdc_subset.o -+obj-$(CPTCFG_USB_NET_ZAURUS) += zaurus.o -+obj-$(CPTCFG_USB_NET_MCS7830) += mcs7830.o - obj-$(CPTCFG_USB_USBNET) += usbnet.o --#obj-$(CPTCFG_USB_NET_INT51X1) += int51x1.o --#obj-$(CPTCFG_USB_CDC_PHONET) += cdc-phonet.o --#obj-$(CPTCFG_USB_NET_KALMIA) += kalmia.o --#obj-$(CPTCFG_USB_IPHETH) += ipheth.o -+obj-$(CPTCFG_USB_NET_INT51X1) += int51x1.o -+obj-$(CPTCFG_USB_CDC_PHONET) += cdc-phonet.o -+obj-$(CPTCFG_USB_NET_KALMIA) += kalmia.o -+obj-$(CPTCFG_USB_IPHETH) += ipheth.o - obj-$(CPTCFG_USB_SIERRA_NET) += sierra_net.o --#obj-$(CPTCFG_USB_NET_CX82310_ETH) += cx82310_eth.o -+obj-$(CPTCFG_USB_NET_CX82310_ETH) += cx82310_eth.o - obj-$(CPTCFG_USB_NET_CDC_NCM) += cdc_ncm.o --#obj-$(CPTCFG_USB_NET_HUAWEI_CDC_NCM) += huawei_cdc_ncm.o --#obj-$(CPTCFG_USB_VL600) += lg-vl600.o -+obj-$(CPTCFG_USB_NET_HUAWEI_CDC_NCM) += huawei_cdc_ncm.o -+obj-$(CPTCFG_USB_VL600) += lg-vl600.o - obj-$(CPTCFG_USB_NET_QMI_WWAN) += qmi_wwan.o - obj-$(CPTCFG_USB_NET_CDC_MBIM) += cdc_mbim.o - -diff -Naur backports-4.2.6-1.org/drivers/net/usb/mcs7830.c backports-4.2.6-1/drivers/net/usb/mcs7830.c ---- backports-4.2.6-1.org/drivers/net/usb/mcs7830.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/mcs7830.c 2016-06-28 14:35:17.995307218 +0200 -@@ -0,0 +1,643 @@ -+/* -+ * MOSCHIP MCS7830 based (7730/7830/7832) USB 2.0 Ethernet Devices -+ * -+ * based on usbnet.c, asix.c and the vendor provided mcs7830 driver -+ * -+ * Copyright (C) 2010 Andreas Mohr -+ * Copyright (C) 2006 Arnd Bergmann -+ * Copyright (C) 2003-2005 David Hollis -+ * Copyright (C) 2005 Phil Chang -+ * Copyright (c) 2002-2003 TiVo Inc. -+ * -+ * Definitions gathered from MOSCHIP, Data Sheet_7830DA.pdf (thanks!). -+ * -+ * 2010-12-19: add 7832 USB PID ("functionality same as MCS7830"), -+ * per active notification by manufacturer -+ * -+ * TODO: -+ * - support HIF_REG_CONFIG_SLEEPMODE/HIF_REG_CONFIG_TXENABLE (via autopm?) -+ * - implement ethtool_ops get_pauseparam/set_pauseparam -+ * via HIF_REG_PAUSE_THRESHOLD (>= revision C only!) -+ * - implement get_eeprom/[set_eeprom] -+ * - switch PHY on/off on ifup/ifdown (perhaps in usbnet.c, via MII) -+ * - mcs7830_get_regs() handling is weird: for rev 2 we return 32 regs, -+ * can access only ~ 24, remaining user buffer is uninitialized garbage -+ * - anything else? -+ * -+ * -+ * 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, see . -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* requests */ -+#define MCS7830_RD_BMREQ (USB_DIR_IN | USB_TYPE_VENDOR | \ -+ USB_RECIP_DEVICE) -+#define MCS7830_WR_BMREQ (USB_DIR_OUT | USB_TYPE_VENDOR | \ -+ USB_RECIP_DEVICE) -+#define MCS7830_RD_BREQ 0x0E -+#define MCS7830_WR_BREQ 0x0D -+ -+#define MCS7830_CTRL_TIMEOUT 1000 -+#define MCS7830_MAX_MCAST 64 -+ -+#define MCS7830_VENDOR_ID 0x9710 -+#define MCS7832_PRODUCT_ID 0x7832 -+#define MCS7830_PRODUCT_ID 0x7830 -+#define MCS7730_PRODUCT_ID 0x7730 -+ -+#define SITECOM_VENDOR_ID 0x0DF6 -+#define LN_030_PRODUCT_ID 0x0021 -+ -+#define MCS7830_MII_ADVERTISE (ADVERTISE_PAUSE_CAP | ADVERTISE_100FULL | \ -+ ADVERTISE_100HALF | ADVERTISE_10FULL | \ -+ ADVERTISE_10HALF | ADVERTISE_CSMA) -+ -+/* HIF_REG_XX corresponding index value */ -+enum { -+ HIF_REG_MULTICAST_HASH = 0x00, -+ HIF_REG_PACKET_GAP1 = 0x08, -+ HIF_REG_PACKET_GAP2 = 0x09, -+ HIF_REG_PHY_DATA = 0x0a, -+ HIF_REG_PHY_CMD1 = 0x0c, -+ HIF_REG_PHY_CMD1_READ = 0x40, -+ HIF_REG_PHY_CMD1_WRITE = 0x20, -+ HIF_REG_PHY_CMD1_PHYADDR = 0x01, -+ HIF_REG_PHY_CMD2 = 0x0d, -+ HIF_REG_PHY_CMD2_PEND_FLAG_BIT = 0x80, -+ HIF_REG_PHY_CMD2_READY_FLAG_BIT = 0x40, -+ HIF_REG_CONFIG = 0x0e, -+ /* hmm, spec sez: "R/W", "Except bit 3" (likely TXENABLE). */ -+ HIF_REG_CONFIG_CFG = 0x80, -+ HIF_REG_CONFIG_SPEED100 = 0x40, -+ HIF_REG_CONFIG_FULLDUPLEX_ENABLE = 0x20, -+ HIF_REG_CONFIG_RXENABLE = 0x10, -+ HIF_REG_CONFIG_TXENABLE = 0x08, -+ HIF_REG_CONFIG_SLEEPMODE = 0x04, -+ HIF_REG_CONFIG_ALLMULTICAST = 0x02, -+ HIF_REG_CONFIG_PROMISCUOUS = 0x01, -+ HIF_REG_ETHERNET_ADDR = 0x0f, -+ HIF_REG_FRAME_DROP_COUNTER = 0x15, /* 0..ff; reset: 0 */ -+ HIF_REG_PAUSE_THRESHOLD = 0x16, -+ HIF_REG_PAUSE_THRESHOLD_DEFAULT = 0, -+}; -+ -+/* Trailing status byte in Ethernet Rx frame */ -+enum { -+ MCS7830_RX_SHORT_FRAME = 0x01, /* < 64 bytes */ -+ MCS7830_RX_LENGTH_ERROR = 0x02, /* framelen != Ethernet length field */ -+ MCS7830_RX_ALIGNMENT_ERROR = 0x04, /* non-even number of nibbles */ -+ MCS7830_RX_CRC_ERROR = 0x08, -+ MCS7830_RX_LARGE_FRAME = 0x10, /* > 1518 bytes */ -+ MCS7830_RX_FRAME_CORRECT = 0x20, /* frame is correct */ -+ /* [7:6] reserved */ -+}; -+ -+struct mcs7830_data { -+ u8 multi_filter[8]; -+ u8 config; -+}; -+ -+static const char driver_name[] = "MOSCHIP usb-ethernet driver"; -+ -+static int mcs7830_get_reg(struct usbnet *dev, u16 index, u16 size, void *data) -+{ -+ return usbnet_read_cmd(dev, MCS7830_RD_BREQ, MCS7830_RD_BMREQ, -+ 0x0000, index, data, size); -+} -+ -+static int mcs7830_set_reg(struct usbnet *dev, u16 index, u16 size, const void *data) -+{ -+ return usbnet_write_cmd(dev, MCS7830_WR_BREQ, MCS7830_WR_BMREQ, -+ 0x0000, index, data, size); -+} -+ -+static void mcs7830_set_reg_async(struct usbnet *dev, u16 index, u16 size, void *data) -+{ -+ usbnet_write_cmd_async(dev, MCS7830_WR_BREQ, MCS7830_WR_BMREQ, -+ 0x0000, index, data, size); -+} -+ -+static int mcs7830_hif_get_mac_address(struct usbnet *dev, unsigned char *addr) -+{ -+ int ret = mcs7830_get_reg(dev, HIF_REG_ETHERNET_ADDR, ETH_ALEN, addr); -+ if (ret < 0) -+ return ret; -+ return 0; -+} -+ -+static int mcs7830_hif_set_mac_address(struct usbnet *dev, unsigned char *addr) -+{ -+ int ret = mcs7830_set_reg(dev, HIF_REG_ETHERNET_ADDR, ETH_ALEN, addr); -+ -+ if (ret < 0) -+ return ret; -+ return 0; -+} -+ -+static int mcs7830_set_mac_address(struct net_device *netdev, void *p) -+{ -+ int ret; -+ struct usbnet *dev = netdev_priv(netdev); -+ struct sockaddr *addr = p; -+ -+ if (netif_running(netdev)) -+ return -EBUSY; -+ -+ if (!is_valid_ether_addr(addr->sa_data)) -+ return -EADDRNOTAVAIL; -+ -+ ret = mcs7830_hif_set_mac_address(dev, addr->sa_data); -+ -+ if (ret < 0) -+ return ret; -+ -+ /* it worked --> adopt it on netdev side */ -+ memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); -+ -+ return 0; -+} -+ -+static int mcs7830_read_phy(struct usbnet *dev, u8 index) -+{ -+ int ret; -+ int i; -+ __le16 val; -+ -+ u8 cmd[2] = { -+ HIF_REG_PHY_CMD1_READ | HIF_REG_PHY_CMD1_PHYADDR, -+ HIF_REG_PHY_CMD2_PEND_FLAG_BIT | index, -+ }; -+ -+ mutex_lock(&dev->phy_mutex); -+ /* write the MII command */ -+ ret = mcs7830_set_reg(dev, HIF_REG_PHY_CMD1, 2, cmd); -+ if (ret < 0) -+ goto out; -+ -+ /* wait for the data to become valid, should be within < 1ms */ -+ for (i = 0; i < 10; i++) { -+ ret = mcs7830_get_reg(dev, HIF_REG_PHY_CMD1, 2, cmd); -+ if ((ret < 0) || (cmd[1] & HIF_REG_PHY_CMD2_READY_FLAG_BIT)) -+ break; -+ ret = -EIO; -+ msleep(1); -+ } -+ if (ret < 0) -+ goto out; -+ -+ /* read actual register contents */ -+ ret = mcs7830_get_reg(dev, HIF_REG_PHY_DATA, 2, &val); -+ if (ret < 0) -+ goto out; -+ ret = le16_to_cpu(val); -+ dev_dbg(&dev->udev->dev, "read PHY reg %02x: %04x (%d tries)\n", -+ index, val, i); -+out: -+ mutex_unlock(&dev->phy_mutex); -+ return ret; -+} -+ -+static int mcs7830_write_phy(struct usbnet *dev, u8 index, u16 val) -+{ -+ int ret; -+ int i; -+ __le16 le_val; -+ -+ u8 cmd[2] = { -+ HIF_REG_PHY_CMD1_WRITE | HIF_REG_PHY_CMD1_PHYADDR, -+ HIF_REG_PHY_CMD2_PEND_FLAG_BIT | (index & 0x1F), -+ }; -+ -+ mutex_lock(&dev->phy_mutex); -+ -+ /* write the new register contents */ -+ le_val = cpu_to_le16(val); -+ ret = mcs7830_set_reg(dev, HIF_REG_PHY_DATA, 2, &le_val); -+ if (ret < 0) -+ goto out; -+ -+ /* write the MII command */ -+ ret = mcs7830_set_reg(dev, HIF_REG_PHY_CMD1, 2, cmd); -+ if (ret < 0) -+ goto out; -+ -+ /* wait for the command to be accepted by the PHY */ -+ for (i = 0; i < 10; i++) { -+ ret = mcs7830_get_reg(dev, HIF_REG_PHY_CMD1, 2, cmd); -+ if ((ret < 0) || (cmd[1] & HIF_REG_PHY_CMD2_READY_FLAG_BIT)) -+ break; -+ ret = -EIO; -+ msleep(1); -+ } -+ if (ret < 0) -+ goto out; -+ -+ ret = 0; -+ dev_dbg(&dev->udev->dev, "write PHY reg %02x: %04x (%d tries)\n", -+ index, val, i); -+out: -+ mutex_unlock(&dev->phy_mutex); -+ return ret; -+} -+ -+/* -+ * This algorithm comes from the original mcs7830 version 1.4 driver, -+ * not sure if it is needed. -+ */ -+static int mcs7830_set_autoneg(struct usbnet *dev, int ptrUserPhyMode) -+{ -+ int ret; -+ /* Enable all media types */ -+ ret = mcs7830_write_phy(dev, MII_ADVERTISE, MCS7830_MII_ADVERTISE); -+ -+ /* First reset BMCR */ -+ if (!ret) -+ ret = mcs7830_write_phy(dev, MII_BMCR, 0x0000); -+ /* Enable Auto Neg */ -+ if (!ret) -+ ret = mcs7830_write_phy(dev, MII_BMCR, BMCR_ANENABLE); -+ /* Restart Auto Neg (Keep the Enable Auto Neg Bit Set) */ -+ if (!ret) -+ ret = mcs7830_write_phy(dev, MII_BMCR, -+ BMCR_ANENABLE | BMCR_ANRESTART ); -+ return ret; -+} -+ -+ -+/* -+ * if we can read register 22, the chip revision is C or higher -+ */ -+static int mcs7830_get_rev(struct usbnet *dev) -+{ -+ u8 dummy[2]; -+ int ret; -+ ret = mcs7830_get_reg(dev, HIF_REG_FRAME_DROP_COUNTER, 2, dummy); -+ if (ret > 0) -+ return 2; /* Rev C or later */ -+ return 1; /* earlier revision */ -+} -+ -+/* -+ * On rev. C we need to set the pause threshold -+ */ -+static void mcs7830_rev_C_fixup(struct usbnet *dev) -+{ -+ u8 pause_threshold = HIF_REG_PAUSE_THRESHOLD_DEFAULT; -+ int retry; -+ -+ for (retry = 0; retry < 2; retry++) { -+ if (mcs7830_get_rev(dev) == 2) { -+ dev_info(&dev->udev->dev, "applying rev.C fixup\n"); -+ mcs7830_set_reg(dev, HIF_REG_PAUSE_THRESHOLD, -+ 1, &pause_threshold); -+ } -+ msleep(1); -+ } -+} -+ -+static int mcs7830_mdio_read(struct net_device *netdev, int phy_id, -+ int location) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ return mcs7830_read_phy(dev, location); -+} -+ -+static void mcs7830_mdio_write(struct net_device *netdev, int phy_id, -+ int location, int val) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ mcs7830_write_phy(dev, location, val); -+} -+ -+static int mcs7830_ioctl(struct net_device *net, struct ifreq *rq, int cmd) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); -+} -+ -+static inline struct mcs7830_data *mcs7830_get_data(struct usbnet *dev) -+{ -+ return (struct mcs7830_data *)&dev->data; -+} -+ -+static void mcs7830_hif_update_multicast_hash(struct usbnet *dev) -+{ -+ struct mcs7830_data *data = mcs7830_get_data(dev); -+ mcs7830_set_reg_async(dev, HIF_REG_MULTICAST_HASH, -+ sizeof data->multi_filter, -+ data->multi_filter); -+} -+ -+static void mcs7830_hif_update_config(struct usbnet *dev) -+{ -+ /* implementation specific to data->config -+ (argument needs to be heap-based anyway - USB DMA!) */ -+ struct mcs7830_data *data = mcs7830_get_data(dev); -+ mcs7830_set_reg_async(dev, HIF_REG_CONFIG, 1, &data->config); -+} -+ -+static void mcs7830_data_set_multicast(struct net_device *net) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ struct mcs7830_data *data = mcs7830_get_data(dev); -+ -+ memset(data->multi_filter, 0, sizeof data->multi_filter); -+ -+ data->config = HIF_REG_CONFIG_TXENABLE; -+ -+ /* this should not be needed, but it doesn't work otherwise */ -+ data->config |= HIF_REG_CONFIG_ALLMULTICAST; -+ -+ if (net->flags & IFF_PROMISC) { -+ data->config |= HIF_REG_CONFIG_PROMISCUOUS; -+ } else if (net->flags & IFF_ALLMULTI || -+ netdev_mc_count(net) > MCS7830_MAX_MCAST) { -+ data->config |= HIF_REG_CONFIG_ALLMULTICAST; -+ } else if (netdev_mc_empty(net)) { -+ /* just broadcast and directed */ -+ } else { -+ /* We use the 20 byte dev->data -+ * for our 8 byte filter buffer -+ * to avoid allocating memory that -+ * is tricky to free later */ -+ struct netdev_hw_addr *ha; -+ u32 crc_bits; -+ -+ /* Build the multicast hash filter. */ -+ netdev_for_each_mc_addr(ha, net) { -+ crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26; -+ data->multi_filter[crc_bits >> 3] |= 1 << (crc_bits & 7); -+ } -+ } -+} -+ -+static int mcs7830_apply_base_config(struct usbnet *dev) -+{ -+ int ret; -+ -+ /* re-configure known MAC (suspend case etc.) */ -+ ret = mcs7830_hif_set_mac_address(dev, dev->net->dev_addr); -+ if (ret) { -+ dev_info(&dev->udev->dev, "Cannot set MAC address\n"); -+ goto out; -+ } -+ -+ /* Set up PHY */ -+ ret = mcs7830_set_autoneg(dev, 0); -+ if (ret) { -+ dev_info(&dev->udev->dev, "Cannot set autoneg\n"); -+ goto out; -+ } -+ -+ mcs7830_hif_update_multicast_hash(dev); -+ mcs7830_hif_update_config(dev); -+ -+ mcs7830_rev_C_fixup(dev); -+ ret = 0; -+out: -+ return ret; -+} -+ -+/* credits go to asix_set_multicast */ -+static void mcs7830_set_multicast(struct net_device *net) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ -+ mcs7830_data_set_multicast(net); -+ -+ mcs7830_hif_update_multicast_hash(dev); -+ mcs7830_hif_update_config(dev); -+} -+ -+static int mcs7830_get_regs_len(struct net_device *net) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ -+ switch (mcs7830_get_rev(dev)) { -+ case 1: -+ return 21; -+ case 2: -+ return 32; -+ } -+ return 0; -+} -+ -+static void mcs7830_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *drvinfo) -+{ -+ usbnet_get_drvinfo(net, drvinfo); -+ drvinfo->regdump_len = mcs7830_get_regs_len(net); -+} -+ -+static void mcs7830_get_regs(struct net_device *net, struct ethtool_regs *regs, void *data) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ -+ regs->version = mcs7830_get_rev(dev); -+ mcs7830_get_reg(dev, 0, regs->len, data); -+} -+ -+static const struct ethtool_ops mcs7830_ethtool_ops = { -+ .get_drvinfo = mcs7830_get_drvinfo, -+ .get_regs_len = mcs7830_get_regs_len, -+ .get_regs = mcs7830_get_regs, -+ -+ /* common usbnet calls */ -+ .get_link = usbnet_get_link, -+ .get_msglevel = usbnet_get_msglevel, -+ .set_msglevel = usbnet_set_msglevel, -+ .get_settings = usbnet_get_settings, -+ .set_settings = usbnet_set_settings, -+ .nway_reset = usbnet_nway_reset, -+}; -+ -+static const struct net_device_ops mcs7830_netdev_ops = { -+ .ndo_open = usbnet_open, -+ .ndo_stop = usbnet_stop, -+ .ndo_start_xmit = usbnet_start_xmit, -+ .ndo_tx_timeout = usbnet_tx_timeout, -+ .ndo_change_mtu = usbnet_change_mtu, -+ .ndo_validate_addr = eth_validate_addr, -+ .ndo_do_ioctl = mcs7830_ioctl, -+ .ndo_set_rx_mode = mcs7830_set_multicast, -+ .ndo_set_mac_address = mcs7830_set_mac_address, -+}; -+ -+static int mcs7830_bind(struct usbnet *dev, struct usb_interface *udev) -+{ -+ struct net_device *net = dev->net; -+ int ret; -+ int retry; -+ -+ /* Initial startup: Gather MAC address setting from EEPROM */ -+ ret = -EINVAL; -+ for (retry = 0; retry < 5 && ret; retry++) -+ ret = mcs7830_hif_get_mac_address(dev, net->dev_addr); -+ if (ret) { -+ dev_warn(&dev->udev->dev, "Cannot read MAC address\n"); -+ goto out; -+ } -+ -+ mcs7830_data_set_multicast(net); -+ -+ ret = mcs7830_apply_base_config(dev); -+ if (ret) -+ goto out; -+ -+ net->ethtool_ops = &mcs7830_ethtool_ops; -+ net->netdev_ops = &mcs7830_netdev_ops; -+ -+ /* reserve space for the status byte on rx */ -+ dev->rx_urb_size = ETH_FRAME_LEN + 1; -+ -+ dev->mii.mdio_read = mcs7830_mdio_read; -+ dev->mii.mdio_write = mcs7830_mdio_write; -+ dev->mii.dev = net; -+ dev->mii.phy_id_mask = 0x3f; -+ dev->mii.reg_num_mask = 0x1f; -+ dev->mii.phy_id = *((u8 *) net->dev_addr + 1); -+ -+ ret = usbnet_get_endpoints(dev, udev); -+out: -+ return ret; -+} -+ -+/* The chip always appends a status byte that we need to strip */ -+static int mcs7830_rx_fixup(struct usbnet *dev, struct sk_buff *skb) -+{ -+ u8 status; -+ -+ /* This check is no longer done by usbnet */ -+ if (skb->len < dev->net->hard_header_len) { -+ dev_err(&dev->udev->dev, "unexpected tiny rx frame\n"); -+ return 0; -+ } -+ -+ skb_trim(skb, skb->len - 1); -+ status = skb->data[skb->len]; -+ -+ if (status != MCS7830_RX_FRAME_CORRECT) { -+ dev_dbg(&dev->udev->dev, "rx fixup status %x\n", status); -+ -+ /* hmm, perhaps usbnet.c already sees a globally visible -+ frame error and increments rx_errors on its own already? */ -+ dev->net->stats.rx_errors++; -+ -+ if (status & (MCS7830_RX_SHORT_FRAME -+ |MCS7830_RX_LENGTH_ERROR -+ |MCS7830_RX_LARGE_FRAME)) -+ dev->net->stats.rx_length_errors++; -+ if (status & MCS7830_RX_ALIGNMENT_ERROR) -+ dev->net->stats.rx_frame_errors++; -+ if (status & MCS7830_RX_CRC_ERROR) -+ dev->net->stats.rx_crc_errors++; -+ } -+ -+ return skb->len > 0; -+} -+ -+static void mcs7830_status(struct usbnet *dev, struct urb *urb) -+{ -+ u8 *buf = urb->transfer_buffer; -+ bool link, link_changed; -+ -+ if (urb->actual_length < 16) -+ return; -+ -+ link = !(buf[1] == 0x20); -+ link_changed = netif_carrier_ok(dev->net) != link; -+ if (link_changed) { -+ usbnet_link_change(dev, link, 0); -+ netdev_dbg(dev->net, "Link Status is: %d\n", link); -+ } -+} -+ -+static const struct driver_info moschip_info = { -+ .description = "MOSCHIP 7830/7832/7730 usb-NET adapter", -+ .bind = mcs7830_bind, -+ .rx_fixup = mcs7830_rx_fixup, -+ .flags = FLAG_ETHER | FLAG_LINK_INTR, -+ .status = mcs7830_status, -+ .in = 1, -+ .out = 2, -+}; -+ -+static const struct driver_info sitecom_info = { -+ .description = "Sitecom LN-30 usb-NET adapter", -+ .bind = mcs7830_bind, -+ .rx_fixup = mcs7830_rx_fixup, -+ .flags = FLAG_ETHER | FLAG_LINK_INTR, -+ .status = mcs7830_status, -+ .in = 1, -+ .out = 2, -+}; -+ -+static const struct usb_device_id products[] = { -+ { -+ USB_DEVICE(MCS7830_VENDOR_ID, MCS7832_PRODUCT_ID), -+ .driver_info = (unsigned long) &moschip_info, -+ }, -+ { -+ USB_DEVICE(MCS7830_VENDOR_ID, MCS7830_PRODUCT_ID), -+ .driver_info = (unsigned long) &moschip_info, -+ }, -+ { -+ USB_DEVICE(MCS7830_VENDOR_ID, MCS7730_PRODUCT_ID), -+ .driver_info = (unsigned long) &moschip_info, -+ }, -+ { -+ USB_DEVICE(SITECOM_VENDOR_ID, LN_030_PRODUCT_ID), -+ .driver_info = (unsigned long) &sitecom_info, -+ }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(usb, products); -+ -+static int mcs7830_reset_resume (struct usb_interface *intf) -+{ -+ /* YES, this function is successful enough that ethtool -d -+ does show same output pre-/post-suspend */ -+ -+ struct usbnet *dev = usb_get_intfdata(intf); -+ -+ mcs7830_apply_base_config(dev); -+ -+ usbnet_resume(intf); -+ -+ return 0; -+} -+ -+static struct usb_driver mcs7830_driver = { -+ .name = driver_name, -+ .id_table = products, -+ .probe = usbnet_probe, -+ .disconnect = usbnet_disconnect, -+ .suspend = usbnet_suspend, -+ .resume = usbnet_resume, -+ .reset_resume = mcs7830_reset_resume, -+ .disable_hub_initiated_lpm = 1, -+}; -+ -+module_usb_driver(mcs7830_driver); -+ -+MODULE_DESCRIPTION("USB to network adapter MCS7830)"); -+MODULE_LICENSE("GPL"); -diff -Naur backports-4.2.6-1.org/drivers/net/usb/net1080.c backports-4.2.6-1/drivers/net/usb/net1080.c ---- backports-4.2.6-1.org/drivers/net/usb/net1080.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/net1080.c 2016-06-28 14:35:17.995307218 +0200 -@@ -0,0 +1,544 @@ -+/* -+ * Net1080 based USB host-to-host cables -+ * Copyright (C) 2000-2005 by David Brownell -+ * -+ * 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, see . -+ */ -+ -+// #define DEBUG // error path messages, extra info -+// #define VERBOSE // more; success messages -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+ -+/* -+ * Netchip 1080 driver ... http://www.netchip.com -+ * (Sept 2004: End-of-life announcement has been sent.) -+ * Used in (some) LapLink cables -+ */ -+ -+#define frame_errors data[1] -+ -+/* -+ * NetChip framing of ethernet packets, supporting additional error -+ * checks for links that may drop bulk packets from inside messages. -+ * Odd USB length == always short read for last usb packet. -+ * - nc_header -+ * - Ethernet header (14 bytes) -+ * - payload -+ * - (optional padding byte, if needed so length becomes odd) -+ * - nc_trailer -+ * -+ * This framing is to be avoided for non-NetChip devices. -+ */ -+ -+struct nc_header { // packed: -+ __le16 hdr_len; // sizeof nc_header (LE, all) -+ __le16 packet_len; // payload size (including ethhdr) -+ __le16 packet_id; // detects dropped packets -+#define MIN_HEADER 6 -+ -+ // all else is optional, and must start with: -+ // __le16 vendorId; // from usb-if -+ // __le16 productId; -+} __packed; -+ -+#define PAD_BYTE ((unsigned char)0xAC) -+ -+struct nc_trailer { -+ __le16 packet_id; -+} __packed; -+ -+// packets may use FLAG_FRAMING_NC and optional pad -+#define FRAMED_SIZE(mtu) (sizeof (struct nc_header) \ -+ + sizeof (struct ethhdr) \ -+ + (mtu) \ -+ + 1 \ -+ + sizeof (struct nc_trailer)) -+ -+#define MIN_FRAMED FRAMED_SIZE(0) -+ -+/* packets _could_ be up to 64KB... */ -+#define NC_MAX_PACKET 32767 -+ -+ -+/* -+ * Zero means no timeout; else, how long a 64 byte bulk packet may be queued -+ * before the hardware drops it. If that's done, the driver will need to -+ * frame network packets to guard against the dropped USB packets. The win32 -+ * driver sets this for both sides of the link. -+ */ -+#define NC_READ_TTL_MS ((u8)255) // ms -+ -+/* -+ * We ignore most registers and EEPROM contents. -+ */ -+#define REG_USBCTL ((u8)0x04) -+#define REG_TTL ((u8)0x10) -+#define REG_STATUS ((u8)0x11) -+ -+/* -+ * Vendor specific requests to read/write data -+ */ -+#define REQUEST_REGISTER ((u8)0x10) -+#define REQUEST_EEPROM ((u8)0x11) -+ -+static int -+nc_vendor_read(struct usbnet *dev, u8 req, u8 regnum, u16 *retval_ptr) -+{ -+ int status = usbnet_read_cmd(dev, req, -+ USB_DIR_IN | USB_TYPE_VENDOR | -+ USB_RECIP_DEVICE, -+ 0, regnum, retval_ptr, -+ sizeof *retval_ptr); -+ if (status > 0) -+ status = 0; -+ if (!status) -+ le16_to_cpus(retval_ptr); -+ return status; -+} -+ -+static inline int -+nc_register_read(struct usbnet *dev, u8 regnum, u16 *retval_ptr) -+{ -+ return nc_vendor_read(dev, REQUEST_REGISTER, regnum, retval_ptr); -+} -+ -+// no retval ... can become async, usable in_interrupt() -+static void -+nc_vendor_write(struct usbnet *dev, u8 req, u8 regnum, u16 value) -+{ -+ usbnet_write_cmd(dev, req, -+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, -+ value, regnum, NULL, 0); -+} -+ -+static inline void -+nc_register_write(struct usbnet *dev, u8 regnum, u16 value) -+{ -+ nc_vendor_write(dev, REQUEST_REGISTER, regnum, value); -+} -+ -+ -+#if 0 -+static void nc_dump_registers(struct usbnet *dev) -+{ -+ u8 reg; -+ u16 *vp = kmalloc(sizeof (u16)); -+ -+ if (!vp) -+ return; -+ -+ netdev_dbg(dev->net, "registers:\n"); -+ for (reg = 0; reg < 0x20; reg++) { -+ int retval; -+ -+ // reading some registers is trouble -+ if (reg >= 0x08 && reg <= 0xf) -+ continue; -+ if (reg >= 0x12 && reg <= 0x1e) -+ continue; -+ -+ retval = nc_register_read(dev, reg, vp); -+ if (retval < 0) -+ netdev_dbg(dev->net, "reg [0x%x] ==> error %d\n", -+ reg, retval); -+ else -+ netdev_dbg(dev->net, "reg [0x%x] = 0x%x\n", reg, *vp); -+ } -+ kfree(vp); -+} -+#endif -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* -+ * Control register -+ */ -+ -+#define USBCTL_WRITABLE_MASK 0x1f0f -+// bits 15-13 reserved, r/o -+#define USBCTL_ENABLE_LANG (1 << 12) -+#define USBCTL_ENABLE_MFGR (1 << 11) -+#define USBCTL_ENABLE_PROD (1 << 10) -+#define USBCTL_ENABLE_SERIAL (1 << 9) -+#define USBCTL_ENABLE_DEFAULTS (1 << 8) -+// bits 7-4 reserved, r/o -+#define USBCTL_FLUSH_OTHER (1 << 3) -+#define USBCTL_FLUSH_THIS (1 << 2) -+#define USBCTL_DISCONN_OTHER (1 << 1) -+#define USBCTL_DISCONN_THIS (1 << 0) -+ -+static inline void nc_dump_usbctl(struct usbnet *dev, u16 usbctl) -+{ -+ netif_dbg(dev, link, dev->net, -+ "net1080 %s-%s usbctl 0x%x:%s%s%s%s%s; this%s%s; other%s%s; r/o 0x%x\n", -+ dev->udev->bus->bus_name, dev->udev->devpath, -+ usbctl, -+ (usbctl & USBCTL_ENABLE_LANG) ? " lang" : "", -+ (usbctl & USBCTL_ENABLE_MFGR) ? " mfgr" : "", -+ (usbctl & USBCTL_ENABLE_PROD) ? " prod" : "", -+ (usbctl & USBCTL_ENABLE_SERIAL) ? " serial" : "", -+ (usbctl & USBCTL_ENABLE_DEFAULTS) ? " defaults" : "", -+ -+ (usbctl & USBCTL_FLUSH_THIS) ? " FLUSH" : "", -+ (usbctl & USBCTL_DISCONN_THIS) ? " DIS" : "", -+ -+ (usbctl & USBCTL_FLUSH_OTHER) ? " FLUSH" : "", -+ (usbctl & USBCTL_DISCONN_OTHER) ? " DIS" : "", -+ -+ usbctl & ~USBCTL_WRITABLE_MASK); -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* -+ * Status register -+ */ -+ -+#define STATUS_PORT_A (1 << 15) -+ -+#define STATUS_CONN_OTHER (1 << 14) -+#define STATUS_SUSPEND_OTHER (1 << 13) -+#define STATUS_MAILBOX_OTHER (1 << 12) -+#define STATUS_PACKETS_OTHER(n) (((n) >> 8) & 0x03) -+ -+#define STATUS_CONN_THIS (1 << 6) -+#define STATUS_SUSPEND_THIS (1 << 5) -+#define STATUS_MAILBOX_THIS (1 << 4) -+#define STATUS_PACKETS_THIS(n) (((n) >> 0) & 0x03) -+ -+#define STATUS_UNSPEC_MASK 0x0c8c -+#define STATUS_NOISE_MASK ((u16)~(0x0303|STATUS_UNSPEC_MASK)) -+ -+ -+static inline void nc_dump_status(struct usbnet *dev, u16 status) -+{ -+ netif_dbg(dev, link, dev->net, -+ "net1080 %s-%s status 0x%x: this (%c) PKT=%d%s%s%s; other PKT=%d%s%s%s; unspec 0x%x\n", -+ dev->udev->bus->bus_name, dev->udev->devpath, -+ status, -+ -+ // XXX the packet counts don't seem right -+ // (1 at reset, not 0); maybe UNSPEC too -+ -+ (status & STATUS_PORT_A) ? 'A' : 'B', -+ STATUS_PACKETS_THIS(status), -+ (status & STATUS_CONN_THIS) ? " CON" : "", -+ (status & STATUS_SUSPEND_THIS) ? " SUS" : "", -+ (status & STATUS_MAILBOX_THIS) ? " MBOX" : "", -+ -+ STATUS_PACKETS_OTHER(status), -+ (status & STATUS_CONN_OTHER) ? " CON" : "", -+ (status & STATUS_SUSPEND_OTHER) ? " SUS" : "", -+ (status & STATUS_MAILBOX_OTHER) ? " MBOX" : "", -+ -+ status & STATUS_UNSPEC_MASK); -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* -+ * TTL register -+ */ -+ -+#define TTL_THIS(ttl) (0x00ff & ttl) -+#define TTL_OTHER(ttl) (0x00ff & (ttl >> 8)) -+#define MK_TTL(this,other) ((u16)(((other)<<8)|(0x00ff&(this)))) -+ -+static inline void nc_dump_ttl(struct usbnet *dev, u16 ttl) -+{ -+ netif_dbg(dev, link, dev->net, "net1080 %s-%s ttl 0x%x this = %d, other = %d\n", -+ dev->udev->bus->bus_name, dev->udev->devpath, -+ ttl, TTL_THIS(ttl), TTL_OTHER(ttl)); -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+static int net1080_reset(struct usbnet *dev) -+{ -+ u16 usbctl, status, ttl; -+ u16 vp; -+ int retval; -+ -+ // nc_dump_registers(dev); -+ -+ if ((retval = nc_register_read(dev, REG_STATUS, &vp)) < 0) { -+ netdev_dbg(dev->net, "can't read %s-%s status: %d\n", -+ dev->udev->bus->bus_name, dev->udev->devpath, retval); -+ goto done; -+ } -+ status = vp; -+ nc_dump_status(dev, status); -+ -+ if ((retval = nc_register_read(dev, REG_USBCTL, &vp)) < 0) { -+ netdev_dbg(dev->net, "can't read USBCTL, %d\n", retval); -+ goto done; -+ } -+ usbctl = vp; -+ nc_dump_usbctl(dev, usbctl); -+ -+ nc_register_write(dev, REG_USBCTL, -+ USBCTL_FLUSH_THIS | USBCTL_FLUSH_OTHER); -+ -+ if ((retval = nc_register_read(dev, REG_TTL, &vp)) < 0) { -+ netdev_dbg(dev->net, "can't read TTL, %d\n", retval); -+ goto done; -+ } -+ ttl = vp; -+ // nc_dump_ttl(dev, ttl); -+ -+ nc_register_write(dev, REG_TTL, -+ MK_TTL(NC_READ_TTL_MS, TTL_OTHER(ttl)) ); -+ netdev_dbg(dev->net, "assigned TTL, %d ms\n", NC_READ_TTL_MS); -+ -+ netif_info(dev, link, dev->net, "port %c, peer %sconnected\n", -+ (status & STATUS_PORT_A) ? 'A' : 'B', -+ (status & STATUS_CONN_OTHER) ? "" : "dis"); -+ retval = 0; -+ -+done: -+ return retval; -+} -+ -+static int net1080_check_connect(struct usbnet *dev) -+{ -+ int retval; -+ u16 status; -+ u16 vp; -+ -+ retval = nc_register_read(dev, REG_STATUS, &vp); -+ status = vp; -+ if (retval != 0) { -+ netdev_dbg(dev->net, "net1080_check_conn read - %d\n", retval); -+ return retval; -+ } -+ if ((status & STATUS_CONN_OTHER) != STATUS_CONN_OTHER) -+ return -ENOLINK; -+ return 0; -+} -+ -+static void nc_ensure_sync(struct usbnet *dev) -+{ -+ if (++dev->frame_errors <= 5) -+ return; -+ -+ if (usbnet_write_cmd_async(dev, REQUEST_REGISTER, -+ USB_DIR_OUT | USB_TYPE_VENDOR | -+ USB_RECIP_DEVICE, -+ USBCTL_FLUSH_THIS | -+ USBCTL_FLUSH_OTHER, -+ REG_USBCTL, NULL, 0)) -+ return; -+ -+ netif_dbg(dev, rx_err, dev->net, -+ "flush net1080; too many framing errors\n"); -+ dev->frame_errors = 0; -+} -+ -+static int net1080_rx_fixup(struct usbnet *dev, struct sk_buff *skb) -+{ -+ struct nc_header *header; -+ struct nc_trailer *trailer; -+ u16 hdr_len, packet_len; -+ -+ /* This check is no longer done by usbnet */ -+ if (skb->len < dev->net->hard_header_len) -+ return 0; -+ -+ if (!(skb->len & 0x01)) { -+ netdev_dbg(dev->net, "rx framesize %d range %d..%d mtu %d\n", -+ skb->len, dev->net->hard_header_len, dev->hard_mtu, -+ dev->net->mtu); -+ dev->net->stats.rx_frame_errors++; -+ nc_ensure_sync(dev); -+ return 0; -+ } -+ -+ header = (struct nc_header *) skb->data; -+ hdr_len = le16_to_cpup(&header->hdr_len); -+ packet_len = le16_to_cpup(&header->packet_len); -+ if (FRAMED_SIZE(packet_len) > NC_MAX_PACKET) { -+ dev->net->stats.rx_frame_errors++; -+ netdev_dbg(dev->net, "packet too big, %d\n", packet_len); -+ nc_ensure_sync(dev); -+ return 0; -+ } else if (hdr_len < MIN_HEADER) { -+ dev->net->stats.rx_frame_errors++; -+ netdev_dbg(dev->net, "header too short, %d\n", hdr_len); -+ nc_ensure_sync(dev); -+ return 0; -+ } else if (hdr_len > MIN_HEADER) { -+ // out of band data for us? -+ netdev_dbg(dev->net, "header OOB, %d bytes\n", hdr_len - MIN_HEADER); -+ nc_ensure_sync(dev); -+ // switch (vendor/product ids) { ... } -+ } -+ skb_pull(skb, hdr_len); -+ -+ trailer = (struct nc_trailer *) -+ (skb->data + skb->len - sizeof *trailer); -+ skb_trim(skb, skb->len - sizeof *trailer); -+ -+ if ((packet_len & 0x01) == 0) { -+ if (skb->data [packet_len] != PAD_BYTE) { -+ dev->net->stats.rx_frame_errors++; -+ netdev_dbg(dev->net, "bad pad\n"); -+ return 0; -+ } -+ skb_trim(skb, skb->len - 1); -+ } -+ if (skb->len != packet_len) { -+ dev->net->stats.rx_frame_errors++; -+ netdev_dbg(dev->net, "bad packet len %d (expected %d)\n", -+ skb->len, packet_len); -+ nc_ensure_sync(dev); -+ return 0; -+ } -+ if (header->packet_id != get_unaligned(&trailer->packet_id)) { -+ dev->net->stats.rx_fifo_errors++; -+ netdev_dbg(dev->net, "(2+ dropped) rx packet_id mismatch 0x%x 0x%x\n", -+ le16_to_cpu(header->packet_id), -+ le16_to_cpu(trailer->packet_id)); -+ return 0; -+ } -+#if 0 -+ netdev_dbg(dev->net, "frame hdr_len, -+ header->packet_len, header->packet_id); -+#endif -+ dev->frame_errors = 0; -+ return 1; -+} -+ -+static struct sk_buff * -+net1080_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) -+{ -+ struct sk_buff *skb2; -+ struct nc_header *header = NULL; -+ struct nc_trailer *trailer = NULL; -+ int padlen = sizeof (struct nc_trailer); -+ int len = skb->len; -+ -+ if (!((len + padlen + sizeof (struct nc_header)) & 0x01)) -+ padlen++; -+ if (!skb_cloned(skb)) { -+ int headroom = skb_headroom(skb); -+ int tailroom = skb_tailroom(skb); -+ -+ if (padlen <= tailroom && -+ sizeof(struct nc_header) <= headroom) -+ /* There's enough head and tail room */ -+ goto encapsulate; -+ -+ if ((sizeof (struct nc_header) + padlen) < -+ (headroom + tailroom)) { -+ /* There's enough total room, so just readjust */ -+ skb->data = memmove(skb->head -+ + sizeof (struct nc_header), -+ skb->data, skb->len); -+ skb_set_tail_pointer(skb, len); -+ goto encapsulate; -+ } -+ } -+ -+ /* Create a new skb to use with the correct size */ -+ skb2 = skb_copy_expand(skb, -+ sizeof (struct nc_header), -+ padlen, -+ flags); -+ dev_kfree_skb_any(skb); -+ if (!skb2) -+ return skb2; -+ skb = skb2; -+ -+encapsulate: -+ /* header first */ -+ header = (struct nc_header *) skb_push(skb, sizeof *header); -+ header->hdr_len = cpu_to_le16(sizeof (*header)); -+ header->packet_len = cpu_to_le16(len); -+ header->packet_id = cpu_to_le16((u16)dev->xid++); -+ -+ /* maybe pad; then trailer */ -+ if (!((skb->len + sizeof *trailer) & 0x01)) -+ *skb_put(skb, 1) = PAD_BYTE; -+ trailer = (struct nc_trailer *) skb_put(skb, sizeof *trailer); -+ put_unaligned(header->packet_id, &trailer->packet_id); -+#if 0 -+ netdev_dbg(dev->net, "frame >tx h %d p %d id %d\n", -+ header->hdr_len, header->packet_len, -+ header->packet_id); -+#endif -+ return skb; -+} -+ -+static int net1080_bind(struct usbnet *dev, struct usb_interface *intf) -+{ -+ unsigned extra = sizeof (struct nc_header) -+ + 1 -+ + sizeof (struct nc_trailer); -+ -+ dev->net->hard_header_len += extra; -+ dev->rx_urb_size = dev->net->hard_header_len + dev->net->mtu; -+ dev->hard_mtu = NC_MAX_PACKET; -+ return usbnet_get_endpoints (dev, intf); -+} -+ -+static const struct driver_info net1080_info = { -+ .description = "NetChip TurboCONNECT", -+ .flags = FLAG_POINTTOPOINT | FLAG_FRAMING_NC, -+ .bind = net1080_bind, -+ .reset = net1080_reset, -+ .check_connect = net1080_check_connect, -+ .rx_fixup = net1080_rx_fixup, -+ .tx_fixup = net1080_tx_fixup, -+}; -+ -+static const struct usb_device_id products [] = { -+{ -+ USB_DEVICE(0x0525, 0x1080), // NetChip ref design -+ .driver_info = (unsigned long) &net1080_info, -+}, { -+ USB_DEVICE(0x06D0, 0x0622), // Laplink Gold -+ .driver_info = (unsigned long) &net1080_info, -+}, -+ { }, // END -+}; -+MODULE_DEVICE_TABLE(usb, products); -+ -+static struct usb_driver net1080_driver = { -+ .name = "net1080", -+ .id_table = products, -+ .probe = usbnet_probe, -+ .disconnect = usbnet_disconnect, -+ .suspend = usbnet_suspend, -+ .resume = usbnet_resume, -+ .disable_hub_initiated_lpm = 1, -+}; -+ -+module_usb_driver(net1080_driver); -+ -+MODULE_AUTHOR("David Brownell"); -+MODULE_DESCRIPTION("NetChip 1080 based USB Host-to-Host Links"); -+MODULE_LICENSE("GPL"); -diff -Naur backports-4.2.6-1.org/drivers/net/usb/pegasus.c backports-4.2.6-1/drivers/net/usb/pegasus.c ---- backports-4.2.6-1.org/drivers/net/usb/pegasus.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/pegasus.c 2016-06-28 14:35:17.995307218 +0200 -@@ -0,0 +1,1335 @@ -+/* -+ * Copyright (c) 1999-2013 Petko Manolov (petkan@nucleusys.com) -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ * -+ * ChangeLog: -+ * .... Most of the time spent on reading sources & docs. -+ * v0.2.x First official release for the Linux kernel. -+ * v0.3.0 Beutified and structured, some bugs fixed. -+ * v0.3.x URBifying bulk requests and bugfixing. First relatively -+ * stable release. Still can touch device's registers only -+ * from top-halves. -+ * v0.4.0 Control messages remained unurbified are now URBs. -+ * Now we can touch the HW at any time. -+ * v0.4.9 Control urbs again use process context to wait. Argh... -+ * Some long standing bugs (enable_net_traffic) fixed. -+ * Also nasty trick about resubmiting control urb from -+ * interrupt context used. Please let me know how it -+ * behaves. Pegasus II support added since this version. -+ * TODO: suppressing HCD warnings spewage on disconnect. -+ * v0.4.13 Ethernet address is now set at probe(), not at open() -+ * time as this seems to break dhcpd. -+ * v0.5.0 branch to 2.5.x kernels -+ * v0.5.1 ethtool support added -+ * v0.5.5 rx socket buffers are in a pool and the their allocation -+ * is out of the interrupt routine. -+ * ... -+ * v0.9.3 simplified [get|set]_register(s), async update registers -+ * logic revisited, receive skb_pool removed. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "pegasus.h" -+ -+/* -+ * Version Information -+ */ -+#define DRIVER_VERSION "v0.9.3 (2013/04/25)" -+#define DRIVER_AUTHOR "Petko Manolov " -+#define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver" -+ -+static const char driver_name[] = "pegasus"; -+ -+#undef PEGASUS_WRITE_EEPROM -+#define BMSR_MEDIA (BMSR_10HALF | BMSR_10FULL | BMSR_100HALF | \ -+ BMSR_100FULL | BMSR_ANEGCAPABLE) -+ -+static bool loopback; -+static bool mii_mode; -+static char *devid; -+ -+static struct usb_eth_dev usb_dev_id[] = { -+#define PEGASUS_DEV(pn, vid, pid, flags) \ -+ {.name = pn, .vendor = vid, .device = pid, .private = flags}, -+#define PEGASUS_DEV_CLASS(pn, vid, pid, dclass, flags) \ -+ PEGASUS_DEV(pn, vid, pid, flags) -+#include "pegasus.h" -+#undef PEGASUS_DEV -+#undef PEGASUS_DEV_CLASS -+ {NULL, 0, 0, 0}, -+ {NULL, 0, 0, 0} -+}; -+ -+static struct usb_device_id pegasus_ids[] = { -+#define PEGASUS_DEV(pn, vid, pid, flags) \ -+ {.match_flags = USB_DEVICE_ID_MATCH_DEVICE, .idVendor = vid, .idProduct = pid}, -+/* -+ * The Belkin F8T012xx1 bluetooth adaptor has the same vendor and product -+ * IDs as the Belkin F5D5050, so we need to teach the pegasus driver to -+ * ignore adaptors belonging to the "Wireless" class 0xE0. For this one -+ * case anyway, seeing as the pegasus is for "Wired" adaptors. -+ */ -+#define PEGASUS_DEV_CLASS(pn, vid, pid, dclass, flags) \ -+ {.match_flags = (USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_DEV_CLASS), \ -+ .idVendor = vid, .idProduct = pid, .bDeviceClass = dclass}, -+#include "pegasus.h" -+#undef PEGASUS_DEV -+#undef PEGASUS_DEV_CLASS -+ {}, -+ {} -+}; -+ -+MODULE_AUTHOR(DRIVER_AUTHOR); -+MODULE_DESCRIPTION(DRIVER_DESC); -+MODULE_LICENSE("GPL"); -+module_param(loopback, bool, 0); -+module_param(mii_mode, bool, 0); -+module_param(devid, charp, 0); -+MODULE_PARM_DESC(loopback, "Enable MAC loopback mode (bit 0)"); -+MODULE_PARM_DESC(mii_mode, "Enable HomePNA mode (bit 0),default=MII mode = 0"); -+MODULE_PARM_DESC(devid, "The format is: 'DEV_name:VendorID:DeviceID:Flags'"); -+ -+/* use ethtool to change the level for any given device */ -+static int msg_level = -1; -+module_param(msg_level, int, 0); -+MODULE_PARM_DESC(msg_level, "Override default message level"); -+ -+MODULE_DEVICE_TABLE(usb, pegasus_ids); -+static const struct net_device_ops pegasus_netdev_ops; -+ -+/*****/ -+ -+static void async_ctrl_callback(struct urb *urb) -+{ -+ struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context; -+ int status = urb->status; -+ -+ if (status < 0) -+ dev_dbg(&urb->dev->dev, "%s failed with %d", __func__, status); -+ kfree(req); -+ usb_free_urb(urb); -+} -+ -+static int get_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data) -+{ -+ int ret; -+ -+ ret = usb_control_msg(pegasus->usb, usb_rcvctrlpipe(pegasus->usb, 0), -+ PEGASUS_REQ_GET_REGS, PEGASUS_REQT_READ, 0, -+ indx, data, size, 1000); -+ if (ret < 0) -+ netif_dbg(pegasus, drv, pegasus->net, -+ "%s returned %d\n", __func__, ret); -+ return ret; -+} -+ -+static int set_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data) -+{ -+ int ret; -+ -+ ret = usb_control_msg(pegasus->usb, usb_sndctrlpipe(pegasus->usb, 0), -+ PEGASUS_REQ_SET_REGS, PEGASUS_REQT_WRITE, 0, -+ indx, data, size, 100); -+ if (ret < 0) -+ netif_dbg(pegasus, drv, pegasus->net, -+ "%s returned %d\n", __func__, ret); -+ return ret; -+} -+ -+static int set_register(pegasus_t *pegasus, __u16 indx, __u8 data) -+{ -+ int ret; -+ -+ ret = usb_control_msg(pegasus->usb, usb_sndctrlpipe(pegasus->usb, 0), -+ PEGASUS_REQ_SET_REG, PEGASUS_REQT_WRITE, data, -+ indx, &data, 1, 1000); -+ if (ret < 0) -+ netif_dbg(pegasus, drv, pegasus->net, -+ "%s returned %d\n", __func__, ret); -+ return ret; -+} -+ -+static int update_eth_regs_async(pegasus_t *pegasus) -+{ -+ int ret = -ENOMEM; -+ struct urb *async_urb; -+ struct usb_ctrlrequest *req; -+ -+ req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC); -+ if (req == NULL) -+ return ret; -+ -+ async_urb = usb_alloc_urb(0, GFP_ATOMIC); -+ if (async_urb == NULL) { -+ kfree(req); -+ return ret; -+ } -+ req->bRequestType = PEGASUS_REQT_WRITE; -+ req->bRequest = PEGASUS_REQ_SET_REGS; -+ req->wValue = cpu_to_le16(0); -+ req->wIndex = cpu_to_le16(EthCtrl0); -+ req->wLength = cpu_to_le16(3); -+ -+ usb_fill_control_urb(async_urb, pegasus->usb, -+ usb_sndctrlpipe(pegasus->usb, 0), (void *)req, -+ pegasus->eth_regs, 3, async_ctrl_callback, req); -+ -+ ret = usb_submit_urb(async_urb, GFP_ATOMIC); -+ if (ret) { -+ if (ret == -ENODEV) -+ netif_device_detach(pegasus->net); -+ netif_err(pegasus, drv, pegasus->net, -+ "%s returned %d\n", __func__, ret); -+ } -+ return ret; -+} -+ -+static int __mii_op(pegasus_t *p, __u8 phy, __u8 indx, __u16 *regd, __u8 cmd) -+{ -+ int i; -+ __u8 data[4] = { phy, 0, 0, indx }; -+ __le16 regdi; -+ int ret = -ETIMEDOUT; -+ -+ if (cmd & PHY_WRITE) { -+ __le16 *t = (__le16 *) & data[1]; -+ *t = cpu_to_le16(*regd); -+ } -+ set_register(p, PhyCtrl, 0); -+ set_registers(p, PhyAddr, sizeof(data), data); -+ set_register(p, PhyCtrl, (indx | cmd)); -+ for (i = 0; i < REG_TIMEOUT; i++) { -+ ret = get_registers(p, PhyCtrl, 1, data); -+ if (ret < 0) -+ goto fail; -+ if (data[0] & PHY_DONE) -+ break; -+ } -+ if (i >= REG_TIMEOUT) -+ goto fail; -+ if (cmd & PHY_READ) { -+ ret = get_registers(p, PhyData, 2, ®di); -+ *regd = le16_to_cpu(regdi); -+ return ret; -+ } -+ return 0; -+fail: -+ netif_dbg(p, drv, p->net, "%s failed\n", __func__); -+ return ret; -+} -+ -+/* Returns non-negative int on success, error on failure */ -+static int read_mii_word(pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 *regd) -+{ -+ return __mii_op(pegasus, phy, indx, regd, PHY_READ); -+} -+ -+/* Returns zero on success, error on failure */ -+static int write_mii_word(pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 *regd) -+{ -+ return __mii_op(pegasus, phy, indx, regd, PHY_WRITE); -+} -+ -+static int mdio_read(struct net_device *dev, int phy_id, int loc) -+{ -+ pegasus_t *pegasus = netdev_priv(dev); -+ u16 res; -+ -+ read_mii_word(pegasus, phy_id, loc, &res); -+ return (int)res; -+} -+ -+static void mdio_write(struct net_device *dev, int phy_id, int loc, int val) -+{ -+ pegasus_t *pegasus = netdev_priv(dev); -+ u16 data = val; -+ -+ write_mii_word(pegasus, phy_id, loc, &data); -+} -+ -+static int read_eprom_word(pegasus_t *pegasus, __u8 index, __u16 *retdata) -+{ -+ int i; -+ __u8 tmp; -+ __le16 retdatai; -+ int ret; -+ -+ set_register(pegasus, EpromCtrl, 0); -+ set_register(pegasus, EpromOffset, index); -+ set_register(pegasus, EpromCtrl, EPROM_READ); -+ -+ for (i = 0; i < REG_TIMEOUT; i++) { -+ ret = get_registers(pegasus, EpromCtrl, 1, &tmp); -+ if (tmp & EPROM_DONE) -+ break; -+ if (ret == -ESHUTDOWN) -+ goto fail; -+ } -+ if (i >= REG_TIMEOUT) -+ goto fail; -+ -+ ret = get_registers(pegasus, EpromData, 2, &retdatai); -+ *retdata = le16_to_cpu(retdatai); -+ return ret; -+ -+fail: -+ netif_warn(pegasus, drv, pegasus->net, "%s failed\n", __func__); -+ return -ETIMEDOUT; -+} -+ -+#ifdef PEGASUS_WRITE_EEPROM -+static inline void enable_eprom_write(pegasus_t *pegasus) -+{ -+ __u8 tmp; -+ -+ get_registers(pegasus, EthCtrl2, 1, &tmp); -+ set_register(pegasus, EthCtrl2, tmp | EPROM_WR_ENABLE); -+} -+ -+static inline void disable_eprom_write(pegasus_t *pegasus) -+{ -+ __u8 tmp; -+ -+ get_registers(pegasus, EthCtrl2, 1, &tmp); -+ set_register(pegasus, EpromCtrl, 0); -+ set_register(pegasus, EthCtrl2, tmp & ~EPROM_WR_ENABLE); -+} -+ -+static int write_eprom_word(pegasus_t *pegasus, __u8 index, __u16 data) -+{ -+ int i; -+ __u8 tmp, d[4] = { 0x3f, 0, 0, EPROM_WRITE }; -+ int ret; -+ __le16 le_data = cpu_to_le16(data); -+ -+ set_registers(pegasus, EpromOffset, 4, d); -+ enable_eprom_write(pegasus); -+ set_register(pegasus, EpromOffset, index); -+ set_registers(pegasus, EpromData, 2, &le_data); -+ set_register(pegasus, EpromCtrl, EPROM_WRITE); -+ -+ for (i = 0; i < REG_TIMEOUT; i++) { -+ ret = get_registers(pegasus, EpromCtrl, 1, &tmp); -+ if (ret == -ESHUTDOWN) -+ goto fail; -+ if (tmp & EPROM_DONE) -+ break; -+ } -+ disable_eprom_write(pegasus); -+ if (i >= REG_TIMEOUT) -+ goto fail; -+ -+ return ret; -+ -+fail: -+ netif_warn(pegasus, drv, pegasus->net, "%s failed\n", __func__); -+ return -ETIMEDOUT; -+} -+#endif /* PEGASUS_WRITE_EEPROM */ -+ -+static inline void get_node_id(pegasus_t *pegasus, __u8 *id) -+{ -+ int i; -+ __u16 w16; -+ -+ for (i = 0; i < 3; i++) { -+ read_eprom_word(pegasus, i, &w16); -+ ((__le16 *) id)[i] = cpu_to_le16(w16); -+ } -+} -+ -+static void set_ethernet_addr(pegasus_t *pegasus) -+{ -+ __u8 node_id[6]; -+ -+ if (pegasus->features & PEGASUS_II) { -+ get_registers(pegasus, 0x10, sizeof(node_id), node_id); -+ } else { -+ get_node_id(pegasus, node_id); -+ set_registers(pegasus, EthID, sizeof(node_id), node_id); -+ } -+ memcpy(pegasus->net->dev_addr, node_id, sizeof(node_id)); -+} -+ -+static inline int reset_mac(pegasus_t *pegasus) -+{ -+ __u8 data = 0x8; -+ int i; -+ -+ set_register(pegasus, EthCtrl1, data); -+ for (i = 0; i < REG_TIMEOUT; i++) { -+ get_registers(pegasus, EthCtrl1, 1, &data); -+ if (~data & 0x08) { -+ if (loopback) -+ break; -+ if (mii_mode && (pegasus->features & HAS_HOME_PNA)) -+ set_register(pegasus, Gpio1, 0x34); -+ else -+ set_register(pegasus, Gpio1, 0x26); -+ set_register(pegasus, Gpio0, pegasus->features); -+ set_register(pegasus, Gpio0, DEFAULT_GPIO_SET); -+ break; -+ } -+ } -+ if (i == REG_TIMEOUT) -+ return -ETIMEDOUT; -+ -+ if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS || -+ usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK) { -+ set_register(pegasus, Gpio0, 0x24); -+ set_register(pegasus, Gpio0, 0x26); -+ } -+ if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_ELCON) { -+ __u16 auxmode; -+ read_mii_word(pegasus, 3, 0x1b, &auxmode); -+ auxmode |= 4; -+ write_mii_word(pegasus, 3, 0x1b, &auxmode); -+ } -+ -+ return 0; -+} -+ -+static int enable_net_traffic(struct net_device *dev, struct usb_device *usb) -+{ -+ __u16 linkpart; -+ __u8 data[4]; -+ pegasus_t *pegasus = netdev_priv(dev); -+ int ret; -+ -+ read_mii_word(pegasus, pegasus->phy, MII_LPA, &linkpart); -+ data[0] = 0xc9; -+ data[1] = 0; -+ if (linkpart & (ADVERTISE_100FULL | ADVERTISE_10FULL)) -+ data[1] |= 0x20; /* set full duplex */ -+ if (linkpart & (ADVERTISE_100FULL | ADVERTISE_100HALF)) -+ data[1] |= 0x10; /* set 100 Mbps */ -+ if (mii_mode) -+ data[1] = 0; -+ data[2] = loopback ? 0x09 : 0x01; -+ -+ memcpy(pegasus->eth_regs, data, sizeof(data)); -+ ret = set_registers(pegasus, EthCtrl0, 3, data); -+ -+ if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS || -+ usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS2 || -+ usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK) { -+ u16 auxmode; -+ read_mii_word(pegasus, 0, 0x1b, &auxmode); -+ auxmode |= 4; -+ write_mii_word(pegasus, 0, 0x1b, &auxmode); -+ } -+ -+ return ret; -+} -+ -+static void read_bulk_callback(struct urb *urb) -+{ -+ pegasus_t *pegasus = urb->context; -+ struct net_device *net; -+ int rx_status, count = urb->actual_length; -+ int status = urb->status; -+ u8 *buf = urb->transfer_buffer; -+ __u16 pkt_len; -+ -+ if (!pegasus) -+ return; -+ -+ net = pegasus->net; -+ if (!netif_device_present(net) || !netif_running(net)) -+ return; -+ -+ switch (status) { -+ case 0: -+ break; -+ case -ETIME: -+ netif_dbg(pegasus, rx_err, net, "reset MAC\n"); -+ pegasus->flags &= ~PEGASUS_RX_BUSY; -+ break; -+ case -EPIPE: /* stall, or disconnect from TT */ -+ /* FIXME schedule work to clear the halt */ -+ netif_warn(pegasus, rx_err, net, "no rx stall recovery\n"); -+ return; -+ case -ENOENT: -+ case -ECONNRESET: -+ case -ESHUTDOWN: -+ netif_dbg(pegasus, ifdown, net, "rx unlink, %d\n", status); -+ return; -+ default: -+ netif_dbg(pegasus, rx_err, net, "RX status %d\n", status); -+ goto goon; -+ } -+ -+ if (!count || count < 4) -+ goto goon; -+ -+ rx_status = buf[count - 2]; -+ if (rx_status & 0x1e) { -+ netif_dbg(pegasus, rx_err, net, -+ "RX packet error %x\n", rx_status); -+ pegasus->stats.rx_errors++; -+ if (rx_status & 0x06) /* long or runt */ -+ pegasus->stats.rx_length_errors++; -+ if (rx_status & 0x08) -+ pegasus->stats.rx_crc_errors++; -+ if (rx_status & 0x10) /* extra bits */ -+ pegasus->stats.rx_frame_errors++; -+ goto goon; -+ } -+ if (pegasus->chip == 0x8513) { -+ pkt_len = le32_to_cpu(*(__le32 *)urb->transfer_buffer); -+ pkt_len &= 0x0fff; -+ pegasus->rx_skb->data += 2; -+ } else { -+ pkt_len = buf[count - 3] << 8; -+ pkt_len += buf[count - 4]; -+ pkt_len &= 0xfff; -+ pkt_len -= 8; -+ } -+ -+ /* -+ * If the packet is unreasonably long, quietly drop it rather than -+ * kernel panicing by calling skb_put. -+ */ -+ if (pkt_len > PEGASUS_MTU) -+ goto goon; -+ -+ /* -+ * at this point we are sure pegasus->rx_skb != NULL -+ * so we go ahead and pass up the packet. -+ */ -+ skb_put(pegasus->rx_skb, pkt_len); -+ pegasus->rx_skb->protocol = eth_type_trans(pegasus->rx_skb, net); -+ netif_rx(pegasus->rx_skb); -+ pegasus->stats.rx_packets++; -+ pegasus->stats.rx_bytes += pkt_len; -+ -+ if (pegasus->flags & PEGASUS_UNPLUG) -+ return; -+ -+ pegasus->rx_skb = __netdev_alloc_skb_ip_align(pegasus->net, PEGASUS_MTU, -+ GFP_ATOMIC); -+ -+ if (pegasus->rx_skb == NULL) -+ goto tl_sched; -+goon: -+ usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb, -+ usb_rcvbulkpipe(pegasus->usb, 1), -+ pegasus->rx_skb->data, PEGASUS_MTU + 8, -+ read_bulk_callback, pegasus); -+ rx_status = usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC); -+ if (rx_status == -ENODEV) -+ netif_device_detach(pegasus->net); -+ else if (rx_status) { -+ pegasus->flags |= PEGASUS_RX_URB_FAIL; -+ goto tl_sched; -+ } else { -+ pegasus->flags &= ~PEGASUS_RX_URB_FAIL; -+ } -+ -+ return; -+ -+tl_sched: -+ tasklet_schedule(&pegasus->rx_tl); -+} -+ -+static void rx_fixup(unsigned long data) -+{ -+ pegasus_t *pegasus; -+ int status; -+ -+ pegasus = (pegasus_t *) data; -+ if (pegasus->flags & PEGASUS_UNPLUG) -+ return; -+ -+ if (pegasus->flags & PEGASUS_RX_URB_FAIL) -+ if (pegasus->rx_skb) -+ goto try_again; -+ if (pegasus->rx_skb == NULL) -+ pegasus->rx_skb = __netdev_alloc_skb_ip_align(pegasus->net, -+ PEGASUS_MTU, -+ GFP_ATOMIC); -+ if (pegasus->rx_skb == NULL) { -+ netif_warn(pegasus, rx_err, pegasus->net, "low on memory\n"); -+ tasklet_schedule(&pegasus->rx_tl); -+ return; -+ } -+ usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb, -+ usb_rcvbulkpipe(pegasus->usb, 1), -+ pegasus->rx_skb->data, PEGASUS_MTU + 8, -+ read_bulk_callback, pegasus); -+try_again: -+ status = usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC); -+ if (status == -ENODEV) -+ netif_device_detach(pegasus->net); -+ else if (status) { -+ pegasus->flags |= PEGASUS_RX_URB_FAIL; -+ tasklet_schedule(&pegasus->rx_tl); -+ } else { -+ pegasus->flags &= ~PEGASUS_RX_URB_FAIL; -+ } -+} -+ -+static void write_bulk_callback(struct urb *urb) -+{ -+ pegasus_t *pegasus = urb->context; -+ struct net_device *net; -+ int status = urb->status; -+ -+ if (!pegasus) -+ return; -+ -+ net = pegasus->net; -+ -+ if (!netif_device_present(net) || !netif_running(net)) -+ return; -+ -+ switch (status) { -+ case -EPIPE: -+ /* FIXME schedule_work() to clear the tx halt */ -+ netif_stop_queue(net); -+ netif_warn(pegasus, tx_err, net, "no tx stall recovery\n"); -+ return; -+ case -ENOENT: -+ case -ECONNRESET: -+ case -ESHUTDOWN: -+ netif_dbg(pegasus, ifdown, net, "tx unlink, %d\n", status); -+ return; -+ default: -+ netif_info(pegasus, tx_err, net, "TX status %d\n", status); -+ /* FALL THROUGH */ -+ case 0: -+ break; -+ } -+ -+ net->trans_start = jiffies; /* prevent tx timeout */ -+ netif_wake_queue(net); -+} -+ -+static void intr_callback(struct urb *urb) -+{ -+ pegasus_t *pegasus = urb->context; -+ struct net_device *net; -+ int res, status = urb->status; -+ -+ if (!pegasus) -+ return; -+ net = pegasus->net; -+ -+ switch (status) { -+ case 0: -+ break; -+ case -ECONNRESET: /* unlink */ -+ case -ENOENT: -+ case -ESHUTDOWN: -+ return; -+ default: -+ /* some Pegasus-I products report LOTS of data -+ * toggle errors... avoid log spamming -+ */ -+ netif_dbg(pegasus, timer, net, "intr status %d\n", status); -+ } -+ -+ if (urb->actual_length >= 6) { -+ u8 *d = urb->transfer_buffer; -+ -+ /* byte 0 == tx_status1, reg 2B */ -+ if (d[0] & (TX_UNDERRUN|EXCESSIVE_COL -+ |LATE_COL|JABBER_TIMEOUT)) { -+ pegasus->stats.tx_errors++; -+ if (d[0] & TX_UNDERRUN) -+ pegasus->stats.tx_fifo_errors++; -+ if (d[0] & (EXCESSIVE_COL | JABBER_TIMEOUT)) -+ pegasus->stats.tx_aborted_errors++; -+ if (d[0] & LATE_COL) -+ pegasus->stats.tx_window_errors++; -+ } -+ -+ /* d[5].LINK_STATUS lies on some adapters. -+ * d[0].NO_CARRIER kicks in only with failed TX. -+ * ... so monitoring with MII may be safest. -+ */ -+ -+ /* bytes 3-4 == rx_lostpkt, reg 2E/2F */ -+ pegasus->stats.rx_missed_errors += ((d[3] & 0x7f) << 8) | d[4]; -+ } -+ -+ res = usb_submit_urb(urb, GFP_ATOMIC); -+ if (res == -ENODEV) -+ netif_device_detach(pegasus->net); -+ if (res) -+ netif_err(pegasus, timer, net, -+ "can't resubmit interrupt urb, %d\n", res); -+} -+ -+static void pegasus_tx_timeout(struct net_device *net) -+{ -+ pegasus_t *pegasus = netdev_priv(net); -+ netif_warn(pegasus, timer, net, "tx timeout\n"); -+ usb_unlink_urb(pegasus->tx_urb); -+ pegasus->stats.tx_errors++; -+} -+ -+static netdev_tx_t pegasus_start_xmit(struct sk_buff *skb, -+ struct net_device *net) -+{ -+ pegasus_t *pegasus = netdev_priv(net); -+ int count = ((skb->len + 2) & 0x3f) ? skb->len + 2 : skb->len + 3; -+ int res; -+ __u16 l16 = skb->len; -+ -+ netif_stop_queue(net); -+ -+ ((__le16 *) pegasus->tx_buff)[0] = cpu_to_le16(l16); -+ skb_copy_from_linear_data(skb, pegasus->tx_buff + 2, skb->len); -+ usb_fill_bulk_urb(pegasus->tx_urb, pegasus->usb, -+ usb_sndbulkpipe(pegasus->usb, 2), -+ pegasus->tx_buff, count, -+ write_bulk_callback, pegasus); -+ if ((res = usb_submit_urb(pegasus->tx_urb, GFP_ATOMIC))) { -+ netif_warn(pegasus, tx_err, net, "fail tx, %d\n", res); -+ switch (res) { -+ case -EPIPE: /* stall, or disconnect from TT */ -+ /* cleanup should already have been scheduled */ -+ break; -+ case -ENODEV: /* disconnect() upcoming */ -+ case -EPERM: -+ netif_device_detach(pegasus->net); -+ break; -+ default: -+ pegasus->stats.tx_errors++; -+ netif_start_queue(net); -+ } -+ } else { -+ pegasus->stats.tx_packets++; -+ pegasus->stats.tx_bytes += skb->len; -+ } -+ dev_kfree_skb(skb); -+ -+ return NETDEV_TX_OK; -+} -+ -+static struct net_device_stats *pegasus_netdev_stats(struct net_device *dev) -+{ -+ return &((pegasus_t *) netdev_priv(dev))->stats; -+} -+ -+static inline void disable_net_traffic(pegasus_t *pegasus) -+{ -+ __le16 tmp = cpu_to_le16(0); -+ -+ set_registers(pegasus, EthCtrl0, sizeof(tmp), &tmp); -+} -+ -+static inline void get_interrupt_interval(pegasus_t *pegasus) -+{ -+ u16 data; -+ u8 interval; -+ -+ read_eprom_word(pegasus, 4, &data); -+ interval = data >> 8; -+ if (pegasus->usb->speed != USB_SPEED_HIGH) { -+ if (interval < 0x80) { -+ netif_info(pegasus, timer, pegasus->net, -+ "intr interval changed from %ums to %ums\n", -+ interval, 0x80); -+ interval = 0x80; -+ data = (data & 0x00FF) | ((u16)interval << 8); -+#ifdef PEGASUS_WRITE_EEPROM -+ write_eprom_word(pegasus, 4, data); -+#endif -+ } -+ } -+ pegasus->intr_interval = interval; -+} -+ -+static void set_carrier(struct net_device *net) -+{ -+ pegasus_t *pegasus = netdev_priv(net); -+ u16 tmp; -+ -+ if (read_mii_word(pegasus, pegasus->phy, MII_BMSR, &tmp)) -+ return; -+ -+ if (tmp & BMSR_LSTATUS) -+ netif_carrier_on(net); -+ else -+ netif_carrier_off(net); -+} -+ -+static void free_all_urbs(pegasus_t *pegasus) -+{ -+ usb_free_urb(pegasus->intr_urb); -+ usb_free_urb(pegasus->tx_urb); -+ usb_free_urb(pegasus->rx_urb); -+} -+ -+static void unlink_all_urbs(pegasus_t *pegasus) -+{ -+ usb_kill_urb(pegasus->intr_urb); -+ usb_kill_urb(pegasus->tx_urb); -+ usb_kill_urb(pegasus->rx_urb); -+} -+ -+static int alloc_urbs(pegasus_t *pegasus) -+{ -+ int res = -ENOMEM; -+ -+ pegasus->rx_urb = usb_alloc_urb(0, GFP_KERNEL); -+ if (!pegasus->rx_urb) { -+ return res; -+ } -+ pegasus->tx_urb = usb_alloc_urb(0, GFP_KERNEL); -+ if (!pegasus->tx_urb) { -+ usb_free_urb(pegasus->rx_urb); -+ return res; -+ } -+ pegasus->intr_urb = usb_alloc_urb(0, GFP_KERNEL); -+ if (!pegasus->intr_urb) { -+ usb_free_urb(pegasus->tx_urb); -+ usb_free_urb(pegasus->rx_urb); -+ return res; -+ } -+ -+ return 0; -+} -+ -+static int pegasus_open(struct net_device *net) -+{ -+ pegasus_t *pegasus = netdev_priv(net); -+ int res=-ENOMEM; -+ -+ if (pegasus->rx_skb == NULL) -+ pegasus->rx_skb = __netdev_alloc_skb_ip_align(pegasus->net, -+ PEGASUS_MTU, -+ GFP_KERNEL); -+ if (!pegasus->rx_skb) -+ goto exit; -+ -+ res = set_registers(pegasus, EthID, 6, net->dev_addr); -+ -+ usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb, -+ usb_rcvbulkpipe(pegasus->usb, 1), -+ pegasus->rx_skb->data, PEGASUS_MTU + 8, -+ read_bulk_callback, pegasus); -+ if ((res = usb_submit_urb(pegasus->rx_urb, GFP_KERNEL))) { -+ if (res == -ENODEV) -+ netif_device_detach(pegasus->net); -+ netif_dbg(pegasus, ifup, net, "failed rx_urb, %d\n", res); -+ goto exit; -+ } -+ -+ usb_fill_int_urb(pegasus->intr_urb, pegasus->usb, -+ usb_rcvintpipe(pegasus->usb, 3), -+ pegasus->intr_buff, sizeof(pegasus->intr_buff), -+ intr_callback, pegasus, pegasus->intr_interval); -+ if ((res = usb_submit_urb(pegasus->intr_urb, GFP_KERNEL))) { -+ if (res == -ENODEV) -+ netif_device_detach(pegasus->net); -+ netif_dbg(pegasus, ifup, net, "failed intr_urb, %d\n", res); -+ usb_kill_urb(pegasus->rx_urb); -+ goto exit; -+ } -+ res = enable_net_traffic(net, pegasus->usb); -+ if (res < 0) { -+ netif_dbg(pegasus, ifup, net, -+ "can't enable_net_traffic() - %d\n", res); -+ res = -EIO; -+ usb_kill_urb(pegasus->rx_urb); -+ usb_kill_urb(pegasus->intr_urb); -+ goto exit; -+ } -+ set_carrier(net); -+ netif_start_queue(net); -+ netif_dbg(pegasus, ifup, net, "open\n"); -+ res = 0; -+exit: -+ return res; -+} -+ -+static int pegasus_close(struct net_device *net) -+{ -+ pegasus_t *pegasus = netdev_priv(net); -+ -+ netif_stop_queue(net); -+ if (!(pegasus->flags & PEGASUS_UNPLUG)) -+ disable_net_traffic(pegasus); -+ tasklet_kill(&pegasus->rx_tl); -+ unlink_all_urbs(pegasus); -+ -+ return 0; -+} -+ -+static void pegasus_get_drvinfo(struct net_device *dev, -+ struct ethtool_drvinfo *info) -+{ -+ pegasus_t *pegasus = netdev_priv(dev); -+ -+ strlcpy(info->driver, driver_name, sizeof(info->driver)); -+ strlcpy(info->version, DRIVER_VERSION, sizeof(info->version)); -+ usb_make_path(pegasus->usb, info->bus_info, sizeof(info->bus_info)); -+} -+ -+/* also handles three patterns of some kind in hardware */ -+#define WOL_SUPPORTED (WAKE_MAGIC|WAKE_PHY) -+ -+static void -+pegasus_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) -+{ -+ pegasus_t *pegasus = netdev_priv(dev); -+ -+ wol->supported = WAKE_MAGIC | WAKE_PHY; -+ wol->wolopts = pegasus->wolopts; -+} -+ -+static int -+pegasus_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) -+{ -+ pegasus_t *pegasus = netdev_priv(dev); -+ u8 reg78 = 0x04; -+ int ret; -+ -+ if (wol->wolopts & ~WOL_SUPPORTED) -+ return -EINVAL; -+ -+ if (wol->wolopts & WAKE_MAGIC) -+ reg78 |= 0x80; -+ if (wol->wolopts & WAKE_PHY) -+ reg78 |= 0x40; -+ /* FIXME this 0x10 bit still needs to get set in the chip... */ -+ if (wol->wolopts) -+ pegasus->eth_regs[0] |= 0x10; -+ else -+ pegasus->eth_regs[0] &= ~0x10; -+ pegasus->wolopts = wol->wolopts; -+ -+ ret = set_register(pegasus, WakeupControl, reg78); -+ if (!ret) -+ ret = device_set_wakeup_enable(&pegasus->usb->dev, -+ wol->wolopts); -+ return ret; -+} -+ -+static inline void pegasus_reset_wol(struct net_device *dev) -+{ -+ struct ethtool_wolinfo wol; -+ -+ memset(&wol, 0, sizeof wol); -+ (void) pegasus_set_wol(dev, &wol); -+} -+ -+static int -+pegasus_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) -+{ -+ pegasus_t *pegasus; -+ -+ pegasus = netdev_priv(dev); -+ mii_ethtool_gset(&pegasus->mii, ecmd); -+ return 0; -+} -+ -+static int -+pegasus_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) -+{ -+ pegasus_t *pegasus = netdev_priv(dev); -+ return mii_ethtool_sset(&pegasus->mii, ecmd); -+} -+ -+static int pegasus_nway_reset(struct net_device *dev) -+{ -+ pegasus_t *pegasus = netdev_priv(dev); -+ return mii_nway_restart(&pegasus->mii); -+} -+ -+static u32 pegasus_get_link(struct net_device *dev) -+{ -+ pegasus_t *pegasus = netdev_priv(dev); -+ return mii_link_ok(&pegasus->mii); -+} -+ -+static u32 pegasus_get_msglevel(struct net_device *dev) -+{ -+ pegasus_t *pegasus = netdev_priv(dev); -+ return pegasus->msg_enable; -+} -+ -+static void pegasus_set_msglevel(struct net_device *dev, u32 v) -+{ -+ pegasus_t *pegasus = netdev_priv(dev); -+ pegasus->msg_enable = v; -+} -+ -+static const struct ethtool_ops ops = { -+ .get_drvinfo = pegasus_get_drvinfo, -+ .get_settings = pegasus_get_settings, -+ .set_settings = pegasus_set_settings, -+ .nway_reset = pegasus_nway_reset, -+ .get_link = pegasus_get_link, -+ .get_msglevel = pegasus_get_msglevel, -+ .set_msglevel = pegasus_set_msglevel, -+ .get_wol = pegasus_get_wol, -+ .set_wol = pegasus_set_wol, -+}; -+ -+static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd) -+{ -+ __u16 *data = (__u16 *) &rq->ifr_ifru; -+ pegasus_t *pegasus = netdev_priv(net); -+ int res; -+ -+ switch (cmd) { -+ case SIOCDEVPRIVATE: -+ data[0] = pegasus->phy; -+ case SIOCDEVPRIVATE + 1: -+ read_mii_word(pegasus, data[0], data[1] & 0x1f, &data[3]); -+ res = 0; -+ break; -+ case SIOCDEVPRIVATE + 2: -+ if (!capable(CAP_NET_ADMIN)) -+ return -EPERM; -+ write_mii_word(pegasus, pegasus->phy, data[1] & 0x1f, &data[2]); -+ res = 0; -+ break; -+ default: -+ res = -EOPNOTSUPP; -+ } -+ return res; -+} -+ -+static void pegasus_set_multicast(struct net_device *net) -+{ -+ pegasus_t *pegasus = netdev_priv(net); -+ -+ if (net->flags & IFF_PROMISC) { -+ pegasus->eth_regs[EthCtrl2] |= RX_PROMISCUOUS; -+ netif_info(pegasus, link, net, "Promiscuous mode enabled\n"); -+ } else if (!netdev_mc_empty(net) || (net->flags & IFF_ALLMULTI)) { -+ pegasus->eth_regs[EthCtrl0] |= RX_MULTICAST; -+ pegasus->eth_regs[EthCtrl2] &= ~RX_PROMISCUOUS; -+ netif_dbg(pegasus, link, net, "set allmulti\n"); -+ } else { -+ pegasus->eth_regs[EthCtrl0] &= ~RX_MULTICAST; -+ pegasus->eth_regs[EthCtrl2] &= ~RX_PROMISCUOUS; -+ } -+ update_eth_regs_async(pegasus); -+} -+ -+static __u8 mii_phy_probe(pegasus_t *pegasus) -+{ -+ int i; -+ __u16 tmp; -+ -+ for (i = 0; i < 32; i++) { -+ read_mii_word(pegasus, i, MII_BMSR, &tmp); -+ if (tmp == 0 || tmp == 0xffff || (tmp & BMSR_MEDIA) == 0) -+ continue; -+ else -+ return i; -+ } -+ -+ return 0xff; -+} -+ -+static inline void setup_pegasus_II(pegasus_t *pegasus) -+{ -+ __u8 data = 0xa5; -+ -+ set_register(pegasus, Reg1d, 0); -+ set_register(pegasus, Reg7b, 1); -+ mdelay(100); -+ if ((pegasus->features & HAS_HOME_PNA) && mii_mode) -+ set_register(pegasus, Reg7b, 0); -+ else -+ set_register(pegasus, Reg7b, 2); -+ -+ set_register(pegasus, 0x83, data); -+ get_registers(pegasus, 0x83, 1, &data); -+ -+ if (data == 0xa5) -+ pegasus->chip = 0x8513; -+ else -+ pegasus->chip = 0; -+ -+ set_register(pegasus, 0x80, 0xc0); -+ set_register(pegasus, 0x83, 0xff); -+ set_register(pegasus, 0x84, 0x01); -+ -+ if (pegasus->features & HAS_HOME_PNA && mii_mode) -+ set_register(pegasus, Reg81, 6); -+ else -+ set_register(pegasus, Reg81, 2); -+} -+ -+ -+static int pegasus_count; -+static struct workqueue_struct *pegasus_workqueue; -+#define CARRIER_CHECK_DELAY (2 * HZ) -+ -+static void check_carrier(struct work_struct *work) -+{ -+ pegasus_t *pegasus = container_of(work, pegasus_t, carrier_check.work); -+ set_carrier(pegasus->net); -+ if (!(pegasus->flags & PEGASUS_UNPLUG)) { -+ queue_delayed_work(pegasus_workqueue, &pegasus->carrier_check, -+ CARRIER_CHECK_DELAY); -+ } -+} -+ -+static int pegasus_blacklisted(struct usb_device *udev) -+{ -+ struct usb_device_descriptor *udd = &udev->descriptor; -+ -+ /* Special quirk to keep the driver from handling the Belkin Bluetooth -+ * dongle which happens to have the same ID. -+ */ -+ if ((udd->idVendor == cpu_to_le16(VENDOR_BELKIN)) && -+ (udd->idProduct == cpu_to_le16(0x0121)) && -+ (udd->bDeviceClass == USB_CLASS_WIRELESS_CONTROLLER) && -+ (udd->bDeviceProtocol == 1)) -+ return 1; -+ -+ return 0; -+} -+ -+/* we rely on probe() and remove() being serialized so we -+ * don't need extra locking on pegasus_count. -+ */ -+static void pegasus_dec_workqueue(void) -+{ -+ pegasus_count--; -+ if (pegasus_count == 0) { -+ destroy_workqueue(pegasus_workqueue); -+ pegasus_workqueue = NULL; -+ } -+} -+ -+static int pegasus_probe(struct usb_interface *intf, -+ const struct usb_device_id *id) -+{ -+ struct usb_device *dev = interface_to_usbdev(intf); -+ struct net_device *net; -+ pegasus_t *pegasus; -+ int dev_index = id - pegasus_ids; -+ int res = -ENOMEM; -+ -+ if (pegasus_blacklisted(dev)) -+ return -ENODEV; -+ -+ if (pegasus_count == 0) { -+ pegasus_workqueue = create_singlethread_workqueue("pegasus"); -+ if (!pegasus_workqueue) -+ return -ENOMEM; -+ } -+ pegasus_count++; -+ -+ net = alloc_etherdev(sizeof(struct pegasus)); -+ if (!net) -+ goto out; -+ -+ pegasus = netdev_priv(net); -+ pegasus->dev_index = dev_index; -+ -+ res = alloc_urbs(pegasus); -+ if (res < 0) { -+ dev_err(&intf->dev, "can't allocate %s\n", "urbs"); -+ goto out1; -+ } -+ -+ tasklet_init(&pegasus->rx_tl, rx_fixup, (unsigned long) pegasus); -+ -+ INIT_DELAYED_WORK(&pegasus->carrier_check, check_carrier); -+ -+ pegasus->intf = intf; -+ pegasus->usb = dev; -+ pegasus->net = net; -+ -+ -+ net->watchdog_timeo = PEGASUS_TX_TIMEOUT; -+ net->netdev_ops = &pegasus_netdev_ops; -+ net->ethtool_ops = &ops; -+ pegasus->mii.dev = net; -+ pegasus->mii.mdio_read = mdio_read; -+ pegasus->mii.mdio_write = mdio_write; -+ pegasus->mii.phy_id_mask = 0x1f; -+ pegasus->mii.reg_num_mask = 0x1f; -+ pegasus->msg_enable = netif_msg_init(msg_level, NETIF_MSG_DRV -+ | NETIF_MSG_PROBE | NETIF_MSG_LINK); -+ -+ pegasus->features = usb_dev_id[dev_index].private; -+ get_interrupt_interval(pegasus); -+ if (reset_mac(pegasus)) { -+ dev_err(&intf->dev, "can't reset MAC\n"); -+ res = -EIO; -+ goto out2; -+ } -+ set_ethernet_addr(pegasus); -+ if (pegasus->features & PEGASUS_II) { -+ dev_info(&intf->dev, "setup Pegasus II specific registers\n"); -+ setup_pegasus_II(pegasus); -+ } -+ pegasus->phy = mii_phy_probe(pegasus); -+ if (pegasus->phy == 0xff) { -+ dev_warn(&intf->dev, "can't locate MII phy, using default\n"); -+ pegasus->phy = 1; -+ } -+ pegasus->mii.phy_id = pegasus->phy; -+ usb_set_intfdata(intf, pegasus); -+ SET_NETDEV_DEV(net, &intf->dev); -+ pegasus_reset_wol(net); -+ res = register_netdev(net); -+ if (res) -+ goto out3; -+ queue_delayed_work(pegasus_workqueue, &pegasus->carrier_check, -+ CARRIER_CHECK_DELAY); -+ dev_info(&intf->dev, "%s, %s, %pM\n", net->name, -+ usb_dev_id[dev_index].name, net->dev_addr); -+ return 0; -+ -+out3: -+ usb_set_intfdata(intf, NULL); -+out2: -+ free_all_urbs(pegasus); -+out1: -+ free_netdev(net); -+out: -+ pegasus_dec_workqueue(); -+ return res; -+} -+ -+static void pegasus_disconnect(struct usb_interface *intf) -+{ -+ struct pegasus *pegasus = usb_get_intfdata(intf); -+ -+ usb_set_intfdata(intf, NULL); -+ if (!pegasus) { -+ dev_dbg(&intf->dev, "unregistering non-bound device?\n"); -+ return; -+ } -+ -+ pegasus->flags |= PEGASUS_UNPLUG; -+ cancel_delayed_work(&pegasus->carrier_check); -+ unregister_netdev(pegasus->net); -+ unlink_all_urbs(pegasus); -+ free_all_urbs(pegasus); -+ if (pegasus->rx_skb != NULL) { -+ dev_kfree_skb(pegasus->rx_skb); -+ pegasus->rx_skb = NULL; -+ } -+ free_netdev(pegasus->net); -+ pegasus_dec_workqueue(); -+} -+ -+static int pegasus_suspend(struct usb_interface *intf, pm_message_t message) -+{ -+ struct pegasus *pegasus = usb_get_intfdata(intf); -+ -+ netif_device_detach(pegasus->net); -+ cancel_delayed_work(&pegasus->carrier_check); -+ if (netif_running(pegasus->net)) { -+ usb_kill_urb(pegasus->rx_urb); -+ usb_kill_urb(pegasus->intr_urb); -+ } -+ return 0; -+} -+ -+static int pegasus_resume(struct usb_interface *intf) -+{ -+ struct pegasus *pegasus = usb_get_intfdata(intf); -+ -+ netif_device_attach(pegasus->net); -+ if (netif_running(pegasus->net)) { -+ pegasus->rx_urb->status = 0; -+ pegasus->rx_urb->actual_length = 0; -+ read_bulk_callback(pegasus->rx_urb); -+ -+ pegasus->intr_urb->status = 0; -+ pegasus->intr_urb->actual_length = 0; -+ intr_callback(pegasus->intr_urb); -+ } -+ queue_delayed_work(pegasus_workqueue, &pegasus->carrier_check, -+ CARRIER_CHECK_DELAY); -+ return 0; -+} -+ -+static const struct net_device_ops pegasus_netdev_ops = { -+ .ndo_open = pegasus_open, -+ .ndo_stop = pegasus_close, -+ .ndo_do_ioctl = pegasus_ioctl, -+ .ndo_start_xmit = pegasus_start_xmit, -+ .ndo_set_rx_mode = pegasus_set_multicast, -+ .ndo_get_stats = pegasus_netdev_stats, -+ .ndo_tx_timeout = pegasus_tx_timeout, -+ .ndo_change_mtu = eth_change_mtu, -+ .ndo_set_mac_address = eth_mac_addr, -+ .ndo_validate_addr = eth_validate_addr, -+}; -+ -+static struct usb_driver pegasus_driver = { -+ .name = driver_name, -+ .probe = pegasus_probe, -+ .disconnect = pegasus_disconnect, -+ .id_table = pegasus_ids, -+ .suspend = pegasus_suspend, -+ .resume = pegasus_resume, -+ .disable_hub_initiated_lpm = 1, -+}; -+ -+static void __init parse_id(char *id) -+{ -+ unsigned int vendor_id = 0, device_id = 0, flags = 0, i = 0; -+ char *token, *name = NULL; -+ -+ if ((token = strsep(&id, ":")) != NULL) -+ name = token; -+ /* name now points to a null terminated string*/ -+ if ((token = strsep(&id, ":")) != NULL) -+ vendor_id = simple_strtoul(token, NULL, 16); -+ if ((token = strsep(&id, ":")) != NULL) -+ device_id = simple_strtoul(token, NULL, 16); -+ flags = simple_strtoul(id, NULL, 16); -+ pr_info("%s: new device %s, vendor ID 0x%04x, device ID 0x%04x, flags: 0x%x\n", -+ driver_name, name, vendor_id, device_id, flags); -+ -+ if (vendor_id > 0x10000 || vendor_id == 0) -+ return; -+ if (device_id > 0x10000 || device_id == 0) -+ return; -+ -+ for (i = 0; usb_dev_id[i].name; i++); -+ usb_dev_id[i].name = name; -+ usb_dev_id[i].vendor = vendor_id; -+ usb_dev_id[i].device = device_id; -+ usb_dev_id[i].private = flags; -+ pegasus_ids[i].match_flags = USB_DEVICE_ID_MATCH_DEVICE; -+ pegasus_ids[i].idVendor = vendor_id; -+ pegasus_ids[i].idProduct = device_id; -+} -+ -+static int __init pegasus_init(void) -+{ -+ pr_info("%s: %s, " DRIVER_DESC "\n", driver_name, DRIVER_VERSION); -+ if (devid) -+ parse_id(devid); -+ return usb_register(&pegasus_driver); -+} -+ -+static void __exit pegasus_exit(void) -+{ -+ usb_deregister(&pegasus_driver); -+} -+ -+module_init(pegasus_init); -+module_exit(pegasus_exit); -diff -Naur backports-4.2.6-1.org/drivers/net/usb/pegasus.h backports-4.2.6-1/drivers/net/usb/pegasus.h ---- backports-4.2.6-1.org/drivers/net/usb/pegasus.h 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/pegasus.h 2016-06-28 14:35:17.998640551 +0200 -@@ -0,0 +1,308 @@ -+/* -+ * Copyright (c) 1999-2013 Petko Manolov (petkan@nucleusys.com) -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as published -+ * by the Free Software Foundation. -+ */ -+ -+ -+#ifndef PEGASUS_DEV -+ -+#define PEGASUS_II 0x80000000 -+#define HAS_HOME_PNA 0x40000000 -+ -+#define PEGASUS_MTU 1536 -+ -+#define EPROM_WRITE 0x01 -+#define EPROM_READ 0x02 -+#define EPROM_DONE 0x04 -+#define EPROM_WR_ENABLE 0x10 -+#define EPROM_LOAD 0x20 -+ -+#define PHY_DONE 0x80 -+#define PHY_READ 0x40 -+#define PHY_WRITE 0x20 -+#define DEFAULT_GPIO_RESET 0x24 -+#define DEFAULT_GPIO_SET 0x26 -+ -+#define PEGASUS_PRESENT 0x00000001 -+#define PEGASUS_TX_BUSY 0x00000004 -+#define PEGASUS_RX_BUSY 0x00000008 -+#define CTRL_URB_RUNNING 0x00000010 -+#define CTRL_URB_SLEEP 0x00000020 -+#define PEGASUS_UNPLUG 0x00000040 -+#define PEGASUS_RX_URB_FAIL 0x00000080 -+ -+#define RX_MULTICAST 2 -+#define RX_PROMISCUOUS 4 -+ -+#define REG_TIMEOUT (HZ) -+#define PEGASUS_TX_TIMEOUT (HZ*10) -+ -+#define TX_UNDERRUN 0x80 -+#define EXCESSIVE_COL 0x40 -+#define LATE_COL 0x20 -+#define NO_CARRIER 0x10 -+#define LOSS_CARRIER 0x08 -+#define JABBER_TIMEOUT 0x04 -+ -+#define LINK_STATUS 0x01 -+ -+#define PEGASUS_REQT_READ 0xc0 -+#define PEGASUS_REQT_WRITE 0x40 -+#define PEGASUS_REQ_GET_REGS 0xf0 -+#define PEGASUS_REQ_SET_REGS 0xf1 -+#define PEGASUS_REQ_SET_REG PEGASUS_REQ_SET_REGS -+ -+enum pegasus_registers { -+ EthCtrl0 = 0, -+ EthCtrl1 = 1, -+ EthCtrl2 = 2, -+ EthID = 0x10, -+ Reg1d = 0x1d, -+ EpromOffset = 0x20, -+ EpromData = 0x21, /* 0x21 low, 0x22 high byte */ -+ EpromCtrl = 0x23, -+ PhyAddr = 0x25, -+ PhyData = 0x26, /* 0x26 low, 0x27 high byte */ -+ PhyCtrl = 0x28, -+ UsbStst = 0x2a, -+ EthTxStat0 = 0x2b, -+ EthTxStat1 = 0x2c, -+ EthRxStat = 0x2d, -+ WakeupControl = 0x78, -+ Reg7b = 0x7b, -+ Gpio0 = 0x7e, -+ Gpio1 = 0x7f, -+ Reg81 = 0x81, -+}; -+ -+ -+typedef struct pegasus { -+ struct usb_device *usb; -+ struct usb_interface *intf; -+ struct net_device *net; -+ struct net_device_stats stats; -+ struct mii_if_info mii; -+ unsigned flags; -+ unsigned features; -+ u32 msg_enable; -+ u32 wolopts; -+ int dev_index; -+ int intr_interval; -+ struct tasklet_struct rx_tl; -+ struct delayed_work carrier_check; -+ struct urb *rx_urb, *tx_urb, *intr_urb; -+ struct sk_buff *rx_skb; -+ int chip; -+ unsigned char intr_buff[8]; -+ __u8 tx_buff[PEGASUS_MTU]; -+ __u8 eth_regs[4]; -+ __u8 phy; -+ __u8 gpio_res; -+} pegasus_t; -+ -+ -+struct usb_eth_dev { -+ char *name; -+ __u16 vendor; -+ __u16 device; -+ __u32 private; /* LSB is gpio reset value */ -+}; -+ -+#define VENDOR_3COM 0x0506 -+#define VENDOR_ABOCOM 0x07b8 -+#define VENDOR_ACCTON 0x083a -+#define VENDOR_ADMTEK 0x07a6 -+#define VENDOR_AEILAB 0x3334 -+#define VENDOR_ALLIEDTEL 0x07c9 -+#define VENDOR_ATEN 0x0557 -+#define VENDOR_BELKIN 0x050d -+#define VENDOR_BILLIONTON 0x08dd -+#define VENDOR_COMPAQ 0x049f -+#define VENDOR_COREGA 0x07aa -+#define VENDOR_DLINK 0x2001 -+#define VENDOR_ELCON 0x0db7 -+#define VENDOR_ELECOM 0x056e -+#define VENDOR_ELSA 0x05cc -+#define VENDOR_GIGABYTE 0x1044 -+#define VENDOR_HAWKING 0x0e66 -+#define VENDOR_HP 0x03f0 -+#define VENDOR_IODATA 0x04bb -+#define VENDOR_KINGSTON 0x0951 -+#define VENDOR_LANEED 0x056e -+#define VENDOR_LINKSYS 0x066b -+#define VENDOR_LINKSYS2 0x077b -+#define VENDOR_MELCO 0x0411 -+#define VENDOR_MICROSOFT 0x045e -+#define VENDOR_MOBILITY 0x1342 -+#define VENDOR_NETGEAR 0x0846 -+#define VENDOR_OCT 0x0b39 -+#define VENDOR_SMARTBRIDGES 0x08d1 -+#define VENDOR_SMC 0x0707 -+#define VENDOR_SOHOWARE 0x15e8 -+#define VENDOR_SIEMENS 0x067c -+ -+ -+#else /* PEGASUS_DEV */ -+ -+PEGASUS_DEV("3Com USB Ethernet 3C460B", VENDOR_3COM, 0x4601, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("ATEN USB Ethernet UC-110T", VENDOR_ATEN, 0x2007, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("USB HPNA/Ethernet", VENDOR_ABOCOM, 0x110c, -+ DEFAULT_GPIO_RESET | PEGASUS_II | HAS_HOME_PNA) -+PEGASUS_DEV("USB HPNA/Ethernet", VENDOR_ABOCOM, 0x4104, -+ DEFAULT_GPIO_RESET | HAS_HOME_PNA) -+PEGASUS_DEV("USB HPNA/Ethernet", VENDOR_ABOCOM, 0x4004, -+ DEFAULT_GPIO_RESET | HAS_HOME_PNA) -+PEGASUS_DEV("USB HPNA/Ethernet", VENDOR_ABOCOM, 0x4007, -+ DEFAULT_GPIO_RESET | HAS_HOME_PNA) -+PEGASUS_DEV("USB 10/100 Fast Ethernet", VENDOR_ABOCOM, 0x4102, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("USB 10/100 Fast Ethernet", VENDOR_ABOCOM, 0x4002, -+ DEFAULT_GPIO_RESET) -+PEGASUS_DEV("USB 10/100 Fast Ethernet", VENDOR_ABOCOM, 0x400b, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("USB 10/100 Fast Ethernet", VENDOR_ABOCOM, 0x400c, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("USB 10/100 Fast Ethernet", VENDOR_ABOCOM, 0xabc1, -+ DEFAULT_GPIO_RESET) -+PEGASUS_DEV("USB 10/100 Fast Ethernet", VENDOR_ABOCOM, 0x200c, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("Accton USB 10/100 Ethernet Adapter", VENDOR_ACCTON, 0x1046, -+ DEFAULT_GPIO_RESET) -+PEGASUS_DEV("SpeedStream USB 10/100 Ethernet", VENDOR_ACCTON, 0x5046, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("Philips USB 10/100 Ethernet", VENDOR_ACCTON, 0xb004, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("ADMtek ADM8511 \"Pegasus II\" USB Ethernet", -+ VENDOR_ADMTEK, 0x8511, -+ DEFAULT_GPIO_RESET | PEGASUS_II | HAS_HOME_PNA) -+PEGASUS_DEV("ADMtek ADM8513 \"Pegasus II\" USB Ethernet", -+ VENDOR_ADMTEK, 0x8513, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("ADMtek ADM8515 \"Pegasus II\" USB-2.0 Ethernet", -+ VENDOR_ADMTEK, 0x8515, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("ADMtek AN986 \"Pegasus\" USB Ethernet (evaluation board)", -+ VENDOR_ADMTEK, 0x0986, -+ DEFAULT_GPIO_RESET | HAS_HOME_PNA) -+PEGASUS_DEV("AN986A USB MAC", VENDOR_ADMTEK, 1986, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("AEI USB Fast Ethernet Adapter", VENDOR_AEILAB, 0x1701, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("Allied Telesyn Int. AT-USB100", VENDOR_ALLIEDTEL, 0xb100, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+/* -+ * Distinguish between this Belkin adaptor and the Belkin bluetooth adaptors -+ * with the same product IDs by checking the device class too. -+ */ -+PEGASUS_DEV_CLASS("Belkin F5D5050 USB Ethernet", VENDOR_BELKIN, 0x0121, 0x00, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("Belkin F5U122 10/100 USB Ethernet", VENDOR_BELKIN, 0x0122, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("Billionton USB-100", VENDOR_BILLIONTON, 0x0986, -+ DEFAULT_GPIO_RESET) -+PEGASUS_DEV("Billionton USBLP-100", VENDOR_BILLIONTON, 0x0987, -+ DEFAULT_GPIO_RESET | HAS_HOME_PNA) -+PEGASUS_DEV("iPAQ Networking 10/100 USB", VENDOR_COMPAQ, 0x8511, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("Billionton USBEL-100", VENDOR_BILLIONTON, 0x0988, -+ DEFAULT_GPIO_RESET) -+PEGASUS_DEV("Billionton USBE-100", VENDOR_BILLIONTON, 0x8511, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("Corega FEther USB-TX", VENDOR_COREGA, 0x0004, -+ DEFAULT_GPIO_RESET) -+PEGASUS_DEV("Corega FEther USB-TXS", VENDOR_COREGA, 0x000d, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("D-Link DSB-650TX", VENDOR_DLINK, 0x4001, -+ DEFAULT_GPIO_RESET) -+PEGASUS_DEV("D-Link DSB-650TX", VENDOR_DLINK, 0x4002, -+ DEFAULT_GPIO_RESET) -+PEGASUS_DEV("D-Link DSB-650TX", VENDOR_DLINK, 0x4102, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("D-Link DSB-650TX", VENDOR_DLINK, 0x400b, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("D-Link DSB-650TX", VENDOR_DLINK, 0x200c, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("D-Link DSB-650TX(PNA)", VENDOR_DLINK, 0x4003, -+ DEFAULT_GPIO_RESET | HAS_HOME_PNA) -+PEGASUS_DEV("D-Link DSB-650", VENDOR_DLINK, 0xabc1, -+ DEFAULT_GPIO_RESET) -+PEGASUS_DEV("GOLDPFEIL USB Adapter", VENDOR_ELCON, 0x0002, -+ DEFAULT_GPIO_RESET | PEGASUS_II | HAS_HOME_PNA) -+PEGASUS_DEV("ELECOM USB Ethernet LD-USB20", VENDOR_ELECOM, 0x4010, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("EasiDock Ethernet", VENDOR_MOBILITY, 0x0304, -+ DEFAULT_GPIO_RESET) -+PEGASUS_DEV("Elsa Micolink USB2Ethernet", VENDOR_ELSA, 0x3000, -+ DEFAULT_GPIO_RESET) -+PEGASUS_DEV("GIGABYTE GN-BR402W Wireless Router", VENDOR_GIGABYTE, 0x8002, -+ DEFAULT_GPIO_RESET) -+PEGASUS_DEV("Hawking UF100 10/100 Ethernet", VENDOR_HAWKING, 0x400c, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("HP hn210c Ethernet USB", VENDOR_HP, 0x811c, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("IO DATA USB ET/TX", VENDOR_IODATA, 0x0904, -+ DEFAULT_GPIO_RESET) -+PEGASUS_DEV("IO DATA USB ET/TX-S", VENDOR_IODATA, 0x0913, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("IO DATA USB ETX-US2", VENDOR_IODATA, 0x093a, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("Kingston KNU101TX Ethernet", VENDOR_KINGSTON, 0x000a, -+ DEFAULT_GPIO_RESET) -+PEGASUS_DEV("LANEED USB Ethernet LD-USB/TX", VENDOR_LANEED, 0x4002, -+ DEFAULT_GPIO_RESET) -+PEGASUS_DEV("LANEED USB Ethernet LD-USBL/TX", VENDOR_LANEED, 0x4005, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("LANEED USB Ethernet LD-USB/TX", VENDOR_LANEED, 0x400b, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("LANEED USB Ethernet LD-USB/T", VENDOR_LANEED, 0xabc1, -+ DEFAULT_GPIO_RESET) -+PEGASUS_DEV("LANEED USB Ethernet LD-USB/TX", VENDOR_LANEED, 0x200c, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("Linksys USB10TX", VENDOR_LINKSYS, 0x2202, -+ DEFAULT_GPIO_RESET) -+PEGASUS_DEV("Linksys USB100TX", VENDOR_LINKSYS, 0x2203, -+ DEFAULT_GPIO_RESET) -+PEGASUS_DEV("Linksys USB100TX", VENDOR_LINKSYS, 0x2204, -+ DEFAULT_GPIO_RESET | HAS_HOME_PNA) -+PEGASUS_DEV("Linksys USB10T Ethernet Adapter", VENDOR_LINKSYS, 0x2206, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("Linksys USBVPN1", VENDOR_LINKSYS2, 0x08b4, -+ DEFAULT_GPIO_RESET) -+PEGASUS_DEV("Linksys USB USB100TX", VENDOR_LINKSYS, 0x400b, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("Linksys USB10TX", VENDOR_LINKSYS, 0x200c, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("MELCO/BUFFALO LUA-TX", VENDOR_MELCO, 0x0001, -+ DEFAULT_GPIO_RESET) -+PEGASUS_DEV("MELCO/BUFFALO LUA-TX", VENDOR_MELCO, 0x0005, -+ DEFAULT_GPIO_RESET) -+PEGASUS_DEV("MELCO/BUFFALO LUA2-TX", VENDOR_MELCO, 0x0009, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("Microsoft MN-110", VENDOR_MICROSOFT, 0x007a, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("NETGEAR FA101", VENDOR_NETGEAR, 0x1020, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("OCT Inc.", VENDOR_OCT, 0x0109, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("OCT USB TO Ethernet", VENDOR_OCT, 0x0901, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("smartNIC 2 PnP Adapter", VENDOR_SMARTBRIDGES, 0x0003, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("SMC 202 USB Ethernet", VENDOR_SMC, 0x0200, -+ DEFAULT_GPIO_RESET) -+PEGASUS_DEV("SMC 2206 USB Ethernet", VENDOR_SMC, 0x0201, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("SOHOware NUB100 Ethernet", VENDOR_SOHOWARE, 0x9100, -+ DEFAULT_GPIO_RESET) -+PEGASUS_DEV("SOHOware NUB110 Ethernet", VENDOR_SOHOWARE, 0x9110, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+PEGASUS_DEV("SpeedStream USB 10/100 Ethernet", VENDOR_SIEMENS, 0x1001, -+ DEFAULT_GPIO_RESET | PEGASUS_II) -+ -+ -+#endif /* PEGASUS_DEV */ -diff -Naur backports-4.2.6-1.org/drivers/net/usb/plusb.c backports-4.2.6-1/drivers/net/usb/plusb.c ---- backports-4.2.6-1.org/drivers/net/usb/plusb.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/plusb.c 2016-06-28 14:35:17.998640551 +0200 -@@ -0,0 +1,162 @@ -+/* -+ * PL-2301/2302 USB host-to-host link cables -+ * Copyright (C) 2000-2005 by David Brownell -+ * -+ * 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, see . -+ */ -+ -+// #define DEBUG // error path messages, extra info -+// #define VERBOSE // more; success messages -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+ -+/* -+ * Prolific PL-2301/PL-2302 driver ... http://www.prolific.com.tw/ -+ * -+ * The protocol and handshaking used here should be bug-compatible -+ * with the Linux 2.2 "plusb" driver, by Deti Fliegl. -+ * -+ * HEADS UP: this handshaking isn't all that robust. This driver -+ * gets confused easily if you unplug one end of the cable then -+ * try to connect it again; you'll need to restart both ends. The -+ * "naplink" software (used by some PlayStation/2 deveopers) does -+ * the handshaking much better! Also, sometimes this hardware -+ * seems to get wedged under load. Prolific docs are weak, and -+ * don't identify differences between PL2301 and PL2302, much less -+ * anything to explain the different PL2302 versions observed. -+ * -+ * NOTE: pl2501 has several modes, including pl2301 and pl2302 -+ * compatibility. Some docs suggest the difference between 2301 -+ * and 2302 is only to make MS-Windows use a different driver... -+ * -+ * pl25a1 glue based on patch from Tony Gibbs. Prolific "docs" on -+ * this chip are as usual incomplete about what control messages -+ * are supported. -+ */ -+ -+/* -+ * Bits 0-4 can be used for software handshaking; they're set from -+ * one end, cleared from the other, "read" with the interrupt byte. -+ */ -+#define PL_S_EN (1<<7) /* (feature only) suspend enable */ -+/* reserved bit -- rx ready (6) ? */ -+#define PL_TX_READY (1<<5) /* (interrupt only) transmit ready */ -+#define PL_RESET_OUT (1<<4) /* reset output pipe */ -+#define PL_RESET_IN (1<<3) /* reset input pipe */ -+#define PL_TX_C (1<<2) /* transmission complete */ -+#define PL_TX_REQ (1<<1) /* transmission received */ -+#define PL_PEER_E (1<<0) /* peer exists */ -+ -+static inline int -+pl_vendor_req(struct usbnet *dev, u8 req, u8 val, u8 index) -+{ -+ return usbnet_read_cmd(dev, req, -+ USB_DIR_IN | USB_TYPE_VENDOR | -+ USB_RECIP_DEVICE, -+ val, index, NULL, 0); -+} -+ -+static inline int -+pl_clear_QuickLink_features(struct usbnet *dev, int val) -+{ -+ return pl_vendor_req(dev, 1, (u8) val, 0); -+} -+ -+static inline int -+pl_set_QuickLink_features(struct usbnet *dev, int val) -+{ -+ return pl_vendor_req(dev, 3, (u8) val, 0); -+} -+ -+static int pl_reset(struct usbnet *dev) -+{ -+ int status; -+ -+ /* some units seem to need this reset, others reject it utterly. -+ * FIXME be more like "naplink" or windows drivers. -+ */ -+ status = pl_set_QuickLink_features(dev, -+ PL_S_EN|PL_RESET_OUT|PL_RESET_IN|PL_PEER_E); -+ if (status != 0 && netif_msg_probe(dev)) -+ netif_dbg(dev, link, dev->net, "pl_reset --> %d\n", status); -+ return 0; -+} -+ -+static const struct driver_info prolific_info = { -+ .description = "Prolific PL-2301/PL-2302/PL-25A1", -+ .flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT, -+ /* some PL-2302 versions seem to fail usb_set_interface() */ -+ .reset = pl_reset, -+}; -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* -+ * Proilific's name won't normally be on the cables, and -+ * may not be on the device. -+ */ -+ -+static const struct usb_device_id products [] = { -+ -+/* full speed cables */ -+{ -+ USB_DEVICE(0x067b, 0x0000), // PL-2301 -+ .driver_info = (unsigned long) &prolific_info, -+}, { -+ USB_DEVICE(0x067b, 0x0001), // PL-2302 -+ .driver_info = (unsigned long) &prolific_info, -+}, -+ -+/* high speed cables */ -+{ -+ USB_DEVICE(0x067b, 0x25a1), /* PL-25A1, no eeprom */ -+ .driver_info = (unsigned long) &prolific_info, -+}, { -+ USB_DEVICE(0x050d, 0x258a), /* Belkin F5U258/F5U279 (PL-25A1) */ -+ .driver_info = (unsigned long) &prolific_info, -+}, { -+ USB_DEVICE(0x3923, 0x7825), /* National Instruments USB -+ * Host-to-Host Cable -+ */ -+ .driver_info = (unsigned long) &prolific_info, -+}, -+ -+ { }, // END -+}; -+MODULE_DEVICE_TABLE(usb, products); -+ -+static struct usb_driver plusb_driver = { -+ .name = "plusb", -+ .id_table = products, -+ .probe = usbnet_probe, -+ .disconnect = usbnet_disconnect, -+ .suspend = usbnet_suspend, -+ .resume = usbnet_resume, -+ .disable_hub_initiated_lpm = 1, -+}; -+ -+module_usb_driver(plusb_driver); -+ -+MODULE_AUTHOR("David Brownell"); -+MODULE_DESCRIPTION("Prolific PL-2301/2302/25A1 USB Host to Host Link Driver"); -+MODULE_LICENSE("GPL"); -diff -Naur backports-4.2.6-1.org/drivers/net/usb/r8152.c backports-4.2.6-1/drivers/net/usb/r8152.c ---- backports-4.2.6-1.org/drivers/net/usb/r8152.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/r8152.c 2016-06-28 14:45:32.005250978 +0200 -@@ -0,0 +1,2856 @@ -+/* -+ * Copyright (c) 2014 Realtek Semiconductor Corp. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * version 2 as published by the Free Software Foundation. -+ * -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* Version Information */ -+#define DRIVER_VERSION "v1.04.0 (2014/01/15)" -+#define DRIVER_AUTHOR "Realtek linux nic maintainers " -+#define DRIVER_DESC "Realtek RTL8152/RTL8153 Based USB Ethernet Adapters" -+#define MODULENAME "r8152" -+ -+#define R8152_PHY_ID 32 -+ -+#define PLA_IDR 0xc000 -+#define PLA_RCR 0xc010 -+#define PLA_RMS 0xc016 -+#define PLA_RXFIFO_CTRL0 0xc0a0 -+#define PLA_RXFIFO_CTRL1 0xc0a4 -+#define PLA_RXFIFO_CTRL2 0xc0a8 -+#define PLA_FMC 0xc0b4 -+#define PLA_CFG_WOL 0xc0b6 -+#define PLA_TEREDO_CFG 0xc0bc -+#define PLA_MAR 0xcd00 -+#define PLA_BACKUP 0xd000 -+#define PAL_BDC_CR 0xd1a0 -+#define PLA_TEREDO_TIMER 0xd2cc -+#define PLA_REALWOW_TIMER 0xd2e8 -+#define PLA_LEDSEL 0xdd90 -+#define PLA_LED_FEATURE 0xdd92 -+#define PLA_PHYAR 0xde00 -+#define PLA_BOOT_CTRL 0xe004 -+#define PLA_GPHY_INTR_IMR 0xe022 -+#define PLA_EEE_CR 0xe040 -+#define PLA_EEEP_CR 0xe080 -+#define PLA_MAC_PWR_CTRL 0xe0c0 -+#define PLA_MAC_PWR_CTRL2 0xe0ca -+#define PLA_MAC_PWR_CTRL3 0xe0cc -+#define PLA_MAC_PWR_CTRL4 0xe0ce -+#define PLA_WDT6_CTRL 0xe428 -+#define PLA_TCR0 0xe610 -+#define PLA_TCR1 0xe612 -+#define PLA_TXFIFO_CTRL 0xe618 -+#define PLA_RSTTELLY 0xe800 -+#define PLA_CR 0xe813 -+#define PLA_CRWECR 0xe81c -+#define PLA_CONFIG5 0xe822 -+#define PLA_PHY_PWR 0xe84c -+#define PLA_OOB_CTRL 0xe84f -+#define PLA_CPCR 0xe854 -+#define PLA_MISC_0 0xe858 -+#define PLA_MISC_1 0xe85a -+#define PLA_OCP_GPHY_BASE 0xe86c -+#define PLA_TELLYCNT 0xe890 -+#define PLA_SFF_STS_7 0xe8de -+#define PLA_PHYSTATUS 0xe908 -+#define PLA_BP_BA 0xfc26 -+#define PLA_BP_0 0xfc28 -+#define PLA_BP_1 0xfc2a -+#define PLA_BP_2 0xfc2c -+#define PLA_BP_3 0xfc2e -+#define PLA_BP_4 0xfc30 -+#define PLA_BP_5 0xfc32 -+#define PLA_BP_6 0xfc34 -+#define PLA_BP_7 0xfc36 -+#define PLA_BP_EN 0xfc38 -+ -+#define USB_U2P3_CTRL 0xb460 -+#define USB_DEV_STAT 0xb808 -+#define USB_USB_CTRL 0xd406 -+#define USB_PHY_CTRL 0xd408 -+#define USB_TX_AGG 0xd40a -+#define USB_RX_BUF_TH 0xd40c -+#define USB_USB_TIMER 0xd428 -+#define USB_RX_EARLY_AGG 0xd42c -+#define USB_PM_CTRL_STATUS 0xd432 -+#define USB_TX_DMA 0xd434 -+#define USB_TOLERANCE 0xd490 -+#define USB_LPM_CTRL 0xd41a -+#define USB_UPS_CTRL 0xd800 -+#define USB_MISC_0 0xd81a -+#define USB_POWER_CUT 0xd80a -+#define USB_AFE_CTRL2 0xd824 -+#define USB_WDT11_CTRL 0xe43c -+#define USB_BP_BA 0xfc26 -+#define USB_BP_0 0xfc28 -+#define USB_BP_1 0xfc2a -+#define USB_BP_2 0xfc2c -+#define USB_BP_3 0xfc2e -+#define USB_BP_4 0xfc30 -+#define USB_BP_5 0xfc32 -+#define USB_BP_6 0xfc34 -+#define USB_BP_7 0xfc36 -+#define USB_BP_EN 0xfc38 -+ -+/* OCP Registers */ -+#define OCP_ALDPS_CONFIG 0x2010 -+#define OCP_EEE_CONFIG1 0x2080 -+#define OCP_EEE_CONFIG2 0x2092 -+#define OCP_EEE_CONFIG3 0x2094 -+#define OCP_BASE_MII 0xa400 -+#define OCP_EEE_AR 0xa41a -+#define OCP_EEE_DATA 0xa41c -+#define OCP_PHY_STATUS 0xa420 -+#define OCP_POWER_CFG 0xa430 -+#define OCP_EEE_CFG 0xa432 -+#define OCP_SRAM_ADDR 0xa436 -+#define OCP_SRAM_DATA 0xa438 -+#define OCP_DOWN_SPEED 0xa442 -+#define OCP_EEE_CFG2 0xa5d0 -+#define OCP_ADC_CFG 0xbc06 -+ -+/* SRAM Register */ -+#define SRAM_LPF_CFG 0x8012 -+#define SRAM_10M_AMP1 0x8080 -+#define SRAM_10M_AMP2 0x8082 -+#define SRAM_IMPEDANCE 0x8084 -+ -+/* PLA_RCR */ -+#define RCR_AAP 0x00000001 -+#define RCR_APM 0x00000002 -+#define RCR_AM 0x00000004 -+#define RCR_AB 0x00000008 -+#define RCR_ACPT_ALL (RCR_AAP | RCR_APM | RCR_AM | RCR_AB) -+ -+/* PLA_RXFIFO_CTRL0 */ -+#define RXFIFO_THR1_NORMAL 0x00080002 -+#define RXFIFO_THR1_OOB 0x01800003 -+ -+/* PLA_RXFIFO_CTRL1 */ -+#define RXFIFO_THR2_FULL 0x00000060 -+#define RXFIFO_THR2_HIGH 0x00000038 -+#define RXFIFO_THR2_OOB 0x0000004a -+#define RXFIFO_THR2_NORMAL 0x00a0 -+ -+/* PLA_RXFIFO_CTRL2 */ -+#define RXFIFO_THR3_FULL 0x00000078 -+#define RXFIFO_THR3_HIGH 0x00000048 -+#define RXFIFO_THR3_OOB 0x0000005a -+#define RXFIFO_THR3_NORMAL 0x0110 -+ -+/* PLA_TXFIFO_CTRL */ -+#define TXFIFO_THR_NORMAL 0x00400008 -+#define TXFIFO_THR_NORMAL2 0x01000008 -+ -+/* PLA_FMC */ -+#define FMC_FCR_MCU_EN 0x0001 -+ -+/* PLA_EEEP_CR */ -+#define EEEP_CR_EEEP_TX 0x0002 -+ -+/* PLA_WDT6_CTRL */ -+#define WDT6_SET_MODE 0x0010 -+ -+/* PLA_TCR0 */ -+#define TCR0_TX_EMPTY 0x0800 -+#define TCR0_AUTO_FIFO 0x0080 -+ -+/* PLA_TCR1 */ -+#define VERSION_MASK 0x7cf0 -+ -+/* PLA_CR */ -+#define CR_RST 0x10 -+#define CR_RE 0x08 -+#define CR_TE 0x04 -+ -+/* PLA_CRWECR */ -+#define CRWECR_NORAML 0x00 -+#define CRWECR_CONFIG 0xc0 -+ -+/* PLA_OOB_CTRL */ -+#define NOW_IS_OOB 0x80 -+#define TXFIFO_EMPTY 0x20 -+#define RXFIFO_EMPTY 0x10 -+#define LINK_LIST_READY 0x02 -+#define DIS_MCU_CLROOB 0x01 -+#define FIFO_EMPTY (TXFIFO_EMPTY | RXFIFO_EMPTY) -+ -+/* PLA_MISC_1 */ -+#define RXDY_GATED_EN 0x0008 -+ -+/* PLA_SFF_STS_7 */ -+#define RE_INIT_LL 0x8000 -+#define MCU_BORW_EN 0x4000 -+ -+/* PLA_CPCR */ -+#define CPCR_RX_VLAN 0x0040 -+ -+/* PLA_CFG_WOL */ -+#define MAGIC_EN 0x0001 -+ -+/* PLA_TEREDO_CFG */ -+#define TEREDO_SEL 0x8000 -+#define TEREDO_WAKE_MASK 0x7f00 -+#define TEREDO_RS_EVENT_MASK 0x00fe -+#define OOB_TEREDO_EN 0x0001 -+ -+/* PAL_BDC_CR */ -+#define ALDPS_PROXY_MODE 0x0001 -+ -+/* PLA_CONFIG5 */ -+#define LAN_WAKE_EN 0x0002 -+ -+/* PLA_LED_FEATURE */ -+#define LED_MODE_MASK 0x0700 -+ -+/* PLA_PHY_PWR */ -+#define TX_10M_IDLE_EN 0x0080 -+#define PFM_PWM_SWITCH 0x0040 -+ -+/* PLA_MAC_PWR_CTRL */ -+#define D3_CLK_GATED_EN 0x00004000 -+#define MCU_CLK_RATIO 0x07010f07 -+#define MCU_CLK_RATIO_MASK 0x0f0f0f0f -+#define ALDPS_SPDWN_RATIO 0x0f87 -+ -+/* PLA_MAC_PWR_CTRL2 */ -+#define EEE_SPDWN_RATIO 0x8007 -+ -+/* PLA_MAC_PWR_CTRL3 */ -+#define PKT_AVAIL_SPDWN_EN 0x0100 -+#define SUSPEND_SPDWN_EN 0x0004 -+#define U1U2_SPDWN_EN 0x0002 -+#define L1_SPDWN_EN 0x0001 -+ -+/* PLA_MAC_PWR_CTRL4 */ -+#define PWRSAVE_SPDWN_EN 0x1000 -+#define RXDV_SPDWN_EN 0x0800 -+#define TX10MIDLE_EN 0x0100 -+#define TP100_SPDWN_EN 0x0020 -+#define TP500_SPDWN_EN 0x0010 -+#define TP1000_SPDWN_EN 0x0008 -+#define EEE_SPDWN_EN 0x0001 -+ -+/* PLA_GPHY_INTR_IMR */ -+#define GPHY_STS_MSK 0x0001 -+#define SPEED_DOWN_MSK 0x0002 -+#define SPDWN_RXDV_MSK 0x0004 -+#define SPDWN_LINKCHG_MSK 0x0008 -+ -+/* PLA_PHYAR */ -+#define PHYAR_FLAG 0x80000000 -+ -+/* PLA_EEE_CR */ -+#define EEE_RX_EN 0x0001 -+#define EEE_TX_EN 0x0002 -+ -+/* PLA_BOOT_CTRL */ -+#define AUTOLOAD_DONE 0x0002 -+ -+/* USB_DEV_STAT */ -+#define STAT_SPEED_MASK 0x0006 -+#define STAT_SPEED_HIGH 0x0000 -+#define STAT_SPEED_FULL 0x0001 -+ -+/* USB_TX_AGG */ -+#define TX_AGG_MAX_THRESHOLD 0x03 -+ -+/* USB_RX_BUF_TH */ -+#define RX_THR_SUPPER 0x0c350180 -+#define RX_THR_HIGH 0x7a120180 -+#define RX_THR_SLOW 0xffff0180 -+ -+/* USB_TX_DMA */ -+#define TEST_MODE_DISABLE 0x00000001 -+#define TX_SIZE_ADJUST1 0x00000100 -+ -+/* USB_UPS_CTRL */ -+#define POWER_CUT 0x0100 -+ -+/* USB_PM_CTRL_STATUS */ -+#define RESUME_INDICATE 0x0001 -+ -+/* USB_USB_CTRL */ -+#define RX_AGG_DISABLE 0x0010 -+ -+/* USB_U2P3_CTRL */ -+#define U2P3_ENABLE 0x0001 -+ -+/* USB_POWER_CUT */ -+#define PWR_EN 0x0001 -+#define PHASE2_EN 0x0008 -+ -+/* USB_MISC_0 */ -+#define PCUT_STATUS 0x0001 -+ -+/* USB_RX_EARLY_AGG */ -+#define EARLY_AGG_SUPPER 0x0e832981 -+#define EARLY_AGG_HIGH 0x0e837a12 -+#define EARLY_AGG_SLOW 0x0e83ffff -+ -+/* USB_WDT11_CTRL */ -+#define TIMER11_EN 0x0001 -+ -+/* USB_LPM_CTRL */ -+#define LPM_TIMER_MASK 0x0c -+#define LPM_TIMER_500MS 0x04 /* 500 ms */ -+#define LPM_TIMER_500US 0x0c /* 500 us */ -+ -+/* USB_AFE_CTRL2 */ -+#define SEN_VAL_MASK 0xf800 -+#define SEN_VAL_NORMAL 0xa000 -+#define SEL_RXIDLE 0x0100 -+ -+/* OCP_ALDPS_CONFIG */ -+#define ENPWRSAVE 0x8000 -+#define ENPDNPS 0x0200 -+#define LINKENA 0x0100 -+#define DIS_SDSAVE 0x0010 -+ -+/* OCP_PHY_STATUS */ -+#define PHY_STAT_MASK 0x0007 -+#define PHY_STAT_LAN_ON 3 -+#define PHY_STAT_PWRDN 5 -+ -+/* OCP_POWER_CFG */ -+#define EEE_CLKDIV_EN 0x8000 -+#define EN_ALDPS 0x0004 -+#define EN_10M_PLLOFF 0x0001 -+ -+/* OCP_EEE_CONFIG1 */ -+#define RG_TXLPI_MSK_HFDUP 0x8000 -+#define RG_MATCLR_EN 0x4000 -+#define EEE_10_CAP 0x2000 -+#define EEE_NWAY_EN 0x1000 -+#define TX_QUIET_EN 0x0200 -+#define RX_QUIET_EN 0x0100 -+#define SDRISETIME 0x0010 /* bit 4 ~ 6 */ -+#define RG_RXLPI_MSK_HFDUP 0x0008 -+#define SDFALLTIME 0x0007 /* bit 0 ~ 2 */ -+ -+/* OCP_EEE_CONFIG2 */ -+#define RG_LPIHYS_NUM 0x7000 /* bit 12 ~ 15 */ -+#define RG_DACQUIET_EN 0x0400 -+#define RG_LDVQUIET_EN 0x0200 -+#define RG_CKRSEL 0x0020 -+#define RG_EEEPRG_EN 0x0010 -+ -+/* OCP_EEE_CONFIG3 */ -+#define FST_SNR_EYE_R 0x1500 /* bit 7 ~ 15 */ -+#define RG_LFS_SEL 0x0060 /* bit 6 ~ 5 */ -+#define MSK_PH 0x0006 /* bit 0 ~ 3 */ -+ -+/* OCP_EEE_AR */ -+/* bit[15:14] function */ -+#define FUN_ADDR 0x0000 -+#define FUN_DATA 0x4000 -+/* bit[4:0] device addr */ -+#define DEVICE_ADDR 0x0007 -+ -+/* OCP_EEE_DATA */ -+#define EEE_ADDR 0x003C -+#define EEE_DATA 0x0002 -+ -+/* OCP_EEE_CFG */ -+#define CTAP_SHORT_EN 0x0040 -+#define EEE10_EN 0x0010 -+ -+/* OCP_DOWN_SPEED */ -+#define EN_10M_BGOFF 0x0080 -+ -+/* OCP_EEE_CFG2 */ -+#define MY1000_EEE 0x0004 -+#define MY100_EEE 0x0002 -+ -+/* OCP_ADC_CFG */ -+#define CKADSEL_L 0x0100 -+#define ADC_EN 0x0080 -+#define EN_EMI_L 0x0040 -+ -+/* SRAM_LPF_CFG */ -+#define LPF_AUTO_TUNE 0x8000 -+ -+/* SRAM_10M_AMP1 */ -+#define GDAC_IB_UPALL 0x0008 -+ -+/* SRAM_10M_AMP2 */ -+#define AMP_DN 0x0200 -+ -+/* SRAM_IMPEDANCE */ -+#define RX_DRIVING_MASK 0x6000 -+ -+enum rtl_register_content { -+ _1000bps = 0x10, -+ _100bps = 0x08, -+ _10bps = 0x04, -+ LINK_STATUS = 0x02, -+ FULL_DUP = 0x01, -+}; -+ -+#define RTL8152_MAX_TX 10 -+#define RTL8152_MAX_RX 10 -+#define INTBUFSIZE 2 -+#define CRC_SIZE 4 -+#define TX_ALIGN 4 -+#define RX_ALIGN 8 -+ -+#define INTR_LINK 0x0004 -+ -+#define RTL8152_REQT_READ 0xc0 -+#define RTL8152_REQT_WRITE 0x40 -+#define RTL8152_REQ_GET_REGS 0x05 -+#define RTL8152_REQ_SET_REGS 0x05 -+ -+#define BYTE_EN_DWORD 0xff -+#define BYTE_EN_WORD 0x33 -+#define BYTE_EN_BYTE 0x11 -+#define BYTE_EN_SIX_BYTES 0x3f -+#define BYTE_EN_START_MASK 0x0f -+#define BYTE_EN_END_MASK 0xf0 -+ -+#define RTL8152_RMS (VLAN_ETH_FRAME_LEN + VLAN_HLEN) -+#define RTL8152_TX_TIMEOUT (HZ) -+ -+/* rtl8152 flags */ -+enum rtl8152_flags { -+ RTL8152_UNPLUG = 0, -+ RTL8152_SET_RX_MODE, -+ WORK_ENABLE, -+ RTL8152_LINK_CHG, -+}; -+ -+/* Define these values to match your device */ -+#define VENDOR_ID_REALTEK 0x0bda -+#define PRODUCT_ID_RTL8152 0x8152 -+#define PRODUCT_ID_RTL8153 0x8153 -+ -+#define VENDOR_ID_SAMSUNG 0x04e8 -+#define PRODUCT_ID_SAMSUNG 0xa101 -+ -+#define VENDOR_ID_LENOVO 0x17ef -+#define PRODUCT_ID_LENOVO 0x7205 -+ -+#define VENDOR_ID_NVIDIA 0x0955 -+#define PRODUCT_ID_NVIDIA 0x09ff -+ -+ -+#define MCU_TYPE_PLA 0x0100 -+#define MCU_TYPE_USB 0x0000 -+ -+struct rx_desc { -+ __le32 opts1; -+#define RX_LEN_MASK 0x7fff -+ __le32 opts2; -+ __le32 opts3; -+ __le32 opts4; -+ __le32 opts5; -+ __le32 opts6; -+}; -+ -+struct tx_desc { -+ __le32 opts1; -+#define TX_FS (1 << 31) /* First segment of a packet */ -+#define TX_LS (1 << 30) /* Final segment of a packet */ -+#define TX_LEN_MASK 0x3ffff -+ -+ __le32 opts2; -+#define UDP_CS (1 << 31) /* Calculate UDP/IP checksum */ -+#define TCP_CS (1 << 30) /* Calculate TCP/IP checksum */ -+#define IPV4_CS (1 << 29) /* Calculate IPv4 checksum */ -+#define IPV6_CS (1 << 28) /* Calculate IPv6 checksum */ -+}; -+ -+struct r8152; -+ -+struct rx_agg { -+ struct list_head list; -+ struct urb *urb; -+ struct r8152 *context; -+ void *buffer; -+ void *head; -+}; -+ -+struct tx_agg { -+ struct list_head list; -+ struct urb *urb; -+ struct r8152 *context; -+ void *buffer; -+ void *head; -+ u32 skb_num; -+ u32 skb_len; -+}; -+ -+struct r8152 { -+ unsigned long flags; -+ struct usb_device *udev; -+ struct tasklet_struct tl; -+ struct usb_interface *intf; -+ struct net_device *netdev; -+ struct urb *intr_urb; -+ struct tx_agg tx_info[RTL8152_MAX_TX]; -+ struct rx_agg rx_info[RTL8152_MAX_RX]; -+ struct list_head rx_done, tx_free; -+ struct sk_buff_head tx_queue; -+ spinlock_t rx_lock, tx_lock; -+ struct delayed_work schedule; -+ struct mii_if_info mii; -+ -+ struct rtl_ops { -+ void (*init)(struct r8152 *); -+ int (*enable)(struct r8152 *); -+ void (*disable)(struct r8152 *); -+ void (*down)(struct r8152 *); -+ void (*unload)(struct r8152 *); -+ } __no_const rtl_ops; -+ -+ int intr_interval; -+ u32 msg_enable; -+ u32 tx_qlen; -+ u16 ocp_base; -+ u8 *intr_buff; -+ u8 version; -+ u8 speed; -+}; -+ -+enum rtl_version { -+ RTL_VER_UNKNOWN = 0, -+ RTL_VER_01, -+ RTL_VER_02, -+ RTL_VER_03, -+ RTL_VER_04, -+ RTL_VER_05, -+ RTL_VER_MAX -+}; -+ -+/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). -+ * The RTL chips use a 64 element hash table based on the Ethernet CRC. -+ */ -+static const int multicast_filter_limit = 32; -+static unsigned int rx_buf_sz = 16384; -+ -+static -+int get_registers(struct r8152 *tp, u16 value, u16 index, u16 size, void *data) -+{ -+ int ret; -+ void *tmp; -+ -+ tmp = kmalloc(size, GFP_KERNEL); -+ if (!tmp) -+ return -ENOMEM; -+ -+ ret = usb_control_msg(tp->udev, usb_rcvctrlpipe(tp->udev, 0), -+ RTL8152_REQ_GET_REGS, RTL8152_REQT_READ, -+ value, index, tmp, size, 500); -+ -+ memcpy(data, tmp, size); -+ kfree(tmp); -+ -+ return ret; -+} -+ -+static -+int set_registers(struct r8152 *tp, u16 value, u16 index, u16 size, void *data) -+{ -+ int ret; -+ void *tmp; -+ -+ tmp = kmalloc(size, GFP_KERNEL); -+ if (!tmp) -+ return -ENOMEM; -+ -+ memcpy(tmp, data, size); -+ -+ ret = usb_control_msg(tp->udev, usb_sndctrlpipe(tp->udev, 0), -+ RTL8152_REQ_SET_REGS, RTL8152_REQT_WRITE, -+ value, index, tmp, size, 500); -+ -+ kfree(tmp); -+ return ret; -+} -+ -+static int generic_ocp_read(struct r8152 *tp, u16 index, u16 size, -+ void *data, u16 type) -+{ -+ u16 limit = 64; -+ int ret = 0; -+ -+ if (test_bit(RTL8152_UNPLUG, &tp->flags)) -+ return -ENODEV; -+ -+ /* both size and indix must be 4 bytes align */ -+ if ((size & 3) || !size || (index & 3) || !data) -+ return -EPERM; -+ -+ if ((u32)index + (u32)size > 0xffff) -+ return -EPERM; -+ -+ while (size) { -+ if (size > limit) { -+ ret = get_registers(tp, index, type, limit, data); -+ if (ret < 0) -+ break; -+ -+ index += limit; -+ data += limit; -+ size -= limit; -+ } else { -+ ret = get_registers(tp, index, type, size, data); -+ if (ret < 0) -+ break; -+ -+ index += size; -+ data += size; -+ size = 0; -+ break; -+ } -+ } -+ -+ return ret; -+} -+ -+static int generic_ocp_write(struct r8152 *tp, u16 index, u16 byteen, -+ u16 size, void *data, u16 type) -+{ -+ int ret; -+ u16 byteen_start, byteen_end, byen; -+ u16 limit = 512; -+ -+ if (test_bit(RTL8152_UNPLUG, &tp->flags)) -+ return -ENODEV; -+ -+ /* both size and indix must be 4 bytes align */ -+ if ((size & 3) || !size || (index & 3) || !data) -+ return -EPERM; -+ -+ if ((u32)index + (u32)size > 0xffff) -+ return -EPERM; -+ -+ byteen_start = byteen & BYTE_EN_START_MASK; -+ byteen_end = byteen & BYTE_EN_END_MASK; -+ -+ byen = byteen_start | (byteen_start << 4); -+ ret = set_registers(tp, index, type | byen, 4, data); -+ if (ret < 0) -+ goto error1; -+ -+ index += 4; -+ data += 4; -+ size -= 4; -+ -+ if (size) { -+ size -= 4; -+ -+ while (size) { -+ if (size > limit) { -+ ret = set_registers(tp, index, -+ type | BYTE_EN_DWORD, -+ limit, data); -+ if (ret < 0) -+ goto error1; -+ -+ index += limit; -+ data += limit; -+ size -= limit; -+ } else { -+ ret = set_registers(tp, index, -+ type | BYTE_EN_DWORD, -+ size, data); -+ if (ret < 0) -+ goto error1; -+ -+ index += size; -+ data += size; -+ size = 0; -+ break; -+ } -+ } -+ -+ byen = byteen_end | (byteen_end >> 4); -+ ret = set_registers(tp, index, type | byen, 4, data); -+ if (ret < 0) -+ goto error1; -+ } -+ -+error1: -+ return ret; -+} -+ -+static inline -+int pla_ocp_read(struct r8152 *tp, u16 index, u16 size, void *data) -+{ -+ return generic_ocp_read(tp, index, size, data, MCU_TYPE_PLA); -+} -+ -+static inline -+int pla_ocp_write(struct r8152 *tp, u16 index, u16 byteen, u16 size, void *data) -+{ -+ return generic_ocp_write(tp, index, byteen, size, data, MCU_TYPE_PLA); -+} -+ -+static inline -+int usb_ocp_read(struct r8152 *tp, u16 index, u16 size, void *data) -+{ -+ return generic_ocp_read(tp, index, size, data, MCU_TYPE_USB); -+} -+ -+static inline -+int usb_ocp_write(struct r8152 *tp, u16 index, u16 byteen, u16 size, void *data) -+{ -+ return generic_ocp_write(tp, index, byteen, size, data, MCU_TYPE_USB); -+} -+ -+static u32 ocp_read_dword(struct r8152 *tp, u16 type, u16 index) -+{ -+ __le32 data; -+ -+ generic_ocp_read(tp, index, sizeof(data), &data, type); -+ -+ return __le32_to_cpu(data); -+} -+ -+static void ocp_write_dword(struct r8152 *tp, u16 type, u16 index, u32 data) -+{ -+ __le32 tmp = __cpu_to_le32(data); -+ -+ generic_ocp_write(tp, index, BYTE_EN_DWORD, sizeof(tmp), &tmp, type); -+} -+ -+static u16 ocp_read_word(struct r8152 *tp, u16 type, u16 index) -+{ -+ u32 data; -+ __le32 tmp; -+ u8 shift = index & 2; -+ -+ index &= ~3; -+ -+ generic_ocp_read(tp, index, sizeof(tmp), &tmp, type); -+ -+ data = __le32_to_cpu(tmp); -+ data >>= (shift * 8); -+ data &= 0xffff; -+ -+ return (u16)data; -+} -+ -+static void ocp_write_word(struct r8152 *tp, u16 type, u16 index, u32 data) -+{ -+ u32 mask = 0xffff; -+ __le32 tmp; -+ u16 byen = BYTE_EN_WORD; -+ u8 shift = index & 2; -+ -+ data &= mask; -+ -+ if (index & 2) { -+ byen <<= shift; -+ mask <<= (shift * 8); -+ data <<= (shift * 8); -+ index &= ~3; -+ } -+ -+ generic_ocp_read(tp, index, sizeof(tmp), &tmp, type); -+ -+ data |= __le32_to_cpu(tmp) & ~mask; -+ tmp = __cpu_to_le32(data); -+ -+ generic_ocp_write(tp, index, byen, sizeof(tmp), &tmp, type); -+} -+ -+static u8 ocp_read_byte(struct r8152 *tp, u16 type, u16 index) -+{ -+ u32 data; -+ __le32 tmp; -+ u8 shift = index & 3; -+ -+ index &= ~3; -+ -+ generic_ocp_read(tp, index, sizeof(tmp), &tmp, type); -+ -+ data = __le32_to_cpu(tmp); -+ data >>= (shift * 8); -+ data &= 0xff; -+ -+ return (u8)data; -+} -+ -+static void ocp_write_byte(struct r8152 *tp, u16 type, u16 index, u32 data) -+{ -+ u32 mask = 0xff; -+ __le32 tmp; -+ u16 byen = BYTE_EN_BYTE; -+ u8 shift = index & 3; -+ -+ data &= mask; -+ -+ if (index & 3) { -+ byen <<= shift; -+ mask <<= (shift * 8); -+ data <<= (shift * 8); -+ index &= ~3; -+ } -+ -+ generic_ocp_read(tp, index, sizeof(tmp), &tmp, type); -+ -+ data |= __le32_to_cpu(tmp) & ~mask; -+ tmp = __cpu_to_le32(data); -+ -+ generic_ocp_write(tp, index, byen, sizeof(tmp), &tmp, type); -+} -+ -+static u16 ocp_reg_read(struct r8152 *tp, u16 addr) -+{ -+ u16 ocp_base, ocp_index; -+ -+ ocp_base = addr & 0xf000; -+ if (ocp_base != tp->ocp_base) { -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, ocp_base); -+ tp->ocp_base = ocp_base; -+ } -+ -+ ocp_index = (addr & 0x0fff) | 0xb000; -+ return ocp_read_word(tp, MCU_TYPE_PLA, ocp_index); -+} -+ -+static void ocp_reg_write(struct r8152 *tp, u16 addr, u16 data) -+{ -+ u16 ocp_base, ocp_index; -+ -+ ocp_base = addr & 0xf000; -+ if (ocp_base != tp->ocp_base) { -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, ocp_base); -+ tp->ocp_base = ocp_base; -+ } -+ -+ ocp_index = (addr & 0x0fff) | 0xb000; -+ ocp_write_word(tp, MCU_TYPE_PLA, ocp_index, data); -+} -+ -+static inline void r8152_mdio_write(struct r8152 *tp, u32 reg_addr, u32 value) -+{ -+ ocp_reg_write(tp, OCP_BASE_MII + reg_addr * 2, value); -+} -+ -+static inline int r8152_mdio_read(struct r8152 *tp, u32 reg_addr) -+{ -+ return ocp_reg_read(tp, OCP_BASE_MII + reg_addr * 2); -+} -+ -+static void sram_write(struct r8152 *tp, u16 addr, u16 data) -+{ -+ ocp_reg_write(tp, OCP_SRAM_ADDR, addr); -+ ocp_reg_write(tp, OCP_SRAM_DATA, data); -+} -+ -+static u16 sram_read(struct r8152 *tp, u16 addr) -+{ -+ ocp_reg_write(tp, OCP_SRAM_ADDR, addr); -+ return ocp_reg_read(tp, OCP_SRAM_DATA); -+} -+ -+static int read_mii_word(struct net_device *netdev, int phy_id, int reg) -+{ -+ struct r8152 *tp = netdev_priv(netdev); -+ -+ if (phy_id != R8152_PHY_ID) -+ return -EINVAL; -+ -+ return r8152_mdio_read(tp, reg); -+} -+ -+static -+void write_mii_word(struct net_device *netdev, int phy_id, int reg, int val) -+{ -+ struct r8152 *tp = netdev_priv(netdev); -+ -+ if (phy_id != R8152_PHY_ID) -+ return; -+ -+ r8152_mdio_write(tp, reg, val); -+} -+ -+static -+int r8152_submit_rx(struct r8152 *tp, struct rx_agg *agg, gfp_t mem_flags); -+ -+static inline void set_ethernet_addr(struct r8152 *tp) -+{ -+ struct net_device *dev = tp->netdev; -+ u8 node_id[8] = {0}; -+ -+ if (pla_ocp_read(tp, PLA_IDR, sizeof(node_id), node_id) < 0) -+ netif_notice(tp, probe, dev, "inet addr fail\n"); -+ else { -+ memcpy(dev->dev_addr, node_id, dev->addr_len); -+ memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); -+ } -+} -+ -+static int rtl8152_set_mac_address(struct net_device *netdev, void *p) -+{ -+ struct r8152 *tp = netdev_priv(netdev); -+ struct sockaddr *addr = p; -+ -+ if (!is_valid_ether_addr(addr->sa_data)) -+ return -EADDRNOTAVAIL; -+ -+ memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); -+ -+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG); -+ pla_ocp_write(tp, PLA_IDR, BYTE_EN_SIX_BYTES, 8, addr->sa_data); -+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML); -+ -+ return 0; -+} -+ -+static struct net_device_stats *rtl8152_get_stats(struct net_device *dev) -+{ -+ return &dev->stats; -+} -+ -+static void read_bulk_callback(struct urb *urb) -+{ -+ struct net_device *netdev; -+ unsigned long flags; -+ int status = urb->status; -+ struct rx_agg *agg; -+ struct r8152 *tp; -+ int result; -+ -+ agg = urb->context; -+ if (!agg) -+ return; -+ -+ tp = agg->context; -+ if (!tp) -+ return; -+ -+ if (test_bit(RTL8152_UNPLUG, &tp->flags)) -+ return; -+ -+ if (!test_bit(WORK_ENABLE, &tp->flags)) -+ return; -+ -+ netdev = tp->netdev; -+ -+ /* When link down, the driver would cancel all bulks. */ -+ /* This avoid the re-submitting bulk */ -+ if (!netif_carrier_ok(netdev)) -+ return; -+ -+ switch (status) { -+ case 0: -+ if (urb->actual_length < ETH_ZLEN) -+ break; -+ -+ spin_lock_irqsave(&tp->rx_lock, flags); -+ list_add_tail(&agg->list, &tp->rx_done); -+ spin_unlock_irqrestore(&tp->rx_lock, flags); -+ tasklet_schedule(&tp->tl); -+ return; -+ case -ESHUTDOWN: -+ set_bit(RTL8152_UNPLUG, &tp->flags); -+ netif_device_detach(tp->netdev); -+ return; -+ case -ENOENT: -+ return; /* the urb is in unlink state */ -+ case -ETIME: -+ if (net_ratelimit()) -+ netdev_warn(netdev, "maybe reset is needed?\n"); -+ break; -+ default: -+ if (net_ratelimit()) -+ netdev_warn(netdev, "Rx status %d\n", status); -+ break; -+ } -+ -+ result = r8152_submit_rx(tp, agg, GFP_ATOMIC); -+ if (result == -ENODEV) { -+ netif_device_detach(tp->netdev); -+ } else if (result) { -+ spin_lock_irqsave(&tp->rx_lock, flags); -+ list_add_tail(&agg->list, &tp->rx_done); -+ spin_unlock_irqrestore(&tp->rx_lock, flags); -+ tasklet_schedule(&tp->tl); -+ } -+} -+ -+static void write_bulk_callback(struct urb *urb) -+{ -+ struct net_device_stats *stats; -+ unsigned long flags; -+ struct tx_agg *agg; -+ struct r8152 *tp; -+ int status = urb->status; -+ -+ agg = urb->context; -+ if (!agg) -+ return; -+ -+ tp = agg->context; -+ if (!tp) -+ return; -+ -+ stats = rtl8152_get_stats(tp->netdev); -+ if (status) { -+ if (net_ratelimit()) -+ netdev_warn(tp->netdev, "Tx status %d\n", status); -+ stats->tx_errors += agg->skb_num; -+ } else { -+ stats->tx_packets += agg->skb_num; -+ stats->tx_bytes += agg->skb_len; -+ } -+ -+ spin_lock_irqsave(&tp->tx_lock, flags); -+ list_add_tail(&agg->list, &tp->tx_free); -+ spin_unlock_irqrestore(&tp->tx_lock, flags); -+ -+ if (!netif_carrier_ok(tp->netdev)) -+ return; -+ -+ if (!test_bit(WORK_ENABLE, &tp->flags)) -+ return; -+ -+ if (test_bit(RTL8152_UNPLUG, &tp->flags)) -+ return; -+ -+ if (!skb_queue_empty(&tp->tx_queue)) -+ tasklet_schedule(&tp->tl); -+} -+ -+static void intr_callback(struct urb *urb) -+{ -+ struct r8152 *tp; -+ __le16 *d; -+ int status = urb->status; -+ int res; -+ -+ tp = urb->context; -+ if (!tp) -+ return; -+ -+ if (!test_bit(WORK_ENABLE, &tp->flags)) -+ return; -+ -+ if (test_bit(RTL8152_UNPLUG, &tp->flags)) -+ return; -+ -+ switch (status) { -+ case 0: /* success */ -+ break; -+ case -ECONNRESET: /* unlink */ -+ case -ESHUTDOWN: -+ netif_device_detach(tp->netdev); -+ case -ENOENT: -+ return; -+ case -EOVERFLOW: -+ netif_info(tp, intr, tp->netdev, "intr status -EOVERFLOW\n"); -+ goto resubmit; -+ /* -EPIPE: should clear the halt */ -+ default: -+ netif_info(tp, intr, tp->netdev, "intr status %d\n", status); -+ goto resubmit; -+ } -+ -+ d = urb->transfer_buffer; -+ if (INTR_LINK & __le16_to_cpu(d[0])) { -+ if (!(tp->speed & LINK_STATUS)) { -+ set_bit(RTL8152_LINK_CHG, &tp->flags); -+ schedule_delayed_work(&tp->schedule, 0); -+ } -+ } else { -+ if (tp->speed & LINK_STATUS) { -+ set_bit(RTL8152_LINK_CHG, &tp->flags); -+ schedule_delayed_work(&tp->schedule, 0); -+ } -+ } -+ -+resubmit: -+ res = usb_submit_urb(urb, GFP_ATOMIC); -+ if (res == -ENODEV) -+ netif_device_detach(tp->netdev); -+ else if (res) -+ netif_err(tp, intr, tp->netdev, -+ "can't resubmit intr, status %d\n", res); -+} -+ -+static inline void *rx_agg_align(void *data) -+{ -+ return (void *)ALIGN((uintptr_t)data, RX_ALIGN); -+} -+ -+static inline void *tx_agg_align(void *data) -+{ -+ return (void *)ALIGN((uintptr_t)data, TX_ALIGN); -+} -+ -+static void free_all_mem(struct r8152 *tp) -+{ -+ int i; -+ -+ for (i = 0; i < RTL8152_MAX_RX; i++) { -+ usb_free_urb(tp->rx_info[i].urb); -+ tp->rx_info[i].urb = NULL; -+ -+ kfree(tp->rx_info[i].buffer); -+ tp->rx_info[i].buffer = NULL; -+ tp->rx_info[i].head = NULL; -+ } -+ -+ for (i = 0; i < RTL8152_MAX_TX; i++) { -+ usb_free_urb(tp->tx_info[i].urb); -+ tp->tx_info[i].urb = NULL; -+ -+ kfree(tp->tx_info[i].buffer); -+ tp->tx_info[i].buffer = NULL; -+ tp->tx_info[i].head = NULL; -+ } -+ -+ usb_free_urb(tp->intr_urb); -+ tp->intr_urb = NULL; -+ -+ kfree(tp->intr_buff); -+ tp->intr_buff = NULL; -+} -+ -+static int alloc_all_mem(struct r8152 *tp) -+{ -+ struct net_device *netdev = tp->netdev; -+ struct usb_interface *intf = tp->intf; -+ struct usb_host_interface *alt = intf->cur_altsetting; -+ struct usb_host_endpoint *ep_intr = alt->endpoint + 2; -+ struct urb *urb; -+ int node, i; -+ u8 *buf; -+ -+ node = netdev->dev.parent ? dev_to_node(netdev->dev.parent) : -1; -+ -+ spin_lock_init(&tp->rx_lock); -+ spin_lock_init(&tp->tx_lock); -+ INIT_LIST_HEAD(&tp->rx_done); -+ INIT_LIST_HEAD(&tp->tx_free); -+ skb_queue_head_init(&tp->tx_queue); -+ -+ for (i = 0; i < RTL8152_MAX_RX; i++) { -+ buf = kmalloc_node(rx_buf_sz, GFP_KERNEL, node); -+ if (!buf) -+ goto err1; -+ -+ if (buf != rx_agg_align(buf)) { -+ kfree(buf); -+ buf = kmalloc_node(rx_buf_sz + RX_ALIGN, GFP_KERNEL, -+ node); -+ if (!buf) -+ goto err1; -+ } -+ -+ urb = usb_alloc_urb(0, GFP_KERNEL); -+ if (!urb) { -+ kfree(buf); -+ goto err1; -+ } -+ -+ INIT_LIST_HEAD(&tp->rx_info[i].list); -+ tp->rx_info[i].context = tp; -+ tp->rx_info[i].urb = urb; -+ tp->rx_info[i].buffer = buf; -+ tp->rx_info[i].head = rx_agg_align(buf); -+ } -+ -+ for (i = 0; i < RTL8152_MAX_TX; i++) { -+ buf = kmalloc_node(rx_buf_sz, GFP_KERNEL, node); -+ if (!buf) -+ goto err1; -+ -+ if (buf != tx_agg_align(buf)) { -+ kfree(buf); -+ buf = kmalloc_node(rx_buf_sz + TX_ALIGN, GFP_KERNEL, -+ node); -+ if (!buf) -+ goto err1; -+ } -+ -+ urb = usb_alloc_urb(0, GFP_KERNEL); -+ if (!urb) { -+ kfree(buf); -+ goto err1; -+ } -+ -+ INIT_LIST_HEAD(&tp->tx_info[i].list); -+ tp->tx_info[i].context = tp; -+ tp->tx_info[i].urb = urb; -+ tp->tx_info[i].buffer = buf; -+ tp->tx_info[i].head = tx_agg_align(buf); -+ -+ list_add_tail(&tp->tx_info[i].list, &tp->tx_free); -+ } -+ -+ tp->intr_urb = usb_alloc_urb(0, GFP_KERNEL); -+ if (!tp->intr_urb) -+ goto err1; -+ -+ tp->intr_buff = kmalloc(INTBUFSIZE, GFP_KERNEL); -+ if (!tp->intr_buff) -+ goto err1; -+ -+ tp->intr_interval = (int)ep_intr->desc.bInterval; -+ usb_fill_int_urb(tp->intr_urb, tp->udev, usb_rcvintpipe(tp->udev, 3), -+ tp->intr_buff, INTBUFSIZE, intr_callback, -+ tp, tp->intr_interval); -+ -+ return 0; -+ -+err1: -+ free_all_mem(tp); -+ return -ENOMEM; -+} -+ -+static struct tx_agg *r8152_get_tx_agg(struct r8152 *tp) -+{ -+ struct tx_agg *agg = NULL; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&tp->tx_lock, flags); -+ if (!list_empty(&tp->tx_free)) { -+ struct list_head *cursor; -+ -+ cursor = tp->tx_free.next; -+ list_del_init(cursor); -+ agg = list_entry(cursor, struct tx_agg, list); -+ } -+ spin_unlock_irqrestore(&tp->tx_lock, flags); -+ -+ return agg; -+} -+ -+static void -+r8152_tx_csum(struct r8152 *tp, struct tx_desc *desc, struct sk_buff *skb) -+{ -+ memset(desc, 0, sizeof(*desc)); -+ -+ desc->opts1 = cpu_to_le32((skb->len & TX_LEN_MASK) | TX_FS | TX_LS); -+ -+ if (skb->ip_summed == CHECKSUM_PARTIAL) { -+ __be16 protocol; -+ u8 ip_protocol; -+ u32 opts2 = 0; -+ -+ if (skb->protocol == htons(ETH_P_8021Q)) -+ protocol = vlan_eth_hdr(skb)->h_vlan_encapsulated_proto; -+ else -+ protocol = skb->protocol; -+ -+ switch (protocol) { -+ case htons(ETH_P_IP): -+ opts2 |= IPV4_CS; -+ ip_protocol = ip_hdr(skb)->protocol; -+ break; -+ -+ case htons(ETH_P_IPV6): -+ opts2 |= IPV6_CS; -+ ip_protocol = ipv6_hdr(skb)->nexthdr; -+ break; -+ -+ default: -+ ip_protocol = IPPROTO_RAW; -+ break; -+ } -+ -+ if (ip_protocol == IPPROTO_TCP) { -+ opts2 |= TCP_CS; -+ opts2 |= (skb_transport_offset(skb) & 0x7fff) << 17; -+ } else if (ip_protocol == IPPROTO_UDP) { -+ opts2 |= UDP_CS; -+ } else { -+ WARN_ON_ONCE(1); -+ } -+ -+ desc->opts2 = cpu_to_le32(opts2); -+ } -+} -+ -+static int r8152_tx_agg_fill(struct r8152 *tp, struct tx_agg *agg) -+{ -+ int remain; -+ u8 *tx_data; -+ -+ tx_data = agg->head; -+ agg->skb_num = agg->skb_len = 0; -+ remain = rx_buf_sz; -+ -+ while (remain >= ETH_ZLEN + sizeof(struct tx_desc)) { -+ struct tx_desc *tx_desc; -+ struct sk_buff *skb; -+ unsigned int len; -+ -+ skb = skb_dequeue(&tp->tx_queue); -+ if (!skb) -+ break; -+ -+ remain -= sizeof(*tx_desc); -+ len = skb->len; -+ if (remain < len) { -+ skb_queue_head(&tp->tx_queue, skb); -+ break; -+ } -+ -+ tx_data = tx_agg_align(tx_data); -+ tx_desc = (struct tx_desc *)tx_data; -+ tx_data += sizeof(*tx_desc); -+ -+ r8152_tx_csum(tp, tx_desc, skb); -+ memcpy(tx_data, skb->data, len); -+ agg->skb_num++; -+ agg->skb_len += len; -+ dev_kfree_skb_any(skb); -+ -+ tx_data += len; -+ remain = rx_buf_sz - (int)(tx_agg_align(tx_data) - agg->head); -+ } -+ -+ netif_tx_lock(tp->netdev); -+ -+ if (netif_queue_stopped(tp->netdev) && -+ skb_queue_len(&tp->tx_queue) < tp->tx_qlen) -+ netif_wake_queue(tp->netdev); -+ -+ netif_tx_unlock(tp->netdev); -+ -+ usb_fill_bulk_urb(agg->urb, tp->udev, usb_sndbulkpipe(tp->udev, 2), -+ agg->head, (int)(tx_data - (u8 *)agg->head), -+ (usb_complete_t)write_bulk_callback, agg); -+ -+ return usb_submit_urb(agg->urb, GFP_ATOMIC); -+} -+ -+static void rx_bottom(struct r8152 *tp) -+{ -+ unsigned long flags; -+ struct list_head *cursor, *next; -+ -+ spin_lock_irqsave(&tp->rx_lock, flags); -+ list_for_each_safe(cursor, next, &tp->rx_done) { -+ struct rx_desc *rx_desc; -+ struct rx_agg *agg; -+ int len_used = 0; -+ struct urb *urb; -+ u8 *rx_data; -+ int ret; -+ -+ list_del_init(cursor); -+ spin_unlock_irqrestore(&tp->rx_lock, flags); -+ -+ agg = list_entry(cursor, struct rx_agg, list); -+ urb = agg->urb; -+ if (urb->actual_length < ETH_ZLEN) -+ goto submit; -+ -+ rx_desc = agg->head; -+ rx_data = agg->head; -+ len_used += sizeof(struct rx_desc); -+ -+ while (urb->actual_length > len_used) { -+ struct net_device *netdev = tp->netdev; -+ struct net_device_stats *stats; -+ unsigned int pkt_len; -+ struct sk_buff *skb; -+ -+ pkt_len = le32_to_cpu(rx_desc->opts1) & RX_LEN_MASK; -+ if (pkt_len < ETH_ZLEN) -+ break; -+ -+ len_used += pkt_len; -+ if (urb->actual_length < len_used) -+ break; -+ -+ stats = rtl8152_get_stats(netdev); -+ -+ pkt_len -= CRC_SIZE; -+ rx_data += sizeof(struct rx_desc); -+ -+ skb = netdev_alloc_skb_ip_align(netdev, pkt_len); -+ if (!skb) { -+ stats->rx_dropped++; -+ break; -+ } -+ memcpy(skb->data, rx_data, pkt_len); -+ skb_put(skb, pkt_len); -+ skb->protocol = eth_type_trans(skb, netdev); -+ netif_rx(skb); -+ stats->rx_packets++; -+ stats->rx_bytes += pkt_len; -+ -+ rx_data = rx_agg_align(rx_data + pkt_len + CRC_SIZE); -+ rx_desc = (struct rx_desc *)rx_data; -+ len_used = (int)(rx_data - (u8 *)agg->head); -+ len_used += sizeof(struct rx_desc); -+ } -+ -+submit: -+ ret = r8152_submit_rx(tp, agg, GFP_ATOMIC); -+ spin_lock_irqsave(&tp->rx_lock, flags); -+ if (ret && ret != -ENODEV) { -+ list_add_tail(&agg->list, next); -+ tasklet_schedule(&tp->tl); -+ } -+ } -+ spin_unlock_irqrestore(&tp->rx_lock, flags); -+} -+ -+static void tx_bottom(struct r8152 *tp) -+{ -+ int res; -+ -+ do { -+ struct tx_agg *agg; -+ -+ if (skb_queue_empty(&tp->tx_queue)) -+ break; -+ -+ agg = r8152_get_tx_agg(tp); -+ if (!agg) -+ break; -+ -+ res = r8152_tx_agg_fill(tp, agg); -+ if (res) { -+ struct net_device_stats *stats; -+ struct net_device *netdev; -+ unsigned long flags; -+ -+ netdev = tp->netdev; -+ stats = rtl8152_get_stats(netdev); -+ -+ if (res == -ENODEV) { -+ netif_device_detach(netdev); -+ } else { -+ netif_warn(tp, tx_err, netdev, -+ "failed tx_urb %d\n", res); -+ stats->tx_dropped += agg->skb_num; -+ spin_lock_irqsave(&tp->tx_lock, flags); -+ list_add_tail(&agg->list, &tp->tx_free); -+ spin_unlock_irqrestore(&tp->tx_lock, flags); -+ } -+ } -+ } while (res == 0); -+} -+ -+static void bottom_half(unsigned long data) -+{ -+ struct r8152 *tp; -+ -+ tp = (struct r8152 *)data; -+ -+ if (test_bit(RTL8152_UNPLUG, &tp->flags)) -+ return; -+ -+ if (!test_bit(WORK_ENABLE, &tp->flags)) -+ return; -+ -+ /* When link down, the driver would cancel all bulks. */ -+ /* This avoid the re-submitting bulk */ -+ if (!netif_carrier_ok(tp->netdev)) -+ return; -+ -+ rx_bottom(tp); -+ tx_bottom(tp); -+} -+ -+static -+int r8152_submit_rx(struct r8152 *tp, struct rx_agg *agg, gfp_t mem_flags) -+{ -+ usb_fill_bulk_urb(agg->urb, tp->udev, usb_rcvbulkpipe(tp->udev, 1), -+ agg->head, rx_buf_sz, -+ (usb_complete_t)read_bulk_callback, agg); -+ -+ return usb_submit_urb(agg->urb, mem_flags); -+} -+ -+static void rtl8152_tx_timeout(struct net_device *netdev) -+{ -+ struct r8152 *tp = netdev_priv(netdev); -+ int i; -+ -+ netif_warn(tp, tx_err, netdev, "Tx timeout\n"); -+ for (i = 0; i < RTL8152_MAX_TX; i++) -+ usb_unlink_urb(tp->tx_info[i].urb); -+} -+ -+static void rtl8152_set_rx_mode(struct net_device *netdev) -+{ -+ struct r8152 *tp = netdev_priv(netdev); -+ -+ if (tp->speed & LINK_STATUS) { -+ set_bit(RTL8152_SET_RX_MODE, &tp->flags); -+ schedule_delayed_work(&tp->schedule, 0); -+ } -+} -+ -+static void _rtl8152_set_rx_mode(struct net_device *netdev) -+{ -+ struct r8152 *tp = netdev_priv(netdev); -+ u32 mc_filter[2]; /* Multicast hash filter */ -+ __le32 tmp[2]; -+ u32 ocp_data; -+ -+ clear_bit(RTL8152_SET_RX_MODE, &tp->flags); -+ netif_stop_queue(netdev); -+ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); -+ ocp_data &= ~RCR_ACPT_ALL; -+ ocp_data |= RCR_AB | RCR_APM; -+ -+ if (netdev->flags & IFF_PROMISC) { -+ /* Unconditionally log net taps. */ -+ netif_notice(tp, link, netdev, "Promiscuous mode enabled\n"); -+ ocp_data |= RCR_AM | RCR_AAP; -+ mc_filter[1] = mc_filter[0] = 0xffffffff; -+ } else if ((netdev_mc_count(netdev) > multicast_filter_limit) || -+ (netdev->flags & IFF_ALLMULTI)) { -+ /* Too many to filter perfectly -- accept all multicasts. */ -+ ocp_data |= RCR_AM; -+ mc_filter[1] = mc_filter[0] = 0xffffffff; -+ } else { -+ struct netdev_hw_addr *ha; -+ -+ mc_filter[1] = mc_filter[0] = 0; -+ netdev_for_each_mc_addr(ha, netdev) { -+ int bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26; -+ mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); -+ ocp_data |= RCR_AM; -+ } -+ } -+ -+ tmp[0] = __cpu_to_le32(swab32(mc_filter[1])); -+ tmp[1] = __cpu_to_le32(swab32(mc_filter[0])); -+ -+ pla_ocp_write(tp, PLA_MAR, BYTE_EN_DWORD, sizeof(tmp), tmp); -+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); -+ netif_wake_queue(netdev); -+} -+ -+static netdev_tx_t rtl8152_start_xmit(struct sk_buff *skb, -+ struct net_device *netdev) -+{ -+ struct r8152 *tp = netdev_priv(netdev); -+ -+ skb_tx_timestamp(skb); -+ -+ skb_queue_tail(&tp->tx_queue, skb); -+ -+ if (list_empty(&tp->tx_free) && -+ skb_queue_len(&tp->tx_queue) > tp->tx_qlen) -+ netif_stop_queue(netdev); -+ -+ if (!list_empty(&tp->tx_free)) -+ tasklet_schedule(&tp->tl); -+ -+ return NETDEV_TX_OK; -+} -+ -+static void r8152b_reset_packet_filter(struct r8152 *tp) -+{ -+ u32 ocp_data; -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_FMC); -+ ocp_data &= ~FMC_FCR_MCU_EN; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_FMC, ocp_data); -+ ocp_data |= FMC_FCR_MCU_EN; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_FMC, ocp_data); -+} -+ -+static void rtl8152_nic_reset(struct r8152 *tp) -+{ -+ int i; -+ -+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, CR_RST); -+ -+ for (i = 0; i < 1000; i++) { -+ if (!(ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR) & CR_RST)) -+ break; -+ udelay(100); -+ } -+} -+ -+static void set_tx_qlen(struct r8152 *tp) -+{ -+ struct net_device *netdev = tp->netdev; -+ -+ tp->tx_qlen = rx_buf_sz / (netdev->mtu + VLAN_ETH_HLEN + VLAN_HLEN + -+ sizeof(struct tx_desc)); -+} -+ -+static inline u8 rtl8152_get_speed(struct r8152 *tp) -+{ -+ return ocp_read_byte(tp, MCU_TYPE_PLA, PLA_PHYSTATUS); -+} -+ -+static void rtl_set_eee_plus(struct r8152 *tp) -+{ -+ u32 ocp_data; -+ u8 speed; -+ -+ speed = rtl8152_get_speed(tp); -+ if (speed & _10bps) { -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR); -+ ocp_data |= EEEP_CR_EEEP_TX; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR, ocp_data); -+ } else { -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR); -+ ocp_data &= ~EEEP_CR_EEEP_TX; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR, ocp_data); -+ } -+} -+ -+static int rtl_enable(struct r8152 *tp) -+{ -+ u32 ocp_data; -+ int i, ret; -+ -+ r8152b_reset_packet_filter(tp); -+ -+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR); -+ ocp_data |= CR_RE | CR_TE; -+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, ocp_data); -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MISC_1); -+ ocp_data &= ~RXDY_GATED_EN; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MISC_1, ocp_data); -+ -+ INIT_LIST_HEAD(&tp->rx_done); -+ ret = 0; -+ for (i = 0; i < RTL8152_MAX_RX; i++) { -+ INIT_LIST_HEAD(&tp->rx_info[i].list); -+ ret |= r8152_submit_rx(tp, &tp->rx_info[i], GFP_KERNEL); -+ } -+ -+ return ret; -+} -+ -+static int rtl8152_enable(struct r8152 *tp) -+{ -+ set_tx_qlen(tp); -+ rtl_set_eee_plus(tp); -+ -+ return rtl_enable(tp); -+} -+ -+static void r8153_set_rx_agg(struct r8152 *tp) -+{ -+ u8 speed; -+ -+ speed = rtl8152_get_speed(tp); -+ if (speed & _1000bps) { -+ if (tp->udev->speed == USB_SPEED_SUPER) { -+ ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_BUF_TH, -+ RX_THR_SUPPER); -+ ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_EARLY_AGG, -+ EARLY_AGG_SUPPER); -+ } else { -+ ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_BUF_TH, -+ RX_THR_HIGH); -+ ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_EARLY_AGG, -+ EARLY_AGG_HIGH); -+ } -+ } else { -+ ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_BUF_TH, RX_THR_SLOW); -+ ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_EARLY_AGG, -+ EARLY_AGG_SLOW); -+ } -+} -+ -+static int rtl8153_enable(struct r8152 *tp) -+{ -+ set_tx_qlen(tp); -+ rtl_set_eee_plus(tp); -+ r8153_set_rx_agg(tp); -+ -+ return rtl_enable(tp); -+} -+ -+static void rtl8152_disable(struct r8152 *tp) -+{ -+ struct net_device_stats *stats = rtl8152_get_stats(tp->netdev); -+ struct sk_buff *skb; -+ u32 ocp_data; -+ int i; -+ -+ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); -+ ocp_data &= ~RCR_ACPT_ALL; -+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); -+ -+ while ((skb = skb_dequeue(&tp->tx_queue))) { -+ dev_kfree_skb(skb); -+ stats->tx_dropped++; -+ } -+ -+ for (i = 0; i < RTL8152_MAX_TX; i++) -+ usb_kill_urb(tp->tx_info[i].urb); -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MISC_1); -+ ocp_data |= RXDY_GATED_EN; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MISC_1, ocp_data); -+ -+ for (i = 0; i < 1000; i++) { -+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); -+ if ((ocp_data & FIFO_EMPTY) == FIFO_EMPTY) -+ break; -+ mdelay(1); -+ } -+ -+ for (i = 0; i < 1000; i++) { -+ if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR0) & TCR0_TX_EMPTY) -+ break; -+ mdelay(1); -+ } -+ -+ for (i = 0; i < RTL8152_MAX_RX; i++) -+ usb_kill_urb(tp->rx_info[i].urb); -+ -+ rtl8152_nic_reset(tp); -+} -+ -+static void r8152b_exit_oob(struct r8152 *tp) -+{ -+ u32 ocp_data; -+ int i; -+ -+ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); -+ ocp_data &= ~RCR_ACPT_ALL; -+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MISC_1); -+ ocp_data |= RXDY_GATED_EN; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MISC_1, ocp_data); -+ -+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML); -+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, 0x00); -+ -+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); -+ ocp_data &= ~NOW_IS_OOB; -+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); -+ ocp_data &= ~MCU_BORW_EN; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); -+ -+ for (i = 0; i < 1000; i++) { -+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); -+ if (ocp_data & LINK_LIST_READY) -+ break; -+ mdelay(1); -+ } -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); -+ ocp_data |= RE_INIT_LL; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); -+ -+ for (i = 0; i < 1000; i++) { -+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); -+ if (ocp_data & LINK_LIST_READY) -+ break; -+ mdelay(1); -+ } -+ -+ rtl8152_nic_reset(tp); -+ -+ /* rx share fifo credit full threshold */ -+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL0, RXFIFO_THR1_NORMAL); -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_DEV_STAT); -+ ocp_data &= STAT_SPEED_MASK; -+ if (ocp_data == STAT_SPEED_FULL) { -+ /* rx share fifo credit near full threshold */ -+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, -+ RXFIFO_THR2_FULL); -+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, -+ RXFIFO_THR3_FULL); -+ } else { -+ /* rx share fifo credit near full threshold */ -+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, -+ RXFIFO_THR2_HIGH); -+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, -+ RXFIFO_THR3_HIGH); -+ } -+ -+ /* TX share fifo free credit full threshold */ -+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_TXFIFO_CTRL, TXFIFO_THR_NORMAL); -+ -+ ocp_write_byte(tp, MCU_TYPE_USB, USB_TX_AGG, TX_AGG_MAX_THRESHOLD); -+ ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_BUF_TH, RX_THR_HIGH); -+ ocp_write_dword(tp, MCU_TYPE_USB, USB_TX_DMA, -+ TEST_MODE_DISABLE | TX_SIZE_ADJUST1); -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CPCR); -+ ocp_data &= ~CPCR_RX_VLAN; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_CPCR, ocp_data); -+ -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, RTL8152_RMS); -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR0); -+ ocp_data |= TCR0_AUTO_FIFO; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_TCR0, ocp_data); -+} -+ -+static void r8152b_enter_oob(struct r8152 *tp) -+{ -+ u32 ocp_data; -+ int i; -+ -+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); -+ ocp_data &= ~NOW_IS_OOB; -+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); -+ -+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL0, RXFIFO_THR1_OOB); -+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, RXFIFO_THR2_OOB); -+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, RXFIFO_THR3_OOB); -+ -+ rtl8152_disable(tp); -+ -+ for (i = 0; i < 1000; i++) { -+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); -+ if (ocp_data & LINK_LIST_READY) -+ break; -+ mdelay(1); -+ } -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); -+ ocp_data |= RE_INIT_LL; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); -+ -+ for (i = 0; i < 1000; i++) { -+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); -+ if (ocp_data & LINK_LIST_READY) -+ break; -+ mdelay(1); -+ } -+ -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, RTL8152_RMS); -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CFG_WOL); -+ ocp_data |= MAGIC_EN; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_CFG_WOL, ocp_data); -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CPCR); -+ ocp_data |= CPCR_RX_VLAN; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_CPCR, ocp_data); -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PAL_BDC_CR); -+ ocp_data |= ALDPS_PROXY_MODE; -+ ocp_write_word(tp, MCU_TYPE_PLA, PAL_BDC_CR, ocp_data); -+ -+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); -+ ocp_data |= NOW_IS_OOB | DIS_MCU_CLROOB; -+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); -+ -+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CONFIG5, LAN_WAKE_EN); -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MISC_1); -+ ocp_data &= ~RXDY_GATED_EN; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MISC_1, ocp_data); -+ -+ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); -+ ocp_data |= RCR_APM | RCR_AM | RCR_AB; -+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); -+} -+ -+static void r8152b_disable_aldps(struct r8152 *tp) -+{ -+ ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPDNPS | LINKENA | DIS_SDSAVE); -+ msleep(20); -+} -+ -+static inline void r8152b_enable_aldps(struct r8152 *tp) -+{ -+ ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPWRSAVE | ENPDNPS | -+ LINKENA | DIS_SDSAVE); -+} -+ -+static void r8153_hw_phy_cfg(struct r8152 *tp) -+{ -+ u32 ocp_data; -+ u16 data; -+ -+ ocp_reg_write(tp, OCP_ADC_CFG, CKADSEL_L | ADC_EN | EN_EMI_L); -+ r8152_mdio_write(tp, MII_BMCR, BMCR_ANENABLE); -+ -+ if (tp->version == RTL_VER_03) { -+ data = ocp_reg_read(tp, OCP_EEE_CFG); -+ data &= ~CTAP_SHORT_EN; -+ ocp_reg_write(tp, OCP_EEE_CFG, data); -+ } -+ -+ data = ocp_reg_read(tp, OCP_POWER_CFG); -+ data |= EEE_CLKDIV_EN; -+ ocp_reg_write(tp, OCP_POWER_CFG, data); -+ -+ data = ocp_reg_read(tp, OCP_DOWN_SPEED); -+ data |= EN_10M_BGOFF; -+ ocp_reg_write(tp, OCP_DOWN_SPEED, data); -+ data = ocp_reg_read(tp, OCP_POWER_CFG); -+ data |= EN_10M_PLLOFF; -+ ocp_reg_write(tp, OCP_POWER_CFG, data); -+ data = sram_read(tp, SRAM_IMPEDANCE); -+ data &= ~RX_DRIVING_MASK; -+ sram_write(tp, SRAM_IMPEDANCE, data); -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR); -+ ocp_data |= PFM_PWM_SWITCH; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data); -+ -+ data = sram_read(tp, SRAM_LPF_CFG); -+ data |= LPF_AUTO_TUNE; -+ sram_write(tp, SRAM_LPF_CFG, data); -+ -+ data = sram_read(tp, SRAM_10M_AMP1); -+ data |= GDAC_IB_UPALL; -+ sram_write(tp, SRAM_10M_AMP1, data); -+ data = sram_read(tp, SRAM_10M_AMP2); -+ data |= AMP_DN; -+ sram_write(tp, SRAM_10M_AMP2, data); -+} -+ -+static void r8153_u1u2en(struct r8152 *tp, int enable) -+{ -+ u8 u1u2[8]; -+ -+ if (enable) -+ memset(u1u2, 0xff, sizeof(u1u2)); -+ else -+ memset(u1u2, 0x00, sizeof(u1u2)); -+ -+ usb_ocp_write(tp, USB_TOLERANCE, BYTE_EN_SIX_BYTES, sizeof(u1u2), u1u2); -+} -+ -+static void r8153_u2p3en(struct r8152 *tp, int enable) -+{ -+ u32 ocp_data; -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_U2P3_CTRL); -+ if (enable) -+ ocp_data |= U2P3_ENABLE; -+ else -+ ocp_data &= ~U2P3_ENABLE; -+ ocp_write_word(tp, MCU_TYPE_USB, USB_U2P3_CTRL, ocp_data); -+} -+ -+static void r8153_power_cut_en(struct r8152 *tp, int enable) -+{ -+ u32 ocp_data; -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_POWER_CUT); -+ if (enable) -+ ocp_data |= PWR_EN | PHASE2_EN; -+ else -+ ocp_data &= ~(PWR_EN | PHASE2_EN); -+ ocp_write_word(tp, MCU_TYPE_USB, USB_POWER_CUT, ocp_data); -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0); -+ ocp_data &= ~PCUT_STATUS; -+ ocp_write_word(tp, MCU_TYPE_USB, USB_MISC_0, ocp_data); -+} -+ -+static void r8153_teredo_off(struct r8152 *tp) -+{ -+ u32 ocp_data; -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG); -+ ocp_data &= ~(TEREDO_SEL | TEREDO_RS_EVENT_MASK | OOB_TEREDO_EN); -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG, ocp_data); -+ -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_WDT6_CTRL, WDT6_SET_MODE); -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_REALWOW_TIMER, 0); -+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_TEREDO_TIMER, 0); -+} -+ -+static void r8153_first_init(struct r8152 *tp) -+{ -+ u32 ocp_data; -+ int i; -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MISC_1); -+ ocp_data |= RXDY_GATED_EN; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MISC_1, ocp_data); -+ -+ r8153_teredo_off(tp); -+ -+ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); -+ ocp_data &= ~RCR_ACPT_ALL; -+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); -+ -+ r8153_hw_phy_cfg(tp); -+ -+ rtl8152_nic_reset(tp); -+ -+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); -+ ocp_data &= ~NOW_IS_OOB; -+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); -+ ocp_data &= ~MCU_BORW_EN; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); -+ -+ for (i = 0; i < 1000; i++) { -+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); -+ if (ocp_data & LINK_LIST_READY) -+ break; -+ mdelay(1); -+ } -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); -+ ocp_data |= RE_INIT_LL; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); -+ -+ for (i = 0; i < 1000; i++) { -+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); -+ if (ocp_data & LINK_LIST_READY) -+ break; -+ mdelay(1); -+ } -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CPCR); -+ ocp_data &= ~CPCR_RX_VLAN; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_CPCR, ocp_data); -+ -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, RTL8152_RMS); -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR0); -+ ocp_data |= TCR0_AUTO_FIFO; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_TCR0, ocp_data); -+ -+ rtl8152_nic_reset(tp); -+ -+ /* rx share fifo credit full threshold */ -+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL0, RXFIFO_THR1_NORMAL); -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, RXFIFO_THR2_NORMAL); -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, RXFIFO_THR3_NORMAL); -+ /* TX share fifo free credit full threshold */ -+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_TXFIFO_CTRL, TXFIFO_THR_NORMAL2); -+ -+ /* rx aggregation */ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL); -+ ocp_data &= ~RX_AGG_DISABLE; -+ ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data); -+} -+ -+static void r8153_enter_oob(struct r8152 *tp) -+{ -+ u32 ocp_data; -+ int i; -+ -+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); -+ ocp_data &= ~NOW_IS_OOB; -+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); -+ -+ rtl8152_disable(tp); -+ -+ for (i = 0; i < 1000; i++) { -+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); -+ if (ocp_data & LINK_LIST_READY) -+ break; -+ mdelay(1); -+ } -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); -+ ocp_data |= RE_INIT_LL; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); -+ -+ for (i = 0; i < 1000; i++) { -+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); -+ if (ocp_data & LINK_LIST_READY) -+ break; -+ mdelay(1); -+ } -+ -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, RTL8152_RMS); -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CFG_WOL); -+ ocp_data |= MAGIC_EN; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_CFG_WOL, ocp_data); -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG); -+ ocp_data &= ~TEREDO_WAKE_MASK; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG, ocp_data); -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CPCR); -+ ocp_data |= CPCR_RX_VLAN; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_CPCR, ocp_data); -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PAL_BDC_CR); -+ ocp_data |= ALDPS_PROXY_MODE; -+ ocp_write_word(tp, MCU_TYPE_PLA, PAL_BDC_CR, ocp_data); -+ -+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); -+ ocp_data |= NOW_IS_OOB | DIS_MCU_CLROOB; -+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); -+ -+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CONFIG5, LAN_WAKE_EN); -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MISC_1); -+ ocp_data &= ~RXDY_GATED_EN; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MISC_1, ocp_data); -+ -+ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); -+ ocp_data |= RCR_APM | RCR_AM | RCR_AB; -+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); -+} -+ -+static void r8153_disable_aldps(struct r8152 *tp) -+{ -+ u16 data; -+ -+ data = ocp_reg_read(tp, OCP_POWER_CFG); -+ data &= ~EN_ALDPS; -+ ocp_reg_write(tp, OCP_POWER_CFG, data); -+ msleep(20); -+} -+ -+static void r8153_enable_aldps(struct r8152 *tp) -+{ -+ u16 data; -+ -+ data = ocp_reg_read(tp, OCP_POWER_CFG); -+ data |= EN_ALDPS; -+ ocp_reg_write(tp, OCP_POWER_CFG, data); -+} -+ -+static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u16 speed, u8 duplex) -+{ -+ u16 bmcr, anar, gbcr; -+ int ret = 0; -+ -+ cancel_delayed_work_sync(&tp->schedule); -+ anar = r8152_mdio_read(tp, MII_ADVERTISE); -+ anar &= ~(ADVERTISE_10HALF | ADVERTISE_10FULL | -+ ADVERTISE_100HALF | ADVERTISE_100FULL); -+ if (tp->mii.supports_gmii) { -+ gbcr = r8152_mdio_read(tp, MII_CTRL1000); -+ gbcr &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); -+ } else { -+ gbcr = 0; -+ } -+ -+ if (autoneg == AUTONEG_DISABLE) { -+ if (speed == SPEED_10) { -+ bmcr = 0; -+ anar |= ADVERTISE_10HALF | ADVERTISE_10FULL; -+ } else if (speed == SPEED_100) { -+ bmcr = BMCR_SPEED100; -+ anar |= ADVERTISE_100HALF | ADVERTISE_100FULL; -+ } else if (speed == SPEED_1000 && tp->mii.supports_gmii) { -+ bmcr = BMCR_SPEED1000; -+ gbcr |= ADVERTISE_1000FULL | ADVERTISE_1000HALF; -+ } else { -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ if (duplex == DUPLEX_FULL) -+ bmcr |= BMCR_FULLDPLX; -+ } else { -+ if (speed == SPEED_10) { -+ if (duplex == DUPLEX_FULL) -+ anar |= ADVERTISE_10HALF | ADVERTISE_10FULL; -+ else -+ anar |= ADVERTISE_10HALF; -+ } else if (speed == SPEED_100) { -+ if (duplex == DUPLEX_FULL) { -+ anar |= ADVERTISE_10HALF | ADVERTISE_10FULL; -+ anar |= ADVERTISE_100HALF | ADVERTISE_100FULL; -+ } else { -+ anar |= ADVERTISE_10HALF; -+ anar |= ADVERTISE_100HALF; -+ } -+ } else if (speed == SPEED_1000 && tp->mii.supports_gmii) { -+ if (duplex == DUPLEX_FULL) { -+ anar |= ADVERTISE_10HALF | ADVERTISE_10FULL; -+ anar |= ADVERTISE_100HALF | ADVERTISE_100FULL; -+ gbcr |= ADVERTISE_1000FULL | ADVERTISE_1000HALF; -+ } else { -+ anar |= ADVERTISE_10HALF; -+ anar |= ADVERTISE_100HALF; -+ gbcr |= ADVERTISE_1000HALF; -+ } -+ } else { -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ bmcr = BMCR_ANENABLE | BMCR_ANRESTART; -+ } -+ -+ if (tp->mii.supports_gmii) -+ r8152_mdio_write(tp, MII_CTRL1000, gbcr); -+ -+ r8152_mdio_write(tp, MII_ADVERTISE, anar); -+ r8152_mdio_write(tp, MII_BMCR, bmcr); -+ -+out: -+ -+ return ret; -+} -+ -+static void rtl8152_down(struct r8152 *tp) -+{ -+ u32 ocp_data; -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_UPS_CTRL); -+ ocp_data &= ~POWER_CUT; -+ ocp_write_word(tp, MCU_TYPE_USB, USB_UPS_CTRL, ocp_data); -+ -+ r8152b_disable_aldps(tp); -+ r8152b_enter_oob(tp); -+ r8152b_enable_aldps(tp); -+} -+ -+static void rtl8153_down(struct r8152 *tp) -+{ -+ r8153_u1u2en(tp, 0); -+ r8153_power_cut_en(tp, 0); -+ r8153_disable_aldps(tp); -+ r8153_enter_oob(tp); -+ r8153_enable_aldps(tp); -+} -+ -+static void set_carrier(struct r8152 *tp) -+{ -+ struct net_device *netdev = tp->netdev; -+ u8 speed; -+ -+ clear_bit(RTL8152_LINK_CHG, &tp->flags); -+ speed = rtl8152_get_speed(tp); -+ -+ if (speed & LINK_STATUS) { -+ if (!(tp->speed & LINK_STATUS)) { -+ tp->rtl_ops.enable(tp); -+ set_bit(RTL8152_SET_RX_MODE, &tp->flags); -+ netif_carrier_on(netdev); -+ } -+ } else { -+ if (tp->speed & LINK_STATUS) { -+ netif_carrier_off(netdev); -+ tasklet_disable(&tp->tl); -+ tp->rtl_ops.disable(tp); -+ tasklet_enable(&tp->tl); -+ } -+ } -+ tp->speed = speed; -+} -+ -+static void rtl_work_func_t(struct work_struct *work) -+{ -+ struct r8152 *tp = container_of(work, struct r8152, schedule.work); -+ -+ if (!test_bit(WORK_ENABLE, &tp->flags)) -+ goto out1; -+ -+ if (test_bit(RTL8152_UNPLUG, &tp->flags)) -+ goto out1; -+ -+ if (test_bit(RTL8152_LINK_CHG, &tp->flags)) -+ set_carrier(tp); -+ -+ if (test_bit(RTL8152_SET_RX_MODE, &tp->flags)) -+ _rtl8152_set_rx_mode(tp->netdev); -+ -+out1: -+ return; -+} -+ -+static int rtl8152_open(struct net_device *netdev) -+{ -+ struct r8152 *tp = netdev_priv(netdev); -+ int res = 0; -+ -+ rtl8152_set_speed(tp, AUTONEG_ENABLE, -+ tp->mii.supports_gmii ? SPEED_1000 : SPEED_100, -+ DUPLEX_FULL); -+ tp->speed = 0; -+ netif_carrier_off(netdev); -+ netif_start_queue(netdev); -+ set_bit(WORK_ENABLE, &tp->flags); -+ res = usb_submit_urb(tp->intr_urb, GFP_KERNEL); -+ if (res) { -+ if (res == -ENODEV) -+ netif_device_detach(tp->netdev); -+ netif_warn(tp, ifup, netdev, "intr_urb submit failed: %d\n", -+ res); -+ } -+ -+ -+ return res; -+} -+ -+static int rtl8152_close(struct net_device *netdev) -+{ -+ struct r8152 *tp = netdev_priv(netdev); -+ int res = 0; -+ -+ clear_bit(WORK_ENABLE, &tp->flags); -+ usb_kill_urb(tp->intr_urb); -+ cancel_delayed_work_sync(&tp->schedule); -+ netif_stop_queue(netdev); -+ tasklet_disable(&tp->tl); -+ tp->rtl_ops.disable(tp); -+ tasklet_enable(&tp->tl); -+ -+ return res; -+} -+ -+static void rtl_clear_bp(struct r8152 *tp) -+{ -+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_BP_0, 0); -+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_BP_2, 0); -+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_BP_4, 0); -+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_BP_6, 0); -+ ocp_write_dword(tp, MCU_TYPE_USB, USB_BP_0, 0); -+ ocp_write_dword(tp, MCU_TYPE_USB, USB_BP_2, 0); -+ ocp_write_dword(tp, MCU_TYPE_USB, USB_BP_4, 0); -+ ocp_write_dword(tp, MCU_TYPE_USB, USB_BP_6, 0); -+ mdelay(3); -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_BP_BA, 0); -+ ocp_write_word(tp, MCU_TYPE_USB, USB_BP_BA, 0); -+} -+ -+static void r8153_clear_bp(struct r8152 *tp) -+{ -+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_BP_EN, 0); -+ ocp_write_byte(tp, MCU_TYPE_USB, USB_BP_EN, 0); -+ rtl_clear_bp(tp); -+} -+ -+static void r8152b_enable_eee(struct r8152 *tp) -+{ -+ u32 ocp_data; -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEE_CR); -+ ocp_data |= EEE_RX_EN | EEE_TX_EN; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEE_CR, ocp_data); -+ ocp_reg_write(tp, OCP_EEE_CONFIG1, RG_TXLPI_MSK_HFDUP | RG_MATCLR_EN | -+ EEE_10_CAP | EEE_NWAY_EN | -+ TX_QUIET_EN | RX_QUIET_EN | -+ SDRISETIME | RG_RXLPI_MSK_HFDUP | -+ SDFALLTIME); -+ ocp_reg_write(tp, OCP_EEE_CONFIG2, RG_LPIHYS_NUM | RG_DACQUIET_EN | -+ RG_LDVQUIET_EN | RG_CKRSEL | -+ RG_EEEPRG_EN); -+ ocp_reg_write(tp, OCP_EEE_CONFIG3, FST_SNR_EYE_R | RG_LFS_SEL | MSK_PH); -+ ocp_reg_write(tp, OCP_EEE_AR, FUN_ADDR | DEVICE_ADDR); -+ ocp_reg_write(tp, OCP_EEE_DATA, EEE_ADDR); -+ ocp_reg_write(tp, OCP_EEE_AR, FUN_DATA | DEVICE_ADDR); -+ ocp_reg_write(tp, OCP_EEE_DATA, EEE_DATA); -+ ocp_reg_write(tp, OCP_EEE_AR, 0x0000); -+} -+ -+static void r8153_enable_eee(struct r8152 *tp) -+{ -+ u32 ocp_data; -+ u16 data; -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEE_CR); -+ ocp_data |= EEE_RX_EN | EEE_TX_EN; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEE_CR, ocp_data); -+ data = ocp_reg_read(tp, OCP_EEE_CFG); -+ data |= EEE10_EN; -+ ocp_reg_write(tp, OCP_EEE_CFG, data); -+ data = ocp_reg_read(tp, OCP_EEE_CFG2); -+ data |= MY1000_EEE | MY100_EEE; -+ ocp_reg_write(tp, OCP_EEE_CFG2, data); -+} -+ -+static void r8152b_enable_fc(struct r8152 *tp) -+{ -+ u16 anar; -+ -+ anar = r8152_mdio_read(tp, MII_ADVERTISE); -+ anar |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM; -+ r8152_mdio_write(tp, MII_ADVERTISE, anar); -+} -+ -+static void r8152b_hw_phy_cfg(struct r8152 *tp) -+{ -+ r8152_mdio_write(tp, MII_BMCR, BMCR_ANENABLE); -+ r8152b_disable_aldps(tp); -+} -+ -+static void r8152b_init(struct r8152 *tp) -+{ -+ u32 ocp_data; -+ int i; -+ -+ rtl_clear_bp(tp); -+ -+ if (tp->version == RTL_VER_01) { -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE); -+ ocp_data &= ~LED_MODE_MASK; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE, ocp_data); -+ } -+ -+ r8152b_hw_phy_cfg(tp); -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_UPS_CTRL); -+ ocp_data &= ~POWER_CUT; -+ ocp_write_word(tp, MCU_TYPE_USB, USB_UPS_CTRL, ocp_data); -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_PM_CTRL_STATUS); -+ ocp_data &= ~RESUME_INDICATE; -+ ocp_write_word(tp, MCU_TYPE_USB, USB_PM_CTRL_STATUS, ocp_data); -+ -+ r8152b_exit_oob(tp); -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR); -+ ocp_data |= TX_10M_IDLE_EN | PFM_PWM_SWITCH; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data); -+ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL); -+ ocp_data &= ~MCU_CLK_RATIO_MASK; -+ ocp_data |= MCU_CLK_RATIO | D3_CLK_GATED_EN; -+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL, ocp_data); -+ ocp_data = GPHY_STS_MSK | SPEED_DOWN_MSK | -+ SPDWN_RXDV_MSK | SPDWN_LINKCHG_MSK; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_GPHY_INTR_IMR, ocp_data); -+ -+ r8152b_enable_eee(tp); -+ r8152b_enable_aldps(tp); -+ r8152b_enable_fc(tp); -+ -+ r8152_mdio_write(tp, MII_BMCR, BMCR_RESET | BMCR_ANENABLE | -+ BMCR_ANRESTART); -+ for (i = 0; i < 100; i++) { -+ udelay(100); -+ if (!(r8152_mdio_read(tp, MII_BMCR) & BMCR_RESET)) -+ break; -+ } -+ -+ /* enable rx aggregation */ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL); -+ ocp_data &= ~RX_AGG_DISABLE; -+ ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data); -+} -+ -+static void r8153_init(struct r8152 *tp) -+{ -+ u32 ocp_data; -+ int i; -+ -+ r8153_u1u2en(tp, 0); -+ -+ for (i = 0; i < 500; i++) { -+ if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_BOOT_CTRL) & -+ AUTOLOAD_DONE) -+ break; -+ msleep(20); -+ } -+ -+ for (i = 0; i < 500; i++) { -+ ocp_data = ocp_reg_read(tp, OCP_PHY_STATUS) & PHY_STAT_MASK; -+ if (ocp_data == PHY_STAT_LAN_ON || ocp_data == PHY_STAT_PWRDN) -+ break; -+ msleep(20); -+ } -+ -+ r8153_u2p3en(tp, 0); -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_WDT11_CTRL); -+ ocp_data &= ~TIMER11_EN; -+ ocp_write_word(tp, MCU_TYPE_USB, USB_WDT11_CTRL, ocp_data); -+ -+ r8153_clear_bp(tp); -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE); -+ ocp_data &= ~LED_MODE_MASK; -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE, ocp_data); -+ -+ ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_LPM_CTRL); -+ ocp_data &= ~LPM_TIMER_MASK; -+ if (tp->udev->speed == USB_SPEED_SUPER) -+ ocp_data |= LPM_TIMER_500US; -+ else -+ ocp_data |= LPM_TIMER_500MS; -+ ocp_write_byte(tp, MCU_TYPE_USB, USB_LPM_CTRL, ocp_data); -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_AFE_CTRL2); -+ ocp_data &= ~SEN_VAL_MASK; -+ ocp_data |= SEN_VAL_NORMAL | SEL_RXIDLE; -+ ocp_write_word(tp, MCU_TYPE_USB, USB_AFE_CTRL2, ocp_data); -+ -+ r8153_power_cut_en(tp, 0); -+ r8153_u1u2en(tp, 1); -+ -+ r8153_first_init(tp); -+ -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL, ALDPS_SPDWN_RATIO); -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2, EEE_SPDWN_RATIO); -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, -+ PKT_AVAIL_SPDWN_EN | SUSPEND_SPDWN_EN | -+ U1U2_SPDWN_EN | L1_SPDWN_EN); -+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4, -+ PWRSAVE_SPDWN_EN | RXDV_SPDWN_EN | TX10MIDLE_EN | -+ TP100_SPDWN_EN | TP500_SPDWN_EN | TP1000_SPDWN_EN | -+ EEE_SPDWN_EN); -+ -+ r8153_enable_eee(tp); -+ r8153_enable_aldps(tp); -+ r8152b_enable_fc(tp); -+ -+ r8152_mdio_write(tp, MII_BMCR, BMCR_RESET | BMCR_ANENABLE | -+ BMCR_ANRESTART); -+} -+ -+static int rtl8152_suspend(struct usb_interface *intf, pm_message_t message) -+{ -+ struct r8152 *tp = usb_get_intfdata(intf); -+ -+ netif_device_detach(tp->netdev); -+ -+ if (netif_running(tp->netdev)) { -+ clear_bit(WORK_ENABLE, &tp->flags); -+ usb_kill_urb(tp->intr_urb); -+ cancel_delayed_work_sync(&tp->schedule); -+ tasklet_disable(&tp->tl); -+ } -+ -+ tp->rtl_ops.down(tp); -+ -+ return 0; -+} -+ -+static int rtl8152_resume(struct usb_interface *intf) -+{ -+ struct r8152 *tp = usb_get_intfdata(intf); -+ -+ tp->rtl_ops.init(tp); -+ netif_device_attach(tp->netdev); -+ if (netif_running(tp->netdev)) { -+ rtl8152_set_speed(tp, AUTONEG_ENABLE, -+ tp->mii.supports_gmii ? SPEED_1000 : SPEED_100, -+ DUPLEX_FULL); -+ tp->speed = 0; -+ netif_carrier_off(tp->netdev); -+ set_bit(WORK_ENABLE, &tp->flags); -+ usb_submit_urb(tp->intr_urb, GFP_KERNEL); -+ tasklet_enable(&tp->tl); -+ } -+ -+ return 0; -+} -+ -+static void rtl8152_get_drvinfo(struct net_device *netdev, -+ struct ethtool_drvinfo *info) -+{ -+ struct r8152 *tp = netdev_priv(netdev); -+ -+ strncpy(info->driver, MODULENAME, ETHTOOL_BUSINFO_LEN); -+ strncpy(info->version, DRIVER_VERSION, ETHTOOL_BUSINFO_LEN); -+ usb_make_path(tp->udev, info->bus_info, sizeof(info->bus_info)); -+} -+ -+static -+int rtl8152_get_settings(struct net_device *netdev, struct ethtool_cmd *cmd) -+{ -+ struct r8152 *tp = netdev_priv(netdev); -+ -+ if (!tp->mii.mdio_read) -+ return -EOPNOTSUPP; -+ -+ return mii_ethtool_gset(&tp->mii, cmd); -+} -+ -+static int rtl8152_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) -+{ -+ struct r8152 *tp = netdev_priv(dev); -+ -+ return rtl8152_set_speed(tp, cmd->autoneg, cmd->speed, cmd->duplex); -+} -+ -+static struct ethtool_ops ops = { -+ .get_drvinfo = rtl8152_get_drvinfo, -+ .get_settings = rtl8152_get_settings, -+ .set_settings = rtl8152_set_settings, -+ .get_link = ethtool_op_get_link, -+}; -+ -+static int rtl8152_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) -+{ -+ struct r8152 *tp = netdev_priv(netdev); -+ struct mii_ioctl_data *data = if_mii(rq); -+ int res = 0; -+ -+ switch (cmd) { -+ case SIOCGMIIPHY: -+ data->phy_id = R8152_PHY_ID; /* Internal PHY */ -+ break; -+ -+ case SIOCGMIIREG: -+ data->val_out = r8152_mdio_read(tp, data->reg_num); -+ break; -+ -+ case SIOCSMIIREG: -+ if (!capable(CAP_NET_ADMIN)) { -+ res = -EPERM; -+ break; -+ } -+ r8152_mdio_write(tp, data->reg_num, data->val_in); -+ break; -+ -+ default: -+ res = -EOPNOTSUPP; -+ } -+ -+ return res; -+} -+ -+static const struct net_device_ops rtl8152_netdev_ops = { -+ .ndo_open = rtl8152_open, -+ .ndo_stop = rtl8152_close, -+ .ndo_do_ioctl = rtl8152_ioctl, -+ .ndo_start_xmit = rtl8152_start_xmit, -+ .ndo_tx_timeout = rtl8152_tx_timeout, -+ .ndo_set_rx_mode = rtl8152_set_rx_mode, -+ .ndo_set_mac_address = rtl8152_set_mac_address, -+ -+ .ndo_change_mtu = eth_change_mtu, -+ .ndo_validate_addr = eth_validate_addr, -+}; -+ -+static void r8152b_get_version(struct r8152 *tp) -+{ -+ u32 ocp_data; -+ u16 version; -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR1); -+ version = (u16)(ocp_data & VERSION_MASK); -+ -+ switch (version) { -+ case 0x4c00: -+ tp->version = RTL_VER_01; -+ break; -+ case 0x4c10: -+ tp->version = RTL_VER_02; -+ break; -+ case 0x5c00: -+ tp->version = RTL_VER_03; -+ tp->mii.supports_gmii = 1; -+ break; -+ case 0x5c10: -+ tp->version = RTL_VER_04; -+ tp->mii.supports_gmii = 1; -+ break; -+ case 0x5c20: -+ tp->version = RTL_VER_05; -+ tp->mii.supports_gmii = 1; -+ break; -+ default: -+ netif_info(tp, probe, tp->netdev, -+ "Unknown version 0x%04x\n", version); -+ break; -+ } -+} -+ -+static void rtl8152_unload(struct r8152 *tp) -+{ -+ u32 ocp_data; -+ -+ if (tp->version != RTL_VER_01) { -+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_UPS_CTRL); -+ ocp_data |= POWER_CUT; -+ ocp_write_word(tp, MCU_TYPE_USB, USB_UPS_CTRL, ocp_data); -+ } -+ -+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_PM_CTRL_STATUS); -+ ocp_data &= ~RESUME_INDICATE; -+ ocp_write_word(tp, MCU_TYPE_USB, USB_PM_CTRL_STATUS, ocp_data); -+} -+ -+static void rtl8153_unload(struct r8152 *tp) -+{ -+ r8153_power_cut_en(tp, 1); -+} -+ -+static int rtl_ops_init(struct r8152 *tp, const struct usb_device_id *id) -+{ -+ struct rtl_ops *ops = &tp->rtl_ops; -+ int ret = -ENODEV; -+ -+ switch (id->idVendor) { -+ case VENDOR_ID_REALTEK: -+ switch (id->idProduct) { -+ case PRODUCT_ID_RTL8152: -+ ops->init = r8152b_init; -+ ops->enable = rtl8152_enable; -+ ops->disable = rtl8152_disable; -+ ops->down = rtl8152_down; -+ ops->unload = rtl8152_unload; -+ ret = 0; -+ break; -+ case PRODUCT_ID_RTL8153: -+ ops->init = r8153_init; -+ ops->enable = rtl8153_enable; -+ ops->disable = rtl8152_disable; -+ ops->down = rtl8153_down; -+ ops->unload = rtl8153_unload; -+ ret = 0; -+ break; -+ default: -+ break; -+ } -+ break; -+ -+ case VENDOR_ID_SAMSUNG: -+ switch (id->idProduct) { -+ case PRODUCT_ID_SAMSUNG: -+ ops->init = r8153_init; -+ ops->enable = rtl8153_enable; -+ ops->disable = rtl8152_disable; -+ ops->down = rtl8153_down; -+ ops->unload = rtl8153_unload; -+ ret = 0; -+ break; -+ default: -+ break; -+ } -+ break; -+ -+ default: -+ break; -+ } -+ -+ if (ret) -+ netif_err(tp, probe, tp->netdev, "Unknown Device\n"); -+ -+ return ret; -+} -+ -+static int rtl8152_probe(struct usb_interface *intf, -+ const struct usb_device_id *id) -+{ -+ struct usb_device *udev = interface_to_usbdev(intf); -+ struct r8152 *tp; -+ struct net_device *netdev; -+ int ret; -+ -+ if (udev->actconfig->desc.bConfigurationValue != 1) { -+ usb_driver_set_configuration(udev, 1); -+ return -ENODEV; -+ } -+ -+ usb_reset_device(udev); -+ netdev = alloc_etherdev(sizeof(struct r8152)); -+ if (!netdev) { -+ dev_err(&intf->dev, "Out of memory\n"); -+ return -ENOMEM; -+ } -+ -+ SET_NETDEV_DEV(netdev, &intf->dev); -+ tp = netdev_priv(netdev); -+ tp->msg_enable = 0x7FFF; -+ -+ tp->udev = udev; -+ tp->netdev = netdev; -+ tp->intf = intf; -+ -+ ret = rtl_ops_init(tp, id); -+ if (ret) -+ goto out; -+ -+ tasklet_init(&tp->tl, bottom_half, (unsigned long)tp); -+ INIT_DELAYED_WORK(&tp->schedule, rtl_work_func_t); -+ -+ netdev->netdev_ops = &rtl8152_netdev_ops; -+ netdev->watchdog_timeo = RTL8152_TX_TIMEOUT; -+ -+ netdev->features |= NETIF_F_IP_CSUM; -+ netdev->hw_features = NETIF_F_IP_CSUM; -+ SET_ETHTOOL_OPS(netdev, &ops); -+ -+ tp->mii.dev = netdev; -+ tp->mii.mdio_read = read_mii_word; -+ tp->mii.mdio_write = write_mii_word; -+ tp->mii.phy_id_mask = 0x3f; -+ tp->mii.reg_num_mask = 0x1f; -+ tp->mii.phy_id = R8152_PHY_ID; -+ tp->mii.supports_gmii = 0; -+ -+ r8152b_get_version(tp); -+ tp->rtl_ops.init(tp); -+ set_ethernet_addr(tp); -+ -+ ret = alloc_all_mem(tp); -+ if (ret) -+ goto out; -+ -+ usb_set_intfdata(intf, tp); -+ -+ ret = register_netdev(netdev); -+ if (ret != 0) { -+ netif_err(tp, probe, netdev, "couldn't register the device\n"); -+ goto out1; -+ } -+ -+ netif_info(tp, probe, netdev, "%s\n", DRIVER_VERSION); -+ -+ return 0; -+ -+out1: -+ usb_set_intfdata(intf, NULL); -+out: -+ free_netdev(netdev); -+ return ret; -+} -+ -+static void rtl8152_disconnect(struct usb_interface *intf) -+{ -+ struct r8152 *tp = usb_get_intfdata(intf); -+ -+ usb_set_intfdata(intf, NULL); -+ if (tp) { -+ set_bit(RTL8152_UNPLUG, &tp->flags); -+ tasklet_kill(&tp->tl); -+ unregister_netdev(tp->netdev); -+ tp->rtl_ops.unload(tp); -+ free_all_mem(tp); -+ free_netdev(tp->netdev); -+ } -+} -+ -+/* table of devices that work with this driver */ -+static struct usb_device_id rtl8152_table[] = { -+ {USB_DEVICE(VENDOR_ID_REALTEK, PRODUCT_ID_RTL8152)}, -+ {USB_DEVICE(VENDOR_ID_REALTEK, PRODUCT_ID_RTL8153)}, -+ {USB_DEVICE(VENDOR_ID_SAMSUNG, PRODUCT_ID_SAMSUNG)}, -+ {USB_DEVICE(VENDOR_ID_LENOVO, PRODUCT_ID_LENOVO)}, -+ {USB_DEVICE(VENDOR_ID_NVIDIA, PRODUCT_ID_NVIDIA)}, -+ {} -+}; -+ -+MODULE_DEVICE_TABLE(usb, rtl8152_table); -+ -+static struct usb_driver rtl8152_driver = { -+ .name = MODULENAME, -+ .id_table = rtl8152_table, -+ .probe = rtl8152_probe, -+ .disconnect = rtl8152_disconnect, -+ .suspend = rtl8152_suspend, -+ .resume = rtl8152_resume, -+ .reset_resume = rtl8152_resume, -+}; -+ -+module_usb_driver(rtl8152_driver); -+ -+MODULE_AUTHOR(DRIVER_AUTHOR); -+MODULE_DESCRIPTION(DRIVER_DESC); -+MODULE_LICENSE("GPL"); -diff -Naur backports-4.2.6-1.org/drivers/net/usb/rtl8150.c backports-4.2.6-1/drivers/net/usb/rtl8150.c ---- backports-4.2.6-1.org/drivers/net/usb/rtl8150.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/rtl8150.c 2016-06-28 14:35:18.001973885 +0200 -@@ -0,0 +1,949 @@ -+/* -+ * Copyright (c) 2002 Petko Manolov (petkan@users.sourceforge.net) -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * version 2 as published by the Free Software Foundation. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* Version Information */ -+#define DRIVER_VERSION "v0.6.2 (2004/08/27)" -+#define DRIVER_AUTHOR "Petko Manolov " -+#define DRIVER_DESC "rtl8150 based usb-ethernet driver" -+ -+#define IDR 0x0120 -+#define MAR 0x0126 -+#define CR 0x012e -+#define TCR 0x012f -+#define RCR 0x0130 -+#define TSR 0x0132 -+#define RSR 0x0133 -+#define CON0 0x0135 -+#define CON1 0x0136 -+#define MSR 0x0137 -+#define PHYADD 0x0138 -+#define PHYDAT 0x0139 -+#define PHYCNT 0x013b -+#define GPPC 0x013d -+#define BMCR 0x0140 -+#define BMSR 0x0142 -+#define ANAR 0x0144 -+#define ANLP 0x0146 -+#define AER 0x0148 -+#define CSCR 0x014C /* This one has the link status */ -+#define CSCR_LINK_STATUS (1 << 3) -+ -+#define IDR_EEPROM 0x1202 -+ -+#define PHY_READ 0 -+#define PHY_WRITE 0x20 -+#define PHY_GO 0x40 -+ -+#define MII_TIMEOUT 10 -+#define INTBUFSIZE 8 -+ -+#define RTL8150_REQT_READ 0xc0 -+#define RTL8150_REQT_WRITE 0x40 -+#define RTL8150_REQ_GET_REGS 0x05 -+#define RTL8150_REQ_SET_REGS 0x05 -+ -+ -+/* Transmit status register errors */ -+#define TSR_ECOL (1<<5) -+#define TSR_LCOL (1<<4) -+#define TSR_LOSS_CRS (1<<3) -+#define TSR_JBR (1<<2) -+#define TSR_ERRORS (TSR_ECOL | TSR_LCOL | TSR_LOSS_CRS | TSR_JBR) -+/* Receive status register errors */ -+#define RSR_CRC (1<<2) -+#define RSR_FAE (1<<1) -+#define RSR_ERRORS (RSR_CRC | RSR_FAE) -+ -+/* Media status register definitions */ -+#define MSR_DUPLEX (1<<4) -+#define MSR_SPEED (1<<3) -+#define MSR_LINK (1<<2) -+ -+/* Interrupt pipe data */ -+#define INT_TSR 0x00 -+#define INT_RSR 0x01 -+#define INT_MSR 0x02 -+#define INT_WAKSR 0x03 -+#define INT_TXOK_CNT 0x04 -+#define INT_RXLOST_CNT 0x05 -+#define INT_CRERR_CNT 0x06 -+#define INT_COL_CNT 0x07 -+ -+ -+#define RTL8150_MTU 1540 -+#define RTL8150_TX_TIMEOUT (HZ) -+#define RX_SKB_POOL_SIZE 4 -+ -+/* rtl8150 flags */ -+#define RTL8150_HW_CRC 0 -+#define RX_REG_SET 1 -+#define RTL8150_UNPLUG 2 -+#define RX_URB_FAIL 3 -+ -+/* Define these values to match your device */ -+#define VENDOR_ID_REALTEK 0x0bda -+#define VENDOR_ID_MELCO 0x0411 -+#define VENDOR_ID_MICRONET 0x3980 -+#define VENDOR_ID_LONGSHINE 0x07b8 -+#define VENDOR_ID_OQO 0x1557 -+#define VENDOR_ID_ZYXEL 0x0586 -+ -+#define PRODUCT_ID_RTL8150 0x8150 -+#define PRODUCT_ID_LUAKTX 0x0012 -+#define PRODUCT_ID_LCS8138TX 0x401a -+#define PRODUCT_ID_SP128AR 0x0003 -+#define PRODUCT_ID_PRESTIGE 0x401a -+ -+#undef EEPROM_WRITE -+ -+/* table of devices that work with this driver */ -+static struct usb_device_id rtl8150_table[] = { -+ {USB_DEVICE(VENDOR_ID_REALTEK, PRODUCT_ID_RTL8150)}, -+ {USB_DEVICE(VENDOR_ID_MELCO, PRODUCT_ID_LUAKTX)}, -+ {USB_DEVICE(VENDOR_ID_MICRONET, PRODUCT_ID_SP128AR)}, -+ {USB_DEVICE(VENDOR_ID_LONGSHINE, PRODUCT_ID_LCS8138TX)}, -+ {USB_DEVICE(VENDOR_ID_OQO, PRODUCT_ID_RTL8150)}, -+ {USB_DEVICE(VENDOR_ID_ZYXEL, PRODUCT_ID_PRESTIGE)}, -+ {} -+}; -+ -+MODULE_DEVICE_TABLE(usb, rtl8150_table); -+ -+struct rtl8150 { -+ unsigned long flags; -+ struct usb_device *udev; -+ struct tasklet_struct tl; -+ struct net_device *netdev; -+ struct urb *rx_urb, *tx_urb, *intr_urb; -+ struct sk_buff *tx_skb, *rx_skb; -+ struct sk_buff *rx_skb_pool[RX_SKB_POOL_SIZE]; -+ spinlock_t rx_pool_lock; -+ struct usb_ctrlrequest dr; -+ int intr_interval; -+ u8 *intr_buff; -+ u8 phy; -+}; -+ -+typedef struct rtl8150 rtl8150_t; -+ -+struct async_req { -+ struct usb_ctrlrequest dr; -+ u16 rx_creg; -+}; -+ -+static const char driver_name [] = "rtl8150"; -+ -+/* -+** -+** device related part of the code -+** -+*/ -+static int get_registers(rtl8150_t * dev, u16 indx, u16 size, void *data) -+{ -+ return usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), -+ RTL8150_REQ_GET_REGS, RTL8150_REQT_READ, -+ indx, 0, data, size, 500); -+} -+ -+static int set_registers(rtl8150_t * dev, u16 indx, u16 size, void *data) -+{ -+ return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), -+ RTL8150_REQ_SET_REGS, RTL8150_REQT_WRITE, -+ indx, 0, data, size, 500); -+} -+ -+static void async_set_reg_cb(struct urb *urb) -+{ -+ struct async_req *req = (struct async_req *)urb->context; -+ int status = urb->status; -+ -+ if (status < 0) -+ dev_dbg(&urb->dev->dev, "%s failed with %d", __func__, status); -+ kfree(req); -+ usb_free_urb(urb); -+} -+ -+static int async_set_registers(rtl8150_t *dev, u16 indx, u16 size, u16 reg) -+{ -+ int res = -ENOMEM; -+ struct urb *async_urb; -+ struct async_req *req; -+ -+ req = kmalloc(sizeof(struct async_req), GFP_ATOMIC); -+ if (req == NULL) -+ return res; -+ async_urb = usb_alloc_urb(0, GFP_ATOMIC); -+ if (async_urb == NULL) { -+ kfree(req); -+ return res; -+ } -+ req->rx_creg = cpu_to_le16(reg); -+ req->dr.bRequestType = RTL8150_REQT_WRITE; -+ req->dr.bRequest = RTL8150_REQ_SET_REGS; -+ req->dr.wIndex = 0; -+ req->dr.wValue = cpu_to_le16(indx); -+ req->dr.wLength = cpu_to_le16(size); -+ usb_fill_control_urb(async_urb, dev->udev, -+ usb_sndctrlpipe(dev->udev, 0), (void *)&req->dr, -+ &req->rx_creg, size, async_set_reg_cb, req); -+ res = usb_submit_urb(async_urb, GFP_ATOMIC); -+ if (res) { -+ if (res == -ENODEV) -+ netif_device_detach(dev->netdev); -+ dev_err(&dev->udev->dev, "%s failed with %d\n", __func__, res); -+ } -+ return res; -+} -+ -+static int read_mii_word(rtl8150_t * dev, u8 phy, __u8 indx, u16 * reg) -+{ -+ int i; -+ u8 data[3], tmp; -+ -+ data[0] = phy; -+ data[1] = data[2] = 0; -+ tmp = indx | PHY_READ | PHY_GO; -+ i = 0; -+ -+ set_registers(dev, PHYADD, sizeof(data), data); -+ set_registers(dev, PHYCNT, 1, &tmp); -+ do { -+ get_registers(dev, PHYCNT, 1, data); -+ } while ((data[0] & PHY_GO) && (i++ < MII_TIMEOUT)); -+ -+ if (i <= MII_TIMEOUT) { -+ get_registers(dev, PHYDAT, 2, data); -+ *reg = data[0] | (data[1] << 8); -+ return 0; -+ } else -+ return 1; -+} -+ -+static int write_mii_word(rtl8150_t * dev, u8 phy, __u8 indx, u16 reg) -+{ -+ int i; -+ u8 data[3], tmp; -+ -+ data[0] = phy; -+ data[1] = reg & 0xff; -+ data[2] = (reg >> 8) & 0xff; -+ tmp = indx | PHY_WRITE | PHY_GO; -+ i = 0; -+ -+ set_registers(dev, PHYADD, sizeof(data), data); -+ set_registers(dev, PHYCNT, 1, &tmp); -+ do { -+ get_registers(dev, PHYCNT, 1, data); -+ } while ((data[0] & PHY_GO) && (i++ < MII_TIMEOUT)); -+ -+ if (i <= MII_TIMEOUT) -+ return 0; -+ else -+ return 1; -+} -+ -+static inline void set_ethernet_addr(rtl8150_t * dev) -+{ -+ u8 node_id[6]; -+ -+ get_registers(dev, IDR, sizeof(node_id), node_id); -+ memcpy(dev->netdev->dev_addr, node_id, sizeof(node_id)); -+} -+ -+static int rtl8150_set_mac_address(struct net_device *netdev, void *p) -+{ -+ struct sockaddr *addr = p; -+ rtl8150_t *dev = netdev_priv(netdev); -+ -+ if (netif_running(netdev)) -+ return -EBUSY; -+ -+ memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); -+ netdev_dbg(netdev, "Setting MAC address to %pM\n", netdev->dev_addr); -+ /* Set the IDR registers. */ -+ set_registers(dev, IDR, netdev->addr_len, netdev->dev_addr); -+#ifdef EEPROM_WRITE -+ { -+ int i; -+ u8 cr; -+ /* Get the CR contents. */ -+ get_registers(dev, CR, 1, &cr); -+ /* Set the WEPROM bit (eeprom write enable). */ -+ cr |= 0x20; -+ set_registers(dev, CR, 1, &cr); -+ /* Write the MAC address into eeprom. Eeprom writes must be word-sized, -+ so we need to split them up. */ -+ for (i = 0; i * 2 < netdev->addr_len; i++) { -+ set_registers(dev, IDR_EEPROM + (i * 2), 2, -+ netdev->dev_addr + (i * 2)); -+ } -+ /* Clear the WEPROM bit (preventing accidental eeprom writes). */ -+ cr &= 0xdf; -+ set_registers(dev, CR, 1, &cr); -+ } -+#endif -+ return 0; -+} -+ -+static int rtl8150_reset(rtl8150_t * dev) -+{ -+ u8 data = 0x10; -+ int i = HZ; -+ -+ set_registers(dev, CR, 1, &data); -+ do { -+ get_registers(dev, CR, 1, &data); -+ } while ((data & 0x10) && --i); -+ -+ return (i > 0) ? 1 : 0; -+} -+ -+static int alloc_all_urbs(rtl8150_t * dev) -+{ -+ dev->rx_urb = usb_alloc_urb(0, GFP_KERNEL); -+ if (!dev->rx_urb) -+ return 0; -+ dev->tx_urb = usb_alloc_urb(0, GFP_KERNEL); -+ if (!dev->tx_urb) { -+ usb_free_urb(dev->rx_urb); -+ return 0; -+ } -+ dev->intr_urb = usb_alloc_urb(0, GFP_KERNEL); -+ if (!dev->intr_urb) { -+ usb_free_urb(dev->rx_urb); -+ usb_free_urb(dev->tx_urb); -+ return 0; -+ } -+ -+ return 1; -+} -+ -+static void free_all_urbs(rtl8150_t * dev) -+{ -+ usb_free_urb(dev->rx_urb); -+ usb_free_urb(dev->tx_urb); -+ usb_free_urb(dev->intr_urb); -+} -+ -+static void unlink_all_urbs(rtl8150_t * dev) -+{ -+ usb_kill_urb(dev->rx_urb); -+ usb_kill_urb(dev->tx_urb); -+ usb_kill_urb(dev->intr_urb); -+} -+ -+static inline struct sk_buff *pull_skb(rtl8150_t *dev) -+{ -+ struct sk_buff *skb; -+ int i; -+ -+ for (i = 0; i < RX_SKB_POOL_SIZE; i++) { -+ if (dev->rx_skb_pool[i]) { -+ skb = dev->rx_skb_pool[i]; -+ dev->rx_skb_pool[i] = NULL; -+ return skb; -+ } -+ } -+ return NULL; -+} -+ -+static void read_bulk_callback(struct urb *urb) -+{ -+ rtl8150_t *dev; -+ unsigned pkt_len, res; -+ struct sk_buff *skb; -+ struct net_device *netdev; -+ u16 rx_stat; -+ int status = urb->status; -+ int result; -+ -+ dev = urb->context; -+ if (!dev) -+ return; -+ if (test_bit(RTL8150_UNPLUG, &dev->flags)) -+ return; -+ netdev = dev->netdev; -+ if (!netif_device_present(netdev)) -+ return; -+ -+ switch (status) { -+ case 0: -+ break; -+ case -ENOENT: -+ return; /* the urb is in unlink state */ -+ case -ETIME: -+ if (printk_ratelimit()) -+ dev_warn(&urb->dev->dev, "may be reset is needed?..\n"); -+ goto goon; -+ default: -+ if (printk_ratelimit()) -+ dev_warn(&urb->dev->dev, "Rx status %d\n", status); -+ goto goon; -+ } -+ -+ if (!dev->rx_skb) -+ goto resched; -+ /* protect against short packets (tell me why we got some?!?) */ -+ if (urb->actual_length < 4) -+ goto goon; -+ -+ res = urb->actual_length; -+ rx_stat = le16_to_cpu(*(__le16 *)(urb->transfer_buffer + res - 4)); -+ pkt_len = res - 4; -+ -+ skb_put(dev->rx_skb, pkt_len); -+ dev->rx_skb->protocol = eth_type_trans(dev->rx_skb, netdev); -+ netif_rx(dev->rx_skb); -+ netdev->stats.rx_packets++; -+ netdev->stats.rx_bytes += pkt_len; -+ -+ spin_lock(&dev->rx_pool_lock); -+ skb = pull_skb(dev); -+ spin_unlock(&dev->rx_pool_lock); -+ if (!skb) -+ goto resched; -+ -+ dev->rx_skb = skb; -+goon: -+ usb_fill_bulk_urb(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1), -+ dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev); -+ result = usb_submit_urb(dev->rx_urb, GFP_ATOMIC); -+ if (result == -ENODEV) -+ netif_device_detach(dev->netdev); -+ else if (result) { -+ set_bit(RX_URB_FAIL, &dev->flags); -+ goto resched; -+ } else { -+ clear_bit(RX_URB_FAIL, &dev->flags); -+ } -+ -+ return; -+resched: -+ tasklet_schedule(&dev->tl); -+} -+ -+static void write_bulk_callback(struct urb *urb) -+{ -+ rtl8150_t *dev; -+ int status = urb->status; -+ -+ dev = urb->context; -+ if (!dev) -+ return; -+ dev_kfree_skb_irq(dev->tx_skb); -+ if (!netif_device_present(dev->netdev)) -+ return; -+ if (status) -+ dev_info(&urb->dev->dev, "%s: Tx status %d\n", -+ dev->netdev->name, status); -+ dev->netdev->trans_start = jiffies; -+ netif_wake_queue(dev->netdev); -+} -+ -+static void intr_callback(struct urb *urb) -+{ -+ rtl8150_t *dev; -+ __u8 *d; -+ int status = urb->status; -+ int res; -+ -+ dev = urb->context; -+ if (!dev) -+ return; -+ switch (status) { -+ case 0: /* success */ -+ break; -+ case -ECONNRESET: /* unlink */ -+ case -ENOENT: -+ case -ESHUTDOWN: -+ return; -+ /* -EPIPE: should clear the halt */ -+ default: -+ dev_info(&urb->dev->dev, "%s: intr status %d\n", -+ dev->netdev->name, status); -+ goto resubmit; -+ } -+ -+ d = urb->transfer_buffer; -+ if (d[0] & TSR_ERRORS) { -+ dev->netdev->stats.tx_errors++; -+ if (d[INT_TSR] & (TSR_ECOL | TSR_JBR)) -+ dev->netdev->stats.tx_aborted_errors++; -+ if (d[INT_TSR] & TSR_LCOL) -+ dev->netdev->stats.tx_window_errors++; -+ if (d[INT_TSR] & TSR_LOSS_CRS) -+ dev->netdev->stats.tx_carrier_errors++; -+ } -+ /* Report link status changes to the network stack */ -+ if ((d[INT_MSR] & MSR_LINK) == 0) { -+ if (netif_carrier_ok(dev->netdev)) { -+ netif_carrier_off(dev->netdev); -+ netdev_dbg(dev->netdev, "%s: LINK LOST\n", __func__); -+ } -+ } else { -+ if (!netif_carrier_ok(dev->netdev)) { -+ netif_carrier_on(dev->netdev); -+ netdev_dbg(dev->netdev, "%s: LINK CAME BACK\n", __func__); -+ } -+ } -+ -+resubmit: -+ res = usb_submit_urb (urb, GFP_ATOMIC); -+ if (res == -ENODEV) -+ netif_device_detach(dev->netdev); -+ else if (res) -+ dev_err(&dev->udev->dev, -+ "can't resubmit intr, %s-%s/input0, status %d\n", -+ dev->udev->bus->bus_name, dev->udev->devpath, res); -+} -+ -+static int rtl8150_suspend(struct usb_interface *intf, pm_message_t message) -+{ -+ rtl8150_t *dev = usb_get_intfdata(intf); -+ -+ netif_device_detach(dev->netdev); -+ -+ if (netif_running(dev->netdev)) { -+ usb_kill_urb(dev->rx_urb); -+ usb_kill_urb(dev->intr_urb); -+ } -+ return 0; -+} -+ -+static int rtl8150_resume(struct usb_interface *intf) -+{ -+ rtl8150_t *dev = usb_get_intfdata(intf); -+ -+ netif_device_attach(dev->netdev); -+ if (netif_running(dev->netdev)) { -+ dev->rx_urb->status = 0; -+ dev->rx_urb->actual_length = 0; -+ read_bulk_callback(dev->rx_urb); -+ -+ dev->intr_urb->status = 0; -+ dev->intr_urb->actual_length = 0; -+ intr_callback(dev->intr_urb); -+ } -+ return 0; -+} -+ -+/* -+** -+** network related part of the code -+** -+*/ -+ -+static void fill_skb_pool(rtl8150_t *dev) -+{ -+ struct sk_buff *skb; -+ int i; -+ -+ for (i = 0; i < RX_SKB_POOL_SIZE; i++) { -+ if (dev->rx_skb_pool[i]) -+ continue; -+ skb = dev_alloc_skb(RTL8150_MTU + 2); -+ if (!skb) { -+ return; -+ } -+ skb_reserve(skb, 2); -+ dev->rx_skb_pool[i] = skb; -+ } -+} -+ -+static void free_skb_pool(rtl8150_t *dev) -+{ -+ int i; -+ -+ for (i = 0; i < RX_SKB_POOL_SIZE; i++) -+ if (dev->rx_skb_pool[i]) -+ dev_kfree_skb(dev->rx_skb_pool[i]); -+} -+ -+static void rx_fixup(unsigned long data) -+{ -+ struct rtl8150 *dev = (struct rtl8150 *)data; -+ struct sk_buff *skb; -+ int status; -+ -+ spin_lock_irq(&dev->rx_pool_lock); -+ fill_skb_pool(dev); -+ spin_unlock_irq(&dev->rx_pool_lock); -+ if (test_bit(RX_URB_FAIL, &dev->flags)) -+ if (dev->rx_skb) -+ goto try_again; -+ spin_lock_irq(&dev->rx_pool_lock); -+ skb = pull_skb(dev); -+ spin_unlock_irq(&dev->rx_pool_lock); -+ if (skb == NULL) -+ goto tlsched; -+ dev->rx_skb = skb; -+ usb_fill_bulk_urb(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1), -+ dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev); -+try_again: -+ status = usb_submit_urb(dev->rx_urb, GFP_ATOMIC); -+ if (status == -ENODEV) { -+ netif_device_detach(dev->netdev); -+ } else if (status) { -+ set_bit(RX_URB_FAIL, &dev->flags); -+ goto tlsched; -+ } else { -+ clear_bit(RX_URB_FAIL, &dev->flags); -+ } -+ -+ return; -+tlsched: -+ tasklet_schedule(&dev->tl); -+} -+ -+static int enable_net_traffic(rtl8150_t * dev) -+{ -+ u8 cr, tcr, rcr, msr; -+ -+ if (!rtl8150_reset(dev)) { -+ dev_warn(&dev->udev->dev, "device reset failed\n"); -+ } -+ /* RCR bit7=1 attach Rx info at the end; =0 HW CRC (which is broken) */ -+ rcr = 0x9e; -+ tcr = 0xd8; -+ cr = 0x0c; -+ if (!(rcr & 0x80)) -+ set_bit(RTL8150_HW_CRC, &dev->flags); -+ set_registers(dev, RCR, 1, &rcr); -+ set_registers(dev, TCR, 1, &tcr); -+ set_registers(dev, CR, 1, &cr); -+ get_registers(dev, MSR, 1, &msr); -+ -+ return 0; -+} -+ -+static void disable_net_traffic(rtl8150_t * dev) -+{ -+ u8 cr; -+ -+ get_registers(dev, CR, 1, &cr); -+ cr &= 0xf3; -+ set_registers(dev, CR, 1, &cr); -+} -+ -+static void rtl8150_tx_timeout(struct net_device *netdev) -+{ -+ rtl8150_t *dev = netdev_priv(netdev); -+ dev_warn(&netdev->dev, "Tx timeout.\n"); -+ usb_unlink_urb(dev->tx_urb); -+ netdev->stats.tx_errors++; -+} -+ -+static void rtl8150_set_multicast(struct net_device *netdev) -+{ -+ rtl8150_t *dev = netdev_priv(netdev); -+ u16 rx_creg = 0x9e; -+ -+ netif_stop_queue(netdev); -+ if (netdev->flags & IFF_PROMISC) { -+ rx_creg |= 0x0001; -+ dev_info(&netdev->dev, "%s: promiscuous mode\n", netdev->name); -+ } else if (!netdev_mc_empty(netdev) || -+ (netdev->flags & IFF_ALLMULTI)) { -+ rx_creg &= 0xfffe; -+ rx_creg |= 0x0002; -+ dev_info(&netdev->dev, "%s: allmulti set\n", netdev->name); -+ } else { -+ /* ~RX_MULTICAST, ~RX_PROMISCUOUS */ -+ rx_creg &= 0x00fc; -+ } -+ async_set_registers(dev, RCR, sizeof(rx_creg), rx_creg); -+ netif_wake_queue(netdev); -+} -+ -+static netdev_tx_t rtl8150_start_xmit(struct sk_buff *skb, -+ struct net_device *netdev) -+{ -+ rtl8150_t *dev = netdev_priv(netdev); -+ int count, res; -+ -+ netif_stop_queue(netdev); -+ count = (skb->len < 60) ? 60 : skb->len; -+ count = (count & 0x3f) ? count : count + 1; -+ dev->tx_skb = skb; -+ usb_fill_bulk_urb(dev->tx_urb, dev->udev, usb_sndbulkpipe(dev->udev, 2), -+ skb->data, count, write_bulk_callback, dev); -+ if ((res = usb_submit_urb(dev->tx_urb, GFP_ATOMIC))) { -+ /* Can we get/handle EPIPE here? */ -+ if (res == -ENODEV) -+ netif_device_detach(dev->netdev); -+ else { -+ dev_warn(&netdev->dev, "failed tx_urb %d\n", res); -+ netdev->stats.tx_errors++; -+ netif_start_queue(netdev); -+ } -+ } else { -+ netdev->stats.tx_packets++; -+ netdev->stats.tx_bytes += skb->len; -+ netdev->trans_start = jiffies; -+ } -+ -+ return NETDEV_TX_OK; -+} -+ -+ -+static void set_carrier(struct net_device *netdev) -+{ -+ rtl8150_t *dev = netdev_priv(netdev); -+ short tmp; -+ -+ get_registers(dev, CSCR, 2, &tmp); -+ if (tmp & CSCR_LINK_STATUS) -+ netif_carrier_on(netdev); -+ else -+ netif_carrier_off(netdev); -+} -+ -+static int rtl8150_open(struct net_device *netdev) -+{ -+ rtl8150_t *dev = netdev_priv(netdev); -+ int res; -+ -+ if (dev->rx_skb == NULL) -+ dev->rx_skb = pull_skb(dev); -+ if (!dev->rx_skb) -+ return -ENOMEM; -+ -+ set_registers(dev, IDR, 6, netdev->dev_addr); -+ -+ usb_fill_bulk_urb(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1), -+ dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev); -+ if ((res = usb_submit_urb(dev->rx_urb, GFP_KERNEL))) { -+ if (res == -ENODEV) -+ netif_device_detach(dev->netdev); -+ dev_warn(&netdev->dev, "rx_urb submit failed: %d\n", res); -+ return res; -+ } -+ usb_fill_int_urb(dev->intr_urb, dev->udev, usb_rcvintpipe(dev->udev, 3), -+ dev->intr_buff, INTBUFSIZE, intr_callback, -+ dev, dev->intr_interval); -+ if ((res = usb_submit_urb(dev->intr_urb, GFP_KERNEL))) { -+ if (res == -ENODEV) -+ netif_device_detach(dev->netdev); -+ dev_warn(&netdev->dev, "intr_urb submit failed: %d\n", res); -+ usb_kill_urb(dev->rx_urb); -+ return res; -+ } -+ enable_net_traffic(dev); -+ set_carrier(netdev); -+ netif_start_queue(netdev); -+ -+ return res; -+} -+ -+static int rtl8150_close(struct net_device *netdev) -+{ -+ rtl8150_t *dev = netdev_priv(netdev); -+ -+ netif_stop_queue(netdev); -+ if (!test_bit(RTL8150_UNPLUG, &dev->flags)) -+ disable_net_traffic(dev); -+ unlink_all_urbs(dev); -+ -+ return 0; -+} -+ -+static void rtl8150_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info) -+{ -+ rtl8150_t *dev = netdev_priv(netdev); -+ -+ strlcpy(info->driver, driver_name, sizeof(info->driver)); -+ strlcpy(info->version, DRIVER_VERSION, sizeof(info->version)); -+ usb_make_path(dev->udev, info->bus_info, sizeof(info->bus_info)); -+} -+ -+static int rtl8150_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) -+{ -+ rtl8150_t *dev = netdev_priv(netdev); -+ short lpa, bmcr; -+ -+ ecmd->supported = (SUPPORTED_10baseT_Half | -+ SUPPORTED_10baseT_Full | -+ SUPPORTED_100baseT_Half | -+ SUPPORTED_100baseT_Full | -+ SUPPORTED_Autoneg | -+ SUPPORTED_TP | SUPPORTED_MII); -+ ecmd->port = PORT_TP; -+ ecmd->transceiver = XCVR_INTERNAL; -+ ecmd->phy_address = dev->phy; -+ get_registers(dev, BMCR, 2, &bmcr); -+ get_registers(dev, ANLP, 2, &lpa); -+ if (bmcr & BMCR_ANENABLE) { -+ u32 speed = ((lpa & (LPA_100HALF | LPA_100FULL)) ? -+ SPEED_100 : SPEED_10); -+ ethtool_cmd_speed_set(ecmd, speed); -+ ecmd->autoneg = AUTONEG_ENABLE; -+ if (speed == SPEED_100) -+ ecmd->duplex = (lpa & LPA_100FULL) ? -+ DUPLEX_FULL : DUPLEX_HALF; -+ else -+ ecmd->duplex = (lpa & LPA_10FULL) ? -+ DUPLEX_FULL : DUPLEX_HALF; -+ } else { -+ ecmd->autoneg = AUTONEG_DISABLE; -+ ethtool_cmd_speed_set(ecmd, ((bmcr & BMCR_SPEED100) ? -+ SPEED_100 : SPEED_10)); -+ ecmd->duplex = (bmcr & BMCR_FULLDPLX) ? -+ DUPLEX_FULL : DUPLEX_HALF; -+ } -+ return 0; -+} -+ -+static const struct ethtool_ops ops = { -+ .get_drvinfo = rtl8150_get_drvinfo, -+ .get_settings = rtl8150_get_settings, -+ .get_link = ethtool_op_get_link -+}; -+ -+static int rtl8150_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) -+{ -+ rtl8150_t *dev = netdev_priv(netdev); -+ u16 *data = (u16 *) & rq->ifr_ifru; -+ int res = 0; -+ -+ switch (cmd) { -+ case SIOCDEVPRIVATE: -+ data[0] = dev->phy; -+ case SIOCDEVPRIVATE + 1: -+ read_mii_word(dev, dev->phy, (data[1] & 0x1f), &data[3]); -+ break; -+ case SIOCDEVPRIVATE + 2: -+ if (!capable(CAP_NET_ADMIN)) -+ return -EPERM; -+ write_mii_word(dev, dev->phy, (data[1] & 0x1f), data[2]); -+ break; -+ default: -+ res = -EOPNOTSUPP; -+ } -+ -+ return res; -+} -+ -+static const struct net_device_ops rtl8150_netdev_ops = { -+ .ndo_open = rtl8150_open, -+ .ndo_stop = rtl8150_close, -+ .ndo_do_ioctl = rtl8150_ioctl, -+ .ndo_start_xmit = rtl8150_start_xmit, -+ .ndo_tx_timeout = rtl8150_tx_timeout, -+ .ndo_set_rx_mode = rtl8150_set_multicast, -+ .ndo_set_mac_address = rtl8150_set_mac_address, -+ -+ .ndo_change_mtu = eth_change_mtu, -+ .ndo_validate_addr = eth_validate_addr, -+}; -+ -+static int rtl8150_probe(struct usb_interface *intf, -+ const struct usb_device_id *id) -+{ -+ struct usb_device *udev = interface_to_usbdev(intf); -+ rtl8150_t *dev; -+ struct net_device *netdev; -+ -+ netdev = alloc_etherdev(sizeof(rtl8150_t)); -+ if (!netdev) -+ return -ENOMEM; -+ -+ dev = netdev_priv(netdev); -+ -+ dev->intr_buff = kmalloc(INTBUFSIZE, GFP_KERNEL); -+ if (!dev->intr_buff) { -+ free_netdev(netdev); -+ return -ENOMEM; -+ } -+ -+ tasklet_init(&dev->tl, rx_fixup, (unsigned long)dev); -+ spin_lock_init(&dev->rx_pool_lock); -+ -+ dev->udev = udev; -+ dev->netdev = netdev; -+ netdev->netdev_ops = &rtl8150_netdev_ops; -+ netdev->watchdog_timeo = RTL8150_TX_TIMEOUT; -+ netdev->ethtool_ops = &ops; -+ dev->intr_interval = 100; /* 100ms */ -+ -+ if (!alloc_all_urbs(dev)) { -+ dev_err(&intf->dev, "out of memory\n"); -+ goto out; -+ } -+ if (!rtl8150_reset(dev)) { -+ dev_err(&intf->dev, "couldn't reset the device\n"); -+ goto out1; -+ } -+ fill_skb_pool(dev); -+ set_ethernet_addr(dev); -+ -+ usb_set_intfdata(intf, dev); -+ SET_NETDEV_DEV(netdev, &intf->dev); -+ if (register_netdev(netdev) != 0) { -+ dev_err(&intf->dev, "couldn't register the device\n"); -+ goto out2; -+ } -+ -+ dev_info(&intf->dev, "%s: rtl8150 is detected\n", netdev->name); -+ -+ return 0; -+ -+out2: -+ usb_set_intfdata(intf, NULL); -+ free_skb_pool(dev); -+out1: -+ free_all_urbs(dev); -+out: -+ kfree(dev->intr_buff); -+ free_netdev(netdev); -+ return -EIO; -+} -+ -+static void rtl8150_disconnect(struct usb_interface *intf) -+{ -+ rtl8150_t *dev = usb_get_intfdata(intf); -+ -+ usb_set_intfdata(intf, NULL); -+ if (dev) { -+ set_bit(RTL8150_UNPLUG, &dev->flags); -+ tasklet_kill(&dev->tl); -+ unregister_netdev(dev->netdev); -+ unlink_all_urbs(dev); -+ free_all_urbs(dev); -+ free_skb_pool(dev); -+ if (dev->rx_skb) -+ dev_kfree_skb(dev->rx_skb); -+ kfree(dev->intr_buff); -+ free_netdev(dev->netdev); -+ } -+} -+ -+static struct usb_driver rtl8150_driver = { -+ .name = driver_name, -+ .probe = rtl8150_probe, -+ .disconnect = rtl8150_disconnect, -+ .id_table = rtl8150_table, -+ .suspend = rtl8150_suspend, -+ .resume = rtl8150_resume, -+ .disable_hub_initiated_lpm = 1, -+}; -+ -+module_usb_driver(rtl8150_driver); -+ -+MODULE_AUTHOR(DRIVER_AUTHOR); -+MODULE_DESCRIPTION(DRIVER_DESC); -+MODULE_LICENSE("GPL"); -diff -Naur backports-4.2.6-1.org/drivers/net/usb/smsc75xx.c backports-4.2.6-1/drivers/net/usb/smsc75xx.c ---- backports-4.2.6-1.org/drivers/net/usb/smsc75xx.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/smsc75xx.c 2016-06-28 14:35:18.008640551 +0200 -@@ -0,0 +1,2286 @@ -+ /*************************************************************************** -+ * -+ * Copyright (C) 2007-2010 SMSC -+ * -+ * 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, see . -+ * -+ *****************************************************************************/ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "smsc75xx.h" -+ -+#define SMSC_CHIPNAME "smsc75xx" -+#define SMSC_DRIVER_VERSION "1.0.0" -+#define HS_USB_PKT_SIZE (512) -+#define FS_USB_PKT_SIZE (64) -+#define DEFAULT_HS_BURST_CAP_SIZE (16 * 1024 + 5 * HS_USB_PKT_SIZE) -+#define DEFAULT_FS_BURST_CAP_SIZE (6 * 1024 + 33 * FS_USB_PKT_SIZE) -+#define DEFAULT_BULK_IN_DELAY (0x00002000) -+#define MAX_SINGLE_PACKET_SIZE (9000) -+#define LAN75XX_EEPROM_MAGIC (0x7500) -+#define EEPROM_MAC_OFFSET (0x01) -+#define DEFAULT_TX_CSUM_ENABLE (true) -+#define DEFAULT_RX_CSUM_ENABLE (true) -+#define SMSC75XX_INTERNAL_PHY_ID (1) -+#define SMSC75XX_TX_OVERHEAD (8) -+#define MAX_RX_FIFO_SIZE (20 * 1024) -+#define MAX_TX_FIFO_SIZE (12 * 1024) -+#define USB_VENDOR_ID_SMSC (0x0424) -+#define USB_PRODUCT_ID_LAN7500 (0x7500) -+#define USB_PRODUCT_ID_LAN7505 (0x7505) -+#define RXW_PADDING 2 -+#define SUPPORTED_WAKE (WAKE_PHY | WAKE_UCAST | WAKE_BCAST | \ -+ WAKE_MCAST | WAKE_ARP | WAKE_MAGIC) -+ -+#define SUSPEND_SUSPEND0 (0x01) -+#define SUSPEND_SUSPEND1 (0x02) -+#define SUSPEND_SUSPEND2 (0x04) -+#define SUSPEND_SUSPEND3 (0x08) -+#define SUSPEND_ALLMODES (SUSPEND_SUSPEND0 | SUSPEND_SUSPEND1 | \ -+ SUSPEND_SUSPEND2 | SUSPEND_SUSPEND3) -+ -+struct smsc75xx_priv { -+ struct usbnet *dev; -+ u32 rfe_ctl; -+ u32 wolopts; -+ u32 multicast_hash_table[DP_SEL_VHF_HASH_LEN]; -+ struct mutex dataport_mutex; -+ spinlock_t rfe_ctl_lock; -+ struct work_struct set_multicast; -+ u8 suspend_flags; -+}; -+ -+struct usb_context { -+ struct usb_ctrlrequest req; -+ struct usbnet *dev; -+}; -+ -+static bool turbo_mode = true; -+module_param(turbo_mode, bool, 0644); -+MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction"); -+ -+static int __must_check __smsc75xx_read_reg(struct usbnet *dev, u32 index, -+ u32 *data, int in_pm) -+{ -+ u32 buf; -+ int ret; -+ int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16); -+ -+ BUG_ON(!dev); -+ -+ if (!in_pm) -+ fn = usbnet_read_cmd; -+ else -+ fn = usbnet_read_cmd_nopm; -+ -+ ret = fn(dev, USB_VENDOR_REQUEST_READ_REGISTER, USB_DIR_IN -+ | USB_TYPE_VENDOR | USB_RECIP_DEVICE, -+ 0, index, &buf, 4); -+ if (unlikely(ret < 0)) -+ netdev_warn(dev->net, "Failed to read reg index 0x%08x: %d\n", -+ index, ret); -+ -+ le32_to_cpus(&buf); -+ *data = buf; -+ -+ return ret; -+} -+ -+static int __must_check __smsc75xx_write_reg(struct usbnet *dev, u32 index, -+ u32 data, int in_pm) -+{ -+ u32 buf; -+ int ret; -+ int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16); -+ -+ BUG_ON(!dev); -+ -+ if (!in_pm) -+ fn = usbnet_write_cmd; -+ else -+ fn = usbnet_write_cmd_nopm; -+ -+ buf = data; -+ cpu_to_le32s(&buf); -+ -+ ret = fn(dev, USB_VENDOR_REQUEST_WRITE_REGISTER, USB_DIR_OUT -+ | USB_TYPE_VENDOR | USB_RECIP_DEVICE, -+ 0, index, &buf, 4); -+ if (unlikely(ret < 0)) -+ netdev_warn(dev->net, "Failed to write reg index 0x%08x: %d\n", -+ index, ret); -+ -+ return ret; -+} -+ -+static int __must_check smsc75xx_read_reg_nopm(struct usbnet *dev, u32 index, -+ u32 *data) -+{ -+ return __smsc75xx_read_reg(dev, index, data, 1); -+} -+ -+static int __must_check smsc75xx_write_reg_nopm(struct usbnet *dev, u32 index, -+ u32 data) -+{ -+ return __smsc75xx_write_reg(dev, index, data, 1); -+} -+ -+static int __must_check smsc75xx_read_reg(struct usbnet *dev, u32 index, -+ u32 *data) -+{ -+ return __smsc75xx_read_reg(dev, index, data, 0); -+} -+ -+static int __must_check smsc75xx_write_reg(struct usbnet *dev, u32 index, -+ u32 data) -+{ -+ return __smsc75xx_write_reg(dev, index, data, 0); -+} -+ -+/* Loop until the read is completed with timeout -+ * called with phy_mutex held */ -+static __must_check int __smsc75xx_phy_wait_not_busy(struct usbnet *dev, -+ int in_pm) -+{ -+ unsigned long start_time = jiffies; -+ u32 val; -+ int ret; -+ -+ do { -+ ret = __smsc75xx_read_reg(dev, MII_ACCESS, &val, in_pm); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading MII_ACCESS\n"); -+ return ret; -+ } -+ -+ if (!(val & MII_ACCESS_BUSY)) -+ return 0; -+ } while (!time_after(jiffies, start_time + HZ)); -+ -+ return -EIO; -+} -+ -+static int __smsc75xx_mdio_read(struct net_device *netdev, int phy_id, int idx, -+ int in_pm) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ u32 val, addr; -+ int ret; -+ -+ mutex_lock(&dev->phy_mutex); -+ -+ /* confirm MII not busy */ -+ ret = __smsc75xx_phy_wait_not_busy(dev, in_pm); -+ if (ret < 0) { -+ netdev_warn(dev->net, "MII is busy in smsc75xx_mdio_read\n"); -+ goto done; -+ } -+ -+ /* set the address, index & direction (read from PHY) */ -+ phy_id &= dev->mii.phy_id_mask; -+ idx &= dev->mii.reg_num_mask; -+ addr = ((phy_id << MII_ACCESS_PHY_ADDR_SHIFT) & MII_ACCESS_PHY_ADDR) -+ | ((idx << MII_ACCESS_REG_ADDR_SHIFT) & MII_ACCESS_REG_ADDR) -+ | MII_ACCESS_READ | MII_ACCESS_BUSY; -+ ret = __smsc75xx_write_reg(dev, MII_ACCESS, addr, in_pm); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing MII_ACCESS\n"); -+ goto done; -+ } -+ -+ ret = __smsc75xx_phy_wait_not_busy(dev, in_pm); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Timed out reading MII reg %02X\n", idx); -+ goto done; -+ } -+ -+ ret = __smsc75xx_read_reg(dev, MII_DATA, &val, in_pm); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading MII_DATA\n"); -+ goto done; -+ } -+ -+ ret = (u16)(val & 0xFFFF); -+ -+done: -+ mutex_unlock(&dev->phy_mutex); -+ return ret; -+} -+ -+static void __smsc75xx_mdio_write(struct net_device *netdev, int phy_id, -+ int idx, int regval, int in_pm) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ u32 val, addr; -+ int ret; -+ -+ mutex_lock(&dev->phy_mutex); -+ -+ /* confirm MII not busy */ -+ ret = __smsc75xx_phy_wait_not_busy(dev, in_pm); -+ if (ret < 0) { -+ netdev_warn(dev->net, "MII is busy in smsc75xx_mdio_write\n"); -+ goto done; -+ } -+ -+ val = regval; -+ ret = __smsc75xx_write_reg(dev, MII_DATA, val, in_pm); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing MII_DATA\n"); -+ goto done; -+ } -+ -+ /* set the address, index & direction (write to PHY) */ -+ phy_id &= dev->mii.phy_id_mask; -+ idx &= dev->mii.reg_num_mask; -+ addr = ((phy_id << MII_ACCESS_PHY_ADDR_SHIFT) & MII_ACCESS_PHY_ADDR) -+ | ((idx << MII_ACCESS_REG_ADDR_SHIFT) & MII_ACCESS_REG_ADDR) -+ | MII_ACCESS_WRITE | MII_ACCESS_BUSY; -+ ret = __smsc75xx_write_reg(dev, MII_ACCESS, addr, in_pm); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing MII_ACCESS\n"); -+ goto done; -+ } -+ -+ ret = __smsc75xx_phy_wait_not_busy(dev, in_pm); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Timed out writing MII reg %02X\n", idx); -+ goto done; -+ } -+ -+done: -+ mutex_unlock(&dev->phy_mutex); -+} -+ -+static int smsc75xx_mdio_read_nopm(struct net_device *netdev, int phy_id, -+ int idx) -+{ -+ return __smsc75xx_mdio_read(netdev, phy_id, idx, 1); -+} -+ -+static void smsc75xx_mdio_write_nopm(struct net_device *netdev, int phy_id, -+ int idx, int regval) -+{ -+ __smsc75xx_mdio_write(netdev, phy_id, idx, regval, 1); -+} -+ -+static int smsc75xx_mdio_read(struct net_device *netdev, int phy_id, int idx) -+{ -+ return __smsc75xx_mdio_read(netdev, phy_id, idx, 0); -+} -+ -+static void smsc75xx_mdio_write(struct net_device *netdev, int phy_id, int idx, -+ int regval) -+{ -+ __smsc75xx_mdio_write(netdev, phy_id, idx, regval, 0); -+} -+ -+static int smsc75xx_wait_eeprom(struct usbnet *dev) -+{ -+ unsigned long start_time = jiffies; -+ u32 val; -+ int ret; -+ -+ do { -+ ret = smsc75xx_read_reg(dev, E2P_CMD, &val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading E2P_CMD\n"); -+ return ret; -+ } -+ -+ if (!(val & E2P_CMD_BUSY) || (val & E2P_CMD_TIMEOUT)) -+ break; -+ udelay(40); -+ } while (!time_after(jiffies, start_time + HZ)); -+ -+ if (val & (E2P_CMD_TIMEOUT | E2P_CMD_BUSY)) { -+ netdev_warn(dev->net, "EEPROM read operation timeout\n"); -+ return -EIO; -+ } -+ -+ return 0; -+} -+ -+static int smsc75xx_eeprom_confirm_not_busy(struct usbnet *dev) -+{ -+ unsigned long start_time = jiffies; -+ u32 val; -+ int ret; -+ -+ do { -+ ret = smsc75xx_read_reg(dev, E2P_CMD, &val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading E2P_CMD\n"); -+ return ret; -+ } -+ -+ if (!(val & E2P_CMD_BUSY)) -+ return 0; -+ -+ udelay(40); -+ } while (!time_after(jiffies, start_time + HZ)); -+ -+ netdev_warn(dev->net, "EEPROM is busy\n"); -+ return -EIO; -+} -+ -+static int smsc75xx_read_eeprom(struct usbnet *dev, u32 offset, u32 length, -+ u8 *data) -+{ -+ u32 val; -+ int i, ret; -+ -+ BUG_ON(!dev); -+ BUG_ON(!data); -+ -+ ret = smsc75xx_eeprom_confirm_not_busy(dev); -+ if (ret) -+ return ret; -+ -+ for (i = 0; i < length; i++) { -+ val = E2P_CMD_BUSY | E2P_CMD_READ | (offset & E2P_CMD_ADDR); -+ ret = smsc75xx_write_reg(dev, E2P_CMD, val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing E2P_CMD\n"); -+ return ret; -+ } -+ -+ ret = smsc75xx_wait_eeprom(dev); -+ if (ret < 0) -+ return ret; -+ -+ ret = smsc75xx_read_reg(dev, E2P_DATA, &val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading E2P_DATA\n"); -+ return ret; -+ } -+ -+ data[i] = val & 0xFF; -+ offset++; -+ } -+ -+ return 0; -+} -+ -+static int smsc75xx_write_eeprom(struct usbnet *dev, u32 offset, u32 length, -+ u8 *data) -+{ -+ u32 val; -+ int i, ret; -+ -+ BUG_ON(!dev); -+ BUG_ON(!data); -+ -+ ret = smsc75xx_eeprom_confirm_not_busy(dev); -+ if (ret) -+ return ret; -+ -+ /* Issue write/erase enable command */ -+ val = E2P_CMD_BUSY | E2P_CMD_EWEN; -+ ret = smsc75xx_write_reg(dev, E2P_CMD, val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing E2P_CMD\n"); -+ return ret; -+ } -+ -+ ret = smsc75xx_wait_eeprom(dev); -+ if (ret < 0) -+ return ret; -+ -+ for (i = 0; i < length; i++) { -+ -+ /* Fill data register */ -+ val = data[i]; -+ ret = smsc75xx_write_reg(dev, E2P_DATA, val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing E2P_DATA\n"); -+ return ret; -+ } -+ -+ /* Send "write" command */ -+ val = E2P_CMD_BUSY | E2P_CMD_WRITE | (offset & E2P_CMD_ADDR); -+ ret = smsc75xx_write_reg(dev, E2P_CMD, val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing E2P_CMD\n"); -+ return ret; -+ } -+ -+ ret = smsc75xx_wait_eeprom(dev); -+ if (ret < 0) -+ return ret; -+ -+ offset++; -+ } -+ -+ return 0; -+} -+ -+static int smsc75xx_dataport_wait_not_busy(struct usbnet *dev) -+{ -+ int i, ret; -+ -+ for (i = 0; i < 100; i++) { -+ u32 dp_sel; -+ ret = smsc75xx_read_reg(dev, DP_SEL, &dp_sel); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading DP_SEL\n"); -+ return ret; -+ } -+ -+ if (dp_sel & DP_SEL_DPRDY) -+ return 0; -+ -+ udelay(40); -+ } -+ -+ netdev_warn(dev->net, "smsc75xx_dataport_wait_not_busy timed out\n"); -+ -+ return -EIO; -+} -+ -+static int smsc75xx_dataport_write(struct usbnet *dev, u32 ram_select, u32 addr, -+ u32 length, u32 *buf) -+{ -+ struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); -+ u32 dp_sel; -+ int i, ret; -+ -+ mutex_lock(&pdata->dataport_mutex); -+ -+ ret = smsc75xx_dataport_wait_not_busy(dev); -+ if (ret < 0) { -+ netdev_warn(dev->net, "smsc75xx_dataport_write busy on entry\n"); -+ goto done; -+ } -+ -+ ret = smsc75xx_read_reg(dev, DP_SEL, &dp_sel); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading DP_SEL\n"); -+ goto done; -+ } -+ -+ dp_sel &= ~DP_SEL_RSEL; -+ dp_sel |= ram_select; -+ ret = smsc75xx_write_reg(dev, DP_SEL, dp_sel); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing DP_SEL\n"); -+ goto done; -+ } -+ -+ for (i = 0; i < length; i++) { -+ ret = smsc75xx_write_reg(dev, DP_ADDR, addr + i); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing DP_ADDR\n"); -+ goto done; -+ } -+ -+ ret = smsc75xx_write_reg(dev, DP_DATA, buf[i]); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing DP_DATA\n"); -+ goto done; -+ } -+ -+ ret = smsc75xx_write_reg(dev, DP_CMD, DP_CMD_WRITE); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing DP_CMD\n"); -+ goto done; -+ } -+ -+ ret = smsc75xx_dataport_wait_not_busy(dev); -+ if (ret < 0) { -+ netdev_warn(dev->net, "smsc75xx_dataport_write timeout\n"); -+ goto done; -+ } -+ } -+ -+done: -+ mutex_unlock(&pdata->dataport_mutex); -+ return ret; -+} -+ -+/* returns hash bit number for given MAC address */ -+static u32 smsc75xx_hash(char addr[ETH_ALEN]) -+{ -+ return (ether_crc(ETH_ALEN, addr) >> 23) & 0x1ff; -+} -+ -+static void smsc75xx_deferred_multicast_write(struct work_struct *param) -+{ -+ struct smsc75xx_priv *pdata = -+ container_of(param, struct smsc75xx_priv, set_multicast); -+ struct usbnet *dev = pdata->dev; -+ int ret; -+ -+ netif_dbg(dev, drv, dev->net, "deferred multicast write 0x%08x\n", -+ pdata->rfe_ctl); -+ -+ smsc75xx_dataport_write(dev, DP_SEL_VHF, DP_SEL_VHF_VLAN_LEN, -+ DP_SEL_VHF_HASH_LEN, pdata->multicast_hash_table); -+ -+ ret = smsc75xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl); -+ if (ret < 0) -+ netdev_warn(dev->net, "Error writing RFE_CRL\n"); -+} -+ -+static void smsc75xx_set_multicast(struct net_device *netdev) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); -+ unsigned long flags; -+ int i; -+ -+ spin_lock_irqsave(&pdata->rfe_ctl_lock, flags); -+ -+ pdata->rfe_ctl &= -+ ~(RFE_CTL_AU | RFE_CTL_AM | RFE_CTL_DPF | RFE_CTL_MHF); -+ pdata->rfe_ctl |= RFE_CTL_AB; -+ -+ for (i = 0; i < DP_SEL_VHF_HASH_LEN; i++) -+ pdata->multicast_hash_table[i] = 0; -+ -+ if (dev->net->flags & IFF_PROMISC) { -+ netif_dbg(dev, drv, dev->net, "promiscuous mode enabled\n"); -+ pdata->rfe_ctl |= RFE_CTL_AM | RFE_CTL_AU; -+ } else if (dev->net->flags & IFF_ALLMULTI) { -+ netif_dbg(dev, drv, dev->net, "receive all multicast enabled\n"); -+ pdata->rfe_ctl |= RFE_CTL_AM | RFE_CTL_DPF; -+ } else if (!netdev_mc_empty(dev->net)) { -+ struct netdev_hw_addr *ha; -+ -+ netif_dbg(dev, drv, dev->net, "receive multicast hash filter\n"); -+ -+ pdata->rfe_ctl |= RFE_CTL_MHF | RFE_CTL_DPF; -+ -+ netdev_for_each_mc_addr(ha, netdev) { -+ u32 bitnum = smsc75xx_hash(ha->addr); -+ pdata->multicast_hash_table[bitnum / 32] |= -+ (1 << (bitnum % 32)); -+ } -+ } else { -+ netif_dbg(dev, drv, dev->net, "receive own packets only\n"); -+ pdata->rfe_ctl |= RFE_CTL_DPF; -+ } -+ -+ spin_unlock_irqrestore(&pdata->rfe_ctl_lock, flags); -+ -+ /* defer register writes to a sleepable context */ -+ schedule_work(&pdata->set_multicast); -+} -+ -+static int smsc75xx_update_flowcontrol(struct usbnet *dev, u8 duplex, -+ u16 lcladv, u16 rmtadv) -+{ -+ u32 flow = 0, fct_flow = 0; -+ int ret; -+ -+ if (duplex == DUPLEX_FULL) { -+ u8 cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv); -+ -+ if (cap & FLOW_CTRL_TX) { -+ flow = (FLOW_TX_FCEN | 0xFFFF); -+ /* set fct_flow thresholds to 20% and 80% */ -+ fct_flow = (8 << 8) | 32; -+ } -+ -+ if (cap & FLOW_CTRL_RX) -+ flow |= FLOW_RX_FCEN; -+ -+ netif_dbg(dev, link, dev->net, "rx pause %s, tx pause %s\n", -+ (cap & FLOW_CTRL_RX ? "enabled" : "disabled"), -+ (cap & FLOW_CTRL_TX ? "enabled" : "disabled")); -+ } else { -+ netif_dbg(dev, link, dev->net, "half duplex\n"); -+ } -+ -+ ret = smsc75xx_write_reg(dev, FLOW, flow); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing FLOW\n"); -+ return ret; -+ } -+ -+ ret = smsc75xx_write_reg(dev, FCT_FLOW, fct_flow); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing FCT_FLOW\n"); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int smsc75xx_link_reset(struct usbnet *dev) -+{ -+ struct mii_if_info *mii = &dev->mii; -+ struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET }; -+ u16 lcladv, rmtadv; -+ int ret; -+ -+ /* write to clear phy interrupt status */ -+ smsc75xx_mdio_write(dev->net, mii->phy_id, PHY_INT_SRC, -+ PHY_INT_SRC_CLEAR_ALL); -+ -+ ret = smsc75xx_write_reg(dev, INT_STS, INT_STS_CLEAR_ALL); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing INT_STS\n"); -+ return ret; -+ } -+ -+ mii_check_media(mii, 1, 1); -+ mii_ethtool_gset(&dev->mii, &ecmd); -+ lcladv = smsc75xx_mdio_read(dev->net, mii->phy_id, MII_ADVERTISE); -+ rmtadv = smsc75xx_mdio_read(dev->net, mii->phy_id, MII_LPA); -+ -+ netif_dbg(dev, link, dev->net, "speed: %u duplex: %d lcladv: %04x rmtadv: %04x\n", -+ ethtool_cmd_speed(&ecmd), ecmd.duplex, lcladv, rmtadv); -+ -+ return smsc75xx_update_flowcontrol(dev, ecmd.duplex, lcladv, rmtadv); -+} -+ -+static void smsc75xx_status(struct usbnet *dev, struct urb *urb) -+{ -+ u32 intdata; -+ -+ if (urb->actual_length != 4) { -+ netdev_warn(dev->net, "unexpected urb length %d\n", -+ urb->actual_length); -+ return; -+ } -+ -+ memcpy(&intdata, urb->transfer_buffer, 4); -+ le32_to_cpus(&intdata); -+ -+ netif_dbg(dev, link, dev->net, "intdata: 0x%08X\n", intdata); -+ -+ if (intdata & INT_ENP_PHY_INT) -+ usbnet_defer_kevent(dev, EVENT_LINK_RESET); -+ else -+ netdev_warn(dev->net, "unexpected interrupt, intdata=0x%08X\n", -+ intdata); -+} -+ -+static int smsc75xx_ethtool_get_eeprom_len(struct net_device *net) -+{ -+ return MAX_EEPROM_SIZE; -+} -+ -+static int smsc75xx_ethtool_get_eeprom(struct net_device *netdev, -+ struct ethtool_eeprom *ee, u8 *data) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ -+ ee->magic = LAN75XX_EEPROM_MAGIC; -+ -+ return smsc75xx_read_eeprom(dev, ee->offset, ee->len, data); -+} -+ -+static int smsc75xx_ethtool_set_eeprom(struct net_device *netdev, -+ struct ethtool_eeprom *ee, u8 *data) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ -+ if (ee->magic != LAN75XX_EEPROM_MAGIC) { -+ netdev_warn(dev->net, "EEPROM: magic value mismatch: 0x%x\n", -+ ee->magic); -+ return -EINVAL; -+ } -+ -+ return smsc75xx_write_eeprom(dev, ee->offset, ee->len, data); -+} -+ -+static void smsc75xx_ethtool_get_wol(struct net_device *net, -+ struct ethtool_wolinfo *wolinfo) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); -+ -+ wolinfo->supported = SUPPORTED_WAKE; -+ wolinfo->wolopts = pdata->wolopts; -+} -+ -+static int smsc75xx_ethtool_set_wol(struct net_device *net, -+ struct ethtool_wolinfo *wolinfo) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); -+ int ret; -+ -+ pdata->wolopts = wolinfo->wolopts & SUPPORTED_WAKE; -+ -+ ret = device_set_wakeup_enable(&dev->udev->dev, pdata->wolopts); -+ if (ret < 0) -+ netdev_warn(dev->net, "device_set_wakeup_enable error %d\n", ret); -+ -+ return ret; -+} -+ -+static const struct ethtool_ops smsc75xx_ethtool_ops = { -+ .get_link = usbnet_get_link, -+ .nway_reset = usbnet_nway_reset, -+ .get_drvinfo = usbnet_get_drvinfo, -+ .get_msglevel = usbnet_get_msglevel, -+ .set_msglevel = usbnet_set_msglevel, -+ .get_settings = usbnet_get_settings, -+ .set_settings = usbnet_set_settings, -+ .get_eeprom_len = smsc75xx_ethtool_get_eeprom_len, -+ .get_eeprom = smsc75xx_ethtool_get_eeprom, -+ .set_eeprom = smsc75xx_ethtool_set_eeprom, -+ .get_wol = smsc75xx_ethtool_get_wol, -+ .set_wol = smsc75xx_ethtool_set_wol, -+}; -+ -+static int smsc75xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ -+ if (!netif_running(netdev)) -+ return -EINVAL; -+ -+ return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); -+} -+ -+static void smsc75xx_init_mac_address(struct usbnet *dev) -+{ -+ /* try reading mac address from EEPROM */ -+ if (smsc75xx_read_eeprom(dev, EEPROM_MAC_OFFSET, ETH_ALEN, -+ dev->net->dev_addr) == 0) { -+ if (is_valid_ether_addr(dev->net->dev_addr)) { -+ /* eeprom values are valid so use them */ -+ netif_dbg(dev, ifup, dev->net, -+ "MAC address read from EEPROM\n"); -+ return; -+ } -+ } -+ -+ /* no eeprom, or eeprom values are invalid. generate random MAC */ -+ eth_hw_addr_random(dev->net); -+ netif_dbg(dev, ifup, dev->net, "MAC address set to eth_random_addr\n"); -+} -+ -+static int smsc75xx_set_mac_address(struct usbnet *dev) -+{ -+ u32 addr_lo = dev->net->dev_addr[0] | dev->net->dev_addr[1] << 8 | -+ dev->net->dev_addr[2] << 16 | dev->net->dev_addr[3] << 24; -+ u32 addr_hi = dev->net->dev_addr[4] | dev->net->dev_addr[5] << 8; -+ -+ int ret = smsc75xx_write_reg(dev, RX_ADDRH, addr_hi); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to write RX_ADDRH: %d\n", ret); -+ return ret; -+ } -+ -+ ret = smsc75xx_write_reg(dev, RX_ADDRL, addr_lo); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to write RX_ADDRL: %d\n", ret); -+ return ret; -+ } -+ -+ addr_hi |= ADDR_FILTX_FB_VALID; -+ ret = smsc75xx_write_reg(dev, ADDR_FILTX, addr_hi); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to write ADDR_FILTX: %d\n", ret); -+ return ret; -+ } -+ -+ ret = smsc75xx_write_reg(dev, ADDR_FILTX + 4, addr_lo); -+ if (ret < 0) -+ netdev_warn(dev->net, "Failed to write ADDR_FILTX+4: %d\n", ret); -+ -+ return ret; -+} -+ -+static int smsc75xx_phy_initialize(struct usbnet *dev) -+{ -+ int bmcr, ret, timeout = 0; -+ -+ /* Initialize MII structure */ -+ dev->mii.dev = dev->net; -+ dev->mii.mdio_read = smsc75xx_mdio_read; -+ dev->mii.mdio_write = smsc75xx_mdio_write; -+ dev->mii.phy_id_mask = 0x1f; -+ dev->mii.reg_num_mask = 0x1f; -+ dev->mii.supports_gmii = 1; -+ dev->mii.phy_id = SMSC75XX_INTERNAL_PHY_ID; -+ -+ /* reset phy and wait for reset to complete */ -+ smsc75xx_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET); -+ -+ do { -+ msleep(10); -+ bmcr = smsc75xx_mdio_read(dev->net, dev->mii.phy_id, MII_BMCR); -+ if (bmcr < 0) { -+ netdev_warn(dev->net, "Error reading MII_BMCR\n"); -+ return bmcr; -+ } -+ timeout++; -+ } while ((bmcr & BMCR_RESET) && (timeout < 100)); -+ -+ if (timeout >= 100) { -+ netdev_warn(dev->net, "timeout on PHY Reset\n"); -+ return -EIO; -+ } -+ -+ smsc75xx_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, -+ ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP | -+ ADVERTISE_PAUSE_ASYM); -+ smsc75xx_mdio_write(dev->net, dev->mii.phy_id, MII_CTRL1000, -+ ADVERTISE_1000FULL); -+ -+ /* read and write to clear phy interrupt status */ -+ ret = smsc75xx_mdio_read(dev->net, dev->mii.phy_id, PHY_INT_SRC); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading PHY_INT_SRC\n"); -+ return ret; -+ } -+ -+ smsc75xx_mdio_write(dev->net, dev->mii.phy_id, PHY_INT_SRC, 0xffff); -+ -+ smsc75xx_mdio_write(dev->net, dev->mii.phy_id, PHY_INT_MASK, -+ PHY_INT_MASK_DEFAULT); -+ mii_nway_restart(&dev->mii); -+ -+ netif_dbg(dev, ifup, dev->net, "phy initialised successfully\n"); -+ return 0; -+} -+ -+static int smsc75xx_set_rx_max_frame_length(struct usbnet *dev, int size) -+{ -+ int ret = 0; -+ u32 buf; -+ bool rxenabled; -+ -+ ret = smsc75xx_read_reg(dev, MAC_RX, &buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to read MAC_RX: %d\n", ret); -+ return ret; -+ } -+ -+ rxenabled = ((buf & MAC_RX_RXEN) != 0); -+ -+ if (rxenabled) { -+ buf &= ~MAC_RX_RXEN; -+ ret = smsc75xx_write_reg(dev, MAC_RX, buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret); -+ return ret; -+ } -+ } -+ -+ /* add 4 to size for FCS */ -+ buf &= ~MAC_RX_MAX_SIZE; -+ buf |= (((size + 4) << MAC_RX_MAX_SIZE_SHIFT) & MAC_RX_MAX_SIZE); -+ -+ ret = smsc75xx_write_reg(dev, MAC_RX, buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret); -+ return ret; -+ } -+ -+ if (rxenabled) { -+ buf |= MAC_RX_RXEN; -+ ret = smsc75xx_write_reg(dev, MAC_RX, buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret); -+ return ret; -+ } -+ } -+ -+ return 0; -+} -+ -+static int smsc75xx_change_mtu(struct net_device *netdev, int new_mtu) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ int ret; -+ -+ if (new_mtu > MAX_SINGLE_PACKET_SIZE) -+ return -EINVAL; -+ -+ ret = smsc75xx_set_rx_max_frame_length(dev, new_mtu + ETH_HLEN); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to set mac rx frame length\n"); -+ return ret; -+ } -+ -+ return usbnet_change_mtu(netdev, new_mtu); -+} -+ -+/* Enable or disable Rx checksum offload engine */ -+static int smsc75xx_set_features(struct net_device *netdev, -+ netdev_features_t features) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); -+ unsigned long flags; -+ int ret; -+ -+ spin_lock_irqsave(&pdata->rfe_ctl_lock, flags); -+ -+ if (features & NETIF_F_RXCSUM) -+ pdata->rfe_ctl |= RFE_CTL_TCPUDP_CKM | RFE_CTL_IP_CKM; -+ else -+ pdata->rfe_ctl &= ~(RFE_CTL_TCPUDP_CKM | RFE_CTL_IP_CKM); -+ -+ spin_unlock_irqrestore(&pdata->rfe_ctl_lock, flags); -+ /* it's racing here! */ -+ -+ ret = smsc75xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl); -+ if (ret < 0) -+ netdev_warn(dev->net, "Error writing RFE_CTL\n"); -+ -+ return ret; -+} -+ -+static int smsc75xx_wait_ready(struct usbnet *dev, int in_pm) -+{ -+ int timeout = 0; -+ -+ do { -+ u32 buf; -+ int ret; -+ -+ ret = __smsc75xx_read_reg(dev, PMT_CTL, &buf, in_pm); -+ -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to read PMT_CTL: %d\n", ret); -+ return ret; -+ } -+ -+ if (buf & PMT_CTL_DEV_RDY) -+ return 0; -+ -+ msleep(10); -+ timeout++; -+ } while (timeout < 100); -+ -+ netdev_warn(dev->net, "timeout waiting for device ready\n"); -+ return -EIO; -+} -+ -+static int smsc75xx_reset(struct usbnet *dev) -+{ -+ struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); -+ u32 buf; -+ int ret = 0, timeout; -+ -+ netif_dbg(dev, ifup, dev->net, "entering smsc75xx_reset\n"); -+ -+ ret = smsc75xx_wait_ready(dev, 0); -+ if (ret < 0) { -+ netdev_warn(dev->net, "device not ready in smsc75xx_reset\n"); -+ return ret; -+ } -+ -+ ret = smsc75xx_read_reg(dev, HW_CFG, &buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); -+ return ret; -+ } -+ -+ buf |= HW_CFG_LRST; -+ -+ ret = smsc75xx_write_reg(dev, HW_CFG, buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to write HW_CFG: %d\n", ret); -+ return ret; -+ } -+ -+ timeout = 0; -+ do { -+ msleep(10); -+ ret = smsc75xx_read_reg(dev, HW_CFG, &buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); -+ return ret; -+ } -+ timeout++; -+ } while ((buf & HW_CFG_LRST) && (timeout < 100)); -+ -+ if (timeout >= 100) { -+ netdev_warn(dev->net, "timeout on completion of Lite Reset\n"); -+ return -EIO; -+ } -+ -+ netif_dbg(dev, ifup, dev->net, "Lite reset complete, resetting PHY\n"); -+ -+ ret = smsc75xx_read_reg(dev, PMT_CTL, &buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to read PMT_CTL: %d\n", ret); -+ return ret; -+ } -+ -+ buf |= PMT_CTL_PHY_RST; -+ -+ ret = smsc75xx_write_reg(dev, PMT_CTL, buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to write PMT_CTL: %d\n", ret); -+ return ret; -+ } -+ -+ timeout = 0; -+ do { -+ msleep(10); -+ ret = smsc75xx_read_reg(dev, PMT_CTL, &buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to read PMT_CTL: %d\n", ret); -+ return ret; -+ } -+ timeout++; -+ } while ((buf & PMT_CTL_PHY_RST) && (timeout < 100)); -+ -+ if (timeout >= 100) { -+ netdev_warn(dev->net, "timeout waiting for PHY Reset\n"); -+ return -EIO; -+ } -+ -+ netif_dbg(dev, ifup, dev->net, "PHY reset complete\n"); -+ -+ ret = smsc75xx_set_mac_address(dev); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to set mac address\n"); -+ return ret; -+ } -+ -+ netif_dbg(dev, ifup, dev->net, "MAC Address: %pM\n", -+ dev->net->dev_addr); -+ -+ ret = smsc75xx_read_reg(dev, HW_CFG, &buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); -+ return ret; -+ } -+ -+ netif_dbg(dev, ifup, dev->net, "Read Value from HW_CFG : 0x%08x\n", -+ buf); -+ -+ buf |= HW_CFG_BIR; -+ -+ ret = smsc75xx_write_reg(dev, HW_CFG, buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to write HW_CFG: %d\n", ret); -+ return ret; -+ } -+ -+ ret = smsc75xx_read_reg(dev, HW_CFG, &buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); -+ return ret; -+ } -+ -+ netif_dbg(dev, ifup, dev->net, "Read Value from HW_CFG after writing HW_CFG_BIR: 0x%08x\n", -+ buf); -+ -+ if (!turbo_mode) { -+ buf = 0; -+ dev->rx_urb_size = MAX_SINGLE_PACKET_SIZE; -+ } else if (dev->udev->speed == USB_SPEED_HIGH) { -+ buf = DEFAULT_HS_BURST_CAP_SIZE / HS_USB_PKT_SIZE; -+ dev->rx_urb_size = DEFAULT_HS_BURST_CAP_SIZE; -+ } else { -+ buf = DEFAULT_FS_BURST_CAP_SIZE / FS_USB_PKT_SIZE; -+ dev->rx_urb_size = DEFAULT_FS_BURST_CAP_SIZE; -+ } -+ -+ netif_dbg(dev, ifup, dev->net, "rx_urb_size=%ld\n", -+ (ulong)dev->rx_urb_size); -+ -+ ret = smsc75xx_write_reg(dev, BURST_CAP, buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to write BURST_CAP: %d\n", ret); -+ return ret; -+ } -+ -+ ret = smsc75xx_read_reg(dev, BURST_CAP, &buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to read BURST_CAP: %d\n", ret); -+ return ret; -+ } -+ -+ netif_dbg(dev, ifup, dev->net, -+ "Read Value from BURST_CAP after writing: 0x%08x\n", buf); -+ -+ ret = smsc75xx_write_reg(dev, BULK_IN_DLY, DEFAULT_BULK_IN_DELAY); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to write BULK_IN_DLY: %d\n", ret); -+ return ret; -+ } -+ -+ ret = smsc75xx_read_reg(dev, BULK_IN_DLY, &buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to read BULK_IN_DLY: %d\n", ret); -+ return ret; -+ } -+ -+ netif_dbg(dev, ifup, dev->net, -+ "Read Value from BULK_IN_DLY after writing: 0x%08x\n", buf); -+ -+ if (turbo_mode) { -+ ret = smsc75xx_read_reg(dev, HW_CFG, &buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); -+ return ret; -+ } -+ -+ netif_dbg(dev, ifup, dev->net, "HW_CFG: 0x%08x\n", buf); -+ -+ buf |= (HW_CFG_MEF | HW_CFG_BCE); -+ -+ ret = smsc75xx_write_reg(dev, HW_CFG, buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to write HW_CFG: %d\n", ret); -+ return ret; -+ } -+ -+ ret = smsc75xx_read_reg(dev, HW_CFG, &buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); -+ return ret; -+ } -+ -+ netif_dbg(dev, ifup, dev->net, "HW_CFG: 0x%08x\n", buf); -+ } -+ -+ /* set FIFO sizes */ -+ buf = (MAX_RX_FIFO_SIZE - 512) / 512; -+ ret = smsc75xx_write_reg(dev, FCT_RX_FIFO_END, buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to write FCT_RX_FIFO_END: %d\n", ret); -+ return ret; -+ } -+ -+ netif_dbg(dev, ifup, dev->net, "FCT_RX_FIFO_END set to 0x%08x\n", buf); -+ -+ buf = (MAX_TX_FIFO_SIZE - 512) / 512; -+ ret = smsc75xx_write_reg(dev, FCT_TX_FIFO_END, buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to write FCT_TX_FIFO_END: %d\n", ret); -+ return ret; -+ } -+ -+ netif_dbg(dev, ifup, dev->net, "FCT_TX_FIFO_END set to 0x%08x\n", buf); -+ -+ ret = smsc75xx_write_reg(dev, INT_STS, INT_STS_CLEAR_ALL); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to write INT_STS: %d\n", ret); -+ return ret; -+ } -+ -+ ret = smsc75xx_read_reg(dev, ID_REV, &buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to read ID_REV: %d\n", ret); -+ return ret; -+ } -+ -+ netif_dbg(dev, ifup, dev->net, "ID_REV = 0x%08x\n", buf); -+ -+ ret = smsc75xx_read_reg(dev, E2P_CMD, &buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to read E2P_CMD: %d\n", ret); -+ return ret; -+ } -+ -+ /* only set default GPIO/LED settings if no EEPROM is detected */ -+ if (!(buf & E2P_CMD_LOADED)) { -+ ret = smsc75xx_read_reg(dev, LED_GPIO_CFG, &buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to read LED_GPIO_CFG: %d\n", ret); -+ return ret; -+ } -+ -+ buf &= ~(LED_GPIO_CFG_LED2_FUN_SEL | LED_GPIO_CFG_LED10_FUN_SEL); -+ buf |= LED_GPIO_CFG_LEDGPIO_EN | LED_GPIO_CFG_LED2_FUN_SEL; -+ -+ ret = smsc75xx_write_reg(dev, LED_GPIO_CFG, buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to write LED_GPIO_CFG: %d\n", ret); -+ return ret; -+ } -+ } -+ -+ ret = smsc75xx_write_reg(dev, FLOW, 0); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to write FLOW: %d\n", ret); -+ return ret; -+ } -+ -+ ret = smsc75xx_write_reg(dev, FCT_FLOW, 0); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to write FCT_FLOW: %d\n", ret); -+ return ret; -+ } -+ -+ /* Don't need rfe_ctl_lock during initialisation */ -+ ret = smsc75xx_read_reg(dev, RFE_CTL, &pdata->rfe_ctl); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to read RFE_CTL: %d\n", ret); -+ return ret; -+ } -+ -+ pdata->rfe_ctl |= RFE_CTL_AB | RFE_CTL_DPF; -+ -+ ret = smsc75xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to write RFE_CTL: %d\n", ret); -+ return ret; -+ } -+ -+ ret = smsc75xx_read_reg(dev, RFE_CTL, &pdata->rfe_ctl); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to read RFE_CTL: %d\n", ret); -+ return ret; -+ } -+ -+ netif_dbg(dev, ifup, dev->net, "RFE_CTL set to 0x%08x\n", -+ pdata->rfe_ctl); -+ -+ /* Enable or disable checksum offload engines */ -+ smsc75xx_set_features(dev->net, dev->net->features); -+ -+ smsc75xx_set_multicast(dev->net); -+ -+ ret = smsc75xx_phy_initialize(dev); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to initialize PHY: %d\n", ret); -+ return ret; -+ } -+ -+ ret = smsc75xx_read_reg(dev, INT_EP_CTL, &buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to read INT_EP_CTL: %d\n", ret); -+ return ret; -+ } -+ -+ /* enable PHY interrupts */ -+ buf |= INT_ENP_PHY_INT; -+ -+ ret = smsc75xx_write_reg(dev, INT_EP_CTL, buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to write INT_EP_CTL: %d\n", ret); -+ return ret; -+ } -+ -+ /* allow mac to detect speed and duplex from phy */ -+ ret = smsc75xx_read_reg(dev, MAC_CR, &buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to read MAC_CR: %d\n", ret); -+ return ret; -+ } -+ -+ buf |= (MAC_CR_ADD | MAC_CR_ASD); -+ ret = smsc75xx_write_reg(dev, MAC_CR, buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to write MAC_CR: %d\n", ret); -+ return ret; -+ } -+ -+ ret = smsc75xx_read_reg(dev, MAC_TX, &buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to read MAC_TX: %d\n", ret); -+ return ret; -+ } -+ -+ buf |= MAC_TX_TXEN; -+ -+ ret = smsc75xx_write_reg(dev, MAC_TX, buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to write MAC_TX: %d\n", ret); -+ return ret; -+ } -+ -+ netif_dbg(dev, ifup, dev->net, "MAC_TX set to 0x%08x\n", buf); -+ -+ ret = smsc75xx_read_reg(dev, FCT_TX_CTL, &buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to read FCT_TX_CTL: %d\n", ret); -+ return ret; -+ } -+ -+ buf |= FCT_TX_CTL_EN; -+ -+ ret = smsc75xx_write_reg(dev, FCT_TX_CTL, buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to write FCT_TX_CTL: %d\n", ret); -+ return ret; -+ } -+ -+ netif_dbg(dev, ifup, dev->net, "FCT_TX_CTL set to 0x%08x\n", buf); -+ -+ ret = smsc75xx_set_rx_max_frame_length(dev, dev->net->mtu + ETH_HLEN); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to set max rx frame length\n"); -+ return ret; -+ } -+ -+ ret = smsc75xx_read_reg(dev, MAC_RX, &buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to read MAC_RX: %d\n", ret); -+ return ret; -+ } -+ -+ buf |= MAC_RX_RXEN; -+ -+ ret = smsc75xx_write_reg(dev, MAC_RX, buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret); -+ return ret; -+ } -+ -+ netif_dbg(dev, ifup, dev->net, "MAC_RX set to 0x%08x\n", buf); -+ -+ ret = smsc75xx_read_reg(dev, FCT_RX_CTL, &buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to read FCT_RX_CTL: %d\n", ret); -+ return ret; -+ } -+ -+ buf |= FCT_RX_CTL_EN; -+ -+ ret = smsc75xx_write_reg(dev, FCT_RX_CTL, buf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to write FCT_RX_CTL: %d\n", ret); -+ return ret; -+ } -+ -+ netif_dbg(dev, ifup, dev->net, "FCT_RX_CTL set to 0x%08x\n", buf); -+ -+ netif_dbg(dev, ifup, dev->net, "smsc75xx_reset, return 0\n"); -+ return 0; -+} -+ -+static const struct net_device_ops smsc75xx_netdev_ops = { -+ .ndo_open = usbnet_open, -+ .ndo_stop = usbnet_stop, -+ .ndo_start_xmit = usbnet_start_xmit, -+ .ndo_tx_timeout = usbnet_tx_timeout, -+ .ndo_change_mtu = smsc75xx_change_mtu, -+ .ndo_set_mac_address = eth_mac_addr, -+ .ndo_validate_addr = eth_validate_addr, -+ .ndo_do_ioctl = smsc75xx_ioctl, -+ .ndo_set_rx_mode = smsc75xx_set_multicast, -+ .ndo_set_features = smsc75xx_set_features, -+}; -+ -+static int smsc75xx_bind(struct usbnet *dev, struct usb_interface *intf) -+{ -+ struct smsc75xx_priv *pdata = NULL; -+ int ret; -+ -+ printk(KERN_INFO SMSC_CHIPNAME " v" SMSC_DRIVER_VERSION "\n"); -+ -+ ret = usbnet_get_endpoints(dev, intf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "usbnet_get_endpoints failed: %d\n", ret); -+ return ret; -+ } -+ -+ dev->data[0] = (unsigned long)kzalloc(sizeof(struct smsc75xx_priv), -+ GFP_KERNEL); -+ -+ pdata = (struct smsc75xx_priv *)(dev->data[0]); -+ if (!pdata) -+ return -ENOMEM; -+ -+ pdata->dev = dev; -+ -+ spin_lock_init(&pdata->rfe_ctl_lock); -+ mutex_init(&pdata->dataport_mutex); -+ -+ INIT_WORK(&pdata->set_multicast, smsc75xx_deferred_multicast_write); -+ -+ if (DEFAULT_TX_CSUM_ENABLE) -+ dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; -+ -+ if (DEFAULT_RX_CSUM_ENABLE) -+ dev->net->features |= NETIF_F_RXCSUM; -+ -+ dev->net->hw_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | -+ NETIF_F_RXCSUM; -+ -+ ret = smsc75xx_wait_ready(dev, 0); -+ if (ret < 0) { -+ netdev_warn(dev->net, "device not ready in smsc75xx_bind\n"); -+ return ret; -+ } -+ -+ smsc75xx_init_mac_address(dev); -+ -+ /* Init all registers */ -+ ret = smsc75xx_reset(dev); -+ if (ret < 0) { -+ netdev_warn(dev->net, "smsc75xx_reset error %d\n", ret); -+ return ret; -+ } -+ -+ dev->net->netdev_ops = &smsc75xx_netdev_ops; -+ dev->net->ethtool_ops = &smsc75xx_ethtool_ops; -+ dev->net->flags |= IFF_MULTICAST; -+ dev->net->hard_header_len += SMSC75XX_TX_OVERHEAD; -+ dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; -+ return 0; -+} -+ -+static void smsc75xx_unbind(struct usbnet *dev, struct usb_interface *intf) -+{ -+ struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); -+ if (pdata) { -+ netif_dbg(dev, ifdown, dev->net, "free pdata\n"); -+ kfree(pdata); -+ pdata = NULL; -+ dev->data[0] = 0; -+ } -+} -+ -+static u16 smsc_crc(const u8 *buffer, size_t len) -+{ -+ return bitrev16(crc16(0xFFFF, buffer, len)); -+} -+ -+static int smsc75xx_write_wuff(struct usbnet *dev, int filter, u32 wuf_cfg, -+ u32 wuf_mask1) -+{ -+ int cfg_base = WUF_CFGX + filter * 4; -+ int mask_base = WUF_MASKX + filter * 16; -+ int ret; -+ -+ ret = smsc75xx_write_reg(dev, cfg_base, wuf_cfg); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing WUF_CFGX\n"); -+ return ret; -+ } -+ -+ ret = smsc75xx_write_reg(dev, mask_base, wuf_mask1); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing WUF_MASKX\n"); -+ return ret; -+ } -+ -+ ret = smsc75xx_write_reg(dev, mask_base + 4, 0); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing WUF_MASKX\n"); -+ return ret; -+ } -+ -+ ret = smsc75xx_write_reg(dev, mask_base + 8, 0); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing WUF_MASKX\n"); -+ return ret; -+ } -+ -+ ret = smsc75xx_write_reg(dev, mask_base + 12, 0); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing WUF_MASKX\n"); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int smsc75xx_enter_suspend0(struct usbnet *dev) -+{ -+ struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); -+ u32 val; -+ int ret; -+ -+ ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading PMT_CTL\n"); -+ return ret; -+ } -+ -+ val &= (~(PMT_CTL_SUS_MODE | PMT_CTL_PHY_RST)); -+ val |= PMT_CTL_SUS_MODE_0 | PMT_CTL_WOL_EN | PMT_CTL_WUPS; -+ -+ ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing PMT_CTL\n"); -+ return ret; -+ } -+ -+ pdata->suspend_flags |= SUSPEND_SUSPEND0; -+ -+ return 0; -+} -+ -+static int smsc75xx_enter_suspend1(struct usbnet *dev) -+{ -+ struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); -+ u32 val; -+ int ret; -+ -+ ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading PMT_CTL\n"); -+ return ret; -+ } -+ -+ val &= ~(PMT_CTL_SUS_MODE | PMT_CTL_WUPS | PMT_CTL_PHY_RST); -+ val |= PMT_CTL_SUS_MODE_1; -+ -+ ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing PMT_CTL\n"); -+ return ret; -+ } -+ -+ /* clear wol status, enable energy detection */ -+ val &= ~PMT_CTL_WUPS; -+ val |= (PMT_CTL_WUPS_ED | PMT_CTL_ED_EN); -+ -+ ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing PMT_CTL\n"); -+ return ret; -+ } -+ -+ pdata->suspend_flags |= SUSPEND_SUSPEND1; -+ -+ return 0; -+} -+ -+static int smsc75xx_enter_suspend2(struct usbnet *dev) -+{ -+ struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); -+ u32 val; -+ int ret; -+ -+ ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading PMT_CTL\n"); -+ return ret; -+ } -+ -+ val &= ~(PMT_CTL_SUS_MODE | PMT_CTL_WUPS | PMT_CTL_PHY_RST); -+ val |= PMT_CTL_SUS_MODE_2; -+ -+ ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing PMT_CTL\n"); -+ return ret; -+ } -+ -+ pdata->suspend_flags |= SUSPEND_SUSPEND2; -+ -+ return 0; -+} -+ -+static int smsc75xx_enter_suspend3(struct usbnet *dev) -+{ -+ struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); -+ u32 val; -+ int ret; -+ -+ ret = smsc75xx_read_reg_nopm(dev, FCT_RX_CTL, &val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading FCT_RX_CTL\n"); -+ return ret; -+ } -+ -+ if (val & FCT_RX_CTL_RXUSED) { -+ netdev_dbg(dev->net, "rx fifo not empty in autosuspend\n"); -+ return -EBUSY; -+ } -+ -+ ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading PMT_CTL\n"); -+ return ret; -+ } -+ -+ val &= ~(PMT_CTL_SUS_MODE | PMT_CTL_WUPS | PMT_CTL_PHY_RST); -+ val |= PMT_CTL_SUS_MODE_3 | PMT_CTL_RES_CLR_WKP_EN; -+ -+ ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing PMT_CTL\n"); -+ return ret; -+ } -+ -+ /* clear wol status */ -+ val &= ~PMT_CTL_WUPS; -+ val |= PMT_CTL_WUPS_WOL; -+ -+ ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing PMT_CTL\n"); -+ return ret; -+ } -+ -+ pdata->suspend_flags |= SUSPEND_SUSPEND3; -+ -+ return 0; -+} -+ -+static int smsc75xx_enable_phy_wakeup_interrupts(struct usbnet *dev, u16 mask) -+{ -+ struct mii_if_info *mii = &dev->mii; -+ int ret; -+ -+ netdev_dbg(dev->net, "enabling PHY wakeup interrupts\n"); -+ -+ /* read to clear */ -+ ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_INT_SRC); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading PHY_INT_SRC\n"); -+ return ret; -+ } -+ -+ /* enable interrupt source */ -+ ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_INT_MASK); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading PHY_INT_MASK\n"); -+ return ret; -+ } -+ -+ ret |= mask; -+ -+ smsc75xx_mdio_write_nopm(dev->net, mii->phy_id, PHY_INT_MASK, ret); -+ -+ return 0; -+} -+ -+static int smsc75xx_link_ok_nopm(struct usbnet *dev) -+{ -+ struct mii_if_info *mii = &dev->mii; -+ int ret; -+ -+ /* first, a dummy read, needed to latch some MII phys */ -+ ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, MII_BMSR); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading MII_BMSR\n"); -+ return ret; -+ } -+ -+ ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, MII_BMSR); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading MII_BMSR\n"); -+ return ret; -+ } -+ -+ return !!(ret & BMSR_LSTATUS); -+} -+ -+static int smsc75xx_autosuspend(struct usbnet *dev, u32 link_up) -+{ -+ int ret; -+ -+ if (!netif_running(dev->net)) { -+ /* interface is ifconfig down so fully power down hw */ -+ netdev_dbg(dev->net, "autosuspend entering SUSPEND2\n"); -+ return smsc75xx_enter_suspend2(dev); -+ } -+ -+ if (!link_up) { -+ /* link is down so enter EDPD mode */ -+ netdev_dbg(dev->net, "autosuspend entering SUSPEND1\n"); -+ -+ /* enable PHY wakeup events for if cable is attached */ -+ ret = smsc75xx_enable_phy_wakeup_interrupts(dev, -+ PHY_INT_MASK_ANEG_COMP); -+ if (ret < 0) { -+ netdev_warn(dev->net, "error enabling PHY wakeup ints\n"); -+ return ret; -+ } -+ -+ netdev_info(dev->net, "entering SUSPEND1 mode\n"); -+ return smsc75xx_enter_suspend1(dev); -+ } -+ -+ /* enable PHY wakeup events so we remote wakeup if cable is pulled */ -+ ret = smsc75xx_enable_phy_wakeup_interrupts(dev, -+ PHY_INT_MASK_LINK_DOWN); -+ if (ret < 0) { -+ netdev_warn(dev->net, "error enabling PHY wakeup ints\n"); -+ return ret; -+ } -+ -+ netdev_dbg(dev->net, "autosuspend entering SUSPEND3\n"); -+ return smsc75xx_enter_suspend3(dev); -+} -+ -+static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message) -+{ -+ struct usbnet *dev = usb_get_intfdata(intf); -+ struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); -+ u32 val, link_up; -+ int ret; -+ -+ ret = usbnet_suspend(intf, message); -+ if (ret < 0) { -+ netdev_warn(dev->net, "usbnet_suspend error\n"); -+ return ret; -+ } -+ -+ if (pdata->suspend_flags) { -+ netdev_warn(dev->net, "error during last resume\n"); -+ pdata->suspend_flags = 0; -+ } -+ -+ /* determine if link is up using only _nopm functions */ -+ link_up = smsc75xx_link_ok_nopm(dev); -+ -+ if (message.event == PM_EVENT_AUTO_SUSPEND) { -+ ret = smsc75xx_autosuspend(dev, link_up); -+ goto done; -+ } -+ -+ /* if we get this far we're not autosuspending */ -+ /* if no wol options set, or if link is down and we're not waking on -+ * PHY activity, enter lowest power SUSPEND2 mode -+ */ -+ if (!(pdata->wolopts & SUPPORTED_WAKE) || -+ !(link_up || (pdata->wolopts & WAKE_PHY))) { -+ netdev_info(dev->net, "entering SUSPEND2 mode\n"); -+ -+ /* disable energy detect (link up) & wake up events */ -+ ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading WUCSR\n"); -+ goto done; -+ } -+ -+ val &= ~(WUCSR_MPEN | WUCSR_WUEN); -+ -+ ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing WUCSR\n"); -+ goto done; -+ } -+ -+ ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading PMT_CTL\n"); -+ goto done; -+ } -+ -+ val &= ~(PMT_CTL_ED_EN | PMT_CTL_WOL_EN); -+ -+ ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing PMT_CTL\n"); -+ goto done; -+ } -+ -+ ret = smsc75xx_enter_suspend2(dev); -+ goto done; -+ } -+ -+ if (pdata->wolopts & WAKE_PHY) { -+ ret = smsc75xx_enable_phy_wakeup_interrupts(dev, -+ (PHY_INT_MASK_ANEG_COMP | PHY_INT_MASK_LINK_DOWN)); -+ if (ret < 0) { -+ netdev_warn(dev->net, "error enabling PHY wakeup ints\n"); -+ goto done; -+ } -+ -+ /* if link is down then configure EDPD and enter SUSPEND1, -+ * otherwise enter SUSPEND0 below -+ */ -+ if (!link_up) { -+ struct mii_if_info *mii = &dev->mii; -+ netdev_info(dev->net, "entering SUSPEND1 mode\n"); -+ -+ /* enable energy detect power-down mode */ -+ ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, -+ PHY_MODE_CTRL_STS); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading PHY_MODE_CTRL_STS\n"); -+ goto done; -+ } -+ -+ ret |= MODE_CTRL_STS_EDPWRDOWN; -+ -+ smsc75xx_mdio_write_nopm(dev->net, mii->phy_id, -+ PHY_MODE_CTRL_STS, ret); -+ -+ /* enter SUSPEND1 mode */ -+ ret = smsc75xx_enter_suspend1(dev); -+ goto done; -+ } -+ } -+ -+ if (pdata->wolopts & (WAKE_MCAST | WAKE_ARP)) { -+ int i, filter = 0; -+ -+ /* disable all filters */ -+ for (i = 0; i < WUF_NUM; i++) { -+ ret = smsc75xx_write_reg_nopm(dev, WUF_CFGX + i * 4, 0); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing WUF_CFGX\n"); -+ goto done; -+ } -+ } -+ -+ if (pdata->wolopts & WAKE_MCAST) { -+ const u8 mcast[] = {0x01, 0x00, 0x5E}; -+ netdev_info(dev->net, "enabling multicast detection\n"); -+ -+ val = WUF_CFGX_EN | WUF_CFGX_ATYPE_MULTICAST -+ | smsc_crc(mcast, 3); -+ ret = smsc75xx_write_wuff(dev, filter++, val, 0x0007); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing wakeup filter\n"); -+ goto done; -+ } -+ } -+ -+ if (pdata->wolopts & WAKE_ARP) { -+ const u8 arp[] = {0x08, 0x06}; -+ netdev_info(dev->net, "enabling ARP detection\n"); -+ -+ val = WUF_CFGX_EN | WUF_CFGX_ATYPE_ALL | (0x0C << 16) -+ | smsc_crc(arp, 2); -+ ret = smsc75xx_write_wuff(dev, filter++, val, 0x0003); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing wakeup filter\n"); -+ goto done; -+ } -+ } -+ -+ /* clear any pending pattern match packet status */ -+ ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading WUCSR\n"); -+ goto done; -+ } -+ -+ val |= WUCSR_WUFR; -+ -+ ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing WUCSR\n"); -+ goto done; -+ } -+ -+ netdev_info(dev->net, "enabling packet match detection\n"); -+ ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading WUCSR\n"); -+ goto done; -+ } -+ -+ val |= WUCSR_WUEN; -+ -+ ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing WUCSR\n"); -+ goto done; -+ } -+ } else { -+ netdev_info(dev->net, "disabling packet match detection\n"); -+ ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading WUCSR\n"); -+ goto done; -+ } -+ -+ val &= ~WUCSR_WUEN; -+ -+ ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing WUCSR\n"); -+ goto done; -+ } -+ } -+ -+ /* disable magic, bcast & unicast wakeup sources */ -+ ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading WUCSR\n"); -+ goto done; -+ } -+ -+ val &= ~(WUCSR_MPEN | WUCSR_BCST_EN | WUCSR_PFDA_EN); -+ -+ ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing WUCSR\n"); -+ goto done; -+ } -+ -+ if (pdata->wolopts & WAKE_PHY) { -+ netdev_info(dev->net, "enabling PHY wakeup\n"); -+ -+ ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading PMT_CTL\n"); -+ goto done; -+ } -+ -+ /* clear wol status, enable energy detection */ -+ val &= ~PMT_CTL_WUPS; -+ val |= (PMT_CTL_WUPS_ED | PMT_CTL_ED_EN); -+ -+ ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing PMT_CTL\n"); -+ goto done; -+ } -+ } -+ -+ if (pdata->wolopts & WAKE_MAGIC) { -+ netdev_info(dev->net, "enabling magic packet wakeup\n"); -+ ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading WUCSR\n"); -+ goto done; -+ } -+ -+ /* clear any pending magic packet status */ -+ val |= WUCSR_MPR | WUCSR_MPEN; -+ -+ ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing WUCSR\n"); -+ goto done; -+ } -+ } -+ -+ if (pdata->wolopts & WAKE_BCAST) { -+ netdev_info(dev->net, "enabling broadcast detection\n"); -+ ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading WUCSR\n"); -+ goto done; -+ } -+ -+ val |= WUCSR_BCAST_FR | WUCSR_BCST_EN; -+ -+ ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing WUCSR\n"); -+ goto done; -+ } -+ } -+ -+ if (pdata->wolopts & WAKE_UCAST) { -+ netdev_info(dev->net, "enabling unicast detection\n"); -+ ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading WUCSR\n"); -+ goto done; -+ } -+ -+ val |= WUCSR_WUFR | WUCSR_PFDA_EN; -+ -+ ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing WUCSR\n"); -+ goto done; -+ } -+ } -+ -+ /* enable receiver to enable frame reception */ -+ ret = smsc75xx_read_reg_nopm(dev, MAC_RX, &val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to read MAC_RX: %d\n", ret); -+ goto done; -+ } -+ -+ val |= MAC_RX_RXEN; -+ -+ ret = smsc75xx_write_reg_nopm(dev, MAC_RX, val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret); -+ goto done; -+ } -+ -+ /* some wol options are enabled, so enter SUSPEND0 */ -+ netdev_info(dev->net, "entering SUSPEND0 mode\n"); -+ ret = smsc75xx_enter_suspend0(dev); -+ -+done: -+ /* -+ * TODO: resume() might need to handle the suspend failure -+ * in system sleep -+ */ -+ if (ret && PMSG_IS_AUTO(message)) -+ usbnet_resume(intf); -+ return ret; -+} -+ -+static int smsc75xx_resume(struct usb_interface *intf) -+{ -+ struct usbnet *dev = usb_get_intfdata(intf); -+ struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); -+ u8 suspend_flags = pdata->suspend_flags; -+ int ret; -+ u32 val; -+ -+ netdev_dbg(dev->net, "resume suspend_flags=0x%02x\n", suspend_flags); -+ -+ /* do this first to ensure it's cleared even in error case */ -+ pdata->suspend_flags = 0; -+ -+ if (suspend_flags & SUSPEND_ALLMODES) { -+ /* Disable wakeup sources */ -+ ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading WUCSR\n"); -+ return ret; -+ } -+ -+ val &= ~(WUCSR_WUEN | WUCSR_MPEN | WUCSR_PFDA_EN -+ | WUCSR_BCST_EN); -+ -+ ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing WUCSR\n"); -+ return ret; -+ } -+ -+ /* clear wake-up status */ -+ ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading PMT_CTL\n"); -+ return ret; -+ } -+ -+ val &= ~PMT_CTL_WOL_EN; -+ val |= PMT_CTL_WUPS; -+ -+ ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing PMT_CTL\n"); -+ return ret; -+ } -+ } -+ -+ if (suspend_flags & SUSPEND_SUSPEND2) { -+ netdev_info(dev->net, "resuming from SUSPEND2\n"); -+ -+ ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading PMT_CTL\n"); -+ return ret; -+ } -+ -+ val |= PMT_CTL_PHY_PWRUP; -+ -+ ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing PMT_CTL\n"); -+ return ret; -+ } -+ } -+ -+ ret = smsc75xx_wait_ready(dev, 1); -+ if (ret < 0) { -+ netdev_warn(dev->net, "device not ready in smsc75xx_resume\n"); -+ return ret; -+ } -+ -+ return usbnet_resume(intf); -+} -+ -+static void smsc75xx_rx_csum_offload(struct usbnet *dev, struct sk_buff *skb, -+ u32 rx_cmd_a, u32 rx_cmd_b) -+{ -+ if (!(dev->net->features & NETIF_F_RXCSUM) || -+ unlikely(rx_cmd_a & RX_CMD_A_LCSM)) { -+ skb->ip_summed = CHECKSUM_NONE; -+ } else { -+ skb->csum = ntohs((u16)(rx_cmd_b >> RX_CMD_B_CSUM_SHIFT)); -+ skb->ip_summed = CHECKSUM_COMPLETE; -+ } -+} -+ -+static int smsc75xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb) -+{ -+ /* This check is no longer done by usbnet */ -+ if (skb->len < dev->net->hard_header_len) -+ return 0; -+ -+ while (skb->len > 0) { -+ u32 rx_cmd_a, rx_cmd_b, align_count, size; -+ struct sk_buff *ax_skb; -+ unsigned char *packet; -+ -+ memcpy(&rx_cmd_a, skb->data, sizeof(rx_cmd_a)); -+ le32_to_cpus(&rx_cmd_a); -+ skb_pull(skb, 4); -+ -+ memcpy(&rx_cmd_b, skb->data, sizeof(rx_cmd_b)); -+ le32_to_cpus(&rx_cmd_b); -+ skb_pull(skb, 4 + RXW_PADDING); -+ -+ packet = skb->data; -+ -+ /* get the packet length */ -+ size = (rx_cmd_a & RX_CMD_A_LEN) - RXW_PADDING; -+ align_count = (4 - ((size + RXW_PADDING) % 4)) % 4; -+ -+ if (unlikely(rx_cmd_a & RX_CMD_A_RED)) { -+ netif_dbg(dev, rx_err, dev->net, -+ "Error rx_cmd_a=0x%08x\n", rx_cmd_a); -+ dev->net->stats.rx_errors++; -+ dev->net->stats.rx_dropped++; -+ -+ if (rx_cmd_a & RX_CMD_A_FCS) -+ dev->net->stats.rx_crc_errors++; -+ else if (rx_cmd_a & (RX_CMD_A_LONG | RX_CMD_A_RUNT)) -+ dev->net->stats.rx_frame_errors++; -+ } else { -+ /* MAX_SINGLE_PACKET_SIZE + 4(CRC) + 2(COE) + 4(Vlan) */ -+ if (unlikely(size > (MAX_SINGLE_PACKET_SIZE + ETH_HLEN + 12))) { -+ netif_dbg(dev, rx_err, dev->net, -+ "size err rx_cmd_a=0x%08x\n", -+ rx_cmd_a); -+ return 0; -+ } -+ -+ /* last frame in this batch */ -+ if (skb->len == size) { -+ smsc75xx_rx_csum_offload(dev, skb, rx_cmd_a, -+ rx_cmd_b); -+ -+ skb_trim(skb, skb->len - 4); /* remove fcs */ -+ skb->truesize = size + sizeof(struct sk_buff); -+ -+ return 1; -+ } -+ -+ ax_skb = skb_clone(skb, GFP_ATOMIC); -+ if (unlikely(!ax_skb)) { -+ netdev_warn(dev->net, "Error allocating skb\n"); -+ return 0; -+ } -+ -+ ax_skb->len = size; -+ ax_skb->data = packet; -+ skb_set_tail_pointer(ax_skb, size); -+ -+ smsc75xx_rx_csum_offload(dev, ax_skb, rx_cmd_a, -+ rx_cmd_b); -+ -+ skb_trim(ax_skb, ax_skb->len - 4); /* remove fcs */ -+ ax_skb->truesize = size + sizeof(struct sk_buff); -+ -+ usbnet_skb_return(dev, ax_skb); -+ } -+ -+ skb_pull(skb, size); -+ -+ /* padding bytes before the next frame starts */ -+ if (skb->len) -+ skb_pull(skb, align_count); -+ } -+ -+ if (unlikely(skb->len < 0)) { -+ netdev_warn(dev->net, "invalid rx length<0 %d\n", skb->len); -+ return 0; -+ } -+ -+ return 1; -+} -+ -+static struct sk_buff *smsc75xx_tx_fixup(struct usbnet *dev, -+ struct sk_buff *skb, gfp_t flags) -+{ -+ u32 tx_cmd_a, tx_cmd_b; -+ -+ if (skb_headroom(skb) < SMSC75XX_TX_OVERHEAD) { -+ struct sk_buff *skb2 = -+ skb_copy_expand(skb, SMSC75XX_TX_OVERHEAD, 0, flags); -+ dev_kfree_skb_any(skb); -+ skb = skb2; -+ if (!skb) -+ return NULL; -+ } -+ -+ tx_cmd_a = (u32)(skb->len & TX_CMD_A_LEN) | TX_CMD_A_FCS; -+ -+ if (skb->ip_summed == CHECKSUM_PARTIAL) -+ tx_cmd_a |= TX_CMD_A_IPE | TX_CMD_A_TPE; -+ -+ if (skb_is_gso(skb)) { -+ u16 mss = max(skb_shinfo(skb)->gso_size, TX_MSS_MIN); -+ tx_cmd_b = (mss << TX_CMD_B_MSS_SHIFT) & TX_CMD_B_MSS; -+ -+ tx_cmd_a |= TX_CMD_A_LSO; -+ } else { -+ tx_cmd_b = 0; -+ } -+ -+ skb_push(skb, 4); -+ cpu_to_le32s(&tx_cmd_b); -+ memcpy(skb->data, &tx_cmd_b, 4); -+ -+ skb_push(skb, 4); -+ cpu_to_le32s(&tx_cmd_a); -+ memcpy(skb->data, &tx_cmd_a, 4); -+ -+ return skb; -+} -+ -+static int smsc75xx_manage_power(struct usbnet *dev, int on) -+{ -+ dev->intf->needs_remote_wakeup = on; -+ return 0; -+} -+ -+static const struct driver_info smsc75xx_info = { -+ .description = "smsc75xx USB 2.0 Gigabit Ethernet", -+ .bind = smsc75xx_bind, -+ .unbind = smsc75xx_unbind, -+ .link_reset = smsc75xx_link_reset, -+ .reset = smsc75xx_reset, -+ .rx_fixup = smsc75xx_rx_fixup, -+ .tx_fixup = smsc75xx_tx_fixup, -+ .status = smsc75xx_status, -+ .manage_power = smsc75xx_manage_power, -+ .flags = FLAG_ETHER | FLAG_SEND_ZLP | FLAG_LINK_INTR, -+}; -+ -+static const struct usb_device_id products[] = { -+ { -+ /* SMSC7500 USB Gigabit Ethernet Device */ -+ USB_DEVICE(USB_VENDOR_ID_SMSC, USB_PRODUCT_ID_LAN7500), -+ .driver_info = (unsigned long) &smsc75xx_info, -+ }, -+ { -+ /* SMSC7500 USB Gigabit Ethernet Device */ -+ USB_DEVICE(USB_VENDOR_ID_SMSC, USB_PRODUCT_ID_LAN7505), -+ .driver_info = (unsigned long) &smsc75xx_info, -+ }, -+ { }, /* END */ -+}; -+MODULE_DEVICE_TABLE(usb, products); -+ -+static struct usb_driver smsc75xx_driver = { -+ .name = SMSC_CHIPNAME, -+ .id_table = products, -+ .probe = usbnet_probe, -+ .suspend = smsc75xx_suspend, -+ .resume = smsc75xx_resume, -+ .reset_resume = smsc75xx_resume, -+ .disconnect = usbnet_disconnect, -+ .disable_hub_initiated_lpm = 1, -+ .supports_autosuspend = 1, -+}; -+ -+module_usb_driver(smsc75xx_driver); -+ -+MODULE_AUTHOR("Nancy Lin"); -+MODULE_AUTHOR("Steve Glendinning "); -+MODULE_DESCRIPTION("SMSC75XX USB 2.0 Gigabit Ethernet Devices"); -+MODULE_LICENSE("GPL"); -diff -Naur backports-4.2.6-1.org/drivers/net/usb/smsc75xx.h backports-4.2.6-1/drivers/net/usb/smsc75xx.h ---- backports-4.2.6-1.org/drivers/net/usb/smsc75xx.h 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/smsc75xx.h 2016-06-28 14:35:18.008640551 +0200 -@@ -0,0 +1,421 @@ -+ /*************************************************************************** -+ * -+ * Copyright (C) 2007-2010 SMSC -+ * -+ * 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, see . -+ * -+ *****************************************************************************/ -+ -+#ifndef _SMSC75XX_H -+#define _SMSC75XX_H -+ -+/* Tx command words */ -+#define TX_CMD_A_LSO (0x08000000) -+#define TX_CMD_A_IPE (0x04000000) -+#define TX_CMD_A_TPE (0x02000000) -+#define TX_CMD_A_IVTG (0x01000000) -+#define TX_CMD_A_RVTG (0x00800000) -+#define TX_CMD_A_FCS (0x00400000) -+#define TX_CMD_A_LEN (0x000FFFFF) -+ -+#define TX_CMD_B_MSS (0x3FFF0000) -+#define TX_CMD_B_MSS_SHIFT (16) -+#define TX_MSS_MIN ((u16)8) -+#define TX_CMD_B_VTAG (0x0000FFFF) -+ -+/* Rx command words */ -+#define RX_CMD_A_ICE (0x80000000) -+#define RX_CMD_A_TCE (0x40000000) -+#define RX_CMD_A_IPV (0x20000000) -+#define RX_CMD_A_PID (0x18000000) -+#define RX_CMD_A_PID_NIP (0x00000000) -+#define RX_CMD_A_PID_TCP (0x08000000) -+#define RX_CMD_A_PID_UDP (0x10000000) -+#define RX_CMD_A_PID_PP (0x18000000) -+#define RX_CMD_A_PFF (0x04000000) -+#define RX_CMD_A_BAM (0x02000000) -+#define RX_CMD_A_MAM (0x01000000) -+#define RX_CMD_A_FVTG (0x00800000) -+#define RX_CMD_A_RED (0x00400000) -+#define RX_CMD_A_RWT (0x00200000) -+#define RX_CMD_A_RUNT (0x00100000) -+#define RX_CMD_A_LONG (0x00080000) -+#define RX_CMD_A_RXE (0x00040000) -+#define RX_CMD_A_DRB (0x00020000) -+#define RX_CMD_A_FCS (0x00010000) -+#define RX_CMD_A_UAM (0x00008000) -+#define RX_CMD_A_LCSM (0x00004000) -+#define RX_CMD_A_LEN (0x00003FFF) -+ -+#define RX_CMD_B_CSUM (0xFFFF0000) -+#define RX_CMD_B_CSUM_SHIFT (16) -+#define RX_CMD_B_VTAG (0x0000FFFF) -+ -+/* SCSRs */ -+#define ID_REV (0x0000) -+ -+#define FPGA_REV (0x0004) -+ -+#define BOND_CTL (0x0008) -+ -+#define INT_STS (0x000C) -+#define INT_STS_RDFO_INT (0x00400000) -+#define INT_STS_TXE_INT (0x00200000) -+#define INT_STS_MACRTO_INT (0x00100000) -+#define INT_STS_TX_DIS_INT (0x00080000) -+#define INT_STS_RX_DIS_INT (0x00040000) -+#define INT_STS_PHY_INT_ (0x00020000) -+#define INT_STS_MAC_ERR_INT (0x00008000) -+#define INT_STS_TDFU (0x00004000) -+#define INT_STS_TDFO (0x00002000) -+#define INT_STS_GPIOS (0x00000FFF) -+#define INT_STS_CLEAR_ALL (0xFFFFFFFF) -+ -+#define HW_CFG (0x0010) -+#define HW_CFG_SMDET_STS (0x00008000) -+#define HW_CFG_SMDET_EN (0x00004000) -+#define HW_CFG_EEM (0x00002000) -+#define HW_CFG_RST_PROTECT (0x00001000) -+#define HW_CFG_PORT_SWAP (0x00000800) -+#define HW_CFG_PHY_BOOST (0x00000600) -+#define HW_CFG_PHY_BOOST_NORMAL (0x00000000) -+#define HW_CFG_PHY_BOOST_4 (0x00002000) -+#define HW_CFG_PHY_BOOST_8 (0x00004000) -+#define HW_CFG_PHY_BOOST_12 (0x00006000) -+#define HW_CFG_LEDB (0x00000100) -+#define HW_CFG_BIR (0x00000080) -+#define HW_CFG_SBP (0x00000040) -+#define HW_CFG_IME (0x00000020) -+#define HW_CFG_MEF (0x00000010) -+#define HW_CFG_ETC (0x00000008) -+#define HW_CFG_BCE (0x00000004) -+#define HW_CFG_LRST (0x00000002) -+#define HW_CFG_SRST (0x00000001) -+ -+#define PMT_CTL (0x0014) -+#define PMT_CTL_PHY_PWRUP (0x00000400) -+#define PMT_CTL_RES_CLR_WKP_EN (0x00000100) -+#define PMT_CTL_DEV_RDY (0x00000080) -+#define PMT_CTL_SUS_MODE (0x00000060) -+#define PMT_CTL_SUS_MODE_0 (0x00000000) -+#define PMT_CTL_SUS_MODE_1 (0x00000020) -+#define PMT_CTL_SUS_MODE_2 (0x00000040) -+#define PMT_CTL_SUS_MODE_3 (0x00000060) -+#define PMT_CTL_PHY_RST (0x00000010) -+#define PMT_CTL_WOL_EN (0x00000008) -+#define PMT_CTL_ED_EN (0x00000004) -+#define PMT_CTL_WUPS (0x00000003) -+#define PMT_CTL_WUPS_NO (0x00000000) -+#define PMT_CTL_WUPS_ED (0x00000001) -+#define PMT_CTL_WUPS_WOL (0x00000002) -+#define PMT_CTL_WUPS_MULTI (0x00000003) -+ -+#define LED_GPIO_CFG (0x0018) -+#define LED_GPIO_CFG_LED2_FUN_SEL (0x80000000) -+#define LED_GPIO_CFG_LED10_FUN_SEL (0x40000000) -+#define LED_GPIO_CFG_LEDGPIO_EN (0x0000F000) -+#define LED_GPIO_CFG_LEDGPIO_EN_0 (0x00001000) -+#define LED_GPIO_CFG_LEDGPIO_EN_1 (0x00002000) -+#define LED_GPIO_CFG_LEDGPIO_EN_2 (0x00004000) -+#define LED_GPIO_CFG_LEDGPIO_EN_3 (0x00008000) -+#define LED_GPIO_CFG_GPBUF (0x00000F00) -+#define LED_GPIO_CFG_GPBUF_0 (0x00000100) -+#define LED_GPIO_CFG_GPBUF_1 (0x00000200) -+#define LED_GPIO_CFG_GPBUF_2 (0x00000400) -+#define LED_GPIO_CFG_GPBUF_3 (0x00000800) -+#define LED_GPIO_CFG_GPDIR (0x000000F0) -+#define LED_GPIO_CFG_GPDIR_0 (0x00000010) -+#define LED_GPIO_CFG_GPDIR_1 (0x00000020) -+#define LED_GPIO_CFG_GPDIR_2 (0x00000040) -+#define LED_GPIO_CFG_GPDIR_3 (0x00000080) -+#define LED_GPIO_CFG_GPDATA (0x0000000F) -+#define LED_GPIO_CFG_GPDATA_0 (0x00000001) -+#define LED_GPIO_CFG_GPDATA_1 (0x00000002) -+#define LED_GPIO_CFG_GPDATA_2 (0x00000004) -+#define LED_GPIO_CFG_GPDATA_3 (0x00000008) -+ -+#define GPIO_CFG (0x001C) -+#define GPIO_CFG_SHIFT (24) -+#define GPIO_CFG_GPEN (0xFF000000) -+#define GPIO_CFG_GPBUF (0x00FF0000) -+#define GPIO_CFG_GPDIR (0x0000FF00) -+#define GPIO_CFG_GPDATA (0x000000FF) -+ -+#define GPIO_WAKE (0x0020) -+#define GPIO_WAKE_PHY_LINKUP_EN (0x80000000) -+#define GPIO_WAKE_POL (0x0FFF0000) -+#define GPIO_WAKE_POL_SHIFT (16) -+#define GPIO_WAKE_WK (0x00000FFF) -+ -+#define DP_SEL (0x0024) -+#define DP_SEL_DPRDY (0x80000000) -+#define DP_SEL_RSEL (0x0000000F) -+#define DP_SEL_URX (0x00000000) -+#define DP_SEL_VHF (0x00000001) -+#define DP_SEL_VHF_HASH_LEN (16) -+#define DP_SEL_VHF_VLAN_LEN (128) -+#define DP_SEL_LSO_HEAD (0x00000002) -+#define DP_SEL_FCT_RX (0x00000003) -+#define DP_SEL_FCT_TX (0x00000004) -+#define DP_SEL_DESCRIPTOR (0x00000005) -+#define DP_SEL_WOL (0x00000006) -+ -+#define DP_CMD (0x0028) -+#define DP_CMD_WRITE (0x01) -+#define DP_CMD_READ (0x00) -+ -+#define DP_ADDR (0x002C) -+ -+#define DP_DATA (0x0030) -+ -+#define BURST_CAP (0x0034) -+#define BURST_CAP_MASK (0x0000000F) -+ -+#define INT_EP_CTL (0x0038) -+#define INT_EP_CTL_INTEP_ON (0x80000000) -+#define INT_EP_CTL_RDFO_EN (0x00400000) -+#define INT_EP_CTL_TXE_EN (0x00200000) -+#define INT_EP_CTL_MACROTO_EN (0x00100000) -+#define INT_EP_CTL_TX_DIS_EN (0x00080000) -+#define INT_EP_CTL_RX_DIS_EN (0x00040000) -+#define INT_EP_CTL_PHY_EN_ (0x00020000) -+#define INT_EP_CTL_MAC_ERR_EN (0x00008000) -+#define INT_EP_CTL_TDFU_EN (0x00004000) -+#define INT_EP_CTL_TDFO_EN (0x00002000) -+#define INT_EP_CTL_RX_FIFO_EN (0x00001000) -+#define INT_EP_CTL_GPIOX_EN (0x00000FFF) -+ -+#define BULK_IN_DLY (0x003C) -+#define BULK_IN_DLY_MASK (0xFFFF) -+ -+#define E2P_CMD (0x0040) -+#define E2P_CMD_BUSY (0x80000000) -+#define E2P_CMD_MASK (0x70000000) -+#define E2P_CMD_READ (0x00000000) -+#define E2P_CMD_EWDS (0x10000000) -+#define E2P_CMD_EWEN (0x20000000) -+#define E2P_CMD_WRITE (0x30000000) -+#define E2P_CMD_WRAL (0x40000000) -+#define E2P_CMD_ERASE (0x50000000) -+#define E2P_CMD_ERAL (0x60000000) -+#define E2P_CMD_RELOAD (0x70000000) -+#define E2P_CMD_TIMEOUT (0x00000400) -+#define E2P_CMD_LOADED (0x00000200) -+#define E2P_CMD_ADDR (0x000001FF) -+ -+#define MAX_EEPROM_SIZE (512) -+ -+#define E2P_DATA (0x0044) -+#define E2P_DATA_MASK_ (0x000000FF) -+ -+#define RFE_CTL (0x0060) -+#define RFE_CTL_TCPUDP_CKM (0x00001000) -+#define RFE_CTL_IP_CKM (0x00000800) -+#define RFE_CTL_AB (0x00000400) -+#define RFE_CTL_AM (0x00000200) -+#define RFE_CTL_AU (0x00000100) -+#define RFE_CTL_VS (0x00000080) -+#define RFE_CTL_UF (0x00000040) -+#define RFE_CTL_VF (0x00000020) -+#define RFE_CTL_SPF (0x00000010) -+#define RFE_CTL_MHF (0x00000008) -+#define RFE_CTL_DHF (0x00000004) -+#define RFE_CTL_DPF (0x00000002) -+#define RFE_CTL_RST_RF (0x00000001) -+ -+#define VLAN_TYPE (0x0064) -+#define VLAN_TYPE_MASK (0x0000FFFF) -+ -+#define FCT_RX_CTL (0x0090) -+#define FCT_RX_CTL_EN (0x80000000) -+#define FCT_RX_CTL_RST (0x40000000) -+#define FCT_RX_CTL_SBF (0x02000000) -+#define FCT_RX_CTL_OVERFLOW (0x01000000) -+#define FCT_RX_CTL_FRM_DROP (0x00800000) -+#define FCT_RX_CTL_RX_NOT_EMPTY (0x00400000) -+#define FCT_RX_CTL_RX_EMPTY (0x00200000) -+#define FCT_RX_CTL_RX_DISABLED (0x00100000) -+#define FCT_RX_CTL_RXUSED (0x0000FFFF) -+ -+#define FCT_TX_CTL (0x0094) -+#define FCT_TX_CTL_EN (0x80000000) -+#define FCT_TX_CTL_RST (0x40000000) -+#define FCT_TX_CTL_TX_NOT_EMPTY (0x00400000) -+#define FCT_TX_CTL_TX_EMPTY (0x00200000) -+#define FCT_TX_CTL_TX_DISABLED (0x00100000) -+#define FCT_TX_CTL_TXUSED (0x0000FFFF) -+ -+#define FCT_RX_FIFO_END (0x0098) -+#define FCT_RX_FIFO_END_MASK (0x0000007F) -+ -+#define FCT_TX_FIFO_END (0x009C) -+#define FCT_TX_FIFO_END_MASK (0x0000003F) -+ -+#define FCT_FLOW (0x00A0) -+#define FCT_FLOW_THRESHOLD_OFF (0x00007F00) -+#define FCT_FLOW_THRESHOLD_OFF_SHIFT (8) -+#define FCT_FLOW_THRESHOLD_ON (0x0000007F) -+ -+/* MAC CSRs */ -+#define MAC_CR (0x100) -+#define MAC_CR_ADP (0x00002000) -+#define MAC_CR_ADD (0x00001000) -+#define MAC_CR_ASD (0x00000800) -+#define MAC_CR_INT_LOOP (0x00000400) -+#define MAC_CR_BOLMT (0x000000C0) -+#define MAC_CR_FDPX (0x00000008) -+#define MAC_CR_CFG (0x00000006) -+#define MAC_CR_CFG_10 (0x00000000) -+#define MAC_CR_CFG_100 (0x00000002) -+#define MAC_CR_CFG_1000 (0x00000004) -+#define MAC_CR_RST (0x00000001) -+ -+#define MAC_RX (0x104) -+#define MAC_RX_MAX_SIZE (0x3FFF0000) -+#define MAC_RX_MAX_SIZE_SHIFT (16) -+#define MAC_RX_FCS_STRIP (0x00000010) -+#define MAC_RX_FSE (0x00000004) -+#define MAC_RX_RXD (0x00000002) -+#define MAC_RX_RXEN (0x00000001) -+ -+#define MAC_TX (0x108) -+#define MAC_TX_BFCS (0x00000004) -+#define MAC_TX_TXD (0x00000002) -+#define MAC_TX_TXEN (0x00000001) -+ -+#define FLOW (0x10C) -+#define FLOW_FORCE_FC (0x80000000) -+#define FLOW_TX_FCEN (0x40000000) -+#define FLOW_RX_FCEN (0x20000000) -+#define FLOW_FPF (0x10000000) -+#define FLOW_PAUSE_TIME (0x0000FFFF) -+ -+#define RAND_SEED (0x110) -+#define RAND_SEED_MASK (0x0000FFFF) -+ -+#define ERR_STS (0x114) -+#define ERR_STS_FCS_ERR (0x00000100) -+#define ERR_STS_LFRM_ERR (0x00000080) -+#define ERR_STS_RUNT_ERR (0x00000040) -+#define ERR_STS_COLLISION_ERR (0x00000010) -+#define ERR_STS_ALIGN_ERR (0x00000008) -+#define ERR_STS_URUN_ERR (0x00000004) -+ -+#define RX_ADDRH (0x118) -+#define RX_ADDRH_MASK (0x0000FFFF) -+ -+#define RX_ADDRL (0x11C) -+ -+#define MII_ACCESS (0x120) -+#define MII_ACCESS_PHY_ADDR (0x0000F800) -+#define MII_ACCESS_PHY_ADDR_SHIFT (11) -+#define MII_ACCESS_REG_ADDR (0x000007C0) -+#define MII_ACCESS_REG_ADDR_SHIFT (6) -+#define MII_ACCESS_READ (0x00000000) -+#define MII_ACCESS_WRITE (0x00000002) -+#define MII_ACCESS_BUSY (0x00000001) -+ -+#define MII_DATA (0x124) -+#define MII_DATA_MASK (0x0000FFFF) -+ -+#define WUCSR (0x140) -+#define WUCSR_PFDA_FR (0x00000080) -+#define WUCSR_WUFR (0x00000040) -+#define WUCSR_MPR (0x00000020) -+#define WUCSR_BCAST_FR (0x00000010) -+#define WUCSR_PFDA_EN (0x00000008) -+#define WUCSR_WUEN (0x00000004) -+#define WUCSR_MPEN (0x00000002) -+#define WUCSR_BCST_EN (0x00000001) -+ -+#define WUF_CFGX (0x144) -+#define WUF_CFGX_EN (0x80000000) -+#define WUF_CFGX_ATYPE (0x03000000) -+#define WUF_CFGX_ATYPE_UNICAST (0x00000000) -+#define WUF_CFGX_ATYPE_MULTICAST (0x02000000) -+#define WUF_CFGX_ATYPE_ALL (0x03000000) -+#define WUF_CFGX_PATTERN_OFFSET (0x007F0000) -+#define WUF_CFGX_PATTERN_OFFSET_SHIFT (16) -+#define WUF_CFGX_CRC16 (0x0000FFFF) -+#define WUF_NUM (8) -+ -+#define WUF_MASKX (0x170) -+#define WUF_MASKX_AVALID (0x80000000) -+#define WUF_MASKX_ATYPE (0x40000000) -+ -+#define ADDR_FILTX (0x300) -+#define ADDR_FILTX_FB_VALID (0x80000000) -+#define ADDR_FILTX_FB_TYPE (0x40000000) -+#define ADDR_FILTX_FB_ADDRHI (0x0000FFFF) -+#define ADDR_FILTX_SB_ADDRLO (0xFFFFFFFF) -+ -+#define WUCSR2 (0x500) -+#define WUCSR2_NS_RCD (0x00000040) -+#define WUCSR2_ARP_RCD (0x00000020) -+#define WUCSR2_TCPSYN_RCD (0x00000010) -+#define WUCSR2_NS_OFFLOAD (0x00000004) -+#define WUCSR2_ARP_OFFLOAD (0x00000002) -+#define WUCSR2_TCPSYN_OFFLOAD (0x00000001) -+ -+#define WOL_FIFO_STS (0x504) -+ -+#define IPV6_ADDRX (0x510) -+ -+#define IPV4_ADDRX (0x590) -+ -+ -+/* Vendor-specific PHY Definitions */ -+ -+/* Mode Control/Status Register */ -+#define PHY_MODE_CTRL_STS (17) -+#define MODE_CTRL_STS_EDPWRDOWN ((u16)0x2000) -+#define MODE_CTRL_STS_ENERGYON ((u16)0x0002) -+ -+#define PHY_INT_SRC (29) -+#define PHY_INT_SRC_ENERGY_ON ((u16)0x0080) -+#define PHY_INT_SRC_ANEG_COMP ((u16)0x0040) -+#define PHY_INT_SRC_REMOTE_FAULT ((u16)0x0020) -+#define PHY_INT_SRC_LINK_DOWN ((u16)0x0010) -+#define PHY_INT_SRC_CLEAR_ALL ((u16)0xffff) -+ -+#define PHY_INT_MASK (30) -+#define PHY_INT_MASK_ENERGY_ON ((u16)0x0080) -+#define PHY_INT_MASK_ANEG_COMP ((u16)0x0040) -+#define PHY_INT_MASK_REMOTE_FAULT ((u16)0x0020) -+#define PHY_INT_MASK_LINK_DOWN ((u16)0x0010) -+#define PHY_INT_MASK_DEFAULT (PHY_INT_MASK_ANEG_COMP | \ -+ PHY_INT_MASK_LINK_DOWN) -+ -+#define PHY_SPECIAL (31) -+#define PHY_SPECIAL_SPD ((u16)0x001C) -+#define PHY_SPECIAL_SPD_10HALF ((u16)0x0004) -+#define PHY_SPECIAL_SPD_10FULL ((u16)0x0014) -+#define PHY_SPECIAL_SPD_100HALF ((u16)0x0008) -+#define PHY_SPECIAL_SPD_100FULL ((u16)0x0018) -+ -+/* USB Vendor Requests */ -+#define USB_VENDOR_REQUEST_WRITE_REGISTER 0xA0 -+#define USB_VENDOR_REQUEST_READ_REGISTER 0xA1 -+#define USB_VENDOR_REQUEST_GET_STATS 0xA2 -+ -+/* Interrupt Endpoint status word bitfields */ -+#define INT_ENP_RDFO_INT ((u32)BIT(22)) -+#define INT_ENP_TXE_INT ((u32)BIT(21)) -+#define INT_ENP_TX_DIS_INT ((u32)BIT(19)) -+#define INT_ENP_RX_DIS_INT ((u32)BIT(18)) -+#define INT_ENP_PHY_INT ((u32)BIT(17)) -+#define INT_ENP_MAC_ERR_INT ((u32)BIT(15)) -+#define INT_ENP_RX_FIFO_DATA_INT ((u32)BIT(12)) -+ -+#endif /* _SMSC75XX_H */ -diff -Naur backports-4.2.6-1.org/drivers/net/usb/smsc95xx.c backports-4.2.6-1/drivers/net/usb/smsc95xx.c ---- backports-4.2.6-1.org/drivers/net/usb/smsc95xx.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/smsc95xx.c 2016-06-28 14:35:18.011973884 +0200 -@@ -0,0 +1,2032 @@ -+ /*************************************************************************** -+ * -+ * Copyright (C) 2007-2008 SMSC -+ * -+ * 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, see . -+ * -+ *****************************************************************************/ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "smsc95xx.h" -+ -+#define SMSC_CHIPNAME "smsc95xx" -+#define SMSC_DRIVER_VERSION "1.0.4" -+#define HS_USB_PKT_SIZE (512) -+#define FS_USB_PKT_SIZE (64) -+#define DEFAULT_HS_BURST_CAP_SIZE (16 * 1024 + 5 * HS_USB_PKT_SIZE) -+#define DEFAULT_FS_BURST_CAP_SIZE (6 * 1024 + 33 * FS_USB_PKT_SIZE) -+#define DEFAULT_BULK_IN_DELAY (0x00002000) -+#define MAX_SINGLE_PACKET_SIZE (2048) -+#define LAN95XX_EEPROM_MAGIC (0x9500) -+#define EEPROM_MAC_OFFSET (0x01) -+#define DEFAULT_TX_CSUM_ENABLE (true) -+#define DEFAULT_RX_CSUM_ENABLE (true) -+#define SMSC95XX_INTERNAL_PHY_ID (1) -+#define SMSC95XX_TX_OVERHEAD (8) -+#define SMSC95XX_TX_OVERHEAD_CSUM (12) -+#define SUPPORTED_WAKE (WAKE_PHY | WAKE_UCAST | WAKE_BCAST | \ -+ WAKE_MCAST | WAKE_ARP | WAKE_MAGIC) -+ -+#define FEATURE_8_WAKEUP_FILTERS (0x01) -+#define FEATURE_PHY_NLP_CROSSOVER (0x02) -+#define FEATURE_REMOTE_WAKEUP (0x04) -+ -+#define SUSPEND_SUSPEND0 (0x01) -+#define SUSPEND_SUSPEND1 (0x02) -+#define SUSPEND_SUSPEND2 (0x04) -+#define SUSPEND_SUSPEND3 (0x08) -+#define SUSPEND_ALLMODES (SUSPEND_SUSPEND0 | SUSPEND_SUSPEND1 | \ -+ SUSPEND_SUSPEND2 | SUSPEND_SUSPEND3) -+ -+struct smsc95xx_priv { -+ u32 mac_cr; -+ u32 hash_hi; -+ u32 hash_lo; -+ u32 wolopts; -+ spinlock_t mac_cr_lock; -+ u8 features; -+ u8 suspend_flags; -+}; -+ -+static bool turbo_mode = true; -+module_param(turbo_mode, bool, 0644); -+MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction"); -+ -+static int __must_check __smsc95xx_read_reg(struct usbnet *dev, u32 index, -+ u32 *data, int in_pm) -+{ -+ u32 buf; -+ int ret; -+ int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16); -+ -+ BUG_ON(!dev); -+ -+ if (!in_pm) -+ fn = usbnet_read_cmd; -+ else -+ fn = usbnet_read_cmd_nopm; -+ -+ ret = fn(dev, USB_VENDOR_REQUEST_READ_REGISTER, USB_DIR_IN -+ | USB_TYPE_VENDOR | USB_RECIP_DEVICE, -+ 0, index, &buf, 4); -+ if (unlikely(ret < 0)) -+ netdev_warn(dev->net, "Failed to read reg index 0x%08x: %d\n", -+ index, ret); -+ -+ le32_to_cpus(&buf); -+ *data = buf; -+ -+ return ret; -+} -+ -+static int __must_check __smsc95xx_write_reg(struct usbnet *dev, u32 index, -+ u32 data, int in_pm) -+{ -+ u32 buf; -+ int ret; -+ int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16); -+ -+ BUG_ON(!dev); -+ -+ if (!in_pm) -+ fn = usbnet_write_cmd; -+ else -+ fn = usbnet_write_cmd_nopm; -+ -+ buf = data; -+ cpu_to_le32s(&buf); -+ -+ ret = fn(dev, USB_VENDOR_REQUEST_WRITE_REGISTER, USB_DIR_OUT -+ | USB_TYPE_VENDOR | USB_RECIP_DEVICE, -+ 0, index, &buf, 4); -+ if (unlikely(ret < 0)) -+ netdev_warn(dev->net, "Failed to write reg index 0x%08x: %d\n", -+ index, ret); -+ -+ return ret; -+} -+ -+static int __must_check smsc95xx_read_reg_nopm(struct usbnet *dev, u32 index, -+ u32 *data) -+{ -+ return __smsc95xx_read_reg(dev, index, data, 1); -+} -+ -+static int __must_check smsc95xx_write_reg_nopm(struct usbnet *dev, u32 index, -+ u32 data) -+{ -+ return __smsc95xx_write_reg(dev, index, data, 1); -+} -+ -+static int __must_check smsc95xx_read_reg(struct usbnet *dev, u32 index, -+ u32 *data) -+{ -+ return __smsc95xx_read_reg(dev, index, data, 0); -+} -+ -+static int __must_check smsc95xx_write_reg(struct usbnet *dev, u32 index, -+ u32 data) -+{ -+ return __smsc95xx_write_reg(dev, index, data, 0); -+} -+ -+/* Loop until the read is completed with timeout -+ * called with phy_mutex held */ -+static int __must_check __smsc95xx_phy_wait_not_busy(struct usbnet *dev, -+ int in_pm) -+{ -+ unsigned long start_time = jiffies; -+ u32 val; -+ int ret; -+ -+ do { -+ ret = __smsc95xx_read_reg(dev, MII_ADDR, &val, in_pm); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading MII_ACCESS\n"); -+ return ret; -+ } -+ -+ if (!(val & MII_BUSY_)) -+ return 0; -+ } while (!time_after(jiffies, start_time + HZ)); -+ -+ return -EIO; -+} -+ -+static int __smsc95xx_mdio_read(struct net_device *netdev, int phy_id, int idx, -+ int in_pm) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ u32 val, addr; -+ int ret; -+ -+ mutex_lock(&dev->phy_mutex); -+ -+ /* confirm MII not busy */ -+ ret = __smsc95xx_phy_wait_not_busy(dev, in_pm); -+ if (ret < 0) { -+ netdev_warn(dev->net, "MII is busy in smsc95xx_mdio_read\n"); -+ goto done; -+ } -+ -+ /* set the address, index & direction (read from PHY) */ -+ phy_id &= dev->mii.phy_id_mask; -+ idx &= dev->mii.reg_num_mask; -+ addr = (phy_id << 11) | (idx << 6) | MII_READ_ | MII_BUSY_; -+ ret = __smsc95xx_write_reg(dev, MII_ADDR, addr, in_pm); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing MII_ADDR\n"); -+ goto done; -+ } -+ -+ ret = __smsc95xx_phy_wait_not_busy(dev, in_pm); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Timed out reading MII reg %02X\n", idx); -+ goto done; -+ } -+ -+ ret = __smsc95xx_read_reg(dev, MII_DATA, &val, in_pm); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading MII_DATA\n"); -+ goto done; -+ } -+ -+ ret = (u16)(val & 0xFFFF); -+ -+done: -+ mutex_unlock(&dev->phy_mutex); -+ return ret; -+} -+ -+static void __smsc95xx_mdio_write(struct net_device *netdev, int phy_id, -+ int idx, int regval, int in_pm) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ u32 val, addr; -+ int ret; -+ -+ mutex_lock(&dev->phy_mutex); -+ -+ /* confirm MII not busy */ -+ ret = __smsc95xx_phy_wait_not_busy(dev, in_pm); -+ if (ret < 0) { -+ netdev_warn(dev->net, "MII is busy in smsc95xx_mdio_write\n"); -+ goto done; -+ } -+ -+ val = regval; -+ ret = __smsc95xx_write_reg(dev, MII_DATA, val, in_pm); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing MII_DATA\n"); -+ goto done; -+ } -+ -+ /* set the address, index & direction (write to PHY) */ -+ phy_id &= dev->mii.phy_id_mask; -+ idx &= dev->mii.reg_num_mask; -+ addr = (phy_id << 11) | (idx << 6) | MII_WRITE_ | MII_BUSY_; -+ ret = __smsc95xx_write_reg(dev, MII_ADDR, addr, in_pm); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing MII_ADDR\n"); -+ goto done; -+ } -+ -+ ret = __smsc95xx_phy_wait_not_busy(dev, in_pm); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Timed out writing MII reg %02X\n", idx); -+ goto done; -+ } -+ -+done: -+ mutex_unlock(&dev->phy_mutex); -+} -+ -+static int smsc95xx_mdio_read_nopm(struct net_device *netdev, int phy_id, -+ int idx) -+{ -+ return __smsc95xx_mdio_read(netdev, phy_id, idx, 1); -+} -+ -+static void smsc95xx_mdio_write_nopm(struct net_device *netdev, int phy_id, -+ int idx, int regval) -+{ -+ __smsc95xx_mdio_write(netdev, phy_id, idx, regval, 1); -+} -+ -+static int smsc95xx_mdio_read(struct net_device *netdev, int phy_id, int idx) -+{ -+ return __smsc95xx_mdio_read(netdev, phy_id, idx, 0); -+} -+ -+static void smsc95xx_mdio_write(struct net_device *netdev, int phy_id, int idx, -+ int regval) -+{ -+ __smsc95xx_mdio_write(netdev, phy_id, idx, regval, 0); -+} -+ -+static int __must_check smsc95xx_wait_eeprom(struct usbnet *dev) -+{ -+ unsigned long start_time = jiffies; -+ u32 val; -+ int ret; -+ -+ do { -+ ret = smsc95xx_read_reg(dev, E2P_CMD, &val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading E2P_CMD\n"); -+ return ret; -+ } -+ -+ if (!(val & E2P_CMD_BUSY_) || (val & E2P_CMD_TIMEOUT_)) -+ break; -+ udelay(40); -+ } while (!time_after(jiffies, start_time + HZ)); -+ -+ if (val & (E2P_CMD_TIMEOUT_ | E2P_CMD_BUSY_)) { -+ netdev_warn(dev->net, "EEPROM read operation timeout\n"); -+ return -EIO; -+ } -+ -+ return 0; -+} -+ -+static int __must_check smsc95xx_eeprom_confirm_not_busy(struct usbnet *dev) -+{ -+ unsigned long start_time = jiffies; -+ u32 val; -+ int ret; -+ -+ do { -+ ret = smsc95xx_read_reg(dev, E2P_CMD, &val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading E2P_CMD\n"); -+ return ret; -+ } -+ -+ if (!(val & E2P_CMD_BUSY_)) -+ return 0; -+ -+ udelay(40); -+ } while (!time_after(jiffies, start_time + HZ)); -+ -+ netdev_warn(dev->net, "EEPROM is busy\n"); -+ return -EIO; -+} -+ -+static int smsc95xx_read_eeprom(struct usbnet *dev, u32 offset, u32 length, -+ u8 *data) -+{ -+ u32 val; -+ int i, ret; -+ -+ BUG_ON(!dev); -+ BUG_ON(!data); -+ -+ ret = smsc95xx_eeprom_confirm_not_busy(dev); -+ if (ret) -+ return ret; -+ -+ for (i = 0; i < length; i++) { -+ val = E2P_CMD_BUSY_ | E2P_CMD_READ_ | (offset & E2P_CMD_ADDR_); -+ ret = smsc95xx_write_reg(dev, E2P_CMD, val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing E2P_CMD\n"); -+ return ret; -+ } -+ -+ ret = smsc95xx_wait_eeprom(dev); -+ if (ret < 0) -+ return ret; -+ -+ ret = smsc95xx_read_reg(dev, E2P_DATA, &val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error reading E2P_DATA\n"); -+ return ret; -+ } -+ -+ data[i] = val & 0xFF; -+ offset++; -+ } -+ -+ return 0; -+} -+ -+static int smsc95xx_write_eeprom(struct usbnet *dev, u32 offset, u32 length, -+ u8 *data) -+{ -+ u32 val; -+ int i, ret; -+ -+ BUG_ON(!dev); -+ BUG_ON(!data); -+ -+ ret = smsc95xx_eeprom_confirm_not_busy(dev); -+ if (ret) -+ return ret; -+ -+ /* Issue write/erase enable command */ -+ val = E2P_CMD_BUSY_ | E2P_CMD_EWEN_; -+ ret = smsc95xx_write_reg(dev, E2P_CMD, val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing E2P_DATA\n"); -+ return ret; -+ } -+ -+ ret = smsc95xx_wait_eeprom(dev); -+ if (ret < 0) -+ return ret; -+ -+ for (i = 0; i < length; i++) { -+ -+ /* Fill data register */ -+ val = data[i]; -+ ret = smsc95xx_write_reg(dev, E2P_DATA, val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing E2P_DATA\n"); -+ return ret; -+ } -+ -+ /* Send "write" command */ -+ val = E2P_CMD_BUSY_ | E2P_CMD_WRITE_ | (offset & E2P_CMD_ADDR_); -+ ret = smsc95xx_write_reg(dev, E2P_CMD, val); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Error writing E2P_CMD\n"); -+ return ret; -+ } -+ -+ ret = smsc95xx_wait_eeprom(dev); -+ if (ret < 0) -+ return ret; -+ -+ offset++; -+ } -+ -+ return 0; -+} -+ -+static int __must_check smsc95xx_write_reg_async(struct usbnet *dev, u16 index, -+ u32 data) -+{ -+ const u16 size = 4; -+ u32 buf; -+ int ret; -+ -+ buf = data; -+ cpu_to_le32s(&buf); -+ -+ ret = usbnet_write_cmd_async(dev, USB_VENDOR_REQUEST_WRITE_REGISTER, -+ USB_DIR_OUT | USB_TYPE_VENDOR | -+ USB_RECIP_DEVICE, -+ 0, index, &buf, size); -+ if (ret < 0) -+ netdev_warn(dev->net, "Error write async cmd, sts=%d\n", -+ ret); -+ return ret; -+} -+ -+/* returns hash bit number for given MAC address -+ * example: -+ * 01 00 5E 00 00 01 -> returns bit number 31 */ -+static unsigned int smsc95xx_hash(char addr[ETH_ALEN]) -+{ -+ return (ether_crc(ETH_ALEN, addr) >> 26) & 0x3f; -+} -+ -+static void smsc95xx_set_multicast(struct net_device *netdev) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); -+ unsigned long flags; -+ int ret; -+ -+ pdata->hash_hi = 0; -+ pdata->hash_lo = 0; -+ -+ spin_lock_irqsave(&pdata->mac_cr_lock, flags); -+ -+ if (dev->net->flags & IFF_PROMISC) { -+ netif_dbg(dev, drv, dev->net, "promiscuous mode enabled\n"); -+ pdata->mac_cr |= MAC_CR_PRMS_; -+ pdata->mac_cr &= ~(MAC_CR_MCPAS_ | MAC_CR_HPFILT_); -+ } else if (dev->net->flags & IFF_ALLMULTI) { -+ netif_dbg(dev, drv, dev->net, "receive all multicast enabled\n"); -+ pdata->mac_cr |= MAC_CR_MCPAS_; -+ pdata->mac_cr &= ~(MAC_CR_PRMS_ | MAC_CR_HPFILT_); -+ } else if (!netdev_mc_empty(dev->net)) { -+ struct netdev_hw_addr *ha; -+ -+ pdata->mac_cr |= MAC_CR_HPFILT_; -+ pdata->mac_cr &= ~(MAC_CR_PRMS_ | MAC_CR_MCPAS_); -+ -+ netdev_for_each_mc_addr(ha, netdev) { -+ u32 bitnum = smsc95xx_hash(ha->addr); -+ u32 mask = 0x01 << (bitnum & 0x1F); -+ if (bitnum & 0x20) -+ pdata->hash_hi |= mask; -+ else -+ pdata->hash_lo |= mask; -+ } -+ -+ netif_dbg(dev, drv, dev->net, "HASHH=0x%08X, HASHL=0x%08X\n", -+ pdata->hash_hi, pdata->hash_lo); -+ } else { -+ netif_dbg(dev, drv, dev->net, "receive own packets only\n"); -+ pdata->mac_cr &= -+ ~(MAC_CR_PRMS_ | MAC_CR_MCPAS_ | MAC_CR_HPFILT_); -+ } -+ -+ spin_unlock_irqrestore(&pdata->mac_cr_lock, flags); -+ -+ /* Initiate async writes, as we can't wait for completion here */ -+ ret = smsc95xx_write_reg_async(dev, HASHH, pdata->hash_hi); -+ if (ret < 0) -+ netdev_warn(dev->net, "failed to initiate async write to HASHH\n"); -+ -+ ret = smsc95xx_write_reg_async(dev, HASHL, pdata->hash_lo); -+ if (ret < 0) -+ netdev_warn(dev->net, "failed to initiate async write to HASHL\n"); -+ -+ ret = smsc95xx_write_reg_async(dev, MAC_CR, pdata->mac_cr); -+ if (ret < 0) -+ netdev_warn(dev->net, "failed to initiate async write to MAC_CR\n"); -+} -+ -+static int smsc95xx_phy_update_flowcontrol(struct usbnet *dev, u8 duplex, -+ u16 lcladv, u16 rmtadv) -+{ -+ u32 flow, afc_cfg = 0; -+ -+ int ret = smsc95xx_read_reg(dev, AFC_CFG, &afc_cfg); -+ if (ret < 0) -+ return ret; -+ -+ if (duplex == DUPLEX_FULL) { -+ u8 cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv); -+ -+ if (cap & FLOW_CTRL_RX) -+ flow = 0xFFFF0002; -+ else -+ flow = 0; -+ -+ if (cap & FLOW_CTRL_TX) -+ afc_cfg |= 0xF; -+ else -+ afc_cfg &= ~0xF; -+ -+ netif_dbg(dev, link, dev->net, "rx pause %s, tx pause %s\n", -+ cap & FLOW_CTRL_RX ? "enabled" : "disabled", -+ cap & FLOW_CTRL_TX ? "enabled" : "disabled"); -+ } else { -+ netif_dbg(dev, link, dev->net, "half duplex\n"); -+ flow = 0; -+ afc_cfg |= 0xF; -+ } -+ -+ ret = smsc95xx_write_reg(dev, FLOW, flow); -+ if (ret < 0) -+ return ret; -+ -+ return smsc95xx_write_reg(dev, AFC_CFG, afc_cfg); -+} -+ -+static int smsc95xx_link_reset(struct usbnet *dev) -+{ -+ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); -+ struct mii_if_info *mii = &dev->mii; -+ struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET }; -+ unsigned long flags; -+ u16 lcladv, rmtadv; -+ int ret; -+ -+ /* clear interrupt status */ -+ ret = smsc95xx_mdio_read(dev->net, mii->phy_id, PHY_INT_SRC); -+ if (ret < 0) -+ return ret; -+ -+ ret = smsc95xx_write_reg(dev, INT_STS, INT_STS_CLEAR_ALL_); -+ if (ret < 0) -+ return ret; -+ -+ mii_check_media(mii, 1, 1); -+ mii_ethtool_gset(&dev->mii, &ecmd); -+ lcladv = smsc95xx_mdio_read(dev->net, mii->phy_id, MII_ADVERTISE); -+ rmtadv = smsc95xx_mdio_read(dev->net, mii->phy_id, MII_LPA); -+ -+ netif_dbg(dev, link, dev->net, -+ "speed: %u duplex: %d lcladv: %04x rmtadv: %04x\n", -+ ethtool_cmd_speed(&ecmd), ecmd.duplex, lcladv, rmtadv); -+ -+ spin_lock_irqsave(&pdata->mac_cr_lock, flags); -+ if (ecmd.duplex != DUPLEX_FULL) { -+ pdata->mac_cr &= ~MAC_CR_FDPX_; -+ pdata->mac_cr |= MAC_CR_RCVOWN_; -+ } else { -+ pdata->mac_cr &= ~MAC_CR_RCVOWN_; -+ pdata->mac_cr |= MAC_CR_FDPX_; -+ } -+ spin_unlock_irqrestore(&pdata->mac_cr_lock, flags); -+ -+ ret = smsc95xx_write_reg(dev, MAC_CR, pdata->mac_cr); -+ if (ret < 0) -+ return ret; -+ -+ ret = smsc95xx_phy_update_flowcontrol(dev, ecmd.duplex, lcladv, rmtadv); -+ if (ret < 0) -+ netdev_warn(dev->net, "Error updating PHY flow control\n"); -+ -+ return ret; -+} -+ -+static void smsc95xx_status(struct usbnet *dev, struct urb *urb) -+{ -+ u32 intdata; -+ -+ if (urb->actual_length != 4) { -+ netdev_warn(dev->net, "unexpected urb length %d\n", -+ urb->actual_length); -+ return; -+ } -+ -+ memcpy(&intdata, urb->transfer_buffer, 4); -+ le32_to_cpus(&intdata); -+ -+ netif_dbg(dev, link, dev->net, "intdata: 0x%08X\n", intdata); -+ -+ if (intdata & INT_ENP_PHY_INT_) -+ usbnet_defer_kevent(dev, EVENT_LINK_RESET); -+ else -+ netdev_warn(dev->net, "unexpected interrupt, intdata=0x%08X\n", -+ intdata); -+} -+ -+/* Enable or disable Tx & Rx checksum offload engines */ -+static int smsc95xx_set_features(struct net_device *netdev, -+ netdev_features_t features) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ u32 read_buf; -+ int ret; -+ -+ ret = smsc95xx_read_reg(dev, COE_CR, &read_buf); -+ if (ret < 0) -+ return ret; -+ -+ if (features & NETIF_F_HW_CSUM) -+ read_buf |= Tx_COE_EN_; -+ else -+ read_buf &= ~Tx_COE_EN_; -+ -+ if (features & NETIF_F_RXCSUM) -+ read_buf |= Rx_COE_EN_; -+ else -+ read_buf &= ~Rx_COE_EN_; -+ -+ ret = smsc95xx_write_reg(dev, COE_CR, read_buf); -+ if (ret < 0) -+ return ret; -+ -+ netif_dbg(dev, hw, dev->net, "COE_CR = 0x%08x\n", read_buf); -+ return 0; -+} -+ -+static int smsc95xx_ethtool_get_eeprom_len(struct net_device *net) -+{ -+ return MAX_EEPROM_SIZE; -+} -+ -+static int smsc95xx_ethtool_get_eeprom(struct net_device *netdev, -+ struct ethtool_eeprom *ee, u8 *data) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ -+ ee->magic = LAN95XX_EEPROM_MAGIC; -+ -+ return smsc95xx_read_eeprom(dev, ee->offset, ee->len, data); -+} -+ -+static int smsc95xx_ethtool_set_eeprom(struct net_device *netdev, -+ struct ethtool_eeprom *ee, u8 *data) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ -+ if (ee->magic != LAN95XX_EEPROM_MAGIC) { -+ netdev_warn(dev->net, "EEPROM: magic value mismatch, magic = 0x%x\n", -+ ee->magic); -+ return -EINVAL; -+ } -+ -+ return smsc95xx_write_eeprom(dev, ee->offset, ee->len, data); -+} -+ -+static int smsc95xx_ethtool_getregslen(struct net_device *netdev) -+{ -+ /* all smsc95xx registers */ -+ return COE_CR - ID_REV + sizeof(u32); -+} -+ -+static void -+smsc95xx_ethtool_getregs(struct net_device *netdev, struct ethtool_regs *regs, -+ void *buf) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ unsigned int i, j; -+ int retval; -+ u32 *data = buf; -+ -+ retval = smsc95xx_read_reg(dev, ID_REV, ®s->version); -+ if (retval < 0) { -+ netdev_warn(netdev, "REGS: cannot read ID_REV\n"); -+ return; -+ } -+ -+ for (i = ID_REV, j = 0; i <= COE_CR; i += (sizeof(u32)), j++) { -+ retval = smsc95xx_read_reg(dev, i, &data[j]); -+ if (retval < 0) { -+ netdev_warn(netdev, "REGS: cannot read reg[%x]\n", i); -+ return; -+ } -+ } -+} -+ -+static void smsc95xx_ethtool_get_wol(struct net_device *net, -+ struct ethtool_wolinfo *wolinfo) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); -+ -+ wolinfo->supported = SUPPORTED_WAKE; -+ wolinfo->wolopts = pdata->wolopts; -+} -+ -+static int smsc95xx_ethtool_set_wol(struct net_device *net, -+ struct ethtool_wolinfo *wolinfo) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); -+ int ret; -+ -+ pdata->wolopts = wolinfo->wolopts & SUPPORTED_WAKE; -+ -+ ret = device_set_wakeup_enable(&dev->udev->dev, pdata->wolopts); -+ if (ret < 0) -+ netdev_warn(dev->net, "device_set_wakeup_enable error %d\n", ret); -+ -+ return ret; -+} -+ -+static const struct ethtool_ops smsc95xx_ethtool_ops = { -+ .get_link = usbnet_get_link, -+ .nway_reset = usbnet_nway_reset, -+ .get_drvinfo = usbnet_get_drvinfo, -+ .get_msglevel = usbnet_get_msglevel, -+ .set_msglevel = usbnet_set_msglevel, -+ .get_settings = usbnet_get_settings, -+ .set_settings = usbnet_set_settings, -+ .get_eeprom_len = smsc95xx_ethtool_get_eeprom_len, -+ .get_eeprom = smsc95xx_ethtool_get_eeprom, -+ .set_eeprom = smsc95xx_ethtool_set_eeprom, -+ .get_regs_len = smsc95xx_ethtool_getregslen, -+ .get_regs = smsc95xx_ethtool_getregs, -+ .get_wol = smsc95xx_ethtool_get_wol, -+ .set_wol = smsc95xx_ethtool_set_wol, -+}; -+ -+static int smsc95xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ -+ if (!netif_running(netdev)) -+ return -EINVAL; -+ -+ return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); -+} -+ -+static void smsc95xx_init_mac_address(struct usbnet *dev) -+{ -+ /* try reading mac address from EEPROM */ -+ if (smsc95xx_read_eeprom(dev, EEPROM_MAC_OFFSET, ETH_ALEN, -+ dev->net->dev_addr) == 0) { -+ if (is_valid_ether_addr(dev->net->dev_addr)) { -+ /* eeprom values are valid so use them */ -+ netif_dbg(dev, ifup, dev->net, "MAC address read from EEPROM\n"); -+ return; -+ } -+ } -+ -+ /* no eeprom, or eeprom values are invalid. generate random MAC */ -+ eth_hw_addr_random(dev->net); -+ netif_dbg(dev, ifup, dev->net, "MAC address set to eth_random_addr\n"); -+} -+ -+static int smsc95xx_set_mac_address(struct usbnet *dev) -+{ -+ u32 addr_lo = dev->net->dev_addr[0] | dev->net->dev_addr[1] << 8 | -+ dev->net->dev_addr[2] << 16 | dev->net->dev_addr[3] << 24; -+ u32 addr_hi = dev->net->dev_addr[4] | dev->net->dev_addr[5] << 8; -+ int ret; -+ -+ ret = smsc95xx_write_reg(dev, ADDRL, addr_lo); -+ if (ret < 0) -+ return ret; -+ -+ return smsc95xx_write_reg(dev, ADDRH, addr_hi); -+} -+ -+/* starts the TX path */ -+static int smsc95xx_start_tx_path(struct usbnet *dev) -+{ -+ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); -+ unsigned long flags; -+ int ret; -+ -+ /* Enable Tx at MAC */ -+ spin_lock_irqsave(&pdata->mac_cr_lock, flags); -+ pdata->mac_cr |= MAC_CR_TXEN_; -+ spin_unlock_irqrestore(&pdata->mac_cr_lock, flags); -+ -+ ret = smsc95xx_write_reg(dev, MAC_CR, pdata->mac_cr); -+ if (ret < 0) -+ return ret; -+ -+ /* Enable Tx at SCSRs */ -+ return smsc95xx_write_reg(dev, TX_CFG, TX_CFG_ON_); -+} -+ -+/* Starts the Receive path */ -+static int smsc95xx_start_rx_path(struct usbnet *dev, int in_pm) -+{ -+ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); -+ unsigned long flags; -+ -+ spin_lock_irqsave(&pdata->mac_cr_lock, flags); -+ pdata->mac_cr |= MAC_CR_RXEN_; -+ spin_unlock_irqrestore(&pdata->mac_cr_lock, flags); -+ -+ return __smsc95xx_write_reg(dev, MAC_CR, pdata->mac_cr, in_pm); -+} -+ -+static int smsc95xx_phy_initialize(struct usbnet *dev) -+{ -+ int bmcr, ret, timeout = 0; -+ -+ /* Initialize MII structure */ -+ dev->mii.dev = dev->net; -+ dev->mii.mdio_read = smsc95xx_mdio_read; -+ dev->mii.mdio_write = smsc95xx_mdio_write; -+ dev->mii.phy_id_mask = 0x1f; -+ dev->mii.reg_num_mask = 0x1f; -+ dev->mii.phy_id = SMSC95XX_INTERNAL_PHY_ID; -+ -+ /* reset phy and wait for reset to complete */ -+ smsc95xx_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET); -+ -+ do { -+ msleep(10); -+ bmcr = smsc95xx_mdio_read(dev->net, dev->mii.phy_id, MII_BMCR); -+ timeout++; -+ } while ((bmcr & BMCR_RESET) && (timeout < 100)); -+ -+ if (timeout >= 100) { -+ netdev_warn(dev->net, "timeout on PHY Reset"); -+ return -EIO; -+ } -+ -+ smsc95xx_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, -+ ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP | -+ ADVERTISE_PAUSE_ASYM); -+ -+ /* read to clear */ -+ ret = smsc95xx_mdio_read(dev->net, dev->mii.phy_id, PHY_INT_SRC); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to read PHY_INT_SRC during init\n"); -+ return ret; -+ } -+ -+ smsc95xx_mdio_write(dev->net, dev->mii.phy_id, PHY_INT_MASK, -+ PHY_INT_MASK_DEFAULT_); -+ mii_nway_restart(&dev->mii); -+ -+ netif_dbg(dev, ifup, dev->net, "phy initialised successfully\n"); -+ return 0; -+} -+ -+static int smsc95xx_reset(struct usbnet *dev) -+{ -+ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); -+ u32 read_buf, write_buf, burst_cap; -+ int ret = 0, timeout; -+ -+ netif_dbg(dev, ifup, dev->net, "entering smsc95xx_reset\n"); -+ -+ ret = smsc95xx_write_reg(dev, HW_CFG, HW_CFG_LRST_); -+ if (ret < 0) -+ return ret; -+ -+ timeout = 0; -+ do { -+ msleep(10); -+ ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); -+ if (ret < 0) -+ return ret; -+ timeout++; -+ } while ((read_buf & HW_CFG_LRST_) && (timeout < 100)); -+ -+ if (timeout >= 100) { -+ netdev_warn(dev->net, "timeout waiting for completion of Lite Reset\n"); -+ return ret; -+ } -+ -+ ret = smsc95xx_write_reg(dev, PM_CTRL, PM_CTL_PHY_RST_); -+ if (ret < 0) -+ return ret; -+ -+ timeout = 0; -+ do { -+ msleep(10); -+ ret = smsc95xx_read_reg(dev, PM_CTRL, &read_buf); -+ if (ret < 0) -+ return ret; -+ timeout++; -+ } while ((read_buf & PM_CTL_PHY_RST_) && (timeout < 100)); -+ -+ if (timeout >= 100) { -+ netdev_warn(dev->net, "timeout waiting for PHY Reset\n"); -+ return ret; -+ } -+ -+ ret = smsc95xx_set_mac_address(dev); -+ if (ret < 0) -+ return ret; -+ -+ netif_dbg(dev, ifup, dev->net, "MAC Address: %pM\n", -+ dev->net->dev_addr); -+ -+ ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); -+ if (ret < 0) -+ return ret; -+ -+ netif_dbg(dev, ifup, dev->net, "Read Value from HW_CFG : 0x%08x\n", -+ read_buf); -+ -+ read_buf |= HW_CFG_BIR_; -+ -+ ret = smsc95xx_write_reg(dev, HW_CFG, read_buf); -+ if (ret < 0) -+ return ret; -+ -+ ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); -+ if (ret < 0) -+ return ret; -+ -+ netif_dbg(dev, ifup, dev->net, -+ "Read Value from HW_CFG after writing HW_CFG_BIR_: 0x%08x\n", -+ read_buf); -+ -+ if (!turbo_mode) { -+ burst_cap = 0; -+ dev->rx_urb_size = MAX_SINGLE_PACKET_SIZE; -+ } else if (dev->udev->speed == USB_SPEED_HIGH) { -+ burst_cap = DEFAULT_HS_BURST_CAP_SIZE / HS_USB_PKT_SIZE; -+ dev->rx_urb_size = DEFAULT_HS_BURST_CAP_SIZE; -+ } else { -+ burst_cap = DEFAULT_FS_BURST_CAP_SIZE / FS_USB_PKT_SIZE; -+ dev->rx_urb_size = DEFAULT_FS_BURST_CAP_SIZE; -+ } -+ -+ netif_dbg(dev, ifup, dev->net, "rx_urb_size=%ld\n", -+ (ulong)dev->rx_urb_size); -+ -+ ret = smsc95xx_write_reg(dev, BURST_CAP, burst_cap); -+ if (ret < 0) -+ return ret; -+ -+ ret = smsc95xx_read_reg(dev, BURST_CAP, &read_buf); -+ if (ret < 0) -+ return ret; -+ -+ netif_dbg(dev, ifup, dev->net, -+ "Read Value from BURST_CAP after writing: 0x%08x\n", -+ read_buf); -+ -+ ret = smsc95xx_write_reg(dev, BULK_IN_DLY, DEFAULT_BULK_IN_DELAY); -+ if (ret < 0) -+ return ret; -+ -+ ret = smsc95xx_read_reg(dev, BULK_IN_DLY, &read_buf); -+ if (ret < 0) -+ return ret; -+ -+ netif_dbg(dev, ifup, dev->net, -+ "Read Value from BULK_IN_DLY after writing: 0x%08x\n", -+ read_buf); -+ -+ ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); -+ if (ret < 0) -+ return ret; -+ -+ netif_dbg(dev, ifup, dev->net, "Read Value from HW_CFG: 0x%08x\n", -+ read_buf); -+ -+ if (turbo_mode) -+ read_buf |= (HW_CFG_MEF_ | HW_CFG_BCE_); -+ -+ read_buf &= ~HW_CFG_RXDOFF_; -+ -+ /* set Rx data offset=2, Make IP header aligns on word boundary. */ -+ read_buf |= NET_IP_ALIGN << 9; -+ -+ ret = smsc95xx_write_reg(dev, HW_CFG, read_buf); -+ if (ret < 0) -+ return ret; -+ -+ ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); -+ if (ret < 0) -+ return ret; -+ -+ netif_dbg(dev, ifup, dev->net, -+ "Read Value from HW_CFG after writing: 0x%08x\n", read_buf); -+ -+ ret = smsc95xx_write_reg(dev, INT_STS, INT_STS_CLEAR_ALL_); -+ if (ret < 0) -+ return ret; -+ -+ ret = smsc95xx_read_reg(dev, ID_REV, &read_buf); -+ if (ret < 0) -+ return ret; -+ netif_dbg(dev, ifup, dev->net, "ID_REV = 0x%08x\n", read_buf); -+ -+ /* Configure GPIO pins as LED outputs */ -+ write_buf = LED_GPIO_CFG_SPD_LED | LED_GPIO_CFG_LNK_LED | -+ LED_GPIO_CFG_FDX_LED; -+ ret = smsc95xx_write_reg(dev, LED_GPIO_CFG, write_buf); -+ if (ret < 0) -+ return ret; -+ -+ /* Init Tx */ -+ ret = smsc95xx_write_reg(dev, FLOW, 0); -+ if (ret < 0) -+ return ret; -+ -+ ret = smsc95xx_write_reg(dev, AFC_CFG, AFC_CFG_DEFAULT); -+ if (ret < 0) -+ return ret; -+ -+ /* Don't need mac_cr_lock during initialisation */ -+ ret = smsc95xx_read_reg(dev, MAC_CR, &pdata->mac_cr); -+ if (ret < 0) -+ return ret; -+ -+ /* Init Rx */ -+ /* Set Vlan */ -+ ret = smsc95xx_write_reg(dev, VLAN1, (u32)ETH_P_8021Q); -+ if (ret < 0) -+ return ret; -+ -+ /* Enable or disable checksum offload engines */ -+ ret = smsc95xx_set_features(dev->net, dev->net->features); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to set checksum offload features\n"); -+ return ret; -+ } -+ -+ smsc95xx_set_multicast(dev->net); -+ -+ ret = smsc95xx_phy_initialize(dev); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to init PHY\n"); -+ return ret; -+ } -+ -+ ret = smsc95xx_read_reg(dev, INT_EP_CTL, &read_buf); -+ if (ret < 0) -+ return ret; -+ -+ /* enable PHY interrupts */ -+ read_buf |= INT_EP_CTL_PHY_INT_; -+ -+ ret = smsc95xx_write_reg(dev, INT_EP_CTL, read_buf); -+ if (ret < 0) -+ return ret; -+ -+ ret = smsc95xx_start_tx_path(dev); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to start TX path\n"); -+ return ret; -+ } -+ -+ ret = smsc95xx_start_rx_path(dev, 0); -+ if (ret < 0) { -+ netdev_warn(dev->net, "Failed to start RX path\n"); -+ return ret; -+ } -+ -+ netif_dbg(dev, ifup, dev->net, "smsc95xx_reset, return 0\n"); -+ return 0; -+} -+ -+static const struct net_device_ops smsc95xx_netdev_ops = { -+ .ndo_open = usbnet_open, -+ .ndo_stop = usbnet_stop, -+ .ndo_start_xmit = usbnet_start_xmit, -+ .ndo_tx_timeout = usbnet_tx_timeout, -+ .ndo_change_mtu = usbnet_change_mtu, -+ .ndo_set_mac_address = eth_mac_addr, -+ .ndo_validate_addr = eth_validate_addr, -+ .ndo_do_ioctl = smsc95xx_ioctl, -+ .ndo_set_rx_mode = smsc95xx_set_multicast, -+ .ndo_set_features = smsc95xx_set_features, -+}; -+ -+static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf) -+{ -+ struct smsc95xx_priv *pdata = NULL; -+ u32 val; -+ int ret; -+ -+ printk(KERN_INFO SMSC_CHIPNAME " v" SMSC_DRIVER_VERSION "\n"); -+ -+ ret = usbnet_get_endpoints(dev, intf); -+ if (ret < 0) { -+ netdev_warn(dev->net, "usbnet_get_endpoints failed: %d\n", ret); -+ return ret; -+ } -+ -+ dev->data[0] = (unsigned long)kzalloc(sizeof(struct smsc95xx_priv), -+ GFP_KERNEL); -+ -+ pdata = (struct smsc95xx_priv *)(dev->data[0]); -+ if (!pdata) -+ return -ENOMEM; -+ -+ spin_lock_init(&pdata->mac_cr_lock); -+ -+ if (DEFAULT_TX_CSUM_ENABLE) -+ dev->net->features |= NETIF_F_HW_CSUM; -+ if (DEFAULT_RX_CSUM_ENABLE) -+ dev->net->features |= NETIF_F_RXCSUM; -+ -+ dev->net->hw_features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM; -+ -+ smsc95xx_init_mac_address(dev); -+ -+ /* Init all registers */ -+ ret = smsc95xx_reset(dev); -+ -+ /* detect device revision as different features may be available */ -+ ret = smsc95xx_read_reg(dev, ID_REV, &val); -+ if (ret < 0) -+ return ret; -+ val >>= 16; -+ -+ if ((val == ID_REV_CHIP_ID_9500A_) || (val == ID_REV_CHIP_ID_9530_) || -+ (val == ID_REV_CHIP_ID_89530_) || (val == ID_REV_CHIP_ID_9730_)) -+ pdata->features = (FEATURE_8_WAKEUP_FILTERS | -+ FEATURE_PHY_NLP_CROSSOVER | -+ FEATURE_REMOTE_WAKEUP); -+ else if (val == ID_REV_CHIP_ID_9512_) -+ pdata->features = FEATURE_8_WAKEUP_FILTERS; -+ -+ dev->net->netdev_ops = &smsc95xx_netdev_ops; -+ dev->net->ethtool_ops = &smsc95xx_ethtool_ops; -+ dev->net->flags |= IFF_MULTICAST; -+ dev->net->hard_header_len += SMSC95XX_TX_OVERHEAD_CSUM; -+ dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; -+ return 0; -+} -+ -+static void smsc95xx_unbind(struct usbnet *dev, struct usb_interface *intf) -+{ -+ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); -+ if (pdata) { -+ netif_dbg(dev, ifdown, dev->net, "free pdata\n"); -+ kfree(pdata); -+ pdata = NULL; -+ dev->data[0] = 0; -+ } -+} -+ -+static u32 smsc_crc(const u8 *buffer, size_t len, int filter) -+{ -+ u32 crc = bitrev16(crc16(0xFFFF, buffer, len)); -+ return crc << ((filter % 2) * 16); -+} -+ -+static int smsc95xx_enable_phy_wakeup_interrupts(struct usbnet *dev, u16 mask) -+{ -+ struct mii_if_info *mii = &dev->mii; -+ int ret; -+ -+ netdev_dbg(dev->net, "enabling PHY wakeup interrupts\n"); -+ -+ /* read to clear */ -+ ret = smsc95xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_INT_SRC); -+ if (ret < 0) -+ return ret; -+ -+ /* enable interrupt source */ -+ ret = smsc95xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_INT_MASK); -+ if (ret < 0) -+ return ret; -+ -+ ret |= mask; -+ -+ smsc95xx_mdio_write_nopm(dev->net, mii->phy_id, PHY_INT_MASK, ret); -+ -+ return 0; -+} -+ -+static int smsc95xx_link_ok_nopm(struct usbnet *dev) -+{ -+ struct mii_if_info *mii = &dev->mii; -+ int ret; -+ -+ /* first, a dummy read, needed to latch some MII phys */ -+ ret = smsc95xx_mdio_read_nopm(dev->net, mii->phy_id, MII_BMSR); -+ if (ret < 0) -+ return ret; -+ -+ ret = smsc95xx_mdio_read_nopm(dev->net, mii->phy_id, MII_BMSR); -+ if (ret < 0) -+ return ret; -+ -+ return !!(ret & BMSR_LSTATUS); -+} -+ -+static int smsc95xx_enter_suspend0(struct usbnet *dev) -+{ -+ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); -+ u32 val; -+ int ret; -+ -+ ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val); -+ if (ret < 0) -+ return ret; -+ -+ val &= (~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_)); -+ val |= PM_CTL_SUS_MODE_0; -+ -+ ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); -+ if (ret < 0) -+ return ret; -+ -+ /* clear wol status */ -+ val &= ~PM_CTL_WUPS_; -+ val |= PM_CTL_WUPS_WOL_; -+ -+ /* enable energy detection */ -+ if (pdata->wolopts & WAKE_PHY) -+ val |= PM_CTL_WUPS_ED_; -+ -+ ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); -+ if (ret < 0) -+ return ret; -+ -+ /* read back PM_CTRL */ -+ ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val); -+ if (ret < 0) -+ return ret; -+ -+ pdata->suspend_flags |= SUSPEND_SUSPEND0; -+ -+ return 0; -+} -+ -+static int smsc95xx_enter_suspend1(struct usbnet *dev) -+{ -+ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); -+ struct mii_if_info *mii = &dev->mii; -+ u32 val; -+ int ret; -+ -+ /* reconfigure link pulse detection timing for -+ * compatibility with non-standard link partners -+ */ -+ if (pdata->features & FEATURE_PHY_NLP_CROSSOVER) -+ smsc95xx_mdio_write_nopm(dev->net, mii->phy_id, PHY_EDPD_CONFIG, -+ PHY_EDPD_CONFIG_DEFAULT); -+ -+ /* enable energy detect power-down mode */ -+ ret = smsc95xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_MODE_CTRL_STS); -+ if (ret < 0) -+ return ret; -+ -+ ret |= MODE_CTRL_STS_EDPWRDOWN_; -+ -+ smsc95xx_mdio_write_nopm(dev->net, mii->phy_id, PHY_MODE_CTRL_STS, ret); -+ -+ /* enter SUSPEND1 mode */ -+ ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val); -+ if (ret < 0) -+ return ret; -+ -+ val &= ~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_); -+ val |= PM_CTL_SUS_MODE_1; -+ -+ ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); -+ if (ret < 0) -+ return ret; -+ -+ /* clear wol status, enable energy detection */ -+ val &= ~PM_CTL_WUPS_; -+ val |= (PM_CTL_WUPS_ED_ | PM_CTL_ED_EN_); -+ -+ ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); -+ if (ret < 0) -+ return ret; -+ -+ pdata->suspend_flags |= SUSPEND_SUSPEND1; -+ -+ return 0; -+} -+ -+static int smsc95xx_enter_suspend2(struct usbnet *dev) -+{ -+ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); -+ u32 val; -+ int ret; -+ -+ ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val); -+ if (ret < 0) -+ return ret; -+ -+ val &= ~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_); -+ val |= PM_CTL_SUS_MODE_2; -+ -+ ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); -+ if (ret < 0) -+ return ret; -+ -+ pdata->suspend_flags |= SUSPEND_SUSPEND2; -+ -+ return 0; -+} -+ -+static int smsc95xx_enter_suspend3(struct usbnet *dev) -+{ -+ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); -+ u32 val; -+ int ret; -+ -+ ret = smsc95xx_read_reg_nopm(dev, RX_FIFO_INF, &val); -+ if (ret < 0) -+ return ret; -+ -+ if (val & 0xFFFF) { -+ netdev_info(dev->net, "rx fifo not empty in autosuspend\n"); -+ return -EBUSY; -+ } -+ -+ ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val); -+ if (ret < 0) -+ return ret; -+ -+ val &= ~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_); -+ val |= PM_CTL_SUS_MODE_3 | PM_CTL_RES_CLR_WKP_STS; -+ -+ ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); -+ if (ret < 0) -+ return ret; -+ -+ /* clear wol status */ -+ val &= ~PM_CTL_WUPS_; -+ val |= PM_CTL_WUPS_WOL_; -+ -+ ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); -+ if (ret < 0) -+ return ret; -+ -+ pdata->suspend_flags |= SUSPEND_SUSPEND3; -+ -+ return 0; -+} -+ -+static int smsc95xx_autosuspend(struct usbnet *dev, u32 link_up) -+{ -+ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); -+ int ret; -+ -+ if (!netif_running(dev->net)) { -+ /* interface is ifconfig down so fully power down hw */ -+ netdev_dbg(dev->net, "autosuspend entering SUSPEND2\n"); -+ return smsc95xx_enter_suspend2(dev); -+ } -+ -+ if (!link_up) { -+ /* link is down so enter EDPD mode, but only if device can -+ * reliably resume from it. This check should be redundant -+ * as current FEATURE_REMOTE_WAKEUP parts also support -+ * FEATURE_PHY_NLP_CROSSOVER but it's included for clarity */ -+ if (!(pdata->features & FEATURE_PHY_NLP_CROSSOVER)) { -+ netdev_warn(dev->net, "EDPD not supported\n"); -+ return -EBUSY; -+ } -+ -+ netdev_dbg(dev->net, "autosuspend entering SUSPEND1\n"); -+ -+ /* enable PHY wakeup events for if cable is attached */ -+ ret = smsc95xx_enable_phy_wakeup_interrupts(dev, -+ PHY_INT_MASK_ANEG_COMP_); -+ if (ret < 0) { -+ netdev_warn(dev->net, "error enabling PHY wakeup ints\n"); -+ return ret; -+ } -+ -+ netdev_info(dev->net, "entering SUSPEND1 mode\n"); -+ return smsc95xx_enter_suspend1(dev); -+ } -+ -+ /* enable PHY wakeup events so we remote wakeup if cable is pulled */ -+ ret = smsc95xx_enable_phy_wakeup_interrupts(dev, -+ PHY_INT_MASK_LINK_DOWN_); -+ if (ret < 0) { -+ netdev_warn(dev->net, "error enabling PHY wakeup ints\n"); -+ return ret; -+ } -+ -+ netdev_dbg(dev->net, "autosuspend entering SUSPEND3\n"); -+ return smsc95xx_enter_suspend3(dev); -+} -+ -+static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message) -+{ -+ struct usbnet *dev = usb_get_intfdata(intf); -+ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); -+ u32 val, link_up; -+ int ret; -+ -+ ret = usbnet_suspend(intf, message); -+ if (ret < 0) { -+ netdev_warn(dev->net, "usbnet_suspend error\n"); -+ return ret; -+ } -+ -+ if (pdata->suspend_flags) { -+ netdev_warn(dev->net, "error during last resume\n"); -+ pdata->suspend_flags = 0; -+ } -+ -+ /* determine if link is up using only _nopm functions */ -+ link_up = smsc95xx_link_ok_nopm(dev); -+ -+ if (message.event == PM_EVENT_AUTO_SUSPEND && -+ (pdata->features & FEATURE_REMOTE_WAKEUP)) { -+ ret = smsc95xx_autosuspend(dev, link_up); -+ goto done; -+ } -+ -+ /* if we get this far we're not autosuspending */ -+ /* if no wol options set, or if link is down and we're not waking on -+ * PHY activity, enter lowest power SUSPEND2 mode -+ */ -+ if (!(pdata->wolopts & SUPPORTED_WAKE) || -+ !(link_up || (pdata->wolopts & WAKE_PHY))) { -+ netdev_info(dev->net, "entering SUSPEND2 mode\n"); -+ -+ /* disable energy detect (link up) & wake up events */ -+ ret = smsc95xx_read_reg_nopm(dev, WUCSR, &val); -+ if (ret < 0) -+ goto done; -+ -+ val &= ~(WUCSR_MPEN_ | WUCSR_WAKE_EN_); -+ -+ ret = smsc95xx_write_reg_nopm(dev, WUCSR, val); -+ if (ret < 0) -+ goto done; -+ -+ ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val); -+ if (ret < 0) -+ goto done; -+ -+ val &= ~(PM_CTL_ED_EN_ | PM_CTL_WOL_EN_); -+ -+ ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); -+ if (ret < 0) -+ goto done; -+ -+ ret = smsc95xx_enter_suspend2(dev); -+ goto done; -+ } -+ -+ if (pdata->wolopts & WAKE_PHY) { -+ ret = smsc95xx_enable_phy_wakeup_interrupts(dev, -+ (PHY_INT_MASK_ANEG_COMP_ | PHY_INT_MASK_LINK_DOWN_)); -+ if (ret < 0) { -+ netdev_warn(dev->net, "error enabling PHY wakeup ints\n"); -+ goto done; -+ } -+ -+ /* if link is down then configure EDPD and enter SUSPEND1, -+ * otherwise enter SUSPEND0 below -+ */ -+ if (!link_up) { -+ netdev_info(dev->net, "entering SUSPEND1 mode\n"); -+ ret = smsc95xx_enter_suspend1(dev); -+ goto done; -+ } -+ } -+ -+ if (pdata->wolopts & (WAKE_BCAST | WAKE_MCAST | WAKE_ARP | WAKE_UCAST)) { -+ u32 *filter_mask = kzalloc(sizeof(u32) * 32, GFP_KERNEL); -+ u32 command[2]; -+ u32 offset[2]; -+ u32 crc[4]; -+ int wuff_filter_count = -+ (pdata->features & FEATURE_8_WAKEUP_FILTERS) ? -+ LAN9500A_WUFF_NUM : LAN9500_WUFF_NUM; -+ int i, filter = 0; -+ -+ if (!filter_mask) { -+ netdev_warn(dev->net, "Unable to allocate filter_mask\n"); -+ ret = -ENOMEM; -+ goto done; -+ } -+ -+ memset(command, 0, sizeof(command)); -+ memset(offset, 0, sizeof(offset)); -+ memset(crc, 0, sizeof(crc)); -+ -+ if (pdata->wolopts & WAKE_BCAST) { -+ const u8 bcast[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; -+ netdev_info(dev->net, "enabling broadcast detection\n"); -+ filter_mask[filter * 4] = 0x003F; -+ filter_mask[filter * 4 + 1] = 0x00; -+ filter_mask[filter * 4 + 2] = 0x00; -+ filter_mask[filter * 4 + 3] = 0x00; -+ command[filter/4] |= 0x05UL << ((filter % 4) * 8); -+ offset[filter/4] |= 0x00 << ((filter % 4) * 8); -+ crc[filter/2] |= smsc_crc(bcast, 6, filter); -+ filter++; -+ } -+ -+ if (pdata->wolopts & WAKE_MCAST) { -+ const u8 mcast[] = {0x01, 0x00, 0x5E}; -+ netdev_info(dev->net, "enabling multicast detection\n"); -+ filter_mask[filter * 4] = 0x0007; -+ filter_mask[filter * 4 + 1] = 0x00; -+ filter_mask[filter * 4 + 2] = 0x00; -+ filter_mask[filter * 4 + 3] = 0x00; -+ command[filter/4] |= 0x09UL << ((filter % 4) * 8); -+ offset[filter/4] |= 0x00 << ((filter % 4) * 8); -+ crc[filter/2] |= smsc_crc(mcast, 3, filter); -+ filter++; -+ } -+ -+ if (pdata->wolopts & WAKE_ARP) { -+ const u8 arp[] = {0x08, 0x06}; -+ netdev_info(dev->net, "enabling ARP detection\n"); -+ filter_mask[filter * 4] = 0x0003; -+ filter_mask[filter * 4 + 1] = 0x00; -+ filter_mask[filter * 4 + 2] = 0x00; -+ filter_mask[filter * 4 + 3] = 0x00; -+ command[filter/4] |= 0x05UL << ((filter % 4) * 8); -+ offset[filter/4] |= 0x0C << ((filter % 4) * 8); -+ crc[filter/2] |= smsc_crc(arp, 2, filter); -+ filter++; -+ } -+ -+ if (pdata->wolopts & WAKE_UCAST) { -+ netdev_info(dev->net, "enabling unicast detection\n"); -+ filter_mask[filter * 4] = 0x003F; -+ filter_mask[filter * 4 + 1] = 0x00; -+ filter_mask[filter * 4 + 2] = 0x00; -+ filter_mask[filter * 4 + 3] = 0x00; -+ command[filter/4] |= 0x01UL << ((filter % 4) * 8); -+ offset[filter/4] |= 0x00 << ((filter % 4) * 8); -+ crc[filter/2] |= smsc_crc(dev->net->dev_addr, ETH_ALEN, filter); -+ filter++; -+ } -+ -+ for (i = 0; i < (wuff_filter_count * 4); i++) { -+ ret = smsc95xx_write_reg_nopm(dev, WUFF, filter_mask[i]); -+ if (ret < 0) { -+ kfree(filter_mask); -+ goto done; -+ } -+ } -+ kfree(filter_mask); -+ -+ for (i = 0; i < (wuff_filter_count / 4); i++) { -+ ret = smsc95xx_write_reg_nopm(dev, WUFF, command[i]); -+ if (ret < 0) -+ goto done; -+ } -+ -+ for (i = 0; i < (wuff_filter_count / 4); i++) { -+ ret = smsc95xx_write_reg_nopm(dev, WUFF, offset[i]); -+ if (ret < 0) -+ goto done; -+ } -+ -+ for (i = 0; i < (wuff_filter_count / 2); i++) { -+ ret = smsc95xx_write_reg_nopm(dev, WUFF, crc[i]); -+ if (ret < 0) -+ goto done; -+ } -+ -+ /* clear any pending pattern match packet status */ -+ ret = smsc95xx_read_reg_nopm(dev, WUCSR, &val); -+ if (ret < 0) -+ goto done; -+ -+ val |= WUCSR_WUFR_; -+ -+ ret = smsc95xx_write_reg_nopm(dev, WUCSR, val); -+ if (ret < 0) -+ goto done; -+ } -+ -+ if (pdata->wolopts & WAKE_MAGIC) { -+ /* clear any pending magic packet status */ -+ ret = smsc95xx_read_reg_nopm(dev, WUCSR, &val); -+ if (ret < 0) -+ goto done; -+ -+ val |= WUCSR_MPR_; -+ -+ ret = smsc95xx_write_reg_nopm(dev, WUCSR, val); -+ if (ret < 0) -+ goto done; -+ } -+ -+ /* enable/disable wakeup sources */ -+ ret = smsc95xx_read_reg_nopm(dev, WUCSR, &val); -+ if (ret < 0) -+ goto done; -+ -+ if (pdata->wolopts & (WAKE_BCAST | WAKE_MCAST | WAKE_ARP | WAKE_UCAST)) { -+ netdev_info(dev->net, "enabling pattern match wakeup\n"); -+ val |= WUCSR_WAKE_EN_; -+ } else { -+ netdev_info(dev->net, "disabling pattern match wakeup\n"); -+ val &= ~WUCSR_WAKE_EN_; -+ } -+ -+ if (pdata->wolopts & WAKE_MAGIC) { -+ netdev_info(dev->net, "enabling magic packet wakeup\n"); -+ val |= WUCSR_MPEN_; -+ } else { -+ netdev_info(dev->net, "disabling magic packet wakeup\n"); -+ val &= ~WUCSR_MPEN_; -+ } -+ -+ ret = smsc95xx_write_reg_nopm(dev, WUCSR, val); -+ if (ret < 0) -+ goto done; -+ -+ /* enable wol wakeup source */ -+ ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val); -+ if (ret < 0) -+ goto done; -+ -+ val |= PM_CTL_WOL_EN_; -+ -+ /* phy energy detect wakeup source */ -+ if (pdata->wolopts & WAKE_PHY) -+ val |= PM_CTL_ED_EN_; -+ -+ ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); -+ if (ret < 0) -+ goto done; -+ -+ /* enable receiver to enable frame reception */ -+ smsc95xx_start_rx_path(dev, 1); -+ -+ /* some wol options are enabled, so enter SUSPEND0 */ -+ netdev_info(dev->net, "entering SUSPEND0 mode\n"); -+ ret = smsc95xx_enter_suspend0(dev); -+ -+done: -+ /* -+ * TODO: resume() might need to handle the suspend failure -+ * in system sleep -+ */ -+ if (ret && PMSG_IS_AUTO(message)) -+ usbnet_resume(intf); -+ return ret; -+} -+ -+static int smsc95xx_resume(struct usb_interface *intf) -+{ -+ struct usbnet *dev = usb_get_intfdata(intf); -+ struct smsc95xx_priv *pdata; -+ u8 suspend_flags; -+ int ret; -+ u32 val; -+ -+ BUG_ON(!dev); -+ pdata = (struct smsc95xx_priv *)(dev->data[0]); -+ suspend_flags = pdata->suspend_flags; -+ -+ netdev_dbg(dev->net, "resume suspend_flags=0x%02x\n", suspend_flags); -+ -+ /* do this first to ensure it's cleared even in error case */ -+ pdata->suspend_flags = 0; -+ -+ if (suspend_flags & SUSPEND_ALLMODES) { -+ /* clear wake-up sources */ -+ ret = smsc95xx_read_reg_nopm(dev, WUCSR, &val); -+ if (ret < 0) -+ return ret; -+ -+ val &= ~(WUCSR_WAKE_EN_ | WUCSR_MPEN_); -+ -+ ret = smsc95xx_write_reg_nopm(dev, WUCSR, val); -+ if (ret < 0) -+ return ret; -+ -+ /* clear wake-up status */ -+ ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val); -+ if (ret < 0) -+ return ret; -+ -+ val &= ~PM_CTL_WOL_EN_; -+ val |= PM_CTL_WUPS_; -+ -+ ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); -+ if (ret < 0) -+ return ret; -+ } -+ -+ ret = usbnet_resume(intf); -+ if (ret < 0) -+ netdev_warn(dev->net, "usbnet_resume error\n"); -+ -+ return ret; -+} -+ -+static int smsc95xx_reset_resume(struct usb_interface *intf) -+{ -+ struct usbnet *dev = usb_get_intfdata(intf); -+ int ret; -+ -+ ret = smsc95xx_reset(dev); -+ if (ret < 0) -+ return ret; -+ -+ return smsc95xx_resume(intf); -+} -+ -+static void smsc95xx_rx_csum_offload(struct sk_buff *skb) -+{ -+ skb->csum = *(u16 *)(skb_tail_pointer(skb) - 2); -+ skb->ip_summed = CHECKSUM_COMPLETE; -+ skb_trim(skb, skb->len - 2); -+} -+ -+static int smsc95xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb) -+{ -+ /* This check is no longer done by usbnet */ -+ if (skb->len < dev->net->hard_header_len) -+ return 0; -+ -+ while (skb->len > 0) { -+ u32 header, align_count; -+ struct sk_buff *ax_skb; -+ unsigned char *packet; -+ u16 size; -+ -+ memcpy(&header, skb->data, sizeof(header)); -+ le32_to_cpus(&header); -+ skb_pull(skb, 4 + NET_IP_ALIGN); -+ packet = skb->data; -+ -+ /* get the packet length */ -+ size = (u16)((header & RX_STS_FL_) >> 16); -+ align_count = (4 - ((size + NET_IP_ALIGN) % 4)) % 4; -+ -+ if (unlikely(header & RX_STS_ES_)) { -+ netif_dbg(dev, rx_err, dev->net, -+ "Error header=0x%08x\n", header); -+ dev->net->stats.rx_errors++; -+ dev->net->stats.rx_dropped++; -+ -+ if (header & RX_STS_CRC_) { -+ dev->net->stats.rx_crc_errors++; -+ } else { -+ if (header & (RX_STS_TL_ | RX_STS_RF_)) -+ dev->net->stats.rx_frame_errors++; -+ -+ if ((header & RX_STS_LE_) && -+ (!(header & RX_STS_FT_))) -+ dev->net->stats.rx_length_errors++; -+ } -+ } else { -+ /* ETH_FRAME_LEN + 4(CRC) + 2(COE) + 4(Vlan) */ -+ if (unlikely(size > (ETH_FRAME_LEN + 12))) { -+ netif_dbg(dev, rx_err, dev->net, -+ "size err header=0x%08x\n", header); -+ return 0; -+ } -+ -+ /* last frame in this batch */ -+ if (skb->len == size) { -+ if (dev->net->features & NETIF_F_RXCSUM) -+ smsc95xx_rx_csum_offload(skb); -+ skb_trim(skb, skb->len - 4); /* remove fcs */ -+ skb->truesize = size + sizeof(struct sk_buff); -+ -+ return 1; -+ } -+ -+ ax_skb = skb_clone(skb, GFP_ATOMIC); -+ if (unlikely(!ax_skb)) { -+ netdev_warn(dev->net, "Error allocating skb\n"); -+ return 0; -+ } -+ -+ ax_skb->len = size; -+ ax_skb->data = packet; -+ skb_set_tail_pointer(ax_skb, size); -+ -+ if (dev->net->features & NETIF_F_RXCSUM) -+ smsc95xx_rx_csum_offload(ax_skb); -+ skb_trim(ax_skb, ax_skb->len - 4); /* remove fcs */ -+ ax_skb->truesize = size + sizeof(struct sk_buff); -+ -+ usbnet_skb_return(dev, ax_skb); -+ } -+ -+ skb_pull(skb, size); -+ -+ /* padding bytes before the next frame starts */ -+ if (skb->len) -+ skb_pull(skb, align_count); -+ } -+ -+ if (unlikely(skb->len < 0)) { -+ netdev_warn(dev->net, "invalid rx length<0 %d\n", skb->len); -+ return 0; -+ } -+ -+ return 1; -+} -+ -+static u32 smsc95xx_calc_csum_preamble(struct sk_buff *skb) -+{ -+ u16 low_16 = (u16)skb_checksum_start_offset(skb); -+ u16 high_16 = low_16 + skb->csum_offset; -+ return (high_16 << 16) | low_16; -+} -+ -+static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev, -+ struct sk_buff *skb, gfp_t flags) -+{ -+ bool csum = skb->ip_summed == CHECKSUM_PARTIAL; -+ int overhead = csum ? SMSC95XX_TX_OVERHEAD_CSUM : SMSC95XX_TX_OVERHEAD; -+ u32 tx_cmd_a, tx_cmd_b; -+ -+ /* We do not advertise SG, so skbs should be already linearized */ -+ BUG_ON(skb_shinfo(skb)->nr_frags); -+ -+ if (skb_headroom(skb) < overhead) { -+ struct sk_buff *skb2 = skb_copy_expand(skb, -+ overhead, 0, flags); -+ dev_kfree_skb_any(skb); -+ skb = skb2; -+ if (!skb) -+ return NULL; -+ } -+ -+ if (csum) { -+ if (skb->len <= 45) { -+ /* workaround - hardware tx checksum does not work -+ * properly with extremely small packets */ -+ long csstart = skb_checksum_start_offset(skb); -+ __wsum calc = csum_partial(skb->data + csstart, -+ skb->len - csstart, 0); -+ *((__sum16 *)(skb->data + csstart -+ + skb->csum_offset)) = csum_fold(calc); -+ -+ csum = false; -+ } else { -+ u32 csum_preamble = smsc95xx_calc_csum_preamble(skb); -+ skb_push(skb, 4); -+ cpu_to_le32s(&csum_preamble); -+ memcpy(skb->data, &csum_preamble, 4); -+ } -+ } -+ -+ skb_push(skb, 4); -+ tx_cmd_b = (u32)(skb->len - 4); -+ if (csum) -+ tx_cmd_b |= TX_CMD_B_CSUM_ENABLE; -+ cpu_to_le32s(&tx_cmd_b); -+ memcpy(skb->data, &tx_cmd_b, 4); -+ -+ skb_push(skb, 4); -+ tx_cmd_a = (u32)(skb->len - 8) | TX_CMD_A_FIRST_SEG_ | -+ TX_CMD_A_LAST_SEG_; -+ cpu_to_le32s(&tx_cmd_a); -+ memcpy(skb->data, &tx_cmd_a, 4); -+ -+ return skb; -+} -+ -+static int smsc95xx_manage_power(struct usbnet *dev, int on) -+{ -+ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); -+ -+ dev->intf->needs_remote_wakeup = on; -+ -+ if (pdata->features & FEATURE_REMOTE_WAKEUP) -+ return 0; -+ -+ /* this chip revision isn't capable of remote wakeup */ -+ netdev_info(dev->net, "hardware isn't capable of remote wakeup\n"); -+ -+ if (on) -+ usb_autopm_get_interface_no_resume(dev->intf); -+ else -+ usb_autopm_put_interface(dev->intf); -+ -+ return 0; -+} -+ -+static const struct driver_info smsc95xx_info = { -+ .description = "smsc95xx USB 2.0 Ethernet", -+ .bind = smsc95xx_bind, -+ .unbind = smsc95xx_unbind, -+ .link_reset = smsc95xx_link_reset, -+ .reset = smsc95xx_reset, -+ .rx_fixup = smsc95xx_rx_fixup, -+ .tx_fixup = smsc95xx_tx_fixup, -+ .status = smsc95xx_status, -+ .manage_power = smsc95xx_manage_power, -+ .flags = FLAG_ETHER | FLAG_SEND_ZLP | FLAG_LINK_INTR, -+}; -+ -+static const struct usb_device_id products[] = { -+ { -+ /* SMSC9500 USB Ethernet Device */ -+ USB_DEVICE(0x0424, 0x9500), -+ .driver_info = (unsigned long) &smsc95xx_info, -+ }, -+ { -+ /* SMSC9505 USB Ethernet Device */ -+ USB_DEVICE(0x0424, 0x9505), -+ .driver_info = (unsigned long) &smsc95xx_info, -+ }, -+ { -+ /* SMSC9500A USB Ethernet Device */ -+ USB_DEVICE(0x0424, 0x9E00), -+ .driver_info = (unsigned long) &smsc95xx_info, -+ }, -+ { -+ /* SMSC9505A USB Ethernet Device */ -+ USB_DEVICE(0x0424, 0x9E01), -+ .driver_info = (unsigned long) &smsc95xx_info, -+ }, -+ { -+ /* SMSC9512/9514 USB Hub & Ethernet Device */ -+ USB_DEVICE(0x0424, 0xec00), -+ .driver_info = (unsigned long) &smsc95xx_info, -+ }, -+ { -+ /* SMSC9500 USB Ethernet Device (SAL10) */ -+ USB_DEVICE(0x0424, 0x9900), -+ .driver_info = (unsigned long) &smsc95xx_info, -+ }, -+ { -+ /* SMSC9505 USB Ethernet Device (SAL10) */ -+ USB_DEVICE(0x0424, 0x9901), -+ .driver_info = (unsigned long) &smsc95xx_info, -+ }, -+ { -+ /* SMSC9500A USB Ethernet Device (SAL10) */ -+ USB_DEVICE(0x0424, 0x9902), -+ .driver_info = (unsigned long) &smsc95xx_info, -+ }, -+ { -+ /* SMSC9505A USB Ethernet Device (SAL10) */ -+ USB_DEVICE(0x0424, 0x9903), -+ .driver_info = (unsigned long) &smsc95xx_info, -+ }, -+ { -+ /* SMSC9512/9514 USB Hub & Ethernet Device (SAL10) */ -+ USB_DEVICE(0x0424, 0x9904), -+ .driver_info = (unsigned long) &smsc95xx_info, -+ }, -+ { -+ /* SMSC9500A USB Ethernet Device (HAL) */ -+ USB_DEVICE(0x0424, 0x9905), -+ .driver_info = (unsigned long) &smsc95xx_info, -+ }, -+ { -+ /* SMSC9505A USB Ethernet Device (HAL) */ -+ USB_DEVICE(0x0424, 0x9906), -+ .driver_info = (unsigned long) &smsc95xx_info, -+ }, -+ { -+ /* SMSC9500 USB Ethernet Device (Alternate ID) */ -+ USB_DEVICE(0x0424, 0x9907), -+ .driver_info = (unsigned long) &smsc95xx_info, -+ }, -+ { -+ /* SMSC9500A USB Ethernet Device (Alternate ID) */ -+ USB_DEVICE(0x0424, 0x9908), -+ .driver_info = (unsigned long) &smsc95xx_info, -+ }, -+ { -+ /* SMSC9512/9514 USB Hub & Ethernet Device (Alternate ID) */ -+ USB_DEVICE(0x0424, 0x9909), -+ .driver_info = (unsigned long) &smsc95xx_info, -+ }, -+ { -+ /* SMSC LAN9530 USB Ethernet Device */ -+ USB_DEVICE(0x0424, 0x9530), -+ .driver_info = (unsigned long) &smsc95xx_info, -+ }, -+ { -+ /* SMSC LAN9730 USB Ethernet Device */ -+ USB_DEVICE(0x0424, 0x9730), -+ .driver_info = (unsigned long) &smsc95xx_info, -+ }, -+ { -+ /* SMSC LAN89530 USB Ethernet Device */ -+ USB_DEVICE(0x0424, 0x9E08), -+ .driver_info = (unsigned long) &smsc95xx_info, -+ }, -+ { }, /* END */ -+}; -+MODULE_DEVICE_TABLE(usb, products); -+ -+static struct usb_driver smsc95xx_driver = { -+ .name = "smsc95xx", -+ .id_table = products, -+ .probe = usbnet_probe, -+ .suspend = smsc95xx_suspend, -+ .resume = smsc95xx_resume, -+ .reset_resume = smsc95xx_reset_resume, -+ .disconnect = usbnet_disconnect, -+ .disable_hub_initiated_lpm = 1, -+ .supports_autosuspend = 1, -+}; -+ -+module_usb_driver(smsc95xx_driver); -+ -+MODULE_AUTHOR("Nancy Lin"); -+MODULE_AUTHOR("Steve Glendinning "); -+MODULE_DESCRIPTION("SMSC95XX USB 2.0 Ethernet Devices"); -+MODULE_LICENSE("GPL"); -diff -Naur backports-4.2.6-1.org/drivers/net/usb/smsc95xx.h backports-4.2.6-1/drivers/net/usb/smsc95xx.h ---- backports-4.2.6-1.org/drivers/net/usb/smsc95xx.h 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/smsc95xx.h 2016-06-28 14:35:18.011973884 +0200 -@@ -0,0 +1,290 @@ -+ /*************************************************************************** -+ * -+ * Copyright (C) 2007-2008 SMSC -+ * -+ * 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, see . -+ * -+ *****************************************************************************/ -+ -+#ifndef _SMSC95XX_H -+#define _SMSC95XX_H -+ -+/* Tx command words */ -+#define TX_CMD_A_DATA_OFFSET_ (0x001F0000) -+#define TX_CMD_A_FIRST_SEG_ (0x00002000) -+#define TX_CMD_A_LAST_SEG_ (0x00001000) -+#define TX_CMD_A_BUF_SIZE_ (0x000007FF) -+ -+#define TX_CMD_B_CSUM_ENABLE (0x00004000) -+#define TX_CMD_B_ADD_CRC_DISABLE_ (0x00002000) -+#define TX_CMD_B_DISABLE_PADDING_ (0x00001000) -+#define TX_CMD_B_PKT_BYTE_LENGTH_ (0x000007FF) -+ -+/* Rx status word */ -+#define RX_STS_FF_ (0x40000000) /* Filter Fail */ -+#define RX_STS_FL_ (0x3FFF0000) /* Frame Length */ -+#define RX_STS_ES_ (0x00008000) /* Error Summary */ -+#define RX_STS_BF_ (0x00002000) /* Broadcast Frame */ -+#define RX_STS_LE_ (0x00001000) /* Length Error */ -+#define RX_STS_RF_ (0x00000800) /* Runt Frame */ -+#define RX_STS_MF_ (0x00000400) /* Multicast Frame */ -+#define RX_STS_TL_ (0x00000080) /* Frame too long */ -+#define RX_STS_CS_ (0x00000040) /* Collision Seen */ -+#define RX_STS_FT_ (0x00000020) /* Frame Type */ -+#define RX_STS_RW_ (0x00000010) /* Receive Watchdog */ -+#define RX_STS_ME_ (0x00000008) /* Mii Error */ -+#define RX_STS_DB_ (0x00000004) /* Dribbling */ -+#define RX_STS_CRC_ (0x00000002) /* CRC Error */ -+ -+/* SCSRs */ -+#define ID_REV (0x00) -+#define ID_REV_CHIP_ID_MASK_ (0xFFFF0000) -+#define ID_REV_CHIP_REV_MASK_ (0x0000FFFF) -+#define ID_REV_CHIP_ID_9500_ (0x9500) -+#define ID_REV_CHIP_ID_9500A_ (0x9E00) -+#define ID_REV_CHIP_ID_9512_ (0xEC00) -+#define ID_REV_CHIP_ID_9530_ (0x9530) -+#define ID_REV_CHIP_ID_89530_ (0x9E08) -+#define ID_REV_CHIP_ID_9730_ (0x9730) -+ -+#define INT_STS (0x08) -+#define INT_STS_TX_STOP_ (0x00020000) -+#define INT_STS_RX_STOP_ (0x00010000) -+#define INT_STS_PHY_INT_ (0x00008000) -+#define INT_STS_TXE_ (0x00004000) -+#define INT_STS_TDFU_ (0x00002000) -+#define INT_STS_TDFO_ (0x00001000) -+#define INT_STS_RXDF_ (0x00000800) -+#define INT_STS_GPIOS_ (0x000007FF) -+#define INT_STS_CLEAR_ALL_ (0xFFFFFFFF) -+ -+#define RX_CFG (0x0C) -+#define RX_FIFO_FLUSH_ (0x00000001) -+ -+#define TX_CFG (0x10) -+#define TX_CFG_ON_ (0x00000004) -+#define TX_CFG_STOP_ (0x00000002) -+#define TX_CFG_FIFO_FLUSH_ (0x00000001) -+ -+#define HW_CFG (0x14) -+#define HW_CFG_BIR_ (0x00001000) -+#define HW_CFG_LEDB_ (0x00000800) -+#define HW_CFG_RXDOFF_ (0x00000600) -+#define HW_CFG_DRP_ (0x00000040) -+#define HW_CFG_MEF_ (0x00000020) -+#define HW_CFG_LRST_ (0x00000008) -+#define HW_CFG_PSEL_ (0x00000004) -+#define HW_CFG_BCE_ (0x00000002) -+#define HW_CFG_SRST_ (0x00000001) -+ -+#define RX_FIFO_INF (0x18) -+ -+#define PM_CTRL (0x20) -+#define PM_CTL_RES_CLR_WKP_STS (0x00000200) -+#define PM_CTL_DEV_RDY_ (0x00000080) -+#define PM_CTL_SUS_MODE_ (0x00000060) -+#define PM_CTL_SUS_MODE_0 (0x00000000) -+#define PM_CTL_SUS_MODE_1 (0x00000020) -+#define PM_CTL_SUS_MODE_2 (0x00000040) -+#define PM_CTL_SUS_MODE_3 (0x00000060) -+#define PM_CTL_PHY_RST_ (0x00000010) -+#define PM_CTL_WOL_EN_ (0x00000008) -+#define PM_CTL_ED_EN_ (0x00000004) -+#define PM_CTL_WUPS_ (0x00000003) -+#define PM_CTL_WUPS_NO_ (0x00000000) -+#define PM_CTL_WUPS_ED_ (0x00000001) -+#define PM_CTL_WUPS_WOL_ (0x00000002) -+#define PM_CTL_WUPS_MULTI_ (0x00000003) -+ -+#define LED_GPIO_CFG (0x24) -+#define LED_GPIO_CFG_SPD_LED (0x01000000) -+#define LED_GPIO_CFG_LNK_LED (0x00100000) -+#define LED_GPIO_CFG_FDX_LED (0x00010000) -+ -+#define GPIO_CFG (0x28) -+ -+#define AFC_CFG (0x2C) -+ -+/* Hi watermark = 15.5Kb (~10 mtu pkts) */ -+/* low watermark = 3k (~2 mtu pkts) */ -+/* backpressure duration = ~ 350us */ -+/* Apply FC on any frame. */ -+#define AFC_CFG_DEFAULT (0x00F830A1) -+ -+#define E2P_CMD (0x30) -+#define E2P_CMD_BUSY_ (0x80000000) -+#define E2P_CMD_MASK_ (0x70000000) -+#define E2P_CMD_READ_ (0x00000000) -+#define E2P_CMD_EWDS_ (0x10000000) -+#define E2P_CMD_EWEN_ (0x20000000) -+#define E2P_CMD_WRITE_ (0x30000000) -+#define E2P_CMD_WRAL_ (0x40000000) -+#define E2P_CMD_ERASE_ (0x50000000) -+#define E2P_CMD_ERAL_ (0x60000000) -+#define E2P_CMD_RELOAD_ (0x70000000) -+#define E2P_CMD_TIMEOUT_ (0x00000400) -+#define E2P_CMD_LOADED_ (0x00000200) -+#define E2P_CMD_ADDR_ (0x000001FF) -+ -+#define MAX_EEPROM_SIZE (512) -+ -+#define E2P_DATA (0x34) -+#define E2P_DATA_MASK_ (0x000000FF) -+ -+#define BURST_CAP (0x38) -+ -+#define GPIO_WAKE (0x64) -+ -+#define INT_EP_CTL (0x68) -+#define INT_EP_CTL_INTEP_ (0x80000000) -+#define INT_EP_CTL_MACRTO_ (0x00080000) -+#define INT_EP_CTL_TX_STOP_ (0x00020000) -+#define INT_EP_CTL_RX_STOP_ (0x00010000) -+#define INT_EP_CTL_PHY_INT_ (0x00008000) -+#define INT_EP_CTL_TXE_ (0x00004000) -+#define INT_EP_CTL_TDFU_ (0x00002000) -+#define INT_EP_CTL_TDFO_ (0x00001000) -+#define INT_EP_CTL_RXDF_ (0x00000800) -+#define INT_EP_CTL_GPIOS_ (0x000007FF) -+ -+#define BULK_IN_DLY (0x6C) -+ -+/* MAC CSRs */ -+#define MAC_CR (0x100) -+#define MAC_CR_RXALL_ (0x80000000) -+#define MAC_CR_RCVOWN_ (0x00800000) -+#define MAC_CR_LOOPBK_ (0x00200000) -+#define MAC_CR_FDPX_ (0x00100000) -+#define MAC_CR_MCPAS_ (0x00080000) -+#define MAC_CR_PRMS_ (0x00040000) -+#define MAC_CR_INVFILT_ (0x00020000) -+#define MAC_CR_PASSBAD_ (0x00010000) -+#define MAC_CR_HFILT_ (0x00008000) -+#define MAC_CR_HPFILT_ (0x00002000) -+#define MAC_CR_LCOLL_ (0x00001000) -+#define MAC_CR_BCAST_ (0x00000800) -+#define MAC_CR_DISRTY_ (0x00000400) -+#define MAC_CR_PADSTR_ (0x00000100) -+#define MAC_CR_BOLMT_MASK (0x000000C0) -+#define MAC_CR_DFCHK_ (0x00000020) -+#define MAC_CR_TXEN_ (0x00000008) -+#define MAC_CR_RXEN_ (0x00000004) -+ -+#define ADDRH (0x104) -+ -+#define ADDRL (0x108) -+ -+#define HASHH (0x10C) -+ -+#define HASHL (0x110) -+ -+#define MII_ADDR (0x114) -+#define MII_WRITE_ (0x02) -+#define MII_BUSY_ (0x01) -+#define MII_READ_ (0x00) /* ~of MII Write bit */ -+ -+#define MII_DATA (0x118) -+ -+#define FLOW (0x11C) -+#define FLOW_FCPT_ (0xFFFF0000) -+#define FLOW_FCPASS_ (0x00000004) -+#define FLOW_FCEN_ (0x00000002) -+#define FLOW_FCBSY_ (0x00000001) -+ -+#define VLAN1 (0x120) -+ -+#define VLAN2 (0x124) -+ -+#define WUFF (0x128) -+#define LAN9500_WUFF_NUM (4) -+#define LAN9500A_WUFF_NUM (8) -+ -+#define WUCSR (0x12C) -+#define WUCSR_WFF_PTR_RST_ (0x80000000) -+#define WUCSR_GUE_ (0x00000200) -+#define WUCSR_WUFR_ (0x00000040) -+#define WUCSR_MPR_ (0x00000020) -+#define WUCSR_WAKE_EN_ (0x00000004) -+#define WUCSR_MPEN_ (0x00000002) -+ -+#define COE_CR (0x130) -+#define Tx_COE_EN_ (0x00010000) -+#define Rx_COE_MODE_ (0x00000002) -+#define Rx_COE_EN_ (0x00000001) -+ -+/* Vendor-specific PHY Definitions */ -+ -+/* EDPD NLP / crossover time configuration (LAN9500A only) */ -+#define PHY_EDPD_CONFIG (16) -+#define PHY_EDPD_CONFIG_TX_NLP_EN_ ((u16)0x8000) -+#define PHY_EDPD_CONFIG_TX_NLP_1000_ ((u16)0x0000) -+#define PHY_EDPD_CONFIG_TX_NLP_768_ ((u16)0x2000) -+#define PHY_EDPD_CONFIG_TX_NLP_512_ ((u16)0x4000) -+#define PHY_EDPD_CONFIG_TX_NLP_256_ ((u16)0x6000) -+#define PHY_EDPD_CONFIG_RX_1_NLP_ ((u16)0x1000) -+#define PHY_EDPD_CONFIG_RX_NLP_64_ ((u16)0x0000) -+#define PHY_EDPD_CONFIG_RX_NLP_256_ ((u16)0x0400) -+#define PHY_EDPD_CONFIG_RX_NLP_512_ ((u16)0x0800) -+#define PHY_EDPD_CONFIG_RX_NLP_1000_ ((u16)0x0C00) -+#define PHY_EDPD_CONFIG_EXT_CROSSOVER_ ((u16)0x0001) -+#define PHY_EDPD_CONFIG_DEFAULT (PHY_EDPD_CONFIG_TX_NLP_EN_ | \ -+ PHY_EDPD_CONFIG_TX_NLP_768_ | \ -+ PHY_EDPD_CONFIG_RX_1_NLP_) -+ -+/* Mode Control/Status Register */ -+#define PHY_MODE_CTRL_STS (17) -+#define MODE_CTRL_STS_EDPWRDOWN_ ((u16)0x2000) -+#define MODE_CTRL_STS_ENERGYON_ ((u16)0x0002) -+ -+#define SPECIAL_CTRL_STS (27) -+#define SPECIAL_CTRL_STS_OVRRD_AMDIX_ ((u16)0x8000) -+#define SPECIAL_CTRL_STS_AMDIX_ENABLE_ ((u16)0x4000) -+#define SPECIAL_CTRL_STS_AMDIX_STATE_ ((u16)0x2000) -+ -+#define PHY_INT_SRC (29) -+#define PHY_INT_SRC_ENERGY_ON_ ((u16)0x0080) -+#define PHY_INT_SRC_ANEG_COMP_ ((u16)0x0040) -+#define PHY_INT_SRC_REMOTE_FAULT_ ((u16)0x0020) -+#define PHY_INT_SRC_LINK_DOWN_ ((u16)0x0010) -+ -+#define PHY_INT_MASK (30) -+#define PHY_INT_MASK_ENERGY_ON_ ((u16)0x0080) -+#define PHY_INT_MASK_ANEG_COMP_ ((u16)0x0040) -+#define PHY_INT_MASK_REMOTE_FAULT_ ((u16)0x0020) -+#define PHY_INT_MASK_LINK_DOWN_ ((u16)0x0010) -+#define PHY_INT_MASK_DEFAULT_ (PHY_INT_MASK_ANEG_COMP_ | \ -+ PHY_INT_MASK_LINK_DOWN_) -+ -+#define PHY_SPECIAL (31) -+#define PHY_SPECIAL_SPD_ ((u16)0x001C) -+#define PHY_SPECIAL_SPD_10HALF_ ((u16)0x0004) -+#define PHY_SPECIAL_SPD_10FULL_ ((u16)0x0014) -+#define PHY_SPECIAL_SPD_100HALF_ ((u16)0x0008) -+#define PHY_SPECIAL_SPD_100FULL_ ((u16)0x0018) -+ -+/* USB Vendor Requests */ -+#define USB_VENDOR_REQUEST_WRITE_REGISTER 0xA0 -+#define USB_VENDOR_REQUEST_READ_REGISTER 0xA1 -+#define USB_VENDOR_REQUEST_GET_STATS 0xA2 -+ -+/* Interrupt Endpoint status word bitfields */ -+#define INT_ENP_TX_STOP_ ((u32)BIT(17)) -+#define INT_ENP_RX_STOP_ ((u32)BIT(16)) -+#define INT_ENP_PHY_INT_ ((u32)BIT(15)) -+#define INT_ENP_TXE_ ((u32)BIT(14)) -+#define INT_ENP_TDFU_ ((u32)BIT(13)) -+#define INT_ENP_TDFO_ ((u32)BIT(12)) -+#define INT_ENP_RXDF_ ((u32)BIT(11)) -+ -+#endif /* _SMSC95XX_H */ -diff -Naur backports-4.2.6-1.org/drivers/net/usb/sr9700.c backports-4.2.6-1/drivers/net/usb/sr9700.c ---- backports-4.2.6-1.org/drivers/net/usb/sr9700.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/sr9700.c 2016-06-28 14:35:18.011973884 +0200 -@@ -0,0 +1,559 @@ -+/* -+ * CoreChip-sz SR9700 one chip USB 1.1 Ethernet Devices -+ * -+ * Author : Liu Junliang -+ * -+ * Based on dm9601.c -+ * -+ * This file is licensed under the terms of the GNU General Public License -+ * version 2. This program is licensed "as is" without any warranty of any -+ * kind, whether express or implied. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "sr9700.h" -+ -+static int sr_read(struct usbnet *dev, u8 reg, u16 length, void *data) -+{ -+ int err; -+ -+ err = usbnet_read_cmd(dev, SR_RD_REGS, SR_REQ_RD_REG, 0, reg, data, -+ length); -+ if ((err != length) && (err >= 0)) -+ err = -EINVAL; -+ return err; -+} -+ -+static int sr_write(struct usbnet *dev, u8 reg, u16 length, void *data) -+{ -+ int err; -+ -+ err = usbnet_write_cmd(dev, SR_WR_REGS, SR_REQ_WR_REG, 0, reg, data, -+ length); -+ if ((err >= 0) && (err < length)) -+ err = -EINVAL; -+ return err; -+} -+ -+static int sr_read_reg(struct usbnet *dev, u8 reg, u8 *value) -+{ -+ return sr_read(dev, reg, 1, value); -+} -+ -+static int sr_write_reg(struct usbnet *dev, u8 reg, u8 value) -+{ -+ return usbnet_write_cmd(dev, SR_WR_REGS, SR_REQ_WR_REG, -+ value, reg, NULL, 0); -+} -+ -+static void sr_write_async(struct usbnet *dev, u8 reg, u16 length, void *data) -+{ -+ usbnet_write_cmd_async(dev, SR_WR_REGS, SR_REQ_WR_REG, -+ 0, reg, data, length); -+} -+ -+static void sr_write_reg_async(struct usbnet *dev, u8 reg, u8 value) -+{ -+ usbnet_write_cmd_async(dev, SR_WR_REGS, SR_REQ_WR_REG, -+ value, reg, NULL, 0); -+} -+ -+static int wait_phy_eeprom_ready(struct usbnet *dev, int phy) -+{ -+ int i; -+ -+ for (i = 0; i < SR_SHARE_TIMEOUT; i++) { -+ u8 tmp = 0; -+ int ret; -+ -+ udelay(1); -+ ret = sr_read_reg(dev, SR_EPCR, &tmp); -+ if (ret < 0) -+ return ret; -+ -+ /* ready */ -+ if (!(tmp & EPCR_ERRE)) -+ return 0; -+ } -+ -+ netdev_err(dev->net, "%s write timed out!\n", phy ? "phy" : "eeprom"); -+ -+ return -EIO; -+} -+ -+static int sr_share_read_word(struct usbnet *dev, int phy, u8 reg, -+ __le16 *value) -+{ -+ int ret; -+ -+ mutex_lock(&dev->phy_mutex); -+ -+ sr_write_reg(dev, SR_EPAR, phy ? (reg | EPAR_PHY_ADR) : reg); -+ sr_write_reg(dev, SR_EPCR, phy ? (EPCR_EPOS | EPCR_ERPRR) : EPCR_ERPRR); -+ -+ ret = wait_phy_eeprom_ready(dev, phy); -+ if (ret < 0) -+ goto out_unlock; -+ -+ sr_write_reg(dev, SR_EPCR, 0x0); -+ ret = sr_read(dev, SR_EPDR, 2, value); -+ -+ netdev_dbg(dev->net, "read shared %d 0x%02x returned 0x%04x, %d\n", -+ phy, reg, *value, ret); -+ -+out_unlock: -+ mutex_unlock(&dev->phy_mutex); -+ return ret; -+} -+ -+static int sr_share_write_word(struct usbnet *dev, int phy, u8 reg, -+ __le16 value) -+{ -+ int ret; -+ -+ mutex_lock(&dev->phy_mutex); -+ -+ ret = sr_write(dev, SR_EPDR, 2, &value); -+ if (ret < 0) -+ goto out_unlock; -+ -+ sr_write_reg(dev, SR_EPAR, phy ? (reg | EPAR_PHY_ADR) : reg); -+ sr_write_reg(dev, SR_EPCR, phy ? (EPCR_WEP | EPCR_EPOS | EPCR_ERPRW) : -+ (EPCR_WEP | EPCR_ERPRW)); -+ -+ ret = wait_phy_eeprom_ready(dev, phy); -+ if (ret < 0) -+ goto out_unlock; -+ -+ sr_write_reg(dev, SR_EPCR, 0x0); -+ -+out_unlock: -+ mutex_unlock(&dev->phy_mutex); -+ return ret; -+} -+ -+static int sr_read_eeprom_word(struct usbnet *dev, u8 offset, void *value) -+{ -+ return sr_share_read_word(dev, 0, offset, value); -+} -+ -+static int sr9700_get_eeprom_len(struct net_device *netdev) -+{ -+ return SR_EEPROM_LEN; -+} -+ -+static int sr9700_get_eeprom(struct net_device *netdev, -+ struct ethtool_eeprom *eeprom, u8 *data) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ __le16 *buf = (__le16 *)data; -+ int ret = 0; -+ int i; -+ -+ /* access is 16bit */ -+ if ((eeprom->offset & 0x01) || (eeprom->len & 0x01)) -+ return -EINVAL; -+ -+ for (i = 0; i < eeprom->len / 2; i++) { -+ ret = sr_read_eeprom_word(dev, eeprom->offset / 2 + i, buf + i); -+ if (ret < 0) -+ break; -+ } -+ -+ return ret; -+} -+ -+static int sr_mdio_read(struct net_device *netdev, int phy_id, int loc) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ __le16 res; -+ int rc = 0; -+ -+ if (phy_id) { -+ netdev_dbg(netdev, "Only internal phy supported\n"); -+ return 0; -+ } -+ -+ /* Access NSR_LINKST bit for link status instead of MII_BMSR */ -+ if (loc == MII_BMSR) { -+ u8 value; -+ -+ sr_read_reg(dev, SR_NSR, &value); -+ if (value & NSR_LINKST) -+ rc = 1; -+ } -+ sr_share_read_word(dev, 1, loc, &res); -+ if (rc == 1) -+ res = le16_to_cpu(res) | BMSR_LSTATUS; -+ else -+ res = le16_to_cpu(res) & ~BMSR_LSTATUS; -+ -+ netdev_dbg(netdev, "sr_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n", -+ phy_id, loc, res); -+ -+ return res; -+} -+ -+static void sr_mdio_write(struct net_device *netdev, int phy_id, int loc, -+ int val) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ __le16 res = cpu_to_le16(val); -+ -+ if (phy_id) { -+ netdev_dbg(netdev, "Only internal phy supported\n"); -+ return; -+ } -+ -+ netdev_dbg(netdev, "sr_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n", -+ phy_id, loc, val); -+ -+ sr_share_write_word(dev, 1, loc, res); -+} -+ -+static u32 sr9700_get_link(struct net_device *netdev) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ u8 value = 0; -+ int rc = 0; -+ -+ /* Get the Link Status directly */ -+ sr_read_reg(dev, SR_NSR, &value); -+ if (value & NSR_LINKST) -+ rc = 1; -+ -+ return rc; -+} -+ -+static int sr9700_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ -+ return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); -+} -+ -+static const struct ethtool_ops sr9700_ethtool_ops = { -+ .get_drvinfo = usbnet_get_drvinfo, -+ .get_link = sr9700_get_link, -+ .get_msglevel = usbnet_get_msglevel, -+ .set_msglevel = usbnet_set_msglevel, -+ .get_eeprom_len = sr9700_get_eeprom_len, -+ .get_eeprom = sr9700_get_eeprom, -+ .get_settings = usbnet_get_settings, -+ .set_settings = usbnet_set_settings, -+ .nway_reset = usbnet_nway_reset, -+}; -+ -+static void sr9700_set_multicast(struct net_device *netdev) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ /* We use the 20 byte dev->data for our 8 byte filter buffer -+ * to avoid allocating memory that is tricky to free later -+ */ -+ u8 *hashes = (u8 *)&dev->data; -+ /* rx_ctl setting : enable, disable_long, disable_crc */ -+ u8 rx_ctl = RCR_RXEN | RCR_DIS_CRC | RCR_DIS_LONG; -+ -+ memset(hashes, 0x00, SR_MCAST_SIZE); -+ /* broadcast address */ -+ hashes[SR_MCAST_SIZE - 1] |= SR_MCAST_ADDR_FLAG; -+ if (netdev->flags & IFF_PROMISC) { -+ rx_ctl |= RCR_PRMSC; -+ } else if (netdev->flags & IFF_ALLMULTI || -+ netdev_mc_count(netdev) > SR_MCAST_MAX) { -+ rx_ctl |= RCR_RUNT; -+ } else if (!netdev_mc_empty(netdev)) { -+ struct netdev_hw_addr *ha; -+ -+ netdev_for_each_mc_addr(ha, netdev) { -+ u32 crc = ether_crc(ETH_ALEN, ha->addr) >> 26; -+ hashes[crc >> 3] |= 1 << (crc & 0x7); -+ } -+ } -+ -+ sr_write_async(dev, SR_MAR, SR_MCAST_SIZE, hashes); -+ sr_write_reg_async(dev, SR_RCR, rx_ctl); -+} -+ -+static int sr9700_set_mac_address(struct net_device *netdev, void *p) -+{ -+ struct usbnet *dev = netdev_priv(netdev); -+ struct sockaddr *addr = p; -+ -+ if (!is_valid_ether_addr(addr->sa_data)) { -+ netdev_err(netdev, "not setting invalid mac address %pM\n", -+ addr->sa_data); -+ return -EINVAL; -+ } -+ -+ memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); -+ sr_write_async(dev, SR_PAR, 6, netdev->dev_addr); -+ -+ return 0; -+} -+ -+static const struct net_device_ops sr9700_netdev_ops = { -+ .ndo_open = usbnet_open, -+ .ndo_stop = usbnet_stop, -+ .ndo_start_xmit = usbnet_start_xmit, -+ .ndo_tx_timeout = usbnet_tx_timeout, -+ .ndo_change_mtu = usbnet_change_mtu, -+ .ndo_validate_addr = eth_validate_addr, -+ .ndo_do_ioctl = sr9700_ioctl, -+ .ndo_set_rx_mode = sr9700_set_multicast, -+ .ndo_set_mac_address = sr9700_set_mac_address, -+}; -+ -+static int sr9700_bind(struct usbnet *dev, struct usb_interface *intf) -+{ -+ struct net_device *netdev; -+ struct mii_if_info *mii; -+ int ret; -+ -+ ret = usbnet_get_endpoints(dev, intf); -+ if (ret) -+ goto out; -+ -+ netdev = dev->net; -+ -+ netdev->netdev_ops = &sr9700_netdev_ops; -+ netdev->ethtool_ops = &sr9700_ethtool_ops; -+ netdev->hard_header_len += SR_TX_OVERHEAD; -+ dev->hard_mtu = netdev->mtu + netdev->hard_header_len; -+ /* bulkin buffer is preferably not less than 3K */ -+ dev->rx_urb_size = 3072; -+ -+ mii = &dev->mii; -+ mii->dev = netdev; -+ mii->mdio_read = sr_mdio_read; -+ mii->mdio_write = sr_mdio_write; -+ mii->phy_id_mask = 0x1f; -+ mii->reg_num_mask = 0x1f; -+ -+ sr_write_reg(dev, SR_NCR, NCR_RST); -+ udelay(20); -+ -+ /* read MAC -+ * After Chip Power on, the Chip will reload the MAC from -+ * EEPROM automatically to PAR. In case there is no EEPROM externally, -+ * a default MAC address is stored in PAR for making chip work properly. -+ */ -+ if (sr_read(dev, SR_PAR, ETH_ALEN, netdev->dev_addr) < 0) { -+ netdev_err(netdev, "Error reading MAC address\n"); -+ ret = -ENODEV; -+ goto out; -+ } -+ -+ /* power up and reset phy */ -+ sr_write_reg(dev, SR_PRR, PRR_PHY_RST); -+ /* at least 10ms, here 20ms for safe */ -+ mdelay(20); -+ sr_write_reg(dev, SR_PRR, 0); -+ /* at least 1ms, here 2ms for reading right register */ -+ udelay(2 * 1000); -+ -+ /* receive broadcast packets */ -+ sr9700_set_multicast(netdev); -+ -+ sr_mdio_write(netdev, mii->phy_id, MII_BMCR, BMCR_RESET); -+ sr_mdio_write(netdev, mii->phy_id, MII_ADVERTISE, ADVERTISE_ALL | -+ ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP); -+ mii_nway_restart(mii); -+ -+out: -+ return ret; -+} -+ -+static int sr9700_rx_fixup(struct usbnet *dev, struct sk_buff *skb) -+{ -+ struct sk_buff *sr_skb; -+ int len; -+ -+ /* skb content (packets) format : -+ * p0 p1 p2 ...... pm -+ * / \ -+ * / \ -+ * / \ -+ * / \ -+ * p0b0 p0b1 p0b2 p0b3 ...... p0b(n-4) p0b(n-3)...p0bn -+ * -+ * p0 : packet 0 -+ * p0b0 : packet 0 byte 0 -+ * -+ * b0: rx status -+ * b1: packet length (incl crc) low -+ * b2: packet length (incl crc) high -+ * b3..n-4: packet data -+ * bn-3..bn: ethernet packet crc -+ */ -+ if (unlikely(skb->len < SR_RX_OVERHEAD)) { -+ netdev_err(dev->net, "unexpected tiny rx frame\n"); -+ return 0; -+ } -+ -+ /* one skb may contains multiple packets */ -+ while (skb->len > SR_RX_OVERHEAD) { -+ if (skb->data[0] != 0x40) -+ return 0; -+ -+ /* ignore the CRC length */ -+ len = (skb->data[1] | (skb->data[2] << 8)) - 4; -+ -+ if (len > ETH_FRAME_LEN) -+ return 0; -+ -+ /* the last packet of current skb */ -+ if (skb->len == (len + SR_RX_OVERHEAD)) { -+ skb_pull(skb, 3); -+ skb->len = len; -+ skb_set_tail_pointer(skb, len); -+ skb->truesize = len + sizeof(struct sk_buff); -+ return 2; -+ } -+ -+ /* skb_clone is used for address align */ -+ sr_skb = skb_clone(skb, GFP_ATOMIC); -+ if (!sr_skb) -+ return 0; -+ -+ sr_skb->len = len; -+ sr_skb->data = skb->data + 3; -+ skb_set_tail_pointer(sr_skb, len); -+ sr_skb->truesize = len + sizeof(struct sk_buff); -+ usbnet_skb_return(dev, sr_skb); -+ -+ skb_pull(skb, len + SR_RX_OVERHEAD); -+ }; -+ -+ return 0; -+} -+ -+static struct sk_buff *sr9700_tx_fixup(struct usbnet *dev, struct sk_buff *skb, -+ gfp_t flags) -+{ -+ int len; -+ -+ /* SR9700 can only send out one ethernet packet at once. -+ * -+ * b0 b1 b2 b3 ...... b(n-4) b(n-3)...bn -+ * -+ * b0: rx status -+ * b1: packet length (incl crc) low -+ * b2: packet length (incl crc) high -+ * b3..n-4: packet data -+ * bn-3..bn: ethernet packet crc -+ */ -+ -+ len = skb->len; -+ -+ if (skb_headroom(skb) < SR_TX_OVERHEAD) { -+ struct sk_buff *skb2; -+ -+ skb2 = skb_copy_expand(skb, SR_TX_OVERHEAD, 0, flags); -+ dev_kfree_skb_any(skb); -+ skb = skb2; -+ if (!skb) -+ return NULL; -+ } -+ -+ __skb_push(skb, SR_TX_OVERHEAD); -+ -+ /* usbnet adds padding if length is a multiple of packet size -+ * if so, adjust length value in header -+ */ -+ if ((skb->len % dev->maxpacket) == 0) -+ len++; -+ -+ skb->data[0] = len; -+ skb->data[1] = len >> 8; -+ -+ return skb; -+} -+ -+static void sr9700_status(struct usbnet *dev, struct urb *urb) -+{ -+ int link; -+ u8 *buf; -+ -+ /* format: -+ b0: net status -+ b1: tx status 1 -+ b2: tx status 2 -+ b3: rx status -+ b4: rx overflow -+ b5: rx count -+ b6: tx count -+ b7: gpr -+ */ -+ -+ if (urb->actual_length < 8) -+ return; -+ -+ buf = urb->transfer_buffer; -+ -+ link = !!(buf[0] & 0x40); -+ if (netif_carrier_ok(dev->net) != link) { -+ usbnet_link_change(dev, link, 1); -+ netdev_dbg(dev->net, "Link Status is: %d\n", link); -+ } -+} -+ -+static int sr9700_link_reset(struct usbnet *dev) -+{ -+ struct ethtool_cmd ecmd; -+ -+ mii_check_media(&dev->mii, 1, 1); -+ mii_ethtool_gset(&dev->mii, &ecmd); -+ -+ netdev_dbg(dev->net, "link_reset() speed: %d duplex: %d\n", -+ ecmd.speed, ecmd.duplex); -+ -+ return 0; -+} -+ -+static const struct driver_info sr9700_driver_info = { -+ .description = "CoreChip SR9700 USB Ethernet", -+ .flags = FLAG_ETHER, -+ .bind = sr9700_bind, -+ .rx_fixup = sr9700_rx_fixup, -+ .tx_fixup = sr9700_tx_fixup, -+ .status = sr9700_status, -+ .link_reset = sr9700_link_reset, -+ .reset = sr9700_link_reset, -+}; -+ -+static const struct usb_device_id products[] = { -+ { -+ USB_DEVICE(0x0fe6, 0x9700), /* SR9700 device */ -+ .driver_info = (unsigned long)&sr9700_driver_info, -+ }, -+ {}, /* END */ -+}; -+ -+MODULE_DEVICE_TABLE(usb, products); -+ -+static struct usb_driver sr9700_usb_driver = { -+ .name = "sr9700", -+ .id_table = products, -+ .probe = usbnet_probe, -+ .disconnect = usbnet_disconnect, -+ .suspend = usbnet_suspend, -+ .resume = usbnet_resume, -+ .disable_hub_initiated_lpm = 1, -+}; -+ -+module_usb_driver(sr9700_usb_driver); -+ -+MODULE_AUTHOR("liujl "); -+MODULE_DESCRIPTION("SR9700 one chip USB 1.1 USB to Ethernet device from http://www.corechip-sz.com/"); -+MODULE_LICENSE("GPL"); -diff -Naur backports-4.2.6-1.org/drivers/net/usb/sr9700.h backports-4.2.6-1/drivers/net/usb/sr9700.h ---- backports-4.2.6-1.org/drivers/net/usb/sr9700.h 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/sr9700.h 2016-06-28 14:35:18.011973884 +0200 -@@ -0,0 +1,173 @@ -+/* -+ * CoreChip-sz SR9700 one chip USB 1.1 Ethernet Devices -+ * -+ * Author : Liu Junliang -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * version 2 as published by the Free Software Foundation. -+ */ -+ -+#ifndef _SR9700_H -+#define _SR9700_H -+ -+/* sr9700 spec. register table on Linux platform */ -+ -+/* Network Control Reg */ -+#define SR_NCR 0x00 -+#define NCR_RST (1 << 0) -+#define NCR_LBK (3 << 1) -+#define NCR_FDX (1 << 3) -+#define NCR_WAKEEN (1 << 6) -+/* Network Status Reg */ -+#define SR_NSR 0x01 -+#define NSR_RXRDY (1 << 0) -+#define NSR_RXOV (1 << 1) -+#define NSR_TX1END (1 << 2) -+#define NSR_TX2END (1 << 3) -+#define NSR_TXFULL (1 << 4) -+#define NSR_WAKEST (1 << 5) -+#define NSR_LINKST (1 << 6) -+#define NSR_SPEED (1 << 7) -+/* Tx Control Reg */ -+#define SR_TCR 0x02 -+#define TCR_CRC_DIS (1 << 1) -+#define TCR_PAD_DIS (1 << 2) -+#define TCR_LC_CARE (1 << 3) -+#define TCR_CRS_CARE (1 << 4) -+#define TCR_EXCECM (1 << 5) -+#define TCR_LF_EN (1 << 6) -+/* Tx Status Reg for Packet Index 1 */ -+#define SR_TSR1 0x03 -+#define TSR1_EC (1 << 2) -+#define TSR1_COL (1 << 3) -+#define TSR1_LC (1 << 4) -+#define TSR1_NC (1 << 5) -+#define TSR1_LOC (1 << 6) -+#define TSR1_TLF (1 << 7) -+/* Tx Status Reg for Packet Index 2 */ -+#define SR_TSR2 0x04 -+#define TSR2_EC (1 << 2) -+#define TSR2_COL (1 << 3) -+#define TSR2_LC (1 << 4) -+#define TSR2_NC (1 << 5) -+#define TSR2_LOC (1 << 6) -+#define TSR2_TLF (1 << 7) -+/* Rx Control Reg*/ -+#define SR_RCR 0x05 -+#define RCR_RXEN (1 << 0) -+#define RCR_PRMSC (1 << 1) -+#define RCR_RUNT (1 << 2) -+#define RCR_ALL (1 << 3) -+#define RCR_DIS_CRC (1 << 4) -+#define RCR_DIS_LONG (1 << 5) -+/* Rx Status Reg */ -+#define SR_RSR 0x06 -+#define RSR_AE (1 << 2) -+#define RSR_MF (1 << 6) -+#define RSR_RF (1 << 7) -+/* Rx Overflow Counter Reg */ -+#define SR_ROCR 0x07 -+#define ROCR_ROC (0x7F << 0) -+#define ROCR_RXFU (1 << 7) -+/* Back Pressure Threshold Reg */ -+#define SR_BPTR 0x08 -+#define BPTR_JPT (0x0F << 0) -+#define BPTR_BPHW (0x0F << 4) -+/* Flow Control Threshold Reg */ -+#define SR_FCTR 0x09 -+#define FCTR_LWOT (0x0F << 0) -+#define FCTR_HWOT (0x0F << 4) -+/* rx/tx Flow Control Reg */ -+#define SR_FCR 0x0A -+#define FCR_FLCE (1 << 0) -+#define FCR_BKPA (1 << 4) -+#define FCR_TXPEN (1 << 5) -+#define FCR_TXPF (1 << 6) -+#define FCR_TXP0 (1 << 7) -+/* Eeprom & Phy Control Reg */ -+#define SR_EPCR 0x0B -+#define EPCR_ERRE (1 << 0) -+#define EPCR_ERPRW (1 << 1) -+#define EPCR_ERPRR (1 << 2) -+#define EPCR_EPOS (1 << 3) -+#define EPCR_WEP (1 << 4) -+/* Eeprom & Phy Address Reg */ -+#define SR_EPAR 0x0C -+#define EPAR_EROA (0x3F << 0) -+#define EPAR_PHY_ADR_MASK (0x03 << 6) -+#define EPAR_PHY_ADR (0x01 << 6) -+/* Eeprom & Phy Data Reg */ -+#define SR_EPDR 0x0D /* 0x0D ~ 0x0E for Data Reg Low & High */ -+/* Wakeup Control Reg */ -+#define SR_WCR 0x0F -+#define WCR_MAGICST (1 << 0) -+#define WCR_LINKST (1 << 2) -+#define WCR_MAGICEN (1 << 3) -+#define WCR_LINKEN (1 << 5) -+/* Physical Address Reg */ -+#define SR_PAR 0x10 /* 0x10 ~ 0x15 6 bytes for PAR */ -+/* Multicast Address Reg */ -+#define SR_MAR 0x16 /* 0x16 ~ 0x1D 8 bytes for MAR */ -+/* 0x1e unused */ -+/* Phy Reset Reg */ -+#define SR_PRR 0x1F -+#define PRR_PHY_RST (1 << 0) -+/* Tx sdram Write Pointer Address Low */ -+#define SR_TWPAL 0x20 -+/* Tx sdram Write Pointer Address High */ -+#define SR_TWPAH 0x21 -+/* Tx sdram Read Pointer Address Low */ -+#define SR_TRPAL 0x22 -+/* Tx sdram Read Pointer Address High */ -+#define SR_TRPAH 0x23 -+/* Rx sdram Write Pointer Address Low */ -+#define SR_RWPAL 0x24 -+/* Rx sdram Write Pointer Address High */ -+#define SR_RWPAH 0x25 -+/* Rx sdram Read Pointer Address Low */ -+#define SR_RRPAL 0x26 -+/* Rx sdram Read Pointer Address High */ -+#define SR_RRPAH 0x27 -+/* Vendor ID register */ -+#define SR_VID 0x28 /* 0x28 ~ 0x29 2 bytes for VID */ -+/* Product ID register */ -+#define SR_PID 0x2A /* 0x2A ~ 0x2B 2 bytes for PID */ -+/* CHIP Revision register */ -+#define SR_CHIPR 0x2C -+/* 0x2D --> 0xEF unused */ -+/* USB Device Address */ -+#define SR_USBDA 0xF0 -+#define USBDA_USBFA (0x7F << 0) -+/* RX packet Counter Reg */ -+#define SR_RXC 0xF1 -+/* Tx packet Counter & USB Status Reg */ -+#define SR_TXC_USBS 0xF2 -+#define TXC_USBS_TXC0 (1 << 0) -+#define TXC_USBS_TXC1 (1 << 1) -+#define TXC_USBS_TXC2 (1 << 2) -+#define TXC_USBS_EP1RDY (1 << 5) -+#define TXC_USBS_SUSFLAG (1 << 6) -+#define TXC_USBS_RXFAULT (1 << 7) -+/* USB Control register */ -+#define SR_USBC 0xF4 -+#define USBC_EP3NAK (1 << 4) -+#define USBC_EP3ACK (1 << 5) -+ -+/* Register access commands and flags */ -+#define SR_RD_REGS 0x00 -+#define SR_WR_REGS 0x01 -+#define SR_WR_REG 0x03 -+#define SR_REQ_RD_REG (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE) -+#define SR_REQ_WR_REG (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE) -+ -+/* parameters */ -+#define SR_SHARE_TIMEOUT 1000 -+#define SR_EEPROM_LEN 256 -+#define SR_MCAST_SIZE 8 -+#define SR_MCAST_ADDR_FLAG 0x80 -+#define SR_MCAST_MAX 64 -+#define SR_TX_OVERHEAD 2 /* 2bytes header */ -+#define SR_RX_OVERHEAD 7 /* 3bytes header + 4crc tail */ -+ -+#endif /* _SR9700_H */ -diff -Naur backports-4.2.6-1.org/drivers/net/usb/sr9800.c backports-4.2.6-1/drivers/net/usb/sr9800.c ---- backports-4.2.6-1.org/drivers/net/usb/sr9800.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/sr9800.c 2016-06-28 14:35:18.015307217 +0200 -@@ -0,0 +1,875 @@ -+/* CoreChip-sz SR9800 one chip USB 2.0 Ethernet Devices -+ * -+ * Author : Liu Junliang -+ * -+ * Based on asix_common.c, asix_devices.c -+ * -+ * This file is licensed under the terms of the GNU General Public License -+ * version 2. This program is licensed "as is" without any warranty of any -+ * kind, whether express or implied.* -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "sr9800.h" -+ -+static int sr_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, -+ u16 size, void *data) -+{ -+ int err; -+ -+ err = usbnet_read_cmd(dev, cmd, SR_REQ_RD_REG, value, index, -+ data, size); -+ if ((err != size) && (err >= 0)) -+ err = -EINVAL; -+ -+ return err; -+} -+ -+static int sr_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, -+ u16 size, void *data) -+{ -+ int err; -+ -+ err = usbnet_write_cmd(dev, cmd, SR_REQ_WR_REG, value, index, -+ data, size); -+ if ((err != size) && (err >= 0)) -+ err = -EINVAL; -+ -+ return err; -+} -+ -+static void -+sr_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index, -+ u16 size, void *data) -+{ -+ usbnet_write_cmd_async(dev, cmd, SR_REQ_WR_REG, value, index, data, -+ size); -+} -+ -+static int sr_rx_fixup(struct usbnet *dev, struct sk_buff *skb) -+{ -+ int offset = 0; -+ -+ /* This check is no longer done by usbnet */ -+ if (skb->len < dev->net->hard_header_len) -+ return 0; -+ -+ while (offset + sizeof(u32) < skb->len) { -+ struct sk_buff *sr_skb; -+ u16 size; -+ u32 header = get_unaligned_le32(skb->data + offset); -+ -+ offset += sizeof(u32); -+ /* get the packet length */ -+ size = (u16) (header & 0x7ff); -+ if (size != ((~header >> 16) & 0x07ff)) { -+ netdev_err(dev->net, "%s : Bad Header Length\n", -+ __func__); -+ return 0; -+ } -+ -+ if ((size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) || -+ (size + offset > skb->len)) { -+ netdev_err(dev->net, "%s : Bad RX Length %d\n", -+ __func__, size); -+ return 0; -+ } -+ sr_skb = netdev_alloc_skb_ip_align(dev->net, size); -+ if (!sr_skb) -+ return 0; -+ -+ skb_put(sr_skb, size); -+ memcpy(sr_skb->data, skb->data + offset, size); -+ usbnet_skb_return(dev, sr_skb); -+ -+ offset += (size + 1) & 0xfffe; -+ } -+ -+ if (skb->len != offset) { -+ netdev_err(dev->net, "%s : Bad SKB Length %d\n", __func__, -+ skb->len); -+ return 0; -+ } -+ -+ return 1; -+} -+ -+static struct sk_buff *sr_tx_fixup(struct usbnet *dev, struct sk_buff *skb, -+ gfp_t flags) -+{ -+ int headroom = skb_headroom(skb); -+ int tailroom = skb_tailroom(skb); -+ u32 padbytes = 0xffff0000; -+ u32 packet_len; -+ int padlen; -+ -+ padlen = ((skb->len + 4) % (dev->maxpacket - 1)) ? 0 : 4; -+ -+ if ((!skb_cloned(skb)) && ((headroom + tailroom) >= (4 + padlen))) { -+ if ((headroom < 4) || (tailroom < padlen)) { -+ skb->data = memmove(skb->head + 4, skb->data, -+ skb->len); -+ skb_set_tail_pointer(skb, skb->len); -+ } -+ } else { -+ struct sk_buff *skb2; -+ skb2 = skb_copy_expand(skb, 4, padlen, flags); -+ dev_kfree_skb_any(skb); -+ skb = skb2; -+ if (!skb) -+ return NULL; -+ } -+ -+ skb_push(skb, 4); -+ packet_len = (((skb->len - 4) ^ 0x0000ffff) << 16) + (skb->len - 4); -+ cpu_to_le32s(&packet_len); -+ skb_copy_to_linear_data(skb, &packet_len, sizeof(packet_len)); -+ -+ if (padlen) { -+ cpu_to_le32s(&padbytes); -+ memcpy(skb_tail_pointer(skb), &padbytes, sizeof(padbytes)); -+ skb_put(skb, sizeof(padbytes)); -+ } -+ -+ usbnet_set_skb_tx_stats(skb, 1, 0); -+ return skb; -+} -+ -+static void sr_status(struct usbnet *dev, struct urb *urb) -+{ -+ struct sr9800_int_data *event; -+ int link; -+ -+ if (urb->actual_length < 8) -+ return; -+ -+ event = urb->transfer_buffer; -+ link = event->link & 0x01; -+ if (netif_carrier_ok(dev->net) != link) { -+ usbnet_link_change(dev, link, 1); -+ netdev_dbg(dev->net, "Link Status is: %d\n", link); -+ } -+ -+ return; -+} -+ -+static inline int sr_set_sw_mii(struct usbnet *dev) -+{ -+ int ret; -+ -+ ret = sr_write_cmd(dev, SR_CMD_SET_SW_MII, 0x0000, 0, 0, NULL); -+ if (ret < 0) -+ netdev_err(dev->net, "Failed to enable software MII access\n"); -+ return ret; -+} -+ -+static inline int sr_set_hw_mii(struct usbnet *dev) -+{ -+ int ret; -+ -+ ret = sr_write_cmd(dev, SR_CMD_SET_HW_MII, 0x0000, 0, 0, NULL); -+ if (ret < 0) -+ netdev_err(dev->net, "Failed to enable hardware MII access\n"); -+ return ret; -+} -+ -+static inline int sr_get_phy_addr(struct usbnet *dev) -+{ -+ u8 buf[2]; -+ int ret; -+ -+ ret = sr_read_cmd(dev, SR_CMD_READ_PHY_ID, 0, 0, 2, buf); -+ if (ret < 0) { -+ netdev_err(dev->net, "%s : Error reading PHYID register:%02x\n", -+ __func__, ret); -+ goto out; -+ } -+ netdev_dbg(dev->net, "%s : returning 0x%04x\n", __func__, -+ *((__le16 *)buf)); -+ -+ ret = buf[1]; -+ -+out: -+ return ret; -+} -+ -+static int sr_sw_reset(struct usbnet *dev, u8 flags) -+{ -+ int ret; -+ -+ ret = sr_write_cmd(dev, SR_CMD_SW_RESET, flags, 0, 0, NULL); -+ if (ret < 0) -+ netdev_err(dev->net, "Failed to send software reset:%02x\n", -+ ret); -+ -+ return ret; -+} -+ -+static u16 sr_read_rx_ctl(struct usbnet *dev) -+{ -+ __le16 v; -+ int ret; -+ -+ ret = sr_read_cmd(dev, SR_CMD_READ_RX_CTL, 0, 0, 2, &v); -+ if (ret < 0) { -+ netdev_err(dev->net, "Error reading RX_CTL register:%02x\n", -+ ret); -+ goto out; -+ } -+ -+ ret = le16_to_cpu(v); -+out: -+ return ret; -+} -+ -+static int sr_write_rx_ctl(struct usbnet *dev, u16 mode) -+{ -+ int ret; -+ -+ netdev_dbg(dev->net, "%s : mode = 0x%04x\n", __func__, mode); -+ ret = sr_write_cmd(dev, SR_CMD_WRITE_RX_CTL, mode, 0, 0, NULL); -+ if (ret < 0) -+ netdev_err(dev->net, -+ "Failed to write RX_CTL mode to 0x%04x:%02x\n", -+ mode, ret); -+ -+ return ret; -+} -+ -+static u16 sr_read_medium_status(struct usbnet *dev) -+{ -+ __le16 v; -+ int ret; -+ -+ ret = sr_read_cmd(dev, SR_CMD_READ_MEDIUM_STATUS, 0, 0, 2, &v); -+ if (ret < 0) { -+ netdev_err(dev->net, -+ "Error reading Medium Status register:%02x\n", ret); -+ return ret; /* TODO: callers not checking for error ret */ -+ } -+ -+ return le16_to_cpu(v); -+} -+ -+static int sr_write_medium_mode(struct usbnet *dev, u16 mode) -+{ -+ int ret; -+ -+ netdev_dbg(dev->net, "%s : mode = 0x%04x\n", __func__, mode); -+ ret = sr_write_cmd(dev, SR_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL); -+ if (ret < 0) -+ netdev_err(dev->net, -+ "Failed to write Medium Mode mode to 0x%04x:%02x\n", -+ mode, ret); -+ return ret; -+} -+ -+static int sr_write_gpio(struct usbnet *dev, u16 value, int sleep) -+{ -+ int ret; -+ -+ netdev_dbg(dev->net, "%s : value = 0x%04x\n", __func__, value); -+ ret = sr_write_cmd(dev, SR_CMD_WRITE_GPIOS, value, 0, 0, NULL); -+ if (ret < 0) -+ netdev_err(dev->net, "Failed to write GPIO value 0x%04x:%02x\n", -+ value, ret); -+ if (sleep) -+ msleep(sleep); -+ -+ return ret; -+} -+ -+/* SR9800 have a 16-bit RX_CTL value */ -+static void sr_set_multicast(struct net_device *net) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ struct sr_data *data = (struct sr_data *)&dev->data; -+ u16 rx_ctl = SR_DEFAULT_RX_CTL; -+ -+ if (net->flags & IFF_PROMISC) { -+ rx_ctl |= SR_RX_CTL_PRO; -+ } else if (net->flags & IFF_ALLMULTI || -+ netdev_mc_count(net) > SR_MAX_MCAST) { -+ rx_ctl |= SR_RX_CTL_AMALL; -+ } else if (netdev_mc_empty(net)) { -+ /* just broadcast and directed */ -+ } else { -+ /* We use the 20 byte dev->data -+ * for our 8 byte filter buffer -+ * to avoid allocating memory that -+ * is tricky to free later -+ */ -+ struct netdev_hw_addr *ha; -+ u32 crc_bits; -+ -+ memset(data->multi_filter, 0, SR_MCAST_FILTER_SIZE); -+ -+ /* Build the multicast hash filter. */ -+ netdev_for_each_mc_addr(ha, net) { -+ crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26; -+ data->multi_filter[crc_bits >> 3] |= -+ 1 << (crc_bits & 7); -+ } -+ -+ sr_write_cmd_async(dev, SR_CMD_WRITE_MULTI_FILTER, 0, 0, -+ SR_MCAST_FILTER_SIZE, data->multi_filter); -+ -+ rx_ctl |= SR_RX_CTL_AM; -+ } -+ -+ sr_write_cmd_async(dev, SR_CMD_WRITE_RX_CTL, rx_ctl, 0, 0, NULL); -+} -+ -+static int sr_mdio_read(struct net_device *net, int phy_id, int loc) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ __le16 res; -+ -+ mutex_lock(&dev->phy_mutex); -+ sr_set_sw_mii(dev); -+ sr_read_cmd(dev, SR_CMD_READ_MII_REG, phy_id, (__u16)loc, 2, &res); -+ sr_set_hw_mii(dev); -+ mutex_unlock(&dev->phy_mutex); -+ -+ netdev_dbg(dev->net, -+ "%s : phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n", __func__, -+ phy_id, loc, le16_to_cpu(res)); -+ -+ return le16_to_cpu(res); -+} -+ -+static void -+sr_mdio_write(struct net_device *net, int phy_id, int loc, int val) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ __le16 res = cpu_to_le16(val); -+ -+ netdev_dbg(dev->net, -+ "%s : phy_id=0x%02x, loc=0x%02x, val=0x%04x\n", __func__, -+ phy_id, loc, val); -+ mutex_lock(&dev->phy_mutex); -+ sr_set_sw_mii(dev); -+ sr_write_cmd(dev, SR_CMD_WRITE_MII_REG, phy_id, (__u16)loc, 2, &res); -+ sr_set_hw_mii(dev); -+ mutex_unlock(&dev->phy_mutex); -+} -+ -+/* Get the PHY Identifier from the PHYSID1 & PHYSID2 MII registers */ -+static u32 sr_get_phyid(struct usbnet *dev) -+{ -+ int phy_reg; -+ u32 phy_id; -+ int i; -+ -+ /* Poll for the rare case the FW or phy isn't ready yet. */ -+ for (i = 0; i < 100; i++) { -+ phy_reg = sr_mdio_read(dev->net, dev->mii.phy_id, MII_PHYSID1); -+ if (phy_reg != 0 && phy_reg != 0xFFFF) -+ break; -+ mdelay(1); -+ } -+ -+ if (phy_reg <= 0 || phy_reg == 0xFFFF) -+ return 0; -+ -+ phy_id = (phy_reg & 0xffff) << 16; -+ -+ phy_reg = sr_mdio_read(dev->net, dev->mii.phy_id, MII_PHYSID2); -+ if (phy_reg < 0) -+ return 0; -+ -+ phy_id |= (phy_reg & 0xffff); -+ -+ return phy_id; -+} -+ -+static void -+sr_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ u8 opt; -+ -+ if (sr_read_cmd(dev, SR_CMD_READ_MONITOR_MODE, 0, 0, 1, &opt) < 0) { -+ wolinfo->supported = 0; -+ wolinfo->wolopts = 0; -+ return; -+ } -+ wolinfo->supported = WAKE_PHY | WAKE_MAGIC; -+ wolinfo->wolopts = 0; -+ if (opt & SR_MONITOR_LINK) -+ wolinfo->wolopts |= WAKE_PHY; -+ if (opt & SR_MONITOR_MAGIC) -+ wolinfo->wolopts |= WAKE_MAGIC; -+} -+ -+static int -+sr_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ u8 opt = 0; -+ -+ if (wolinfo->wolopts & WAKE_PHY) -+ opt |= SR_MONITOR_LINK; -+ if (wolinfo->wolopts & WAKE_MAGIC) -+ opt |= SR_MONITOR_MAGIC; -+ -+ if (sr_write_cmd(dev, SR_CMD_WRITE_MONITOR_MODE, -+ opt, 0, 0, NULL) < 0) -+ return -EINVAL; -+ -+ return 0; -+} -+ -+static int sr_get_eeprom_len(struct net_device *net) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ struct sr_data *data = (struct sr_data *)&dev->data; -+ -+ return data->eeprom_len; -+} -+ -+static int sr_get_eeprom(struct net_device *net, -+ struct ethtool_eeprom *eeprom, u8 *data) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ __le16 *ebuf = (__le16 *)data; -+ int ret; -+ int i; -+ -+ /* Crude hack to ensure that we don't overwrite memory -+ * if an odd length is supplied -+ */ -+ if (eeprom->len % 2) -+ return -EINVAL; -+ -+ eeprom->magic = SR_EEPROM_MAGIC; -+ -+ /* sr9800 returns 2 bytes from eeprom on read */ -+ for (i = 0; i < eeprom->len / 2; i++) { -+ ret = sr_read_cmd(dev, SR_CMD_READ_EEPROM, eeprom->offset + i, -+ 0, 2, &ebuf[i]); -+ if (ret < 0) -+ return -EINVAL; -+ } -+ return 0; -+} -+ -+static void sr_get_drvinfo(struct net_device *net, -+ struct ethtool_drvinfo *info) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ struct sr_data *data = (struct sr_data *)&dev->data; -+ -+ /* Inherit standard device info */ -+ usbnet_get_drvinfo(net, info); -+ strncpy(info->driver, DRIVER_NAME, sizeof(info->driver)); -+ strncpy(info->version, DRIVER_VERSION, sizeof(info->version)); -+ info->eedump_len = data->eeprom_len; -+} -+ -+static u32 sr_get_link(struct net_device *net) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ -+ return mii_link_ok(&dev->mii); -+} -+ -+static int sr_ioctl(struct net_device *net, struct ifreq *rq, int cmd) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ -+ return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); -+} -+ -+static int sr_set_mac_address(struct net_device *net, void *p) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ struct sr_data *data = (struct sr_data *)&dev->data; -+ struct sockaddr *addr = p; -+ -+ if (netif_running(net)) -+ return -EBUSY; -+ if (!is_valid_ether_addr(addr->sa_data)) -+ return -EADDRNOTAVAIL; -+ -+ memcpy(net->dev_addr, addr->sa_data, ETH_ALEN); -+ -+ /* We use the 20 byte dev->data -+ * for our 6 byte mac buffer -+ * to avoid allocating memory that -+ * is tricky to free later -+ */ -+ memcpy(data->mac_addr, addr->sa_data, ETH_ALEN); -+ sr_write_cmd_async(dev, SR_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN, -+ data->mac_addr); -+ -+ return 0; -+} -+ -+static const struct ethtool_ops sr9800_ethtool_ops = { -+ .get_drvinfo = sr_get_drvinfo, -+ .get_link = sr_get_link, -+ .get_msglevel = usbnet_get_msglevel, -+ .set_msglevel = usbnet_set_msglevel, -+ .get_wol = sr_get_wol, -+ .set_wol = sr_set_wol, -+ .get_eeprom_len = sr_get_eeprom_len, -+ .get_eeprom = sr_get_eeprom, -+ .get_settings = usbnet_get_settings, -+ .set_settings = usbnet_set_settings, -+ .nway_reset = usbnet_nway_reset, -+}; -+ -+static int sr9800_link_reset(struct usbnet *dev) -+{ -+ struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET }; -+ u16 mode; -+ -+ mii_check_media(&dev->mii, 1, 1); -+ mii_ethtool_gset(&dev->mii, &ecmd); -+ mode = SR9800_MEDIUM_DEFAULT; -+ -+ if (ethtool_cmd_speed(&ecmd) != SPEED_100) -+ mode &= ~SR_MEDIUM_PS; -+ -+ if (ecmd.duplex != DUPLEX_FULL) -+ mode &= ~SR_MEDIUM_FD; -+ -+ netdev_dbg(dev->net, "%s : speed: %u duplex: %d mode: 0x%04x\n", -+ __func__, ethtool_cmd_speed(&ecmd), ecmd.duplex, mode); -+ -+ sr_write_medium_mode(dev, mode); -+ -+ return 0; -+} -+ -+ -+static int sr9800_set_default_mode(struct usbnet *dev) -+{ -+ u16 rx_ctl; -+ int ret; -+ -+ sr_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET); -+ sr_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, -+ ADVERTISE_ALL | ADVERTISE_CSMA); -+ mii_nway_restart(&dev->mii); -+ -+ ret = sr_write_medium_mode(dev, SR9800_MEDIUM_DEFAULT); -+ if (ret < 0) -+ goto out; -+ -+ ret = sr_write_cmd(dev, SR_CMD_WRITE_IPG012, -+ SR9800_IPG0_DEFAULT | SR9800_IPG1_DEFAULT, -+ SR9800_IPG2_DEFAULT, 0, NULL); -+ if (ret < 0) { -+ netdev_dbg(dev->net, "Write IPG,IPG1,IPG2 failed: %d\n", ret); -+ goto out; -+ } -+ -+ /* Set RX_CTL to default values with 2k buffer, and enable cactus */ -+ ret = sr_write_rx_ctl(dev, SR_DEFAULT_RX_CTL); -+ if (ret < 0) -+ goto out; -+ -+ rx_ctl = sr_read_rx_ctl(dev); -+ netdev_dbg(dev->net, "RX_CTL is 0x%04x after all initializations\n", -+ rx_ctl); -+ -+ rx_ctl = sr_read_medium_status(dev); -+ netdev_dbg(dev->net, "Medium Status:0x%04x after all initializations\n", -+ rx_ctl); -+ -+ return 0; -+out: -+ return ret; -+} -+ -+static int sr9800_reset(struct usbnet *dev) -+{ -+ struct sr_data *data = (struct sr_data *)&dev->data; -+ int ret, embd_phy; -+ u16 rx_ctl; -+ -+ ret = sr_write_gpio(dev, -+ SR_GPIO_RSE | SR_GPIO_GPO_2 | SR_GPIO_GPO2EN, 5); -+ if (ret < 0) -+ goto out; -+ -+ embd_phy = ((sr_get_phy_addr(dev) & 0x1f) == 0x10 ? 1 : 0); -+ -+ ret = sr_write_cmd(dev, SR_CMD_SW_PHY_SELECT, embd_phy, 0, 0, NULL); -+ if (ret < 0) { -+ netdev_dbg(dev->net, "Select PHY #1 failed: %d\n", ret); -+ goto out; -+ } -+ -+ ret = sr_sw_reset(dev, SR_SWRESET_IPPD | SR_SWRESET_PRL); -+ if (ret < 0) -+ goto out; -+ -+ msleep(150); -+ -+ ret = sr_sw_reset(dev, SR_SWRESET_CLEAR); -+ if (ret < 0) -+ goto out; -+ -+ msleep(150); -+ -+ if (embd_phy) { -+ ret = sr_sw_reset(dev, SR_SWRESET_IPRL); -+ if (ret < 0) -+ goto out; -+ } else { -+ ret = sr_sw_reset(dev, SR_SWRESET_PRTE); -+ if (ret < 0) -+ goto out; -+ } -+ -+ msleep(150); -+ rx_ctl = sr_read_rx_ctl(dev); -+ netdev_dbg(dev->net, "RX_CTL is 0x%04x after software reset\n", rx_ctl); -+ ret = sr_write_rx_ctl(dev, 0x0000); -+ if (ret < 0) -+ goto out; -+ -+ rx_ctl = sr_read_rx_ctl(dev); -+ netdev_dbg(dev->net, "RX_CTL is 0x%04x setting to 0x0000\n", rx_ctl); -+ -+ ret = sr_sw_reset(dev, SR_SWRESET_PRL); -+ if (ret < 0) -+ goto out; -+ -+ msleep(150); -+ -+ ret = sr_sw_reset(dev, SR_SWRESET_IPRL | SR_SWRESET_PRL); -+ if (ret < 0) -+ goto out; -+ -+ msleep(150); -+ -+ ret = sr9800_set_default_mode(dev); -+ if (ret < 0) -+ goto out; -+ -+ /* Rewrite MAC address */ -+ memcpy(data->mac_addr, dev->net->dev_addr, ETH_ALEN); -+ ret = sr_write_cmd(dev, SR_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN, -+ data->mac_addr); -+ if (ret < 0) -+ goto out; -+ -+ return 0; -+ -+out: -+ return ret; -+} -+ -+static const struct net_device_ops sr9800_netdev_ops = { -+ .ndo_open = usbnet_open, -+ .ndo_stop = usbnet_stop, -+ .ndo_start_xmit = usbnet_start_xmit, -+ .ndo_tx_timeout = usbnet_tx_timeout, -+ .ndo_change_mtu = usbnet_change_mtu, -+ .ndo_set_mac_address = sr_set_mac_address, -+ .ndo_validate_addr = eth_validate_addr, -+ .ndo_do_ioctl = sr_ioctl, -+ .ndo_set_rx_mode = sr_set_multicast, -+}; -+ -+static int sr9800_phy_powerup(struct usbnet *dev) -+{ -+ int ret; -+ -+ /* set the embedded Ethernet PHY in power-down state */ -+ ret = sr_sw_reset(dev, SR_SWRESET_IPPD | SR_SWRESET_IPRL); -+ if (ret < 0) { -+ netdev_err(dev->net, "Failed to power down PHY : %d\n", ret); -+ return ret; -+ } -+ msleep(20); -+ -+ /* set the embedded Ethernet PHY in power-up state */ -+ ret = sr_sw_reset(dev, SR_SWRESET_IPRL); -+ if (ret < 0) { -+ netdev_err(dev->net, "Failed to reset PHY: %d\n", ret); -+ return ret; -+ } -+ msleep(600); -+ -+ /* set the embedded Ethernet PHY in reset state */ -+ ret = sr_sw_reset(dev, SR_SWRESET_CLEAR); -+ if (ret < 0) { -+ netdev_err(dev->net, "Failed to power up PHY: %d\n", ret); -+ return ret; -+ } -+ msleep(20); -+ -+ /* set the embedded Ethernet PHY in power-up state */ -+ ret = sr_sw_reset(dev, SR_SWRESET_IPRL); -+ if (ret < 0) { -+ netdev_err(dev->net, "Failed to reset PHY: %d\n", ret); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int sr9800_bind(struct usbnet *dev, struct usb_interface *intf) -+{ -+ struct sr_data *data = (struct sr_data *)&dev->data; -+ u16 led01_mux, led23_mux; -+ int ret, embd_phy; -+ u32 phyid; -+ u16 rx_ctl; -+ -+ data->eeprom_len = SR9800_EEPROM_LEN; -+ -+ usbnet_get_endpoints(dev, intf); -+ -+ /* LED Setting Rule : -+ * AABB:CCDD -+ * AA : MFA0(LED0) -+ * BB : MFA1(LED1) -+ * CC : MFA2(LED2), Reserved for SR9800 -+ * DD : MFA3(LED3), Reserved for SR9800 -+ */ -+ led01_mux = (SR_LED_MUX_LINK_ACTIVE << 8) | SR_LED_MUX_LINK; -+ led23_mux = (SR_LED_MUX_LINK_ACTIVE << 8) | SR_LED_MUX_TX_ACTIVE; -+ ret = sr_write_cmd(dev, SR_CMD_LED_MUX, led01_mux, led23_mux, 0, NULL); -+ if (ret < 0) { -+ netdev_err(dev->net, "set LINK LED failed : %d\n", ret); -+ goto out; -+ } -+ -+ /* Get the MAC address */ -+ ret = sr_read_cmd(dev, SR_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, -+ dev->net->dev_addr); -+ if (ret < 0) { -+ netdev_dbg(dev->net, "Failed to read MAC address: %d\n", ret); -+ return ret; -+ } -+ netdev_dbg(dev->net, "mac addr : %pM\n", dev->net->dev_addr); -+ -+ /* Initialize MII structure */ -+ dev->mii.dev = dev->net; -+ dev->mii.mdio_read = sr_mdio_read; -+ dev->mii.mdio_write = sr_mdio_write; -+ dev->mii.phy_id_mask = 0x1f; -+ dev->mii.reg_num_mask = 0x1f; -+ dev->mii.phy_id = sr_get_phy_addr(dev); -+ -+ dev->net->netdev_ops = &sr9800_netdev_ops; -+ dev->net->ethtool_ops = &sr9800_ethtool_ops; -+ -+ embd_phy = ((dev->mii.phy_id & 0x1f) == 0x10 ? 1 : 0); -+ /* Reset the PHY to normal operation mode */ -+ ret = sr_write_cmd(dev, SR_CMD_SW_PHY_SELECT, embd_phy, 0, 0, NULL); -+ if (ret < 0) { -+ netdev_dbg(dev->net, "Select PHY #1 failed: %d\n", ret); -+ return ret; -+ } -+ -+ /* Init PHY routine */ -+ ret = sr9800_phy_powerup(dev); -+ if (ret < 0) -+ goto out; -+ -+ rx_ctl = sr_read_rx_ctl(dev); -+ netdev_dbg(dev->net, "RX_CTL is 0x%04x after software reset\n", rx_ctl); -+ ret = sr_write_rx_ctl(dev, 0x0000); -+ if (ret < 0) -+ goto out; -+ -+ rx_ctl = sr_read_rx_ctl(dev); -+ netdev_dbg(dev->net, "RX_CTL is 0x%04x setting to 0x0000\n", rx_ctl); -+ -+ /* Read PHYID register *AFTER* the PHY was reset properly */ -+ phyid = sr_get_phyid(dev); -+ netdev_dbg(dev->net, "PHYID=0x%08x\n", phyid); -+ -+ /* medium mode setting */ -+ ret = sr9800_set_default_mode(dev); -+ if (ret < 0) -+ goto out; -+ -+ if (dev->udev->speed == USB_SPEED_HIGH) { -+ ret = sr_write_cmd(dev, SR_CMD_BULKIN_SIZE, -+ SR9800_BULKIN_SIZE[SR9800_MAX_BULKIN_4K].byte_cnt, -+ SR9800_BULKIN_SIZE[SR9800_MAX_BULKIN_4K].threshold, -+ 0, NULL); -+ if (ret < 0) { -+ netdev_err(dev->net, "Reset RX_CTL failed: %d\n", ret); -+ goto out; -+ } -+ dev->rx_urb_size = -+ SR9800_BULKIN_SIZE[SR9800_MAX_BULKIN_4K].size; -+ } else { -+ ret = sr_write_cmd(dev, SR_CMD_BULKIN_SIZE, -+ SR9800_BULKIN_SIZE[SR9800_MAX_BULKIN_2K].byte_cnt, -+ SR9800_BULKIN_SIZE[SR9800_MAX_BULKIN_2K].threshold, -+ 0, NULL); -+ if (ret < 0) { -+ netdev_err(dev->net, "Reset RX_CTL failed: %d\n", ret); -+ goto out; -+ } -+ dev->rx_urb_size = -+ SR9800_BULKIN_SIZE[SR9800_MAX_BULKIN_2K].size; -+ } -+ netdev_dbg(dev->net, "%s : setting rx_urb_size with : %zu\n", __func__, -+ dev->rx_urb_size); -+ return 0; -+ -+out: -+ return ret; -+} -+ -+static const struct driver_info sr9800_driver_info = { -+ .description = "CoreChip SR9800 USB 2.0 Ethernet", -+ .bind = sr9800_bind, -+ .status = sr_status, -+ .link_reset = sr9800_link_reset, -+ .reset = sr9800_reset, -+ .flags = DRIVER_FLAG, -+ .rx_fixup = sr_rx_fixup, -+ .tx_fixup = sr_tx_fixup, -+}; -+ -+static const struct usb_device_id products[] = { -+ { -+ USB_DEVICE(0x0fe6, 0x9800), /* SR9800 Device */ -+ .driver_info = (unsigned long) &sr9800_driver_info, -+ }, -+ {}, /* END */ -+}; -+ -+MODULE_DEVICE_TABLE(usb, products); -+ -+static struct usb_driver sr_driver = { -+ .name = DRIVER_NAME, -+ .id_table = products, -+ .probe = usbnet_probe, -+ .suspend = usbnet_suspend, -+ .resume = usbnet_resume, -+ .disconnect = usbnet_disconnect, -+ .supports_autosuspend = 1, -+}; -+ -+module_usb_driver(sr_driver); -+ -+MODULE_AUTHOR("Liu Junliang -+ * -+ * This file is licensed under the terms of the GNU General Public License -+ * version 2. This program is licensed "as is" without any warranty of any -+ * kind, whether express or implied. -+ */ -+ -+#ifndef _SR9800_H -+#define _SR9800_H -+ -+/* SR9800 spec. command table on Linux Platform */ -+ -+/* command : Software Station Management Control Reg */ -+#define SR_CMD_SET_SW_MII 0x06 -+/* command : PHY Read Reg */ -+#define SR_CMD_READ_MII_REG 0x07 -+/* command : PHY Write Reg */ -+#define SR_CMD_WRITE_MII_REG 0x08 -+/* command : Hardware Station Management Control Reg */ -+#define SR_CMD_SET_HW_MII 0x0a -+/* command : SROM Read Reg */ -+#define SR_CMD_READ_EEPROM 0x0b -+/* command : SROM Write Reg */ -+#define SR_CMD_WRITE_EEPROM 0x0c -+/* command : SROM Write Enable Reg */ -+#define SR_CMD_WRITE_ENABLE 0x0d -+/* command : SROM Write Disable Reg */ -+#define SR_CMD_WRITE_DISABLE 0x0e -+/* command : RX Control Read Reg */ -+#define SR_CMD_READ_RX_CTL 0x0f -+#define SR_RX_CTL_PRO (1 << 0) -+#define SR_RX_CTL_AMALL (1 << 1) -+#define SR_RX_CTL_SEP (1 << 2) -+#define SR_RX_CTL_AB (1 << 3) -+#define SR_RX_CTL_AM (1 << 4) -+#define SR_RX_CTL_AP (1 << 5) -+#define SR_RX_CTL_ARP (1 << 6) -+#define SR_RX_CTL_SO (1 << 7) -+#define SR_RX_CTL_RH1M (1 << 8) -+#define SR_RX_CTL_RH2M (1 << 9) -+#define SR_RX_CTL_RH3M (1 << 10) -+/* command : RX Control Write Reg */ -+#define SR_CMD_WRITE_RX_CTL 0x10 -+/* command : IPG0/IPG1/IPG2 Control Read Reg */ -+#define SR_CMD_READ_IPG012 0x11 -+/* command : IPG0/IPG1/IPG2 Control Write Reg */ -+#define SR_CMD_WRITE_IPG012 0x12 -+/* command : Node ID Read Reg */ -+#define SR_CMD_READ_NODE_ID 0x13 -+/* command : Node ID Write Reg */ -+#define SR_CMD_WRITE_NODE_ID 0x14 -+/* command : Multicast Filter Array Read Reg */ -+#define SR_CMD_READ_MULTI_FILTER 0x15 -+/* command : Multicast Filter Array Write Reg */ -+#define SR_CMD_WRITE_MULTI_FILTER 0x16 -+/* command : Eth/HomePNA PHY Address Reg */ -+#define SR_CMD_READ_PHY_ID 0x19 -+/* command : Medium Status Read Reg */ -+#define SR_CMD_READ_MEDIUM_STATUS 0x1a -+#define SR_MONITOR_LINK (1 << 1) -+#define SR_MONITOR_MAGIC (1 << 2) -+#define SR_MONITOR_HSFS (1 << 4) -+/* command : Medium Status Write Reg */ -+#define SR_CMD_WRITE_MEDIUM_MODE 0x1b -+#define SR_MEDIUM_GM (1 << 0) -+#define SR_MEDIUM_FD (1 << 1) -+#define SR_MEDIUM_AC (1 << 2) -+#define SR_MEDIUM_ENCK (1 << 3) -+#define SR_MEDIUM_RFC (1 << 4) -+#define SR_MEDIUM_TFC (1 << 5) -+#define SR_MEDIUM_JFE (1 << 6) -+#define SR_MEDIUM_PF (1 << 7) -+#define SR_MEDIUM_RE (1 << 8) -+#define SR_MEDIUM_PS (1 << 9) -+#define SR_MEDIUM_RSV (1 << 10) -+#define SR_MEDIUM_SBP (1 << 11) -+#define SR_MEDIUM_SM (1 << 12) -+/* command : Monitor Mode Status Read Reg */ -+#define SR_CMD_READ_MONITOR_MODE 0x1c -+/* command : Monitor Mode Status Write Reg */ -+#define SR_CMD_WRITE_MONITOR_MODE 0x1d -+/* command : GPIO Status Read Reg */ -+#define SR_CMD_READ_GPIOS 0x1e -+#define SR_GPIO_GPO0EN (1 << 0) /* GPIO0 Output enable */ -+#define SR_GPIO_GPO_0 (1 << 1) /* GPIO0 Output value */ -+#define SR_GPIO_GPO1EN (1 << 2) /* GPIO1 Output enable */ -+#define SR_GPIO_GPO_1 (1 << 3) /* GPIO1 Output value */ -+#define SR_GPIO_GPO2EN (1 << 4) /* GPIO2 Output enable */ -+#define SR_GPIO_GPO_2 (1 << 5) /* GPIO2 Output value */ -+#define SR_GPIO_RESERVED (1 << 6) /* Reserved */ -+#define SR_GPIO_RSE (1 << 7) /* Reload serial EEPROM */ -+/* command : GPIO Status Write Reg */ -+#define SR_CMD_WRITE_GPIOS 0x1f -+/* command : Eth PHY Power and Reset Control Reg */ -+#define SR_CMD_SW_RESET 0x20 -+#define SR_SWRESET_CLEAR 0x00 -+#define SR_SWRESET_RR (1 << 0) -+#define SR_SWRESET_RT (1 << 1) -+#define SR_SWRESET_PRTE (1 << 2) -+#define SR_SWRESET_PRL (1 << 3) -+#define SR_SWRESET_BZ (1 << 4) -+#define SR_SWRESET_IPRL (1 << 5) -+#define SR_SWRESET_IPPD (1 << 6) -+/* command : Software Interface Selection Status Read Reg */ -+#define SR_CMD_SW_PHY_STATUS 0x21 -+/* command : Software Interface Selection Status Write Reg */ -+#define SR_CMD_SW_PHY_SELECT 0x22 -+/* command : BULK in Buffer Size Reg */ -+#define SR_CMD_BULKIN_SIZE 0x2A -+/* command : LED_MUX Control Reg */ -+#define SR_CMD_LED_MUX 0x70 -+#define SR_LED_MUX_TX_ACTIVE (1 << 0) -+#define SR_LED_MUX_RX_ACTIVE (1 << 1) -+#define SR_LED_MUX_COLLISION (1 << 2) -+#define SR_LED_MUX_DUP_COL (1 << 3) -+#define SR_LED_MUX_DUP (1 << 4) -+#define SR_LED_MUX_SPEED (1 << 5) -+#define SR_LED_MUX_LINK_ACTIVE (1 << 6) -+#define SR_LED_MUX_LINK (1 << 7) -+ -+/* Register Access Flags */ -+#define SR_REQ_RD_REG (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE) -+#define SR_REQ_WR_REG (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE) -+ -+/* Multicast Filter Array size & Max Number */ -+#define SR_MCAST_FILTER_SIZE 8 -+#define SR_MAX_MCAST 64 -+ -+/* IPG0/1/2 Default Value */ -+#define SR9800_IPG0_DEFAULT 0x15 -+#define SR9800_IPG1_DEFAULT 0x0c -+#define SR9800_IPG2_DEFAULT 0x12 -+ -+/* Medium Status Default Mode */ -+#define SR9800_MEDIUM_DEFAULT \ -+ (SR_MEDIUM_FD | SR_MEDIUM_RFC | \ -+ SR_MEDIUM_TFC | SR_MEDIUM_PS | \ -+ SR_MEDIUM_AC | SR_MEDIUM_RE) -+ -+/* RX Control Default Setting */ -+#define SR_DEFAULT_RX_CTL \ -+ (SR_RX_CTL_SO | SR_RX_CTL_AB | SR_RX_CTL_RH1M) -+ -+/* EEPROM Magic Number & EEPROM Size */ -+#define SR_EEPROM_MAGIC 0xdeadbeef -+#define SR9800_EEPROM_LEN 0xff -+ -+/* SR9800 Driver Version and Driver Name */ -+#define DRIVER_VERSION "11-Nov-2013" -+#define DRIVER_NAME "CoreChips" -+#define DRIVER_FLAG \ -+ (FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | FLAG_MULTI_PACKET) -+ -+/* SR9800 BULKIN Buffer Size */ -+#define SR9800_MAX_BULKIN_2K 0 -+#define SR9800_MAX_BULKIN_4K 1 -+#define SR9800_MAX_BULKIN_6K 2 -+#define SR9800_MAX_BULKIN_8K 3 -+#define SR9800_MAX_BULKIN_16K 4 -+#define SR9800_MAX_BULKIN_20K 5 -+#define SR9800_MAX_BULKIN_24K 6 -+#define SR9800_MAX_BULKIN_32K 7 -+ -+struct {unsigned short size, byte_cnt, threshold; } SR9800_BULKIN_SIZE[] = { -+ /* 2k */ -+ {2048, 0x8000, 0x8001}, -+ /* 4k */ -+ {4096, 0x8100, 0x8147}, -+ /* 6k */ -+ {6144, 0x8200, 0x81EB}, -+ /* 8k */ -+ {8192, 0x8300, 0x83D7}, -+ /* 16 */ -+ {16384, 0x8400, 0x851E}, -+ /* 20k */ -+ {20480, 0x8500, 0x8666}, -+ /* 24k */ -+ {24576, 0x8600, 0x87AE}, -+ /* 32k */ -+ {32768, 0x8700, 0x8A3D}, -+}; -+ -+/* This structure cannot exceed sizeof(unsigned long [5]) AKA 20 bytes */ -+struct sr_data { -+ u8 multi_filter[SR_MCAST_FILTER_SIZE]; -+ u8 mac_addr[ETH_ALEN]; -+ u8 phymode; -+ u8 ledmode; -+ u8 eeprom_len; -+}; -+ -+struct sr9800_int_data { -+ __le16 res1; -+ u8 link; -+ __le16 res2; -+ u8 status; -+ __le16 res3; -+} __packed; -+ -+#endif /* _SR9800_H */ -diff -Naur backports-4.2.6-1.org/drivers/net/usb/zaurus.c backports-4.2.6-1/drivers/net/usb/zaurus.c ---- backports-4.2.6-1.org/drivers/net/usb/zaurus.c 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/zaurus.c 2016-06-28 14:35:18.015307217 +0200 -@@ -0,0 +1,385 @@ -+/* -+ * Copyright (C) 2002 Pavel Machek -+ * Copyright (C) 2002-2005 by David Brownell -+ * -+ * 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, see . -+ */ -+ -+// #define DEBUG // error path messages, extra info -+// #define VERBOSE // more; success messages -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+ -+/* -+ * All known Zaurii lie about their standards conformance. At least -+ * the earliest SA-1100 models lie by saying they support CDC Ethernet. -+ * Some later models (especially PXA-25x and PXA-27x based ones) lie -+ * and say they support CDC MDLM (for access to cell phone modems). -+ * -+ * There are non-Zaurus products that use these same protocols too. -+ * -+ * The annoying thing is that at the same time Sharp was developing -+ * that annoying standards-breaking software, the Linux community had -+ * a simple "CDC Subset" working reliably on the same SA-1100 hardware. -+ * That is, the same functionality but not violating standards. -+ * -+ * The CDC Ethernet nonconformance points are troublesome to hosts -+ * with a true CDC Ethernet implementation: -+ * - Framing appends a CRC, which the spec says drivers "must not" do; -+ * - Transfers data in altsetting zero, instead of altsetting 1; -+ * - All these peripherals use the same ethernet address. -+ * -+ * The CDC MDLM nonconformance is less immediately troublesome, since all -+ * MDLM implementations are quasi-proprietary anyway. -+ */ -+ -+static struct sk_buff * -+zaurus_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) -+{ -+ int padlen; -+ struct sk_buff *skb2; -+ -+ padlen = 2; -+ if (!skb_cloned(skb)) { -+ int tailroom = skb_tailroom(skb); -+ if ((padlen + 4) <= tailroom) -+ goto done; -+ } -+ skb2 = skb_copy_expand(skb, 0, 4 + padlen, flags); -+ dev_kfree_skb_any(skb); -+ skb = skb2; -+ if (skb) { -+ u32 fcs; -+done: -+ fcs = crc32_le(~0, skb->data, skb->len); -+ fcs = ~fcs; -+ -+ *skb_put (skb, 1) = fcs & 0xff; -+ *skb_put (skb, 1) = (fcs>> 8) & 0xff; -+ *skb_put (skb, 1) = (fcs>>16) & 0xff; -+ *skb_put (skb, 1) = (fcs>>24) & 0xff; -+ } -+ return skb; -+} -+ -+static int zaurus_bind(struct usbnet *dev, struct usb_interface *intf) -+{ -+ /* Belcarra's funky framing has other options; mostly -+ * TRAILERS (!) with 4 bytes CRC, and maybe 2 pad bytes. -+ */ -+ dev->net->hard_header_len += 6; -+ dev->rx_urb_size = dev->net->hard_header_len + dev->net->mtu; -+ return usbnet_generic_cdc_bind(dev, intf); -+} -+ -+/* PDA style devices are always connected if present */ -+static int always_connected (struct usbnet *dev) -+{ -+ return 0; -+} -+ -+static const struct driver_info zaurus_sl5x00_info = { -+ .description = "Sharp Zaurus SL-5x00", -+ .flags = FLAG_POINTTOPOINT | FLAG_FRAMING_Z, -+ .check_connect = always_connected, -+ .bind = zaurus_bind, -+ .unbind = usbnet_cdc_unbind, -+ .tx_fixup = zaurus_tx_fixup, -+}; -+#define ZAURUS_STRONGARM_INFO ((unsigned long)&zaurus_sl5x00_info) -+ -+static const struct driver_info zaurus_pxa_info = { -+ .description = "Sharp Zaurus, PXA-2xx based", -+ .flags = FLAG_POINTTOPOINT | FLAG_FRAMING_Z, -+ .check_connect = always_connected, -+ .bind = zaurus_bind, -+ .unbind = usbnet_cdc_unbind, -+ .tx_fixup = zaurus_tx_fixup, -+}; -+#define ZAURUS_PXA_INFO ((unsigned long)&zaurus_pxa_info) -+ -+static const struct driver_info olympus_mxl_info = { -+ .description = "Olympus R1000", -+ .flags = FLAG_POINTTOPOINT | FLAG_FRAMING_Z, -+ .check_connect = always_connected, -+ .bind = zaurus_bind, -+ .unbind = usbnet_cdc_unbind, -+ .tx_fixup = zaurus_tx_fixup, -+}; -+#define OLYMPUS_MXL_INFO ((unsigned long)&olympus_mxl_info) -+ -+ -+/* Some more recent products using Lineo/Belcarra code will wrongly claim -+ * CDC MDLM conformance. They aren't conformant: data endpoints live -+ * in the control interface, there's no data interface, and it's not used -+ * to talk to a cell phone radio. But at least we can detect these two -+ * pseudo-classes, rather than growing this product list with entries for -+ * each new nonconformant product (sigh). -+ */ -+static const u8 safe_guid[16] = { -+ 0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6, -+ 0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f, -+}; -+static const u8 blan_guid[16] = { -+ 0x74, 0xf0, 0x3d, 0xbd, 0x1e, 0xc1, 0x44, 0x70, -+ 0xa3, 0x67, 0x71, 0x34, 0xc9, 0xf5, 0x54, 0x37, -+}; -+ -+static int blan_mdlm_bind(struct usbnet *dev, struct usb_interface *intf) -+{ -+ u8 *buf = intf->cur_altsetting->extra; -+ int len = intf->cur_altsetting->extralen; -+ struct usb_cdc_mdlm_desc *desc = NULL; -+ struct usb_cdc_mdlm_detail_desc *detail = NULL; -+ -+ while (len > 3) { -+ if (buf [1] != USB_DT_CS_INTERFACE) -+ goto next_desc; -+ -+ /* use bDescriptorSubType, and just verify that we get a -+ * "BLAN" (or "SAFE") descriptor. -+ */ -+ switch (buf [2]) { -+ case USB_CDC_MDLM_TYPE: -+ if (desc) { -+ dev_dbg(&intf->dev, "extra MDLM\n"); -+ goto bad_desc; -+ } -+ desc = (void *) buf; -+ if (desc->bLength != sizeof *desc) { -+ dev_dbg(&intf->dev, "MDLM len %u\n", -+ desc->bLength); -+ goto bad_desc; -+ } -+ /* expect bcdVersion 1.0, ignore */ -+ if (memcmp(&desc->bGUID, blan_guid, 16) && -+ memcmp(&desc->bGUID, safe_guid, 16)) { -+ /* hey, this one might _really_ be MDLM! */ -+ dev_dbg(&intf->dev, "MDLM guid\n"); -+ goto bad_desc; -+ } -+ break; -+ case USB_CDC_MDLM_DETAIL_TYPE: -+ if (detail) { -+ dev_dbg(&intf->dev, "extra MDLM detail\n"); -+ goto bad_desc; -+ } -+ detail = (void *) buf; -+ switch (detail->bGuidDescriptorType) { -+ case 0: /* "SAFE" */ -+ if (detail->bLength != (sizeof *detail + 2)) -+ goto bad_detail; -+ break; -+ case 1: /* "BLAN" */ -+ if (detail->bLength != (sizeof *detail + 3)) -+ goto bad_detail; -+ break; -+ default: -+ goto bad_detail; -+ } -+ -+ /* assuming we either noticed BLAN already, or will -+ * find it soon, there are some data bytes here: -+ * - bmNetworkCapabilities (unused) -+ * - bmDataCapabilities (bits, see below) -+ * - bPad (ignored, for PADAFTER -- BLAN-only) -+ * bits are: -+ * - 0x01 -- Zaurus framing (add CRC) -+ * - 0x02 -- PADBEFORE (CRC includes some padding) -+ * - 0x04 -- PADAFTER (some padding after CRC) -+ * - 0x08 -- "fermat" packet mangling (for hw bugs) -+ * the PADBEFORE appears not to matter; we interop -+ * with devices that use it and those that don't. -+ */ -+ if ((detail->bDetailData[1] & ~0x02) != 0x01) { -+ /* bmDataCapabilities == 0 would be fine too, -+ * but framing is minidriver-coupled for now. -+ */ -+bad_detail: -+ dev_dbg(&intf->dev, -+ "bad MDLM detail, %d %d %d\n", -+ detail->bLength, -+ detail->bDetailData[0], -+ detail->bDetailData[2]); -+ goto bad_desc; -+ } -+ -+ /* same extra framing as for non-BLAN mode */ -+ dev->net->hard_header_len += 6; -+ dev->rx_urb_size = dev->net->hard_header_len -+ + dev->net->mtu; -+ break; -+ } -+next_desc: -+ len -= buf [0]; /* bLength */ -+ buf += buf [0]; -+ } -+ -+ if (!desc || !detail) { -+ dev_dbg(&intf->dev, "missing cdc mdlm %s%sdescriptor\n", -+ desc ? "" : "func ", -+ detail ? "" : "detail "); -+ goto bad_desc; -+ } -+ -+ /* There's probably a CDC Ethernet descriptor there, but we can't -+ * rely on the Ethernet address it provides since not all vendors -+ * bother to make it unique. Likewise there's no point in tracking -+ * of the CDC event notifications. -+ */ -+ return usbnet_get_endpoints(dev, intf); -+ -+bad_desc: -+ dev_info(&dev->udev->dev, "unsupported MDLM descriptors\n"); -+ return -ENODEV; -+} -+ -+static const struct driver_info bogus_mdlm_info = { -+ .description = "pseudo-MDLM (BLAN) device", -+ .flags = FLAG_POINTTOPOINT | FLAG_FRAMING_Z, -+ .check_connect = always_connected, -+ .tx_fixup = zaurus_tx_fixup, -+ .bind = blan_mdlm_bind, -+}; -+ -+static const struct usb_device_id products [] = { -+#define ZAURUS_MASTER_INTERFACE \ -+ .bInterfaceClass = USB_CLASS_COMM, \ -+ .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, \ -+ .bInterfaceProtocol = USB_CDC_PROTO_NONE -+ -+/* SA-1100 based Sharp Zaurus ("collie"), or compatible. */ -+{ -+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO -+ | USB_DEVICE_ID_MATCH_DEVICE, -+ .idVendor = 0x04DD, -+ .idProduct = 0x8004, -+ ZAURUS_MASTER_INTERFACE, -+ .driver_info = ZAURUS_STRONGARM_INFO, -+}, -+ -+/* PXA-2xx based models are also lying-about-cdc. If you add any -+ * more devices that claim to be CDC Ethernet, make sure they get -+ * added to the blacklist in cdc_ether too. -+ * -+ * NOTE: OpenZaurus versions with 2.6 kernels won't use these entries, -+ * unlike the older ones with 2.4 "embedix" kernels. -+ */ -+{ -+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO -+ | USB_DEVICE_ID_MATCH_DEVICE, -+ .idVendor = 0x04DD, -+ .idProduct = 0x8005, /* A-300 */ -+ ZAURUS_MASTER_INTERFACE, -+ .driver_info = ZAURUS_PXA_INFO, -+}, { -+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO -+ | USB_DEVICE_ID_MATCH_DEVICE, -+ .idVendor = 0x04DD, -+ .idProduct = 0x8006, /* B-500/SL-5600 */ -+ ZAURUS_MASTER_INTERFACE, -+ .driver_info = ZAURUS_PXA_INFO, -+}, { -+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO -+ | USB_DEVICE_ID_MATCH_DEVICE, -+ .idVendor = 0x04DD, -+ .idProduct = 0x8007, /* C-700 */ -+ ZAURUS_MASTER_INTERFACE, -+ .driver_info = ZAURUS_PXA_INFO, -+}, { -+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO -+ | USB_DEVICE_ID_MATCH_DEVICE, -+ .idVendor = 0x04DD, -+ .idProduct = 0x9031, /* C-750 C-760 */ -+ ZAURUS_MASTER_INTERFACE, -+ .driver_info = ZAURUS_PXA_INFO, -+}, { -+ /* C-750/C-760/C-860/SL-C3000 PDA in MDLM mode */ -+ USB_DEVICE_AND_INTERFACE_INFO(0x04DD, 0x9031, USB_CLASS_COMM, -+ USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), -+ .driver_info = (unsigned long) &bogus_mdlm_info, -+}, { -+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO -+ | USB_DEVICE_ID_MATCH_DEVICE, -+ .idVendor = 0x04DD, -+ .idProduct = 0x9032, /* SL-6000 */ -+ ZAURUS_MASTER_INTERFACE, -+ .driver_info = ZAURUS_PXA_INFO, -+}, { -+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO -+ | USB_DEVICE_ID_MATCH_DEVICE, -+ .idVendor = 0x04DD, -+ /* reported with some C860 units */ -+ .idProduct = 0x9050, /* C-860 */ -+ ZAURUS_MASTER_INTERFACE, -+ .driver_info = ZAURUS_PXA_INFO, -+}, -+{ -+ /* Motorola Rokr E6 */ -+ USB_DEVICE_AND_INTERFACE_INFO(0x22b8, 0x6027, USB_CLASS_COMM, -+ USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), -+ .driver_info = (unsigned long) &bogus_mdlm_info, -+}, { -+ /* Motorola MOTOMAGX phones */ -+ USB_DEVICE_AND_INTERFACE_INFO(0x22b8, 0x6425, USB_CLASS_COMM, -+ USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), -+ .driver_info = (unsigned long) &bogus_mdlm_info, -+}, -+ -+/* Olympus has some models with a Zaurus-compatible option. -+ * R-1000 uses a FreeScale i.MXL cpu (ARMv4T) -+ */ -+{ -+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO -+ | USB_DEVICE_ID_MATCH_DEVICE, -+ .idVendor = 0x07B4, -+ .idProduct = 0x0F02, /* R-1000 */ -+ ZAURUS_MASTER_INTERFACE, -+ .driver_info = OLYMPUS_MXL_INFO, -+}, -+ -+/* Logitech Harmony 900 - uses the pseudo-MDLM (BLAN) driver */ -+{ -+ USB_DEVICE_AND_INTERFACE_INFO(0x046d, 0xc11f, USB_CLASS_COMM, -+ USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), -+ .driver_info = (unsigned long) &bogus_mdlm_info, -+}, -+ { }, // END -+}; -+MODULE_DEVICE_TABLE(usb, products); -+ -+static struct usb_driver zaurus_driver = { -+ .name = "zaurus", -+ .id_table = products, -+ .probe = usbnet_probe, -+ .disconnect = usbnet_disconnect, -+ .suspend = usbnet_suspend, -+ .resume = usbnet_resume, -+ .disable_hub_initiated_lpm = 1, -+}; -+ -+module_usb_driver(zaurus_driver); -+ -+MODULE_AUTHOR("Pavel Machek, David Brownell"); -+MODULE_DESCRIPTION("Sharp Zaurus PDA, and compatible products"); -+MODULE_LICENSE("GPL"); diff --git a/src/patches/backports-4.2.6-1-ath9k_add_missing_mask_init.patch b/src/patches/backports-4.2.6-1-ath9k_add_missing_mask_init.patch deleted file mode 100644 index 01aee6d928..0000000000 --- a/src/patches/backports-4.2.6-1-ath9k_add_missing_mask_init.patch +++ /dev/null @@ -1,56 +0,0 @@ -by moving common code to ar5008_hw_cmn_spur_mitigate i forgot to move -mask_m & mask_p initialisation. This coused a performance regression -on ar9281. - - -Fixes: f911085ffa88 ("ath9k: split ar5008_hw_spur_mitigate and reuse common -code in ar9002_hw_spur_mitigate.") -Reported-by: Gustav Frederiksen -Tested-by: Gustav Frederiksen -Signed-off-by: Oleksij Rempel -diff -Naur backports-4.2.6-1.org/drivers/net/wireless/ath/ath9k/ar5008_phy.c backports-4.2.6-1/drivers/net/wireless/ath/ath9k/ar5008_phy.c ---- backports-4.2.6-1.org/drivers/net/wireless/ath/ath9k/ar5008_phy.c 2015-11-15 22:19:39.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/wireless/ath/ath9k/ar5008_phy.c 2016-04-25 09:37:58.765112541 +0200 -@@ -274,6 +274,9 @@ - }; - static const int inc[4] = { 0, 100, 0, 0 }; - -+ memset(&mask_m, 0, sizeof(int8_t) * 123); -+ memset(&mask_p, 0, sizeof(int8_t) * 123); -+ - cur_bin = -6000; - upper = bin + 100; - lower = bin - 100; -@@ -424,14 +427,9 @@ - int tmp, new; - int i; - -- int8_t mask_m[123]; -- int8_t mask_p[123]; - int cur_bb_spur; - bool is2GHz = IS_CHAN_2GHZ(chan); - -- memset(&mask_m, 0, sizeof(int8_t) * 123); -- memset(&mask_p, 0, sizeof(int8_t) * 123); -- - for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { - cur_bb_spur = ah->eep_ops->get_spur_channel(ah, i, is2GHz); - if (AR_NO_SPUR == cur_bb_spur) -diff -Naur backports-4.2.6-1.org/drivers/net/wireless/ath/ath9k/ar9002_phy.c backports-4.2.6-1/drivers/net/wireless/ath/ath9k/ar9002_phy.c ---- backports-4.2.6-1.org/drivers/net/wireless/ath/ath9k/ar9002_phy.c 2015-11-15 22:19:39.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/wireless/ath/ath9k/ar9002_phy.c 2016-04-25 09:38:57.729109148 +0200 -@@ -178,14 +178,9 @@ - int i; - struct chan_centers centers; - -- int8_t mask_m[123]; -- int8_t mask_p[123]; - int cur_bb_spur; - bool is2GHz = IS_CHAN_2GHZ(chan); - -- memset(&mask_m, 0, sizeof(int8_t) * 123); -- memset(&mask_p, 0, sizeof(int8_t) * 123); -- - ath9k_hw_get_channel_centers(ah, chan, ¢ers); - freq = centers.synth_center; - diff --git a/src/patches/backports-4.2.6-1-grsecurity.patch b/src/patches/backports-4.2.6-1-grsecurity.patch deleted file mode 100644 index 1a44409a42..0000000000 --- a/src/patches/backports-4.2.6-1-grsecurity.patch +++ /dev/null @@ -1,3995 +0,0 @@ -diff -Naur backports-4.2.6-1.org/drivers/bluetooth/btwilink.c backports-4.2.6-1/drivers/bluetooth/btwilink.c ---- backports-4.2.6-1.org/drivers/bluetooth/btwilink.c 2015-11-15 22:19:40.000000000 +0100 -+++ backports-4.2.6-1/drivers/bluetooth/btwilink.c 2016-01-27 12:26:16.319959957 +0100 -@@ -288,7 +288,7 @@ - - static int bt_ti_probe(struct platform_device *pdev) - { -- static struct ti_st *hst; -+ struct ti_st *hst; - struct hci_dev *hdev; - int err; - -diff -Naur backports-4.2.6-1.org/drivers/media/dvb-core/dvbdev.c backports-4.2.6-1/drivers/media/dvb-core/dvbdev.c ---- backports-4.2.6-1.org/drivers/media/dvb-core/dvbdev.c 2015-11-15 22:19:39.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/dvb-core/dvbdev.c 2016-01-27 12:26:21.266626324 +0100 -@@ -272,7 +272,7 @@ - const struct dvb_device *template, void *priv, int type) - { - struct dvb_device *dvbdev; -- struct file_operations *dvbdevfops; -+ file_operations_no_const *dvbdevfops; - struct device *clsdev; - int minor; - int id; -diff -Naur backports-4.2.6-1.org/drivers/media/dvb-frontends/af9033.h backports-4.2.6-1/drivers/media/dvb-frontends/af9033.h ---- backports-4.2.6-1.org/drivers/media/dvb-frontends/af9033.h 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/dvb-frontends/af9033.h 2016-01-27 12:26:21.266626324 +0100 -@@ -96,6 +96,6 @@ - int (*pid_filter_ctrl)(struct dvb_frontend *fe, int onoff); - int (*pid_filter)(struct dvb_frontend *fe, int index, u16 pid, - int onoff); --}; -+} __no_const; - - #endif /* AF9033_H */ -diff -Naur backports-4.2.6-1.org/drivers/media/dvb-frontends/dib3000.h backports-4.2.6-1/drivers/media/dvb-frontends/dib3000.h ---- backports-4.2.6-1.org/drivers/media/dvb-frontends/dib3000.h 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/dvb-frontends/dib3000.h 2016-01-27 12:26:21.266626324 +0100 -@@ -39,7 +39,7 @@ - int (*fifo_ctrl)(struct dvb_frontend *fe, int onoff); - int (*pid_ctrl)(struct dvb_frontend *fe, int index, int pid, int onoff); - int (*tuner_pass_ctrl)(struct dvb_frontend *fe, int onoff, u8 pll_ctrl); --}; -+} __no_const; - - #if IS_REACHABLE(CPTCFG_DVB_DIB3000MB) - extern struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config, -diff -Naur backports-4.2.6-1.org/drivers/media/dvb-frontends/dib7000p.h backports-4.2.6-1/drivers/media/dvb-frontends/dib7000p.h ---- backports-4.2.6-1.org/drivers/media/dvb-frontends/dib7000p.h 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/dvb-frontends/dib7000p.h 2016-01-27 12:26:21.266626324 +0100 -@@ -64,7 +64,7 @@ - int (*get_adc_power)(struct dvb_frontend *fe); - int (*slave_reset)(struct dvb_frontend *fe); - struct dvb_frontend *(*init)(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg); --}; -+} __no_const; - - #if IS_REACHABLE(CPTCFG_DVB_DIB7000P) - void *dib7000p_attach(struct dib7000p_ops *ops); -diff -Naur backports-4.2.6-1.org/drivers/media/dvb-frontends/dib8000.h backports-4.2.6-1/drivers/media/dvb-frontends/dib8000.h ---- backports-4.2.6-1.org/drivers/media/dvb-frontends/dib8000.h 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/dvb-frontends/dib8000.h 2016-01-27 12:26:21.266626324 +0100 -@@ -61,7 +61,7 @@ - int (*pid_filter_ctrl)(struct dvb_frontend *fe, u8 onoff); - int (*pid_filter)(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff); - struct dvb_frontend *(*init)(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg); --}; -+} __no_const; - - #if IS_REACHABLE(CPTCFG_DVB_DIB8000) - void *dib8000_attach(struct dib8000_ops *ops); -diff -Naur backports-4.2.6-1.org/drivers/media/pci/cx88/cx88-video.c backports-4.2.6-1/drivers/media/pci/cx88/cx88-video.c ---- backports-4.2.6-1.org/drivers/media/pci/cx88/cx88-video.c 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/pci/cx88/cx88-video.c 2016-01-27 12:26:21.266626324 +0100 -@@ -50,9 +50,9 @@ - - /* ------------------------------------------------------------------ */ - --static unsigned int video_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; --static unsigned int vbi_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; --static unsigned int radio_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; -+static int video_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; -+static int vbi_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; -+static int radio_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; - - module_param_array(video_nr, int, NULL, 0444); - module_param_array(vbi_nr, int, NULL, 0444); -diff -Naur backports-4.2.6-1.org/drivers/media/pci/ivtv/ivtv-driver.c backports-4.2.6-1/drivers/media/pci/ivtv/ivtv-driver.c ---- backports-4.2.6-1.org/drivers/media/pci/ivtv/ivtv-driver.c 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/pci/ivtv/ivtv-driver.c 2016-01-27 12:26:21.266626324 +0100 -@@ -83,7 +83,7 @@ - MODULE_DEVICE_TABLE(pci,ivtv_pci_tbl); - - /* ivtv instance counter */ --static atomic_t ivtv_instance = ATOMIC_INIT(0); -+static atomic_unchecked_t ivtv_instance = ATOMIC_INIT(0); - - /* Parameter declarations */ - static int cardtype[IVTV_MAX_CARDS]; -diff -Naur backports-4.2.6-1.org/drivers/media/pci/solo6x10/solo6x10-core.c backports-4.2.6-1/drivers/media/pci/solo6x10/solo6x10-core.c ---- backports-4.2.6-1.org/drivers/media/pci/solo6x10/solo6x10-core.c 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/pci/solo6x10/solo6x10-core.c 2016-01-27 12:26:21.266626324 +0100 -@@ -424,7 +424,7 @@ - - static int solo_sysfs_init(struct solo_dev *solo_dev) - { -- struct bin_attribute *sdram_attr = &solo_dev->sdram_attr; -+ bin_attribute_no_const *sdram_attr = &solo_dev->sdram_attr; - struct device *dev = &solo_dev->dev; - const char *driver; - int i; -diff -Naur backports-4.2.6-1.org/drivers/media/pci/solo6x10/solo6x10-g723.c backports-4.2.6-1/drivers/media/pci/solo6x10/solo6x10-g723.c ---- backports-4.2.6-1.org/drivers/media/pci/solo6x10/solo6x10-g723.c 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/pci/solo6x10/solo6x10-g723.c 2016-01-27 12:26:21.266626324 +0100 -@@ -351,7 +351,7 @@ - - int solo_g723_init(struct solo_dev *solo_dev) - { -- static struct snd_device_ops ops = { NULL }; -+ static struct snd_device_ops ops = { }; - struct snd_card *card; - struct snd_kcontrol_new kctl; - char name[32]; -diff -Naur backports-4.2.6-1.org/drivers/media/pci/solo6x10/solo6x10.h backports-4.2.6-1/drivers/media/pci/solo6x10/solo6x10.h ---- backports-4.2.6-1.org/drivers/media/pci/solo6x10/solo6x10.h 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/pci/solo6x10/solo6x10.h 2016-01-27 12:26:21.266626324 +0100 -@@ -218,7 +218,7 @@ - - /* P2M DMA Engine */ - struct solo_p2m_dev p2m_dev[SOLO_NR_P2M]; -- atomic_t p2m_count; -+ atomic_unchecked_t p2m_count; - int p2m_jiffies; - unsigned int p2m_timeouts; - -diff -Naur backports-4.2.6-1.org/drivers/media/pci/solo6x10/solo6x10-p2m.c backports-4.2.6-1/drivers/media/pci/solo6x10/solo6x10-p2m.c ---- backports-4.2.6-1.org/drivers/media/pci/solo6x10/solo6x10-p2m.c 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/pci/solo6x10/solo6x10-p2m.c 2016-01-27 12:26:21.266626324 +0100 -@@ -73,7 +73,7 @@ - - /* Get next ID. According to Softlogic, 6110 has problems on !=0 P2M */ - if (solo_dev->type != SOLO_DEV_6110 && multi_p2m) { -- p2m_id = atomic_inc_return(&solo_dev->p2m_count) % SOLO_NR_P2M; -+ p2m_id = atomic_inc_return_unchecked(&solo_dev->p2m_count) % SOLO_NR_P2M; - if (p2m_id < 0) - p2m_id = -p2m_id; - } -diff -Naur backports-4.2.6-1.org/drivers/media/pci/tw68/tw68-core.c backports-4.2.6-1/drivers/media/pci/tw68/tw68-core.c ---- backports-4.2.6-1.org/drivers/media/pci/tw68/tw68-core.c 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/pci/tw68/tw68-core.c 2016-01-27 12:26:21.266626324 +0100 -@@ -60,7 +60,7 @@ - module_param_array(card, int, NULL, 0444); - MODULE_PARM_DESC(card, "card type"); - --static atomic_t tw68_instance = ATOMIC_INIT(0); -+static atomic_unchecked_t tw68_instance = ATOMIC_INIT(0); - - /* ------------------------------------------------------------------ */ - -diff -Naur backports-4.2.6-1.org/drivers/media/platform/omap/omap_vout.c backports-4.2.6-1/drivers/media/platform/omap/omap_vout.c ---- backports-4.2.6-1.org/drivers/media/platform/omap/omap_vout.c 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/platform/omap/omap_vout.c 2016-01-27 12:26:21.266626324 +0100 -@@ -63,7 +63,6 @@ - OMAP_VIDEO2, - }; - --static struct videobuf_queue_ops video_vbq_ops; - /* Variables configurable through module params*/ - static u32 video1_numbuffers = 3; - static u32 video2_numbuffers = 3; -@@ -1008,6 +1007,12 @@ - { - struct videobuf_queue *q; - struct omap_vout_device *vout = NULL; -+ static struct videobuf_queue_ops video_vbq_ops = { -+ .buf_setup = omap_vout_buffer_setup, -+ .buf_prepare = omap_vout_buffer_prepare, -+ .buf_release = omap_vout_buffer_release, -+ .buf_queue = omap_vout_buffer_queue, -+ }; - - vout = video_drvdata(file); - v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Entering %s\n", __func__); -@@ -1025,10 +1030,6 @@ - vout->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - - q = &vout->vbq; -- video_vbq_ops.buf_setup = omap_vout_buffer_setup; -- video_vbq_ops.buf_prepare = omap_vout_buffer_prepare; -- video_vbq_ops.buf_release = omap_vout_buffer_release; -- video_vbq_ops.buf_queue = omap_vout_buffer_queue; - spin_lock_init(&vout->vbq_lock); - - videobuf_queue_dma_contig_init(q, &video_vbq_ops, q->dev, -diff -Naur backports-4.2.6-1.org/drivers/media/platform/s5p-tv/mixer_grp_layer.c backports-4.2.6-1/drivers/media/platform/s5p-tv/mixer_grp_layer.c ---- backports-4.2.6-1.org/drivers/media/platform/s5p-tv/mixer_grp_layer.c 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/platform/s5p-tv/mixer_grp_layer.c 2016-01-27 12:26:21.266626324 +0100 -@@ -235,7 +235,7 @@ - { - struct mxr_layer *layer; - int ret; -- struct mxr_layer_ops ops = { -+ static struct mxr_layer_ops ops = { - .release = mxr_graph_layer_release, - .buffer_set = mxr_graph_buffer_set, - .stream_set = mxr_graph_stream_set, -diff -Naur backports-4.2.6-1.org/drivers/media/platform/s5p-tv/mixer.h backports-4.2.6-1/drivers/media/platform/s5p-tv/mixer.h ---- backports-4.2.6-1.org/drivers/media/platform/s5p-tv/mixer.h 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/platform/s5p-tv/mixer.h 2016-01-27 12:26:21.266626324 +0100 -@@ -156,7 +156,7 @@ - /** layer index (unique identifier) */ - int idx; - /** callbacks for layer methods */ -- struct mxr_layer_ops ops; -+ struct mxr_layer_ops *ops; - /** format array */ - const struct mxr_format **fmt_array; - /** size of format array */ -diff -Naur backports-4.2.6-1.org/drivers/media/platform/s5p-tv/mixer_reg.c backports-4.2.6-1/drivers/media/platform/s5p-tv/mixer_reg.c ---- backports-4.2.6-1.org/drivers/media/platform/s5p-tv/mixer_reg.c 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/platform/s5p-tv/mixer_reg.c 2016-01-27 12:26:21.266626324 +0100 -@@ -276,7 +276,7 @@ - layer->update_buf = next; - } - -- layer->ops.buffer_set(layer, layer->update_buf); -+ layer->ops->buffer_set(layer, layer->update_buf); - - if (done && done != layer->shadow_buf) - vb2_buffer_done(&done->vb, VB2_BUF_STATE_DONE); -diff -Naur backports-4.2.6-1.org/drivers/media/platform/s5p-tv/mixer_video.c backports-4.2.6-1/drivers/media/platform/s5p-tv/mixer_video.c ---- backports-4.2.6-1.org/drivers/media/platform/s5p-tv/mixer_video.c 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/platform/s5p-tv/mixer_video.c 2016-01-27 12:26:21.266626324 +0100 -@@ -210,7 +210,7 @@ - layer->geo.src.height = layer->geo.src.full_height; - - mxr_geometry_dump(mdev, &layer->geo); -- layer->ops.fix_geometry(layer, MXR_GEOMETRY_SINK, 0); -+ layer->ops->fix_geometry(layer, MXR_GEOMETRY_SINK, 0); - mxr_geometry_dump(mdev, &layer->geo); - } - -@@ -228,7 +228,7 @@ - layer->geo.dst.full_width = mbus_fmt.width; - layer->geo.dst.full_height = mbus_fmt.height; - layer->geo.dst.field = mbus_fmt.field; -- layer->ops.fix_geometry(layer, MXR_GEOMETRY_SINK, 0); -+ layer->ops->fix_geometry(layer, MXR_GEOMETRY_SINK, 0); - - mxr_geometry_dump(mdev, &layer->geo); - } -@@ -334,7 +334,7 @@ - /* set source size to highest accepted value */ - geo->src.full_width = max(geo->dst.full_width, pix->width); - geo->src.full_height = max(geo->dst.full_height, pix->height); -- layer->ops.fix_geometry(layer, MXR_GEOMETRY_SOURCE, 0); -+ layer->ops->fix_geometry(layer, MXR_GEOMETRY_SOURCE, 0); - mxr_geometry_dump(mdev, &layer->geo); - /* set cropping to total visible screen */ - geo->src.width = pix->width; -@@ -342,12 +342,12 @@ - geo->src.x_offset = 0; - geo->src.y_offset = 0; - /* assure consistency of geometry */ -- layer->ops.fix_geometry(layer, MXR_GEOMETRY_CROP, MXR_NO_OFFSET); -+ layer->ops->fix_geometry(layer, MXR_GEOMETRY_CROP, MXR_NO_OFFSET); - mxr_geometry_dump(mdev, &layer->geo); - /* set full size to lowest possible value */ - geo->src.full_width = 0; - geo->src.full_height = 0; -- layer->ops.fix_geometry(layer, MXR_GEOMETRY_SOURCE, 0); -+ layer->ops->fix_geometry(layer, MXR_GEOMETRY_SOURCE, 0); - mxr_geometry_dump(mdev, &layer->geo); - - /* returning results */ -@@ -474,7 +474,7 @@ - target->width = s->r.width; - target->height = s->r.height; - -- layer->ops.fix_geometry(layer, stage, s->flags); -+ layer->ops->fix_geometry(layer, stage, s->flags); - - /* retrieve update selection rectangle */ - res.left = target->x_offset; -@@ -938,13 +938,13 @@ - mxr_output_get(mdev); - - mxr_layer_update_output(layer); -- layer->ops.format_set(layer); -+ layer->ops->format_set(layer); - /* enabling layer in hardware */ - spin_lock_irqsave(&layer->enq_slock, flags); - layer->state = MXR_LAYER_STREAMING; - spin_unlock_irqrestore(&layer->enq_slock, flags); - -- layer->ops.stream_set(layer, MXR_ENABLE); -+ layer->ops->stream_set(layer, MXR_ENABLE); - mxr_streamer_get(mdev); - - return 0; -@@ -1014,7 +1014,7 @@ - spin_unlock_irqrestore(&layer->enq_slock, flags); - - /* disabling layer in hardware */ -- layer->ops.stream_set(layer, MXR_DISABLE); -+ layer->ops->stream_set(layer, MXR_DISABLE); - /* remove one streamer */ - mxr_streamer_put(mdev); - /* allow changes in output configuration */ -@@ -1052,8 +1052,8 @@ - - void mxr_layer_release(struct mxr_layer *layer) - { -- if (layer->ops.release) -- layer->ops.release(layer); -+ if (layer->ops->release) -+ layer->ops->release(layer); - } - - void mxr_base_layer_release(struct mxr_layer *layer) -@@ -1079,7 +1079,7 @@ - - layer->mdev = mdev; - layer->idx = idx; -- layer->ops = *ops; -+ layer->ops = ops; - - spin_lock_init(&layer->enq_slock); - INIT_LIST_HEAD(&layer->enq_list); -diff -Naur backports-4.2.6-1.org/drivers/media/platform/s5p-tv/mixer_vp_layer.c backports-4.2.6-1/drivers/media/platform/s5p-tv/mixer_vp_layer.c ---- backports-4.2.6-1.org/drivers/media/platform/s5p-tv/mixer_vp_layer.c 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/platform/s5p-tv/mixer_vp_layer.c 2016-01-27 12:26:21.266626324 +0100 -@@ -206,7 +206,7 @@ - { - struct mxr_layer *layer; - int ret; -- struct mxr_layer_ops ops = { -+ static struct mxr_layer_ops ops = { - .release = mxr_vp_layer_release, - .buffer_set = mxr_vp_buffer_set, - .stream_set = mxr_vp_stream_set, -diff -Naur backports-4.2.6-1.org/drivers/media/platform/vivid/vivid-osd.c backports-4.2.6-1/drivers/media/platform/vivid/vivid-osd.c ---- backports-4.2.6-1.org/drivers/media/platform/vivid/vivid-osd.c 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/platform/vivid/vivid-osd.c 2016-01-27 12:26:21.269959657 +0100 -@@ -85,6 +85,7 @@ - case FBIOGET_VBLANK: { - struct fb_vblank vblank; - -+ memset(&vblank, 0, sizeof(vblank)); - vblank.flags = FB_VBLANK_HAVE_COUNT | FB_VBLANK_HAVE_VCOUNT | - FB_VBLANK_HAVE_VSYNC; - vblank.count = 0; -diff -Naur backports-4.2.6-1.org/drivers/media/radio/radio-cadet.c backports-4.2.6-1/drivers/media/radio/radio-cadet.c ---- backports-4.2.6-1.org/drivers/media/radio/radio-cadet.c 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/radio/radio-cadet.c 2016-01-27 12:26:21.269959657 +0100 -@@ -333,6 +333,8 @@ - unsigned char readbuf[RDS_BUFFER]; - int i = 0; - -+ if (count > RDS_BUFFER) -+ return -EFAULT; - mutex_lock(&dev->lock); - if (dev->rdsstat == 0) - cadet_start_rds(dev); -@@ -349,8 +351,9 @@ - readbuf[i++] = dev->rdsbuf[dev->rdsout++]; - mutex_unlock(&dev->lock); - -- if (i && copy_to_user(data, readbuf, i)) -- return -EFAULT; -+ if (i > sizeof(readbuf) || (i && copy_to_user(data, readbuf, i))) -+ i = -EFAULT; -+ - return i; - } - -diff -Naur backports-4.2.6-1.org/drivers/media/radio/radio-maxiradio.c backports-4.2.6-1/drivers/media/radio/radio-maxiradio.c ---- backports-4.2.6-1.org/drivers/media/radio/radio-maxiradio.c 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/radio/radio-maxiradio.c 2016-01-27 12:26:21.269959657 +0100 -@@ -61,7 +61,7 @@ - /* TEA5757 pin mappings */ - static const int clk = 1, data = 2, wren = 4, mo_st = 8, power = 16; - --static atomic_t maxiradio_instance = ATOMIC_INIT(0); -+static atomic_unchecked_t maxiradio_instance = ATOMIC_INIT(0); - - #define PCI_VENDOR_ID_GUILLEMOT 0x5046 - #define PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO 0x1001 -diff -Naur backports-4.2.6-1.org/drivers/media/radio/radio-shark2.c backports-4.2.6-1/drivers/media/radio/radio-shark2.c ---- backports-4.2.6-1.org/drivers/media/radio/radio-shark2.c 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/radio/radio-shark2.c 2016-01-27 12:26:21.269959657 +0100 -@@ -74,7 +74,7 @@ - u8 *transfer_buffer; - }; - --static atomic_t shark_instance = ATOMIC_INIT(0); -+static atomic_unchecked_t shark_instance = ATOMIC_INIT(0); - - static int shark_write_reg(struct radio_tea5777 *tea, u64 reg) - { -diff -Naur backports-4.2.6-1.org/drivers/media/radio/radio-shark.c backports-4.2.6-1/drivers/media/radio/radio-shark.c ---- backports-4.2.6-1.org/drivers/media/radio/radio-shark.c 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/radio/radio-shark.c 2016-01-27 12:26:21.269959657 +0100 -@@ -79,7 +79,7 @@ - u32 last_val; - }; - --static atomic_t shark_instance = ATOMIC_INIT(0); -+static atomic_unchecked_t shark_instance = ATOMIC_INIT(0); - - static void shark_write_val(struct snd_tea575x *tea, u32 val) - { -diff -Naur backports-4.2.6-1.org/drivers/media/radio/radio-si476x.c backports-4.2.6-1/drivers/media/radio/radio-si476x.c ---- backports-4.2.6-1.org/drivers/media/radio/radio-si476x.c 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/radio/radio-si476x.c 2016-01-27 12:26:21.269959657 +0100 -@@ -1445,7 +1445,7 @@ - struct si476x_radio *radio; - struct v4l2_ctrl *ctrl; - -- static atomic_t instance = ATOMIC_INIT(0); -+ static atomic_unchecked_t instance = ATOMIC_INIT(0); - - radio = devm_kzalloc(&pdev->dev, sizeof(*radio), GFP_KERNEL); - if (!radio) -diff -Naur backports-4.2.6-1.org/drivers/media/radio/wl128x/fmdrv_common.c backports-4.2.6-1/drivers/media/radio/wl128x/fmdrv_common.c ---- backports-4.2.6-1.org/drivers/media/radio/wl128x/fmdrv_common.c 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/radio/wl128x/fmdrv_common.c 2016-01-27 12:26:21.269959657 +0100 -@@ -71,7 +71,7 @@ - MODULE_PARM_DESC(rds_buf, "RDS buffer entries"); - - /* Radio Nr */ --static u32 radio_nr = -1; -+static int radio_nr = -1; - module_param(radio_nr, int, 0444); - MODULE_PARM_DESC(radio_nr, "Radio Nr"); - -diff -Naur backports-4.2.6-1.org/drivers/media/usb/dvb-usb/cinergyT2-core.c backports-4.2.6-1/drivers/media/usb/dvb-usb/cinergyT2-core.c ---- backports-4.2.6-1.org/drivers/media/usb/dvb-usb/cinergyT2-core.c 2015-11-15 22:19:39.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/usb/dvb-usb/cinergyT2-core.c 2016-01-27 12:26:21.269959657 +0100 -@@ -50,29 +50,73 @@ - - static int cinergyt2_streaming_ctrl(struct dvb_usb_adapter *adap, int enable) - { -- char buf[] = { CINERGYT2_EP1_CONTROL_STREAM_TRANSFER, enable ? 1 : 0 }; -- char result[64]; -- return dvb_usb_generic_rw(adap->dev, buf, sizeof(buf), result, -- sizeof(result), 0); -+ char *buf; -+ char *result; -+ int retval; -+ -+ buf = kmalloc(2, GFP_KERNEL); -+ if (buf == NULL) -+ return -ENOMEM; -+ result = kmalloc(64, GFP_KERNEL); -+ if (result == NULL) { -+ kfree(buf); -+ return -ENOMEM; -+ } -+ -+ buf[0] = CINERGYT2_EP1_CONTROL_STREAM_TRANSFER; -+ buf[1] = enable ? 1 : 0; -+ -+ retval = dvb_usb_generic_rw(adap->dev, buf, 2, result, 64, 0); -+ -+ kfree(buf); -+ kfree(result); -+ return retval; - } - - static int cinergyt2_power_ctrl(struct dvb_usb_device *d, int enable) - { -- char buf[] = { CINERGYT2_EP1_SLEEP_MODE, enable ? 0 : 1 }; -- char state[3]; -- return dvb_usb_generic_rw(d, buf, sizeof(buf), state, sizeof(state), 0); -+ char *buf; -+ char *state; -+ int retval; -+ -+ buf = kmalloc(2, GFP_KERNEL); -+ if (buf == NULL) -+ return -ENOMEM; -+ state = kmalloc(3, GFP_KERNEL); -+ if (state == NULL) { -+ kfree(buf); -+ return -ENOMEM; -+ } -+ -+ buf[0] = CINERGYT2_EP1_SLEEP_MODE; -+ buf[1] = enable ? 1 : 0; -+ -+ retval = dvb_usb_generic_rw(d, buf, 2, state, 3, 0); -+ -+ kfree(buf); -+ kfree(state); -+ return retval; - } - - static int cinergyt2_frontend_attach(struct dvb_usb_adapter *adap) - { -- char query[] = { CINERGYT2_EP1_GET_FIRMWARE_VERSION }; -- char state[3]; -+ char *query; -+ char *state; - int ret; -+ query = kmalloc(1, GFP_KERNEL); -+ if (query == NULL) -+ return -ENOMEM; -+ state = kmalloc(3, GFP_KERNEL); -+ if (state == NULL) { -+ kfree(query); -+ return -ENOMEM; -+ } -+ -+ query[0] = CINERGYT2_EP1_GET_FIRMWARE_VERSION; - - adap->fe_adap[0].fe = cinergyt2_fe_attach(adap->dev); - -- ret = dvb_usb_generic_rw(adap->dev, query, sizeof(query), state, -- sizeof(state), 0); -+ ret = dvb_usb_generic_rw(adap->dev, query, 1, state, 3, 0); - if (ret < 0) { - deb_rc("cinergyt2_power_ctrl() Failed to retrieve sleep " - "state info\n"); -@@ -80,7 +124,8 @@ - - /* Copy this pointer as we are gonna need it in the release phase */ - cinergyt2_usb_device = adap->dev; -- -+ kfree(query); -+ kfree(state); - return 0; - } - -@@ -141,12 +186,23 @@ - static int cinergyt2_rc_query(struct dvb_usb_device *d, u32 *event, int *state) - { - struct cinergyt2_state *st = d->priv; -- u8 key[5] = {0, 0, 0, 0, 0}, cmd = CINERGYT2_EP1_GET_RC_EVENTS; -+ u8 *key, *cmd; - int i; - -+ cmd = kmalloc(1, GFP_KERNEL); -+ if (cmd == NULL) -+ return -EINVAL; -+ key = kzalloc(5, GFP_KERNEL); -+ if (key == NULL) { -+ kfree(cmd); -+ return -EINVAL; -+ } -+ -+ cmd[0] = CINERGYT2_EP1_GET_RC_EVENTS; -+ - *state = REMOTE_NO_KEY_PRESSED; - -- dvb_usb_generic_rw(d, &cmd, 1, key, sizeof(key), 0); -+ dvb_usb_generic_rw(d, cmd, 1, key, 5, 0); - if (key[4] == 0xff) { - /* key repeat */ - st->rc_counter++; -@@ -157,12 +213,12 @@ - *event = d->last_event; - deb_rc("repeat key, event %x\n", - *event); -- return 0; -+ goto out; - } - } - deb_rc("repeated key (non repeatable)\n"); - } -- return 0; -+ goto out; - } - - /* hack to pass checksum on the custom field */ -@@ -174,6 +230,9 @@ - - deb_rc("key: %*ph\n", 5, key); - } -+out: -+ kfree(cmd); -+ kfree(key); - return 0; - } - -diff -Naur backports-4.2.6-1.org/drivers/media/usb/dvb-usb/cinergyT2-fe.c backports-4.2.6-1/drivers/media/usb/dvb-usb/cinergyT2-fe.c ---- backports-4.2.6-1.org/drivers/media/usb/dvb-usb/cinergyT2-fe.c 2015-11-15 22:19:39.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/usb/dvb-usb/cinergyT2-fe.c 2016-01-27 12:26:21.269959657 +0100 -@@ -145,103 +145,176 @@ - enum fe_status *status) - { - struct cinergyt2_fe_state *state = fe->demodulator_priv; -- struct dvbt_get_status_msg result; -- u8 cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS }; -+ struct dvbt_get_status_msg *result; -+ u8 *cmd; - int ret; - -- ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (u8 *)&result, -- sizeof(result), 0); -+ cmd = kmalloc(1, GFP_KERNEL); -+ if (cmd == NULL) -+ return -ENOMEM; -+ result = kmalloc(sizeof(*result), GFP_KERNEL); -+ if (result == NULL) { -+ kfree(cmd); -+ return -ENOMEM; -+ } -+ -+ cmd[0] = CINERGYT2_EP1_GET_TUNER_STATUS; -+ -+ ret = dvb_usb_generic_rw(state->d, cmd, 1, (u8 *)result, -+ sizeof(*result), 0); - if (ret < 0) -- return ret; -+ goto out; - - *status = 0; - -- if (0xffff - le16_to_cpu(result.gain) > 30) -+ if (0xffff - le16_to_cpu(result->gain) > 30) - *status |= FE_HAS_SIGNAL; -- if (result.lock_bits & (1 << 6)) -+ if (result->lock_bits & (1 << 6)) - *status |= FE_HAS_LOCK; -- if (result.lock_bits & (1 << 5)) -+ if (result->lock_bits & (1 << 5)) - *status |= FE_HAS_SYNC; -- if (result.lock_bits & (1 << 4)) -+ if (result->lock_bits & (1 << 4)) - *status |= FE_HAS_CARRIER; -- if (result.lock_bits & (1 << 1)) -+ if (result->lock_bits & (1 << 1)) - *status |= FE_HAS_VITERBI; - - if ((*status & (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) != - (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) - *status &= ~FE_HAS_LOCK; - -- return 0; -+out: -+ kfree(cmd); -+ kfree(result); -+ return ret; - } - - static int cinergyt2_fe_read_ber(struct dvb_frontend *fe, u32 *ber) - { - struct cinergyt2_fe_state *state = fe->demodulator_priv; -- struct dvbt_get_status_msg status; -- char cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS }; -+ struct dvbt_get_status_msg *status; -+ char *cmd; - int ret; - -- ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (char *)&status, -- sizeof(status), 0); -+ cmd = kmalloc(1, GFP_KERNEL); -+ if (cmd == NULL) -+ return -ENOMEM; -+ status = kmalloc(sizeof(*status), GFP_KERNEL); -+ if (status == NULL) { -+ kfree(cmd); -+ return -ENOMEM; -+ } -+ -+ cmd[0] = CINERGYT2_EP1_GET_TUNER_STATUS; -+ -+ ret = dvb_usb_generic_rw(state->d, cmd, 1, (char *)status, -+ sizeof(*status), 0); - if (ret < 0) -- return ret; -+ goto out; - -- *ber = le32_to_cpu(status.viterbi_error_rate); -+ *ber = le32_to_cpu(status->viterbi_error_rate); -+out: -+ kfree(cmd); -+ kfree(status); - return 0; - } - - static int cinergyt2_fe_read_unc_blocks(struct dvb_frontend *fe, u32 *unc) - { - struct cinergyt2_fe_state *state = fe->demodulator_priv; -- struct dvbt_get_status_msg status; -- u8 cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS }; -+ struct dvbt_get_status_msg *status; -+ u8 *cmd; - int ret; - -- ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (u8 *)&status, -- sizeof(status), 0); -+ cmd = kmalloc(1, GFP_KERNEL); -+ if (cmd == NULL) -+ return -ENOMEM; -+ status = kmalloc(sizeof(*status), GFP_KERNEL); -+ if (status == NULL) { -+ kfree(cmd); -+ return -ENOMEM; -+ } -+ -+ cmd[0] = CINERGYT2_EP1_GET_TUNER_STATUS; -+ -+ ret = dvb_usb_generic_rw(state->d, cmd, 1, (u8 *)status, -+ sizeof(*status), 0); - if (ret < 0) { - err("cinergyt2_fe_read_unc_blocks() Failed! (Error=%d)\n", - ret); -- return ret; -+ goto out; - } -- *unc = le32_to_cpu(status.uncorrected_block_count); -- return 0; -+ *unc = le32_to_cpu(status->uncorrected_block_count); -+ -+out: -+ kfree(cmd); -+ kfree(status); -+ return ret; - } - - static int cinergyt2_fe_read_signal_strength(struct dvb_frontend *fe, - u16 *strength) - { - struct cinergyt2_fe_state *state = fe->demodulator_priv; -- struct dvbt_get_status_msg status; -- char cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS }; -+ struct dvbt_get_status_msg *status; -+ char *cmd; - int ret; - -- ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (char *)&status, -- sizeof(status), 0); -+ cmd = kmalloc(1, GFP_KERNEL); -+ if (cmd == NULL) -+ return -ENOMEM; -+ status = kmalloc(sizeof(*status), GFP_KERNEL); -+ if (status == NULL) { -+ kfree(cmd); -+ return -ENOMEM; -+ } -+ -+ cmd[0] = CINERGYT2_EP1_GET_TUNER_STATUS; -+ -+ ret = dvb_usb_generic_rw(state->d, cmd, 1, (char *)status, -+ sizeof(*status), 0); - if (ret < 0) { - err("cinergyt2_fe_read_signal_strength() Failed!" - " (Error=%d)\n", ret); -- return ret; -+ goto out; - } -- *strength = (0xffff - le16_to_cpu(status.gain)); -+ *strength = (0xffff - le16_to_cpu(status->gain)); -+ -+out: -+ kfree(cmd); -+ kfree(status); - return 0; - } - - static int cinergyt2_fe_read_snr(struct dvb_frontend *fe, u16 *snr) - { - struct cinergyt2_fe_state *state = fe->demodulator_priv; -- struct dvbt_get_status_msg status; -- char cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS }; -+ struct dvbt_get_status_msg *status; -+ char *cmd; - int ret; - -- ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (char *)&status, -- sizeof(status), 0); -+ cmd = kmalloc(1, GFP_KERNEL); -+ if (cmd == NULL) -+ return -ENOMEM; -+ status = kmalloc(sizeof(*status), GFP_KERNEL); -+ if (status == NULL) { -+ kfree(cmd); -+ return -ENOMEM; -+ } -+ -+ cmd[0] = CINERGYT2_EP1_GET_TUNER_STATUS; -+ -+ ret = dvb_usb_generic_rw(state->d, cmd, 1, (char *)status, -+ sizeof(*status), 0); - if (ret < 0) { - err("cinergyt2_fe_read_snr() Failed! (Error=%d)\n", ret); -- return ret; -+ goto out; - } -- *snr = (status.snr << 8) | status.snr; -- return 0; -+ *snr = (status->snr << 8) | status->snr; -+ -+out: -+ kfree(cmd); -+ kfree(status); -+ return ret; - } - - static int cinergyt2_fe_init(struct dvb_frontend *fe) -@@ -266,35 +339,46 @@ - { - struct dtv_frontend_properties *fep = &fe->dtv_property_cache; - struct cinergyt2_fe_state *state = fe->demodulator_priv; -- struct dvbt_set_parameters_msg param; -- char result[2]; -+ struct dvbt_set_parameters_msg *param; -+ char *result; - int err; - -- param.cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS; -- param.tps = cpu_to_le16(compute_tps(fep)); -- param.freq = cpu_to_le32(fep->frequency / 1000); -- param.flags = 0; -+ result = kmalloc(2, GFP_KERNEL); -+ if (result == NULL) -+ return -ENOMEM; -+ param = kmalloc(sizeof(*param), GFP_KERNEL); -+ if (param == NULL) { -+ kfree(result); -+ return -ENOMEM; -+ } -+ -+ param->cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS; -+ param->tps = cpu_to_le16(compute_tps(fep)); -+ param->freq = cpu_to_le32(fep->frequency / 1000); -+ param->flags = 0; - - switch (fep->bandwidth_hz) { - default: - case 8000000: -- param.bandwidth = 8; -+ param->bandwidth = 8; - break; - case 7000000: -- param.bandwidth = 7; -+ param->bandwidth = 7; - break; - case 6000000: -- param.bandwidth = 6; -+ param->bandwidth = 6; - break; - } - - err = dvb_usb_generic_rw(state->d, -- (char *)¶m, sizeof(param), -- result, sizeof(result), 0); -+ (char *)param, sizeof(*param), -+ result, 2, 0); - if (err < 0) - err("cinergyt2_fe_set_frontend() Failed! err=%d\n", err); - -- return (err < 0) ? err : 0; -+ kfree(result); -+ kfree(param); -+ return err; - } - - static void cinergyt2_fe_release(struct dvb_frontend *fe) -diff -Naur backports-4.2.6-1.org/drivers/media/usb/dvb-usb/dvb-usb-firmware.c backports-4.2.6-1/drivers/media/usb/dvb-usb/dvb-usb-firmware.c ---- backports-4.2.6-1.org/drivers/media/usb/dvb-usb/dvb-usb-firmware.c 2015-11-15 22:19:39.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/usb/dvb-usb/dvb-usb-firmware.c 2016-01-27 12:26:21.269959657 +0100 -@@ -35,42 +35,57 @@ - - int usb_cypress_load_firmware(struct usb_device *udev, const struct firmware *fw, int type) - { -- struct hexline hx; -- u8 reset; -+ struct hexline *hx; -+ u8 *reset; - int ret,pos=0; - -+ reset = kmalloc(1, GFP_KERNEL); -+ if (reset == NULL) -+ return -ENOMEM; -+ -+ hx = kmalloc(sizeof(struct hexline), GFP_KERNEL); -+ if (hx == NULL) { -+ kfree(reset); -+ return -ENOMEM; -+ } -+ - /* stop the CPU */ -- reset = 1; -- if ((ret = usb_cypress_writemem(udev,cypress[type].cpu_cs_register,&reset,1)) != 1) -+ reset[0] = 1; -+ if ((ret = usb_cypress_writemem(udev,cypress[type].cpu_cs_register,reset,1)) != 1) - err("could not stop the USB controller CPU."); - -- while ((ret = dvb_usb_get_hexline(fw,&hx,&pos)) > 0) { -- deb_fw("writing to address 0x%04x (buffer: 0x%02x %02x)\n",hx.addr,hx.len,hx.chk); -- ret = usb_cypress_writemem(udev,hx.addr,hx.data,hx.len); -+ while ((ret = dvb_usb_get_hexline(fw,hx,&pos)) > 0) { -+ deb_fw("writing to address 0x%04x (buffer: 0x%02x %02x)\n",hx->addr,hx->len,hx->chk); -+ ret = usb_cypress_writemem(udev,hx->addr,hx->data,hx->len); - -- if (ret != hx.len) { -+ if (ret != hx->len) { - err("error while transferring firmware " - "(transferred size: %d, block size: %d)", -- ret,hx.len); -+ ret,hx->len); - ret = -EINVAL; - break; - } - } - if (ret < 0) { - err("firmware download failed at %d with %d",pos,ret); -+ kfree(reset); -+ kfree(hx); - return ret; - } - - if (ret == 0) { - /* restart the CPU */ -- reset = 0; -- if (ret || usb_cypress_writemem(udev,cypress[type].cpu_cs_register,&reset,1) != 1) { -+ reset[0] = 0; -+ if (ret || usb_cypress_writemem(udev,cypress[type].cpu_cs_register,reset,1) != 1) { - err("could not restart the USB controller CPU."); - ret = -EINVAL; - } - } else - ret = -EIO; - -+ kfree(reset); -+ kfree(hx); -+ - return ret; - } - EXPORT_SYMBOL(usb_cypress_load_firmware); -diff -Naur backports-4.2.6-1.org/drivers/media/usb/dvb-usb/technisat-usb2.c backports-4.2.6-1/drivers/media/usb/dvb-usb/technisat-usb2.c ---- backports-4.2.6-1.org/drivers/media/usb/dvb-usb/technisat-usb2.c 2015-11-15 22:19:39.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/usb/dvb-usb/technisat-usb2.c 2016-01-27 12:26:21.269959657 +0100 -@@ -87,8 +87,11 @@ - static int technisat_usb2_i2c_access(struct usb_device *udev, - u8 device_addr, u8 *tx, u8 txlen, u8 *rx, u8 rxlen) - { -- u8 b[64]; -- int ret, actual_length; -+ u8 *b = kmalloc(64, GFP_KERNEL); -+ int ret, actual_length, error = 0; -+ -+ if (b == NULL) -+ return -ENOMEM; - - deb_i2c("i2c-access: %02x, tx: ", device_addr); - debug_dump(tx, txlen, deb_i2c); -@@ -121,7 +124,8 @@ - - if (ret < 0) { - err("i2c-error: out failed %02x = %d", device_addr, ret); -- return -ENODEV; -+ error = -ENODEV; -+ goto out; - } - - ret = usb_bulk_msg(udev, -@@ -129,7 +133,8 @@ - b, 64, &actual_length, 1000); - if (ret < 0) { - err("i2c-error: in failed %02x = %d", device_addr, ret); -- return -ENODEV; -+ error = -ENODEV; -+ goto out; - } - - if (b[0] != I2C_STATUS_OK) { -@@ -137,8 +142,10 @@ - /* handle tuner-i2c-nak */ - if (!(b[0] == I2C_STATUS_NAK && - device_addr == 0x60 -- /* && device_is_technisat_usb2 */)) -- return -ENODEV; -+ /* && device_is_technisat_usb2 */)) { -+ error = -ENODEV; -+ goto out; -+ } - } - - deb_i2c("status: %d, ", b[0]); -@@ -152,7 +159,9 @@ - - deb_i2c("\n"); - -- return 0; -+out: -+ kfree(b); -+ return error; - } - - static int technisat_usb2_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, -@@ -224,14 +233,16 @@ - { - int ret; - -- u8 led[8] = { -- red ? SET_RED_LED_VENDOR_REQUEST : SET_GREEN_LED_VENDOR_REQUEST, -- 0 -- }; -+ u8 *led = kzalloc(8, GFP_KERNEL); -+ -+ if (led == NULL) -+ return -ENOMEM; - - if (disable_led_control && state != TECH_LED_OFF) - return 0; - -+ led[0] = red ? SET_RED_LED_VENDOR_REQUEST : SET_GREEN_LED_VENDOR_REQUEST; -+ - switch (state) { - case TECH_LED_ON: - led[1] = 0x82; -@@ -263,16 +274,22 @@ - red ? SET_RED_LED_VENDOR_REQUEST : SET_GREEN_LED_VENDOR_REQUEST, - USB_TYPE_VENDOR | USB_DIR_OUT, - 0, 0, -- led, sizeof(led), 500); -+ led, 8, 500); - - mutex_unlock(&d->i2c_mutex); -+ -+ kfree(led); -+ - return ret; - } - - static int technisat_usb2_set_led_timer(struct dvb_usb_device *d, u8 red, u8 green) - { - int ret; -- u8 b = 0; -+ u8 *b = kzalloc(1, GFP_KERNEL); -+ -+ if (b == NULL) -+ return -ENOMEM; - - if (mutex_lock_interruptible(&d->i2c_mutex) < 0) - return -EAGAIN; -@@ -281,10 +298,12 @@ - SET_LED_TIMER_DIVIDER_VENDOR_REQUEST, - USB_TYPE_VENDOR | USB_DIR_OUT, - (red << 8) | green, 0, -- &b, 1, 500); -+ b, 1, 500); - - mutex_unlock(&d->i2c_mutex); - -+ kfree(b); -+ - return ret; - } - -@@ -328,7 +347,7 @@ - struct dvb_usb_device_description **desc, int *cold) - { - int ret; -- u8 version[3]; -+ u8 *version = kmalloc(3, GFP_KERNEL); - - /* first select the interface */ - if (usb_set_interface(udev, 0, 1) != 0) -@@ -338,11 +357,14 @@ - - *cold = 0; /* by default do not download a firmware - just in case something is wrong */ - -+ if (version == NULL) -+ return 0; -+ - ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), - GET_VERSION_INFO_VENDOR_REQUEST, - USB_TYPE_VENDOR | USB_DIR_IN, - 0, 0, -- version, sizeof(version), 500); -+ version, 3, 500); - - if (ret < 0) - *cold = 1; -@@ -351,6 +373,8 @@ - *cold = 0; - } - -+ kfree(version); -+ - return 0; - } - -@@ -594,10 +618,15 @@ - - static int technisat_usb2_get_ir(struct dvb_usb_device *d) - { -- u8 buf[62], *b; -+ u8 *buf, *b; - int ret; - struct ir_raw_event ev; - -+ buf = kmalloc(62, GFP_KERNEL); -+ -+ if (buf == NULL) -+ return -ENOMEM; -+ - buf[0] = GET_IR_DATA_VENDOR_REQUEST; - buf[1] = 0x08; - buf[2] = 0x8f; -@@ -620,16 +649,20 @@ - GET_IR_DATA_VENDOR_REQUEST, - USB_TYPE_VENDOR | USB_DIR_IN, - 0x8080, 0, -- buf, sizeof(buf), 500); -+ buf, 62, 500); - - unlock: - mutex_unlock(&d->i2c_mutex); - -- if (ret < 0) -+ if (ret < 0) { -+ kfree(buf); - return ret; -+ } - -- if (ret == 1) -+ if (ret == 1) { -+ kfree(buf); - return 0; /* no key pressed */ -+ } - - /* decoding */ - b = buf+1; -@@ -656,6 +689,8 @@ - - ir_raw_event_handle(d->rc_dev); - -+ kfree(buf); -+ - return 1; - } - -diff -Naur backports-4.2.6-1.org/drivers/media/v4l2-core/v4l2-compat-ioctl32.c backports-4.2.6-1/drivers/media/v4l2-core/v4l2-compat-ioctl32.c ---- backports-4.2.6-1.org/drivers/media/v4l2-core/v4l2-compat-ioctl32.c 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/v4l2-core/v4l2-compat-ioctl32.c 2016-01-27 12:26:21.269959657 +0100 -@@ -429,7 +429,7 @@ - * by passing a very big num_planes value */ - uplane = compat_alloc_user_space(num_planes * - sizeof(struct v4l2_plane)); -- kp->m.planes = (__force struct v4l2_plane *)uplane; -+ kp->m.planes = (__force_kernel struct v4l2_plane *)uplane; - - while (--num_planes >= 0) { - ret = get_v4l2_plane32(uplane, uplane32, kp->memory); -@@ -500,7 +500,7 @@ - if (num_planes == 0) - return 0; - -- uplane = (__force struct v4l2_plane __user *)kp->m.planes; -+ uplane = (struct v4l2_plane __force_user *)kp->m.planes; - if (get_user(p, &up->m.planes)) - return -EFAULT; - uplane32 = compat_ptr(p); -@@ -564,7 +564,7 @@ - get_user(kp->flags, &up->flags) || - copy_from_user(&kp->fmt, &up->fmt, sizeof(up->fmt))) - return -EFAULT; -- kp->base = (__force void *)compat_ptr(tmp); -+ kp->base = (__force_kernel void *)compat_ptr(tmp); - return 0; - } - -@@ -669,7 +669,7 @@ - n * sizeof(struct v4l2_ext_control32))) - return -EFAULT; - kcontrols = compat_alloc_user_space(n * sizeof(struct v4l2_ext_control)); -- kp->controls = (__force struct v4l2_ext_control *)kcontrols; -+ kp->controls = (__force_kernel struct v4l2_ext_control *)kcontrols; - while (--n >= 0) { - u32 id; - -@@ -696,7 +696,7 @@ - { - struct v4l2_ext_control32 __user *ucontrols; - struct v4l2_ext_control __user *kcontrols = -- (__force struct v4l2_ext_control __user *)kp->controls; -+ (struct v4l2_ext_control __force_user *)kp->controls; - int n = kp->count; - compat_caddr_t p; - -@@ -780,7 +780,7 @@ - get_user(tmp, &up->edid) || - copy_from_user(kp->reserved, up->reserved, sizeof(kp->reserved))) - return -EFAULT; -- kp->edid = (__force u8 *)compat_ptr(tmp); -+ kp->edid = (__force_kernel u8 *)compat_ptr(tmp); - return 0; - } - -diff -Naur backports-4.2.6-1.org/drivers/media/v4l2-core/v4l2-device.c backports-4.2.6-1/drivers/media/v4l2-core/v4l2-device.c ---- backports-4.2.6-1.org/drivers/media/v4l2-core/v4l2-device.c 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/v4l2-core/v4l2-device.c 2016-01-27 12:26:21.269959657 +0100 -@@ -74,9 +74,9 @@ - EXPORT_SYMBOL_GPL(v4l2_device_put); - - int v4l2_device_set_name(struct v4l2_device *v4l2_dev, const char *basename, -- atomic_t *instance) -+ atomic_unchecked_t *instance) - { -- int num = atomic_inc_return(instance) - 1; -+ int num = atomic_inc_return_unchecked(instance) - 1; - int len = strlen(basename); - - if (basename[len - 1] >= '0' && basename[len - 1] <= '9') -diff -Naur backports-4.2.6-1.org/drivers/media/v4l2-core/v4l2-ioctl.c backports-4.2.6-1/drivers/media/v4l2-core/v4l2-ioctl.c ---- backports-4.2.6-1.org/drivers/media/v4l2-core/v4l2-ioctl.c 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/drivers/media/v4l2-core/v4l2-ioctl.c 2016-01-27 12:26:21.269959657 +0100 -@@ -2341,7 +2341,8 @@ - struct file *file, void *fh, void *p); - } u; - void (*debug)(const void *arg, bool write_only); --}; -+} __do_const; -+typedef struct v4l2_ioctl_info __no_const v4l2_ioctl_info_no_const; - - /* This control needs a priority check */ - #define INFO_FL_PRIO (1 << 0) -@@ -2525,7 +2526,7 @@ - struct video_device *vfd = video_devdata(file); - const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops; - bool write_only = false; -- struct v4l2_ioctl_info default_info; -+ v4l2_ioctl_info_no_const default_info; - const struct v4l2_ioctl_info *info; - void *fh = file->private_data; - struct v4l2_fh *vfh = NULL; -@@ -2616,7 +2617,7 @@ - ret = -EINVAL; - break; - } -- *user_ptr = (void __user *)buf->m.planes; -+ *user_ptr = (void __force_user *)buf->m.planes; - *kernel_ptr = (void **)&buf->m.planes; - *array_size = sizeof(struct v4l2_plane) * buf->length; - ret = 1; -@@ -2633,7 +2634,7 @@ - ret = -EINVAL; - break; - } -- *user_ptr = (void __user *)edid->edid; -+ *user_ptr = (void __force_user *)edid->edid; - *kernel_ptr = (void **)&edid->edid; - *array_size = edid->blocks * 128; - ret = 1; -@@ -2651,7 +2652,7 @@ - ret = -EINVAL; - break; - } -- *user_ptr = (void __user *)ctrls->controls; -+ *user_ptr = (void __force_user *)ctrls->controls; - *kernel_ptr = (void **)&ctrls->controls; - *array_size = sizeof(struct v4l2_ext_control) - * ctrls->count; -@@ -2752,7 +2753,7 @@ - } - - if (has_array_args) { -- *kernel_ptr = (void __force *)user_ptr; -+ *kernel_ptr = (void __force_kernel *)user_ptr; - if (copy_to_user(user_ptr, mbuf, array_size)) - err = -EFAULT; - goto out_array_args; -diff -Naur backports-4.2.6-1.org/drivers/net/usb/sierra_net.c backports-4.2.6-1/drivers/net/usb/sierra_net.c ---- backports-4.2.6-1.org/drivers/net/usb/sierra_net.c 2015-11-15 22:19:39.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/usb/sierra_net.c 2016-01-27 12:26:21.283292990 +0100 -@@ -51,7 +51,7 @@ - /* atomic counter partially included in MAC address to make sure 2 devices - * do not end up with the same MAC - concept breaks in case of > 255 ifaces - */ --static atomic_t iface_counter = ATOMIC_INIT(0); -+static atomic_unchecked_t iface_counter = ATOMIC_INIT(0); - - /* - * SYNC Timer Delay definition used to set the expiry time -@@ -697,7 +697,7 @@ - dev->net->netdev_ops = &sierra_net_device_ops; - - /* change MAC addr to include, ifacenum, and to be unique */ -- dev->net->dev_addr[ETH_ALEN-2] = atomic_inc_return(&iface_counter); -+ dev->net->dev_addr[ETH_ALEN-2] = atomic_inc_return_unchecked(&iface_counter); - dev->net->dev_addr[ETH_ALEN-1] = ifacenum; - - /* we will have to manufacture ethernet headers, prepare template */ -diff -Naur backports-4.2.6-1.org/drivers/net/wireless/airo.c backports-4.2.6-1/drivers/net/wireless/airo.c ---- backports-4.2.6-1.org/drivers/net/wireless/airo.c 2015-11-15 22:19:39.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/wireless/airo.c 2016-01-27 12:26:21.286626323 +0100 -@@ -7846,7 +7846,7 @@ - struct airo_info *ai = dev->ml_priv; - int ridcode; - int enabled; -- static int (* writer)(struct airo_info *, u16 rid, const void *, int, int); -+ int (* writer)(struct airo_info *, u16 rid, const void *, int, int); - unsigned char *iobuf; - - /* Only super-user can write RIDs */ -diff -Naur backports-4.2.6-1.org/drivers/net/wireless/at76c50x-usb.c backports-4.2.6-1/drivers/net/wireless/at76c50x-usb.c ---- backports-4.2.6-1.org/drivers/net/wireless/at76c50x-usb.c 2015-11-15 22:19:39.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/wireless/at76c50x-usb.c 2016-01-27 12:26:21.286626323 +0100 -@@ -353,7 +353,7 @@ - } - - /* Convert timeout from the DFU status to jiffies */ --static inline unsigned long at76_get_timeout(struct dfu_status *s) -+static inline unsigned long __intentional_overflow(-1) at76_get_timeout(struct dfu_status *s) - { - return msecs_to_jiffies((s->poll_timeout[2] << 16) - | (s->poll_timeout[1] << 8) -diff -Naur backports-4.2.6-1.org/drivers/net/wireless/ath/ath10k/ce.c backports-4.2.6-1/drivers/net/wireless/ath/ath10k/ce.c ---- backports-4.2.6-1.org/drivers/net/wireless/ath/ath10k/ce.c 2015-11-15 22:19:40.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/wireless/ath/ath10k/ce.c 2016-01-27 12:26:21.286626323 +0100 -@@ -896,12 +896,12 @@ - return 0; - } - --static struct ath10k_ce_ring * -+static struct ath10k_ce_ring * __intentional_overflow(-1) - ath10k_ce_alloc_src_ring(struct ath10k *ar, unsigned int ce_id, - const struct ce_attr *attr) - { - struct ath10k_ce_ring *src_ring; -- u32 nentries = attr->src_nentries; -+ unsigned long nentries = attr->src_nentries; - dma_addr_t base_addr; - - nentries = roundup_pow_of_two(nentries); -@@ -968,7 +968,7 @@ - const struct ce_attr *attr) - { - struct ath10k_ce_ring *dest_ring; -- u32 nentries; -+ unsigned long nentries; - dma_addr_t base_addr; - - nentries = roundup_pow_of_two(attr->dest_nentries); -diff -Naur backports-4.2.6-1.org/drivers/net/wireless/ath/ath10k/htc.c backports-4.2.6-1/drivers/net/wireless/ath/ath10k/htc.c ---- backports-4.2.6-1.org/drivers/net/wireless/ath/ath10k/htc.c 2015-11-15 22:19:40.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/wireless/ath/ath10k/htc.c 2016-01-27 12:26:21.286626323 +0100 -@@ -841,7 +841,10 @@ - /* registered target arrival callback from the HIF layer */ - int ath10k_htc_init(struct ath10k *ar) - { -- struct ath10k_hif_cb htc_callbacks; -+ static struct ath10k_hif_cb htc_callbacks = { -+ .rx_completion = ath10k_htc_rx_completion_handler, -+ .tx_completion = ath10k_htc_tx_completion_handler, -+ }; - struct ath10k_htc_ep *ep = NULL; - struct ath10k_htc *htc = &ar->htc; - -@@ -850,8 +853,6 @@ - ath10k_htc_reset_endpoint_states(htc); - - /* setup HIF layer callbacks */ -- htc_callbacks.rx_completion = ath10k_htc_rx_completion_handler; -- htc_callbacks.tx_completion = ath10k_htc_tx_completion_handler; - htc->ar = ar; - - /* Get HIF default pipe for HTC message exchange */ -diff -Naur backports-4.2.6-1.org/drivers/net/wireless/ath/ath10k/htc.h backports-4.2.6-1/drivers/net/wireless/ath/ath10k/htc.h ---- backports-4.2.6-1.org/drivers/net/wireless/ath/ath10k/htc.h 2015-11-15 22:19:40.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/wireless/ath/ath10k/htc.h 2016-01-27 12:26:21.286626323 +0100 -@@ -270,13 +270,13 @@ - - struct ath10k_htc_ops { - void (*target_send_suspend_complete)(struct ath10k *ar); --}; -+} __no_const; - - struct ath10k_htc_ep_ops { - void (*ep_tx_complete)(struct ath10k *, struct sk_buff *); - void (*ep_rx_complete)(struct ath10k *, struct sk_buff *); - void (*ep_tx_credits)(struct ath10k *); --}; -+} __no_const; - - /* service connection information */ - struct ath10k_htc_svc_conn_req { -diff -Naur backports-4.2.6-1.org/drivers/net/wireless/ath/ath9k/ar9002_mac.c backports-4.2.6-1/drivers/net/wireless/ath/ath9k/ar9002_mac.c ---- backports-4.2.6-1.org/drivers/net/wireless/ath/ath9k/ar9002_mac.c 2015-11-15 22:19:39.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/wireless/ath/ath9k/ar9002_mac.c 2016-01-27 12:26:21.286626323 +0100 -@@ -220,8 +220,8 @@ - ads->ds_txstatus6 = ads->ds_txstatus7 = 0; - ads->ds_txstatus8 = ads->ds_txstatus9 = 0; - -- ACCESS_ONCE(ads->ds_link) = i->link; -- ACCESS_ONCE(ads->ds_data) = i->buf_addr[0]; -+ ACCESS_ONCE_RW(ads->ds_link) = i->link; -+ ACCESS_ONCE_RW(ads->ds_data) = i->buf_addr[0]; - - ctl1 = i->buf_len[0] | (i->is_last ? 0 : AR_TxMore); - ctl6 = SM(i->keytype, AR_EncrType); -@@ -235,26 +235,26 @@ - - if ((i->is_first || i->is_last) && - i->aggr != AGGR_BUF_MIDDLE && i->aggr != AGGR_BUF_LAST) { -- ACCESS_ONCE(ads->ds_ctl2) = set11nTries(i->rates, 0) -+ ACCESS_ONCE_RW(ads->ds_ctl2) = set11nTries(i->rates, 0) - | set11nTries(i->rates, 1) - | set11nTries(i->rates, 2) - | set11nTries(i->rates, 3) - | (i->dur_update ? AR_DurUpdateEna : 0) - | SM(0, AR_BurstDur); - -- ACCESS_ONCE(ads->ds_ctl3) = set11nRate(i->rates, 0) -+ ACCESS_ONCE_RW(ads->ds_ctl3) = set11nRate(i->rates, 0) - | set11nRate(i->rates, 1) - | set11nRate(i->rates, 2) - | set11nRate(i->rates, 3); - } else { -- ACCESS_ONCE(ads->ds_ctl2) = 0; -- ACCESS_ONCE(ads->ds_ctl3) = 0; -+ ACCESS_ONCE_RW(ads->ds_ctl2) = 0; -+ ACCESS_ONCE_RW(ads->ds_ctl3) = 0; - } - - if (!i->is_first) { -- ACCESS_ONCE(ads->ds_ctl0) = 0; -- ACCESS_ONCE(ads->ds_ctl1) = ctl1; -- ACCESS_ONCE(ads->ds_ctl6) = ctl6; -+ ACCESS_ONCE_RW(ads->ds_ctl0) = 0; -+ ACCESS_ONCE_RW(ads->ds_ctl1) = ctl1; -+ ACCESS_ONCE_RW(ads->ds_ctl6) = ctl6; - return; - } - -@@ -279,7 +279,7 @@ - break; - } - -- ACCESS_ONCE(ads->ds_ctl0) = (i->pkt_len & AR_FrameLen) -+ ACCESS_ONCE_RW(ads->ds_ctl0) = (i->pkt_len & AR_FrameLen) - | (i->flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0) - | SM(i->txpower[0], AR_XmitPower0) - | (i->flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0) -@@ -289,27 +289,27 @@ - | (i->flags & ATH9K_TXDESC_RTSENA ? AR_RTSEnable : - (i->flags & ATH9K_TXDESC_CTSENA ? AR_CTSEnable : 0)); - -- ACCESS_ONCE(ads->ds_ctl1) = ctl1; -- ACCESS_ONCE(ads->ds_ctl6) = ctl6; -+ ACCESS_ONCE_RW(ads->ds_ctl1) = ctl1; -+ ACCESS_ONCE_RW(ads->ds_ctl6) = ctl6; - - if (i->aggr == AGGR_BUF_MIDDLE || i->aggr == AGGR_BUF_LAST) - return; - -- ACCESS_ONCE(ads->ds_ctl4) = set11nPktDurRTSCTS(i->rates, 0) -+ ACCESS_ONCE_RW(ads->ds_ctl4) = set11nPktDurRTSCTS(i->rates, 0) - | set11nPktDurRTSCTS(i->rates, 1); - -- ACCESS_ONCE(ads->ds_ctl5) = set11nPktDurRTSCTS(i->rates, 2) -+ ACCESS_ONCE_RW(ads->ds_ctl5) = set11nPktDurRTSCTS(i->rates, 2) - | set11nPktDurRTSCTS(i->rates, 3); - -- ACCESS_ONCE(ads->ds_ctl7) = set11nRateFlags(i->rates, 0) -+ ACCESS_ONCE_RW(ads->ds_ctl7) = set11nRateFlags(i->rates, 0) - | set11nRateFlags(i->rates, 1) - | set11nRateFlags(i->rates, 2) - | set11nRateFlags(i->rates, 3) - | SM(i->rtscts_rate, AR_RTSCTSRate); - -- ACCESS_ONCE(ads->ds_ctl9) = SM(i->txpower[1], AR_XmitPower1); -- ACCESS_ONCE(ads->ds_ctl10) = SM(i->txpower[2], AR_XmitPower2); -- ACCESS_ONCE(ads->ds_ctl11) = SM(i->txpower[3], AR_XmitPower3); -+ ACCESS_ONCE_RW(ads->ds_ctl9) = SM(i->txpower[1], AR_XmitPower1); -+ ACCESS_ONCE_RW(ads->ds_ctl10) = SM(i->txpower[2], AR_XmitPower2); -+ ACCESS_ONCE_RW(ads->ds_ctl11) = SM(i->txpower[3], AR_XmitPower3); - } - - static int ar9002_hw_proc_txdesc(struct ath_hw *ah, void *ds, -diff -Naur backports-4.2.6-1.org/drivers/net/wireless/ath/ath9k/ar9003_mac.c backports-4.2.6-1/drivers/net/wireless/ath/ath9k/ar9003_mac.c ---- backports-4.2.6-1.org/drivers/net/wireless/ath/ath9k/ar9003_mac.c 2015-11-15 22:19:39.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/wireless/ath/ath9k/ar9003_mac.c 2016-01-27 12:26:21.286626323 +0100 -@@ -39,47 +39,47 @@ - (i->qcu << AR_TxQcuNum_S) | desc_len; - - checksum += val; -- ACCESS_ONCE(ads->info) = val; -+ ACCESS_ONCE_RW(ads->info) = val; - - checksum += i->link; -- ACCESS_ONCE(ads->link) = i->link; -+ ACCESS_ONCE_RW(ads->link) = i->link; - - checksum += i->buf_addr[0]; -- ACCESS_ONCE(ads->data0) = i->buf_addr[0]; -+ ACCESS_ONCE_RW(ads->data0) = i->buf_addr[0]; - checksum += i->buf_addr[1]; -- ACCESS_ONCE(ads->data1) = i->buf_addr[1]; -+ ACCESS_ONCE_RW(ads->data1) = i->buf_addr[1]; - checksum += i->buf_addr[2]; -- ACCESS_ONCE(ads->data2) = i->buf_addr[2]; -+ ACCESS_ONCE_RW(ads->data2) = i->buf_addr[2]; - checksum += i->buf_addr[3]; -- ACCESS_ONCE(ads->data3) = i->buf_addr[3]; -+ ACCESS_ONCE_RW(ads->data3) = i->buf_addr[3]; - - checksum += (val = (i->buf_len[0] << AR_BufLen_S) & AR_BufLen); -- ACCESS_ONCE(ads->ctl3) = val; -+ ACCESS_ONCE_RW(ads->ctl3) = val; - checksum += (val = (i->buf_len[1] << AR_BufLen_S) & AR_BufLen); -- ACCESS_ONCE(ads->ctl5) = val; -+ ACCESS_ONCE_RW(ads->ctl5) = val; - checksum += (val = (i->buf_len[2] << AR_BufLen_S) & AR_BufLen); -- ACCESS_ONCE(ads->ctl7) = val; -+ ACCESS_ONCE_RW(ads->ctl7) = val; - checksum += (val = (i->buf_len[3] << AR_BufLen_S) & AR_BufLen); -- ACCESS_ONCE(ads->ctl9) = val; -+ ACCESS_ONCE_RW(ads->ctl9) = val; - - checksum = (u16) (((checksum & 0xffff) + (checksum >> 16)) & 0xffff); -- ACCESS_ONCE(ads->ctl10) = checksum; -+ ACCESS_ONCE_RW(ads->ctl10) = checksum; - - if (i->is_first || i->is_last) { -- ACCESS_ONCE(ads->ctl13) = set11nTries(i->rates, 0) -+ ACCESS_ONCE_RW(ads->ctl13) = set11nTries(i->rates, 0) - | set11nTries(i->rates, 1) - | set11nTries(i->rates, 2) - | set11nTries(i->rates, 3) - | (i->dur_update ? AR_DurUpdateEna : 0) - | SM(0, AR_BurstDur); - -- ACCESS_ONCE(ads->ctl14) = set11nRate(i->rates, 0) -+ ACCESS_ONCE_RW(ads->ctl14) = set11nRate(i->rates, 0) - | set11nRate(i->rates, 1) - | set11nRate(i->rates, 2) - | set11nRate(i->rates, 3); - } else { -- ACCESS_ONCE(ads->ctl13) = 0; -- ACCESS_ONCE(ads->ctl14) = 0; -+ ACCESS_ONCE_RW(ads->ctl13) = 0; -+ ACCESS_ONCE_RW(ads->ctl14) = 0; - } - - ads->ctl20 = 0; -@@ -89,17 +89,17 @@ - - ctl17 = SM(i->keytype, AR_EncrType); - if (!i->is_first) { -- ACCESS_ONCE(ads->ctl11) = 0; -- ACCESS_ONCE(ads->ctl12) = i->is_last ? 0 : AR_TxMore; -- ACCESS_ONCE(ads->ctl15) = 0; -- ACCESS_ONCE(ads->ctl16) = 0; -- ACCESS_ONCE(ads->ctl17) = ctl17; -- ACCESS_ONCE(ads->ctl18) = 0; -- ACCESS_ONCE(ads->ctl19) = 0; -+ ACCESS_ONCE_RW(ads->ctl11) = 0; -+ ACCESS_ONCE_RW(ads->ctl12) = i->is_last ? 0 : AR_TxMore; -+ ACCESS_ONCE_RW(ads->ctl15) = 0; -+ ACCESS_ONCE_RW(ads->ctl16) = 0; -+ ACCESS_ONCE_RW(ads->ctl17) = ctl17; -+ ACCESS_ONCE_RW(ads->ctl18) = 0; -+ ACCESS_ONCE_RW(ads->ctl19) = 0; - return; - } - -- ACCESS_ONCE(ads->ctl11) = (i->pkt_len & AR_FrameLen) -+ ACCESS_ONCE_RW(ads->ctl11) = (i->pkt_len & AR_FrameLen) - | (i->flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0) - | SM(i->txpower[0], AR_XmitPower0) - | (i->flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0) -@@ -135,26 +135,26 @@ - val = (i->flags & ATH9K_TXDESC_PAPRD) >> ATH9K_TXDESC_PAPRD_S; - ctl12 |= SM(val, AR_PAPRDChainMask); - -- ACCESS_ONCE(ads->ctl12) = ctl12; -- ACCESS_ONCE(ads->ctl17) = ctl17; -+ ACCESS_ONCE_RW(ads->ctl12) = ctl12; -+ ACCESS_ONCE_RW(ads->ctl17) = ctl17; - -- ACCESS_ONCE(ads->ctl15) = set11nPktDurRTSCTS(i->rates, 0) -+ ACCESS_ONCE_RW(ads->ctl15) = set11nPktDurRTSCTS(i->rates, 0) - | set11nPktDurRTSCTS(i->rates, 1); - -- ACCESS_ONCE(ads->ctl16) = set11nPktDurRTSCTS(i->rates, 2) -+ ACCESS_ONCE_RW(ads->ctl16) = set11nPktDurRTSCTS(i->rates, 2) - | set11nPktDurRTSCTS(i->rates, 3); - -- ACCESS_ONCE(ads->ctl18) = set11nRateFlags(i->rates, 0) -+ ACCESS_ONCE_RW(ads->ctl18) = set11nRateFlags(i->rates, 0) - | set11nRateFlags(i->rates, 1) - | set11nRateFlags(i->rates, 2) - | set11nRateFlags(i->rates, 3) - | SM(i->rtscts_rate, AR_RTSCTSRate); - -- ACCESS_ONCE(ads->ctl19) = AR_Not_Sounding; -+ ACCESS_ONCE_RW(ads->ctl19) = AR_Not_Sounding; - -- ACCESS_ONCE(ads->ctl20) = SM(i->txpower[1], AR_XmitPower1); -- ACCESS_ONCE(ads->ctl21) = SM(i->txpower[2], AR_XmitPower2); -- ACCESS_ONCE(ads->ctl22) = SM(i->txpower[3], AR_XmitPower3); -+ ACCESS_ONCE_RW(ads->ctl20) = SM(i->txpower[1], AR_XmitPower1); -+ ACCESS_ONCE_RW(ads->ctl21) = SM(i->txpower[2], AR_XmitPower2); -+ ACCESS_ONCE_RW(ads->ctl22) = SM(i->txpower[3], AR_XmitPower3); - } - - static u16 ar9003_calc_ptr_chksum(struct ar9003_txc *ads) -diff -Naur backports-4.2.6-1.org/drivers/net/wireless/ath/ath9k/hw.h backports-4.2.6-1/drivers/net/wireless/ath/ath9k/hw.h ---- backports-4.2.6-1.org/drivers/net/wireless/ath/ath9k/hw.h 2015-11-15 22:19:39.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/wireless/ath/ath9k/hw.h 2016-01-27 12:33:44.649931973 +0100 -@@ -671,7 +671,7 @@ - #ifdef CPTCFG_ATH9K_BTCOEX_SUPPORT - bool (*is_aic_enabled)(struct ath_hw *ah); - #endif /* CPTCFG_ATH9K_BTCOEX_SUPPORT */ --}; -+} __no_const; - - /** - * struct ath_spec_scan - parameters for Atheros spectral scan -@@ -747,7 +747,7 @@ - #ifdef CPTCFG_ATH9K_BTCOEX_SUPPORT - void (*set_bt_ant_diversity)(struct ath_hw *hw, bool enable); - #endif --}; -+} __no_const; - - struct ath_nf_limits { - s16 max; -diff -Naur backports-4.2.6-1.org/drivers/net/wireless/ath/ath9k/Kconfig backports-4.2.6-1/drivers/net/wireless/ath/ath9k/Kconfig ---- backports-4.2.6-1.org/drivers/net/wireless/ath/ath9k/Kconfig 2015-11-15 22:19:40.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/wireless/ath/ath9k/Kconfig 2016-01-27 12:34:48.923262299 +0100 -@@ -5,7 +5,6 @@ - tristate - depends on m - select ATH_COMMON -- depends on DEBUG_FS - depends on RELAY - config ATH9K_DFS_DEBUGFS - def_bool y -diff -Naur backports-4.2.6-1.org/drivers/net/wireless/ath/ath9k/main.c backports-4.2.6-1/drivers/net/wireless/ath/ath9k/main.c ---- backports-4.2.6-1.org/drivers/net/wireless/ath/ath9k/main.c 2015-11-15 22:19:39.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/wireless/ath/ath9k/main.c 2016-01-27 12:26:21.289959656 +0100 -@@ -2574,16 +2574,18 @@ - if (!ath9k_is_chanctx_enabled()) - return; - -- ath9k_ops.hw_scan = ath9k_hw_scan; -- ath9k_ops.cancel_hw_scan = ath9k_cancel_hw_scan; -- ath9k_ops.remain_on_channel = ath9k_remain_on_channel; -- ath9k_ops.cancel_remain_on_channel = ath9k_cancel_remain_on_channel; -- ath9k_ops.add_chanctx = ath9k_add_chanctx; -- ath9k_ops.remove_chanctx = ath9k_remove_chanctx; -- ath9k_ops.change_chanctx = ath9k_change_chanctx; -- ath9k_ops.assign_vif_chanctx = ath9k_assign_vif_chanctx; -- ath9k_ops.unassign_vif_chanctx = ath9k_unassign_vif_chanctx; -- ath9k_ops.mgd_prepare_tx = ath9k_mgd_prepare_tx; -+ pax_open_kernel(); -+ *(void **)&ath9k_ops.hw_scan = ath9k_hw_scan; -+ *(void **)&ath9k_ops.cancel_hw_scan = ath9k_cancel_hw_scan; -+ *(void **)&ath9k_ops.remain_on_channel = ath9k_remain_on_channel; -+ *(void **)&ath9k_ops.cancel_remain_on_channel = ath9k_cancel_remain_on_channel; -+ *(void **)&ath9k_ops.add_chanctx = ath9k_add_chanctx; -+ *(void **)&ath9k_ops.remove_chanctx = ath9k_remove_chanctx; -+ *(void **)&ath9k_ops.change_chanctx = ath9k_change_chanctx; -+ *(void **)&ath9k_ops.assign_vif_chanctx = ath9k_assign_vif_chanctx; -+ *(void **)&ath9k_ops.unassign_vif_chanctx = ath9k_unassign_vif_chanctx; -+ *(void **)&ath9k_ops.mgd_prepare_tx = ath9k_mgd_prepare_tx; -+ pax_close_kernel(); - } - - #endif -diff -Naur backports-4.2.6-1.org/drivers/net/wireless/b43/phy_lp.c backports-4.2.6-1/drivers/net/wireless/b43/phy_lp.c ---- backports-4.2.6-1.org/drivers/net/wireless/b43/phy_lp.c 2015-11-15 22:19:39.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/wireless/b43/phy_lp.c 2016-01-27 12:26:21.289959656 +0100 -@@ -2502,7 +2502,7 @@ - { - struct ssb_bus *bus = dev->dev->sdev->bus; - -- static const struct b206x_channel *chandata = NULL; -+ const struct b206x_channel *chandata = NULL; - u32 crystal_freq = bus->chipco.pmu.crystalfreq * 1000; - u32 freqref, vco_freq, val1, val2, val3, timeout, timeoutref, count; - u16 old_comm15, scale; -diff -Naur backports-4.2.6-1.org/drivers/net/wireless/iwlegacy/3945-mac.c backports-4.2.6-1/drivers/net/wireless/iwlegacy/3945-mac.c ---- backports-4.2.6-1.org/drivers/net/wireless/iwlegacy/3945-mac.c 2015-11-15 22:19:40.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/wireless/iwlegacy/3945-mac.c 2016-01-27 12:26:21.289959656 +0100 -@@ -3633,7 +3633,9 @@ - */ - if (il3945_mod_params.disable_hw_scan) { - D_INFO("Disabling hw_scan\n"); -- il3945_mac_ops.hw_scan = NULL; -+ pax_open_kernel(); -+ *(void **)&il3945_mac_ops.hw_scan = NULL; -+ pax_close_kernel(); - } - - D_INFO("*** LOAD DRIVER ***\n"); -diff -Naur backports-4.2.6-1.org/drivers/net/wireless/iwlwifi/dvm/debugfs.c backports-4.2.6-1/drivers/net/wireless/iwlwifi/dvm/debugfs.c ---- backports-4.2.6-1.org/drivers/net/wireless/iwlwifi/dvm/debugfs.c 2015-11-15 22:19:39.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/wireless/iwlwifi/dvm/debugfs.c 2016-01-27 12:26:21.289959656 +0100 -@@ -188,7 +188,7 @@ - { - struct iwl_priv *priv = file->private_data; - char buf[64]; -- int buf_size; -+ size_t buf_size; - u32 offset, len; - - memset(buf, 0, sizeof(buf)); -@@ -458,7 +458,7 @@ - struct iwl_priv *priv = file->private_data; - - char buf[8]; -- int buf_size; -+ size_t buf_size; - u32 reset_flag; - - memset(buf, 0, sizeof(buf)); -@@ -539,7 +539,7 @@ - { - struct iwl_priv *priv = file->private_data; - char buf[8]; -- int buf_size; -+ size_t buf_size; - int ht40; - - memset(buf, 0, sizeof(buf)); -@@ -591,7 +591,7 @@ - { - struct iwl_priv *priv = file->private_data; - char buf[8]; -- int buf_size; -+ size_t buf_size; - int value; - - memset(buf, 0, sizeof(buf)); -@@ -683,10 +683,10 @@ - DEBUGFS_READ_WRITE_FILE_OPS(sleep_level_override); - DEBUGFS_READ_FILE_OPS(current_sleep_command); - --static const char *fmt_value = " %-30s %10u\n"; --static const char *fmt_hex = " %-30s 0x%02X\n"; --static const char *fmt_table = " %-30s %10u %10u %10u %10u\n"; --static const char *fmt_header = -+static const char fmt_value[] = " %-30s %10u\n"; -+static const char fmt_hex[] = " %-30s 0x%02X\n"; -+static const char fmt_table[] = " %-30s %10u %10u %10u %10u\n"; -+static const char fmt_header[] = - "%-32s current cumulative delta max\n"; - - static int iwl_statistics_flag(struct iwl_priv *priv, char *buf, int bufsz) -@@ -1856,7 +1856,7 @@ - { - struct iwl_priv *priv = file->private_data; - char buf[8]; -- int buf_size; -+ size_t buf_size; - int clear; - - memset(buf, 0, sizeof(buf)); -@@ -1901,7 +1901,7 @@ - { - struct iwl_priv *priv = file->private_data; - char buf[8]; -- int buf_size; -+ size_t buf_size; - int trace; - - memset(buf, 0, sizeof(buf)); -@@ -1972,7 +1972,7 @@ - { - struct iwl_priv *priv = file->private_data; - char buf[8]; -- int buf_size; -+ size_t buf_size; - int missed; - - memset(buf, 0, sizeof(buf)); -@@ -2013,7 +2013,7 @@ - - struct iwl_priv *priv = file->private_data; - char buf[8]; -- int buf_size; -+ size_t buf_size; - int plcp; - - memset(buf, 0, sizeof(buf)); -@@ -2073,7 +2073,7 @@ - - struct iwl_priv *priv = file->private_data; - char buf[8]; -- int buf_size; -+ size_t buf_size; - int flush; - - memset(buf, 0, sizeof(buf)); -@@ -2163,7 +2163,7 @@ - - struct iwl_priv *priv = file->private_data; - char buf[8]; -- int buf_size; -+ size_t buf_size; - int rts; - - if (!priv->cfg->ht_params) -@@ -2204,7 +2204,7 @@ - { - struct iwl_priv *priv = file->private_data; - char buf[8]; -- int buf_size; -+ size_t buf_size; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); -@@ -2238,7 +2238,7 @@ - struct iwl_priv *priv = file->private_data; - u32 event_log_flag; - char buf[8]; -- int buf_size; -+ size_t buf_size; - - /* check that the interface is up */ - if (!iwl_is_ready(priv)) -@@ -2292,7 +2292,7 @@ - struct iwl_priv *priv = file->private_data; - char buf[8]; - u32 calib_disabled; -- int buf_size; -+ size_t buf_size; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); -diff -Naur backports-4.2.6-1.org/drivers/net/wireless/iwlwifi/pcie/trans.c backports-4.2.6-1/drivers/net/wireless/iwlwifi/pcie/trans.c ---- backports-4.2.6-1.org/drivers/net/wireless/iwlwifi/pcie/trans.c 2015-11-15 22:19:39.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/wireless/iwlwifi/pcie/trans.c 2016-01-27 12:26:21.289959656 +0100 -@@ -1950,7 +1950,7 @@ - struct isr_statistics *isr_stats = &trans_pcie->isr_stats; - - char buf[8]; -- int buf_size; -+ size_t buf_size; - u32 reset_flag; - - memset(buf, 0, sizeof(buf)); -@@ -1971,7 +1971,7 @@ - { - struct iwl_trans *trans = file->private_data; - char buf[8]; -- int buf_size; -+ size_t buf_size; - int csr; - - memset(buf, 0, sizeof(buf)); -diff -Naur backports-4.2.6-1.org/drivers/net/wireless/mac80211_hwsim.c backports-4.2.6-1/drivers/net/wireless/mac80211_hwsim.c ---- backports-4.2.6-1.org/drivers/net/wireless/mac80211_hwsim.c 2015-11-15 22:19:39.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/wireless/mac80211_hwsim.c 2016-01-27 12:26:21.289959656 +0100 -@@ -3150,20 +3150,20 @@ - if (channels < 1) - return -EINVAL; - -- mac80211_hwsim_mchan_ops = mac80211_hwsim_ops; -- mac80211_hwsim_mchan_ops.hw_scan = mac80211_hwsim_hw_scan; -- mac80211_hwsim_mchan_ops.cancel_hw_scan = mac80211_hwsim_cancel_hw_scan; -- mac80211_hwsim_mchan_ops.sw_scan_start = NULL; -- mac80211_hwsim_mchan_ops.sw_scan_complete = NULL; -- mac80211_hwsim_mchan_ops.remain_on_channel = mac80211_hwsim_roc; -- mac80211_hwsim_mchan_ops.cancel_remain_on_channel = mac80211_hwsim_croc; -- mac80211_hwsim_mchan_ops.add_chanctx = mac80211_hwsim_add_chanctx; -- mac80211_hwsim_mchan_ops.remove_chanctx = mac80211_hwsim_remove_chanctx; -- mac80211_hwsim_mchan_ops.change_chanctx = mac80211_hwsim_change_chanctx; -- mac80211_hwsim_mchan_ops.assign_vif_chanctx = -- mac80211_hwsim_assign_vif_chanctx; -- mac80211_hwsim_mchan_ops.unassign_vif_chanctx = -- mac80211_hwsim_unassign_vif_chanctx; -+ pax_open_kernel(); -+ memcpy((void *)&mac80211_hwsim_mchan_ops, &mac80211_hwsim_ops, sizeof mac80211_hwsim_mchan_ops); -+ *(void **)&mac80211_hwsim_mchan_ops.hw_scan = mac80211_hwsim_hw_scan; -+ *(void **)&mac80211_hwsim_mchan_ops.cancel_hw_scan = mac80211_hwsim_cancel_hw_scan; -+ *(void **)&mac80211_hwsim_mchan_ops.sw_scan_start = NULL; -+ *(void **)&mac80211_hwsim_mchan_ops.sw_scan_complete = NULL; -+ *(void **)&mac80211_hwsim_mchan_ops.remain_on_channel = mac80211_hwsim_roc; -+ *(void **)&mac80211_hwsim_mchan_ops.cancel_remain_on_channel = mac80211_hwsim_croc; -+ *(void **)&mac80211_hwsim_mchan_ops.add_chanctx = mac80211_hwsim_add_chanctx; -+ *(void **)&mac80211_hwsim_mchan_ops.remove_chanctx = mac80211_hwsim_remove_chanctx; -+ *(void **)&mac80211_hwsim_mchan_ops.change_chanctx = mac80211_hwsim_change_chanctx; -+ *(void **)&mac80211_hwsim_mchan_ops.assign_vif_chanctx = mac80211_hwsim_assign_vif_chanctx; -+ *(void **)&mac80211_hwsim_mchan_ops.unassign_vif_chanctx = mac80211_hwsim_unassign_vif_chanctx; -+ pax_close_kernel(); - - spin_lock_init(&hwsim_radio_lock); - INIT_LIST_HEAD(&hwsim_radios); -diff -Naur backports-4.2.6-1.org/drivers/net/wireless/rndis_wlan.c backports-4.2.6-1/drivers/net/wireless/rndis_wlan.c ---- backports-4.2.6-1.org/drivers/net/wireless/rndis_wlan.c 2015-11-15 22:19:39.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/wireless/rndis_wlan.c 2016-01-27 12:26:21.293292990 +0100 -@@ -1236,7 +1236,7 @@ - - netdev_dbg(usbdev->net, "%s(): %i\n", __func__, rts_threshold); - -- if (rts_threshold < 0 || rts_threshold > 2347) -+ if (rts_threshold > 2347) - rts_threshold = 2347; - - tmp = cpu_to_le32(rts_threshold); -diff -Naur backports-4.2.6-1.org/drivers/net/wireless/rt2x00/rt2x00.h backports-4.2.6-1/drivers/net/wireless/rt2x00/rt2x00.h ---- backports-4.2.6-1.org/drivers/net/wireless/rt2x00/rt2x00.h 2015-11-15 22:19:39.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/wireless/rt2x00/rt2x00.h 2016-01-27 12:26:21.293292990 +0100 -@@ -375,7 +375,7 @@ - * for hardware which doesn't support hardware - * sequence counting. - */ -- atomic_t seqno; -+ atomic_unchecked_t seqno; - }; - - static inline struct rt2x00_intf* vif_to_intf(struct ieee80211_vif *vif) -diff -Naur backports-4.2.6-1.org/drivers/net/wireless/rt2x00/rt2x00queue.c backports-4.2.6-1/drivers/net/wireless/rt2x00/rt2x00queue.c ---- backports-4.2.6-1.org/drivers/net/wireless/rt2x00/rt2x00queue.c 2015-11-15 22:19:39.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/wireless/rt2x00/rt2x00queue.c 2016-01-27 12:26:21.293292990 +0100 -@@ -224,9 +224,9 @@ - * sequence counter given by mac80211. - */ - if (test_bit(ENTRY_TXD_FIRST_FRAGMENT, &txdesc->flags)) -- seqno = atomic_add_return(0x10, &intf->seqno); -+ seqno = atomic_add_return_unchecked(0x10, &intf->seqno); - else -- seqno = atomic_read(&intf->seqno); -+ seqno = atomic_read_unchecked(&intf->seqno); - - hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); - hdr->seq_ctrl |= cpu_to_le16(seqno); -diff -Naur backports-4.2.6-1.org/drivers/net/wireless/ti/wl1251/sdio.c backports-4.2.6-1/drivers/net/wireless/ti/wl1251/sdio.c ---- backports-4.2.6-1.org/drivers/net/wireless/ti/wl1251/sdio.c 2015-11-15 22:19:39.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/wireless/ti/wl1251/sdio.c 2016-01-27 12:26:21.293292990 +0100 -@@ -282,13 +282,17 @@ - - irq_set_irq_type(wl->irq, IRQ_TYPE_EDGE_RISING); - -- wl1251_sdio_ops.enable_irq = wl1251_enable_line_irq; -- wl1251_sdio_ops.disable_irq = wl1251_disable_line_irq; -+ pax_open_kernel(); -+ *(void **)&wl1251_sdio_ops.enable_irq = wl1251_enable_line_irq; -+ *(void **)&wl1251_sdio_ops.disable_irq = wl1251_disable_line_irq; -+ pax_close_kernel(); - - wl1251_info("using dedicated interrupt line"); - } else { -- wl1251_sdio_ops.enable_irq = wl1251_sdio_enable_irq; -- wl1251_sdio_ops.disable_irq = wl1251_sdio_disable_irq; -+ pax_open_kernel(); -+ *(void **)&wl1251_sdio_ops.enable_irq = wl1251_sdio_enable_irq; -+ *(void **)&wl1251_sdio_ops.disable_irq = wl1251_sdio_disable_irq; -+ pax_close_kernel(); - - wl1251_info("using SDIO interrupt"); - } -diff -Naur backports-4.2.6-1.org/drivers/net/wireless/ti/wl12xx/main.c backports-4.2.6-1/drivers/net/wireless/ti/wl12xx/main.c ---- backports-4.2.6-1.org/drivers/net/wireless/ti/wl12xx/main.c 2015-11-15 22:19:39.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/wireless/ti/wl12xx/main.c 2016-01-27 12:26:21.293292990 +0100 -@@ -655,7 +655,9 @@ - sizeof(wl->conf.mem)); - - /* read data preparation is only needed by wl127x */ -- wl->ops->prepare_read = wl127x_prepare_read; -+ pax_open_kernel(); -+ *(void **)&wl->ops->prepare_read = wl127x_prepare_read; -+ pax_close_kernel(); - - wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER, - WL127X_IFTYPE_SR_VER, WL127X_MAJOR_SR_VER, -@@ -680,7 +682,9 @@ - sizeof(wl->conf.mem)); - - /* read data preparation is only needed by wl127x */ -- wl->ops->prepare_read = wl127x_prepare_read; -+ pax_open_kernel(); -+ *(void **)&wl->ops->prepare_read = wl127x_prepare_read; -+ pax_close_kernel(); - - wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER, - WL127X_IFTYPE_SR_VER, WL127X_MAJOR_SR_VER, -diff -Naur backports-4.2.6-1.org/drivers/net/wireless/ti/wl18xx/main.c backports-4.2.6-1/drivers/net/wireless/ti/wl18xx/main.c ---- backports-4.2.6-1.org/drivers/net/wireless/ti/wl18xx/main.c 2015-11-15 22:19:39.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/wireless/ti/wl18xx/main.c 2016-01-27 12:26:21.293292990 +0100 -@@ -1952,8 +1952,10 @@ - } - - if (!checksum_param) { -- wl18xx_ops.set_rx_csum = NULL; -- wl18xx_ops.init_vif = NULL; -+ pax_open_kernel(); -+ *(void **)&wl18xx_ops.set_rx_csum = NULL; -+ *(void **)&wl18xx_ops.init_vif = NULL; -+ pax_close_kernel(); - } - - /* Enable 11a Band only if we have 5G antennas */ -diff -Naur backports-4.2.6-1.org/drivers/net/wireless/zd1211rw/zd_usb.c backports-4.2.6-1/drivers/net/wireless/zd1211rw/zd_usb.c ---- backports-4.2.6-1.org/drivers/net/wireless/zd1211rw/zd_usb.c 2015-11-15 22:19:39.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/wireless/zd1211rw/zd_usb.c 2016-01-27 12:26:21.293292990 +0100 -@@ -385,7 +385,7 @@ - { - struct zd_usb *usb = urb->context; - struct zd_usb_interrupt *intr = &usb->intr; -- int len; -+ unsigned int len; - u16 int_num; - - ZD_ASSERT(in_interrupt()); -diff -Naur backports-4.2.6-1.org/drivers/nfc/nfcwilink.c backports-4.2.6-1/drivers/nfc/nfcwilink.c ---- backports-4.2.6-1.org/drivers/nfc/nfcwilink.c 2015-11-15 22:19:39.000000000 +0100 -+++ backports-4.2.6-1/drivers/nfc/nfcwilink.c 2016-01-27 12:26:21.293292990 +0100 -@@ -497,7 +497,7 @@ - - static int nfcwilink_probe(struct platform_device *pdev) - { -- static struct nfcwilink *drv; -+ struct nfcwilink *drv; - int rc; - __u32 protocols; - -diff -Naur backports-4.2.6-1.org/include/linux/gracl_compat.h backports-4.2.6-1/include/linux/gracl_compat.h ---- backports-4.2.6-1.org/include/linux/gracl_compat.h 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/include/linux/gracl_compat.h 2016-01-27 12:26:26.289959354 +0100 -@@ -0,0 +1,156 @@ -+#ifndef GR_ACL_COMPAT_H -+#define GR_ACL_COMPAT_H -+ -+#include -+#include -+ -+struct sprole_pw_compat { -+ compat_uptr_t rolename; -+ unsigned char salt[GR_SALT_LEN]; -+ unsigned char sum[GR_SHA_LEN]; -+}; -+ -+struct gr_hash_struct_compat { -+ compat_uptr_t table; -+ compat_uptr_t nametable; -+ compat_uptr_t first; -+ __u32 table_size; -+ __u32 used_size; -+ int type; -+}; -+ -+struct acl_subject_label_compat { -+ compat_uptr_t filename; -+ compat_u64 inode; -+ __u32 device; -+ __u32 mode; -+ kernel_cap_t cap_mask; -+ kernel_cap_t cap_lower; -+ kernel_cap_t cap_invert_audit; -+ -+ struct compat_rlimit res[GR_NLIMITS]; -+ __u32 resmask; -+ -+ __u8 user_trans_type; -+ __u8 group_trans_type; -+ compat_uptr_t user_transitions; -+ compat_uptr_t group_transitions; -+ __u16 user_trans_num; -+ __u16 group_trans_num; -+ -+ __u32 sock_families[2]; -+ __u32 ip_proto[8]; -+ __u32 ip_type; -+ compat_uptr_t ips; -+ __u32 ip_num; -+ __u32 inaddr_any_override; -+ -+ __u32 crashes; -+ compat_ulong_t expires; -+ -+ compat_uptr_t parent_subject; -+ compat_uptr_t hash; -+ compat_uptr_t prev; -+ compat_uptr_t next; -+ -+ compat_uptr_t obj_hash; -+ __u32 obj_hash_size; -+ __u16 pax_flags; -+}; -+ -+struct role_allowed_ip_compat { -+ __u32 addr; -+ __u32 netmask; -+ -+ compat_uptr_t prev; -+ compat_uptr_t next; -+}; -+ -+struct role_transition_compat { -+ compat_uptr_t rolename; -+ -+ compat_uptr_t prev; -+ compat_uptr_t next; -+}; -+ -+struct acl_role_label_compat { -+ compat_uptr_t rolename; -+ uid_t uidgid; -+ __u16 roletype; -+ -+ __u16 auth_attempts; -+ compat_ulong_t expires; -+ -+ compat_uptr_t root_label; -+ compat_uptr_t hash; -+ -+ compat_uptr_t prev; -+ compat_uptr_t next; -+ -+ compat_uptr_t transitions; -+ compat_uptr_t allowed_ips; -+ compat_uptr_t domain_children; -+ __u16 domain_child_num; -+ -+ umode_t umask; -+ -+ compat_uptr_t subj_hash; -+ __u32 subj_hash_size; -+}; -+ -+struct user_acl_role_db_compat { -+ compat_uptr_t r_table; -+ __u32 num_pointers; -+ __u32 num_roles; -+ __u32 num_domain_children; -+ __u32 num_subjects; -+ __u32 num_objects; -+}; -+ -+struct acl_object_label_compat { -+ compat_uptr_t filename; -+ compat_u64 inode; -+ __u32 device; -+ __u32 mode; -+ -+ compat_uptr_t nested; -+ compat_uptr_t globbed; -+ -+ compat_uptr_t prev; -+ compat_uptr_t next; -+}; -+ -+struct acl_ip_label_compat { -+ compat_uptr_t iface; -+ __u32 addr; -+ __u32 netmask; -+ __u16 low, high; -+ __u8 mode; -+ __u32 type; -+ __u32 proto[8]; -+ -+ compat_uptr_t prev; -+ compat_uptr_t next; -+}; -+ -+struct gr_arg_compat { -+ struct user_acl_role_db_compat role_db; -+ unsigned char pw[GR_PW_LEN]; -+ unsigned char salt[GR_SALT_LEN]; -+ unsigned char sum[GR_SHA_LEN]; -+ unsigned char sp_role[GR_SPROLE_LEN]; -+ compat_uptr_t sprole_pws; -+ __u32 segv_device; -+ compat_u64 segv_inode; -+ uid_t segv_uid; -+ __u16 num_sprole_pws; -+ __u16 mode; -+}; -+ -+struct gr_arg_wrapper_compat { -+ compat_uptr_t arg; -+ __u32 version; -+ __u32 size; -+}; -+ -+#endif -diff -Naur backports-4.2.6-1.org/include/linux/gracl.h backports-4.2.6-1/include/linux/gracl.h ---- backports-4.2.6-1.org/include/linux/gracl.h 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/include/linux/gracl.h 2016-01-27 12:26:26.289959354 +0100 -@@ -0,0 +1,342 @@ -+#ifndef GR_ACL_H -+#define GR_ACL_H -+ -+#include -+#include -+#include -+#include -+#include -+ -+/* Major status information */ -+ -+#define GR_VERSION "grsecurity 3.1" -+#define GRSECURITY_VERSION 0x3100 -+ -+enum { -+ GR_SHUTDOWN = 0, -+ GR_ENABLE = 1, -+ GR_SPROLE = 2, -+ GR_OLDRELOAD = 3, -+ GR_SEGVMOD = 4, -+ GR_STATUS = 5, -+ GR_UNSPROLE = 6, -+ GR_PASSSET = 7, -+ GR_SPROLEPAM = 8, -+ GR_RELOAD = 9, -+}; -+ -+/* Password setup definitions -+ * kernel/grhash.c */ -+enum { -+ GR_PW_LEN = 128, -+ GR_SALT_LEN = 16, -+ GR_SHA_LEN = 32, -+}; -+ -+enum { -+ GR_SPROLE_LEN = 64, -+}; -+ -+enum { -+ GR_NO_GLOB = 0, -+ GR_REG_GLOB, -+ GR_CREATE_GLOB -+}; -+ -+#define GR_NLIMITS 32 -+ -+/* Begin Data Structures */ -+ -+struct sprole_pw { -+ unsigned char *rolename; -+ unsigned char salt[GR_SALT_LEN]; -+ unsigned char sum[GR_SHA_LEN]; /* 256-bit SHA hash of the password */ -+}; -+ -+struct name_entry { -+ __u32 key; -+ u64 inode; -+ dev_t device; -+ char *name; -+ __u16 len; -+ __u8 deleted; -+ struct name_entry *prev; -+ struct name_entry *next; -+}; -+ -+struct inodev_entry { -+ struct name_entry *nentry; -+ struct inodev_entry *prev; -+ struct inodev_entry *next; -+}; -+ -+struct acl_role_db { -+ struct acl_role_label **r_hash; -+ __u32 r_size; -+}; -+ -+struct inodev_db { -+ struct inodev_entry **i_hash; -+ __u32 i_size; -+}; -+ -+struct name_db { -+ struct name_entry **n_hash; -+ __u32 n_size; -+}; -+ -+struct crash_uid { -+ uid_t uid; -+ unsigned long expires; -+}; -+ -+struct gr_hash_struct { -+ void **table; -+ void **nametable; -+ void *first; -+ __u32 table_size; -+ __u32 used_size; -+ int type; -+}; -+ -+/* Userspace Grsecurity ACL data structures */ -+ -+struct acl_subject_label { -+ char *filename; -+ u64 inode; -+ dev_t device; -+ __u32 mode; -+ kernel_cap_t cap_mask; -+ kernel_cap_t cap_lower; -+ kernel_cap_t cap_invert_audit; -+ -+ struct rlimit res[GR_NLIMITS]; -+ __u32 resmask; -+ -+ __u8 user_trans_type; -+ __u8 group_trans_type; -+ uid_t *user_transitions; -+ gid_t *group_transitions; -+ __u16 user_trans_num; -+ __u16 group_trans_num; -+ -+ __u32 sock_families[2]; -+ __u32 ip_proto[8]; -+ __u32 ip_type; -+ struct acl_ip_label **ips; -+ __u32 ip_num; -+ __u32 inaddr_any_override; -+ -+ __u32 crashes; -+ unsigned long expires; -+ -+ struct acl_subject_label *parent_subject; -+ struct gr_hash_struct *hash; -+ struct acl_subject_label *prev; -+ struct acl_subject_label *next; -+ -+ struct acl_object_label **obj_hash; -+ __u32 obj_hash_size; -+ __u16 pax_flags; -+}; -+ -+struct role_allowed_ip { -+ __u32 addr; -+ __u32 netmask; -+ -+ struct role_allowed_ip *prev; -+ struct role_allowed_ip *next; -+}; -+ -+struct role_transition { -+ char *rolename; -+ -+ struct role_transition *prev; -+ struct role_transition *next; -+}; -+ -+struct acl_role_label { -+ char *rolename; -+ uid_t uidgid; -+ __u16 roletype; -+ -+ __u16 auth_attempts; -+ unsigned long expires; -+ -+ struct acl_subject_label *root_label; -+ struct gr_hash_struct *hash; -+ -+ struct acl_role_label *prev; -+ struct acl_role_label *next; -+ -+ struct role_transition *transitions; -+ struct role_allowed_ip *allowed_ips; -+ uid_t *domain_children; -+ __u16 domain_child_num; -+ -+ umode_t umask; -+ -+ struct acl_subject_label **subj_hash; -+ __u32 subj_hash_size; -+}; -+ -+struct user_acl_role_db { -+ struct acl_role_label **r_table; -+ __u32 num_pointers; /* Number of allocations to track */ -+ __u32 num_roles; /* Number of roles */ -+ __u32 num_domain_children; /* Number of domain children */ -+ __u32 num_subjects; /* Number of subjects */ -+ __u32 num_objects; /* Number of objects */ -+}; -+ -+struct acl_object_label { -+ char *filename; -+ u64 inode; -+ dev_t device; -+ __u32 mode; -+ -+ struct acl_subject_label *nested; -+ struct acl_object_label *globbed; -+ -+ /* next two structures not used */ -+ -+ struct acl_object_label *prev; -+ struct acl_object_label *next; -+}; -+ -+struct acl_ip_label { -+ char *iface; -+ __u32 addr; -+ __u32 netmask; -+ __u16 low, high; -+ __u8 mode; -+ __u32 type; -+ __u32 proto[8]; -+ -+ /* next two structures not used */ -+ -+ struct acl_ip_label *prev; -+ struct acl_ip_label *next; -+}; -+ -+struct gr_arg { -+ struct user_acl_role_db role_db; -+ unsigned char pw[GR_PW_LEN]; -+ unsigned char salt[GR_SALT_LEN]; -+ unsigned char sum[GR_SHA_LEN]; -+ unsigned char sp_role[GR_SPROLE_LEN]; -+ struct sprole_pw *sprole_pws; -+ dev_t segv_device; -+ u64 segv_inode; -+ uid_t segv_uid; -+ __u16 num_sprole_pws; -+ __u16 mode; -+}; -+ -+struct gr_arg_wrapper { -+ struct gr_arg *arg; -+ __u32 version; -+ __u32 size; -+}; -+ -+struct subject_map { -+ struct acl_subject_label *user; -+ struct acl_subject_label *kernel; -+ struct subject_map *prev; -+ struct subject_map *next; -+}; -+ -+struct acl_subj_map_db { -+ struct subject_map **s_hash; -+ __u32 s_size; -+}; -+ -+struct gr_policy_state { -+ struct sprole_pw **acl_special_roles; -+ __u16 num_sprole_pws; -+ struct acl_role_label *kernel_role; -+ struct acl_role_label *role_list; -+ struct acl_role_label *default_role; -+ struct acl_role_db acl_role_set; -+ struct acl_subj_map_db subj_map_set; -+ struct name_db name_set; -+ struct inodev_db inodev_set; -+}; -+ -+struct gr_alloc_state { -+ unsigned long alloc_stack_next; -+ unsigned long alloc_stack_size; -+ void **alloc_stack; -+}; -+ -+struct gr_reload_state { -+ struct gr_policy_state oldpolicy; -+ struct gr_alloc_state oldalloc; -+ struct gr_policy_state newpolicy; -+ struct gr_alloc_state newalloc; -+ struct gr_policy_state *oldpolicy_ptr; -+ struct gr_alloc_state *oldalloc_ptr; -+ unsigned char oldmode; -+}; -+ -+/* End Data Structures Section */ -+ -+/* Hash functions generated by empirical testing by Brad Spengler -+ Makes good use of the low bits of the inode. Generally 0-1 times -+ in loop for successful match. 0-3 for unsuccessful match. -+ Shift/add algorithm with modulus of table size and an XOR*/ -+ -+static __inline__ unsigned int -+gr_rhash(const uid_t uid, const __u16 type, const unsigned int sz) -+{ -+ return ((((uid + type) << (16 + type)) ^ uid) % sz); -+} -+ -+ static __inline__ unsigned int -+gr_shash(const struct acl_subject_label *userp, const unsigned int sz) -+{ -+ return ((const unsigned long)userp % sz); -+} -+ -+static __inline__ unsigned int -+gr_fhash(const u64 ino, const dev_t dev, const unsigned int sz) -+{ -+ unsigned int rem; -+ div_u64_rem((ino + dev) ^ ((ino << 13) + (ino << 23) + (dev << 9)), sz, &rem); -+ return rem; -+} -+ -+static __inline__ unsigned int -+gr_nhash(const char *name, const __u16 len, const unsigned int sz) -+{ -+ return full_name_hash((const unsigned char *)name, len) % sz; -+} -+ -+#define FOR_EACH_SUBJECT_START(role,subj,iter) \ -+ subj = NULL; \ -+ iter = 0; \ -+ while (iter < role->subj_hash_size) { \ -+ if (subj == NULL) \ -+ subj = role->subj_hash[iter]; \ -+ if (subj == NULL) { \ -+ iter++; \ -+ continue; \ -+ } -+ -+#define FOR_EACH_SUBJECT_END(subj,iter) \ -+ subj = subj->next; \ -+ if (subj == NULL) \ -+ iter++; \ -+ } -+ -+ -+#define FOR_EACH_NESTED_SUBJECT_START(role,subj) \ -+ subj = role->hash->first; \ -+ while (subj != NULL) { -+ -+#define FOR_EACH_NESTED_SUBJECT_END(subj) \ -+ subj = subj->next; \ -+ } -+ -+#endif -+ -diff -Naur backports-4.2.6-1.org/include/linux/gralloc.h backports-4.2.6-1/include/linux/gralloc.h ---- backports-4.2.6-1.org/include/linux/gralloc.h 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/include/linux/gralloc.h 2016-01-27 12:26:26.289959354 +0100 -@@ -0,0 +1,9 @@ -+#ifndef __GRALLOC_H -+#define __GRALLOC_H -+ -+void acl_free_all(void); -+int acl_alloc_stack_init(unsigned long size); -+void *acl_alloc(unsigned long len); -+void *acl_alloc_num(unsigned long num, unsigned long len); -+ -+#endif -diff -Naur backports-4.2.6-1.org/include/linux/grdefs.h backports-4.2.6-1/include/linux/grdefs.h ---- backports-4.2.6-1.org/include/linux/grdefs.h 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/include/linux/grdefs.h 2016-01-27 12:26:26.289959354 +0100 -@@ -0,0 +1,140 @@ -+#ifndef GRDEFS_H -+#define GRDEFS_H -+ -+/* Begin grsecurity status declarations */ -+ -+enum { -+ GR_READY = 0x01, -+ GR_STATUS_INIT = 0x00 // disabled state -+}; -+ -+/* Begin ACL declarations */ -+ -+/* Role flags */ -+ -+enum { -+ GR_ROLE_USER = 0x0001, -+ GR_ROLE_GROUP = 0x0002, -+ GR_ROLE_DEFAULT = 0x0004, -+ GR_ROLE_SPECIAL = 0x0008, -+ GR_ROLE_AUTH = 0x0010, -+ GR_ROLE_NOPW = 0x0020, -+ GR_ROLE_GOD = 0x0040, -+ GR_ROLE_LEARN = 0x0080, -+ GR_ROLE_TPE = 0x0100, -+ GR_ROLE_DOMAIN = 0x0200, -+ GR_ROLE_PAM = 0x0400, -+ GR_ROLE_PERSIST = 0x0800 -+}; -+ -+/* ACL Subject and Object mode flags */ -+enum { -+ GR_DELETED = 0x80000000 -+}; -+ -+/* ACL Object-only mode flags */ -+enum { -+ GR_READ = 0x00000001, -+ GR_APPEND = 0x00000002, -+ GR_WRITE = 0x00000004, -+ GR_EXEC = 0x00000008, -+ GR_FIND = 0x00000010, -+ GR_INHERIT = 0x00000020, -+ GR_SETID = 0x00000040, -+ GR_CREATE = 0x00000080, -+ GR_DELETE = 0x00000100, -+ GR_LINK = 0x00000200, -+ GR_AUDIT_READ = 0x00000400, -+ GR_AUDIT_APPEND = 0x00000800, -+ GR_AUDIT_WRITE = 0x00001000, -+ GR_AUDIT_EXEC = 0x00002000, -+ GR_AUDIT_FIND = 0x00004000, -+ GR_AUDIT_INHERIT= 0x00008000, -+ GR_AUDIT_SETID = 0x00010000, -+ GR_AUDIT_CREATE = 0x00020000, -+ GR_AUDIT_DELETE = 0x00040000, -+ GR_AUDIT_LINK = 0x00080000, -+ GR_PTRACERD = 0x00100000, -+ GR_NOPTRACE = 0x00200000, -+ GR_SUPPRESS = 0x00400000, -+ GR_NOLEARN = 0x00800000, -+ GR_INIT_TRANSFER= 0x01000000 -+}; -+ -+#define GR_AUDITS (GR_AUDIT_READ | GR_AUDIT_WRITE | GR_AUDIT_APPEND | GR_AUDIT_EXEC | \ -+ GR_AUDIT_FIND | GR_AUDIT_INHERIT | GR_AUDIT_SETID | \ -+ GR_AUDIT_CREATE | GR_AUDIT_DELETE | GR_AUDIT_LINK) -+ -+/* ACL subject-only mode flags */ -+enum { -+ GR_KILL = 0x00000001, -+ GR_VIEW = 0x00000002, -+ GR_PROTECTED = 0x00000004, -+ GR_LEARN = 0x00000008, -+ GR_OVERRIDE = 0x00000010, -+ /* just a placeholder, this mode is only used in userspace */ -+ GR_DUMMY = 0x00000020, -+ GR_PROTSHM = 0x00000040, -+ GR_KILLPROC = 0x00000080, -+ GR_KILLIPPROC = 0x00000100, -+ /* just a placeholder, this mode is only used in userspace */ -+ GR_NOTROJAN = 0x00000200, -+ GR_PROTPROCFD = 0x00000400, -+ GR_PROCACCT = 0x00000800, -+ GR_RELAXPTRACE = 0x00001000, -+ //GR_NESTED = 0x00002000, -+ GR_INHERITLEARN = 0x00004000, -+ GR_PROCFIND = 0x00008000, -+ GR_POVERRIDE = 0x00010000, -+ GR_KERNELAUTH = 0x00020000, -+ GR_ATSECURE = 0x00040000, -+ GR_SHMEXEC = 0x00080000 -+}; -+ -+enum { -+ GR_PAX_ENABLE_SEGMEXEC = 0x0001, -+ GR_PAX_ENABLE_PAGEEXEC = 0x0002, -+ GR_PAX_ENABLE_MPROTECT = 0x0004, -+ GR_PAX_ENABLE_RANDMMAP = 0x0008, -+ GR_PAX_ENABLE_EMUTRAMP = 0x0010, -+ GR_PAX_DISABLE_SEGMEXEC = 0x0100, -+ GR_PAX_DISABLE_PAGEEXEC = 0x0200, -+ GR_PAX_DISABLE_MPROTECT = 0x0400, -+ GR_PAX_DISABLE_RANDMMAP = 0x0800, -+ GR_PAX_DISABLE_EMUTRAMP = 0x1000, -+}; -+ -+enum { -+ GR_ID_USER = 0x01, -+ GR_ID_GROUP = 0x02, -+}; -+ -+enum { -+ GR_ID_ALLOW = 0x01, -+ GR_ID_DENY = 0x02, -+}; -+ -+#define GR_CRASH_RES 31 -+#define GR_UIDTABLE_MAX 500 -+ -+/* begin resource learning section */ -+enum { -+ GR_RLIM_CPU_BUMP = 60, -+ GR_RLIM_FSIZE_BUMP = 50000, -+ GR_RLIM_DATA_BUMP = 10000, -+ GR_RLIM_STACK_BUMP = 1000, -+ GR_RLIM_CORE_BUMP = 10000, -+ GR_RLIM_RSS_BUMP = 500000, -+ GR_RLIM_NPROC_BUMP = 1, -+ GR_RLIM_NOFILE_BUMP = 5, -+ GR_RLIM_MEMLOCK_BUMP = 50000, -+ GR_RLIM_AS_BUMP = 500000, -+ GR_RLIM_LOCKS_BUMP = 2, -+ GR_RLIM_SIGPENDING_BUMP = 5, -+ GR_RLIM_MSGQUEUE_BUMP = 10000, -+ GR_RLIM_NICE_BUMP = 1, -+ GR_RLIM_RTPRIO_BUMP = 1, -+ GR_RLIM_RTTIME_BUMP = 1000000 -+}; -+ -+#endif -diff -Naur backports-4.2.6-1.org/include/linux/grinternal.h backports-4.2.6-1/include/linux/grinternal.h ---- backports-4.2.6-1.org/include/linux/grinternal.h 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/include/linux/grinternal.h 2016-01-27 12:26:26.289959354 +0100 -@@ -0,0 +1,230 @@ -+#ifndef __GRINTERNAL_H -+#define __GRINTERNAL_H -+ -+#ifdef CONFIG_GRKERNSEC -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+void gr_add_learn_entry(const char *fmt, ...) -+ __attribute__ ((format (printf, 1, 2))); -+__u32 gr_search_file(const struct dentry *dentry, const __u32 mode, -+ const struct vfsmount *mnt); -+__u32 gr_check_create(const struct dentry *new_dentry, -+ const struct dentry *parent, -+ const struct vfsmount *mnt, const __u32 mode); -+int gr_check_protected_task(const struct task_struct *task); -+__u32 to_gr_audit(const __u32 reqmode); -+int gr_set_acls(const int type); -+int gr_acl_is_enabled(void); -+char gr_roletype_to_char(void); -+ -+void gr_handle_alertkill(struct task_struct *task); -+char *gr_to_filename(const struct dentry *dentry, -+ const struct vfsmount *mnt); -+char *gr_to_filename1(const struct dentry *dentry, -+ const struct vfsmount *mnt); -+char *gr_to_filename2(const struct dentry *dentry, -+ const struct vfsmount *mnt); -+char *gr_to_filename3(const struct dentry *dentry, -+ const struct vfsmount *mnt); -+ -+extern int grsec_enable_ptrace_readexec; -+extern int grsec_enable_harden_ptrace; -+extern int grsec_enable_link; -+extern int grsec_enable_fifo; -+extern int grsec_enable_execve; -+extern int grsec_enable_shm; -+extern int grsec_enable_execlog; -+extern int grsec_enable_signal; -+extern int grsec_enable_audit_ptrace; -+extern int grsec_enable_forkfail; -+extern int grsec_enable_time; -+extern int grsec_enable_rofs; -+extern int grsec_deny_new_usb; -+extern int grsec_enable_chroot_shmat; -+extern int grsec_enable_chroot_mount; -+extern int grsec_enable_chroot_double; -+extern int grsec_enable_chroot_pivot; -+extern int grsec_enable_chroot_chdir; -+extern int grsec_enable_chroot_chmod; -+extern int grsec_enable_chroot_mknod; -+extern int grsec_enable_chroot_fchdir; -+extern int grsec_enable_chroot_nice; -+extern int grsec_enable_chroot_execlog; -+extern int grsec_enable_chroot_caps; -+extern int grsec_enable_chroot_rename; -+extern int grsec_enable_chroot_sysctl; -+extern int grsec_enable_chroot_unix; -+extern int grsec_enable_symlinkown; -+extern kgid_t grsec_symlinkown_gid; -+extern int grsec_enable_tpe; -+extern kgid_t grsec_tpe_gid; -+extern int grsec_enable_tpe_all; -+extern int grsec_enable_tpe_invert; -+extern int grsec_enable_socket_all; -+extern kgid_t grsec_socket_all_gid; -+extern int grsec_enable_socket_client; -+extern kgid_t grsec_socket_client_gid; -+extern int grsec_enable_socket_server; -+extern kgid_t grsec_socket_server_gid; -+extern kgid_t grsec_audit_gid; -+extern int grsec_enable_group; -+extern int grsec_enable_log_rwxmaps; -+extern int grsec_enable_mount; -+extern int grsec_enable_chdir; -+extern int grsec_resource_logging; -+extern int grsec_enable_blackhole; -+extern int grsec_lastack_retries; -+extern int grsec_enable_brute; -+extern int grsec_enable_harden_ipc; -+extern int grsec_lock; -+ -+extern spinlock_t grsec_alert_lock; -+extern unsigned long grsec_alert_wtime; -+extern unsigned long grsec_alert_fyet; -+ -+extern spinlock_t grsec_audit_lock; -+ -+extern rwlock_t grsec_exec_file_lock; -+ -+#define gr_task_fullpath(tsk) ((tsk)->exec_file ? \ -+ gr_to_filename2((tsk)->exec_file->f_path.dentry, \ -+ (tsk)->exec_file->f_path.mnt) : "/") -+ -+#define gr_parent_task_fullpath(tsk) ((tsk)->real_parent->exec_file ? \ -+ gr_to_filename3((tsk)->real_parent->exec_file->f_path.dentry, \ -+ (tsk)->real_parent->exec_file->f_path.mnt) : "/") -+ -+#define gr_task_fullpath0(tsk) ((tsk)->exec_file ? \ -+ gr_to_filename((tsk)->exec_file->f_path.dentry, \ -+ (tsk)->exec_file->f_path.mnt) : "/") -+ -+#define gr_parent_task_fullpath0(tsk) ((tsk)->real_parent->exec_file ? \ -+ gr_to_filename1((tsk)->real_parent->exec_file->f_path.dentry, \ -+ (tsk)->real_parent->exec_file->f_path.mnt) : "/") -+ -+#define proc_is_chrooted(tsk_a) ((tsk_a)->gr_is_chrooted) -+ -+#define have_same_root(tsk_a,tsk_b) ((tsk_a)->gr_chroot_dentry == (tsk_b)->gr_chroot_dentry) -+ -+static inline bool gr_is_same_file(const struct file *file1, const struct file *file2) -+{ -+ if (file1 && file2) { -+ const struct inode *inode1 = file1->f_path.dentry->d_inode; -+ const struct inode *inode2 = file2->f_path.dentry->d_inode; -+ if (inode1->i_ino == inode2->i_ino && inode1->i_sb->s_dev == inode2->i_sb->s_dev) -+ return true; -+ } -+ -+ return false; -+} -+ -+#define GR_CHROOT_CAPS {{ \ -+ CAP_TO_MASK(CAP_LINUX_IMMUTABLE) | CAP_TO_MASK(CAP_NET_ADMIN) | \ -+ CAP_TO_MASK(CAP_SYS_MODULE) | CAP_TO_MASK(CAP_SYS_RAWIO) | \ -+ CAP_TO_MASK(CAP_SYS_PACCT) | CAP_TO_MASK(CAP_SYS_ADMIN) | \ -+ CAP_TO_MASK(CAP_SYS_BOOT) | CAP_TO_MASK(CAP_SYS_TIME) | \ -+ CAP_TO_MASK(CAP_NET_RAW) | CAP_TO_MASK(CAP_SYS_TTY_CONFIG) | \ -+ CAP_TO_MASK(CAP_IPC_OWNER) | CAP_TO_MASK(CAP_SETFCAP), \ -+ CAP_TO_MASK(CAP_SYSLOG) | CAP_TO_MASK(CAP_MAC_ADMIN) }} -+ -+#define security_learn(normal_msg,args...) \ -+({ \ -+ read_lock(&grsec_exec_file_lock); \ -+ gr_add_learn_entry(normal_msg "\n", ## args); \ -+ read_unlock(&grsec_exec_file_lock); \ -+}) -+ -+enum { -+ GR_DO_AUDIT, -+ GR_DONT_AUDIT, -+ /* used for non-audit messages that we shouldn't kill the task on */ -+ GR_DONT_AUDIT_GOOD -+}; -+ -+enum { -+ GR_TTYSNIFF, -+ GR_RBAC, -+ GR_RBAC_STR, -+ GR_STR_RBAC, -+ GR_RBAC_MODE2, -+ GR_RBAC_MODE3, -+ GR_FILENAME, -+ GR_SYSCTL_HIDDEN, -+ GR_NOARGS, -+ GR_ONE_INT, -+ GR_ONE_INT_TWO_STR, -+ GR_ONE_STR, -+ GR_STR_INT, -+ GR_TWO_STR_INT, -+ GR_TWO_INT, -+ GR_TWO_U64, -+ GR_THREE_INT, -+ GR_FIVE_INT_TWO_STR, -+ GR_TWO_STR, -+ GR_THREE_STR, -+ GR_FOUR_STR, -+ GR_STR_FILENAME, -+ GR_FILENAME_STR, -+ GR_FILENAME_TWO_INT, -+ GR_FILENAME_TWO_INT_STR, -+ GR_TEXTREL, -+ GR_PTRACE, -+ GR_RESOURCE, -+ GR_CAP, -+ GR_SIG, -+ GR_SIG2, -+ GR_CRASH1, -+ GR_CRASH2, -+ GR_PSACCT, -+ GR_RWXMAP, -+ GR_RWXMAPVMA -+}; -+ -+#define gr_log_hidden_sysctl(audit, msg, str) gr_log_varargs(audit, msg, GR_SYSCTL_HIDDEN, str) -+#define gr_log_ttysniff(audit, msg, task) gr_log_varargs(audit, msg, GR_TTYSNIFF, task) -+#define gr_log_fs_rbac_generic(audit, msg, dentry, mnt) gr_log_varargs(audit, msg, GR_RBAC, dentry, mnt) -+#define gr_log_fs_rbac_str(audit, msg, dentry, mnt, str) gr_log_varargs(audit, msg, GR_RBAC_STR, dentry, mnt, str) -+#define gr_log_fs_str_rbac(audit, msg, str, dentry, mnt) gr_log_varargs(audit, msg, GR_STR_RBAC, str, dentry, mnt) -+#define gr_log_fs_rbac_mode2(audit, msg, dentry, mnt, str1, str2) gr_log_varargs(audit, msg, GR_RBAC_MODE2, dentry, mnt, str1, str2) -+#define gr_log_fs_rbac_mode3(audit, msg, dentry, mnt, str1, str2, str3) gr_log_varargs(audit, msg, GR_RBAC_MODE3, dentry, mnt, str1, str2, str3) -+#define gr_log_fs_generic(audit, msg, dentry, mnt) gr_log_varargs(audit, msg, GR_FILENAME, dentry, mnt) -+#define gr_log_noargs(audit, msg) gr_log_varargs(audit, msg, GR_NOARGS) -+#define gr_log_int(audit, msg, num) gr_log_varargs(audit, msg, GR_ONE_INT, num) -+#define gr_log_int_str2(audit, msg, num, str1, str2) gr_log_varargs(audit, msg, GR_ONE_INT_TWO_STR, num, str1, str2) -+#define gr_log_str(audit, msg, str) gr_log_varargs(audit, msg, GR_ONE_STR, str) -+#define gr_log_str_int(audit, msg, str, num) gr_log_varargs(audit, msg, GR_STR_INT, str, num) -+#define gr_log_int_int(audit, msg, num1, num2) gr_log_varargs(audit, msg, GR_TWO_INT, num1, num2) -+#define gr_log_two_u64(audit, msg, num1, num2) gr_log_varargs(audit, msg, GR_TWO_U64, num1, num2) -+#define gr_log_int3(audit, msg, num1, num2, num3) gr_log_varargs(audit, msg, GR_THREE_INT, num1, num2, num3) -+#define gr_log_int5_str2(audit, msg, num1, num2, str1, str2) gr_log_varargs(audit, msg, GR_FIVE_INT_TWO_STR, num1, num2, str1, str2) -+#define gr_log_str_str(audit, msg, str1, str2) gr_log_varargs(audit, msg, GR_TWO_STR, str1, str2) -+#define gr_log_str2_int(audit, msg, str1, str2, num) gr_log_varargs(audit, msg, GR_TWO_STR_INT, str1, str2, num) -+#define gr_log_str3(audit, msg, str1, str2, str3) gr_log_varargs(audit, msg, GR_THREE_STR, str1, str2, str3) -+#define gr_log_str4(audit, msg, str1, str2, str3, str4) gr_log_varargs(audit, msg, GR_FOUR_STR, str1, str2, str3, str4) -+#define gr_log_str_fs(audit, msg, str, dentry, mnt) gr_log_varargs(audit, msg, GR_STR_FILENAME, str, dentry, mnt) -+#define gr_log_fs_str(audit, msg, dentry, mnt, str) gr_log_varargs(audit, msg, GR_FILENAME_STR, dentry, mnt, str) -+#define gr_log_fs_int2(audit, msg, dentry, mnt, num1, num2) gr_log_varargs(audit, msg, GR_FILENAME_TWO_INT, dentry, mnt, num1, num2) -+#define gr_log_fs_int2_str(audit, msg, dentry, mnt, num1, num2, str) gr_log_varargs(audit, msg, GR_FILENAME_TWO_INT_STR, dentry, mnt, num1, num2, str) -+#define gr_log_textrel_ulong_ulong(audit, msg, str, file, ulong1, ulong2) gr_log_varargs(audit, msg, GR_TEXTREL, str, file, ulong1, ulong2) -+#define gr_log_ptrace(audit, msg, task) gr_log_varargs(audit, msg, GR_PTRACE, task) -+#define gr_log_res_ulong2_str(audit, msg, task, ulong1, str, ulong2) gr_log_varargs(audit, msg, GR_RESOURCE, task, ulong1, str, ulong2) -+#define gr_log_cap(audit, msg, task, str) gr_log_varargs(audit, msg, GR_CAP, task, str) -+#define gr_log_sig_addr(audit, msg, str, addr) gr_log_varargs(audit, msg, GR_SIG, str, addr) -+#define gr_log_sig_task(audit, msg, task, num) gr_log_varargs(audit, msg, GR_SIG2, task, num) -+#define gr_log_crash1(audit, msg, task, ulong) gr_log_varargs(audit, msg, GR_CRASH1, task, ulong) -+#define gr_log_crash2(audit, msg, task, ulong1) gr_log_varargs(audit, msg, GR_CRASH2, task, ulong1) -+#define gr_log_procacct(audit, msg, task, num1, num2, num3, num4, num5, num6, num7, num8, num9) gr_log_varargs(audit, msg, GR_PSACCT, task, num1, num2, num3, num4, num5, num6, num7, num8, num9) -+#define gr_log_rwxmap(audit, msg, str) gr_log_varargs(audit, msg, GR_RWXMAP, str) -+#define gr_log_rwxmap_vma(audit, msg, str) gr_log_varargs(audit, msg, GR_RWXMAPVMA, str) -+ -+void gr_log_varargs(int audit, const char *msg, int argtypes, ...); -+ -+#endif -+ -+#endif -diff -Naur backports-4.2.6-1.org/include/linux/grmsg.h backports-4.2.6-1/include/linux/grmsg.h ---- backports-4.2.6-1.org/include/linux/grmsg.h 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/include/linux/grmsg.h 2016-01-27 12:26:26.289959354 +0100 -@@ -0,0 +1,118 @@ -+#define DEFAULTSECMSG "%.256s[%.16s:%d] uid/euid:%u/%u gid/egid:%u/%u, parent %.256s[%.16s:%d] uid/euid:%u/%u gid/egid:%u/%u" -+#define GR_ACL_PROCACCT_MSG "%.256s[%.16s:%d] IP:%pI4 TTY:%.64s uid/euid:%u/%u gid/egid:%u/%u run time:[%ud %uh %um %us] cpu time:[%ud %uh %um %us] %s with exit code %ld, parent %.256s[%.16s:%d] IP:%pI4 TTY:%.64s uid/euid:%u/%u gid/egid:%u/%u" -+#define GR_PTRACE_ACL_MSG "denied ptrace of %.950s(%.16s:%d) by " -+#define GR_STOPMOD_MSG "denied modification of module state by " -+#define GR_ROFS_BLOCKWRITE_MSG "denied write to block device %.950s by " -+#define GR_ROFS_MOUNT_MSG "denied writable mount of %.950s by " -+#define GR_IOPERM_MSG "denied use of ioperm() by " -+#define GR_IOPL_MSG "denied use of iopl() by " -+#define GR_SHMAT_ACL_MSG "denied attach of shared memory of UID %u, PID %d, ID %u by " -+#define GR_UNIX_CHROOT_MSG "denied connect() to abstract AF_UNIX socket outside of chroot by " -+#define GR_SHMAT_CHROOT_MSG "denied attach of shared memory outside of chroot by " -+#define GR_MEM_READWRITE_MSG "denied access of range %Lx -> %Lx in /dev/mem by " -+#define GR_SYMLINK_MSG "not following symlink %.950s owned by %d.%d by " -+#define GR_LEARN_AUDIT_MSG "%s\t%u\t%u\t%u\t%.4095s\t%.4095s\t%lu\t%lu\t%.4095s\t%lu\t%pI4" -+#define GR_ID_LEARN_MSG "%s\t%u\t%u\t%u\t%.4095s\t%.4095s\t%c\t%d\t%d\t%d\t%pI4" -+#define GR_HIDDEN_ACL_MSG "%s access to hidden file %.950s by " -+#define GR_OPEN_ACL_MSG "%s open of %.950s for%s%s by " -+#define GR_CREATE_ACL_MSG "%s create of %.950s for%s%s by " -+#define GR_FIFO_MSG "denied writing FIFO %.950s of %d.%d by " -+#define GR_MKNOD_CHROOT_MSG "denied mknod of %.950s from chroot by " -+#define GR_MKNOD_ACL_MSG "%s mknod of %.950s by " -+#define GR_UNIXCONNECT_ACL_MSG "%s connect() to the unix domain socket %.950s by " -+#define GR_TTYSNIFF_ACL_MSG "terminal being sniffed by IP:%pI4 %.480s[%.16s:%d], parent %.480s[%.16s:%d] against " -+#define GR_MKDIR_ACL_MSG "%s mkdir of %.950s by " -+#define GR_RMDIR_ACL_MSG "%s rmdir of %.950s by " -+#define GR_UNLINK_ACL_MSG "%s unlink of %.950s by " -+#define GR_SYMLINK_ACL_MSG "%s symlink from %.480s to %.480s by " -+#define GR_HARDLINK_MSG "denied hardlink of %.930s (owned by %d.%d) to %.30s for " -+#define GR_LINK_ACL_MSG "%s link of %.480s to %.480s by " -+#define GR_INHERIT_ACL_MSG "successful inherit of %.480s's ACL for %.480s by " -+#define GR_RENAME_ACL_MSG "%s rename of %.480s to %.480s by " -+#define GR_UNSAFESHARE_EXEC_ACL_MSG "denied exec with cloned fs of %.950s by " -+#define GR_PTRACE_EXEC_ACL_MSG "denied ptrace of %.950s by " -+#define GR_EXEC_ACL_MSG "%s execution of %.950s by " -+#define GR_EXEC_TPE_MSG "denied untrusted exec (due to %.70s) of %.950s by " -+#define GR_SEGVSTART_ACL_MSG "possible exploit bruteforcing on " DEFAULTSECMSG " banning uid %u from login for %lu seconds" -+#define GR_SEGVNOSUID_ACL_MSG "possible exploit bruteforcing on " DEFAULTSECMSG " banning execution for %lu seconds" -+#define GR_MOUNT_CHROOT_MSG "denied mount of %.256s as %.930s from chroot by " -+#define GR_PIVOT_CHROOT_MSG "denied pivot_root from chroot by " -+#define GR_TRUNCATE_ACL_MSG "%s truncate of %.950s by " -+#define GR_ATIME_ACL_MSG "%s access time change of %.950s by " -+#define GR_ACCESS_ACL_MSG "%s access of %.950s for%s%s%s by " -+#define GR_CHROOT_CHROOT_MSG "denied double chroot to %.950s by " -+#define GR_CHROOT_RENAME_MSG "denied bad rename of %.950s out of a chroot by " -+#define GR_CHMOD_CHROOT_MSG "denied chmod +s of %.950s by " -+#define GR_CHMOD_ACL_MSG "%s chmod of %.950s by " -+#define GR_CHROOT_FCHDIR_MSG "denied fchdir outside of chroot to %.950s by " -+#define GR_CHROOT_FHANDLE_MSG "denied use of file handles inside chroot by " -+#define GR_CHOWN_ACL_MSG "%s chown of %.950s by " -+#define GR_SETXATTR_ACL_MSG "%s setting extended attribute of %.950s by " -+#define GR_REMOVEXATTR_ACL_MSG "%s removing extended attribute of %.950s by " -+#define GR_WRITLIB_ACL_MSG "denied load of writable library %.950s by " -+#define GR_INITF_ACL_MSG "init_variables() failed %s by " -+#define GR_DISABLED_ACL_MSG "Error loading %s, trying to run kernel with acls disabled. To disable acls at startup use gracl=off from your boot loader" -+#define GR_DEV_ACL_MSG "/dev/grsec: %d bytes sent %d required, being fed garbage by " -+#define GR_SHUTS_ACL_MSG "shutdown auth success for " -+#define GR_SHUTF_ACL_MSG "shutdown auth failure for " -+#define GR_SHUTI_ACL_MSG "ignoring shutdown for disabled RBAC system for " -+#define GR_SEGVMODS_ACL_MSG "segvmod auth success for " -+#define GR_SEGVMODF_ACL_MSG "segvmod auth failure for " -+#define GR_SEGVMODI_ACL_MSG "ignoring segvmod for disabled RBAC system for " -+#define GR_ENABLE_ACL_MSG "%s RBAC system loaded by " -+#define GR_ENABLEF_ACL_MSG "unable to load %s for " -+#define GR_RELOADI_ACL_MSG "ignoring reload request for disabled RBAC system" -+#define GR_RELOAD_ACL_MSG "%s RBAC system reloaded by " -+#define GR_RELOADF_ACL_MSG "failed reload of %s for " -+#define GR_SPROLEI_ACL_MSG "ignoring change to special role for disabled RBAC system for " -+#define GR_SPROLES_ACL_MSG "successful change to special role %s (id %d) by " -+#define GR_SPROLEL_ACL_MSG "special role %s (id %d) exited by " -+#define GR_SPROLEF_ACL_MSG "special role %s failure for " -+#define GR_UNSPROLEI_ACL_MSG "ignoring unauth of special role for disabled RBAC system for " -+#define GR_UNSPROLES_ACL_MSG "successful unauth of special role %s (id %d) by " -+#define GR_INVMODE_ACL_MSG "invalid mode %d by " -+#define GR_PRIORITY_CHROOT_MSG "denied priority change of process (%.16s:%d) by " -+#define GR_FAILFORK_MSG "failed fork with errno %s by " -+#define GR_NICE_CHROOT_MSG "denied priority change by " -+#define GR_UNISIGLOG_MSG "%.32s occurred at %p in " -+#define GR_DUALSIGLOG_MSG "signal %d sent to " DEFAULTSECMSG " by " -+#define GR_SIG_ACL_MSG "denied send of signal %d to protected task " DEFAULTSECMSG " by " -+#define GR_SYSCTL_MSG "denied modification of grsecurity sysctl value : %.32s by " -+#define GR_SYSCTL_ACL_MSG "%s sysctl of %.950s for%s%s by " -+#define GR_TIME_MSG "time set by " -+#define GR_DEFACL_MSG "fatal: unable to find subject for (%.16s:%d), loaded by " -+#define GR_MMAP_ACL_MSG "%s executable mmap of %.950s by " -+#define GR_MPROTECT_ACL_MSG "%s executable mprotect of %.950s by " -+#define GR_SOCK_MSG "denied socket(%.16s,%.16s,%.16s) by " -+#define GR_SOCK_NOINET_MSG "denied socket(%.16s,%.16s,%d) by " -+#define GR_BIND_MSG "denied bind() by " -+#define GR_CONNECT_MSG "denied connect() by " -+#define GR_BIND_ACL_MSG "denied bind() to %pI4 port %u sock type %.16s protocol %.16s by " -+#define GR_CONNECT_ACL_MSG "denied connect() to %pI4 port %u sock type %.16s protocol %.16s by " -+#define GR_IP_LEARN_MSG "%s\t%u\t%u\t%u\t%.4095s\t%.4095s\t%pI4\t%u\t%u\t%u\t%u\t%pI4" -+#define GR_EXEC_CHROOT_MSG "exec of %.980s within chroot by process " -+#define GR_CAP_ACL_MSG "use of %s denied for " -+#define GR_CAP_CHROOT_MSG "use of %s in chroot denied for " -+#define GR_CAP_ACL_MSG2 "use of %s permitted for " -+#define GR_USRCHANGE_ACL_MSG "change to uid %u denied for " -+#define GR_GRPCHANGE_ACL_MSG "change to gid %u denied for " -+#define GR_REMOUNT_AUDIT_MSG "remount of %.256s by " -+#define GR_UNMOUNT_AUDIT_MSG "unmount of %.256s by " -+#define GR_MOUNT_AUDIT_MSG "mount of %.256s to %.256s by " -+#define GR_CHDIR_AUDIT_MSG "chdir to %.980s by " -+#define GR_EXEC_AUDIT_MSG "exec of %.930s (%.128s) by " -+#define GR_RESOURCE_MSG "denied resource overstep by requesting %lu for %.16s against limit %lu for " -+#define GR_RWXMMAP_MSG "denied RWX mmap of %.950s by " -+#define GR_RWXMPROTECT_MSG "denied RWX mprotect of %.950s by " -+#define GR_TEXTREL_AUDIT_MSG "allowed %s text relocation transition in %.950s, VMA:0x%08lx 0x%08lx by " -+#define GR_PTGNUSTACK_MSG "denied marking stack executable as requested by PT_GNU_STACK marking in %.950s by " -+#define GR_VM86_MSG "denied use of vm86 by " -+#define GR_PTRACE_AUDIT_MSG "process %.950s(%.16s:%d) attached to via ptrace by " -+#define GR_PTRACE_READEXEC_MSG "denied ptrace of unreadable binary %.950s by " -+#define GR_INIT_TRANSFER_MSG "persistent special role transferred privilege to init by " -+#define GR_BADPROCPID_MSG "denied read of sensitive /proc/pid/%s entry via fd passed across exec by " -+#define GR_SYMLINKOWNER_MSG "denied following symlink %.950s since symlink owner %u does not match target owner %u, by " -+#define GR_BRUTE_DAEMON_MSG "bruteforce prevention initiated for the next 30 minutes or until service restarted, stalling each fork 30 seconds. Please investigate the crash report for " -+#define GR_BRUTE_SUID_MSG "bruteforce prevention initiated due to crash of %.950s against uid %u, banning suid/sgid execs for %u minutes. Please investigate the crash report for " -+#define GR_IPC_DENIED_MSG "denied %s of overly-permissive IPC object with creator uid %u by " -+#define GR_MSRWRITE_MSG "denied write to CPU MSR by " -diff -Naur backports-4.2.6-1.org/include/linux/grsecurity.h backports-4.2.6-1/include/linux/grsecurity.h ---- backports-4.2.6-1.org/include/linux/grsecurity.h 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/include/linux/grsecurity.h 2016-01-27 12:26:26.289959354 +0100 -@@ -0,0 +1,255 @@ -+#ifndef GR_SECURITY_H -+#define GR_SECURITY_H -+#include -+#include -+#include -+#include -+ -+/* notify of brain-dead configs */ -+#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_GRKERNSEC_KMEM) -+#error "CONFIG_DEBUG_FS being enabled is a security risk when CONFIG_GRKERNSEC_KMEM is enabled" -+#endif -+#if defined(CONFIG_PROC_PAGE_MONITOR) && defined(CONFIG_GRKERNSEC) -+#error "CONFIG_PROC_PAGE_MONITOR is a security risk" -+#endif -+#if defined(CONFIG_GRKERNSEC_PROC_USER) && defined(CONFIG_GRKERNSEC_PROC_USERGROUP) -+#error "CONFIG_GRKERNSEC_PROC_USER and CONFIG_GRKERNSEC_PROC_USERGROUP cannot both be enabled." -+#endif -+#if defined(CONFIG_GRKERNSEC_PROC) && !defined(CONFIG_GRKERNSEC_PROC_USER) && !defined(CONFIG_GRKERNSEC_PROC_USERGROUP) -+#error "CONFIG_GRKERNSEC_PROC enabled, but neither CONFIG_GRKERNSEC_PROC_USER nor CONFIG_GRKERNSEC_PROC_USERGROUP enabled" -+#endif -+#if defined(CONFIG_PAX_NOEXEC) && !defined(CONFIG_PAX_PAGEEXEC) && !defined(CONFIG_PAX_SEGMEXEC) && !defined(CONFIG_PAX_KERNEXEC) -+#error "CONFIG_PAX_NOEXEC enabled, but PAGEEXEC, SEGMEXEC, and KERNEXEC are disabled." -+#endif -+#if defined(CONFIG_PAX_ASLR) && !defined(CONFIG_PAX_RANDKSTACK) && !defined(CONFIG_PAX_RANDUSTACK) && !defined(CONFIG_PAX_RANDMMAP) -+#error "CONFIG_PAX_ASLR enabled, but RANDKSTACK, RANDUSTACK, and RANDMMAP are disabled." -+#endif -+#if defined(CONFIG_PAX) && !defined(CONFIG_PAX_NOEXEC) && !defined(CONFIG_PAX_ASLR) -+#error "CONFIG_PAX enabled, but no PaX options are enabled." -+#endif -+ -+int gr_handle_new_usb(void); -+ -+void gr_handle_brute_attach(int dumpable); -+void gr_handle_brute_check(void); -+void gr_handle_kernel_exploit(void); -+ -+char gr_roletype_to_char(void); -+ -+int gr_proc_is_restricted(void); -+ -+int gr_acl_enable_at_secure(void); -+ -+int gr_check_user_change(kuid_t real, kuid_t effective, kuid_t fs); -+int gr_check_group_change(kgid_t real, kgid_t effective, kgid_t fs); -+ -+int gr_learn_cap(const struct task_struct *task, const struct cred *cred, const int cap); -+ -+void gr_del_task_from_ip_table(struct task_struct *p); -+ -+int gr_pid_is_chrooted(struct task_struct *p); -+int gr_handle_chroot_fowner(struct pid *pid, enum pid_type type); -+int gr_handle_chroot_nice(void); -+int gr_handle_chroot_sysctl(const int op); -+int gr_handle_chroot_setpriority(struct task_struct *p, -+ const int niceval); -+int gr_chroot_fchdir(struct dentry *u_dentry, struct vfsmount *u_mnt); -+int gr_chroot_fhandle(void); -+int gr_handle_chroot_chroot(const struct dentry *dentry, -+ const struct vfsmount *mnt); -+void gr_handle_chroot_chdir(const struct path *path); -+int gr_handle_chroot_chmod(const struct dentry *dentry, -+ const struct vfsmount *mnt, const int mode); -+int gr_handle_chroot_mknod(const struct dentry *dentry, -+ const struct vfsmount *mnt, const int mode); -+int gr_handle_chroot_mount(const struct dentry *dentry, -+ const struct vfsmount *mnt, -+ const char *dev_name); -+int gr_handle_chroot_pivot(void); -+int gr_handle_chroot_unix(const pid_t pid); -+ -+int gr_handle_rawio(const struct inode *inode); -+ -+void gr_handle_ioperm(void); -+void gr_handle_iopl(void); -+void gr_handle_msr_write(void); -+ -+umode_t gr_acl_umask(void); -+ -+int gr_tpe_allow(const struct file *file); -+ -+void gr_set_chroot_entries(struct task_struct *task, const struct path *path); -+void gr_clear_chroot_entries(struct task_struct *task); -+ -+void gr_log_forkfail(const int retval); -+void gr_log_timechange(void); -+void gr_log_signal(const int sig, const void *addr, const struct task_struct *t); -+void gr_log_chdir(const struct dentry *dentry, -+ const struct vfsmount *mnt); -+void gr_log_chroot_exec(const struct dentry *dentry, -+ const struct vfsmount *mnt); -+void gr_log_remount(const char *devname, const int retval); -+void gr_log_unmount(const char *devname, const int retval); -+void gr_log_mount(const char *from, struct path *to, const int retval); -+void gr_log_textrel(struct vm_area_struct *vma, bool is_textrel_rw); -+void gr_log_ptgnustack(struct file *file); -+void gr_log_rwxmmap(struct file *file); -+void gr_log_rwxmprotect(struct vm_area_struct *vma); -+ -+int gr_handle_follow_link(const struct dentry *dentry, -+ const struct vfsmount *mnt); -+int gr_handle_fifo(const struct dentry *dentry, -+ const struct vfsmount *mnt, -+ const struct dentry *dir, const int flag, -+ const int acc_mode); -+int gr_handle_hardlink(const struct dentry *dentry, -+ const struct vfsmount *mnt, -+ const struct filename *to); -+ -+int gr_is_capable(const int cap); -+int gr_is_capable_nolog(const int cap); -+int gr_task_is_capable(const struct task_struct *task, const struct cred *cred, const int cap); -+int gr_task_is_capable_nolog(const struct task_struct *task, const int cap); -+ -+void gr_copy_label(struct task_struct *tsk); -+void gr_handle_crash(struct task_struct *task, const int sig); -+int gr_handle_signal(const struct task_struct *p, const int sig); -+int gr_check_crash_uid(const kuid_t uid); -+int gr_check_protected_task(const struct task_struct *task); -+int gr_check_protected_task_fowner(struct pid *pid, enum pid_type type); -+int gr_acl_handle_mmap(const struct file *file, -+ const unsigned long prot); -+int gr_acl_handle_mprotect(const struct file *file, -+ const unsigned long prot); -+int gr_check_hidden_task(const struct task_struct *tsk); -+__u32 gr_acl_handle_truncate(const struct dentry *dentry, -+ const struct vfsmount *mnt); -+__u32 gr_acl_handle_utime(const struct dentry *dentry, -+ const struct vfsmount *mnt); -+__u32 gr_acl_handle_access(const struct dentry *dentry, -+ const struct vfsmount *mnt, const int fmode); -+__u32 gr_acl_handle_chmod(const struct dentry *dentry, -+ const struct vfsmount *mnt, umode_t *mode); -+__u32 gr_acl_handle_chown(const struct dentry *dentry, -+ const struct vfsmount *mnt); -+__u32 gr_acl_handle_setxattr(const struct dentry *dentry, -+ const struct vfsmount *mnt); -+__u32 gr_acl_handle_removexattr(const struct dentry *dentry, -+ const struct vfsmount *mnt); -+int gr_handle_ptrace(struct task_struct *task, const long request); -+int gr_handle_proc_ptrace(struct task_struct *task); -+__u32 gr_acl_handle_execve(const struct dentry *dentry, -+ const struct vfsmount *mnt); -+int gr_check_crash_exec(const struct file *filp); -+int gr_acl_is_enabled(void); -+void gr_set_role_label(struct task_struct *task, const kuid_t uid, -+ const kgid_t gid); -+int gr_set_proc_label(const struct dentry *dentry, -+ const struct vfsmount *mnt, -+ const int unsafe_flags); -+__u32 gr_acl_handle_hidden_file(const struct dentry *dentry, -+ const struct vfsmount *mnt); -+__u32 gr_acl_handle_open(const struct dentry *dentry, -+ const struct vfsmount *mnt, int acc_mode); -+__u32 gr_acl_handle_creat(const struct dentry *dentry, -+ const struct dentry *p_dentry, -+ const struct vfsmount *p_mnt, -+ int open_flags, int acc_mode, const int imode); -+void gr_handle_create(const struct dentry *dentry, -+ const struct vfsmount *mnt); -+void gr_handle_proc_create(const struct dentry *dentry, -+ const struct inode *inode); -+__u32 gr_acl_handle_mknod(const struct dentry *new_dentry, -+ const struct dentry *parent_dentry, -+ const struct vfsmount *parent_mnt, -+ const int mode); -+__u32 gr_acl_handle_mkdir(const struct dentry *new_dentry, -+ const struct dentry *parent_dentry, -+ const struct vfsmount *parent_mnt); -+__u32 gr_acl_handle_rmdir(const struct dentry *dentry, -+ const struct vfsmount *mnt); -+void gr_handle_delete(const u64 ino, const dev_t dev); -+__u32 gr_acl_handle_unlink(const struct dentry *dentry, -+ const struct vfsmount *mnt); -+__u32 gr_acl_handle_symlink(const struct dentry *new_dentry, -+ const struct dentry *parent_dentry, -+ const struct vfsmount *parent_mnt, -+ const struct filename *from); -+__u32 gr_acl_handle_link(const struct dentry *new_dentry, -+ const struct dentry *parent_dentry, -+ const struct vfsmount *parent_mnt, -+ const struct dentry *old_dentry, -+ const struct vfsmount *old_mnt, const struct filename *to); -+int gr_handle_symlink_owner(const struct path *link, const struct inode *target); -+int gr_acl_handle_rename(struct dentry *new_dentry, -+ struct dentry *parent_dentry, -+ const struct vfsmount *parent_mnt, -+ struct dentry *old_dentry, -+ struct inode *old_parent_inode, -+ struct vfsmount *old_mnt, const struct filename *newname, unsigned int flags); -+void gr_handle_rename(struct inode *old_dir, struct inode *new_dir, -+ struct dentry *old_dentry, -+ struct dentry *new_dentry, -+ struct vfsmount *mnt, const __u8 replace, unsigned int flags); -+__u32 gr_check_link(const struct dentry *new_dentry, -+ const struct dentry *parent_dentry, -+ const struct vfsmount *parent_mnt, -+ const struct dentry *old_dentry, -+ const struct vfsmount *old_mnt); -+int gr_acl_handle_filldir(const struct file *file, const char *name, -+ const unsigned int namelen, const u64 ino); -+ -+__u32 gr_acl_handle_unix(const struct dentry *dentry, -+ const struct vfsmount *mnt); -+void gr_acl_handle_exit(void); -+void gr_acl_handle_psacct(struct task_struct *task, const long code); -+int gr_acl_handle_procpidmem(const struct task_struct *task); -+int gr_handle_rofs_mount(struct dentry *dentry, struct vfsmount *mnt, int mnt_flags); -+int gr_handle_rofs_blockwrite(struct dentry *dentry, struct vfsmount *mnt, int acc_mode); -+void gr_audit_ptrace(struct task_struct *task); -+dev_t gr_get_dev_from_dentry(struct dentry *dentry); -+u64 gr_get_ino_from_dentry(struct dentry *dentry); -+void gr_put_exec_file(struct task_struct *task); -+ -+int gr_get_symlinkown_enabled(void); -+ -+int gr_ptrace_readexec(struct file *file, int unsafe_flags); -+ -+void gr_inc_chroot_refcnts(struct dentry *dentry, struct vfsmount *mnt); -+void gr_dec_chroot_refcnts(struct dentry *dentry, struct vfsmount *mnt); -+int gr_bad_chroot_rename(struct dentry *olddentry, struct vfsmount *oldmnt, -+ struct dentry *newdentry, struct vfsmount *newmnt); -+ -+#ifdef CONFIG_GRKERNSEC_RESLOG -+extern void gr_log_resource(const struct task_struct *task, const int res, -+ const unsigned long wanted, const int gt); -+#else -+static inline void gr_log_resource(const struct task_struct *task, const int res, -+ const unsigned long wanted, const int gt) -+{ -+} -+#endif -+ -+#ifdef CONFIG_GRKERNSEC -+void task_grsec_rbac(struct seq_file *m, struct task_struct *p); -+void gr_handle_vm86(void); -+void gr_handle_mem_readwrite(u64 from, u64 to); -+ -+void gr_log_badprocpid(const char *entry); -+ -+extern int grsec_enable_dmesg; -+extern int grsec_disable_privio; -+ -+#ifdef CONFIG_GRKERNSEC_PROC_USERGROUP -+extern kgid_t grsec_proc_gid; -+#endif -+ -+#ifdef CONFIG_GRKERNSEC_CHROOT_FINDTASK -+extern int grsec_enable_chroot_findtask; -+#endif -+#ifdef CONFIG_GRKERNSEC_SETXID -+extern int grsec_enable_setxid; -+#endif -+#endif -+ -+#endif -diff -Naur backports-4.2.6-1.org/include/linux/grsock.h backports-4.2.6-1/include/linux/grsock.h ---- backports-4.2.6-1.org/include/linux/grsock.h 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/include/linux/grsock.h 2016-01-27 12:26:26.289959354 +0100 -@@ -0,0 +1,19 @@ -+#ifndef __GRSOCK_H -+#define __GRSOCK_H -+ -+extern void gr_attach_curr_ip(const struct sock *sk); -+extern int gr_handle_sock_all(const int family, const int type, -+ const int protocol); -+extern int gr_handle_sock_server(const struct sockaddr *sck); -+extern int gr_handle_sock_server_other(const struct sock *sck); -+extern int gr_handle_sock_client(const struct sockaddr *sck); -+extern int gr_search_connect(struct socket * sock, -+ struct sockaddr_in * addr); -+extern int gr_search_bind(struct socket * sock, -+ struct sockaddr_in * addr); -+extern int gr_search_listen(struct socket * sock); -+extern int gr_search_accept(struct socket * sock); -+extern int gr_search_socket(const int domain, const int type, -+ const int protocol); -+ -+#endif -diff -Naur backports-4.2.6-1.org/include/linux/netfilter/xt_gradm.h backports-4.2.6-1/include/linux/netfilter/xt_gradm.h ---- backports-4.2.6-1.org/include/linux/netfilter/xt_gradm.h 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/include/linux/netfilter/xt_gradm.h 2016-01-27 12:26:31.209959056 +0100 -@@ -0,0 +1,9 @@ -+#ifndef _LINUX_NETFILTER_XT_GRADM_H -+#define _LINUX_NETFILTER_XT_GRADM_H 1 -+ -+struct xt_gradm_mtinfo { -+ __u16 flags; -+ __u16 invflags; -+}; -+ -+#endif -diff -Naur backports-4.2.6-1.org/include/linux/unaligned/access_ok.h backports-4.2.6-1/include/linux/unaligned/access_ok.h ---- backports-4.2.6-1.org/include/linux/unaligned/access_ok.h 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/include/linux/unaligned/access_ok.h 2016-01-27 12:26:31.219959057 +0100 -@@ -4,34 +4,34 @@ - #include - #include - --static inline u16 get_unaligned_le16(const void *p) -+static inline u16 __intentional_overflow(-1) get_unaligned_le16(const void *p) - { -- return le16_to_cpup((__le16 *)p); -+ return le16_to_cpup((const __le16 *)p); - } - --static inline u32 get_unaligned_le32(const void *p) -+static inline u32 __intentional_overflow(-1) get_unaligned_le32(const void *p) - { -- return le32_to_cpup((__le32 *)p); -+ return le32_to_cpup((const __le32 *)p); - } - --static inline u64 get_unaligned_le64(const void *p) -+static inline u64 __intentional_overflow(-1) get_unaligned_le64(const void *p) - { -- return le64_to_cpup((__le64 *)p); -+ return le64_to_cpup((const __le64 *)p); - } - --static inline u16 get_unaligned_be16(const void *p) -+static inline u16 __intentional_overflow(-1) get_unaligned_be16(const void *p) - { -- return be16_to_cpup((__be16 *)p); -+ return be16_to_cpup((const __be16 *)p); - } - --static inline u32 get_unaligned_be32(const void *p) -+static inline u32 __intentional_overflow(-1) get_unaligned_be32(const void *p) - { -- return be32_to_cpup((__be32 *)p); -+ return be32_to_cpup((const __be32 *)p); - } - --static inline u64 get_unaligned_be64(const void *p) -+static inline u64 __intentional_overflow(-1) get_unaligned_be64(const void *p) - { -- return be64_to_cpup((__be64 *)p); -+ return be64_to_cpup((const __be64 *)p); - } - - static inline void put_unaligned_le16(u16 val, void *p) -diff -Naur backports-4.2.6-1.org/include/media/v4l2-dev.h backports-4.2.6-1/include/media/v4l2-dev.h ---- backports-4.2.6-1.org/include/media/v4l2-dev.h 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/include/media/v4l2-dev.h 2016-01-27 12:26:31.219959057 +0100 -@@ -74,7 +74,7 @@ - int (*mmap) (struct file *, struct vm_area_struct *); - int (*open) (struct file *); - int (*release) (struct file *); --}; -+} __do_const; - - /* - * Newer version of video_device, handled by videodev2.c -diff -Naur backports-4.2.6-1.org/include/media/v4l2-device.h backports-4.2.6-1/include/media/v4l2-device.h ---- backports-4.2.6-1.org/include/media/v4l2-device.h 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/include/media/v4l2-device.h 2016-01-27 12:26:31.219959057 +0100 -@@ -93,7 +93,7 @@ - this function returns 0. If the name ends with a digit (e.g. cx18), - then the name will be set to cx18-0 since cx180 looks really odd. */ - int v4l2_device_set_name(struct v4l2_device *v4l2_dev, const char *basename, -- atomic_t *instance); -+ atomic_unchecked_t *instance); - - /* Set v4l2_dev->dev to NULL. Call when the USB parent disconnects. - Since the parent disappears this ensures that v4l2_dev doesn't have an -diff -Naur backports-4.2.6-1.org/include/net/bluetooth/l2cap.h backports-4.2.6-1/include/net/bluetooth/l2cap.h ---- backports-4.2.6-1.org/include/net/bluetooth/l2cap.h 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/include/net/bluetooth/l2cap.h 2016-01-27 12:31:52.866600109 +0100 -@@ -615,7 +615,7 @@ - struct iovec *iov, - int len); - #endif --}; -+} __do_const; - - struct l2cap_conn { - struct hci_conn *hcon; -diff -Naur backports-4.2.6-1.org/include/net/mac80211.h backports-4.2.6-1/include/net/mac80211.h ---- backports-4.2.6-1.org/include/net/mac80211.h 2015-11-15 22:19:38.000000000 +0100 -+++ backports-4.2.6-1/include/net/mac80211.h 2016-01-27 12:26:31.223292389 +0100 -@@ -5106,7 +5106,7 @@ - struct sk_buff *skb; - struct ieee80211_tx_rate reported_rate; - bool rts, short_preamble; -- u8 max_rate_idx; -+ s8 max_rate_idx; - u32 rate_idx_mask; - u8 *rate_idx_mcs_mask; - bool bss; -@@ -5143,7 +5143,7 @@ - void (*remove_sta_debugfs)(void *priv, void *priv_sta); - - u32 (*get_expected_throughput)(void *priv_sta); --}; -+} __do_const; - - static inline int rate_supported(struct ieee80211_sta *sta, - enum ieee80211_band band, -diff -Naur backports-4.2.6-1.org/include/trace/events/fs.h backports-4.2.6-1/include/trace/events/fs.h ---- backports-4.2.6-1.org/include/trace/events/fs.h 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/include/trace/events/fs.h 2016-01-27 12:26:31.226625722 +0100 -@@ -0,0 +1,53 @@ -+#undef TRACE_SYSTEM -+#define TRACE_SYSTEM fs -+ -+#if !defined(_TRACE_FS_H) || defined(TRACE_HEADER_MULTI_READ) -+#define _TRACE_FS_H -+ -+#include -+#include -+ -+TRACE_EVENT(do_sys_open, -+ -+ TP_PROTO(const char *filename, int flags, int mode), -+ -+ TP_ARGS(filename, flags, mode), -+ -+ TP_STRUCT__entry( -+ __string( filename, filename ) -+ __field( int, flags ) -+ __field( int, mode ) -+ ), -+ -+ TP_fast_assign( -+ __assign_str(filename, filename); -+ __entry->flags = flags; -+ __entry->mode = mode; -+ ), -+ -+ TP_printk("\"%s\" %x %o", -+ __get_str(filename), __entry->flags, __entry->mode) -+); -+ -+TRACE_EVENT(open_exec, -+ -+ TP_PROTO(const char *filename), -+ -+ TP_ARGS(filename), -+ -+ TP_STRUCT__entry( -+ __string( filename, filename ) -+ ), -+ -+ TP_fast_assign( -+ __assign_str(filename, filename); -+ ), -+ -+ TP_printk("\"%s\"", -+ __get_str(filename)) -+); -+ -+#endif /* _TRACE_FS_H */ -+ -+/* This part must be outside protection */ -+#include -diff -Naur backports-4.2.6-1.org/net/bluetooth/hci_sock.c backports-4.2.6-1/net/bluetooth/hci_sock.c ---- backports-4.2.6-1.org/net/bluetooth/hci_sock.c 2015-11-15 22:19:40.000000000 +0100 -+++ backports-4.2.6-1/net/bluetooth/hci_sock.c 2016-01-27 12:26:36.269958751 +0100 -@@ -1266,7 +1266,7 @@ - uf.event_mask[1] = *((u32 *) f->event_mask + 1); - } - -- len = min_t(unsigned int, len, sizeof(uf)); -+ len = min((size_t)len, sizeof(uf)); - if (copy_from_user(&uf, optval, len)) { - err = -EFAULT; - break; -diff -Naur backports-4.2.6-1.org/net/bluetooth/l2cap_core.c backports-4.2.6-1/net/bluetooth/l2cap_core.c ---- backports-4.2.6-1.org/net/bluetooth/l2cap_core.c 2015-11-15 22:19:40.000000000 +0100 -+++ backports-4.2.6-1/net/bluetooth/l2cap_core.c 2016-01-27 12:26:36.269958751 +0100 -@@ -3547,8 +3547,10 @@ - break; - - case L2CAP_CONF_RFC: -- if (olen == sizeof(rfc)) -- memcpy(&rfc, (void *)val, olen); -+ if (olen != sizeof(rfc)) -+ break; -+ -+ memcpy(&rfc, (void *)val, olen); - - if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state) && - rfc.mode != chan->mode) -diff -Naur backports-4.2.6-1.org/net/bluetooth/l2cap_sock.c backports-4.2.6-1/net/bluetooth/l2cap_sock.c ---- backports-4.2.6-1.org/net/bluetooth/l2cap_sock.c 2015-11-15 22:19:40.000000000 +0100 -+++ backports-4.2.6-1/net/bluetooth/l2cap_sock.c 2016-01-27 12:26:36.269958751 +0100 -@@ -633,7 +633,8 @@ - struct sock *sk = sock->sk; - struct l2cap_chan *chan = l2cap_pi(sk)->chan; - struct l2cap_options opts; -- int len, err = 0; -+ int err = 0; -+ size_t len = optlen; - u32 opt; - - BT_DBG("sk %p", sk); -@@ -660,7 +661,7 @@ - opts.max_tx = chan->max_tx; - opts.txwin_size = chan->tx_win; - -- len = min_t(unsigned int, sizeof(opts), optlen); -+ len = min(sizeof(opts), len); - if (copy_from_user((char *) &opts, optval, len)) { - err = -EFAULT; - break; -@@ -747,7 +748,8 @@ - struct bt_security sec; - struct bt_power pwr; - struct l2cap_conn *conn; -- int len, err = 0; -+ int err = 0; -+ size_t len = optlen; - u32 opt; - - BT_DBG("sk %p", sk); -@@ -771,7 +773,7 @@ - - sec.level = BT_SECURITY_LOW; - -- len = min_t(unsigned int, sizeof(sec), optlen); -+ len = min(sizeof(sec), len); - if (copy_from_user((char *) &sec, optval, len)) { - err = -EFAULT; - break; -@@ -867,7 +869,7 @@ - - pwr.force_active = BT_POWER_FORCE_ACTIVE_ON; - -- len = min_t(unsigned int, sizeof(pwr), optlen); -+ len = min(sizeof(pwr), len); - if (copy_from_user((char *) &pwr, optval, len)) { - err = -EFAULT; - break; -diff -Naur backports-4.2.6-1.org/net/bluetooth/rfcomm/sock.c backports-4.2.6-1/net/bluetooth/rfcomm/sock.c ---- backports-4.2.6-1.org/net/bluetooth/rfcomm/sock.c 2015-11-15 22:19:40.000000000 +0100 -+++ backports-4.2.6-1/net/bluetooth/rfcomm/sock.c 2016-01-27 12:26:36.269958751 +0100 -@@ -713,7 +713,7 @@ - struct sock *sk = sock->sk; - struct bt_security sec; - int err = 0; -- size_t len; -+ size_t len = optlen; - u32 opt; - - BT_DBG("sk %p", sk); -@@ -735,7 +735,7 @@ - - sec.level = BT_SECURITY_LOW; - -- len = min_t(unsigned int, sizeof(sec), optlen); -+ len = min(sizeof(sec), len); - if (copy_from_user((char *) &sec, optval, len)) { - err = -EFAULT; - break; -diff -Naur backports-4.2.6-1.org/net/bluetooth/rfcomm/tty.c backports-4.2.6-1/net/bluetooth/rfcomm/tty.c ---- backports-4.2.6-1.org/net/bluetooth/rfcomm/tty.c 2015-11-15 22:19:40.000000000 +0100 -+++ backports-4.2.6-1/net/bluetooth/rfcomm/tty.c 2016-01-27 12:26:36.269958751 +0100 -@@ -752,7 +752,7 @@ - BT_DBG("tty %p id %d", tty, tty->index); - - BT_DBG("dev %p dst %pMR channel %d opened %d", dev, &dev->dst, -- dev->channel, dev->port.count); -+ dev->channel, atomic_read(&dev->port.count)); - - err = tty_port_open(&dev->port, tty, filp); - if (err) -@@ -775,7 +775,7 @@ - struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; - - BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc, -- dev->port.count); -+ atomic_read(&dev->port.count)); - - tty_port_close(&dev->port, tty, filp); - } -diff -Naur backports-4.2.6-1.org/net/ieee802154/6lowpan/core.c backports-4.2.6-1/net/ieee802154/6lowpan/core.c ---- backports-4.2.6-1.org/net/ieee802154/6lowpan/core.c 2015-11-15 22:19:40.000000000 +0100 -+++ backports-4.2.6-1/net/ieee802154/6lowpan/core.c 2016-01-27 12:26:36.273292083 +0100 -@@ -191,7 +191,7 @@ - dev_put(real_dev); - } - --static struct rtnl_link_ops lowpan_link_ops __read_mostly = { -+static struct rtnl_link_ops lowpan_link_ops = { - .kind = "lowpan", - .priv_size = sizeof(struct lowpan_dev_info), - .setup = lowpan_setup, -diff -Naur backports-4.2.6-1.org/net/ieee802154/6lowpan/reassembly.c backports-4.2.6-1/net/ieee802154/6lowpan/reassembly.c ---- backports-4.2.6-1.org/net/ieee802154/6lowpan/reassembly.c 2015-11-15 22:19:40.000000000 +0100 -+++ backports-4.2.6-1/net/ieee802154/6lowpan/reassembly.c 2016-01-27 12:26:36.273292083 +0100 -@@ -435,14 +435,13 @@ - - static int __net_init lowpan_frags_ns_sysctl_register(struct net *net) - { -- struct ctl_table *table; -+ ctl_table_no_const *table = NULL; - struct ctl_table_header *hdr; - struct netns_ieee802154_lowpan *ieee802154_lowpan = - net_ieee802154_lowpan(net); - -- table = lowpan_frags_ns_ctl_table; - if (!net_eq(net, &init_net)) { -- table = kmemdup(table, sizeof(lowpan_frags_ns_ctl_table), -+ table = kmemdup(lowpan_frags_ns_ctl_table, sizeof(lowpan_frags_ns_ctl_table), - GFP_KERNEL); - if (table == NULL) - goto err_alloc; -@@ -457,9 +456,9 @@ - /* Don't export sysctls to unprivileged users */ - if (net->user_ns != &init_user_ns) - table[0].procname = NULL; -- } -- -- hdr = register_net_sysctl(net, "net/ieee802154/6lowpan", table); -+ hdr = register_net_sysctl(net, "net/ieee802154/6lowpan", table); -+ } else -+ hdr = register_net_sysctl(net, "net/ieee802154/6lowpan", lowpan_frags_ns_ctl_table); - if (hdr == NULL) - goto err_reg; - -@@ -467,8 +466,7 @@ - return 0; - - err_reg: -- if (!net_eq(net, &init_net)) -- kfree(table); -+ kfree(table); - err_alloc: - return -ENOMEM; - } -diff -Naur backports-4.2.6-1.org/net/mac80211/cfg.c backports-4.2.6-1/net/mac80211/cfg.c ---- backports-4.2.6-1.org/net/mac80211/cfg.c 2015-11-15 22:19:40.000000000 +0100 -+++ backports-4.2.6-1/net/mac80211/cfg.c 2016-01-27 12:26:36.286625417 +0100 -@@ -580,7 +580,7 @@ - ret = ieee80211_vif_use_channel(sdata, chandef, - IEEE80211_CHANCTX_EXCLUSIVE); - } -- } else if (local->open_count == local->monitors) { -+ } else if (local_read(&local->open_count) == local->monitors) { - local->_oper_chandef = *chandef; - ieee80211_hw_config(local, 0); - } -@@ -3488,7 +3488,7 @@ - else - local->probe_req_reg--; - -- if (!local->open_count) -+ if (!local_read(&local->open_count)) - break; - - ieee80211_queue_work(&local->hw, &local->reconfig_filter); -@@ -3637,8 +3637,8 @@ - if (chanctx_conf) { - *chandef = sdata->vif.bss_conf.chandef; - ret = 0; -- } else if (local->open_count > 0 && -- local->open_count == local->monitors && -+ } else if (local_read(&local->open_count) > 0 && -+ local_read(&local->open_count) == local->monitors && - sdata->vif.type == NL80211_IFTYPE_MONITOR) { - if (local->use_chanctx) - *chandef = local->monitor_chandef; -diff -Naur backports-4.2.6-1.org/net/mac80211/ieee80211_i.h backports-4.2.6-1/net/mac80211/ieee80211_i.h ---- backports-4.2.6-1.org/net/mac80211/ieee80211_i.h 2015-11-15 22:19:40.000000000 +0100 -+++ backports-4.2.6-1/net/mac80211/ieee80211_i.h 2016-01-27 12:26:36.289958749 +0100 -@@ -30,6 +30,7 @@ - #include - #include - #include -+#include - #include "key.h" - #include "sta_info.h" - #include "debug.h" -@@ -1112,7 +1113,7 @@ - /* also used to protect ampdu_ac_queue and amdpu_ac_stop_refcnt */ - spinlock_t queue_stop_reason_lock; - -- int open_count; -+ local_t open_count; - int monitors, cooked_mntrs; - /* number of interfaces with corresponding FIF_ flags */ - int fif_fcsfail, fif_plcpfail, fif_control, fif_other_bss, fif_pspoll, -diff -Naur backports-4.2.6-1.org/net/mac80211/iface.c backports-4.2.6-1/net/mac80211/iface.c ---- backports-4.2.6-1.org/net/mac80211/iface.c 2015-11-15 22:19:40.000000000 +0100 -+++ backports-4.2.6-1/net/mac80211/iface.c 2016-01-27 12:26:36.289958749 +0100 -@@ -550,7 +550,7 @@ - break; - } - -- if (local->open_count == 0) { -+ if (local_read(&local->open_count) == 0) { - res = drv_start(local); - if (res) - goto err_del_bss; -@@ -597,7 +597,7 @@ - res = drv_add_interface(local, sdata); - if (res) - goto err_stop; -- } else if (local->monitors == 0 && local->open_count == 0) { -+ } else if (local->monitors == 0 && local_read(&local->open_count) == 0) { - res = ieee80211_add_virtual_monitor(local); - if (res) - goto err_stop; -@@ -704,7 +704,7 @@ - atomic_inc(&local->iff_allmultis); - - if (coming_up) -- local->open_count++; -+ local_inc(&local->open_count); - - if (hw_reconf_flags) - ieee80211_hw_config(local, hw_reconf_flags); -@@ -742,7 +742,7 @@ - err_del_interface: - drv_remove_interface(local, sdata); - err_stop: -- if (!local->open_count) -+ if (!local_read(&local->open_count)) - drv_stop(local); - err_del_bss: - sdata->bss = NULL; -@@ -909,7 +909,7 @@ - } - - if (going_down) -- local->open_count--; -+ local_dec(&local->open_count); - - switch (sdata->vif.type) { - case NL80211_IFTYPE_AP_VLAN: -@@ -978,7 +978,7 @@ - atomic_set(&sdata->txqs_len[txqi->txq.ac], 0); - } - -- if (local->open_count == 0) -+ if (local_read(&local->open_count) == 0) - ieee80211_clear_tx_pending(local); - - /* -@@ -1021,7 +1021,7 @@ - if (cancel_scan) - flush_delayed_work(&local->scan_work); - -- if (local->open_count == 0) { -+ if (local_read(&local->open_count) == 0) { - ieee80211_stop_device(local); - - /* no reconfiguring after stop! */ -@@ -1032,7 +1032,7 @@ - ieee80211_configure_filter(local); - ieee80211_hw_config(local, hw_reconf_flags); - -- if (local->monitors == local->open_count) -+ if (local->monitors == local_read(&local->open_count)) - ieee80211_add_virtual_monitor(local); - } - -@@ -1905,8 +1905,8 @@ - */ - cfg80211_shutdown_all_interfaces(local->hw.wiphy); - -- WARN(local->open_count, "%s: open count remains %d\n", -- wiphy_name(local->hw.wiphy), local->open_count); -+ WARN(local_read(&local->open_count), "%s: open count remains %ld\n", -+ wiphy_name(local->hw.wiphy), local_read(&local->open_count)); - - mutex_lock(&local->iflist_mtx); - list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) { -diff -Naur backports-4.2.6-1.org/net/mac80211/main.c backports-4.2.6-1/net/mac80211/main.c ---- backports-4.2.6-1.org/net/mac80211/main.c 2015-11-15 22:19:40.000000000 +0100 -+++ backports-4.2.6-1/net/mac80211/main.c 2016-01-27 12:26:36.289958749 +0100 -@@ -172,7 +172,7 @@ - changed &= ~(IEEE80211_CONF_CHANGE_CHANNEL | - IEEE80211_CONF_CHANGE_POWER); - -- if (changed && local->open_count) { -+ if (changed && local_read(&local->open_count)) { - ret = drv_config(local, changed); - /* - * Goal: -diff -Naur backports-4.2.6-1.org/net/mac80211/pm.c backports-4.2.6-1/net/mac80211/pm.c ---- backports-4.2.6-1.org/net/mac80211/pm.c 2015-11-15 22:19:40.000000000 +0100 -+++ backports-4.2.6-1/net/mac80211/pm.c 2016-01-27 12:26:36.289958749 +0100 -@@ -12,7 +12,7 @@ - struct ieee80211_sub_if_data *sdata; - struct sta_info *sta; - -- if (!local->open_count) -+ if (!local_read(&local->open_count)) - goto suspend; - - ieee80211_scan_cancel(local); -@@ -166,7 +166,7 @@ - WARN_ON(!list_empty(&local->chanctx_list)); - - /* stop hardware - this must stop RX */ -- if (local->open_count) -+ if (local_read(&local->open_count)) - ieee80211_stop_device(local); - - suspend: -diff -Naur backports-4.2.6-1.org/net/mac80211/rate.c backports-4.2.6-1/net/mac80211/rate.c ---- backports-4.2.6-1.org/net/mac80211/rate.c 2015-11-15 22:19:40.000000000 +0100 -+++ backports-4.2.6-1/net/mac80211/rate.c 2016-01-27 12:26:36.289958749 +0100 -@@ -730,7 +730,7 @@ - - ASSERT_RTNL(); - -- if (local->open_count) -+ if (local_read(&local->open_count)) - return -EBUSY; - - if (ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL)) { -diff -Naur backports-4.2.6-1.org/net/mac80211/sta_info.c backports-4.2.6-1/net/mac80211/sta_info.c ---- backports-4.2.6-1.org/net/mac80211/sta_info.c 2015-11-15 22:19:40.000000000 +0100 -+++ backports-4.2.6-1/net/mac80211/sta_info.c 2016-01-27 12:26:36.289958749 +0100 -@@ -341,7 +341,7 @@ - int size = sizeof(struct txq_info) + - ALIGN(hw->txq_data_size, sizeof(void *)); - -- txq_data = kcalloc(ARRAY_SIZE(sta->sta.txq), size, gfp); -+ txq_data = kcalloc(size, ARRAY_SIZE(sta->sta.txq), gfp); - if (!txq_data) - goto free; - -diff -Naur backports-4.2.6-1.org/net/mac80211/util.c backports-4.2.6-1/net/mac80211/util.c ---- backports-4.2.6-1.org/net/mac80211/util.c 2015-11-15 22:19:40.000000000 +0100 -+++ backports-4.2.6-1/net/mac80211/util.c 2016-01-27 12:26:36.289958749 +0100 -@@ -1761,7 +1761,7 @@ - bool sched_scan_stopped = false; - - /* nothing to do if HW shouldn't run */ -- if (!local->open_count) -+ if (!local_read(&local->open_count)) - goto wake_up; - - #ifdef CONFIG_PM -@@ -2033,7 +2033,7 @@ - local->in_reconfig = false; - barrier(); - -- if (local->monitors == local->open_count && local->monitors > 0) -+ if (local->monitors == local_read(&local->open_count) && local->monitors > 0) - ieee80211_add_virtual_monitor(local); - - /* -@@ -2088,7 +2088,7 @@ - * If this is for hw restart things are still running. - * We may want to change that later, however. - */ -- if (local->open_count && (!local->suspended || reconfig_due_to_wowlan)) -+ if (local_read(&local->open_count) && (!local->suspended || reconfig_due_to_wowlan)) - drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_RESTART); - - if (!local->suspended) -@@ -2112,7 +2112,7 @@ - flush_delayed_work(&local->scan_work); - } - -- if (local->open_count && !reconfig_due_to_wowlan) -+ if (local_read(&local->open_count) && !reconfig_due_to_wowlan) - drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_SUSPEND); - - list_for_each_entry(sdata, &local->interfaces, list) { -diff -Naur backports-4.2.6-1.org/net/wireless/wext-core.c backports-4.2.6-1/net/wireless/wext-core.c ---- backports-4.2.6-1.org/net/wireless/wext-core.c 2015-11-15 22:19:40.000000000 +0100 -+++ backports-4.2.6-1/net/wireless/wext-core.c 2016-01-27 12:26:36.303292082 +0100 -@@ -748,8 +748,7 @@ - */ - - /* Support for very large requests */ -- if ((descr->flags & IW_DESCR_FLAG_NOMAX) && -- (user_length > descr->max_tokens)) { -+ if (user_length > descr->max_tokens) { - /* Allow userspace to GET more than max so - * we can support any size GET requests. - * There is still a limit : -ENOMEM. -@@ -788,22 +787,6 @@ - } - } - -- if (IW_IS_GET(cmd) && !(descr->flags & IW_DESCR_FLAG_NOMAX)) { -- /* -- * If this is a GET, but not NOMAX, it means that the extra -- * data is not bounded by userspace, but by max_tokens. Thus -- * set the length to max_tokens. This matches the extra data -- * allocation. -- * The driver should fill it with the number of tokens it -- * provided, and it may check iwp->length rather than having -- * knowledge of max_tokens. If the driver doesn't change the -- * iwp->length, this ioctl just copies back max_token tokens -- * filled with zeroes. Hopefully the driver isn't claiming -- * them to be valid data. -- */ -- iwp->length = descr->max_tokens; -- } -- - err = handler(dev, info, (union iwreq_data *) iwp, extra); - - iwp->length += essid_compat; -diff -Naur backports-4.2.6-1.org/scripts/gcc-plugin.sh backports-4.2.6-1/scripts/gcc-plugin.sh ---- backports-4.2.6-1.org/scripts/gcc-plugin.sh 1970-01-01 01:00:00.000000000 +0100 -+++ backports-4.2.6-1/scripts/gcc-plugin.sh 2016-01-27 12:26:36.303292082 +0100 -@@ -0,0 +1,51 @@ -+#!/bin/sh -+srctree=$(dirname "$0") -+gccplugins_dir=$($3 -print-file-name=plugin) -+plugincc=$($1 -E -x c++ - -o /dev/null -I"${srctree}"/../tools/gcc -I"${gccplugins_dir}"/include 2>&1 <= 4008 || defined(ENABLE_BUILD_WITH_CXX) -+#warning $2 CXX -+#else -+#warning $1 CC -+#endif -+EOF -+) -+ -+if [ $? -ne 0 ] -+then -+ exit 1 -+fi -+ -+case "$plugincc" in -+ *"$1 CC"*) -+ echo "$1" -+ exit 0 -+ ;; -+ -+ *"$2 CXX"*) -+ # the c++ compiler needs another test, see below -+ ;; -+ -+ *) -+ exit 1 -+ ;; -+esac -+ -+# we need a c++ compiler that supports the designated initializer GNU extension -+plugincc=$($2 -c -x c++ -std=gnu++98 - -fsyntax-only -I"${srctree}"/../tools/gcc -I"${gccplugins_dir}"/include 2>&1 < -Date: Fri, 31 Jul 2015 15:04:46 +0200 -Subject: mt7601u: fix dma from stack address - -DMA to variables located on the stack is a bad idea. -For simplicity and to avoid frequent allocations create -a buffer inside the device structure. Protect this -buffer with vendor_req_mutex. Don't protect vendor -requests which don't use this buffer. - -Signed-off-by: Jakub Kicinski -Signed-off-by: Kalle Valo ---- - drivers/net/wireless/mediatek/mt7601u/mt7601u.h | 4 +- - drivers/net/wireless/mediatek/mt7601u/usb.c | 63 ++++++++++++------------- - drivers/net/wireless/mediatek/mt7601u/usb.h | 2 + - 3 files changed, 34 insertions(+), 35 deletions(-) - -diff --git a/drivers/net/wireless/mediatek/mt7601u/mt7601u.h b/drivers/net/wireless/mediatek/mt7601u/mt7601u.h -index 9102be6b..6bdfc11 100644 ---- a/drivers/net/wireless/mediatek/mt7601u/mt7601u.h -+++ b/drivers/net/wireless/mediatek/mt7601u/mt7601u.h -@@ -146,7 +146,7 @@ enum { - * @rx_lock: protects @rx_q. - * @con_mon_lock: protects @ap_bssid, @bcn_*, @avg_rssi. - * @mutex: ensures exclusive access from mac80211 callbacks. -- * @vendor_req_mutex: ensures atomicity of vendor requests. -+ * @vendor_req_mutex: protects @vend_buf, ensures atomicity of split writes. - * @reg_atomic_mutex: ensures atomicity of indirect register accesses - * (accesses to RF and BBP). - * @hw_atomic_mutex: ensures exclusive access to HW during critical -@@ -184,6 +184,8 @@ struct mt7601u_dev { - struct mt7601u_eeprom_params *ee; - - struct mutex vendor_req_mutex; -+ void *vend_buf; -+ - struct mutex reg_atomic_mutex; - struct mutex hw_atomic_mutex; - -diff --git a/drivers/net/wireless/mediatek/mt7601u/usb.c b/drivers/net/wireless/mediatek/mt7601u/usb.c -index 54dba40..416c604 100644 ---- a/drivers/net/wireless/mediatek/mt7601u/usb.c -+++ b/drivers/net/wireless/mediatek/mt7601u/usb.c -@@ -92,10 +92,9 @@ void mt7601u_complete_urb(struct urb *urb) - complete(cmpl); - } - --static int --__mt7601u_vendor_request(struct mt7601u_dev *dev, const u8 req, -- const u8 direction, const u16 val, const u16 offset, -- void *buf, const size_t buflen) -+int mt7601u_vendor_request(struct mt7601u_dev *dev, const u8 req, -+ const u8 direction, const u16 val, const u16 offset, -+ void *buf, const size_t buflen) - { - int i, ret; - struct usb_device *usb_dev = mt7601u_to_usb_dev(dev); -@@ -110,6 +109,8 @@ __mt7601u_vendor_request(struct mt7601u_dev *dev, const u8 req, - trace_mt_vend_req(dev, pipe, req, req_type, val, offset, - buf, buflen, ret); - -+ if (ret == -ENODEV) -+ set_bit(MT7601U_STATE_REMOVED, &dev->state); - if (ret >= 0 || ret == -ENODEV) - return ret; - -@@ -122,25 +123,6 @@ __mt7601u_vendor_request(struct mt7601u_dev *dev, const u8 req, - return ret; - } - --int --mt7601u_vendor_request(struct mt7601u_dev *dev, const u8 req, -- const u8 direction, const u16 val, const u16 offset, -- void *buf, const size_t buflen) --{ -- int ret; -- -- mutex_lock(&dev->vendor_req_mutex); -- -- ret = __mt7601u_vendor_request(dev, req, direction, val, offset, -- buf, buflen); -- if (ret == -ENODEV) -- set_bit(MT7601U_STATE_REMOVED, &dev->state); -- -- mutex_unlock(&dev->vendor_req_mutex); -- -- return ret; --} -- - void mt7601u_vendor_reset(struct mt7601u_dev *dev) - { - mt7601u_vendor_request(dev, MT_VEND_DEV_MODE, USB_DIR_OUT, -@@ -150,19 +132,21 @@ void mt7601u_vendor_reset(struct mt7601u_dev *dev) - u32 mt7601u_rr(struct mt7601u_dev *dev, u32 offset) - { - int ret; -- __le32 reg; -- u32 val; -+ u32 val = ~0; - - WARN_ONCE(offset > USHRT_MAX, "read high off:%08x", offset); - -+ mutex_lock(&dev->vendor_req_mutex); -+ - ret = mt7601u_vendor_request(dev, MT_VEND_MULTI_READ, USB_DIR_IN, -- 0, offset, ®, sizeof(reg)); -- val = le32_to_cpu(reg); -- if (ret > 0 && ret != sizeof(reg)) { -+ 0, offset, dev->vend_buf, MT_VEND_BUF); -+ if (ret == MT_VEND_BUF) -+ val = get_unaligned_le32(dev->vend_buf); -+ else if (ret > 0) - dev_err(dev->dev, "Error: wrong size read:%d off:%08x\n", - ret, offset); -- val = ~0; -- } -+ -+ mutex_unlock(&dev->vendor_req_mutex); - - trace_reg_read(dev, offset, val); - return val; -@@ -173,12 +157,17 @@ int mt7601u_vendor_single_wr(struct mt7601u_dev *dev, const u8 req, - { - int ret; - -+ mutex_lock(&dev->vendor_req_mutex); -+ - ret = mt7601u_vendor_request(dev, req, USB_DIR_OUT, - val & 0xffff, offset, NULL, 0); -- if (ret) -- return ret; -- return mt7601u_vendor_request(dev, req, USB_DIR_OUT, -- val >> 16, offset + 2, NULL, 0); -+ if (!ret) -+ ret = mt7601u_vendor_request(dev, req, USB_DIR_OUT, -+ val >> 16, offset + 2, NULL, 0); -+ -+ mutex_unlock(&dev->vendor_req_mutex); -+ -+ return ret; - } - - void mt7601u_wr(struct mt7601u_dev *dev, u32 offset, u32 val) -@@ -275,6 +264,12 @@ static int mt7601u_probe(struct usb_interface *usb_intf, - - usb_set_intfdata(usb_intf, dev); - -+ dev->vend_buf = devm_kmalloc(dev->dev, MT_VEND_BUF, GFP_KERNEL); -+ if (!dev->vend_buf) { -+ ret = -ENOMEM; -+ goto err; -+ } -+ - ret = mt7601u_assign_pipes(usb_intf, dev); - if (ret) - goto err; -diff --git a/drivers/net/wireless/mediatek/mt7601u/usb.h b/drivers/net/wireless/mediatek/mt7601u/usb.h -index 49e188f..bc18202 100644 ---- a/drivers/net/wireless/mediatek/mt7601u/usb.h -+++ b/drivers/net/wireless/mediatek/mt7601u/usb.h -@@ -23,6 +23,8 @@ - - #define MT_VEND_DEV_MODE_RESET 1 - -+#define MT_VEND_BUF sizeof(__le32) -+ - enum mt_vendor_req { - MT_VEND_DEV_MODE = 1, - MT_VEND_WRITE = 2, --- -cgit v0.12 - -From d9517c0a5d7468a7ea63086057604fcb0fff480e Mon Sep 17 00:00:00 2001 -From: Jakub Kicinski -Date: Fri, 31 Jul 2015 15:04:47 +0200 -Subject: mt7601u: use correct ieee80211_rx variant - -Rx is run inside a tasklet so ieee80211_rx() should be used -instead of ieee80211_rx_ni(). - -Signed-off-by: Jakub Kicinski -Signed-off-by: Kalle Valo ---- - drivers/net/wireless/mediatek/mt7601u/dma.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/net/wireless/mediatek/mt7601u/dma.c b/drivers/net/wireless/mediatek/mt7601u/dma.c -index 7217da4..fb183e3 100644 ---- a/drivers/net/wireless/mediatek/mt7601u/dma.c -+++ b/drivers/net/wireless/mediatek/mt7601u/dma.c -@@ -112,7 +112,7 @@ static void mt7601u_rx_process_seg(struct mt7601u_dev *dev, u8 *data, - if (!skb) - return; - -- ieee80211_rx_ni(dev->hw, skb); -+ ieee80211_rx(dev->hw, skb); - } - - static u16 mt7601u_rx_next_seg_len(u8 *data, u32 data_len) --- -cgit v0.12 - -From 4513493d188d5e3052aee68eda85eaaa1a4e41c2 Mon Sep 17 00:00:00 2001 -From: Jakub Kicinski -Date: Fri, 31 Jul 2015 15:04:48 +0200 -Subject: mt7601u: fix tx status reporting contexts - -mac80211 requires that rx path does not run concurrently with -tx status reporting. Since rx path is run in driver tasklet, -tx status cannot be reported directly from interrupt context -(there would be no way to lock it out). - -Add tasklet for tx and move all possible code from irq handler -there. - -Note: tx tasklet is needed because workqueue is queued very - rarely and that kills TCP performance. - -Signed-off-by: Jakub Kicinski -Signed-off-by: Kalle Valo ---- - drivers/net/wireless/mediatek/mt7601u/dma.c | 30 +++++++++++++++++++++---- - drivers/net/wireless/mediatek/mt7601u/init.c | 1 + - drivers/net/wireless/mediatek/mt7601u/mac.c | 4 ++++ - drivers/net/wireless/mediatek/mt7601u/mt7601u.h | 2 ++ - 4 files changed, 33 insertions(+), 4 deletions(-) - -diff --git a/drivers/net/wireless/mediatek/mt7601u/dma.c b/drivers/net/wireless/mediatek/mt7601u/dma.c -index fb183e3..63c4854 100644 ---- a/drivers/net/wireless/mediatek/mt7601u/dma.c -+++ b/drivers/net/wireless/mediatek/mt7601u/dma.c -@@ -236,23 +236,42 @@ static void mt7601u_complete_tx(struct urb *urb) - skb = q->e[q->start].skb; - trace_mt_tx_dma_done(dev, skb); - -- mt7601u_tx_status(dev, skb); -+ __skb_queue_tail(&dev->tx_skb_done, skb); -+ tasklet_schedule(&dev->tx_tasklet); - - if (q->used == q->entries - q->entries / 8) - ieee80211_wake_queue(dev->hw, skb_get_queue_mapping(skb)); - - q->start = (q->start + 1) % q->entries; - q->used--; -+out: -+ spin_unlock_irqrestore(&dev->tx_lock, flags); -+} - -- if (urb->status) -- goto out; -+static void mt7601u_tx_tasklet(unsigned long data) -+{ -+ struct mt7601u_dev *dev = (struct mt7601u_dev *) data; -+ struct sk_buff_head skbs; -+ unsigned long flags; -+ -+ __skb_queue_head_init(&skbs); -+ -+ spin_lock_irqsave(&dev->tx_lock, flags); - - set_bit(MT7601U_STATE_MORE_STATS, &dev->state); - if (!test_and_set_bit(MT7601U_STATE_READING_STATS, &dev->state)) - queue_delayed_work(dev->stat_wq, &dev->stat_work, - msecs_to_jiffies(10)); --out: -+ -+ skb_queue_splice_init(&dev->tx_skb_done, &skbs); -+ - spin_unlock_irqrestore(&dev->tx_lock, flags); -+ -+ while (!skb_queue_empty(&skbs)) { -+ struct sk_buff *skb = __skb_dequeue(&skbs); -+ -+ mt7601u_tx_status(dev, skb); -+ } - } - - static int mt7601u_dma_submit_tx(struct mt7601u_dev *dev, -@@ -475,6 +494,7 @@ int mt7601u_dma_init(struct mt7601u_dev *dev) - { - int ret = -ENOMEM; - -+ tasklet_init(&dev->tx_tasklet, mt7601u_tx_tasklet, (unsigned long) dev); - tasklet_init(&dev->rx_tasklet, mt7601u_rx_tasklet, (unsigned long) dev); - - ret = mt7601u_alloc_tx(dev); -@@ -502,4 +522,6 @@ void mt7601u_dma_cleanup(struct mt7601u_dev *dev) - - mt7601u_free_rx(dev); - mt7601u_free_tx(dev); -+ -+ tasklet_kill(&dev->tx_tasklet); - } -diff --git a/drivers/net/wireless/mediatek/mt7601u/init.c b/drivers/net/wireless/mediatek/mt7601u/init.c -index df3dd56..38eb20b 100644 ---- a/drivers/net/wireless/mediatek/mt7601u/init.c -+++ b/drivers/net/wireless/mediatek/mt7601u/init.c -@@ -456,6 +456,7 @@ struct mt7601u_dev *mt7601u_alloc_device(struct device *pdev) - spin_lock_init(&dev->lock); - spin_lock_init(&dev->con_mon_lock); - atomic_set(&dev->avg_ampdu_len, 1); -+ skb_queue_head_init(&dev->tx_skb_done); - - dev->stat_wq = alloc_workqueue("mt7601u", WQ_UNBOUND, 0); - if (!dev->stat_wq) { -diff --git a/drivers/net/wireless/mediatek/mt7601u/mac.c b/drivers/net/wireless/mediatek/mt7601u/mac.c -index 7514bce..e3928cf 100644 ---- a/drivers/net/wireless/mediatek/mt7601u/mac.c -+++ b/drivers/net/wireless/mediatek/mt7601u/mac.c -@@ -181,7 +181,11 @@ void mt76_send_tx_status(struct mt7601u_dev *dev, struct mt76_tx_status *stat) - } - - mt76_mac_fill_tx_status(dev, &info, stat); -+ -+ local_bh_disable(); - ieee80211_tx_status_noskb(dev->hw, sta, &info); -+ local_bh_enable(); -+ - rcu_read_unlock(); - } - -diff --git a/drivers/net/wireless/mediatek/mt7601u/mt7601u.h b/drivers/net/wireless/mediatek/mt7601u/mt7601u.h -index 6bdfc11..bc5e294 100644 ---- a/drivers/net/wireless/mediatek/mt7601u/mt7601u.h -+++ b/drivers/net/wireless/mediatek/mt7601u/mt7601u.h -@@ -199,7 +199,9 @@ struct mt7601u_dev { - - /* TX */ - spinlock_t tx_lock; -+ struct tasklet_struct tx_tasklet; - struct mt7601u_tx_queue *tx_q; -+ struct sk_buff_head tx_skb_done; - - atomic_t avg_ampdu_len; - --- -cgit v0.12 - -From 78623bfb6f4cbdba3183621e8e0e781611217022 Mon Sep 17 00:00:00 2001 -From: Jakub Kicinski -Date: Fri, 31 Jul 2015 15:04:49 +0200 -Subject: mt7601u: lock out rx path and tx status reporting - -mac80211 requires that rx path does not run concurrently with -tx status reporting. Add a spinlock which will ensure that. - -Signed-off-by: Jakub Kicinski -Signed-off-by: Kalle Valo ---- - drivers/net/wireless/mediatek/mt7601u/dma.c | 2 ++ - drivers/net/wireless/mediatek/mt7601u/init.c | 1 + - drivers/net/wireless/mediatek/mt7601u/mac.c | 4 ++-- - drivers/net/wireless/mediatek/mt7601u/mt7601u.h | 4 +++- - drivers/net/wireless/mediatek/mt7601u/tx.c | 3 +++ - 5 files changed, 11 insertions(+), 3 deletions(-) - -diff --git a/drivers/net/wireless/mediatek/mt7601u/dma.c b/drivers/net/wireless/mediatek/mt7601u/dma.c -index 63c4854..57a80cf 100644 ---- a/drivers/net/wireless/mediatek/mt7601u/dma.c -+++ b/drivers/net/wireless/mediatek/mt7601u/dma.c -@@ -112,7 +112,9 @@ static void mt7601u_rx_process_seg(struct mt7601u_dev *dev, u8 *data, - if (!skb) - return; - -+ spin_lock(&dev->mac_lock); - ieee80211_rx(dev->hw, skb); -+ spin_unlock(&dev->mac_lock); - } - - static u16 mt7601u_rx_next_seg_len(u8 *data, u32 data_len) -diff --git a/drivers/net/wireless/mediatek/mt7601u/init.c b/drivers/net/wireless/mediatek/mt7601u/init.c -index 38eb20b..26190fd 100644 ---- a/drivers/net/wireless/mediatek/mt7601u/init.c -+++ b/drivers/net/wireless/mediatek/mt7601u/init.c -@@ -454,6 +454,7 @@ struct mt7601u_dev *mt7601u_alloc_device(struct device *pdev) - spin_lock_init(&dev->tx_lock); - spin_lock_init(&dev->rx_lock); - spin_lock_init(&dev->lock); -+ spin_lock_init(&dev->mac_lock); - spin_lock_init(&dev->con_mon_lock); - atomic_set(&dev->avg_ampdu_len, 1); - skb_queue_head_init(&dev->tx_skb_done); -diff --git a/drivers/net/wireless/mediatek/mt7601u/mac.c b/drivers/net/wireless/mediatek/mt7601u/mac.c -index e3928cf..e21c53e 100644 ---- a/drivers/net/wireless/mediatek/mt7601u/mac.c -+++ b/drivers/net/wireless/mediatek/mt7601u/mac.c -@@ -182,9 +182,9 @@ void mt76_send_tx_status(struct mt7601u_dev *dev, struct mt76_tx_status *stat) - - mt76_mac_fill_tx_status(dev, &info, stat); - -- local_bh_disable(); -+ spin_lock_bh(&dev->mac_lock); - ieee80211_tx_status_noskb(dev->hw, sta, &info); -- local_bh_enable(); -+ spin_unlock_bh(&dev->mac_lock); - - rcu_read_unlock(); - } -diff --git a/drivers/net/wireless/mediatek/mt7601u/mt7601u.h b/drivers/net/wireless/mediatek/mt7601u/mt7601u.h -index bc5e294..428bd2f 100644 ---- a/drivers/net/wireless/mediatek/mt7601u/mt7601u.h -+++ b/drivers/net/wireless/mediatek/mt7601u/mt7601u.h -@@ -141,8 +141,9 @@ enum { - /** - * struct mt7601u_dev - adapter structure - * @lock: protects @wcid->tx_rate. -+ * @mac_lock: locks out mac80211's tx status and rx paths. - * @tx_lock: protects @tx_q and changes of MT7601U_STATE_*_STATS -- flags in @state. -+ * flags in @state. - * @rx_lock: protects @rx_q. - * @con_mon_lock: protects @ap_bssid, @bcn_*, @avg_rssi. - * @mutex: ensures exclusive access from mac80211 callbacks. -@@ -177,6 +178,7 @@ struct mt7601u_dev { - struct mt76_wcid __rcu *wcid[N_WCIDS]; - - spinlock_t lock; -+ spinlock_t mac_lock; - - const u16 *beacon_offsets; - -diff --git a/drivers/net/wireless/mediatek/mt7601u/tx.c b/drivers/net/wireless/mediatek/mt7601u/tx.c -index 0be2080..a0a33dc 100644 ---- a/drivers/net/wireless/mediatek/mt7601u/tx.c -+++ b/drivers/net/wireless/mediatek/mt7601u/tx.c -@@ -116,7 +116,10 @@ void mt7601u_tx_status(struct mt7601u_dev *dev, struct sk_buff *skb) - ieee80211_tx_info_clear_status(info); - info->status.rates[0].idx = -1; - info->flags |= IEEE80211_TX_STAT_ACK; -+ -+ spin_lock(&dev->mac_lock); - ieee80211_tx_status(dev->hw, skb); -+ spin_unlock(&dev->mac_lock); - } - - static int mt7601u_skb_rooms(struct mt7601u_dev *dev, struct sk_buff *skb) --- -cgit v0.12 - diff --git a/src/patches/backports-4.2.6-1_ath10k_remove_logspam.patch b/src/patches/backports-4.2.6-1_ath10k_remove_logspam.patch deleted file mode 100644 index bdb1534c2d..0000000000 --- a/src/patches/backports-4.2.6-1_ath10k_remove_logspam.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -Naur backports-4.2.6-1.org/drivers/net/wireless/ath/ath10k/htt_rx.c backports-4.2.6-1/drivers/net/wireless/ath/ath10k/htt_rx.c ---- backports-4.2.6-1.org/drivers/net/wireless/ath/ath10k/htt_rx.c 2015-11-15 22:19:40.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/wireless/ath/ath10k/htt_rx.c 2016-03-26 10:48:17.189430101 +0100 -@@ -1476,7 +1476,7 @@ - */ - - if (!rx_status->freq) { -- ath10k_warn(ar, "no channel configured; ignoring frame(s)!\n"); -+ ath10k_dbg(ar,ATH10K_DBG_DATA ,"no channel configured; ignoring frame(s)!\n"); - return false; - } - diff --git a/src/patches/linux/backports-4.2.6-1_ath10k_remove_logspam.patch b/src/patches/linux/backports-4.2.6-1_ath10k_remove_logspam.patch deleted file mode 100644 index bdb1534c2d..0000000000 --- a/src/patches/linux/backports-4.2.6-1_ath10k_remove_logspam.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -Naur backports-4.2.6-1.org/drivers/net/wireless/ath/ath10k/htt_rx.c backports-4.2.6-1/drivers/net/wireless/ath/ath10k/htt_rx.c ---- backports-4.2.6-1.org/drivers/net/wireless/ath/ath10k/htt_rx.c 2015-11-15 22:19:40.000000000 +0100 -+++ backports-4.2.6-1/drivers/net/wireless/ath/ath10k/htt_rx.c 2016-03-26 10:48:17.189430101 +0100 -@@ -1476,7 +1476,7 @@ - */ - - if (!rx_status->freq) { -- ath10k_warn(ar, "no channel configured; ignoring frame(s)!\n"); -+ ath10k_dbg(ar,ATH10K_DBG_DATA ,"no channel configured; ignoring frame(s)!\n"); - return false; - } -