天天看點

裝置模型5之總線、裝置、驅動(執行個體,簡單示範probe過程)

上一次做了一個kobject執行個體,這次做的是總線、裝置、驅動的執行個體。這兩個例子都比較虛,實際開發很難看見它們。對于一個急于開發某款硬體驅動的人來說意義不大,且對于有實際任務的你也很難靜下心來看這些蒼白的東西。

Ldd3的話:

許多驅動作者将不會需要這裡涉及的材料。這個水準的細節通常在總線級别處理,并且很少作者需要添加一個新總線類型。這個資訊是有用的,但是,對任何人好奇在 PCI, USB等層面的裡面發生了什麼或者誰需要在那個級别做改變。

我第一次看到這句話時就把裝置模型這章跳過了,現在做一些總線的驅動才會感到很多的疑惑,疑惑當然就是總線、裝置、驅動、sysfs之間的關系。雖然這些疑惑可能不會影響你開發驅動(很多人包括我認為驅動隻是對系統提供的接口的填充,最後主要是對硬體的了解),但請相信我,看完裝置模型後,你會感覺自己站在另一個高度去看待驅動。

開場白結束。

基本知識:略。

基本函數:網上有。

開發平台:還是那個平台。linux-3.2.36

執行個體結構圖:

裝置模型5之總線、裝置、驅動(執行個體,簡單示範probe過程)

曲線表示對應關系

有個總線叫mine,它有一個驅動叫my_dev,有兩個裝置叫my_dev0,my_dev1。my_dev0和my_dev1對應驅動my_dev。

先貼兩個.h檔案

//my_bus.h

#ifndef __MY_BUS_H__

#define __MY_BUS_H__

#define CHR_LEN 20

//模仿usb,usb用這個記錄一些特别的裝置

#define MY_DEVICE(vend, prod) \

        .idVendor = (vend), \

        .idProduct = (prod)

//是否用初始化

typedef enum

{

    NO = 0,

    YES = 1,

}initFg;//可以用個bool

struct my_device_id

{

    __u16 idVendor;

    __u16 idProduct;

    initFg initFg; 

};

#define MATCH_ID(id1, id2) ((id1->idVendor == id2->idVendor) & (id1->idProduct == id2->idProduct))

struct my_driver

{

    char const *version;

    struct module *module;

    struct device_driver driver;

    //在usb中沒用device_driver,而是自己定義了usb_driver和struct usb_device_driver等

    struct driver_attribute *version_attr;

    const struct my_device_id *id_table;

};

#define to_my_driver(drv) container_of(drv, struct my_driver, driver)

struct my_device

{

    char const *name;

    const struct my_device_id *id;

    struct device_attribute *attr;

    struct device dev;

    struct my_driver *mydriver;

};

#define to_my_device(dev) container_of(dev, struct my_device, dev)

extern int register_my_device(struct my_device *);

extern void unregister_my_device(struct my_device *);

extern int register_my_driver(struct my_driver *);

extern void unregister_my_driver(struct my_driver *);

#endif//__MY_BUS_H__

//common.h

#ifndef __COMMON_H__

#define __COMMON_H__

#define DEBUG_EN

#define PRINT_COLOR_RESET     "\33[0m"

#define PRINT_COLOR_BLACK     "\33[30m"

#define PRINT_COLOR_RED       "\33[31m"

#define PRINT_COLOR_GREEN     "\33[32m"

#define PRINT_COLOR_YELLOW    "\33[33m"

#define PRINT_COLOR_BLUE      "\33[34m"

#define PRINT_COLOR_MAGENTA   "\33[35m"

#define PRINT_COLOR_CYAN      "\33[36m"

#define PRINT_COLOR_WHITE     "\33[37m"

#define Printk(COLOR, ARGs...) \

    printk(COLOR);             \

    printk(KERN_DEBUG ARGs);   \

    printk(PRINT_COLOR_RESET)  \

#ifdef DEBUG_EN

#define MY_DEBUG(ARGs...) Printk(PRINT_COLOR_YELLOW, ARGs)

#else

#define MY_DEBUG(ARGs...) ;

#endif

#endif//__COMMON_H__

//my_bus.c

#include <linux/device.h>

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/init.h>

#include "my_bus.h"

#include "common.h"

MODULE_AUTHOR("wwxxxxll");

MODULE_LICENSE("Dual BSD/GPL");

struct my_bus_info

{

    struct bus_type my_bus_type;

    struct device my_bus;

    struct bus_attribute *my_bus_attrs_version;

    struct bus_attribute *my_bus_attrs_info;

    char info[CHR_LEN];

};

#define to_my_bus(data) container_of(data, struct my_bus_info, my_bus_type)

static ssize_t show_bus_version(struct bus_type *bus, char *buf)

{

    return snprintf(buf, CHR_LEN, "%s\n", "my_bus-1.0.0");

}

static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);//定義一個attr

static ssize_t show_bus_info(struct bus_type *bus, char *buf)

{

    struct my_bus_info *my_bus_info_p = to_my_bus(bus);

    return snprintf(buf, CHR_LEN, "%s\n", my_bus_info_p->info);

}

static ssize_t store_bus_info(struct bus_type *bus, const char *buf, size_t count)

{

    struct my_bus_info *my_bus_info_p = to_my_bus(bus);

    sscanf(buf, "%s", my_bus_info_p->info);

    return count;

}

//S_IRUGO | S_IWUSR 允許root 來改變參數

static BUS_ATTR(info, S_IRUGO | S_IWUSR, show_bus_info, store_bus_info);

//熱插拔先不管

static int my_bus_uevent(struct device *dev, struct kobj_uevent_env *env)

{

    MY_DEBUG("my_bus_uevent\n");

    return 0;

}

static int my_bus_match(struct device *dev, struct device_driver *driver)

{

    //platform_data會在my_dvc.c中指派

    struct my_device *mydev = (struct my_device*)dev->platform_data;

    MY_DEBUG("my_bus_match\n");

    if (!strncmp(mydev->name, driver->name, strlen(driver->name)))

    {

        mydev->mydriver = to_my_driver(driver);

        return 1;

    }

    return 0;

}

static void my_bus_release(struct device *dev)

{

    MY_DEBUG("my_bus_release\n");

}

static struct my_bus_info my_bus_info_data =

{

    .my_bus_type =

    {

        .name = "mine",

        .match = my_bus_match,

        .uevent = my_bus_uevent,

    },

    .my_bus =

    {

        .init_name = "mine0",

        .release  = my_bus_release,

    },

    .my_bus_attrs_version = &bus_attr_version,

    .my_bus_attrs_info = &bus_attr_info,

    .info = "Hello World",

};

int register_my_device(struct my_device *mydev)

{

    int ret = 0;

    mydev->dev.bus = &my_bus_info_data.my_bus_type;

    mydev->dev.parent = &my_bus_info_data.my_bus;

    ret = device_register(&mydev->dev);

    if (ret)

    {

        return ret;

    }

    if (mydev->attr == NULL)

    {

        return ret;

    }

    ret = device_create_file(&mydev->dev, mydev->attr);

    if (ret)

    {

        device_unregister(&mydev->dev);

    }

    return ret;

}

EXPORT_SYMBOL(register_my_device);

void unregister_my_device(struct my_device *mydev)

{

    device_unregister(&mydev->dev);

}

EXPORT_SYMBOL(unregister_my_device);

int register_my_driver(struct my_driver *driver)

{

    int ret = 0;

    driver->driver.bus = &my_bus_info_data.my_bus_type;

    ret = driver_register(&driver->driver);

    if (ret)

        return ret;

    if (driver->version_attr == NULL)

    {

        return ret;

    }

    ret = driver_create_file(&driver->driver, driver->version_attr);\

    if (ret)

    {

        driver_unregister(&driver->driver);

    }

    return ret;

}

EXPORT_SYMBOL(register_my_driver);

void unregister_my_driver(struct my_driver *driver)

{

    driver_unregister(&driver->driver);

}

EXPORT_SYMBOL(unregister_my_driver);

static int __init my_bus_init(void)

{

    int ret;

    ret = bus_register(&my_bus_info_data.my_bus_type);

    if (ret)

        return ret;

    ret = bus_create_file(&my_bus_info_data.my_bus_type, my_bus_info_data.my_bus_attrs_version);

    if (ret)

        goto fail;

    ret = bus_create_file(&my_bus_info_data.my_bus_type, my_bus_info_data.my_bus_attrs_info);

    if (ret)

        goto fail1;

    ret = device_register(&my_bus_info_data.my_bus);

    if (ret)

    {

        goto fail2;

    }

    return 0;

fail2:

    bus_remove_file(&my_bus_info_data.my_bus_type, my_bus_info_data.my_bus_attrs_info); 

fail1:

    bus_remove_file(&my_bus_info_data.my_bus_type, my_bus_info_data.my_bus_attrs_version);

fail:

    bus_unregister(&my_bus_info_data.my_bus_type);

    return ret;

}

static void __exit my_bus_exit(void)

{

    device_unregister(&my_bus_info_data.my_bus);

    bus_remove_file(&my_bus_info_data.my_bus_type, my_bus_info_data.my_bus_attrs_info);

    bus_remove_file(&my_bus_info_data.my_bus_type, my_bus_info_data.my_bus_attrs_version);

    bus_unregister(&my_bus_info_data.my_bus_type);

}

module_init(my_bus_init);

module_exit(my_bus_exit);

//調試bus

# insmod my_bus.ko

# ls /sys/bus/

mine      platform

有我們的mine

你可以試試version 和 info,我就不做了

//my_dvr.c

#include <linux/module.h>

#include <linux/device.h>

#include <linux/init.h>

#include <linux/kernel.h>   

#include <linux/fs.h>       

#include <linux/errno.h>   

#include <linux/types.h>   

#include "my_bus.h"

#include "common.h"

MODULE_AUTHOR("wwxxxxll");

MODULE_LICENSE("Dual BSD/GPL");

//假設一個裝置需要特殊的初始化

static const struct my_device_id my_device_list[] = {

    {MY_DEVICE(0x0002, 0x0002), YES},

    {}

};

static ssize_t show_version(struct device_driver *driver, char *buf)

{

    struct my_driver *ldriver = to_my_driver(driver);

    sprintf(buf, "%s\n", ldriver->version);

    return strlen(buf);

}

static DRIVER_ATTR(version, S_IRUGO, show_version, NULL);//不說了

static bool special_match(const struct my_device_id *id, const struct my_device_id *sdvc_list)

{

    int i = sizeof(my_device_list);

    //雖然隻有一個,我們還是認為它有很多

    while(i--)

    {

        if (MATCH_ID(sdvc_list, id))

        {

            return 1;

        }

        sdvc_list++;

    }

    return 0; 

}

//特殊器件初始化

static void special_init(void)

{

    MY_DEBUG("special_init\n");

}

//這裡我們做個probe的例子

static int my_dvr_probe(struct device *dev)

{

    struct my_device *mydev = (struct my_device*)dev->platform_data;

    MY_DEBUG("my_dvr_probe\n");//隻是列印太沒意思,是以做了一個特殊裝置判斷

    if (special_match(mydev->id, mydev->mydriver->id_table))//mydev->mydriver的指派在bus的match中

    {

        special_init();

    }

    return 0;

}

static int my_dvr_remove(struct device *dev)

{

    MY_DEBUG("my_dvr_remove\n");

    return 0;

}

static struct my_driver my_dvr_driver = {

    .version = "mydvr: 1.0.0",

    .module = THIS_MODULE,

    .driver = {

        .name = "my_dev",

        .probe = my_dvr_probe,

        .remove = my_dvr_remove,

    },

    .version_attr = &driver_attr_version,

    .id_table = my_device_list,

};

int my_dvr_init(void)

{

    return register_my_driver(&my_dvr_driver);

}

void my_dvr_cleanup(void)

{

    unregister_my_driver(&my_dvr_driver);

}

module_init(my_dvr_init);

module_exit(my_dvr_cleanup);

//調試my_dvr.c

# insmod my_dvr.ko

# ls /sys/bus/mine/drivers

my_dev

# ls /sys/bus/mine/drivers/my_dev/

bind     uevent   unbind   version

有了my_dev驅動

//my_dvc.c

#include <linux/module.h>

#include <linux/device.h>

#include <linux/init.h>

#include <linux/kernel.h>   

#include <linux/fs.h>       

#include <linux/errno.h>   

#include <linux/types.h>   

#include "my_bus.h"

#include "common.h"

MODULE_AUTHOR("wwxxxxll");

MODULE_LICENSE("Dual BSD/GPL");

//看到下面這個,是不是想起usb驅動

#define VENDOR_ID1      0x0001

#define PRODUCT_ID1     0x0001

#define VENDOR_ID2      0x0002

#define PRODUCT_ID2     0x0002

static const struct my_device_id my_dvc_table[] = {

        {MY_DEVICE(VENDOR_ID1, PRODUCT_ID1), NO},

        {MY_DEVICE(VENDOR_ID2, PRODUCT_ID2), NO},

        {}

};

MODULE_DEVICE_TABLE(my_dvc, my_dvc_table);

static ssize_t my_dvc_show_info(struct device *devp, struct device_attribute *attr, char *buf)

{

    return snprintf(buf, strlen("test device\n"), "test device\n");

}

//生成/sys/bus/mine/devices/my_dev0/info,老玩意了,不調試

static DEVICE_ATTR(info, S_IRUGO, my_dvc_show_info, NULL);

static void my_dev_release(struct device *dev)

{

    struct my_device *mydev = (struct my_device*)dev->platform_data;

    MY_DEBUG("%s_release\n", mydev->name);

}

static struct  my_device my_dvcs[2] =

{

    {   

        .name = "my_dev0",

        .id = my_dvc_table,

        .dev =

        {

            .platform_data = &my_dvcs[0],

            .release = my_dev_release,

            .init_name = "my_dev0",

        },

        .attr = &dev_attr_info,

    },

    {

        .name = "my_dev1",

        .id = &my_dvc_table[1],

        .dev =

        {

            .platform_data = &my_dvcs[1],

            .release = my_dev_release,

            .init_name = "my_dev1",

        },

    },

};

int my_dvc_init(void)

{

    int i = 0, ret = 0;

    for (i = 0; i < 2; i++)

    {

        if ((ret = register_my_device(&my_dvcs[i])))

        {

            break;

        }

    }

    if (ret)

    {

        while(--i)

        {

            unregister_my_device(&my_dvcs[i]);

        }

    }

    return ret; 

}

void my_dvc_cleanup(void)

{

    int i=0;

    for (i = 0; i < 2; i++) {

        unregister_my_device(&my_dvcs[i]);

    }

}

module_init(my_dvc_init);

module_exit(my_dvc_cleanup);

//調試my_dvc.c

# insmod my_dvc.ko

#dmesg

my_bus_uevent

my_bus_match

my_dvr_probe

my_bus_uevent

my_bus_match

my_dvr_probe

special_init

前三個是my_dev0

後四個是my_dev1,它是特殊裝置,是以調用special_init

檢視一下

# ls /sys/bus/mine/devices/     

my_dev0  my_dev1

# ls /sys/bus/mine/devices/my_dev0

driver     info       power      subsystem  uevent

# ls /sys/bus/mine/devices/my_dev1

driver     power      subsystem  uevent

看一下drivers

# ls -l /sys/bus/mine/drivers/my_dev/*

--w-------    1 0        0             4096 Jan  1 00:03 /sys/bus/mine/drivers/my_dev/bind

lrwxrwxrwx    1 0        0                0 Jan  1 00:03 /sys/bus/mine/drivers/my_dev/my_dev0 -> ../../../../devices/mine0/my_dev0

lrwxrwxrwx    1 0        0                0 Jan  1 00:03 /sys/bus/mine/drivers/my_dev/my_dev1 -> ../../../../devices/mine0/my_dev1

--w-------    1 0        0             4096 Jan  1 00:03 /sys/bus/mine/drivers/my_dev/uevent

--w-------    1 0        0             4096 Jan  1 00:03 /sys/bus/mine/drivers/my_dev/unbind

-r--r--r--    1 0        0             4096 Jan  1 00:03 /sys/bus/mine/drivers/my_dev/version

看到連接配接了吧

# rmmod my_dvc

rmmod: remove 'my_bus': Resource temporarily unavailable

//my_bus沒解除安裝成功,應該是my_dvr用到了my_bus中的

extern int register_my_driver(struct my_driver *);

extern void unregister_my_driver(struct my_driver *);

是以會報Resource temporarily unavailable,先這樣

#dmesg

...

my_dvr_remove

my_bus_uevent

my_dev0_release

my_dvr_remove

my_bus_uevent

my_dev1_release

//同時解除安裝 my_dvr

//補一個解除安裝my_bus

#rmmod my_bus

#dmesg

...

my_bus_release

 代碼下載下傳:

 http://download.csdn.net/detail/xxxxxlllllxl/5736659

用的vs編輯,裡面有^M符号,不要管它。

class不說了。

下期預告:

熱插拔

繼續閱讀