diff options
Diffstat (limited to 'drivers/usb/gadget/function/uvc_configfs.c')
-rw-r--r-- | drivers/usb/gadget/function/uvc_configfs.c | 1168 |
1 files changed, 668 insertions, 500 deletions
diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index b51f0d278826..bc1e2af566c3 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -9,9 +9,16 @@ * * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> */ + +#include <linux/sort.h> + #include "u_uvc.h" #include "uvc_configfs.h" +/* ----------------------------------------------------------------------------- + * Global Utility Structures and Macros + */ + #define UVCG_STREAMING_CONTROL_SIZE 1 #define UVC_ATTR(prefix, cname, aname) \ @@ -31,13 +38,93 @@ static struct configfs_attribute prefix##attr_##cname = { \ .show = prefix##cname##_show, \ } +#define le8_to_cpu(x) (x) +#define cpu_to_le8(x) (x) + +static int uvcg_config_compare_u32(const void *l, const void *r) +{ + u32 li = *(const u32 *)l; + u32 ri = *(const u32 *)r; + + return li < ri ? -1 : li == ri ? 0 : 1; +} + static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item) { return container_of(to_config_group(item), struct f_uvc_opts, func_inst.group); } -/* control/header/<NAME> */ +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 void uvcg_config_item_release(struct config_item *item) +{ + struct config_group *group = to_config_group(item); + + kfree(group); +} + +static struct configfs_item_operations uvcg_config_item_ops = { + .release = uvcg_config_item_release, +}; + +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); +} + +static void uvcg_config_remove_children(struct config_group *group) +{ + struct config_group *child, *n; + + list_for_each_entry_safe(child, n, &group->default_groups, group_entry) { + list_del(&child->group_entry); + uvcg_config_remove_children(child); + config_item_put(&child->cg_item); + } +} + +/* ----------------------------------------------------------------------------- + * control/header/<NAME> + * control/header + */ + DECLARE_UVC_HEADER_DESCRIPTOR(1); struct uvcg_control_header { @@ -51,9 +138,9 @@ static struct uvcg_control_header *to_uvcg_control_header(struct config_item *it return container_of(item, struct uvcg_control_header, item); } -#define UVCG_CTRL_HDR_ATTR(cname, aname, conv, str2u, uxx, vnoc, limit) \ +#define UVCG_CTRL_HDR_ATTR(cname, aname, bits, limit) \ static ssize_t uvcg_control_header_##cname##_show( \ - struct config_item *item, char *page) \ + struct config_item *item, char *page) \ { \ struct uvcg_control_header *ch = to_uvcg_control_header(item); \ struct f_uvc_opts *opts; \ @@ -67,7 +154,7 @@ static ssize_t uvcg_control_header_##cname##_show( \ opts = to_f_uvc_opts(opts_item); \ \ mutex_lock(&opts->lock); \ - result = sprintf(page, "%d\n", conv(ch->desc.aname)); \ + result = sprintf(page, "%u\n", le##bits##_to_cpu(ch->desc.aname));\ mutex_unlock(&opts->lock); \ \ mutex_unlock(su_mutex); \ @@ -83,7 +170,7 @@ uvcg_control_header_##cname##_store(struct config_item *item, \ struct config_item *opts_item; \ struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex;\ int ret; \ - uxx num; \ + u##bits num; \ \ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ \ @@ -96,7 +183,7 @@ uvcg_control_header_##cname##_store(struct config_item *item, \ goto end; \ } \ \ - ret = str2u(page, 0, &num); \ + ret = kstrtou##bits(page, 0, &num); \ if (ret) \ goto end; \ \ @@ -104,7 +191,7 @@ uvcg_control_header_##cname##_store(struct config_item *item, \ ret = -EINVAL; \ goto end; \ } \ - ch->desc.aname = vnoc(num); \ + ch->desc.aname = cpu_to_le##bits(num); \ ret = len; \ end: \ mutex_unlock(&opts->lock); \ @@ -114,11 +201,9 @@ end: \ \ UVC_ATTR(uvcg_control_header_, cname, aname) -UVCG_CTRL_HDR_ATTR(bcd_uvc, bcdUVC, le16_to_cpu, kstrtou16, u16, cpu_to_le16, - 0xffff); +UVCG_CTRL_HDR_ATTR(bcd_uvc, bcdUVC, 16, 0xffff); -UVCG_CTRL_HDR_ATTR(dw_clock_frequency, dwClockFrequency, le32_to_cpu, kstrtou32, - u32, cpu_to_le32, 0x7fffffff); +UVCG_CTRL_HDR_ATTR(dw_clock_frequency, dwClockFrequency, 32, 0x7fffffff); #undef UVCG_CTRL_HDR_ATTR @@ -129,6 +214,7 @@ static struct configfs_attribute *uvcg_control_header_attrs[] = { }; static const struct config_item_type uvcg_control_header_type = { + .ct_item_ops = &uvcg_config_item_ops, .ct_attrs = uvcg_control_header_attrs, .ct_owner = THIS_MODULE, }; @@ -153,60 +239,42 @@ static struct config_item *uvcg_control_header_make(struct config_group *group, return &h->item; } -static void uvcg_control_header_drop(struct config_group *group, - struct config_item *item) -{ - struct uvcg_control_header *h = to_uvcg_control_header(item); - - kfree(h); -} - -/* control/header */ -static struct uvcg_control_header_grp { - struct config_group 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_item_ops = &uvcg_config_item_ops, + .ct_group_ops = &uvcg_control_header_grp_ops, + .ct_owner = THIS_MODULE, + }, + .name = "header", }; -/* control/processing/default */ -static struct uvcg_default_processing { - struct config_group group; -} uvcg_default_processing; - -static inline struct uvcg_default_processing -*to_uvcg_default_processing(struct config_item *item) -{ - return container_of(to_config_group(item), - struct uvcg_default_processing, group); -} +/* ----------------------------------------------------------------------------- + * control/processing/default + */ -#define UVCG_DEFAULT_PROCESSING_ATTR(cname, aname, conv) \ +#define UVCG_DEFAULT_PROCESSING_ATTR(cname, aname, bits) \ static ssize_t uvcg_default_processing_##cname##_show( \ struct config_item *item, char *page) \ { \ - struct uvcg_default_processing *dp = to_uvcg_default_processing(item); \ + struct config_group *group = to_config_group(item); \ struct f_uvc_opts *opts; \ struct config_item *opts_item; \ - struct mutex *su_mutex = &dp->group.cg_subsys->su_mutex; \ + struct mutex *su_mutex = &group->cg_subsys->su_mutex; \ struct uvc_processing_unit_descriptor *pd; \ int result; \ \ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ \ - opts_item = dp->group.cg_item.ci_parent->ci_parent->ci_parent; \ + opts_item = group->cg_item.ci_parent->ci_parent->ci_parent; \ opts = to_f_uvc_opts(opts_item); \ pd = &opts->uvc_processing; \ \ mutex_lock(&opts->lock); \ - result = sprintf(page, "%d\n", conv(pd->aname)); \ + result = sprintf(page, "%u\n", le##bits##_to_cpu(pd->aname)); \ mutex_unlock(&opts->lock); \ \ mutex_unlock(su_mutex); \ @@ -215,37 +283,33 @@ static ssize_t uvcg_default_processing_##cname##_show( \ \ UVC_ATTR_RO(uvcg_default_processing_, cname, aname) -#define identity_conv(x) (x) - -UVCG_DEFAULT_PROCESSING_ATTR(b_unit_id, bUnitID, identity_conv); -UVCG_DEFAULT_PROCESSING_ATTR(b_source_id, bSourceID, identity_conv); -UVCG_DEFAULT_PROCESSING_ATTR(w_max_multiplier, wMaxMultiplier, le16_to_cpu); -UVCG_DEFAULT_PROCESSING_ATTR(i_processing, iProcessing, identity_conv); - -#undef identity_conv +UVCG_DEFAULT_PROCESSING_ATTR(b_unit_id, bUnitID, 8); +UVCG_DEFAULT_PROCESSING_ATTR(b_source_id, bSourceID, 8); +UVCG_DEFAULT_PROCESSING_ATTR(w_max_multiplier, wMaxMultiplier, 16); +UVCG_DEFAULT_PROCESSING_ATTR(i_processing, iProcessing, 8); #undef UVCG_DEFAULT_PROCESSING_ATTR static ssize_t uvcg_default_processing_bm_controls_show( struct config_item *item, char *page) { - struct uvcg_default_processing *dp = to_uvcg_default_processing(item); + struct config_group *group = to_config_group(item); struct f_uvc_opts *opts; struct config_item *opts_item; - struct mutex *su_mutex = &dp->group.cg_subsys->su_mutex; + struct mutex *su_mutex = &group->cg_subsys->su_mutex; struct uvc_processing_unit_descriptor *pd; int result, i; char *pg = page; mutex_lock(su_mutex); /* for navigating configfs hierarchy */ - opts_item = dp->group.cg_item.ci_parent->ci_parent->ci_parent; + opts_item = group->cg_item.ci_parent->ci_parent->ci_parent; opts = to_f_uvc_opts(opts_item); pd = &opts->uvc_processing; mutex_lock(&opts->lock); for (result = 0, i = 0; i < pd->bControlSize; ++i) { - result += sprintf(pg, "%d\n", pd->bmControls[i]); + result += sprintf(pg, "%u\n", pd->bmControls[i]); pg = page + result; } mutex_unlock(&opts->lock); @@ -266,54 +330,55 @@ 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_item_ops = &uvcg_config_item_ops, + .ct_attrs = uvcg_default_processing_attrs, + .ct_owner = THIS_MODULE, + }, + .name = "default", }; -/* struct uvcg_processing {}; */ - -/* control/processing */ -static struct uvcg_processing_grp { - struct config_group group; -} uvcg_processing_grp; +/* ----------------------------------------------------------------------------- + * control/processing + */ -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_item_ops = &uvcg_config_item_ops, + .ct_owner = THIS_MODULE, + }, + .name = "processing", + .children = (const struct uvcg_config_group_type*[]) { + &uvcg_default_processing_type, + NULL, + }, }; -/* control/terminal/camera/default */ -static struct uvcg_default_camera { - struct config_group group; -} uvcg_default_camera; - -static inline struct uvcg_default_camera -*to_uvcg_default_camera(struct config_item *item) -{ - return container_of(to_config_group(item), - struct uvcg_default_camera, group); -} +/* ----------------------------------------------------------------------------- + * control/terminal/camera/default + */ -#define UVCG_DEFAULT_CAMERA_ATTR(cname, aname, conv) \ +#define UVCG_DEFAULT_CAMERA_ATTR(cname, aname, bits) \ static ssize_t uvcg_default_camera_##cname##_show( \ struct config_item *item, char *page) \ { \ - struct uvcg_default_camera *dc = to_uvcg_default_camera(item); \ + struct config_group *group = to_config_group(item); \ struct f_uvc_opts *opts; \ struct config_item *opts_item; \ - struct mutex *su_mutex = &dc->group.cg_subsys->su_mutex; \ + struct mutex *su_mutex = &group->cg_subsys->su_mutex; \ struct uvc_camera_terminal_descriptor *cd; \ int result; \ \ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ \ - opts_item = dc->group.cg_item.ci_parent->ci_parent->ci_parent-> \ + opts_item = group->cg_item.ci_parent->ci_parent->ci_parent-> \ ci_parent; \ opts = to_f_uvc_opts(opts_item); \ cd = &opts->uvc_camera_terminal; \ \ mutex_lock(&opts->lock); \ - result = sprintf(page, "%d\n", conv(cd->aname)); \ + result = sprintf(page, "%u\n", le##bits##_to_cpu(cd->aname)); \ mutex_unlock(&opts->lock); \ \ mutex_unlock(su_mutex); \ @@ -323,44 +388,40 @@ static ssize_t uvcg_default_camera_##cname##_show( \ \ UVC_ATTR_RO(uvcg_default_camera_, cname, aname) -#define identity_conv(x) (x) - -UVCG_DEFAULT_CAMERA_ATTR(b_terminal_id, bTerminalID, identity_conv); -UVCG_DEFAULT_CAMERA_ATTR(w_terminal_type, wTerminalType, le16_to_cpu); -UVCG_DEFAULT_CAMERA_ATTR(b_assoc_terminal, bAssocTerminal, identity_conv); -UVCG_DEFAULT_CAMERA_ATTR(i_terminal, iTerminal, identity_conv); +UVCG_DEFAULT_CAMERA_ATTR(b_terminal_id, bTerminalID, 8); +UVCG_DEFAULT_CAMERA_ATTR(w_terminal_type, wTerminalType, 16); +UVCG_DEFAULT_CAMERA_ATTR(b_assoc_terminal, bAssocTerminal, 8); +UVCG_DEFAULT_CAMERA_ATTR(i_terminal, iTerminal, 8); UVCG_DEFAULT_CAMERA_ATTR(w_objective_focal_length_min, wObjectiveFocalLengthMin, - le16_to_cpu); + 16); UVCG_DEFAULT_CAMERA_ATTR(w_objective_focal_length_max, wObjectiveFocalLengthMax, - le16_to_cpu); + 16); UVCG_DEFAULT_CAMERA_ATTR(w_ocular_focal_length, wOcularFocalLength, - le16_to_cpu); - -#undef identity_conv + 16); #undef UVCG_DEFAULT_CAMERA_ATTR static ssize_t uvcg_default_camera_bm_controls_show( struct config_item *item, char *page) { - struct uvcg_default_camera *dc = to_uvcg_default_camera(item); + struct config_group *group = to_config_group(item); struct f_uvc_opts *opts; struct config_item *opts_item; - struct mutex *su_mutex = &dc->group.cg_subsys->su_mutex; + struct mutex *su_mutex = &group->cg_subsys->su_mutex; struct uvc_camera_terminal_descriptor *cd; int result, i; char *pg = page; mutex_lock(su_mutex); /* for navigating configfs hierarchy */ - opts_item = dc->group.cg_item.ci_parent->ci_parent->ci_parent-> + opts_item = group->cg_item.ci_parent->ci_parent->ci_parent-> ci_parent; opts = to_f_uvc_opts(opts_item); cd = &opts->uvc_camera_terminal; mutex_lock(&opts->lock); for (result = 0, i = 0; i < cd->bControlSize; ++i) { - result += sprintf(pg, "%d\n", cd->bmControls[i]); + result += sprintf(pg, "%u\n", cd->bmControls[i]); pg = page + result; } mutex_unlock(&opts->lock); @@ -383,54 +444,55 @@ 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_item_ops = &uvcg_config_item_ops, + .ct_attrs = uvcg_default_camera_attrs, + .ct_owner = THIS_MODULE, + }, + .name = "default", }; -/* struct uvcg_camera {}; */ - -/* control/terminal/camera */ -static struct uvcg_camera_grp { - struct config_group group; -} uvcg_camera_grp; +/* ----------------------------------------------------------------------------- + * control/terminal/camera + */ -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_item_ops = &uvcg_config_item_ops, + .ct_owner = THIS_MODULE, + }, + .name = "camera", + .children = (const struct uvcg_config_group_type*[]) { + &uvcg_default_camera_type, + NULL, + }, }; -/* control/terminal/output/default */ -static struct uvcg_default_output { - struct config_group group; -} uvcg_default_output; - -static inline struct uvcg_default_output -*to_uvcg_default_output(struct config_item *item) -{ - return container_of(to_config_group(item), - struct uvcg_default_output, group); -} +/* ----------------------------------------------------------------------------- + * control/terminal/output/default + */ -#define UVCG_DEFAULT_OUTPUT_ATTR(cname, aname, conv) \ +#define UVCG_DEFAULT_OUTPUT_ATTR(cname, aname, bits) \ static ssize_t uvcg_default_output_##cname##_show( \ - struct config_item *item, char *page) \ + struct config_item *item, char *page) \ { \ - struct uvcg_default_output *dout = to_uvcg_default_output(item); \ + struct config_group *group = to_config_group(item); \ struct f_uvc_opts *opts; \ struct config_item *opts_item; \ - struct mutex *su_mutex = &dout->group.cg_subsys->su_mutex; \ + struct mutex *su_mutex = &group->cg_subsys->su_mutex; \ struct uvc_output_terminal_descriptor *cd; \ int result; \ \ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ \ - opts_item = dout->group.cg_item.ci_parent->ci_parent-> \ + opts_item = group->cg_item.ci_parent->ci_parent-> \ ci_parent->ci_parent; \ opts = to_f_uvc_opts(opts_item); \ cd = &opts->uvc_output_terminal; \ \ mutex_lock(&opts->lock); \ - result = sprintf(page, "%d\n", conv(cd->aname)); \ + result = sprintf(page, "%u\n", le##bits##_to_cpu(cd->aname)); \ mutex_unlock(&opts->lock); \ \ mutex_unlock(su_mutex); \ @@ -440,15 +502,11 @@ static ssize_t uvcg_default_output_##cname##_show( \ \ UVC_ATTR_RO(uvcg_default_output_, cname, aname) -#define identity_conv(x) (x) - -UVCG_DEFAULT_OUTPUT_ATTR(b_terminal_id, bTerminalID, identity_conv); -UVCG_DEFAULT_OUTPUT_ATTR(w_terminal_type, wTerminalType, le16_to_cpu); -UVCG_DEFAULT_OUTPUT_ATTR(b_assoc_terminal, bAssocTerminal, identity_conv); -UVCG_DEFAULT_OUTPUT_ATTR(b_source_id, bSourceID, identity_conv); -UVCG_DEFAULT_OUTPUT_ATTR(i_terminal, iTerminal, identity_conv); - -#undef identity_conv +UVCG_DEFAULT_OUTPUT_ATTR(b_terminal_id, bTerminalID, 8); +UVCG_DEFAULT_OUTPUT_ATTR(w_terminal_type, wTerminalType, 16); +UVCG_DEFAULT_OUTPUT_ATTR(b_assoc_terminal, bAssocTerminal, 8); +UVCG_DEFAULT_OUTPUT_ATTR(b_source_id, bSourceID, 8); +UVCG_DEFAULT_OUTPUT_ATTR(i_terminal, iTerminal, 8); #undef UVCG_DEFAULT_OUTPUT_ATTR @@ -461,47 +519,68 @@ 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_item_ops = &uvcg_config_item_ops, + .ct_attrs = uvcg_default_output_attrs, + .ct_owner = THIS_MODULE, + }, + .name = "default", }; -/* struct uvcg_output {}; */ - -/* control/terminal/output */ -static struct uvcg_output_grp { - struct config_group group; -} uvcg_output_grp; +/* ----------------------------------------------------------------------------- + * control/terminal/output + */ -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_item_ops = &uvcg_config_item_ops, + .ct_owner = THIS_MODULE, + }, + .name = "output", + .children = (const struct uvcg_config_group_type*[]) { + &uvcg_default_output_type, + NULL, + }, }; -/* control/terminal */ -static struct uvcg_terminal_grp { - struct config_group group; -} uvcg_terminal_grp; +/* ----------------------------------------------------------------------------- + * control/terminal + */ -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_item_ops = &uvcg_config_item_ops, + .ct_owner = THIS_MODULE, + }, + .name = "terminal", + .children = (const struct uvcg_config_group_type*[]) { + &uvcg_camera_grp_type, + &uvcg_output_grp_type, + NULL, + }, }; -/* control/class/{fs} */ -static struct uvcg_control_class { - struct config_group group; -} uvcg_control_class_fs, uvcg_control_class_ss; +/* ----------------------------------------------------------------------------- + * control/class/{fs|ss} + */ +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 uvcg_control_class *cl = container_of(to_config_group(i), - struct uvcg_control_class, group); + struct uvcg_control_class_group *group = + container_of(i, struct uvcg_control_class_group, + group.cg_item); - if (cl == &uvcg_control_class_fs) + if (!strcmp(group->name, "fs")) return o->uvc_fs_control_cls; - if (cl == &uvcg_control_class_ss) + if (!strcmp(group->name, "ss")) return o->uvc_ss_control_cls; return NULL; @@ -544,6 +623,7 @@ static int uvcg_control_class_allow_link(struct config_item *src, unlock: mutex_unlock(&opts->lock); out: + config_item_put(header); mutex_unlock(su_mutex); return ret; } @@ -579,10 +659,12 @@ static void uvcg_control_class_drop_link(struct config_item *src, unlock: mutex_unlock(&opts->lock); out: + config_item_put(header); mutex_unlock(su_mutex); } static struct configfs_item_operations uvcg_control_class_item_ops = { + .release = uvcg_config_item_release, .allow_link = uvcg_control_class_allow_link, .drop_link = uvcg_control_class_drop_link, }; @@ -592,37 +674,99 @@ static const struct config_item_type uvcg_control_class_type = { .ct_owner = THIS_MODULE, }; -/* control/class */ -static struct uvcg_control_class_grp { - struct config_group group; -} uvcg_control_class_grp; +/* ----------------------------------------------------------------------------- + * control/class + */ + +static int uvcg_control_class_create_children(struct config_group *parent) +{ + static const char * const names[] = { "fs", "ss" }; + unsigned int i; -static const struct config_item_type uvcg_control_class_grp_type = { - .ct_owner = THIS_MODULE, + 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]; + + 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_item_ops = &uvcg_config_item_ops, + .ct_owner = THIS_MODULE, + }, + .name = "class", + .create_children = uvcg_control_class_create_children, }; -/* control */ -static struct uvcg_control_grp { - struct config_group group; -} uvcg_control_grp; +/* ----------------------------------------------------------------------------- + * control + */ + +static ssize_t uvcg_default_control_b_interface_number_show( + struct config_item *item, char *page) +{ + struct config_group *group = to_config_group(item); + struct mutex *su_mutex = &group->cg_subsys->su_mutex; + struct config_item *opts_item; + struct f_uvc_opts *opts; + int result = 0; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = item->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + result += sprintf(page, "%u\n", opts->control_interface); + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + + return result; +} + +UVC_ATTR_RO(uvcg_default_control_, b_interface_number, bInterfaceNumber); -static const struct config_item_type uvcg_control_grp_type = { - .ct_owner = THIS_MODULE, +static struct configfs_attribute *uvcg_default_control_attrs[] = { + &uvcg_default_control_attr_b_interface_number, + NULL, }; -/* streaming/uncompressed */ -static struct uvcg_uncompressed_grp { - struct config_group group; -} uvcg_uncompressed_grp; +static const struct uvcg_config_group_type uvcg_control_grp_type = { + .type = { + .ct_item_ops = &uvcg_config_item_ops, + .ct_attrs = uvcg_default_control_attrs, + .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, + }, +}; -/* streaming/mjpeg */ -static struct uvcg_mjpeg_grp { - struct config_group group; -} uvcg_mjpeg_grp; +/* ----------------------------------------------------------------------------- + * streaming/uncompressed + * streaming/mjpeg + */ -static struct config_item *fmt_parent[] = { - &uvcg_uncompressed_grp.group.cg_item, - &uvcg_mjpeg_grp.group.cg_item, +static const char * const uvcg_format_names[] = { + "uncompressed", + "mjpeg", }; enum uvcg_format_type { @@ -706,7 +850,11 @@ struct uvcg_format_ptr { struct list_head entry; }; -/* streaming/header/<NAME> */ +/* ----------------------------------------------------------------------------- + * streaming/header/<NAME> + * streaming/header + */ + struct uvcg_streaming_header { struct config_item item; struct uvc_input_header_descriptor desc; @@ -720,6 +868,8 @@ static struct uvcg_streaming_header *to_uvcg_streaming_header(struct config_item return container_of(item, struct uvcg_streaming_header, item); } +static void uvcg_format_set_indices(struct config_group *fmt); + static int uvcg_streaming_header_allow_link(struct config_item *src, struct config_item *target) { @@ -744,10 +894,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, @@ -755,6 +917,8 @@ static int uvcg_streaming_header_allow_link(struct config_item *src, if (!target_fmt) goto out; + uvcg_format_set_indices(to_config_group(target)); + format_ptr = kzalloc(sizeof(*format_ptr), GFP_KERNEL); if (!format_ptr) { ret = -ENOMEM; @@ -764,6 +928,7 @@ static int uvcg_streaming_header_allow_link(struct config_item *src, format_ptr->fmt = target_fmt; list_add_tail(&format_ptr->entry, &src_hdr->formats); ++src_hdr->num_fmt; + ++target_fmt->linked; out: mutex_unlock(&opts->lock); @@ -801,19 +966,22 @@ static void uvcg_streaming_header_drop_link(struct config_item *src, break; } + --target_fmt->linked; + out: mutex_unlock(&opts->lock); mutex_unlock(su_mutex); } static struct configfs_item_operations uvcg_streaming_header_item_ops = { - .allow_link = uvcg_streaming_header_allow_link, - .drop_link = uvcg_streaming_header_drop_link, + .release = uvcg_config_item_release, + .allow_link = uvcg_streaming_header_allow_link, + .drop_link = uvcg_streaming_header_drop_link, }; -#define UVCG_STREAMING_HEADER_ATTR(cname, aname, conv) \ +#define UVCG_STREAMING_HEADER_ATTR(cname, aname, bits) \ static ssize_t uvcg_streaming_header_##cname##_show( \ - struct config_item *item, char *page) \ + struct config_item *item, char *page) \ { \ struct uvcg_streaming_header *sh = to_uvcg_streaming_header(item); \ struct f_uvc_opts *opts; \ @@ -827,7 +995,7 @@ static ssize_t uvcg_streaming_header_##cname##_show( \ opts = to_f_uvc_opts(opts_item); \ \ mutex_lock(&opts->lock); \ - result = sprintf(page, "%d\n", conv(sh->desc.aname)); \ + result = sprintf(page, "%u\n", le##bits##_to_cpu(sh->desc.aname));\ mutex_unlock(&opts->lock); \ \ mutex_unlock(su_mutex); \ @@ -836,16 +1004,11 @@ static ssize_t uvcg_streaming_header_##cname##_show( \ \ UVC_ATTR_RO(uvcg_streaming_header_, cname, aname) -#define identity_conv(x) (x) - -UVCG_STREAMING_HEADER_ATTR(bm_info, bmInfo, identity_conv); -UVCG_STREAMING_HEADER_ATTR(b_terminal_link, bTerminalLink, identity_conv); -UVCG_STREAMING_HEADER_ATTR(b_still_capture_method, bStillCaptureMethod, - identity_conv); -UVCG_STREAMING_HEADER_ATTR(b_trigger_support, bTriggerSupport, identity_conv); -UVCG_STREAMING_HEADER_ATTR(b_trigger_usage, bTriggerUsage, identity_conv); - -#undef identity_conv +UVCG_STREAMING_HEADER_ATTR(bm_info, bmInfo, 8); +UVCG_STREAMING_HEADER_ATTR(b_terminal_link, bTerminalLink, 8); +UVCG_STREAMING_HEADER_ATTR(b_still_capture_method, bStillCaptureMethod, 8); +UVCG_STREAMING_HEADER_ATTR(b_trigger_support, bTriggerSupport, 8); +UVCG_STREAMING_HEADER_ATTR(b_trigger_usage, bTriggerUsage, 8); #undef UVCG_STREAMING_HEADER_ATTR @@ -884,31 +1047,26 @@ static struct config_item return &h->item; } -static void uvcg_streaming_header_drop(struct config_group *group, - struct config_item *item) -{ - struct uvcg_streaming_header *h = to_uvcg_streaming_header(item); - - kfree(h); -} - -/* streaming/header */ -static struct uvcg_streaming_header_grp { - struct config_group 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_item_ops = &uvcg_config_item_ops, + .ct_group_ops = &uvcg_streaming_header_grp_ops, + .ct_owner = THIS_MODULE, + }, + .name = "header", }; -/* streaming/<mode>/<format>/<NAME> */ +/* ----------------------------------------------------------------------------- + * streaming/<mode>/<format>/<NAME> + */ + struct uvcg_frame { + struct config_item item; + enum uvcg_format_type fmt_type; struct { u8 b_length; u8 b_descriptor_type; @@ -924,8 +1082,6 @@ struct uvcg_frame { u8 b_frame_interval_type; } __attribute__((packed)) frame; u32 *dw_frame_interval; - enum uvcg_format_type fmt_type; - struct config_item item; }; static struct uvcg_frame *to_uvcg_frame(struct config_item *item) @@ -933,7 +1089,7 @@ static struct uvcg_frame *to_uvcg_frame(struct config_item *item) return container_of(item, struct uvcg_frame, item); } -#define UVCG_FRAME_ATTR(cname, aname, to_cpu_endian, to_little_endian, bits) \ +#define UVCG_FRAME_ATTR(cname, aname, bits) \ static ssize_t uvcg_frame_##cname##_show(struct config_item *item, char *page)\ { \ struct uvcg_frame *f = to_uvcg_frame(item); \ @@ -948,7 +1104,7 @@ static ssize_t uvcg_frame_##cname##_show(struct config_item *item, char *page)\ opts = to_f_uvc_opts(opts_item); \ \ mutex_lock(&opts->lock); \ - result = sprintf(page, "%d\n", to_cpu_endian(f->frame.cname)); \ + result = sprintf(page, "%u\n", f->frame.cname); \ mutex_unlock(&opts->lock); \ \ mutex_unlock(su_mutex); \ @@ -963,8 +1119,8 @@ static ssize_t uvcg_frame_##cname##_store(struct config_item *item, \ struct config_item *opts_item; \ struct uvcg_format *fmt; \ struct mutex *su_mutex = &f->item.ci_group->cg_subsys->su_mutex;\ + typeof(f->frame.cname) num; \ int ret; \ - u##bits num; \ \ ret = kstrtou##bits(page, 0, &num); \ if (ret) \ @@ -982,7 +1138,7 @@ static ssize_t uvcg_frame_##cname##_store(struct config_item *item, \ goto end; \ } \ \ - f->frame.cname = to_little_endian(num); \ + f->frame.cname = num; \ ret = len; \ end: \ mutex_unlock(&opts->lock); \ @@ -992,20 +1148,48 @@ end: \ \ UVC_ATTR(uvcg_frame_, cname, aname); -#define noop_conversion(x) (x) +static ssize_t uvcg_frame_b_frame_index_show(struct config_item *item, + char *page) +{ + struct uvcg_frame *f = to_uvcg_frame(item); + struct uvcg_format *fmt; + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct config_item *fmt_item; + struct mutex *su_mutex = &f->item.ci_group->cg_subsys->su_mutex; + int result; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + fmt_item = f->item.ci_parent; + fmt = to_uvcg_format(fmt_item); -UVCG_FRAME_ATTR(bm_capabilities, bmCapabilities, noop_conversion, - noop_conversion, 8); -UVCG_FRAME_ATTR(w_width, wWidth, le16_to_cpu, cpu_to_le16, 16); -UVCG_FRAME_ATTR(w_height, wHeight, le16_to_cpu, cpu_to_le16, 16); -UVCG_FRAME_ATTR(dw_min_bit_rate, dwMinBitRate, le32_to_cpu, cpu_to_le32, 32); -UVCG_FRAME_ATTR(dw_max_bit_rate, dwMaxBitRate, le32_to_cpu, cpu_to_le32, 32); -UVCG_FRAME_ATTR(dw_max_video_frame_buffer_size, dwMaxVideoFrameBufferSize, - le32_to_cpu, cpu_to_le32, 32); -UVCG_FRAME_ATTR(dw_default_frame_interval, dwDefaultFrameInterval, - le32_to_cpu, cpu_to_le32, 32); + if (!fmt->linked) { + result = -EBUSY; + goto out; + } -#undef noop_conversion + opts_item = fmt_item->ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + result = sprintf(page, "%u\n", f->frame.b_frame_index); + mutex_unlock(&opts->lock); + +out: + mutex_unlock(su_mutex); + return result; +} + +UVC_ATTR_RO(uvcg_frame_, b_frame_index, bFrameIndex); + +UVCG_FRAME_ATTR(bm_capabilities, bmCapabilities, 8); +UVCG_FRAME_ATTR(w_width, wWidth, 16); +UVCG_FRAME_ATTR(w_height, wHeight, 16); +UVCG_FRAME_ATTR(dw_min_bit_rate, dwMinBitRate, 32); +UVCG_FRAME_ATTR(dw_max_bit_rate, dwMaxBitRate, 32); +UVCG_FRAME_ATTR(dw_max_video_frame_buffer_size, dwMaxVideoFrameBufferSize, 32); +UVCG_FRAME_ATTR(dw_default_frame_interval, dwDefaultFrameInterval, 32); #undef UVCG_FRAME_ATTR @@ -1026,8 +1210,7 @@ static ssize_t uvcg_frame_dw_frame_interval_show(struct config_item *item, mutex_lock(&opts->lock); for (result = 0, i = 0; i < frm->frame.b_frame_interval_type; ++i) { - result += sprintf(pg, "%d\n", - le32_to_cpu(frm->dw_frame_interval[i])); + result += sprintf(pg, "%u\n", frm->dw_frame_interval[i]); pg = page + result; } mutex_unlock(&opts->lock); @@ -1052,7 +1235,7 @@ static inline int __uvcg_fill_frm_intrv(char *buf, void *priv) return ret; interv = priv; - **interv = cpu_to_le32(num); + **interv = num; ++*interv; return 0; @@ -1129,6 +1312,8 @@ static ssize_t uvcg_frame_dw_frame_interval_store(struct config_item *item, kfree(ch->dw_frame_interval); ch->dw_frame_interval = frm_intrv; ch->frame.b_frame_interval_type = n; + sort(ch->dw_frame_interval, n, sizeof(*ch->dw_frame_interval), + uvcg_config_compare_u32, NULL); ret = len; end: @@ -1140,6 +1325,7 @@ end: UVC_ATTR(uvcg_frame_, dw_frame_interval, dwFrameInterval); static struct configfs_attribute *uvcg_frame_attrs[] = { + &uvcg_frame_attr_b_frame_index, &uvcg_frame_attr_bm_capabilities, &uvcg_frame_attr_w_width, &uvcg_frame_attr_w_height, @@ -1152,6 +1338,7 @@ static struct configfs_attribute *uvcg_frame_attrs[] = { }; static const struct config_item_type uvcg_frame_type = { + .ct_item_ops = &uvcg_config_item_ops, .ct_attrs = uvcg_frame_attrs, .ct_owner = THIS_MODULE, }; @@ -1170,12 +1357,12 @@ static struct config_item *uvcg_frame_make(struct config_group *group, h->frame.b_descriptor_type = USB_DT_CS_INTERFACE; h->frame.b_frame_index = 1; - h->frame.w_width = cpu_to_le16(640); - h->frame.w_height = cpu_to_le16(360); - h->frame.dw_min_bit_rate = cpu_to_le32(18432000); - h->frame.dw_max_bit_rate = cpu_to_le32(55296000); - h->frame.dw_max_video_frame_buffer_size = cpu_to_le32(460800); - h->frame.dw_default_frame_interval = cpu_to_le32(666666); + h->frame.w_width = 640; + h->frame.w_height = 360; + h->frame.dw_min_bit_rate = 18432000; + h->frame.dw_max_bit_rate = 55296000; + h->frame.dw_max_video_frame_buffer_size = 460800; + h->frame.dw_default_frame_interval = 666666; opts_item = group->cg_item.ci_parent->ci_parent->ci_parent; opts = to_f_uvc_opts(opts_item); @@ -1203,7 +1390,6 @@ static struct config_item *uvcg_frame_make(struct config_group *group, static void uvcg_frame_drop(struct config_group *group, struct config_item *item) { - struct uvcg_frame *h = to_uvcg_frame(item); struct uvcg_format *fmt; struct f_uvc_opts *opts; struct config_item *opts_item; @@ -1214,11 +1400,31 @@ static void uvcg_frame_drop(struct config_group *group, struct config_item *item mutex_lock(&opts->lock); fmt = to_uvcg_format(&group->cg_item); --fmt->num_frames; - kfree(h); mutex_unlock(&opts->lock); + + config_item_put(item); +} + +static void uvcg_format_set_indices(struct config_group *fmt) +{ + struct config_item *ci; + unsigned int i = 1; + + list_for_each_entry(ci, &fmt->cg_children, ci_entry) { + struct uvcg_frame *frm; + + if (ci->ci_type != &uvcg_frame_type) + continue; + + frm = to_uvcg_frame(ci); + frm->frame.b_frame_index = i++; + } } -/* streaming/uncompressed/<NAME> */ +/* ----------------------------------------------------------------------------- + * streaming/uncompressed/<NAME> + */ + struct uvcg_uncompressed { struct uvcg_format fmt; struct uvc_format_uncompressed desc; @@ -1290,7 +1496,7 @@ end: UVC_ATTR(uvcg_uncompressed_, guid_format, guidFormat); -#define UVCG_UNCOMPRESSED_ATTR_RO(cname, aname, conv) \ +#define UVCG_UNCOMPRESSED_ATTR_RO(cname, aname, bits) \ static ssize_t uvcg_uncompressed_##cname##_show( \ struct config_item *item, char *page) \ { \ @@ -1306,7 +1512,7 @@ static ssize_t uvcg_uncompressed_##cname##_show( \ opts = to_f_uvc_opts(opts_item); \ \ mutex_lock(&opts->lock); \ - result = sprintf(page, "%d\n", conv(u->desc.aname)); \ + result = sprintf(page, "%u\n", le##bits##_to_cpu(u->desc.aname));\ mutex_unlock(&opts->lock); \ \ mutex_unlock(su_mutex); \ @@ -1315,7 +1521,7 @@ static ssize_t uvcg_uncompressed_##cname##_show( \ \ UVC_ATTR_RO(uvcg_uncompressed_, cname, aname); -#define UVCG_UNCOMPRESSED_ATTR(cname, aname, conv) \ +#define UVCG_UNCOMPRESSED_ATTR(cname, aname, bits) \ static ssize_t uvcg_uncompressed_##cname##_show( \ struct config_item *item, char *page) \ { \ @@ -1331,7 +1537,7 @@ static ssize_t uvcg_uncompressed_##cname##_show( \ opts = to_f_uvc_opts(opts_item); \ \ mutex_lock(&opts->lock); \ - result = sprintf(page, "%d\n", conv(u->desc.aname)); \ + result = sprintf(page, "%u\n", le##bits##_to_cpu(u->desc.aname));\ mutex_unlock(&opts->lock); \ \ mutex_unlock(su_mutex); \ @@ -1378,16 +1584,12 @@ end: \ \ UVC_ATTR(uvcg_uncompressed_, cname, aname); -#define identity_conv(x) (x) - -UVCG_UNCOMPRESSED_ATTR(b_bits_per_pixel, bBitsPerPixel, identity_conv); -UVCG_UNCOMPRESSED_ATTR(b_default_frame_index, bDefaultFrameIndex, - identity_conv); -UVCG_UNCOMPRESSED_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, identity_conv); -UVCG_UNCOMPRESSED_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, identity_conv); -UVCG_UNCOMPRESSED_ATTR_RO(bm_interface_flags, bmInterfaceFlags, identity_conv); - -#undef identity_conv +UVCG_UNCOMPRESSED_ATTR_RO(b_format_index, bFormatIndex, 8); +UVCG_UNCOMPRESSED_ATTR(b_bits_per_pixel, bBitsPerPixel, 8); +UVCG_UNCOMPRESSED_ATTR(b_default_frame_index, bDefaultFrameIndex, 8); +UVCG_UNCOMPRESSED_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, 8); +UVCG_UNCOMPRESSED_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, 8); +UVCG_UNCOMPRESSED_ATTR_RO(bm_interface_flags, bmInterfaceFlags, 8); #undef UVCG_UNCOMPRESSED_ATTR #undef UVCG_UNCOMPRESSED_ATTR_RO @@ -1410,6 +1612,7 @@ uvcg_uncompressed_bma_controls_store(struct config_item *item, UVC_ATTR(uvcg_uncompressed_, bma_controls, bmaControls); static struct configfs_attribute *uvcg_uncompressed_attrs[] = { + &uvcg_uncompressed_attr_b_format_index, &uvcg_uncompressed_attr_guid_format, &uvcg_uncompressed_attr_b_bits_per_pixel, &uvcg_uncompressed_attr_b_default_frame_index, @@ -1421,6 +1624,7 @@ static struct configfs_attribute *uvcg_uncompressed_attrs[] = { }; static const struct config_item_type uvcg_uncompressed_type = { + .ct_item_ops = &uvcg_config_item_ops, .ct_group_ops = &uvcg_uncompressed_group_ops, .ct_attrs = uvcg_uncompressed_attrs, .ct_owner = THIS_MODULE, @@ -1457,25 +1661,23 @@ static struct config_group *uvcg_uncompressed_make(struct config_group *group, return &h->fmt.group; } -static void uvcg_uncompressed_drop(struct config_group *group, - struct config_item *item) -{ - struct uvcg_uncompressed *h = to_uvcg_uncompressed(item); - - kfree(h); -} - static struct configfs_group_operations uvcg_uncompressed_grp_ops = { .make_group = uvcg_uncompressed_make, - .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_item_ops = &uvcg_config_item_ops, + .ct_group_ops = &uvcg_uncompressed_grp_ops, + .ct_owner = THIS_MODULE, + }, + .name = "uncompressed", }; -/* streaming/mjpeg/<NAME> */ +/* ----------------------------------------------------------------------------- + * streaming/mjpeg/<NAME> + */ + struct uvcg_mjpeg { struct uvcg_format fmt; struct uvc_format_mjpeg desc; @@ -1493,7 +1695,7 @@ static struct configfs_group_operations uvcg_mjpeg_group_ops = { .drop_item = uvcg_frame_drop, }; -#define UVCG_MJPEG_ATTR_RO(cname, aname, conv) \ +#define UVCG_MJPEG_ATTR_RO(cname, aname, bits) \ static ssize_t uvcg_mjpeg_##cname##_show(struct config_item *item, char *page)\ { \ struct uvcg_mjpeg *u = to_uvcg_mjpeg(item); \ @@ -1508,7 +1710,7 @@ static ssize_t uvcg_mjpeg_##cname##_show(struct config_item *item, char *page)\ opts = to_f_uvc_opts(opts_item); \ \ mutex_lock(&opts->lock); \ - result = sprintf(page, "%d\n", conv(u->desc.aname)); \ + result = sprintf(page, "%u\n", le##bits##_to_cpu(u->desc.aname));\ mutex_unlock(&opts->lock); \ \ mutex_unlock(su_mutex); \ @@ -1517,7 +1719,7 @@ static ssize_t uvcg_mjpeg_##cname##_show(struct config_item *item, char *page)\ \ UVC_ATTR_RO(uvcg_mjpeg_, cname, aname) -#define UVCG_MJPEG_ATTR(cname, aname, conv) \ +#define UVCG_MJPEG_ATTR(cname, aname, bits) \ static ssize_t uvcg_mjpeg_##cname##_show(struct config_item *item, char *page)\ { \ struct uvcg_mjpeg *u = to_uvcg_mjpeg(item); \ @@ -1532,7 +1734,7 @@ static ssize_t uvcg_mjpeg_##cname##_show(struct config_item *item, char *page)\ opts = to_f_uvc_opts(opts_item); \ \ mutex_lock(&opts->lock); \ - result = sprintf(page, "%d\n", conv(u->desc.aname)); \ + result = sprintf(page, "%u\n", le##bits##_to_cpu(u->desc.aname));\ mutex_unlock(&opts->lock); \ \ mutex_unlock(su_mutex); \ @@ -1579,16 +1781,12 @@ end: \ \ UVC_ATTR(uvcg_mjpeg_, cname, aname) -#define identity_conv(x) (x) - -UVCG_MJPEG_ATTR(b_default_frame_index, bDefaultFrameIndex, - identity_conv); -UVCG_MJPEG_ATTR_RO(bm_flags, bmFlags, identity_conv); -UVCG_MJPEG_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, identity_conv); -UVCG_MJPEG_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, identity_conv); -UVCG_MJPEG_ATTR_RO(bm_interface_flags, bmInterfaceFlags, identity_conv); - -#undef identity_conv +UVCG_MJPEG_ATTR_RO(b_format_index, bFormatIndex, 8); +UVCG_MJPEG_ATTR(b_default_frame_index, bDefaultFrameIndex, 8); +UVCG_MJPEG_ATTR_RO(bm_flags, bmFlags, 8); +UVCG_MJPEG_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, 8); +UVCG_MJPEG_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, 8); +UVCG_MJPEG_ATTR_RO(bm_interface_flags, bmInterfaceFlags, 8); #undef UVCG_MJPEG_ATTR #undef UVCG_MJPEG_ATTR_RO @@ -1611,6 +1809,7 @@ uvcg_mjpeg_bma_controls_store(struct config_item *item, UVC_ATTR(uvcg_mjpeg_, bma_controls, bmaControls); static struct configfs_attribute *uvcg_mjpeg_attrs[] = { + &uvcg_mjpeg_attr_b_format_index, &uvcg_mjpeg_attr_b_default_frame_index, &uvcg_mjpeg_attr_bm_flags, &uvcg_mjpeg_attr_b_aspect_ratio_x, @@ -1621,6 +1820,7 @@ static struct configfs_attribute *uvcg_mjpeg_attrs[] = { }; static const struct config_item_type uvcg_mjpeg_type = { + .ct_item_ops = &uvcg_config_item_ops, .ct_group_ops = &uvcg_mjpeg_group_ops, .ct_attrs = uvcg_mjpeg_attrs, .ct_owner = THIS_MODULE, @@ -1651,56 +1851,42 @@ static struct config_group *uvcg_mjpeg_make(struct config_group *group, return &h->fmt.group; } -static void uvcg_mjpeg_drop(struct config_group *group, - struct config_item *item) -{ - struct uvcg_mjpeg *h = to_uvcg_mjpeg(item); - - kfree(h); -} - static struct configfs_group_operations uvcg_mjpeg_grp_ops = { .make_group = uvcg_mjpeg_make, - .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_item_ops = &uvcg_config_item_ops, + .ct_group_ops = &uvcg_mjpeg_grp_ops, + .ct_owner = THIS_MODULE, + }, + .name = "mjpeg", }; -/* streaming/color_matching/default */ -static struct uvcg_default_color_matching { - struct config_group group; -} uvcg_default_color_matching; - -static inline struct uvcg_default_color_matching -*to_uvcg_default_color_matching(struct config_item *item) -{ - return container_of(to_config_group(item), - struct uvcg_default_color_matching, group); -} +/* ----------------------------------------------------------------------------- + * streaming/color_matching/default + */ -#define UVCG_DEFAULT_COLOR_MATCHING_ATTR(cname, aname, conv) \ +#define UVCG_DEFAULT_COLOR_MATCHING_ATTR(cname, aname, bits) \ static ssize_t uvcg_default_color_matching_##cname##_show( \ - struct config_item *item, char *page) \ + struct config_item *item, char *page) \ { \ - struct uvcg_default_color_matching *dc = \ - to_uvcg_default_color_matching(item); \ + struct config_group *group = to_config_group(item); \ struct f_uvc_opts *opts; \ struct config_item *opts_item; \ - struct mutex *su_mutex = &dc->group.cg_subsys->su_mutex; \ + struct mutex *su_mutex = &group->cg_subsys->su_mutex; \ struct uvc_color_matching_descriptor *cd; \ int result; \ \ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ \ - opts_item = dc->group.cg_item.ci_parent->ci_parent->ci_parent; \ + opts_item = group->cg_item.ci_parent->ci_parent->ci_parent; \ opts = to_f_uvc_opts(opts_item); \ cd = &opts->uvc_color_matching; \ \ mutex_lock(&opts->lock); \ - result = sprintf(page, "%d\n", conv(cd->aname)); \ + result = sprintf(page, "%u\n", le##bits##_to_cpu(cd->aname)); \ mutex_unlock(&opts->lock); \ \ mutex_unlock(su_mutex); \ @@ -1709,16 +1895,10 @@ static ssize_t uvcg_default_color_matching_##cname##_show( \ \ UVC_ATTR_RO(uvcg_default_color_matching_, cname, aname) -#define identity_conv(x) (x) - -UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_color_primaries, bColorPrimaries, - identity_conv); +UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_color_primaries, bColorPrimaries, 8); UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_transfer_characteristics, - bTransferCharacteristics, identity_conv); -UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_matrix_coefficients, bMatrixCoefficients, - identity_conv); - -#undef identity_conv + bTransferCharacteristics, 8); +UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_matrix_coefficients, bMatrixCoefficients, 8); #undef UVCG_DEFAULT_COLOR_MATCHING_ATTR @@ -1729,41 +1909,54 @@ 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_item_ops = &uvcg_config_item_ops, + .ct_attrs = uvcg_default_color_matching_attrs, + .ct_owner = THIS_MODULE, + }, + .name = "default", }; -/* struct uvcg_color_matching {}; */ - -/* streaming/color_matching */ -static struct uvcg_color_matching_grp { - struct config_group group; -} uvcg_color_matching_grp; +/* ----------------------------------------------------------------------------- + * streaming/color_matching + */ -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_item_ops = &uvcg_config_item_ops, + .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 uvcg_streaming_class { - struct config_group group; -} uvcg_streaming_class_fs, uvcg_streaming_class_hs, uvcg_streaming_class_ss; +/* ----------------------------------------------------------------------------- + * streaming/class/{fs|hs|ss} + */ +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 uvcg_streaming_class *cl = container_of(to_config_group(i), - struct uvcg_streaming_class, group); + struct uvcg_streaming_class_group *group = + container_of(i, struct uvcg_streaming_class_group, + group.cg_item); - if (cl == &uvcg_streaming_class_fs) + if (!strcmp(group->name, "fs")) return &o->uvc_fs_streaming_cls; - if (cl == &uvcg_streaming_class_hs) + if (!strcmp(group->name, "hs")) return &o->uvc_hs_streaming_cls; - if (cl == &uvcg_streaming_class_ss) + if (!strcmp(group->name, "ss")) return &o->uvc_ss_streaming_cls; return NULL; @@ -1922,24 +2115,22 @@ static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n, struct uvcg_format *fmt = priv1; if (fmt->type == UVCG_UNCOMPRESSED) { - struct uvc_format_uncompressed *unc = *dest; struct uvcg_uncompressed *u = container_of(fmt, struct uvcg_uncompressed, fmt); + u->desc.bFormatIndex = n + 1; + u->desc.bNumFrameDescriptors = fmt->num_frames; memcpy(*dest, &u->desc, sizeof(u->desc)); *dest += sizeof(u->desc); - unc->bNumFrameDescriptors = fmt->num_frames; - unc->bFormatIndex = n + 1; } else if (fmt->type == UVCG_MJPEG) { - struct uvc_format_mjpeg *mjp = *dest; struct uvcg_mjpeg *m = container_of(fmt, struct uvcg_mjpeg, fmt); + m->desc.bFormatIndex = n + 1; + m->desc.bNumFrameDescriptors = fmt->num_frames; memcpy(*dest, &m->desc, sizeof(m->desc)); *dest += sizeof(m->desc); - mjp->bNumFrameDescriptors = fmt->num_frames; - mjp->bFormatIndex = n + 1; } else { return -EINVAL; } @@ -2038,6 +2229,7 @@ static int uvcg_streaming_class_allow_link(struct config_item *src, unlock: mutex_unlock(&opts->lock); out: + config_item_put(header); mutex_unlock(su_mutex); return ret; } @@ -2078,10 +2270,12 @@ static void uvcg_streaming_class_drop_link(struct config_item *src, unlock: mutex_unlock(&opts->lock); out: + config_item_put(header); mutex_unlock(su_mutex); } static struct configfs_item_operations uvcg_streaming_class_item_ops = { + .release = uvcg_config_item_release, .allow_link = uvcg_streaming_class_allow_link, .drop_link = uvcg_streaming_class_drop_link, }; @@ -2091,36 +2285,109 @@ static const struct config_item_type uvcg_streaming_class_type = { .ct_owner = THIS_MODULE, }; -/* streaming/class */ -static struct uvcg_streaming_class_grp { - struct config_group group; -} uvcg_streaming_class_grp; +/* ----------------------------------------------------------------------------- + * streaming/class + */ + +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; -static const struct config_item_type uvcg_streaming_class_grp_type = { - .ct_owner = THIS_MODULE, + 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 uvcg_config_group_type uvcg_streaming_class_grp_type = { + .type = { + .ct_item_ops = &uvcg_config_item_ops, + .ct_owner = THIS_MODULE, + }, + .name = "class", + .create_children = uvcg_streaming_class_create_children, }; -/* streaming */ -static struct uvcg_streaming_grp { - struct config_group group; -} uvcg_streaming_grp; +/* ----------------------------------------------------------------------------- + * streaming + */ -static const struct config_item_type uvcg_streaming_grp_type = { - .ct_owner = THIS_MODULE, +static ssize_t uvcg_default_streaming_b_interface_number_show( + struct config_item *item, char *page) +{ + struct config_group *group = to_config_group(item); + struct mutex *su_mutex = &group->cg_subsys->su_mutex; + struct config_item *opts_item; + struct f_uvc_opts *opts; + int result = 0; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = item->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + result += sprintf(page, "%u\n", opts->streaming_interface); + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + + return result; +} + +UVC_ATTR_RO(uvcg_default_streaming_, b_interface_number, bInterfaceNumber); + +static struct configfs_attribute *uvcg_default_streaming_attrs[] = { + &uvcg_default_streaming_attr_b_interface_number, + NULL, +}; + +static const struct uvcg_config_group_type uvcg_streaming_grp_type = { + .type = { + .ct_item_ops = &uvcg_config_item_ops, + .ct_attrs = uvcg_default_streaming_attrs, + .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, + }, }; -static void uvc_attr_release(struct config_item *item) +/* ----------------------------------------------------------------------------- + * UVC function + */ + +static void uvc_func_item_release(struct config_item *item) { struct f_uvc_opts *opts = to_f_uvc_opts(item); + uvcg_config_remove_children(to_config_group(item)); usb_put_function_instance(&opts->func_inst); } -static struct configfs_item_operations uvc_item_ops = { - .release = uvc_attr_release, +static struct configfs_item_operations uvc_func_item_ops = { + .release = uvc_func_item_release, }; -#define UVCG_OPTS_ATTR(cname, aname, conv, str2u, uxx, vnoc, limit) \ +#define UVCG_OPTS_ATTR(cname, aname, limit) \ static ssize_t f_uvc_opts_##cname##_show( \ struct config_item *item, char *page) \ { \ @@ -2128,7 +2395,7 @@ static ssize_t f_uvc_opts_##cname##_show( \ int result; \ \ mutex_lock(&opts->lock); \ - result = sprintf(page, "%d\n", conv(opts->cname)); \ + result = sprintf(page, "%u\n", opts->cname); \ mutex_unlock(&opts->lock); \ \ return result; \ @@ -2139,8 +2406,8 @@ f_uvc_opts_##cname##_store(struct config_item *item, \ const char *page, size_t len) \ { \ struct f_uvc_opts *opts = to_f_uvc_opts(item); \ + unsigned int num; \ int ret; \ - uxx num; \ \ mutex_lock(&opts->lock); \ if (opts->refcnt) { \ @@ -2148,7 +2415,7 @@ f_uvc_opts_##cname##_store(struct config_item *item, \ goto end; \ } \ \ - ret = str2u(page, 0, &num); \ + ret = kstrtouint(page, 0, &num); \ if (ret) \ goto end; \ \ @@ -2156,7 +2423,7 @@ f_uvc_opts_##cname##_store(struct config_item *item, \ ret = -EINVAL; \ goto end; \ } \ - opts->cname = vnoc(num); \ + opts->cname = num; \ ret = len; \ end: \ mutex_unlock(&opts->lock); \ @@ -2165,16 +2432,9 @@ end: \ \ UVC_ATTR(f_uvc_opts_, cname, cname) -#define identity_conv(x) (x) - -UVCG_OPTS_ATTR(streaming_interval, streaming_interval, identity_conv, - kstrtou8, u8, identity_conv, 16); -UVCG_OPTS_ATTR(streaming_maxpacket, streaming_maxpacket, le16_to_cpu, - kstrtou16, u16, le16_to_cpu, 3072); -UVCG_OPTS_ATTR(streaming_maxburst, streaming_maxburst, identity_conv, - kstrtou8, u8, identity_conv, 15); - -#undef identity_conv +UVCG_OPTS_ATTR(streaming_interval, streaming_interval, 16); +UVCG_OPTS_ATTR(streaming_maxpacket, streaming_maxpacket, 3072); +UVCG_OPTS_ATTR(streaming_maxburst, streaming_maxburst, 15); #undef UVCG_OPTS_ATTR @@ -2185,123 +2445,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_func_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.group, - "header", - &uvcg_control_header_grp_type); - - config_group_init_type_name(&uvcg_default_processing.group, - "default", &uvcg_default_processing_type); - config_group_init_type_name(&uvcg_processing_grp.group, - "processing", &uvcg_processing_grp_type); - configfs_add_default_group(&uvcg_default_processing.group, - &uvcg_processing_grp.group); - - config_group_init_type_name(&uvcg_default_camera.group, - "default", &uvcg_default_camera_type); - config_group_init_type_name(&uvcg_camera_grp.group, - "camera", &uvcg_camera_grp_type); - configfs_add_default_group(&uvcg_default_camera.group, - &uvcg_camera_grp.group); - - config_group_init_type_name(&uvcg_default_output.group, - "default", &uvcg_default_output_type); - config_group_init_type_name(&uvcg_output_grp.group, - "output", &uvcg_output_grp_type); - configfs_add_default_group(&uvcg_default_output.group, - &uvcg_output_grp.group); - - config_group_init_type_name(&uvcg_terminal_grp.group, - "terminal", &uvcg_terminal_grp_type); - configfs_add_default_group(&uvcg_camera_grp.group, - &uvcg_terminal_grp.group); - configfs_add_default_group(&uvcg_output_grp.group, - &uvcg_terminal_grp.group); - - config_group_init_type_name(&uvcg_control_class_fs.group, - "fs", &uvcg_control_class_type); - config_group_init_type_name(&uvcg_control_class_ss.group, - "ss", &uvcg_control_class_type); - config_group_init_type_name(&uvcg_control_class_grp.group, - "class", - &uvcg_control_class_grp_type); - configfs_add_default_group(&uvcg_control_class_fs.group, - &uvcg_control_class_grp.group); - configfs_add_default_group(&uvcg_control_class_ss.group, - &uvcg_control_class_grp.group); - - config_group_init_type_name(&uvcg_control_grp.group, - "control", - &uvcg_control_grp_type); - configfs_add_default_group(&uvcg_control_header_grp.group, - &uvcg_control_grp.group); - configfs_add_default_group(&uvcg_processing_grp.group, - &uvcg_control_grp.group); - configfs_add_default_group(&uvcg_terminal_grp.group, - &uvcg_control_grp.group); - configfs_add_default_group(&uvcg_control_class_grp.group, - &uvcg_control_grp.group); - - config_group_init_type_name(&uvcg_streaming_header_grp.group, - "header", - &uvcg_streaming_header_grp_type); - config_group_init_type_name(&uvcg_uncompressed_grp.group, - "uncompressed", - &uvcg_uncompressed_grp_type); - config_group_init_type_name(&uvcg_mjpeg_grp.group, - "mjpeg", - &uvcg_mjpeg_grp_type); - config_group_init_type_name(&uvcg_default_color_matching.group, - "default", - &uvcg_default_color_matching_type); - config_group_init_type_name(&uvcg_color_matching_grp.group, - "color_matching", - &uvcg_color_matching_grp_type); - configfs_add_default_group(&uvcg_default_color_matching.group, - &uvcg_color_matching_grp.group); - - config_group_init_type_name(&uvcg_streaming_class_fs.group, - "fs", &uvcg_streaming_class_type); - config_group_init_type_name(&uvcg_streaming_class_hs.group, - "hs", &uvcg_streaming_class_type); - config_group_init_type_name(&uvcg_streaming_class_ss.group, - "ss", &uvcg_streaming_class_type); - config_group_init_type_name(&uvcg_streaming_class_grp.group, - "class", &uvcg_streaming_class_grp_type); - configfs_add_default_group(&uvcg_streaming_class_fs.group, - &uvcg_streaming_class_grp.group); - configfs_add_default_group(&uvcg_streaming_class_hs.group, - &uvcg_streaming_class_grp.group); - configfs_add_default_group(&uvcg_streaming_class_ss.group, - &uvcg_streaming_class_grp.group); - - config_group_init_type_name(&uvcg_streaming_grp.group, - "streaming", &uvcg_streaming_grp_type); - configfs_add_default_group(&uvcg_streaming_header_grp.group, - &uvcg_streaming_grp.group); - configfs_add_default_group(&uvcg_uncompressed_grp.group, - &uvcg_streaming_grp.group); - configfs_add_default_group(&uvcg_mjpeg_grp.group, - &uvcg_streaming_grp.group); - configfs_add_default_group(&uvcg_color_matching_grp.group, - &uvcg_streaming_grp.group); - configfs_add_default_group(&uvcg_streaming_class_grp.group, - &uvcg_streaming_grp.group); - - config_group_init_type_name(&opts->func_inst.group, - "", - &uvc_func_type); - configfs_add_default_group(&uvcg_control_grp.group, - &opts->func_inst.group); - configfs_add_default_group(&uvcg_streaming_grp.group, - &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; } |