Re: [RFCv11 PATCH 14/29] v4l2-ctrls: add core request support

2018-04-23 Thread Hans Verkuil
On 04/11/2018 11:43 AM, Tomasz Figa wrote:
> Hi Hans,
> 
> On Mon, Apr 9, 2018 at 11:20 PM Hans Verkuil  wrote:
> 
>> From: Hans Verkuil 
> 
>> Integrate the request support. This adds the v4l2_ctrl_request_complete
>> and v4l2_ctrl_request_setup functions to complete a request and (as a
>> helper function) to apply a request to the hardware.
> 
> Please see my comments inline.
> 
> [snip]
>> +static int v4l2_ctrl_request_clone(struct v4l2_ctrl_handler *hdl,
>> +  const struct v4l2_ctrl_handler *from)
>> +{
>> +   struct v4l2_ctrl_ref *ref;
>> +   int err;
>> +
>> +   if (WARN_ON(!hdl || hdl == from))
>> +   return -EINVAL;
>> +
>> +   if (hdl->error)
>> +   return hdl->error;
>> +
>> +   WARN_ON(hdl->lock != &hdl->_lock);
>> +
>> +   mutex_lock(from->lock);
>> +   list_for_each_entry(ref, &from->ctrl_refs, node) {
>> +   struct v4l2_ctrl *ctrl = ref->ctrl;
>> +   struct v4l2_ctrl_ref *new_ref;
>> +
>> +   /* Skip refs inherited from other devices */
>> +   if (ref->from_other_dev)
>> +   continue;
>> +   /* And buttons */
>> +   if (ctrl->type == V4L2_CTRL_TYPE_BUTTON)
>> +   continue;
>> +   err = handler_new_ref(hdl, ctrl, &new_ref, false, true);
>> +   if (err) {
>> +   printk("%s: handler_new_ref on control %x (%s)
> returned %d\n", __func__, ctrl->id, ctrl->name, err);
> 
> Perhaps pr_err()?
> 
>> +   err = 0;
>> +   continue;
> 
> Hmm, is it really fine to ignore the error and continue here?

It's a debugging left-over. It should just break and return the error.

> 
>> +   }
>> +   if (err)
>> +   break;
>> +   }
>> +   mutex_unlock(from->lock);
>> +   return err;
>> +}
>> +
>> +static void v4l2_ctrl_request_queue(struct media_request_object *obj)
>> +{
>> +   struct v4l2_ctrl_handler *hdl =
>> +   container_of(obj, struct v4l2_ctrl_handler, req_obj);
>> +   struct v4l2_ctrl_handler *main_hdl = obj->priv;
>> +   struct v4l2_ctrl_handler *prev = NULL;
>> +   struct v4l2_ctrl_ref *ref_hdl, *ref_prev = NULL;
>> +
>> +   if (list_empty(&main_hdl->requests_queued))
>> +   goto queue;
>> +
>> +   prev = list_last_entry(&main_hdl->requests_queued,
>> +  struct v4l2_ctrl_handler, requests_queued);
>> +   mutex_lock(prev->lock);
>> +   ref_prev = list_first_entry(&prev->ctrl_refs,
>> +   struct v4l2_ctrl_ref, node);
>> +   list_for_each_entry(ref_hdl, &hdl->ctrl_refs, node) {
>> +   if (ref_hdl->req)
>> +   continue;
>> +   while (ref_prev->ctrl->id < ref_hdl->ctrl->id)
>> +   ref_prev = list_next_entry(ref_prev, node);
> 
> Is this really safe? The only stop condition here is the control id.
> Perhaps the code below could be better?

The two ctrl_refs lists must contain the same controls, so it is safe.
However, I modified the code to check that ref_prev isn't the last
element in the list, just in case something gets messed up.

BTW, the variable names in this function are awful :-)

I've changed them so this code is hopefully easier to understand.

> 
> list_for_each_entry_from(ref_prev, &prev->ctrl_refs, node)
>  if (ref_prev->ctrl->id >= ref_hdl->ctrl->id)
>  break;
> 
>> +   if (WARN_ON(ref_prev->ctrl->id != ref_hdl->ctrl->id))
>> +   break;
>> +   ref_hdl->req = ref_prev->req;
>> +   }
>> +   mutex_unlock(prev->lock);
>> +queue:
>> +   list_add_tail(&hdl->requests_queued, &main_hdl->requests_queued);
>> +   hdl->request_is_queued = true;
>> +}
>> +
> 
> [snip]
>> +void v4l2_ctrl_request_complete(struct media_request *req,
>> +   struct v4l2_ctrl_handler *main_hdl)
>> +{
>> +   struct media_request_object *obj;
>> +   struct v4l2_ctrl_handler *hdl;
>> +   struct v4l2_ctrl_ref *ref;
>> +
>> +   if (!req || !main_hdl)
>> +   return;
> 
> Can this happen normally? Perhaps WARN_ON() would make sense?

Yes, this can happen. See e.g. vim2m_stop_streaming(). This function is
called for all buffers, but not all buffers may have a request associated
with them. In that case req == NULL and this function should do nothing.

There are also cases (rare, but they exist) where a handler may be NULL
because the specific hardware version does not support the controls in that
handler. In that case the handler may never be created.

Basically this function means: if there is a request, and if there is a
handler, then complete the request.

> 
> [snip]
>> +void v4l2_ctrl_request_setup(struct media_request *req,
>> +struct v4l2_ctrl_handler *main_hd

Re: [RFCv11 PATCH 14/29] v4l2-ctrls: add core request support

2018-04-11 Thread Tomasz Figa
Hi Hans,

On Mon, Apr 9, 2018 at 11:20 PM Hans Verkuil  wrote:

> From: Hans Verkuil 

> Integrate the request support. This adds the v4l2_ctrl_request_complete
> and v4l2_ctrl_request_setup functions to complete a request and (as a
> helper function) to apply a request to the hardware.

Please see my comments inline.

[snip]
> +static int v4l2_ctrl_request_clone(struct v4l2_ctrl_handler *hdl,
> +  const struct v4l2_ctrl_handler *from)
> +{
> +   struct v4l2_ctrl_ref *ref;
> +   int err;
> +
> +   if (WARN_ON(!hdl || hdl == from))
> +   return -EINVAL;
> +
> +   if (hdl->error)
> +   return hdl->error;
> +
> +   WARN_ON(hdl->lock != &hdl->_lock);
> +
> +   mutex_lock(from->lock);
> +   list_for_each_entry(ref, &from->ctrl_refs, node) {
> +   struct v4l2_ctrl *ctrl = ref->ctrl;
> +   struct v4l2_ctrl_ref *new_ref;
> +
> +   /* Skip refs inherited from other devices */
> +   if (ref->from_other_dev)
> +   continue;
> +   /* And buttons */
> +   if (ctrl->type == V4L2_CTRL_TYPE_BUTTON)
> +   continue;
> +   err = handler_new_ref(hdl, ctrl, &new_ref, false, true);
> +   if (err) {
> +   printk("%s: handler_new_ref on control %x (%s)
returned %d\n", __func__, ctrl->id, ctrl->name, err);

Perhaps pr_err()?

> +   err = 0;
> +   continue;

Hmm, is it really fine to ignore the error and continue here?

> +   }
> +   if (err)
> +   break;
> +   }
> +   mutex_unlock(from->lock);
> +   return err;
> +}
> +
> +static void v4l2_ctrl_request_queue(struct media_request_object *obj)
> +{
> +   struct v4l2_ctrl_handler *hdl =
> +   container_of(obj, struct v4l2_ctrl_handler, req_obj);
> +   struct v4l2_ctrl_handler *main_hdl = obj->priv;
> +   struct v4l2_ctrl_handler *prev = NULL;
> +   struct v4l2_ctrl_ref *ref_hdl, *ref_prev = NULL;
> +
> +   if (list_empty(&main_hdl->requests_queued))
> +   goto queue;
> +
> +   prev = list_last_entry(&main_hdl->requests_queued,
> +  struct v4l2_ctrl_handler, requests_queued);
> +   mutex_lock(prev->lock);
> +   ref_prev = list_first_entry(&prev->ctrl_refs,
> +   struct v4l2_ctrl_ref, node);
> +   list_for_each_entry(ref_hdl, &hdl->ctrl_refs, node) {
> +   if (ref_hdl->req)
> +   continue;
> +   while (ref_prev->ctrl->id < ref_hdl->ctrl->id)
> +   ref_prev = list_next_entry(ref_prev, node);

Is this really safe? The only stop condition here is the control id.
Perhaps the code below could be better?

list_for_each_entry_from(ref_prev, &prev->ctrl_refs, node)
 if (ref_prev->ctrl->id >= ref_hdl->ctrl->id)
 break;

> +   if (WARN_ON(ref_prev->ctrl->id != ref_hdl->ctrl->id))
> +   break;
> +   ref_hdl->req = ref_prev->req;
> +   }
> +   mutex_unlock(prev->lock);
> +queue:
> +   list_add_tail(&hdl->requests_queued, &main_hdl->requests_queued);
> +   hdl->request_is_queued = true;
> +}
> +

[snip]
> +void v4l2_ctrl_request_complete(struct media_request *req,
> +   struct v4l2_ctrl_handler *main_hdl)
> +{
> +   struct media_request_object *obj;
> +   struct v4l2_ctrl_handler *hdl;
> +   struct v4l2_ctrl_ref *ref;
> +
> +   if (!req || !main_hdl)
> +   return;

Can this happen normally? Perhaps WARN_ON() would make sense?

[snip]
> +void v4l2_ctrl_request_setup(struct media_request *req,
> +struct v4l2_ctrl_handler *main_hdl)
> +{
> +   struct media_request_object *obj;
> +   struct v4l2_ctrl_handler *hdl;
> +   struct v4l2_ctrl_ref *ref;
> +
> +   if (!req || !main_hdl)

Can this happen normally? Perhaps WARN_ON() would make sense?

> +   return;
> +
> +   obj = media_request_object_find(req, &req_ops, main_hdl);
> +   if (!obj)
> +   return;
> +   if (obj->completed) {
> +   media_request_object_put(obj);
> +   return;
> +   }
> +   hdl = container_of(obj, struct v4l2_ctrl_handler, req_obj);
> +
> +   mutex_lock(hdl->lock);
> +
> +   list_for_each_entry(ref, &hdl->ctrl_refs, node)
> +   ref->done = false;
> +
> +   list_for_each_entry(ref, &hdl->ctrl_refs, node) {
> +   struct v4l2_ctrl *ctrl = ref->ctrl;
> +   struct v4l2_ctrl *master = ctrl->cluster[0];
> +   bool have_new_data = false;
> +   int i;
> +
> +   /* Skip if this control was already handled by a cluster.
*/
> +   /* Skip button controls and read-only controls. */
> +   

Re: [RFCv11 PATCH 14/29] v4l2-ctrls: add core request support

2018-04-11 Thread Paul Kocialkowski
Hi,

On Mon, 2018-04-09 at 16:20 +0200, Hans Verkuil wrote:
> From: Hans Verkuil 
> 
> Integrate the request support. This adds the
> v4l2_ctrl_request_complete
> and v4l2_ctrl_request_setup functions to complete a request and (as a
> helper function) to apply a request to the hardware.
> 
> It takes care of queuing requests and correctly chaining control
> values
> in the request queue.
> 
> Note that when a request is marked completed it will copy control
> values
> to the internal request state. This can be optimized in the future
> since
> this is sub-optimal when dealing with large compound and/or array
> controls.
> 
> For the initial 'stateless codec' use-case the current implementation
> is
> sufficient.

See one comment about a missing unlock below.

> Signed-off-by: Hans Verkuil 
> ---
>  drivers/media/v4l2-core/v4l2-ctrls.c | 325
> ++-
>  include/media/v4l2-ctrls.h   |  23 +++
>  2 files changed, 342 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c
> b/drivers/media/v4l2-core/v4l2-ctrls.c
> index da4cc1485dc4..6e2c5e24734f 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls.c
> @@ -1647,6 +1647,13 @@ static int new_to_user(struct v4l2_ext_control
> *c,
>   return ptr_to_user(c, ctrl, ctrl->p_new);
>  }
>  
> +/* Helper function: copy the request value back to the caller */
> +static int req_to_user(struct v4l2_ext_control *c,
> +struct v4l2_ctrl_ref *ref)
> +{
> + return ptr_to_user(c, ref->ctrl, ref->p_req);
> +}
> +
>  /* Helper function: copy the initial control value back to the caller
> */
>  static int def_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl
> *ctrl)
>  {
> @@ -1766,6 +1773,26 @@ static void cur_to_new(struct v4l2_ctrl *ctrl)
>   ptr_to_ptr(ctrl, ctrl->p_cur, ctrl->p_new);
>  }
>  
> +/* Copy the new value to the request value */
> +static void new_to_req(struct v4l2_ctrl_ref *ref)
> +{
> + if (!ref)
> + return;
> + ptr_to_ptr(ref->ctrl, ref->ctrl->p_new, ref->p_req);
> + ref->req = ref;
> +}
> +
> +/* Copy the request value to the new value */
> +static void req_to_new(struct v4l2_ctrl_ref *ref)
> +{
> + if (!ref)
> + return;
> + if (ref->req)
> + ptr_to_ptr(ref->ctrl, ref->req->p_req, ref->ctrl-
> >p_new);
> + else
> + ptr_to_ptr(ref->ctrl, ref->ctrl->p_cur, ref->ctrl-
> >p_new);
> +}
> +
>  /* Return non-zero if one or more of the controls in the cluster has
> a new
> value that differs from the current value. */
>  static int cluster_changed(struct v4l2_ctrl *master)
> @@ -1875,6 +1902,9 @@ int v4l2_ctrl_handler_init_class(struct
> v4l2_ctrl_handler *hdl,
>   lockdep_set_class_and_name(hdl->lock, key, name);
>   INIT_LIST_HEAD(&hdl->ctrls);
>   INIT_LIST_HEAD(&hdl->ctrl_refs);
> + INIT_LIST_HEAD(&hdl->requests);
> + INIT_LIST_HEAD(&hdl->requests_queued);
> + hdl->request_is_queued = false;
>   hdl->nr_of_buckets = 1 + nr_of_controls_hint / 8;
>   hdl->buckets = kvmalloc_array(hdl->nr_of_buckets,
> sizeof(hdl->buckets[0]),
> @@ -1895,6 +1925,14 @@ void v4l2_ctrl_handler_free(struct
> v4l2_ctrl_handler *hdl)
>   if (hdl == NULL || hdl->buckets == NULL)
>   return;
>  
> + if (!hdl->req_obj.req && !list_empty(&hdl->requests)) {
> + struct v4l2_ctrl_handler *req, *next_req;
> +
> + list_for_each_entry_safe(req, next_req, &hdl-
> >requests, requests) {
> + media_request_object_unbind(&req->req_obj);
> + media_request_object_put(&req->req_obj);
> + }
> + }
>   mutex_lock(hdl->lock);
>   /* Free all nodes */
>   list_for_each_entry_safe(ref, next_ref, &hdl->ctrl_refs,
> node) {
> @@ -2816,6 +2854,126 @@ int v4l2_querymenu(struct v4l2_ctrl_handler
> *hdl, struct v4l2_querymenu *qm)
>  }
>  EXPORT_SYMBOL(v4l2_querymenu);
>  
> +static int v4l2_ctrl_request_clone(struct v4l2_ctrl_handler *hdl,
> +const struct v4l2_ctrl_handler
> *from)
> +{
> + struct v4l2_ctrl_ref *ref;
> + int err;
> +
> + if (WARN_ON(!hdl || hdl == from))
> + return -EINVAL;
> +
> + if (hdl->error)
> + return hdl->error;
> +
> + WARN_ON(hdl->lock != &hdl->_lock);
> +
> + mutex_lock(from->lock);
> + list_for_each_entry(ref, &from->ctrl_refs, node) {
> + struct v4l2_ctrl *ctrl = ref->ctrl;
> + struct v4l2_ctrl_ref *new_ref;
> +
> + /* Skip refs inherited from other devices */
> + if (ref->from_other_dev)
> + continue;
> + /* And buttons */
> + if (ctrl->type == V4L2_CTRL_TYPE_BUTTON)
> + continue;
> + err = handler_new_ref(hdl, ctrl, &new_ref, false,
> true);
> + if (err) {
> +   

[RFCv11 PATCH 14/29] v4l2-ctrls: add core request support

2018-04-09 Thread Hans Verkuil
From: Hans Verkuil 

Integrate the request support. This adds the v4l2_ctrl_request_complete
and v4l2_ctrl_request_setup functions to complete a request and (as a
helper function) to apply a request to the hardware.

It takes care of queuing requests and correctly chaining control values
in the request queue.

Note that when a request is marked completed it will copy control values
to the internal request state. This can be optimized in the future since
this is sub-optimal when dealing with large compound and/or array controls.

For the initial 'stateless codec' use-case the current implementation is
sufficient.

Signed-off-by: Hans Verkuil 
---
 drivers/media/v4l2-core/v4l2-ctrls.c | 325 ++-
 include/media/v4l2-ctrls.h   |  23 +++
 2 files changed, 342 insertions(+), 6 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c 
b/drivers/media/v4l2-core/v4l2-ctrls.c
index da4cc1485dc4..6e2c5e24734f 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -1647,6 +1647,13 @@ static int new_to_user(struct v4l2_ext_control *c,
return ptr_to_user(c, ctrl, ctrl->p_new);
 }
 
+/* Helper function: copy the request value back to the caller */
+static int req_to_user(struct v4l2_ext_control *c,
+  struct v4l2_ctrl_ref *ref)
+{
+   return ptr_to_user(c, ref->ctrl, ref->p_req);
+}
+
 /* Helper function: copy the initial control value back to the caller */
 static int def_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
 {
@@ -1766,6 +1773,26 @@ static void cur_to_new(struct v4l2_ctrl *ctrl)
ptr_to_ptr(ctrl, ctrl->p_cur, ctrl->p_new);
 }
 
+/* Copy the new value to the request value */
+static void new_to_req(struct v4l2_ctrl_ref *ref)
+{
+   if (!ref)
+   return;
+   ptr_to_ptr(ref->ctrl, ref->ctrl->p_new, ref->p_req);
+   ref->req = ref;
+}
+
+/* Copy the request value to the new value */
+static void req_to_new(struct v4l2_ctrl_ref *ref)
+{
+   if (!ref)
+   return;
+   if (ref->req)
+   ptr_to_ptr(ref->ctrl, ref->req->p_req, ref->ctrl->p_new);
+   else
+   ptr_to_ptr(ref->ctrl, ref->ctrl->p_cur, ref->ctrl->p_new);
+}
+
 /* Return non-zero if one or more of the controls in the cluster has a new
value that differs from the current value. */
 static int cluster_changed(struct v4l2_ctrl *master)
@@ -1875,6 +1902,9 @@ int v4l2_ctrl_handler_init_class(struct v4l2_ctrl_handler 
*hdl,
lockdep_set_class_and_name(hdl->lock, key, name);
INIT_LIST_HEAD(&hdl->ctrls);
INIT_LIST_HEAD(&hdl->ctrl_refs);
+   INIT_LIST_HEAD(&hdl->requests);
+   INIT_LIST_HEAD(&hdl->requests_queued);
+   hdl->request_is_queued = false;
hdl->nr_of_buckets = 1 + nr_of_controls_hint / 8;
hdl->buckets = kvmalloc_array(hdl->nr_of_buckets,
  sizeof(hdl->buckets[0]),
@@ -1895,6 +1925,14 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler 
*hdl)
if (hdl == NULL || hdl->buckets == NULL)
return;
 
+   if (!hdl->req_obj.req && !list_empty(&hdl->requests)) {
+   struct v4l2_ctrl_handler *req, *next_req;
+
+   list_for_each_entry_safe(req, next_req, &hdl->requests, 
requests) {
+   media_request_object_unbind(&req->req_obj);
+   media_request_object_put(&req->req_obj);
+   }
+   }
mutex_lock(hdl->lock);
/* Free all nodes */
list_for_each_entry_safe(ref, next_ref, &hdl->ctrl_refs, node) {
@@ -2816,6 +2854,126 @@ int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, 
struct v4l2_querymenu *qm)
 }
 EXPORT_SYMBOL(v4l2_querymenu);
 
+static int v4l2_ctrl_request_clone(struct v4l2_ctrl_handler *hdl,
+  const struct v4l2_ctrl_handler *from)
+{
+   struct v4l2_ctrl_ref *ref;
+   int err;
+
+   if (WARN_ON(!hdl || hdl == from))
+   return -EINVAL;
+
+   if (hdl->error)
+   return hdl->error;
+
+   WARN_ON(hdl->lock != &hdl->_lock);
+
+   mutex_lock(from->lock);
+   list_for_each_entry(ref, &from->ctrl_refs, node) {
+   struct v4l2_ctrl *ctrl = ref->ctrl;
+   struct v4l2_ctrl_ref *new_ref;
+
+   /* Skip refs inherited from other devices */
+   if (ref->from_other_dev)
+   continue;
+   /* And buttons */
+   if (ctrl->type == V4L2_CTRL_TYPE_BUTTON)
+   continue;
+   err = handler_new_ref(hdl, ctrl, &new_ref, false, true);
+   if (err) {
+   printk("%s: handler_new_ref on control %x (%s) returned 
%d\n", __func__, ctrl->id, ctrl->name, err);
+   err = 0;
+   continue;
+   }
+   if (err)
+   break;
+   }
+   mutex_unlock(from-