Release 4.7 drivers/usb/gadget/legacy/ether.c
  
  
/*
 * ether.c -- Ethernet gadget driver, with CDC and non-CDC options
 *
 * 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.
 */
/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
#include <linux/netdevice.h>
#if defined USB_ETH_RNDIS
#  undef USB_ETH_RNDIS
#endif
#ifdef CONFIG_USB_ETH_RNDIS
#  define USB_ETH_RNDIS y
#endif
#include "u_ether.h"
/*
 * Ethernet gadget driver -- with CDC and non-CDC options
 * Builds on hardware support for a full duplex link.
 *
 * CDC Ethernet is the standard USB solution for sending Ethernet frames
 * using USB.  Real hardware tends to use the same framing protocol but look
 * different for control features.  This driver strongly prefers to use
 * this USB-IF standard as its open-systems interoperability solution;
 * most host side USB stacks (except from Microsoft) support it.
 *
 * This is sometimes called "CDC ECM" (Ethernet Control Model) to support
 * TLA-soup.  "CDC ACM" (Abstract Control Model) is for modems, and a new
 * "CDC EEM" (Ethernet Emulation Model) is starting to spread.
 *
 * There's some hardware that can't talk CDC ECM.  We make that hardware
 * implement a "minimalist" vendor-agnostic CDC core:  same framing, but
 * link-level setup only requires activating the configuration.  Only the
 * endpoint descriptors, and product/vendor IDs, are relevant; no control
 * operations are available.  Linux supports it, but other host operating
 * systems may not.  (This is a subset of CDC Ethernet.)
 *
 * It turns out that if you add a few descriptors to that "CDC Subset",
 * (Windows) host side drivers from MCCI can treat it as one submode of
 * a proprietary scheme called "SAFE" ... without needing to know about
 * specific product/vendor IDs.  So we do that, making it easier to use
 * those MS-Windows drivers.  Those added descriptors make it resemble a
 * CDC MDLM device, but they don't change device behavior at all.  (See
 * MCCI Engineering report 950198 "SAFE Networking Functions".)
 *
 * A third option is also in use.  Rather than CDC Ethernet, or something
 * simpler, Microsoft pushes their own approach: RNDIS.  The published
 * RNDIS specs are ambiguous and appear to be incomplete, and are also
 * needlessly complex.  They borrow more from CDC ACM than CDC ECM.
 */
#define DRIVER_DESC		"Ethernet Gadget"
#define DRIVER_VERSION		"Memorial Day 2008"
#ifdef USB_ETH_RNDIS
#define PREFIX			"RNDIS/"
#else
#define PREFIX			""
#endif
/*
 * This driver aims for interoperability by using CDC ECM unless
 *
 *              can_support_ecm()
 *
 * returns false, in which case it supports the CDC Subset.  By default,
 * that returns true; most hardware has no problems with CDC ECM, that's
 * a good default.  Previous versions of this driver had no default; this
 * version changes that, removing overhead for new controller support.
 *
 *      IF YOUR HARDWARE CAN'T SUPPORT CDC ECM, UPDATE THAT ROUTINE!
 */
static inline bool has_rndis(void)
{
#ifdef	USB_ETH_RNDIS
	return true;
#else
	return false;
#endif
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| david brownell | david brownell | 21 | 95.45% | 2 | 66.67% | 
| michal nazarewicz | michal nazarewicz | 1 | 4.55% | 1 | 33.33% | 
 | Total | 22 | 100.00% | 3 | 100.00% | 
#include <linux/module.h>
#include "u_ecm.h"
#include "u_gether.h"
#ifdef	USB_ETH_RNDIS
#include "u_rndis.h"
#include "rndis.h"
#else
#define rndis_borrow_net(...) do {} while (0)
#endif
#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.
 */
/* 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 */
/* For hardware that can't talk CDC, we use the same vendor ID that
 * ARM Linux has used for ethernet-over-usb, both with sa1100 and
 * with pxa250.  We're protocol-compatible, if the host-side drivers
 * use the endpoint descriptors.  bcdDevice (version) is nonzero, so
 * drivers that need to hard-wire endpoint numbers have a hook.
 *
 * The protocol is a minimal subset of CDC Ether, which works on any bulk
 * hardware that's not deeply broken ... even on hardware that can't talk
 * RNDIS (like SA-1100, with no interrupt endpoint, or anything that
 * doesn't handle control-OUT).
 */
#define	SIMPLE_VENDOR_NUM	0x049f
#define	SIMPLE_PRODUCT_NUM	0x505a
/* For hardware that can talk RNDIS and either of the above protocols,
 * use this ID ... the windows INF files will know it.  Unless it's
 * used with CDC Ethernet, Linux 2.4 hosts will need updates to choose
 * the non-RNDIS configuration.
 */
#define RNDIS_VENDOR_NUM	0x0525	
/* NetChip */
#define RNDIS_PRODUCT_NUM	0xa4a2	
/* Ethernet/RNDIS Gadget */
/* For EEM gadgets */
#define EEM_VENDOR_NUM		0x1d6b	
/* Linux Foundation */
#define EEM_PRODUCT_NUM		0x0102	
/* EEM Gadget */
/*-------------------------------------------------------------------------*/
static struct usb_device_descriptor device_desc = {
	.bLength =		sizeof device_desc,
	.bDescriptorType =	USB_DT_DEVICE,
	/* .bcdUSB = DYNAMIC */
	.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 const struct usb_descriptor_header *otg_desc[2];
static struct usb_string strings_dev[] = {
	[USB_GADGET_MANUFACTURER_IDX].s = "",
	[USB_GADGET_PRODUCT_IDX].s = PREFIX DRIVER_DESC,
	[USB_GADGET_SERIAL_IDX].s = "",
	{  } /* 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 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;
/*-------------------------------------------------------------------------*/
/*
 * We may not have an RNDIS configuration, but if we do it needs to be
 * the first one present.  That's to make Microsoft's drivers happy,
 * and to follow DOCSIS 1.0 (cable modem standard).
 */
static int 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)) {
		c->descriptors = otg_desc;
		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
	}
	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;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| david brownell | david brownell | 43 | 51.81% | 3 | 75.00% | 
| andrzej pietrasiewicz | andrzej pietrasiewicz | 40 | 48.19% | 1 | 25.00% | 
 | Total | 83 | 100.00% | 4 | 100.00% | 
static struct usb_configuration rndis_config_driver = {
	.label			= "RNDIS",
	.bConfigurationValue	= 2,
	/* .iConfiguration = DYNAMIC */
	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER,
};
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_USB_ETH_EEM
static bool use_eem = 1;
#else
static bool use_eem;
#endif
module_param(use_eem, bool, 0);
MODULE_PARM_DESC(use_eem, "use CDC EEM mode");
/*
 * We _always_ have an ECM, CDC Subset, or EEM configuration.
 */
static int 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)) {
		c->descriptors = otg_desc;
		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
	}
	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;
	}
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| andrzej pietrasiewicz | andrzej pietrasiewicz | 122 | 62.89% | 3 | 27.27% | 
| david brownell | david brownell | 55 | 28.35% | 5 | 45.45% | 
| brian niebuhr | brian niebuhr | 10 | 5.15% | 1 | 9.09% | 
| aras vaichas | aras vaichas | 6 | 3.09% | 1 | 9.09% | 
| sebastian andrzej siewior | sebastian andrzej siewior | 1 | 0.52% | 1 | 9.09% | 
 | Total | 194 | 100.00% | 11 | 100.00% | 
static struct usb_configuration eth_config_driver = {
	/* .label = f(hardware) */
	.bConfigurationValue	= 1,
	/* .iConfiguration = DYNAMIC */
	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER,
};
/*-------------------------------------------------------------------------*/
static int eth_bind(struct usb_composite_dev *cdev)
{
	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 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(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);
		device_desc.idProduct = cpu_to_le16(SIMPLE_PRODUCT_NUM);
		if (!has_rndis())
			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;
	}
	/* Allocate string descriptor numbers ... note that string
         * contents can be overridden by the composite_dev glue.
         */
	status = usb_string_ids_tab(cdev, strings_dev);
	if (status < 0)
		goto fail1;
	device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
	device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
	if (gadget_is_otg(gadget) && !otg_desc[0]) {
		struct usb_descriptor_header *usb_desc;
		usb_desc = usb_otg_descriptor_alloc(gadget);
		if (!usb_desc)
			goto fail1;
		usb_otg_descriptor_init(gadget, usb_desc);
		otg_desc[0] = usb_desc;
		otg_desc[1] = NULL;
	}
	/* 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 fail2;
	}
	status = usb_add_config(cdev, ð_config_driver, eth_do_config);
	if (status < 0)
		goto fail2;
	usb_composite_overwrite_options(cdev, &coverwrite);
	dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n",
			DRIVER_DESC);
	return 0;
fail2:
	kfree(otg_desc[0]);
	otg_desc[0] = NULL;
fail1:
	if (has_rndis())
		usb_put_function_instance(fi_rndis);
fail:
	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;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| andrzej pietrasiewicz | andrzej pietrasiewicz | 320 | 49.54% | 4 | 18.18% | 
| david brownell | david brownell | 195 | 30.19% | 11 | 50.00% | 
| li jun | li jun | 75 | 11.61% | 1 | 4.55% | 
| brian niebuhr | brian niebuhr | 32 | 4.95% | 1 | 4.55% | 
| sebastian andrzej siewior | sebastian andrzej siewior | 20 | 3.10% | 4 | 18.18% | 
| uwe kleine-koenig | uwe kleine-koenig | 4 | 0.62% | 1 | 4.55% | 
 | Total | 646 | 100.00% | 22 | 100.00% | 
static int eth_unbind(struct usb_composite_dev *cdev)
{
	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);
	}
	kfree(otg_desc[0]);
	otg_desc[0] = NULL;
	return 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| andrzej pietrasiewicz | andrzej pietrasiewicz | 65 | 67.01% | 5 | 55.56% | 
| li jun | li jun | 15 | 15.46% | 1 | 11.11% | 
| david brownell | david brownell | 15 | 15.46% | 2 | 22.22% | 
| sebastian andrzej siewior | sebastian andrzej siewior | 2 | 2.06% | 1 | 11.11% | 
 | Total | 97 | 100.00% | 9 | 100.00% | 
static struct usb_composite_driver eth_driver = {
	.name		= "g_ether",
	.dev		= &device_desc,
	.strings	= dev_strings,
	.max_speed	= USB_SPEED_SUPER,
	.bind		= eth_bind,
	.unbind		= eth_unbind,
};
module_usb_composite_driver(eth_driver);
MODULE_DESCRIPTION(PREFIX DRIVER_DESC)
;
MODULE_AUTHOR("David Brownell, Benedikt Spanger");
MODULE_LICENSE("GPL");
Overall Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| andrzej pietrasiewicz | andrzej pietrasiewicz | 618 | 41.09% | 7 | 15.56% | 
| david brownell | david brownell | 615 | 40.89% | 17 | 37.78% | 
| li jun | li jun | 93 | 6.18% | 1 | 2.22% | 
| brian niebuhr | brian niebuhr | 85 | 5.65% | 2 | 4.44% | 
| sebastian andrzej siewior | sebastian andrzej siewior | 42 | 2.79% | 6 | 13.33% | 
| michal nazarewicz | michal nazarewicz | 21 | 1.40% | 1 | 2.22% | 
| aras vaichas | aras vaichas | 6 | 0.40% | 1 | 2.22% | 
| tobias klauser | tobias klauser | 5 | 0.33% | 1 | 2.22% | 
| tatyana brokhman | tatyana brokhman | 4 | 0.27% | 1 | 2.22% | 
| uwe kleine-koenig | uwe kleine-koenig | 4 | 0.27% | 1 | 2.22% | 
| arnd bergmann | arnd bergmann | 3 | 0.20% | 1 | 2.22% | 
| rusty russell | rusty russell | 2 | 0.13% | 1 | 2.22% | 
| harvey harrison | harvey harrison | 2 | 0.13% | 1 | 2.22% | 
| igor kotrasinski | igor kotrasinski | 1 | 0.07% | 1 | 2.22% | 
| klaus schwarzkopf | klaus schwarzkopf | 1 | 0.07% | 1 | 2.22% | 
| paul zimmerman | paul zimmerman | 1 | 0.07% | 1 | 2.22% | 
| robert p. j. day | robert p. j. day | 1 | 0.07% | 1 | 2.22% | 
 | Total | 1504 | 100.00% | 45 | 100.00% | 
  
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.