【Linux筆記】設(shè)備樹(shù)實(shí)例分析
點(diǎn)擊上方「嵌入式大雜燴」,選擇「置頂公眾號(hào)」第一時(shí)間查看編程筆記!
前言
我們可以從LED程序中榨取很多知識(shí):基本的驅(qū)動(dòng)框架、驅(qū)動(dòng)的簡(jiǎn)單分層、驅(qū)動(dòng)的分層+分離思想、總線設(shè)備驅(qū)動(dòng)模型、設(shè)備樹(shù)等。這大多都是結(jié)合韋老師的教程學(xué)的。
這篇筆記結(jié)合第6個(gè)demo(基于設(shè)備樹(shù)
)來(lái)學(xué)習(xí)、分析:
框圖
下面是LED程序的幾個(gè)層次結(jié)構(gòu)圖:
注意:層與層之間的箭頭指向是相對(duì)的,從哪指向哪看你怎么理解。比如有兩個(gè)函數(shù):函數(shù)A和函數(shù)B,我們可以說(shuō)函數(shù)A調(diào)用函數(shù)B,也可以說(shuō)函數(shù)B被函數(shù)A調(diào)用。
本篇筆記基于第⑤個(gè)圖來(lái)分析。
體驗(yàn)設(shè)備樹(shù)
我們先來(lái)體驗(yàn)一下使用設(shè)備樹(shù)描述引腳信息的方式來(lái)點(diǎn)燈。
以百問(wèn)網(wǎng)開(kāi)發(fā)板為例,修改內(nèi)核目錄Linux-4.9.88/arch/arm/boot/dts
下的100ask_imx6ull-14x14.dts
(百問(wèn)科技開(kāi)發(fā)板出廠帶的設(shè)備樹(shù)文件)設(shè)備樹(shù)文件。
把出廠帶的設(shè)備樹(shù)文件的led相關(guān)節(jié)點(diǎn)給屏蔽掉,然后添加如下節(jié)點(diǎn)信息至根節(jié)點(diǎn):
#define GROUP_PIN(g,p) ((g<<16) | (p))
100ask_led@0 {
compatible = "100as,leddrv";
pin = <GROUP_PIN(5, 3)>;
};
修改后的設(shè)備樹(shù)文件內(nèi)容如:
在內(nèi)核根目錄下使用如下命令編譯設(shè)備樹(shù)源文件:
make dtbs V=1
然后把設(shè)備樹(shù)文件與可加載的led驅(qū)動(dòng)模塊、led應(yīng)用程序上傳到板子里:
上傳成功的文件如下:
運(yùn)行測(cè)試:
實(shí)驗(yàn)過(guò)程分析
這個(gè)實(shí)驗(yàn)的led驅(qū)動(dòng)同樣依賴的是總線設(shè)備驅(qū)動(dòng)模型
。
我們?cè)?a target="_blank" textvalue="【Linux筆記】總線設(shè)備驅(qū)動(dòng)模型" tab="innerlink" data-linktype="2" rel="nofollow">【Linux筆記】總線設(shè)備驅(qū)動(dòng)模型中也有提到描述設(shè)備有兩種方法:一種是直接用platform_device結(jié)構(gòu)體來(lái)指定,另一種是用設(shè)備樹(shù)來(lái)指定。
在本次的實(shí)驗(yàn)中我們就是用設(shè)備樹(shù)來(lái)描述設(shè)備。
之前我們用platform_device結(jié)構(gòu)體來(lái)指定設(shè)備信息時(shí),platform_driver是直接從platform_device結(jié)構(gòu)體里拿資源的,如:
現(xiàn)在我們用設(shè)備樹(shù)來(lái)指定設(shè)備信息時(shí),platform_driver是如何獲取相關(guān)資源的呢?大致過(guò)程如下:
這里我們還需要注意的一點(diǎn)是:并不是所有的設(shè)備樹(shù)節(jié)點(diǎn)都可以轉(zhuǎn)換為platform_device。下面看看幾條規(guī)則:
-
根節(jié)點(diǎn)下含有 compatile 屬性的子節(jié)點(diǎn) 能轉(zhuǎn)換為platform_device
; -
含有特定 compatile 屬性(它的值是 "simple-bus","simplemfd","isa","arm,amba-bus" 四者之一)的節(jié)點(diǎn)的子節(jié)點(diǎn) 能轉(zhuǎn)換為platform_device
; -
I2C、 SPI 總線節(jié)點(diǎn)下的子節(jié)點(diǎn) 不不不能轉(zhuǎn)換為platform_device
,這些總線下的子節(jié)點(diǎn), 應(yīng)該交給對(duì)應(yīng)的總線驅(qū)動(dòng)程序來(lái)處理。
下面看一個(gè)例子:
接下來(lái),我們簡(jiǎn)單來(lái)看一下platform_device與platform_driver匹配的函數(shù):
這里有幾種匹配方式,其它幾種匹配方式在之前的筆記【Linux筆記】總線設(shè)備驅(qū)動(dòng)模型中也有簡(jiǎn)單地分析過(guò)。
這里,我們來(lái)看第二種匹配方式(使用設(shè)備樹(shù)時(shí)的匹配方式)。下面看看具體如何匹配:
其中過(guò)程①優(yōu)先匹配,其次是過(guò)程②,最后是過(guò)程③。
但是,實(shí)際上現(xiàn)在主要使用的是過(guò)程①的匹配,即匹配compatible屬性。
過(guò)程②與過(guò)程③已經(jīng)過(guò)時(shí)了,Linux內(nèi)核不推薦使用這兩種匹配方法。
這一點(diǎn)我們?cè)谏弦黄墓P記【Linux筆記】設(shè)備樹(shù)基礎(chǔ)知識(shí)中也有簡(jiǎn)單提到。
在本次實(shí)驗(yàn)中,我們的匹配示意圖如下:
實(shí)驗(yàn)代碼
代碼大多與之前的【Linux筆記】LED驅(qū)動(dòng)(總線設(shè)備驅(qū)動(dòng)模型)中的代碼一樣,這里也來(lái)簡(jiǎn)單看一下。
1、應(yīng)用程序ledtest.c:
int main(int argc, char **argv)
{
int fd;
char status;
/* 1. 判斷參數(shù) */
if (argc != 3)
{
printf("Usage: %s <dev> <on | off>\n", argv[0]);
return -1;
}
/* 2. 打開(kāi)文件 */
fd = open(argv[1], O_RDWR);
if (fd == -1)
{
printf("can not open file %s\n", argv[1]);
return -1;
}
/* 3. 寫(xiě)文件 */
if (0 == strcmp(argv[2], "on"))
{
status = 1;
write(fd, &status, 1);
}
else
{
status = 0;
write(fd, &status, 1);
}
close(fd);
return 0;
}
運(yùn)行測(cè)試命令:
./ledtest /dev/100ask_led0 on
./ledtest /dev/100ask_led0 off
int main(int argc, char **argv)
形式的main函數(shù)相關(guān)筆記:main()函數(shù)有哪幾種形式?。
2、驅(qū)動(dòng)層leddrv.c
這一層主要是放一些通用的驅(qū)動(dòng)操作函數(shù),核心代碼如:
驅(qū)動(dòng)程序入口函數(shù):
open、write函數(shù):
其它代碼:
其中l(wèi)ed的操作結(jié)構(gòu)體如下:
3、硬件層:chip_demo_gpio.c
這一層主要是一些寄存器相關(guān)的操作,及platform_driver相關(guān)。與上一個(gè)實(shí)驗(yàn)代碼不同的部分就是這個(gè)文件。
(1)驅(qū)動(dòng)初始化函數(shù):
(2)probe函數(shù):
當(dāng)設(shè)備樹(shù)的compatible屬性與platform_driver中的設(shè)備匹配表中的compatible成員互相匹配時(shí)會(huì)執(zhí)行此函數(shù)獲取設(shè)備信息。
這里的pin屬性與compatible屬性(標(biāo)準(zhǔn)屬性)類(lèi)別不同,pin屬性是個(gè)自定義屬性。
我們可以使用of_property_read_u32
函數(shù)來(lái)獲取這些自定義屬性的內(nèi)容。
與設(shè)備樹(shù)相關(guān)的讀取函數(shù)我們?cè)谏弦黄P記【Linux筆記】設(shè)備樹(shù)基礎(chǔ)知識(shí)中也有詳細(xì)介紹。
這些函數(shù)大多在文件 include/linux/of.h
中可以找到:
(3)led寄存器操作相關(guān)的代碼:
/* 寄存器物理地址 */
#define CCM_CCGR1_BASE (0X020C406C)
#define SW_MUX_GPIO5_IO03_BASE (0X02290014)
#define GPIO5_DR_BASE (0X020AC000)
#define GPIO5_GDIR_BASE (0X020AC004)
/* 映射后的寄存器虛擬地址指針 */
static void __iomem *CCM_CCGR1;
static void __iomem *SW_MUX_GPIO5_IO03;
static void __iomem *GPIO5_DR;
static void __iomem *GPIO5_GDIR;
/* 初始化LED, which-哪個(gè)LED */
static int board_demo_led_init (int which)
{
int group, pin;
unsigned int val;
group = GROUP(g_ledpins[which]);
pin = PIN(g_ledpins[which]);
printk("init gpio: group %d, pin %d\n", group, pin);
/* 100ask_IMX6uLL_Board LED:GPIO5_3 */
if ((5 == group) && (3 == pin))
{
/* 相關(guān)寄存器物理地址與虛擬地址之間的映射 */
/* 1、地址映射:時(shí)鐘寄存器 */
CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);
/* 2、地址映射:模式寄存器 */
SW_MUX_GPIO5_IO03 = ioremap(SW_MUX_GPIO5_IO03_BASE, 4);
/* 3、地址映射:數(shù)據(jù)寄存器 */
GPIO5_DR = ioremap(GPIO5_DR_BASE, 4);
/* 地址映射:方向寄存器 */
GPIO5_GDIR = ioremap(GPIO5_GDIR_BASE, 4);
/* 使能GPIO5時(shí)鐘 */
val = readl(CCM_CCGR1); /* 讀出當(dāng)前CCM_CCGR1配置值 */
val &= ~(3 << 30); /* 清除以前的設(shè)置 */
val |= (3 << 30); /* 設(shè)置新值 */
writel(val, CCM_CCGR1);
/* 設(shè)置GPIO5_IO03的為IO模式 */
writel(5, SW_MUX_GPIO5_IO03);
/* 設(shè)置GPIO5_IO03方向?yàn)檩敵?nbsp;*/
val = readl(GPIO5_GDIR);
val &= ~(1 << 3);
val |= (1 << 3);
writel(val, GPIO5_GDIR);
}
else
{
printk("This is not 100ask_IMX6ULL_Board!\n");
}
return 0;
}
/* 控制LED, which-哪個(gè)LED, status:1-亮,0-滅 */
static int board_demo_led_ctl (int which, char status)
{
int group, pin;
unsigned int val;
group = GROUP(g_ledpins[which]);
pin = PIN(g_ledpins[which]);
printk("init gpio: group %d, pin %d\n", group, pin);
/* 100ask_IMX6uLL_Board LED:GPIO5_3 */
if ((5 == group) && (3 == pin))
{
/* 點(diǎn)燈 */
if (1 == status)
{
printk("<<<<<<<<led on>>>>>>>>>>\n");
val = readl(GPIO5_DR);
val &= ~(1 << 3);
writel(val, GPIO5_DR);
}
/* 滅燈 */
else if (0 == status)
{
printk("<<<<<<<<led off>>>>>>>>>>\n");
val = readl(GPIO5_DR);
val|= (1 << 3);
writel(val, GPIO5_DR);
}
else{}
}
else
{
printk("This is not 100ask_IMX6ULL_Board!\n");
}
return 0;
}
4、Makefile文件
運(yùn)行測(cè)試
這在文章開(kāi)頭的體驗(yàn)設(shè)備樹(shù)
一節(jié)中也有演示測(cè)試結(jié)果:
(話說(shuō)我好像還沒(méi)給板子露過(guò)面,這下來(lái)點(diǎn)個(gè)燈露個(gè)面)
同時(shí),在目錄/sys/firmware/devicetree/base
下,我們可以查看設(shè)備樹(shù)節(jié)點(diǎn):
可以看到,我們創(chuàng)建的設(shè)備樹(shù)節(jié)點(diǎn)100ask_led@0
也在該目錄下。100ask_led@0
節(jié)點(diǎn)本身就是一個(gè)文件夾,可以使用cd命令進(jìn)入該文件夾查看該節(jié)點(diǎn)的屬性信息:
屬性值是字符串時(shí),用 cat 命令可以打印出來(lái);屬性值是數(shù)值時(shí),用 hexdump 命令可以打印出來(lái)。
最后
以上就是本次的實(shí)驗(yàn)分享。如有錯(cuò)誤,歡迎指出!謝謝
本篇筆記會(huì)同步至我的個(gè)人博客:https://www.lizhengnian.cn/中,歡迎來(lái)訪。
原創(chuàng)不易,期待您的在看、分享~
參考/學(xué)習(xí)資料:
《嵌入式Linux應(yīng)用開(kāi)發(fā)完全手冊(cè)第2版_韋東山》
Linux-4.9.88
往期筆記:
基于LiteOS的智慧農(nóng)業(yè)案例實(shí)驗(yàn)分享
【Linux筆記】通俗易懂的Linux驅(qū)動(dòng)基礎(chǔ)
【Linux筆記】pc機(jī)_開(kāi)發(fā)板_ubuntu互ping實(shí)驗(yàn)
【Linux筆記】掛載網(wǎng)絡(luò)文件系統(tǒng)
學(xué)習(xí)STM32的一些經(jīng)驗(yàn)分享
后臺(tái)回復(fù):加群。添加ZhengN微信,加入技術(shù)交流群
點(diǎn)個(gè)贊,證明你還愛(ài)我
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!