linux/drivers/media/video/v4l2-event.c
<<
>>
Prefs
   1/*
   2 * v4l2-event.c
   3 *
   4 * V4L2 events.
   5 *
   6 * Copyright (C) 2009--2010 Nokia Corporation.
   7 *
   8 * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
   9 *
  10 * This program is free software; you can redistribute it and/or
  11 * modify it under the terms of the GNU General Public License
  12 * version 2 as published by the Free Software Foundation.
  13 *
  14 * This program is distributed in the hope that it will be useful, but
  15 * WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 * General Public License for more details.
  18 *
  19 * You should have received a copy of the GNU General Public License
  20 * along with this program; if not, write to the Free Software
  21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  22 * 02110-1301 USA
  23 */
  24
  25#include <media/v4l2-dev.h>
  26#include <media/v4l2-fh.h>
  27#include <media/v4l2-event.h>
  28#include <media/v4l2-ctrls.h>
  29
  30#include <linux/sched.h>
  31#include <linux/slab.h>
  32
  33static unsigned sev_pos(const struct v4l2_subscribed_event *sev, unsigned idx)
  34{
  35        idx += sev->first;
  36        return idx >= sev->elems ? idx - sev->elems : idx;
  37}
  38
  39static int __v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event)
  40{
  41        struct v4l2_kevent *kev;
  42        unsigned long flags;
  43
  44        spin_lock_irqsave(&fh->vdev->fh_lock, flags);
  45
  46        if (list_empty(&fh->available)) {
  47                spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
  48                return -ENOENT;
  49        }
  50
  51        WARN_ON(fh->navailable == 0);
  52
  53        kev = list_first_entry(&fh->available, struct v4l2_kevent, list);
  54        list_del(&kev->list);
  55        fh->navailable--;
  56
  57        kev->event.pending = fh->navailable;
  58        *event = kev->event;
  59        kev->sev->first = sev_pos(kev->sev, 1);
  60        kev->sev->in_use--;
  61
  62        spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
  63
  64        return 0;
  65}
  66
  67int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event,
  68                       int nonblocking)
  69{
  70        int ret;
  71
  72        if (nonblocking)
  73                return __v4l2_event_dequeue(fh, event);
  74
  75        /* Release the vdev lock while waiting */
  76        if (fh->vdev->lock)
  77                mutex_unlock(fh->vdev->lock);
  78
  79        do {
  80                ret = wait_event_interruptible(fh->wait,
  81                                               fh->navailable != 0);
  82                if (ret < 0)
  83                        break;
  84
  85                ret = __v4l2_event_dequeue(fh, event);
  86        } while (ret == -ENOENT);
  87
  88        if (fh->vdev->lock)
  89                mutex_lock(fh->vdev->lock);
  90
  91        return ret;
  92}
  93EXPORT_SYMBOL_GPL(v4l2_event_dequeue);
  94
  95/* Caller must hold fh->vdev->fh_lock! */
  96static struct v4l2_subscribed_event *v4l2_event_subscribed(
  97                struct v4l2_fh *fh, u32 type, u32 id)
  98{
  99        struct v4l2_subscribed_event *sev;
 100
 101        assert_spin_locked(&fh->vdev->fh_lock);
 102
 103        list_for_each_entry(sev, &fh->subscribed, list)
 104                if (sev->type == type && sev->id == id)
 105                        return sev;
 106
 107        return NULL;
 108}
 109
 110static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev,
 111                const struct timespec *ts)
 112{
 113        struct v4l2_subscribed_event *sev;
 114        struct v4l2_kevent *kev;
 115        bool copy_payload = true;
 116
 117        /* Are we subscribed? */
 118        sev = v4l2_event_subscribed(fh, ev->type, ev->id);
 119        if (sev == NULL)
 120                return;
 121
 122        /* Increase event sequence number on fh. */
 123        fh->sequence++;
 124
 125        /* Do we have any free events? */
 126        if (sev->in_use == sev->elems) {
 127                /* no, remove the oldest one */
 128                kev = sev->events + sev_pos(sev, 0);
 129                list_del(&kev->list);
 130                sev->in_use--;
 131                sev->first = sev_pos(sev, 1);
 132                fh->navailable--;
 133                if (sev->elems == 1) {
 134                        if (sev->replace) {
 135                                sev->replace(&kev->event, ev);
 136                                copy_payload = false;
 137                        }
 138                } else if (sev->merge) {
 139                        struct v4l2_kevent *second_oldest =
 140                                sev->events + sev_pos(sev, 0);
 141                        sev->merge(&kev->event, &second_oldest->event);
 142                }
 143        }
 144
 145        /* Take one and fill it. */
 146        kev = sev->events + sev_pos(sev, sev->in_use);
 147        kev->event.type = ev->type;
 148        if (copy_payload)
 149                kev->event.u = ev->u;
 150        kev->event.id = ev->id;
 151        kev->event.timestamp = *ts;
 152        kev->event.sequence = fh->sequence;
 153        sev->in_use++;
 154        list_add_tail(&kev->list, &fh->available);
 155
 156        fh->navailable++;
 157
 158        wake_up_all(&fh->wait);
 159}
 160
 161void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev)
 162{
 163        struct v4l2_fh *fh;
 164        unsigned long flags;
 165        struct timespec timestamp;
 166
 167        ktime_get_ts(&timestamp);
 168
 169        spin_lock_irqsave(&vdev->fh_lock, flags);
 170
 171        list_for_each_entry(fh, &vdev->fh_list, list)
 172                __v4l2_event_queue_fh(fh, ev, &timestamp);
 173
 174        spin_unlock_irqrestore(&vdev->fh_lock, flags);
 175}
 176EXPORT_SYMBOL_GPL(v4l2_event_queue);
 177
 178void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev)
 179{
 180        unsigned long flags;
 181        struct timespec timestamp;
 182
 183        ktime_get_ts(&timestamp);
 184
 185        spin_lock_irqsave(&fh->vdev->fh_lock, flags);
 186        __v4l2_event_queue_fh(fh, ev, &timestamp);
 187        spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
 188}
 189EXPORT_SYMBOL_GPL(v4l2_event_queue_fh);
 190
 191int v4l2_event_pending(struct v4l2_fh *fh)
 192{
 193        return fh->navailable;
 194}
 195EXPORT_SYMBOL_GPL(v4l2_event_pending);
 196
 197static void ctrls_replace(struct v4l2_event *old, const struct v4l2_event *new)
 198{
 199        u32 old_changes = old->u.ctrl.changes;
 200
 201        old->u.ctrl = new->u.ctrl;
 202        old->u.ctrl.changes |= old_changes;
 203}
 204
 205static void ctrls_merge(const struct v4l2_event *old, struct v4l2_event *new)
 206{
 207        new->u.ctrl.changes |= old->u.ctrl.changes;
 208}
 209
 210int v4l2_event_subscribe(struct v4l2_fh *fh,
 211                         struct v4l2_event_subscription *sub, unsigned elems)
 212{
 213        struct v4l2_subscribed_event *sev, *found_ev;
 214        struct v4l2_ctrl *ctrl = NULL;
 215        unsigned long flags;
 216        unsigned i;
 217
 218        if (elems < 1)
 219                elems = 1;
 220        if (sub->type == V4L2_EVENT_CTRL) {
 221                ctrl = v4l2_ctrl_find(fh->ctrl_handler, sub->id);
 222                if (ctrl == NULL)
 223                        return -EINVAL;
 224        }
 225
 226        sev = kzalloc(sizeof(*sev) + sizeof(struct v4l2_kevent) * elems, GFP_KERNEL);
 227        if (!sev)
 228                return -ENOMEM;
 229        for (i = 0; i < elems; i++)
 230                sev->events[i].sev = sev;
 231        sev->type = sub->type;
 232        sev->id = sub->id;
 233        sev->flags = sub->flags;
 234        sev->fh = fh;
 235        sev->elems = elems;
 236        if (ctrl) {
 237                sev->replace = ctrls_replace;
 238                sev->merge = ctrls_merge;
 239        }
 240
 241        spin_lock_irqsave(&fh->vdev->fh_lock, flags);
 242        found_ev = v4l2_event_subscribed(fh, sub->type, sub->id);
 243        if (!found_ev)
 244                list_add(&sev->list, &fh->subscribed);
 245        spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
 246
 247        /* v4l2_ctrl_add_event uses a mutex, so do this outside the spin lock */
 248        if (found_ev)
 249                kfree(sev);
 250        else if (ctrl)
 251                v4l2_ctrl_add_event(ctrl, sev);
 252
 253        return 0;
 254}
 255EXPORT_SYMBOL_GPL(v4l2_event_subscribe);
 256
 257void v4l2_event_unsubscribe_all(struct v4l2_fh *fh)
 258{
 259        struct v4l2_event_subscription sub;
 260        struct v4l2_subscribed_event *sev;
 261        unsigned long flags;
 262
 263        do {
 264                sev = NULL;
 265
 266                spin_lock_irqsave(&fh->vdev->fh_lock, flags);
 267                if (!list_empty(&fh->subscribed)) {
 268                        sev = list_first_entry(&fh->subscribed,
 269                                        struct v4l2_subscribed_event, list);
 270                        sub.type = sev->type;
 271                        sub.id = sev->id;
 272                }
 273                spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
 274                if (sev)
 275                        v4l2_event_unsubscribe(fh, &sub);
 276        } while (sev);
 277}
 278EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe_all);
 279
 280int v4l2_event_unsubscribe(struct v4l2_fh *fh,
 281                           struct v4l2_event_subscription *sub)
 282{
 283        struct v4l2_subscribed_event *sev;
 284        unsigned long flags;
 285
 286        if (sub->type == V4L2_EVENT_ALL) {
 287                v4l2_event_unsubscribe_all(fh);
 288                return 0;
 289        }
 290
 291        spin_lock_irqsave(&fh->vdev->fh_lock, flags);
 292
 293        sev = v4l2_event_subscribed(fh, sub->type, sub->id);
 294        if (sev != NULL) {
 295                list_del(&sev->list);
 296                sev->fh = NULL;
 297        }
 298
 299        spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
 300        if (sev && sev->type == V4L2_EVENT_CTRL) {
 301                struct v4l2_ctrl *ctrl = v4l2_ctrl_find(fh->ctrl_handler, sev->id);
 302
 303                if (ctrl)
 304                        v4l2_ctrl_del_event(ctrl, sev);
 305        }
 306
 307        kfree(sev);
 308
 309        return 0;
 310}
 311EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe);
 312