V4L2框架分析
? ? ? ?V4L2是Video for linux2的簡稱,為linux中關(guān)于視頻設(shè)備的內(nèi)核驅(qū)動。v4L2是針對uvc(USB Video Class)免驅(qū)usb設(shè)備的編程框架,主要用于采集usb攝像頭等。
? ? ? 下圖是V4L2的框架,首先系統(tǒng)核心層分配設(shè)置注冊一個名為cdev結(jié)構(gòu)體變量(cdev結(jié)構(gòu)體是video_device結(jié)構(gòu)體里的一部分),并設(shè)置cdev->ops = v4l2_fops;在硬件層我們分配設(shè)置注冊了一個名為vfd結(jié)構(gòu)體變量(video_device結(jié)構(gòu)體),并設(shè)置vfd->fops = &vivi_fops,vfd->ioctl_ops ?= &vivi_ioctl_ops;當(dāng)應(yīng)用程序(APP)調(diào)用read、open等函數(shù)時,會調(diào)用到v4l2_fops里的read、open函數(shù),然后v4l2_fops里的read、open函數(shù)會再調(diào)用到硬件層相關(guān)的vfd->fops里的read、open函數(shù)。ioctl函數(shù)也類似。
? ? ? ? 下面我們從程序入手來分析V4L2的框架,本文借助Linux內(nèi)核目錄下的drivers\medio\video里的虛擬視頻驅(qū)動程序vivi.c(這段代碼使用v4l2 api模擬真實的視頻設(shè)備)來分析V4L2的框架。它的總體框架如下所示:
vivi_initvivi_create_instancev4l2_device_register // 不是主要, 只是用于初始化一些東西,比如自旋鎖、引用計數(shù)video_device_alloc// 設(shè)置1. vfd:.fops = &vivi_fops,.ioctl_ops = &vivi_ioctl_ops,.release = video_device_release,2.vfd->v4l2_dev = &dev->v4l2_dev;3. 設(shè)置"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函數(shù)入手發(fā)現(xiàn)它調(diào)用了v4l2_device_register,該函數(shù)用于初始化一些東西,比如自旋鎖、引用計數(shù),這個并不是必需的;②調(diào)用了video_device_alloc分配video_device結(jié)構(gòu)體并對其進行相應(yīng)的設(shè)置,例如
.fops? ? ? ? ? ? ?= &vivi_fops,
.ioctl_ops ?? ?= &vivi_ioctl_ops,
.release? ? ? ?= video_device_release,
等設(shè)置,然后video_register_device注冊該結(jié)構(gòu)體;
③video_register_device函數(shù)調(diào)用了__video_register_device實現(xiàn)了如下操作:
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函數(shù)的一部分,首先分配一個video_device結(jié)構(gòu)體的變量vfd,然后設(shè)置*vfd = vivi_template;其中vivi_template是一個video_device的結(jié)構(gòu)體變量,它本身設(shè)置好了一些如.fops之類信息(如下圖),此操作便相當(dāng)于設(shè)置
?1. vfd:
.fops? ? ? ? ? ? ?= &vivi_fops,
.ioctl_ops ?? ?= &vivi_ioctl_ops,
.release? ? ? ?= video_device_release,
然后進入video_register_device函數(shù),下面是video_register_device里的一部分源碼,首先分配一個cdev結(jié)構(gòu)體
然后設(shè)置cdev->ops = &v4l2_fops;v4l2_fops本身指向了一些函數(shù)(如下圖),這樣cdev便也指向了這些函數(shù),當(dāng)APP調(diào)用read函數(shù)時,便會調(diào)用cdev里面的read函數(shù)
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函數(shù)如下圖,首先根據(jù)filp獲取到video_device結(jié)構(gòu)體,然后判斷該video_device結(jié)構(gòu)體里的read函數(shù)是否存在,若存在則調(diào)用它,所以最后便調(diào)用到了前面我們設(shè)置的vfd.fops里的read函數(shù)。
?
?
?
?
相比open、read函數(shù),ioctl的調(diào)用過程更復(fù)雜一些,下面我們來看一下(我們以VIDIOC_QUERYCAP為例)。下圖是v4l2_fops里的.unlocked_ioctl指向的v4l2_ioctl函數(shù)。
它調(diào)用了前面vivi_template的fops里面的ioctl。
vivi_template的fops里面的ioctl里調(diào)用到下圖的__video_do_ioctl函數(shù),該函數(shù)最終調(diào)用到vfd里的ioctl_ops成員里面的函數(shù),即vivi_ioctl_ops里的函數(shù)
比如調(diào)用的是?VIDIOC_QUERYCAP,則最終會調(diào)用到下面的函數(shù)。
/* ------------------------------------------------------------------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; }?
?
總結(jié):ioctl的調(diào)用比open、read多了一層,當(dāng)APP調(diào)用ioctl函數(shù)時,便會調(diào)用cdev里面的ioctl函數(shù),然后調(diào)用到了前面我們設(shè)置的vfd.fops里的ioctl函數(shù),即和read、open函數(shù)同一結(jié)構(gòu)體里的v4l2_ioctl,然后最終再去調(diào)用到和??
.fops ? ? ? ? ? = &vivi_fops,同一結(jié)構(gòu)體里的
.ioctl_ops ?? ?= &vivi_ioctl_ops,里對應(yīng)的函數(shù)。
?
?
相關(guān)文章:
https://blog.csdn.net/qingkongyeyue/article/details/53447331
https://blog.csdn.net/qingkongyeyue/article/details/52372960
總結(jié)
- 上一篇: 输卵管积液的表现症状
- 下一篇: 成都欢乐谷三岁小朋友要买票吗