SPI、I2C等集成的外设控制器可以认为是platform设备,这些设备的注册过程以及其驱动的注册过程都会
用到platform_device_register()和platform_driver_register()函数,本文就分析这两个函数。
首先,先看下我自己在用的开发板内核源代码中和SPI控制器有关的代码。
I2C控制器注册时会调用platform_device_register(),注册驱动时会调用platform_driver_register()
1.下面分析platform_device_register()注册函数,参数就用SPI控制器的参数。
该函数做的工作主要有创建设备目录,将设备添加到总线上,检测是否有合适的驱动,具体看代码。
platform_device_add()和device_add()相比,也只是多了一个insert_resource()
1.1
Platform_device_add和device_add最主要的区别是多了一步insert_resource(p, r),
即将platform资源(resource)添加进内核,由内核统一管理
本例中用到了IORESOURCE_MEM,IORESOURCE_IRQ两种资源,其中IORESOURCE_MEM资源的根定义如下:
resource的设置和用法比较标准,这里不再分析了。
1.2.2.1 创建目录kobject_add()
上文提到kobject_add()会调用kobject_add_internal()创建目录,其实创建目录之前还会设置kobj->entry。
因为在1.1中初始化了kobj.kset,所以会执行下面的代码段
1.2.2.2 将设备添加到总线上
当然,此时还没注册SPI控制器的驱动程序,所以检测不到。但还是分析一下吧
主要工作是遍历总线上的所有驱动,调用总线中match函数判断是否匹配,匹配则将他们绑定在一起,
并调用总线或者驱动中的probe函数
用到platform_device_register()和platform_driver_register()函数,本文就分析这两个函数。
首先,先看下我自己在用的开发板内核源代码中和SPI控制器有关的代码。
a.SPI控制器用platform_device结构表示,本例中的CPU型号是pnx8xxx,所以SPI控制器的名字设置为spi-pnx8xxx
static struct platform_device pnx8xxx_spi2_master = {
.name = "spi-pnx8xxx",
.id = -1,
.resource = pnx8xxx_spi2_resources,
.num_resources = ARRAY_SIZE(pnx8xxx_spi2_resources),
.dev = {
.platform_data = &pnx8xxx_spi2_mdata,
}
};
其中用到的全局变量有
static struct resource pnx8xxx_spi2_resources[] = {
{
.name = "spi-registers",
.flags = IORESOURCE_MEM,
.start = 0xC2002000, /* spi2_global */
.end = 0xC2002408, /* spi2_timer_status */
},
{
.name = "spi-interrupt",
.flags = IORESOURCE_IRQ,
.start = 26,
.end = 26,
},
};
struct pnx8xxx_mdata pnx8xxx_spi2_mdata = {
.bus_num = 1,
.num_chipselect = 100,
.cs_control = NULL,
.clock = "spi1",
};
SPI控制器的注册函数如下:
static __init int spi_init(void)
{
...
/* Register the master */
err = platform_device_register(&pnx8xxx_spi2_master);
...
}
b.SPI控制器驱动的注册函数如下:
static struct platform_driver driver = {
.driver = {
.name = "spi-pnx8xxx",//名字和上面一样
.owner = THIS_MODULE,
.bus = &platform_bus_type,
},
.remove = __exit_p(pnx_remove),
};
static int __init pnx_spi_init(void)
{
return platform_driver_probe(&driver, pnx_probe);//本文也会分析函数pnx_probe()的调用流程
}
I2C和SPI类似,分析I2C的代码也会发现I2C控制器用platform_device表示,
I2C控制器注册时会调用platform_device_register(),注册驱动时会调用platform_driver_register()
1.下面分析platform_device_register()注册函数,参数就用SPI控制器的参数。
该函数做的工作主要有创建设备目录,将设备添加到总线上,检测是否有合适的驱动,具体看代码。
platform_device_register(&pnx8xxx_spi2_master);
int platform_device_register(struct platform_device *pdev)
{
device_initialize(&pdev->dev);
return platform_device_add(pdev);
}
platform_device_register()和device_register()十分类似,
platform_device_add()和device_add()相比,也只是多了一个insert_resource()
1.1
void device_initialize(struct device *dev)
{
/*父容器(父kset)指向devices_kset
*devices_kset在上篇文章中分析过,代表/sys/devices目录
*表示spi-pnx8xxx对象(kobj)属于/sys/devices这个对象集合(kset)
*/
dev->kobj.kset = devices_kset;
/*初始化本对象(kobj),并设置kobj->ktype*/
kobject_init(&dev->kobj, &device_ktype);
/*设置其他元素*/
INIT_LIST_HEAD(&dev->dma_pools);
init_MUTEX(&dev->sem);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
device_init_wakeup(dev, 0);
device_pm_init(dev);
set_dev_node(dev, -1);
}
1.2
int platform_device_add(struct platform_device *pdev)
{
int i, ret = 0;
if (!pdev)
return -EINVAL;
/*设置父设备,platform_bus表示/sys/devices/platform目录,
*可以在该目录下找到spi-pnx8xxx目录
*/
if (!pdev->dev.parent)
pdev->dev.parent = &platform_bus;
/*设置所属总线,platform_bus_type表示/sys/bus/platform目录,
*可以在/sys/bus/platform/devices目录下找到spi-pnx8xxx文件
*该文件是个软链接,等下会见到创建该链接的函数
*/
pdev->dev.bus = &platform_bus_type;
/*设置pdev->dev->kobj->name*/
if (pdev->id != -1)
dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
else
dev_set_name(&pdev->dev, "%s", pdev->name);
/*1.2.1 将资源插入资源树,以后驱动程序再从资源树中获取*/
for (i = 0; i < pdev->num_resources; i++) {
struct resource *p, *r = &pdev->resource[i];
/*设置name*/
if (r->name == NULL)
r->name = dev_name(&pdev->dev);
/*设置parent*/
p = r->parent;
if (!p) {
if (resource_type(r) == IORESOURCE_MEM)
p = &iomem_resource;
else if (resource_type(r) == IORESOURCE_IO)
p = &ioport_resource;
}
/*插入资源树*/
if (p && insert_resource(p, r)) {
printk(KERN_ERR
"%s: failed to claim resource %d\n",
dev_name(&pdev->dev), i);
ret = -EBUSY;
goto failed;
}
}
pr_debug("Registering platform device '%s'. Parent at %s\n",
dev_name(&pdev->dev), dev_name(pdev->dev.parent));
/*1.2.2 添加设备到设备层次结构中 add device to device hierarchy.*/
ret = device_add(&pdev->dev);
if (ret == 0)
return ret;
failed:
while (--i >= 0) {
struct resource *r = &pdev->resource[i];
unsigned long type = resource_type(r);
if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
release_resource(r);
}
return ret;
}
1.2.1 Inserts a resource in the resource tree
Platform_device_add和device_add最主要的区别是多了一步insert_resource(p, r),
即将platform资源(resource)添加进内核,由内核统一管理
本例中用到了IORESOURCE_MEM,IORESOURCE_IRQ两种资源,其中IORESOURCE_MEM资源的根定义如下:
struct resource iomem_resource = {
.name = "PCI mem",
.start = 0,
.end = -1,
.flags = IORESOURCE_MEM,
};
IORESOURCE_IRQ不用插入?只用插入IORESOURCE_MEM资源。
resource的设置和用法比较标准,这里不再分析了。
1.2.2 add device to device hierarchy
device_add(&pdev->dev);
之前的文章有讲过device_add,当时因为dev->parent和dev-bus等都为NULL,现在dev->parent和dev-bus都有设置,
就重新分析下。
int device_add(struct device *dev)
{
struct device *parent = NULL;
struct class_interface *class_intf;
int error = -EINVAL;
/*增加引用计数*/
dev = get_device(dev);
if (!dev)
goto done;
/*初始化设备的私有数据,主要是klist_children的初始化,以后会用到*/
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()
*/
/*NULL*/
if (dev->init_name) {
dev_set_name(dev, "%s", dev->init_name);
dev->init_name = NULL;
}
if (!dev_name(dev))
goto name_error;
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
/*获取父设备,并增加其引用计数*/
parent = get_device(dev->parent);
/*将dev->kobj.parent,指向父设备的kobj*/
setup_parent(dev, parent);
/*和CONFIG_NUMA有关,没用到*/
/* 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 */
/*1.2.2.1该函数会调用kobject_add_internal()中的create_dir()函数,创建目录*/
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
if (error)
goto Error;
/*为NULL*/
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
/*在目录下创建uevent文件*/
error = device_create_file(dev, &uevent_attr);
if (error)
goto attrError;
/*为NULL*/
if (MAJOR(dev->devt)) {
error = device_create_file(dev, &devt_attr);
if (error)
goto ueventattrError;
error = device_create_sys_dev_entry(dev);
if (error)
goto devtattrError;
devtmpfs_create_node(dev);
}
/*为NULL*/
error = device_add_class_symlinks(dev);
if (error)
goto SymlinkError;
/*为NULL*/
error = device_add_attrs(dev);
if (error)
goto AttrsError;
/*1.2.2.2 add device to bus 将设备添加到总线上*/
error = bus_add_device(dev);
if (error)
goto BusError;
/*为NULL*/
error = dpm_sysfs_add(dev);
if (error)
goto DPMError;
/*为NULL*/
device_pm_add(dev);
/* Notify clients of device addition. This call must come
* after dpm_sysf_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);
/*1.2.2.3 probe drivers for a new device 检测是否有合适的驱动*/
bus_probe_device(dev);
/*将dev->p->knode_parent添加到父设备parent->p->klist_children中*/
if (parent)
klist_add_tail(&dev->p->knode_parent,
&parent->p->klist_children);
/*为NULL*/
if (dev->class) {
mutex_lock(&dev->class->p->class_mutex);
/* tie the class to the device */
klist_add_tail(&dev->knode_class,
&dev->class->p->class_devices);
/* notify any interfaces that the device is here */
list_for_each_entry(class_intf,
&dev->class->p->class_interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
mutex_unlock(&dev->class->p->class_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))
device_remove_sys_dev_entry(dev);
devtattrError:
if (MAJOR(dev->devt))
device_remove_file(dev, &devt_attr);
ueventattrError:
device_remove_file(dev, &uevent_attr);
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.2.2.1 创建目录kobject_add()
上文提到kobject_add()会调用kobject_add_internal()创建目录,其实创建目录之前还会设置kobj->entry。
因为在1.1中初始化了kobj.kset,所以会执行下面的代码段
if (kobj->kset) {
/*parent指向父设备,不为NULL*/
if (!parent)
parent = kobject_get(&kobj->kset->kobj);
/*将kobj->entry添加都到父容器kset->list中*/
kobj_kset_join(kobj);
kobj->parent = parent;
}
这个过程会在本文最后的结构图中体现
1.2.2.2 将设备添加到总线上
bus_add_device(dev);
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));
/*根据bus->dev_attrs创建文件,这里创建了/sys/devices/platform/spi-pnx8xxx文件
*bus->dev_attrs在platform_bus_type中定义。
*/
error = device_add_attrs(bus, dev);
if (error)
goto out_put;
/*创建一个软连接,/sys/bus/platform/devices/spi-pnx8xxx ->
*/sys/devices/platform/spi-pnx8xxx
*第一个参数中bus表示/sys/bus/platform,devices_kset表示
*/sys/bus/platform/devices/
*/
error = sysfs_create_link(&bus->p->devices_kset->kobj,
&dev->kobj, dev_name(dev));
if (error)
goto out_id;
/*创建软连接
*/sys/devices/platform/spi-pnx8xxx/subsystem ->
*/sys/bus/platform/
*/
error = sysfs_create_link(&dev->kobj,
&dev->bus->p->subsys.kobj, "subsystem");
if (error)
goto out_subsys;
/*和CONFIG_SYSFS_DEPRECATED有关,没用到*/
error = make_deprecated_bus_links(dev);
if (error)
goto out_deprecated;
/*将dev->p->knode_bus添加到bus->p->klist_devices中,从而将设备注册到总线上
*以后可以遍历总线的bus->p->klist_devices链表找到该设备
*/
klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
}
return 0;
out_deprecated:
sysfs_remove_link(&dev->kobj, "subsystem");
out_subsys:
sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev));
out_id:
device_remove_attrs(bus, dev);
out_put:
bus_put(dev->bus);
return error;
}
1.2.2.3 probe drivers for a new device 检测是否有合适的驱动
当然,此时还没注册SPI控制器的驱动程序,所以检测不到。但还是分析一下吧
主要工作是遍历总线上的所有驱动,调用总线中match函数判断是否匹配,匹配则将他们绑定在一起,
并调用总线或者驱动中的probe函数
bus_probe_device(dev);
void bus_probe_device(struct device *dev)
{
struct bus_type *bus = dev->bus;
int ret;
/*判断drivers_autoprobe*/
if (bus && bus->p->drivers_autoprobe) {
ret = device_attach(dev);
WARN_ON(ret < 0);
}
}
int device_attach(struct device *dev)
{
int ret = 0;
down(&dev->sem);
/*为NULL*/
if (dev->driver) {
ret = device_bind_driver(dev);
if (ret == 0)
ret = 1;
else {
dev->driver = NULL;
ret = 0;
}
} else {
/*执行该分支*/
pm_runtime_get_noresume(dev);
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
pm_runtime_put_sync(dev);
}
up(&dev->sem);
return ret;
}
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_drivers,对每个驱动调用__device_attach检测是否匹配*/
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;
}
static int __device_attach(struct device_driver *drv, void *data)
{
struct device *dev = data;
/*调用match函数,判断是否匹配。可以参照platform_bus_type中的platform_match()函数*/
if (!driver_match_device(drv, dev))
return 0;
/*若匹配,就将他们绑定在一起,并执行驱动中的probe函数*/
return driver_probe_device(drv, dev);
}
判断是否匹配
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
绑定,并执行驱动中的probe函数
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
int ret = 0;
if (!device_is_registered(dev))
return -ENODEV;
pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
pm_runtime_get_noresume(dev);
pm_runtime_barrier(dev);
ret = really_probe(dev, drv);
pm_runtime_put_sync(dev);
return ret;
}
static int really_probe(struct device *dev, struct device_driver *drv)
{
int ret = 0;
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 (driver_sysfs_add(dev)) {
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
__func__, dev_name(dev));
goto probe_failed;
}
/*执行总线上的probe函数或者驱动中probe函数
*总线上的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;
}
/*将dev->p->knode_driver添加到ev->driver->p->klist_devices中
*因为一个驱动程序可以对应多个设备
*/
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;
probe_failed:
devres_release_all(dev);
driver_sysfs_remove(dev);
dev->driver = NULL;
if (ret != -ENODEV && ret != -ENXIO) {
/* driver matched but the probe failed */
printk(KERN_WARNING
"%s: probe of %s failed with error %d\n",
drv->name, dev_name(dev), ret);
}
/*
* Ignore errors returned by ->probe so that the next driver can try
* its luck.
*/
ret = 0;
done:
atomic_dec(&probe_count);
wake_up(&probe_waitqueue);
return ret;
}