@@ -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
@@ -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
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@linaro.org> --- drivers/usb/gadget/Kconfig | 9 ++++++ drivers/usb/gadget/g_ffs.c | 71 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+)