1.mmap应用层编程
mmap一般搭配VIDIOC_QUERYBUF使用
/* 将内核态内存映射到用户态 */
for(i=0; i<req_buf.count; i++)
{
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
ret = ioctl(pdevice->fd, VIDIOC_QUERYBUF, &buf);
if(ret < 0)
{
perror("ioctl call \'VIDIOC_QUERYBUF\' failed");
return -1;
}
pdevice->mmap_buf[i].size = buf.length;
//mmap一般搭配VIDIOC_QUERYBUF使用,pdevice->mmap_buf[i].addr用来记录mmap之后的地址
pdevice->mmap_buf[i].addr = (char *)mmap(NULL, buf.length, PROT_READ|PROT_WRITE, MAP_SHARED, pdevice->fd, buf.m.offset);
if(MAP_FAILED == pdevice->mmap_buf[i].addr)
{
perror("mmap failed");
return -1;
}
2.驱动层mmap函数
mmap对应驱动中v4l2_file_operations即xvip_dma_fops->mmap(vb2_fop_mmap)
int vb2_fop_mmap(struct file *file, struct vm_area_struct *vma)
{
struct video_device *vdev = video_devdata(file);
return vb2_mmap(vdev->queue, vma);
}
vb_mmap
int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma)
{
unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
struct vb2_buffer *vb;
unsigned int buffer = 0, plane = 0;
int ret;
unsigned long length;
//check q->memory的类型
if (q->memory != V4L2_MEMORY_MMAP) {
dprintk(1, "queue is not currently set up for mmap\n");
return -EINVAL;
}
/*
* Check memory area access mode.
*/
//如果vma的flags不是多个进程共享,退出
if (!(vma->vm_flags & VM_SHARED)) {
dprintk(1, "invalid vma flags, VM_SHARED needed\n");
return -EINVAL;
}
//判断q->type是否为output,不是,不走这个case
if (V4L2_TYPE_IS_OUTPUT(q->type)) {
if (!(vma->vm_flags & VM_WRITE)) {
dprintk(1, "invalid vma flags, VM_WRITE needed\n");
return -EINVAL;
}
} else {
//走这个分支,说明页可读
if (!(vma->vm_flags & VM_READ)) {
dprintk(1, "invalid vma flags, VM_READ needed\n");
return -EINVAL;
}
}
// 返回q->fileio,这里 q->fileio为0
if (vb2_fileio_is_active(q)) {
dprintk(1, "mmap: file io in progress\n");
return -EBUSY;
}
/*
* Find the plane corresponding to the offset passed by userspace.
*/
/*
* off就是offset的值,off由应用层传递下来
* __find_plane_by_offset
* 通过offset的值找到对应的buffer以及plane的值
*/
ret = __find_plane_by_offset(q, off, &buffer, &plane);
if (ret)
return ret;
vb = q->bufs[buffer];
/*
* MMAP requires page_aligned buffers.
* The buffer length was page_aligned at __vb2_buf_mem_alloc(),
* so, we need to do the same here.
*/
//页对齐,并且判断对齐的范围是否有效
length = PAGE_ALIGN(vb->v4l2_planes[plane].length);
if (length < (vma->vm_end - vma->vm_start)) {
dprintk(1,
"MMAP invalid, as it would overflow buffer length\n");
return -EINVAL;
}
mutex_lock(&q->mmap_lock);
//调用mmap函数
ret = call_memop(vb, mmap, vb->planes[plane].mem_priv, vma);
mutex_unlock(&q->mmap_lock);
if (ret)
return ret;
dprintk(3, "buffer %d, plane %d successfully mapped\n", buffer, plane);
return 0;
}
__find_plane_by_offset通过offset的值找到对应的buffer以及plane的值,这里只用到一个plane
tatic int __find_plane_by_offset(struct vb2_queue *q, unsigned long off,
unsigned int *_buffer, unsigned int *_plane)
{
struct vb2_buffer *vb;
unsigned int buffer, plane;
/*
* Go over all buffers and their planes, comparing the given offset
* with an offset assigned to each plane. If a match is found,
* return its buffer and plane numbers.
*/
for (buffer = 0; buffer < q->num_buffers; ++buffer) {
vb = q->bufs[buffer];
for (plane = 0; plane < vb->num_planes; ++plane) {
if (vb->v4l2_planes[plane].m.mem_offset == off) {
*_buffer = buffer;
*_plane = plane;
return 0;
}
}
}
return -EINVAL;
}
call_memop(vb, mmap, vb->planes[plane].mem_priv, vma)调用的是vb2_dma_contig_memops->.mmap(vb2_dc_mmap),vb2_dc_mmap用来将DMA申请的内存映射到用户空间连续的虚拟地址上
static int vb2_dc_mmap(void *buf_priv, struct vm_area_struct *vma)
{
struct vb2_dc_buf *buf = buf_priv;
int ret;
if (!buf) {
printk(KERN_ERR "No buffer to map\n");
return -EINVAL;
}
/*
* dma_mmap_* uses vm_pgoff as in-buffer offset, but we want to
* map whole buffer
*/
vma->vm_pgoff = 0;
/*将申请到的DMA内存映射到用户空间的连续的虚拟地址上*/
ret = dma_mmap_coherent(buf->dev, vma, buf->vaddr,
buf->dma_addr, buf->size);
if (ret) {
pr_err("Remapping memory failed, error: %d\n", ret);
return ret;
}
vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
vma->vm_private_data = &buf->handler;
vma->vm_ops = &vb2_common_vm_ops;
//调用vb2_common_vm_ops->vb2_common_vm_open增加vb2_vmarea_handler
vma->vm_ops->open(vma);
pr_debug("%s: mapped dma addr 0x%08lx at 0x%08lx, size %ld\n",
__func__, (unsigned long)buf->dma_addr, vma->vm_start,
buf->size);
return 0;
}
vma->vm_ops->open(vma);
static void vb2_common_vm_open(struct vm_area_struct *vma)
{
struct vb2_vmarea_handler *h = vma->vm_private_data;
pr_debug("%s: %p, refcount: %d, vma: %08lx-%08lx\n",
__func__, h, atomic_read(h->refcount), vma->vm_start,
vma->vm_end);
/*增加vb2_vmarea_handler引用*/
atomic_inc(h->refcount);
}