2014年8月18日 星期一

Linux典型裝置架構-Bus

    這次們把之前的記憶體字元裝置作一些改變,讓他符合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。

沒有留言:

張貼留言