usb: gadget: Add RNDIS (Remote Network Driver Interface Specification) for supporting native USB Tethering.
This commit is contained in:
		| @@ -320,6 +320,21 @@ static struct platform_device usb_mass_storage_device = { | ||||
| 	}, | ||||
| }; | ||||
|  | ||||
| #ifdef CONFIG_USB_ANDROID_RNDIS | ||||
| static struct usb_ether_platform_data rndis_pdata = { | ||||
| 	.vendorID	= 0x0bb4, | ||||
| 	.vendorDescr	= "HTC", | ||||
| }; | ||||
|  | ||||
| static struct platform_device rndis_device = { | ||||
| 	.name	= "rndis", | ||||
| 	.id	= -1, | ||||
| 	.dev	= { | ||||
| 		.platform_data = &rndis_pdata, | ||||
| 	}, | ||||
| }; | ||||
| #endif | ||||
|  | ||||
| static struct android_usb_platform_data android_usb_pdata = { | ||||
| 	.vendor_id	= 0x0bb4, | ||||
| 	.product_id	= 0x0c02, | ||||
| @@ -350,6 +365,9 @@ static void htcleo_add_usb_devices(void) | ||||
| 	gpio_set_value(HTCLEO_GPIO_USBPHY_3V3_ENABLE, 1); | ||||
| 	platform_device_register(&msm_device_hsusb); | ||||
| 	platform_device_register(&usb_mass_storage_device); | ||||
| #ifdef CONFIG_USB_ANDROID_RNDIS | ||||
| 	platform_device_register(&rndis_device); | ||||
| #endif | ||||
| 	platform_device_register(&android_usb_device); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -25,14 +25,18 @@ static char *usb_functions_ums[] = { | ||||
| 	"usb_mass_storage", | ||||
| }; | ||||
|  | ||||
| static char *usb_functions_adb[] = { | ||||
| static char *usb_functions_ums_adb[] = { | ||||
| 	"usb_mass_storage", | ||||
| 	"adb", | ||||
| }; | ||||
|  | ||||
| #ifdef CONFIG_USB_ANDROID_RNDIS | ||||
| static char *usb_functions_rndis[] = { | ||||
| 	"ether", | ||||
| 	"rndis", | ||||
| }; | ||||
| static char *usb_functions_rndis_adb[] = { | ||||
| 	"rndis", | ||||
| 	"adb", | ||||
| }; | ||||
| #endif | ||||
|  | ||||
| @@ -110,7 +114,7 @@ static char *usb_functions_diag_serial[] = { | ||||
|  | ||||
| static char *usb_functions_all[] = { | ||||
| #ifdef CONFIG_USB_ANDROID_RNDIS | ||||
| 	"ether", | ||||
| 	"rndis", | ||||
| #endif | ||||
| #ifdef CONFIG_USB_ANDROID_MTP | ||||
| 	"mtp", | ||||
| @@ -130,15 +134,15 @@ static char *usb_functions_all[] = { | ||||
|  | ||||
| static struct android_usb_product usb_products[] = { | ||||
| 	{ | ||||
| 		 .product_id = 0x0c02, /* vary by board */ | ||||
| 		.num_functions	= ARRAY_SIZE(usb_functions_adb), | ||||
| 		.functions	= usb_functions_adb, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.product_id	= 0x0ff9, | ||||
| 		.product_id     = 0x0c01, | ||||
| 		.num_functions	= ARRAY_SIZE(usb_functions_ums), | ||||
| 		.functions	= usb_functions_ums, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.product_id     = 0x0c02, | ||||
| 		.num_functions	= ARRAY_SIZE(usb_functions_ums_adb), | ||||
| 		.functions	= usb_functions_ums_adb, | ||||
| 	}, | ||||
| #ifdef CONFIG_USB_ANDROID_SERIAL | ||||
| 	{ | ||||
| 		.product_id	= 0x0c03, | ||||
| @@ -215,6 +219,11 @@ static struct android_usb_product usb_products[] = { | ||||
| 		.num_functions	= ARRAY_SIZE(usb_functions_rndis), | ||||
| 		.functions	= usb_functions_rndis, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.product_id	= 0x0ffc, | ||||
| 		.num_functions	= ARRAY_SIZE(usb_functions_rndis_adb), | ||||
| 		.functions	= usb_functions_rndis_adb, | ||||
| 	}, | ||||
| #endif | ||||
| }; | ||||
| #endif | ||||
|   | ||||
| @@ -836,6 +836,15 @@ config USB_ANDROID_RNDIS | ||||
| 	help | ||||
| 	  Provides RNDIS ethernet function for android gadget driver. | ||||
|  | ||||
| config USB_ANDROID_RNDIS_WCEIS | ||||
|         boolean "Use Windows Internet Sharing Class/SubClass/Protocol" | ||||
|         depends on USB_ANDROID_RNDIS | ||||
|         help | ||||
|          Causes the driver to look like a Windows-compatible Internet | ||||
|          Sharing device, so Windows auto-detects it. | ||||
|          If you enable this option, the device is no longer CDC ethernet | ||||
|          compatible. | ||||
|  | ||||
| config USB_ANDROID_SERIAL | ||||
| 	boolean "Android gadget Serial function" | ||||
| 	depends on USB_ANDROID | ||||
|   | ||||
| @@ -440,7 +440,7 @@ int android_switch_function(unsigned func) | ||||
| 			!strcmp(f->name, "adb")) | ||||
| 			f->hidden = 0; | ||||
| 		else if ((func & (1 << USB_FUNCTION_RNDIS)) && | ||||
| 			!strcmp(f->name, "ether")) | ||||
| 			!strcmp(f->name, "rndis")) | ||||
| 			f->hidden = 0; | ||||
| 		else if ((func & (1 << USB_FUNCTION_DIAG)) && | ||||
| 			!strcmp(f->name, "diag")) | ||||
| @@ -499,23 +499,40 @@ void android_enable_function(struct usb_function *f, int enable) | ||||
|  | ||||
| 	if (!!f->hidden != disable) { | ||||
| 		f->hidden = disable; | ||||
|  | ||||
| #ifdef CONFIG_USB_ANDROID_RNDIS | ||||
| 		if (!strcmp(f->name, "rndis")) { | ||||
| 	struct usb_function		*func; | ||||
|  | ||||
| 			/* We need to specify the COMM class in the device descriptor | ||||
| 			 * if we are using RNDIS. | ||||
| 			 */ | ||||
| 			if (enable) | ||||
| #ifdef CONFIG_USB_ANDROID_RNDIS_WCEIS | ||||
| 				dev->cdev->desc.bDeviceClass = USB_CLASS_WIRELESS_CONTROLLER; | ||||
| #else | ||||
| 				dev->cdev->desc.bDeviceClass = USB_CLASS_COMM; | ||||
| #endif | ||||
| 			else | ||||
| 				dev->cdev->desc.bDeviceClass = USB_CLASS_PER_INTERFACE; | ||||
|  | ||||
| 			/* Windows does not support other interfaces when RNDIS is enabled, | ||||
| 			 * so we disable UMS when RNDIS is on. | ||||
| 			 */ | ||||
| 			list_for_each_entry(func, &android_config_driver.functions, list) { | ||||
| 				if (!strcmp(func->name, "usb_mass_storage")) { | ||||
| 					func->hidden = enable; | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| #endif | ||||
|  | ||||
| 		product_id = get_product_id(dev); | ||||
| 		device_desc.idProduct = __constant_cpu_to_le16(product_id); | ||||
| 		if (dev->cdev) | ||||
| 			dev->cdev->desc.idProduct = device_desc.idProduct; | ||||
|  | ||||
| #ifdef CONFIG_USB_ANDROID_RNDIS | ||||
| 		/* We need to specify the COMM class in the device descriptor | ||||
| 		* if we are using RNDIS. | ||||
| 		*/ | ||||
| 		if (!strcmp(f->name, "ether")) { | ||||
| 			if (enable) | ||||
| 				dev->cdev->desc.bDeviceClass = USB_CLASS_COMM; | ||||
| 			else | ||||
| 				dev->cdev->desc.bDeviceClass = USB_CLASS_PER_INTERFACE; | ||||
| 		} | ||||
| #endif | ||||
|  | ||||
| #ifdef CONFIG_USB_GADGET_MSM_72K | ||||
| 	msm_hsusb_request_reset(); | ||||
| #else | ||||
|   | ||||
| @@ -23,7 +23,7 @@ | ||||
| /* #define VERBOSE_DEBUG */ | ||||
|  | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/device.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/etherdevice.h> | ||||
| #include <linux/usb/android_composite.h> | ||||
|  | ||||
| @@ -95,7 +95,6 @@ struct f_rndis { | ||||
| 	atomic_t			notify_count; | ||||
| }; | ||||
|  | ||||
| static char		manufacturer [10] = "HTC"; | ||||
| static inline struct f_rndis *func_to_rndis(struct usb_function *f) | ||||
| { | ||||
| 	return container_of(f, struct f_rndis, port.func); | ||||
| @@ -128,9 +127,16 @@ static struct usb_interface_descriptor rndis_control_intf __initdata = { | ||||
| 	/* .bInterfaceNumber = DYNAMIC */ | ||||
| 	/* status endpoint is optional; this could be patched later */ | ||||
| 	.bNumEndpoints =	1, | ||||
| #ifdef CONFIG_USB_ANDROID_RNDIS_WCEIS | ||||
| 	/* "Wireless" RNDIS; auto-detected by Windows */ | ||||
| 	.bInterfaceClass =	USB_CLASS_WIRELESS_CONTROLLER, | ||||
| 	.bInterfaceSubClass =   1, | ||||
| 	.bInterfaceProtocol =	3, | ||||
| #else | ||||
| 	.bInterfaceClass =	USB_CLASS_COMM, | ||||
| 	.bInterfaceSubClass =   USB_CDC_SUBCLASS_ACM, | ||||
| 	.bInterfaceProtocol =   USB_CDC_ACM_PROTO_VENDOR, | ||||
| #endif | ||||
| 	/* .iInterface = DYNAMIC */ | ||||
| }; | ||||
|  | ||||
| @@ -286,6 +292,10 @@ static struct usb_gadget_strings *rndis_strings[] = { | ||||
| 	NULL, | ||||
| }; | ||||
|  | ||||
| #ifdef CONFIG_USB_ANDROID_RNDIS | ||||
| static struct usb_ether_platform_data *rndis_pdata; | ||||
| #endif | ||||
|  | ||||
| /*-------------------------------------------------------------------------*/ | ||||
|  | ||||
| static struct sk_buff *rndis_add_header(struct gether *port, | ||||
| @@ -583,7 +593,6 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) | ||||
| 	struct f_rndis		*rndis = func_to_rndis(f); | ||||
| 	int			status; | ||||
| 	struct usb_ep		*ep; | ||||
| 	u32	vendorID = 0x0bb4; | ||||
|  | ||||
| 	/* allocate instance-specific interface IDs */ | ||||
| 	status = usb_interface_id(c, f); | ||||
| @@ -689,9 +698,13 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) | ||||
| 	rndis_set_param_medium(rndis->config, NDIS_MEDIUM_802_3, 0); | ||||
| 	rndis_set_host_mac(rndis->config, rndis->ethaddr); | ||||
|  | ||||
| 	if (rndis_set_param_vendor(rndis->config, vendorID, | ||||
| 				manufacturer)) | ||||
| 		goto fail; | ||||
| #ifdef CONFIG_USB_ANDROID_RNDIS | ||||
| 	if (rndis_pdata) { | ||||
| 		if (rndis_set_param_vendor(rndis->config, rndis_pdata->vendorID, | ||||
| 					rndis_pdata->vendorDescr)) | ||||
| 			goto fail; | ||||
| 	} | ||||
| #endif | ||||
|  | ||||
| 	/* NOTE:  all that is done without knowing or caring about | ||||
| 	 * the network link ... which is unavailable to this code | ||||
| @@ -816,7 +829,7 @@ int __init rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) | ||||
| 	rndis->port.wrap = rndis_add_header; | ||||
| 	rndis->port.unwrap = rndis_rm_hdr; | ||||
|  | ||||
| 	rndis->port.func.name = "ether"; | ||||
| 	rndis->port.func.name = "rndis"; | ||||
| 	rndis->port.func.strings = rndis_strings; | ||||
| 	/* descriptors are per-instance copies */ | ||||
| 	rndis->port.func.bind = rndis_bind; | ||||
| @@ -842,26 +855,47 @@ fail: | ||||
| #ifdef CONFIG_USB_ANDROID_RNDIS | ||||
| #include "rndis.c" | ||||
|  | ||||
| // FIXME - using bogus MAC address for now | ||||
| static int __init rndis_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	rndis_pdata = pdev->dev.platform_data; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static u8 ethaddr[ETH_ALEN] = { 11, 22, 33, 44, 55, 66 }; | ||||
| static struct platform_driver rndis_platform_driver = { | ||||
| 	.driver = { .name = "rndis", }, | ||||
| 	.probe = rndis_probe, | ||||
| }; | ||||
|  | ||||
| int rndis_function_bind_config(struct usb_configuration *c) | ||||
| { | ||||
| 	int ret = gether_setup(c->cdev->gadget, ethaddr); | ||||
| 	int ret; | ||||
|  | ||||
| 	if (!rndis_pdata) { | ||||
| 		printk(KERN_ERR "rndis_pdata null in rndis_function_bind_config\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	printk(KERN_INFO | ||||
| 		"rndis_function_bind_config MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", | ||||
| 		rndis_pdata->ethaddr[0], rndis_pdata->ethaddr[1], | ||||
| 		rndis_pdata->ethaddr[2], rndis_pdata->ethaddr[3], | ||||
| 		rndis_pdata->ethaddr[4], rndis_pdata->ethaddr[5]); | ||||
|  | ||||
| 	ret = gether_setup(c->cdev->gadget, rndis_pdata->ethaddr); | ||||
| 	if (ret == 0) | ||||
| 		ret = rndis_bind_config(c, ethaddr); | ||||
| 		ret = rndis_bind_config(c, rndis_pdata->ethaddr); | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| static struct android_usb_function rndis_function = { | ||||
| 	.name = "ether", | ||||
| 	.name = "rndis", | ||||
| 	.bind_config = rndis_function_bind_config, | ||||
| }; | ||||
|  | ||||
| static int __init init(void) | ||||
| { | ||||
| 	printk(KERN_INFO "f_rndis init\n"); | ||||
| 	platform_driver_register(&rndis_platform_driver); | ||||
| 	android_register_function(&rndis_function); | ||||
| 	return 0; | ||||
| } | ||||
|   | ||||
| @@ -18,6 +18,7 @@ | ||||
| #define	__LINUX_USB_ANDROID_H | ||||
|  | ||||
| #include <linux/usb/composite.h> | ||||
| #include <linux/if_ether.h> | ||||
|  | ||||
| struct android_usb_function { | ||||
| 	struct list_head	list; | ||||
| @@ -26,6 +27,11 @@ struct android_usb_function { | ||||
| }; | ||||
|  | ||||
| struct android_usb_product { | ||||
| 	/* Vendor ID for this set of functions. | ||||
| 	 * Default vendor_id in platform data will be used if this is zero. | ||||
| 	 */ | ||||
| 	__u16 vendor_id; | ||||
|  | ||||
| 	/* Default product ID. */ | ||||
| 	__u16 product_id; | ||||
|  | ||||
| @@ -83,6 +89,13 @@ struct usb_mass_storage_platform_data { | ||||
| 	int cdrom_lun; | ||||
| }; | ||||
|  | ||||
| /* Platform data for USB ethernet driver. */ | ||||
| struct usb_ether_platform_data { | ||||
| 	u8	ethaddr[ETH_ALEN]; | ||||
| 	u32	vendorID; | ||||
| 	const char *vendorDescr; | ||||
| }; | ||||
|  | ||||
| extern void android_usb_set_connected(int on); | ||||
|  | ||||
| extern void android_register_function(struct android_usb_function *f); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user