加入星計劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴散
  • 作品版權(quán)保護
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
    • 一、V4L2架構(gòu)
    • 二、V4L2架構(gòu)包括哪些設(shè)備
    • 三、Linux內(nèi)核中V4L2驅(qū)動代碼
    • 四、結(jié)構(gòu)體詳解
    • 五、 video_device、v4l2_device和v4l2_subdev的關(guān)系舉例
    • 六、videobuf2
    • 七、v4l2拓撲結(jié)構(gòu)
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請入駐 產(chǎn)業(yè)圖譜

Camera | 5.Linux v4l2架構(gòu)(基于rk3568)

2023/03/02
3466
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論
瑞芯微 | 攝像頭ov13850移植筆記

Camera | 1.Camera基礎(chǔ)知識?

Camera | 2.MIPI、CSI基礎(chǔ)

Camera | 3.瑞芯微平臺MIPI攝像頭常用調(diào)試命令

Camera | 4.瑞芯微平臺MIPI攝像頭應(yīng)用程序編寫

上一篇我們講解了如何編寫基于V4L2的應(yīng)用程序編寫,本文主要講解內(nèi)核中V4L2架構(gòu),以及一些最重要的結(jié)構(gòu)體、注冊函數(shù)。

廠家在實現(xiàn)自己的攝像頭控制器驅(qū)動時,總體上都遵循這個架構(gòu)來實現(xiàn),但是不同廠家、不同型號的SoC,具體的驅(qū)動實現(xiàn)仍然會有一些差別。

讀者可以通過本文了解各個結(jié)構(gòu)體與對應(yīng)的攝像頭模塊、SoC上控制器模塊、以及他們之間接口關(guān)系,并能夠了解這些硬件模塊與V4L2架構(gòu)之間關(guān)系。

下一張我們基于瑞芯微rk3568來詳細講解具體V4L2的實現(xiàn)。

一、V4L2架構(gòu)

V4L2子系統(tǒng)是Linux內(nèi)核中關(guān)于Video(視頻)設(shè)備的API接口,是V4L(Video for Linux)子系統(tǒng)的升級版本。

V4L(Video for Linux)是Linux內(nèi)核中關(guān)于視頻設(shè)備的API接口,出現(xiàn)于Linux內(nèi)核2.1版本,經(jīng)過修改bug和添加功能,Linux內(nèi)核2.5版本推出了V4L2(Video for Linux Two)子系統(tǒng),功能更多且更穩(wěn)定。

V4L2子系統(tǒng)向上為虛擬文件系統(tǒng)提供了統(tǒng)一的接口,應(yīng)用程序可通過虛擬文件系統(tǒng)訪問Video設(shè)備。

V4L2子系統(tǒng)向下給Video設(shè)備提供接口,同時管理所有Video設(shè)備。

二、V4L2架構(gòu)包括哪些設(shè)備

Video設(shè)備又分為主設(shè)備和從設(shè)備對于Camera來說,

主設(shè)備:
Camera Host控制器為主設(shè)備,負責圖像數(shù)據(jù)的接收和傳輸,

從設(shè)備:
從設(shè)備為Camera Sensor,一般為I2C接口,可通過從設(shè)備控制Camera采集圖像的行為,如圖像的大小、圖像的FPS等。

V4L2的主設(shè)備號是81,次設(shè)備號范圍0~255
這些次設(shè)備號又分為多類設(shè)備:

    視頻設(shè)備(次設(shè)備號范圍0-63)Radio(收音機)設(shè)備(次設(shè)備號范圍64-127)Teletext設(shè)備(次設(shè)備號范圍192-223)VBI設(shè)備(次設(shè)備號范圍224-255)。
    1. V4L2設(shè)備對應(yīng)的設(shè)備節(jié)點有

/dev/videoX、/dev/vbiX、/dev/radioX。本文只討論視頻設(shè)備,視頻設(shè)備對應(yīng)的設(shè)備節(jié)點是/dev/videoX

    ,視頻設(shè)備以高頻攝像頭或Camera為輸入源,Linux內(nèi)核驅(qū)動該類設(shè)備,接收相應(yīng)的視頻信息并處理。

V4L2框架的架構(gòu)如下圖所示:

user space:

應(yīng)用程序主要通過libv4l庫來操作攝像頭

Applications: 也可以基于字符設(shè)備/dev/videoX自己編寫應(yīng)用程序

guvcview:用于調(diào)試usb攝像頭(還有個軟件cheese也可以)

v4l2 utilities: v4l2 的工具集(參考前面第3篇文章)

kernel space:

ensor、ISP、VIPP、CSI、CCI都為從設(shè)備

從dphy物理層獲取視頻數(shù)據(jù)冊通過vb2子模塊

CCI :主要是通過GPIO(供電、片選)、I2C(下發(fā)配置命令給sensor)實現(xiàn)配置sensor

EHCI/OHCI:USB類型攝像頭

hardware

SIC Controller:從dphy獲取mipi協(xié)議幀
I2C Controller:與sensor的i2c block通信

GPIO Controller:sensor通常需要供電或者片選

external device
sensror:

像頭的接口主要有:USB,DVP.MIPI(CSI)

三、Linux內(nèi)核中V4L2驅(qū)動代碼

Linux系統(tǒng)中視頻輸入設(shè)備主要包括以下四個部分:

1.字符設(shè)備驅(qū)動:V4L2本身就是一個字符設(shè)備,具有字符設(shè)備所有的特性,暴露接口給用戶空間;

2.V4L2驅(qū)動核心:主要是構(gòu)建一個內(nèi)核中標準視頻設(shè)備驅(qū)動的框架,為視頻操作提供統(tǒng)一的接口函數(shù);

3.平臺V4L2設(shè)備驅(qū)動:在V4L2框架下,根據(jù)平臺自身的特性實現(xiàn)與平臺相關(guān)的V4L2驅(qū)動部分,包括注冊video_device和v4l2_device;

4.具體的sensor驅(qū)動:主要上電、提供工作時鐘、視頻圖像裁剪、流IO開啟等,實現(xiàn)各種設(shè)備控制方法供上層調(diào)用并注冊v4l2_subdev。

V4L2核心源碼位于drivers/media/v4l2-core,根據(jù)功能可以劃分為四類:

由上圖可知:

1.字符設(shè)備模塊:由v4l2-dev.c實現(xiàn),主要作用申請字符主設(shè)備號、注冊class和提供video device注冊注銷等相關(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等文件實現(xiàn),完成videobuffer的分配、管理和注銷。

4.Ioctl框架:由v4l2-ioctl.c文件實現(xiàn),構(gòu)建V4L2 ioctl的框架。

瑞芯微平臺還包括ISP的驅(qū)動框架,下面是rk3568對應(yīng)的ISP相關(guān)代碼:
Linux?Kernel-4.19
?|--?arch/arm/boot/dts?DTS配置文件
?|--?drivers/phy/rockchip
?|--?phy-rockchip-mipi-rx.c?mipi?dphy驅(qū)動
?|--?phy-rockchip-csi2-dphy-common.h
?|--?phy-rockchip-csi2-dphy-hw.c
?|--?phy-rockchip-csi2-dphy.c
?|--?drivers/media
??|--?v4l2-core
??|--?platform/rockchip/cif?RKCIF驅(qū)動
??|--?platform/rockchip/isp?RKISP驅(qū)動
???|--?dev.c?包含?probe、異步注冊、clock、pipeline、?iommu及media/v4l2?framework
???|--?capture_v21.c?包含?mp/sp/rawwr的配置及?vb2,幀中斷處理
???|--?dmarx.c?包含?rawrd的配置及?vb2,幀中斷處理
???|--?isp_params.c?3A相關(guān)參數(shù)設(shè)置
???|--?isp_stats.c?3A相關(guān)統(tǒng)計
???|--?isp_mipi_luma.c?mipi數(shù)據(jù)亮度統(tǒng)計
???|--?regs.c?寄存器相關(guān)的讀寫操作
???|--?rkisp.c?isp?subdev和entity注冊,包含從?mipi?接收數(shù)據(jù),并有?crop?功能
???|--?csi.c?csi?subdev和mipi配置
???|--?bridge.c?bridge?subdev,isp和ispp交互橋梁
??|--?platform/rockchip/ispp?rkispp驅(qū)動
???|--?dev.c?包含?probe、異步注冊、clock、pipeline、?iommu及media/v4l2?framework
???|--?stream.c?包含?4路video輸出的配置及?vb2,幀中斷處理
???|--?rkispp.c?ispp?subdev和entity注冊
???|--?params.c?TNR/NR/SHP/FEC/ORB參數(shù)設(shè)置
???|--?stats.c?ORB統(tǒng)計信息
??|--?i2c
???|--?ov13850.c?CIS(cmos?image?sensor)驅(qū)動

四、結(jié)構(gòu)體詳解

V4L2中有幾個最重要的幾個結(jié)構(gòu)體,v4l2_device、video_device、v4l2_subdev等。他們大致關(guān)系如下:

1.v4l2_device主設(shè)備

V4L2主設(shè)備實例使用struct v4l2_device結(jié)構(gòu)體表示,v4l2_device是V4L2子系統(tǒng)的入口,管理著V4L2子系統(tǒng)的主設(shè)備和從設(shè)備;

v4l2_device用來描述一個v4l2設(shè)備實例,可以包含多個子設(shè)備,對應(yīng)的是例如 I2C、CSI、MIPI 等設(shè)備,它們是從屬于一個 V4L2 device 之下的;

簡單設(shè)備可以僅分配這個結(jié)構(gòu)體,但在大多數(shù)情況下,都會將這個結(jié)構(gòu)體嵌入到一個更大的結(jié)構(gòu)體中以提供v4l2框架的功能,比如struct isp_device

需要與媒體框架整合的驅(qū)動必須手動設(shè)置dev->driver_data,指向包含v4l2_device結(jié)構(gòu)體實例的驅(qū)動特定設(shè)備結(jié)構(gòu)體。這可以在注冊V4L2設(shè)備實例前通過dev_set_drvdata()函數(shù)完成。

同時必須設(shè)置v4l2_device結(jié)構(gòu)體的mdev域,指向適當?shù)某跏蓟⒆赃^的media_device實例。

?[include/media/v4l2-device.h]
????struct?v4l2_device?{
????????struct?device?*dev;??//?父設(shè)備指針
????#if?defined(CONFIG_MEDIA_CONTROLLER)??//?多媒體設(shè)備配置選項
????????//?用于運行時數(shù)據(jù)流的管理,
????????struct?media_device?*mdev;
????#endif
????????//?注冊的子設(shè)備的v4l2_subdev結(jié)構(gòu)體都掛載此鏈表中
????????struct?list_head?subdevs;
????????//?同步用的自旋鎖
????????spinlock_t?lock;
????????//?獨一無二的設(shè)備名稱,默認使用driver?name?+?bus?ID
????????char?name[V4L2_DEVICE_NAME_SIZE];
????????//?被一些子設(shè)備回調(diào)的通知函數(shù),但這個設(shè)置與子設(shè)備相關(guān)。子設(shè)備支持的任何通知必須在
????????// include/media/<subdevice>.h 中定義一個消息頭。
????????void?(*notify)(struct?v4l2_subdev?*sd,?unsigned?int?notification,?void?*arg);
????????//?提供子設(shè)備(主要是video和ISP設(shè)備)在用戶空間的特效操作接口,
????????//?比如改變輸出圖像的亮度、對比度、飽和度等等
????????struct?v4l2_ctrl_handler?*ctrl_handler;
????????//?設(shè)備優(yōu)先級狀態(tài)
????????struct?v4l2_prio_state?prio;
????????/*?BKL?replacement?mutex.?Temporary?solution?only.?*/
????????struct?mutex?ioctl_lock;
????????//?struct?v4l2_device結(jié)構(gòu)體的引用計數(shù),等于0時才釋放
????????struct?kref?ref;
????????//?引用計數(shù)ref為0時,調(diào)用release函數(shù)進行釋放資源和清理工作
????????void?(*release)(struct?v4l2_device?*v4l2_dev);
????};

注冊函數(shù):

v4l2_device_register

使用v4l2_device_register注冊v4l2_device結(jié)構(gòu)體.如果v4l2_dev->name為空,則它將被設(shè)置為從dev中衍生出的值(為了更加精確,形式為驅(qū)動名后跟bus_id)。

如果在調(diào)用v4l2_device_register前已經(jīng)設(shè)置好了,則不會被修改。如果dev為NULL,則必須在調(diào)用v4l2_device_register前設(shè)置v4l2_dev->name。可以基于驅(qū)動名和驅(qū)動的全局atomic_t類型的實例編號,通過v4l2_device_set_name()設(shè)置name。

這樣會生成類似ivtv0、ivtv1等名字。若驅(qū)動名以數(shù)字結(jié)尾,則會在編號和驅(qū)動名間插入一個破折號,如:cx18-0、cx18-1等。

dev參數(shù)通常是一個指向pci_dev、usb_interface或platform_device的指針,很少使其為NULL,除非是一個ISA設(shè)備或者當一個設(shè)備創(chuàng)建了多個PCI設(shè)備,使得v4l2_dev無法與一個特定的父設(shè)備關(guān)聯(lián)。

使用v4l2_device_unregister卸載v4l2_device結(jié)構(gòu)體。如果dev->driver_data域指向 v4l2_dev,將會被重置為NULL。主設(shè)備注銷的同時也會自動注銷所有子設(shè)備。如果你有一個熱插拔設(shè)備(如USB設(shè)備),則當斷開發(fā)生時,父設(shè)備將無效。

由于v4l2_device有一個指向父設(shè)備的指針必須被清除,同時標志父設(shè)備已消失,所以必須調(diào)用v4l2_device_disconnect函數(shù)清理v4l2_device中指向父設(shè)備的dev指針。v4l2_device_disconnect并不注銷主設(shè)備,因此依然要調(diào)用v4l2_device_unregister函數(shù)注銷主設(shè)備。

[include/media/v4l2-device.h]
????//?注冊v4l2_device結(jié)構(gòu)體,并初始化v4l2_device結(jié)構(gòu)體
????//?dev-父設(shè)備結(jié)構(gòu)體指針,若為NULL,在注冊之前設(shè)備名稱name必須被設(shè)置,
????//?v4l2_dev-v4l2_device結(jié)構(gòu)體指針
????//?返回值-0成功,小于0-失敗
????int?v4l2_device_register(struct?device?*dev,?struct?v4l2_device?*v4l2_dev)

????//?卸載注冊的v4l2_device結(jié)構(gòu)體
????//?v4l2_dev-v4l2_device結(jié)構(gòu)體指針
????void?v4l2_device_unregister(struct?v4l2_device?*v4l2_dev)

????//?設(shè)置設(shè)備名稱,填充v4l2_device結(jié)構(gòu)體中的name成員
????//?v4l2_dev-v4l2_device結(jié)構(gòu)體指針
????//?basename-設(shè)備名稱基本字符串
????//?instance-設(shè)備計數(shù),調(diào)用v4l2_device_set_name后會自加1
????//?返回值-返回設(shè)備計數(shù)自加1的值
????int?v4l2_device_set_name(struct?v4l2_device?*v4l2_dev,?
????????????const?char?*basename,?atomic_t?*instance)

????//?熱插拔設(shè)備斷開時調(diào)用此函數(shù)
????//?v4l2_dev-v4l2_device結(jié)構(gòu)體指針
????void?v4l2_device_disconnect(struct?v4l2_device?*v4l2_dev);
同一個硬件的情況下。如ivtvfb驅(qū)動是一個使用ivtv硬件的幀緩沖驅(qū)動,同時alsa驅(qū)動也使用此硬件??梢允褂萌缦吕瘫闅v所有注冊的設(shè)備:
????static?int?callback(struct?device?*dev,?void?*p)
????{
????????struct?v4l2_device?*v4l2_dev?=?dev_get_drvdata(dev);

????????/*?測試這個設(shè)備是否已經(jīng)初始化?*/
????????if?(v4l2_dev?==?NULL)
????????????return?0;
????????...
????????return?0;
????}

????int?iterate(void?*p)
????{
????????struct?device_driver?*drv;
????????int?err;

????????/*?在PCI 總線上查找ivtv驅(qū)動。
??????? pci_bus_type是全局的. 對于USB總線使用usb_bus_type。?*/
????????drv?=?driver_find("ivtv",?&pci_bus_type);
????????/*?遍歷所有的ivtv設(shè)備實例?*/
????????err?=?driver_for_each_device(drv,?NULL,?p,?callback);
????????put_driver(drv);
????????return?err;
????}

2. video_device

V4L2子系統(tǒng)使用v4l2_device結(jié)構(gòu)體管理設(shè)備,設(shè)備的具體操作方法根據(jù)設(shè)備類型決定,

前面說過管理的設(shè)備分為很多種,

若是視頻設(shè)備,則需要注冊video_device結(jié)構(gòu)體,并提供相應(yīng)的操作方法。

對于視頻設(shè)備Camera而言,Camera控制器可以視為主設(shè)備,接在Camera控制器上的攝像頭可以視為從設(shè)備。

struct?video_device
{
?const?struct?v4l2_file_operations?*fops;
?struct?cdev?*cdev;?????//vdev->cdev->ops?=?&v4l2_fops;??字符設(shè)備描述符
?struct?v4l2_device?*v4l2_dev;
?struct?v4l2_ctrl_handler?*ctrl_handler;

?struct?vb2_queue?*queue;??//q->ops?=?&dmarx_vb2_ops;?buf操作真正驅(qū)動回調(diào)函數(shù)
??…………
?const?struct?v4l2_ioctl_ops?*ioctl_ops;//vdev->ioctl_ops?=?&rkisp_dmarx_ioctl;?
??…………
};

注冊函數(shù):

[rk_android11.0_sdk_220718kerneldriversmediav4l2-corev4l2-dev.c]


static?inline?int?__must_check?video_register_device(struct?video_device?*vdev,
????????int?type,?int?nr)
{
????return?__video_register_device(vdev,?type,?nr,?1,?vdev->fops->owner);
}
int?__video_register_device(struct?video_device?*vdev,?int?type,?int?nr,
????????int?warn_if_nr_in_use,?struct?module?*owner)
{
????····
????int?minor_cnt?=?VIDEO_NUM_DEVICES;//次設(shè)備個數(shù)默認為256
????const?char?*name_base;

????/*?A?minor?value?of?-1?marks?this?video?device?as?never
???????having?been?registered?*/
????vdev->minor?=?-1;

????/*?the?release?callback?MUST?be?present?如果之前沒有聲明銷毀函數(shù),則報錯*/
????if?(WARN_ON(!vdev->release))
????????return?-EINVAL;
????/*?the?v4l2_dev?pointer?MUST?be?present?如果之前未注冊v4l2_device則報錯*/
????if?(WARN_ON(!vdev->v4l2_dev))
????????return?-EINVAL;

????/*?Part?1:?check?device?type?*/
????switch?(type)?{
????//根據(jù)設(shè)備類型類注冊設(shè)備,攝像頭設(shè)備為VFL_TYPE_GRABBER類型
????case?VFL_TYPE_GRABBER:
????????name_base?=?"video";
????·····
????·····
????vdev->vfl_type?=?type;
????vdev->cdev?=?NULL;
????if?(vdev->dev_parent?==?NULL)
????????vdev->dev_parent?=?vdev->v4l2_dev->dev;
????if?(vdev->ctrl_handler?==?NULL)
????????//設(shè)置video_device的ctrl_handler,存在v4l2_device結(jié)構(gòu)體中
????????vdev->ctrl_handler?=?vdev->v4l2_dev->ctrl_handler;
????/*?Part?2:?find?a?free?minor,?device?node?number?and?device?index.?*/
????/*2.尋找空閑次設(shè)備號,設(shè)備個數(shù)和設(shè)備下標*/

????/*?Pick?a?device?node?number?尋找一個空項位置*/
????mutex_lock(&videodev_lock);
????nr?=?devnode_find(vdev,?nr?==?-1???0?:?nr,?minor_cnt);
????//
????if?(nr?==?minor_cnt)
????????nr?=?devnode_find(vdev,?0,?minor_cnt);
????if?(nr?==?minor_cnt)?{
????????printk(KERN_ERR?"could?not?get?a?free?device?node?numbern");
????????mutex_unlock(&videodev_lock);
????????return?-ENFILE;
????}
#ifdef?CONFIG_VIDEO_FIXED_MINOR_RANGES
????/*?1-on-1?mapping?of?device?node?number?to?minor?number?*/
????i?=?nr;
#else
????/*?The?device?node?number?and?minor?numbers?are?independent,?so
???????we?just?find?the?first?free?minor?number.?*/
????for?(i?=?0;?i?<?VIDEO_NUM_DEVICES;?i++)
????????if?(video_device[i]?==?NULL)
????????????break;
????if?(i?==?VIDEO_NUM_DEVICES)?{
????????mutex_unlock(&videodev_lock);
????????printk(KERN_ERR?"could?not?get?a?free?minorn");
????????return?-ENFILE;
????}
#endif
????vdev->minor?=?i?+?minor_offset;
????vdev->num?=?nr;
????devnode_set(vdev);

????/*?Should?not?happen?since?we?thought?this?minor?was?free?*/

????vdev->index?=?get_index(vdev);
????video_device[vdev->minor]?=?vdev;

????if?(vdev->ioctl_ops)
????????determine_valid_ioctls(vdev);

????/*?Part?3:?Initialize?the?character?device?*/
????vdev->cdev?=?cdev_alloc();
????if?(vdev->cdev?==?NULL)?{
????????ret?=?-ENOMEM;
????????goto?cleanup;
????}
????vdev->cdev->ops?=?&v4l2_fops;//設(shè)置字符設(shè)備的系統(tǒng)調(diào)用函數(shù)
????vdev->cdev->owner?=?owner;

????//注冊字符設(shè)備
????ret?=?cdev_add(vdev->cdev,?MKDEV(VIDEO_MAJOR,?vdev->minor),?1);

????/*?Part?4:?register?the?device?with?sysfs?*/
????vdev->dev.class?=?&video_class;
????vdev->dev.devt?=?MKDEV(VIDEO_MAJOR,?vdev->minor);
????vdev->dev.parent?=?vdev->dev_parent;
????//設(shè)置video結(jié)點名稱,如果設(shè)備類型為VFL_TYPE_GRABBER,名稱為videoX
????dev_set_name(&vdev->dev,?"%s%d",?name_base,?vdev->num);

????//注冊device文件,生成設(shè)備文件/dev/videoX
????ret?=?device_register(&vdev->dev);
????/*?Register?the?release?callback?that?will?be?called?when?the?last
???????reference?to?the?device?goes?away.?*/
????//設(shè)置銷毀video設(shè)備的回調(diào)函數(shù)
????vdev->dev.release?=?v4l2_device_release;

????/*?Increase?v4l2_device?refcount?*/
????v4l2_device_get(vdev->v4l2_dev);

這個函數(shù)主要做四件事:

    檢查設(shè)備類型,賦予設(shè)備名稱尋找一個空閑的設(shè)備位置,尋找合適的主設(shè)備號和次設(shè)號初始化字符設(shè)備,使用v4l2_device的v4l2_fops初始化video_device的fops,release函數(shù)等注冊字符設(shè)備,并生成/dev/videoX結(jié)點,注冊subdev時也會調(diào)用這個接口

3. v4l2_subdev從設(shè)備

V4L2從設(shè)備使用struct v4l2_subdev結(jié)構(gòu)體表示,該結(jié)構(gòu)體用于對子設(shè)備進行抽象。

幾乎所有的設(shè)備都有多個 IC 模塊

    它們可能是實體的(例如 USB 攝像頭里面包含 ISP、sensor 等)也可能是抽象的(如 USB 設(shè)備里面的抽象拓撲結(jié)構(gòu))它們在 /dev 目錄下面生成了多個設(shè)備節(jié)點,并且這些 IC 模塊還創(chuàng)建了一些非 v4l2 設(shè)備:DVB、ALSA、FB、I2C 和輸入設(shè)備。

通常情況下,這些IC模塊通過一個或者多個 I2C 總線連接到主橋驅(qū)動上面,同時其它的總線仍然可用,這些 IC 就稱為 ‘sub-devices’。

一個V4L2主設(shè)備可能對應(yīng)多個V4L2從設(shè)備,所有主設(shè)備對應(yīng)的從設(shè)備都掛到v4l2_device結(jié)構(gòu)體的subdevs鏈表中。

對于視頻設(shè)備,從設(shè)備就是攝像頭,通常情況下是I2C設(shè)備,主設(shè)備可通過I2C總線控制從設(shè)備

例如控制攝像頭的焦距、閃光燈等,同時使用 MIPI 或者 LVDS 等接口進行圖像數(shù)據(jù)傳輸。

struct v4l2_subdev中包含的struct v4l2_subdev_ops是一個完備的操作函數(shù)集,用于對接各種不同的子設(shè)備,比如video、audio、sensor等;

同時還有一個核心的函數(shù)集struct v4l2_subdev_core_ops,提供更通用的功能。子設(shè)備驅(qū)動根據(jù)設(shè)備特點實現(xiàn)該函數(shù)集中的某些函數(shù)即可。

?[include/media/v4l2-subdev.h]
????#define?V4L2_SUBDEV_FL_IS_I2C????????(1U?<<?0)??//?從設(shè)備是I2C設(shè)備
????#define?V4L2_SUBDEV_FL_IS_SPI????????(1U?<<?1)??//?從設(shè)備是SPI設(shè)備
????#define?V4L2_SUBDEV_FL_HAS_DEVNODE????(1U?<<?2)??//?從設(shè)備需要設(shè)備節(jié)點
????#define?V4L2_SUBDEV_FL_HAS_EVENTS????(1U?<<?3)??//?從設(shè)備會產(chǎn)生事件

????struct?v4l2_subdev?{
????#if?defined(CONFIG_MEDIA_CONTROLLER)??//?多媒體配置選項
????????struct?media_entity?entity;
????#endif
????????struct?list_head?list;??//?子設(shè)備串聯(lián)鏈表
????????struct?module?*owner;??//?屬于那個模塊,一般指向i2c_lient驅(qū)動模塊
????????bool?owner_v4l2_dev;
????????//?標志位,確定該設(shè)備屬于那種設(shè)備,由V4L2_SUBDEV_FL_IS_XX宏確定
????????u32?flags;
????????//?指向主設(shè)備的v4l2_device結(jié)構(gòu)體
????????struct?v4l2_device?*v4l2_dev;
????????//?v4l2子設(shè)備的操作函數(shù)集合
????????const?struct?v4l2_subdev_ops?*ops;
????????//?提供給v4l2框架的操作函數(shù),只有v4l2框架會調(diào)用,驅(qū)動不使用
????????const?struct?v4l2_subdev_internal_ops?*internal_ops;
????????//?從設(shè)備的控制接口
????????struct?v4l2_ctrl_handler?*ctrl_handler;
????????//?從設(shè)備的名稱,必須獨一無二
????????char?name[V4L2_SUBDEV_NAME_SIZE];
????????//?從設(shè)備組的ID,由驅(qū)動定義,相似的從設(shè)備可以編為一組,
????????u32?grp_id;
????????//?從設(shè)備私有數(shù)據(jù)指針,一般指向i2c_client的設(shè)備結(jié)構(gòu)體dev
????????void?*dev_priv;
????????//?主設(shè)備私有數(shù)據(jù)指針,一般指向v4l2_device嵌入的結(jié)構(gòu)體
????????void?*host_priv;
????????//?指向video設(shè)備結(jié)構(gòu)體
????????struct?video_device?*devnode;
????????//?指向物理設(shè)備
????????struct?device?*dev;
????????//?將所有從設(shè)備連接到全局subdev_list鏈表或notifier->done鏈表
????????struct?list_head?async_list;
????????//?指向struct?v4l2_async_subdev,用于異步事件
????????struct?v4l2_async_subdev?*asd;
????????//?指向管理的notifier,用于主設(shè)備和從設(shè)備的異步關(guān)聯(lián)
????????struct?v4l2_async_notifier?*notifier;
????????/*?common?part?of?subdevice?platform?data?*/
????????struct?v4l2_subdev_platform_data?*pdata;
????};
????//?提供給v4l2框架的操作函數(shù),只有v4l2框架會調(diào)用,驅(qū)動不使用
????struct?v4l2_subdev_internal_ops?{
????????//?v4l2_subdev注冊時回調(diào)此函數(shù),使v4l2_dev指向主設(shè)備的v4l2_device結(jié)構(gòu)體
????????int?(*registered)(struct?v4l2_subdev?*sd);
????????//?v4l2_subdev卸載時回調(diào)此函數(shù)
????????void?(*unregistered)(struct?v4l2_subdev?*sd);
????????//?應(yīng)用調(diào)用open打開從設(shè)備節(jié)點時調(diào)用此函數(shù)
????????int?(*open)(struct?v4l2_subdev?*sd,?struct?v4l2_subdev_fh?*fh);
????????//?應(yīng)用調(diào)用close時調(diào)用此函數(shù)
????????int?(*close)(struct?v4l2_subdev?*sd,?struct?v4l2_subdev_fh?*fh);
????};

使用v4l2_subdev_init初始化v4l2_subdev結(jié)構(gòu)體。然后必須用一個唯一的名字初始化subdev->name,同時初始化模塊的owner域。

若從設(shè)備是I2C設(shè)備,則可使用v4l2_i2c_subdev_init函數(shù)進行初始化,該函數(shù)內(nèi)部會調(diào)用v4l2_subdev_init,同時設(shè)置flags、owner、dev、name等成員。

?[include/media/v4l2-subdev.h]
????//?初始化v4l2_subdev結(jié)構(gòu)體
????//?ops-v4l2子設(shè)備的操作函數(shù)集合指針,保存到v4l2_subdev結(jié)構(gòu)體的ops成員中
????void?v4l2_subdev_init(struct?v4l2_subdev?*sd,
????????????const?struct?v4l2_subdev_ops?*ops);

????[include/media/v4l2-common.h]
????//?初始化V4L2從設(shè)備為I2C設(shè)備的v4l2_subdev結(jié)構(gòu)體
????//?sd-v4l2_subdev結(jié)構(gòu)體指針
????//?client-i2c_client結(jié)構(gòu)體指針
????//?ops-v4l2子設(shè)備的操作函數(shù)集合指針,保存到v4l2_subdev結(jié)構(gòu)體的ops成員中
????void?v4l2_i2c_subdev_init(struct?v4l2_subdev?*sd,?
????????struct?i2c_client?*client,
????????const?struct?v4l2_subdev_ops?*ops);

從設(shè)備必須向V4L2子系統(tǒng)注冊v4l2_subdev結(jié)構(gòu)體,使用v4l2_device_register_subdev注冊,使用v4l2_device_unregister_subdev注銷。

[include/media/v4l2-device.h]
????//?向V4L2子系統(tǒng)注冊v4l2_subdev結(jié)構(gòu)體
????//?v4l2_dev-主設(shè)備v4l2_device結(jié)構(gòu)體指針
????//?sd-從設(shè)備v4l2_subdev結(jié)構(gòu)體指針
????//?返回值?0-成功,小于0-失敗
????int?v4l2_device_register_subdev(struct?v4l2_device?*v4l2_dev,
????????????????????struct?v4l2_subdev?*sd)

????//?從V4L2子系統(tǒng)注銷v4l2_subdev結(jié)構(gòu)體
????//?sd-從設(shè)備v4l2_subdev結(jié)構(gòu)體指針????
????void?v4l2_device_unregister_subdev(struct?v4l2_subdev?*sd);

V4L2從設(shè)備驅(qū)動都必須有一個v4l2_subdev結(jié)構(gòu)體。這個結(jié)構(gòu)體可以單獨代表一個簡單的從設(shè)備,也可以嵌入到一個更大的結(jié)構(gòu)體中,與更多設(shè)備狀態(tài)信息保存在一起。通常有一個下級設(shè)備結(jié)構(gòu)體(比如:i2c_client)包含了內(nèi)核創(chuàng)建的設(shè)備數(shù)據(jù)。

建議使用v4l2_set_subdevdata()將這個結(jié)構(gòu)體的指針保存在v4l2_subdev的私有數(shù)據(jù)域(dev_priv)中??梢愿奖愕耐ㄟ^v4l2_subdev找到實際的低層總線特定設(shè)備數(shù)據(jù)。

對于常用的i2c_client結(jié)構(gòu)體,i2c_set_clientdata函數(shù)可用于保存一個v4l2_subdev指針,i2c_get_clientdata可以獲取一個v4l2_subdev指針;對于其他總線可能需要使用其他相關(guān)函數(shù)。

[include/media/v4l2-subdev.h]
????//?將i2c_client的指針保存到v4l2_subdev結(jié)構(gòu)體的dev_priv成員中
????static?inline?void?v4l2_set_subdevdata(struct?v4l2_subdev?*sd,?void?*p)
????{
????????sd->dev_priv?=?p;
????}

????[include/linux/i2c.h]
????//?可以將v4l2_subdev結(jié)構(gòu)體指針保存到i2c_client中dev成員的driver_data中
????static?inline?void?i2c_set_clientdata(struct?i2c_client?*dev,?void?*data)
????{
????????dev_set_drvdata(&dev->dev,?data);
????}
????//?獲取i2c_client結(jié)構(gòu)體中dev成員的driver_data,一般指向v4l2_subdev
????static?inline?void?*i2c_get_clientdata(const?struct?i2c_client?*dev)
????{
????????return?dev_get_drvdata(&dev->dev);
????}

主設(shè)備驅(qū)動中也應(yīng)保存每個子設(shè)備的私有數(shù)據(jù),比如一個指向特定主設(shè)備的各設(shè)備私有數(shù)據(jù)的指針。為此v4l2_subdev結(jié)構(gòu)體提供主設(shè)備私有數(shù)據(jù)域(host_priv),并可通過v4l2_get_subdev_hostdata和 v4l2_set_subdev_hostdata訪問。

?[include/media/v4l2-subdev.h]
????static?inline?void?*v4l2_get_subdev_hostdata(const?struct?v4l2_subdev?*sd)
????{
????????return?sd->host_priv;
????}
????static?inline?void?v4l2_set_subdev_hostdata(struct?v4l2_subdev?*sd,?void?*p)
????{
????????sd->host_priv?=?p;
????}

每個v4l2_subdev都包含子設(shè)備驅(qū)動需要實現(xiàn)的函數(shù)指針(如果對此設(shè)備不適用,可為NULL),具體在v4l2_subdev_ops結(jié)構(gòu)體當中。

由于子設(shè)備可完成許多不同的工作,而在一個龐大的函數(shù)指針結(jié)構(gòu)體中通常僅有少數(shù)有用的函數(shù)實現(xiàn)其功能肯定不合適。

所以,函數(shù)指針根據(jù)其實現(xiàn)的功能被分類,每一類都有自己的函數(shù)指針結(jié)構(gòu)體,如v4l2_subdev_core_ops、v4l2_subdev_audio_ops、v4l2_subdev_video_ops等等。

頂層函數(shù)指針結(jié)構(gòu)體包含了指向各類函數(shù)指針結(jié)構(gòu)體的指針,如果子設(shè)備驅(qū)動不支持該類函數(shù)中的任何一個功能,則指向該類結(jié)構(gòu)體的指針為NULL。

?[include/media/v4l2-subdev.h]
????/*?v4l2從設(shè)備的操作函數(shù)集合,從設(shè)備根據(jù)自身設(shè)備類型選擇實現(xiàn),
???????其中core函數(shù)集通??捎糜谒凶釉O(shè)備,其他類別的實現(xiàn)依賴于
???????子設(shè)備。如視頻設(shè)備可能不支持音頻操作函數(shù),反之亦然。這樣的
???????設(shè)置在限制了函數(shù)指針數(shù)量的同時,還使增加新的操作函數(shù)和分類
???????變得較為容易。?*/
????struct?v4l2_subdev_ops?{
????????//?從設(shè)備的通用操作函數(shù)集合,進行初始化、reset、控制等操作
????????const?struct?v4l2_subdev_core_ops????*core;
????????const?struct?v4l2_subdev_tuner_ops????*tuner;
????????const?struct?v4l2_subdev_audio_ops????*audio;??//?音頻設(shè)備
????????//?視頻設(shè)備
????????const?struct?v4l2_subdev_video_ops????*video;??
????????const?struct?v4l2_subdev_vbi_ops????*vbi;????//?VBI設(shè)備
????????const?struct?v4l2_subdev_ir_ops????????*ir;
????????const?struct?v4l2_subdev_sensor_ops????*sensor;
????????const?struct?v4l2_subdev_pad_ops????*pad;
????};
????//?適用于所有v4l2從設(shè)備的操作函數(shù)集合
????struct?v4l2_subdev_core_ops?{
????????//?IO引腳復(fù)用配置
????????int?(*s_io_pin_config)(struct?v4l2_subdev?*sd,?size_t?n,
????????????????????????struct?v4l2_subdev_io_pin_config?*pincfg);
????????//?初始化從設(shè)備的某些寄存器,使其恢復(fù)默認
????????int?(*init)(struct?v4l2_subdev?*sd,?u32?val);
????????//?加載固件
????????int?(*load_fw)(struct?v4l2_subdev?*sd);
????????//?復(fù)位
????????int?(*reset)(struct?v4l2_subdev?*sd,?u32?val);
????????//?設(shè)置GPIO引腳輸出值
????????int?(*s_gpio)(struct?v4l2_subdev?*sd,?u32?val);
????????//?設(shè)置從設(shè)備的電源狀態(tài),0-省電模式,1-正常操作模式
????????int?(*s_power)(struct?v4l2_subdev?*sd,?int?on);
????????//?中斷函數(shù),被主設(shè)備的中斷函數(shù)調(diào)用
????????int?(*interrupt_service_routine)(struct?v4l2_subdev?*sd,
????????????????????????????u32?status,?bool?*handled);
????????......
????};

使用v4l2_device_register_subdev注冊從設(shè)備后,就可以調(diào)用v4l2_subdev_ops中的方法了。

可以通過v4l2_subdev直接調(diào)用,也可以使用內(nèi)核提供的宏定義v4l2_subdev_call間接調(diào)用某一個方法。

若要調(diào)用多個從設(shè)備的同一個方法,則可使用v4l2_device_call_all宏定義。

//?直接調(diào)用
????err?=?sd->ops->video->g_std(sd,?&norm);

????//?使用宏定義調(diào)用,這個宏將會做NULL指針檢查,如果su為NULL,則返回-ENODEV;
????//?如果sd->ops->video或sd->ops->video->g_std為NULL,則返回-ENOIOCTLCMD;
????//?否則將返回sd->ops->video->g_std的調(diào)用的實際結(jié)果
????err?=?v4l2_subdev_call(sd,?video,?g_std,?&norm);

????[include/media/v4l2-subdev.h]
????#define?v4l2_subdev_call(sd,?o,?f,?args...)????????????????
????(!(sd)???-ENODEV?:?(((sd)->ops->o?&&?(sd)->ops->o->f)??????
????????(sd)->ops->o->f((sd)?,?##args)?:?-ENOIOCTLCMD))


????v4l2_device_call_all(v4l2_dev,?0,?video,?g_std,?&norm);
??
[include/media/v4l2-device.h]
????#define?v4l2_device_call_all(v4l2_dev,?grpid,?o,?f,?args...)????????
????do?{????????????????????????????????
????????struct?v4l2_subdev?*__sd;????????????????
????????__v4l2_device_call_subdevs_p(v4l2_dev,?__sd,????????
????????????!(grpid)?||?__sd->grp_id?==?(grpid),?o,?f?,????
????????????##args);????????????????????
????}?while?(0)

如果子設(shè)備需要通知它的v4l2_device主設(shè)備一個事件,可以調(diào)用**v4l2_subdev_notify(sd,notification, arg)**。

這個宏檢查是否有一個notify回調(diào)被注冊,如果沒有,返回-ENODEV。否則返回 notify調(diào)用結(jié)果。notify回調(diào)函數(shù)由主設(shè)備提供。

[include/media/v4l2-device.h]
????//?從設(shè)備通知主設(shè)備,最終回調(diào)到v4l2_device的notify函數(shù)
????static?inline?void?v4l2_subdev_notify(struct?v4l2_subdev?*sd,
????????????????????????unsigned?int?notification,?void?*arg)
????{
????????if?(sd?&&?sd->v4l2_dev?&&?sd->v4l2_dev->notify)
????????????sd->v4l2_dev->notify(sd,?notification,?arg);
????}

使用v4l2_subdev的好處在于它是一個通用結(jié)構(gòu)體,且不包含任何底層硬件信息。

所有驅(qū)動可以包含多個I2C總線的從設(shè)備,但也有從設(shè)備是通過GPIO控制。這個區(qū)別僅在配置設(shè)備時有關(guān)系,一旦子設(shè)備注冊完成,對于v4l2子系統(tǒng)來說就完全透明了。

4. ? ?v4l2_fh

文件訪問控制

5. ? v4l2_ctrl_handler

控制模塊,提供子設(shè)備(主要是 video 和 ISP 設(shè)備)在用戶空間的特效操作接口

6. ? media_device

用于運行時數(shù)據(jù)流的管理,嵌入在 V4L2 device 內(nèi)部

五、 video_device、v4l2_device和v4l2_subdev的關(guān)系舉例

下面以我們手機的攝像頭來舉例:

    1. 假定一款CMOS攝像頭,有兩個接口:

一個是攝像頭接口(數(shù)據(jù)),一個是I2C接口(控制命令)

攝像頭接口負責傳輸圖像數(shù)據(jù),I2C接口負責傳輸控制信息,所以又可以將CMOS攝像頭看作是一個I2C模塊

在一款SoC芯片上面,攝像頭相關(guān)的有攝像頭控制器、攝像頭接口、I2C總線SOC上可以有多個攝像頭控制器,多個攝像頭接口,多個I2C總線
攝像頭控制器負責接收和處理攝像頭數(shù)據(jù),攝像頭接口負責傳輸圖像數(shù)據(jù),I2C總線負責傳輸控制信息

對于手機而言,一般都有兩個攝像頭:一個前置攝像頭,一個后置攝像頭

如下圖所示:

我們可以選擇讓控制器去操作哪一個攝像頭(可以使用某個gpio供電,通過電平來選擇攝像頭),這就做到了使用一個攝像頭控制器來控制多個攝像頭,這就是多路復(fù)用

我們回到V4L2來,再來談v4l2_device和v4l2_subdev:

    • v4l2_device表示一個v4l2實例,在V4L2驅(qū)動中,使用v4l2_device來表示攝像頭控制器使用v4l2_subdev來表示具體的某一個攝像頭的I2C控制模塊,進而通過其控制攝像頭v4l2_device里有一個v4l2_subdev鏈表,可以選擇v4l2_device去控制哪一個v4l2_subdev

 

    subdev的設(shè)計目的是為了多路復(fù)用,就是用一個v4l2_device可以服務(wù)多個v4l2_subdev

然而某些驅(qū)動是沒有v4l2_subdev,只有video_device

我們用一張圖來總結(jié)設(shè)備之間關(guān)系:

    video_device是一個字符設(shè)備,video_device內(nèi)含一個cdevv4l2_device是一個v4l2實例,嵌入到video_device中v4l2_device維護者一個鏈表管理v4l2_subdev,v4l2_subdev表示攝像頭的I2C控制模塊主設(shè)備可通過v4l2_subdev_call的宏調(diào)用從設(shè)備提供的方法,反過來從設(shè)備可以調(diào)用主設(shè)備的notify方法通知主設(shè)備某些事件發(fā)生了。

核心層(core)負責注冊字符設(shè)備,然后提供video_device對象和相應(yīng)的注冊接口給硬件相關(guān)層使用;

硬件相關(guān)層需要分配一個video_device并設(shè)置它,然后向核心層注冊,核心層會為其注冊字符設(shè)備并且創(chuàng)建設(shè)備節(jié)點(/dev/videox);

同時硬件相關(guān)層還需要分配和設(shè)置相應(yīng)的v4l2_device和v4l2_subdev,其中v4l2_device的一個比較重要的意義就是管理v4l2_subdev,當然有一些驅(qū)動并不需要實現(xiàn)v4l2_subdev,此時v4l2_device的意義就不是很大了;

應(yīng)用層通過/dev/video來操作設(shè)備的時候,首先會來到V4L2的核心層,核心層通過注冊進的video_device的回調(diào)函數(shù)調(diào)用相應(yīng)的操作函數(shù),video_device可以直接操作硬件或者是通過v4l2_subdev來操作硬件。

一口君再把各個結(jié)構(gòu)體與各回調(diào)函數(shù)之間關(guān)系匯總到下面這個圖里(rk3568):

主要架構(gòu)部分Linux內(nèi)核已經(jīng)實現(xiàn)了,Camera控制器驅(qū)動,廠家一般都會實現(xiàn),對于一般驅(qū)動工程師來說,我們只需要實現(xiàn)子設(shè)備驅(qū)動即可。

六、videobuf2

從數(shù)據(jù)流角度來分析,V4L2框架可以分成兩個部分看:控制流+數(shù)據(jù)流

    控制流主要由v4l2_subdev的回調(diào)函數(shù)實現(xiàn)(一般由攝像頭廠商提供),主要用于控制攝像數(shù)據(jù)流的部分就是video buffer,驅(qū)動部分通常由SoC廠商提供(比如瑞芯微rk3568平臺,對應(yīng)到rkisp_rawrd0_m、rkisp_rawrd2_s子模塊)。

V4L2的buffer管理是通過videobuf2來完成的,它充當用戶空間和驅(qū)動之間的中間層,并提供low-level,模塊化的內(nèi)存管理功能;

回顧一下獲取攝像頭視頻流的主要步驟:

要獲取圖像信息需要執(zhí)行VIDIOC_DQBUF、VIDIOC_QBUF命令。

瑞芯微rk3568平臺videobuf2相關(guān)結(jié)構(gòu)體和ops回調(diào)函數(shù)關(guān)系如下:

其中struct rkisp_device是瑞芯微3568平臺用于管理Camera控制器的最重要的結(jié)構(gòu)體

struct rkisp_capture_device 對應(yīng)拓撲結(jié)構(gòu)中的模塊rkisp_rawrd0_m 、rkisp_rawrd2_s 。

該模塊是一個video設(shè)備,用于獲取原始圖像信息,所以在struct rkisp_vdev_node vnode中包含了struct vb2_queue buf_queue、struct video_device vdev

struct vb2_queue中的回調(diào)函數(shù)struct vb2_mem_ops *mem_ops、struct vb2_buf_ops *buf_ops、struct vb2_ops *ops就是videobuf2驅(qū)動。

videobuf2驅(qū)動部分相關(guān)結(jié)構(gòu)體如下:

上圖大體包含了videobuf2的框架;

    vb2_queue:核心的數(shù)據(jù)結(jié)構(gòu),用于描述buffer的隊列,其中struct vb2_buffer *bufs[]是存放buffer節(jié)點的數(shù)組,該數(shù)組中的成員代表了vb2 buffer,并將在queued_list和done_list兩個隊列中進行流轉(zhuǎn);struct vb2_buf_ops:buffer的操作函數(shù)集,由驅(qū)動來實現(xiàn),并由框架通過call_bufop宏來對特定的函數(shù)進行調(diào)用;struct vb2_mem_ops:內(nèi)存buffer分配函數(shù)接口,buffer類型分為三種:1)虛擬地址和物理地址都分散,可以通過dma-sg來完成;2)物理地址分散,虛擬地址連續(xù),可以通過vmalloc分配;3)物理地址連續(xù),可以通過dma-contig來完成;三種類型也vb2框架中都有實現(xiàn),框架可以通過call_memop來進行調(diào)用;struct vb2_ops:vb2隊列操作函數(shù)集,由驅(qū)動來實現(xiàn)對應(yīng)的接口,并在框架中通過call_vb_qop宏被調(diào)用;

調(diào)用流程:

?????????????????????通用接口????----------isp?ioctrl接口----------???????????驅(qū)動????????????????????????
字符設(shè)備->v4l2_ioctl->v4l_qbuf->vb2_ioctl_qbuf->vb2_qbuf->vb2_core_qbuf->rkisp_buf_queue
    下面是VIDIOC_DQBUF命令執(zhí)行的 log【在函數(shù)vb2_core_dqbuf入口調(diào)用stack_dump()】:
/*?*/
[??105.813743]?vb2_core_dqbuf+0x54/0x5b8
[??105.813753]?vb2_dqbuf+0x94/0xc8
[??105.813763]?vb2_ioctl_dqbuf+0x50/0x60

[??105.813774]?v4l_dqbuf+0x44/0x58
[??105.813785]?__video_do_ioctl+0x1a0/0x348
[??105.813795]?video_usercopy+0x228/0x740
[??105.813805]?video_ioctl2+0x14/0x20
[??105.813815]?v4l2_ioctl+0x44/0x68
[??105.813825]?v4l2_compat_ioctl32+0x1d0/0x3a48

[??105.813836]?__arm64_compat_sys_ioctl+0xbc/0x15b0
[??105.813847]?el0_svc_common.constprop.0+0x64/0x178
[??105.813859]?el0_svc_compat_handler+0x18/0x20
[??105.813869]?el0_svc_compat+0x8/0x34
    VIDIOC_QBUF命令執(zhí)行的log:
[??105.944858]?vb2_core_qbuf+0x28/0x338
[??105.944883]?vb2_qbuf+0x6c/0x90
[??105.944904]?vb2_ioctl_qbuf+0x48/0x58
[??105.944928]?v4l_qbuf+0x44/0x58
[??105.944951]?__video_do_ioctl+0x1a0/0x348
[??105.944972]?video_usercopy+0x228/0x740
[??105.944993]?video_ioctl2+0x14/0x20
[??105.945013]?v4l2_ioctl+0x44/0x68
[??105.945036]?v4l2_compat_ioctl32+0x1d0/0x3a48
[??105.945058]?__arm64_compat_sys_ioctl+0xbc/0x15b0
[??105.945082]?el0_svc_common.constprop.0+0x64/0x178
[??105.945105]?el0_svc_compat_handler+0x18/0x20
[??105.945125]?el0_svc_compat+0x8/0x34

七、v4l2拓撲結(jié)構(gòu)

關(guān)于如何使用設(shè)備樹節(jié)點描述拓撲結(jié)構(gòu),后續(xù)文章會詳細講解。

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

公眾號『一口Linux』號主彭老師,擁有15年嵌入式開發(fā)經(jīng)驗和培訓經(jīng)驗。曾任職ZTE,某研究所,華清遠見教學總監(jiān)。擁有多篇網(wǎng)絡(luò)協(xié)議相關(guān)專利和軟件著作。精通計算機網(wǎng)絡(luò)、Linux系統(tǒng)編程、ARM、Linux驅(qū)動、龍芯、物聯(lián)網(wǎng)。原創(chuàng)內(nèi)容基本從實際項目出發(fā),保持原理+實踐風格,適合Linux驅(qū)動新手入門和技術(shù)進階。