diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 33c4cbb37f01..fcd7bf10a314 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -2259,16 +2259,7 @@ 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->valid_p_req = true; -} - -/* Copy the current value to the request value */ -static void cur_to_req(struct v4l2_ctrl_ref *ref) -{ - if (!ref) - return; - ptr_to_ptr(ref->ctrl, ref->ctrl->p_cur, ref->p_req); - ref->valid_p_req = true; + ref->req = ref; } /* Copy the request value to the new value */ @@ -2276,8 +2267,8 @@ static void req_to_new(struct v4l2_ctrl_ref *ref) { if (!ref) return; - if (ref->valid_p_req) - ptr_to_ptr(ref->ctrl, ref->p_req, ref->ctrl->p_new); + 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); } @@ -3447,8 +3438,39 @@ 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_hdl = NULL; + struct v4l2_ctrl_ref *ref_ctrl, *ref_ctrl_prev = NULL; mutex_lock(main_hdl->lock); + if (list_empty(&main_hdl->requests_queued)) + goto queue; + + prev_hdl = list_last_entry(&main_hdl->requests_queued, + struct v4l2_ctrl_handler, requests_queued); + /* + * Note: prev_hdl and hdl must contain the same list of control + * references, so if any differences are detected then that is a + * driver bug and the WARN_ON is triggered. + */ + mutex_lock(prev_hdl->lock); + ref_ctrl_prev = list_first_entry(&prev_hdl->ctrl_refs, + struct v4l2_ctrl_ref, node); + list_for_each_entry(ref_ctrl, &hdl->ctrl_refs, node) { + if (ref_ctrl->req) + continue; + while (ref_ctrl_prev->ctrl->id < ref_ctrl->ctrl->id) { + /* Should never happen, but just in case... */ + if (list_is_last(&ref_ctrl_prev->node, + &prev_hdl->ctrl_refs)) + break; + ref_ctrl_prev = list_next_entry(ref_ctrl_prev, node); + } + if (WARN_ON(ref_ctrl_prev->ctrl->id != ref_ctrl->ctrl->id)) + break; + ref_ctrl->req = ref_ctrl_prev->req; + } + mutex_unlock(prev_hdl->lock); +queue: list_add_tail(&hdl->requests_queued, &main_hdl->requests_queued); hdl->request_is_queued = true; mutex_unlock(main_hdl->lock); @@ -3505,7 +3527,7 @@ v4l2_ctrl_request_hdl_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id) { struct v4l2_ctrl_ref *ref = find_ref_lock(hdl, id); - return (ref && ref->valid_p_req) ? ref->ctrl : NULL; + return (ref && ref->req == ref) ? ref->ctrl : NULL; } EXPORT_SYMBOL_GPL(v4l2_ctrl_request_hdl_ctrl_find); @@ -3691,13 +3713,7 @@ static int class_check(struct v4l2_ctrl_handler *hdl, u32 which) return find_ref_lock(hdl, which | 1) ? 0 : -EINVAL; } -/* - * Get extended controls. Allocates the helpers array if needed. - * - * Note that v4l2_g_ext_ctrls_common() with 'which' set to - * V4L2_CTRL_WHICH_REQUEST_VAL is only called if the request was - * completed, and in that case valid_p_req is true for all controls. - */ +/* Get extended controls. Allocates the helpers array if needed. */ static int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs, struct video_device *vdev) @@ -3706,10 +3722,9 @@ static int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl, struct v4l2_ctrl_helper *helpers = helper; int ret; int i, j; - bool is_default, is_request; + bool def_value; - is_default = (cs->which == V4L2_CTRL_WHICH_DEF_VAL); - is_request = (cs->which == V4L2_CTRL_WHICH_REQUEST_VAL); + def_value = (cs->which == V4L2_CTRL_WHICH_DEF_VAL); cs->error_idx = cs->count; cs->which = V4L2_CTRL_ID2WHICH(cs->which); @@ -3735,9 +3750,11 @@ static int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl, ret = -EACCES; for (i = 0; !ret && i < cs->count; i++) { + int (*ctrl_to_user)(struct v4l2_ext_control *c, + struct v4l2_ctrl *ctrl); struct v4l2_ctrl *master; - bool is_volatile = false; - u32 idx = i; + + ctrl_to_user = def_value ? def_to_user : cur_to_user; if (helpers[i].mref == NULL) continue; @@ -3747,48 +3764,31 @@ static int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl, v4l2_ctrl_lock(master); - /* - * g_volatile_ctrl will update the new control values. - * This makes no sense for V4L2_CTRL_WHICH_DEF_VAL and - * V4L2_CTRL_WHICH_REQUEST_VAL. In the case of requests - * it is v4l2_ctrl_request_complete() that copies the - * volatile controls at the time of request completion - * to the request, so you don't want to do that again. - */ - if (!is_default && !is_request && + /* g_volatile_ctrl will update the new control values */ + if (!def_value && ((master->flags & V4L2_CTRL_FLAG_VOLATILE) || (master->has_volatiles && !is_cur_manual(master)))) { for (j = 0; j < master->ncontrols; j++) cur_to_new(master->cluster[j]); ret = call_op(master, g_volatile_ctrl); - is_volatile = true; + ctrl_to_user = new_to_user; } + /* If OK, then copy the current (for non-volatile controls) + or the new (for volatile controls) control values to the + caller */ + if (!ret) { + u32 idx = i; - if (ret) { - v4l2_ctrl_unlock(master); - break; + do { + if (helpers[idx].ref->req) + ret = req_to_user(cs->controls + idx, + helpers[idx].ref->req); + else + ret = ctrl_to_user(cs->controls + idx, + helpers[idx].ref->ctrl); + idx = helpers[idx].next; + } while (!ret && idx); } - - /* - * Copy the default value (if is_default is true), the - * request value (if is_request is true and p_req is valid), - * the new volatile value (if is_volatile is true) or the - * current value. - */ - do { - struct v4l2_ctrl_ref *ref = helpers[idx].ref; - - if (is_default) - ret = def_to_user(cs->controls + idx, ref->ctrl); - else if (is_request && ref->valid_p_req) - ret = req_to_user(cs->controls + idx, ref); - else if (is_volatile) - ret = new_to_user(cs->controls + idx, ref->ctrl); - else - ret = cur_to_user(cs->controls + idx, ref->ctrl); - idx = helpers[idx].next; - } while (!ret && idx); - v4l2_ctrl_unlock(master); } @@ -4426,6 +4426,8 @@ void v4l2_ctrl_request_complete(struct media_request *req, unsigned int i; if (ctrl->flags & V4L2_CTRL_FLAG_VOLATILE) { + ref->req = ref; + v4l2_ctrl_lock(master); /* g_volatile_ctrl will update the current control values */ for (i = 0; i < master->ncontrols; i++) @@ -4435,12 +4437,21 @@ void v4l2_ctrl_request_complete(struct media_request *req, v4l2_ctrl_unlock(master); continue; } - if (ref->valid_p_req) + if (ref->req == ref) continue; - /* Copy the current control value into the request */ v4l2_ctrl_lock(ctrl); - cur_to_req(ref); + if (ref->req) { + ptr_to_ptr(ctrl, ref->req->p_req, ref->p_req); + } else { + ptr_to_ptr(ctrl, ctrl->p_cur, ref->p_req); + /* + * Set ref->req to ensure that when userspace wants to + * obtain the controls of this request it will take + * this value and not the current value of the control. + */ + ref->req = ref; + } v4l2_ctrl_unlock(ctrl); } @@ -4505,7 +4516,7 @@ int v4l2_ctrl_request_setup(struct media_request *req, struct v4l2_ctrl_ref *r = find_ref(hdl, master->cluster[i]->id); - if (r->valid_p_req) { + if (r->req && r == r->req) { have_new_data = true; break; } diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 9ecbb98908f0..cb25f345e9ad 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -303,14 +303,12 @@ struct v4l2_ctrl { * the control has been applied. This prevents applying controls * from a cluster with multiple controls twice (when the first * control of a cluster is applied, they all are). - * @valid_p_req: If set, then p_req contains the control value for the request. + * @req: If set, this refers to another request that sets this control. * @p_req: If the control handler containing this control reference * is bound to a media request, then this points to the - * value of the control that must be applied when the request + * value of the control that should be applied when the request * is executed, or to the value of the control at the time - * that the request was completed. If @valid_p_req is false, - * then this control was never set for this request and the - * control will not be updated when this request is applied. + * that the request was completed. * * Each control handler has a list of these refs. The list_head is used to * keep a sorted-by-control-ID list of all controls, while the next pointer @@ -323,7 +321,7 @@ struct v4l2_ctrl_ref { struct v4l2_ctrl_helper *helper; bool from_other_dev; bool req_done; - bool valid_p_req; + struct v4l2_ctrl_ref *req; union v4l2_ctrl_ptr p_req; }; @@ -350,7 +348,7 @@ struct v4l2_ctrl_ref { * @error: The error code of the first failed control addition. * @request_is_queued: True if the request was queued. * @requests: List to keep track of open control handler request objects. - * For the parent control handler (@req_obj.ops == NULL) this + * For the parent control handler (@req_obj.req == NULL) this * is the list header. When the parent control handler is * removed, it has to unbind and put all these requests since * they refer to the parent.