V4L2机制
谨以此文纪念过往的岁月
一.前言
在上一篇中主要讲述了V4L2中几个比较中要的cmd,那在该篇中来看一下V4L2真正的面目,亦如以前看input子系统时一样,只有在知道了这些东东的内涵后才会知道这些东东真正的用途。请记住我们不是内核API调用工程师,而是驱动工程师,唯有真正的理解内核驱动才会有真正的突破,否则我们就仅仅是一个内核API调用工程师。
二.V4L2的本质
V4L2与input类似都是自己均会注册一个设备和驱动,然后再提供一些接口即API供驱动工程师使用。
2.1 V4L2设备注册以及注销
在v4l2-dev.c中实现了v4l2父设备的注册。
static int __init videodev_init(void)
{
dev_t dev = MKDEV(VIDEO_MAJOR, 0);
int ret;
ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME); --注册一个主设备号
if (ret < 0) {
return ret;
}
ret = class_register(&video_class); --注册设备类
if (ret < 0) {
unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
return -EIO;
}
return 0;
}
static void __exit videodev_exit(void)
{
dev_t dev = MKDEV(VIDEO_MAJOR, 0);
class_unregister(&video_class); --注销设备类
unregister_chrdev_region(dev, VIDEO_NUM_DEVICES); --注销设备号
}
上面就是v4l2核心的注册。
2.2 video设备的注册
那来看一下一个video设备的注册,即在v4l2主设备号下创建一个从设备。
int video_register_device_index(struct video_device *vfd, int type, int nr,int index)
{
int i = 0;
int ret;
int minor_offset = 0;
int minor_cnt = VIDEO_NUM_DEVICES;
const char *name_base;
void *priv = video_get_drvdata(vfd);
/* the release callback MUST be present */
BUG_ON(!vfd->release);
if (vfd == NULL)
return -EINVAL;
switch (type) {
case VFL_TYPE_GRABBER:
name_base = "video";
break;
case VFL_TYPE_VTX:
name_base = "vtx";
break;
case VFL_TYPE_VBI:
name_base = "vbi";
break;
case VFL_TYPE_RADIO:
name_base = "radio";
break;
default:
printk(KERN_ERR "%s called with unknown type: %d\n",__func__, type);
return -EINVAL;
}
vfd->vfl_type = type;
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES --以下是根据不同的设备类型来给从设备号分类
switch (type) {
case VFL_TYPE_GRABBER:
minor_offset = 0;
minor_cnt = 64;
break;
case VFL_TYPE_RADIO:
minor_offset = 64;
minor_cnt = 64;
break;
case VFL_TYPE_VTX:
minor_offset = 192;
minor_cnt = 32;
break;
case VFL_TYPE_VBI:
minor_offset = 224;
minor_cnt = 32;
break;
default:
minor_offset = 128;
minor_cnt = 64;
break;
}
#endif
cdev_init(&vfd->cdev, vfd->fops); --很明显初始化一个cdev
vfd->cdev.owner = vfd->fops->owner;
mutex_lock(&videodev_lock);
nr = find_next_zero_bit(video_nums[type], minor_cnt, nr == -1 ? 0 : nr); --下面是寻找空的从设备号
if (nr == minor_cnt)
nr = find_first_zero_bit(video_nums[type], minor_cnt);
if (nr == minor_cnt) {
printk(KERN_ERR "could not get a free kernel number\n");
mutex_unlock(&videodev_lock);
return -ENFILE;
}
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
i = nr;
#else
/* The kernel number and minor numbers are independent */
for (i = 0; i < VIDEO_NUM_DEVICES; i++)
if (video_device[i] == NULL)
break;
if (i == VIDEO_NUM_DEVICES) {
mutex_unlock(&videodev_lock);
printk(KERN_ERR "could not get a free minor\n");
return -ENFILE;
}
#endif
vfd->minor = i + minor_offset;
vfd->num = nr;
set_bit(nr, video_nums[type]);
BUG_ON(video_device[vfd->minor]);
video_device[vfd->minor] = vfd; --用全局变量来存储
ret = get_index(vfd, index); --根据父流来查询空的index
vfd->index = ret;
mutex_unlock(&videodev_lock);
if (ret < 0) {
printk(KERN_ERR "%s: get_index failed\n", __func__);
goto fail_minor;
}
ret = cdev_add(&vfd->cdev, MKDEV(VIDEO_MAJOR, vfd->minor), 1); --添加cdev
if (ret < 0) {
printk(KERN_ERR "%s: cdev_add failed\n", __func__);
goto fail_minor;
}
/* sysfs class */
memset(&vfd->dev, 0, sizeof(vfd->dev));
video_set_drvdata(vfd, priv);
vfd->dev.class = &video_class; --设置父类
vfd->dev.devt = MKDEV(VIDEO_MAJOR, vfd->minor);
if (vfd->parent)
vfd->dev.parent = vfd->parent;
sprintf(vfd->dev.bus_id, "%s%d", name_base, nr);
ret = device_register(&vfd->dev); --注册设备
if (ret < 0) {
printk(KERN_ERR "%s: device_register failed\n", __func__);
goto del_cdev;
}
vfd->cdev_release = vfd->cdev.kobj.ktype->release; --这两段code比较难懂,其实还是需要回过头去看kobj等的真正含义。
vfd->cdev.kobj.ktype = &v4l2_ktype_cdev_default;
return 0;
del_cdev:
cdev_del(&vfd->cdev);
fail_minor:
mutex_lock(&videodev_lock);
video_device[vfd->minor] = NULL;
clear_bit(vfd->num, video_nums[type]);
mutex_unlock(&videodev_lock);
vfd->minor = -1;
return ret;
}
到此为止那回过头去看v4l2机制,这些东西都是在最基础的cdev的类型上构造出一个更好的平台去使用。对于字符类型的驱动而言,其根本均是cdev。
阅读(819) | 评论(0) | 转发(0) |