linux設備模型十(bus_device_driver總結)

前面九章分別對linux驅動模型中的細節部分進行了分析,本節作爲小節,使用一個簡單的例子,分別使用前面分析的內容,實現一個簡單的總線,設備,驅動之間的關係。

 

實現一條總線

#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>

MODULE_LICENSE("Dual BSD/GPL");

static char *Version = "$Revision: 1.9 $";

/* 驅動和設備匹配 */
static int my_match(struct device *dev, struct device_driver *driver)
{
    return !strncmp(dev_name(dev), driver->name, strlen(driver->name));
}



static void my_bus_release(struct device *dev)
{
    printk(KERN_DEBUG "my bus release\n");
}
    
/* 總線也是一個設備 */
struct device my_bus = { 
    .init_name = "my_bus0",        
    .release   = my_bus_release
};

/* 定義一個總線 */
struct bus_type my_bus_type = { 
    .name = "my_bus",
    .match = my_match,
};

/* 導出總線和總線設備 */
EXPORT_SYMBOL(my_bus);
EXPORT_SYMBOL(my_bus_type);


/*
 * Export a simple attribute.
 */
static ssize_t show_bus_version(struct bus_type *bus, char *buf)
{
    return snprintf(buf, PAGE_SIZE, "%s\n", Version);
}

static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);


static int __init my_bus_init(void)
{
    int ret;

        /*註冊總線*/
    ret = bus_register(&my_bus_type);
    if (ret)
        return ret;

    /*創建屬性文件*/
    if (bus_create_file(&my_bus_type, &bus_attr_version))
        printk(KERN_NOTICE "Fail to create version attribute!\n");

    /*註冊總線設備*/
    ret = device_register(&my_bus);
    if (ret)
        printk(KERN_NOTICE "Fail to register device:my_bus!\n");

    return ret;
}

static void my_bus_exit(void)
{
    device_unregister(&my_bus);
    bus_unregister(&my_bus_type);
}

module_init(my_bus_init);
module_exit(my_bus_exit);
                                                    

這裏是註冊一個總線,和前面總線章節那個例子相比,總線作爲一個設備註冊了下來(我們可以sys/devices/目錄下面看到)。

當然也同樣實現了一個屬性文件,可以使用它的讀功能,查看版本。

 

這裏看一下注冊總線前後的對比:

可以看到總線名和設備名都能對應上。

同時因爲這對總線的設備註冊時沒父節點以及掛接到別的bus上的,所以這個設備直接是在/sys/devices/這個目錄下。

同時我們這裏對總線和總線所代表的設備的符號進行了導出,可以讓其他內核模塊使用。

 

 

實現一個掛接在上面總線的設備

#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>


MODULE_LICENSE("Dual BSD/GPL");

/* 使用bus模塊中導出的符號 */
extern struct device my_bus; 
extern struct bus_type my_bus_type;


/* Why need this ?*/
static void my_dev_release(struct device *dev)
{ 
    
}

struct device my_dev = { 
    .bus = &my_bus_type,
    .parent = &my_bus,
    .release = my_dev_release,
};


/*
 * Export a simple attribute.
 */
static ssize_t mydev_show(struct device *dev, struct device_attribute *attr,
            char *buf)
{
    return sprintf(buf, "%s\n", "This is my device!");
}

static DEVICE_ATTR(dev, S_IRUGO, mydev_show, NULL);

static int __init my_device_init(void)
{
    int ret = 0;

    /* 初始化設備 */
    dev_set_name(&my_dev, "my_dev");

    /*註冊設備*/
    ret = device_register(&my_dev);

    /*創建屬性文件*/
    device_create_file(&my_dev, &dev_attr_dev);

    return ret;

}


static void my_device_exit(void)
{
    device_unregister(&my_dev);
}

module_init(my_device_init);
module_exit(my_device_exit);
                                   

這個設備的實現就簡單很多了,初始化時,確定了設備是掛載那個總線下面,其父設備是那個後,註冊這個設備即可。

這裏也實現了一個讀的屬性文件。

設備名叫“my_dev”

 

實現一個掛接在上面總線的驅動程序

#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>


MODULE_LICENSE("Dual BSD/GPL");


/* 使用導出的總線 */
extern struct bus_type my_bus_type;

static int my_probe(struct device *dev)
{
    printk("Driver found device which my driver can handle!\n");
    return 0;
}

static int my_remove(struct device *dev)
{
    printk("Driver found device unpluged!\n");
    return 0;
}

struct device_driver my_driver = { 
    .name = "my_dev",        /* 注意這裏和設備名字一樣 */
    .bus = &my_bus_type,     /* 掛接我們實現的總線 */
    .probe = my_probe,
        .remove = my_remove,
};


/*
 * Export a simple attribute.
 */
static ssize_t mydriver_show(struct device_driver *driver, char *buf)
{
    return sprintf(buf, "%s\n", "This is my driver!");
}

static DRIVER_ATTR(drv, S_IRUGO, mydriver_show, NULL);

static int __init my_driver_init(void)
{
    int ret = 0;

        /*註冊驅動*/
    ret = driver_register(&my_driver);

    /*創建屬性文件*/
    ret = driver_create_file(&my_driver, &driver_attr_drv);

    return ret;

}

static void my_driver_exit(void)
{
    driver_unregister(&my_driver);
}

module_init(my_driver_init);
module_exit(my_driver_exit);
                                 

驅動這裏要注意兩點:

1.同樣綁定了我們實現的總線

2.驅動的name和設備那邊的name一樣

同時我們也知道,probe函數是總線和驅動匹配上後,被調用的。

 

 

實驗測試:

顯示看一下總線以及總線設備安裝前後的對比。

當然這時候總線下面的設備和驅動都是沒東西的。

 

在總線安裝的前提下,安裝設備

這裏要知道的一點是,bus下的設備都是,devices下的符號鏈接,而具體設備裏的subsystme則是bus下的具體總線的符號鏈接。

有了上面兩個,就可以在devices目錄下直接進入bus目錄,bus也可以直接進入devices目錄。

 

 

在總線安裝的前提下,安裝驅動

因爲上面我們先安裝了設備,這裏再安裝驅動。所以驅動安裝完就直接匹配上了設備。

 

 

下面我們先卸載設備。

可以看到,在卸載了設備後,因爲沒了和驅動匹配的設備,所以驅動下面的設備的符號鏈接也被移除了。

 

如果我們分析一下linux中的那條虛擬總線platform總線,會發現,其實和我們上面的實現是一樣的。

只不過platform總選,完善了match的id_table的情況。

platform總線,實現了一個uevnt函數,來通知上層。

總之,基本的驅動模型以及實現。

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章