diff options
Diffstat (limited to 'drivers/media/v4l2-core/v4l2-ctrls.c')
| -rw-r--r-- | drivers/media/v4l2-core/v4l2-ctrls.c | 971 | 
1 files changed, 686 insertions, 285 deletions
diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 55c683254102..f030d6a9e044 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -462,6 +462,13 @@ const char * const *v4l2_ctrl_get_menu(u32 id)  		"RGB full range (0-255)",  		NULL,  	}; +	static const char * const detect_md_mode[] = { +		"Disabled", +		"Global", +		"Threshold Grid", +		"Region Grid", +		NULL, +	};  	switch (id) { @@ -553,6 +560,8 @@ const char * const *v4l2_ctrl_get_menu(u32 id)  	case V4L2_CID_DV_TX_RGB_RANGE:  	case V4L2_CID_DV_RX_RGB_RANGE:  		return dv_rgb_range; +	case V4L2_CID_DETECT_MD_MODE: +		return detect_md_mode;  	default:  		return NULL; @@ -592,7 +601,7 @@ const char *v4l2_ctrl_get_name(u32 id)  {  	switch (id) {  	/* USER controls */ -	/* Keep the order of the 'case's the same as in videodev2.h! */ +	/* Keep the order of the 'case's the same as in v4l2-controls.h! */  	case V4L2_CID_USER_CLASS:		return "User Controls";  	case V4L2_CID_BRIGHTNESS:		return "Brightness";  	case V4L2_CID_CONTRAST:			return "Contrast"; @@ -754,7 +763,7 @@ const char *v4l2_ctrl_get_name(u32 id)  	case V4L2_CID_MPEG_VIDEO_VPX_PROFILE:			return "VPX Profile";  	/* CAMERA controls */ -	/* Keep the order of the 'case's the same as in videodev2.h! */ +	/* Keep the order of the 'case's the same as in v4l2-controls.h! */  	case V4L2_CID_CAMERA_CLASS:		return "Camera Controls";  	case V4L2_CID_EXPOSURE_AUTO:		return "Auto Exposure";  	case V4L2_CID_EXPOSURE_ABSOLUTE:	return "Exposure Time, Absolute"; @@ -788,14 +797,23 @@ const char *v4l2_ctrl_get_name(u32 id)  	case V4L2_CID_AUTO_FOCUS_STATUS:	return "Auto Focus, Status";  	case V4L2_CID_AUTO_FOCUS_RANGE:		return "Auto Focus, Range"; -	/* FM Radio Modulator control */ -	/* Keep the order of the 'case's the same as in videodev2.h! */ +	/* FM Radio Modulator controls */ +	/* Keep the order of the 'case's the same as in v4l2-controls.h! */  	case V4L2_CID_FM_TX_CLASS:		return "FM Radio Modulator Controls";  	case V4L2_CID_RDS_TX_DEVIATION:		return "RDS Signal Deviation";  	case V4L2_CID_RDS_TX_PI:		return "RDS Program ID";  	case V4L2_CID_RDS_TX_PTY:		return "RDS Program Type";  	case V4L2_CID_RDS_TX_PS_NAME:		return "RDS PS Name";  	case V4L2_CID_RDS_TX_RADIO_TEXT:	return "RDS Radio Text"; +	case V4L2_CID_RDS_TX_MONO_STEREO:	return "RDS Stereo"; +	case V4L2_CID_RDS_TX_ARTIFICIAL_HEAD:	return "RDS Artificial Head"; +	case V4L2_CID_RDS_TX_COMPRESSED:	return "RDS Compressed"; +	case V4L2_CID_RDS_TX_DYNAMIC_PTY:	return "RDS Dynamic PTY"; +	case V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT: return "RDS Traffic Announcement"; +	case V4L2_CID_RDS_TX_TRAFFIC_PROGRAM:	return "RDS Traffic Program"; +	case V4L2_CID_RDS_TX_MUSIC_SPEECH:	return "RDS Music"; +	case V4L2_CID_RDS_TX_ALT_FREQS_ENABLE:	return "RDS Enable Alt Frequencies"; +	case V4L2_CID_RDS_TX_ALT_FREQS:		return "RDS Alternate Frequencies";  	case V4L2_CID_AUDIO_LIMITER_ENABLED:	return "Audio Limiter Feature Enabled";  	case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: return "Audio Limiter Release Time";  	case V4L2_CID_AUDIO_LIMITER_DEVIATION:	return "Audio Limiter Deviation"; @@ -812,6 +830,7 @@ const char *v4l2_ctrl_get_name(u32 id)  	case V4L2_CID_TUNE_ANTENNA_CAPACITOR:	return "Tune Antenna Capacitor";  	/* Flash controls */ +	/* Keep the order of the 'case's the same as in v4l2-controls.h! */  	case V4L2_CID_FLASH_CLASS:		return "Flash Controls";  	case V4L2_CID_FLASH_LED_MODE:		return "LED Mode";  	case V4L2_CID_FLASH_STROBE_SOURCE:	return "Strobe Source"; @@ -827,7 +846,7 @@ const char *v4l2_ctrl_get_name(u32 id)  	case V4L2_CID_FLASH_READY:		return "Ready to Strobe";  	/* JPEG encoder controls */ -	/* Keep the order of the 'case's the same as in videodev2.h! */ +	/* Keep the order of the 'case's the same as in v4l2-controls.h! */  	case V4L2_CID_JPEG_CLASS:		return "JPEG Compression Controls";  	case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:	return "Chroma Subsampling";  	case V4L2_CID_JPEG_RESTART_INTERVAL:	return "Restart Interval"; @@ -835,18 +854,21 @@ const char *v4l2_ctrl_get_name(u32 id)  	case V4L2_CID_JPEG_ACTIVE_MARKER:	return "Active Markers";  	/* Image source controls */ +	/* Keep the order of the 'case's the same as in v4l2-controls.h! */  	case V4L2_CID_IMAGE_SOURCE_CLASS:	return "Image Source Controls";  	case V4L2_CID_VBLANK:			return "Vertical Blanking";  	case V4L2_CID_HBLANK:			return "Horizontal Blanking";  	case V4L2_CID_ANALOGUE_GAIN:		return "Analogue Gain";  	/* Image processing controls */ +	/* Keep the order of the 'case's the same as in v4l2-controls.h! */  	case V4L2_CID_IMAGE_PROC_CLASS:		return "Image Processing Controls";  	case V4L2_CID_LINK_FREQ:		return "Link Frequency";  	case V4L2_CID_PIXEL_RATE:		return "Pixel Rate";  	case V4L2_CID_TEST_PATTERN:		return "Test Pattern";  	/* DV controls */ +	/* Keep the order of the 'case's the same as in v4l2-controls.h! */  	case V4L2_CID_DV_CLASS:			return "Digital Video Controls";  	case V4L2_CID_DV_TX_HOTPLUG:		return "Hotplug Present";  	case V4L2_CID_DV_TX_RXSENSE:		return "RxSense Present"; @@ -859,7 +881,6 @@ const char *v4l2_ctrl_get_name(u32 id)  	case V4L2_CID_FM_RX_CLASS:		return "FM Radio Receiver Controls";  	case V4L2_CID_TUNE_DEEMPHASIS:		return "De-Emphasis";  	case V4L2_CID_RDS_RECEPTION:		return "RDS Reception"; -  	case V4L2_CID_RF_TUNER_CLASS:		return "RF Tuner Controls";  	case V4L2_CID_RF_TUNER_LNA_GAIN_AUTO:	return "LNA Gain, Auto";  	case V4L2_CID_RF_TUNER_LNA_GAIN:	return "LNA Gain"; @@ -870,6 +891,20 @@ const char *v4l2_ctrl_get_name(u32 id)  	case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO:	return "Bandwidth, Auto";  	case V4L2_CID_RF_TUNER_BANDWIDTH:	return "Bandwidth";  	case V4L2_CID_RF_TUNER_PLL_LOCK:	return "PLL Lock"; +	case V4L2_CID_RDS_RX_PTY:		return "RDS Program Type"; +	case V4L2_CID_RDS_RX_PS_NAME:		return "RDS PS Name"; +	case V4L2_CID_RDS_RX_RADIO_TEXT:	return "RDS Radio Text"; +	case V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT: return "RDS Traffic Announcement"; +	case V4L2_CID_RDS_RX_TRAFFIC_PROGRAM:	return "RDS Traffic Program"; +	case V4L2_CID_RDS_RX_MUSIC_SPEECH:	return "RDS Music"; + +	/* Detection controls */ +	/* Keep the order of the 'case's the same as in v4l2-controls.h! */ +	case V4L2_CID_DETECT_CLASS:		return "Detection Controls"; +	case V4L2_CID_DETECT_MD_MODE:		return "Motion Detection Mode"; +	case V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD: return "MD Global Threshold"; +	case V4L2_CID_DETECT_MD_THRESHOLD_GRID:	return "MD Threshold Grid"; +	case V4L2_CID_DETECT_MD_REGION_GRID:	return "MD Region Grid";  	default:  		return NULL;  	} @@ -877,7 +912,7 @@ const char *v4l2_ctrl_get_name(u32 id)  EXPORT_SYMBOL(v4l2_ctrl_get_name);  void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, -		    s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags) +		    s64 *min, s64 *max, u64 *step, s64 *def, u32 *flags)  {  	*name = v4l2_ctrl_get_name(id);  	*flags = 0; @@ -924,6 +959,17 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,  	case V4L2_CID_RF_TUNER_IF_GAIN_AUTO:  	case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO:  	case V4L2_CID_RF_TUNER_PLL_LOCK: +	case V4L2_CID_RDS_TX_MONO_STEREO: +	case V4L2_CID_RDS_TX_ARTIFICIAL_HEAD: +	case V4L2_CID_RDS_TX_COMPRESSED: +	case V4L2_CID_RDS_TX_DYNAMIC_PTY: +	case V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT: +	case V4L2_CID_RDS_TX_TRAFFIC_PROGRAM: +	case V4L2_CID_RDS_TX_MUSIC_SPEECH: +	case V4L2_CID_RDS_TX_ALT_FREQS_ENABLE: +	case V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT: +	case V4L2_CID_RDS_RX_TRAFFIC_PROGRAM: +	case V4L2_CID_RDS_RX_MUSIC_SPEECH:  		*type = V4L2_CTRL_TYPE_BOOLEAN;  		*min = 0;  		*max = *step = 1; @@ -988,6 +1034,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,  	case V4L2_CID_TEST_PATTERN:  	case V4L2_CID_TUNE_DEEMPHASIS:  	case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL: +	case V4L2_CID_DETECT_MD_MODE:  		*type = V4L2_CTRL_TYPE_MENU;  		break;  	case V4L2_CID_LINK_FREQ: @@ -995,6 +1042,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,  		break;  	case V4L2_CID_RDS_TX_PS_NAME:  	case V4L2_CID_RDS_TX_RADIO_TEXT: +	case V4L2_CID_RDS_RX_PS_NAME: +	case V4L2_CID_RDS_RX_RADIO_TEXT:  		*type = V4L2_CTRL_TYPE_STRING;  		break;  	case V4L2_CID_ISO_SENSITIVITY: @@ -1014,6 +1063,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,  	case V4L2_CID_DV_CLASS:  	case V4L2_CID_FM_RX_CLASS:  	case V4L2_CID_RF_TUNER_CLASS: +	case V4L2_CID_DETECT_CLASS:  		*type = V4L2_CTRL_TYPE_CTRL_CLASS;  		/* You can neither read not write these */  		*flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY; @@ -1041,14 +1091,32 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,  		*type = V4L2_CTRL_TYPE_INTEGER;  		*flags |= V4L2_CTRL_FLAG_READ_ONLY;  		break; -	case V4L2_CID_MPEG_VIDEO_DEC_FRAME:  	case V4L2_CID_MPEG_VIDEO_DEC_PTS: -		*flags |= V4L2_CTRL_FLAG_VOLATILE; -		/* Fall through */ +		*type = V4L2_CTRL_TYPE_INTEGER64; +		*flags |= V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY; +		*min = *def = 0; +		*max = 0x1ffffffffLL; +		*step = 1; +		break; +	case V4L2_CID_MPEG_VIDEO_DEC_FRAME: +		*type = V4L2_CTRL_TYPE_INTEGER64; +		*flags |= V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY; +		*min = *def = 0; +		*max = 0x7fffffffffffffffLL; +		*step = 1; +		break;  	case V4L2_CID_PIXEL_RATE:  		*type = V4L2_CTRL_TYPE_INTEGER64;  		*flags |= V4L2_CTRL_FLAG_READ_ONLY; -		*min = *max = *step = *def = 0; +		break; +	case V4L2_CID_DETECT_MD_REGION_GRID: +		*type = V4L2_CTRL_TYPE_U8; +		break; +	case V4L2_CID_DETECT_MD_THRESHOLD_GRID: +		*type = V4L2_CTRL_TYPE_U16; +		break; +	case V4L2_CID_RDS_TX_ALT_FREQS: +		*type = V4L2_CTRL_TYPE_U32;  		break;  	default:  		*type = V4L2_CTRL_TYPE_INTEGER; @@ -1090,6 +1158,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,  	case V4L2_CID_RF_TUNER_MIXER_GAIN:  	case V4L2_CID_RF_TUNER_IF_GAIN:  	case V4L2_CID_RF_TUNER_BANDWIDTH: +	case V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD:  		*flags |= V4L2_CTRL_FLAG_SLIDER;  		break;  	case V4L2_CID_PAN_RELATIVE: @@ -1106,6 +1175,12 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,  	case V4L2_CID_DV_TX_RXSENSE:  	case V4L2_CID_DV_TX_EDID_PRESENT:  	case V4L2_CID_DV_RX_POWER_PRESENT: +	case V4L2_CID_RDS_RX_PTY: +	case V4L2_CID_RDS_RX_PS_NAME: +	case V4L2_CID_RDS_RX_RADIO_TEXT: +	case V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT: +	case V4L2_CID_RDS_RX_TRAFFIC_PROGRAM: +	case V4L2_CID_RDS_RX_MUSIC_SPEECH:  		*flags |= V4L2_CTRL_FLAG_READ_ONLY;  		break;  	case V4L2_CID_RF_TUNER_PLL_LOCK: @@ -1115,20 +1190,6 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,  }  EXPORT_SYMBOL(v4l2_ctrl_fill); -/* Helper function to determine whether the control type is compatible with -   VIDIOC_G/S_CTRL. */ -static bool type_is_int(const struct v4l2_ctrl *ctrl) -{ -	switch (ctrl->type) { -	case V4L2_CTRL_TYPE_INTEGER64: -	case V4L2_CTRL_TYPE_STRING: -		/* Nope, these need v4l2_ext_control */ -		return false; -	default: -		return true; -	} -} -  static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 changes)  {  	memset(ev->reserved, 0, sizeof(ev->reserved)); @@ -1137,10 +1198,10 @@ static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 change  	ev->u.ctrl.changes = changes;  	ev->u.ctrl.type = ctrl->type;  	ev->u.ctrl.flags = ctrl->flags; -	if (ctrl->type == V4L2_CTRL_TYPE_STRING) +	if (ctrl->is_ptr)  		ev->u.ctrl.value64 = 0;  	else -		ev->u.ctrl.value64 = ctrl->cur.val64; +		ev->u.ctrl.value64 = *ctrl->p_cur.p_s64;  	ev->u.ctrl.minimum = ctrl->minimum;  	ev->u.ctrl.maximum = ctrl->maximum;  	if (ctrl->type == V4L2_CTRL_TYPE_MENU @@ -1166,42 +1227,283 @@ static void send_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 changes)  			v4l2_event_queue_fh(sev->fh, &ev);  } -/* Helper function: copy the current control value back to the caller */ -static int cur_to_user(struct v4l2_ext_control *c, -		       struct v4l2_ctrl *ctrl) +static bool std_equal(const struct v4l2_ctrl *ctrl, u32 idx, +		      union v4l2_ctrl_ptr ptr1, +		      union v4l2_ctrl_ptr ptr2) +{ +	switch (ctrl->type) { +	case V4L2_CTRL_TYPE_BUTTON: +		return false; +	case V4L2_CTRL_TYPE_STRING: +		idx *= ctrl->elem_size; +		/* strings are always 0-terminated */ +		return !strcmp(ptr1.p_char + idx, ptr2.p_char + idx); +	case V4L2_CTRL_TYPE_INTEGER64: +		return ptr1.p_s64[idx] == ptr2.p_s64[idx]; +	case V4L2_CTRL_TYPE_U8: +		return ptr1.p_u8[idx] == ptr2.p_u8[idx]; +	case V4L2_CTRL_TYPE_U16: +		return ptr1.p_u16[idx] == ptr2.p_u16[idx]; +	case V4L2_CTRL_TYPE_U32: +		return ptr1.p_u32[idx] == ptr2.p_u32[idx]; +	default: +		if (ctrl->is_int) +			return ptr1.p_s32[idx] == ptr2.p_s32[idx]; +		idx *= ctrl->elem_size; +		return !memcmp(ptr1.p + idx, ptr2.p + idx, ctrl->elem_size); +	} +} + +static void std_init(const struct v4l2_ctrl *ctrl, u32 idx, +		     union v4l2_ctrl_ptr ptr) +{ +	switch (ctrl->type) { +	case V4L2_CTRL_TYPE_STRING: +		idx *= ctrl->elem_size; +		memset(ptr.p_char + idx, ' ', ctrl->minimum); +		ptr.p_char[idx + ctrl->minimum] = '\0'; +		break; +	case V4L2_CTRL_TYPE_INTEGER64: +		ptr.p_s64[idx] = ctrl->default_value; +		break; +	case V4L2_CTRL_TYPE_INTEGER: +	case V4L2_CTRL_TYPE_INTEGER_MENU: +	case V4L2_CTRL_TYPE_MENU: +	case V4L2_CTRL_TYPE_BITMASK: +	case V4L2_CTRL_TYPE_BOOLEAN: +		ptr.p_s32[idx] = ctrl->default_value; +		break; +	case V4L2_CTRL_TYPE_U8: +		ptr.p_u8[idx] = ctrl->default_value; +		break; +	case V4L2_CTRL_TYPE_U16: +		ptr.p_u16[idx] = ctrl->default_value; +		break; +	case V4L2_CTRL_TYPE_U32: +		ptr.p_u32[idx] = ctrl->default_value; +		break; +	default: +		idx *= ctrl->elem_size; +		memset(ptr.p + idx, 0, ctrl->elem_size); +		break; +	} +} + +static void std_log(const struct v4l2_ctrl *ctrl) +{ +	union v4l2_ctrl_ptr ptr = ctrl->p_cur; + +	if (ctrl->is_array) { +		unsigned i; + +		for (i = 0; i < ctrl->nr_of_dims; i++) +			pr_cont("[%u]", ctrl->dims[i]); +		pr_cont(" "); +	} + +	switch (ctrl->type) { +	case V4L2_CTRL_TYPE_INTEGER: +		pr_cont("%d", *ptr.p_s32); +		break; +	case V4L2_CTRL_TYPE_BOOLEAN: +		pr_cont("%s", *ptr.p_s32 ? "true" : "false"); +		break; +	case V4L2_CTRL_TYPE_MENU: +		pr_cont("%s", ctrl->qmenu[*ptr.p_s32]); +		break; +	case V4L2_CTRL_TYPE_INTEGER_MENU: +		pr_cont("%lld", ctrl->qmenu_int[*ptr.p_s32]); +		break; +	case V4L2_CTRL_TYPE_BITMASK: +		pr_cont("0x%08x", *ptr.p_s32); +		break; +	case V4L2_CTRL_TYPE_INTEGER64: +		pr_cont("%lld", *ptr.p_s64); +		break; +	case V4L2_CTRL_TYPE_STRING: +		pr_cont("%s", ptr.p_char); +		break; +	case V4L2_CTRL_TYPE_U8: +		pr_cont("%u", (unsigned)*ptr.p_u8); +		break; +	case V4L2_CTRL_TYPE_U16: +		pr_cont("%u", (unsigned)*ptr.p_u16); +		break; +	case V4L2_CTRL_TYPE_U32: +		pr_cont("%u", (unsigned)*ptr.p_u32); +		break; +	default: +		pr_cont("unknown type %d", ctrl->type); +		break; +	} +} + +/* + * Round towards the closest legal value. Be careful when we are + * close to the maximum range of the control type to prevent + * wrap-arounds. + */ +#define ROUND_TO_RANGE(val, offset_type, ctrl)			\ +({								\ +	offset_type offset;					\ +	if ((ctrl)->maximum >= 0 &&				\ +	    val >= (ctrl)->maximum - (s32)((ctrl)->step / 2))	\ +		val = (ctrl)->maximum;				\ +	else							\ +		val += (s32)((ctrl)->step / 2);			\ +	val = clamp_t(typeof(val), val,				\ +		      (ctrl)->minimum, (ctrl)->maximum);	\ +	offset = (val) - (ctrl)->minimum;			\ +	offset = (ctrl)->step * (offset / (u32)(ctrl)->step);	\ +	val = (ctrl)->minimum + offset;				\ +	0;							\ +}) + +/* Validate a new control */ +static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx, +			union v4l2_ctrl_ptr ptr) +{ +	size_t len; +	u64 offset; +	s64 val; + +	switch (ctrl->type) { +	case V4L2_CTRL_TYPE_INTEGER: +		return ROUND_TO_RANGE(ptr.p_s32[idx], u32, ctrl); +	case V4L2_CTRL_TYPE_INTEGER64: +		/* +		 * We can't use the ROUND_TO_RANGE define here due to +		 * the u64 divide that needs special care. +		 */ +		val = ptr.p_s64[idx]; +		if (ctrl->maximum >= 0 && val >= ctrl->maximum - (s64)(ctrl->step / 2)) +			val = ctrl->maximum; +		else +			val += (s64)(ctrl->step / 2); +		val = clamp_t(s64, val, ctrl->minimum, ctrl->maximum); +		offset = val - ctrl->minimum; +		do_div(offset, ctrl->step); +		ptr.p_s64[idx] = ctrl->minimum + offset * ctrl->step; +		return 0; +	case V4L2_CTRL_TYPE_U8: +		return ROUND_TO_RANGE(ptr.p_u8[idx], u8, ctrl); +	case V4L2_CTRL_TYPE_U16: +		return ROUND_TO_RANGE(ptr.p_u16[idx], u16, ctrl); +	case V4L2_CTRL_TYPE_U32: +		return ROUND_TO_RANGE(ptr.p_u32[idx], u32, ctrl); + +	case V4L2_CTRL_TYPE_BOOLEAN: +		ptr.p_s32[idx] = !!ptr.p_s32[idx]; +		return 0; + +	case V4L2_CTRL_TYPE_MENU: +	case V4L2_CTRL_TYPE_INTEGER_MENU: +		if (ptr.p_s32[idx] < ctrl->minimum || ptr.p_s32[idx] > ctrl->maximum) +			return -ERANGE; +		if (ctrl->menu_skip_mask & (1 << ptr.p_s32[idx])) +			return -EINVAL; +		if (ctrl->type == V4L2_CTRL_TYPE_MENU && +		    ctrl->qmenu[ptr.p_s32[idx]][0] == '\0') +			return -EINVAL; +		return 0; + +	case V4L2_CTRL_TYPE_BITMASK: +		ptr.p_s32[idx] &= ctrl->maximum; +		return 0; + +	case V4L2_CTRL_TYPE_BUTTON: +	case V4L2_CTRL_TYPE_CTRL_CLASS: +		ptr.p_s32[idx] = 0; +		return 0; + +	case V4L2_CTRL_TYPE_STRING: +		idx *= ctrl->elem_size; +		len = strlen(ptr.p_char + idx); +		if (len < ctrl->minimum) +			return -ERANGE; +		if ((len - (u32)ctrl->minimum) % (u32)ctrl->step) +			return -ERANGE; +		return 0; + +	default: +		return -EINVAL; +	} +} + +static const struct v4l2_ctrl_type_ops std_type_ops = { +	.equal = std_equal, +	.init = std_init, +	.log = std_log, +	.validate = std_validate, +}; + +/* Helper function: copy the given control value back to the caller */ +static int ptr_to_user(struct v4l2_ext_control *c, +		       struct v4l2_ctrl *ctrl, +		       union v4l2_ctrl_ptr ptr)  {  	u32 len; +	if (ctrl->is_ptr && !ctrl->is_string) +		return copy_to_user(c->ptr, ptr.p, c->size) ? +		       -EFAULT : 0; +  	switch (ctrl->type) {  	case V4L2_CTRL_TYPE_STRING: -		len = strlen(ctrl->cur.string); +		len = strlen(ptr.p_char);  		if (c->size < len + 1) { -			c->size = len + 1; +			c->size = ctrl->elem_size;  			return -ENOSPC;  		} -		return copy_to_user(c->string, ctrl->cur.string, -						len + 1) ? -EFAULT : 0; +		return copy_to_user(c->string, ptr.p_char, len + 1) ? +		       -EFAULT : 0;  	case V4L2_CTRL_TYPE_INTEGER64: -		c->value64 = ctrl->cur.val64; +		c->value64 = *ptr.p_s64;  		break;  	default: -		c->value = ctrl->cur.val; +		c->value = *ptr.p_s32;  		break;  	}  	return 0;  } -/* Helper function: copy the caller-provider value as the new control value */ -static int user_to_new(struct v4l2_ext_control *c, +/* Helper function: copy the current control value back to the caller */ +static int cur_to_user(struct v4l2_ext_control *c,  		       struct v4l2_ctrl *ctrl)  { +	return ptr_to_user(c, ctrl, ctrl->p_cur); +} + +/* Helper function: copy the new control value back to the caller */ +static int new_to_user(struct v4l2_ext_control *c, +		       struct v4l2_ctrl *ctrl) +{ +	return ptr_to_user(c, ctrl, ctrl->p_new); +} + +/* Helper function: copy the caller-provider value to the given control value */ +static int user_to_ptr(struct v4l2_ext_control *c, +		       struct v4l2_ctrl *ctrl, +		       union v4l2_ctrl_ptr ptr) +{  	int ret;  	u32 size;  	ctrl->is_new = 1; +	if (ctrl->is_ptr && !ctrl->is_string) { +		unsigned idx; + +		ret = copy_from_user(ptr.p, c->ptr, c->size) ? -EFAULT : 0; +		if (ret || !ctrl->is_array) +			return ret; +		for (idx = c->size / ctrl->elem_size; idx < ctrl->elems; idx++) +			ctrl->type_ops->init(ctrl, idx, ptr); +		return 0; +	} +  	switch (ctrl->type) {  	case V4L2_CTRL_TYPE_INTEGER64: -		ctrl->val64 = c->value64; +		*ptr.p_s64 = c->value64;  		break;  	case V4L2_CTRL_TYPE_STRING:  		size = c->size; @@ -1209,74 +1511,53 @@ static int user_to_new(struct v4l2_ext_control *c,  			return -ERANGE;  		if (size > ctrl->maximum + 1)  			size = ctrl->maximum + 1; -		ret = copy_from_user(ctrl->string, c->string, size); +		ret = copy_from_user(ptr.p_char, c->string, size) ? -EFAULT : 0;  		if (!ret) { -			char last = ctrl->string[size - 1]; +			char last = ptr.p_char[size - 1]; -			ctrl->string[size - 1] = 0; +			ptr.p_char[size - 1] = 0;  			/* If the string was longer than ctrl->maximum,  			   then return an error. */ -			if (strlen(ctrl->string) == ctrl->maximum && last) +			if (strlen(ptr.p_char) == ctrl->maximum && last)  				return -ERANGE;  		} -		return ret ? -EFAULT : 0; +		return ret;  	default: -		ctrl->val = c->value; +		*ptr.p_s32 = c->value;  		break;  	}  	return 0;  } -/* Helper function: copy the new control value back to the caller */ -static int new_to_user(struct v4l2_ext_control *c, +/* Helper function: copy the caller-provider value as the new control value */ +static int user_to_new(struct v4l2_ext_control *c,  		       struct v4l2_ctrl *ctrl)  { -	u32 len; +	return user_to_ptr(c, ctrl, ctrl->p_new); +} -	switch (ctrl->type) { -	case V4L2_CTRL_TYPE_STRING: -		len = strlen(ctrl->string); -		if (c->size < len + 1) { -			c->size = ctrl->maximum + 1; -			return -ENOSPC; -		} -		return copy_to_user(c->string, ctrl->string, -						len + 1) ? -EFAULT : 0; -	case V4L2_CTRL_TYPE_INTEGER64: -		c->value64 = ctrl->val64; -		break; -	default: -		c->value = ctrl->val; -		break; -	} -	return 0; +/* Copy the one value to another. */ +static void ptr_to_ptr(struct v4l2_ctrl *ctrl, +		       union v4l2_ctrl_ptr from, union v4l2_ctrl_ptr to) +{ +	if (ctrl == NULL) +		return; +	memcpy(to.p, from.p, ctrl->elems * ctrl->elem_size);  }  /* Copy the new value to the current value. */  static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 ch_flags)  { -	bool changed = false; +	bool changed;  	if (ctrl == NULL)  		return; -	switch (ctrl->type) { -	case V4L2_CTRL_TYPE_BUTTON: -		changed = true; -		break; -	case V4L2_CTRL_TYPE_STRING: -		/* strings are always 0-terminated */ -		changed = strcmp(ctrl->string, ctrl->cur.string); -		strcpy(ctrl->cur.string, ctrl->string); -		break; -	case V4L2_CTRL_TYPE_INTEGER64: -		changed = ctrl->val64 != ctrl->cur.val64; -		ctrl->cur.val64 = ctrl->val64; -		break; -	default: -		changed = ctrl->val != ctrl->cur.val; -		ctrl->cur.val = ctrl->val; -		break; -	} + +	/* has_changed is set by cluster_changed */ +	changed = ctrl->has_changed; +	if (changed) +		ptr_to_ptr(ctrl, ctrl->p_new, ctrl->p_cur); +  	if (ch_flags & V4L2_EVENT_CTRL_CH_FLAGS) {  		/* Note: CH_FLAGS is only set for auto clusters. */  		ctrl->flags &= @@ -1305,62 +1586,47 @@ static void cur_to_new(struct v4l2_ctrl *ctrl)  {  	if (ctrl == NULL)  		return; -	switch (ctrl->type) { -	case V4L2_CTRL_TYPE_STRING: -		/* strings are always 0-terminated */ -		strcpy(ctrl->string, ctrl->cur.string); -		break; -	case V4L2_CTRL_TYPE_INTEGER64: -		ctrl->val64 = ctrl->cur.val64; -		break; -	default: -		ctrl->val = ctrl->cur.val; -		break; -	} +	ptr_to_ptr(ctrl, ctrl->p_cur, ctrl->p_new);  }  /* Return non-zero if one or more of the controls in the cluster has a new     value that differs from the current value. */  static int cluster_changed(struct v4l2_ctrl *master)  { -	int diff = 0; +	bool changed = false; +	unsigned idx;  	int i; -	for (i = 0; !diff && i < master->ncontrols; i++) { +	for (i = 0; i < master->ncontrols; i++) {  		struct v4l2_ctrl *ctrl = master->cluster[i]; +		bool ctrl_changed = false;  		if (ctrl == NULL)  			continue; -		switch (ctrl->type) { -		case V4L2_CTRL_TYPE_BUTTON: -			/* Button controls are always 'different' */ -			return 1; -		case V4L2_CTRL_TYPE_STRING: -			/* strings are always 0-terminated */ -			diff = strcmp(ctrl->string, ctrl->cur.string); -			break; -		case V4L2_CTRL_TYPE_INTEGER64: -			diff = ctrl->val64 != ctrl->cur.val64; -			break; -		default: -			diff = ctrl->val != ctrl->cur.val; -			break; -		} +		for (idx = 0; !ctrl_changed && idx < ctrl->elems; idx++) +			ctrl_changed = !ctrl->type_ops->equal(ctrl, idx, +				ctrl->p_cur, ctrl->p_new); +		ctrl->has_changed = ctrl_changed; +		changed |= ctrl->has_changed;  	} -	return diff; +	return changed;  }  /* Control range checking */  static int check_range(enum v4l2_ctrl_type type, -		s32 min, s32 max, u32 step, s32 def) +		s64 min, s64 max, u64 step, s64 def)  {  	switch (type) {  	case V4L2_CTRL_TYPE_BOOLEAN:  		if (step != 1 || max > 1 || min < 0)  			return -ERANGE;  		/* fall through */ +	case V4L2_CTRL_TYPE_U8: +	case V4L2_CTRL_TYPE_U16: +	case V4L2_CTRL_TYPE_U32:  	case V4L2_CTRL_TYPE_INTEGER: -		if (step <= 0 || min > max || def < min || def > max) +	case V4L2_CTRL_TYPE_INTEGER64: +		if (step == 0 || min > max || def < min || def > max)  			return -ERANGE;  		return 0;  	case V4L2_CTRL_TYPE_BITMASK: @@ -1389,58 +1655,33 @@ static int check_range(enum v4l2_ctrl_type type,  static int validate_new(const struct v4l2_ctrl *ctrl,  			struct v4l2_ext_control *c)  { -	size_t len; -	u32 offset; -	s32 val; - -	switch (ctrl->type) { -	case V4L2_CTRL_TYPE_INTEGER: -		/* Round towards the closest legal value */ -		val = c->value + ctrl->step / 2; -		val = clamp(val, ctrl->minimum, ctrl->maximum); -		offset = val - ctrl->minimum; -		offset = ctrl->step * (offset / ctrl->step); -		c->value = ctrl->minimum + offset; -		return 0; - -	case V4L2_CTRL_TYPE_BOOLEAN: -		c->value = !!c->value; -		return 0; - -	case V4L2_CTRL_TYPE_MENU: -	case V4L2_CTRL_TYPE_INTEGER_MENU: -		if (c->value < ctrl->minimum || c->value > ctrl->maximum) -			return -ERANGE; -		if (ctrl->menu_skip_mask & (1 << c->value)) -			return -EINVAL; -		if (ctrl->type == V4L2_CTRL_TYPE_MENU && -		    ctrl->qmenu[c->value][0] == '\0') -			return -EINVAL; -		return 0; - -	case V4L2_CTRL_TYPE_BITMASK: -		c->value &= ctrl->maximum; -		return 0; - -	case V4L2_CTRL_TYPE_BUTTON: -	case V4L2_CTRL_TYPE_CTRL_CLASS: -		c->value = 0; -		return 0; - -	case V4L2_CTRL_TYPE_INTEGER64: -		return 0; +	union v4l2_ctrl_ptr ptr; +	unsigned idx; +	int err = 0; -	case V4L2_CTRL_TYPE_STRING: -		len = strlen(c->string); -		if (len < ctrl->minimum) -			return -ERANGE; -		if ((len - ctrl->minimum) % ctrl->step) -			return -ERANGE; -		return 0; +	if (!ctrl->is_ptr) { +		switch (ctrl->type) { +		case V4L2_CTRL_TYPE_INTEGER: +		case V4L2_CTRL_TYPE_INTEGER_MENU: +		case V4L2_CTRL_TYPE_MENU: +		case V4L2_CTRL_TYPE_BITMASK: +		case V4L2_CTRL_TYPE_BOOLEAN: +		case V4L2_CTRL_TYPE_BUTTON: +		case V4L2_CTRL_TYPE_CTRL_CLASS: +			ptr.p_s32 = &c->value; +			return ctrl->type_ops->validate(ctrl, 0, ptr); -	default: -		return -EINVAL; +		case V4L2_CTRL_TYPE_INTEGER64: +			ptr.p_s64 = &c->value64; +			return ctrl->type_ops->validate(ctrl, 0, ptr); +		default: +			break; +		}  	} +	ptr.p = c->ptr; +	for (idx = 0; !err && idx < c->size / ctrl->elem_size; idx++) +		err = ctrl->type_ops->validate(ctrl, idx, ptr); +	return err;  }  static inline u32 node2id(struct list_head *node) @@ -1522,7 +1763,7 @@ static struct v4l2_ctrl_ref *find_private_ref(  		   VIDIOC_G/S_CTRL. */  		if (V4L2_CTRL_ID2CLASS(ref->ctrl->id) == V4L2_CTRL_CLASS_USER &&  		    V4L2_CTRL_DRIVER_PRIV(ref->ctrl->id)) { -			if (!type_is_int(ref->ctrl)) +			if (!ref->ctrl->is_int)  				continue;  			if (id == 0)  				return ref; @@ -1592,8 +1833,12 @@ static int handler_new_ref(struct v4l2_ctrl_handler *hdl,  	u32 class_ctrl = V4L2_CTRL_ID2CLASS(id) | 1;  	int bucket = id % hdl->nr_of_buckets;	/* which bucket to use */ -	/* Automatically add the control class if it is not yet present. */ -	if (id != class_ctrl && find_ref_lock(hdl, class_ctrl) == NULL) +	/* +	 * Automatically add the control class if it is not yet present and +	 * the new control is not a compound control. +	 */ +	if (ctrl->type < V4L2_CTRL_COMPOUND_TYPES && +	    id != class_ctrl && find_ref_lock(hdl, class_ctrl) == NULL)  		if (!v4l2_ctrl_new_std(hdl, NULL, class_ctrl, 0, 0, 0, 0))  			return hdl->error; @@ -1652,20 +1897,61 @@ unlock:  /* Add a new control */  static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,  			const struct v4l2_ctrl_ops *ops, +			const struct v4l2_ctrl_type_ops *type_ops,  			u32 id, const char *name, enum v4l2_ctrl_type type, -			s32 min, s32 max, u32 step, s32 def, +			s64 min, s64 max, u64 step, s64 def, +			const u32 dims[V4L2_CTRL_MAX_DIMS], u32 elem_size,  			u32 flags, const char * const *qmenu,  			const s64 *qmenu_int, void *priv)  {  	struct v4l2_ctrl *ctrl; -	unsigned sz_extra = 0; +	unsigned sz_extra; +	unsigned nr_of_dims = 0; +	unsigned elems = 1; +	bool is_array; +	unsigned tot_ctrl_size; +	unsigned idx; +	void *data;  	int err;  	if (hdl->error)  		return NULL; +	while (dims && dims[nr_of_dims]) { +		elems *= dims[nr_of_dims]; +		nr_of_dims++; +		if (nr_of_dims == V4L2_CTRL_MAX_DIMS) +			break; +	} +	is_array = nr_of_dims > 0; + +	/* Prefill elem_size for all types handled by std_type_ops */ +	switch (type) { +	case V4L2_CTRL_TYPE_INTEGER64: +		elem_size = sizeof(s64); +		break; +	case V4L2_CTRL_TYPE_STRING: +		elem_size = max + 1; +		break; +	case V4L2_CTRL_TYPE_U8: +		elem_size = sizeof(u8); +		break; +	case V4L2_CTRL_TYPE_U16: +		elem_size = sizeof(u16); +		break; +	case V4L2_CTRL_TYPE_U32: +		elem_size = sizeof(u32); +		break; +	default: +		if (type < V4L2_CTRL_COMPOUND_TYPES) +			elem_size = sizeof(s32); +		break; +	} +	tot_ctrl_size = elem_size * elems; +  	/* Sanity checks */ -	if (id == 0 || name == NULL || id >= V4L2_CID_PRIVATE_BASE || +	if (id == 0 || name == NULL || !elem_size || +	    id >= V4L2_CID_PRIVATE_BASE ||  	    (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) ||  	    (type == V4L2_CTRL_TYPE_INTEGER_MENU && qmenu_int == NULL)) {  		handler_set_err(hdl, -ERANGE); @@ -1680,13 +1966,23 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,  		handler_set_err(hdl, -ERANGE);  		return NULL;  	} +	if (is_array && +	    (type == V4L2_CTRL_TYPE_BUTTON || +	     type == V4L2_CTRL_TYPE_CTRL_CLASS)) { +		handler_set_err(hdl, -EINVAL); +		return NULL; +	} +	sz_extra = 0;  	if (type == V4L2_CTRL_TYPE_BUTTON)  		flags |= V4L2_CTRL_FLAG_WRITE_ONLY;  	else if (type == V4L2_CTRL_TYPE_CTRL_CLASS)  		flags |= V4L2_CTRL_FLAG_READ_ONLY; -	else if (type == V4L2_CTRL_TYPE_STRING) -		sz_extra += 2 * (max + 1); +	else if (type == V4L2_CTRL_TYPE_INTEGER64 || +		 type == V4L2_CTRL_TYPE_STRING || +		 type >= V4L2_CTRL_COMPOUND_TYPES || +		 is_array) +		sz_extra += 2 * tot_ctrl_size;  	ctrl = kzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);  	if (ctrl == NULL) { @@ -1698,6 +1994,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,  	INIT_LIST_HEAD(&ctrl->ev_subs);  	ctrl->handler = hdl;  	ctrl->ops = ops; +	ctrl->type_ops = type_ops ? type_ops : &std_type_ops;  	ctrl->id = id;  	ctrl->name = name;  	ctrl->type = type; @@ -1705,19 +2002,36 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,  	ctrl->minimum = min;  	ctrl->maximum = max;  	ctrl->step = step; +	ctrl->default_value = def; +	ctrl->is_string = !is_array && type == V4L2_CTRL_TYPE_STRING; +	ctrl->is_ptr = is_array || type >= V4L2_CTRL_COMPOUND_TYPES || ctrl->is_string; +	ctrl->is_int = !ctrl->is_ptr && type != V4L2_CTRL_TYPE_INTEGER64; +	ctrl->is_array = is_array; +	ctrl->elems = elems; +	ctrl->nr_of_dims = nr_of_dims; +	if (nr_of_dims) +		memcpy(ctrl->dims, dims, nr_of_dims * sizeof(dims[0])); +	ctrl->elem_size = elem_size;  	if (type == V4L2_CTRL_TYPE_MENU)  		ctrl->qmenu = qmenu;  	else if (type == V4L2_CTRL_TYPE_INTEGER_MENU)  		ctrl->qmenu_int = qmenu_int;  	ctrl->priv = priv; -	ctrl->cur.val = ctrl->val = ctrl->default_value = def; +	ctrl->cur.val = ctrl->val = def; +	data = &ctrl[1]; -	if (ctrl->type == V4L2_CTRL_TYPE_STRING) { -		ctrl->cur.string = (char *)&ctrl[1] + sz_extra - (max + 1); -		ctrl->string = (char *)&ctrl[1] + sz_extra - 2 * (max + 1); -		if (ctrl->minimum) -			memset(ctrl->cur.string, ' ', ctrl->minimum); +	if (!ctrl->is_int) { +		ctrl->p_new.p = data; +		ctrl->p_cur.p = data + tot_ctrl_size; +	} else { +		ctrl->p_new.p = &ctrl->val; +		ctrl->p_cur.p = &ctrl->cur.val;  	} +	for (idx = 0; idx < elems; idx++) { +		ctrl->type_ops->init(ctrl, idx, ctrl->p_cur); +		ctrl->type_ops->init(ctrl, idx, ctrl->p_new); +	} +  	if (handler_new_ref(hdl, ctrl)) {  		kfree(ctrl);  		return NULL; @@ -1738,10 +2052,10 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,  	const s64 *qmenu_int = cfg->qmenu_int;  	enum v4l2_ctrl_type type = cfg->type;  	u32 flags = cfg->flags; -	s32 min = cfg->min; -	s32 max = cfg->max; -	u32 step = cfg->step; -	s32 def = cfg->def; +	s64 min = cfg->min; +	s64 max = cfg->max; +	u64 step = cfg->step; +	s64 def = cfg->def;  	if (name == NULL)  		v4l2_ctrl_fill(cfg->id, &name, &type, &min, &max, &step, @@ -1761,10 +2075,11 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,  		return NULL;  	} -	ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->id, name, +	ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->type_ops, cfg->id, name,  			type, min, max, -			is_menu ? cfg->menu_skip_mask : step, -			def, flags, qmenu, qmenu_int, priv); +			is_menu ? cfg->menu_skip_mask : step, def, +			cfg->dims, cfg->elem_size, +			flags, qmenu, qmenu_int, priv);  	if (ctrl)  		ctrl->is_private = cfg->is_private;  	return ctrl; @@ -1774,35 +2089,39 @@ EXPORT_SYMBOL(v4l2_ctrl_new_custom);  /* Helper function for standard non-menu controls */  struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,  			const struct v4l2_ctrl_ops *ops, -			u32 id, s32 min, s32 max, u32 step, s32 def) +			u32 id, s64 min, s64 max, u64 step, s64 def)  {  	const char *name;  	enum v4l2_ctrl_type type;  	u32 flags;  	v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags); -	if (type == V4L2_CTRL_TYPE_MENU -	    || type == V4L2_CTRL_TYPE_INTEGER_MENU) { +	if (type == V4L2_CTRL_TYPE_MENU || +	    type == V4L2_CTRL_TYPE_INTEGER_MENU || +	    type >= V4L2_CTRL_COMPOUND_TYPES) {  		handler_set_err(hdl, -EINVAL);  		return NULL;  	} -	return v4l2_ctrl_new(hdl, ops, id, name, type, -			     min, max, step, def, flags, NULL, NULL, NULL); +	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, +			     min, max, step, def, NULL, 0, +			     flags, NULL, NULL, NULL);  }  EXPORT_SYMBOL(v4l2_ctrl_new_std);  /* Helper function for standard menu controls */  struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,  			const struct v4l2_ctrl_ops *ops, -			u32 id, s32 max, s32 mask, s32 def) +			u32 id, u8 _max, u64 mask, u8 _def)  {  	const char * const *qmenu = NULL;  	const s64 *qmenu_int = NULL;  	unsigned int qmenu_int_len = 0;  	const char *name;  	enum v4l2_ctrl_type type; -	s32 min; -	s32 step; +	s64 min; +	s64 max = _max; +	s64 def = _def; +	u64 step;  	u32 flags;  	v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags); @@ -1816,21 +2135,24 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,  		handler_set_err(hdl, -EINVAL);  		return NULL;  	} -	return v4l2_ctrl_new(hdl, ops, id, name, type, -			     0, max, mask, def, flags, qmenu, qmenu_int, NULL); +	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, +			     0, max, mask, def, NULL, 0, +			     flags, qmenu, qmenu_int, NULL);  }  EXPORT_SYMBOL(v4l2_ctrl_new_std_menu);  /* Helper function for standard menu controls with driver defined menu */  struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl, -			const struct v4l2_ctrl_ops *ops, u32 id, s32 max, -			s32 mask, s32 def, const char * const *qmenu) +			const struct v4l2_ctrl_ops *ops, u32 id, u8 _max, +			u64 mask, u8 _def, const char * const *qmenu)  {  	enum v4l2_ctrl_type type;  	const char *name;  	u32 flags; -	s32 step; -	s32 min; +	u64 step; +	s64 min; +	s64 max = _max; +	s64 def = _def;  	/* v4l2_ctrl_new_std_menu_items() should only be called for  	 * standard controls without a standard menu. @@ -1845,7 +2167,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl,  		handler_set_err(hdl, -EINVAL);  		return NULL;  	} -	return v4l2_ctrl_new(hdl, ops, id, name, type, 0, max, mask, def, +	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, +			     0, max, mask, def, NULL, 0,  			     flags, qmenu, NULL, NULL);  } @@ -1854,12 +2177,14 @@ EXPORT_SYMBOL(v4l2_ctrl_new_std_menu_items);  /* Helper function for standard integer menu controls */  struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl,  			const struct v4l2_ctrl_ops *ops, -			u32 id, s32 max, s32 def, const s64 *qmenu_int) +			u32 id, u8 _max, u8 _def, const s64 *qmenu_int)  {  	const char *name;  	enum v4l2_ctrl_type type; -	s32 min; -	s32 step; +	s64 min; +	u64 step; +	s64 max = _max; +	s64 def = _def;  	u32 flags;  	v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags); @@ -1867,8 +2192,9 @@ struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl,  		handler_set_err(hdl, -EINVAL);  		return NULL;  	} -	return v4l2_ctrl_new(hdl, ops, id, name, type, -			     0, max, 0, def, flags, NULL, qmenu_int, NULL); +	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, +			     0, max, 0, def, NULL, 0, +			     flags, NULL, qmenu_int, NULL);  }  EXPORT_SYMBOL(v4l2_ctrl_new_int_menu); @@ -2048,45 +2374,21 @@ static void log_ctrl(const struct v4l2_ctrl *ctrl,  	if (ctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)  		return; -	printk(KERN_INFO "%s%s%s: ", prefix, colon, ctrl->name); +	pr_info("%s%s%s: ", prefix, colon, ctrl->name); + +	ctrl->type_ops->log(ctrl); -	switch (ctrl->type) { -	case V4L2_CTRL_TYPE_INTEGER: -		printk(KERN_CONT "%d", ctrl->cur.val); -		break; -	case V4L2_CTRL_TYPE_BOOLEAN: -		printk(KERN_CONT "%s", ctrl->cur.val ? "true" : "false"); -		break; -	case V4L2_CTRL_TYPE_MENU: -		printk(KERN_CONT "%s", ctrl->qmenu[ctrl->cur.val]); -		break; -	case V4L2_CTRL_TYPE_INTEGER_MENU: -		printk(KERN_CONT "%lld", ctrl->qmenu_int[ctrl->cur.val]); -		break; -	case V4L2_CTRL_TYPE_BITMASK: -		printk(KERN_CONT "0x%08x", ctrl->cur.val); -		break; -	case V4L2_CTRL_TYPE_INTEGER64: -		printk(KERN_CONT "%lld", ctrl->cur.val64); -		break; -	case V4L2_CTRL_TYPE_STRING: -		printk(KERN_CONT "%s", ctrl->cur.string); -		break; -	default: -		printk(KERN_CONT "unknown type %d", ctrl->type); -		break; -	}  	if (ctrl->flags & (V4L2_CTRL_FLAG_INACTIVE |  			   V4L2_CTRL_FLAG_GRABBED |  			   V4L2_CTRL_FLAG_VOLATILE)) {  		if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) -			printk(KERN_CONT " inactive"); +			pr_cont(" inactive");  		if (ctrl->flags & V4L2_CTRL_FLAG_GRABBED) -			printk(KERN_CONT " grabbed"); +			pr_cont(" grabbed");  		if (ctrl->flags & V4L2_CTRL_FLAG_VOLATILE) -			printk(KERN_CONT " volatile"); +			pr_cont(" volatile");  	} -	printk(KERN_CONT "\n"); +	pr_cont("\n");  }  /* Log all controls owned by the handler */ @@ -2157,9 +2459,10 @@ int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl)  }  EXPORT_SYMBOL(v4l2_ctrl_handler_setup); -/* Implement VIDIOC_QUERYCTRL */ -int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) +/* Implement VIDIOC_QUERY_EXT_CTRL */ +int v4l2_query_ext_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_query_ext_ctrl *qc)  { +	const unsigned next_flags = V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND;  	u32 id = qc->id & V4L2_CTRL_ID_MASK;  	struct v4l2_ctrl_ref *ref;  	struct v4l2_ctrl *ctrl; @@ -2172,7 +2475,20 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc)  	/* Try to find it */  	ref = find_ref(hdl, id); -	if ((qc->id & V4L2_CTRL_FLAG_NEXT_CTRL) && !list_empty(&hdl->ctrl_refs)) { +	if ((qc->id & next_flags) && !list_empty(&hdl->ctrl_refs)) { +		bool is_compound; +		/* Match any control that is not hidden */ +		unsigned mask = 1; +		bool match = false; + +		if ((qc->id & next_flags) == V4L2_CTRL_FLAG_NEXT_COMPOUND) { +			/* Match any hidden control */ +			match = true; +		} else if ((qc->id & next_flags) == next_flags) { +			/* Match any control, compound or not */ +			mask = 0; +		} +  		/* Find the next control with ID > qc->id */  		/* Did we reach the end of the control list? */ @@ -2180,19 +2496,34 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc)  			ref = NULL; /* Yes, so there is no next control */  		} else if (ref) {  			/* We found a control with the given ID, so just get -			   the next one in the list. */ -			ref = list_entry(ref->node.next, typeof(*ref), node); +			   the next valid one in the list. */ +			list_for_each_entry_continue(ref, &hdl->ctrl_refs, node) { +				is_compound = +					ref->ctrl->type >= V4L2_CTRL_COMPOUND_TYPES; +				if (id < ref->ctrl->id && +				    (is_compound & mask) == match) +					break; +			} +			if (&ref->node == &hdl->ctrl_refs) +				ref = NULL;  		} else {  			/* No control with the given ID exists, so start  			   searching for the next largest ID. We know there  			   is one, otherwise the first 'if' above would have  			   been true. */ -			list_for_each_entry(ref, &hdl->ctrl_refs, node) -				if (id < ref->ctrl->id) +			list_for_each_entry(ref, &hdl->ctrl_refs, node) { +				is_compound = +					ref->ctrl->type >= V4L2_CTRL_COMPOUND_TYPES; +				if (id < ref->ctrl->id && +				    (is_compound & mask) == match)  					break; +			} +			if (&ref->node == &hdl->ctrl_refs) +				ref = NULL;  		}  	}  	mutex_unlock(hdl->lock); +  	if (!ref)  		return -EINVAL; @@ -2203,6 +2534,14 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc)  	else  		qc->id = ctrl->id;  	strlcpy(qc->name, ctrl->name, sizeof(qc->name)); +	qc->flags = ctrl->flags; +	qc->type = ctrl->type; +	if (ctrl->is_ptr) +		qc->flags |= V4L2_CTRL_FLAG_HAS_PAYLOAD; +	qc->elem_size = ctrl->elem_size; +	qc->elems = ctrl->elems; +	qc->nr_of_dims = ctrl->nr_of_dims; +	memcpy(qc->dims, ctrl->dims, qc->nr_of_dims * sizeof(qc->dims[0]));  	qc->minimum = ctrl->minimum;  	qc->maximum = ctrl->maximum;  	qc->default_value = ctrl->default_value; @@ -2211,15 +2550,50 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc)  		qc->step = 1;  	else  		qc->step = ctrl->step; -	qc->flags = ctrl->flags; -	qc->type = ctrl->type; +	return 0; +} +EXPORT_SYMBOL(v4l2_query_ext_ctrl); + +/* Implement VIDIOC_QUERYCTRL */ +int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) +{ +	struct v4l2_query_ext_ctrl qec = { qc->id }; +	int rc; + +	rc = v4l2_query_ext_ctrl(hdl, &qec); +	if (rc) +		return rc; + +	qc->id = qec.id; +	qc->type = qec.type; +	qc->flags = qec.flags; +	strlcpy(qc->name, qec.name, sizeof(qc->name)); +	switch (qc->type) { +	case V4L2_CTRL_TYPE_INTEGER: +	case V4L2_CTRL_TYPE_BOOLEAN: +	case V4L2_CTRL_TYPE_MENU: +	case V4L2_CTRL_TYPE_INTEGER_MENU: +	case V4L2_CTRL_TYPE_STRING: +	case V4L2_CTRL_TYPE_BITMASK: +		qc->minimum = qec.minimum; +		qc->maximum = qec.maximum; +		qc->step = qec.step; +		qc->default_value = qec.default_value; +		break; +	default: +		qc->minimum = 0; +		qc->maximum = 0; +		qc->step = 0; +		qc->default_value = 0; +		break; +	}  	return 0;  }  EXPORT_SYMBOL(v4l2_queryctrl);  int v4l2_subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)  { -	if (qc->id & V4L2_CTRL_FLAG_NEXT_CTRL) +	if (qc->id & (V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND))  		return -EINVAL;  	return v4l2_queryctrl(sd->ctrl_handler, qc);  } @@ -2319,7 +2693,8 @@ EXPORT_SYMBOL(v4l2_subdev_querymenu);     Find the controls in the control array and do some basic checks. */  static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,  			     struct v4l2_ext_controls *cs, -			     struct v4l2_ctrl_helper *helpers) +			     struct v4l2_ctrl_helper *helpers, +			     bool get)  {  	struct v4l2_ctrl_helper *h;  	bool have_clusters = false; @@ -2351,6 +2726,18 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,  			have_clusters = true;  		if (ctrl->cluster[0] != ctrl)  			ref = find_ref_lock(hdl, ctrl->cluster[0]->id); +		if (ctrl->is_ptr && !ctrl->is_string) { +			unsigned tot_size = ctrl->elems * ctrl->elem_size; + +			if (c->size < tot_size) { +				if (get) { +					c->size = tot_size; +					return -ENOSPC; +				} +				return -EFAULT; +			} +			c->size = tot_size; +		}  		/* Store the ref to the master control of the cluster */  		h->mref = ref;  		h->ctrl = ctrl; @@ -2431,7 +2818,7 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs  			return -ENOMEM;  	} -	ret = prepare_ext_ctrls(hdl, cs, helpers); +	ret = prepare_ext_ctrls(hdl, cs, helpers, true);  	cs->error_idx = cs->count;  	for (i = 0; !ret && i < cs->count; i++) @@ -2493,11 +2880,11 @@ static int get_ctrl(struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c)  	int ret = 0;  	int i; -	/* String controls are not supported. The new_to_user() and +	/* Compound controls are not supported. The new_to_user() and  	 * cur_to_user() calls below would need to be modified not to access  	 * userspace memory when called from get_ctrl().  	 */ -	if (ctrl->type == V4L2_CTRL_TYPE_STRING) +	if (!ctrl->is_int)  		return -EINVAL;  	if (ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY) @@ -2523,7 +2910,7 @@ int v4l2_g_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *control)  	struct v4l2_ext_control c;  	int ret; -	if (ctrl == NULL || !type_is_int(ctrl)) +	if (ctrl == NULL || !ctrl->is_int)  		return -EINVAL;  	ret = get_ctrl(ctrl, &c);  	control->value = c.value; @@ -2542,7 +2929,7 @@ s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl)  	struct v4l2_ext_control c;  	/* It's a driver bug if this happens. */ -	WARN_ON(!type_is_int(ctrl)); +	WARN_ON(!ctrl->is_int);  	c.value = 0;  	get_ctrl(ctrl, &c);  	return c.value; @@ -2554,7 +2941,7 @@ s64 v4l2_ctrl_g_ctrl_int64(struct v4l2_ctrl *ctrl)  	struct v4l2_ext_control c;  	/* It's a driver bug if this happens. */ -	WARN_ON(ctrl->type != V4L2_CTRL_TYPE_INTEGER64); +	WARN_ON(ctrl->is_ptr || ctrl->type != V4L2_CTRL_TYPE_INTEGER64);  	c.value = 0;  	get_ctrl(ctrl, &c);  	return c.value; @@ -2678,7 +3065,7 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,  		if (!helpers)  			return -ENOMEM;  	} -	ret = prepare_ext_ctrls(hdl, cs, helpers); +	ret = prepare_ext_ctrls(hdl, cs, helpers, false);  	if (!ret)  		ret = validate_ctrls(cs, helpers, set);  	if (ret && set) @@ -2783,26 +3170,22 @@ static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl,  	struct v4l2_ctrl *master = ctrl->cluster[0];  	int i; -	/* String controls are not supported. The user_to_new() and -	 * cur_to_user() calls below would need to be modified not to access -	 * userspace memory when called from set_ctrl(). -	 */ -	if (ctrl->type == V4L2_CTRL_TYPE_STRING) -		return -EINVAL; -  	/* Reset the 'is_new' flags of the cluster */  	for (i = 0; i < master->ncontrols; i++)  		if (master->cluster[i])  			master->cluster[i]->is_new = 0; +	if (c) +		user_to_new(c, ctrl); +  	/* For autoclusters with volatiles that are switched from auto to  	   manual mode we have to update the current volatile values since  	   those will become the initial manual values after such a switch. */  	if (master->is_auto && master->has_volatiles && ctrl == master && -	    !is_cur_manual(master) && c->value == master->manual_mode_value) +	    !is_cur_manual(master) && ctrl->val == master->manual_mode_value)  		update_from_auto_cluster(master); -	user_to_new(c, ctrl); +	ctrl->is_new = 1;  	return try_or_set_cluster(fh, master, true, ch_flags);  } @@ -2829,7 +3212,7 @@ int v4l2_s_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,  	struct v4l2_ext_control c;  	int ret; -	if (ctrl == NULL || !type_is_int(ctrl)) +	if (ctrl == NULL || !ctrl->is_int)  		return -EINVAL;  	if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY) @@ -2848,27 +3231,38 @@ int v4l2_subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control)  }  EXPORT_SYMBOL(v4l2_subdev_s_ctrl); -int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val) +int __v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val)  { -	struct v4l2_ext_control c; +	lockdep_assert_held(ctrl->handler->lock);  	/* It's a driver bug if this happens. */ -	WARN_ON(!type_is_int(ctrl)); -	c.value = val; -	return set_ctrl_lock(NULL, ctrl, &c); +	WARN_ON(!ctrl->is_int); +	ctrl->val = val; +	return set_ctrl(NULL, ctrl, NULL, 0);  } -EXPORT_SYMBOL(v4l2_ctrl_s_ctrl); +EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl); -int v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val) +int __v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val)  { -	struct v4l2_ext_control c; +	lockdep_assert_held(ctrl->handler->lock);  	/* It's a driver bug if this happens. */ -	WARN_ON(ctrl->type != V4L2_CTRL_TYPE_INTEGER64); -	c.value64 = val; -	return set_ctrl_lock(NULL, ctrl, &c); +	WARN_ON(ctrl->is_ptr || ctrl->type != V4L2_CTRL_TYPE_INTEGER64); +	*ctrl->p_new.p_s64 = val; +	return set_ctrl(NULL, ctrl, NULL, 0);  } -EXPORT_SYMBOL(v4l2_ctrl_s_ctrl_int64); +EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_int64); + +int __v4l2_ctrl_s_ctrl_string(struct v4l2_ctrl *ctrl, const char *s) +{ +	lockdep_assert_held(ctrl->handler->lock); + +	/* It's a driver bug if this happens. */ +	WARN_ON(ctrl->type != V4L2_CTRL_TYPE_STRING); +	strlcpy(ctrl->p_new.p_char, s, ctrl->maximum + 1); +	return set_ctrl(NULL, ctrl, NULL, 0); +} +EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_string);  void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void *priv)  { @@ -2886,40 +3280,47 @@ void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void  }  EXPORT_SYMBOL(v4l2_ctrl_notify); -int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, -			s32 min, s32 max, u32 step, s32 def) +int __v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, +			s64 min, s64 max, u64 step, s64 def)  { -	int ret = check_range(ctrl->type, min, max, step, def); +	int ret;  	struct v4l2_ext_control c; +	lockdep_assert_held(ctrl->handler->lock); +  	switch (ctrl->type) {  	case V4L2_CTRL_TYPE_INTEGER: +	case V4L2_CTRL_TYPE_INTEGER64:  	case V4L2_CTRL_TYPE_BOOLEAN:  	case V4L2_CTRL_TYPE_MENU:  	case V4L2_CTRL_TYPE_INTEGER_MENU:  	case V4L2_CTRL_TYPE_BITMASK: +	case V4L2_CTRL_TYPE_U8: +	case V4L2_CTRL_TYPE_U16: +	case V4L2_CTRL_TYPE_U32: +		if (ctrl->is_array) +			return -EINVAL; +		ret = check_range(ctrl->type, min, max, step, def);  		if (ret)  			return ret;  		break;  	default:  		return -EINVAL;  	} -	v4l2_ctrl_lock(ctrl);  	ctrl->minimum = min;  	ctrl->maximum = max;  	ctrl->step = step;  	ctrl->default_value = def; -	c.value = ctrl->cur.val; +	c.value = *ctrl->p_cur.p_s32;  	if (validate_new(ctrl, &c))  		c.value = def; -	if (c.value != ctrl->cur.val) +	if (c.value != *ctrl->p_cur.p_s32)  		ret = set_ctrl(NULL, ctrl, &c, V4L2_EVENT_CTRL_CH_RANGE);  	else  		send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_RANGE); -	v4l2_ctrl_unlock(ctrl);  	return ret;  } -EXPORT_SYMBOL(v4l2_ctrl_modify_range); +EXPORT_SYMBOL(__v4l2_ctrl_modify_range);  static int v4l2_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems)  {  |