📄 video-buf.c
字号:
done:
up(&q->lock);
return retval;
}
int videobuf_read_start(struct file *file, struct videobuf_queue *q)
{
enum v4l2_field field;
unsigned long flags;
int count = 0, size = 0;
int err, i;
q->ops->buf_setup(file,&count,&size);
if (count < 2)
count = 2;
if (count > VIDEO_MAX_FRAME)
count = VIDEO_MAX_FRAME;
size = PAGE_ALIGN(size);
err = videobuf_mmap_setup(file, q, count, size, V4L2_MEMORY_USERPTR);
if (err)
return err;
for (i = 0; i < count; i++) {
field = videobuf_next_field(q);
err = q->ops->buf_prepare(file,q->bufs[i],field);
if (err)
return err;
list_add_tail(&q->bufs[i]->stream, &q->stream);
}
spin_lock_irqsave(q->irqlock,flags);
for (i = 0; i < count; i++)
q->ops->buf_queue(file,q->bufs[i]);
spin_unlock_irqrestore(q->irqlock,flags);
q->reading = 1;
return 0;
}
void videobuf_read_stop(struct file *file, struct videobuf_queue *q)
{
int i;
videobuf_queue_cancel(file,q);
INIT_LIST_HEAD(&q->stream);
for (i = 0; i < VIDEO_MAX_FRAME; i++) {
if (NULL == q->bufs[i])
continue;
kfree(q->bufs[i]);
q->bufs[i] = NULL;
}
q->read_buf = NULL;
q->reading = 0;
}
ssize_t videobuf_read_stream(struct file *file, struct videobuf_queue *q,
char *data, size_t count, loff_t *ppos,
int vbihack)
{
unsigned int *fc, bytes;
int err, retval;
unsigned long flags;
down(&q->lock);
retval = -EBUSY;
if (q->streaming)
goto done;
if (!q->reading) {
retval = videobuf_read_start(file,q);
if (retval < 0)
goto done;
}
retval = 0;
while (count > 0) {
/* get / wait for data */
if (NULL == q->read_buf) {
q->read_buf = list_entry(q->stream.next,
struct videobuf_buffer,
stream);
list_del(&q->read_buf->stream);
q->read_off = 0;
}
err = videobuf_waiton(q->read_buf,
file->f_flags & O_NONBLOCK,1);
if (err < 0) {
if (0 == retval)
retval = err;
break;
}
if (q->read_buf->state == STATE_DONE) {
if (vbihack) {
/* dirty, undocumented hack -- pass the frame counter
* within the last four bytes of each vbi data block.
* We need that one to maintain backward compatibility
* to all vbi decoding software out there ... */
fc = (unsigned int*)q->read_buf->dma.vmalloc;
fc += (q->read_buf->size>>2) -1;
*fc = q->read_buf->field_count >> 1;
dprintk(1,"vbihack: %d\n",*fc);
}
/* copy stuff */
bytes = count;
if (bytes > q->read_buf->size - q->read_off)
bytes = q->read_buf->size - q->read_off;
if (copy_to_user(data + retval,
q->read_buf->dma.vmalloc + q->read_off,
bytes)) {
if (0 == retval)
retval = -EFAULT;
break;
}
count -= bytes;
retval += bytes;
q->read_off += bytes;
} else {
/* some error */
q->read_off = q->read_buf->size;
if (0 == retval)
retval = -EIO;
}
/* requeue buffer when done with copying */
if (q->read_off == q->read_buf->size) {
list_add_tail(&q->read_buf->stream,
&q->stream);
spin_lock_irqsave(q->irqlock,flags);
q->ops->buf_queue(file,q->read_buf);
spin_unlock_irqrestore(q->irqlock,flags);
q->read_buf = NULL;
}
if (retval < 0)
break;
}
done:
up(&q->lock);
return retval;
}
unsigned int videobuf_poll_stream(struct file *file,
struct videobuf_queue *q,
poll_table *wait)
{
struct videobuf_buffer *buf = NULL;
unsigned int rc = 0;
down(&q->lock);
if (q->streaming) {
if (!list_empty(&q->stream))
buf = list_entry(q->stream.next,
struct videobuf_buffer, stream);
} else {
if (!q->reading)
videobuf_read_start(file,q);
if (!q->reading) {
rc = POLLERR;
} else if (NULL == q->read_buf) {
q->read_buf = list_entry(q->stream.next,
struct videobuf_buffer,
stream);
list_del(&q->read_buf->stream);
q->read_off = 0;
}
buf = q->read_buf;
}
if (!buf)
rc = POLLERR;
if (0 == rc) {
poll_wait(file, &buf->done, wait);
if (buf->state == STATE_DONE ||
buf->state == STATE_ERROR)
rc = POLLIN|POLLRDNORM;
}
up(&q->lock);
return rc;
}
/* --------------------------------------------------------------------- */
static void
videobuf_vm_open(struct vm_area_struct *vma)
{
struct videobuf_mapping *map = vma->vm_private_data;
dprintk(2,"vm_open %p [count=%d,vma=%08lx-%08lx]\n",map,
map->count,vma->vm_start,vma->vm_end);
map->count++;
}
static void
videobuf_vm_close(struct vm_area_struct *vma)
{
struct videobuf_mapping *map = vma->vm_private_data;
int i;
dprintk(2,"vm_close %p [count=%d,vma=%08lx-%08lx]\n",map,
map->count,vma->vm_start,vma->vm_end);
/* down(&fh->lock); FIXME */
map->count--;
if (0 == map->count) {
dprintk(1,"munmap %p\n",map);
for (i = 0; i < VIDEO_MAX_FRAME; i++) {
if (NULL == map->q->bufs[i])
continue;
if (map->q->bufs[i])
;
if (map->q->bufs[i]->map != map)
continue;
map->q->bufs[i]->map = NULL;
map->q->bufs[i]->baddr = 0;
map->q->ops->buf_release(vma->vm_file,map->q->bufs[i]);
}
kfree(map);
}
/* up(&fh->lock); FIXME */
return;
}
/*
* Get a anonymous page for the mapping. Make sure we can DMA to that
* memory location with 32bit PCI devices (i.e. don't use highmem for
* now ...). Bounce buffers don't work very well for the data rates
* video capture has.
*/
static struct page*
videobuf_vm_nopage(struct vm_area_struct *vma, unsigned long vaddr,
int write_access)
{
struct page *page;
dprintk(3,"nopage: fault @ %08lx [vma %08lx-%08lx]\n",
vaddr,vma->vm_start,vma->vm_end);
if (vaddr > vma->vm_end)
return NOPAGE_SIGBUS;
page = alloc_page(GFP_USER);
if (!page)
return NOPAGE_OOM;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,20)
clear_user_page(page_address(page), vaddr);
#else
clear_user_page(page_address(page), vaddr, page);
#endif
return page;
}
static struct vm_operations_struct videobuf_vm_ops =
{
.open = videobuf_vm_open,
.close = videobuf_vm_close,
.nopage = videobuf_vm_nopage,
};
int videobuf_mmap_setup(struct file *file, struct videobuf_queue *q,
unsigned int bcount, unsigned int bsize,
enum v4l2_memory memory)
{
unsigned int i;
int err;
err = videobuf_mmap_free(file,q);
if (0 != err)
return err;
for (i = 0; i < bcount; i++) {
q->bufs[i] = videobuf_alloc(q->msize);
q->bufs[i]->i = i;
q->bufs[i]->memory = memory;
q->bufs[i]->bsize = bsize;
switch (memory) {
case V4L2_MEMORY_MMAP:
q->bufs[i]->boff = bsize * i;
break;
case V4L2_MEMORY_USERPTR:
case V4L2_MEMORY_OVERLAY:
/* nothing */
break;
};
}
dprintk(1,"mmap setup: %d buffers, %d bytes each\n",
bcount,bsize);
return 0;
}
int videobuf_mmap_free(struct file *file, struct videobuf_queue *q)
{
int i;
for (i = 0; i < VIDEO_MAX_FRAME; i++)
if (q->bufs[i] && q->bufs[i]->map)
return -EBUSY;
for (i = 0; i < VIDEO_MAX_FRAME; i++) {
if (NULL == q->bufs[i])
continue;
q->ops->buf_release(file,q->bufs[i]);
kfree(q->bufs[i]);
q->bufs[i] = NULL;
}
return 0;
}
int videobuf_mmap_mapper(struct vm_area_struct *vma,
struct videobuf_queue *q)
{
struct videobuf_mapping *map;
unsigned int first,last,size,i;
int retval;
down(&q->lock);
retval = -EINVAL;
if (!(vma->vm_flags & VM_WRITE)) {
dprintk(1,"mmap app bug: PROT_WRITE please\n");
goto done;
}
if (!(vma->vm_flags & VM_SHARED)) {
dprintk(1,"mmap app bug: MAP_SHARED please\n");
goto done;
}
/* look for first buffer to map */
for (first = 0; first < VIDEO_MAX_FRAME; first++) {
if (NULL == q->bufs[first])
continue;
if (V4L2_MEMORY_MMAP != q->bufs[first]->memory)
continue;
if (q->bufs[first]->boff == (vma->vm_pgoff << PAGE_SHIFT))
break;
}
if (VIDEO_MAX_FRAME == first) {
dprintk(1,"mmap app bug: offset invalid [offset=0x%lx]\n",
(vma->vm_pgoff << PAGE_SHIFT));
goto done;
}
/* look for last buffer to map */
for (size = 0, last = first; last < VIDEO_MAX_FRAME; last++) {
if (NULL == q->bufs[last])
continue;
if (V4L2_MEMORY_MMAP != q->bufs[last]->memory)
continue;
if (q->bufs[last]->map) {
retval = -EBUSY;
goto done;
}
size += q->bufs[last]->bsize;
if (size == (vma->vm_end - vma->vm_start))
break;
}
if (VIDEO_MAX_FRAME == last) {
dprintk(1,"mmap app bug: size invalid [size=0x%lx]\n",
(vma->vm_end - vma->vm_start));
goto done;
}
/* create mapping + update buffer list */
retval = -ENOMEM;
map = kmalloc(sizeof(struct videobuf_mapping),GFP_KERNEL);
if (NULL == map)
goto done;
for (size = 0, i = first; i <= last; size += q->bufs[i++]->bsize) {
q->bufs[i]->map = map;
q->bufs[i]->baddr = vma->vm_start + size;
}
map->count = 1;
map->start = vma->vm_start;
map->end = vma->vm_end;
map->q = q;
vma->vm_ops = &videobuf_vm_ops;
vma->vm_flags |= VM_DONTEXPAND;
vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */
vma->vm_private_data = map;
dprintk(1,"mmap %p: %08lx-%08lx pgoff %08lx bufs %d-%d\n",
map,vma->vm_start,vma->vm_end,vma->vm_pgoff,first,last);
retval = 0;
done:
up(&q->lock);
return retval;
}
/* --------------------------------------------------------------------- */
EXPORT_SYMBOL_GPL(videobuf_vmalloc_to_sg);
EXPORT_SYMBOL_GPL(videobuf_lock);
EXPORT_SYMBOL_GPL(videobuf_unlock);
EXPORT_SYMBOL_GPL(videobuf_dma_init_user);
EXPORT_SYMBOL_GPL(videobuf_dma_init_kernel);
EXPORT_SYMBOL_GPL(videobuf_dma_init_overlay);
EXPORT_SYMBOL_GPL(videobuf_dma_pci_map);
EXPORT_SYMBOL_GPL(videobuf_dma_pci_sync);
EXPORT_SYMBOL_GPL(videobuf_dma_pci_unmap);
EXPORT_SYMBOL_GPL(videobuf_dma_free);
EXPORT_SYMBOL_GPL(videobuf_alloc);
EXPORT_SYMBOL_GPL(videobuf_waiton);
EXPORT_SYMBOL_GPL(videobuf_iolock);
EXPORT_SYMBOL_GPL(videobuf_queue_init);
EXPORT_SYMBOL_GPL(videobuf_queue_cancel);
EXPORT_SYMBOL_GPL(videobuf_queue_is_busy);
EXPORT_SYMBOL_GPL(videobuf_next_field);
EXPORT_SYMBOL_GPL(videobuf_status);
EXPORT_SYMBOL_GPL(videobuf_reqbufs);
EXPORT_SYMBOL_GPL(videobuf_querybuf);
EXPORT_SYMBOL_GPL(videobuf_qbuf);
EXPORT_SYMBOL_GPL(videobuf_dqbuf);
EXPORT_SYMBOL_GPL(videobuf_streamon);
EXPORT_SYMBOL_GPL(videobuf_streamoff);
EXPORT_SYMBOL_GPL(videobuf_read_start);
EXPORT_SYMBOL_GPL(videobuf_read_stop);
EXPORT_SYMBOL_GPL(videobuf_read_stream);
EXPORT_SYMBOL_GPL(videobuf_read_one);
EXPORT_SYMBOL_GPL(videobuf_poll_stream);
EXPORT_SYMBOL_GPL(videobuf_mmap_setup);
EXPORT_SYMBOL_GPL(videobuf_mmap_free);
EXPORT_SYMBOL_GPL(videobuf_mmap_mapper);
/*
* Local variables:
* c-basic-offset: 8
* End:
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -