一、描述u-boot驅動模型的數據結構
u-boot有一個功能強大的驅動模型,這一點與linux內核一致。驅動模型對設備驅動相關操作做了一個抽象:使用uclass
來描述設備類,使用driver
來描述驅動,使用udevice
來描述設備。
(1-1)uclass
uclass表示以相同特征方式運行的一組device。uclass提供一種以相同接口方式訪問組內單個設備的方式。例如:GPIO類提供了get/set值的操作。一個I2C類可能有10個I2C端口,其中4個用于一個驅動程序,6個用于另一個驅動程序。
該結構由struct uclass
表示(/include/dm/uclass.h):
structuclass{
void*priv;//這個類的私有數據
structuclass_driver*uc_drv;//類本身的驅動程序,不要與struct driver混淆。
structlist_headdev_head;//該類中的設備列表(當設備的綁定方法被調用時,它們會被附加到它們的類上)。
structlist_headsibling_node;//類鏈表中的下一個類。
};
(1-2)driver
用于提供與外設交互的高級接口。本文將分析這一點。
(1-3)udevice
與特定端口或外圍設備綁定的驅動程序實例
二、聲明驅動
通過分析u-boot的/drivers目錄下的文件可以得出u-boot驅動程序具有共同的特征,驅動程序聲明一般具有如下類似的結構(參見drivers/demo/demo-shape.c):
staticconststructdemo_opsshape_ops={
.hello=shape_hello,
.status=shape_status,
};
U_BOOT_DRIVER(demo_shape_drv)={
.name="demo_shape_drv",
.id=UCLASS_DEMO,
.ops=&shape_ops,
.priv_data_size=sizeof(structshape_data),
};
例如上述代碼所示,首先會創建一個xxx_ops結構,該結構與具體的設備驅動相關。然后使用U_BOOT_DRIVER
宏將其聲明為u-boot驅動。
在U_BOOT_DRIVER中,還可以指定用于綁定和解綁定的方法,這些方法會在適當的時候被調用。對于大多數驅動程序來說,一般只會使用到“probe”和“remove”方法。
設備驅動可以提供的方法記錄在device.h頭文件中:
structdriver{
char*name;//設備名稱。
enumuclass_idid;//指示該驅動屬于哪個uclass。
conststructudevice_id*of_match;//要匹配的兼容字符串列表,以及每個字符串的標識數據。
int(*bind)(structudevice*dev);//調用該函數將設備綁定到其驅動程序。
int(*probe)(structudevice*dev);//用于探測設備,即激活設備。
int(*remove)(structudevice*dev);//調用該函數來移除一個設備。
int(*unbind)(structudevice*dev);//調用該函數來解除設備與驅動程序的綁定。
int(*ofdata_to_platdata)(structudevice*dev);//在probe之前調用該函數以解碼設備樹數據。
int(*child_post_bind)(structudevice*dev);//在一個新子設備被綁后調用。
int(*child_pre_probe)(structudevice*dev);//在probe子設備之前調用。設備已經分配了內存,但還沒有被probe到。
int(*child_post_remove)(structudevice*dev);//在子設備被移除后調用。設備已經分配了內存,但是它的device remove()方法已經被調用。
intpriv_auto_alloc_size;//如果非零,這是在設備的->priv指針中分配的私有數據的大小。如果為零,則驅動負責分配所需的數據。
//如果非零,這是在設備的->platdata中分配的平臺數據的大小。這通常只對設備樹驅動有用(那些有of_match的驅動),因為使用平臺數據的驅動會在U_BOOT_DEVICE()實例化中提供數據。
intplatdata_auto_alloc_size;
//每個設備都可以保存其父設備擁有的私有數據。如果這個值非零,這個將被自動分配。
intper_child_auto_alloc_size;
//bus存儲關于它的子設備的信息。如果非零,該數值則是數據的大小,將分配在子進程的parent_platdata指針指向的區域中。
intper_child_platdata_auto_alloc_size;
//驅動特殊操作。這通常是一個由驅動定義的函數指針列表,用于實現類(uclass)所需要的驅動函數。
constvoid*ops;
//驅動標志。
uint32_tflags;
};
在u-boot中,讓一個設備工作的順序是:
U_BOOT_DRIVER宏創建了一個可從C訪問的數據結構,因此驅動模型可以找到可用的驅動程序。下文將分析該宏的具體實現。
三、U_BOOT_DRIVER宏分析
U_BOOT_DRIVER
宏定義在/include/dm/device.h文件中:
/*DeclareanewU-Bootdriver*/
#defineU_BOOT_DRIVER(__name)
ll_entry_declare(structdriver,__name,driver)
ll_entry_declare
同樣是一個宏定義,用于聲明鏈接器生成的數組項,定義在/include/linker_lists.h中:
#definell_entry_declare(_type,_name,_list)
_type_u_boot_list_2_##_list##_2_##_name__aligned(4)
__attribute__((unused,
section(".u_boot_list_2_"#_list"_2_"#_name)))
-
_type:條目的數據類型。
-
_name:條目的名稱。
-
_list:列表名稱。只包含在C變量中允許的字符。
ll_entry_declare宏聲明了一個變量,該變量被放置在鏈接器生成的數組中。使用此宏聲明的變量必須在編譯時初始化。
此處以/drivers/led目錄下的led_gpio.c
驅動為例,在該文件的末尾使用U_BOOT_DRIVER
進行了驅動聲明:
那么將98行宏定義展開則是:
structdriver_u_boot_list_2_driver_2_led_gpio__aligned(4)
__attribute__((unused,
section(".u_boot_list_2_driver_2_led_gpio")))={
.name="gpio_led",
.id=UCLASS_LED,
.of_match=led_gpio_ids,
.ops=&gpio_led_ops,
.priv_auto_alloc_size=sizeof(structled_gpio_priv),
.bind=led_gpio_bind,
.probe=led_gpio_probe,
.remove=led_gpio_remove,
}
從上述代碼片段可知,宏定義展開后本質則是定義一個struct driver
的驅動結構變量,并初始化結構變量中的元素。然后將其放到.u_boot_list_2_driver_2_led_gpio
節段中。在u-boot源碼/drivers目錄下存在大量使用U_BOOT_DRIVER聲明的驅動,這些驅動會形成一張表(本質為數組)。
至此,分析完U_BOOT_DRIVER這個宏定義,則有一個疑問產生了?u-boot是如何使用這張表的呢?
我們繼續查看/include/linker_lists.h文件,該文件中以宏定義的方式提供了訪問這張表的首元素和尾元素和數組總數的接口:
(1)指向連接器生成數組的第一個條目:
#definell_entry_start(_type,_list)
({
staticcharstart[0]__aligned(4)__attribute__((unused,
section(".u_boot_list_2_"#_list"_1")));
(_type*)&start;
})
(2)指向在連接器生成的數組的最后一個條目的后面:
#definell_entry_end(_type,_list)
({
staticcharend[0]__aligned(4)__attribute__((unused,
section(".u_boot_list_2_"#_list"_3")));
(_type*)&end;
})
(3)返回鏈接器生成數組中條目的數量:
#definell_entry_count(_type,_list)
({
_type*start=ll_entry_start(_type,_list);
_type*end=ll_entry_end(_type,_list);
unsignedint_ll_result=end-start;
_ll_result;
})
綜上,終于撥開迷霧,實則這張表開始的表項則是:u_boot_list_2_drivers_1
,表尾項則是:u_boot_list_2_drivers_3,大量的驅動則在這兩者之間。三者關系如下圖所示:
在u-boot源碼中,當需要操作這張表的時候,則會使用到這三個宏定義來完成這張表的循環遍歷操作。
四、總結
本文描述了u-boot驅動模型的大致組成,重點描述在驅動程序中U_BOOT_DRIVER宏定義的使用以及背后的實現機制。
下回會接著分析u-boot驅動模型是如何起來的,如何“雄霸一方”。
審核編輯:湯梓紅
-
u-boot
+關注
關注
0文章
121瀏覽量
38228 -
數據結構
+關注
關注
3文章
573瀏覽量
40132 -
驅動模型
+關注
關注
0文章
5瀏覽量
7403 -
宏定義
+關注
關注
0文章
50瀏覽量
9013
原文標題:扒一扒u-boot的驅動模型(第一回)
文章出處:【微信號:嵌入式小生,微信公眾號:嵌入式小生】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論