系列文章
高通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 設備設計的一套視頻框架纱昧,網(wǎng)上已經(jīng)有很多博文可以研究學習埃脏。
V4L2源碼路徑:kernel/msm-4.9/drivers/media/v4l2-core
閱讀本文之前煌张,需要對V4L2有一定的了解。
推薦一些不錯的V4L2文章
0.V4L2_htjacky的專欄-CSDN博客
1.v4l2的學習建議和流程解析 - silenceer - 博客園 (cnblogs.com)
2.linux內(nèi)核之 V4L2框架分析_zhc的博客-CSDN博客_v4l2 框架
3.深入學習Linux攝像頭(一)v4l2應用編程_JT同學的博客-CSDN博客_v4l2應用編程
4.linux-V4L2系列博客
5.深入理解Android相機體系結構之七_u012596975的博客-CSDN博客
本文希望從一個高通開發(fā)者的角度虏劲,去研究如何使用V4L2架構實現(xiàn)高通的camera功能。
如果你是高通開發(fā)者团秽,你會怎么寫代碼去實現(xiàn)呢疯趟?
一、V4L2架構和一些關鍵結構體
關鍵結構體
- v4l2_device:用來描述一個v4l2設備實例锡溯,可以包含多個子設備赶舆,對應的是例如 I2C、CSI祭饭、MIPI 等設備芜茵,它們是從屬于一個 V4L2 device 之下的。
- v4l2_subdev:用來初始化和控制子設備的方法
- video_device:創(chuàng)建設備節(jié)點/dev/videoX甜癞,用戶空間和內(nèi)核空間溝通的橋梁
- videobuf2:視頻緩沖的處理
- v4l2_fh:文件訪問控制
- v4l2_ctrl_handler:控制模塊夕晓,提供子設備(主要是 video 和 ISP 設備)在用戶空間的特效操作接口
- media_device:用于運行時數(shù)據(jù)流的管理,嵌入在 V4L2 device 內(nèi)部
**video_device悠咱、v4l2_device和v4l2_subdev的關系
二蒸辆、Camera應該掛在什么總線上征炼?
Linux的設備和驅動通常都要掛在一種總線上,如I2C總線躬贡,USB總線等谆奥,但在Soc系統(tǒng)中集成的獨立外設控制器是不依附于此類總線的,為了統(tǒng)一性拂玻,Linux發(fā)明了一種虛擬總線——platform總線酸些,相應的設備就是platform_device,驅動就是platform_driver。
Camera也屬于一顆集成的Soc檐蚜,它有很多子設備:sensor感光芯片魄懂,eeprom,flash等闯第,通過MIPI傳輸圖像數(shù)據(jù)市栗,通過I2C控制sensor的行為。
因此咳短,Camera 設備屬于platform_device,掛在platform總線上填帽。
假設我是高通的開發(fā)者,那我是不是應該寫一個platform_device咙好,一個platform_driver篡腌,然后調(diào)用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總線會調(diào)用match函數(shù)去匹配驅動和設備,一旦匹配成功葵第,就會調(diào)用probe函數(shù)绘迁。
一般compatible = "qcom,msm-cam"節(jié)點匹配設備和驅動。
匹配成功了卒密,就會調(diào)用msm_probe函數(shù)去做實現(xiàn)一些功能缀台。
作為開發(fā)者,我應該在msm_probe實現(xiàn)什么功能呢哮奇?platform只是虛擬的膛腐,我們還是要回歸現(xiàn)實。
現(xiàn)實中鼎俘,camera是用來拍照哲身,錄視頻的,
那么我是不是應該把camera定義為一個v4l2_device實例贸伐,
子設備比如sensor勘天,eeprom我就定義為v4l2_subdev,歸v4l2_device統(tǒng)一管理;
同時一個 V4L2 device 下屬可能有非常多同類型的子設備(兩個或者多個 sensor脯丝、ISP 等)商膊,
那么在設備運行的時候我怎么知道我的數(shù)據(jù)流需要用到哪一個類型的哪一個子設備呢。
這個時候就輪到 media_device出手了宠进,它為這一坨的子設備建立一條虛擬的連線晕拆,建立起來一個運行時的 pipeline(管道),并且可以在運行時動態(tài)改變材蹬、管理接入的設備实幕。
另外,如果內(nèi)核空間要和用戶空間溝通堤器,我們還應該初始化 video_device
msm_probe函數(shù)應該完成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申請內(nèi)存
msm_v4l2_dev = kzalloc(sizeof(*msm_v4l2_dev),GFP_KERNEL);
//為結構體msm_video_device申請內(nèi)存吼旧,里面包含video_device結構體
pvdev = kzalloc(sizeof(struct msm_video_device),GFP_KERNEL);
//為video_device申請內(nèi)存
pvdev->vdev = video_device_alloc();
#if defined(CONFIG_MEDIA_CONTROLLER)
//為media_device申請內(nèi)存
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回調(diào)函數(shù)凰锡,通常用于子設備傳遞事件,這些事件可以是自定義事件
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));
//釋放函數(shù)
pvdev->vdev->release = video_device_release;
//設置v4l2_file_operations
pvdev->vdev->fops = &msm_fops;
//設置ioctl_ops:可以通過設備節(jié)點被用戶空間程序訪問
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);
//初始設備的打開次數(shù)為0
atomic_set(&pvdev->opened, 0);
//保存msm_video_device指針信息
video_set_drvdata(pvdev->vdev, pvdev);
//申請內(nèi)存
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.為相關結構體分配內(nèi)存
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節(jié)點
-
3. 注冊一個v4l2_device實例
v4l2_device_register(&(pdev->dev), pvdev->vdev->v4l2_dev);
該函數(shù)將會初始化v4l2_device結構體,主要做了如下工作:- 初始化管理子設備的雙向鏈表subdevs昼扛,所有注冊到的子設備都需要加入到這個鏈表當中
- 初始化自旋鎖
- 初始引用計數(shù)
-
4.注冊一個視頻設備:video_device
video_register_device(pvdev->vdev,VFL_TYPE_GRABBER, -1);
該函數(shù)將會初始化video_device結構體寸齐,主要做了如下工作:- 1.檢查設備類型
- 2.查找空閑的次要節(jié)點、設備節(jié)點號和設備索引
- 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/下可以找到我們的設備節(jié)點毅厚。
我們注冊的設備類型為VFL_TYPE_GRABBER,對應的節(jié)點名稱video
第一個注冊的設備從0開始浦箱,以此類推吸耿,因此節(jié)點名稱為video0
視頻設備節(jié)點video0的name為:msm-config
視頻設備節(jié)點video0的主設備號和次設備號為:81:2
同時 udev文件系統(tǒng)會為我們在dev/目錄下創(chuàng)建一個video0節(jié)點,即dev/video0
用戶可以打開dev/video0節(jié)點酷窥,通過IOCTL命令和內(nèi)核空間進行通信咽安。
四、總結
- camera是一個集成soc蓬推,camera設備掛在platform總線上妆棒,屬于platform_device,相應的驅動是platform_driver.
- msm_probe函數(shù)完成了v4l2_device,media_device,video_device等相關結構體的初始化工作
本文只寫了msm v4l2驅動是怎么進行初始化的糕珊,一個V4L2驅動應該還要提供其他的功能动分,例如:
- 數(shù)據(jù)流的創(chuàng)建和刪除:msm_create_stream,msm_delete_stream
- 會話的創(chuàng)建和刪除:msm_create_session放接,msm_destroy_session
- 事件分發(fā):msm_post_event等等
Stay hungry刺啦,Stay foolish!