|
@@ -1021,19 +1021,57 @@ int uvc_ctrl_set(struct uvc_video_chain *chain,
|
|
|
{
|
|
|
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 @@ int uvc_ctrl_set(struct uvc_video_chain *chain,
|
|
|
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),
|