這次們把之前的記憶體字元裝置作一些改變,讓他符合linux典型分層的架構,也就是在sysfs裝置架構裡面右半邊在bus目錄下的裝置和驅動,當然還包括了一個中繼站,字元裝置,提供給user space存取記憶體裝置的方法。這個裝置可以分成三個部份來看,test_bus(註冊bus和提供test_device及test_drive的註冊),test_module(主要是對test_device的包裝和test_driver的實現),test_chrdev(字元裝置),這些在之後都會看到。首先就來看看test_bus部份,他是在test_bus.c這個檔案裡面建構和註冊的,這個檔案也另外提供了test_device和test_driver註冊的方法
static __inline struct test_device *to_test_dev(struct device *dev)
{
struct test_device *tdev = container_of(dev, struct test_device, dev);
return tdev;
}
static __inline struct test_driver *to_test_drv(struct device_driver *drv)
{
struct test_driver *tdrv = container_of(drv, struct test_driver, driver);
return tdrv;
static int test_bus_match(struct device *dev, struct device_driver *drv)
{
struct test_device *tdev = to_test_dev(dev);
return (strcmp(tdev->name, drv->name) == 0);
}
struct bus_type test_bus_type = {
.name = "test_bus",
.match = test_bus_match,
};
EXPORT_SYMBOL(test_bus_type);
struct device test_bus = {
.init_name = "test_bus",
};
EXPORT_SYMBOL(test_bus);
static int __init test_bus_init(void)
{
int ret;
ret = bus_register(&test_bus_type);
if (ret)
goto out;
ret = device_register(&test_bus);
if (ret)
goto bus_dev_fail;
return ret;
bus_dev_fail:
bus_unregister(&test_bus_type);
out:
return ret;
}
static void __exit test_bus_exit(void)
{
device_unregister(&test_bus);
bus_unregister(&test_bus_type);
}
首先建構了test_bus_type結構,成員有bus名稱和一個test_bus_match()的callback function,這個match()函式提供設備註冊時從已經掛在bus上的驅動中搜尋是否有支援目前這個設備的驅動,判斷是否支援的方法是比較設備和驅動的名稱,如果都沒有找到,就會把這些裝置或驅動掛在對應的device的linked-list上。在其他的bus,例如PCI bus在搜尋支援的驅動時則需要比較pci_id_table內的vendor_id和device_id或更多,而驅動註冊時則同樣也會搜尋是否有被支援的設備已經掛在bus下了。這個bus也是一個裝置,因此初始化test_bus的device結構,提供bus裝置的名稱。EXPORT_SYMBOL()的目的是讓其他的module可以利用這個結構或函式。
這個test_bus實際上編寫為一個module,所以也是要有載入和卸載函式的,載入函式,test_bus_init(),主要就是註冊test_bus_type和test_bus,test_bus_exit()則是移除他們。
tese_bus.c裡還定義了test_device的結構,包含一些基本的成員和一組存取這個裝置的操作函式(test_operations),之後實際的裝置會以這個結構的基礎再作功能上擴充。這裡還提供裝置註冊的函式test_device_register(),設定test_device的parent, 所屬的bus和裝置在/sys目錄下的名稱,之後調用kernel提供的裝置註冊函式device_register(),裝置移除函式則直接調用device_unregister()。
struct test_device {
char name[256];
int id;
struct test_operations *ops;
struct device dev;
};
struct test_operations {
int (*transfer)(struct test_device *, struct test_msg *, int);
int (*erase)(struct test_device *);
};
int test_device_register(struct test_device *tdev)
{
int ret;
tdev->dev.parent = &test_bus;
tdev->dev.bus = &test_bus_type;
dev_set_name(&tdev->dev, "%s-%d", tdev->name, tdev->id);
ret = device_register(&tdev->dev);
return ret;
}
EXPORT_SYMBOL(test_device_register);
void test_device_unregister(struct test_device *tdev)
{
device_unregister(&tdev->dev);
}
EXPORT_SYMBOL(test_device_unregister);
驅動註冊的部份工作比較多一點,基本上一定要提供probe()和remove()的方法,其他還有suspend(),resume()...等等的方法實做有關電源管理的部分。probe()在什麼時候會用到呢?當裝置(驅動)註冊且找到支援的驅動(裝置),match成功後就會調用probe()方法,probe()方法主要工作是記憶體空間的分配,資源的申請(request_mem_region),實體位址映射到虛擬位址(ioremap)和中斷的申請(irq_request),remove()則需要釋放申請的所有資源。
struct test_driver {
struct device_driver driver;
int (*probe)(struct test_device *);
int (*remove)(struct test_device *);
};
static int test_drv_probe(struct device *dev)
{
struct test_device *tdev = to_test_dev(dev);
struct test_driver *tdrv = to_test_drv(dev->driver);
return tdrv->probe(tdev);
}
static int test_drv_remove(struct device *dev)
{
struct test_device *tdev = to_test_dev(dev);
struct test_driver *tdrv = to_test_drv(dev->driver);
return tdrv->remove(tdev);
}
int test_driver_register(struct test_driver *tdrv)
{
int ret;
tdrv->driver.owner = THIS_MODULE;
tdrv->driver.bus = &test_bus_type;
if (tdrv->probe)
tdrv->driver.probe = test_drv_probe;
if (tdrv->remove)
tdrv->driver.remove = test_drv_remove;
ret = driver_register(&tdrv->driver);
return ret;
}
EXPORT_SYMBOL(test_driver_register);
void test_driver_unregister(struct test_driver *tdrv)
{
driver_unregister(&tdrv->driver);
}
EXPORT_SYMBOL(test_driver_unregister);
剛剛有講到可是在程式碼裡面沒出現的之後都會有機會遇到,就留待遇到再講了,下次就開始另外的兩個部份,test_module和test_chrdev。
沒有留言:
張貼留言