5.总线
5.1 overview
前面我们介绍了设备模型的底层部分,下面我们来看下设备模型的高层部分,总线、设备和驱动
5.2 总线
l 总线,是处理器与一个或者多个设备之间的通道。在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);
int (*uevent)(struct device *dev, struct kobj_uevent_env*env);
int (*probe)(struct device *dev);
int (*remove)(struct device *dev);
struct subsys_private *p;
};
Match函数 注册驱动或设备时被用来完成匹配
Uevent函数 用于热插拔,添加环境变量
Probe函数 匹配成功时调用,完成相应工作
P 私有数据,包含内嵌的kset,设备kset和链表,驱动kset和链表等
5.3 总线属性
l 总线属性用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_tcount);
};
BUS_ATTR(_name,_mode, _show, _store)
l 属性操作
n 创建属性
intbus_create_file(structbus_type *, struct bus_attribute *);
n 删除属性
void bus_remove_file(struct bus_type *, structbus_attribute *);
5.4 总线基本操作
l 总线注册和注销函数
n int bus_register(struct bus_type *bus);
n void bus_unregister(struct bus_type *bus);
l 遍历总线
n 遍历总线上每个驱动,并执行fn函数
int bus_for_each_drv(struct bus_type *bus, struct device_driver*start,
void *data, int (*fn)(struct device_driver*, void *));
n 遍历总线上每个设备,并执行fn函数
int bus_for_each_dev(structbus_type *bus, struct device *start, void *data,
int(*fn)(struct device *dev, void *data));
l 总线注册分析
int bus_register(struct bus_type *bus) //包括创建与bus同名的kset、devices、driver、probe文件
{
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;
retval =kset_register(&priv->subsys);
retval = bus_create_file(bus,&bus_attr_uevent);
priv->devices_kset =kset_create_and_add("devices",NULL,
&priv->subsys.kobj);
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的集合。
5.5 实例分析
创建一条scbus总线,并添加版本属性。实际中我们并不需要自己创建总线,此试验仅用来学习
#include
#include
#include
#include
static char * Version = "revision 1.0,scbus";
static int scbus_match(struct device *dev,structdevice_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);
static ssize_t show_bus_version(structbus_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 scbus_init(void)
{
int ret;
ret=bus_register(&scbus_type);
if(ret)
return ret;
ret=bus_create_file(&scbus_type,&bus_attr_version);
if(ret)
gotocreate_error;
ret= device_register(&scbus); //产生 /sys/devices/scbus0 devices目录下第一级一般为总线设备,一般把他们作为其他设备的parent
if(ret)
goto device_error;
printk("Createa scbus\n");
return 0;
device_error:
bus_remove_file(&scbus_type,&bus_attr_version);
create_error:
bus_unregister(&scbus_type);
return ret;
}
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");
试验结果: