Hi Laurent, Thank you for the patch,
On 01/08/18 01:29, Laurent Pinchart wrote: > The UVC configfs implementation creates all groups as global static > variables. This prevents creationg of multiple UVC function instances, /creationg/creation/ > as they would all require their own configfs group instances. > > Fix this by allocating all groups dynamically. To avoid duplicating code > around, extend the config_item_type structure with group name and > children, and implement helper functions to create children > automatically for most groups. > > Signed-off-by: Laurent Pinchart <laurent.pinch...@ideasonboard.com> I'm struggling to see what paths free all of the dynamically created children in this patch. Is this already supported by the config_group framework? I see a reference to config_group_put(&opts->func_inst.group); in one error path - but that's about it. Am I missing something nice and obvious? (or is it already handled by framework code not in this patch) In fact, I can't see how it could be handled by core - as the children are added to a new structure you have created. I'll let you look into this :) > --- > drivers/usb/gadget/function/f_uvc.c | 8 +- > drivers/usb/gadget/function/uvc_configfs.c | 480 > +++++++++++++++++------------ > 2 files changed, 282 insertions(+), 206 deletions(-) > > diff --git a/drivers/usb/gadget/function/f_uvc.c > b/drivers/usb/gadget/function/f_uvc.c > index d8ce7868fe22..95cb1b5f5ffe 100644 > --- a/drivers/usb/gadget/function/f_uvc.c > +++ b/drivers/usb/gadget/function/f_uvc.c > @@ -792,6 +792,7 @@ static struct usb_function_instance *uvc_alloc_inst(void) > struct uvc_output_terminal_descriptor *od; > struct uvc_color_matching_descriptor *md; > struct uvc_descriptor_header **ctl_cls; > + int ret; > > opts = kzalloc(sizeof(*opts), GFP_KERNEL); > if (!opts) > @@ -868,7 +869,12 @@ static struct usb_function_instance *uvc_alloc_inst(void) > opts->streaming_interval = 1; > opts->streaming_maxpacket = 1024; > > - uvcg_attach_configfs(opts); > + ret = uvcg_attach_configfs(opts); > + if (ret < 0) { > + kfree(opts); > + return ERR_PTR(ret); > + } > + > return &opts->func_inst; > } > > diff --git a/drivers/usb/gadget/function/uvc_configfs.c > b/drivers/usb/gadget/function/uvc_configfs.c > index dbc95c9558de..e019ea317c7a 100644 > --- a/drivers/usb/gadget/function/uvc_configfs.c > +++ b/drivers/usb/gadget/function/uvc_configfs.c > @@ -41,6 +41,49 @@ static inline struct f_uvc_opts *to_f_uvc_opts(struct > config_item *item) > func_inst.group); > } > > +struct uvcg_config_group_type { > + struct config_item_type type; > + const char *name; > + const struct uvcg_config_group_type **children; > + int (*create_children)(struct config_group *group); > +}; > + > +static int uvcg_config_create_group(struct config_group *parent, > + const struct uvcg_config_group_type *type); > + > +static int uvcg_config_create_children(struct config_group *group, > + const struct uvcg_config_group_type *type) > +{ > + const struct uvcg_config_group_type **child; > + int ret; > + > + if (type->create_children) > + return type->create_children(group); > + > + for (child = type->children; child && *child; ++child) { > + ret = uvcg_config_create_group(group, *child); > + if (ret < 0) > + return ret; > + } > + > + return 0; > +} > + > +static int uvcg_config_create_group(struct config_group *parent, > + const struct uvcg_config_group_type *type) > +{ > + struct config_group *group; > + > + group = kzalloc(sizeof(*group), GFP_KERNEL); > + if (!group) > + return -ENOMEM; > + > + config_group_init_type_name(group, type->name, &type->type); > + configfs_add_default_group(group, parent); > + > + return uvcg_config_create_children(group, type); > +} > + > /* > ----------------------------------------------------------------------------- > * control/header/<NAME> > * control/header > @@ -169,24 +212,23 @@ static void uvcg_control_header_drop(struct > config_group *group, > kfree(h); > } > > -static struct config_group uvcg_control_header_grp; > - > static struct configfs_group_operations uvcg_control_header_grp_ops = { > .make_item = uvcg_control_header_make, > .drop_item = uvcg_control_header_drop, > }; > > -static const struct config_item_type uvcg_control_header_grp_type = { > - .ct_group_ops = &uvcg_control_header_grp_ops, > - .ct_owner = THIS_MODULE, > +static const struct uvcg_config_group_type uvcg_control_header_grp_type = { > + .type = { > + .ct_group_ops = &uvcg_control_header_grp_ops, > + .ct_owner = THIS_MODULE, > + }, > + .name = "header", > }; > > /* > ----------------------------------------------------------------------------- > * control/processing/default > */ > > -static struct config_group uvcg_default_processing_grp; > - > #define UVCG_DEFAULT_PROCESSING_ATTR(cname, aname, conv) \ > static ssize_t uvcg_default_processing_##cname##_show( > \ > struct config_item *item, char *page) \ > @@ -265,27 +307,33 @@ static struct configfs_attribute > *uvcg_default_processing_attrs[] = { > NULL, > }; > > -static const struct config_item_type uvcg_default_processing_type = { > - .ct_attrs = uvcg_default_processing_attrs, > - .ct_owner = THIS_MODULE, > +static const struct uvcg_config_group_type uvcg_default_processing_type = { > + .type = { > + .ct_attrs = uvcg_default_processing_attrs, > + .ct_owner = THIS_MODULE, > + }, > + .name = "default", > }; > > /* > ----------------------------------------------------------------------------- > * control/processing > */ > > -static struct config_group uvcg_processing_grp; > - > -static const struct config_item_type uvcg_processing_grp_type = { > - .ct_owner = THIS_MODULE, > +static const struct uvcg_config_group_type uvcg_processing_grp_type = { > + .type = { > + .ct_owner = THIS_MODULE, > + }, > + .name = "processing", > + .children = (const struct uvcg_config_group_type*[]) { > + &uvcg_default_processing_type, > + NULL, > + }, > }; > > /* > ----------------------------------------------------------------------------- > * control/terminal/camera/default > */ > > -static struct config_group uvcg_default_camera_grp; > - > #define UVCG_DEFAULT_CAMERA_ATTR(cname, aname, conv) \ > static ssize_t uvcg_default_camera_##cname##_show( \ > struct config_item *item, char *page) \ > @@ -375,27 +423,33 @@ static struct configfs_attribute > *uvcg_default_camera_attrs[] = { > NULL, > }; > > -static const struct config_item_type uvcg_default_camera_type = { > - .ct_attrs = uvcg_default_camera_attrs, > - .ct_owner = THIS_MODULE, > +static const struct uvcg_config_group_type uvcg_default_camera_type = { > + .type = { > + .ct_attrs = uvcg_default_camera_attrs, > + .ct_owner = THIS_MODULE, > + }, > + .name = "default", > }; > > /* > ----------------------------------------------------------------------------- > * control/terminal/camera > */ > > -static struct config_group uvcg_camera_grp; > - > -static const struct config_item_type uvcg_camera_grp_type = { > - .ct_owner = THIS_MODULE, > +static const struct uvcg_config_group_type uvcg_camera_grp_type = { > + .type = { > + .ct_owner = THIS_MODULE, > + }, > + .name = "camera", > + .children = (const struct uvcg_config_group_type*[]) { > + &uvcg_default_camera_type, > + NULL, > + }, > }; > > /* > ----------------------------------------------------------------------------- > * control/terminal/output/default > */ > > -static struct config_group uvcg_default_output_grp; > - > #define UVCG_DEFAULT_OUTPUT_ATTR(cname, aname, conv) \ > static ssize_t uvcg_default_output_##cname##_show( \ > struct config_item *item, char *page) \ > @@ -446,47 +500,65 @@ static struct configfs_attribute > *uvcg_default_output_attrs[] = { > NULL, > }; > > -static const struct config_item_type uvcg_default_output_type = { > - .ct_attrs = uvcg_default_output_attrs, > - .ct_owner = THIS_MODULE, > +static const struct uvcg_config_group_type uvcg_default_output_type = { > + .type = { > + .ct_attrs = uvcg_default_output_attrs, > + .ct_owner = THIS_MODULE, > + }, > + .name = "default", > }; > > /* > ----------------------------------------------------------------------------- > * control/terminal/output > */ > > -static struct config_group uvcg_output_grp; > - > -static const struct config_item_type uvcg_output_grp_type = { > - .ct_owner = THIS_MODULE, > +static const struct uvcg_config_group_type uvcg_output_grp_type = { > + .type = { > + .ct_owner = THIS_MODULE, > + }, > + .name = "output", > + .children = (const struct uvcg_config_group_type*[]) { > + &uvcg_default_output_type, > + NULL, > + }, > }; > > /* > ----------------------------------------------------------------------------- > * control/terminal > */ > > -static struct config_group uvcg_terminal_grp; > - > -static const struct config_item_type uvcg_terminal_grp_type = { > - .ct_owner = THIS_MODULE, > +static const struct uvcg_config_group_type uvcg_terminal_grp_type = { > + .type = { > + .ct_owner = THIS_MODULE, > + }, > + .name = "terminal", > + .children = (const struct uvcg_config_group_type*[]) { Is this cast really needed? Or is it just to constify the array ? > + &uvcg_camera_grp_type, > + &uvcg_output_grp_type, > + NULL, > + }, > }; > > /* > ----------------------------------------------------------------------------- > * control/class/{fs|ss} > */ > > -static struct config_group uvcg_control_class_fs_grp; > -static struct config_group uvcg_control_class_ss_grp; > +struct uvcg_control_class_group { > + struct config_group group; > + const char *name; > +}; > > static inline struct uvc_descriptor_header > **uvcg_get_ctl_class_arr(struct config_item *i, struct f_uvc_opts *o) > { > - struct config_group *group = to_config_group(i); > + struct uvcg_control_class_group *group = > + container_of(i, struct uvcg_control_class_group, > + group.cg_item); > > - if (group == &uvcg_control_class_fs_grp) > + if (!strcmp(group->name, "fs")) > return o->uvc_fs_control_cls; > > - if (group == &uvcg_control_class_ss_grp) > + if (!strcmp(group->name, "ss")) > return o->uvc_ss_control_cls; > > return NULL; > @@ -581,20 +653,52 @@ static const struct config_item_type > uvcg_control_class_type = { > * control/class > */ > > -static struct config_group uvcg_control_class_grp; > +static int uvcg_control_class_create_children(struct config_group *parent) > +{ > + static const char * const names[] = { "fs", "ss" }; > + unsigned int i; > + > + for (i = 0; i < ARRAY_SIZE(names); ++i) { > + struct uvcg_control_class_group *group; > + > + group = kzalloc(sizeof(*group), GFP_KERNEL); > + if (!group) > + return -ENOMEM; > + > + group->name = names[i]; > > -static const struct config_item_type uvcg_control_class_grp_type = { > - .ct_owner = THIS_MODULE, > + config_group_init_type_name(&group->group, group->name, > + &uvcg_control_class_type); > + configfs_add_default_group(&group->group, parent); > + } > + > + return 0; > +} > + > +static const struct uvcg_config_group_type uvcg_control_class_grp_type = { > + .type = { > + .ct_owner = THIS_MODULE, > + }, > + .name = "class", > + .create_children = uvcg_control_class_create_children, > }; > > /* > ----------------------------------------------------------------------------- > * control > */ > > -static struct config_group uvcg_control_grp; > - > -static const struct config_item_type uvcg_control_grp_type = { > - .ct_owner = THIS_MODULE, > +static const struct uvcg_config_group_type uvcg_control_grp_type = { > + .type = { > + .ct_owner = THIS_MODULE, > + }, > + .name = "control", > + .children = (const struct uvcg_config_group_type*[]) { > + &uvcg_control_header_grp_type, > + &uvcg_processing_grp_type, > + &uvcg_terminal_grp_type, > + &uvcg_control_class_grp_type, > + NULL, > + }, > }; > > /* > ----------------------------------------------------------------------------- > @@ -602,12 +706,9 @@ static const struct config_item_type > uvcg_control_grp_type = { > * streaming/mjpeg > */ > > -static struct config_group uvcg_uncompressed_grp; > -static struct config_group uvcg_mjpeg_grp; > - > -static struct config_item *fmt_parent[] = { > - &uvcg_uncompressed_grp.cg_item, > - &uvcg_mjpeg_grp.cg_item, > +static const char * const uvcg_format_names[] = { > + "uncompressed", > + "mjpeg", > }; > > enum uvcg_format_type { > @@ -733,10 +834,22 @@ static int uvcg_streaming_header_allow_link(struct > config_item *src, > goto out; > } > > - for (i = 0; i < ARRAY_SIZE(fmt_parent); ++i) > - if (target->ci_parent == fmt_parent[i]) > + /* > + * Linking is only allowed to direct children of the format nodes > + * (streaming/uncompressed or streaming/mjpeg nodes). First check that > + * the grand-parent of the target matches the grand-parent of the source > + * (the streaming node), and then verify that the target parent is a > + * format node. > + */ > + if (src->ci_parent->ci_parent != target->ci_parent->ci_parent) > + goto out; > + > + for (i = 0; i < ARRAY_SIZE(uvcg_format_names); ++i) { > + if (!strcmp(target->ci_parent->ci_name, uvcg_format_names[i])) > break; > - if (i == ARRAY_SIZE(fmt_parent)) > + } > + > + if (i == ARRAY_SIZE(uvcg_format_names)) > goto out; > > target_fmt = container_of(to_config_group(target), struct uvcg_format, > @@ -881,16 +994,17 @@ static void uvcg_streaming_header_drop(struct > config_group *group, > kfree(h); > } > > -static struct config_group uvcg_streaming_header_grp; > - > static struct configfs_group_operations uvcg_streaming_header_grp_ops = { > .make_item = uvcg_streaming_header_make, > .drop_item = uvcg_streaming_header_drop, > }; > > -static const struct config_item_type uvcg_streaming_header_grp_type = { > - .ct_group_ops = &uvcg_streaming_header_grp_ops, > - .ct_owner = THIS_MODULE, > +static const struct uvcg_config_group_type uvcg_streaming_header_grp_type = { > + .type = { > + .ct_group_ops = &uvcg_streaming_header_grp_ops, > + .ct_owner = THIS_MODULE, > + }, > + .name = "header", > }; > > /* > ----------------------------------------------------------------------------- > @@ -1462,9 +1576,12 @@ static struct configfs_group_operations > uvcg_uncompressed_grp_ops = { > .drop_item = uvcg_uncompressed_drop, > }; > > -static const struct config_item_type uvcg_uncompressed_grp_type = { > - .ct_group_ops = &uvcg_uncompressed_grp_ops, > - .ct_owner = THIS_MODULE, > +static const struct uvcg_config_group_type uvcg_uncompressed_grp_type = { > + .type = { > + .ct_group_ops = &uvcg_uncompressed_grp_ops, > + .ct_owner = THIS_MODULE, > + }, > + .name = "uncompressed", > }; > > /* > ----------------------------------------------------------------------------- > @@ -1659,17 +1776,18 @@ static struct configfs_group_operations > uvcg_mjpeg_grp_ops = { > .drop_item = uvcg_mjpeg_drop, > }; > > -static const struct config_item_type uvcg_mjpeg_grp_type = { > - .ct_group_ops = &uvcg_mjpeg_grp_ops, > - .ct_owner = THIS_MODULE, > +static const struct uvcg_config_group_type uvcg_mjpeg_grp_type = { > + .type = { > + .ct_group_ops = &uvcg_mjpeg_grp_ops, > + .ct_owner = THIS_MODULE, > + }, > + .name = "mjpeg", > }; > > /* > ----------------------------------------------------------------------------- > * streaming/color_matching/default > */ > > -static struct config_group uvcg_default_color_matching_grp; > - > #define UVCG_DEFAULT_COLOR_MATCHING_ATTR(cname, aname, conv) \ > static ssize_t uvcg_default_color_matching_##cname##_show( \ > struct config_item *item, char *page) \ > @@ -1717,41 +1835,52 @@ static struct configfs_attribute > *uvcg_default_color_matching_attrs[] = { > NULL, > }; > > -static const struct config_item_type uvcg_default_color_matching_type = { > - .ct_attrs = uvcg_default_color_matching_attrs, > - .ct_owner = THIS_MODULE, > +static const struct uvcg_config_group_type uvcg_default_color_matching_type > = { > + .type = { > + .ct_attrs = uvcg_default_color_matching_attrs, > + .ct_owner = THIS_MODULE, > + }, > + .name = "default", > }; > > /* > ----------------------------------------------------------------------------- > * streaming/color_matching > */ > > -static struct config_group uvcg_color_matching_grp; > - > -static const struct config_item_type uvcg_color_matching_grp_type = { > - .ct_owner = THIS_MODULE, > +static const struct uvcg_config_group_type uvcg_color_matching_grp_type = { > + .type = { > + .ct_owner = THIS_MODULE, > + }, > + .name = "color_matching", > + .children = (const struct uvcg_config_group_type*[]) { > + &uvcg_default_color_matching_type, > + NULL, > + }, > }; > > /* > ----------------------------------------------------------------------------- > * streaming/class/{fs|hs|ss} > */ > > -static struct config_group uvcg_streaming_class_fs_grp; > -static struct config_group uvcg_streaming_class_hs_grp; > -static struct config_group uvcg_streaming_class_ss_grp; > +struct uvcg_streaming_class_group { > + struct config_group group; > + const char *name; > +}; > > static inline struct uvc_descriptor_header > ***__uvcg_get_stream_class_arr(struct config_item *i, struct f_uvc_opts *o) > { > - struct config_group *group = to_config_group(i); > + struct uvcg_streaming_class_group *group = > + container_of(i, struct uvcg_streaming_class_group, > + group.cg_item); > > - if (group == &uvcg_streaming_class_fs_grp) > + if (!strcmp(group->name, "fs")) > return &o->uvc_fs_streaming_cls; > > - if (group == &uvcg_streaming_class_hs_grp) > + if (!strcmp(group->name, "hs")) > return &o->uvc_hs_streaming_cls; > > - if (group == &uvcg_streaming_class_ss_grp) > + if (!strcmp(group->name, "ss")) > return &o->uvc_ss_streaming_cls; > > return NULL; > @@ -2083,20 +2212,53 @@ static const struct config_item_type > uvcg_streaming_class_type = { > * streaming/class > */ > > -static struct config_group uvcg_streaming_class_grp; > +static int uvcg_streaming_class_create_children(struct config_group *parent) > +{ > + static const char * const names[] = { "fs", "hs", "ss" }; > + unsigned int i; > + > + for (i = 0; i < ARRAY_SIZE(names); ++i) { > + struct uvcg_streaming_class_group *group; > + > + group = kzalloc(sizeof(*group), GFP_KERNEL); > + if (!group) > + return -ENOMEM; > + > + group->name = names[i]; > + > + config_group_init_type_name(&group->group, group->name, > + &uvcg_streaming_class_type); > + configfs_add_default_group(&group->group, parent); > + } > + > + return 0; > +} > > -static const struct config_item_type uvcg_streaming_class_grp_type = { > - .ct_owner = THIS_MODULE, > +static const struct uvcg_config_group_type uvcg_streaming_class_grp_type = { > + .type = { > + .ct_owner = THIS_MODULE, > + }, > + .name = "class", > + .create_children = uvcg_streaming_class_create_children, > }; > > /* > ----------------------------------------------------------------------------- > * streaming > */ > > -static struct config_group uvcg_streaming_grp; > - > -static const struct config_item_type uvcg_streaming_grp_type = { > - .ct_owner = THIS_MODULE, > +static const struct uvcg_config_group_type uvcg_streaming_grp_type = { > + .type = { > + .ct_owner = THIS_MODULE, > + }, > + .name = "streaming", > + .children = (const struct uvcg_config_group_type*[]) { > + &uvcg_streaming_header_grp_type, > + &uvcg_uncompressed_grp_type, > + &uvcg_mjpeg_grp_type, > + &uvcg_color_matching_grp_type, > + &uvcg_streaming_class_grp_type, > + NULL, > + }, > }; > > /* > ----------------------------------------------------------------------------- > @@ -2179,123 +2341,31 @@ static struct configfs_attribute *uvc_attrs[] = { > NULL, > }; > > -static const struct config_item_type uvc_func_type = { > - .ct_item_ops = &uvc_item_ops, > - .ct_attrs = uvc_attrs, > - .ct_owner = THIS_MODULE, > +static const struct uvcg_config_group_type uvc_func_type = { > + .type = { > + .ct_item_ops = &uvc_item_ops, > + .ct_attrs = uvc_attrs, > + .ct_owner = THIS_MODULE, > + }, > + .name = "", > + .children = (const struct uvcg_config_group_type*[]) { > + &uvcg_control_grp_type, > + &uvcg_streaming_grp_type, > + NULL, > + }, > }; > > int uvcg_attach_configfs(struct f_uvc_opts *opts) > { > - config_group_init_type_name(&uvcg_control_header_grp, > - "header", > - &uvcg_control_header_grp_type); > - > - config_group_init_type_name(&uvcg_default_processing_grp, > - "default", &uvcg_default_processing_type); > - config_group_init_type_name(&uvcg_processing_grp, > - "processing", &uvcg_processing_grp_type); > - configfs_add_default_group(&uvcg_default_processing_grp, > - &uvcg_processing_grp); > - > - config_group_init_type_name(&uvcg_default_camera_grp, > - "default", &uvcg_default_camera_type); > - config_group_init_type_name(&uvcg_camera_grp, > - "camera", &uvcg_camera_grp_type); > - configfs_add_default_group(&uvcg_default_camera_grp, > - &uvcg_camera_grp); > - > - config_group_init_type_name(&uvcg_default_output_grp, > - "default", &uvcg_default_output_type); > - config_group_init_type_name(&uvcg_output_grp, > - "output", &uvcg_output_grp_type); > - configfs_add_default_group(&uvcg_default_output_grp, > - &uvcg_output_grp); > - > - config_group_init_type_name(&uvcg_terminal_grp, > - "terminal", &uvcg_terminal_grp_type); > - configfs_add_default_group(&uvcg_camera_grp, > - &uvcg_terminal_grp); > - configfs_add_default_group(&uvcg_output_grp, > - &uvcg_terminal_grp); > - > - config_group_init_type_name(&uvcg_control_class_fs_grp, > - "fs", &uvcg_control_class_type); > - config_group_init_type_name(&uvcg_control_class_ss_grp, > - "ss", &uvcg_control_class_type); > - config_group_init_type_name(&uvcg_control_class_grp, > - "class", > - &uvcg_control_class_grp_type); > - configfs_add_default_group(&uvcg_control_class_fs_grp, > - &uvcg_control_class_grp); > - configfs_add_default_group(&uvcg_control_class_ss_grp, > - &uvcg_control_class_grp); > - > - config_group_init_type_name(&uvcg_control_grp, > - "control", > - &uvcg_control_grp_type); > - configfs_add_default_group(&uvcg_control_header_grp, > - &uvcg_control_grp); > - configfs_add_default_group(&uvcg_processing_grp, > - &uvcg_control_grp); > - configfs_add_default_group(&uvcg_terminal_grp, > - &uvcg_control_grp); > - configfs_add_default_group(&uvcg_control_class_grp, > - &uvcg_control_grp); > - > - config_group_init_type_name(&uvcg_streaming_header_grp, > - "header", > - &uvcg_streaming_header_grp_type); > - config_group_init_type_name(&uvcg_uncompressed_grp, > - "uncompressed", > - &uvcg_uncompressed_grp_type); > - config_group_init_type_name(&uvcg_mjpeg_grp, > - "mjpeg", > - &uvcg_mjpeg_grp_type); > - config_group_init_type_name(&uvcg_default_color_matching_grp, > - "default", > - &uvcg_default_color_matching_type); > - config_group_init_type_name(&uvcg_color_matching_grp, > - "color_matching", > - &uvcg_color_matching_grp_type); > - configfs_add_default_group(&uvcg_default_color_matching_grp, > - &uvcg_color_matching_grp); > - > - config_group_init_type_name(&uvcg_streaming_class_fs_grp, > - "fs", &uvcg_streaming_class_type); > - config_group_init_type_name(&uvcg_streaming_class_hs_grp, > - "hs", &uvcg_streaming_class_type); > - config_group_init_type_name(&uvcg_streaming_class_ss_grp, > - "ss", &uvcg_streaming_class_type); > - config_group_init_type_name(&uvcg_streaming_class_grp, > - "class", &uvcg_streaming_class_grp_type); > - configfs_add_default_group(&uvcg_streaming_class_fs_grp, > - &uvcg_streaming_class_grp); > - configfs_add_default_group(&uvcg_streaming_class_hs_grp, > - &uvcg_streaming_class_grp); > - configfs_add_default_group(&uvcg_streaming_class_ss_grp, > - &uvcg_streaming_class_grp); > - > - config_group_init_type_name(&uvcg_streaming_grp, > - "streaming", &uvcg_streaming_grp_type); > - configfs_add_default_group(&uvcg_streaming_header_grp, > - &uvcg_streaming_grp); > - configfs_add_default_group(&uvcg_uncompressed_grp, > - &uvcg_streaming_grp); > - configfs_add_default_group(&uvcg_mjpeg_grp, > - &uvcg_streaming_grp); > - configfs_add_default_group(&uvcg_color_matching_grp, > - &uvcg_streaming_grp); > - configfs_add_default_group(&uvcg_streaming_class_grp, > - &uvcg_streaming_grp); > - > - config_group_init_type_name(&opts->func_inst.group, > - "", > - &uvc_func_type); > - configfs_add_default_group(&uvcg_control_grp, > - &opts->func_inst.group); > - configfs_add_default_group(&uvcg_streaming_grp, > - &opts->func_inst.group); > + int ret; > > - return 0; > + config_group_init_type_name(&opts->func_inst.group, uvc_func_type.name, > + &uvc_func_type.type); > + > + ret = uvcg_config_create_children(&opts->func_inst.group, > + &uvc_func_type); > + if (ret < 0) > + config_group_put(&opts->func_inst.group); > + > + return ret; > } > -- Regards -- Kieran -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html