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