1. 程式人生 > >我眼中的Linux裝置樹(三 屬性)

我眼中的Linux裝置樹(三 屬性)

三 屬性(property)

device_type = "memory"就是一個屬性,等號前邊是屬性,後邊是值。節點是一個邏輯上相對獨立的實體,屬性是用來描述節點特性的,根據需要一個節點由0個,1個或多個屬性表示節點的特性。一個屬性由名字和值兩部分組成。

和節點的名字類似,規範要求屬性名字由1到31個字元組成。和節點名字字元的種類有些區別,不允許有大寫字母,增加了問號和井號兩個字元。不清楚為什麼沒有和節點名字完全保持一致,井號對於初學者容易誤解,以為是註釋。

0-9    數字
a-z    小寫字母
,    逗號
.    句點(英)
_    下劃線
+    加號
-    破折號(英)
?    問號
#    井號


為了容易區分以及避免重複,標準未定義的屬性名字應該用公司或組織名稱開頭,比如:
fsl,channel-fifo-len
ibm,ppc-interrupt-server#s
linux,network-index

屬性的值在記憶體中由0個或多個位元組儲存。標準定義的基本型別包括:空,u32,u64,字串,<prop-encoded-array>,字元陣列6種。空前邊我們已經提到,當不需要值就可以表示節點的特性時,屬性的值可以為空。u32,u64,字串,字元陣列和c語言的定義沒有區別,注意的是規範要求都是大端表示,字串也是以0x00結尾。<prop-encoded-array>是一個結構體陣列,陣列的元素具體是什麼根據屬性的定義確定,後邊我們講到具體的屬性時會詳細說明。規範中還有一個型別的屬性值,叫<phandle>,這個型別的屬性在記憶體中儲存時本質上是u32。


規範預定義了一些標準的屬性。“compatible”,“model”,"device_type"都是用來表示節點基本資訊的。

“compatible”屬性是用來匹配驅動的,他的型別是字串陣列,每個字串表示一種裝置的型別,從具體到一般。舉個例子就比較清楚了,比如某個串列埠控制器節點的屬性”compatible = “fsl,mpc8641-uart”, “ns16550"“。第一個字串“fsl,mpc8641-uart”前邊部分是廠商(推測是frescale),後邊部分是控制器具體型號,這個形式也是規範建議的標準寫法。第二個字串ns16550表示一類符合同一標準的串列埠控制器,比第一個字串表示的範圍更大。核心匹配驅動時首先看是否有匹配第一個字串的驅動,如果沒有的話再匹配第二個(如果有更多的,依次類推,所以優先匹配前邊的)。


"model"屬性用來表示裝置的型號,用字串表示,不像"compatible"用多個字串,只需一個就夠了。"device_type"屬性用來表示裝置型別,用字串表示。

"#address-cells","#size-cells","reg","ranges","dma-ranges"屬性都是和地址有關的。

不同的平臺,不同的匯流排,地址位長度可能不同,有32位地址,有64位地址,為了適應這個,規範規定一個32位的長度為一個cell。"#address-cells"屬性用來表示匯流排地址需要幾個cell表示,該屬性本身是u32型別的。"#size-cells"屬性用來表示子匯流排地址空間的長度需要幾個cell表示,屬性本身的型別也是u32。可以這麼理解父節點表示匯流排,總線上每個裝置的地址長度以及地址範圍是匯流排的一個特性,用"#address-cells","#size-cells"屬性表示,比如匯流排是32位,那麼"#address-cells"設定成1就可以了。這兩個屬性不可以繼承,就是說在未定義這兩個屬性的時候,不會繼承更高一級父節點的設定,如果沒有設定的話,核心預設認為"#address-cells"為2,"#size-cells"為1。

"reg"屬性用來表示節點地址資源的,比如常見的就是暫存器的起始地址及大小。要想表示一塊連續地址,必須包含起始地址和空間大小兩個引數,如果有多塊地址,那麼就需要多組這樣的值表示。還記得前邊說過的<prop-encoded-array>型別的屬性吧,就是用來幹這個的,他表示一個數組,每個元素的具體格式根據屬性而定,對於'reg'屬性,每個元素是一個二元組,包含起始地址和大小。還有另外一個問題,地址和大小用幾個u32表示呢?這個就由父節點的"#address-cells","#size-cells"屬性確定。

總線上裝置在匯流排地址和匯流排本身的地址可能不同,"ranges"屬性用來表示如何轉換。和'reg'屬性類似,'ranges'屬性也是<prop-encoded-array>型別的屬性,不同的是'ranges'屬性的每個元素是三元組,按照前後順序分別是(子匯流排地址,父匯流排地址,大小)。子匯流排地址需要幾個u32表示由'ranges'屬性所在節點的'#address-cells'屬性決定,父匯流排地址需要幾個u32表示由上一級節點的'#address-cells'屬性決定,大小需要幾個u32表示由當前節點的'#size-cells'屬性確定。

'dma-ranges'屬性的結構和定義與'ranges'屬性完全相同,唯一不同的是地址是dma使用的地址,'ranges'中的地址是cpu使用的地址。

有的時候在一個節點中需要引用另外一個節點,比如某個外設的中斷連在哪個中斷控制器上。在講節點那一節我們說過,可以通過節點的全路徑指定是哪個節點,但這種方法非常繁瑣。'phandle'屬性是專門為方便引用節點設計的,想要引用哪個節點就在該節點下邊增加一個'phandle'屬性,設定值為一個u32,如'phandle = <1>',引用的地方直接使用數字1就可以引用該節點,如'interrupt-parent = <1>'。以上是規範中描述的方法,實際上這樣也不方便,我在實際的程式碼中沒有看到這麼用的。還記得節點那節說過節點名字前邊可以定義一個標籤吧,實際情況是都用標籤引用,比如節點標籤為intc1,那麼用'interrupt-parent = <&intc1>'就可以引用了。

'status'屬性用來表示節點的狀態的,其實就是硬體的狀態,用字串表示。'okay'表示硬體正常工作,“disabled”表示硬體當前不可用,“fail”表示因為出錯不可用,“fail-sss”表示因為某種原因出錯不可用,sss表示具體的出錯原因。實際中,基本只用'okay'和'disabled'。