diff options
-rw-r--r-- | drivers/usb/gadget/Kconfig | 4 | ||||
-rw-r--r-- | drivers/usb/gadget/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/gadget/acm_ms.c | 2 | ||||
-rw-r--r-- | drivers/usb/gadget/cdc2.c | 2 | ||||
-rw-r--r-- | drivers/usb/gadget/f_acm.c | 141 | ||||
-rw-r--r-- | drivers/usb/gadget/multi.c | 2 | ||||
-rw-r--r-- | drivers/usb/gadget/nokia.c | 1 | ||||
-rw-r--r-- | drivers/usb/gadget/serial.c | 83 | ||||
-rw-r--r-- | drivers/usb/gadget/u_serial.h | 5 |
9 files changed, 186 insertions, 55 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 8aefbbddf2a..b61c72fc305 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -500,6 +500,9 @@ config USB_LIBCOMPOSITE tristate depends on USB_GADGET +config USB_F_ACM + tristate + config USB_F_SS_LB tristate @@ -758,6 +761,7 @@ config USB_GADGET_TARGET config USB_G_SERIAL tristate "Serial Gadget (with CDC ACM and CDC OBEX support)" select USB_U_SERIAL + select USB_F_ACM select USB_LIBCOMPOSITE help The Serial Gadget talks to the Linux-USB generic serial driver. diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index b88ee775de6..97a13c349cc 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -76,6 +76,7 @@ obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o # USB Functions +obj-$(CONFIG_USB_F_ACM) += f_acm.o f_ss_lb-y := f_loopback.o f_sourcesink.o obj-$(CONFIG_USB_F_SS_LB) += f_ss_lb.o obj-$(CONFIG_USB_U_SERIAL) += u_serial.o diff --git a/drivers/usb/gadget/acm_ms.c b/drivers/usb/gadget/acm_ms.c index eb0fbdae5cc..4105a06582f 100644 --- a/drivers/usb/gadget/acm_ms.c +++ b/drivers/usb/gadget/acm_ms.c @@ -40,7 +40,7 @@ * the runtime footprint, and giving us at least some parts of what * a "gcc --combine ... part1.c part2.c part3.c ... " build would. */ - +#define USB_FACM_INCLUDED #include "f_acm.c" #include "f_mass_storage.c" diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c index 16911a5aeb0..13b17f0b2da 100644 --- a/drivers/usb/gadget/cdc2.c +++ b/drivers/usb/gadget/cdc2.c @@ -42,7 +42,7 @@ USB_GADGET_COMPOSITE_OPTIONS(); * the runtime footprint, and giving us at least some parts of what * a "gcc --combine ... part1.c part2.c part3.c ... " build would. */ - +#define USB_FACM_INCLUDED #include "f_acm.c" #include "f_ecm.c" #include "u_ether.c" diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c index 3178fa70916..3ea7dc89b43 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/f_acm.c @@ -16,7 +16,9 @@ #include <linux/slab.h> #include <linux/kernel.h> +#include <linux/module.h> #include <linux/device.h> +#include <linux/err.h> #include "u_serial.h" #include "gadget_chips.h" @@ -608,6 +610,22 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) int status; struct usb_ep *ep; + /* REVISIT might want instance-specific strings to help + * distinguish instances ... + */ + + /* maybe allocate device-global string IDs, and patch descriptors */ + if (acm_string_defs[0].id == 0) { + status = usb_string_ids_tab(c->cdev, acm_string_defs); + if (status < 0) + return status; + acm_control_interface_desc.iInterface = + acm_string_defs[ACM_CTRL_IDX].id; + acm_data_interface_desc.iInterface = + acm_string_defs[ACM_DATA_IDX].id; + acm_iad_descriptor.iFunction = acm_string_defs[ACM_IAD_IDX].id; + } + /* allocate instance-specific interface IDs, and patch descriptors */ status = usb_interface_id(c, f); if (status < 0) @@ -700,14 +718,41 @@ fail: return status; } +static struct f_acm *acm_alloc_basic_func(void) +{ + struct f_acm *acm; + + acm = kzalloc(sizeof(*acm), GFP_KERNEL); + if (!acm) + return NULL; + + spin_lock_init(&acm->lock); + + acm->port.connect = acm_connect; + acm->port.disconnect = acm_disconnect; + acm->port.send_break = acm_send_break; + + acm->port.func.name = "acm"; + acm->port.func.strings = acm_strings; + /* descriptors are per-instance copies */ + acm->port.func.bind = acm_bind; + acm->port.func.set_alt = acm_set_alt; + acm->port.func.setup = acm_setup; + acm->port.func.disable = acm_disable; + + return acm; +} + +#ifdef USB_FACM_INCLUDED static void -acm_unbind(struct usb_configuration *c, struct usb_function *f) +acm_old_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_acm *acm = func_to_acm(f); acm_string_defs[0].id = 0; usb_free_all_descriptors(f); - gs_free_req(acm->notify, acm->notify_req); + if (acm->notify_req) + gs_free_req(acm->notify, acm->notify_req); kfree(acm); } @@ -725,46 +770,74 @@ int acm_bind_config(struct usb_configuration *c, u8 port_num) struct f_acm *acm; int status; - /* REVISIT might want instance-specific strings to help - * distinguish instances ... - */ - - /* maybe allocate device-global string IDs, and patch descriptors */ - if (acm_string_defs[0].id == 0) { - status = usb_string_ids_tab(c->cdev, acm_string_defs); - if (status < 0) - return status; - acm_control_interface_desc.iInterface = - acm_string_defs[ACM_CTRL_IDX].id; - acm_data_interface_desc.iInterface = - acm_string_defs[ACM_DATA_IDX].id; - acm_iad_descriptor.iFunction = acm_string_defs[ACM_IAD_IDX].id; - } - /* allocate and initialize one new instance */ - acm = kzalloc(sizeof *acm, GFP_KERNEL); + acm = acm_alloc_basic_func(); if (!acm) return -ENOMEM; - spin_lock_init(&acm->lock); - acm->port_num = port_num; - - acm->port.connect = acm_connect; - acm->port.disconnect = acm_disconnect; - acm->port.send_break = acm_send_break; - - acm->port.func.name = "acm"; - acm->port.func.strings = acm_strings; - /* descriptors are per-instance copies */ - acm->port.func.bind = acm_bind; - acm->port.func.unbind = acm_unbind; - acm->port.func.set_alt = acm_set_alt; - acm->port.func.setup = acm_setup; - acm->port.func.disable = acm_disable; + acm->port.func.unbind = acm_old_unbind; status = usb_add_function(c, &acm->port.func); if (status) kfree(acm); return status; } + +#else + +static void acm_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_acm *acm = func_to_acm(f); + + acm_string_defs[0].id = 0; + usb_free_all_descriptors(f); + if (acm->notify_req) + gs_free_req(acm->notify, acm->notify_req); +} + +static void acm_free_func(struct usb_function *f) +{ + struct f_acm *acm = func_to_acm(f); + + kfree(acm); +} + +static struct usb_function *acm_alloc_func(struct usb_function_instance *fi) +{ + struct f_serial_opts *opts; + struct f_acm *acm; + + acm = acm_alloc_basic_func(); + if (!acm) + return ERR_PTR(-ENOMEM); + + opts = container_of(fi, struct f_serial_opts, func_inst); + acm->port_num = opts->port_num; + acm->port.func.unbind = acm_unbind; + acm->port.func.free_func = acm_free_func; + + return &acm->port.func; +} + +static void acm_free_instance(struct usb_function_instance *fi) +{ + struct f_serial_opts *opts; + + opts = container_of(fi, struct f_serial_opts, func_inst); + kfree(opts); +} + +static struct usb_function_instance *acm_alloc_instance(void) +{ + struct f_serial_opts *opts; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + opts->func_inst.free_func_inst = acm_free_instance; + return &opts->func_inst; +} +DECLARE_USB_FUNCTION_INIT(acm, acm_alloc_instance, acm_alloc_func); +MODULE_LICENSE("GPL"); +#endif diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index 9ca0ac0334e..24b0f5630d4 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -41,7 +41,7 @@ MODULE_LICENSE("GPL"); * a "gcc --combine ... part1.c part2.c part3.c ... " build would. */ #include "f_mass_storage.c" - +#define USB_FACM_INCLUDED #include "f_acm.c" #include "f_ecm.c" diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c index 084d0d7cb8f..def37403989 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/nokia.c @@ -37,6 +37,7 @@ * the runtime footprint, and giving us at least some parts of what * a "gcc --combine ... part1.c part2.c part3.c ... " build would. */ +#define USB_FACM_INCLUDED #include "f_acm.c" #include "f_ecm.c" #include "f_obex.c" diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index a883562f6a8..68d7bb06ebc 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -36,7 +36,6 @@ * the runtime footprint, and giving us at least some parts of what * a "gcc --combine ... part1.c part2.c part3.c ... " build would. */ -#include "f_acm.c" #include "f_obex.c" #include "f_serial.c" @@ -129,16 +128,6 @@ MODULE_PARM_DESC(n_ports, "number of ports to create, default=1"); /*-------------------------------------------------------------------------*/ static unsigned char tty_lines[MAX_U_SERIAL_PORTS]; -static int __init serial_bind_acm_config(struct usb_configuration *c) -{ - unsigned i; - int status = 0; - - for (i = 0; i < n_ports && status == 0; i++) - status = acm_bind_config(c, tty_lines[i]); - return status; -} - static int __init serial_bind_obex_config(struct usb_configuration *c) { unsigned i; @@ -166,13 +155,58 @@ static struct usb_configuration serial_config_driver = { .bmAttributes = USB_CONFIG_ATT_SELFPOWER, }; -static int gs_unbind(struct usb_composite_dev *cdev) +static struct usb_function_instance *fi_serial[MAX_U_SERIAL_PORTS]; +static struct usb_function *f_serial[MAX_U_SERIAL_PORTS]; + +static int serial_register_ports(struct usb_composite_dev *cdev, + struct usb_configuration *c, const char *f_name) { int i; + int ret; + + ret = usb_add_config_only(cdev, c); + if (ret) + goto out; + + for (i = 0; i < n_ports; i++) { + struct f_serial_opts *opts; + + fi_serial[i] = usb_get_function_instance(f_name); + if (IS_ERR(fi_serial[i])) { + ret = PTR_ERR(fi_serial[i]); + goto fail; + } + opts = container_of(fi_serial[i], struct f_serial_opts, func_inst); + opts->port_num = tty_lines[i]; + + f_serial[i] = usb_get_function(fi_serial[i]); + if (IS_ERR(f_serial[i])) { + ret = PTR_ERR(f_serial[i]); + goto err_get_func; + } + + ret = usb_add_function(c, f_serial[i]); + if (ret) + goto err_add_func; + } - for (i = 0; i < n_ports; i++) - gserial_free_line(tty_lines[i]); return 0; + +err_add_func: + usb_put_function(f_serial[i]); +err_get_func: + usb_put_function_instance(fi_serial[i]); + +fail: + i--; + while (i >= 0) { + usb_remove_function(c, f_serial[i]); + usb_put_function(f_serial[i]); + usb_put_function_instance(fi_serial[i]); + i--; + } +out: + return ret; } static int __init gs_bind(struct usb_composite_dev *cdev) @@ -204,10 +238,11 @@ static int __init gs_bind(struct usb_composite_dev *cdev) } /* register our configuration */ - if (use_acm) - status = usb_add_config(cdev, &serial_config_driver, - serial_bind_acm_config); - else if (use_obex) + if (use_acm) { + status = serial_register_ports(cdev, &serial_config_driver, + "acm"); + usb_ep_autoconfig_reset(cdev->gadget); + } else if (use_obex) status = usb_add_config(cdev, &serial_config_driver, serial_bind_obex_config); else @@ -228,6 +263,18 @@ fail: return status; } +static int gs_unbind(struct usb_composite_dev *cdev) +{ + int i; + + for (i = 0; i < n_ports; i++) { + usb_put_function(f_serial[i]); + usb_put_function_instance(fi_serial[i]); + gserial_free_line(tty_lines[i]); + } + return 0; +} + static __refdata struct usb_composite_driver gserial_driver = { .name = "g_serial", .dev = &device_desc, diff --git a/drivers/usb/gadget/u_serial.h b/drivers/usb/gadget/u_serial.h index f06ee9a5db5..66ce73a0050 100644 --- a/drivers/usb/gadget/u_serial.h +++ b/drivers/usb/gadget/u_serial.h @@ -17,6 +17,11 @@ #define MAX_U_SERIAL_PORTS 4 +struct f_serial_opts { + struct usb_function_instance func_inst; + u8 port_num; +}; + /* * One non-multiplexed "serial" I/O port ... there can be several of these * on any given USB peripheral device, if it provides enough endpoints. |