Rev 516 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
516 | giacomo | 1 | /* |
2 | * generic helper functions for video4linux capture buffers, to handle |
||
3 | * memory management and PCI DMA. Right now bttv + saa7134 use it. |
||
4 | * |
||
5 | * The functions expect the hardware being able to scatter gatter |
||
6 | * (i.e. the buffers are not linear in physical memory, but fragmented |
||
7 | * into PAGE_SIZE chunks). They also assume the driver does not need |
||
8 | * to touch the video data (thus it is probably not useful for USB as |
||
9 | * data often must be uncompressed by the drivers). |
||
10 | * |
||
11 | * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> |
||
12 | * |
||
13 | * This program is free software; you can redistribute it and/or modify |
||
14 | * it under the terms of the GNU General Public License as published by |
||
15 | * the Free Software Foundation; either version 2 of the License, or |
||
16 | * (at your option) any later version. |
||
17 | */ |
||
18 | |||
19 | #include <linuxcomp.h> |
||
20 | |||
21 | #include <linux/init.h> |
||
22 | #include <linux/module.h> |
||
23 | #include <linux/vmalloc.h> |
||
24 | #include <linux/pagemap.h> |
||
25 | #include <linux/slab.h> |
||
26 | #include <linux/pci.h> |
||
27 | #include <linux/interrupt.h> |
||
28 | #include <asm/page.h> |
||
29 | #include <asm/pgtable.h> |
||
30 | |||
31 | #ifndef TryLockPage |
||
32 | # include "linux/page-flags.h" |
||
33 | # define TryLockPage TestSetPageLocked |
||
34 | #endif |
||
35 | |||
36 | #include <media/video-buf.h> |
||
37 | |||
38 | static int debug = 1; |
||
39 | |||
40 | MODULE_DESCRIPTION("helper module to manage video4linux pci dma buffers"); |
||
41 | MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); |
||
42 | MODULE_LICENSE("GPL"); |
||
43 | MODULE_PARM(debug,"i"); |
||
44 | |||
45 | #define dprintk(level, fmt, arg...) if (debug >= level) \ |
||
46 | printk(KERN_DEBUG "vbuf: " fmt, ## arg) |
||
47 | |||
48 | /* |
||
49 | struct scatterlist* |
||
50 | videobuf_vmalloc_to_sg(unsigned char *virt, int nr_pages) |
||
51 | { |
||
52 | struct scatterlist *sglist; |
||
53 | struct page *pg; |
||
54 | int i; |
||
55 | |||
56 | sglist = kmalloc(sizeof(struct scatterlist)*nr_pages, GFP_KERNEL); |
||
57 | if (NULL == sglist) |
||
58 | return NULL; |
||
59 | memset(sglist,0,sizeof(struct scatterlist)*nr_pages); |
||
60 | for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) { |
||
61 | pg = vmalloc_to_page(virt); |
||
62 | if (NULL == pg) |
||
63 | goto err; |
||
64 | if (PageHighMem(pg)) |
||
65 | BUG(); |
||
66 | sglist[i].page = pg; |
||
67 | sglist[i].length = PAGE_SIZE; |
||
68 | } |
||
69 | return sglist; |
||
70 | |||
71 | err: |
||
72 | kfree(sglist); |
||
73 | return NULL; |
||
74 | } |
||
75 | |||
76 | struct scatterlist* |
||
77 | videobuf_pages_to_sg(struct page **pages, int nr_pages, int offset) |
||
78 | { |
||
79 | struct scatterlist *sglist; |
||
80 | int i = 0; |
||
81 | |||
82 | if (NULL == pages[0]) |
||
83 | return NULL; |
||
84 | sglist = kmalloc(sizeof(*sglist) * nr_pages, GFP_KERNEL); |
||
85 | if (NULL == sglist) |
||
86 | return NULL; |
||
87 | memset(sglist, 0, sizeof(*sglist) * nr_pages); |
||
88 | |||
89 | if (NULL == pages[0]) |
||
90 | goto nopage; |
||
91 | if (PageHighMem(pages[0])) |
||
92 | goto highmem; |
||
93 | sglist[0].page = pages[0]; |
||
94 | sglist[0].offset = offset; |
||
95 | sglist[0].length = PAGE_SIZE - offset; |
||
96 | for (i = 1; i < nr_pages; i++) { |
||
97 | if (NULL == pages[i]) |
||
98 | goto nopage; |
||
99 | if (PageHighMem(pages[i])) |
||
100 | goto highmem; |
||
101 | sglist[i].page = pages[i]; |
||
102 | sglist[i].length = PAGE_SIZE; |
||
103 | } |
||
104 | return sglist; |
||
105 | |||
106 | nopage: |
||
107 | dprintk(2,"sgl: oops - no page\n"); |
||
108 | kfree(sglist); |
||
109 | return NULL; |
||
110 | |||
111 | highmem: |
||
112 | dprintk(2,"sgl: oops - highmem page\n"); |
||
113 | kfree(sglist); |
||
114 | return NULL; |
||
115 | } |
||
116 | */ |
||
117 | int videobuf_lock(struct page **pages, int nr_pages) |
||
118 | { |
||
119 | int i = 0; |
||
120 | |||
121 | dprintk(2,"lock start ...\n"); |
||
122 | //for (i = 0; i < nr_pages; i++) |
||
123 | // if (TryLockPage(pages[i])) |
||
124 | // goto err; |
||
125 | dprintk(2,"lock ok [%d pages]\n",nr_pages); |
||
126 | return 0; |
||
127 | |||
128 | // err: |
||
129 | dprintk(2,"lock failed, unlock ...\n"); |
||
130 | while (i > 0) |
||
131 | //unlock_page(pages[--i]); |
||
132 | dprintk(2,"lock quit\n"); |
||
133 | return -EINVAL; |
||
134 | } |
||
135 | |||
136 | int videobuf_unlock(struct page **pages, int nr_pages) |
||
137 | { |
||
138 | //int i; |
||
139 | |||
140 | dprintk(2,"unlock start ...\n"); |
||
141 | //for (i = 0; i < nr_pages; i++) |
||
142 | // unlock_page(pages[i]); |
||
143 | dprintk(2,"unlock ok [%d pages]\n",nr_pages); |
||
144 | return 0; |
||
145 | } |
||
146 | |||
147 | /* --------------------------------------------------------------------- */ |
||
148 | |||
149 | int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction, |
||
150 | unsigned long data, unsigned long size) |
||
151 | { |
||
152 | unsigned long first,last; |
||
153 | int err, rw = 0; |
||
154 | |||
155 | dma->direction = direction; |
||
156 | switch (dma->direction) { |
||
157 | case PCI_DMA_FROMDEVICE: rw = READ; break; |
||
158 | case PCI_DMA_TODEVICE: rw = WRITE; break; |
||
159 | default: BUG(); |
||
160 | } |
||
161 | |||
162 | first = (data & PAGE_MASK) >> PAGE_SHIFT; |
||
163 | last = ((data+size-1) & PAGE_MASK) >> PAGE_SHIFT; |
||
164 | dma->offset = data & ~PAGE_MASK; |
||
165 | dma->nr_pages = last-first+1; |
||
166 | dma->pages = kmalloc(dma->nr_pages * sizeof(struct page*), |
||
167 | GFP_KERNEL); |
||
168 | if (NULL == dma->pages) |
||
169 | return -ENOMEM; |
||
170 | dprintk(1,"init user [0x%lx+0x%lx => %d pages]\n", |
||
171 | data,size,dma->nr_pages); |
||
172 | |||
173 | //down_read(¤t->mm->mmap_sem); |
||
174 | err = dma->nr_pages;//get_user_pages(current,current->mm, |
||
175 | // data & PAGE_MASK, dma->nr_pages, |
||
176 | // rw == READ, 1, /* force */ |
||
177 | // dma->pages, NULL); |
||
178 | //up_read(¤t->mm->mmap_sem); |
||
179 | if (err != dma->nr_pages) { |
||
180 | dma->nr_pages = (err >= 0) ? err : 0; |
||
181 | dprintk(1,"get_user_pages: err=%d [%d]\n",err,dma->nr_pages); |
||
182 | return err < 0 ? err : -EINVAL; |
||
183 | } |
||
184 | return 0; |
||
185 | } |
||
186 | |||
187 | int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, |
||
188 | int nr_pages) |
||
189 | { |
||
190 | dprintk(1,"init kernel [%d pages]\n",nr_pages); |
||
191 | dma->direction = direction; |
||
192 | dma->vmalloc = vmalloc_32(nr_pages << PAGE_SHIFT); |
||
193 | if (NULL == dma->vmalloc) { |
||
194 | dprintk(1,"vmalloc_32(%d pages) failed\n",nr_pages); |
||
195 | return -ENOMEM; |
||
196 | } |
||
197 | memset(dma->vmalloc,0,nr_pages << PAGE_SHIFT); |
||
198 | dma->nr_pages = nr_pages; |
||
199 | return 0; |
||
200 | } |
||
201 | |||
202 | int videobuf_dma_init_overlay(struct videobuf_dmabuf *dma, int direction, |
||
203 | dma_addr_t addr, int nr_pages) |
||
204 | { |
||
205 | dprintk(1,"init overlay [%d pages @ bus 0x%lx]\n", |
||
206 | nr_pages,(unsigned long)addr); |
||
207 | dma->direction = direction; |
||
208 | if (0 == addr) |
||
209 | return -EINVAL; |
||
210 | |||
211 | dma->bus_addr = addr; |
||
212 | dma->nr_pages = nr_pages; |
||
213 | return 0; |
||
214 | } |
||
215 | /* |
||
216 | int videobuf_dma_pci_map(struct pci_dev *dev, struct videobuf_dmabuf *dma) |
||
217 | { |
||
218 | int err; |
||
219 | |||
220 | if (0 == dma->nr_pages) |
||
221 | BUG(); |
||
222 | |||
223 | if (dma->pages) { |
||
224 | if (0 != (err = videobuf_lock(dma->pages, dma->nr_pages))) { |
||
225 | dprintk(1,"videobuf_lock: %d\n",err); |
||
226 | return err; |
||
227 | } |
||
228 | dma->sglist = videobuf_pages_to_sg(dma->pages, dma->nr_pages, |
||
229 | dma->offset); |
||
230 | if (NULL == dma->sglist) |
||
231 | videobuf_unlock(dma->pages, dma->nr_pages); |
||
232 | } |
||
233 | if (dma->vmalloc) { |
||
234 | dma->sglist = videobuf_vmalloc_to_sg |
||
235 | (dma->vmalloc,dma->nr_pages); |
||
236 | } |
||
237 | if (dma->bus_addr) { |
||
238 | dma->sglist = kmalloc(sizeof(struct scatterlist), GFP_KERNEL); |
||
239 | if (NULL != dma->sglist) { |
||
240 | dma->sglen = 1; |
||
241 | sg_dma_address(&dma->sglist[0]) = dma->bus_addr & ~PAGE_MASK; |
||
242 | dma->sglist[0].offset = dma->bus_addr & PAGE_MASK; |
||
243 | sg_dma_len(&dma->sglist[0]) = dma->nr_pages * PAGE_SIZE; |
||
244 | } |
||
245 | } |
||
246 | if (NULL == dma->sglist) { |
||
247 | dprintk(1,"scatterlist is NULL\n"); |
||
248 | return -ENOMEM; |
||
249 | } |
||
250 | |||
251 | if (!dma->bus_addr) |
||
252 | dma->sglen = pci_map_sg(dev,dma->sglist,dma->nr_pages, |
||
253 | dma->direction); |
||
254 | return 0; |
||
255 | } |
||
256 | |||
257 | int videobuf_dma_pci_sync(struct pci_dev *dev, struct videobuf_dmabuf *dma) |
||
258 | { |
||
259 | if (!dma->sglen) |
||
260 | BUG(); |
||
261 | |||
262 | if (!dma->bus_addr) |
||
263 | pci_dma_sync_sg(dev,dma->sglist,dma->nr_pages,dma->direction); |
||
264 | return 0; |
||
265 | } |
||
266 | |||
267 | int videobuf_dma_pci_unmap(struct pci_dev *dev, struct videobuf_dmabuf *dma) |
||
268 | { |
||
269 | if (!dma->sglen) |
||
270 | return 0; |
||
271 | |||
272 | if (!dma->bus_addr) |
||
273 | pci_unmap_sg(dev,dma->sglist,dma->nr_pages,dma->direction); |
||
274 | kfree(dma->sglist); |
||
275 | dma->sglist = NULL; |
||
276 | dma->sglen = 0; |
||
277 | if (dma->pages) |
||
278 | videobuf_unlock(dma->pages, dma->nr_pages); |
||
279 | return 0; |
||
280 | } |
||
281 | |||
282 | int videobuf_dma_free(struct videobuf_dmabuf *dma) |
||
283 | { |
||
284 | if (dma->sglen) |
||
285 | BUG(); |
||
286 | |||
287 | if (dma->pages) { |
||
288 | int i; |
||
289 | for (i=0; i < dma->nr_pages; i++) |
||
290 | page_cache_release(dma->pages[i]); |
||
291 | kfree(dma->pages); |
||
292 | dma->pages = NULL; |
||
293 | } |
||
294 | if (dma->vmalloc) { |
||
295 | vfree(dma->vmalloc); |
||
296 | dma->vmalloc = NULL; |
||
297 | } |
||
298 | if (dma->bus_addr) { |
||
299 | dma->bus_addr = 0; |
||
300 | } |
||
301 | dma->direction = PCI_DMA_NONE; |
||
302 | return 0; |
||
303 | } |
||
304 | */ |
||
305 | /* --------------------------------------------------------------------- */ |
||
306 | |||
307 | void* videobuf_alloc(unsigned int size) |
||
308 | { |
||
309 | struct videobuf_buffer *vb; |
||
310 | |||
311 | vb = kmalloc(size,GFP_KERNEL); |
||
312 | if (NULL != vb) { |
||
313 | memset(vb,0,size); |
||
314 | //init_waitqueue_head(&vb->done); |
||
315 | } |
||
316 | return vb; |
||
317 | } |
||
318 | |||
319 | int videobuf_waiton(struct videobuf_buffer *vb, int non_blocking, int intr) |
||
320 | { |
||
321 | int retval = 0; |
||
322 | /* |
||
323 | DECLARE_WAITQUEUE(wait, current); |
||
324 | |||
325 | add_wait_queue(&vb->done, &wait); |
||
326 | while (vb->state == STATE_ACTIVE || vb->state == STATE_QUEUED) { |
||
327 | if (non_blocking) { |
||
328 | retval = -EAGAIN; |
||
329 | break; |
||
330 | } |
||
331 | set_current_state(intr ? TASK_INTERRUPTIBLE : |
||
332 | TASK_UNINTERRUPTIBLE); |
||
333 | if (vb->state == STATE_ACTIVE || vb->state == STATE_QUEUED) |
||
334 | schedule(); |
||
335 | set_current_state(TASK_RUNNING); |
||
336 | if (intr && signal_pending(current)) { |
||
337 | dprintk(1,"buffer waiton: -EINTR\n"); |
||
338 | retval = -EINTR; |
||
339 | break; |
||
340 | } |
||
341 | } |
||
342 | remove_wait_queue(&vb->done, &wait); |
||
343 | */ |
||
344 | return retval; |
||
345 | } |
||
346 | |||
347 | int |
||
348 | videobuf_iolock(struct pci_dev *pci, struct videobuf_buffer *vb, |
||
349 | struct v4l2_framebuffer *fbuf) |
||
350 | { |
||
351 | int err,pages; |
||
352 | dma_addr_t bus; |
||
353 | |||
354 | switch (vb->memory) { |
||
355 | case V4L2_MEMORY_MMAP: |
||
356 | case V4L2_MEMORY_USERPTR: |
||
357 | if (0 == vb->baddr) { |
||
358 | /* no userspace addr -- kernel bounce buffer */ |
||
359 | pages = PAGE_ALIGN(vb->size) >> PAGE_SHIFT; |
||
360 | err = 0;//videobuf_dma_init_kernel(&vb->dma,PCI_DMA_FROMDEVICE, |
||
361 | // pages); |
||
362 | if (0 != err) |
||
363 | return err; |
||
364 | } else { |
||
365 | /* dma directly to userspace */ |
||
366 | err = 0;//videobuf_dma_init_user(&vb->dma,PCI_DMA_FROMDEVICE, |
||
367 | // vb->baddr,vb->bsize); |
||
368 | if (0 != err) |
||
369 | return err; |
||
370 | } |
||
371 | break; |
||
372 | case V4L2_MEMORY_OVERLAY: |
||
373 | if (NULL == fbuf) |
||
374 | return -EINVAL; |
||
375 | /* FIXME: need sanity checks for vb->boff */ |
||
376 | bus = (dma_addr_t)fbuf->base + vb->boff; |
||
377 | pages = PAGE_ALIGN(vb->size) >> PAGE_SHIFT; |
||
378 | err = 0;//videobuf_dma_init_overlay(&vb->dma,PCI_DMA_FROMDEVICE, |
||
379 | // bus, pages); |
||
380 | if (0 != err) |
||
381 | return err; |
||
382 | break; |
||
383 | default: |
||
384 | BUG(); |
||
385 | } |
||
386 | err = 0;//videobuf_dma_pci_map(pci,&vb->dma); |
||
387 | if (0 != err) |
||
388 | return err; |
||
389 | |||
390 | return 0; |
||
391 | } |
||
392 | |||
393 | /* --------------------------------------------------------------------- */ |
||
394 | |||
395 | void |
||
396 | videobuf_queue_init(struct videobuf_queue *q, |
||
397 | struct videobuf_queue_ops *ops, |
||
398 | struct pci_dev *pci, |
||
399 | spinlock_t *irqlock, |
||
400 | enum v4l2_buf_type type, |
||
401 | enum v4l2_field field, |
||
402 | unsigned int msize) |
||
403 | { |
||
404 | memset(q,0,sizeof(*q)); |
||
405 | |||
406 | q->irqlock = irqlock; |
||
407 | q->pci = pci; |
||
408 | q->type = type; |
||
409 | q->field = field; |
||
410 | q->msize = msize; |
||
411 | q->ops = ops; |
||
412 | |||
413 | //init_MUTEX(&q->lock); |
||
414 | INIT_LIST_HEAD(&q->stream); |
||
415 | } |
||
416 | |||
417 | int |
||
418 | videobuf_queue_is_busy(struct videobuf_queue *q) |
||
419 | { |
||
420 | int i; |
||
421 | |||
422 | if (q->streaming) { |
||
423 | dprintk(1,"busy: streaming active\n"); |
||
424 | return 1; |
||
425 | } |
||
426 | if (q->reading) { |
||
427 | dprintk(1,"busy: pending read #1\n"); |
||
428 | return 1; |
||
429 | } |
||
430 | if (q->read_buf) { |
||
431 | dprintk(1,"busy: pending read #2\n"); |
||
432 | return 1; |
||
433 | } |
||
434 | for (i = 0; i < VIDEO_MAX_FRAME; i++) { |
||
435 | if (NULL == q->bufs[i]) |
||
436 | continue; |
||
437 | if (q->bufs[i]->map) { |
||
438 | dprintk(1,"busy: buffer #%d mapped\n",i); |
||
439 | return 1; |
||
440 | } |
||
441 | if (q->bufs[i]->state == STATE_QUEUED) { |
||
442 | dprintk(1,"busy: buffer #%d queued\n",i); |
||
443 | return 1; |
||
444 | } |
||
445 | if (q->bufs[i]->state == STATE_ACTIVE) { |
||
446 | dprintk(1,"busy: buffer #%d avtive\n",i); |
||
447 | return 1; |
||
448 | } |
||
449 | } |
||
450 | return 0; |
||
451 | } |
||
452 | |||
453 | void |
||
454 | videobuf_queue_cancel(struct file *file, struct videobuf_queue *q) |
||
455 | { |
||
456 | unsigned long flags; |
||
457 | int i; |
||
458 | |||
459 | /* remove queued buffers from list */ |
||
460 | spin_lock_irqsave(q->irqlock,flags); |
||
461 | for (i = 0; i < VIDEO_MAX_FRAME; i++) { |
||
462 | if (NULL == q->bufs[i]) |
||
463 | continue; |
||
464 | if (q->bufs[i]->state == STATE_QUEUED) { |
||
465 | list_del(&q->bufs[i]->queue); |
||
466 | q->bufs[i]->state = STATE_ERROR; |
||
467 | } |
||
468 | } |
||
469 | spin_unlock_irqrestore(q->irqlock,flags); |
||
470 | |||
471 | /* free all buffers + clear queue */ |
||
472 | for (i = 0; i < VIDEO_MAX_FRAME; i++) { |
||
473 | if (NULL == q->bufs[i]) |
||
474 | continue; |
||
475 | q->ops->buf_release(file,q->bufs[i]); |
||
476 | } |
||
477 | INIT_LIST_HEAD(&q->stream); |
||
478 | } |
||
479 | |||
480 | /* --------------------------------------------------------------------- */ |
||
481 | |||
482 | enum v4l2_field |
||
483 | videobuf_next_field(struct videobuf_queue *q) |
||
484 | { |
||
485 | enum v4l2_field field = q->field; |
||
486 | |||
487 | BUG_ON(V4L2_FIELD_ANY == field); |
||
488 | |||
489 | if (V4L2_FIELD_ALTERNATE == field) { |
||
490 | if (V4L2_FIELD_TOP == q->last) { |
||
491 | field = V4L2_FIELD_BOTTOM; |
||
492 | q->last = V4L2_FIELD_BOTTOM; |
||
493 | } else { |
||
494 | field = V4L2_FIELD_TOP; |
||
495 | q->last = V4L2_FIELD_TOP; |
||
496 | } |
||
497 | } |
||
498 | return field; |
||
499 | } |
||
500 | |||
501 | void |
||
502 | videobuf_status(struct v4l2_buffer *b, struct videobuf_buffer *vb, |
||
503 | enum v4l2_buf_type type) |
||
504 | { |
||
505 | b->index = vb->i; |
||
506 | b->type = type; |
||
507 | |||
508 | b->memory = vb->memory; |
||
509 | switch (b->memory) { |
||
510 | case V4L2_MEMORY_MMAP: |
||
511 | b->m.offset = vb->boff; |
||
512 | b->length = vb->bsize; |
||
513 | break; |
||
514 | case V4L2_MEMORY_USERPTR: |
||
515 | b->m.userptr = vb->baddr; |
||
516 | b->length = vb->bsize; |
||
517 | break; |
||
518 | case V4L2_MEMORY_OVERLAY: |
||
519 | b->m.offset = vb->boff; |
||
520 | break; |
||
521 | } |
||
522 | |||
523 | b->flags = 0; |
||
524 | if (vb->map) |
||
525 | b->flags |= V4L2_BUF_FLAG_MAPPED; |
||
526 | |||
527 | switch (vb->state) { |
||
528 | case STATE_PREPARED: |
||
529 | case STATE_QUEUED: |
||
530 | case STATE_ACTIVE: |
||
531 | b->flags |= V4L2_BUF_FLAG_QUEUED; |
||
532 | break; |
||
533 | case STATE_DONE: |
||
534 | case STATE_ERROR: |
||
535 | b->flags |= V4L2_BUF_FLAG_DONE; |
||
536 | break; |
||
537 | case STATE_NEEDS_INIT: |
||
538 | case STATE_IDLE: |
||
539 | /* nothing */ |
||
540 | break; |
||
541 | } |
||
542 | |||
543 | b->field = vb->field; |
||
544 | b->timestamp = vb->ts; |
||
545 | b->bytesused = vb->size; |
||
546 | b->sequence = vb->field_count >> 1; |
||
547 | } |
||
548 | |||
549 | int |
||
550 | videobuf_reqbufs(struct file *file, struct videobuf_queue *q, |
||
551 | struct v4l2_requestbuffers *req) |
||
552 | { |
||
553 | unsigned int size,count; |
||
554 | int retval; |
||
555 | |||
556 | if (req->type != q->type) |
||
557 | return -EINVAL; |
||
558 | if (req->count < 1) |
||
559 | return -EINVAL; |
||
560 | if (req->memory != V4L2_MEMORY_MMAP && |
||
561 | req->memory != V4L2_MEMORY_USERPTR && |
||
562 | req->memory != V4L2_MEMORY_OVERLAY) |
||
563 | return -EINVAL; |
||
564 | |||
565 | //down(&q->lock); |
||
566 | count = req->count; |
||
567 | if (count > VIDEO_MAX_FRAME) |
||
568 | count = VIDEO_MAX_FRAME; |
||
569 | size = 0; |
||
570 | q->ops->buf_setup(file,&count,&size); |
||
571 | size = PAGE_ALIGN(size); |
||
572 | dprintk(1,"reqbufs: bufs=%d, size=0x%x [%d pages total]\n", |
||
573 | count, size, (count*size)>>PAGE_SHIFT); |
||
574 | |||
575 | retval = videobuf_mmap_setup(file,q,count,size,req->memory); |
||
576 | if (retval < 0) |
||
577 | goto done; |
||
578 | |||
579 | req->count = count; |
||
580 | |||
581 | done: |
||
582 | //up(&q->lock); |
||
583 | return retval; |
||
584 | } |
||
585 | |||
586 | int |
||
587 | videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b) |
||
588 | { |
||
589 | if (unlikely(b->type != q->type)) |
||
590 | return -EINVAL; |
||
591 | if (unlikely(b->index < 0 || b->index >= VIDEO_MAX_FRAME)) |
||
592 | return -EINVAL; |
||
593 | if (unlikely(NULL == q->bufs[b->index])) |
||
594 | return -EINVAL; |
||
595 | videobuf_status(b,q->bufs[b->index],q->type); |
||
596 | return 0; |
||
597 | } |
||
598 | |||
599 | int |
||
600 | videobuf_qbuf(struct file *file, struct videobuf_queue *q, |
||
601 | struct v4l2_buffer *b) |
||
602 | { |
||
603 | struct videobuf_buffer *buf; |
||
604 | enum v4l2_field field; |
||
605 | unsigned long flags; |
||
606 | int retval; |
||
607 | |||
608 | //down(&q->lock); |
||
609 | retval = -EBUSY; |
||
610 | if (q->reading) |
||
611 | goto done; |
||
612 | retval = -EINVAL; |
||
613 | if (b->type != q->type) |
||
614 | goto done; |
||
615 | if (b->index < 0 || b->index >= VIDEO_MAX_FRAME) |
||
616 | goto done; |
||
617 | buf = q->bufs[b->index]; |
||
618 | if (NULL == buf) |
||
619 | goto done; |
||
620 | if (buf->memory != b->memory) |
||
621 | goto done; |
||
622 | if (buf->state == STATE_QUEUED || |
||
623 | buf->state == STATE_ACTIVE) |
||
624 | goto done; |
||
625 | |||
626 | switch (b->memory) { |
||
627 | case V4L2_MEMORY_MMAP: |
||
628 | if (0 == buf->baddr) |
||
629 | goto done; |
||
630 | break; |
||
631 | case V4L2_MEMORY_USERPTR: |
||
632 | if (b->length < buf->bsize) |
||
633 | goto done; |
||
634 | buf->baddr = b->m.userptr; |
||
635 | break; |
||
636 | case V4L2_MEMORY_OVERLAY: |
||
637 | buf->boff = b->m.offset; |
||
638 | break; |
||
639 | default: |
||
640 | goto done; |
||
641 | } |
||
642 | |||
643 | field = videobuf_next_field(q); |
||
644 | retval = q->ops->buf_prepare(file,buf,field); |
||
645 | if (0 != retval) |
||
646 | goto done; |
||
647 | |||
648 | list_add_tail(&buf->stream,&q->stream); |
||
649 | if (q->streaming) { |
||
650 | spin_lock_irqsave(q->irqlock,flags); |
||
651 | q->ops->buf_queue(file,buf); |
||
652 | spin_unlock_irqrestore(q->irqlock,flags); |
||
653 | } |
||
654 | retval = 0; |
||
655 | |||
656 | done: |
||
657 | //up(&q->lock); |
||
658 | return retval; |
||
659 | } |
||
660 | |||
661 | int |
||
662 | videobuf_dqbuf(struct file *file, struct videobuf_queue *q, |
||
663 | struct v4l2_buffer *b) |
||
664 | { |
||
665 | struct videobuf_buffer *buf; |
||
666 | int retval; |
||
667 | |||
668 | //down(&q->lock); |
||
669 | retval = -EBUSY; |
||
670 | if (q->reading) |
||
671 | goto done; |
||
672 | retval = -EINVAL; |
||
673 | if (b->type != q->type) |
||
674 | goto done; |
||
675 | if (list_empty(&q->stream)) |
||
676 | goto done; |
||
677 | buf = list_entry(q->stream.next, struct videobuf_buffer, stream); |
||
678 | retval = videobuf_waiton(buf, file->f_flags & O_NONBLOCK, 1); |
||
679 | if (retval < 0) |
||
680 | goto done; |
||
681 | switch (buf->state) { |
||
682 | case STATE_ERROR: |
||
683 | retval = -EIO; |
||
684 | /* fall through */ |
||
685 | case STATE_DONE: |
||
686 | //videobuf_dma_pci_sync(q->pci,&buf->dma); |
||
687 | buf->state = STATE_IDLE; |
||
688 | break; |
||
689 | default: |
||
690 | retval = -EINVAL; |
||
691 | goto done; |
||
692 | } |
||
693 | list_del(&buf->stream); |
||
694 | memset(b,0,sizeof(*b)); |
||
695 | videobuf_status(b,buf,q->type); |
||
696 | |||
697 | done: |
||
698 | //up(&q->lock); |
||
699 | return retval; |
||
700 | } |
||
701 | |||
702 | int videobuf_streamon(struct file *file, struct videobuf_queue *q) |
||
703 | { |
||
704 | struct videobuf_buffer *buf; |
||
705 | struct list_head *list; |
||
706 | unsigned long flags; |
||
707 | int retval; |
||
708 | |||
709 | //down(&q->lock); |
||
710 | retval = -EBUSY; |
||
711 | if (q->reading) |
||
712 | goto done; |
||
713 | retval = 0; |
||
714 | if (q->streaming) |
||
715 | goto done; |
||
716 | q->streaming = 1; |
||
717 | spin_lock_irqsave(q->irqlock,flags); |
||
718 | list_for_each(list,&q->stream) { |
||
719 | buf = list_entry(list, struct videobuf_buffer, stream); |
||
720 | if (buf->state == STATE_PREPARED) |
||
721 | q->ops->buf_queue(file,buf); |
||
722 | } |
||
723 | spin_unlock_irqrestore(q->irqlock,flags); |
||
724 | |||
725 | done: |
||
726 | //up(&q->lock); |
||
727 | return retval; |
||
728 | } |
||
729 | |||
730 | int videobuf_streamoff(struct file *file, struct videobuf_queue *q) |
||
731 | { |
||
732 | int retval = -EINVAL; |
||
733 | |||
734 | //down(&q->lock); |
||
735 | if (!q->streaming) |
||
736 | goto done; |
||
737 | videobuf_queue_cancel(file,q); |
||
738 | q->streaming = 0; |
||
739 | retval = 0; |
||
740 | |||
741 | done: |
||
742 | //up(&q->lock); |
||
743 | return retval; |
||
744 | } |
||
745 | |||
746 | static ssize_t |
||
747 | videobuf_read_zerocopy(struct file *file, struct videobuf_queue *q, |
||
748 | char *data, size_t count, loff_t *ppos) |
||
749 | { |
||
750 | enum v4l2_field field; |
||
751 | unsigned long flags; |
||
752 | int retval; |
||
753 | |||
754 | /* setup stuff */ |
||
755 | retval = -ENOMEM; |
||
756 | q->read_buf = videobuf_alloc(q->msize); |
||
757 | if (NULL == q->read_buf) |
||
758 | goto done; |
||
759 | |||
760 | q->read_buf->memory = V4L2_MEMORY_USERPTR; |
||
761 | q->read_buf->baddr = (unsigned long)data; |
||
762 | q->read_buf->bsize = count; |
||
763 | field = videobuf_next_field(q); |
||
764 | retval = q->ops->buf_prepare(file,q->read_buf,field); |
||
765 | if (0 != retval) |
||
766 | goto done; |
||
767 | |||
768 | /* start capture & wait */ |
||
769 | spin_lock_irqsave(q->irqlock,flags); |
||
770 | q->ops->buf_queue(file,q->read_buf); |
||
771 | spin_unlock_irqrestore(q->irqlock,flags); |
||
772 | retval = videobuf_waiton(q->read_buf,0,0); |
||
773 | if (0 == retval) { |
||
774 | //videobuf_dma_pci_sync(q->pci,&q->read_buf->dma); |
||
775 | if (STATE_ERROR == q->read_buf->state) |
||
776 | retval = -EIO; |
||
777 | else |
||
778 | retval = q->read_buf->size; |
||
779 | } |
||
780 | |||
781 | done: |
||
782 | /* cleanup */ |
||
783 | q->ops->buf_release(file,q->read_buf); |
||
784 | kfree(q->read_buf); |
||
785 | q->read_buf = NULL; |
||
786 | return retval; |
||
787 | } |
||
788 | |||
789 | ssize_t videobuf_read_one(struct file *file, struct videobuf_queue *q, |
||
790 | char *data, size_t count, loff_t *ppos) |
||
791 | { |
||
792 | enum v4l2_field field; |
||
793 | unsigned long flags; |
||
794 | unsigned size, nbufs, bytes; |
||
795 | int retval; |
||
796 | |||
797 | //down(&q->lock); |
||
798 | |||
799 | nbufs = 1; size = 0; |
||
800 | q->ops->buf_setup(file,&nbufs,&size); |
||
801 | if (NULL == q->read_buf && |
||
802 | count >= size && |
||
803 | !(file->f_flags & O_NONBLOCK)) { |
||
804 | retval = videobuf_read_zerocopy(file,q,data,count,ppos); |
||
805 | if (retval >= 0 || retval == -EIO) |
||
806 | /* ok, all done */ |
||
807 | goto done; |
||
808 | /* fallback to kernel bounce buffer on failures */ |
||
809 | } |
||
810 | |||
811 | if (NULL == q->read_buf) { |
||
812 | /* need to capture a new frame */ |
||
813 | retval = -ENOMEM; |
||
814 | q->read_buf = videobuf_alloc(q->msize); |
||
815 | if (NULL == q->read_buf) |
||
816 | goto done; |
||
817 | q->read_buf->memory = V4L2_MEMORY_USERPTR; |
||
818 | field = videobuf_next_field(q); |
||
819 | retval = q->ops->buf_prepare(file,q->read_buf,field); |
||
820 | if (0 != retval) |
||
821 | goto done; |
||
822 | spin_lock_irqsave(q->irqlock,flags); |
||
823 | q->ops->buf_queue(file,q->read_buf); |
||
824 | spin_unlock_irqrestore(q->irqlock,flags); |
||
825 | q->read_off = 0; |
||
826 | } |
||
827 | |||
828 | /* wait until capture is done */ |
||
829 | retval = videobuf_waiton(q->read_buf, file->f_flags & O_NONBLOCK, 1); |
||
830 | if (0 != retval) |
||
831 | goto done; |
||
832 | //videobuf_dma_pci_sync(q->pci,&q->read_buf->dma); |
||
833 | |||
834 | if (STATE_ERROR == q->read_buf->state) { |
||
835 | /* catch I/O errors */ |
||
836 | q->ops->buf_release(file,q->read_buf); |
||
837 | kfree(q->read_buf); |
||
838 | q->read_buf = NULL; |
||
839 | retval = -EIO; |
||
840 | goto done; |
||
841 | } |
||
842 | |||
843 | /* copy to userspace */ |
||
844 | bytes = count; |
||
845 | if (bytes > q->read_buf->size - q->read_off) |
||
846 | bytes = q->read_buf->size - q->read_off; |
||
847 | retval = -EFAULT; |
||
848 | if (copy_to_user(data, q->read_buf->dma.vmalloc+q->read_off, bytes)) |
||
849 | goto done; |
||
850 | |||
851 | retval = bytes; |
||
852 | q->read_off += bytes; |
||
853 | if (q->read_off == q->read_buf->size) { |
||
854 | /* all data copied, cleanup */ |
||
855 | q->ops->buf_release(file,q->read_buf); |
||
856 | kfree(q->read_buf); |
||
857 | q->read_buf = NULL; |
||
858 | } |
||
859 | |||
860 | done: |
||
861 | //up(&q->lock); |
||
862 | return retval; |
||
863 | } |
||
864 | |||
865 | int videobuf_read_start(struct file *file, struct videobuf_queue *q) |
||
866 | { |
||
867 | enum v4l2_field field; |
||
868 | unsigned long flags; |
||
869 | int count = 0, size = 0; |
||
870 | int err, i; |
||
871 | |||
872 | q->ops->buf_setup(file,&count,&size); |
||
873 | if (count < 2) |
||
874 | count = 2; |
||
875 | if (count > VIDEO_MAX_FRAME) |
||
876 | count = VIDEO_MAX_FRAME; |
||
877 | size = PAGE_ALIGN(size); |
||
878 | |||
879 | err = videobuf_mmap_setup(file, q, count, size, V4L2_MEMORY_USERPTR); |
||
880 | if (err) |
||
881 | return err; |
||
882 | for (i = 0; i < count; i++) { |
||
883 | field = videobuf_next_field(q); |
||
884 | err = q->ops->buf_prepare(file,q->bufs[i],field); |
||
885 | if (err) |
||
886 | return err; |
||
887 | list_add_tail(&q->bufs[i]->stream, &q->stream); |
||
888 | } |
||
889 | spin_lock_irqsave(q->irqlock,flags); |
||
890 | for (i = 0; i < count; i++) |
||
891 | q->ops->buf_queue(file,q->bufs[i]); |
||
892 | spin_unlock_irqrestore(q->irqlock,flags); |
||
893 | q->reading = 1; |
||
894 | return 0; |
||
895 | } |
||
896 | |||
897 | void videobuf_read_stop(struct file *file, struct videobuf_queue *q) |
||
898 | { |
||
899 | int i; |
||
900 | |||
901 | videobuf_queue_cancel(file,q); |
||
902 | INIT_LIST_HEAD(&q->stream); |
||
903 | for (i = 0; i < VIDEO_MAX_FRAME; i++) { |
||
904 | if (NULL == q->bufs[i]) |
||
905 | continue; |
||
906 | kfree(q->bufs[i]); |
||
907 | q->bufs[i] = NULL; |
||
908 | } |
||
909 | q->read_buf = NULL; |
||
910 | q->reading = 0; |
||
911 | } |
||
912 | |||
913 | ssize_t videobuf_read_stream(struct file *file, struct videobuf_queue *q, |
||
914 | char *data, size_t count, loff_t *ppos, |
||
915 | int vbihack) |
||
916 | { |
||
917 | unsigned int *fc, bytes; |
||
918 | int err, retval; |
||
919 | unsigned long flags; |
||
920 | |||
921 | //down(&q->lock); |
||
922 | retval = -EBUSY; |
||
923 | if (q->streaming) |
||
924 | goto done; |
||
925 | if (!q->reading) { |
||
926 | retval = videobuf_read_start(file,q); |
||
927 | if (retval < 0) |
||
928 | goto done; |
||
929 | } |
||
930 | |||
931 | retval = 0; |
||
932 | while (count > 0) { |
||
933 | /* get / wait for data */ |
||
934 | if (NULL == q->read_buf) { |
||
935 | q->read_buf = list_entry(q->stream.next, |
||
936 | struct videobuf_buffer, |
||
937 | stream); |
||
938 | list_del(&q->read_buf->stream); |
||
939 | q->read_off = 0; |
||
940 | } |
||
941 | err = videobuf_waiton(q->read_buf, |
||
942 | file->f_flags & O_NONBLOCK,1); |
||
943 | if (err < 0) { |
||
944 | if (0 == retval) |
||
945 | retval = err; |
||
946 | break; |
||
947 | } |
||
948 | |||
949 | if (q->read_buf->state == STATE_DONE) { |
||
950 | if (vbihack) { |
||
951 | /* dirty, undocumented hack -- pass the frame counter |
||
952 | * within the last four bytes of each vbi data block. |
||
953 | * We need that one to maintain backward compatibility |
||
954 | * to all vbi decoding software out there ... */ |
||
955 | fc = (unsigned int*)q->read_buf->dma.vmalloc; |
||
956 | fc += (q->read_buf->size>>2) -1; |
||
957 | *fc = q->read_buf->field_count >> 1; |
||
958 | dprintk(1,"vbihack: %d\n",*fc); |
||
959 | } |
||
960 | |||
961 | /* copy stuff */ |
||
962 | bytes = count; |
||
963 | if (bytes > q->read_buf->size - q->read_off) |
||
964 | bytes = q->read_buf->size - q->read_off; |
||
965 | if (copy_to_user(data + retval, |
||
966 | q->read_buf->dma.vmalloc + q->read_off, |
||
967 | bytes)) { |
||
968 | if (0 == retval) |
||
969 | retval = -EFAULT; |
||
970 | break; |
||
971 | } |
||
972 | count -= bytes; |
||
973 | retval += bytes; |
||
974 | q->read_off += bytes; |
||
975 | } else { |
||
976 | /* some error -- skip buffer */ |
||
977 | q->read_off = q->read_buf->size; |
||
978 | } |
||
979 | |||
980 | /* requeue buffer when done with copying */ |
||
981 | if (q->read_off == q->read_buf->size) { |
||
982 | list_add_tail(&q->read_buf->stream, |
||
983 | &q->stream); |
||
984 | spin_lock_irqsave(q->irqlock,flags); |
||
985 | q->ops->buf_queue(file,q->read_buf); |
||
986 | spin_unlock_irqrestore(q->irqlock,flags); |
||
987 | q->read_buf = NULL; |
||
988 | } |
||
989 | } |
||
990 | |||
991 | done: |
||
992 | //up(&q->lock); |
||
993 | return retval; |
||
994 | } |
||
995 | |||
996 | unsigned int videobuf_poll_stream(struct file *file, |
||
997 | struct videobuf_queue *q, |
||
998 | poll_table *wait) |
||
999 | { |
||
1000 | struct videobuf_buffer *buf = NULL; |
||
1001 | unsigned int rc = 0; |
||
1002 | |||
1003 | //down(&q->lock); |
||
1004 | if (q->streaming) { |
||
1005 | if (!list_empty(&q->stream)) |
||
1006 | buf = list_entry(q->stream.next, |
||
1007 | struct videobuf_buffer, stream); |
||
1008 | } else { |
||
1009 | if (!q->reading) |
||
1010 | videobuf_read_start(file,q); |
||
1011 | if (!q->reading) { |
||
1012 | rc = POLLERR; |
||
1013 | } else if (NULL == q->read_buf) { |
||
1014 | q->read_buf = list_entry(q->stream.next, |
||
1015 | struct videobuf_buffer, |
||
1016 | stream); |
||
1017 | list_del(&q->read_buf->stream); |
||
1018 | q->read_off = 0; |
||
1019 | } |
||
1020 | buf = q->read_buf; |
||
1021 | } |
||
1022 | if (!buf) |
||
1023 | rc = POLLERR; |
||
1024 | |||
1025 | if (0 == rc) { |
||
1026 | poll_wait(file, &buf->done, wait); |
||
1027 | if (buf->state == STATE_DONE || |
||
1028 | buf->state == STATE_ERROR) |
||
1029 | rc = POLLIN|POLLRDNORM; |
||
1030 | } |
||
1031 | //up(&q->lock); |
||
1032 | return rc; |
||
1033 | } |
||
1034 | |||
1035 | /* --------------------------------------------------------------------- */ |
||
1036 | |||
1037 | static void |
||
1038 | videobuf_vm_open(struct vm_area_struct *vma) |
||
1039 | { |
||
1040 | struct videobuf_mapping *map = vma->vm_private_data; |
||
1041 | |||
1042 | dprintk(2,"vm_open %p [count=%d,vma=%08lx-%08lx]\n",map, |
||
1043 | map->count,vma->vm_start,vma->vm_end); |
||
1044 | map->count++; |
||
1045 | } |
||
1046 | |||
1047 | static void |
||
1048 | videobuf_vm_close(struct vm_area_struct *vma) |
||
1049 | { |
||
1050 | struct videobuf_mapping *map = vma->vm_private_data; |
||
1051 | int i; |
||
1052 | |||
1053 | dprintk(2,"vm_close %p [count=%d,vma=%08lx-%08lx]\n",map, |
||
1054 | map->count,vma->vm_start,vma->vm_end); |
||
1055 | |||
1056 | /* down(&fh->lock); FIXME */ |
||
1057 | map->count--; |
||
1058 | if (0 == map->count) { |
||
1059 | dprintk(1,"munmap %p\n",map); |
||
1060 | for (i = 0; i < VIDEO_MAX_FRAME; i++) { |
||
1061 | if (NULL == map->q->bufs[i]) |
||
1062 | continue; |
||
1063 | if (map->q->bufs[i]) |
||
1064 | ; |
||
1065 | if (map->q->bufs[i]->map != map) |
||
1066 | continue; |
||
1067 | map->q->bufs[i]->map = NULL; |
||
1068 | map->q->bufs[i]->baddr = 0; |
||
1069 | map->q->ops->buf_release(vma->vm_file,map->q->bufs[i]); |
||
1070 | } |
||
1071 | kfree(map); |
||
1072 | } |
||
1073 | /* up(&fh->lock); FIXME */ |
||
1074 | return; |
||
1075 | } |
||
1076 | |||
1077 | /* |
||
1078 | * Get a anonymous page for the mapping. Make sure we can DMA to that |
||
1079 | * memory location with 32bit PCI devices (i.e. don't use highmem for |
||
1080 | * now ...). Bounce buffers don't work very well for the data rates |
||
1081 | * video capture has. |
||
1082 | */ |
||
1083 | static struct page* |
||
1084 | videobuf_vm_nopage(struct vm_area_struct *vma, unsigned long vaddr, |
||
1085 | int write_access) |
||
1086 | { |
||
1087 | struct page *page; |
||
1088 | |||
1089 | dprintk(3,"nopage: fault @ %08lx [vma %08lx-%08lx]\n", |
||
1090 | vaddr,vma->vm_start,vma->vm_end); |
||
1091 | if (vaddr > vma->vm_end) |
||
1092 | return NOPAGE_SIGBUS; |
||
1093 | page = kmalloc(sizeof(struct page),GFP_KERNEL); |
||
1094 | if (!page) |
||
1095 | return NOPAGE_OOM; |
||
1096 | //clear_user_page(page_address(page), vaddr, page); |
||
1097 | return page; |
||
1098 | } |
||
1099 | |||
1100 | static struct vm_operations_struct videobuf_vm_ops = |
||
1101 | { |
||
1102 | .open = videobuf_vm_open, |
||
1103 | .close = videobuf_vm_close, |
||
1104 | .nopage = videobuf_vm_nopage, |
||
1105 | }; |
||
1106 | |||
1107 | int videobuf_mmap_setup(struct file *file, struct videobuf_queue *q, |
||
1108 | unsigned int bcount, unsigned int bsize, |
||
1109 | enum v4l2_memory memory) |
||
1110 | { |
||
1111 | unsigned int i; |
||
1112 | int err; |
||
1113 | |||
1114 | err = videobuf_mmap_free(file,q); |
||
1115 | if (0 != err) |
||
1116 | return err; |
||
1117 | |||
1118 | for (i = 0; i < bcount; i++) { |
||
1119 | q->bufs[i] = videobuf_alloc(q->msize); |
||
1120 | q->bufs[i]->i = i; |
||
1121 | q->bufs[i]->memory = memory; |
||
1122 | q->bufs[i]->bsize = bsize; |
||
1123 | switch (memory) { |
||
1124 | case V4L2_MEMORY_MMAP: |
||
1125 | q->bufs[i]->boff = bsize * i; |
||
1126 | break; |
||
1127 | case V4L2_MEMORY_USERPTR: |
||
1128 | case V4L2_MEMORY_OVERLAY: |
||
1129 | /* nothing */ |
||
1130 | break; |
||
1131 | }; |
||
1132 | } |
||
1133 | dprintk(1,"mmap setup: %d buffers, %d bytes each\n", |
||
1134 | bcount,bsize); |
||
1135 | return 0; |
||
1136 | } |
||
1137 | |||
1138 | int videobuf_mmap_free(struct file *file, struct videobuf_queue *q) |
||
1139 | { |
||
1140 | int i; |
||
1141 | |||
1142 | for (i = 0; i < VIDEO_MAX_FRAME; i++) |
||
1143 | if (q->bufs[i] && q->bufs[i]->map) |
||
1144 | return -EBUSY; |
||
1145 | for (i = 0; i < VIDEO_MAX_FRAME; i++) { |
||
1146 | if (NULL == q->bufs[i]) |
||
1147 | continue; |
||
1148 | q->ops->buf_release(file,q->bufs[i]); |
||
1149 | kfree(q->bufs[i]); |
||
1150 | q->bufs[i] = NULL; |
||
1151 | } |
||
1152 | return 0; |
||
1153 | } |
||
1154 | |||
1155 | int videobuf_mmap_mapper(struct vm_area_struct *vma, |
||
1156 | struct videobuf_queue *q) |
||
1157 | { |
||
1158 | struct videobuf_mapping *map; |
||
1159 | unsigned int first,last,size,i; |
||
1160 | int retval; |
||
1161 | |||
1162 | //down(&q->lock); |
||
1163 | |||
1164 | retval = -EINVAL; |
||
1165 | if (!(vma->vm_flags & VM_WRITE)) { |
||
1166 | dprintk(1,"mmap app bug: PROT_WRITE please\n"); |
||
1167 | goto done; |
||
1168 | } |
||
1169 | if (!(vma->vm_flags & VM_SHARED)) { |
||
1170 | dprintk(1,"mmap app bug: MAP_SHARED please\n"); |
||
1171 | goto done; |
||
1172 | } |
||
1173 | |||
1174 | /* look for first buffer to map */ |
||
1175 | for (first = 0; first < VIDEO_MAX_FRAME; first++) { |
||
1176 | if (NULL == q->bufs[first]) |
||
1177 | continue; |
||
1178 | if (V4L2_MEMORY_MMAP != q->bufs[first]->memory) |
||
1179 | continue; |
||
1180 | if (q->bufs[first]->boff == (vma->vm_pgoff << PAGE_SHIFT)) |
||
1181 | break; |
||
1182 | } |
||
1183 | if (VIDEO_MAX_FRAME == first) { |
||
1184 | dprintk(1,"mmap app bug: offset invalid [offset=0x%lx]\n", |
||
1185 | (vma->vm_pgoff << PAGE_SHIFT)); |
||
1186 | goto done; |
||
1187 | } |
||
1188 | |||
1189 | /* look for last buffer to map */ |
||
1190 | for (size = 0, last = first; last < VIDEO_MAX_FRAME; last++) { |
||
1191 | if (NULL == q->bufs[last]) |
||
1192 | continue; |
||
1193 | if (V4L2_MEMORY_MMAP != q->bufs[last]->memory) |
||
1194 | continue; |
||
1195 | if (q->bufs[last]->map) { |
||
1196 | retval = -EBUSY; |
||
1197 | goto done; |
||
1198 | } |
||
1199 | size += q->bufs[last]->bsize; |
||
1200 | if (size == (vma->vm_end - vma->vm_start)) |
||
1201 | break; |
||
1202 | } |
||
1203 | if (VIDEO_MAX_FRAME == last) { |
||
1204 | dprintk(1,"mmap app bug: size invalid [size=0x%lx]\n", |
||
1205 | (vma->vm_end - vma->vm_start)); |
||
1206 | goto done; |
||
1207 | } |
||
1208 | |||
1209 | /* create mapping + update buffer list */ |
||
1210 | retval = -ENOMEM; |
||
1211 | map = kmalloc(sizeof(struct videobuf_mapping),GFP_KERNEL); |
||
1212 | if (NULL == map) |
||
1213 | goto done; |
||
1214 | for (size = 0, i = first; i <= last; size += q->bufs[i++]->bsize) { |
||
1215 | q->bufs[i]->map = map; |
||
1216 | q->bufs[i]->baddr = vma->vm_start + size; |
||
1217 | } |
||
1218 | map->count = 1; |
||
1219 | map->start = vma->vm_start; |
||
1220 | map->end = vma->vm_end; |
||
1221 | map->q = q; |
||
1222 | vma->vm_ops = &videobuf_vm_ops; |
||
1223 | vma->vm_flags |= VM_DONTEXPAND; |
||
1224 | vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */ |
||
1225 | vma->vm_private_data = map; |
||
1226 | dprintk(1,"mmap %p: %08lx-%08lx pgoff %08lx bufs %d-%d\n", |
||
1227 | map,vma->vm_start,vma->vm_end,vma->vm_pgoff,first,last); |
||
1228 | retval = 0; |
||
1229 | |||
1230 | done: |
||
1231 | //up(&q->lock); |
||
1232 | return retval; |
||
1233 | } |
||
1234 | |||
1235 | /* --------------------------------------------------------------------- */ |
||
1236 | |||
1237 | EXPORT_SYMBOL_GPL(videobuf_vmalloc_to_sg); |
||
1238 | EXPORT_SYMBOL_GPL(videobuf_lock); |
||
1239 | EXPORT_SYMBOL_GPL(videobuf_unlock); |
||
1240 | |||
1241 | EXPORT_SYMBOL_GPL(videobuf_dma_init_user); |
||
1242 | EXPORT_SYMBOL_GPL(videobuf_dma_init_kernel); |
||
1243 | EXPORT_SYMBOL_GPL(videobuf_dma_init_overlay); |
||
1244 | EXPORT_SYMBOL_GPL(videobuf_dma_pci_map); |
||
1245 | EXPORT_SYMBOL_GPL(videobuf_dma_pci_sync); |
||
1246 | EXPORT_SYMBOL_GPL(videobuf_dma_pci_unmap); |
||
1247 | EXPORT_SYMBOL_GPL(videobuf_dma_free); |
||
1248 | |||
1249 | EXPORT_SYMBOL_GPL(videobuf_alloc); |
||
1250 | EXPORT_SYMBOL_GPL(videobuf_waiton); |
||
1251 | EXPORT_SYMBOL_GPL(videobuf_iolock); |
||
1252 | |||
1253 | EXPORT_SYMBOL_GPL(videobuf_queue_init); |
||
1254 | EXPORT_SYMBOL_GPL(videobuf_queue_cancel); |
||
1255 | EXPORT_SYMBOL_GPL(videobuf_queue_is_busy); |
||
1256 | |||
1257 | EXPORT_SYMBOL_GPL(videobuf_next_field); |
||
1258 | EXPORT_SYMBOL_GPL(videobuf_status); |
||
1259 | EXPORT_SYMBOL_GPL(videobuf_reqbufs); |
||
1260 | EXPORT_SYMBOL_GPL(videobuf_querybuf); |
||
1261 | EXPORT_SYMBOL_GPL(videobuf_qbuf); |
||
1262 | EXPORT_SYMBOL_GPL(videobuf_dqbuf); |
||
1263 | EXPORT_SYMBOL_GPL(videobuf_streamon); |
||
1264 | EXPORT_SYMBOL_GPL(videobuf_streamoff); |
||
1265 | |||
1266 | EXPORT_SYMBOL_GPL(videobuf_read_start); |
||
1267 | EXPORT_SYMBOL_GPL(videobuf_read_stop); |
||
1268 | EXPORT_SYMBOL_GPL(videobuf_read_stream); |
||
1269 | EXPORT_SYMBOL_GPL(videobuf_read_one); |
||
1270 | EXPORT_SYMBOL_GPL(videobuf_poll_stream); |
||
1271 | |||
1272 | EXPORT_SYMBOL_GPL(videobuf_mmap_setup); |
||
1273 | EXPORT_SYMBOL_GPL(videobuf_mmap_free); |
||
1274 | EXPORT_SYMBOL_GPL(videobuf_mmap_mapper); |
||
1275 | |||
1276 | /* |
||
1277 | * Local variables: |
||
1278 | * c-basic-offset: 8 |
||
1279 | * End: |
||
1280 | */ |