When mapping from UVC_CT_PANTILT_RELATIVE_CONTROL to V4L2_CID_PAN_SPEED and V4L2_CID_TILT_SPEED, and from UVC_CT_ZOOM_RELATIVE_CONTROL to V4L2_CID_ZOOM_CONTINUOUS, the minimum value of the movement should be negated of the maximum value. For example, if a UVC device (e.g., OBSBOT Tiny 2) declares a pan speed range [1, 160], its V4L2_CID_PAN_SPEED mapping has range [-160, 160]. Currently, calling ioctl with VIDIOC_QUERY_EXT_CTRL and V4L2_CID_PAN_SPEED returns a minimum value of -1. When calling ioctl with VIDIOC_S_CTRL, V4L2_CID_PAN_SPEED and -100, the speed (velocity) of the pan movement gets clamped to -1. To get the minimum value of V4L2_CID_PAN_SPEED, uvc_ctrl_get_rel_speed is called with uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN) as data, which should be uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX). The same thing should be done for V4L2_CID_TILT_SPEED and V4L2_CID_ZOOM_CONTINUOUS. For V4L2_CID_ZOOM_CONTINUOUS, uvc_ctrl_get_zoom does not add the sign to the returned minimum value, so it's impossible to zoom out. Modify the data that is passed when querying the minimum value for V4L2_CID_PAN_SPEED, V4L2_CID_TILT_SPEED and V4L2_CID_ZOOM_CONTINUOUS. Also add sign to the returned minimum value in uvc_ctrl_get_zoom. Thus, the correct minimum value for relative PTZ controls can be returned. Signed-off-by: Linh Vu <linh.tp.vu@xxxxxxxxx> --- drivers/media/usb/uvc/uvc_ctrl.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index e59a463c27618..00fd7e74e6d6b 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -415,6 +415,7 @@ static s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping, return (zoom == 0) ? 0 : (zoom > 0 ? data[2] : -data[2]); case UVC_GET_MIN: + return -data[2]; case UVC_GET_MAX: case UVC_GET_RES: case UVC_GET_DEF: @@ -1322,9 +1323,16 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, break; } - if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN) - v4l2_ctrl->minimum = mapping->get(mapping, UVC_GET_MIN, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN)); + if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN) { + if (v4l2_ctrl->id == V4L2_CID_PAN_SPEED + || v4l2_ctrl->id == V4L2_CID_TILT_SPEED + || v4l2_ctrl->id == V4L2_CID_ZOOM_CONTINUOUS) + v4l2_ctrl->minimum = mapping->get(mapping, UVC_GET_MIN, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX)); + else + v4l2_ctrl->minimum = mapping->get(mapping, UVC_GET_MIN, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN)); + } if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX) v4l2_ctrl->maximum = mapping->get(mapping, UVC_GET_MAX, @@ -1909,9 +1917,15 @@ int uvc_ctrl_set(struct uvc_fh *handle, if (ret < 0) return ret; } + if (mapping->id == V4L2_CID_PAN_SPEED + || mapping->id == V4L2_CID_TILT_SPEED + || mapping->id == V4L2_CID_ZOOM_CONTINUOUS) + min = mapping->get(mapping, UVC_GET_MIN, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX)); + else + min = mapping->get(mapping, UVC_GET_MIN, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN)); - 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, base-commit: e0b8eb0f6d652981bfd9ba7c619c9d81ed087ad0 -- 2.34.1