V4L2 驱动层分析
一、Camera V4L2 驅(qū)動(dòng)層分析
Linux系統(tǒng)中視頻輸入設(shè)備主要包括以下四個(gè)部分:
1.字符設(shè)備驅(qū)動(dòng):V4L2本身就是一個(gè)字符設(shè)備,具有字符設(shè)備所有的特性,暴露接口給用戶空間;
2.V4L2驅(qū)動(dòng)核心:主要是構(gòu)建一個(gè)內(nèi)核中標(biāo)準(zhǔn)視頻設(shè)備驅(qū)動(dòng)的框架,為視頻操作提供統(tǒng)一的接口函數(shù);
3.平臺(tái)V4L2設(shè)備驅(qū)動(dòng):在V4L2框架下,根據(jù)平臺(tái)自身的特性實(shí)現(xiàn)與平臺(tái)相關(guān)的V4L2驅(qū)動(dòng)部分,包括注冊(cè)video_device和v4l2_dev;
4.具體的sensor驅(qū)動(dòng):主要上電、提供工作時(shí)鐘、視頻圖像裁剪、流IO開啟等,實(shí)現(xiàn)各種設(shè)備控制方法供上層調(diào)用并注冊(cè)v4l2_subdev。
V4L2核心源碼位于drivers/media/v4l2-core,根據(jù)功能可以劃分為四類:
1.字符設(shè)備模塊:由v4l2-dev.c實(shí)現(xiàn),主要作用申請(qǐng)字符主設(shè)備號(hào)、注冊(cè)class和提供video device注冊(cè)注銷等相關(guān)函數(shù)。
2.V4L2基礎(chǔ)框架:由v4l2-device.c、v4l2-subdev.c、v4l2-fh.c、v4l2-ctrls.c等文件構(gòu)建V4L2基礎(chǔ)框架。
3.videobuf管理
由videobuf2-core.c、videobuf2-dma-contig.c、videobuf2-dma-sg.c、videobuf2-memops.c、videobuf2-vmalloc.c、v4l2-mem2mem.c等文件實(shí)現(xiàn),完成videobuffer的分配、管理和注銷。
4.Ioctl框架:由v4l2-ioctl.c文件實(shí)現(xiàn),構(gòu)建V4L2ioctl的框架。
-
創(chuàng)建v4l2_device結(jié)構(gòu)體,填充信息,通過v4l2_device_register方法向系統(tǒng)注冊(cè)并且創(chuàng)建video設(shè)備節(jié)點(diǎn)。 //“kernel/msm-4.19/drivers/media/v4l2-core/v4l2-device.c”
-
創(chuàng)建media_device結(jié)構(gòu)體,填充信息,通過media_device_register向系統(tǒng)注冊(cè),并創(chuàng)建media設(shè)備節(jié)點(diǎn),并將其賦值給v4l2_device中的mdev。 //“kernel/msm-4.19/drivers/media/media-device.c”
-
創(chuàng)建v4l2_subdev結(jié)構(gòu)體,填充信息,通過v4l2_device_register_subdev向系統(tǒng)注冊(cè),并將其掛載到v4l2_device設(shè)備中 //“kernel/msm-4.19/drivers/media/v4l2-core/v4l2-device.c”
-
創(chuàng)建對(duì)應(yīng)的media_entity,并通過media_device_register_entity方法其添加到media controller中進(jìn)行統(tǒng)一管理。 //“kernel/msm-4.19/drivers/media/media-device.c”
二、V4L2基礎(chǔ)框架
2.1 /media/v4l2-core/v4l2-dev.c
在該文件中,主要是負(fù)責(zé)創(chuàng)建/sys/classs/video4linux目錄 ,當(dāng)有設(shè)備注冊(cè)進(jìn)來時(shí),創(chuàng)建對(duì)應(yīng)的 /dev/videox 、/dev/vbix、/dev/radiox、/dev/subdevx等節(jié)點(diǎn)。
主要工作如下:
1.將字符設(shè)備號(hào)(81,0)到(81,255)這期間256個(gè)字次設(shè)備號(hào),均申請(qǐng)為 v4l2 使用,name=video4linux
2.注冊(cè) /sys/classs/video4linux目錄
2.2 注冊(cè)V4L2設(shè)備 __video_register_device()
當(dāng)用設(shè)備需要注冊(cè)為 v4l2 subdev 時(shí),會(huì)調(diào)用video_register_device()函數(shù)進(jìn)行注冊(cè):
"kernel/msm-4.19/drivers/media/platform/msm/camera_v2/camera/camera.c" int camera_init_v4l2(struct device *dev, unsigned int *session) {struct msm_video_device *pvdev;struct v4l2_device *v4l2_dev = NULL;int rc = 0;pvdev = kzalloc(sizeof(struct msm_video_device),GFP_KERNEL);if (WARN_ON(!pvdev)) {rc = -ENOMEM;goto init_end;}pvdev->vdev = video_device_alloc(); //分配video_device內(nèi)存if (WARN_ON(!pvdev->vdev)) {rc = -ENOMEM;goto video_fail;}v4l2_dev = kzalloc(sizeof(struct v4l2_device), GFP_KERNEL); //分配v4l2_dev 內(nèi)存if (WARN_ON(!v4l2_dev)) {rc = -ENOMEM;goto v4l2_fail;}#if defined(CONFIG_MEDIA_CONTROLLER)v4l2_dev->mdev = kzalloc(sizeof(struct media_device), //分配media_device 內(nèi)存GFP_KERNEL);if (!v4l2_dev->mdev) {rc = -ENOMEM;goto mdev_fail;}media_device_init(v4l2_dev->mdev);strlcpy(v4l2_dev->mdev->model, MSM_CAMERA_NAME,sizeof(v4l2_dev->mdev->model)); //model 為msm_camerav4l2_dev->mdev->dev = dev;rc = media_device_register(v4l2_dev->mdev); //media_device 注冊(cè)if (WARN_ON(rc < 0))goto media_fail;rc = media_entity_pads_init(&pvdev->vdev->entity, 0, NULL); //建立media_entity與media_pad之間的鏈接:if (WARN_ON(rc < 0))goto entity_fail;pvdev->vdev->entity.function = QCAMERA_VNODE_GROUP_ID; #endifv4l2_dev->notify = NULL;pvdev->vdev->v4l2_dev = v4l2_dev;rc = v4l2_device_register(dev, pvdev->vdev->v4l2_dev); // 設(shè)置父設(shè)備為dev ,信息根據(jù)傳入?yún)?shù). 例如:dv4l2_dev->name =qcom,camera ca0c000.qcom,cci:qcom,cif (WARN_ON(rc < 0))goto register_fail;strlcpy(pvdev->vdev->name, "msm-sensor", sizeof(pvdev->vdev->name));pvdev->vdev->release = video_device_release;pvdev->vdev->fops = &camera_v4l2_fops; // 配置 video_device 的字符設(shè)備操作函數(shù)pvdev->vdev->ioctl_ops = &camera_v4l2_ioctl_ops; // 配置 v4l2 IOCTRLpvdev->vdev->minor = -1;pvdev->vdev->vfl_type = VFL_TYPE_GRABBER;pvdev->vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;rc = video_register_device(pvdev->vdev,VFL_TYPE_GRABBER, -1); 調(diào)用__video_register_device() kernel/msm-4.19/drivers/media/platform/msm/camera_v2/msm.c static int msm_probe(struct platform_device *pdev) {struct msm_video_device *pvdev = NULL;static struct dentry *cam_debugfs_root;int rc = 0;// 1. 初始化一個(gè) v4l2_device 類型的結(jié)構(gòu)體,并分配好結(jié)構(gòu)體內(nèi)存msm_v4l2_dev = kzalloc(sizeof(*msm_v4l2_dev),GFP_KERNEL);if (WARN_ON(!msm_v4l2_dev)) {rc = -ENOMEM;goto probe_end;}pvdev = kzalloc(sizeof(struct msm_video_device),GFP_KERNEL);if (WARN_ON(!pvdev)) {rc = -ENOMEM;goto pvdev_fail;}// 2. 分配 video_device 結(jié)構(gòu)體內(nèi)存pvdev->vdev = video_device_alloc();if (WARN_ON(!pvdev->vdev)) {rc = -ENOMEM;goto video_fail;}#if defined(CONFIG_MEDIA_CONTROLLER)// 3. 分配 media_device 結(jié)構(gòu)體內(nèi)存msm_v4l2_dev->mdev = kzalloc(sizeof(struct media_device),GFP_KERNEL);if (!msm_v4l2_dev->mdev) {rc = -ENOMEM;goto mdev_fail;}// 4.初始化 media_device 結(jié)構(gòu)體media_device_init(msm_v4l2_dev->mdev);strlcpy(msm_v4l2_dev->mdev->model, MSM_CONFIGURATION_NAME,sizeof(msm_v4l2_dev->mdev->model)); //MSM_CONFIGURATION_NAME = "msm_config" 代碼中open 節(jié)點(diǎn),會(huì)比較是否為smsm_configmsm_v4l2_dev->mdev->dev = &(pdev->dev);// 5. 注冊(cè) media_device , 使用的 v4l2 rc = media_device_register(msm_v4l2_dev->mdev); /** media_device_register()media_devnode_register () device_initialize() //初始化media 創(chuàng)建mediaXcdev_init() //初始化字符設(shè)備cdev_device_add() // device_create_file(&devnode->dev, &dev_attr_model) // 創(chuàng)建的節(jié)點(diǎn) sys/devices/platform/soc/ca00000.qcom,msm-cam/media0/model*/if (WARN_ON(rc < 0))goto media_fail;if (WARN_ON((rc == media_entity_pads_init(&pvdev->vdev->entity,0, NULL)) < 0))goto entity_fail;pvdev->vdev->entity.function = QCAMERA_VNODE_GROUP_ID; #endifmsm_v4l2_dev->notify = msm_sd_notify;pvdev->vdev->v4l2_dev = msm_v4l2_dev;// 6. 設(shè)置父設(shè)備為 pdev->dev (也就是 qcom,msm-cam 的設(shè)備信息)rc = v4l2_device_register(&(pdev->dev), pvdev->vdev->v4l2_dev); /** int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev) {if (v4l2_dev == NULL)return -EINVAL;INIT_LIST_HEAD(&v4l2_dev->subdevs);spin_lock_init(&v4l2_dev->lock);v4l2_prio_init(&v4l2_dev->prio);kref_init(&v4l2_dev->ref);get_device(dev);v4l2_dev->dev = dev;if (dev == NULL) {/* If dev == NULL, then name must be filled in by the caller */if (WARN_ON(!v4l2_dev->name[0]))return -EINVAL;return 0;}/* Set name to driver name + device name if it is empty. */if (!v4l2_dev->name[0])snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s",dev->driver->name, dev_name(dev));printk("v4l2_dev->name =%s \n",v4l2_dev->name); //log 信息v4l2_dev->name =msm ca00000.qcom,msm-cam . dev->driver->name 驅(qū)動(dòng)中設(shè)置的名字,dev_name(dev) dtsi 中的lableif (!dev_get_drvdata(dev))dev_set_drvdata(dev, v4l2_dev);return 0; }*/if (WARN_ON(rc < 0))goto register_fail;// 7. 注冊(cè) video_device設(shè)備 strlcpy(pvdev->vdev->name, "msm-config", sizeof(pvdev->vdev->name));pvdev->vdev->release = video_device_release;pvdev->vdev->fops = &msm_fops;pvdev->vdev->ioctl_ops = &g_msm_ioctl_ops;pvdev->vdev->minor = -1;pvdev->vdev->vfl_type = VFL_TYPE_GRABBER;rc = video_register_device(pvdev->vdev,VFL_TYPE_GRABBER, -1); // 節(jié)點(diǎn) /dev/vdieoX2.3 __video_register_device
以"qcom,msm-cam"為例,其注冊(cè)時(shí),傳遞的 nr = -1,說明從第一個(gè)開始分配,也就是 /dev/video0。
但是如果有其他先執(zhí)行video_register_device . /dev/video0 可以是其他值. 可以查看節(jié)點(diǎn)
/sys/class/video4linux # cat video0/name
sde_rotator
因?yàn)?#34;platform/msm/sde/rotator/sde_rotator_dev.c" 先執(zhí)行,傳入的nr = -1, so /dev/video0 為sde_rotator
2.3.1 字符設(shè)備操作函數(shù) v4l2_fops
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, };創(chuàng)建成功 /dev/video0 節(jié)點(diǎn)后,后續(xù)要打開對(duì)應(yīng)的節(jié)點(diǎn)時(shí),會(huì)調(diào)用 fops對(duì)應(yīng)的操作函數(shù),對(duì)應(yīng)的代碼在注冊(cè)時(shí)賦值的。
"kernel/msm-4.19/drivers/media/platform/msm/camera_v2/msm.c" strlcpy(pvdev->vdev->name, "msm-config", sizeof(pvdev->vdev->name)); pvdev->vdev->release = video_device_release; pvdev->vdev->fops = &msm_fops; pvdev->vdev->ioctl_ops = &g_msm_ioctl_ops;static struct v4l2_file_operations msm_fops = {.owner = THIS_MODULE,.open = msm_open,.poll = msm_poll,.release = msm_close,.unlocked_ioctl = video_ioctl2, #ifdef CONFIG_COMPAT.compat_ioctl32 = video_ioctl2, #endif }; "kernel/msm-4.19/drivers/media/platform/msm/camera_v2/camera/camera.c" strlcpy(pvdev->vdev->name, "msm-sensor", sizeof(pvdev->vdev->name)); pvdev->vdev->release = video_device_release; pvdev->vdev->fops = &camera_v4l2_fops; pvdev->vdev->ioctl_ops = &camera_v4l2_ioctl_ops;static struct v4l2_file_operations camera_v4l2_fops = {.owner = THIS_MODULE,.open = camera_v4l2_open,.poll = camera_v4l2_poll,.release = camera_v4l2_close,.unlocked_ioctl = video_ioctl2, #ifdef CONFIG_COMPAT.compat_ioctl32 = camera_v4l2_compat_ioctl, #endif };log 信息
02-11 06:44:53.201 0 0 W : v4l2_open 02-11 06:44:53.201 0 0 W : msm_open02-11 06:44:54.756 0 0 W : v4l2_open 02-11 06:44:54.756 0 0 W : camera_v4l2_open根據(jù)open 不同的節(jié)點(diǎn)調(diào)用不同的v4l2_file_operations2.3.2 v4l2_ioctrl
kernel/msm-4.4/drivers/media/v4l2-core/v4l2-compat-ioctl32.c //iotctrl 同理open ,有默認(rèn)的v4l2 的 ioctrl , 有對(duì)應(yīng)驅(qū)動(dòng)的ioctrl long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) {if (_IOC_TYPE(cmd) == 'V' && _IOC_NR(cmd) < BASE_VIDIOC_PRIVATE)ret = do_video_ioctl(file, cmd, arg);else if (vdev->fops->compat_ioctl32)ret = vdev->fops->compat_ioctl32(file, cmd, arg);return ret; }2.4 注冊(cè)子設(shè)備 /media/v4l2-core/v4l2-subdev.c
當(dāng)有sub-dev 需要注冊(cè)到v4l2 時(shí),調(diào)用 v4l2_device_register_subdev()函數(shù)。
最終調(diào)用 __video_register_device(),傳遞參數(shù) VFL_TYPE_SUBDEV,說明是注冊(cè) sub_dev 設(shè)備。
參考:https://blog.csdn.net/Ciellee/article/details/105483079
總結(jié)
以上是生活随笔為你收集整理的V4L2 驱动层分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 史上最强模型 GPT-4 上线:一张手绘
- 下一篇: 虚拟机分配ip地址