示例程序运行kernel版本为3.0.36,示例程序是国嵌的,修改使之在3.0.36内核版本可运行,并消除编译时的警告信息
linux设备模型的介绍,在ldd3中已经介绍的很详细了,没有了解过的,可以去看看第十四章设备模型这一章,,这里只简单介绍一下设备总线,设备和驱动,并提供一个可运行的示例程序,用做备忘!
Linux设备模型是由总线(bus_type),设备(device),驱动(device_driver)这三个数据结构来描述的
总线:
在设备模型中, 所有的设备都通过总线相连, 甚至是内部的虚拟“platform”总线, 在Linux 设备模型中, 总线由 bus_type 结构表示:
struct bus_type {
const char *name; //总线名称
struct bus_attribute *bus_attrs; //总线属性
struct device_attribute *dev_attrs; //设备属性
struct driver_attribute *drv_attrs; //驱动属性
int (*match)(struct device *dev, struct device_driver *drv);//当一个新设备或驱动添加到此总线时,该函数被调用,用于判断驱动程序是否能处理指定的设备,若可以,则返回非0值
int (*uevent)(struct device *dev, struct kobj_uevent_env *env); //在为用户空间产生热插拔事件之前,该函数允许为总线添加环境变量
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);
const struct dev_pm_ops *pm;
struct subsys_private *p;
};
//总线属性
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);
};
下面贴上创建一个名为my_bus总线的示例程序:
-
#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)
-
{
-
printk("dev->kobj.name=%s,driver->name=%s\n",dev->kobj.name,driver->name);
-
//这里对比设备名字和驱动名字是否相等
-
return !strncmp(dev->kobj.name, 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//设备引用为0时的释放函数
-
};
-
-
//定义一个总线
-
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.
-
*/
-
//读取bus属性version时调用该函数
-
static ssize_t show_bus_version(struct bus_type *bus, char *buf)
-
{
-
return snprintf(buf, PAGE_SIZE, "%s\n", Version);
-
}
-
//该宏在编译时创建和初始化bus_attr_version,创建的名字由bus_attr_加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");
-
printk("bus register success!!!\n");
-
return ret;
-
}
-
//模块退出函数
-
static void my_bus_exit(void)
-
{
-
device_unregister(&my_bus);
-
bus_unregister(&my_bus_type);
-
printk("bus remove success!!!\n");
-
}
-
-
module_init(my_bus_init);
-
module_exit(my_bus_exit);
编译成bus.ko,加载到系统中 insmod bus.ko
该模块加载到系统之后,sys文件系统中多了两个目录,一个是/sys/bus/my_bus,一个是sys/devices/my_bus0, 分别对应于模块里面注册的总线和设备,/sys/bus/my_bus还有一个有我们自己闯属性文件version,读取该文件将调用show_bus_version函数
设备:
系统中的每个设备由一个struct device结构描述:
struct device {
struct device *parent; //父设备
struct device_private *p;
struct kobject kobj; //设备内嵌的kobject
const char *init_name; /* initial name of the device */
const struct device_type *type;
struct mutex mutex; /* mutex to synchronize calls to
* its driver.
*/
struct bus_type *bus; /* type of bus device is on */
struct device_driver *driver; /* which driver has allocated this
device */
void *platform_data; /* Platform specific data, device
core doesn't touch it */
/*
这里省略部分信息
*/
void (*release)(struct device *dev);
};
设备属性定义如下:
struct device_attribute {
struct attribute attr;
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
};
下面贴上创建一个名为my_dev设备,并挂载在上面创建的my_bus总线上的示例程序:
-
#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");
-
-
//声明外部变量my_bus和my_bus_type,由bus模块里导出
-
extern struct device my_bus;
-
extern struct bus_type my_bus_type;
-
-
-
static void my_dev_release(struct device *dev)
-
{
-
}
-
//定义一个设备
-
struct device my_dev = {
-
.init_name = "my_dev",//用于初始化设备名字,即设备内嵌kobject的名字,初始化操作完成之后,该指针赋值为NULL
-
.bus = &my_bus_type, //设备挂载对应的总线
-
.parent = &my_bus, //设备的父设备,此处即总线设备
-
.release = my_dev_release, //所有设备引用消失时,调用此函数
-
};
-
-
/*
-
* Export a simple attribute.
-
*/
-
//读取设备属性dev时调用该函数
-
static ssize_t mydev_show(struct device *dev, struct device_attribute *attr,char *buf)
-
{
-
return sprintf(buf, "%s\n", "This is my device!");
-
}
-
//该宏在编译时创建和初始化dev_attr_dev,创建的名字由dev_attr_加dev组成
-
static DEVICE_ATTR(dev, S_IRUGO, mydev_show, NULL);
-
-
//模块初始化函数
-
static int __init my_device_init(void)
-
{
-
int ret = 0;
-
//注册设备
-
ret=device_register(&my_dev);
-
if(ret){
-
printk("device register error!!!\n");
-
return -1;
-
}
-
-
//创建设备属性文件
-
ret=device_create_file(&my_dev, &dev_attr_dev);
-
if(ret){
-
printk("create device file error!!!\n");
-
}
-
-
printk("register device(%s) success\n",my_dev.kobj.name);
-
return ret;
-
-
}
-
//模块退出函数
-
static void my_device_exit(void)
-
{
-
device_unregister(&my_dev);
-
printk("remove device success\n");
-
}
-
-
module_init(my_device_init);
-
module_exit(my_device_exit);
编译成device.ko,加载到系统中 insmod device.ko(要先加载bus.ko,加载device.ko才能成功,因为device.ko引用了bus.ko的两个内核导出符)
device模块加载成功之后,/sys/devices/my_bus0/设备下面多了一个my_dev目录,/sys/bus/my_bus/devices/目录下也多了一个连接,是链接到
/sys/devices/my_bus0/my_dev目录的,在该目录下还生成了一个由我们创建的名为dev的可读属性文件,读取该文件会调用mydev_show该函数。
驱动:
系统中的每个驱动程序由一个struct device_driver结构描述:
struct device_driver {
const char *name; //驱动名字
struct bus_type *bus; //驱动程序所在的总线
struct module *owner;
const char *mod_name; /* used for built-in modules */
bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
const struct of_device_id *of_match_table;
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);
const struct attribute_group **groups;
const struct dev_pm_ops *pm;
struct driver_private *p;
};
驱动属性:
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);
};
下面贴上创建一个名为my_dev的驱动程序,并挂载在上面创建的my_bus总线上的示例程序:
-
#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");
-
//声明外部总线my_bus_type,由bus模块里导出
-
extern struct bus_type my_bus_type;
-
-
//探测函数,当总线的match函数匹配成功后,该函数开始执行
-
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;
-
}
-
//定义一个名为my_dev的驱动程序
-
struct device_driver my_driver = {
-
.name = "my_dev", //驱动名
-
.bus = &my_bus_type,//驱动挂载对应的总线
-
.probe = my_probe, //探测函数,当总线的match函数匹配成功后,该函数开始执行
-
.remove = my_remove, //当驱动移除时,该函数开始执行
-
};
-
-
/*
-
* Export a simple attribute.
-
*/
-
//读取驱动属性drv时调用该函数
-
static ssize_t mydriver_show(struct device_driver *driver, char *buf)
-
{
-
return sprintf(buf, "%s\n", "This is my driver!");
-
}
-
//该宏在编译时创建和初始化driver_attr_drv,创建的名字由driver_attr_加drv组成
-
static DRIVER_ATTR(drv, S_IRUGO, mydriver_show, NULL);
-
//模块初始化函数
-
static int __init my_driver_init(void)
-
{
-
int ret = 0;
-
-
//注册驱动
-
ret=driver_register(&my_driver);
-
if(ret){
-
printk("driver register error!!!\n");
-
return -1;
-
}
-
//创建驱动属性文件
-
ret=driver_create_file(&my_driver, &driver_attr_drv);
-
if(ret){
-
printk("create driver file error!!!\n");
-
}
-
-
printk("register driver(%s) success\n",my_driver.name);
-
return ret;
-
-
}
-
//模块退出函数
-
static void my_driver_exit(void)
-
{
-
driver_unregister(&my_driver);
-
printk("remove driver success\n");
-
}
-
-
module_init(my_driver_init);
-
module_exit(my_driver_exit);
编译成driver.ko,加载到系统中 insmod driver.ko(要先加载bus.ko,driver.ko模块才能加载成功)
驱动加载过程中,系统会调用my_bus总线的match函数来对设备和驱动进行匹配。匹配成功之后,才调用驱动的probe函数。
驱动加载成功之后,会在/sys/bus/my_bus/drivers目录下面创建一个名为my_dev的驱动目录,该驱动目录下面包含一个由我们自己创建的名为drv的属性文件和一个名为my_dev的设备连接。