Camera drive (I) -- Analysis of V4L2 framework

V4L2: video for linux version 2 is a set of standard video drivers in linux, which supports uvc standard cameras. This article analyzes its core framework.


The whole framework of v4l2 is divided into three layers:

In the application layer, we can find a device node similar to video0 in the / dev directory. The upper camera program opens the device node to capture data and display video images. The names of device nodes are very uniform, video0 video1 video2... These device nodes are registered in the core layer.

The core layer v4l2-dev.c, connecting the preceding and the following, sets a unified interface v4l2 ﹐ FOPS for each device registered in the hardware related layer. Since it is a unified interface, it is not necessarily the operation function of the specific video device. The application layer calls the function in v4l2 ﹐ fops and finally calls the FOPS of the video ﹐ device in the hardware related layer.

Hardware related layer, dealing with specific video hardware, allocating, setting and registering the video device structure.

  1. static int __init videodev_init(void)  
  2. {  
  3.     /* Request device number for video device*/  
  4.     dev_t dev = MKDEV(VIDEO_MAJOR, 0);  
  5.     ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME);  
  6.     /* Create a video class*/  
  7.     ret = class_register(&video_class);  
  8.     return 0;  
  9. }  
  1. struct video_device  
  2. {  
  3.     /* device ops */  
  4.     const struct v4l2_file_operations *fops;  
  5.   
  6.     /* sysfs */  
  7.     struct device dev;      /* v4l device */  
  8.     struct cdev *cdev;      /* character device */  
  9.   
  10.     /* Set either parent or v4l2_dev if your driver uses v4l2_device */  
  11.     struct device *parent;          /* device parent */  
  12.     struct v4l2_device *v4l2_dev;   /* v4l2_device parent */  
  13.   
  14.     /* Control handler associated with this device node. May be NULL. */  
  15.     struct v4l2_ctrl_handler *ctrl_handler;  
  16.   
  17.     /* Priority state. If NULL, then v4l2_dev->prio will be used. */  
  18.     struct v4l2_prio_state *prio;  
  19.   
  20.     /* device info */  
  21.     char name[32];  
  22.     int vfl_type;  
  23.     /* 'minor' is set to -1 if the registration failed */  
  24.     int minor;  
  25.     u16 num;  
  26.     /* use bitops to set/clear/test flags */  
  27.     unsigned long flags;  
  28.     /* attribute to differentiate multiple indices on one physical device */  
  29.     int index;  
  30.   
  31.     /* V4L2 file handles */  
  32.     spinlock_t      fh_lock; /* Lock for all v4l2_fhs */  
  33.     struct list_head    fh_list; /* List of struct v4l2_fh */  
  34.   
  35.     int debug;          /* Activates debug level*/  
  36.   
  37.     /* Video standard vars */  
  38.     v4l2_std_id tvnorms;        /* Supported tv norms */  
  39.     v4l2_std_id current_norm;   /* Current tvnorm */  
  40.   
  41.     /* callbacks */  
  42.     void (*release)(struct video_device *vdev);  
  43.   
  44.     /* ioctl callbacks */  
  45.     const struct v4l2_ioctl_ops *ioctl_ops;  
  46.     DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE);  
  47.   
  48.     /* serialization lock */  
  49.     DECLARE_BITMAP(disable_locking, BASE_VIDIOC_PRIVATE);  
  50.     struct mutex *lock;  
  51. };  
  1. struct v4l2_device {  
  2.     struct device *dev;  
  3.     /* used to keep track of the registered subdevs */  
  4.     struct list_head subdevs;  
  5.     spinlock_t lock;  
  6.     char name[V4L2_DEVICE_NAME_SIZE];  
  7.     void (*notify)(struct v4l2_subdev *sd, unsigned int notification, void *arg);  
  8.     struct v4l2_ctrl_handler *ctrl_handler;  
  9.     struct v4l2_prio_state prio;  
  10.     struct mutex ioctl_lock;  
  11.     struct kref ref;  
  12.     void (*release)(struct v4l2_device *v4l2_dev);  
  13. };  
  1. static inline int __must_check video_register_device(struct video_device *vdev,  
  2.         int type, int nr)  
  3. {  
  4.     return __video_register_device(vdev, type, nr, 1, vdev->fops->owner);  
  5. }  
  6.   
  7. int __video_register_device(struct video_device *vdev, int type, int nr,  
  8.         int warn_if_nr_in_use, struct module *owner)  
  9. {  
  10.     int i = 0;  
  11.     int ret;  
  12.     int minor_offset = 0;  
  13.     int minor_cnt = VIDEO_NUM_DEVICES;  
  14.     const char *name_base;  
  15.   
  16.     /* A minor value of -1 marks this video device as never having been registered */  
  17.     vdev->minor = -1;  
  18.   
  19.     /* The device node of a video device is generally video0. Video comes from this*/  
  20.     switch (type) {  
  21.     case VFL_TYPE_GRABBER:  
  22.         name_base = "video";  
  23.         break;  
  24.     case VFL_TYPE_VBI:  
  25.         name_base = "vbi";  
  26.         break;  
  27.     ...  
  28.     }  
  29.   
  30.     vdev->vfl_type = type;  
  31.     vdev->cdev = NULL;  
  32.   
  33.     /* Part 2: find a free minor, device node number and device index. */  
  34. #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES  
  35.     switch (type) {  
  36.     case VFL_TYPE_GRABBER:  
  37.         minor_offset = 0;  
  38.         minor_cnt = 64;  
  39.         break;  
  40.     ...  
  41. #endif  
  42.   
  43.     /* It doesn't seem important to find an empty item*/  
  44.     mutex_lock(&videodev_lock);  
  45.     nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt);  
  46.     if (nr == minor_cnt)  
  47.         nr = devnode_find(vdev, 0, minor_cnt);  
  48.     if (nr == minor_cnt) {  
  49.         printk(KERN_ERR "could not get a free device node number\n");  
  50.         mutex_unlock(&videodev_lock);  
  51.         return -ENFILE;  
  52.     }  
  53. #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES  
  54.     /* 1-on-1 mapping of device node number to minor number */  
  55.     i = nr;  
  56. #else  
  57.     /* Find an empty item in the global video ﹣ deivce array, with the subscript + minor ﹣ offset as the secondary device number of the device*/  
  58.     for (i = 0; i < VIDEO_NUM_DEVICES; i++)  
  59.         if (video_device[i] == NULL)  
  60.             break;  
  61.     if (i == VIDEO_NUM_DEVICES) {  
  62.         mutex_unlock(&videodev_lock);  
  63.         printk(KERN_ERR "could not get a free minor\n");  
  64.         return -ENFILE;  
  65.     }  
  66. #endif  
  67.     vdev->minor = i + minor_offset;  
  68.     vdev->num = nr;  
  69.     devnode_set(vdev);  
  70.   
  71.     if (vdev->ioctl_ops)  
  72.         determine_valid_ioctls(vdev);  
  73.   
  74.     /* Register character device*/  
  75.     vdev->cdev = cdev_alloc();  
  76.     vdev->cdev->ops = &v4l2_fops;  
  77.     vdev->cdev->owner = owner;  
  78.     ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);  
  79.       
  80.     /* The device must be registered in the kernel before mdev can automatically create a device node. The video0 in the / dev directory comes from here*/  
  81.     vdev->dev.class = &video_class;  
  82.     vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);  
  83.     if (vdev->parent)  
  84.         vdev->dev.parent = vdev->parent;  
  85.     dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);  
  86.     ret = device_register(&vdev->dev);  
  87.   
  88.     vdev->dev.release = v4l2_device_release;  
  89.   
  90.     /* Part 6: Activate this minor. The char device can now be used. */  
  91.     set_bit(V4L2_FL_REGISTERED, &vdev->flags);  
  92.     mutex_lock(&videodev_lock);  
  93.     video_device[vdev->minor] = vdev;  
  94.     mutex_unlock(&videodev_lock);  
  95.   
  96.     return 0;  
  97.   
  98. }  
  99. EXPORT_SYMBOL(__video_register_device);  
  1. static const struct file_operations v4l2_fops = {  
  2.     .owner = THIS_MODULE,  
  3.     .read = v4l2_read,  
  4.     .write = v4l2_write,  
  5.     .open = v4l2_open,  
  6.     .get_unmapped_area = v4l2_get_unmapped_area,  
  7.     .mmap = v4l2_mmap,  
  8.     .unlocked_ioctl = v4l2_ioctl,  
  9. #ifdef CONFIG_COMPAT  
  10.     .compat_ioctl = v4l2_compat_ioctl32,  
  11. #endif  
  12.     .release = v4l2_release,  
  13.     .poll = v4l2_poll,  
  14.     .llseek = no_llseek,  
  15. };  
  1. static int v4l2_open(struct inode *inode, struct file *filp)  
  2. {  
  3.     struct video_device *vdev = video_devdata(filp);  
  4.     if (vdev->fops->open) {  
  5.   
  6.         if (video_is_registered(vdev))  
  7.             ret = vdev->fops->open(filp);  
  8.     }  
  9. }  
  10. static ssize_t v4l2_read(struct file *filp, char __user *buf,  
  11.         size_t sz, loff_t *off)  
  12. {  
  13.     struct video_device *vdev = video_devdata(filp);  
  14.     if (!vdev->fops->read)  
  15.         return -EINVAL;  
  16.     if (video_is_registered(vdev))  
  17.         ret = vdev->fops->read(filp, buf, sz, off);  
  18. }  
  19. static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)  
  20. {  
  21.     struct video_device *vdev = video_devdata(filp);  
  22.     if (!vdev->fops->mmap)  
  23.         return ret;  
  24.     ret = vdev->fops->mmap(filp, vm);  
  25. }  
  26.   
  27. static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)  
  28. {  
  29.     struct video_device *vdev = video_devdata(filp);  
  30.     if (vdev->fops->unlocked_ioctl) {  
  31.         if (video_is_registered(vdev))  
  32.             ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);  
  33.     } else if (vdev->fops->ioctl) {  
  34.         if (video_is_registered(vdev))  
  35.             ret = vdev->fops->ioctl(filp, cmd, arg);  
  36.     }   
  37. }  

Tags: Linux Attribute

Posted on Tue, 31 Mar 2020 03:43:29 -0700 by vwinstead