从零写UVC摄像头驱动(二)数据传输

5 篇文章 0 订阅
5 篇文章 1 订阅

前言:设置打印输出调试信息

A、dmesg命令

A. 设置ubuntu让它从串口0输出printk信息
        a. 设置vmware添加serial port, 使用文件作为串口
        b. 启动ubuntu,修改/etc/default/grub
                GRUB_CMDLINE_LINUX_DEFAULT=""
                GRUB_CMDLINE_LINUX="console=tty0 console=ttyS0,115200n8"

                sudo update-grub
                sudo reboot

当USB插上主机,就会产生两个接口(VC和VS),然后获取到USB描述符并解析,从而设置摄像头(比如分辨率、格式);然后分配缓冲区,启动摄像头,便从USB得到摄像头采集数据,保存到缓冲区供应用程序使用。详细过程看这两篇:UVC 设备框架在 Linux 4.15 内核的演变_v4l2 核心在尝试映射 uvc 控件时找不到相应的文件或目录-CSDN博客Linux摄像头驱动2——UVC(转)_uvc协议分主从关系吗-CSDN博客说的很清楚。

缓冲区的相关操作

首先是申请缓冲区vidioc_reqbufs(),应用层ioctl调用此函数,让其分配若干个buf,应用层后面将从这些buf读取视频数据。
驱动先从传入的v4l2_requestbuffers结构体获得count(buf数量),每个buf的大小是前面my_uvc_format的sizeimage(每帧图像大小),且长度页对齐。

/*A7 APP调用该ioctrl让驱动程序分配若干个缓存,APP将从这些缓存中读到视频数据
 *   参考uvcg_alloc_buffers
 * */
static int myuvc_vidioc_reqbufs(struct file *file, void *priv,
			  struct v4l2_requestbuffers *p)
{
	int nbuffers = p->count;
	int bufsize = PAGE_ALIGN(myuvc_format.fmt.pix.sizeimage);
	unsigned int i;
	void *mem = NULL;
	int ret;

	/*如果已经有BUF则先free掉*/
	if((ret=myuvc_free_buffers())<0)
	{
		goto done;
	}
	/* Bail out if no buffers should be allocated. */
    if (nbuffers == 0)
	{
		goto done;
	}
    /* Decrement the number of buffers until allocation succeeds. */
    for (; nbuffers > 0; --nbuffers) 
	{
        mem = vmalloc_32(nbuffers * bufsize);
        if (mem != NULL)
		{
			break;
		}
    }
	/*沒有成功则返回*/
	if (mem == NULL) 
	{
        ret = -ENOMEM;
        goto done;
    }
	/* 这些缓存是一次性作为一个整体来分配的 */
    memset(&myuvc_queue, 0, sizeof(myuvc_queue));

	/*初始化两个队列头*/
	INIT_LIST_HEAD(&myuvc_queue.mainqueue);
	INIT_LIST_HEAD(&myuvc_queue.irqqueue);

	/*将一整块区域人工分开,分成几个区域*/
	for (i = 0; i < nbuffers; ++i) {
       myuvc_queue.buffer[i].buf.index = i;		/*索引*/
        myuvc_queue.buffer[i].buf.m.offset = i * bufsize;	/*偏移*/
        myuvc_queue.buffer[i].buf.length = myuvc_format.fmt.pix.sizeimage;	/*原始大小*/
        myuvc_queue.buffer[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;	/*视频捕获设备*/
        myuvc_queue.buffer[i].buf.sequence = 0;				/*序列技术*/
        myuvc_queue.buffer[i].buf.field = V4L2_FIELD_NONE;	/*扫描方式*/
        myuvc_queue.buffer[i].buf.memory = V4L2_MEMORY_MMAP;/*内存类型*/
        myuvc_queue.buffer[i].buf.flags = 0;				/*标志*/
        myuvc_queue.buffer[i].state     = VIDEOBUF_IDLE;	/*状态*/
        init_waitqueue_head(&myuvc_queue.buffer[i].wait);	/*初始化等待队列*/
    }
	myuvc_queue.mem = mem;
    myuvc_queue.count = nbuffers;
    myuvc_queue.buf_size = bufsize;
    ret = nbuffers;
done:
    return ret;
}

判断my_uvc_queue结构体里的mem(内存地址)是否为空,非空的话说明原来已经分配了buf,需要先释放内存、清空my_uvc_queue。如果传入需要的buf数量为0,则表明不需要分配,直接退出。然后就分配buf,将所有buf作为一个整体一次性分配,大小也就是nbuffers * bufsize,如果分配失败,减小buf数量,再尝试。

现在就有了一整块buf,对应的起始地址是mem,再清空my_uvc_queue进行初始化。

再初始化两个队列(双向链表),mainqueue用于供应用层读取数据用,irqqueue用于供驱动产生数据用。

再依次设置每个buf的v4l2_buffer结构体的index(索引)、m.offset(偏移)、length(大小)、type(类型)、sequence(序列计数)、field(扫描方式)、memory(内存类型)、flags(标志),再设置my_uvc_bufferstate(状态)和初始化等待队列wait。最后再设置my_uvc_q,记录buf首地址、数量和大小。

 


                        

补充:

  •  VideoControl Interface用于控制,比如设置亮度。

       它内部有多个Unit/Terminal(在程序里Unit/Terminal都称为entity)
       可以通过uvc_query_ctrl类似的函数来访问:

ret = uvc_query_ctrl(dev /*哪一个USB设备*/, SET_CUR, ctrl->entity->id /*哪一个unit/terminal*/, dev->intfnum /*哪一个接口:VC interface*/, ctrl->info->selector, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), ctrl->info->size);
  • VideoStreaming Interface用于获得视频数据,也可以用来选择fromat/frame(VS可能有多种format,一个format支持多种frame,frame用来表示分辨率等信息)

       可以通过__uvc_query_ctrl类似的函数来访问:

ret = __uvc_query_ctrl(video->dev /*哪一个USB设备*/, SET_CUR, 0, video->streaming->intfnum /*哪一个接口: VS*/, probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size, uvc_timeout_param);

我们在设置FORMAT时只是简单的使用video->streaming->format[fmt->index]等数据,这些数据哪来的?
   应是设备被枚举时设置的,也就是分析它的描述符时设置的。

参考链接:https://blog.csdn.net/Parismoor/article/details/97374008

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值