diff options
Diffstat (limited to 'drivers/net/usb')
45 files changed, 17288 insertions, 4905 deletions
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index 6f600cced6e..7e7269fd370 100644 --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -8,8 +8,7 @@ menu "USB Network Adapters"  	depends on USB && NET  config USB_CATC -	tristate "USB CATC NetMate-based Ethernet device support (EXPERIMENTAL)" -	depends on EXPERIMENTAL +	tristate "USB CATC NetMate-based Ethernet device support"  	select CRC32  	---help---  	  Say Y if you want to use one of the following 10Mbps USB Ethernet @@ -82,8 +81,7 @@ config USB_PEGASUS  	  module will be called pegasus.  config USB_RTL8150 -	tristate "USB RTL8150 based ethernet device support (EXPERIMENTAL)" -	depends on EXPERIMENTAL +	tristate "USB RTL8150 based ethernet device support"  	select MII  	help  	  Say Y here if you have RTL8150 based usb-ethernet adapter. @@ -93,6 +91,17 @@ config USB_RTL8150  	  To compile this driver as a module, choose M here: the  	  module will be called rtl8150. +config USB_RTL8152 +	tristate "Realtek RTL8152/RTL8153 Based USB Ethernet Adapters" +	select 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"  	select MII @@ -131,6 +140,7 @@ config USB_NET_AX8817X  	tristate "ASIX AX88xxx Based USB 2.0 Ethernet Adapters"  	depends on USB_USBNET  	select CRC32 +	select PHYLIB  	default y  	help  	  This option adds support for ASIX AX88xxx based USB 2.0 @@ -154,6 +164,24 @@ config USB_NET_AX8817X  	  This driver creates an interface named "ethX", where X depends on  	  what other networking devices you have in use. +config USB_NET_AX88179_178A +	tristate "ASIX AX88179/178A USB 3.0/2.0 to Gigabit Ethernet" +	depends on USB_USBNET +	select CRC32 +	select 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 USB_USBNET @@ -184,7 +212,7 @@ config USB_NET_CDCETHER  config USB_NET_CDC_EEM  	tristate "CDC EEM support" -	depends on USB_USBNET && EXPERIMENTAL +	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 @@ -215,25 +243,85 @@ config USB_NET_CDC_NCM  	    * ST-Ericsson M343 HSPA Mobile Broadband Modem (reference design)  	    * Ericsson F5521gw Mobile Broadband Module +config USB_NET_HUAWEI_CDC_NCM +	tristate "Huawei NCM embedded AT channel support" +	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 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 <http://www.usb.org/>. + +	  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 -	tristate "Davicom DM9601 based USB 1.1 10/100 ethernet devices" +	tristate "Davicom DM96xx based USB 10/100 ethernet devices" +	depends on USB_USBNET +	select CRC32 +	help +	  This option adds support for Davicom DM9601/DM9620/DM9621A +	  based USB 10/100 Ethernet adapters. + +config USB_NET_SR9700 +	tristate "CoreChip-sz SR9700 based USB 1.1 10/100 ethernet devices"  	depends on USB_USBNET  	select CRC32  	help -	  This option adds support for Davicom DM9601 based USB 1.1 +	  This option adds support for CoreChip-sz SR9700 based USB 1.1  	  10/100 Ethernet adapters. +config USB_NET_SR9800 +	tristate "CoreChip-sz SR9800 based USB 2.0 10/100 ethernet devices" +	depends on USB_USBNET +	select 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  	tristate "SMSC LAN75XX based USB 2.0 gigabit ethernet devices"  	depends on USB_USBNET +	select BITREVERSE +	select CRC16  	select CRC32  	help -	  This option adds support for SMSC LAN95XX based USB 2.0 +	  This option adds support for SMSC LAN75XX based USB 2.0  	  Gigabit Ethernet adapters.  config USB_NET_SMSC95XX  	tristate "SMSC LAN95XX based USB 2.0 10/100 ethernet devices"  	depends on USB_USBNET +	select BITREVERSE +	select CRC16  	select CRC32  	help  	  This option adds support for SMSC LAN95XX based USB 2.0 @@ -258,10 +346,10 @@ config USB_NET_NET1080  	  optionally with LEDs that indicate traffic  config USB_NET_PLUSB -	tristate "Prolific PL-2301/2302 based cables" +	tristate "Prolific PL-2301/2302/25A1 based cables"  	# if the handshake/init/reset problems, from original 'plusb',  	# are ever resolved ... then remove "experimental" -	depends on USB_USBNET && EXPERIMENTAL +	depends on USB_USBNET  	help  	  Choose this option if you're using a host-to-host cable  	  with one of these chips. @@ -275,8 +363,8 @@ config USB_NET_MCS7830  	  adapters marketed under the DeLOCK brand.  config USB_NET_RNDIS_HOST -	tristate "Host for RNDIS and ActiveSync devices (EXPERIMENTAL)" -	depends on USB_USBNET && EXPERIMENTAL +	tristate "Host for RNDIS and ActiveSync devices" +	depends on USB_USBNET  	select USB_NET_CDCETHER  	help  	  This option enables hosting "Remote NDIS" USB networking links, @@ -354,7 +442,7 @@ config USB_EPSON2888  config USB_KC2190  	boolean "KT Technology KC2190 based cables (InstaNet)" -	depends on USB_NET_CDC_SUBSET && EXPERIMENTAL +	depends on USB_NET_CDC_SUBSET  	help  	  Choose this option if you're using a host-to-host cable  	  with one of these chips. @@ -385,9 +473,41 @@ config USB_NET_CX82310_ETH  	  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 +	tristate "Samsung Kalmia based LTE USB modem" +	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 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  	tristate "Option USB High Speed Mobile Devices" -	depends on USB && RFKILL +	depends on USB && RFKILL && TTY  	default n  	help  	  Choose this option if you have an Option HSDPA/HSUPA card. @@ -433,4 +553,20 @@ config USB_SIERRA_NET  	  To compile this driver as a module, choose M here: the  	  module will be called sierra_net. +config USB_VL600 +	tristate "LG VL600 modem dongle" +	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 + +  endmenu diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile index cac17030118..e2797f1e1b3 100644 --- a/drivers/net/usb/Makefile +++ b/drivers/net/usb/Makefile @@ -6,11 +6,16 @@ obj-$(CONFIG_USB_CATC)		+= catc.o  obj-$(CONFIG_USB_KAWETH)	+= kaweth.o  obj-$(CONFIG_USB_PEGASUS)	+= pegasus.o  obj-$(CONFIG_USB_RTL8150)	+= rtl8150.o +obj-$(CONFIG_USB_RTL8152)	+= r8152.o  obj-$(CONFIG_USB_HSO)		+= hso.o  obj-$(CONFIG_USB_NET_AX8817X)	+= asix.o +asix-y := asix_devices.o asix_common.o ax88172a.o +obj-$(CONFIG_USB_NET_AX88179_178A)      += ax88179_178a.o  obj-$(CONFIG_USB_NET_CDCETHER)	+= cdc_ether.o  obj-$(CONFIG_USB_NET_CDC_EEM)	+= cdc_eem.o  obj-$(CONFIG_USB_NET_DM9601)	+= dm9601.o +obj-$(CONFIG_USB_NET_SR9700)	+= sr9700.o +obj-$(CONFIG_USB_NET_SR9800)	+= sr9800.o  obj-$(CONFIG_USB_NET_SMSC75XX)	+= smsc75xx.o  obj-$(CONFIG_USB_NET_SMSC95XX)	+= smsc95xx.o  obj-$(CONFIG_USB_NET_GL620A)	+= gl620a.o @@ -23,8 +28,13 @@ obj-$(CONFIG_USB_NET_MCS7830)	+= mcs7830.o  obj-$(CONFIG_USB_USBNET)	+= usbnet.o  obj-$(CONFIG_USB_NET_INT51X1)	+= int51x1.o  obj-$(CONFIG_USB_CDC_PHONET)	+= cdc-phonet.o +obj-$(CONFIG_USB_NET_KALMIA)	+= kalmia.o  obj-$(CONFIG_USB_IPHETH)	+= ipheth.o  obj-$(CONFIG_USB_SIERRA_NET)	+= sierra_net.o  obj-$(CONFIG_USB_NET_CX82310_ETH)	+= cx82310_eth.o  obj-$(CONFIG_USB_NET_CDC_NCM)	+= cdc_ncm.o +obj-$(CONFIG_USB_NET_HUAWEI_CDC_NCM)	+= huawei_cdc_ncm.o +obj-$(CONFIG_USB_VL600)		+= lg-vl600.o +obj-$(CONFIG_USB_NET_QMI_WWAN)	+= qmi_wwan.o +obj-$(CONFIG_USB_NET_CDC_MBIM)	+= cdc_mbim.o diff --git a/drivers/net/usb/asix.c b/drivers/net/usb/asix.c deleted file mode 100644 index aea4645be7f..00000000000 --- a/drivers/net/usb/asix.c +++ /dev/null @@ -1,1584 +0,0 @@ -/* - * ASIX AX8817X based USB 2.0 Ethernet Devices - * Copyright (C) 2003-2006 David Hollis <dhollis@davehollis.com> - * Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net> - * Copyright (C) 2006 James Painter <jamie.painter@iname.com> - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA - */ - -// #define	DEBUG			// error path messages, extra info -// #define	VERBOSE			// more; success messages - -#include <linux/module.h> -#include <linux/kmod.h> -#include <linux/init.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/ethtool.h> -#include <linux/workqueue.h> -#include <linux/mii.h> -#include <linux/usb.h> -#include <linux/crc32.h> -#include <linux/usb/usbnet.h> -#include <linux/slab.h> - -#define DRIVER_VERSION "14-Jun-2006" -static const char 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_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 AX88172_EEPROM_LEN	0x40 -#define AX88772_EEPROM_LEN	0xff - -#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 - -/* 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 eeprom_len; -}; - -struct ax88172_int_data { -	__le16 res1; -	u8 link; -	__le16 res2; -	u8 status; -	__le16 res3; -} __packed; - -static int asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, -			    u16 size, void *data) -{ -	void *buf; -	int err = -ENOMEM; - -	netdev_dbg(dev->net, "asix_read_cmd() cmd=0x%02x value=0x%04x index=0x%04x size=%d\n", -		   cmd, value, index, size); - -	buf = kmalloc(size, GFP_KERNEL); -	if (!buf) -		goto out; - -	err = usb_control_msg( -		dev->udev, -		usb_rcvctrlpipe(dev->udev, 0), -		cmd, -		USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, -		value, -		index, -		buf, -		size, -		USB_CTRL_GET_TIMEOUT); -	if (err == size) -		memcpy(data, buf, size); -	else if (err >= 0) -		err = -EINVAL; -	kfree(buf); - -out: -	return err; -} - -static int asix_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, -			     u16 size, void *data) -{ -	void *buf = NULL; -	int err = -ENOMEM; - -	netdev_dbg(dev->net, "asix_write_cmd() cmd=0x%02x value=0x%04x index=0x%04x size=%d\n", -		   cmd, value, index, size); - -	if (data) { -		buf = kmemdup(data, size, GFP_KERNEL); -		if (!buf) -			goto out; -	} - -	err = usb_control_msg( -		dev->udev, -		usb_sndctrlpipe(dev->udev, 0), -		cmd, -		USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, -		value, -		index, -		buf, -		size, -		USB_CTRL_SET_TIMEOUT); -	kfree(buf); - -out: -	return err; -} - -static void asix_async_cmd_callback(struct urb *urb) -{ -	struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context; -	int status = urb->status; - -	if (status < 0) -		printk(KERN_DEBUG "asix_async_cmd_callback() failed with %d", -			status); - -	kfree(req); -	usb_free_urb(urb); -} - -static void -asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index, -				    u16 size, void *data) -{ -	struct usb_ctrlrequest *req; -	int status; -	struct urb *urb; - -	netdev_dbg(dev->net, "asix_write_cmd_async() cmd=0x%02x value=0x%04x index=0x%04x size=%d\n", -		   cmd, value, index, size); -	if ((urb = usb_alloc_urb(0, GFP_ATOMIC)) == NULL) { -		netdev_err(dev->net, "Error allocating URB in write_cmd_async!\n"); -		return; -	} - -	if ((req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC)) == NULL) { -		netdev_err(dev->net, "Failed to allocate memory for control request\n"); -		usb_free_urb(urb); -		return; -	} - -	req->bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE; -	req->bRequest = cmd; -	req->wValue = cpu_to_le16(value); -	req->wIndex = cpu_to_le16(index); -	req->wLength = cpu_to_le16(size); - -	usb_fill_control_urb(urb, dev->udev, -			     usb_sndctrlpipe(dev->udev, 0), -			     (void *)req, data, size, -			     asix_async_cmd_callback, req); - -	if((status = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { -		netdev_err(dev->net, "Error submitting the control message: status=%d\n", -			   status); -		kfree(req); -		usb_free_urb(urb); -	} -} - -static int asix_rx_fixup(struct usbnet *dev, struct sk_buff *skb) -{ -	u8  *head; -	u32  header; -	char *packet; -	struct sk_buff *ax_skb; -	u16 size; - -	head = (u8 *) skb->data; -	memcpy(&header, head, sizeof(header)); -	le32_to_cpus(&header); -	packet = head + sizeof(header); - -	skb_pull(skb, 4); - -	while (skb->len > 0) { -		if ((short)(header & 0x0000ffff) != -		    ~((short)((header & 0xffff0000) >> 16))) { -			netdev_err(dev->net, "asix_rx_fixup() Bad Header Length\n"); -		} -		/* get the packet length */ -		size = (u16) (header & 0x0000ffff); - -		if ((skb->len) - ((size + 1) & 0xfffe) == 0) { -			u8 alignment = (unsigned long)skb->data & 0x3; -			if (alignment != 0x2) { -				/* -				 * not 16bit aligned so use the room provided by -				 * the 32 bit header to align the data -				 * -				 * note we want 16bit alignment as MAC header is -				 * 14bytes thus ip header will be aligned on -				 * 32bit boundary so accessing ipheader elements -				 * using a cast to struct ip header wont cause -				 * an unaligned accesses. -				 */ -				u8 realignment = (alignment + 2) & 0x3; -				memmove(skb->data - realignment, -					skb->data, -					size); -				skb->data -= realignment; -				skb_set_tail_pointer(skb, size); -			} -			return 2; -		} - -		if (size > dev->net->mtu + ETH_HLEN) { -			netdev_err(dev->net, "asix_rx_fixup() Bad RX Length %d\n", -				   size); -			return 0; -		} -		ax_skb = skb_clone(skb, GFP_ATOMIC); -		if (ax_skb) { -			u8 alignment = (unsigned long)packet & 0x3; -			ax_skb->len = size; - -			if (alignment != 0x2) { -				/* -				 * not 16bit aligned use the room provided by -				 * the 32 bit header to align the data -				 */ -				u8 realignment = (alignment + 2) & 0x3; -				memmove(packet - realignment, packet, size); -				packet -= realignment; -			} -			ax_skb->data = packet; -			skb_set_tail_pointer(ax_skb, size); -			usbnet_skb_return(dev, ax_skb); -		} else { -			return 0; -		} - -		skb_pull(skb, (size + 1) & 0xfffe); - -		if (skb->len == 0) -			break; - -		head = (u8 *) skb->data; -		memcpy(&header, head, sizeof(header)); -		le32_to_cpus(&header); -		packet = head + sizeof(header); -		skb_pull(skb, 4); -	} - -	if (skb->len < 0) { -		netdev_err(dev->net, "asix_rx_fixup() Bad SKB Length %d\n", -			   skb->len); -		return 0; -	} -	return 1; -} - -static 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) % 512) ? 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 ((skb->len % 512) == 0) { -		cpu_to_le32s(&padbytes); -		memcpy(skb_tail_pointer(skb), &padbytes, sizeof(padbytes)); -		skb_put(skb, sizeof(padbytes)); -	} -	return skb; -} - -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) { -		if (link) { -			netif_carrier_on(dev->net); -			usbnet_defer_kevent (dev, EVENT_LINK_RESET ); -		} else -			netif_carrier_off(dev->net); -		netdev_dbg(dev->net, "Link Status is: %d\n", link); -	} -} - -static inline 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; -} - -static inline 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; -} - -static inline int asix_get_phy_addr(struct usbnet *dev) -{ -	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[1]; - -out: -	return ret; -} - -static 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; -} - -static 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; -} - -static 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; -} - -static 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); -		goto out; -	} -	ret = le16_to_cpu(v); -out: -	return ret; -} - -static 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; -} - -static 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 - */ -static 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); -} - -static 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); -} - -static 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); -} - -/* Get the PHY Identifier from the PHYSID1 & PHYSID2 MII registers */ -static u32 asix_get_phyid(struct usbnet *dev) -{ -	int phy_reg; -	u32 phy_id; - -	phy_reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_PHYSID1); -	if (phy_reg < 0) -		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 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_MODE) { -		if (opt & AX_MONITOR_LINK) -			wolinfo->wolopts |= WAKE_PHY; -		if (opt & AX_MONITOR_MAGIC) -			wolinfo->wolopts |= WAKE_MAGIC; -	} -} - -static 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 (opt != 0) -		opt |= AX_MONITOR_MODE; - -	if (asix_write_cmd(dev, AX_CMD_WRITE_MONITOR_MODE, -			      opt, 0, 0, NULL) < 0) -		return -EINVAL; - -	return 0; -} - -static int asix_get_eeprom_len(struct net_device *net) -{ -	struct usbnet *dev = netdev_priv(net); -	struct asix_data *data = (struct asix_data *)&dev->data; - -	return data->eeprom_len; -} - -static int asix_get_eeprom(struct net_device *net, -			      struct ethtool_eeprom *eeprom, u8 *data) -{ -	struct usbnet *dev = netdev_priv(net); -	__le16 *ebuf = (__le16 *)data; -	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 = AX_EEPROM_MAGIC; - -	/* ax8817x returns 2 bytes from eeprom on read */ -	for (i=0; i < eeprom->len / 2; i++) { -		if (asix_read_cmd(dev, AX_CMD_READ_EEPROM, -			eeprom->offset + i, 0, 2, &ebuf[i]) < 0) -			return -EINVAL; -	} -	return 0; -} - -static void asix_get_drvinfo (struct net_device *net, -				 struct ethtool_drvinfo *info) -{ -	struct usbnet *dev = netdev_priv(net); -	struct asix_data *data = (struct asix_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 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); -} - -static 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; -} - -/* 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, -	.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; - -	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: %d duplex: %d setting mode to 0x%04x\n", -		   ecmd.speed, 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_multicast_list = 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; -	struct asix_data *data = (struct asix_data *)&dev->data; - -	data->eeprom_len = AX88172_EEPROM_LEN; - -	usbnet_get_endpoints(dev,intf); - -	/* Toggle the GPIOs in a manufacturer/model specific way */ -	for (i = 2; i >= 0; i--) { -		if ((ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS, -					(gpio_bits >> (i * 8)) & 0xff, 0, 0, -					NULL)) < 0) -			goto out; -		msleep(5); -	} - -	if ((ret = asix_write_rx_ctl(dev, 0x80)) < 0) -		goto out; - -	/* Get the MAC address */ -	if ((ret = asix_read_cmd(dev, AX88172_CMD_READ_NODE_ID, -				0, 0, ETH_ALEN, buf)) < 0) { -		dbg("read AX_CMD_READ_NODE_ID failed: %d", ret); -		goto out; -	} -	memcpy(dev->net->dev_addr, buf, ETH_ALEN); - -	/* 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; - -	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, -	.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; - -	mii_check_media(&dev->mii, 1, 1); -	mii_ethtool_gset(&dev->mii, &ecmd); -	mode = AX88772_MEDIUM_DEFAULT; - -	if (ecmd.speed != SPEED_100) -		mode &= ~AX_MEDIUM_PS; - -	if (ecmd.duplex != DUPLEX_FULL) -		mode &= ~AX_MEDIUM_FD; - -	netdev_dbg(dev->net, "ax88772_link_reset() speed: %d duplex: %d setting mode to 0x%04x\n", -		   ecmd.speed, ecmd.duplex, mode); - -	asix_write_medium_mode(dev, mode); - -	return 0; -} - -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_multicast_list = asix_set_multicast, -}; - -static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf) -{ -	int ret, embd_phy; -	u16 rx_ctl; -	struct asix_data *data = (struct asix_data *)&dev->data; -	u8 buf[ETH_ALEN]; -	u32 phyid; - -	data->eeprom_len = AX88772_EEPROM_LEN; - -	usbnet_get_endpoints(dev,intf); - -	if ((ret = asix_write_gpio(dev, -			AX_GPIO_RSE | AX_GPIO_GPO_2 | AX_GPIO_GPO2EN, 5)) < 0) -		goto out; - -	/* 0x10 is the phy id of the embedded 10/100 ethernet phy */ -	embd_phy = ((asix_get_phy_addr(dev) & 0x1f) == 0x10 ? 1 : 0); -	if ((ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, -				embd_phy, 0, 0, NULL)) < 0) { -		dbg("Select PHY #1 failed: %d", ret); -		goto out; -	} - -	if ((ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_PRL)) < 0) -		goto out; - -	msleep(150); -	if ((ret = asix_sw_reset(dev, AX_SWRESET_CLEAR)) < 0) -		goto out; - -	msleep(150); -	if (embd_phy) { -		if ((ret = asix_sw_reset(dev, AX_SWRESET_IPRL)) < 0) -			goto out; -	} -	else { -		if ((ret = asix_sw_reset(dev, AX_SWRESET_PRTE)) < 0) -			goto out; -	} - -	msleep(150); -	rx_ctl = asix_read_rx_ctl(dev); -	dbg("RX_CTL is 0x%04x after software reset", rx_ctl); -	if ((ret = asix_write_rx_ctl(dev, 0x0000)) < 0) -		goto out; - -	rx_ctl = asix_read_rx_ctl(dev); -	dbg("RX_CTL is 0x%04x setting to 0x0000", rx_ctl); - -	/* Get the MAC address */ -	if ((ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, -				0, 0, ETH_ALEN, buf)) < 0) { -		dbg("Failed to read MAC address: %d", ret); -		goto out; -	} -	memcpy(dev->net->dev_addr, buf, ETH_ALEN); - -	/* 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); - -	phyid = asix_get_phyid(dev); -	dbg("PHYID=0x%08x", phyid); - -	if ((ret = asix_sw_reset(dev, AX_SWRESET_PRL)) < 0) -		goto out; - -	msleep(150); - -	if ((ret = asix_sw_reset(dev, AX_SWRESET_IPRL | AX_SWRESET_PRL)) < 0) -		goto out; - -	msleep(150); - -	dev->net->netdev_ops = &ax88772_netdev_ops; -	dev->net->ethtool_ops = &ax88772_ethtool_ops; - -	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); - -	if ((ret = asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT)) < 0) -		goto out; - -	if ((ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0, -				AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT, -				AX88772_IPG2_DEFAULT, 0, NULL)) < 0) { -		dbg("Write IPG,IPG1,IPG2 failed: %d", ret); -		goto out; -	} - -	/* Set RX_CTL to default values with 2k buffer, and enable cactus */ -	if ((ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL)) < 0) -		goto out; - -	rx_ctl = asix_read_rx_ctl(dev); -	dbg("RX_CTL is 0x%04x after all initializations", rx_ctl); - -	rx_ctl = asix_read_medium_status(dev); -	dbg("Medium Status is 0x%04x after all initializations", rx_ctl); - -	/* 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; -	} -	return 0; - -out: -	return ret; -} - -static 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, -	.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 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_link_reset(struct usbnet *dev) -{ -	u16 mode; -	struct ethtool_cmd ecmd; -	struct asix_data *data = (struct asix_data *)&dev->data; - -	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; - -	if (ecmd.speed == SPEED_1000) -		mode |= AX_MEDIUM_GM; -	else if (ecmd.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: %d duplex: %d setting mode to 0x%04x\n", -		   ecmd.speed, ecmd.duplex, mode); - -	asix_write_medium_mode(dev, mode); - -	if (data->phymode == PHY_MODE_MARVELL && data->ledmode) -		marvell_led_status(dev, ecmd.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); - -	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_multicast_list = 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) -{ -	struct asix_data *data = (struct asix_data *)&dev->data; -	int ret; -	u8 buf[ETH_ALEN]; -	__le16 eeprom; -	u8 status; -	int gpio0 = 0; -	u32 phyid; - -	usbnet_get_endpoints(dev,intf); - -	asix_read_cmd(dev, AX_CMD_READ_GPIOS, 0, 0, 1, &status); -	dbg("GPIO Status: 0x%04x", 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); - -	dbg("EEPROM index 0x17 is 0x%04x", eeprom); - -	if (eeprom == cpu_to_le16(0xffff)) { -		data->phymode = PHY_MODE_MARVELL; -		data->ledmode = 0; -		gpio0 = 1; -	} else { -		data->phymode = le16_to_cpu(eeprom) & 7; -		data->ledmode = le16_to_cpu(eeprom) >> 8; -		gpio0 = (le16_to_cpu(eeprom) & 0x80) ? 0 : 1; -	} -	dbg("GPIO0: %d, PhyMode: %d", gpio0, data->phymode); - -	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 { -		dbg("gpio phymode == 1 path"); -		asix_write_gpio(dev, AX_GPIO_GPO1EN, 30); -		asix_write_gpio(dev, AX_GPIO_GPO1EN | AX_GPIO_GPO_1, 30); -	} - -	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); - -	/* Get the MAC address */ -	if ((ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, -				0, 0, ETH_ALEN, buf)) < 0) { -		dbg("Failed to read MAC address: %d", ret); -		goto out; -	} -	memcpy(dev->net->dev_addr, buf, ETH_ALEN); - -	/* 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; - -	phyid = asix_get_phyid(dev); -	dbg("PHYID=0x%08x", phyid); - -	if (data->phymode == PHY_MODE_MARVELL) { -		marvell_phy_init(dev); -		msleep(60); -	} - -	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); - -	if ((ret = asix_write_medium_mode(dev, AX88178_MEDIUM_DEFAULT)) < 0) -		goto out; - -	if ((ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL)) < 0) -		goto out; - -	/* 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; -	} -	return 0; - -out: -	return ret; -} - -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, -	.status = asix_status, -	.link_reset = ax88772_link_reset, -	.reset = ax88772_link_reset, -	.flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR, -	.rx_fixup = asix_rx_fixup, -	.tx_fixup = asix_tx_fixup, -}; - -static const struct driver_info ax88178_info = { -	.description = "ASIX AX88178 USB 2.0 Ethernet", -	.bind = ax88178_bind, -	.status = asix_status, -	.link_reset = ax88178_link_reset, -	.reset = ax88178_link_reset, -	.flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR, -	.rx_fixup = asix_rx_fixup, -	.tx_fixup = asix_tx_fixup, -}; - -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, -}, { -	// 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, -}, { -	// 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, -}, { -	// 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, -}, { -	// 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, -}, -	{ },		// END -}; -MODULE_DEVICE_TABLE(usb, products); - -static struct usb_driver asix_driver = { -	.name =		"asix", -	.id_table =	products, -	.probe =	usbnet_probe, -	.suspend =	usbnet_suspend, -	.resume =	usbnet_resume, -	.disconnect =	usbnet_disconnect, -	.supports_autosuspend = 1, -}; - -static int __init asix_init(void) -{ - 	return usb_register(&asix_driver); -} -module_init(asix_init); - -static void __exit asix_exit(void) -{ - 	usb_deregister(&asix_driver); -} -module_exit(asix_exit); - -MODULE_AUTHOR("David Hollis"); -MODULE_DESCRIPTION("ASIX AX8817X based USB 2.0 Ethernet Devices"); -MODULE_LICENSE("GPL"); - diff --git a/drivers/net/usb/asix.h b/drivers/net/usb/asix.h new file mode 100644 index 00000000000..5d049d00c2d --- /dev/null +++ b/drivers/net/usb/asix.h @@ -0,0 +1,234 @@ +/* + * ASIX AX8817X based USB 2.0 Ethernet Devices + * Copyright (C) 2003-2006 David Hollis <dhollis@davehollis.com> + * Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net> + * Copyright (C) 2006 James Painter <jamie.painter@iname.com> + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef _ASIX_H +#define _ASIX_H + +// #define	DEBUG			// error path messages, extra info +// #define	VERBOSE			// more; success messages + +#include <linux/module.h> +#include <linux/kmod.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/ethtool.h> +#include <linux/workqueue.h> +#include <linux/mii.h> +#include <linux/usb.h> +#include <linux/crc32.h> +#include <linux/usb/usbnet.h> +#include <linux/slab.h> +#include <linux/if_vlan.h> + +#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 --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c new file mode 100644 index 00000000000..5c55f11572b --- /dev/null +++ b/drivers/net/usb/asix_common.c @@ -0,0 +1,582 @@ +/* + * ASIX AX8817X based USB 2.0 Ethernet Devices + * Copyright (C) 2003-2006 David Hollis <dhollis@davehollis.com> + * Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net> + * Copyright (C) 2006 James Painter <jamie.painter@iname.com> + * 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 <http://www.gnu.org/licenses/>. + */ + +#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)); +	} +	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 --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c new file mode 100644 index 00000000000..5d194093f3e --- /dev/null +++ b/drivers/net/usb/asix_devices.c @@ -0,0 +1,1116 @@ +/* + * ASIX AX8817X based USB 2.0 Ethernet Devices + * Copyright (C) 2003-2006 David Hollis <dhollis@davehollis.com> + * Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net> + * Copyright (C) 2006 James Painter <jamie.painter@iname.com> + * 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 <http://www.gnu.org/licenses/>. + */ + +#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; +	} + +	ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_PRL); +	if (ret < 0) +		return ret; + +	msleep(150); + +	ret = asix_sw_reset(dev, AX_SWRESET_CLEAR); +	if (ret < 0) +		return ret; + +	msleep(150); + +	ret = asix_sw_reset(dev, embd_phy ? AX_SWRESET_IPRL : AX_SWRESET_PRTE); + +	/* 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) +{ +	if (dev->driver_priv) +		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_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, +}, { +	// 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 --git a/drivers/net/usb/ax88172a.c b/drivers/net/usb/ax88172a.c new file mode 100644 index 00000000000..5f18fcb8dcc --- /dev/null +++ b/drivers/net/usb/ax88172a.c @@ -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 <dhollis@davehollis.com> + * Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net> + * Copyright (C) 2006 James Painter <jamie.painter@iname.com> + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "asix.h" +#include <linux/phy.h> + +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-<usb bus number>-<usb device number> */ +	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 --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c new file mode 100644 index 00000000000..054e59ca694 --- /dev/null +++ b/drivers/net/usb/ax88179_178a.c @@ -0,0 +1,1487 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/etherdevice.h> +#include <linux/mii.h> +#include <linux/usb.h> +#include <linux/crc32.h> +#include <linux/usb/usbnet.h> + +#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 + +struct ax88179_data { +	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 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_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, +	.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; + +	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 */ +	return ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, +				 ETH_ALEN, net->dev_addr); +} + +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; + +	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); + +	/* 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); + +	netif_carrier_on(dev->net); + +	return 0; +} + +static int ax88179_reset(struct usbnet *dev) +{ +	u8 buf[5]; +	u16 *tmp16; +	u8 *tmp; + +	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); + +	/* 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 --git a/drivers/net/usb/catc.c b/drivers/net/usb/catc.c index 97687d33590..8cfc3bb0c6a 100644 --- a/drivers/net/usb/catc.c +++ b/drivers/net/usb/catc.c @@ -24,15 +24,13 @@   * GNU General Public License for more details.   *    * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>.   *    * Should you need to contact me, the author, you can do so either by   * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:   * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic   */ -#include <linux/init.h>  #include <linux/module.h>  #include <linux/kernel.h>  #include <linux/string.h> @@ -236,7 +234,8 @@ static void catc_rx_done(struct urb *urb)  	}  	if (status) { -		dbg("rx_done, status %d, length %d", status, urb->actual_length); +		dev_dbg(&urb->dev->dev, "rx_done, status %d, length %d\n", +			status, urb->actual_length);  		return;  	} @@ -275,10 +274,11 @@ static void catc_rx_done(struct urb *urb)  		if (atomic_read(&catc->recq_sz)) {  			int state;  			atomic_dec(&catc->recq_sz); -			dbg("getting extra packet"); +			netdev_dbg(catc->netdev, "getting extra packet\n");  			urb->dev = catc->usbdev;  			if ((state = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { -				dbg("submit(rx_urb) status %d", state); +				netdev_dbg(catc->netdev, +					   "submit(rx_urb) status %d\n", state);  			}  		} else {  			clear_bit(RX_RUNNING, &catc->flags); @@ -317,18 +317,20 @@ static void catc_irq_done(struct urb *urb)  		return;  	/* -EPIPE:  should clear the halt */  	default:		/* error */ -		dbg("irq_done, status %d, data %02x %02x.", status, data[0], data[1]); +		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); -		dbg("link ok"); +		netdev_dbg(catc->netdev, "link ok\n");  	}  	if (linksts == LinkBad) {  		netif_carrier_off(catc->netdev); -		dbg("link bad"); +		netdev_dbg(catc->netdev, "link bad\n");  	}  	if (hasdata) { @@ -338,16 +340,18 @@ static void catc_irq_done(struct urb *urb)  		} else {  			catc->rx_urb->dev = catc->usbdev;  			if ((res = usb_submit_urb(catc->rx_urb, GFP_ATOMIC)) < 0) { -				err("submit(rx_urb) status %d", res); +				dev_err(&catc->usbdev->dev, +					"submit(rx_urb) status %d\n", res);  			}  		}   	}  resubmit:  	res = usb_submit_urb (urb, GFP_ATOMIC);  	if (res) -		err ("can't resubmit intr, %s-%s, status %d", -				catc->usbdev->bus->bus_name, -				catc->usbdev->devpath, res); +		dev_err(&catc->usbdev->dev, +			"can't resubmit intr, %s-%s, status %d\n", +			catc->usbdev->bus->bus_name, +			catc->usbdev->devpath, res);  }  /* @@ -366,7 +370,8 @@ static int catc_tx_run(struct catc *catc)  	catc->tx_urb->dev = catc->usbdev;  	if ((status = usb_submit_urb(catc->tx_urb, GFP_ATOMIC)) < 0) -		err("submit(tx_urb), status %d", status); +		dev_err(&catc->usbdev->dev, "submit(tx_urb), status %d\n", +			status);  	catc->tx_idx = !catc->tx_idx;  	catc->tx_ptr = 0; @@ -382,7 +387,7 @@ static void catc_tx_done(struct urb *urb)  	int r, status = urb->status;  	if (status == -ECONNRESET) { -		dbg("Tx Reset."); +		dev_dbg(&urb->dev->dev, "Tx Reset.\n");  		urb->status = 0;  		catc->netdev->trans_start = jiffies;  		catc->netdev->stats.tx_errors++; @@ -392,7 +397,8 @@ static void catc_tx_done(struct urb *urb)  	}  	if (status) { -		dbg("tx_done, status %d, length %d", status, urb->actual_length); +		dev_dbg(&urb->dev->dev, "tx_done, status %d, length %d\n", +			status, urb->actual_length);  		return;  	} @@ -495,8 +501,9 @@ static void catc_ctrl_run(struct catc *catc)  	if (!q->dir && q->buf && q->len)  		memcpy(catc->ctrl_buf, q->buf, q->len); -	if ((status = usb_submit_urb(catc->ctrl_urb, GFP_KERNEL))) -		err("submit(ctrl_urb) status %d", status); +	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) @@ -507,7 +514,8 @@ static void catc_ctrl_done(struct urb *urb)  	int status = urb->status;  	if (status) -		dbg("ctrl_done, status %d, len %d.", status, urb->actual_length); +		dev_dbg(&urb->dev->dev, "ctrl_done, status %d, len %d.\n", +			status, urb->actual_length);  	spin_lock_irqsave(&catc->ctrl_lock, flags); @@ -555,7 +563,7 @@ static int catc_ctrl_async(struct catc *catc, u8 dir, u8 request, u16 value,  	catc->ctrl_head = (catc->ctrl_head + 1) & (CTRL_QUEUE - 1);  	if (catc->ctrl_head == catc->ctrl_tail) { -		err("ctrl queue full"); +		dev_err(&catc->usbdev->dev, "ctrl queue full\n");  		catc->ctrl_tail = (catc->ctrl_tail + 1) & (CTRL_QUEUE - 1);  		retval = -1;  	} @@ -630,10 +638,10 @@ static void catc_set_multicast_list(struct net_device *netdev)  {  	struct catc *catc = netdev_priv(netdev);  	struct netdev_hw_addr *ha; -	u8 broadcast[6]; +	u8 broadcast[ETH_ALEN];  	u8 rx = RxEnable | RxPolarity | RxMultiCast; -	memset(broadcast, 0xff, 6); +	memset(broadcast, 0xff, ETH_ALEN);  	memset(catc->multicast, 0, 64);  	catc_multicast(broadcast, catc->multicast); @@ -663,7 +671,9 @@ static void catc_set_multicast_list(struct net_device *netdev)  		f5u011_mchash_async(catc, catc->multicast);  		if (catc->rxmode[0] != rx) {  			catc->rxmode[0] = rx; -			dbg("Setting RX mode to %2.2X %2.2X", catc->rxmode[0], catc->rxmode[1]); +			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);  		}  	} @@ -673,9 +683,9 @@ static void catc_get_drvinfo(struct net_device *dev,  			     struct ethtool_drvinfo *info)  {  	struct catc *catc = netdev_priv(dev); -	strncpy(info->driver, driver_name, ETHTOOL_BUSINFO_LEN); -	strncpy(info->version, DRIVER_VERSION, ETHTOOL_BUSINFO_LEN); -	usb_make_path (catc->usbdev, info->bus_info, sizeof info->bus_info); +	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) @@ -686,7 +696,7 @@ static int catc_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)  	cmd->supported = SUPPORTED_10baseT_Half | SUPPORTED_TP;  	cmd->advertising = ADVERTISED_10baseT_Half | ADVERTISED_TP; -	cmd->speed = SPEED_10; +	ethtool_cmd_speed_set(cmd, SPEED_10);  	cmd->duplex = DUPLEX_HALF;  	cmd->port = PORT_TP;   	cmd->phy_address = 0; @@ -714,7 +724,8 @@ static int catc_open(struct net_device *netdev)  	catc->irq_urb->dev = catc->usbdev;  	if ((status = usb_submit_urb(catc->irq_urb, GFP_KERNEL)) < 0) { -		err("submit(irq_urb) status %d", status); +		dev_err(&catc->usbdev->dev, "submit(irq_urb) status %d\n", +			status);  		return -1;  	} @@ -749,7 +760,7 @@ static const struct net_device_ops catc_netdev_ops = {  	.ndo_start_xmit		= catc_start_xmit,  	.ndo_tx_timeout		= catc_tx_timeout, -	.ndo_set_multicast_list = catc_set_multicast_list, +	.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, @@ -761,15 +772,16 @@ static const struct net_device_ops catc_netdev_ops = {  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[6]; +	u8 broadcast[ETH_ALEN];  	int i, pktsz;  	if (usb_set_interface(usbdev,  			intf->altsetting->desc.bInterfaceNumber, 1)) { -                err("Can't set altsetting 1."); +		dev_err(dev, "Can't set altsetting 1.\n");  		return -EIO;  	} @@ -781,7 +793,7 @@ static int catc_probe(struct usb_interface *intf, const struct usb_device_id *id  	netdev->netdev_ops = &catc_netdev_ops;  	netdev->watchdog_timeo = TX_TIMEOUT; -	SET_ETHTOOL_OPS(netdev, &ops); +	netdev->ethtool_ops = &ops;  	catc->usbdev = usbdev;  	catc->netdev = netdev; @@ -799,7 +811,7 @@ static int catc_probe(struct usb_interface *intf, const struct usb_device_id *id  	catc->irq_urb = usb_alloc_urb(0, GFP_KERNEL);  	if ((!catc->ctrl_urb) || (!catc->tx_urb) ||   	    (!catc->rx_urb) || (!catc->irq_urb)) { -		err("No free urbs available."); +		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); @@ -812,7 +824,7 @@ static int catc_probe(struct usb_interface *intf, const struct usb_device_id *id  	if (le16_to_cpu(usbdev->descriptor.idVendor) == 0x0423 &&   	    le16_to_cpu(usbdev->descriptor.idProduct) == 0xa &&  	    le16_to_cpu(catc->usbdev->descriptor.bcdDevice) == 0x0130) { -		dbg("Testing for f5u011"); +		dev_dbg(dev, "Testing for f5u011\n");  		catc->is_f5u011 = 1;		  		atomic_set(&catc->recq_sz, 0);  		pktsz = RX_PKT_SZ; @@ -833,7 +845,7 @@ static int catc_probe(struct usb_interface *intf, const struct usb_device_id *id                  catc->irq_buf, 2, catc_irq_done, catc, 1);  	if (!catc->is_f5u011) { -		dbg("Checking memory size\n"); +		dev_dbg(dev, "Checking memory size\n");  		i = 0x12345678;  		catc_write_mem(catc, 0x7a80, &i, 4); @@ -845,7 +857,7 @@ static int catc_probe(struct usb_interface *intf, const struct usb_device_id *id  		case 0x12345678:  			catc_set_reg(catc, TxBufCount, 8);  			catc_set_reg(catc, RxBufCount, 32); -			dbg("64k Memory\n"); +			dev_dbg(dev, "64k Memory\n");  			break;  		default:  			dev_warn(&intf->dev, @@ -853,49 +865,49 @@ static int catc_probe(struct usb_interface *intf, const struct usb_device_id *id  		case 0x87654321:  			catc_set_reg(catc, TxBufCount, 4);  			catc_set_reg(catc, RxBufCount, 16); -			dbg("32k Memory\n"); +			dev_dbg(dev, "32k Memory\n");  			break;  		} -		dbg("Getting MAC from SEEROM."); +		dev_dbg(dev, "Getting MAC from SEEROM.\n");  		catc_get_mac(catc, netdev->dev_addr); -		dbg("Setting MAC into registers."); +		dev_dbg(dev, "Setting MAC into registers.\n");  		for (i = 0; i < 6; i++)  			catc_set_reg(catc, StationAddr0 - i, netdev->dev_addr[i]); -		dbg("Filling the multicast list."); +		dev_dbg(dev, "Filling the multicast list.\n"); -		memset(broadcast, 0xff, 6); +		memset(broadcast, 0xff, ETH_ALEN);  		catc_multicast(broadcast, catc->multicast);  		catc_multicast(netdev->dev_addr, catc->multicast);  		catc_write_mem(catc, 0xfa80, catc->multicast, 64); -		dbg("Clearing error counters."); +		dev_dbg(dev, "Clearing error counters.\n");  		for (i = 0; i < 8; i++)  			catc_set_reg(catc, EthStats + i, 0);  		catc->last_stats = jiffies; -		dbg("Enabling."); +		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 { -		dbg("Performing reset\n"); +		dev_dbg(dev, "Performing reset\n");  		catc_reset(catc);  		catc_get_mac(catc, netdev->dev_addr); -		dbg("Setting RX Mode"); +		dev_dbg(dev, "Setting RX Mode\n");  		catc->rxmode[0] = RxEnable | RxPolarity | RxMultiCast;  		catc->rxmode[1] = 0;  		f5u011_rxmode(catc, catc->rxmode);  	} -	dbg("Init done."); +	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); @@ -947,21 +959,7 @@ static struct usb_driver catc_driver = {  	.probe =	catc_probe,  	.disconnect =	catc_disconnect,  	.id_table =	catc_id_table, +	.disable_hub_initiated_lpm = 1,  }; -static int __init catc_init(void) -{ -	int result = usb_register(&catc_driver); -	if (result == 0) -		printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" -		       DRIVER_DESC "\n"); -	return result; -} - -static void __exit catc_exit(void) -{ -	usb_deregister(&catc_driver); -} - -module_init(catc_init); -module_exit(catc_exit); +module_usb_driver(catc_driver); diff --git a/drivers/net/usb/cdc-phonet.c b/drivers/net/usb/cdc-phonet.c index 109751bad3b..6358d420e18 100644 --- a/drivers/net/usb/cdc-phonet.c +++ b/drivers/net/usb/cdc-phonet.c @@ -21,6 +21,7 @@   */  #include <linux/kernel.h> +#include <linux/mm.h>  #include <linux/module.h>  #include <linux/gfp.h>  #include <linux/usb.h> @@ -129,7 +130,7 @@ static int rx_submit(struct usbpn_dev *pnd, struct urb *req, gfp_t gfp_flags)  	struct page *page;  	int err; -	page = __netdev_alloc_page(dev, gfp_flags); +	page = __skb_alloc_page(gfp_flags | __GFP_NOMEMALLOC, NULL);  	if (!page)  		return -ENOMEM; @@ -139,7 +140,7 @@ static int rx_submit(struct usbpn_dev *pnd, struct urb *req, gfp_t gfp_flags)  	err = usb_submit_urb(req, gfp_flags);  	if (unlikely(err)) {  		dev_dbg(&dev->dev, "RX submit error (%d)\n", err); -		netdev_free_page(dev, page); +		put_page(page);  	}  	return err;  } @@ -163,12 +164,14 @@ static void rx_complete(struct urb *req)  				/* 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, 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, 0, req->actual_length, +					PAGE_SIZE);  			page = NULL;  		}  		if (req->actual_length < PAGE_SIZE) @@ -207,9 +210,9 @@ static void rx_complete(struct urb *req)  	dev->stats.rx_errors++;  resubmit:  	if (page) -		netdev_free_page(dev, page); +		put_page(page);  	if (req) -		rx_submit(pnd, req, GFP_ATOMIC); +		rx_submit(pnd, req, GFP_ATOMIC | __GFP_COLD);  }  static int usbpn_close(struct net_device *dev); @@ -228,7 +231,8 @@ static int usbpn_open(struct net_device *dev)  	for (i = 0; i < rxq_size; i++) {  		struct urb *req = usb_alloc_urb(0, GFP_KERNEL); -		if (!req || rx_submit(pnd, req, GFP_KERNEL)) { +		if (!req || rx_submit(pnd, req, GFP_KERNEL | __GFP_COLD)) { +			usb_free_urb(req);  			usbpn_close(dev);  			return -ENOMEM;  		} @@ -324,17 +328,17 @@ MODULE_DEVICE_TABLE(usb, usbpn_ids);  static struct usb_driver usbpn_driver; -int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *id) +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_cdc_header_desc *phonet_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; @@ -355,10 +359,7 @@ int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *id)  					(struct usb_cdc_union_desc *)data;  				break;  			case 0xAB: -				if (phonet_header || dlen < 5) -					break; -				phonet_header = -					(struct usb_cdc_header_desc *)data; +				phonet = 1;  				break;  			}  		} @@ -366,7 +367,7 @@ int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *id)  		len -= dlen;  	} -	if (!union_header || !phonet_header) +	if (!union_header || !phonet)  		return -EINVAL;  	data_intf = usb_ifnum_to_if(usbdev, union_header->bSlaveInterface0); @@ -392,10 +393,9 @@ int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *id)  	pnd = netdev_priv(dev);  	SET_NETDEV_DEV(dev, &intf->dev); -	netif_stop_queue(dev);  	pnd->dev = dev; -	pnd->usb = usb_get_dev(usbdev); +	pnd->usb = usbdev;  	pnd->intf = intf;  	pnd->data_intf = data_intf;  	spin_lock_init(&pnd->tx_lock); @@ -441,7 +441,6 @@ out:  static void usbpn_disconnect(struct usb_interface *intf)  {  	struct usbpn_dev *pnd = usb_get_intfdata(intf); -	struct usb_device *usb = pnd->usb;  	if (pnd->disconnected)  		return; @@ -450,7 +449,6 @@ static void usbpn_disconnect(struct usb_interface *intf)  	usb_driver_release_interface(&usbpn_driver,  			(pnd->intf == intf) ? pnd->data_intf : pnd->intf);  	unregister_netdev(pnd->dev); -	usb_put_dev(usb);  }  static struct usb_driver usbpn_driver = { @@ -458,20 +456,10 @@ static struct usb_driver usbpn_driver = {  	.probe =	usbpn_probe,  	.disconnect =	usbpn_disconnect,  	.id_table =	usbpn_ids, +	.disable_hub_initiated_lpm = 1,  }; -static int __init usbpn_init(void) -{ -	return usb_register(&usbpn_driver); -} - -static void __exit usbpn_exit(void) -{ -	usb_deregister(&usbpn_driver); -} - -module_init(usbpn_init); -module_exit(usbpn_exit); +module_usb_driver(usbpn_driver);  MODULE_AUTHOR("Remi Denis-Courmont");  MODULE_DESCRIPTION("USB CDC Phonet host interface"); diff --git a/drivers/net/usb/cdc_eem.c b/drivers/net/usb/cdc_eem.c index 5f3b97668e6..f7180f8db39 100644 --- a/drivers/net/usb/cdc_eem.c +++ b/drivers/net/usb/cdc_eem.c @@ -14,12 +14,10 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  #include <linux/module.h> -#include <linux/init.h>  #include <linux/netdevice.h>  #include <linux/etherdevice.h>  #include <linux/ctype.h> @@ -31,6 +29,7 @@  #include <linux/usb/cdc.h>  #include <linux/usb/usbnet.h>  #include <linux/gfp.h> +#include <linux/if_vlan.h>  /* @@ -92,7 +91,8 @@ static int eem_bind(struct usbnet *dev, struct usb_interface *intf)  	/* no jumbogram (16K) support for now */ -	dev->net->hard_header_len += EEM_HEAD + ETH_FCS_LEN; +	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;  } @@ -190,7 +190,7 @@ static int eem_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  		/*  		 * EEM packet header format: -		 * b0..14:	EEM type dependant (Data or Command) +		 * b0..14:	EEM type dependent (Data or Command)  		 * b15:		bmType  		 */  		header = get_unaligned_le16(skb->data); @@ -243,8 +243,12 @@ static int eem_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  			 *  - 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; @@ -340,7 +344,7 @@ next:  static const struct driver_info eem_info = {  	.description =	"CDC EEM Device", -	.flags =	FLAG_ETHER, +	.flags =	FLAG_ETHER | FLAG_POINTTOPOINT,  	.bind =		eem_bind,  	.rx_fixup =	eem_rx_fixup,  	.tx_fixup =	eem_tx_fixup, @@ -367,20 +371,10 @@ static struct usb_driver eem_driver = {  	.disconnect =	usbnet_disconnect,  	.suspend =	usbnet_suspend,  	.resume =	usbnet_resume, +	.disable_hub_initiated_lpm = 1,  }; - -static int __init eem_init(void) -{ -	return usb_register(&eem_driver); -} -module_init(eem_init); - -static void __exit eem_exit(void) -{ -	usb_deregister(&eem_driver); -} -module_exit(eem_exit); +module_usb_driver(eem_driver);  MODULE_AUTHOR("Omar Laazimani <omar.oberthur@gmail.com>");  MODULE_DESCRIPTION("USB CDC EEM"); diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index b3fe0de4046..2a32d9167d3 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -14,15 +14,13 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  // #define	DEBUG			// error path messages, extra info  // #define	VERBOSE			// more; success messages  #include <linux/module.h> -#include <linux/init.h>  #include <linux/netdevice.h>  #include <linux/etherdevice.h>  #include <linux/ethtool.h> @@ -33,7 +31,7 @@  #include <linux/usb/usbnet.h> -#if defined(CONFIG_USB_NET_RNDIS_HOST) || defined(CONFIG_USB_NET_RNDIS_HOST_MODULE) +#if IS_ENABLED(CONFIG_USB_NET_RNDIS_HOST)  static int is_rndis(struct usb_interface_descriptor *desc)  { @@ -69,8 +67,7 @@ static const u8 mbm_guid[16] = {  	0xa6, 0x07, 0xc0, 0xff, 0xcb, 0x7e, 0x39, 0x2a,  }; -/* - * probes control interface, claims data interface, collects the bulk +/* probes control interface, claims data interface, collects the bulk   * endpoints, activates data interface (if needed), maybe sets MTU.   * all pure cdc, except for certain firmware workarounds, and knowing   * that rndis uses one different rule. @@ -83,11 +80,12 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)  	struct cdc_state		*info = (void *) &dev->data;  	int				status;  	int				rndis; +	bool				android_rndis_quirk = false;  	struct usb_driver		*driver = driver_of(intf);  	struct usb_cdc_mdlm_desc	*desc = NULL;  	struct usb_cdc_mdlm_detail_desc *detail = NULL; -	if (sizeof dev->data < sizeof *info) +	if (sizeof(dev->data) < sizeof(*info))  		return -EDOM;  	/* expect strict spec conformance for the descriptors, but @@ -99,9 +97,7 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)  		 */  		buf = dev->udev->actconfig->extra;  		len = dev->udev->actconfig->extralen; -		if (len) -			dev_dbg(&intf->dev, -				"CDC descriptors on config\n"); +		dev_dbg(&intf->dev, "CDC descriptors on config\n");  	}  	/* Maybe CDC descriptors are after the endpoint?  This bug has @@ -127,10 +123,10 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)  		 is_activesync(&intf->cur_altsetting->desc) ||  		 is_wireless_rndis(&intf->cur_altsetting->desc)); -	memset(info, 0, sizeof *info); +	memset(info, 0, sizeof(*info));  	info->control = intf;  	while (len > 3) { -		if (buf [1] != USB_DT_CS_INTERFACE) +		if (buf[1] != USB_DT_CS_INTERFACE)  			goto next_desc;  		/* use bDescriptorSubType to identify the CDC descriptors. @@ -140,14 +136,14 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)  		 * in favor of a complicated OID-based RPC scheme doing what  		 * CDC Ethernet achieves with a simple descriptor.  		 */ -		switch (buf [2]) { +		switch (buf[2]) {  		case USB_CDC_HEADER_TYPE:  			if (info->header) {  				dev_dbg(&intf->dev, "extra CDC header\n");  				goto bad_desc;  			}  			info->header = (void *) buf; -			if (info->header->bLength != sizeof *info->header) { +			if (info->header->bLength != sizeof(*info->header)) {  				dev_dbg(&intf->dev, "CDC header len %u\n",  					info->header->bLength);  				goto bad_desc; @@ -176,7 +172,7 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)  				goto bad_desc;  			}  			info->u = (void *) buf; -			if (info->u->bLength != sizeof *info->u) { +			if (info->u->bLength != sizeof(*info->u)) {  				dev_dbg(&intf->dev, "CDC union len %u\n",  					info->u->bLength);  				goto bad_desc; @@ -197,6 +193,11 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)  					info->control,  					info->u->bSlaveInterface0,  					info->data); +				/* fall back to hard-wiring for RNDIS */ +				if (rndis) { +					android_rndis_quirk = true; +					goto next_desc; +				}  				goto bad_desc;  			}  			if (info->control != intf) { @@ -211,6 +212,10 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)  					goto bad_desc;  			} +			/* some devices merge these - skip class check */ +			if (info->control == info->data) +				goto next_desc; +  			/* a data interface altsetting does the real i/o */  			d = &info->data->cur_altsetting->desc;  			if (d->bInterfaceClass != USB_CLASS_CDC_DATA) { @@ -225,7 +230,7 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)  				goto bad_desc;  			}  			info->ether = (void *) buf; -			if (info->ether->bLength != sizeof *info->ether) { +			if (info->ether->bLength != sizeof(*info->ether)) {  				dev_dbg(&intf->dev, "CDC ether len %u\n",  					info->ether->bLength);  				goto bad_desc; @@ -266,18 +271,22 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)  			break;  		}  next_desc: -		len -= buf [0];	/* bLength */ -		buf += buf [0]; +		len -= buf[0];	/* bLength */ +		buf += buf[0];  	}  	/* Microsoft ActiveSync based and some regular RNDIS devices lack the  	 * CDC descriptors, so we'll hard-wire the interfaces and not check  	 * for descriptors. +	 * +	 * Some Android RNDIS devices have a CDC Union descriptor pointing +	 * to non-existing interfaces.  Ignore that and attempt the same +	 * hard-wired 0 and 1 interfaces.  	 */ -	if (rndis && !info->u) { +	if (rndis && (!info->u || android_rndis_quirk)) {  		info->control = usb_ifnum_to_if(dev->udev, 0);  		info->data = usb_ifnum_to_if(dev->udev, 1); -		if (!info->control || !info->data) { +		if (!info->control || !info->data || info->control != intf) {  			dev_dbg(&intf->dev,  				"rndis: master #0/%p slave #1/%p\n",  				info->control, @@ -296,19 +305,23 @@ next_desc:  	/* claim data interface and set it up ... with side effects.  	 * network traffic can't flow until an altsetting is enabled.  	 */ -	status = usb_driver_claim_interface(driver, info->data, dev); -	if (status < 0) -		return status; +	if (info->data != info->control) { +		status = usb_driver_claim_interface(driver, info->data, dev); +		if (status < 0) +			return status; +	}  	status = usbnet_get_endpoints(dev, info->data);  	if (status < 0) {  		/* ensure immediate exit from usbnet_disconnect */  		usb_set_intfdata(info->data, NULL); -		usb_driver_release_interface(driver, info->data); +		if (info->data != info->control) +			usb_driver_release_interface(driver, info->data);  		return status;  	}  	/* status endpoint: optional for CDC Ethernet, not RNDIS (or ACM) */ -	dev->status = NULL; +	if (info->data != info->control) +		dev->status = NULL;  	if (info->control->cur_altsetting->desc.bNumEndpoints == 1) {  		struct usb_endpoint_descriptor	*desc; @@ -328,6 +341,22 @@ next_desc:  		usb_driver_release_interface(driver, info->data);  		return -ENODEV;  	} + +	/* Some devices don't initialise properly. In particular +	 * the packet filter is not reset. There are devices that +	 * don't do reset all the way. So the packet filter should +	 * be set to a sane initial value. +	 */ +	usb_control_msg(dev->udev, +			usb_sndctrlpipe(dev->udev, 0), +			USB_CDC_SET_ETHERNET_PACKET_FILTER, +			USB_TYPE_CLASS | USB_RECIP_INTERFACE, +			USB_CDC_PACKET_TYPE_ALL_MULTICAST | USB_CDC_PACKET_TYPE_DIRECTED | USB_CDC_PACKET_TYPE_BROADCAST, +			intf->cur_altsetting->desc.bInterfaceNumber, +			NULL, +			0, +			USB_CTRL_SET_TIMEOUT +		);  	return 0;  bad_desc: @@ -341,6 +370,10 @@ void usbnet_cdc_unbind(struct usbnet *dev, struct usb_interface *intf)  	struct cdc_state		*info = (void *) &dev->data;  	struct usb_driver		*driver = driver_of(intf); +	/* combined interface - nothing  to do */ +	if (info->data == info->control) +		return; +  	/* disconnect master --> disconnect slave */  	if (intf == info->control && info->data) {  		/* ensure immediate exit from usbnet_disconnect */ @@ -359,9 +392,7 @@ void usbnet_cdc_unbind(struct usbnet *dev, struct usb_interface *intf)  }  EXPORT_SYMBOL_GPL(usbnet_cdc_unbind); -/*------------------------------------------------------------------------- - * - * Communications Device Class, Ethernet Control model +/* Communications Device Class, Ethernet Control model   *   * Takes two interfaces.  The DATA interface is inactive till an altsetting   * is selected.  Configuration data includes class descriptors.  There's @@ -369,8 +400,7 @@ EXPORT_SYMBOL_GPL(usbnet_cdc_unbind);   *   * This should interop with whatever the 2.4 "CDCEther.c" driver   * (by Brad Hards) talked with, with more functionality. - * - *-------------------------------------------------------------------------*/ + */  static void dumpspeed(struct usbnet *dev, __le32 *speeds)  { @@ -380,11 +410,11 @@ static void dumpspeed(struct usbnet *dev, __le32 *speeds)  		   __le32_to_cpu(speeds[1]) / 1000);  } -static void cdc_status(struct usbnet *dev, struct urb *urb) +void usbnet_cdc_status(struct usbnet *dev, struct urb *urb)  {  	struct usb_cdc_notification	*event; -	if (urb->actual_length < sizeof *event) +	if (urb->actual_length < sizeof(*event))  		return;  	/* SPEED_CHANGE can get split into two 8-byte packets */ @@ -398,15 +428,12 @@ static void cdc_status(struct usbnet *dev, struct urb *urb)  	case USB_CDC_NOTIFY_NETWORK_CONNECTION:  		netif_dbg(dev, timer, dev->net, "CDC: carrier %s\n",  			  event->wValue ? "on" : "off"); -		if (event->wValue) -			netif_carrier_on(dev->net); -		else -			netif_carrier_off(dev->net); +		usbnet_link_change(dev, !!event->wValue, 0);  		break;  	case USB_CDC_NOTIFY_SPEED_CHANGE:	/* tx/rx rates */  		netif_dbg(dev, timer, dev->net, "CDC: speed change (len %d)\n",  			  urb->actual_length); -		if (urb->actual_length != (sizeof *event + 8)) +		if (urb->actual_length != (sizeof(*event) + 8))  			set_bit(EVENT_STS_SPLIT, &dev->flags);  		else  			dumpspeed(dev, (__le32 *) &event[1]); @@ -420,12 +447,16 @@ static void cdc_status(struct usbnet *dev, struct urb *urb)  		break;  	}  } +EXPORT_SYMBOL_GPL(usbnet_cdc_status); -static int cdc_bind(struct usbnet *dev, struct usb_interface *intf) +int usbnet_cdc_bind(struct usbnet *dev, struct usb_interface *intf)  {  	int				status;  	struct cdc_state		*info = (void *) &dev->data; +	BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) +			< sizeof(struct cdc_state))); +  	status = usbnet_generic_cdc_bind(dev, intf);  	if (status < 0)  		return status; @@ -443,38 +474,37 @@ static int cdc_bind(struct usbnet *dev, struct usb_interface *intf)  	 */  	return 0;  } - -static int cdc_manage_power(struct usbnet *dev, int on) -{ -	dev->intf->needs_remote_wakeup = on; -	return 0; -} +EXPORT_SYMBOL_GPL(usbnet_cdc_bind);  static const struct driver_info	cdc_info = {  	.description =	"CDC Ethernet Device", -	.flags =	FLAG_ETHER, -	// .check_connect = cdc_check_connect, -	.bind =		cdc_bind, +	.flags =	FLAG_ETHER | FLAG_POINTTOPOINT, +	.bind =		usbnet_cdc_bind,  	.unbind =	usbnet_cdc_unbind, -	.status =	cdc_status, -	.manage_power =	cdc_manage_power, +	.status =	usbnet_cdc_status, +	.manage_power =	usbnet_manage_power,  }; -static const struct driver_info mbm_info = { +static const struct driver_info wwan_info = {  	.description =	"Mobile Broadband Network Device",  	.flags =	FLAG_WWAN, -	.bind = 	cdc_bind, +	.bind =		usbnet_cdc_bind,  	.unbind =	usbnet_cdc_unbind, -	.status =	cdc_status, -	.manage_power =	cdc_manage_power, +	.status =	usbnet_cdc_status, +	.manage_power =	usbnet_manage_power,  };  /*-------------------------------------------------------------------------*/ +#define HUAWEI_VENDOR_ID	0x12D1 +#define NOVATEL_VENDOR_ID	0x1410 +#define ZTE_VENDOR_ID		0x19D2 +#define DELL_VENDOR_ID		0x413C +#define REALTEK_VENDOR_ID	0x0bda +#define SAMSUNG_VENDOR_ID	0x04e8 -static const struct usb_device_id	products [] = { -/* - * BLACKLIST !! +static const struct usb_device_id	products[] = { +/* BLACKLIST !!   *   * First blacklist any products that are egregiously nonconformant   * with the CDC Ethernet specs.  Minor braindamage we cope with; when @@ -521,7 +551,7 @@ static const struct usb_device_id	products [] = {  	.driver_info		= 0,  }, {  	.match_flags    =   USB_DEVICE_ID_MATCH_INT_INFO -	          | USB_DEVICE_ID_MATCH_DEVICE, +			  | USB_DEVICE_ID_MATCH_DEVICE,  	.idVendor		= 0x04DD,  	.idProduct		= 0x8007,	/* C-700 */  	ZAURUS_MASTER_INTERFACE, @@ -562,8 +592,97 @@ static const struct usb_device_id	products [] = {  	.driver_info		= 0,  }, -/* - * WHITELIST!!! +/* LG Electronics VL600 wants additional headers on every frame */ +{ +	USB_DEVICE_AND_INTERFACE_INFO(0x1004, 0x61aa, USB_CLASS_COMM, +			USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), +	.driver_info = 0, +}, + +/* 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		= 0, +}, + +/* Novatel USB551L and MC551 - handled by qmi_wwan */ +{ +	USB_DEVICE_AND_INTERFACE_INFO(NOVATEL_VENDOR_ID, 0xB001, USB_CLASS_COMM, +			USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), +	.driver_info = 0, +}, + +/* Novatel E362 - handled by qmi_wwan */ +{ +	USB_DEVICE_AND_INTERFACE_INFO(NOVATEL_VENDOR_ID, 0x9010, USB_CLASS_COMM, +			USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), +	.driver_info = 0, +}, + +/* Dell Wireless 5800 (Novatel E362) - handled by qmi_wwan */ +{ +	USB_DEVICE_AND_INTERFACE_INFO(DELL_VENDOR_ID, 0x8195, USB_CLASS_COMM, +			USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), +	.driver_info = 0, +}, + +/* Dell Wireless 5800 (Novatel E362) - handled by qmi_wwan */ +{ +	USB_DEVICE_AND_INTERFACE_INFO(DELL_VENDOR_ID, 0x8196, USB_CLASS_COMM, +			USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), +	.driver_info = 0, +}, + +/* Dell Wireless 5804 (Novatel E371) - handled by qmi_wwan */ +{ +	USB_DEVICE_AND_INTERFACE_INFO(DELL_VENDOR_ID, 0x819b, USB_CLASS_COMM, +			USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), +	.driver_info = 0, +}, + +/* Novatel Expedite E371 - handled by qmi_wwan */ +{ +	USB_DEVICE_AND_INTERFACE_INFO(NOVATEL_VENDOR_ID, 0x9011, USB_CLASS_COMM, +			USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), +	.driver_info = 0, +}, + +/* AnyDATA ADU960S - handled by qmi_wwan */ +{ +	USB_DEVICE_AND_INTERFACE_INFO(0x16d5, 0x650a, USB_CLASS_COMM, +			USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), +	.driver_info = 0, +}, + +/* Huawei E1820 - handled by qmi_wwan */ +{ +	USB_DEVICE_INTERFACE_NUMBER(HUAWEI_VENDOR_ID, 0x14ac, 1), +	.driver_info = 0, +}, + +/* Realtek RTL8152 Based USB 2.0 Ethernet Adapters */ +{ +	USB_DEVICE_AND_INTERFACE_INFO(REALTEK_VENDOR_ID, 0x8152, USB_CLASS_COMM, +			USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), +	.driver_info = 0, +}, + +/* Realtek RTL8153 Based USB 3.0 Ethernet Adapters */ +{ +	USB_DEVICE_AND_INTERFACE_INFO(REALTEK_VENDOR_ID, 0x8153, USB_CLASS_COMM, +			USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), +	.driver_info = 0, +}, + +/* Samsung USB Ethernet Adapters */ +{ +	USB_DEVICE_AND_INTERFACE_INFO(SAMSUNG_VENDOR_ID, 0xa101, USB_CLASS_COMM, +			USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), +	.driver_info = 0, +}, + +/* WHITELIST!!!   *   * CDC Ether uses two interfaces, not necessarily consecutive.   * We match the main interface, ignoring the optional device @@ -574,16 +693,56 @@ static const struct usb_device_id	products [] = {   * because of bugs/quirks in a given product (like Zaurus, above).   */  { +	/* ZTE (Vodafone) K3805-Z */ +	USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1003, USB_CLASS_COMM, +				      USB_CDC_SUBCLASS_ETHERNET, +				      USB_CDC_PROTO_NONE), +	.driver_info = (unsigned long)&wwan_info, +}, { +	/* ZTE (Vodafone) K3806-Z */ +	USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1015, USB_CLASS_COMM, +				      USB_CDC_SUBCLASS_ETHERNET, +				      USB_CDC_PROTO_NONE), +	.driver_info = (unsigned long)&wwan_info, +}, { +	/* ZTE (Vodafone) K4510-Z */ +	USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1173, USB_CLASS_COMM, +				      USB_CDC_SUBCLASS_ETHERNET, +				      USB_CDC_PROTO_NONE), +	.driver_info = (unsigned long)&wwan_info, +}, { +	/* ZTE (Vodafone) K3770-Z */ +	USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1177, USB_CLASS_COMM, +				      USB_CDC_SUBCLASS_ETHERNET, +				      USB_CDC_PROTO_NONE), +	.driver_info = (unsigned long)&wwan_info, +}, { +	/* ZTE (Vodafone) K3772-Z */ +	USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1181, USB_CLASS_COMM, +				      USB_CDC_SUBCLASS_ETHERNET, +				      USB_CDC_PROTO_NONE), +	.driver_info = (unsigned long)&wwan_info, +}, { +	/* Telit modules */ +	USB_VENDOR_AND_INTERFACE_INFO(0x1bc7, USB_CLASS_COMM, +			USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), +	.driver_info = (kernel_ulong_t) &wwan_info, +}, {  	USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ETHERNET,  			USB_CDC_PROTO_NONE),  	.driver_info = (unsigned long) &cdc_info,  }, {  	USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM,  			USB_CDC_PROTO_NONE), -	.driver_info = (unsigned long)&mbm_info, +	.driver_info = (unsigned long)&wwan_info, +}, { +	/* Various Huawei modems with a network port like the UMG1831 */ +	USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_COMM, +				      USB_CDC_SUBCLASS_ETHERNET, 255), +	.driver_info = (unsigned long)&wwan_info,  }, -	{ },		// END +	{ },		/* END */  };  MODULE_DEVICE_TABLE(usb, products); @@ -596,23 +755,10 @@ static struct usb_driver cdc_driver = {  	.resume =	usbnet_resume,  	.reset_resume =	usbnet_resume,  	.supports_autosuspend = 1, +	.disable_hub_initiated_lpm = 1,  }; - -static int __init cdc_init(void) -{ -	BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) -			< sizeof(struct cdc_state))); - - 	return usb_register(&cdc_driver); -} -module_init(cdc_init); - -static void __exit cdc_exit(void) -{ - 	usb_deregister(&cdc_driver); -} -module_exit(cdc_exit); +module_usb_driver(cdc_driver);  MODULE_AUTHOR("David Brownell");  MODULE_DESCRIPTION("USB CDC Ethernet devices"); diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c new file mode 100644 index 00000000000..5ee7a1dbc02 --- /dev/null +++ b/drivers/net/usb/cdc_mbim.c @@ -0,0 +1,625 @@ +/* + * Copyright (c) 2012  Smith Micro Software, Inc. + * Copyright (c) 2012  Bjørn Mork <bjorn@mork.no> + * + * This driver is based on and reuse most of cdc_ncm, which is + * Copyright (C) ST-Ericsson 2010-2012 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/ethtool.h> +#include <linux/if_vlan.h> +#include <linux/ip.h> +#include <linux/mii.h> +#include <linux/usb.h> +#include <linux/usb/cdc.h> +#include <linux/usb/usbnet.h> +#include <linux/usb/cdc-wdm.h> +#include <linux/usb/cdc_ncm.h> +#include <net/ipv6.h> +#include <net/addrconf.h> + +/* alternative VLAN for IP session 0 if not untagged */ +#define MBIM_IPS0_VID	4094 + +/* driver specific data - must match cdc_ncm usage */ +struct cdc_mbim_state { +	struct cdc_ncm_ctx *ctx; +	atomic_t pmcount; +	struct usb_driver *subdriver; +	unsigned long _unused; +	unsigned long flags; +}; + +/* flags for the cdc_mbim_state.flags field */ +enum cdc_mbim_flags { +	FLAG_IPS0_VLAN = 1 << 0,	/* IP session 0 is tagged  */ +}; + +/* using a counter to merge subdriver requests with our own into a combined state */ +static int cdc_mbim_manage_power(struct usbnet *dev, int on) +{ +	struct cdc_mbim_state *info = (void *)&dev->data; +	int rv = 0; + +	dev_dbg(&dev->intf->dev, "%s() pmcount=%d, on=%d\n", __func__, atomic_read(&info->pmcount), on); + +	if ((on && atomic_add_return(1, &info->pmcount) == 1) || (!on && atomic_dec_and_test(&info->pmcount))) { +		/* need autopm_get/put here to ensure the usbcore sees the new value */ +		rv = usb_autopm_get_interface(dev->intf); +		dev->intf->needs_remote_wakeup = on; +		if (!rv) +			usb_autopm_put_interface(dev->intf); +	} +	return 0; +} + +static int cdc_mbim_wdm_manage_power(struct usb_interface *intf, int status) +{ +	struct usbnet *dev = usb_get_intfdata(intf); + +	/* can be called while disconnecting */ +	if (!dev) +		return 0; + +	return cdc_mbim_manage_power(dev, status); +} + +static int cdc_mbim_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid) +{ +	struct usbnet *dev = netdev_priv(netdev); +	struct cdc_mbim_state *info = (void *)&dev->data; + +	/* creation of this VLAN is a request to tag IP session 0 */ +	if (vid == MBIM_IPS0_VID) +		info->flags |= FLAG_IPS0_VLAN; +	else +		if (vid >= 512)	/* we don't map these to MBIM session */ +			return -EINVAL; +	return 0; +} + +static int cdc_mbim_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid) +{ +	struct usbnet *dev = netdev_priv(netdev); +	struct cdc_mbim_state *info = (void *)&dev->data; + +	/* this is a request for an untagged IP session 0 */ +	if (vid == MBIM_IPS0_VID) +		info->flags &= ~FLAG_IPS0_VLAN; +	return 0; +} + +static const struct net_device_ops cdc_mbim_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_vlan_rx_add_vid  = cdc_mbim_rx_add_vid, +	.ndo_vlan_rx_kill_vid = cdc_mbim_rx_kill_vid, +}; + +/* Change the control interface altsetting and update the .driver_info + * pointer if the matching entry after changing class codes points to + * a different struct + */ +static int cdc_mbim_set_ctrlalt(struct usbnet *dev, struct usb_interface *intf, u8 alt) +{ +	struct usb_driver *driver = to_usb_driver(intf->dev.driver); +	const struct usb_device_id *id; +	struct driver_info *info; +	int ret; + +	ret = usb_set_interface(dev->udev, +				intf->cur_altsetting->desc.bInterfaceNumber, +				alt); +	if (ret) +		return ret; + +	id = usb_match_id(intf, driver->id_table); +	if (!id) +		return -ENODEV; + +	info = (struct driver_info *)id->driver_info; +	if (info != dev->driver_info) { +		dev_dbg(&intf->dev, "driver_info updated to '%s'\n", +			info->description); +		dev->driver_info = info; +	} +	return 0; +} + +static int cdc_mbim_bind(struct usbnet *dev, struct usb_interface *intf) +{ +	struct cdc_ncm_ctx *ctx; +	struct usb_driver *subdriver = ERR_PTR(-ENODEV); +	int ret = -ENODEV; +	u8 data_altsetting = 1; +	struct cdc_mbim_state *info = (void *)&dev->data; + +	/* should we change control altsetting on a NCM/MBIM function? */ +	if (cdc_ncm_select_altsetting(intf) == CDC_NCM_COMM_ALTSETTING_MBIM) { +		data_altsetting = CDC_NCM_DATA_ALTSETTING_MBIM; +		ret = cdc_mbim_set_ctrlalt(dev, intf, CDC_NCM_COMM_ALTSETTING_MBIM); +		if (ret) +			goto err; +		ret = -ENODEV; +	} + +	/* we will hit this for NCM/MBIM functions if prefer_mbim is false */ +	if (!cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting)) +		goto err; + +	ret = cdc_ncm_bind_common(dev, intf, data_altsetting); +	if (ret) +		goto err; + +	ctx = info->ctx; + +	/* The MBIM descriptor and the status endpoint are required */ +	if (ctx->mbim_desc && dev->status) +		subdriver = usb_cdc_wdm_register(ctx->control, +						 &dev->status->desc, +						 le16_to_cpu(ctx->mbim_desc->wMaxControlMessage), +						 cdc_mbim_wdm_manage_power); +	if (IS_ERR(subdriver)) { +		ret = PTR_ERR(subdriver); +		cdc_ncm_unbind(dev, intf); +		goto err; +	} + +	/* can't let usbnet use the interrupt endpoint */ +	dev->status = NULL; +	info->subdriver = subdriver; + +	/* MBIM cannot do ARP */ +	dev->net->flags |= IFF_NOARP; + +	/* no need to put the VLAN tci in the packet headers */ +	dev->net->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_FILTER; + +	/* monitor VLAN additions and removals */ +	dev->net->netdev_ops = &cdc_mbim_netdev_ops; +err: +	return ret; +} + +static void cdc_mbim_unbind(struct usbnet *dev, struct usb_interface *intf) +{ +	struct cdc_mbim_state *info = (void *)&dev->data; +	struct cdc_ncm_ctx *ctx = info->ctx; + +	/* disconnect subdriver from control interface */ +	if (info->subdriver && info->subdriver->disconnect) +		info->subdriver->disconnect(ctx->control); +	info->subdriver = NULL; + +	/* let NCM unbind clean up both control and data interface */ +	cdc_ncm_unbind(dev, intf); +} + +/* verify that the ethernet protocol is IPv4 or IPv6 */ +static bool is_ip_proto(__be16 proto) +{ +	switch (proto) { +	case htons(ETH_P_IP): +	case htons(ETH_P_IPV6): +		return true; +	} +	return false; +} + +static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) +{ +	struct sk_buff *skb_out; +	struct cdc_mbim_state *info = (void *)&dev->data; +	struct cdc_ncm_ctx *ctx = info->ctx; +	__le32 sign = cpu_to_le32(USB_CDC_MBIM_NDP16_IPS_SIGN); +	u16 tci = 0; +	bool is_ip; +	u8 *c; + +	if (!ctx) +		goto error; + +	if (skb) { +		if (skb->len <= ETH_HLEN) +			goto error; + +		/* Some applications using e.g. packet sockets will +		 * bypass the VLAN acceleration and create tagged +		 * ethernet frames directly.  We primarily look for +		 * the accelerated out-of-band tag, but fall back if +		 * required +		 */ +		skb_reset_mac_header(skb); +		if (vlan_get_tag(skb, &tci) < 0 && skb->len > VLAN_ETH_HLEN && +		    __vlan_get_tag(skb, &tci) == 0) { +			is_ip = is_ip_proto(vlan_eth_hdr(skb)->h_vlan_encapsulated_proto); +			skb_pull(skb, VLAN_ETH_HLEN); +		} else { +			is_ip = is_ip_proto(eth_hdr(skb)->h_proto); +			skb_pull(skb, ETH_HLEN); +		} + +		/* Is IP session <0> tagged too? */ +		if (info->flags & FLAG_IPS0_VLAN) { +			/* drop all untagged packets */ +			if (!tci) +				goto error; +			/* map MBIM_IPS0_VID to IPS<0> */ +			if (tci == MBIM_IPS0_VID) +				tci = 0; +		} + +		/* mapping VLANs to MBIM sessions: +		 *   no tag     => IPS session <0> if !FLAG_IPS0_VLAN +		 *   1 - 255    => IPS session <vlanid> +		 *   256 - 511  => DSS session <vlanid - 256> +		 *   512 - 4093 => unsupported, drop +		 *   4094       => IPS session <0> if FLAG_IPS0_VLAN +		 */ + +		switch (tci & 0x0f00) { +		case 0x0000: /* VLAN ID 0 - 255 */ +			if (!is_ip) +				goto error; +			c = (u8 *)&sign; +			c[3] = tci; +			break; +		case 0x0100: /* VLAN ID 256 - 511 */ +			if (is_ip) +				goto error; +			sign = cpu_to_le32(USB_CDC_MBIM_NDP16_DSS_SIGN); +			c = (u8 *)&sign; +			c[3] = tci; +			break; +		default: +			netif_err(dev, tx_err, dev->net, +				  "unsupported tci=0x%04x\n", tci); +			goto error; +		} +	} + +	spin_lock_bh(&ctx->mtx); +	skb_out = cdc_ncm_fill_tx_frame(dev, skb, sign); +	spin_unlock_bh(&ctx->mtx); +	return skb_out; + +error: +	if (skb) +		dev_kfree_skb_any(skb); + +	return NULL; +} + +/* Some devices are known to send Neigbor Solicitation messages and + * require Neigbor Advertisement replies.  The IPv6 core will not + * respond since IFF_NOARP is set, so we must handle them ourselves. + */ +static void do_neigh_solicit(struct usbnet *dev, u8 *buf, u16 tci) +{ +	struct ipv6hdr *iph = (void *)buf; +	struct nd_msg *msg = (void *)(iph + 1); +	struct net_device *netdev; +	struct inet6_dev *in6_dev; +	bool is_router; + +	/* we'll only respond to requests from unicast addresses to +	 * our solicited node addresses. +	 */ +	if (!ipv6_addr_is_solict_mult(&iph->daddr) || +	    !(ipv6_addr_type(&iph->saddr) & IPV6_ADDR_UNICAST)) +		return; + +	/* need to send the NA on the VLAN dev, if any */ +	rcu_read_lock(); +	if (tci) { +		netdev = __vlan_find_dev_deep_rcu(dev->net, htons(ETH_P_8021Q), +						  tci); +		if (!netdev) { +			rcu_read_unlock(); +			return; +		} +	} else { +		netdev = dev->net; +	} +	dev_hold(netdev); +	rcu_read_unlock(); + +	in6_dev = in6_dev_get(netdev); +	if (!in6_dev) +		goto out; +	is_router = !!in6_dev->cnf.forwarding; +	in6_dev_put(in6_dev); + +	/* ipv6_stub != NULL if in6_dev_get returned an inet6_dev */ +	ipv6_stub->ndisc_send_na(netdev, NULL, &iph->saddr, &msg->target, +				 is_router /* router */, +				 true /* solicited */, +				 false /* override */, +				 true /* inc_opt */); +out: +	dev_put(netdev); +} + +static bool is_neigh_solicit(u8 *buf, size_t len) +{ +	struct ipv6hdr *iph = (void *)buf; +	struct nd_msg *msg = (void *)(iph + 1); + +	return (len >= sizeof(struct ipv6hdr) + sizeof(struct nd_msg) && +		iph->nexthdr == IPPROTO_ICMPV6 && +		msg->icmph.icmp6_code == 0 && +		msg->icmph.icmp6_type == NDISC_NEIGHBOUR_SOLICITATION); +} + + +static struct sk_buff *cdc_mbim_process_dgram(struct usbnet *dev, u8 *buf, size_t len, u16 tci) +{ +	__be16 proto = htons(ETH_P_802_3); +	struct sk_buff *skb = NULL; + +	if (tci < 256 || tci == MBIM_IPS0_VID) { /* IPS session? */ +		if (len < sizeof(struct iphdr)) +			goto err; + +		switch (*buf & 0xf0) { +		case 0x40: +			proto = htons(ETH_P_IP); +			break; +		case 0x60: +			if (is_neigh_solicit(buf, len)) +				do_neigh_solicit(dev, buf, tci); +			proto = htons(ETH_P_IPV6); +			break; +		default: +			goto err; +		} +	} + +	skb = netdev_alloc_skb_ip_align(dev->net,  len + ETH_HLEN); +	if (!skb) +		goto err; + +	/* add an ethernet header */ +	skb_put(skb, ETH_HLEN); +	skb_reset_mac_header(skb); +	eth_hdr(skb)->h_proto = proto; +	memset(eth_hdr(skb)->h_source, 0, ETH_ALEN); +	memcpy(eth_hdr(skb)->h_dest, dev->net->dev_addr, ETH_ALEN); + +	/* add datagram */ +	memcpy(skb_put(skb, len), buf, len); + +	/* map MBIM session to VLAN */ +	if (tci) +		vlan_put_tag(skb, htons(ETH_P_8021Q), tci); +err: +	return skb; +} + +static int cdc_mbim_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) +{ +	struct sk_buff *skb; +	struct cdc_mbim_state *info = (void *)&dev->data; +	struct cdc_ncm_ctx *ctx = info->ctx; +	int len; +	int nframes; +	int x; +	int offset; +	struct usb_cdc_ncm_ndp16 *ndp16; +	struct usb_cdc_ncm_dpe16 *dpe16; +	int ndpoffset; +	int loopcount = 50; /* arbitrary max preventing infinite loop */ +	u32 payload = 0; +	u8 *c; +	u16 tci; + +	ndpoffset = cdc_ncm_rx_verify_nth16(ctx, skb_in); +	if (ndpoffset < 0) +		goto error; + +next_ndp: +	nframes = cdc_ncm_rx_verify_ndp16(skb_in, ndpoffset); +	if (nframes < 0) +		goto error; + +	ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb_in->data + ndpoffset); + +	switch (ndp16->dwSignature & cpu_to_le32(0x00ffffff)) { +	case cpu_to_le32(USB_CDC_MBIM_NDP16_IPS_SIGN): +		c = (u8 *)&ndp16->dwSignature; +		tci = c[3]; +		/* tag IPS<0> packets too if MBIM_IPS0_VID exists */ +		if (!tci && info->flags & FLAG_IPS0_VLAN) +			tci = MBIM_IPS0_VID; +		break; +	case cpu_to_le32(USB_CDC_MBIM_NDP16_DSS_SIGN): +		c = (u8 *)&ndp16->dwSignature; +		tci = c[3] + 256; +		break; +	default: +		netif_dbg(dev, rx_err, dev->net, +			  "unsupported NDP signature <0x%08x>\n", +			  le32_to_cpu(ndp16->dwSignature)); +		goto err_ndp; + +	} + +	dpe16 = ndp16->dpe16; +	for (x = 0; x < nframes; x++, dpe16++) { +		offset = le16_to_cpu(dpe16->wDatagramIndex); +		len = le16_to_cpu(dpe16->wDatagramLength); + +		/* +		 * CDC NCM ch. 3.7 +		 * All entries after first NULL entry are to be ignored +		 */ +		if ((offset == 0) || (len == 0)) { +			if (!x) +				goto err_ndp; /* empty NTB */ +			break; +		} + +		/* sanity checking */ +		if (((offset + len) > skb_in->len) || (len > ctx->rx_max)) { +			netif_dbg(dev, rx_err, dev->net, +				  "invalid frame detected (ignored) offset[%u]=%u, length=%u, skb=%p\n", +				  x, offset, len, skb_in); +			if (!x) +				goto err_ndp; +			break; +		} else { +			skb = cdc_mbim_process_dgram(dev, skb_in->data + offset, len, tci); +			if (!skb) +				goto error; +			usbnet_skb_return(dev, skb); +			payload += len;	/* count payload bytes in this NTB */ +		} +	} +err_ndp: +	/* are there more NDPs to process? */ +	ndpoffset = le16_to_cpu(ndp16->wNextNdpIndex); +	if (ndpoffset && loopcount--) +		goto next_ndp; + +	/* update stats */ +	ctx->rx_overhead += skb_in->len - payload; +	ctx->rx_ntbs++; + +	return 1; +error: +	return 0; +} + +static int cdc_mbim_suspend(struct usb_interface *intf, pm_message_t message) +{ +	int ret = -ENODEV; +	struct usbnet *dev = usb_get_intfdata(intf); +	struct cdc_mbim_state *info = (void *)&dev->data; +	struct cdc_ncm_ctx *ctx = info->ctx; + +	if (!ctx) +		goto error; + +	/* +	 * Both usbnet_suspend() and subdriver->suspend() MUST return 0 +	 * in system sleep context, otherwise, the resume callback has +	 * to recover device from previous suspend failure. +	 */ +	ret = usbnet_suspend(intf, message); +	if (ret < 0) +		goto error; + +	if (intf == ctx->control && info->subdriver && info->subdriver->suspend) +		ret = info->subdriver->suspend(intf, message); +	if (ret < 0) +		usbnet_resume(intf); + +error: +	return ret; +} + +static int cdc_mbim_resume(struct usb_interface *intf) +{ +	int  ret = 0; +	struct usbnet *dev = usb_get_intfdata(intf); +	struct cdc_mbim_state *info = (void *)&dev->data; +	struct cdc_ncm_ctx *ctx = info->ctx; +	bool callsub = (intf == ctx->control && info->subdriver && info->subdriver->resume); + +	if (callsub) +		ret = info->subdriver->resume(intf); +	if (ret < 0) +		goto err; +	ret = usbnet_resume(intf); +	if (ret < 0 && callsub) +		info->subdriver->suspend(intf, PMSG_SUSPEND); +err: +	return ret; +} + +static const struct driver_info cdc_mbim_info = { +	.description = "CDC MBIM", +	.flags = FLAG_NO_SETINT | FLAG_MULTI_PACKET | FLAG_WWAN, +	.bind = cdc_mbim_bind, +	.unbind = cdc_mbim_unbind, +	.manage_power = cdc_mbim_manage_power, +	.rx_fixup = cdc_mbim_rx_fixup, +	.tx_fixup = cdc_mbim_tx_fixup, +}; + +/* MBIM and NCM devices should not need a ZLP after NTBs with + * dwNtbOutMaxSize length. Nevertheless, a number of devices from + * different vendor IDs will fail unless we send ZLPs, forcing us + * to make this the default. + * + * This default may cause a performance penalty for spec conforming + * devices wanting to take advantage of optimizations possible without + * ZLPs.  A whitelist is added in an attempt to avoid this for devices + * known to conform to the MBIM specification. + * + * All known devices supporting NCM compatibility mode are also + * conforming to the NCM and MBIM specifications. For this reason, the + * NCM subclass entry is also in the ZLP whitelist. + */ +static const struct driver_info cdc_mbim_info_zlp = { +	.description = "CDC MBIM", +	.flags = FLAG_NO_SETINT | FLAG_MULTI_PACKET | FLAG_WWAN | FLAG_SEND_ZLP, +	.bind = cdc_mbim_bind, +	.unbind = cdc_mbim_unbind, +	.manage_power = cdc_mbim_manage_power, +	.rx_fixup = cdc_mbim_rx_fixup, +	.tx_fixup = cdc_mbim_tx_fixup, +}; + +static const struct usb_device_id mbim_devs[] = { +	/* This duplicate NCM entry is intentional. MBIM devices can +	 * be disguised as NCM by default, and this is necessary to +	 * allow us to bind the correct driver_info to such devices. +	 * +	 * bind() will sort out this for us, selecting the correct +	 * entry and reject the other +	 */ +	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE), +	  .driver_info = (unsigned long)&cdc_mbim_info, +	}, +	/* ZLP conformance whitelist: All Ericsson MBIM devices */ +	{ USB_VENDOR_AND_INTERFACE_INFO(0x0bdb, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE), +	  .driver_info = (unsigned long)&cdc_mbim_info, +	}, +	/* default entry */ +	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE), +	  .driver_info = (unsigned long)&cdc_mbim_info_zlp, +	}, +	{ +	}, +}; +MODULE_DEVICE_TABLE(usb, mbim_devs); + +static struct usb_driver cdc_mbim_driver = { +	.name = "cdc_mbim", +	.id_table = mbim_devs, +	.probe = usbnet_probe, +	.disconnect = usbnet_disconnect, +	.suspend = cdc_mbim_suspend, +	.resume = cdc_mbim_resume, +	.reset_resume =	cdc_mbim_resume, +	.supports_autosuspend = 1, +	.disable_hub_initiated_lpm = 1, +}; +module_usb_driver(cdc_mbim_driver); + +MODULE_AUTHOR("Greg Suarez <gsuarez@smithmicro.com>"); +MODULE_AUTHOR("Bjørn Mork <bjorn@mork.no>"); +MODULE_DESCRIPTION("USB CDC MBIM host driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 593c104ab19..80a844e0ae0 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -1,7 +1,7 @@  /*   * cdc_ncm.c   * - * Copyright (C) ST-Ericsson 2010 + * Copyright (C) ST-Ericsson 2010-2012   * Contact: Alexey Orishko <alexey.orishko@stericsson.com>   * Original author: Hans Petter Selasky <hans.petter.selasky@stericsson.com>   * @@ -39,7 +39,6 @@   */  #include <linux/module.h> -#include <linux/init.h>  #include <linux/netdevice.h>  #include <linux/ctype.h>  #include <linux/ethtool.h> @@ -47,214 +46,524 @@  #include <linux/mii.h>  #include <linux/crc32.h>  #include <linux/usb.h> -#include <linux/version.h> -#include <linux/timer.h> -#include <linux/spinlock.h> +#include <linux/hrtimer.h>  #include <linux/atomic.h>  #include <linux/usb/usbnet.h>  #include <linux/usb/cdc.h> +#include <linux/usb/cdc_ncm.h> + +#if IS_ENABLED(CONFIG_USB_NET_CDC_MBIM) +static bool prefer_mbim = true; +#else +static bool prefer_mbim; +#endif +module_param(prefer_mbim, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(prefer_mbim, "Prefer MBIM setting on dual NCM/MBIM functions"); + +static void cdc_ncm_txpath_bh(unsigned long param); +static void cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx); +static enum hrtimer_restart cdc_ncm_tx_timer_cb(struct hrtimer *hr_timer); +static struct usb_driver cdc_ncm_driver; -#define	DRIVER_VERSION				"30-Nov-2010" +struct cdc_ncm_stats { +	char stat_string[ETH_GSTRING_LEN]; +	int sizeof_stat; +	int stat_offset; +}; -/* CDC NCM subclass 3.2.1 */ -#define USB_CDC_NCM_NDP16_LENGTH_MIN		0x10 +#define CDC_NCM_STAT(str, m) { \ +		.stat_string = str, \ +		.sizeof_stat = sizeof(((struct cdc_ncm_ctx *)0)->m), \ +		.stat_offset = offsetof(struct cdc_ncm_ctx, m) } +#define CDC_NCM_SIMPLE_STAT(m)	CDC_NCM_STAT(__stringify(m), m) + +static const struct cdc_ncm_stats cdc_ncm_gstrings_stats[] = { +	CDC_NCM_SIMPLE_STAT(tx_reason_ntb_full), +	CDC_NCM_SIMPLE_STAT(tx_reason_ndp_full), +	CDC_NCM_SIMPLE_STAT(tx_reason_timeout), +	CDC_NCM_SIMPLE_STAT(tx_reason_max_datagram), +	CDC_NCM_SIMPLE_STAT(tx_overhead), +	CDC_NCM_SIMPLE_STAT(tx_ntbs), +	CDC_NCM_SIMPLE_STAT(rx_overhead), +	CDC_NCM_SIMPLE_STAT(rx_ntbs), +}; -/* Maximum NTB length */ -#define	CDC_NCM_NTB_MAX_SIZE_TX			16384	/* bytes */ -#define	CDC_NCM_NTB_MAX_SIZE_RX			16384	/* bytes */ +static int cdc_ncm_get_sset_count(struct net_device __always_unused *netdev, int sset) +{ +	switch (sset) { +	case ETH_SS_STATS: +		return ARRAY_SIZE(cdc_ncm_gstrings_stats); +	default: +		return -EOPNOTSUPP; +	} +} -/* Minimum value for MaxDatagramSize, ch. 6.2.9 */ -#define	CDC_NCM_MIN_DATAGRAM_SIZE		1514	/* bytes */ +static void cdc_ncm_get_ethtool_stats(struct net_device *netdev, +				    struct ethtool_stats __always_unused *stats, +				    u64 *data) +{ +	struct usbnet *dev = netdev_priv(netdev); +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; +	int i; +	char *p = NULL; -#define	CDC_NCM_MIN_TX_PKT			512	/* bytes */ +	for (i = 0; i < ARRAY_SIZE(cdc_ncm_gstrings_stats); i++) { +		p = (char *)ctx + cdc_ncm_gstrings_stats[i].stat_offset; +		data[i] = (cdc_ncm_gstrings_stats[i].sizeof_stat == sizeof(u64)) ? *(u64 *)p : *(u32 *)p; +	} +} -/* Default value for MaxDatagramSize */ -#define	CDC_NCM_MAX_DATAGRAM_SIZE		2048	/* bytes */ +static void cdc_ncm_get_strings(struct net_device __always_unused *netdev, u32 stringset, u8 *data) +{ +	u8 *p = data; +	int i; + +	switch (stringset) { +	case ETH_SS_STATS: +		for (i = 0; i < ARRAY_SIZE(cdc_ncm_gstrings_stats); i++) { +			memcpy(p, cdc_ncm_gstrings_stats[i].stat_string, ETH_GSTRING_LEN); +			p += ETH_GSTRING_LEN; +		} +	} +} -/* - * Maximum amount of datagrams in NCM Datagram Pointer Table, not counting - * the last NULL entry. Any additional datagrams in NTB would be discarded. - */ -#define	CDC_NCM_DPT_DATAGRAMS_MAX		32 +static void cdc_ncm_update_rxtx_max(struct usbnet *dev, u32 new_rx, u32 new_tx); + +static const struct ethtool_ops cdc_ncm_ethtool_ops = { +	.get_settings      = usbnet_get_settings, +	.set_settings      = usbnet_set_settings, +	.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_ts_info       = ethtool_op_get_ts_info, +	.get_sset_count    = cdc_ncm_get_sset_count, +	.get_strings       = cdc_ncm_get_strings, +	.get_ethtool_stats = cdc_ncm_get_ethtool_stats, +}; -/* Restart the timer, if amount of datagrams is less than given value */ -#define	CDC_NCM_RESTART_TIMER_DATAGRAM_CNT	3 +static u32 cdc_ncm_check_rx_max(struct usbnet *dev, u32 new_rx) +{ +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; +	u32 val, max, min; -/* The following macro defines the minimum header space */ -#define	CDC_NCM_MIN_HDR_SIZE \ -	(sizeof(struct usb_cdc_ncm_nth16) + sizeof(struct usb_cdc_ncm_ndp16) + \ -	(CDC_NCM_DPT_DATAGRAMS_MAX + 1) * sizeof(struct usb_cdc_ncm_dpe16)) +	/* clamp new_rx to sane values */ +	min = USB_CDC_NCM_NTB_MIN_IN_SIZE; +	max = min_t(u32, CDC_NCM_NTB_MAX_SIZE_RX, le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize)); -struct connection_speed_change { -	__le32	USBitRate; /* holds 3GPP downlink value, bits per second */ -	__le32	DSBitRate; /* holds 3GPP uplink value, bits per second */ -} __attribute__ ((packed)); +	/* dwNtbInMaxSize spec violation? Use MIN size for both limits */ +	if (max < min) { +		dev_warn(&dev->intf->dev, "dwNtbInMaxSize=%u is too small. Using %u\n", +			 le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize), min); +		max = min; +	} -struct cdc_ncm_data { -	struct usb_cdc_ncm_nth16 nth16; -	struct usb_cdc_ncm_ndp16 ndp16; -	struct usb_cdc_ncm_dpe16 dpe16[CDC_NCM_DPT_DATAGRAMS_MAX + 1]; -}; +	val = clamp_t(u32, new_rx, min, max); +	if (val != new_rx) +		dev_dbg(&dev->intf->dev, "rx_max must be in the [%u, %u] range\n", min, max); -struct cdc_ncm_ctx { -	struct cdc_ncm_data rx_ncm; -	struct cdc_ncm_data tx_ncm; -	struct usb_cdc_ncm_ntb_parameters ncm_parm; -	struct timer_list tx_timer; - -	const struct usb_cdc_ncm_desc *func_desc; -	const struct usb_cdc_header_desc *header_desc; -	const struct usb_cdc_union_desc *union_desc; -	const struct usb_cdc_ether_desc *ether_desc; - -	struct net_device *netdev; -	struct usb_device *udev; -	struct usb_host_endpoint *in_ep; -	struct usb_host_endpoint *out_ep; -	struct usb_host_endpoint *status_ep; -	struct usb_interface *intf; -	struct usb_interface *control; -	struct usb_interface *data; - -	struct sk_buff *tx_curr_skb; -	struct sk_buff *tx_rem_skb; - -	spinlock_t mtx; - -	u32 tx_timer_pending; -	u32 tx_curr_offset; -	u32 tx_curr_last_offset; -	u32 tx_curr_frame_num; -	u32 rx_speed; -	u32 tx_speed; -	u32 rx_max; -	u32 tx_max; -	u32 max_datagram_size; -	u16 tx_max_datagrams; -	u16 tx_remainder; -	u16 tx_modulus; -	u16 tx_ndp_modulus; -	u16 tx_seq; -	u16 connected; -	u8 data_claimed; -	u8 control_claimed; -}; +	return val; +} -static void cdc_ncm_tx_timeout(unsigned long arg); -static const struct driver_info cdc_ncm_info; -static struct usb_driver cdc_ncm_driver; -static struct ethtool_ops cdc_ncm_ethtool_ops; +static u32 cdc_ncm_check_tx_max(struct usbnet *dev, u32 new_tx) +{ +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; +	u32 val, max, min; -static const struct usb_device_id cdc_devs[] = { -	{ USB_INTERFACE_INFO(USB_CLASS_COMM, -		USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE), -		.driver_info = (unsigned long)&cdc_ncm_info, -	}, -	{ -	}, -}; +	/* clamp new_tx to sane values */ +	min = ctx->max_datagram_size + ctx->max_ndp_size + sizeof(struct usb_cdc_ncm_nth16); +	max = min_t(u32, CDC_NCM_NTB_MAX_SIZE_TX, le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize)); -MODULE_DEVICE_TABLE(usb, cdc_devs); +	/* some devices set dwNtbOutMaxSize too low for the above default */ +	min = min(min, max); -static void -cdc_ncm_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) +	val = clamp_t(u32, new_tx, min, max); +	if (val != new_tx) +		dev_dbg(&dev->intf->dev, "tx_max must be in the [%u, %u] range\n", min, max); + +	return val; +} + +static ssize_t cdc_ncm_show_min_tx_pkt(struct device *d, struct device_attribute *attr, char *buf)  { -	struct usbnet *dev = netdev_priv(net); +	struct usbnet *dev = netdev_priv(to_net_dev(d)); +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; -	strncpy(info->driver, dev->driver_name, sizeof(info->driver)); -	strncpy(info->version, DRIVER_VERSION, sizeof(info->version)); -	strncpy(info->fw_version, dev->driver_info->description, -		sizeof(info->fw_version)); -	usb_make_path(dev->udev, info->bus_info, sizeof(info->bus_info)); +	return sprintf(buf, "%u\n", ctx->min_tx_pkt);  } -static int -cdc_ncm_do_request(struct cdc_ncm_ctx *ctx, struct usb_cdc_notification *req, -		   void *data, u16 flags, u16 *actlen, u16 timeout) +static ssize_t cdc_ncm_show_rx_max(struct device *d, struct device_attribute *attr, char *buf)  { -	int err; +	struct usbnet *dev = netdev_priv(to_net_dev(d)); +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; -	err = usb_control_msg(ctx->udev, (req->bmRequestType & USB_DIR_IN) ? -				usb_rcvctrlpipe(ctx->udev, 0) : -				usb_sndctrlpipe(ctx->udev, 0), -				req->bNotificationType, req->bmRequestType, -				req->wValue, -				req->wIndex, data, -				req->wLength, timeout); +	return sprintf(buf, "%u\n", ctx->rx_max); +} -	if (err < 0) { -		if (actlen) -			*actlen = 0; -		return err; +static ssize_t cdc_ncm_show_tx_max(struct device *d, struct device_attribute *attr, char *buf) +{ +	struct usbnet *dev = netdev_priv(to_net_dev(d)); +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + +	return sprintf(buf, "%u\n", ctx->tx_max); +} + +static ssize_t cdc_ncm_show_tx_timer_usecs(struct device *d, struct device_attribute *attr, char *buf) +{ +	struct usbnet *dev = netdev_priv(to_net_dev(d)); +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + +	return sprintf(buf, "%u\n", ctx->timer_interval / (u32)NSEC_PER_USEC); +} + +static ssize_t cdc_ncm_store_min_tx_pkt(struct device *d,  struct device_attribute *attr, const char *buf, size_t len) +{ +	struct usbnet *dev = netdev_priv(to_net_dev(d)); +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; +	unsigned long val; + +	/* no need to restrict values - anything from 0 to infinity is OK */ +	if (kstrtoul(buf, 0, &val)) +		return -EINVAL; + +	ctx->min_tx_pkt = val; +	return len; +} + +static ssize_t cdc_ncm_store_rx_max(struct device *d,  struct device_attribute *attr, const char *buf, size_t len) +{ +	struct usbnet *dev = netdev_priv(to_net_dev(d)); +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; +	unsigned long val; + +	if (kstrtoul(buf, 0, &val) || cdc_ncm_check_rx_max(dev, val) != val) +		return -EINVAL; + +	cdc_ncm_update_rxtx_max(dev, val, ctx->tx_max); +	return len; +} + +static ssize_t cdc_ncm_store_tx_max(struct device *d,  struct device_attribute *attr, const char *buf, size_t len) +{ +	struct usbnet *dev = netdev_priv(to_net_dev(d)); +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; +	unsigned long val; + +	if (kstrtoul(buf, 0, &val) || cdc_ncm_check_tx_max(dev, val) != val) +		return -EINVAL; + +	cdc_ncm_update_rxtx_max(dev, ctx->rx_max, val); +	return len; +} + +static ssize_t cdc_ncm_store_tx_timer_usecs(struct device *d,  struct device_attribute *attr, const char *buf, size_t len) +{ +	struct usbnet *dev = netdev_priv(to_net_dev(d)); +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; +	ssize_t ret; +	unsigned long val; + +	ret = kstrtoul(buf, 0, &val); +	if (ret) +		return ret; +	if (val && (val < CDC_NCM_TIMER_INTERVAL_MIN || val > CDC_NCM_TIMER_INTERVAL_MAX)) +		return -EINVAL; + +	spin_lock_bh(&ctx->mtx); +	ctx->timer_interval = val * NSEC_PER_USEC; +	if (!ctx->timer_interval) +		ctx->tx_timer_pending = 0; +	spin_unlock_bh(&ctx->mtx); +	return len; +} + +static DEVICE_ATTR(min_tx_pkt, S_IRUGO | S_IWUSR, cdc_ncm_show_min_tx_pkt, cdc_ncm_store_min_tx_pkt); +static DEVICE_ATTR(rx_max, S_IRUGO | S_IWUSR, cdc_ncm_show_rx_max, cdc_ncm_store_rx_max); +static DEVICE_ATTR(tx_max, S_IRUGO | S_IWUSR, cdc_ncm_show_tx_max, cdc_ncm_store_tx_max); +static DEVICE_ATTR(tx_timer_usecs, S_IRUGO | S_IWUSR, cdc_ncm_show_tx_timer_usecs, cdc_ncm_store_tx_timer_usecs); + +#define NCM_PARM_ATTR(name, format, tocpu)				\ +static ssize_t cdc_ncm_show_##name(struct device *d, struct device_attribute *attr, char *buf) \ +{ \ +	struct usbnet *dev = netdev_priv(to_net_dev(d)); \ +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; \ +	return sprintf(buf, format "\n", tocpu(ctx->ncm_parm.name));	\ +} \ +static DEVICE_ATTR(name, S_IRUGO, cdc_ncm_show_##name, NULL) + +NCM_PARM_ATTR(bmNtbFormatsSupported, "0x%04x", le16_to_cpu); +NCM_PARM_ATTR(dwNtbInMaxSize, "%u", le32_to_cpu); +NCM_PARM_ATTR(wNdpInDivisor, "%u", le16_to_cpu); +NCM_PARM_ATTR(wNdpInPayloadRemainder, "%u", le16_to_cpu); +NCM_PARM_ATTR(wNdpInAlignment, "%u", le16_to_cpu); +NCM_PARM_ATTR(dwNtbOutMaxSize, "%u", le32_to_cpu); +NCM_PARM_ATTR(wNdpOutDivisor, "%u", le16_to_cpu); +NCM_PARM_ATTR(wNdpOutPayloadRemainder, "%u", le16_to_cpu); +NCM_PARM_ATTR(wNdpOutAlignment, "%u", le16_to_cpu); +NCM_PARM_ATTR(wNtbOutMaxDatagrams, "%u", le16_to_cpu); + +static struct attribute *cdc_ncm_sysfs_attrs[] = { +	&dev_attr_min_tx_pkt.attr, +	&dev_attr_rx_max.attr, +	&dev_attr_tx_max.attr, +	&dev_attr_tx_timer_usecs.attr, +	&dev_attr_bmNtbFormatsSupported.attr, +	&dev_attr_dwNtbInMaxSize.attr, +	&dev_attr_wNdpInDivisor.attr, +	&dev_attr_wNdpInPayloadRemainder.attr, +	&dev_attr_wNdpInAlignment.attr, +	&dev_attr_dwNtbOutMaxSize.attr, +	&dev_attr_wNdpOutDivisor.attr, +	&dev_attr_wNdpOutPayloadRemainder.attr, +	&dev_attr_wNdpOutAlignment.attr, +	&dev_attr_wNtbOutMaxDatagrams.attr, +	NULL, +}; + +static struct attribute_group cdc_ncm_sysfs_attr_group = { +	.name = "cdc_ncm", +	.attrs = cdc_ncm_sysfs_attrs, +}; + +/* handle rx_max and tx_max changes */ +static void cdc_ncm_update_rxtx_max(struct usbnet *dev, u32 new_rx, u32 new_tx) +{ +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; +	u8 iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber; +	u32 val; + +	val = cdc_ncm_check_rx_max(dev, new_rx); + +	/* inform device about NTB input size changes */ +	if (val != ctx->rx_max) { +		__le32 dwNtbInMaxSize = cpu_to_le32(val); + +		dev_info(&dev->intf->dev, "setting rx_max = %u\n", val); + +		/* tell device to use new size */ +		if (usbnet_write_cmd(dev, USB_CDC_SET_NTB_INPUT_SIZE, +				     USB_TYPE_CLASS | USB_DIR_OUT +				     | USB_RECIP_INTERFACE, +				     0, iface_no, &dwNtbInMaxSize, 4) < 0) +			dev_dbg(&dev->intf->dev, "Setting NTB Input Size failed\n"); +		else +			ctx->rx_max = val; +	} + +	/* usbnet use these values for sizing rx queues */ +	if (dev->rx_urb_size != ctx->rx_max) { +		dev->rx_urb_size = ctx->rx_max; +		if (netif_running(dev->net)) +			usbnet_unlink_rx_urbs(dev); +	} + +	val = cdc_ncm_check_tx_max(dev, new_tx); +	if (val != ctx->tx_max) +		dev_info(&dev->intf->dev, "setting tx_max = %u\n", val); + +	/* Adding a pad byte here if necessary simplifies the handling +	 * in cdc_ncm_fill_tx_frame, making tx_max always represent +	 * the real skb max size. +	 * +	 * We cannot use dev->maxpacket here because this is called from +	 * .bind which is called before usbnet sets up dev->maxpacket +	 */ +	if (val != le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize) && +	    val % usb_maxpacket(dev->udev, dev->out, 1) == 0) +		val++; + +	/* we might need to flush any pending tx buffers if running */ +	if (netif_running(dev->net) && val > ctx->tx_max) { +		netif_tx_lock_bh(dev->net); +		usbnet_start_xmit(NULL, dev->net); +		/* make sure tx_curr_skb is reallocated if it was empty */ +		if (ctx->tx_curr_skb) { +			dev_kfree_skb_any(ctx->tx_curr_skb); +			ctx->tx_curr_skb = NULL; +		} +		ctx->tx_max = val; +		netif_tx_unlock_bh(dev->net); +	} else { +		ctx->tx_max = val;  	} -	if (actlen) -		*actlen = err; +	dev->hard_mtu = ctx->tx_max; + +	/* max qlen depend on hard_mtu and rx_urb_size */ +	usbnet_update_max_qlen(dev); + +	/* never pad more than 3 full USB packets per transfer */ +	ctx->min_tx_pkt = clamp_t(u16, ctx->tx_max - 3 * usb_maxpacket(dev->udev, dev->out, 1), +				  CDC_NCM_MIN_TX_PKT, ctx->tx_max); +} + +/* helpers for NCM and MBIM differences */ +static u8 cdc_ncm_flags(struct usbnet *dev) +{ +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; +	if (cdc_ncm_comm_intf_is_mbim(dev->intf->cur_altsetting) && ctx->mbim_desc) +		return ctx->mbim_desc->bmNetworkCapabilities; +	if (ctx->func_desc) +		return ctx->func_desc->bmNetworkCapabilities;  	return 0;  } -static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx) +static int cdc_ncm_eth_hlen(struct usbnet *dev)  { -	struct usb_cdc_notification req; -	u32 val; -	__le16 max_datagram_size; -	u8 flags; -	u8 iface_no; +	if (cdc_ncm_comm_intf_is_mbim(dev->intf->cur_altsetting)) +		return 0; +	return ETH_HLEN; +} + +static u32 cdc_ncm_min_dgram_size(struct usbnet *dev) +{ +	if (cdc_ncm_comm_intf_is_mbim(dev->intf->cur_altsetting)) +		return CDC_MBIM_MIN_DATAGRAM_SIZE; +	return CDC_NCM_MIN_DATAGRAM_SIZE; +} + +static u32 cdc_ncm_max_dgram_size(struct usbnet *dev) +{ +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + +	if (cdc_ncm_comm_intf_is_mbim(dev->intf->cur_altsetting) && ctx->mbim_desc) +		return le16_to_cpu(ctx->mbim_desc->wMaxSegmentSize); +	if (ctx->ether_desc) +		return le16_to_cpu(ctx->ether_desc->wMaxSegmentSize); +	return CDC_NCM_MAX_DATAGRAM_SIZE; +} + +/* initial one-time device setup.  MUST be called with the data interface + * in altsetting 0 + */ +static int cdc_ncm_init(struct usbnet *dev) +{ +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; +	u8 iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber;  	int err; -	iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber; +	err = usbnet_read_cmd(dev, USB_CDC_GET_NTB_PARAMETERS, +			      USB_TYPE_CLASS | USB_DIR_IN +			      |USB_RECIP_INTERFACE, +			      0, iface_no, &ctx->ncm_parm, +			      sizeof(ctx->ncm_parm)); +	if (err < 0) { +		dev_err(&dev->intf->dev, "failed GET_NTB_PARAMETERS\n"); +		return err; /* GET_NTB_PARAMETERS is required */ +	} -	req.bmRequestType = USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE; -	req.bNotificationType = USB_CDC_GET_NTB_PARAMETERS; -	req.wValue = 0; -	req.wIndex = cpu_to_le16(iface_no); -	req.wLength = cpu_to_le16(sizeof(ctx->ncm_parm)); +	/* set CRC Mode */ +	if (cdc_ncm_flags(dev) & USB_CDC_NCM_NCAP_CRC_MODE) { +		dev_dbg(&dev->intf->dev, "Setting CRC mode off\n"); +		err = usbnet_write_cmd(dev, USB_CDC_SET_CRC_MODE, +				       USB_TYPE_CLASS | USB_DIR_OUT +				       | USB_RECIP_INTERFACE, +				       USB_CDC_NCM_CRC_NOT_APPENDED, +				       iface_no, NULL, 0); +		if (err < 0) +			dev_err(&dev->intf->dev, "SET_CRC_MODE failed\n"); +	} -	err = cdc_ncm_do_request(ctx, &req, &ctx->ncm_parm, 0, NULL, 1000); -	if (err) { -		pr_debug("failed GET_NTB_PARAMETERS\n"); -		return 1; +	/* set NTB format, if both formats are supported. +	 * +	 * "The host shall only send this command while the NCM Data +	 *  Interface is in alternate setting 0." +	 */ +	if (le16_to_cpu(ctx->ncm_parm.bmNtbFormatsSupported) & +						USB_CDC_NCM_NTB32_SUPPORTED) { +		dev_dbg(&dev->intf->dev, "Setting NTB format to 16-bit\n"); +		err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_FORMAT, +				       USB_TYPE_CLASS | USB_DIR_OUT +				       | USB_RECIP_INTERFACE, +				       USB_CDC_NCM_NTB16_FORMAT, +				       iface_no, NULL, 0); +		if (err < 0) +			dev_err(&dev->intf->dev, "SET_NTB_FORMAT failed\n");  	} -	/* read correct set of parameters according to device mode */ +	/* set initial device values */  	ctx->rx_max = le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize);  	ctx->tx_max = le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize);  	ctx->tx_remainder = le16_to_cpu(ctx->ncm_parm.wNdpOutPayloadRemainder);  	ctx->tx_modulus = le16_to_cpu(ctx->ncm_parm.wNdpOutDivisor);  	ctx->tx_ndp_modulus = le16_to_cpu(ctx->ncm_parm.wNdpOutAlignment); +	/* devices prior to NCM Errata shall set this field to zero */ +	ctx->tx_max_datagrams = le16_to_cpu(ctx->ncm_parm.wNtbOutMaxDatagrams); -	if (ctx->func_desc != NULL) -		flags = ctx->func_desc->bmNetworkCapabilities; -	else -		flags = 0; - -	pr_debug("dwNtbInMaxSize=%u dwNtbOutMaxSize=%u " -		 "wNdpOutPayloadRemainder=%u wNdpOutDivisor=%u " -		 "wNdpOutAlignment=%u flags=0x%x\n", -		 ctx->rx_max, ctx->tx_max, ctx->tx_remainder, ctx->tx_modulus, -		 ctx->tx_ndp_modulus, flags); - -	/* max count of tx datagrams without terminating NULL entry */ -	ctx->tx_max_datagrams = CDC_NCM_DPT_DATAGRAMS_MAX; - -	/* verify maximum size of received NTB in bytes */ -	if ((ctx->rx_max < -	    (CDC_NCM_MIN_HDR_SIZE + CDC_NCM_MIN_DATAGRAM_SIZE)) || -	    (ctx->rx_max > CDC_NCM_NTB_MAX_SIZE_RX)) { -		pr_debug("Using default maximum receive length=%d\n", -						CDC_NCM_NTB_MAX_SIZE_RX); -		ctx->rx_max = CDC_NCM_NTB_MAX_SIZE_RX; +	dev_dbg(&dev->intf->dev, +		"dwNtbInMaxSize=%u dwNtbOutMaxSize=%u wNdpOutPayloadRemainder=%u wNdpOutDivisor=%u wNdpOutAlignment=%u wNtbOutMaxDatagrams=%u flags=0x%x\n", +		ctx->rx_max, ctx->tx_max, ctx->tx_remainder, ctx->tx_modulus, +		ctx->tx_ndp_modulus, ctx->tx_max_datagrams, cdc_ncm_flags(dev)); + +	/* max count of tx datagrams */ +	if ((ctx->tx_max_datagrams == 0) || +			(ctx->tx_max_datagrams > CDC_NCM_DPT_DATAGRAMS_MAX)) +		ctx->tx_max_datagrams = CDC_NCM_DPT_DATAGRAMS_MAX; + +	/* set up maximum NDP size */ +	ctx->max_ndp_size = sizeof(struct usb_cdc_ncm_ndp16) + (ctx->tx_max_datagrams + 1) * sizeof(struct usb_cdc_ncm_dpe16); + +	/* initial coalescing timer interval */ +	ctx->timer_interval = CDC_NCM_TIMER_INTERVAL_USEC * NSEC_PER_USEC; + +	return 0; +} + +/* set a new max datagram size */ +static void cdc_ncm_set_dgram_size(struct usbnet *dev, int new_size) +{ +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; +	u8 iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber; +	__le16 max_datagram_size; +	u16 mbim_mtu; +	int err; + +	/* set default based on descriptors */ +	ctx->max_datagram_size = clamp_t(u32, new_size, +					 cdc_ncm_min_dgram_size(dev), +					 CDC_NCM_MAX_DATAGRAM_SIZE); + +	/* inform the device about the selected Max Datagram Size? */ +	if (!(cdc_ncm_flags(dev) & USB_CDC_NCM_NCAP_MAX_DATAGRAM_SIZE)) +		goto out; + +	/* read current mtu value from device */ +	err = usbnet_read_cmd(dev, USB_CDC_GET_MAX_DATAGRAM_SIZE, +			      USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE, +			      0, iface_no, &max_datagram_size, 2); +	if (err < 0) { +		dev_dbg(&dev->intf->dev, "GET_MAX_DATAGRAM_SIZE failed\n"); +		goto out;  	} -	/* verify maximum size of transmitted NTB in bytes */ -	if ((ctx->tx_max < -	    (CDC_NCM_MIN_HDR_SIZE + CDC_NCM_MIN_DATAGRAM_SIZE)) || -	    (ctx->tx_max > CDC_NCM_NTB_MAX_SIZE_TX)) { -		pr_debug("Using default maximum transmit length=%d\n", -						CDC_NCM_NTB_MAX_SIZE_TX); -		ctx->tx_max = CDC_NCM_NTB_MAX_SIZE_TX; +	if (le16_to_cpu(max_datagram_size) == ctx->max_datagram_size) +		goto out; + +	max_datagram_size = cpu_to_le16(ctx->max_datagram_size); +	err = usbnet_write_cmd(dev, USB_CDC_SET_MAX_DATAGRAM_SIZE, +			       USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE, +			       0, iface_no, &max_datagram_size, 2); +	if (err < 0) +		dev_dbg(&dev->intf->dev, "SET_MAX_DATAGRAM_SIZE failed\n"); + +out: +	/* set MTU to max supported by the device if necessary */ +	dev->net->mtu = min_t(int, dev->net->mtu, ctx->max_datagram_size - cdc_ncm_eth_hlen(dev)); + +	/* do not exceed operater preferred MTU */ +	if (ctx->mbim_extended_desc) { +		mbim_mtu = le16_to_cpu(ctx->mbim_extended_desc->wMTU); +		if (mbim_mtu != 0 && mbim_mtu < dev->net->mtu) +			dev->net->mtu = mbim_mtu;  	} +} + +static void cdc_ncm_fix_modulus(struct usbnet *dev) +{ +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; +	u32 val;  	/*  	 * verify that the structure alignment is: @@ -266,7 +575,7 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)  	if ((val < USB_CDC_NCM_NDP_ALIGN_MIN_SIZE) ||  	    (val != ((-val) & val)) || (val >= ctx->tx_max)) { -		pr_debug("Using default alignment: 4 bytes\n"); +		dev_dbg(&dev->intf->dev, "Using default alignment: 4 bytes\n");  		ctx->tx_ndp_modulus = USB_CDC_NCM_NDP_ALIGN_MIN_SIZE;  	} @@ -280,76 +589,49 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)  	if ((val < USB_CDC_NCM_NDP_ALIGN_MIN_SIZE) ||  	    (val != ((-val) & val)) || (val >= ctx->tx_max)) { -		pr_debug("Using default transmit modulus: 4 bytes\n"); +		dev_dbg(&dev->intf->dev, "Using default transmit modulus: 4 bytes\n");  		ctx->tx_modulus = USB_CDC_NCM_NDP_ALIGN_MIN_SIZE;  	}  	/* verify the payload remainder */  	if (ctx->tx_remainder >= ctx->tx_modulus) { -		pr_debug("Using default transmit remainder: 0 bytes\n"); +		dev_dbg(&dev->intf->dev, "Using default transmit remainder: 0 bytes\n");  		ctx->tx_remainder = 0;  	}  	/* adjust TX-remainder according to NCM specification. */ -	ctx->tx_remainder = ((ctx->tx_remainder - ETH_HLEN) & -						(ctx->tx_modulus - 1)); +	ctx->tx_remainder = ((ctx->tx_remainder - cdc_ncm_eth_hlen(dev)) & +			     (ctx->tx_modulus - 1)); +} -	/* additional configuration */ +static int cdc_ncm_setup(struct usbnet *dev) +{ +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; +	u32 def_rx, def_tx; -	/* set CRC Mode */ -	req.bmRequestType = USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE; -	req.bNotificationType = USB_CDC_SET_CRC_MODE; -	req.wValue = cpu_to_le16(USB_CDC_NCM_CRC_NOT_APPENDED); -	req.wIndex = cpu_to_le16(iface_no); -	req.wLength = 0; - -	err = cdc_ncm_do_request(ctx, &req, NULL, 0, NULL, 1000); -	if (err) -		pr_debug("Setting CRC mode off failed\n"); - -	/* set NTB format */ -	req.bmRequestType = USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE; -	req.bNotificationType = USB_CDC_SET_NTB_FORMAT; -	req.wValue = cpu_to_le16(USB_CDC_NCM_NTB16_FORMAT); -	req.wIndex = cpu_to_le16(iface_no); -	req.wLength = 0; - -	err = cdc_ncm_do_request(ctx, &req, NULL, 0, NULL, 1000); -	if (err) -		pr_debug("Setting NTB format to 16-bit failed\n"); - -	/* set Max Datagram Size (MTU) */ -	req.bmRequestType = USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE; -	req.bNotificationType = USB_CDC_GET_MAX_DATAGRAM_SIZE; -	req.wValue = 0; -	req.wIndex = cpu_to_le16(iface_no); -	req.wLength = cpu_to_le16(2); - -	err = cdc_ncm_do_request(ctx, &req, &max_datagram_size, 0, NULL, 1000); -	if (err) { -		pr_debug(" GET_MAX_DATAGRAM_SIZE failed, using size=%u\n", -			 CDC_NCM_MIN_DATAGRAM_SIZE); -		/* use default */ -		ctx->max_datagram_size = CDC_NCM_MIN_DATAGRAM_SIZE; -	} else { -		ctx->max_datagram_size = le16_to_cpu(max_datagram_size); +	/* be conservative when selecting intial buffer size to +	 * increase the number of hosts this will work for +	 */ +	def_rx = min_t(u32, CDC_NCM_NTB_DEF_SIZE_RX, +		       le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize)); +	def_tx = min_t(u32, CDC_NCM_NTB_DEF_SIZE_TX, +		       le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize)); -		if (ctx->max_datagram_size < CDC_NCM_MIN_DATAGRAM_SIZE) -			ctx->max_datagram_size = CDC_NCM_MIN_DATAGRAM_SIZE; -		else if (ctx->max_datagram_size > CDC_NCM_MAX_DATAGRAM_SIZE) -			ctx->max_datagram_size = CDC_NCM_MAX_DATAGRAM_SIZE; -	} +	/* clamp rx_max and tx_max and inform device */ +	cdc_ncm_update_rxtx_max(dev, def_rx, def_tx); -	if (ctx->netdev->mtu != (ctx->max_datagram_size - ETH_HLEN)) -		ctx->netdev->mtu = ctx->max_datagram_size - ETH_HLEN; +	/* sanitize the modulus and remainder values */ +	cdc_ncm_fix_modulus(dev); +	/* set max datagram size */ +	cdc_ncm_set_dgram_size(dev, cdc_ncm_max_dgram_size(dev));  	return 0;  }  static void -cdc_ncm_find_endpoints(struct cdc_ncm_ctx *ctx, struct usb_interface *intf) +cdc_ncm_find_endpoints(struct usbnet *dev, struct usb_interface *intf)  { -	struct usb_host_endpoint *e; +	struct usb_host_endpoint *e, *in = NULL, *out = NULL;  	u8 ep;  	for (ep = 0; ep < intf->cur_altsetting->desc.bNumEndpoints; ep++) { @@ -358,18 +640,18 @@ cdc_ncm_find_endpoints(struct cdc_ncm_ctx *ctx, struct usb_interface *intf)  		switch (e->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {  		case USB_ENDPOINT_XFER_INT:  			if (usb_endpoint_dir_in(&e->desc)) { -				if (ctx->status_ep == NULL) -					ctx->status_ep = e; +				if (!dev->status) +					dev->status = e;  			}  			break;  		case USB_ENDPOINT_XFER_BULK:  			if (usb_endpoint_dir_in(&e->desc)) { -				if (ctx->in_ep == NULL) -					ctx->in_ep = e; +				if (!in) +					in = e;  			} else { -				if (ctx->out_ep == NULL) -					ctx->out_ep = e; +				if (!out) +					out = e;  			}  			break; @@ -377,6 +659,14 @@ cdc_ncm_find_endpoints(struct cdc_ncm_ctx *ctx, struct usb_interface *intf)  			break;  		}  	} +	if (in && !dev->in) +		dev->in = usb_rcvbulkpipe(dev->udev, +					  in->desc.bEndpointAddress & +					  USB_ENDPOINT_NUMBER_MASK); +	if (out && !dev->out) +		dev->out = usb_sndbulkpipe(dev->udev, +					   out->desc.bEndpointAddress & +					   USB_ENDPOINT_NUMBER_MASK);  }  static void cdc_ncm_free(struct cdc_ncm_ctx *ctx) @@ -384,19 +674,6 @@ static void cdc_ncm_free(struct cdc_ncm_ctx *ctx)  	if (ctx == NULL)  		return; -	del_timer_sync(&ctx->tx_timer); - -	if (ctx->data_claimed) { -		usb_set_intfdata(ctx->data, NULL); -		usb_driver_release_interface(driver_of(ctx->intf), ctx->data); -	} - -	if (ctx->control_claimed) { -		usb_set_intfdata(ctx->control, NULL); -		usb_driver_release_interface(driver_of(ctx->intf), -								ctx->control); -	} -  	if (ctx->tx_rem_skb != NULL) {  		dev_kfree_skb_any(ctx->tx_rem_skb);  		ctx->tx_rem_skb = NULL; @@ -410,8 +687,9 @@ static void cdc_ncm_free(struct cdc_ncm_ctx *ctx)  	kfree(ctx);  } -static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf) +int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting)  { +	const struct usb_cdc_union_desc *union_desc = NULL;  	struct cdc_ncm_ctx *ctx;  	struct usb_driver *driver;  	u8 *buf; @@ -419,27 +697,28 @@ static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)  	int temp;  	u8 iface_no; -	ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); -	if (ctx == NULL) -		goto error; +	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); +	if (!ctx) +		return -ENOMEM; -	memset(ctx, 0, sizeof(*ctx)); - -	init_timer(&ctx->tx_timer); +	hrtimer_init(&ctx->tx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); +	ctx->tx_timer.function = &cdc_ncm_tx_timer_cb; +	ctx->bh.data = (unsigned long)dev; +	ctx->bh.func = cdc_ncm_txpath_bh; +	atomic_set(&ctx->stop, 0);  	spin_lock_init(&ctx->mtx); -	ctx->netdev = dev->net;  	/* store ctx pointer in device data field */  	dev->data[0] = (unsigned long)ctx; +	/* only the control interface can be successfully probed */ +	ctx->control = intf; +  	/* get some pointers */  	driver = driver_of(intf);  	buf = intf->cur_altsetting->extra;  	len = intf->cur_altsetting->extralen; -	ctx->udev = dev->udev; -	ctx->intf = intf; -  	/* parse through descriptors associated with control interface */  	while ((len > 0) && (buf[0] > 2) && (buf[0] <= len)) { @@ -448,16 +727,18 @@ static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)  		switch (buf[2]) {  		case USB_CDC_UNION_TYPE: -			if (buf[0] < sizeof(*(ctx->union_desc))) +			if (buf[0] < sizeof(*union_desc))  				break; -			ctx->union_desc = -					(const struct usb_cdc_union_desc *)buf; - -			ctx->control = usb_ifnum_to_if(dev->udev, -					ctx->union_desc->bMasterInterface0); +			union_desc = (const struct usb_cdc_union_desc *)buf; +			/* the master must be the interface we are probing */ +			if (intf->cur_altsetting->desc.bInterfaceNumber != +			    union_desc->bMasterInterface0) { +				dev_dbg(&intf->dev, "bogus CDC Union\n"); +				goto error; +			}  			ctx->data = usb_ifnum_to_if(dev->udev, -					ctx->union_desc->bSlaveInterface0); +						    union_desc->bSlaveInterface0);  			break;  		case USB_CDC_ETHERNET_TYPE: @@ -466,19 +747,6 @@ static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)  			ctx->ether_desc =  					(const struct usb_cdc_ether_desc *)buf; - -			dev->hard_mtu = -				le16_to_cpu(ctx->ether_desc->wMaxSegmentSize); - -			if (dev->hard_mtu < -			    (CDC_NCM_MIN_DATAGRAM_SIZE - ETH_HLEN)) -				dev->hard_mtu = -					CDC_NCM_MIN_DATAGRAM_SIZE - ETH_HLEN; - -			else if (dev->hard_mtu > -				 (CDC_NCM_MAX_DATAGRAM_SIZE - ETH_HLEN)) -				dev->hard_mtu = -					CDC_NCM_MAX_DATAGRAM_SIZE - ETH_HLEN;  			break;  		case USB_CDC_NCM_TYPE: @@ -488,6 +756,21 @@ static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)  			ctx->func_desc = (const struct usb_cdc_ncm_desc *)buf;  			break; +		case USB_CDC_MBIM_TYPE: +			if (buf[0] < sizeof(*(ctx->mbim_desc))) +				break; + +			ctx->mbim_desc = (const struct usb_cdc_mbim_desc *)buf; +			break; + +		case USB_CDC_MBIM_EXTENDED_TYPE: +			if (buf[0] < sizeof(*(ctx->mbim_extended_desc))) +				break; + +			ctx->mbim_extended_desc = +				(const struct usb_cdc_mbim_extended_desc *)buf; +			break; +  		default:  			break;  		} @@ -498,198 +781,288 @@ advance:  		len -= temp;  	} +	/* some buggy devices have an IAD but no CDC Union */ +	if (!union_desc && intf->intf_assoc && intf->intf_assoc->bInterfaceCount == 2) { +		ctx->data = usb_ifnum_to_if(dev->udev, intf->cur_altsetting->desc.bInterfaceNumber + 1); +		dev_dbg(&intf->dev, "CDC Union missing - got slave from IAD\n"); +	} +  	/* check if we got everything */ -	if ((ctx->control == NULL) || (ctx->data == NULL) || -	    (ctx->ether_desc == NULL)) +	if (!ctx->data) { +		dev_dbg(&intf->dev, "CDC Union missing and no IAD found\n");  		goto error; - -	/* claim interfaces, if any */ -	if (ctx->data != intf) { -		temp = usb_driver_claim_interface(driver, ctx->data, dev); -		if (temp) +	} +	if (cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting)) { +		if (!ctx->mbim_desc) { +			dev_dbg(&intf->dev, "MBIM functional descriptor missing\n");  			goto error; -		ctx->data_claimed = 1; +		} +	} else { +		if (!ctx->ether_desc || !ctx->func_desc) { +			dev_dbg(&intf->dev, "NCM or ECM functional descriptors missing\n"); +			goto error; +		}  	} -	if (ctx->control != intf) { -		temp = usb_driver_claim_interface(driver, ctx->control, dev); -		if (temp) +	/* claim data interface, if different from control */ +	if (ctx->data != ctx->control) { +		temp = usb_driver_claim_interface(driver, ctx->data, dev); +		if (temp) { +			dev_dbg(&intf->dev, "failed to claim data intf\n");  			goto error; -		ctx->control_claimed = 1; +		}  	}  	iface_no = ctx->data->cur_altsetting->desc.bInterfaceNumber;  	/* reset data interface */  	temp = usb_set_interface(dev->udev, iface_no, 0); -	if (temp) -		goto error; +	if (temp) { +		dev_dbg(&intf->dev, "set interface failed\n"); +		goto error2; +	} -	/* initialize data interface */ -	if (cdc_ncm_setup(ctx)) -		goto error; +	/* initialize basic device settings */ +	if (cdc_ncm_init(dev)) +		goto error2;  	/* configure data interface */ -	temp = usb_set_interface(dev->udev, iface_no, 1); -	if (temp) -		goto error; - -	cdc_ncm_find_endpoints(ctx, ctx->data); -	cdc_ncm_find_endpoints(ctx, ctx->control); - -	if ((ctx->in_ep == NULL) || (ctx->out_ep == NULL) || -	    (ctx->status_ep == NULL)) -		goto error; +	temp = usb_set_interface(dev->udev, iface_no, data_altsetting); +	if (temp) { +		dev_dbg(&intf->dev, "set interface failed\n"); +		goto error2; +	} -	dev->net->ethtool_ops = &cdc_ncm_ethtool_ops; +	cdc_ncm_find_endpoints(dev, ctx->data); +	cdc_ncm_find_endpoints(dev, ctx->control); +	if (!dev->in || !dev->out || !dev->status) { +		dev_dbg(&intf->dev, "failed to collect endpoints\n"); +		goto error2; +	}  	usb_set_intfdata(ctx->data, dev);  	usb_set_intfdata(ctx->control, dev); -	usb_set_intfdata(ctx->intf, dev); -	temp = usbnet_get_ethernet_addr(dev, ctx->ether_desc->iMACAddress); -	if (temp) -		goto error; +	if (ctx->ether_desc) { +		temp = usbnet_get_ethernet_addr(dev, ctx->ether_desc->iMACAddress); +		if (temp) { +			dev_dbg(&intf->dev, "failed to get mac address\n"); +			goto error2; +		} +		dev_info(&intf->dev, "MAC-Address: %pM\n", dev->net->dev_addr); +	} -	dev_info(&dev->udev->dev, "MAC-Address: " -				"0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x\n", -				dev->net->dev_addr[0], dev->net->dev_addr[1], -				dev->net->dev_addr[2], dev->net->dev_addr[3], -				dev->net->dev_addr[4], dev->net->dev_addr[5]); +	/* finish setting up the device specific data */ +	cdc_ncm_setup(dev); -	dev->in = usb_rcvbulkpipe(dev->udev, -		ctx->in_ep->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); -	dev->out = usb_sndbulkpipe(dev->udev, -		ctx->out_ep->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); -	dev->status = ctx->status_ep; -	dev->rx_urb_size = ctx->rx_max; +	/* override ethtool_ops */ +	dev->net->ethtool_ops = &cdc_ncm_ethtool_ops; + +	/* add our sysfs attrs */ +	dev->net->sysfs_groups[0] = &cdc_ncm_sysfs_attr_group; -	/* -	 * We should get an event when network connection is "connected" or -	 * "disconnected". Set network connection in "disconnected" state -	 * (carrier is OFF) during attach, so the IP network stack does not -	 * start IPv6 negotiation and more. -	 */ -	netif_carrier_off(dev->net); -	ctx->tx_speed = ctx->rx_speed = 0;  	return 0; +error2: +	usb_set_intfdata(ctx->control, NULL); +	usb_set_intfdata(ctx->data, NULL); +	if (ctx->data != ctx->control) +		usb_driver_release_interface(driver, ctx->data);  error:  	cdc_ncm_free((struct cdc_ncm_ctx *)dev->data[0]);  	dev->data[0] = 0; -	dev_info(&dev->udev->dev, "Descriptor failure\n"); +	dev_info(&intf->dev, "bind() failure\n");  	return -ENODEV;  } +EXPORT_SYMBOL_GPL(cdc_ncm_bind_common); -static void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf) +void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf)  {  	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; -	struct usb_driver *driver; +	struct usb_driver *driver = driver_of(intf);  	if (ctx == NULL)  		return;		/* no setup */ -	driver = driver_of(intf); +	atomic_set(&ctx->stop, 1); -	usb_set_intfdata(ctx->data, NULL); -	usb_set_intfdata(ctx->control, NULL); -	usb_set_intfdata(ctx->intf, NULL); +	if (hrtimer_active(&ctx->tx_timer)) +		hrtimer_cancel(&ctx->tx_timer); + +	tasklet_kill(&ctx->bh); -	/* release interfaces, if any */ -	if (ctx->data_claimed) { +	/* handle devices with combined control and data interface */ +	if (ctx->control == ctx->data) +		ctx->data = NULL; + +	/* disconnect master --> disconnect slave */ +	if (intf == ctx->control && ctx->data) { +		usb_set_intfdata(ctx->data, NULL);  		usb_driver_release_interface(driver, ctx->data); -		ctx->data_claimed = 0; -	} +		ctx->data = NULL; -	if (ctx->control_claimed) { +	} else if (intf == ctx->data && ctx->control) { +		usb_set_intfdata(ctx->control, NULL);  		usb_driver_release_interface(driver, ctx->control); -		ctx->control_claimed = 0; +		ctx->control = NULL;  	} +	usb_set_intfdata(intf, NULL);  	cdc_ncm_free(ctx);  } +EXPORT_SYMBOL_GPL(cdc_ncm_unbind); -static void cdc_ncm_zero_fill(u8 *ptr, u32 first, u32 end, u32 max) +/* Return the number of the MBIM control interface altsetting iff it + * is preferred and available, + */ +u8 cdc_ncm_select_altsetting(struct usb_interface *intf)  { -	if (first >= max) -		return; -	if (first >= end) -		return; -	if (end > max) -		end = max; -	memset(ptr + first, 0, end - first); +	struct usb_host_interface *alt; + +	/* The MBIM spec defines a NCM compatible default altsetting, +	 * which we may have matched: +	 * +	 *  "Functions that implement both NCM 1.0 and MBIM (an +	 *   “NCM/MBIM function”) according to this recommendation +	 *   shall provide two alternate settings for the +	 *   Communication Interface.  Alternate setting 0, and the +	 *   associated class and endpoint descriptors, shall be +	 *   constructed according to the rules given for the +	 *   Communication Interface in section 5 of [USBNCM10]. +	 *   Alternate setting 1, and the associated class and +	 *   endpoint descriptors, shall be constructed according to +	 *   the rules given in section 6 (USB Device Model) of this +	 *   specification." +	 */ +	if (intf->num_altsetting < 2) +		return intf->cur_altsetting->desc.bAlternateSetting; + +	if (prefer_mbim) { +		alt = usb_altnum_to_altsetting(intf, CDC_NCM_COMM_ALTSETTING_MBIM); +		if (alt && cdc_ncm_comm_intf_is_mbim(alt)) +			return CDC_NCM_COMM_ALTSETTING_MBIM; +	} +	return CDC_NCM_COMM_ALTSETTING_NCM;  } +EXPORT_SYMBOL_GPL(cdc_ncm_select_altsetting); -static struct sk_buff * -cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb) +static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)  { -	struct sk_buff *skb_out; -	u32 rem; -	u32 offset; -	u32 last_offset; -	u16 n = 0; -	u8 timeout = 0; +	int ret; -	/* if there is a remaining skb, it gets priority */ -	if (skb != NULL) -		swap(skb, ctx->tx_rem_skb); -	else -		timeout = 1; +	/* MBIM backwards compatible function? */ +	if (cdc_ncm_select_altsetting(intf) != CDC_NCM_COMM_ALTSETTING_NCM) +		return -ENODEV; + +	/* The NCM data altsetting is fixed */ +	ret = cdc_ncm_bind_common(dev, intf, CDC_NCM_DATA_ALTSETTING_NCM);  	/* -	 * +----------------+ -	 * | skb_out        | -	 * +----------------+ -	 *           ^ offset -	 *        ^ last_offset +	 * We should get an event when network connection is "connected" or +	 * "disconnected". Set network connection in "disconnected" state +	 * (carrier is OFF) during attach, so the IP network stack does not +	 * start IPv6 negotiation and more.  	 */ +	usbnet_link_change(dev, 0, 0); +	return ret; +} -	/* check if we are resuming an OUT skb */ -	if (ctx->tx_curr_skb != NULL) { -		/* pop variables */ -		skb_out = ctx->tx_curr_skb; -		offset = ctx->tx_curr_offset; -		last_offset = ctx->tx_curr_last_offset; -		n = ctx->tx_curr_frame_num; +static void cdc_ncm_align_tail(struct sk_buff *skb, size_t modulus, size_t remainder, size_t max) +{ +	size_t align = ALIGN(skb->len, modulus) - skb->len + remainder; + +	if (skb->len + align > max) +		align = max - skb->len; +	if (align && skb_tailroom(skb) >= align) +		memset(skb_put(skb, align), 0, align); +} + +/* return a pointer to a valid struct usb_cdc_ncm_ndp16 of type sign, possibly + * allocating a new one within skb + */ +static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign, size_t reserve) +{ +	struct usb_cdc_ncm_ndp16 *ndp16 = NULL; +	struct usb_cdc_ncm_nth16 *nth16 = (void *)skb->data; +	size_t ndpoffset = le16_to_cpu(nth16->wNdpIndex); + +	/* follow the chain of NDPs, looking for a match */ +	while (ndpoffset) { +		ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb->data + ndpoffset); +		if  (ndp16->dwSignature == sign) +			return ndp16; +		ndpoffset = le16_to_cpu(ndp16->wNextNdpIndex); +	} + +	/* align new NDP */ +	cdc_ncm_align_tail(skb, ctx->tx_ndp_modulus, 0, ctx->tx_max); + +	/* verify that there is room for the NDP and the datagram (reserve) */ +	if ((ctx->tx_max - skb->len - reserve) < ctx->max_ndp_size) +		return NULL; +	/* link to it */ +	if (ndp16) +		ndp16->wNextNdpIndex = cpu_to_le16(skb->len); +	else +		nth16->wNdpIndex = cpu_to_le16(skb->len); + +	/* push a new empty NDP */ +	ndp16 = (struct usb_cdc_ncm_ndp16 *)memset(skb_put(skb, ctx->max_ndp_size), 0, ctx->max_ndp_size); +	ndp16->dwSignature = sign; +	ndp16->wLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_ndp16) + sizeof(struct usb_cdc_ncm_dpe16)); +	return ndp16; +} + +struct sk_buff * +cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) +{ +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; +	struct usb_cdc_ncm_nth16 *nth16; +	struct usb_cdc_ncm_ndp16 *ndp16; +	struct sk_buff *skb_out; +	u16 n = 0, index, ndplen; +	u8 ready2send = 0; + +	/* if there is a remaining skb, it gets priority */ +	if (skb != NULL) { +		swap(skb, ctx->tx_rem_skb); +		swap(sign, ctx->tx_rem_sign);  	} else { -		/* reset variables */ +		ready2send = 1; +	} + +	/* check if we are resuming an OUT skb */ +	skb_out = ctx->tx_curr_skb; + +	/* allocate a new OUT skb */ +	if (!skb_out) {  		skb_out = alloc_skb(ctx->tx_max, GFP_ATOMIC);  		if (skb_out == NULL) {  			if (skb != NULL) {  				dev_kfree_skb_any(skb); -				ctx->netdev->stats.tx_dropped++; +				dev->net->stats.tx_dropped++;  			}  			goto exit_no_skb;  		} +		/* fill out the initial 16-bit NTB header */ +		nth16 = (struct usb_cdc_ncm_nth16 *)memset(skb_put(skb_out, sizeof(struct usb_cdc_ncm_nth16)), 0, sizeof(struct usb_cdc_ncm_nth16)); +		nth16->dwSignature = cpu_to_le32(USB_CDC_NCM_NTH16_SIGN); +		nth16->wHeaderLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_nth16)); +		nth16->wSequence = cpu_to_le16(ctx->tx_seq++); -		/* make room for NTH and NDP */ -		offset = ALIGN(sizeof(struct usb_cdc_ncm_nth16), -					ctx->tx_ndp_modulus) + -					sizeof(struct usb_cdc_ncm_ndp16) + -					(ctx->tx_max_datagrams + 1) * -					sizeof(struct usb_cdc_ncm_dpe16); - -		/* store last valid offset before alignment */ -		last_offset = offset; -		/* align first Datagram offset correctly */ -		offset = ALIGN(offset, ctx->tx_modulus) + ctx->tx_remainder; -		/* zero buffer till the first IP datagram */ -		cdc_ncm_zero_fill(skb_out->data, 0, offset, offset); -		n = 0; +		/* count total number of frames in this NTB */  		ctx->tx_curr_frame_num = 0; -	} -	for (; n < ctx->tx_max_datagrams; n++) { -		/* check if end of transmit buffer is reached */ -		if (offset >= ctx->tx_max) -			break; - -		/* compute maximum buffer size */ -		rem = ctx->tx_max - offset; +		/* recent payload counter for this skb_out */ +		ctx->tx_curr_frame_payload = 0; +	} +	for (n = ctx->tx_curr_frame_num; n < ctx->tx_max_datagrams; n++) { +		/* send any remaining skb first */  		if (skb == NULL) {  			skb = ctx->tx_rem_skb; +			sign = ctx->tx_rem_sign;  			ctx->tx_rem_skb = NULL;  			/* check for end of skb */ @@ -697,53 +1070,60 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb)  				break;  		} -		if (skb->len > rem) { +		/* get the appropriate NDP for this skb */ +		ndp16 = cdc_ncm_ndp(ctx, skb_out, sign, skb->len + ctx->tx_modulus + ctx->tx_remainder); + +		/* align beginning of next frame */ +		cdc_ncm_align_tail(skb_out,  ctx->tx_modulus, ctx->tx_remainder, ctx->tx_max); + +		/* check if we had enough room left for both NDP and frame */ +		if (!ndp16 || skb_out->len + skb->len > ctx->tx_max) {  			if (n == 0) {  				/* won't fit, MTU problem? */  				dev_kfree_skb_any(skb);  				skb = NULL; -				ctx->netdev->stats.tx_dropped++; +				dev->net->stats.tx_dropped++;  			} else {  				/* no room for skb - store for later */  				if (ctx->tx_rem_skb != NULL) {  					dev_kfree_skb_any(ctx->tx_rem_skb); -					ctx->netdev->stats.tx_dropped++; +					dev->net->stats.tx_dropped++;  				}  				ctx->tx_rem_skb = skb; +				ctx->tx_rem_sign = sign;  				skb = NULL; - -				/* loop one more time */ -				timeout = 1; +				ready2send = 1; +				ctx->tx_reason_ntb_full++;	/* count reason for transmitting */  			}  			break;  		} -		memcpy(((u8 *)skb_out->data) + offset, skb->data, skb->len); - -		ctx->tx_ncm.dpe16[n].wDatagramLength = cpu_to_le16(skb->len); -		ctx->tx_ncm.dpe16[n].wDatagramIndex = cpu_to_le16(offset); - -		/* update offset */ -		offset += skb->len; - -		/* store last valid offset before alignment */ -		last_offset = offset; - -		/* align offset correctly */ -		offset = ALIGN(offset, ctx->tx_modulus) + ctx->tx_remainder; +		/* calculate frame number withing this NDP */ +		ndplen = le16_to_cpu(ndp16->wLength); +		index = (ndplen - sizeof(struct usb_cdc_ncm_ndp16)) / sizeof(struct usb_cdc_ncm_dpe16) - 1; -		/* zero padding */ -		cdc_ncm_zero_fill(skb_out->data, last_offset, offset, -								ctx->tx_max); +		/* OK, add this skb */ +		ndp16->dpe16[index].wDatagramLength = cpu_to_le16(skb->len); +		ndp16->dpe16[index].wDatagramIndex = cpu_to_le16(skb_out->len); +		ndp16->wLength = cpu_to_le16(ndplen + sizeof(struct usb_cdc_ncm_dpe16)); +		memcpy(skb_put(skb_out, skb->len), skb->data, skb->len); +		ctx->tx_curr_frame_payload += skb->len;	/* count real tx payload data */  		dev_kfree_skb_any(skb);  		skb = NULL; + +		/* send now if this NDP is full */ +		if (index >= CDC_NCM_DPT_DATAGRAMS_MAX) { +			ready2send = 1; +			ctx->tx_reason_ndp_full++;	/* count reason for transmitting */ +			break; +		}  	}  	/* free up any dangling skb */  	if (skb != NULL) {  		dev_kfree_skb_any(skb);  		skb = NULL; -		ctx->netdev->stats.tx_dropped++; +		dev->net->stats.tx_dropped++;  	}  	ctx->tx_curr_frame_num = n; @@ -752,139 +1132,113 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb)  		/* wait for more frames */  		/* push variables */  		ctx->tx_curr_skb = skb_out; -		ctx->tx_curr_offset = offset; -		ctx->tx_curr_last_offset = last_offset;  		goto exit_no_skb; -	} else if ((n < ctx->tx_max_datagrams) && (timeout == 0)) { +	} else if ((n < ctx->tx_max_datagrams) && (ready2send == 0) && (ctx->timer_interval > 0)) {  		/* wait for more frames */  		/* push variables */  		ctx->tx_curr_skb = skb_out; -		ctx->tx_curr_offset = offset; -		ctx->tx_curr_last_offset = last_offset;  		/* set the pending count */  		if (n < CDC_NCM_RESTART_TIMER_DATAGRAM_CNT) -			ctx->tx_timer_pending = 2; +			ctx->tx_timer_pending = CDC_NCM_TIMER_PENDING_CNT;  		goto exit_no_skb;  	} else { +		if (n == ctx->tx_max_datagrams) +			ctx->tx_reason_max_datagram++;	/* count reason for transmitting */  		/* frame goes out */  		/* variables will be reset at next call */  	} -	/* check for overflow */ -	if (last_offset > ctx->tx_max) -		last_offset = ctx->tx_max; - -	/* revert offset */ -	offset = last_offset; - -	/* -	 * If collected data size is less or equal CDC_NCM_MIN_TX_PKT bytes, -	 * we send buffers as it is. If we get more data, it would be more -	 * efficient for USB HS mobile device with DMA engine to receive a full -	 * size NTB, than canceling DMA transfer and receiving a short packet. +	/* If collected data size is less or equal ctx->min_tx_pkt +	 * bytes, we send buffers as it is. If we get more data, it +	 * would be more efficient for USB HS mobile device with DMA +	 * engine to receive a full size NTB, than canceling DMA +	 * transfer and receiving a short packet. +	 * +	 * This optimization support is pointless if we end up sending +	 * a ZLP after full sized NTBs.  	 */ -	if (offset > CDC_NCM_MIN_TX_PKT) -		offset = ctx->tx_max; +	if (!(dev->driver_info->flags & FLAG_SEND_ZLP) && +	    skb_out->len > ctx->min_tx_pkt) +		memset(skb_put(skb_out, ctx->tx_max - skb_out->len), 0, +		       ctx->tx_max - skb_out->len); +	else if (skb_out->len < ctx->tx_max && (skb_out->len % dev->maxpacket) == 0) +		*skb_put(skb_out, 1) = 0;	/* force short packet */ -	/* final zero padding */ -	cdc_ncm_zero_fill(skb_out->data, last_offset, offset, ctx->tx_max); +	/* set final frame length */ +	nth16 = (struct usb_cdc_ncm_nth16 *)skb_out->data; +	nth16->wBlockLength = cpu_to_le16(skb_out->len); -	/* store last offset */ -	last_offset = offset; - -	if ((last_offset < ctx->tx_max) && ((last_offset % -			le16_to_cpu(ctx->out_ep->desc.wMaxPacketSize)) == 0)) { -		/* force short packet */ -		*(((u8 *)skb_out->data) + last_offset) = 0; -		last_offset++; -	} - -	/* zero the rest of the DPEs plus the last NULL entry */ -	for (; n <= CDC_NCM_DPT_DATAGRAMS_MAX; n++) { -		ctx->tx_ncm.dpe16[n].wDatagramLength = 0; -		ctx->tx_ncm.dpe16[n].wDatagramIndex = 0; -	} - -	/* fill out 16-bit NTB header */ -	ctx->tx_ncm.nth16.dwSignature = cpu_to_le32(USB_CDC_NCM_NTH16_SIGN); -	ctx->tx_ncm.nth16.wHeaderLength = -					cpu_to_le16(sizeof(ctx->tx_ncm.nth16)); -	ctx->tx_ncm.nth16.wSequence = cpu_to_le16(ctx->tx_seq); -	ctx->tx_ncm.nth16.wBlockLength = cpu_to_le16(last_offset); -	ctx->tx_ncm.nth16.wFpIndex = ALIGN(sizeof(struct usb_cdc_ncm_nth16), -							ctx->tx_ndp_modulus); - -	memcpy(skb_out->data, &(ctx->tx_ncm.nth16), sizeof(ctx->tx_ncm.nth16)); -	ctx->tx_seq++; - -	/* fill out 16-bit NDP table */ -	ctx->tx_ncm.ndp16.dwSignature = -				cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN); -	rem = sizeof(ctx->tx_ncm.ndp16) + ((ctx->tx_curr_frame_num + 1) * -					sizeof(struct usb_cdc_ncm_dpe16)); -	ctx->tx_ncm.ndp16.wLength = cpu_to_le16(rem); -	ctx->tx_ncm.ndp16.wNextFpIndex = 0; /* reserved */ - -	memcpy(((u8 *)skb_out->data) + ctx->tx_ncm.nth16.wFpIndex, -						&(ctx->tx_ncm.ndp16), -						sizeof(ctx->tx_ncm.ndp16)); +	/* return skb */ +	ctx->tx_curr_skb = NULL; +	dev->net->stats.tx_packets += ctx->tx_curr_frame_num; -	memcpy(((u8 *)skb_out->data) + ctx->tx_ncm.nth16.wFpIndex + -					sizeof(ctx->tx_ncm.ndp16), -					&(ctx->tx_ncm.dpe16), -					(ctx->tx_curr_frame_num + 1) * -					sizeof(struct usb_cdc_ncm_dpe16)); +	/* keep private stats: framing overhead and number of NTBs */ +	ctx->tx_overhead += skb_out->len - ctx->tx_curr_frame_payload; +	ctx->tx_ntbs++; -	/* set frame length */ -	skb_put(skb_out, last_offset); +	/* usbnet has already counted all the framing overhead. +	 * Adjust the stats so that the tx_bytes counter show real +	 * payload data instead. +	 */ +	dev->net->stats.tx_bytes -= skb_out->len - ctx->tx_curr_frame_payload; -	/* return skb */ -	ctx->tx_curr_skb = NULL;  	return skb_out;  exit_no_skb: +	/* Start timer, if there is a remaining non-empty skb */ +	if (ctx->tx_curr_skb != NULL && n > 0) +		cdc_ncm_tx_timeout_start(ctx);  	return NULL;  } +EXPORT_SYMBOL_GPL(cdc_ncm_fill_tx_frame);  static void cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx)  {  	/* start timer, if not already started */ -	if (timer_pending(&ctx->tx_timer) == 0) { -		ctx->tx_timer.function = &cdc_ncm_tx_timeout; -		ctx->tx_timer.data = (unsigned long)ctx; -		ctx->tx_timer.expires = jiffies + ((HZ + 999) / 1000); -		add_timer(&ctx->tx_timer); -	} +	if (!(hrtimer_active(&ctx->tx_timer) || atomic_read(&ctx->stop))) +		hrtimer_start(&ctx->tx_timer, +				ktime_set(0, ctx->timer_interval), +				HRTIMER_MODE_REL);  } -static void cdc_ncm_tx_timeout(unsigned long arg) +static enum hrtimer_restart cdc_ncm_tx_timer_cb(struct hrtimer *timer)  { -	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)arg; -	u8 restart; +	struct cdc_ncm_ctx *ctx = +			container_of(timer, struct cdc_ncm_ctx, tx_timer); -	spin_lock(&ctx->mtx); -	if (ctx->tx_timer_pending != 0) { -		ctx->tx_timer_pending--; -		restart = 1; -	} else -		restart = 0; +	if (!atomic_read(&ctx->stop)) +		tasklet_schedule(&ctx->bh); +	return HRTIMER_NORESTART; +} -	spin_unlock(&ctx->mtx); +static void cdc_ncm_txpath_bh(unsigned long param) +{ +	struct usbnet *dev = (struct usbnet *)param; +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; -	if (restart) +	spin_lock_bh(&ctx->mtx); +	if (ctx->tx_timer_pending != 0) { +		ctx->tx_timer_pending--;  		cdc_ncm_tx_timeout_start(ctx); -	else if (ctx->netdev != NULL) -		usbnet_start_xmit(NULL, ctx->netdev); +		spin_unlock_bh(&ctx->mtx); +	} else if (dev->net != NULL) { +		ctx->tx_reason_timeout++;	/* count reason for transmitting */ +		spin_unlock_bh(&ctx->mtx); +		netif_tx_lock_bh(dev->net); +		usbnet_start_xmit(NULL, dev->net); +		netif_tx_unlock_bh(dev->net); +	} else { +		spin_unlock_bh(&ctx->mtx); +	}  } -static struct sk_buff * +struct sk_buff *  cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)  {  	struct sk_buff *skb_out;  	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; -	u8 need_timer = 0;  	/*  	 * The Ethernet API we are using does not support transmitting @@ -896,18 +1250,9 @@ cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)  	if (ctx == NULL)  		goto error; -	spin_lock(&ctx->mtx); -	skb_out = cdc_ncm_fill_tx_frame(ctx, skb); -	if (ctx->tx_curr_skb != NULL) -		need_timer = 1; -	spin_unlock(&ctx->mtx); - -	/* Start timer, if there is a remaining skb */ -	if (need_timer) -		cdc_ncm_tx_timeout_start(ctx); - -	if (skb_out) -		dev->net->stats.tx_packets += ctx->tx_curr_frame_num; +	spin_lock_bh(&ctx->mtx); +	skb_out = cdc_ncm_fill_tx_frame(dev, skb, cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN)); +	spin_unlock_bh(&ctx->mtx);  	return skb_out;  error: @@ -916,159 +1261,197 @@ error:  	return NULL;  } +EXPORT_SYMBOL_GPL(cdc_ncm_tx_fixup); -static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) +/* verify NTB header and return offset of first NDP, or negative error */ +int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in)  { -	struct sk_buff *skb; -	struct cdc_ncm_ctx *ctx; -	int sumlen; -	int actlen; -	int temp; -	int nframes; -	int x; -	int offset; +	struct usbnet *dev = netdev_priv(skb_in->dev); +	struct usb_cdc_ncm_nth16 *nth16; +	int len; +	int ret = -EINVAL; -	ctx = (struct cdc_ncm_ctx *)dev->data[0];  	if (ctx == NULL)  		goto error; -	actlen = skb_in->len; -	sumlen = CDC_NCM_NTB_MAX_SIZE_RX; - -	if (actlen < (sizeof(ctx->rx_ncm.nth16) + sizeof(ctx->rx_ncm.ndp16))) { -		pr_debug("frame too short\n"); +	if (skb_in->len < (sizeof(struct usb_cdc_ncm_nth16) + +					sizeof(struct usb_cdc_ncm_ndp16))) { +		netif_dbg(dev, rx_err, dev->net, "frame too short\n");  		goto error;  	} -	memcpy(&(ctx->rx_ncm.nth16), ((u8 *)skb_in->data), -						sizeof(ctx->rx_ncm.nth16)); +	nth16 = (struct usb_cdc_ncm_nth16 *)skb_in->data; -	if (le32_to_cpu(ctx->rx_ncm.nth16.dwSignature) != -	    USB_CDC_NCM_NTH16_SIGN) { -		pr_debug("invalid NTH16 signature <%u>\n", -			 le32_to_cpu(ctx->rx_ncm.nth16.dwSignature)); +	if (nth16->dwSignature != cpu_to_le32(USB_CDC_NCM_NTH16_SIGN)) { +		netif_dbg(dev, rx_err, dev->net, +			  "invalid NTH16 signature <%#010x>\n", +			  le32_to_cpu(nth16->dwSignature));  		goto error;  	} -	temp = le16_to_cpu(ctx->rx_ncm.nth16.wBlockLength); -	if (temp > sumlen) { -		pr_debug("unsupported NTB block length %u/%u\n", temp, sumlen); +	len = le16_to_cpu(nth16->wBlockLength); +	if (len > ctx->rx_max) { +		netif_dbg(dev, rx_err, dev->net, +			  "unsupported NTB block length %u/%u\n", len, +			  ctx->rx_max);  		goto error;  	} -	temp = le16_to_cpu(ctx->rx_ncm.nth16.wFpIndex); -	if ((temp + sizeof(ctx->rx_ncm.ndp16)) > actlen) { -		pr_debug("invalid DPT16 index\n"); -		goto error; +	if ((ctx->rx_seq + 1) != le16_to_cpu(nth16->wSequence) && +	    (ctx->rx_seq || le16_to_cpu(nth16->wSequence)) && +	    !((ctx->rx_seq == 0xffff) && !le16_to_cpu(nth16->wSequence))) { +		netif_dbg(dev, rx_err, dev->net, +			  "sequence number glitch prev=%d curr=%d\n", +			  ctx->rx_seq, le16_to_cpu(nth16->wSequence));  	} +	ctx->rx_seq = le16_to_cpu(nth16->wSequence); + +	ret = le16_to_cpu(nth16->wNdpIndex); +error: +	return ret; +} +EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_nth16); -	memcpy(&(ctx->rx_ncm.ndp16), ((u8 *)skb_in->data) + temp, -						sizeof(ctx->rx_ncm.ndp16)); +/* verify NDP header and return number of datagrams, or negative error */ +int cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset) +{ +	struct usbnet *dev = netdev_priv(skb_in->dev); +	struct usb_cdc_ncm_ndp16 *ndp16; +	int ret = -EINVAL; -	if (le32_to_cpu(ctx->rx_ncm.ndp16.dwSignature) != -	    USB_CDC_NCM_NDP16_NOCRC_SIGN) { -		pr_debug("invalid DPT16 signature <%u>\n", -			 le32_to_cpu(ctx->rx_ncm.ndp16.dwSignature)); +	if ((ndpoffset + sizeof(struct usb_cdc_ncm_ndp16)) > skb_in->len) { +		netif_dbg(dev, rx_err, dev->net, "invalid NDP offset  <%u>\n", +			  ndpoffset);  		goto error;  	} +	ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb_in->data + ndpoffset); -	if (le16_to_cpu(ctx->rx_ncm.ndp16.wLength) < -	    USB_CDC_NCM_NDP16_LENGTH_MIN) { -		pr_debug("invalid DPT16 length <%u>\n", -			 le32_to_cpu(ctx->rx_ncm.ndp16.dwSignature)); +	if (le16_to_cpu(ndp16->wLength) < USB_CDC_NCM_NDP16_LENGTH_MIN) { +		netif_dbg(dev, rx_err, dev->net, "invalid DPT16 length <%u>\n", +			  le16_to_cpu(ndp16->wLength));  		goto error;  	} -	nframes = ((le16_to_cpu(ctx->rx_ncm.ndp16.wLength) - +	ret = ((le16_to_cpu(ndp16->wLength) -  					sizeof(struct usb_cdc_ncm_ndp16)) /  					sizeof(struct usb_cdc_ncm_dpe16)); -	nframes--; /* we process NDP entries except for the last one */ +	ret--; /* we process NDP entries except for the last one */ -	pr_debug("nframes = %u\n", nframes); +	if ((sizeof(struct usb_cdc_ncm_ndp16) + +	     ret * (sizeof(struct usb_cdc_ncm_dpe16))) > skb_in->len) { +		netif_dbg(dev, rx_err, dev->net, "Invalid nframes = %d\n", ret); +		ret = -EINVAL; +	} + +error: +	return ret; +} +EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_ndp16); -	temp += sizeof(ctx->rx_ncm.ndp16); +int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) +{ +	struct sk_buff *skb; +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; +	int len; +	int nframes; +	int x; +	int offset; +	struct usb_cdc_ncm_ndp16 *ndp16; +	struct usb_cdc_ncm_dpe16 *dpe16; +	int ndpoffset; +	int loopcount = 50; /* arbitrary max preventing infinite loop */ +	u32 payload = 0; + +	ndpoffset = cdc_ncm_rx_verify_nth16(ctx, skb_in); +	if (ndpoffset < 0) +		goto error; -	if ((temp + nframes * (sizeof(struct usb_cdc_ncm_dpe16))) > actlen) { -		pr_debug("Invalid nframes = %d\n", nframes); +next_ndp: +	nframes = cdc_ncm_rx_verify_ndp16(skb_in, ndpoffset); +	if (nframes < 0)  		goto error; -	} -	if (nframes > CDC_NCM_DPT_DATAGRAMS_MAX) { -		pr_debug("Truncating number of frames from %u to %u\n", -					nframes, CDC_NCM_DPT_DATAGRAMS_MAX); -		nframes = CDC_NCM_DPT_DATAGRAMS_MAX; -	} +	ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb_in->data + ndpoffset); -	memcpy(&(ctx->rx_ncm.dpe16), ((u8 *)skb_in->data) + temp, -				nframes * (sizeof(struct usb_cdc_ncm_dpe16))); +	if (ndp16->dwSignature != cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN)) { +		netif_dbg(dev, rx_err, dev->net, +			  "invalid DPT16 signature <%#010x>\n", +			  le32_to_cpu(ndp16->dwSignature)); +		goto err_ndp; +	} +	dpe16 = ndp16->dpe16; -	for (x = 0; x < nframes; x++) { -		offset = le16_to_cpu(ctx->rx_ncm.dpe16[x].wDatagramIndex); -		temp = le16_to_cpu(ctx->rx_ncm.dpe16[x].wDatagramLength); +	for (x = 0; x < nframes; x++, dpe16++) { +		offset = le16_to_cpu(dpe16->wDatagramIndex); +		len = le16_to_cpu(dpe16->wDatagramLength);  		/*  		 * CDC NCM ch. 3.7  		 * All entries after first NULL entry are to be ignored  		 */ -		if ((offset == 0) || (temp == 0)) { +		if ((offset == 0) || (len == 0)) {  			if (!x) -				goto error; /* empty NTB */ +				goto err_ndp; /* empty NTB */  			break;  		}  		/* sanity checking */ -		if (((offset + temp) > actlen) || -		    (temp > CDC_NCM_MAX_DATAGRAM_SIZE) || (temp < ETH_HLEN)) { -			pr_debug("invalid frame detected (ignored)" -				"offset[%u]=%u, length=%u, skb=%p\n", -							x, offset, temp, skb); +		if (((offset + len) > skb_in->len) || +				(len > ctx->rx_max) || (len < ETH_HLEN)) { +			netif_dbg(dev, rx_err, dev->net, +				  "invalid frame detected (ignored) offset[%u]=%u, length=%u, skb=%p\n", +				  x, offset, len, skb_in);  			if (!x) -				goto error; +				goto err_ndp;  			break;  		} else { -			skb = skb_clone(skb_in, GFP_ATOMIC); -			skb->len = temp; -			skb->data = ((u8 *)skb_in->data) + offset; -			skb_set_tail_pointer(skb, temp); +			/* create a fresh copy to reduce truesize */ +			skb = netdev_alloc_skb_ip_align(dev->net,  len); +			if (!skb) +				goto error; +			memcpy(skb_put(skb, len), skb_in->data + offset, len);  			usbnet_skb_return(dev, skb); +			payload += len;	/* count payload bytes in this NTB */  		}  	} +err_ndp: +	/* are there more NDPs to process? */ +	ndpoffset = le16_to_cpu(ndp16->wNextNdpIndex); +	if (ndpoffset && loopcount--) +		goto next_ndp; + +	/* update stats */ +	ctx->rx_overhead += skb_in->len - payload; +	ctx->rx_ntbs++; +  	return 1;  error:  	return 0;  } +EXPORT_SYMBOL_GPL(cdc_ncm_rx_fixup);  static void -cdc_ncm_speed_change(struct cdc_ncm_ctx *ctx, -		     struct connection_speed_change *data) +cdc_ncm_speed_change(struct usbnet *dev, +		     struct usb_cdc_speed_change *data)  { -	uint32_t rx_speed = le32_to_cpu(data->USBitRate); -	uint32_t tx_speed = le32_to_cpu(data->DSBitRate); +	uint32_t rx_speed = le32_to_cpu(data->DLBitRRate); +	uint32_t tx_speed = le32_to_cpu(data->ULBitRate);  	/*  	 * Currently the USB-NET API does not support reporting the actual  	 * device speed. Do print it instead.  	 */ -	if ((tx_speed != ctx->tx_speed) || (rx_speed != ctx->rx_speed)) { -		ctx->tx_speed = tx_speed; -		ctx->rx_speed = rx_speed; - -		if ((tx_speed > 1000000) && (rx_speed > 1000000)) { -			printk(KERN_INFO KBUILD_MODNAME -				": %s: %u mbit/s downlink " -				"%u mbit/s uplink\n", -				ctx->netdev->name, -				(unsigned int)(rx_speed / 1000000U), -				(unsigned int)(tx_speed / 1000000U)); -		} else { -			printk(KERN_INFO KBUILD_MODNAME -				": %s: %u kbit/s downlink " -				"%u kbit/s uplink\n", -				ctx->netdev->name, -				(unsigned int)(rx_speed / 1000U), -				(unsigned int)(tx_speed / 1000U)); -		} +	if ((tx_speed > 1000000) && (rx_speed > 1000000)) { +		netif_info(dev, link, dev->net, +			   "%u mbit/s downlink %u mbit/s uplink\n", +			   (unsigned int)(rx_speed / 1000000U), +			   (unsigned int)(tx_speed / 1000000U)); +	} else { +		netif_info(dev, link, dev->net, +			   "%u kbit/s downlink %u kbit/s uplink\n", +			   (unsigned int)(rx_speed / 1000U), +			   (unsigned int)(tx_speed / 1000U));  	}  } @@ -1084,8 +1467,8 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb)  	/* test for split data in 8-byte chunks */  	if (test_and_clear_bit(EVENT_STS_SPLIT, &dev->flags)) { -		cdc_ncm_speed_change(ctx, -		      (struct connection_speed_change *)urb->transfer_buffer); +		cdc_ncm_speed_change(dev, +		      (struct usb_cdc_speed_change *)urb->transfer_buffer);  		return;  	} @@ -1098,115 +1481,135 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb)  		 * USB_CDC_NOTIFY_NETWORK_CONNECTION notification shall be  		 * sent by device after USB_CDC_NOTIFY_SPEED_CHANGE.  		 */ -		ctx->connected = event->wValue; - -		printk(KERN_INFO KBUILD_MODNAME ": %s: network connection:" -			" %sconnected\n", -			ctx->netdev->name, ctx->connected ? "" : "dis"); - -		if (ctx->connected) -			netif_carrier_on(dev->net); -		else { -			netif_carrier_off(dev->net); -			ctx->tx_speed = ctx->rx_speed = 0; -		} +		netif_info(dev, link, dev->net, +			   "network connection: %sconnected\n", +			   !!event->wValue ? "" : "dis"); +		usbnet_link_change(dev, !!event->wValue, 0);  		break;  	case USB_CDC_NOTIFY_SPEED_CHANGE: -		if (urb->actual_length < -		    (sizeof(*event) + sizeof(struct connection_speed_change))) +		if (urb->actual_length < (sizeof(*event) + +					sizeof(struct usb_cdc_speed_change)))  			set_bit(EVENT_STS_SPLIT, &dev->flags);  		else -			cdc_ncm_speed_change(ctx, -				(struct connection_speed_change *) &event[1]); +			cdc_ncm_speed_change(dev, +					     (struct usb_cdc_speed_change *)&event[1]);  		break;  	default: -		dev_err(&dev->udev->dev, "NCM: unexpected " -			"notification 0x%02x!\n", event->bNotificationType); +		dev_dbg(&dev->udev->dev, +			"NCM: unexpected notification 0x%02x!\n", +			event->bNotificationType);  		break;  	}  } -static int cdc_ncm_check_connect(struct usbnet *dev) -{ -	struct cdc_ncm_ctx *ctx; +static const struct driver_info cdc_ncm_info = { +	.description = "CDC NCM", +	.flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT | FLAG_MULTI_PACKET, +	.bind = cdc_ncm_bind, +	.unbind = cdc_ncm_unbind, +	.manage_power = usbnet_manage_power, +	.status = cdc_ncm_status, +	.rx_fixup = cdc_ncm_rx_fixup, +	.tx_fixup = cdc_ncm_tx_fixup, +}; -	ctx = (struct cdc_ncm_ctx *)dev->data[0]; -	if (ctx == NULL) -		return 1;	/* disconnected */ +/* Same as cdc_ncm_info, but with FLAG_WWAN */ +static const struct driver_info wwan_info = { +	.description = "Mobile Broadband Network Device", +	.flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT | FLAG_MULTI_PACKET +			| FLAG_WWAN, +	.bind = cdc_ncm_bind, +	.unbind = cdc_ncm_unbind, +	.manage_power = usbnet_manage_power, +	.status = cdc_ncm_status, +	.rx_fixup = cdc_ncm_rx_fixup, +	.tx_fixup = cdc_ncm_tx_fixup, +}; -	return !ctx->connected; -} +/* Same as wwan_info, but with FLAG_NOARP  */ +static const struct driver_info wwan_noarp_info = { +	.description = "Mobile Broadband Network Device (NO ARP)", +	.flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT | FLAG_MULTI_PACKET +			| FLAG_WWAN | FLAG_NOARP, +	.bind = cdc_ncm_bind, +	.unbind = cdc_ncm_unbind, +	.manage_power = usbnet_manage_power, +	.status = cdc_ncm_status, +	.rx_fixup = cdc_ncm_rx_fixup, +	.tx_fixup = cdc_ncm_tx_fixup, +}; -static int -cdc_ncm_probe(struct usb_interface *udev, const struct usb_device_id *prod) -{ -	return usbnet_probe(udev, prod); -} +static const struct usb_device_id cdc_devs[] = { +	/* Ericsson MBM devices like F5521gw */ +	{ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO +		| USB_DEVICE_ID_MATCH_VENDOR, +	  .idVendor = 0x0bdb, +	  .bInterfaceClass = USB_CLASS_COMM, +	  .bInterfaceSubClass = USB_CDC_SUBCLASS_NCM, +	  .bInterfaceProtocol = USB_CDC_PROTO_NONE, +	  .driver_info = (unsigned long) &wwan_info, +	}, -static void cdc_ncm_disconnect(struct usb_interface *intf) -{ -	struct usbnet *dev = usb_get_intfdata(intf); +	/* Dell branded MBM devices like DW5550 */ +	{ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO +		| USB_DEVICE_ID_MATCH_VENDOR, +	  .idVendor = 0x413c, +	  .bInterfaceClass = USB_CLASS_COMM, +	  .bInterfaceSubClass = USB_CDC_SUBCLASS_NCM, +	  .bInterfaceProtocol = USB_CDC_PROTO_NONE, +	  .driver_info = (unsigned long) &wwan_info, +	}, -	if (dev == NULL) -		return;		/* already disconnected */ +	/* Toshiba branded MBM devices */ +	{ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO +		| USB_DEVICE_ID_MATCH_VENDOR, +	  .idVendor = 0x0930, +	  .bInterfaceClass = USB_CLASS_COMM, +	  .bInterfaceSubClass = USB_CDC_SUBCLASS_NCM, +	  .bInterfaceProtocol = USB_CDC_PROTO_NONE, +	  .driver_info = (unsigned long) &wwan_info, +	}, -	usbnet_disconnect(intf); -} +	/* tag Huawei devices as wwan */ +	{ USB_VENDOR_AND_INTERFACE_INFO(0x12d1, +					USB_CLASS_COMM, +					USB_CDC_SUBCLASS_NCM, +					USB_CDC_PROTO_NONE), +	  .driver_info = (unsigned long)&wwan_info, +	}, -static int cdc_ncm_manage_power(struct usbnet *dev, int status) -{ -	dev->intf->needs_remote_wakeup = status; -	return 0; -} +	/* Infineon(now Intel) HSPA Modem platform */ +	{ USB_DEVICE_AND_INTERFACE_INFO(0x1519, 0x0443, +		USB_CLASS_COMM, +		USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE), +	  .driver_info = (unsigned long)&wwan_noarp_info, +	}, -static const struct driver_info cdc_ncm_info = { -	.description = "CDC NCM", -	.flags = FLAG_NO_SETINT | FLAG_MULTI_PACKET, -	.bind = cdc_ncm_bind, -	.unbind = cdc_ncm_unbind, -	.check_connect = cdc_ncm_check_connect, -	.manage_power = cdc_ncm_manage_power, -	.status = cdc_ncm_status, -	.rx_fixup = cdc_ncm_rx_fixup, -	.tx_fixup = cdc_ncm_tx_fixup, +	/* Generic CDC-NCM devices */ +	{ USB_INTERFACE_INFO(USB_CLASS_COMM, +		USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE), +		.driver_info = (unsigned long)&cdc_ncm_info, +	}, +	{ +	},  }; +MODULE_DEVICE_TABLE(usb, cdc_devs);  static struct usb_driver cdc_ncm_driver = {  	.name = "cdc_ncm",  	.id_table = cdc_devs, -	.probe = cdc_ncm_probe, -	.disconnect = cdc_ncm_disconnect, +	.probe = usbnet_probe, +	.disconnect = usbnet_disconnect,  	.suspend = usbnet_suspend,  	.resume = usbnet_resume, +	.reset_resume =	usbnet_resume,  	.supports_autosuspend = 1, +	.disable_hub_initiated_lpm = 1,  }; -static struct ethtool_ops cdc_ncm_ethtool_ops = { -	.get_drvinfo = cdc_ncm_get_drvinfo, -	.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 int __init cdc_ncm_init(void) -{ -	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION "\n"); -	return usb_register(&cdc_ncm_driver); -} - -module_init(cdc_ncm_init); - -static void __exit cdc_ncm_exit(void) -{ -	usb_deregister(&cdc_ncm_driver); -} - -module_exit(cdc_ncm_exit); +module_usb_driver(cdc_ncm_driver);  MODULE_AUTHOR("Hans Petter Selasky");  MODULE_DESCRIPTION("USB CDC NCM host driver"); diff --git a/drivers/net/usb/cdc_subset.c b/drivers/net/usb/cdc_subset.c index ca39ace0b0e..91f0919fe27 100644 --- a/drivers/net/usb/cdc_subset.c +++ b/drivers/net/usb/cdc_subset.c @@ -13,13 +13,11 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  #include <linux/module.h>  #include <linux/kmod.h> -#include <linux/init.h>  #include <linux/netdevice.h>  #include <linux/etherdevice.h>  #include <linux/ethtool.h> @@ -89,6 +87,7 @@ static int always_connected (struct usbnet *dev)  static const struct driver_info	ali_m5632_info = {  	.description =	"ALi M5632", +	.flags       = FLAG_POINTTOPOINT,  };  #endif @@ -110,6 +109,7 @@ static const struct driver_info	ali_m5632_info = {  static const struct driver_info	an2720_info = {  	.description =	"AnchorChips/Cypress 2720", +	.flags       = FLAG_POINTTOPOINT,  	// no reset available!  	// no check_connect available! @@ -132,6 +132,7 @@ static const struct driver_info	an2720_info = {  static const struct driver_info	belkin_info = {  	.description =	"Belkin, eTEK, or compatible", +	.flags       = FLAG_POINTTOPOINT,  };  #endif	/* CONFIG_USB_BELKIN */ @@ -157,6 +158,7 @@ static const struct driver_info	belkin_info = {  static const struct driver_info	epson2888_info = {  	.description =	"Epson USB Device",  	.check_connect = always_connected, +	.flags = FLAG_POINTTOPOINT,  	.in = 4, .out = 3,  }; @@ -173,6 +175,7 @@ static const struct driver_info	epson2888_info = {  #define HAVE_HARDWARE  static const struct driver_info kc2190_info = {  	.description =  "KC Technology KC-190", +	.flags = FLAG_POINTTOPOINT,  };  #endif /* CONFIG_USB_KC2190 */ @@ -200,16 +203,19 @@ static const struct driver_info kc2190_info = {  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 */ @@ -328,19 +334,10 @@ static struct usb_driver cdc_subset_driver = {  	.resume =	usbnet_resume,  	.disconnect =	usbnet_disconnect,  	.id_table =	products, +	.disable_hub_initiated_lpm = 1,  }; -static int __init cdc_subset_init(void) -{ -	return usb_register(&cdc_subset_driver); -} -module_init(cdc_subset_init); - -static void __exit cdc_subset_exit(void) -{ -	usb_deregister(&cdc_subset_driver); -} -module_exit(cdc_subset_exit); +module_usb_driver(cdc_subset_driver);  MODULE_AUTHOR("David Brownell");  MODULE_DESCRIPTION("Simple 'CDC Subset' USB networking links"); diff --git a/drivers/net/usb/cx82310_eth.c b/drivers/net/usb/cx82310_eth.c index 8969f124c18..3eed708a618 100644 --- a/drivers/net/usb/cx82310_eth.c +++ b/drivers/net/usb/cx82310_eth.c @@ -14,12 +14,10 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  #include <linux/module.h> -#include <linux/init.h>  #include <linux/netdevice.h>  #include <linux/etherdevice.h>  #include <linux/ethtool.h> @@ -302,18 +300,9 @@ static const struct driver_info	cx82310_info = {  	.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), +		USB_DEVICE_AND_INTERFACE_INFO(0x0572, 0xcb01, 0xff, 0, 0),  		.driver_info = (unsigned long) &cx82310_info  	},  	{ }, @@ -327,19 +316,10 @@ static struct usb_driver cx82310_driver = {  	.disconnect	= usbnet_disconnect,  	.suspend	= usbnet_suspend,  	.resume		= usbnet_resume, +	.disable_hub_initiated_lpm = 1,  }; -static int __init cx82310_init(void) -{ -	return usb_register(&cx82310_driver); -} -module_init(cx82310_init); - -static void __exit cx82310_exit(void) -{ -	usb_deregister(&cx82310_driver); -} -module_exit(cx82310_exit); +module_usb_driver(cx82310_driver);  MODULE_AUTHOR("Ondrej Zary");  MODULE_DESCRIPTION("Conexant CX82310-based ADSL router USB ethernet driver"); diff --git a/drivers/net/usb/dm9601.c b/drivers/net/usb/dm9601.c index 02b622e3b9f..6e9c344c7a2 100644 --- a/drivers/net/usb/dm9601.c +++ b/drivers/net/usb/dm9601.c @@ -1,5 +1,5 @@  /* - * Davicom DM9601 USB 1.1 10/100Mbps ethernet devices + * Davicom DM96xx USB 10/100Mbps ethernet devices   *   * Peter Korsgaard <jacmet@sunsite.dk>   * @@ -13,7 +13,6 @@  #include <linux/module.h>  #include <linux/sched.h>  #include <linux/stddef.h> -#include <linux/init.h>  #include <linux/netdevice.h>  #include <linux/etherdevice.h>  #include <linux/ethtool.h> @@ -45,6 +44,12 @@  #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 @@ -53,30 +58,14 @@  #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)  { -	void *buf; -	int err = -ENOMEM; - -	netdev_dbg(dev->net, "dm_read() reg=0x%02x length=%d\n", reg, length); - -	buf = kmalloc(length, GFP_KERNEL); -	if (!buf) -		goto out; - -	err = usb_control_msg(dev->udev, -			      usb_rcvctrlpipe(dev->udev, 0), -			      DM_READ_REGS, -			      USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, -			      0, reg, buf, length, USB_CTRL_SET_TIMEOUT); -	if (err == length) -		memcpy(data, buf, length); -	else if (err >= 0) +	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; -	kfree(buf); - - out:  	return err;  } @@ -87,106 +76,35 @@ static int dm_read_reg(struct usbnet *dev, u8 reg, u8 *value)  static int dm_write(struct usbnet *dev, u8 reg, u16 length, void *data)  { -	void *buf = NULL; -	int err = -ENOMEM; - -	netdev_dbg(dev->net, "dm_write() reg=0x%02x, length=%d\n", reg, length); +	int err; +	err = usbnet_write_cmd(dev, DM_WRITE_REGS, +				USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, +				0, reg, data, length); -	if (data) { -		buf = kmemdup(data, length, GFP_KERNEL); -		if (!buf) -			goto out; -	} - -	err = usb_control_msg(dev->udev, -			      usb_sndctrlpipe(dev->udev, 0), -			      DM_WRITE_REGS, -			      USB_DIR_OUT | USB_TYPE_VENDOR |USB_RECIP_DEVICE, -			      0, reg, buf, length, USB_CTRL_SET_TIMEOUT); -	kfree(buf);  	if (err >= 0 && err < length)  		err = -EINVAL; - out:  	return err;  }  static int dm_write_reg(struct usbnet *dev, u8 reg, u8 value)  { -	netdev_dbg(dev->net, "dm_write_reg() reg=0x%02x, value=0x%02x\n", -		   reg, value); -	return usb_control_msg(dev->udev, -			       usb_sndctrlpipe(dev->udev, 0), -			       DM_WRITE_REG, -			       USB_DIR_OUT | USB_TYPE_VENDOR |USB_RECIP_DEVICE, -			       value, reg, NULL, 0, USB_CTRL_SET_TIMEOUT); -} - -static void dm_write_async_callback(struct urb *urb) -{ -	struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context; -	int status = urb->status; - -	if (status < 0) -		printk(KERN_DEBUG "dm_write_async_callback() failed with %d\n", -		       status); - -	kfree(req); -	usb_free_urb(urb); -} - -static void dm_write_async_helper(struct usbnet *dev, u8 reg, u8 value, -				  u16 length, void *data) -{ -	struct usb_ctrlrequest *req; -	struct urb *urb; -	int status; - -	urb = usb_alloc_urb(0, GFP_ATOMIC); -	if (!urb) { -		netdev_err(dev->net, "Error allocating URB in dm_write_async_helper!\n"); -		return; -	} - -	req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC); -	if (!req) { -		netdev_err(dev->net, "Failed to allocate memory for control request\n"); -		usb_free_urb(urb); -		return; -	} - -	req->bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE; -	req->bRequest = length ? DM_WRITE_REGS : DM_WRITE_REG; -	req->wValue = cpu_to_le16(value); -	req->wIndex = cpu_to_le16(reg); -	req->wLength = cpu_to_le16(length); - -	usb_fill_control_urb(urb, dev->udev, -			     usb_sndctrlpipe(dev->udev, 0), -			     (void *)req, data, length, -			     dm_write_async_callback, req); - -	status = usb_submit_urb(urb, GFP_ATOMIC); -	if (status < 0) { -		netdev_err(dev->net, "Error submitting the control message: status=%d\n", -			   status); -		kfree(req); -		usb_free_urb(urb); -	} +	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)  { -	netdev_dbg(dev->net, "dm_write_async() reg=0x%02x length=%d\n", reg, length); - -	dm_write_async_helper(dev, reg, 0, length, 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)  { -	netdev_dbg(dev->net, "dm_write_reg_async() reg=0x%02x value=0x%02x\n", -		   reg, value); - -	dm_write_async_helper(dev, reg, value, 0, NULL); +	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) @@ -199,7 +117,7 @@ static int dm_read_shared_word(struct usbnet *dev, int phy, u8 reg, __le16 *valu  	dm_write_reg(dev, DM_SHARED_CTRL, phy ? 0xc : 0x4);  	for (i = 0; i < DM_TIMEOUT; i++) { -		u8 tmp; +		u8 tmp = 0;  		udelay(1);  		ret = dm_read_reg(dev, DM_SHARED_CTRL, &tmp); @@ -242,7 +160,7 @@ static int dm_write_shared_word(struct usbnet *dev, int phy, u8 reg, __le16 valu  	dm_write_reg(dev, DM_SHARED_CTRL, phy ? 0x1a : 0x12);  	for (i = 0; i < DM_TIMEOUT; i++) { -		u8 tmp; +		u8 tmp = 0;  		udelay(1);  		ret = dm_read_reg(dev, DM_SHARED_CTRL, &tmp); @@ -384,7 +302,7 @@ static void dm9601_set_multicast(struct net_device *net)  		rx_ctl |= 0x02;  	} else if (net->flags & IFF_ALLMULTI ||  		   netdev_mc_count(net) > DM_MAX_MCAST) { -		rx_ctl |= 0x04; +		rx_ctl |= 0x08;  	} else if (!netdev_mc_empty(net)) {  		struct netdev_hw_addr *ha; @@ -428,14 +346,14 @@ static const struct net_device_ops dm9601_netdev_ops = {  	.ndo_change_mtu		= usbnet_change_mtu,  	.ndo_validate_addr	= eth_validate_addr,  	.ndo_do_ioctl 		= dm9601_ioctl, -	.ndo_set_multicast_list = dm9601_set_multicast, +	.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]; +	u8 mac[ETH_ALEN], id;  	ret = usbnet_get_endpoints(dev, intf);  	if (ret) @@ -445,7 +363,12 @@ static int dm9601_bind(struct usbnet *dev, struct usb_interface *intf)  	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; -	dev->rx_urb_size = dev->net->mtu + ETH_HLEN + DM_RX_OVERHEAD; + +	/* 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; @@ -476,6 +399,24 @@ static int dm9601_bind(struct usbnet *dev, struct usb_interface *intf)  		__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); @@ -531,7 +472,7 @@ static int dm9601_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  static struct sk_buff *dm9601_tx_fixup(struct usbnet *dev, struct sk_buff *skb,  				       gfp_t flags)  { -	int len; +	int len, pad;  	/* format:  	   b1: packet length low @@ -539,12 +480,23 @@ static struct sk_buff *dm9601_tx_fixup(struct usbnet *dev, struct sk_buff *skb,  	   b3..n: packet data  	*/ -	len = skb->len; +	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) { +	if (skb_headroom(skb) < DM_TX_OVERHEAD || skb_tailroom(skb) < pad) {  		struct sk_buff *skb2; -		skb2 = skb_copy_expand(skb, DM_TX_OVERHEAD, 0, flags); +		skb2 = skb_copy_expand(skb, DM_TX_OVERHEAD, pad, flags);  		dev_kfree_skb_any(skb);  		skb = skb2;  		if (!skb) @@ -553,10 +505,10 @@ static struct sk_buff *dm9601_tx_fixup(struct usbnet *dev, struct sk_buff *skb,  	__skb_push(skb, DM_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++; +	if (pad) { +		memset(skb->data + skb->len, 0, pad); +		__skb_put(skb, pad); +	}  	skb->data[0] = len;  	skb->data[1] = len >> 8; @@ -587,31 +539,26 @@ static void dm9601_status(struct usbnet *dev, struct urb *urb)  	link = !!(buf[0] & 0x40);  	if (netif_carrier_ok(dev->net) != link) { -		if (link) { -			netif_carrier_on(dev->net); -			usbnet_defer_kevent (dev, EVENT_LINK_RESET); -		} -		else -			netif_carrier_off(dev->net); +		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; +	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: %d duplex: %d\n", -		   ecmd.speed, ecmd.duplex); +	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 DM9601 USB Ethernet", +	.description	= "Davicom DM96xx USB 10/100 Ethernet",  	.flags		= FLAG_ETHER | FLAG_LINK_INTR,  	.bind		= dm9601_bind,  	.rx_fixup	= dm9601_rx_fixup, @@ -651,9 +598,33 @@ static const struct usb_device_id products[] = {  	.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  }; @@ -666,21 +637,11 @@ static struct usb_driver dm9601_driver = {  	.disconnect = usbnet_disconnect,  	.suspend = usbnet_suspend,  	.resume = usbnet_resume, +	.disable_hub_initiated_lpm = 1,  }; -static int __init dm9601_init(void) -{ -	return usb_register(&dm9601_driver); -} - -static void __exit dm9601_exit(void) -{ -	usb_deregister(&dm9601_driver); -} - -module_init(dm9601_init); -module_exit(dm9601_exit); +module_usb_driver(dm9601_driver);  MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>"); -MODULE_DESCRIPTION("Davicom DM9601 USB 1.1 ethernet devices"); +MODULE_DESCRIPTION("Davicom DM96xx USB 10/100 ethernet devices");  MODULE_LICENSE("GPL"); diff --git a/drivers/net/usb/gl620a.c b/drivers/net/usb/gl620a.c index dcd57c37ef7..1cc24e6f23e 100644 --- a/drivers/net/usb/gl620a.c +++ b/drivers/net/usb/gl620a.c @@ -14,15 +14,13 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  // #define	DEBUG			// error path messages, extra info  // #define	VERBOSE			// more; success messages  #include <linux/module.h> -#include <linux/init.h>  #include <linux/netdevice.h>  #include <linux/etherdevice.h>  #include <linux/ethtool.h> @@ -86,12 +84,18 @@ static int genelink_rx_fixup(struct usbnet *dev, struct sk_buff *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) { -		dbg("genelink: invalid received packet count %u", count); +		netdev_dbg(dev->net, +			   "genelink: invalid received packet count %u\n", +			   count);  		return 0;  	} @@ -107,7 +111,8 @@ static int genelink_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  		// this may be a broken packet  		if (size > GL_MAX_PACKET_LEN) { -			dbg("genelink: invalid rx length %d", size); +			netdev_dbg(dev->net, "genelink: invalid rx length %d\n", +				   size);  			return 0;  		} @@ -133,7 +138,8 @@ static int genelink_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  	skb_pull(skb, 4);  	if (skb->len > GL_MAX_PACKET_LEN) { -		dbg("genelink: invalid rx length %d", skb->len); +		netdev_dbg(dev->net, "genelink: invalid rx length %d\n", +			   skb->len);  		return 0;  	}  	return 1; @@ -193,7 +199,7 @@ static int genelink_bind(struct usbnet *dev, struct usb_interface *intf)  static const struct driver_info	genelink_info = {  	.description =	"Genesys GeneLink", -	.flags =	FLAG_FRAMING_GL | FLAG_NO_SETINT, +	.flags =	FLAG_POINTTOPOINT | FLAG_FRAMING_GL | FLAG_NO_SETINT,  	.bind =		genelink_bind,  	.rx_fixup =	genelink_rx_fixup,  	.tx_fixup =	genelink_tx_fixup, @@ -225,19 +231,10 @@ static struct usb_driver gl620a_driver = {  	.disconnect =	usbnet_disconnect,  	.suspend =	usbnet_suspend,  	.resume =	usbnet_resume, +	.disable_hub_initiated_lpm = 1,  }; -static int __init usbnet_init(void) -{ - 	return usb_register(&gl620a_driver); -} -module_init(usbnet_init); - -static void __exit usbnet_exit(void) -{ - 	usb_deregister(&gl620a_driver); -} -module_exit(usbnet_exit); +module_usb_driver(gl620a_driver);  MODULE_AUTHOR("Jiun-Jie Huang");  MODULE_DESCRIPTION("GL620-USB-A Host-to-Host Link cables"); diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index be8cc2a8e21..a4272ed62da 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -106,13 +106,6 @@  #define MAX_RX_URBS			2 -static inline struct hso_serial *get_serial_by_tty(struct tty_struct *tty) -{ -	if (tty) -		return tty->driver_data; -	return NULL; -} -  /*****************************************************************************/  /* Debugging functions                                                       */  /*****************************************************************************/ @@ -192,7 +185,6 @@ enum rx_ctrl_state{  #define BM_REQUEST_TYPE (0xa1)  #define B_NOTIFICATION  (0x20)  #define W_VALUE         (0x0) -#define W_INDEX         (0x2)  #define W_LENGTH        (0x2)  #define B_OVERRUN       (0x1<<6) @@ -255,9 +247,8 @@ struct hso_serial {  	u8 dtr_state;  	unsigned tx_urb_used:1; +	struct tty_port port;  	/* from usb_serial_port */ -	struct tty_struct *tty; -	int open_count;  	spinlock_t serial_lock;  	int (*write_data) (struct hso_serial *serial); @@ -267,10 +258,8 @@ struct hso_serial {  	 * so as not to drop characters on the floor.  	 */  	int  curr_rx_urb_idx; -	u16  curr_rx_urb_offset;  	u8   rx_urb_filled[MAX_RX_URBS];  	struct tasklet_struct unthrottle_tasklet; -	struct work_struct    retry_unthrottle_workqueue;  };  struct hso_device { @@ -324,7 +313,7 @@ struct hso_device {  /* Prototypes                                                                */  /*****************************************************************************/  /* Serial driver functions */ -static int hso_serial_tiocmset(struct tty_struct *tty, struct file *file, +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); @@ -958,10 +947,6 @@ static void packetizeRx(struct hso_net *odev, unsigned char *ip_pkt,  				/* Packet is complete. Inject into stack. */  				/* We have IP packet here */  				odev->skb_rx_buf->protocol = cpu_to_be16(ETH_P_IP); -				/* don't check it */ -				odev->skb_rx_buf->ip_summed = -					CHECKSUM_UNNECESSARY; -  				skb_reset_mac_header(odev->skb_rx_buf);  				/* Ship it off to the kernel */ @@ -1001,6 +986,18 @@ static void packetizeRx(struct hso_net *odev, unsigned char *ip_pkt,  	}  } +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)  { @@ -1029,17 +1026,8 @@ static void read_bulk_callback(struct urb *urb)  		return;  	} -	if (odev->parent->port_spec & HSO_INFO_CRC_BUG) { -		u32 rest; -		u8 crc_check[4] = { 0xDE, 0xAD, 0xBE, 0xEF }; -		rest = urb->actual_length % -			le16_to_cpu(odev->in_endp->wMaxPacketSize); -		if (((rest == 5) || (rest == 6)) && -		    !memcmp(((u8 *) urb->transfer_buffer) + -			    urb->actual_length - 4, crc_check, 4)) { -			urb->actual_length -= 4; -		} -	} +	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) { @@ -1115,8 +1103,7 @@ static void hso_init_termios(struct ktermios *termios)  static void _hso_serial_set_termios(struct tty_struct *tty,  				    struct ktermios *old)  { -	struct hso_serial *serial = get_serial_by_tty(tty); -	struct ktermios *termios; +	struct hso_serial *serial = tty->driver_data;  	if (!serial) {  		printk(KERN_ERR "%s: no tty structures", __func__); @@ -1128,16 +1115,15 @@ static void _hso_serial_set_termios(struct tty_struct *tty,  	/*  	 *	Fix up unsupported bits  	 */ -	termios = tty->termios; -	termios->c_iflag &= ~IXON; /* disable enable XON/XOFF flow control */ +	tty->termios.c_iflag &= ~IXON; /* disable enable XON/XOFF flow control */ -	termios->c_cflag &= +	tty->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 */ +	tty->termios.c_cflag |= CS8;	/* character size 8 bits */  	/* baud rate 115200 */  	tty_encode_baud_rate(tty, 115200, 115200); @@ -1191,7 +1177,7 @@ static void put_rxbuf_data_and_resubmit_ctrl_urb(struct hso_serial *serial)  	struct urb *urb;  	urb = serial->rx_urb[0]; -	if (serial->open_count > 0) { +	if (serial->port.count > 0) {  		count = put_rxbuf_data(urb, serial);  		if (count == -1)  			return; @@ -1213,52 +1199,32 @@ 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; -	} else if (status) { +	} +	if (status) {  		handle_usb_error(status, __func__, serial->parent);  		return;  	} -	D4("\n--- Got serial_read_bulk callback %02x ---", status);  	D1("Actual length = %d\n", urb->actual_length);  	DUMP1(urb->transfer_buffer, urb->actual_length);  	/* Anyone listening? */ -	if (serial->open_count == 0) +	if (serial->port.count == 0)  		return; -	if (status == 0) { -		if (serial->parent->port_spec & HSO_INFO_CRC_BUG) { -			u32 rest; -			u8 crc_check[4] = { 0xDE, 0xAD, 0xBE, 0xEF }; -			rest = -			    urb->actual_length % -			    le16_to_cpu(serial->in_endp->wMaxPacketSize); -			if (((rest == 5) || (rest == 6)) && -			    !memcmp(((u8 *) urb->transfer_buffer) + -				    urb->actual_length - 4, crc_check, 4)) { -				urb->actual_length -= 4; -			} -		} -		/* 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); -	} else if (status == -ENOENT || status == -ECONNRESET) { -		/* Unlinked - check for throttled port. */ -		D2("Port %d, successfully unlinked urb", serial->minor); -		spin_lock(&serial->serial_lock); -		serial->rx_urb_filled[hso_urb_to_index(serial, urb)] = 0; -		hso_resubmit_rx_bulk_urb(serial, urb); -		spin_unlock(&serial->serial_lock); -	} else { -		D2("Port %d, status = %d for read urb", serial->minor, status); -		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);  }  /* @@ -1279,19 +1245,11 @@ static void hso_unthrottle_tasklet(struct hso_serial *serial)  static	void hso_unthrottle(struct tty_struct *tty)  { -	struct hso_serial *serial = get_serial_by_tty(tty); +	struct hso_serial *serial = tty->driver_data;  	tasklet_hi_schedule(&serial->unthrottle_tasklet);  } -static void hso_unthrottle_workfunc(struct work_struct *work) -{ -	struct hso_serial *serial = -	    container_of(work, struct hso_serial, -			 retry_unthrottle_workqueue); -	hso_unthrottle_tasklet(serial); -} -  /* open the requested serial port */  static int hso_serial_open(struct tty_struct *tty, struct file *filp)  { @@ -1315,27 +1273,22 @@ static int hso_serial_open(struct tty_struct *tty, struct file *filp)  	kref_get(&serial->parent->ref);  	/* setup */ -	spin_lock_irq(&serial->serial_lock);  	tty->driver_data = serial; -	tty_kref_put(serial->tty); -	serial->tty = tty_kref_get(tty); -	spin_unlock_irq(&serial->serial_lock); +	tty_port_tty_set(&serial->port, tty);  	/* check for port already opened, if not set the termios */ -	serial->open_count++; -	if (serial->open_count == 1) { +	serial->port.count++; +	if (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); -		INIT_WORK(&serial->retry_unthrottle_workqueue, -			  hso_unthrottle_workfunc);  		result = hso_start_serial_device(serial->parent, GFP_KERNEL);  		if (result) {  			hso_stop_serial_device(serial->parent); -			serial->open_count--; +			serial->port.count--;  			kref_put(&serial->parent->ref, hso_serial_ref_free);  		}  	} else { @@ -1346,7 +1299,7 @@ static int hso_serial_open(struct tty_struct *tty, struct file *filp)  	/* done */  	if (result) -		hso_serial_tiocmset(tty, NULL, TIOCM_RTS | TIOCM_DTR, 0); +		hso_serial_tiocmset(tty, TIOCM_RTS | TIOCM_DTR, 0);  err_out:  	mutex_unlock(&serial->parent->mutex);  	return result; @@ -1372,21 +1325,14 @@ static void hso_serial_close(struct tty_struct *tty, struct file *filp)  	/* reset the rts and dtr */  	/* do the actual close */ -	serial->open_count--; +	serial->port.count--; -	if (serial->open_count <= 0) { -		serial->open_count = 0; -		spin_lock_irq(&serial->serial_lock); -		if (serial->tty == tty) { -			serial->tty->driver_data = NULL; -			serial->tty = NULL; -			tty_kref_put(tty); -		} -		spin_unlock_irq(&serial->serial_lock); +	if (serial->port.count <= 0) { +		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); -		cancel_work_sync(&serial->retry_unthrottle_workqueue);  	}  	if (!usb_gone) @@ -1401,7 +1347,7 @@ static void hso_serial_close(struct tty_struct *tty, struct file *filp)  static int hso_serial_write(struct tty_struct *tty, const unsigned char *buf,  			    int count)  { -	struct hso_serial *serial = get_serial_by_tty(tty); +	struct hso_serial *serial = tty->driver_data;  	int space, tx_bytes;  	unsigned long flags; @@ -1433,7 +1379,7 @@ out:  /* how much room is there for writing */  static int hso_serial_write_room(struct tty_struct *tty)  { -	struct hso_serial *serial = get_serial_by_tty(tty); +	struct hso_serial *serial = tty->driver_data;  	int room;  	unsigned long flags; @@ -1448,19 +1394,19 @@ static int hso_serial_write_room(struct tty_struct *tty)  /* setup the term */  static void hso_serial_set_termios(struct tty_struct *tty, struct ktermios *old)  { -	struct hso_serial *serial = get_serial_by_tty(tty); +	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); +		   tty->termios.c_cflag, old->c_cflag);  	/* the actual setup */  	spin_lock_irqsave(&serial->serial_lock, flags); -	if (serial->open_count) +	if (serial->port.count)  		_hso_serial_set_termios(tty, old);  	else -		tty->termios = old; +		tty->termios = *old;  	spin_unlock_irqrestore(&serial->serial_lock, flags);  	/* done */ @@ -1469,7 +1415,7 @@ static void hso_serial_set_termios(struct tty_struct *tty, struct ktermios *old)  /* how many characters in the buffer */  static int hso_serial_chars_in_buffer(struct tty_struct *tty)  { -	struct hso_serial *serial = get_serial_by_tty(tty); +	struct hso_serial *serial = tty->driver_data;  	int chars;  	unsigned long flags; @@ -1517,6 +1463,7 @@ static void tiocmget_intr_callback(struct urb *urb)  	struct uart_icount *icount;  	struct hso_serial_state_notification *serial_state_notification;  	struct usb_device *usb; +	int if_num;  	/* Sanity checks */  	if (!serial) @@ -1525,15 +1472,24 @@ static void tiocmget_intr_callback(struct urb *urb)  		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; +	if_num = serial->parent->interface->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) != W_INDEX || +	    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"); @@ -1640,10 +1596,10 @@ static int hso_get_count(struct tty_struct *tty,  		  struct serial_icounter_struct *icount)  {  	struct uart_icount cnow; -	struct hso_serial *serial = get_serial_by_tty(tty); +	struct hso_serial *serial = tty->driver_data;  	struct hso_tiocmget  *tiocmget = serial->tiocmget; -	memset(&icount, 0, sizeof(struct serial_icounter_struct)); +	memset(icount, 0, sizeof(struct serial_icounter_struct));  	if (!tiocmget)  		 return -ENOENT; @@ -1667,10 +1623,10 @@ static int hso_get_count(struct tty_struct *tty,  } -static int hso_serial_tiocmget(struct tty_struct *tty, struct file *file) +static int hso_serial_tiocmget(struct tty_struct *tty)  {  	int retval; -	struct hso_serial *serial = get_serial_by_tty(tty); +	struct hso_serial *serial = tty->driver_data;  	struct hso_tiocmget  *tiocmget;  	u16 UART_state_bitmap; @@ -1698,13 +1654,13 @@ static int hso_serial_tiocmget(struct tty_struct *tty, struct file *file)  	return retval;  } -static int hso_serial_tiocmset(struct tty_struct *tty, struct file *file, +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 = get_serial_by_tty(tty); +	struct hso_serial *serial = tty->driver_data;  	/* sanity check */  	if (!serial) { @@ -1741,10 +1697,10 @@ static int hso_serial_tiocmset(struct tty_struct *tty, struct file *file,  			       USB_CTRL_SET_TIMEOUT);  } -static int hso_serial_ioctl(struct tty_struct *tty, struct file *file, +static int hso_serial_ioctl(struct tty_struct *tty,  			    unsigned int cmd, unsigned long arg)  { -	struct hso_serial *serial =  get_serial_by_tty(tty); +	struct hso_serial *serial = tty->driver_data;  	int ret = 0;  	D4("IOCTL cmd: %d, arg: %ld", cmd, arg); @@ -1916,7 +1872,7 @@ static void intr_callback(struct urb *urb)  				D1("Pending read interrupt on port %d\n", i);  				spin_lock(&serial->serial_lock);  				if (serial->rx_state == RX_IDLE && -					serial->open_count > 0) { +					serial->port.count > 0) {  					/* Setup and send a ctrl req read on  					 * port i */  					if (!serial->rx_urb_filled[0]) { @@ -1955,7 +1911,6 @@ static void hso_std_serial_write_bulk_callback(struct urb *urb)  {  	struct hso_serial *serial = urb->context;  	int status = urb->status; -	struct tty_struct *tty;  	/* sanity check */  	if (!serial) { @@ -1965,18 +1920,13 @@ static void hso_std_serial_write_bulk_callback(struct urb *urb)  	spin_lock(&serial->serial_lock);  	serial->tx_urb_used = 0; -	tty = tty_kref_get(serial->tty);  	spin_unlock(&serial->serial_lock);  	if (status) {  		handle_usb_error(status, __func__, serial->parent); -		tty_kref_put(tty);  		return;  	}  	hso_put_activity(serial->parent); -	if (tty) { -		tty_wakeup(tty); -		tty_kref_put(tty); -	} +	tty_port_tty_wakeup(&serial->port);  	hso_kick_transmit(serial);  	D1(" "); @@ -2012,7 +1962,6 @@ static void ctrl_callback(struct urb *urb)  	struct hso_serial *serial = urb->context;  	struct usb_ctrlrequest *req;  	int status = urb->status; -	struct tty_struct *tty;  	/* sanity check */  	if (!serial) @@ -2020,11 +1969,9 @@ static void ctrl_callback(struct urb *urb)  	spin_lock(&serial->serial_lock);  	serial->tx_urb_used = 0; -	tty = tty_kref_get(serial->tty);  	spin_unlock(&serial->serial_lock);  	if (status) {  		handle_usb_error(status, __func__, serial->parent); -		tty_kref_put(tty);  		return;  	} @@ -2043,20 +1990,17 @@ static void ctrl_callback(struct urb *urb)  		spin_unlock(&serial->serial_lock);  	} else {  		hso_put_activity(serial->parent); -		if (tty) -			tty_wakeup(tty); +		tty_port_tty_wakeup(&serial->port);  		/* response to a write command */  		hso_kick_transmit(serial);  	} -	tty_kref_put(tty);  }  /* handle RX data for serial port */  static int put_rxbuf_data(struct urb *urb, struct hso_serial *serial)  {  	struct tty_struct *tty; -	int write_length_remaining = 0; -	int curr_write_len; +	int count;  	/* Sanity check */  	if (urb == NULL || serial == NULL) { @@ -2064,34 +2008,30 @@ static int put_rxbuf_data(struct urb *urb, struct hso_serial *serial)  		return -2;  	} -	/* All callers to put_rxbuf_data hold serial_lock */ -	tty = tty_kref_get(serial->tty); +	tty = tty_port_tty_get(&serial->port); -	/* Push data to tty */ -	if (tty) { -		write_length_remaining = urb->actual_length - -			serial->curr_rx_urb_offset; -		D1("data to push to tty"); -		while (write_length_remaining) { -			if (test_bit(TTY_THROTTLED, &tty->flags)) { -				tty_kref_put(tty); -				return -1; -			} -			curr_write_len =  tty_insert_flip_string -				(tty, urb->transfer_buffer + -				 serial->curr_rx_urb_offset, -				 write_length_remaining); -			serial->curr_rx_urb_offset += curr_write_len; -			write_length_remaining -= curr_write_len; -			tty_flip_buffer_push(tty); -		} +	if (tty && test_bit(TTY_THROTTLED, &tty->flags)) { +		tty_kref_put(tty); +		return -1;  	} -	if (write_length_remaining == 0) { -		serial->curr_rx_urb_offset = 0; -		serial->rx_urb_filled[hso_urb_to_index(serial, urb)] = 0; + +	/* 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); -	return write_length_remaining; + +	serial->rx_urb_filled[hso_urb_to_index(serial, urb)] = 0; + +	return 0;  } @@ -2262,7 +2202,6 @@ static int hso_stop_serial_device(struct hso_device *hso_dev)  		}  	}  	serial->curr_rx_urb_idx = 0; -	serial->curr_rx_urb_offset = 0;  	if (serial->tx_urb)  		usb_kill_urb(serial->tx_urb); @@ -2307,6 +2246,7 @@ static void hso_serial_common_free(struct hso_serial *serial)  	/* unlink and free TX URB */  	usb_free_urb(serial->tx_urb);  	kfree(serial->tx_data); +	tty_port_destroy(&serial->port);  }  static int hso_serial_common_create(struct hso_serial *serial, int num_urbs, @@ -2316,13 +2256,15 @@ static int hso_serial_common_create(struct hso_serial *serial, int num_urbs,  	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_register_device(tty_drv, minor, -					&serial->parent->interface->dev); +	serial->parent->dev = tty_port_register_device(&serial->port, tty_drv, +			minor, &serial->parent->interface->dev);  	dev = serial->parent->dev;  	dev_set_drvdata(dev, serial->parent);  	i = device_create_file(dev, &dev_attr_hsotype); @@ -2347,10 +2289,8 @@ static int hso_serial_common_create(struct hso_serial *serial, int num_urbs,  		serial->rx_urb[i]->transfer_buffer_length = 0;  		serial->rx_data[i] = kzalloc(serial->rx_data_length,  					     GFP_KERNEL); -		if (!serial->rx_data[i]) { -			dev_err(dev, "%s - Out of memory\n", __func__); +		if (!serial->rx_data[i])  			goto exit; -		}  	}  	/* TX, allocate urb and initialize */ @@ -2366,15 +2306,12 @@ static int hso_serial_common_create(struct hso_serial *serial, int num_urbs,  	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) { -		dev_err(dev, "%s - Out of memory\n", __func__); +	if (!serial->tx_data)  		goto exit; -	} +  	serial->tx_buffer = kzalloc(serial->tx_data_length, GFP_KERNEL); -	if (!serial->tx_buffer) { -		dev_err(dev, "%s - Out of memory\n", __func__); +	if (!serial->tx_buffer)  		goto exit; -	}  	return 0;  exit: @@ -2432,10 +2369,8 @@ static void hso_free_net_device(struct hso_device *hso_dev)  	remove_net_device(hso_net->parent); -	if (hso_net->net) { +	if (hso_net->net)  		unregister_netdev(hso_net->net); -		free_netdev(hso_net->net); -	}  	/* start freeing */  	for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) { @@ -2447,6 +2382,9 @@ static void hso_free_net_device(struct hso_device *hso_dev)  	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);  } @@ -2471,7 +2409,7 @@ static void hso_net_init(struct net_device *net)  	net->type = ARPHRD_NONE;  	net->mtu = DEFAULT_MTU - 14;  	net->tx_queue_len = 10; -	SET_ETHTOOL_OPS(net, &ops); +	net->ethtool_ops = &ops;  	/* and initialize the semaphore */  	spin_lock_init(&hso_net->net_lock); @@ -2609,10 +2547,8 @@ static struct hso_device *hso_create_net_device(struct usb_interface *interface,  		}  		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]) { -			dev_err(&interface->dev, "Could not allocate rx buf\n"); +		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) { @@ -2620,10 +2556,8 @@ static struct hso_device *hso_create_net_device(struct usb_interface *interface,  		goto exit;  	}  	hso_net->mux_bulk_tx_buf = kzalloc(MUX_BULK_TX_BUF_SIZE, GFP_KERNEL); -	if (!hso_net->mux_bulk_tx_buf) { -		dev_err(&interface->dev, "Could not allocate tx buf\n"); +	if (!hso_net->mux_bulk_tx_buf)  		goto exit; -	}  	add_net_device(hso_dev); @@ -2639,15 +2573,15 @@ exit:  static void hso_free_tiomget(struct hso_serial *serial)  { -	struct hso_tiocmget *tiocmget = serial->tiocmget; +	struct hso_tiocmget *tiocmget; +	if (!serial) +		return; +	tiocmget = serial->tiocmget;  	if (tiocmget) { -		if (tiocmget->urb) { -			usb_free_urb(tiocmget->urb); -			tiocmget->urb = NULL; -		} +		usb_free_urb(tiocmget->urb); +		tiocmget->urb = NULL;  		serial->tiocmget = NULL;  		kfree(tiocmget); -  	}  } @@ -2847,10 +2781,8 @@ struct hso_shared_int *hso_create_shared_int(struct usb_interface *interface)  	mux->shared_intr_buf =  		kzalloc(le16_to_cpu(mux->intr_endp->wMaxPacketSize),  			GFP_KERNEL); -	if (!mux->shared_intr_buf) { -		dev_err(&interface->dev, "Could not allocate intr buf?\n"); +	if (!mux->shared_intr_buf)  		goto exit; -	}  	mutex_init(&mux->shared_int_lock); @@ -2867,13 +2799,16 @@ exit:  static int hso_get_config_data(struct usb_interface *interface)  {  	struct usb_device *usbdev = interface_to_usbdev(interface); -	u8 config_data[17]; +	u8 *config_data = kmalloc(17, GFP_KERNEL);  	u32 if_num = interface->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;  	} @@ -2924,6 +2859,7 @@ static int hso_get_config_data(struct usb_interface *interface)  	if (config_data[16] & 0x1)  		result |= HSO_INFO_CRC_BUG; +	kfree(config_data);  	return result;  } @@ -2937,6 +2873,11 @@ static int hso_probe(struct usb_interface *interface,  	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->altsetting->desc.bInterfaceNumber;  	/* Get the interface/port specification from either driver_info or from @@ -2946,10 +2887,6 @@ static int hso_probe(struct usb_interface *interface,  	else  		port_spec = hso_get_config_data(interface); -	if (interface->cur_altsetting->desc.bInterfaceClass != 0xFF) { -		dev_err(&interface->dev, "Not our interface\n"); -		return -ENODEV; -	}  	/* Check if we need to switch to alt interfaces prior to port  	 * configuration */  	if (interface->num_altsetting > 1) @@ -2993,12 +2930,14 @@ static int hso_probe(struct usb_interface *interface,  	case HSO_INTF_BULK:  		/* It's a regular bulk interface */ -		if (((port_spec & HSO_PORT_MASK) == HSO_PORT_NETWORK) && -		    !disable_net) -			hso_dev = hso_create_net_device(interface, port_spec); -		else +		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; @@ -3106,7 +3045,7 @@ static int hso_resume(struct usb_interface *iface)  	/* Start all serial ports */  	for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) {  		if (serial_table[i] && (serial_table[i]->interface == iface)) { -			if (dev2ser(serial_table[i])->open_count) { +			if (dev2ser(serial_table[i])->port.count) {  				result =  				    hso_start_serial_device(serial_table[i], GFP_NOIO);  				hso_kick_transmit(dev2ser(serial_table[i])); @@ -3173,20 +3112,14 @@ static void hso_serial_ref_free(struct kref *ref)  static void hso_free_interface(struct usb_interface *interface)  {  	struct hso_serial *hso_dev; -	struct tty_struct *tty;  	int i;  	for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) {  		if (serial_table[i] &&  		    (serial_table[i]->interface == interface)) {  			hso_dev = dev2ser(serial_table[i]); -			spin_lock_irq(&hso_dev->serial_lock); -			tty = tty_kref_get(hso_dev->tty); -			spin_unlock_irq(&hso_dev->serial_lock); -			if (tty) -				tty_hangup(tty); +			tty_port_tty_hangup(&hso_dev->port, false);  			mutex_lock(&hso_dev->parent->mutex); -			tty_kref_put(tty);  			hso_dev->parent->usb_gone = 1;  			mutex_unlock(&hso_dev->parent->mutex);  			kref_put(&serial_table[i]->ref, hso_serial_ref_free); @@ -3299,6 +3232,7 @@ static struct usb_driver hso_driver = {  	.resume = hso_resume,  	.reset_resume = hso_resume,  	.supports_autosuspend = 1, +	.disable_hub_initiated_lpm = 1,  };  static int __init hso_init(void) @@ -3320,8 +3254,6 @@ static int __init hso_init(void)  		return -ENOMEM;  	/* fill in all needed values */ -	tty_drv->magic = TTY_DRIVER_MAGIC; -	tty_drv->owner = THIS_MODULE;  	tty_drv->driver_name = driver_name;  	tty_drv->name = tty_filename; @@ -3330,7 +3262,6 @@ static int __init hso_init(void)  		tty_drv->major = tty_major;  	tty_drv->minor_start = 0; -	tty_drv->num = HSO_SERIAL_TTY_MINORS;  	tty_drv->type = TTY_DRIVER_TYPE_SERIAL;  	tty_drv->subtype = SERIAL_TYPE_NORMAL;  	tty_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; @@ -3343,7 +3274,7 @@ static int __init hso_init(void)  	if (result) {  		printk(KERN_ERR "%s - tty_register_driver failed(%d)\n",  			__func__, result); -		return result; +		goto err_free_tty;  	}  	/* register this module as an usb driver */ @@ -3351,13 +3282,16 @@ static int __init hso_init(void)  	if (result) {  		printk(KERN_ERR "Could not register hso driver? error: %d\n",  			result); -		/* cleanup serial interface */ -		tty_unregister_driver(tty_drv); -		return 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) @@ -3365,6 +3299,7 @@ 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);  } diff --git a/drivers/net/usb/huawei_cdc_ncm.c b/drivers/net/usb/huawei_cdc_ncm.c new file mode 100644 index 00000000000..735f7dadb9a --- /dev/null +++ b/drivers/net/usb/huawei_cdc_ncm.c @@ -0,0 +1,221 @@ +/* huawei_cdc_ncm.c - handles Huawei devices using the CDC NCM protocol as + * transport layer. + * Copyright (C) 2013	 Enrico Mioso <mrkiko.rs@gmail.com> + * + * + * 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 <bjorn@mork.no> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/ethtool.h> +#include <linux/if_vlan.h> +#include <linux/ip.h> +#include <linux/mii.h> +#include <linux/usb.h> +#include <linux/usb/cdc.h> +#include <linux/usb/usbnet.h> +#include <linux/usb/cdc-wdm.h> +#include <linux/usb/cdc_ncm.h> + +/* 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; + +	/* altsetting should always be 1 for NCM devices - so we hard-coded +	 * it here +	 */ +	ret = cdc_ncm_bind_common(usbnet_dev, intf, 1); +	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 <mrkiko.rs@gmail.com>"); +MODULE_DESCRIPTION("USB CDC NCM host driver with encapsulated protocol support"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/usb/int51x1.c b/drivers/net/usb/int51x1.c index be02a25da71..4ff70b22c6e 100644 --- a/drivers/net/usb/int51x1.c +++ b/drivers/net/usb/int51x1.c @@ -20,8 +20,7 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  #include <linux/module.h> @@ -116,23 +115,8 @@ static struct sk_buff *int51x1_tx_fixup(struct usbnet *dev,  	return skb;  } -static void int51x1_async_cmd_callback(struct urb *urb) -{ -	struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context; -	int status = urb->status; - -	if (status < 0) -		dev_warn(&urb->dev->dev, "async callback failed with %d\n", status); - -	kfree(req); -	usb_free_urb(urb); -} -  static void int51x1_set_multicast(struct net_device *netdev)  { -	struct usb_ctrlrequest *req; -	int status; -	struct urb *urb;  	struct usbnet *dev = netdev_priv(netdev);  	u16 filter = PACKET_TYPE_DIRECTED | PACKET_TYPE_BROADCAST; @@ -149,40 +133,9 @@ static void int51x1_set_multicast(struct net_device *netdev)  		netdev_dbg(dev->net, "receive own packets only\n");  	} -	urb = usb_alloc_urb(0, GFP_ATOMIC); -	if (!urb) { -		netdev_warn(dev->net, "Error allocating URB\n"); -		return; -	} - -	req = kmalloc(sizeof(*req), GFP_ATOMIC); -	if (!req) { -		netdev_warn(dev->net, "Error allocating control msg\n"); -		goto out; -	} - -	req->bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; -	req->bRequest = SET_ETHERNET_PACKET_FILTER; -	req->wValue = cpu_to_le16(filter); -	req->wIndex = 0; -	req->wLength = 0; - -	usb_fill_control_urb(urb, dev->udev, usb_sndctrlpipe(dev->udev, 0), -		(void *)req, NULL, 0, -		int51x1_async_cmd_callback, -		(void *)req); - -	status = usb_submit_urb(urb, GFP_ATOMIC); -	if (status < 0) { -		netdev_warn(dev->net, "Error submitting control msg, sts=%d\n", -			    status); -		goto out1; -	} -	return; -out1: -	kfree(req); -out: -	usb_free_urb(urb); +	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 = { @@ -193,7 +146,7 @@ static const struct net_device_ops int51x1_netdev_ops = {  	.ndo_change_mtu		= usbnet_change_mtu,  	.ndo_set_mac_address	= eth_mac_addr,  	.ndo_validate_addr	= eth_validate_addr, -	.ndo_set_multicast_list	= int51x1_set_multicast, +	.ndo_set_rx_mode	= int51x1_set_multicast,  };  static int int51x1_bind(struct usbnet *dev, struct usb_interface *intf) @@ -236,19 +189,10 @@ static struct usb_driver int51x1_driver = {  	.disconnect = usbnet_disconnect,  	.suspend    = usbnet_suspend,  	.resume     = usbnet_resume, +	.disable_hub_initiated_lpm = 1,  }; -static int __init int51x1_init(void) -{ -	return usb_register(&int51x1_driver); -} -module_init(int51x1_init); - -static void __exit int51x1_exit(void) -{ -	usb_deregister(&int51x1_driver); -} -module_exit(int51x1_exit); +module_usb_driver(int51x1_driver);  MODULE_AUTHOR("Peter Holik");  MODULE_DESCRIPTION("Intellon usb powerline adapter"); diff --git a/drivers/net/usb/ipheth.c b/drivers/net/usb/ipheth.c index 7d42f9a2c06..76465b117b7 100644 --- a/drivers/net/usb/ipheth.c +++ b/drivers/net/usb/ipheth.c @@ -45,7 +45,6 @@  #include <linux/kernel.h>  #include <linux/errno.h> -#include <linux/init.h>  #include <linux/slab.h>  #include <linux/module.h>  #include <linux/netdevice.h> @@ -59,12 +58,20 @@  #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 @@ -97,6 +104,34 @@ static struct usb_device_id ipheth_table[] = {  		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); @@ -198,22 +233,27 @@ static void ipheth_rcvbulk_callback(struct urb *urb)  	case 0:  		break;  	default: -		err("%s: urb status: %d", __func__, status); +		dev_err(&dev->intf->dev, "%s: urb status: %d\n", +			__func__, status);  		return;  	} -	len = urb->actual_length; -	buf = urb->transfer_buffer; +	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(NET_IP_ALIGN + len); +	skb = dev_alloc_skb(len);  	if (!skb) { -		err("%s: dev_alloc_skb: -ENOMEM", __func__); +		dev_err(&dev->intf->dev, "%s: dev_alloc_skb: -ENOMEM\n", +			__func__);  		dev->net->stats.rx_dropped++;  		return;  	} -	skb_reserve(skb, NET_IP_ALIGN); -	memcpy(skb_put(skb, len), buf + NET_IP_ALIGN, len - NET_IP_ALIGN); +	memcpy(skb_put(skb, len), buf, len);  	skb->dev = dev->net;  	skb->protocol = eth_type_trans(skb, dev->net); @@ -237,7 +277,8 @@ static void ipheth_sndbulk_callback(struct urb *urb)  	    status != -ENOENT &&  	    status != -ECONNRESET &&  	    status != -ESHUTDOWN) -		err("%s: urb status: %d", __func__, status); +		dev_err(&dev->intf->dev, "%s: urb status: %d\n", +		__func__, status);  	dev_kfree_skb_irq(dev->tx_skb);  	netif_wake_queue(dev->net); @@ -257,7 +298,8 @@ static int ipheth_carrier_set(struct ipheth_device *dev)  			dev->ctrl_buf, IPHETH_CTRL_BUF_SIZE,  			IPHETH_CTRL_TIMEOUT);  	if (retval < 0) { -		err("%s: usb_control_msg: %d", __func__, retval); +		dev_err(&dev->intf->dev, "%s: usb_control_msg: %d\n", +			__func__, retval);  		return retval;  	} @@ -294,9 +336,11 @@ static int ipheth_get_macaddr(struct ipheth_device *dev)  				 IPHETH_CTRL_BUF_SIZE,  				 IPHETH_CTRL_TIMEOUT);  	if (retval < 0) { -		err("%s: usb_control_msg: %d", __func__, retval); +		dev_err(&dev->intf->dev, "%s: usb_control_msg: %d\n", +			__func__, retval);  	} else if (retval < ETH_ALEN) { -		err("%s: usb_control_msg: short packet: %d bytes", +		dev_err(&dev->intf->dev, +			"%s: usb_control_msg: short packet: %d bytes\n",  			__func__, retval);  		retval = -EINVAL;  	} else { @@ -321,7 +365,8 @@ static int ipheth_rx_submit(struct ipheth_device *dev, gfp_t mem_flags)  	retval = usb_submit_urb(dev->rx_urb, mem_flags);  	if (retval) -		err("%s: usb_submit_urb: %d", __func__, retval); +		dev_err(&dev->intf->dev, "%s: usb_submit_urb: %d\n", +			__func__, retval);  	return retval;  } @@ -382,7 +427,8 @@ static int ipheth_tx(struct sk_buff *skb, struct net_device *net)  	retval = usb_submit_urb(dev->tx_urb, GFP_ATOMIC);  	if (retval) { -		err("%s: usb_submit_urb: %d", __func__, 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 { @@ -400,33 +446,26 @@ static void ipheth_tx_timeout(struct net_device *net)  {  	struct ipheth_device *dev = netdev_priv(net); -	err("%s: TX timeout", __func__); +	dev_err(&dev->intf->dev, "%s: TX timeout\n", __func__);  	dev->net->stats.tx_errors++;  	usb_unlink_urb(dev->tx_urb);  } -static struct net_device_stats *ipheth_stats(struct net_device *net) -{ -	struct ipheth_device *dev = netdev_priv(net); -	return &dev->net->stats; -} -  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 struct ethtool_ops ops = { +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, -	.ndo_get_stats = &ipheth_stats, +	.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, @@ -457,7 +496,7 @@ static int ipheth_probe(struct usb_interface *intf,  	hintf = usb_altnum_to_altsetting(intf, IPHETH_ALT_INTFNUM);  	if (hintf == NULL) {  		retval = -ENODEV; -		err("Unable to find alternate settings interface"); +		dev_err(&intf->dev, "Unable to find alternate settings interface\n");  		goto err_endpoints;  	} @@ -470,7 +509,7 @@ static int ipheth_probe(struct usb_interface *intf,  	}  	if (!(dev->bulk_in && dev->bulk_out)) {  		retval = -ENODEV; -		err("Unable to find endpoints"); +		dev_err(&intf->dev, "Unable to find endpoints\n");  		goto err_endpoints;  	} @@ -488,18 +527,18 @@ static int ipheth_probe(struct usb_interface *intf,  	retval = ipheth_alloc_urbs(dev);  	if (retval) { -		err("error allocating urbs: %d", 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); -	SET_ETHTOOL_OPS(netdev, &ops); +	netdev->ethtool_ops = &ops;  	retval = register_netdev(netdev);  	if (retval) { -		err("error registering netdev: %d", retval); +		dev_err(&intf->dev, "error registering netdev: %d\n", retval);  		retval = -EIO;  		goto err_register_netdev;  	} @@ -539,27 +578,10 @@ static struct usb_driver ipheth_driver = {  	.probe =	ipheth_probe,  	.disconnect =	ipheth_disconnect,  	.id_table =	ipheth_table, +	.disable_hub_initiated_lpm = 1,  }; -static int __init ipheth_init(void) -{ -	int retval; - -	retval = usb_register(&ipheth_driver); -	if (retval) { -		err("usb_register failed: %d", retval); -		return retval; -	} -	return 0; -} - -static void __exit ipheth_exit(void) -{ -	usb_deregister(&ipheth_driver); -} - -module_init(ipheth_init); -module_exit(ipheth_exit); +module_usb_driver(ipheth_driver);  MODULE_AUTHOR("Diego Giagio <diego@giagio.com>");  MODULE_DESCRIPTION("Apple iPhone USB Ethernet driver"); diff --git a/drivers/net/usb/kalmia.c b/drivers/net/usb/kalmia.c new file mode 100644 index 00000000000..5662babf058 --- /dev/null +++ b/drivers/net/usb/kalmia.c @@ -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 <marius@kotsbak.com> + * + * 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 <linux/module.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/ctype.h> +#include <linux/ethtool.h> +#include <linux/workqueue.h> +#include <linux/mii.h> +#include <linux/usb.h> +#include <linux/crc32.h> +#include <linux/usb/cdc.h> +#include <linux/usb/usbnet.h> +#include <linux/gfp.h> + +/* + * 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 <marius@kotsbak.com>"); +MODULE_DESCRIPTION("Samsung Kalmia USB network driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/usb/kaweth.c b/drivers/net/usb/kaweth.c index 5e98643a4a2..dcb6d33141e 100644 --- a/drivers/net/usb/kaweth.c +++ b/drivers/net/usb/kaweth.c @@ -25,8 +25,7 @@   *     GNU General Public License for more details.   *   *     You should have received a copy of the GNU General Public License - *     along with this program; if not, write to the Free Software Foundation, - *     Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *     along with this program; if not, see <http://www.gnu.org/licenses/>.   *   ****************************************************************/ @@ -46,7 +45,6 @@  #include <linux/module.h>  #include <linux/slab.h>  #include <linux/string.h> -#include <linux/init.h>  #include <linux/delay.h>  #include <linux/netdevice.h>  #include <linux/etherdevice.h> @@ -179,6 +177,7 @@ static struct usb_driver kaweth_driver = {  	.resume =	kaweth_resume,  	.id_table =     usb_klsi_table,  	.supports_autosuspend =	1, +	.disable_hub_initiated_lpm = 1,  };  typedef __u8 eth_addr_t[6]; @@ -266,19 +265,16 @@ static int kaweth_control(struct kaweth_device *kaweth,  	struct usb_ctrlrequest *dr;  	int retval; -	dbg("kaweth_control()"); +	netdev_dbg(kaweth->net, "kaweth_control()\n");  	if(in_interrupt()) { -		dbg("in_interrupt()"); +		netdev_dbg(kaweth->net, "in_interrupt()\n");  		return -EBUSY;  	}  	dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC); - -	if (!dr) { -		dbg("kmalloc() failed"); +	if (!dr)  		return -ENOMEM; -	}  	dr->bRequestType = requesttype;  	dr->bRequest = request; @@ -304,7 +300,7 @@ static int kaweth_read_configuration(struct kaweth_device *kaweth)  {  	int retval; -	dbg("Reading kaweth configuration"); +	netdev_dbg(kaweth->net, "Reading kaweth configuration\n");  	retval = kaweth_control(kaweth,  				usb_rcvctrlpipe(kaweth->dev, 0), @@ -326,7 +322,7 @@ static int kaweth_set_urb_size(struct kaweth_device *kaweth, __u16 urb_size)  {  	int retval; -	dbg("Setting URB size to %d", (unsigned)urb_size); +	netdev_dbg(kaweth->net, "Setting URB size to %d\n", (unsigned)urb_size);  	retval = kaweth_control(kaweth,  				usb_sndctrlpipe(kaweth->dev, 0), @@ -348,7 +344,7 @@ static int kaweth_set_sofs_wait(struct kaweth_device *kaweth, __u16 sofs_wait)  {  	int retval; -	dbg("Set SOFS wait to %d", (unsigned)sofs_wait); +	netdev_dbg(kaweth->net, "Set SOFS wait to %d\n", (unsigned)sofs_wait);  	retval = kaweth_control(kaweth,  				usb_sndctrlpipe(kaweth->dev, 0), @@ -371,7 +367,8 @@ static int kaweth_set_receive_filter(struct kaweth_device *kaweth,  {  	int retval; -	dbg("Set receive filter to %d", (unsigned)receive_filter); +	netdev_dbg(kaweth->net, "Set receive filter to %d\n", +		   (unsigned)receive_filter);  	retval = kaweth_control(kaweth,  				usb_sndctrlpipe(kaweth->dev, 0), @@ -400,12 +397,14 @@ static int kaweth_download_firmware(struct kaweth_device *kaweth,  	ret = request_firmware(&fw, fwname, &kaweth->dev->dev);  	if (ret) { -		err("Firmware request failed\n"); +		dev_err(&kaweth->intf->dev, "Firmware request failed\n");  		return ret;  	}  	if (fw->size > KAWETH_FIRMWARE_BUF_SIZE) { -		err("Firmware too big: %zu", fw->size); +		dev_err(&kaweth->intf->dev, "Firmware too big: %zu\n", +			fw->size); +		release_firmware(fw);  		return -ENOSPC;  	}  	data_len = fw->size; @@ -418,12 +417,13 @@ static int kaweth_download_firmware(struct kaweth_device *kaweth,  	kaweth->firmware_buf[4] = type;  	kaweth->firmware_buf[5] = interrupt; -	dbg("High: %i, Low:%i", kaweth->firmware_buf[3], +	netdev_dbg(kaweth->net, "High: %i, Low:%i\n", kaweth->firmware_buf[3],  		   kaweth->firmware_buf[2]); -	dbg("Downloading firmware at %p to kaweth device at %p", -	    fw->data, kaweth); -	dbg("Firmware length: %d", data_len); +	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), @@ -451,7 +451,7 @@ static int kaweth_trigger_firmware(struct kaweth_device *kaweth,  	kaweth->firmware_buf[6] = 0x00;  	kaweth->firmware_buf[7] = 0x00; -	dbg("Triggering firmware"); +	netdev_dbg(kaweth->net, "Triggering firmware\n");  	return kaweth_control(kaweth,  			      usb_sndctrlpipe(kaweth->dev, 0), @@ -471,11 +471,11 @@ static int kaweth_reset(struct kaweth_device *kaweth)  {  	int result; -	dbg("kaweth_reset(%p)", kaweth); +	netdev_dbg(kaweth->net, "kaweth_reset(%p)\n", kaweth);  	result = usb_reset_configuration(kaweth->dev);  	mdelay(10); -	dbg("kaweth_reset() returns %d.",result); +	netdev_dbg(kaweth->net, "kaweth_reset() returns %d.\n", result);  	return result;  } @@ -500,9 +500,10 @@ static void kaweth_resubmit_int_urb(struct kaweth_device *kaweth, gfp_t mf)  	}  	if (status) -		err ("can't resubmit intr, %s-%s, status %d", -				kaweth->dev->bus->bus_name, -				kaweth->dev->devpath, 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) @@ -575,7 +576,8 @@ static int kaweth_resubmit_rx_urb(struct kaweth_device *kaweth,  			kaweth->suspend_lowmem_rx = 1;  			schedule_delayed_work(&kaweth->lowmem_work, HZ/4);  		} -		err("resubmitting rx_urb %d failed", result); +		dev_err(&kaweth->intf->dev, "resubmitting rx_urb %d failed\n", +			result);  	} else {  		kaweth->suspend_lowmem_rx = 0;  	} @@ -590,6 +592,7 @@ static void kaweth_async_set_rx_mode(struct kaweth_device *kaweth);   ****************************************************************/  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; @@ -605,25 +608,25 @@ static void kaweth_usb_receive(struct urb *urb)  		kaweth->stats.rx_errors++;  		kaweth->end = 1;  		wake_up(&kaweth->term_wait); -		dbg("Status was -EPIPE."); +		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); -		dbg("Status was -ECONNRESET or -ESHUTDOWN."); +		dev_dbg(dev, "Status was -ECONNRESET or -ESHUTDOWN.\n");  		return;  	}  	if (unlikely(status == -EPROTO || status == -ETIME ||  		     status == -EILSEQ)) {  		kaweth->stats.rx_errors++; -		dbg("Status was -EPROTO, -ETIME, or -EILSEQ."); +		dev_dbg(dev, "Status was -EPROTO, -ETIME, or -EILSEQ.\n");  		return;  	}  	if (unlikely(status == -EOVERFLOW)) {  		kaweth->stats.rx_errors++; -		dbg("Status was -EOVERFLOW."); +		dev_dbg(dev, "Status was -EOVERFLOW.\n");  	}  	spin_lock(&kaweth->device_lock);  	if (IS_BLOCKED(kaweth->status)) { @@ -633,20 +636,21 @@ static void kaweth_usb_receive(struct urb *urb)  	spin_unlock(&kaweth->device_lock);  	if(status && status != -EREMOTEIO && count != 1) { -		err("%s RX status: %d count: %d packet_len: %d", -                           net->name, -			   status, -			   count, -			   (int)pkt_len); +		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)) { -			err("Packet length too long for USB frame (pkt_len: %x, count: %x)",pkt_len, count); -			err("Packet len & 2047: %x", pkt_len & 2047); -			err("Count 2: %x", count2); +			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;                  } @@ -681,11 +685,11 @@ static int kaweth_open(struct net_device *net)  	struct kaweth_device *kaweth = netdev_priv(net);  	int res; -	dbg("Opening network device."); +	netdev_dbg(kaweth->net, "Opening network device.\n");  	res = usb_autopm_get_interface(kaweth->intf);  	if (res) { -		err("Interface cannot be resumed."); +		dev_err(&kaweth->intf->dev, "Interface cannot be resumed.\n");  		return -EIO;  	}  	res = kaweth_resubmit_rx_urb(kaweth, GFP_KERNEL); @@ -781,7 +785,8 @@ static void kaweth_usb_transmit_complete(struct urb *urb)  	if (unlikely(status != 0))  		if (status != -ENOENT) -			dbg("%s: TX status %d.", kaweth->net->name, status); +			dev_dbg(&urb->dev->dev, "%s: TX status %d.\n", +				kaweth->net->name, status);  	netif_wake_queue(kaweth->net);  	dev_kfree_skb_irq(skb); @@ -865,7 +870,7 @@ static void kaweth_set_rx_mode(struct net_device *net)                                       KAWETH_PACKET_FILTER_BROADCAST |  		                     KAWETH_PACKET_FILTER_MULTICAST; -	dbg("Setting Rx mode to %d", packet_filter_bitmap); +	netdev_dbg(net, "Setting Rx mode to %d\n", packet_filter_bitmap);  	netif_stop_queue(net); @@ -906,10 +911,12 @@ static void kaweth_async_set_rx_mode(struct kaweth_device *kaweth)  				KAWETH_CONTROL_TIMEOUT);  	if(result < 0) { -		err("Failed to set Rx mode: %d", result); +		dev_err(&kaweth->intf->dev, "Failed to set Rx mode: %d\n", +			result);  	}  	else { -		dbg("Set Rx mode to %d", packet_filter_bitmap); +		netdev_dbg(kaweth->net, "Set Rx mode to %d\n", +			   packet_filter_bitmap);  	}  } @@ -944,7 +951,7 @@ static int kaweth_suspend(struct usb_interface *intf, pm_message_t message)  	struct kaweth_device *kaweth = usb_get_intfdata(intf);  	unsigned long flags; -	dbg("Suspending device"); +	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); @@ -961,7 +968,7 @@ static int kaweth_resume(struct usb_interface *intf)  	struct kaweth_device *kaweth = usb_get_intfdata(intf);  	unsigned long flags; -	dbg("Resuming device"); +	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); @@ -984,7 +991,7 @@ static const struct net_device_ops kaweth_netdev_ops = {  	.ndo_stop =			kaweth_close,  	.ndo_start_xmit =		kaweth_start_xmit,  	.ndo_tx_timeout =		kaweth_tx_timeout, -	.ndo_set_multicast_list =	kaweth_set_rx_mode, +	.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, @@ -996,36 +1003,37 @@ static int kaweth_probe(  		const struct usb_device_id *id      /* from id_table */  	)  { -	struct usb_device *dev = interface_to_usbdev(intf); +	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; -	dbg("Kawasaki Device Probe (Device number:%d): 0x%4.4x:0x%4.4x:0x%4.4x", -		 dev->devnum, -		 le16_to_cpu(dev->descriptor.idVendor), -		 le16_to_cpu(dev->descriptor.idProduct), -		 le16_to_cpu(dev->descriptor.bcdDevice)); +	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)); -	dbg("Device at %p", dev); +	dev_dbg(dev, "Device at %p\n", udev); -	dbg("Descriptor length: %x type: %x", -		 (int)dev->descriptor.bLength, -		 (int)dev->descriptor.bDescriptorType); +	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 = dev; +	kaweth->dev = udev;  	kaweth->net = netdev;  	spin_lock_init(&kaweth->device_lock);  	init_waitqueue_head(&kaweth->term_wait); -	dbg("Resetting."); +	dev_dbg(dev, "Resetting.\n");  	kaweth_reset(kaweth); @@ -1034,17 +1042,18 @@ static int kaweth_probe(  	 * downloaded. Don't try to do it again, or we'll hang the device.  	 */ -	if (le16_to_cpu(dev->descriptor.bcdDevice) >> 8) { -		dev_info(&intf->dev, "Firmware present in device.\n"); +	if (le16_to_cpu(udev->descriptor.bcdDevice) >> 8) { +		dev_info(dev, "Firmware present in device.\n");  	} else {  		/* Download the firmware */ -		dev_info(&intf->dev, "Downloading firmware...\n"); +		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) { -			err("Error downloading firmware (%d)", result); +			dev_err(dev, "Error downloading firmware (%d)\n", +				result);  			goto err_fw;  		} @@ -1052,7 +1061,8 @@ static int kaweth_probe(  						      "kaweth/new_code_fix.bin",  						      100,  						      3)) < 0) { -			err("Error downloading firmware fix (%d)", result); +			dev_err(dev, "Error downloading firmware fix (%d)\n", +				result);  			goto err_fw;  		} @@ -1060,7 +1070,8 @@ static int kaweth_probe(  						      "kaweth/trigger_code.bin",  						      126,  						      2)) < 0) { -			err("Error downloading trigger code (%d)", result); +			dev_err(dev, "Error downloading trigger code (%d)\n", +				result);  			goto err_fw;  		} @@ -1069,18 +1080,18 @@ static int kaweth_probe(  						      "kaweth/trigger_code_fix.bin",  						      126,  						      3)) < 0) { -			err("Error downloading trigger code fix (%d)", result); +			dev_err(dev, "Error downloading trigger code fix (%d)\n", result);  			goto err_fw;  		}  		if ((result = kaweth_trigger_firmware(kaweth, 126)) < 0) { -			err("Error triggering firmware (%d)", result); +			dev_err(dev, "Error triggering firmware (%d)\n", result);  			goto err_fw;  		}  		/* Device will now disappear for a moment...  */ -		dev_info(&intf->dev, "Firmware loaded.  I'll be back...\n"); +		dev_info(dev, "Firmware loaded.  I'll be back...\n");  err_fw:  		free_page((unsigned long)kaweth->firmware_buf);  		free_netdev(netdev); @@ -1090,35 +1101,29 @@ err_fw:  	result = kaweth_read_configuration(kaweth);  	if(result < 0) { -		err("Error reading configuration (%d), no net device created", result); +		dev_err(dev, "Error reading configuration (%d), no net device created\n", result);  		goto err_free_netdev;  	} -	dev_info(&intf->dev, "Statistics collection: %x\n", kaweth->configuration.statistics_mask); -	dev_info(&intf->dev, "Multicast filter limit: %x\n", kaweth->configuration.max_multicast_filters & ((1 << 15) - 1)); -	dev_info(&intf->dev, "MTU: %d\n", le16_to_cpu(kaweth->configuration.segment_size)); -	dev_info(&intf->dev, "Read MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n", -		 (int)kaweth->configuration.hw_addr[0], -		 (int)kaweth->configuration.hw_addr[1], -		 (int)kaweth->configuration.hw_addr[2], -		 (int)kaweth->configuration.hw_addr[3], -		 (int)kaweth->configuration.hw_addr[4], -		 (int)kaweth->configuration.hw_addr[5]); +	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))) { -		err("Firmware not functioning properly, no net device created"); +		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) { -		dbg("Error setting URB size"); +		dev_dbg(dev, "Error setting URB size\n");  		goto err_free_netdev;  	}  	if(kaweth_set_sofs_wait(kaweth, KAWETH_SOFS_TO_WAIT) < 0) { -		err("Error setting SOFS wait"); +		dev_err(dev, "Error setting SOFS wait\n");  		goto err_free_netdev;  	} @@ -1128,11 +1133,11 @@ err_fw:                                             KAWETH_PACKET_FILTER_MULTICAST);  	if(result < 0) { -		err("Error setting receive filter"); +		dev_err(dev, "Error setting receive filter\n");  		goto err_free_netdev;  	} -	dbg("Initializing net device."); +	dev_dbg(dev, "Initializing net device.\n");  	kaweth->intf = intf; @@ -1166,7 +1171,7 @@ err_fw:  	netdev->netdev_ops = &kaweth_netdev_ops;  	netdev->watchdog_timeo = KAWETH_TX_TIMEOUT;  	netdev->mtu = le16_to_cpu(kaweth->configuration.segment_size); -	SET_ETHTOOL_OPS(netdev, &ops); +	netdev->ethtool_ops = &ops;  	/* kaweth is zeroed as part of alloc_netdev */  	INIT_DELAYED_WORK(&kaweth->lowmem_work, kaweth_resubmit_tl); @@ -1174,20 +1179,20 @@ err_fw:  #if 0  // dma_supported() is deeply broken on almost all architectures -	if (dma_supported (&intf->dev, 0xffffffffffffffffULL)) +	if (dma_supported (dev, 0xffffffffffffffffULL))  		kaweth->net->features |= NETIF_F_HIGHDMA;  #endif -	SET_NETDEV_DEV(netdev, &intf->dev); +	SET_NETDEV_DEV(netdev, dev);  	if (register_netdev(netdev) != 0) { -		err("Error registering netdev."); +		dev_err(dev, "Error registering netdev.\n");  		goto err_intfdata;  	} -	dev_info(&intf->dev, "kaweth interface created at %s\n", +	dev_info(dev, "kaweth interface created at %s\n",  		 kaweth->net->name); -	dbg("Kaweth probe returning."); +	dev_dbg(dev, "Kaweth probe returning.\n");  	return 0; @@ -1220,12 +1225,12 @@ static void kaweth_disconnect(struct usb_interface *intf)  	usb_set_intfdata(intf, NULL);  	if (!kaweth) { -		dev_warn(&intf->dev, "unregistering non-existant device\n"); +		dev_warn(&intf->dev, "unregistering non-existent device\n");  		return;  	}  	netdev = kaweth->net; -	dbg("Unregistering net device"); +	netdev_dbg(kaweth->net, "Unregistering net device\n");  	unregister_netdev(netdev);  	usb_free_urb(kaweth->rx_urb); @@ -1307,7 +1312,7 @@ static int kaweth_internal_control_msg(struct usb_device *usb_dev,          int retv;          int length = 0; /* shut up GCC */ -        urb = usb_alloc_urb(0, GFP_NOIO); +	urb = usb_alloc_urb(0, GFP_ATOMIC);          if (!urb)                  return -ENOMEM; @@ -1323,32 +1328,4 @@ static int kaweth_internal_control_msg(struct usb_device *usb_dev,  	}  } - -/**************************************************************** - *     kaweth_init - ****************************************************************/ -static int __init kaweth_init(void) -{ -	dbg("Driver loading"); -	return usb_register(&kaweth_driver); -} - -/**************************************************************** - *     kaweth_exit - ****************************************************************/ -static void __exit kaweth_exit(void) -{ -	usb_deregister(&kaweth_driver); -} - -module_init(kaweth_init); -module_exit(kaweth_exit); - - - - - - - - - +module_usb_driver(kaweth_driver); diff --git a/drivers/net/usb/lg-vl600.c b/drivers/net/usb/lg-vl600.c new file mode 100644 index 00000000000..8f37efd2d2f --- /dev/null +++ b/drivers/net/usb/lg-vl600.c @@ -0,0 +1,353 @@ +/* + * Ethernet interface part of the LG VL600 LTE modem (4G dongle) + * + * Copyright (C) 2011 Intel Corporation + * Author: Andrzej Zaborowski <balrogg@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ +#include <linux/etherdevice.h> +#include <linux/ethtool.h> +#include <linux/mii.h> +#include <linux/usb.h> +#include <linux/usb/cdc.h> +#include <linux/usb/usbnet.h> +#include <linux/if_ether.h> +#include <linux/if_arp.h> +#include <linux/inetdevice.h> +#include <linux/module.h> + +/* + * 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 { +			memset(ethhdr->h_source, 0, ETH_ALEN); +			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 --git a/drivers/net/usb/mcs7830.c b/drivers/net/usb/mcs7830.c index a6281e3987b..82d844a8ebd 100644 --- a/drivers/net/usb/mcs7830.c +++ b/drivers/net/usb/mcs7830.c @@ -1,5 +1,5 @@  /* - * MOSCHIP MCS7830 based USB 2.0 Ethernet Devices + * MOSCHIP MCS7830 based (7730/7830/7832) USB 2.0 Ethernet Devices   *   * based on usbnet.c, asix.c and the vendor provided mcs7830 driver   * @@ -11,6 +11,9 @@   *   * 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 @@ -33,14 +36,12 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  #include <linux/crc32.h>  #include <linux/etherdevice.h>  #include <linux/ethtool.h> -#include <linux/init.h>  #include <linux/mii.h>  #include <linux/module.h>  #include <linux/netdevice.h> @@ -60,6 +61,7 @@  #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 @@ -119,93 +121,20 @@ static const char driver_name[] = "MOSCHIP usb-ethernet driver";  static int mcs7830_get_reg(struct usbnet *dev, u16 index, u16 size, void *data)  { -	struct usb_device *xdev = dev->udev; -	int ret; -	void *buffer; - -	buffer = kmalloc(size, GFP_NOIO); -	if (buffer == NULL) -		return -ENOMEM; - -	ret = usb_control_msg(xdev, usb_rcvctrlpipe(xdev, 0), MCS7830_RD_BREQ, -			      MCS7830_RD_BMREQ, 0x0000, index, buffer, -			      size, MCS7830_CTRL_TIMEOUT); -	memcpy(data, buffer, size); -	kfree(buffer); - -	return ret; +	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)  { -	struct usb_device *xdev = dev->udev; -	int ret; -	void *buffer; - -	buffer = kmemdup(data, size, GFP_NOIO); -	if (buffer == NULL) -		return -ENOMEM; - -	ret = usb_control_msg(xdev, usb_sndctrlpipe(xdev, 0), MCS7830_WR_BREQ, -			      MCS7830_WR_BMREQ, 0x0000, index, buffer, -			      size, MCS7830_CTRL_TIMEOUT); -	kfree(buffer); -	return ret; -} - -static void mcs7830_async_cmd_callback(struct urb *urb) -{ -	struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context; -	int status = urb->status; - -	if (status < 0) -		printk(KERN_DEBUG "%s() failed with %d\n", -		       __func__, status); - -	kfree(req); -	usb_free_urb(urb); +	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)  { -	struct usb_ctrlrequest *req; -	int ret; -	struct urb *urb; - -	urb = usb_alloc_urb(0, GFP_ATOMIC); -	if (!urb) { -		dev_dbg(&dev->udev->dev, -			"Error allocating URB in write_cmd_async!\n"); -		return; -	} - -	req = kmalloc(sizeof *req, GFP_ATOMIC); -	if (!req) { -		dev_err(&dev->udev->dev, -			"Failed to allocate memory for control request\n"); -		goto out; -	} -	req->bRequestType = MCS7830_WR_BMREQ; -	req->bRequest = MCS7830_WR_BREQ; -	req->wValue = 0; -	req->wIndex = cpu_to_le16(index); -	req->wLength = cpu_to_le16(size); - -	usb_fill_control_urb(urb, dev->udev, -			     usb_sndctrlpipe(dev->udev, 0), -			     (void *)req, data, size, -			     mcs7830_async_cmd_callback, req); - -	ret = usb_submit_urb(urb, GFP_ATOMIC); -	if (ret < 0) { -		dev_err(&dev->udev->dev, -			"Error submitting the control message: ret=%d\n", ret); -		goto out; -	} -	return; -out: -	kfree(req); -	usb_free_urb(urb); +	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) @@ -235,7 +164,7 @@ static int mcs7830_set_mac_address(struct net_device *netdev, void *p)  		return -EBUSY;  	if (!is_valid_ether_addr(addr->sa_data)) -		return -EINVAL; +		return -EADDRNOTAVAIL;  	ret = mcs7830_hif_set_mac_address(dev, addr->sa_data); @@ -351,7 +280,7 @@ static int mcs7830_set_autoneg(struct usbnet *dev, int ptrUserPhyMode)  	if (!ret)  		ret = mcs7830_write_phy(dev, MII_BMCR,  				BMCR_ANENABLE | BMCR_ANRESTART	); -	return ret < 0 ? : 0; +	return ret;  } @@ -549,7 +478,7 @@ static const struct net_device_ops mcs7830_netdev_ops = {  	.ndo_change_mtu		= usbnet_change_mtu,  	.ndo_validate_addr	= eth_validate_addr,  	.ndo_do_ioctl 		= mcs7830_ioctl, -	.ndo_set_multicast_list = mcs7830_set_multicast, +	.ndo_set_rx_mode	= mcs7830_set_multicast,  	.ndo_set_mac_address	= mcs7830_set_mac_address,  }; @@ -597,8 +526,9 @@ static int mcs7830_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  {  	u8 status; -	if (skb->len == 0) { -		dev_err(&dev->udev->dev, "unexpected empty rx frame\n"); +	/* 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;  	} @@ -625,11 +555,28 @@ static int mcs7830_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  	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/7730 usb-NET adapter", +	.description	= "MOSCHIP 7830/7832/7730 usb-NET adapter",  	.bind		= mcs7830_bind,  	.rx_fixup	= mcs7830_rx_fixup, -	.flags		= FLAG_ETHER, +	.flags		= FLAG_ETHER | FLAG_LINK_INTR, +	.status		= mcs7830_status,  	.in		= 1,  	.out		= 2,  }; @@ -638,13 +585,18 @@ 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, +	.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,  	}, @@ -682,19 +634,10 @@ static struct usb_driver mcs7830_driver = {  	.suspend = usbnet_suspend,  	.resume = usbnet_resume,  	.reset_resume = mcs7830_reset_resume, +	.disable_hub_initiated_lpm = 1,  }; -static int __init mcs7830_init(void) -{ -	return usb_register(&mcs7830_driver); -} -module_init(mcs7830_init); - -static void __exit mcs7830_exit(void) -{ -	usb_deregister(&mcs7830_driver); -} -module_exit(mcs7830_exit); +module_usb_driver(mcs7830_driver);  MODULE_DESCRIPTION("USB to network adapter MCS7830)");  MODULE_LICENSE("GPL"); diff --git a/drivers/net/usb/net1080.c b/drivers/net/usb/net1080.c index ba72a7281cb..4cbdb1307f3 100644 --- a/drivers/net/usb/net1080.c +++ b/drivers/net/usb/net1080.c @@ -13,15 +13,13 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  // #define	DEBUG			// error path messages, extra info  // #define	VERBOSE			// more; success messages  #include <linux/module.h> -#include <linux/init.h>  #include <linux/netdevice.h>  #include <linux/etherdevice.h>  #include <linux/ethtool.h> @@ -109,13 +107,11 @@ struct nc_trailer {  static int  nc_vendor_read(struct usbnet *dev, u8 req, u8 regnum, u16 *retval_ptr)  { -	int status = usb_control_msg(dev->udev, -		usb_rcvctrlpipe(dev->udev, 0), -		req, -		USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, -		0, regnum, -		retval_ptr, sizeof *retval_ptr, -		USB_CTRL_GET_TIMEOUT); +	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) @@ -133,13 +129,9 @@ nc_register_read(struct usbnet *dev, u8 regnum, u16 *retval_ptr)  static void  nc_vendor_write(struct usbnet *dev, u8 req, u8 regnum, u16 value)  { -	usb_control_msg(dev->udev, -		usb_sndctrlpipe(dev->udev, 0), -		req, -		USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, -		value, regnum, -		NULL, 0,			// data is in setup packet -		USB_CTRL_SET_TIMEOUT); +	usbnet_write_cmd(dev, req, +			 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, +			 value, regnum, NULL, 0);  }  static inline void @@ -155,12 +147,10 @@ static void nc_dump_registers(struct usbnet *dev)  	u8	reg;  	u16	*vp = kmalloc(sizeof (u16)); -	if (!vp) { -		dbg("no memory?"); +	if (!vp)  		return; -	} -	dbg("%s registers:", dev->net->name); +	netdev_dbg(dev->net, "registers:\n");  	for (reg = 0; reg < 0x20; reg++) {  		int retval; @@ -172,11 +162,10 @@ static void nc_dump_registers(struct usbnet *dev)  		retval = nc_register_read(dev, reg, vp);  		if (retval < 0) -			dbg("%s reg [0x%x] ==> error %d", -				dev->net->name, reg, retval); +			netdev_dbg(dev->net, "reg [0x%x] ==> error %d\n", +				   reg, retval);  		else -			dbg("%s reg [0x%x] = 0x%x", -				dev->net->name, reg, *vp); +			netdev_dbg(dev->net, "reg [0x%x] = 0x%x\n", reg, *vp);  	}  	kfree(vp);  } @@ -291,42 +280,39 @@ static inline void nc_dump_ttl(struct usbnet *dev, u16 ttl)  static int net1080_reset(struct usbnet *dev)  {  	u16		usbctl, status, ttl; -	u16		*vp = kmalloc(sizeof (u16), GFP_KERNEL); +	u16		vp;  	int		retval; -	if (!vp) -		return -ENOMEM; -  	// nc_dump_registers(dev); -	if ((retval = nc_register_read(dev, REG_STATUS, vp)) < 0) { -		dbg("can't read %s-%s status: %d", -			dev->udev->bus->bus_name, dev->udev->devpath, retval); +	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; +	status = vp;  	nc_dump_status(dev, status); -	if ((retval = nc_register_read(dev, REG_USBCTL, vp)) < 0) { -		dbg("can't read USBCTL, %d", retval); +	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; +	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) { -		dbg("can't read TTL, %d", retval); +	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; +	ttl = vp;  	// nc_dump_ttl(dev, ttl);  	nc_register_write(dev, REG_TTL,  			MK_TTL(NC_READ_TTL_MS, TTL_OTHER(ttl)) ); -	dbg("%s: assigned TTL, %d ms", dev->net->name, NC_READ_TTL_MS); +	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', @@ -334,7 +320,6 @@ static int net1080_reset(struct usbnet *dev)  	retval = 0;  done: -	kfree(vp);  	return retval;  } @@ -342,15 +327,12 @@ static int net1080_check_connect(struct usbnet *dev)  {  	int			retval;  	u16			status; -	u16			*vp = kmalloc(sizeof (u16), GFP_KERNEL); +	u16			vp; -	if (!vp) -		return -ENOMEM; -	retval = nc_register_read(dev, REG_STATUS, vp); -	status = *vp; -	kfree(vp); +	retval = nc_register_read(dev, REG_STATUS, &vp); +	status = vp;  	if (retval != 0) { -		dbg("%s net1080_check_conn read - %d", dev->net->name, retval); +		netdev_dbg(dev->net, "net1080_check_conn read - %d\n", retval);  		return retval;  	}  	if ((status & STATUS_CONN_OTHER) != STATUS_CONN_OTHER) @@ -358,59 +340,22 @@ static int net1080_check_connect(struct usbnet *dev)  	return 0;  } -static void nc_flush_complete(struct urb *urb) -{ -	kfree(urb->context); -	usb_free_urb(urb); -} -  static void nc_ensure_sync(struct usbnet *dev)  { -	dev->frame_errors++; -	if (dev->frame_errors > 5) { -		struct urb		*urb; -		struct usb_ctrlrequest	*req; -		int			status; - -		/* Send a flush */ -		urb = usb_alloc_urb(0, GFP_ATOMIC); -		if (!urb) -			return; - -		req = kmalloc(sizeof *req, GFP_ATOMIC); -		if (!req) { -			usb_free_urb(urb); -			return; -		} +	if (++dev->frame_errors <= 5) +		return; -		req->bRequestType = USB_DIR_OUT -			| USB_TYPE_VENDOR -			| USB_RECIP_DEVICE; -		req->bRequest = REQUEST_REGISTER; -		req->wValue = cpu_to_le16(USBCTL_FLUSH_THIS -				| USBCTL_FLUSH_OTHER); -		req->wIndex = cpu_to_le16(REG_USBCTL); -		req->wLength = cpu_to_le16(0); - -		/* queue an async control request, we don't need -		 * to do anything when it finishes except clean up. -		 */ -		usb_fill_control_urb(urb, dev->udev, -			usb_sndctrlpipe(dev->udev, 0), -			(unsigned char *) req, -			NULL, 0, -			nc_flush_complete, req); -		status = usb_submit_urb(urb, GFP_ATOMIC); -		if (status) { -			kfree(req); -			usb_free_urb(urb); -			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; -	} +	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) @@ -419,12 +364,14 @@ static int net1080_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  	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)) { -#ifdef DEBUG -		struct net_device	*net = dev->net; -		dbg("rx framesize %d range %d..%d mtu %d", skb->len, -			net->hard_header_len, dev->hard_mtu, net->mtu); -#endif +		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; @@ -435,17 +382,17 @@ static int net1080_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  	packet_len = le16_to_cpup(&header->packet_len);  	if (FRAMED_SIZE(packet_len) > NC_MAX_PACKET) {  		dev->net->stats.rx_frame_errors++; -		dbg("packet too big, %d", packet_len); +		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++; -		dbg("header too short, %d", hdr_len); +		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? -		dbg("header OOB, %d bytes", hdr_len - MIN_HEADER); +		netdev_dbg(dev->net, "header OOB, %d bytes\n", hdr_len - MIN_HEADER);  		nc_ensure_sync(dev);  		// switch (vendor/product ids) { ... }  	} @@ -458,23 +405,23 @@ static int net1080_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  	if ((packet_len & 0x01) == 0) {  		if (skb->data [packet_len] != PAD_BYTE) {  			dev->net->stats.rx_frame_errors++; -			dbg("bad pad"); +			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++; -		dbg("bad packet len %d (expected %d)", -			skb->len, packet_len); +		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++; -		dbg("(2+ dropped) rx packet_id mismatch 0x%x 0x%x", -			le16_to_cpu(header->packet_id), -			le16_to_cpu(trailer->packet_id)); +		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 @@ -560,7 +507,7 @@ static int net1080_bind(struct usbnet *dev, struct usb_interface *intf)  static const struct driver_info	net1080_info = {  	.description =	"NetChip TurboCONNECT", -	.flags =	FLAG_FRAMING_NC, +	.flags =	FLAG_POINTTOPOINT | FLAG_FRAMING_NC,  	.bind =		net1080_bind,  	.reset =	net1080_reset,  	.check_connect = net1080_check_connect, @@ -587,19 +534,10 @@ static struct usb_driver net1080_driver = {  	.disconnect =	usbnet_disconnect,  	.suspend =	usbnet_suspend,  	.resume =	usbnet_resume, +	.disable_hub_initiated_lpm = 1,  }; -static int __init net1080_init(void) -{ - 	return usb_register(&net1080_driver); -} -module_init(net1080_init); - -static void __exit net1080_exit(void) -{ - 	usb_deregister(&net1080_driver); -} -module_exit(net1080_exit); +module_usb_driver(net1080_driver);  MODULE_AUTHOR("David Brownell");  MODULE_DESCRIPTION("NetChip 1080 based USB Host-to-Host Links"); diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c index ef3667690b1..f8408021591 100644 --- a/drivers/net/usb/pegasus.c +++ b/drivers/net/usb/pegasus.c @@ -1,5 +1,5 @@  /* - *  Copyright (c) 1999-2005 Petko Manolov (petkan@users.sourceforge.net) + *  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 @@ -26,6 +26,9 @@   *		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 <linux/sched.h> @@ -45,8 +48,8 @@  /*   * Version Information   */ -#define DRIVER_VERSION "v0.6.14 (2006/09/27)" -#define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>" +#define DRIVER_VERSION "v0.9.3 (2013/04/25)" +#define DRIVER_AUTHOR "Petko Manolov <petkan@nucleusys.com>"  #define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver"  static const char driver_name[] = "pegasus"; @@ -55,8 +58,8 @@ static const char driver_name[] = "pegasus";  #define	BMSR_MEDIA	(BMSR_10HALF | BMSR_10FULL | BMSR_100HALF | \  			BMSR_100FULL | BMSR_ANEGCAPABLE) -static int loopback; -static int mii_mode; +static bool loopback; +static bool mii_mode;  static char *devid;  static struct usb_eth_dev usb_dev_id[] = { @@ -108,253 +111,137 @@ 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 int update_eth_regs_async(pegasus_t *); -/* Aargh!!! I _really_ hate such tweaks */ -static void ctrl_callback(struct urb *urb) +/*****/ + +static void async_ctrl_callback(struct urb *urb)  { -	pegasus_t *pegasus = urb->context; +	struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context;  	int status = urb->status; -	if (!pegasus) -		return; - -	switch (status) { -	case 0: -		if (pegasus->flags & ETH_REGS_CHANGE) { -			pegasus->flags &= ~ETH_REGS_CHANGE; -			pegasus->flags |= ETH_REGS_CHANGED; -			update_eth_regs_async(pegasus); -			return; -		} -		break; -	case -EINPROGRESS: -		return; -	case -ENOENT: -		break; -	default: -		if (net_ratelimit()) -			netif_dbg(pegasus, drv, pegasus->net, -				  "%s, status %d\n", __func__, status); -		break; -	} -	pegasus->flags &= ~ETH_REGS_CHANGED; -	wake_up(&pegasus->ctrl_wait); +	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) +static int get_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data)  {  	int ret; -	char *buffer; -	DECLARE_WAITQUEUE(wait, current); - -	buffer = kmalloc(size, GFP_KERNEL); -	if (!buffer) { -		netif_warn(pegasus, drv, pegasus->net, -			   "out of memory in %s\n", __func__); -		return -ENOMEM; -	} -	add_wait_queue(&pegasus->ctrl_wait, &wait); -	set_current_state(TASK_UNINTERRUPTIBLE); -	while (pegasus->flags & ETH_REGS_CHANGED) -		schedule(); -	remove_wait_queue(&pegasus->ctrl_wait, &wait); -	set_current_state(TASK_RUNNING); - -	pegasus->dr.bRequestType = PEGASUS_REQT_READ; -	pegasus->dr.bRequest = PEGASUS_REQ_GET_REGS; -	pegasus->dr.wValue = cpu_to_le16(0); -	pegasus->dr.wIndex = cpu_to_le16(indx); -	pegasus->dr.wLength = cpu_to_le16(size); -	pegasus->ctrl_urb->transfer_buffer_length = size; - -	usb_fill_control_urb(pegasus->ctrl_urb, pegasus->usb, -			     usb_rcvctrlpipe(pegasus->usb, 0), -			     (char *) &pegasus->dr, -			     buffer, size, ctrl_callback, pegasus); - -	add_wait_queue(&pegasus->ctrl_wait, &wait); -	set_current_state(TASK_UNINTERRUPTIBLE); - -	/* using ATOMIC, we'd never wake up if we slept */ -	if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) { -		set_current_state(TASK_RUNNING); -		if (ret == -ENODEV) -			netif_device_detach(pegasus->net); -		if (net_ratelimit()) -			netif_err(pegasus, drv, pegasus->net, -				  "%s, status %d\n", __func__, ret); -		goto out; -	} - -	schedule(); -out: -	remove_wait_queue(&pegasus->ctrl_wait, &wait); -	memcpy(data, buffer, size); -	kfree(buffer); +	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) +static int set_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data)  {  	int ret; -	char *buffer; -	DECLARE_WAITQUEUE(wait, current); - -	buffer = kmemdup(data, size, GFP_KERNEL); -	if (!buffer) { -		netif_warn(pegasus, drv, pegasus->net, -			   "out of memory in %s\n", __func__); -		return -ENOMEM; -	} - -	add_wait_queue(&pegasus->ctrl_wait, &wait); -	set_current_state(TASK_UNINTERRUPTIBLE); -	while (pegasus->flags & ETH_REGS_CHANGED) -		schedule(); -	remove_wait_queue(&pegasus->ctrl_wait, &wait); -	set_current_state(TASK_RUNNING); - -	pegasus->dr.bRequestType = PEGASUS_REQT_WRITE; -	pegasus->dr.bRequest = PEGASUS_REQ_SET_REGS; -	pegasus->dr.wValue = cpu_to_le16(0); -	pegasus->dr.wIndex = cpu_to_le16(indx); -	pegasus->dr.wLength = cpu_to_le16(size); -	pegasus->ctrl_urb->transfer_buffer_length = size; - -	usb_fill_control_urb(pegasus->ctrl_urb, pegasus->usb, -			     usb_sndctrlpipe(pegasus->usb, 0), -			     (char *) &pegasus->dr, -			     buffer, size, ctrl_callback, pegasus); - -	add_wait_queue(&pegasus->ctrl_wait, &wait); -	set_current_state(TASK_UNINTERRUPTIBLE); - -	if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) { -		if (ret == -ENODEV) -			netif_device_detach(pegasus->net); -		netif_err(pegasus, drv, pegasus->net, -			  "%s, status %d\n", __func__, ret); -		goto out; -	} - -	schedule(); -out: -	remove_wait_queue(&pegasus->ctrl_wait, &wait); -	kfree(buffer); +	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; -	char *tmp; -	DECLARE_WAITQUEUE(wait, current); - -	tmp = kmemdup(&data, 1, GFP_KERNEL); -	if (!tmp) { -		netif_warn(pegasus, drv, pegasus->net, -			   "out of memory in %s\n", __func__); -		return -ENOMEM; -	} -	add_wait_queue(&pegasus->ctrl_wait, &wait); -	set_current_state(TASK_UNINTERRUPTIBLE); -	while (pegasus->flags & ETH_REGS_CHANGED) -		schedule(); -	remove_wait_queue(&pegasus->ctrl_wait, &wait); -	set_current_state(TASK_RUNNING); - -	pegasus->dr.bRequestType = PEGASUS_REQT_WRITE; -	pegasus->dr.bRequest = PEGASUS_REQ_SET_REG; -	pegasus->dr.wValue = cpu_to_le16(data); -	pegasus->dr.wIndex = cpu_to_le16(indx); -	pegasus->dr.wLength = cpu_to_le16(1); -	pegasus->ctrl_urb->transfer_buffer_length = 1; - -	usb_fill_control_urb(pegasus->ctrl_urb, pegasus->usb, -			     usb_sndctrlpipe(pegasus->usb, 0), -			     (char *) &pegasus->dr, -			     tmp, 1, ctrl_callback, pegasus); - -	add_wait_queue(&pegasus->ctrl_wait, &wait); -	set_current_state(TASK_UNINTERRUPTIBLE); - -	if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) { -		if (ret == -ENODEV) -			netif_device_detach(pegasus->net); -		if (net_ratelimit()) -			netif_err(pegasus, drv, pegasus->net, -				  "%s, status %d\n", __func__, ret); -		goto out; -	} - -	schedule(); -out: -	remove_wait_queue(&pegasus->ctrl_wait, &wait); -	kfree(tmp); +	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; - -	pegasus->dr.bRequestType = PEGASUS_REQT_WRITE; -	pegasus->dr.bRequest = PEGASUS_REQ_SET_REGS; -	pegasus->dr.wValue = cpu_to_le16(0); -	pegasus->dr.wIndex = cpu_to_le16(EthCtrl0); -	pegasus->dr.wLength = cpu_to_le16(3); -	pegasus->ctrl_urb->transfer_buffer_length = 3; - -	usb_fill_control_urb(pegasus->ctrl_urb, pegasus->usb, -			     usb_sndctrlpipe(pegasus->usb, 0), -			     (char *) &pegasus->dr, -			     pegasus->eth_regs, 3, ctrl_callback, pegasus); - -	if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) { +	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, status %d\n", __func__, ret); +			  "%s returned %d\n", __func__, ret);  	} -  	return ret;  } -/* Returns 0 on success, error on failure */ -static int read_mii_word(pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 *regd) +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; +	int ret = -ETIMEDOUT; -	set_register(pegasus, PhyCtrl, 0); -	set_registers(pegasus, PhyAddr, sizeof(data), data); -	set_register(pegasus, PhyCtrl, (indx | PHY_READ)); +	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(pegasus, PhyCtrl, 1, data); -		if (ret == -ESHUTDOWN) +		ret = get_registers(p, PhyCtrl, 1, data); +		if (ret < 0)  			goto fail;  		if (data[0] & PHY_DONE)  			break;  	} -  	if (i >= REG_TIMEOUT)  		goto fail; - -	ret = get_registers(pegasus, PhyData, 2, ®di); -	*regd = le16_to_cpu(regdi); +	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; +} -fail: -	netif_warn(pegasus, drv, pegasus->net, "%s failed\n", __func__); +/* 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); +} -	return ret; +/* 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) @@ -366,40 +253,12 @@ static int mdio_read(struct net_device *dev, int phy_id, int loc)  	return (int)res;  } -static int write_mii_word(pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 regd) -{ -	int i; -	__u8 data[4] = { phy, 0, 0, indx }; -	int ret; - -	data[1] = (u8) regd; -	data[2] = (u8) (regd >> 8); -	set_register(pegasus, PhyCtrl, 0); -	set_registers(pegasus, PhyAddr, sizeof(data), data); -	set_register(pegasus, PhyCtrl, (indx | PHY_WRITE)); -	for (i = 0; i < REG_TIMEOUT; i++) { -		ret = get_registers(pegasus, PhyCtrl, 1, data); -		if (ret == -ESHUTDOWN) -			goto fail; -		if (data[0] & PHY_DONE) -			break; -	} - -	if (i >= REG_TIMEOUT) -		goto fail; - -	return ret; - -fail: -	netif_warn(pegasus, drv, pegasus->net, "%s failed\n", __func__); -	return -ETIMEDOUT; -} -  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, val); +	write_mii_word(pegasus, phy_id, loc, &data);  }  static int read_eprom_word(pegasus_t *pegasus, __u8 index, __u16 *retdata) @@ -436,7 +295,6 @@ fail:  static inline void enable_eprom_write(pegasus_t *pegasus)  {  	__u8 tmp; -	int ret;  	get_registers(pegasus, EthCtrl2, 1, &tmp);  	set_register(pegasus, EthCtrl2, tmp | EPROM_WR_ENABLE); @@ -445,7 +303,6 @@ static inline void enable_eprom_write(pegasus_t *pegasus)  static inline void disable_eprom_write(pegasus_t *pegasus)  {  	__u8 tmp; -	int ret;  	get_registers(pegasus, EthCtrl2, 1, &tmp);  	set_register(pegasus, EpromCtrl, 0); @@ -517,7 +374,7 @@ static inline int reset_mac(pegasus_t *pegasus)  	for (i = 0; i < REG_TIMEOUT; i++) {  		get_registers(pegasus, EthCtrl1, 1, &data);  		if (~data & 0x08) { -			if (loopback & 1) +			if (loopback)  				break;  			if (mii_mode && (pegasus->features & HAS_HOME_PNA))  				set_register(pegasus, Gpio1, 0x34); @@ -539,7 +396,8 @@ static inline int reset_mac(pegasus_t *pegasus)  	if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_ELCON) {  		__u16 auxmode;  		read_mii_word(pegasus, 3, 0x1b, &auxmode); -		write_mii_word(pegasus, 3, 0x1b, auxmode | 4); +		auxmode |= 4; +		write_mii_word(pegasus, 3, 0x1b, &auxmode);  	}  	return 0; @@ -561,7 +419,7 @@ static int enable_net_traffic(struct net_device *dev, struct usb_device *usb)  		data[1] |= 0x10;	/* set 100 Mbps */  	if (mii_mode)  		data[1] = 0; -	data[2] = (loopback & 1) ? 0x09 : 0x01; +	data[2] = loopback ? 0x09 : 0x01;  	memcpy(pegasus->eth_regs, data, sizeof(data));  	ret = set_registers(pegasus, EthCtrl0, 3, data); @@ -571,57 +429,13 @@ static int enable_net_traffic(struct net_device *dev, struct usb_device *usb)  	    usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK) {  		u16 auxmode;  		read_mii_word(pegasus, 0, 0x1b, &auxmode); -		write_mii_word(pegasus, 0, 0x1b, auxmode | 4); +		auxmode |= 4; +		write_mii_word(pegasus, 0, 0x1b, &auxmode);  	}  	return ret;  } -static void fill_skb_pool(pegasus_t *pegasus) -{ -	int i; - -	for (i = 0; i < RX_SKBS; i++) { -		if (pegasus->rx_pool[i]) -			continue; -		pegasus->rx_pool[i] = dev_alloc_skb(PEGASUS_MTU + 2); -		/* -		 ** we give up if the allocation fail. the tasklet will be -		 ** rescheduled again anyway... -		 */ -		if (pegasus->rx_pool[i] == NULL) -			return; -		skb_reserve(pegasus->rx_pool[i], 2); -	} -} - -static void free_skb_pool(pegasus_t *pegasus) -{ -	int i; - -	for (i = 0; i < RX_SKBS; i++) { -		if (pegasus->rx_pool[i]) { -			dev_kfree_skb(pegasus->rx_pool[i]); -			pegasus->rx_pool[i] = NULL; -		} -	} -} - -static inline struct sk_buff *pull_skb(pegasus_t * pegasus) -{ -	int i; -	struct sk_buff *skb; - -	for (i = 0; i < RX_SKBS; i++) { -		if (likely(pegasus->rx_pool[i] != NULL)) { -			skb = pegasus->rx_pool[i]; -			pegasus->rx_pool[i] = NULL; -			return skb; -		} -	} -	return NULL; -} -  static void read_bulk_callback(struct urb *urb)  {  	pegasus_t *pegasus = urb->context; @@ -706,9 +520,8 @@ static void read_bulk_callback(struct urb *urb)  	if (pegasus->flags & PEGASUS_UNPLUG)  		return; -	spin_lock(&pegasus->rx_pool_lock); -	pegasus->rx_skb = pull_skb(pegasus); -	spin_unlock(&pegasus->rx_pool_lock); +	pegasus->rx_skb = __netdev_alloc_skb_ip_align(pegasus->net, PEGASUS_MTU, +						      GFP_ATOMIC);  	if (pegasus->rx_skb == NULL)  		goto tl_sched; @@ -736,24 +549,23 @@ tl_sched:  static void rx_fixup(unsigned long data)  {  	pegasus_t *pegasus; -	unsigned long flags;  	int status;  	pegasus = (pegasus_t *) data;  	if (pegasus->flags & PEGASUS_UNPLUG)  		return; -	spin_lock_irqsave(&pegasus->rx_pool_lock, flags); -	fill_skb_pool(pegasus);  	if (pegasus->flags & PEGASUS_RX_URB_FAIL)  		if (pegasus->rx_skb)  			goto try_again;  	if (pegasus->rx_skb == NULL) -		pegasus->rx_skb = pull_skb(pegasus); +		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); -		goto done; +		return;  	}  	usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb,  			  usb_rcvbulkpipe(pegasus->usb, 1), @@ -769,8 +581,6 @@ try_again:  	} else {  		pegasus->flags &= ~PEGASUS_RX_URB_FAIL;  	} -done: -	spin_unlock_irqrestore(&pegasus->rx_pool_lock, flags);  }  static void write_bulk_callback(struct urb *urb) @@ -965,7 +775,6 @@ 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); -	usb_free_urb(pegasus->ctrl_urb);  }  static void unlink_all_urbs(pegasus_t *pegasus) @@ -973,48 +782,42 @@ 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); -	usb_kill_urb(pegasus->ctrl_urb);  }  static int alloc_urbs(pegasus_t *pegasus)  { -	pegasus->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL); -	if (!pegasus->ctrl_urb) -		return 0; +	int res = -ENOMEM; +  	pegasus->rx_urb = usb_alloc_urb(0, GFP_KERNEL);  	if (!pegasus->rx_urb) { -		usb_free_urb(pegasus->ctrl_urb); -		return 0; +		return res;  	}  	pegasus->tx_urb = usb_alloc_urb(0, GFP_KERNEL);  	if (!pegasus->tx_urb) {  		usb_free_urb(pegasus->rx_urb); -		usb_free_urb(pegasus->ctrl_urb); -		return 0; +		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); -		usb_free_urb(pegasus->ctrl_urb); -		return 0; +		return res;  	} -	return 1; +	return 0;  }  static int pegasus_open(struct net_device *net)  {  	pegasus_t *pegasus = netdev_priv(net); -	int res; +	int res=-ENOMEM;  	if (pegasus->rx_skb == NULL) -		pegasus->rx_skb = pull_skb(pegasus); -	/* -	 ** Note: no point to free the pool.  it is empty :-) -	 */ +		pegasus->rx_skb = __netdev_alloc_skb_ip_align(pegasus->net, +							      PEGASUS_MTU, +							      GFP_KERNEL);  	if (!pegasus->rx_skb) -		return -ENOMEM; +		goto exit;  	res = set_registers(pegasus, EthID, 6, net->dev_addr); @@ -1040,13 +843,13 @@ static int pegasus_open(struct net_device *net)  		usb_kill_urb(pegasus->rx_urb);  		goto exit;  	} -	if ((res = enable_net_traffic(net, pegasus->usb))) { +	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); -		free_skb_pool(pegasus);  		goto exit;  	}  	set_carrier(net); @@ -1074,8 +877,9 @@ static void pegasus_get_drvinfo(struct net_device *dev,  				struct ethtool_drvinfo *info)  {  	pegasus_t *pegasus = netdev_priv(dev); -	strncpy(info->driver, driver_name, sizeof(info->driver) - 1); -	strncpy(info->version, DRIVER_VERSION, sizeof(info->version) - 1); + +	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));  } @@ -1096,6 +900,7 @@ 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; @@ -1110,7 +915,12 @@ pegasus_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)  	else  		pegasus->eth_regs[0] &= ~0x10;  	pegasus->wolopts = wol->wolopts; -	return set_register(pegasus, WakeupControl, reg78); + +	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) @@ -1190,7 +1000,7 @@ static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd)  	case SIOCDEVPRIVATE + 2:  		if (!capable(CAP_NET_ADMIN))  			return -EPERM; -		write_mii_word(pegasus, pegasus->phy, data[1] & 0x1f, data[2]); +		write_mii_word(pegasus, pegasus->phy, data[1] & 0x1f, &data[2]);  		res = 0;  		break;  	default: @@ -1214,11 +1024,7 @@ static void pegasus_set_multicast(struct net_device *net)  		pegasus->eth_regs[EthCtrl0] &= ~RX_MULTICAST;  		pegasus->eth_regs[EthCtrl2] &= ~RX_PROMISCUOUS;  	} - -	pegasus->ctrl_urb->status = 0; - -	pegasus->flags |= ETH_REGS_CHANGE; -	ctrl_callback(pegasus->ctrl_urb); +	update_eth_regs_async(pegasus);  }  static __u8 mii_phy_probe(pegasus_t *pegasus) @@ -1329,19 +1135,15 @@ static int pegasus_probe(struct usb_interface *intf,  	}  	pegasus_count++; -	usb_get_dev(dev); -  	net = alloc_etherdev(sizeof(struct pegasus)); -	if (!net) { -		dev_err(&intf->dev, "can't allocate %s\n", "device"); +	if (!net)  		goto out; -	}  	pegasus = netdev_priv(net);  	pegasus->dev_index = dev_index; -	init_waitqueue_head(&pegasus->ctrl_wait); -	if (!alloc_urbs(pegasus)) { +	res = alloc_urbs(pegasus); +	if (res < 0) {  		dev_err(&intf->dev, "can't allocate %s\n", "urbs");  		goto out1;  	} @@ -1357,13 +1159,12 @@ static int pegasus_probe(struct usb_interface *intf,  	net->watchdog_timeo = PEGASUS_TX_TIMEOUT;  	net->netdev_ops = &pegasus_netdev_ops; -	SET_ETHTOOL_OPS(net, &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; -	spin_lock_init(&pegasus->rx_pool_lock);  	pegasus->msg_enable = netif_msg_init(msg_level, NETIF_MSG_DRV  				| NETIF_MSG_PROBE | NETIF_MSG_LINK); @@ -1375,7 +1176,6 @@ static int pegasus_probe(struct usb_interface *intf,  		goto out2;  	}  	set_ethernet_addr(pegasus); -	fill_skb_pool(pegasus);  	if (pegasus->features & PEGASUS_II) {  		dev_info(&intf->dev, "setup Pegasus II specific registers\n");  		setup_pegasus_II(pegasus); @@ -1393,23 +1193,18 @@ static int pegasus_probe(struct usb_interface *intf,  	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); +			   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); -	free_skb_pool(pegasus);  out2:  	free_all_urbs(pegasus);  out1:  	free_netdev(net);  out: -	usb_put_dev(dev);  	pegasus_dec_workqueue();  	return res;  } @@ -1427,10 +1222,8 @@ static void pegasus_disconnect(struct usb_interface *intf)  	pegasus->flags |= PEGASUS_UNPLUG;  	cancel_delayed_work(&pegasus->carrier_check);  	unregister_netdev(pegasus->net); -	usb_put_dev(interface_to_usbdev(intf));  	unlink_all_urbs(pegasus);  	free_all_urbs(pegasus); -	free_skb_pool(pegasus);  	if (pegasus->rx_skb != NULL) {  		dev_kfree_skb(pegasus->rx_skb);  		pegasus->rx_skb = NULL; @@ -1476,7 +1269,7 @@ static const struct net_device_ops pegasus_netdev_ops = {  	.ndo_stop =			pegasus_close,  	.ndo_do_ioctl =			pegasus_ioctl,  	.ndo_start_xmit =		pegasus_start_xmit, -	.ndo_set_multicast_list =	pegasus_set_multicast, +	.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, @@ -1491,6 +1284,7 @@ static struct usb_driver pegasus_driver = {  	.id_table = pegasus_ids,  	.suspend = pegasus_suspend,  	.resume = pegasus_resume, +	.disable_hub_initiated_lpm = 1,  };  static void __init parse_id(char *id) diff --git a/drivers/net/usb/pegasus.h b/drivers/net/usb/pegasus.h index 65b78b35b73..d15646244fd 100644 --- a/drivers/net/usb/pegasus.h +++ b/drivers/net/usb/pegasus.h @@ -1,5 +1,5 @@  /* - * Copyright (c) 1999-2003 Petko Manolov - Petkan (petkan@users.sourceforge.net) + * 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 @@ -13,7 +13,6 @@  #define	HAS_HOME_PNA		0x40000000  #define	PEGASUS_MTU		1536 -#define	RX_SKBS			4  #define	EPROM_WRITE		0x01  #define	EPROM_READ		0x02 @@ -34,8 +33,6 @@  #define	CTRL_URB_SLEEP		0x00000020  #define	PEGASUS_UNPLUG		0x00000040  #define	PEGASUS_RX_URB_FAIL	0x00000080 -#define	ETH_REGS_CHANGE		0x40000000 -#define	ETH_REGS_CHANGED	0x80000000  #define	RX_MULTICAST		2  #define	RX_PROMISCUOUS		4 @@ -96,12 +93,8 @@ typedef struct pegasus {  	int			intr_interval;  	struct tasklet_struct	rx_tl;  	struct delayed_work	carrier_check; -	struct urb		*ctrl_urb, *rx_urb, *tx_urb, *intr_urb; -	struct sk_buff		*rx_pool[RX_SKBS]; +	struct urb		*rx_urb, *tx_urb, *intr_urb;  	struct sk_buff		*rx_skb; -	struct usb_ctrlrequest	dr; -	wait_queue_head_t	ctrl_wait; -	spinlock_t		rx_pool_lock;  	int			chip;  	unsigned char		intr_buff[8];  	__u8			tx_buff[PEGASUS_MTU]; diff --git a/drivers/net/usb/plusb.c b/drivers/net/usb/plusb.c index 08ad269f6b4..3d18bb0eee8 100644 --- a/drivers/net/usb/plusb.c +++ b/drivers/net/usb/plusb.c @@ -13,15 +13,13 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  // #define	DEBUG			// error path messages, extra info  // #define	VERBOSE			// more; success messages  #include <linux/module.h> -#include <linux/init.h>  #include <linux/netdevice.h>  #include <linux/etherdevice.h>  #include <linux/ethtool.h> @@ -45,6 +43,14 @@   * 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.   */  /* @@ -63,13 +69,10 @@  static inline int  pl_vendor_req(struct usbnet *dev, u8 req, u8 val, u8 index)  { -	return usb_control_msg(dev->udev, -		usb_rcvctrlpipe(dev->udev, 0), -		req, -		USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, -		val, index, -		NULL, 0, -		USB_CTRL_GET_TIMEOUT); +	return usbnet_read_cmd(dev, req, +				USB_DIR_IN | USB_TYPE_VENDOR | +				USB_RECIP_DEVICE, +				val, index, NULL, 0);  }  static inline int @@ -86,17 +89,21 @@ pl_set_QuickLink_features(struct usbnet *dev, int val)  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.  	 */ -	(void) pl_set_QuickLink_features(dev, +	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", -	.flags =	FLAG_NO_SETINT, +	.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,  }; @@ -111,6 +118,7 @@ static const struct driver_info	prolific_info = {  static const struct usb_device_id	products [] = { +/* full speed cables */  {  	USB_DEVICE(0x067b, 0x0000),	// PL-2301  	.driver_info =	(unsigned long) &prolific_info, @@ -119,6 +127,15 @@ static const struct usb_device_id	products [] = {  	.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, +}, +  	{ },		// END  };  MODULE_DEVICE_TABLE(usb, products); @@ -130,20 +147,11 @@ static struct usb_driver plusb_driver = {  	.disconnect =	usbnet_disconnect,  	.suspend =	usbnet_suspend,  	.resume =	usbnet_resume, +	.disable_hub_initiated_lpm = 1,  }; -static int __init plusb_init(void) -{ - 	return usb_register(&plusb_driver); -} -module_init(plusb_init); - -static void __exit plusb_exit(void) -{ - 	usb_deregister(&plusb_driver); -} -module_exit(plusb_exit); +module_usb_driver(plusb_driver);  MODULE_AUTHOR("David Brownell"); -MODULE_DESCRIPTION("Prolific PL-2301/2302 USB Host to Host Link Driver"); +MODULE_DESCRIPTION("Prolific PL-2301/2302/25A1 USB Host to Host Link Driver");  MODULE_LICENSE("GPL"); diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c new file mode 100644 index 00000000000..22756db53dc --- /dev/null +++ b/drivers/net/usb/qmi_wwan.c @@ -0,0 +1,878 @@ +/* + * Copyright (c) 2012  Bjørn Mork <bjorn@mork.no> + * + * The probing code is heavily inspired by cdc_ether, which is: + * Copyright (C) 2003-2005 by David Brownell + * Copyright (C) 2006 by Ole Andre Vadla Ravnas (ActiveSync) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/ethtool.h> +#include <linux/etherdevice.h> +#include <linux/mii.h> +#include <linux/usb.h> +#include <linux/usb/cdc.h> +#include <linux/usb/usbnet.h> +#include <linux/usb/cdc-wdm.h> + +/* This driver supports wwan (3G/LTE/?) devices using a vendor + * specific management protocol called Qualcomm MSM Interface (QMI) - + * in addition to the more common AT commands over serial interface + * management + * + * QMI is wrapped in CDC, using CDC encapsulated commands on the + * control ("master") interface of a two-interface CDC Union + * resembling standard CDC ECM.  The devices do not use the control + * interface for any other CDC messages.  Most likely because the + * management protocol is used in place of the standard CDC + * notifications NOTIFY_NETWORK_CONNECTION and NOTIFY_SPEED_CHANGE + * + * Alternatively, control and data functions can be combined in a + * single USB interface. + * + * Handling a protocol like QMI is out of the scope for any driver. + * It is exported as a character device using the cdc-wdm driver as + * a subdriver, enabling userspace applications ("modem managers") to + * handle it. + * + * These devices may alternatively/additionally be configured using AT + * commands on a serial interface + */ + +/* driver specific data */ +struct qmi_wwan_state { +	struct usb_driver *subdriver; +	atomic_t pmcount; +	unsigned long unused; +	struct usb_interface *control; +	struct usb_interface *data; +}; + +/* default ethernet address used by the modem */ +static const u8 default_modem_addr[ETH_ALEN] = {0x02, 0x50, 0xf3}; + +/* Make up an ethernet header if the packet doesn't have one. + * + * A firmware bug common among several devices cause them to send raw + * IP packets under some circumstances.  There is no way for the + * driver/host to know when this will happen.  And even when the bug + * hits, some packets will still arrive with an intact header. + * + * The supported devices are only capably of sending IPv4, IPv6 and + * ARP packets on a point-to-point link. Any packet with an ethernet + * header will have either our address or a broadcast/multicast + * address as destination.  ARP packets will always have a header. + * + * This means that this function will reliably add the appropriate + * header iff necessary, provided our hardware address does not start + * with 4 or 6. + * + * Another common firmware bug results in all packets being addressed + * to 00:a0:c6:00:00:00 despite the host address being different. + * This function will also fixup such packets. + */ +static int qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb) +{ +	__be16 proto; + +	/* This check is no longer done by usbnet */ +	if (skb->len < dev->net->hard_header_len) +		return 0; + +	switch (skb->data[0] & 0xf0) { +	case 0x40: +		proto = htons(ETH_P_IP); +		break; +	case 0x60: +		proto = htons(ETH_P_IPV6); +		break; +	case 0x00: +		if (is_multicast_ether_addr(skb->data)) +			return 1; +		/* possibly bogus destination - rewrite just in case */ +		skb_reset_mac_header(skb); +		goto fix_dest; +	default: +		/* pass along other packets without modifications */ +		return 1; +	} +	if (skb_headroom(skb) < ETH_HLEN) +		return 0; +	skb_push(skb, ETH_HLEN); +	skb_reset_mac_header(skb); +	eth_hdr(skb)->h_proto = proto; +	memset(eth_hdr(skb)->h_source, 0, ETH_ALEN); +fix_dest: +	memcpy(eth_hdr(skb)->h_dest, dev->net->dev_addr, ETH_ALEN); +	return 1; +} + +/* very simplistic detection of IPv4 or IPv6 headers */ +static bool possibly_iphdr(const char *data) +{ +	return (data[0] & 0xd0) == 0x40; +} + +/* disallow addresses which may be confused with IP headers */ +static int qmi_wwan_mac_addr(struct net_device *dev, void *p) +{ +	int ret; +	struct sockaddr *addr = p; + +	ret = eth_prepare_mac_addr_change(dev, p); +	if (ret < 0) +		return ret; +	if (possibly_iphdr(addr->sa_data)) +		return -EADDRNOTAVAIL; +	eth_commit_mac_addr_change(dev, p); +	return 0; +} + +static const struct net_device_ops qmi_wwan_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	= qmi_wwan_mac_addr, +	.ndo_validate_addr	= eth_validate_addr, +}; + +/* using a counter to merge subdriver requests with our own into a + * combined state + */ +static int qmi_wwan_manage_power(struct usbnet *dev, int on) +{ +	struct qmi_wwan_state *info = (void *)&dev->data; +	int rv; + +	dev_dbg(&dev->intf->dev, "%s() pmcount=%d, on=%d\n", __func__, +		atomic_read(&info->pmcount), on); + +	if ((on && atomic_add_return(1, &info->pmcount) == 1) || +	    (!on && atomic_dec_and_test(&info->pmcount))) { +		/* need autopm_get/put here to ensure the usbcore sees +		 * the new value +		 */ +		rv = usb_autopm_get_interface(dev->intf); +		dev->intf->needs_remote_wakeup = on; +		if (!rv) +			usb_autopm_put_interface(dev->intf); +	} +	return 0; +} + +static int qmi_wwan_cdc_wdm_manage_power(struct usb_interface *intf, int on) +{ +	struct usbnet *dev = usb_get_intfdata(intf); + +	/* can be called while disconnecting */ +	if (!dev) +		return 0; +	return qmi_wwan_manage_power(dev, on); +} + +/* collect all three endpoints and register subdriver */ +static int qmi_wwan_register_subdriver(struct usbnet *dev) +{ +	int rv; +	struct usb_driver *subdriver = NULL; +	struct qmi_wwan_state *info = (void *)&dev->data; + +	/* collect bulk endpoints */ +	rv = usbnet_get_endpoints(dev, info->data); +	if (rv < 0) +		goto err; + +	/* update status endpoint if separate control interface */ +	if (info->control != info->data) +		dev->status = &info->control->cur_altsetting->endpoint[0]; + +	/* require interrupt endpoint for subdriver */ +	if (!dev->status) { +		rv = -EINVAL; +		goto err; +	} + +	/* for subdriver power management */ +	atomic_set(&info->pmcount, 0); + +	/* register subdriver */ +	subdriver = usb_cdc_wdm_register(info->control, &dev->status->desc, +					 4096, &qmi_wwan_cdc_wdm_manage_power); +	if (IS_ERR(subdriver)) { +		dev_err(&info->control->dev, "subdriver registration failed\n"); +		rv = PTR_ERR(subdriver); +		goto err; +	} + +	/* prevent usbnet from using status endpoint */ +	dev->status = NULL; + +	/* save subdriver struct for suspend/resume wrappers */ +	info->subdriver = subdriver; + +err: +	return rv; +} + +static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) +{ +	int status = -1; +	u8 *buf = intf->cur_altsetting->extra; +	int len = intf->cur_altsetting->extralen; +	struct usb_interface_descriptor *desc = &intf->cur_altsetting->desc; +	struct usb_cdc_union_desc *cdc_union = NULL; +	struct usb_cdc_ether_desc *cdc_ether = NULL; +	u32 found = 0; +	struct usb_driver *driver = driver_of(intf); +	struct qmi_wwan_state *info = (void *)&dev->data; + +	BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) < +		      sizeof(struct qmi_wwan_state))); + +	/* set up initial state */ +	info->control = intf; +	info->data = intf; + +	/* and a number of CDC descriptors */ +	while (len > 3) { +		struct usb_descriptor_header *h = (void *)buf; + +		/* ignore any misplaced descriptors */ +		if (h->bDescriptorType != USB_DT_CS_INTERFACE) +			goto next_desc; + +		/* buf[2] is CDC descriptor subtype */ +		switch (buf[2]) { +		case USB_CDC_HEADER_TYPE: +			if (found & 1 << USB_CDC_HEADER_TYPE) { +				dev_dbg(&intf->dev, "extra CDC header\n"); +				goto err; +			} +			if (h->bLength != sizeof(struct usb_cdc_header_desc)) { +				dev_dbg(&intf->dev, "CDC header len %u\n", +					h->bLength); +				goto err; +			} +			break; +		case USB_CDC_UNION_TYPE: +			if (found & 1 << USB_CDC_UNION_TYPE) { +				dev_dbg(&intf->dev, "extra CDC union\n"); +				goto err; +			} +			if (h->bLength != sizeof(struct usb_cdc_union_desc)) { +				dev_dbg(&intf->dev, "CDC union len %u\n", +					h->bLength); +				goto err; +			} +			cdc_union = (struct usb_cdc_union_desc *)buf; +			break; +		case USB_CDC_ETHERNET_TYPE: +			if (found & 1 << USB_CDC_ETHERNET_TYPE) { +				dev_dbg(&intf->dev, "extra CDC ether\n"); +				goto err; +			} +			if (h->bLength != sizeof(struct usb_cdc_ether_desc)) { +				dev_dbg(&intf->dev, "CDC ether len %u\n", +					h->bLength); +				goto err; +			} +			cdc_ether = (struct usb_cdc_ether_desc *)buf; +			break; +		} + +		/* Remember which CDC functional descriptors we've seen.  Works +		 * for all types we care about, of which USB_CDC_ETHERNET_TYPE +		 * (0x0f) is the highest numbered +		 */ +		if (buf[2] < 32) +			found |= 1 << buf[2]; + +next_desc: +		len -= h->bLength; +		buf += h->bLength; +	} + +	/* Use separate control and data interfaces if we found a CDC Union */ +	if (cdc_union) { +		info->data = usb_ifnum_to_if(dev->udev, +					     cdc_union->bSlaveInterface0); +		if (desc->bInterfaceNumber != cdc_union->bMasterInterface0 || +		    !info->data) { +			dev_err(&intf->dev, +				"bogus CDC Union: master=%u, slave=%u\n", +				cdc_union->bMasterInterface0, +				cdc_union->bSlaveInterface0); +			goto err; +		} +	} + +	/* errors aren't fatal - we can live with the dynamic address */ +	if (cdc_ether) { +		dev->hard_mtu = le16_to_cpu(cdc_ether->wMaxSegmentSize); +		usbnet_get_ethernet_addr(dev, cdc_ether->iMACAddress); +	} + +	/* claim data interface and set it up */ +	if (info->control != info->data) { +		status = usb_driver_claim_interface(driver, info->data, dev); +		if (status < 0) +			goto err; +	} + +	status = qmi_wwan_register_subdriver(dev); +	if (status < 0 && info->control != info->data) { +		usb_set_intfdata(info->data, NULL); +		usb_driver_release_interface(driver, info->data); +	} + +	/* Never use the same address on both ends of the link, even +	 * if the buggy firmware told us to. +	 */ +	if (ether_addr_equal(dev->net->dev_addr, default_modem_addr)) +		eth_hw_addr_random(dev->net); + +	/* make MAC addr easily distinguishable from an IP header */ +	if (possibly_iphdr(dev->net->dev_addr)) { +		dev->net->dev_addr[0] |= 0x02;	/* set local assignment bit */ +		dev->net->dev_addr[0] &= 0xbf;	/* clear "IP" bit */ +	} +	dev->net->netdev_ops = &qmi_wwan_netdev_ops; +err: +	return status; +} + +static void qmi_wwan_unbind(struct usbnet *dev, struct usb_interface *intf) +{ +	struct qmi_wwan_state *info = (void *)&dev->data; +	struct usb_driver *driver = driver_of(intf); +	struct usb_interface *other; + +	if (info->subdriver && info->subdriver->disconnect) +		info->subdriver->disconnect(info->control); + +	/* allow user to unbind using either control or data */ +	if (intf == info->control) +		other = info->data; +	else +		other = info->control; + +	/* only if not shared */ +	if (other && intf != other) { +		usb_set_intfdata(other, NULL); +		usb_driver_release_interface(driver, other); +	} + +	info->subdriver = NULL; +	info->data = NULL; +	info->control = NULL; +} + +/* suspend/resume wrappers calling both usbnet and the cdc-wdm + * subdriver if present. + * + * NOTE: cdc-wdm also supports pre/post_reset, but we cannot provide + * wrappers for those without adding usbnet reset support first. + */ +static int qmi_wwan_suspend(struct usb_interface *intf, pm_message_t message) +{ +	struct usbnet *dev = usb_get_intfdata(intf); +	struct qmi_wwan_state *info = (void *)&dev->data; +	int ret; + +	/* Both usbnet_suspend() and subdriver->suspend() MUST return 0 +	 * in system sleep context, otherwise, the resume callback has +	 * to recover device from previous suspend failure. +	 */ +	ret = usbnet_suspend(intf, message); +	if (ret < 0) +		goto err; + +	if (intf == info->control && info->subdriver && +	    info->subdriver->suspend) +		ret = info->subdriver->suspend(intf, message); +	if (ret < 0) +		usbnet_resume(intf); +err: +	return ret; +} + +static int qmi_wwan_resume(struct usb_interface *intf) +{ +	struct usbnet *dev = usb_get_intfdata(intf); +	struct qmi_wwan_state *info = (void *)&dev->data; +	int ret = 0; +	bool callsub = (intf == info->control && info->subdriver && +			info->subdriver->resume); + +	if (callsub) +		ret = info->subdriver->resume(intf); +	if (ret < 0) +		goto err; +	ret = usbnet_resume(intf); +	if (ret < 0 && callsub) +		info->subdriver->suspend(intf, PMSG_SUSPEND); +err: +	return ret; +} + +static const struct driver_info	qmi_wwan_info = { +	.description	= "WWAN/QMI device", +	.flags		= FLAG_WWAN, +	.bind		= qmi_wwan_bind, +	.unbind		= qmi_wwan_unbind, +	.manage_power	= qmi_wwan_manage_power, +	.rx_fixup       = qmi_wwan_rx_fixup, +}; + +#define HUAWEI_VENDOR_ID	0x12D1 + +/* map QMI/wwan function by a fixed interface number */ +#define QMI_FIXED_INTF(vend, prod, num) \ +	USB_DEVICE_INTERFACE_NUMBER(vend, prod, num), \ +	.driver_info = (unsigned long)&qmi_wwan_info + +/* Gobi 1000 QMI/wwan interface number is 3 according to qcserial */ +#define QMI_GOBI1K_DEVICE(vend, prod) \ +	QMI_FIXED_INTF(vend, prod, 3) + +/* Gobi 2000/3000 QMI/wwan interface number is 0 according to qcserial */ +#define QMI_GOBI_DEVICE(vend, prod) \ +	QMI_FIXED_INTF(vend, prod, 0) + +static const struct usb_device_id products[] = { +	/* 1. CDC ECM like devices match on the control interface */ +	{	/* Huawei E392, E398 and possibly others sharing both device id and more... */ +		USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 1, 9), +		.driver_info        = (unsigned long)&qmi_wwan_info, +	}, +	{	/* Vodafone/Huawei K5005 (12d1:14c8) and similar modems */ +		USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 1, 57), +		.driver_info        = (unsigned long)&qmi_wwan_info, +	}, +	{	/* HUAWEI_INTERFACE_NDIS_CONTROL_QUALCOMM */ +		USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 0x01, 0x69), +		.driver_info        = (unsigned long)&qmi_wwan_info, +	}, + +	/* 2. Combined interface devices matching on class+protocol */ +	{	/* Huawei E367 and possibly others in "Windows mode" */ +		USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 1, 7), +		.driver_info        = (unsigned long)&qmi_wwan_info, +	}, +	{	/* Huawei E392, E398 and possibly others in "Windows mode" */ +		USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 1, 17), +		.driver_info        = (unsigned long)&qmi_wwan_info, +	}, +	{	/* HUAWEI_NDIS_SINGLE_INTERFACE_VDF */ +		USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 0x01, 0x37), +		.driver_info        = (unsigned long)&qmi_wwan_info, +	}, +	{	/* HUAWEI_INTERFACE_NDIS_HW_QUALCOMM */ +		USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 0x01, 0x67), +		.driver_info        = (unsigned long)&qmi_wwan_info, +	}, +	{	/* Pantech UML290, P4200 and more */ +		USB_VENDOR_AND_INTERFACE_INFO(0x106c, USB_CLASS_VENDOR_SPEC, 0xf0, 0xff), +		.driver_info        = (unsigned long)&qmi_wwan_info, +	}, +	{	/* Pantech UML290 - newer firmware */ +		USB_VENDOR_AND_INTERFACE_INFO(0x106c, USB_CLASS_VENDOR_SPEC, 0xf1, 0xff), +		.driver_info        = (unsigned long)&qmi_wwan_info, +	}, +	{	/* Novatel USB551L and MC551 */ +		USB_DEVICE_AND_INTERFACE_INFO(0x1410, 0xb001, +		                              USB_CLASS_COMM, +		                              USB_CDC_SUBCLASS_ETHERNET, +		                              USB_CDC_PROTO_NONE), +		.driver_info        = (unsigned long)&qmi_wwan_info, +	}, +	{	/* Novatel E362 */ +		USB_DEVICE_AND_INTERFACE_INFO(0x1410, 0x9010, +		                              USB_CLASS_COMM, +		                              USB_CDC_SUBCLASS_ETHERNET, +		                              USB_CDC_PROTO_NONE), +		.driver_info        = (unsigned long)&qmi_wwan_info, +	}, +	{	/* Novatel Expedite E371 */ +		USB_DEVICE_AND_INTERFACE_INFO(0x1410, 0x9011, +		                              USB_CLASS_COMM, +		                              USB_CDC_SUBCLASS_ETHERNET, +		                              USB_CDC_PROTO_NONE), +		.driver_info        = (unsigned long)&qmi_wwan_info, +	}, +	{	/* Dell Wireless 5800 (Novatel E362) */ +		USB_DEVICE_AND_INTERFACE_INFO(0x413C, 0x8195, +					      USB_CLASS_COMM, +					      USB_CDC_SUBCLASS_ETHERNET, +					      USB_CDC_PROTO_NONE), +		.driver_info        = (unsigned long)&qmi_wwan_info, +	}, +	{	/* Dell Wireless 5800 V2 (Novatel E362) */ +		USB_DEVICE_AND_INTERFACE_INFO(0x413C, 0x8196, +					      USB_CLASS_COMM, +					      USB_CDC_SUBCLASS_ETHERNET, +					      USB_CDC_PROTO_NONE), +		.driver_info        = (unsigned long)&qmi_wwan_info, +	}, +	{	/* Dell Wireless 5804 (Novatel E371) */ +		USB_DEVICE_AND_INTERFACE_INFO(0x413C, 0x819b, +					      USB_CLASS_COMM, +					      USB_CDC_SUBCLASS_ETHERNET, +					      USB_CDC_PROTO_NONE), +		.driver_info        = (unsigned long)&qmi_wwan_info, +	}, +	{	/* ADU960S */ +		USB_DEVICE_AND_INTERFACE_INFO(0x16d5, 0x650a, +					      USB_CLASS_COMM, +					      USB_CDC_SUBCLASS_ETHERNET, +					      USB_CDC_PROTO_NONE), +		.driver_info        = (unsigned long)&qmi_wwan_info, +	}, + +	/* 3. Combined interface devices matching on interface number */ +	{QMI_FIXED_INTF(0x0408, 0xea42, 4)},	/* Yota / Megafon M100-1 */ +	{QMI_FIXED_INTF(0x05c6, 0x7000, 0)}, +	{QMI_FIXED_INTF(0x05c6, 0x7001, 1)}, +	{QMI_FIXED_INTF(0x05c6, 0x7002, 1)}, +	{QMI_FIXED_INTF(0x05c6, 0x7101, 1)}, +	{QMI_FIXED_INTF(0x05c6, 0x7101, 2)}, +	{QMI_FIXED_INTF(0x05c6, 0x7101, 3)}, +	{QMI_FIXED_INTF(0x05c6, 0x7102, 1)}, +	{QMI_FIXED_INTF(0x05c6, 0x7102, 2)}, +	{QMI_FIXED_INTF(0x05c6, 0x7102, 3)}, +	{QMI_FIXED_INTF(0x05c6, 0x8000, 7)}, +	{QMI_FIXED_INTF(0x05c6, 0x8001, 6)}, +	{QMI_FIXED_INTF(0x05c6, 0x9000, 4)}, +	{QMI_FIXED_INTF(0x05c6, 0x9003, 4)}, +	{QMI_FIXED_INTF(0x05c6, 0x9005, 2)}, +	{QMI_FIXED_INTF(0x05c6, 0x900a, 4)}, +	{QMI_FIXED_INTF(0x05c6, 0x900b, 2)}, +	{QMI_FIXED_INTF(0x05c6, 0x900c, 4)}, +	{QMI_FIXED_INTF(0x05c6, 0x900c, 5)}, +	{QMI_FIXED_INTF(0x05c6, 0x900c, 6)}, +	{QMI_FIXED_INTF(0x05c6, 0x900d, 5)}, +	{QMI_FIXED_INTF(0x05c6, 0x900f, 3)}, +	{QMI_FIXED_INTF(0x05c6, 0x900f, 4)}, +	{QMI_FIXED_INTF(0x05c6, 0x900f, 5)}, +	{QMI_FIXED_INTF(0x05c6, 0x9010, 4)}, +	{QMI_FIXED_INTF(0x05c6, 0x9010, 5)}, +	{QMI_FIXED_INTF(0x05c6, 0x9011, 3)}, +	{QMI_FIXED_INTF(0x05c6, 0x9011, 4)}, +	{QMI_FIXED_INTF(0x05c6, 0x9021, 1)}, +	{QMI_FIXED_INTF(0x05c6, 0x9022, 2)}, +	{QMI_FIXED_INTF(0x05c6, 0x9025, 4)},	/* Alcatel-sbell ASB TL131 TDD LTE  (China Mobile) */ +	{QMI_FIXED_INTF(0x05c6, 0x9026, 3)}, +	{QMI_FIXED_INTF(0x05c6, 0x902e, 5)}, +	{QMI_FIXED_INTF(0x05c6, 0x9031, 5)}, +	{QMI_FIXED_INTF(0x05c6, 0x9032, 4)}, +	{QMI_FIXED_INTF(0x05c6, 0x9033, 3)}, +	{QMI_FIXED_INTF(0x05c6, 0x9033, 4)}, +	{QMI_FIXED_INTF(0x05c6, 0x9033, 5)}, +	{QMI_FIXED_INTF(0x05c6, 0x9033, 6)}, +	{QMI_FIXED_INTF(0x05c6, 0x9034, 3)}, +	{QMI_FIXED_INTF(0x05c6, 0x9034, 4)}, +	{QMI_FIXED_INTF(0x05c6, 0x9034, 5)}, +	{QMI_FIXED_INTF(0x05c6, 0x9034, 6)}, +	{QMI_FIXED_INTF(0x05c6, 0x9034, 7)}, +	{QMI_FIXED_INTF(0x05c6, 0x9035, 4)}, +	{QMI_FIXED_INTF(0x05c6, 0x9036, 3)}, +	{QMI_FIXED_INTF(0x05c6, 0x9037, 5)}, +	{QMI_FIXED_INTF(0x05c6, 0x9038, 4)}, +	{QMI_FIXED_INTF(0x05c6, 0x903b, 7)}, +	{QMI_FIXED_INTF(0x05c6, 0x903c, 6)}, +	{QMI_FIXED_INTF(0x05c6, 0x903d, 6)}, +	{QMI_FIXED_INTF(0x05c6, 0x903e, 5)}, +	{QMI_FIXED_INTF(0x05c6, 0x9043, 3)}, +	{QMI_FIXED_INTF(0x05c6, 0x9046, 3)}, +	{QMI_FIXED_INTF(0x05c6, 0x9046, 4)}, +	{QMI_FIXED_INTF(0x05c6, 0x9046, 5)}, +	{QMI_FIXED_INTF(0x05c6, 0x9047, 2)}, +	{QMI_FIXED_INTF(0x05c6, 0x9047, 3)}, +	{QMI_FIXED_INTF(0x05c6, 0x9047, 4)}, +	{QMI_FIXED_INTF(0x05c6, 0x9048, 4)}, +	{QMI_FIXED_INTF(0x05c6, 0x9048, 5)}, +	{QMI_FIXED_INTF(0x05c6, 0x9048, 6)}, +	{QMI_FIXED_INTF(0x05c6, 0x9048, 7)}, +	{QMI_FIXED_INTF(0x05c6, 0x9048, 8)}, +	{QMI_FIXED_INTF(0x05c6, 0x904c, 5)}, +	{QMI_FIXED_INTF(0x05c6, 0x904c, 6)}, +	{QMI_FIXED_INTF(0x05c6, 0x904c, 7)}, +	{QMI_FIXED_INTF(0x05c6, 0x904c, 8)}, +	{QMI_FIXED_INTF(0x05c6, 0x9050, 3)}, +	{QMI_FIXED_INTF(0x05c6, 0x9052, 4)}, +	{QMI_FIXED_INTF(0x05c6, 0x9053, 6)}, +	{QMI_FIXED_INTF(0x05c6, 0x9053, 7)}, +	{QMI_FIXED_INTF(0x05c6, 0x9054, 5)}, +	{QMI_FIXED_INTF(0x05c6, 0x9054, 6)}, +	{QMI_FIXED_INTF(0x05c6, 0x9055, 3)}, +	{QMI_FIXED_INTF(0x05c6, 0x9055, 4)}, +	{QMI_FIXED_INTF(0x05c6, 0x9055, 5)}, +	{QMI_FIXED_INTF(0x05c6, 0x9055, 6)}, +	{QMI_FIXED_INTF(0x05c6, 0x9055, 7)}, +	{QMI_FIXED_INTF(0x05c6, 0x9056, 3)}, +	{QMI_FIXED_INTF(0x05c6, 0x9062, 2)}, +	{QMI_FIXED_INTF(0x05c6, 0x9062, 3)}, +	{QMI_FIXED_INTF(0x05c6, 0x9062, 4)}, +	{QMI_FIXED_INTF(0x05c6, 0x9062, 5)}, +	{QMI_FIXED_INTF(0x05c6, 0x9062, 6)}, +	{QMI_FIXED_INTF(0x05c6, 0x9062, 7)}, +	{QMI_FIXED_INTF(0x05c6, 0x9062, 8)}, +	{QMI_FIXED_INTF(0x05c6, 0x9062, 9)}, +	{QMI_FIXED_INTF(0x05c6, 0x9064, 3)}, +	{QMI_FIXED_INTF(0x05c6, 0x9065, 6)}, +	{QMI_FIXED_INTF(0x05c6, 0x9065, 7)}, +	{QMI_FIXED_INTF(0x05c6, 0x9066, 5)}, +	{QMI_FIXED_INTF(0x05c6, 0x9066, 6)}, +	{QMI_FIXED_INTF(0x05c6, 0x9067, 1)}, +	{QMI_FIXED_INTF(0x05c6, 0x9068, 2)}, +	{QMI_FIXED_INTF(0x05c6, 0x9068, 3)}, +	{QMI_FIXED_INTF(0x05c6, 0x9068, 4)}, +	{QMI_FIXED_INTF(0x05c6, 0x9068, 5)}, +	{QMI_FIXED_INTF(0x05c6, 0x9068, 6)}, +	{QMI_FIXED_INTF(0x05c6, 0x9068, 7)}, +	{QMI_FIXED_INTF(0x05c6, 0x9069, 5)}, +	{QMI_FIXED_INTF(0x05c6, 0x9069, 6)}, +	{QMI_FIXED_INTF(0x05c6, 0x9069, 7)}, +	{QMI_FIXED_INTF(0x05c6, 0x9069, 8)}, +	{QMI_FIXED_INTF(0x05c6, 0x9070, 4)}, +	{QMI_FIXED_INTF(0x05c6, 0x9070, 5)}, +	{QMI_FIXED_INTF(0x05c6, 0x9075, 5)}, +	{QMI_FIXED_INTF(0x05c6, 0x9076, 4)}, +	{QMI_FIXED_INTF(0x05c6, 0x9076, 5)}, +	{QMI_FIXED_INTF(0x05c6, 0x9076, 6)}, +	{QMI_FIXED_INTF(0x05c6, 0x9076, 7)}, +	{QMI_FIXED_INTF(0x05c6, 0x9076, 8)}, +	{QMI_FIXED_INTF(0x05c6, 0x9077, 3)}, +	{QMI_FIXED_INTF(0x05c6, 0x9077, 4)}, +	{QMI_FIXED_INTF(0x05c6, 0x9077, 5)}, +	{QMI_FIXED_INTF(0x05c6, 0x9077, 6)}, +	{QMI_FIXED_INTF(0x05c6, 0x9078, 3)}, +	{QMI_FIXED_INTF(0x05c6, 0x9079, 4)}, +	{QMI_FIXED_INTF(0x05c6, 0x9079, 5)}, +	{QMI_FIXED_INTF(0x05c6, 0x9079, 6)}, +	{QMI_FIXED_INTF(0x05c6, 0x9079, 7)}, +	{QMI_FIXED_INTF(0x05c6, 0x9079, 8)}, +	{QMI_FIXED_INTF(0x05c6, 0x9080, 5)}, +	{QMI_FIXED_INTF(0x05c6, 0x9080, 6)}, +	{QMI_FIXED_INTF(0x05c6, 0x9080, 7)}, +	{QMI_FIXED_INTF(0x05c6, 0x9080, 8)}, +	{QMI_FIXED_INTF(0x05c6, 0x9083, 3)}, +	{QMI_FIXED_INTF(0x05c6, 0x9084, 4)}, +	{QMI_FIXED_INTF(0x05c6, 0x920d, 0)}, +	{QMI_FIXED_INTF(0x05c6, 0x920d, 5)}, +	{QMI_FIXED_INTF(0x0846, 0x68a2, 8)}, +	{QMI_FIXED_INTF(0x12d1, 0x140c, 1)},	/* Huawei E173 */ +	{QMI_FIXED_INTF(0x12d1, 0x14ac, 1)},	/* Huawei E1820 */ +	{QMI_FIXED_INTF(0x16d8, 0x6003, 0)},	/* CMOTech 6003 */ +	{QMI_FIXED_INTF(0x16d8, 0x6007, 0)},	/* CMOTech CHE-628S */ +	{QMI_FIXED_INTF(0x16d8, 0x6008, 0)},	/* CMOTech CMU-301 */ +	{QMI_FIXED_INTF(0x16d8, 0x6280, 0)},	/* CMOTech CHU-628 */ +	{QMI_FIXED_INTF(0x16d8, 0x7001, 0)},	/* CMOTech CHU-720S */ +	{QMI_FIXED_INTF(0x16d8, 0x7002, 0)},	/* CMOTech 7002 */ +	{QMI_FIXED_INTF(0x16d8, 0x7003, 4)},	/* CMOTech CHU-629K */ +	{QMI_FIXED_INTF(0x16d8, 0x7004, 3)},	/* CMOTech 7004 */ +	{QMI_FIXED_INTF(0x16d8, 0x7006, 5)},	/* CMOTech CGU-629 */ +	{QMI_FIXED_INTF(0x16d8, 0x700a, 4)},	/* CMOTech CHU-629S */ +	{QMI_FIXED_INTF(0x16d8, 0x7211, 0)},	/* CMOTech CHU-720I */ +	{QMI_FIXED_INTF(0x16d8, 0x7212, 0)},	/* CMOTech 7212 */ +	{QMI_FIXED_INTF(0x16d8, 0x7213, 0)},	/* CMOTech 7213 */ +	{QMI_FIXED_INTF(0x16d8, 0x7251, 1)},	/* CMOTech 7251 */ +	{QMI_FIXED_INTF(0x16d8, 0x7252, 1)},	/* CMOTech 7252 */ +	{QMI_FIXED_INTF(0x16d8, 0x7253, 1)},	/* CMOTech 7253 */ +	{QMI_FIXED_INTF(0x19d2, 0x0002, 1)}, +	{QMI_FIXED_INTF(0x19d2, 0x0012, 1)}, +	{QMI_FIXED_INTF(0x19d2, 0x0017, 3)}, +	{QMI_FIXED_INTF(0x19d2, 0x0019, 3)},	/* ONDA MT689DC */ +	{QMI_FIXED_INTF(0x19d2, 0x0021, 4)}, +	{QMI_FIXED_INTF(0x19d2, 0x0025, 1)}, +	{QMI_FIXED_INTF(0x19d2, 0x0031, 4)}, +	{QMI_FIXED_INTF(0x19d2, 0x0042, 4)}, +	{QMI_FIXED_INTF(0x19d2, 0x0049, 5)}, +	{QMI_FIXED_INTF(0x19d2, 0x0052, 4)}, +	{QMI_FIXED_INTF(0x19d2, 0x0055, 1)},	/* ZTE (Vodafone) K3520-Z */ +	{QMI_FIXED_INTF(0x19d2, 0x0058, 4)}, +	{QMI_FIXED_INTF(0x19d2, 0x0063, 4)},	/* ZTE (Vodafone) K3565-Z */ +	{QMI_FIXED_INTF(0x19d2, 0x0104, 4)},	/* ZTE (Vodafone) K4505-Z */ +	{QMI_FIXED_INTF(0x19d2, 0x0113, 5)}, +	{QMI_FIXED_INTF(0x19d2, 0x0118, 5)}, +	{QMI_FIXED_INTF(0x19d2, 0x0121, 5)}, +	{QMI_FIXED_INTF(0x19d2, 0x0123, 4)}, +	{QMI_FIXED_INTF(0x19d2, 0x0124, 5)}, +	{QMI_FIXED_INTF(0x19d2, 0x0125, 6)}, +	{QMI_FIXED_INTF(0x19d2, 0x0126, 5)}, +	{QMI_FIXED_INTF(0x19d2, 0x0130, 1)}, +	{QMI_FIXED_INTF(0x19d2, 0x0133, 3)}, +	{QMI_FIXED_INTF(0x19d2, 0x0141, 5)}, +	{QMI_FIXED_INTF(0x19d2, 0x0157, 5)},	/* ZTE MF683 */ +	{QMI_FIXED_INTF(0x19d2, 0x0158, 3)}, +	{QMI_FIXED_INTF(0x19d2, 0x0167, 4)},	/* ZTE MF820D */ +	{QMI_FIXED_INTF(0x19d2, 0x0168, 4)}, +	{QMI_FIXED_INTF(0x19d2, 0x0176, 3)}, +	{QMI_FIXED_INTF(0x19d2, 0x0178, 3)}, +	{QMI_FIXED_INTF(0x19d2, 0x0191, 4)},	/* ZTE EuFi890 */ +	{QMI_FIXED_INTF(0x19d2, 0x0199, 1)},	/* ZTE MF820S */ +	{QMI_FIXED_INTF(0x19d2, 0x0200, 1)}, +	{QMI_FIXED_INTF(0x19d2, 0x0257, 3)},	/* ZTE MF821 */ +	{QMI_FIXED_INTF(0x19d2, 0x0265, 4)},	/* ONDA MT8205 4G LTE */ +	{QMI_FIXED_INTF(0x19d2, 0x0284, 4)},	/* ZTE MF880 */ +	{QMI_FIXED_INTF(0x19d2, 0x0326, 4)},	/* ZTE MF821D */ +	{QMI_FIXED_INTF(0x19d2, 0x0412, 4)},	/* Telewell TW-LTE 4G */ +	{QMI_FIXED_INTF(0x19d2, 0x1008, 4)},	/* ZTE (Vodafone) K3570-Z */ +	{QMI_FIXED_INTF(0x19d2, 0x1010, 4)},	/* ZTE (Vodafone) K3571-Z */ +	{QMI_FIXED_INTF(0x19d2, 0x1012, 4)}, +	{QMI_FIXED_INTF(0x19d2, 0x1018, 3)},	/* ZTE (Vodafone) K5006-Z */ +	{QMI_FIXED_INTF(0x19d2, 0x1021, 2)}, +	{QMI_FIXED_INTF(0x19d2, 0x1245, 4)}, +	{QMI_FIXED_INTF(0x19d2, 0x1247, 4)}, +	{QMI_FIXED_INTF(0x19d2, 0x1252, 4)}, +	{QMI_FIXED_INTF(0x19d2, 0x1254, 4)}, +	{QMI_FIXED_INTF(0x19d2, 0x1255, 3)}, +	{QMI_FIXED_INTF(0x19d2, 0x1255, 4)}, +	{QMI_FIXED_INTF(0x19d2, 0x1256, 4)}, +	{QMI_FIXED_INTF(0x19d2, 0x1270, 5)},	/* ZTE MF667 */ +	{QMI_FIXED_INTF(0x19d2, 0x1401, 2)}, +	{QMI_FIXED_INTF(0x19d2, 0x1402, 2)},	/* ZTE MF60 */ +	{QMI_FIXED_INTF(0x19d2, 0x1424, 2)}, +	{QMI_FIXED_INTF(0x19d2, 0x1425, 2)}, +	{QMI_FIXED_INTF(0x19d2, 0x1426, 2)},	/* ZTE MF91 */ +	{QMI_FIXED_INTF(0x19d2, 0x1428, 2)},	/* Telewell TW-LTE 4G v2 */ +	{QMI_FIXED_INTF(0x19d2, 0x2002, 4)},	/* ZTE (Vodafone) K3765-Z */ +	{QMI_FIXED_INTF(0x0f3d, 0x68a2, 8)},    /* Sierra Wireless MC7700 */ +	{QMI_FIXED_INTF(0x114f, 0x68a2, 8)},    /* Sierra Wireless MC7750 */ +	{QMI_FIXED_INTF(0x1199, 0x68a2, 8)},	/* Sierra Wireless MC7710 in QMI mode */ +	{QMI_FIXED_INTF(0x1199, 0x68a2, 19)},	/* Sierra Wireless MC7710 in QMI mode */ +	{QMI_FIXED_INTF(0x1199, 0x68c0, 8)},	/* Sierra Wireless MC73xx */ +	{QMI_FIXED_INTF(0x1199, 0x68c0, 10)},	/* Sierra Wireless MC73xx */ +	{QMI_FIXED_INTF(0x1199, 0x901c, 8)},    /* Sierra Wireless EM7700 */ +	{QMI_FIXED_INTF(0x1199, 0x901f, 8)},    /* Sierra Wireless EM7355 */ +	{QMI_FIXED_INTF(0x1199, 0x9041, 8)},	/* Sierra Wireless MC7305/MC7355 */ +	{QMI_FIXED_INTF(0x1199, 0x9051, 8)},	/* Netgear AirCard 340U */ +	{QMI_FIXED_INTF(0x1199, 0x9053, 8)},	/* Sierra Wireless Modem */ +	{QMI_FIXED_INTF(0x1199, 0x9054, 8)},	/* Sierra Wireless Modem */ +	{QMI_FIXED_INTF(0x1199, 0x9055, 8)},	/* Netgear AirCard 341U */ +	{QMI_FIXED_INTF(0x1199, 0x9056, 8)},	/* Sierra Wireless Modem */ +	{QMI_FIXED_INTF(0x1199, 0x9057, 8)}, +	{QMI_FIXED_INTF(0x1199, 0x9061, 8)},	/* Sierra Wireless Modem */ +	{QMI_FIXED_INTF(0x1bbb, 0x011e, 4)},	/* Telekom Speedstick LTE II (Alcatel One Touch L100V LTE) */ +	{QMI_FIXED_INTF(0x1bbb, 0x0203, 2)},	/* Alcatel L800MA */ +	{QMI_FIXED_INTF(0x2357, 0x0201, 4)},	/* TP-LINK HSUPA Modem MA180 */ +	{QMI_FIXED_INTF(0x2357, 0x9000, 4)},	/* TP-LINK MA260 */ +	{QMI_FIXED_INTF(0x1bc7, 0x1200, 5)},	/* Telit LE920 */ +	{QMI_FIXED_INTF(0x1bc7, 0x1201, 2)},	/* Telit LE920 */ +	{QMI_FIXED_INTF(0x0b3c, 0xc000, 4)},	/* Olivetti Olicard 100 */ +	{QMI_FIXED_INTF(0x0b3c, 0xc001, 4)},	/* Olivetti Olicard 120 */ +	{QMI_FIXED_INTF(0x0b3c, 0xc002, 4)},	/* Olivetti Olicard 140 */ +	{QMI_FIXED_INTF(0x0b3c, 0xc004, 6)},	/* Olivetti Olicard 155 */ +	{QMI_FIXED_INTF(0x0b3c, 0xc005, 6)},	/* Olivetti Olicard 200 */ +	{QMI_FIXED_INTF(0x0b3c, 0xc00a, 6)},	/* Olivetti Olicard 160 */ +	{QMI_FIXED_INTF(0x0b3c, 0xc00b, 4)},	/* Olivetti Olicard 500 */ +	{QMI_FIXED_INTF(0x1e2d, 0x0060, 4)},	/* Cinterion PLxx */ +	{QMI_FIXED_INTF(0x1e2d, 0x0053, 4)},	/* Cinterion PHxx,PXxx */ +	{QMI_FIXED_INTF(0x413c, 0x81a2, 8)},	/* Dell Wireless 5806 Gobi(TM) 4G LTE Mobile Broadband Card */ +	{QMI_FIXED_INTF(0x413c, 0x81a3, 8)},	/* Dell Wireless 5570 HSPA+ (42Mbps) Mobile Broadband Card */ +	{QMI_FIXED_INTF(0x413c, 0x81a4, 8)},	/* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */ +	{QMI_FIXED_INTF(0x413c, 0x81a8, 8)},	/* Dell Wireless 5808 Gobi(TM) 4G LTE Mobile Broadband Card */ +	{QMI_FIXED_INTF(0x413c, 0x81a9, 8)},	/* Dell Wireless 5808e Gobi(TM) 4G LTE Mobile Broadband Card */ + +	/* 4. Gobi 1000 devices */ +	{QMI_GOBI1K_DEVICE(0x05c6, 0x9212)},	/* Acer Gobi Modem Device */ +	{QMI_GOBI1K_DEVICE(0x03f0, 0x1f1d)},	/* HP un2400 Gobi Modem Device */ +	{QMI_GOBI1K_DEVICE(0x04da, 0x250d)},	/* Panasonic Gobi Modem device */ +	{QMI_GOBI1K_DEVICE(0x413c, 0x8172)},	/* Dell Gobi Modem device */ +	{QMI_GOBI1K_DEVICE(0x1410, 0xa001)},	/* Novatel/Verizon USB-1000 */ +	{QMI_GOBI1K_DEVICE(0x1410, 0xa002)},	/* Novatel Gobi Modem device */ +	{QMI_GOBI1K_DEVICE(0x1410, 0xa003)},	/* Novatel Gobi Modem device */ +	{QMI_GOBI1K_DEVICE(0x1410, 0xa004)},	/* Novatel Gobi Modem device */ +	{QMI_GOBI1K_DEVICE(0x1410, 0xa005)},	/* Novatel Gobi Modem device */ +	{QMI_GOBI1K_DEVICE(0x1410, 0xa006)},	/* Novatel Gobi Modem device */ +	{QMI_GOBI1K_DEVICE(0x1410, 0xa007)},	/* Novatel Gobi Modem device */ +	{QMI_GOBI1K_DEVICE(0x0b05, 0x1776)},	/* Asus Gobi Modem device */ +	{QMI_GOBI1K_DEVICE(0x19d2, 0xfff3)},	/* ONDA Gobi Modem device */ +	{QMI_GOBI1K_DEVICE(0x05c6, 0x9001)},	/* Generic Gobi Modem device */ +	{QMI_GOBI1K_DEVICE(0x05c6, 0x9002)},	/* Generic Gobi Modem device */ +	{QMI_GOBI1K_DEVICE(0x05c6, 0x9202)},	/* Generic Gobi Modem device */ +	{QMI_GOBI1K_DEVICE(0x05c6, 0x9203)},	/* Generic Gobi Modem device */ +	{QMI_GOBI1K_DEVICE(0x05c6, 0x9222)},	/* Generic Gobi Modem device */ +	{QMI_GOBI1K_DEVICE(0x05c6, 0x9009)},	/* Generic Gobi Modem device */ + +	/* 5. Gobi 2000 and 3000 devices */ +	{QMI_GOBI_DEVICE(0x413c, 0x8186)},	/* Dell Gobi 2000 Modem device (N0218, VU936) */ +	{QMI_GOBI_DEVICE(0x413c, 0x8194)},	/* Dell Gobi 3000 Composite */ +	{QMI_GOBI_DEVICE(0x05c6, 0x920b)},	/* Generic Gobi 2000 Modem device */ +	{QMI_GOBI_DEVICE(0x05c6, 0x9225)},	/* Sony Gobi 2000 Modem device (N0279, VU730) */ +	{QMI_GOBI_DEVICE(0x05c6, 0x9245)},	/* Samsung Gobi 2000 Modem device (VL176) */ +	{QMI_GOBI_DEVICE(0x03f0, 0x251d)},	/* HP Gobi 2000 Modem device (VP412) */ +	{QMI_GOBI_DEVICE(0x05c6, 0x9215)},	/* Acer Gobi 2000 Modem device (VP413) */ +	{QMI_GOBI_DEVICE(0x05c6, 0x9265)},	/* Asus Gobi 2000 Modem device (VR305) */ +	{QMI_GOBI_DEVICE(0x05c6, 0x9235)},	/* Top Global Gobi 2000 Modem device (VR306) */ +	{QMI_GOBI_DEVICE(0x05c6, 0x9275)},	/* iRex Technologies Gobi 2000 Modem device (VR307) */ +	{QMI_GOBI_DEVICE(0x0af0, 0x8120)},	/* Option GTM681W */ +	{QMI_GOBI_DEVICE(0x1199, 0x68a5)},	/* Sierra Wireless Modem */ +	{QMI_GOBI_DEVICE(0x1199, 0x68a9)},	/* Sierra Wireless Modem */ +	{QMI_GOBI_DEVICE(0x1199, 0x9001)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */ +	{QMI_GOBI_DEVICE(0x1199, 0x9002)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */ +	{QMI_GOBI_DEVICE(0x1199, 0x9003)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */ +	{QMI_GOBI_DEVICE(0x1199, 0x9004)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */ +	{QMI_GOBI_DEVICE(0x1199, 0x9005)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */ +	{QMI_GOBI_DEVICE(0x1199, 0x9006)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */ +	{QMI_GOBI_DEVICE(0x1199, 0x9007)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */ +	{QMI_GOBI_DEVICE(0x1199, 0x9008)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */ +	{QMI_GOBI_DEVICE(0x1199, 0x9009)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */ +	{QMI_GOBI_DEVICE(0x1199, 0x900a)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */ +	{QMI_GOBI_DEVICE(0x1199, 0x9011)},	/* Sierra Wireless Gobi 2000 Modem device (MC8305) */ +	{QMI_GOBI_DEVICE(0x16d8, 0x8002)},	/* CMDTech Gobi 2000 Modem device (VU922) */ +	{QMI_GOBI_DEVICE(0x05c6, 0x9205)},	/* Gobi 2000 Modem device */ +	{QMI_GOBI_DEVICE(0x1199, 0x9013)},	/* Sierra Wireless Gobi 3000 Modem device (MC8355) */ +	{QMI_GOBI_DEVICE(0x03f0, 0x371d)},	/* HP un2430 Mobile Broadband Module */ +	{QMI_GOBI_DEVICE(0x1199, 0x9015)},	/* Sierra Wireless Gobi 3000 Modem device */ +	{QMI_GOBI_DEVICE(0x1199, 0x9019)},	/* Sierra Wireless Gobi 3000 Modem device */ +	{QMI_GOBI_DEVICE(0x1199, 0x901b)},	/* Sierra Wireless MC7770 */ +	{QMI_GOBI_DEVICE(0x12d1, 0x14f1)},	/* Sony Gobi 3000 Composite */ +	{QMI_GOBI_DEVICE(0x1410, 0xa021)},	/* Foxconn Gobi 3000 Modem device (Novatel E396) */ + +	{ }					/* END */ +}; +MODULE_DEVICE_TABLE(usb, products); + +static int qmi_wwan_probe(struct usb_interface *intf, +			  const struct usb_device_id *prod) +{ +	struct usb_device_id *id = (struct usb_device_id *)prod; + +	/* Workaround to enable dynamic IDs.  This disables usbnet +	 * blacklisting functionality.  Which, if required, can be +	 * reimplemented here by using a magic "blacklist" value +	 * instead of 0 in the static device id table +	 */ +	if (!id->driver_info) { +		dev_dbg(&intf->dev, "setting defaults for dynamic device id\n"); +		id->driver_info = (unsigned long)&qmi_wwan_info; +	} + +	return usbnet_probe(intf, id); +} + +static struct usb_driver qmi_wwan_driver = { +	.name		      = "qmi_wwan", +	.id_table	      = products, +	.probe		      = qmi_wwan_probe, +	.disconnect	      = usbnet_disconnect, +	.suspend	      = qmi_wwan_suspend, +	.resume		      =	qmi_wwan_resume, +	.reset_resume         = qmi_wwan_resume, +	.supports_autosuspend = 1, +	.disable_hub_initiated_lpm = 1, +}; + +module_usb_driver(qmi_wwan_driver); + +MODULE_AUTHOR("Bjørn Mork <bjorn@mork.no>"); +MODULE_DESCRIPTION("Qualcomm MSM Interface (QMI) WWAN driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c new file mode 100644 index 00000000000..3eab74c7c55 --- /dev/null +++ b/drivers/net/usb/r8152.c @@ -0,0 +1,3539 @@ +/* + *  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 <linux/signal.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/mii.h> +#include <linux/ethtool.h> +#include <linux/usb.h> +#include <linux/crc32.h> +#include <linux/if_vlan.h> +#include <linux/uaccess.h> +#include <linux/list.h> +#include <linux/ip.h> +#include <linux/ipv6.h> +#include <net/ip6_checksum.h> + +/* Version Information */ +#define DRIVER_VERSION "v1.06.0 (2014/03/03)" +#define DRIVER_AUTHOR "Realtek linux nic maintainers <nic_swsd@realtek.com>" +#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_RSTTALLY		0xe800 +#define PLA_CR			0xe813 +#define PLA_CRWECR		0xe81c +#define PLA_CONFIG12		0xe81e	/* CONFIG1, CONFIG2 */ +#define PLA_CONFIG34		0xe820	/* CONFIG3, CONFIG4 */ +#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_TALLYCNT		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_RSTTALLY */ +#define TALLY_RESET		0x0001 + +/* 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_CONFIG34 */ +#define LINK_ON_WAKE_EN		0x0010 +#define LINK_OFF_WAKE_EN	0x0008 + +/* PLA_CONFIG5 */ +#define BWF_EN			0x0040 +#define MWF_EN			0x0020 +#define UWF_EN			0x0010 +#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		0x0002 + +/* 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, +	SELECTIVE_SUSPEND, +	PHY_RESET, +	SCHEDULE_TASKLET, +}; + +/* 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 MCU_TYPE_PLA			0x0100 +#define MCU_TYPE_USB			0x0000 + +#define REALTEK_USB_DEVICE(vend, prod)	\ +	USB_DEVICE_INTERFACE_CLASS(vend, prod, USB_CLASS_VENDOR_SPEC) + +struct tally_counter { +	__le64	tx_packets; +	__le64	rx_packets; +	__le64	tx_errors; +	__le32	rx_errors; +	__le16	rx_missed; +	__le16	align_errors; +	__le32	tx_one_collision; +	__le32	tx_multi_collision; +	__le64	rx_unicast; +	__le64	rx_broadcast; +	__le32	rx_multicast; +	__le16	tx_aborted; +	__le16	tx_underun; +}; + +struct rx_desc { +	__le32 opts1; +#define RX_LEN_MASK			0x7fff + +	__le32 opts2; +#define RD_UDP_CS			(1 << 23) +#define RD_TCP_CS			(1 << 22) +#define RD_IPV6_CS			(1 << 20) +#define RD_IPV4_CS			(1 << 19) + +	__le32 opts3; +#define IPF				(1 << 23) /* IP checksum fail */ +#define UDPF				(1 << 22) /* UDP checksum fail */ +#define TCPF				(1 << 21) /* TCP checksum fail */ + +	__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 GTSENDV4		(1 << 28) +#define GTSENDV6		(1 << 27) +#define GTTCPHO_SHIFT		18 +#define GTTCPHO_MAX		0x7fU +#define TX_LEN_MAX		0x3ffffU + +	__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 */ +#define MSS_SHIFT		17 +#define MSS_MAX			0x7ffU +#define TCPHO_SHIFT		17 +#define TCPHO_MAX		0x7ffU +}; + +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 (*up)(struct r8152 *); +		void (*down)(struct r8152 *); +		void (*unload)(struct r8152 *); +	} rtl_ops; + +	int intr_interval; +	u32 saved_wolopts; +	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 +}; + +enum tx_csum_stat { +	TX_CSUM_SUCCESS = 0, +	TX_CSUM_TSO, +	TX_CSUM_NONE +}; + +/* 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; + +#define RTL_LIMITED_TSO_SIZE	(rx_buf_sz - sizeof(struct tx_desc) - \ +				 VLAN_ETH_HLEN - VLAN_HLEN) + +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 = kmemdup(data, size, GFP_KERNEL); +	if (!tmp) +		return -ENOMEM; + +	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); +	int ret; + +	if (test_bit(RTL8152_UNPLUG, &tp->flags)) +		return -ENODEV; + +	if (phy_id != R8152_PHY_ID) +		return -EINVAL; + +	ret = usb_autopm_get_interface(tp->intf); +	if (ret < 0) +		goto out; + +	ret = r8152_mdio_read(tp, reg); + +	usb_autopm_put_interface(tp->intf); + +out: +	return ret; +} + +static +void write_mii_word(struct net_device *netdev, int phy_id, int reg, int val) +{ +	struct r8152 *tp = netdev_priv(netdev); + +	if (test_bit(RTL8152_UNPLUG, &tp->flags)) +		return; + +	if (phy_id != R8152_PHY_ID) +		return; + +	if (usb_autopm_get_interface(tp->intf) < 0) +		return; + +	r8152_mdio_write(tp, reg, val); + +	usb_autopm_put_interface(tp->intf); +} + +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; +	int ret; +	u8 node_id[8] = {0}; + +	if (tp->version == RTL_VER_01) +		ret = pla_ocp_read(tp, PLA_IDR, sizeof(node_id), node_id); +	else +		ret = pla_ocp_read(tp, PLA_BACKUP, sizeof(node_id), node_id); + +	if (ret < 0) { +		netif_notice(tp, probe, dev, "inet addr fail\n"); +	} else { +		if (tp->version != RTL_VER_01) { +			ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, +				       CRWECR_CONFIG); +			pla_ocp_write(tp, PLA_IDR, BYTE_EN_SIX_BYTES, +				      sizeof(node_id), node_id); +			ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, +				       CRWECR_NORAML); +		} + +		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 void read_bulk_callback(struct urb *urb) +{ +	struct net_device *netdev; +	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; + +	usb_mark_last_busy(tp->udev); + +	switch (status) { +	case 0: +		if (urb->actual_length < ETH_ZLEN) +			break; + +		spin_lock(&tp->rx_lock); +		list_add_tail(&agg->list, &tp->rx_done); +		spin_unlock(&tp->rx_lock); +		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(&tp->rx_lock); +		list_add_tail(&agg->list, &tp->rx_done); +		spin_unlock(&tp->rx_lock); +		tasklet_schedule(&tp->tl); +	} +} + +static void write_bulk_callback(struct urb *urb) +{ +	struct net_device_stats *stats; +	struct net_device *netdev; +	struct tx_agg *agg; +	struct r8152 *tp; +	int status = urb->status; + +	agg = urb->context; +	if (!agg) +		return; + +	tp = agg->context; +	if (!tp) +		return; + +	netdev = tp->netdev; +	stats = &netdev->stats; +	if (status) { +		if (net_ratelimit()) +			netdev_warn(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(&tp->tx_lock); +	list_add_tail(&agg->list, &tp->tx_free); +	spin_unlock(&tp->tx_lock); + +	usb_autopm_put_interface_async(tp->intf); + +	if (!netif_carrier_ok(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; + +	if (list_empty(&tp->tx_free)) +		return NULL; + +	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 inline __be16 get_protocol(struct sk_buff *skb) +{ +	__be16 protocol; + +	if (skb->protocol == htons(ETH_P_8021Q)) +		protocol = vlan_eth_hdr(skb)->h_vlan_encapsulated_proto; +	else +		protocol = skb->protocol; + +	return protocol; +} + +/* + * r8152_csum_workaround() + * The hw limites the value the transport offset. When the offset is out of the + * range, calculate the checksum by sw. + */ +static void r8152_csum_workaround(struct r8152 *tp, struct sk_buff *skb, +				  struct sk_buff_head *list) +{ +	if (skb_shinfo(skb)->gso_size) { +		netdev_features_t features = tp->netdev->features; +		struct sk_buff_head seg_list; +		struct sk_buff *segs, *nskb; + +		features &= ~(NETIF_F_SG | NETIF_F_IPV6_CSUM | NETIF_F_TSO6); +		segs = skb_gso_segment(skb, features); +		if (IS_ERR(segs) || !segs) +			goto drop; + +		__skb_queue_head_init(&seg_list); + +		do { +			nskb = segs; +			segs = segs->next; +			nskb->next = NULL; +			__skb_queue_tail(&seg_list, nskb); +		} while (segs); + +		skb_queue_splice(&seg_list, list); +		dev_kfree_skb(skb); +	} else if (skb->ip_summed == CHECKSUM_PARTIAL) { +		if (skb_checksum_help(skb) < 0) +			goto drop; + +		__skb_queue_head(list, skb); +	} else { +		struct net_device_stats *stats; + +drop: +		stats = &tp->netdev->stats; +		stats->tx_dropped++; +		dev_kfree_skb(skb); +	} +} + +/* + * msdn_giant_send_check() + * According to the document of microsoft, the TCP Pseudo Header excludes the + * packet length for IPv6 TCP large packets. + */ +static int msdn_giant_send_check(struct sk_buff *skb) +{ +	const struct ipv6hdr *ipv6h; +	struct tcphdr *th; +	int ret; + +	ret = skb_cow_head(skb, 0); +	if (ret) +		return ret; + +	ipv6h = ipv6_hdr(skb); +	th = tcp_hdr(skb); + +	th->check = 0; +	th->check = ~tcp_v6_check(0, &ipv6h->saddr, &ipv6h->daddr, 0); + +	return ret; +} + +static int r8152_tx_csum(struct r8152 *tp, struct tx_desc *desc, +			 struct sk_buff *skb, u32 len, u32 transport_offset) +{ +	u32 mss = skb_shinfo(skb)->gso_size; +	u32 opts1, opts2 = 0; +	int ret = TX_CSUM_SUCCESS; + +	WARN_ON_ONCE(len > TX_LEN_MAX); + +	opts1 = len | TX_FS | TX_LS; + +	if (mss) { +		if (transport_offset > GTTCPHO_MAX) { +			netif_warn(tp, tx_err, tp->netdev, +				   "Invalid transport offset 0x%x for TSO\n", +				   transport_offset); +			ret = TX_CSUM_TSO; +			goto unavailable; +		} + +		switch (get_protocol(skb)) { +		case htons(ETH_P_IP): +			opts1 |= GTSENDV4; +			break; + +		case htons(ETH_P_IPV6): +			if (msdn_giant_send_check(skb)) { +				ret = TX_CSUM_TSO; +				goto unavailable; +			} +			opts1 |= GTSENDV6; +			break; + +		default: +			WARN_ON_ONCE(1); +			break; +		} + +		opts1 |= transport_offset << GTTCPHO_SHIFT; +		opts2 |= min(mss, MSS_MAX) << MSS_SHIFT; +	} else if (skb->ip_summed == CHECKSUM_PARTIAL) { +		u8 ip_protocol; + +		if (transport_offset > TCPHO_MAX) { +			netif_warn(tp, tx_err, tp->netdev, +				   "Invalid transport offset 0x%x\n", +				   transport_offset); +			ret = TX_CSUM_NONE; +			goto unavailable; +		} + +		switch (get_protocol(skb)) { +		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; +		else if (ip_protocol == IPPROTO_UDP) +			opts2 |= UDP_CS; +		else +			WARN_ON_ONCE(1); + +		opts2 |= transport_offset << TCPHO_SHIFT; +	} + +	desc->opts2 = cpu_to_le32(opts2); +	desc->opts1 = cpu_to_le32(opts1); + +unavailable: +	return ret; +} + +static int r8152_tx_agg_fill(struct r8152 *tp, struct tx_agg *agg) +{ +	struct sk_buff_head skb_head, *tx_queue = &tp->tx_queue; +	int remain, ret; +	u8 *tx_data; + +	__skb_queue_head_init(&skb_head); +	spin_lock(&tx_queue->lock); +	skb_queue_splice_init(tx_queue, &skb_head); +	spin_unlock(&tx_queue->lock); + +	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; +		u32 offset; + +		skb = __skb_dequeue(&skb_head); +		if (!skb) +			break; + +		len = skb->len + sizeof(*tx_desc); + +		if (len > remain) { +			__skb_queue_head(&skb_head, skb); +			break; +		} + +		tx_data = tx_agg_align(tx_data); +		tx_desc = (struct tx_desc *)tx_data; + +		offset = (u32)skb_transport_offset(skb); + +		if (r8152_tx_csum(tp, tx_desc, skb, skb->len, offset)) { +			r8152_csum_workaround(tp, skb, &skb_head); +			continue; +		} + +		tx_data += sizeof(*tx_desc); + +		len = skb->len; +		if (skb_copy_bits(skb, 0, tx_data, len) < 0) { +			struct net_device_stats *stats = &tp->netdev->stats; + +			stats->tx_dropped++; +			dev_kfree_skb_any(skb); +			tx_data -= sizeof(*tx_desc); +			continue; +		} + +		tx_data += len; +		agg->skb_len += len; +		agg->skb_num++; + +		dev_kfree_skb_any(skb); + +		remain = rx_buf_sz - (int)(tx_agg_align(tx_data) - agg->head); +	} + +	if (!skb_queue_empty(&skb_head)) { +		spin_lock(&tx_queue->lock); +		skb_queue_splice(&skb_head, tx_queue); +		spin_unlock(&tx_queue->lock); +	} + +	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); + +	ret = usb_autopm_get_interface_async(tp->intf); +	if (ret < 0) +		goto out_tx_fill; + +	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); + +	ret = usb_submit_urb(agg->urb, GFP_ATOMIC); +	if (ret < 0) +		usb_autopm_put_interface_async(tp->intf); + +out_tx_fill: +	return ret; +} + +static u8 r8152_rx_csum(struct r8152 *tp, struct rx_desc *rx_desc) +{ +	u8 checksum = CHECKSUM_NONE; +	u32 opts2, opts3; + +	if (tp->version == RTL_VER_01) +		goto return_result; + +	opts2 = le32_to_cpu(rx_desc->opts2); +	opts3 = le32_to_cpu(rx_desc->opts3); + +	if (opts2 & RD_IPV4_CS) { +		if (opts3 & IPF) +			checksum = CHECKSUM_NONE; +		else if ((opts2 & RD_UDP_CS) && (opts3 & UDPF)) +			checksum = CHECKSUM_NONE; +		else if ((opts2 & RD_TCP_CS) && (opts3 & TCPF)) +			checksum = CHECKSUM_NONE; +		else +			checksum = CHECKSUM_UNNECESSARY; +	} else if (RD_IPV6_CS) { +		if ((opts2 & RD_UDP_CS) && !(opts3 & UDPF)) +			checksum = CHECKSUM_UNNECESSARY; +		else if ((opts2 & RD_TCP_CS) && !(opts3 & TCPF)) +			checksum = CHECKSUM_UNNECESSARY; +	} + +return_result: +	return checksum; +} + +static void rx_bottom(struct r8152 *tp) +{ +	unsigned long flags; +	struct list_head *cursor, *next, rx_queue; + +	if (list_empty(&tp->rx_done)) +		return; + +	INIT_LIST_HEAD(&rx_queue); +	spin_lock_irqsave(&tp->rx_lock, flags); +	list_splice_init(&tp->rx_done, &rx_queue); +	spin_unlock_irqrestore(&tp->rx_lock, flags); + +	list_for_each_safe(cursor, next, &rx_queue) { +		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); + +		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 = &netdev->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; + +			pkt_len -= CRC_SIZE; +			rx_data += sizeof(struct rx_desc); + +			skb = netdev_alloc_skb_ip_align(netdev, pkt_len); +			if (!skb) { +				stats->rx_dropped++; +				goto find_next_rx; +			} + +			skb->ip_summed = r8152_rx_csum(tp, rx_desc); +			memcpy(skb->data, rx_data, pkt_len); +			skb_put(skb, pkt_len); +			skb->protocol = eth_type_trans(skb, netdev); +			netif_receive_skb(skb); +			stats->rx_packets++; +			stats->rx_bytes += pkt_len; + +find_next_rx: +			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); +		if (ret && ret != -ENODEV) { +			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 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 *netdev = tp->netdev; + +			if (res == -ENODEV) { +				netif_device_detach(netdev); +			} else { +				struct net_device_stats *stats = &netdev->stats; +				unsigned long flags; + +				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 rtl_drop_queued_tx(struct r8152 *tp) +{ +	struct net_device_stats *stats = &tp->netdev->stats; +	struct sk_buff_head skb_head, *tx_queue = &tp->tx_queue; +	struct sk_buff *skb; + +	if (skb_queue_empty(tx_queue)) +		return; + +	__skb_queue_head_init(&skb_head); +	spin_lock_bh(&tx_queue->lock); +	skb_queue_splice_init(tx_queue, &skb_head); +	spin_unlock_bh(&tx_queue->lock); + +	while ((skb = __skb_dequeue(&skb_head))) { +		dev_kfree_skb(skb); +		stats->tx_dropped++; +	} +} + +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)) { +		if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) { +			set_bit(SCHEDULE_TASKLET, &tp->flags); +			schedule_delayed_work(&tp->schedule, 0); +		} else { +			usb_mark_last_busy(tp->udev); +			tasklet_schedule(&tp->tl); +		} +	} else if (skb_queue_len(&tp->tx_queue) > tp->tx_qlen) +		netif_stop_queue(netdev); + +	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 void rxdy_gated_en(struct r8152 *tp, bool enable) +{ +	u32 ocp_data; + +	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MISC_1); +	if (enable) +		ocp_data |= RXDY_GATED_EN; +	else +		ocp_data &= ~RXDY_GATED_EN; +	ocp_write_word(tp, MCU_TYPE_PLA, PLA_MISC_1, 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); + +	rxdy_gated_en(tp, false); + +	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) +{ +	if (test_bit(RTL8152_UNPLUG, &tp->flags)) +		return -ENODEV; + +	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) +{ +	if (test_bit(RTL8152_UNPLUG, &tp->flags)) +		return -ENODEV; + +	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) +{ +	u32 ocp_data; +	int i; + +	if (test_bit(RTL8152_UNPLUG, &tp->flags)) { +		rtl_drop_queued_tx(tp); +		return; +	} + +	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); + +	rtl_drop_queued_tx(tp); + +	for (i = 0; i < RTL8152_MAX_TX; i++) +		usb_kill_urb(tp->tx_info[i].urb); + +	rxdy_gated_en(tp, true); + +	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 r8152_power_cut_en(struct r8152 *tp, bool enable) +{ +	u32 ocp_data; + +	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_UPS_CTRL); +	if (enable) +		ocp_data |= POWER_CUT; +	else +		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); +} + +#define WAKE_ANY (WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_BCAST | WAKE_MCAST) + +static u32 __rtl_get_wol(struct r8152 *tp) +{ +	u32 ocp_data; +	u32 wolopts = 0; + +	ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CONFIG5); +	if (!(ocp_data & LAN_WAKE_EN)) +		return 0; + +	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG34); +	if (ocp_data & LINK_ON_WAKE_EN) +		wolopts |= WAKE_PHY; + +	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG5); +	if (ocp_data & UWF_EN) +		wolopts |= WAKE_UCAST; +	if (ocp_data & BWF_EN) +		wolopts |= WAKE_BCAST; +	if (ocp_data & MWF_EN) +		wolopts |= WAKE_MCAST; + +	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CFG_WOL); +	if (ocp_data & MAGIC_EN) +		wolopts |= WAKE_MAGIC; + +	return wolopts; +} + +static void __rtl_set_wol(struct r8152 *tp, u32 wolopts) +{ +	u32 ocp_data; + +	ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG); + +	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG34); +	ocp_data &= ~LINK_ON_WAKE_EN; +	if (wolopts & WAKE_PHY) +		ocp_data |= LINK_ON_WAKE_EN; +	ocp_write_word(tp, MCU_TYPE_PLA, PLA_CONFIG34, ocp_data); + +	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG5); +	ocp_data &= ~(UWF_EN | BWF_EN | MWF_EN | LAN_WAKE_EN); +	if (wolopts & WAKE_UCAST) +		ocp_data |= UWF_EN; +	if (wolopts & WAKE_BCAST) +		ocp_data |= BWF_EN; +	if (wolopts & WAKE_MCAST) +		ocp_data |= MWF_EN; +	if (wolopts & WAKE_ANY) +		ocp_data |= LAN_WAKE_EN; +	ocp_write_word(tp, MCU_TYPE_PLA, PLA_CONFIG5, ocp_data); + +	ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML); + +	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CFG_WOL); +	ocp_data &= ~MAGIC_EN; +	if (wolopts & WAKE_MAGIC) +		ocp_data |= MAGIC_EN; +	ocp_write_word(tp, MCU_TYPE_PLA, PLA_CFG_WOL, ocp_data); + +	if (wolopts & WAKE_ANY) +		device_set_wakeup_enable(&tp->udev->dev, true); +	else +		device_set_wakeup_enable(&tp->udev->dev, false); +} + +static void rtl_runtime_suspend_enable(struct r8152 *tp, bool enable) +{ +	if (enable) { +		u32 ocp_data; + +		__rtl_set_wol(tp, WAKE_ANY); + +		ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG); + +		ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG34); +		ocp_data |= LINK_OFF_WAKE_EN; +		ocp_write_word(tp, MCU_TYPE_PLA, PLA_CONFIG34, ocp_data); + +		ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML); +	} else { +		__rtl_set_wol(tp, tp->saved_wolopts); +	} +} + +static void rtl_phy_reset(struct r8152 *tp) +{ +	u16 data; +	int i; + +	clear_bit(PHY_RESET, &tp->flags); + +	data = r8152_mdio_read(tp, MII_BMCR); + +	/* don't reset again before the previous one complete */ +	if (data & BMCR_RESET) +		return; + +	data |= BMCR_RESET; +	r8152_mdio_write(tp, MII_BMCR, data); + +	for (i = 0; i < 50; i++) { +		msleep(20); +		if ((r8152_mdio_read(tp, MII_BMCR) & BMCR_RESET) == 0) +			break; +	} +} + +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 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 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 r8152b_hw_phy_cfg(struct r8152 *tp) +{ +	u16 data; + +	data = r8152_mdio_read(tp, MII_BMCR); +	if (data & BMCR_PDOWN) { +		data &= ~BMCR_PDOWN; +		r8152_mdio_write(tp, MII_BMCR, data); +	} + +	r8152b_disable_aldps(tp); + +	rtl_clear_bp(tp); + +	r8152b_enable_aldps(tp); +	set_bit(PHY_RESET, &tp->flags); +} + +static void r8152b_exit_oob(struct r8152 *tp) +{ +	u32 ocp_data; +	int i; + +	if (test_bit(RTL8152_UNPLUG, &tp->flags)) +		return; + +	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); + +	rxdy_gated_en(tp, true); +	r8153_teredo_off(tp); +	r8152b_hw_phy_cfg(tp); + +	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); + +	if (tp->udev->speed == USB_SPEED_FULL || +	    tp->udev->speed == USB_SPEED_LOW) { +		/* 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_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); + +	rxdy_gated_en(tp, false); + +	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_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); +	data = r8152_mdio_read(tp, MII_BMCR); +	if (data & BMCR_PDOWN) { +		data &= ~BMCR_PDOWN; +		r8152_mdio_write(tp, MII_BMCR, data); +	} + +	r8153_clear_bp(tp); + +	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); + +	set_bit(PHY_RESET, &tp->flags); +} + +static void r8153_u1u2en(struct r8152 *tp, bool 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, bool 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, bool 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_first_init(struct r8152 *tp) +{ +	u32 ocp_data; +	int i; + +	if (test_bit(RTL8152_UNPLUG, &tp->flags)) +		return; + +	rxdy_gated_en(tp, true); +	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_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); + +	rxdy_gated_en(tp, false); + +	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 (test_bit(PHY_RESET, &tp->flags)) +		bmcr |= BMCR_RESET; + +	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); + +	if (test_bit(PHY_RESET, &tp->flags)) { +		int i; + +		clear_bit(PHY_RESET, &tp->flags); +		for (i = 0; i < 50; i++) { +			msleep(20); +			if ((r8152_mdio_read(tp, MII_BMCR) & BMCR_RESET) == 0) +				break; +		} +	} + +out: + +	return ret; +} + +static void rtl8152_down(struct r8152 *tp) +{ +	if (test_bit(RTL8152_UNPLUG, &tp->flags)) { +		rtl_drop_queued_tx(tp); +		return; +	} + +	r8152_power_cut_en(tp, false); +	r8152b_disable_aldps(tp); +	r8152b_enter_oob(tp); +	r8152b_enable_aldps(tp); +} + +static void rtl8153_down(struct r8152 *tp) +{ +	if (test_bit(RTL8152_UNPLUG, &tp->flags)) { +		rtl_drop_queued_tx(tp); +		return; +	} + +	r8153_u1u2en(tp, false); +	r8153_power_cut_en(tp, false); +	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 (usb_autopm_get_interface(tp->intf) < 0) +		return; + +	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); + +	if (test_bit(SCHEDULE_TASKLET, &tp->flags) && +	    (tp->speed & LINK_STATUS)) { +		clear_bit(SCHEDULE_TASKLET, &tp->flags); +		tasklet_schedule(&tp->tl); +	} + +	if (test_bit(PHY_RESET, &tp->flags)) +		rtl_phy_reset(tp); + +out1: +	usb_autopm_put_interface(tp->intf); +} + +static int rtl8152_open(struct net_device *netdev) +{ +	struct r8152 *tp = netdev_priv(netdev); +	int res = 0; + +	res = alloc_all_mem(tp); +	if (res) +		goto out; + +	res = usb_autopm_get_interface(tp->intf); +	if (res < 0) { +		free_all_mem(tp); +		goto out; +	} + +	/* The WORK_ENABLE may be set when autoresume occurs */ +	if (test_bit(WORK_ENABLE, &tp->flags)) { +		clear_bit(WORK_ENABLE, &tp->flags); +		usb_kill_urb(tp->intr_urb); +		cancel_delayed_work_sync(&tp->schedule); +		if (tp->speed & LINK_STATUS) +			tp->rtl_ops.disable(tp); +	} + +	tp->rtl_ops.up(tp); + +	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); +		free_all_mem(tp); +	} + +	usb_autopm_put_interface(tp->intf); + +out: +	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); + +	res = usb_autopm_get_interface(tp->intf); +	if (res < 0) { +		rtl_drop_queued_tx(tp); +	} else { +		/* +		 * The autosuspend may have been enabled and wouldn't +		 * be disable when autoresume occurs, because the +		 * netif_running() would be false. +		 */ +		if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) { +			rtl_runtime_suspend_enable(tp, false); +			clear_bit(SELECTIVE_SUSPEND, &tp->flags); +		} + +		tasklet_disable(&tp->tl); +		tp->rtl_ops.down(tp); +		tasklet_enable(&tp->tl); +		usb_autopm_put_interface(tp->intf); +	} + +	free_all_mem(tp); + +	return res; +} + +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 rtl_tally_reset(struct r8152 *tp) +{ +	u32 ocp_data; + +	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_RSTTALLY); +	ocp_data |= TALLY_RESET; +	ocp_write_word(tp, MCU_TYPE_PLA, PLA_RSTTALLY, ocp_data); +} + +static void r8152b_init(struct r8152 *tp) +{ +	u32 ocp_data; + +	if (test_bit(RTL8152_UNPLUG, &tp->flags)) +		return; + +	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); +	} + +	r8152_power_cut_en(tp, false); + +	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); +	rtl_tally_reset(tp); + +	/* 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; + +	if (test_bit(RTL8152_UNPLUG, &tp->flags)) +		return; + +	r8153_u1u2en(tp, false); + +	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, false); + +	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); + +	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, false); +	r8153_u1u2en(tp, true); + +	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); +	rtl_tally_reset(tp); +} + +static int rtl8152_suspend(struct usb_interface *intf, pm_message_t message) +{ +	struct r8152 *tp = usb_get_intfdata(intf); + +	if (PMSG_IS_AUTO(message)) +		set_bit(SELECTIVE_SUSPEND, &tp->flags); +	else +		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); +		if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) { +			rtl_runtime_suspend_enable(tp, true); +		} else { +			tasklet_disable(&tp->tl); +			tp->rtl_ops.down(tp); +			tasklet_enable(&tp->tl); +		} +	} + +	return 0; +} + +static int rtl8152_resume(struct usb_interface *intf) +{ +	struct r8152 *tp = usb_get_intfdata(intf); + +	if (!test_bit(SELECTIVE_SUSPEND, &tp->flags)) { +		tp->rtl_ops.init(tp); +		netif_device_attach(tp->netdev); +	} + +	if (netif_running(tp->netdev)) { +		if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) { +			rtl_runtime_suspend_enable(tp, false); +			clear_bit(SELECTIVE_SUSPEND, &tp->flags); +			if (tp->speed & LINK_STATUS) +				tp->rtl_ops.disable(tp); +		} else { +			tp->rtl_ops.up(tp); +			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); +	} + +	return 0; +} + +static void rtl8152_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ +	struct r8152 *tp = netdev_priv(dev); + +	if (usb_autopm_get_interface(tp->intf) < 0) +		return; + +	wol->supported = WAKE_ANY; +	wol->wolopts = __rtl_get_wol(tp); + +	usb_autopm_put_interface(tp->intf); +} + +static int rtl8152_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ +	struct r8152 *tp = netdev_priv(dev); +	int ret; + +	ret = usb_autopm_get_interface(tp->intf); +	if (ret < 0) +		goto out_set_wol; + +	__rtl_set_wol(tp, wol->wolopts); +	tp->saved_wolopts = wol->wolopts & WAKE_ANY; + +	usb_autopm_put_interface(tp->intf); + +out_set_wol: +	return ret; +} + +static u32 rtl8152_get_msglevel(struct net_device *dev) +{ +	struct r8152 *tp = netdev_priv(dev); + +	return tp->msg_enable; +} + +static void rtl8152_set_msglevel(struct net_device *dev, u32 value) +{ +	struct r8152 *tp = netdev_priv(dev); + +	tp->msg_enable = value; +} + +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); +	int ret; + +	ret = usb_autopm_get_interface(tp->intf); +	if (ret < 0) +		goto out; + +	ret = rtl8152_set_speed(tp, cmd->autoneg, cmd->speed, cmd->duplex); + +	usb_autopm_put_interface(tp->intf); + +out: +	return ret; +} + +static const char rtl8152_gstrings[][ETH_GSTRING_LEN] = { +	"tx_packets", +	"rx_packets", +	"tx_errors", +	"rx_errors", +	"rx_missed", +	"align_errors", +	"tx_single_collisions", +	"tx_multi_collisions", +	"rx_unicast", +	"rx_broadcast", +	"rx_multicast", +	"tx_aborted", +	"tx_underrun", +}; + +static int rtl8152_get_sset_count(struct net_device *dev, int sset) +{ +	switch (sset) { +	case ETH_SS_STATS: +		return ARRAY_SIZE(rtl8152_gstrings); +	default: +		return -EOPNOTSUPP; +	} +} + +static void rtl8152_get_ethtool_stats(struct net_device *dev, +				      struct ethtool_stats *stats, u64 *data) +{ +	struct r8152 *tp = netdev_priv(dev); +	struct tally_counter tally; + +	if (usb_autopm_get_interface(tp->intf) < 0) +		return; + +	generic_ocp_read(tp, PLA_TALLYCNT, sizeof(tally), &tally, MCU_TYPE_PLA); + +	usb_autopm_put_interface(tp->intf); + +	data[0] = le64_to_cpu(tally.tx_packets); +	data[1] = le64_to_cpu(tally.rx_packets); +	data[2] = le64_to_cpu(tally.tx_errors); +	data[3] = le32_to_cpu(tally.rx_errors); +	data[4] = le16_to_cpu(tally.rx_missed); +	data[5] = le16_to_cpu(tally.align_errors); +	data[6] = le32_to_cpu(tally.tx_one_collision); +	data[7] = le32_to_cpu(tally.tx_multi_collision); +	data[8] = le64_to_cpu(tally.rx_unicast); +	data[9] = le64_to_cpu(tally.rx_broadcast); +	data[10] = le32_to_cpu(tally.rx_multicast); +	data[11] = le16_to_cpu(tally.tx_aborted); +	data[12] = le16_to_cpu(tally.tx_underun); +} + +static void rtl8152_get_strings(struct net_device *dev, u32 stringset, u8 *data) +{ +	switch (stringset) { +	case ETH_SS_STATS: +		memcpy(data, *rtl8152_gstrings, sizeof(rtl8152_gstrings)); +		break; +	} +} + +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, +	.get_msglevel = rtl8152_get_msglevel, +	.set_msglevel = rtl8152_set_msglevel, +	.get_wol = rtl8152_get_wol, +	.set_wol = rtl8152_set_wol, +	.get_strings = rtl8152_get_strings, +	.get_sset_count = rtl8152_get_sset_count, +	.get_ethtool_stats = rtl8152_get_ethtool_stats, +}; + +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; + +	if (test_bit(RTL8152_UNPLUG, &tp->flags)) +		return -ENODEV; + +	res = usb_autopm_get_interface(tp->intf); +	if (res < 0) +		goto out; + +	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; +	} + +	usb_autopm_put_interface(tp->intf); + +out: +	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) +{ +	if (test_bit(RTL8152_UNPLUG, &tp->flags)) +		return; + +	if (tp->version != RTL_VER_01) +		r8152_power_cut_en(tp, true); +} + +static void rtl8153_unload(struct r8152 *tp) +{ +	if (test_bit(RTL8152_UNPLUG, &tp->flags)) +		return; + +	r8153_power_cut_en(tp, true); +} + +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->up			= r8152b_exit_oob; +			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->up			= r8153_first_init; +			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->up			= r8153_first_init; +			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_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_SG | +			    NETIF_F_TSO | NETIF_F_FRAGLIST | NETIF_F_IPV6_CSUM | +			    NETIF_F_TSO6; +	netdev->hw_features = NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_SG | +			      NETIF_F_TSO | NETIF_F_FRAGLIST | +			      NETIF_F_IPV6_CSUM | NETIF_F_TSO6; + +	netdev->ethtool_ops = &ops; +	netif_set_gso_max_size(netdev, RTL_LIMITED_TSO_SIZE); + +	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; + +	intf->needs_remote_wakeup = 1; + +	r8152b_get_version(tp); +	tp->rtl_ops.init(tp); +	set_ethernet_addr(tp); + +	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; +	} + +	tp->saved_wolopts = __rtl_get_wol(tp); +	if (tp->saved_wolopts) +		device_set_wakeup_enable(&udev->dev, true); +	else +		device_set_wakeup_enable(&udev->dev, false); + +	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_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)}, +	{} +}; + +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, +	.supports_autosuspend = 1, +	.disable_hub_initiated_lpm = 1, +}; + +module_usb_driver(rtl8152_driver); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c index dd8a4adf48c..524a47a2812 100644 --- a/drivers/net/usb/rndis_host.c +++ b/drivers/net/usb/rndis_host.c @@ -13,11 +13,9 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  #include <linux/module.h> -#include <linux/init.h>  #include <linux/netdevice.h>  #include <linux/etherdevice.h>  #include <linux/ethtool.h> @@ -77,7 +75,9 @@ static void rndis_msg_indicate(struct usbnet *dev, struct rndis_indicate *msg,  	if (dev->driver_info->indication) {  		dev->driver_info->indication(dev, msg, buflen);  	} else { -		switch (msg->status) { +		u32 status = le32_to_cpu(msg->status); + +		switch (status) {  		case RNDIS_STATUS_MEDIA_CONNECT:  			dev_info(udev, "rndis media connect\n");  			break; @@ -85,8 +85,7 @@ static void rndis_msg_indicate(struct usbnet *dev, struct rndis_indicate *msg,  			dev_info(udev, "rndis media disconnect\n");  			break;  		default: -			dev_info(udev, "rndis indication: 0x%08x\n", -					le32_to_cpu(msg->status)); +			dev_info(udev, "rndis indication: 0x%08x\n", status);  		}  	}  } @@ -104,19 +103,22 @@ static void rndis_msg_indicate(struct usbnet *dev, struct rndis_indicate *msg,  int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf, int buflen)  {  	struct cdc_state	*info = (void *) &dev->data; +	struct usb_cdc_notification notification;  	int			master_ifnum;  	int			retval; +	int			partial;  	unsigned		count; -	__le32			rsp; -	u32			xid = 0, msg_len, request_id; +	u32			xid = 0, msg_len, request_id, msg_type, rsp, +				status;  	/* REVISIT when this gets called from contexts other than probe() or  	 * disconnect(): either serialize, or dispatch responses on xid  	 */ +	msg_type = le32_to_cpu(buf->msg_type); +  	/* Issue the request; xid is unique, don't bother byteswapping it */ -	if (likely(buf->msg_type != RNDIS_MSG_HALT && -		   buf->msg_type != RNDIS_MSG_RESET)) { +	if (likely(msg_type != RNDIS_MSG_HALT && msg_type != RNDIS_MSG_RESET)) {  		xid = dev->xid++;  		if (!xid)  			xid = dev->xid++; @@ -133,14 +135,21 @@ int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf, int buflen)  	if (unlikely(retval < 0 || xid == 0))  		return retval; -	// FIXME Seems like some devices discard responses when -	// we time out and cancel our "get response" requests... -	// so, this is fragile.  Probably need to poll for status. +	/* Some devices don't respond on the control channel until +	 * polled on the status channel, so do that first. */ +	if (dev->driver_info->data & RNDIS_DRIVER_DATA_POLL_STATUS) { +		retval = usb_interrupt_msg( +			dev->udev, +			usb_rcvintpipe(dev->udev, +				       dev->status->desc.bEndpointAddress), +			¬ification, sizeof(notification), &partial, +			RNDIS_CONTROL_TIMEOUT_MS); +		if (unlikely(retval < 0)) +			return retval; +	} -	/* ignore status endpoint, just poll the control channel; -	 * the request probably completed immediately -	 */ -	rsp = buf->msg_type | RNDIS_MSG_COMPLETION; +	/* Poll the control channel; the request probably completed immediately */ +	rsp = le32_to_cpu(buf->msg_type) | RNDIS_MSG_COMPLETION;  	for (count = 0; count < 10; count++) {  		memset(buf, 0, CONTROL_BUFFER_SIZE);  		retval = usb_control_msg(dev->udev, @@ -151,35 +160,36 @@ int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf, int buflen)  			buf, buflen,  			RNDIS_CONTROL_TIMEOUT_MS);  		if (likely(retval >= 8)) { +			msg_type = le32_to_cpu(buf->msg_type);  			msg_len = le32_to_cpu(buf->msg_len); +			status = le32_to_cpu(buf->status);  			request_id = (__force u32) buf->request_id; -			if (likely(buf->msg_type == rsp)) { +			if (likely(msg_type == rsp)) {  				if (likely(request_id == xid)) {  					if (unlikely(rsp == RNDIS_MSG_RESET_C))  						return 0; -					if (likely(RNDIS_STATUS_SUCCESS -							== buf->status)) +					if (likely(RNDIS_STATUS_SUCCESS == +							status))  						return 0;  					dev_dbg(&info->control->dev,  						"rndis reply status %08x\n", -						le32_to_cpu(buf->status)); +						status);  					return -EL3RST;  				}  				dev_dbg(&info->control->dev,  					"rndis reply id %d expected %d\n",  					request_id, xid);  				/* then likely retry */ -			} else switch (buf->msg_type) { -			case RNDIS_MSG_INDICATE:	/* fault/event */ +			} else switch (msg_type) { +			case RNDIS_MSG_INDICATE: /* fault/event */  				rndis_msg_indicate(dev, (void *)buf, buflen); -  				break; -			case RNDIS_MSG_KEEPALIVE: {	/* ping */ +			case RNDIS_MSG_KEEPALIVE: { /* ping */  				struct rndis_keepalive_c *msg = (void *)buf; -				msg->msg_type = RNDIS_MSG_KEEPALIVE_C; +				msg->msg_type = cpu_to_le32(RNDIS_MSG_KEEPALIVE_C);  				msg->msg_len = cpu_to_le32(sizeof *msg); -				msg->status = RNDIS_STATUS_SUCCESS; +				msg->status = cpu_to_le32(RNDIS_STATUS_SUCCESS);  				retval = usb_control_msg(dev->udev,  					usb_sndctrlpipe(dev->udev, 0),  					USB_CDC_SEND_ENCAPSULATED_COMMAND, @@ -227,7 +237,7 @@ EXPORT_SYMBOL_GPL(rndis_command);   * ActiveSync 4.1 Windows driver.   */  static int rndis_query(struct usbnet *dev, struct usb_interface *intf, -		void *buf, __le32 oid, u32 in_len, +		void *buf, u32 oid, u32 in_len,  		void **reply, int *reply_len)  {  	int retval; @@ -242,9 +252,9 @@ static int rndis_query(struct usbnet *dev, struct usb_interface *intf,  	u.buf = buf;  	memset(u.get, 0, sizeof *u.get + in_len); -	u.get->msg_type = RNDIS_MSG_QUERY; +	u.get->msg_type = cpu_to_le32(RNDIS_MSG_QUERY);  	u.get->msg_len = cpu_to_le32(sizeof *u.get + in_len); -	u.get->oid = oid; +	u.get->oid = cpu_to_le32(oid);  	u.get->len = cpu_to_le32(in_len);  	u.get->offset = cpu_to_le32(20); @@ -315,7 +325,7 @@ generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf, int flags)  	if (retval < 0)  		goto fail; -	u.init->msg_type = RNDIS_MSG_INIT; +	u.init->msg_type = cpu_to_le32(RNDIS_MSG_INIT);  	u.init->msg_len = cpu_to_le32(sizeof *u.init);  	u.init->major_version = cpu_to_le32(1);  	u.init->minor_version = cpu_to_le32(0); @@ -386,22 +396,23 @@ generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf, int flags)  	/* Check physical medium */  	phym = NULL;  	reply_len = sizeof *phym; -	retval = rndis_query(dev, intf, u.buf, OID_GEN_PHYSICAL_MEDIUM, -			0, (void **) &phym, &reply_len); +	retval = rndis_query(dev, intf, u.buf, +			     RNDIS_OID_GEN_PHYSICAL_MEDIUM, +			     0, (void **) &phym, &reply_len);  	if (retval != 0 || !phym) {  		/* OID is optional so don't fail here. */ -		phym_unspec = RNDIS_PHYSICAL_MEDIUM_UNSPECIFIED; +		phym_unspec = cpu_to_le32(RNDIS_PHYSICAL_MEDIUM_UNSPECIFIED);  		phym = &phym_unspec;  	}  	if ((flags & FLAG_RNDIS_PHYM_WIRELESS) && -			*phym != RNDIS_PHYSICAL_MEDIUM_WIRELESS_LAN) { +	    le32_to_cpup(phym) != RNDIS_PHYSICAL_MEDIUM_WIRELESS_LAN) {  		netif_dbg(dev, probe, dev->net,  			  "driver requires wireless physical medium, but device is not\n");  		retval = -ENODEV;  		goto halt_fail_and_release;  	}  	if ((flags & FLAG_RNDIS_PHYM_NOT_WIRELESS) && -			*phym == RNDIS_PHYSICAL_MEDIUM_WIRELESS_LAN) { +	    le32_to_cpup(phym) == RNDIS_PHYSICAL_MEDIUM_WIRELESS_LAN) {  		netif_dbg(dev, probe, dev->net,  			  "driver requires non-wireless physical medium, but device is wireless.\n");  		retval = -ENODEV; @@ -410,23 +421,23 @@ generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf, int flags)  	/* Get designated host ethernet address */  	reply_len = ETH_ALEN; -	retval = rndis_query(dev, intf, u.buf, OID_802_3_PERMANENT_ADDRESS, -			48, (void **) &bp, &reply_len); +	retval = rndis_query(dev, intf, u.buf, +			     RNDIS_OID_802_3_PERMANENT_ADDRESS, +			     48, (void **) &bp, &reply_len);  	if (unlikely(retval< 0)) {  		dev_err(&intf->dev, "rndis get ethaddr, %d\n", retval);  		goto halt_fail_and_release;  	}  	memcpy(net->dev_addr, bp, ETH_ALEN); -	memcpy(net->perm_addr, bp, ETH_ALEN);  	/* set a nonzero filter to enable data transfers */  	memset(u.set, 0, sizeof *u.set); -	u.set->msg_type = RNDIS_MSG_SET; +	u.set->msg_type = cpu_to_le32(RNDIS_MSG_SET);  	u.set->msg_len = cpu_to_le32(4 + sizeof *u.set); -	u.set->oid = OID_GEN_CURRENT_PACKET_FILTER; +	u.set->oid = cpu_to_le32(RNDIS_OID_GEN_CURRENT_PACKET_FILTER);  	u.set->len = cpu_to_le32(4);  	u.set->offset = cpu_to_le32((sizeof *u.set) - 8); -	*(__le32 *)(u.buf + sizeof *u.set) = RNDIS_DEFAULT_FILTER; +	*(__le32 *)(u.buf + sizeof *u.set) = cpu_to_le32(RNDIS_DEFAULT_FILTER);  	retval = rndis_command(dev, u.header, CONTROL_BUFFER_SIZE);  	if (unlikely(retval < 0)) { @@ -441,7 +452,7 @@ generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf, int flags)  halt_fail_and_release:  	memset(u.halt, 0, sizeof *u.halt); -	u.halt->msg_type = RNDIS_MSG_HALT; +	u.halt->msg_type = cpu_to_le32(RNDIS_MSG_HALT);  	u.halt->msg_len = cpu_to_le32(sizeof *u.halt);  	(void) rndis_command(dev, (void *)u.halt, CONTROL_BUFFER_SIZE);  fail_and_release: @@ -466,7 +477,7 @@ void rndis_unbind(struct usbnet *dev, struct usb_interface *intf)  	/* try to clear any rndis state/activity (no i/o from stack!) */  	halt = kzalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL);  	if (halt) { -		halt->msg_type = RNDIS_MSG_HALT; +		halt->msg_type = cpu_to_le32(RNDIS_MSG_HALT);  		halt->msg_len = cpu_to_le32(sizeof *halt);  		(void) rndis_command(dev, (void *)halt, CONTROL_BUFFER_SIZE);  		kfree(halt); @@ -481,20 +492,24 @@ EXPORT_SYMBOL_GPL(rndis_unbind);   */  int rndis_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; +  	/* peripheral may have batched packets to us... */  	while (likely(skb->len)) {  		struct rndis_data_hdr	*hdr = (void *)skb->data;  		struct sk_buff		*skb2; -		u32			msg_len, data_offset, data_len; +		u32			msg_type, msg_len, data_offset, data_len; +		msg_type = le32_to_cpu(hdr->msg_type);  		msg_len = le32_to_cpu(hdr->msg_len);  		data_offset = le32_to_cpu(hdr->data_offset);  		data_len = le32_to_cpu(hdr->data_len);  		/* don't choke if we see oob, per-packet data, etc */ -		if (unlikely(hdr->msg_type != RNDIS_MSG_PACKET || -			     skb->len < msg_len || -			     (data_offset + data_len + 8) > msg_len)) { +		if (unlikely(msg_type != RNDIS_MSG_PACKET || skb->len < msg_len +				|| (data_offset + data_len + 8) > msg_len)) {  			dev->net->stats.rx_frame_errors++;  			netdev_dbg(dev->net, "bad rndis message %d/%d/%d/%d, len %d\n",  				   le32_to_cpu(hdr->msg_type), @@ -560,7 +575,7 @@ rndis_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)  fill:  	hdr = (void *) __skb_push(skb, sizeof *hdr);  	memset(hdr, 0, sizeof *hdr); -	hdr->msg_type = RNDIS_MSG_PACKET; +	hdr->msg_type = cpu_to_le32(RNDIS_MSG_PACKET);  	hdr->msg_len = cpu_to_le32(skb->len);  	hdr->data_offset = cpu_to_le32(sizeof(*hdr) - 8);  	hdr->data_len = cpu_to_le32(len); @@ -573,7 +588,18 @@ EXPORT_SYMBOL_GPL(rndis_tx_fixup);  static const struct driver_info	rndis_info = {  	.description =	"RNDIS device", -	.flags =	FLAG_ETHER | FLAG_FRAMING_RN | FLAG_NO_SETINT, +	.flags =	FLAG_ETHER | FLAG_POINTTOPOINT | FLAG_FRAMING_RN | FLAG_NO_SETINT, +	.bind =		rndis_bind, +	.unbind =	rndis_unbind, +	.status =	rndis_status, +	.rx_fixup =	rndis_rx_fixup, +	.tx_fixup =	rndis_tx_fixup, +}; + +static const struct driver_info	rndis_poll_status_info = { +	.description =	"RNDIS device (poll status before control)", +	.flags =	FLAG_ETHER | FLAG_POINTTOPOINT | FLAG_FRAMING_RN | FLAG_NO_SETINT, +	.data =		RNDIS_DRIVER_DATA_POLL_STATUS,  	.bind =		rndis_bind,  	.unbind =	rndis_unbind,  	.status =	rndis_status, @@ -585,13 +611,18 @@ static const struct driver_info	rndis_info = {  static const struct usb_device_id	products [] = {  { +	/* 2Wire HomePortal 1000SW */ +	USB_DEVICE_AND_INTERFACE_INFO(0x1630, 0x0042, +				      USB_CLASS_COMM, 2 /* ACM */, 0x0ff), +	.driver_info = (unsigned long) &rndis_poll_status_info, +}, {  	/* RNDIS is MSFT's un-official variant of CDC ACM */  	USB_INTERFACE_INFO(USB_CLASS_COMM, 2 /* ACM */, 0x0ff),  	.driver_info = (unsigned long) &rndis_info,  }, {  	/* "ActiveSync" is an undocumented variant of RNDIS, used in WM5 */  	USB_INTERFACE_INFO(USB_CLASS_MISC, 1, 1), -	.driver_info = (unsigned long) &rndis_info, +	.driver_info = (unsigned long) &rndis_poll_status_info,  }, {  	/* RNDIS for tethering */  	USB_INTERFACE_INFO(USB_CLASS_WIRELESS_CONTROLLER, 1, 3), @@ -608,19 +639,10 @@ static struct usb_driver rndis_driver = {  	.disconnect =	usbnet_disconnect,  	.suspend =	usbnet_suspend,  	.resume =	usbnet_resume, +	.disable_hub_initiated_lpm = 1,  }; -static int __init rndis_init(void) -{ -	return usb_register(&rndis_driver); -} -module_init(rndis_init); - -static void __exit rndis_exit(void) -{ -	usb_deregister(&rndis_driver); -} -module_exit(rndis_exit); +module_usb_driver(rndis_driver);  MODULE_AUTHOR("David Brownell");  MODULE_DESCRIPTION("USB Host side RNDIS driver"); diff --git a/drivers/net/usb/rtl8150.c b/drivers/net/usb/rtl8150.c index e85c89c6706..6e87e571004 100644 --- a/drivers/net/usb/rtl8150.c +++ b/drivers/net/usb/rtl8150.c @@ -6,7 +6,6 @@   * version 2 as published by the Free Software Foundation.   */ -#include <linux/init.h>  #include <linux/signal.h>  #include <linux/slab.h>  #include <linux/module.h> @@ -85,32 +84,6 @@  #define INT_CRERR_CNT		0x06  #define INT_COL_CNT		0x07 -/* 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) @@ -156,39 +129,25 @@ struct rtl8150 {  	struct usb_device *udev;  	struct tasklet_struct tl;  	struct net_device *netdev; -	struct urb *rx_urb, *tx_urb, *intr_urb, *ctrl_urb; +	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; -	__le16 rx_creg;  	u8 *intr_buff;  	u8 phy;  };  typedef struct rtl8150 rtl8150_t; -static void fill_skb_pool(rtl8150_t *); -static void free_skb_pool(rtl8150_t *); -static inline struct sk_buff *pull_skb(rtl8150_t *); -static void rtl8150_disconnect(struct usb_interface *intf); -static int rtl8150_probe(struct usb_interface *intf, -			   const struct usb_device_id *id); -static int rtl8150_suspend(struct usb_interface *intf, pm_message_t message); -static int rtl8150_resume(struct usb_interface *intf); +struct async_req { +	struct usb_ctrlrequest dr; +	u16 rx_creg; +};  static const char driver_name [] = "rtl8150"; -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 -}; -  /*  **  **	device related part of the code @@ -208,50 +167,47 @@ static int set_registers(rtl8150_t * dev, u16 indx, u16 size, void *data)  			       indx, 0, data, size, 500);  } -static void ctrl_callback(struct urb *urb) +static void async_set_reg_cb(struct urb *urb)  { -	rtl8150_t *dev; +	struct async_req *req = (struct async_req *)urb->context;  	int status = urb->status; -	switch (status) { -	case 0: -		break; -	case -EINPROGRESS: -		break; -	case -ENOENT: -		break; -	default: -		if (printk_ratelimit()) -			dev_warn(&urb->dev->dev, "ctrl urb status %d\n", status); -	} -	dev = urb->context; -	clear_bit(RX_REG_SET, &dev->flags); +	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) +static int async_set_registers(rtl8150_t *dev, u16 indx, u16 size, u16 reg)  { -	int ret; - -	if (test_bit(RX_REG_SET, &dev->flags)) -		return -EAGAIN; +	int res = -ENOMEM; +	struct urb *async_urb; +	struct async_req *req; -	dev->dr.bRequestType = RTL8150_REQT_WRITE; -	dev->dr.bRequest = RTL8150_REQ_SET_REGS; -	dev->dr.wValue = cpu_to_le16(indx); -	dev->dr.wIndex = 0; -	dev->dr.wLength = cpu_to_le16(size); -	dev->ctrl_urb->transfer_buffer_length = size; -	usb_fill_control_urb(dev->ctrl_urb, dev->udev, -			 usb_sndctrlpipe(dev->udev, 0), (char *) &dev->dr, -			 &dev->rx_creg, size, ctrl_callback, dev); -	if ((ret = usb_submit_urb(dev->ctrl_urb, GFP_ATOMIC))) { -		if (ret == -ENODEV) +	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); -		err("control request submission failed: %d", ret); -	} else -		set_bit(RX_REG_SET, &dev->flags); - -	return ret; +		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) @@ -318,7 +274,7 @@ static int rtl8150_set_mac_address(struct net_device *netdev, void *p)  		return -EBUSY;  	memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); -	dbg("%s: Setting MAC address to %pM\n", netdev->name, netdev->dev_addr); +	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 @@ -333,7 +289,7 @@ static int rtl8150_set_mac_address(struct net_device *netdev, void *p)  	/* 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,  +		set_registers(dev, IDR_EEPROM + (i * 2), 2,  		netdev->dev_addr + (i * 2));  	}  	/* Clear the WEPROM bit (preventing accidental eeprom writes). */ @@ -373,13 +329,6 @@ static int alloc_all_urbs(rtl8150_t * dev)  		usb_free_urb(dev->tx_urb);  		return 0;  	} -	dev->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL); -	if (!dev->ctrl_urb) { -		usb_free_urb(dev->rx_urb); -		usb_free_urb(dev->tx_urb); -		usb_free_urb(dev->intr_urb); -		return 0; -	}  	return 1;  } @@ -389,7 +338,6 @@ 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); -	usb_free_urb(dev->ctrl_urb);  }  static void unlink_all_urbs(rtl8150_t * dev) @@ -397,7 +345,6 @@ 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); -	usb_kill_urb(dev->ctrl_urb);  }  static inline struct sk_buff *pull_skb(rtl8150_t *dev) @@ -490,44 +437,6 @@ resched:  	tasklet_schedule(&dev->tl);  } -static void rx_fixup(unsigned long data) -{ -	rtl8150_t *dev; -	struct sk_buff *skb; -	int status; - -	dev = (rtl8150_t *)data; - -	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 void write_bulk_callback(struct urb *urb)  {  	rtl8150_t *dev; @@ -584,12 +493,12 @@ static void intr_callback(struct urb *urb)  	if ((d[INT_MSR] & MSR_LINK) == 0) {  		if (netif_carrier_ok(dev->netdev)) {  			netif_carrier_off(dev->netdev); -			dbg("%s: LINK LOST\n", __func__); +			netdev_dbg(dev->netdev, "%s: LINK LOST\n", __func__);  		}  	} else {  		if (!netif_carrier_ok(dev->netdev)) {  			netif_carrier_on(dev->netdev); -			dbg("%s: LINK CAME BACK\n", __func__); +			netdev_dbg(dev->netdev, "%s: LINK CAME BACK\n", __func__);  		}  	} @@ -598,9 +507,9 @@ resubmit:  	if (res == -ENODEV)  		netif_device_detach(dev->netdev);  	else if (res) -		err ("can't resubmit intr, %s-%s/input0, status %d", -				dev->udev->bus->bus_name, -				dev->udev->devpath, 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) @@ -665,6 +574,42 @@ static void free_skb_pool(rtl8150_t *dev)  			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; @@ -674,7 +619,6 @@ static int enable_net_traffic(rtl8150_t * dev)  	}  	/* RCR bit7=1 attach Rx info at the end;  =0 HW CRC (which is broken) */  	rcr = 0x9e; -	dev->rx_creg = cpu_to_le16(rcr);  	tcr = 0xd8;  	cr = 0x0c;  	if (!(rcr & 0x80)) @@ -707,20 +651,22 @@ static void rtl8150_tx_timeout(struct net_device *netdev)  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) { -		dev->rx_creg |= cpu_to_le16(0x0001); +		rx_creg |= 0x0001;  		dev_info(&netdev->dev, "%s: promiscuous mode\n", netdev->name);  	} else if (!netdev_mc_empty(netdev) ||  		   (netdev->flags & IFF_ALLMULTI)) { -		dev->rx_creg &= cpu_to_le16(0xfffe); -		dev->rx_creg |= cpu_to_le16(0x0002); +		rx_creg &= 0xfffe; +		rx_creg |= 0x0002;  		dev_info(&netdev->dev, "%s: allmulti set\n", netdev->name);  	} else {  		/* ~RX_MULTICAST, ~RX_PROMISCUOUS */ -		dev->rx_creg &= cpu_to_le16(0x00fc); +		rx_creg &= 0x00fc;  	} -	async_set_registers(dev, RCR, 2); +	async_set_registers(dev, RCR, sizeof(rx_creg), rx_creg);  	netif_wake_queue(netdev);  } @@ -778,7 +724,7 @@ static int rtl8150_open(struct net_device *netdev)  		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))) { @@ -821,9 +767,9 @@ static void rtl8150_get_drvinfo(struct net_device *netdev, struct ethtool_drvinf  {  	rtl8150_t *dev = netdev_priv(netdev); -	strncpy(info->driver, driver_name, ETHTOOL_BUSINFO_LEN); -	strncpy(info->version, DRIVER_VERSION, ETHTOOL_BUSINFO_LEN); -	usb_make_path(dev->udev, info->bus_info, sizeof info->bus_info); +	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) @@ -843,10 +789,11 @@ static int rtl8150_get_settings(struct net_device *netdev, struct ethtool_cmd *e  	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; -		ecmd->speed = (lpa & (LPA_100HALF | LPA_100FULL)) ? -			     SPEED_100 : SPEED_10; -		if (ecmd->speed == SPEED_100) +		if (speed == SPEED_100)  			ecmd->duplex = (lpa & LPA_100FULL) ?  			    DUPLEX_FULL : DUPLEX_HALF;  		else @@ -854,8 +801,8 @@ static int rtl8150_get_settings(struct net_device *netdev, struct ethtool_cmd *e  			    DUPLEX_FULL : DUPLEX_HALF;  	} else {  		ecmd->autoneg = AUTONEG_DISABLE; -		ecmd->speed = (bmcr & BMCR_SPEED100) ? -		    SPEED_100 : SPEED_10; +		ethtool_cmd_speed_set(ecmd, ((bmcr & BMCR_SPEED100) ? +					     SPEED_100 : SPEED_10));  		ecmd->duplex = (bmcr & BMCR_FULLDPLX) ?  		    DUPLEX_FULL : DUPLEX_HALF;  	} @@ -897,8 +844,8 @@ static const struct net_device_ops rtl8150_netdev_ops = {  	.ndo_stop		= rtl8150_close,  	.ndo_do_ioctl		= rtl8150_ioctl,  	.ndo_start_xmit		= rtl8150_start_xmit, -	.ndo_tx_timeout 	= rtl8150_tx_timeout, -	.ndo_set_multicast_list = rtl8150_set_multicast, +	.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, @@ -913,10 +860,8 @@ static int rtl8150_probe(struct usb_interface *intf,  	struct net_device *netdev;  	netdev = alloc_etherdev(sizeof(rtl8150_t)); -	if (!netdev) { -		err("Out of memory"); +	if (!netdev)  		return -ENOMEM; -	}  	dev = netdev_priv(netdev); @@ -928,29 +873,29 @@ static int rtl8150_probe(struct usb_interface *intf,  	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; -	SET_ETHTOOL_OPS(netdev, &ops); +	netdev->ethtool_ops = &ops;  	dev->intr_interval = 100;	/* 100ms */  	if (!alloc_all_urbs(dev)) { -		err("out of memory"); +		dev_err(&intf->dev, "out of memory\n");  		goto out;  	}  	if (!rtl8150_reset(dev)) { -		err("couldn't reset the device"); +		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) { -		err("couldn't register the device"); +		dev_err(&intf->dev, "couldn't register the device\n");  		goto out2;  	} @@ -976,7 +921,6 @@ static void rtl8150_disconnect(struct usb_interface *intf)  	usb_set_intfdata(intf, NULL);  	if (dev) {  		set_bit(RTL8150_UNPLUG, &dev->flags); -		tasklet_disable(&dev->tl);  		tasklet_kill(&dev->tl);  		unregister_netdev(dev->netdev);  		unlink_all_urbs(dev); @@ -989,20 +933,17 @@ static void rtl8150_disconnect(struct usb_interface *intf)  	}  } -static int __init usb_rtl8150_init(void) -{ -	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" -	       DRIVER_DESC "\n"); -	return usb_register(&rtl8150_driver); -} - -static void __exit usb_rtl8150_exit(void) -{ -	usb_deregister(&rtl8150_driver); -} +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_init(usb_rtl8150_init); -module_exit(usb_rtl8150_exit); +module_usb_driver(rtl8150_driver);  MODULE_AUTHOR(DRIVER_AUTHOR);  MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/net/usb/sierra_net.c b/drivers/net/usb/sierra_net.c index d1ac15c95fa..a251588762e 100644 --- a/drivers/net/usb/sierra_net.c +++ b/drivers/net/usb/sierra_net.c @@ -21,8 +21,7 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  #define DRIVER_VERSION "v.2.0" @@ -68,16 +67,8 @@ static	atomic_t iface_counter = ATOMIC_INIT(0);   */  #define SIERRA_NET_USBCTL_BUF_LEN	1024 -/* list of interface numbers - used for constructing interface lists */ -struct sierra_net_iface_info { -	const u32 infolen;	/* number of interface numbers on list */ -	const u8  *ifaceinfo;	/* pointer to the array holding the numbers */ -}; - -struct sierra_net_info_data { -	u16 rx_urb_size; -	struct sierra_net_iface_info whitelist; -}; +/* Overriding the default usbnet rx_urb_size */ +#define SIERRA_NET_RX_URB_SIZE		(8 * 1024)  /* Private data structure */  struct sierra_net_data { @@ -319,10 +310,9 @@ static int sierra_net_send_cmd(struct usbnet *dev,  	struct sierra_net_data *priv = sierra_net_get_private(dev);  	int  status; -	status = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), -			USB_CDC_SEND_ENCAPSULATED_COMMAND, -			USB_DIR_OUT|USB_TYPE_CLASS|USB_RECIP_INTERFACE,	0, -			priv->ifnum, cmd, cmdlen, USB_CTRL_SET_TIMEOUT); +	status = usbnet_write_cmd(dev, USB_CDC_SEND_ENCAPSULATED_COMMAND, +				  USB_DIR_OUT|USB_TYPE_CLASS|USB_RECIP_INTERFACE, +				  0, priv->ifnum, cmd, cmdlen);  	if (status != cmdlen && status != -ENODEV)  		netdev_err(dev->net, "Submit %s failed %d\n", cmd_name, status); @@ -348,7 +338,7 @@ static void sierra_net_set_ctx_index(struct sierra_net_data *priv, u8 ctx_ix)  	dev_dbg(&(priv->usbnet->udev->dev), "%s %d", __func__, ctx_ix);  	priv->tx_hdr_template[0] = 0x3F;  	priv->tx_hdr_template[1] = ctx_ix; -	*((u16 *)&priv->tx_hdr_template[2]) = +	*((__be16 *)&priv->tx_hdr_template[2]) =  		cpu_to_be16(SIERRA_NET_HIP_EXT_IP_OUT_ID);  } @@ -422,11 +412,10 @@ static void sierra_net_handle_lsi(struct usbnet *dev, char *data,  	if (link_up) {  		sierra_net_set_ctx_index(priv, hh->msgspecific.byte);  		priv->link_up = 1; -		netif_carrier_on(dev->net);  	} else {  		priv->link_up = 0; -		netif_carrier_off(dev->net);  	} +	usbnet_link_change(dev, link_up, 0);  }  static void sierra_net_dosync(struct usbnet *dev) @@ -436,6 +425,13 @@ static void sierra_net_dosync(struct usbnet *dev)  	dev_dbg(&dev->udev->dev, "%s", __func__); +	/* The SIERRA_NET_HIP_MSYNC_ID command appears to request that the +	 * firmware restart itself.  After restarting, the modem will respond +	 * with the SIERRA_NET_HIP_RESTART_ID indication.  The driver continues +	 * sending MSYNC commands every few seconds until it receives the +	 * RESTART event from the firmware +	 */ +  	/* tell modem we are ready */  	status = sierra_net_send_sync(dev);  	if (status < 0) @@ -468,11 +464,9 @@ static void sierra_net_kevent(struct work_struct *work)  		/* Query the modem for the LSI message */  		buf = kzalloc(SIERRA_NET_USBCTL_BUF_LEN, GFP_KERNEL); -		if (!buf) { -			netdev_err(dev->net, -				"failed to allocate buf for LS msg\n"); +		if (!buf)  			return; -		} +  		ifnum = priv->ifnum;  		len = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),  				USB_CDC_GET_ENCAPSULATED_RESPONSE, @@ -567,7 +561,7 @@ static void sierra_net_defer_kevent(struct usbnet *dev, int work)  /*   * Sync Retransmit Timer Handler. On expiry, kick the work queue   */ -void sierra_sync_timer(unsigned long syncdata) +static void sierra_sync_timer(unsigned long syncdata)  {  	struct usbnet *dev = (struct usbnet *)syncdata; @@ -607,8 +601,8 @@ static void sierra_net_get_drvinfo(struct net_device *net,  {  	/* 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); +	strlcpy(info->driver, driver_name, sizeof(info->driver)); +	strlcpy(info->version, DRIVER_VERSION, sizeof(info->version));  }  static u32 sierra_net_get_link(struct net_device *net) @@ -618,7 +612,7 @@ static u32 sierra_net_get_link(struct net_device *net)  	return sierra_net_get_private(dev)->link_up && netif_running(net);  } -static struct ethtool_ops sierra_net_ethtool_ops = { +static const struct ethtool_ops sierra_net_ethtool_ops = {  	.get_drvinfo = sierra_net_get_drvinfo,  	.get_link = sierra_net_get_link,  	.get_msglevel = usbnet_get_msglevel, @@ -637,50 +631,25 @@ static int sierra_net_change_mtu(struct net_device *net, int new_mtu)  	return usbnet_change_mtu(net, new_mtu);  } -static int is_whitelisted(const u8 ifnum, -			const struct sierra_net_iface_info *whitelist) -{ -	if (whitelist) { -		const u8 *list = whitelist->ifaceinfo; -		int i; - -		for (i = 0; i < whitelist->infolen; i++) { -			if (list[i] == ifnum) -				return 1; -		} -	} -	return 0; -} -  static int sierra_net_get_fw_attr(struct usbnet *dev, u16 *datap)  {  	int result = 0; -	u16 *attrdata; - -	attrdata = kmalloc(sizeof(*attrdata), GFP_KERNEL); -	if (!attrdata) -		return -ENOMEM; - -	result = usb_control_msg( -			dev->udev, -			usb_rcvctrlpipe(dev->udev, 0), -			/* _u8 vendor specific request */ -			SWI_USB_REQUEST_GET_FW_ATTR, -			USB_DIR_IN | USB_TYPE_VENDOR,	/* __u8 request type */ -			0x0000,		/* __u16 value not used */ -			0x0000,		/* __u16 index  not used */ -			attrdata,	/* char *data */ -			sizeof(*attrdata),		/* __u16 size */ -			USB_CTRL_SET_TIMEOUT);	/* int timeout */ - -	if (result < 0) { -		kfree(attrdata); +	__le16 attrdata; + +	result = usbnet_read_cmd(dev, +				/* _u8 vendor specific request */ +				SWI_USB_REQUEST_GET_FW_ATTR, +				USB_DIR_IN | USB_TYPE_VENDOR,	/* __u8 request type */ +				0x0000,		/* __u16 value not used */ +				0x0000,		/* __u16 index  not used */ +				&attrdata,	/* char *data */ +				sizeof(attrdata)	/* __u16 size */ +				); + +	if (result < 0)  		return -EIO; -	} - -	*datap = *attrdata; -	kfree(attrdata); +	*datap = le16_to_cpu(attrdata);  	return result;  } @@ -700,17 +669,9 @@ static int sierra_net_bind(struct usbnet *dev, struct usb_interface *intf)  	static const u8 shdwn_tmplate[sizeof(priv->shdwn_msg)] = {  		0x00, 0x00, SIERRA_NET_HIP_SHUTD_ID, 0x00}; -	struct sierra_net_info_data *data = -			(struct sierra_net_info_data *)dev->driver_info->data; -  	dev_dbg(&dev->udev->dev, "%s", __func__);  	ifacenum = intf->cur_altsetting->desc.bInterfaceNumber; -	/* We only accept certain interfaces */ -	if (!is_whitelisted(ifacenum, &data->whitelist)) { -		dev_dbg(&dev->udev->dev, "Ignoring interface: %d", ifacenum); -		return -ENODEV; -	}  	numendpoints = intf->cur_altsetting->desc.bNumEndpoints;  	/* We have three endpoints, bulk in and out, and a status */  	if (numendpoints != 3) { @@ -728,10 +689,8 @@ static int sierra_net_bind(struct usbnet *dev, struct usb_interface *intf)  	}  	/* Initialize sierra private data */  	priv = kzalloc(sizeof *priv, GFP_KERNEL); -	if (!priv) { -		dev_err(&dev->udev->dev, "No memory"); +	if (!priv)  		return -ENOMEM; -	}  	priv->usbnet = dev;  	priv->ifnum = ifacenum; @@ -751,10 +710,13 @@ static int sierra_net_bind(struct usbnet *dev, struct usb_interface *intf)  	/* set context index initially to 0 - prepares tx hdr template */  	sierra_net_set_ctx_index(priv, 0); +	/* prepare sync message template */ +	memcpy(priv->sync_msg, sync_tmplate, sizeof(priv->sync_msg)); +  	/* decrease the rx_urb_size and max_tx_size to 4k on USB 1.1 */ -	dev->rx_urb_size  = data->rx_urb_size; +	dev->rx_urb_size  = SIERRA_NET_RX_URB_SIZE;  	if (dev->udev->speed != USB_SPEED_HIGH) -		dev->rx_urb_size  = min_t(size_t, 4096, data->rx_urb_size); +		dev->rx_urb_size  = min_t(size_t, 4096, SIERRA_NET_RX_URB_SIZE);  	dev->net->hard_header_len += SIERRA_NET_HIP_EXT_HDR_LEN;  	dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; @@ -786,11 +748,6 @@ static int sierra_net_bind(struct usbnet *dev, struct usb_interface *intf)  		kfree(priv);  		return -ENODEV;  	} -	/* prepare sync message from template */ -	memcpy(priv->sync_msg, sync_tmplate, sizeof(priv->sync_msg)); - -	/* initiate the sync sequence */ -	sierra_net_dosync(dev);  	return 0;  } @@ -802,10 +759,9 @@ static void sierra_net_unbind(struct usbnet *dev, struct usb_interface *intf)  	dev_dbg(&dev->udev->dev, "%s", __func__); -	/* Kill the timer then flush the work queue */ +	/* kill the timer and work */  	del_timer_sync(&priv->sync_timer); - -	flush_scheduled_work(); +	cancel_work_sync(&priv->sierra_net_kevent);  	/* tell modem we are going away */  	status = sierra_net_send_cmd(dev, priv->shdwn_msg, @@ -814,8 +770,9 @@ static void sierra_net_unbind(struct usbnet *dev, struct usb_interface *intf)  		netdev_err(dev->net,  			"usb_control_msg failed, status %d\n", status); -	sierra_net_set_private(dev, NULL); +	usbnet_status_stop(dev); +	sierra_net_set_private(dev, NULL);  	kfree(priv);  } @@ -870,7 +827,7 @@ static int sierra_net_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  				netdev_err(dev->net, "HIP/ETH: Invalid pkt\n");  			dev->net->stats.rx_frame_errors++; -			/* dev->net->stats.rx_errors incremented by caller */; +			/* dev->net->stats.rx_errors incremented by caller */  			return 0;  		} @@ -894,13 +851,16 @@ static int sierra_net_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  }  /* ---------------------------- Transmit data path ----------------------*/ -struct sk_buff *sierra_net_tx_fixup(struct usbnet *dev, struct sk_buff *skb, -		gfp_t flags) +static struct sk_buff *sierra_net_tx_fixup(struct usbnet *dev, +					   struct sk_buff *skb, gfp_t flags)  {  	struct sierra_net_data *priv = sierra_net_get_private(dev);  	u16 len;  	bool need_tail; +	BUILD_BUG_ON(FIELD_SIZEOF(struct usbnet, data) +				< sizeof(struct cdc_state)); +  	dev_dbg(&dev->udev->dev, "%s", __func__);  	if (priv->link_up && check_ethip_packet(skb, dev) && is_ip(skb)) {  		/* enough head room as is? */ @@ -943,16 +903,7 @@ struct sk_buff *sierra_net_tx_fixup(struct usbnet *dev, struct sk_buff *skb,  	return NULL;  } -static const u8 sierra_net_ifnum_list[] = { 7, 10, 11 }; -static const struct sierra_net_info_data sierra_net_info_data_68A3 = { -	.rx_urb_size = 8 * 1024, -	.whitelist = { -		.infolen = ARRAY_SIZE(sierra_net_ifnum_list), -		.ifaceinfo = sierra_net_ifnum_list -	} -}; - -static const struct driver_info sierra_net_info_68A3 = { +static const struct driver_info sierra_net_info_direct_ip = {  	.description = "Sierra Wireless USB-to-WWAN Modem",  	.flags = FLAG_WWAN | FLAG_SEND_ZLP,  	.bind = sierra_net_bind, @@ -960,12 +911,39 @@ static const struct driver_info sierra_net_info_68A3 = {  	.status = sierra_net_status,  	.rx_fixup = sierra_net_rx_fixup,  	.tx_fixup = sierra_net_tx_fixup, -	.data = (unsigned long)&sierra_net_info_data_68A3,  }; +static int +sierra_net_probe(struct usb_interface *udev, const struct usb_device_id *prod) +{ +	int ret; + +	ret = usbnet_probe(udev, prod); +	if (ret == 0) { +		struct usbnet *dev = usb_get_intfdata(udev); + +		ret = usbnet_status_start(dev, GFP_KERNEL); +		if (ret == 0) { +			/* Interrupt URB now set up; initiate sync sequence */ +			sierra_net_dosync(dev); +		} +	} +	return ret; +} + +#define DIRECT_IP_DEVICE(vend, prod) \ +	{USB_DEVICE_INTERFACE_NUMBER(vend, prod, 7), \ +	.driver_info = (unsigned long)&sierra_net_info_direct_ip}, \ +	{USB_DEVICE_INTERFACE_NUMBER(vend, prod, 10), \ +	.driver_info = (unsigned long)&sierra_net_info_direct_ip}, \ +	{USB_DEVICE_INTERFACE_NUMBER(vend, prod, 11), \ +	.driver_info = (unsigned long)&sierra_net_info_direct_ip} +  static const struct usb_device_id products[] = { -	{USB_DEVICE(0x1199, 0x68A3), /* Sierra Wireless USB-to-WWAN modem */ -	.driver_info = (unsigned long) &sierra_net_info_68A3}, +	DIRECT_IP_DEVICE(0x1199, 0x68A3), /* Sierra Wireless USB-to-WWAN modem */ +	DIRECT_IP_DEVICE(0x0F3D, 0x68A3), /* AT&T Direct IP modem */ +	DIRECT_IP_DEVICE(0x1199, 0x68AA), /* Sierra Wireless Direct IP LTE modem */ +	DIRECT_IP_DEVICE(0x0F3D, 0x68AA), /* AT&T Direct IP LTE modem */  	{}, /* last item */  }; @@ -975,28 +953,15 @@ MODULE_DEVICE_TABLE(usb, products);  static struct usb_driver sierra_net_driver = {  	.name = "sierra_net",  	.id_table = products, -	.probe = usbnet_probe, +	.probe = sierra_net_probe,  	.disconnect = usbnet_disconnect,  	.suspend = usbnet_suspend,  	.resume = usbnet_resume,  	.no_dynamic_id = 1, +	.disable_hub_initiated_lpm = 1,  }; -static int __init sierra_net_init(void) -{ -	BUILD_BUG_ON(FIELD_SIZEOF(struct usbnet, data) -				< sizeof(struct cdc_state)); - -	return usb_register(&sierra_net_driver); -} - -static void __exit sierra_net_exit(void) -{ -	usb_deregister(&sierra_net_driver); -} - -module_exit(sierra_net_exit); -module_init(sierra_net_init); +module_usb_driver(sierra_net_driver);  MODULE_AUTHOR(DRIVER_AUTHOR);  MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c index 753ee6eb7ed..d9e7892262f 100644 --- a/drivers/net/usb/smsc75xx.c +++ b/drivers/net/usb/smsc75xx.c @@ -13,19 +13,19 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. + * along with this program; if not, see <http://www.gnu.org/licenses/>.   *   *****************************************************************************/  #include <linux/module.h>  #include <linux/kmod.h> -#include <linux/init.h>  #include <linux/netdevice.h>  #include <linux/etherdevice.h>  #include <linux/ethtool.h>  #include <linux/mii.h>  #include <linux/usb.h> +#include <linux/bitrev.h> +#include <linux/crc16.h>  #include <linux/crc32.h>  #include <linux/usb/usbnet.h>  #include <linux/slab.h> @@ -43,7 +43,6 @@  #define EEPROM_MAC_OFFSET		(0x01)  #define DEFAULT_TX_CSUM_ENABLE		(true)  #define DEFAULT_RX_CSUM_ENABLE		(true) -#define DEFAULT_TSO_ENABLE		(true)  #define SMSC75XX_INTERNAL_PHY_ID	(1)  #define SMSC75XX_TX_OVERHEAD		(8)  #define MAX_RX_FIFO_SIZE		(20 * 1024) @@ -51,24 +50,26 @@  #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 check_warn(ret, fmt, args...) \ -	({ if (ret < 0) netdev_warn(dev->net, fmt, ##args); }) - -#define check_warn_return(ret, fmt, args...) \ -	({ if (ret < 0) { netdev_warn(dev->net, fmt, ##args); return ret; } }) - -#define check_warn_goto_done(ret, fmt, args...) \ -	({ if (ret < 0) { netdev_warn(dev->net, fmt, ##args); goto done; } }) +#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]; -	bool use_rx_csum;  	struct mutex dataport_mutex;  	spinlock_t rfe_ctl_lock;  	struct work_struct set_multicast; +	u8 suspend_flags;  };  struct usb_context { @@ -76,76 +77,103 @@ struct usb_context {  	struct usbnet *dev;  }; -static int turbo_mode = true; +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) +static int __must_check __smsc75xx_read_reg(struct usbnet *dev, u32 index, +					    u32 *data, int in_pm)  { -	u32 *buf = kmalloc(4, GFP_KERNEL); +	u32 buf;  	int ret; +	int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16);  	BUG_ON(!dev); -	if (!buf) -		return -ENOMEM; - -	ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), -		USB_VENDOR_REQUEST_READ_REGISTER, -		USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, -		00, index, buf, 4, USB_CTRL_GET_TIMEOUT); +	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 register index 0x%08x", index); +		netdev_warn(dev->net, "Failed to read reg index 0x%08x: %d\n", +			    index, ret); -	le32_to_cpus(buf); -	*data = *buf; -	kfree(buf); +	le32_to_cpus(&buf); +	*data = buf;  	return ret;  } -static int __must_check smsc75xx_write_reg(struct usbnet *dev, u32 index, -					   u32 data) +static int __must_check __smsc75xx_write_reg(struct usbnet *dev, u32 index, +					     u32 data, int in_pm)  { -	u32 *buf = kmalloc(4, GFP_KERNEL); +	u32 buf;  	int ret; +	int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16);  	BUG_ON(!dev); -	if (!buf) -		return -ENOMEM; - -	*buf = data; -	cpu_to_le32s(buf); +	if (!in_pm) +		fn = usbnet_write_cmd; +	else +		fn = usbnet_write_cmd_nopm; -	ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), -		USB_VENDOR_REQUEST_WRITE_REGISTER, -		USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, -		00, index, buf, 4, USB_CTRL_SET_TIMEOUT); +	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 register index 0x%08x", index); - -	kfree(buf); +		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 int smsc75xx_phy_wait_not_busy(struct usbnet *dev) +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); -		check_warn_return(ret, "Error reading MII_ACCESS"); +		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; @@ -154,7 +182,8 @@ static int smsc75xx_phy_wait_not_busy(struct usbnet *dev)  	return -EIO;  } -static int smsc75xx_mdio_read(struct net_device *netdev, int phy_id, int idx) +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; @@ -163,23 +192,35 @@ static int smsc75xx_mdio_read(struct net_device *netdev, int phy_id, int idx)  	mutex_lock(&dev->phy_mutex);  	/* confirm MII not busy */ -	ret = smsc75xx_phy_wait_not_busy(dev); -	check_warn_goto_done(ret, "MII is busy in smsc75xx_mdio_read"); +	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; -	ret = smsc75xx_write_reg(dev, MII_ACCESS, addr); -	check_warn_goto_done(ret, "Error writing MII_ACCESS"); +		| 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); -	check_warn_goto_done(ret, "Timed out reading MII reg %02X", idx); +	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); -	check_warn_goto_done(ret, "Error reading MII_DATA"); +	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); @@ -188,8 +229,8 @@ done:  	return ret;  } -static void smsc75xx_mdio_write(struct net_device *netdev, int phy_id, int idx, -				int regval) +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; @@ -198,29 +239,64 @@ static void smsc75xx_mdio_write(struct net_device *netdev, int phy_id, int idx,  	mutex_lock(&dev->phy_mutex);  	/* confirm MII not busy */ -	ret = smsc75xx_phy_wait_not_busy(dev); -	check_warn_goto_done(ret, "MII is busy in smsc75xx_mdio_write"); +	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); -	check_warn_goto_done(ret, "Error writing MII_DATA"); +	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; -	ret = smsc75xx_write_reg(dev, MII_ACCESS, addr); -	check_warn_goto_done(ret, "Error writing MII_ACCESS"); +		| 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); -	check_warn_goto_done(ret, "Timed out writing MII reg %02X", idx); +	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; @@ -229,7 +305,10 @@ static int smsc75xx_wait_eeprom(struct usbnet *dev)  	do {  		ret = smsc75xx_read_reg(dev, E2P_CMD, &val); -		check_warn_return(ret, "Error reading E2P_CMD"); +		if (ret < 0) { +			netdev_warn(dev->net, "Error reading E2P_CMD\n"); +			return ret; +		}  		if (!(val & E2P_CMD_BUSY) || (val & E2P_CMD_TIMEOUT))  			break; @@ -237,7 +316,7 @@ static int smsc75xx_wait_eeprom(struct usbnet *dev)  	} while (!time_after(jiffies, start_time + HZ));  	if (val & (E2P_CMD_TIMEOUT | E2P_CMD_BUSY)) { -		netdev_warn(dev->net, "EEPROM read operation timeout"); +		netdev_warn(dev->net, "EEPROM read operation timeout\n");  		return -EIO;  	} @@ -252,7 +331,10 @@ static int smsc75xx_eeprom_confirm_not_busy(struct usbnet *dev)  	do {  		ret = smsc75xx_read_reg(dev, E2P_CMD, &val); -		check_warn_return(ret, "Error reading E2P_CMD"); +		if (ret < 0) { +			netdev_warn(dev->net, "Error reading E2P_CMD\n"); +			return ret; +		}  		if (!(val & E2P_CMD_BUSY))  			return 0; @@ -260,7 +342,7 @@ static int smsc75xx_eeprom_confirm_not_busy(struct usbnet *dev)  		udelay(40);  	} while (!time_after(jiffies, start_time + HZ)); -	netdev_warn(dev->net, "EEPROM is busy"); +	netdev_warn(dev->net, "EEPROM is busy\n");  	return -EIO;  } @@ -280,14 +362,20 @@ static int smsc75xx_read_eeprom(struct usbnet *dev, u32 offset, u32 length,  	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); -		check_warn_return(ret, "Error writing E2P_CMD"); +		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); -		check_warn_return(ret, "Error reading E2P_DATA"); +		if (ret < 0) { +			netdev_warn(dev->net, "Error reading E2P_DATA\n"); +			return ret; +		}  		data[i] = val & 0xFF;  		offset++; @@ -312,7 +400,10 @@ static int smsc75xx_write_eeprom(struct usbnet *dev, u32 offset, u32 length,  	/* Issue write/erase enable command */  	val = E2P_CMD_BUSY | E2P_CMD_EWEN;  	ret = smsc75xx_write_reg(dev, E2P_CMD, val); -	check_warn_return(ret, "Error writing E2P_CMD"); +	if (ret < 0) { +		netdev_warn(dev->net, "Error writing E2P_CMD\n"); +		return ret; +	}  	ret = smsc75xx_wait_eeprom(dev);  	if (ret < 0) @@ -323,12 +414,18 @@ static int smsc75xx_write_eeprom(struct usbnet *dev, u32 offset, u32 length,  		/* Fill data register */  		val = data[i];  		ret = smsc75xx_write_reg(dev, E2P_DATA, val); -		check_warn_return(ret, "Error writing E2P_DATA"); +		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); -		check_warn_return(ret, "Error writing E2P_CMD"); +		if (ret < 0) { +			netdev_warn(dev->net, "Error writing E2P_CMD\n"); +			return ret; +		}  		ret = smsc75xx_wait_eeprom(dev);  		if (ret < 0) @@ -347,7 +444,10 @@ static int smsc75xx_dataport_wait_not_busy(struct usbnet *dev)  	for (i = 0; i < 100; i++) {  		u32 dp_sel;  		ret = smsc75xx_read_reg(dev, DP_SEL, &dp_sel); -		check_warn_return(ret, "Error reading DP_SEL"); +		if (ret < 0) { +			netdev_warn(dev->net, "Error reading DP_SEL\n"); +			return ret; +		}  		if (dp_sel & DP_SEL_DPRDY)  			return 0; @@ -355,7 +455,7 @@ static int smsc75xx_dataport_wait_not_busy(struct usbnet *dev)  		udelay(40);  	} -	netdev_warn(dev->net, "smsc75xx_dataport_wait_not_busy timed out"); +	netdev_warn(dev->net, "smsc75xx_dataport_wait_not_busy timed out\n");  	return -EIO;  } @@ -370,28 +470,49 @@ static int smsc75xx_dataport_write(struct usbnet *dev, u32 ram_select, u32 addr,  	mutex_lock(&pdata->dataport_mutex);  	ret = smsc75xx_dataport_wait_not_busy(dev); -	check_warn_goto_done(ret, "smsc75xx_dataport_write busy on entry"); +	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); -	check_warn_goto_done(ret, "Error reading 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); -	check_warn_goto_done(ret, "Error writing 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); -		check_warn_goto_done(ret, "Error writing DP_ADDR"); +		if (ret < 0) { +			netdev_warn(dev->net, "Error writing DP_ADDR\n"); +			goto done; +		}  		ret = smsc75xx_write_reg(dev, DP_DATA, buf[i]); -		check_warn_goto_done(ret, "Error writing DP_DATA"); +		if (ret < 0) { +			netdev_warn(dev->net, "Error writing DP_DATA\n"); +			goto done; +		}  		ret = smsc75xx_write_reg(dev, DP_CMD, DP_CMD_WRITE); -		check_warn_goto_done(ret, "Error writing DP_CMD"); +		if (ret < 0) { +			netdev_warn(dev->net, "Error writing DP_CMD\n"); +			goto done; +		}  		ret = smsc75xx_dataport_wait_not_busy(dev); -		check_warn_goto_done(ret, "smsc75xx_dataport_write timeout"); +		if (ret < 0) { +			netdev_warn(dev->net, "smsc75xx_dataport_write timeout\n"); +			goto done; +		}  	}  done: @@ -412,14 +533,15 @@ static void smsc75xx_deferred_multicast_write(struct work_struct *param)  	struct usbnet *dev = pdata->dev;  	int ret; -	netif_dbg(dev, drv, dev->net, "deferred multicast write 0x%08x", -		pdata->rfe_ctl); +	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); -	check_warn(ret, "Error writing RFE_CRL"); +	if (ret < 0) +		netdev_warn(dev->net, "Error writing RFE_CRL\n");  }  static void smsc75xx_set_multicast(struct net_device *netdev) @@ -439,15 +561,15 @@ static void smsc75xx_set_multicast(struct net_device *netdev)  		pdata->multicast_hash_table[i] = 0;  	if (dev->net->flags & IFF_PROMISC) { -		netif_dbg(dev, drv, dev->net, "promiscuous mode enabled"); +		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"); +		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"); +		netif_dbg(dev, drv, dev->net, "receive multicast hash filter\n");  		pdata->rfe_ctl |= RFE_CTL_MHF | RFE_CTL_DPF; @@ -457,7 +579,7 @@ static void smsc75xx_set_multicast(struct net_device *netdev)  				(1 << (bitnum % 32));  		}  	} else { -		netif_dbg(dev, drv, dev->net, "receive own packets only"); +		netif_dbg(dev, drv, dev->net, "receive own packets only\n");  		pdata->rfe_ctl |= RFE_CTL_DPF;  	} @@ -485,18 +607,24 @@ static int smsc75xx_update_flowcontrol(struct usbnet *dev, u8 duplex,  		if (cap & FLOW_CTRL_RX)  			flow |= FLOW_RX_FCEN; -		netif_dbg(dev, link, dev->net, "rx pause %s, tx pause %s", -			(cap & FLOW_CTRL_RX ? "enabled" : "disabled"), -			(cap & FLOW_CTRL_TX ? "enabled" : "disabled")); +		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"); +		netif_dbg(dev, link, dev->net, "half duplex\n");  	}  	ret = smsc75xx_write_reg(dev, FLOW, flow); -	check_warn_return(ret, "Error writing FLOW"); +	if (ret < 0) { +		netdev_warn(dev->net, "Error writing FLOW\n"); +		return ret; +	}  	ret = smsc75xx_write_reg(dev, FCT_FLOW, fct_flow); -	check_warn_return(ret, "Error writing FCT_FLOW"); +	if (ret < 0) { +		netdev_warn(dev->net, "Error writing FCT_FLOW\n"); +		return ret; +	}  	return 0;  } @@ -504,24 +632,27 @@ static int smsc75xx_update_flowcontrol(struct usbnet *dev, u8 duplex,  static int smsc75xx_link_reset(struct usbnet *dev)  {  	struct mii_if_info *mii = &dev->mii; -	struct ethtool_cmd ecmd; +	struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET };  	u16 lcladv, rmtadv;  	int ret; -	/* clear interrupt status */ -	ret = smsc75xx_mdio_read(dev->net, mii->phy_id, PHY_INT_SRC); -	check_warn_return(ret, "Error reading PHY_INT_SRC"); +	/* 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); -	check_warn_return(ret, "Error writing INT_STS"); +	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: %d duplex: %d lcladv: %04x" -		" rmtadv: %04x", ecmd.speed, ecmd.duplex, lcladv, rmtadv); +	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);  } @@ -531,43 +662,21 @@ 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", urb->actual_length); +		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", 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", intdata); -} - -/* Enable or disable Rx checksum offload engine */ -static int smsc75xx_set_rx_csum_offload(struct usbnet *dev) -{ -	struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); -	unsigned long flags; -	int ret; - -	spin_lock_irqsave(&pdata->rfe_ctl_lock, flags); - -	if (pdata->use_rx_csum) -		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); - -	ret = smsc75xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl); -	check_warn_return(ret, "Error writing RFE_CTL"); - -	return 0; +		netdev_warn(dev->net, "unexpected interrupt, intdata=0x%08X\n", +			    intdata);  }  static int smsc75xx_ethtool_get_eeprom_len(struct net_device *net) @@ -591,40 +700,38 @@ static int smsc75xx_ethtool_set_eeprom(struct net_device *netdev,  	struct usbnet *dev = netdev_priv(netdev);  	if (ee->magic != LAN75XX_EEPROM_MAGIC) { -		netdev_warn(dev->net, -			"EEPROM: magic value mismatch: 0x%x", ee->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 u32 smsc75xx_ethtool_get_rx_csum(struct net_device *netdev) +static void smsc75xx_ethtool_get_wol(struct net_device *net, +				     struct ethtool_wolinfo *wolinfo)  { -	struct usbnet *dev = netdev_priv(netdev); +	struct usbnet *dev = netdev_priv(net);  	struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); -	return pdata->use_rx_csum; +	wolinfo->supported = SUPPORTED_WAKE; +	wolinfo->wolopts = pdata->wolopts;  } -static int smsc75xx_ethtool_set_rx_csum(struct net_device *netdev, u32 val) +static int smsc75xx_ethtool_set_wol(struct net_device *net, +				    struct ethtool_wolinfo *wolinfo)  { -	struct usbnet *dev = netdev_priv(netdev); +	struct usbnet *dev = netdev_priv(net);  	struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); +	int ret; -	pdata->use_rx_csum = !!val; - -	return smsc75xx_set_rx_csum_offload(dev); -} +	pdata->wolopts = wolinfo->wolopts & SUPPORTED_WAKE; -static int smsc75xx_ethtool_set_tso(struct net_device *netdev, u32 data) -{ -	if (data) -		netdev->features |= NETIF_F_TSO | NETIF_F_TSO6; -	else -		netdev->features &= ~(NETIF_F_TSO | NETIF_F_TSO6); +	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 0; +	return ret;  }  static const struct ethtool_ops smsc75xx_ethtool_ops = { @@ -638,12 +745,8 @@ static const struct ethtool_ops smsc75xx_ethtool_ops = {  	.get_eeprom_len	= smsc75xx_ethtool_get_eeprom_len,  	.get_eeprom	= smsc75xx_ethtool_get_eeprom,  	.set_eeprom	= smsc75xx_ethtool_set_eeprom, -	.get_tx_csum	= ethtool_op_get_tx_csum, -	.set_tx_csum	= ethtool_op_set_tx_hw_csum, -	.get_rx_csum	= smsc75xx_ethtool_get_rx_csum, -	.set_rx_csum	= smsc75xx_ethtool_set_rx_csum, -	.get_tso	= ethtool_op_get_tso, -	.set_tso	= smsc75xx_ethtool_set_tso, +	.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) @@ -664,14 +767,14 @@ static void smsc75xx_init_mac_address(struct usbnet *dev)  		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"); +				  "MAC address read from EEPROM\n");  			return;  		}  	}  	/* no eeprom, or eeprom values are invalid. generate random MAC */ -	random_ether_addr(dev->net->dev_addr); -	netif_dbg(dev, ifup, dev->net, "MAC address set to random_ether_addr"); +	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) @@ -681,24 +784,34 @@ static int smsc75xx_set_mac_address(struct usbnet *dev)  	u32 addr_hi = dev->net->dev_addr[4] | dev->net->dev_addr[5] << 8;  	int ret = smsc75xx_write_reg(dev, RX_ADDRH, addr_hi); -	check_warn_return(ret, "Failed to write RX_ADDRH: %d", ret); +	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); -	check_warn_return(ret, "Failed to write RX_ADDRL: %d", ret); +	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); -	check_warn_return(ret, "Failed to write ADDR_FILTX: %d", ret); +	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); -	check_warn_return(ret, "Failed to write ADDR_FILTX+4: %d", ret); +	if (ret < 0) +		netdev_warn(dev->net, "Failed to write ADDR_FILTX+4: %d\n", ret); -	return 0; +	return ret;  }  static int smsc75xx_phy_initialize(struct usbnet *dev)  { -	int bmcr, timeout = 0; +	int bmcr, ret, timeout = 0;  	/* Initialize MII structure */  	dev->mii.dev = dev->net; @@ -706,6 +819,7 @@ static int smsc75xx_phy_initialize(struct usbnet *dev)  	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 */ @@ -714,28 +828,38 @@ static int smsc75xx_phy_initialize(struct usbnet *dev)  	do {  		msleep(10);  		bmcr = smsc75xx_mdio_read(dev->net, dev->mii.phy_id, MII_BMCR); -		check_warn_return(bmcr, "Error reading MII_BMCR"); +		if (bmcr < 0) { +			netdev_warn(dev->net, "Error reading MII_BMCR\n"); +			return bmcr; +		}  		timeout++; -	} while ((bmcr & MII_BMCR) && (timeout < 100)); +	} while ((bmcr & BMCR_RESET) && (timeout < 100));  	if (timeout >= 100) { -		netdev_warn(dev->net, "timeout on PHY Reset"); +		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 to clear */ -	smsc75xx_mdio_read(dev->net, dev->mii.phy_id, PHY_INT_SRC); -	check_warn_return(bmcr, "Error reading PHY_INT_SRC"); +	/* 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"); +	netif_dbg(dev, ifup, dev->net, "phy initialised successfully\n");  	return 0;  } @@ -746,14 +870,20 @@ static int smsc75xx_set_rx_max_frame_length(struct usbnet *dev, int size)  	bool rxenabled;  	ret = smsc75xx_read_reg(dev, MAC_RX, &buf); -	check_warn_return(ret, "Failed to read MAC_RX: %d", ret); +	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); -		check_warn_return(ret, "Failed to write MAC_RX: %d", ret); +		if (ret < 0) { +			netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret); +			return ret; +		}  	}  	/* add 4 to size for FCS */ @@ -761,12 +891,18 @@ static int smsc75xx_set_rx_max_frame_length(struct usbnet *dev, int size)  	buf |= (((size + 4) << MAC_RX_MAX_SIZE_SHIFT) & MAC_RX_MAX_SIZE);  	ret = smsc75xx_write_reg(dev, MAC_RX, buf); -	check_warn_return(ret, "Failed to write MAC_RX: %d", ret); +	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); -		check_warn_return(ret, "Failed to write MAC_RX: %d", ret); +		if (ret < 0) { +			netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret); +			return ret; +		}  	}  	return 0; @@ -775,89 +911,184 @@ static int smsc75xx_set_rx_max_frame_length(struct usbnet *dev, int size)  static int smsc75xx_change_mtu(struct net_device *netdev, int new_mtu)  {  	struct usbnet *dev = netdev_priv(netdev); +	int ret; -	int ret = smsc75xx_set_rx_max_frame_length(dev, new_mtu); -	check_warn_return(ret, "Failed to set mac rx frame length"); +	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"); +	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); -	check_warn_return(ret, "Failed to read HW_CFG: %d", ret); +	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); -	check_warn_return(ret, "Failed to write HW_CFG: %d", ret); +	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); -		check_warn_return(ret, "Failed to read HW_CFG: %d", ret); +		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"); +		netdev_warn(dev->net, "timeout on completion of Lite Reset\n");  		return -EIO;  	} -	netif_dbg(dev, ifup, dev->net, "Lite reset complete, resetting PHY"); +	netif_dbg(dev, ifup, dev->net, "Lite reset complete, resetting PHY\n");  	ret = smsc75xx_read_reg(dev, PMT_CTL, &buf); -	check_warn_return(ret, "Failed to read PMT_CTL: %d", ret); +	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); -	check_warn_return(ret, "Failed to write PMT_CTL: %d", ret); +	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); -		check_warn_return(ret, "Failed to read PMT_CTL: %d", ret); +		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"); +		netdev_warn(dev->net, "timeout waiting for PHY Reset\n");  		return -EIO;  	} -	netif_dbg(dev, ifup, dev->net, "PHY reset complete"); - -	smsc75xx_init_mac_address(dev); +	netif_dbg(dev, ifup, dev->net, "PHY reset complete\n");  	ret = smsc75xx_set_mac_address(dev); -	check_warn_return(ret, "Failed to set mac address"); +	if (ret < 0) { +		netdev_warn(dev->net, "Failed to set mac address\n"); +		return ret; +	} -	netif_dbg(dev, ifup, dev->net, "MAC Address: %pM", dev->net->dev_addr); +	netif_dbg(dev, ifup, dev->net, "MAC Address: %pM\n", +		  dev->net->dev_addr);  	ret = smsc75xx_read_reg(dev, HW_CFG, &buf); -	check_warn_return(ret, "Failed to read HW_CFG: %d", ret); +	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", buf); +	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); -	check_warn_return(ret, "Failed to write HW_CFG: %d", ret); +	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); -	check_warn_return(ret, "Failed to read HW_CFG: %d", ret); +	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", buf); +	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; @@ -870,160 +1101,269 @@ static int smsc75xx_reset(struct usbnet *dev)  		dev->rx_urb_size = DEFAULT_FS_BURST_CAP_SIZE;  	} -	netif_dbg(dev, ifup, dev->net, "rx_urb_size=%ld", -		(ulong)dev->rx_urb_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); -	check_warn_return(ret, "Failed to write BURST_CAP: %d", ret); +	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); -	check_warn_return(ret, "Failed to read BURST_CAP: %d", ret); +	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", buf); +		  "Read Value from BURST_CAP after writing: 0x%08x\n", buf);  	ret = smsc75xx_write_reg(dev, BULK_IN_DLY, DEFAULT_BULK_IN_DELAY); -	check_warn_return(ret, "Failed to write BULK_IN_DLY: %d", ret); +	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); -	check_warn_return(ret, "Failed to read BULK_IN_DLY: %d", ret); +	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", buf); +		  "Read Value from BULK_IN_DLY after writing: 0x%08x\n", buf);  	if (turbo_mode) {  		ret = smsc75xx_read_reg(dev, HW_CFG, &buf); -		check_warn_return(ret, "Failed to read HW_CFG: %d", ret); +		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", buf); +		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); -		check_warn_return(ret, "Failed to write HW_CFG: %d", ret); +		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); -		check_warn_return(ret, "Failed to read HW_CFG: %d", ret); +		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", buf); +		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); -	check_warn_return(ret, "Failed to write FCT_RX_FIFO_END: %d", ret); +	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", buf); +	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); -	check_warn_return(ret, "Failed to write FCT_TX_FIFO_END: %d", ret); +	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", buf); +	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); -	check_warn_return(ret, "Failed to write INT_STS: %d", ret); +	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); -	check_warn_return(ret, "Failed to read ID_REV: %d", ret); +	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", buf); +	netif_dbg(dev, ifup, dev->net, "ID_REV = 0x%08x\n", buf); -	/* Configure GPIO pins as LED outputs */ -	ret = smsc75xx_read_reg(dev, LED_GPIO_CFG, &buf); -	check_warn_return(ret, "Failed to read LED_GPIO_CFG: %d", ret); +	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; +	} -	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; +	/* 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; +		} -	ret = smsc75xx_write_reg(dev, LED_GPIO_CFG, buf); -	check_warn_return(ret, "Failed to write LED_GPIO_CFG: %d", 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); -	check_warn_return(ret, "Failed to write FLOW: %d", ret); +	if (ret < 0) { +		netdev_warn(dev->net, "Failed to write FLOW: %d\n", ret); +		return ret; +	}  	ret = smsc75xx_write_reg(dev, FCT_FLOW, 0); -	check_warn_return(ret, "Failed to write FCT_FLOW: %d", ret); +	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); -	check_warn_return(ret, "Failed to read RFE_CTL: %d", ret); +	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); -	check_warn_return(ret, "Failed to write RFE_CTL: %d", ret); +	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); -	check_warn_return(ret, "Failed to read RFE_CTL: %d", ret); +	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", pdata->rfe_ctl); +	netif_dbg(dev, ifup, dev->net, "RFE_CTL set to 0x%08x\n", +		  pdata->rfe_ctl);  	/* Enable or disable checksum offload engines */ -	ethtool_op_set_tx_hw_csum(dev->net, DEFAULT_TX_CSUM_ENABLE); -	ret = smsc75xx_set_rx_csum_offload(dev); -	check_warn_return(ret, "Failed to set rx csum offload: %d", ret); - -	smsc75xx_ethtool_set_tso(dev->net, DEFAULT_TSO_ENABLE); +	smsc75xx_set_features(dev->net, dev->net->features);  	smsc75xx_set_multicast(dev->net);  	ret = smsc75xx_phy_initialize(dev); -	check_warn_return(ret, "Failed to initialize PHY: %d", ret); +	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); -	check_warn_return(ret, "Failed to read INT_EP_CTL: %d", ret); +	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); -	check_warn_return(ret, "Failed to write INT_EP_CTL: %d", ret); +	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); -	check_warn_return(ret, "Failed to read MAC_TX: %d", ret); +	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); -	check_warn_return(ret, "Failed to write MAC_TX: %d", ret); +	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", buf); +	netif_dbg(dev, ifup, dev->net, "MAC_TX set to 0x%08x\n", buf);  	ret = smsc75xx_read_reg(dev, FCT_TX_CTL, &buf); -	check_warn_return(ret, "Failed to read FCT_TX_CTL: %d", ret); +	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); -	check_warn_return(ret, "Failed to write FCT_TX_CTL: %d", ret); +	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", buf); +	netif_dbg(dev, ifup, dev->net, "FCT_TX_CTL set to 0x%08x\n", buf); -	ret = smsc75xx_set_rx_max_frame_length(dev, 1514); -	check_warn_return(ret, "Failed to set max rx frame length"); +	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); -	check_warn_return(ret, "Failed to read MAC_RX: %d", ret); +	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); -	check_warn_return(ret, "Failed to write MAC_RX: %d", ret); +	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", buf); +	netif_dbg(dev, ifup, dev->net, "MAC_RX set to 0x%08x\n", buf);  	ret = smsc75xx_read_reg(dev, FCT_RX_CTL, &buf); -	check_warn_return(ret, "Failed to read FCT_RX_CTL: %d", ret); +	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); -	check_warn_return(ret, "Failed to write FCT_RX_CTL: %d", ret); +	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", buf); +	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"); +	netif_dbg(dev, ifup, dev->net, "smsc75xx_reset, return 0\n");  	return 0;  } @@ -1036,7 +1376,8 @@ static const struct net_device_ops smsc75xx_netdev_ops = {  	.ndo_set_mac_address 	= eth_mac_addr,  	.ndo_validate_addr	= eth_validate_addr,  	.ndo_do_ioctl 		= smsc75xx_ioctl, -	.ndo_set_multicast_list = smsc75xx_set_multicast, +	.ndo_set_rx_mode	= smsc75xx_set_multicast, +	.ndo_set_features	= smsc75xx_set_features,  };  static int smsc75xx_bind(struct usbnet *dev, struct usb_interface *intf) @@ -1047,16 +1388,17 @@ static int smsc75xx_bind(struct usbnet *dev, struct usb_interface *intf)  	printk(KERN_INFO SMSC_CHIPNAME " v" SMSC_DRIVER_VERSION "\n");  	ret = usbnet_get_endpoints(dev, intf); -	check_warn_return(ret, "usbnet_get_endpoints failed: %d", ret); +	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); +					      GFP_KERNEL);  	pdata = (struct smsc75xx_priv *)(dev->data[0]); -	if (!pdata) { -		netdev_warn(dev->net, "Unable to allocate smsc75xx_priv"); +	if (!pdata)  		return -ENOMEM; -	}  	pdata->dev = dev; @@ -1065,18 +1407,35 @@ static int smsc75xx_bind(struct usbnet *dev, struct usb_interface *intf)  	INIT_WORK(&pdata->set_multicast, smsc75xx_deferred_multicast_write); -	pdata->use_rx_csum = DEFAULT_RX_CSUM_ENABLE; +	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; +	} -	/* We have to advertise SG otherwise TSO cannot be enabled */ -	dev->net->features |= NETIF_F_SG; +	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;  } @@ -1084,17 +1443,660 @@ 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"); +		netif_dbg(dev, ifdown, dev->net, "free pdata\n");  		kfree(pdata);  		pdata = NULL;  		dev->data[0] = 0;  	}  } -static void smsc75xx_rx_csum_offload(struct sk_buff *skb, u32 rx_cmd_a, -				     u32 rx_cmd_b) +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)  { -	if (unlikely(rx_cmd_a & RX_CMD_A_LCSM)) { +	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)); @@ -1104,7 +2106,9 @@ static void smsc75xx_rx_csum_offload(struct sk_buff *skb, u32 rx_cmd_a,  static int smsc75xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  { -	struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); +	/* 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; @@ -1117,17 +2121,17 @@ static int smsc75xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  		memcpy(&rx_cmd_b, skb->data, sizeof(rx_cmd_b));  		le32_to_cpus(&rx_cmd_b); -		skb_pull(skb, 4 + NET_IP_ALIGN); +		skb_pull(skb, 4 + RXW_PADDING);  		packet = skb->data;  		/* get the packet length */ -		size = (rx_cmd_a & RX_CMD_A_LEN) - NET_IP_ALIGN; -		align_count = (4 - ((size + NET_IP_ALIGN) % 4)) % 4; +		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", rx_cmd_a); +				  "Error rx_cmd_a=0x%08x\n", rx_cmd_a);  			dev->net->stats.rx_errors++;  			dev->net->stats.rx_dropped++; @@ -1136,20 +2140,18 @@ static int smsc75xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  			else if (rx_cmd_a & (RX_CMD_A_LONG | RX_CMD_A_RUNT))  				dev->net->stats.rx_frame_errors++;  		} else { -			/* ETH_FRAME_LEN + 4(CRC) + 2(COE) + 4(Vlan) */ -			if (unlikely(size > (ETH_FRAME_LEN + 12))) { +			/* 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", rx_cmd_a); +					  "size err rx_cmd_a=0x%08x\n", +					  rx_cmd_a);  				return 0;  			}  			/* last frame in this batch */  			if (skb->len == size) { -				if (pdata->use_rx_csum) -					smsc75xx_rx_csum_offload(skb, rx_cmd_a, -						rx_cmd_b); -				else -					skb->ip_summed = CHECKSUM_NONE; +				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); @@ -1159,7 +2161,7 @@ static int smsc75xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  			ax_skb = skb_clone(skb, GFP_ATOMIC);  			if (unlikely(!ax_skb)) { -				netdev_warn(dev->net, "Error allocating skb"); +				netdev_warn(dev->net, "Error allocating skb\n");  				return 0;  			} @@ -1167,11 +2169,8 @@ static int smsc75xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  			ax_skb->data = packet;  			skb_set_tail_pointer(ax_skb, size); -			if (pdata->use_rx_csum) -				smsc75xx_rx_csum_offload(ax_skb, rx_cmd_a, -					rx_cmd_b); -			else -				ax_skb->ip_summed = CHECKSUM_NONE; +			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); @@ -1187,7 +2186,7 @@ static int smsc75xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  	}  	if (unlikely(skb->len < 0)) { -		netdev_warn(dev->net, "invalid rx length<0 %d", skb->len); +		netdev_warn(dev->net, "invalid rx length<0 %d\n", skb->len);  		return 0;  	} @@ -1199,8 +2198,6 @@ static struct sk_buff *smsc75xx_tx_fixup(struct usbnet *dev,  {  	u32 tx_cmd_a, tx_cmd_b; -	skb_linearize(skb); -  	if (skb_headroom(skb) < SMSC75XX_TX_OVERHEAD) {  		struct sk_buff *skb2 =  			skb_copy_expand(skb, SMSC75XX_TX_OVERHEAD, 0, flags); @@ -1235,6 +2232,12 @@ static struct sk_buff *smsc75xx_tx_fixup(struct usbnet *dev,  	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, @@ -1244,7 +2247,8 @@ static const struct driver_info smsc75xx_info = {  	.rx_fixup	= smsc75xx_rx_fixup,  	.tx_fixup	= smsc75xx_tx_fixup,  	.status		= smsc75xx_status, -	.flags		= FLAG_ETHER | FLAG_SEND_ZLP, +	.manage_power	= smsc75xx_manage_power, +	.flags		= FLAG_ETHER | FLAG_SEND_ZLP | FLAG_LINK_INTR,  };  static const struct usb_device_id products[] = { @@ -1266,24 +2270,17 @@ static struct usb_driver smsc75xx_driver = {  	.name		= SMSC_CHIPNAME,  	.id_table	= products,  	.probe		= usbnet_probe, -	.suspend	= usbnet_suspend, -	.resume		= usbnet_resume, +	.suspend	= smsc75xx_suspend, +	.resume		= smsc75xx_resume, +	.reset_resume	= smsc75xx_resume,  	.disconnect	= usbnet_disconnect, +	.disable_hub_initiated_lpm = 1, +	.supports_autosuspend = 1,  }; -static int __init smsc75xx_init(void) -{ -	return usb_register(&smsc75xx_driver); -} -module_init(smsc75xx_init); - -static void __exit smsc75xx_exit(void) -{ -	usb_deregister(&smsc75xx_driver); -} -module_exit(smsc75xx_exit); +module_usb_driver(smsc75xx_driver);  MODULE_AUTHOR("Nancy Lin"); -MODULE_AUTHOR("Steve Glendinning <steve.glendinning@smsc.com>"); +MODULE_AUTHOR("Steve Glendinning <steve.glendinning@shawell.net>");  MODULE_DESCRIPTION("SMSC75XX USB 2.0 Gigabit Ethernet Devices");  MODULE_LICENSE("GPL"); diff --git a/drivers/net/usb/smsc75xx.h b/drivers/net/usb/smsc75xx.h index 16e98c77834..2c7ea8fd184 100644 --- a/drivers/net/usb/smsc75xx.h +++ b/drivers/net/usb/smsc75xx.h @@ -13,8 +13,7 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. + * along with this program; if not, see <http://www.gnu.org/licenses/>.   *   *****************************************************************************/ @@ -388,6 +387,7 @@  #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) diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index 65cb1abfbe5..d07bf4cb893 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -13,19 +13,19 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. + * along with this program; if not, see <http://www.gnu.org/licenses/>.   *   *****************************************************************************/  #include <linux/module.h>  #include <linux/kmod.h> -#include <linux/init.h>  #include <linux/netdevice.h>  #include <linux/etherdevice.h>  #include <linux/ethtool.h>  #include <linux/mii.h>  #include <linux/usb.h> +#include <linux/bitrev.h> +#include <linux/crc16.h>  #include <linux/crc32.h>  #include <linux/usb/usbnet.h>  #include <linux/slab.h> @@ -46,83 +46,128 @@  #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; -	bool use_tx_csum; -	bool use_rx_csum; +	u8 features; +	u8 suspend_flags;  }; -struct usb_context { -	struct usb_ctrlrequest req; -	struct usbnet *dev; -}; - -static int turbo_mode = true; +static bool turbo_mode = true;  module_param(turbo_mode, bool, 0644);  MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction"); -static int smsc95xx_read_reg(struct usbnet *dev, u32 index, u32 *data) +static int __must_check __smsc95xx_read_reg(struct usbnet *dev, u32 index, +					    u32 *data, int in_pm)  { -	u32 *buf = kmalloc(4, GFP_KERNEL); +	u32 buf;  	int ret; +	int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16);  	BUG_ON(!dev); -	if (!buf) -		return -ENOMEM; - -	ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), -		USB_VENDOR_REQUEST_READ_REGISTER, -		USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, -		00, index, buf, 4, USB_CTRL_GET_TIMEOUT); +	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 register index 0x%08x\n", index); +		netdev_warn(dev->net, "Failed to read reg index 0x%08x: %d\n", +			    index, ret); -	le32_to_cpus(buf); -	*data = *buf; -	kfree(buf); +	le32_to_cpus(&buf); +	*data = buf;  	return ret;  } -static int smsc95xx_write_reg(struct usbnet *dev, u32 index, u32 data) +static int __must_check __smsc95xx_write_reg(struct usbnet *dev, u32 index, +					     u32 data, int in_pm)  { -	u32 *buf = kmalloc(4, GFP_KERNEL); +	u32 buf;  	int ret; +	int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16);  	BUG_ON(!dev); -	if (!buf) -		return -ENOMEM; - -	*buf = data; -	cpu_to_le32s(buf); +	if (!in_pm) +		fn = usbnet_write_cmd; +	else +		fn = usbnet_write_cmd_nopm; -	ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), -		USB_VENDOR_REQUEST_WRITE_REGISTER, -		USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, -		00, index, buf, 4, USB_CTRL_SET_TIMEOUT); +	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 register index 0x%08x\n", index); - -	kfree(buf); +		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 smsc95xx_phy_wait_not_busy(struct usbnet *dev) +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 { -		smsc95xx_read_reg(dev, MII_ADDR, &val); +		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)); @@ -130,76 +175,130 @@ static int smsc95xx_phy_wait_not_busy(struct usbnet *dev)  	return -EIO;  } -static int smsc95xx_mdio_read(struct net_device *netdev, int phy_id, int idx) +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 */ -	if (smsc95xx_phy_wait_not_busy(dev)) { +	ret = __smsc95xx_phy_wait_not_busy(dev, in_pm); +	if (ret < 0) {  		netdev_warn(dev->net, "MII is busy in smsc95xx_mdio_read\n"); -		mutex_unlock(&dev->phy_mutex); -		return -EIO; +		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_; -	smsc95xx_write_reg(dev, MII_ADDR, addr); +	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; +	} -	if (smsc95xx_phy_wait_not_busy(dev)) { +	ret = __smsc95xx_phy_wait_not_busy(dev, in_pm); +	if (ret < 0) {  		netdev_warn(dev->net, "Timed out reading MII reg %02X\n", idx); -		mutex_unlock(&dev->phy_mutex); -		return -EIO; +		goto done;  	} -	smsc95xx_read_reg(dev, MII_DATA, &val); +	ret = __smsc95xx_read_reg(dev, MII_DATA, &val, in_pm); +	if (ret < 0) { +		netdev_warn(dev->net, "Error reading MII_DATA\n"); +		goto done; +	} -	mutex_unlock(&dev->phy_mutex); +	ret = (u16)(val & 0xFFFF); -	return (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) +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 */ -	if (smsc95xx_phy_wait_not_busy(dev)) { +	ret = __smsc95xx_phy_wait_not_busy(dev, in_pm); +	if (ret < 0) {  		netdev_warn(dev->net, "MII is busy in smsc95xx_mdio_write\n"); -		mutex_unlock(&dev->phy_mutex); -		return; +		goto done;  	}  	val = regval; -	smsc95xx_write_reg(dev, MII_DATA, val); +	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_; -	smsc95xx_write_reg(dev, MII_ADDR, addr); +	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; +	} -	if (smsc95xx_phy_wait_not_busy(dev)) +	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_wait_eeprom(struct usbnet *dev) +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 { -		smsc95xx_read_reg(dev, E2P_CMD, &val); +		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); @@ -213,13 +312,18 @@ static int smsc95xx_wait_eeprom(struct usbnet *dev)  	return 0;  } -static int smsc95xx_eeprom_confirm_not_busy(struct usbnet *dev) +static int __must_check smsc95xx_eeprom_confirm_not_busy(struct usbnet *dev)  {  	unsigned long start_time = jiffies;  	u32 val; +	int ret;  	do { -		smsc95xx_read_reg(dev, E2P_CMD, &val); +		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; @@ -246,13 +350,21 @@ static int smsc95xx_read_eeprom(struct usbnet *dev, u32 offset, u32 length,  	for (i = 0; i < length; i++) {  		val = E2P_CMD_BUSY_ | E2P_CMD_READ_ | (offset & E2P_CMD_ADDR_); -		smsc95xx_write_reg(dev, E2P_CMD, val); +		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; -		smsc95xx_read_reg(dev, E2P_DATA, &val); +		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++; @@ -276,7 +388,11 @@ static int smsc95xx_write_eeprom(struct usbnet *dev, u32 offset, u32 length,  	/* Issue write/erase enable command */  	val = E2P_CMD_BUSY_ | E2P_CMD_EWEN_; -	smsc95xx_write_reg(dev, E2P_CMD, val); +	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) @@ -286,11 +402,19 @@ static int smsc95xx_write_eeprom(struct usbnet *dev, u32 offset, u32 length,  		/* Fill data register */  		val = data[i]; -		smsc95xx_write_reg(dev, E2P_DATA, val); +		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_); -		smsc95xx_write_reg(dev, E2P_CMD, val); +		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) @@ -302,60 +426,24 @@ static int smsc95xx_write_eeprom(struct usbnet *dev, u32 offset, u32 length,  	return 0;  } -static void smsc95xx_async_cmd_callback(struct urb *urb) +static int __must_check smsc95xx_write_reg_async(struct usbnet *dev, u16 index, +						 u32 data)  { -	struct usb_context *usb_context = urb->context; -	struct usbnet *dev = usb_context->dev; -	int status = urb->status; - -	if (status < 0) -		netdev_warn(dev->net, "async callback failed with %d\n", status); - -	kfree(usb_context); -	usb_free_urb(urb); -} - -static int smsc95xx_write_reg_async(struct usbnet *dev, u16 index, u32 *data) -{ -	struct usb_context *usb_context; -	int status; -	struct urb *urb;  	const u16 size = 4; +	u32 buf; +	int ret; -	urb = usb_alloc_urb(0, GFP_ATOMIC); -	if (!urb) { -		netdev_warn(dev->net, "Error allocating URB\n"); -		return -ENOMEM; -	} - -	usb_context = kmalloc(sizeof(struct usb_context), GFP_ATOMIC); -	if (usb_context == NULL) { -		netdev_warn(dev->net, "Error allocating control msg\n"); -		usb_free_urb(urb); -		return -ENOMEM; -	} - -	usb_context->req.bRequestType = -		USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE; -	usb_context->req.bRequest = USB_VENDOR_REQUEST_WRITE_REGISTER; -	usb_context->req.wValue = 00; -	usb_context->req.wIndex = cpu_to_le16(index); -	usb_context->req.wLength = cpu_to_le16(size); - -	usb_fill_control_urb(urb, dev->udev, usb_sndctrlpipe(dev->udev, 0), -		(void *)&usb_context->req, data, size, -		smsc95xx_async_cmd_callback, -		(void *)usb_context); - -	status = usb_submit_urb(urb, GFP_ATOMIC); -	if (status < 0) { -		netdev_warn(dev->net, "Error submitting control msg, sts=%d\n", -			    status); -		kfree(usb_context); -		usb_free_urb(urb); -	} +	buf = data; +	cpu_to_le32s(&buf); -	return status; +	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 @@ -370,9 +458,11 @@ 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]); -	u32 hash_hi = 0; -	u32 hash_lo = 0;  	unsigned long flags; +	int ret; + +	pdata->hash_hi = 0; +	pdata->hash_lo = 0;  	spin_lock_irqsave(&pdata->mac_cr_lock, flags); @@ -394,13 +484,13 @@ static void smsc95xx_set_multicast(struct net_device *netdev)  			u32 bitnum = smsc95xx_hash(ha->addr);  			u32 mask = 0x01 << (bitnum & 0x1F);  			if (bitnum & 0x20) -				hash_hi |= mask; +				pdata->hash_hi |= mask;  			else -				hash_lo |= mask; +				pdata->hash_lo |= mask;  		}  		netif_dbg(dev, drv, dev->net, "HASHH=0x%08X, HASHL=0x%08X\n", -				   hash_hi, hash_lo); +				   pdata->hash_hi, pdata->hash_lo);  	} else {  		netif_dbg(dev, drv, dev->net, "receive own packets only\n");  		pdata->mac_cr &= @@ -410,21 +500,27 @@ static void smsc95xx_set_multicast(struct net_device *netdev)  	spin_unlock_irqrestore(&pdata->mac_cr_lock, flags);  	/* Initiate async writes, as we can't wait for completion here */ -	smsc95xx_write_reg_async(dev, HASHH, &hash_hi); -	smsc95xx_write_reg_async(dev, HASHL, &hash_lo); -	smsc95xx_write_reg_async(dev, MAC_CR, &pdata->mac_cr); +	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 void smsc95xx_phy_update_flowcontrol(struct usbnet *dev, u8 duplex, -					    u16 lcladv, u16 rmtadv) +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) { -		netdev_warn(dev->net, "error reading AFC_CFG\n"); -		return; -	} +	if (ret < 0) +		return ret;  	if (duplex == DUPLEX_FULL) {  		u8 cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv); @@ -448,31 +544,39 @@ static void smsc95xx_phy_update_flowcontrol(struct usbnet *dev, u8 duplex,  		afc_cfg |= 0xF;  	} -	smsc95xx_write_reg(dev, FLOW, flow); -	smsc95xx_write_reg(dev,	AFC_CFG, afc_cfg); +	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; +	struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET };  	unsigned long flags;  	u16 lcladv, rmtadv; -	u32 intdata; +	int ret;  	/* clear interrupt status */ -	smsc95xx_mdio_read(dev->net, mii->phy_id, PHY_INT_SRC); -	intdata = 0xFFFFFFFF; -	smsc95xx_write_reg(dev, INT_STS, intdata); +	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: %d duplex: %d lcladv: %04x rmtadv: %04x\n", -		  ecmd.speed, ecmd.duplex, lcladv, rmtadv); +	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) { @@ -484,11 +588,15 @@ static int smsc95xx_link_reset(struct usbnet *dev)  	}  	spin_unlock_irqrestore(&pdata->mac_cr_lock, flags); -	smsc95xx_write_reg(dev, MAC_CR, pdata->mac_cr); +	ret = smsc95xx_write_reg(dev, MAC_CR, pdata->mac_cr); +	if (ret < 0) +		return ret; -	smsc95xx_phy_update_flowcontrol(dev, ecmd.duplex, lcladv, rmtadv); +	ret = smsc95xx_phy_update_flowcontrol(dev, ecmd.duplex, lcladv, rmtadv); +	if (ret < 0) +		netdev_warn(dev->net, "Error updating PHY flow control\n"); -	return 0; +	return ret;  }  static void smsc95xx_status(struct usbnet *dev, struct urb *urb) @@ -514,31 +622,30 @@ static void smsc95xx_status(struct usbnet *dev, struct urb *urb)  }  /* Enable or disable Tx & Rx checksum offload engines */ -static int smsc95xx_set_csums(struct usbnet *dev) +static int smsc95xx_set_features(struct net_device *netdev, +	netdev_features_t features)  { -	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); +	struct usbnet *dev = netdev_priv(netdev);  	u32 read_buf; -	int ret = smsc95xx_read_reg(dev, COE_CR, &read_buf); -	if (ret < 0) { -		netdev_warn(dev->net, "Failed to read COE_CR: %d\n", ret); +	int ret; + +	ret = smsc95xx_read_reg(dev, COE_CR, &read_buf); +	if (ret < 0)  		return ret; -	} -	if (pdata->use_tx_csum) +	if (features & NETIF_F_HW_CSUM)  		read_buf |= Tx_COE_EN_;  	else  		read_buf &= ~Tx_COE_EN_; -	if (pdata->use_rx_csum) +	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) { -		netdev_warn(dev->net, "Failed to write COE_CR: %d\n", ret); +	if (ret < 0)  		return ret; -	}  	netif_dbg(dev, hw, dev->net, "COE_CR = 0x%08x\n", read_buf);  	return 0; @@ -573,41 +680,60 @@ static int smsc95xx_ethtool_set_eeprom(struct net_device *netdev,  	return smsc95xx_write_eeprom(dev, ee->offset, ee->len, data);  } -static u32 smsc95xx_ethtool_get_rx_csum(struct net_device *netdev) +static int smsc95xx_ethtool_getregslen(struct net_device *netdev)  { -	struct usbnet *dev = netdev_priv(netdev); -	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); - -	return pdata->use_rx_csum; +	/* all smsc95xx registers */ +	return COE_CR - ID_REV + sizeof(u32);  } -static int smsc95xx_ethtool_set_rx_csum(struct net_device *netdev, u32 val) +static void +smsc95xx_ethtool_getregs(struct net_device *netdev, struct ethtool_regs *regs, +			 void *buf)  {  	struct usbnet *dev = netdev_priv(netdev); -	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); +	unsigned int i, j; +	int retval; +	u32 *data = buf; -	pdata->use_rx_csum = !!val; +	retval = smsc95xx_read_reg(dev, ID_REV, ®s->version); +	if (retval < 0) { +		netdev_warn(netdev, "REGS: cannot read ID_REV\n"); +		return; +	} -	return smsc95xx_set_csums(dev); +	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 u32 smsc95xx_ethtool_get_tx_csum(struct net_device *netdev) +static void smsc95xx_ethtool_get_wol(struct net_device *net, +				     struct ethtool_wolinfo *wolinfo)  { -	struct usbnet *dev = netdev_priv(netdev); +	struct usbnet *dev = netdev_priv(net);  	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); -	return pdata->use_tx_csum; +	wolinfo->supported = SUPPORTED_WAKE; +	wolinfo->wolopts = pdata->wolopts;  } -static int smsc95xx_ethtool_set_tx_csum(struct net_device *netdev, u32 val) +static int smsc95xx_ethtool_set_wol(struct net_device *net, +				    struct ethtool_wolinfo *wolinfo)  { -	struct usbnet *dev = netdev_priv(netdev); +	struct usbnet *dev = netdev_priv(net);  	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); +	int ret; -	pdata->use_tx_csum = !!val; +	pdata->wolopts = wolinfo->wolopts & SUPPORTED_WAKE; -	ethtool_op_set_tx_hw_csum(netdev, pdata->use_tx_csum); -	return smsc95xx_set_csums(dev); +	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 = { @@ -621,10 +747,10 @@ static const struct ethtool_ops smsc95xx_ethtool_ops = {  	.get_eeprom_len	= smsc95xx_ethtool_get_eeprom_len,  	.get_eeprom	= smsc95xx_ethtool_get_eeprom,  	.set_eeprom	= smsc95xx_ethtool_set_eeprom, -	.get_tx_csum	= smsc95xx_ethtool_get_tx_csum, -	.set_tx_csum	= smsc95xx_ethtool_set_tx_csum, -	.get_rx_csum	= smsc95xx_ethtool_get_rx_csum, -	.set_rx_csum	= smsc95xx_ethtool_set_rx_csum, +	.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) @@ -650,8 +776,8 @@ static void smsc95xx_init_mac_address(struct usbnet *dev)  	}  	/* no eeprom, or eeprom values are invalid. generate random MAC */ -	random_ether_addr(dev->net->dev_addr); -	netif_dbg(dev, ifup, dev->net, "MAC address set to random_ether_addr\n"); +	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) @@ -662,41 +788,34 @@ static int smsc95xx_set_mac_address(struct usbnet *dev)  	int ret;  	ret = smsc95xx_write_reg(dev, ADDRL, addr_lo); -	if (ret < 0) { -		netdev_warn(dev->net, "Failed to write ADDRL: %d\n", ret); -		return ret; -	} - -	ret = smsc95xx_write_reg(dev, ADDRH, addr_hi); -	if (ret < 0) { -		netdev_warn(dev->net, "Failed to write ADDRH: %d\n", ret); +	if (ret < 0)  		return ret; -	} -	return 0; +	return smsc95xx_write_reg(dev, ADDRH, addr_hi);  }  /* starts the TX path */ -static void smsc95xx_start_tx_path(struct usbnet *dev) +static int smsc95xx_start_tx_path(struct usbnet *dev)  {  	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);  	unsigned long flags; -	u32 reg_val; +	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); -	smsc95xx_write_reg(dev, MAC_CR, pdata->mac_cr); +	ret = smsc95xx_write_reg(dev, MAC_CR, pdata->mac_cr); +	if (ret < 0) +		return ret;  	/* Enable Tx at SCSRs */ -	reg_val = TX_CFG_ON_; -	smsc95xx_write_reg(dev, TX_CFG, reg_val); +	return smsc95xx_write_reg(dev, TX_CFG, TX_CFG_ON_);  }  /* Starts the Receive path */ -static void smsc95xx_start_rx_path(struct usbnet *dev) +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; @@ -705,12 +824,12 @@ static void smsc95xx_start_rx_path(struct usbnet *dev)  	pdata->mac_cr |= MAC_CR_RXEN_;  	spin_unlock_irqrestore(&pdata->mac_cr_lock, flags); -	smsc95xx_write_reg(dev, MAC_CR, pdata->mac_cr); +	return __smsc95xx_write_reg(dev, MAC_CR, pdata->mac_cr, in_pm);  }  static int smsc95xx_phy_initialize(struct usbnet *dev)  { -	int bmcr, timeout = 0; +	int bmcr, ret, timeout = 0;  	/* Initialize MII structure */  	dev->mii.dev = dev->net; @@ -727,7 +846,7 @@ static int smsc95xx_phy_initialize(struct usbnet *dev)  		msleep(10);  		bmcr = smsc95xx_mdio_read(dev->net, dev->mii.phy_id, MII_BMCR);  		timeout++; -	} while ((bmcr & MII_BMCR) && (timeout < 100)); +	} while ((bmcr & BMCR_RESET) && (timeout < 100));  	if (timeout >= 100) {  		netdev_warn(dev->net, "timeout on PHY Reset"); @@ -739,7 +858,11 @@ static int smsc95xx_phy_initialize(struct usbnet *dev)  		ADVERTISE_PAUSE_ASYM);  	/* read to clear */ -	smsc95xx_mdio_read(dev->net, dev->mii.phy_id, PHY_INT_SRC); +	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_); @@ -752,28 +875,21 @@ static int smsc95xx_phy_initialize(struct usbnet *dev)  static int smsc95xx_reset(struct usbnet *dev)  {  	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); -	struct net_device *netdev = dev->net;  	u32 read_buf, write_buf, burst_cap;  	int ret = 0, timeout;  	netif_dbg(dev, ifup, dev->net, "entering smsc95xx_reset\n"); -	write_buf = HW_CFG_LRST_; -	ret = smsc95xx_write_reg(dev, HW_CFG, write_buf); -	if (ret < 0) { -		netdev_warn(dev->net, "Failed to write HW_CFG_LRST_ bit in HW_CFG register, ret = %d\n", -			    ret); +	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) { -			netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); +		if (ret < 0)  			return ret; -		} -		msleep(10);  		timeout++;  	} while ((read_buf & HW_CFG_LRST_) && (timeout < 100)); @@ -782,21 +898,16 @@ static int smsc95xx_reset(struct usbnet *dev)  		return ret;  	} -	write_buf = PM_CTL_PHY_RST_; -	ret = smsc95xx_write_reg(dev, PM_CTRL, write_buf); -	if (ret < 0) { -		netdev_warn(dev->net, "Failed to write PM_CTRL: %d\n", 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) { -			netdev_warn(dev->net, "Failed to read PM_CTRL: %d\n", ret); +		if (ret < 0)  			return ret; -		} -		msleep(10);  		timeout++;  	} while ((read_buf & PM_CTL_PHY_RST_) && (timeout < 100)); @@ -809,32 +920,26 @@ static int smsc95xx_reset(struct usbnet *dev)  	if (ret < 0)  		return ret; -	netif_dbg(dev, ifup, dev->net, -		  "MAC Address: %pM\n", dev->net->dev_addr); +	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) { -		netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); +	if (ret < 0)  		return ret; -	} -	netif_dbg(dev, ifup, dev->net, -		  "Read Value from HW_CFG : 0x%08x\n", read_buf); +	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) { -		netdev_warn(dev->net, "Failed to write HW_CFG_BIR_ bit in HW_CFG register, ret = %d\n", -			    ret); +	if (ret < 0)  		return ret; -	}  	ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); -	if (ret < 0) { -		netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); +	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); @@ -850,47 +955,39 @@ static int smsc95xx_reset(struct usbnet *dev)  		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); +	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) { -		netdev_warn(dev->net, "Failed to write BURST_CAP: %d\n", ret); +	if (ret < 0)  		return ret; -	}  	ret = smsc95xx_read_reg(dev, BURST_CAP, &read_buf); -	if (ret < 0) { -		netdev_warn(dev->net, "Failed to read BURST_CAP: %d\n", ret); +	if (ret < 0)  		return ret; -	} +  	netif_dbg(dev, ifup, dev->net,  		  "Read Value from BURST_CAP after writing: 0x%08x\n",  		  read_buf); -	read_buf = DEFAULT_BULK_IN_DELAY; -	ret = smsc95xx_write_reg(dev, BULK_IN_DLY, read_buf); -	if (ret < 0) { -		netdev_warn(dev->net, "ret = %d\n", ret); +	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) { -		netdev_warn(dev->net, "Failed to read BULK_IN_DLY: %d\n", ret); +	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) { -		netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); +	if (ret < 0)  		return ret; -	} -	netif_dbg(dev, ifup, dev->net, -		  "Read Value from HW_CFG: 0x%08x\n", read_buf); + +	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_); @@ -901,106 +998,89 @@ static int smsc95xx_reset(struct usbnet *dev)  	read_buf |= NET_IP_ALIGN << 9;  	ret = smsc95xx_write_reg(dev, HW_CFG, read_buf); -	if (ret < 0) { -		netdev_warn(dev->net, "Failed to write HW_CFG register, ret=%d\n", -			    ret); +	if (ret < 0)  		return ret; -	}  	ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); -	if (ret < 0) { -		netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); +	if (ret < 0)  		return ret; -	} +  	netif_dbg(dev, ifup, dev->net,  		  "Read Value from HW_CFG after writing: 0x%08x\n", read_buf); -	write_buf = 0xFFFFFFFF; -	ret = smsc95xx_write_reg(dev, INT_STS, write_buf); -	if (ret < 0) { -		netdev_warn(dev->net, "Failed to write INT_STS register, ret=%d\n", -			    ret); +	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) { -		netdev_warn(dev->net, "Failed to read ID_REV: %d\n", ret); +	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) { -		netdev_warn(dev->net, "Failed to write LED_GPIO_CFG register, ret=%d\n", -			    ret); +	if (ret < 0)  		return ret; -	}  	/* Init Tx */ -	write_buf = 0; -	ret = smsc95xx_write_reg(dev, FLOW, write_buf); -	if (ret < 0) { -		netdev_warn(dev->net, "Failed to write FLOW: %d\n", ret); +	ret = smsc95xx_write_reg(dev, FLOW, 0); +	if (ret < 0)  		return ret; -	} -	read_buf = AFC_CFG_DEFAULT; -	ret = smsc95xx_write_reg(dev, AFC_CFG, read_buf); -	if (ret < 0) { -		netdev_warn(dev->net, "Failed to write AFC_CFG: %d\n", 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) { -		netdev_warn(dev->net, "Failed to read MAC_CR: %d\n", ret); +	if (ret < 0)  		return ret; -	}  	/* Init Rx */  	/* Set Vlan */ -	write_buf = (u32)ETH_P_8021Q; -	ret = smsc95xx_write_reg(dev, VLAN1, write_buf); -	if (ret < 0) { -		netdev_warn(dev->net, "Failed to write VAN1: %d\n", ret); +	ret = smsc95xx_write_reg(dev, VLAN1, (u32)ETH_P_8021Q); +	if (ret < 0)  		return ret; -	}  	/* Enable or disable checksum offload engines */ -	ethtool_op_set_tx_hw_csum(netdev, pdata->use_tx_csum); -	ret = smsc95xx_set_csums(dev); +	ret = smsc95xx_set_features(dev->net, dev->net->features);  	if (ret < 0) { -		netdev_warn(dev->net, "Failed to set csum offload: %d\n", ret); +		netdev_warn(dev->net, "Failed to set checksum offload features\n");  		return ret;  	}  	smsc95xx_set_multicast(dev->net); -	if (smsc95xx_phy_initialize(dev) < 0) -		return -EIO; - -	ret = smsc95xx_read_reg(dev, INT_EP_CTL, &read_buf); +	ret = smsc95xx_phy_initialize(dev);  	if (ret < 0) { -		netdev_warn(dev->net, "Failed to read INT_EP_CTL: %d\n", ret); +		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 write INT_EP_CTL: %d\n", ret); +		netdev_warn(dev->net, "Failed to start TX path\n");  		return ret;  	} -	smsc95xx_start_tx_path(dev); -	smsc95xx_start_rx_path(dev); +	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; @@ -1015,12 +1095,14 @@ static const struct net_device_ops smsc95xx_netdev_ops = {  	.ndo_set_mac_address 	= eth_mac_addr,  	.ndo_validate_addr	= eth_validate_addr,  	.ndo_do_ioctl 		= smsc95xx_ioctl, -	.ndo_set_multicast_list = smsc95xx_set_multicast, +	.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"); @@ -1032,28 +1114,45 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)  	}  	dev->data[0] = (unsigned long)kzalloc(sizeof(struct smsc95xx_priv), -		GFP_KERNEL); +					      GFP_KERNEL);  	pdata = (struct smsc95xx_priv *)(dev->data[0]); -	if (!pdata) { -		netdev_warn(dev->net, "Unable to allocate struct smsc95xx_priv\n"); +	if (!pdata)  		return -ENOMEM; -	}  	spin_lock_init(&pdata->mac_cr_lock); -	pdata->use_tx_csum = DEFAULT_TX_CSUM_ENABLE; -	pdata->use_rx_csum = DEFAULT_RX_CSUM_ENABLE; +	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; +	dev->net->hard_header_len += SMSC95XX_TX_OVERHEAD_CSUM; +	dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len;  	return 0;  } @@ -1068,6 +1167,565 @@ static void smsc95xx_unbind(struct usbnet *dev, struct usb_interface *intf)  	}  } +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 = (struct smsc95xx_priv *)(dev->data[0]); +	u8 suspend_flags = pdata->suspend_flags; +	int ret; +	u32 val; + +	BUG_ON(!dev); + +	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); @@ -1077,7 +1735,9 @@ static void smsc95xx_rx_csum_offload(struct sk_buff *skb)  static int smsc95xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  { -	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); +	/* 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; @@ -1120,7 +1780,7 @@ static int smsc95xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  			/* last frame in this batch */  			if (skb->len == size) { -				if (pdata->use_rx_csum) +				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); @@ -1138,7 +1798,7 @@ static int smsc95xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  			ax_skb->data = packet;  			skb_set_tail_pointer(ax_skb, size); -			if (pdata->use_rx_csum) +			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); @@ -1163,17 +1823,15 @@ static int smsc95xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  static u32 smsc95xx_calc_csum_preamble(struct sk_buff *skb)  { -	int len = skb->data - skb->head; -	u16 high_16 = (u16)(skb->csum_offset + skb->csum_start - len); -	u16 low_16 = (u16)(skb->csum_start - len); +	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)  { -	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); -	bool csum = pdata->use_tx_csum && (skb->ip_summed == CHECKSUM_PARTIAL); +	bool csum = skb->ip_summed == CHECKSUM_PARTIAL;  	int overhead = csum ? SMSC95XX_TX_OVERHEAD_CSUM : SMSC95XX_TX_OVERHEAD;  	u32 tx_cmd_a, tx_cmd_b; @@ -1193,7 +1851,7 @@ static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev,  		if (skb->len <= 45) {  			/* workaround - hardware tx checksum does not work  			 * properly with extremely small packets */ -			long csstart = skb->csum_start - skb_headroom(skb); +			long csstart = skb_checksum_start_offset(skb);  			__wsum calc = csum_partial(skb->data + csstart,  				skb->len - csstart, 0);  			*((__sum16 *)(skb->data + csstart @@ -1203,6 +1861,7 @@ static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev,  		} else {  			u32 csum_preamble = smsc95xx_calc_csum_preamble(skb);  			skb_push(skb, 4); +			cpu_to_le32s(&csum_preamble);  			memcpy(skb->data, &csum_preamble, 4);  		}  	} @@ -1223,6 +1882,26 @@ static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev,  	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, @@ -1232,7 +1911,8 @@ static const struct driver_info smsc95xx_info = {  	.rx_fixup	= smsc95xx_rx_fixup,  	.tx_fixup	= smsc95xx_tx_fixup,  	.status		= smsc95xx_status, -	.flags		= FLAG_ETHER | FLAG_SEND_ZLP, +	.manage_power	= smsc95xx_manage_power, +	.flags		= FLAG_ETHER | FLAG_SEND_ZLP | FLAG_LINK_INTR,  };  static const struct usb_device_id products[] = { @@ -1311,6 +1991,21 @@ static const struct usb_device_id products[] = {  		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); @@ -1319,24 +2014,17 @@ static struct usb_driver smsc95xx_driver = {  	.name		= "smsc95xx",  	.id_table	= products,  	.probe		= usbnet_probe, -	.suspend	= usbnet_suspend, -	.resume		= usbnet_resume, +	.suspend	= smsc95xx_suspend, +	.resume		= smsc95xx_resume, +	.reset_resume	= smsc95xx_reset_resume,  	.disconnect	= usbnet_disconnect, +	.disable_hub_initiated_lpm = 1, +	.supports_autosuspend = 1,  }; -static int __init smsc95xx_init(void) -{ -	return usb_register(&smsc95xx_driver); -} -module_init(smsc95xx_init); - -static void __exit smsc95xx_exit(void) -{ -	usb_deregister(&smsc95xx_driver); -} -module_exit(smsc95xx_exit); +module_usb_driver(smsc95xx_driver);  MODULE_AUTHOR("Nancy Lin"); -MODULE_AUTHOR("Steve Glendinning <steve.glendinning@smsc.com>"); +MODULE_AUTHOR("Steve Glendinning <steve.glendinning@shawell.net>");  MODULE_DESCRIPTION("SMSC95XX USB 2.0 Ethernet Devices");  MODULE_LICENSE("GPL"); diff --git a/drivers/net/usb/smsc95xx.h b/drivers/net/usb/smsc95xx.h index 86bc44977fb..526faa0c44e 100644 --- a/drivers/net/usb/smsc95xx.h +++ b/drivers/net/usb/smsc95xx.h @@ -13,8 +13,7 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. + * along with this program; if not, see <http://www.gnu.org/licenses/>.   *   *****************************************************************************/ @@ -53,6 +52,11 @@  #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) @@ -63,6 +67,7 @@  #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) @@ -83,12 +88,16 @@  #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		(0x00000060) +#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) @@ -198,8 +207,16 @@  #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) @@ -208,6 +225,23 @@  /* 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) diff --git a/drivers/net/usb/sr9700.c b/drivers/net/usb/sr9700.c new file mode 100644 index 00000000000..99b69af1427 --- /dev/null +++ b/drivers/net/usb/sr9700.c @@ -0,0 +1,559 @@ +/* + * CoreChip-sz SR9700 one chip USB 1.1 Ethernet Devices + * + * Author : Liu Junliang <liujunliang_ljl@163.com> + * + * 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 <linux/module.h> +#include <linux/sched.h> +#include <linux/stddef.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/ethtool.h> +#include <linux/mii.h> +#include <linux/usb.h> +#include <linux/crc32.h> +#include <linux/usb/usbnet.h> + +#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, 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, EPAR, phy ? (reg | EPAR_PHY_ADR) : reg); +	sr_write_reg(dev, 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, EPCR, 0x0); +	ret = sr_read(dev, 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, EPDR, 2, &value); +	if (ret < 0) +		goto out_unlock; + +	sr_write_reg(dev, EPAR, phy ? (reg | EPAR_PHY_ADR) : reg); +	sr_write_reg(dev, 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, 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, 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, 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, MAR, SR_MCAST_SIZE, hashes); +	sr_write_reg_async(dev, 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, 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, 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, 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, PRR, PRR_PHY_RST); +	/* at least 10ms, here 20ms for safe */ +	mdelay(20); +	sr_write_reg(dev, 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 <liujunliang_ljl@163.com>"); +MODULE_DESCRIPTION("SR9700 one chip USB 1.1 USB to Ethernet device from http://www.corechip-sz.com/"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/usb/sr9700.h b/drivers/net/usb/sr9700.h new file mode 100644 index 00000000000..fd687c575e7 --- /dev/null +++ b/drivers/net/usb/sr9700.h @@ -0,0 +1,173 @@ +/* + * CoreChip-sz SR9700 one chip USB 1.1 Ethernet Devices + * + * Author : Liu Junliang <liujunliang_ljl@163.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 _SR9700_H +#define	_SR9700_H + +/* sr9700 spec. register table on Linux platform */ + +/* Network Control Reg */ +#define	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	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	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	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	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	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	RSR			0x06 +#define		RSR_AE			(1 << 2) +#define		RSR_MF			(1 << 6) +#define		RSR_RF			(1 << 7) +/* Rx Overflow Counter Reg */ +#define	ROCR		0x07 +#define		ROCR_ROC		(0x7F << 0) +#define		ROCR_RXFU		(1 << 7) +/* Back Pressure Threshold Reg */ +#define	BPTR		0x08 +#define		BPTR_JPT		(0x0F << 0) +#define		BPTR_BPHW		(0x0F << 4) +/* Flow Control Threshold Reg */ +#define	FCTR		0x09 +#define		FCTR_LWOT		(0x0F << 0) +#define		FCTR_HWOT		(0x0F << 4) +/* rx/tx Flow Control Reg */ +#define	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	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	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	EPDR		0x0D	/* 0x0D ~ 0x0E for Data Reg Low & High */ +/* Wakeup Control Reg */ +#define	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	PAR			0x10	/* 0x10 ~ 0x15 6 bytes for PAR */ +/* Multicast Address Reg */ +#define	MAR			0x16	/* 0x16 ~ 0x1D 8 bytes for MAR */ +/* 0x1e unused */ +/* Phy Reset Reg */ +#define	PRR			0x1F +#define		PRR_PHY_RST		(1 << 0) +/* Tx sdram Write Pointer Address Low */ +#define	TWPAL		0x20 +/* Tx sdram Write Pointer Address High */ +#define	TWPAH		0x21 +/* Tx sdram Read Pointer Address Low */ +#define	TRPAL		0x22 +/* Tx sdram Read Pointer Address High */ +#define	TRPAH		0x23 +/* Rx sdram Write Pointer Address Low */ +#define	RWPAL		0x24 +/* Rx sdram Write Pointer Address High */ +#define	RWPAH		0x25 +/* Rx sdram Read Pointer Address Low */ +#define	RRPAL		0x26 +/* Rx sdram Read Pointer Address High */ +#define	RRPAH		0x27 +/* Vendor ID register */ +#define	VID			0x28	/* 0x28 ~ 0x29 2 bytes for VID */ +/* Product ID register */ +#define	PID			0x2A	/* 0x2A ~ 0x2B 2 bytes for PID */ +/* CHIP Revision register */ +#define	CHIPR		0x2C +/* 0x2D --> 0xEF unused */ +/* USB Device Address */ +#define	USBDA		0xF0 +#define		USBDA_USBFA		(0x7F << 0) +/* RX packet Counter Reg */ +#define	RXC			0xF1 +/* Tx packet Counter & USB Status Reg */ +#define	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	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 --git a/drivers/net/usb/sr9800.c b/drivers/net/usb/sr9800.c new file mode 100644 index 00000000000..b94a0fbb8b3 --- /dev/null +++ b/drivers/net/usb/sr9800.c @@ -0,0 +1,874 @@ +/* CoreChip-sz SR9800 one chip USB 2.0 Ethernet Devices + * + * Author : Liu Junliang <liujunliang_ljl@163.com> + * + * 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 <linux/module.h> +#include <linux/kmod.h> +#include <linux/init.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/ethtool.h> +#include <linux/workqueue.h> +#include <linux/mii.h> +#include <linux/usb.h> +#include <linux/crc32.h> +#include <linux/usb/usbnet.h> +#include <linux/slab.h> +#include <linux/if_vlan.h> + +#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)); +	} + +	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 <liujunliang_ljl@163.com"); +MODULE_VERSION(DRIVER_VERSION); +MODULE_DESCRIPTION("SR9800 USB 2.0 USB2NET Dev : http://www.corechip-sz.com"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/usb/sr9800.h b/drivers/net/usb/sr9800.h new file mode 100644 index 00000000000..18f67025127 --- /dev/null +++ b/drivers/net/usb/sr9800.h @@ -0,0 +1,202 @@ +/* CoreChip-sz SR9800 one chip USB 2.0 Ethernet Devices + * + * Author : Liu Junliang <liujunliang_ljl@163.com> + * + * 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 --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index cff74b81a7d..f9e96c42755 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -14,8 +14,7 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  /* @@ -59,15 +58,13 @@   * For high speed, each frame comfortably fits almost 36 max size   * Ethernet packets (so queues should be bigger).   * - * REVISIT qlens should be members of 'struct usbnet'; the goal is to - * let the USB host controller be busy for 5msec or more before an irq - * is required, under load.  Jumbograms change the equation. + * The goal is to let the USB host controller be busy for 5msec or + * more before an irq is required, under load.  Jumbograms change + * the equation.   */ -#define RX_MAX_QUEUE_MEMORY (60 * 1518) -#define	RX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? \ -			(RX_MAX_QUEUE_MEMORY/(dev)->rx_urb_size) : 4) -#define	TX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? \ -			(RX_MAX_QUEUE_MEMORY/(dev)->hard_mtu) : 4) +#define	MAX_QUEUE_MEMORY	(60 * 1518) +#define	RX_QLEN(dev)		((dev)->rx_qlen) +#define	TX_QLEN(dev)		((dev)->tx_qlen)  // reawaken network queue this soon after stopping; else watchdog barks  #define TX_TIMEOUT_JIFFIES	(5*HZ) @@ -109,7 +106,7 @@ int usbnet_get_endpoints(struct usbnet *dev, struct usb_interface *intf)  		/* take the first altsetting with in-bulk + out-bulk;  		 * remember any status endpoint, just in case; -		 * ignore other endpoints and altsetttings. +		 * ignore other endpoints and altsettings.  		 */  		for (ep = 0; ep < alt->desc.bNumEndpoints; ep++) {  			struct usb_host_endpoint	*e; @@ -180,7 +177,37 @@ int usbnet_get_ethernet_addr(struct usbnet *dev, int iMACAddress)  }  EXPORT_SYMBOL_GPL(usbnet_get_ethernet_addr); -static void intr_complete (struct urb *urb); +static void intr_complete (struct urb *urb) +{ +	struct usbnet	*dev = urb->context; +	int		status = urb->status; + +	switch (status) { +	/* success */ +	case 0: +		dev->driver_info->status(dev, urb); +		break; + +	/* software-driven interface shutdown */ +	case -ENOENT:		/* urb killed */ +	case -ESHUTDOWN:	/* hardware gone */ +		netif_dbg(dev, ifdown, dev->net, +			  "intr shutdown, code %d\n", status); +		return; + +	/* NOTE:  not throttling like RX/TX, since this endpoint +	 * already polls infrequently +	 */ +	default: +		netdev_dbg(dev->net, "intr status %d\n", status); +		break; +	} + +	status = usb_submit_urb (urb, GFP_ATOMIC); +	if (status != 0) +		netif_err(dev, timer, dev->net, +			  "intr resubmit --> %d\n", status); +}  static int init_status (struct usbnet *dev, struct usb_interface *intf)  { @@ -210,6 +237,7 @@ static int init_status (struct usbnet *dev, struct usb_interface *intf)  		} else {  			usb_fill_int_urb(dev->interrupt, dev->udev, pipe,  				buf, maxp, intr_complete, dev, period); +			dev->interrupt->transfer_flags |= URB_FREE_BUFFER;  			dev_dbg(&intf->dev,  				"status ep%din, %d bytes period %d\n",  				usb_pipeendpoint(pipe), maxp, period); @@ -218,6 +246,70 @@ static int init_status (struct usbnet *dev, struct usb_interface *intf)  	return 0;  } +/* Submit the interrupt URB if not previously submitted, increasing refcount */ +int usbnet_status_start(struct usbnet *dev, gfp_t mem_flags) +{ +	int ret = 0; + +	WARN_ON_ONCE(dev->interrupt == NULL); +	if (dev->interrupt) { +		mutex_lock(&dev->interrupt_mutex); + +		if (++dev->interrupt_count == 1) +			ret = usb_submit_urb(dev->interrupt, mem_flags); + +		dev_dbg(&dev->udev->dev, "incremented interrupt URB count to %d\n", +			dev->interrupt_count); +		mutex_unlock(&dev->interrupt_mutex); +	} +	return ret; +} +EXPORT_SYMBOL_GPL(usbnet_status_start); + +/* For resume; submit interrupt URB if previously submitted */ +static int __usbnet_status_start_force(struct usbnet *dev, gfp_t mem_flags) +{ +	int ret = 0; + +	mutex_lock(&dev->interrupt_mutex); +	if (dev->interrupt_count) { +		ret = usb_submit_urb(dev->interrupt, mem_flags); +		dev_dbg(&dev->udev->dev, +			"submitted interrupt URB for resume\n"); +	} +	mutex_unlock(&dev->interrupt_mutex); +	return ret; +} + +/* Kill the interrupt URB if all submitters want it killed */ +void usbnet_status_stop(struct usbnet *dev) +{ +	if (dev->interrupt) { +		mutex_lock(&dev->interrupt_mutex); +		WARN_ON(dev->interrupt_count == 0); + +		if (dev->interrupt_count && --dev->interrupt_count == 0) +			usb_kill_urb(dev->interrupt); + +		dev_dbg(&dev->udev->dev, +			"decremented interrupt URB count to %d\n", +			dev->interrupt_count); +		mutex_unlock(&dev->interrupt_mutex); +	} +} +EXPORT_SYMBOL_GPL(usbnet_status_stop); + +/* For suspend; always kill interrupt URB */ +static void __usbnet_status_stop_force(struct usbnet *dev) +{ +	if (dev->interrupt) { +		mutex_lock(&dev->interrupt_mutex); +		usb_kill_urb(dev->interrupt); +		dev_dbg(&dev->udev->dev, "killed interrupt URB for suspend\n"); +		mutex_unlock(&dev->interrupt_mutex); +	} +} +  /* Passes this packet up the stack, updating its accounting.   * Some link protocols batch packets, so their rx_fixup paths   * can return clones as well as just modify the original skb. @@ -238,6 +330,10 @@ void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb)  	netif_dbg(dev, rx_status, dev->net, "< rx, len %zu, type 0x%x\n",  		  skb->len + sizeof (struct ethhdr), skb->protocol);  	memset (skb->cb, 0, sizeof (struct skb_data)); + +	if (skb_defer_rx_timestamp(skb)) +		return; +  	status = netif_rx (skb);  	if (status != NET_RX_SUCCESS)  		netif_dbg(dev, rx_err, dev->net, @@ -245,6 +341,31 @@ void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb)  }  EXPORT_SYMBOL_GPL(usbnet_skb_return); +/* must be called if hard_mtu or rx_urb_size changed */ +void usbnet_update_max_qlen(struct usbnet *dev) +{ +	enum usb_device_speed speed = dev->udev->speed; + +	switch (speed) { +	case USB_SPEED_HIGH: +		dev->rx_qlen = MAX_QUEUE_MEMORY / dev->rx_urb_size; +		dev->tx_qlen = MAX_QUEUE_MEMORY / dev->hard_mtu; +		break; +	case USB_SPEED_SUPER: +		/* +		 * Not take default 5ms qlen for super speed HC to +		 * save memory, and iperf tests show 2.5ms qlen can +		 * work well +		 */ +		dev->rx_qlen = 5 * MAX_QUEUE_MEMORY / dev->rx_urb_size; +		dev->tx_qlen = 5 * MAX_QUEUE_MEMORY / dev->hard_mtu; +		break; +	default: +		dev->rx_qlen = dev->tx_qlen = 4; +	} +} +EXPORT_SYMBOL_GPL(usbnet_update_max_qlen); +  /*-------------------------------------------------------------------------   * @@ -273,21 +394,39 @@ int usbnet_change_mtu (struct net_device *net, int new_mtu)  			usbnet_unlink_rx_urbs(dev);  	} +	/* max qlen depend on hard_mtu and rx_urb_size */ +	usbnet_update_max_qlen(dev); +  	return 0;  }  EXPORT_SYMBOL_GPL(usbnet_change_mtu); +/* The caller must hold list->lock */ +static void __usbnet_queue_skb(struct sk_buff_head *list, +			struct sk_buff *newsk, enum skb_state state) +{ +	struct skb_data *entry = (struct skb_data *) newsk->cb; + +	__skb_queue_tail(list, newsk); +	entry->state = state; +} +  /*-------------------------------------------------------------------------*/  /* some LK 2.4 HCDs oopsed if we freed or resubmitted urbs from   * completion callbacks.  2.5 should have fixed those bugs...   */ -static void defer_bh(struct usbnet *dev, struct sk_buff *skb, struct sk_buff_head *list) +static enum skb_state defer_bh(struct usbnet *dev, struct sk_buff *skb, +		struct sk_buff_head *list, enum skb_state state)  {  	unsigned long		flags; +	enum skb_state 		old_state; +	struct skb_data *entry = (struct skb_data *) skb->cb;  	spin_lock_irqsave(&list->lock, flags); +	old_state = entry->state; +	entry->state = state;  	__skb_unlink(skb, list);  	spin_unlock(&list->lock);  	spin_lock(&dev->done.lock); @@ -295,6 +434,7 @@ static void defer_bh(struct usbnet *dev, struct sk_buff *skb, struct sk_buff_hea  	if (dev->done.qlen == 1)  		tasklet_schedule(&dev->bh);  	spin_unlock_irqrestore(&dev->done.lock, flags); +	return old_state;  }  /* some work can't be done in tasklets, so we use keventd @@ -305,10 +445,12 @@ static void defer_bh(struct usbnet *dev, struct sk_buff *skb, struct sk_buff_hea  void usbnet_defer_kevent (struct usbnet *dev, int work)  {  	set_bit (work, &dev->flags); -	if (!schedule_work (&dev->kevent)) -		netdev_err(dev->net, "kevent %d may have been dropped\n", work); -	else +	if (!schedule_work (&dev->kevent)) { +		if (net_ratelimit()) +			netdev_err(dev->net, "kevent %d may have been dropped\n", work); +	} else {  		netdev_dbg(dev->net, "kevent %d scheduled\n", work); +	}  }  EXPORT_SYMBOL_GPL(usbnet_defer_kevent); @@ -324,18 +466,23 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)  	unsigned long		lockflags;  	size_t			size = dev->rx_urb_size; -	if ((skb = alloc_skb (size + NET_IP_ALIGN, flags)) == NULL) { +	/* prevent rx skb allocation when error ratio is high */ +	if (test_bit(EVENT_RX_KILL, &dev->flags)) { +		usb_free_urb(urb); +		return -ENOLINK; +	} + +	skb = __netdev_alloc_skb_ip_align(dev->net, size, flags); +	if (!skb) {  		netif_dbg(dev, rx_err, dev->net, "no rx skb\n");  		usbnet_defer_kevent (dev, EVENT_RX_MEMORY);  		usb_free_urb (urb);  		return -ENOMEM;  	} -	skb_reserve (skb, NET_IP_ALIGN);  	entry = (struct skb_data *) skb->cb;  	entry->urb = urb;  	entry->dev = dev; -	entry->state = rx_start;  	entry->length = 0;  	usb_fill_bulk_urb (urb, dev->udev, dev->in, @@ -367,7 +514,7 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)  			tasklet_schedule (&dev->bh);  			break;  		case 0: -			__skb_queue_tail (&dev->rxq, skb); +			__usbnet_queue_skb(&dev->rxq, skb, rx_start);  		}  	} else {  		netif_dbg(dev, ifdown, dev->net, "rx: stopped\n"); @@ -387,22 +534,28 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)  static inline void rx_process (struct usbnet *dev, struct sk_buff *skb)  {  	if (dev->driver_info->rx_fixup && -	    !dev->driver_info->rx_fixup (dev, skb)) -		goto error; +	    !dev->driver_info->rx_fixup (dev, skb)) { +		/* With RX_ASSEMBLE, rx_fixup() must update counters */ +		if (!(dev->driver_info->flags & FLAG_RX_ASSEMBLE)) +			dev->net->stats.rx_errors++; +		goto done; +	}  	// else network stack removes extra byte if we forced a short packet -	if (skb->len) { -		/* all data was already cloned from skb inside the driver */ -		if (dev->driver_info->flags & FLAG_MULTI_PACKET) -			dev_kfree_skb_any(skb); -		else -			usbnet_skb_return(dev, skb); +	/* all data was already cloned from skb inside the driver */ +	if (dev->driver_info->flags & FLAG_MULTI_PACKET) +		goto done; + +	if (skb->len < ETH_HLEN) { +		dev->net->stats.rx_errors++; +		dev->net->stats.rx_length_errors++; +		netif_dbg(dev, rx_err, dev->net, "rx length %d\n", skb->len); +	} else { +		usbnet_skb_return(dev, skb);  		return;  	} -	netif_dbg(dev, rx_err, dev->net, "drop\n"); -error: -	dev->net->stats.rx_errors++; +done:  	skb_queue_tail(&dev->done, skb);  } @@ -414,21 +567,15 @@ static void rx_complete (struct urb *urb)  	struct skb_data		*entry = (struct skb_data *) skb->cb;  	struct usbnet		*dev = entry->dev;  	int			urb_status = urb->status; +	enum skb_state		state;  	skb_put (skb, urb->actual_length); -	entry->state = rx_done; +	state = rx_done;  	entry->urb = NULL;  	switch (urb_status) {  	/* success */  	case 0: -		if (skb->len < dev->net->hard_header_len) { -			entry->state = rx_cleanup; -			dev->net->stats.rx_errors++; -			dev->net->stats.rx_length_errors++; -			netif_dbg(dev, rx_err, dev->net, -				  "rx length %d\n", skb->len); -		}  		break;  	/* stalls need manual reset. this is rare ... except that @@ -462,7 +609,7 @@ static void rx_complete (struct urb *urb)  				  "rx throttle %d\n", urb_status);  		}  block: -		entry->state = rx_cleanup; +		state = rx_cleanup;  		entry->urb = urb;  		urb = NULL;  		break; @@ -473,18 +620,31 @@ block:  		// FALLTHROUGH  	default: -		entry->state = rx_cleanup; +		state = rx_cleanup;  		dev->net->stats.rx_errors++;  		netif_dbg(dev, rx_err, dev->net, "rx status %d\n", urb_status);  		break;  	} -	defer_bh(dev, skb, &dev->rxq); +	/* stop rx if packet error rate is high */ +	if (++dev->pkt_cnt > 30) { +		dev->pkt_cnt = 0; +		dev->pkt_err = 0; +	} else { +		if (state == rx_cleanup) +			dev->pkt_err++; +		if (dev->pkt_err > 20) +			set_bit(EVENT_RX_KILL, &dev->flags); +	} + +	state = defer_bh(dev, skb, &dev->rxq, state);  	if (urb) {  		if (netif_running (dev->net) && -		    !test_bit (EVENT_RX_HALT, &dev->flags)) { +		    !test_bit (EVENT_RX_HALT, &dev->flags) && +		    state != unlink_start) {  			rx_submit (dev, urb, GFP_ATOMIC); +			usb_mark_last_busy(dev->udev);  			return;  		}  		usb_free_urb (urb); @@ -492,42 +652,6 @@ block:  	netif_dbg(dev, rx_err, dev->net, "no read resubmitted\n");  } -static void intr_complete (struct urb *urb) -{ -	struct usbnet	*dev = urb->context; -	int		status = urb->status; - -	switch (status) { -	/* success */ -	case 0: -		dev->driver_info->status(dev, urb); -		break; - -	/* software-driven interface shutdown */ -	case -ENOENT:		/* urb killed */ -	case -ESHUTDOWN:	/* hardware gone */ -		netif_dbg(dev, ifdown, dev->net, -			  "intr shutdown, code %d\n", status); -		return; - -	/* NOTE:  not throttling like RX/TX, since this endpoint -	 * already polls infrequently -	 */ -	default: -		netdev_dbg(dev->net, "intr status %d\n", status); -		break; -	} - -	if (!netif_running (dev->net)) -		return; - -	memset(urb->transfer_buffer, 0, urb->transfer_buffer_length); -	status = usb_submit_urb (urb, GFP_ATOMIC); -	if (status != 0) -		netif_err(dev, timer, dev->net, -			  "intr resubmit --> %d\n", status); -} -  /*-------------------------------------------------------------------------*/  void usbnet_pause_rx(struct usbnet *dev)  { @@ -569,18 +693,34 @@ EXPORT_SYMBOL_GPL(usbnet_purge_paused_rxq);  static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)  {  	unsigned long		flags; -	struct sk_buff		*skb, *skbnext; +	struct sk_buff		*skb;  	int			count = 0;  	spin_lock_irqsave (&q->lock, flags); -	skb_queue_walk_safe(q, skb, skbnext) { +	while (!skb_queue_empty(q)) {  		struct skb_data		*entry;  		struct urb		*urb;  		int			retval; -		entry = (struct skb_data *) skb->cb; +		skb_queue_walk(q, skb) { +			entry = (struct skb_data *) skb->cb; +			if (entry->state != unlink_start) +				goto found; +		} +		break; +found: +		entry->state = unlink_start;  		urb = entry->urb; +		/* +		 * Get reference count of the URB to avoid it to be +		 * freed during usb_unlink_urb, which may trigger +		 * use-after-free problem inside usb_unlink_urb since +		 * usb_unlink_urb is always racing with .complete +		 * handler(include defer_bh). +		 */ +		usb_get_urb(urb); +		spin_unlock_irqrestore(&q->lock, flags);  		// during some PM-driven resume scenarios,  		// these (async) unlinks complete immediately  		retval = usb_unlink_urb (urb); @@ -588,6 +728,8 @@ static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)  			netdev_dbg(dev->net, "unlink urb err, %d\n", retval);  		else  			count++; +		usb_put_urb(urb); +		spin_lock_irqsave(&q->lock, flags);  	}  	spin_unlock_irqrestore (&q->lock, flags);  	return count; @@ -610,14 +752,12 @@ EXPORT_SYMBOL_GPL(usbnet_unlink_rx_urbs);  // precondition: never called in_interrupt  static void usbnet_terminate_urbs(struct usbnet *dev)  { -	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(unlink_wakeup);  	DECLARE_WAITQUEUE(wait, current);  	int temp;  	/* ensure there are no more active urbs */ -	add_wait_queue(&unlink_wakeup, &wait); +	add_wait_queue(&dev->wait, &wait);  	set_current_state(TASK_UNINTERRUPTIBLE); -	dev->wait = &unlink_wakeup;  	temp = unlink_urbs(dev, &dev->txq) +  		unlink_urbs(dev, &dev->rxq); @@ -631,16 +771,16 @@ static void usbnet_terminate_urbs(struct usbnet *dev)  				  "waited for %d urb completions\n", temp);  	}  	set_current_state(TASK_RUNNING); -	dev->wait = NULL; -	remove_wait_queue(&unlink_wakeup, &wait); +	remove_wait_queue(&dev->wait, &wait);  }  int usbnet_stop (struct net_device *net)  {  	struct usbnet		*dev = netdev_priv(net);  	struct driver_info	*info = dev->driver_info; -	int			retval; +	int			retval, pm; +	clear_bit(EVENT_DEV_OPEN, &dev->flags);  	netif_stop_queue (net);  	netif_info(dev, ifdown, dev->net, @@ -648,6 +788,8 @@ int usbnet_stop (struct net_device *net)  		   net->stats.rx_packets, net->stats.tx_packets,  		   net->stats.rx_errors, net->stats.tx_errors); +	/* to not race resume */ +	pm = usb_autopm_get_interface(dev->intf);  	/* allow minidriver to stop correctly (wireless devices to turn off  	 * radio etc) */  	if (info->stop) { @@ -663,7 +805,7 @@ int usbnet_stop (struct net_device *net)  	if (!(info->flags & FLAG_AVOID_UNLINK_URBS))  		usbnet_terminate_urbs(dev); -	usb_kill_urb(dev->interrupt); +	usbnet_status_stop(dev);  	usbnet_purge_paused_rxq(dev); @@ -674,7 +816,11 @@ int usbnet_stop (struct net_device *net)  	dev->flags = 0;  	del_timer_sync (&dev->delay);  	tasklet_kill (&dev->bh); -	if (info->manage_power) +	if (!pm) +		usb_autopm_put_interface(dev->intf); + +	if (info->manage_power && +	    !test_and_clear_bit(EVENT_NO_RUNTIME_PM, &dev->flags))  		info->manage_power(dev, 0);  	else  		usb_autopm_put_interface(dev->intf); @@ -716,6 +862,9 @@ int usbnet_open (struct net_device *net)  		goto done;  	} +	/* hard_mtu or rx_urb_size may change in reset() */ +	usbnet_update_max_qlen(dev); +  	// insist peer be connected  	if (info->check_connect && (retval = info->check_connect (dev)) < 0) {  		netif_dbg(dev, ifup, dev->net, "can't open; %d\n", retval); @@ -724,7 +873,7 @@ int usbnet_open (struct net_device *net)  	/* start any status interrupt transfer */  	if (dev->interrupt) { -		retval = usb_submit_urb (dev->interrupt, GFP_KERNEL); +		retval = usbnet_status_start(dev, GFP_KERNEL);  		if (retval < 0) {  			netif_err(dev, ifup, dev->net,  				  "intr submit %d\n", retval); @@ -732,6 +881,7 @@ int usbnet_open (struct net_device *net)  		}  	} +	set_bit(EVENT_DEV_OPEN, &dev->flags);  	netif_start_queue (net);  	netif_info(dev, ifup, dev->net,  		   "open: enable queueing (rx %d, tx %d) mtu %d %s framing\n", @@ -744,16 +894,23 @@ int usbnet_open (struct net_device *net)  		   (dev->driver_info->flags & FLAG_FRAMING_AX) ? "ASIX" :  		   "simple"); +	/* reset rx error state */ +	dev->pkt_cnt = 0; +	dev->pkt_err = 0; +	clear_bit(EVENT_RX_KILL, &dev->flags); +  	// delay posting reads until we're fully open  	tasklet_schedule (&dev->bh);  	if (info->manage_power) {  		retval = info->manage_power(dev, 1); -		if (retval < 0) -			goto done; -		usb_autopm_put_interface(dev->intf); +		if (retval < 0) { +			retval = 0; +			set_bit(EVENT_NO_RUNTIME_PM, &dev->flags); +		} else { +			usb_autopm_put_interface(dev->intf); +		}  	}  	return retval; -  done:  	usb_autopm_put_interface(dev->intf);  done_nopm: @@ -792,6 +949,9 @@ int usbnet_set_settings (struct net_device *net, struct ethtool_cmd *cmd)  	if (dev->driver_info->link_reset)  		dev->driver_info->link_reset(dev); +	/* hard_mtu or rx_urb_size may change in link_reset() */ +	usbnet_update_max_qlen(dev); +  	return retval;  } @@ -829,9 +989,9 @@ void usbnet_get_drvinfo (struct net_device *net, struct ethtool_drvinfo *info)  {  	struct usbnet *dev = netdev_priv(net); -	strncpy (info->driver, dev->driver_name, sizeof info->driver); -	strncpy (info->version, DRIVER_VERSION, sizeof info->version); -	strncpy (info->fw_version, dev->driver_info->description, +	strlcpy (info->driver, dev->driver_name, sizeof info->driver); +	strlcpy (info->version, DRIVER_VERSION, sizeof info->version); +	strlcpy (info->fw_version, dev->driver_info->description,  		sizeof info->fw_version);  	usb_make_path (dev->udev, info->bus_info, sizeof info->bus_info);  } @@ -862,10 +1022,35 @@ static const struct ethtool_ops usbnet_ethtool_ops = {  	.get_drvinfo		= usbnet_get_drvinfo,  	.get_msglevel		= usbnet_get_msglevel,  	.set_msglevel		= usbnet_set_msglevel, +	.get_ts_info		= ethtool_op_get_ts_info,  };  /*-------------------------------------------------------------------------*/ +static void __handle_link_change(struct usbnet *dev) +{ +	if (!test_bit(EVENT_DEV_OPEN, &dev->flags)) +		return; + +	if (!netif_carrier_ok(dev->net)) { +		/* kill URBs for reading packets to save bus bandwidth */ +		unlink_urbs(dev, &dev->rxq); + +		/* +		 * tx_timeout will unlink URBs for sending packets and +		 * tx queue is stopped by netcore after link becomes off +		 */ +	} else { +		/* submitting URBs for reading packets */ +		tasklet_schedule(&dev->bh); +	} + +	/* hard_mtu or rx_urb_size may change during link change */ +	usbnet_update_max_qlen(dev); + +	clear_bit(EVENT_LINK_CHANGE, &dev->flags); +} +  /* work that cannot be done in interrupt context uses keventd.   *   * NOTE:  with 2.5 we could do more of this using completion callbacks, @@ -931,8 +1116,10 @@ fail_halt:  		if (urb != NULL) {  			clear_bit (EVENT_RX_MEMORY, &dev->flags);  			status = usb_autopm_get_interface(dev->intf); -			if (status < 0) +			if (status < 0) { +				usb_free_urb(urb);  				goto fail_lowmem; +			}  			if (rx_submit (dev, urb, GFP_KERNEL) == -ENOLINK)  				resched = 0;  			usb_autopm_put_interface(dev->intf); @@ -961,8 +1148,14 @@ skip_reset:  		} else {  			usb_autopm_put_interface(dev->intf);  		} + +		/* handle link change from link resetting */ +		__handle_link_change(dev);  	} +	if (test_bit (EVENT_LINK_CHANGE, &dev->flags)) +		__handle_link_change(dev); +  	if (dev->flags)  		netdev_dbg(dev->net, "kevent done, flags = 0x%lx\n", dev->flags);  } @@ -1014,9 +1207,7 @@ static void tx_complete (struct urb *urb)  	}  	usb_autopm_put_interface_async(dev->intf); -	urb->dev = NULL; -	entry->state = tx_done; -	defer_bh(dev, skb, &dev->txq); +	(void) defer_bh(dev, skb, &dev->txq, tx_done);  }  /*-------------------------------------------------------------------------*/ @@ -1034,6 +1225,39 @@ EXPORT_SYMBOL_GPL(usbnet_tx_timeout);  /*-------------------------------------------------------------------------*/ +static int build_dma_sg(const struct sk_buff *skb, struct urb *urb) +{ +	unsigned num_sgs, total_len = 0; +	int i, s = 0; + +	num_sgs = skb_shinfo(skb)->nr_frags + 1; +	if (num_sgs == 1) +		return 0; + +	/* reserve one for zero packet */ +	urb->sg = kmalloc((num_sgs + 1) * sizeof(struct scatterlist), +			  GFP_ATOMIC); +	if (!urb->sg) +		return -ENOMEM; + +	urb->num_sgs = num_sgs; +	sg_init_table(urb->sg, urb->num_sgs + 1); + +	sg_set_buf(&urb->sg[s++], skb->data, skb_headlen(skb)); +	total_len += skb_headlen(skb); + +	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { +		struct skb_frag_struct *f = &skb_shinfo(skb)->frags[i]; + +		total_len += skb_frag_size(f); +		sg_set_page(&urb->sg[i + s], f->page.p, f->size, +				f->page_offset); +	} +	urb->transfer_buffer_length = total_len; + +	return 1; +} +  netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,  				     struct net_device *net)  { @@ -1045,21 +1269,21 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,  	unsigned long		flags;  	int retval; +	if (skb) +		skb_tx_timestamp(skb); +  	// some devices want funky USB-level framing, for  	// win32 driver (usually) and/or hardware quirks  	if (info->tx_fixup) {  		skb = info->tx_fixup (dev, skb, GFP_ATOMIC);  		if (!skb) { -			if (netif_msg_tx_err(dev)) { -				netif_dbg(dev, tx_err, dev->net, "can't tx_fixup skb\n"); -				goto drop; -			} else { -				/* cdc_ncm collected packet; waits for more */ +			/* packet collected; minidriver waiting for more */ +			if (info->flags & FLAG_MULTI_PACKET)  				goto not_drop; -			} +			netif_dbg(dev, tx_err, dev->net, "can't tx_fixup skb\n"); +			goto drop;  		}  	} -	length = skb->len;  	if (!(urb = usb_alloc_urb (0, GFP_ATOMIC))) {  		netif_dbg(dev, tx_err, dev->net, "no urb\n"); @@ -1069,11 +1293,14 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,  	entry = (struct skb_data *) skb->cb;  	entry->urb = urb;  	entry->dev = dev; -	entry->state = tx_start; -	entry->length = length;  	usb_fill_bulk_urb (urb, dev->udev, dev->out,  			skb->data, skb->len, tx_complete, skb); +	if (dev->can_dma_sg) { +		if (build_dma_sg(skb, urb) < 0) +			goto drop; +	} +	length = urb->transfer_buffer_length;  	/* don't assume the hardware handles USB_ZERO_PACKET  	 * NOTE:  strictly conforming cdc-ether devices should expect @@ -1085,15 +1312,18 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,  	if (length % dev->maxpacket == 0) {  		if (!(info->flags & FLAG_SEND_ZLP)) {  			if (!(info->flags & FLAG_MULTI_PACKET)) { -				urb->transfer_buffer_length++; -				if (skb_tailroom(skb)) { +				length++; +				if (skb_tailroom(skb) && !urb->num_sgs) {  					skb->data[skb->len] = 0;  					__skb_put(skb, 1); -				} +				} else if (urb->num_sgs) +					sg_set_buf(&urb->sg[urb->num_sgs++], +							dev->padding_pkt, 1);  			}  		} else  			urb->transfer_flags |= URB_ZERO_PACKET;  	} +	entry->length = urb->transfer_buffer_length = length;  	spin_lock_irqsave(&dev->txq.lock, flags);  	retval = usb_autopm_get_interface_async(dev->intf); @@ -1109,6 +1339,7 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,  		usb_anchor_urb(urb, &dev->deferred);  		/* no use to process more packets */  		netif_stop_queue(net); +		usb_put_urb(urb);  		spin_unlock_irqrestore(&dev->txq.lock, flags);  		netdev_dbg(dev->net, "Delaying transmission for resumption\n");  		goto deferred; @@ -1128,7 +1359,7 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,  		break;  	case 0:  		net->trans_start = jiffies; -		__skb_queue_tail (&dev->txq, skb); +		__usbnet_queue_skb(&dev->txq, skb, tx_start);  		if (dev->txq.qlen >= TX_QLEN (dev))  			netif_stop_queue (net);  	} @@ -1141,7 +1372,10 @@ drop:  not_drop:  		if (skb)  			dev_kfree_skb_any (skb); -		usb_free_urb (urb); +		if (urb) { +			kfree(urb->sg); +			usb_free_urb(urb); +		}  	} else  		netif_dbg(dev, tx_queued, dev->net,  			  "> tx, len %d, type 0x%x\n", length, skb->protocol); @@ -1152,6 +1386,28 @@ deferred:  }  EXPORT_SYMBOL_GPL(usbnet_start_xmit); +static int rx_alloc_submit(struct usbnet *dev, gfp_t flags) +{ +	struct urb	*urb; +	int		i; +	int		ret = 0; + +	/* don't refill the queue all at once */ +	for (i = 0; i < 10 && dev->rxq.qlen < RX_QLEN(dev); i++) { +		urb = usb_alloc_urb(0, flags); +		if (urb != NULL) { +			ret = rx_submit(dev, urb, flags); +			if (ret) +				goto err; +		} else { +			ret = -ENOMEM; +			goto err; +		} +	} +err: +	return ret; +} +  /*-------------------------------------------------------------------------*/  // tasklet (work deferred from completions, in_irq) or timer @@ -1170,6 +1426,7 @@ static void usbnet_bh (unsigned long param)  			rx_process (dev, skb);  			continue;  		case tx_done: +			kfree(entry->urb->sg);  		case rx_cleanup:  			usb_free_urb (entry->urb);  			dev_kfree_skb (skb); @@ -1179,38 +1436,32 @@ static void usbnet_bh (unsigned long param)  		}  	} -	// waiting for all pending urbs to complete? -	if (dev->wait) { -		if ((dev->txq.qlen + dev->rxq.qlen + dev->done.qlen) == 0) { -			wake_up (dev->wait); -		} +	/* restart RX again after disabling due to high error rate */ +	clear_bit(EVENT_RX_KILL, &dev->flags); + +	/* waiting for all pending urbs to complete? +	 * only then can we forgo submitting anew +	 */ +	if (waitqueue_active(&dev->wait)) { +		if (dev->txq.qlen + dev->rxq.qlen + dev->done.qlen == 0) +			wake_up_all(&dev->wait);  	// or are we maybe short a few urbs?  	} else if (netif_running (dev->net) &&  		   netif_device_present (dev->net) && +		   netif_carrier_ok(dev->net) &&  		   !timer_pending (&dev->delay) &&  		   !test_bit (EVENT_RX_HALT, &dev->flags)) {  		int	temp = dev->rxq.qlen; -		int	qlen = RX_QLEN (dev); - -		if (temp < qlen) { -			struct urb	*urb; -			int		i; - -			// don't refill the queue all at once -			for (i = 0; i < 10 && dev->rxq.qlen < qlen; i++) { -				urb = usb_alloc_urb (0, GFP_ATOMIC); -				if (urb != NULL) { -					if (rx_submit (dev, urb, GFP_ATOMIC) == -					    -ENOLINK) -						return; -				} -			} + +		if (temp < RX_QLEN(dev)) { +			if (rx_alloc_submit(dev, GFP_ATOMIC) == -ENOLINK) +				return;  			if (temp != dev->rxq.qlen)  				netif_dbg(dev, link, dev->net,  					  "rxqlen %d --> %d\n",  					  temp, dev->rxq.qlen); -			if (dev->rxq.qlen < qlen) +			if (dev->rxq.qlen < RX_QLEN(dev))  				tasklet_schedule (&dev->bh);  		}  		if (dev->txq.qlen < TX_QLEN (dev)) @@ -1248,14 +1499,18 @@ void usbnet_disconnect (struct usb_interface *intf)  	net = dev->net;  	unregister_netdev (net); -	/* we don't hold rtnl here ... */ -	flush_scheduled_work (); +	cancel_work_sync(&dev->kevent); + +	usb_scuttle_anchored_urbs(&dev->deferred);  	if (dev->driver_info->unbind)  		dev->driver_info->unbind (dev, intf); +	usb_kill_urb(dev->interrupt); +	usb_free_urb(dev->interrupt); +	kfree(dev->padding_pkt); +  	free_netdev(net); -	usb_put_dev (xdev);  }  EXPORT_SYMBOL_GPL(usbnet_disconnect); @@ -1295,7 +1550,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)  	/* usbnet already took usb runtime pm, so have to enable the feature  	 * for usb interface, otherwise usb_autopm_get_interface may return -	 * failure if USB_SUSPEND(RUNTIME_PM) is enabled. +	 * failure if RUNTIME_PM is enabled.  	 */  	if (!driver->supports_autosuspend) {  		driver->supports_autosuspend = 1; @@ -1311,16 +1566,12 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)  	xdev = interface_to_usbdev (udev);  	interface = udev->cur_altsetting; -	usb_get_dev (xdev); -  	status = -ENOMEM;  	// set up our own records  	net = alloc_etherdev(sizeof(*dev)); -	if (!net) { -		dbg ("can't kmalloc dev"); +	if (!net)  		goto out; -	}  	/* netdev_printk() needs this so do it as early as possible */  	SET_NETDEV_DEV(net, &udev->dev); @@ -1332,6 +1583,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)  	dev->driver_name = name;  	dev->msg_enable = netif_msg_init (msg_level, NETIF_MSG_DRV  				| NETIF_MSG_PROBE | NETIF_MSG_LINK); +	init_waitqueue_head(&dev->wait);  	skb_queue_head_init (&dev->rxq);  	skb_queue_head_init (&dev->txq);  	skb_queue_head_init (&dev->done); @@ -1344,6 +1596,8 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)  	dev->delay.data = (unsigned long) dev;  	init_timer (&dev->delay);  	mutex_init (&dev->phy_mutex); +	mutex_init(&dev->interrupt_mutex); +	dev->interrupt_count = 0;  	dev->net = net;  	strcpy (net->name, "usb%d"); @@ -1375,7 +1629,8 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)  		// else "eth%d" when there's reasonable doubt.  userspace  		// can rename the link if it knows better.  		if ((dev->driver_info->flags & FLAG_ETHER) != 0 && -		    (net->dev_addr [0] & 0x02) == 0) +		    ((dev->driver_info->flags & FLAG_POINTTOPOINT) == 0 || +		     (net->dev_addr [0] & 0x02) == 0))  			strcpy (net->name, "eth%d");  		/* WLAN devices should always be named "wlan%d" */  		if ((dev->driver_info->flags & FLAG_WLAN) != 0) @@ -1384,6 +1639,10 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)  		if ((dev->driver_info->flags & FLAG_WWAN) != 0)  			strcpy(net->name, "wwan%d"); +		/* devices that cannot do ARP */ +		if ((dev->driver_info->flags & FLAG_NOARP) != 0) +			net->flags |= IFF_NOARP; +  		/* maybe the remote can't receive an Ethernet MTU */  		if (net->mtu > (dev->hard_mtu - net->hard_header_len))  			net->mtu = dev->hard_mtu - net->hard_header_len; @@ -1409,14 +1668,30 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)  		dev->rx_urb_size = dev->hard_mtu;  	dev->maxpacket = usb_maxpacket (dev->udev, dev->out, 1); +	/* let userspace know we have a random address */ +	if (ether_addr_equal(net->dev_addr, node_id)) +		net->addr_assign_type = NET_ADDR_RANDOM; +  	if ((dev->driver_info->flags & FLAG_WLAN) != 0)  		SET_NETDEV_DEVTYPE(net, &wlan_type);  	if ((dev->driver_info->flags & FLAG_WWAN) != 0)  		SET_NETDEV_DEVTYPE(net, &wwan_type); +	/* initialize max rx_qlen and tx_qlen */ +	usbnet_update_max_qlen(dev); + +	if (dev->can_dma_sg && !(info->flags & FLAG_SEND_ZLP) && +		!(info->flags & FLAG_MULTI_PACKET)) { +		dev->padding_pkt = kzalloc(1, GFP_KERNEL); +		if (!dev->padding_pkt) { +			status = -ENOMEM; +			goto out4; +		} +	} +  	status = register_netdev (net);  	if (status) -		goto out3; +		goto out5;  	netif_info(dev, probe, dev->net,  		   "register '%s' at usb-%s-%s, %s, %pM\n",  		   udev->dev.driver->name, @@ -1430,17 +1705,20 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)  	netif_device_attach (net);  	if (dev->driver_info->flags & FLAG_LINK_INTR) -		netif_carrier_off(net); +		usbnet_link_change(dev, 0, 0);  	return 0; +out5: +	kfree(dev->padding_pkt); +out4: +	usb_free_urb(dev->interrupt);  out3:  	if (info->unbind)  		info->unbind (dev, udev);  out1:  	free_netdev(net);  out: -	usb_put_dev(xdev);  	return status;  }  EXPORT_SYMBOL_GPL(usbnet_probe); @@ -1459,7 +1737,8 @@ int usbnet_suspend (struct usb_interface *intf, pm_message_t message)  	if (!dev->suspend_count++) {  		spin_lock_irq(&dev->txq.lock);  		/* don't autosuspend while transmitting */ -		if (dev->txq.qlen && (message.event & PM_EVENT_AUTO)) { +		if (dev->txq.qlen && PMSG_IS_AUTO(message)) { +			dev->suspend_count--;  			spin_unlock_irq(&dev->txq.lock);  			return -EBUSY;  		} else { @@ -1472,7 +1751,7 @@ int usbnet_suspend (struct usb_interface *intf, pm_message_t message)  		 */  		netif_device_detach (dev->net);  		usbnet_terminate_urbs(dev); -		usb_kill_urb(dev->interrupt); +		__usbnet_status_stop_force(dev);  		/*  		 * reattach so runtime management can use and @@ -1492,6 +1771,9 @@ int usbnet_resume (struct usb_interface *intf)  	int                     retval;  	if (!--dev->suspend_count) { +		/* resume interrupt URB if it was previously submitted */ +		__usbnet_status_start_force(dev, GFP_NOIO); +  		spin_lock_irq(&dev->txq.lock);  		while ((res = usb_get_from_anchor(&dev->deferred))) { @@ -1499,6 +1781,7 @@ int usbnet_resume (struct usb_interface *intf)  			retval = usb_submit_urb(res, GFP_ATOMIC);  			if (retval < 0) {  				dev_kfree_skb_any(skb); +				kfree(res->sg);  				usb_free_urb(res);  				usb_autopm_put_interface_async(dev->intf);  			} else { @@ -1510,24 +1793,270 @@ int usbnet_resume (struct usb_interface *intf)  		smp_mb();  		clear_bit(EVENT_DEV_ASLEEP, &dev->flags);  		spin_unlock_irq(&dev->txq.lock); -		if (!(dev->txq.qlen >= TX_QLEN(dev))) -			netif_start_queue(dev->net); -		tasklet_schedule (&dev->bh); + +		if (test_bit(EVENT_DEV_OPEN, &dev->flags)) { +			/* handle remote wakeup ASAP +			 * we cannot race against stop +			 */ +			if (netif_device_present(dev->net) && +				!timer_pending(&dev->delay) && +				!test_bit(EVENT_RX_HALT, &dev->flags)) +					rx_alloc_submit(dev, GFP_NOIO); + +			if (!(dev->txq.qlen >= TX_QLEN(dev))) +				netif_tx_wake_all_queues(dev->net); +			tasklet_schedule (&dev->bh); +		}  	} + +	if (test_and_clear_bit(EVENT_DEVICE_REPORT_IDLE, &dev->flags)) +		usb_autopm_get_interface_no_resume(intf); +  	return 0;  }  EXPORT_SYMBOL_GPL(usbnet_resume); +/* + * Either a subdriver implements manage_power, then it is assumed to always + * be ready to be suspended or it reports the readiness to be suspended + * explicitly + */ +void usbnet_device_suggests_idle(struct usbnet *dev) +{ +	if (!test_and_set_bit(EVENT_DEVICE_REPORT_IDLE, &dev->flags)) { +		dev->intf->needs_remote_wakeup = 1; +		usb_autopm_put_interface_async(dev->intf); +	} +} +EXPORT_SYMBOL(usbnet_device_suggests_idle); + +/* + * For devices that can do without special commands + */ +int usbnet_manage_power(struct usbnet *dev, int on) +{ +	dev->intf->needs_remote_wakeup = on; +	return 0; +} +EXPORT_SYMBOL(usbnet_manage_power); + +void usbnet_link_change(struct usbnet *dev, bool link, bool need_reset) +{ +	/* update link after link is reseted */ +	if (link && !need_reset) +		netif_carrier_on(dev->net); +	else +		netif_carrier_off(dev->net); + +	if (need_reset && link) +		usbnet_defer_kevent(dev, EVENT_LINK_RESET); +	else +		usbnet_defer_kevent(dev, EVENT_LINK_CHANGE); +} +EXPORT_SYMBOL(usbnet_link_change); + +/*-------------------------------------------------------------------------*/ +static int __usbnet_read_cmd(struct usbnet *dev, u8 cmd, u8 reqtype, +			     u16 value, u16 index, void *data, u16 size) +{ +	void *buf = NULL; +	int err = -ENOMEM; + +	netdev_dbg(dev->net, "usbnet_read_cmd cmd=0x%02x reqtype=%02x" +		   " value=0x%04x index=0x%04x size=%d\n", +		   cmd, reqtype, value, index, size); + +	if (data) { +		buf = kmalloc(size, GFP_KERNEL); +		if (!buf) +			goto out; +	} + +	err = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), +			      cmd, reqtype, value, index, buf, size, +			      USB_CTRL_GET_TIMEOUT); +	if (err > 0 && err <= size) +		memcpy(data, buf, err); +	kfree(buf); +out: +	return err; +} + +static int __usbnet_write_cmd(struct usbnet *dev, u8 cmd, u8 reqtype, +			      u16 value, u16 index, const void *data, +			      u16 size) +{ +	void *buf = NULL; +	int err = -ENOMEM; + +	netdev_dbg(dev->net, "usbnet_write_cmd cmd=0x%02x reqtype=%02x" +		   " value=0x%04x index=0x%04x size=%d\n", +		   cmd, reqtype, value, index, size); + +	if (data) { +		buf = kmemdup(data, size, GFP_KERNEL); +		if (!buf) +			goto out; +	} + +	err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), +			      cmd, reqtype, value, index, buf, size, +			      USB_CTRL_SET_TIMEOUT); +	kfree(buf); + +out: +	return err; +} + +/* + * The function can't be called inside suspend/resume callback, + * otherwise deadlock will be caused. + */ +int usbnet_read_cmd(struct usbnet *dev, u8 cmd, u8 reqtype, +		    u16 value, u16 index, void *data, u16 size) +{ +	int ret; + +	if (usb_autopm_get_interface(dev->intf) < 0) +		return -ENODEV; +	ret = __usbnet_read_cmd(dev, cmd, reqtype, value, index, +				data, size); +	usb_autopm_put_interface(dev->intf); +	return ret; +} +EXPORT_SYMBOL_GPL(usbnet_read_cmd); + +/* + * The function can't be called inside suspend/resume callback, + * otherwise deadlock will be caused. + */ +int usbnet_write_cmd(struct usbnet *dev, u8 cmd, u8 reqtype, +		     u16 value, u16 index, const void *data, u16 size) +{ +	int ret; + +	if (usb_autopm_get_interface(dev->intf) < 0) +		return -ENODEV; +	ret = __usbnet_write_cmd(dev, cmd, reqtype, value, index, +				 data, size); +	usb_autopm_put_interface(dev->intf); +	return ret; +} +EXPORT_SYMBOL_GPL(usbnet_write_cmd); + +/* + * The function can be called inside suspend/resume callback safely + * and should only be called by suspend/resume callback generally. + */ +int usbnet_read_cmd_nopm(struct usbnet *dev, u8 cmd, u8 reqtype, +			  u16 value, u16 index, void *data, u16 size) +{ +	return __usbnet_read_cmd(dev, cmd, reqtype, value, index, +				 data, size); +} +EXPORT_SYMBOL_GPL(usbnet_read_cmd_nopm); + +/* + * The function can be called inside suspend/resume callback safely + * and should only be called by suspend/resume callback generally. + */ +int usbnet_write_cmd_nopm(struct usbnet *dev, u8 cmd, u8 reqtype, +			  u16 value, u16 index, const void *data, +			  u16 size) +{ +	return __usbnet_write_cmd(dev, cmd, reqtype, value, index, +				  data, size); +} +EXPORT_SYMBOL_GPL(usbnet_write_cmd_nopm); + +static void usbnet_async_cmd_cb(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); +} +/* + * The caller must make sure that device can't be put into suspend + * state until the control URB completes. + */ +int usbnet_write_cmd_async(struct usbnet *dev, u8 cmd, u8 reqtype, +			   u16 value, u16 index, const void *data, u16 size) +{ +	struct usb_ctrlrequest *req = NULL; +	struct urb *urb; +	int err = -ENOMEM; +	void *buf = NULL; + +	netdev_dbg(dev->net, "usbnet_write_cmd cmd=0x%02x reqtype=%02x" +		   " value=0x%04x index=0x%04x size=%d\n", +		   cmd, reqtype, value, index, size); + +	urb = usb_alloc_urb(0, GFP_ATOMIC); +	if (!urb) { +		netdev_err(dev->net, "Error allocating URB in" +			   " %s!\n", __func__); +		goto fail; +	} + +	if (data) { +		buf = kmemdup(data, size, GFP_ATOMIC); +		if (!buf) { +			netdev_err(dev->net, "Error allocating buffer" +				   " in %s!\n", __func__); +			goto fail_free; +		} +	} + +	req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC); +	if (!req) +		goto fail_free_buf; + +	req->bRequestType = reqtype; +	req->bRequest = cmd; +	req->wValue = cpu_to_le16(value); +	req->wIndex = cpu_to_le16(index); +	req->wLength = cpu_to_le16(size); + +	usb_fill_control_urb(urb, dev->udev, +			     usb_sndctrlpipe(dev->udev, 0), +			     (void *)req, buf, size, +			     usbnet_async_cmd_cb, req); +	urb->transfer_flags |= URB_FREE_BUFFER; + +	err = usb_submit_urb(urb, GFP_ATOMIC); +	if (err < 0) { +		netdev_err(dev->net, "Error submitting the control" +			   " message: status=%d\n", err); +		goto fail_free; +	} +	return 0; + +fail_free_buf: +	kfree(buf); +fail_free: +	kfree(req); +	usb_free_urb(urb); +fail: +	return err; + +} +EXPORT_SYMBOL_GPL(usbnet_write_cmd_async);  /*-------------------------------------------------------------------------*/  static int __init usbnet_init(void)  { -	/* compiler should optimize this out */ -	BUILD_BUG_ON (sizeof (((struct sk_buff *)0)->cb) -			< sizeof (struct skb_data)); +	/* Compiler should optimize this out. */ +	BUILD_BUG_ON( +		FIELD_SIZEOF(struct sk_buff, cb) < sizeof(struct skb_data)); -	random_ether_addr(node_id); +	eth_random_addr(node_id);  	return 0;  }  module_init(usbnet_init); diff --git a/drivers/net/usb/zaurus.c b/drivers/net/usb/zaurus.c index 3eb0b167b5b..6aaa6eb9df7 100644 --- a/drivers/net/usb/zaurus.c +++ b/drivers/net/usb/zaurus.c @@ -13,15 +13,13 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  // #define	DEBUG			// error path messages, extra info  // #define	VERBOSE			// more; success messages  #include <linux/module.h> -#include <linux/init.h>  #include <linux/netdevice.h>  #include <linux/ethtool.h>  #include <linux/workqueue.h> @@ -102,7 +100,7 @@ static int always_connected (struct usbnet *dev)  static const struct driver_info	zaurus_sl5x00_info = {  	.description =	"Sharp Zaurus SL-5x00", -	.flags =	FLAG_FRAMING_Z, +	.flags =	FLAG_POINTTOPOINT | FLAG_FRAMING_Z,  	.check_connect = always_connected,  	.bind =		zaurus_bind,  	.unbind =	usbnet_cdc_unbind, @@ -112,7 +110,7 @@ static const struct driver_info	zaurus_sl5x00_info = {  static const struct driver_info	zaurus_pxa_info = {  	.description =	"Sharp Zaurus, PXA-2xx based", -	.flags =	FLAG_FRAMING_Z, +	.flags =	FLAG_POINTTOPOINT | FLAG_FRAMING_Z,  	.check_connect = always_connected,  	.bind =		zaurus_bind,  	.unbind =	usbnet_cdc_unbind, @@ -122,7 +120,7 @@ static const struct driver_info	zaurus_pxa_info = {  static const struct driver_info	olympus_mxl_info = {  	.description =	"Olympus R1000", -	.flags =	FLAG_FRAMING_Z, +	.flags =	FLAG_POINTTOPOINT | FLAG_FRAMING_Z,  	.check_connect = always_connected,  	.bind =		zaurus_bind,  	.unbind =	usbnet_cdc_unbind, @@ -258,7 +256,7 @@ bad_desc:  static const struct driver_info	bogus_mdlm_info = {  	.description =	"pseudo-MDLM (BLAN) device", -	.flags =	FLAG_FRAMING_Z, +	.flags =	FLAG_POINTTOPOINT | FLAG_FRAMING_Z,  	.check_connect = always_connected,  	.tx_fixup =	zaurus_tx_fixup,  	.bind =		blan_mdlm_bind, @@ -316,6 +314,11 @@ static const struct usb_device_id	products [] = {  	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, @@ -331,15 +334,10 @@ static const struct usb_device_id	products [] = {  	ZAURUS_MASTER_INTERFACE,  	.driver_info = ZAURUS_PXA_INFO,  }, - - -/* At least some of the newest PXA units have very different lies about - * their standards support:  they claim to be cell phones offering - * direct access to their radios!  (No, they don't conform to CDC MDLM.) - */  { -	USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM, -			USB_CDC_PROTO_NONE), +	/* 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 */ @@ -359,6 +357,13 @@ static const struct usb_device_id	products [] = {  	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); @@ -370,19 +375,10 @@ static struct usb_driver zaurus_driver = {  	.disconnect =	usbnet_disconnect,  	.suspend =	usbnet_suspend,  	.resume =	usbnet_resume, +	.disable_hub_initiated_lpm = 1,  }; -static int __init zaurus_init(void) -{ -	return usb_register(&zaurus_driver); -} -module_init(zaurus_init); - -static void __exit zaurus_exit(void) -{ -	usb_deregister(&zaurus_driver); -} -module_exit(zaurus_exit); +module_usb_driver(zaurus_driver);  MODULE_AUTHOR("Pavel Machek, David Brownell");  MODULE_DESCRIPTION("Sharp Zaurus PDA, and compatible products");  | 
