Signed-off-by: Andrzej Pietrasiewicz <[email protected]>
Signed-off-by: Kyungmin Park <[email protected]>
---
drivers/usb/gadget/Kconfig | 1 +
drivers/usb/gadget/g_ffs.c | 142 +++++++++++++++++++++++++++++++++----------
2 files changed, 110 insertions(+), 33 deletions(-)
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 0d3d87a..fa7a9df 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -776,6 +776,7 @@ config USB_FUNCTIONFS
tristate "Function Filesystem"
select USB_LIBCOMPOSITE
select USB_FUNCTIONFS_GENERIC if !(USB_FUNCTIONFS_ETH ||
USB_FUNCTIONFS_RNDIS)
+ select USB_F_FS
help
The Function Filesystem (FunctionFS) lets one create USB
composite functions in user space in the same way GadgetFS
diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c
index 1914c3d..b0b098d 100644
--- a/drivers/usb/gadget/g_ffs.c
+++ b/drivers/usb/gadget/g_ffs.c
@@ -54,8 +54,7 @@ static int rndis_bind_config(struct usb_configuration *c,
struct eth_dev;
#endif
-#define USB_FFS_INCLUDED
-#include "f_fs.c"
+#include "f_fs.h"
#define DRIVER_NAME "g_ffs"
#define DRIVER_DESC "USB Function Filesystem"
@@ -143,12 +142,18 @@ static struct usb_gadget_strings *gfs_dev_strings[] = {
NULL,
};
-static struct gfs_configuration {
- struct usb_configuration c;
+struct gfs_func_pair {
struct usb_function_instance *fi;
struct usb_function *f;
+};
+
+static struct gfs_configuration {
+ struct usb_configuration c;
+ struct usb_function_instance *fi_eth;
+ struct usb_function *f_eth;
int (*eth)(struct usb_configuration *c, u8 *ethaddr,
struct eth_dev *dev);
+ struct gfs_func_pair *ffs_func;
} gfs_configurations[] = {
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
{
@@ -225,6 +230,22 @@ static int __init gfs_init(void)
if (!ffs_tab)
return -ENOMEM;
+ for (i = 0; i < ARRAY_SIZE(gfs_configurations); ++i) {
+ struct gfs_configuration *c = gfs_configurations + i;
+
+ c->ffs_func = kcalloc(func_num, sizeof(struct gfs_func_pair),
+ GFP_KERNEL);
+ if (!c->ffs_func) {
+ while (i--) {
+ struct gfs_configuration *c =
+ gfs_configurations + i;
+ kfree(c->ffs_func);
+ }
+ kfree(ffs_tab);
+ return -ENOMEM;
+ }
+ }
+
if (!gfs_single_func)
for (i = 0; i < func_num; i++)
ffs_tab[i].name = func_names[i];
@@ -237,6 +258,8 @@ module_init(gfs_init);
static void __exit gfs_exit(void)
{
+ int i;
+
ENTER();
mutex_lock(&gfs_lock);
@@ -247,6 +270,10 @@ static void __exit gfs_exit(void)
functionfs_cleanup();
mutex_unlock(&gfs_lock);
+ for (i = 0; i < ARRAY_SIZE(gfs_configurations); ++i) {
+ struct gfs_configuration *c = gfs_configurations + i;
+ kfree(c->ffs_func);
+ }
kfree(ffs_tab);
}
module_exit(gfs_exit);
@@ -373,7 +400,7 @@ static void functionfs_release_dev_callback(struct ffs_data
*ffs_data)
*/
static int gfs_bind(struct usb_composite_dev *cdev)
{
- int ret, i;
+ int ret, i, j;
ENTER();
@@ -411,17 +438,20 @@ static int gfs_bind(struct usb_composite_dev *cdev)
c->c.bConfigurationValue = 1 + i;
c->c.bmAttributes = USB_CONFIG_ATT_SELFPOWER;
+ for (j = func_num; --j >= 0; ) {
+ struct usb_function_instance *fi;
+ fi = c->ffs_func[j].fi =
+ usb_get_function_instance("functionfs");
+ if (IS_ERR(fi)) {
+ while (++j < func_num)
+ usb_put_function_instance(fi);
+ goto error_unbind;
+ }
+ }
ret = usb_add_config(cdev, &c->c, gfs_do_config);
if (unlikely(ret < 0)) {
- while (--i >= 0) {
- struct gfs_configuration *c =
- gfs_configurations + i;
-
- if (!IS_ERR_OR_NULL(c->f))
- usb_put_function(c->f);
- if (!IS_ERR_OR_NULL(c->fi))
- usb_put_function_instance(c->fi);
- }
+ for (j = 0; j < func_num; j++)
+ usb_put_function_instance(c->ffs_func[j].fi);
goto error_unbind;
}
}
@@ -429,8 +459,23 @@ static int gfs_bind(struct usb_composite_dev *cdev)
return 0;
error_unbind:
- for (i = 0; i < func_num; i++)
- functionfs_unbind(ffs_tab[i].ffs_data);
+ for (j = 0; j < func_num; j++)
+ functionfs_unbind(ffs_tab[j].ffs_data);
+ while (--i >= 0) {
+ struct gfs_configuration *c =
+ gfs_configurations + i;
+
+ if (!IS_ERR_OR_NULL(c->f_eth))
+ usb_put_function(c->f_eth);
+ if (!IS_ERR_OR_NULL(c->fi_eth))
+ usb_put_function_instance(c->fi_eth);
+ for (j = 0; j < func_num; j++) {
+ if (!IS_ERR_OR_NULL(c->ffs_func[j].f))
+ usb_put_function(c->ffs_func[j].f);
+ if (!IS_ERR_OR_NULL(c->ffs_func[j].fi))
+ usb_put_function_instance(c->ffs_func[j].fi);
+ }
+ }
error:
gether_cleanup(the_dev);
error_quick:
@@ -459,19 +504,26 @@ static int gfs_unbind(struct usb_composite_dev *cdev)
gether_cleanup(the_dev);
gfs_ether_setup = false;
- for (i = 0; i < ARRAY_SIZE(gfs_configurations); i++) {
- struct gfs_configuration *c = gfs_configurations + i;
-
- if (!IS_ERR_OR_NULL(c->f))
- usb_put_function(c->f);
- if (!IS_ERR_OR_NULL(c->fi))
- usb_put_function_instance(c->fi);
- }
-
for (i = func_num; i--; )
if (ffs_tab[i].ffs_data)
functionfs_unbind(ffs_tab[i].ffs_data);
+ for (i = 0; i < ARRAY_SIZE(gfs_configurations); i++) {
+ struct gfs_configuration *c = gfs_configurations + i;
+ int j;
+
+ if (!IS_ERR_OR_NULL(c->f_eth))
+ usb_put_function(c->f_eth);
+ if (!IS_ERR_OR_NULL(c->fi_eth))
+ usb_put_function_instance(c->fi_eth);
+ for (j = 0; j < func_num; j++) {
+ if (!IS_ERR_OR_NULL(c->ffs_func[j].f))
+ usb_put_function(c->ffs_func[j].f);
+ if (!IS_ERR_OR_NULL(c->ffs_func[j].fi))
+ usb_put_function_instance(c->ffs_func[j].fi);
+ }
+ }
+
return 0;
}
@@ -500,9 +552,32 @@ static int gfs_do_config(struct usb_configuration *c)
}
for (i = 0; i < func_num; i++) {
- ret = functionfs_bind_config(c->cdev, c, ffs_tab[i].ffs_data);
- if (unlikely(ret < 0))
+ struct ffs_opts *opts;
+
+ opts = container_of(gc->ffs_func[i].fi, struct ffs_opts,
+ func_inst);
+ opts->conf = c;
+ opts->ffs = ffs_tab[i].ffs_data;
+
+ gc->ffs_func[i].f = usb_get_function(gc->ffs_func[i].fi);
+ if (IS_ERR(gc->ffs_func[i].f)) {
+ ret = PTR_ERR(gc->ffs_func[i].f);
+ while (i--) {
+ usb_remove_function(c, gc->ffs_func[i].f);
+ usb_put_function(gc->ffs_func[i].f);
+ }
return ret;
+ }
+
+ ret = usb_add_function(c, gc->ffs_func[i].f);
+ if (unlikely(ret < 0)) {
+ usb_put_function(gc->ffs_func[i].f);
+ while (i--) {
+ usb_remove_function(c, gc->ffs_func[i].f);
+ usb_put_function(gc->ffs_func[i].f);
+ }
+ return ret;
+ }
}
/*
@@ -534,7 +609,8 @@ static int eth_bind_config(struct usb_configuration *c, u8
ethaddr[ETH_ALEN],
int status;
gfs_conf = to_gfs_configuration(c);
- gfs_conf->fi = f_ecm_inst = usb_get_function_instance("ecm");
+ gfs_conf->fi_eth = f_ecm_inst =
+ usb_get_function_instance("ecm");
if (IS_ERR(f_ecm_inst))
return PTR_ERR(f_ecm_inst);
@@ -543,7 +619,7 @@ static int eth_bind_config(struct usb_configuration *c, u8
ethaddr[ETH_ALEN],
ecm_opts->ethaddr = ethaddr;
ecm_opts->dev = dev;
- gfs_conf->f = f_ecm = usb_get_function(f_ecm_inst);
+ gfs_conf->f_eth = f_ecm = usb_get_function(f_ecm_inst);
if (IS_ERR(f_ecm)) {
status = PTR_ERR(f_ecm);
usb_put_function_instance(f_ecm_inst);
@@ -566,7 +642,7 @@ static int eth_bind_config(struct usb_configuration *c, u8
ethaddr[ETH_ALEN],
int status;
gfs_conf = to_gfs_configuration(c);
- gfs_conf->fi = f_gether_inst =
+ gfs_conf->fi_eth = f_gether_inst =
usb_get_function_instance("geth");
if (IS_ERR(f_gether_inst))
return PTR_ERR(f_gether_inst);
@@ -576,7 +652,7 @@ static int eth_bind_config(struct usb_configuration *c, u8
ethaddr[ETH_ALEN],
gether_opts->ethaddr = ethaddr;
gether_opts->dev = dev;
- gfs_conf->f = f_gether = usb_get_function(f_gether_inst);
+ gfs_conf->f_eth = f_gether = usb_get_function(f_gether_inst);
if (IS_ERR(f_gether)) {
status = PTR_ERR(f_gether);
usb_put_function_instance(f_gether_inst);
@@ -608,7 +684,7 @@ static int rndis_bind_config(struct usb_configuration *c,
int status;
gfs_conf = to_gfs_configuration(c);
- gfs_conf->fi = f_rndis_inst = usb_get_function_instance("rndis");
+ gfs_conf->fi_eth = f_rndis_inst = usb_get_function_instance("rndis");
if (IS_ERR(f_rndis_inst))
return PTR_ERR(f_rndis_inst);
@@ -618,7 +694,7 @@ static int rndis_bind_config(struct usb_configuration *c,
rndis_opts->manufacturer = NULL;
rndis_opts->dev = dev;
- gfs_conf->f = f_rndis = usb_get_function(f_rndis_inst);
+ gfs_conf->f_eth = f_rndis = usb_get_function(f_rndis_inst);
if (IS_ERR(f_rndis)) {
status = PTR_ERR(f_rndis);
usb_put_function_instance(f_rndis_inst);
--
1.7.0.4
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html