diff options
author | Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 2010-01-23 05:07:53 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2010-02-26 13:11:04 -0500 |
commit | e54532e591cd5b9ce77dbc8d9786ae9f600f101a (patch) | |
tree | 725c07637e0197acd3176827299d4186962221e4 | |
parent | 59529081e092506edb81a42d914e2d0522f65ca7 (diff) |
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>
-rw-r--r-- | drivers/media/video/uvc/uvc_ctrl.c | 47 | ||||
-rw-r--r-- | drivers/media/video/uvc/uvc_v4l2.c | 2 |
2 files changed, 45 insertions, 4 deletions
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 5ff501390842..f38bc6bbc3d2 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c | |||
@@ -1021,19 +1021,57 @@ int uvc_ctrl_set(struct uvc_video_chain *chain, | |||
1021 | { | 1021 | { |
1022 | struct uvc_control *ctrl; | 1022 | struct uvc_control *ctrl; |
1023 | struct uvc_control_mapping *mapping; | 1023 | struct uvc_control_mapping *mapping; |
1024 | s32 value = xctrl->value; | 1024 | s32 value; |
1025 | u32 step; | ||
1026 | s32 min; | ||
1027 | s32 max; | ||
1025 | int ret; | 1028 | int ret; |
1026 | 1029 | ||
1027 | ctrl = uvc_find_control(chain, xctrl->id, &mapping); | 1030 | ctrl = uvc_find_control(chain, xctrl->id, &mapping); |
1028 | if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_SET_CUR) == 0) | 1031 | if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_SET_CUR) == 0) |
1029 | return -EINVAL; | 1032 | return -EINVAL; |
1030 | 1033 | ||
1031 | if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { | 1034 | /* Clamp out of range values. */ |
1032 | if (value < 0 || value >= mapping->menu_count) | 1035 | switch (mapping->v4l2_type) { |
1036 | case V4L2_CTRL_TYPE_INTEGER: | ||
1037 | if (!ctrl->cached) { | ||
1038 | ret = uvc_ctrl_populate_cache(chain, ctrl); | ||
1039 | if (ret < 0) | ||
1040 | return ret; | ||
1041 | } | ||
1042 | |||
1043 | min = mapping->get(mapping, UVC_GET_MIN, | ||
1044 | uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN)); | ||
1045 | max = mapping->get(mapping, UVC_GET_MAX, | ||
1046 | uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX)); | ||
1047 | step = mapping->get(mapping, UVC_GET_RES, | ||
1048 | uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES)); | ||
1049 | |||
1050 | xctrl->value = min + (xctrl->value - min + step/2) / step * step; | ||
1051 | xctrl->value = clamp(xctrl->value, min, max); | ||
1052 | value = xctrl->value; | ||
1053 | break; | ||
1054 | |||
1055 | case V4L2_CTRL_TYPE_BOOLEAN: | ||
1056 | xctrl->value = clamp(xctrl->value, 0, 1); | ||
1057 | value = xctrl->value; | ||
1058 | break; | ||
1059 | |||
1060 | case V4L2_CTRL_TYPE_MENU: | ||
1061 | if (xctrl->value < 0 || xctrl->value >= mapping->menu_count) | ||
1033 | return -ERANGE; | 1062 | return -ERANGE; |
1034 | value = mapping->menu_info[value].value; | 1063 | value = mapping->menu_info[xctrl->value].value; |
1064 | break; | ||
1065 | |||
1066 | default: | ||
1067 | value = xctrl->value; | ||
1068 | break; | ||
1035 | } | 1069 | } |
1036 | 1070 | ||
1071 | /* If the mapping doesn't span the whole UVC control, the current value | ||
1072 | * needs to be loaded from the device to perform the read-modify-write | ||
1073 | * operation. | ||
1074 | */ | ||
1037 | if (!ctrl->loaded && (ctrl->info->size * 8) != mapping->size) { | 1075 | if (!ctrl->loaded && (ctrl->info->size * 8) != mapping->size) { |
1038 | if ((ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0) { | 1076 | if ((ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0) { |
1039 | memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), | 1077 | memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), |
@@ -1051,6 +1089,7 @@ int uvc_ctrl_set(struct uvc_video_chain *chain, | |||
1051 | ctrl->loaded = 1; | 1089 | ctrl->loaded = 1; |
1052 | } | 1090 | } |
1053 | 1091 | ||
1092 | /* Backup the current value in case we need to rollback later. */ | ||
1054 | if (!ctrl->dirty) { | 1093 | if (!ctrl->dirty) { |
1055 | memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), | 1094 | memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), |
1056 | uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), | 1095 | uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), |
diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index 23239a4adefe..bf1fc7f29ca7 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c | |||
@@ -549,6 +549,8 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) | |||
549 | return ret; | 549 | return ret; |
550 | } | 550 | } |
551 | ret = uvc_ctrl_commit(chain); | 551 | ret = uvc_ctrl_commit(chain); |
552 | if (ret == 0) | ||
553 | ctrl->value = xctrl.value; | ||
552 | break; | 554 | break; |
553 | } | 555 | } |
554 | 556 | ||