diff options
author | Hans de Goede <hdegoede@redhat.com> | 2012-04-08 19:59:51 +0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-05-07 23:35:05 +0400 |
commit | b4012002f3a3983ba2797fde1613691d7f287048 (patch) | |
tree | 149ff2a22013fb77efd8bb374b9069083f4d5cd5 /drivers/media/video/uvc/uvc_ctrl.c | |
parent | cb74d482f81bf6a3ef3e29cb228c917e371773f2 (diff) | |
download | linux-b4012002f3a3983ba2797fde1613691d7f287048.tar.xz |
[media] uvcvideo: Add support for control events
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/uvc/uvc_ctrl.c')
-rw-r--r-- | drivers/media/video/uvc/uvc_ctrl.c | 121 |
1 files changed, 120 insertions, 1 deletions
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 0c27cc154eaf..f15a437e5861 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -21,6 +21,7 @@ #include <linux/vmalloc.h> #include <linux/wait.h> #include <linux/atomic.h> +#include <media/v4l2-ctrls.h> #include "uvcvideo.h" @@ -1102,6 +1103,117 @@ done: return ret; } +/* -------------------------------------------------------------------------- + * Ctrl event handling + */ + +static void uvc_ctrl_fill_event(struct uvc_video_chain *chain, + struct v4l2_event *ev, + struct uvc_control *ctrl, + struct uvc_control_mapping *mapping, + s32 value, u32 changes) +{ + struct v4l2_queryctrl v4l2_ctrl; + + __uvc_query_v4l2_ctrl(chain, ctrl, mapping, &v4l2_ctrl); + + memset(ev->reserved, 0, sizeof(ev->reserved)); + ev->type = V4L2_EVENT_CTRL; + ev->id = v4l2_ctrl.id; + ev->u.ctrl.value = value; + ev->u.ctrl.changes = changes; + ev->u.ctrl.type = v4l2_ctrl.type; + ev->u.ctrl.flags = v4l2_ctrl.flags; + ev->u.ctrl.minimum = v4l2_ctrl.minimum; + ev->u.ctrl.maximum = v4l2_ctrl.maximum; + ev->u.ctrl.step = v4l2_ctrl.step; + ev->u.ctrl.default_value = v4l2_ctrl.default_value; +} + +static void uvc_ctrl_send_event(struct uvc_fh *handle, + struct uvc_control *ctrl, struct uvc_control_mapping *mapping, + s32 value, u32 changes) +{ + struct v4l2_subscribed_event *sev; + struct v4l2_event ev; + + if (list_empty(&mapping->ev_subs)) + return; + + uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, value, changes); + + list_for_each_entry(sev, &mapping->ev_subs, node) { + if (sev->fh && (sev->fh != &handle->vfh || + (sev->flags & V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK))) + v4l2_event_queue_fh(sev->fh, &ev); + } +} + +static void uvc_ctrl_send_events(struct uvc_fh *handle, + const struct v4l2_ext_control *xctrls, unsigned int xctrls_count) +{ + struct uvc_control_mapping *mapping; + struct uvc_control *ctrl; + unsigned int i; + + for (i = 0; i < xctrls_count; ++i) { + ctrl = uvc_find_control(handle->chain, xctrls[i].id, &mapping); + uvc_ctrl_send_event(handle, ctrl, mapping, xctrls[i].value, + V4L2_EVENT_CTRL_CH_VALUE); + } +} + +static int uvc_ctrl_add_event(struct v4l2_subscribed_event *sev) +{ + struct uvc_fh *handle = container_of(sev->fh, struct uvc_fh, vfh); + struct uvc_control_mapping *mapping; + struct uvc_control *ctrl; + int ret; + + ret = mutex_lock_interruptible(&handle->chain->ctrl_mutex); + if (ret < 0) + return -ERESTARTSYS; + + ctrl = uvc_find_control(handle->chain, sev->id, &mapping); + if (ctrl == NULL) { + ret = -EINVAL; + goto done; + } + + list_add_tail(&sev->node, &mapping->ev_subs); + if (sev->flags & V4L2_EVENT_SUB_FL_SEND_INITIAL) { + struct v4l2_event ev; + u32 changes = V4L2_EVENT_CTRL_CH_FLAGS; + s32 val = 0; + + if (__uvc_ctrl_get(handle->chain, ctrl, mapping, &val) == 0) + changes |= V4L2_EVENT_CTRL_CH_VALUE; + + uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, val, + changes); + v4l2_event_queue_fh(sev->fh, &ev); + } + +done: + mutex_unlock(&handle->chain->ctrl_mutex); + return ret; +} + +static void uvc_ctrl_del_event(struct v4l2_subscribed_event *sev) +{ + struct uvc_fh *handle = container_of(sev->fh, struct uvc_fh, vfh); + + mutex_lock(&handle->chain->ctrl_mutex); + list_del(&sev->node); + mutex_unlock(&handle->chain->ctrl_mutex); +} + +const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops = { + .add = uvc_ctrl_add_event, + .del = uvc_ctrl_del_event, + .replace = v4l2_ctrl_replace, + .merge = v4l2_ctrl_merge, +}; /* -------------------------------------------------------------------------- * Control transactions @@ -1179,8 +1291,11 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev, return 0; } -int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback) +int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback, + const struct v4l2_ext_control *xctrls, + unsigned int xctrls_count) { + struct uvc_video_chain *chain = handle->chain; struct uvc_entity *entity; int ret = 0; @@ -1191,6 +1306,8 @@ int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback) goto done; } + if (!rollback) + uvc_ctrl_send_events(handle, xctrls, xctrls_count); done: mutex_unlock(&chain->ctrl_mutex); return ret; @@ -1662,6 +1779,8 @@ static int __uvc_ctrl_add_mapping(struct uvc_device *dev, if (map == NULL) return -ENOMEM; + INIT_LIST_HEAD(&map->ev_subs); + size = sizeof(*mapping->menu_info) * mapping->menu_count; map->menu_info = kmemdup(mapping->menu_info, size, GFP_KERNEL); if (map->menu_info == NULL) { |