内核在初始化的过程中调用platform_bus_init()函数来初始化平台总线,调用流程如下
kernel_init_freeable()->do_basic_setup() -> driver_init()->platform_bus_init()
我们直接分析platform_bus_init函数
drivers/base/platform.c , linux代码还是很简洁的,在内核3.18这个文件代码1300多行
int __init platform_bus_init(void)
{
int error;
early_platform_cleanup();
error = device_register(&platform_bus);
if (error)
return error;
error = bus_register(&platform_bus_type);
if (error)
device_unregister(&platform_bus);
return error;
}
void __init early_platform_cleanup(void)
{
struct platform_device *pd, *pd2;
/* clean up the devres list used to chain devices */
list_for_each_entry_safe(pd, pd2, &early_platform_device_list,
dev.devres_head) {
list_del(&pd->dev.devres_head);
memset(&pd->dev.devres_head, 0, sizeof(pd->dev.devres_head));
}
}
struct device platform_bus = {
.init_name = "platform",
};
struct bus_type platform_bus_type = {
.name = "platform",
.dev_groups = platform_dev_groups,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
1 函数首先清空总线early_platform_device_list上的所有节点
2 调用device_register(platform_bus_type) 注册设备 , 这个设备为platform_bus
3 注册总线 bus_register(&platform_bus_type) , bus_type为platform_bus_type
我们就先围绕这三步分析platform bus device是如何注册的,第一步只是简单的清空链表,就不再说明了,后面我们再回来说明early_platform_device_list链表是做什么的
首先分析device_register函数, 在drivers/base/core.c文件下面
int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}
device_register明显是设备注册的含义,注册设备分为两步1 初始化,2 添加到设备系统
device_initialize 函数如下
void device_initialize(struct device *dev)
{
dev->kobj.kset = devices_kset;
kobject_init(&dev->kobj, &device_ktype);
INIT_LIST_HEAD(&dev->dma_pools);
mutex_init(&dev->mutex);
lockdep_set_novalidate_class(&dev->mutex);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
device_pm_init(dev);
set_dev_node(dev, -1);
}
static struct kobj_type device_ktype = {
.release = device_release,
.sysfs_ops = &dev_sysfs_ops,
.namespace = device_namespace,
};
主要做的工作
1把设备挂在到sys/device节点的kset下面,对应的kobj_type为device_ktype, device_ktype的device_release函数会在设备注销的时候做一些清理工作,这会关联到driver的操作,比较有意思。我们后面分析,现在主要分析注册流程
2 初始化锁和链表
3初始化电源管理
4 numa node
这些都不是我们的主要目的,主要目的是分析platform bus,所以就不深入分析了,初始化操作完成之后就是调用device_add函数将设备添加进系统,我们看看这会触发什么操作
int device_add(struct device *dev)
{
struct device *parent = NULL;
struct kobject *kobj;
struct class_interface *class_intf;
int error = -EINVAL;
dev = get_device(dev);
if (!dev)
goto done;
if (!dev->p) {
error = device_private_init(dev);
if (error)
goto done;
}
/*
* for statically allocated devices, which should all be converted
* some day, we need to initialize the name. We prevent reading back
* the name, and force the use of dev_name()
*/
if (dev->init_name) {
dev_set_name(dev, "%s", dev->init_name);
dev->init_name = NULL;
}
/* subsystems can specify simple device enumeration */
if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);
if (!dev_name(dev)) {
error = -EINVAL;
goto name_error;
}
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
parent = get_device(dev->parent);
kobj = get_device_parent(dev, parent);
if (kobj)
dev->kobj.parent = kobj;
/* use parent numa_node */
if (parent)
set_dev_node(dev, dev_to_node(parent));
/* first, register with generic layer. */
/* we require the name to be set before, and pass NULL */
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
if (error)
goto Error;
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
error = device_create_file(dev, &dev_attr_uevent);
if (error)
goto attrError;
if (MAJOR(dev->devt)) {
error = device_create_file(dev, &dev_attr_dev);
if (error)
goto ueventattrError;
error = device_create_sys_dev_entry(dev);
if (error)
goto devtattrError;
devtmpfs_create_node(dev);
}
error = device_add_class_symlinks(dev);
if (error)
goto SymlinkError;
error = device_add_attrs(dev);
if (error)
goto AttrsError;
error = bus_add_device(dev);
if (error)
goto BusError;
error = dpm_sysfs_add(dev);
if (error)
goto DPMError;
device_pm_add(dev);
/* Notify clients of device addition. This call must come
* after dpm_sysfs_add() and before kobject_uevent().
*/
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_ADD_DEVICE, dev);
kobject_uevent(&dev->kobj, KOBJ_ADD);
bus_probe_device(dev);
if (parent)
klist_add_tail(&dev->p->knode_parent,
&parent->p->klist_children);
if (dev->class) {
mutex_lock(&dev->class->p->mutex);
/* tie the class to the device */
klist_add_tail(&dev->knode_class,
&dev->class->p->klist_devices);
/* notify any interfaces that the device is here */
list_for_each_entry(class_intf,
&dev->class->p->interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
mutex_unlock(&dev->class->p->mutex);
}
done:
put_device(dev);
return error;
DPMError:
bus_remove_device(dev);
BusError:
device_remove_attrs(dev);
AttrsError:
device_remove_class_symlinks(dev);
SymlinkError:
if (MAJOR(dev->devt))
devtmpfs_delete_node(dev);
if (MAJOR(dev->devt))
device_remove_sys_dev_entry(dev);
devtattrError:
if (MAJOR(dev->devt))
device_remove_file(dev, &dev_attr_dev);
ueventattrError:
device_remove_file(dev, &dev_attr_uevent);
attrError:
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
kobject_del(&dev->kobj);
Error:
cleanup_device_parent(dev);
if (parent)
put_device(parent);
name_error:
kfree(dev->p);
dev->p = NULL;
goto done;
}
这个函数还是比较复杂的 ,我们逐步分析
1 增加device 引用计数
2 为device设置名称,其实就是设置kobject名称,用于创建kobj目录的时候使用,如果没有指定名称,则设置名称为bus_name, id,对于设备总线的名称为platform
3 设置kobj的父子关系与device的父子关系一致,parent一般情况下为总线或者某些控制器,主要反应设备的挂在关系,将这些关系反应到sys目录就是通过kobj体系
4 设置numa节点和父设备节点相同, 每个cpu对内存的访问速度是不同的,对自己所属的node访问速度要快,这就是numa,对于设备cpu的访问速度也是不同的,所以可能需要设备numa node,我自己的猜想
5 添加kobject到系统, 注意这里平台总线的父设备是不存在的,所以kobject的目录会创建到kset对应的目录下面
6 发出通知 platform_notify??? 稍后分析
7 创建uevent文件
8 创建设备属性文件
9 创建sys_dev_entry
10 创建tmfs node
12 创建class符号链接
13 创建其他设备自身的属性文件
14 dpm
15 pm
16 总线通知
17 KOBJ_ADD uevent
18 bus_probe_device 调用总线的probe函数
19 添加到设备链表
20 添加到class 链表
下面就按照这20步流程分析
前面5步都比较好理解,我从第6步开始详细分析
platform_notify主要用于电源管理,x86的实现在drivers/acpi/glue.c下的 acpi_platform_notify函数,这部分我们不分析了
7 创建uevent文件,这个attribute 的实现在kernel/module.c 文件下面,主要用于用户空间出发内核空间发送uevent事件,如向/dev/block/sda/uevent写入ADD,会出发uevent发出消息
8 error = device_create_file(dev, &dev_attr_dev);
9 如果class不为空,需要创建sys文件系统的class视图
10 通知这部分对platform bus不存在 跳过
11 bus_probe_device 在drivers/base/bus.c文件中
/**
* bus_probe_device - probe drivers for a new device
* @dev: device to probe
*
* - Automatically probe for a driver if the bus allows it.
*/
void bus_probe_device(struct device *dev)
{
struct bus_type *bus = dev->bus;
struct subsys_interface *sif;
int ret;
if (!bus)
return;
if (bus->p->drivers_autoprobe) {
ret = device_attach(dev);
WARN_ON(ret < 0);
}
mutex_lock(&bus->p->mutex);
list_for_each_entry(sif, &bus->p->interfaces, node)
if (sif->add_dev)
sif->add_dev(dev, sif);
mutex_unlock(&bus->p->mutex);
}
/**
* struct bus_type - The bus type of the device
*
* @name: The name of the bus.
* @dev_name: Used for subsystems to enumerate devices like ("foo%u", dev->id).
* @dev_root: Default device to use as the parent.
* @dev_attrs: Default attributes of the devices on the bus.
* @bus_groups: Default attributes of the bus.
* @dev_groups: Default attributes of the devices on the bus.
* @drv_groups: Default attributes of the device drivers on the bus.
* @match: Called, perhaps multiple times, whenever a new device or driver
* is added for this bus. It should return a nonzero value if the
* given device can be handled by the given driver.
* @uevent: Called when a device is added, removed, or a few other things
* that generate uevents to add the environment variables.
* @probe: Called when a new device or driver add to this bus, and callback
* the specific driver's probe to initial the matched device.
* @remove: Called when a device removed from this bus.
* @shutdown: Called at shut-down time to quiesce the device.
*
* @online: Called to put the device back online (after offlining it).
* @offline: Called to put the device offline for hot-removal. May fail.
*
* @suspend: Called when a device on this bus wants to go to sleep mode.
* @resume: Called to bring a device on this bus out of sleep mode.
* @pm: Power management operations of this bus, callback the specific
* device driver's pm-ops.
* @iommu_ops: IOMMU specific operations for this bus, used to attach IOMMU
* driver implementations to a bus and allow the driver to do
* bus-specific setup
* @p: The private data of the driver core, only the driver core can
* touch this.
* @lock_key: Lock class key for use by the lock validator
*
* A bus is a channel between the processor and one or more devices. For the
* purposes of the device model, all devices are connected via a bus, even if
* it is an internal, virtual, "platform" bus. Buses can plug into each other.
* A USB controller is usually a PCI device, for example. The device model
* represents the actual connections between buses and the devices they control.
* A bus is represented by the bus_type structure. It contains the name, the
* default attributes, the bus' methods, PM operations, and the driver core's
* private data.
*/
struct bus_type {
const char *name;
const char *dev_name;
struct device *dev_root;
struct device_attribute *dev_attrs; /* use dev_groups instead */
const struct attribute_group **bus_groups;
const struct attribute_group **dev_groups;
const struct attribute_group **drv_groups;
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 (*online)(struct device *dev);
int (*offline)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
const struct dev_pm_ops *pm;
const struct iommu_ops *iommu_ops;
struct subsys_private *p;
struct lock_class_key lock_key;
};
/**
* struct subsys_private - structure to hold the private to the driver core portions of the bus_type/class structure.
*
* @subsys - the struct kset that defines this subsystem
* @devices_kset - the subsystem's 'devices' directory
* @interfaces - list of subsystem interfaces associated
* @mutex - protect the devices, and interfaces lists.
*
* @drivers_kset - the list of drivers associated
* @klist_devices - the klist to iterate over the @devices_kset
* @klist_drivers - the klist to iterate over the @drivers_kset
* @bus_notifier - the bus notifier list for anything that cares about things
* on this bus.
* @bus - pointer back to the struct bus_type that this structure is associated
* with.
*
* @glue_dirs - "glue" directory to put in-between the parent device to
* avoid namespace conflicts
* @class - pointer back to the struct class that this structure is associated
* with.
*
* This structure is the one that is the actual kobject allowing struct
* bus_type/class to be statically allocated safely. Nothing outside of the
* driver core should ever touch these fields.
*/
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;
struct kset glue_dirs;
struct class *class;
};
bus_type的数据结构如上面,其中 subsys_private *p是什么? 它维护了一下该bus特有的数据,如果p->drivers_autoprobe为真,则添加设备的时候自动探测对应的驱动,执行device_attach函数,最后调用bus->p->interfaces下面的方法,靠,这里分析平台总线都是不需要的
继续往下看添加到链表的部分很简单,就不分析了。
总结下设备注册主要做的工作
1 创先sys文件目录和uevent事件,以及电源管理相关注册
2 执行prope绑定device和driver
上面分析的device的注册,其实这些和bus关系不大主要是要强行把bus弄成一种设备方便管理
下面就分析bus_register函数了
/**
* bus_register - register a driver-core subsystem
* @bus: bus to register
*
* Once we have that, we register the bus with the kobject
* infrastructure, then register the children subsystems it has:
* the devices and drivers that belong to the subsystem.
*/
int bus_register(struct bus_type *bus)
{
int retval;
struct subsys_private *priv;
struct lock_class_key *key = &bus->lock_key;
priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->bus = bus;
bus->p = priv;
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
if (retval)
goto out;
priv->subsys.kobj.kset = bus_kset;
priv->subsys.kobj.ktype = &bus_ktype;
priv->drivers_autoprobe = 1;
retval = kset_register(&priv->subsys);
if (retval)
goto out;
retval = bus_create_file(bus, &bus_attr_uevent);
if (retval)
goto bus_uevent_fail;
priv->devices_kset = kset_create_and_add("devices", NULL,
&priv->subsys.kobj);
if (!priv->devices_kset) {
retval = -ENOMEM;
goto bus_devices_fail;
}
priv->drivers_kset = kset_create_and_add("drivers", NULL,
&priv->subsys.kobj);
if (!priv->drivers_kset) {
retval = -ENOMEM;
goto bus_drivers_fail;
}
INIT_LIST_HEAD(&priv->interfaces);
__mutex_init(&priv->mutex, "subsys mutex", key);
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&priv->klist_drivers, NULL, NULL);
retval = add_probe_files(bus);
if (retval)
goto bus_probe_files_fail;
retval = bus_add_groups(bus, bus->bus_groups);
if (retval)
goto bus_groups_fail;
pr_debug("bus: '%s': registered\n", bus->name);
return 0;
bus_groups_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;
}
注释中说bus_register 注册driver-core 子系统
1 创建driver-core 也就是subsys_private结构,并设置与bus的关系
2 初始化bus_notifier链表,这个链表用于存放关系bus事件的通知函数
3 设置kobject名称
4 设置kset为bus_kset,也就是目录sys/bus,没有设置父目录,所以创建的sys目录为/sys/bus/${busname}
注意这里设置了priv->drivers_autoprobe = 1 所以会自动绑定设备
5 创建/sys/bus/
b
u
s
n
a
m
e
/
u
e
v
e
n
t
用
于
u
d
e
v
系
统
6
创
建
d
e
v
i
c
e
s
和
d
r
i
v
e
r
s
两
个
k
s
e
t
,
目
录
分
别
为
/
s
y
s
/
b
u
s
/
{busname}/uevent 用于udev系统 6 创建devices和drivers两个kset ,目录分别为/sys/bus/
busname/uevent 用于udev系统6创建devices和drivers两个kset,目录分别为/sys/bus/{busname}/devices , /sys/bus/
b
u
s
n
a
m
e
/
d
r
i
v
e
r
s
7
初
始
化
p
r
i
v
−
>
k
l
i
s
t
d
e
v
i
c
e
s
和
p
r
i
v
−
>
k
l
i
s
t
d
r
i
v
e
r
s
列
表
8
a
d
d
p
r
o
b
e
f
i
l
e
s
,
主
要
创
建
/
s
y
s
/
b
u
s
/
{busname}/drivers 7 初始化priv->klist_devices 和priv->klist_drivers 列表 8 add_probe_files, 主要创建/sys/bus/
busname/drivers7初始化priv−>klistdevices和priv−>klistdrivers 列表8addprobefiles,主要创建/sys/bus/{busname}/drivers_probe和 /sys/bus/
b
u
s
n
a
m
e
/
d
r
i
v
e
r
s
a
u
t
o
p
r
o
b
e
/
s
y
s
/
b
u
s
/
{busname}/drivers_autoprobe /sys/bus/
busname/driversautoprobe/sys/bus/{busname}/drivers_probe支持的功能是你向这个文件写入设备名称后,会重新扫描bus上对应的驱动(执行attach_device函数)。/sys/bus/${busname}/drivers_autoprobe主要用于设置priv->drivers_autoprobe的值,来告诉bus是否自动执行attach_device函数
9 bus_add_groups 创建goups相关的文件 platform-bus不存在
这样总线的注册我们就分析完了 总结一下
主要就是初始化subsys_private数据结构,创建相应的sys目录和链表
这样我们就可以分析platform添加设备的过程了