上篇主要对总线进行了一些梳理,现在来看看内核对于设备的处理过程。
struct device {
struct device *parent; /*指向父上游的指针*/
struct device_private *p;
struct kobject kobj; /*用它联系到sysfs中*/
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; /*属于那条总线*/
struct device_driver *driver; /*那个驱动能匹配该设备*/
void *platform_data; /* Platform specific data, device
.
.
.
void (*release)(struct device *dev);
};
其中比较重要的是释放函数,当这个设备被删除时,内核调用该方法,所有的注册到内核的设备结构必须有也给释放方法。和总线一样,device也有一个联系总线,设备,驱动程序的数据结构。
/* interface for exporting device attributes */
struct device_attribute {
struct attribute attr;
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
};
//定义DEVICE_ATTR ( _name , _mode , _show , _store ) \
struct device_attribute dev_attr _ ## _ name = __ATTR ( _name , _mode , _show , _store )
该接口用于向外暴露设备属性。
同总线过程一样,device的初始化也是在driver_init完成初始化的:
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;
/*创建dev目录下的block目录*/
sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
if (!sysfs_dev_block_kobj)
goto block_kobj_err;
/*创建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;
}
这个函数干的事情很明显,就是创建在sysfs中的设备目录和dev目录,还在dev目录下创建了block和char两个子目录。
下面分析下设备的注册过程:
int device_register(struct device *dev)
{
/*设备初始化*/
device_initialize(dev);
/*将设备添加到系统中*/
return device_add(dev);
}
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);
}
device_initialize主要是完成设备结构的初始化,把设备中能初始化的部分全部初始化。
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_private 空间并初始化*/
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的名字*/
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);/*将设备加入到sys/中*/
if (error)
goto Error;
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
/*创建uevent文件*/
error = device_create_file(dev, &dev_attr_uevent);
if (error)
goto attrError;
/*dev与class创建软链接*/
error = device_add_class_symlinks(dev);
if (error)
goto SymlinkError;
error = device_add_attrs(dev);
if (error)
goto AttrsError;
/*将设备加入到bus的设备链表中*/
error = bus_add_device(dev);
if (error)
goto BusError;
error = dpm_sysfs_add(dev);
if (error)
goto DPMError;
device_pm_add(dev);
/*没有设置主设备号,就创建dev*/
if (MAJOR(dev->devt)) {
error = device_create_file(dev, &dev_attr_dev);
if (error)
goto DevAttrError;
/*创建软链接*/
error = device_create_sys_dev_entry(dev);
if (error)
goto SysEntryError;
devtmpfs_create_node(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;
}
第8行:增加设备的引用计数
第12-17行:分配device_private空间并初始化
第24-36行:设置驱动的名字
第51行:将设备加入到sys文件系统中
第59行:创建设备属性uevent文件
第63行:创建dev和class的软链接
第70行:将设备加入到bus的设备链表中
第99行:为设备寻找合适的驱动,我们重点看下这个函数
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);
}
第10行:drivers_autoprobe在总线注册时被设置为1,所以这里执行device_attach进行设备和驱动的匹配
int device_attach(struct device *dev)
{
int ret = 0;
device_lock(dev);
if (dev->driver) {
if (klist_node_attached(&dev->p->knode_driver)) {
ret = 1;
goto out_unlock;
}
ret = device_bind_driver(dev);
if (ret == 0)
ret = 1;
else {
dev->driver = NULL;
ret = 0;
}
} else {
/*进行驱动和设备的匹配*/
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
pm_request_idle(dev);
}
out_unlock:
device_unlock(dev);
return ret;
}
device_attach函数中走到19行,进行设备和驱动的匹配:
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
void *data, int (*fn)(struct device_driver *, void *))
{
struct klist_iter i;
struct device_driver *drv;
int error = 0;
if (!bus)
return -EINVAL;
klist_iter_init_node(&bus->p->klist_drivers, &i,
start ? &start->p->knode_bus : NULL);
while ((drv = next_driver(&i)) && !error)
error = fn(drv, data);
klist_iter_exit(&i);
return error;
}
第13-14行:遍历bus上所有的驱动,调用__device_attach进行匹配,为设备找驱动。
static int __device_attach(struct device_driver *drv, void *data)
{
struct device *dev = data;
/*调用bus-match函数进行匹配*/
if (!driver_match_device(drv, dev))
return 0;
/*
匹配成功以后执行probe
如果bus有probe函数,优先执行,
否则执行drvier的probe函数
*/
return driver_probe_device(drv, dev);
}
__device_attach调用driver_match_device进行匹配:
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
匹配函数是在总线注册时,实现的match函数。在匹配成功后,调用probe函数:
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
int ret = 0;
.
ret = really_probe(dev, drv);
.
return ret;
}
static int really_probe(struct device *dev, struct device_driver *drv)
{
int ret = 0;
int local_trigger_count = atomic_read(&deferred_trigger_count);
atomic_inc(&probe_count);
pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
drv->bus->name, __func__, drv->name, dev_name(dev));
WARN_ON(!list_empty(&dev->devres_head));
dev->driver = drv;
.
.
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;
}
if (dev->pm_domain && dev->pm_domain->sync)
dev->pm_domain->sync(dev);
driver_bound(dev);
ret = 1;
pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
goto done;
.
.
}
really_probe函数中重点看17-25行,如果bus->probe不为空,则执行总线的probe函数,如果不存在,则查看drv->probe,不为空的话,执行驱动的probe函数。
下面来看下例子:
extern bus_type my_bus_type;
extern struct device my_bus;
static void my_dev_release(struct device *dev)
{
}
struct device my_dev = {
.bus = &my_bus_type,
.parent = &my_bus,
.release = my_dev_release,
};
static ssize_t mydev_show(struct device *dev, char * buf)
{
return sprintf(buf, "%s \n", "这是我的设备");
}
static DEVICE_ATTR(dev, S_IRUGO, mydev_show, NULL);
static int __init my_device_init(void)
{
int ret = 0 ;
/ *初始化设备* /
strncpy(my_dev.bus_id , “my_dev” , BUS_ID_SIZE );
/ *注册设备* /
device_register(&my_dev);
device_create_file (&my_dev, &dev_attr_dev);
return ret;
}