目的
前面文章 《嵌入式Linux驅(qū)動(dòng)開發(fā) 03:平臺(tái)(platform)總線驅(qū)動(dòng)模型》 引入了資源和驅(qū)動(dòng)分離的概念,這篇文章將在前面基礎(chǔ)上更進(jìn)一步,引入設(shè)備樹的概念。
基礎(chǔ)說明
在平臺(tái)總線驅(qū)動(dòng)模型中資源和驅(qū)動(dòng)已經(jīng)從邏輯上和代碼組織上進(jìn)行了分離,但每次調(diào)整資源還是會(huì)涉及到內(nèi)核,所以現(xiàn)在更加流行的是設(shè)備樹方式。設(shè)備樹的好處是通過獨(dú)立于內(nèi)核存在,這樣如果設(shè)備上外設(shè)功能啟用與否以及位置變動(dòng)的話很多時(shí)候不用修改與編譯內(nèi)核,只要重新處理設(shè)備樹文件即可。
設(shè)備樹代碼(或者都算不上代碼)只是一些樹狀的數(shù)據(jù),有點(diǎn)像JSON。通常每個(gè)系列的芯片廠家都會(huì)編寫好后綴為 .dtsi
的設(shè)備樹文件,里面把芯片基本上的功能資源都定義了。而對(duì)于某個(gè)具體的電路板來說,只要編寫后綴為 .dts
的文件,在其中引入前述的 .dtsi
文件,然后在 .dts
文件中選擇性啟用 .dtsi
中已經(jīng)定義好的并且電路中需要用到的功能。當(dāng)然在 .dts
文件中也可以自定義新的功能。
.dts
文件最終可以編譯為 .dtb
文件,系統(tǒng)在啟動(dòng)的時(shí)候會(huì)通過Bootloader將該文件傳遞給內(nèi)核,內(nèi)核就會(huì)解析取用其中的資源并與驅(qū)動(dòng)進(jìn)行匹配。如果資源需要調(diào)整,通常只需要調(diào)整 .dts
文件生成新的 .dtb
文件即可。
開發(fā)準(zhǔn)備
本文中演示中涉及目錄與文件結(jié)構(gòu)組織如下:
基本上和前文相同,只需稍作修改。
進(jìn)入源碼目錄:
cd ~/nuc980-sdk/NUC980-linux-5.10.y/
調(diào)整 drivers/user/char_dev
目錄下的 Makefile
文件,其內(nèi)容改為如下:
obj-$(CONFIG_USER_CHAR_DEV) += char_drv.o
設(shè)備樹調(diào)整
在這篇文章中將在設(shè)備樹中創(chuàng)建一個(gè)自己的節(jié)點(diǎn)供下面的驅(qū)動(dòng)程序使用:
# cd ~/nuc980-sdk/NUC980-linux-5.10.y/
gedit arch/arm/boot/dts/nuc980-dev-v1.0.dts
自定義節(jié)點(diǎn)內(nèi)容如下:
nx_node@0 {
compatible = "nx_dts_node";
str = "Naisu 233!";
num = <0x00000000 0x00000020>;
};
nx_node@1 {
compatible = "nx_dts_node";
str = "Hello Naisu!";
num = <0x000000 0x00000040>;
};
完整的設(shè)備樹文件內(nèi)容見文章結(jié)尾。
上面節(jié)點(diǎn)中 compatible
字段的內(nèi)容用于驅(qū)動(dòng)程序查找匹配; str
字段后面的形式是字符串; num
字段后面是數(shù)值,可以用來表示u32或u64(需要用兩個(gè)u32,中間用空格隔開)。
修改完成后編譯然后拷貝到開發(fā)板上進(jìn)行測(cè)試:
# 設(shè)置編譯工具鏈
# export ARCH=arm; export CROSS_COMPILE=arm-buildroot-linux-gnueabi-
# export PATH=$PATH:/home/nx/nuc980-sdk/buildroot-2023.02/output/host/bin
# 編譯生成設(shè)備樹文件
make dtbs
# 編譯完成后拷貝到電腦上再拷貝到SD卡中
# sudo cp arch/arm/boot/dts/nuc980-dev-v1.0.dtb /media/sf_common/
# 我這里開發(fā)環(huán)境和開發(fā)板在同一局域網(wǎng)中,所以可以直接通過網(wǎng)絡(luò)將dtb文件拷貝到開發(fā)板上
# 在開發(fā)板中掛載boot分區(qū)
# mount /dev/mmcblk0p1 /mnt/
# 在ubuntu中使用scp命令拷貝dtb文件到開發(fā)板上
# scp arch/arm/boot/dts/nuc980-dev-v1.0.dtb root@192.168.31.142:/mnt/
# 拷貝完成后重啟開發(fā)板即可測(cè)試
# reboot
可以在 /sys/firmware/devicetree/base/
或者 /proc/device-tree/
目錄下看到被內(nèi)核解析后設(shè)設(shè)備樹節(jié)點(diǎn)信息:
驅(qū)動(dòng)程序與測(cè)試
驅(qū)動(dòng)文件 char_drv.c 內(nèi)容如下:
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
static int major = 0;
static const char *char_drv_name = "char_drv";
static struct class *char_drv_class;
static struct device *char_drv_device;
/* 探測(cè)到資源操作 */
static int char_probe(struct platform_device *pdev)
{
const char *tmp_str;
u64 tmp_num;
printk("NX modlog: file %s, func %s, line %d.\n", __FILE__, __FUNCTION__, __LINE__);
of_property_read_string(pdev->dev.of_node, "str", &tmp_str); // 讀取字符串?dāng)?shù)據(jù)
of_property_read_u64(pdev->dev.of_node, "num", &tmp_num); // 讀取u64內(nèi)容
printk("NX modlog: %s %llu\n", tmp_str, tmp_num);
return 0;
}
/* 移除資源操作 */
static int char_remove(struct platform_device *pdev)
{
printk("NX modlog: file %s, func %s, line %d.\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static const struct of_device_id dts_device_ids[] = {
{ .compatible = "nx_dts_node", } // 通過設(shè)備樹 compatible 字段進(jìn)行匹配
};
/* 定義platform_driver,用于探測(cè)和獲取資源等 */
static struct platform_driver char_driver = {
.probe = char_probe,
.remove = char_remove,
.driver = {
.name = "naisu_char_dev", // 沒有該字段啟動(dòng)時(shí)會(huì)崩潰
.of_match_table = dts_device_ids,
},
};
/* 驅(qū)動(dòng)文件操作接口集合 */
static const struct file_operations char_drv_fops = {
.owner = THIS_MODULE,
};
/* 模塊加載操作 */
static int __init char_drv_init(void)
{
int err;
printk("NX modlog: file %s, func %s, line %d.\n", __FILE__, __FUNCTION__, __LINE__);
major = register_chrdev(0, char_drv_name, &char_drv_fops); // 注冊(cè)字符設(shè)備,第一個(gè)參數(shù)0表示讓內(nèi)核自動(dòng)分配主設(shè)備號(hào)
char_drv_class = class_create(THIS_MODULE, "char_drv_class");
if (IS_ERR(char_drv_class))
{
unregister_chrdev(major, char_drv_name);
return -1;
}
char_drv_device = device_create(char_drv_class, NULL, MKDEV(major, 0), NULL, char_drv_name); // 創(chuàng)建設(shè)備節(jié)點(diǎn)創(chuàng)建設(shè)備節(jié)點(diǎn),成功后就會(huì)出現(xiàn)/dev/char_drv_name的設(shè)備文件
if (IS_ERR(char_drv_device))
{
device_destroy(char_drv_class, MKDEV(major, 0));
unregister_chrdev(major, char_drv_name);
return -1;
}
err = platform_driver_register(&char_driver); // 注冊(cè)platform_driver
return err;
}
/* 模塊退出操作 */
static void __exit char_drv_exit(void)
{
printk("NX modlog: file %s, func %s, line %d.\n", __FILE__, __FUNCTION__, __LINE__);
platform_driver_unregister(&char_driver); // 釋放platform_driver
device_destroy(char_drv_class, MKDEV(major, 0)); // 銷毀設(shè)備節(jié)點(diǎn),銷毀后/dev/下設(shè)備節(jié)點(diǎn)文件就會(huì)刪除
class_destroy(char_drv_class);
unregister_chrdev(major, char_drv_name); // 注銷字符設(shè)備
}
module_init(char_drv_init); // 模塊入口
module_exit(char_drv_exit); // 模塊出口
MODULE_LICENSE("GPL"); // 模塊許可
修改完成后編譯然后拷貝到開發(fā)板上進(jìn)行測(cè)試:
# 設(shè)置編譯工具鏈
# export ARCH=arm; export CROSS_COMPILE=arm-buildroot-linux-gnueabi-
# export PATH=$PATH:/home/nx/nuc980-sdk/buildroot-2023.02/output/host/bin
# 編譯生成內(nèi)核鏡像
make uImage
# 可以根據(jù)電腦配置使用make -jx等加快編譯速度
# 編譯完成后拷貝到電腦上再拷貝到SD卡中
# sudo cp arch/arm/boot/uImage /media/sf_common/
# 我這里開發(fā)環(huán)境和開發(fā)板在同一局域網(wǎng)中,所以可以直接通過網(wǎng)絡(luò)將uImage文件拷貝到開發(fā)板上
# 在開發(fā)板中掛載boot分區(qū)
# mount /dev/mmcblk0p1 /mnt/
# 在ubuntu中使用scp命令拷貝dtb文件到開發(fā)板上
# scp arch/arm/boot/uImage root@192.168.31.142:/mnt/
# 拷貝完成后重啟開發(fā)板即可測(cè)試
# reboot
文章來源:http://www.zghlxwxcb.cn/news/detail-560408.html
總結(jié)
這篇文章簡單試了下通過設(shè)備樹創(chuàng)建資源節(jié)點(diǎn),然后在驅(qū)動(dòng)程序中獲取這些節(jié)點(diǎn)的數(shù)據(jù)。實(shí)時(shí)上關(guān)于設(shè)備樹以及設(shè)備樹下驅(qū)動(dòng)程序資源類型和資源獲取還有很多細(xì)節(jié)的內(nèi)容和操作函數(shù),這些內(nèi)容更多的可以通過使用過程中參考各種已有的驅(qū)動(dòng)代碼來了解,這里就不進(jìn)行展開了。文章來源地址http://www.zghlxwxcb.cn/news/detail-560408.html
設(shè)備樹文件內(nèi)容
/*
* Device Tree Source for NUC980 DEV board
*
* Copyright (C) 2018 Nuvoton Technology Corp.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
/dts-v1/;
#include "nuc980.dtsi"
/ {
model = "Nuvoton NUC980 DEV V1.0";
compatible = "nuvoton,nuc980-dev-v1.0", "nuvoton,nuc980";
chosen {
bootargs = "console=ttyS0,115200n8 noinitrd rootfstype=ext4 root=/dev/mmcblk0p2 rw rootwait mem=64M";
};
apb {
uart1: serial@b0071000 {
status = "disabled";
};
uart2: serial@b0072000 {
status = "disabled";
};
uart3: serial@b0073000 {
status = "disabled";
};
uart4: serial@b0074000 {
status = "disabled";
};
uart5: serial@b0075000 {
status = "disabled";
};
uart6: serial@b0076000 {
status = "disabled";
};
uart7: serial@b0077000 {
status = "disabled";
};
uart8: serial@b0078000 {
status = "disabled";
};
uart9: serial@b0079000 {
status = "disabled";
};
can0: can@b00a0000 {
status = "disabled";
};
can1: can@b00a1000 {
status = "disabled";
};
rtc: rtc@b0041000 {
status = "disabled";
};
gpio: gpio@b0004000 {
pinctrl-0 = <>;
eint2-config = <0 0 0>;
eint3-config = <0 0 0>;
};
nadc: nadc@b0043000 {
status = "disabled";
};
pwm0: pwm@b0058000 {
status = "disabled";
};
pwm1: pwm@b0059000 {
status = "disabled";
};
etimer0: etimer0@b0050000 {
status = "disabled";
};
etimer1: etimer1@b0050100 {
status = "disabled";
};
etimer2: etimer2@b0051000 {
status = "disabled";
};
etimer3: etimer3@b0051100 {
status = "disabled";
};
i2c0: i2c0@b0080000 {
status = "disabled";
};
i2c1: i2c1@b0081000 {
status = "disabled";
pinctrl-0 = <&pinctrl_i2c1_PB>;
};
i2c2: i2c2@b0082000 {
status = "disabled";
pinctrl-0 = <&pinctrl_i2c2_PB>;
};
};
ahb {
usbh_ehci@b0015000 {
pinctrl-0 = <>; /*disable PWREN and OVC*/
ov_active = <1>;/*disable PWREN and OVC*/
status = "okay";
};
usbh_ohci@b0017000{
status = "okay";
};
usbdev@b0016000 {
status = "okay";
};
fmi@b0019000 {
status = "disabled";
};
sdh@b0018000 {
status = "okay";
};
emac0@b0012000 {
status = "okay";
};
emac1@b0022000 {
status = "disabled";
};
ccap0@b0024000 {
status = "disabled";
};
i2c_gpio0: i2c-gpio-0 {
status = "disabled";
};
ccap1@b0014000 {
status = "disabled";
};
i2c_gpio1: i2c-gpio-1 {
status = "disabled";
};
dma@b0008000 {
status = "okay";
};
i2s: i2s@b0020000 {
status = "disabled";
};
i2s_pcm: i2s_pcm {
status = "disabled";
};
sound {
compatible = "nuvoton,nuc980-audio";
i2s-controller = <&i2s>;
i2s-platform = <&i2s_pcm>;
status = "disabled";
};
ebi: ebi@b0010000 {
status = "disabled";
};
};
nx_node@0 {
compatible = "nx_dts_node";
str = "Naisu 233!";
num = <0x00000000 0x00000020>;
};
nx_node@1 {
compatible = "nx_dts_node";
str = "Hello Naisu!";
num = <0x000000 0x00000040>;
};
};
到了這里,關(guān)于嵌入式Linux驅(qū)動(dòng)開發(fā) 04:基于設(shè)備樹的驅(qū)動(dòng)開發(fā)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!