diff options
Diffstat (limited to 'drivers/net/usb')
43 files changed, 4342 insertions, 1274 deletions
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index 40db3123331..7e7269fd370 100644 --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -92,11 +92,12 @@ config USB_RTL8150  	  module will be called rtl8150.  config USB_RTL8152 -	tristate "Realtek RTL8152 Based USB 2.0 Ethernet Adapters" +	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. +	  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. @@ -242,6 +243,21 @@ 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 @@ -261,12 +277,12 @@ config USB_NET_CDC_MBIM  	  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 based USB 1.1 -	  10/100 Ethernet adapters. +	  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" @@ -276,6 +292,21 @@ config USB_NET_SR9700  	  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 diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile index 8b342cf992f..e2797f1e1b3 100644 --- a/drivers/net/usb/Makefile +++ b/drivers/net/usb/Makefile @@ -11,10 +11,11 @@ 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 r815x.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 @@ -32,6 +33,7 @@ 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.h b/drivers/net/usb/asix.h index bdaa12d07a1..5d049d00c2d 100644 --- a/drivers/net/usb/asix.h +++ b/drivers/net/usb/asix.h @@ -16,8 +16,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/>.   */  #ifndef _ASIX_H @@ -28,7 +27,6 @@  #include <linux/module.h>  #include <linux/kmod.h> -#include <linux/init.h>  #include <linux/netdevice.h>  #include <linux/etherdevice.h>  #include <linux/ethtool.h> diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c index 577c72d5f36..5c55f11572b 100644 --- a/drivers/net/usb/asix_common.c +++ b/drivers/net/usb/asix_common.c @@ -16,8 +16,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 "asix.h" diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c index 386a3df5367..5d194093f3e 100644 --- a/drivers/net/usb/asix_devices.c +++ b/drivers/net/usb/asix_devices.c @@ -16,8 +16,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 "asix.h" @@ -918,7 +917,8 @@ static const struct driver_info ax88178_info = {  	.status = asix_status,  	.link_reset = ax88178_link_reset,  	.reset = ax88178_reset, -	.flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR, +	.flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | +		 FLAG_MULTI_PACKET,  	.rx_fixup = asix_rx_fixup_common,  	.tx_fixup = asix_tx_fixup,  }; diff --git a/drivers/net/usb/ax88172a.c b/drivers/net/usb/ax88172a.c index 723b3879ecc..5f18fcb8dcc 100644 --- a/drivers/net/usb/ax88172a.c +++ b/drivers/net/usb/ax88172a.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/>.   */  #include "asix.h" diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c index 3569293df87..054e59ca694 100644 --- a/drivers/net/usb/ax88179_178a.c +++ b/drivers/net/usb/ax88179_178a.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/>.   */  #include <linux/module.h> @@ -36,8 +35,8 @@  #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(31)) -#define AX_RXHDR_DROP_ERR			((u32)BIT(30)) +#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 @@ -78,7 +77,6 @@  #define AX_MEDIUM_STATUS_MODE			0x22  	#define AX_MEDIUM_GIGAMODE	0x01  	#define AX_MEDIUM_FULL_DUPLEX	0x02 -	#define AX_MEDIUM_ALWAYS_ONE	0x04  	#define AX_MEDIUM_EN_125MHZ	0x08  	#define AX_MEDIUM_RXFLOW_CTRLEN	0x10  	#define AX_MEDIUM_TXFLOW_CTRLEN	0x20 @@ -1031,20 +1029,12 @@ static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf)  	dev->mii.phy_id = 0x03;  	dev->mii.supports_gmii = 1; -	if (usb_device_no_sg_constraint(dev->udev)) -		dev->can_dma_sg = 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; -	if (dev->can_dma_sg) { -		dev->net->features |= NETIF_F_SG | NETIF_F_TSO; -		dev->net->hw_features |= NETIF_F_SG | NETIF_F_TSO; -	} -  	/* Enable checksum offload */  	*tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |  	       AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6; @@ -1065,8 +1055,8 @@ static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf)  	/* Configure default medium type => giga */  	*tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN | -		 AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_ALWAYS_ONE | -		 AX_MEDIUM_FULL_DUPLEX | AX_MEDIUM_GIGAMODE; +		 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); @@ -1120,6 +1110,10 @@ static int ax88179_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  	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); @@ -1225,7 +1219,7 @@ static int ax88179_link_reset(struct usbnet *dev)  	}  	mode = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN | -	       AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_ALWAYS_ONE; +	       AX_MEDIUM_RXFLOW_CTRLEN;  	ax88179_read_cmd(dev, AX_ACCESS_MAC, PHYSICAL_LINK_STATUS,  			 1, 1, &link_sts); @@ -1339,8 +1333,8 @@ static int ax88179_reset(struct usbnet *dev)  	/* Configure default medium type => giga */  	*tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN | -		 AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_ALWAYS_ONE | -		 AX_MEDIUM_FULL_DUPLEX | AX_MEDIUM_GIGAMODE; +		 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); @@ -1393,6 +1387,19 @@ static const struct driver_info ax88178a_info = {  	.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, @@ -1406,6 +1413,32 @@ static const struct driver_info sitecom_info = {  	.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 */ @@ -1416,9 +1449,21 @@ static const struct usb_device_id products[] = {  	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, +	.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,  },  	{ },  }; diff --git a/drivers/net/usb/catc.c b/drivers/net/usb/catc.c index 8d5cac2d8e3..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> @@ -640,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); @@ -778,7 +776,7 @@ static int catc_probe(struct usb_interface *intf, const struct usb_device_id *id  	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, @@ -795,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; @@ -882,7 +880,7 @@ static int catc_probe(struct usb_interface *intf, const struct usb_device_id *id  		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); diff --git a/drivers/net/usb/cdc-phonet.c b/drivers/net/usb/cdc-phonet.c index 7d78669000d..6358d420e18 100644 --- a/drivers/net/usb/cdc-phonet.c +++ b/drivers/net/usb/cdc-phonet.c @@ -328,7 +328,7 @@ 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; diff --git a/drivers/net/usb/cdc_eem.c b/drivers/net/usb/cdc_eem.c index 08d55b6bf27..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> diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index 2023f3ea891..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> @@ -343,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: @@ -487,6 +501,7 @@ static const struct driver_info wwan_info = {  #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 !! @@ -626,6 +641,13 @@ static const struct usb_device_id	products[] = {  	.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, @@ -653,6 +675,13 @@ static const struct usb_device_id	products[] = {  	.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. diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c index 25ba7eca9a1..5ee7a1dbc02 100644 --- a/drivers/net/usb/cdc_mbim.c +++ b/drivers/net/usb/cdc_mbim.c @@ -21,14 +21,24 @@  #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; -	struct usb_interface *control; -	struct usb_interface *data; +	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 */ @@ -42,13 +52,11 @@ static int cdc_mbim_manage_power(struct usbnet *dev, int 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); -		if (rv < 0) -			goto err;  		dev->intf->needs_remote_wakeup = on; -		usb_autopm_put_interface(dev->intf); +		if (!rv) +			usb_autopm_put_interface(dev->intf);  	} -err: -	return rv; +	return 0;  }  static int cdc_mbim_wdm_manage_power(struct usb_interface *intf, int status) @@ -62,16 +70,91 @@ static int cdc_mbim_wdm_manage_power(struct usb_interface *intf, int status)  	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 = cdc_ncm_select_altsetting(dev, intf); +	u8 data_altsetting = 1;  	struct cdc_mbim_state *info = (void *)&dev->data; -	/* Probably NCM, defer for cdc_ncm_bind */ +	/* 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; @@ -101,7 +184,10 @@ static int cdc_mbim_bind(struct usbnet *dev, struct usb_interface *intf)  	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; +	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;  } @@ -120,6 +206,16 @@ static void cdc_mbim_unbind(struct usbnet *dev, struct usb_interface *intf)  	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)  { @@ -128,6 +224,7 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb  	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) @@ -137,29 +234,50 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *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> +		 *   no tag     => IPS session <0> if !FLAG_IPS0_VLAN  		 *   1 - 255    => IPS session <vlanid>  		 *   256 - 511  => DSS session <vlanid - 256> -		 *   512 - 4095 => unsupported, drop +		 *   512 - 4093 => unsupported, drop +		 *   4094       => IPS session <0> if FLAG_IPS0_VLAN  		 */ -		vlan_get_tag(skb, &tci);  		switch (tci & 0x0f00) {  		case 0x0000: /* VLAN ID 0 - 255 */ -			/* verify that datagram is IPv4 or IPv6 */ -			skb_reset_mac_header(skb); -			switch (eth_hdr(skb)->h_proto) { -			case htons(ETH_P_IP): -			case htons(ETH_P_IPV6): -				break; -			default: +			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; @@ -169,11 +287,10 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb  				  "unsupported tci=0x%04x\n", tci);  			goto error;  		} -		skb_pull(skb, ETH_HLEN);  	}  	spin_lock_bh(&ctx->mtx); -	skb_out = cdc_ncm_fill_tx_frame(ctx, skb, sign); +	skb_out = cdc_ncm_fill_tx_frame(dev, skb, sign);  	spin_unlock_bh(&ctx->mtx);  	return skb_out; @@ -184,12 +301,74 @@ error:  	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) { /* IPS session? */ +	if (tci < 256 || tci == MBIM_IPS0_VID) { /* IPS session? */  		if (len < sizeof(struct iphdr))  			goto err; @@ -198,6 +377,8 @@ static struct sk_buff *cdc_mbim_process_dgram(struct usbnet *dev, u8 *buf, size_  			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: @@ -239,6 +420,7 @@ static int cdc_mbim_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)  	struct usb_cdc_ncm_dpe16 *dpe16;  	int ndpoffset;  	int loopcount = 50; /* arbitrary max preventing infinite loop */ +	u32 payload = 0;  	u8 *c;  	u16 tci; @@ -257,6 +439,9 @@ next_ndp:  	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; @@ -298,6 +483,7 @@ next_ndp:  			if (!skb)  				goto error;  			usbnet_skb_return(dev, skb); +			payload += len;	/* count payload bytes in this NTB */  		}  	}  err_ndp: @@ -306,6 +492,10 @@ err_ndp:  	if (ndpoffset && loopcount--)  		goto next_ndp; +	/* update stats */ +	ctx->rx_overhead += skb_in->len - payload; +	ctx->rx_ntbs++; +  	return 1;  error:  	return 0; @@ -313,15 +503,13 @@ error:  static int cdc_mbim_suspend(struct usb_interface *intf, pm_message_t message)  { -	int ret = 0; +	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 == NULL) { -		ret = -1; +	if (!ctx)  		goto error; -	}  	/*  	 * Both usbnet_suspend() and subdriver->suspend() MUST return 0 @@ -354,7 +542,7 @@ static int cdc_mbim_resume(struct usb_interface *intf)  	if (ret < 0)  		goto err;  	ret = usbnet_resume(intf); -	if (ret < 0 && callsub && info->subdriver->suspend) +	if (ret < 0 && callsub)  		info->subdriver->suspend(intf, PMSG_SUSPEND);  err:  	return ret; @@ -371,9 +559,18 @@ static const struct driver_info cdc_mbim_info = {  };  /* MBIM and NCM devices should not need a ZLP after NTBs with - * dwNtbOutMaxSize length. This driver_info is for the exceptional - * devices requiring it anyway, allowing them to be supported without - * forcing the performance penalty on all the sane devices. + * 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", @@ -396,16 +593,13 @@ static const struct usb_device_id mbim_devs[] = {  	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE),  	  .driver_info = (unsigned long)&cdc_mbim_info,  	}, -	/* Sierra Wireless MC7710 need ZLPs */ -	{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x68a2, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE), -	  .driver_info = (unsigned long)&cdc_mbim_info_zlp, -	}, -	/* HP hs2434 Mobile Broadband Module needs ZLPs */ -	{ USB_DEVICE_AND_INTERFACE_INFO(0x3f0, 0x4b1d, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE), -	  .driver_info = (unsigned long)&cdc_mbim_info_zlp, +	/* 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, +	  .driver_info = (unsigned long)&cdc_mbim_info_zlp,  	},  	{  	}, diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 43afde8f48d..80a844e0ae0 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -39,7 +39,6 @@   */  #include <linux/module.h> -#include <linux/init.h>  #include <linux/netdevice.h>  #include <linux/ctype.h>  #include <linux/ethtool.h> @@ -53,8 +52,6 @@  #include <linux/usb/cdc.h>  #include <linux/usb/cdc_ncm.h> -#define	DRIVER_VERSION				"14-Mar-2012" -  #if IS_ENABLED(CONFIG_USB_NET_CDC_MBIM)  static bool prefer_mbim = true;  #else @@ -68,31 +65,384 @@ 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; -static void -cdc_ncm_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) +struct cdc_ncm_stats { +	char stat_string[ETH_GSTRING_LEN]; +	int sizeof_stat; +	int stat_offset; +}; + +#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), +}; + +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; +	} +} + +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; + +	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; +	} +} + +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; +		} +	} +} + +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, +}; + +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; + +	/* 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)); + +	/* 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; +	} + +	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); + +	return val; +} + +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; + +	/* 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)); + +	/* some devices set dwNtbOutMaxSize too low for the above default */ +	min = min(min, max); + +	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(to_net_dev(d)); +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + +	return sprintf(buf, "%u\n", ctx->min_tx_pkt); +} + +static ssize_t cdc_ncm_show_rx_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->rx_max); +} + +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(net); +	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; -	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)); +	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 u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx) +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; -	u8 flags; -	u8 iface_no; -	int err; -	int eth_hlen; -	u16 ntb_fmt_supported; -	u32 min_dgram_size; -	u32 min_hdr_size; -	struct usbnet *dev = netdev_priv(ctx->netdev); -	iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber; +	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; +	} + +	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 int cdc_ncm_eth_hlen(struct usbnet *dev) +{ +	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;  	err = usbnet_read_cmd(dev, USB_CDC_GET_NTB_PARAMETERS,  			      USB_TYPE_CLASS | USB_DIR_IN @@ -100,11 +450,40 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)  			      0, iface_no, &ctx->ncm_parm,  			      sizeof(ctx->ncm_parm));  	if (err < 0) { -		pr_debug("failed GET_NTB_PARAMETERS\n"); -		return 1; +		dev_err(&dev->intf->dev, "failed GET_NTB_PARAMETERS\n"); +		return err; /* GET_NTB_PARAMETERS is required */ +	} + +	/* 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"); +	} + +	/* 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); @@ -112,66 +491,79 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)  	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); -	ntb_fmt_supported = le16_to_cpu(ctx->ncm_parm.bmNtbFormatsSupported); - -	eth_hlen = ETH_HLEN; -	min_dgram_size = CDC_NCM_MIN_DATAGRAM_SIZE; -	min_hdr_size = CDC_NCM_MIN_HDR_SIZE; -	if (ctx->mbim_desc != NULL) { -		flags = ctx->mbim_desc->bmNetworkCapabilities; -		eth_hlen = 0; -		min_dgram_size = CDC_MBIM_MIN_DATAGRAM_SIZE; -		min_hdr_size = 0; -	} else 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 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, flags); +	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; -	/* verify maximum size of received NTB in bytes */ -	if (ctx->rx_max < USB_CDC_NCM_NTB_MIN_IN_SIZE) { -		pr_debug("Using min receive length=%d\n", -						USB_CDC_NCM_NTB_MIN_IN_SIZE); -		ctx->rx_max = USB_CDC_NCM_NTB_MIN_IN_SIZE; -	} +	/* 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); -	if (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; -	} +	/* initial coalescing timer interval */ +	ctx->timer_interval = CDC_NCM_TIMER_INTERVAL_USEC * NSEC_PER_USEC; -	/* inform device about NTB input size changes */ -	if (ctx->rx_max != le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize)) { -		__le32 dwNtbInMaxSize = cpu_to_le32(ctx->rx_max); +	return 0; +} -		err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_INPUT_SIZE, -				       USB_TYPE_CLASS | USB_DIR_OUT -				       | USB_RECIP_INTERFACE, -				       0, iface_no, &dwNtbInMaxSize, 4); -		if (err < 0) -			pr_debug("Setting NTB Input Size failed\n"); +/* 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 < -	    (min_hdr_size + min_dgram_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: @@ -183,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;  	} @@ -197,104 +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_remainder = ((ctx->tx_remainder - cdc_ncm_eth_hlen(dev)) &  			     (ctx->tx_modulus - 1)); +} -	/* additional configuration */ - -	/* set CRC Mode */ -	if (flags & USB_CDC_NCM_NCAP_CRC_MODE) { -		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) -			pr_debug("Setting CRC mode off failed\n"); -	} - -	/* set NTB format, if both formats are supported */ -	if (ntb_fmt_supported & USB_CDC_NCM_NTH32_SIGN) { -		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) -			pr_debug("Setting NTB format to 16-bit failed\n"); -	} +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; -	ctx->max_datagram_size = min_dgram_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)); -	/* set Max Datagram Size (MTU) */ -	if (flags & USB_CDC_NCM_NCAP_MAX_DATAGRAM_SIZE) { -		__le16 max_datagram_size; -		u16 eth_max_sz; -		if (ctx->ether_desc != NULL) -			eth_max_sz = le16_to_cpu(ctx->ether_desc->wMaxSegmentSize); -		else if (ctx->mbim_desc != NULL) -			eth_max_sz = le16_to_cpu(ctx->mbim_desc->wMaxSegmentSize); -		else -			goto max_dgram_err; - -		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) { -			pr_debug("GET_MAX_DATAGRAM_SIZE failed, use size=%u\n", -				 min_dgram_size); -		} else { -			ctx->max_datagram_size = -				le16_to_cpu(max_datagram_size); -			/* Check Eth descriptor value */ -			if (ctx->max_datagram_size > eth_max_sz) -					ctx->max_datagram_size = eth_max_sz; - -			if (ctx->max_datagram_size > CDC_NCM_MAX_DATAGRAM_SIZE) -				ctx->max_datagram_size = CDC_NCM_MAX_DATAGRAM_SIZE; - -			if (ctx->max_datagram_size < min_dgram_size) -				ctx->max_datagram_size = min_dgram_size; - -			/* if value changed, update device */ -			if (ctx->max_datagram_size != -					le16_to_cpu(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) -					pr_debug("SET_MAX_DGRAM_SIZE failed\n"); -			} -		} -	} +	/* clamp rx_max and tx_max and inform device */ +	cdc_ncm_update_rxtx_max(dev, def_rx, def_tx); -max_dgram_err: -	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++) { @@ -303,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; @@ -322,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) @@ -342,18 +687,9 @@ static void cdc_ncm_free(struct cdc_ncm_ctx *ctx)  	kfree(ctx);  } -static const 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, -}; -  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; @@ -367,23 +703,22 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_  	hrtimer_init(&ctx->tx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);  	ctx->tx_timer.function = &cdc_ncm_tx_timer_cb; -	ctx->bh.data = (unsigned long)ctx; +	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)) { @@ -392,16 +727,18 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_  		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: @@ -410,13 +747,6 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_  			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) -				dev->hard_mtu =	CDC_NCM_MIN_DATAGRAM_SIZE; -			else if (dev->hard_mtu > CDC_NCM_MAX_DATAGRAM_SIZE) -				dev->hard_mtu =	CDC_NCM_MAX_DATAGRAM_SIZE;  			break;  		case USB_CDC_NCM_TYPE: @@ -433,6 +763,14 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_  			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;  		} @@ -444,69 +782,85 @@ advance:  	}  	/* some buggy devices have an IAD but no CDC Union */ -	if (!ctx->union_desc && intf->intf_assoc && intf->intf_assoc->bInterfaceCount == 2) { -		ctx->control = intf; +	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->mbim_desc) && ((ctx->ether_desc == NULL) || (ctx->control != intf)))) +	if (!ctx->data) { +		dev_dbg(&intf->dev, "CDC Union missing and no IAD found\n");  		goto error; +	} +	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; +		} +	} else { +		if (!ctx->ether_desc || !ctx->func_desc) { +			dev_dbg(&intf->dev, "NCM or ECM functional descriptors missing\n"); +			goto error; +		} +	}  	/* claim data interface, if different from control */  	if (ctx->data != ctx->control) {  		temp = usb_driver_claim_interface(driver, ctx->data, dev); -		if (temp) +		if (temp) { +			dev_dbg(&intf->dev, "failed to claim data intf\n");  			goto error; +		}  	}  	iface_no = ctx->data->cur_altsetting->desc.bInterfaceNumber;  	/* reset data interface */  	temp = usb_set_interface(dev->udev, iface_no, 0); -	if (temp) +	if (temp) { +		dev_dbg(&intf->dev, "set interface failed\n");  		goto error2; +	} -	/* initialize data interface */ -	if (cdc_ncm_setup(ctx)) +	/* initialize basic device settings */ +	if (cdc_ncm_init(dev))  		goto error2;  	/* configure data interface */  	temp = usb_set_interface(dev->udev, iface_no, data_altsetting); -	if (temp) +	if (temp) { +		dev_dbg(&intf->dev, "set interface failed\n");  		goto error2; +	} -	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)) +	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; - -	dev->net->ethtool_ops = &cdc_ncm_ethtool_ops; +	}  	usb_set_intfdata(ctx->data, dev);  	usb_set_intfdata(ctx->control, dev); -	usb_set_intfdata(ctx->intf, dev);  	if (ctx->ether_desc) {  		temp = usbnet_get_ethernet_addr(dev, ctx->ether_desc->iMACAddress); -		if (temp) +		if (temp) { +			dev_dbg(&intf->dev, "failed to get mac address\n");  			goto error2; -		dev_info(&dev->udev->dev, "MAC-Address: %pM\n", dev->net->dev_addr); +		} +		dev_info(&intf->dev, "MAC-Address: %pM\n", dev->net->dev_addr);  	} +	/* 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; -	ctx->tx_speed = ctx->rx_speed = 0;  	return 0;  error2: @@ -517,7 +871,7 @@ error2:  error:  	cdc_ncm_free((struct cdc_ncm_ctx *)dev->data[0]);  	dev->data[0] = 0; -	dev_info(&dev->udev->dev, "bind() failure\n"); +	dev_info(&intf->dev, "bind() failure\n");  	return -ENODEV;  }  EXPORT_SYMBOL_GPL(cdc_ncm_bind_common); @@ -553,15 +907,15 @@ void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf)  		ctx->control = NULL;  	} -	usb_set_intfdata(ctx->intf, NULL); +	usb_set_intfdata(intf, NULL);  	cdc_ncm_free(ctx);  }  EXPORT_SYMBOL_GPL(cdc_ncm_unbind); -/* Select the MBIM altsetting iff it is preferred and available, - * returning the number of the corresponding data interface altsetting +/* Return the number of the MBIM control interface altsetting iff it + * is preferred and available,   */ -u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf) +u8 cdc_ncm_select_altsetting(struct usb_interface *intf)  {  	struct usb_host_interface *alt; @@ -580,15 +934,15 @@ u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf)  	 *   the rules given in section 6 (USB Device Model) of this  	 *   specification."  	 */ -	if (prefer_mbim && intf->num_altsetting == 2) { +	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) && -		    !usb_set_interface(dev->udev, -				       intf->cur_altsetting->desc.bInterfaceNumber, -				       CDC_NCM_COMM_ALTSETTING_MBIM)) -			return CDC_NCM_DATA_ALTSETTING_MBIM; +		if (alt && cdc_ncm_comm_intf_is_mbim(alt)) +			return CDC_NCM_COMM_ALTSETTING_MBIM;  	} -	return CDC_NCM_DATA_ALTSETTING_NCM; +	return CDC_NCM_COMM_ALTSETTING_NCM;  }  EXPORT_SYMBOL_GPL(cdc_ncm_select_altsetting); @@ -597,12 +951,11 @@ static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)  	int ret;  	/* MBIM backwards compatible function? */ -	cdc_ncm_select_altsetting(dev, intf); -	if (cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting)) +	if (cdc_ncm_select_altsetting(intf) != CDC_NCM_COMM_ALTSETTING_NCM)  		return -ENODEV; -	/* NCM data altsetting is always 1 */ -	ret = cdc_ncm_bind_common(dev, intf, 1); +	/* The NCM data altsetting is fixed */ +	ret = cdc_ncm_bind_common(dev, intf, CDC_NCM_DATA_ALTSETTING_NCM);  	/*  	 * We should get an event when network connection is "connected" or @@ -645,7 +998,7 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_  	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) < CDC_NCM_NDP_SIZE) +	if ((ctx->tx_max - skb->len - reserve) < ctx->max_ndp_size)  		return NULL;  	/* link to it */ @@ -655,15 +1008,16 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_  		nth16->wNdpIndex = cpu_to_le16(skb->len);  	/* push a new empty NDP */ -	ndp16 = (struct usb_cdc_ncm_ndp16 *)memset(skb_put(skb, CDC_NCM_NDP_SIZE), 0, CDC_NCM_NDP_SIZE); +	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 cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign) +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; @@ -683,11 +1037,11 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign)  	/* allocate a new OUT skb */  	if (!skb_out) { -		skb_out = alloc_skb((ctx->tx_max + 1), GFP_ATOMIC); +		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;  		} @@ -699,6 +1053,9 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign)  		/* count total number of frames in this NTB */  		ctx->tx_curr_frame_num = 0; + +		/* 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++) { @@ -725,17 +1082,18 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign)  				/* 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;  				ready2send = 1; +				ctx->tx_reason_ntb_full++;	/* count reason for transmitting */  			}  			break;  		} @@ -749,12 +1107,14 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign)  		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;  		}  	} @@ -763,7 +1123,7 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign)  	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; @@ -774,7 +1134,7 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign)  		ctx->tx_curr_skb = skb_out;  		goto exit_no_skb; -	} else if ((n < ctx->tx_max_datagrams) && (ready2send == 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; @@ -784,23 +1144,26 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign)  		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 */  	} -	/* -	 * 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 (skb_out->len > CDC_NCM_MIN_TX_PKT) -		/* final zero padding */ -		memset(skb_put(skb_out, ctx->tx_max - skb_out->len), 0, ctx->tx_max - skb_out->len); - -	/* do we need to prevent a ZLP? */ -	if (((skb_out->len % le16_to_cpu(ctx->out_ep->desc.wMaxPacketSize)) == 0) && -	    (skb_out->len < le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize)) && skb_tailroom(skb_out)) +	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 */  	/* set final frame length */ @@ -809,12 +1172,23 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign)  	/* return skb */  	ctx->tx_curr_skb = NULL; -	ctx->netdev->stats.tx_packets += ctx->tx_curr_frame_num; +	dev->net->stats.tx_packets += ctx->tx_curr_frame_num; + +	/* keep private stats: framing overhead and number of NTBs */ +	ctx->tx_overhead += skb_out->len - ctx->tx_curr_frame_payload; +	ctx->tx_ntbs++; + +	/* 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_out;  exit_no_skb: -	/* Start timer, if there is a remaining skb */ -	if (ctx->tx_curr_skb != NULL) +	/* 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;  } @@ -825,7 +1199,7 @@ static void cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx)  	/* start timer, if not already started */  	if (!(hrtimer_active(&ctx->tx_timer) || atomic_read(&ctx->stop)))  		hrtimer_start(&ctx->tx_timer, -				ktime_set(0, CDC_NCM_TIMER_INTERVAL), +				ktime_set(0, ctx->timer_interval),  				HRTIMER_MODE_REL);  } @@ -841,24 +1215,26 @@ static enum hrtimer_restart cdc_ncm_tx_timer_cb(struct hrtimer *timer)  static void cdc_ncm_txpath_bh(unsigned long param)  { -	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)param; +	struct usbnet *dev = (struct usbnet *)param; +	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];  	spin_lock_bh(&ctx->mtx);  	if (ctx->tx_timer_pending != 0) {  		ctx->tx_timer_pending--;  		cdc_ncm_tx_timeout_start(ctx);  		spin_unlock_bh(&ctx->mtx); -	} else if (ctx->netdev != NULL) { +	} else if (dev->net != NULL) { +		ctx->tx_reason_timeout++;	/* count reason for transmitting */  		spin_unlock_bh(&ctx->mtx); -		netif_tx_lock_bh(ctx->netdev); -		usbnet_start_xmit(NULL, ctx->netdev); -		netif_tx_unlock_bh(ctx->netdev); +		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; @@ -875,7 +1251,7 @@ cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)  		goto error;  	spin_lock_bh(&ctx->mtx); -	skb_out = cdc_ncm_fill_tx_frame(ctx, skb, cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN)); +	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; @@ -885,10 +1261,12 @@ error:  	return NULL;  } +EXPORT_SYMBOL_GPL(cdc_ncm_tx_fixup);  /* 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 usbnet *dev = netdev_priv(skb_in->dev);  	struct usb_cdc_ncm_nth16 *nth16;  	int len;  	int ret = -EINVAL; @@ -898,30 +1276,33 @@ int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in)  	if (skb_in->len < (sizeof(struct usb_cdc_ncm_nth16) +  					sizeof(struct usb_cdc_ncm_ndp16))) { -		pr_debug("frame too short\n"); +		netif_dbg(dev, rx_err, dev->net, "frame too short\n");  		goto error;  	}  	nth16 = (struct usb_cdc_ncm_nth16 *)skb_in->data; -	if (le32_to_cpu(nth16->dwSignature) != USB_CDC_NCM_NTH16_SIGN) { -		pr_debug("invalid NTH16 signature <%u>\n", -					le32_to_cpu(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;  	}  	len = le16_to_cpu(nth16->wBlockLength);  	if (len > ctx->rx_max) { -		pr_debug("unsupported NTB block length %u/%u\n", len, -								ctx->rx_max); +		netif_dbg(dev, rx_err, dev->net, +			  "unsupported NTB block length %u/%u\n", len, +			  ctx->rx_max);  		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))) { -		pr_debug("sequence number glitch prev=%d curr=%d\n", -				ctx->rx_seq, 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); @@ -934,18 +1315,20 @@ EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_nth16);  /* 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 ((ndpoffset + sizeof(struct usb_cdc_ncm_ndp16)) > skb_in->len) { -		pr_debug("invalid NDP offset  <%u>\n", ndpoffset); +		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(ndp16->wLength) < USB_CDC_NCM_NDP16_LENGTH_MIN) { -		pr_debug("invalid DPT16 length <%u>\n", -					le32_to_cpu(ndp16->dwSignature)); +		netif_dbg(dev, rx_err, dev->net, "invalid DPT16 length <%u>\n", +			  le16_to_cpu(ndp16->wLength));  		goto error;  	} @@ -954,9 +1337,9 @@ int cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset)  					sizeof(struct usb_cdc_ncm_dpe16));  	ret--; /* we process NDP entries except for the last one */ -	if ((sizeof(struct usb_cdc_ncm_ndp16) + ret * (sizeof(struct usb_cdc_ncm_dpe16))) > -								skb_in->len) { -		pr_debug("Invalid nframes = %d\n", ret); +	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;  	} @@ -965,7 +1348,7 @@ error:  }  EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_ndp16); -static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) +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]; @@ -977,6 +1360,7 @@ static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)  	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) @@ -989,9 +1373,10 @@ next_ndp:  	ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb_in->data + ndpoffset); -	if (le32_to_cpu(ndp16->dwSignature) != USB_CDC_NCM_NDP16_NOCRC_SIGN) { -		pr_debug("invalid DPT16 signature <%u>\n", -			 le32_to_cpu(ndp16->dwSignature)); +	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; @@ -1013,21 +1398,21 @@ next_ndp:  		/* sanity checking */  		if (((offset + len) > skb_in->len) ||  				(len > ctx->rx_max) || (len < ETH_HLEN)) { -			pr_debug("invalid frame detected (ignored)" -					"offset[%u]=%u, length=%u, skb=%p\n", -					x, offset, len, skb_in); +			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 = skb_clone(skb_in, GFP_ATOMIC); +			/* create a fresh copy to reduce truesize */ +			skb = netdev_alloc_skb_ip_align(dev->net,  len);  			if (!skb)  				goto error; -			skb->len = len; -			skb->data = ((u8 *)skb_in->data) + offset; -			skb_set_tail_pointer(skb, len); +			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: @@ -1036,13 +1421,18 @@ err_ndp:  	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, +cdc_ncm_speed_change(struct usbnet *dev,  		     struct usb_cdc_speed_change *data)  {  	uint32_t rx_speed = le32_to_cpu(data->DLBitRRate); @@ -1052,25 +1442,16 @@ cdc_ncm_speed_change(struct cdc_ncm_ctx *ctx,  	 * 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));  	}  } @@ -1086,7 +1467,7 @@ 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, +		cdc_ncm_speed_change(dev,  		      (struct usb_cdc_speed_change *)urb->transfer_buffer);  		return;  	} @@ -1100,15 +1481,10 @@ 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 = le16_to_cpu(event->wValue); - -		printk(KERN_INFO KBUILD_MODNAME ": %s: network connection:" -			" %sconnected\n", -			ctx->netdev->name, ctx->connected ? "" : "dis"); - -		usbnet_link_change(dev, ctx->connected, 0); -		if (!ctx->connected) -			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: @@ -1116,8 +1492,8 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb)  					sizeof(struct usb_cdc_speed_change)))  			set_bit(EVENT_STS_SPLIT, &dev->flags);  		else -			cdc_ncm_speed_change(ctx, -				(struct usb_cdc_speed_change *) &event[1]); +			cdc_ncm_speed_change(dev, +					     (struct usb_cdc_speed_change *)&event[1]);  		break;  	default: @@ -1128,39 +1504,11 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb)  	}  } -static int cdc_ncm_check_connect(struct usbnet *dev) -{ -	struct cdc_ncm_ctx *ctx; - -	ctx = (struct cdc_ncm_ctx *)dev->data[0]; -	if (ctx == NULL) -		return 1;	/* disconnected */ - -	return !ctx->connected; -} - -static int -cdc_ncm_probe(struct usb_interface *udev, const struct usb_device_id *prod) -{ -	return usbnet_probe(udev, prod); -} - -static void cdc_ncm_disconnect(struct usb_interface *intf) -{ -	struct usbnet *dev = usb_get_intfdata(intf); - -	if (dev == NULL) -		return;		/* already disconnected */ - -	usbnet_disconnect(intf); -} -  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, -	.check_connect = cdc_ncm_check_connect,  	.manage_power = usbnet_manage_power,  	.status = cdc_ncm_status,  	.rx_fixup = cdc_ncm_rx_fixup, @@ -1174,7 +1522,6 @@ static const struct driver_info wwan_info = {  			| FLAG_WWAN,  	.bind = cdc_ncm_bind,  	.unbind = cdc_ncm_unbind, -	.check_connect = cdc_ncm_check_connect,  	.manage_power = usbnet_manage_power,  	.status = cdc_ncm_status,  	.rx_fixup = cdc_ncm_rx_fixup, @@ -1188,7 +1535,6 @@ static const struct driver_info wwan_noarp_info = {  			| FLAG_WWAN | FLAG_NOARP,  	.bind = cdc_ncm_bind,  	.unbind = cdc_ncm_unbind, -	.check_connect = cdc_ncm_check_connect,  	.manage_power = usbnet_manage_power,  	.status = cdc_ncm_status,  	.rx_fixup = cdc_ncm_rx_fixup, @@ -1234,17 +1580,6 @@ static const struct usb_device_id cdc_devs[] = {  	  .driver_info = (unsigned long)&wwan_info,  	}, -	/* Huawei NCM devices disguised as vendor specific */ -	{ USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x16), -	  .driver_info = (unsigned long)&wwan_info, -	}, -	{ USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x46), -	  .driver_info = (unsigned long)&wwan_info, -	}, -	{ USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x76), -	  .driver_info = (unsigned long)&wwan_info, -	}, -  	/* Infineon(now Intel) HSPA Modem platform */  	{ USB_DEVICE_AND_INTERFACE_INFO(0x1519, 0x0443,  		USB_CLASS_COMM, @@ -1265,8 +1600,8 @@ 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, diff --git a/drivers/net/usb/cdc_subset.c b/drivers/net/usb/cdc_subset.c index 0d1fe89ae0b..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> diff --git a/drivers/net/usb/cx82310_eth.c b/drivers/net/usb/cx82310_eth.c index 1e207f086b7..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> diff --git a/drivers/net/usb/dm9601.c b/drivers/net/usb/dm9601.c index c6867f926cf..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> @@ -364,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; @@ -468,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 @@ -476,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; -	if (skb_headroom(skb) < DM_TX_OVERHEAD) { +	/* workaround for dm962x errata with tx fifo getting out of +	 * sync if a USB bulk transfer retry happens right after a +	 * packet with odd / maxpacket length by adding up to 3 bytes +	 * padding. +	 */ +	while ((len & 1) || !(len % dev->maxpacket)) +		len++; + +	len -= DM_TX_OVERHEAD; /* hw header doesn't count as part of length */ +	pad = len - skb->len; + +	if (skb_headroom(skb) < DM_TX_OVERHEAD || skb_tailroom(skb) < pad) {  		struct sk_buff *skb2; -		skb2 = skb_copy_expand(skb, DM_TX_OVERHEAD, 0, flags); +		skb2 = skb_copy_expand(skb, DM_TX_OVERHEAD, pad, flags);  		dev_kfree_skb_any(skb);  		skb = skb2;  		if (!skb) @@ -490,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; @@ -543,7 +558,7 @@ static int dm9601_link_reset(struct usbnet *dev)  }  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, @@ -594,6 +609,22 @@ static const struct usb_device_id products[] = {  	 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  }; @@ -612,5 +643,5 @@ static struct usb_driver dm9601_driver = {  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 a7e3f4e55bf..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,6 +84,10 @@ 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 diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index 86292e6aaf4..a4272ed62da 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -185,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) @@ -259,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 { @@ -1202,16 +1199,18 @@ 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); @@ -1219,25 +1218,13 @@ static void hso_std_serial_read_bulk_callback(struct urb *urb)  	if (serial->port.count == 0)  		return; -	if (status == 0) { -		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); -	} 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);  }  /* @@ -1263,14 +1250,6 @@ static	void hso_unthrottle(struct tty_struct *tty)  	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)  { @@ -1306,8 +1285,6 @@ static int hso_serial_open(struct tty_struct *tty, struct file *filp)  		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); @@ -1356,7 +1333,6 @@ static void hso_serial_close(struct tty_struct *tty, struct file *filp)  		if (!usb_gone)  			hso_stop_serial_device(serial->parent);  		tasklet_kill(&serial->unthrottle_tasklet); -		cancel_work_sync(&serial->retry_unthrottle_workqueue);  	}  	if (!usb_gone) @@ -1487,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) @@ -1495,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"); @@ -2014,8 +2000,7 @@ static void ctrl_callback(struct urb *urb)  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) { @@ -2025,29 +2010,28 @@ static int put_rxbuf_data(struct urb *urb, struct hso_serial *serial)  	tty = tty_port_tty_get(&serial->port); +	if (tty && test_bit(TTY_THROTTLED, &tty->flags)) { +		tty_kref_put(tty); +		return -1; +	} +  	/* Push data to tty */ -	write_length_remaining = urb->actual_length - -		serial->curr_rx_urb_offset;  	D1("data to push to tty"); -	while (write_length_remaining) { -		if (tty && test_bit(TTY_THROTTLED, &tty->flags)) { -			tty_kref_put(tty); -			return -1; -		} -		curr_write_len = tty_insert_flip_string(&serial->port, -			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; +	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); -	if (write_length_remaining == 0) { -		serial->curr_rx_urb_offset = 0; -		serial->rx_urb_filled[hso_urb_to_index(serial, urb)] = 0; -	} -	return write_length_remaining; +	serial->rx_urb_filled[hso_urb_to_index(serial, urb)] = 0; + +	return 0;  } @@ -2218,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); @@ -2426,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); 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 ace9e74ffbd..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> diff --git a/drivers/net/usb/ipheth.c b/drivers/net/usb/ipheth.c index ff8594d8dd2..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> @@ -60,6 +59,8 @@  #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 @@ -108,6 +109,14 @@ static struct usb_device_id ipheth_table[] = {  		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) }, @@ -525,7 +534,7 @@ static int ipheth_probe(struct usb_interface *intf,  	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) { diff --git a/drivers/net/usb/kalmia.c b/drivers/net/usb/kalmia.c index 6866eae3e38..5662babf058 100644 --- a/drivers/net/usb/kalmia.c +++ b/drivers/net/usb/kalmia.c @@ -15,7 +15,6 @@   */  #include <linux/module.h> -#include <linux/init.h>  #include <linux/netdevice.h>  #include <linux/etherdevice.h>  #include <linux/ctype.h> diff --git a/drivers/net/usb/kaweth.c b/drivers/net/usb/kaweth.c index afb117c16d2..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> @@ -1173,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); diff --git a/drivers/net/usb/lg-vl600.c b/drivers/net/usb/lg-vl600.c index 808d6506da4..8f37efd2d2f 100644 --- a/drivers/net/usb/lg-vl600.c +++ b/drivers/net/usb/lg-vl600.c @@ -15,8 +15,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/etherdevice.h>  #include <linux/ethtool.h> @@ -211,7 +210,7 @@ static int vl600_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  			 * (0x86dd) so Linux can understand it.  			 */  			if ((buf->data[sizeof(*ethhdr)] & 0xf0) == 0x60) -				ethhdr->h_proto = __constant_htons(ETH_P_IPV6); +				ethhdr->h_proto = htons(ETH_P_IPV6);  		}  		if (count) { diff --git a/drivers/net/usb/mcs7830.c b/drivers/net/usb/mcs7830.c index 03832d3780a..82d844a8ebd 100644 --- a/drivers/net/usb/mcs7830.c +++ b/drivers/net/usb/mcs7830.c @@ -36,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> @@ -117,7 +115,6 @@ enum {  struct mcs7830_data {  	u8 multi_filter[8];  	u8 config; -	u8 link_counter;  };  static const char driver_name[] = "MOSCHIP usb-ethernet driver"; @@ -529,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;  	} @@ -561,26 +559,16 @@ static void mcs7830_status(struct usbnet *dev, struct urb *urb)  {  	u8 *buf = urb->transfer_buffer;  	bool link, link_changed; -	struct mcs7830_data *data = mcs7830_get_data(dev);  	if (urb->actual_length < 16)  		return; -	link = !(buf[1] & 0x20); +	link = !(buf[1] == 0x20);  	link_changed = netif_carrier_ok(dev->net) != link;  	if (link_changed) { -		data->link_counter++; -		/* -		   track link state 20 times to guard against erroneous -		   link state changes reported sometimes by the chip -		 */ -		if (data->link_counter > 20) { -			data->link_counter = 0; -			usbnet_link_change(dev, link, 0); -			netdev_dbg(dev->net, "Link Status is: %d\n", link); -		} -	} else -		data->link_counter = 0; +		usbnet_link_change(dev, link, 0); +		netdev_dbg(dev->net, "Link Status is: %d\n", link); +	}  }  static const struct driver_info moschip_info = { diff --git a/drivers/net/usb/net1080.c b/drivers/net/usb/net1080.c index 93e0716a118..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> @@ -366,6 +364,10 @@ 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)) {  		netdev_dbg(dev->net, "rx framesize %d range %d..%d mtu %d\n",  			   skb->len, dev->net->hard_header_len, dev->hard_mtu, diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c index 03e8a15d7de..f8408021591 100644 --- a/drivers/net/usb/pegasus.c +++ b/drivers/net/usb/pegasus.c @@ -1159,7 +1159,7 @@ 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; diff --git a/drivers/net/usb/plusb.c b/drivers/net/usb/plusb.c index 0fcc8e65a06..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> diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 3d6aaf79d8b..22756db53dc 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -80,10 +80,10 @@ static int qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  {  	__be16 proto; -	/* usbnet rx_complete guarantees that skb->len is at least -	 * hard_header_len, so we can inspect the dest address without -	 * checking skb->len -	 */ +	/* 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); @@ -143,24 +143,28 @@ static const struct net_device_ops qmi_wwan_netdev_ops = {  	.ndo_validate_addr	= eth_validate_addr,  }; -/* using a counter to merge subdriver requests with our own into a combined state */ +/* 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 = 0; +	int rv; -	dev_dbg(&dev->intf->dev, "%s() pmcount=%d, on=%d\n", __func__, atomic_read(&info->pmcount), on); +	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 */ +	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); -		if (rv < 0) -			goto err;  		dev->intf->needs_remote_wakeup = on; -		usb_autopm_put_interface(dev->intf); +		if (!rv) +			usb_autopm_put_interface(dev->intf);  	} -err: -	return rv; +	return 0;  }  static int qmi_wwan_cdc_wdm_manage_power(struct usb_interface *intf, int on) @@ -199,7 +203,8 @@ static int qmi_wwan_register_subdriver(struct usbnet *dev)  	atomic_set(&info->pmcount, 0);  	/* register subdriver */ -	subdriver = usb_cdc_wdm_register(info->control, &dev->status->desc, 4096, &qmi_wwan_cdc_wdm_manage_power); +	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); @@ -228,7 +233,8 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)  	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))); +	BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) < +		      sizeof(struct qmi_wwan_state)));  	/* set up initial state */  	info->control = intf; @@ -250,7 +256,8 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)  				goto err;  			}  			if (h->bLength != sizeof(struct usb_cdc_header_desc)) { -				dev_dbg(&intf->dev, "CDC header len %u\n", h->bLength); +				dev_dbg(&intf->dev, "CDC header len %u\n", +					h->bLength);  				goto err;  			}  			break; @@ -260,7 +267,8 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)  				goto err;  			}  			if (h->bLength != sizeof(struct usb_cdc_union_desc)) { -				dev_dbg(&intf->dev, "CDC union len %u\n", h->bLength); +				dev_dbg(&intf->dev, "CDC union len %u\n", +					h->bLength);  				goto err;  			}  			cdc_union = (struct usb_cdc_union_desc *)buf; @@ -271,15 +279,15 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)  				goto err;  			}  			if (h->bLength != sizeof(struct usb_cdc_ether_desc)) { -				dev_dbg(&intf->dev, "CDC ether len %u\n",  h->bLength); +				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 +		/* 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  		 */ @@ -293,10 +301,14 @@ next_desc:  	/* 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); +		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;  		}  	} @@ -374,8 +386,7 @@ static int qmi_wwan_suspend(struct usb_interface *intf, pm_message_t message)  	struct qmi_wwan_state *info = (void *)&dev->data;  	int ret; -	/* -	 * Both usbnet_suspend() and subdriver->suspend() MUST return 0 +	/* 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.  	 */ @@ -383,7 +394,8 @@ static int qmi_wwan_suspend(struct usb_interface *intf, pm_message_t message)  	if (ret < 0)  		goto err; -	if (intf == info->control && info->subdriver && info->subdriver->suspend) +	if (intf == info->control && info->subdriver && +	    info->subdriver->suspend)  		ret = info->subdriver->suspend(intf, message);  	if (ret < 0)  		usbnet_resume(intf); @@ -396,14 +408,15 @@ 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); +	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) +	if (ret < 0 && callsub)  		info->subdriver->suspend(intf, PMSG_SUSPEND);  err:  	return ret; @@ -487,6 +500,13 @@ static const struct usb_device_id products[] = {  		                              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, @@ -647,8 +667,25 @@ static const struct usb_device_id products[] = {  	{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)}, @@ -699,22 +736,50 @@ static const struct usb_device_id products[] = {  	{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 */ @@ -776,7 +841,8 @@ static const struct usb_device_id products[] = {  };  MODULE_DEVICE_TABLE(usb, products); -static int qmi_wwan_probe(struct usb_interface *intf, const struct usb_device_id *prod) +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; diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index f3fce412c0c..3eab74c7c55 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -1,5 +1,5 @@  /* - *  Copyright (c) 2013 Realtek Semiconductor Corp. All rights reserved. + *  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 @@ -7,7 +7,6 @@   *   */ -#include <linux/init.h>  #include <linux/signal.h>  #include <linux/slab.h>  #include <linux/module.h> @@ -22,11 +21,12 @@  #include <linux/list.h>  #include <linux/ip.h>  #include <linux/ipv6.h> +#include <net/ip6_checksum.h>  /* Version Information */ -#define DRIVER_VERSION "v1.01.0 (2013/08/12)" +#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 Based USB 2.0 Ethernet Adapters" +#define DRIVER_DESC "Realtek RTL8152/RTL8153 Based USB Ethernet Adapters"  #define MODULENAME "r8152"  #define R8152_PHY_ID		32 @@ -39,21 +39,32 @@  #define PLA_RXFIFO_CTRL2	0xc0a8  #define PLA_FMC			0xc0b4  #define PLA_CFG_WOL		0xc0b6 +#define PLA_TEREDO_CFG		0xc0bc  #define PLA_MAR			0xcd00 +#define PLA_BACKUP		0xd000  #define PAL_BDC_CR		0xd1a0 +#define PLA_TEREDO_TIMER	0xd2cc +#define PLA_REALWOW_TIMER	0xd2e8  #define PLA_LEDSEL		0xdd90  #define PLA_LED_FEATURE		0xdd92  #define PLA_PHYAR		0xde00 +#define PLA_BOOT_CTRL		0xe004  #define PLA_GPHY_INTR_IMR	0xe022  #define PLA_EEE_CR		0xe040  #define PLA_EEEP_CR		0xe080  #define PLA_MAC_PWR_CTRL	0xe0c0 +#define PLA_MAC_PWR_CTRL2	0xe0ca +#define PLA_MAC_PWR_CTRL3	0xe0cc +#define PLA_MAC_PWR_CTRL4	0xe0ce +#define PLA_WDT6_CTRL		0xe428  #define PLA_TCR0		0xe610  #define PLA_TCR1		0xe612  #define PLA_TXFIFO_CTRL		0xe618 -#define PLA_RSTTELLY		0xe800 +#define PLA_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 @@ -61,7 +72,7 @@  #define PLA_MISC_0		0xe858  #define PLA_MISC_1		0xe85a  #define PLA_OCP_GPHY_BASE	0xe86c -#define PLA_TELLYCNT		0xe890 +#define PLA_TALLYCNT		0xe890  #define PLA_SFF_STS_7		0xe8de  #define PLA_PHYSTATUS		0xe908  #define PLA_BP_BA		0xfc26 @@ -73,16 +84,25 @@  #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 @@ -92,14 +112,30 @@  #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 @@ -116,14 +152,17 @@  #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 @@ -131,6 +170,9 @@  /* 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 @@ -138,6 +180,9 @@  /* PLA_TCR1 */  #define VERSION_MASK		0x7cf0 +/* PLA_RSTTALLY */ +#define TALLY_RESET		0x0001 +  /* PLA_CR */  #define CR_RST			0x10  #define CR_RE			0x08 @@ -168,10 +213,23 @@  /* 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 */ @@ -185,6 +243,25 @@  #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 @@ -199,16 +276,21 @@  #define EEE_RX_EN		0x0001  #define EEE_TX_EN		0x0002 +/* PLA_BOOT_CTRL */ +#define AUTOLOAD_DONE		0x0002 +  /* USB_DEV_STAT */  #define STAT_SPEED_MASK		0x0006  #define STAT_SPEED_HIGH		0x0000 -#define STAT_SPEED_FULL		0x0001 +#define STAT_SPEED_FULL		0x0002  /* USB_TX_AGG */  #define TX_AGG_MAX_THRESHOLD	0x03  /* USB_RX_BUF_TH */ -#define RX_BUF_THR		0x7a120180 +#define RX_THR_SUPPER		0x0c350180 +#define RX_THR_HIGH		0x7a120180 +#define RX_THR_SLOW		0xffff0180  /* USB_TX_DMA */  #define TEST_MODE_DISABLE	0x00000001 @@ -218,17 +300,55 @@  #define POWER_CUT		0x0100  /* USB_PM_CTRL_STATUS */ -#define RWSUME_INDICATE		0x0001 +#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 @@ -263,7 +383,36 @@  #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, @@ -273,6 +422,9 @@ enum rtl_register_content {  #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 @@ -297,36 +449,80 @@ enum rtl8152_flags {  	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 { -	u32 opts1; +	__le32 opts1;  #define RX_LEN_MASK			0x7fff -	u32 opts2; -	u32 opts3; -	u32 opts4; -	u32 opts5; -	u32 opts6; + +	__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 { -	u32 opts1; +	__le32 opts1;  #define TX_FS			(1 << 31) /* First segment of a packet */  #define TX_LS			(1 << 30) /* Final segment of a packet */ -#define TX_LEN_MASK		0x3ffff +#define GTSENDV4		(1 << 28) +#define GTSENDV6		(1 << 27) +#define GTTCPHO_SHIFT		18 +#define GTTCPHO_MAX		0x7fU +#define TX_LEN_MAX		0x3ffffU -	u32 opts2; +	__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; @@ -363,8 +559,20 @@ struct r8152 {  	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; @@ -374,7 +582,17 @@ struct r8152 {  enum rtl_version {  	RTL_VER_UNKNOWN = 0,  	RTL_VER_01, -	RTL_VER_02 +	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). @@ -383,6 +601,9 @@ enum rtl_version {  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)  { @@ -409,25 +630,24 @@ int set_registers(struct r8152 *tp, u16 value, u16 index, u16 size, void *data)  	int ret;  	void *tmp; -	tmp = kmalloc(size, GFP_KERNEL); +	tmp = kmemdup(data, size, GFP_KERNEL);  	if (!tmp)  		return -ENOMEM; -	memcpy(tmp, data, size); -  	ret = usb_control_msg(tp->udev, usb_sndctrlpipe(tp->udev, 0),  			       RTL8152_REQ_SET_REGS, RTL8152_REQT_WRITE,  			       value, index, tmp, size, 500);  	kfree(tmp); +  	return ret;  }  static int generic_ocp_read(struct r8152 *tp, u16 index, u16 size,  				void *data, u16 type)  { -	u16	limit = 64; -	int	ret = 0; +	u16 limit = 64; +	int ret = 0;  	if (test_bit(RTL8152_UNPLUG, &tp->flags))  		return -ENODEV; @@ -466,9 +686,9 @@ static int generic_ocp_read(struct r8152 *tp, u16 index, u16 size,  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; +	int ret; +	u16 byteen_start, byteen_end, byen; +	u16 limit = 512;  	if (test_bit(RTL8152_UNPLUG, &tp->flags))  		return -ENODEV; @@ -652,55 +872,77 @@ static void ocp_write_byte(struct r8152 *tp, u16 type, u16 index, u32 data)  	generic_ocp_write(tp, index, byen, sizeof(tmp), &tmp, type);  } -static void r8152_mdio_write(struct r8152 *tp, u32 reg_addr, u32 value) +static u16 ocp_reg_read(struct r8152 *tp, u16 addr)  { -	u32	ocp_data; -	int	i; +	u16 ocp_base, ocp_index; -	ocp_data = PHYAR_FLAG | ((reg_addr & 0x1f) << 16) | -		   (value & 0xffff); +	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_write_dword(tp, MCU_TYPE_PLA, PLA_PHYAR, ocp_data); +	ocp_index = (addr & 0x0fff) | 0xb000; +	return ocp_read_word(tp, MCU_TYPE_PLA, ocp_index); +} -	for (i = 20; i > 0; i--) { -		udelay(25); -		ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_PHYAR); -		if (!(ocp_data & PHYAR_FLAG)) -			break; +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;  	} -	udelay(20); + +	ocp_index = (addr & 0x0fff) | 0xb000; +	ocp_write_word(tp, MCU_TYPE_PLA, ocp_index, data);  } -static int r8152_mdio_read(struct r8152 *tp, u32 reg_addr) +static inline void r8152_mdio_write(struct r8152 *tp, u32 reg_addr, u32 value)  { -	u32	ocp_data; -	int	i; - -	ocp_data = (reg_addr & 0x1f) << 16; -	ocp_write_dword(tp, MCU_TYPE_PLA, PLA_PHYAR, ocp_data); +	ocp_reg_write(tp, OCP_BASE_MII + reg_addr * 2, value); +} -	for (i = 20; i > 0; i--) { -		udelay(25); -		ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_PHYAR); -		if (ocp_data & PHYAR_FLAG) -			break; -	} -	udelay(20); +static inline int r8152_mdio_read(struct r8152 *tp, u32 reg_addr) +{ +	return ocp_reg_read(tp, OCP_BASE_MII + reg_addr * 2); +} -	if (!(ocp_data & PHYAR_FLAG)) -		return -EAGAIN; +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); +} -	return (u16)(ocp_data & 0xffff); +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; -	return r8152_mdio_read(tp, reg); +	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 @@ -708,24 +950,18 @@ void write_mii_word(struct net_device *netdev, int phy_id, int reg, int val)  {  	struct r8152 *tp = netdev_priv(netdev); -	if (phy_id != R8152_PHY_ID) +	if (test_bit(RTL8152_UNPLUG, &tp->flags))  		return; -	r8152_mdio_write(tp, reg, val); -} +	if (phy_id != R8152_PHY_ID) +		return; -static void ocp_reg_write(struct r8152 *tp, u16 addr, u16 data) -{ -	u16 ocp_base, ocp_index; +	if (usb_autopm_get_interface(tp->intf) < 0) +		return; -	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; -	} +	r8152_mdio_write(tp, reg, val); -	ocp_index = (addr & 0x0fff) | 0xb000; -	ocp_write_word(tp, MCU_TYPE_PLA, ocp_index, data); +	usb_autopm_put_interface(tp->intf);  }  static @@ -734,11 +970,26 @@ 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 (pla_ocp_read(tp, PLA_IDR, sizeof(node_id), node_id) < 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 { +	} 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);  	} @@ -761,15 +1012,9 @@ static int rtl8152_set_mac_address(struct net_device *netdev, void *p)  	return 0;  } -static struct net_device_stats *rtl8152_get_stats(struct net_device *dev) -{ -	return &dev->stats; -} -  static void read_bulk_callback(struct urb *urb)  {  	struct net_device *netdev; -	unsigned long flags;  	int status = urb->status;  	struct rx_agg *agg;  	struct r8152 *tp; @@ -796,14 +1041,16 @@ static void read_bulk_callback(struct urb *urb)  	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_irqsave(&tp->rx_lock, flags); +		spin_lock(&tp->rx_lock);  		list_add_tail(&agg->list, &tp->rx_done); -		spin_unlock_irqrestore(&tp->rx_lock, flags); +		spin_unlock(&tp->rx_lock);  		tasklet_schedule(&tp->tl);  		return;  	case -ESHUTDOWN: @@ -813,10 +1060,12 @@ static void read_bulk_callback(struct urb *urb)  	case -ENOENT:  		return;	/* the urb is in unlink state */  	case -ETIME: -		pr_warn_ratelimited("may be reset is needed?..\n"); +		if (net_ratelimit()) +			netdev_warn(netdev, "maybe reset is needed?\n");  		break;  	default: -		pr_warn_ratelimited("Rx status %d\n", status); +		if (net_ratelimit()) +			netdev_warn(netdev, "Rx status %d\n", status);  		break;  	} @@ -824,9 +1073,9 @@ static void read_bulk_callback(struct urb *urb)  	if (result == -ENODEV) {  		netif_device_detach(tp->netdev);  	} else if (result) { -		spin_lock_irqsave(&tp->rx_lock, flags); +		spin_lock(&tp->rx_lock);  		list_add_tail(&agg->list, &tp->rx_done); -		spin_unlock_irqrestore(&tp->rx_lock, flags); +		spin_unlock(&tp->rx_lock);  		tasklet_schedule(&tp->tl);  	}  } @@ -834,7 +1083,7 @@ static void read_bulk_callback(struct urb *urb)  static void write_bulk_callback(struct urb *urb)  {  	struct net_device_stats *stats; -	unsigned long flags; +	struct net_device *netdev;  	struct tx_agg *agg;  	struct r8152 *tp;  	int status = urb->status; @@ -847,20 +1096,24 @@ static void write_bulk_callback(struct urb *urb)  	if (!tp)  		return; -	stats = rtl8152_get_stats(tp->netdev); +	netdev = tp->netdev; +	stats = &netdev->stats;  	if (status) { -		pr_warn_ratelimited("Tx status %d\n", 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_irqsave(&tp->tx_lock, flags); +	spin_lock(&tp->tx_lock);  	list_add_tail(&agg->list, &tp->tx_free); -	spin_unlock_irqrestore(&tp->tx_lock, flags); +	spin_unlock(&tp->tx_lock); -	if (!netif_carrier_ok(tp->netdev)) +	usb_autopm_put_interface_async(tp->intf); + +	if (!netif_carrier_ok(netdev))  		return;  	if (!test_bit(WORK_ENABLE, &tp->flags)) @@ -876,7 +1129,7 @@ static void write_bulk_callback(struct urb *urb)  static void intr_callback(struct urb *urb)  {  	struct r8152 *tp; -	__u16 *d; +	__le16 *d;  	int status = urb->status;  	int res; @@ -926,17 +1179,17 @@ resubmit:  		netif_device_detach(tp->netdev);  	else if (res)  		netif_err(tp, intr, tp->netdev, -			"can't resubmit intr, status %d\n", res); +			  "can't resubmit intr, status %d\n", res);  }  static inline void *rx_agg_align(void *data)  { -	return (void *)ALIGN((uintptr_t)data, 8); +	return (void *)ALIGN((uintptr_t)data, RX_ALIGN);  }  static inline void *tx_agg_align(void *data)  { -	return (void *)ALIGN((uintptr_t)data, 4); +	return (void *)ALIGN((uintptr_t)data, TX_ALIGN);  }  static void free_all_mem(struct r8152 *tp) @@ -944,40 +1197,28 @@ static void free_all_mem(struct r8152 *tp)  	int i;  	for (i = 0; i < RTL8152_MAX_RX; i++) { -		if (tp->rx_info[i].urb) { -			usb_free_urb(tp->rx_info[i].urb); -			tp->rx_info[i].urb = NULL; -		} +		usb_free_urb(tp->rx_info[i].urb); +		tp->rx_info[i].urb = NULL; -		if (tp->rx_info[i].buffer) { -			kfree(tp->rx_info[i].buffer); -			tp->rx_info[i].buffer = NULL; -			tp->rx_info[i].head = 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++) { -		if (tp->tx_info[i].urb) { -			usb_free_urb(tp->tx_info[i].urb); -			tp->tx_info[i].urb = NULL; -		} +		usb_free_urb(tp->tx_info[i].urb); +		tp->tx_info[i].urb = NULL; -		if (tp->tx_info[i].buffer) { -			kfree(tp->tx_info[i].buffer); -			tp->tx_info[i].buffer = NULL; -			tp->tx_info[i].head = NULL; -		} +		kfree(tp->tx_info[i].buffer); +		tp->tx_info[i].buffer = NULL; +		tp->tx_info[i].head = NULL;  	} -	if (tp->intr_urb) { -		usb_free_urb(tp->intr_urb); -		tp->intr_urb = NULL; -	} +	usb_free_urb(tp->intr_urb); +	tp->intr_urb = NULL; -	if (tp->intr_buff) { -		kfree(tp->intr_buff); -		tp->intr_buff = NULL; -	} +	kfree(tp->intr_buff); +	tp->intr_buff = NULL;  }  static int alloc_all_mem(struct r8152 *tp) @@ -1005,7 +1246,8 @@ static int alloc_all_mem(struct r8152 *tp)  		if (buf != rx_agg_align(buf)) {  			kfree(buf); -			buf = kmalloc_node(rx_buf_sz + 8, GFP_KERNEL, node); +			buf = kmalloc_node(rx_buf_sz + RX_ALIGN, GFP_KERNEL, +					   node);  			if (!buf)  				goto err1;  		} @@ -1030,7 +1272,8 @@ static int alloc_all_mem(struct r8152 *tp)  		if (buf != tx_agg_align(buf)) {  			kfree(buf); -			buf = kmalloc_node(rx_buf_sz + 4, GFP_KERNEL, node); +			buf = kmalloc_node(rx_buf_sz + TX_ALIGN, GFP_KERNEL, +					   node);  			if (!buf)  				goto err1;  		} @@ -1075,6 +1318,9 @@ 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; @@ -1088,24 +1334,138 @@ static struct tx_agg *r8152_get_tx_agg(struct r8152 *tp)  	return agg;  } -static void -r8152_tx_csum(struct r8152 *tp, struct tx_desc *desc, struct sk_buff *skb) +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)  { -	memset(desc, 0, sizeof(*desc)); +	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; -	desc->opts1 = cpu_to_le32((skb->len & TX_LEN_MASK) | TX_FS | TX_LS); +		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; +		} -	if (skb->ip_summed == CHECKSUM_PARTIAL) { -		__be16 protocol; +		opts1 |= transport_offset << GTTCPHO_SHIFT; +		opts2 |= min(mss, MSS_MAX) << MSS_SHIFT; +	} else if (skb->ip_summed == CHECKSUM_PARTIAL) {  		u8 ip_protocol; -		u32 opts2 = 0; -		if (skb->protocol == htons(ETH_P_8021Q)) -			protocol = vlan_eth_hdr(skb)->h_vlan_encapsulated_proto; -		else -			protocol = skb->protocol; +		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 (protocol) { +		switch (get_protocol(skb)) {  		case htons(ETH_P_IP):  			opts2 |= IPV4_CS;  			ip_protocol = ip_hdr(skb)->protocol; @@ -1121,81 +1481,169 @@ r8152_tx_csum(struct r8152 *tp, struct tx_desc *desc, struct sk_buff *skb)  			break;  		} -		if (ip_protocol == IPPROTO_TCP) { +		if (ip_protocol == IPPROTO_TCP)  			opts2 |= TCP_CS; -			opts2 |= (skb_transport_offset(skb) & 0x7fff) << 17; -		} else if (ip_protocol == IPPROTO_UDP) { +		else if (ip_protocol == IPPROTO_UDP)  			opts2 |= UDP_CS; -		} else { +		else  			WARN_ON_ONCE(1); -		} -		desc->opts2 = cpu_to_le32(opts2); +		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)  { -	u32 remain; +	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 - sizeof(struct tx_desc); +	remain = rx_buf_sz; -	while (remain >= ETH_ZLEN) { +	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(&tp->tx_queue); +		skb = __skb_dequeue(&skb_head);  		if (!skb)  			break; -		len = skb->len; -		if (remain < len) { -			skb_queue_head(&tp->tx_queue, skb); +		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); -		r8152_tx_csum(tp, tx_desc, skb); -		memcpy(tx_data, skb->data, len); -		agg->skb_num++; +		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); -		tx_data = tx_agg_align(tx_data + len); -		remain = rx_buf_sz - sizeof(*tx_desc) - -			 (u32)((void *)tx_data - agg->head); +		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); -	return usb_submit_urb(agg->urb, GFP_ATOMIC); +	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; +	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_for_each_safe(cursor, next, &tp->rx_done) { +	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; -		unsigned pkt_len;  		int len_used = 0;  		struct urb *urb;  		u8 *rx_data;  		int ret;  		list_del_init(cursor); -		spin_unlock_irqrestore(&tp->rx_lock, flags);  		agg = list_entry(cursor, struct rx_agg, list);  		urb = agg->urb; @@ -1204,50 +1652,55 @@ static void rx_bottom(struct r8152 *tp)  		rx_desc = agg->head;  		rx_data = agg->head; -		pkt_len = le32_to_cpu(rx_desc->opts1) & RX_LEN_MASK; -		len_used += sizeof(struct rx_desc) + pkt_len; +		len_used += sizeof(struct rx_desc); -		while (urb->actual_length >= len_used) { +		while (urb->actual_length > len_used) {  			struct net_device *netdev = tp->netdev; -			struct net_device_stats *stats; +			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; -			stats = rtl8152_get_stats(netdev); +			len_used += pkt_len; +			if (urb->actual_length < len_used) +				break; -			pkt_len -= 4; /* CRC */ +			pkt_len -= CRC_SIZE;  			rx_data += sizeof(struct rx_desc);  			skb = netdev_alloc_skb_ip_align(netdev, pkt_len);  			if (!skb) {  				stats->rx_dropped++; -				break; +				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_rx(skb); +			netif_receive_skb(skb);  			stats->rx_packets++;  			stats->rx_bytes += pkt_len; -			rx_data = rx_agg_align(rx_data + pkt_len + 4); +find_next_rx: +			rx_data = rx_agg_align(rx_data + pkt_len + CRC_SIZE);  			rx_desc = (struct rx_desc *)rx_data; -			pkt_len = le32_to_cpu(rx_desc->opts1) & RX_LEN_MASK;  			len_used = (int)(rx_data - (u8 *)agg->head); -			len_used += sizeof(struct rx_desc) + pkt_len; +			len_used += sizeof(struct rx_desc);  		}  submit:  		ret = r8152_submit_rx(tp, agg, GFP_ATOMIC); -		spin_lock_irqsave(&tp->rx_lock, flags);  		if (ret && ret != -ENODEV) { -			list_add_tail(&agg->list, next); +			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);  		}  	} -	spin_unlock_irqrestore(&tp->rx_lock, flags);  }  static void tx_bottom(struct r8152 *tp) @@ -1266,19 +1719,18 @@ static void tx_bottom(struct r8152 *tp)  		res = r8152_tx_agg_fill(tp, agg);  		if (res) { -			struct net_device_stats *stats; -			struct net_device *netdev; -			unsigned long flags; - -			netdev = tp->netdev; -			stats = rtl8152_get_stats(netdev); +			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); @@ -1318,12 +1770,32 @@ int r8152_submit_rx(struct r8152 *tp, struct rx_agg *agg, gfp_t mem_flags)  	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"); +	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);  } @@ -1381,56 +1853,24 @@ static void _rtl8152_set_rx_mode(struct net_device *netdev)  }  static netdev_tx_t rtl8152_start_xmit(struct sk_buff *skb, -					    struct net_device *netdev) +					struct net_device *netdev)  {  	struct r8152 *tp = netdev_priv(netdev); -	struct net_device_stats *stats = rtl8152_get_stats(netdev); -	unsigned long flags; -	struct tx_agg *agg = NULL; -	struct tx_desc *tx_desc; -	unsigned int len; -	u8 *tx_data; -	int res;  	skb_tx_timestamp(skb); -	/* If tx_queue is not empty, it means at least one previous packt */ -	/* is waiting for sending. Don't send current one before it.      */ -	if (skb_queue_empty(&tp->tx_queue)) -		agg = r8152_get_tx_agg(tp); - -	if (!agg) { -		skb_queue_tail(&tp->tx_queue, skb); -		return NETDEV_TX_OK; -	} +	skb_queue_tail(&tp->tx_queue, skb); -	tx_desc = (struct tx_desc *)agg->head; -	tx_data = agg->head + sizeof(*tx_desc); -	agg->skb_num = agg->skb_len = 0; - -	len = skb->len; -	r8152_tx_csum(tp, tx_desc, skb); -	memcpy(tx_data, skb->data, len); -	dev_kfree_skb_any(skb); -	agg->skb_num++; -	agg->skb_len += len; -	usb_fill_bulk_urb(agg->urb, tp->udev, usb_sndbulkpipe(tp->udev, 2), -			  agg->head, len + sizeof(*tx_desc), -			  (usb_complete_t)write_bulk_callback, agg); -	res = usb_submit_urb(agg->urb, GFP_ATOMIC); -	if (res) { -		/* Can we get/handle EPIPE here? */ -		if (res == -ENODEV) { -			netif_device_detach(tp->netdev); +	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 { -			netif_warn(tp, tx_err, netdev, -				   "failed tx_urb %d\n", res); -			stats->tx_dropped++; -			spin_lock_irqsave(&tp->tx_lock, flags); -			list_add_tail(&agg->list, &tp->tx_free); -			spin_unlock_irqrestore(&tp->tx_lock, flags); +			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;  } @@ -1459,15 +1899,22 @@ static void rtl8152_nic_reset(struct r8152 *tp)  	}  } +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 int rtl8152_enable(struct r8152 *tp) +static void rtl_set_eee_plus(struct r8152 *tp)  {  	u32 ocp_data; -	int i, ret;  	u8 speed;  	speed = rtl8152_get_speed(tp); @@ -1480,6 +1927,24 @@ static int rtl8152_enable(struct r8152 *tp)  		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); @@ -1487,9 +1952,7 @@ static int rtl8152_enable(struct r8152 *tp)  	ocp_data |= CR_RE | CR_TE;  	ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, ocp_data); -	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MISC_1); -	ocp_data &= ~RXDY_GATED_EN; -	ocp_write_word(tp, MCU_TYPE_PLA, PLA_MISC_1, ocp_data); +	rxdy_gated_en(tp, false);  	INIT_LIST_HEAD(&tp->rx_done);  	ret = 0; @@ -1501,28 +1964,73 @@ static int rtl8152_enable(struct r8152 *tp)  	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)  { -	struct net_device_stats *stats = rtl8152_get_stats(tp->netdev); -	struct sk_buff *skb;  	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); -	while ((skb = skb_dequeue(&tp->tx_queue))) { -		dev_kfree_skb(skb); -		stats->tx_dropped++; -	} +	rtl_drop_queued_tx(tp);  	for (i = 0; i < RTL8152_MAX_TX; i++)  		usb_kill_urb(tp->tx_info[i].urb); -	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MISC_1); -	ocp_data |= RXDY_GATED_EN; -	ocp_write_word(tp, MCU_TYPE_PLA, PLA_MISC_1, ocp_data); +	rxdy_gated_en(tp, true);  	for (i = 0; i < 1000; i++) {  		ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); @@ -1543,18 +2051,212 @@ static void rtl8152_disable(struct r8152 *tp)  	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; +	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); -	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MISC_1); -	ocp_data |= RXDY_GATED_EN; -	ocp_write_word(tp, MCU_TYPE_PLA, PLA_MISC_1, ocp_data); +	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); @@ -1590,9 +2292,8 @@ static void r8152b_exit_oob(struct r8152 *tp)  	/* rx share fifo credit full threshold */  	ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL0, RXFIFO_THR1_NORMAL); -	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_DEV_STAT); -	ocp_data &= STAT_SPEED_MASK; -	if (ocp_data == STAT_SPEED_FULL) { +	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); @@ -1610,7 +2311,7 @@ static void r8152b_exit_oob(struct r8152 *tp)  	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_BUF_THR); +	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); @@ -1627,8 +2328,8 @@ static void r8152b_exit_oob(struct r8152 *tp)  static void r8152b_enter_oob(struct r8152 *tp)  { -	u32	ocp_data; -	int	i; +	u32 ocp_data; +	int i;  	ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);  	ocp_data &= ~NOW_IS_OOB; @@ -1660,10 +2361,6 @@ static void r8152b_enter_oob(struct r8152 *tp)  	ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, RTL8152_RMS); -	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CFG_WOL); -	ocp_data |= MAGIC_EN; -	ocp_write_word(tp, MCU_TYPE_PLA, PLA_CFG_WOL, ocp_data); -  	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CPCR);  	ocp_data |= CPCR_RX_VLAN;  	ocp_write_word(tp, MCU_TYPE_PLA, PLA_CPCR, ocp_data); @@ -1676,38 +2373,263 @@ static void r8152b_enter_oob(struct r8152 *tp)  	ocp_data |= NOW_IS_OOB | DIS_MCU_CLROOB;  	ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); -	ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CONFIG5, LAN_WAKE_EN); +	rxdy_gated_en(tp, false); -	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MISC_1); -	ocp_data &= ~RXDY_GATED_EN; -	ocp_write_word(tp, MCU_TYPE_PLA, PLA_MISC_1, ocp_data); +	ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); +	ocp_data |= RCR_APM | RCR_AM | RCR_AB; +	ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); +} + +static void r8153_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 r8152b_disable_aldps(struct r8152 *tp) +static void r8153_disable_aldps(struct r8152 *tp)  { -	ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPDNPS | LINKENA | DIS_SDSAVE); +	u16 data; + +	data = ocp_reg_read(tp, OCP_POWER_CFG); +	data &= ~EN_ALDPS; +	ocp_reg_write(tp, OCP_POWER_CFG, data);  	msleep(20);  } -static inline void r8152b_enable_aldps(struct r8152 *tp) +static void r8153_enable_aldps(struct r8152 *tp)  { -	ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPWRSAVE | ENPDNPS | -					    LINKENA | DIS_SDSAVE); +	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; +	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) { @@ -1716,6 +2638,9 @@ static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u16 speed, u8 duplex)  		} 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; @@ -1737,6 +2662,16 @@ static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u16 speed, u8 duplex)  				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; @@ -1745,9 +2680,26 @@ static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u16 speed, u8 duplex)  		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; @@ -1755,17 +2707,31 @@ out:  static void rtl8152_down(struct r8152 *tp)  { -	u32	ocp_data; - -	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_UPS_CTRL); -	ocp_data &= ~POWER_CUT; -	ocp_write_word(tp, MCU_TYPE_USB, USB_UPS_CTRL, ocp_data); +	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; @@ -1776,7 +2742,7 @@ static void set_carrier(struct r8152 *tp)  	if (speed & LINK_STATUS) {  		if (!(tp->speed & LINK_STATUS)) { -			rtl8152_enable(tp); +			tp->rtl_ops.enable(tp);  			set_bit(RTL8152_SET_RX_MODE, &tp->flags);  			netif_carrier_on(netdev);  		} @@ -1784,7 +2750,7 @@ static void set_carrier(struct r8152 *tp)  		if (tp->speed & LINK_STATUS) {  			netif_carrier_off(netdev);  			tasklet_disable(&tp->tl); -			rtl8152_disable(tp); +			tp->rtl_ops.disable(tp);  			tasklet_enable(&tp->tl);  		}  	} @@ -1795,6 +2761,9 @@ 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; @@ -1807,8 +2776,17 @@ static void rtl_work_func_t(struct work_struct *work)  	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: -	return; +	usb_autopm_put_interface(tp->intf);  }  static int rtl8152_open(struct net_device *netdev) @@ -1816,21 +2794,47 @@ static int rtl8152_open(struct net_device *netdev)  	struct r8152 *tp = netdev_priv(netdev);  	int res = 0; -	res = usb_submit_urb(tp->intr_urb, GFP_KERNEL); -	if (res) { -		if (res == -ENODEV) -			netif_device_detach(tp->netdev); -		netif_warn(tp, ifup, netdev, -			"intr_urb submit failed: %d\n", res); -		return res; +	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);  	} -	rtl8152_set_speed(tp, AUTONEG_ENABLE, SPEED_100, DUPLEX_FULL); +	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;  } @@ -1839,35 +2843,39 @@ static int rtl8152_close(struct net_device *netdev)  	struct r8152 *tp = netdev_priv(netdev);  	int res = 0; -	usb_kill_urb(tp->intr_urb);  	clear_bit(WORK_ENABLE, &tp->flags); +	usb_kill_urb(tp->intr_urb);  	cancel_delayed_work_sync(&tp->schedule);  	netif_stop_queue(netdev); -	tasklet_disable(&tp->tl); -	rtl8152_disable(tp); -	tasklet_enable(&tp->tl); -	return res; -} +	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); +		} -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); +		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; +	u32 ocp_data;  	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEE_CR);  	ocp_data |= EEE_RX_EN | EEE_TX_EN; @@ -1888,6 +2896,22 @@ static void r8152b_enable_eee(struct r8152 *tp)  	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; @@ -1897,18 +2921,21 @@ static void r8152b_enable_fc(struct r8152 *tp)  	r8152_mdio_write(tp, MII_ADVERTISE, anar);  } -static void r8152b_hw_phy_cfg(struct r8152 *tp) +static void rtl_tally_reset(struct r8152 *tp)  { -	r8152_mdio_write(tp, MII_BMCR, BMCR_ANENABLE); -	r8152b_disable_aldps(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; -	int i; -	rtl_clear_bp(tp); +	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); @@ -1916,17 +2943,7 @@ static void r8152b_init(struct r8152 *tp)  		ocp_write_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE, ocp_data);  	} -	r8152b_hw_phy_cfg(tp); - -	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_UPS_CTRL); -	ocp_data &= ~POWER_CUT; -	ocp_write_word(tp, MCU_TYPE_USB, USB_UPS_CTRL, ocp_data); - -	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_PM_CTRL_STATUS); -	ocp_data &= ~RWSUME_INDICATE; -	ocp_write_word(tp, MCU_TYPE_USB, USB_PM_CTRL_STATUS, ocp_data); - -	r8152b_exit_oob(tp); +	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; @@ -1942,14 +2959,7 @@ static void r8152b_init(struct r8152 *tp)  	r8152b_enable_eee(tp);  	r8152b_enable_aldps(tp);  	r8152b_enable_fc(tp); - -	r8152_mdio_write(tp, MII_BMCR, BMCR_RESET | BMCR_ANENABLE | -				       BMCR_ANRESTART); -	for (i = 0; i < 100; i++) { -		udelay(100); -		if (!(r8152_mdio_read(tp, MII_BMCR) & BMCR_RESET)) -			break; -	} +	rtl_tally_reset(tp);  	/* enable rx aggregation */  	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL); @@ -1957,21 +2967,94 @@ static void r8152b_init(struct r8152 *tp)  	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); -	netif_device_detach(tp->netdev); +	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); -		tasklet_disable(&tp->tl); +		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); +		}  	} -	rtl8152_down(tp); -  	return 0;  } @@ -1979,20 +3062,77 @@ static int rtl8152_resume(struct usb_interface *intf)  {  	struct r8152 *tp = usb_get_intfdata(intf); -	r8152b_init(tp); -	netif_device_attach(tp->netdev); +	if (!test_bit(SELECTIVE_SUSPEND, &tp->flags)) { +		tp->rtl_ops.init(tp); +		netif_device_attach(tp->netdev); +	} +  	if (netif_running(tp->netdev)) { -		rtl8152_set_speed(tp, AUTONEG_ENABLE, SPEED_100, DUPLEX_FULL); +		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); -		tasklet_enable(&tp->tl);  	}  	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)  { @@ -2017,8 +3157,81 @@ int rtl8152_get_settings(struct net_device *netdev, struct ethtool_cmd *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); +} -	return rtl8152_set_speed(tp, cmd->autoneg, cmd->speed, cmd->duplex); +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 = { @@ -2026,13 +3239,27 @@ static struct ethtool_ops ops = {  	.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 = 0; +	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: @@ -2055,6 +3282,9 @@ static int rtl8152_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)  		res = -EOPNOTSUPP;  	} +	usb_autopm_put_interface(tp->intf); + +out:  	return res;  } @@ -2086,6 +3316,18 @@ static void r8152b_get_version(struct r8152 *tp)  	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); @@ -2093,6 +3335,80 @@ static void r8152b_get_version(struct r8152 *tp)  	}  } +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)  { @@ -2106,9 +3422,10 @@ static int rtl8152_probe(struct usb_interface *intf,  		return -ENODEV;  	} +	usb_reset_device(udev);  	netdev = alloc_etherdev(sizeof(struct r8152));  	if (!netdev) { -		dev_err(&intf->dev, "Out of memory"); +		dev_err(&intf->dev, "Out of memory\n");  		return -ENOMEM;  	} @@ -2116,18 +3433,29 @@ static int rtl8152_probe(struct usb_interface *intf,  	tp = netdev_priv(netdev);  	tp->msg_enable = 0x7FFF; -	tasklet_init(&tp->tl, bottom_half, (unsigned long)tp); -	INIT_DELAYED_WORK(&tp->schedule, rtl_work_func_t); -  	tp->udev = udev;  	tp->netdev = netdev;  	tp->intf = intf; + +	ret = rtl_ops_init(tp, id); +	if (ret) +		goto out; + +	tasklet_init(&tp->tl, bottom_half, (unsigned long)tp); +	INIT_DELAYED_WORK(&tp->schedule, rtl_work_func_t); +  	netdev->netdev_ops = &rtl8152_netdev_ops;  	netdev->watchdog_timeo = RTL8152_TX_TIMEOUT; -	netdev->features |= NETIF_F_IP_CSUM; -	netdev->hw_features = NETIF_F_IP_CSUM; -	SET_ETHTOOL_OPS(netdev, &ops); +	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; @@ -2137,23 +3465,27 @@ static int rtl8152_probe(struct usb_interface *intf,  	tp->mii.phy_id = R8152_PHY_ID;  	tp->mii.supports_gmii = 0; +	intf->needs_remote_wakeup = 1; +  	r8152b_get_version(tp); -	r8152b_init(tp); +	tp->rtl_ops.init(tp);  	set_ethernet_addr(tp); -	ret = alloc_all_mem(tp); -	if (ret) -		goto out; -  	usb_set_intfdata(intf, tp);  	ret = register_netdev(netdev);  	if (ret != 0) { -		netif_err(tp, probe, netdev, "couldn't register the device"); +		netif_err(tp, probe, netdev, "couldn't register the device\n");  		goto out1;  	} -	netif_info(tp, probe, netdev, "%s", DRIVER_VERSION); +	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; @@ -2164,21 +3496,6 @@ out:  	return ret;  } -static void rtl8152_unload(struct r8152 *tp) -{ -	u32	ocp_data; - -	if (tp->version != RTL_VER_01) { -		ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_UPS_CTRL); -		ocp_data |= POWER_CUT; -		ocp_write_word(tp, MCU_TYPE_USB, USB_UPS_CTRL, ocp_data); -	} - -	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_PM_CTRL_STATUS); -	ocp_data &= ~RWSUME_INDICATE; -	ocp_write_word(tp, MCU_TYPE_USB, USB_PM_CTRL_STATUS, ocp_data); -} -  static void rtl8152_disconnect(struct usb_interface *intf)  {  	struct r8152 *tp = usb_get_intfdata(intf); @@ -2188,8 +3505,7 @@ static void rtl8152_disconnect(struct usb_interface *intf)  		set_bit(RTL8152_UNPLUG, &tp->flags);  		tasklet_kill(&tp->tl);  		unregister_netdev(tp->netdev); -		rtl8152_unload(tp); -		free_all_mem(tp); +		tp->rtl_ops.unload(tp);  		free_netdev(tp->netdev);  	}  } @@ -2197,6 +3513,8 @@ static void rtl8152_disconnect(struct usb_interface *intf)  /* 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)},  	{}  }; @@ -2210,6 +3528,8 @@ static struct usb_driver rtl8152_driver = {  	.suspend =	rtl8152_suspend,  	.resume =	rtl8152_resume,  	.reset_resume =	rtl8152_resume, +	.supports_autosuspend = 1, +	.disable_hub_initiated_lpm = 1,  };  module_usb_driver(rtl8152_driver); diff --git a/drivers/net/usb/r815x.c b/drivers/net/usb/r815x.c deleted file mode 100644 index 2df2f4fb42a..00000000000 --- a/drivers/net/usb/r815x.c +++ /dev/null @@ -1,256 +0,0 @@ -#include <linux/module.h> -#include <linux/netdevice.h> -#include <linux/mii.h> -#include <linux/usb.h> -#include <linux/usb/cdc.h> -#include <linux/usb/usbnet.h> - -#define RTL815x_REQT_READ	0xc0 -#define RTL815x_REQT_WRITE	0x40 -#define RTL815x_REQ_GET_REGS	0x05 -#define RTL815x_REQ_SET_REGS	0x05 - -#define MCU_TYPE_PLA		0x0100 -#define OCP_BASE		0xe86c -#define BASE_MII		0xa400 - -#define BYTE_EN_DWORD		0xff -#define BYTE_EN_WORD		0x33 -#define BYTE_EN_BYTE		0x11 - -#define R815x_PHY_ID		32 -#define REALTEK_VENDOR_ID	0x0bda - - -static int pla_read_word(struct usb_device *udev, u16 index) -{ -	int ret; -	u8 shift = index & 2; -	__le32 *tmp; - -	tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); -	if (!tmp) -		return -ENOMEM; - -	index &= ~3; - -	ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), -			      RTL815x_REQ_GET_REGS, RTL815x_REQT_READ, -			      index, MCU_TYPE_PLA, tmp, sizeof(*tmp), 500); -	if (ret < 0) -		goto out2; - -	ret = __le32_to_cpu(*tmp); -	ret >>= (shift * 8); -	ret &= 0xffff; - -out2: -	kfree(tmp); -	return ret; -} - -static int pla_write_word(struct usb_device *udev, u16 index, u32 data) -{ -	__le32 *tmp; -	u32 mask = 0xffff; -	u16 byen = BYTE_EN_WORD; -	u8 shift = index & 2; -	int ret; - -	tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); -	if (!tmp) -		return -ENOMEM; - -	data &= mask; - -	if (shift) { -		byen <<= shift; -		mask <<= (shift * 8); -		data <<= (shift * 8); -		index &= ~3; -	} - -	ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), -			      RTL815x_REQ_GET_REGS, RTL815x_REQT_READ, -			      index, MCU_TYPE_PLA, tmp, sizeof(*tmp), 500); -	if (ret < 0) -		goto out3; - -	data |= __le32_to_cpu(*tmp) & ~mask; -	*tmp = __cpu_to_le32(data); - -	ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), -			      RTL815x_REQ_SET_REGS, RTL815x_REQT_WRITE, -			      index, MCU_TYPE_PLA | byen, tmp, sizeof(*tmp), -			      500); - -out3: -	kfree(tmp); -	return ret; -} - -static int ocp_reg_read(struct usbnet *dev, u16 addr) -{ -	u16 ocp_base, ocp_index; -	int ret; - -	ocp_base = addr & 0xf000; -	ret = pla_write_word(dev->udev, OCP_BASE, ocp_base); -	if (ret < 0) -		goto out; - -	ocp_index = (addr & 0x0fff) | 0xb000; -	ret = pla_read_word(dev->udev, ocp_index); - -out: -	return ret; -} - -static int ocp_reg_write(struct usbnet *dev, u16 addr, u16 data) -{ -	u16 ocp_base, ocp_index; -	int ret; - -	ocp_base = addr & 0xf000; -	ret = pla_write_word(dev->udev, OCP_BASE, ocp_base); -	if (ret < 0) -		goto out1; - -	ocp_index = (addr & 0x0fff) | 0xb000; -	ret = pla_write_word(dev->udev, ocp_index, data); - -out1: -	return ret; -} - -static int r815x_mdio_read(struct net_device *netdev, int phy_id, int reg) -{ -	struct usbnet *dev = netdev_priv(netdev); -	int ret; - -	if (phy_id != R815x_PHY_ID) -		return -EINVAL; - -	if (usb_autopm_get_interface(dev->intf) < 0) -		return -ENODEV; - -	ret = ocp_reg_read(dev, BASE_MII + reg * 2); - -	usb_autopm_put_interface(dev->intf); -	return ret; -} - -static -void r815x_mdio_write(struct net_device *netdev, int phy_id, int reg, int val) -{ -	struct usbnet *dev = netdev_priv(netdev); - -	if (phy_id != R815x_PHY_ID) -		return; - -	if (usb_autopm_get_interface(dev->intf) < 0) -		return; - -	ocp_reg_write(dev, BASE_MII + reg * 2, val); - -	usb_autopm_put_interface(dev->intf); -} - -static int r8153_bind(struct usbnet *dev, struct usb_interface *intf) -{ -	int status; - -	status = usbnet_cdc_bind(dev, intf); -	if (status < 0) -		return status; - -	dev->mii.dev = dev->net; -	dev->mii.mdio_read = r815x_mdio_read; -	dev->mii.mdio_write = r815x_mdio_write; -	dev->mii.phy_id_mask = 0x3f; -	dev->mii.reg_num_mask = 0x1f; -	dev->mii.phy_id = R815x_PHY_ID; -	dev->mii.supports_gmii = 1; - -	return status; -} - -static int r8152_bind(struct usbnet *dev, struct usb_interface *intf) -{ -	int status; - -	status = usbnet_cdc_bind(dev, intf); -	if (status < 0) -		return status; - -	dev->mii.dev = dev->net; -	dev->mii.mdio_read = r815x_mdio_read; -	dev->mii.mdio_write = r815x_mdio_write; -	dev->mii.phy_id_mask = 0x3f; -	dev->mii.reg_num_mask = 0x1f; -	dev->mii.phy_id = R815x_PHY_ID; -	dev->mii.supports_gmii = 0; - -	return status; -} - -static const struct driver_info r8152_info = { -	.description =	"RTL8152 ECM Device", -	.flags =	FLAG_ETHER | FLAG_POINTTOPOINT, -	.bind =		r8152_bind, -	.unbind =	usbnet_cdc_unbind, -	.status =	usbnet_cdc_status, -	.manage_power =	usbnet_manage_power, -}; - -static const struct driver_info r8153_info = { -	.description =	"RTL8153 ECM Device", -	.flags =	FLAG_ETHER | FLAG_POINTTOPOINT, -	.bind =		r8153_bind, -	.unbind =	usbnet_cdc_unbind, -	.status =	usbnet_cdc_status, -	.manage_power =	usbnet_manage_power, -}; - -static const struct usb_device_id products[] = { -{ -	USB_DEVICE_AND_INTERFACE_INFO(REALTEK_VENDOR_ID, 0x8152, USB_CLASS_COMM, -			USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), -#if defined(CONFIG_USB_RTL8152) || defined(CONFIG_USB_RTL8152_MODULE) -	.driver_info = 0, -#else -	.driver_info = (unsigned long) &r8152_info, -#endif -}, - -{ -	USB_DEVICE_AND_INTERFACE_INFO(REALTEK_VENDOR_ID, 0x8153, USB_CLASS_COMM, -			USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), -#if defined(CONFIG_USB_RTL8153) || defined(CONFIG_USB_RTL8153_MODULE) -	.driver_info = 0, -#else -	.driver_info = (unsigned long) &r8153_info, -#endif -}, - -	{ },		/* END */ -}; -MODULE_DEVICE_TABLE(usb, products); - -static struct usb_driver r815x_driver = { -	.name =		"r815x", -	.id_table =	products, -	.probe =	usbnet_probe, -	.disconnect =	usbnet_disconnect, -	.suspend =	usbnet_suspend, -	.resume =	usbnet_resume, -	.reset_resume =	usbnet_resume, -	.supports_autosuspend = 1, -	.disable_hub_initiated_lpm = 1, -}; - -module_usb_driver(r815x_driver); - -MODULE_AUTHOR("Hayes Wang"); -MODULE_DESCRIPTION("Realtek USB ECM device"); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c index cc49aac7022..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> @@ -494,6 +492,10 @@ 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; diff --git a/drivers/net/usb/rtl8150.c b/drivers/net/usb/rtl8150.c index 6cbdac67f3a..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> @@ -879,7 +878,7 @@ static int rtl8150_probe(struct usb_interface *intf,  	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)) { diff --git a/drivers/net/usb/sierra_net.c b/drivers/net/usb/sierra_net.c index a79e9d33492..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" diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c index 66ebbacf066..d9e7892262f 100644 --- a/drivers/net/usb/smsc75xx.c +++ b/drivers/net/usb/smsc75xx.c @@ -13,14 +13,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/module.h>  #include <linux/kmod.h> -#include <linux/init.h>  #include <linux/netdevice.h>  #include <linux/etherdevice.h>  #include <linux/ethtool.h> @@ -2108,6 +2106,10 @@ static void smsc75xx_rx_csum_offload(struct usbnet *dev, struct sk_buff *skb,  static int smsc75xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  { +	/* This check is no longer done by usbnet */ +	if (skb->len < dev->net->hard_header_len) +		return 0; +  	while (skb->len > 0) {  		u32 rx_cmd_a, rx_cmd_b, align_count, size;  		struct sk_buff *ax_skb; diff --git a/drivers/net/usb/smsc75xx.h b/drivers/net/usb/smsc75xx.h index 67eba39e6ee..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/>.   *   *****************************************************************************/ diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index 3f38ba868f6..d07bf4cb893 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -13,14 +13,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/module.h>  #include <linux/kmod.h> -#include <linux/init.h>  #include <linux/netdevice.h>  #include <linux/etherdevice.h>  #include <linux/ethtool.h> @@ -1716,6 +1714,18 @@ static int smsc95xx_resume(struct usb_interface *intf)  	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); @@ -1725,6 +1735,10 @@ static void smsc95xx_rx_csum_offload(struct sk_buff *skb)  static int smsc95xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  { +	/* This check is no longer done by usbnet */ +	if (skb->len < dev->net->hard_header_len) +		return 0; +  	while (skb->len > 0) {  		u32 header, align_count;  		struct sk_buff *ax_skb; @@ -2002,7 +2016,7 @@ static struct usb_driver smsc95xx_driver = {  	.probe		= usbnet_probe,  	.suspend	= smsc95xx_suspend,  	.resume		= smsc95xx_resume, -	.reset_resume	= smsc95xx_resume, +	.reset_resume	= smsc95xx_reset_resume,  	.disconnect	= usbnet_disconnect,  	.disable_hub_initiated_lpm = 1,  	.supports_autosuspend = 1, diff --git a/drivers/net/usb/smsc95xx.h b/drivers/net/usb/smsc95xx.h index f360ee37255..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/>.   *   *****************************************************************************/ diff --git a/drivers/net/usb/sr9700.c b/drivers/net/usb/sr9700.c index 7ec3e0ee078..99b69af1427 100644 --- a/drivers/net/usb/sr9700.c +++ b/drivers/net/usb/sr9700.c @@ -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> 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 bf94e10a37c..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/>.   */  /* @@ -204,9 +203,6 @@ static void intr_complete (struct urb *urb)  		break;  	} -	if (!netif_running (dev->net)) -		return; -  	status = usb_submit_urb (urb, GFP_ATOMIC);  	if (status != 0)  		netif_err(dev, timer, dev->net, @@ -546,17 +542,19 @@ static inline void rx_process (struct usbnet *dev, struct sk_buff *skb)  	}  	// 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"); -	dev->net->stats.rx_errors++;  done:  	skb_queue_tail(&dev->done, skb);  } @@ -578,13 +576,6 @@ static void rx_complete (struct urb *urb)  	switch (urb_status) {  	/* success */  	case 0: -		if (skb->len < dev->net->hard_header_len) { -			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 @@ -761,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); @@ -782,15 +771,14 @@ 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); @@ -800,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) { @@ -826,6 +816,9 @@ int usbnet_stop (struct net_device *net)  	dev->flags = 0;  	del_timer_sync (&dev->delay);  	tasklet_kill (&dev->bh); +	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); @@ -1248,7 +1241,7 @@ static int build_dma_sg(const struct sk_buff *skb, struct urb *urb)  		return -ENOMEM;  	urb->num_sgs = num_sgs; -	sg_init_table(urb->sg, urb->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); @@ -1446,11 +1439,12 @@ static void usbnet_bh (unsigned long param)  	/* restart RX again after disabling due to high error rate */  	clear_bit(EVENT_RX_KILL, &dev->flags); -	// 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); -		} +	/* 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) && @@ -1589,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); @@ -1688,8 +1683,10 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)  	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) +		if (!dev->padding_pkt) { +			status = -ENOMEM;  			goto out4; +		}  	}  	status = register_netdev (net); @@ -1798,9 +1795,10 @@ int usbnet_resume (struct usb_interface *intf)  		spin_unlock_irq(&dev->txq.lock);  		if (test_bit(EVENT_DEV_OPEN, &dev->flags)) { -			/* handle remote wakeup ASAP */ -			if (!dev->wait && -				netif_device_present(dev->net) && +			/* 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); diff --git a/drivers/net/usb/zaurus.c b/drivers/net/usb/zaurus.c index 35c90307d47..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>  | 
