linux设备模型十(bus_device_driver总结)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_16777851/article/details/84574516

前面九章分别对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函数,来通知上层。

总之,基本的驱动模型以及实现。

猜你喜欢

转载自blog.csdn.net/qq_16777851/article/details/84574516