diff options
Diffstat (limited to 'drivers/usb/gadget/ncm.c')
| -rw-r--r-- | drivers/usb/gadget/ncm.c | 248 | 
1 files changed, 248 insertions, 0 deletions
diff --git a/drivers/usb/gadget/ncm.c b/drivers/usb/gadget/ncm.c new file mode 100644 index 00000000000..99c179ad729 --- /dev/null +++ b/drivers/usb/gadget/ncm.c @@ -0,0 +1,248 @@ +/* + * ncm.c -- NCM gadget driver + * + * Copyright (C) 2010 Nokia Corporation + * Contact: Yauheni Kaliuta <yauheni.kaliuta@nokia.com> + * + * The driver borrows from ether.c which is: + * + * Copyright (C) 2003-2005,2008 David Brownell + * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger + * Copyright (C) 2008 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + */ + +/* #define DEBUG */ +/* #define VERBOSE_DEBUG */ + +#include <linux/kernel.h> +#include <linux/utsname.h> + + +#include "u_ether.h" + +#define DRIVER_DESC		"NCM Gadget" + +/*-------------------------------------------------------------------------*/ + +/* + * 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 "f_ncm.c" +#include "u_ether.c" + +/*-------------------------------------------------------------------------*/ + +/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!! + * Instead:  allocate your own, using normal USB-IF procedures. + */ + +/* Thanks to NetChip Technologies for donating this product ID. + * It's for devices with only CDC Ethernet configurations. + */ +#define CDC_VENDOR_NUM		0x0525	/* NetChip */ +#define CDC_PRODUCT_NUM		0xa4a1	/* Linux-USB Ethernet Gadget */ + +/*-------------------------------------------------------------------------*/ + +static struct usb_device_descriptor device_desc = { +	.bLength =		sizeof device_desc, +	.bDescriptorType =	USB_DT_DEVICE, + +	.bcdUSB =		cpu_to_le16 (0x0200), + +	.bDeviceClass =		USB_CLASS_COMM, +	.bDeviceSubClass =	0, +	.bDeviceProtocol =	0, +	/* .bMaxPacketSize0 = f(hardware) */ + +	/* Vendor and product id defaults change according to what configs +	 * we support.  (As does bNumConfigurations.)  These values can +	 * also be overridden by module parameters. +	 */ +	.idVendor =		cpu_to_le16 (CDC_VENDOR_NUM), +	.idProduct =		cpu_to_le16 (CDC_PRODUCT_NUM), +	/* .bcdDevice = f(hardware) */ +	/* .iManufacturer = DYNAMIC */ +	/* .iProduct = DYNAMIC */ +	/* NO SERIAL NUMBER */ +	.bNumConfigurations =	1, +}; + +static struct usb_otg_descriptor otg_descriptor = { +	.bLength =		sizeof otg_descriptor, +	.bDescriptorType =	USB_DT_OTG, + +	/* REVISIT SRP-only hardware is possible, although +	 * it would not be called "OTG" ... +	 */ +	.bmAttributes =		USB_OTG_SRP | USB_OTG_HNP, +}; + +static const struct usb_descriptor_header *otg_desc[] = { +	(struct usb_descriptor_header *) &otg_descriptor, +	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 = DRIVER_DESC, +	{  } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_dev = { +	.language	= 0x0409,	/* en-us */ +	.strings	= strings_dev, +}; + +static struct usb_gadget_strings *dev_strings[] = { +	&stringtab_dev, +	NULL, +}; + +static u8 hostaddr[ETH_ALEN]; + +/*-------------------------------------------------------------------------*/ + +static int __init ncm_do_config(struct usb_configuration *c) +{ +	/* FIXME alloc iConfiguration string, set it in c->strings */ + +	if (gadget_is_otg(c->cdev->gadget)) { +		c->descriptors = otg_desc; +		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; +	} + +	return ncm_bind_config(c, hostaddr); +} + +static struct usb_configuration ncm_config_driver = { +	/* .label = f(hardware) */ +	.label			= "CDC Ethernet (NCM)", +	.bConfigurationValue	= 1, +	/* .iConfiguration = DYNAMIC */ +	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER, +}; + +/*-------------------------------------------------------------------------*/ + +static int __init gncm_bind(struct usb_composite_dev *cdev) +{ +	int			gcnum; +	struct usb_gadget	*gadget = cdev->gadget; +	int			status; + +	/* set up network link layer */ +	status = gether_setup(cdev->gadget, hostaddr); +	if (status < 0) +		return status; + +	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, +			 ncm_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); +	if (status < 0) +		goto fail; +	strings_dev[STRING_PRODUCT_IDX].id = status; +	device_desc.iProduct = status; + +	status = usb_add_config(cdev, &ncm_config_driver, +				ncm_do_config); +	if (status < 0) +		goto fail; + +	dev_info(&gadget->dev, "%s\n", DRIVER_DESC); + +	return 0; + +fail: +	gether_cleanup(); +	return status; +} + +static int __exit gncm_unbind(struct usb_composite_dev *cdev) +{ +	gether_cleanup(); +	return 0; +} + +static struct usb_composite_driver ncm_driver = { +	.name		= "g_ncm", +	.dev		= &device_desc, +	.strings	= dev_strings, +	.unbind		= __exit_p(gncm_unbind), +}; + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Yauheni Kaliuta"); +MODULE_LICENSE("GPL"); + +static int __init init(void) +{ +	return usb_composite_probe(&ncm_driver, gncm_bind); +} +module_init(init); + +static void __exit cleanup(void) +{ +	usb_composite_unregister(&ncm_driver); +} +module_exit(cleanup);  |