03_从0编写一个虚拟摄像头驱动#

参考资料:

  • 深入理解linux内核v4l2框架之videobuf2:https://blog.csdn.net/yyjsword/article/details/9243717

  • V4L2框架-videobuf2:https://blog.csdn.net/u013904227/article/details/81054611

1. 回顾与编写程序框架#

本节视频对应的代码:

doc_and_source_for_drivers\
	IMX6ULL\source\
		13_V4L2\
			06_virtual_driver\
				01_virtual_driver_framework

参考《1.3.2 videobuffer2的3个ops》里面完整的注册流程。

完整的注册流程(参考drivers\media\usb\airspy\airspy.c):

static struct video_device airspy_template = {
	.name                     = "AirSpy SDR",
	.release                  = video_device_release_empty,
	.fops                     = &airspy_fops,
	.ioctl_ops                = &airspy_ioctl_ops,
};

/* 构造一个vb2_queue */
	/* Init videobuf2 queue structure */
	s->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE;
	s->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
	s->vb_queue.drv_priv = s;
	s->vb_queue.buf_struct_size = sizeof(struct airspy_frame_buf);
	s->vb_queue.ops = &airspy_vb2_ops;          // vb2_ops, 硬件相关的操作函数
	s->vb_queue.mem_ops = &vb2_vmalloc_memops;  // vb2_mem_ops, 辅助结构体,用于mem ops(alloc、mmap)
	s->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
	ret = vb2_queue_init(&s->vb_queue);
				q->buf_ops = &v4l2_buf_ops;    // vb2_buf_ops, 用于APP和驱动传递参数

// 分配/设置video_device结构体
s->vdev = airspy_template;
s->vdev.queue = &s->vb_queue;    // 指向前面构造的vb2_queue

// 初始化一个v4l2_device结构体(起辅助作用)
/* Register the v4l2_device structure */
s->v4l2_dev.release = airspy_video_release;
ret = v4l2_device_register(&intf->dev, &s->v4l2_dev);

// video_device和4l2_device建立联系
s->vdev.v4l2_dev = &s->v4l2_dev;

// 注册video_device结构体
ret = video_register_device(&s->vdev, VFL_TYPE_SDR, -1);
		__video_register_device
			// 根据次设备号把video_device结构体放入数组
			video_device[vdev->minor] = vdev;
			
			// 注册字符设备驱动程序
			vdev->cdev->ops = &v4l2_fops;
			vdev->cdev->owner = owner;
			ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);

2. 编写硬件相关代码#

本节视频对应的代码:

doc_and_source_for_drivers\
	IMX6ULL\source\
		13_V4L2\
			06_virtual_driver\
				02_virtual_driver_hardware

2.1 ioctl相关的代码#

2.2 buffer相关的代码#

2.3 数据传输#

linux下可以使用如下命令:

hexdump -v -e '8/1 "0x%02x, " "\n"'  red.jpg

windows下使用winhex工具,视频有教。

3. 上机测试#

可以禁止LCD自动黑屏,执行以下命令即可:

#close lcd sleep
echo -e "\033[9;0]" > /dev/tty1
echo -e "\033[?25l"  > /dev/tty1

3.1 IMX6ULL#

先关闭GUI:

[root@100ask:~]# mv /etc/init.d/S99myirhmi2 /root/
[root@100ask:~]# mv /etc/init.d/*lvgl* /root/
[root@100ask:~]# reboot

然后编译驱动:13_V4L2\06_virtual_driver\04_virtual_driver_ok\

编译APP:13_V4L2\02_video2lcd\

测试:

insmod my_video_drv.ko
./video2lcd /dev/video1

3.2 STM32MP157#

3.2.1 编译#

先编译内核,编译模块:

make 100ask_stm32mp157_pro_defconfig
make uImage LOADADDR=0xC2000040  -j 12
make modules -j 12

然后编译驱动:13_V4L2\06_virtual_driver\04_virtual_driver_ok\

编译APP:13_V4L2\02_video2lcd\

3.2.2 在开发板上操作#

关闭GUI:

systemctl stop myir  //关闭自带的GUI程序

测试:

modprobe videobuf2-common
modprobe videobuf2-v4l2
modprobe videobuf2-memops
modprobe videobuf2-vmalloc

insmod my_video_drv.ko
./video2lcd /dev/video0