#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
#include <sys/proc.h>
#include <sys/types.h>
#include <sys/bus.h>
#include <sys/lock.h>
#include <sys/condvar.h>
#include <sys/malloc.h>
#include <sys/conf.h>
#include <sys/fcntl.h>
#include <bus/u4b/usb.h>
#define USB_DEBUG_VAR uvc_debug
#include <bus/u4b/usb_debug.h>
#include <bus/u4b/usbdi.h>
#include <contrib/v4l/videodev.h>
#include <contrib/v4l/videodev2.h>
#include "uvc_drv.h"
#include "uvc_buf.h"
#include "uvc_v4l2.h"
#define UVC_LOCK(lkp) lockmgr(lkp, LK_EXCLUSIVE)
#define UVC_UNLOCK(lkp) lockmgr(lkp, LK_RELEASE)
#define STAILQ_FOREACH_SAFE STAILQ_FOREACH_MUTABLE
#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
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#define UVC_BITMASK 0x7
#define UVC_BITSHIFT 0x3
#define UVC_VALMASK 0x1
static unsigned int
uvc_test_bit(const uint8_t *buf, int b)
{
return (buf[b >> UVC_BITSHIFT] >> (b & UVC_BITMASK)) & UVC_VALMASK;
}
static int
uvc_ctrl_init_sub_info(struct uvc_ctrl_info *info,
struct uvc_ctrl_sub_info *sub_info)
{
int ret = 0;
enum v4l2_ctrl_type type = V4L2_CTRL_TYPE_INTEGER;
uint8_t bit_offset = 0;
uint8_t bit_size = 0;
uint8_t name[32] = { 0 };
uint32_t superior_id = 0;
uint32_t superior_manual = 0;
uint32_t inferior_ids[2] = { 0 };
uint32_t id = sub_info->v4l2_id;
if (info->topo_type == UVC_TOPO_TYPE_PROCESSING_UNIT) {
switch (id) {
case V4L2_CID_BRIGHTNESS:
ksnprintf(name, sizeof(name), "Brightness");
bit_size = 16;
break;
case V4L2_CID_CONTRAST:
ksnprintf(name, sizeof(name), "Contrast");
bit_size = 16;
break;
case V4L2_CID_HUE:
ksnprintf(name, sizeof(name), "Hue");
bit_size = 16;
superior_id = V4L2_CID_HUE_AUTO;
break;
case V4L2_CID_SATURATION:
ksnprintf(name, sizeof(name), "Saturation");
bit_size = 16;
break;
case V4L2_CID_SHARPNESS:
ksnprintf(name, sizeof(name), "Sharpness");
bit_size = 16;
break;
case V4L2_CID_GAMMA:
ksnprintf(name, sizeof(name), "Gamma");
bit_size = 16;
break;
case V4L2_CID_BACKLIGHT_COMPENSATION:
ksnprintf(name, sizeof(name), "Backlight Compensation");
bit_size = 16;
break;
case V4L2_CID_GAIN:
ksnprintf(name, sizeof(name), "Gain");
bit_size = 16;
break;
case V4L2_CID_POWER_LINE_FREQUENCY:
ksnprintf(name, sizeof(name), "Power Line Frequency");
type = V4L2_CTRL_TYPE_MENU;
bit_size = 2;
break;
case V4L2_CID_HUE_AUTO:
ksnprintf(name, sizeof(name), "Hue, Auto");
type = V4L2_CTRL_TYPE_BOOLEAN;
bit_size = 1;
sub_info->inferior_v4l2_ids[0] = V4L2_CID_HUE;
break;
case V4L2_CID_AUTO_WHITE_BALANCE:
if (info->selector ==
UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL) {
ksnprintf(name, sizeof(name), "White Balance Temperature, Auto");
type = V4L2_CTRL_TYPE_BOOLEAN;
bit_size = 1;
inferior_ids[0] =
V4L2_CID_WHITE_BALANCE_TEMPERATURE;
} else if (info->selector ==
UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL) {
ksnprintf(name, sizeof(name), "White Balance Component, Auto");
type = V4L2_CTRL_TYPE_BOOLEAN;
bit_size = 1;
inferior_ids[0] = V4L2_CID_BLUE_BALANCE;
inferior_ids[1] = V4L2_CID_RED_BALANCE;
} else {
ret = EINVAL;
}
break;
case V4L2_CID_WHITE_BALANCE_TEMPERATURE:
ksnprintf(name, sizeof(name), "White Balance Temperature");
bit_size = 16;
superior_id = V4L2_CID_AUTO_WHITE_BALANCE;
break;
case V4L2_CID_BLUE_BALANCE:
ksnprintf(name, sizeof(name), "White Balance Blue Component");
bit_size = 16;
superior_id = V4L2_CID_AUTO_WHITE_BALANCE;
break;
case V4L2_CID_RED_BALANCE:
ksnprintf(name, sizeof(name), "White Balance Red Component");
bit_offset = 16;
bit_size = 16;
superior_id = V4L2_CID_AUTO_WHITE_BALANCE;
break;
default:
ret = EINVAL;
goto err_done;
break;
}
} else if (info->topo_type == UVC_TOPO_TYPE_CAMERA_TERMINAL) {
switch (id) {
case V4L2_CID_EXPOSURE_AUTO:
ksnprintf(name, sizeof(name), "Exposure, Auto");
type = V4L2_CTRL_TYPE_MENU;
bit_size = 4;
inferior_ids[0] = V4L2_CID_EXPOSURE_ABSOLUTE;
break;
case V4L2_CID_EXPOSURE_AUTO_PRIORITY:
ksnprintf(name, sizeof(name), "Exposure, Auto Priority");
type = V4L2_CTRL_TYPE_BOOLEAN;
bit_size = 1;
break;
case V4L2_CID_EXPOSURE_ABSOLUTE:
ksnprintf(name, sizeof(name), "Exposure (Absolute)");
bit_size = 32;
superior_id = V4L2_CID_EXPOSURE_AUTO;
superior_manual = V4L2_EXPOSURE_MANUAL;
break;
case V4L2_CID_FOCUS_ABSOLUTE:
ksnprintf(name, sizeof(name), "Focus (absolute)");
bit_size = 16;
superior_id = V4L2_CID_FOCUS_AUTO;
break;
case V4L2_CID_FOCUS_AUTO:
ksnprintf(name, sizeof(name), "Focus, Auto");
type = V4L2_CTRL_TYPE_BOOLEAN;
bit_size = 1;
inferior_ids[0] = V4L2_CID_FOCUS_ABSOLUTE;
break;
case V4L2_CID_IRIS_ABSOLUTE:
ksnprintf(name, sizeof(name), "Iris, Absolute");
bit_size = 16;
break;
case V4L2_CID_IRIS_RELATIVE:
ksnprintf(name, sizeof(name), "Iris, Relative");
bit_size = 8;
break;
case V4L2_CID_ZOOM_ABSOLUTE:
ksnprintf(name, sizeof(name), "Zoom, Absolute");
bit_size = 16;
break;
case V4L2_CID_ZOOM_CONTINUOUS:
ksnprintf(name, sizeof(name), "Zoom, Continuous");
bit_size = 0;
break;
case V4L2_CID_PAN_ABSOLUTE:
ksnprintf(name, sizeof(name), "Pan (Absolute)");
bit_size = 32;
break;
case V4L2_CID_TILT_ABSOLUTE:
ksnprintf(name, sizeof(name), "Tilt (Absolute)");
bit_offset = 32;
bit_size = 32;
break;
case V4L2_CID_PAN_SPEED:
ksnprintf(name, sizeof(name), "Pan (Speed)");
bit_size = 16;
break;
case V4L2_CID_TILT_SPEED:
ksnprintf(name, sizeof(name), "Tilt (Speed)");
bit_offset = 16;
bit_size = 16;
break;
case V4L2_CID_PRIVACY:
ksnprintf(name, sizeof(name), "Privacy");
type = V4L2_CTRL_TYPE_BOOLEAN;
bit_size = 1;
break;
default:
ret = EINVAL;
goto err_done;
break;
}
}
strcpy(sub_info->v4l2_name, name);
sub_info->v4l2_type = type;
sub_info->bit_offset = bit_offset;
sub_info->bit_size = bit_size;
sub_info->superior_v4l2_id = superior_id;
sub_info->superior_manual = superior_manual;
sub_info->inferior_v4l2_ids[0] = inferior_ids[0];
sub_info->inferior_v4l2_ids[1] = inferior_ids[1];
return 0;
err_done:
kprintf("%s:%d Unknown topo 0x%x, selector 0x%x, v4l2 id 0x%x\n",
__func__, __LINE__, info->topo_type, info->selector, id);
return ret;
}
static int
uvc_ctrl_get_info_ct(uint8_t bit_idx, struct uvc_ctrl_info *info)
{
int ret = 0;
memset(info, 0, sizeof(*info));
info->topo_type = UVC_TOPO_TYPE_CAMERA_TERMINAL;
info->index = bit_idx;
switch (bit_idx) {
case 0:
info->selector = UVC_CT_SCANNING_MODE_CONTROL;
info->byte_size = 1;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_CUR |
UVC_CTRL_RESTORE;
break;
case 1:
info->selector = UVC_CT_AE_MODE_CONTROL;
info->byte_size = 1;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_CUR |
UVC_CTRL_GET_DEF | UVC_CTRL_GET_RES | UVC_CTRL_RESTORE;
info->data_type = UVC_CTRL_DATA_BITMASK;
break;
case 2:
info->selector = UVC_CT_AE_PRIORITY_CONTROL;
info->byte_size = 1;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_CUR |
UVC_CTRL_RESTORE;
info->data_type = UVC_CTRL_DATA_BOOLEAN;
break;
case 3:
info->selector = UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL;
info->byte_size = 4;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
UVC_CTRL_RESTORE | UVC_CTRL_AUTO_UPDATE;
info->data_type = UVC_CTRL_DATA_UNSIGNED;
break;
case 4:
info->selector = UVC_CT_EXPOSURE_TIME_RELATIVE_CONTROL;
info->byte_size = 1;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_RESTORE;
break;
case 5:
info->selector = UVC_CT_FOCUS_ABSOLUTE_CONTROL;
info->byte_size = 2;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
UVC_CTRL_RESTORE | UVC_CTRL_AUTO_UPDATE;
info->data_type = UVC_CTRL_DATA_UNSIGNED;
break;
case 6:
info->selector = UVC_CT_FOCUS_RELATIVE_CONTROL;
info->byte_size = 2;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_MIN |
UVC_CTRL_GET_MAX | UVC_CTRL_GET_RES | UVC_CTRL_GET_DEF |
UVC_CTRL_AUTO_UPDATE;
break;
case 7:
info->selector = UVC_CT_IRIS_ABSOLUTE_CONTROL;
info->byte_size = 2;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
UVC_CTRL_RESTORE | UVC_CTRL_AUTO_UPDATE;
info->data_type = UVC_CTRL_DATA_UNSIGNED;
break;
case 8:
info->selector = UVC_CT_IRIS_RELATIVE_CONTROL;
info->byte_size = 1;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_AUTO_UPDATE;
info->data_type = UVC_CTRL_DATA_SIGNED;
break;
case 9:
info->selector = UVC_CT_ZOOM_ABSOLUTE_CONTROL;
info->byte_size = 2;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
UVC_CTRL_RESTORE | UVC_CTRL_AUTO_UPDATE;
info->data_type = UVC_CTRL_DATA_UNSIGNED;
break;
case 10:
info->selector = UVC_CT_ZOOM_RELATIVE_CONTROL;
info->byte_size = 3;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_MIN |
UVC_CTRL_GET_MAX | UVC_CTRL_GET_RES | UVC_CTRL_GET_DEF |
UVC_CTRL_AUTO_UPDATE;
info->data_type = UVC_CTRL_DATA_SIGNED;
break;
case 11:
info->selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL;
info->byte_size = 8;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
UVC_CTRL_RESTORE | UVC_CTRL_AUTO_UPDATE;
info->data_type = UVC_CTRL_DATA_SIGNED;
break;
case 12:
info->selector = UVC_CT_PANTILT_RELATIVE_CONTROL;
info->byte_size = 4;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
UVC_CTRL_AUTO_UPDATE;
info->data_type = UVC_CTRL_DATA_SIGNED;
break;
case 13:
info->selector = UVC_CT_ROLL_ABSOLUTE_CONTROL;
info->byte_size = 2;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
UVC_CTRL_RESTORE | UVC_CTRL_AUTO_UPDATE;
break;
case 14:
info->selector = UVC_CT_ROLL_RELATIVE_CONTROL;
info->byte_size = 2;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_MIN |
UVC_CTRL_GET_MAX | UVC_CTRL_GET_RES | UVC_CTRL_GET_DEF |
UVC_CTRL_AUTO_UPDATE;
break;
case 17:
info->selector = UVC_CT_FOCUS_AUTO_CONTROL;
info->byte_size = 1;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_CUR |
UVC_CTRL_GET_DEF | UVC_CTRL_RESTORE;
info->data_type = UVC_CTRL_DATA_BOOLEAN;
break;
case 18:
info->selector = UVC_CT_PRIVACY_CONTROL;
info->byte_size = 1;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_CUR |
UVC_CTRL_RESTORE | UVC_CTRL_AUTO_UPDATE;
info->data_type = UVC_CTRL_DATA_BOOLEAN;
break;
default:
ret = EINVAL;
break;
}
if (ret != 0) {
memset(info, 0, sizeof(*info));
}
return ret;
}
static int
uvc_ctrl_get_info_pu(uint8_t bit_idx, struct uvc_ctrl_info *info)
{
int ret = 0;
memset(info, 0, sizeof(*info));
info->topo_type = UVC_TOPO_TYPE_PROCESSING_UNIT;
info->index = bit_idx;
switch (bit_idx) {
case 0:
info->selector = UVC_PU_BRIGHTNESS_CONTROL;
info->byte_size = 2;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
UVC_CTRL_RESTORE;
info->data_type = UVC_CTRL_DATA_SIGNED;
break;
case 1:
info->selector = UVC_PU_CONTRAST_CONTROL;
info->byte_size = 2;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
UVC_CTRL_RESTORE;
info->data_type = UVC_CTRL_DATA_UNSIGNED;
break;
case 2:
info->selector = UVC_PU_HUE_CONTROL;
info->byte_size = 2;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
UVC_CTRL_RESTORE | UVC_CTRL_AUTO_UPDATE;
info->data_type = UVC_CTRL_DATA_SIGNED;
break;
case 3:
info->selector = UVC_PU_SATURATION_CONTROL;
info->byte_size = 2;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
UVC_CTRL_RESTORE;
info->data_type = UVC_CTRL_DATA_UNSIGNED;
break;
case 4:
info->selector = UVC_PU_SHARPNESS_CONTROL;
info->byte_size = 2;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
UVC_CTRL_RESTORE;
info->data_type = UVC_CTRL_DATA_UNSIGNED;
break;
case 5:
info->selector = UVC_PU_GAMMA_CONTROL;
info->byte_size = 2;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
UVC_CTRL_RESTORE;
info->data_type = UVC_CTRL_DATA_UNSIGNED;
break;
case 6:
info->selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL;
info->byte_size = 2;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
UVC_CTRL_RESTORE | UVC_CTRL_AUTO_UPDATE;
info->data_type = UVC_CTRL_DATA_UNSIGNED;
break;
case 7:
info->selector = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL;
info->byte_size = 4;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
UVC_CTRL_RESTORE | UVC_CTRL_AUTO_UPDATE;
info->data_type = UVC_CTRL_DATA_SIGNED;
break;
case 8:
info->selector = UVC_PU_BACKLIGHT_COMPENSATION_CONTROL;
info->byte_size = 2;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
UVC_CTRL_RESTORE;
info->data_type = UVC_CTRL_DATA_UNSIGNED;
break;
case 9:
info->selector = UVC_PU_GAIN_CONTROL;
info->byte_size = 2;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
UVC_CTRL_RESTORE;
info->data_type = UVC_CTRL_DATA_UNSIGNED;
break;
case 10:
info->selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL;
info->byte_size = 1;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_CUR |
UVC_CTRL_GET_DEF | UVC_CTRL_RESTORE;
info->data_type = UVC_CTRL_DATA_ENUM;
break;
case 11:
info->selector = UVC_PU_HUE_AUTO_CONTROL;
info->byte_size = 1;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_CUR |
UVC_CTRL_GET_DEF | UVC_CTRL_RESTORE;
info->data_type = UVC_CTRL_DATA_BOOLEAN;
break;
case 12:
info->selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL;
info->byte_size = 1;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_CUR |
UVC_CTRL_GET_DEF | UVC_CTRL_RESTORE;
info->data_type = UVC_CTRL_DATA_BOOLEAN;
break;
case 13:
info->selector = UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL;
info->byte_size = 1;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_CUR |
UVC_CTRL_GET_DEF | UVC_CTRL_RESTORE;
info->data_type = UVC_CTRL_DATA_BOOLEAN;
break;
case 14:
info->selector = UVC_PU_DIGITAL_MULTIPLIER_CONTROL;
info->byte_size = 2;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
UVC_CTRL_RESTORE;
break;
case 15:
info->selector = UVC_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL;
info->byte_size = 2;
info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
UVC_CTRL_RESTORE;
break;
case 16:
info->selector = UVC_PU_ANALOG_VIDEO_STANDARD_CONTROL;
info->byte_size = 1;
info->flags = UVC_CTRL_GET_CUR;
break;
case 17:
info->selector = UVC_PU_ANALOG_LOCK_STATUS_CONTROL;
info->byte_size = 1;
info->flags = UVC_CTRL_GET_CUR;
break;
default:
ret = EINVAL;
break;
}
if (ret != 0) {
memset(info, 0, sizeof(*info));
}
return ret;
}
static const struct uvc_ctrl_menu_item exposure_mode_menu[] = {
{ 2, "Auto Mode" },
{ 1, "Manual Mode" },
{ 4, "Shutter Priority Mode" },
{ 8, "Aperture Priority Mode" },
};
static const struct uvc_ctrl_menu_item power_line_freq_menu[] = {
{ 0, "Disabled" },
{ 1, "50 Hz" },
{ 2, "60 Hz" },
};
static int
uvc_ctrl_init_menu(struct uvc_ctrl_info *info)
{
const struct uvc_ctrl_menu_item *src_menu_data = NULL;
struct uvc_ctrl_menu_item *dst_menu_data = NULL;
uint32_t item_num = 0;
uint32_t len = 0;
if (info->topo_type == UVC_TOPO_TYPE_CAMERA_TERMINAL &&
info->selector == UVC_CT_AE_MODE_CONTROL) {
src_menu_data = exposure_mode_menu;
item_num = ARRAY_SIZE(exposure_mode_menu);
} else if (info->topo_type == UVC_TOPO_TYPE_PROCESSING_UNIT &&
info->selector == UVC_PU_POWER_LINE_FREQUENCY_CONTROL) {
src_menu_data = power_line_freq_menu;
item_num = ARRAY_SIZE(power_line_freq_menu);
} else {
return 0;
}
len = item_num * sizeof(struct uvc_ctrl_menu_item);
dst_menu_data = kmalloc(len, M_UVC, M_ZERO | M_WAITOK);
if (dst_menu_data == NULL) {
kprintf("%s:%d fail to alloc mem\n", __func__, __LINE__);
return ENOMEM;
}
memcpy(dst_menu_data, src_menu_data, len);
info->menus[0].item_num = item_num;
info->menus[0].menu_data = dst_menu_data;
return 0;
}
static void
uvc_ctrl_init_sub_info_num(struct uvc_ctrl_info *info)
{
info->sub_info_num = 1;
if (info->topo_type == UVC_TOPO_TYPE_CAMERA_TERMINAL) {
switch (info->selector) {
case UVC_CT_PANTILT_ABSOLUTE_CONTROL:
case UVC_CT_PANTILT_RELATIVE_CONTROL:
info->sub_info_num = 2;
break;
}
} else if (info->topo_type == UVC_TOPO_TYPE_PROCESSING_UNIT) {
switch (info->selector) {
case UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL:
info->sub_info_num = 2;
break;
}
}
}
static int
uvc_ctrl_build_mapping_ct(struct uvc_ctrl_info *info)
{
int ret = 0;
switch (info->selector) {
case UVC_CT_AE_MODE_CONTROL:
info->sub_infos[0]->v4l2_id = V4L2_CID_EXPOSURE_AUTO;
break;
case UVC_CT_AE_PRIORITY_CONTROL:
info->sub_infos[0]->v4l2_id =
V4L2_CID_EXPOSURE_AUTO_PRIORITY;
break;
case UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL:
info->sub_infos[0]->v4l2_id = V4L2_CID_EXPOSURE_ABSOLUTE;
break;
case UVC_CT_FOCUS_ABSOLUTE_CONTROL:
info->sub_infos[0]->v4l2_id = V4L2_CID_FOCUS_ABSOLUTE;
break;
case UVC_CT_FOCUS_AUTO_CONTROL:
info->sub_infos[0]->v4l2_id = V4L2_CID_FOCUS_AUTO;
break;
case UVC_CT_IRIS_ABSOLUTE_CONTROL:
info->sub_infos[0]->v4l2_id = V4L2_CID_IRIS_ABSOLUTE;
break;
case UVC_CT_IRIS_RELATIVE_CONTROL:
info->sub_infos[0]->v4l2_id = V4L2_CID_IRIS_RELATIVE;
break;
case UVC_CT_ZOOM_ABSOLUTE_CONTROL:
info->sub_infos[0]->v4l2_id = V4L2_CID_ZOOM_ABSOLUTE;
break;
case UVC_CT_ZOOM_RELATIVE_CONTROL:
info->sub_infos[0]->v4l2_id = V4L2_CID_ZOOM_CONTINUOUS;
break;
case UVC_CT_PANTILT_ABSOLUTE_CONTROL:
info->sub_infos[0]->v4l2_id = V4L2_CID_PAN_ABSOLUTE;
info->sub_infos[1]->v4l2_id = V4L2_CID_TILT_ABSOLUTE;
break;
case UVC_CT_PANTILT_RELATIVE_CONTROL:
info->sub_infos[0]->v4l2_id = V4L2_CID_PAN_SPEED;
info->sub_infos[1]->v4l2_id = V4L2_CID_TILT_SPEED;
break;
case UVC_CT_PRIVACY_CONTROL:
info->sub_infos[0]->v4l2_id = V4L2_CID_PRIVACY;
break;
default:
ret = EINVAL;
break;
}
return ret;
}
static int
uvc_ctrl_build_mapping_pu(struct uvc_ctrl_info *info)
{
int ret = 0;
switch (info->selector) {
case UVC_PU_BRIGHTNESS_CONTROL:
info->sub_infos[0]->v4l2_id = V4L2_CID_BRIGHTNESS;
break;
case UVC_PU_CONTRAST_CONTROL:
info->sub_infos[0]->v4l2_id = V4L2_CID_CONTRAST;
break;
case UVC_PU_HUE_CONTROL:
info->sub_infos[0]->v4l2_id = V4L2_CID_HUE;
break;
case UVC_PU_SATURATION_CONTROL:
info->sub_infos[0]->v4l2_id = V4L2_CID_SATURATION;
break;
case UVC_PU_SHARPNESS_CONTROL:
info->sub_infos[0]->v4l2_id = V4L2_CID_SHARPNESS;
break;
case UVC_PU_GAMMA_CONTROL:
info->sub_infos[0]->v4l2_id = V4L2_CID_GAMMA;
break;
case UVC_PU_BACKLIGHT_COMPENSATION_CONTROL:
info->sub_infos[0]->v4l2_id =
V4L2_CID_BACKLIGHT_COMPENSATION;
break;
case UVC_PU_GAIN_CONTROL:
info->sub_infos[0]->v4l2_id = V4L2_CID_GAIN;
break;
case UVC_PU_POWER_LINE_FREQUENCY_CONTROL:
info->sub_infos[0]->v4l2_id =
V4L2_CID_POWER_LINE_FREQUENCY;
break;
case UVC_PU_HUE_AUTO_CONTROL:
info->sub_infos[0]->v4l2_id = V4L2_CID_HUE_AUTO;
break;
case UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL:
info->sub_infos[0]->v4l2_id = V4L2_CID_AUTO_WHITE_BALANCE;
break;
case UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL:
info->sub_infos[0]->v4l2_id =
V4L2_CID_WHITE_BALANCE_TEMPERATURE;
break;
case UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL:
info->sub_infos[0]->v4l2_id = V4L2_CID_AUTO_WHITE_BALANCE;
break;
case UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL:
info->sub_infos[0]->v4l2_id = V4L2_CID_BLUE_BALANCE;
info->sub_infos[1]->v4l2_id = V4L2_CID_RED_BALANCE;
break;
default:
ret = EINVAL;
}
return ret;
}
static int
uvc_ctrl_build_mapping(struct uvc_ctrl_info *info)
{
int ret = 0;
uint8_t i = 0;
enum uvc_topo_type topo_type = info->topo_type;
uint8_t sub_info_num = info->sub_info_num;
struct uvc_ctrl_sub_info *sub_info = NULL;
if (sub_info_num == 0) {
return 0;
}
for (i = 0; i < sub_info_num; i++) {
sub_info = kmalloc(sizeof(*sub_info), M_UVC, M_ZERO | M_WAITOK);
if (sub_info == NULL) {
kprintf("%s:%d fail to alloc mem\n", __func__, __LINE__);
ret = ENOMEM;
goto err_done;
}
sub_info->uvc_info = info;
info->sub_infos[i] = sub_info;
}
if (topo_type == UVC_TOPO_TYPE_CAMERA_TERMINAL) {
ret = uvc_ctrl_build_mapping_ct(info);
} else if (topo_type == UVC_TOPO_TYPE_PROCESSING_UNIT) {
ret = uvc_ctrl_build_mapping_pu(info);
} else {
ret = EINVAL;
}
if (ret != 0) {
goto err_done;
}
for (int i = 0; i < sub_info_num; i++) {
uvc_ctrl_init_sub_info(info, info->sub_infos[i]);
}
return 0;
err_done:
for (i = 0; i < MAX_MAPPING_NUM; i++) {
if (info->sub_infos[i] != NULL) {
kfree(info->sub_infos[i], M_UVC);
info->sub_infos[i] = NULL;
}
}
return ret;
}
static int
uvc_ctrl_initialize_control(struct uvc_control *ctrl)
{
int ret = 0;
uint8_t bit_idx = ctrl->index;
uint16_t node_type = UVC_ENT_TYPE(ctrl->topo_node);
struct uvc_ctrl_info tmp_info;
switch (node_type) {
case UVC_ITT_CAMERA:
ret = uvc_ctrl_get_info_ct(bit_idx, &tmp_info);
if (ret != 0) {
return EINVAL;
}
break;
case UDESCSUB_VC_PROCESSING_UNIT:
ret = uvc_ctrl_get_info_pu(bit_idx, &tmp_info);
if (ret != 0) {
return EINVAL;
}
break;
default:
return 0;
}
ctrl->uvc_data = kmalloc(tmp_info.byte_size * UVC_CTRL_DATA_LAST + 1, M_UVC,
M_ZERO | M_WAITOK);
if (ctrl->uvc_data == NULL) {
kprintf("%s:%d fail to allocate memory\n", __func__, __LINE__);
ret = ENOMEM;
goto err;
}
uvc_ctrl_init_sub_info_num(&tmp_info);
ret = uvc_ctrl_build_mapping(&tmp_info);
if (0 != ret) {
goto err;
}
ret = uvc_ctrl_init_menu(&tmp_info);
if (0 != ret) {
goto err;
}
ctrl->info = tmp_info;
ctrl->initialized = 1;
return 0;
err:
if (ctrl->uvc_data != NULL) {
kfree(ctrl->uvc_data, M_UVC);
ctrl->uvc_data = NULL;
}
return ret;
}
void
uvc_ctrl_destroy_mappings(struct uvc_control *ctrl)
{
uint8_t i = 0;
if (!ctrl)
return;
for (i = 0; i < MAX_MAPPING_NUM; i++) {
if (ctrl->info.sub_infos[i] != NULL) {
DPRINTF("removing v4l2 mapping '%s' \n",
ctrl->info.sub_infos[i]->v4l2_name);
kfree(ctrl->info.sub_infos[i], M_UVC);
ctrl->info.sub_infos[i] = NULL;
}
if (ctrl->info.menus[i].menu_data != NULL) {
kfree(ctrl->info.menus[i].menu_data, M_UVC);
ctrl->info.menus[i].menu_data = NULL;
}
}
ctrl->info.sub_info_num = 0;
return;
}
static unsigned int
uvc_ctrl_count_control(const uint8_t *bmCtrls, uint8_t bCtrlSize)
{
int i = 0;
unsigned int count = 0;
const uint8_t *data = bmCtrls;
if (data == NULL || bCtrlSize == 0)
return 0;
for (i = 0; i < bCtrlSize * 8; i++) {
if ((data[i >> UVC_BITSHIFT] >>
(i & UVC_BITMASK)) & UVC_VALMASK)
count++;
}
return count;
}
int
uvc_ctrl_init_dev(struct uvc_softc *sc, struct uvc_drv_ctrl *ctrls)
{
struct uvc_topo_node *topo_node, *tmp;
struct uvc_control *ctrl = NULL;
uint8_t bCtrlSize = 0;
uint32_t nctrls = 0;
uint8_t *bmCtrls = NULL;
uint8_t i = 0;
struct uvc_xu_node_info *node_info_xu = NULL;
struct uvc_pu_node_info *node_info_pu = NULL;
struct uvc_ct_node_info *node_info_ct = NULL;
if (!ctrls) {
return EINVAL;
}
STAILQ_FOREACH_SAFE(topo_node, &ctrls->topo_nodes, link, tmp) {
if (UVC_ENT_TYPE(topo_node) == UDESCSUB_VC_EXTENSION_UNIT) {
node_info_xu = (struct uvc_xu_node_info*)topo_node->node_info;
bmCtrls = node_info_xu->bmControls;
bCtrlSize = node_info_xu->bControlSize;
} else if (UVC_ENT_TYPE(topo_node) == UDESCSUB_VC_PROCESSING_UNIT) {
node_info_pu = (struct uvc_pu_node_info*)topo_node->node_info;
bmCtrls = node_info_pu->bmControls;
bCtrlSize = node_info_pu->bControlSize;
} else if (UVC_ENT_TYPE(topo_node) == UVC_ITT_CAMERA) {
node_info_ct = (struct uvc_ct_node_info*)topo_node->node_info;
bmCtrls = node_info_ct->bmControls;
bCtrlSize = node_info_ct->bControlSize;
} else
continue;
nctrls = uvc_ctrl_count_control(bmCtrls, bCtrlSize);
if (nctrls == 0)
continue;
topo_node->controls = kmalloc(nctrls * sizeof(*ctrl), M_UVC,
M_ZERO | M_WAITOK);
if (!topo_node->controls)
return ENOMEM;
topo_node->controls_num = nctrls;
ctrl = topo_node->controls;
for (i = 0; i < bCtrlSize * 8; i++) {
if (uvc_test_bit(bmCtrls, i) == 0)
continue;
ctrl->topo_node = topo_node;
ctrl->index = i;
uvc_ctrl_initialize_control(ctrl);
ctrl++;
}
}
return 0;
}
static int
uvc_query_v4l2_ctrl_sub(struct uvc_control *ctrl, struct v4l2_queryctrl *query)
{
int i = 0;
uint8_t sub_info_idx = ctrl->info.cached_sub_info_idx;
struct uvc_ctrl_menu_item *menu_items =
ctrl->info.menus[sub_info_idx].menu_data;
uint8_t menu_item_num = ctrl->info.menus[sub_info_idx].item_num;
struct uvc_ctrl_sub_info *sub_info = ctrl->info.sub_infos[sub_info_idx];
memset(query, 0, sizeof(*query));
if (sub_info == NULL) {
return EINVAL;
}
query->id = sub_info->v4l2_id;
query->type = sub_info->v4l2_type;
strlcpy(query->name, sub_info->v4l2_name, sizeof(query->name));
query->flags = 0;
switch (sub_info->v4l2_type) {
case V4L2_CTRL_TYPE_BOOLEAN:
query->minimum = 0;
query->maximum = 1;
query->step = 1;
goto end;
case V4L2_CTRL_TYPE_BUTTON:
query->minimum = 0;
query->maximum = 0;
query->step = 0;
goto end;
case V4L2_CTRL_TYPE_MENU:
query->minimum = 0;
query->maximum = menu_item_num - 1;
query->step = 1;
for (i = 0; i < menu_item_num; i++) {
if (menu_items[i].value == query->default_value) {
query->default_value = i;
break;
}
}
goto end;
default:
break;
}
DPRINTF("WARNING __TO_BE_IMPLEMENT__ other par %s\n", __func__);
end:
return 0;
}
static struct uvc_control *
uvc_search_control_sub(struct uvc_topo_node *topo_node, uint32_t v4l2_id)
{
unsigned int i;
unsigned int j;
struct uvc_control *ctrl = NULL;
if (topo_node == NULL) {
return NULL;
}
for (i = 0; i < topo_node->controls_num; ++i) {
ctrl = &topo_node->controls[i];
if (!ctrl->initialized)
continue;
for (j = 0; j < MAX_MAPPING_NUM; j++) {
if (ctrl->info.sub_infos[j] == NULL) {
continue;
}
if (ctrl->info.sub_infos[j]->v4l2_id == v4l2_id) {
ctrl->info.cached_sub_info_idx = j;
return ctrl;
}
}
}
return NULL;
}
static struct uvc_control *
uvc_search_control(struct uvc_drv_ctrl *ctrls, uint32_t v4l2_id)
{
struct uvc_control *ret_ctrl = NULL;
struct uvc_topo_node *topo_node = NULL, *tmp = NULL;
if (v4l2_id & V4L2_CTRL_FLAG_NEXT_CTRL) {
DPRINTF("Not support V4L2_CTRL_FLAG_NEXT_CTRL (0x%08x )\n",
v4l2_id);
return NULL;
}
v4l2_id &= V4L2_CTRL_ID_MASK;
STAILQ_FOREACH_SAFE(topo_node, &ctrls->topo_nodes, link, tmp) {
ret_ctrl = uvc_search_control_sub(topo_node, v4l2_id);
if (ret_ctrl != NULL) {
return ret_ctrl;
}
}
DPRINTF("Control 0x%08x not found.\n", v4l2_id);
return NULL;
}
int
uvc_query_v4l2_ctrl(struct uvc_drv_video *video, struct v4l2_queryctrl *query)
{
struct uvc_control *ctrl;
int ret = EINVAL;
UVC_LOCK(&video->ctrl->mtx);
ctrl = uvc_search_control(video->ctrl, query->id);
if (ctrl == NULL) {
ret = EINVAL;
goto done;
}
ret = uvc_query_v4l2_ctrl_sub(ctrl, query);
done:
UVC_UNLOCK(&video->ctrl->mtx);
return ret;
}
int
uvc_query_v4l2_menu(struct uvc_drv_video *video,
struct v4l2_querymenu *qm)
{
struct uvc_control *ctrl;
uint8_t sub_info_idx = 0;
int ret = 0;
int id = qm->id;
int index = qm->index;
memset(qm, 0, sizeof(*qm));
qm->id = id;
qm->index = index;
UVC_LOCK(&video->ctrl->mtx);
ctrl = uvc_search_control(video->ctrl, qm->id);
if (ctrl == NULL) {
ret = EINVAL;
goto done;
}
sub_info_idx = ctrl->info.cached_sub_info_idx;
if (ctrl->info.menus[sub_info_idx].menu_data == NULL) {
ret = EINVAL;
goto done;
}
strlcpy(qm->name,
ctrl->info.sub_infos[sub_info_idx]->v4l2_name,
sizeof(qm->name));
done:
UVC_UNLOCK(&video->ctrl->mtx);
return ret;
}