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