Linux驱动之设备模型(4)-总线
1 总线 总线,是处理器与一个或者多个设备之间的通道。在Linux设备模型中,所有的设备都通过总线相连,甚至是那些内部的虚拟"platform"总线。用bus_type结构来描述。struct bus_type {const char*name;/* 总线名 */const char*dev_name;struct device
1 总线
总线,是处理器与一个或者多个设备之间的通道。在Linux设备模型中,所有的设备都通过总线相连,甚至是那些内部的虚拟"platform"总线。用bus_type结构来描述。
struct bus_type {
const char *name; /* 总线名 */
const char *dev_name;
struct device *dev_root;
struct bus_attribute *bus_attrs; /* 总线属性 */
struct device_attribute *dev_attrs; /* 设备属性 */
struct driver_attribute *drv_attrs; /* 驱动属性 */
/* 当向该总线注册设备或总线调用,用于设备与驱动的匹配 */
int (*match)(struct device *dev, struct device_driver *drv);
/* 当添加或移除设备时,调用此函数向用户空间产生环境变量 */
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 iommu_ops *iommu_ops;
/* 私有数据指针,这个比较重要的一个成员 */
struct subsys_private *p;
};
2. 总线声明
内核中的每一种总线类型(PCI,I2C,USB,etc)都用bus_type结构体来描述,需要初始化相应的name成员和match函数。
struct bus_type pci_bus_type = {
.name = "pci",
.match = pci_bus_match,
};
3. 注册和注销函数
int bus_register(struct bus_type *bus);
void bus_unregister(struct bus_type *bus);
总线 注册函数分析
int bus_register(struct bus_type *bus)
{
retval =kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
priv->subsys.kobj.kset = bus_kset;
priv->subsys.kobj.ktype =&bus_ktype;
priv->drivers_autoprobe = 1;
/* 注册bus kset,在bus/下产生bus->name目录 */
retval = kset_register(&priv->subsys);
retval = bus_create_file(bus,&bus_attr_uevent);
/* 产生device目录 */
priv->devices_kset =kset_create_and_add("devices", NULL,
&priv->subsys.kobj);
/* 产生driver目录 */
priv->drivers_kset =kset_create_and_add("drivers", NULL,
&priv->subsys.kobj);
/* 初始化设备链和驱动链 */
klist_init(&priv->klist_devices,klist_devices_get, klist_devices_put);
klist_init(&priv->klist_drivers,NULL, NULL);
retval = add_probe_files(bus);
retval = bus_add_attrs(bus);
}
Klist_drivers和klist_devices这两条链非常重要,用来遍历drivers_kset和devices_kset,drivers_kset和devices_kset分别是总线上所有driver和device的集合。
4. match() 函数
match函数为总线提供了检测驱动是否支持此设备的功能,通过device IDs来进行匹配。当驱动被注册到总线上,总线上的设备链表会被遍历,match()被调用来完成对每个设备的匹配。
5. subsys_private
subsys_private是一个比较重要的成员。
struct subsys_private {
struct kset subsys;
struct kset *devices_kset; /* 设备集,包含总线上所有的设备 */
struct list_head interfaces;
struct mutex mutex;
struct kset *drivers_kset; /* 驱动集,包含总线所有的驱动 */
struct klist klist_devices; /* 设备链表,连接总线上所有的设备 */
struct klist klist_drivers; /* 驱动链表,连接总线上所有的驱动 */
struct blocking_notifier_head bus_notifier;
unsigned int drivers_autoprobe:1;
struct bus_type *bus; /* 指回bus_type */
struct kset glue_dirs;
struct class *class;
};
设备链表和驱动链表连接了注册到总线上的所有设备和驱动,经常用来遍历和查询总线上的设备和驱动。设备模型核心层提供了两个API,用来遍历设备和驱动。
int bus_for_each_dev(struct bus_type * bus, struct device * start, void * data,
int (*fn)(struct device *, void *));
int bus_for_each_drv(struct bus_type * bus, struct device_driver * start,
void * data, int (*fn)(struct device_driver *, void *));
6. 导出属性,总线的属性用bus_attribute来描述
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_ATTR宏可以在编译时刻创建和初始化bus_attribute结构体
BUS_ATTR(_name, _mode, _show, _store)
在LDM里提供了两个接口用来在sysfs目录下产生和移除属性文件
int bus_create_file(struct bus_type *, struct bus_attribute *);
void bus_remove_file(struct bus_type *, struct bus_attribute *);
7. 实例分析
创建一条scbus总线,并添加版本属性。实际中我们并不需要自己创建总线,此试验仅用来学习
/*
* for learn bus
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
static char *Version = "revision 1.0,scbus";
/* 匹配函数,通过设备名和驱动名来匹配 */
static int scbus_match(struct device *dev,struct device_driver *driver)
{
printk("\n%s,%s\n", dev_name(dev), driver->name);
return!strncmp(dev_name(dev), driver->name, strlen(driver->name));
}
static void scbus_release(struct device*dev)
{
printk("scbusrelease\n");
}
struct bus_type scbus_type = {
.name = "scbus",
.match = scbus_match,
};
EXPORT_SYMBOL_GPL(scbus_type);
struct device scbus = {
.init_name = "scbus0",
.release = scbus_release,
};
EXPORT_SYMBOL_GPL(scbus);
/*
* export bus attribute
*/
static ssize_t show_bus_version(structbus_type *bus, char *buf)
{
returnsnprintf(buf, PAGE_SIZE, "%s\n", Version);
}
static BUS_ATTR(version, S_IRUGO,show_bus_version, NULL);
static int __init scbus_init(void)
{
intret;
ret= bus_register(&scbus_type);
if(ret)
returnret;
ret= bus_create_file(&scbus_type, &bus_attr_version);
if(ret)
gotocreate_error;
ret= device_register(&scbus);
if(ret)
gotodevice_error;
printk("Createa scbus\n");
return0;
device_error:
bus_remove_file(&scbus_type,&bus_attr_version);
create_error:
bus_unregister(&scbus_type);
returnret;
}
static void __exit scbus_exit(void)
{
device_unregister(&scbus);
bus_remove_file(&scbus_type,&bus_attr_version);
bus_unregister(&scbus_type);
printk("Removea scbus\n");
}
module_init(scbus_init);
module_exit(scbus_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("CJOK<cjok.liao@gmail.com>");
试验结果:
更多推荐
所有评论(0)