高通msm-V4L2-Camera驱动浅析1-初识

系列文章

高通msm-V4L2-Camera驱动浅析1-初识
高通msm-V4L2-Camera驱动浅析2-框架详解
高通msm-V4L2-Camera驱动浅析3-session
高通msm-V4L2-Camera驱动浅析4-stream
高通msm-V4L2-Camera驱动浅析5-buffer

前言

V4L2是 linux 设备设计的一套视频框架,网上已经有很多博文可以研究学习。
V4L2源码路径:kernel/msm-4.9/drivers/media/v4l2-core
阅读本文之前,需要对V4L2有一定的了解。

推荐一些不错的V4L2文章
0.V4L2_htjacky的专栏-CSDN博客
1.v4l2的学习建议和流程解析 - silenceer - 博客园 (cnblogs.com)
2.linux内核之 V4L2框架分析_zhc的博客-CSDN博客_v4l2 框架
3.深入学习Linux摄像头(一)v4l2应用编程_JT同学的博客-CSDN博客_v4l2应用编程
4.linux-V4L2系列博客
5.深入理解Android相机体系结构之七_u012596975的博客-CSDN博客

本文希望从一个高通开发者的角度,去研究如何使用V4L2架构实现高通的camera功能。
如果你是高通开发者,你会怎么写代码去实现呢?

一、V4L2架构和一些关键结构体

关键结构体

  • v4l2_device:用来描述一个v4l2设备实例,可以包含多个子设备,对应的是例如 I2C、CSI、MIPI 等设备,它们是从属于一个 V4L2 device 之下的。
  • v4l2_subdev:用来初始化和控制子设备的方法
  • video_device:创建设备节点/dev/videoX,用户空间和内核空间沟通的桥梁
  • videobuf2:视频缓冲的处理
  • v4l2_fh:文件访问控制
  • v4l2_ctrl_handler:控制模块,提供子设备(主要是 video 和 ISP 设备)在用户空间的特效操作接口
  • media_device:用于运行时数据流的管理,嵌入在 V4L2 device 内部

**video_device、v4l2_device和v4l2_subdev的关系


二、Camera应该挂在什么总线上?

Linux的设备和驱动通常都要挂在一种总线上,如I2C总线,USB总线等,但在Soc系统中集成的独立外设控制器是不依附于此类总线的,为了统一性,Linux发明了一种虚拟总线——platform总线,相应的设备就是platform_device,驱动就是platform_driver。


Camera也属于一颗集成的Soc,它有很多子设备:sensor感光芯片,eeprom,flash等,通过MIPI传输图像数据,通过I2C控制sensor的行为。
因此,Camera 设备属于platform_device,挂在platform总线上。

假设我是高通的开发者,那我是不是应该写一个platform_device,一个platform_driver,然后调用platform_driver_register去初始化?

果不其然,让我们来看看源码:

  • platform_device

kernel/msm-4.9/arch/arm64/boot/dts/qcom/msm8937-camera.dtsi

&soc {
    qcom,msm-cam@1b00000 {
        compatible = "qcom,msm-cam";
        reg = <0x1b00000 0x40000>;
        reg-names = "msm-cam";
        status = "ok";
        bus-vectors = "suspend", "svs", "nominal", "turbo";
        qcom,bus-votes = <0 160000000 320000000 320000000>;
    };
}
  • platform_driver
    kernel/msm-4.9/drivers/media/platform/msm/camera_v2/msm.c
static const struct of_device_id msm_dt_match[] = {
    {.compatible = "qcom,msm-cam"},
    {}
};
MODULE_DEVICE_TABLE(of, msm_dt_match);

static struct platform_driver msm_driver = {
    .probe = msm_probe,
    .driver = {
        .name = "msm",
        .owner = THIS_MODULE,
        .of_match_table = msm_dt_match,
    },
};
  • platform_driver_register
static int __init msm_init(void)
{
    return platform_driver_register(&msm_driver);
}

static void __exit msm_exit(void)
{
    platform_driver_unregister(&msm_driver);
}
module_init(msm_init);
module_exit(msm_exit);
MODULE_DESCRIPTION("MSM V4L2 Camera");
MODULE_LICENSE("GPL v2");

注册成功了,就可以在sys/bus/platform找到相应的设备和驱动:


platform总线会调用match函数去匹配驱动和设备,一旦匹配成功,就会调用probe函数。
一般compatible = "qcom,msm-cam"节点匹配设备和驱动。

匹配成功了,就会调用msm_probe函数去做实现一些功能。

作为开发者,我应该在msm_probe实现什么功能呢?platform只是虚拟的,我们还是要回归现实。

现实中,camera是用来拍照,录视频的,
那么我是不是应该把camera定义为一个v4l2_device实例
子设备比如sensor,eeprom我就定义为v4l2_subdev,归v4l2_device统一管理;

同时一个 V4L2 device 下属可能有非常多同类型的子设备(两个或者多个 sensor、ISP 等),
那么在设备运行的时候我怎么知道我的数据流需要用到哪一个类型的哪一个子设备呢。
这个时候就轮到 media_device出手了,它为这一坨的子设备建立一条虚拟的连线,建立起来一个运行时的 pipeline(管道),并且可以在运行时动态改变、管理接入的设备。

另外,如果内核空间要和用户空间沟通,我们还应该初始化 video_device

msm_probe函数应该完成v4l2_device,media_device,video_device等相关结构体的初始化工作

三、MSM V4L2 Camera驱动的初始化

3.1 一些V4L2相关变量

kernel/msm-4.9/drivers/media/platform/msm/camera_v2/msm.c

//v4l2_device实例
static struct v4l2_device *msm_v4l2_dev;

static struct v4l2_fh  *msm_eventq;

struct msm_video_device {
    struct video_device *vdev;
    atomic_t opened;
    struct mutex video_drvdata_mutex;
};
struct msm_video_device *pvdev = NULL;

3.2 在msm_probe进行初始化

  • msm_probe
static int msm_probe(struct platform_device *pdev)
{
···省略部分源码
    struct msm_video_device *pvdev = NULL;
    int rc = 0;

    //为结构体v4l2_device申请内存
    msm_v4l2_dev = kzalloc(sizeof(*msm_v4l2_dev),GFP_KERNEL);
    //为结构体msm_video_device申请内存,里面包含video_device结构体
    pvdev = kzalloc(sizeof(struct msm_video_device),GFP_KERNEL);
    //为video_device申请内存
    pvdev->vdev = video_device_alloc();

#if defined(CONFIG_MEDIA_CONTROLLER)
    //为media_device申请内存
    msm_v4l2_dev->mdev = kzalloc(sizeof(struct media_device),GFP_KERNEL);
    //初始化media_device
    media_device_init(msm_v4l2_dev->mdev);
    //这里 MSM_CONFIGURATION_NAME "msm_config"
    strlcpy(msm_v4l2_dev->mdev->model, MSM_CONFIGURATION_NAME,
            sizeof(msm_v4l2_dev->mdev->model));
    //记录父设备
    msm_v4l2_dev->mdev->dev = &(pdev->dev);
    //注册一个媒体设备元素
    rc = media_device_register(msm_v4l2_dev->mdev);

    //初始化硬件设备端口pad
    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;
#endif
    //注册notify回调函数,通常用于子设备传递事件,这些事件可以是自定义事件
    msm_v4l2_dev->notify = msm_sd_notify;
    //video_device中记录msm_v4l2_dev
    pvdev->vdev->v4l2_dev = msm_v4l2_dev;
    //注册一个v4l2_device实例
    rc = v4l2_device_register(&(pdev->dev), pvdev->vdev->v4l2_dev);
    //v4l2_device实例名称为"msm-config"
    strlcpy(pvdev->vdev->name, "msm-config", sizeof(pvdev->vdev->name));
    //释放函数
    pvdev->vdev->release  = video_device_release;
    //设置v4l2_file_operations
    pvdev->vdev->fops     = &msm_fops;
    //设置ioctl_ops:可以通过设备节点被用户空间程序访问
    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);
    //初始设备的打开次数为0
    atomic_set(&pvdev->opened, 0);
    //保存msm_video_device指针信息
    video_set_drvdata(pvdev->vdev, pvdev);
    //申请内存
    msm_session_q = kzalloc(sizeof(*msm_session_q), GFP_KERNEL);
    //其他一些相关初始化,如自旋锁,等待队列等待
    msm_init_queue(msm_session_q);
    spin_lock_init(&msm_eventq_lock);
    spin_lock_init(&msm_pid_lock);
    mutex_init(&ordered_sd_mtx);
    mutex_init(&v4l2_event_mtx);
    INIT_LIST_HEAD(&ordered_sd_list);

    return rc;
}
  • 1.为相关结构体分配内存
    msm_v4l2_dev = kzalloc(sizeof(*msm_v4l2_dev),GFP_KERNEL);
    pvdev = kzalloc(sizeof(struct msm_video_device),GFP_KERNEL);
    pvdev->vdev = video_device_alloc();

  • 2. 注册一个媒体设备
    media_device_register(msm_v4l2_dev->mdev);

    media_device结构体有一个model成员,代表的是:设备型号名称
    这里我们注册的媒体设备名称为:msm_config

    注册完成之后,可以在sys/bus/media/devices/下看到一个media0节点

  • 3. 注册一个v4l2_device实例
    v4l2_device_register(&(pdev->dev), pvdev->vdev->v4l2_dev);
    该函数将会初始化v4l2_device结构体,主要做了如下工作:

    • 初始化管理子设备的双向链表subdevs,所有注册到的子设备都需要加入到这个链表当中
    • 初始化自旋锁
    • 初始引用计数
  • 4.注册一个视频设备:video_device
    video_register_device(pvdev->vdev,VFL_TYPE_GRABBER, -1);
    该函数将会初始化video_device结构体,主要做了如下工作:

    • 1.检查设备类型
    • 2.查找空闲的次要节点、设备节点号和设备索引
    • 3.初始化字符设备(主设备号为81,次设备号从0开始)
    • 4.向sysfs注册设备
类型(type) 名称(name)
VFL_TYPE_GRABBER video
VFL_TYPE_VBI vbi
VFL_TYPE_RADIO radio
VFL_TYPE_SUBDEV v4l-subdev
VFL_TYPE_SDR swradio
VFL_TYPE_TOUCH v4l-touch

ps: GRABBER在英文中的意思是掠夺者,video grabber 即 视频捕获器

video_register_device本质上是一个字符设备,注册完成后,通过如下指令可以查到:

另外,在sys/class/video4linux/下可以找到我们的设备节点。

我们注册的设备类型为VFL_TYPE_GRABBER,对应的节点名称video
第一个注册的设备从0开始,以此类推,因此节点名称为video0
视频设备节点video0的name为:msm-config
视频设备节点video0的主设备号和次设备号为:81:2

同时 udev文件系统会为我们在dev/目录下创建一个video0节点,即dev/video0


用户可以打开dev/video0节点,通过IOCTL命令和内核空间进行通信。

四、总结

  • camera是一个集成soc,camera设备挂在platform总线上,属于platform_device,相应的驱动是platform_driver.
  • msm_probe函数完成了v4l2_device,media_device,video_device等相关结构体的初始化工作

本文只写了msm v4l2驱动是怎么进行初始化的,一个V4L2驱动应该还要提供其他的功能,例如:

  • 数据流的创建和删除:msm_create_stream,msm_delete_stream
  • 会话的创建和删除:msm_create_session,msm_destroy_session
  • 事件分发:msm_post_event等等

Stay hungry,Stay foolish!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容