? ? ? ?V4L2是Video for linux2的簡稱,為linux中關于視頻設備的內核驅動。v4L2是針對uvc(USB Video Class)免驅usb設備的編程框架,主要用于采集usb攝像頭等。
? ? ? 下圖是V4L2的框架,首先系統核心層分配設置注冊一個名為cdev結構體變量(cdev結構體是video_device結構體里的一部分),并設置cdev->ops = v4l2_fops;在硬件層我們分配設置注冊了一個名為vfd結構體變量(video_device結構體),并設置vfd->fops = &vivi_fops,vfd->ioctl_ops ?= &vivi_ioctl_ops;當應用程序(APP)調用read、open等函數時,會調用到v4l2_fops里的read、open函數,然后v4l2_fops里的read、open函數會再調用到硬件層相關的vfd->fops里的read、open函數。ioctl函數也類似。
? ? ? ? 下面我們從程序入手來分析V4L2的框架,本文借助Linux內核目錄下的drivers\medio\video里的虛擬視頻驅動程序vivi.c(這段代碼使用v4l2 api模擬真實的視頻設備)來分析V4L2的框架。它的總體框架如下所示:
vivi_initvivi_create_instancev4l2_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_devicevdev->cdev = cdev_alloc();vdev->cdev->ops = &v4l2_fops;cdev_addvideo_device[vdev->minor] = vdev;if (vdev->ctrl_handler == NULL)vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;
①我們從vivi.c里的vivi_init函數入手發現它調用了v4l2_device_register,該函數用于初始化一些東西,比如自旋鎖、引用計數,這個并不是必需的;②調用了video_device_alloc分配video_device結構體并對其進行相應的設置,例如
.fops? ? ? ? ? ? ?= &vivi_fops,
.ioctl_ops ?? ?= &vivi_ioctl_ops,
.release? ? ? ?= video_device_release,
等設置,然后video_register_device注冊該結構體;
③video_register_device函數調用了__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;
?
?
上圖是vivi_create_instance函數的一部分,首先分配一個video_device結構體的變量vfd,然后設置*vfd = vivi_template;其中vivi_template是一個video_device的結構體變量,它本身設置好了一些如.fops之類信息(如下圖),此操作便相當于設置
?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,
};
然后進入video_register_device函數,下面是video_register_device里的一部分源碼,首先分配一個cdev結構體
然后設置cdev->ops = &v4l2_fops;v4l2_fops本身指向了一些函數(如下圖),這樣cdev便也指向了這些函數,當APP調用read函數時,便會調用cdev里面的read函數
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,
};
cdev里面的read函數如下圖,首先根據filp獲取到video_device結構體,然后判斷該video_device結構體里的read函數是否存在,若存在則調用它,所以最后便調用到了前面我們設置的vfd.fops里的read函數。
?
?
?
?
相比open、read函數,ioctl的調用過程更復雜一些,下面我們來看一下(我們以VIDIOC_QUERYCAP為例)。下圖是v4l2_fops里的.unlocked_ioctl指向的v4l2_ioctl函數。
它調用了前面vivi_template的fops里面的ioctl。
vivi_template的fops里面的ioctl里調用到下圖的__video_do_ioctl函數,該函數最終調用到vfd里的ioctl_ops成員里面的函數,即vivi_ioctl_ops里的函數
比如調用的是?VIDIOC_QUERYCAP,則最終會調用到下面的函數。
/* ------------------------------------------------------------------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;
}
?
?
總結:ioctl的調用比open、read多了一層,當APP調用ioctl函數時,便會調用cdev里面的ioctl函數,然后調用到了前面我們設置的vfd.fops里的ioctl函數,即和read、open函數同一結構體里的v4l2_ioctl,然后最終再去調用到和??
.fops ? ? ? ? ? = &vivi_fops,同一結構體里的
.ioctl_ops ?? ?= &vivi_ioctl_ops,里對應的函數。
?
?
相關文章:
https://blog.csdn.net/qingkongyeyue/article/details/53447331
https://blog.csdn.net/qingkongyeyue/article/details/52372960