xref: /openbmc/linux/drivers/media/usb/uvc/uvc_ctrl.c (revision d37cf9b63113f13d742713881ce691fc615d8b3b)
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