--- /dev/null
+/*
+ * uvc_configfs.c
+ *
+ * Configfs support for the uvc function.
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include "u_uvc.h"
+#include "uvc_configfs.h"
+
+#define UVCG_STREAMING_CONTROL_SIZE    1
+
+#define CONFIGFS_ATTR_OPS_RO(_item)                                    \
+static ssize_t _item##_attr_show(struct config_item *item,             \
+                                struct configfs_attribute *attr,       \
+                                char *page)                            \
+{                                                                      \
+       struct _item *_item = to_##_item(item);                         \
+       struct _item##_attribute *_item##_attr =                        \
+               container_of(attr, struct _item##_attribute, attr);     \
+       ssize_t ret = 0;                                                \
+                                                                       \
+       if (_item##_attr->show)                                         \
+               ret = _item##_attr->show(_item, page);                  \
+       return ret;                                                     \
+}
+
+static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item);
+
+/* control/header/<NAME> */
+DECLARE_UVC_HEADER_DESCRIPTOR(1);
+
+struct uvcg_control_header {
+       struct config_item              item;
+       struct UVC_HEADER_DESCRIPTOR(1) desc;
+       unsigned                        linked;
+};
+
+struct uvcg_control_header *to_uvcg_control_header(struct config_item *item)
+{
+       return container_of(item, struct uvcg_control_header, item);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_control_header);
+CONFIGFS_ATTR_OPS(uvcg_control_header);
+
+static struct configfs_item_operations uvcg_control_header_item_ops = {
+       .show_attribute         = uvcg_control_header_attr_show,
+       .store_attribute        = uvcg_control_header_attr_store,
+};
+
+#define UVCG_CTRL_HDR_ATTR(cname, aname, conv, str2u, uxx, vnoc, limit)        \
+static ssize_t uvcg_control_header_##cname##_show(                     \
+       struct uvcg_control_header *ch, char *page)                     \
+{                                                                      \
+       struct f_uvc_opts *opts;                                        \
+       struct config_item *opts_item;                                  \
+       struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex;\
+       int result;                                                     \
+                                                                       \
+       mutex_lock(su_mutex); /* for navigating configfs hierarchy */   \
+                                                                       \
+       opts_item = ch->item.ci_parent->ci_parent->ci_parent;           \
+       opts = to_f_uvc_opts(opts_item);                                \
+                                                                       \
+       mutex_lock(&opts->lock);                                        \
+       result = sprintf(page, "%d\n", conv(ch->desc.aname));           \
+       mutex_unlock(&opts->lock);                                      \
+                                                                       \
+       mutex_unlock(su_mutex);                                         \
+       return result;                                                  \
+}                                                                      \
+                                                                       \
+static ssize_t                                                         \
+uvcg_control_header_##cname##_store(struct uvcg_control_header *ch,    \
+                          const char *page, size_t len)                \
+{                                                                      \
+       struct f_uvc_opts *opts;                                        \
+       struct config_item *opts_item;                                  \
+       struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex;\
+       int ret;                                                        \
+       uxx num;                                                        \
+                                                                       \
+       mutex_lock(su_mutex); /* for navigating configfs hierarchy */   \
+                                                                       \
+       opts_item = ch->item.ci_parent->ci_parent->ci_parent;           \
+       opts = to_f_uvc_opts(opts_item);                                \
+                                                                       \
+       mutex_lock(&opts->lock);                                        \
+       if (ch->linked || opts->refcnt) {                               \
+               ret = -EBUSY;                                           \
+               goto end;                                               \
+       }                                                               \
+                                                                       \
+       ret = str2u(page, 0, &num);                                     \
+       if (ret)                                                        \
+               goto end;                                               \
+                                                                       \
+       if (num > limit) {                                              \
+               ret = -EINVAL;                                          \
+               goto end;                                               \
+       }                                                               \
+       ch->desc.aname = vnoc(num);                                     \
+       ret = len;                                                      \
+end:                                                                   \
+       mutex_unlock(&opts->lock);                                      \
+       mutex_unlock(su_mutex);                                         \
+       return ret;                                                     \
+}                                                                      \
+                                                                       \
+static struct uvcg_control_header_attribute                            \
+       uvcg_control_header_##cname =                                   \
+       __CONFIGFS_ATTR(aname, S_IRUGO | S_IWUSR,                       \
+                       uvcg_control_header_##cname##_show,             \
+                       uvcg_control_header_##cname##_store)
+
+UVCG_CTRL_HDR_ATTR(bcd_uvc, bcdUVC, le16_to_cpu, kstrtou16, u16, cpu_to_le16,
+                  0xffff);
+
+UVCG_CTRL_HDR_ATTR(dw_clock_frequency, dwClockFrequency, le32_to_cpu, kstrtou32,
+                  u32, cpu_to_le32, 0x7fffffff);
+
+#undef UVCG_CTRL_HDR_ATTR
+
+static struct configfs_attribute *uvcg_control_header_attrs[] = {
+       &uvcg_control_header_bcd_uvc.attr,
+       &uvcg_control_header_dw_clock_frequency.attr,
+       NULL,
+};
+
+struct config_item_type uvcg_control_header_type = {
+       .ct_item_ops    = &uvcg_control_header_item_ops,
+       .ct_attrs       = uvcg_control_header_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+static struct config_item *uvcg_control_header_make(struct config_group *group,
+                                                   const char *name)
+{
+       struct uvcg_control_header *h;
+
+       h = kzalloc(sizeof(*h), GFP_KERNEL);
+       if (!h)
+               return ERR_CAST(h);
+
+       h->desc.bLength                 = UVC_DT_HEADER_SIZE(1);
+       h->desc.bDescriptorType         = USB_DT_CS_INTERFACE;
+       h->desc.bDescriptorSubType      = UVC_VC_HEADER;
+       h->desc.bcdUVC                  = cpu_to_le16(0x0100);
+       h->desc.dwClockFrequency        = cpu_to_le32(48000000);
+
+       config_item_init_type_name(&h->item, name, &uvcg_control_header_type);
+
+       return &h->item;
+}
+
+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 struct config_item_type uvcg_control_header_grp_type = {
+       .ct_group_ops   = &uvcg_control_header_grp_ops,
+       .ct_owner       = THIS_MODULE,
+};
+
+/* 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);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_default_processing);
+CONFIGFS_ATTR_OPS_RO(uvcg_default_processing);
+
+static struct configfs_item_operations uvcg_default_processing_item_ops = {
+       .show_attribute         = uvcg_default_processing_attr_show,
+};
+
+#define UVCG_DEFAULT_PROCESSING_ATTR(cname, aname, conv)               \
+static ssize_t uvcg_default_processing_##cname##_show(                 \
+       struct uvcg_default_processing *dp, char *page)                 \
+{                                                                      \
+       struct f_uvc_opts *opts;                                        \
+       struct config_item *opts_item;                                  \
+       struct mutex *su_mutex = &dp->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 = to_f_uvc_opts(opts_item);                                \
+       pd = &opts->uvc_processing;                                     \
+                                                                       \
+       mutex_lock(&opts->lock);                                        \
+       result = sprintf(page, "%d\n", conv(pd->aname));                \
+       mutex_unlock(&opts->lock);                                      \
+                                                                       \
+       mutex_unlock(su_mutex);                                         \
+       return result;                                                  \
+}                                                                      \
+                                                                       \
+static struct uvcg_default_processing_attribute                                \
+       uvcg_default_processing_##cname =                               \
+       __CONFIGFS_ATTR_RO(aname, uvcg_default_processing_##cname##_show)
+
+#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
+
+#undef UVCG_DEFAULT_PROCESSING_ATTR
+
+static ssize_t uvcg_default_processing_bm_controls_show(
+       struct uvcg_default_processing *dp, char *page)
+{
+       struct f_uvc_opts *opts;
+       struct config_item *opts_item;
+       struct mutex *su_mutex = &dp->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 = 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]);
+               pg = page + result;
+       }
+       mutex_unlock(&opts->lock);
+
+       mutex_unlock(su_mutex);
+
+       return result;
+}
+
+static struct uvcg_default_processing_attribute
+       uvcg_default_processing_bm_controls =
+       __CONFIGFS_ATTR_RO(bmControls,
+               uvcg_default_processing_bm_controls_show);
+
+static struct configfs_attribute *uvcg_default_processing_attrs[] = {
+       &uvcg_default_processing_b_unit_id.attr,
+       &uvcg_default_processing_b_source_id.attr,
+       &uvcg_default_processing_w_max_multiplier.attr,
+       &uvcg_default_processing_bm_controls.attr,
+       &uvcg_default_processing_i_processing.attr,
+       NULL,
+};
+
+static struct config_item_type uvcg_default_processing_type = {
+       .ct_item_ops    = &uvcg_default_processing_item_ops,
+       .ct_attrs       = uvcg_default_processing_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+/* struct uvcg_processing {}; */
+
+static struct config_group *uvcg_processing_default_groups[] = {
+       &uvcg_default_processing.group,
+       NULL,
+};
+
+/* control/processing */
+static struct uvcg_processing_grp {
+       struct config_group     group;
+} uvcg_processing_grp;
+
+static struct config_item_type uvcg_processing_grp_type = {
+       .ct_owner = THIS_MODULE,
+};
+
+/* 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);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_default_camera);
+CONFIGFS_ATTR_OPS_RO(uvcg_default_camera);
+
+static struct configfs_item_operations uvcg_default_camera_item_ops = {
+       .show_attribute         = uvcg_default_camera_attr_show,
+};
+
+#define UVCG_DEFAULT_CAMERA_ATTR(cname, aname, conv)                   \
+static ssize_t uvcg_default_camera_##cname##_show(                     \
+       struct uvcg_default_camera *dc, char *page)                     \
+{                                                                      \
+       struct f_uvc_opts *opts;                                        \
+       struct config_item *opts_item;                                  \
+       struct mutex *su_mutex = &dc->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-> \
+                       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));                \
+       mutex_unlock(&opts->lock);                                      \
+                                                                       \
+       mutex_unlock(su_mutex);                                         \
+                                                                       \
+       return result;                                                  \
+}                                                                      \
+                                                                       \
+static struct uvcg_default_camera_attribute                            \
+       uvcg_default_camera_##cname =                                   \
+       __CONFIGFS_ATTR_RO(aname, uvcg_default_camera_##cname##_show)
+
+#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(w_objective_focal_length_min, wObjectiveFocalLengthMin,
+                        le16_to_cpu);
+UVCG_DEFAULT_CAMERA_ATTR(w_objective_focal_length_max, wObjectiveFocalLengthMax,
+                        le16_to_cpu);
+UVCG_DEFAULT_CAMERA_ATTR(w_ocular_focal_length, wOcularFocalLength,
+                        le16_to_cpu);
+
+#undef identity_conv
+
+#undef UVCG_DEFAULT_CAMERA_ATTR
+
+static ssize_t uvcg_default_camera_bm_controls_show(
+       struct uvcg_default_camera *dc, char *page)
+{
+       struct f_uvc_opts *opts;
+       struct config_item *opts_item;
+       struct mutex *su_mutex = &dc->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->
+                       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]);
+               pg = page + result;
+       }
+       mutex_unlock(&opts->lock);
+
+       mutex_unlock(su_mutex);
+       return result;
+}
+
+static struct uvcg_default_camera_attribute
+       uvcg_default_camera_bm_controls =
+       __CONFIGFS_ATTR_RO(bmControls, uvcg_default_camera_bm_controls_show);
+
+static struct configfs_attribute *uvcg_default_camera_attrs[] = {
+       &uvcg_default_camera_b_terminal_id.attr,
+       &uvcg_default_camera_w_terminal_type.attr,
+       &uvcg_default_camera_b_assoc_terminal.attr,
+       &uvcg_default_camera_i_terminal.attr,
+       &uvcg_default_camera_w_objective_focal_length_min.attr,
+       &uvcg_default_camera_w_objective_focal_length_max.attr,
+       &uvcg_default_camera_w_ocular_focal_length.attr,
+       &uvcg_default_camera_bm_controls.attr,
+       NULL,
+};
+
+static struct config_item_type uvcg_default_camera_type = {
+       .ct_item_ops    = &uvcg_default_camera_item_ops,
+       .ct_attrs       = uvcg_default_camera_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+/* struct uvcg_camera {}; */
+
+static struct config_group *uvcg_camera_default_groups[] = {
+       &uvcg_default_camera.group,
+       NULL,
+};
+
+/* control/terminal/camera */
+static struct uvcg_camera_grp {
+       struct config_group     group;
+} uvcg_camera_grp;
+
+static struct config_item_type uvcg_camera_grp_type = {
+       .ct_owner = THIS_MODULE,
+};
+
+/* 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);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_default_output);
+CONFIGFS_ATTR_OPS_RO(uvcg_default_output);
+
+static struct configfs_item_operations uvcg_default_output_item_ops = {
+       .show_attribute         = uvcg_default_output_attr_show,
+};
+
+#define UVCG_DEFAULT_OUTPUT_ATTR(cname, aname, conv)                   \
+static ssize_t uvcg_default_output_##cname##_show(                     \
+       struct uvcg_default_output *dout, char *page)                   \
+{                                                                      \
+       struct f_uvc_opts *opts;                                        \
+       struct config_item *opts_item;                                  \
+       struct mutex *su_mutex = &dout->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->          \
+                       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));                \
+       mutex_unlock(&opts->lock);                                      \
+                                                                       \
+       mutex_unlock(su_mutex);                                         \
+                                                                       \
+       return result;                                                  \
+}                                                                      \
+                                                                       \
+static struct uvcg_default_output_attribute                            \
+       uvcg_default_output_##cname =                                   \
+       __CONFIGFS_ATTR_RO(aname, uvcg_default_output_##cname##_show)
+
+#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
+
+#undef UVCG_DEFAULT_OUTPUT_ATTR
+
+static struct configfs_attribute *uvcg_default_output_attrs[] = {
+       &uvcg_default_output_b_terminal_id.attr,
+       &uvcg_default_output_w_terminal_type.attr,
+       &uvcg_default_output_b_assoc_terminal.attr,
+       &uvcg_default_output_b_source_id.attr,
+       &uvcg_default_output_i_terminal.attr,
+       NULL,
+};
+
+static struct config_item_type uvcg_default_output_type = {
+       .ct_item_ops    = &uvcg_default_output_item_ops,
+       .ct_attrs       = uvcg_default_output_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+/* struct uvcg_output {}; */
+
+static struct config_group *uvcg_output_default_groups[] = {
+       &uvcg_default_output.group,
+       NULL,
+};
+
+/* control/terminal/output */
+static struct uvcg_output_grp {
+       struct config_group     group;
+} uvcg_output_grp;
+
+static struct config_item_type uvcg_output_grp_type = {
+       .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *uvcg_terminal_default_groups[] = {
+       &uvcg_camera_grp.group,
+       &uvcg_output_grp.group,
+       NULL,
+};
+
+/* control/terminal */
+static struct uvcg_terminal_grp {
+       struct config_group     group;
+} uvcg_terminal_grp;
+
+static struct config_item_type uvcg_terminal_grp_type = {
+       .ct_owner = THIS_MODULE,
+};
+
+/* control/class/{fs} */
+static struct uvcg_control_class {
+       struct config_group     group;
+} uvcg_control_class_fs, uvcg_control_class_ss;
+
+
+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);
+
+       if (cl == &uvcg_control_class_fs)
+               return o->uvc_fs_control_cls;
+
+       if (cl == &uvcg_control_class_ss)
+               return o->uvc_ss_control_cls;
+
+       return NULL;
+}
+
+static int uvcg_control_class_allow_link(struct config_item *src,
+                                        struct config_item *target)
+{
+       struct config_item *control, *header;
+       struct f_uvc_opts *opts;
+       struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+       struct uvc_descriptor_header **class_array;
+       struct uvcg_control_header *target_hdr;
+       int ret = -EINVAL;
+
+       mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+       control = src->ci_parent->ci_parent;
+       header = config_group_find_item(to_config_group(control), "header");
+       if (!header || target->ci_parent != header)
+               goto out;
+
+       opts = to_f_uvc_opts(control->ci_parent);
+
+       mutex_lock(&opts->lock);
+
+       class_array = uvcg_get_ctl_class_arr(src, opts);
+       if (!class_array)
+               goto unlock;
+       if (opts->refcnt || class_array[0]) {
+               ret = -EBUSY;
+               goto unlock;
+       }
+
+       target_hdr = to_uvcg_control_header(target);
+       ++target_hdr->linked;
+       class_array[0] = (struct uvc_descriptor_header *)&target_hdr->desc;
+       ret = 0;
+
+unlock:
+       mutex_unlock(&opts->lock);
+out:
+       mutex_unlock(su_mutex);
+       return ret;
+}
+
+static int uvcg_control_class_drop_link(struct config_item *src,
+                                       struct config_item *target)
+{
+       struct config_item *control, *header;
+       struct f_uvc_opts *opts;
+       struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+       struct uvc_descriptor_header **class_array;
+       struct uvcg_control_header *target_hdr;
+       int ret = -EINVAL;
+
+       mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+       control = src->ci_parent->ci_parent;
+       header = config_group_find_item(to_config_group(control), "header");
+       if (!header || target->ci_parent != header)
+               goto out;
+
+       opts = to_f_uvc_opts(control->ci_parent);
+
+       mutex_lock(&opts->lock);
+
+       class_array = uvcg_get_ctl_class_arr(src, opts);
+       if (!class_array)
+               goto unlock;
+       if (opts->refcnt) {
+               ret = -EBUSY;
+               goto unlock;
+       }
+
+       target_hdr = to_uvcg_control_header(target);
+       --target_hdr->linked;
+       class_array[0] = NULL;
+       ret = 0;
+
+unlock:
+       mutex_unlock(&opts->lock);
+out:
+       mutex_unlock(su_mutex);
+       return ret;
+}
+
+static struct configfs_item_operations uvcg_control_class_item_ops = {
+       .allow_link     = uvcg_control_class_allow_link,
+       .drop_link      = uvcg_control_class_drop_link,
+};
+
+static struct config_item_type uvcg_control_class_type = {
+       .ct_item_ops    = &uvcg_control_class_item_ops,
+       .ct_owner       = THIS_MODULE,
+};
+
+static struct config_group *uvcg_control_class_default_groups[] = {
+       &uvcg_control_class_fs.group,
+       &uvcg_control_class_ss.group,
+       NULL,
+};
+
+/* control/class */
+static struct uvcg_control_class_grp {
+       struct config_group     group;
+} uvcg_control_class_grp;
+
+static struct config_item_type uvcg_control_class_grp_type = {
+       .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *uvcg_control_default_groups[] = {
+       &uvcg_control_header_grp.group,
+       &uvcg_processing_grp.group,
+       &uvcg_terminal_grp.group,
+       &uvcg_control_class_grp.group,
+       NULL,
+};
+
+/* control */
+static struct uvcg_control_grp {
+       struct config_group     group;
+} uvcg_control_grp;
+
+static struct config_item_type uvcg_control_grp_type = {
+       .ct_owner = THIS_MODULE,
+};
+
+/* streaming/uncompressed */
+static struct uvcg_uncompressed_grp {
+       struct config_group     group;
+} uvcg_uncompressed_grp;
+
+/* streaming/mjpeg */
+static struct uvcg_mjpeg_grp {
+       struct config_group     group;
+} uvcg_mjpeg_grp;
+
+static struct config_item *fmt_parent[] = {
+       &uvcg_uncompressed_grp.group.cg_item,
+       &uvcg_mjpeg_grp.group.cg_item,
+};
+
+enum uvcg_format_type {
+       UVCG_UNCOMPRESSED = 0,
+       UVCG_MJPEG,
+};
+
+struct uvcg_format {
+       struct config_group     group;
+       enum uvcg_format_type   type;
+       unsigned                linked;
+       unsigned                num_frames;
+       __u8                    bmaControls[UVCG_STREAMING_CONTROL_SIZE];
+};
+
+struct uvcg_format *to_uvcg_format(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct uvcg_format, group);
+}
+
+static ssize_t uvcg_format_bma_controls_show(struct uvcg_format *f, char *page)
+{
+       struct f_uvc_opts *opts;
+       struct config_item *opts_item;
+       struct mutex *su_mutex = &f->group.cg_subsys->su_mutex;
+       int result, i;
+       char *pg = page;
+
+       mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+       opts_item = f->group.cg_item.ci_parent->ci_parent->ci_parent;
+       opts = to_f_uvc_opts(opts_item);
+
+       mutex_lock(&opts->lock);
+       result = sprintf(pg, "0x");
+       pg += result;
+       for (i = 0; i < UVCG_STREAMING_CONTROL_SIZE; ++i) {
+               result += sprintf(pg, "%x\n", f->bmaControls[i]);
+               pg = page + result;
+       }
+       mutex_unlock(&opts->lock);
+
+       mutex_unlock(su_mutex);
+       return result;
+}
+
+static ssize_t uvcg_format_bma_controls_store(struct uvcg_format *ch,
+                                             const char *page, size_t len)
+{
+       struct f_uvc_opts *opts;
+       struct config_item *opts_item;
+       struct mutex *su_mutex = &ch->group.cg_subsys->su_mutex;
+       int ret = -EINVAL;
+
+       mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+       opts_item = ch->group.cg_item.ci_parent->ci_parent->ci_parent;
+       opts = to_f_uvc_opts(opts_item);
+
+       mutex_lock(&opts->lock);
+       if (ch->linked || opts->refcnt) {
+               ret = -EBUSY;
+               goto end;
+       }
+
+       if (len < 4 || *page != '0' ||
+           (*(page + 1) != 'x' && *(page + 1) != 'X'))
+               goto end;
+       ret = hex2bin(ch->bmaControls, page + 2, 1);
+       if (ret < 0)
+               goto end;
+       ret = len;
+end:
+       mutex_unlock(&opts->lock);
+       mutex_unlock(su_mutex);
+       return ret;
+}
+
+struct uvcg_format_ptr {
+       struct uvcg_format      *fmt;
+       struct list_head        entry;
+};
+
+/* streaming/header/<NAME> */
+struct uvcg_streaming_header {
+       struct config_item                              item;
+       struct uvc_input_header_descriptor              desc;
+       unsigned                                        linked;
+       struct list_head                                formats;
+       unsigned                                        num_fmt;
+};
+
+struct uvcg_streaming_header *to_uvcg_streaming_header(struct config_item *item)
+{
+       return container_of(item, struct uvcg_streaming_header, item);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_streaming_header);
+CONFIGFS_ATTR_OPS(uvcg_streaming_header);
+
+static int uvcg_streaming_header_allow_link(struct config_item *src,
+                                           struct config_item *target)
+{
+       struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+       struct config_item *opts_item;
+       struct f_uvc_opts *opts;
+       struct uvcg_streaming_header *src_hdr;
+       struct uvcg_format *target_fmt = NULL;
+       struct uvcg_format_ptr *format_ptr;
+       int i, ret = -EINVAL;
+
+       src_hdr = to_uvcg_streaming_header(src);
+       mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+       opts_item = src->ci_parent->ci_parent->ci_parent;
+       opts = to_f_uvc_opts(opts_item);
+
+       mutex_lock(&opts->lock);
+
+       if (src_hdr->linked) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(fmt_parent); ++i)
+               if (target->ci_parent == fmt_parent[i])
+                       break;
+       if (i == ARRAY_SIZE(fmt_parent))
+               goto out;
+
+       target_fmt = container_of(to_config_group(target), struct uvcg_format,
+                                 group);
+       if (!target_fmt)
+               goto out;
+
+       format_ptr = kzalloc(sizeof(*format_ptr), GFP_KERNEL);
+       if (!format_ptr) {
+               ret = PTR_ERR(format_ptr);
+               goto out;
+       }
+       ret = 0;
+       format_ptr->fmt = target_fmt;
+       list_add_tail(&format_ptr->entry, &src_hdr->formats);
+       ++src_hdr->num_fmt;
+
+out:
+       mutex_unlock(&opts->lock);
+       mutex_unlock(su_mutex);
+       return ret;
+}
+
+static int uvcg_streaming_header_drop_link(struct config_item *src,
+                                          struct config_item *target)
+{
+       struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+       struct config_item *opts_item;
+       struct f_uvc_opts *opts;
+       struct uvcg_streaming_header *src_hdr;
+       struct uvcg_format *target_fmt = NULL;
+       struct uvcg_format_ptr *format_ptr, *tmp;
+       int ret = -EINVAL;
+
+       src_hdr = to_uvcg_streaming_header(src);
+       mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+       opts_item = src->ci_parent->ci_parent->ci_parent;
+       opts = to_f_uvc_opts(opts_item);
+
+       mutex_lock(&opts->lock);
+       target_fmt = container_of(to_config_group(target), struct uvcg_format,
+                                 group);
+       if (!target_fmt)
+               goto out;
+
+       list_for_each_entry_safe(format_ptr, tmp, &src_hdr->formats, entry)
+               if (format_ptr->fmt == target_fmt) {
+                       list_del(&format_ptr->entry);
+                       kfree(format_ptr);
+                       --src_hdr->num_fmt;
+                       break;
+               }
+
+out:
+       mutex_unlock(&opts->lock);
+       mutex_unlock(su_mutex);
+       return ret;
+
+}
+
+static struct configfs_item_operations uvcg_streaming_header_item_ops = {
+       .show_attribute         = uvcg_streaming_header_attr_show,
+       .store_attribute        = uvcg_streaming_header_attr_store,
+       .allow_link             = uvcg_streaming_header_allow_link,
+       .drop_link              = uvcg_streaming_header_drop_link,
+};
+
+#define UVCG_STREAMING_HEADER_ATTR(cname, aname, conv)                 \
+static ssize_t uvcg_streaming_header_##cname##_show(                   \
+       struct uvcg_streaming_header *sh, char *page)                   \
+{                                                                      \
+       struct f_uvc_opts *opts;                                        \
+       struct config_item *opts_item;                                  \
+       struct mutex *su_mutex = &sh->item.ci_group->cg_subsys->su_mutex;\
+       int result;                                                     \
+                                                                       \
+       mutex_lock(su_mutex); /* for navigating configfs hierarchy */   \
+                                                                       \
+       opts_item = sh->item.ci_parent->ci_parent->ci_parent;           \
+       opts = to_f_uvc_opts(opts_item);                                \
+                                                                       \
+       mutex_lock(&opts->lock);                                        \
+       result = sprintf(page, "%d\n", conv(sh->desc.aname));           \
+       mutex_unlock(&opts->lock);                                      \
+                                                                       \
+       mutex_unlock(su_mutex);                                         \
+       return result;                                                  \
+}                                                                      \
+                                                                       \
+static struct uvcg_streaming_header_attribute                          \
+       uvcg_streaming_header_##cname =                                 \
+       __CONFIGFS_ATTR_RO(aname, uvcg_streaming_header_##cname##_show)
+
+#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
+
+#undef UVCG_STREAMING_HEADER_ATTR
+
+static struct configfs_attribute *uvcg_streaming_header_attrs[] = {
+       &uvcg_streaming_header_bm_info.attr,
+       &uvcg_streaming_header_b_terminal_link.attr,
+       &uvcg_streaming_header_b_still_capture_method.attr,
+       &uvcg_streaming_header_b_trigger_support.attr,
+       &uvcg_streaming_header_b_trigger_usage.attr,
+       NULL,
+};
+
+struct config_item_type uvcg_streaming_header_type = {
+       .ct_item_ops    = &uvcg_streaming_header_item_ops,
+       .ct_attrs       = uvcg_streaming_header_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+static struct config_item
+*uvcg_streaming_header_make(struct config_group *group, const char *name)
+{
+       struct uvcg_streaming_header *h;
+
+       h = kzalloc(sizeof(*h), GFP_KERNEL);
+       if (!h)
+               return ERR_CAST(h);
+
+       INIT_LIST_HEAD(&h->formats);
+       h->desc.bDescriptorType         = USB_DT_CS_INTERFACE;
+       h->desc.bDescriptorSubType      = UVC_VS_INPUT_HEADER;
+       h->desc.bTerminalLink           = 3;
+       h->desc.bControlSize            = UVCG_STREAMING_CONTROL_SIZE;
+
+       config_item_init_type_name(&h->item, name, &uvcg_streaming_header_type);
+
+       return &h->item;
+}
+
+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 struct config_item_type uvcg_streaming_header_grp_type = {
+       .ct_group_ops   = &uvcg_streaming_header_grp_ops,
+       .ct_owner       = THIS_MODULE,
+};
+
+/* streaming/<mode>/<format>/<NAME> */
+struct uvcg_frame {
+       struct {
+               u8      b_length;
+               u8      b_descriptor_type;
+               u8      b_descriptor_subtype;
+               u8      b_frame_index;
+               u8      bm_capabilities;
+               u16     w_width;
+               u16     w_height;
+               u32     dw_min_bit_rate;
+               u32     dw_max_bit_rate;
+               u32     dw_max_video_frame_buffer_size;
+               u32     dw_default_frame_interval;
+               u8      b_frame_interval_type;
+       } __attribute__((packed)) frame;
+       u32 *dw_frame_interval;
+       enum uvcg_format_type   fmt_type;
+       struct config_item      item;
+};
+
+struct uvcg_frame *to_uvcg_frame(struct config_item *item)
+{
+       return container_of(item, struct uvcg_frame, item);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_frame);
+CONFIGFS_ATTR_OPS(uvcg_frame);
+
+static struct configfs_item_operations uvcg_frame_item_ops = {
+       .show_attribute         = uvcg_frame_attr_show,
+       .store_attribute        = uvcg_frame_attr_store,
+};
+
+#define UVCG_FRAME_ATTR(cname, aname, conv, str2u, uxx, vnoc, limit)   \
+static ssize_t uvcg_frame_##cname##_show(struct uvcg_frame *f, char *page)\
+{                                                                      \
+       struct f_uvc_opts *opts;                                        \
+       struct config_item *opts_item;                                  \
+       struct mutex *su_mutex = &f->item.ci_group->cg_subsys->su_mutex;\
+       int result;                                                     \
+                                                                       \
+       mutex_lock(su_mutex); /* for navigating configfs hierarchy */   \
+                                                                       \
+       opts_item = f->item.ci_parent->ci_parent->ci_parent->ci_parent; \
+       opts = to_f_uvc_opts(opts_item);                                \
+                                                                       \
+       mutex_lock(&opts->lock);                                        \
+       result = sprintf(page, "%d\n", conv(f->frame.cname));           \
+       mutex_unlock(&opts->lock);                                      \
+                                                                       \
+       mutex_unlock(su_mutex);                                         \
+       return result;                                                  \
+}                                                                      \
+                                                                       \
+static ssize_t  uvcg_frame_##cname##_store(struct uvcg_frame *f,       \
+                                          const char *page, size_t len)\
+{                                                                      \
+       struct f_uvc_opts *opts;                                        \
+       struct config_item *opts_item;                                  \
+       struct uvcg_format *fmt;                                        \
+       struct mutex *su_mutex = &f->item.ci_group->cg_subsys->su_mutex;\
+       int ret;                                                        \
+       uxx num;                                                        \
+                                                                       \
+       mutex_lock(su_mutex); /* for navigating configfs hierarchy */   \
+                                                                       \
+       opts_item = f->item.ci_parent->ci_parent->ci_parent->ci_parent; \
+       opts = to_f_uvc_opts(opts_item);                                \
+       fmt = to_uvcg_format(f->item.ci_parent);                        \
+                                                                       \
+       mutex_lock(&opts->lock);                                        \
+       if (fmt->linked || opts->refcnt) {                              \
+               ret = -EBUSY;                                           \
+               goto end;                                               \
+       }                                                               \
+                                                                       \
+       ret = str2u(page, 0, &num);                                     \
+       if (ret)                                                        \
+               goto end;                                               \
+                                                                       \
+       if (num > limit) {                                              \
+               ret = -EINVAL;                                          \
+               goto end;                                               \
+       }                                                               \
+       f->frame.cname = vnoc(num);                                     \
+       ret = len;                                                      \
+end:                                                                   \
+       mutex_unlock(&opts->lock);                                      \
+       mutex_unlock(su_mutex);                                         \
+       return ret;                                                     \
+}                                                                      \
+                                                                       \
+static struct uvcg_frame_attribute                                     \
+       uvcg_frame_##cname =                                            \
+       __CONFIGFS_ATTR(aname, S_IRUGO | S_IWUSR,                       \
+                       uvcg_frame_##cname##_show,                      \
+                       uvcg_frame_##cname##_store)
+
+#define identity_conv(x) (x)
+
+UVCG_FRAME_ATTR(bm_capabilities, bmCapabilities, identity_conv, kstrtou8, u8,
+               identity_conv, 0xFF);
+UVCG_FRAME_ATTR(w_width, wWidth, le16_to_cpu, kstrtou16, u16, cpu_to_le16,
+               0xFFFF);
+UVCG_FRAME_ATTR(w_height, wHeight, le16_to_cpu, kstrtou16, u16, cpu_to_le16,
+               0xFFFF);
+UVCG_FRAME_ATTR(dw_min_bit_rate, dwMinBitRate, le32_to_cpu, kstrtou32, u32,
+               cpu_to_le32, 0xFFFFFFFF);
+UVCG_FRAME_ATTR(dw_max_bit_rate, dwMaxBitRate, le32_to_cpu, kstrtou32, u32,
+               cpu_to_le32, 0xFFFFFFFF);
+UVCG_FRAME_ATTR(dw_max_video_frame_buffer_size, dwMaxVideoFrameBufferSize,
+               le32_to_cpu, kstrtou32, u32, cpu_to_le32, 0xFFFFFFFF);
+UVCG_FRAME_ATTR(dw_default_frame_interval, dwDefaultFrameInterval,
+               le32_to_cpu, kstrtou32, u32, cpu_to_le32, 0xFFFFFFFF);
+
+#undef identity_conv
+
+#undef UVCG_FRAME_ATTR
+
+static ssize_t uvcg_frame_dw_frame_interval_show(struct uvcg_frame *frm,
+                                                char *page)
+{
+       struct f_uvc_opts *opts;
+       struct config_item *opts_item;
+       struct mutex *su_mutex = &frm->item.ci_group->cg_subsys->su_mutex;
+       int result, i;
+       char *pg = page;
+
+       mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+       opts_item = frm->item.ci_parent->ci_parent->ci_parent->ci_parent;
+       opts = to_f_uvc_opts(opts_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]));
+               pg = page + result;
+       }
+       mutex_unlock(&opts->lock);
+
+       mutex_unlock(su_mutex);
+       return result;
+}
+
+static inline int __uvcg_count_frm_intrv(char *buf, void *priv)
+{
+       ++*((int *)priv);
+       return 0;
+}
+
+static inline int __uvcg_fill_frm_intrv(char *buf, void *priv)
+{
+       u32 num, **interv;
+       int ret;
+
+       ret = kstrtou32(buf, 0, &num);
+       if (ret)
+               return ret;
+       if (num > 0xFFFFFFFF)
+               return -EINVAL;
+
+       interv = priv;
+       **interv = cpu_to_le32(num);
+       ++*interv;
+
+       return 0;
+}
+
+static int __uvcg_iter_frm_intrv(const char *page, size_t len,
+                                int (*fun)(char *, void *), void *priv)
+{
+       /* sign, base 2 representation, newline, terminator */
+       char buf[1 + sizeof(u32) * 8 + 1 + 1];
+       const char *pg = page;
+       int i, ret;
+
+       if (!fun)
+               return -EINVAL;
+
+       while (pg - page < len) {
+               i = 0;
+               while (i < sizeof(buf) && (pg - page < len) &&
+                               *pg != '\0' && *pg != '\n')
+                       buf[i++] = *pg++;
+               if (i == sizeof(buf))
+                       return -EINVAL;
+               while ((pg - page < len) && (*pg == '\0' || *pg == '\n'))
+                       ++pg;
+               buf[i] = '\0';
+               ret = fun(buf, priv);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static ssize_t uvcg_frame_dw_frame_interval_store(struct uvcg_frame *ch,
+                                                 const char *page, size_t len)
+{
+       struct f_uvc_opts *opts;
+       struct config_item *opts_item;
+       struct uvcg_format *fmt;
+       struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex;
+       int ret = 0, n = 0;
+       u32 *frm_intrv, *tmp;
+
+       mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+       opts_item = ch->item.ci_parent->ci_parent->ci_parent->ci_parent;
+       opts = to_f_uvc_opts(opts_item);
+       fmt = to_uvcg_format(ch->item.ci_parent);
+
+       mutex_lock(&opts->lock);
+       if (fmt->linked || opts->refcnt) {
+               ret = -EBUSY;
+               goto end;
+       }
+
+       ret = __uvcg_iter_frm_intrv(page, len, __uvcg_count_frm_intrv, &n);
+       if (ret)
+               goto end;
+
+       tmp = frm_intrv = kcalloc(n, sizeof(u32), GFP_KERNEL);
+       if (!frm_intrv) {
+               ret = -ENOMEM;
+               goto end;
+       }
+
+       ret = __uvcg_iter_frm_intrv(page, len, __uvcg_fill_frm_intrv, &tmp);
+       if (ret) {
+               kfree(frm_intrv);
+               goto end;
+       }
+
+       kfree(ch->dw_frame_interval);
+       ch->dw_frame_interval = frm_intrv;
+       ch->frame.b_frame_interval_type = n;
+       ret = len;
+
+end:
+       mutex_unlock(&opts->lock);
+       mutex_unlock(su_mutex);
+       return ret;
+}
+
+static struct uvcg_frame_attribute
+       uvcg_frame_dw_frame_interval =
+       __CONFIGFS_ATTR(dwFrameInterval, S_IRUGO | S_IWUSR,
+                       uvcg_frame_dw_frame_interval_show,
+                       uvcg_frame_dw_frame_interval_store);
+
+static struct configfs_attribute *uvcg_frame_attrs[] = {
+       &uvcg_frame_bm_capabilities.attr,
+       &uvcg_frame_w_width.attr,
+       &uvcg_frame_w_height.attr,
+       &uvcg_frame_dw_min_bit_rate.attr,
+       &uvcg_frame_dw_max_bit_rate.attr,
+       &uvcg_frame_dw_max_video_frame_buffer_size.attr,
+       &uvcg_frame_dw_default_frame_interval.attr,
+       &uvcg_frame_dw_frame_interval.attr,
+       NULL,
+};
+
+struct config_item_type uvcg_frame_type = {
+       .ct_item_ops    = &uvcg_frame_item_ops,
+       .ct_attrs       = uvcg_frame_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+static struct config_item *uvcg_frame_make(struct config_group *group,
+                                          const char *name)
+{
+       struct uvcg_frame *h;
+       struct uvcg_format *fmt;
+       struct f_uvc_opts *opts;
+       struct config_item *opts_item;
+
+       h = kzalloc(sizeof(*h), GFP_KERNEL);
+       if (!h)
+               return ERR_CAST(h);
+
+       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);
+
+       opts_item = group->cg_item.ci_parent->ci_parent->ci_parent;
+       opts = to_f_uvc_opts(opts_item);
+
+       mutex_lock(&opts->lock);
+       fmt = to_uvcg_format(&group->cg_item);
+       if (fmt->type == UVCG_UNCOMPRESSED) {
+               h->frame.b_descriptor_subtype = UVC_VS_FRAME_UNCOMPRESSED;
+               h->fmt_type = UVCG_UNCOMPRESSED;
+       } else if (fmt->type == UVCG_MJPEG) {
+               h->frame.b_descriptor_subtype = UVC_VS_FRAME_MJPEG;
+               h->fmt_type = UVCG_MJPEG;
+       } else {
+               mutex_unlock(&opts->lock);
+               return ERR_PTR(-EINVAL);
+       }
+       ++fmt->num_frames;
+       mutex_unlock(&opts->lock);
+
+       config_item_init_type_name(&h->item, name, &uvcg_frame_type);
+
+       return &h->item;
+}
+
+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;
+
+       opts_item = group->cg_item.ci_parent->ci_parent->ci_parent;
+       opts = to_f_uvc_opts(opts_item);
+
+       mutex_lock(&opts->lock);
+       fmt = to_uvcg_format(&group->cg_item);
+       --fmt->num_frames;
+       kfree(h);
+       mutex_unlock(&opts->lock);
+}
+
+/* streaming/uncompressed/<NAME> */
+struct uvcg_uncompressed {
+       struct uvcg_format              fmt;
+       struct uvc_format_uncompressed  desc;
+};
+
+struct uvcg_uncompressed *to_uvcg_uncompressed(struct config_item *item)
+{
+       return container_of(
+               container_of(to_config_group(item), struct uvcg_format, group),
+               struct uvcg_uncompressed, fmt);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_uncompressed);
+CONFIGFS_ATTR_OPS(uvcg_uncompressed);
+
+static struct configfs_item_operations uvcg_uncompressed_item_ops = {
+       .show_attribute         = uvcg_uncompressed_attr_show,
+       .store_attribute        = uvcg_uncompressed_attr_store,
+};
+
+static struct configfs_group_operations uvcg_uncompressed_group_ops = {
+       .make_item              = uvcg_frame_make,
+       .drop_item              = uvcg_frame_drop,
+};
+
+static ssize_t uvcg_uncompressed_guid_format_show(struct uvcg_uncompressed *ch,
+                                                       char *page)
+{
+       struct f_uvc_opts *opts;
+       struct config_item *opts_item;
+       struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex;
+
+       mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+       opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;
+       opts = to_f_uvc_opts(opts_item);
+
+       mutex_lock(&opts->lock);
+       memcpy(page, ch->desc.guidFormat, sizeof(ch->desc.guidFormat));
+       mutex_unlock(&opts->lock);
+
+       mutex_unlock(su_mutex);
+
+       return sizeof(ch->desc.guidFormat);
+}
+
+static ssize_t uvcg_uncompressed_guid_format_store(struct uvcg_uncompressed *ch,
+                                                  const char *page, size_t len)
+{
+       struct f_uvc_opts *opts;
+       struct config_item *opts_item;
+       struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex;
+       int ret;
+
+       mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+       opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;
+       opts = to_f_uvc_opts(opts_item);
+
+       mutex_lock(&opts->lock);
+       if (ch->fmt.linked || opts->refcnt) {
+               ret = -EBUSY;
+               goto end;
+       }
+
+       memcpy(ch->desc.guidFormat, page,
+              min(sizeof(ch->desc.guidFormat), len));
+       ret = sizeof(ch->desc.guidFormat);
+
+end:
+       mutex_unlock(&opts->lock);
+       mutex_unlock(su_mutex);
+       return ret;
+}
+
+static struct uvcg_uncompressed_attribute uvcg_uncompressed_guid_format =
+       __CONFIGFS_ATTR(guidFormat, S_IRUGO | S_IWUSR,
+                       uvcg_uncompressed_guid_format_show,
+                       uvcg_uncompressed_guid_format_store);
+
+
+#define UVCG_UNCOMPRESSED_ATTR_RO(cname, aname, conv)                  \
+static ssize_t uvcg_uncompressed_##cname##_show(                       \
+       struct uvcg_uncompressed *u, char *page)                        \
+{                                                                      \
+       struct f_uvc_opts *opts;                                        \
+       struct config_item *opts_item;                                  \
+       struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex;     \
+       int result;                                                     \
+                                                                       \
+       mutex_lock(su_mutex); /* for navigating configfs hierarchy */   \
+                                                                       \
+       opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+       opts = to_f_uvc_opts(opts_item);                                \
+                                                                       \
+       mutex_lock(&opts->lock);                                        \
+       result = sprintf(page, "%d\n", conv(u->desc.aname));            \
+       mutex_unlock(&opts->lock);                                      \
+                                                                       \
+       mutex_unlock(su_mutex);                                         \
+       return result;                                                  \
+}                                                                      \
+                                                                       \
+static struct uvcg_uncompressed_attribute                              \
+       uvcg_uncompressed_##cname =                                     \
+       __CONFIGFS_ATTR_RO(aname, uvcg_uncompressed_##cname##_show)
+
+#define UVCG_UNCOMPRESSED_ATTR(cname, aname, conv)                     \
+static ssize_t uvcg_uncompressed_##cname##_show(                       \
+       struct uvcg_uncompressed *u, char *page)                        \
+{                                                                      \
+       struct f_uvc_opts *opts;                                        \
+       struct config_item *opts_item;                                  \
+       struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex;     \
+       int result;                                                     \
+                                                                       \
+       mutex_lock(su_mutex); /* for navigating configfs hierarchy */   \
+                                                                       \
+       opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+       opts = to_f_uvc_opts(opts_item);                                \
+                                                                       \
+       mutex_lock(&opts->lock);                                        \
+       result = sprintf(page, "%d\n", conv(u->desc.aname));            \
+       mutex_unlock(&opts->lock);                                      \
+                                                                       \
+       mutex_unlock(su_mutex);                                         \
+       return result;                                                  \
+}                                                                      \
+                                                                       \
+static ssize_t                                                         \
+uvcg_uncompressed_##cname##_store(struct uvcg_uncompressed *u,         \
+                                   const char *page, size_t len)       \
+{                                                                      \
+       struct f_uvc_opts *opts;                                        \
+       struct config_item *opts_item;                                  \
+       struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex;     \
+       int ret;                                                        \
+       u8 num;                                                         \
+                                                                       \
+       mutex_lock(su_mutex); /* for navigating configfs hierarchy */   \
+                                                                       \
+       opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+       opts = to_f_uvc_opts(opts_item);                                \
+                                                                       \
+       mutex_lock(&opts->lock);                                        \
+       if (u->fmt.linked || opts->refcnt) {                            \
+               ret = -EBUSY;                                           \
+               goto end;                                               \
+       }                                                               \
+                                                                       \
+       ret = kstrtou8(page, 0, &num);                                  \
+       if (ret)                                                        \
+               goto end;                                               \
+                                                                       \
+       if (num > 255) {                                                \
+               ret = -EINVAL;                                          \
+               goto end;                                               \
+       }                                                               \
+       u->desc.aname = num;                                            \
+       ret = len;                                                      \
+end:                                                                   \
+       mutex_unlock(&opts->lock);                                      \
+       mutex_unlock(su_mutex);                                         \
+       return ret;                                                     \
+}                                                                      \
+                                                                       \
+static struct uvcg_uncompressed_attribute                              \
+       uvcg_uncompressed_##cname =                                     \
+       __CONFIGFS_ATTR(aname, S_IRUGO | S_IWUSR,                       \
+                       uvcg_uncompressed_##cname##_show,               \
+                       uvcg_uncompressed_##cname##_store)
+
+#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
+
+#undef UVCG_UNCOMPRESSED_ATTR
+#undef UVCG_UNCOMPRESSED_ATTR_RO
+
+static inline ssize_t
+uvcg_uncompressed_bma_controls_show(struct uvcg_uncompressed *unc, char *page)
+{
+       return uvcg_format_bma_controls_show(&unc->fmt, page);
+}
+
+static inline ssize_t
+uvcg_uncompressed_bma_controls_store(struct uvcg_uncompressed *ch,
+                                    const char *page, size_t len)
+{
+       return uvcg_format_bma_controls_store(&ch->fmt, page, len);
+}
+
+static struct uvcg_uncompressed_attribute uvcg_uncompressed_bma_controls =
+       __CONFIGFS_ATTR(bmaControls, S_IRUGO | S_IWUSR,
+                       uvcg_uncompressed_bma_controls_show,
+                       uvcg_uncompressed_bma_controls_store);
+
+static struct configfs_attribute *uvcg_uncompressed_attrs[] = {
+       &uvcg_uncompressed_guid_format.attr,
+       &uvcg_uncompressed_b_bits_per_pixel.attr,
+       &uvcg_uncompressed_b_default_frame_index.attr,
+       &uvcg_uncompressed_b_aspect_ratio_x.attr,
+       &uvcg_uncompressed_b_aspect_ratio_y.attr,
+       &uvcg_uncompressed_bm_interface_flags.attr,
+       &uvcg_uncompressed_bma_controls.attr,
+       NULL,
+};
+
+struct config_item_type uvcg_uncompressed_type = {
+       .ct_item_ops    = &uvcg_uncompressed_item_ops,
+       .ct_group_ops   = &uvcg_uncompressed_group_ops,
+       .ct_attrs       = uvcg_uncompressed_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+static struct config_group *uvcg_uncompressed_make(struct config_group *group,
+                                                  const char *name)
+{
+       static char guid[] = {
+               'Y',  'U',  'Y',  '2', 0x00, 0x00, 0x10, 0x00,
+                0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
+       };
+       struct uvcg_uncompressed *h;
+
+       h = kzalloc(sizeof(*h), GFP_KERNEL);
+       if (!h)
+               return ERR_CAST(h);
+
+       h->desc.bLength                 = UVC_DT_FORMAT_UNCOMPRESSED_SIZE;
+       h->desc.bDescriptorType         = USB_DT_CS_INTERFACE;
+       h->desc.bDescriptorSubType      = UVC_VS_FORMAT_UNCOMPRESSED;
+       memcpy(h->desc.guidFormat, guid, sizeof(guid));
+       h->desc.bBitsPerPixel           = 16;
+       h->desc.bDefaultFrameIndex      = 1;
+       h->desc.bAspectRatioX           = 0;
+       h->desc.bAspectRatioY           = 0;
+       h->desc.bmInterfaceFlags        = 0;
+       h->desc.bCopyProtect            = 0;
+
+       h->fmt.type = UVCG_UNCOMPRESSED;
+       config_group_init_type_name(&h->fmt.group, name,
+                                   &uvcg_uncompressed_type);
+
+       return &h->fmt.group;
+}
+
+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 struct config_item_type uvcg_uncompressed_grp_type = {
+       .ct_group_ops   = &uvcg_uncompressed_grp_ops,
+       .ct_owner       = THIS_MODULE,
+};
+
+/* streaming/mjpeg/<NAME> */
+struct uvcg_mjpeg {
+       struct uvcg_format              fmt;
+       struct uvc_format_mjpeg         desc;
+};
+
+struct uvcg_mjpeg *to_uvcg_mjpeg(struct config_item *item)
+{
+       return container_of(
+               container_of(to_config_group(item), struct uvcg_format, group),
+               struct uvcg_mjpeg, fmt);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_mjpeg);
+CONFIGFS_ATTR_OPS(uvcg_mjpeg);
+
+static struct configfs_item_operations uvcg_mjpeg_item_ops = {
+       .show_attribute         = uvcg_mjpeg_attr_show,
+       .store_attribute        = uvcg_mjpeg_attr_store,
+};
+
+static struct configfs_group_operations uvcg_mjpeg_group_ops = {
+       .make_item              = uvcg_frame_make,
+       .drop_item              = uvcg_frame_drop,
+};
+
+#define UVCG_MJPEG_ATTR_RO(cname, aname, conv)                         \
+static ssize_t uvcg_mjpeg_##cname##_show(struct uvcg_mjpeg *u, char *page)\
+{                                                                      \
+       struct f_uvc_opts *opts;                                        \
+       struct config_item *opts_item;                                  \
+       struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex;     \
+       int result;                                                     \
+                                                                       \
+       mutex_lock(su_mutex); /* for navigating configfs hierarchy */   \
+                                                                       \
+       opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+       opts = to_f_uvc_opts(opts_item);                                \
+                                                                       \
+       mutex_lock(&opts->lock);                                        \
+       result = sprintf(page, "%d\n", conv(u->desc.aname));            \
+       mutex_unlock(&opts->lock);                                      \
+                                                                       \
+       mutex_unlock(su_mutex);                                         \
+       return result;                                                  \
+}                                                                      \
+                                                                       \
+static struct uvcg_mjpeg_attribute                                     \
+       uvcg_mjpeg_##cname =                                            \
+       __CONFIGFS_ATTR_RO(aname, uvcg_mjpeg_##cname##_show)
+
+#define UVCG_MJPEG_ATTR(cname, aname, conv)                            \
+static ssize_t uvcg_mjpeg_##cname##_show(struct uvcg_mjpeg *u, char *page)\
+{                                                                      \
+       struct f_uvc_opts *opts;                                        \
+       struct config_item *opts_item;                                  \
+       struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex;     \
+       int result;                                                     \
+                                                                       \
+       mutex_lock(su_mutex); /* for navigating configfs hierarchy */   \
+                                                                       \
+       opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+       opts = to_f_uvc_opts(opts_item);                                \
+                                                                       \
+       mutex_lock(&opts->lock);                                        \
+       result = sprintf(page, "%d\n", conv(u->desc.aname));            \
+       mutex_unlock(&opts->lock);                                      \
+                                                                       \
+       mutex_unlock(su_mutex);                                         \
+       return result;                                                  \
+}                                                                      \
+                                                                       \
+static ssize_t                                                         \
+uvcg_mjpeg_##cname##_store(struct uvcg_mjpeg *u,                       \
+                          const char *page, size_t len)                \
+{                                                                      \
+       struct f_uvc_opts *opts;                                        \
+       struct config_item *opts_item;                                  \
+       struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex;     \
+       int ret;                                                        \
+       u8 num;                                                         \
+                                                                       \
+       mutex_lock(su_mutex); /* for navigating configfs hierarchy */   \
+                                                                       \
+       opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+       opts = to_f_uvc_opts(opts_item);                                \
+                                                                       \
+       mutex_lock(&opts->lock);                                        \
+       if (u->fmt.linked || opts->refcnt) {                            \
+               ret = -EBUSY;                                           \
+               goto end;                                               \
+       }                                                               \
+                                                                       \
+       ret = kstrtou8(page, 0, &num);                                  \
+       if (ret)                                                        \
+               goto end;                                               \
+                                                                       \
+       if (num > 255) {                                                \
+               ret = -EINVAL;                                          \
+               goto end;                                               \
+       }                                                               \
+       u->desc.aname = num;                                            \
+       ret = len;                                                      \
+end:                                                                   \
+       mutex_unlock(&opts->lock);                                      \
+       mutex_unlock(su_mutex);                                         \
+       return ret;                                                     \
+}                                                                      \
+                                                                       \
+static struct uvcg_mjpeg_attribute                                     \
+       uvcg_mjpeg_##cname =                                            \
+       __CONFIGFS_ATTR(aname, S_IRUGO | S_IWUSR,                       \
+                       uvcg_mjpeg_##cname##_show,                      \
+                       uvcg_mjpeg_##cname##_store)
+
+#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
+
+#undef UVCG_MJPEG_ATTR
+#undef UVCG_MJPEG_ATTR_RO
+
+static inline ssize_t
+uvcg_mjpeg_bma_controls_show(struct uvcg_mjpeg *unc, char *page)
+{
+       return uvcg_format_bma_controls_show(&unc->fmt, page);
+}
+
+static inline ssize_t
+uvcg_mjpeg_bma_controls_store(struct uvcg_mjpeg *ch,
+                                    const char *page, size_t len)
+{
+       return uvcg_format_bma_controls_store(&ch->fmt, page, len);
+}
+
+static struct uvcg_mjpeg_attribute uvcg_mjpeg_bma_controls =
+       __CONFIGFS_ATTR(bmaControls, S_IRUGO | S_IWUSR,
+                       uvcg_mjpeg_bma_controls_show,
+                       uvcg_mjpeg_bma_controls_store);
+
+static struct configfs_attribute *uvcg_mjpeg_attrs[] = {
+       &uvcg_mjpeg_b_default_frame_index.attr,
+       &uvcg_mjpeg_bm_flags.attr,
+       &uvcg_mjpeg_b_aspect_ratio_x.attr,
+       &uvcg_mjpeg_b_aspect_ratio_y.attr,
+       &uvcg_mjpeg_bm_interface_flags.attr,
+       &uvcg_mjpeg_bma_controls.attr,
+       NULL,
+};
+
+struct config_item_type uvcg_mjpeg_type = {
+       .ct_item_ops    = &uvcg_mjpeg_item_ops,
+       .ct_group_ops   = &uvcg_mjpeg_group_ops,
+       .ct_attrs       = uvcg_mjpeg_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+static struct config_group *uvcg_mjpeg_make(struct config_group *group,
+                                                  const char *name)
+{
+       struct uvcg_mjpeg *h;
+
+       h = kzalloc(sizeof(*h), GFP_KERNEL);
+       if (!h)
+               return ERR_CAST(h);
+
+       h->desc.bLength                 = UVC_DT_FORMAT_MJPEG_SIZE;
+       h->desc.bDescriptorType         = USB_DT_CS_INTERFACE;
+       h->desc.bDescriptorSubType      = UVC_VS_FORMAT_MJPEG;
+       h->desc.bDefaultFrameIndex      = 1;
+       h->desc.bAspectRatioX           = 0;
+       h->desc.bAspectRatioY           = 0;
+       h->desc.bmInterfaceFlags        = 0;
+       h->desc.bCopyProtect            = 0;
+
+       h->fmt.type = UVCG_MJPEG;
+       config_group_init_type_name(&h->fmt.group, name,
+                                   &uvcg_mjpeg_type);
+
+       return &h->fmt.group;
+}
+
+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 struct config_item_type uvcg_mjpeg_grp_type = {
+       .ct_group_ops   = &uvcg_mjpeg_grp_ops,
+       .ct_owner       = THIS_MODULE,
+};
+
+/* 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);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_default_color_matching);
+CONFIGFS_ATTR_OPS_RO(uvcg_default_color_matching);
+
+static struct configfs_item_operations uvcg_default_color_matching_item_ops = {
+       .show_attribute         = uvcg_default_color_matching_attr_show,
+};
+
+#define UVCG_DEFAULT_COLOR_MATCHING_ATTR(cname, aname, conv)           \
+static ssize_t uvcg_default_color_matching_##cname##_show(             \
+       struct uvcg_default_color_matching *dc, char *page)             \
+{                                                                      \
+       struct f_uvc_opts *opts;                                        \
+       struct config_item *opts_item;                                  \
+       struct mutex *su_mutex = &dc->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 = to_f_uvc_opts(opts_item);                                \
+       cd = &opts->uvc_color_matching;                                 \
+                                                                       \
+       mutex_lock(&opts->lock);                                        \
+       result = sprintf(page, "%d\n", conv(cd->aname));                \
+       mutex_unlock(&opts->lock);                                      \
+                                                                       \
+       mutex_unlock(su_mutex);                                         \
+       return result;                                                  \
+}                                                                      \
+                                                                       \
+static struct uvcg_default_color_matching_attribute                    \
+       uvcg_default_color_matching_##cname =                           \
+       __CONFIGFS_ATTR_RO(aname, uvcg_default_color_matching_##cname##_show)
+
+#define identity_conv(x) (x)
+
+UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_color_primaries, bColorPrimaries,
+                                identity_conv);
+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
+
+#undef UVCG_DEFAULT_COLOR_MATCHING_ATTR
+
+static struct configfs_attribute *uvcg_default_color_matching_attrs[] = {
+       &uvcg_default_color_matching_b_color_primaries.attr,
+       &uvcg_default_color_matching_b_transfer_characteristics.attr,
+       &uvcg_default_color_matching_b_matrix_coefficients.attr,
+       NULL,
+};
+
+static struct config_item_type uvcg_default_color_matching_type = {
+       .ct_item_ops    = &uvcg_default_color_matching_item_ops,
+       .ct_attrs       = uvcg_default_color_matching_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+/* struct uvcg_color_matching {}; */
+
+static struct config_group *uvcg_color_matching_default_groups[] = {
+       &uvcg_default_color_matching.group,
+       NULL,
+};
+
+/* streaming/color_matching */
+static struct uvcg_color_matching_grp {
+       struct config_group     group;
+} uvcg_color_matching_grp;
+
+static struct config_item_type uvcg_color_matching_grp_type = {
+       .ct_owner = THIS_MODULE,
+};
+
+/* 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;
+
+
+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);
+
+       if (cl == &uvcg_streaming_class_fs)
+               return &o->uvc_fs_streaming_cls;
+
+       if (cl == &uvcg_streaming_class_hs)
+               return &o->uvc_hs_streaming_cls;
+
+       if (cl == &uvcg_streaming_class_ss)
+               return &o->uvc_ss_streaming_cls;
+
+       return NULL;
+}
+
+enum uvcg_strm_type {
+       UVCG_HEADER = 0,
+       UVCG_FORMAT,
+       UVCG_FRAME
+};
+
+static int __uvcg_iter_strm_cls(void *priv1, void *priv2, void *priv3,
+       int (*fun)(void *, void *, void *, int, enum uvcg_strm_type type))
+{
+       struct uvcg_streaming_header *h = priv1;
+       struct uvcg_format_ptr *f;
+       struct config_group *grp;
+       struct config_item *item;
+       struct uvcg_frame *frm;
+       int ret, i, j;
+
+       if (!fun)
+               return -EINVAL;
+
+       i = j = 0;
+       ret = fun(h, priv2, priv3, 0, UVCG_HEADER);
+       if (ret)
+               return ret;
+       list_for_each_entry(f, &h->formats, entry) {
+               ret = fun(f->fmt, priv2, priv3, i++, UVCG_FORMAT);
+               if (ret)
+                       return ret;
+               grp = &f->fmt->group;
+               list_for_each_entry(item, &grp->cg_children, ci_entry) {
+                       frm = to_uvcg_frame(item);
+                       ret = fun(frm, priv2, priv3, j++, UVCG_FRAME);
+                       if (ret)
+                               return ret;
+               }
+       }
+
+       return ret;
+}
+
+static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n,
+                          enum uvcg_strm_type type)
+{
+       size_t *size = priv2;
+       size_t *count = priv3;
+
+       switch (type) {
+       case UVCG_HEADER: {
+               struct uvcg_streaming_header *h = priv1;
+
+               *size += sizeof(h->desc);
+               /* bmaControls */
+               *size += h->num_fmt * UVCG_STREAMING_CONTROL_SIZE;
+       }
+       break;
+       case UVCG_FORMAT: {
+               struct uvcg_format *fmt = priv1;
+
+               if (fmt->type == UVCG_UNCOMPRESSED) {
+                       struct uvcg_uncompressed *u =
+                               container_of(fmt, struct uvcg_uncompressed,
+                                            fmt);
+
+                       *size += sizeof(u->desc);
+               } else if (fmt->type == UVCG_MJPEG) {
+                       struct uvcg_mjpeg *m =
+                               container_of(fmt, struct uvcg_mjpeg, fmt);
+
+                       *size += sizeof(m->desc);
+               } else {
+                       return -EINVAL;
+               }
+       }
+       break;
+       case UVCG_FRAME: {
+               struct uvcg_frame *frm = priv1;
+               int sz = sizeof(frm->dw_frame_interval);
+
+               *size += sizeof(frm->frame);
+               *size += frm->frame.b_frame_interval_type * sz;
+       }
+       break;
+       }
+
+       ++*count;
+
+       return 0;
+}
+
+static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n,
+                           enum uvcg_strm_type type)
+{
+       void **dest = priv2;
+       struct uvc_descriptor_header ***array = priv3;
+       size_t sz;
+
+       **array = *dest;
+       ++*array;
+
+       switch (type) {
+       case UVCG_HEADER: {
+               struct uvc_input_header_descriptor *ihdr = *dest;
+               struct uvcg_streaming_header *h = priv1;
+               struct uvcg_format_ptr *f;
+
+               memcpy(*dest, &h->desc, sizeof(h->desc));
+               *dest += sizeof(h->desc);
+               sz = UVCG_STREAMING_CONTROL_SIZE;
+               list_for_each_entry(f, &h->formats, entry) {
+                       memcpy(*dest, f->fmt->bmaControls, sz);
+                       *dest += sz;
+               }
+               ihdr->bLength = sizeof(h->desc) + h->num_fmt * sz;
+               ihdr->bNumFormats = h->num_fmt;
+       }
+       break;
+       case UVCG_FORMAT: {
+               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);
+
+                       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);
+
+                       memcpy(*dest, &m->desc, sizeof(m->desc));
+                       *dest += sizeof(m->desc);
+                       mjp->bNumFrameDescriptors = fmt->num_frames;
+                       mjp->bFormatIndex = n + 1;
+               } else {
+                       return -EINVAL;
+               }
+       }
+       break;
+       case UVCG_FRAME: {
+               struct uvcg_frame *frm = priv1;
+               struct uvc_descriptor_header *h = *dest;
+
+               sz = sizeof(frm->frame);
+               memcpy(*dest, &frm->frame, sz);
+               *dest += sz;
+               sz = frm->frame.b_frame_interval_type *
+                       sizeof(*frm->dw_frame_interval);
+               memcpy(*dest, frm->dw_frame_interval, sz);
+               *dest += sz;
+               if (frm->fmt_type == UVCG_UNCOMPRESSED)
+                       h->bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(
+                               frm->frame.b_frame_interval_type);
+               else if (frm->fmt_type == UVCG_MJPEG)
+                       h->bLength = UVC_DT_FRAME_MJPEG_SIZE(
+                               frm->frame.b_frame_interval_type);
+       }
+       break;
+       }
+
+       return 0;
+}
+
+static int uvcg_streaming_class_allow_link(struct config_item *src,
+                                          struct config_item *target)
+{
+       struct config_item *streaming, *header;
+       struct f_uvc_opts *opts;
+       struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+       struct uvc_descriptor_header ***class_array, **cl_arr;
+       struct uvcg_streaming_header *target_hdr;
+       void *data;
+       size_t size = 0, count = 0;
+       int ret = -EINVAL;
+
+       mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+       streaming = src->ci_parent->ci_parent;
+       header = config_group_find_item(to_config_group(streaming), "header");
+       if (!header || target->ci_parent != header)
+               goto out;
+
+       opts = to_f_uvc_opts(streaming->ci_parent);
+
+       mutex_lock(&opts->lock);
+
+       class_array = __uvcg_get_stream_class_arr(src, opts);
+       if (!class_array || *class_array || opts->refcnt) {
+               ret = -EBUSY;
+               goto unlock;
+       }
+
+       target_hdr = to_uvcg_streaming_header(target);
+       ret = __uvcg_iter_strm_cls(target_hdr, &size, &count, __uvcg_cnt_strm);
+       if (ret)
+               goto unlock;
+
+       count += 2; /* color_matching, NULL */
+       *class_array = kcalloc(count, sizeof(void *), GFP_KERNEL);
+       if (!*class_array) {
+               ret = PTR_ERR(*class_array);
+               goto unlock;
+       }
+
+       data = kzalloc(size, GFP_KERNEL);
+       if (!data) {
+               kfree(*class_array);
+               *class_array = NULL;
+               ret = PTR_ERR(data);
+               goto unlock;
+       }
+       cl_arr = *class_array;
+       ret = __uvcg_iter_strm_cls(target_hdr, &data, &cl_arr,
+                                  __uvcg_fill_strm);
+       if (ret) {
+               kfree(*class_array);
+               *class_array = NULL;
+               kfree(data);
+               goto unlock;
+       }
+       *cl_arr = (struct uvc_descriptor_header *)&opts->uvc_color_matching;
+
+       ++target_hdr->linked;
+       ret = 0;
+
+unlock:
+       mutex_unlock(&opts->lock);
+out:
+       mutex_unlock(su_mutex);
+       return ret;
+}
+
+static int uvcg_streaming_class_drop_link(struct config_item *src,
+                                         struct config_item *target)
+{
+       struct config_item *streaming, *header;
+       struct f_uvc_opts *opts;
+       struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+       struct uvc_descriptor_header ***class_array;
+       struct uvcg_streaming_header *target_hdr;
+       int ret = -EINVAL;
+
+       mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+       streaming = src->ci_parent->ci_parent;
+       header = config_group_find_item(to_config_group(streaming), "header");
+       if (!header || target->ci_parent != header)
+               goto out;
+
+       opts = to_f_uvc_opts(streaming->ci_parent);
+
+       mutex_lock(&opts->lock);
+
+       class_array = __uvcg_get_stream_class_arr(src, opts);
+       if (!class_array || !*class_array)
+               goto unlock;
+
+       if (opts->refcnt) {
+               ret = -EBUSY;
+               goto unlock;
+       }
+
+       target_hdr = to_uvcg_streaming_header(target);
+       --target_hdr->linked;
+       kfree(**class_array);
+       kfree(*class_array);
+       *class_array = NULL;
+       ret = 0;
+
+unlock:
+       mutex_unlock(&opts->lock);
+out:
+       mutex_unlock(su_mutex);
+       return ret;
+}
+
+static struct configfs_item_operations uvcg_streaming_class_item_ops = {
+       .allow_link     = uvcg_streaming_class_allow_link,
+       .drop_link      = uvcg_streaming_class_drop_link,
+};
+
+static struct config_item_type uvcg_streaming_class_type = {
+       .ct_item_ops    = &uvcg_streaming_class_item_ops,
+       .ct_owner       = THIS_MODULE,
+};
+
+static struct config_group *uvcg_streaming_class_default_groups[] = {
+       &uvcg_streaming_class_fs.group,
+       &uvcg_streaming_class_hs.group,
+       &uvcg_streaming_class_ss.group,
+       NULL,
+};
+
+/* streaming/class */
+static struct uvcg_streaming_class_grp {
+       struct config_group     group;
+} uvcg_streaming_class_grp;
+
+static struct config_item_type uvcg_streaming_class_grp_type = {
+       .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *uvcg_streaming_default_groups[] = {
+       &uvcg_streaming_header_grp.group,
+       &uvcg_uncompressed_grp.group,
+       &uvcg_mjpeg_grp.group,
+       &uvcg_color_matching_grp.group,
+       &uvcg_streaming_class_grp.group,
+       NULL,
+};
+
+/* streaming */
+static struct uvcg_streaming_grp {
+       struct config_group     group;
+} uvcg_streaming_grp;
+
+static struct config_item_type uvcg_streaming_grp_type = {
+       .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *uvcg_default_groups[] = {
+       &uvcg_control_grp.group,
+       &uvcg_streaming_grp.group,
+       NULL,
+};
+
+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);
+}
+
+CONFIGFS_ATTR_STRUCT(f_uvc_opts);
+CONFIGFS_ATTR_OPS(f_uvc_opts);
+
+static void uvc_attr_release(struct config_item *item)
+{
+       struct f_uvc_opts *opts = to_f_uvc_opts(item);
+
+       usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations uvc_item_ops = {
+       .release                = uvc_attr_release,
+       .show_attribute         = f_uvc_opts_attr_show,
+       .store_attribute        = f_uvc_opts_attr_store,
+};
+
+#define UVCG_OPTS_ATTR(cname, conv, str2u, uxx, vnoc, limit)           \
+static ssize_t f_uvc_opts_##cname##_show(                              \
+       struct f_uvc_opts *opts, char *page)                            \
+{                                                                      \
+       int result;                                                     \
+                                                                       \
+       mutex_lock(&opts->lock);                                        \
+       result = sprintf(page, "%d\n", conv(opts->cname));              \
+       mutex_unlock(&opts->lock);                                      \
+                                                                       \
+       return result;                                                  \
+}                                                                      \
+                                                                       \
+static ssize_t                                                         \
+f_uvc_opts_##cname##_store(struct f_uvc_opts *opts,                    \
+                          const char *page, size_t len)                \
+{                                                                      \
+       int ret;                                                        \
+       uxx num;                                                        \
+                                                                       \
+       mutex_lock(&opts->lock);                                        \
+       if (opts->refcnt) {                                             \
+               ret = -EBUSY;                                           \
+               goto end;                                               \
+       }                                                               \
+                                                                       \
+       ret = str2u(page, 0, &num);                                     \
+       if (ret)                                                        \
+               goto end;                                               \
+                                                                       \
+       if (num > limit) {                                              \
+               ret = -EINVAL;                                          \
+               goto end;                                               \
+       }                                                               \
+       opts->cname = vnoc(num);                                        \
+       ret = len;                                                      \
+end:                                                                   \
+       mutex_unlock(&opts->lock);                                      \
+       return ret;                                                     \
+}                                                                      \
+                                                                       \
+static struct f_uvc_opts_attribute                                     \
+       f_uvc_opts_attribute_##cname =                                  \
+       __CONFIGFS_ATTR(cname, S_IRUGO | S_IWUSR,                       \
+                       f_uvc_opts_##cname##_show,                      \
+                       f_uvc_opts_##cname##_store)
+
+#define identity_conv(x) (x)
+
+UVCG_OPTS_ATTR(streaming_interval, identity_conv, kstrtou8, u8, identity_conv,
+              16);
+UVCG_OPTS_ATTR(streaming_maxpacket, le16_to_cpu, kstrtou16, u16, le16_to_cpu,
+              3072);
+UVCG_OPTS_ATTR(streaming_maxburst, identity_conv, kstrtou8, u8, identity_conv,
+              15);
+
+#undef identity_conv
+
+#undef UVCG_OPTS_ATTR
+
+static struct configfs_attribute *uvc_attrs[] = {
+       &f_uvc_opts_attribute_streaming_interval.attr,
+       &f_uvc_opts_attribute_streaming_maxpacket.attr,
+       &f_uvc_opts_attribute_streaming_maxburst.attr,
+       NULL,
+};
+
+static struct config_item_type uvc_func_type = {
+       .ct_item_ops    = &uvc_item_ops,
+       .ct_attrs       = uvc_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+static inline void uvcg_init_group(struct config_group *g,
+                                  struct config_group **default_groups,
+                                  const char *name,
+                                  struct config_item_type *type)
+{
+       g->default_groups = default_groups;
+       config_group_init_type_name(g, name, type);
+}
+
+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);
+       uvcg_init_group(&uvcg_processing_grp.group,
+                       uvcg_processing_default_groups,
+                       "processing",
+                       &uvcg_processing_grp_type);
+       config_group_init_type_name(&uvcg_default_camera.group,
+                                   "default",
+                                   &uvcg_default_camera_type);
+       uvcg_init_group(&uvcg_camera_grp.group,
+                       uvcg_camera_default_groups,
+                       "camera",
+                       &uvcg_camera_grp_type);
+       config_group_init_type_name(&uvcg_default_output.group,
+                                   "default",
+                                   &uvcg_default_output_type);
+       uvcg_init_group(&uvcg_output_grp.group,
+                       uvcg_output_default_groups,
+                       "output",
+                       &uvcg_output_grp_type);
+       uvcg_init_group(&uvcg_terminal_grp.group,
+                       uvcg_terminal_default_groups,
+                       "terminal",
+                       &uvcg_terminal_grp_type);
+       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);
+       uvcg_init_group(&uvcg_control_class_grp.group,
+                       uvcg_control_class_default_groups,
+                       "class",
+                       &uvcg_control_class_grp_type);
+       uvcg_init_group(&uvcg_control_grp.group,
+                       uvcg_control_default_groups,
+                       "control",
+                       &uvcg_control_grp_type);
+       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);
+       uvcg_init_group(&uvcg_color_matching_grp.group,
+                       uvcg_color_matching_default_groups,
+                       "color_matching",
+                       &uvcg_color_matching_grp_type);
+       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);
+       uvcg_init_group(&uvcg_streaming_class_grp.group,
+                       uvcg_streaming_class_default_groups,
+                       "class",
+                       &uvcg_streaming_class_grp_type);
+       uvcg_init_group(&uvcg_streaming_grp.group,
+                       uvcg_streaming_default_groups,
+                       "streaming",
+                       &uvcg_streaming_grp_type);
+       uvcg_init_group(&opts->func_inst.group,
+                       uvcg_default_groups,
+                       "",
+                       &uvc_func_type);
+       return 0;
+}