Linux v4l2架构学习总链接
step 5 : 设置缓存
映射
mmap一般都是配合VIDIOC_QUERYBUF使用
for (n_buffers = 0; n_buffers < req.count; ++n_buffers)
{
struct v4l2_buffer buf;
CLEAR (buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = n_buffers;
if (-1 == ioctl (fd, VIDIOC_QUERYBUF, &buf))
printf ("VIDIOC_QUERYBUF error\n");
buffers[n_buffers].length = buf.length;
buffers[n_buffers].start =
mmap (NULL /* start anywhere */,
buf.length,
PROT_READ | PROT_WRITE /* required */,
MAP_SHARED /* recommended */,
fd, buf.m.offset);
if (MAP_FAILED == buffers[n_buffers].start)
printf ("mmap failed\n");
}
在这里又一次看到了offset的身影,这一次可以搞定它。
mmap对应驱动的 videox的fops也就是 v4l2_fops的mmap
static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
{
struct video_device *vdev = video_devdata(filp);
int ret = -ENODEV;
if (!vdev->fops->mmap)
return -ENODEV;
if (video_is_registered(vdev))
ret = vdev->fops->mmap(filp, vm);
if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP)
dprintk("%s: mmap (%d)\n",
video_device_node_name(vdev), ret);
return ret;
}
vdev->fops对应
vivi_fops.mmap 也就是vivi_mmap
static int vivi_mmap(struct file *file, struct vm_area_struct *vma)
{
struct vivi_dev *dev = video_drvdata(file);
int ret;
ret = vb2_mmap(&dev->vb_vidq, vma);
return ret;
}
分析vb2_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;
/*
* 对于下面的判断条件不深入分析,主要不是很了解那些标志位
*/
if (q->memory != VB2_MEMORY_MMAP) {
dprintk(1, "queue is not currently set up for mmap\n");
return -EINVAL;
}
/*
* Check memory area access mode.
*/
if (!(vma->vm_flags & VM_SHARED)) {
dprintk(1, "invalid vma flags, VM_SHARED needed\n");
return -EINVAL;
}
if (q->is_output) {
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;
}
}
mutex_lock(&q->mmap_lock);
/*
* 暂且认为非active,也就是q->fileio = 0
*/
if (vb2_fileio_is_active(q)) {
dprintk(1, "mmap: file io in progress\n");
ret = -EBUSY;
goto unlock;
}
/*
* off就是offset的值
* __find_plane_by_offset
* 这个函数非常简单,核心思想就是通过offset的值找到对应的buffer
* 单独分析见下面
*/
/*
* Find the plane corresponding to the offset passed by userspace.
*/
ret = __find_plane_by_offset(q, off, &buffer, &plane);
if (ret)
goto unlock;
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.
*/
/*
* mmap需要页对齐,所以这里将length进行一个页对齐的操作
* 同时计算申请的范围是否有效
*/
length = PAGE_ALIGN(vb->planes[plane].length);
if (length < (vma->vm_end - vma->vm_start)) {
dprintk(1,
"MMAP invalid, as it would overflow buffer length\n");
ret = -EINVAL;
goto unlock;
}
/*
* 对应vb->vb2_queue->mem_ops->mmap
* 也就是vb2_vmalloc_memops的mmap
* vb2_vmalloc_mmap 这里就是映射相关的了,不再追踪,offset的作用已经知道了
* 最重要的是mmap我暂时也搞不明白
*/
ret = call_memop(vb, mmap, vb->planes[plane].mem_priv, vma);
unlock:
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
static 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->planes[plane].m.offset == off) {
*_buffer = buffer;
*_plane = plane;
return 0;
}
}
}
return -EINVAL;
}
其实很明显的可以看出来,就是通过offset找到对应的buffer及plane的值
对应的应用程序可以这么写
struct buffer {
void * start;
size_t length;
};
struct buffer *buffers = NULL;
buffers = calloc (req.count, sizeof (*buffers));
for(i = 0; i < req.count; i++) {
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if (-1 == ioctl (fd, VIDIOC_QUERYBUF, &buf)) {
printf("Querybuf fail\n");
goto err;
}
printf("buffer[%d]: length = %d, offset = %d\n", i, buf.length, buf.m.offset);
buffers[i].length = buf.length;
buffers[i].start =
mmap (NULL /* start anywhere */,
buf.length,
PROT_READ | PROT_WRITE /* required */,
MAP_SHARED /* recommended */,
fd, buf.m.offset);
if (MAP_FAILED == buffers[i].start)
printf ("mmap failed\n");
}
buffers用于记录mmap后的地址