V4L/DVB: uvcvideo: Clamp control values to the minimum and maximum values

When setting a control, the V4L2 specification requires drivers to
either clamp the control value to the [minimum, maximum] range or return
the -ERANGE error.

Fix the driver to clamp control values to the valid range in
uvc_ctrl_set() and make sure the value differs from the minimum by an
integer multiple of step.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Tested-by: Márton Németh <nm127@freemail.hu>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c
index 5ff5013..f38bc6b 100644
--- a/drivers/media/video/uvc/uvc_ctrl.c
+++ b/drivers/media/video/uvc/uvc_ctrl.c
@@ -1021,19 +1021,57 @@
 {
 	struct uvc_control *ctrl;
 	struct uvc_control_mapping *mapping;
-	s32 value = xctrl->value;
+	s32 value;
+	u32 step;
+	s32 min;
+	s32 max;
 	int ret;
 
 	ctrl = uvc_find_control(chain, xctrl->id, &mapping);
 	if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_SET_CUR) == 0)
 		return -EINVAL;
 
-	if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) {
-		if (value < 0 || value >= mapping->menu_count)
+	/* Clamp out of range values. */
+	switch (mapping->v4l2_type) {
+	case V4L2_CTRL_TYPE_INTEGER:
+		if (!ctrl->cached) {
+			ret = uvc_ctrl_populate_cache(chain, ctrl);
+			if (ret < 0)
+				return ret;
+		}
+
+		min = mapping->get(mapping, UVC_GET_MIN,
+				   uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));
+		max = mapping->get(mapping, UVC_GET_MAX,
+				   uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));
+		step = mapping->get(mapping, UVC_GET_RES,
+				    uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
+
+		xctrl->value = min + (xctrl->value - min + step/2) / step * step;
+		xctrl->value = clamp(xctrl->value, min, max);
+		value = xctrl->value;
+		break;
+
+	case V4L2_CTRL_TYPE_BOOLEAN:
+		xctrl->value = clamp(xctrl->value, 0, 1);
+		value = xctrl->value;
+		break;
+
+	case V4L2_CTRL_TYPE_MENU:
+		if (xctrl->value < 0 || xctrl->value >= mapping->menu_count)
 			return -ERANGE;
-		value = mapping->menu_info[value].value;
+		value = mapping->menu_info[xctrl->value].value;
+		break;
+
+	default:
+		value = xctrl->value;
+		break;
 	}
 
+	/* If the mapping doesn't span the whole UVC control, the current value
+	 * needs to be loaded from the device to perform the read-modify-write
+	 * operation.
+	 */
 	if (!ctrl->loaded && (ctrl->info->size * 8) != mapping->size) {
 		if ((ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0) {
 			memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
@@ -1051,6 +1089,7 @@
 		ctrl->loaded = 1;
 	}
 
+	/* Backup the current value in case we need to rollback later. */
 	if (!ctrl->dirty) {
 		memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
 		       uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),