驅動工程師必備技能
摘要:今天偶然看到一篇大佬寫的文章,感覺對從事驅動開發的同學很有參考意義,故稍作精簡後引文到此,供大家圍觀。
作為驅動工程師,主要的工作就是移植各種驅動,接觸各種硬體。接觸最多的就是dts、中斷、gpio、sysfs、proc fs。一個基本合格的驅動工程師,要時刻想着如何利用你平時看的代碼,如何展現在工作的調試中,如何利用sysfs、proc fs及核心提供的接口為我們降低調試難度,快速解決問題。
注:部分代碼分析舉例基于linux-4.15。
1.如何利用dts
首先我們關注的主要是兩點,gpio和irq。其他的選擇忽略。先展示一下期望的gpio和irq的使用方法。
dts如下:
device {
rst-gpio = <&gpioc_ctl 10 OF_GPIO_ACTIVE_LOW>;
irq-gpio = <&gpioc_ctl 11 0>;
interrupts-extended = <&vic 11 IRQF_TRIGGER_RISING>;
};
對于以上的dts你應該再熟悉不過,當然這裡不是教你如何使用dts,而是關注gpio和irq最後一個數字可以如何利用。例如rst-gpio的OF_GPIO_ACTIVE_LOW代表什麼意思呢?可以了解為低有效。什麼意思呢?舉個例子,正常情況下,我們需要一個gpio口控制燈,我們認為燈打開就是active狀态。對于一個程式員來說,我們可以封裝一個函數,寫1就是打開燈,寫0就是關燈。但是對于硬體來說,變化的是gpio口的電平狀态。如果gpio輸出高電平燈亮,那麼這就是高有效。如果硬體設計是gpio輸出低電平燈亮,那麼就是低有效。對于一個軟體工程師來說,我們的期望是寫1就是亮燈,寫0就是關燈。我可不管硬體工程師是怎麼設計的。我們可以認為dts是描述具體的硬體。是以對于驅動來說,硬體的這種變化,隻需要修改dts即可。軟體不用任何修改。軟體可以如下實作。
int device_probe(struct platform_device *pdev)
{
rst_gpio = of_get_named_gpio_flags(np, "rst-gpio", 0, &flags);
if (flags & OF_GPIO_ACTIVE_LOW) {
struct gpio_desc *desc;
desc = gpio_to_desc(rst_gpio);
set_bit(FLAG_ACTIVE_LOW, &desc->flags);
}
irq = of_irq_get(np, 0);
trigger_type = irq_get_trigger_type(irq);
request_threaded_irq(irq, NULL, irq_handler, trigger_type, "irq", NULL);
}
驅動按照以上代碼實作的話,如果修改中斷觸發類型或者電平有效狀态隻需要修改dts即可。例如不同的IC複位電平是不一樣的,有的IC是高電平複位,有的IC卻是低電平複位。其實這就是一個電平有效狀态的例子。
2.如何調試gpio
移植驅動階段或者調試階段的工程中,難免想知道目前gpio的電平狀态。當然很easy。萬用表戳上去不就行了。是啊!硬體工程師的思維。作為軟體工程師自然是要軟體的方法。下面介紹兩個api接口。自己摸索使用吧。點到為止。
static inline int gpio\_export(unsigned gpio, bool direction\_may\_change);
static inline int gpio\_export\_link(struct device \*dev, const char \*name,unsigned gpio);
在你的driver中調用以上api後,編譯下載下傳。去/sys/class/gpio目錄看看有什麼發現。
3.如何調試irq
調試的時候也難免會确定硬體是否産生中斷。我們該怎麼辦呢?也很easy。
cat
輸出資訊不想多餘的介紹。看代碼去。根據輸出的irq num,假設是irq_num。請進入以下目錄。看看下面都有什麼檔案。摸索這些檔案可以為你調試帶來哪些友善。
cd /proc/irq/irq\_num
4.dts和sysfs有什麼關聯
曾經寫過一篇dts解析的文章http://www.wowotech.net/device_model/dt-code-file-struct-parse.html。最後一節其實說了一個很有意思的東西。但是僅僅是寥寥結尾。并沒有展開。因為他不關乎我寫的文章的的主題。但是,他卻和調試息息相關。
cd
該目錄的資訊就是整個dts。将整個dts的節點以及屬性全部展現在sysfs中。他對我們的調試有什麼用呢?Let me tell you。如果你的項目非常的複雜,例如一套代碼相容多種硬體配置。這些硬體配置的差異資訊主要是依靠dts進行區分。當編譯kernel的時候,你發現你很難确定就是你用的是哪個dts。可能你就會憑借自己多年的工作經驗去猜測。是的,你的經驗很厲害。但是,如何确定你的dts資訊是否添加到kernel使用的dts呢?我覺得你應該got it。就到這個目錄去查找是否包含你添加的ndoe和property。dts中有status屬性可以設定某個devicec是否使能。當你發現你的driver的probe沒有執行的時候,我覺得你就需要确定一遍status是不是“ok”、“okay”或者沒有這個屬性。
遠不止我所說的這個功能,還可以判斷目前的硬體是比對哪個dts檔案。你還不去探索一波源碼的設計與實作嗎?節點為什麼出現在某些目錄?為什麼有些節點的屬性cat卻是亂碼?就是亂碼,你沒看錯。至于為什麼。點到為止。
5.sysfs可以看出什麼存在某種問題或陰謀
sysfs有什麼用?sysfs可以看出device是否注冊成功、存在哪些device、driver是否注冊、device和driver是都比對、device比對的driver是哪個等等。先說第一項技能。deivce是否注冊。
就以i2c裝置為例說明。/sys/bus/i2c/devices該目錄下面全是i2c總線下面的devices。如何确定自己的device是否注冊呢?首選你需要确定自己的device挂接的總線是哪個i2c(i2c0, i2c1…)。假設裝置挂接i2c3,從位址假設0x55。那麼devices目錄隻需要檢視是否有3-0055目錄即可。如何确定device是否比對了驅動?進入3-0055目錄,其實你可以看到driver的符号連結。如果沒有,那麼就是沒有driver。driver是否注冊如何确定呢?方法類似。/sys/bus/i2c/drivers目錄就是所有注冊的i2c driver。方法如上。不再列舉。
你以為就這些簡單的功能了嗎?其實不是,還有很多待你探讨。主要是各種目錄之間的關系,device注冊會出現什麼目錄?那麼driver呢?各種符号連結會在那些目錄?等等。
6.如何排查driver的probe沒有執行問題
我想這類問題是移植過程中極容易出現的第一個攔路虎。這裡需要聲明一點。可能有些驅動工程師認為probe沒有執行就是driver沒有注冊成功。其實這兩個沒有半毛錢關系。probe是否執行隻和device和driver的是否比對成功有關。我們有什麼思路排查這類問題呢?主要排查方法可以如下。
1. 通過sysfs确定對應的device是否注冊成功。
2. 通過sysfs确定對應的driver是否注冊成功。
3. 通過sysfs中dts的展開資訊得到compatible屬性的值,然後和driver的compatible進行對比。
如果發現device沒有注冊,如何确定問題。首先通過sysfs中dts展開檔案檢視是否有你添加的device資訊。如果沒有的話,沒有device注冊就是正常的,去找到正确的dts添加正确的device資訊。如果發現sysfs有device的dts資訊。那麼就看看status屬性的值是不是ok的。如果也是ok的。那麼就非常奇怪了。這種情況一般出現在kernel的device注冊路徑出錯了。曾經就有一個小夥伴提出問題,現象就是這樣。最後我幫他确定問題原因是dts章reg屬性的位址是0xc0。這是一個大于0x7f的值。在i2c總線注冊驅動的時候會解析目前總線下的所有device。然後注冊所有的從裝置。就是這裡的位址檢查出了問題,是以這個device沒有注冊上。
如果發現driver沒有注冊,那麼去看看對應的Makefile是否參與編譯。如果參與了編譯,就檢視log資訊,是不是驅動注冊的時候有錯誤資訊。
最後一點的compatible屬性的值隻需要cat一下,然後compare即可。不多說。
7.後記
sysfs還有很多的其他的調試資訊可以檢視。是以,我建議驅動工程師都應該掌握sysfs的使用和原理。看看代碼實作。我始終堅信隻有更好地掌握技術的原理才能更好地利用技術。文章内容不想展開。我告訴你的結果,你永遠記憶不深刻,而且我說的也不一定對。還是需要自己專研。我隻是給你指條明路,剩下的就需要自己去走。最後說一句,代碼不會騙你,還會告訴你别人不能告訴你的。