From cc6eddcd37ce6be403b5820ffd84cb84b33b653f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 May 2018 04:54:34 -0400 Subject: media: v4l2-dev: lock req_queue_mutex We need to serialize streamon/off with queueing new requests. These ioctls may trigger the cancellation of a streaming operation, and that should not be mixed with queuing a new request at the same time. Finally close() needs this lock since that too can trigger the cancellation of a streaming operation. We take the req_queue_mutex here before any other locks since it is a very high-level lock. Signed-off-by: Hans Verkuil Signed-off-by: Sakari Ailus Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dev.c | 18 ++++++++++++++++-- drivers/media/v4l2-core/v4l2-ioctl.c | 22 +++++++++++++++++++++- 2 files changed, 37 insertions(+), 3 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 69e775930fc4..feb749aaaa42 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -444,8 +444,22 @@ static int v4l2_release(struct inode *inode, struct file *filp) struct video_device *vdev = video_devdata(filp); int ret = 0; - if (vdev->fops->release) - ret = vdev->fops->release(filp); + /* + * We need to serialize the release() with queueing new requests. + * The release() may trigger the cancellation of a streaming + * operation, and that should not be mixed with queueing a new + * request at the same time. + */ + if (vdev->fops->release) { + if (v4l2_device_supports_requests(vdev->v4l2_dev)) { + mutex_lock(&vdev->v4l2_dev->mdev->req_queue_mutex); + ret = vdev->fops->release(filp); + mutex_unlock(&vdev->v4l2_dev->mdev->req_queue_mutex); + } else { + ret = vdev->fops->release(filp); + } + } + if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP) dprintk("%s: release\n", video_device_node_name(vdev)); diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 54afc9c7ee6e..ea475d833dd6 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -2780,6 +2780,7 @@ static long __video_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *vfd = video_devdata(file); + struct mutex *req_queue_lock = NULL; struct mutex *lock; /* ioctl serialization mutex */ const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops; bool write_only = false; @@ -2799,10 +2800,27 @@ static long __video_do_ioctl(struct file *file, if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) vfh = file->private_data; + /* + * We need to serialize streamon/off with queueing new requests. + * These ioctls may trigger the cancellation of a streaming + * operation, and that should not be mixed with queueing a new + * request at the same time. + */ + if (v4l2_device_supports_requests(vfd->v4l2_dev) && + (cmd == VIDIOC_STREAMON || cmd == VIDIOC_STREAMOFF)) { + req_queue_lock = &vfd->v4l2_dev->mdev->req_queue_mutex; + + if (mutex_lock_interruptible(req_queue_lock)) + return -ERESTARTSYS; + } + lock = v4l2_ioctl_get_lock(vfd, vfh, cmd, arg); - if (lock && mutex_lock_interruptible(lock)) + if (lock && mutex_lock_interruptible(lock)) { + if (req_queue_lock) + mutex_unlock(req_queue_lock); return -ERESTARTSYS; + } if (!video_is_registered(vfd)) { ret = -ENODEV; @@ -2861,6 +2879,8 @@ done: unlock: if (lock) mutex_unlock(lock); + if (req_queue_lock) + mutex_unlock(req_queue_lock); return ret; } -- cgit v1.2.3 From f23317adf6a726b9dbedbe3a0363846f597cc0e8 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Mon, 21 May 2018 04:54:35 -0400 Subject: media: videodev2.h: add request_fd field to v4l2_ext_controls If 'which' is V4L2_CTRL_WHICH_REQUEST_VAL, then the 'request_fd' field can be used to specify a request for the G/S/TRY_EXT_CTRLS ioctls. Signed-off-by: Alexandre Courbot Signed-off-by: Hans Verkuil Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 5 ++++- drivers/media/v4l2-core/v4l2-ioctl.c | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index 6481212fda77..dcce86c1fe40 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -834,7 +834,8 @@ struct v4l2_ext_controls32 { __u32 which; __u32 count; __u32 error_idx; - __u32 reserved[2]; + __s32 request_fd; + __u32 reserved[1]; compat_caddr_t controls; /* actually struct v4l2_ext_control32 * */ }; @@ -909,6 +910,7 @@ static int get_v4l2_ext_controls32(struct file *file, get_user(count, &p32->count) || put_user(count, &p64->count) || assign_in_user(&p64->error_idx, &p32->error_idx) || + assign_in_user(&p64->request_fd, &p32->request_fd) || copy_in_user(p64->reserved, p32->reserved, sizeof(p64->reserved))) return -EFAULT; @@ -974,6 +976,7 @@ static int put_v4l2_ext_controls32(struct file *file, get_user(count, &p64->count) || put_user(count, &p32->count) || assign_in_user(&p32->error_idx, &p64->error_idx) || + assign_in_user(&p32->request_fd, &p64->request_fd) || copy_in_user(p32->reserved, p64->reserved, sizeof(p32->reserved)) || get_user(kcontrols, &p64->controls)) return -EFAULT; diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index ea475d833dd6..03241d6b7ef8 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -590,8 +590,8 @@ static void v4l_print_ext_controls(const void *arg, bool write_only) const struct v4l2_ext_controls *p = arg; int i; - pr_cont("which=0x%x, count=%d, error_idx=%d", - p->which, p->count, p->error_idx); + pr_cont("which=0x%x, count=%d, error_idx=%d, request_fd=%d", + p->which, p->count, p->error_idx, p->request_fd); for (i = 0; i < p->count; i++) { if (!p->controls[i].size) pr_cont(", id/val=0x%x/0x%x", @@ -907,7 +907,7 @@ static int check_ext_ctrls(struct v4l2_ext_controls *c, int allow_priv) __u32 i; /* zero the reserved fields */ - c->reserved[0] = c->reserved[1] = 0; + c->reserved[0] = 0; for (i = 0; i < c->count; i++) c->controls[i].reserved2[0] = 0; -- cgit v1.2.3 From da1b1aeac1aced231ac85329112a592dc14d173a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 May 2018 04:54:36 -0400 Subject: media: v4l2-ctrls: v4l2_ctrl_add_handler: add from_other_dev Add a 'bool from_other_dev' argument: set to true if the two handlers refer to different devices (e.g. it is true when inheriting controls from a subdev into a main v4l2 bridge driver). This will be used later when implementing support for the request API since we need to skip such controls. Signed-off-by: Hans Verkuil Signed-off-by: Alexandre Courbot Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 11 +++++++---- drivers/media/v4l2-core/v4l2-device.c | 3 ++- 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 599c1cbff3b9..404291f00715 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -2016,7 +2016,8 @@ EXPORT_SYMBOL(v4l2_ctrl_find); /* Allocate a new v4l2_ctrl_ref and hook it into the handler. */ static int handler_new_ref(struct v4l2_ctrl_handler *hdl, - struct v4l2_ctrl *ctrl) + struct v4l2_ctrl *ctrl, + bool from_other_dev) { struct v4l2_ctrl_ref *ref; struct v4l2_ctrl_ref *new_ref; @@ -2040,6 +2041,7 @@ static int handler_new_ref(struct v4l2_ctrl_handler *hdl, if (!new_ref) return handler_set_err(hdl, -ENOMEM); new_ref->ctrl = ctrl; + new_ref->from_other_dev = from_other_dev; if (ctrl->handler == hdl) { /* By default each control starts in a cluster of its own. new_ref->ctrl is basically a cluster array with one @@ -2220,7 +2222,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, ctrl->type_ops->init(ctrl, idx, ctrl->p_new); } - if (handler_new_ref(hdl, ctrl)) { + if (handler_new_ref(hdl, ctrl, false)) { kvfree(ctrl); return NULL; } @@ -2389,7 +2391,8 @@ EXPORT_SYMBOL(v4l2_ctrl_new_int_menu); /* Add the controls from another handler to our own. */ int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl, struct v4l2_ctrl_handler *add, - bool (*filter)(const struct v4l2_ctrl *ctrl)) + bool (*filter)(const struct v4l2_ctrl *ctrl), + bool from_other_dev) { struct v4l2_ctrl_ref *ref; int ret = 0; @@ -2412,7 +2415,7 @@ int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl, /* Filter any unwanted controls */ if (filter && !filter(ctrl)) continue; - ret = handler_new_ref(hdl, ctrl); + ret = handler_new_ref(hdl, ctrl, from_other_dev); if (ret) break; } diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c index 3940e55c72f1..5189fb9f741f 100644 --- a/drivers/media/v4l2-core/v4l2-device.c +++ b/drivers/media/v4l2-core/v4l2-device.c @@ -178,7 +178,8 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, sd->v4l2_dev = v4l2_dev; /* This just returns 0 if either of the two args is NULL */ - err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler, NULL); + err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler, + NULL, true); if (err) goto error_module; -- cgit v1.2.3 From 52beeddb68833e02c0923bc46868b347a6ad393c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 May 2018 04:54:37 -0400 Subject: media: v4l2-ctrls: prepare internal structs for request API Embed and initialize a media_request_object in struct v4l2_ctrl_handler. Add a p_req field to struct v4l2_ctrl_ref that will store the request value. Signed-off-by: Hans Verkuil Signed-off-by: Alexandre Courbot Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 404291f00715..b33a8bee82b0 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1901,6 +1901,7 @@ int v4l2_ctrl_handler_init_class(struct v4l2_ctrl_handler *hdl, sizeof(hdl->buckets[0]), GFP_KERNEL | __GFP_ZERO); hdl->error = hdl->buckets ? 0 : -ENOMEM; + media_request_object_init(&hdl->req_obj); return hdl->error; } EXPORT_SYMBOL(v4l2_ctrl_handler_init_class); -- cgit v1.2.3 From 178543a3ba96b7857fcb6cef806ed9f98ea0cca1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 May 2018 04:54:38 -0400 Subject: media: v4l2-ctrls: alloc memory for p_req To store request data the handler_new_ref() allocates memory for it if needed. Signed-off-by: Hans Verkuil Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index b33a8bee82b0..95e60d7ee32b 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -2018,13 +2018,18 @@ EXPORT_SYMBOL(v4l2_ctrl_find); /* Allocate a new v4l2_ctrl_ref and hook it into the handler. */ static int handler_new_ref(struct v4l2_ctrl_handler *hdl, struct v4l2_ctrl *ctrl, - bool from_other_dev) + struct v4l2_ctrl_ref **ctrl_ref, + bool from_other_dev, bool allocate_req) { struct v4l2_ctrl_ref *ref; struct v4l2_ctrl_ref *new_ref; u32 id = ctrl->id; u32 class_ctrl = V4L2_CTRL_ID2WHICH(id) | 1; int bucket = id % hdl->nr_of_buckets; /* which bucket to use */ + unsigned int size_extra_req = 0; + + if (ctrl_ref) + *ctrl_ref = NULL; /* * Automatically add the control class if it is not yet present and @@ -2038,11 +2043,16 @@ static int handler_new_ref(struct v4l2_ctrl_handler *hdl, if (hdl->error) return hdl->error; - new_ref = kzalloc(sizeof(*new_ref), GFP_KERNEL); + if (allocate_req) + size_extra_req = ctrl->elems * ctrl->elem_size; + new_ref = kzalloc(sizeof(*new_ref) + size_extra_req, GFP_KERNEL); if (!new_ref) return handler_set_err(hdl, -ENOMEM); new_ref->ctrl = ctrl; new_ref->from_other_dev = from_other_dev; + if (size_extra_req) + new_ref->p_req.p = &new_ref[1]; + if (ctrl->handler == hdl) { /* By default each control starts in a cluster of its own. new_ref->ctrl is basically a cluster array with one @@ -2082,6 +2092,8 @@ insert_in_hash: /* Insert the control node in the hash */ new_ref->next = hdl->buckets[bucket]; hdl->buckets[bucket] = new_ref; + if (ctrl_ref) + *ctrl_ref = new_ref; unlock: mutex_unlock(hdl->lock); @@ -2223,7 +2235,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, ctrl->type_ops->init(ctrl, idx, ctrl->p_new); } - if (handler_new_ref(hdl, ctrl, false)) { + if (handler_new_ref(hdl, ctrl, NULL, false, false)) { kvfree(ctrl); return NULL; } @@ -2416,7 +2428,7 @@ int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl, /* Filter any unwanted controls */ if (filter && !filter(ctrl)) continue; - ret = handler_new_ref(hdl, ctrl, from_other_dev); + ret = handler_new_ref(hdl, ctrl, NULL, from_other_dev, false); if (ret) break; } -- cgit v1.2.3 From dcea5601dbdfb13be91526eb898c0f9122381032 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 May 2018 04:54:39 -0400 Subject: media: v4l2-ctrls: use ref in helper instead of ctrl The next patch needs the reference to a control instead of the control itself, so change struct v4l2_ctrl_helper accordingly. Signed-off-by: Hans Verkuil Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 95e60d7ee32b..c20e74ba48ab 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -37,8 +37,8 @@ struct v4l2_ctrl_helper { /* Pointer to the control reference of the master control */ struct v4l2_ctrl_ref *mref; - /* The control corresponding to the v4l2_ext_control ID field. */ - struct v4l2_ctrl *ctrl; + /* The control ref corresponding to the v4l2_ext_control ID field. */ + struct v4l2_ctrl_ref *ref; /* v4l2_ext_control index of the next control belonging to the same cluster, or 0 if there isn't any. */ u32 next; @@ -2908,6 +2908,7 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl, ref = find_ref_lock(hdl, id); if (ref == NULL) return -EINVAL; + h->ref = ref; ctrl = ref->ctrl; if (ctrl->flags & V4L2_CTRL_FLAG_DISABLED) return -EINVAL; @@ -2930,7 +2931,6 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl, } /* Store the ref to the master control of the cluster */ h->mref = ref; - h->ctrl = ctrl; /* Initially set next to 0, meaning that there is no other control in this helper array belonging to the same cluster */ @@ -3015,7 +3015,7 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs cs->error_idx = cs->count; for (i = 0; !ret && i < cs->count; i++) - if (helpers[i].ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY) + if (helpers[i].ref->ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY) ret = -EACCES; for (i = 0; !ret && i < cs->count; i++) { @@ -3050,7 +3050,7 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs do { ret = ctrl_to_user(cs->controls + idx, - helpers[idx].ctrl); + helpers[idx].ref->ctrl); idx = helpers[idx].next; } while (!ret && idx); } @@ -3202,7 +3202,7 @@ static int validate_ctrls(struct v4l2_ext_controls *cs, cs->error_idx = cs->count; for (i = 0; i < cs->count; i++) { - struct v4l2_ctrl *ctrl = helpers[i].ctrl; + struct v4l2_ctrl *ctrl = helpers[i].ref->ctrl; union v4l2_ctrl_ptr p_new; cs->error_idx = i; @@ -3314,7 +3314,7 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl, do { /* Check if the auto control is part of the list, and remember the new value. */ - if (helpers[tmp_idx].ctrl == master) + if (helpers[tmp_idx].ref->ctrl == master) new_auto_val = cs->controls[tmp_idx].value; tmp_idx = helpers[tmp_idx].next; } while (tmp_idx); @@ -3327,7 +3327,7 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl, /* Copy the new caller-supplied control values. user_to_new() sets 'is_new' to 1. */ do { - struct v4l2_ctrl *ctrl = helpers[idx].ctrl; + struct v4l2_ctrl *ctrl = helpers[idx].ref->ctrl; ret = user_to_new(cs->controls + idx, ctrl); if (!ret && ctrl->is_ptr) @@ -3343,7 +3343,7 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl, idx = i; do { ret = new_to_user(cs->controls + idx, - helpers[idx].ctrl); + helpers[idx].ref->ctrl); idx = helpers[idx].next; } while (!ret && idx); } -- cgit v1.2.3 From 6fa6f831f0950bf46934e6c3a9766b258a9ea85f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 May 2018 04:54:40 -0400 Subject: media: v4l2-ctrls: add core request support 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 Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 336 ++++++++++++++++++++++++++++++++++- 1 file changed, 330 insertions(+), 6 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index c20e74ba48ab..89e7bfee108f 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1668,6 +1668,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) { @@ -1787,6 +1794,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) @@ -1896,6 +1923,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]), @@ -1916,6 +1946,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) { @@ -2837,6 +2875,123 @@ 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) + 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_hdl = NULL; + struct v4l2_ctrl_ref *ref_ctrl, *ref_ctrl_prev = NULL; + + 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; +} + +static void v4l2_ctrl_request_unbind(struct media_request_object *obj) +{ + struct v4l2_ctrl_handler *hdl = + container_of(obj, struct v4l2_ctrl_handler, req_obj); + + list_del_init(&hdl->requests); + if (hdl->request_is_queued) { + list_del_init(&hdl->requests_queued); + hdl->request_is_queued = false; + } +} + +static void v4l2_ctrl_request_release(struct media_request_object *obj) +{ + struct v4l2_ctrl_handler *hdl = + container_of(obj, struct v4l2_ctrl_handler, req_obj); + + v4l2_ctrl_handler_free(hdl); + kfree(hdl); +} + +static const struct media_request_object_ops req_ops = { + .queue = v4l2_ctrl_request_queue, + .unbind = v4l2_ctrl_request_unbind, + .release = v4l2_ctrl_request_release, +}; + +static int v4l2_ctrl_request_bind(struct media_request *req, + struct v4l2_ctrl_handler *hdl, + struct v4l2_ctrl_handler *from) +{ + int ret; + + ret = v4l2_ctrl_request_clone(hdl, from); + + if (!ret) { + ret = media_request_object_bind(req, &req_ops, + from, false, &hdl->req_obj); + if (!ret) + list_add_tail(&hdl->requests, &from->requests); + } + return ret; +} /* Some general notes on the atomic requirements of VIDIOC_G/TRY/S_EXT_CTRLS: @@ -2898,6 +3053,7 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl, if (cs->which && cs->which != V4L2_CTRL_WHICH_DEF_VAL && + cs->which != V4L2_CTRL_WHICH_REQUEST_VAL && V4L2_CTRL_ID2WHICH(id) != cs->which) return -EINVAL; @@ -2977,13 +3133,12 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl, whether there are any controls at all. */ static int class_check(struct v4l2_ctrl_handler *hdl, u32 which) { - if (which == 0 || which == V4L2_CTRL_WHICH_DEF_VAL) + if (which == 0 || which == V4L2_CTRL_WHICH_DEF_VAL || + which == V4L2_CTRL_WHICH_REQUEST_VAL) return 0; return find_ref_lock(hdl, which | 1) ? 0 : -EINVAL; } - - /* Get extended controls. Allocates the helpers array if needed. */ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs) { @@ -3049,8 +3204,12 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs u32 idx = i; do { - ret = ctrl_to_user(cs->controls + idx, - helpers[idx].ref->ctrl); + 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); } @@ -3336,7 +3495,16 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl, } while (!ret && idx); if (!ret) - ret = try_or_set_cluster(fh, master, set, 0); + ret = try_or_set_cluster(fh, master, + !hdl->req_obj.req && set, 0); + if (!ret && hdl->req_obj.req && set) { + for (j = 0; j < master->ncontrols; j++) { + struct v4l2_ctrl_ref *ref = + find_ref(hdl, master->cluster[j]->id); + + new_to_req(ref); + } + } /* Copy the new values back to userspace. */ if (!ret) { @@ -3463,6 +3631,162 @@ int __v4l2_ctrl_s_ctrl_string(struct v4l2_ctrl *ctrl, const char *s) } EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_string); +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; + + /* + * Note that it is valid if nothing was found. It means + * that this request doesn't have any controls and so just + * wants to leave the controls unchanged. + */ + obj = media_request_object_find(req, &req_ops, main_hdl); + if (!obj) + return; + hdl = container_of(obj, struct v4l2_ctrl_handler, req_obj); + + list_for_each_entry(ref, &hdl->ctrl_refs, node) { + struct v4l2_ctrl *ctrl = ref->ctrl; + struct v4l2_ctrl *master = ctrl->cluster[0]; + 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++) + cur_to_new(master->cluster[i]); + call_op(master, g_volatile_ctrl); + new_to_req(ref); + v4l2_ctrl_unlock(master); + continue; + } + if (ref->req == ref) + continue; + + v4l2_ctrl_lock(ctrl); + 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); + v4l2_ctrl_unlock(ctrl); + } + + WARN_ON(!hdl->request_is_queued); + list_del_init(&hdl->requests_queued); + hdl->request_is_queued = false; + media_request_object_complete(obj); + media_request_object_put(obj); +} +EXPORT_SYMBOL(v4l2_ctrl_request_complete); + +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) + return; + + if (WARN_ON(req->state != MEDIA_REQUEST_STATE_QUEUED)) + return; + + /* + * Note that it is valid if nothing was found. It means + * that this request doesn't have any controls and so just + * wants to leave the controls unchanged. + */ + 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); + + list_for_each_entry(ref, &hdl->ctrl_refs, node) + ref->req_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. + */ + if (ref->req_done || ctrl->type == V4L2_CTRL_TYPE_BUTTON || + (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)) + continue; + + v4l2_ctrl_lock(master); + for (i = 0; i < master->ncontrols; i++) { + if (master->cluster[i]) { + struct v4l2_ctrl_ref *r = + find_ref(hdl, master->cluster[i]->id); + + if (r->req && r == r->req) { + have_new_data = true; + break; + } + } + } + if (!have_new_data) { + v4l2_ctrl_unlock(master); + continue; + } + + for (i = 0; i < master->ncontrols; i++) { + if (master->cluster[i]) { + struct v4l2_ctrl_ref *r = + find_ref(hdl, master->cluster[i]->id); + + req_to_new(r); + master->cluster[i]->is_new = 1; + r->req_done = true; + } + } + /* + * For volatile autoclusters that are currently in auto mode + * we need to discover if it will be set to manual mode. + * If so, then we have to copy the current volatile values + * first since those will become the new manual values (which + * may be overwritten by explicit new values from this set + * of controls). + */ + if (master->is_auto && master->has_volatiles && + !is_cur_manual(master)) { + s32 new_auto_val = *master->p_new.p_s32; + + /* + * If the new value == the manual value, then copy + * the current volatile values. + */ + if (new_auto_val == master->manual_mode_value) + update_from_auto_cluster(master); + } + + try_or_set_cluster(NULL, master, true, 0); + + v4l2_ctrl_unlock(master); + } + + media_request_object_put(obj); +} +EXPORT_SYMBOL(v4l2_ctrl_request_setup); + void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void *priv) { if (ctrl == NULL) -- cgit v1.2.3 From c41e9cff704a06b8cbd9eeea0fdec54fb6d13825 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 May 2018 04:54:42 -0400 Subject: media: v4l2-ctrls: support g/s_ext_ctrls for requests The v4l2_g/s_ext_ctrls functions now support control handlers that represent requests. The v4l2_ctrls_find_req_obj() function is responsible for finding the request from the fd. Signed-off-by: Hans Verkuil Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 138 ++++++++++++++++++++++++++++++++-- drivers/media/v4l2-core/v4l2-ioctl.c | 12 +-- drivers/media/v4l2-core/v4l2-subdev.c | 9 ++- 3 files changed, 142 insertions(+), 17 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 89e7bfee108f..6d34d7a6f235 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -3140,7 +3140,8 @@ static int class_check(struct v4l2_ctrl_handler *hdl, u32 which) } /* Get extended controls. Allocates the helpers array if needed. */ -int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs) +static int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl, + struct v4l2_ext_controls *cs) { struct v4l2_ctrl_helper helper[4]; struct v4l2_ctrl_helper *helpers = helper; @@ -3220,6 +3221,83 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs kvfree(helpers); return ret; } + +static struct media_request_object * +v4l2_ctrls_find_req_obj(struct v4l2_ctrl_handler *hdl, + struct media_request *req, bool set) +{ + struct media_request_object *obj; + struct v4l2_ctrl_handler *new_hdl; + int ret; + + if (IS_ERR(req)) + return ERR_CAST(req); + + if (set && WARN_ON(req->state != MEDIA_REQUEST_STATE_UPDATING)) + return ERR_PTR(-EBUSY); + + obj = media_request_object_find(req, &req_ops, hdl); + if (obj) + return obj; + if (!set) + return ERR_PTR(-ENOENT); + + new_hdl = kzalloc(sizeof(*new_hdl), GFP_KERNEL); + if (!new_hdl) + return ERR_PTR(-ENOMEM); + + obj = &new_hdl->req_obj; + ret = v4l2_ctrl_handler_init(new_hdl, (hdl->nr_of_buckets - 1) * 8); + if (!ret) + ret = v4l2_ctrl_request_bind(req, new_hdl, hdl); + if (ret) { + kfree(new_hdl); + + return ERR_PTR(ret); + } + + media_request_object_get(obj); + return obj; +} + +int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct media_device *mdev, + struct v4l2_ext_controls *cs) +{ + struct media_request_object *obj = NULL; + int ret; + + if (cs->which == V4L2_CTRL_WHICH_REQUEST_VAL) { + struct media_request *req; + + if (!mdev || cs->request_fd < 0) + return -EINVAL; + + req = media_request_get_by_fd(mdev, cs->request_fd); + if (IS_ERR(req)) + return PTR_ERR(req); + + if (req->state != MEDIA_REQUEST_STATE_IDLE && + req->state != MEDIA_REQUEST_STATE_COMPLETE) { + media_request_put(req); + return -EBUSY; + } + + obj = v4l2_ctrls_find_req_obj(hdl, req, false); + /* Reference to the request held through obj */ + media_request_put(req); + if (IS_ERR(obj)) + return PTR_ERR(obj); + + hdl = container_of(obj, struct v4l2_ctrl_handler, + req_obj); + } + + ret = v4l2_g_ext_ctrls_common(hdl, cs); + + if (obj) + media_request_object_put(obj); + return ret; +} EXPORT_SYMBOL(v4l2_g_ext_ctrls); /* Helper function to get a single control */ @@ -3408,9 +3486,9 @@ static void update_from_auto_cluster(struct v4l2_ctrl *master) } /* Try or try-and-set controls */ -static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl, - struct v4l2_ext_controls *cs, - bool set) +static int try_set_ext_ctrls_common(struct v4l2_fh *fh, + struct v4l2_ctrl_handler *hdl, + struct v4l2_ext_controls *cs, bool set) { struct v4l2_ctrl_helper helper[4]; struct v4l2_ctrl_helper *helpers = helper; @@ -3523,16 +3601,60 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl, return ret; } -int v4l2_try_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs) +static int try_set_ext_ctrls(struct v4l2_fh *fh, + struct v4l2_ctrl_handler *hdl, struct media_device *mdev, + struct v4l2_ext_controls *cs, bool set) +{ + struct media_request_object *obj = NULL; + struct media_request *req = NULL; + int ret; + + if (cs->which == V4L2_CTRL_WHICH_REQUEST_VAL) { + if (!mdev || cs->request_fd < 0) + return -EINVAL; + + req = media_request_get_by_fd(mdev, cs->request_fd); + if (IS_ERR(req)) + return PTR_ERR(req); + + ret = media_request_lock_for_update(req); + if (ret) { + media_request_put(req); + return ret; + } + + obj = v4l2_ctrls_find_req_obj(hdl, req, set); + /* Reference to the request held through obj */ + media_request_put(req); + if (IS_ERR(obj)) { + media_request_unlock_for_update(req); + return PTR_ERR(obj); + } + hdl = container_of(obj, struct v4l2_ctrl_handler, + req_obj); + } + + ret = try_set_ext_ctrls_common(fh, hdl, cs, set); + + if (obj) { + media_request_unlock_for_update(obj->req); + media_request_object_put(obj); + } + + return ret; +} + +int v4l2_try_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct media_device *mdev, + struct v4l2_ext_controls *cs) { - return try_set_ext_ctrls(NULL, hdl, cs, false); + return try_set_ext_ctrls(NULL, hdl, mdev, cs, false); } EXPORT_SYMBOL(v4l2_try_ext_ctrls); int v4l2_s_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl, - struct v4l2_ext_controls *cs) + struct media_device *mdev, struct v4l2_ext_controls *cs) { - return try_set_ext_ctrls(fh, hdl, cs, true); + return try_set_ext_ctrls(fh, hdl, mdev, cs, true); } EXPORT_SYMBOL(v4l2_s_ext_ctrls); diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 03241d6b7ef8..20b5145a5254 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -2109,9 +2109,9 @@ static int v4l_g_ext_ctrls(const struct v4l2_ioctl_ops *ops, p->error_idx = p->count; if (vfh && vfh->ctrl_handler) - return v4l2_g_ext_ctrls(vfh->ctrl_handler, p); + return v4l2_g_ext_ctrls(vfh->ctrl_handler, vfd->v4l2_dev->mdev, p); if (vfd->ctrl_handler) - return v4l2_g_ext_ctrls(vfd->ctrl_handler, p); + return v4l2_g_ext_ctrls(vfd->ctrl_handler, vfd->v4l2_dev->mdev, p); if (ops->vidioc_g_ext_ctrls == NULL) return -ENOTTY; return check_ext_ctrls(p, 0) ? ops->vidioc_g_ext_ctrls(file, fh, p) : @@ -2128,9 +2128,9 @@ static int v4l_s_ext_ctrls(const struct v4l2_ioctl_ops *ops, p->error_idx = p->count; if (vfh && vfh->ctrl_handler) - return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, p); + return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, vfd->v4l2_dev->mdev, p); if (vfd->ctrl_handler) - return v4l2_s_ext_ctrls(NULL, vfd->ctrl_handler, p); + return v4l2_s_ext_ctrls(NULL, vfd->ctrl_handler, vfd->v4l2_dev->mdev, p); if (ops->vidioc_s_ext_ctrls == NULL) return -ENOTTY; return check_ext_ctrls(p, 0) ? ops->vidioc_s_ext_ctrls(file, fh, p) : @@ -2147,9 +2147,9 @@ static int v4l_try_ext_ctrls(const struct v4l2_ioctl_ops *ops, p->error_idx = p->count; if (vfh && vfh->ctrl_handler) - return v4l2_try_ext_ctrls(vfh->ctrl_handler, p); + return v4l2_try_ext_ctrls(vfh->ctrl_handler, vfd->v4l2_dev->mdev, p); if (vfd->ctrl_handler) - return v4l2_try_ext_ctrls(vfd->ctrl_handler, p); + return v4l2_try_ext_ctrls(vfd->ctrl_handler, vfd->v4l2_dev->mdev, p); if (ops->vidioc_try_ext_ctrls == NULL) return -ENOTTY; return check_ext_ctrls(p, 0) ? ops->vidioc_try_ext_ctrls(file, fh, p) : diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 2b63fa6b6fc9..0fdbd85532dd 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -222,17 +222,20 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_G_EXT_CTRLS: if (!vfh->ctrl_handler) return -ENOTTY; - return v4l2_g_ext_ctrls(vfh->ctrl_handler, arg); + return v4l2_g_ext_ctrls(vfh->ctrl_handler, + sd->v4l2_dev->mdev, arg); case VIDIOC_S_EXT_CTRLS: if (!vfh->ctrl_handler) return -ENOTTY; - return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, arg); + return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, + sd->v4l2_dev->mdev, arg); case VIDIOC_TRY_EXT_CTRLS: if (!vfh->ctrl_handler) return -ENOTTY; - return v4l2_try_ext_ctrls(vfh->ctrl_handler, arg); + return v4l2_try_ext_ctrls(vfh->ctrl_handler, + sd->v4l2_dev->mdev, arg); case VIDIOC_DQEVENT: if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS)) -- cgit v1.2.3 From 5f611d74c2bd89296aa045609df0e5309ff7ab41 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 10 Jul 2018 04:00:53 -0400 Subject: media: v4l2-ctrls: add v4l2_ctrl_request_hdl_find/put/ctrl_find functions If a driver needs to find/inspect the controls set in a request then it can use these functions. E.g. to check if a required control is set in a request use this in the req_validate() implementation: int res = -EINVAL; hdl = v4l2_ctrl_request_hdl_find(req, parent_hdl); if (hdl) { if (v4l2_ctrl_request_hdl_ctrl_find(hdl, ctrl_id)) res = 0; v4l2_ctrl_request_hdl_put(hdl); } return res; Signed-off-by: Hans Verkuil Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 6d34d7a6f235..a197b60183f5 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -2976,6 +2976,31 @@ static const struct media_request_object_ops req_ops = { .release = v4l2_ctrl_request_release, }; +struct v4l2_ctrl_handler *v4l2_ctrl_request_hdl_find(struct media_request *req, + struct v4l2_ctrl_handler *parent) +{ + struct media_request_object *obj; + + if (WARN_ON(req->state != MEDIA_REQUEST_STATE_VALIDATING && + req->state != MEDIA_REQUEST_STATE_QUEUED)) + return NULL; + + obj = media_request_object_find(req, &req_ops, parent); + if (obj) + return container_of(obj, struct v4l2_ctrl_handler, req_obj); + return NULL; +} +EXPORT_SYMBOL_GPL(v4l2_ctrl_request_hdl_find); + +struct v4l2_ctrl * +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->req == ref) ? ref->ctrl : NULL; +} +EXPORT_SYMBOL_GPL(v4l2_ctrl_request_hdl_ctrl_find); + static int v4l2_ctrl_request_bind(struct media_request *req, struct v4l2_ctrl_handler *hdl, struct v4l2_ctrl_handler *from) -- cgit v1.2.3 From 62fed26ff4338eeccc702799be358bbb1471b76c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 May 2018 04:54:44 -0400 Subject: media: videodev2.h: Add request_fd field to v4l2_buffer When queuing buffers allow for passing the request that should be associated with this buffer. If V4L2_BUF_FLAG_REQUEST_FD is set, then request_fd is used as the file descriptor. If a buffer is stored in a request, but not yet queued to the driver, then V4L2_BUF_FLAG_IN_REQUEST is set. Signed-off-by: Hans Verkuil Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 9 ++++++--- drivers/media/v4l2-core/v4l2-ioctl.c | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index dcce86c1fe40..633465d21d04 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -482,7 +482,7 @@ struct v4l2_buffer32 { } m; __u32 length; __u32 reserved2; - __u32 reserved; + __s32 request_fd; }; static int get_v4l2_plane32(struct v4l2_plane __user *p64, @@ -581,6 +581,7 @@ static int get_v4l2_buffer32(struct v4l2_buffer __user *p64, { u32 type; u32 length; + s32 request_fd; enum v4l2_memory memory; struct v4l2_plane32 __user *uplane32; struct v4l2_plane __user *uplane; @@ -595,7 +596,9 @@ static int get_v4l2_buffer32(struct v4l2_buffer __user *p64, get_user(memory, &p32->memory) || put_user(memory, &p64->memory) || get_user(length, &p32->length) || - put_user(length, &p64->length)) + put_user(length, &p64->length) || + get_user(request_fd, &p32->request_fd) || + put_user(request_fd, &p64->request_fd)) return -EFAULT; if (V4L2_TYPE_IS_OUTPUT(type)) @@ -699,7 +702,7 @@ static int put_v4l2_buffer32(struct v4l2_buffer __user *p64, copy_in_user(&p32->timecode, &p64->timecode, sizeof(p64->timecode)) || assign_in_user(&p32->sequence, &p64->sequence) || assign_in_user(&p32->reserved2, &p64->reserved2) || - assign_in_user(&p32->reserved, &p64->reserved) || + assign_in_user(&p32->request_fd, &p64->request_fd) || get_user(length, &p64->length) || put_user(length, &p32->length)) return -EFAULT; diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 20b5145a5254..2a84ca9e328a 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -474,13 +474,13 @@ static void v4l_print_buffer(const void *arg, bool write_only) const struct v4l2_plane *plane; int i; - pr_cont("%02ld:%02d:%02d.%08ld index=%d, type=%s, flags=0x%08x, field=%s, sequence=%d, memory=%s", + pr_cont("%02ld:%02d:%02d.%08ld index=%d, type=%s, request_fd=%d, flags=0x%08x, field=%s, sequence=%d, memory=%s", p->timestamp.tv_sec / 3600, (int)(p->timestamp.tv_sec / 60) % 60, (int)(p->timestamp.tv_sec % 60), (long)p->timestamp.tv_usec, p->index, - prt_names(p->type, v4l2_type_names), + prt_names(p->type, v4l2_type_names), p->request_fd, p->flags, prt_names(p->field, v4l2_field_names), p->sequence, prt_names(p->memory, v4l2_memory_names)); -- cgit v1.2.3 From 394dc588809158826e2877adb670391829f91c63 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 May 2018 02:46:22 -0400 Subject: media: videobuf2-v4l2: integrate with media requests This implements the V4L2 part of the request support. The main change is that vb2_qbuf and vb2_prepare_buf now have a new media_device pointer. This required changes to several drivers that did not use the vb2_ioctl_qbuf/prepare_buf helper functions. Signed-off-by: Hans Verkuil Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index ce9bd1b91210..4de8fa163fd3 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -473,11 +473,12 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_querybuf); int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct v4l2_buffer *buf) { + struct video_device *vdev = video_devdata(file); struct vb2_queue *vq; int ret; vq = v4l2_m2m_get_vq(m2m_ctx, buf->type); - ret = vb2_qbuf(vq, buf); + ret = vb2_qbuf(vq, vdev->v4l2_dev->mdev, buf); if (!ret) v4l2_m2m_try_schedule(m2m_ctx); @@ -498,11 +499,12 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_dqbuf); int v4l2_m2m_prepare_buf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct v4l2_buffer *buf) { + struct video_device *vdev = video_devdata(file); struct vb2_queue *vq; int ret; vq = v4l2_m2m_get_vq(m2m_ctx, buf->type); - ret = vb2_prepare_buf(vq, buf); + ret = vb2_prepare_buf(vq, vdev->v4l2_dev->mdev, buf); if (!ret) v4l2_m2m_try_schedule(m2m_ctx); -- cgit v1.2.3 From 803a7ab758252599ddadf5fe9326acee7340d9b6 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 May 2018 04:54:53 -0400 Subject: media: v4l2-mem2mem: add vb2_m2m_request_queue For mem2mem devices we have to make sure that v4l2_m2m_try_schedule() is called whenever a request is queued. We do that by creating a vb2_m2m_request_queue() helper that should be used instead of the 'normal' vb2_request_queue() helper. The m2m helper function will call v4l2_m2m_try_schedule() as needed. In addition we also avoid calling v4l2_m2m_try_schedule() when preparing or queueing a buffer for a request since that is no longer needed. Instead this helper function will do that when the request is actually queued. Signed-off-by: Hans Verkuil Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 63 +++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 8 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 4de8fa163fd3..d7806db222d8 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -387,7 +387,7 @@ static void v4l2_m2m_cancel_job(struct v4l2_m2m_ctx *m2m_ctx) spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); if (m2m_dev->m2m_ops->job_abort) m2m_dev->m2m_ops->job_abort(m2m_ctx->priv); - dprintk("m2m_ctx %p running, will wait to complete", m2m_ctx); + dprintk("m2m_ctx %p running, will wait to complete\n", m2m_ctx); wait_event(m2m_ctx->finished, !(m2m_ctx->job_flags & TRANS_RUNNING)); } else if (m2m_ctx->job_flags & TRANS_QUEUED) { @@ -478,8 +478,14 @@ int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, int ret; vq = v4l2_m2m_get_vq(m2m_ctx, buf->type); + if (!V4L2_TYPE_IS_OUTPUT(vq->type) && + (buf->flags & V4L2_BUF_FLAG_REQUEST_FD)) { + dprintk("%s: requests cannot be used with capture buffers\n", + __func__); + return -EPERM; + } ret = vb2_qbuf(vq, vdev->v4l2_dev->mdev, buf); - if (!ret) + if (!ret && !(buf->flags & V4L2_BUF_FLAG_IN_REQUEST)) v4l2_m2m_try_schedule(m2m_ctx); return ret; @@ -501,14 +507,9 @@ int v4l2_m2m_prepare_buf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, { struct video_device *vdev = video_devdata(file); struct vb2_queue *vq; - int ret; vq = v4l2_m2m_get_vq(m2m_ctx, buf->type); - ret = vb2_prepare_buf(vq, vdev->v4l2_dev->mdev, buf); - if (!ret) - v4l2_m2m_try_schedule(m2m_ctx); - - return ret; + return vb2_prepare_buf(vq, vdev->v4l2_dev->mdev, buf); } EXPORT_SYMBOL_GPL(v4l2_m2m_prepare_buf); @@ -952,6 +953,52 @@ void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, } EXPORT_SYMBOL_GPL(v4l2_m2m_buf_queue); +void vb2_m2m_request_queue(struct media_request *req) +{ + struct media_request_object *obj, *obj_safe; + struct v4l2_m2m_ctx *m2m_ctx = NULL; + + /* + * Queue all objects. Note that buffer objects are at the end of the + * objects list, after all other object types. Once buffer objects + * are queued, the driver might delete them immediately (if the driver + * processes the buffer at once), so we have to use + * list_for_each_entry_safe() to handle the case where the object we + * queue is deleted. + */ + list_for_each_entry_safe(obj, obj_safe, &req->objects, list) { + struct v4l2_m2m_ctx *m2m_ctx_obj; + struct vb2_buffer *vb; + + if (!obj->ops->queue) + continue; + + if (vb2_request_object_is_buffer(obj)) { + /* Sanity checks */ + vb = container_of(obj, struct vb2_buffer, req_obj); + WARN_ON(!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)); + m2m_ctx_obj = container_of(vb->vb2_queue, + struct v4l2_m2m_ctx, + out_q_ctx.q); + WARN_ON(m2m_ctx && m2m_ctx_obj != m2m_ctx); + m2m_ctx = m2m_ctx_obj; + } + + /* + * The buffer we queue here can in theory be immediately + * unbound, hence the use of list_for_each_entry_safe() + * above and why we call the queue op last. + */ + obj->ops->queue(obj); + } + + WARN_ON(!m2m_ctx); + + if (m2m_ctx) + v4l2_m2m_try_schedule(m2m_ctx); +} +EXPORT_SYMBOL_GPL(vb2_m2m_request_queue); + /* Videobuf2 ioctl helpers */ int v4l2_m2m_ioctl_reqbufs(struct file *file, void *priv, -- cgit v1.2.3 From b6b84557eca71ae0631be1567fec85870a2c1b93 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 23 Aug 2018 06:33:49 -0400 Subject: media: v4l2-ctrls: return -EACCES if request wasn't completed For now (this might be relaxed in the future) we do not allow getting controls from a request that isn't completed. In that case we return -EACCES. Update the documentation accordingly. Signed-off-by: Hans Verkuil Reviewed-by: Tomasz Figa Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index a197b60183f5..ccaf3068de6d 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -3301,10 +3301,9 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct media_device *mdev, if (IS_ERR(req)) return PTR_ERR(req); - if (req->state != MEDIA_REQUEST_STATE_IDLE && - req->state != MEDIA_REQUEST_STATE_COMPLETE) { + if (req->state != MEDIA_REQUEST_STATE_COMPLETE) { media_request_put(req); - return -EBUSY; + return -EACCES; } obj = v4l2_ctrls_find_req_obj(hdl, req, false); -- cgit v1.2.3 From e5079cf11373e4cc98be8b1072aece429eb2d4d2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 23 Aug 2018 10:18:35 -0400 Subject: media: vb2: set reqbufs/create_bufs capabilities Set the capabilities field of v4l2_requestbuffers and v4l2_create_buffers. The various mapping modes were easy, but for signaling the request capability a new 'supports_requests' bitfield was added to videobuf2-core.h (and set in vim2m and vivid). Drivers have to set this bitfield for any queue where requests are supported. Signed-off-by: Hans Verkuil Reviewed-by: Tomasz Figa Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 4 +++- drivers/media/v4l2-core/v4l2-ioctl.c | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index 633465d21d04..0028e0be6b5b 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -251,7 +251,8 @@ struct v4l2_create_buffers32 { __u32 count; __u32 memory; /* enum v4l2_memory */ struct v4l2_format32 format; - __u32 reserved[8]; + __u32 capabilities; + __u32 reserved[7]; }; static int __bufsize_v4l2_format(struct v4l2_format32 __user *p32, u32 *size) @@ -411,6 +412,7 @@ static int put_v4l2_create32(struct v4l2_create_buffers __user *p64, if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) || copy_in_user(p32, p64, offsetof(struct v4l2_create_buffers32, format)) || + assign_in_user(&p32->capabilities, &p64->capabilities) || copy_in_user(p32->reserved, p64->reserved, sizeof(p64->reserved))) return -EFAULT; return __put_v4l2_format32(&p64->format, &p32->format); diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 2a84ca9e328a..87dba0b9c0a7 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1877,7 +1877,7 @@ static int v4l_reqbufs(const struct v4l2_ioctl_ops *ops, if (ret) return ret; - CLEAR_AFTER_FIELD(p, memory); + CLEAR_AFTER_FIELD(p, capabilities); return ops->vidioc_reqbufs(file, fh, p); } @@ -1918,7 +1918,7 @@ static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops, if (ret) return ret; - CLEAR_AFTER_FIELD(create, format); + CLEAR_AFTER_FIELD(create, capabilities); v4l_sanitize_format(&create->format); -- cgit v1.2.3 From ca6c163399a9f1913efdbeab654a185b3cde03c4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 27 Aug 2018 11:20:10 -0400 Subject: media: v4l2-ctrls: use media_request_(un)lock_for_access When getting control values from a completed request, we have to protect the request against being re-inited when it is being accessed by calling media_request_(un)lock_for_access. Signed-off-by: Hans Verkuil Reviewed-by: Tomasz Figa Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index ccaf3068de6d..cc266a4a6e88 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -3289,11 +3289,10 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct media_device *mdev, struct v4l2_ext_controls *cs) { struct media_request_object *obj = NULL; + struct media_request *req = NULL; int ret; if (cs->which == V4L2_CTRL_WHICH_REQUEST_VAL) { - struct media_request *req; - if (!mdev || cs->request_fd < 0) return -EINVAL; @@ -3306,11 +3305,18 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct media_device *mdev, return -EACCES; } + ret = media_request_lock_for_access(req); + if (ret) { + media_request_put(req); + return ret; + } + obj = v4l2_ctrls_find_req_obj(hdl, req, false); - /* Reference to the request held through obj */ - media_request_put(req); - if (IS_ERR(obj)) + if (IS_ERR(obj)) { + media_request_unlock_for_access(req); + media_request_put(req); return PTR_ERR(obj); + } hdl = container_of(obj, struct v4l2_ctrl_handler, req_obj); @@ -3318,8 +3324,11 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct media_device *mdev, ret = v4l2_g_ext_ctrls_common(hdl, cs); - if (obj) + if (obj) { + media_request_unlock_for_access(req); media_request_object_put(obj); + media_request_put(req); + } return ret; } EXPORT_SYMBOL(v4l2_g_ext_ctrls); -- cgit v1.2.3 From ffda0b4c2481b2a0faf634abccb1da0d0ba4eddb Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 27 Aug 2018 11:23:41 -0400 Subject: media: v4l2-ctrls: improve media_request_(un)lock_for_update The request reference count was decreased again once a reference to the request object was taken. Postpone this until we finished using the object. In theory I think it is possible that the request_fd can be closed by the application from another thread. In that case when request_put is called the whole request would be freed. It's highly unlikely, but let's just be safe and fix this potential race condition. Signed-off-by: Hans Verkuil Reviewed-by: Tomasz Figa Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index cc266a4a6e88..95d065d54308 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -3657,10 +3657,9 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, } obj = v4l2_ctrls_find_req_obj(hdl, req, set); - /* Reference to the request held through obj */ - media_request_put(req); if (IS_ERR(obj)) { media_request_unlock_for_update(req); + media_request_put(req); return PTR_ERR(obj); } hdl = container_of(obj, struct v4l2_ctrl_handler, @@ -3670,8 +3669,9 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, ret = try_set_ext_ctrls_common(fh, hdl, cs, set); if (obj) { - media_request_unlock_for_update(obj->req); + media_request_unlock_for_update(req); media_request_object_put(obj); + media_request_put(req); } return ret; -- cgit v1.2.3 From 9e39b43612e5cc496b02784a8560ded1ebb9e3af Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 14 Sep 2018 05:57:51 -0400 Subject: media: v4l2-compat-ioctl32.c: add missing documentation for a field Fix this sparse warning: drivers/media/v4l2-core/v4l2-compat-ioctl32.c:256: warning: Function parameter or member 'capabilities' not described in 'v4l2_create_buffers32' Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index 0028e0be6b5b..f4325329fbd6 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -244,6 +244,7 @@ struct v4l2_format32 { * return: number of created buffers * @memory: buffer memory type * @format: frame format, for which buffers are requested + * @capabilities: capabilities of this buffer type. * @reserved: future extensions */ struct v4l2_create_buffers32 { -- cgit v1.2.3 From 7390ba4397c1ed069de3b7f88c3f3821d648a582 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 14 Sep 2018 05:56:28 -0400 Subject: media: v4l2-ctrls.c: initialize an error return code with zero Fix this smatch error: drivers/media/v4l2-core/v4l2-ctrls.c:2971 v4l2_ctrl_request_clone() error: uninitialized symbol 'err'. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 95d065d54308..5310ac857e83 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -2879,7 +2879,7 @@ static int v4l2_ctrl_request_clone(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_handler *from) { struct v4l2_ctrl_ref *ref; - int err; + int err = 0; if (WARN_ON(!hdl || hdl == from)) return -EINVAL; -- cgit v1.2.3 From c27bb30e7b6d385c5bff26406089377d678f1a1d Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Thu, 13 Sep 2018 10:51:52 -0400 Subject: media: v4l: Add definitions for MPEG-2 slice format and metadata Stateless video decoding engines require both the MPEG-2 slices and associated metadata from the video stream in order to decode frames. This introduces definitions for a new pixel format, describing buffers with MPEG-2 slice data, as well as control structure sfor passing the frame metadata to drivers. This is based on work from both Florent Revest and Hugues Fruchet. Signed-off-by: Paul Kocialkowski Signed-off-by: Maxime Ripard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 63 ++++++++++++++++++++++++++++++++++++ drivers/media/v4l2-core/v4l2-ioctl.c | 1 + 2 files changed, 64 insertions(+) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 5310ac857e83..65e3cf838ac7 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -844,6 +844,8 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_MV_V_SEARCH_RANGE: return "Vertical MV Search Range"; case V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER: return "Repeat Sequence Header"; case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME: return "Force Key Frame"; + case V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS: return "MPEG-2 Slice Parameters"; + case V4L2_CID_MPEG_VIDEO_MPEG2_QUANTIZATION: return "MPEG-2 Quantization Matrices"; /* VPX controls */ case V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS: return "VPX Number of Partitions"; @@ -1292,6 +1294,12 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_RDS_TX_ALT_FREQS: *type = V4L2_CTRL_TYPE_U32; break; + case V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS: + *type = V4L2_CTRL_TYPE_MPEG2_SLICE_PARAMS; + break; + case V4L2_CID_MPEG_VIDEO_MPEG2_QUANTIZATION: + *type = V4L2_CTRL_TYPE_MPEG2_QUANTIZATION; + break; default: *type = V4L2_CTRL_TYPE_INTEGER; break; @@ -1550,6 +1558,7 @@ static void std_log(const struct v4l2_ctrl *ctrl) static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx, union v4l2_ctrl_ptr ptr) { + struct v4l2_ctrl_mpeg2_slice_params *p_mpeg2_slice_params; size_t len; u64 offset; s64 val; @@ -1612,6 +1621,54 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx, return -ERANGE; return 0; + case V4L2_CTRL_TYPE_MPEG2_SLICE_PARAMS: + p_mpeg2_slice_params = ptr.p; + + switch (p_mpeg2_slice_params->sequence.chroma_format) { + case 1: /* 4:2:0 */ + case 2: /* 4:2:2 */ + case 3: /* 4:4:4 */ + break; + default: + return -EINVAL; + } + + switch (p_mpeg2_slice_params->picture.intra_dc_precision) { + case 0: /* 8 bits */ + case 1: /* 9 bits */ + case 11: /* 11 bits */ + break; + default: + return -EINVAL; + } + + switch (p_mpeg2_slice_params->picture.picture_structure) { + case 1: /* interlaced top field */ + case 2: /* interlaced bottom field */ + case 3: /* progressive */ + break; + default: + return -EINVAL; + } + + switch (p_mpeg2_slice_params->picture.picture_coding_type) { + case V4L2_MPEG2_PICTURE_CODING_TYPE_I: + case V4L2_MPEG2_PICTURE_CODING_TYPE_P: + case V4L2_MPEG2_PICTURE_CODING_TYPE_B: + break; + default: + return -EINVAL; + } + + if (p_mpeg2_slice_params->backward_ref_index >= VIDEO_MAX_FRAME || + p_mpeg2_slice_params->forward_ref_index >= VIDEO_MAX_FRAME) + return -EINVAL; + + return 0; + + case V4L2_CTRL_TYPE_MPEG2_QUANTIZATION: + return 0; + default: return -EINVAL; } @@ -2186,6 +2243,12 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, case V4L2_CTRL_TYPE_U32: elem_size = sizeof(u32); break; + case V4L2_CTRL_TYPE_MPEG2_SLICE_PARAMS: + elem_size = sizeof(struct v4l2_ctrl_mpeg2_slice_params); + break; + case V4L2_CTRL_TYPE_MPEG2_QUANTIZATION: + elem_size = sizeof(struct v4l2_ctrl_mpeg2_quantization); + break; default: if (type < V4L2_CTRL_COMPOUND_TYPES) elem_size = sizeof(s32); diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 87dba0b9c0a7..1a8feaf6c3f7 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1309,6 +1309,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_H263: descr = "H.263"; break; case V4L2_PIX_FMT_MPEG1: descr = "MPEG-1 ES"; break; case V4L2_PIX_FMT_MPEG2: descr = "MPEG-2 ES"; break; + case V4L2_PIX_FMT_MPEG2_SLICE: descr = "MPEG-2 Parsed Slice Data"; break; case V4L2_PIX_FMT_MPEG4: descr = "MPEG-4 part 2 ES"; break; case V4L2_PIX_FMT_XVID: descr = "Xvid"; break; case V4L2_PIX_FMT_VC1_ANNEX_G: descr = "VC-1 (SMPTE 412M Annex G)"; break; -- cgit v1.2.3 From 36cf35b7864002c2601e4bda4d78d5622ad92544 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Thu, 13 Sep 2018 10:51:53 -0400 Subject: media: v4l: Add definition for the Sunxi tiled NV12 format This introduces support for the Sunxi tiled NV12 format, where each component of the YUV frame is divided into macroblocks. Hence, the size of each plane requires specific alignment. The pixels inside each macroblock are coded in linear order (line after line from top to bottom). This tiled NV12 format is used by the video engine on Allwinner platforms: it is the default format for decoded frames (and the only one available in the oldest supported platforms). Signed-off-by: Paul Kocialkowski Signed-off-by: Maxime Ripard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 1a8feaf6c3f7..c148c44caffb 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1337,6 +1337,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_SE401: descr = "GSPCA SE401"; break; case V4L2_PIX_FMT_S5C_UYVY_JPG: descr = "S5C73MX interleaved UYVY/JPEG"; break; case V4L2_PIX_FMT_MT21C: descr = "Mediatek Compressed Format"; break; + case V4L2_PIX_FMT_SUNXI_TILED_NV12: descr = "Sunxi Tiled NV12 Format"; break; default: WARN(1, "Unknown pixelformat 0x%08x\n", fmt->pixelformat); if (fmt->description[0]) -- cgit v1.2.3