SPI、I2C等集成的外设控制器可以认为是platform设备,这些设备的注册过程以及其驱动的注册过程都会
用到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;
}


Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐