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");
req.count = i;
goto unmmap;
}
}
static long __video_do_ioctl(struct file *file,
unsigned int cmd, void *arg)
{
... ...
const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
... ...
case VIDIOC_QUERYBUF:
{
struct v4l2_buffer *p = arg;
if (!ops->vidioc_querybuf)
break;
ret = check_fmt(ops, p->type);//先判断是否支持是支持的type
if (ret)
break;
//下面调用video_device.ioctl_ops的vidioc_querybuf函数
//主要是根据上层传下来的buf.index,找到之前分配好的空间,并把
//空间地址通过offset传给上层
ret = ops->vidioc_querybuf(file, fh, p);
if (!ret)
dbgbuf(cmd, vfd, p);
break;
}
... ...
}
下面具体看一下vidioc_querybuf函数
static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
struct vivi_dev *dev = video_drvdata(file);
return vb2_querybuf(&dev->vb_vidq, p);
}
int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b)
{
struct vb2_buffer *vb;
//类型判断,不满足返回
if (b->type != q->type) {
dprintk(1, "querybuf: wrong buffer type\n");
return -EINVAL;
}
//index是否超过了之前申请的buffer最大数量,如超过返回
if (b->index >= q->num_buffers) {
dprintk(1, "querybuf: buffer index out of range\n");
return -EINVAL;
}
//通过buf.index获取到对应vb2_buffer
vb = q->bufs[b->index];
return __fill_v4l2_buffer(vb, b);
}
__fill_v4l2_buffer又做了些什么
static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b)
{
struct vb2_queue *q = vb->vb2_queue;
int ret = 0;
/* Copy back data such as timestamp, flags, input, etc. */
//如上英文注释拷贝时间戳,标志等等
memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m));
b->input = vb->v4l2_buf.input;
b->reserved = vb->v4l2_buf.reserved;
//判断是否是多个planar,vivi这里不是所以跳到else
if (V4L2_TYPE_IS_MULTIPLANAR(q->type)) {
ret = __verify_planes_array(vb, b);
if (ret)
return ret;
/*
* Fill in plane-related data if userspace provided an array
* for it. The memory and size is verified above.
*/
memcpy(b->m.planes, vb->v4l2_planes,
b->length * sizeof(struct v4l2_plane));
} else {
/*
* We use length and offset in v4l2_planes array even for
* single-planar buffers, but userspace does not.
*/
//由于是单plane,所以这里直接使用v4l2_planes[0]
//这里获取到了length
b->length = vb->v4l2_planes[0].length;
b->bytesused = vb->v4l2_planes[0].bytesused;
//下面这里就获取到了m.offset
if (q->memory == V4L2_MEMORY_MMAP)
b->m.offset = vb->v4l2_planes[0].m.mem_offset;
else if (q->memory == V4L2_MEMORY_USERPTR)
b->m.userptr = vb->v4l2_planes[0].m.userptr;
}
/*
* Clear any buffer state related flags.
*/
b->flags &= ~V4L2_BUFFER_STATE_FLAGS;
//参考上一篇,申请完内存空间vb->state=VB2_BUF_STATE_DEQUEUED
switch (vb->state) {
case VB2_BUF_STATE_QUEUED:
case VB2_BUF_STATE_ACTIVE:
b->flags |= V4L2_BUF_FLAG_QUEUED;
break;
case VB2_BUF_STATE_ERROR:
b->flags |= V4L2_BUF_FLAG_ERROR;
/* fall through */
case VB2_BUF_STATE_DONE:
b->flags |= V4L2_BUF_FLAG_DONE;
break;
case VB2_BUF_STATE_DEQUEUED:
/* nothing */
break;
}
//这里暂时不知道要干啥,先放着
if (vb->num_planes_mapped == vb->num_planes)
b->flags |= V4L2_BUF_FLAG_MAPPED;
return ret;
}
mmap系统调用最后会进入kernel,反正有一番处理最后应该会到/dev/videoX对应file_operationsd的mmap函数
static const struct file_operations v4l2_fops = {
.owner = THIS_MODULE,
.read = v4l2_read,
.write = v4l2_write,
.open = v4l2_open,
.get_unmapped_area = v4l2_get_unmapped_area,
.mmap = v4l2_mmap,
.unlocked_ioctl = v4l2_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l2_compat_ioctl32,
#endif
.release = v4l2_release,
.poll = v4l2_poll,
.llseek = no_llseek,
};
v4l2_mmap主要判断video_device的fops->mmap是否定义,如定义则继续调用
static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
{
struct video_device *vdev = video_devdata(filp);
int ret = -ENODEV;
//对应vivi驱动是定义了。
if (!vdev->fops->mmap)
return ret;
if (vdev->lock && mutex_lock_interruptible(vdev->lock))
return -ERESTARTSYS;
//直接调用vivi.fops的mmap
if (video_is_registered(vdev))
ret = vdev->fops->mmap(filp, vm);
if (vdev->lock)
mutex_unlock(vdev->lock);
return ret;
}
最后调用到vivi_mmap
static const struct v4l2_file_operations vivi_fops = {
.owner = THIS_MODULE,
.open = v4l2_fh_open,
.release = vivi_close,
.read = vivi_read,
.poll = vivi_poll,
.unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
.mmap = vivi_mmap,
};
static int vivi_mmap(struct file *file, struct vm_area_struct *vma)
{
struct vivi_dev *dev = video_drvdata(file);
int ret;
dprintk(dev, 1, "mmap called, vma=0x%08lx\n", (unsigned long)vma);
//vb2_mmap应该就是根据传进去的offset,最后获取到虚拟映射地址和大小
ret = vb2_mmap(&dev->vb_vidq, vma);
dprintk(dev, 1, "vma start=0x%08lx, size=%ld, ret=%d\n",
(unsigned long)vma->vm_start,
(unsigned long)vma->vm_end - (unsigned long)vma->vm_start,
ret);
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_plane *vb_plane;
struct vb2_buffer *vb;
unsigned int buffer, plane;
int ret;
if (q->memory != V4L2_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 (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;
}
}
/*
* Find the plane corresponding to the offset passed by userspace.
*/
//通过offset找到所对应的vb2_buffer对应的index和vb2_plane index
ret = __find_plane_by_offset(q, off, &buffer, &plane);
if (ret)
return ret;
vb = q->bufs[buffer];
vb_plane = &vb->planes[plane];
//第一个参数就是之前申请的内存地址
ret = q->mem_ops->mmap(vb_plane->mem_priv, vma);
if (ret)
return ret;
vb_plane->mapped = 1;
vb->num_planes_mapped++;
dprintk(3, "Buffer %d, plane %d successfully mapped\n", buffer, plane);
return 0;
}
EXPORT_SYMBOL_GPL(vb2_mmap);
接着看一下vb2_mem_ops.mmap干了什么
const struct vb2_mem_ops vb2_vmalloc_memops = {
.alloc = vb2_vmalloc_alloc,
.put = vb2_vmalloc_put,
.vaddr = vb2_vmalloc_vaddr,
.mmap = vb2_vmalloc_mmap,
.num_users = vb2_vmalloc_num_users,
};
EXPORT_SYMBOL_GPL(vb2_vmalloc_memops);
static int vb2_vmalloc_mmap(void *buf_priv, struct vm_area_struct *vma)
{
struct vb2_vmalloc_buf *buf = buf_priv;
int ret;
if (!buf) {
printk(KERN_ERR "No memory to map\n");
return -EINVAL;
}
//根据buf->vaddr转化成对应的vma->vma_start和vma->vma_end
ret = remap_vmalloc_range(vma, buf->vaddr, 0);
if (ret) {
printk(KERN_ERR "Remapping vmalloc memory, error: %d\n", ret);
return ret;
}
/*
* Make sure that vm_areas for 2 buffers won't be merged together
*/
vma->vm_flags |= VM_DONTEXPAND;
/*
* Use common vm_area operations to track buffer refcount.
*/
vma->vm_private_data = &buf->handler;
vma->vm_ops = &vb2_common_vm_ops;
//这个open主要做就是增加引用计数
vma->vm_ops->open(vma);
return 0;
}