# Linux 核心專題-RPMsg ## 目標簡介 期末專題的預設目標為使用 Linux 核心提供的 rpmsg ,在 milkv-duo256m 這塊嵌入式開發板上建立大小核之間的通訊。並且也有前置步驟需要完成,包括 8051 子系統的功能開通、架設 ADB (android debug bridge) 開發環境。 ## milkv-duo 這是一個僅須 5 美元的嵌入式開發板,裡頭的 SoC 主要包含了兩個 RISC-V 處理器,但是這兩個處理器的規格不同,因此屬於 AMP (Asymmetric Multi Processing) 架構。 我們分別讓這兩顆處理器運行不同的作業系統,功能較完善的大核運行 Linux ,而小核則是運行 FreeRTOS 。 ## rpmsg (Remote Processer Message) 以這次的專題來說,我的目標是要讓 Linux 以及 FreeRTOS 都能啟用 rpmsg 的功能。但是實作上並非如此容易,由於 rpmsg 的概念類似於 OSI 網路堆疊,位在傳輸層的 rpmsg ,底下還有資料連結層負責媒介存取的 Virtio ,還有實體層的 Mailbox 。 因此在 milkv-duo 只預設提供 Mailbox 的情況下,我們還需要額外了解 rpmsg 、 Virtio (以及 Mailbox )的機制,才能動手實作這次的專題。 ## ADB (Android Debug Bridge) 首先 ADB 原先是應用在 Android 裝置的除錯工作上,但是因為其功能強大,例如只要使用 USB 連接裝置就可直接建立連線、方便的命令包含連線裝置終端機以及傳輸檔案。因此在 buildroot 這個常見的 linux 嵌入式環境建立工具 , 也將 ADB 涵蓋在支援的功能當中。 ADB 的運作原理如下, ADB 共由三個行程組成 , 分別是 adb clients, adb server, adb daemon ,前兩者會運行在主機端,後者則是運行在目標端上。 adb clients 屬於 ADB 在主機端的 end point ,負責接收傳進來的命令。 adb server 則是作為 adb clients 以及 adb daemon 之間的 proxy ,在主機端上作為背景程式運行。 adb daemon ,又簡稱為 adbd , 在目標端負責將主機端傳送來的命令交給 shell 執行,並將結果回傳到主機端。 ![Screenshot from 2024-06-21 15-37-12](https://hackmd.io/_uploads/HJn0Tsz8C.png) ### USB gadget drivers 這個是 Linux 所提供用來控制 USB 裝置的 frame work , 從最底層的 USB Control Driver 用來與 USB 裝置控制器 (UDC)溝通 ; 再到中間層的 Gadget driver , 用來設定這個 USB 裝置的資訊、相關行為以及與其相關的 functions ; 最後是上層使用這個 gadget driver 的其他 Linux driver , framework 。 在我們使用的 milkv-duo 當中, USB 裝置已經在 DTS 當中有設置節點,上述的兩個 driver 也存在系統當中,我們唯一需要作的是設定 Gadget driver 的相關資訊,讓它可以使用 USB 裝置並且提供的 function 要包含接下來提到的 functionfs 。 我們會使用 Configfs 來設定。 ### Configfs 在提到 Configfs 之前,我們先簡介一下 sysfs , sysfs 可以讓使用者空間的使用者或行程查看目前使用的核心物件。至於 configfs 則是允許使用者空間可以設定核心物件,並且可以建立以及刪除核心物件。 因此我們利用這項特性來建立我們的 gadget driver , 並且設定它可以配對到唯一的 USB 裝置,以及 function 必須要是 functionfs , 其中 functionfs 是幫助原本 function 的 endpoint 呈現到使用者空間的手段,因此我們再將 adb 這個 function 配置到 functionfs 上即可讓 adbd 在使用者模式下依然可以直接讀取資料。 ### 實作分析 * defconfig 在 milkv-duo256m 所提供的 SDK 當中, SDK 底下的 build 目錄會存放各個開發板對應的設定檔,例如 device tree source (`.dts`) 檔案 以及 Linux 中因設定產生的 `.config` 檔(對應到 defconfig),都會存放在對應開發板的目錄底下。 由於我們這次的 USB gadget 設定想要在使用者空間手動設定,因此我們需要 configfs 幫忙,但是 configfs 支援的方法有限,因此我們就需要在 `build` 目錄下的 defconfig 檔案設定這些 configfs 中的方法啟用與否。我們想要啟用的是 functionfs 這個方法,因此我們作了如下改動: * **cvitek_cv1812cp_milkv_duo256m_sd_defconfig** ```diff CONFIG_USB_CONFIGFS_ACM=y CONFIG_USB_CONFIGFS_F_UAC1=y +CONFIG_USB_CONFIGFS_F_FS=y -# CONFIG_USB_CONFIGFS_F_FS is not set CONFIG_USB_ROLE_SWITCH=y CONFIG_MMC=y ``` * android-tools 這個是 buildroot 所提供的 application package , 因為 milkv `debian` 版本的 linux 沒有像 ubuntu 擁有 `apt` 這麼方便的應用程式工具安裝及管理套件 ,因此若要安裝在 linux 當中, 則必須進入使用 buildroot 當中下達 `make menuconfig` 這個命令,由 Kconfig 生成選單介面並將想要安裝的應用程式選項設定到 `.config` 檔當中,最後 buildroot 在生成環境時也會一並將其載入到 linux 環境當中。 我們要安裝的是 `android-tools` 這個工具包,裡面包含了前述提到的 adb 環境以及 adbd 執行檔。 ![Screenshot from 2024-06-22 17-51-43](https://hackmd.io/_uploads/rkprx748R.png) * USB gadget driver 接著我們就要透過 configfs 來配置屬於我們自己的 gadget driver , 我們會將配置的過程寫成 shell script , 步驟如下: 首先我們要先掛載 configfs ,接著再創建新的 gadget driver ,其中創建 gadget driver 的方法是 `mkdir` ,這個是在 configfs 中新增核心物件的方法。 ```sh # Enale USB ConfigFS mount none $CVI_DIR -t configfs # Create gadget dev mkdir $CVI_GADGET ``` 接著要對 gadget driver 進行描述,包含設定 `VID` , `PID` 來辨別這個裝置、在 `strings` 描述目錄下填寫屬性 (其中 0x409 表示支持各種語言),最後則是這個 gadget 的 configs , 一個 gadget 可以擁有多個 config , 但是每次 function 運作時只能指定一個 configs 作使用。 ```sh # Set the VID and PID echo $VID >$CVI_GADGET/idVendor echo $PID >$CVI_GADGET/idProduct # Set the product information string mkdir $CVI_GADGET/strings/0x409 echo $MANUFACTURER>$CVI_GADGET/strings/0x409/manufacturer echo $PRODUCT>$CVI_GADGET/strings/0x409/product echo $SERIAL>$CVI_GADGET/strings/0x409/serialnumber # Set the USB configuration mkdir $CVI_GADGET/configs/c.1 mkdir $CVI_GADGET/configs/c.1/strings/0x409 echo "config1">$CVI_GADGET/configs/c.1/strings/0x409/configuration # Set the MaxPower of USB descriptor echo 120 >$CVI_GADGET/configs/c.1/MaxPower ``` 這個則是 USB probe 過程最後的步驟,我們要創建 gadget 上的 function , 以此例來說我們 sh 變數 `$CLASS` 會是 `ffs.adb` 意思是說 function 是 adb , 並且要使用 functionfs 的方式將 gadget 的 endpoint 呈現到使用者空間。這裡值得一提的是若 function 沒有註冊到 configfs 當中,則使用 `mkdir` 設定方法時就會被 gadget 拒絕。 ```bash mkdir $CVI_GADGET/functions/$CLASS ``` 再來是 USB start ,因為在 USB probe 的過程當中已經有了 gadget 的設定 (config) 以及它的方法 (function) , 因此我們現在要讓 function 對應到其適合的 config 。 ```sh ln -s $CVI_GADGET/functions/ffs.adb $CVI_GADGET/configs/c.1 ``` 接著是剛才 functionfs 的實作,我們會將 adb 呈現到使用者空間,讓需要的程式可以自行取用資料,以這個案例來說需要使用此裝置的程式就是 adbd , 最後我們再使用 `&` 讓 adbd 得以在背景執行。 ```sh mkdir /dev/usb-ffs/adb -p mount -t functionfs adb /dev/usb-ffs/adb if [ -f $ADBD_PATH/adbd ]; then $ADBD_PATH/adbd & fi ``` 最後則是要尋找可用的 USB Device Controller (UDC) , 並且將其配置給 USB controller driver , 這裡值得一提的是若這個 UDC 正在分配給其他 gadget 使用,則此 UDC 就無法配置給目前這個 gadget driver 。 最後若配置成功, driver 也就可以順利配對到 device , 讓我們 gadget driver 開始正常運作。 ```sh UDC=`ls /sys/class/udc/ | awk '{print $1}'` echo ${UDC} >$CVI_GADGET/UDC ``` ## Mailbox