usb: gadget: uvc: configfs support in uvc function
authorAndrzej Pietrasiewicz <andrzej.p@samsung.com>
Wed, 10 Dec 2014 11:34:02 +0000 (12:34 +0100)
committerFelipe Balbi <balbi@ti.com>
Mon, 12 Jan 2015 18:13:26 +0000 (12:13 -0600)
Add support for using the uvc function as a component of USB gadgets composed
with configfs.

Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
Documentation/ABI/testing/configfs-usb-gadget-uvc [new file with mode: 0644]
drivers/usb/gadget/Kconfig
drivers/usb/gadget/function/Makefile
drivers/usb/gadget/function/f_uvc.c
drivers/usb/gadget/function/u_uvc.h
drivers/usb/gadget/function/uvc_configfs.c [new file with mode: 0644]
drivers/usb/gadget/function/uvc_configfs.h [new file with mode: 0644]

diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uvc b/Documentation/ABI/testing/configfs-usb-gadget-uvc
new file mode 100644 (file)
index 0000000..2f4a005
--- /dev/null
@@ -0,0 +1,265 @@
+What:          /config/usb-gadget/gadget/functions/uvc.name
+Date:          Dec 2014
+KernelVersion: 3.20
+Description:   UVC function directory
+
+               streaming_maxburst      - 0..15 (ss only)
+               streaming_maxpacket     - 1..1023 (fs), 1..3072 (hs/ss)
+               streaming_interval      - 1..16
+
+What:          /config/usb-gadget/gadget/functions/uvc.name/control
+Date:          Dec 2014
+KernelVersion: 3.20
+Description:   Control descriptors
+
+What:          /config/usb-gadget/gadget/functions/uvc.name/control/class
+Date:          Dec 2014
+KernelVersion: 3.20
+Description:   Class descriptors
+
+What:          /config/usb-gadget/gadget/functions/uvc.name/control/class/ss
+Date:          Dec 2014
+KernelVersion: 3.20
+Description:   Super speed control class descriptors
+
+What:          /config/usb-gadget/gadget/functions/uvc.name/control/class/fs
+Date:          Dec 2014
+KernelVersion: 3.20
+Description:   Full speed control class descriptors
+
+What:          /config/usb-gadget/gadget/functions/uvc.name/control/terminal
+Date:          Dec 2014
+KernelVersion: 3.20
+Description:   Terminal descriptors
+
+What:          /config/usb-gadget/gadget/functions/uvc.name/control/terminal/output
+Date:          Dec 2014
+KernelVersion: 3.20
+Description:   Output terminal descriptors
+
+What:          /config/usb-gadget/gadget/functions/uvc.name/control/terminal/output/default
+Date:          Dec 2014
+KernelVersion: 3.20
+Description:   Default output terminal descriptors
+
+               All attributes read only:
+               iTerminal       - index of string descriptor
+               bSourceID       - id of the terminal to which this terminal
+                               is connected
+               bAssocTerminal  - id of the input terminal to which this output
+                               terminal is associated
+               wTerminalType   - terminal type
+               bTerminalID     - a non-zero id of this terminal
+
+What:          /config/usb-gadget/gadget/functions/uvc.name/control/terminal/camera
+Date:          Dec 2014
+KernelVersion: 3.20
+Description:   Camera terminal descriptors
+
+What:          /config/usb-gadget/gadget/functions/uvc.name/control/terminal/camera/default
+Date:          Dec 2014
+KernelVersion: 3.20
+Description:   Default camera terminal descriptors
+
+               All attributes read only:
+               bmControls              - bitmap specifying which controls are
+                                       supported for the video stream
+               wOcularFocalLength      - the value of Locular
+               wObjectiveFocalLengthMax- the value of Lmin
+               wObjectiveFocalLengthMin- the value of Lmax
+               iTerminal               - index of string descriptor
+               bAssocTerminal          - id of the output terminal to which
+                                       this terminal is connected
+               wTerminalType           - terminal type
+               bTerminalID             - a non-zero id of this terminal
+
+What:          /config/usb-gadget/gadget/functions/uvc.name/control/processing
+Date:          Dec 2014
+KernelVersion: 3.20
+Description:   Processing unit descriptors
+
+What:          /config/usb-gadget/gadget/functions/uvc.name/control/processing/default
+Date:          Dec 2014
+KernelVersion: 3.20
+Description:   Default processing unit descriptors
+
+               All attributes read only:
+               iProcessing     - index of string descriptor
+               bmControls      - bitmap specifying which controls are
+                               supported for the video stream
+               wMaxMultiplier  - maximum digital magnification x100
+               bSourceID       - id of the terminal to which this unit is
+                               connected
+               bUnitID         - a non-zero id of this unit
+
+What:          /config/usb-gadget/gadget/functions/uvc.name/control/header
+Date:          Dec 2014
+KernelVersion: 3.20
+Description:   Control header descriptors
+
+What:          /config/usb-gadget/gadget/functions/uvc.name/control/header/name
+Date:          Dec 2014
+KernelVersion: 3.20
+Description:   Specific control header descriptors
+
+dwClockFrequency
+bcdUVC
+What:          /config/usb-gadget/gadget/functions/uvc.name/streaming
+Date:          Dec 2014
+KernelVersion: 3.20
+Description:   Streaming descriptors
+
+What:          /config/usb-gadget/gadget/functions/uvc.name/streaming/class
+Date:          Dec 2014
+KernelVersion: 3.20
+Description:   Streaming class descriptors
+
+What:          /config/usb-gadget/gadget/functions/uvc.name/streaming/class/ss
+Date:          Dec 2014
+KernelVersion: 3.20
+Description:   Super speed streaming class descriptors
+
+What:          /config/usb-gadget/gadget/functions/uvc.name/streaming/class/hs
+Date:          Dec 2014
+KernelVersion: 3.20
+Description:   High speed streaming class descriptors
+
+What:          /config/usb-gadget/gadget/functions/uvc.name/streaming/class/fs
+Date:          Dec 2014
+KernelVersion: 3.20
+Description:   Full speed streaming class descriptors
+
+What:          /config/usb-gadget/gadget/functions/uvc.name/streaming/color_matching
+Date:          Dec 2014
+KernelVersion: 3.20
+Description:   Color matching descriptors
+
+What:          /config/usb-gadget/gadget/functions/uvc.name/streaming/color_matching/default
+Date:          Dec 2014
+KernelVersion: 3.20
+Description:   Default color matching descriptors
+
+               All attributes read only:
+               bMatrixCoefficients     - matrix used to compute luma and
+                                       chroma values from the color primaries
+               bTransferCharacteristics- optoelectronic transfer
+                                       characteristic of the source picutre,
+                                       also called the gamma function
+               bColorPrimaries         - color primaries and the reference
+                                       white
+
+What:          /config/usb-gadget/gadget/functions/uvc.name/streaming/mjpeg
+Date:          Dec 2014
+KernelVersion: 3.20
+Description:   MJPEG format descriptors
+
+What:          /config/usb-gadget/gadget/functions/uvc.name/streaming/mjpeg/name
+Date:          Dec 2014
+KernelVersion: 3.20
+Description:   Specific MJPEG format descriptors
+
+               All attributes read only,
+               except bmaControls and bDefaultFrameIndex:
+               bmaControls             - this format's data for bmaControls in
+                                       the streaming header
+               bmInterfaceFlags        - specifies interlace information,
+                                       read-only
+               bAspectRatioY           - the X dimension of the picture aspect
+                                       ratio, read-only
+               bAspectRatioX           - the Y dimension of the picture aspect
+                                       ratio, read-only
+               bmFlags                 - characteristics of this format,
+                                       read-only
+               bDefaultFrameIndex      - optimum frame index for this stream
+
+What:          /config/usb-gadget/gadget/functions/uvc.name/streaming/mjpeg/name/name
+Date:          Dec 2014
+KernelVersion: 3.20
+Description:   Specific MJPEG frame descriptors
+
+               dwFrameInterval         - indicates how frame interval can be
+                                       programmed; a number of values
+                                       separated by newline can be specified
+               dwDefaultFrameInterval  - the frame interval the device would
+                                       like to use as default
+               dwMaxVideoFrameBufferSize- the maximum number of bytes the
+                                       compressor will produce for a video
+                                       frame or still image
+               dwMaxBitRate            - the maximum bit rate at the shortest
+                                       frame interval in bps
+               dwMinBitRate            - the minimum bit rate at the longest
+                                       frame interval in bps
+               wHeight                 - height of decoded bitmap frame in px
+               wWidth                  - width of decoded bitmam frame in px
+               bmCapabilities          - still image support, fixed frame-rate
+                                       support
+
+What:          /config/usb-gadget/gadget/functions/uvc.name/streaming/uncompressed
+Date:          Dec 2014
+KernelVersion: 3.20
+Description:   Uncompressed format descriptors
+
+What:          /config/usb-gadget/gadget/functions/uvc.name/streaming/uncompressed/name
+Date:          Dec 2014
+KernelVersion: 3.20
+Description:   Specific uncompressed format descriptors
+
+               bmaControls             - this format's data for bmaControls in
+                                       the streaming header
+               bmInterfaceFlags        - specifies interlace information,
+                                       read-only
+               bAspectRatioY           - the X dimension of the picture aspect
+                                       ratio, read-only
+               bAspectRatioX           - the Y dimension of the picture aspect
+                                       ratio, read-only
+               bDefaultFrameIndex      - optimum frame index for this stream
+               bBitsPerPixel           - number of bits per pixel used to
+                                       specify color in the decoded video
+                                       frame
+               guidFormat              - globally unique id used to identify
+                                       stream-encoding format
+
+What:          /config/usb-gadget/gadget/functions/uvc.name/streaming/uncompressed/name/name
+Date:          Dec 2014
+KernelVersion: 3.20
+Description:   Specific uncompressed frame descriptors
+
+               dwFrameInterval         - indicates how frame interval can be
+                                       programmed; a number of values
+                                       separated by newline can be specified
+               dwDefaultFrameInterval  - the frame interval the device would
+                                       like to use as default
+               dwMaxVideoFrameBufferSize- the maximum number of bytes the
+                                       compressor will produce for a video
+                                       frame or still image
+               dwMaxBitRate            - the maximum bit rate at the shortest
+                                       frame interval in bps
+               dwMinBitRate            - the minimum bit rate at the longest
+                                       frame interval in bps
+               wHeight                 - height of decoded bitmap frame in px
+               wWidth                  - width of decoded bitmam frame in px
+               bmCapabilities          - still image support, fixed frame-rate
+                                       support
+
+What:          /config/usb-gadget/gadget/functions/uvc.name/streaming/header
+Date:          Dec 2014
+KernelVersion: 3.20
+Description:   Streaming header descriptors
+
+What:          /config/usb-gadget/gadget/functions/uvc.name/streaming/header/name
+Date:          Dec 2014
+KernelVersion: 3.20
+Description:   Specific streaming header descriptors
+
+               All attributes read only:
+               bTriggerUsage           - how the host software will respond to
+                                       a hardware trigger interrupt event
+               bTriggerSupport         - flag specifying if hardware
+                                       triggering is supported
+               bStillCaptureMethod     - method of still image caputre
+                                       supported
+               bTerminalLink           - id of the output terminal to which
+                                       the video endpoint of this interface
+                                       is connected
+               bmInfo                  - capabilities of this video streaming
+                                       interface
index 747ef53bda14ecb4392e9c295f698806b583341c..65f7f126552251973f0c9679757cebb98c7d4a70 100644 (file)
@@ -423,6 +423,17 @@ config USB_CONFIGFS_F_HID
 
          For more information, see Documentation/usb/gadget_hid.txt.
 
+config USB_CONFIGFS_F_UVC
+       boolean "USB Webcam function"
+       depends on USB_CONFIGFS
+       depends on VIDEO_DEV
+       select VIDEOBUF2_VMALLOC
+       select USB_F_UVC
+       help
+         The Webcam function acts as a composite USB Audio and Video Class
+         device. It provides a userspace API to process UVC control requests
+         and stream video data to the host.
+
 source "drivers/usb/gadget/legacy/Kconfig"
 
 endchoice
index dd68091d92f0478c4a1361f1d0f9f1df4715dc89..f71b1aaa0edfcaf78fe464170f508009140cc375 100644 (file)
@@ -36,7 +36,7 @@ usb_f_uac1-y                  := f_uac1.o u_uac1.o
 obj-$(CONFIG_USB_F_UAC1)       += usb_f_uac1.o
 usb_f_uac2-y                   := f_uac2.o
 obj-$(CONFIG_USB_F_UAC2)       += usb_f_uac2.o
-usb_f_uvc-y                    := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o
+usb_f_uvc-y                    := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_configfs.o
 obj-$(CONFIG_USB_F_UVC)                += usb_f_uvc.o
 usb_f_midi-y                   := f_midi.o
 obj-$(CONFIG_USB_F_MIDI)       += usb_f_midi.o
index 62ca0c5c7f6e14742380630c9d23a08ddbef5830..76891adfba7a414b87e5fd03252928e32492f7eb 100644 (file)
 #include <media/v4l2-dev.h>
 #include <media/v4l2-event.h>
 
+#include "u_uvc.h"
 #include "uvc.h"
+#include "uvc_configfs.h"
 #include "uvc_v4l2.h"
 #include "uvc_video.h"
-#include "u_uvc.h"
 
 unsigned int uvc_gadget_trace_param;
 
@@ -788,25 +789,104 @@ static void uvc_free_inst(struct usb_function_instance *f)
 {
        struct f_uvc_opts *opts = fi_to_f_uvc_opts(f);
 
+       mutex_destroy(&opts->lock);
        kfree(opts);
 }
 
 static struct usb_function_instance *uvc_alloc_inst(void)
 {
        struct f_uvc_opts *opts;
+       struct uvc_camera_terminal_descriptor *cd;
+       struct uvc_processing_unit_descriptor *pd;
+       struct uvc_output_terminal_descriptor *od;
+       struct uvc_color_matching_descriptor *md;
+       struct uvc_descriptor_header **ctl_cls;
 
        opts = kzalloc(sizeof(*opts), GFP_KERNEL);
        if (!opts)
                return ERR_PTR(-ENOMEM);
        opts->func_inst.free_func_inst = uvc_free_inst;
-
+       mutex_init(&opts->lock);
+
+       cd = &opts->uvc_camera_terminal;
+       cd->bLength                     = UVC_DT_CAMERA_TERMINAL_SIZE(3);
+       cd->bDescriptorType             = USB_DT_CS_INTERFACE;
+       cd->bDescriptorSubType          = UVC_VC_INPUT_TERMINAL;
+       cd->bTerminalID                 = 1;
+       cd->wTerminalType               = cpu_to_le16(0x0201);
+       cd->bAssocTerminal              = 0;
+       cd->iTerminal                   = 0;
+       cd->wObjectiveFocalLengthMin    = cpu_to_le16(0);
+       cd->wObjectiveFocalLengthMax    = cpu_to_le16(0);
+       cd->wOcularFocalLength          = cpu_to_le16(0);
+       cd->bControlSize                = 3;
+       cd->bmControls[0]               = 2;
+       cd->bmControls[1]               = 0;
+       cd->bmControls[2]               = 0;
+
+       pd = &opts->uvc_processing;
+       pd->bLength                     = UVC_DT_PROCESSING_UNIT_SIZE(2);
+       pd->bDescriptorType             = USB_DT_CS_INTERFACE;
+       pd->bDescriptorSubType          = UVC_VC_PROCESSING_UNIT;
+       pd->bUnitID                     = 2;
+       pd->bSourceID                   = 1;
+       pd->wMaxMultiplier              = cpu_to_le16(16*1024);
+       pd->bControlSize                = 2;
+       pd->bmControls[0]               = 1;
+       pd->bmControls[1]               = 0;
+       pd->iProcessing                 = 0;
+
+       od = &opts->uvc_output_terminal;
+       od->bLength                     = UVC_DT_OUTPUT_TERMINAL_SIZE;
+       od->bDescriptorType             = USB_DT_CS_INTERFACE;
+       od->bDescriptorSubType          = UVC_VC_OUTPUT_TERMINAL;
+       od->bTerminalID                 = 3;
+       od->wTerminalType               = cpu_to_le16(0x0101);
+       od->bAssocTerminal              = 0;
+       od->bSourceID                   = 2;
+       od->iTerminal                   = 0;
+
+       md = &opts->uvc_color_matching;
+       md->bLength                     = UVC_DT_COLOR_MATCHING_SIZE;
+       md->bDescriptorType             = USB_DT_CS_INTERFACE;
+       md->bDescriptorSubType          = UVC_VS_COLORFORMAT;
+       md->bColorPrimaries             = 1;
+       md->bTransferCharacteristics    = 1;
+       md->bMatrixCoefficients         = 4;
+
+       /* Prepare fs control class descriptors for configfs-based gadgets */
+       ctl_cls = opts->uvc_fs_control_cls;
+       ctl_cls[0] = NULL;      /* assigned elsewhere by configfs */
+       ctl_cls[1] = (struct uvc_descriptor_header *)cd;
+       ctl_cls[2] = (struct uvc_descriptor_header *)pd;
+       ctl_cls[3] = (struct uvc_descriptor_header *)od;
+       ctl_cls[4] = NULL;      /* NULL-terminate */
+       opts->fs_control =
+               (const struct uvc_descriptor_header * const *)ctl_cls;
+
+       /* Prepare hs control class descriptors for configfs-based gadgets */
+       ctl_cls = opts->uvc_ss_control_cls;
+       ctl_cls[0] = NULL;      /* assigned elsewhere by configfs */
+       ctl_cls[1] = (struct uvc_descriptor_header *)cd;
+       ctl_cls[2] = (struct uvc_descriptor_header *)pd;
+       ctl_cls[3] = (struct uvc_descriptor_header *)od;
+       ctl_cls[4] = NULL;      /* NULL-terminate */
+       opts->ss_control =
+               (const struct uvc_descriptor_header * const *)ctl_cls;
+
+       opts->streaming_interval = 1;
+       opts->streaming_maxpacket = 1024;
+
+       uvcg_attach_configfs(opts);
        return &opts->func_inst;
 }
 
 static void uvc_free(struct usb_function *f)
 {
        struct uvc_device *uvc = to_uvc(f);
-
+       struct f_uvc_opts *opts = container_of(f->fi, struct f_uvc_opts,
+                                              func_inst);
+       --opts->refcnt;
        kfree(uvc);
 }
 
@@ -832,6 +912,7 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
 {
        struct uvc_device *uvc;
        struct f_uvc_opts *opts;
+       struct uvc_descriptor_header **strm_cls;
 
        uvc = kzalloc(sizeof(*uvc), GFP_KERNEL);
        if (uvc == NULL)
@@ -840,11 +921,30 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
        uvc->state = UVC_STATE_DISCONNECTED;
        opts = fi_to_f_uvc_opts(fi);
 
+       mutex_lock(&opts->lock);
+       if (opts->uvc_fs_streaming_cls) {
+               strm_cls = opts->uvc_fs_streaming_cls;
+               opts->fs_streaming =
+                       (const struct uvc_descriptor_header * const *)strm_cls;
+       }
+       if (opts->uvc_hs_streaming_cls) {
+               strm_cls = opts->uvc_hs_streaming_cls;
+               opts->hs_streaming =
+                       (const struct uvc_descriptor_header * const *)strm_cls;
+       }
+       if (opts->uvc_ss_streaming_cls) {
+               strm_cls = opts->uvc_ss_streaming_cls;
+               opts->ss_streaming =
+                       (const struct uvc_descriptor_header * const *)strm_cls;
+       }
+
        uvc->desc.fs_control = opts->fs_control;
        uvc->desc.ss_control = opts->ss_control;
        uvc->desc.fs_streaming = opts->fs_streaming;
        uvc->desc.hs_streaming = opts->hs_streaming;
        uvc->desc.ss_streaming = opts->ss_streaming;
+       ++opts->refcnt;
+       mutex_unlock(&opts->lock);
 
        /* Register the function. */
        uvc->func.name = "uvc";
index c0706a3d701932fd87dcf395922000bd184fe176..4676b60a506361498dfc920756c24ec5c070031e 100644 (file)
@@ -17,6 +17,7 @@
 #define U_UVC_H
 
 #include <linux/usb/composite.h>
+#include <linux/usb/video.h>
 
 #define fi_to_f_uvc_opts(f)    container_of(f, struct f_uvc_opts, func_inst)
 
@@ -26,11 +27,60 @@ struct f_uvc_opts {
        unsigned int                                    streaming_interval;
        unsigned int                                    streaming_maxpacket;
        unsigned int                                    streaming_maxburst;
+
+       /*
+        * Control descriptors array pointers for full-/high-speed and
+        * super-speed. They point by default to the uvc_fs_control_cls and
+        * uvc_ss_control_cls arrays respectively. Legacy gadgets must
+        * override them in their gadget bind callback.
+        */
        const struct uvc_descriptor_header * const      *fs_control;
        const struct uvc_descriptor_header * const      *ss_control;
+
+       /*
+        * Streaming descriptors array pointers for full-speed, high-speed and
+        * super-speed. They will point to the uvc_[fhs]s_streaming_cls arrays
+        * for configfs-based gadgets. Legacy gadgets must initialize them in
+        * their gadget bind callback.
+        */
        const struct uvc_descriptor_header * const      *fs_streaming;
        const struct uvc_descriptor_header * const      *hs_streaming;
        const struct uvc_descriptor_header * const      *ss_streaming;
+
+       /* Default control descriptors for configfs-based gadgets. */
+       struct uvc_camera_terminal_descriptor           uvc_camera_terminal;
+       struct uvc_processing_unit_descriptor           uvc_processing;
+       struct uvc_output_terminal_descriptor           uvc_output_terminal;
+       struct uvc_color_matching_descriptor            uvc_color_matching;
+
+       /*
+        * Control descriptors pointers arrays for full-/high-speed and
+        * super-speed. The first element is a configurable control header
+        * descriptor, the other elements point to the fixed default control
+        * descriptors. Used by configfs only, must not be touched by legacy
+        * gadgets.
+        */
+       struct uvc_descriptor_header                    *uvc_fs_control_cls[5];
+       struct uvc_descriptor_header                    *uvc_ss_control_cls[5];
+
+       /*
+        * Streaming descriptors for full-speed, high-speed and super-speed.
+        * Used by configfs only, must not be touched by legacy gadgets. The
+        * arrays are allocated at runtime as the number of descriptors isn't
+        * known in advance.
+        */
+       struct uvc_descriptor_header                    **uvc_fs_streaming_cls;
+       struct uvc_descriptor_header                    **uvc_hs_streaming_cls;
+       struct uvc_descriptor_header                    **uvc_ss_streaming_cls;
+
+       /*
+        * Read/write access to configfs attributes is handled by configfs.
+        *
+        * This lock protects the descriptors from concurrent access by
+        * read/write and symlink creation/removal.
+        */
+       struct mutex                    lock;
+       int                             refcnt;
 };
 
 void uvc_set_trace_param(unsigned int trace);
diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c
new file mode 100644 (file)
index 0000000..33d92ab
--- /dev/null
@@ -0,0 +1,2439 @@
+/*
+ * 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;
+}
diff --git a/drivers/usb/gadget/function/uvc_configfs.h b/drivers/usb/gadget/function/uvc_configfs.h
new file mode 100644 (file)
index 0000000..085e67b
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * uvc_configfs.h
+ *
+ * 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.
+ */
+#ifndef UVC_CONFIGFS_H
+#define UVC_CONFIGFS_H
+
+struct f_uvc_opts;
+
+int uvcg_attach_configfs(struct f_uvc_opts *opts);
+
+#endif /* UVC_CONFIGFS_H */