Hi Linh Could you share the "lsusb -v " info of your device, and also what are the raw values from the device for UVC_GET_MAX, UVC_GET_MIN for UVC_CT_PANTILT_RELATIVE_CONTROL and UVC_CT_ZOOM_RELATIVE_CONTROL ? Something like this (not tested :) ): diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index e59a463c27618..76c7adc3aa579 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -415,7 +415,11 @@ 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: + printk(KERN_ERR "zoom: GET_MIN 0%x 0x%x 0%x\n", data[0], data[1], data[2]); + return data[2]; case UVC_GET_MAX: + printk(KERN_ERR "zoom: GET_MAX 0%x 0x%x 0%x\n", data[0], data[1], data[2]); + fallthrough; case UVC_GET_RES: case UVC_GET_DEF: default: @@ -441,8 +445,11 @@ static s32 uvc_ctrl_get_rel_speed(struct uvc_control_mapping *mapping, return (rel == 0) ? 0 : (rel > 0 ? data[first+1] : -data[first+1]); case UVC_GET_MIN: + printk(KERN_ERR "speed: GET_MIN 0%x 0x%x 0%x\n", data[first], data[first+1], data[first+2]); return -data[first+1]; case UVC_GET_MAX: + printk(KERN_ERR "speed: GET_MAX 0%x 0x%x 0%x\n", data[first], data[first+1], data[first+2]); + fallthrough; case UVC_GET_RES: case UVC_GET_DEF: default: Thanks! On Tue, 19 Mar 2024 at 06:30, Linh Vu <linh.tp.vu@xxxxxxxxx> wrote: > > 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 > > -- Ricardo Ribalda