diff options
Diffstat (limited to 'drivers/usb/gadget/f_rndis.c')
-rw-r--r-- | drivers/usb/gadget/f_rndis.c | 219 |
1 files changed, 187 insertions, 32 deletions
diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c index 882484a4039..6e6489b4958 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -26,8 +26,9 @@ #include <linux/slab.h> #include <linux/kernel.h> -#include <linux/device.h> +#include <linux/platform_device.h> #include <linux/etherdevice.h> +#include <linux/usb/android_composite.h> #include <asm/atomic.h> @@ -129,9 +130,16 @@ static struct usb_interface_descriptor rndis_control_intf = { /* .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 */ }; @@ -190,9 +198,16 @@ rndis_iad_descriptor = { .bFirstInterface = 0, /* XXX, hardcoded */ .bInterfaceCount = 2, // control + data +#ifdef CONFIG_USB_ANDROID_RNDIS_WCEIS + /* "Wireless" RNDIS; auto-detected by Windows */ + .bFunctionClass = USB_CLASS_WIRELESS_CONTROLLER, + .bFunctionSubClass = 1, + .bFunctionProtocol = 3, +#else .bFunctionClass = USB_CLASS_COMM, - .bFunctionSubClass = USB_CDC_SUBCLASS_ETHERNET, - .bFunctionProtocol = USB_CDC_PROTO_NONE, + .bFunctionSubClass = USB_CDC_SUBCLASS_ACM, + .bFunctionProtocol = USB_CDC_ACM_PROTO_VENDOR, +#endif /* .iFunction = DYNAMIC */ }; @@ -304,6 +319,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, @@ -487,10 +506,10 @@ static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt) usb_ep_disable(rndis->notify); } else { VDBG(cdev, "init rndis ctrl %d\n", intf); - rndis->notify_desc = ep_choose(cdev->gadget, - rndis->hs.notify, - rndis->fs.notify); } + rndis->notify_desc = ep_choose(cdev->gadget, + rndis->hs.notify, + rndis->fs.notify); usb_ep_enable(rndis->notify, rndis->notify_desc); rndis->notify->driver_data = rndis; @@ -504,11 +523,11 @@ static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (!rndis->port.in) { DBG(cdev, "init rndis\n"); - rndis->port.in = ep_choose(cdev->gadget, - rndis->hs.in, rndis->fs.in); - rndis->port.out = ep_choose(cdev->gadget, - rndis->hs.out, rndis->fs.out); } + rndis->port.in = ep_choose(cdev->gadget, + rndis->hs.in, rndis->fs.in); + rndis->port.out = ep_choose(cdev->gadget, + rndis->hs.out, rndis->fs.out); /* Avoid ZLPs; they can be troublesome. */ rndis->port.is_zlp_ok = false; @@ -590,6 +609,33 @@ static void rndis_close(struct gether *geth) rndis_signal_disconnect(rndis->config); } +static rndis_release(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct f_rndis *rndis = func_to_rndis(f); + int status; + struct usb_ep *ep; + + if (gadget_is_dualspeed(c->cdev->gadget) && f->hs_descriptors) + usb_free_descriptors(f->hs_descriptors); + if (f->descriptors) + usb_free_descriptors(f->descriptors); + + if (rndis->notify_req) { + kfree(rndis->notify_req->buf); + usb_ep_free_request(rndis->notify, rndis->notify_req); + } + + /* we might as well release our claims on endpoints */ + if (rndis->notify) + rndis->notify->driver_data = NULL; + if (rndis->port.out) + rndis->port.out_ep->driver_data = NULL; + if (rndis->port.in) + rndis->port.in_ep->driver_data = NULL; + +} + /*-------------------------------------------------------------------------*/ /* ethernet function driver setup/binding */ @@ -707,11 +753,12 @@ 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 0 -// FIXME - if (rndis_set_param_vendor(rndis->config, vendorID, - manufacturer)) - goto fail0; +#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 @@ -726,23 +773,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) return 0; fail: - if (gadget_is_dualspeed(c->cdev->gadget) && f->hs_descriptors) - usb_free_descriptors(f->hs_descriptors); - if (f->descriptors) - usb_free_descriptors(f->descriptors); - - if (rndis->notify_req) { - kfree(rndis->notify_req->buf); - usb_ep_free_request(rndis->notify, rndis->notify_req); - } - - /* we might as well release our claims on endpoints */ - if (rndis->notify) - rndis->notify->driver_data = NULL; - if (rndis->port.out) - rndis->port.out_ep->driver_data = NULL; - if (rndis->port.in) - rndis->port.in_ep->driver_data = NULL; + rndis_release(c, f); ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); @@ -850,6 +881,11 @@ rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) rndis->port.func.setup = rndis_setup; rndis->port.func.disable = rndis_disable; +#ifdef CONFIG_USB_ANDROID_RNDIS + /* start disabled */ + rndis->port.func.disabled = 1; +#endif + status = usb_add_function(c, &rndis->port.func); if (status) { kfree(rndis); @@ -858,3 +894,122 @@ fail: } return status; } + +#ifdef CONFIG_USB_ANDROID_RNDIS +#include "rndis.c" + +static int rndis_probe(struct platform_device *pdev) +{ + rndis_pdata = pdev->dev.platform_data; + return 0; +} + +static struct platform_driver rndis_platform_driver = { + .driver = { .name = "rndis", }, + .probe = rndis_probe, +}; + +int rndis_function_bind_config(struct usb_configuration *c) +{ + 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, rndis_pdata->ethaddr); + return ret; +} + +static int rndis_function_rebind_config(struct usb_configuration *c, + struct usb_function *f) +{ + + struct usb_composite_dev *cdev = c->cdev; + struct f_rndis *rndis = func_to_rndis(f); + int status1, status2 = 0; + struct usb_descriptor_header **desc = f->descriptors; + + /* re-allocate instance-specific interface IDs */ + status1 = usb_interface_id(c, f); + + if (status1 < 0) + goto fail; + rndis->ctrl_id = status1; + + rndis_iad_descriptor.bFirstInterface = status1; + rndis_control_intf.bInterfaceNumber = status1; + rndis_union_desc.bMasterInterface0 = status1; + + ((struct usb_interface_assoc_descriptor *)desc[0]) + ->bFirstInterface = status1; + ((struct usb_interface_descriptor *)desc[1]) + ->bInterfaceNumber = status1; + ((struct usb_cdc_union_desc *)desc[5]) + ->bMasterInterface0 = status1; + + status2 = usb_interface_id(c, f); + if (status2 < 0) + goto fail; + rndis->data_id = status2; + + rndis_data_intf.bInterfaceNumber = status2; + rndis_union_desc.bSlaveInterface0 = status2; + + ((struct usb_interface_descriptor *)desc[7]) + ->bInterfaceNumber = status2; + ((struct usb_cdc_union_desc *)desc[5]) + ->bSlaveInterface0 = status2; + + if (gadget_is_dualspeed(c->cdev->gadget)) { + desc = f->hs_descriptors; + ((struct usb_interface_assoc_descriptor *) + desc[0])->bFirstInterface = status1; + ((struct usb_interface_descriptor *) + desc[1])->bInterfaceNumber = status1; + ((struct usb_cdc_union_desc *) + desc[3])->bMasterInterface0 = status1; + + ((struct usb_interface_descriptor *) + desc[7])->bInterfaceNumber = status2; + ((struct usb_cdc_union_desc *) + desc[5])->bSlaveInterface0 = status2; + } + + return 0; + +fail: + rndis_release(c, f); + + ERROR(cdev, "%s: can't re-bind, err %d %d\n", f->name, + status1, status2); + if (status2 < 0) + status1 = status2; + return status1; +} + +static struct android_usb_function rndis_function = { + .name = "rndis", + .bind_config = rndis_function_bind_config, + .rebind_config = rndis_function_rebind_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; +} +module_init(init); + +#endif /* CONFIG_USB_ANDROID_RNDIS */ |