/*
 *      uvc_ctrl.c  --  USB Video Class driver - Controls
 *
 *      Copyright (C) 2005-2010
 *          Laurent Pinchart (laurent.pinchart@ideasonboard.com)
 *
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 2 of the License, or
 *      (at your option) any later version.
 *
 */

#include <platform_opts.h>
    
#if defined(CONFIG_USBH_UVC) || defined(REFER_USBH_UVC)

#include "usb.h"
#include "v4l2-ctrls.h"
#include "uapi_videodev2.h"
#include "uvcvideo.h"
#include "uvc_os_wrap_via_osdep_api.h"

#define UVC_CTRL_DATA_CURRENT   0
#define UVC_CTRL_DATA_BACKUP    1
#define UVC_CTRL_DATA_MIN   2
#define UVC_CTRL_DATA_MAX   3
#define UVC_CTRL_DATA_RES   4
#define UVC_CTRL_DATA_DEF   5
#define UVC_CTRL_DATA_LAST  6

/* ------------------------------------------------------------------------
 * Controls
 */
#include "section_config.h"
SDRAM_DATA_SECTION static struct uvc_control_info uvc_ctrls[] = {
    {
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_BRIGHTNESS_CONTROL,
        .index      = 0,
        .size       = 2,
        .flags      = UVC_CTRL_FLAG_SET_CUR
                      | UVC_CTRL_FLAG_GET_RANGE
                      | UVC_CTRL_FLAG_RESTORE,
    },
    {
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_CONTRAST_CONTROL,
        .index      = 1,
        .size       = 2,
        .flags      = UVC_CTRL_FLAG_SET_CUR
                      | UVC_CTRL_FLAG_GET_RANGE
                      | UVC_CTRL_FLAG_RESTORE,
    },
    {
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_HUE_CONTROL,
        .index      = 2,
        .size       = 2,
        .flags      = UVC_CTRL_FLAG_SET_CUR
                      | UVC_CTRL_FLAG_GET_RANGE
                      | UVC_CTRL_FLAG_RESTORE
                      | UVC_CTRL_FLAG_AUTO_UPDATE,
    },
    {
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_SATURATION_CONTROL,
        .index      = 3,
        .size       = 2,
        .flags      = UVC_CTRL_FLAG_SET_CUR
                      | UVC_CTRL_FLAG_GET_RANGE
                      | UVC_CTRL_FLAG_RESTORE,
    },
    {
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_SHARPNESS_CONTROL,
        .index      = 4,
        .size       = 2,
        .flags      = UVC_CTRL_FLAG_SET_CUR
                      | UVC_CTRL_FLAG_GET_RANGE
                      | UVC_CTRL_FLAG_RESTORE,
    },
    {
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_GAMMA_CONTROL,
        .index      = 5,
        .size       = 2,
        .flags      = UVC_CTRL_FLAG_SET_CUR
                      | UVC_CTRL_FLAG_GET_RANGE
                      | UVC_CTRL_FLAG_RESTORE,
    },
    {
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL,
        .index      = 6,
        .size       = 2,
        .flags      = UVC_CTRL_FLAG_SET_CUR
                      | UVC_CTRL_FLAG_GET_RANGE
                      | UVC_CTRL_FLAG_RESTORE
                      | UVC_CTRL_FLAG_AUTO_UPDATE,
    },
    {
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL,
        .index      = 7,
        .size       = 4,
        .flags      = UVC_CTRL_FLAG_SET_CUR
                      | UVC_CTRL_FLAG_GET_RANGE
                      | UVC_CTRL_FLAG_RESTORE
                      | UVC_CTRL_FLAG_AUTO_UPDATE,
    },
    {
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_BACKLIGHT_COMPENSATION_CONTROL,
        .index      = 8,
        .size       = 2,
        .flags      = UVC_CTRL_FLAG_SET_CUR
                      | UVC_CTRL_FLAG_GET_RANGE
                      | UVC_CTRL_FLAG_RESTORE,
    },
    {
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_GAIN_CONTROL,
        .index      = 9,
        .size       = 2,
        .flags      = UVC_CTRL_FLAG_SET_CUR
                      | UVC_CTRL_FLAG_GET_RANGE
                      | UVC_CTRL_FLAG_RESTORE,
    },
    {
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
        .index      = 10,
        .size       = 1,
        .flags      = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
                      | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_RESTORE,
    },
    {
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_HUE_AUTO_CONTROL,
        .index      = 11,
        .size       = 1,
        .flags      = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
                      | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_RESTORE,
    },
    {
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL,
        .index      = 12,
        .size       = 1,
        .flags      = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
                      | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_RESTORE,
    },
    {
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL,
        .index      = 13,
        .size       = 1,
        .flags      = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
                      | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_RESTORE,
    },
    {
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_DIGITAL_MULTIPLIER_CONTROL,
        .index      = 14,
        .size       = 2,
        .flags      = UVC_CTRL_FLAG_SET_CUR
                      | UVC_CTRL_FLAG_GET_RANGE
                      | UVC_CTRL_FLAG_RESTORE,
    },
    {
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL,
        .index      = 15,
        .size       = 2,
        .flags      = UVC_CTRL_FLAG_SET_CUR
                      | UVC_CTRL_FLAG_GET_RANGE
                      | UVC_CTRL_FLAG_RESTORE,
    },
    {
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_ANALOG_VIDEO_STANDARD_CONTROL,
        .index      = 16,
        .size       = 1,
        .flags      = UVC_CTRL_FLAG_GET_CUR,
    },
    {
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_ANALOG_LOCK_STATUS_CONTROL,
        .index      = 17,
        .size       = 1,
        .flags      = UVC_CTRL_FLAG_GET_CUR,
    },
    {
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_SCANNING_MODE_CONTROL,
        .index      = 0,
        .size       = 1,
        .flags      = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
                      | UVC_CTRL_FLAG_RESTORE,
    },
    {
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_AE_MODE_CONTROL,
        .index      = 1,
        .size       = 1,
        .flags      = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
                      | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_GET_RES
                      | UVC_CTRL_FLAG_RESTORE,
    },
    {
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_AE_PRIORITY_CONTROL,
        .index      = 2,
        .size       = 1,
        .flags      = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
                      | UVC_CTRL_FLAG_RESTORE,
    },
    {
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL,
        .index      = 3,
        .size       = 4,
        .flags      = UVC_CTRL_FLAG_SET_CUR
                      | UVC_CTRL_FLAG_GET_RANGE
                      | UVC_CTRL_FLAG_RESTORE,
    },
    {
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_EXPOSURE_TIME_RELATIVE_CONTROL,
        .index      = 4,
        .size       = 1,
        .flags      = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_RESTORE,
    },
    {
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_FOCUS_ABSOLUTE_CONTROL,
        .index      = 5,
        .size       = 2,
        .flags      = UVC_CTRL_FLAG_SET_CUR
                      | UVC_CTRL_FLAG_GET_RANGE
                      | UVC_CTRL_FLAG_RESTORE
                      | UVC_CTRL_FLAG_AUTO_UPDATE,
    },
    {
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_FOCUS_RELATIVE_CONTROL,
        .index      = 6,
        .size       = 2,
        .flags      = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_MIN
                      | UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES
                      | UVC_CTRL_FLAG_GET_DEF
                      | UVC_CTRL_FLAG_AUTO_UPDATE,
    },
    {
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_IRIS_ABSOLUTE_CONTROL,
        .index      = 7,
        .size       = 2,
        .flags      = UVC_CTRL_FLAG_SET_CUR
                      | UVC_CTRL_FLAG_GET_RANGE
                      | UVC_CTRL_FLAG_RESTORE
                      | UVC_CTRL_FLAG_AUTO_UPDATE,
    },
    {
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_IRIS_RELATIVE_CONTROL,
        .index      = 8,
        .size       = 1,
        .flags      = UVC_CTRL_FLAG_SET_CUR
                      | UVC_CTRL_FLAG_AUTO_UPDATE,
    },
    {
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_ZOOM_ABSOLUTE_CONTROL,
        .index      = 9,
        .size       = 2,
        .flags      = UVC_CTRL_FLAG_SET_CUR
                      | UVC_CTRL_FLAG_GET_RANGE
                      | UVC_CTRL_FLAG_RESTORE
                      | UVC_CTRL_FLAG_AUTO_UPDATE,
    },
    {
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_ZOOM_RELATIVE_CONTROL,
        .index      = 10,
        .size       = 3,
        .flags      = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_MIN
                      | UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES
                      | UVC_CTRL_FLAG_GET_DEF
                      | UVC_CTRL_FLAG_AUTO_UPDATE,
    },
    {
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_PANTILT_ABSOLUTE_CONTROL,
        .index      = 11,
        .size       = 8,
        .flags      = UVC_CTRL_FLAG_SET_CUR
                      | UVC_CTRL_FLAG_GET_RANGE
                      | UVC_CTRL_FLAG_RESTORE
                      | UVC_CTRL_FLAG_AUTO_UPDATE,
    },
    {
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_PANTILT_RELATIVE_CONTROL,
        .index      = 12,
        .size       = 4,
        .flags      = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_MIN
                      | UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES
                      | UVC_CTRL_FLAG_GET_DEF
                      | UVC_CTRL_FLAG_AUTO_UPDATE,
    },
    {
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_ROLL_ABSOLUTE_CONTROL,
        .index      = 13,
        .size       = 2,
        .flags      = UVC_CTRL_FLAG_SET_CUR
                      | UVC_CTRL_FLAG_GET_RANGE
                      | UVC_CTRL_FLAG_RESTORE
                      | UVC_CTRL_FLAG_AUTO_UPDATE,
    },
    {
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_ROLL_RELATIVE_CONTROL,
        .index      = 14,
        .size       = 2,
        .flags      = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_MIN
                      | UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES
                      | UVC_CTRL_FLAG_GET_DEF
                      | UVC_CTRL_FLAG_AUTO_UPDATE,
    },
    {
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_FOCUS_AUTO_CONTROL,
        .index      = 17,
        .size       = 1,
        .flags      = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
                      | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_RESTORE,
    },
    {
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_PRIVACY_CONTROL,
        .index      = 18,
        .size       = 1,
        .flags      = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
                      | UVC_CTRL_FLAG_RESTORE
                      | UVC_CTRL_FLAG_AUTO_UPDATE,
    },
};

static struct uvc_menu_info power_line_frequency_controls[] = {
    { 0, "Disabled" },
    { 1, "50 Hz" },
    { 2, "60 Hz" },
};

static struct uvc_menu_info exposure_auto_controls[] = {
    { 2, "Auto Mode" },
    { 1, "Manual Mode" },
    { 4, "Shutter Priority Mode" },
    { 8, "Aperture Priority Mode" },
};

static __s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping,
    __u8 query, const __u8 *data)
{
    __s8 zoom = (__s8)data[0];

    UNUSED(mapping);

    switch (query) {
        case UVC_GET_CUR:
            return (zoom == 0) ? 0 : (zoom > 0 ? data[2] : -data[2]);

        case UVC_GET_MIN:
        case UVC_GET_MAX:
        case UVC_GET_RES:
        case UVC_GET_DEF:
        default:
            return data[2];
    }
}

static void uvc_ctrl_set_zoom(struct uvc_control_mapping *mapping,
    __s32 value, __u8 *data)
{
    UNUSED(mapping);
    
    data[0] = value == 0 ? 0 : (value > 0) ? 1 : 0xff;
    data[2] = min((int)abs(value), 0xff);
}

#include "section_config.h"
SDRAM_DATA_SECTION static struct uvc_control_mapping uvc_ctrl_mappings[] = {
    {
        .id         = V4L2_CID_BRIGHTNESS,
        .name       = "Brightness",
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_BRIGHTNESS_CONTROL,
        .size       = 16,
        .offset     = 0,
        .v4l2_type  = V4L2_CTRL_TYPE_INTEGER,
        .data_type  = UVC_CTRL_DATA_TYPE_SIGNED,
    },
    {
        .id         = V4L2_CID_CONTRAST,
        .name       = "Contrast",
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_CONTRAST_CONTROL,
        .size       = 16,
        .offset     = 0,
        .v4l2_type  = V4L2_CTRL_TYPE_INTEGER,
        .data_type  = UVC_CTRL_DATA_TYPE_UNSIGNED,
    },
    {
        .id         = V4L2_CID_HUE,
        .name       = "Hue",
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_HUE_CONTROL,
        .size       = 16,
        .offset     = 0,
        .v4l2_type  = V4L2_CTRL_TYPE_INTEGER,
        .data_type  = UVC_CTRL_DATA_TYPE_SIGNED,
        .master_id  = V4L2_CID_HUE_AUTO,
        .master_manual  = 0,
    },
    {
        .id         = V4L2_CID_SATURATION,
        .name       = "Saturation",
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_SATURATION_CONTROL,
        .size       = 16,
        .offset     = 0,
        .v4l2_type  = V4L2_CTRL_TYPE_INTEGER,
        .data_type  = UVC_CTRL_DATA_TYPE_UNSIGNED,
    },
    {
        .id         = V4L2_CID_SHARPNESS,
        .name       = "Sharpness",
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_SHARPNESS_CONTROL,
        .size       = 16,
        .offset     = 0,
        .v4l2_type  = V4L2_CTRL_TYPE_INTEGER,
        .data_type  = UVC_CTRL_DATA_TYPE_UNSIGNED,
    },
    {
        .id         = V4L2_CID_GAMMA,
        .name       = "Gamma",
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_GAMMA_CONTROL,
        .size       = 16,
        .offset     = 0,
        .v4l2_type  = V4L2_CTRL_TYPE_INTEGER,
        .data_type  = UVC_CTRL_DATA_TYPE_UNSIGNED,
    },
    {
        .id         = V4L2_CID_BACKLIGHT_COMPENSATION,
        .name       = "Backlight Compensation",
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_BACKLIGHT_COMPENSATION_CONTROL,
        .size       = 16,
        .offset     = 0,
        .v4l2_type  = V4L2_CTRL_TYPE_INTEGER,
        .data_type  = UVC_CTRL_DATA_TYPE_UNSIGNED,
    },
    {
        .id         = V4L2_CID_GAIN,
        .name       = "Gain",
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_GAIN_CONTROL,
        .size       = 16,
        .offset     = 0,
        .v4l2_type  = V4L2_CTRL_TYPE_INTEGER,
        .data_type  = UVC_CTRL_DATA_TYPE_UNSIGNED,
    },
    {
        .id         = V4L2_CID_POWER_LINE_FREQUENCY,
        .name       = "Power Line Frequency",
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
        .size       = 2,
        .offset     = 0,
        .v4l2_type  = V4L2_CTRL_TYPE_MENU,
        .data_type  = UVC_CTRL_DATA_TYPE_ENUM,
        .menu_info  = power_line_frequency_controls,
        .menu_count = /*3*/ARRAY_SIZE(power_line_frequency_controls),
    },
    {
        .id         = V4L2_CID_HUE_AUTO,
        .name       = "Hue, Auto",
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_HUE_AUTO_CONTROL,
        .size       = 1,
        .offset     = 0,
        .v4l2_type  = V4L2_CTRL_TYPE_BOOLEAN,
        .data_type  = UVC_CTRL_DATA_TYPE_BOOLEAN,
        .slave_ids  = { V4L2_CID_HUE, },
    },
    {
        .id         = V4L2_CID_EXPOSURE_AUTO,
        .name       = "Exposure, Auto",
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_AE_MODE_CONTROL,
        .size       = 4,
        .offset     = 0,
        .v4l2_type  = V4L2_CTRL_TYPE_MENU,
        .data_type  = UVC_CTRL_DATA_TYPE_BITMASK,
        .menu_info  = exposure_auto_controls,
        .menu_count = /*4*/ARRAY_SIZE(exposure_auto_controls),
        .slave_ids  = { V4L2_CID_EXPOSURE_ABSOLUTE, },
    },
    {
        .id         = V4L2_CID_EXPOSURE_AUTO_PRIORITY,
        .name       = "Exposure, Auto Priority",
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_AE_PRIORITY_CONTROL,
        .size       = 1,
        .offset     = 0,
        .v4l2_type  = V4L2_CTRL_TYPE_BOOLEAN,
        .data_type  = UVC_CTRL_DATA_TYPE_BOOLEAN,
    },
    {
        .id         = V4L2_CID_EXPOSURE_ABSOLUTE,
        .name       = "Exposure (Absolute)",
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL,
        .size       = 32,
        .offset     = 0,
        .v4l2_type  = V4L2_CTRL_TYPE_INTEGER,
        .data_type  = UVC_CTRL_DATA_TYPE_UNSIGNED,
        .master_id  = V4L2_CID_EXPOSURE_AUTO,
        .master_manual  = V4L2_EXPOSURE_MANUAL,
    },
    {
        .id         = V4L2_CID_AUTO_WHITE_BALANCE,
        .name       = "White Balance Temperature, Auto",
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL,
        .size       = 1,
        .offset     = 0,
        .v4l2_type  = V4L2_CTRL_TYPE_BOOLEAN,
        .data_type  = UVC_CTRL_DATA_TYPE_BOOLEAN,
        .slave_ids  = { V4L2_CID_WHITE_BALANCE_TEMPERATURE, },
    },
    {
        .id         = V4L2_CID_WHITE_BALANCE_TEMPERATURE,
        .name       = "White Balance Temperature",
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL,
        .size       = 16,
        .offset     = 0,
        .v4l2_type  = V4L2_CTRL_TYPE_INTEGER,
        .data_type  = UVC_CTRL_DATA_TYPE_UNSIGNED,
        .master_id  = V4L2_CID_AUTO_WHITE_BALANCE,
        .master_manual  = 0,
    },
    {
        .id         = V4L2_CID_AUTO_WHITE_BALANCE,
        .name       = "White Balance Component, Auto",
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL,
        .size       = 1,
        .offset     = 0,
        .v4l2_type  = V4L2_CTRL_TYPE_BOOLEAN,
        .data_type  = UVC_CTRL_DATA_TYPE_BOOLEAN,
        .slave_ids  = {
            V4L2_CID_BLUE_BALANCE,
            V4L2_CID_RED_BALANCE
        },
    },
    {
        .id         = V4L2_CID_BLUE_BALANCE,
        .name       = "White Balance Blue Component",
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL,
        .size       = 16,
        .offset     = 0,
        .v4l2_type  = V4L2_CTRL_TYPE_INTEGER,
        .data_type  = UVC_CTRL_DATA_TYPE_SIGNED,
        .master_id  = V4L2_CID_AUTO_WHITE_BALANCE,
        .master_manual  = 0,
    },
    {
        .id         = V4L2_CID_RED_BALANCE,
        .name       = "White Balance Red Component",
        .entity     = UVC_GUID_UVC_PROCESSING,
        .selector   = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL,
        .size       = 16,
        .offset     = 16,
        .v4l2_type  = V4L2_CTRL_TYPE_INTEGER,
        .data_type  = UVC_CTRL_DATA_TYPE_SIGNED,
        .master_id  = V4L2_CID_AUTO_WHITE_BALANCE,
        .master_manual  = 0,
    },
    {
        .id         = V4L2_CID_FOCUS_ABSOLUTE,
        .name       = "Focus (absolute)",
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_FOCUS_ABSOLUTE_CONTROL,
        .size       = 16,
        .offset     = 0,
        .v4l2_type  = V4L2_CTRL_TYPE_INTEGER,
        .data_type  = UVC_CTRL_DATA_TYPE_UNSIGNED,
        .master_id  = V4L2_CID_FOCUS_AUTO,
        .master_manual  = 0,
    },
    {
        .id         = V4L2_CID_FOCUS_AUTO,
        .name       = "Focus, Auto",
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_FOCUS_AUTO_CONTROL,
        .size       = 1,
        .offset     = 0,
        .v4l2_type  = V4L2_CTRL_TYPE_BOOLEAN,
        .data_type  = UVC_CTRL_DATA_TYPE_BOOLEAN,
        .slave_ids  = { V4L2_CID_FOCUS_ABSOLUTE, },
    },
    {
        .id         = V4L2_CID_IRIS_ABSOLUTE,
        .name       = "Iris, Absolute",
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_IRIS_ABSOLUTE_CONTROL,
        .size       = 16,
        .offset     = 0,
        .v4l2_type  = V4L2_CTRL_TYPE_INTEGER,
        .data_type  = UVC_CTRL_DATA_TYPE_UNSIGNED,
    },
    {
        .id         = V4L2_CID_IRIS_RELATIVE,
        .name       = "Iris, Relative",
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_IRIS_RELATIVE_CONTROL,
        .size       = 8,
        .offset     = 0,
        .v4l2_type  = V4L2_CTRL_TYPE_INTEGER,
        .data_type  = UVC_CTRL_DATA_TYPE_SIGNED,
    },
    {
        .id         = V4L2_CID_ZOOM_ABSOLUTE,
        .name       = "Zoom, Absolute",
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_ZOOM_ABSOLUTE_CONTROL,
        .size       = 16,
        .offset     = 0,
        .v4l2_type  = V4L2_CTRL_TYPE_INTEGER,
        .data_type  = UVC_CTRL_DATA_TYPE_UNSIGNED,
    },
    {
        .id         = V4L2_CID_ZOOM_CONTINUOUS,
        .name       = "Zoom, Continuous",
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_ZOOM_RELATIVE_CONTROL,
        .size       = 0,
        .offset     = 0,
        .v4l2_type  = V4L2_CTRL_TYPE_INTEGER,
        .data_type  = UVC_CTRL_DATA_TYPE_SIGNED,
        .get        = uvc_ctrl_get_zoom,
        .set        = uvc_ctrl_set_zoom,
    },
    {
        .id         = V4L2_CID_PAN_ABSOLUTE,
        .name       = "Pan (Absolute)",
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_PANTILT_ABSOLUTE_CONTROL,
        .size       = 32,
        .offset     = 0,
        .v4l2_type  = V4L2_CTRL_TYPE_INTEGER,
        .data_type  = UVC_CTRL_DATA_TYPE_UNSIGNED,
    },
    {
        .id         = V4L2_CID_TILT_ABSOLUTE,
        .name       = "Tilt (Absolute)",
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_PANTILT_ABSOLUTE_CONTROL,
        .size       = 32,
        .offset     = 32,
        .v4l2_type  = V4L2_CTRL_TYPE_INTEGER,
        .data_type  = UVC_CTRL_DATA_TYPE_UNSIGNED,
    },
    {
        .id         = V4L2_CID_PRIVACY,
        .name       = "Privacy",
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_PRIVACY_CONTROL,
        .size       = 1,
        .offset     = 0,
        .v4l2_type  = V4L2_CTRL_TYPE_BOOLEAN,
        .data_type  = UVC_CTRL_DATA_TYPE_BOOLEAN,
    },
};

/* ------------------------------------------------------------------------
 * Utility functions
 */
static inline __u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id)
{
    return ctrl->uvc_data + id * 4 * (ctrl->info.size / 4 + 1);
}

// process: bmControls = {0x7F,0x15}, i = 0 ~ 15
static inline int uvc_test_bit(const __u8 *data, int bit)
{
    return (data[bit >> 3] >> (bit & 7)) & 1;
}

#if 0
static inline void uvc_clear_bit(__u8 *data, int bit)
{
    data[bit >> 3] &= ~(1 << (bit & 7));
}
#endif

/* Extract the bit string specified by mapping->offset and mapping->size
 * from the little-endian data stored at 'data' and return the result as
 * a signed 32bit integer. Sign extension will be performed if the mapping
 * references a signed data type.
 */
static __s32 uvc_get_le_value(struct uvc_control_mapping *mapping,
    __u8 query, const __u8 *data)
{
    int bits = mapping->size;
    int offset = mapping->offset;
    __s32 value = 0;
    __u8 mask;

    UNUSED(query);
    
    data += offset / 8;
    offset &= 7;
    mask = ((1LL << bits) - 1) << offset;

    for (; bits > 0; data++) {
        __u8 byte = *data & mask;
        value |= offset > 0 ? (byte >> offset) : (byte << (-offset));
        bits -= 8 - (offset > 0 ? offset : 0);
        offset -= 8;
        mask = (1 << bits) - 1;
    }

    /* Sign-extend the value if needed. */
    if (mapping->data_type == UVC_CTRL_DATA_TYPE_SIGNED) {
        value |= -(value & (1 << (mapping->size - 1)));
    }

    return value;
}

/* Set the bit string specified by mapping->offset and mapping->size
 * in the little-endian data stored at 'data' to the value 'value'.
 */
static void uvc_set_le_value(struct uvc_control_mapping *mapping,
    __s32 value, __u8 *data)
{
    int bits = mapping->size;
    int offset = mapping->offset;
    __u8 mask;

    /* According to the v4l2 spec, writing any value to a button control
     * should result in the action belonging to the button control being
     * triggered. UVC devices however want to see a 1 written -> override
     * value.
     */
    if (mapping->v4l2_type == V4L2_CTRL_TYPE_BUTTON) {
        value = -1;
    }

    data += offset / 8;
    offset &= 7;

    for (; bits > 0; data++) {
        mask = ((1LL << bits) - 1) << offset;
        *data = (*data & ~mask) | ((value << offset) & mask);
        value >>= offset ? offset : 8;
        bits -= 8 - offset;
        offset = 0;
    }
}

/* ------------------------------------------------------------------------
 * Terminal and unit management
 */

static const __u8 uvc_processing_guid[16] = UVC_GUID_UVC_PROCESSING;
static const __u8 uvc_camera_guid[16] = UVC_GUID_UVC_CAMERA;
static const __u8 uvc_media_transport_input_guid[16] =
    UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT;

// ctrl->entity(process) ,
static int uvc_entity_match_guid(const struct uvc_entity *entity,
    const __u8 guid[16])
{
    switch (UVC_ENTITY_TYPE(entity)) {
        case UVC_ITT_CAMERA:
            return memcmp(uvc_camera_guid, guid, 16) == 0;

        case UVC_ITT_MEDIA_TRANSPORT_INPUT:
            return memcmp(uvc_media_transport_input_guid, guid, 16) == 0;

        case UVC_VC_PROCESSING_UNIT:
            return memcmp(uvc_processing_guid, guid, 16) == 0;

        case UVC_VC_EXTENSION_UNIT:
            return memcmp(entity->extension.guidExtensionCode,
                    guid, 16) == 0;

        default:
            return 0;
    }
}

/* ------------------------------------------------------------------------
 * UVC Controls
 */

static void __uvc_find_control(struct uvc_entity *entity, __u32 v4l2_id,
    struct uvc_control_mapping **mapping, struct uvc_control **control,
    int next)
{
    struct uvc_control *ctrl;
    struct uvc_control_mapping *map;
    unsigned int i;

    if (entity == NULL) {
        return;
    }

    for (i = 0; i < entity->ncontrols; ++i) {
        ctrl = &entity->controls[i];

        if (!ctrl->initialized) {
            continue;
        }

        list_for_each_entry(map, &ctrl->info.mappings, list, struct uvc_control_mapping) {
            if ((map->id == v4l2_id) && !next) {
                *control = ctrl;
                *mapping = map;
                return;
            }

            if ((*mapping == NULL || (*mapping)->id > map->id) &&
                (map->id > v4l2_id) && next) {
                *control = ctrl;
                *mapping = map;
            }
        }
    }
}

static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
    __u32 v4l2_id, struct uvc_control_mapping **mapping)
{
    struct uvc_control *ctrl = NULL;
    struct uvc_entity *entity;
    int next = v4l2_id & V4L2_CTRL_FLAG_NEXT_CTRL;
    
    *mapping = NULL;
    /* Mask the query flags. */
    v4l2_id &= V4L2_CTRL_ID_MASK;
    
    /* Find the control. */
    list_for_each_entry(entity, &chain->entities, chain, struct uvc_entity) {
        __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next);

        if (ctrl && !next) {
            return ctrl;
        }
    }

    if (ctrl == NULL && !next) {
        UVC_PRINTF("Control 0x%x not found.\n", v4l2_id);
    }

    return ctrl;
}

static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain,
    struct uvc_control *ctrl)
{
    int ret;

    if (ctrl->info.flags & UVC_CTRL_FLAG_GET_DEF) {
        ret = uvc_query_ctrl(chain->dev, UVC_GET_DEF, ctrl->entity->id,
                chain->dev->intfnum, ctrl->info.selector,
                uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF),
                ctrl->info.size);
        if (ret < 0) {
            return ret;
        }
    }

    if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN) {
        ret = uvc_query_ctrl(chain->dev, UVC_GET_MIN, ctrl->entity->id,
                chain->dev->intfnum, ctrl->info.selector,
                uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN),
                ctrl->info.size);
        if (ret < 0) {
            return ret;
        }
    }

    if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX) {
        ret = uvc_query_ctrl(chain->dev, UVC_GET_MAX, ctrl->entity->id,
                chain->dev->intfnum, ctrl->info.selector,
                uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX),
                ctrl->info.size);
        if (ret < 0) {
            return ret;
        }
    }

    if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES) {
        ret = uvc_query_ctrl(chain->dev, UVC_GET_RES, ctrl->entity->id,
                chain->dev->intfnum, ctrl->info.selector,
                uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES),
                ctrl->info.size);
        if (ret < 0) {
            if (UVC_ENTITY_TYPE(ctrl->entity) !=
                UVC_VC_EXTENSION_UNIT) {
                return ret;
            }

            /* GET_RES is mandatory for XU controls, but some
             * cameras still choke on it. Ignore errors and set the
             * resolution value to zero.
             */
            rtw_memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES), 0, ctrl->info.size);
        }
    }

    ctrl->cached = 1;
    return 0;
}

static int __uvc_ctrl_get(struct uvc_video_chain *chain,
    struct uvc_control *ctrl, struct uvc_control_mapping *mapping,
    s32 *value)
{
    struct uvc_menu_info *menu;
    unsigned int i;
    int ret;

    if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0) {
        return -USB_EACCES;
    }

    if (!ctrl->loaded) {
        ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, ctrl->entity->id,
                chain->dev->intfnum, ctrl->info.selector,
                uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
                ctrl->info.size);
        if (ret < 0) {
            return ret;
        }

        ctrl->loaded = 1;
    }

    *value = mapping->get(mapping, UVC_GET_CUR,
            uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));

    if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) {
        menu = mapping->menu_info;
        for (i = 0; i < mapping->menu_count; ++i, ++menu) {
            if (menu->value == (__u32)*value) {
                *value = i;
                break;
            }
        }
    }

    return 0;
}

static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
    struct uvc_control *ctrl,
    struct uvc_control_mapping *mapping,
    struct v4l2_queryctrl *v4l2_ctrl)
{
    struct uvc_control_mapping *master_map = NULL;
    struct uvc_control *master_ctrl = NULL;
    struct uvc_menu_info *menu;
    unsigned int i;
    
    rtw_memset(v4l2_ctrl, 0, sizeof * v4l2_ctrl);
    v4l2_ctrl->id = mapping->id;
    v4l2_ctrl->type = mapping->v4l2_type;
    strlcpy((char *)v4l2_ctrl->name, (const char *)mapping->name, sizeof v4l2_ctrl->name);
    v4l2_ctrl->flags = 0;

    if (!(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) {
        v4l2_ctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
    }

    if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR)) {
        v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
    }

    if (mapping->master_id)
        __uvc_find_control(ctrl->entity, mapping->master_id,
            &master_map, &master_ctrl, 0);

    if (master_ctrl && (master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) {
        s32 val;
        int ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val);
        if (ret < 0) {
            return ret;
        }

        if (val != mapping->master_manual) {
            v4l2_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
        }
    }

    if (!ctrl->cached) {
        int ret = uvc_ctrl_populate_cache(chain, ctrl);
        if (ret < 0) {
            return ret;
        }
    }

    if (ctrl->info.flags & UVC_CTRL_FLAG_GET_DEF) {
        v4l2_ctrl->default_value = mapping->get(mapping, UVC_GET_DEF,
                uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF));
    }

    switch (mapping->v4l2_type) {
        case V4L2_CTRL_TYPE_MENU:
            v4l2_ctrl->minimum = 0;
            v4l2_ctrl->maximum = mapping->menu_count - 1;
            v4l2_ctrl->step = 1;
            menu = mapping->menu_info;

            for (i = 0; i < mapping->menu_count; ++i, ++menu) {
                if (menu->value == (__u32)v4l2_ctrl->default_value) {
                    v4l2_ctrl->default_value = i;
                    break;
                }
            }

            return 0;

        case V4L2_CTRL_TYPE_BOOLEAN:
            v4l2_ctrl->minimum = 0;
            v4l2_ctrl->maximum = 1;
            v4l2_ctrl->step = 1;
            return 0;

        case V4L2_CTRL_TYPE_BUTTON:
            v4l2_ctrl->minimum = 0;
            v4l2_ctrl->maximum = 0;
            v4l2_ctrl->step = 0;
            return 0;

        default:
            break;
    }

    if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN)
        v4l2_ctrl->minimum = mapping->get(mapping, UVC_GET_MIN,
                uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));

    if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX)
        v4l2_ctrl->maximum = mapping->get(mapping, UVC_GET_MAX,
                uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));

    if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)
        v4l2_ctrl->step = mapping->get(mapping, UVC_GET_RES,
                uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));

    return 0;
}

int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
    struct v4l2_queryctrl *v4l2_ctrl)
{
    struct uvc_control *ctrl;
    struct uvc_control_mapping *mapping;
    int ret;
    
    Mutex_Lock(&chain->ctrl_mutex);
    ctrl = uvc_find_control(chain, v4l2_ctrl->id, &mapping);
    if (ctrl == NULL) {
        ret = -USB_EINVAL;
        goto done;
    }

    ret = __uvc_query_v4l2_ctrl(chain, ctrl, mapping, v4l2_ctrl);
done:
    Mutex_Unlock(&chain->ctrl_mutex);
    return ret;
}

/*
 * Mapping V4L2 controls to UVC controls can be straighforward if done well.
 * Most of the UVC controls exist in V4L2, and can be mapped directly. Some
 * must be grouped (for instance the Red Balance, Blue Balance and Do White
 * Balance V4L2 controls use the White Balance Component UVC control) or
 * otherwise translated. The approach we take here is to use a translation
 * table for the controls that can be mapped directly, and handle the others
 * manually.
 */
int uvc_query_v4l2_menu(struct uvc_video_chain *chain,
    struct v4l2_querymenu *query_menu)
{
    struct uvc_menu_info *menu_info;
    struct uvc_control_mapping *mapping;
    struct uvc_control *ctrl;
    u32 index = query_menu->index;
    u32 id = query_menu->id;
    int ret;
    
    rtw_memset(query_menu, 0, sizeof(*query_menu));
    query_menu->id = id;
    query_menu->index = index;
    
    Mutex_Lock(&chain->ctrl_mutex);
    
    ctrl = uvc_find_control(chain, query_menu->id, &mapping);
    if (ctrl == NULL || mapping->v4l2_type != V4L2_CTRL_TYPE_MENU) {
        ret = -USB_EINVAL;
        goto done;
    }

    if (query_menu->index >= mapping->menu_count) {
        ret = -USB_EINVAL;
        goto done;
    }

    menu_info = &mapping->menu_info[query_menu->index];

    if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK &&
        (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)) {
        s32 bitmap;

        if (!ctrl->cached) {
            ret = uvc_ctrl_populate_cache(chain, ctrl);
            if (ret < 0) {
                goto done;
            }
        }

        bitmap = mapping->get(mapping, UVC_GET_RES,
                uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
        if (!(bitmap & menu_info->value)) {
            ret = -USB_EINVAL;
            goto done;
        }
    }

    strlcpy((char *)query_menu->name, (const char *)menu_info->name, sizeof query_menu->name);
done:
    Mutex_Unlock(&chain->ctrl_mutex);
    return ret;
}

/* --------------------------------------------------------------------------
 * Control transactions
 *
 * To make extended set operations as atomic as the hardware allows, controls
 * are handled using begin/commit/rollback operations.
 *
 * At the beginning of a set request, uvc_ctrl_begin should be called to
 * initialize the request. This function acquires the control lock.
 *
 * When setting a control, the new value is stored in the control data field
 * at position UVC_CTRL_DATA_CURRENT. The control is then marked as dirty for
 * later processing. If the UVC and V4L2 control sizes differ, the current
 * value is loaded from the hardware before storing the new value in the data
 * field.
 *
 * After processing all controls in the transaction, uvc_ctrl_commit or
 * uvc_ctrl_rollback must be called to apply the pending changes to the
 * hardware or revert them. When applying changes, all controls marked as
 * dirty will be modified in the UVC device, and the dirty flag will be
 * cleared. When reverting controls, the control data field
 * UVC_CTRL_DATA_CURRENT is reverted to its previous value
 * (UVC_CTRL_DATA_BACKUP) for all dirty controls. Both functions release the
 * control lock.
 */
int uvc_ctrl_begin(struct uvc_video_chain *chain)
{
    Mutex_Lock(&chain->ctrl_mutex);
    return 0;
}

static int uvc_ctrl_commit_entity(struct uvc_device *dev,
    struct uvc_entity *entity, int rollback)
{
    struct uvc_control *ctrl;
    unsigned int i;
    int ret;

    if (entity == NULL) {
        return 0;
    }

    for (i = 0; i < entity->ncontrols; ++i) {
        ctrl = &entity->controls[i];

        if (!ctrl->initialized) {
            continue;
        }

        /* Reset the loaded flag for auto-update controls that were
         * marked as loaded in uvc_ctrl_get/uvc_ctrl_set to prevent
         * uvc_ctrl_get from using the cached value, and for write-only
         * controls to prevent uvc_ctrl_set from setting bits not
         * explicitly set by the user.
         */
        if (ctrl->info.flags & UVC_CTRL_FLAG_AUTO_UPDATE ||
            !(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) {
            ctrl->loaded = 0;
        }

        if (!ctrl->dirty) {
            continue;
        }

        if (!rollback)
            ret = uvc_query_ctrl(dev, UVC_SET_CUR, ctrl->entity->id,
                    dev->intfnum, ctrl->info.selector,
                    uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
                    ctrl->info.size);
        else {
            ret = 0;
        }

        if (rollback || ret < 0)
            rtw_memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
                uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
                ctrl->info.size);

        ctrl->dirty = 0;

        if (ret < 0) {
            return ret;
        }
    }

    return 0;
}

int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback,
    const struct v4l2_ext_control *xctrls,
    unsigned int xctrls_count)
{
    struct uvc_video_chain *chain = handle->chain;
    struct uvc_entity *entity;
    int ret = 0;
    
    UNUSED(xctrls);
    UNUSED(xctrls_count);
    
    /* Find the control. */
    list_for_each_entry(entity, &chain->entities, chain, struct uvc_entity) {
        ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback);

        if (ret < 0) {
            goto done;
        }
    }
    /*edit by Ian -- disable ctrl event interface*/
#if 0
    if (!rollback) {
        uvc_ctrl_send_events(handle, xctrls, xctrls_count);
    }
#endif
done:
    Mutex_Unlock(&chain->ctrl_mutex);
    return ret;
}

int uvc_ctrl_get(struct uvc_video_chain *chain,
    struct v4l2_ext_control *xctrl)
{
    int ret;
    s32 value;
    struct uvc_control *ctrl;
    struct uvc_control_mapping *mapping;
    ctrl = uvc_find_control(chain, xctrl->id, &mapping);

    if (ctrl == NULL) {
        return -USB_EINVAL;
    }

    ret = __uvc_ctrl_get(chain, ctrl, mapping, &value);
    xctrl->value = value;
    return ret;
}

int uvc_ctrl_set(struct uvc_video_chain *chain,
    struct v4l2_ext_control *xctrl)
{
    struct uvc_control *ctrl;
    struct uvc_control_mapping *mapping;
    s32 value;
    u32 step;
    s32 min;
    s32 max;
    int ret;
    ctrl = uvc_find_control(chain, xctrl->id, &mapping);

    if (ctrl == NULL) {
        return -USB_EINVAL;
    }

    if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR)) {
        return -USB_EACCES;
    }

    /* Clamp out of range values. */
    switch (mapping->v4l2_type) {
        case V4L2_CTRL_TYPE_INTEGER:
            if (!ctrl->cached) {
                ret = uvc_ctrl_populate_cache(chain, ctrl);

                if (ret < 0) {
                    return ret;
                }
            }

            min = mapping->get(mapping, UVC_GET_MIN,
                    uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));
            max = mapping->get(mapping, UVC_GET_MAX,
                    uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));
            step = mapping->get(mapping, UVC_GET_RES,
                    uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));

            if (step == 0) {
                step = 1;
            }

            xctrl->value = min + ((u32)(xctrl->value - min) + step / 2)
                / step * step;

            if (mapping->data_type == UVC_CTRL_DATA_TYPE_SIGNED) {
                clamp(xctrl->value, xctrl->value, min, max, s32);
            } else {
                clamp(xctrl->value, xctrl->value, min, max, u32);
            }

            value = xctrl->value;
            break;

        case V4L2_CTRL_TYPE_BOOLEAN:
            clamp(xctrl->value, xctrl->value, 0, 1, __s32);
            value = xctrl->value;
            break;

        case V4L2_CTRL_TYPE_MENU:
            if (xctrl->value < 0 || xctrl->value >= (__s32)mapping->menu_count) {
                return -USB_ERANGE;
            }

            value = mapping->menu_info[xctrl->value].value;

            /* Valid menu indices are reported by the GET_RES request for
             * UVC controls that support it.
             */
            if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK &&
                (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)) {
                if (!ctrl->cached) {
                    ret = uvc_ctrl_populate_cache(chain, ctrl);

                    if (ret < 0) {
                        return ret;
                    }
                }

                step = mapping->get(mapping, UVC_GET_RES,
                        uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));

                if (!(step & value)) {
                    return -USB_EINVAL;
                }
            }

            break;

        default:
            value = xctrl->value;
            break;
    }

    /* If the mapping doesn't span the whole UVC control, the current value
     * needs to be loaded from the device to perform the read-modify-write
     * operation.
     */
    if (!ctrl->loaded && (ctrl->info.size * 8) != mapping->size) {
        if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0) {
            rtw_memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
                0, ctrl->info.size);
        } else {
            ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
                    ctrl->entity->id, chain->dev->intfnum,
                    ctrl->info.selector,
                    uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
                    ctrl->info.size);

            if (ret < 0) {
                return ret;
            }
        }

        ctrl->loaded = 1;
    }

    /* Backup the current value in case we need to rollback later. */
    if (!ctrl->dirty) {
        rtw_memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
            uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
            ctrl->info.size);
    }

    mapping->set(mapping, value,
        uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
    ctrl->dirty = 1;
    ctrl->modified = 1;
    return 0;
}

/*
 * Query control information (size and flags) for XU controls.
 */
static int uvc_ctrl_fill_xu_info(struct uvc_device *dev,
    const struct uvc_control *ctrl, struct uvc_control_info *info)
{
    u8 *data;
    int ret;
    data = rtw_malloc(2);

    if (data == NULL) {
        return -USB_ENOMEM;
    }

    rtw_memcpy(info->entity, ctrl->entity->extension.guidExtensionCode,
        sizeof(info->entity));
    info->index = ctrl->index;
    info->selector = ctrl->index + 1;
    /* Query and verify the control length (GET_LEN) */
    ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id, dev->intfnum,
            info->selector, data, 2);

    if (ret < 0) {
        UVC_ERROR("GET_LEN failed on control %pUl/%d (%d).\n", info->entity, info->selector, ret);
        goto done;
    }

    info->size = le16_to_cpup((__le16 *)data);
    /* Query the control information (GET_INFO) */
    ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, dev->intfnum,
            info->selector, data, 1);

    if (ret < 0) {
        UVC_ERROR("GET_INFO failed on control %pUl/%d (%d).\n", info->entity, info->selector, ret);
        goto done;
    }

    info->flags = UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX
        | UVC_CTRL_FLAG_GET_RES | UVC_CTRL_FLAG_GET_DEF
        | (data[0] & UVC_CONTROL_CAP_GET ?
            UVC_CTRL_FLAG_GET_CUR : 0)
        | (data[0] & UVC_CONTROL_CAP_SET ?
            UVC_CTRL_FLAG_SET_CUR : 0)
        | (data[0] & UVC_CONTROL_CAP_AUTOUPDATE ?
            UVC_CTRL_FLAG_AUTO_UPDATE : 0);

    UVC_PRINTF("XU control %pUl/%d queried: len %d, flags { get %d set %d auto %d }.\n",  info->entity, info->selector,
        info->size, (info->flags & UVC_CTRL_FLAG_GET_CUR) ? 1 : 0, (info->flags & UVC_CTRL_FLAG_SET_CUR) ? 1 : 0,
        (info->flags & UVC_CTRL_FLAG_AUTO_UPDATE) ? 1 : 0);
done:
    rtw_free(data);
    return ret;
}

static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl,
    const struct uvc_control_info *info);

static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev,
    struct uvc_control *ctrl)
{
    struct uvc_control_info info;
    int ret;

    if (ctrl->initialized) {
        return 0;
    }

    ret = uvc_ctrl_fill_xu_info(dev, ctrl, &info);
    if (ret < 0) {
        return ret;
    }

    ret = uvc_ctrl_add_info(dev, ctrl, &info);
    if (ret < 0) {
        UVC_ERROR("Failed to initialize control %pUl/%d on device %s entity %d\n", info.entity, info.selector,
            dev->udev->devnum, ctrl->entity->id);
    }

    return ret;
}

int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
    struct uvc_xu_control_query *xqry)
{
    struct uvc_entity *entity;
    struct uvc_control *ctrl;
    unsigned int i, found = 0;
    __u32 reqflags;
    __u16 size;
    __u8 *data = NULL;
    int ret;
    
    /* Find the extension unit. */
    list_for_each_entry(entity, &chain->entities, chain, struct uvc_entity) {
        if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT &&
            entity->id == xqry->unit) {
            break;
        }
    }

    if (entity->id != xqry->unit) {
        UVC_ERROR("Extension unit %d not found.\n", xqry->unit);
        return -USB_ENOENT;
    }

    /* Find the control and perform delayed initialization if needed. */
    for (i = 0; i < entity->ncontrols; ++i) {
        ctrl = &entity->controls[i];

        if (ctrl->index == xqry->selector - 1) {
            found = 1;
            break;
        }
    }

    if (!found) {
        UVC_ERROR("Control %pUl/%d not found.\n", entity->extension.guidExtensionCode, xqry->selector);
        return -USB_ENOENT;
    }

    Mutex_Lock(&chain->ctrl_mutex);
    
    ret = uvc_ctrl_init_xu_ctrl(chain->dev, ctrl);
    if (ret < 0) {
        ret = -USB_ENOENT;
        goto done;
    }

    /* Validate the required buffer size and flags for the request */
    reqflags = 0;
    size = ctrl->info.size;

    switch (xqry->query) {
        case UVC_GET_CUR:
            reqflags = UVC_CTRL_FLAG_GET_CUR;
            break;

        case UVC_GET_MIN:
            reqflags = UVC_CTRL_FLAG_GET_MIN;
            break;

        case UVC_GET_MAX:
            reqflags = UVC_CTRL_FLAG_GET_MAX;
            break;

        case UVC_GET_DEF:
            reqflags = UVC_CTRL_FLAG_GET_DEF;
            break;

        case UVC_GET_RES:
            reqflags = UVC_CTRL_FLAG_GET_RES;
            break;

        case UVC_SET_CUR:
            reqflags = UVC_CTRL_FLAG_SET_CUR;
            break;

        case UVC_GET_LEN:
            size = 2;
            break;

        case UVC_GET_INFO:
            size = 1;
            break;

        default:
            ret = -USB_EINVAL;
            goto done;
    }

    if (size != xqry->size) {
        ret = -USB_ENOBUFS;
        goto done;
    }

    if (reqflags && !(ctrl->info.flags & reqflags)) {
        ret = -USB_EBADRQC;
        goto done;
    }

    data = rtw_malloc(size);
    if (data == NULL) {
        ret = -USB_ENOMEM;
        goto done;
    }

    if (xqry->query == UVC_SET_CUR &&
        !copy_from_user((void *)data, (void *)xqry->data, size)) {
        ret = -USB_EFAULT;
        goto done;
    }

    ret = uvc_query_ctrl(chain->dev, xqry->query, xqry->unit,
            chain->dev->intfnum, xqry->selector, data, size);
    if (ret < 0) {
        goto done;
    }

    if (xqry->query != UVC_SET_CUR &&
        !copy_to_user((void *)xqry->data, (void *)data, size)) {
        ret = -USB_EFAULT;
    }

done:
    rtw_free(data);
    Mutex_Unlock(&chain->ctrl_mutex);
    return ret;
}

/* --------------------------------------------------------------------------
 * Suspend/resume
 */

/*
 * Restore control values after resume, skipping controls that haven't been
 * changed.
 *
 * TODO
 * - Don't restore modified controls that are back to their default value.
 * - Handle restore order (Auto-Exposure Mode should be restored before
 *   Exposure Time).
 */
int uvc_ctrl_resume_device(struct uvc_device *dev)
{
    struct uvc_control *ctrl;
    struct uvc_entity *entity;
    unsigned int i;
    int ret;
    /* Walk the entities list and restore controls when possible. */
    list_for_each_entry(entity, &dev->entities, list, struct uvc_entity) {
        for (i = 0; i < entity->ncontrols; ++i) {
            ctrl = &entity->controls[i];

            if (!ctrl->initialized || !ctrl->modified ||
                (ctrl->info.flags & UVC_CTRL_FLAG_RESTORE) == 0) {
                continue;
            }

            UVC_ERROR("restoring control %pUl/%d/%d\n", ctrl->info.entity, ctrl->info.index, ctrl->info.selector);
            ctrl->dirty = 1;
        }

        ret = uvc_ctrl_commit_entity(dev, entity, 0);
        if (ret < 0) {
            return ret;
        }
    }
    return 0;
}

/* --------------------------------------------------------------------------
 * Control and mapping handling
 */

/*
 * Add control information to a given control.
 */
static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl,
    const struct uvc_control_info *info)
{
    int ret = 0;
    
    UNUSED(dev);
    
    ctrl->info = *info;
    INIT_LIST_HEAD(&ctrl->info.mappings);
    
    /* Allocate an array to save control values (cur, def, max, etc.) */
    /* edit by Ian -- allocate enough size for buffer DWORD alignment */
    ctrl->uvc_data = rtw_zmalloc((ctrl->info.size + (4 - ctrl->info.size % 4)) * UVC_CTRL_DATA_LAST + 1);
    if (ctrl->uvc_data == NULL) {
        ret = -USB_ENOMEM;
        goto done;
    }

    ctrl->initialized = 1;
    UVC_PRINTF("Added control %d/0x%x to device %d entity %d\n", ctrl->info.index, ctrl->info.selector, dev->udev->devnum,
        ctrl->entity->id);
done:

    if (ret < 0) {
        rtw_free(ctrl->uvc_data);
    }

    return ret;
}

/*
 * Add a control mapping to a given control.
 */
/*  mapping
{
    .id     = V4L2_CID_BRIGHTNESS,
    .name       = "Brightness",
    .entity     = UVC_GUID_UVC_PROCESSING,
    .selector   = UVC_PU_BRIGHTNESS_CONTROL,
    .size       = 16,
    .offset     = 0,
    .v4l2_type  = V4L2_CTRL_TYPE_INTEGER,
    .data_type  = UVC_CTRL_DATA_TYPE_SIGNED,
},
*/
static int __uvc_ctrl_add_mapping(struct uvc_device *dev,
    struct uvc_control *ctrl, const struct uvc_control_mapping *mapping)
{
    struct uvc_control_mapping *map;
    unsigned int size;
    
    UNUSED(dev);
    
    /* Most mappings come from static kernel data and need to be duplicated.
     * Mappings that come from userspace will be unnecessarily duplicated,
     * this could be optimized.
     */
    // copy sizeof(*mapping) bytes data from the address which mapping
    // pointer to then return the buffer address.
    map = kmemdup(mapping, sizeof(*mapping), GFP_KERNEL);

    if (map == NULL) {
        return -USB_ENOMEM;
    }

    INIT_LIST_HEAD(&map->ev_subs);

    //          8      *    0 ????
    /*edit by Ian -- somehow some mappings don't have menu_info*/
    if (mapping->v4l2_type ==  V4L2_CTRL_TYPE_MENU) {
        size = sizeof(*mapping->menu_info) * mapping->menu_count;
        map->menu_info = kmemdup(mapping->menu_info, size, GFP_KERNEL);

        if (map->menu_info == NULL) {
            rtw_free((unsigned char *)map);
            return -USB_ENOMEM;
        }
    }

    // settinig the menu's call back function about get/set
    if (map->get == NULL) {
        map->get = uvc_get_le_value;
    }

    if (map->set == NULL) {
        map->set = uvc_set_le_value;
    }

    list_add_tail(&map->list, &ctrl->info.mappings);
    UVC_PRINTF("Adding mapping '%s' to control %d/0x%x.\n", map->name, ctrl->info.index, ctrl->info.selector);
    return 0;
}

int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
    const struct uvc_control_mapping *mapping)
{
    struct uvc_device *dev = chain->dev;
    struct uvc_control_mapping *map;
    struct uvc_entity *entity;
    struct uvc_control *ctrl;
    int found = 0;
    int ret;

    if (mapping->id & ~V4L2_CTRL_ID_MASK) {
        UVC_PRINTF("Can't add mapping '%s', control id 0x%08x is invalid.\n", mapping->name, mapping->id);
        return -USB_EINVAL;
    }

    /* Search for the matching (GUID/CS) control on the current chain */
    list_for_each_entry(entity, &chain->entities, chain, struct uvc_entity) {
        unsigned int i;
        if (!uvc_entity_match_guid(entity, mapping->entity)) {
            continue;
        }

        for (i = 0; i < entity->ncontrols; ++i) {
            ctrl = &entity->controls[i];
            if (ctrl->initialized && ctrl->info.selector == mapping->selector) {
                found = 1;
                break;
            }

            if (ctrl->index == mapping->selector - 1) {
                found = 1;
                break;
            }
        }

        if (found) {
            break;
        }
    }

    if (!found) {
        return -USB_ENOENT;
    }

    Mutex_Lock(&chain->ctrl_mutex);
    /* Perform delayed initialization of XU controls */
    ret = uvc_ctrl_init_xu_ctrl(dev, ctrl);

    if (ret < 0) {
        ret = -USB_ENOENT;
        goto done;
    }

    list_for_each_entry(map, &ctrl->info.mappings, list, struct uvc_control_mapping) {
        if (mapping->id == map->id) {
            UVC_PRINTF("Can't add mapping '%s', control id 0x%08x already exists.\n", mapping->name, mapping->id);
            ret = -USB_EEXIST;
            goto done;
        }
    }

    /* Prevent excess memory consumption */
    if (atomic_inc_return(&dev->nmappings) > UVC_MAX_CONTROL_MAPPINGS) {
        atomic_dec(&dev->nmappings);
        UVC_PRINTF("Can't add mapping '%s', maximum mappings count (%d) exceeded.\n", mapping->name, UVC_MAX_CONTROL_MAPPINGS);
        ret = -USB_ENOMEM;
        goto done;
    }

    ret = __uvc_ctrl_add_mapping(dev, ctrl, mapping);

    if (ret < 0) {
        atomic_dec(&dev->nmappings);
    }

done:
    Mutex_Unlock(&chain->ctrl_mutex);
    return ret;
}

/*
 * Add control information and hardcoded stock control mappings to the given
 * device.
 */
static void uvc_ctrl_init_ctrl(struct uvc_device *dev, struct uvc_control *ctrl)
{
    const struct uvc_control_info *info = uvc_ctrls;
    const struct uvc_control_info *iend = info + ARRAY_SIZE(uvc_ctrls);
    const struct uvc_control_mapping *mapping = uvc_ctrl_mappings;
    const struct uvc_control_mapping *mend = mapping + ARRAY_SIZE(uvc_ctrl_mappings);

    /* XU controls initialization requires querying the device for control
     * information. As some buggy(i.e car's)UVC devices will crash when queried
     * repeatedly in a tight loop, delay XU controls initialization until
     * first use.
     */
    if (UVC_ENTITY_TYPE(ctrl->entity) == UVC_VC_EXTENSION_UNIT) {
        return;
    }

    // for processing : index = 0,1,2,3,4,5,6,8,10,12
    for (; info < iend; ++info) {
        // ctrl->entity(process) ,      ctrl->index = 0,1,2,3,4,5,6,8,10,12 *match if the same control in the same entity
        if (uvc_entity_match_guid(ctrl->entity, info->entity) && ctrl->index == info->index) {
            uvc_ctrl_add_info(dev, ctrl, info);
            break;
        }
    }

    if (!ctrl->initialized) {
        return;
    }

    /* */
    for (; mapping < mend; ++mapping) {
        if (uvc_entity_match_guid(ctrl->entity, mapping->entity) && ctrl->info.selector == mapping->selector) {
            __uvc_ctrl_add_mapping(dev, ctrl, mapping);
        }
    }
}

/*
 * Initialize device controls.
 */
int uvc_ctrl_init_device(struct uvc_device *dev)
{
    struct uvc_entity *entity;
    unsigned int i;
    
    /* Walk the entities list and instantiate controls */
    list_for_each_entry(entity, &dev->entities, list, struct uvc_entity) {
        struct uvc_control *ctrl;
        unsigned int bControlSize = 0, ncontrols = 0;
        __u8 *bmControls = NULL;

        if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT) {
            // pointer to the bmControls buffer base address
            // {0xFF,0xFF,0xFF}
            UVC_PRINTF("UVC_VC_EXTENSION_UNIT\n");
            bmControls = entity->extension.bmControls;
            // get the bControlSize value 0x03(3) which means the size of
            // the bmControls field,in bytes is 3.
            bControlSize = entity->extension.bControlSize;
        } else if (UVC_ENTITY_TYPE(entity) == UVC_VC_PROCESSING_UNIT) {
            // pointer to the bmControls buffer base address
            // {0x7F,0x15}
            UVC_PRINTF("UVC_VC_PROCESSING_UNIT\n");
            bmControls = entity->processing.bmControls;
            // bControlSize = 0x02
            bControlSize = entity->processing.bControlSize;
        } else if (UVC_ENTITY_TYPE(entity) == UVC_ITT_CAMERA) {
            // pointer to the bmControls buffer base address
            // {0x00,0x00,0x00}
            UVC_PRINTF("UVC_ITT_CAMERA\n");
            bmControls = entity->camera.bmControls;
            // bControlSize = 0x03
            bControlSize = entity->camera.bControlSize;
        }

        /* Remove bogus(i.e fake)/black_listed controls */
        // It only prune the UVC_VC_PROCESSING_UNIT and UVC_ITT_CAMERA.
        /* edit by Ian -- ignore this operation to save code size*/
        //uvc_ctrl_prune_entity(dev, entity);
        /* Count supported controls and allocate the controls array */
        /**
        * memweight - count the total number of bits set in memory area
        * @ptr: pointer to the start of the area
        * @bytes: the size of the area
        */
        //extension: 24 ?? ; process: 10 ; itt: 0 ??
        ncontrols = memweight(bmControls, bControlSize);
        UVC_PRINTF("number of bits set in memory area:%d", ncontrols);

        if (ncontrols == 0) {
            continue;
        }

        entity->controls = kcalloc(ncontrols, sizeof(*ctrl), GFP_KERNEL);
        if (entity->controls == NULL) {
            return -USB_ENOMEM;
        }

        // pointer to the struct which allocate to operate the control
        entity->ncontrols = ncontrols;
        /* Initialize all supported controls */
        ctrl = entity->controls;

        // extension:3,{0xFF,0xFF,0xFF} ; process:2,{0x7F,0x15} ; itt:3,{0x00,0x00,0x00}
        // bControlSize * 8 : total bits in bControls pointer buffer
        for (i = 0; i < bControlSize * 8; ++i) {
            // process: bmControls = {0x7F,0x15}, i = 0 ~ 15
            if (uvc_test_bit(bmControls, i) == 0) {
                continue;
            }

            ctrl->entity = entity;
            // for extension: i = 0 ~ 23 ????
            // for process: i = 0,1,2,3,4,5,6,8,10,12
            // for itt: not into this
            ctrl->index = i;
            uvc_ctrl_init_ctrl(dev, ctrl);
            ctrl++;
        }
    }
    
    return 0;
}

/*
 * Cleanup device controls.
 */
static void uvc_ctrl_cleanup_mappings(struct uvc_device *dev, struct uvc_control *ctrl)
{
    struct uvc_control_mapping *mapping, *nm;

    UNUSED(dev);
    
    list_for_each_entry_safe(mapping, nm, &ctrl->info.mappings, list, struct uvc_control_mapping) {
        list_del(&mapping->list);

        if (mapping->menu_info != NULL) {
            rtw_free((unsigned char *)mapping->menu_info);
        }

        rtw_free((unsigned char *)mapping);
    }
}

void uvc_ctrl_cleanup_device(struct uvc_device *dev)
{
    struct uvc_entity *entity;
    unsigned int i;
    
    /* Free controls and control mappings for all entities. */
    list_for_each_entry(entity, &dev->entities, list, struct uvc_entity) {
        for (i = 0; i < entity->ncontrols; ++i) {
            struct uvc_control *ctrl = &entity->controls[i];

            if (!ctrl->initialized) {
                continue;
            }

            uvc_ctrl_cleanup_mappings(dev, ctrl);
            rtw_free(ctrl->uvc_data);
        }

        rtw_free((unsigned char *)entity->controls);
    }
}

#endif // CONFIG_USBH_UVC
