如果要提供一個方法讓新裝置註冊後在/dev目錄下建立相對應的裝置檔案,kernel為每個bus提供了一個通知的機制,他其實就是一個linked-list的架構,當有新的裝置註冊或是就的裝置移除時,就會走訪這個linked-list下掛的結構,並執行對應的callback function。也就是說test_chrdev如果要被通知,就必須註冊相對應的callback function到test_bus的通知鏈上,我們看看需要怎麼做 。
static int test_notifier_call(struct notifier_block *nb, unsigned long act, void *data)
{
struct device *dev = data;
switch (act) {
case BUS_NOTIFY_ADD_DEVICE:
printk("notify add device\n");
return create_dev(dev, NULL);
case BUS_NOTIFY_DEL_DEVICE:
printk("notify delete device\n");
return destroy_dev(dev, NULL);
}
return 0;
}
struct notifier_block test_notifier = {
.notifier_call = test_notifier_call,
};
test_notifier_call()就是我們的callback function,當有新裝置註冊,這個函式就會被執行,並且傳入BUS_NOTIFY_ADD_DEVICE的動作和註冊的device結構,當接收到這個動作後就可以調用之前提過的create_dev()函式在/dev下建立相對應的裝置檔案。那有裝置移除就會收到BUS_NOTIFY_DEL_DEVICE的動作,也是調用destroy_device()函式移除在/dev目錄下的裝置。notifier_block就是會被掛在每個bus下的linked-list的結構。
那想要這個callback function的被test_bus通知的方式就是在test_chrdev_init()和test_chrdev_exit()裡各增加註冊和移除的函式。
bus_register_notifier(&test_bus_type, &test_notifier);
bus_unregister_notifier(&test_bus_type, &test_notifier);
到這邊Linux的裝置架構也講完了,每一層的分工也大概清楚了,那最後載入所有的module後在/sys目錄下會呈現怎樣的架構呢?請看下圖囉!
這張圖裡面藍色方塊都是預設會有的目錄,其他顏色的方塊是載入所有module後才會出現的目錄,而綠色的方塊其實只是一個symbolic link,它們會指向相對應的橘色方塊的目錄,test_inst在這邊並不會出現,所以用虛線表示,它只有在probe()方法裡面被建立,並沒有被註冊。最後也用一張圖來看一下從user space到底層的存取方式。
從user space一直到test_chrdev的存取方式應該很熟悉,在"一個簡單的字元裝置"曾經講過,而test_chrdev和test_device之間就是透過test_deivce提供的callback functions來溝通了。
這個架構在kernel裡面應用的實例其實很多,例如I2C,SPI Flash,MTD(NOR Flash or NAND Flash),UART...等等。其實他們的abstarction layer已經開發得很完整了,我們只需要拿來直接用就可以了,需要重新開發的部份就是依照我們的platform 的datasheet寫出和硬體有關的部份。
那照這樣講字元裝置的部份平常並不會接觸到囉?當然不是啦!我們在user space要使用這些裝置的時候必須要知道他們在abstarction layer是怎麼調用下層的裝置,並不是所有都可以用單純的read()和write()來完成,舉例來說,I2C裝置因為傳輸格式的關係,read()和write()沒辦法滿足這個需求,所以在abstraction layer幾乎都是用ioctl()跟下層溝通,這樣就要知道傳給ioctl()的命令號和參數(arg)的結構。如果板子上有用到CPLD,且他是接在Parallel Flash Bus上,那字元裝置的mmap()函式就很有用,可以把相對應的記憶體位址映射到user space,之後就直接用這個位址和每個CPLD功能的offset位置操作相對應的功能。所以不要忽視字元裝置,即使他是比較簡單的架構,他在kernel裡也扮演了舉足輕重的角色!
沒有留言:
張貼留言