驱动管理一定的设备,其中的关系主要是内核的内部机制实现的,但是实现的具体逻辑需要在bus_type中的match函数中具体设计。通常是一定的设备名和驱动名匹配,当然也可以有其他的逻辑,具体的只需要设计好bus_type中的match函数。
驱动是由驱动结构体实现的。具体如下所示:
-
struct device_driver {
-
/*驱动名,通常用来匹配设备*/
-
const char *name;
-
/*关联的总线类型,总线、设备、驱动关联的总线类型*/
-
struct bus_type *bus;
-
-
struct module *owner;
-
const char *mod_name; /* used for built-in modules */
-
-
/*驱动中最应该实现的操作函数主要包括probe和remove函数*/
-
/*当匹配完成以后的,入口函数*/
-
int (*probe) (struct device *dev);
-
/*驱动卸载时操作的相关函数,退出函数*/
-
int (*remove) (struct device *dev);
-
-
void (*shutdown) (struct device *dev);
-
int (*suspend) (struct device *dev, pm_message_t state);
-
int (*resume) (struct device *dev);
-
struct attribute_group **groups;
-
-
struct dev_pm_ops *pm;
-
-
struct driver_private *p;
-
};
-
-
/*驱动注册函数,返回值必须检测*/
-
int __must_check driver_register(struct device_driver *drv);
-
/*驱动释放函数*/
-
void driver_unregister(struct device_driver *drv);
-
-
/*驱动属性结构体*/
-
struct driver_attribute {
-
/*属性值*/
-
struct attribute attr;
-
/*属性读操作函数*/
-
ssize_t (*show)(struct device_driver *driver, char *buf);
-
/*属性写操作函数*/
-
ssize_t (*store)(struct device_driver *driver, const char *buf,
-
size_t count);
-
};
-
-
/*驱动属性定义宏命令*/
-
#define DRIVER_ATTR(_name, _mode, _show, _store)
-
struct driver_attribute driver_attr_##_name =
-
__ATTR(_name, _mode, _show, _store)
/*驱动属性文件创建函数,返回值必须检测*/
-
int __must_check driver_create_file(struct device_driver *driver,struct driver_attribute *attr);
-
/*驱动属性文件移除函数*/
-
void driver_remove_file(struct device_driver *driver,struct driver_attribute *attr);
驱动结构体的定义不需要完成所有元素的赋值,只需要完成主要的几个变量的赋值即可,其中主要的元素包含name,bus,以及probe和remove函数的实现。
其中的probe函数是当总线中的match完成匹配操作以后,进入驱动的入口函数,因此必须实现。remove我认为就是对应的退出函数,因此也有必要实现。
驱动的注册,释放也有相关的函数来操作,主要是driver_register()和driver_unregister()。
总结:
1、在总线驱动模型中我认为最主要的是搞清楚三个不同的结构体,分别是总线、驱动、设备。了解三个元素对应的属性结构体以及相应的属性操作函数的差异性。
2、不同驱动设计的关键主要是完成不同结构体的填充过程,但是并不需要对结构体中所有的对象进行赋值,只需要完成重要的几个元素的值。
3、总线是一种类型,同时也是一种设备,在总线的相关处理中需要首先添加总线类型,然后添加总线设备,这是需要注意的。由于总线类型关联驱动和设备,因此需要导出总线类型变量。由于总线设备是设备的父设备,因此也需要将总线设备变量导出。同样在驱动和设备中也要导出相关的结构体变量,便于总线中的match函数实现驱动和设备的匹配操作。
4、XXX_attr结构体基本相同,都是一个属性结构体和函数show()、stroe()。但是不同的XXX可能会导致show、stroe函数的参数发生变化。这需要对照源码。
5、struct device中的init_name是一个特殊的量,不能直接读写操作,只能采用函数device_name()和set_device_name来设置设备名。
6、xxx_register()之类的函数,需要对返回值进行检查。因为很有可能不成功。
例子
接总线与设备的代码
driver.c
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
MODULE_AUTHOR("David Xie");
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;
/*注册驱动*/
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);