只需设置type,memory, buffer的index就可以了。
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 (ioctl (fd, VIDIOC_QBUF, &buf) < 0)
printf ("VIDIOC_QBUF failed\n");
printf("buf[%d].flags = 0x%08x\n", i, buf.flags);
}
上层调用下来,和之前一样进入__video_do_ioctl函数
static long __video_do_ioctl(struct file *file,
unsigned int cmd, void *arg)
{
struct video_device *vfd = video_devdata(file);
const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
void *fh = file->private_data;
struct v4l2_fh *vfh = NULL;
struct v4l2_format f_copy;
int use_fh_prio = 0;
long ret = -EINVAL;
... ...
case VIDIOC_QBUF:
{
struct v4l2_buffer *p = arg;
if (!ops->vidioc_qbuf)
break;
//判断一下type类型是否支持
ret = check_fmt(ops, p->type);
if (ret)
break;
//直接调用vidioc_qbuf
ret = ops->vidioc_qbuf(file, fh, p);
if (!ret)
dbgbuf(cmd, vfd, p);
break;
}
... ...
}
下面看看vivi驱动里面vidioc_qbuf
static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
struct vivi_dev *dev = video_drvdata(file);
//直接调用vb2_qbuf
return vb2_qbuf(&dev->vb_vidq, p);
}
int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
{
struct vb2_buffer *vb;
int ret = 0;
//下面是一些条件判断,这里直接掉过
if (q->fileio) {
dprintk(1, "qbuf: file io in progress\n");
return -EBUSY;
}
if (b->type != q->type) {
dprintk(1, "qbuf: invalid buffer type\n");
return -EINVAL;
}
if (b->index >= q->num_buffers) {
dprintk(1, "qbuf: buffer index out of range\n");
return -EINVAL;
}
//根据index获取到vb2_buffer
vb = q->bufs[b->index];
if (NULL == vb) {
/* Should never happen */
dprintk(1, "qbuf: buffer is NULL\n");
return -EINVAL;
}
if (b->memory != q->memory) {
dprintk(1, "qbuf: invalid memory type\n");
return -EINVAL;
}
if (vb->state != VB2_BUF_STATE_DEQUEUED) {
dprintk(1, "qbuf: buffer already in use\n");
return -EINVAL;
}
//这里是V4L2_MEMORY_MMAP类型,所走下面分支
//__qbuf_mmap主要是将b->field, b->timestap, b->input, b->flags赋值给
//vb->v4l2_buf
if (q->memory == V4L2_MEMORY_MMAP)
ret = __qbuf_mmap(vb, b);
else if (q->memory == V4L2_MEMORY_USERPTR)
ret = __qbuf_userptr(vb, b);
else {
WARN(1, "Invalid queue type\n");
return -EINVAL;
}
if (ret)
return ret;
//buf_prepare主要设置vb2_buffer->v4l2_planes[0].bytesused
//vivi_buffer.fmt=vivi_dev.fmt
//准备vivi输出数据precalculate_bars, precalculate_line
ret = call_qop(q, buf_prepare, vb);
if (ret) {
dprintk(1, "qbuf: buffer preparation failed\n");
return ret;
}
/*
* Add to the queued buffers list, a buffer will stay on it until
* dequeued in dqbuf.
*/
//将vb2_buffer->quued_entry加入到vb2_queue->queued_list
//并将vb2_buffer state改成VB2_BUF_STATE_QUEUED
list_add_tail(&vb->queued_entry, &q->queued_list);
vb->state = VB2_BUF_STATE_QUEUED;
/*
* If already streaming, give the buffer to driver for processing.
* If not, the buffer will be given to driver on next streamon.
*/
if (q->streaming)
__enqueue_in_driver(vb);
dprintk(1, "qbuf of buffer %d succeeded\n", vb->v4l2_buf.index);
return 0;
}
EXPORT_SYMBOL_GPL(vb2_qbuf);
_qbuf_mmap
static int __qbuf_mmap(struct vb2_buffer *vb, struct v4l2_buffer *b)
{
return __fill_vb2_buffer(vb, b, vb->v4l2_planes);
}
static int __fill_vb2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b,
struct v4l2_plane *v4l2_planes)
{
unsigned int plane;
int ret;
//不满足条件直接进else
if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
/*
* Verify that the userspace gave us a valid array for
* plane information.
*/
ret = __verify_planes_array(vb, b);
if (ret)
return ret;
/* Fill in driver-provided information for OUTPUT types */
if (V4L2_TYPE_IS_OUTPUT(b->type)) {
/*
* Will have to go up to b->length when API starts
* accepting variable number of planes.
*/
for (plane = 0; plane < vb->num_planes; ++plane) {
v4l2_planes[plane].bytesused =
b->m.planes[plane].bytesused;
v4l2_planes[plane].data_offset =
b->m.planes[plane].data_offset;
}
}
if (b->memory == V4L2_MEMORY_USERPTR) {
for (plane = 0; plane < vb->num_planes; ++plane) {
v4l2_planes[plane].m.userptr =
b->m.planes[plane].m.userptr;
v4l2_planes[plane].length =
b->m.planes[plane].length;
}
}
} else {
/*
* Single-planar buffers do not use planes array,
* so fill in relevant v4l2_buffer struct fields instead.
* In videobuf we use our internal V4l2_planes struct for
* single-planar buffers as well, for simplicity.
*/
//不是OUTPUT类型跳过
if (V4L2_TYPE_IS_OUTPUT(b->type))
v4l2_planes[0].bytesused = b->bytesused;
if (b->memory == V4L2_MEMORY_USERPTR) {
v4l2_planes[0].m.userptr = b->m.userptr;
v4l2_planes[0].length = b->length;
}
}
//将vb2_buffer的v4l2_buf赋值
vb->v4l2_buf.field = b->field;
vb->v4l2_buf.timestamp = b->timestamp;
vb->v4l2_buf.input = b->input;
vb->v4l2_buf.flags = b->flags & ~V4L2_BUFFER_STATE_FLAGS;
return 0;
}
再看一下buffer_prepare
static int buffer_prepare(struct vb2_buffer *vb)
{
struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb);
unsigned long size;
dprintk(dev, 1, "%s, field=%d\n", __func__, vb->v4l2_buf.field);
BUG_ON(NULL == dev->fmt);
/*
* Theses properties only change when queue is idle, see s_fmt.
* The below checks should not be performed here, on each
* buffer_prepare (i.e. on each qbuf). Most of the code in this function
* should thus be moved to buffer_init and s_fmt.
*/
if (dev->width < 48 || dev->width > MAX_WIDTH ||
dev->height < 32 || dev->height > MAX_HEIGHT)
return -EINVAL;
//计算一帧数据的大小
size = dev->width * dev->height * 2;
if (vb2_plane_size(vb, 0) < size) {
dprintk(dev, 1, "%s data will not fit into plane (%lu < %lu)\n",
__func__, vb2_plane_size(vb, 0), size);
return -EINVAL;
}
//将vb->v4l2_planes[0].bytesused = size;
vb2_set_plane_payload(&buf->vb, 0, size);
//将vivi_dev->fmt赋值给vivi_buffer->fmt
buf->fmt = dev->fmt;
//提前准备视频数据
precalculate_bars(dev);
precalculate_line(dev);
return 0;
}