记一次 VIDIOC_REQBUFS failed 问题

Linux v4l2架构学习总链接

最近更新了一个手里板子的sdk,在写应用测试vivi驱动的时候,出现了一个错误。

	req.count = 5;
	req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	req.memory = V4L2_MEMORY_MMAP;
	if (ioctl(fd, VIDIOC_REQBUFS, &req) < 0) {
		printf("Reqbufs fail\n");
		goto err;
	}
Reqbufs fail

这里比较奇怪,于是打算去分析一下原因。

static int vidioc_reqbufs(struct file *file, void *priv,
			  struct v4l2_requestbuffers *p)
{
	struct vivi_dev *dev = video_drvdata(file);
	return vb2_reqbufs(&dev->vb_vidq, p);
}

从vivi的reqbufs开始分析,发现会使用vb2_reqbufs

这个函数的代码位置是 kernel/drivers/media/common/videobuf2/videobuf2-v4l2.c

打开其对应的打印信息

echo 7 > /sys/module/videobuf2_common/parameters/debug

执行应用程序

打印信息如下

videobuf2_common: vb2_verify_memory_type: file io in progress

对应的代码位置 vb2_verify_memory_type

判断q->fileio的值非空,则就会打印这个信息。

由于不想去改内核代码,所以决定修改vivi驱动,加一些打印信息

static int vidioc_reqbufs(struct file *file, void *priv,
                          struct v4l2_requestbuffers *p)
{
        struct vivi_dev *dev = video_drvdata(file);
        struct vb2_queue *q = &dev->vb_vidq;


        printk(KERN_ERR "%s\n", __func__);

        if (vb2_fileio_is_active(q))
                printk(KERN_ERR "q->fileio --------------\n");
        return vb2_reqbufs(&dev->vb_vidq, p);
}

但是测试的时候,q->fileio -----这条语句并没有打印,没办法打算内核中加打印

int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
{
        int ret;
        printk(KERN_ERR "%s\n", __func__);
        ret = vb2_verify_memory_type(q, req->memory, req->type);
        printk(KERN_ERR "%s 1111\n", __func__);
        return ret ? ret : vb2_core_reqbufs(q, req->memory, &req->count);
}

并且在vb2_core_reqbufs中也加了打印信息

测试发现vb2_core_reqbufs中打印信息没有出现,所以可以定位到就是这里的vb2_verify_memory_type出了问题

由于是更新了sdk

怀疑是不是打开摄像头的这些步骤中加了一些其他的东西,追了一下并没有发现什么改动。

但是同样结构体经过一个函数后,成员值变了,这就很纳闷了。

于是又想到一个问题,是不是vivi编译成ko,导致一些变量的偏移量和新的sdk的不同,所以决定打印一下这个结构体的大小

vivi加入下面的log

static int vidioc_reqbufs(struct file *file, void *priv,
                          struct v4l2_requestbuffers *p)
{
        struct vivi_dev *dev = video_drvdata(file);
        struct vb2_queue *q = &dev->vb_vidq;


        printk(KERN_ERR "%s\n", __func__);
        printk(KERN_ERR "%s size = %d\n", __func__, sizeof(struct vb2_queue));

        if (vb2_fileio_is_active(q))
                printk(KERN_ERR "q->fileio --------------\n");
        return vb2_reqbufs(&dev->vb_vidq, p);
}

内核中加入如下log

int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
{
        int ret;
        printk(KERN_ERR "%s\n", __func__);
        printk(KERN_ERR "%s size = %d\n", __func__, sizeof(struct vb2_queue));
        ret = vb2_verify_memory_type(q, req->memory, req->type);
        printk(KERN_ERR "%s 1111\n", __func__);
        return ret ? ret : vb2_core_reqbufs(q, req->memory, &req->count);
}

 测试打印信息如下

[   36.774267] vidioc_reqbufs size = 304
[   36.774283] vb2_reqbufs size = 432

大小变了,这就好办了,于是对比结构体,发现了如下的差异

struct vb2_queue {
	...
	struct device			*alloc_devs[VB2_MAX_PLANES];
        ...

	struct vb2_fileio_data		*fileio;
	...
}

原来的VB2_MAX_PLANES值为32,同步后新的值为64,所以说内核使用fileio和vivi并不是一个。

 

最后的结论就是,不要偷懒,如果使用新的内核,那么所有的程序都要在新的内核上编译,这样才不会出现这种问题。

int init_camera_attribute(int fd) { int numBufs; v4l2_std_id id; struct v4l2_format fmt; struct v4l2_requestbuffers req; struct v4l2_buffer buf; //检查当前视频设备支持的标准 ioctl(fd,VIDIOC_QUERYSTD,&id); //设置视频捕获格式 memset(&fmt,0,sizeof(fmt)); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = 640; fmt.fmt.pix.height = 480; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; // fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; if(ioctl(fd,VIDIOC_S_FMT,&fmt) == -1){ perror("set VIDIOC_S_FMT is fail"); exit(EXIT_FAILURE); } //分配内存 memset(&req,0,sizeof(req)); req.count = 4; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; if(ioctl(fd,VIDIOC_REQBUFS,&req) == -1){ perror("set VIDIOC_REQBUFS is fail"); exit(EXIT_FAILURE); } //获取并录缓存的物理空间 buffers = calloc(req.count,sizeof(*buffers)); for(numBufs = 0; numBufs < req.count; numBufs ++){ memset(&buf,0,sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = numBufs; //读取缓存 if(ioctl(fd,VIDIOC_QUERYBUF,&buf) == -1){ perror("set VIDIOC_REQBUFS is fail"); exit(EXIT_FAILURE); } // 转换成相对地址 buffers[numBufs].length = buf.length; buffers[numBufs].start = mmap(NULL,buf.length,PROT_READ|PROT_WRITE, MAP_SHARED,fd,buf.m.offset); if(buffers[numBufs].start == MAP_FAILED){ perror("mmap is fail"); exit(EXIT_FAILURE); } // 放入缓存队列 if(ioctl(fd,VIDIOC_QBUF,&buf) == -1){ perror("set VIDIOC_QBUF is fail"); exit(EXIT_FAILURE); } } return 0; }
06-12
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dianlong_lee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值