usb: gadget: Add RNDIS (Remote Network Driver Interface Specification) for supporting native USB Tethering.

This commit is contained in:
tytung 2011-08-17 23:59:31 +08:00
parent bdfeaacf04
commit 6ca79cd6f1
6 changed files with 134 additions and 34 deletions

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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);