diff options
-rw-r--r-- | drivers/usb/gadget/android.c | 65 | ||||
-rw-r--r-- | drivers/usb/gadget/f_acm.c | 40 | ||||
-rw-r--r-- | drivers/usb/gadget/u_serial.c | 2 | ||||
-rw-r--r-- | include/linux/usb/android_composite.h | 5 |
4 files changed, 98 insertions, 14 deletions
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index ed4f5739cb9c..d9d4d2fd5998 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -108,7 +108,7 @@ static struct usb_device_descriptor device_desc = { }; static struct list_head _functions = LIST_HEAD_INIT(_functions); -static int _registered_function_count = 0; +static bool _are_functions_bound; static struct android_usb_function *get_function(const char *name) { @@ -120,6 +120,50 @@ static struct android_usb_function *get_function(const char *name) return 0; } +static bool are_functions_registered(struct android_dev *dev) +{ + char **functions = dev->functions; + int i; + + /* Look only for functions required by the board config */ + for (i = 0; i < dev->num_functions; i++) { + char *name = *functions++; + bool is_match = false; + /* Could reuse get_function() here, but a reverse search + * should yield less comparisons overall */ + struct android_usb_function *f; + list_for_each_entry_reverse(f, &_functions, list) { + if (!strcmp(name, f->name)) { + is_match = true; + break; + } + } + if (is_match) + continue; + else + return false; + } + + return true; +} + +static bool should_bind_functions(struct android_dev *dev) +{ + /* Don't waste time if the main driver hasn't bound */ + if (!dev->config) + return false; + + /* Don't waste time if we've already bound the functions */ + if (_are_functions_bound) + return false; + + /* This call is the most costly, so call it last */ + if (!are_functions_registered(dev)) + return false; + + return true; +} + static void bind_functions(struct android_dev *dev) { struct android_usb_function *f; @@ -134,6 +178,8 @@ static void bind_functions(struct android_dev *dev) else printk(KERN_ERR "function %s not found in bind_functions\n", name); } + + _are_functions_bound = true; } static int android_bind_config(struct usb_configuration *c) @@ -143,8 +189,7 @@ static int android_bind_config(struct usb_configuration *c) printk(KERN_DEBUG "android_bind_config\n"); dev->config = c; - /* bind our functions if they have all registered */ - if (_registered_function_count == dev->num_functions) + if (should_bind_functions(dev)) bind_functions(dev); return 0; @@ -188,7 +233,13 @@ static int product_has_function(struct android_usb_product *p, int i; for (i = 0; i < count; i++) { - if (!strcmp(name, *functions++)) + /* For functions with multiple instances, usb_function.name + * will have an index appended to the core name (ex: acm0), + * while android_usb_product.functions[i] will only have the + * core name (ex: acm). So, only compare up to the length of + * android_usb_product.functions[i]. + */ + if (!strncmp(name, functions[i], strlen(functions[i]))) return 1; } return 0; @@ -295,12 +346,8 @@ void android_register_function(struct android_usb_function *f) printk(KERN_INFO "android_register_function %s\n", f->name); list_add_tail(&f->list, &_functions); - _registered_function_count++; - /* bind our functions if they have all registered - * and the main driver has bound. - */ - if (dev && dev->config && _registered_function_count == dev->num_functions) + if (dev && should_bind_functions(dev)) bind_functions(dev); } diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c index e3e5410d922f..2d7fdcce310d 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/f_acm.c @@ -698,6 +698,7 @@ acm_unbind(struct usb_configuration *c, struct usb_function *f) usb_free_descriptors(f->hs_descriptors); usb_free_descriptors(f->descriptors); gs_free_req(acm->notify, acm->notify_req); + kfree(acm->port.func.name); kfree(acm); } @@ -769,7 +770,11 @@ int acm_bind_config(struct usb_configuration *c, u8 port_num) acm->port.disconnect = acm_disconnect; acm->port.send_break = acm_send_break; - acm->port.func.name = "acm"; + acm->port.func.name = kasprintf(GFP_KERNEL, "acm%u", port_num); + if (!acm->port.func.name) { + kfree(acm); + return -ENOMEM; + } acm->port.func.strings = acm_strings; /* descriptors are per-instance copies */ acm->port.func.bind = acm_bind; @@ -785,12 +790,38 @@ int acm_bind_config(struct usb_configuration *c, u8 port_num) } #ifdef CONFIG_USB_ANDROID_ACM +#include <linux/platform_device.h> + +static struct acm_platform_data *acm_pdata; + +static int acm_probe(struct platform_device *pdev) +{ + acm_pdata = pdev->dev.platform_data; + return 0; +} + +static struct platform_driver acm_platform_driver = { + .driver = { .name = "acm", }, + .probe = acm_probe, +}; int acm_function_bind_config(struct usb_configuration *c) { - int ret = acm_bind_config(c, 0); - if (ret == 0) - gserial_setup(c->cdev->gadget, 1); + int i; + u8 num_inst = acm_pdata ? acm_pdata->num_inst : 1; + int ret = gserial_setup(c->cdev->gadget, num_inst); + + if (ret) + return ret; + + for (i = 0; i < num_inst; i++) { + ret = acm_bind_config(c, i); + if (ret) { + pr_err("Could not bind acm%u config\n", i); + break; + } + } + return ret; } @@ -802,6 +833,7 @@ static struct android_usb_function acm_function = { static int __init init(void) { printk(KERN_INFO "f_acm init\n"); + platform_driver_register(&acm_platform_driver); android_register_function(&acm_function); return 0; } diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c index 01e5354a4c20..fdc934523c0f 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/u_serial.c @@ -118,7 +118,7 @@ struct gs_port { }; /* increase N_PORTS if you need more */ -#define N_PORTS 4 +#define N_PORTS 8 static struct portmaster { struct mutex lock; /* protect open/close */ struct gs_port *port; diff --git a/include/linux/usb/android_composite.h b/include/linux/usb/android_composite.h index ac09dcb71775..62e72e3bd2b6 100644 --- a/include/linux/usb/android_composite.h +++ b/include/linux/usb/android_composite.h @@ -88,6 +88,11 @@ struct usb_ether_platform_data { const char *vendorDescr; }; +/* Platform data for ACM driver. */ +struct acm_platform_data { + u8 num_inst; +}; + extern void android_register_function(struct android_usb_function *f); extern void android_enable_function(struct usb_function *f, int enable); |