diff options
Diffstat (limited to 'drivers/usb/gadget/ether.c')
| -rw-r--r-- | drivers/usb/gadget/ether.c | 259 | 
1 files changed, 165 insertions, 94 deletions
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 1690c9d6825..c1c113ef950 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -9,22 +9,12 @@   * it under the terms of the GNU General Public License as published by   * the Free Software Foundation; either version 2 of the License, or   * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA   */  /* #define VERBOSE_DEBUG */  #include <linux/kernel.h> -#include <linux/utsname.h> - +#include <linux/netdevice.h>  #if defined USB_ETH_RNDIS  #  undef USB_ETH_RNDIS @@ -102,30 +92,22 @@ static inline bool has_rndis(void)  #endif  } -/*-------------------------------------------------------------------------*/ - -/* - * Kbuild is not very cooperative with respect to linking separately - * compiled library objects into one module.  So for now we won't use - * separate compilation ... ensuring init/exit sections work to shrink - * the runtime footprint, and giving us at least some parts of what - * a "gcc --combine ... part1.c part2.c part3.c ... " build would. - */ -#include "composite.c" -#include "usbstring.c" -#include "config.c" -#include "epautoconf.c" +#include <linux/module.h> -#include "f_ecm.c" -#include "f_subset.c" +#include "u_ecm.h" +#include "u_gether.h"  #ifdef	USB_ETH_RNDIS -#include "f_rndis.c" -#include "rndis.c" +#include "u_rndis.h" +#include "rndis.h" +#else +#define rndis_borrow_net(...) do {} while (0)  #endif -#include "f_eem.c" -#include "u_ether.c" +#include "u_eem.h"  /*-------------------------------------------------------------------------*/ +USB_GADGET_COMPOSITE_OPTIONS(); + +USB_ETHERNET_MODULE_PARAMETERS();  /* DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!!   * Instead:  allocate your own, using normal USB-IF procedures. @@ -204,17 +186,10 @@ static const struct usb_descriptor_header *otg_desc[] = {  	NULL,  }; - -/* string IDs are assigned dynamically */ - -#define STRING_MANUFACTURER_IDX		0 -#define STRING_PRODUCT_IDX		1 - -static char manufacturer[50]; -  static struct usb_string strings_dev[] = { -	[STRING_MANUFACTURER_IDX].s = manufacturer, -	[STRING_PRODUCT_IDX].s = PREFIX DRIVER_DESC, +	[USB_GADGET_MANUFACTURER_IDX].s = "", +	[USB_GADGET_PRODUCT_IDX].s = PREFIX DRIVER_DESC, +	[USB_GADGET_SERIAL_IDX].s = "",  	{  } /* end of list */  }; @@ -228,7 +203,17 @@ static struct usb_gadget_strings *dev_strings[] = {  	NULL,  }; -static u8 hostaddr[ETH_ALEN]; +static struct usb_function_instance *fi_ecm; +static struct usb_function *f_ecm; + +static struct usb_function_instance *fi_eem; +static struct usb_function *f_eem; + +static struct usb_function_instance *fi_geth; +static struct usb_function *f_geth; + +static struct usb_function_instance *fi_rndis; +static struct usb_function *f_rndis;  /*-------------------------------------------------------------------------*/ @@ -239,6 +224,8 @@ static u8 hostaddr[ETH_ALEN];   */  static int __init rndis_do_config(struct usb_configuration *c)  { +	int status; +  	/* FIXME alloc iConfiguration string, set it in c->strings */  	if (gadget_is_otg(c->cdev->gadget)) { @@ -246,7 +233,15 @@ static int __init rndis_do_config(struct usb_configuration *c)  		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;  	} -	return rndis_bind_config(c, hostaddr); +	f_rndis = usb_get_function(fi_rndis); +	if (IS_ERR(f_rndis)) +		return PTR_ERR(f_rndis); + +	status = usb_add_function(c, f_rndis); +	if (status < 0) +		usb_put_function(f_rndis); + +	return status;  }  static struct usb_configuration rndis_config_driver = { @@ -259,9 +254,9 @@ static struct usb_configuration rndis_config_driver = {  /*-------------------------------------------------------------------------*/  #ifdef CONFIG_USB_ETH_EEM -static int use_eem = 1; +static bool use_eem = 1;  #else -static int use_eem; +static bool use_eem;  #endif  module_param(use_eem, bool, 0);  MODULE_PARM_DESC(use_eem, "use CDC EEM mode"); @@ -271,6 +266,8 @@ MODULE_PARM_DESC(use_eem, "use CDC EEM mode");   */  static int __init eth_do_config(struct usb_configuration *c)  { +	int status = 0; +  	/* FIXME alloc iConfiguration string, set it in c->strings */  	if (gadget_is_otg(c->cdev->gadget)) { @@ -278,12 +275,38 @@ static int __init eth_do_config(struct usb_configuration *c)  		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;  	} -	if (use_eem) -		return eem_bind_config(c); -	else if (can_support_ecm(c->cdev->gadget)) -		return ecm_bind_config(c, hostaddr); -	else -		return geth_bind_config(c, hostaddr); +	if (use_eem) { +		f_eem = usb_get_function(fi_eem); +		if (IS_ERR(f_eem)) +			return PTR_ERR(f_eem); + +		status = usb_add_function(c, f_eem); +		if (status < 0) +			usb_put_function(f_eem); + +		return status; +	} else if (can_support_ecm(c->cdev->gadget)) { +		f_ecm = usb_get_function(fi_ecm); +		if (IS_ERR(f_ecm)) +			return PTR_ERR(f_ecm); + +		status = usb_add_function(c, f_ecm); +		if (status < 0) +			usb_put_function(f_ecm); + +		return status; +	} else { +		f_geth = usb_get_function(fi_geth); +		if (IS_ERR(f_geth)) +			return PTR_ERR(f_geth); + +		status = usb_add_function(c, f_geth); +		if (status < 0) +			usb_put_function(f_geth); + +		return status; +	} +  }  static struct usb_configuration eth_config_driver = { @@ -297,26 +320,51 @@ static struct usb_configuration eth_config_driver = {  static int __init eth_bind(struct usb_composite_dev *cdev)  { -	int			gcnum;  	struct usb_gadget	*gadget = cdev->gadget; +	struct f_eem_opts	*eem_opts = NULL; +	struct f_ecm_opts	*ecm_opts = NULL; +	struct f_gether_opts	*geth_opts = NULL; +	struct net_device	*net;  	int			status; -	/* set up network link layer */ -	status = gether_setup(cdev->gadget, hostaddr); -	if (status < 0) -		return status; -  	/* set up main config label and device descriptor */  	if (use_eem) {  		/* EEM */ +		fi_eem = usb_get_function_instance("eem"); +		if (IS_ERR(fi_eem)) +			return PTR_ERR(fi_eem); + +		eem_opts = container_of(fi_eem, struct f_eem_opts, func_inst); + +		net = eem_opts->net; +  		eth_config_driver.label = "CDC Ethernet (EEM)";  		device_desc.idVendor = cpu_to_le16(EEM_VENDOR_NUM);  		device_desc.idProduct = cpu_to_le16(EEM_PRODUCT_NUM); -	} else if (can_support_ecm(cdev->gadget)) { +	} else if (can_support_ecm(gadget)) {  		/* ECM */ + +		fi_ecm = usb_get_function_instance("ecm"); +		if (IS_ERR(fi_ecm)) +			return PTR_ERR(fi_ecm); + +		ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst); + +		net = ecm_opts->net; +  		eth_config_driver.label = "CDC Ethernet (ECM)";  	} else {  		/* CDC Subset */ + +		fi_geth = usb_get_function_instance("geth"); +		if (IS_ERR(fi_geth)) +			return PTR_ERR(fi_geth); + +		geth_opts = container_of(fi_geth, struct f_gether_opts, +					 func_inst); + +		net = geth_opts->net; +  		eth_config_driver.label = "CDC Subset/SAFE";  		device_desc.idVendor = cpu_to_le16(SIMPLE_VENDOR_NUM); @@ -325,82 +373,105 @@ static int __init eth_bind(struct usb_composite_dev *cdev)  			device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC;  	} +	gether_set_qmult(net, qmult); +	if (!gether_set_host_addr(net, host_addr)) +		pr_info("using host ethernet address: %s", host_addr); +	if (!gether_set_dev_addr(net, dev_addr)) +		pr_info("using self ethernet address: %s", dev_addr); +  	if (has_rndis()) {  		/* RNDIS plus ECM-or-Subset */ +		gether_set_gadget(net, cdev->gadget); +		status = gether_register_netdev(net); +		if (status) +			goto fail; + +		if (use_eem) +			eem_opts->bound = true; +		else if (can_support_ecm(gadget)) +			ecm_opts->bound = true; +		else +			geth_opts->bound = true; + +		fi_rndis = usb_get_function_instance("rndis"); +		if (IS_ERR(fi_rndis)) { +			status = PTR_ERR(fi_rndis); +			goto fail; +		} + +		rndis_borrow_net(fi_rndis, net); +  		device_desc.idVendor = cpu_to_le16(RNDIS_VENDOR_NUM);  		device_desc.idProduct = cpu_to_le16(RNDIS_PRODUCT_NUM);  		device_desc.bNumConfigurations = 2;  	} -	gcnum = usb_gadget_controller_number(gadget); -	if (gcnum >= 0) -		device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum); -	else { -		/* We assume that can_support_ecm() tells the truth; -		 * but if the controller isn't recognized at all then -		 * that assumption is a bit more likely to be wrong. -		 */ -		dev_warn(&gadget->dev, -				"controller '%s' not recognized; trying %s\n", -				gadget->name, -				eth_config_driver.label); -		device_desc.bcdDevice = -			cpu_to_le16(0x0300 | 0x0099); -	} - -  	/* Allocate string descriptor numbers ... note that string  	 * contents can be overridden by the composite_dev glue.  	 */ -	/* device descriptor strings: manufacturer, product */ -	snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", -		init_utsname()->sysname, init_utsname()->release, -		gadget->name); -	status = usb_string_id(cdev); -	if (status < 0) -		goto fail; -	strings_dev[STRING_MANUFACTURER_IDX].id = status; -	device_desc.iManufacturer = status; - -	status = usb_string_id(cdev); +	status = usb_string_ids_tab(cdev, strings_dev);  	if (status < 0) -		goto fail; -	strings_dev[STRING_PRODUCT_IDX].id = status; -	device_desc.iProduct = status; +		goto fail1; +	device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; +	device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;  	/* register our configuration(s); RNDIS first, if it's used */  	if (has_rndis()) {  		status = usb_add_config(cdev, &rndis_config_driver,  				rndis_do_config);  		if (status < 0) -			goto fail; +			goto fail1;  	}  	status = usb_add_config(cdev, ð_config_driver, eth_do_config);  	if (status < 0) -		goto fail; +		goto fail1; +	usb_composite_overwrite_options(cdev, &coverwrite);  	dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n",  			DRIVER_DESC);  	return 0; +fail1: +	if (has_rndis()) +		usb_put_function_instance(fi_rndis);  fail: -	gether_cleanup(); +	if (use_eem) +		usb_put_function_instance(fi_eem); +	else if (can_support_ecm(gadget)) +		usb_put_function_instance(fi_ecm); +	else +		usb_put_function_instance(fi_geth);  	return status;  }  static int __exit eth_unbind(struct usb_composite_dev *cdev)  { -	gether_cleanup(); +	if (has_rndis()) { +		usb_put_function(f_rndis); +		usb_put_function_instance(fi_rndis); +	} +	if (use_eem) { +		usb_put_function(f_eem); +		usb_put_function_instance(fi_eem); +	} else if (can_support_ecm(cdev->gadget)) { +		usb_put_function(f_ecm); +		usb_put_function_instance(fi_ecm); +	} else { +		usb_put_function(f_geth); +		usb_put_function_instance(fi_geth); +	}  	return 0;  } -static struct usb_composite_driver eth_driver = { +static __refdata struct usb_composite_driver eth_driver = {  	.name		= "g_ether",  	.dev		= &device_desc,  	.strings	= dev_strings, +	.max_speed	= USB_SPEED_SUPER, +	.bind		= eth_bind,  	.unbind		= __exit_p(eth_unbind),  }; @@ -410,7 +481,7 @@ MODULE_LICENSE("GPL");  static int __init init(void)  { -	return usb_composite_probe(ð_driver, eth_bind); +	return usb_composite_probe(ð_driver);  }  module_init(init);  | 
