V4L2 framework analysis

       V4L2 is the abbreviation of Video for linux2, which is the kernel driver of video equipment in linux. v4L2 is a programming framework for uvc (USB Video Class) drive-free USB devices, mainly used to collect USB cameras and so on.

      The following figure is the framework of V4L2. First, the system core layer allocation settings register a variable named cdev structure (cdev structure is part of the video_device structure), and set cdev-> ops = v4l2_fops; on the hardware layer, we allocate settings registration A variable named vfd structure (video_device structure), and set vfd-> fops = & vivi_fops, vfd-> ioctl_ops = & vivi_ioctl_ops; when the application (APP) calls functions such as read and open, it will be called in v4l2_fops The read and open functions, and then the read and open functions in v4l2_fops will call the read and open functions in vfd-> fops related to the hardware layer. The ioctl function is similar.

        Let's start with the program to analyze the V4L2 framework. This article uses the virtual video driver vivi.c in the drivers \ medio \ video in the Linux kernel directory (this code uses v4l2 api to simulate a real video device) to analyze the V4L2 framework. . Its overall framework is as follows:

vivi_init
    vivi_create_instance
        v4l2_device_register   // 不是主要, 只是用于初始化一些东西,比如自旋锁、引用计数
        video_device_alloc
        // 设置
          1. vfd:
            .fops           = &vivi_fops,
            .ioctl_ops 	= &vivi_ioctl_ops,
            .release	= video_device_release,
          2.
            vfd->v4l2_dev = &dev->v4l2_dev;
          3. 设置"ctrl属性"(用于APP的ioctl):
            	v4l2_ctrl_handler_init(hdl, 11);
            	dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
            			V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200);
            	dev->brightness = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
            			V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
            	dev->contrast = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
            			V4L2_CID_CONTRAST, 0, 255, 1, 16);                        
        video_register_device(video_device, type:VFL_TYPE_GRABBER, nr)
            __video_register_device
                vdev->cdev = cdev_alloc();
                vdev->cdev->ops = &v4l2_fops;
                cdev_add
                
                video_device[vdev->minor] = vdev;

        		if (vdev->ctrl_handler == NULL)
        			vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;

①We started from the vivi_init function in vivi.c and found that it called v4l2_device_register, which is used to initialize some things, such as spin lock, reference counting, which is not necessary; ②Called video_device_alloc to allocate video_device structure and Make the appropriate settings, for example

.fops             = &vivi_fops,
.ioctl_ops     = &vivi_ioctl_ops,
.release       = video_device_release,

Wait for the settings, and then register the structure with video_register_device;

③ The video_register_device function calls __video_register_device to achieve the following operations:

vdev->cdev = cdev_alloc();
vdev->cdev->ops = &v4l2_fops;
cdev_add
                
video_device[vdev->minor] = vdev;

if (vdev->ctrl_handler == NULL)
    vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;

 

 

The picture above is part of the vivi_create_instance function. First assign a variable vfd of the video_device structure, and then set * vfd = vivi_template; where vivi_template is a structure variable of the video_device, which itself sets some information such as .fops (as shown below ), This operation is equivalent to setting

 1. vfd:

.fops             = &vivi_fops,
.ioctl_ops     = &vivi_ioctl_ops,
.release       = video_device_release,

static struct video_device vivi_template = {
	.name		= "vivi",
	.fops           = &vivi_fops,
	.ioctl_ops 	= &vivi_ioctl_ops,
	.release	= video_device_release,

	.tvnorms              = V4L2_STD_525_60,
	.current_norm         = V4L2_STD_NTSC_M,
};

Then enter the video_register_device function, the following is part of the source code in video_register_device, first allocate a cdev structure

Then set cdev-> ops = & v4l2_fops; v4l2_fops itself points to some functions (as shown below), so cdev also points to these functions, when the APP calls the read function, it will call the read function in cdev

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,
};

The read function in cdev is as shown in the figure below. First, the video_device structure is obtained according to filp, and then the read function in the video_device structure is judged. If it exists, it is called, so finally it is called to the vfd.fops we set earlier read function.

 

 

 

 

Compared with the open and read functions, the calling process of ioctl is more complicated. Let's take a look below (we take VIDIOC_QUERYCAP as an example). The following figure is the v4l2_ioctl function pointed to by .unlocked_ioctl in v4l2_fops.

It calls the ioctl in the fops of the previous vivi_template.

The ioctl in the fops of vivi_template is called to the __video_do_ioctl function in the following figure. This function finally calls the function in the ioctl_ops member in vfd, that is, the function in vivi_ioctl_ops

For example, if VIDIOC_QUERYCAP is called, the following function will eventually be called.

/* ------------------------------------------------------------------
	IOCTL vidioc handling
   ------------------------------------------------------------------*/
static int vidioc_querycap(struct file *file, void  *priv,
					struct v4l2_capability *cap)
{
	struct vivi_dev *dev = video_drvdata(file);

	strcpy(cap->driver, "vivi");
	strcpy(cap->card, "vivi");
	strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info));
	cap->version = VIVI_VERSION;
	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | \
			    V4L2_CAP_READWRITE;
	return 0;
}

 

 

Summary: The call of ioctl is one layer more than open and read . When the APP calls the ioctl function, it will call the ioctl function in cdev, and then call the ioctl function in vfd.fops we set earlier, that is, read, open V4l2_ioctl in the same structure of the function, and then finally call to and  

.fops = & vivi_fops, the
corresponding function in .ioctl_ops = & vivi_ioctl_ops in the same structure .

 

 

related articles:

https://blog.csdn.net/qingkongyeyue/article/details/53447331

https://blog.csdn.net/qingkongyeyue/article/details/52372960

Published 42 original articles · Like 10 · Visitors 10,000+

Guess you like

Origin blog.csdn.net/qq_37659294/article/details/104139839