Virtual entities need to provide more values than get_cur and get_cur
for their controls. Add support for get_def, get_min, get_max and
get_res.
This is a preparation patch.
Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
---
drivers/media/usb/uvc/uvc_ctrl.c | 12 ++++++++++++
drivers/media/usb/uvc/uvcvideo.h | 8 ++++++++
2 files changed, 20 insertions(+)
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 21ec7b978bc7aca21db7cb8fd5d135d876f3330c..59be62ae24a4219fa9d7aacf2ae7382c95362178 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -596,6 +596,18 @@ static int uvc_ctrl_query_entity(struct uvc_device *dev,
if (query == UVC_GET_CUR && ctrl->entity->get_cur)
return ctrl->entity->get_cur(dev, ctrl->entity,
ctrl->info.selector, data, len);
+ if (query == UVC_GET_DEF && ctrl->entity->get_def)
+ return ctrl->entity->get_def(dev, ctrl->entity,
+ ctrl->info.selector, data, len);
+ if (query == UVC_GET_MIN && ctrl->entity->get_min)
+ return ctrl->entity->get_min(dev, ctrl->entity,
+ ctrl->info.selector, data, len);
+ if (query == UVC_GET_MAX && ctrl->entity->get_max)
+ return ctrl->entity->get_max(dev, ctrl->entity,
+ ctrl->info.selector, data, len);
+ if (query == UVC_GET_RES && ctrl->entity->get_res)
+ return ctrl->entity->get_res(dev, ctrl->entity,
+ ctrl->info.selector, data, len);
if (query == UVC_GET_INFO && ctrl->entity->get_info)
return ctrl->entity->get_info(dev, ctrl->entity,
ctrl->info.selector, data);
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index a931750bdea25b9062dcc7644bf5f2ed89c1cb4c..d6da8ed3ad4cf3377df49923e051fe04d83d2e38 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -261,6 +261,14 @@ struct uvc_entity {
u8 cs, u8 *caps);
int (*get_cur)(struct uvc_device *dev, struct uvc_entity *entity,
u8 cs, void *data, u16 size);
+ int (*get_def)(struct uvc_device *dev, struct uvc_entity *entity,
+ u8 cs, void *data, u16 size);
+ int (*get_min)(struct uvc_device *dev, struct uvc_entity *entity,
+ u8 cs, void *data, u16 size);
+ int (*get_max)(struct uvc_device *dev, struct uvc_entity *entity,
+ u8 cs, void *data, u16 size);
+ int (*get_res)(struct uvc_device *dev, struct uvc_entity *entity,
+ u8 cs, void *data, u16 size);
unsigned int ncontrols;
struct uvc_control *controls;
--
2.50.0.rc0.642.g800a2b2222-goog
Hi Ricardo,
Thank you for the patch.
On Thu, Jun 05, 2025 at 05:53:03PM +0000, Ricardo Ribalda wrote:
> Virtual entities need to provide more values than get_cur and get_cur
I think you meant "get_info and get_cur".
> for their controls. Add support for get_def, get_min, get_max and
> get_res.
Do they ? The UVC specification defines controls that don't list
GET_DEF, GET_MIN, GET_MAX and GET_RES as mandatory requests. Can't we do
the same for the software controls ? This patch is meant to support the
UVC_SWENTITY_ORIENTATION and UVC_SWENTITY_ROTATION control in the next
patch, and those are read-only controls. Aren't GET_INFO and GET_CUR
enough ?
>
> This is a preparation patch.
>
> Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
> ---
> drivers/media/usb/uvc/uvc_ctrl.c | 12 ++++++++++++
> drivers/media/usb/uvc/uvcvideo.h | 8 ++++++++
> 2 files changed, 20 insertions(+)
>
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index 21ec7b978bc7aca21db7cb8fd5d135d876f3330c..59be62ae24a4219fa9d7aacf2ae7382c95362178 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -596,6 +596,18 @@ static int uvc_ctrl_query_entity(struct uvc_device *dev,
> if (query == UVC_GET_CUR && ctrl->entity->get_cur)
> return ctrl->entity->get_cur(dev, ctrl->entity,
> ctrl->info.selector, data, len);
> + if (query == UVC_GET_DEF && ctrl->entity->get_def)
> + return ctrl->entity->get_def(dev, ctrl->entity,
> + ctrl->info.selector, data, len);
> + if (query == UVC_GET_MIN && ctrl->entity->get_min)
> + return ctrl->entity->get_min(dev, ctrl->entity,
> + ctrl->info.selector, data, len);
> + if (query == UVC_GET_MAX && ctrl->entity->get_max)
> + return ctrl->entity->get_max(dev, ctrl->entity,
> + ctrl->info.selector, data, len);
> + if (query == UVC_GET_RES && ctrl->entity->get_res)
> + return ctrl->entity->get_res(dev, ctrl->entity,
> + ctrl->info.selector, data, len);
> if (query == UVC_GET_INFO && ctrl->entity->get_info)
> return ctrl->entity->get_info(dev, ctrl->entity,
> ctrl->info.selector, data);
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index a931750bdea25b9062dcc7644bf5f2ed89c1cb4c..d6da8ed3ad4cf3377df49923e051fe04d83d2e38 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -261,6 +261,14 @@ struct uvc_entity {
> u8 cs, u8 *caps);
> int (*get_cur)(struct uvc_device *dev, struct uvc_entity *entity,
> u8 cs, void *data, u16 size);
> + int (*get_def)(struct uvc_device *dev, struct uvc_entity *entity,
> + u8 cs, void *data, u16 size);
> + int (*get_min)(struct uvc_device *dev, struct uvc_entity *entity,
> + u8 cs, void *data, u16 size);
> + int (*get_max)(struct uvc_device *dev, struct uvc_entity *entity,
> + u8 cs, void *data, u16 size);
> + int (*get_res)(struct uvc_device *dev, struct uvc_entity *entity,
> + u8 cs, void *data, u16 size);
>
> unsigned int ncontrols;
> struct uvc_control *controls;
--
Regards,
Laurent Pinchart
Hi Laurent
On Sun, 29 Jun 2025 at 20:13, Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Hi Ricardo,
>
> Thank you for the patch.
>
> On Thu, Jun 05, 2025 at 05:53:03PM +0000, Ricardo Ribalda wrote:
> > Virtual entities need to provide more values than get_cur and get_cur
>
> I think you meant "get_info and get_cur".
>
> > for their controls. Add support for get_def, get_min, get_max and
> > get_res.
>
> Do they ? The UVC specification defines controls that don't list
> GET_DEF, GET_MIN, GET_MAX and GET_RES as mandatory requests. Can't we do
> the same for the software controls ? This patch is meant to support the
> UVC_SWENTITY_ORIENTATION and UVC_SWENTITY_ROTATION control in the next
> patch, and those are read-only controls. Aren't GET_INFO and GET_CUR
> enough ?
V4L2_CID_CAMERA_ROTATION has the type UVC_CTRL_DATA_TYPE_UNSIGNED,
that time requires get_min and get_max.
We can create a new type UVC_CTRL_DATA_TYPE_UNSIGNED_READ_ONLY that
fakes min, max and res, but I think that it is cleaner this approach.
>
> >
> > This is a preparation patch.
> >
> > Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
> > ---
> > drivers/media/usb/uvc/uvc_ctrl.c | 12 ++++++++++++
> > drivers/media/usb/uvc/uvcvideo.h | 8 ++++++++
> > 2 files changed, 20 insertions(+)
> >
> > diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> > index 21ec7b978bc7aca21db7cb8fd5d135d876f3330c..59be62ae24a4219fa9d7aacf2ae7382c95362178 100644
> > --- a/drivers/media/usb/uvc/uvc_ctrl.c
> > +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> > @@ -596,6 +596,18 @@ static int uvc_ctrl_query_entity(struct uvc_device *dev,
> > if (query == UVC_GET_CUR && ctrl->entity->get_cur)
> > return ctrl->entity->get_cur(dev, ctrl->entity,
> > ctrl->info.selector, data, len);
> > + if (query == UVC_GET_DEF && ctrl->entity->get_def)
> > + return ctrl->entity->get_def(dev, ctrl->entity,
> > + ctrl->info.selector, data, len);
> > + if (query == UVC_GET_MIN && ctrl->entity->get_min)
> > + return ctrl->entity->get_min(dev, ctrl->entity,
> > + ctrl->info.selector, data, len);
> > + if (query == UVC_GET_MAX && ctrl->entity->get_max)
> > + return ctrl->entity->get_max(dev, ctrl->entity,
> > + ctrl->info.selector, data, len);
> > + if (query == UVC_GET_RES && ctrl->entity->get_res)
> > + return ctrl->entity->get_res(dev, ctrl->entity,
> > + ctrl->info.selector, data, len);
> > if (query == UVC_GET_INFO && ctrl->entity->get_info)
> > return ctrl->entity->get_info(dev, ctrl->entity,
> > ctrl->info.selector, data);
> > diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> > index a931750bdea25b9062dcc7644bf5f2ed89c1cb4c..d6da8ed3ad4cf3377df49923e051fe04d83d2e38 100644
> > --- a/drivers/media/usb/uvc/uvcvideo.h
> > +++ b/drivers/media/usb/uvc/uvcvideo.h
> > @@ -261,6 +261,14 @@ struct uvc_entity {
> > u8 cs, u8 *caps);
> > int (*get_cur)(struct uvc_device *dev, struct uvc_entity *entity,
> > u8 cs, void *data, u16 size);
> > + int (*get_def)(struct uvc_device *dev, struct uvc_entity *entity,
> > + u8 cs, void *data, u16 size);
> > + int (*get_min)(struct uvc_device *dev, struct uvc_entity *entity,
> > + u8 cs, void *data, u16 size);
> > + int (*get_max)(struct uvc_device *dev, struct uvc_entity *entity,
> > + u8 cs, void *data, u16 size);
> > + int (*get_res)(struct uvc_device *dev, struct uvc_entity *entity,
> > + u8 cs, void *data, u16 size);
> >
> > unsigned int ncontrols;
> > struct uvc_control *controls;
>
> --
> Regards,
>
> Laurent Pinchart
--
Ricardo Ribalda
On Tue, Jul 01, 2025 at 01:13:10PM +0200, Ricardo Ribalda wrote:
> On Sun, 29 Jun 2025 at 20:13, Laurent Pinchart wrote:
> > On Thu, Jun 05, 2025 at 05:53:03PM +0000, Ricardo Ribalda wrote:
> > > Virtual entities need to provide more values than get_cur and get_cur
> >
> > I think you meant "get_info and get_cur".
> >
> > > for their controls. Add support for get_def, get_min, get_max and
> > > get_res.
> >
> > Do they ? The UVC specification defines controls that don't list
> > GET_DEF, GET_MIN, GET_MAX and GET_RES as mandatory requests. Can't we do
> > the same for the software controls ? This patch is meant to support the
> > UVC_SWENTITY_ORIENTATION and UVC_SWENTITY_ROTATION control in the next
> > patch, and those are read-only controls. Aren't GET_INFO and GET_CUR
> > enough ?
>
> V4L2_CID_CAMERA_ROTATION has the type UVC_CTRL_DATA_TYPE_UNSIGNED,
> that time requires get_min and get_max.
Where does that requirement come from ? Is it because how the
corresponding V4L2 type (V4L2_CTRL_TYPE_INTEGER) is handled in
uvc_ctrl_clamp() ? uvc_ctrl_clamp() is only called when setting a
control, from uvc_ctrl_set(), and V4L2_CID_CAMERA_ROTATION should be
read-only.
> We can create a new type UVC_CTRL_DATA_TYPE_UNSIGNED_READ_ONLY that
> fakes min, max and res, but I think that it is cleaner this approach.
>
> > > This is a preparation patch.
> > >
> > > Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
> > > ---
> > > drivers/media/usb/uvc/uvc_ctrl.c | 12 ++++++++++++
> > > drivers/media/usb/uvc/uvcvideo.h | 8 ++++++++
> > > 2 files changed, 20 insertions(+)
> > >
> > > diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> > > index 21ec7b978bc7aca21db7cb8fd5d135d876f3330c..59be62ae24a4219fa9d7aacf2ae7382c95362178 100644
> > > --- a/drivers/media/usb/uvc/uvc_ctrl.c
> > > +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> > > @@ -596,6 +596,18 @@ static int uvc_ctrl_query_entity(struct uvc_device *dev,
> > > if (query == UVC_GET_CUR && ctrl->entity->get_cur)
> > > return ctrl->entity->get_cur(dev, ctrl->entity,
> > > ctrl->info.selector, data, len);
> > > + if (query == UVC_GET_DEF && ctrl->entity->get_def)
> > > + return ctrl->entity->get_def(dev, ctrl->entity,
> > > + ctrl->info.selector, data, len);
> > > + if (query == UVC_GET_MIN && ctrl->entity->get_min)
> > > + return ctrl->entity->get_min(dev, ctrl->entity,
> > > + ctrl->info.selector, data, len);
> > > + if (query == UVC_GET_MAX && ctrl->entity->get_max)
> > > + return ctrl->entity->get_max(dev, ctrl->entity,
> > > + ctrl->info.selector, data, len);
> > > + if (query == UVC_GET_RES && ctrl->entity->get_res)
> > > + return ctrl->entity->get_res(dev, ctrl->entity,
> > > + ctrl->info.selector, data, len);
> > > if (query == UVC_GET_INFO && ctrl->entity->get_info)
> > > return ctrl->entity->get_info(dev, ctrl->entity,
> > > ctrl->info.selector, data);
> > > diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> > > index a931750bdea25b9062dcc7644bf5f2ed89c1cb4c..d6da8ed3ad4cf3377df49923e051fe04d83d2e38 100644
> > > --- a/drivers/media/usb/uvc/uvcvideo.h
> > > +++ b/drivers/media/usb/uvc/uvcvideo.h
> > > @@ -261,6 +261,14 @@ struct uvc_entity {
> > > u8 cs, u8 *caps);
> > > int (*get_cur)(struct uvc_device *dev, struct uvc_entity *entity,
> > > u8 cs, void *data, u16 size);
> > > + int (*get_def)(struct uvc_device *dev, struct uvc_entity *entity,
> > > + u8 cs, void *data, u16 size);
> > > + int (*get_min)(struct uvc_device *dev, struct uvc_entity *entity,
> > > + u8 cs, void *data, u16 size);
> > > + int (*get_max)(struct uvc_device *dev, struct uvc_entity *entity,
> > > + u8 cs, void *data, u16 size);
> > > + int (*get_res)(struct uvc_device *dev, struct uvc_entity *entity,
> > > + u8 cs, void *data, u16 size);
> > >
> > > unsigned int ncontrols;
> > > struct uvc_control *controls;
--
Regards,
Laurent Pinchart
Hi Laurent
On Mon, 14 Jul 2025 at 16:30, Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> On Tue, Jul 01, 2025 at 01:13:10PM +0200, Ricardo Ribalda wrote:
> > On Sun, 29 Jun 2025 at 20:13, Laurent Pinchart wrote:
> > > On Thu, Jun 05, 2025 at 05:53:03PM +0000, Ricardo Ribalda wrote:
> > > > Virtual entities need to provide more values than get_cur and get_cur
> > >
> > > I think you meant "get_info and get_cur".
> > >
> > > > for their controls. Add support for get_def, get_min, get_max and
> > > > get_res.
> > >
> > > Do they ? The UVC specification defines controls that don't list
> > > GET_DEF, GET_MIN, GET_MAX and GET_RES as mandatory requests. Can't we do
> > > the same for the software controls ? This patch is meant to support the
> > > UVC_SWENTITY_ORIENTATION and UVC_SWENTITY_ROTATION control in the next
> > > patch, and those are read-only controls. Aren't GET_INFO and GET_CUR
> > > enough ?
> >
> > V4L2_CID_CAMERA_ROTATION has the type UVC_CTRL_DATA_TYPE_UNSIGNED,
> > that time requires get_min and get_max.
>
> Where does that requirement come from ? Is it because how the
> corresponding V4L2 type (V4L2_CTRL_TYPE_INTEGER) is handled in
> uvc_ctrl_clamp() ? uvc_ctrl_clamp() is only called when setting a
> control, from uvc_ctrl_set(), and V4L2_CID_CAMERA_ROTATION should be
> read-only.
It its for VIDIOC_QUERY_EXT_CTRL
uvc_query_v4l2_ctrl -> __uvc_query_v4l2_ctrl -> __uvc_queryctrl_boundaries
We need to list the min, max, def and step for every control. They are
fetched with uvc_ctrl_populate_cache()
>
> > We can create a new type UVC_CTRL_DATA_TYPE_UNSIGNED_READ_ONLY that
> > fakes min, max and res, but I think that it is cleaner this approach.
> >
> > > > This is a preparation patch.
> > > >
> > > > Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
> > > > ---
> > > > drivers/media/usb/uvc/uvc_ctrl.c | 12 ++++++++++++
> > > > drivers/media/usb/uvc/uvcvideo.h | 8 ++++++++
> > > > 2 files changed, 20 insertions(+)
> > > >
> > > > diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> > > > index 21ec7b978bc7aca21db7cb8fd5d135d876f3330c..59be62ae24a4219fa9d7aacf2ae7382c95362178 100644
> > > > --- a/drivers/media/usb/uvc/uvc_ctrl.c
> > > > +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> > > > @@ -596,6 +596,18 @@ static int uvc_ctrl_query_entity(struct uvc_device *dev,
> > > > if (query == UVC_GET_CUR && ctrl->entity->get_cur)
> > > > return ctrl->entity->get_cur(dev, ctrl->entity,
> > > > ctrl->info.selector, data, len);
> > > > + if (query == UVC_GET_DEF && ctrl->entity->get_def)
> > > > + return ctrl->entity->get_def(dev, ctrl->entity,
> > > > + ctrl->info.selector, data, len);
> > > > + if (query == UVC_GET_MIN && ctrl->entity->get_min)
> > > > + return ctrl->entity->get_min(dev, ctrl->entity,
> > > > + ctrl->info.selector, data, len);
> > > > + if (query == UVC_GET_MAX && ctrl->entity->get_max)
> > > > + return ctrl->entity->get_max(dev, ctrl->entity,
> > > > + ctrl->info.selector, data, len);
> > > > + if (query == UVC_GET_RES && ctrl->entity->get_res)
> > > > + return ctrl->entity->get_res(dev, ctrl->entity,
> > > > + ctrl->info.selector, data, len);
> > > > if (query == UVC_GET_INFO && ctrl->entity->get_info)
> > > > return ctrl->entity->get_info(dev, ctrl->entity,
> > > > ctrl->info.selector, data);
> > > > diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> > > > index a931750bdea25b9062dcc7644bf5f2ed89c1cb4c..d6da8ed3ad4cf3377df49923e051fe04d83d2e38 100644
> > > > --- a/drivers/media/usb/uvc/uvcvideo.h
> > > > +++ b/drivers/media/usb/uvc/uvcvideo.h
> > > > @@ -261,6 +261,14 @@ struct uvc_entity {
> > > > u8 cs, u8 *caps);
> > > > int (*get_cur)(struct uvc_device *dev, struct uvc_entity *entity,
> > > > u8 cs, void *data, u16 size);
> > > > + int (*get_def)(struct uvc_device *dev, struct uvc_entity *entity,
> > > > + u8 cs, void *data, u16 size);
> > > > + int (*get_min)(struct uvc_device *dev, struct uvc_entity *entity,
> > > > + u8 cs, void *data, u16 size);
> > > > + int (*get_max)(struct uvc_device *dev, struct uvc_entity *entity,
> > > > + u8 cs, void *data, u16 size);
> > > > + int (*get_res)(struct uvc_device *dev, struct uvc_entity *entity,
> > > > + u8 cs, void *data, u16 size);
> > > >
> > > > unsigned int ncontrols;
> > > > struct uvc_control *controls;
>
> --
> Regards,
>
> Laurent Pinchart
--
Ricardo Ribalda
On Mon, Jul 14, 2025 at 05:46:40PM +0200, Ricardo Ribalda wrote:
> On Mon, 14 Jul 2025 at 16:30, Laurent Pinchart wrote:
> > On Tue, Jul 01, 2025 at 01:13:10PM +0200, Ricardo Ribalda wrote:
> > > On Sun, 29 Jun 2025 at 20:13, Laurent Pinchart wrote:
> > > > On Thu, Jun 05, 2025 at 05:53:03PM +0000, Ricardo Ribalda wrote:
> > > > > Virtual entities need to provide more values than get_cur and get_cur
> > > >
> > > > I think you meant "get_info and get_cur".
> > > >
> > > > > for their controls. Add support for get_def, get_min, get_max and
> > > > > get_res.
> > > >
> > > > Do they ? The UVC specification defines controls that don't list
> > > > GET_DEF, GET_MIN, GET_MAX and GET_RES as mandatory requests. Can't we do
> > > > the same for the software controls ? This patch is meant to support the
> > > > UVC_SWENTITY_ORIENTATION and UVC_SWENTITY_ROTATION control in the next
> > > > patch, and those are read-only controls. Aren't GET_INFO and GET_CUR
> > > > enough ?
> > >
> > > V4L2_CID_CAMERA_ROTATION has the type UVC_CTRL_DATA_TYPE_UNSIGNED,
> > > that time requires get_min and get_max.
> >
> > Where does that requirement come from ? Is it because how the
> > corresponding V4L2 type (V4L2_CTRL_TYPE_INTEGER) is handled in
> > uvc_ctrl_clamp() ? uvc_ctrl_clamp() is only called when setting a
> > control, from uvc_ctrl_set(), and V4L2_CID_CAMERA_ROTATION should be
> > read-only.
>
> It its for VIDIOC_QUERY_EXT_CTRL
>
> uvc_query_v4l2_ctrl -> __uvc_query_v4l2_ctrl -> __uvc_queryctrl_boundaries
>
> We need to list the min, max, def and step for every control. They are
> fetched with uvc_ctrl_populate_cache()
Ah, I see, thanks.
For GET_RES, I think we can leave it unimplemented.
__uvc_queryctrl_boundaries() will set v4l2_ctrl->step = 0 which seems to
be the right behaviour for a read-only control whose value never
changes.
As for the minimum and maximum, they are currently set to 0 if the
corresponding operations are not supported. I wonder if we should set
them to the current value instead for read-only controls (as in controls
whose flags report support for GET_CUR only)..
> > > We can create a new type UVC_CTRL_DATA_TYPE_UNSIGNED_READ_ONLY that
> > > fakes min, max and res, but I think that it is cleaner this approach.
> > >
> > > > > This is a preparation patch.
> > > > >
> > > > > Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
> > > > > ---
> > > > > drivers/media/usb/uvc/uvc_ctrl.c | 12 ++++++++++++
> > > > > drivers/media/usb/uvc/uvcvideo.h | 8 ++++++++
> > > > > 2 files changed, 20 insertions(+)
> > > > >
> > > > > diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> > > > > index 21ec7b978bc7aca21db7cb8fd5d135d876f3330c..59be62ae24a4219fa9d7aacf2ae7382c95362178 100644
> > > > > --- a/drivers/media/usb/uvc/uvc_ctrl.c
> > > > > +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> > > > > @@ -596,6 +596,18 @@ static int uvc_ctrl_query_entity(struct uvc_device *dev,
> > > > > if (query == UVC_GET_CUR && ctrl->entity->get_cur)
> > > > > return ctrl->entity->get_cur(dev, ctrl->entity,
> > > > > ctrl->info.selector, data, len);
> > > > > + if (query == UVC_GET_DEF && ctrl->entity->get_def)
> > > > > + return ctrl->entity->get_def(dev, ctrl->entity,
> > > > > + ctrl->info.selector, data, len);
> > > > > + if (query == UVC_GET_MIN && ctrl->entity->get_min)
> > > > > + return ctrl->entity->get_min(dev, ctrl->entity,
> > > > > + ctrl->info.selector, data, len);
> > > > > + if (query == UVC_GET_MAX && ctrl->entity->get_max)
> > > > > + return ctrl->entity->get_max(dev, ctrl->entity,
> > > > > + ctrl->info.selector, data, len);
> > > > > + if (query == UVC_GET_RES && ctrl->entity->get_res)
> > > > > + return ctrl->entity->get_res(dev, ctrl->entity,
> > > > > + ctrl->info.selector, data, len);
> > > > > if (query == UVC_GET_INFO && ctrl->entity->get_info)
> > > > > return ctrl->entity->get_info(dev, ctrl->entity,
> > > > > ctrl->info.selector, data);
> > > > > diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> > > > > index a931750bdea25b9062dcc7644bf5f2ed89c1cb4c..d6da8ed3ad4cf3377df49923e051fe04d83d2e38 100644
> > > > > --- a/drivers/media/usb/uvc/uvcvideo.h
> > > > > +++ b/drivers/media/usb/uvc/uvcvideo.h
> > > > > @@ -261,6 +261,14 @@ struct uvc_entity {
> > > > > u8 cs, u8 *caps);
> > > > > int (*get_cur)(struct uvc_device *dev, struct uvc_entity *entity,
> > > > > u8 cs, void *data, u16 size);
> > > > > + int (*get_def)(struct uvc_device *dev, struct uvc_entity *entity,
> > > > > + u8 cs, void *data, u16 size);
> > > > > + int (*get_min)(struct uvc_device *dev, struct uvc_entity *entity,
> > > > > + u8 cs, void *data, u16 size);
> > > > > + int (*get_max)(struct uvc_device *dev, struct uvc_entity *entity,
> > > > > + u8 cs, void *data, u16 size);
> > > > > + int (*get_res)(struct uvc_device *dev, struct uvc_entity *entity,
> > > > > + u8 cs, void *data, u16 size);
> > > > >
> > > > > unsigned int ncontrols;
> > > > > struct uvc_control *controls;
--
Regards,
Laurent Pinchart
On Tue, 15 Jul 2025 at 21:35, Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> On Mon, Jul 14, 2025 at 05:46:40PM +0200, Ricardo Ribalda wrote:
> > On Mon, 14 Jul 2025 at 16:30, Laurent Pinchart wrote:
> > > On Tue, Jul 01, 2025 at 01:13:10PM +0200, Ricardo Ribalda wrote:
> > > > On Sun, 29 Jun 2025 at 20:13, Laurent Pinchart wrote:
> > > > > On Thu, Jun 05, 2025 at 05:53:03PM +0000, Ricardo Ribalda wrote:
> > > > > > Virtual entities need to provide more values than get_cur and get_cur
> > > > >
> > > > > I think you meant "get_info and get_cur".
> > > > >
> > > > > > for their controls. Add support for get_def, get_min, get_max and
> > > > > > get_res.
> > > > >
> > > > > Do they ? The UVC specification defines controls that don't list
> > > > > GET_DEF, GET_MIN, GET_MAX and GET_RES as mandatory requests. Can't we do
> > > > > the same for the software controls ? This patch is meant to support the
> > > > > UVC_SWENTITY_ORIENTATION and UVC_SWENTITY_ROTATION control in the next
> > > > > patch, and those are read-only controls. Aren't GET_INFO and GET_CUR
> > > > > enough ?
> > > >
> > > > V4L2_CID_CAMERA_ROTATION has the type UVC_CTRL_DATA_TYPE_UNSIGNED,
> > > > that time requires get_min and get_max.
> > >
> > > Where does that requirement come from ? Is it because how the
> > > corresponding V4L2 type (V4L2_CTRL_TYPE_INTEGER) is handled in
> > > uvc_ctrl_clamp() ? uvc_ctrl_clamp() is only called when setting a
> > > control, from uvc_ctrl_set(), and V4L2_CID_CAMERA_ROTATION should be
> > > read-only.
> >
> > It its for VIDIOC_QUERY_EXT_CTRL
> >
> > uvc_query_v4l2_ctrl -> __uvc_query_v4l2_ctrl -> __uvc_queryctrl_boundaries
> >
> > We need to list the min, max, def and step for every control. They are
> > fetched with uvc_ctrl_populate_cache()
>
> Ah, I see, thanks.
>
> For GET_RES, I think we can leave it unimplemented.
> __uvc_queryctrl_boundaries() will set v4l2_ctrl->step = 0 which seems to
> be the right behaviour for a read-only control whose value never
> changes.
That will break v4l2-compatiblity. Step needs to be != 0
https://git.linuxtv.org/v4l-utils.git/tree/utils/v4l2-compliance/v4l2-test-controls.cpp#n77
Control ioctls (Input 0):
fail: v4l2-test-controls.cpp(77): step == 0
fail: v4l2-test-controls.cpp(201): invalid control 009a0923
>
> As for the minimum and maximum, they are currently set to 0 if the
> corresponding operations are not supported. I wonder if we should set
> them to the current value instead for read-only controls (as in controls
> whose flags report support for GET_CUR only)..
I am not sure that I like that approach IMO the code looks worse...
but if you prefer that, we can go that way
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index ec472e111248..47224437018b 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -35,6 +35,8 @@
/* ------------------------------------------------------------------------
* Controls
*/
+static int __uvc_ctrl_load_cur(struct uvc_video_chain *chain,
+ struct uvc_control *ctrl);
static const struct uvc_control_info uvc_ctrls[] = {
{
@@ -1272,6 +1274,13 @@ static int uvc_ctrl_populate_cache(struct
uvc_video_chain *chain,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF));
if (ret < 0)
return ret;
+ } else if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR)) {
+ ret = __uvc_ctrl_load_cur(chain, ctrl);
+ if (!ret) {
+ memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF),
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
+ ctrl->info.size);
+ }
}
if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN) {
@@ -1279,14 +1288,31 @@ static int uvc_ctrl_populate_cache(struct
uvc_video_chain *chain,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));
if (ret < 0)
return ret;
+ } else if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR)) {
+ ret = __uvc_ctrl_load_cur(chain, ctrl);
+ if (!ret) {
+ memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN),
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
+ ctrl->info.size);
+ }
}
+
if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX) {
ret = uvc_ctrl_query_entity(chain->dev, ctrl, UVC_GET_MAX,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));
if (ret < 0)
return ret;
+ } else if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR)) {
+ ret = __uvc_ctrl_load_cur(chain, ctrl);
+ if (!ret) {
+ memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX),
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
+ ctrl->info.size);
+ }
}
+
if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES) {
+ u8 *res;
ret = uvc_ctrl_query_entity(chain->dev, ctrl, UVC_GET_RES,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
if (ret < 0) {
@@ -1304,7 +1330,13 @@ static int uvc_ctrl_populate_cache(struct
uvc_video_chain *chain,
"an XU control. Enabling workaround.\n");
memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES), 0,
ctrl->info.size);
+ res = uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES);
+ *res = 1
}
+ } else {
+ memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES), 0,
ctrl->info.size);
+ res = uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES);
+ *res = 1;
}
ctrl->cached = 1;
@@ -1541,11 +1573,8 @@ static int __uvc_queryctrl_boundaries(struct
uvc_video_chain *chain,
return ret;
}
- if (ctrl->info.flags & UVC_CTRL_FLAG_GET_DEF)
v4l2_ctrl->default_value = uvc_mapping_get_s32(mapping,
UVC_GET_DEF, uvc_ctrl_data(ctrl,
UVC_CTRL_DATA_DEF));
- else
- v4l2_ctrl->default_value = 0;
switch (mapping->v4l2_type) {
case V4L2_CTRL_TYPE_MENU:
@@ -1576,23 +1605,14 @@ static int __uvc_queryctrl_boundaries(struct
uvc_video_chain *chain,
break;
}
- if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN)
- v4l2_ctrl->minimum = uvc_mapping_get_s32(mapping, UVC_GET_MIN,
- uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));
- else
- v4l2_ctrl->minimum = 0;
+ v4l2_ctrl->minimum = uvc_mapping_get_s32(mapping, UVC_GET_MIN,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));
- if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX)
- v4l2_ctrl->maximum = uvc_mapping_get_s32(mapping, UVC_GET_MAX,
- uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));
- else
- v4l2_ctrl->maximum = 0;
+ v4l2_ctrl->maximum = uvc_mapping_get_s32(mapping, UVC_GET_MAX,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));
- if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)
- v4l2_ctrl->step = uvc_mapping_get_s32(mapping, UVC_GET_RES,
- uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
- else
- v4l2_ctrl->step = 0;
+ v4l2_ctrl->step = uvc_mapping_get_s32(mapping, UVC_GET_RES,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
return 0;
}
>
> > > > We can create a new type UVC_CTRL_DATA_TYPE_UNSIGNED_READ_ONLY that
> > > > fakes min, max and res, but I think that it is cleaner this approach.
> > > >
> > > > > > This is a preparation patch.
> > > > > >
> > > > > > Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
> > > > > > ---
> > > > > > drivers/media/usb/uvc/uvc_ctrl.c | 12 ++++++++++++
> > > > > > drivers/media/usb/uvc/uvcvideo.h | 8 ++++++++
> > > > > > 2 files changed, 20 insertions(+)
> > > > > >
> > > > > > diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> > > > > > index 21ec7b978bc7aca21db7cb8fd5d135d876f3330c..59be62ae24a4219fa9d7aacf2ae7382c95362178 100644
> > > > > > --- a/drivers/media/usb/uvc/uvc_ctrl.c
> > > > > > +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> > > > > > @@ -596,6 +596,18 @@ static int uvc_ctrl_query_entity(struct uvc_device *dev,
> > > > > > if (query == UVC_GET_CUR && ctrl->entity->get_cur)
> > > > > > return ctrl->entity->get_cur(dev, ctrl->entity,
> > > > > > ctrl->info.selector, data, len);
> > > > > > + if (query == UVC_GET_DEF && ctrl->entity->get_def)
> > > > > > + return ctrl->entity->get_def(dev, ctrl->entity,
> > > > > > + ctrl->info.selector, data, len);
> > > > > > + if (query == UVC_GET_MIN && ctrl->entity->get_min)
> > > > > > + return ctrl->entity->get_min(dev, ctrl->entity,
> > > > > > + ctrl->info.selector, data, len);
> > > > > > + if (query == UVC_GET_MAX && ctrl->entity->get_max)
> > > > > > + return ctrl->entity->get_max(dev, ctrl->entity,
> > > > > > + ctrl->info.selector, data, len);
> > > > > > + if (query == UVC_GET_RES && ctrl->entity->get_res)
> > > > > > + return ctrl->entity->get_res(dev, ctrl->entity,
> > > > > > + ctrl->info.selector, data, len);
> > > > > > if (query == UVC_GET_INFO && ctrl->entity->get_info)
> > > > > > return ctrl->entity->get_info(dev, ctrl->entity,
> > > > > > ctrl->info.selector, data);
> > > > > > diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> > > > > > index a931750bdea25b9062dcc7644bf5f2ed89c1cb4c..d6da8ed3ad4cf3377df49923e051fe04d83d2e38 100644
> > > > > > --- a/drivers/media/usb/uvc/uvcvideo.h
> > > > > > +++ b/drivers/media/usb/uvc/uvcvideo.h
> > > > > > @@ -261,6 +261,14 @@ struct uvc_entity {
> > > > > > u8 cs, u8 *caps);
> > > > > > int (*get_cur)(struct uvc_device *dev, struct uvc_entity *entity,
> > > > > > u8 cs, void *data, u16 size);
> > > > > > + int (*get_def)(struct uvc_device *dev, struct uvc_entity *entity,
> > > > > > + u8 cs, void *data, u16 size);
> > > > > > + int (*get_min)(struct uvc_device *dev, struct uvc_entity *entity,
> > > > > > + u8 cs, void *data, u16 size);
> > > > > > + int (*get_max)(struct uvc_device *dev, struct uvc_entity *entity,
> > > > > > + u8 cs, void *data, u16 size);
> > > > > > + int (*get_res)(struct uvc_device *dev, struct uvc_entity *entity,
> > > > > > + u8 cs, void *data, u16 size);
> > > > > >
> > > > > > unsigned int ncontrols;
> > > > > > struct uvc_control *controls;
>
> --
> Regards,
>
> Laurent Pinchart
--
Ricardo Ribalda
Hi,
On 16-Jul-25 12:32, Ricardo Ribalda wrote:
> On Tue, 15 Jul 2025 at 21:35, Laurent Pinchart
> <laurent.pinchart@ideasonboard.com> wrote:
...
>> As for the minimum and maximum, they are currently set to 0 if the
>> corresponding operations are not supported. I wonder if we should set
>> them to the current value instead for read-only controls (as in controls
>> whose flags report support for GET_CUR only)..
>
> I am not sure that I like that approach IMO the code looks worse...
> but if you prefer that, we can go that way
>
>
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index ec472e111248..47224437018b 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -35,6 +35,8 @@
> /* ------------------------------------------------------------------------
> * Controls
> */
> +static int __uvc_ctrl_load_cur(struct uvc_video_chain *chain,
> + struct uvc_control *ctrl);
>
> static const struct uvc_control_info uvc_ctrls[] = {
> {
> @@ -1272,6 +1274,13 @@ static int uvc_ctrl_populate_cache(struct
> uvc_video_chain *chain,
> uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF));
> if (ret < 0)
> return ret;
> + } else if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR)) {
> + ret = __uvc_ctrl_load_cur(chain, ctrl);
> + if (!ret) {
> + memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF),
> + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
> + ctrl->info.size);
> + }
> }
>
> if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN) {
Interesting change. Note you also need to check for
(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) being true,
__uvc_ctrl_load_cur() will return a 0 filled buffer
and success if that is not set.
I wonder why not do something like this instead though:
if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR) &&
(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) &&
__uvc_ctrl_load_cur(chain, ctrl) == 0) {
/* Read-only control, set def / min / max to cur */
memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF),
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
ctrl->info.size);
memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN),
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
ctrl->info.size);
memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX),
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
ctrl->info.size);
}
IOW why bother to make the GET_DEF, etc. calls at all for a
read-only control (even if they are supported) ?
Generally speaking making less calls into the hw seems better?
Although maybe replace the:
if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR) &&
(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) &&
part of the check with a flag in ctrl->info indicating to do
this and do this for specific controls like the new
rotation and orientation controls ?
...
> @@ -1541,11 +1573,8 @@ static int __uvc_queryctrl_boundaries(struct
> uvc_video_chain *chain,
> return ret;
> }
>
> - if (ctrl->info.flags & UVC_CTRL_FLAG_GET_DEF)
> v4l2_ctrl->default_value = uvc_mapping_get_s32(mapping,
> UVC_GET_DEF, uvc_ctrl_data(ctrl,
> UVC_CTRL_DATA_DEF));
> - else
> - v4l2_ctrl->default_value = 0;
>
> switch (mapping->v4l2_type) {
> case V4L2_CTRL_TYPE_MENU:
> @@ -1576,23 +1605,14 @@ static int __uvc_queryctrl_boundaries(struct
> uvc_video_chain *chain,
> break;
> }
>
> - if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN)
> - v4l2_ctrl->minimum = uvc_mapping_get_s32(mapping, UVC_GET_MIN,
> - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));
> - else
> - v4l2_ctrl->minimum = 0;
> + v4l2_ctrl->minimum = uvc_mapping_get_s32(mapping, UVC_GET_MIN,
> + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));
>
> - if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX)
> - v4l2_ctrl->maximum = uvc_mapping_get_s32(mapping, UVC_GET_MAX,
> - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));
> - else
> - v4l2_ctrl->maximum = 0;
> + v4l2_ctrl->maximum = uvc_mapping_get_s32(mapping, UVC_GET_MAX,
> + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));
>
> - if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)
> - v4l2_ctrl->step = uvc_mapping_get_s32(mapping, UVC_GET_RES,
> - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
> - else
> - v4l2_ctrl->step = 0;
> + v4l2_ctrl->step = uvc_mapping_get_s32(mapping, UVC_GET_RES,
> + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
>
> return 0;
> }
I agree with Laurent that thee changes are nice, but please split them into
a separate patch.
Regards,
Hans
Hi Laurent
On Wed, 16 Jul 2025 at 12:32, Ricardo Ribalda <ribalda@chromium.org> wrote:
>
> On Tue, 15 Jul 2025 at 21:35, Laurent Pinchart
> <laurent.pinchart@ideasonboard.com> wrote:
> >
> > On Mon, Jul 14, 2025 at 05:46:40PM +0200, Ricardo Ribalda wrote:
> > > On Mon, 14 Jul 2025 at 16:30, Laurent Pinchart wrote:
> > > > On Tue, Jul 01, 2025 at 01:13:10PM +0200, Ricardo Ribalda wrote:
> > > > > On Sun, 29 Jun 2025 at 20:13, Laurent Pinchart wrote:
> > > > > > On Thu, Jun 05, 2025 at 05:53:03PM +0000, Ricardo Ribalda wrote:
> > > > > > > Virtual entities need to provide more values than get_cur and get_cur
> > > > > >
> > > > > > I think you meant "get_info and get_cur".
> > > > > >
> > > > > > > for their controls. Add support for get_def, get_min, get_max and
> > > > > > > get_res.
> > > > > >
> > > > > > Do they ? The UVC specification defines controls that don't list
> > > > > > GET_DEF, GET_MIN, GET_MAX and GET_RES as mandatory requests. Can't we do
> > > > > > the same for the software controls ? This patch is meant to support the
> > > > > > UVC_SWENTITY_ORIENTATION and UVC_SWENTITY_ROTATION control in the next
> > > > > > patch, and those are read-only controls. Aren't GET_INFO and GET_CUR
> > > > > > enough ?
> > > > >
> > > > > V4L2_CID_CAMERA_ROTATION has the type UVC_CTRL_DATA_TYPE_UNSIGNED,
> > > > > that time requires get_min and get_max.
> > > >
> > > > Where does that requirement come from ? Is it because how the
> > > > corresponding V4L2 type (V4L2_CTRL_TYPE_INTEGER) is handled in
> > > > uvc_ctrl_clamp() ? uvc_ctrl_clamp() is only called when setting a
> > > > control, from uvc_ctrl_set(), and V4L2_CID_CAMERA_ROTATION should be
> > > > read-only.
> > >
> > > It its for VIDIOC_QUERY_EXT_CTRL
> > >
> > > uvc_query_v4l2_ctrl -> __uvc_query_v4l2_ctrl -> __uvc_queryctrl_boundaries
> > >
> > > We need to list the min, max, def and step for every control. They are
> > > fetched with uvc_ctrl_populate_cache()
> >
> > Ah, I see, thanks.
> >
> > For GET_RES, I think we can leave it unimplemented.
> > __uvc_queryctrl_boundaries() will set v4l2_ctrl->step = 0 which seems to
> > be the right behaviour for a read-only control whose value never
> > changes.
>
> That will break v4l2-compatiblity. Step needs to be != 0
> https://git.linuxtv.org/v4l-utils.git/tree/utils/v4l2-compliance/v4l2-test-controls.cpp#n77
>
> Control ioctls (Input 0):
> fail: v4l2-test-controls.cpp(77): step == 0
> fail: v4l2-test-controls.cpp(201): invalid control 009a0923
>
> >
> > As for the minimum and maximum, they are currently set to 0 if the
> > corresponding operations are not supported. I wonder if we should set
> > them to the current value instead for read-only controls (as in controls
> > whose flags report support for GET_CUR only)..
>
> I am not sure that I like that approach IMO the code looks worse...
> but if you prefer that, we can go that way
I am almost ready to send a new version.
What approach do you prefer?
Regards!
>
>
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index ec472e111248..47224437018b 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -35,6 +35,8 @@
> /* ------------------------------------------------------------------------
> * Controls
> */
> +static int __uvc_ctrl_load_cur(struct uvc_video_chain *chain,
> + struct uvc_control *ctrl);
>
> static const struct uvc_control_info uvc_ctrls[] = {
> {
> @@ -1272,6 +1274,13 @@ static int uvc_ctrl_populate_cache(struct
> uvc_video_chain *chain,
> uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF));
> if (ret < 0)
> return ret;
> + } else if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR)) {
> + ret = __uvc_ctrl_load_cur(chain, ctrl);
> + if (!ret) {
> + memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF),
> + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
> + ctrl->info.size);
> + }
> }
>
> if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN) {
> @@ -1279,14 +1288,31 @@ static int uvc_ctrl_populate_cache(struct
> uvc_video_chain *chain,
> uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));
> if (ret < 0)
> return ret;
> + } else if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR)) {
> + ret = __uvc_ctrl_load_cur(chain, ctrl);
> + if (!ret) {
> + memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN),
> + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
> + ctrl->info.size);
> + }
> }
> +
> if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX) {
> ret = uvc_ctrl_query_entity(chain->dev, ctrl, UVC_GET_MAX,
> uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));
> if (ret < 0)
> return ret;
> + } else if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR)) {
> + ret = __uvc_ctrl_load_cur(chain, ctrl);
> + if (!ret) {
> + memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX),
> + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
> + ctrl->info.size);
> + }
> }
> +
> if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES) {
> + u8 *res;
> ret = uvc_ctrl_query_entity(chain->dev, ctrl, UVC_GET_RES,
> uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
> if (ret < 0) {
> @@ -1304,7 +1330,13 @@ static int uvc_ctrl_populate_cache(struct
> uvc_video_chain *chain,
> "an XU control. Enabling workaround.\n");
> memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES), 0,
> ctrl->info.size);
> + res = uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES);
> + *res = 1
> }
> + } else {
> + memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES), 0,
> ctrl->info.size);
> + res = uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES);
> + *res = 1;
> }
>
> ctrl->cached = 1;
> @@ -1541,11 +1573,8 @@ static int __uvc_queryctrl_boundaries(struct
> uvc_video_chain *chain,
> return ret;
> }
>
> - if (ctrl->info.flags & UVC_CTRL_FLAG_GET_DEF)
> v4l2_ctrl->default_value = uvc_mapping_get_s32(mapping,
> UVC_GET_DEF, uvc_ctrl_data(ctrl,
> UVC_CTRL_DATA_DEF));
> - else
> - v4l2_ctrl->default_value = 0;
>
> switch (mapping->v4l2_type) {
> case V4L2_CTRL_TYPE_MENU:
> @@ -1576,23 +1605,14 @@ static int __uvc_queryctrl_boundaries(struct
> uvc_video_chain *chain,
> break;
> }
>
> - if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN)
> - v4l2_ctrl->minimum = uvc_mapping_get_s32(mapping, UVC_GET_MIN,
> - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));
> - else
> - v4l2_ctrl->minimum = 0;
> + v4l2_ctrl->minimum = uvc_mapping_get_s32(mapping, UVC_GET_MIN,
> + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));
>
> - if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX)
> - v4l2_ctrl->maximum = uvc_mapping_get_s32(mapping, UVC_GET_MAX,
> - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));
> - else
> - v4l2_ctrl->maximum = 0;
> + v4l2_ctrl->maximum = uvc_mapping_get_s32(mapping, UVC_GET_MAX,
> + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));
>
> - if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)
> - v4l2_ctrl->step = uvc_mapping_get_s32(mapping, UVC_GET_RES,
> - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
> - else
> - v4l2_ctrl->step = 0;
> + v4l2_ctrl->step = uvc_mapping_get_s32(mapping, UVC_GET_RES,
> + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
>
> return 0;
> }
>
> >
> > > > > We can create a new type UVC_CTRL_DATA_TYPE_UNSIGNED_READ_ONLY that
> > > > > fakes min, max and res, but I think that it is cleaner this approach.
> > > > >
> > > > > > > This is a preparation patch.
> > > > > > >
> > > > > > > Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
> > > > > > > ---
> > > > > > > drivers/media/usb/uvc/uvc_ctrl.c | 12 ++++++++++++
> > > > > > > drivers/media/usb/uvc/uvcvideo.h | 8 ++++++++
> > > > > > > 2 files changed, 20 insertions(+)
> > > > > > >
> > > > > > > diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> > > > > > > index 21ec7b978bc7aca21db7cb8fd5d135d876f3330c..59be62ae24a4219fa9d7aacf2ae7382c95362178 100644
> > > > > > > --- a/drivers/media/usb/uvc/uvc_ctrl.c
> > > > > > > +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> > > > > > > @@ -596,6 +596,18 @@ static int uvc_ctrl_query_entity(struct uvc_device *dev,
> > > > > > > if (query == UVC_GET_CUR && ctrl->entity->get_cur)
> > > > > > > return ctrl->entity->get_cur(dev, ctrl->entity,
> > > > > > > ctrl->info.selector, data, len);
> > > > > > > + if (query == UVC_GET_DEF && ctrl->entity->get_def)
> > > > > > > + return ctrl->entity->get_def(dev, ctrl->entity,
> > > > > > > + ctrl->info.selector, data, len);
> > > > > > > + if (query == UVC_GET_MIN && ctrl->entity->get_min)
> > > > > > > + return ctrl->entity->get_min(dev, ctrl->entity,
> > > > > > > + ctrl->info.selector, data, len);
> > > > > > > + if (query == UVC_GET_MAX && ctrl->entity->get_max)
> > > > > > > + return ctrl->entity->get_max(dev, ctrl->entity,
> > > > > > > + ctrl->info.selector, data, len);
> > > > > > > + if (query == UVC_GET_RES && ctrl->entity->get_res)
> > > > > > > + return ctrl->entity->get_res(dev, ctrl->entity,
> > > > > > > + ctrl->info.selector, data, len);
> > > > > > > if (query == UVC_GET_INFO && ctrl->entity->get_info)
> > > > > > > return ctrl->entity->get_info(dev, ctrl->entity,
> > > > > > > ctrl->info.selector, data);
> > > > > > > diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> > > > > > > index a931750bdea25b9062dcc7644bf5f2ed89c1cb4c..d6da8ed3ad4cf3377df49923e051fe04d83d2e38 100644
> > > > > > > --- a/drivers/media/usb/uvc/uvcvideo.h
> > > > > > > +++ b/drivers/media/usb/uvc/uvcvideo.h
> > > > > > > @@ -261,6 +261,14 @@ struct uvc_entity {
> > > > > > > u8 cs, u8 *caps);
> > > > > > > int (*get_cur)(struct uvc_device *dev, struct uvc_entity *entity,
> > > > > > > u8 cs, void *data, u16 size);
> > > > > > > + int (*get_def)(struct uvc_device *dev, struct uvc_entity *entity,
> > > > > > > + u8 cs, void *data, u16 size);
> > > > > > > + int (*get_min)(struct uvc_device *dev, struct uvc_entity *entity,
> > > > > > > + u8 cs, void *data, u16 size);
> > > > > > > + int (*get_max)(struct uvc_device *dev, struct uvc_entity *entity,
> > > > > > > + u8 cs, void *data, u16 size);
> > > > > > > + int (*get_res)(struct uvc_device *dev, struct uvc_entity *entity,
> > > > > > > + u8 cs, void *data, u16 size);
> > > > > > >
> > > > > > > unsigned int ncontrols;
> > > > > > > struct uvc_control *controls;
> >
> > --
> > Regards,
> >
> > Laurent Pinchart
>
>
>
> --
> Ricardo Ribalda
--
Ricardo Ribalda
A question for Hans Verkuil below.
On Thu, Aug 07, 2025 at 09:35:14AM +0200, Ricardo Ribalda wrote:
> On Wed, 16 Jul 2025 at 12:32, Ricardo Ribalda wrote:
> > On Tue, 15 Jul 2025 at 21:35, Laurent Pinchart wrote:
> > > On Mon, Jul 14, 2025 at 05:46:40PM +0200, Ricardo Ribalda wrote:
> > > > On Mon, 14 Jul 2025 at 16:30, Laurent Pinchart wrote:
> > > > > On Tue, Jul 01, 2025 at 01:13:10PM +0200, Ricardo Ribalda wrote:
> > > > > > On Sun, 29 Jun 2025 at 20:13, Laurent Pinchart wrote:
> > > > > > > On Thu, Jun 05, 2025 at 05:53:03PM +0000, Ricardo Ribalda wrote:
> > > > > > > > Virtual entities need to provide more values than get_cur and get_cur
> > > > > > >
> > > > > > > I think you meant "get_info and get_cur".
> > > > > > >
> > > > > > > > for their controls. Add support for get_def, get_min, get_max and
> > > > > > > > get_res.
> > > > > > >
> > > > > > > Do they ? The UVC specification defines controls that don't list
> > > > > > > GET_DEF, GET_MIN, GET_MAX and GET_RES as mandatory requests. Can't we do
> > > > > > > the same for the software controls ? This patch is meant to support the
> > > > > > > UVC_SWENTITY_ORIENTATION and UVC_SWENTITY_ROTATION control in the next
> > > > > > > patch, and those are read-only controls. Aren't GET_INFO and GET_CUR
> > > > > > > enough ?
> > > > > >
> > > > > > V4L2_CID_CAMERA_ROTATION has the type UVC_CTRL_DATA_TYPE_UNSIGNED,
> > > > > > that time requires get_min and get_max.
> > > > >
> > > > > Where does that requirement come from ? Is it because how the
> > > > > corresponding V4L2 type (V4L2_CTRL_TYPE_INTEGER) is handled in
> > > > > uvc_ctrl_clamp() ? uvc_ctrl_clamp() is only called when setting a
> > > > > control, from uvc_ctrl_set(), and V4L2_CID_CAMERA_ROTATION should be
> > > > > read-only.
> > > >
> > > > It its for VIDIOC_QUERY_EXT_CTRL
> > > >
> > > > uvc_query_v4l2_ctrl -> __uvc_query_v4l2_ctrl -> __uvc_queryctrl_boundaries
> > > >
> > > > We need to list the min, max, def and step for every control. They are
> > > > fetched with uvc_ctrl_populate_cache()
> > >
> > > Ah, I see, thanks.
> > >
> > > For GET_RES, I think we can leave it unimplemented.
> > > __uvc_queryctrl_boundaries() will set v4l2_ctrl->step = 0 which seems to
> > > be the right behaviour for a read-only control whose value never
> > > changes.
> >
> > That will break v4l2-compatiblity. Step needs to be != 0
> > https://git.linuxtv.org/v4l-utils.git/tree/utils/v4l2-compliance/v4l2-test-controls.cpp#n77
> >
> > Control ioctls (Input 0):
> > fail: v4l2-test-controls.cpp(77): step == 0
> > fail: v4l2-test-controls.cpp(201): invalid control 009a0923
Is that an issue in v4l2-compliance ? For integer controls,
https://docs.kernel.org/userspace-api/media/v4l/vidioc-queryctrl.html#c.V4L.v4l2_ctrl_type
documents the step value as "any". For a read-only control whose value
is constant, do we want to enforce a non-zero value ? If so we should
update the specification.
Hans, what's your opinion ?
In any case, if GET_RES isn't implemented, we could update
__uvc_queryctrl_boundaries() to set step to 1 instead of 0. That would
fix v4l2-compliance for real controls that don't implement GET_RES.
> > > As for the minimum and maximum, they are currently set to 0 if the
> > > corresponding operations are not supported. I wonder if we should set
> > > them to the current value instead for read-only controls (as in controls
> > > whose flags report support for GET_CUR only)..
> >
> > I am not sure that I like that approach IMO the code looks worse...
> > but if you prefer that, we can go that way
>
> I am almost ready to send a new version.
>
> What approach do you prefer?
I particularly like the change in __uvc_queryctrl_boundaries(). That
could probably be done without the rest of the changes though, as
ctrl->uvc_data is already allocated with kzalloc().
I also like the fact that the driver can rely on the min/max values to
always be populated in the control data. This could be useful for real
read-only UVC controls.
Thinking a bit more about this, for read-only controls whose value never
changes, min, max and step are meaningless. V4L2 requires their value to
be set, that's a decision we made in the V4L2 API, but I think a model
where min, max and step would be undefined (or 0) wouldn't be worse. So
maybe it makes sense to handle this in __uvc_queryctrl_boundaries(),
which is where the adaptation between UVC and V4L2 is handled, instead
of storing CUR in the ctrl->uvc_data DEF/MIN/MAX in
uvc_ctrl_populate_cache() ? I think the code could look cleaner.
> > diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
u> > index ec472e111248..47224437018b 100644
> > --- a/drivers/media/usb/uvc/uvc_ctrl.c
> > +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> > @@ -35,6 +35,8 @@
> > /* ------------------------------------------------------------------------
> > * Controls
> > */
> > +static int __uvc_ctrl_load_cur(struct uvc_video_chain *chain,
> > + struct uvc_control *ctrl);
I think you can move the function up instead of adding a forward
declaration.
> >
> > static const struct uvc_control_info uvc_ctrls[] = {
> > {
> > @@ -1272,6 +1274,13 @@ static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain,
> > uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF));
> > if (ret < 0)
> > return ret;
> > + } else if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR)) {
A comment (probably at the top of the function) to explain the fallback
would be useful.
> > + ret = __uvc_ctrl_load_cur(chain, ctrl);
> > + if (!ret) {
> > + memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF),
> > + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
> > + ctrl->info.size);
> > + }
> > }
> >
> > if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN) {
> > @@ -1279,14 +1288,31 @@ static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain,
> > uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));
> > if (ret < 0)
> > return ret;
> > + } else if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR)) {
> > + ret = __uvc_ctrl_load_cur(chain, ctrl);
> > + if (!ret) {
> > + memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN),
> > + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
> > + ctrl->info.size);
> > + }
> > }
> > +
> > if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX) {
> > ret = uvc_ctrl_query_entity(chain->dev, ctrl, UVC_GET_MAX,
> > uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));
> > if (ret < 0)
> > return ret;
> > + } else if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR)) {
> > + ret = __uvc_ctrl_load_cur(chain, ctrl);
> > + if (!ret) {
> > + memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX),
> > + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
> > + ctrl->info.size);
> > + }
> > }
> > +
> > if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES) {
> > + u8 *res;
> > ret = uvc_ctrl_query_entity(chain->dev, ctrl, UVC_GET_RES,
> > uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
> > if (ret < 0) {
> > @@ -1304,7 +1330,13 @@ static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain,
> > "an XU control. Enabling workaround.\n");
> > memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES), 0,
> > ctrl->info.size);
> > + res = uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES);
> > + *res = 1
> > }
> > + } else {
> > + memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES), 0, ctrl->info.size);
> > + res = uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES);
> > + *res = 1;
> > }
> >
> > ctrl->cached = 1;
> > @@ -1541,11 +1573,8 @@ static int __uvc_queryctrl_boundaries(struct uvc_video_chain *chain,
> > return ret;
> > }
> >
> > - if (ctrl->info.flags & UVC_CTRL_FLAG_GET_DEF)
> > v4l2_ctrl->default_value = uvc_mapping_get_s32(mapping,
> > UVC_GET_DEF, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF));
You forgot to reduce the indentation here.
> > - else
> > - v4l2_ctrl->default_value = 0;
> >
> > switch (mapping->v4l2_type) {
> > case V4L2_CTRL_TYPE_MENU:
> > @@ -1576,23 +1605,14 @@ static int __uvc_queryctrl_boundaries(struct uvc_video_chain *chain,
> > break;
> > }
> >
> > - if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN)
> > - v4l2_ctrl->minimum = uvc_mapping_get_s32(mapping, UVC_GET_MIN,
> > - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));
> > - else
> > - v4l2_ctrl->minimum = 0;
> > + v4l2_ctrl->minimum = uvc_mapping_get_s32(mapping, UVC_GET_MIN,
> > + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));
> >
> > - if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX)
> > - v4l2_ctrl->maximum = uvc_mapping_get_s32(mapping, UVC_GET_MAX,
> > - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));
> > - else
> > - v4l2_ctrl->maximum = 0;
> > + v4l2_ctrl->maximum = uvc_mapping_get_s32(mapping, UVC_GET_MAX,
> > + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));
> >
> > - if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)
> > - v4l2_ctrl->step = uvc_mapping_get_s32(mapping, UVC_GET_RES,
> > - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
> > - else
> > - v4l2_ctrl->step = 0;
> > + v4l2_ctrl->step = uvc_mapping_get_s32(mapping, UVC_GET_RES,
> > + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
> >
> > return 0;
> > }
> >
> > > > > > We can create a new type UVC_CTRL_DATA_TYPE_UNSIGNED_READ_ONLY that
> > > > > > fakes min, max and res, but I think that it is cleaner this approach.
> > > > > >
> > > > > > > > This is a preparation patch.
> > > > > > > >
> > > > > > > > Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
> > > > > > > > ---
> > > > > > > > drivers/media/usb/uvc/uvc_ctrl.c | 12 ++++++++++++
> > > > > > > > drivers/media/usb/uvc/uvcvideo.h | 8 ++++++++
> > > > > > > > 2 files changed, 20 insertions(+)
> > > > > > > >
> > > > > > > > diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> > > > > > > > index 21ec7b978bc7aca21db7cb8fd5d135d876f3330c..59be62ae24a4219fa9d7aacf2ae7382c95362178 100644
> > > > > > > > --- a/drivers/media/usb/uvc/uvc_ctrl.c
> > > > > > > > +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> > > > > > > > @@ -596,6 +596,18 @@ static int uvc_ctrl_query_entity(struct uvc_device *dev,
> > > > > > > > if (query == UVC_GET_CUR && ctrl->entity->get_cur)
> > > > > > > > return ctrl->entity->get_cur(dev, ctrl->entity,
> > > > > > > > ctrl->info.selector, data, len);
> > > > > > > > + if (query == UVC_GET_DEF && ctrl->entity->get_def)
> > > > > > > > + return ctrl->entity->get_def(dev, ctrl->entity,
> > > > > > > > + ctrl->info.selector, data, len);
> > > > > > > > + if (query == UVC_GET_MIN && ctrl->entity->get_min)
> > > > > > > > + return ctrl->entity->get_min(dev, ctrl->entity,
> > > > > > > > + ctrl->info.selector, data, len);
> > > > > > > > + if (query == UVC_GET_MAX && ctrl->entity->get_max)
> > > > > > > > + return ctrl->entity->get_max(dev, ctrl->entity,
> > > > > > > > + ctrl->info.selector, data, len);
> > > > > > > > + if (query == UVC_GET_RES && ctrl->entity->get_res)
> > > > > > > > + return ctrl->entity->get_res(dev, ctrl->entity,
> > > > > > > > + ctrl->info.selector, data, len);
> > > > > > > > if (query == UVC_GET_INFO && ctrl->entity->get_info)
> > > > > > > > return ctrl->entity->get_info(dev, ctrl->entity,
> > > > > > > > ctrl->info.selector, data);
> > > > > > > > diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> > > > > > > > index a931750bdea25b9062dcc7644bf5f2ed89c1cb4c..d6da8ed3ad4cf3377df49923e051fe04d83d2e38 100644
> > > > > > > > --- a/drivers/media/usb/uvc/uvcvideo.h
> > > > > > > > +++ b/drivers/media/usb/uvc/uvcvideo.h
> > > > > > > > @@ -261,6 +261,14 @@ struct uvc_entity {
> > > > > > > > u8 cs, u8 *caps);
> > > > > > > > int (*get_cur)(struct uvc_device *dev, struct uvc_entity *entity,
> > > > > > > > u8 cs, void *data, u16 size);
> > > > > > > > + int (*get_def)(struct uvc_device *dev, struct uvc_entity *entity,
> > > > > > > > + u8 cs, void *data, u16 size);
> > > > > > > > + int (*get_min)(struct uvc_device *dev, struct uvc_entity *entity,
> > > > > > > > + u8 cs, void *data, u16 size);
> > > > > > > > + int (*get_max)(struct uvc_device *dev, struct uvc_entity *entity,
> > > > > > > > + u8 cs, void *data, u16 size);
> > > > > > > > + int (*get_res)(struct uvc_device *dev, struct uvc_entity *entity,
> > > > > > > > + u8 cs, void *data, u16 size);
> > > > > > > >
> > > > > > > > unsigned int ncontrols;
> > > > > > > > struct uvc_control *controls;
--
Regards,
Laurent Pinchart
On 08/09/2025 12:13, Laurent Pinchart wrote:
> A question for Hans Verkuil below.
>
> On Thu, Aug 07, 2025 at 09:35:14AM +0200, Ricardo Ribalda wrote:
>> On Wed, 16 Jul 2025 at 12:32, Ricardo Ribalda wrote:
>>> On Tue, 15 Jul 2025 at 21:35, Laurent Pinchart wrote:
>>>> On Mon, Jul 14, 2025 at 05:46:40PM +0200, Ricardo Ribalda wrote:
>>>>> On Mon, 14 Jul 2025 at 16:30, Laurent Pinchart wrote:
>>>>>> On Tue, Jul 01, 2025 at 01:13:10PM +0200, Ricardo Ribalda wrote:
>>>>>>> On Sun, 29 Jun 2025 at 20:13, Laurent Pinchart wrote:
>>>>>>>> On Thu, Jun 05, 2025 at 05:53:03PM +0000, Ricardo Ribalda wrote:
>>>>>>>>> Virtual entities need to provide more values than get_cur and get_cur
>>>>>>>>
>>>>>>>> I think you meant "get_info and get_cur".
>>>>>>>>
>>>>>>>>> for their controls. Add support for get_def, get_min, get_max and
>>>>>>>>> get_res.
>>>>>>>>
>>>>>>>> Do they ? The UVC specification defines controls that don't list
>>>>>>>> GET_DEF, GET_MIN, GET_MAX and GET_RES as mandatory requests. Can't we do
>>>>>>>> the same for the software controls ? This patch is meant to support the
>>>>>>>> UVC_SWENTITY_ORIENTATION and UVC_SWENTITY_ROTATION control in the next
>>>>>>>> patch, and those are read-only controls. Aren't GET_INFO and GET_CUR
>>>>>>>> enough ?
>>>>>>>
>>>>>>> V4L2_CID_CAMERA_ROTATION has the type UVC_CTRL_DATA_TYPE_UNSIGNED,
>>>>>>> that time requires get_min and get_max.
>>>>>>
>>>>>> Where does that requirement come from ? Is it because how the
>>>>>> corresponding V4L2 type (V4L2_CTRL_TYPE_INTEGER) is handled in
>>>>>> uvc_ctrl_clamp() ? uvc_ctrl_clamp() is only called when setting a
>>>>>> control, from uvc_ctrl_set(), and V4L2_CID_CAMERA_ROTATION should be
>>>>>> read-only.
>>>>>
>>>>> It its for VIDIOC_QUERY_EXT_CTRL
>>>>>
>>>>> uvc_query_v4l2_ctrl -> __uvc_query_v4l2_ctrl -> __uvc_queryctrl_boundaries
>>>>>
>>>>> We need to list the min, max, def and step for every control. They are
>>>>> fetched with uvc_ctrl_populate_cache()
>>>>
>>>> Ah, I see, thanks.
>>>>
>>>> For GET_RES, I think we can leave it unimplemented.
>>>> __uvc_queryctrl_boundaries() will set v4l2_ctrl->step = 0 which seems to
>>>> be the right behaviour for a read-only control whose value never
>>>> changes.
>>>
>>> That will break v4l2-compatiblity. Step needs to be != 0
>>> https://git.linuxtv.org/v4l-utils.git/tree/utils/v4l2-compliance/v4l2-test-controls.cpp#n77
>>>
>>> Control ioctls (Input 0):
>>> fail: v4l2-test-controls.cpp(77): step == 0
>>> fail: v4l2-test-controls.cpp(201): invalid control 009a0923
>
> Is that an issue in v4l2-compliance ? For integer controls,
> https://docs.kernel.org/userspace-api/media/v4l/vidioc-queryctrl.html#c.V4L.v4l2_ctrl_type
> documents the step value as "any". For a read-only control whose value
> is constant, do we want to enforce a non-zero value ? If so we should
> update the specification.
>
> Hans, what's your opinion ?
The spec is not quite precise enough w.r.t. the step value. Whenever that table
says 'any' for the step value, it really should read '>= 1'. It does that already
for TYPE_STRING, but it is equally true for INTEGER(64) and U8/16/32.
If you create a control using the control framework, then that's actually checked.
It's verified by the check_range() function in v4l2-ctrls-core.c.
Regards,
Hans
>
> In any case, if GET_RES isn't implemented, we could update
> __uvc_queryctrl_boundaries() to set step to 1 instead of 0. That would
> fix v4l2-compliance for real controls that don't implement GET_RES.
>
>>>> As for the minimum and maximum, they are currently set to 0 if the
>>>> corresponding operations are not supported. I wonder if we should set
>>>> them to the current value instead for read-only controls (as in controls
>>>> whose flags report support for GET_CUR only)..
>>>
>>> I am not sure that I like that approach IMO the code looks worse...
>>> but if you prefer that, we can go that way
>>
>> I am almost ready to send a new version.
>>
>> What approach do you prefer?
>
> I particularly like the change in __uvc_queryctrl_boundaries(). That
> could probably be done without the rest of the changes though, as
> ctrl->uvc_data is already allocated with kzalloc().
>
> I also like the fact that the driver can rely on the min/max values to
> always be populated in the control data. This could be useful for real
> read-only UVC controls.
>
> Thinking a bit more about this, for read-only controls whose value never
> changes, min, max and step are meaningless. V4L2 requires their value to
> be set, that's a decision we made in the V4L2 API, but I think a model
> where min, max and step would be undefined (or 0) wouldn't be worse. So
> maybe it makes sense to handle this in __uvc_queryctrl_boundaries(),
> which is where the adaptation between UVC and V4L2 is handled, instead
> of storing CUR in the ctrl->uvc_data DEF/MIN/MAX in
> uvc_ctrl_populate_cache() ? I think the code could look cleaner.
>
>>> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> u> > index ec472e111248..47224437018b 100644
>>> --- a/drivers/media/usb/uvc/uvc_ctrl.c
>>> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
>>> @@ -35,6 +35,8 @@
>>> /* ------------------------------------------------------------------------
>>> * Controls
>>> */
>>> +static int __uvc_ctrl_load_cur(struct uvc_video_chain *chain,
>>> + struct uvc_control *ctrl);
>
> I think you can move the function up instead of adding a forward
> declaration.
>
>>>
>>> static const struct uvc_control_info uvc_ctrls[] = {
>>> {
>>> @@ -1272,6 +1274,13 @@ static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain,
>>> uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF));
>>> if (ret < 0)
>>> return ret;
>>> + } else if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR)) {
>
> A comment (probably at the top of the function) to explain the fallback
> would be useful.
>
>>> + ret = __uvc_ctrl_load_cur(chain, ctrl);
>>> + if (!ret) {
>>> + memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF),
>>> + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
>>> + ctrl->info.size);
>>> + }
>>> }
>>>
>>> if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN) {
>>> @@ -1279,14 +1288,31 @@ static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain,
>>> uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));
>>> if (ret < 0)
>>> return ret;
>>> + } else if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR)) {
>>> + ret = __uvc_ctrl_load_cur(chain, ctrl);
>>> + if (!ret) {
>>> + memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN),
>>> + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
>>> + ctrl->info.size);
>>> + }
>>> }
>>> +
>>> if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX) {
>>> ret = uvc_ctrl_query_entity(chain->dev, ctrl, UVC_GET_MAX,
>>> uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));
>>> if (ret < 0)
>>> return ret;
>>> + } else if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR)) {
>>> + ret = __uvc_ctrl_load_cur(chain, ctrl);
>>> + if (!ret) {
>>> + memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX),
>>> + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
>>> + ctrl->info.size);
>>> + }
>>> }
>>> +
>>> if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES) {
>>> + u8 *res;
>>> ret = uvc_ctrl_query_entity(chain->dev, ctrl, UVC_GET_RES,
>>> uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
>>> if (ret < 0) {
>>> @@ -1304,7 +1330,13 @@ static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain,
>>> "an XU control. Enabling workaround.\n");
>>> memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES), 0,
>>> ctrl->info.size);
>>> + res = uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES);
>>> + *res = 1
>>> }
>>> + } else {
>>> + memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES), 0, ctrl->info.size);
>>> + res = uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES);
>>> + *res = 1;
>>> }
>>>
>>> ctrl->cached = 1;
>>> @@ -1541,11 +1573,8 @@ static int __uvc_queryctrl_boundaries(struct uvc_video_chain *chain,
>>> return ret;
>>> }
>>>
>>> - if (ctrl->info.flags & UVC_CTRL_FLAG_GET_DEF)
>>> v4l2_ctrl->default_value = uvc_mapping_get_s32(mapping,
>>> UVC_GET_DEF, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF));
>
> You forgot to reduce the indentation here.
>
>>> - else
>>> - v4l2_ctrl->default_value = 0;
>>>
>>> switch (mapping->v4l2_type) {
>>> case V4L2_CTRL_TYPE_MENU:
>>> @@ -1576,23 +1605,14 @@ static int __uvc_queryctrl_boundaries(struct uvc_video_chain *chain,
>>> break;
>>> }
>>>
>>> - if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN)
>>> - v4l2_ctrl->minimum = uvc_mapping_get_s32(mapping, UVC_GET_MIN,
>>> - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));
>>> - else
>>> - v4l2_ctrl->minimum = 0;
>>> + v4l2_ctrl->minimum = uvc_mapping_get_s32(mapping, UVC_GET_MIN,
>>> + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));
>>>
>>> - if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX)
>>> - v4l2_ctrl->maximum = uvc_mapping_get_s32(mapping, UVC_GET_MAX,
>>> - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));
>>> - else
>>> - v4l2_ctrl->maximum = 0;
>>> + v4l2_ctrl->maximum = uvc_mapping_get_s32(mapping, UVC_GET_MAX,
>>> + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));
>>>
>>> - if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)
>>> - v4l2_ctrl->step = uvc_mapping_get_s32(mapping, UVC_GET_RES,
>>> - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
>>> - else
>>> - v4l2_ctrl->step = 0;
>>> + v4l2_ctrl->step = uvc_mapping_get_s32(mapping, UVC_GET_RES,
>>> + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
>>>
>>> return 0;
>>> }
>>>
>>>>>>> We can create a new type UVC_CTRL_DATA_TYPE_UNSIGNED_READ_ONLY that
>>>>>>> fakes min, max and res, but I think that it is cleaner this approach.
>>>>>>>
>>>>>>>>> This is a preparation patch.
>>>>>>>>>
>>>>>>>>> Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
>>>>>>>>> ---
>>>>>>>>> drivers/media/usb/uvc/uvc_ctrl.c | 12 ++++++++++++
>>>>>>>>> drivers/media/usb/uvc/uvcvideo.h | 8 ++++++++
>>>>>>>>> 2 files changed, 20 insertions(+)
>>>>>>>>>
>>>>>>>>> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
>>>>>>>>> index 21ec7b978bc7aca21db7cb8fd5d135d876f3330c..59be62ae24a4219fa9d7aacf2ae7382c95362178 100644
>>>>>>>>> --- a/drivers/media/usb/uvc/uvc_ctrl.c
>>>>>>>>> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
>>>>>>>>> @@ -596,6 +596,18 @@ static int uvc_ctrl_query_entity(struct uvc_device *dev,
>>>>>>>>> if (query == UVC_GET_CUR && ctrl->entity->get_cur)
>>>>>>>>> return ctrl->entity->get_cur(dev, ctrl->entity,
>>>>>>>>> ctrl->info.selector, data, len);
>>>>>>>>> + if (query == UVC_GET_DEF && ctrl->entity->get_def)
>>>>>>>>> + return ctrl->entity->get_def(dev, ctrl->entity,
>>>>>>>>> + ctrl->info.selector, data, len);
>>>>>>>>> + if (query == UVC_GET_MIN && ctrl->entity->get_min)
>>>>>>>>> + return ctrl->entity->get_min(dev, ctrl->entity,
>>>>>>>>> + ctrl->info.selector, data, len);
>>>>>>>>> + if (query == UVC_GET_MAX && ctrl->entity->get_max)
>>>>>>>>> + return ctrl->entity->get_max(dev, ctrl->entity,
>>>>>>>>> + ctrl->info.selector, data, len);
>>>>>>>>> + if (query == UVC_GET_RES && ctrl->entity->get_res)
>>>>>>>>> + return ctrl->entity->get_res(dev, ctrl->entity,
>>>>>>>>> + ctrl->info.selector, data, len);
>>>>>>>>> if (query == UVC_GET_INFO && ctrl->entity->get_info)
>>>>>>>>> return ctrl->entity->get_info(dev, ctrl->entity,
>>>>>>>>> ctrl->info.selector, data);
>>>>>>>>> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
>>>>>>>>> index a931750bdea25b9062dcc7644bf5f2ed89c1cb4c..d6da8ed3ad4cf3377df49923e051fe04d83d2e38 100644
>>>>>>>>> --- a/drivers/media/usb/uvc/uvcvideo.h
>>>>>>>>> +++ b/drivers/media/usb/uvc/uvcvideo.h
>>>>>>>>> @@ -261,6 +261,14 @@ struct uvc_entity {
>>>>>>>>> u8 cs, u8 *caps);
>>>>>>>>> int (*get_cur)(struct uvc_device *dev, struct uvc_entity *entity,
>>>>>>>>> u8 cs, void *data, u16 size);
>>>>>>>>> + int (*get_def)(struct uvc_device *dev, struct uvc_entity *entity,
>>>>>>>>> + u8 cs, void *data, u16 size);
>>>>>>>>> + int (*get_min)(struct uvc_device *dev, struct uvc_entity *entity,
>>>>>>>>> + u8 cs, void *data, u16 size);
>>>>>>>>> + int (*get_max)(struct uvc_device *dev, struct uvc_entity *entity,
>>>>>>>>> + u8 cs, void *data, u16 size);
>>>>>>>>> + int (*get_res)(struct uvc_device *dev, struct uvc_entity *entity,
>>>>>>>>> + u8 cs, void *data, u16 size);
>>>>>>>>>
>>>>>>>>> unsigned int ncontrols;
>>>>>>>>> struct uvc_control *controls;
>
Hi Ricardo, Laurent,
On 1-Jul-25 13:13, Ricardo Ribalda wrote:
> Hi Laurent
>
> On Sun, 29 Jun 2025 at 20:13, Laurent Pinchart
> <laurent.pinchart@ideasonboard.com> wrote:
>>
>> Hi Ricardo,
>>
>> Thank you for the patch.
>>
>> On Thu, Jun 05, 2025 at 05:53:03PM +0000, Ricardo Ribalda wrote:
>>> Virtual entities need to provide more values than get_cur and get_cur
>>
>> I think you meant "get_info and get_cur".
>>
>>> for their controls. Add support for get_def, get_min, get_max and
>>> get_res.
>>
>> Do they ? The UVC specification defines controls that don't list
>> GET_DEF, GET_MIN, GET_MAX and GET_RES as mandatory requests. Can't we do
>> the same for the software controls ? This patch is meant to support the
>> UVC_SWENTITY_ORIENTATION and UVC_SWENTITY_ROTATION control in the next
>> patch, and those are read-only controls. Aren't GET_INFO and GET_CUR
>> enough ?
>
> V4L2_CID_CAMERA_ROTATION has the type UVC_CTRL_DATA_TYPE_UNSIGNED,
> that time requires get_min and get_max.
> We can create a new type UVC_CTRL_DATA_TYPE_UNSIGNED_READ_ONLY that
> fakes min, max and res, but I think that it is cleaner this approach.
If I read this right, then we could at least drop adding get_def and
get_res callbacks from this patch, right?
Can you do that for the next version please?
Regards,
Hans
>>> This is a preparation patch.
>>>
>>> Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
>>> ---
>>> drivers/media/usb/uvc/uvc_ctrl.c | 12 ++++++++++++
>>> drivers/media/usb/uvc/uvcvideo.h | 8 ++++++++
>>> 2 files changed, 20 insertions(+)
>>>
>>> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
>>> index 21ec7b978bc7aca21db7cb8fd5d135d876f3330c..59be62ae24a4219fa9d7aacf2ae7382c95362178 100644
>>> --- a/drivers/media/usb/uvc/uvc_ctrl.c
>>> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
>>> @@ -596,6 +596,18 @@ static int uvc_ctrl_query_entity(struct uvc_device *dev,
>>> if (query == UVC_GET_CUR && ctrl->entity->get_cur)
>>> return ctrl->entity->get_cur(dev, ctrl->entity,
>>> ctrl->info.selector, data, len);
>>> + if (query == UVC_GET_DEF && ctrl->entity->get_def)
>>> + return ctrl->entity->get_def(dev, ctrl->entity,
>>> + ctrl->info.selector, data, len);
>>> + if (query == UVC_GET_MIN && ctrl->entity->get_min)
>>> + return ctrl->entity->get_min(dev, ctrl->entity,
>>> + ctrl->info.selector, data, len);
>>> + if (query == UVC_GET_MAX && ctrl->entity->get_max)
>>> + return ctrl->entity->get_max(dev, ctrl->entity,
>>> + ctrl->info.selector, data, len);
>>> + if (query == UVC_GET_RES && ctrl->entity->get_res)
>>> + return ctrl->entity->get_res(dev, ctrl->entity,
>>> + ctrl->info.selector, data, len);
>>> if (query == UVC_GET_INFO && ctrl->entity->get_info)
>>> return ctrl->entity->get_info(dev, ctrl->entity,
>>> ctrl->info.selector, data);
>>> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
>>> index a931750bdea25b9062dcc7644bf5f2ed89c1cb4c..d6da8ed3ad4cf3377df49923e051fe04d83d2e38 100644
>>> --- a/drivers/media/usb/uvc/uvcvideo.h
>>> +++ b/drivers/media/usb/uvc/uvcvideo.h
>>> @@ -261,6 +261,14 @@ struct uvc_entity {
>>> u8 cs, u8 *caps);
>>> int (*get_cur)(struct uvc_device *dev, struct uvc_entity *entity,
>>> u8 cs, void *data, u16 size);
>>> + int (*get_def)(struct uvc_device *dev, struct uvc_entity *entity,
>>> + u8 cs, void *data, u16 size);
>>> + int (*get_min)(struct uvc_device *dev, struct uvc_entity *entity,
>>> + u8 cs, void *data, u16 size);
>>> + int (*get_max)(struct uvc_device *dev, struct uvc_entity *entity,
>>> + u8 cs, void *data, u16 size);
>>> + int (*get_res)(struct uvc_device *dev, struct uvc_entity *entity,
>>> + u8 cs, void *data, u16 size);
>>>
>>> unsigned int ncontrols;
>>> struct uvc_control *controls;
>>
>> --
>> Regards,
>>
>> Laurent Pinchart
>
>
>
© 2016 - 2025 Red Hat, Inc.