1. 程式人生 > >linux裝置模型十(bus_device_driver總結)

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函式,來通知上層。

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