# Linux 核心專題: 虛擬攝影機裝置驅動程式
> 執行人: hankTaro
> [專題解說錄影](https://youtu.be/Ze4QXG0Ov9I)
:::success
:question: 提問清單
* ?
:::
## 任務簡述
[vcam](https://github.com/sysprog21/vcam) 是個針對 Linux 核心開發的虛擬攝影機裝置,全部程式碼僅 1 千 5 百行,從而可理解 V4L2 (video fro Linux APIs, version 2) 的使用和 Linux 多媒體架構。
## TODO: 整理過往報告
閱讀以下報告,並針對 Linux v5.15+ 的 [V4L2 介面](https://www.kernel.org/doc/html/latest/driver-api/media/) (及異動),予以更新,應包含 V4L2 和 [vcam](https://github.com/sysprog21/vcam) 運作原理,可善用 [HackMD 書本模式](https://hackmd.io/s/how-to-create-book-tw)展現。
* [2020 年開發紀錄](https://hackmd.io/@eecheng/B16rQ3GjU)
* [2021 年開發紀錄](https://hackmd.io/@WayneLin1992/HkDBmLUDO)
* [2022 年開發紀錄](https://hackmd.io/@Masamaloka/linux2022-vcam)
可沿用過去的內容,但應該在整理的材料開頭提及貢獻者。紀錄你的認知和疑惑。
### V4L2 架構與簡介
V4L2 全名為 video for linux two ,前身為 V4L ,故 V4L2 尾端的 2 是2代的概念
V4L2 是 linux 中的一個驅動程式框架,其作為 API 支援實時(realtime)影音捕捉,他提供不同硬體一個統一的框架,像是你電腦的攝影機與螢幕之間,sensor 獲取了資料,要如何處理這些資料、怎麼傳輸、傳輸到哪,都是在 V4L2 的框架下統一管理。
為何需要一個這樣的框架去架在裝置與裝置之間,因為在過去不同裝置之間會需要有個別的驅動程序來供其運作,這是一件耗時且困難的工作,也容易使得裝置不正確運作,並且許多通用的代碼由於沒有共同框架,在[代碼重構](https://en.wikipedia.org/wiki/Code_refactoring)上會十分困難,故 V4L2 提供一個驅動框架收錄了各類驅動所需的基礎架構塊(basic building blocks),可以依照使用者所需選用。
在Linux 中,所有裝置都被看成一種特殊的文件,可以像存取一般文件一樣對其進行讀寫。像是筆電的攝影機就是在 `/dev/videoX` (其中`X`是數字,取決於有幾個註冊的攝影裝置),V4L2 藉由對其記憶體映射(mmap)或直接讀取(read)來採集影像。
V4L2規範中不僅定義了通用API元素(Common API Elements),圖像的格式(Image Formats),輸入/輸出方法(Input/Output),還定義了Linux內核驅動處理視頻信息的一系列介面(Interfaces),這些介面主要有:
* 視頻採集介面——Video Capture Interface;
* 視頻輸出介面—— Video Output Interface;
* 視頻覆蓋/預覽介面——Video Overlay Interface;
* 視頻輸出覆蓋介面——Video Output Overlay Interface;
* 編解碼介面——Codec Interface。
<!-- 雖然說其名稱是 video for linux 2 ,但其應用面相十分廣泛,影片傳輸只是其中一個部份,還有包括解碼、格式轉換等等各種 -->
對於使用者來說,V4L2 的大體操作流程如下:
* Opening the device
* Changing device properties, selecting a video and audio input, video standard, picture brightness a. o.
* Negotiating a data format
* Negotiating an input/output method
* The actual input/output loop
* Closing the device
<!-- 接下來會依照 [2.4 Video device' s internal representation](https://www.kernel.org/doc/html/latest/driver-api/media/v4l2-dev.html) 來介紹如何將一新裝置藉由 V4L2 註冊進電腦 -->
<!-- #### 裝置的宣告
The actual device nodes in the /dev directory are created using the video_device struct (v4l2-dev.h). This struct can either be allocated dynamically or embedded in a larger struct.
To allocate it dynamically use video_device_alloc():
```c
struct video_device *vdev = video_device_alloc();
if (vdev == NULL)
return -ENOMEM;
vdev->release = video_device_release;
``` -->
### v4l2 framework
<!-- ### Vcam 其作用 -->
## 確認通過 v4l2-compliance
依據 [2022 年開發紀錄](https://hackmd.io/@Masamaloka/linux2022-vcam),確保 vcam 可通過所有 [v4l2-compliance](https://manpages.ubuntu.com/manpages/bionic/man1/v4l2-compliance.1.html) 的測試項目,若有無法通過,則逐一排除。
使用 `sudo v4l2-compliance -d /dev/video2 -f` 測試
### 5.19.0 測試結果
```shell
$ sudo v4l2-compliance -d /dev/video2 -f
v4l2-compliance 1.22.1, 64 bits, 64-bit time_t
Compliance test for vcam device /dev/video2:
Driver Info:
Driver name : vcam
Card type : vcam
Bus info : platform: virtual
Driver version : 5.19.17
Capabilities : 0x85200001
Video Capture
Read/Write
Streaming
Extended Pix Format
Device Capabilities
Device Caps : 0x05200001
Video Capture
Read/Write
Streaming
Extended Pix Format
Required ioctls:
test VIDIOC_QUERYCAP: OK
test invalid ioctls: OK
Allow for multiple opens:
test second /dev/video2 open: OK
test VIDIOC_QUERYCAP: OK
test VIDIOC_G/S_PRIORITY: OK
test for unlimited opens: OK
Debug ioctls:
test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
test VIDIOC_LOG_STATUS: OK (Not Supported)
Input ioctls:
test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
test VIDIOC_ENUMAUDIO: OK (Not Supported)
test VIDIOC_G/S/ENUMINPUT: OK
test VIDIOC_G/S_AUDIO: OK (Not Supported)
Inputs: 1 Audio Inputs: 0 Tuners: 0
Output ioctls:
test VIDIOC_G/S_MODULATOR: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_ENUMAUDOUT: OK (Not Supported)
test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
test VIDIOC_G/S_AUDOUT: OK (Not Supported)
Outputs: 0 Audio Outputs: 0 Modulators: 0
Input/Output configuration ioctls:
test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
test VIDIOC_G/S_EDID: OK (Not Supported)
Control ioctls (Input 0):
test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
test VIDIOC_QUERYCTRL: OK (Not Supported)
test VIDIOC_G/S_CTRL: OK (Not Supported)
test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
Standard Controls: 0 Private Controls: 0
Format ioctls (Input 0):
test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
test VIDIOC_G/S_PARM: OK
test VIDIOC_G_FBUF: OK (Not Supported)
test VIDIOC_G_FMT: OK
test VIDIOC_TRY_FMT: OK
test VIDIOC_S_FMT: OK
test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
test Cropping: OK (Not Supported)
test Composing: OK (Not Supported)
test Scaling: OK (Not Supported)
Codec ioctls (Input 0):
test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
test VIDIOC_G_ENC_INDEX: OK (Not Supported)
test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)
Buffer ioctls (Input 0):
test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
test VIDIOC_EXPBUF: OK
test Requests: OK (Not Supported)
Test input 0:
Stream using all formats:
test MMAP for Format RGB3, Frame Size 640x480@59.94 Hz:
Stride 1920, Field None: OK
test MMAP for Format RGB3, Frame Size 640x480@1.00 Hz:
Stride 1920, Field None: OK
Total for vcam device /dev/video2: 47, Succeeded: 47, Failed: 0, Warnings: 0
```
### 6.1.32 虛擬環境測試結果
```shell
root@(none):/tmp/vcam# v4l2-compliance -d /dev/video0 -f
v4l2-compliance 1.22.1, 64 bits, 64-bit time_t
Compliance test for vcam device /dev/video0:
Driver Info:
Driver name : vcam
Card type : vcam
Bus info : platform: virtual
Driver version : 6.1.35
Capabilities : 0x85200001
Video Capture
Read/Write
Streaming
Extended Pix Format
Device Capabilities
Device Caps : 0x05200001
Video Capture
Read/Write
Streaming
Extended Pix Format
Required ioctls:
test VIDIOC_QUERYCAP: OK
test invalid ioctls: OK
Allow for multiple opens:
test second /dev/video0 open: OK
test VIDIOC_QUERYCAP: OK
test VIDIOC_G/S_PRIORITY: OK
test for unlimited opens: OK
Debug ioctls:
test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
test VIDIOC_LOG_STATUS: OK (Not Supported)
Input ioctls:
test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
test VIDIOC_ENUMAUDIO: OK (Not Supported)
test VIDIOC_G/S/ENUMINPUT: OK
test VIDIOC_G/S_AUDIO: OK (Not Supported)
Inputs: 1 Audio Inputs: 0 Tuners: 0
Output ioctls:
test VIDIOC_G/S_MODULATOR: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_ENUMAUDOUT: OK (Not Supported)
test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
test VIDIOC_G/S_AUDOUT: OK (Not Supported)
Outputs: 0 Audio Outputs: 0 Modulators: 0
Input/Output configuration ioctls:
test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
test VIDIOC_G/S_EDID: OK (Not Supported)
Control ioctls (Input 0):
test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
test VIDIOC_QUERYCTRL: OK (Not Supported)
test VIDIOC_G/S_CTRL: OK (Not Supported)
test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
Standard Controls: 0 Private Controls: 0
Format ioctls (Input 0):
test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
test VIDIOC_G/S_PARM: OK
test VIDIOC_G_FBUF: OK (Not Supported)
test VIDIOC_G_FMT: OK
test VIDIOC_TRY_FMT: OK
test VIDIOC_S_FMT: OK
test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
test Cropping: OK (Not Supported)
test Composing: OK (Not Supported)
test Scaling: OK (Not Supported)
Codec ioctls (Input 0):
test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
test VIDIOC_G_ENC_INDEX: OK (Not Supported)
test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)
Buffer ioctls (Input 0):
test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
test VIDIOC_EXPBUF: OK
test Requests: OK (Not Supported)
Test input 0:
Stream using all formats:
test MMAP for Format RGB3, Frame Size 640x480@59.94 Hz:
Stride 1920, Field None: OK
test MMAP for Format RGB3, Frame Size 640x480@1.00 Hz:
Stride 1920, Field None: OK
Total for vcam device /dev/video0: 47, Succeeded: 47, Failed: 0, Warnings: 0
```
## TODO: 排除記憶體錯誤
運用 [kmemleak](https://www.kernel.org/doc/html/latest/dev-tools/kmemleak.html) 和 [kasan](https://www.kernel.org/doc/html/latest/dev-tools/kasan.html) 排除記憶體錯誤
但是在使用 [kmemleak](https://www.kernel.org/doc/html/latest/dev-tools/kmemleak.html) 前,需要先編譯核心,使其可以運作,因為這個工具並非是正常使用者會使用到的,故開發者沒必要讓其在所有環境下都執行(另一方面也不是所有環境下都能讓此工具運做),故其只能在特定狀況下運行,而要如何打造這個環境就是首要的目標
### 打造虛擬環境
根據 [測試 Linux 核心的虛擬化環境](https://hackmd.io/@sysprog/linux-virtme) 來設定環境
在使用 virtme 選取預設核心組態並編譯階段,需要確保 CONFIG_DEBUG_INFO 在組態中有被開啟,以利後續實驗使用,並且在[kmemleak](https://www.kernel.org/doc/html/latest/dev-tools/kmemleak.html) 文件中寫到,需要先將 CONFIG_DEBUG_KMEMLEAK enable。
執行 `$ grep CONFIG_DEBUG_INFO .config` 若輸出沒有出現 `CONFIG_DEBUG_INFO=y` ,如下
```
CONFIG_DEBUG_INFO_NONE=y
# CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT is not set
# CONFIG_DEBUG_INFO_DWARF4 is not set
# CONFIG_DEBUG_INFO_DWARF5 is not set
```
會需要執行 `make menuconfig` 進行設定
6.1.x 的[CONFIG_DEBUG_INFO](https://www.kernelconfig.io/config_debug_info?q=&kernelversion=6.1.32&arch=x86)和[CONFIG_DEBUG_KMEMLEAK](https://www.kernelconfig.io/config_debug_kmemleak?arch=x86&kernelversion=6.1.32)是依照此位置
![](https://hackmd.io/_uploads/SkFNwpaU3.png)
在你的抵達位置時不一定會找到 Compile the kernel with debug info 這個選項,你需要將 Debug information 設定成任一顯示方法,個人是設定成用 DWARF 4代格式顯示
![](https://hackmd.io/_uploads/HkhyOa6U3.png)
隨後就會出現其選項了
![](https://hackmd.io/_uploads/r1_V_66L2.png)
隨後就出現了 `CONFIG_DEBUG_INFO=y`
```
$ grep CONFIG_DEBUG_INFO .config
CONFIG_DEBUG_INFO=y
# CONFIG_DEBUG_INFO_NONE is not set
# CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT is not set
CONFIG_DEBUG_INFO_DWARF4=y
# CONFIG_DEBUG_INFO_DWARF5 is not set
# CONFIG_DEBUG_INFO_REDUCED is not set
CONFIG_DEBUG_INFO_COMPRESSED=y
# CONFIG_DEBUG_INFO_SPLIT is not set
```
![](https://hackmd.io/_uploads/HyN8qmx_n.png)
隨後等待其 make 結束(會需要一段時間),在編譯結束後,預期可見以下訊息:
```shell
Kernel: arch/x86/boot/bzImage is ready
```
隨後便可透過以下命令,在 QEMU 虛擬機器中啟動 Linux 核心:
```shell
$ virtme-run --kdir . --mods=auto
```
:::warning
要注意一點,在 virtme 中,除了/tmp 外的路徑都是唯獨的,若要寫入需要切換到 host 端
:::
![](https://hackmd.io/_uploads/ryspXjkP3.png)
> 確保 Debug Filesystem 是開啟的
### 編譯和測試核心模組
照著[測試 Linux 核心的虛擬化環境](https://hackmd.io/@sysprog/linux-virtme#%E5%88%A9%E7%94%A8-virtme-%E5%BB%BA%E7%AB%8B-Linux-%E6%A0%B8%E5%BF%83%E6%B8%AC%E8%A9%A6%E7%92%B0%E5%A2%83)中,編譯和測試核心模組的步驟,撰寫 hello.c 以及Makefile
```c
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
static int hello_init(void)
{
printk(KERN_ALERT "Hello World! - init\n");
return 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT "Hello World! - exit\n");
}
module_init(hello_init);
module_exit(hello_exit);
```
```
KDIR=/home/ubuntu/linux
obj-m += hello.o
PWD := $(shell pwd)
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
```
:::warning
在[測試 Linux 核心的虛擬化環境](https://hackmd.io/@sysprog/linux-virtme#%E5%88%A9%E7%94%A8-virtme-%E5%BB%BA%E7%AB%8B-Linux-%E6%A0%B8%E5%BF%83%E6%B8%AC%E8%A9%A6%E7%92%B0%E5%A2%83)中, Makefile 的代碼有誤
`KDIR := $(PWD)/..` 這行會導致 KDIR 導引到錯誤位置,故將其刪除
以及要將 `KDIR=/home/ubuntu/linux` 設定為自己存放虛擬環境的目錄
:::
```shell
# make
make -C /home/hank/linux2023/virtme/linux M=/tmp/hello modules
make[1]: Entering directory '/home/hank/linux2023/virtme/linux'
warning: the compiler differs from the one used to build the kernel
The kernel was built by: x86_64-linux-gnu-gcc (Ubuntu 11.3.0-1ubuntu1~22.04.1) 11.3.0
You are using: gcc (Ubuntu 11.3.0-1ubuntu1~22.04.1) 11.3.0
CC [M] /tmp/hello/hello.o
MODPOST /tmp/hello/Module.symvers
CC [M] /tmp/hello/hello.mod.o
LD [M] /tmp/hello/hello.ko
make[1]: Leaving directory '/home/hank/linux2023/virtme/linux'
```
隨後便可測試載入以及移除模組
```shell
# insmod hello.ko
[ 902.474833] Hello World! - init
# rmmod hello
[ 934.470132] Hello World! - exit
```
### 嘗試建立 ko 檔
因為 vcam 需要 videobuf2-vmalloc videobuf2-v4l2 兩個核心模組作為前製,
```
$ sudo make modules_install modules=videobuf2-vmalloc
[sudo] hank 的密碼:
make[1]: *** 沒有規則可製作目標「videobuf2-vmalloc」,由「__modinst」 需求。 停止。
make: *** [Makefile:1941:modules_install] 錯誤 2
```
發現 make modules 也只有少少幾個 ko 檔被編譯出來
```
$ make modules
UPD include/generated/compile.h
CC scripts/mod/empty.o
MKELF scripts/mod/elfconfig.h
HOSTCC scripts/mod/modpost.o
CC scripts/mod/devicetable-offsets.s
HOSTCC scripts/mod/file2alias.o
HOSTCC scripts/mod/sumversion.o
HOSTLD scripts/mod/modpost
CC kernel/bounds.s
CC arch/x86/kernel/asm-offsets.s
CALL scripts/checksyscalls.sh
DESCEND objtool
CC [M] fs/efivarfs/inode.o
CC [M] fs/efivarfs/file.o
CC [M] fs/efivarfs/super.o
CC [M] fs/efivarfs/vars.o
LD [M] fs/efivarfs/efivarfs.o
CC [M] drivers/thermal/intel/x86_pkg_temp_thermal.o
CC [M] net/netfilter/nf_log_syslog.o
CC [M] net/netfilter/xt_mark.o
CC [M] net/netfilter/xt_nat.o
CC [M] net/netfilter/xt_LOG.o
CC [M] net/netfilter/xt_MASQUERADE.o
CC [M] net/netfilter/xt_addrtype.o
CC [M] net/ipv4/netfilter/iptable_nat.o
MODPOST Module.symvers
LDS scripts/module.lds
CC [M] drivers/thermal/intel/x86_pkg_temp_thermal.mod.o
LD [M] drivers/thermal/intel/x86_pkg_temp_thermal.ko
CC [M] fs/efivarfs/efivarfs.mod.o
LD [M] fs/efivarfs/efivarfs.ko
CC [M] net/ipv4/netfilter/iptable_nat.mod.o
LD [M] net/ipv4/netfilter/iptable_nat.ko
CC [M] net/netfilter/nf_log_syslog.mod.o
LD [M] net/netfilter/nf_log_syslog.ko
CC [M] net/netfilter/xt_LOG.mod.o
LD [M] net/netfilter/xt_LOG.ko
CC [M] net/netfilter/xt_MASQUERADE.mod.o
LD [M] net/netfilter/xt_MASQUERADE.ko
CC [M] net/netfilter/xt_addrtype.mod.o
LD [M] net/netfilter/xt_addrtype.ko
CC [M] net/netfilter/xt_mark.mod.o
LD [M] net/netfilter/xt_mark.ko
CC [M] net/netfilter/xt_nat.mod.o
LD [M] net/netfilter/xt_nat.ko
```
<!-- 在 virtme 中無法裝上 v4l2 模組,因為 `/lib/modules/6.3.12/kernel/driver` 中並沒有 media 的目錄
```shell
/lib/modules/6.1.32/kernel/drivers$ ls -a
. .. thermal
``` -->
<!-- 使用 `modinfo` 確認在 host 端所載入的模組位置,確認其是在 `/lib/modules/5.19.0-45-generic/kernel/drivers/media/common/videobuf2/videobuf2-v4l2.ko` 中,這是在 6.3.12 中沒有的路徑,接來就是要想辦法去建構出 6.3.12 版的 videobuf2-v4l2.ko 來
```shell
$ modinfo videobuf2-v4l2
filename: /lib/modules/5.19.0-45-generic/kernel/drivers/media/common/videobuf2/videobuf2-v4l2.ko
```
https://www.kernel.org/doc/Documentation/kbuild/kbuild.txt -->
<!-- 由於原先使用的版本是5.19.0,而虛擬環境是6.1.32,所以在 `lib/modules` 中並沒有 6.1.32 的目錄與相關文件,故在 makefile 中的 `modules_install` target 會變成建立一個 6.1.32 的目錄,並且將 `modules.oreder` 的 ko 檔加入原本的目錄(也就是於只有`modules.oreder` 中的 ko 檔會被建立)
```
modules_install: $(modinst_pre)
PHONY += __modinst_pre
__modinst_pre:
@rm -rf $(MODLIB)/kernel
@rm -f $(MODLIB)/source
@mkdir -p $(MODLIB)/kernel
@ln -s $(abspath $(srctree)) $(MODLIB)/source
@if [ ! $(objtree) -ef $(MODLIB)/build ]; then \
rm -f $(MODLIB)/build ; \
ln -s $(CURDIR) $(MODLIB)/build ; \
fi
@sed 's:^:kernel/:' modules.order > $(MODLIB)/modules.order
@cp -f modules.builtin $(MODLIB)/
@cp -f $(objtree)/modules.builtin.modinfo $(MODLIB)/
endif # CONFIG_MODULES
``` -->
在與教授討論並重新詳閱 kernelconfig 加上反覆驗證後,得知需要在編譯核心時設定 [`CONFIG_MEDIA_SUPPORT = y`](https://www.kernelconfig.io/CONFIG_MEDIA_SUPPORT?kernelversion=6.1.35) 才會編譯出相關的 ko 檔
![](https://hackmd.io/_uploads/B1KJvTMd3.png)
> [videobuf2-vmalloc.ko](https://www.kernelconfig.io/CONFIG_VIDEOBUF_VMALLOC?kernelversion=6.1.35) 與 [videobuf2-v4l2.ko](https://www.kernelconfig.io/CONFIG_VIDEOBUF2_V4L2?q=videobuf2-v4l2.ko&kernelversion=6.1.35&arch=x86) 都依賴於此
<!-- ![](https://hackmd.io/_uploads/Bya3kAzuh.png)
一開始裡面的東西很少,要將 Media device type 內的選項全選才會多出許多選項
![](https://hackmd.io/_uploads/Sy0-gAzO3.png) -->
使用 `/` 搜尋 VIDEOBUF2_V4L2,找出使其 enable 的設定
![](https://hackmd.io/_uploads/r1BxZ0zd3.png)
![](https://hackmd.io/_uploads/ByDOI0fu3.png)
> 需要將自動選擇附加功能的這個選項關掉
![](https://hackmd.io/_uploads/BkYVLRzdn.png)
隨後 make 完使用 make modules_install ,便可見到需要的那兩個 ko 檔
```
root@(none):/# modprobe videobuf2_v4l2 videobuf2_vmalloc
root@(none):/# lsmod
Module Size Used by
videobuf2_vmalloc 16384 0
videobuf2_memops 16384 1 videobuf2_vmalloc
videobuf2_v4l2 36864 0
videobuf2_common 65536 3 videobuf2_vmalloc,videobuf2_v4l2,videobuf2_memops
```
架設成功
### 嘗試在虛擬環境中 make vcam
```
root@(none):/tmp/vcam# make
make -C /lib/modules/6.1.35/build M=/tmp/vcam modules
make[1]: Entering directory '/home/hank/linux2023/virtme/linux'
warning: the compiler differs from the one used to build the kernel
The kernel was built by: x86_64-linux-gnu-gcc (Ubuntu 11.3.0-1ubuntu1~22.04.1) 11.3.0
You are using: gcc (Ubuntu 11.3.0-1ubuntu1~22.04.1) 11.3.0
CC [M] /tmp/vcam/module.o
CC [M] /tmp/vcam/control.o
...
[ 190.819560] Tasks state (memory values in pages):
[ 190.819844] [ pid ] uid tgid total_vm rss pgtables_bytes swapents oom_score_adj name
[ 190.820366] [ 98] 0 98 5780 528 61440 0 0 systemd-udevd
[ 190.820890] [ 163] 0 163 1193 151 45056 0 0 bash
[ 190.821362] [ 169] 0 169 795 77 45056 0 0 make
[ 190.821871] [ 171] 0 171 879 165 45056 0 0 make
[ 190.822344] [ 386] 0 386 897 169 45056 0 0 make
[ 190.822821] [ 409] 0 409 724 38 49152 0 0 sh
[ 190.823284] [ 410] 0 410 939 43 40960 0 0 gcc
[ 190.823756] [ 411] 0 411 18841 9025 188416 0 0 cc1
[ 190.824284] oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),cpuset=/,mems_allowed=0,task=cc1,pid=411,uid=0
[ 190.824992] Out of memory: Killed process 411 (cc1) total-vm:75364kB, anon-rss:35828kB, file-rss:272kB, shmem-rss:0kB, UID:0 pgtables:184kB oom_score_adj:0
gcc: fatal error: Killed signal terminated program cc1
compilation terminated.
make[2]: *** [scripts/Makefile.build:250: /tmp/vcam/control.o] Error 1
make[1]: *** [Makefile:2012: /tmp/vcam] Error 2
make[1]: Leaving directory '/home/hank/linux2023/virtme/linux'
make: *** [Makefile:14: kmod] Error 2
root@(none):/tmp/vcam#
```
發生了 oom (out of memory),由於執行 kmemleak 需要較大的記憶體,故退出虛擬環境利用`virtme-run --kdir . --mods=auto --qemu-opts -m 2048` 配置 2048 的記憶體大小
再度執行 make
```
root@(none):/tmp/vcam# make
make -C /lib/modules/6.1.35/build M=/tmp/vcam modules
make[1]: Entering directory '/home/hank/linux2023/virtme/linux'
warning: the compiler differs from the one used to build the kernel
The kernel was built by: x86_64-linux-gnu-gcc (Ubuntu 11.3.0-1ubuntu1~22.04.1) 11.3.0
You are using: gcc (Ubuntu 11.3.0-1ubuntu1~22.04.1) 11.3.0
CC [M] /tmp/vcam/module.o
CC [M] /tmp/vcam/control.o
CC [M] /tmp/vcam/device.o
CC [M] /tmp/vcam/videobuf.o
CC [M] /tmp/vcam/fb.o
LD [M] /tmp/vcam/vcam.o
MODPOST /tmp/vcam/Module.symvers
CC [M] /tmp/vcam/vcam.mod.o
LD [M] /tmp/vcam/vcam.ko
make[1]: Leaving directory '/home/hank/linux2023/virtme/linux'
cc -O2 -Wall -Wextra -pedantic -std=c99 -o vcam-util vcam-util.c
```
成功通過
隨後將 vcam.ko 架設上,並開始測試
```shell
root@(none):/tmp/vcam# insmod vcam.ko
[65600.023786] vcam: loading out-of-tree module taints kernel.
[65600.026288] Console: switching to colour frame buffer device 80x30
root@(none):/tmp/vcam# lsmod
Module Size Used by
vcam 28672 1
videobuf2_vmalloc 16384 1 vcam
videobuf2_memops 16384 1 videobuf2_vmalloc
videobuf2_v4l2 36864 1 vcam
videobuf2_common 65536 4 videobuf2_vmalloc,vcam,videobuf2_v4l2,videobuf2_memops
root@(none):/tmp/vcam# ./vcam-util -l
Available virtual V4L2 compatible devices:
1. fb0(640,480,1/1,rgb24) -> /dev/video0
```
### kmemleak 的使用與架設
執行 `# mount -t debugfs nodev /sys/kernel/debug/` 後,利用 `mount | grep "debug"` 確認
```shell
root@(none):/# mount | grep "debug"
debugfs on /sys/kernel/debug type debugfs (rw,relatime)
```
確認已成功裝上
<!-- ### 過往被砍掉的東西 -->
<!-- <s>
一開始先在 [The Linux Kernel Archives](https://www.kernel.org/) 下載 linux-6.3.5,並且解壓縮在/usr/src,依照[鳥哥的教學](https://linux.vbird.org/linux_basic/mandrake9/0540kernel-2.4.18.php)似乎是要放在/usr/src/linux,但我的目錄結構中並沒有 /usr/src/linux,而且所有的核心目錄都是在 /usr/src 中,故解壓縮到 /usr/src
</s> -->
<!-- :::warning
不需要特別解壓縮到 `/usr/src` 目錄,在一般使用者的工作目錄編譯核心即可。鳥哥的教學關於 Linux 核心編譯的描述已過時。
:notes: jserv
::: -->
<!-- ![](https://hackmd.io/_uploads/S1B3fQILh.png)
![](https://hackmd.io/_uploads/Hk9Km78Ln.png)
![](https://hackmd.io/_uploads/HkDxVmLLn.png)
-->
<!-- :::warning
不要裝太新的 Linux 核心 (v6.3+),參照 [測試 Linux 核心的虛擬化環境](https://hackmd.io/@sysprog/linux-virtme) 來設定和測試。
:notes: jserv
::: -->