Commit | Line | Data |
---|---|---|
7a7d9a89 MCC |
1 | /* |
2 | * generic helper functions for handling video4linux capture buffers | |
3 | * | |
4 | * (c) 2007 Mauro Carvalho Chehab, <mchehab@infradead.org> | |
5 | * | |
6 | * Highly based on video-buf written originally by: | |
7 | * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> | |
8 | * (c) 2006 Mauro Carvalho Chehab, <mchehab@infradead.org> | |
9 | * (c) 2006 Ted Walther and John Sokol | |
10 | * | |
11 | * This program is free software; you can redistribute it and/or modify | |
12 | * it under the terms of the GNU General Public License as published by | |
13 | * the Free Software Foundation; either version 2 | |
14 | */ | |
15 | ||
16 | #include <linux/init.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/moduleparam.h> | |
27ac792c | 19 | #include <linux/mm.h> |
d43c36dc | 20 | #include <linux/sched.h> |
7a7d9a89 MCC |
21 | #include <linux/slab.h> |
22 | #include <linux/interrupt.h> | |
23 | ||
24 | #include <media/videobuf-core.h> | |
25 | ||
26 | #define MAGIC_BUFFER 0x20070728 | |
7a02264c PO |
27 | #define MAGIC_CHECK(is, should) \ |
28 | do { \ | |
29 | if (unlikely((is) != (should))) { \ | |
30 | printk(KERN_ERR \ | |
31 | "magic mismatch: %x (expected %x)\n", \ | |
32 | is, should); \ | |
33 | BUG(); \ | |
34 | } \ | |
35 | } while (0) | |
7a7d9a89 | 36 | |
e2c77314 | 37 | static int debug; |
7a7d9a89 MCC |
38 | module_param(debug, int, 0644); |
39 | ||
40 | MODULE_DESCRIPTION("helper module to manage video4linux buffers"); | |
41 | MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); | |
42 | MODULE_LICENSE("GPL"); | |
43 | ||
7a02264c PO |
44 | #define dprintk(level, fmt, arg...) \ |
45 | do { \ | |
46 | if (debug >= level) \ | |
47 | printk(KERN_DEBUG "vbuf: " fmt, ## arg); \ | |
48 | } while (0) | |
7a7d9a89 MCC |
49 | |
50 | /* --------------------------------------------------------------------- */ | |
51 | ||
52 | #define CALL(q, f, arg...) \ | |
e2c77314 | 53 | ((q->int_ops->f) ? q->int_ops->f(arg) : 0) |
a8d54e4c HV |
54 | #define CALLPTR(q, f, arg...) \ |
55 | ((q->int_ops->f) ? q->int_ops->f(arg) : NULL) | |
7a7d9a89 | 56 | |
33c38283 | 57 | struct videobuf_buffer *videobuf_alloc_vb(struct videobuf_queue *q) |
7a7d9a89 MCC |
58 | { |
59 | struct videobuf_buffer *vb; | |
60 | ||
e2c77314 | 61 | BUG_ON(q->msize < sizeof(*vb)); |
7a7d9a89 | 62 | |
33c38283 | 63 | if (!q->int_ops || !q->int_ops->alloc_vb) { |
7a7d9a89 MCC |
64 | printk(KERN_ERR "No specific ops defined!\n"); |
65 | BUG(); | |
66 | } | |
67 | ||
33c38283 | 68 | vb = q->int_ops->alloc_vb(q->msize); |
7a7d9a89 MCC |
69 | if (NULL != vb) { |
70 | init_waitqueue_head(&vb->done); | |
7a02264c | 71 | vb->magic = MAGIC_BUFFER; |
7a7d9a89 MCC |
72 | } |
73 | ||
74 | return vb; | |
75 | } | |
33c38283 | 76 | EXPORT_SYMBOL_GPL(videobuf_alloc_vb); |
7a7d9a89 | 77 | |
0e0809a5 | 78 | static int is_state_active_or_queued(struct videobuf_queue *q, struct videobuf_buffer *vb) |
7a7d9a89 | 79 | { |
0e0809a5 HV |
80 | unsigned long flags; |
81 | bool rc; | |
82 | ||
83 | spin_lock_irqsave(q->irqlock, flags); | |
84 | rc = vb->state != VIDEOBUF_ACTIVE && vb->state != VIDEOBUF_QUEUED; | |
85 | spin_unlock_irqrestore(q->irqlock, flags); | |
86 | return rc; | |
87 | }; | |
88 | ||
89 | int videobuf_waiton(struct videobuf_queue *q, struct videobuf_buffer *vb, | |
90 | int non_blocking, int intr) | |
91 | { | |
92 | bool is_ext_locked; | |
93 | int ret = 0; | |
94 | ||
e2c77314 | 95 | MAGIC_CHECK(vb->magic, MAGIC_BUFFER); |
009a9059 BP |
96 | |
97 | if (non_blocking) { | |
0e0809a5 | 98 | if (is_state_active_or_queued(q, vb)) |
009a9059 | 99 | return 0; |
0e0809a5 | 100 | return -EAGAIN; |
7a7d9a89 | 101 | } |
009a9059 | 102 | |
0e0809a5 HV |
103 | is_ext_locked = q->ext_lock && mutex_is_locked(q->ext_lock); |
104 | ||
105 | /* Release vdev lock to prevent this wait from blocking outside access to | |
106 | the device. */ | |
107 | if (is_ext_locked) | |
108 | mutex_unlock(q->ext_lock); | |
009a9059 | 109 | if (intr) |
0e0809a5 | 110 | ret = wait_event_interruptible(vb->done, is_state_active_or_queued(q, vb)); |
009a9059 | 111 | else |
0e0809a5 HV |
112 | wait_event(vb->done, is_state_active_or_queued(q, vb)); |
113 | /* Relock */ | |
114 | if (is_ext_locked) | |
115 | mutex_lock(q->ext_lock); | |
009a9059 | 116 | |
0e0809a5 | 117 | return ret; |
7a7d9a89 | 118 | } |
7a02264c | 119 | EXPORT_SYMBOL_GPL(videobuf_waiton); |
7a7d9a89 | 120 | |
e2c77314 | 121 | int videobuf_iolock(struct videobuf_queue *q, struct videobuf_buffer *vb, |
7a7d9a89 MCC |
122 | struct v4l2_framebuffer *fbuf) |
123 | { | |
e2c77314 MCC |
124 | MAGIC_CHECK(vb->magic, MAGIC_BUFFER); |
125 | MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); | |
7a7d9a89 | 126 | |
e2c77314 | 127 | return CALL(q, iolock, q, vb, fbuf); |
7a7d9a89 | 128 | } |
7a02264c | 129 | EXPORT_SYMBOL_GPL(videobuf_iolock); |
7a7d9a89 | 130 | |
f4fce60e HV |
131 | void *videobuf_queue_to_vaddr(struct videobuf_queue *q, |
132 | struct videobuf_buffer *buf) | |
59d34489 | 133 | { |
037c75eb HV |
134 | if (q->int_ops->vaddr) |
135 | return q->int_ops->vaddr(buf); | |
f4fce60e | 136 | return NULL; |
59d34489 | 137 | } |
f4fce60e | 138 | EXPORT_SYMBOL_GPL(videobuf_queue_to_vaddr); |
59d34489 | 139 | |
7a7d9a89 MCC |
140 | /* --------------------------------------------------------------------- */ |
141 | ||
142 | ||
e2c77314 | 143 | void videobuf_queue_core_init(struct videobuf_queue *q, |
38a54f35 | 144 | const struct videobuf_queue_ops *ops, |
e9bcf667 | 145 | struct device *dev, |
7a7d9a89 MCC |
146 | spinlock_t *irqlock, |
147 | enum v4l2_buf_type type, | |
148 | enum v4l2_field field, | |
149 | unsigned int msize, | |
d4cae5a5 | 150 | void *priv, |
08bff03e HV |
151 | struct videobuf_qtype_ops *int_ops, |
152 | struct mutex *ext_lock) | |
7a7d9a89 | 153 | { |
96ceea27 | 154 | BUG_ON(!q); |
e2c77314 | 155 | memset(q, 0, sizeof(*q)); |
d4cae5a5 | 156 | q->irqlock = irqlock; |
08bff03e | 157 | q->ext_lock = ext_lock; |
d4cae5a5 MCC |
158 | q->dev = dev; |
159 | q->type = type; | |
160 | q->field = field; | |
161 | q->msize = msize; | |
162 | q->ops = ops; | |
7a7d9a89 | 163 | q->priv_data = priv; |
d4cae5a5 | 164 | q->int_ops = int_ops; |
7a7d9a89 MCC |
165 | |
166 | /* All buffer operations are mandatory */ | |
e2c77314 MCC |
167 | BUG_ON(!q->ops->buf_setup); |
168 | BUG_ON(!q->ops->buf_prepare); | |
169 | BUG_ON(!q->ops->buf_queue); | |
170 | BUG_ON(!q->ops->buf_release); | |
7a7d9a89 | 171 | |
0cf4daee BP |
172 | /* Lock is mandatory for queue_cancel to work */ |
173 | BUG_ON(!irqlock); | |
174 | ||
d4cae5a5 | 175 | /* Having implementations for abstract methods are mandatory */ |
e2c77314 | 176 | BUG_ON(!q->int_ops); |
d4cae5a5 | 177 | |
64f9477f | 178 | mutex_init(&q->vb_lock); |
137d1cb1 | 179 | init_waitqueue_head(&q->wait); |
7a7d9a89 MCC |
180 | INIT_LIST_HEAD(&q->stream); |
181 | } | |
7a02264c | 182 | EXPORT_SYMBOL_GPL(videobuf_queue_core_init); |
7a7d9a89 | 183 | |
19bc5133 | 184 | /* Locking: Only usage in bttv unsafe find way to remove */ |
7a7d9a89 MCC |
185 | int videobuf_queue_is_busy(struct videobuf_queue *q) |
186 | { | |
187 | int i; | |
188 | ||
e2c77314 | 189 | MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); |
7a7d9a89 MCC |
190 | |
191 | if (q->streaming) { | |
e2c77314 | 192 | dprintk(1, "busy: streaming active\n"); |
7a7d9a89 MCC |
193 | return 1; |
194 | } | |
195 | if (q->reading) { | |
e2c77314 | 196 | dprintk(1, "busy: pending read #1\n"); |
7a7d9a89 MCC |
197 | return 1; |
198 | } | |
199 | if (q->read_buf) { | |
e2c77314 | 200 | dprintk(1, "busy: pending read #2\n"); |
7a7d9a89 MCC |
201 | return 1; |
202 | } | |
203 | for (i = 0; i < VIDEO_MAX_FRAME; i++) { | |
204 | if (NULL == q->bufs[i]) | |
205 | continue; | |
851c0c96 | 206 | if (q->bufs[i]->map) { |
e2c77314 | 207 | dprintk(1, "busy: buffer #%d mapped\n", i); |
7a7d9a89 MCC |
208 | return 1; |
209 | } | |
0fc0686e | 210 | if (q->bufs[i]->state == VIDEOBUF_QUEUED) { |
e2c77314 | 211 | dprintk(1, "busy: buffer #%d queued\n", i); |
7a7d9a89 MCC |
212 | return 1; |
213 | } | |
0fc0686e | 214 | if (q->bufs[i]->state == VIDEOBUF_ACTIVE) { |
e2c77314 | 215 | dprintk(1, "busy: buffer #%d avtive\n", i); |
7a7d9a89 MCC |
216 | return 1; |
217 | } | |
218 | } | |
219 | return 0; | |
220 | } | |
7a02264c | 221 | EXPORT_SYMBOL_GPL(videobuf_queue_is_busy); |
7a7d9a89 | 222 | |
a438d6da PO |
223 | /** |
224 | * __videobuf_free() - free all the buffers and their control structures | |
225 | * | |
226 | * This function can only be called if streaming/reading is off, i.e. no buffers | |
227 | * are under control of the driver. | |
228 | */ | |
229 | /* Locking: Caller holds q->vb_lock */ | |
230 | static int __videobuf_free(struct videobuf_queue *q) | |
231 | { | |
232 | int i; | |
233 | ||
234 | dprintk(1, "%s\n", __func__); | |
235 | if (!q) | |
236 | return 0; | |
237 | ||
238 | if (q->streaming || q->reading) { | |
239 | dprintk(1, "Cannot free buffers when streaming or reading\n"); | |
240 | return -EBUSY; | |
241 | } | |
242 | ||
243 | MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); | |
244 | ||
245 | for (i = 0; i < VIDEO_MAX_FRAME; i++) | |
246 | if (q->bufs[i] && q->bufs[i]->map) { | |
247 | dprintk(1, "Cannot free mmapped buffers\n"); | |
248 | return -EBUSY; | |
249 | } | |
250 | ||
251 | for (i = 0; i < VIDEO_MAX_FRAME; i++) { | |
252 | if (NULL == q->bufs[i]) | |
253 | continue; | |
254 | q->ops->buf_release(q, q->bufs[i]); | |
255 | kfree(q->bufs[i]); | |
256 | q->bufs[i] = NULL; | |
257 | } | |
258 | ||
259 | return 0; | |
260 | } | |
261 | ||
64f9477f | 262 | /* Locking: Caller holds q->vb_lock */ |
7a7d9a89 MCC |
263 | void videobuf_queue_cancel(struct videobuf_queue *q) |
264 | { | |
e2c77314 | 265 | unsigned long flags = 0; |
7a7d9a89 MCC |
266 | int i; |
267 | ||
137d1cb1 BP |
268 | q->streaming = 0; |
269 | q->reading = 0; | |
270 | wake_up_interruptible_sync(&q->wait); | |
271 | ||
7a7d9a89 | 272 | /* remove queued buffers from list */ |
0cf4daee | 273 | spin_lock_irqsave(q->irqlock, flags); |
7a7d9a89 MCC |
274 | for (i = 0; i < VIDEO_MAX_FRAME; i++) { |
275 | if (NULL == q->bufs[i]) | |
276 | continue; | |
0fc0686e | 277 | if (q->bufs[i]->state == VIDEOBUF_QUEUED) { |
7a7d9a89 | 278 | list_del(&q->bufs[i]->queue); |
0fc0686e | 279 | q->bufs[i]->state = VIDEOBUF_ERROR; |
b608f432 | 280 | wake_up_all(&q->bufs[i]->done); |
7a7d9a89 MCC |
281 | } |
282 | } | |
0cf4daee | 283 | spin_unlock_irqrestore(q->irqlock, flags); |
7a7d9a89 MCC |
284 | |
285 | /* free all buffers + clear queue */ | |
286 | for (i = 0; i < VIDEO_MAX_FRAME; i++) { | |
287 | if (NULL == q->bufs[i]) | |
288 | continue; | |
e2c77314 | 289 | q->ops->buf_release(q, q->bufs[i]); |
7a7d9a89 MCC |
290 | } |
291 | INIT_LIST_HEAD(&q->stream); | |
292 | } | |
7a02264c | 293 | EXPORT_SYMBOL_GPL(videobuf_queue_cancel); |
7a7d9a89 MCC |
294 | |
295 | /* --------------------------------------------------------------------- */ | |
296 | ||
64f9477f | 297 | /* Locking: Caller holds q->vb_lock */ |
7a7d9a89 MCC |
298 | enum v4l2_field videobuf_next_field(struct videobuf_queue *q) |
299 | { | |
300 | enum v4l2_field field = q->field; | |
301 | ||
302 | BUG_ON(V4L2_FIELD_ANY == field); | |
303 | ||
304 | if (V4L2_FIELD_ALTERNATE == field) { | |
305 | if (V4L2_FIELD_TOP == q->last) { | |
306 | field = V4L2_FIELD_BOTTOM; | |
307 | q->last = V4L2_FIELD_BOTTOM; | |
308 | } else { | |
309 | field = V4L2_FIELD_TOP; | |
310 | q->last = V4L2_FIELD_TOP; | |
311 | } | |
312 | } | |
313 | return field; | |
314 | } | |
7a02264c | 315 | EXPORT_SYMBOL_GPL(videobuf_next_field); |
7a7d9a89 | 316 | |
64f9477f | 317 | /* Locking: Caller holds q->vb_lock */ |
7a7d9a89 MCC |
318 | static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b, |
319 | struct videobuf_buffer *vb, enum v4l2_buf_type type) | |
320 | { | |
e2c77314 MCC |
321 | MAGIC_CHECK(vb->magic, MAGIC_BUFFER); |
322 | MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); | |
7a7d9a89 MCC |
323 | |
324 | b->index = vb->i; | |
325 | b->type = type; | |
326 | ||
327 | b->memory = vb->memory; | |
328 | switch (b->memory) { | |
329 | case V4L2_MEMORY_MMAP: | |
330 | b->m.offset = vb->boff; | |
331 | b->length = vb->bsize; | |
332 | break; | |
333 | case V4L2_MEMORY_USERPTR: | |
334 | b->m.userptr = vb->baddr; | |
335 | b->length = vb->bsize; | |
336 | break; | |
337 | case V4L2_MEMORY_OVERLAY: | |
338 | b->m.offset = vb->boff; | |
339 | break; | |
3c3016ba SS |
340 | case V4L2_MEMORY_DMABUF: |
341 | /* DMABUF is not handled in videobuf framework */ | |
342 | break; | |
7a7d9a89 MCC |
343 | } |
344 | ||
1b18e7a0 | 345 | b->flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; |
851c0c96 | 346 | if (vb->map) |
7a7d9a89 MCC |
347 | b->flags |= V4L2_BUF_FLAG_MAPPED; |
348 | ||
349 | switch (vb->state) { | |
0fc0686e BP |
350 | case VIDEOBUF_PREPARED: |
351 | case VIDEOBUF_QUEUED: | |
352 | case VIDEOBUF_ACTIVE: | |
7a7d9a89 MCC |
353 | b->flags |= V4L2_BUF_FLAG_QUEUED; |
354 | break; | |
0fc0686e | 355 | case VIDEOBUF_ERROR: |
b2dfd1a4 HV |
356 | b->flags |= V4L2_BUF_FLAG_ERROR; |
357 | /* fall through */ | |
358 | case VIDEOBUF_DONE: | |
7a7d9a89 MCC |
359 | b->flags |= V4L2_BUF_FLAG_DONE; |
360 | break; | |
0fc0686e BP |
361 | case VIDEOBUF_NEEDS_INIT: |
362 | case VIDEOBUF_IDLE: | |
7a7d9a89 MCC |
363 | /* nothing */ |
364 | break; | |
365 | } | |
366 | ||
7a7d9a89 MCC |
367 | b->field = vb->field; |
368 | b->timestamp = vb->ts; | |
369 | b->bytesused = vb->size; | |
370 | b->sequence = vb->field_count >> 1; | |
371 | } | |
372 | ||
19bc5133 BP |
373 | int videobuf_mmap_free(struct videobuf_queue *q) |
374 | { | |
375 | int ret; | |
97397687 | 376 | videobuf_queue_lock(q); |
a438d6da | 377 | ret = __videobuf_free(q); |
97397687 | 378 | videobuf_queue_unlock(q); |
19bc5133 BP |
379 | return ret; |
380 | } | |
7a02264c | 381 | EXPORT_SYMBOL_GPL(videobuf_mmap_free); |
19bc5133 | 382 | |
64f9477f | 383 | /* Locking: Caller holds q->vb_lock */ |
81b2dbca | 384 | int __videobuf_mmap_setup(struct videobuf_queue *q, |
19bc5133 BP |
385 | unsigned int bcount, unsigned int bsize, |
386 | enum v4l2_memory memory) | |
387 | { | |
388 | unsigned int i; | |
389 | int err; | |
390 | ||
e2c77314 | 391 | MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); |
19bc5133 | 392 | |
a438d6da | 393 | err = __videobuf_free(q); |
19bc5133 BP |
394 | if (0 != err) |
395 | return err; | |
396 | ||
397 | /* Allocate and initialize buffers */ | |
398 | for (i = 0; i < bcount; i++) { | |
33c38283 | 399 | q->bufs[i] = videobuf_alloc_vb(q); |
19bc5133 | 400 | |
7a02264c | 401 | if (NULL == q->bufs[i]) |
19bc5133 BP |
402 | break; |
403 | ||
404 | q->bufs[i]->i = i; | |
19bc5133 BP |
405 | q->bufs[i]->memory = memory; |
406 | q->bufs[i]->bsize = bsize; | |
407 | switch (memory) { | |
408 | case V4L2_MEMORY_MMAP: | |
7cbefad0 | 409 | q->bufs[i]->boff = PAGE_ALIGN(bsize) * i; |
19bc5133 BP |
410 | break; |
411 | case V4L2_MEMORY_USERPTR: | |
412 | case V4L2_MEMORY_OVERLAY: | |
3c3016ba | 413 | case V4L2_MEMORY_DMABUF: |
19bc5133 BP |
414 | /* nothing */ |
415 | break; | |
416 | } | |
417 | } | |
418 | ||
419 | if (!i) | |
420 | return -ENOMEM; | |
421 | ||
7a02264c | 422 | dprintk(1, "mmap setup: %d buffers, %d bytes each\n", i, bsize); |
19bc5133 BP |
423 | |
424 | return i; | |
425 | } | |
7a02264c | 426 | EXPORT_SYMBOL_GPL(__videobuf_mmap_setup); |
19bc5133 BP |
427 | |
428 | int videobuf_mmap_setup(struct videobuf_queue *q, | |
429 | unsigned int bcount, unsigned int bsize, | |
430 | enum v4l2_memory memory) | |
431 | { | |
432 | int ret; | |
97397687 | 433 | videobuf_queue_lock(q); |
19bc5133 | 434 | ret = __videobuf_mmap_setup(q, bcount, bsize, memory); |
97397687 | 435 | videobuf_queue_unlock(q); |
19bc5133 BP |
436 | return ret; |
437 | } | |
7a02264c | 438 | EXPORT_SYMBOL_GPL(videobuf_mmap_setup); |
19bc5133 | 439 | |
7a7d9a89 MCC |
440 | int videobuf_reqbufs(struct videobuf_queue *q, |
441 | struct v4l2_requestbuffers *req) | |
442 | { | |
e2c77314 | 443 | unsigned int size, count; |
7a7d9a89 MCC |
444 | int retval; |
445 | ||
7a7d9a89 MCC |
446 | if (req->memory != V4L2_MEMORY_MMAP && |
447 | req->memory != V4L2_MEMORY_USERPTR && | |
448 | req->memory != V4L2_MEMORY_OVERLAY) { | |
e2c77314 | 449 | dprintk(1, "reqbufs: memory type invalid\n"); |
7a7d9a89 MCC |
450 | return -EINVAL; |
451 | } | |
452 | ||
97397687 | 453 | videobuf_queue_lock(q); |
19bc5133 | 454 | if (req->type != q->type) { |
e2c77314 | 455 | dprintk(1, "reqbufs: queue type invalid\n"); |
19bc5133 BP |
456 | retval = -EINVAL; |
457 | goto done; | |
458 | } | |
459 | ||
7a7d9a89 | 460 | if (q->streaming) { |
e2c77314 | 461 | dprintk(1, "reqbufs: streaming already exists\n"); |
00f98d08 BP |
462 | retval = -EBUSY; |
463 | goto done; | |
7a7d9a89 MCC |
464 | } |
465 | if (!list_empty(&q->stream)) { | |
e2c77314 | 466 | dprintk(1, "reqbufs: stream running\n"); |
00f98d08 BP |
467 | retval = -EBUSY; |
468 | goto done; | |
7a7d9a89 MCC |
469 | } |
470 | ||
b7900eed HG |
471 | if (req->count == 0) { |
472 | dprintk(1, "reqbufs: count invalid (%d)\n", req->count); | |
473 | retval = __videobuf_free(q); | |
474 | goto done; | |
475 | } | |
476 | ||
7a7d9a89 MCC |
477 | count = req->count; |
478 | if (count > VIDEO_MAX_FRAME) | |
479 | count = VIDEO_MAX_FRAME; | |
480 | size = 0; | |
e2c77314 | 481 | q->ops->buf_setup(q, &count, &size); |
296372e3 MN |
482 | dprintk(1, "reqbufs: bufs=%d, size=0x%x [%u pages total]\n", |
483 | count, size, | |
7a02264c | 484 | (unsigned int)((count * PAGE_ALIGN(size)) >> PAGE_SHIFT)); |
7a7d9a89 | 485 | |
e2c77314 | 486 | retval = __videobuf_mmap_setup(q, count, size, req->memory); |
7a7d9a89 | 487 | if (retval < 0) { |
e2c77314 | 488 | dprintk(1, "reqbufs: mmap setup returned %d\n", retval); |
7a7d9a89 MCC |
489 | goto done; |
490 | } | |
491 | ||
49ee718e | 492 | req->count = retval; |
925d74ae | 493 | retval = 0; |
7a7d9a89 MCC |
494 | |
495 | done: | |
97397687 | 496 | videobuf_queue_unlock(q); |
7a7d9a89 MCC |
497 | return retval; |
498 | } | |
7a02264c | 499 | EXPORT_SYMBOL_GPL(videobuf_reqbufs); |
7a7d9a89 MCC |
500 | |
501 | int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b) | |
502 | { | |
19bc5133 BP |
503 | int ret = -EINVAL; |
504 | ||
97397687 | 505 | videobuf_queue_lock(q); |
7a7d9a89 | 506 | if (unlikely(b->type != q->type)) { |
e2c77314 | 507 | dprintk(1, "querybuf: Wrong type.\n"); |
19bc5133 | 508 | goto done; |
7a7d9a89 | 509 | } |
223ffe5f | 510 | if (unlikely(b->index >= VIDEO_MAX_FRAME)) { |
e2c77314 | 511 | dprintk(1, "querybuf: index out of range.\n"); |
19bc5133 | 512 | goto done; |
7a7d9a89 MCC |
513 | } |
514 | if (unlikely(NULL == q->bufs[b->index])) { | |
e2c77314 | 515 | dprintk(1, "querybuf: buffer is null.\n"); |
19bc5133 | 516 | goto done; |
7a7d9a89 | 517 | } |
19bc5133 | 518 | |
e2c77314 | 519 | videobuf_status(q, b, q->bufs[b->index], q->type); |
19bc5133 BP |
520 | |
521 | ret = 0; | |
522 | done: | |
97397687 | 523 | videobuf_queue_unlock(q); |
19bc5133 | 524 | return ret; |
7a7d9a89 | 525 | } |
7a02264c | 526 | EXPORT_SYMBOL_GPL(videobuf_querybuf); |
7a7d9a89 | 527 | |
7a02264c | 528 | int videobuf_qbuf(struct videobuf_queue *q, struct v4l2_buffer *b) |
7a7d9a89 MCC |
529 | { |
530 | struct videobuf_buffer *buf; | |
531 | enum v4l2_field field; | |
e2c77314 | 532 | unsigned long flags = 0; |
7a7d9a89 MCC |
533 | int retval; |
534 | ||
e2c77314 | 535 | MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); |
7a7d9a89 | 536 | |
9900132f ML |
537 | if (b->memory == V4L2_MEMORY_MMAP) |
538 | down_read(¤t->mm->mmap_sem); | |
539 | ||
97397687 | 540 | videobuf_queue_lock(q); |
7a7d9a89 MCC |
541 | retval = -EBUSY; |
542 | if (q->reading) { | |
e2c77314 | 543 | dprintk(1, "qbuf: Reading running...\n"); |
7a7d9a89 MCC |
544 | goto done; |
545 | } | |
546 | retval = -EINVAL; | |
547 | if (b->type != q->type) { | |
e2c77314 | 548 | dprintk(1, "qbuf: Wrong type.\n"); |
7a7d9a89 MCC |
549 | goto done; |
550 | } | |
223ffe5f | 551 | if (b->index >= VIDEO_MAX_FRAME) { |
e2c77314 | 552 | dprintk(1, "qbuf: index out of range.\n"); |
7a7d9a89 MCC |
553 | goto done; |
554 | } | |
555 | buf = q->bufs[b->index]; | |
556 | if (NULL == buf) { | |
e2c77314 | 557 | dprintk(1, "qbuf: buffer is null.\n"); |
7a7d9a89 MCC |
558 | goto done; |
559 | } | |
e2c77314 | 560 | MAGIC_CHECK(buf->magic, MAGIC_BUFFER); |
7a7d9a89 | 561 | if (buf->memory != b->memory) { |
e2c77314 | 562 | dprintk(1, "qbuf: memory type is wrong.\n"); |
7a7d9a89 MCC |
563 | goto done; |
564 | } | |
0fc0686e | 565 | if (buf->state != VIDEOBUF_NEEDS_INIT && buf->state != VIDEOBUF_IDLE) { |
e2c77314 | 566 | dprintk(1, "qbuf: buffer is already queued or active.\n"); |
7a7d9a89 MCC |
567 | goto done; |
568 | } | |
569 | ||
7a7d9a89 MCC |
570 | switch (b->memory) { |
571 | case V4L2_MEMORY_MMAP: | |
572 | if (0 == buf->baddr) { | |
e2c77314 MCC |
573 | dprintk(1, "qbuf: mmap requested " |
574 | "but buffer addr is zero!\n"); | |
7a7d9a89 MCC |
575 | goto done; |
576 | } | |
96f2ec67 PO |
577 | if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT |
578 | || q->type == V4L2_BUF_TYPE_VBI_OUTPUT | |
579 | || q->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) { | |
580 | buf->size = b->bytesused; | |
581 | buf->field = b->field; | |
582 | buf->ts = b->timestamp; | |
583 | } | |
7a7d9a89 MCC |
584 | break; |
585 | case V4L2_MEMORY_USERPTR: | |
586 | if (b->length < buf->bsize) { | |
e2c77314 | 587 | dprintk(1, "qbuf: buffer length is not enough\n"); |
7a7d9a89 MCC |
588 | goto done; |
589 | } | |
e2c77314 MCC |
590 | if (VIDEOBUF_NEEDS_INIT != buf->state && |
591 | buf->baddr != b->m.userptr) | |
592 | q->ops->buf_release(q, buf); | |
7a7d9a89 MCC |
593 | buf->baddr = b->m.userptr; |
594 | break; | |
595 | case V4L2_MEMORY_OVERLAY: | |
596 | buf->boff = b->m.offset; | |
597 | break; | |
598 | default: | |
e2c77314 | 599 | dprintk(1, "qbuf: wrong memory type\n"); |
7a7d9a89 MCC |
600 | goto done; |
601 | } | |
602 | ||
e2c77314 | 603 | dprintk(1, "qbuf: requesting next field\n"); |
7a7d9a89 | 604 | field = videobuf_next_field(q); |
e2c77314 | 605 | retval = q->ops->buf_prepare(q, buf, field); |
7a7d9a89 | 606 | if (0 != retval) { |
e2c77314 | 607 | dprintk(1, "qbuf: buffer_prepare returned %d\n", retval); |
7a7d9a89 MCC |
608 | goto done; |
609 | } | |
610 | ||
e2c77314 | 611 | list_add_tail(&buf->stream, &q->stream); |
7a7d9a89 | 612 | if (q->streaming) { |
0cf4daee | 613 | spin_lock_irqsave(q->irqlock, flags); |
e2c77314 | 614 | q->ops->buf_queue(q, buf); |
0cf4daee | 615 | spin_unlock_irqrestore(q->irqlock, flags); |
7a7d9a89 | 616 | } |
771075bb | 617 | dprintk(1, "qbuf: succeeded\n"); |
7a7d9a89 | 618 | retval = 0; |
137d1cb1 | 619 | wake_up_interruptible_sync(&q->wait); |
7a7d9a89 | 620 | |
7a02264c | 621 | done: |
97397687 | 622 | videobuf_queue_unlock(q); |
9900132f ML |
623 | |
624 | if (b->memory == V4L2_MEMORY_MMAP) | |
625 | up_read(¤t->mm->mmap_sem); | |
626 | ||
7a7d9a89 MCC |
627 | return retval; |
628 | } | |
7a02264c | 629 | EXPORT_SYMBOL_GPL(videobuf_qbuf); |
137d1cb1 BP |
630 | |
631 | /* Locking: Caller holds q->vb_lock */ | |
632 | static int stream_next_buffer_check_queue(struct videobuf_queue *q, int noblock) | |
7a7d9a89 | 633 | { |
7a7d9a89 MCC |
634 | int retval; |
635 | ||
137d1cb1 BP |
636 | checks: |
637 | if (!q->streaming) { | |
638 | dprintk(1, "next_buffer: Not streaming\n"); | |
639 | retval = -EINVAL; | |
7a7d9a89 MCC |
640 | goto done; |
641 | } | |
137d1cb1 | 642 | |
7a7d9a89 | 643 | if (list_empty(&q->stream)) { |
137d1cb1 BP |
644 | if (noblock) { |
645 | retval = -EAGAIN; | |
646 | dprintk(2, "next_buffer: no buffers to dequeue\n"); | |
647 | goto done; | |
648 | } else { | |
649 | dprintk(2, "next_buffer: waiting on buffer\n"); | |
650 | ||
651 | /* Drop lock to avoid deadlock with qbuf */ | |
97397687 | 652 | videobuf_queue_unlock(q); |
137d1cb1 BP |
653 | |
654 | /* Checking list_empty and streaming is safe without | |
655 | * locks because we goto checks to validate while | |
656 | * holding locks before proceeding */ | |
657 | retval = wait_event_interruptible(q->wait, | |
658 | !list_empty(&q->stream) || !q->streaming); | |
97397687 | 659 | videobuf_queue_lock(q); |
137d1cb1 BP |
660 | |
661 | if (retval) | |
662 | goto done; | |
663 | ||
664 | goto checks; | |
665 | } | |
7a7d9a89 | 666 | } |
137d1cb1 BP |
667 | |
668 | retval = 0; | |
669 | ||
670 | done: | |
671 | return retval; | |
672 | } | |
673 | ||
137d1cb1 BP |
674 | /* Locking: Caller holds q->vb_lock */ |
675 | static int stream_next_buffer(struct videobuf_queue *q, | |
676 | struct videobuf_buffer **vb, int nonblocking) | |
677 | { | |
678 | int retval; | |
679 | struct videobuf_buffer *buf = NULL; | |
680 | ||
681 | retval = stream_next_buffer_check_queue(q, nonblocking); | |
682 | if (retval) | |
683 | goto done; | |
684 | ||
7a7d9a89 | 685 | buf = list_entry(q->stream.next, struct videobuf_buffer, stream); |
0e0809a5 | 686 | retval = videobuf_waiton(q, buf, nonblocking, 1); |
137d1cb1 BP |
687 | if (retval < 0) |
688 | goto done; | |
689 | ||
690 | *vb = buf; | |
691 | done: | |
692 | return retval; | |
693 | } | |
694 | ||
695 | int videobuf_dqbuf(struct videobuf_queue *q, | |
7a02264c | 696 | struct v4l2_buffer *b, int nonblocking) |
137d1cb1 BP |
697 | { |
698 | struct videobuf_buffer *buf = NULL; | |
699 | int retval; | |
700 | ||
701 | MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); | |
702 | ||
b2dfd1a4 | 703 | memset(b, 0, sizeof(*b)); |
97397687 | 704 | videobuf_queue_lock(q); |
137d1cb1 BP |
705 | |
706 | retval = stream_next_buffer(q, &buf, nonblocking); | |
7a7d9a89 | 707 | if (retval < 0) { |
137d1cb1 | 708 | dprintk(1, "dqbuf: next_buffer error: %i\n", retval); |
7a7d9a89 MCC |
709 | goto done; |
710 | } | |
137d1cb1 | 711 | |
7a7d9a89 | 712 | switch (buf->state) { |
0fc0686e | 713 | case VIDEOBUF_ERROR: |
e2c77314 | 714 | dprintk(1, "dqbuf: state is error\n"); |
7a7d9a89 | 715 | break; |
0fc0686e | 716 | case VIDEOBUF_DONE: |
e2c77314 | 717 | dprintk(1, "dqbuf: state is done\n"); |
7a7d9a89 MCC |
718 | break; |
719 | default: | |
e2c77314 | 720 | dprintk(1, "dqbuf: state invalid\n"); |
7a7d9a89 MCC |
721 | retval = -EINVAL; |
722 | goto done; | |
723 | } | |
b2dfd1a4 | 724 | CALL(q, sync, q, buf); |
e2c77314 | 725 | videobuf_status(q, b, buf, q->type); |
b2dfd1a4 HV |
726 | list_del(&buf->stream); |
727 | buf->state = VIDEOBUF_IDLE; | |
728 | b->flags &= ~V4L2_BUF_FLAG_DONE; | |
7a02264c | 729 | done: |
97397687 | 730 | videobuf_queue_unlock(q); |
7a7d9a89 MCC |
731 | return retval; |
732 | } | |
7a02264c | 733 | EXPORT_SYMBOL_GPL(videobuf_dqbuf); |
7a7d9a89 MCC |
734 | |
735 | int videobuf_streamon(struct videobuf_queue *q) | |
736 | { | |
737 | struct videobuf_buffer *buf; | |
e2c77314 | 738 | unsigned long flags = 0; |
7a7d9a89 MCC |
739 | int retval; |
740 | ||
97397687 | 741 | videobuf_queue_lock(q); |
7a7d9a89 MCC |
742 | retval = -EBUSY; |
743 | if (q->reading) | |
744 | goto done; | |
745 | retval = 0; | |
746 | if (q->streaming) | |
747 | goto done; | |
748 | q->streaming = 1; | |
0cf4daee | 749 | spin_lock_irqsave(q->irqlock, flags); |
a991f44b | 750 | list_for_each_entry(buf, &q->stream, stream) |
0fc0686e | 751 | if (buf->state == VIDEOBUF_PREPARED) |
e2c77314 | 752 | q->ops->buf_queue(q, buf); |
0cf4daee | 753 | spin_unlock_irqrestore(q->irqlock, flags); |
7a7d9a89 | 754 | |
137d1cb1 | 755 | wake_up_interruptible_sync(&q->wait); |
7a02264c | 756 | done: |
97397687 | 757 | videobuf_queue_unlock(q); |
7a7d9a89 MCC |
758 | return retval; |
759 | } | |
7a02264c | 760 | EXPORT_SYMBOL_GPL(videobuf_streamon); |
7a7d9a89 | 761 | |
64f9477f | 762 | /* Locking: Caller holds q->vb_lock */ |
19bc5133 | 763 | static int __videobuf_streamoff(struct videobuf_queue *q) |
7a7d9a89 | 764 | { |
7a7d9a89 | 765 | if (!q->streaming) |
19bc5133 BP |
766 | return -EINVAL; |
767 | ||
7a7d9a89 | 768 | videobuf_queue_cancel(q); |
7a7d9a89 | 769 | |
19bc5133 BP |
770 | return 0; |
771 | } | |
772 | ||
773 | int videobuf_streamoff(struct videobuf_queue *q) | |
774 | { | |
775 | int retval; | |
776 | ||
97397687 | 777 | videobuf_queue_lock(q); |
19bc5133 | 778 | retval = __videobuf_streamoff(q); |
97397687 | 779 | videobuf_queue_unlock(q); |
19bc5133 | 780 | |
7a7d9a89 MCC |
781 | return retval; |
782 | } | |
7a02264c | 783 | EXPORT_SYMBOL_GPL(videobuf_streamoff); |
7a7d9a89 | 784 | |
64f9477f | 785 | /* Locking: Caller holds q->vb_lock */ |
7a7d9a89 MCC |
786 | static ssize_t videobuf_read_zerocopy(struct videobuf_queue *q, |
787 | char __user *data, | |
788 | size_t count, loff_t *ppos) | |
789 | { | |
790 | enum v4l2_field field; | |
e2c77314 | 791 | unsigned long flags = 0; |
7a7d9a89 MCC |
792 | int retval; |
793 | ||
e2c77314 | 794 | MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); |
7a7d9a89 MCC |
795 | |
796 | /* setup stuff */ | |
33c38283 | 797 | q->read_buf = videobuf_alloc_vb(q); |
7a7d9a89 MCC |
798 | if (NULL == q->read_buf) |
799 | return -ENOMEM; | |
800 | ||
801 | q->read_buf->memory = V4L2_MEMORY_USERPTR; | |
802 | q->read_buf->baddr = (unsigned long)data; | |
803 | q->read_buf->bsize = count; | |
804 | ||
805 | field = videobuf_next_field(q); | |
e2c77314 | 806 | retval = q->ops->buf_prepare(q, q->read_buf, field); |
7a7d9a89 MCC |
807 | if (0 != retval) |
808 | goto done; | |
809 | ||
810 | /* start capture & wait */ | |
0cf4daee | 811 | spin_lock_irqsave(q->irqlock, flags); |
e2c77314 | 812 | q->ops->buf_queue(q, q->read_buf); |
0cf4daee | 813 | spin_unlock_irqrestore(q->irqlock, flags); |
0e0809a5 | 814 | retval = videobuf_waiton(q, q->read_buf, 0, 0); |
7a7d9a89 | 815 | if (0 == retval) { |
e2c77314 | 816 | CALL(q, sync, q, q->read_buf); |
0fc0686e | 817 | if (VIDEOBUF_ERROR == q->read_buf->state) |
7a7d9a89 MCC |
818 | retval = -EIO; |
819 | else | |
820 | retval = q->read_buf->size; | |
821 | } | |
822 | ||
7a02264c | 823 | done: |
7a7d9a89 | 824 | /* cleanup */ |
e2c77314 | 825 | q->ops->buf_release(q, q->read_buf); |
7a7d9a89 MCC |
826 | kfree(q->read_buf); |
827 | q->read_buf = NULL; | |
828 | return retval; | |
829 | } | |
830 | ||
37111039 HV |
831 | static int __videobuf_copy_to_user(struct videobuf_queue *q, |
832 | struct videobuf_buffer *buf, | |
833 | char __user *data, size_t count, | |
834 | int nonblocking) | |
835 | { | |
a8d54e4c | 836 | void *vaddr = CALLPTR(q, vaddr, buf); |
37111039 HV |
837 | |
838 | /* copy to userspace */ | |
839 | if (count > buf->size - q->read_off) | |
840 | count = buf->size - q->read_off; | |
841 | ||
842 | if (copy_to_user(data, vaddr + q->read_off, count)) | |
843 | return -EFAULT; | |
844 | ||
845 | return count; | |
846 | } | |
847 | ||
848 | static int __videobuf_copy_stream(struct videobuf_queue *q, | |
849 | struct videobuf_buffer *buf, | |
850 | char __user *data, size_t count, size_t pos, | |
851 | int vbihack, int nonblocking) | |
852 | { | |
a8d54e4c | 853 | unsigned int *fc = CALLPTR(q, vaddr, buf); |
37111039 HV |
854 | |
855 | if (vbihack) { | |
856 | /* dirty, undocumented hack -- pass the frame counter | |
857 | * within the last four bytes of each vbi data block. | |
858 | * We need that one to maintain backward compatibility | |
859 | * to all vbi decoding software out there ... */ | |
860 | fc += (buf->size >> 2) - 1; | |
861 | *fc = buf->field_count >> 1; | |
862 | dprintk(1, "vbihack: %d\n", *fc); | |
863 | } | |
864 | ||
865 | /* copy stuff using the common method */ | |
866 | count = __videobuf_copy_to_user(q, buf, data, count, nonblocking); | |
867 | ||
868 | if ((count == -EFAULT) && (pos == 0)) | |
869 | return -EFAULT; | |
870 | ||
871 | return count; | |
872 | } | |
873 | ||
7a7d9a89 MCC |
874 | ssize_t videobuf_read_one(struct videobuf_queue *q, |
875 | char __user *data, size_t count, loff_t *ppos, | |
876 | int nonblocking) | |
877 | { | |
878 | enum v4l2_field field; | |
e2c77314 | 879 | unsigned long flags = 0; |
7daa4a88 | 880 | unsigned size = 0, nbufs = 1; |
7a7d9a89 MCC |
881 | int retval; |
882 | ||
e2c77314 | 883 | MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); |
7a7d9a89 | 884 | |
97397687 | 885 | videobuf_queue_lock(q); |
7a7d9a89 | 886 | |
e2c77314 | 887 | q->ops->buf_setup(q, &nbufs, &size); |
7a7d9a89 MCC |
888 | |
889 | if (NULL == q->read_buf && | |
890 | count >= size && | |
891 | !nonblocking) { | |
e2c77314 | 892 | retval = videobuf_read_zerocopy(q, data, count, ppos); |
7a7d9a89 MCC |
893 | if (retval >= 0 || retval == -EIO) |
894 | /* ok, all done */ | |
895 | goto done; | |
896 | /* fallback to kernel bounce buffer on failures */ | |
897 | } | |
898 | ||
899 | if (NULL == q->read_buf) { | |
900 | /* need to capture a new frame */ | |
901 | retval = -ENOMEM; | |
33c38283 | 902 | q->read_buf = videobuf_alloc_vb(q); |
7a7d9a89 | 903 | |
e2c77314 | 904 | dprintk(1, "video alloc=0x%p\n", q->read_buf); |
7a7d9a89 MCC |
905 | if (NULL == q->read_buf) |
906 | goto done; | |
907 | q->read_buf->memory = V4L2_MEMORY_USERPTR; | |
908 | q->read_buf->bsize = count; /* preferred size */ | |
909 | field = videobuf_next_field(q); | |
e2c77314 | 910 | retval = q->ops->buf_prepare(q, q->read_buf, field); |
7a7d9a89 MCC |
911 | |
912 | if (0 != retval) { | |
e2c77314 | 913 | kfree(q->read_buf); |
7a7d9a89 MCC |
914 | q->read_buf = NULL; |
915 | goto done; | |
916 | } | |
7a7d9a89 | 917 | |
0cf4daee | 918 | spin_lock_irqsave(q->irqlock, flags); |
e2c77314 | 919 | q->ops->buf_queue(q, q->read_buf); |
0cf4daee BP |
920 | spin_unlock_irqrestore(q->irqlock, flags); |
921 | ||
7a7d9a89 MCC |
922 | q->read_off = 0; |
923 | } | |
924 | ||
925 | /* wait until capture is done */ | |
0e0809a5 | 926 | retval = videobuf_waiton(q, q->read_buf, nonblocking, 1); |
7a7d9a89 MCC |
927 | if (0 != retval) |
928 | goto done; | |
929 | ||
e2c77314 | 930 | CALL(q, sync, q, q->read_buf); |
7a7d9a89 | 931 | |
0fc0686e | 932 | if (VIDEOBUF_ERROR == q->read_buf->state) { |
7a7d9a89 | 933 | /* catch I/O errors */ |
e2c77314 | 934 | q->ops->buf_release(q, q->read_buf); |
7a7d9a89 MCC |
935 | kfree(q->read_buf); |
936 | q->read_buf = NULL; | |
937 | retval = -EIO; | |
938 | goto done; | |
939 | } | |
940 | ||
941 | /* Copy to userspace */ | |
37111039 | 942 | retval = __videobuf_copy_to_user(q, q->read_buf, data, count, nonblocking); |
e2c77314 | 943 | if (retval < 0) |
7a7d9a89 MCC |
944 | goto done; |
945 | ||
946 | q->read_off += retval; | |
947 | if (q->read_off == q->read_buf->size) { | |
948 | /* all data copied, cleanup */ | |
e2c77314 | 949 | q->ops->buf_release(q, q->read_buf); |
7a7d9a89 MCC |
950 | kfree(q->read_buf); |
951 | q->read_buf = NULL; | |
952 | } | |
953 | ||
7a02264c | 954 | done: |
97397687 | 955 | videobuf_queue_unlock(q); |
7a7d9a89 MCC |
956 | return retval; |
957 | } | |
7a02264c | 958 | EXPORT_SYMBOL_GPL(videobuf_read_one); |
7a7d9a89 | 959 | |
64f9477f | 960 | /* Locking: Caller holds q->vb_lock */ |
225ba900 | 961 | static int __videobuf_read_start(struct videobuf_queue *q) |
7a7d9a89 MCC |
962 | { |
963 | enum v4l2_field field; | |
e2c77314 | 964 | unsigned long flags = 0; |
49ee718e | 965 | unsigned int count = 0, size = 0; |
7a7d9a89 MCC |
966 | int err, i; |
967 | ||
e2c77314 | 968 | q->ops->buf_setup(q, &count, &size); |
7a7d9a89 MCC |
969 | if (count < 2) |
970 | count = 2; | |
971 | if (count > VIDEO_MAX_FRAME) | |
972 | count = VIDEO_MAX_FRAME; | |
973 | size = PAGE_ALIGN(size); | |
974 | ||
19bc5133 | 975 | err = __videobuf_mmap_setup(q, count, size, V4L2_MEMORY_USERPTR); |
49ee718e | 976 | if (err < 0) |
7a7d9a89 MCC |
977 | return err; |
978 | ||
49ee718e BP |
979 | count = err; |
980 | ||
7a7d9a89 MCC |
981 | for (i = 0; i < count; i++) { |
982 | field = videobuf_next_field(q); | |
e2c77314 | 983 | err = q->ops->buf_prepare(q, q->bufs[i], field); |
7a7d9a89 MCC |
984 | if (err) |
985 | return err; | |
986 | list_add_tail(&q->bufs[i]->stream, &q->stream); | |
987 | } | |
0cf4daee | 988 | spin_lock_irqsave(q->irqlock, flags); |
7a7d9a89 | 989 | for (i = 0; i < count; i++) |
e2c77314 | 990 | q->ops->buf_queue(q, q->bufs[i]); |
0cf4daee | 991 | spin_unlock_irqrestore(q->irqlock, flags); |
7a7d9a89 MCC |
992 | q->reading = 1; |
993 | return 0; | |
994 | } | |
995 | ||
19bc5133 | 996 | static void __videobuf_read_stop(struct videobuf_queue *q) |
7a7d9a89 MCC |
997 | { |
998 | int i; | |
999 | ||
1000 | videobuf_queue_cancel(q); | |
a438d6da | 1001 | __videobuf_free(q); |
7a7d9a89 MCC |
1002 | INIT_LIST_HEAD(&q->stream); |
1003 | for (i = 0; i < VIDEO_MAX_FRAME; i++) { | |
1004 | if (NULL == q->bufs[i]) | |
1005 | continue; | |
1006 | kfree(q->bufs[i]); | |
1007 | q->bufs[i] = NULL; | |
1008 | } | |
1009 | q->read_buf = NULL; | |
19bc5133 BP |
1010 | } |
1011 | ||
19fb1457 MCC |
1012 | int videobuf_read_start(struct videobuf_queue *q) |
1013 | { | |
1014 | int rc; | |
1015 | ||
97397687 | 1016 | videobuf_queue_lock(q); |
19fb1457 | 1017 | rc = __videobuf_read_start(q); |
97397687 | 1018 | videobuf_queue_unlock(q); |
19fb1457 MCC |
1019 | |
1020 | return rc; | |
1021 | } | |
7a02264c | 1022 | EXPORT_SYMBOL_GPL(videobuf_read_start); |
19fb1457 | 1023 | |
19bc5133 BP |
1024 | void videobuf_read_stop(struct videobuf_queue *q) |
1025 | { | |
97397687 | 1026 | videobuf_queue_lock(q); |
19bc5133 | 1027 | __videobuf_read_stop(q); |
97397687 | 1028 | videobuf_queue_unlock(q); |
19bc5133 | 1029 | } |
7a02264c | 1030 | EXPORT_SYMBOL_GPL(videobuf_read_stop); |
19bc5133 BP |
1031 | |
1032 | void videobuf_stop(struct videobuf_queue *q) | |
1033 | { | |
97397687 | 1034 | videobuf_queue_lock(q); |
19bc5133 BP |
1035 | |
1036 | if (q->streaming) | |
1037 | __videobuf_streamoff(q); | |
1038 | ||
1039 | if (q->reading) | |
1040 | __videobuf_read_stop(q); | |
1041 | ||
97397687 | 1042 | videobuf_queue_unlock(q); |
7a7d9a89 | 1043 | } |
7a02264c | 1044 | EXPORT_SYMBOL_GPL(videobuf_stop); |
19bc5133 | 1045 | |
7a7d9a89 MCC |
1046 | ssize_t videobuf_read_stream(struct videobuf_queue *q, |
1047 | char __user *data, size_t count, loff_t *ppos, | |
1048 | int vbihack, int nonblocking) | |
1049 | { | |
1050 | int rc, retval; | |
e2c77314 | 1051 | unsigned long flags = 0; |
7a7d9a89 | 1052 | |
e2c77314 | 1053 | MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); |
7a7d9a89 | 1054 | |
7e28adb2 | 1055 | dprintk(2, "%s\n", __func__); |
97397687 | 1056 | videobuf_queue_lock(q); |
7a7d9a89 MCC |
1057 | retval = -EBUSY; |
1058 | if (q->streaming) | |
1059 | goto done; | |
1060 | if (!q->reading) { | |
3f84307a | 1061 | retval = __videobuf_read_start(q); |
7a7d9a89 MCC |
1062 | if (retval < 0) |
1063 | goto done; | |
1064 | } | |
1065 | ||
1066 | retval = 0; | |
1067 | while (count > 0) { | |
1068 | /* get / wait for data */ | |
1069 | if (NULL == q->read_buf) { | |
1070 | q->read_buf = list_entry(q->stream.next, | |
1071 | struct videobuf_buffer, | |
1072 | stream); | |
1073 | list_del(&q->read_buf->stream); | |
1074 | q->read_off = 0; | |
1075 | } | |
0e0809a5 | 1076 | rc = videobuf_waiton(q, q->read_buf, nonblocking, 1); |
7a7d9a89 MCC |
1077 | if (rc < 0) { |
1078 | if (0 == retval) | |
1079 | retval = rc; | |
1080 | break; | |
1081 | } | |
1082 | ||
0fc0686e | 1083 | if (q->read_buf->state == VIDEOBUF_DONE) { |
37111039 | 1084 | rc = __videobuf_copy_stream(q, q->read_buf, data + retval, count, |
7a7d9a89 MCC |
1085 | retval, vbihack, nonblocking); |
1086 | if (rc < 0) { | |
1087 | retval = rc; | |
1088 | break; | |
1089 | } | |
1090 | retval += rc; | |
1091 | count -= rc; | |
1092 | q->read_off += rc; | |
1093 | } else { | |
1094 | /* some error */ | |
1095 | q->read_off = q->read_buf->size; | |
1096 | if (0 == retval) | |
1097 | retval = -EIO; | |
1098 | } | |
1099 | ||
1100 | /* requeue buffer when done with copying */ | |
1101 | if (q->read_off == q->read_buf->size) { | |
1102 | list_add_tail(&q->read_buf->stream, | |
1103 | &q->stream); | |
0cf4daee | 1104 | spin_lock_irqsave(q->irqlock, flags); |
e2c77314 | 1105 | q->ops->buf_queue(q, q->read_buf); |
0cf4daee | 1106 | spin_unlock_irqrestore(q->irqlock, flags); |
7a7d9a89 MCC |
1107 | q->read_buf = NULL; |
1108 | } | |
1109 | if (retval < 0) | |
1110 | break; | |
1111 | } | |
1112 | ||
7a02264c | 1113 | done: |
97397687 | 1114 | videobuf_queue_unlock(q); |
7a7d9a89 MCC |
1115 | return retval; |
1116 | } | |
7a02264c | 1117 | EXPORT_SYMBOL_GPL(videobuf_read_stream); |
7a7d9a89 MCC |
1118 | |
1119 | unsigned int videobuf_poll_stream(struct file *file, | |
1120 | struct videobuf_queue *q, | |
1121 | poll_table *wait) | |
1122 | { | |
0e17e9a9 | 1123 | unsigned long req_events = poll_requested_events(wait); |
7a7d9a89 MCC |
1124 | struct videobuf_buffer *buf = NULL; |
1125 | unsigned int rc = 0; | |
1126 | ||
97397687 | 1127 | videobuf_queue_lock(q); |
7a7d9a89 MCC |
1128 | if (q->streaming) { |
1129 | if (!list_empty(&q->stream)) | |
1130 | buf = list_entry(q->stream.next, | |
1131 | struct videobuf_buffer, stream); | |
0e17e9a9 | 1132 | } else if (req_events & (POLLIN | POLLRDNORM)) { |
7a7d9a89 | 1133 | if (!q->reading) |
3f84307a | 1134 | __videobuf_read_start(q); |
7a7d9a89 MCC |
1135 | if (!q->reading) { |
1136 | rc = POLLERR; | |
1137 | } else if (NULL == q->read_buf) { | |
1138 | q->read_buf = list_entry(q->stream.next, | |
1139 | struct videobuf_buffer, | |
1140 | stream); | |
1141 | list_del(&q->read_buf->stream); | |
1142 | q->read_off = 0; | |
1143 | } | |
1144 | buf = q->read_buf; | |
1145 | } | |
1146 | if (!buf) | |
1147 | rc = POLLERR; | |
1148 | ||
1149 | if (0 == rc) { | |
1150 | poll_wait(file, &buf->done, wait); | |
0fc0686e | 1151 | if (buf->state == VIDEOBUF_DONE || |
9b558434 PO |
1152 | buf->state == VIDEOBUF_ERROR) { |
1153 | switch (q->type) { | |
1154 | case V4L2_BUF_TYPE_VIDEO_OUTPUT: | |
1155 | case V4L2_BUF_TYPE_VBI_OUTPUT: | |
1156 | case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: | |
1157 | rc = POLLOUT | POLLWRNORM; | |
1158 | break; | |
1159 | default: | |
1160 | rc = POLLIN | POLLRDNORM; | |
1161 | break; | |
1162 | } | |
1163 | } | |
7a7d9a89 | 1164 | } |
97397687 | 1165 | videobuf_queue_unlock(q); |
7a7d9a89 MCC |
1166 | return rc; |
1167 | } | |
7a02264c | 1168 | EXPORT_SYMBOL_GPL(videobuf_poll_stream); |
7a7d9a89 | 1169 | |
7a02264c | 1170 | int videobuf_mmap_mapper(struct videobuf_queue *q, struct vm_area_struct *vma) |
7a7d9a89 | 1171 | { |
0b62b737 HV |
1172 | int rc = -EINVAL; |
1173 | int i; | |
7a7d9a89 | 1174 | |
e2c77314 | 1175 | MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); |
7a7d9a89 | 1176 | |
0b62b737 HV |
1177 | if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) { |
1178 | dprintk(1, "mmap appl bug: PROT_WRITE and MAP_SHARED are required\n"); | |
1179 | return -EINVAL; | |
1180 | } | |
1181 | ||
97397687 | 1182 | videobuf_queue_lock(q); |
0b62b737 HV |
1183 | for (i = 0; i < VIDEO_MAX_FRAME; i++) { |
1184 | struct videobuf_buffer *buf = q->bufs[i]; | |
1185 | ||
1186 | if (buf && buf->memory == V4L2_MEMORY_MMAP && | |
1187 | buf->boff == (vma->vm_pgoff << PAGE_SHIFT)) { | |
1188 | rc = CALL(q, mmap_mapper, q, buf, vma); | |
1189 | break; | |
1190 | } | |
1191 | } | |
97397687 | 1192 | videobuf_queue_unlock(q); |
7a7d9a89 | 1193 | |
0b62b737 | 1194 | return rc; |
7a7d9a89 | 1195 | } |
7a02264c | 1196 | EXPORT_SYMBOL_GPL(videobuf_mmap_mapper); |