本文内核版本为4.1.15
本文介绍的是内核自带的vivid代码,分析Linux camera框架,是Linux内核中的基于V4L2的video驱动。
介绍
- 字符设备驱动核心:V4L2是一个字符设备驱动,具有字符设备的属性。file_operation中的函数与用户空间进行操作。
- V4L2 驱动核心:构建一个内核中标准视频设备驱动的框架,为视频操作提供统一的接口函数。
- 平台V4L2设备驱动:V4L2框架下,根据平台自身的特性实现与平台相关的V4L2驱动部分,包括注册video_device和v4l2_dev。
- 具体的sensor驱动:主要上电、提供工作时钟、视频图像裁剪、流IO开启等,实现各种设备控制方法供上层调用并注册v4l2_subdev。
驱动加载与卸载
static struct platform_device vivid_pdev = {
.name = "vivid",
.dev.release = vivid_pdev_release,
};
static struct platform_driver vivid_pdrv = {
.probe = vivid_probe,
.remove = vivid_remove,
.driver = {
.name = "vivid",
},
};
static int __init vivid_init(void)
{
int ret;
ret = platform_device_register(&vivid_pdev);
if (ret)
return ret;
ret = platform_driver_register(&vivid_pdrv);
if (ret)
platform_device_unregister(&vivid_pdev);
return ret;
}
static void __exit vivid_exit(void)
{
platform_driver_unregister(&vivid_pdrv);
platform_device_unregister(&vivid_pdev);
}
module_init(vivid_init);
module_exit(vivid_exit);
加载的时候先行进行注册一个设备,设备注册成功之后再进行注册驱动。这对设备和驱动是配套的。
退出的时候也是将设备和驱动进行卸载。
video节点注册
设备与驱动匹配成功之后就会执行probe函数:
static int vivid_probe(struct platform_device *pdev)
{
const struct font_desc *font = find_font("VGA8x16");
int ret = 0, i;
n_devs = clamp_t(unsigned, n_devs, 1, VIVID_MAX_DEVS);
for (i = 0; i < n_devs; i++) {
ret = vivid_create_instance(pdev, i);
}
}
此函数是通过vivid_create_instance()函数进行注册video设备节点的。
下面将一起来分析该函数。
主要内容就是初始化、配置和注册。
vivid_create_instance
v4l2_device_register //初始化锁、链表等资源
q->ops = &vivid_vid_cap_qops; //设置vbuff的操作函数
q->mem_ops = &vb2_vmalloc_memops; //设置mem的操作函数
vfd->fops = &vivid_fops; //设置v4l2的操作函数
vfd->ioctl_ops = &vivid_ioctl_ops; //设置ioctl操作函数
video_register_device //注册驱动
__video_register_device
case VFL_TYPE_GRABBER: name_base = "video";
vdev->cdev = cdev_alloc();
vdev->cdev->ops = &v4l2_fops;
cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
device_register(&vdev->dev);
ioctl
摄像头的ioctl是应用程序进行设置参数的重点,v4l2编程主要是调用一系列的ioctl函数去对v4l2设备进行打开, 关闭, 查询, 设置等操作,我们可以分析一下ioctl:
v4l2_ioctl
video_ioctl2
video_usercopy
__video_do_ioctl //根据应用层传入的cmd获取、设置
摄像头程序必要的ioctl
static const struct v4l2_ioctl_ops vivid_ioctl_ops = {
.vidioc_querycap = vidioc_querycap, //摄像头设备
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid, //枚举数据格式
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, //获得数据格式
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, //测试数据格式
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, //设置数据格式
.vidioc_reqbufs = vb2_ioctl_reqbufs, //申请队列
.vidioc_querybuf = vb2_ioctl_querybuf, //查询队列
.vidioc_qbuf = vb2_ioctl_qbuf, //放入队列
.vidioc_dqbuf = vb2_ioctl_dqbuf, //取出队列
.vidioc_streamon = vb2_ioctl_streamon, //启动
.vidioc_streamoff = vb2_ioctl_streamoff, //停止
};