总线设备驱动模型
总线是主机和设备之间的通道,由bus_type结构描述。
int bus_register(struct bus_type *bus) 总线的注册,若成功,新的总线将被添加进系统,并可在sysfs的/sys/bus 下看到。
void bus_unregister(struct bus_type *bus) 总线的删除。
int (*match) (struct device *dev, struct device_driver *drv) 比较设备dev和驱动drv是否匹配,驱动能否处理这个设备。
int (*event) (struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size)在为用户空间产生热插拔事件之前,这个方法允许总线添加环境变量。
总线属性由结构bus_attribute描述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);
}; 类似于前面的kobject。
int bus_create_file(struct bus_type *bus, struct bus_attribute *attr) 创建总线属性
void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr) 删除总线属性
例子:在系统中创建一条总线
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
static char *Version = "$Revision: 1.0 $";
static int my_match(struct device *dev, struct device_driver *driver)
{
return !strncmp(dev->bus_id, driver->name, strlen(driver->name));
//判断驱动能否处理这个设备,设备的bus_id和驱动的名字是一样的
}
struct bus_type my_bus_type = {
.name = "my_bus", //创建总线目录名为 my_bus
.match = my_match,
};
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");
return ret;
}
static void my_bus_exit(void)
{
bus_unregister(&my_bus_type);
}
module_init(my_bus_init);
module_exit(my_bus_exit);
执行结果在 /sys/bus 里有一个目录为my_bus,my_bus目录里是总线属性。
下面是设备,每个设备由一个struct device 描述,
int device_register(struct device *dev) 注册设备
int device_unregister(struct device *dev) 注销设备
另外,总线也是设备,也需要按设备注册。
设备属性由device_attribute描述.
int device_create_file(struct device *dev, const struct device_attribute *attr) 创建设备属性文件。
void device_remove_file(struct device *dev, const struct device_attribute *attr)
删除设备属性文件。
Bus.c
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
static char *Version = "$Revision: 1.9 $";
static int my_match(struct device *dev, struct device_driver *driver)
{
return !strncmp(dev->bus_id, driver->name, strlen(driver->name));
}
static void my_bus_release(struct device *dev)
{
printk(KERN_DEBUG "my bus release\n");
}
struct device my_bus = { //定义总线设备
.bus_id = "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);
总线注册后,就该注册设备了,设备是挂在总线上的设备
device.c
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
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, //设备属于my_bus_type总线
.parent = &my_bus, //在这里体现设备和总线的关系
.release = my_dev_release,
};
/*
* Export a simple attribute.
*/
static ssize_t mydev_show(struct device *dev, 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;
/* 初始化设备 设备的名字my_dev*/
strncpy(my_dev.bus_id, "my_dev", BUS_ID_SIZE);
/*注册设备*/
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_bus,bus下所有设备都是链接,真正的设备在device目录下。
最后一个是驱动描述
驱动程序是由struct device_driver描述的,里面有个重要指针
int (*probe) (struct device *dev); 当驱动找到与他匹配的设备的时候,就会调用probe()。
int driver_register(struct device_driver *drv) 注册驱动
void driver_unregister(struct device_driver *drv) 注销驱动
驱动的属性文件struct driver_register。
int driver_create_file(struct device_driver *drv, const struct driver_attribute *attr)
创建属性文件
void driver_remove_file(struct device_driver *drv, const struct driver_attribute *attr)
删除属性文件
Driver.c
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
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, //指明这个驱动程序属于my_bus_type这条总线
.probe = my_probe,
//当驱动程序在这条总线上找到,能处理的设备时会调用my_probe
.remove = my_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;
/*注册驱动,程序会在总线上找他能处理的设备*/
driver_register(&my_driver);
/*创建属性文件*/
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);
先加驱动,后加设备,在加设备时总线会遍历驱动,看有无驱动能处理设备,如果有调用驱动probe()。先加设备后加驱动,加驱动总线也会查找所有设备,看有无设备被驱动处理,如果有调用驱动probe()。