1. 概述
Linux设备模型的目的:为内核建立一个统一的设备模型,从而又一个对系统结构的一般性抽象描述。
换句话说,Linux设备模型提取了设备操作的共同属性,进行抽象,并将这部分共同的属性在内核中实现,而为需要新添加设备或驱动提供一般性的统一接口,这使得驱动程序的开发变得更简单了,而程序员只需要去学习接口就行了。
在内核里,有各种各样的总线,如 usb_bus_type
、spi_bus_type
、pci_bus_type
、platform_bus_type
、i2c_bus_type
等,内核通过总线将设备与驱动分离。
设备模型是层次的结构,层次的每一个节点都是通过kobject
实现的,在文件上则体现在sysfs
文件系统。
简单来说,bus
负责维护 注册进来的devcie
与 driver
,每注册进来一个device
或者 driver
都会调用 Bus->match
函数 将device
与driver
进行配对,并将它们加入链表。
如果配对成功,调用Bus->probe
或者driver->probe
函数, 调用kobject_uevent
函数设置环境变量(通知用户空间),mdev
进行创建设备节点等操作。
对于整个 设备总线驱动模型 的样子,大概如下图:
接下来从Bus
、driver
到 device
三个部分进行详细的分析。
1.1 总线:
总线(bus
)是linux发展过程中抽象出来的一种设备模型,为了统一管理所有的设备,内核中每个设备都会被挂载在总线上,这个bus
可以是对应硬件的bus
(i2c bus
、spi bus
)、可以是虚拟bus
(platform bus
)。
bus将所有挂在上面的具体设备抽象成两部分,device_driver
和device
。
1.2 driver与device
driver
实现了同类型设备的驱动程序实现,而device
则向系统注册具体的设备需要的资源,每当添加一个新的driver
(device
)到bus
中时,都将调用bus
的match
函数,试图寻找匹配的device
(driver
)。
如果匹配成功,就调用probe
函数,在probe
函数中实现设备的初始化、各种配置以及生成用户空间的文件接口。
probe
函数是总线在匹配成功时调用的函数,bus->probe
和drv->probe
中只会有一个起效,同时存在时使用bus->probe
。
1.3 bus, driver, device 框架
linux
的外围设备驱动,都是通过 bus + driver + device
来管理的,其实也好理解 ,外设都是通过总线来与cpu
通讯的。kernel
会实现各种总线的规范以及设备管理(设备检测,驱动绑定等),驱动程序只需要注册自己的驱动,实现对设备的读写控制即可。
这类驱动通常是2
个层次:总线子系统 + 驱动模块,它的流程大概是:
bus_register(xx)
:kernel
里面的各bus
子系统(如:serio
,usb
,pci
, …)会使用该函数来注册自己。driver_register(xx)
驱动模块使用它来向总线系统注册自己,这样驱动模块只需要关注相应driver
接口的实现。通常,bus
子系统会对driver_register
来进行封装,如:serio
提供serio_register_driver()
usb
提供usb_register_driver()
pci
提供pci_register_driver()
registe_device(xx)
各总线除了管理driver
外,还管理device
,通常会提供一支API
来添加设备,如:input_register_device
,serio_add_port
.实现上都是通过一个链表对设备进行管理,通常是在初始化或者probe
的时候, 添加设备。
设备(device
)指的是具体实现总线协议的物理设备,如对serio
总线而言,i8042
就是它的一个设备,而该总线连接的设备(鼠标,键盘)则是一个serio driver
。
2. 初始化
2.1 driver_init
所有的bus
都是在buses_init
,kernel启动以后,进行初始化,最终执行到:
// init/main.c
kernel_init();
kernel_init_freeable();
do_basic_setup();
driver_init(); // 注意这个
do_initcalls();
/**
* 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();// 初始化device
buses_init();// 初始化bus
classes_init();
firmware_init();
hypervisor_init();
/* These are also core pieces, but must come after the
* core core pieces.
*/
of_core_init();
platform_bus_init();
auxiliary_bus_init();
cpu_dev_init();
memory_dev_init();
node_dev_init();
container_dev_init();
}
注意devices_init
与buses_init
这两个函数会创建一些对应的对象,我们能够在sysfs
中看到这些对应的对象,在后续中会用到。
2.2 devices_init
// driversbase/base.h
struct kset *devices_kset;
extern struct kset *devices_kset;
// drivers/base/core.c
int __init devices_init(void)
{
// 创建 /sys/devices
devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
if (!devices_kset)
return -ENOMEM;
// 创建 /sys/dev
dev_kobj = kobject_create_and_add("dev", NULL);
if (!dev_kobj)
goto dev_kobj_err;
// 创建 /sys/dev/block
sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
if (!sysfs_dev_block_kobj)
goto block_kobj_err;
// 创建 /sys/dev/char
sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
if (!sysfs_dev_char_kobj)
goto char_kobj_err;
return 0;
char_kobj_err:
kobject_put(sysfs_dev_block_kobj);
block_kobj_err:
kobject_put(dev_kobj);
dev_kobj_err:
kset_unregister(devices_kset);
return -ENOMEM;
}
2.3 buses_init
// drivers/base/bus.c
static struct kset *system_kset;
static struct kset *bus_kset;
int __init buses_init(void)
{
// 创建 /sys/bus
bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
if (!bus_kset)
return -ENOMEM;
// 创建 /sys/devices/system
system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj);
if (!system_kset)
return -ENOMEM;
return 0;
}
同样是调用了kset_create_and_add
,这里涉及到了kset
、kobject
这些概念。
2.4 kset_create_and_add
kobject_create_and_add
这个函数首先会调用kobject_create
来分配并初始化一个kobject
对象,然后调用kobject_add
函数在sysfs文件系统中为新生成的kobject
对象建立一个新的目录。
/*lib/kobject.c*/
/**
* kset_create_and_add() - Create a struct kset dynamically and add it to sysfs.
*
* @name: the name for the kset
* @uevent_ops: a struct kset_uevent_ops for the kset
* @parent_kobj: the parent kobject of this kset, if any.
*
* This function creates a kset structure dynamically and registers it
* with sysfs. When you are finished with this structure, call
* kset_unregister() and the structure will be dynamically freed when it
* is no longer being used.
*
* If the kset was not able to be created, NULL will be returned.
*/
struct kset *kset_create_and_add(const char *name,
const struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj)
{
struct kset *kset;
int error;
kset = kset_create(name, uevent_ops, parent_kobj);
if (!kset)
return NULL;
error = kset_register(kset);
if (error) {
kfree(kset);
return NULL;
}
return kset;
}
EXPORT_SYMBOL_GPL(kset_create_and_add);
此后,其他bus
通过 bus_register
进行注册,实际上会注册到 bus_kest
中。
3. bus
3.1 bus_type原型
/*./include/linux/device/bus.h*/
/**
* 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.
* @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 positive value if the
* given device can be handled by the given driver and zero
* otherwise. It may also return error code if determining that
* the driver supports the device is not possible. In case of
* -EPROBE_DEFER it will queue the device for deferred probing.
* @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.
* @sync_state: Called to sync device state to software state after all the
* state tracking consumers linked to this device (present at
* the time of late_initcall) have successfully bound to a
* driver. If the device has no consumers, this function will
* be called at late_initcall_sync level. If the device has
* consumers that are never bound to a driver, this function
* will never get called until they do.
* @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.
* @num_vf: Called to find out how many virtual functions a device on this
* bus supports.
* @dma_configure: Called to setup DMA configuration on a device on
* this bus.
* @dma_cleanup: Called to cleanup DMA configuration on a device on
* this bus.
* @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
* @need_parent_lock: When probing or removing a device on this bus, the
* device core should lock the device's parent.
*
* 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;
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);
void (*sync_state)(struct device *dev);
void (*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);
int (*num_vf)(struct device *dev);
int (*dma_configure)(struct device *dev);
void (*dma_cleanup)(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;
bool need_parent_lock;
};
3.2 注册bus:bus_register
bus.c
和 driver.c
分别对 bus
,driver
和device
进行管理,提供注册bus
, driver
和查找 device
功能。
bus_register(*bus)
这个函数会生成两个list
,用来保存设备和驱动。
/*drivers/base/bus.c*/
/**
* 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);
/* 1. bus 与 prv 相互建立联系 */
if (!priv)
return -ENOMEM;
// 私有数据 .bus -> bus 本身
priv->bus = bus;
// bus->p 指向 priv
bus->p = priv;
// 内核通知链
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
/* 2. 设置 bus->prv->subsys->kobj */
// 设置 priv->subsys.kobj.name = bus->name 对应于/sys/ 目录下的目录名
retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
if (retval)
goto out;
// 所有的 priv->subsys.kobj.kset 指向 bus_kse 对应于图中④与六的关系
priv->subsys.kobj.kset = bus_kset;
// 所有的priv->subsys.kobj.ktype 等于 bus_ktype
priv->subsys.kobj.ktype = &bus_ktype;
priv->drivers_autoprobe = 1;
/* 3. 注册 kset (bus->prv->subsys priv->devices_kset priv->drivers_kset) */
// 注册 priv->subsys ,由于 priv->subsys.kobj.kset = bus_kset,所以会在 /sys/bus/目录下创建 目录 如/sys/bus/plateform
retval = kset_register(&priv->subsys);
if (retval)
goto out;
retval = bus_create_file(bus, &bus_attr_uevent);
if (retval)
goto bus_uevent_fail;
// 由于 priv->subsys.kobj.kset = bus_kset ,因此会创建 /sys/bus/XXX/devices 目录 如 /sys/bus/plateform/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/XXX/devices 目录 如 /sys/bus/plateform/drivers
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_devices 并设置get put 函数 初始化 klist_drivers 不知为何没有get put ?
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;
// 添加 bus->attrs 属性文件
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;
}
EXPORT_SYMBOL_GPL(bus_register);
目前,能通过 bus_register
函数处理的工作有:
- 将
Bus
与priv
相互建立联系,用于处理私有数据 - 注册通知链
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier)
; - 设置
bus->priv->subsys(kset).kobj
的名字为bus->name
- 设置
bus->priv->subsys(kset).kobj.kset
指向bus_kset
- 设置
bus->priv->subsys(kset).kobj.ktype
为bus_ktype
,提供show store
函数 - 设置
bus->priv->drivers_autoprobe = 1
; - 注册
bus->priv->subsys(kset)
:对应于图中④与⑥的关系
由于4,且没有指定
bus->priv->subsys(kset).kobj.Parent
,会将bus_kest.kobj
设置为bus->priv->subsys(kset).kobj.Parent
因此,会将bus->priv->subsys(kset).kobj.entry
加入bus_kest
链表,且会在/sys/bus
目录下创建相应的总线目录/sys/bus/$(bus->name)
,例如/sys/bus/platform
- 创建
bus_attr_uevent->attr
属性文件 - 创建并注册
devices_kset
,devices_kset.kobj.parent = bus->priv->subsys.kobj
,名字为device
,因此会创建/sys/bus/$(bus->name)/devices
- 创建并注册
drivers_kset
,drivers_kset.kobj.parent = bus->priv->subsys.kobj
,名字为drivers
,因此会创建/sys/bus/$(bus->name)/drivers
- 初始化
bus->priv->klist_devices
链表 - 初始化
bus->priv->klist_drivers
链表 - 创建
bus->bus_attrs
属性文件
4. driver
4.1 整体流程
driver_register(drv) [core.c]
bus_add_driver(drv) [bus.c]
if (drv->bus->p->drivers_autoprobe)
driver_attach(dev)[dd.c]
bus_for_each_dev(dev->bus, NULL, drv,__driver_attach)
__driver_attach(dev, drv) [dd.c]
driver_match_device(drv, dev) [base.h]
drv-bus->match ? drv->bus-amatch(dev, drv) : 1
if false, return;
driver_probe_device(drv, dev) [dd.c]
really_probe(dev, drv) [dd.c]
dev-driver = drv;
if (dev-bus->probe)
dev->bus->probe(dev);
else if (drv->probe)
drv-aprobe(dev);
probe_failed:
dev->-driver = NULL;
4.2 device_driver原型
/*./include/linux/device/driver.h*/
/**
* struct device_driver - The basic device driver structure
* @name: Name of the device driver.
* @bus: The bus which the device of this driver belongs to.
* @owner: The module owner.
* @mod_name: Used for built-in modules.
* @suppress_bind_attrs: Disables bind/unbind via sysfs.
* @probe_type: Type of the probe (synchronous or asynchronous) to use.
* @of_match_table: The open firmware table.
* @acpi_match_table: The ACPI match table.
* @probe: Called to query the existence of a specific device,
* whether this driver can work with it, and bind the driver
* to a specific device.
* @sync_state: Called to sync device state to software state after all the
* state tracking consumers linked to this device (present at
* the time of late_initcall) have successfully bound to a
* driver. If the device has no consumers, this function will
* be called at late_initcall_sync level. If the device has
* consumers that are never bound to a driver, this function
* will never get called until they do.
* @remove: Called when the device is removed from the system to
* unbind a device from this driver.
* @shutdown: Called at shut-down time to quiesce the device.
* @suspend: Called to put the device to sleep mode. Usually to a
* low power state.
* @resume: Called to bring a device from sleep mode.
* @groups: Default attributes that get created by the driver core
* automatically.
* @dev_groups: Additional attributes attached to device instance once
* it is bound to the driver.
* @pm: Power management operations of the device which matched
* this driver.
* @coredump: Called when sysfs entry is written to. The device driver
* is expected to call the dev_coredump API resulting in a
* uevent.
* @p: Driver core's private data, no one other than the driver
* core can touch this.
*
* The device driver-model tracks all of the drivers known to the system.
* The main reason for this tracking is to enable the driver core to match
* up drivers with new devices. Once drivers are known objects within the
* system, however, a number of other things become possible. Device drivers
* can export information and configuration variables that are independent
* of any specific device.
*/
struct device_driver {
const char *name;
struct bus_type *bus;
struct module *owner;
const char *mod_name; /* used for built-in modules */
bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
enum probe_type probe_type;
const struct of_device_id *of_match_table;
const struct acpi_device_id *acpi_match_table;
int (*probe) (struct device *dev);
void (*sync_state)(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 attribute_group **groups;
const struct attribute_group **dev_groups;
const struct dev_pm_ops *pm;
void (*coredump) (struct device *dev);
struct driver_private *p;
};
4.3 注册驱动并匹配:driver_register
/*drivers/base/driver.c*/
/**
* driver_register - register driver with bus
* @drv: driver to register
*
* We pass off most of the work to the bus_add_driver() call,
* since most of the things we have to do deal with the bus
* structures.
*/
int driver_register(struct device_driver *drv)
{
int ret;
struct device_driver *other;
if (!drv->bus->p) {
pr_err("Driver '%s' was unable to register with bus_type '%s' because the bus was not initialized.\n",
drv->name, drv->bus->name);
return -EINVAL;
}
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown))
pr_warn("Driver '%s' needs updating - please use "
"bus_type methods\n", drv->name);
// 判断是否被注册过了。
other = driver_find(drv->name, drv->bus);
if (other) {
pr_err("Error: Driver '%s' is already registered, "
"aborting...\n", drv->name);
return -EBUSY;
}
// 1、添加驱动到bus中
ret = bus_add_driver(drv);
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups);
if (ret) {
bus_remove_driver(drv);
return ret;
}
kobject_uevent(&drv->p->kobj, KOBJ_ADD);
return ret;
}
EXPORT_SYMBOL_GPL(driver_register);
driver_register
做了这几件事情:
- 判断
driver
是否被注册过:通过名字查找总线中是否已经存在同名的对象 - 把驱动添加进
bus
中, - 进行通知到用户空间。
4.4 在bus_add_driver中注册
// drivers/base/base.h
struct driver_private {
struct kobject kobj;
struct klist klist_devices;
struct klist_node knode_bus;
struct module_kobject *mkobj;
struct device_driver *driver;
};
// drivers/base/bus.c
/**
* bus_add_driver - Add a driver to the bus.
* @drv: driver.
*/
int bus_add_driver(struct device_driver *drv)
{
struct bus_type *bus;
// 驱动的私有数据
struct driver_private *priv;
int error = 0;
// 找到对应的总线
bus = bus_get(drv->bus);
if (!bus)
return -EINVAL;
pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
error = -ENOMEM;
goto out_put_bus;
}
// 初始化私有数据,并登记到 驱动 中
klist_init(&priv->klist_devices, NULL, NULL);
priv->driver = drv;
drv->p = priv;
// 在/sys/bus/xxx/drivers 目录下创建目录
priv->kobj.kset = bus->p->drivers_kset;
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
"%s", drv->name);
if (error)
goto out_unregister;
// 将 driver 加入 Bus 的 drivers 链表中
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
// 1、匹配 dev
if (drv->bus->p->drivers_autoprobe) {
error = driver_attach(drv);
if (error)
goto out_unregister;
}
// 如果设置了drv->mod_name 根据名字寻找模块
module_add_driver(drv->owner, drv);
// 在/sys/bus/xxx/drivers/创建属性文件
error = driver_create_file(drv, &driver_attr_uevent);
if (error) {
printk(KERN_ERR "%s: uevent attr (%s) failed\n",
__func__, drv->name);
}
error = driver_add_groups(drv, bus->drv_groups);
if (error) {
/* How the hell do we get out of this pickle? Give up */
printk(KERN_ERR "%s: driver_add_groups(%s) failed\n",
__func__, drv->name);
}
if (!drv->suppress_bind_attrs) {
error = add_bind_files(drv);
if (error) {
/* Ditto */
printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
__func__, drv->name);
}
}
return 0;
out_unregister:
kobject_put(&priv->kobj);
/* drv->p is freed in driver_release() */
drv->p = NULL;
out_put_bus:
bus_put(bus);
return error;
}
在向Bus
注册一个driver
时,会调用到 driver_attch
来寻找与之配对的 deivice
。
4.4.1 driver_attach
从逻辑上来说,一个驱动可以支持多个设备;一个设备只能绑定一个驱动。
因此,driver_attach
最终一一遍历目前所有的驱动和设备,并绑定对应的设备。
/*drivers/base/dd.c*/
/**
* driver_attach - try to bind driver to devices.
* @drv: driver.
*
* Walk the list of devices that the bus has on it and try to
* match the driver with each one. If driver_probe_device()
* returns 0 and the @dev->driver is set, we've found a
* compatible pair.
*/
int driver_attach(struct device_driver *drv)
{
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
EXPORT_SYMBOL_GPL(driver_attach);
4.4.2 遍历bus_for_each_dev
/*drivers/base/bus.c*/
/**
* bus_for_each_dev - device iterator.
* @bus: bus type.
* @start: device to start iterating from.
* @data: data for the callback.
* @fn: function to be called for each device.
*
* Iterate over @bus's list of devices, and call @fn for each,
* passing it @data. If @start is not NULL, we use that device to
* begin iterating from.
*
* We check the return of @fn each time. If it returns anything
* other than 0, we break out and return that value.
*
* NOTE: The device that returns a non-zero value is not retained
* in any way, nor is its refcount incremented. If the caller needs
* to retain this data, it should do so, and increment the reference
* count in the supplied callback.
*/
int bus_for_each_dev(struct bus_type *bus, struct device *start,
void *data, int (*fn)(struct device *, void *))
{
// 迭代器,在这里用于遍历device
struct klist_iter i;
struct device *dev;
int error = 0;
if (!bus || !bus->p)
return -EINVAL;
// 设置迭代器的起点为 链表的头部
klist_iter_init_node(&bus->p->klist_devices, &i,
(start ? &start->p->knode_bus : NULL));
while (!error && (dev = next_device(&i)))
error = fn(dev, data);
klist_iter_exit(&i);
return error;
}
EXPORT_SYMBOL_GPL(bus_for_each_dev);
根据名字我们应该能猜测出来,调用Bus
的每一个 dev
与 driver
进行__driver_attach
。
具体的做法是通过初始化一个迭代器指向链表的头部(在这里是bus->p->klist_devices
),然后通过next_device
进行遍历,并逐一执行fn
方法。
/*lib/klist.c*/
/**
* klist_iter_init_node - Initialize a klist_iter structure.
* @k: klist we're iterating.
* @i: klist_iter we're filling.
* @n: node to start with.
*
* Similar to klist_iter_init(), but starts the action off with @n,
* instead of with the list head.
*/
void klist_iter_init_node(struct klist *k, struct klist_iter *i,
struct klist_node *n)
{
i->i_klist = k;
i->i_cur = NULL;
if (n && kref_get_unless_zero(&n->n_ref))
i->i_cur = n;
}
EXPORT_SYMBOL_GPL(klist_iter_init_node);
// drivers/base/core.c
static struct device *next_device(struct klist_iter *i)
{
struct klist_node *n = klist_next(i);
struct device *dev = NULL;
struct device_private *p;
if (n) {
p = to_device_private_parent(n);
dev = p->device;
}
return dev;
}
// drivers/base/base.h
#define to_device_private_parent(obj) \
container_of(obj, struct device_private, knode_parent)
我们看看fn
,在这里它执行的是__driver_attach
。
static int __driver_attach(struct device *dev, void *data)
{
struct device_driver *drv = data;
int ret;
/*
* Lock device and try to bind to it. We drop the error
* here and always return 0, because we need to keep trying
* to bind to devices and some drivers will return an error
* simply if it didn't support the device.
*
* driver_probe_device() will spit a warning if there
* is an error.
*/
//当设备和驱动的名字不匹配的时候返回的是0,然后就会调用下面的return 0;
ret = driver_match_device(drv, dev);//驱动和设备是否匹配,不匹配将退出,bus_for_each_dev函数继续下一个设备来匹配
if (ret == 0) {
/* no match */
return 0;
} else if (ret == -EPROBE_DEFER) {
dev_dbg(dev, "Device match requests probe deferral\n");
driver_deferred_probe_add(dev);
} else if (ret < 0) {
dev_dbg(dev, "Bus failed to match device: %d", ret);
return ret;
} /* ret > 0 means positive match */
if (dev->parent && dev->bus->need_parent_lock)
device_lock(dev->parent);
device_lock(dev);
// 2、从这里开始probe
if (!dev->p->dead && !dev->driver)
driver_probe_device(drv, dev);//调用探测函数进行探测,并且调用platform_driver中的probe函数
device_unlock(dev);
if (dev->parent && dev->bus->need_parent_lock)
device_unlock(dev->parent);
return 0;
}
在 __driver_attach
中,首先会调用到 driver_match_device
函数(return drv->bus->match ? drv->bus->match(dev, drv) : 1;
)进行匹配:
/*drivers/base/base.h*/
static inline int driver_match_device(struct device_driver *drv, struct device *dev)
{
return drv->bus->match ? drv->bus->match(dev, drv) : 1;//调用match函数,如果没用则默认返回1
}
当match
方法存在时,进行匹配,返回0
代表成功。否则代表失败。
如果匹配成功,则继续调用 driver_probe_device(drv, dev)
/*drivers/base/dd.c*/
/**
* driver_probe_device - attempt to bind device & driver together
* @drv: driver to bind a device to
* @dev: device to try to bind to the driver
*
* This function returns -ENODEV if the device is not registered, -EBUSY if it
* already has a driver, 0 if the device is bound successfully and a positive
* (inverted) error code for failures from the ->probe method.
*
* This function must be called with @dev lock held. When called for a
* USB interface, @dev->parent lock must be held as well.
*
* If the device has a parent, runtime-resume the parent before driver probing.
*/
static int driver_probe_device(struct device_driver *drv, struct device *dev)
{
int trigger_count = atomic_read(&deferred_trigger_count);
int ret;
atomic_inc(&probe_count);
ret = __driver_probe_device(drv, dev);
if (ret == -EPROBE_DEFER || ret == EPROBE_DEFER) {
driver_deferred_probe_add(dev);
/*
* Did a trigger occur while probing? Need to re-trigger if yes
*/
if (trigger_count != atomic_read(&deferred_trigger_count) &&
!defer_all_probes)
driver_deferred_probe_trigger();
}
atomic_dec(&probe_count);
wake_up_all(&probe_waitqueue);
return ret;
}
static int __driver_probe_device(struct device_driver *drv, struct device *dev)
{
int ret = 0;
if (dev->p->dead || !device_is_registered(dev))
return -ENODEV;
if (dev->driver)
return -EBUSY;
dev->can_match = true;
pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
pm_runtime_get_suppliers(dev);
if (dev->parent)
pm_runtime_get_sync(dev->parent);
pm_runtime_barrier(dev);
if (initcall_debug)
ret = really_probe_debug(dev, drv);
else
ret = really_probe(dev, drv);
pm_request_idle(dev);
if (dev->parent)
pm_runtime_put(dev->parent);
pm_runtime_put_suppliers(dev);
return ret;
}
4.5 在really_probe中probe
4.5.1 device原型
/*include/linux/device.h*/
/**
* struct device - The basic device structure
* @parent: The device's "parent" device, the device to which it is attached.
* In most cases, a parent device is some sort of bus or host
* controller. If parent is NULL, the device, is a top-level device,
* which is not usually what you want.
* @p: Holds the private data of the driver core portions of the device.
* See the comment of the struct device_private for detail.
* @kobj: A top-level, abstract class from which other classes are derived.
* @init_name: Initial name of the device.
* @type: The type of device.
* This identifies the device type and carries type-specific
* information.
* @mutex: Mutex to synchronize calls to its driver.
* @bus: Type of bus device is on.
* @driver: Which driver has allocated this
* @platform_data: Platform data specific to the device.
* Example: For devices on custom boards, as typical of embedded
* and SOC based hardware, Linux often uses platform_data to point
* to board-specific structures describing devices and how they
* are wired. That can include what ports are available, chip
* variants, which GPIO pins act in what additional roles, and so
* on. This shrinks the "Board Support Packages" (BSPs) and
* minimizes board-specific #ifdefs in drivers.
* @driver_data: Private pointer for driver specific info.
* @links: Links to suppliers and consumers of this device.
* @power: For device power management.
* See Documentation/driver-api/pm/devices.rst for details.
* @pm_domain: Provide callbacks that are executed during system suspend,
* hibernation, system resume and during runtime PM transitions
* along with subsystem-level and driver-level callbacks.
* @pins: For device pin management.
* See Documentation/driver-api/pinctl.rst for details.
* @msi_list: Hosts MSI descriptors
* @msi_domain: The generic MSI domain this device is using.
* @numa_node: NUMA node this device is close to.
* @dma_ops: DMA mapping operations for this device.
* @dma_mask: Dma mask (if dma'ble device).
* @coherent_dma_mask: Like dma_mask, but for alloc_coherent mapping as not all
* hardware supports 64-bit addresses for consistent allocations
* such descriptors.
* @bus_dma_mask: Mask of an upstream bridge or bus which imposes a smaller DMA
* limit than the device itself supports.
* @dma_pfn_offset: offset of DMA memory range relatively of RAM
* @dma_parms: A low level driver may set these to teach IOMMU code about
* segment limitations.
* @dma_pools: Dma pools (if dma'ble device).
* @dma_mem: Internal for coherent mem override.
* @cma_area: Contiguous memory area for dma allocations
* @archdata: For arch-specific additions.
* @of_node: Associated device tree node.
* @fwnode: Associated device node supplied by platform firmware.
* @devt: For creating the sysfs "dev".
* @id: device instance
* @devres_lock: Spinlock to protect the resource of the device.
* @devres_head: The resources list of the device.
* @knode_class: The node used to add the device to the class list.
* @class: The class of the device.
* @groups: Optional attribute groups.
* @release: Callback to free the device after all references have
* gone away. This should be set by the allocator of the
* device (i.e. the bus driver that discovered the device).
* @iommu_group: IOMMU group the device belongs to.
* @iommu_fwspec: IOMMU-specific properties supplied by firmware.
*
* @offline_disabled: If set, the device is permanently online.
* @offline: Set after successful invocation of bus type's .offline().
* @of_node_reused: Set if the device-tree node is shared with an ancestor
* device.
*
* At the lowest level, every device in a Linux system is represented by an
* instance of struct device. The device structure contains the information
* that the device model core needs to model the system. Most subsystems,
* however, track additional information about the devices they host. As a
* result, it is rare for devices to be represented by bare device structures;
* instead, that structure, like kobject structures, is usually embedded within
* a higher-level representation of the device.
*/
struct device {
struct device *parent;
struct device_private *p;
struct kobject kobj;
const char *init_name; /* initial name of the device */
const struct device_type *type;
struct mutex mutex; /* mutex to synchronize calls to
* its driver.
*/
struct bus_type *bus; /* type of bus device is on */
struct device_driver *driver; /* which driver has allocated this
device */
void *platform_data; /* Platform specific data, device
core doesn't touch it */
void *driver_data; /* Driver data, set and get with
dev_set/get_drvdata */
struct dev_links_info links;
struct dev_pm_info power;
struct dev_pm_domain *pm_domain;
#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
struct irq_domain *msi_domain;
#endif
#ifdef CONFIG_PINCTRL
struct dev_pin_info *pins;
#endif
#ifdef CONFIG_GENERIC_MSI_IRQ
struct list_head msi_list;
#endif
#ifdef CONFIG_NUMA
int numa_node; /* NUMA node this device is close to */
#endif
const struct dma_map_ops *dma_ops;
u64 *dma_mask; /* dma mask (if dma'able device) */
u64 coherent_dma_mask;/* Like dma_mask, but for
alloc_coherent mappings as
not all hardware supports
64 bit addresses for consistent
allocations such descriptors. */
u64 bus_dma_mask; /* upstream dma_mask constraint */
unsigned long dma_pfn_offset;
struct device_dma_parameters *dma_parms;
struct list_head dma_pools; /* dma pools (if dma'ble) */
struct dma_coherent_mem *dma_mem; /* internal for coherent mem
override */
#ifdef CONFIG_DMA_CMA
struct cma *cma_area; /* contiguous memory area for dma
allocations */
#endif
/* arch specific additions */
struct dev_archdata archdata;
struct device_node *of_node; /* associated device tree node */
struct fwnode_handle *fwnode; /* firmware device node */
dev_t devt; /* dev_t, creates the sysfs "dev" */
u32 id; /* device instance */
spinlock_t devres_lock;
struct list_head devres_head;
struct klist_node knode_class;
struct class *class;
const struct attribute_group **groups; /* optional groups */
void (*release)(struct device *dev);
struct iommu_group *iommu_group;
struct iommu_fwspec *iommu_fwspec;
bool offline_disabled:1;
bool offline:1;
bool of_node_reused:1;
};
4.5.2 device和driver绑定
当增加新device
的时候,bus
会轮循它的驱动列表来找到一个匹配的驱动,它们是通过device id
和 driver
的id_table
来进行 ”匹配”的,主要是在 driver_match_device()[drivers/base/base.h]
通过 bus->match()
这个callback
来让驱动判断是否支持该设备,一旦匹配成功,device
的driver
字段会被设置成相应的driver
指针 :
/*drivers/base/dd.c*/
static int really_probe(struct device *dev, struct device_driver *drv)
{
bool test_remove = IS_ENABLED(CONFIG_DEBUG_TEST_DRIVER_REMOVE) &&
!drv->suppress_bind_attrs;
int ret;
if (defer_all_probes) {
/*
* Value of defer_all_probes can be set only by
* device_block_probing() which, in turn, will call
* wait_for_device_probe() right after that to avoid any races.
*/
dev_dbg(dev, "Driver %s force probe deferral\n", drv->name);
return -EPROBE_DEFER;
}
ret = device_links_check_suppliers(dev);
if (ret)
return ret;
pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
drv->bus->name, __func__, drv->name, dev_name(dev));
if (!list_empty(&dev->devres_head)) {
dev_crit(dev, "Resources present before probing\n");
ret = -EBUSY;
goto done;
}
re_probe:
// 1、关联 dev 与 drv
dev->driver = drv;
/* If using pinctrl, bind pins now before probing */
ret = pinctrl_bind_pins(dev);
if (ret)
goto pinctrl_bind_failed;
if (dev->bus->dma_configure) {
ret = dev->bus->dma_configure(dev);
if (ret)
goto pinctrl_bind_failed;
}
// 2、更新 sysfs
ret = driver_sysfs_add(dev);
if (ret) {
pr_err("%s: driver_sysfs_add(%s) failed\n",
__func__, dev_name(dev));
goto sysfs_failed;
}
if (dev->pm_domain && dev->pm_domain->activate) {
ret = dev->pm_domain->activate(dev);
if (ret)
goto probe_failed;
}
// 3、执行真正的probe
ret = call_driver_probe(dev, drv);
if (ret) {
/*
* Return probe errors as positive values so that the callers
* can distinguish them from other errors.
*/
ret = -ret;
goto probe_failed;
}
ret = device_add_groups(dev, drv->dev_groups);
if (ret) {
dev_err(dev, "device_add_groups() failed\n");
goto dev_groups_failed;
}
if (dev_has_sync_state(dev)) {
ret = device_create_file(dev, &dev_attr_state_synced);
if (ret) {
dev_err(dev, "state_synced sysfs add failed\n");
goto dev_sysfs_state_synced_failed;
}
}
if (test_remove) {
test_remove = false;
device_remove(dev);
driver_sysfs_remove(dev);
device_unbind_cleanup(dev);
goto re_probe;
}
pinctrl_init_done(dev);
if (dev->pm_domain && dev->pm_domain->sync)
dev->pm_domain->sync(dev);
// 4、绑定
driver_bound(dev);
pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
goto done;
dev_sysfs_state_synced_failed:
dev_groups_failed:
device_remove(dev);
probe_failed:
driver_sysfs_remove(dev);
sysfs_failed:
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_DRIVER_NOT_BOUND, dev);
if (dev->bus && dev->bus->dma_cleanup)
dev->bus->dma_cleanup(dev);
pinctrl_bind_failed:
device_links_no_driver(dev);
device_unbind_cleanup(dev);
done:
return ret;
}
然后 callback
该 driver
的 probe
或者 connect
函数,进行一些初始化操作。
同理,当增加新的driver
时,bus
也会执行相同的动作,为驱动查找设备。因此,绑定发生在两个阶段:
- 驱动找设备,发生在
driver
向bus
系统注册自己时候,函数调用链是:driver_register --> bus_add_driver --> driver_attach() [dd.c] -- 将轮循device链表,查找匹配的device。
- 设备查找驱动,发生在设备增加到总线的的时候,函数调用链是:
device_add --> bus_probe_device --> device_initial_probe --> device_attach -- 将轮循driver链表,查找匹配的driver。
匹配成功后,系统继续调用 driver_probe_device()
来 callback drv->probe(dev)'
或者 bus->probe(dev) -->drv->connect()
,在probe
或者connect
函数里面,驱动开始实际的初始化操作。因此,probe()
或者 connect()
是真正的驱动入口。
really_probe
中干了四件大事。
4.5.2.1 关联dev与drv
在 dev
中记录 driver
:
dev->driver = drv;
已经match
上了配对成功了嘛,所以可以将该device
和driver
关联起来:dev -> drv
然而device_driver
中并没有device
成员,因此并没有drv ->dev
。
4.5.2.2 通知bus、更新sysfs
driver_sysfs_add(dev);
- 通知总线绑定了设备和驱动
- 创建两个
symlink
,更新sysfs
- 在
sysfs
中该dev.kobj
目录下创建与之匹配的driver
的符号连接,名字为driver
- 在
sysfs
中该driver.kobj
目录下创建与之匹配的device的符号连接,名字为kobject_name(&dev->kobj)
- 在
/*drivers/base/dd.c*/
static int driver_sysfs_add(struct device *dev)
{
int ret;
/ 通知总线绑定了设备和驱动
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_BIND_DRIVER, dev);
/* 例如,
在/sys/bus/XXX/drivers/XXX 目录下建立symlink,链接名为 kobj->name,
链接指向 /sys/devices/platform/XXX
*/
ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj,
kobject_name(&dev->kobj));
if (ret)
goto fail;
/* 例如,
在/sys/devices/platform/XXX/下建立symlink,链接名为driver,
指向/sys/bus/xxx/drivers目录下的某个目录
*/
ret = sysfs_create_link(&dev->kobj, &dev->driver->p->kobj,
"driver");
if (ret)
goto rm_dev;
if (!IS_ENABLED(CONFIG_DEV_COREDUMP) || !dev->driver->coredump)
return 0;
ret = device_create_file(dev, &dev_attr_coredump);
if (!ret)
return 0;
sysfs_remove_link(&dev->kobj, "driver");
rm_dev:
sysfs_remove_link(&dev->driver->p->kobj,
kobject_name(&dev->kobj));
fail:
return ret;
}
4.5.2.3 执行真正的 probe 方法
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) {
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
probe
的规则是:如果BUS上实现了probe
就用BUS的probe
;否则才会用driver
的probe
。
4.5.2.4 绑定
driver_bound(dev);
将 device
放入 driver
链表中。
看来一个device
只能有一个driver
,但是driver
可以支持多个device
/*drivers/base/dd.c*/
static void driver_bound(struct device *dev)
{
// 判断是否绑定过
if (device_is_bound(dev)) {
pr_warn("%s: device %s already bound\n",
__func__, kobject_name(&dev->kobj));
return;
}
pr_debug("driver: '%s': %s: bound to device '%s'\n", dev->driver->name,
__func__, dev_name(dev));
// 将 device 放入 driver 链表中。
klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);
device_links_driver_bound(dev);
device_pm_check_callbacks(dev);
/*
* Make sure the device is no longer in one of the deferred lists and
* kick off retrying all pending devices
*/
driver_deferred_probe_del(dev);
driver_deferred_probe_trigger();
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_BOUND_DRIVER, dev);
kobject_uevent(&dev->kobj, KOBJ_BIND);
}
//lib/klist.c
/**
* klist_node_attached - Say whether a node is bound to a list or not.
* @n: Node that we're testing.
*/
int klist_node_attached(struct klist_node *n)
{
return (n->n_klist != NULL);
}
EXPORT_SYMBOL_GPL(klist_node_attached);
4.5.3 kobject_uevent通知用户空间
主要是在/sys/devices/.../
中添加dev
的uevent
属性文件。
5. device
5.1 整体流程
device_register(dev)[core.c]
device_initialize()
device_add(dev) [core.c]
bus_add_device(dev)
bus_probe_device(dev) [bus.c]
if (dev->bus && dev->bus-op->drivers_autoprobe)
device_attach(dev) [dd.c]
if (dev->driver)
device_bind_driver(dev)
else // 从这里开始,与 driver一样
bus_for_each_dev(dev->bus, NULL, drv,__driver_attach)
__driver_attach(dev, drv) [dd.c]
driver_match_device(drv, dev) [base.h]
drv-bus->match ? drv->bus-amatch(dev, drv) : 1
if false, return;
driver_probe_device(drv, dev) [dd.c]
really_probe(dev, drv) [dd.c]
dev-driver = drv;
if (dev-bus->probe)
dev->bus->probe(dev);
else if (drv->probe)
drv-aprobe(dev);
probe_failed:
dev->-driver = NULL;
5.2 device原型
上面已经贴出来了源码了,这里不重复占用空间了。
/**
* driver_register - register driver with bus
* @drv: driver to register
*
* We pass off most of the work to the bus_add_driver() call,
* since most of the things we have to do deal with the bus
* structures.
*/
int driver_register(struct device_driver *drv)
{
int ret;
struct device_driver *other;
if (!drv->bus->p) {
pr_err("Driver '%s' was unable to register with bus_type '%s' because the bus was not initialized.\n",
drv->name, drv->bus->name);
return -EINVAL;
}
//检测总线的操作函数和驱动的操作函数是否同时存在
//三种可能都会被考虑进行注册的动作,
//drv的总线probe和drv的probe都已定义
//drv的总线remove和drv的remove都已经定义
//drv的总线shutdown和drv的shutdown都已经进行定义
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown))
printk(KERN_WARNING "Driver '%s' needs updating - please use "
"bus_type methods\n", drv->name);
//去已经注册的driver链表上查看,该driver是否已经被注册,防止重复注册同一个driver
other = driver_find(drv->name, drv->bus);//这里查找drv->name="hub"驱动是否已经在drv->bus总线上注册,并增加引用计数,若已经注册,则返回提示信息
if (other) {
printk(KERN_ERR "Error: Driver '%s' is already registered, "
"aborting...\n", drv->name);
return -EBUSY;
}
/把这个driver添加到该总线维护的driver链表中
ret = bus_add_driver(drv);//如果之前没注册,就在此注册
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups);
if (ret) {
bus_remove_driver(drv);
return ret;
}
kobject_uevent(&drv->p->kobj, KOBJ_ADD);
return ret;
}
EXPORT_SYMBOL_GPL(driver_register);
5.3 device_add
// drviers/base/core.c
/**
* device_add - add device to device hierarchy.
* @dev: device.
*
* This is part 2 of device_register(), though may be called
* separately _iff_ device_initialize() has been called separately.
*
* This adds @dev to the kobject hierarchy via kobject_add(), adds it
* to the global and sibling lists for the device, then
* adds it to the other relevant subsystems of the driver model.
*
* Do not call this routine or device_register() more than once for
* any device structure. The driver model core is not designed to work
* with devices that get unregistered and then spring back to life.
* (Among other things, it's very hard to guarantee that all references
* to the previous incarnation of @dev have been dropped.) Allocate
* and register a fresh new struct device instead.
*
* NOTE: _Never_ directly free @dev after calling this function, even
* if it returned an error! Always use put_device() to give up your
* reference instead.
*/
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;
// 初始化 device的 私有数据
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()
*/
/* 初始化设备内部的kobject的名字 */
// 如果初始名字(init_name)存在,则设为名字 为 init_name
if (dev->init_name) {
dev_set_name(dev, "%s", dev->init_name);
dev->init_name = NULL;
}
/* subsystems can specify simple device enumeration */
// 如果检查发现没有名字,但bus设置了设备名前缀,则以 类似foo%u的方式来设置设备的名字
// 例如 tty0
if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
// 增加设备父设备并增加父设备引用计数,例如:csid的设备节点节v4l-subdev4的父设备是fd8c0000.qcom,msm-cam
parent = get_device(dev->parent);
// 获取v4l-subdev4设备目录的父目录是video4linux,video4linux的父目录是fd8c0000.qcom,msm-cam
kobj = get_device_parent(dev, parent);
// 在kobject层实现设备父子关系
if (kobj)
dev->kobj.parent = kobj;
/* first, register with generic layer. */
/* we require the name to be set before, and pass NULL */
/*
把内嵌的kobject注册到设备模型中将设备加入到kobject模型中,
创建sys相关目录 ,目录名字为kobj->name
*/
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
if (error)
goto Error;
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
// 创建sys目录下设备的uevent属性文件,通过它可以查看设备的uevent事件
error = device_create_file(dev, &dev_attr_uevent);
if (error)
goto attrError;
// 如果有主次设备号 创建dev 属性文件
if (MAJOR(dev->devt)) {
// 在 /sys/devices中创建设备节点
error = device_create_file(dev, &dev_attr_dev);
/* 在/sys/dev/char/或者/sys/dev/block/创建devt的属性的连接文件,
形如10:45,由主设备号和次设备号构成,指向/sys/devices/.../的具体设备目录,
该链接文件只具备读属性,显示主设备号:次设备号,如10:45,用户空间udev相应uevent事件时,将根据设备号在/dev下创建节点文件
*/
error = device_create_sys_dev_entry(dev);
devtmpfs_create_node(dev);
}
// 创建类符号链接,相互创建dev和class之间的链接文件
error = device_add_class_symlinks(dev);
// 创建sys目录下设备其他属性文件
error = device_add_attrs(dev);
// 将设备加入到管理它的bus总线的设备链表上
// 创建subsystem链接文件,链接class下的具体的子系统文件夹
error = bus_add_device(dev);
error = dpm_sysfs_add(dev);
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;
}
EXPORT_SYMBOL_GPL(device_add);
5.4 bus_add_device
/**
* bus_add_device - add device to bus
* @dev: device being added
*
* - Add device's bus attributes.
* - Create links to device's bus.
* - Add the device to its bus's list of devices.
*/
int bus_add_device(struct device *dev)
{
/* 引用计数加一 */
struct bus_type *bus = bus_get(dev->bus);
int error = 0;
if (bus) {
pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev));
/* 创建相应的属性文件 */
error = device_add_attrs(bus, dev);
// 添加属性组。
error = device_add_groups(dev, bus->dev_groups);
// 创建 /sys/bus/$(bus->name)/devices/$(dev->name) 到 /sys/devices/$(dev->name) 的软连接
error = sysfs_create_link(&bus->p->devices_kset->kobj,
&dev->kobj, dev_name(dev));
// 创建 /sys/devices/$(dev->name)/subsystem 到 /sys/bus/$(bus->name)/devices/$(dev->name) 的软连接
error = sysfs_create_link(&dev->kobj,
&dev->bus->p->subsys.kobj, "subsystem");
if (error)
goto out_subsys;
// 将 dev 加入 bus 所管理的 devices 链表
klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
}
return 0;
}
接下来就和我之前的文章介绍的Linux设备驱动和设备匹配过程文章中一样了,总共分为两种匹配方式。
refer to