在分析设备模型的bus之前,我们看一下初始化的过程,很有意思:
start_kernel-->rest_init-->kernel_init-->do_basic_setup
在do_basic_setup中有个usermodehelper_init有意思,凭感觉,这个函数与kobject_uevent有联系,kobject_uevent做什么的,就是内核空间来通知用户空间的,比如udev就是利用这个来在/dev创建设备文件的,再比如在嵌入式中,插拔个硬盘或者USB,gui总会提示插入或者拨出之类的提示,这个一般是内核中驱动模型根据kobject的uevent来告诉用户态的,如果通信的,是通过netlink这种特殊的socket来像用户空间广播的,这样gui中可以用socket来接受信息,并解析,然后显示出来。先提到这里,usermodehelper_init是不是这么回事,还需要更多的时间去研究。
好,继续看do_basic_setup中的其它函数:driver_init
/**
* driver_init - initialize driver model.
*
* Call the driver model init functions to initialize their
* subsystems. Called early from init/main.c.
*/
void __init driver_init(void)
{
/* These are the core pieces */
devtmpfs_init();
devices_init();
buses_init();
classes_init();
firmware_init();
hypervisor_init();
/* These are also core pieces, but must come after the
* core core pieces.
*/
platform_bus_init();
system_bus_init();
cpu_dev_init();
memory_dev_init();
}
看下这个函数的注释,就清楚它就是接下来要分析的设备模型 的初始化。
devtmpfs_init是什么?是在/dev下面挂载的一个内存文件系统,具体可以看《linux设备模型之字符设备》一文,与驱动是有一定联系,应该是2.6.32之后加入的。
今天我们只分析core core pieces中的buses_init:
int __init buses_init(void)
{
bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
if (!bus_kset)
return -ENOMEM;
return 0;
}
很简单,新增一个kset,在/sys下有了bus目录。
我们的重心在后面,如何在bus目录下添加总线类型:
描述总线的结构体: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); //当设备移除时调用
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 bus_type_private *p; //总线的私有变量指针
};
与bus_type紧密相关的结构体:struct bus_type_private
struct bus_type_private {
struct kset subsys; //具体总线的kset
struct kset *drivers_kset; //总线上所有驱动的集合
struct kset *devices_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_register
/**
* bus_register - register a bus with the system.
* @bus: bus.
*
* Once we have that, we registered the bus with the kobject
* infrastructure, then register the children subsystems it has:
* the devices and drivers that belong to the bus.
*/
int bus_register(struct bus_type *bus)
{
int retval;
struct bus_type_private *priv;
priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->bus = bus;
bus->p = priv; //bus_type和bus_type_private紧密联系起来
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name); //设置具体总线kset内嵌kobject的名称
if (retval)
goto out;
priv->subsys.kobj.kset = bus_kset; //总线kset内嵌kobject的kset指向bus_kset,也就是说上级目录是/sys/bus
priv->subsys.kobj.ktype = &bus_ktype;
priv->drivers_autoprobe = 1;
retval = kset_register(&priv->subsys); //注册总线的kset,生成/sys/bus/xxbus 目录
if (retval)
goto out;
retval = bus_create_file(bus, &bus_attr_uevent); //创建uevent目录,只写的文件,写文件会调用kobject_uevent函数
if (retval)
goto bus_uevent_fail;
priv->devices_kset = kset_create_and_add("devices", NULL, //在xxbus目录下建立devices目录
&priv->subsys.kobj);
if (!priv->devices_kset) {
retval = -ENOMEM;
goto bus_devices_fail;
}
priv->drivers_kset = kset_create_and_add("drivers", NULL, //在xxbus目录下建立drivers目录
&priv->subsys.kobj);
if (!priv->drivers_kset) {
retval = -ENOMEM;
goto bus_drivers_fail;
}
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); //初始化klist_devices链表
klist_init(&priv->klist_drivers, NULL, NULL); //初始化klist_drivers链表
retval = add_probe_files(bus); //在xxbus目录下建立drivers_probe和drivers_autoprobe文件
if (retval)
goto bus_probe_files_fail;
retval = bus_add_attrs(bus); //建立bus的总线属性文件
if (retval)
goto bus_attrs_fail;
pr_debug("bus: '%s': registered\n", bus->name);
return 0;
bus_attrs_fail:
remove_probe_files(bus);
bus_probe_files_fail:
kset_unregister(bus->p->drivers_kset);
bus_drivers_fail:
kset_unregister(bus->p->devices_kset);
bus_devices_fail:
bus_remove_file(bus, &bus_attr_uevent);
bus_uevent_fail:
kset_unregister(&bus->p->subsys);
kfree(bus->p);
out:
bus->p = NULL;
return retval;
}
总线的注册方法就是上面这么多,比如platform总线的注册,就是在/sys/bus目录下多了个platform目录,而在platform目录下:
devices drivers drivers_autoprobe drivers_probe uevent
总线远没有目前看到的这么简单,因为它的方法我们还没用到。在分析完设备和驱动后,我们再分析一个完整的过程。