Adds an additional configuration to g_ffs to allow for CDC ACM support in conjuction with FunctionFS. A module parameter is added to allow for multiple ACM ports to be instantiated. Signed-off-by: Matt Porter <matt.porter@xxxxxxxxxx> --- drivers/usb/gadget/Kconfig | 9 ++++++ drivers/usb/gadget/g_ffs.c | 71 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 1292a82..fa3c845 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -882,6 +882,15 @@ config USB_FUNCTIONFS_RNDIS help Include a configuration with RNDIS function (Ethernet) and the Filesystem. +config USB_FUNCTIONFS_ACM + bool "Include configuration with CDC ACM (Serial)" + depends on USB_FUNCTIONFS + select USB_U_SERIAL + select USB_F_ACM + help + Include a configuration with CDC ACM function (Serial) and the + Function Filesystem. + config USB_FUNCTIONFS_GENERIC bool "Include 'pure' configuration" depends on USB_FUNCTIONFS diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index 686b776..0849d3c 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -53,6 +53,14 @@ USB_ETHERNET_MODULE_PARAMETERS(); struct eth_dev; #endif +static int acm_ports = 1; +#ifdef CONFIG_USB_FUNCTIONFS_ACM +# include "u_serial.h" +static int acm_bind_config(struct usb_configuration *c, int ports); +module_param(acm_ports, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(acm_ports, "Number of ACM serial ports to instantiate"); +#endif + #include "f_fs.c" #define DRIVER_NAME "g_ffs" @@ -127,6 +135,9 @@ static struct usb_string gfs_strings[] = { #ifdef CONFIG_USB_FUNCTIONFS_ETH { .s = "FunctionFS + ECM" }, #endif +#ifdef CONFIG_USB_FUNCTIONFS_ACM + { .s = "FunctionFS + ACM" }, +#endif #ifdef CONFIG_USB_FUNCTIONFS_GENERIC { .s = "FunctionFS" }, #endif @@ -145,6 +156,7 @@ struct gfs_configuration { struct usb_configuration c; int (*eth)(struct usb_configuration *c, u8 *ethaddr, struct eth_dev *dev); + int (*acm)(struct usb_configuration *c, int ports); } gfs_configurations[] = { #ifdef CONFIG_USB_FUNCTIONFS_RNDIS { @@ -158,6 +170,12 @@ struct gfs_configuration { }, #endif +#ifdef CONFIG_USB_FUNCTIONFS_ACM + { + .acm = acm_bind_config, + }, +#endif + #ifdef CONFIG_USB_FUNCTIONFS_GENERIC { }, @@ -456,6 +474,12 @@ static int gfs_do_config(struct usb_configuration *c) return ret; } + if (gc->acm) { + ret = gc->acm(c, acm_ports); + if (unlikely(ret < 0)) + return ret; + } + for (i = 0; i < func_num; i++) { ret = functionfs_bind_config(c->cdev, c, ffs_tab[i].ffs_data); if (unlikely(ret < 0)) @@ -489,3 +513,50 @@ static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], } #endif + +#ifdef CONFIG_USB_FUNCTIONFS_ACM + +static struct usb_function_instance *fi[MAX_U_SERIAL_PORTS]; +static struct usb_function *f[MAX_U_SERIAL_PORTS]; + +static int acm_bind_config(struct usb_configuration *c, int ports) +{ + int i, ret; + + for (i = 0; i < ports; i++) { + fi[i] = usb_get_function_instance("acm"); + if (IS_ERR(fi[i])) { + ret = PTR_ERR(fi[i]); + goto err_get_fi; + } + + f[i] = usb_get_function(fi[i]); + if (IS_ERR(f[i])) { + ret = PTR_ERR(f[i]); + goto err_get_f; + } + + ret = usb_add_function(c, f[i]); + if (ret) + goto err_add_f; + } + + return 0; + +err_add_f: + usb_put_function(f[i]); +err_get_f: + usb_put_function_instance(fi[i]); +err_get_fi: + i--; + while (i >= 0) { + usb_remove_function(c, f[i]); + usb_put_function(f[i]); + usb_put_function_instance(fi[i]); + i--; + } + + return ret; +} + +#endif -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html