V4L2学习(三)框架分析
阅读原文时间:2023年07月12日阅读:2

整个v4l2的框架分为三层:

在应用层,我们可以在 /dev 目录发现 video0 类似的设备节点,上层的摄像头程序打开设备节点进行数据捕获,显示视频画面。设备节点的名字很统一,video0 video1 video2…这些设备节点在是核心层注册。

核心层 v4l2-dev.c,承上启下,对于每一个硬件相关层注册进来的设备,设置一个统一的接口 v4l2_fops ,既然是统一的接口必然不是具体的视频设备的操作函数,应用层调用 v4l2_fops 中的函数最终将调用到硬件相关层的 video_device 的 fops 。

硬件相关层,与具体的视频硬件打交道,分配、设置、注册 video_device 结构体。

static int __init videodev_init(void)
{
/* 申请设备号,留给 video 设备使用 */
dev_t dev = MKDEV(VIDEO_MAJOR, );
ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME);
/* 创建 video 类 */
ret = class_register(&video_class);
return ;
}

struct video_device
{
/* device ops */
const struct v4l2_file_operations *fops;

/\* sysfs \*/  
struct device dev;        /\* v4l device \*/  
struct cdev \*cdev;        /\* character device \*/

/\* Set either parent or v4l2\_dev if your driver uses v4l2\_device \*/  
struct device \*parent;            /\* device parent \*/  
struct v4l2\_device \*v4l2\_dev;    /\* v4l2\_device parent \*/

/\* Control handler associated with this device node. May be NULL. \*/  
struct v4l2\_ctrl\_handler \*ctrl\_handler;

/\* Priority state. If NULL, then v4l2\_dev->prio will be used. \*/  
struct v4l2\_prio\_state \*prio;

/\* device info \*/  
char name\[\];  
int vfl\_type;  
/\* 'minor' is set to -1 if the registration failed \*/  
int minor;  
u16 num;  
/\* use bitops to set/clear/test flags \*/  
unsigned long flags;  
/\* attribute to differentiate multiple indices on one physical device \*/  
int index;

/\* V4L2 file handles \*/  
spinlock\_t        fh\_lock; /\* Lock for all v4l2\_fhs \*/  
struct list\_head    fh\_list; /\* List of struct v4l2\_fh \*/

int debug;            /\* Activates debug level\*/

/\* Video standard vars \*/  
v4l2\_std\_id tvnorms;        /\* Supported tv norms \*/  
v4l2\_std\_id current\_norm;    /\* Current tvnorm \*/

/\* callbacks \*/  
void (\*release)(struct video\_device \*vdev);

/\* ioctl callbacks \*/  
const struct v4l2\_ioctl\_ops \*ioctl\_ops;  
DECLARE\_BITMAP(valid\_ioctls, BASE\_VIDIOC\_PRIVATE);

/\* serialization lock \*/  
DECLARE\_BITMAP(disable\_locking, BASE\_VIDIOC\_PRIVATE);  
struct mutex \*lock;  

};

struct v4l2_device {
struct device *dev;
/* used to keep track of the registered subdevs */
struct list_head subdevs;
spinlock_t lock;
char name[V4L2_DEVICE_NAME_SIZE];
void (*notify)(struct v4l2_subdev *sd, unsigned int notification, void *arg);
struct v4l2_ctrl_handler *ctrl_handler;
struct v4l2_prio_state prio;
struct mutex ioctl_lock;
struct kref ref;
void (*release)(struct v4l2_device *v4l2_dev);
};

static inline int __must_check video_register_device(struct video_device *vdev,
int type, int nr)
{
return __video_register_device(vdev, type, nr, , vdev->fops->owner);
}

int __video_register_device(struct video_device *vdev, int type, int nr,
int warn_if_nr_in_use, struct module *owner)
{
int i = ;
int ret;
int minor_offset = ;
int minor_cnt = VIDEO_NUM_DEVICES;
const char *name_base;

/\* A minor value of -1 marks this video device as never having been registered \*/  
vdev->minor = -;

/\* 视频设备的设备节点一般为 video0 ..video 就是由此而来 \*/  
switch (type) {  
case VFL\_TYPE\_GRABBER:  
    name\_base = "video";  
    break;  
case VFL\_TYPE\_VBI:  
    name\_base = "vbi";  
    break;  
...  
}

vdev->vfl\_type = type;  
vdev->cdev = NULL;

/\* Part 2: find a free minor, device node number and device index. \*/  

#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
switch (type) {
case VFL_TYPE_GRABBER:
minor_offset = ;
minor_cnt = ;
break;

#endif

/\* 寻找一个空的项,这个好像并不重要 \*/  
mutex\_lock(&videodev\_lock);  
nr = devnode\_find(vdev, nr == - ?  : nr, minor\_cnt);  
if (nr == minor\_cnt)  
    nr = devnode\_find(vdev, , minor\_cnt);  
if (nr == minor\_cnt) {  
    printk(KERN\_ERR "could not get a free device node number\\n");  
    mutex\_unlock(&videodev\_lock);  
    return -ENFILE;  
}  

#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
/* 1-on-1 mapping of device node number to minor number */
i = nr;
#else
/* 在全局 video_deivce 数组中寻找一个空的项,下标+minor_offset作为设备的次设备号 */
for (i = ; 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 vdev->minor = i + minor_offset;
vdev->num = nr;
devnode_set(vdev);

if (vdev->ioctl\_ops)  
    determine\_valid\_ioctls(vdev);

/\* 注册字符设备 \*/  
vdev->cdev = cdev\_alloc();  
vdev->cdev->ops = &v4l2\_fops;  
vdev->cdev->owner = owner;  
ret = cdev\_add(vdev->cdev, MKDEV(VIDEO\_MAJOR, vdev->minor), );

/\* 得把device注册进内核,mdev才能自动创建设备节点,/dev 目录下的video0 等就是来自这里 \*/  
vdev->dev.class = &video\_class;  
vdev->dev.devt = MKDEV(VIDEO\_MAJOR, vdev->minor);  
if (vdev->parent)  
    vdev->dev.parent = vdev->parent;  
dev\_set\_name(&vdev->dev, "%s%d", name\_base, vdev->num);  
ret = device\_register(&vdev->dev);

vdev->dev.release = v4l2\_device\_release;

/\* Part 6: Activate this minor. The char device can now be used. \*/  
set\_bit(V4L2\_FL\_REGISTERED, &vdev->flags);  
mutex\_lock(&videodev\_lock);  
video\_device\[vdev->minor\] = vdev;  
mutex\_unlock(&videodev\_lock);

return ;

}
EXPORT_SYMBOL(__video_register_device);

static const struct file_operations v4l2_fops = {
.owner = THIS_MODULE,
.read = v4l2_read,
.write = v4l2_write,
.open = v4l2_open,
.get_unmapped_area = v4l2_get_unmapped_area,
.mmap = v4l2_mmap,
.unlocked_ioctl = v4l2_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l2_compat_ioctl32,
#endif
.release = v4l2_release,
.poll = v4l2_poll,
.llseek = no_llseek,
};

static int v4l2_open(struct inode *inode, struct file *filp)
{
struct video_device *vdev = video_devdata(filp);
if (vdev->fops->open) {

    if (video\_is\_registered(vdev))  
        ret = vdev->fops->open(filp);  
}  

}
static ssize_t v4l2_read(struct file *filp, char __user *buf,
size_t sz, loff_t *off)
{
struct video_device *vdev = video_devdata(filp);
if (!vdev->fops->read)
return -EINVAL;
if (video_is_registered(vdev))
ret = vdev->fops->read(filp, buf, sz, off);
}
static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
{
struct video_device *vdev = video_devdata(filp);
if (!vdev->fops->mmap)
return ret;
ret = vdev->fops->mmap(filp, vm);
}

static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct video_device *vdev = video_devdata(filp);
if (vdev->fops->unlocked_ioctl) {
if (video_is_registered(vdev))
ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
} else if (vdev->fops->ioctl) {
if (video_is_registered(vdev))
ret = vdev->fops->ioctl(filp, cmd, arg);
}
}

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章