summaryrefslogtreecommitdiff
path: root/drivers/usb/gadget/f_rndis.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/gadget/f_rndis.c')
-rw-r--r--drivers/usb/gadget/f_rndis.c219
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 */