diff options
Diffstat (limited to 'drivers/net/usb')
| -rw-r--r-- | drivers/net/usb/Kconfig | 12 | ||||
| -rw-r--r-- | drivers/net/usb/Makefile | 1 | ||||
| -rw-r--r-- | drivers/net/usb/asix.c | 28 | ||||
| -rw-r--r-- | drivers/net/usb/catc.c | 4 | ||||
| -rw-r--r-- | drivers/net/usb/cdc_ncm.c | 85 | ||||
| -rw-r--r-- | drivers/net/usb/dm9601.c | 6 | ||||
| -rw-r--r-- | drivers/net/usb/hso.c | 7 | ||||
| -rw-r--r-- | drivers/net/usb/kalmia.c | 392 | ||||
| -rw-r--r-- | drivers/net/usb/plusb.c | 32 | ||||
| -rw-r--r-- | drivers/net/usb/rndis_host.c | 39 | ||||
| -rw-r--r-- | drivers/net/usb/rtl8150.c | 11 | ||||
| -rw-r--r-- | drivers/net/usb/smsc75xx.c | 131 | ||||
| -rw-r--r-- | drivers/net/usb/smsc95xx.c | 90 | ||||
| -rw-r--r-- | drivers/net/usb/usbnet.c | 8 | ||||
| -rw-r--r-- | drivers/net/usb/zaurus.c | 10 | 
15 files changed, 601 insertions, 255 deletions
| diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index 3ec22c307797..84d4608153c9 100644 --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -258,7 +258,7 @@ config USB_NET_NET1080  	  optionally with LEDs that indicate traffic  config USB_NET_PLUSB -	tristate "Prolific PL-2301/2302 based cables" +	tristate "Prolific PL-2301/2302/25A1 based cables"  	# if the handshake/init/reset problems, from original 'plusb',  	# are ever resolved ... then remove "experimental"  	depends on USB_USBNET && EXPERIMENTAL @@ -385,6 +385,16 @@ config USB_NET_CX82310_ETH  	  router with USB ethernet port. This driver is for routers only,  	  it will not work with ADSL modems (use cxacru driver instead). +config USB_NET_KALMIA +	tristate "Samsung Kalmia based LTE USB modem" +	depends on USB_USBNET +	help +	  Choose this option if you have a Samsung Kalmia based USB modem +	  as Samsung GT-B3730. + +	  To compile this driver as a module, choose M here: the +	  module will be called kalmia. +  config USB_HSO  	tristate "Option USB High Speed Mobile Devices"  	depends on USB && RFKILL diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile index c7ec8a5f0a90..c203fa21f6b1 100644 --- a/drivers/net/usb/Makefile +++ b/drivers/net/usb/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_USB_NET_MCS7830)	+= mcs7830.o  obj-$(CONFIG_USB_USBNET)	+= usbnet.o  obj-$(CONFIG_USB_NET_INT51X1)	+= int51x1.o  obj-$(CONFIG_USB_CDC_PHONET)	+= cdc-phonet.o +obj-$(CONFIG_USB_NET_KALMIA)	+= kalmia.o  obj-$(CONFIG_USB_IPHETH)	+= ipheth.o  obj-$(CONFIG_USB_SIERRA_NET)	+= sierra_net.o  obj-$(CONFIG_USB_NET_CX82310_ETH)	+= cx82310_eth.o diff --git a/drivers/net/usb/asix.c b/drivers/net/usb/asix.c index 6140b56cce53..6998aa6b7bb7 100644 --- a/drivers/net/usb/asix.c +++ b/drivers/net/usb/asix.c @@ -847,7 +847,7 @@ static void ax88172_set_multicast(struct net_device *net)  static int ax88172_link_reset(struct usbnet *dev)  {  	u8 mode; -	struct ethtool_cmd ecmd; +	struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET };  	mii_check_media(&dev->mii, 1, 1);  	mii_ethtool_gset(&dev->mii, &ecmd); @@ -856,8 +856,8 @@ static int ax88172_link_reset(struct usbnet *dev)  	if (ecmd.duplex != DUPLEX_FULL)  		mode |= ~AX88172_MEDIUM_FD; -	netdev_dbg(dev->net, "ax88172_link_reset() speed: %d duplex: %d setting mode to 0x%04x\n", -		   ecmd.speed, ecmd.duplex, mode); +	netdev_dbg(dev->net, "ax88172_link_reset() speed: %u duplex: %d setting mode to 0x%04x\n", +		   ethtool_cmd_speed(&ecmd), ecmd.duplex, mode);  	asix_write_medium_mode(dev, mode); @@ -947,20 +947,20 @@ static const struct ethtool_ops ax88772_ethtool_ops = {  static int ax88772_link_reset(struct usbnet *dev)  {  	u16 mode; -	struct ethtool_cmd ecmd; +	struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET };  	mii_check_media(&dev->mii, 1, 1);  	mii_ethtool_gset(&dev->mii, &ecmd);  	mode = AX88772_MEDIUM_DEFAULT; -	if (ecmd.speed != SPEED_100) +	if (ethtool_cmd_speed(&ecmd) != SPEED_100)  		mode &= ~AX_MEDIUM_PS;  	if (ecmd.duplex != DUPLEX_FULL)  		mode &= ~AX_MEDIUM_FD; -	netdev_dbg(dev->net, "ax88772_link_reset() speed: %d duplex: %d setting mode to 0x%04x\n", -		   ecmd.speed, ecmd.duplex, mode); +	netdev_dbg(dev->net, "ax88772_link_reset() speed: %u duplex: %d setting mode to 0x%04x\n", +		   ethtool_cmd_speed(&ecmd), ecmd.duplex, mode);  	asix_write_medium_mode(dev, mode); @@ -1173,18 +1173,20 @@ static int marvell_led_status(struct usbnet *dev, u16 speed)  static int ax88178_link_reset(struct usbnet *dev)  {  	u16 mode; -	struct ethtool_cmd ecmd; +	struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET };  	struct asix_data *data = (struct asix_data *)&dev->data; +	u32 speed;  	netdev_dbg(dev->net, "ax88178_link_reset()\n");  	mii_check_media(&dev->mii, 1, 1);  	mii_ethtool_gset(&dev->mii, &ecmd);  	mode = AX88178_MEDIUM_DEFAULT; +	speed = ethtool_cmd_speed(&ecmd); -	if (ecmd.speed == SPEED_1000) +	if (speed == SPEED_1000)  		mode |= AX_MEDIUM_GM; -	else if (ecmd.speed == SPEED_100) +	else if (speed == SPEED_100)  		mode |= AX_MEDIUM_PS;  	else  		mode &= ~(AX_MEDIUM_PS | AX_MEDIUM_GM); @@ -1196,13 +1198,13 @@ static int ax88178_link_reset(struct usbnet *dev)  	else  		mode &= ~AX_MEDIUM_FD; -	netdev_dbg(dev->net, "ax88178_link_reset() speed: %d duplex: %d setting mode to 0x%04x\n", -		   ecmd.speed, ecmd.duplex, mode); +	netdev_dbg(dev->net, "ax88178_link_reset() speed: %u duplex: %d setting mode to 0x%04x\n", +		   speed, ecmd.duplex, mode);  	asix_write_medium_mode(dev, mode);  	if (data->phymode == PHY_MODE_MARVELL && data->ledmode) -		marvell_led_status(dev, ecmd.speed); +		marvell_led_status(dev, speed);  	return 0;  } diff --git a/drivers/net/usb/catc.c b/drivers/net/usb/catc.c index 97687d335903..8056f8a27c6a 100644 --- a/drivers/net/usb/catc.c +++ b/drivers/net/usb/catc.c @@ -495,7 +495,7 @@ static void catc_ctrl_run(struct catc *catc)  	if (!q->dir && q->buf && q->len)  		memcpy(catc->ctrl_buf, q->buf, q->len); -	if ((status = usb_submit_urb(catc->ctrl_urb, GFP_KERNEL))) +	if ((status = usb_submit_urb(catc->ctrl_urb, GFP_ATOMIC)))  		err("submit(ctrl_urb) status %d", status);  } @@ -686,7 +686,7 @@ static int catc_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)  	cmd->supported = SUPPORTED_10baseT_Half | SUPPORTED_TP;  	cmd->advertising = ADVERTISED_10baseT_Half | ADVERTISED_TP; -	cmd->speed = SPEED_10; +	ethtool_cmd_speed_set(cmd, SPEED_10);  	cmd->duplex = DUPLEX_HALF;  	cmd->port = PORT_TP;   	cmd->phy_address = 0; diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 1033ef6476a4..f33ca6aa29e9 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -54,13 +54,13 @@  #include <linux/usb/usbnet.h>  #include <linux/usb/cdc.h> -#define	DRIVER_VERSION				"23-Apr-2011" +#define	DRIVER_VERSION				"01-June-2011"  /* CDC NCM subclass 3.2.1 */  #define USB_CDC_NCM_NDP16_LENGTH_MIN		0x10  /* Maximum NTB length */ -#define	CDC_NCM_NTB_MAX_SIZE_TX			(16384 + 4) /* bytes, must be short terminated */ +#define	CDC_NCM_NTB_MAX_SIZE_TX			16384	/* bytes */  #define	CDC_NCM_NTB_MAX_SIZE_RX			16384	/* bytes */  /* Minimum value for MaxDatagramSize, ch. 6.2.9 */ @@ -134,8 +134,6 @@ struct cdc_ncm_ctx {  	u16 tx_ndp_modulus;  	u16 tx_seq;  	u16 connected; -	u8 data_claimed; -	u8 control_claimed;  };  static void cdc_ncm_tx_timeout(unsigned long arg); @@ -460,17 +458,6 @@ static void cdc_ncm_free(struct cdc_ncm_ctx *ctx)  	del_timer_sync(&ctx->tx_timer); -	if (ctx->data_claimed) { -		usb_set_intfdata(ctx->data, NULL); -		usb_driver_release_interface(driver_of(ctx->intf), ctx->data); -	} - -	if (ctx->control_claimed) { -		usb_set_intfdata(ctx->control, NULL); -		usb_driver_release_interface(driver_of(ctx->intf), -								ctx->control); -	} -  	if (ctx->tx_rem_skb != NULL) {  		dev_kfree_skb_any(ctx->tx_rem_skb);  		ctx->tx_rem_skb = NULL; @@ -495,7 +482,7 @@ static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)  	ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);  	if (ctx == NULL) -		goto error; +		return -ENODEV;  	memset(ctx, 0, sizeof(*ctx)); @@ -568,46 +555,36 @@ advance:  	/* check if we got everything */  	if ((ctx->control == NULL) || (ctx->data == NULL) || -	    (ctx->ether_desc == NULL)) +	    (ctx->ether_desc == NULL) || (ctx->control != intf))  		goto error;  	/* claim interfaces, if any */ -	if (ctx->data != intf) { -		temp = usb_driver_claim_interface(driver, ctx->data, dev); -		if (temp) -			goto error; -		ctx->data_claimed = 1; -	} - -	if (ctx->control != intf) { -		temp = usb_driver_claim_interface(driver, ctx->control, dev); -		if (temp) -			goto error; -		ctx->control_claimed = 1; -	} +	temp = usb_driver_claim_interface(driver, ctx->data, dev); +	if (temp) +		goto error;  	iface_no = ctx->data->cur_altsetting->desc.bInterfaceNumber;  	/* reset data interface */  	temp = usb_set_interface(dev->udev, iface_no, 0);  	if (temp) -		goto error; +		goto error2;  	/* initialize data interface */  	if (cdc_ncm_setup(ctx)) -		goto error; +		goto error2;  	/* configure data interface */  	temp = usb_set_interface(dev->udev, iface_no, 1);  	if (temp) -		goto error; +		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)) -		goto error; +		goto error2;  	dev->net->ethtool_ops = &cdc_ncm_ethtool_ops; @@ -617,7 +594,7 @@ advance:  	temp = usbnet_get_ethernet_addr(dev, ctx->ether_desc->iMACAddress);  	if (temp) -		goto error; +		goto error2;  	dev_info(&dev->udev->dev, "MAC-Address: "  				"0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x\n", @@ -642,38 +619,38 @@ advance:  	ctx->tx_speed = ctx->rx_speed = 0;  	return 0; +error2: +	usb_set_intfdata(ctx->control, NULL); +	usb_set_intfdata(ctx->data, NULL); +	usb_driver_release_interface(driver, ctx->data);  error:  	cdc_ncm_free((struct cdc_ncm_ctx *)dev->data[0]);  	dev->data[0] = 0; -	dev_info(&dev->udev->dev, "Descriptor failure\n"); +	dev_info(&dev->udev->dev, "bind() failure\n");  	return -ENODEV;  }  static void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf)  {  	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; -	struct usb_driver *driver; +	struct usb_driver *driver = driver_of(intf);  	if (ctx == NULL)  		return;		/* no setup */ -	driver = driver_of(intf); - -	usb_set_intfdata(ctx->data, NULL); -	usb_set_intfdata(ctx->control, NULL); -	usb_set_intfdata(ctx->intf, NULL); - -	/* release interfaces, if any */ -	if (ctx->data_claimed) { +	/* disconnect master --> disconnect slave */ +	if (intf == ctx->control && ctx->data) { +		usb_set_intfdata(ctx->data, NULL);  		usb_driver_release_interface(driver, ctx->data); -		ctx->data_claimed = 0; -	} +		ctx->data = NULL; -	if (ctx->control_claimed) { +	} else if (intf == ctx->data && ctx->control) { +		usb_set_intfdata(ctx->control, NULL);  		usb_driver_release_interface(driver, ctx->control); -		ctx->control_claimed = 0; +		ctx->control = NULL;  	} +	usb_set_intfdata(ctx->intf, NULL);  	cdc_ncm_free(ctx);  } @@ -722,7 +699,7 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb)  	} else {  		/* reset variables */ -		skb_out = alloc_skb(ctx->tx_max, GFP_ATOMIC); +		skb_out = alloc_skb((ctx->tx_max + 1), GFP_ATOMIC);  		if (skb_out == NULL) {  			if (skb != NULL) {  				dev_kfree_skb_any(skb); @@ -861,8 +838,11 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb)  	/* store last offset */  	last_offset = offset; -	if ((last_offset < ctx->tx_max) && ((last_offset % -			le16_to_cpu(ctx->out_ep->desc.wMaxPacketSize)) == 0)) { +	if (((last_offset < ctx->tx_max) && ((last_offset % +			le16_to_cpu(ctx->out_ep->desc.wMaxPacketSize)) == 0)) || +	    (((last_offset == ctx->tx_max) && ((ctx->tx_max % +		le16_to_cpu(ctx->out_ep->desc.wMaxPacketSize)) == 0)) && +		(ctx->tx_max < le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize)))) {  		/* force short packet */  		*(((u8 *)skb_out->data) + last_offset) = 0;  		last_offset++; @@ -1254,6 +1234,7 @@ static struct usb_driver cdc_ncm_driver = {  	.disconnect = cdc_ncm_disconnect,  	.suspend = usbnet_suspend,  	.resume = usbnet_resume, +	.reset_resume =	usbnet_resume,  	.supports_autosuspend = 1,  }; diff --git a/drivers/net/usb/dm9601.c b/drivers/net/usb/dm9601.c index 5002f5be47be..1d93133e9b74 100644 --- a/drivers/net/usb/dm9601.c +++ b/drivers/net/usb/dm9601.c @@ -599,13 +599,13 @@ static void dm9601_status(struct usbnet *dev, struct urb *urb)  static int dm9601_link_reset(struct usbnet *dev)  { -	struct ethtool_cmd ecmd; +	struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET };  	mii_check_media(&dev->mii, 1, 1);  	mii_ethtool_gset(&dev->mii, &ecmd); -	netdev_dbg(dev->net, "link_reset() speed: %d duplex: %d\n", -		   ecmd.speed, ecmd.duplex); +	netdev_dbg(dev->net, "link_reset() speed: %u duplex: %d\n", +		   ethtool_cmd_speed(&ecmd), ecmd.duplex);  	return 0;  } diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index 387ca43f26f4..304fe78ff60e 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -2421,10 +2421,8 @@ static void hso_free_net_device(struct hso_device *hso_dev)  	remove_net_device(hso_net->parent); -	if (hso_net->net) { +	if (hso_net->net)  		unregister_netdev(hso_net->net); -		free_netdev(hso_net->net); -	}  	/* start freeing */  	for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) { @@ -2436,6 +2434,9 @@ static void hso_free_net_device(struct hso_device *hso_dev)  	kfree(hso_net->mux_bulk_tx_buf);  	hso_net->mux_bulk_tx_buf = NULL; +	if (hso_net->net) +		free_netdev(hso_net->net); +  	kfree(hso_dev);  } diff --git a/drivers/net/usb/kalmia.c b/drivers/net/usb/kalmia.c new file mode 100644 index 000000000000..a9b6c63d54e4 --- /dev/null +++ b/drivers/net/usb/kalmia.c @@ -0,0 +1,392 @@ +/* + * USB network interface driver for Samsung Kalmia based LTE USB modem like the + * Samsung GT-B3730 and GT-B3710. + * + * Copyright (C) 2011 Marius Bjoernstad Kotsbak <[email protected]> + * + * Sponsored by Quicklink Video Distribution Services Ltd. + * + * Based on the cdc_eem module. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/ctype.h> +#include <linux/ethtool.h> +#include <linux/workqueue.h> +#include <linux/mii.h> +#include <linux/usb.h> +#include <linux/crc32.h> +#include <linux/usb/cdc.h> +#include <linux/usb/usbnet.h> +#include <linux/gfp.h> + +/* + * The Samsung Kalmia based LTE USB modems have a CDC ACM port for modem control + * handled by the "option" module and an ethernet data port handled by this + * module. + * + * The stick must first be switched into modem mode by usb_modeswitch + * or similar tool. Then the modem gets sent two initialization packets by + * this module, which gives the MAC address of the device. User space can then + * connect the modem using AT commands through the ACM port and then use + * DHCP on the network interface exposed by this module. Network packets are + * sent to and from the modem in a proprietary format discovered after watching + * the behavior of the windows driver for the modem. + * + * More information about the use of the modem is available in usb_modeswitch + * forum and the project page: + * + * http://www.draisberghof.de/usb_modeswitch/bb/viewtopic.php?t=465 + * https://github.com/mkotsbak/Samsung-GT-B3730-linux-driver + */ + +/* #define	DEBUG */ +/* #define	VERBOSE */ + +#define KALMIA_HEADER_LENGTH 6 +#define KALMIA_ALIGN_SIZE 4 +#define KALMIA_USB_TIMEOUT 10000 + +/*-------------------------------------------------------------------------*/ + +static int +kalmia_send_init_packet(struct usbnet *dev, u8 *init_msg, u8 init_msg_len, +	u8 *buffer, u8 expected_len) +{ +	int act_len; +	int status; + +	netdev_dbg(dev->net, "Sending init packet"); + +	status = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 0x02), +		init_msg, init_msg_len, &act_len, KALMIA_USB_TIMEOUT); +	if (status != 0) { +		netdev_err(dev->net, +			"Error sending init packet. Status %i, length %i\n", +			status, act_len); +		return status; +	} +	else if (act_len != init_msg_len) { +		netdev_err(dev->net, +			"Did not send all of init packet. Bytes sent: %i", +			act_len); +	} +	else { +		netdev_dbg(dev->net, "Successfully sent init packet."); +	} + +	status = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, 0x81), +		buffer, expected_len, &act_len, KALMIA_USB_TIMEOUT); + +	if (status != 0) +		netdev_err(dev->net, +			"Error receiving init result. Status %i, length %i\n", +			status, act_len); +	else if (act_len != expected_len) +		netdev_err(dev->net, "Unexpected init result length: %i\n", +			act_len); + +	return status; +} + +static int +kalmia_init_and_get_ethernet_addr(struct usbnet *dev, u8 *ethernet_addr) +{ +	const static char init_msg_1[] = +		{ 0x57, 0x50, 0x04, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, +		0x00, 0x00 }; +	const static char init_msg_2[] = +		{ 0x57, 0x50, 0x04, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xf4, +		0x00, 0x00 }; +	const static int buflen = 28; +	char *usb_buf; +	int status; + +	usb_buf = kmalloc(buflen, GFP_DMA | GFP_KERNEL); +	if (!usb_buf) +		return -ENOMEM; + +	memcpy(usb_buf, init_msg_1, 12); +	status = kalmia_send_init_packet(dev, usb_buf, sizeof(init_msg_1) +		/ sizeof(init_msg_1[0]), usb_buf, 24); +	if (status != 0) +		return status; + +	memcpy(usb_buf, init_msg_2, 12); +	status = kalmia_send_init_packet(dev, usb_buf, sizeof(init_msg_2) +		/ sizeof(init_msg_2[0]), usb_buf, 28); +	if (status != 0) +		return status; + +	memcpy(ethernet_addr, usb_buf + 10, ETH_ALEN); + +	kfree(usb_buf); +	return status; +} + +static int +kalmia_bind(struct usbnet *dev, struct usb_interface *intf) +{ +	int status; +	u8 ethernet_addr[ETH_ALEN]; + +	/* Don't bind to AT command interface */ +	if (intf->cur_altsetting->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC) +		return -EINVAL; + +	dev->in = usb_rcvbulkpipe(dev->udev, 0x81 & USB_ENDPOINT_NUMBER_MASK); +	dev->out = usb_sndbulkpipe(dev->udev, 0x02 & USB_ENDPOINT_NUMBER_MASK); +	dev->status = NULL; + +	dev->net->hard_header_len += KALMIA_HEADER_LENGTH; +	dev->hard_mtu = 1400; +	dev->rx_urb_size = dev->hard_mtu * 10; // Found as optimal after testing + +	status = kalmia_init_and_get_ethernet_addr(dev, ethernet_addr); + +	if (status < 0) { +		usb_set_intfdata(intf, NULL); +		usb_driver_release_interface(driver_of(intf), intf); +		return status; +	} + +	memcpy(dev->net->dev_addr, ethernet_addr, ETH_ALEN); +	memcpy(dev->net->perm_addr, ethernet_addr, ETH_ALEN); + +	return status; +} + +static struct sk_buff * +kalmia_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) +{ +	struct sk_buff *skb2 = NULL; +	u16 content_len; +	unsigned char *header_start; +	unsigned char ether_type_1, ether_type_2; +	u8 remainder, padlen = 0; + +	if (!skb_cloned(skb)) { +		int headroom = skb_headroom(skb); +		int tailroom = skb_tailroom(skb); + +		if ((tailroom >= KALMIA_ALIGN_SIZE) && (headroom +			>= KALMIA_HEADER_LENGTH)) +			goto done; + +		if ((headroom + tailroom) > (KALMIA_HEADER_LENGTH +			+ KALMIA_ALIGN_SIZE)) { +			skb->data = memmove(skb->head + KALMIA_HEADER_LENGTH, +				skb->data, skb->len); +			skb_set_tail_pointer(skb, skb->len); +			goto done; +		} +	} + +	skb2 = skb_copy_expand(skb, KALMIA_HEADER_LENGTH, +		KALMIA_ALIGN_SIZE, flags); +	if (!skb2) +		return NULL; + +	dev_kfree_skb_any(skb); +	skb = skb2; + +done: +	header_start = skb_push(skb, KALMIA_HEADER_LENGTH); +	ether_type_1 = header_start[KALMIA_HEADER_LENGTH + 12]; +	ether_type_2 = header_start[KALMIA_HEADER_LENGTH + 13]; + +	netdev_dbg(dev->net, "Sending etherType: %02x%02x", ether_type_1, +		ether_type_2); + +	/* According to empiric data for data packages */ +	header_start[0] = 0x57; +	header_start[1] = 0x44; +	content_len = skb->len - KALMIA_HEADER_LENGTH; + +	put_unaligned_le16(content_len, &header_start[2]); +	header_start[4] = ether_type_1; +	header_start[5] = ether_type_2; + +	/* Align to 4 bytes by padding with zeros */ +	remainder = skb->len % KALMIA_ALIGN_SIZE; +	if (remainder > 0) { +		padlen = KALMIA_ALIGN_SIZE - remainder; +		memset(skb_put(skb, padlen), 0, padlen); +	} + +	netdev_dbg( +		dev->net, +		"Sending package with length %i and padding %i. Header: %02x:%02x:%02x:%02x:%02x:%02x.", +		content_len, padlen, header_start[0], header_start[1], +		header_start[2], header_start[3], header_start[4], +		header_start[5]); + +	return skb; +} + +static int +kalmia_rx_fixup(struct usbnet *dev, struct sk_buff *skb) +{ +	/* +	 * Our task here is to strip off framing, leaving skb with one +	 * data frame for the usbnet framework code to process. +	 */ +	const static u8 HEADER_END_OF_USB_PACKET[] = +		{ 0x57, 0x5a, 0x00, 0x00, 0x08, 0x00 }; +	const static u8 EXPECTED_UNKNOWN_HEADER_1[] = +		{ 0x57, 0x43, 0x1e, 0x00, 0x15, 0x02 }; +	const static u8 EXPECTED_UNKNOWN_HEADER_2[] = +		{ 0x57, 0x50, 0x0e, 0x00, 0x00, 0x00 }; +	int i = 0; + +	/* incomplete header? */ +	if (skb->len < KALMIA_HEADER_LENGTH) +		return 0; + +	do { +		struct sk_buff *skb2 = NULL; +		u8 *header_start; +		u16 usb_packet_length, ether_packet_length; +		int is_last; + +		header_start = skb->data; + +		if (unlikely(header_start[0] != 0x57 || header_start[1] != 0x44)) { +			if (!memcmp(header_start, EXPECTED_UNKNOWN_HEADER_1, +				sizeof(EXPECTED_UNKNOWN_HEADER_1)) || !memcmp( +				header_start, EXPECTED_UNKNOWN_HEADER_2, +				sizeof(EXPECTED_UNKNOWN_HEADER_2))) { +				netdev_dbg( +					dev->net, +					"Received expected unknown frame header: %02x:%02x:%02x:%02x:%02x:%02x. Package length: %i\n", +					header_start[0], header_start[1], +					header_start[2], header_start[3], +					header_start[4], header_start[5], +					skb->len - KALMIA_HEADER_LENGTH); +			} +			else { +				netdev_err( +					dev->net, +					"Received unknown frame header: %02x:%02x:%02x:%02x:%02x:%02x. Package length: %i\n", +					header_start[0], header_start[1], +					header_start[2], header_start[3], +					header_start[4], header_start[5], +					skb->len - KALMIA_HEADER_LENGTH); +				return 0; +			} +		} +		else +			netdev_dbg( +				dev->net, +				"Received header: %02x:%02x:%02x:%02x:%02x:%02x. Package length: %i\n", +				header_start[0], header_start[1], header_start[2], +				header_start[3], header_start[4], header_start[5], +				skb->len - KALMIA_HEADER_LENGTH); + +		/* subtract start header and end header */ +		usb_packet_length = skb->len - (2 * KALMIA_HEADER_LENGTH); +		ether_packet_length = get_unaligned_le16(&header_start[2]); +		skb_pull(skb, KALMIA_HEADER_LENGTH); + +		/* Some small packets misses end marker */ +		if (usb_packet_length < ether_packet_length) { +			ether_packet_length = usb_packet_length +				+ KALMIA_HEADER_LENGTH; +			is_last = true; +		} +		else { +			netdev_dbg(dev->net, "Correct package length #%i", i +				+ 1); + +			is_last = (memcmp(skb->data + ether_packet_length, +				HEADER_END_OF_USB_PACKET, +				sizeof(HEADER_END_OF_USB_PACKET)) == 0); +			if (!is_last) { +				header_start = skb->data + ether_packet_length; +				netdev_dbg( +					dev->net, +					"End header: %02x:%02x:%02x:%02x:%02x:%02x. Package length: %i\n", +					header_start[0], header_start[1], +					header_start[2], header_start[3], +					header_start[4], header_start[5], +					skb->len - KALMIA_HEADER_LENGTH); +			} +		} + +		if (is_last) { +			skb2 = skb; +		} +		else { +			skb2 = skb_clone(skb, GFP_ATOMIC); +			if (unlikely(!skb2)) +				return 0; +		} + +		skb_trim(skb2, ether_packet_length); + +		if (is_last) { +			return 1; +		} +		else { +			usbnet_skb_return(dev, skb2); +			skb_pull(skb, ether_packet_length); +		} + +		i++; +	} +	while (skb->len); + +	return 1; +} + +static const struct driver_info kalmia_info = { +	.description = "Samsung Kalmia LTE USB dongle", +	.flags = FLAG_WWAN, +	.bind = kalmia_bind, +	.rx_fixup = kalmia_rx_fixup, +	.tx_fixup = kalmia_tx_fixup +}; + +/*-------------------------------------------------------------------------*/ + +static const struct usb_device_id products[] = { +	/* The unswitched USB ID, to get the module auto loaded: */ +	{ USB_DEVICE(0x04e8, 0x689a) }, +	/* The stick swithed into modem (by e.g. usb_modeswitch): */ +	{ USB_DEVICE(0x04e8, 0x6889), +		.driver_info = (unsigned long) &kalmia_info, }, +	{ /* EMPTY == end of list */} }; +MODULE_DEVICE_TABLE( usb, products); + +static struct usb_driver kalmia_driver = { +	.name = "kalmia", +	.id_table = products, +	.probe = usbnet_probe, +	.disconnect = usbnet_disconnect, +	.suspend = usbnet_suspend, +	.resume = usbnet_resume +}; + +static int __init kalmia_init(void) +{ +	return usb_register(&kalmia_driver); +} +module_init( kalmia_init); + +static void __exit kalmia_exit(void) +{ +	usb_deregister(&kalmia_driver); +} +module_exit( kalmia_exit); + +MODULE_AUTHOR("Marius Bjoernstad Kotsbak <[email protected]>"); +MODULE_DESCRIPTION("Samsung Kalmia USB network driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/usb/plusb.c b/drivers/net/usb/plusb.c index 823c53751307..217aec8a768f 100644 --- a/drivers/net/usb/plusb.c +++ b/drivers/net/usb/plusb.c @@ -45,6 +45,14 @@   * seems to get wedged under load.  Prolific docs are weak, and   * don't identify differences between PL2301 and PL2302, much less   * anything to explain the different PL2302 versions observed. + * + * NOTE:  pl2501 has several modes, including pl2301 and pl2302 + * compatibility.   Some docs suggest the difference between 2301 + * and 2302 is only to make MS-Windows use a different driver... + * + * pl25a1 glue based on patch from Tony Gibbs.  Prolific "docs" on + * this chip are as usual incomplete about what control messages + * are supported.   */  /* @@ -86,16 +94,20 @@ pl_set_QuickLink_features(struct usbnet *dev, int val)  static int pl_reset(struct usbnet *dev)  { +	int status; +  	/* some units seem to need this reset, others reject it utterly.  	 * FIXME be more like "naplink" or windows drivers.  	 */ -	(void) pl_set_QuickLink_features(dev, +	status = pl_set_QuickLink_features(dev,  		PL_S_EN|PL_RESET_OUT|PL_RESET_IN|PL_PEER_E); +	if (status != 0 && netif_msg_probe(dev)) +		netif_dbg(dev, link, dev->net, "pl_reset --> %d\n", status);  	return 0;  }  static const struct driver_info	prolific_info = { -	.description =	"Prolific PL-2301/PL-2302", +	.description =	"Prolific PL-2301/PL-2302/PL-25A1",  	.flags =	FLAG_POINTTOPOINT | FLAG_NO_SETINT,  		/* some PL-2302 versions seem to fail usb_set_interface() */  	.reset =	pl_reset, @@ -111,6 +123,7 @@ static const struct driver_info	prolific_info = {  static const struct usb_device_id	products [] = { +/* full speed cables */  {  	USB_DEVICE(0x067b, 0x0000),	// PL-2301  	.driver_info =	(unsigned long) &prolific_info, @@ -119,6 +132,15 @@ static const struct usb_device_id	products [] = {  	.driver_info =	(unsigned long) &prolific_info,  }, +/* high speed cables */ +{ +	USB_DEVICE(0x067b, 0x25a1),     /* PL-25A1, no eeprom */ +	.driver_info =  (unsigned long) &prolific_info, +}, { +	USB_DEVICE(0x050d, 0x258a),     /* Belkin F5U258/F5U279 (PL-25A1) */ +	.driver_info =  (unsigned long) &prolific_info, +}, +  	{ },		// END  };  MODULE_DEVICE_TABLE(usb, products); @@ -134,16 +156,16 @@ static struct usb_driver plusb_driver = {  static int __init plusb_init(void)  { - 	return usb_register(&plusb_driver); +	return usb_register(&plusb_driver);  }  module_init(plusb_init);  static void __exit plusb_exit(void)  { - 	usb_deregister(&plusb_driver); +	usb_deregister(&plusb_driver);  }  module_exit(plusb_exit);  MODULE_AUTHOR("David Brownell"); -MODULE_DESCRIPTION("Prolific PL-2301/2302 USB Host to Host Link Driver"); +MODULE_DESCRIPTION("Prolific PL-2301/2302/25A1 USB Host to Host Link Driver");  MODULE_LICENSE("GPL"); diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c index 5994a25c56ac..255d6a424a6b 100644 --- a/drivers/net/usb/rndis_host.c +++ b/drivers/net/usb/rndis_host.c @@ -104,8 +104,10 @@ static void rndis_msg_indicate(struct usbnet *dev, struct rndis_indicate *msg,  int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf, int buflen)  {  	struct cdc_state	*info = (void *) &dev->data; +	struct usb_cdc_notification notification;  	int			master_ifnum;  	int			retval; +	int			partial;  	unsigned		count;  	__le32			rsp;  	u32			xid = 0, msg_len, request_id; @@ -133,13 +135,20 @@ int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf, int buflen)  	if (unlikely(retval < 0 || xid == 0))  		return retval; -	// FIXME Seems like some devices discard responses when -	// we time out and cancel our "get response" requests... -	// so, this is fragile.  Probably need to poll for status. +	/* Some devices don't respond on the control channel until +	 * polled on the status channel, so do that first. */ +	if (dev->driver_info->data & RNDIS_DRIVER_DATA_POLL_STATUS) { +		retval = usb_interrupt_msg( +			dev->udev, +			usb_rcvintpipe(dev->udev, +				       dev->status->desc.bEndpointAddress), +			¬ification, sizeof(notification), &partial, +			RNDIS_CONTROL_TIMEOUT_MS); +		if (unlikely(retval < 0)) +			return retval; +	} -	/* ignore status endpoint, just poll the control channel; -	 * the request probably completed immediately -	 */ +	/* Poll the control channel; the request probably completed immediately */  	rsp = buf->msg_type | RNDIS_MSG_COMPLETION;  	for (count = 0; count < 10; count++) {  		memset(buf, 0, CONTROL_BUFFER_SIZE); @@ -581,17 +590,33 @@ static const struct driver_info	rndis_info = {  	.tx_fixup =	rndis_tx_fixup,  }; +static const struct driver_info	rndis_poll_status_info = { +	.description =	"RNDIS device (poll status before control)", +	.flags =	FLAG_ETHER | FLAG_POINTTOPOINT | FLAG_FRAMING_RN | FLAG_NO_SETINT, +	.data =		RNDIS_DRIVER_DATA_POLL_STATUS, +	.bind =		rndis_bind, +	.unbind =	rndis_unbind, +	.status =	rndis_status, +	.rx_fixup =	rndis_rx_fixup, +	.tx_fixup =	rndis_tx_fixup, +}; +  /*-------------------------------------------------------------------------*/  static const struct usb_device_id	products [] = {  { +	/* 2Wire HomePortal 1000SW */ +	USB_DEVICE_AND_INTERFACE_INFO(0x1630, 0x0042, +				      USB_CLASS_COMM, 2 /* ACM */, 0x0ff), +	.driver_info = (unsigned long) &rndis_poll_status_info, +}, {  	/* RNDIS is MSFT's un-official variant of CDC ACM */  	USB_INTERFACE_INFO(USB_CLASS_COMM, 2 /* ACM */, 0x0ff),  	.driver_info = (unsigned long) &rndis_info,  }, {  	/* "ActiveSync" is an undocumented variant of RNDIS, used in WM5 */  	USB_INTERFACE_INFO(USB_CLASS_MISC, 1, 1), -	.driver_info = (unsigned long) &rndis_info, +	.driver_info = (unsigned long) &rndis_poll_status_info,  }, {  	/* RNDIS for tethering */  	USB_INTERFACE_INFO(USB_CLASS_WIRELESS_CONTROLLER, 1, 3), diff --git a/drivers/net/usb/rtl8150.c b/drivers/net/usb/rtl8150.c index e85c89c6706d..041fb7d43c4f 100644 --- a/drivers/net/usb/rtl8150.c +++ b/drivers/net/usb/rtl8150.c @@ -843,10 +843,11 @@ static int rtl8150_get_settings(struct net_device *netdev, struct ethtool_cmd *e  	get_registers(dev, BMCR, 2, &bmcr);  	get_registers(dev, ANLP, 2, &lpa);  	if (bmcr & BMCR_ANENABLE) { +		u32 speed = ((lpa & (LPA_100HALF | LPA_100FULL)) ? +			     SPEED_100 : SPEED_10); +		ethtool_cmd_speed_set(ecmd, speed);  		ecmd->autoneg = AUTONEG_ENABLE; -		ecmd->speed = (lpa & (LPA_100HALF | LPA_100FULL)) ? -			     SPEED_100 : SPEED_10; -		if (ecmd->speed == SPEED_100) +		if (speed == SPEED_100)  			ecmd->duplex = (lpa & LPA_100FULL) ?  			    DUPLEX_FULL : DUPLEX_HALF;  		else @@ -854,8 +855,8 @@ static int rtl8150_get_settings(struct net_device *netdev, struct ethtool_cmd *e  			    DUPLEX_FULL : DUPLEX_HALF;  	} else {  		ecmd->autoneg = AUTONEG_DISABLE; -		ecmd->speed = (bmcr & BMCR_SPEED100) ? -		    SPEED_100 : SPEED_10; +		ethtool_cmd_speed_set(ecmd, ((bmcr & BMCR_SPEED100) ? +					     SPEED_100 : SPEED_10));  		ecmd->duplex = (bmcr & BMCR_FULLDPLX) ?  		    DUPLEX_FULL : DUPLEX_HALF;  	} diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c index 753ee6eb7edd..15b3d6888ae9 100644 --- a/drivers/net/usb/smsc75xx.c +++ b/drivers/net/usb/smsc75xx.c @@ -65,7 +65,6 @@ struct smsc75xx_priv {  	struct usbnet *dev;  	u32 rfe_ctl;  	u32 multicast_hash_table[DP_SEL_VHF_HASH_LEN]; -	bool use_rx_csum;  	struct mutex dataport_mutex;  	spinlock_t rfe_ctl_lock;  	struct work_struct set_multicast; @@ -504,7 +503,7 @@ static int smsc75xx_update_flowcontrol(struct usbnet *dev, u8 duplex,  static int smsc75xx_link_reset(struct usbnet *dev)  {  	struct mii_if_info *mii = &dev->mii; -	struct ethtool_cmd ecmd; +	struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET };  	u16 lcladv, rmtadv;  	int ret; @@ -520,8 +519,9 @@ static int smsc75xx_link_reset(struct usbnet *dev)  	lcladv = smsc75xx_mdio_read(dev->net, mii->phy_id, MII_ADVERTISE);  	rmtadv = smsc75xx_mdio_read(dev->net, mii->phy_id, MII_LPA); -	netif_dbg(dev, link, dev->net, "speed: %d duplex: %d lcladv: %04x" -		" rmtadv: %04x", ecmd.speed, ecmd.duplex, lcladv, rmtadv); +	netif_dbg(dev, link, dev->net, "speed: %u duplex: %d lcladv: %04x" +		  " rmtadv: %04x", ethtool_cmd_speed(&ecmd), +		  ecmd.duplex, lcladv, rmtadv);  	return smsc75xx_update_flowcontrol(dev, ecmd.duplex, lcladv, rmtadv);  } @@ -548,28 +548,6 @@ static void smsc75xx_status(struct usbnet *dev, struct urb *urb)  			"unexpected interrupt, intdata=0x%08X", intdata);  } -/* Enable or disable Rx checksum offload engine */ -static int smsc75xx_set_rx_csum_offload(struct usbnet *dev) -{ -	struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); -	unsigned long flags; -	int ret; - -	spin_lock_irqsave(&pdata->rfe_ctl_lock, flags); - -	if (pdata->use_rx_csum) -		pdata->rfe_ctl |= RFE_CTL_TCPUDP_CKM | RFE_CTL_IP_CKM; -	else -		pdata->rfe_ctl &= ~(RFE_CTL_TCPUDP_CKM | RFE_CTL_IP_CKM); - -	spin_unlock_irqrestore(&pdata->rfe_ctl_lock, flags); - -	ret = smsc75xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl); -	check_warn_return(ret, "Error writing RFE_CTL"); - -	return 0; -} -  static int smsc75xx_ethtool_get_eeprom_len(struct net_device *net)  {  	return MAX_EEPROM_SIZE; @@ -599,34 +577,6 @@ static int smsc75xx_ethtool_set_eeprom(struct net_device *netdev,  	return smsc75xx_write_eeprom(dev, ee->offset, ee->len, data);  } -static u32 smsc75xx_ethtool_get_rx_csum(struct net_device *netdev) -{ -	struct usbnet *dev = netdev_priv(netdev); -	struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); - -	return pdata->use_rx_csum; -} - -static int smsc75xx_ethtool_set_rx_csum(struct net_device *netdev, u32 val) -{ -	struct usbnet *dev = netdev_priv(netdev); -	struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); - -	pdata->use_rx_csum = !!val; - -	return smsc75xx_set_rx_csum_offload(dev); -} - -static int smsc75xx_ethtool_set_tso(struct net_device *netdev, u32 data) -{ -	if (data) -		netdev->features |= NETIF_F_TSO | NETIF_F_TSO6; -	else -		netdev->features &= ~(NETIF_F_TSO | NETIF_F_TSO6); - -	return 0; -} -  static const struct ethtool_ops smsc75xx_ethtool_ops = {  	.get_link	= usbnet_get_link,  	.nway_reset	= usbnet_nway_reset, @@ -638,12 +588,6 @@ static const struct ethtool_ops smsc75xx_ethtool_ops = {  	.get_eeprom_len	= smsc75xx_ethtool_get_eeprom_len,  	.get_eeprom	= smsc75xx_ethtool_get_eeprom,  	.set_eeprom	= smsc75xx_ethtool_set_eeprom, -	.get_tx_csum	= ethtool_op_get_tx_csum, -	.set_tx_csum	= ethtool_op_set_tx_hw_csum, -	.get_rx_csum	= smsc75xx_ethtool_get_rx_csum, -	.set_rx_csum	= smsc75xx_ethtool_set_rx_csum, -	.get_tso	= ethtool_op_get_tso, -	.set_tso	= smsc75xx_ethtool_set_tso,  };  static int smsc75xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) @@ -782,6 +726,30 @@ static int smsc75xx_change_mtu(struct net_device *netdev, int new_mtu)  	return usbnet_change_mtu(netdev, new_mtu);  } +/* Enable or disable Rx checksum offload engine */ +static int smsc75xx_set_features(struct net_device *netdev, u32 features) +{ +	struct usbnet *dev = netdev_priv(netdev); +	struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); +	unsigned long flags; +	int ret; + +	spin_lock_irqsave(&pdata->rfe_ctl_lock, flags); + +	if (features & NETIF_F_RXCSUM) +		pdata->rfe_ctl |= RFE_CTL_TCPUDP_CKM | RFE_CTL_IP_CKM; +	else +		pdata->rfe_ctl &= ~(RFE_CTL_TCPUDP_CKM | RFE_CTL_IP_CKM); + +	spin_unlock_irqrestore(&pdata->rfe_ctl_lock, flags); +	/* it's racing here! */ + +	ret = smsc75xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl); +	check_warn_return(ret, "Error writing RFE_CTL"); + +	return 0; +} +  static int smsc75xx_reset(struct usbnet *dev)  {  	struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); @@ -960,11 +928,7 @@ static int smsc75xx_reset(struct usbnet *dev)  	netif_dbg(dev, ifup, dev->net, "RFE_CTL set to 0x%08x", pdata->rfe_ctl);  	/* Enable or disable checksum offload engines */ -	ethtool_op_set_tx_hw_csum(dev->net, DEFAULT_TX_CSUM_ENABLE); -	ret = smsc75xx_set_rx_csum_offload(dev); -	check_warn_return(ret, "Failed to set rx csum offload: %d", ret); - -	smsc75xx_ethtool_set_tso(dev->net, DEFAULT_TSO_ENABLE); +	smsc75xx_set_features(dev->net, dev->net->features);  	smsc75xx_set_multicast(dev->net); @@ -1037,6 +1001,7 @@ static const struct net_device_ops smsc75xx_netdev_ops = {  	.ndo_validate_addr	= eth_validate_addr,  	.ndo_do_ioctl 		= smsc75xx_ioctl,  	.ndo_set_multicast_list = smsc75xx_set_multicast, +	.ndo_set_features	= smsc75xx_set_features,  };  static int smsc75xx_bind(struct usbnet *dev, struct usb_interface *intf) @@ -1065,10 +1030,17 @@ static int smsc75xx_bind(struct usbnet *dev, struct usb_interface *intf)  	INIT_WORK(&pdata->set_multicast, smsc75xx_deferred_multicast_write); -	pdata->use_rx_csum = DEFAULT_RX_CSUM_ENABLE; +	if (DEFAULT_TX_CSUM_ENABLE) { +		dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; +		if (DEFAULT_TSO_ENABLE) +			dev->net->features |= NETIF_F_SG | +				NETIF_F_TSO | NETIF_F_TSO6; +	} +	if (DEFAULT_RX_CSUM_ENABLE) +		dev->net->features |= NETIF_F_RXCSUM; -	/* We have to advertise SG otherwise TSO cannot be enabled */ -	dev->net->features |= NETIF_F_SG; +	dev->net->hw_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | +		NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_RXCSUM;  	/* Init all registers */  	ret = smsc75xx_reset(dev); @@ -1091,10 +1063,11 @@ static void smsc75xx_unbind(struct usbnet *dev, struct usb_interface *intf)  	}  } -static void smsc75xx_rx_csum_offload(struct sk_buff *skb, u32 rx_cmd_a, -				     u32 rx_cmd_b) +static void smsc75xx_rx_csum_offload(struct usbnet *dev, struct sk_buff *skb, +				     u32 rx_cmd_a, u32 rx_cmd_b)  { -	if (unlikely(rx_cmd_a & RX_CMD_A_LCSM)) { +	if (!(dev->net->features & NETIF_F_RXCSUM) || +	    unlikely(rx_cmd_a & RX_CMD_A_LCSM)) {  		skb->ip_summed = CHECKSUM_NONE;  	} else {  		skb->csum = ntohs((u16)(rx_cmd_b >> RX_CMD_B_CSUM_SHIFT)); @@ -1104,8 +1077,6 @@ static void smsc75xx_rx_csum_offload(struct sk_buff *skb, u32 rx_cmd_a,  static int smsc75xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  { -	struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); -  	while (skb->len > 0) {  		u32 rx_cmd_a, rx_cmd_b, align_count, size;  		struct sk_buff *ax_skb; @@ -1145,11 +1116,8 @@ static int smsc75xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  			/* last frame in this batch */  			if (skb->len == size) { -				if (pdata->use_rx_csum) -					smsc75xx_rx_csum_offload(skb, rx_cmd_a, -						rx_cmd_b); -				else -					skb->ip_summed = CHECKSUM_NONE; +				smsc75xx_rx_csum_offload(dev, skb, rx_cmd_a, +					rx_cmd_b);  				skb_trim(skb, skb->len - 4); /* remove fcs */  				skb->truesize = size + sizeof(struct sk_buff); @@ -1167,11 +1135,8 @@ static int smsc75xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  			ax_skb->data = packet;  			skb_set_tail_pointer(ax_skb, size); -			if (pdata->use_rx_csum) -				smsc75xx_rx_csum_offload(ax_skb, rx_cmd_a, -					rx_cmd_b); -			else -				ax_skb->ip_summed = CHECKSUM_NONE; +			smsc75xx_rx_csum_offload(dev, ax_skb, rx_cmd_a, +				rx_cmd_b);  			skb_trim(ax_skb, ax_skb->len - 4); /* remove fcs */  			ax_skb->truesize = size + sizeof(struct sk_buff); diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index 48d4efdb4959..f74f3ce71526 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -52,8 +52,6 @@ struct smsc95xx_priv {  	u32 hash_hi;  	u32 hash_lo;  	spinlock_t mac_cr_lock; -	bool use_tx_csum; -	bool use_rx_csum;  };  struct usb_context { @@ -459,7 +457,7 @@ static int smsc95xx_link_reset(struct usbnet *dev)  {  	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);  	struct mii_if_info *mii = &dev->mii; -	struct ethtool_cmd ecmd; +	struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET };  	unsigned long flags;  	u16 lcladv, rmtadv;  	u32 intdata; @@ -474,8 +472,9 @@ static int smsc95xx_link_reset(struct usbnet *dev)  	lcladv = smsc95xx_mdio_read(dev->net, mii->phy_id, MII_ADVERTISE);  	rmtadv = smsc95xx_mdio_read(dev->net, mii->phy_id, MII_LPA); -	netif_dbg(dev, link, dev->net, "speed: %d duplex: %d lcladv: %04x rmtadv: %04x\n", -		  ecmd.speed, ecmd.duplex, lcladv, rmtadv); +	netif_dbg(dev, link, dev->net, +		  "speed: %u duplex: %d lcladv: %04x rmtadv: %04x\n", +		  ethtool_cmd_speed(&ecmd), ecmd.duplex, lcladv, rmtadv);  	spin_lock_irqsave(&pdata->mac_cr_lock, flags);  	if (ecmd.duplex != DUPLEX_FULL) { @@ -517,22 +516,24 @@ static void smsc95xx_status(struct usbnet *dev, struct urb *urb)  }  /* Enable or disable Tx & Rx checksum offload engines */ -static int smsc95xx_set_csums(struct usbnet *dev) +static int smsc95xx_set_features(struct net_device *netdev, u32 features)  { -	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); +	struct usbnet *dev = netdev_priv(netdev);  	u32 read_buf; -	int ret = smsc95xx_read_reg(dev, COE_CR, &read_buf); +	int ret; + +	ret = smsc95xx_read_reg(dev, COE_CR, &read_buf);  	if (ret < 0) {  		netdev_warn(dev->net, "Failed to read COE_CR: %d\n", ret);  		return ret;  	} -	if (pdata->use_tx_csum) +	if (features & NETIF_F_HW_CSUM)  		read_buf |= Tx_COE_EN_;  	else  		read_buf &= ~Tx_COE_EN_; -	if (pdata->use_rx_csum) +	if (features & NETIF_F_RXCSUM)  		read_buf |= Rx_COE_EN_;  	else  		read_buf &= ~Rx_COE_EN_; @@ -576,43 +577,6 @@ static int smsc95xx_ethtool_set_eeprom(struct net_device *netdev,  	return smsc95xx_write_eeprom(dev, ee->offset, ee->len, data);  } -static u32 smsc95xx_ethtool_get_rx_csum(struct net_device *netdev) -{ -	struct usbnet *dev = netdev_priv(netdev); -	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); - -	return pdata->use_rx_csum; -} - -static int smsc95xx_ethtool_set_rx_csum(struct net_device *netdev, u32 val) -{ -	struct usbnet *dev = netdev_priv(netdev); -	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); - -	pdata->use_rx_csum = !!val; - -	return smsc95xx_set_csums(dev); -} - -static u32 smsc95xx_ethtool_get_tx_csum(struct net_device *netdev) -{ -	struct usbnet *dev = netdev_priv(netdev); -	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); - -	return pdata->use_tx_csum; -} - -static int smsc95xx_ethtool_set_tx_csum(struct net_device *netdev, u32 val) -{ -	struct usbnet *dev = netdev_priv(netdev); -	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); - -	pdata->use_tx_csum = !!val; - -	ethtool_op_set_tx_hw_csum(netdev, pdata->use_tx_csum); -	return smsc95xx_set_csums(dev); -} -  static const struct ethtool_ops smsc95xx_ethtool_ops = {  	.get_link	= usbnet_get_link,  	.nway_reset	= usbnet_nway_reset, @@ -624,10 +588,6 @@ static const struct ethtool_ops smsc95xx_ethtool_ops = {  	.get_eeprom_len	= smsc95xx_ethtool_get_eeprom_len,  	.get_eeprom	= smsc95xx_ethtool_get_eeprom,  	.set_eeprom	= smsc95xx_ethtool_set_eeprom, -	.get_tx_csum	= smsc95xx_ethtool_get_tx_csum, -	.set_tx_csum	= smsc95xx_ethtool_set_tx_csum, -	.get_rx_csum	= smsc95xx_ethtool_get_rx_csum, -	.set_rx_csum	= smsc95xx_ethtool_set_rx_csum,  };  static int smsc95xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) @@ -755,7 +715,6 @@ static int smsc95xx_phy_initialize(struct usbnet *dev)  static int smsc95xx_reset(struct usbnet *dev)  {  	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); -	struct net_device *netdev = dev->net;  	u32 read_buf, write_buf, burst_cap;  	int ret = 0, timeout; @@ -975,12 +934,7 @@ static int smsc95xx_reset(struct usbnet *dev)  	}  	/* Enable or disable checksum offload engines */ -	ethtool_op_set_tx_hw_csum(netdev, pdata->use_tx_csum); -	ret = smsc95xx_set_csums(dev); -	if (ret < 0) { -		netdev_warn(dev->net, "Failed to set csum offload: %d\n", ret); -		return ret; -	} +	smsc95xx_set_features(dev->net, dev->net->features);  	smsc95xx_set_multicast(dev->net); @@ -1019,6 +973,7 @@ static const struct net_device_ops smsc95xx_netdev_ops = {  	.ndo_validate_addr	= eth_validate_addr,  	.ndo_do_ioctl 		= smsc95xx_ioctl,  	.ndo_set_multicast_list = smsc95xx_set_multicast, +	.ndo_set_features	= smsc95xx_set_features,  };  static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf) @@ -1045,8 +1000,12 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)  	spin_lock_init(&pdata->mac_cr_lock); -	pdata->use_tx_csum = DEFAULT_TX_CSUM_ENABLE; -	pdata->use_rx_csum = DEFAULT_RX_CSUM_ENABLE; +	if (DEFAULT_TX_CSUM_ENABLE) +		dev->net->features |= NETIF_F_HW_CSUM; +	if (DEFAULT_RX_CSUM_ENABLE) +		dev->net->features |= NETIF_F_RXCSUM; + +	dev->net->hw_features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM;  	smsc95xx_init_mac_address(dev); @@ -1056,7 +1015,7 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)  	dev->net->netdev_ops = &smsc95xx_netdev_ops;  	dev->net->ethtool_ops = &smsc95xx_ethtool_ops;  	dev->net->flags |= IFF_MULTICAST; -	dev->net->hard_header_len += SMSC95XX_TX_OVERHEAD; +	dev->net->hard_header_len += SMSC95XX_TX_OVERHEAD_CSUM;  	return 0;  } @@ -1080,8 +1039,6 @@ static void smsc95xx_rx_csum_offload(struct sk_buff *skb)  static int smsc95xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  { -	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); -  	while (skb->len > 0) {  		u32 header, align_count;  		struct sk_buff *ax_skb; @@ -1123,7 +1080,7 @@ static int smsc95xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  			/* last frame in this batch */  			if (skb->len == size) { -				if (pdata->use_rx_csum) +				if (dev->net->features & NETIF_F_RXCSUM)  					smsc95xx_rx_csum_offload(skb);  				skb_trim(skb, skb->len - 4); /* remove fcs */  				skb->truesize = size + sizeof(struct sk_buff); @@ -1141,7 +1098,7 @@ static int smsc95xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)  			ax_skb->data = packet;  			skb_set_tail_pointer(ax_skb, size); -			if (pdata->use_rx_csum) +			if (dev->net->features & NETIF_F_RXCSUM)  				smsc95xx_rx_csum_offload(ax_skb);  			skb_trim(ax_skb, ax_skb->len - 4); /* remove fcs */  			ax_skb->truesize = size + sizeof(struct sk_buff); @@ -1174,8 +1131,7 @@ static u32 smsc95xx_calc_csum_preamble(struct sk_buff *skb)  static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev,  					 struct sk_buff *skb, gfp_t flags)  { -	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); -	bool csum = pdata->use_tx_csum && (skb->ip_summed == CHECKSUM_PARTIAL); +	bool csum = skb->ip_summed == CHECKSUM_PARTIAL;  	int overhead = csum ? SMSC95XX_TX_OVERHEAD_CSUM : SMSC95XX_TX_OVERHEAD;  	u32 tx_cmd_a, tx_cmd_b; diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 9ab439d144ed..ce395fe5de26 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -109,7 +109,7 @@ int usbnet_get_endpoints(struct usbnet *dev, struct usb_interface *intf)  		/* take the first altsetting with in-bulk + out-bulk;  		 * remember any status endpoint, just in case; -		 * ignore other endpoints and altsetttings. +		 * ignore other endpoints and altsettings.  		 */  		for (ep = 0; ep < alt->desc.bNumEndpoints; ep++) {  			struct usb_host_endpoint	*e; @@ -1541,9 +1541,9 @@ EXPORT_SYMBOL_GPL(usbnet_resume);  static int __init usbnet_init(void)  { -	/* compiler should optimize this out */ -	BUILD_BUG_ON (sizeof (((struct sk_buff *)0)->cb) -			< sizeof (struct skb_data)); +	/* Compiler should optimize this out. */ +	BUILD_BUG_ON( +		FIELD_SIZEOF(struct sk_buff, cb) < sizeof(struct skb_data));  	random_ether_addr(node_id);  	return 0; diff --git a/drivers/net/usb/zaurus.c b/drivers/net/usb/zaurus.c index 241756e0e86f..1a2234c20514 100644 --- a/drivers/net/usb/zaurus.c +++ b/drivers/net/usb/zaurus.c @@ -331,17 +331,7 @@ static const struct usb_device_id	products [] = {  	ZAURUS_MASTER_INTERFACE,  	.driver_info = ZAURUS_PXA_INFO,  }, - - -/* At least some of the newest PXA units have very different lies about - * their standards support:  they claim to be cell phones offering - * direct access to their radios!  (No, they don't conform to CDC MDLM.) - */  { -	USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM, -			USB_CDC_PROTO_NONE), -	.driver_info = (unsigned long) &bogus_mdlm_info, -}, {  	/* Motorola MOTOMAGX phones */  	USB_DEVICE_AND_INTERFACE_INFO(0x22b8, 0x6425, USB_CLASS_COMM,  			USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), |