v4l2-event.c revision c3b5b0241f620a356c97d8f43343e721c718806d
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 29#include <linux/sched.h> 30#include <linux/slab.h> 31 32int v4l2_event_init(struct v4l2_fh *fh) 33{ 34 fh->events = kzalloc(sizeof(*fh->events), GFP_KERNEL); 35 if (fh->events == NULL) 36 return -ENOMEM; 37 38 init_waitqueue_head(&fh->events->wait); 39 40 INIT_LIST_HEAD(&fh->events->free); 41 INIT_LIST_HEAD(&fh->events->available); 42 INIT_LIST_HEAD(&fh->events->subscribed); 43 44 fh->events->sequence = -1; 45 46 return 0; 47} 48 49int v4l2_event_alloc(struct v4l2_fh *fh, unsigned int n) 50{ 51 struct v4l2_events *events = fh->events; 52 unsigned long flags; 53 54 if (!events) { 55 WARN_ON(1); 56 return -ENOMEM; 57 } 58 59 while (events->nallocated < n) { 60 struct v4l2_kevent *kev; 61 62 kev = kzalloc(sizeof(*kev), GFP_KERNEL); 63 if (kev == NULL) 64 return -ENOMEM; 65 66 spin_lock_irqsave(&fh->vdev->fh_lock, flags); 67 list_add_tail(&kev->list, &events->free); 68 events->nallocated++; 69 spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); 70 } 71 72 return 0; 73} 74EXPORT_SYMBOL_GPL(v4l2_event_alloc); 75 76#define list_kfree(list, type, member) \ 77 while (!list_empty(list)) { \ 78 type *hi; \ 79 hi = list_first_entry(list, type, member); \ 80 list_del(&hi->member); \ 81 kfree(hi); \ 82 } 83 84void v4l2_event_free(struct v4l2_fh *fh) 85{ 86 struct v4l2_events *events = fh->events; 87 88 if (!events) 89 return; 90 91 list_kfree(&events->free, struct v4l2_kevent, list); 92 list_kfree(&events->available, struct v4l2_kevent, list); 93 list_kfree(&events->subscribed, struct v4l2_subscribed_event, list); 94 95 kfree(events); 96 fh->events = NULL; 97} 98EXPORT_SYMBOL_GPL(v4l2_event_free); 99 100static int __v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event) 101{ 102 struct v4l2_events *events = fh->events; 103 struct v4l2_kevent *kev; 104 unsigned long flags; 105 106 spin_lock_irqsave(&fh->vdev->fh_lock, flags); 107 108 if (list_empty(&events->available)) { 109 spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); 110 return -ENOENT; 111 } 112 113 WARN_ON(events->navailable == 0); 114 115 kev = list_first_entry(&events->available, struct v4l2_kevent, list); 116 list_move(&kev->list, &events->free); 117 events->navailable--; 118 119 kev->event.pending = events->navailable; 120 *event = kev->event; 121 122 spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); 123 124 return 0; 125} 126 127int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event, 128 int nonblocking) 129{ 130 struct v4l2_events *events = fh->events; 131 int ret; 132 133 if (nonblocking) 134 return __v4l2_event_dequeue(fh, event); 135 136 do { 137 ret = wait_event_interruptible(events->wait, 138 events->navailable != 0); 139 if (ret < 0) 140 return ret; 141 142 ret = __v4l2_event_dequeue(fh, event); 143 } while (ret == -ENOENT); 144 145 return ret; 146} 147 148/* Caller must hold fh->event->lock! */ 149static struct v4l2_subscribed_event *v4l2_event_subscribed( 150 struct v4l2_fh *fh, u32 type) 151{ 152 struct v4l2_events *events = fh->events; 153 struct v4l2_subscribed_event *sev; 154 155 WARN_ON(!spin_is_locked(&fh->vdev->fh_lock)); 156 157 list_for_each_entry(sev, &events->subscribed, list) { 158 if (sev->type == type) 159 return sev; 160 } 161 162 return NULL; 163} 164 165void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev) 166{ 167 struct v4l2_fh *fh; 168 unsigned long flags; 169 struct timespec timestamp; 170 171 ktime_get_ts(×tamp); 172 173 spin_lock_irqsave(&vdev->fh_lock, flags); 174 175 list_for_each_entry(fh, &vdev->fh_list, list) { 176 struct v4l2_events *events = fh->events; 177 struct v4l2_kevent *kev; 178 179 /* Are we subscribed? */ 180 if (!v4l2_event_subscribed(fh, ev->type)) 181 continue; 182 183 /* Increase event sequence number on fh. */ 184 events->sequence++; 185 186 /* Do we have any free events? */ 187 if (list_empty(&events->free)) 188 continue; 189 190 /* Take one and fill it. */ 191 kev = list_first_entry(&events->free, struct v4l2_kevent, list); 192 kev->event.type = ev->type; 193 kev->event.u = ev->u; 194 kev->event.timestamp = timestamp; 195 kev->event.sequence = events->sequence; 196 list_move_tail(&kev->list, &events->available); 197 198 events->navailable++; 199 200 wake_up_all(&events->wait); 201 } 202 203 spin_unlock_irqrestore(&vdev->fh_lock, flags); 204} 205EXPORT_SYMBOL_GPL(v4l2_event_queue); 206 207int v4l2_event_pending(struct v4l2_fh *fh) 208{ 209 return fh->events->navailable; 210} 211EXPORT_SYMBOL_GPL(v4l2_event_pending); 212 213int v4l2_event_subscribe(struct v4l2_fh *fh, 214 struct v4l2_event_subscription *sub) 215{ 216 struct v4l2_events *events = fh->events; 217 struct v4l2_subscribed_event *sev; 218 unsigned long flags; 219 220 if (fh->events == NULL) { 221 WARN_ON(1); 222 return -ENOMEM; 223 } 224 225 sev = kmalloc(sizeof(*sev), GFP_KERNEL); 226 if (!sev) 227 return -ENOMEM; 228 229 spin_lock_irqsave(&fh->vdev->fh_lock, flags); 230 231 if (v4l2_event_subscribed(fh, sub->type) == NULL) { 232 INIT_LIST_HEAD(&sev->list); 233 sev->type = sub->type; 234 235 list_add(&sev->list, &events->subscribed); 236 sev = NULL; 237 } 238 239 spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); 240 241 kfree(sev); 242 243 return 0; 244} 245EXPORT_SYMBOL_GPL(v4l2_event_subscribe); 246 247static void v4l2_event_unsubscribe_all(struct v4l2_fh *fh) 248{ 249 struct v4l2_events *events = fh->events; 250 struct v4l2_subscribed_event *sev; 251 unsigned long flags; 252 253 do { 254 sev = NULL; 255 256 spin_lock_irqsave(&fh->vdev->fh_lock, flags); 257 if (!list_empty(&events->subscribed)) { 258 sev = list_first_entry(&events->subscribed, 259 struct v4l2_subscribed_event, list); 260 list_del(&sev->list); 261 } 262 spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); 263 kfree(sev); 264 } while (sev); 265} 266 267int v4l2_event_unsubscribe(struct v4l2_fh *fh, 268 struct v4l2_event_subscription *sub) 269{ 270 struct v4l2_subscribed_event *sev; 271 unsigned long flags; 272 273 if (sub->type == V4L2_EVENT_ALL) { 274 v4l2_event_unsubscribe_all(fh); 275 return 0; 276 } 277 278 spin_lock_irqsave(&fh->vdev->fh_lock, flags); 279 280 sev = v4l2_event_subscribed(fh, sub->type); 281 if (sev != NULL) 282 list_del(&sev->list); 283 284 spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); 285 286 kfree(sev); 287 288 return 0; 289} 290EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe); 291