📄 videobuf-dma-sg.c
字号:
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)static struct page*videobuf_vm_nopage(struct vm_area_struct *vma, unsigned long vaddr, int *type){ 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 | __GFP_DMA32); if (!page) return NOPAGE_OOM; clear_user_page(page_address(page), vaddr, page); if (type) *type = VM_FAULT_MINOR; return page;}#elsestatic intvideobuf_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf){ struct page *page; dprintk(3,"fault: fault @ %08lx [vma %08lx-%08lx]\n", (unsigned long)vmf->virtual_address,vma->vm_start,vma->vm_end); page = alloc_page(GFP_USER | __GFP_DMA32); if (!page) return VM_FAULT_OOM; clear_user_page(page_address(page), (unsigned long)vmf->virtual_address, page); vmf->page = page; return 0;}#endifstatic struct vm_operations_struct videobuf_vm_ops ={ .open = videobuf_vm_open, .close = videobuf_vm_close,#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) .nopage = videobuf_vm_nopage,#else .fault = videobuf_vm_fault,#endif};/* --------------------------------------------------------------------- * SG handlers for the generic methods *//* Allocated area consists on 3 parts: struct video_buffer struct <driver>_buffer (cx88_buffer, saa7134_buf, ...) struct videobuf_dma_sg_memory */static void *__videobuf_alloc(size_t size){ struct videobuf_dma_sg_memory *mem; struct videobuf_buffer *vb; vb = kzalloc(size+sizeof(*mem),GFP_KERNEL); mem = vb->priv = ((char *)vb)+size; mem->magic=MAGIC_SG_MEM; videobuf_dma_init(&mem->dma); dprintk(1,"%s: allocated at %p(%ld+%ld) & %p(%ld)\n", __func__,vb,(long)sizeof(*vb),(long)size-sizeof(*vb), mem,(long)sizeof(*mem)); return vb;}static void *__videobuf_to_vmalloc (struct videobuf_buffer *buf){ struct videobuf_dma_sg_memory *mem = buf->priv; BUG_ON(!mem); MAGIC_CHECK(mem->magic, MAGIC_SG_MEM); return mem->dma.vmalloc;}static int __videobuf_iolock (struct videobuf_queue* q, struct videobuf_buffer *vb, struct v4l2_framebuffer *fbuf){ int err,pages; dma_addr_t bus; struct videobuf_dma_sg_memory *mem = vb->priv; BUG_ON(!mem); MAGIC_CHECK(mem->magic, MAGIC_SG_MEM); switch (vb->memory) { case V4L2_MEMORY_MMAP: case V4L2_MEMORY_USERPTR: if (0 == vb->baddr) { /* no userspace addr -- kernel bounce buffer */ pages = PAGE_ALIGN(vb->size) >> PAGE_SHIFT; err = videobuf_dma_init_kernel( &mem->dma, DMA_FROM_DEVICE, pages ); if (0 != err) return err; } else if (vb->memory == V4L2_MEMORY_USERPTR) { /* dma directly to userspace */ err = videobuf_dma_init_user( &mem->dma, DMA_FROM_DEVICE, vb->baddr,vb->bsize ); if (0 != err) return err; } else { /* NOTE: HACK: videobuf_iolock on V4L2_MEMORY_MMAP buffers can only be called from videobuf_qbuf we take current->mm->mmap_sem there, to prevent locking inversion, so don't take it here */ err = videobuf_dma_init_user_locked(&mem->dma, DMA_FROM_DEVICE, vb->baddr, vb->bsize); if (0 != err) return err; } break; case V4L2_MEMORY_OVERLAY: if (NULL == fbuf) return -EINVAL; /* FIXME: need sanity checks for vb->boff */ /* * Using a double cast to avoid compiler warnings when * building for PAE. Compiler doesn't like direct casting * of a 32 bit ptr to 64 bit integer. */ bus = (dma_addr_t)(unsigned long)fbuf->base + vb->boff; pages = PAGE_ALIGN(vb->size) >> PAGE_SHIFT; err = videobuf_dma_init_overlay(&mem->dma, DMA_FROM_DEVICE, bus, pages); if (0 != err) return err; break; default: BUG(); } err = videobuf_dma_map(q, &mem->dma); if (0 != err) return err; return 0;}static int __videobuf_sync(struct videobuf_queue *q, struct videobuf_buffer *buf){ struct videobuf_dma_sg_memory *mem = buf->priv; BUG_ON(!mem); MAGIC_CHECK(mem->magic,MAGIC_SG_MEM); return videobuf_dma_sync(q,&mem->dma);}static int __videobuf_mmap_free(struct videobuf_queue *q){ int i; for (i = 0; i < VIDEO_MAX_FRAME; i++) { if (q->bufs[i]) { if (q->bufs[i]->map) return -EBUSY; } } return 0;}static int __videobuf_mmap_mapper(struct videobuf_queue *q, struct vm_area_struct *vma){ struct videobuf_dma_sg_memory *mem; struct videobuf_mapping *map; unsigned int first,last,size,i; int retval; 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; } /* This function maintains backwards compatibility with V4L1 and will * map more than one buffer if the vma length is equal to the combined * size of multiple buffers than it will map them together. See * VIDIOCGMBUF in the v4l spec * * TODO: Allow drivers to specify if they support this mode */ /* look for first buffer to map */ for (first = 0; first < VIDEO_MAX_FRAME; first++) { if (NULL == q->bufs[first]) continue; mem=q->bufs[first]->priv; BUG_ON(!mem); MAGIC_CHECK(mem->magic,MAGIC_SG_MEM); 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; size = 0; for (i = first; i <= last; i++) { if (NULL == q->bufs[i]) continue; q->bufs[i]->map = map; q->bufs[i]->baddr = vma->vm_start + size; size += q->bufs[i]->bsize; } 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 | VM_RESERVED; vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */ vma->vm_private_data = map; dprintk(1,"mmap %p: q=%p %08lx-%08lx pgoff %08lx bufs %d-%d\n", map,q,vma->vm_start,vma->vm_end,vma->vm_pgoff,first,last); retval = 0; done: return retval;}static int __videobuf_copy_to_user ( struct videobuf_queue *q, char __user *data, size_t count, int nonblocking ){ struct videobuf_dma_sg_memory *mem = q->read_buf->priv; BUG_ON(!mem); MAGIC_CHECK(mem->magic,MAGIC_SG_MEM); /* copy to userspace */ if (count > q->read_buf->size - q->read_off) count = q->read_buf->size - q->read_off; if (copy_to_user(data, mem->dma.vmalloc+q->read_off, count)) return -EFAULT; return count;}static int __videobuf_copy_stream ( struct videobuf_queue *q, char __user *data, size_t count, size_t pos, int vbihack, int nonblocking ){ unsigned int *fc; struct videobuf_dma_sg_memory *mem = q->read_buf->priv; BUG_ON(!mem); MAGIC_CHECK(mem->magic,MAGIC_SG_MEM); 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*)mem->dma.vmalloc; fc += (q->read_buf->size>>2) -1; *fc = q->read_buf->field_count >> 1; dprintk(1,"vbihack: %d\n",*fc); } /* copy stuff using the common method */ count = __videobuf_copy_to_user (q,data,count,nonblocking); if ( (count==-EFAULT) && (0 == pos) ) return -EFAULT; return count;}static struct videobuf_qtype_ops sg_ops = { .magic = MAGIC_QTYPE_OPS, .alloc = __videobuf_alloc, .iolock = __videobuf_iolock, .sync = __videobuf_sync, .mmap_free = __videobuf_mmap_free, .mmap_mapper = __videobuf_mmap_mapper, .video_copy_to_user = __videobuf_copy_to_user, .copy_stream = __videobuf_copy_stream, .vmalloc = __videobuf_to_vmalloc,};void *videobuf_sg_alloc(size_t size){ struct videobuf_queue q; /* Required to make generic handler to call __videobuf_alloc */ q.int_ops = &sg_ops; q.msize = size; return videobuf_alloc(&q);}void videobuf_queue_sg_init(struct videobuf_queue* q, struct videobuf_queue_ops *ops, struct device *dev, spinlock_t *irqlock, enum v4l2_buf_type type, enum v4l2_field field, unsigned int msize, void *priv){ videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize, priv, &sg_ops);}/* --------------------------------------------------------------------- */EXPORT_SYMBOL_GPL(videobuf_vmalloc_to_sg);EXPORT_SYMBOL_GPL(videobuf_to_dma);EXPORT_SYMBOL_GPL(videobuf_dma_init);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_map);EXPORT_SYMBOL_GPL(videobuf_dma_sync);EXPORT_SYMBOL_GPL(videobuf_dma_unmap);EXPORT_SYMBOL_GPL(videobuf_dma_free);EXPORT_SYMBOL_GPL(videobuf_sg_dma_map);EXPORT_SYMBOL_GPL(videobuf_sg_dma_unmap);EXPORT_SYMBOL_GPL(videobuf_sg_alloc);EXPORT_SYMBOL_GPL(videobuf_queue_sg_init);/* * Local variables: * c-basic-offset: 8 * End: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -