Linux设备模型(二)
一、总线
在Linux设备模型中,用bus_type结构表示总线,其结构如下:
struct bus_type {
const char * name;/*总线类型名称*/
struct module * owner;/*指向模块的指针(如果有), 此模块负责操作这个总线*/
struct kset subsys;/*与该总线相关的子系统*/
struct kset drivers;/*总线驱动程序的kset*/
struct kset devices;/* 挂在该总线的所有设备的kset*/
struct klist klist_devices;/*挂接在该总线的设备链表*/
struct klist klist_drivers;/*与该总线相关的驱动程序链表*/
struct bus_attribute * bus_attrs; /*总线属性*/
struct device_attribute * dev_attrs; /*设备属性,指向为每个加入总线的设备建立的默认属性链表*/
struct driver_attribute * drv_attrs; /*驱动程序属性*/
struct bus_attribute drivers_autoprobe_attr;/*驱动自动探测属性*/
struct bus_attribute drivers_probe_attr;/*驱动探测属性*/
};
每个总线都有自己的子系统,然而这些子系统并不在sysfs中的顶层,相反,我们会在总线子系统下面发现它们。一个总线包含两个kset,分别代表了总线的驱动程序和插入总线的所有设备。
1、总线的注册
对于新的总线,我们必须调用bus_register进行注册,如下所示:
ret = bus_register(&ldd_bus_type);
if(ret)
return ret;
如果成功,新的总线子系统将被添加到系统中,可以在sysfs的/sys/bus目录下看到它,然后我们就可以向这个总线添加设备。
当有必要从系统中删除一个总线的时候(比如相应的模块被删除),要使用bus_unregister函数:
void bus_unregister(struct bus_type * bus);
2、总线方法
int (*match)(struct device * dev, struct device_driver * drv);
当一个新设备或者驱动被添加到这个总线时,该方法被调用,用于判断指定的驱动程序是否能处理指定的设备,若可以则返回非零值。
int (*uevent)(struct device *dev, char **envp,
int num_envp, char *buffer, int buffer_size);
在为用户空间产生热插拔事件之前,这个方法允许总线添加环境变量。
3、对设备和驱动程序的迭代
int bus_for_each_dev(struct bus_type * bus, struct device * start,
void * data, int (*fn)(struct device *, void *));
该函数迭代了在总线的每个设备,将相关的device结构传递给fn,同时传递data值。如果start是NULL,将从总线上的第一个设备开始迭代,否则将从start后的第一个设备开始迭代。如果fn返回一个非零值,将停止迭代,而这个值也会从bus_for_each_dev返回。
相似的函数也可用于驱动程序的迭代上:
int bus_for_each_drv(struct bus_type * bus, struct device_driver * start,
void * data, int (*fn)(struct device_driver *, void *));
该函数的工作方式与bus_for_each_dev相同,只是它的工作对象是驱动程序而已。
注意:这两个函数在工作期间,都会拥有总线子系统的读取者/写入者信号量,因此,同时使用这两个函数会发生死锁。
4、总线属性
struct bus_attribute {
struct attribute attr;
ssize_t (*show)(struct bus_type *, char * buf);
ssize_t (*store)(struct bus_type *, const char * buf, size_t count);
};
有一个非常便于使用的宏,可在编译时刻创建和初始化bus_attribute结构:
BUS_ATTR(name,mode,show,store);
这个宏声明了一个结构,它将bus_attr_作为给定的name的前缀来创建总线的真正名称。
创建属于总线的任何属性,都需要显式调用bus_create_file函数:
int bus_create_file(struct bus_type * bus, struct bus_attribute * attr);
使用下面的函数删除属性:
void bus_remove_file(struct bus_type * bus, struct bus_attribute * attr);
二、设备
struct device {
struct device *parent;/* 设备的 "父" 设备,该设备所属的设备,通常一个父设备是某种总线或者主控制器. 如果 parent 是 NULL, 则该设备是顶层设备,较少见 */
struct kobject kobj;/*代表该设备并将其连接到结构体系中的 kobject; 注意:作为通用的规则, device->kobj->parent 应等于 device->parent->kobj*/
char bus_id[BUS_ID_SIZE];/*在总线上唯一标识该设备的字符串;例如: PCI 设备使用标准的 PCI ID 格式, 包含:域, 总线, 设备, 和功能号.*/
struct bus_type * bus; /*标识该设备连接在何种类型的总线上*/
struct device_driver *driver; /*管理该设备的驱动程序*/
void *driver_data; /*该设备驱动使用的私有数据成员*/
void *platform_data; /* Platform specific data, device core doesn't touch it */
void (*release)(struct device * dev);/*当这个设备的最后引用被删除时,内核调用该方法; 它从被嵌入的 kobject 的 release 方法中调用。所有注册到核心的设备结构必须有一个 release 方法, 否则内核将打印错误信息*/
};
注意:在注册device结构前,至少要设置parent、bus_id、bus和release成员。
1、设备注册
常用的注册和注销函数是:
int device_register(struct device *dev);
void device_unregister(struct device * dev);
注意:一个实际的总线是一个设备,因此必须被单独注册。
2、设备属性
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);
};
我们可以在编译时刻用下面的宏构造这些attribute结构:
DEVICE_ATTR(name,mode,show,store);
该结构将dev_attr_作为指定名字的前缀来构造结构的名称。
用下面的两个函数实现对属性文件的实际处理:
int device_create_file(struct device * dev, struct device_attribute * attr);
void device_remove_file(struct device * dev, struct device_attribute * attr);
3、设备结构的嵌入
单纯用device结构表示的设备是很少见的,而是通过把device内嵌在设备的高层表示之中。如下所示:
struct ldd_device {
char *name;
struct ldd_driver *driver;
struct device dev;
};
#define to_ldd_device(dev) container_of(dev, struct ldd_device, dev);
三、设备驱动程序
struct device_driver {
const char * name; /*驱动程序的名字(在sysfs中出现)*/
struct bus_type * bus;/*驱动程序所操作的总线类型*/
struct kobject kobj;/*内嵌的kobject对象*/
struct klist klist_devices;/*当前驱动程序能操作的设备链表*/
struct klist_node knode_bus;
int (*probe) (struct device * dev);/*查询一个特定设备是否存在及驱动是否可以使用它的函数*/
int (*remove) (struct device * dev);/*将设备从系统中删除*/
void (*shutdown) (struct device * dev);/*关闭设备*/
};
1、驱动程序注册
int driver_register(struct device_driver * drv);
void driver_unregister(struct device_driver * drv);
2、驱动程序的属性结构
struct driver_attribute {
struct attribute attr;
ssize_t (*show)(struct device_driver *, char * buf);
ssize_t (*store)(struct device_driver *, const char * buf, size_t count);
};
我们可以在编译时刻用下面的宏构造这些attribute结构:
DRIVER_ATTR(name,mode,show,store);
该结构将drv_attr_作为指定名字的前缀来构造结构的名称。
使用下面的函数创建属性文件:
int driver_create_file(struct device_driver * drv, struct driver_attribute * attr);
void driver_remove_file(struct device_driver * drv, struct driver_attribute * attr);
3、驱动程序结构的嵌入
对于大多数驱动程序核心结构来说,device_driver结构通常被包含在高层和总线相关的结构中。如下所示:
struct ldd_driver {
char *version;
struct module *module;
struct device_driver driver;
struct driver_attribute version_attr;
};
#define to_ldd_driver(drv) container_of(drv, struct ldd_driver, driver);
四、类
为了管理类,驱动程序核心导出了一些接口,其目的之一是提供包含设备号的属性以便自动创建设备节点,所以udev的使用离不开类。 类函数和结构与设备模型的其他部分遵循相同的模式。
1、管理类
用class结构的一个实例来定义类:
struct class {
const char * name;/*每个类需要一个唯一的名字, 它将显示在/sys/class中*/
struct module * owner;
struct class_attribute * class_attrs;/* 指向类属性的指针(以NULL结尾) */
struct class_device_attribute * class_dev_attrs;/* 指向类中每个设备的一组默认属性的指针 */
struct device_attribute * dev_attrs;
int (*uevent)(struct class_device *dev, char **envp,
int num_envp, char *buffer, int buffer_size);/* 类热插拔产生时添加环境变量的函数 */
int (*dev_uevent)(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size);/* 类中的设备热插拔时添加环境变量的函数 */
void (*release)(struct class_device *dev);/* 把设备从类中删除的函数 */
void (*class_release)(struct class *class);/* 删除类本身的函数 */
};
注册函数是:
int class_register(struct class * cls);
void class_unregister(struct class * cls);
处理属性的接口如下:
struct class_attribute {
struct attribute attr;
ssize_t (*show)(struct class *cls, char *buf);
ssize_t (*store)(struct class *cls, const char *buf, size_t count);
};
CLASS_ATTR(_name,_mode,_show,_store);
int class_create_file(struct class *cls, const struct class_attribute *attr);
void class_remove_file(struct class *cls, const struct class_attribute *attr);
2、类设备
struct class_device {
struct class * class; /* 指向该设备所属的类,必须*/
struct device * dev; /* 指向此设备相关的 device结构体,可选。若不为NULL,应是一个从类入口到/sys/devices 下相应入口的符号连接,以便用户空间查找设备入口*/
void * class_data;/* 私有数据指针 */
char class_id[BUS_ID_SIZE];/*包含了要在sysfs中显示的设备名*/
};
常用的注册函数如下:
int class_device_register(struct class_device *cd);
void class_device_unregister(struct class_device *cd);
/*重命名一个已经注册的类设备入口:*/
int class_device_rename(struct class_device *cd, char *new_name);
/*类设备属性:*/
struct class_device_attribute {
struct attribute attr;
ssize_t (*show)(struct class_device *cls, char *buf);
ssize_t (*store)(struct class_device *cls, const char *buf,
size_t count);
};
CLASS_DEVICE_ATTR(_name, _mode, _show, _store);
/*创建和删除除struct class中设备默认属性外的属性*/
int class_device_create_file(struct class_device *cls, const struct class_device_attribute *attr);
void class_device_remove_file(struct class_device *cls, const struct class_device_attribute *attr);
3、类接口
struct class_interface {
struct list_head node;
struct class *class;/* 指向该接口所属的类*/
int (*add) (struct class_device *, struct class_interface *);
/*当一个类设备被加入到在 class_interface 结构中指定的类时, 将调用接口的 add 函数,进行一些设备需要的额外设置,通常是添加更多属性或其他的一些工作*/
void (*remove) (struct class_device *, struct class_interface *);/*一个接口的功能是简单明了的. 当设备从类中删除, 将调用remove 方法来进行必要的清理*/
int (*add_dev) (struct device *, struct class_interface *);
void (*remove_dev) (struct device *, struct class_interface *);
};
/*注册或注销接口的函数:*/
int class_interface_register(struct class_interface *class_intf);
void class_interface_unregister(struct class_interface *class_intf);
五、Linux设备模型的驱动程序
1、总线驱动程序(mybus.c)
#include
#include
#include
#include
#include
static char *Version="$ Version:1.0 $";
//用于判断指定的驱动程序是否能处理指定的设备,若可以返回非零值
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 bus_type my_bus_type={
.name = "my_bus", //初始化总线的名字
.match = my_match,
};
EXPORT_SYMBOL(my_bus_type);
/*定义总线设备(一条总线也是个设备,也必须按设备注册)*/
struct device my_bus={
.bus_id = "my_bus0", //初始化总线设备的名字
.release = my_bus_release,
};
EXPORT_SYMBOL(my_bus);
//显示总线的属性
static ssize_t show_bus_version(struct bus_type *bus,char *buf)
{
return snprintf(buf,PAGE_SIZE,"%s\n",Version);
}
/*在编译时刻创建和初始化bus_attribute结构*/
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); //驱动程序将在/sys/bus/pci中创建一个sysfs目录,其中包含了两个目录:devices和drivers。
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 "Unable to register my_bus0\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);
MODULE_AUTHOR("chenqi");
MODULE_LICENSE("GPL");
2、设备驱动程序
#include
#include
#include
#include
#include
/*申明外部变量*/
extern struct device my_bus;
extern struct bus_type my_bus_type;
//卸载设备的时候调用
static void my_dev_release(struct device *dev)
{
}
/*定义并初始化设备*/
struct device my_dev={
.bus = &my_bus_type, //初始化设备所在的总线
.parent = &my_bus, //初始化设备的父设备
.release= my_dev_release,
};
//显示设备的属性
static ssize_t mydev_show(struct device *dev,struct device_attribute *attr,char *buf)
{
return snprintf(buf,PAGE_SIZE,"%s\n","This is mydevice!");
}
static DEVICE_ATTR(dev,S_IRUGO,mydev_show,NULL);
static int __init my_device_init(void)
{
int ret=0;
/*初始化设备的名字*/
strncpy(my_dev.bus_id,"my_dev",BUS_ID_SIZE);
/*注册设备*/
ret=device_register(&my_dev);
if(ret)
printk(KERN_NOTICE "Unable to register my_dev\n");
/*创建设备属性文件*/
ret=device_create_file(&my_dev,&dev_attr_dev);
if(ret)
printk(KERN_NOTICE "Unable to create file\n");
return ret;
}
static void my_device_exit(void)
{
//卸载设备
device_unregister(&my_dev);
}
module_init(my_device_init);
module_exit(my_device_exit);
MODULE_AUTHOR("chenqi");
MODULE_LICENSE("GPL");
3、设备驱动程序
#include
#include
#include
#include
#include
/*申明外部变量*/
extern struct bus_type my_bus_type;
/*当驱动程序与设备匹配成功后,调用该驱动程序的probe函数*/
static int my_probe(struct device *dev)
{
printk(KERN_NOTICE"Driver found device which my driver can handle!\n");
return 0;
}
/*将设备从系统中删除后调用*/
static int my_remove(struct device *dev)
{
printk(KERN_NOTICE"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,
};
//显示驱动程序的属性
static ssize_t mydriver_show(struct device_driver *driver,char *buf)
{
return sprintf(buf,"%s\n","This is mydriver!");
}
/*在编译时刻创建和初始化driver_attribute结构*/
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(KERN_NOTICE"driver register failed:\n");
/*创建驱动程序属性文件*/
ret=driver_create_file(&my_driver,&driver_attr_drv);
if(ret)
printk(KERN_NOTICE"create file failed:\n");
return ret;
}
static void my_driver_exit(void)
{
/*卸载设备驱动*/
driver_unregister(&my_driver);
}
module_init(my_driver_init);
module_exit(my_driver_exit);
MODULE_AUTHOR("chenqi");
MODULE_LICENSE("GPL");