12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
20c0d06caSMauro Carvalho Chehab /*
30c0d06caSMauro Carvalho Chehab * uvc_ctrl.c -- USB Video Class driver - Controls
40c0d06caSMauro Carvalho Chehab *
50c0d06caSMauro Carvalho Chehab * Copyright (C) 2005-2010
60c0d06caSMauro Carvalho Chehab * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
70c0d06caSMauro Carvalho Chehab */
80c0d06caSMauro Carvalho Chehab
9619d9b71SRicardo Ribalda #include <asm/barrier.h>
1040140edaSRicardo Ribalda #include <linux/bitops.h>
110c0d06caSMauro Carvalho Chehab #include <linux/kernel.h>
120c0d06caSMauro Carvalho Chehab #include <linux/list.h>
130c0d06caSMauro Carvalho Chehab #include <linux/module.h>
140c0d06caSMauro Carvalho Chehab #include <linux/slab.h>
150c0d06caSMauro Carvalho Chehab #include <linux/uaccess.h>
160c0d06caSMauro Carvalho Chehab #include <linux/usb.h>
17e1d5d71dSMichael Grzeschik #include <linux/usb/uvc.h>
180c0d06caSMauro Carvalho Chehab #include <linux/videodev2.h>
190c0d06caSMauro Carvalho Chehab #include <linux/vmalloc.h>
200c0d06caSMauro Carvalho Chehab #include <linux/wait.h>
21e5225c82SGuennadi Liakhovetski #include <linux/workqueue.h>
220c0d06caSMauro Carvalho Chehab #include <linux/atomic.h>
230c0d06caSMauro Carvalho Chehab #include <media/v4l2-ctrls.h>
240c0d06caSMauro Carvalho Chehab
250c0d06caSMauro Carvalho Chehab #include "uvcvideo.h"
260c0d06caSMauro Carvalho Chehab
270c0d06caSMauro Carvalho Chehab #define UVC_CTRL_DATA_CURRENT 0
280c0d06caSMauro Carvalho Chehab #define UVC_CTRL_DATA_BACKUP 1
290c0d06caSMauro Carvalho Chehab #define UVC_CTRL_DATA_MIN 2
300c0d06caSMauro Carvalho Chehab #define UVC_CTRL_DATA_MAX 3
310c0d06caSMauro Carvalho Chehab #define UVC_CTRL_DATA_RES 4
320c0d06caSMauro Carvalho Chehab #define UVC_CTRL_DATA_DEF 5
330c0d06caSMauro Carvalho Chehab #define UVC_CTRL_DATA_LAST 6
340c0d06caSMauro Carvalho Chehab
350c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------
360c0d06caSMauro Carvalho Chehab * Controls
370c0d06caSMauro Carvalho Chehab */
380c0d06caSMauro Carvalho Chehab
398643d237SJoe Perches static const struct uvc_control_info uvc_ctrls[] = {
400c0d06caSMauro Carvalho Chehab {
410c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
420c0d06caSMauro Carvalho Chehab .selector = UVC_PU_BRIGHTNESS_CONTROL,
430c0d06caSMauro Carvalho Chehab .index = 0,
440c0d06caSMauro Carvalho Chehab .size = 2,
450c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR
460c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_GET_RANGE
470c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_RESTORE,
480c0d06caSMauro Carvalho Chehab },
490c0d06caSMauro Carvalho Chehab {
500c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
510c0d06caSMauro Carvalho Chehab .selector = UVC_PU_CONTRAST_CONTROL,
520c0d06caSMauro Carvalho Chehab .index = 1,
530c0d06caSMauro Carvalho Chehab .size = 2,
540c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR
550c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_GET_RANGE
560c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_RESTORE,
570c0d06caSMauro Carvalho Chehab },
580c0d06caSMauro Carvalho Chehab {
590c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
600c0d06caSMauro Carvalho Chehab .selector = UVC_PU_HUE_CONTROL,
610c0d06caSMauro Carvalho Chehab .index = 2,
620c0d06caSMauro Carvalho Chehab .size = 2,
630c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR
640c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_GET_RANGE
650c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_RESTORE
660c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_AUTO_UPDATE,
670c0d06caSMauro Carvalho Chehab },
680c0d06caSMauro Carvalho Chehab {
690c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
700c0d06caSMauro Carvalho Chehab .selector = UVC_PU_SATURATION_CONTROL,
710c0d06caSMauro Carvalho Chehab .index = 3,
720c0d06caSMauro Carvalho Chehab .size = 2,
730c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR
740c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_GET_RANGE
750c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_RESTORE,
760c0d06caSMauro Carvalho Chehab },
770c0d06caSMauro Carvalho Chehab {
780c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
790c0d06caSMauro Carvalho Chehab .selector = UVC_PU_SHARPNESS_CONTROL,
800c0d06caSMauro Carvalho Chehab .index = 4,
810c0d06caSMauro Carvalho Chehab .size = 2,
820c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR
830c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_GET_RANGE
840c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_RESTORE,
850c0d06caSMauro Carvalho Chehab },
860c0d06caSMauro Carvalho Chehab {
870c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
880c0d06caSMauro Carvalho Chehab .selector = UVC_PU_GAMMA_CONTROL,
890c0d06caSMauro Carvalho Chehab .index = 5,
900c0d06caSMauro Carvalho Chehab .size = 2,
910c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR
920c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_GET_RANGE
930c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_RESTORE,
940c0d06caSMauro Carvalho Chehab },
950c0d06caSMauro Carvalho Chehab {
960c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
970c0d06caSMauro Carvalho Chehab .selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL,
980c0d06caSMauro Carvalho Chehab .index = 6,
990c0d06caSMauro Carvalho Chehab .size = 2,
1000c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR
1010c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_GET_RANGE
1020c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_RESTORE
1030c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_AUTO_UPDATE,
1040c0d06caSMauro Carvalho Chehab },
1050c0d06caSMauro Carvalho Chehab {
1060c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
1070c0d06caSMauro Carvalho Chehab .selector = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL,
1080c0d06caSMauro Carvalho Chehab .index = 7,
1090c0d06caSMauro Carvalho Chehab .size = 4,
1100c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR
1110c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_GET_RANGE
1120c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_RESTORE
1130c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_AUTO_UPDATE,
1140c0d06caSMauro Carvalho Chehab },
1150c0d06caSMauro Carvalho Chehab {
1160c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
1170c0d06caSMauro Carvalho Chehab .selector = UVC_PU_BACKLIGHT_COMPENSATION_CONTROL,
1180c0d06caSMauro Carvalho Chehab .index = 8,
1190c0d06caSMauro Carvalho Chehab .size = 2,
1200c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR
1210c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_GET_RANGE
1220c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_RESTORE,
1230c0d06caSMauro Carvalho Chehab },
1240c0d06caSMauro Carvalho Chehab {
1250c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
1260c0d06caSMauro Carvalho Chehab .selector = UVC_PU_GAIN_CONTROL,
1270c0d06caSMauro Carvalho Chehab .index = 9,
1280c0d06caSMauro Carvalho Chehab .size = 2,
1290c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR
1300c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_GET_RANGE
1310c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_RESTORE,
1320c0d06caSMauro Carvalho Chehab },
1330c0d06caSMauro Carvalho Chehab {
1340c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
1350c0d06caSMauro Carvalho Chehab .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
1360c0d06caSMauro Carvalho Chehab .index = 10,
1370c0d06caSMauro Carvalho Chehab .size = 1,
1380c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
1390c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_RESTORE,
1400c0d06caSMauro Carvalho Chehab },
1410c0d06caSMauro Carvalho Chehab {
1420c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
1430c0d06caSMauro Carvalho Chehab .selector = UVC_PU_HUE_AUTO_CONTROL,
1440c0d06caSMauro Carvalho Chehab .index = 11,
1450c0d06caSMauro Carvalho Chehab .size = 1,
1460c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
1470c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_RESTORE,
1480c0d06caSMauro Carvalho Chehab },
1490c0d06caSMauro Carvalho Chehab {
1500c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
1510c0d06caSMauro Carvalho Chehab .selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL,
1520c0d06caSMauro Carvalho Chehab .index = 12,
1530c0d06caSMauro Carvalho Chehab .size = 1,
1540c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
1550c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_RESTORE,
1560c0d06caSMauro Carvalho Chehab },
1570c0d06caSMauro Carvalho Chehab {
1580c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
1590c0d06caSMauro Carvalho Chehab .selector = UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL,
1600c0d06caSMauro Carvalho Chehab .index = 13,
1610c0d06caSMauro Carvalho Chehab .size = 1,
1620c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
1630c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_RESTORE,
1640c0d06caSMauro Carvalho Chehab },
1650c0d06caSMauro Carvalho Chehab {
1660c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
1670c0d06caSMauro Carvalho Chehab .selector = UVC_PU_DIGITAL_MULTIPLIER_CONTROL,
1680c0d06caSMauro Carvalho Chehab .index = 14,
1690c0d06caSMauro Carvalho Chehab .size = 2,
1700c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR
1710c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_GET_RANGE
1720c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_RESTORE,
1730c0d06caSMauro Carvalho Chehab },
1740c0d06caSMauro Carvalho Chehab {
1750c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
1760c0d06caSMauro Carvalho Chehab .selector = UVC_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL,
1770c0d06caSMauro Carvalho Chehab .index = 15,
1780c0d06caSMauro Carvalho Chehab .size = 2,
1790c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR
1800c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_GET_RANGE
1810c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_RESTORE,
1820c0d06caSMauro Carvalho Chehab },
1830c0d06caSMauro Carvalho Chehab {
1840c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
1850c0d06caSMauro Carvalho Chehab .selector = UVC_PU_ANALOG_VIDEO_STANDARD_CONTROL,
1860c0d06caSMauro Carvalho Chehab .index = 16,
1870c0d06caSMauro Carvalho Chehab .size = 1,
1880c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_GET_CUR,
1890c0d06caSMauro Carvalho Chehab },
1900c0d06caSMauro Carvalho Chehab {
1910c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
1920c0d06caSMauro Carvalho Chehab .selector = UVC_PU_ANALOG_LOCK_STATUS_CONTROL,
1930c0d06caSMauro Carvalho Chehab .index = 17,
1940c0d06caSMauro Carvalho Chehab .size = 1,
1950c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_GET_CUR,
1960c0d06caSMauro Carvalho Chehab },
1970c0d06caSMauro Carvalho Chehab {
1980c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_CAMERA,
1990c0d06caSMauro Carvalho Chehab .selector = UVC_CT_SCANNING_MODE_CONTROL,
2000c0d06caSMauro Carvalho Chehab .index = 0,
2010c0d06caSMauro Carvalho Chehab .size = 1,
2020c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
2030c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_RESTORE,
2040c0d06caSMauro Carvalho Chehab },
2050c0d06caSMauro Carvalho Chehab {
2060c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_CAMERA,
2070c0d06caSMauro Carvalho Chehab .selector = UVC_CT_AE_MODE_CONTROL,
2080c0d06caSMauro Carvalho Chehab .index = 1,
2090c0d06caSMauro Carvalho Chehab .size = 1,
2100c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
2110c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_GET_RES
2120c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_RESTORE,
2130c0d06caSMauro Carvalho Chehab },
2140c0d06caSMauro Carvalho Chehab {
2150c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_CAMERA,
2160c0d06caSMauro Carvalho Chehab .selector = UVC_CT_AE_PRIORITY_CONTROL,
2170c0d06caSMauro Carvalho Chehab .index = 2,
2180c0d06caSMauro Carvalho Chehab .size = 1,
2190c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
2200c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_RESTORE,
2210c0d06caSMauro Carvalho Chehab },
2220c0d06caSMauro Carvalho Chehab {
2230c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_CAMERA,
2240c0d06caSMauro Carvalho Chehab .selector = UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL,
2250c0d06caSMauro Carvalho Chehab .index = 3,
2260c0d06caSMauro Carvalho Chehab .size = 4,
2270c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR
2280c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_GET_RANGE
2291ab8c3fcSAnton V. Shokurov | UVC_CTRL_FLAG_RESTORE
2301ab8c3fcSAnton V. Shokurov | UVC_CTRL_FLAG_AUTO_UPDATE,
2310c0d06caSMauro Carvalho Chehab },
2320c0d06caSMauro Carvalho Chehab {
2330c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_CAMERA,
2340c0d06caSMauro Carvalho Chehab .selector = UVC_CT_EXPOSURE_TIME_RELATIVE_CONTROL,
2350c0d06caSMauro Carvalho Chehab .index = 4,
2360c0d06caSMauro Carvalho Chehab .size = 1,
2370c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_RESTORE,
2380c0d06caSMauro Carvalho Chehab },
2390c0d06caSMauro Carvalho Chehab {
2400c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_CAMERA,
2410c0d06caSMauro Carvalho Chehab .selector = UVC_CT_FOCUS_ABSOLUTE_CONTROL,
2420c0d06caSMauro Carvalho Chehab .index = 5,
2430c0d06caSMauro Carvalho Chehab .size = 2,
2440c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR
2450c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_GET_RANGE
2460c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_RESTORE
2470c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_AUTO_UPDATE,
2480c0d06caSMauro Carvalho Chehab },
2490c0d06caSMauro Carvalho Chehab {
2500c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_CAMERA,
2510c0d06caSMauro Carvalho Chehab .selector = UVC_CT_FOCUS_RELATIVE_CONTROL,
2520c0d06caSMauro Carvalho Chehab .index = 6,
2530c0d06caSMauro Carvalho Chehab .size = 2,
2540c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_MIN
2550c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES
2560c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_GET_DEF
2570c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_AUTO_UPDATE,
2580c0d06caSMauro Carvalho Chehab },
2590c0d06caSMauro Carvalho Chehab {
2600c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_CAMERA,
2610c0d06caSMauro Carvalho Chehab .selector = UVC_CT_IRIS_ABSOLUTE_CONTROL,
2620c0d06caSMauro Carvalho Chehab .index = 7,
2630c0d06caSMauro Carvalho Chehab .size = 2,
2640c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR
2650c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_GET_RANGE
2660c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_RESTORE
2670c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_AUTO_UPDATE,
2680c0d06caSMauro Carvalho Chehab },
2690c0d06caSMauro Carvalho Chehab {
2700c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_CAMERA,
2710c0d06caSMauro Carvalho Chehab .selector = UVC_CT_IRIS_RELATIVE_CONTROL,
2720c0d06caSMauro Carvalho Chehab .index = 8,
2730c0d06caSMauro Carvalho Chehab .size = 1,
2740c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR
2750c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_AUTO_UPDATE,
2760c0d06caSMauro Carvalho Chehab },
2770c0d06caSMauro Carvalho Chehab {
2780c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_CAMERA,
2790c0d06caSMauro Carvalho Chehab .selector = UVC_CT_ZOOM_ABSOLUTE_CONTROL,
2800c0d06caSMauro Carvalho Chehab .index = 9,
2810c0d06caSMauro Carvalho Chehab .size = 2,
2820c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR
2830c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_GET_RANGE
2840c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_RESTORE
2850c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_AUTO_UPDATE,
2860c0d06caSMauro Carvalho Chehab },
2870c0d06caSMauro Carvalho Chehab {
2880c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_CAMERA,
2890c0d06caSMauro Carvalho Chehab .selector = UVC_CT_ZOOM_RELATIVE_CONTROL,
2900c0d06caSMauro Carvalho Chehab .index = 10,
2910c0d06caSMauro Carvalho Chehab .size = 3,
2920c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_MIN
2930c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES
2940c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_GET_DEF
2950c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_AUTO_UPDATE,
2960c0d06caSMauro Carvalho Chehab },
2970c0d06caSMauro Carvalho Chehab {
2980c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_CAMERA,
2990c0d06caSMauro Carvalho Chehab .selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL,
3000c0d06caSMauro Carvalho Chehab .index = 11,
3010c0d06caSMauro Carvalho Chehab .size = 8,
3020c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR
3030c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_GET_RANGE
3040c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_RESTORE
3050c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_AUTO_UPDATE,
3060c0d06caSMauro Carvalho Chehab },
3070c0d06caSMauro Carvalho Chehab {
3080c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_CAMERA,
3090c0d06caSMauro Carvalho Chehab .selector = UVC_CT_PANTILT_RELATIVE_CONTROL,
3100c0d06caSMauro Carvalho Chehab .index = 12,
3110c0d06caSMauro Carvalho Chehab .size = 4,
3123ea37523SVincent Palatin .flags = UVC_CTRL_FLAG_SET_CUR
3133ea37523SVincent Palatin | UVC_CTRL_FLAG_GET_RANGE
3140c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_AUTO_UPDATE,
3150c0d06caSMauro Carvalho Chehab },
3160c0d06caSMauro Carvalho Chehab {
3170c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_CAMERA,
3180c0d06caSMauro Carvalho Chehab .selector = UVC_CT_ROLL_ABSOLUTE_CONTROL,
3190c0d06caSMauro Carvalho Chehab .index = 13,
3200c0d06caSMauro Carvalho Chehab .size = 2,
3210c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR
3220c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_GET_RANGE
3230c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_RESTORE
3240c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_AUTO_UPDATE,
3250c0d06caSMauro Carvalho Chehab },
3260c0d06caSMauro Carvalho Chehab {
3270c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_CAMERA,
3280c0d06caSMauro Carvalho Chehab .selector = UVC_CT_ROLL_RELATIVE_CONTROL,
3290c0d06caSMauro Carvalho Chehab .index = 14,
3300c0d06caSMauro Carvalho Chehab .size = 2,
3310c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_MIN
3320c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES
3330c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_GET_DEF
3340c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_AUTO_UPDATE,
3350c0d06caSMauro Carvalho Chehab },
3360c0d06caSMauro Carvalho Chehab {
3370c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_CAMERA,
3380c0d06caSMauro Carvalho Chehab .selector = UVC_CT_FOCUS_AUTO_CONTROL,
3390c0d06caSMauro Carvalho Chehab .index = 17,
3400c0d06caSMauro Carvalho Chehab .size = 1,
3410c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
3420c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_RESTORE,
3430c0d06caSMauro Carvalho Chehab },
3440c0d06caSMauro Carvalho Chehab {
3450c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_CAMERA,
3460c0d06caSMauro Carvalho Chehab .selector = UVC_CT_PRIVACY_CONTROL,
3470c0d06caSMauro Carvalho Chehab .index = 18,
3480c0d06caSMauro Carvalho Chehab .size = 1,
3490c0d06caSMauro Carvalho Chehab .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
3500c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_RESTORE
3510c0d06caSMauro Carvalho Chehab | UVC_CTRL_FLAG_AUTO_UPDATE,
3520c0d06caSMauro Carvalho Chehab },
3536f6a87ebSRicardo Ribalda {
3546f6a87ebSRicardo Ribalda .entity = UVC_GUID_EXT_GPIO_CONTROLLER,
3556f6a87ebSRicardo Ribalda .selector = UVC_CT_PRIVACY_CONTROL,
3566f6a87ebSRicardo Ribalda .index = 0,
3576f6a87ebSRicardo Ribalda .size = 1,
3586f6a87ebSRicardo Ribalda .flags = UVC_CTRL_FLAG_GET_CUR
3596f6a87ebSRicardo Ribalda | UVC_CTRL_FLAG_AUTO_UPDATE,
3606f6a87ebSRicardo Ribalda },
3610c0d06caSMauro Carvalho Chehab };
3620c0d06caSMauro Carvalho Chehab
3639b31ea80SRicardo Ribalda static const u32 uvc_control_classes[] = {
3649b31ea80SRicardo Ribalda V4L2_CID_CAMERA_CLASS,
3659b31ea80SRicardo Ribalda V4L2_CID_USER_CLASS,
3669b31ea80SRicardo Ribalda };
3679b31ea80SRicardo Ribalda
368716c3304SRicardo Ribalda static const int exposure_auto_mapping[] = { 2, 1, 4, 8 };
3690c0d06caSMauro Carvalho Chehab
370716c3304SRicardo Ribalda /*
371716c3304SRicardo Ribalda * This function translates the V4L2 menu index @idx, as exposed to userspace as
372716c3304SRicardo Ribalda * the V4L2 control value, to the corresponding UVC control value used by the
373716c3304SRicardo Ribalda * device. The custom menu_mapping in the control @mapping is used when
374716c3304SRicardo Ribalda * available, otherwise the function assumes that the V4L2 and UVC values are
375716c3304SRicardo Ribalda * identical.
376716c3304SRicardo Ribalda *
377716c3304SRicardo Ribalda * For controls of type UVC_CTRL_DATA_TYPE_BITMASK, the UVC control value is
378716c3304SRicardo Ribalda * expressed as a bitmask and is thus guaranteed to have a single bit set.
379716c3304SRicardo Ribalda *
380716c3304SRicardo Ribalda * The function returns -EINVAL if the V4L2 menu index @idx isn't valid for the
381716c3304SRicardo Ribalda * control, which includes all controls whose type isn't UVC_CTRL_DATA_TYPE_ENUM
382716c3304SRicardo Ribalda * or UVC_CTRL_DATA_TYPE_BITMASK.
383716c3304SRicardo Ribalda */
uvc_mapping_get_menu_value(const struct uvc_control_mapping * mapping,u32 idx)384716c3304SRicardo Ribalda static int uvc_mapping_get_menu_value(const struct uvc_control_mapping *mapping,
385716c3304SRicardo Ribalda u32 idx)
386716c3304SRicardo Ribalda {
387716c3304SRicardo Ribalda if (!test_bit(idx, &mapping->menu_mask))
388716c3304SRicardo Ribalda return -EINVAL;
389716c3304SRicardo Ribalda
390716c3304SRicardo Ribalda if (mapping->menu_mapping)
391716c3304SRicardo Ribalda return mapping->menu_mapping[idx];
392716c3304SRicardo Ribalda
393716c3304SRicardo Ribalda return idx;
394716c3304SRicardo Ribalda }
395716c3304SRicardo Ribalda
396716c3304SRicardo Ribalda static const char *
uvc_mapping_get_menu_name(const struct uvc_control_mapping * mapping,u32 idx)397716c3304SRicardo Ribalda uvc_mapping_get_menu_name(const struct uvc_control_mapping *mapping, u32 idx)
398716c3304SRicardo Ribalda {
399716c3304SRicardo Ribalda if (!test_bit(idx, &mapping->menu_mask))
400716c3304SRicardo Ribalda return NULL;
401716c3304SRicardo Ribalda
402716c3304SRicardo Ribalda if (mapping->menu_names)
403716c3304SRicardo Ribalda return mapping->menu_names[idx];
404716c3304SRicardo Ribalda
405716c3304SRicardo Ribalda return v4l2_ctrl_get_menu(mapping->id)[idx];
406716c3304SRicardo Ribalda }
4070c0d06caSMauro Carvalho Chehab
uvc_ctrl_get_zoom(struct uvc_control_mapping * mapping,u8 query,const u8 * data)4082c6b222cSLaurent Pinchart static s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping,
4092c6b222cSLaurent Pinchart u8 query, const u8 *data)
4100c0d06caSMauro Carvalho Chehab {
4112c6b222cSLaurent Pinchart s8 zoom = (s8)data[0];
4120c0d06caSMauro Carvalho Chehab
4130c0d06caSMauro Carvalho Chehab switch (query) {
4140c0d06caSMauro Carvalho Chehab case UVC_GET_CUR:
4150c0d06caSMauro Carvalho Chehab return (zoom == 0) ? 0 : (zoom > 0 ? data[2] : -data[2]);
4160c0d06caSMauro Carvalho Chehab
4170c0d06caSMauro Carvalho Chehab case UVC_GET_MIN:
4180c0d06caSMauro Carvalho Chehab case UVC_GET_MAX:
4190c0d06caSMauro Carvalho Chehab case UVC_GET_RES:
4200c0d06caSMauro Carvalho Chehab case UVC_GET_DEF:
4210c0d06caSMauro Carvalho Chehab default:
4220c0d06caSMauro Carvalho Chehab return data[2];
4230c0d06caSMauro Carvalho Chehab }
4240c0d06caSMauro Carvalho Chehab }
4250c0d06caSMauro Carvalho Chehab
uvc_ctrl_set_zoom(struct uvc_control_mapping * mapping,s32 value,u8 * data)4260c0d06caSMauro Carvalho Chehab static void uvc_ctrl_set_zoom(struct uvc_control_mapping *mapping,
4272c6b222cSLaurent Pinchart s32 value, u8 *data)
4280c0d06caSMauro Carvalho Chehab {
4290c0d06caSMauro Carvalho Chehab data[0] = value == 0 ? 0 : (value > 0) ? 1 : 0xff;
4300c0d06caSMauro Carvalho Chehab data[2] = min((int)abs(value), 0xff);
4310c0d06caSMauro Carvalho Chehab }
4320c0d06caSMauro Carvalho Chehab
uvc_ctrl_get_rel_speed(struct uvc_control_mapping * mapping,u8 query,const u8 * data)4332c6b222cSLaurent Pinchart static s32 uvc_ctrl_get_rel_speed(struct uvc_control_mapping *mapping,
4342c6b222cSLaurent Pinchart u8 query, const u8 *data)
4353ea37523SVincent Palatin {
4363ea37523SVincent Palatin unsigned int first = mapping->offset / 8;
4372c6b222cSLaurent Pinchart s8 rel = (s8)data[first];
4383ea37523SVincent Palatin
4393ea37523SVincent Palatin switch (query) {
4403ea37523SVincent Palatin case UVC_GET_CUR:
4413ea37523SVincent Palatin return (rel == 0) ? 0 : (rel > 0 ? data[first+1]
4423ea37523SVincent Palatin : -data[first+1]);
4433ea37523SVincent Palatin case UVC_GET_MIN:
4443ea37523SVincent Palatin return -data[first+1];
4453ea37523SVincent Palatin case UVC_GET_MAX:
4463ea37523SVincent Palatin case UVC_GET_RES:
4473ea37523SVincent Palatin case UVC_GET_DEF:
4483ea37523SVincent Palatin default:
4493ea37523SVincent Palatin return data[first+1];
4503ea37523SVincent Palatin }
4513ea37523SVincent Palatin }
4523ea37523SVincent Palatin
uvc_ctrl_set_rel_speed(struct uvc_control_mapping * mapping,s32 value,u8 * data)4533ea37523SVincent Palatin static void uvc_ctrl_set_rel_speed(struct uvc_control_mapping *mapping,
4542c6b222cSLaurent Pinchart s32 value, u8 *data)
4553ea37523SVincent Palatin {
4563ea37523SVincent Palatin unsigned int first = mapping->offset / 8;
4573ea37523SVincent Palatin
4583ea37523SVincent Palatin data[first] = value == 0 ? 0 : (value > 0) ? 1 : 0xff;
4593ea37523SVincent Palatin data[first+1] = min_t(int, abs(value), 0xff);
4603ea37523SVincent Palatin }
4613ea37523SVincent Palatin
4628643d237SJoe Perches static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
4630c0d06caSMauro Carvalho Chehab {
4640c0d06caSMauro Carvalho Chehab .id = V4L2_CID_BRIGHTNESS,
4650c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
4660c0d06caSMauro Carvalho Chehab .selector = UVC_PU_BRIGHTNESS_CONTROL,
4670c0d06caSMauro Carvalho Chehab .size = 16,
4680c0d06caSMauro Carvalho Chehab .offset = 0,
4690c0d06caSMauro Carvalho Chehab .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
4700c0d06caSMauro Carvalho Chehab .data_type = UVC_CTRL_DATA_TYPE_SIGNED,
4710c0d06caSMauro Carvalho Chehab },
4720c0d06caSMauro Carvalho Chehab {
4730c0d06caSMauro Carvalho Chehab .id = V4L2_CID_CONTRAST,
4740c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
4750c0d06caSMauro Carvalho Chehab .selector = UVC_PU_CONTRAST_CONTROL,
4760c0d06caSMauro Carvalho Chehab .size = 16,
4770c0d06caSMauro Carvalho Chehab .offset = 0,
4780c0d06caSMauro Carvalho Chehab .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
4790c0d06caSMauro Carvalho Chehab .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
4800c0d06caSMauro Carvalho Chehab },
4810c0d06caSMauro Carvalho Chehab {
4820c0d06caSMauro Carvalho Chehab .id = V4L2_CID_HUE,
4830c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
4840c0d06caSMauro Carvalho Chehab .selector = UVC_PU_HUE_CONTROL,
4850c0d06caSMauro Carvalho Chehab .size = 16,
4860c0d06caSMauro Carvalho Chehab .offset = 0,
4870c0d06caSMauro Carvalho Chehab .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
4880c0d06caSMauro Carvalho Chehab .data_type = UVC_CTRL_DATA_TYPE_SIGNED,
4890c0d06caSMauro Carvalho Chehab .master_id = V4L2_CID_HUE_AUTO,
4900c0d06caSMauro Carvalho Chehab .master_manual = 0,
4910c0d06caSMauro Carvalho Chehab },
4920c0d06caSMauro Carvalho Chehab {
4930c0d06caSMauro Carvalho Chehab .id = V4L2_CID_SATURATION,
4940c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
4950c0d06caSMauro Carvalho Chehab .selector = UVC_PU_SATURATION_CONTROL,
4960c0d06caSMauro Carvalho Chehab .size = 16,
4970c0d06caSMauro Carvalho Chehab .offset = 0,
4980c0d06caSMauro Carvalho Chehab .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
4990c0d06caSMauro Carvalho Chehab .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
5000c0d06caSMauro Carvalho Chehab },
5010c0d06caSMauro Carvalho Chehab {
5020c0d06caSMauro Carvalho Chehab .id = V4L2_CID_SHARPNESS,
5030c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
5040c0d06caSMauro Carvalho Chehab .selector = UVC_PU_SHARPNESS_CONTROL,
5050c0d06caSMauro Carvalho Chehab .size = 16,
5060c0d06caSMauro Carvalho Chehab .offset = 0,
5070c0d06caSMauro Carvalho Chehab .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
5080c0d06caSMauro Carvalho Chehab .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
5090c0d06caSMauro Carvalho Chehab },
5100c0d06caSMauro Carvalho Chehab {
5110c0d06caSMauro Carvalho Chehab .id = V4L2_CID_GAMMA,
5120c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
5130c0d06caSMauro Carvalho Chehab .selector = UVC_PU_GAMMA_CONTROL,
5140c0d06caSMauro Carvalho Chehab .size = 16,
5150c0d06caSMauro Carvalho Chehab .offset = 0,
5160c0d06caSMauro Carvalho Chehab .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
5170c0d06caSMauro Carvalho Chehab .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
5180c0d06caSMauro Carvalho Chehab },
5190c0d06caSMauro Carvalho Chehab {
5200c0d06caSMauro Carvalho Chehab .id = V4L2_CID_BACKLIGHT_COMPENSATION,
5210c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
5220c0d06caSMauro Carvalho Chehab .selector = UVC_PU_BACKLIGHT_COMPENSATION_CONTROL,
5230c0d06caSMauro Carvalho Chehab .size = 16,
5240c0d06caSMauro Carvalho Chehab .offset = 0,
5250c0d06caSMauro Carvalho Chehab .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
5260c0d06caSMauro Carvalho Chehab .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
5270c0d06caSMauro Carvalho Chehab },
5280c0d06caSMauro Carvalho Chehab {
5290c0d06caSMauro Carvalho Chehab .id = V4L2_CID_GAIN,
5300c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
5310c0d06caSMauro Carvalho Chehab .selector = UVC_PU_GAIN_CONTROL,
5320c0d06caSMauro Carvalho Chehab .size = 16,
5330c0d06caSMauro Carvalho Chehab .offset = 0,
5340c0d06caSMauro Carvalho Chehab .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
5350c0d06caSMauro Carvalho Chehab .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
5360c0d06caSMauro Carvalho Chehab },
5370c0d06caSMauro Carvalho Chehab {
5380c0d06caSMauro Carvalho Chehab .id = V4L2_CID_HUE_AUTO,
5390c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
5400c0d06caSMauro Carvalho Chehab .selector = UVC_PU_HUE_AUTO_CONTROL,
5410c0d06caSMauro Carvalho Chehab .size = 1,
5420c0d06caSMauro Carvalho Chehab .offset = 0,
5430c0d06caSMauro Carvalho Chehab .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
5440c0d06caSMauro Carvalho Chehab .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
5450c0d06caSMauro Carvalho Chehab .slave_ids = { V4L2_CID_HUE, },
5460c0d06caSMauro Carvalho Chehab },
5470c0d06caSMauro Carvalho Chehab {
5480c0d06caSMauro Carvalho Chehab .id = V4L2_CID_EXPOSURE_AUTO,
5490c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_CAMERA,
5500c0d06caSMauro Carvalho Chehab .selector = UVC_CT_AE_MODE_CONTROL,
5510c0d06caSMauro Carvalho Chehab .size = 4,
5520c0d06caSMauro Carvalho Chehab .offset = 0,
5530c0d06caSMauro Carvalho Chehab .v4l2_type = V4L2_CTRL_TYPE_MENU,
5540c0d06caSMauro Carvalho Chehab .data_type = UVC_CTRL_DATA_TYPE_BITMASK,
555716c3304SRicardo Ribalda .menu_mapping = exposure_auto_mapping,
55640140edaSRicardo Ribalda .menu_mask = GENMASK(V4L2_EXPOSURE_APERTURE_PRIORITY,
55740140edaSRicardo Ribalda V4L2_EXPOSURE_AUTO),
5580c0d06caSMauro Carvalho Chehab .slave_ids = { V4L2_CID_EXPOSURE_ABSOLUTE, },
5590c0d06caSMauro Carvalho Chehab },
5600c0d06caSMauro Carvalho Chehab {
5610c0d06caSMauro Carvalho Chehab .id = V4L2_CID_EXPOSURE_AUTO_PRIORITY,
5620c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_CAMERA,
5630c0d06caSMauro Carvalho Chehab .selector = UVC_CT_AE_PRIORITY_CONTROL,
5640c0d06caSMauro Carvalho Chehab .size = 1,
5650c0d06caSMauro Carvalho Chehab .offset = 0,
5660c0d06caSMauro Carvalho Chehab .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
5670c0d06caSMauro Carvalho Chehab .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
5680c0d06caSMauro Carvalho Chehab },
5690c0d06caSMauro Carvalho Chehab {
5700c0d06caSMauro Carvalho Chehab .id = V4L2_CID_EXPOSURE_ABSOLUTE,
5710c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_CAMERA,
5720c0d06caSMauro Carvalho Chehab .selector = UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL,
5730c0d06caSMauro Carvalho Chehab .size = 32,
5740c0d06caSMauro Carvalho Chehab .offset = 0,
5750c0d06caSMauro Carvalho Chehab .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
5760c0d06caSMauro Carvalho Chehab .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
5770c0d06caSMauro Carvalho Chehab .master_id = V4L2_CID_EXPOSURE_AUTO,
5780c0d06caSMauro Carvalho Chehab .master_manual = V4L2_EXPOSURE_MANUAL,
5790c0d06caSMauro Carvalho Chehab },
5800c0d06caSMauro Carvalho Chehab {
5810c0d06caSMauro Carvalho Chehab .id = V4L2_CID_AUTO_WHITE_BALANCE,
5820c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
5830c0d06caSMauro Carvalho Chehab .selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL,
5840c0d06caSMauro Carvalho Chehab .size = 1,
5850c0d06caSMauro Carvalho Chehab .offset = 0,
5860c0d06caSMauro Carvalho Chehab .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
5870c0d06caSMauro Carvalho Chehab .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
5880c0d06caSMauro Carvalho Chehab .slave_ids = { V4L2_CID_WHITE_BALANCE_TEMPERATURE, },
5890c0d06caSMauro Carvalho Chehab },
5900c0d06caSMauro Carvalho Chehab {
5910c0d06caSMauro Carvalho Chehab .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE,
5920c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
5930c0d06caSMauro Carvalho Chehab .selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL,
5940c0d06caSMauro Carvalho Chehab .size = 16,
5950c0d06caSMauro Carvalho Chehab .offset = 0,
5960c0d06caSMauro Carvalho Chehab .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
5970c0d06caSMauro Carvalho Chehab .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
5980c0d06caSMauro Carvalho Chehab .master_id = V4L2_CID_AUTO_WHITE_BALANCE,
5990c0d06caSMauro Carvalho Chehab .master_manual = 0,
6000c0d06caSMauro Carvalho Chehab },
6010c0d06caSMauro Carvalho Chehab {
6020c0d06caSMauro Carvalho Chehab .id = V4L2_CID_AUTO_WHITE_BALANCE,
6030c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
6040c0d06caSMauro Carvalho Chehab .selector = UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL,
6050c0d06caSMauro Carvalho Chehab .size = 1,
6060c0d06caSMauro Carvalho Chehab .offset = 0,
6070c0d06caSMauro Carvalho Chehab .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
6080c0d06caSMauro Carvalho Chehab .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
6090c0d06caSMauro Carvalho Chehab .slave_ids = { V4L2_CID_BLUE_BALANCE,
6100c0d06caSMauro Carvalho Chehab V4L2_CID_RED_BALANCE },
6110c0d06caSMauro Carvalho Chehab },
6120c0d06caSMauro Carvalho Chehab {
6130c0d06caSMauro Carvalho Chehab .id = V4L2_CID_BLUE_BALANCE,
6140c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
6150c0d06caSMauro Carvalho Chehab .selector = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL,
6160c0d06caSMauro Carvalho Chehab .size = 16,
6170c0d06caSMauro Carvalho Chehab .offset = 0,
6180c0d06caSMauro Carvalho Chehab .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
6190c0d06caSMauro Carvalho Chehab .data_type = UVC_CTRL_DATA_TYPE_SIGNED,
6200c0d06caSMauro Carvalho Chehab .master_id = V4L2_CID_AUTO_WHITE_BALANCE,
6210c0d06caSMauro Carvalho Chehab .master_manual = 0,
6220c0d06caSMauro Carvalho Chehab },
6230c0d06caSMauro Carvalho Chehab {
6240c0d06caSMauro Carvalho Chehab .id = V4L2_CID_RED_BALANCE,
6250c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_PROCESSING,
6260c0d06caSMauro Carvalho Chehab .selector = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL,
6270c0d06caSMauro Carvalho Chehab .size = 16,
6280c0d06caSMauro Carvalho Chehab .offset = 16,
6290c0d06caSMauro Carvalho Chehab .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
6300c0d06caSMauro Carvalho Chehab .data_type = UVC_CTRL_DATA_TYPE_SIGNED,
6310c0d06caSMauro Carvalho Chehab .master_id = V4L2_CID_AUTO_WHITE_BALANCE,
6320c0d06caSMauro Carvalho Chehab .master_manual = 0,
6330c0d06caSMauro Carvalho Chehab },
6340c0d06caSMauro Carvalho Chehab {
6350c0d06caSMauro Carvalho Chehab .id = V4L2_CID_FOCUS_ABSOLUTE,
6360c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_CAMERA,
6370c0d06caSMauro Carvalho Chehab .selector = UVC_CT_FOCUS_ABSOLUTE_CONTROL,
6380c0d06caSMauro Carvalho Chehab .size = 16,
6390c0d06caSMauro Carvalho Chehab .offset = 0,
6400c0d06caSMauro Carvalho Chehab .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
6410c0d06caSMauro Carvalho Chehab .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
6420c0d06caSMauro Carvalho Chehab .master_id = V4L2_CID_FOCUS_AUTO,
6430c0d06caSMauro Carvalho Chehab .master_manual = 0,
6440c0d06caSMauro Carvalho Chehab },
6450c0d06caSMauro Carvalho Chehab {
6460c0d06caSMauro Carvalho Chehab .id = V4L2_CID_FOCUS_AUTO,
6470c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_CAMERA,
6480c0d06caSMauro Carvalho Chehab .selector = UVC_CT_FOCUS_AUTO_CONTROL,
6490c0d06caSMauro Carvalho Chehab .size = 1,
6500c0d06caSMauro Carvalho Chehab .offset = 0,
6510c0d06caSMauro Carvalho Chehab .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
6520c0d06caSMauro Carvalho Chehab .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
6530c0d06caSMauro Carvalho Chehab .slave_ids = { V4L2_CID_FOCUS_ABSOLUTE, },
6540c0d06caSMauro Carvalho Chehab },
6550c0d06caSMauro Carvalho Chehab {
6560c0d06caSMauro Carvalho Chehab .id = V4L2_CID_IRIS_ABSOLUTE,
6570c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_CAMERA,
6580c0d06caSMauro Carvalho Chehab .selector = UVC_CT_IRIS_ABSOLUTE_CONTROL,
6590c0d06caSMauro Carvalho Chehab .size = 16,
6600c0d06caSMauro Carvalho Chehab .offset = 0,
6610c0d06caSMauro Carvalho Chehab .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
6620c0d06caSMauro Carvalho Chehab .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
6630c0d06caSMauro Carvalho Chehab },
6640c0d06caSMauro Carvalho Chehab {
6650c0d06caSMauro Carvalho Chehab .id = V4L2_CID_IRIS_RELATIVE,
6660c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_CAMERA,
6670c0d06caSMauro Carvalho Chehab .selector = UVC_CT_IRIS_RELATIVE_CONTROL,
6680c0d06caSMauro Carvalho Chehab .size = 8,
6690c0d06caSMauro Carvalho Chehab .offset = 0,
6700c0d06caSMauro Carvalho Chehab .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
6710c0d06caSMauro Carvalho Chehab .data_type = UVC_CTRL_DATA_TYPE_SIGNED,
6720c0d06caSMauro Carvalho Chehab },
6730c0d06caSMauro Carvalho Chehab {
6740c0d06caSMauro Carvalho Chehab .id = V4L2_CID_ZOOM_ABSOLUTE,
6750c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_CAMERA,
6760c0d06caSMauro Carvalho Chehab .selector = UVC_CT_ZOOM_ABSOLUTE_CONTROL,
6770c0d06caSMauro Carvalho Chehab .size = 16,
6780c0d06caSMauro Carvalho Chehab .offset = 0,
6790c0d06caSMauro Carvalho Chehab .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
6800c0d06caSMauro Carvalho Chehab .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
6810c0d06caSMauro Carvalho Chehab },
6820c0d06caSMauro Carvalho Chehab {
6830c0d06caSMauro Carvalho Chehab .id = V4L2_CID_ZOOM_CONTINUOUS,
6840c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_CAMERA,
6850c0d06caSMauro Carvalho Chehab .selector = UVC_CT_ZOOM_RELATIVE_CONTROL,
6860c0d06caSMauro Carvalho Chehab .size = 0,
6870c0d06caSMauro Carvalho Chehab .offset = 0,
6880c0d06caSMauro Carvalho Chehab .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
6890c0d06caSMauro Carvalho Chehab .data_type = UVC_CTRL_DATA_TYPE_SIGNED,
6900c0d06caSMauro Carvalho Chehab .get = uvc_ctrl_get_zoom,
6910c0d06caSMauro Carvalho Chehab .set = uvc_ctrl_set_zoom,
6920c0d06caSMauro Carvalho Chehab },
6930c0d06caSMauro Carvalho Chehab {
6940c0d06caSMauro Carvalho Chehab .id = V4L2_CID_PAN_ABSOLUTE,
6950c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_CAMERA,
6960c0d06caSMauro Carvalho Chehab .selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL,
6970c0d06caSMauro Carvalho Chehab .size = 32,
6980c0d06caSMauro Carvalho Chehab .offset = 0,
6990c0d06caSMauro Carvalho Chehab .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
7008ca5d2d8SChanho Min .data_type = UVC_CTRL_DATA_TYPE_SIGNED,
7010c0d06caSMauro Carvalho Chehab },
7020c0d06caSMauro Carvalho Chehab {
7030c0d06caSMauro Carvalho Chehab .id = V4L2_CID_TILT_ABSOLUTE,
7040c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_CAMERA,
7050c0d06caSMauro Carvalho Chehab .selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL,
7060c0d06caSMauro Carvalho Chehab .size = 32,
7070c0d06caSMauro Carvalho Chehab .offset = 32,
7080c0d06caSMauro Carvalho Chehab .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
7098ca5d2d8SChanho Min .data_type = UVC_CTRL_DATA_TYPE_SIGNED,
7100c0d06caSMauro Carvalho Chehab },
7110c0d06caSMauro Carvalho Chehab {
7123ea37523SVincent Palatin .id = V4L2_CID_PAN_SPEED,
7133ea37523SVincent Palatin .entity = UVC_GUID_UVC_CAMERA,
7143ea37523SVincent Palatin .selector = UVC_CT_PANTILT_RELATIVE_CONTROL,
7153ea37523SVincent Palatin .size = 16,
7163ea37523SVincent Palatin .offset = 0,
7173ea37523SVincent Palatin .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
7183ea37523SVincent Palatin .data_type = UVC_CTRL_DATA_TYPE_SIGNED,
7193ea37523SVincent Palatin .get = uvc_ctrl_get_rel_speed,
7203ea37523SVincent Palatin .set = uvc_ctrl_set_rel_speed,
7213ea37523SVincent Palatin },
7223ea37523SVincent Palatin {
7233ea37523SVincent Palatin .id = V4L2_CID_TILT_SPEED,
7243ea37523SVincent Palatin .entity = UVC_GUID_UVC_CAMERA,
7253ea37523SVincent Palatin .selector = UVC_CT_PANTILT_RELATIVE_CONTROL,
7263ea37523SVincent Palatin .size = 16,
7273ea37523SVincent Palatin .offset = 16,
7283ea37523SVincent Palatin .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
7293ea37523SVincent Palatin .data_type = UVC_CTRL_DATA_TYPE_SIGNED,
7303ea37523SVincent Palatin .get = uvc_ctrl_get_rel_speed,
7313ea37523SVincent Palatin .set = uvc_ctrl_set_rel_speed,
7323ea37523SVincent Palatin },
7333ea37523SVincent Palatin {
7340c0d06caSMauro Carvalho Chehab .id = V4L2_CID_PRIVACY,
7350c0d06caSMauro Carvalho Chehab .entity = UVC_GUID_UVC_CAMERA,
7360c0d06caSMauro Carvalho Chehab .selector = UVC_CT_PRIVACY_CONTROL,
7370c0d06caSMauro Carvalho Chehab .size = 1,
7380c0d06caSMauro Carvalho Chehab .offset = 0,
7390c0d06caSMauro Carvalho Chehab .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
7400c0d06caSMauro Carvalho Chehab .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
7410c0d06caSMauro Carvalho Chehab },
7426f6a87ebSRicardo Ribalda {
7436f6a87ebSRicardo Ribalda .id = V4L2_CID_PRIVACY,
7446f6a87ebSRicardo Ribalda .entity = UVC_GUID_EXT_GPIO_CONTROLLER,
7456f6a87ebSRicardo Ribalda .selector = UVC_CT_PRIVACY_CONTROL,
7466f6a87ebSRicardo Ribalda .size = 1,
7476f6a87ebSRicardo Ribalda .offset = 0,
7486f6a87ebSRicardo Ribalda .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
7496f6a87ebSRicardo Ribalda .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
7506f6a87ebSRicardo Ribalda },
7510c0d06caSMauro Carvalho Chehab };
7520c0d06caSMauro Carvalho Chehab
7533aa8628eSRicardo Ribalda const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited = {
7543aa8628eSRicardo Ribalda .id = V4L2_CID_POWER_LINE_FREQUENCY,
7553aa8628eSRicardo Ribalda .entity = UVC_GUID_UVC_PROCESSING,
7563aa8628eSRicardo Ribalda .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
7573aa8628eSRicardo Ribalda .size = 2,
7583aa8628eSRicardo Ribalda .offset = 0,
7593aa8628eSRicardo Ribalda .v4l2_type = V4L2_CTRL_TYPE_MENU,
7603aa8628eSRicardo Ribalda .data_type = UVC_CTRL_DATA_TYPE_ENUM,
7613aa8628eSRicardo Ribalda .menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_60HZ,
7623aa8628eSRicardo Ribalda V4L2_CID_POWER_LINE_FREQUENCY_50HZ),
7633aa8628eSRicardo Ribalda };
7643aa8628eSRicardo Ribalda
765a7c28150SRicardo Ribalda const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc11 = {
76671087116SRicardo Ribalda .id = V4L2_CID_POWER_LINE_FREQUENCY,
76771087116SRicardo Ribalda .entity = UVC_GUID_UVC_PROCESSING,
76871087116SRicardo Ribalda .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
76971087116SRicardo Ribalda .size = 2,
77071087116SRicardo Ribalda .offset = 0,
77171087116SRicardo Ribalda .v4l2_type = V4L2_CTRL_TYPE_MENU,
77271087116SRicardo Ribalda .data_type = UVC_CTRL_DATA_TYPE_ENUM,
77340140edaSRicardo Ribalda .menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_60HZ,
77440140edaSRicardo Ribalda V4L2_CID_POWER_LINE_FREQUENCY_DISABLED),
77571087116SRicardo Ribalda };
77671087116SRicardo Ribalda
77796a160b0SRicardo Ribalda static const struct uvc_control_mapping *uvc_ctrl_mappings_uvc11[] = {
77896a160b0SRicardo Ribalda &uvc_ctrl_power_line_mapping_uvc11,
77996a160b0SRicardo Ribalda NULL, /* Sentinel */
78096a160b0SRicardo Ribalda };
78196a160b0SRicardo Ribalda
78296a160b0SRicardo Ribalda static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc15 = {
78371087116SRicardo Ribalda .id = V4L2_CID_POWER_LINE_FREQUENCY,
78471087116SRicardo Ribalda .entity = UVC_GUID_UVC_PROCESSING,
78571087116SRicardo Ribalda .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
78671087116SRicardo Ribalda .size = 2,
78771087116SRicardo Ribalda .offset = 0,
78871087116SRicardo Ribalda .v4l2_type = V4L2_CTRL_TYPE_MENU,
78971087116SRicardo Ribalda .data_type = UVC_CTRL_DATA_TYPE_ENUM,
79040140edaSRicardo Ribalda .menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_AUTO,
79140140edaSRicardo Ribalda V4L2_CID_POWER_LINE_FREQUENCY_DISABLED),
79296a160b0SRicardo Ribalda };
79396a160b0SRicardo Ribalda
79496a160b0SRicardo Ribalda static const struct uvc_control_mapping *uvc_ctrl_mappings_uvc15[] = {
79596a160b0SRicardo Ribalda &uvc_ctrl_power_line_mapping_uvc15,
79696a160b0SRicardo Ribalda NULL, /* Sentinel */
79771087116SRicardo Ribalda };
79871087116SRicardo Ribalda
7990c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------
8000c0d06caSMauro Carvalho Chehab * Utility functions
8010c0d06caSMauro Carvalho Chehab */
8020c0d06caSMauro Carvalho Chehab
uvc_ctrl_data(struct uvc_control * ctrl,int id)8032c6b222cSLaurent Pinchart static inline u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id)
8040c0d06caSMauro Carvalho Chehab {
8050c0d06caSMauro Carvalho Chehab return ctrl->uvc_data + id * ctrl->info.size;
8060c0d06caSMauro Carvalho Chehab }
8070c0d06caSMauro Carvalho Chehab
uvc_test_bit(const u8 * data,int bit)8082c6b222cSLaurent Pinchart static inline int uvc_test_bit(const u8 *data, int bit)
8090c0d06caSMauro Carvalho Chehab {
8100c0d06caSMauro Carvalho Chehab return (data[bit >> 3] >> (bit & 7)) & 1;
8110c0d06caSMauro Carvalho Chehab }
8120c0d06caSMauro Carvalho Chehab
uvc_clear_bit(u8 * data,int bit)8132c6b222cSLaurent Pinchart static inline void uvc_clear_bit(u8 *data, int bit)
8140c0d06caSMauro Carvalho Chehab {
8150c0d06caSMauro Carvalho Chehab data[bit >> 3] &= ~(1 << (bit & 7));
8160c0d06caSMauro Carvalho Chehab }
8170c0d06caSMauro Carvalho Chehab
818699b9a86SLaurent Pinchart /*
819699b9a86SLaurent Pinchart * Extract the bit string specified by mapping->offset and mapping->size
8200c0d06caSMauro Carvalho Chehab * from the little-endian data stored at 'data' and return the result as
8210c0d06caSMauro Carvalho Chehab * a signed 32bit integer. Sign extension will be performed if the mapping
8220c0d06caSMauro Carvalho Chehab * references a signed data type.
8230c0d06caSMauro Carvalho Chehab */
uvc_get_le_value(struct uvc_control_mapping * mapping,u8 query,const u8 * data)8242c6b222cSLaurent Pinchart static s32 uvc_get_le_value(struct uvc_control_mapping *mapping,
8252c6b222cSLaurent Pinchart u8 query, const u8 *data)
8260c0d06caSMauro Carvalho Chehab {
8270c0d06caSMauro Carvalho Chehab int bits = mapping->size;
8280c0d06caSMauro Carvalho Chehab int offset = mapping->offset;
8292c6b222cSLaurent Pinchart s32 value = 0;
8302c6b222cSLaurent Pinchart u8 mask;
8310c0d06caSMauro Carvalho Chehab
8320c0d06caSMauro Carvalho Chehab data += offset / 8;
8330c0d06caSMauro Carvalho Chehab offset &= 7;
8340c0d06caSMauro Carvalho Chehab mask = ((1LL << bits) - 1) << offset;
8350c0d06caSMauro Carvalho Chehab
836171994e4SLaurent Pinchart while (1) {
8372c6b222cSLaurent Pinchart u8 byte = *data & mask;
8380c0d06caSMauro Carvalho Chehab value |= offset > 0 ? (byte >> offset) : (byte << (-offset));
8390c0d06caSMauro Carvalho Chehab bits -= 8 - (offset > 0 ? offset : 0);
840171994e4SLaurent Pinchart if (bits <= 0)
841171994e4SLaurent Pinchart break;
842171994e4SLaurent Pinchart
8430c0d06caSMauro Carvalho Chehab offset -= 8;
8440c0d06caSMauro Carvalho Chehab mask = (1 << bits) - 1;
845171994e4SLaurent Pinchart data++;
8460c0d06caSMauro Carvalho Chehab }
8470c0d06caSMauro Carvalho Chehab
8480c0d06caSMauro Carvalho Chehab /* Sign-extend the value if needed. */
8490c0d06caSMauro Carvalho Chehab if (mapping->data_type == UVC_CTRL_DATA_TYPE_SIGNED)
8500c0d06caSMauro Carvalho Chehab value |= -(value & (1 << (mapping->size - 1)));
8510c0d06caSMauro Carvalho Chehab
8520c0d06caSMauro Carvalho Chehab return value;
8530c0d06caSMauro Carvalho Chehab }
8540c0d06caSMauro Carvalho Chehab
855699b9a86SLaurent Pinchart /*
856699b9a86SLaurent Pinchart * Set the bit string specified by mapping->offset and mapping->size
8570c0d06caSMauro Carvalho Chehab * in the little-endian data stored at 'data' to the value 'value'.
8580c0d06caSMauro Carvalho Chehab */
uvc_set_le_value(struct uvc_control_mapping * mapping,s32 value,u8 * data)8590c0d06caSMauro Carvalho Chehab static void uvc_set_le_value(struct uvc_control_mapping *mapping,
8602c6b222cSLaurent Pinchart s32 value, u8 *data)
8610c0d06caSMauro Carvalho Chehab {
8620c0d06caSMauro Carvalho Chehab int bits = mapping->size;
8630c0d06caSMauro Carvalho Chehab int offset = mapping->offset;
8642c6b222cSLaurent Pinchart u8 mask;
8650c0d06caSMauro Carvalho Chehab
866699b9a86SLaurent Pinchart /*
867699b9a86SLaurent Pinchart * According to the v4l2 spec, writing any value to a button control
8680c0d06caSMauro Carvalho Chehab * should result in the action belonging to the button control being
8690c0d06caSMauro Carvalho Chehab * triggered. UVC devices however want to see a 1 written -> override
8700c0d06caSMauro Carvalho Chehab * value.
8710c0d06caSMauro Carvalho Chehab */
8720c0d06caSMauro Carvalho Chehab if (mapping->v4l2_type == V4L2_CTRL_TYPE_BUTTON)
8730c0d06caSMauro Carvalho Chehab value = -1;
8740c0d06caSMauro Carvalho Chehab
8750c0d06caSMauro Carvalho Chehab data += offset / 8;
8760c0d06caSMauro Carvalho Chehab offset &= 7;
8770c0d06caSMauro Carvalho Chehab
8780c0d06caSMauro Carvalho Chehab for (; bits > 0; data++) {
8790c0d06caSMauro Carvalho Chehab mask = ((1LL << bits) - 1) << offset;
8800c0d06caSMauro Carvalho Chehab *data = (*data & ~mask) | ((value << offset) & mask);
8810c0d06caSMauro Carvalho Chehab value >>= offset ? offset : 8;
8820c0d06caSMauro Carvalho Chehab bits -= 8 - offset;
8830c0d06caSMauro Carvalho Chehab offset = 0;
8840c0d06caSMauro Carvalho Chehab }
8850c0d06caSMauro Carvalho Chehab }
8860c0d06caSMauro Carvalho Chehab
8870c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------
8880c0d06caSMauro Carvalho Chehab * Terminal and unit management
8890c0d06caSMauro Carvalho Chehab */
8900c0d06caSMauro Carvalho Chehab
uvc_entity_match_guid(const struct uvc_entity * entity,const u8 guid[16])8910c0d06caSMauro Carvalho Chehab static int uvc_entity_match_guid(const struct uvc_entity *entity,
8922c6b222cSLaurent Pinchart const u8 guid[16])
8930c0d06caSMauro Carvalho Chehab {
894351509c6SRicardo Ribalda return memcmp(entity->guid, guid, sizeof(entity->guid)) == 0;
8950c0d06caSMauro Carvalho Chehab }
8960c0d06caSMauro Carvalho Chehab
8970c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------
8980c0d06caSMauro Carvalho Chehab * UVC Controls
8990c0d06caSMauro Carvalho Chehab */
9000c0d06caSMauro Carvalho Chehab
__uvc_find_control(struct uvc_entity * entity,u32 v4l2_id,struct uvc_control_mapping ** mapping,struct uvc_control ** control,int next)9012c6b222cSLaurent Pinchart static void __uvc_find_control(struct uvc_entity *entity, u32 v4l2_id,
9020c0d06caSMauro Carvalho Chehab struct uvc_control_mapping **mapping, struct uvc_control **control,
9030c0d06caSMauro Carvalho Chehab int next)
9040c0d06caSMauro Carvalho Chehab {
9050c0d06caSMauro Carvalho Chehab struct uvc_control *ctrl;
9060c0d06caSMauro Carvalho Chehab struct uvc_control_mapping *map;
9070c0d06caSMauro Carvalho Chehab unsigned int i;
9080c0d06caSMauro Carvalho Chehab
9090c0d06caSMauro Carvalho Chehab if (entity == NULL)
9100c0d06caSMauro Carvalho Chehab return;
9110c0d06caSMauro Carvalho Chehab
9120c0d06caSMauro Carvalho Chehab for (i = 0; i < entity->ncontrols; ++i) {
9130c0d06caSMauro Carvalho Chehab ctrl = &entity->controls[i];
9140c0d06caSMauro Carvalho Chehab if (!ctrl->initialized)
9150c0d06caSMauro Carvalho Chehab continue;
9160c0d06caSMauro Carvalho Chehab
9170c0d06caSMauro Carvalho Chehab list_for_each_entry(map, &ctrl->info.mappings, list) {
9180c0d06caSMauro Carvalho Chehab if ((map->id == v4l2_id) && !next) {
9190c0d06caSMauro Carvalho Chehab *control = ctrl;
9200c0d06caSMauro Carvalho Chehab *mapping = map;
9210c0d06caSMauro Carvalho Chehab return;
9220c0d06caSMauro Carvalho Chehab }
9230c0d06caSMauro Carvalho Chehab
9240c0d06caSMauro Carvalho Chehab if ((*mapping == NULL || (*mapping)->id > map->id) &&
9250c0d06caSMauro Carvalho Chehab (map->id > v4l2_id) && next) {
9260c0d06caSMauro Carvalho Chehab *control = ctrl;
9270c0d06caSMauro Carvalho Chehab *mapping = map;
9280c0d06caSMauro Carvalho Chehab }
9290c0d06caSMauro Carvalho Chehab }
9300c0d06caSMauro Carvalho Chehab }
9310c0d06caSMauro Carvalho Chehab }
9320c0d06caSMauro Carvalho Chehab
uvc_find_control(struct uvc_video_chain * chain,u32 v4l2_id,struct uvc_control_mapping ** mapping)9330c0d06caSMauro Carvalho Chehab static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
9342c6b222cSLaurent Pinchart u32 v4l2_id, struct uvc_control_mapping **mapping)
9350c0d06caSMauro Carvalho Chehab {
9360c0d06caSMauro Carvalho Chehab struct uvc_control *ctrl = NULL;
9370c0d06caSMauro Carvalho Chehab struct uvc_entity *entity;
9380c0d06caSMauro Carvalho Chehab int next = v4l2_id & V4L2_CTRL_FLAG_NEXT_CTRL;
9390c0d06caSMauro Carvalho Chehab
9400c0d06caSMauro Carvalho Chehab *mapping = NULL;
9410c0d06caSMauro Carvalho Chehab
9420c0d06caSMauro Carvalho Chehab /* Mask the query flags. */
9430c0d06caSMauro Carvalho Chehab v4l2_id &= V4L2_CTRL_ID_MASK;
9440c0d06caSMauro Carvalho Chehab
9450c0d06caSMauro Carvalho Chehab /* Find the control. */
9460c0d06caSMauro Carvalho Chehab list_for_each_entry(entity, &chain->entities, chain) {
9470c0d06caSMauro Carvalho Chehab __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next);
9480c0d06caSMauro Carvalho Chehab if (ctrl && !next)
9490c0d06caSMauro Carvalho Chehab return ctrl;
9500c0d06caSMauro Carvalho Chehab }
9510c0d06caSMauro Carvalho Chehab
9520c0d06caSMauro Carvalho Chehab if (ctrl == NULL && !next)
9539e56380aSJoe Perches uvc_dbg(chain->dev, CONTROL, "Control 0x%08x not found\n",
9549e56380aSJoe Perches v4l2_id);
9550c0d06caSMauro Carvalho Chehab
9560c0d06caSMauro Carvalho Chehab return ctrl;
9570c0d06caSMauro Carvalho Chehab }
9580c0d06caSMauro Carvalho Chehab
uvc_ctrl_populate_cache(struct uvc_video_chain * chain,struct uvc_control * ctrl)9590c0d06caSMauro Carvalho Chehab static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain,
9600c0d06caSMauro Carvalho Chehab struct uvc_control *ctrl)
9610c0d06caSMauro Carvalho Chehab {
9620c0d06caSMauro Carvalho Chehab int ret;
9630c0d06caSMauro Carvalho Chehab
9640c0d06caSMauro Carvalho Chehab if (ctrl->info.flags & UVC_CTRL_FLAG_GET_DEF) {
9650c0d06caSMauro Carvalho Chehab ret = uvc_query_ctrl(chain->dev, UVC_GET_DEF, ctrl->entity->id,
9660c0d06caSMauro Carvalho Chehab chain->dev->intfnum, ctrl->info.selector,
9670c0d06caSMauro Carvalho Chehab uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF),
9680c0d06caSMauro Carvalho Chehab ctrl->info.size);
9690c0d06caSMauro Carvalho Chehab if (ret < 0)
9700c0d06caSMauro Carvalho Chehab return ret;
9710c0d06caSMauro Carvalho Chehab }
9720c0d06caSMauro Carvalho Chehab
9730c0d06caSMauro Carvalho Chehab if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN) {
9740c0d06caSMauro Carvalho Chehab ret = uvc_query_ctrl(chain->dev, UVC_GET_MIN, ctrl->entity->id,
9750c0d06caSMauro Carvalho Chehab chain->dev->intfnum, ctrl->info.selector,
9760c0d06caSMauro Carvalho Chehab uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN),
9770c0d06caSMauro Carvalho Chehab ctrl->info.size);
9780c0d06caSMauro Carvalho Chehab if (ret < 0)
9790c0d06caSMauro Carvalho Chehab return ret;
9800c0d06caSMauro Carvalho Chehab }
9810c0d06caSMauro Carvalho Chehab if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX) {
9820c0d06caSMauro Carvalho Chehab ret = uvc_query_ctrl(chain->dev, UVC_GET_MAX, ctrl->entity->id,
9830c0d06caSMauro Carvalho Chehab chain->dev->intfnum, ctrl->info.selector,
9840c0d06caSMauro Carvalho Chehab uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX),
9850c0d06caSMauro Carvalho Chehab ctrl->info.size);
9860c0d06caSMauro Carvalho Chehab if (ret < 0)
9870c0d06caSMauro Carvalho Chehab return ret;
9880c0d06caSMauro Carvalho Chehab }
9890c0d06caSMauro Carvalho Chehab if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES) {
9900c0d06caSMauro Carvalho Chehab ret = uvc_query_ctrl(chain->dev, UVC_GET_RES, ctrl->entity->id,
9910c0d06caSMauro Carvalho Chehab chain->dev->intfnum, ctrl->info.selector,
9920c0d06caSMauro Carvalho Chehab uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES),
9930c0d06caSMauro Carvalho Chehab ctrl->info.size);
9940c0d06caSMauro Carvalho Chehab if (ret < 0) {
9950c0d06caSMauro Carvalho Chehab if (UVC_ENTITY_TYPE(ctrl->entity) !=
9960c0d06caSMauro Carvalho Chehab UVC_VC_EXTENSION_UNIT)
9970c0d06caSMauro Carvalho Chehab return ret;
9980c0d06caSMauro Carvalho Chehab
999699b9a86SLaurent Pinchart /*
1000699b9a86SLaurent Pinchart * GET_RES is mandatory for XU controls, but some
10010c0d06caSMauro Carvalho Chehab * cameras still choke on it. Ignore errors and set the
10020c0d06caSMauro Carvalho Chehab * resolution value to zero.
10030c0d06caSMauro Carvalho Chehab */
10040c0d06caSMauro Carvalho Chehab uvc_warn_once(chain->dev, UVC_WARN_XU_GET_RES,
10050c0d06caSMauro Carvalho Chehab "UVC non compliance - GET_RES failed on "
10060c0d06caSMauro Carvalho Chehab "an XU control. Enabling workaround.\n");
10070c0d06caSMauro Carvalho Chehab memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES), 0,
10080c0d06caSMauro Carvalho Chehab ctrl->info.size);
10090c0d06caSMauro Carvalho Chehab }
10100c0d06caSMauro Carvalho Chehab }
10110c0d06caSMauro Carvalho Chehab
10120c0d06caSMauro Carvalho Chehab ctrl->cached = 1;
10130c0d06caSMauro Carvalho Chehab return 0;
10140c0d06caSMauro Carvalho Chehab }
10150c0d06caSMauro Carvalho Chehab
__uvc_ctrl_get_value(struct uvc_control_mapping * mapping,const u8 * data)1016e5225c82SGuennadi Liakhovetski static s32 __uvc_ctrl_get_value(struct uvc_control_mapping *mapping,
1017e5225c82SGuennadi Liakhovetski const u8 *data)
1018e5225c82SGuennadi Liakhovetski {
1019e5225c82SGuennadi Liakhovetski s32 value = mapping->get(mapping, UVC_GET_CUR, data);
1020e5225c82SGuennadi Liakhovetski
1021e5225c82SGuennadi Liakhovetski if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) {
1022e5225c82SGuennadi Liakhovetski unsigned int i;
1023e5225c82SGuennadi Liakhovetski
1024716c3304SRicardo Ribalda for (i = 0; BIT(i) <= mapping->menu_mask; ++i) {
1025716c3304SRicardo Ribalda u32 menu_value;
1026716c3304SRicardo Ribalda
102740140edaSRicardo Ribalda if (!test_bit(i, &mapping->menu_mask))
102840140edaSRicardo Ribalda continue;
1029716c3304SRicardo Ribalda
1030716c3304SRicardo Ribalda menu_value = uvc_mapping_get_menu_value(mapping, i);
1031716c3304SRicardo Ribalda
1032716c3304SRicardo Ribalda if (menu_value == value) {
1033e5225c82SGuennadi Liakhovetski value = i;
1034e5225c82SGuennadi Liakhovetski break;
1035e5225c82SGuennadi Liakhovetski }
1036e5225c82SGuennadi Liakhovetski }
1037e5225c82SGuennadi Liakhovetski }
1038e5225c82SGuennadi Liakhovetski
1039e5225c82SGuennadi Liakhovetski return value;
1040e5225c82SGuennadi Liakhovetski }
1041e5225c82SGuennadi Liakhovetski
__uvc_ctrl_load_cur(struct uvc_video_chain * chain,struct uvc_control * ctrl)10425f36851cSYunke Cao static int __uvc_ctrl_load_cur(struct uvc_video_chain *chain,
10435f36851cSYunke Cao struct uvc_control *ctrl)
10445f36851cSYunke Cao {
10455f36851cSYunke Cao u8 *data;
10465f36851cSYunke Cao int ret;
10475f36851cSYunke Cao
10485f36851cSYunke Cao if (ctrl->loaded)
10495f36851cSYunke Cao return 0;
10505f36851cSYunke Cao
10515f36851cSYunke Cao data = uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT);
10525f36851cSYunke Cao
10535f36851cSYunke Cao if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0) {
10545f36851cSYunke Cao memset(data, 0, ctrl->info.size);
10555f36851cSYunke Cao ctrl->loaded = 1;
10565f36851cSYunke Cao
10575f36851cSYunke Cao return 0;
10585f36851cSYunke Cao }
10595f36851cSYunke Cao
10605f36851cSYunke Cao if (ctrl->entity->get_cur)
10615f36851cSYunke Cao ret = ctrl->entity->get_cur(chain->dev, ctrl->entity,
10625f36851cSYunke Cao ctrl->info.selector, data,
10635f36851cSYunke Cao ctrl->info.size);
10645f36851cSYunke Cao else
10655f36851cSYunke Cao ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
10665f36851cSYunke Cao ctrl->entity->id, chain->dev->intfnum,
10675f36851cSYunke Cao ctrl->info.selector, data,
10685f36851cSYunke Cao ctrl->info.size);
10695f36851cSYunke Cao
10705f36851cSYunke Cao if (ret < 0)
10715f36851cSYunke Cao return ret;
10725f36851cSYunke Cao
10735f36851cSYunke Cao ctrl->loaded = 1;
10745f36851cSYunke Cao
10755f36851cSYunke Cao return ret;
10765f36851cSYunke Cao }
10775f36851cSYunke Cao
__uvc_ctrl_get(struct uvc_video_chain * chain,struct uvc_control * ctrl,struct uvc_control_mapping * mapping,s32 * value)10780c0d06caSMauro Carvalho Chehab static int __uvc_ctrl_get(struct uvc_video_chain *chain,
10795f36851cSYunke Cao struct uvc_control *ctrl,
10805f36851cSYunke Cao struct uvc_control_mapping *mapping,
10810c0d06caSMauro Carvalho Chehab s32 *value)
10820c0d06caSMauro Carvalho Chehab {
10830c0d06caSMauro Carvalho Chehab int ret;
10840c0d06caSMauro Carvalho Chehab
10850c0d06caSMauro Carvalho Chehab if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0)
108630ecb936SLaurent Pinchart return -EACCES;
10870c0d06caSMauro Carvalho Chehab
10885f36851cSYunke Cao ret = __uvc_ctrl_load_cur(chain, ctrl);
10890c0d06caSMauro Carvalho Chehab if (ret < 0)
10900c0d06caSMauro Carvalho Chehab return ret;
10910c0d06caSMauro Carvalho Chehab
1092e5225c82SGuennadi Liakhovetski *value = __uvc_ctrl_get_value(mapping,
10930c0d06caSMauro Carvalho Chehab uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
10940c0d06caSMauro Carvalho Chehab
10950c0d06caSMauro Carvalho Chehab return 0;
10960c0d06caSMauro Carvalho Chehab }
10970c0d06caSMauro Carvalho Chehab
__uvc_query_v4l2_class(struct uvc_video_chain * chain,u32 req_id,u32 found_id)10989b31ea80SRicardo Ribalda static int __uvc_query_v4l2_class(struct uvc_video_chain *chain, u32 req_id,
10999b31ea80SRicardo Ribalda u32 found_id)
11009b31ea80SRicardo Ribalda {
11019b31ea80SRicardo Ribalda bool find_next = req_id & V4L2_CTRL_FLAG_NEXT_CTRL;
11029b31ea80SRicardo Ribalda unsigned int i;
11039b31ea80SRicardo Ribalda
11049b31ea80SRicardo Ribalda req_id &= V4L2_CTRL_ID_MASK;
11059b31ea80SRicardo Ribalda
11069b31ea80SRicardo Ribalda for (i = 0; i < ARRAY_SIZE(uvc_control_classes); i++) {
11079b31ea80SRicardo Ribalda if (!(chain->ctrl_class_bitmap & BIT(i)))
11089b31ea80SRicardo Ribalda continue;
11099b31ea80SRicardo Ribalda if (!find_next) {
11109b31ea80SRicardo Ribalda if (uvc_control_classes[i] == req_id)
11119b31ea80SRicardo Ribalda return i;
11129b31ea80SRicardo Ribalda continue;
11139b31ea80SRicardo Ribalda }
11149b31ea80SRicardo Ribalda if (uvc_control_classes[i] > req_id &&
11159b31ea80SRicardo Ribalda uvc_control_classes[i] < found_id)
11169b31ea80SRicardo Ribalda return i;
11179b31ea80SRicardo Ribalda }
11189b31ea80SRicardo Ribalda
11199b31ea80SRicardo Ribalda return -ENODEV;
11209b31ea80SRicardo Ribalda }
11219b31ea80SRicardo Ribalda
uvc_query_v4l2_class(struct uvc_video_chain * chain,u32 req_id,u32 found_id,struct v4l2_queryctrl * v4l2_ctrl)11229b31ea80SRicardo Ribalda static int uvc_query_v4l2_class(struct uvc_video_chain *chain, u32 req_id,
11239b31ea80SRicardo Ribalda u32 found_id, struct v4l2_queryctrl *v4l2_ctrl)
11249b31ea80SRicardo Ribalda {
11259b31ea80SRicardo Ribalda int idx;
11269b31ea80SRicardo Ribalda
11279b31ea80SRicardo Ribalda idx = __uvc_query_v4l2_class(chain, req_id, found_id);
11289b31ea80SRicardo Ribalda if (idx < 0)
11299b31ea80SRicardo Ribalda return -ENODEV;
11309b31ea80SRicardo Ribalda
11319b31ea80SRicardo Ribalda memset(v4l2_ctrl, 0, sizeof(*v4l2_ctrl));
11329b31ea80SRicardo Ribalda v4l2_ctrl->id = uvc_control_classes[idx];
11339b31ea80SRicardo Ribalda strscpy(v4l2_ctrl->name, v4l2_ctrl_get_name(v4l2_ctrl->id),
11349b31ea80SRicardo Ribalda sizeof(v4l2_ctrl->name));
11359b31ea80SRicardo Ribalda v4l2_ctrl->type = V4L2_CTRL_TYPE_CTRL_CLASS;
11369b31ea80SRicardo Ribalda v4l2_ctrl->flags = V4L2_CTRL_FLAG_WRITE_ONLY
11379b31ea80SRicardo Ribalda | V4L2_CTRL_FLAG_READ_ONLY;
11389b31ea80SRicardo Ribalda return 0;
11399b31ea80SRicardo Ribalda }
11409b31ea80SRicardo Ribalda
11419f582f04SHans Verkuil /*
11429f582f04SHans Verkuil * Check if control @v4l2_id can be accessed by the given control @ioctl
11439f582f04SHans Verkuil * (VIDIOC_G_EXT_CTRLS, VIDIOC_TRY_EXT_CTRLS or VIDIOC_S_EXT_CTRLS).
11449f582f04SHans Verkuil *
11459f582f04SHans Verkuil * For set operations on slave controls, check if the master's value is set to
11469f582f04SHans Verkuil * manual, either in the others controls set in the same ioctl call, or from
11479f582f04SHans Verkuil * the master's current value. This catches VIDIOC_S_EXT_CTRLS calls that set
11489f582f04SHans Verkuil * both the master and slave control, such as for instance setting
11499f582f04SHans Verkuil * auto_exposure=1, exposure_time_absolute=251.
11509f582f04SHans Verkuil */
uvc_ctrl_is_accessible(struct uvc_video_chain * chain,u32 v4l2_id,const struct v4l2_ext_controls * ctrls,unsigned long ioctl)1151ee929d5aSRicardo Ribalda int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
11529f582f04SHans Verkuil const struct v4l2_ext_controls *ctrls,
11539f582f04SHans Verkuil unsigned long ioctl)
1154ee929d5aSRicardo Ribalda {
11559f582f04SHans Verkuil struct uvc_control_mapping *master_map = NULL;
11569f582f04SHans Verkuil struct uvc_control *master_ctrl = NULL;
1157ee929d5aSRicardo Ribalda struct uvc_control_mapping *mapping;
1158ee929d5aSRicardo Ribalda struct uvc_control *ctrl;
11599f582f04SHans Verkuil bool read = ioctl == VIDIOC_G_EXT_CTRLS;
11609f582f04SHans Verkuil s32 val;
11619f582f04SHans Verkuil int ret;
11629f582f04SHans Verkuil int i;
1163ee929d5aSRicardo Ribalda
1164ee929d5aSRicardo Ribalda if (__uvc_query_v4l2_class(chain, v4l2_id, 0) >= 0)
1165ee929d5aSRicardo Ribalda return -EACCES;
1166ee929d5aSRicardo Ribalda
1167ee929d5aSRicardo Ribalda ctrl = uvc_find_control(chain, v4l2_id, &mapping);
1168ee929d5aSRicardo Ribalda if (!ctrl)
1169ee929d5aSRicardo Ribalda return -EINVAL;
1170ee929d5aSRicardo Ribalda
1171ee929d5aSRicardo Ribalda if (!(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) && read)
1172ee929d5aSRicardo Ribalda return -EACCES;
1173ee929d5aSRicardo Ribalda
1174ee929d5aSRicardo Ribalda if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR) && !read)
1175ee929d5aSRicardo Ribalda return -EACCES;
1176ee929d5aSRicardo Ribalda
11779f582f04SHans Verkuil if (ioctl != VIDIOC_S_EXT_CTRLS || !mapping->master_id)
11789f582f04SHans Verkuil return 0;
11799f582f04SHans Verkuil
11809f582f04SHans Verkuil /*
11819f582f04SHans Verkuil * Iterate backwards in cases where the master control is accessed
11829f582f04SHans Verkuil * multiple times in the same ioctl. We want the last value.
11839f582f04SHans Verkuil */
11849f582f04SHans Verkuil for (i = ctrls->count - 1; i >= 0; i--) {
11859f582f04SHans Verkuil if (ctrls->controls[i].id == mapping->master_id)
11869f582f04SHans Verkuil return ctrls->controls[i].value ==
11879f582f04SHans Verkuil mapping->master_manual ? 0 : -EACCES;
11889f582f04SHans Verkuil }
11899f582f04SHans Verkuil
11909f582f04SHans Verkuil __uvc_find_control(ctrl->entity, mapping->master_id, &master_map,
11919f582f04SHans Verkuil &master_ctrl, 0);
11929f582f04SHans Verkuil
11939f582f04SHans Verkuil if (!master_ctrl || !(master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR))
11949f582f04SHans Verkuil return 0;
11959f582f04SHans Verkuil
11969f582f04SHans Verkuil ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val);
11979f582f04SHans Verkuil if (ret >= 0 && val != mapping->master_manual)
11989f582f04SHans Verkuil return -EACCES;
11999f582f04SHans Verkuil
1200ee929d5aSRicardo Ribalda return 0;
1201ee929d5aSRicardo Ribalda }
1202ee929d5aSRicardo Ribalda
uvc_map_get_name(const struct uvc_control_mapping * map)120370fa906dSRicardo Ribalda static const char *uvc_map_get_name(const struct uvc_control_mapping *map)
120470fa906dSRicardo Ribalda {
120570fa906dSRicardo Ribalda const char *name;
120670fa906dSRicardo Ribalda
120770fa906dSRicardo Ribalda if (map->name)
120870fa906dSRicardo Ribalda return map->name;
120970fa906dSRicardo Ribalda
121070fa906dSRicardo Ribalda name = v4l2_ctrl_get_name(map->id);
121170fa906dSRicardo Ribalda if (name)
121270fa906dSRicardo Ribalda return name;
121370fa906dSRicardo Ribalda
121470fa906dSRicardo Ribalda return "Unknown Control";
121570fa906dSRicardo Ribalda }
121670fa906dSRicardo Ribalda
uvc_get_ctrl_bitmap(struct uvc_control * ctrl,struct uvc_control_mapping * mapping)12177faf8ae4SRicardo Ribalda static u32 uvc_get_ctrl_bitmap(struct uvc_control *ctrl,
12187faf8ae4SRicardo Ribalda struct uvc_control_mapping *mapping)
12197faf8ae4SRicardo Ribalda {
12207faf8ae4SRicardo Ribalda /*
12217faf8ae4SRicardo Ribalda * Some controls, like CT_AE_MODE_CONTROL, use GET_RES to represent
12227faf8ae4SRicardo Ribalda * the number of bits supported. Those controls do not list GET_MAX
12237faf8ae4SRicardo Ribalda * as supported.
12247faf8ae4SRicardo Ribalda */
12257faf8ae4SRicardo Ribalda if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)
12267faf8ae4SRicardo Ribalda return mapping->get(mapping, UVC_GET_RES,
12277faf8ae4SRicardo Ribalda uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
12287faf8ae4SRicardo Ribalda
12297faf8ae4SRicardo Ribalda if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX)
12307faf8ae4SRicardo Ribalda return mapping->get(mapping, UVC_GET_MAX,
12317faf8ae4SRicardo Ribalda uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));
12327faf8ae4SRicardo Ribalda
12337faf8ae4SRicardo Ribalda return ~0;
12347faf8ae4SRicardo Ribalda }
12357faf8ae4SRicardo Ribalda
__uvc_query_v4l2_ctrl(struct uvc_video_chain * chain,struct uvc_control * ctrl,struct uvc_control_mapping * mapping,struct v4l2_queryctrl * v4l2_ctrl)12360c0d06caSMauro Carvalho Chehab static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
12370c0d06caSMauro Carvalho Chehab struct uvc_control *ctrl,
12380c0d06caSMauro Carvalho Chehab struct uvc_control_mapping *mapping,
12390c0d06caSMauro Carvalho Chehab struct v4l2_queryctrl *v4l2_ctrl)
12400c0d06caSMauro Carvalho Chehab {
12410c0d06caSMauro Carvalho Chehab struct uvc_control_mapping *master_map = NULL;
12420c0d06caSMauro Carvalho Chehab struct uvc_control *master_ctrl = NULL;
12430c0d06caSMauro Carvalho Chehab unsigned int i;
12440c0d06caSMauro Carvalho Chehab
1245f14d4988SLaurent Pinchart memset(v4l2_ctrl, 0, sizeof(*v4l2_ctrl));
12460c0d06caSMauro Carvalho Chehab v4l2_ctrl->id = mapping->id;
12470c0d06caSMauro Carvalho Chehab v4l2_ctrl->type = mapping->v4l2_type;
124870fa906dSRicardo Ribalda strscpy(v4l2_ctrl->name, uvc_map_get_name(mapping),
124970fa906dSRicardo Ribalda sizeof(v4l2_ctrl->name));
12500c0d06caSMauro Carvalho Chehab v4l2_ctrl->flags = 0;
12510c0d06caSMauro Carvalho Chehab
12520c0d06caSMauro Carvalho Chehab if (!(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR))
12530c0d06caSMauro Carvalho Chehab v4l2_ctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
12540c0d06caSMauro Carvalho Chehab if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR))
12550c0d06caSMauro Carvalho Chehab v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
12560c0d06caSMauro Carvalho Chehab
12570c0d06caSMauro Carvalho Chehab if (mapping->master_id)
12580c0d06caSMauro Carvalho Chehab __uvc_find_control(ctrl->entity, mapping->master_id,
12590c0d06caSMauro Carvalho Chehab &master_map, &master_ctrl, 0);
12600c0d06caSMauro Carvalho Chehab if (master_ctrl && (master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) {
12610c0d06caSMauro Carvalho Chehab s32 val;
12620c0d06caSMauro Carvalho Chehab int ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val);
12630c0d06caSMauro Carvalho Chehab if (ret < 0)
12640c0d06caSMauro Carvalho Chehab return ret;
12650c0d06caSMauro Carvalho Chehab
12660c0d06caSMauro Carvalho Chehab if (val != mapping->master_manual)
12670c0d06caSMauro Carvalho Chehab v4l2_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
12680c0d06caSMauro Carvalho Chehab }
12690c0d06caSMauro Carvalho Chehab
12700c0d06caSMauro Carvalho Chehab if (!ctrl->cached) {
12710c0d06caSMauro Carvalho Chehab int ret = uvc_ctrl_populate_cache(chain, ctrl);
12720c0d06caSMauro Carvalho Chehab if (ret < 0)
12730c0d06caSMauro Carvalho Chehab return ret;
12740c0d06caSMauro Carvalho Chehab }
12750c0d06caSMauro Carvalho Chehab
12760c0d06caSMauro Carvalho Chehab if (ctrl->info.flags & UVC_CTRL_FLAG_GET_DEF) {
12770c0d06caSMauro Carvalho Chehab v4l2_ctrl->default_value = mapping->get(mapping, UVC_GET_DEF,
12780c0d06caSMauro Carvalho Chehab uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF));
12790c0d06caSMauro Carvalho Chehab }
12800c0d06caSMauro Carvalho Chehab
12810c0d06caSMauro Carvalho Chehab switch (mapping->v4l2_type) {
12820c0d06caSMauro Carvalho Chehab case V4L2_CTRL_TYPE_MENU:
128340140edaSRicardo Ribalda v4l2_ctrl->minimum = ffs(mapping->menu_mask) - 1;
128440140edaSRicardo Ribalda v4l2_ctrl->maximum = fls(mapping->menu_mask) - 1;
12850c0d06caSMauro Carvalho Chehab v4l2_ctrl->step = 1;
12860c0d06caSMauro Carvalho Chehab
1287716c3304SRicardo Ribalda for (i = 0; BIT(i) <= mapping->menu_mask; ++i) {
1288716c3304SRicardo Ribalda u32 menu_value;
1289716c3304SRicardo Ribalda
129040140edaSRicardo Ribalda if (!test_bit(i, &mapping->menu_mask))
129140140edaSRicardo Ribalda continue;
1292716c3304SRicardo Ribalda
1293716c3304SRicardo Ribalda menu_value = uvc_mapping_get_menu_value(mapping, i);
1294716c3304SRicardo Ribalda
1295716c3304SRicardo Ribalda if (menu_value == v4l2_ctrl->default_value) {
12960c0d06caSMauro Carvalho Chehab v4l2_ctrl->default_value = i;
12970c0d06caSMauro Carvalho Chehab break;
12980c0d06caSMauro Carvalho Chehab }
12990c0d06caSMauro Carvalho Chehab }
13000c0d06caSMauro Carvalho Chehab
13010c0d06caSMauro Carvalho Chehab return 0;
13020c0d06caSMauro Carvalho Chehab
13030c0d06caSMauro Carvalho Chehab case V4L2_CTRL_TYPE_BOOLEAN:
13040c0d06caSMauro Carvalho Chehab v4l2_ctrl->minimum = 0;
13050c0d06caSMauro Carvalho Chehab v4l2_ctrl->maximum = 1;
13060c0d06caSMauro Carvalho Chehab v4l2_ctrl->step = 1;
13070c0d06caSMauro Carvalho Chehab return 0;
13080c0d06caSMauro Carvalho Chehab
13090c0d06caSMauro Carvalho Chehab case V4L2_CTRL_TYPE_BUTTON:
13100c0d06caSMauro Carvalho Chehab v4l2_ctrl->minimum = 0;
13110c0d06caSMauro Carvalho Chehab v4l2_ctrl->maximum = 0;
13120c0d06caSMauro Carvalho Chehab v4l2_ctrl->step = 0;
13130c0d06caSMauro Carvalho Chehab return 0;
13140c0d06caSMauro Carvalho Chehab
13157faf8ae4SRicardo Ribalda case V4L2_CTRL_TYPE_BITMASK:
13167faf8ae4SRicardo Ribalda v4l2_ctrl->minimum = 0;
13177faf8ae4SRicardo Ribalda v4l2_ctrl->maximum = uvc_get_ctrl_bitmap(ctrl, mapping);
13187faf8ae4SRicardo Ribalda v4l2_ctrl->step = 0;
13197faf8ae4SRicardo Ribalda return 0;
13207faf8ae4SRicardo Ribalda
13210c0d06caSMauro Carvalho Chehab default:
13220c0d06caSMauro Carvalho Chehab break;
13230c0d06caSMauro Carvalho Chehab }
13240c0d06caSMauro Carvalho Chehab
13250c0d06caSMauro Carvalho Chehab if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN)
13260c0d06caSMauro Carvalho Chehab v4l2_ctrl->minimum = mapping->get(mapping, UVC_GET_MIN,
13270c0d06caSMauro Carvalho Chehab uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));
13280c0d06caSMauro Carvalho Chehab
13290c0d06caSMauro Carvalho Chehab if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX)
13300c0d06caSMauro Carvalho Chehab v4l2_ctrl->maximum = mapping->get(mapping, UVC_GET_MAX,
13310c0d06caSMauro Carvalho Chehab uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));
13320c0d06caSMauro Carvalho Chehab
13330c0d06caSMauro Carvalho Chehab if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)
13340c0d06caSMauro Carvalho Chehab v4l2_ctrl->step = mapping->get(mapping, UVC_GET_RES,
13350c0d06caSMauro Carvalho Chehab uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
13360c0d06caSMauro Carvalho Chehab
13370c0d06caSMauro Carvalho Chehab return 0;
13380c0d06caSMauro Carvalho Chehab }
13390c0d06caSMauro Carvalho Chehab
uvc_query_v4l2_ctrl(struct uvc_video_chain * chain,struct v4l2_queryctrl * v4l2_ctrl)13400c0d06caSMauro Carvalho Chehab int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
13410c0d06caSMauro Carvalho Chehab struct v4l2_queryctrl *v4l2_ctrl)
13420c0d06caSMauro Carvalho Chehab {
13430c0d06caSMauro Carvalho Chehab struct uvc_control *ctrl;
13440c0d06caSMauro Carvalho Chehab struct uvc_control_mapping *mapping;
13450c0d06caSMauro Carvalho Chehab int ret;
13460c0d06caSMauro Carvalho Chehab
13470c0d06caSMauro Carvalho Chehab ret = mutex_lock_interruptible(&chain->ctrl_mutex);
13480c0d06caSMauro Carvalho Chehab if (ret < 0)
13490c0d06caSMauro Carvalho Chehab return -ERESTARTSYS;
13500c0d06caSMauro Carvalho Chehab
13519b31ea80SRicardo Ribalda /* Check if the ctrl is a know class */
13529b31ea80SRicardo Ribalda if (!(v4l2_ctrl->id & V4L2_CTRL_FLAG_NEXT_CTRL)) {
13539b31ea80SRicardo Ribalda ret = uvc_query_v4l2_class(chain, v4l2_ctrl->id, 0, v4l2_ctrl);
13549b31ea80SRicardo Ribalda if (!ret)
13559b31ea80SRicardo Ribalda goto done;
13569b31ea80SRicardo Ribalda }
13579b31ea80SRicardo Ribalda
13580c0d06caSMauro Carvalho Chehab ctrl = uvc_find_control(chain, v4l2_ctrl->id, &mapping);
13590c0d06caSMauro Carvalho Chehab if (ctrl == NULL) {
13609c016d61SRafael J. Wysocki ret = -EINVAL;
13610c0d06caSMauro Carvalho Chehab goto done;
13620c0d06caSMauro Carvalho Chehab }
13630c0d06caSMauro Carvalho Chehab
13649b31ea80SRicardo Ribalda /*
13659b31ea80SRicardo Ribalda * If we're enumerating control with V4L2_CTRL_FLAG_NEXT_CTRL, check if
13669b31ea80SRicardo Ribalda * a class should be inserted between the previous control and the one
13679b31ea80SRicardo Ribalda * we have just found.
13689b31ea80SRicardo Ribalda */
13699b31ea80SRicardo Ribalda if (v4l2_ctrl->id & V4L2_CTRL_FLAG_NEXT_CTRL) {
13709b31ea80SRicardo Ribalda ret = uvc_query_v4l2_class(chain, v4l2_ctrl->id, mapping->id,
13719b31ea80SRicardo Ribalda v4l2_ctrl);
13729b31ea80SRicardo Ribalda if (!ret)
13739b31ea80SRicardo Ribalda goto done;
13749b31ea80SRicardo Ribalda }
13759b31ea80SRicardo Ribalda
13760c0d06caSMauro Carvalho Chehab ret = __uvc_query_v4l2_ctrl(chain, ctrl, mapping, v4l2_ctrl);
13770c0d06caSMauro Carvalho Chehab done:
13780c0d06caSMauro Carvalho Chehab mutex_unlock(&chain->ctrl_mutex);
13790c0d06caSMauro Carvalho Chehab return ret;
13800c0d06caSMauro Carvalho Chehab }
13810c0d06caSMauro Carvalho Chehab
13820c0d06caSMauro Carvalho Chehab /*
1383ec55cdbaSKieran Bingham * Mapping V4L2 controls to UVC controls can be straightforward if done well.
13840c0d06caSMauro Carvalho Chehab * Most of the UVC controls exist in V4L2, and can be mapped directly. Some
13850c0d06caSMauro Carvalho Chehab * must be grouped (for instance the Red Balance, Blue Balance and Do White
13860c0d06caSMauro Carvalho Chehab * Balance V4L2 controls use the White Balance Component UVC control) or
13870c0d06caSMauro Carvalho Chehab * otherwise translated. The approach we take here is to use a translation
13880c0d06caSMauro Carvalho Chehab * table for the controls that can be mapped directly, and handle the others
13890c0d06caSMauro Carvalho Chehab * manually.
13900c0d06caSMauro Carvalho Chehab */
uvc_query_v4l2_menu(struct uvc_video_chain * chain,struct v4l2_querymenu * query_menu)13910c0d06caSMauro Carvalho Chehab int uvc_query_v4l2_menu(struct uvc_video_chain *chain,
13920c0d06caSMauro Carvalho Chehab struct v4l2_querymenu *query_menu)
13930c0d06caSMauro Carvalho Chehab {
13940c0d06caSMauro Carvalho Chehab struct uvc_control_mapping *mapping;
13950c0d06caSMauro Carvalho Chehab struct uvc_control *ctrl;
13960c0d06caSMauro Carvalho Chehab u32 index = query_menu->index;
13970c0d06caSMauro Carvalho Chehab u32 id = query_menu->id;
1398716c3304SRicardo Ribalda const char *name;
13990c0d06caSMauro Carvalho Chehab int ret;
14000c0d06caSMauro Carvalho Chehab
14010c0d06caSMauro Carvalho Chehab memset(query_menu, 0, sizeof(*query_menu));
14020c0d06caSMauro Carvalho Chehab query_menu->id = id;
14030c0d06caSMauro Carvalho Chehab query_menu->index = index;
14040c0d06caSMauro Carvalho Chehab
140541ebaa5eSRicardo Ribalda if (index >= BITS_PER_TYPE(mapping->menu_mask))
140641ebaa5eSRicardo Ribalda return -EINVAL;
140741ebaa5eSRicardo Ribalda
14080c0d06caSMauro Carvalho Chehab ret = mutex_lock_interruptible(&chain->ctrl_mutex);
14090c0d06caSMauro Carvalho Chehab if (ret < 0)
14100c0d06caSMauro Carvalho Chehab return -ERESTARTSYS;
14110c0d06caSMauro Carvalho Chehab
14120c0d06caSMauro Carvalho Chehab ctrl = uvc_find_control(chain, query_menu->id, &mapping);
14139c016d61SRafael J. Wysocki if (ctrl == NULL || mapping->v4l2_type != V4L2_CTRL_TYPE_MENU) {
14149c016d61SRafael J. Wysocki ret = -EINVAL;
14150c0d06caSMauro Carvalho Chehab goto done;
14160c0d06caSMauro Carvalho Chehab }
14170c0d06caSMauro Carvalho Chehab
141840140edaSRicardo Ribalda if (!test_bit(query_menu->index, &mapping->menu_mask)) {
14190c0d06caSMauro Carvalho Chehab ret = -EINVAL;
14200c0d06caSMauro Carvalho Chehab goto done;
14210c0d06caSMauro Carvalho Chehab }
14220c0d06caSMauro Carvalho Chehab
14237faf8ae4SRicardo Ribalda if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK) {
1424716c3304SRicardo Ribalda int mask;
1425716c3304SRicardo Ribalda
14260c0d06caSMauro Carvalho Chehab if (!ctrl->cached) {
14270c0d06caSMauro Carvalho Chehab ret = uvc_ctrl_populate_cache(chain, ctrl);
14280c0d06caSMauro Carvalho Chehab if (ret < 0)
14290c0d06caSMauro Carvalho Chehab goto done;
14300c0d06caSMauro Carvalho Chehab }
14310c0d06caSMauro Carvalho Chehab
1432716c3304SRicardo Ribalda mask = uvc_mapping_get_menu_value(mapping, query_menu->index);
1433716c3304SRicardo Ribalda if (mask < 0) {
1434716c3304SRicardo Ribalda ret = mask;
1435716c3304SRicardo Ribalda goto done;
1436716c3304SRicardo Ribalda }
1437716c3304SRicardo Ribalda
1438716c3304SRicardo Ribalda if (!(uvc_get_ctrl_bitmap(ctrl, mapping) & mask)) {
14390c0d06caSMauro Carvalho Chehab ret = -EINVAL;
14400c0d06caSMauro Carvalho Chehab goto done;
14410c0d06caSMauro Carvalho Chehab }
14420c0d06caSMauro Carvalho Chehab }
14430c0d06caSMauro Carvalho Chehab
1444716c3304SRicardo Ribalda name = uvc_mapping_get_menu_name(mapping, query_menu->index);
1445716c3304SRicardo Ribalda if (!name) {
1446716c3304SRicardo Ribalda ret = -EINVAL;
1447716c3304SRicardo Ribalda goto done;
1448716c3304SRicardo Ribalda }
1449716c3304SRicardo Ribalda
1450716c3304SRicardo Ribalda strscpy(query_menu->name, name, sizeof(query_menu->name));
14510c0d06caSMauro Carvalho Chehab
14520c0d06caSMauro Carvalho Chehab done:
14530c0d06caSMauro Carvalho Chehab mutex_unlock(&chain->ctrl_mutex);
14540c0d06caSMauro Carvalho Chehab return ret;
14550c0d06caSMauro Carvalho Chehab }
14560c0d06caSMauro Carvalho Chehab
14570c0d06caSMauro Carvalho Chehab /* --------------------------------------------------------------------------
14580c0d06caSMauro Carvalho Chehab * Ctrl event handling
14590c0d06caSMauro Carvalho Chehab */
14600c0d06caSMauro Carvalho Chehab
uvc_ctrl_fill_event(struct uvc_video_chain * chain,struct v4l2_event * ev,struct uvc_control * ctrl,struct uvc_control_mapping * mapping,s32 value,u32 changes)14610c0d06caSMauro Carvalho Chehab static void uvc_ctrl_fill_event(struct uvc_video_chain *chain,
14620c0d06caSMauro Carvalho Chehab struct v4l2_event *ev,
14630c0d06caSMauro Carvalho Chehab struct uvc_control *ctrl,
14640c0d06caSMauro Carvalho Chehab struct uvc_control_mapping *mapping,
14650c0d06caSMauro Carvalho Chehab s32 value, u32 changes)
14660c0d06caSMauro Carvalho Chehab {
14670c0d06caSMauro Carvalho Chehab struct v4l2_queryctrl v4l2_ctrl;
14680c0d06caSMauro Carvalho Chehab
14690c0d06caSMauro Carvalho Chehab __uvc_query_v4l2_ctrl(chain, ctrl, mapping, &v4l2_ctrl);
14700c0d06caSMauro Carvalho Chehab
1471f45f3f75SHans Verkuil memset(ev, 0, sizeof(*ev));
14720c0d06caSMauro Carvalho Chehab ev->type = V4L2_EVENT_CTRL;
14730c0d06caSMauro Carvalho Chehab ev->id = v4l2_ctrl.id;
14740c0d06caSMauro Carvalho Chehab ev->u.ctrl.value = value;
14750c0d06caSMauro Carvalho Chehab ev->u.ctrl.changes = changes;
14760c0d06caSMauro Carvalho Chehab ev->u.ctrl.type = v4l2_ctrl.type;
14770c0d06caSMauro Carvalho Chehab ev->u.ctrl.flags = v4l2_ctrl.flags;
14780c0d06caSMauro Carvalho Chehab ev->u.ctrl.minimum = v4l2_ctrl.minimum;
14790c0d06caSMauro Carvalho Chehab ev->u.ctrl.maximum = v4l2_ctrl.maximum;
14800c0d06caSMauro Carvalho Chehab ev->u.ctrl.step = v4l2_ctrl.step;
14810c0d06caSMauro Carvalho Chehab ev->u.ctrl.default_value = v4l2_ctrl.default_value;
14820c0d06caSMauro Carvalho Chehab }
14830c0d06caSMauro Carvalho Chehab
1484e5225c82SGuennadi Liakhovetski /*
1485e5225c82SGuennadi Liakhovetski * Send control change events to all subscribers for the @ctrl control. By
1486e5225c82SGuennadi Liakhovetski * default the subscriber that generated the event, as identified by @handle,
1487e5225c82SGuennadi Liakhovetski * is not notified unless it has set the V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK flag.
1488e5225c82SGuennadi Liakhovetski * @handle can be NULL for asynchronous events related to auto-update controls,
1489e5225c82SGuennadi Liakhovetski * in which case all subscribers are notified.
1490e5225c82SGuennadi Liakhovetski */
uvc_ctrl_send_event(struct uvc_video_chain * chain,struct uvc_fh * handle,struct uvc_control * ctrl,struct uvc_control_mapping * mapping,s32 value,u32 changes)1491e5225c82SGuennadi Liakhovetski static void uvc_ctrl_send_event(struct uvc_video_chain *chain,
1492e5225c82SGuennadi Liakhovetski struct uvc_fh *handle, struct uvc_control *ctrl,
1493e5225c82SGuennadi Liakhovetski struct uvc_control_mapping *mapping, s32 value, u32 changes)
14940c0d06caSMauro Carvalho Chehab {
1495e5225c82SGuennadi Liakhovetski struct v4l2_fh *originator = handle ? &handle->vfh : NULL;
14960c0d06caSMauro Carvalho Chehab struct v4l2_subscribed_event *sev;
14970c0d06caSMauro Carvalho Chehab struct v4l2_event ev;
14980c0d06caSMauro Carvalho Chehab
14990c0d06caSMauro Carvalho Chehab if (list_empty(&mapping->ev_subs))
15000c0d06caSMauro Carvalho Chehab return;
15010c0d06caSMauro Carvalho Chehab
1502e5225c82SGuennadi Liakhovetski uvc_ctrl_fill_event(chain, &ev, ctrl, mapping, value, changes);
15030c0d06caSMauro Carvalho Chehab
15040c0d06caSMauro Carvalho Chehab list_for_each_entry(sev, &mapping->ev_subs, node) {
1505e5225c82SGuennadi Liakhovetski if (sev->fh != originator ||
15060c0d06caSMauro Carvalho Chehab (sev->flags & V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK) ||
1507222964eaSGuennadi Liakhovetski (changes & V4L2_EVENT_CTRL_CH_FLAGS))
15080c0d06caSMauro Carvalho Chehab v4l2_event_queue_fh(sev->fh, &ev);
15090c0d06caSMauro Carvalho Chehab }
15100c0d06caSMauro Carvalho Chehab }
15110c0d06caSMauro Carvalho Chehab
1512e5225c82SGuennadi Liakhovetski /*
1513e5225c82SGuennadi Liakhovetski * Send control change events for the slave of the @master control identified
1514e5225c82SGuennadi Liakhovetski * by the V4L2 ID @slave_id. The @handle identifies the event subscriber that
1515e5225c82SGuennadi Liakhovetski * generated the event and may be NULL for auto-update events.
1516e5225c82SGuennadi Liakhovetski */
uvc_ctrl_send_slave_event(struct uvc_video_chain * chain,struct uvc_fh * handle,struct uvc_control * master,u32 slave_id)1517e5225c82SGuennadi Liakhovetski static void uvc_ctrl_send_slave_event(struct uvc_video_chain *chain,
1518e5225c82SGuennadi Liakhovetski struct uvc_fh *handle, struct uvc_control *master, u32 slave_id)
15190c0d06caSMauro Carvalho Chehab {
15200c0d06caSMauro Carvalho Chehab struct uvc_control_mapping *mapping = NULL;
15210c0d06caSMauro Carvalho Chehab struct uvc_control *ctrl = NULL;
15220c0d06caSMauro Carvalho Chehab u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
15230c0d06caSMauro Carvalho Chehab s32 val = 0;
15240c0d06caSMauro Carvalho Chehab
15250c0d06caSMauro Carvalho Chehab __uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0);
15260c0d06caSMauro Carvalho Chehab if (ctrl == NULL)
15270c0d06caSMauro Carvalho Chehab return;
15280c0d06caSMauro Carvalho Chehab
1529e5225c82SGuennadi Liakhovetski if (__uvc_ctrl_get(chain, ctrl, mapping, &val) == 0)
15300c0d06caSMauro Carvalho Chehab changes |= V4L2_EVENT_CTRL_CH_VALUE;
15310c0d06caSMauro Carvalho Chehab
1532e5225c82SGuennadi Liakhovetski uvc_ctrl_send_event(chain, handle, ctrl, mapping, val, changes);
1533e5225c82SGuennadi Liakhovetski }
1534e5225c82SGuennadi Liakhovetski
uvc_ctrl_set_handle(struct uvc_fh * handle,struct uvc_control * ctrl,struct uvc_fh * new_handle)1535*4dbaa738SRicardo Ribalda static void uvc_ctrl_set_handle(struct uvc_fh *handle, struct uvc_control *ctrl,
1536*4dbaa738SRicardo Ribalda struct uvc_fh *new_handle)
1537*4dbaa738SRicardo Ribalda {
1538*4dbaa738SRicardo Ribalda lockdep_assert_held(&handle->chain->ctrl_mutex);
1539*4dbaa738SRicardo Ribalda
1540*4dbaa738SRicardo Ribalda if (new_handle) {
1541*4dbaa738SRicardo Ribalda if (ctrl->handle)
1542*4dbaa738SRicardo Ribalda dev_warn_ratelimited(&handle->stream->dev->udev->dev,
1543*4dbaa738SRicardo Ribalda "UVC non compliance: Setting an async control with a pending operation.");
1544*4dbaa738SRicardo Ribalda
1545*4dbaa738SRicardo Ribalda if (new_handle == ctrl->handle)
1546*4dbaa738SRicardo Ribalda return;
1547*4dbaa738SRicardo Ribalda
1548*4dbaa738SRicardo Ribalda if (ctrl->handle) {
1549*4dbaa738SRicardo Ribalda WARN_ON(!ctrl->handle->pending_async_ctrls);
1550*4dbaa738SRicardo Ribalda if (ctrl->handle->pending_async_ctrls)
1551*4dbaa738SRicardo Ribalda ctrl->handle->pending_async_ctrls--;
1552*4dbaa738SRicardo Ribalda }
1553*4dbaa738SRicardo Ribalda
1554*4dbaa738SRicardo Ribalda ctrl->handle = new_handle;
1555*4dbaa738SRicardo Ribalda handle->pending_async_ctrls++;
1556*4dbaa738SRicardo Ribalda return;
1557*4dbaa738SRicardo Ribalda }
1558*4dbaa738SRicardo Ribalda
1559*4dbaa738SRicardo Ribalda /* Cannot clear the handle for a control not owned by us.*/
1560*4dbaa738SRicardo Ribalda if (WARN_ON(ctrl->handle != handle))
1561*4dbaa738SRicardo Ribalda return;
1562*4dbaa738SRicardo Ribalda
1563*4dbaa738SRicardo Ribalda ctrl->handle = NULL;
1564*4dbaa738SRicardo Ribalda if (WARN_ON(!handle->pending_async_ctrls))
1565*4dbaa738SRicardo Ribalda return;
1566*4dbaa738SRicardo Ribalda handle->pending_async_ctrls--;
1567*4dbaa738SRicardo Ribalda }
1568*4dbaa738SRicardo Ribalda
uvc_ctrl_status_event(struct uvc_video_chain * chain,struct uvc_control * ctrl,const u8 * data)1569d9c8763eSRicardo Ribalda void uvc_ctrl_status_event(struct uvc_video_chain *chain,
1570d9c8763eSRicardo Ribalda struct uvc_control *ctrl, const u8 *data)
1571e5225c82SGuennadi Liakhovetski {
1572e5225c82SGuennadi Liakhovetski struct uvc_control_mapping *mapping;
1573e5225c82SGuennadi Liakhovetski struct uvc_fh *handle;
1574e5225c82SGuennadi Liakhovetski unsigned int i;
1575e5225c82SGuennadi Liakhovetski
1576e5225c82SGuennadi Liakhovetski mutex_lock(&chain->ctrl_mutex);
1577e5225c82SGuennadi Liakhovetski
1578e5225c82SGuennadi Liakhovetski handle = ctrl->handle;
1579*4dbaa738SRicardo Ribalda if (handle)
1580*4dbaa738SRicardo Ribalda uvc_ctrl_set_handle(handle, ctrl, NULL);
1581e5225c82SGuennadi Liakhovetski
1582e5225c82SGuennadi Liakhovetski list_for_each_entry(mapping, &ctrl->info.mappings, list) {
1583d9c8763eSRicardo Ribalda s32 value = __uvc_ctrl_get_value(mapping, data);
1584e5225c82SGuennadi Liakhovetski
1585e5225c82SGuennadi Liakhovetski /*
1586e5225c82SGuennadi Liakhovetski * handle may be NULL here if the device sends auto-update
1587e5225c82SGuennadi Liakhovetski * events without a prior related control set from userspace.
1588e5225c82SGuennadi Liakhovetski */
1589e5225c82SGuennadi Liakhovetski for (i = 0; i < ARRAY_SIZE(mapping->slave_ids); ++i) {
1590e5225c82SGuennadi Liakhovetski if (!mapping->slave_ids[i])
1591e5225c82SGuennadi Liakhovetski break;
1592e5225c82SGuennadi Liakhovetski
1593e5225c82SGuennadi Liakhovetski uvc_ctrl_send_slave_event(chain, handle, ctrl,
1594e5225c82SGuennadi Liakhovetski mapping->slave_ids[i]);
1595e5225c82SGuennadi Liakhovetski }
1596e5225c82SGuennadi Liakhovetski
1597e5225c82SGuennadi Liakhovetski uvc_ctrl_send_event(chain, handle, ctrl, mapping, value,
1598e5225c82SGuennadi Liakhovetski V4L2_EVENT_CTRL_CH_VALUE);
1599e5225c82SGuennadi Liakhovetski }
1600e5225c82SGuennadi Liakhovetski
1601e5225c82SGuennadi Liakhovetski mutex_unlock(&chain->ctrl_mutex);
1602d9c8763eSRicardo Ribalda }
1603d9c8763eSRicardo Ribalda
uvc_ctrl_status_event_work(struct work_struct * work)1604d9c8763eSRicardo Ribalda static void uvc_ctrl_status_event_work(struct work_struct *work)
1605d9c8763eSRicardo Ribalda {
1606d9c8763eSRicardo Ribalda struct uvc_device *dev = container_of(work, struct uvc_device,
1607d9c8763eSRicardo Ribalda async_ctrl.work);
1608d9c8763eSRicardo Ribalda struct uvc_ctrl_work *w = &dev->async_ctrl;
1609d9c8763eSRicardo Ribalda int ret;
1610d9c8763eSRicardo Ribalda
1611d9c8763eSRicardo Ribalda uvc_ctrl_status_event(w->chain, w->ctrl, w->data);
1612e5225c82SGuennadi Liakhovetski
1613619d9b71SRicardo Ribalda /* The barrier is needed to synchronize with uvc_status_stop(). */
1614619d9b71SRicardo Ribalda if (smp_load_acquire(&dev->flush_status))
1615619d9b71SRicardo Ribalda return;
1616619d9b71SRicardo Ribalda
1617e5225c82SGuennadi Liakhovetski /* Resubmit the URB. */
1618e5225c82SGuennadi Liakhovetski w->urb->interval = dev->int_ep->desc.bInterval;
1619e5225c82SGuennadi Liakhovetski ret = usb_submit_urb(w->urb, GFP_KERNEL);
1620e5225c82SGuennadi Liakhovetski if (ret < 0)
162169df0954SRicardo Ribalda dev_err(&dev->udev->dev,
162269df0954SRicardo Ribalda "Failed to resubmit status URB (%d).\n", ret);
1623e5225c82SGuennadi Liakhovetski }
1624e5225c82SGuennadi Liakhovetski
uvc_ctrl_status_event_async(struct urb * urb,struct uvc_video_chain * chain,struct uvc_control * ctrl,const u8 * data)1625d9c8763eSRicardo Ribalda bool uvc_ctrl_status_event_async(struct urb *urb, struct uvc_video_chain *chain,
1626e5225c82SGuennadi Liakhovetski struct uvc_control *ctrl, const u8 *data)
1627e5225c82SGuennadi Liakhovetski {
1628e5225c82SGuennadi Liakhovetski struct uvc_device *dev = chain->dev;
1629e5225c82SGuennadi Liakhovetski struct uvc_ctrl_work *w = &dev->async_ctrl;
1630e5225c82SGuennadi Liakhovetski
1631aed5248fSRicardo Ribalda if (list_empty(&ctrl->info.mappings))
1632e5225c82SGuennadi Liakhovetski return false;
1633e5225c82SGuennadi Liakhovetski
1634e5225c82SGuennadi Liakhovetski w->data = data;
1635e5225c82SGuennadi Liakhovetski w->urb = urb;
1636e5225c82SGuennadi Liakhovetski w->chain = chain;
1637e5225c82SGuennadi Liakhovetski w->ctrl = ctrl;
1638e5225c82SGuennadi Liakhovetski
1639e5225c82SGuennadi Liakhovetski schedule_work(&w->work);
1640e5225c82SGuennadi Liakhovetski
1641e5225c82SGuennadi Liakhovetski return true;
1642e5225c82SGuennadi Liakhovetski }
1643e5225c82SGuennadi Liakhovetski
uvc_ctrl_xctrls_has_control(const struct v4l2_ext_control * xctrls,unsigned int xctrls_count,u32 id)1644e5225c82SGuennadi Liakhovetski static bool uvc_ctrl_xctrls_has_control(const struct v4l2_ext_control *xctrls,
1645e5225c82SGuennadi Liakhovetski unsigned int xctrls_count, u32 id)
1646e5225c82SGuennadi Liakhovetski {
1647e5225c82SGuennadi Liakhovetski unsigned int i;
1648e5225c82SGuennadi Liakhovetski
1649e5225c82SGuennadi Liakhovetski for (i = 0; i < xctrls_count; ++i) {
1650e5225c82SGuennadi Liakhovetski if (xctrls[i].id == id)
1651e5225c82SGuennadi Liakhovetski return true;
1652e5225c82SGuennadi Liakhovetski }
1653e5225c82SGuennadi Liakhovetski
1654e5225c82SGuennadi Liakhovetski return false;
16550c0d06caSMauro Carvalho Chehab }
16560c0d06caSMauro Carvalho Chehab
uvc_ctrl_send_events(struct uvc_fh * handle,const struct v4l2_ext_control * xctrls,unsigned int xctrls_count)16570c0d06caSMauro Carvalho Chehab static void uvc_ctrl_send_events(struct uvc_fh *handle,
16580c0d06caSMauro Carvalho Chehab const struct v4l2_ext_control *xctrls, unsigned int xctrls_count)
16590c0d06caSMauro Carvalho Chehab {
16600c0d06caSMauro Carvalho Chehab struct uvc_control_mapping *mapping;
16610c0d06caSMauro Carvalho Chehab struct uvc_control *ctrl;
16620c0d06caSMauro Carvalho Chehab unsigned int i;
16630c0d06caSMauro Carvalho Chehab unsigned int j;
16640c0d06caSMauro Carvalho Chehab
16650c0d06caSMauro Carvalho Chehab for (i = 0; i < xctrls_count; ++i) {
166653107a8fSRicardo Ribalda u32 changes = V4L2_EVENT_CTRL_CH_VALUE;
16670c0d06caSMauro Carvalho Chehab
166853107a8fSRicardo Ribalda ctrl = uvc_find_control(handle->chain, xctrls[i].id, &mapping);
1669e5225c82SGuennadi Liakhovetski if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS)
1670e5225c82SGuennadi Liakhovetski /* Notification will be sent from an Interrupt event. */
1671e5225c82SGuennadi Liakhovetski continue;
1672e5225c82SGuennadi Liakhovetski
16730c0d06caSMauro Carvalho Chehab for (j = 0; j < ARRAY_SIZE(mapping->slave_ids); ++j) {
1674e5225c82SGuennadi Liakhovetski u32 slave_id = mapping->slave_ids[j];
1675e5225c82SGuennadi Liakhovetski
1676e5225c82SGuennadi Liakhovetski if (!slave_id)
16770c0d06caSMauro Carvalho Chehab break;
1678e5225c82SGuennadi Liakhovetski
1679e5225c82SGuennadi Liakhovetski /*
1680e5225c82SGuennadi Liakhovetski * We can skip sending an event for the slave if the
1681e5225c82SGuennadi Liakhovetski * slave is being modified in the same transaction.
1682e5225c82SGuennadi Liakhovetski */
1683e5225c82SGuennadi Liakhovetski if (uvc_ctrl_xctrls_has_control(xctrls, xctrls_count,
1684e5225c82SGuennadi Liakhovetski slave_id))
1685e5225c82SGuennadi Liakhovetski continue;
1686e5225c82SGuennadi Liakhovetski
1687e5225c82SGuennadi Liakhovetski uvc_ctrl_send_slave_event(handle->chain, handle, ctrl,
1688e5225c82SGuennadi Liakhovetski slave_id);
16890c0d06caSMauro Carvalho Chehab }
16900c0d06caSMauro Carvalho Chehab
16910c0d06caSMauro Carvalho Chehab /*
16920c0d06caSMauro Carvalho Chehab * If the master is being modified in the same transaction
16930c0d06caSMauro Carvalho Chehab * flags may change too.
16940c0d06caSMauro Carvalho Chehab */
1695e5225c82SGuennadi Liakhovetski if (mapping->master_id &&
1696e5225c82SGuennadi Liakhovetski uvc_ctrl_xctrls_has_control(xctrls, xctrls_count,
1697e5225c82SGuennadi Liakhovetski mapping->master_id))
16980c0d06caSMauro Carvalho Chehab changes |= V4L2_EVENT_CTRL_CH_FLAGS;
16990c0d06caSMauro Carvalho Chehab
1700e5225c82SGuennadi Liakhovetski uvc_ctrl_send_event(handle->chain, handle, ctrl, mapping,
1701e5225c82SGuennadi Liakhovetski xctrls[i].value, changes);
17020c0d06caSMauro Carvalho Chehab }
17030c0d06caSMauro Carvalho Chehab }
17040c0d06caSMauro Carvalho Chehab
uvc_ctrl_add_event(struct v4l2_subscribed_event * sev,unsigned elems)17050c0d06caSMauro Carvalho Chehab static int uvc_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems)
17060c0d06caSMauro Carvalho Chehab {
17070c0d06caSMauro Carvalho Chehab struct uvc_fh *handle = container_of(sev->fh, struct uvc_fh, vfh);
17080c0d06caSMauro Carvalho Chehab struct uvc_control_mapping *mapping;
17090c0d06caSMauro Carvalho Chehab struct uvc_control *ctrl;
17100c0d06caSMauro Carvalho Chehab int ret;
17110c0d06caSMauro Carvalho Chehab
17120c0d06caSMauro Carvalho Chehab ret = mutex_lock_interruptible(&handle->chain->ctrl_mutex);
17130c0d06caSMauro Carvalho Chehab if (ret < 0)
17140c0d06caSMauro Carvalho Chehab return -ERESTARTSYS;
17150c0d06caSMauro Carvalho Chehab
17169b31ea80SRicardo Ribalda if (__uvc_query_v4l2_class(handle->chain, sev->id, 0) >= 0) {
17179b31ea80SRicardo Ribalda ret = 0;
17189b31ea80SRicardo Ribalda goto done;
17199b31ea80SRicardo Ribalda }
17209b31ea80SRicardo Ribalda
17210c0d06caSMauro Carvalho Chehab ctrl = uvc_find_control(handle->chain, sev->id, &mapping);
17220c0d06caSMauro Carvalho Chehab if (ctrl == NULL) {
17239c016d61SRafael J. Wysocki ret = -EINVAL;
17240c0d06caSMauro Carvalho Chehab goto done;
17250c0d06caSMauro Carvalho Chehab }
17260c0d06caSMauro Carvalho Chehab
17270c0d06caSMauro Carvalho Chehab list_add_tail(&sev->node, &mapping->ev_subs);
17280c0d06caSMauro Carvalho Chehab if (sev->flags & V4L2_EVENT_SUB_FL_SEND_INITIAL) {
17290c0d06caSMauro Carvalho Chehab struct v4l2_event ev;
17300c0d06caSMauro Carvalho Chehab u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
17310c0d06caSMauro Carvalho Chehab s32 val = 0;
17320c0d06caSMauro Carvalho Chehab
17330c0d06caSMauro Carvalho Chehab if (__uvc_ctrl_get(handle->chain, ctrl, mapping, &val) == 0)
17340c0d06caSMauro Carvalho Chehab changes |= V4L2_EVENT_CTRL_CH_VALUE;
17350c0d06caSMauro Carvalho Chehab
17360c0d06caSMauro Carvalho Chehab uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, val,
17370c0d06caSMauro Carvalho Chehab changes);
1738699b9a86SLaurent Pinchart /*
1739699b9a86SLaurent Pinchart * Mark the queue as active, allowing this initial event to be
1740699b9a86SLaurent Pinchart * accepted.
1741699b9a86SLaurent Pinchart */
17420c0d06caSMauro Carvalho Chehab sev->elems = elems;
17430c0d06caSMauro Carvalho Chehab v4l2_event_queue_fh(sev->fh, &ev);
17440c0d06caSMauro Carvalho Chehab }
17450c0d06caSMauro Carvalho Chehab
17460c0d06caSMauro Carvalho Chehab done:
17470c0d06caSMauro Carvalho Chehab mutex_unlock(&handle->chain->ctrl_mutex);
17480c0d06caSMauro Carvalho Chehab return ret;
17490c0d06caSMauro Carvalho Chehab }
17500c0d06caSMauro Carvalho Chehab
uvc_ctrl_del_event(struct v4l2_subscribed_event * sev)17510c0d06caSMauro Carvalho Chehab static void uvc_ctrl_del_event(struct v4l2_subscribed_event *sev)
17520c0d06caSMauro Carvalho Chehab {
17530c0d06caSMauro Carvalho Chehab struct uvc_fh *handle = container_of(sev->fh, struct uvc_fh, vfh);
17540c0d06caSMauro Carvalho Chehab
17550c0d06caSMauro Carvalho Chehab mutex_lock(&handle->chain->ctrl_mutex);
17569b31ea80SRicardo Ribalda if (__uvc_query_v4l2_class(handle->chain, sev->id, 0) >= 0)
17579b31ea80SRicardo Ribalda goto done;
17580c0d06caSMauro Carvalho Chehab list_del(&sev->node);
17599b31ea80SRicardo Ribalda done:
17600c0d06caSMauro Carvalho Chehab mutex_unlock(&handle->chain->ctrl_mutex);
17610c0d06caSMauro Carvalho Chehab }
17620c0d06caSMauro Carvalho Chehab
17630c0d06caSMauro Carvalho Chehab const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops = {
17640c0d06caSMauro Carvalho Chehab .add = uvc_ctrl_add_event,
17650c0d06caSMauro Carvalho Chehab .del = uvc_ctrl_del_event,
17660c0d06caSMauro Carvalho Chehab .replace = v4l2_ctrl_replace,
17670c0d06caSMauro Carvalho Chehab .merge = v4l2_ctrl_merge,
17680c0d06caSMauro Carvalho Chehab };
17690c0d06caSMauro Carvalho Chehab
17700c0d06caSMauro Carvalho Chehab /* --------------------------------------------------------------------------
17710c0d06caSMauro Carvalho Chehab * Control transactions
17720c0d06caSMauro Carvalho Chehab *
17730c0d06caSMauro Carvalho Chehab * To make extended set operations as atomic as the hardware allows, controls
17740c0d06caSMauro Carvalho Chehab * are handled using begin/commit/rollback operations.
17750c0d06caSMauro Carvalho Chehab *
17760c0d06caSMauro Carvalho Chehab * At the beginning of a set request, uvc_ctrl_begin should be called to
17770c0d06caSMauro Carvalho Chehab * initialize the request. This function acquires the control lock.
17780c0d06caSMauro Carvalho Chehab *
17790c0d06caSMauro Carvalho Chehab * When setting a control, the new value is stored in the control data field
17800c0d06caSMauro Carvalho Chehab * at position UVC_CTRL_DATA_CURRENT. The control is then marked as dirty for
17810c0d06caSMauro Carvalho Chehab * later processing. If the UVC and V4L2 control sizes differ, the current
17820c0d06caSMauro Carvalho Chehab * value is loaded from the hardware before storing the new value in the data
17830c0d06caSMauro Carvalho Chehab * field.
17840c0d06caSMauro Carvalho Chehab *
17850c0d06caSMauro Carvalho Chehab * After processing all controls in the transaction, uvc_ctrl_commit or
17860c0d06caSMauro Carvalho Chehab * uvc_ctrl_rollback must be called to apply the pending changes to the
17870c0d06caSMauro Carvalho Chehab * hardware or revert them. When applying changes, all controls marked as
17880c0d06caSMauro Carvalho Chehab * dirty will be modified in the UVC device, and the dirty flag will be
17890c0d06caSMauro Carvalho Chehab * cleared. When reverting controls, the control data field
17900c0d06caSMauro Carvalho Chehab * UVC_CTRL_DATA_CURRENT is reverted to its previous value
17910c0d06caSMauro Carvalho Chehab * (UVC_CTRL_DATA_BACKUP) for all dirty controls. Both functions release the
17920c0d06caSMauro Carvalho Chehab * control lock.
17930c0d06caSMauro Carvalho Chehab */
uvc_ctrl_begin(struct uvc_video_chain * chain)17940c0d06caSMauro Carvalho Chehab int uvc_ctrl_begin(struct uvc_video_chain *chain)
17950c0d06caSMauro Carvalho Chehab {
17960c0d06caSMauro Carvalho Chehab return mutex_lock_interruptible(&chain->ctrl_mutex) ? -ERESTARTSYS : 0;
17970c0d06caSMauro Carvalho Chehab }
17980c0d06caSMauro Carvalho Chehab
uvc_ctrl_commit_entity(struct uvc_device * dev,struct uvc_fh * handle,struct uvc_entity * entity,int rollback,struct uvc_control ** err_ctrl)17990c0d06caSMauro Carvalho Chehab static int uvc_ctrl_commit_entity(struct uvc_device *dev,
180008384382SRicardo Ribalda struct uvc_fh *handle,
180108384382SRicardo Ribalda struct uvc_entity *entity,
180208384382SRicardo Ribalda int rollback,
180308384382SRicardo Ribalda struct uvc_control **err_ctrl)
18040c0d06caSMauro Carvalho Chehab {
18050c0d06caSMauro Carvalho Chehab struct uvc_control *ctrl;
18060c0d06caSMauro Carvalho Chehab unsigned int i;
18070c0d06caSMauro Carvalho Chehab int ret;
18080c0d06caSMauro Carvalho Chehab
18090c0d06caSMauro Carvalho Chehab if (entity == NULL)
18100c0d06caSMauro Carvalho Chehab return 0;
18110c0d06caSMauro Carvalho Chehab
18120c0d06caSMauro Carvalho Chehab for (i = 0; i < entity->ncontrols; ++i) {
18130c0d06caSMauro Carvalho Chehab ctrl = &entity->controls[i];
18140c0d06caSMauro Carvalho Chehab if (!ctrl->initialized)
18150c0d06caSMauro Carvalho Chehab continue;
18160c0d06caSMauro Carvalho Chehab
1817699b9a86SLaurent Pinchart /*
1818699b9a86SLaurent Pinchart * Reset the loaded flag for auto-update controls that were
18190c0d06caSMauro Carvalho Chehab * marked as loaded in uvc_ctrl_get/uvc_ctrl_set to prevent
18200c0d06caSMauro Carvalho Chehab * uvc_ctrl_get from using the cached value, and for write-only
18210c0d06caSMauro Carvalho Chehab * controls to prevent uvc_ctrl_set from setting bits not
18220c0d06caSMauro Carvalho Chehab * explicitly set by the user.
18230c0d06caSMauro Carvalho Chehab */
18240c0d06caSMauro Carvalho Chehab if (ctrl->info.flags & UVC_CTRL_FLAG_AUTO_UPDATE ||
18250c0d06caSMauro Carvalho Chehab !(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR))
18260c0d06caSMauro Carvalho Chehab ctrl->loaded = 0;
18270c0d06caSMauro Carvalho Chehab
18280c0d06caSMauro Carvalho Chehab if (!ctrl->dirty)
18290c0d06caSMauro Carvalho Chehab continue;
18300c0d06caSMauro Carvalho Chehab
18310c0d06caSMauro Carvalho Chehab if (!rollback)
18320c0d06caSMauro Carvalho Chehab ret = uvc_query_ctrl(dev, UVC_SET_CUR, ctrl->entity->id,
18330c0d06caSMauro Carvalho Chehab dev->intfnum, ctrl->info.selector,
18340c0d06caSMauro Carvalho Chehab uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
18350c0d06caSMauro Carvalho Chehab ctrl->info.size);
18360c0d06caSMauro Carvalho Chehab else
18370c0d06caSMauro Carvalho Chehab ret = 0;
18380c0d06caSMauro Carvalho Chehab
18390c0d06caSMauro Carvalho Chehab if (rollback || ret < 0)
18400c0d06caSMauro Carvalho Chehab memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
18410c0d06caSMauro Carvalho Chehab uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
18420c0d06caSMauro Carvalho Chehab ctrl->info.size);
18430c0d06caSMauro Carvalho Chehab
18440c0d06caSMauro Carvalho Chehab ctrl->dirty = 0;
18450c0d06caSMauro Carvalho Chehab
18466350d6a4SRicardo Ribalda if (ret < 0) {
18476350d6a4SRicardo Ribalda if (err_ctrl)
18486350d6a4SRicardo Ribalda *err_ctrl = ctrl;
18490c0d06caSMauro Carvalho Chehab return ret;
18500c0d06caSMauro Carvalho Chehab }
185108384382SRicardo Ribalda
185208384382SRicardo Ribalda if (!rollback && handle &&
185308384382SRicardo Ribalda ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS)
1854*4dbaa738SRicardo Ribalda uvc_ctrl_set_handle(handle, ctrl, handle);
18556350d6a4SRicardo Ribalda }
18560c0d06caSMauro Carvalho Chehab
18570c0d06caSMauro Carvalho Chehab return 0;
18580c0d06caSMauro Carvalho Chehab }
18590c0d06caSMauro Carvalho Chehab
uvc_ctrl_find_ctrl_idx(struct uvc_entity * entity,struct v4l2_ext_controls * ctrls,struct uvc_control * uvc_control)18606350d6a4SRicardo Ribalda static int uvc_ctrl_find_ctrl_idx(struct uvc_entity *entity,
18616350d6a4SRicardo Ribalda struct v4l2_ext_controls *ctrls,
18626350d6a4SRicardo Ribalda struct uvc_control *uvc_control)
18636350d6a4SRicardo Ribalda {
1864f0577b1bSRicardo Ribalda struct uvc_control_mapping *mapping = NULL;
1865414d3b49SRicardo Ribalda struct uvc_control *ctrl_found = NULL;
18666350d6a4SRicardo Ribalda unsigned int i;
18676350d6a4SRicardo Ribalda
18686350d6a4SRicardo Ribalda if (!entity)
18696350d6a4SRicardo Ribalda return ctrls->count;
18706350d6a4SRicardo Ribalda
18716350d6a4SRicardo Ribalda for (i = 0; i < ctrls->count; i++) {
18726350d6a4SRicardo Ribalda __uvc_find_control(entity, ctrls->controls[i].id, &mapping,
18736350d6a4SRicardo Ribalda &ctrl_found, 0);
18746350d6a4SRicardo Ribalda if (uvc_control == ctrl_found)
18756350d6a4SRicardo Ribalda return i;
18766350d6a4SRicardo Ribalda }
18776350d6a4SRicardo Ribalda
18786350d6a4SRicardo Ribalda return ctrls->count;
18796350d6a4SRicardo Ribalda }
18806350d6a4SRicardo Ribalda
__uvc_ctrl_commit(struct uvc_fh * handle,int rollback,struct v4l2_ext_controls * ctrls)18810c0d06caSMauro Carvalho Chehab int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback,
18826350d6a4SRicardo Ribalda struct v4l2_ext_controls *ctrls)
18830c0d06caSMauro Carvalho Chehab {
18840c0d06caSMauro Carvalho Chehab struct uvc_video_chain *chain = handle->chain;
18856350d6a4SRicardo Ribalda struct uvc_control *err_ctrl;
18860c0d06caSMauro Carvalho Chehab struct uvc_entity *entity;
18870c0d06caSMauro Carvalho Chehab int ret = 0;
18880c0d06caSMauro Carvalho Chehab
18890c0d06caSMauro Carvalho Chehab /* Find the control. */
18900c0d06caSMauro Carvalho Chehab list_for_each_entry(entity, &chain->entities, chain) {
189108384382SRicardo Ribalda ret = uvc_ctrl_commit_entity(chain->dev, handle, entity,
189208384382SRicardo Ribalda rollback, &err_ctrl);
1893bd747c0aSRicardo Ribalda if (ret < 0) {
1894bd747c0aSRicardo Ribalda if (ctrls)
1895bd747c0aSRicardo Ribalda ctrls->error_idx =
1896bd747c0aSRicardo Ribalda uvc_ctrl_find_ctrl_idx(entity, ctrls,
1897bd747c0aSRicardo Ribalda err_ctrl);
18980c0d06caSMauro Carvalho Chehab goto done;
18990c0d06caSMauro Carvalho Chehab }
1900bd747c0aSRicardo Ribalda }
19010c0d06caSMauro Carvalho Chehab
19020c0d06caSMauro Carvalho Chehab if (!rollback)
19036350d6a4SRicardo Ribalda uvc_ctrl_send_events(handle, ctrls->controls, ctrls->count);
19040c0d06caSMauro Carvalho Chehab done:
19050c0d06caSMauro Carvalho Chehab mutex_unlock(&chain->ctrl_mutex);
19060c0d06caSMauro Carvalho Chehab return ret;
19070c0d06caSMauro Carvalho Chehab }
19080c0d06caSMauro Carvalho Chehab
uvc_ctrl_get(struct uvc_video_chain * chain,struct v4l2_ext_control * xctrl)19090c0d06caSMauro Carvalho Chehab int uvc_ctrl_get(struct uvc_video_chain *chain,
19100c0d06caSMauro Carvalho Chehab struct v4l2_ext_control *xctrl)
19110c0d06caSMauro Carvalho Chehab {
19120c0d06caSMauro Carvalho Chehab struct uvc_control *ctrl;
19130c0d06caSMauro Carvalho Chehab struct uvc_control_mapping *mapping;
19140c0d06caSMauro Carvalho Chehab
19159b31ea80SRicardo Ribalda if (__uvc_query_v4l2_class(chain, xctrl->id, 0) >= 0)
19169b31ea80SRicardo Ribalda return -EACCES;
19179b31ea80SRicardo Ribalda
19180c0d06caSMauro Carvalho Chehab ctrl = uvc_find_control(chain, xctrl->id, &mapping);
19190c0d06caSMauro Carvalho Chehab if (ctrl == NULL)
19209c016d61SRafael J. Wysocki return -EINVAL;
19210c0d06caSMauro Carvalho Chehab
19220c0d06caSMauro Carvalho Chehab return __uvc_ctrl_get(chain, ctrl, mapping, &xctrl->value);
19230c0d06caSMauro Carvalho Chehab }
19240c0d06caSMauro Carvalho Chehab
uvc_ctrl_set(struct uvc_fh * handle,struct v4l2_ext_control * xctrl)1925e5225c82SGuennadi Liakhovetski int uvc_ctrl_set(struct uvc_fh *handle,
19260c0d06caSMauro Carvalho Chehab struct v4l2_ext_control *xctrl)
19270c0d06caSMauro Carvalho Chehab {
1928e5225c82SGuennadi Liakhovetski struct uvc_video_chain *chain = handle->chain;
19290c0d06caSMauro Carvalho Chehab struct uvc_control *ctrl;
19300c0d06caSMauro Carvalho Chehab struct uvc_control_mapping *mapping;
19310c0d06caSMauro Carvalho Chehab s32 value;
19320c0d06caSMauro Carvalho Chehab u32 step;
19330c0d06caSMauro Carvalho Chehab s32 min;
19340c0d06caSMauro Carvalho Chehab s32 max;
19350c0d06caSMauro Carvalho Chehab int ret;
19360c0d06caSMauro Carvalho Chehab
19379b31ea80SRicardo Ribalda if (__uvc_query_v4l2_class(chain, xctrl->id, 0) >= 0)
19389b31ea80SRicardo Ribalda return -EACCES;
19399b31ea80SRicardo Ribalda
19400c0d06caSMauro Carvalho Chehab ctrl = uvc_find_control(chain, xctrl->id, &mapping);
19419bf42300SLaurent Pinchart if (ctrl == NULL)
19429c016d61SRafael J. Wysocki return -EINVAL;
19439bf42300SLaurent Pinchart if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR))
19449bf42300SLaurent Pinchart return -EACCES;
19450c0d06caSMauro Carvalho Chehab
19460c0d06caSMauro Carvalho Chehab /* Clamp out of range values. */
19470c0d06caSMauro Carvalho Chehab switch (mapping->v4l2_type) {
19480c0d06caSMauro Carvalho Chehab case V4L2_CTRL_TYPE_INTEGER:
19490c0d06caSMauro Carvalho Chehab if (!ctrl->cached) {
19500c0d06caSMauro Carvalho Chehab ret = uvc_ctrl_populate_cache(chain, ctrl);
19510c0d06caSMauro Carvalho Chehab if (ret < 0)
19520c0d06caSMauro Carvalho Chehab return ret;
19530c0d06caSMauro Carvalho Chehab }
19540c0d06caSMauro Carvalho Chehab
19550c0d06caSMauro Carvalho Chehab min = mapping->get(mapping, UVC_GET_MIN,
19560c0d06caSMauro Carvalho Chehab uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));
19570c0d06caSMauro Carvalho Chehab max = mapping->get(mapping, UVC_GET_MAX,
19580c0d06caSMauro Carvalho Chehab uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));
19590c0d06caSMauro Carvalho Chehab step = mapping->get(mapping, UVC_GET_RES,
19600c0d06caSMauro Carvalho Chehab uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
19610c0d06caSMauro Carvalho Chehab if (step == 0)
19620c0d06caSMauro Carvalho Chehab step = 1;
19630c0d06caSMauro Carvalho Chehab
19647ae53081Szhong jiang xctrl->value = min + DIV_ROUND_CLOSEST((u32)(xctrl->value - min),
19657ae53081Szhong jiang step) * step;
196664ae9958SLaurent Pinchart if (mapping->data_type == UVC_CTRL_DATA_TYPE_SIGNED)
19670c0d06caSMauro Carvalho Chehab xctrl->value = clamp(xctrl->value, min, max);
196864ae9958SLaurent Pinchart else
196964ae9958SLaurent Pinchart xctrl->value = clamp_t(u32, xctrl->value, min, max);
19700c0d06caSMauro Carvalho Chehab value = xctrl->value;
19710c0d06caSMauro Carvalho Chehab break;
19720c0d06caSMauro Carvalho Chehab
19737faf8ae4SRicardo Ribalda case V4L2_CTRL_TYPE_BITMASK:
19747faf8ae4SRicardo Ribalda if (!ctrl->cached) {
19757faf8ae4SRicardo Ribalda ret = uvc_ctrl_populate_cache(chain, ctrl);
19767faf8ae4SRicardo Ribalda if (ret < 0)
19777faf8ae4SRicardo Ribalda return ret;
19787faf8ae4SRicardo Ribalda }
19797faf8ae4SRicardo Ribalda
19807faf8ae4SRicardo Ribalda xctrl->value &= uvc_get_ctrl_bitmap(ctrl, mapping);
19817faf8ae4SRicardo Ribalda value = xctrl->value;
19827faf8ae4SRicardo Ribalda break;
19837faf8ae4SRicardo Ribalda
19840c0d06caSMauro Carvalho Chehab case V4L2_CTRL_TYPE_BOOLEAN:
19850c0d06caSMauro Carvalho Chehab xctrl->value = clamp(xctrl->value, 0, 1);
19860c0d06caSMauro Carvalho Chehab value = xctrl->value;
19870c0d06caSMauro Carvalho Chehab break;
19880c0d06caSMauro Carvalho Chehab
19890c0d06caSMauro Carvalho Chehab case V4L2_CTRL_TYPE_MENU:
199040140edaSRicardo Ribalda if (xctrl->value < (ffs(mapping->menu_mask) - 1) ||
199140140edaSRicardo Ribalda xctrl->value > (fls(mapping->menu_mask) - 1))
19920c0d06caSMauro Carvalho Chehab return -ERANGE;
199340140edaSRicardo Ribalda
199440140edaSRicardo Ribalda if (!test_bit(xctrl->value, &mapping->menu_mask))
199540140edaSRicardo Ribalda return -EINVAL;
199640140edaSRicardo Ribalda
1997716c3304SRicardo Ribalda value = uvc_mapping_get_menu_value(mapping, xctrl->value);
19980c0d06caSMauro Carvalho Chehab
1999699b9a86SLaurent Pinchart /*
2000699b9a86SLaurent Pinchart * Valid menu indices are reported by the GET_RES request for
20010c0d06caSMauro Carvalho Chehab * UVC controls that support it.
20020c0d06caSMauro Carvalho Chehab */
20037faf8ae4SRicardo Ribalda if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK) {
20040c0d06caSMauro Carvalho Chehab if (!ctrl->cached) {
20050c0d06caSMauro Carvalho Chehab ret = uvc_ctrl_populate_cache(chain, ctrl);
20060c0d06caSMauro Carvalho Chehab if (ret < 0)
20070c0d06caSMauro Carvalho Chehab return ret;
20080c0d06caSMauro Carvalho Chehab }
20090c0d06caSMauro Carvalho Chehab
20107faf8ae4SRicardo Ribalda if (!(uvc_get_ctrl_bitmap(ctrl, mapping) & value))
2011b4186971SLaurent Pinchart return -EINVAL;
20120c0d06caSMauro Carvalho Chehab }
20130c0d06caSMauro Carvalho Chehab
20140c0d06caSMauro Carvalho Chehab break;
20150c0d06caSMauro Carvalho Chehab
20160c0d06caSMauro Carvalho Chehab default:
20170c0d06caSMauro Carvalho Chehab value = xctrl->value;
20180c0d06caSMauro Carvalho Chehab break;
20190c0d06caSMauro Carvalho Chehab }
20200c0d06caSMauro Carvalho Chehab
2021699b9a86SLaurent Pinchart /*
2022699b9a86SLaurent Pinchart * If the mapping doesn't span the whole UVC control, the current value
20230c0d06caSMauro Carvalho Chehab * needs to be loaded from the device to perform the read-modify-write
20240c0d06caSMauro Carvalho Chehab * operation.
20250c0d06caSMauro Carvalho Chehab */
20265f36851cSYunke Cao if ((ctrl->info.size * 8) != mapping->size) {
20275f36851cSYunke Cao ret = __uvc_ctrl_load_cur(chain, ctrl);
20280c0d06caSMauro Carvalho Chehab if (ret < 0)
20290c0d06caSMauro Carvalho Chehab return ret;
20300c0d06caSMauro Carvalho Chehab }
20310c0d06caSMauro Carvalho Chehab
20320c0d06caSMauro Carvalho Chehab /* Backup the current value in case we need to rollback later. */
20330c0d06caSMauro Carvalho Chehab if (!ctrl->dirty) {
20340c0d06caSMauro Carvalho Chehab memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
20350c0d06caSMauro Carvalho Chehab uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
20360c0d06caSMauro Carvalho Chehab ctrl->info.size);
20370c0d06caSMauro Carvalho Chehab }
20380c0d06caSMauro Carvalho Chehab
20390c0d06caSMauro Carvalho Chehab mapping->set(mapping, value,
20400c0d06caSMauro Carvalho Chehab uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
20410c0d06caSMauro Carvalho Chehab
20420c0d06caSMauro Carvalho Chehab ctrl->dirty = 1;
20430c0d06caSMauro Carvalho Chehab ctrl->modified = 1;
20440c0d06caSMauro Carvalho Chehab return 0;
20450c0d06caSMauro Carvalho Chehab }
20460c0d06caSMauro Carvalho Chehab
20470c0d06caSMauro Carvalho Chehab /* --------------------------------------------------------------------------
20480c0d06caSMauro Carvalho Chehab * Dynamic controls
20490c0d06caSMauro Carvalho Chehab */
20500c0d06caSMauro Carvalho Chehab
2051859086aeSEdgar Thier /*
2052859086aeSEdgar Thier * Retrieve flags for a given control
2053859086aeSEdgar Thier */
uvc_ctrl_get_flags(struct uvc_device * dev,const struct uvc_control * ctrl,struct uvc_control_info * info)2054859086aeSEdgar Thier static int uvc_ctrl_get_flags(struct uvc_device *dev,
2055859086aeSEdgar Thier const struct uvc_control *ctrl,
2056859086aeSEdgar Thier struct uvc_control_info *info)
2057859086aeSEdgar Thier {
2058859086aeSEdgar Thier u8 *data;
2059859086aeSEdgar Thier int ret;
2060859086aeSEdgar Thier
2061859086aeSEdgar Thier data = kmalloc(1, GFP_KERNEL);
2062859086aeSEdgar Thier if (data == NULL)
2063859086aeSEdgar Thier return -ENOMEM;
2064859086aeSEdgar Thier
206565900c58SRicardo Ribalda if (ctrl->entity->get_info)
206665900c58SRicardo Ribalda ret = ctrl->entity->get_info(dev, ctrl->entity,
206765900c58SRicardo Ribalda ctrl->info.selector, data);
206865900c58SRicardo Ribalda else
206965900c58SRicardo Ribalda ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id,
207065900c58SRicardo Ribalda dev->intfnum, info->selector, data, 1);
2071afb83918SDaniel Schaefer
2072afb83918SDaniel Schaefer if (!ret) {
2073afb83918SDaniel Schaefer info->flags &= ~(UVC_CTRL_FLAG_GET_CUR |
2074afb83918SDaniel Schaefer UVC_CTRL_FLAG_SET_CUR |
2075afb83918SDaniel Schaefer UVC_CTRL_FLAG_AUTO_UPDATE |
2076afb83918SDaniel Schaefer UVC_CTRL_FLAG_ASYNCHRONOUS);
2077afb83918SDaniel Schaefer
20780dc68cabSKieran Bingham info->flags |= (data[0] & UVC_CONTROL_CAP_GET ?
2079859086aeSEdgar Thier UVC_CTRL_FLAG_GET_CUR : 0)
2080859086aeSEdgar Thier | (data[0] & UVC_CONTROL_CAP_SET ?
2081859086aeSEdgar Thier UVC_CTRL_FLAG_SET_CUR : 0)
2082859086aeSEdgar Thier | (data[0] & UVC_CONTROL_CAP_AUTOUPDATE ?
2083e5225c82SGuennadi Liakhovetski UVC_CTRL_FLAG_AUTO_UPDATE : 0)
2084e5225c82SGuennadi Liakhovetski | (data[0] & UVC_CONTROL_CAP_ASYNCHRONOUS ?
2085e5225c82SGuennadi Liakhovetski UVC_CTRL_FLAG_ASYNCHRONOUS : 0);
2086afb83918SDaniel Schaefer }
2087859086aeSEdgar Thier
2088859086aeSEdgar Thier kfree(data);
2089859086aeSEdgar Thier return ret;
2090859086aeSEdgar Thier }
2091859086aeSEdgar Thier
uvc_ctrl_fixup_xu_info(struct uvc_device * dev,const struct uvc_control * ctrl,struct uvc_control_info * info)20920c0d06caSMauro Carvalho Chehab static void uvc_ctrl_fixup_xu_info(struct uvc_device *dev,
20930c0d06caSMauro Carvalho Chehab const struct uvc_control *ctrl, struct uvc_control_info *info)
20940c0d06caSMauro Carvalho Chehab {
20950c0d06caSMauro Carvalho Chehab struct uvc_ctrl_fixup {
20960c0d06caSMauro Carvalho Chehab struct usb_device_id id;
20970c0d06caSMauro Carvalho Chehab u8 entity;
20980c0d06caSMauro Carvalho Chehab u8 selector;
20990c0d06caSMauro Carvalho Chehab u8 flags;
21000c0d06caSMauro Carvalho Chehab };
21010c0d06caSMauro Carvalho Chehab
21020c0d06caSMauro Carvalho Chehab static const struct uvc_ctrl_fixup fixups[] = {
21030c0d06caSMauro Carvalho Chehab { { USB_DEVICE(0x046d, 0x08c2) }, 9, 1,
21040c0d06caSMauro Carvalho Chehab UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX |
21050c0d06caSMauro Carvalho Chehab UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_SET_CUR |
21060c0d06caSMauro Carvalho Chehab UVC_CTRL_FLAG_AUTO_UPDATE },
21070c0d06caSMauro Carvalho Chehab { { USB_DEVICE(0x046d, 0x08cc) }, 9, 1,
21080c0d06caSMauro Carvalho Chehab UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX |
21090c0d06caSMauro Carvalho Chehab UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_SET_CUR |
21100c0d06caSMauro Carvalho Chehab UVC_CTRL_FLAG_AUTO_UPDATE },
21110c0d06caSMauro Carvalho Chehab { { USB_DEVICE(0x046d, 0x0994) }, 9, 1,
21120c0d06caSMauro Carvalho Chehab UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX |
21130c0d06caSMauro Carvalho Chehab UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_SET_CUR |
21140c0d06caSMauro Carvalho Chehab UVC_CTRL_FLAG_AUTO_UPDATE },
21150c0d06caSMauro Carvalho Chehab };
21160c0d06caSMauro Carvalho Chehab
21170c0d06caSMauro Carvalho Chehab unsigned int i;
21180c0d06caSMauro Carvalho Chehab
21190c0d06caSMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(fixups); ++i) {
21200c0d06caSMauro Carvalho Chehab if (!usb_match_one_id(dev->intf, &fixups[i].id))
21210c0d06caSMauro Carvalho Chehab continue;
21220c0d06caSMauro Carvalho Chehab
21230c0d06caSMauro Carvalho Chehab if (fixups[i].entity == ctrl->entity->id &&
21240c0d06caSMauro Carvalho Chehab fixups[i].selector == info->selector) {
21250c0d06caSMauro Carvalho Chehab info->flags = fixups[i].flags;
21260c0d06caSMauro Carvalho Chehab return;
21270c0d06caSMauro Carvalho Chehab }
21280c0d06caSMauro Carvalho Chehab }
21290c0d06caSMauro Carvalho Chehab }
21300c0d06caSMauro Carvalho Chehab
21310c0d06caSMauro Carvalho Chehab /*
21320c0d06caSMauro Carvalho Chehab * Query control information (size and flags) for XU controls.
21330c0d06caSMauro Carvalho Chehab */
uvc_ctrl_fill_xu_info(struct uvc_device * dev,const struct uvc_control * ctrl,struct uvc_control_info * info)21340c0d06caSMauro Carvalho Chehab static int uvc_ctrl_fill_xu_info(struct uvc_device *dev,
21350c0d06caSMauro Carvalho Chehab const struct uvc_control *ctrl, struct uvc_control_info *info)
21360c0d06caSMauro Carvalho Chehab {
21370c0d06caSMauro Carvalho Chehab u8 *data;
21380c0d06caSMauro Carvalho Chehab int ret;
21390c0d06caSMauro Carvalho Chehab
21400c0d06caSMauro Carvalho Chehab data = kmalloc(2, GFP_KERNEL);
21410c0d06caSMauro Carvalho Chehab if (data == NULL)
21420c0d06caSMauro Carvalho Chehab return -ENOMEM;
21430c0d06caSMauro Carvalho Chehab
2144351509c6SRicardo Ribalda memcpy(info->entity, ctrl->entity->guid, sizeof(info->entity));
21450c0d06caSMauro Carvalho Chehab info->index = ctrl->index;
21460c0d06caSMauro Carvalho Chehab info->selector = ctrl->index + 1;
21470c0d06caSMauro Carvalho Chehab
21480c0d06caSMauro Carvalho Chehab /* Query and verify the control length (GET_LEN) */
21490c0d06caSMauro Carvalho Chehab ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id, dev->intfnum,
21500c0d06caSMauro Carvalho Chehab info->selector, data, 2);
21510c0d06caSMauro Carvalho Chehab if (ret < 0) {
21529e56380aSJoe Perches uvc_dbg(dev, CONTROL,
21539e56380aSJoe Perches "GET_LEN failed on control %pUl/%u (%d)\n",
21540c0d06caSMauro Carvalho Chehab info->entity, info->selector, ret);
21550c0d06caSMauro Carvalho Chehab goto done;
21560c0d06caSMauro Carvalho Chehab }
21570c0d06caSMauro Carvalho Chehab
21580c0d06caSMauro Carvalho Chehab info->size = le16_to_cpup((__le16 *)data);
21590c0d06caSMauro Carvalho Chehab
21600dc68cabSKieran Bingham info->flags = UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX
21610dc68cabSKieran Bingham | UVC_CTRL_FLAG_GET_RES | UVC_CTRL_FLAG_GET_DEF;
21620dc68cabSKieran Bingham
2163859086aeSEdgar Thier ret = uvc_ctrl_get_flags(dev, ctrl, info);
21640c0d06caSMauro Carvalho Chehab if (ret < 0) {
21659e56380aSJoe Perches uvc_dbg(dev, CONTROL,
21669e56380aSJoe Perches "Failed to get flags for control %pUl/%u (%d)\n",
21670c0d06caSMauro Carvalho Chehab info->entity, info->selector, ret);
21680c0d06caSMauro Carvalho Chehab goto done;
21690c0d06caSMauro Carvalho Chehab }
21700c0d06caSMauro Carvalho Chehab
21710c0d06caSMauro Carvalho Chehab uvc_ctrl_fixup_xu_info(dev, ctrl, info);
21720c0d06caSMauro Carvalho Chehab
21739e56380aSJoe Perches uvc_dbg(dev, CONTROL,
21749e56380aSJoe Perches "XU control %pUl/%u queried: len %u, flags { get %u set %u auto %u }\n",
21750c0d06caSMauro Carvalho Chehab info->entity, info->selector, info->size,
21760c0d06caSMauro Carvalho Chehab (info->flags & UVC_CTRL_FLAG_GET_CUR) ? 1 : 0,
21770c0d06caSMauro Carvalho Chehab (info->flags & UVC_CTRL_FLAG_SET_CUR) ? 1 : 0,
21780c0d06caSMauro Carvalho Chehab (info->flags & UVC_CTRL_FLAG_AUTO_UPDATE) ? 1 : 0);
21790c0d06caSMauro Carvalho Chehab
21800c0d06caSMauro Carvalho Chehab done:
21810c0d06caSMauro Carvalho Chehab kfree(data);
21820c0d06caSMauro Carvalho Chehab return ret;
21830c0d06caSMauro Carvalho Chehab }
21840c0d06caSMauro Carvalho Chehab
21850c0d06caSMauro Carvalho Chehab static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl,
21860c0d06caSMauro Carvalho Chehab const struct uvc_control_info *info);
21870c0d06caSMauro Carvalho Chehab
uvc_ctrl_init_xu_ctrl(struct uvc_device * dev,struct uvc_control * ctrl)21880c0d06caSMauro Carvalho Chehab static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev,
21890c0d06caSMauro Carvalho Chehab struct uvc_control *ctrl)
21900c0d06caSMauro Carvalho Chehab {
21910c0d06caSMauro Carvalho Chehab struct uvc_control_info info;
21920c0d06caSMauro Carvalho Chehab int ret;
21930c0d06caSMauro Carvalho Chehab
21940c0d06caSMauro Carvalho Chehab if (ctrl->initialized)
21950c0d06caSMauro Carvalho Chehab return 0;
21960c0d06caSMauro Carvalho Chehab
21970c0d06caSMauro Carvalho Chehab ret = uvc_ctrl_fill_xu_info(dev, ctrl, &info);
21980c0d06caSMauro Carvalho Chehab if (ret < 0)
21990c0d06caSMauro Carvalho Chehab return ret;
22000c0d06caSMauro Carvalho Chehab
22010c0d06caSMauro Carvalho Chehab ret = uvc_ctrl_add_info(dev, ctrl, &info);
22020c0d06caSMauro Carvalho Chehab if (ret < 0)
22039e56380aSJoe Perches uvc_dbg(dev, CONTROL,
2204ed4c5fa4SRicardo Ribalda "Failed to initialize control %pUl/%u on device %s entity %u\n",
2205ed4c5fa4SRicardo Ribalda info.entity, info.selector, dev->udev->devpath,
2206ed4c5fa4SRicardo Ribalda ctrl->entity->id);
22070c0d06caSMauro Carvalho Chehab
22080c0d06caSMauro Carvalho Chehab return ret;
22090c0d06caSMauro Carvalho Chehab }
22100c0d06caSMauro Carvalho Chehab
uvc_xu_ctrl_query(struct uvc_video_chain * chain,struct uvc_xu_control_query * xqry)22110c0d06caSMauro Carvalho Chehab int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
22120c0d06caSMauro Carvalho Chehab struct uvc_xu_control_query *xqry)
22130c0d06caSMauro Carvalho Chehab {
2214bd747c0aSRicardo Ribalda struct uvc_entity *entity, *iter;
22150c0d06caSMauro Carvalho Chehab struct uvc_control *ctrl;
2216f875bcc3SDaniel W. S. Almeida unsigned int i;
2217f875bcc3SDaniel W. S. Almeida bool found;
22182c6b222cSLaurent Pinchart u32 reqflags;
22192c6b222cSLaurent Pinchart u16 size;
22202c6b222cSLaurent Pinchart u8 *data = NULL;
22210c0d06caSMauro Carvalho Chehab int ret;
22220c0d06caSMauro Carvalho Chehab
22230c0d06caSMauro Carvalho Chehab /* Find the extension unit. */
2224bd747c0aSRicardo Ribalda entity = NULL;
2225bd747c0aSRicardo Ribalda list_for_each_entry(iter, &chain->entities, chain) {
2226bd747c0aSRicardo Ribalda if (UVC_ENTITY_TYPE(iter) == UVC_VC_EXTENSION_UNIT &&
2227bd747c0aSRicardo Ribalda iter->id == xqry->unit) {
2228bd747c0aSRicardo Ribalda entity = iter;
22290c0d06caSMauro Carvalho Chehab break;
22300c0d06caSMauro Carvalho Chehab }
2231f875bcc3SDaniel W. S. Almeida }
22320c0d06caSMauro Carvalho Chehab
2233bd747c0aSRicardo Ribalda if (!entity) {
22349e56380aSJoe Perches uvc_dbg(chain->dev, CONTROL, "Extension unit %u not found\n",
22359e56380aSJoe Perches xqry->unit);
22360c0d06caSMauro Carvalho Chehab return -ENOENT;
22370c0d06caSMauro Carvalho Chehab }
22380c0d06caSMauro Carvalho Chehab
22390c0d06caSMauro Carvalho Chehab /* Find the control and perform delayed initialization if needed. */
2240f875bcc3SDaniel W. S. Almeida found = false;
22410c0d06caSMauro Carvalho Chehab for (i = 0; i < entity->ncontrols; ++i) {
22420c0d06caSMauro Carvalho Chehab ctrl = &entity->controls[i];
22430c0d06caSMauro Carvalho Chehab if (ctrl->index == xqry->selector - 1) {
2244f875bcc3SDaniel W. S. Almeida found = true;
22450c0d06caSMauro Carvalho Chehab break;
22460c0d06caSMauro Carvalho Chehab }
22470c0d06caSMauro Carvalho Chehab }
22480c0d06caSMauro Carvalho Chehab
22490c0d06caSMauro Carvalho Chehab if (!found) {
22509e56380aSJoe Perches uvc_dbg(chain->dev, CONTROL, "Control %pUl/%u not found\n",
22519e56380aSJoe Perches entity->guid, xqry->selector);
22520c0d06caSMauro Carvalho Chehab return -ENOENT;
22530c0d06caSMauro Carvalho Chehab }
22540c0d06caSMauro Carvalho Chehab
22550c0d06caSMauro Carvalho Chehab if (mutex_lock_interruptible(&chain->ctrl_mutex))
22560c0d06caSMauro Carvalho Chehab return -ERESTARTSYS;
22570c0d06caSMauro Carvalho Chehab
22580c0d06caSMauro Carvalho Chehab ret = uvc_ctrl_init_xu_ctrl(chain->dev, ctrl);
22590c0d06caSMauro Carvalho Chehab if (ret < 0) {
22600c0d06caSMauro Carvalho Chehab ret = -ENOENT;
22610c0d06caSMauro Carvalho Chehab goto done;
22620c0d06caSMauro Carvalho Chehab }
22630c0d06caSMauro Carvalho Chehab
22640c0d06caSMauro Carvalho Chehab /* Validate the required buffer size and flags for the request */
22650c0d06caSMauro Carvalho Chehab reqflags = 0;
22660c0d06caSMauro Carvalho Chehab size = ctrl->info.size;
22670c0d06caSMauro Carvalho Chehab
22680c0d06caSMauro Carvalho Chehab switch (xqry->query) {
22690c0d06caSMauro Carvalho Chehab case UVC_GET_CUR:
22700c0d06caSMauro Carvalho Chehab reqflags = UVC_CTRL_FLAG_GET_CUR;
22710c0d06caSMauro Carvalho Chehab break;
22720c0d06caSMauro Carvalho Chehab case UVC_GET_MIN:
22730c0d06caSMauro Carvalho Chehab reqflags = UVC_CTRL_FLAG_GET_MIN;
22740c0d06caSMauro Carvalho Chehab break;
22750c0d06caSMauro Carvalho Chehab case UVC_GET_MAX:
22760c0d06caSMauro Carvalho Chehab reqflags = UVC_CTRL_FLAG_GET_MAX;
22770c0d06caSMauro Carvalho Chehab break;
22780c0d06caSMauro Carvalho Chehab case UVC_GET_DEF:
22790c0d06caSMauro Carvalho Chehab reqflags = UVC_CTRL_FLAG_GET_DEF;
22800c0d06caSMauro Carvalho Chehab break;
22810c0d06caSMauro Carvalho Chehab case UVC_GET_RES:
22820c0d06caSMauro Carvalho Chehab reqflags = UVC_CTRL_FLAG_GET_RES;
22830c0d06caSMauro Carvalho Chehab break;
22840c0d06caSMauro Carvalho Chehab case UVC_SET_CUR:
22850c0d06caSMauro Carvalho Chehab reqflags = UVC_CTRL_FLAG_SET_CUR;
22860c0d06caSMauro Carvalho Chehab break;
22870c0d06caSMauro Carvalho Chehab case UVC_GET_LEN:
22880c0d06caSMauro Carvalho Chehab size = 2;
22890c0d06caSMauro Carvalho Chehab break;
22900c0d06caSMauro Carvalho Chehab case UVC_GET_INFO:
22910c0d06caSMauro Carvalho Chehab size = 1;
22920c0d06caSMauro Carvalho Chehab break;
22930c0d06caSMauro Carvalho Chehab default:
22940c0d06caSMauro Carvalho Chehab ret = -EINVAL;
22950c0d06caSMauro Carvalho Chehab goto done;
22960c0d06caSMauro Carvalho Chehab }
22970c0d06caSMauro Carvalho Chehab
22980c0d06caSMauro Carvalho Chehab if (size != xqry->size) {
22990c0d06caSMauro Carvalho Chehab ret = -ENOBUFS;
23000c0d06caSMauro Carvalho Chehab goto done;
23010c0d06caSMauro Carvalho Chehab }
23020c0d06caSMauro Carvalho Chehab
23030c0d06caSMauro Carvalho Chehab if (reqflags && !(ctrl->info.flags & reqflags)) {
23040c0d06caSMauro Carvalho Chehab ret = -EBADRQC;
23050c0d06caSMauro Carvalho Chehab goto done;
23060c0d06caSMauro Carvalho Chehab }
23070c0d06caSMauro Carvalho Chehab
23080c0d06caSMauro Carvalho Chehab data = kmalloc(size, GFP_KERNEL);
23090c0d06caSMauro Carvalho Chehab if (data == NULL) {
23100c0d06caSMauro Carvalho Chehab ret = -ENOMEM;
23110c0d06caSMauro Carvalho Chehab goto done;
23120c0d06caSMauro Carvalho Chehab }
23130c0d06caSMauro Carvalho Chehab
23140c0d06caSMauro Carvalho Chehab if (xqry->query == UVC_SET_CUR &&
23150c0d06caSMauro Carvalho Chehab copy_from_user(data, xqry->data, size)) {
23160c0d06caSMauro Carvalho Chehab ret = -EFAULT;
23170c0d06caSMauro Carvalho Chehab goto done;
23180c0d06caSMauro Carvalho Chehab }
23190c0d06caSMauro Carvalho Chehab
23200c0d06caSMauro Carvalho Chehab ret = uvc_query_ctrl(chain->dev, xqry->query, xqry->unit,
23210c0d06caSMauro Carvalho Chehab chain->dev->intfnum, xqry->selector, data, size);
23220c0d06caSMauro Carvalho Chehab if (ret < 0)
23230c0d06caSMauro Carvalho Chehab goto done;
23240c0d06caSMauro Carvalho Chehab
23250c0d06caSMauro Carvalho Chehab if (xqry->query != UVC_SET_CUR &&
23260c0d06caSMauro Carvalho Chehab copy_to_user(xqry->data, data, size))
23270c0d06caSMauro Carvalho Chehab ret = -EFAULT;
23280c0d06caSMauro Carvalho Chehab done:
23290c0d06caSMauro Carvalho Chehab kfree(data);
23300c0d06caSMauro Carvalho Chehab mutex_unlock(&chain->ctrl_mutex);
23310c0d06caSMauro Carvalho Chehab return ret;
23320c0d06caSMauro Carvalho Chehab }
23330c0d06caSMauro Carvalho Chehab
23340c0d06caSMauro Carvalho Chehab /* --------------------------------------------------------------------------
23350c0d06caSMauro Carvalho Chehab * Suspend/resume
23360c0d06caSMauro Carvalho Chehab */
23370c0d06caSMauro Carvalho Chehab
23380c0d06caSMauro Carvalho Chehab /*
23390c0d06caSMauro Carvalho Chehab * Restore control values after resume, skipping controls that haven't been
23400c0d06caSMauro Carvalho Chehab * changed.
23410c0d06caSMauro Carvalho Chehab *
23420c0d06caSMauro Carvalho Chehab * TODO
23430c0d06caSMauro Carvalho Chehab * - Don't restore modified controls that are back to their default value.
23440c0d06caSMauro Carvalho Chehab * - Handle restore order (Auto-Exposure Mode should be restored before
23450c0d06caSMauro Carvalho Chehab * Exposure Time).
23460c0d06caSMauro Carvalho Chehab */
uvc_ctrl_restore_values(struct uvc_device * dev)234717e1319fSWilliam Manley int uvc_ctrl_restore_values(struct uvc_device *dev)
23480c0d06caSMauro Carvalho Chehab {
23490c0d06caSMauro Carvalho Chehab struct uvc_control *ctrl;
23500c0d06caSMauro Carvalho Chehab struct uvc_entity *entity;
23510c0d06caSMauro Carvalho Chehab unsigned int i;
23520c0d06caSMauro Carvalho Chehab int ret;
23530c0d06caSMauro Carvalho Chehab
23540c0d06caSMauro Carvalho Chehab /* Walk the entities list and restore controls when possible. */
23550c0d06caSMauro Carvalho Chehab list_for_each_entry(entity, &dev->entities, list) {
23560c0d06caSMauro Carvalho Chehab
23570c0d06caSMauro Carvalho Chehab for (i = 0; i < entity->ncontrols; ++i) {
23580c0d06caSMauro Carvalho Chehab ctrl = &entity->controls[i];
23590c0d06caSMauro Carvalho Chehab
23600c0d06caSMauro Carvalho Chehab if (!ctrl->initialized || !ctrl->modified ||
23610c0d06caSMauro Carvalho Chehab (ctrl->info.flags & UVC_CTRL_FLAG_RESTORE) == 0)
23620c0d06caSMauro Carvalho Chehab continue;
236315486e09SHans Verkuil dev_dbg(&dev->udev->dev,
236469df0954SRicardo Ribalda "restoring control %pUl/%u/%u\n",
23650c0d06caSMauro Carvalho Chehab ctrl->info.entity, ctrl->info.index,
23660c0d06caSMauro Carvalho Chehab ctrl->info.selector);
23670c0d06caSMauro Carvalho Chehab ctrl->dirty = 1;
23680c0d06caSMauro Carvalho Chehab }
23690c0d06caSMauro Carvalho Chehab
237008384382SRicardo Ribalda ret = uvc_ctrl_commit_entity(dev, NULL, entity, 0, NULL);
23710c0d06caSMauro Carvalho Chehab if (ret < 0)
23720c0d06caSMauro Carvalho Chehab return ret;
23730c0d06caSMauro Carvalho Chehab }
23740c0d06caSMauro Carvalho Chehab
23750c0d06caSMauro Carvalho Chehab return 0;
23760c0d06caSMauro Carvalho Chehab }
23770c0d06caSMauro Carvalho Chehab
23780c0d06caSMauro Carvalho Chehab /* --------------------------------------------------------------------------
23790c0d06caSMauro Carvalho Chehab * Control and mapping handling
23800c0d06caSMauro Carvalho Chehab */
23810c0d06caSMauro Carvalho Chehab
23820c0d06caSMauro Carvalho Chehab /*
23830c0d06caSMauro Carvalho Chehab * Add control information to a given control.
23840c0d06caSMauro Carvalho Chehab */
uvc_ctrl_add_info(struct uvc_device * dev,struct uvc_control * ctrl,const struct uvc_control_info * info)23850c0d06caSMauro Carvalho Chehab static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl,
23860c0d06caSMauro Carvalho Chehab const struct uvc_control_info *info)
23870c0d06caSMauro Carvalho Chehab {
23888c0d44e2SEzequiel Garcia ctrl->info = *info;
23890c0d06caSMauro Carvalho Chehab INIT_LIST_HEAD(&ctrl->info.mappings);
23900c0d06caSMauro Carvalho Chehab
23910c0d06caSMauro Carvalho Chehab /* Allocate an array to save control values (cur, def, max, etc.) */
23920c0d06caSMauro Carvalho Chehab ctrl->uvc_data = kzalloc(ctrl->info.size * UVC_CTRL_DATA_LAST + 1,
23930c0d06caSMauro Carvalho Chehab GFP_KERNEL);
2394bed888deSHans de Goede if (!ctrl->uvc_data)
2395bed888deSHans de Goede return -ENOMEM;
23960c0d06caSMauro Carvalho Chehab
23970c0d06caSMauro Carvalho Chehab ctrl->initialized = 1;
23980c0d06caSMauro Carvalho Chehab
23999e56380aSJoe Perches uvc_dbg(dev, CONTROL, "Added control %pUl/%u to device %s entity %u\n",
2400ed4c5fa4SRicardo Ribalda ctrl->info.entity, ctrl->info.selector, dev->udev->devpath,
2401ed4c5fa4SRicardo Ribalda ctrl->entity->id);
24020c0d06caSMauro Carvalho Chehab
2403bed888deSHans de Goede return 0;
24040c0d06caSMauro Carvalho Chehab }
24050c0d06caSMauro Carvalho Chehab
24060c0d06caSMauro Carvalho Chehab /*
24070c0d06caSMauro Carvalho Chehab * Add a control mapping to a given control.
24080c0d06caSMauro Carvalho Chehab */
__uvc_ctrl_add_mapping(struct uvc_video_chain * chain,struct uvc_control * ctrl,const struct uvc_control_mapping * mapping)2409866c6bddSRicardo Ribalda static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
24100c0d06caSMauro Carvalho Chehab struct uvc_control *ctrl, const struct uvc_control_mapping *mapping)
24110c0d06caSMauro Carvalho Chehab {
24120c0d06caSMauro Carvalho Chehab struct uvc_control_mapping *map;
24130c0d06caSMauro Carvalho Chehab unsigned int size;
24149b31ea80SRicardo Ribalda unsigned int i;
24150c0d06caSMauro Carvalho Chehab
2416699b9a86SLaurent Pinchart /*
2417252d50daSRicardo Ribalda * Most mappings come from static kernel data, and need to be duplicated.
24180c0d06caSMauro Carvalho Chehab * Mappings that come from userspace will be unnecessarily duplicated,
24190c0d06caSMauro Carvalho Chehab * this could be optimized.
24200c0d06caSMauro Carvalho Chehab */
24210c0d06caSMauro Carvalho Chehab map = kmemdup(mapping, sizeof(*mapping), GFP_KERNEL);
2422252d50daSRicardo Ribalda if (!map)
24230c0d06caSMauro Carvalho Chehab return -ENOMEM;
24240c0d06caSMauro Carvalho Chehab
2425252d50daSRicardo Ribalda map->name = NULL;
2426716c3304SRicardo Ribalda map->menu_names = NULL;
2427716c3304SRicardo Ribalda map->menu_mapping = NULL;
2428252d50daSRicardo Ribalda
242918a9b21fSRicardo Ribalda /* For UVCIOC_CTRL_MAP custom control */
243018a9b21fSRicardo Ribalda if (mapping->name) {
243118a9b21fSRicardo Ribalda map->name = kstrdup(mapping->name, GFP_KERNEL);
2432252d50daSRicardo Ribalda if (!map->name)
2433252d50daSRicardo Ribalda goto err_nomem;
243418a9b21fSRicardo Ribalda }
243518a9b21fSRicardo Ribalda
24360c0d06caSMauro Carvalho Chehab INIT_LIST_HEAD(&map->ev_subs);
24370c0d06caSMauro Carvalho Chehab
2438716c3304SRicardo Ribalda if (mapping->menu_mapping && mapping->menu_mask) {
2439716c3304SRicardo Ribalda size = sizeof(mapping->menu_mapping[0])
2440716c3304SRicardo Ribalda * fls(mapping->menu_mask);
2441716c3304SRicardo Ribalda map->menu_mapping = kmemdup(mapping->menu_mapping, size,
2442716c3304SRicardo Ribalda GFP_KERNEL);
2443716c3304SRicardo Ribalda if (!map->menu_mapping)
2444252d50daSRicardo Ribalda goto err_nomem;
2445716c3304SRicardo Ribalda }
2446716c3304SRicardo Ribalda if (mapping->menu_names && mapping->menu_mask) {
2447716c3304SRicardo Ribalda size = sizeof(mapping->menu_names[0])
2448716c3304SRicardo Ribalda * fls(mapping->menu_mask);
2449716c3304SRicardo Ribalda map->menu_names = kmemdup(mapping->menu_names, size,
2450716c3304SRicardo Ribalda GFP_KERNEL);
2451716c3304SRicardo Ribalda if (!map->menu_names)
2452716c3304SRicardo Ribalda goto err_nomem;
2453716c3304SRicardo Ribalda }
24540c0d06caSMauro Carvalho Chehab
24550c0d06caSMauro Carvalho Chehab if (map->get == NULL)
24560c0d06caSMauro Carvalho Chehab map->get = uvc_get_le_value;
24570c0d06caSMauro Carvalho Chehab if (map->set == NULL)
24580c0d06caSMauro Carvalho Chehab map->set = uvc_set_le_value;
24590c0d06caSMauro Carvalho Chehab
24609b31ea80SRicardo Ribalda for (i = 0; i < ARRAY_SIZE(uvc_control_classes); i++) {
24619b31ea80SRicardo Ribalda if (V4L2_CTRL_ID2WHICH(uvc_control_classes[i]) ==
24629b31ea80SRicardo Ribalda V4L2_CTRL_ID2WHICH(map->id)) {
24639b31ea80SRicardo Ribalda chain->ctrl_class_bitmap |= BIT(i);
24649b31ea80SRicardo Ribalda break;
24659b31ea80SRicardo Ribalda }
24669b31ea80SRicardo Ribalda }
24679b31ea80SRicardo Ribalda
24680c0d06caSMauro Carvalho Chehab list_add_tail(&map->list, &ctrl->info.mappings);
2469866c6bddSRicardo Ribalda uvc_dbg(chain->dev, CONTROL, "Adding mapping '%s' to control %pUl/%u\n",
247070fa906dSRicardo Ribalda uvc_map_get_name(map), ctrl->info.entity,
247170fa906dSRicardo Ribalda ctrl->info.selector);
24720c0d06caSMauro Carvalho Chehab
24730c0d06caSMauro Carvalho Chehab return 0;
2474252d50daSRicardo Ribalda
2475252d50daSRicardo Ribalda err_nomem:
2476716c3304SRicardo Ribalda kfree(map->menu_names);
2477716c3304SRicardo Ribalda kfree(map->menu_mapping);
2478252d50daSRicardo Ribalda kfree(map->name);
2479252d50daSRicardo Ribalda kfree(map);
2480252d50daSRicardo Ribalda return -ENOMEM;
24810c0d06caSMauro Carvalho Chehab }
24820c0d06caSMauro Carvalho Chehab
uvc_ctrl_add_mapping(struct uvc_video_chain * chain,const struct uvc_control_mapping * mapping)24830c0d06caSMauro Carvalho Chehab int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
24840c0d06caSMauro Carvalho Chehab const struct uvc_control_mapping *mapping)
24850c0d06caSMauro Carvalho Chehab {
24860c0d06caSMauro Carvalho Chehab struct uvc_device *dev = chain->dev;
24870c0d06caSMauro Carvalho Chehab struct uvc_control_mapping *map;
24880c0d06caSMauro Carvalho Chehab struct uvc_entity *entity;
24890c0d06caSMauro Carvalho Chehab struct uvc_control *ctrl;
24900c0d06caSMauro Carvalho Chehab int found = 0;
24910c0d06caSMauro Carvalho Chehab int ret;
24920c0d06caSMauro Carvalho Chehab
24930c0d06caSMauro Carvalho Chehab if (mapping->id & ~V4L2_CTRL_ID_MASK) {
24949e56380aSJoe Perches uvc_dbg(dev, CONTROL,
24959e56380aSJoe Perches "Can't add mapping '%s', control id 0x%08x is invalid\n",
249670fa906dSRicardo Ribalda uvc_map_get_name(mapping), mapping->id);
24970c0d06caSMauro Carvalho Chehab return -EINVAL;
24980c0d06caSMauro Carvalho Chehab }
24990c0d06caSMauro Carvalho Chehab
25000c0d06caSMauro Carvalho Chehab /* Search for the matching (GUID/CS) control on the current chain */
25010c0d06caSMauro Carvalho Chehab list_for_each_entry(entity, &chain->entities, chain) {
25020c0d06caSMauro Carvalho Chehab unsigned int i;
25030c0d06caSMauro Carvalho Chehab
25040c0d06caSMauro Carvalho Chehab if (UVC_ENTITY_TYPE(entity) != UVC_VC_EXTENSION_UNIT ||
25050c0d06caSMauro Carvalho Chehab !uvc_entity_match_guid(entity, mapping->entity))
25060c0d06caSMauro Carvalho Chehab continue;
25070c0d06caSMauro Carvalho Chehab
25080c0d06caSMauro Carvalho Chehab for (i = 0; i < entity->ncontrols; ++i) {
25090c0d06caSMauro Carvalho Chehab ctrl = &entity->controls[i];
25100c0d06caSMauro Carvalho Chehab if (ctrl->index == mapping->selector - 1) {
25110c0d06caSMauro Carvalho Chehab found = 1;
25120c0d06caSMauro Carvalho Chehab break;
25130c0d06caSMauro Carvalho Chehab }
25140c0d06caSMauro Carvalho Chehab }
25150c0d06caSMauro Carvalho Chehab
25160c0d06caSMauro Carvalho Chehab if (found)
25170c0d06caSMauro Carvalho Chehab break;
25180c0d06caSMauro Carvalho Chehab }
25190c0d06caSMauro Carvalho Chehab if (!found)
25200c0d06caSMauro Carvalho Chehab return -ENOENT;
25210c0d06caSMauro Carvalho Chehab
25220c0d06caSMauro Carvalho Chehab if (mutex_lock_interruptible(&chain->ctrl_mutex))
25230c0d06caSMauro Carvalho Chehab return -ERESTARTSYS;
25240c0d06caSMauro Carvalho Chehab
25250c0d06caSMauro Carvalho Chehab /* Perform delayed initialization of XU controls */
25260c0d06caSMauro Carvalho Chehab ret = uvc_ctrl_init_xu_ctrl(dev, ctrl);
25270c0d06caSMauro Carvalho Chehab if (ret < 0) {
25280c0d06caSMauro Carvalho Chehab ret = -ENOENT;
25290c0d06caSMauro Carvalho Chehab goto done;
25300c0d06caSMauro Carvalho Chehab }
25310c0d06caSMauro Carvalho Chehab
25327e09f7d5SGuenter Roeck /* Validate the user-provided bit-size and offset */
25337e09f7d5SGuenter Roeck if (mapping->size > 32 ||
25347e09f7d5SGuenter Roeck mapping->offset + mapping->size > ctrl->info.size * 8) {
25357e09f7d5SGuenter Roeck ret = -EINVAL;
25367e09f7d5SGuenter Roeck goto done;
25377e09f7d5SGuenter Roeck }
25387e09f7d5SGuenter Roeck
25390c0d06caSMauro Carvalho Chehab list_for_each_entry(map, &ctrl->info.mappings, list) {
25400c0d06caSMauro Carvalho Chehab if (mapping->id == map->id) {
25419e56380aSJoe Perches uvc_dbg(dev, CONTROL,
25429e56380aSJoe Perches "Can't add mapping '%s', control id 0x%08x already exists\n",
254370fa906dSRicardo Ribalda uvc_map_get_name(mapping), mapping->id);
25440c0d06caSMauro Carvalho Chehab ret = -EEXIST;
25450c0d06caSMauro Carvalho Chehab goto done;
25460c0d06caSMauro Carvalho Chehab }
25470c0d06caSMauro Carvalho Chehab }
25480c0d06caSMauro Carvalho Chehab
25490c0d06caSMauro Carvalho Chehab /* Prevent excess memory consumption */
25500c0d06caSMauro Carvalho Chehab if (atomic_inc_return(&dev->nmappings) > UVC_MAX_CONTROL_MAPPINGS) {
25510c0d06caSMauro Carvalho Chehab atomic_dec(&dev->nmappings);
25529e56380aSJoe Perches uvc_dbg(dev, CONTROL,
25539e56380aSJoe Perches "Can't add mapping '%s', maximum mappings count (%u) exceeded\n",
255470fa906dSRicardo Ribalda uvc_map_get_name(mapping), UVC_MAX_CONTROL_MAPPINGS);
25550c0d06caSMauro Carvalho Chehab ret = -ENOMEM;
25560c0d06caSMauro Carvalho Chehab goto done;
25570c0d06caSMauro Carvalho Chehab }
25580c0d06caSMauro Carvalho Chehab
2559866c6bddSRicardo Ribalda ret = __uvc_ctrl_add_mapping(chain, ctrl, mapping);
25600c0d06caSMauro Carvalho Chehab if (ret < 0)
25610c0d06caSMauro Carvalho Chehab atomic_dec(&dev->nmappings);
25620c0d06caSMauro Carvalho Chehab
25630c0d06caSMauro Carvalho Chehab done:
25640c0d06caSMauro Carvalho Chehab mutex_unlock(&chain->ctrl_mutex);
25650c0d06caSMauro Carvalho Chehab return ret;
25660c0d06caSMauro Carvalho Chehab }
25670c0d06caSMauro Carvalho Chehab
25680c0d06caSMauro Carvalho Chehab /*
25690c0d06caSMauro Carvalho Chehab * Prune an entity of its bogus controls using a blacklist. Bogus controls
25700c0d06caSMauro Carvalho Chehab * are currently the ones that crash the camera or unconditionally return an
25710c0d06caSMauro Carvalho Chehab * error when queried.
25720c0d06caSMauro Carvalho Chehab */
uvc_ctrl_prune_entity(struct uvc_device * dev,struct uvc_entity * entity)25730c0d06caSMauro Carvalho Chehab static void uvc_ctrl_prune_entity(struct uvc_device *dev,
25740c0d06caSMauro Carvalho Chehab struct uvc_entity *entity)
25750c0d06caSMauro Carvalho Chehab {
25760c0d06caSMauro Carvalho Chehab struct uvc_ctrl_blacklist {
25770c0d06caSMauro Carvalho Chehab struct usb_device_id id;
25780c0d06caSMauro Carvalho Chehab u8 index;
25790c0d06caSMauro Carvalho Chehab };
25800c0d06caSMauro Carvalho Chehab
25810c0d06caSMauro Carvalho Chehab static const struct uvc_ctrl_blacklist processing_blacklist[] = {
25820c0d06caSMauro Carvalho Chehab { { USB_DEVICE(0x13d3, 0x509b) }, 9 }, /* Gain */
25830c0d06caSMauro Carvalho Chehab { { USB_DEVICE(0x1c4f, 0x3000) }, 6 }, /* WB Temperature */
25840c0d06caSMauro Carvalho Chehab { { USB_DEVICE(0x5986, 0x0241) }, 2 }, /* Hue */
25850c0d06caSMauro Carvalho Chehab };
25860c0d06caSMauro Carvalho Chehab static const struct uvc_ctrl_blacklist camera_blacklist[] = {
25870c0d06caSMauro Carvalho Chehab { { USB_DEVICE(0x06f8, 0x3005) }, 9 }, /* Zoom, Absolute */
25880c0d06caSMauro Carvalho Chehab };
25890c0d06caSMauro Carvalho Chehab
25900c0d06caSMauro Carvalho Chehab const struct uvc_ctrl_blacklist *blacklist;
25910c0d06caSMauro Carvalho Chehab unsigned int size;
25920c0d06caSMauro Carvalho Chehab unsigned int count;
25930c0d06caSMauro Carvalho Chehab unsigned int i;
25940c0d06caSMauro Carvalho Chehab u8 *controls;
25950c0d06caSMauro Carvalho Chehab
25960c0d06caSMauro Carvalho Chehab switch (UVC_ENTITY_TYPE(entity)) {
25970c0d06caSMauro Carvalho Chehab case UVC_VC_PROCESSING_UNIT:
25980c0d06caSMauro Carvalho Chehab blacklist = processing_blacklist;
25990c0d06caSMauro Carvalho Chehab count = ARRAY_SIZE(processing_blacklist);
26000c0d06caSMauro Carvalho Chehab controls = entity->processing.bmControls;
26010c0d06caSMauro Carvalho Chehab size = entity->processing.bControlSize;
26020c0d06caSMauro Carvalho Chehab break;
26030c0d06caSMauro Carvalho Chehab
26040c0d06caSMauro Carvalho Chehab case UVC_ITT_CAMERA:
26050c0d06caSMauro Carvalho Chehab blacklist = camera_blacklist;
26060c0d06caSMauro Carvalho Chehab count = ARRAY_SIZE(camera_blacklist);
26070c0d06caSMauro Carvalho Chehab controls = entity->camera.bmControls;
26080c0d06caSMauro Carvalho Chehab size = entity->camera.bControlSize;
26090c0d06caSMauro Carvalho Chehab break;
26100c0d06caSMauro Carvalho Chehab
26110c0d06caSMauro Carvalho Chehab default:
26120c0d06caSMauro Carvalho Chehab return;
26130c0d06caSMauro Carvalho Chehab }
26140c0d06caSMauro Carvalho Chehab
26150c0d06caSMauro Carvalho Chehab for (i = 0; i < count; ++i) {
26160c0d06caSMauro Carvalho Chehab if (!usb_match_one_id(dev->intf, &blacklist[i].id))
26170c0d06caSMauro Carvalho Chehab continue;
26180c0d06caSMauro Carvalho Chehab
26190c0d06caSMauro Carvalho Chehab if (blacklist[i].index >= 8 * size ||
26200c0d06caSMauro Carvalho Chehab !uvc_test_bit(controls, blacklist[i].index))
26210c0d06caSMauro Carvalho Chehab continue;
26220c0d06caSMauro Carvalho Chehab
26239e56380aSJoe Perches uvc_dbg(dev, CONTROL,
26249e56380aSJoe Perches "%u/%u control is black listed, removing it\n",
2625ed4c5fa4SRicardo Ribalda entity->id, blacklist[i].index);
26260c0d06caSMauro Carvalho Chehab
26270c0d06caSMauro Carvalho Chehab uvc_clear_bit(controls, blacklist[i].index);
26280c0d06caSMauro Carvalho Chehab }
26290c0d06caSMauro Carvalho Chehab }
26300c0d06caSMauro Carvalho Chehab
26310c0d06caSMauro Carvalho Chehab /*
26320c0d06caSMauro Carvalho Chehab * Add control information and hardcoded stock control mappings to the given
26330c0d06caSMauro Carvalho Chehab * device.
26340c0d06caSMauro Carvalho Chehab */
uvc_ctrl_init_ctrl(struct uvc_video_chain * chain,struct uvc_control * ctrl)2635866c6bddSRicardo Ribalda static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain,
2636866c6bddSRicardo Ribalda struct uvc_control *ctrl)
26370c0d06caSMauro Carvalho Chehab {
263896a160b0SRicardo Ribalda const struct uvc_control_mapping **mappings;
2639c58874dfSLaurent Pinchart unsigned int i;
26400c0d06caSMauro Carvalho Chehab
2641699b9a86SLaurent Pinchart /*
2642699b9a86SLaurent Pinchart * XU controls initialization requires querying the device for control
26430c0d06caSMauro Carvalho Chehab * information. As some buggy UVC devices will crash when queried
26440c0d06caSMauro Carvalho Chehab * repeatedly in a tight loop, delay XU controls initialization until
26450c0d06caSMauro Carvalho Chehab * first use.
26460c0d06caSMauro Carvalho Chehab */
26470c0d06caSMauro Carvalho Chehab if (UVC_ENTITY_TYPE(ctrl->entity) == UVC_VC_EXTENSION_UNIT)
26480c0d06caSMauro Carvalho Chehab return;
26490c0d06caSMauro Carvalho Chehab
2650c58874dfSLaurent Pinchart for (i = 0; i < ARRAY_SIZE(uvc_ctrls); ++i) {
2651c58874dfSLaurent Pinchart const struct uvc_control_info *info = &uvc_ctrls[i];
2652c58874dfSLaurent Pinchart
26530c0d06caSMauro Carvalho Chehab if (uvc_entity_match_guid(ctrl->entity, info->entity) &&
26540c0d06caSMauro Carvalho Chehab ctrl->index == info->index) {
2655866c6bddSRicardo Ribalda uvc_ctrl_add_info(chain->dev, ctrl, info);
265693df48d3SHans de Goede /*
265793df48d3SHans de Goede * Retrieve control flags from the device. Ignore errors
265893df48d3SHans de Goede * and work with default flag values from the uvc_ctrl
265993df48d3SHans de Goede * array when the device doesn't properly implement
266093df48d3SHans de Goede * GET_INFO on standard controls.
266193df48d3SHans de Goede */
2662866c6bddSRicardo Ribalda uvc_ctrl_get_flags(chain->dev, ctrl, &ctrl->info);
26630c0d06caSMauro Carvalho Chehab break;
26640c0d06caSMauro Carvalho Chehab }
26650c0d06caSMauro Carvalho Chehab }
26660c0d06caSMauro Carvalho Chehab
26670c0d06caSMauro Carvalho Chehab if (!ctrl->initialized)
26680c0d06caSMauro Carvalho Chehab return;
26690c0d06caSMauro Carvalho Chehab
267086f7ef77SRicardo Ribalda /*
267186f7ef77SRicardo Ribalda * First check if the device provides a custom mapping for this control,
267286f7ef77SRicardo Ribalda * used to override standard mappings for non-conformant devices. Don't
267386f7ef77SRicardo Ribalda * process standard mappings if a custom mapping is found. This
267486f7ef77SRicardo Ribalda * mechanism doesn't support combining standard and custom mappings for
267586f7ef77SRicardo Ribalda * a single control.
267686f7ef77SRicardo Ribalda */
267786f7ef77SRicardo Ribalda if (chain->dev->info->mappings) {
267886f7ef77SRicardo Ribalda bool custom = false;
267986f7ef77SRicardo Ribalda
2680c58874dfSLaurent Pinchart for (i = 0; chain->dev->info->mappings[i]; ++i) {
2681c58874dfSLaurent Pinchart const struct uvc_control_mapping *mapping =
2682c58874dfSLaurent Pinchart chain->dev->info->mappings[i];
2683c58874dfSLaurent Pinchart
268486f7ef77SRicardo Ribalda if (uvc_entity_match_guid(ctrl->entity, mapping->entity) &&
268586f7ef77SRicardo Ribalda ctrl->info.selector == mapping->selector) {
268686f7ef77SRicardo Ribalda __uvc_ctrl_add_mapping(chain, ctrl, mapping);
268786f7ef77SRicardo Ribalda custom = true;
268886f7ef77SRicardo Ribalda }
268986f7ef77SRicardo Ribalda }
269086f7ef77SRicardo Ribalda
269186f7ef77SRicardo Ribalda if (custom)
269286f7ef77SRicardo Ribalda return;
269386f7ef77SRicardo Ribalda }
269486f7ef77SRicardo Ribalda
269586f7ef77SRicardo Ribalda /* Process common mappings next. */
2696c58874dfSLaurent Pinchart for (i = 0; i < ARRAY_SIZE(uvc_ctrl_mappings); ++i) {
2697c58874dfSLaurent Pinchart const struct uvc_control_mapping *mapping = &uvc_ctrl_mappings[i];
269821b1b679SLaurent Pinchart
269971087116SRicardo Ribalda if (uvc_entity_match_guid(ctrl->entity, mapping->entity) &&
270071087116SRicardo Ribalda ctrl->info.selector == mapping->selector)
270171087116SRicardo Ribalda __uvc_ctrl_add_mapping(chain, ctrl, mapping);
270271087116SRicardo Ribalda }
270371087116SRicardo Ribalda
270486f7ef77SRicardo Ribalda /* Finally process version-specific mappings. */
270596a160b0SRicardo Ribalda mappings = chain->dev->uvc_version < 0x0150
270696a160b0SRicardo Ribalda ? uvc_ctrl_mappings_uvc11 : uvc_ctrl_mappings_uvc15;
270771087116SRicardo Ribalda
270896a160b0SRicardo Ribalda for (i = 0; mappings[i]; ++i) {
270996a160b0SRicardo Ribalda const struct uvc_control_mapping *mapping = mappings[i];
2710c58874dfSLaurent Pinchart
27110c0d06caSMauro Carvalho Chehab if (uvc_entity_match_guid(ctrl->entity, mapping->entity) &&
27120c0d06caSMauro Carvalho Chehab ctrl->info.selector == mapping->selector)
2713866c6bddSRicardo Ribalda __uvc_ctrl_add_mapping(chain, ctrl, mapping);
27140c0d06caSMauro Carvalho Chehab }
27150c0d06caSMauro Carvalho Chehab }
27160c0d06caSMauro Carvalho Chehab
27170c0d06caSMauro Carvalho Chehab /*
27180c0d06caSMauro Carvalho Chehab * Initialize device controls.
27190c0d06caSMauro Carvalho Chehab */
uvc_ctrl_init_chain(struct uvc_video_chain * chain)2720866c6bddSRicardo Ribalda static int uvc_ctrl_init_chain(struct uvc_video_chain *chain)
27210c0d06caSMauro Carvalho Chehab {
27220c0d06caSMauro Carvalho Chehab struct uvc_entity *entity;
27230c0d06caSMauro Carvalho Chehab unsigned int i;
27240c0d06caSMauro Carvalho Chehab
27250c0d06caSMauro Carvalho Chehab /* Walk the entities list and instantiate controls */
2726866c6bddSRicardo Ribalda list_for_each_entry(entity, &chain->entities, chain) {
27270c0d06caSMauro Carvalho Chehab struct uvc_control *ctrl;
27280c0d06caSMauro Carvalho Chehab unsigned int bControlSize = 0, ncontrols;
27292c6b222cSLaurent Pinchart u8 *bmControls = NULL;
27300c0d06caSMauro Carvalho Chehab
27310c0d06caSMauro Carvalho Chehab if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT) {
27320c0d06caSMauro Carvalho Chehab bmControls = entity->extension.bmControls;
27330c0d06caSMauro Carvalho Chehab bControlSize = entity->extension.bControlSize;
27340c0d06caSMauro Carvalho Chehab } else if (UVC_ENTITY_TYPE(entity) == UVC_VC_PROCESSING_UNIT) {
27350c0d06caSMauro Carvalho Chehab bmControls = entity->processing.bmControls;
27360c0d06caSMauro Carvalho Chehab bControlSize = entity->processing.bControlSize;
27370c0d06caSMauro Carvalho Chehab } else if (UVC_ENTITY_TYPE(entity) == UVC_ITT_CAMERA) {
27380c0d06caSMauro Carvalho Chehab bmControls = entity->camera.bmControls;
27390c0d06caSMauro Carvalho Chehab bControlSize = entity->camera.bControlSize;
27402886477fSRicardo Ribalda } else if (UVC_ENTITY_TYPE(entity) == UVC_EXT_GPIO_UNIT) {
27412886477fSRicardo Ribalda bmControls = entity->gpio.bmControls;
27422886477fSRicardo Ribalda bControlSize = entity->gpio.bControlSize;
27430c0d06caSMauro Carvalho Chehab }
27440c0d06caSMauro Carvalho Chehab
27450c0d06caSMauro Carvalho Chehab /* Remove bogus/blacklisted controls */
2746866c6bddSRicardo Ribalda uvc_ctrl_prune_entity(chain->dev, entity);
27470c0d06caSMauro Carvalho Chehab
27480c0d06caSMauro Carvalho Chehab /* Count supported controls and allocate the controls array */
27490c0d06caSMauro Carvalho Chehab ncontrols = memweight(bmControls, bControlSize);
27500c0d06caSMauro Carvalho Chehab if (ncontrols == 0)
27510c0d06caSMauro Carvalho Chehab continue;
27520c0d06caSMauro Carvalho Chehab
27530c0d06caSMauro Carvalho Chehab entity->controls = kcalloc(ncontrols, sizeof(*ctrl),
27540c0d06caSMauro Carvalho Chehab GFP_KERNEL);
27550c0d06caSMauro Carvalho Chehab if (entity->controls == NULL)
27560c0d06caSMauro Carvalho Chehab return -ENOMEM;
27570c0d06caSMauro Carvalho Chehab entity->ncontrols = ncontrols;
27580c0d06caSMauro Carvalho Chehab
27590c0d06caSMauro Carvalho Chehab /* Initialize all supported controls */
27600c0d06caSMauro Carvalho Chehab ctrl = entity->controls;
27610c0d06caSMauro Carvalho Chehab for (i = 0; i < bControlSize * 8; ++i) {
27620c0d06caSMauro Carvalho Chehab if (uvc_test_bit(bmControls, i) == 0)
27630c0d06caSMauro Carvalho Chehab continue;
27640c0d06caSMauro Carvalho Chehab
27650c0d06caSMauro Carvalho Chehab ctrl->entity = entity;
27660c0d06caSMauro Carvalho Chehab ctrl->index = i;
27670c0d06caSMauro Carvalho Chehab
2768866c6bddSRicardo Ribalda uvc_ctrl_init_ctrl(chain, ctrl);
27690c0d06caSMauro Carvalho Chehab ctrl++;
27700c0d06caSMauro Carvalho Chehab }
27710c0d06caSMauro Carvalho Chehab }
27720c0d06caSMauro Carvalho Chehab
27730c0d06caSMauro Carvalho Chehab return 0;
27740c0d06caSMauro Carvalho Chehab }
27750c0d06caSMauro Carvalho Chehab
uvc_ctrl_init_device(struct uvc_device * dev)2776866c6bddSRicardo Ribalda int uvc_ctrl_init_device(struct uvc_device *dev)
2777866c6bddSRicardo Ribalda {
2778866c6bddSRicardo Ribalda struct uvc_video_chain *chain;
2779866c6bddSRicardo Ribalda int ret;
2780866c6bddSRicardo Ribalda
2781866c6bddSRicardo Ribalda INIT_WORK(&dev->async_ctrl.work, uvc_ctrl_status_event_work);
2782866c6bddSRicardo Ribalda
2783866c6bddSRicardo Ribalda list_for_each_entry(chain, &dev->chains, list) {
2784866c6bddSRicardo Ribalda ret = uvc_ctrl_init_chain(chain);
2785866c6bddSRicardo Ribalda if (ret)
2786866c6bddSRicardo Ribalda return ret;
2787866c6bddSRicardo Ribalda }
2788866c6bddSRicardo Ribalda
2789866c6bddSRicardo Ribalda return 0;
2790866c6bddSRicardo Ribalda }
2791866c6bddSRicardo Ribalda
uvc_ctrl_cleanup_fh(struct uvc_fh * handle)2792*4dbaa738SRicardo Ribalda void uvc_ctrl_cleanup_fh(struct uvc_fh *handle)
2793*4dbaa738SRicardo Ribalda {
2794*4dbaa738SRicardo Ribalda struct uvc_entity *entity;
2795*4dbaa738SRicardo Ribalda
2796*4dbaa738SRicardo Ribalda guard(mutex)(&handle->chain->ctrl_mutex);
2797*4dbaa738SRicardo Ribalda
2798*4dbaa738SRicardo Ribalda if (!handle->pending_async_ctrls)
2799*4dbaa738SRicardo Ribalda return;
2800*4dbaa738SRicardo Ribalda
2801*4dbaa738SRicardo Ribalda list_for_each_entry(entity, &handle->chain->dev->entities, list) {
2802*4dbaa738SRicardo Ribalda for (unsigned int i = 0; i < entity->ncontrols; ++i) {
2803*4dbaa738SRicardo Ribalda if (entity->controls[i].handle != handle)
2804*4dbaa738SRicardo Ribalda continue;
2805*4dbaa738SRicardo Ribalda uvc_ctrl_set_handle(handle, &entity->controls[i], NULL);
2806*4dbaa738SRicardo Ribalda }
2807*4dbaa738SRicardo Ribalda }
2808*4dbaa738SRicardo Ribalda
2809*4dbaa738SRicardo Ribalda WARN_ON(handle->pending_async_ctrls);
2810*4dbaa738SRicardo Ribalda }
2811*4dbaa738SRicardo Ribalda
28120c0d06caSMauro Carvalho Chehab /*
28130c0d06caSMauro Carvalho Chehab * Cleanup device controls.
28140c0d06caSMauro Carvalho Chehab */
uvc_ctrl_cleanup_mappings(struct uvc_device * dev,struct uvc_control * ctrl)28150c0d06caSMauro Carvalho Chehab static void uvc_ctrl_cleanup_mappings(struct uvc_device *dev,
28160c0d06caSMauro Carvalho Chehab struct uvc_control *ctrl)
28170c0d06caSMauro Carvalho Chehab {
28180c0d06caSMauro Carvalho Chehab struct uvc_control_mapping *mapping, *nm;
28190c0d06caSMauro Carvalho Chehab
28200c0d06caSMauro Carvalho Chehab list_for_each_entry_safe(mapping, nm, &ctrl->info.mappings, list) {
28210c0d06caSMauro Carvalho Chehab list_del(&mapping->list);
2822716c3304SRicardo Ribalda kfree(mapping->menu_names);
2823716c3304SRicardo Ribalda kfree(mapping->menu_mapping);
282470fa906dSRicardo Ribalda kfree(mapping->name);
28250c0d06caSMauro Carvalho Chehab kfree(mapping);
28260c0d06caSMauro Carvalho Chehab }
28270c0d06caSMauro Carvalho Chehab }
28280c0d06caSMauro Carvalho Chehab
uvc_ctrl_cleanup_device(struct uvc_device * dev)28290c0d06caSMauro Carvalho Chehab void uvc_ctrl_cleanup_device(struct uvc_device *dev)
28300c0d06caSMauro Carvalho Chehab {
28310c0d06caSMauro Carvalho Chehab struct uvc_entity *entity;
28320c0d06caSMauro Carvalho Chehab unsigned int i;
28330c0d06caSMauro Carvalho Chehab
283411a087f4SOliver Neukum /* Can be uninitialized if we are aborting on probe error. */
283511a087f4SOliver Neukum if (dev->async_ctrl.work.func)
2836e5225c82SGuennadi Liakhovetski cancel_work_sync(&dev->async_ctrl.work);
2837e5225c82SGuennadi Liakhovetski
28380c0d06caSMauro Carvalho Chehab /* Free controls and control mappings for all entities. */
28390c0d06caSMauro Carvalho Chehab list_for_each_entry(entity, &dev->entities, list) {
28400c0d06caSMauro Carvalho Chehab for (i = 0; i < entity->ncontrols; ++i) {
28410c0d06caSMauro Carvalho Chehab struct uvc_control *ctrl = &entity->controls[i];
28420c0d06caSMauro Carvalho Chehab
28430c0d06caSMauro Carvalho Chehab if (!ctrl->initialized)
28440c0d06caSMauro Carvalho Chehab continue;
28450c0d06caSMauro Carvalho Chehab
28460c0d06caSMauro Carvalho Chehab uvc_ctrl_cleanup_mappings(dev, ctrl);
28470c0d06caSMauro Carvalho Chehab kfree(ctrl->uvc_data);
28480c0d06caSMauro Carvalho Chehab }
28490c0d06caSMauro Carvalho Chehab
28500c0d06caSMauro Carvalho Chehab kfree(entity->controls);
28510c0d06caSMauro Carvalho Chehab }
28520c0d06caSMauro Carvalho Chehab }
2853