您的位置:首页 > 其它

设备模型5之总线、设备、驱动(实例,简单演示probe过程)

2013-07-10 17:29 337 查看
上一次做了一个kobject实例,这次做的是总线、设备、驱动的实例。这两个例子都比较虚,实际开发很难看见它们。对于一个急于开发某款硬件驱动的人来说意义不大,且对于有实际任务的你也很难静下心来看这些苍白的东西。

 

Ldd3的话:

许多驱动作者将不会需要这里涉及的材料。这个水平的细节通常在总线级别处理,并且很少作者需要添加一个新总线类型。这个信息是有用的,但是,对任何人好奇在
PCI, USB等层面的里面发生了什么或者谁需要在那个级别做改变。

 

我第一次看到这句话时就把设备模型这章跳过了,现在做一些总线的驱动才会感到很多的疑惑,疑惑当然就是总线、设备、驱动、sysfs之间的关系。虽然这些疑惑可能不会影响你开发驱动(很多人包括我认为驱动只是对系统提供的接口的填充,最后主要是对硬件的理解),但请相信我,看完设备模型后,你会感觉自己站在另一个高度去看待驱动。

开场白结束。

 

基本知识:略。

基本函数:网上有。

开发平台:还是那个平台。linux-3.2.36

 

实例结构图:



 

曲线表示对应关系

有个总线叫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)

/****************************attr***************************/

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

/*attr结构

struct bus_attribute {

 struct attribute attr;

 ssize_t (*show)(struct bus_type *bus, char *buf);

 ssize_t (*store)(struct bus_type *bus, const char *buf,

 size_t count);

};

*/

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);

/***********************************************************/

/****************************bus****************************/

//热插拔先不管

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");

    /*

        在device_add中

        if (dev->init_name) {

                dev_set_name(dev, "%s", dev->init_name);

                dev->init_name = NULL;

        }

        dev->init_name的值传给dev->kobj中,自己变为NULL

        我开始这样判断

        !strncmp(dev->init_name, driver->name, strlen(driver->name));

        当然会出现段错误,搞死我了!

    */

    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",

};

/***********************************************************/

/******************************dvc**************************/

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);

/***********************************************************/

/******************************dvr**************************/

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>    /* printk() */

#include <linux/fs.h>        /* everything... */

#include <linux/errno.h>    /* error codes */

#include <linux/types.h>    /* size_t */

#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");

}

/*

        if ((drv->bus->probe && drv->probe) ||

            (drv->bus->remove && drv->remove) ||

            (drv->bus->shutdown && drv->shutdown))

                printk(KERN_WARNING "Driver '%s' needs updating - please use "

                        "bus_type methods\n", drv->name);

这是driver_register中的一段,bus和driver的probe都有会报警告,

我用实例验证是只调用了bus的probe

*/

//这里我们做个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>    /* printk() */

#include <linux/fs.h>        /* everything... */

#include <linux/errno.h>    /* error codes */

#include <linux/types.h>    /* size_t */

#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不说了。

下期预告:

热插拔
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: