Linux代码版本:linux3.0
开发板环境: tiny4412
导读:上一节分析了kobject、kset和sys下目录的创建,这只是linux驱动模型最基本的一步,这一节开始分析bus的注册过程,后面会相继分析class、device和driver。下面以platform总线为例,分析platform总线的注册过程。
一、platform相关结构体分析
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 iommu_ops *iommu_ops;
struct subsys_private *p;
};
const char *name:
总线的名字
struct bus_attribute *bus_attrs:
总线默认的属性,如果不为BULL,会在相应的总线下创建属性文件
struct device_attribute *dev_attrs:
总线上device的默认属性,如果不为NULL,platform下各种device目录下会创建属性文件
struct driver_attribute *drv_attrs:
总线上driver的默认属性,如果不为NULL,platform下各种driver目录下会创建属性文件
int (*match)(struct device *dev, struct device_driver *drv):
总线上的device和driver就是通过此函数进行匹配的
int (*uevent)(struct device *dev, struct kobj_uevent_env *env):
总线上增加或移除device时调用
int (*probe)(struct device *dev):
当总线上增加device或driver并且成功匹配时调用此函数
int (*remove)(struct device *dev):
device从总线上移除的时候调用此函数
const struct dev_pm_ops *pm:
总线电源管理的操作集合,后面分析 电源管理的时候再详细分析各成员
struct subsys_private *p:
总线的私有数据成员
再看重要的数据结构
struct subsys_private {
struct kset subsys;
struct kset *devices_kset;
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;
struct list_head class_interfaces;
struct kset glue_dirs;
struct mutex class_mutex;
struct class *class;
};
struct kset subsys:
bus中封装的kset(sys下的目录总是要封装kobject或kset,不然怎么建目录和文件?)
struct kset *devices_kset:相关设备列表,等注册device或driver的时候再分析用途
struct kset *drivers_kset:
相关驱动程序列表,等注册device或driver的时候再分析用途
struct klist klist_devices:
注册的device挂接在此链表中
struct klist klist_drivers:
注册的driver挂接在此链表中
struct blocking_notifier_head bus_notifier:
目前还未看出具体用途,代码中遇到再分析
unsigned int drivers_autoprobe:1:
在注册device 或driver的时候是否自动进行匹配
struct bus_type *bus:
指向的bus
struct list_head class_interfaces:
给class用,后面分析的class的时候再详细解释
struct kset glue_dirs:
给class用,后面分析的class的时候再详细解释
struct mutex class_mutex:
给class用,后面分析的class的时候再详细解释
struct class *class:
给class用,后面分析的class的时候再详细解释
无论加多少注释,都是干巴巴的不易理解,分析代码将会看到每个成员的用途,分析 代码前先介绍几个platform相关的结构体变量。
struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store);
展开:
static bus_attribute bus_attr_uevent = {
.attr = {
.name = uevent,
.mode = S_IWUSR,
}
.show = NULL,
.store = bus_uevent_store,
}
最终实际定义一个bus_attr_uevent的变量,代码中会在/sys/bus/platform下创建一个uevent 的属性文件,该文件为只写,写该文件的时候最终会调用到 bus_uevent_store函数
static BUS_ATTR(drivers_probe, S_IWUSR, NULL, store_drivers_probe);
展开:
static bus_attribute bus_attr_drivers_probe = {
.attr = {
.name = drivers_probe,
.mode = S_IWUSR,
}
.show = NULL,
.store = store_drivers_probe,
}
最终实际定义一个bus_attr_drivers_probe的变量,代码中在/sys/bus/platform下创建一个 drivers_probe 的属性文件,该文件为只写,写该文件的时候最终会调用到 store_drivers_probe 函数
static BUS_ATTR(drivers_autoprobe, S_IWUSR | S_IRUGO,show_drivers_autoprobe, store_drivers_autoprobe);
展开:
static bus_attribute bus_attr_drivers_autoprobe = {
.attr = {
.name = drivers_autoprobe,
.mode = S_IWUSR | S_IRUGO,
}
.show = show_drivers_autoprobe,
.store = store_drivers_autoprobe,
}
最终实际定义一个bus_attr_drivers_autoprobe的变量,代码中在/sys/bus/platform下创建一个 名为drivers_autoprobe 的属性文件,该文件为可读写,读取该文件的时候最终会调用到 show_drivers_autoprobe函数,写该文件的时候最终调用到store_drivers_autoprobe
开始分析代码:
int __init platform_bus_init(void)
{
......
/*注册了一个platform的device,但只有一个name成员赋值,
后面分析注册device的时候再详细分析*/
error = device_register(&platform_bus);
if (error)
return error;
/*注册platform bus*/
error = bus_register(&platform_bus_type);
{
int retval;
struct subsys_private *priv;
/*为platform_bus_type申请一个私有结构体priv*/
priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
/*赋值platform_bus_type->p->bus = platform_bus_type*/
priv->bus = bus;
bus->p = priv;
/*初始化init_rwsem(platform_bus_type->p->bus_notifier->rwsem)
赋值platform_bus_type->p->bus_notifier->head = NULL*/
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
{
do {
init_rwsem(&(name)->rwsem);
(name)->head = NULL;
} while (0)
}
/*又看见了kobj,platform_bus_type的私有结构体封装了kset,
platform_bus_type->p->subsys.kobj.name = "platform"*/
retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
if (retval)
goto out;
/*赋值platform_bus_type->p->subsys.kobj.kset = bus_kset,还记得sys下的bus目录吗
bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);*/
priv->subsys.kobj.kset = bus_kset;
/*赋值platform_bus_type->p->subsys.kobj.ktype = &bus_ktype*/
priv->subsys.kobj.ktype = &bus_ktype;
/*赋值platform_bus_type->p->drivers_autoprobe = 1*/
priv->drivers_autoprobe = 1;
retval = kset_register(&priv->subsys);
{
int err;
if (!k)
return -EINVAL;
kset_init(k);
err = kobject_add_internal(&k->kobj);
{
int error = 0;
struct kobject *parent;
if (!kobj)
return -ENOENT;
/*前面已经初始化platform_bus_type->p->subsys.kobj.name = "platform"*/
if (!kobj->name || !kobj->name[0]) {
WARN(1, "kobject: (%p): attempted to be registered with empty "
"name!\n", kobj);
return -EINVAL;
}
/*前面platform_bus_type->p->subsys.kobj.parent并没有赋值,所以为NULL*/
parent = kobject_get(kobj->parent);
/* join kset if set, use it as parent if we do not already have one */
/*前面已赋值platform_bus_type->p->subsys = bus_kset,因为
platform_bus_type->p->subsys.kobj.parent = NULL,所以
platform_bus_type->p->subsys.kobj.parent = bus_kset-> kobj
也就意味着要在 /sys/bus目录下创建platorm目录*/
if (kobj->kset) {
if (!parent)
parent = kobject_get(&kobj->kset->kobj);
kobj_kset_join(kobj);
kobj->parent = parent;
}
pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
kobject_name(kobj), kobj, __func__,
parent ? kobject_name(parent) : "<NULL>",
kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");
/*在/sys/bus目录下创建platorm目录*/
error = create_dir(kobj);
if (error) {
kobj_kset_leave(kobj);
kobject_put(parent);
kobj->parent = NULL;
/* be noisy on error issues */
if (error == -EEXIST)
printk(KERN_ERR "%s failed for %s with "
"-EEXIST, don't try to register things with "
"the same name in the same directory.\n",
__func__, kobject_name(kobj));
else
printk(KERN_ERR "%s failed for %s (%d)\n",
__func__, kobject_name(kobj), error);
dump_stack();
} else
/*置位已经在sys中建立目录的标志state_in_sysfs为1*/
kobj->state_in_sysfs = 1;
return error;
}
if (err)
return err;
/*发送ADD事件*/
kobject_uevent(&k->kobj, KOBJ_ADD);
return 0;
}
if (retval)
goto out;
/*在sys/bus/platform目录下建立uevent文件*/
retval = bus_create_file(bus, &bus_attr_uevent);
if (retval)
goto bus_uevent_fail;
/*又是kset_create_and_add,是不是很熟悉,sys下bus目录就是调用此函数创建的;
下面指定了parent为platform,也就是在/sys/bus/platform下创建devices目录*/
priv->devices_kset = kset_create_and_add("devices", NULL,
&priv->subsys.kobj);
if (!priv->devices_kset) {
retval = -ENOMEM;
goto bus_devices_fail;
}
/*在/sys/bus/platform下创建drivers目录*/
priv->drivers_kset = kset_create_and_add("drivers", NULL,
&priv->subsys.kobj);
if (!priv->drivers_kset) {
retval = -ENOMEM;
goto bus_drivers_fail;
}
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
{
/*初始化platform_bus_type->p->klist_devices,
platform_bus_type->p->klist_devices->get = klist_devices_get
platform_bus_type->p->klist_devices->put = klist_devices_put*/
INIT_LIST_HEAD(&k->k_list);
spin_lock_init(&k->k_lock);
k->get = get;
k->put = put;
}
/*初始化platform_bus_type->p->klist_drivers
platform_bus_type->p->klist_drivers->get = NULL
platform_bus_type->p->klist_drivers->put = NULL*/
klist_init(&priv->klist_drivers, NULL, NULL);
retval = add_probe_files(bus);
{
int retval;
/*在/sys/bus/platform下创建drivers_probe目录*/
retval = bus_create_file(bus, &bus_attr_drivers_probe);
{
int error;
if (bus_get(bus)) {
/*在/sys/bus/platform下创建drivers_probe文件*/
error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr);
bus_put(bus);
} else
error = -EINVAL;
return error;
}
if (retval)
goto out;
/*在/sys/bus/platform下创建 drivers_autoprobe 文件*/
retval = bus_create_file(bus, &bus_attr_drivers_autoprobe);
if (retval)
bus_remove_file(bus, &bus_attr_drivers_probe);
out:
return retval;
}
if (retval)
goto bus_probe_files_fail;
retval = bus_add_attrs(bus);
{
int error = 0;
int i;
/*platform_bus_type->bus_attrs未赋值,也就是为NULL,所以此步不会创建目录*/
if (bus->bus_attrs) {
for (i = 0; attr_name(bus->bus_attrs[i]); i++) {
error = bus_create_file(bus, &bus->bus_attrs[i]);
if (error)
goto err;
}
}
done:
return error;
err:
while (--i >= 0)
bus_remove_file(bus, &bus->bus_attrs[i]);
goto done;
}
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);
out:
kfree(bus->p);
bus->p = NULL;
return retval;
}
if (error)
device_unregister(&platform_bus);
return error;
}
以上程序中可以看到先在sys/bus/下创建了platform目录,然后又在sys/bus/platform下创建了devices和drivers目录,同时也创建了drivers_probe、drivers_autoprobe和uevent文件,下面在设备上验证以上的目录和文件
[root@FriendlyARM /]# cd sys/bus/
[root@FriendlyARM bus]# ls
hid media platform sdio spi usb-serial
i2c mmc scsi serio usb
[root@FriendlyARM bus]# cd /sys/bus/platform/
[root@FriendlyARM platform]# ls -l
drwxr-xr-x 2 root root 0 Jan 1 12:42 devices
drwxr-xr-x 50 root root 0 Jan 1 12:42 drivers
-rw-r--r-- 1 root root 4096 Jan 1 12:42 drivers_autoprobe
--w------- 1 root root 4096 Jan 1 12:42 drivers_probe
--w------- 1 root root 4096 Jan 1 12:42 uevent
当对drivers_probe、drivers_autoprobe和uevent进行读写的时候,将会调用到相应的show和store函数,下面详细分析store和show函数:
static ssize_t store_drivers_probe(struct bus_type *bus,
const char *buf, size_t count)
{
struct device *dev;
/*从platform_bus上查找指定name的device*/
dev = bus_find_device_by_name(bus, NULL, buf);
{
return bus_find_device(bus, start, (void *)name, match_name);
{
struct klist_iter i;
struct device *dev;
/*platform_bus*/
if (!bus || !bus->p)
return NULL;
/*遍历platform的device链表,并调用match_name函数进行name对比*/
klist_iter_init_node(&bus->p->klist_devices, &i,
(start ? &start->p->knode_bus : NULL));
{
i->i_klist = k;
i->i_cur = n;
if (n)
kref_get(&n->n_ref);
}
while ((dev = next_device(&i)))
if (match(dev, data) && get_device(dev))
break;
klist_iter_exit(&i);
return dev;
}
}
if (!dev)
return -ENODEV;
/*将上面查找到的device绑定相应driver,并调用probe函数*/
if (bus_rescan_devices_helper(dev, NULL) != 0)
{
int ret = 0;
if (!dev->driver) {
if (dev->parent) /* Needed for USB */
device_lock(dev->parent);
/*将device和driver绑定并调用probe函数,后面会在device注册的时候详细分析此函数*/
ret = device_attach(dev);
if (dev->parent)
device_unlock(dev->parent);
}
return ret < 0 ? ret : 0;
}
return -EINVAL;
return count;
}
当向 drivers_probe 文件中写入一个device名字的时候,将会在platform总线device链表platform_bus->p->klist_devices上查找该设备,如果查找到,将会查找相应的driver(还记得device和drive匹配的match函数吗),匹配成功则想用驱动中的probe函数。有没有觉得很像driver或device的注册过程?
static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf)
{
return sprintf(buf, "%d\n", bus->p->drivers_autoprobe);
}
static ssize_t store_drivers_autoprobe(struct bus_type *bus,
const char *buf, size_t count)
{
if (buf[0] == '0')
bus->p->drivers_autoprobe = 0;
else
bus->p->drivers_autoprobe = 1;
return count;
}
上面的两个函数就是读写 bus->p->drivers_autoprobe ,这个成员的作用将会在device或者driver注册过程中分析,其实就是在将device或driver注册到platform总线后是否自动进行相应的match,当然初始化的时候就已经设置为1了(priv->drivers_autoprobe = 1;)。
static ssize_t bus_uevent_store(struct bus_type *bus,
const char *buf, size_t count)
{
enum kobject_action action;
if (kobject_action_type(buf, count, &action) == 0)
kobject_uevent(&bus->p->subsys.kobj, action);
return count;
}
还记得代码中的 kobject_uevent(&k->kobj, KOBJ_ADD) ,uevent文件为用户空间提供的接口,可通过指令触发add和remove等命令,如 echo add > uevent 。 kobject_uevent会发送event给用户空间,udev检测到时间会进行相应的/dev/目录下节点的创建和删除。
下一节将进行class的分析。