linux驱动platform总线的实现原理

https://mp.csdn.net/postedit/80850526

https://mp.csdn.net/postedit/81124536

https://mp.csdn.net/postedit/81145148

前两篇文章以例子的形式举例了platform总线的使用,

第三篇文章结合前两篇文章抽象出公有的部分,以通用性描述总结了platform总线如何使用,以及注意点,这篇文章分析一下platfrom总线的是如何让实现的。

 paltform总线对Linux驱动工程师是非常重要的,因为编写的大多数设备驱动,都是借用了plaftom总线来实现。

下面就是platform在驱动层次中的体现。

这里写图片描述

other driver主要是对platform总线的使用。我在前面的三个链接都有体现。

platform则是我们这次分析的重点。

底层的设备模型我会在后面分析。

扫描二维码关注公众号,回复: 2520022 查看本文章

说明:

内核启动时,要完成一定的初始化操作之后,才会处理device和driver的注册及probe,因此在这之前,常规的platform设备是无法使用的。但是在Linux中,有些设备需要尽早使用(如在启动过程中充当console输出的serial设备),所以platform模块提供了一种称作Early platform device/driver的机制,允许驱动开发人员,在开发驱动时,向内核注册可在内核早期启动过程中使用的driver。这些机制提供了如下接口:

int __init early_platform_driver_register(struct early_platform_driver *epdrv, char *buf);
void __init early_platform_add_devices(struct platform_device **devs, int num);
void __init early_platform_driver_register_all(char *class_str);
early_platform_match(struct early_platform_driver *epdrv, int id);
static  __init int early_platform_left(struct early_platform_driver *epdrv,	int id);			  
static int __init early_platform_driver_probe_id(char *class_str,int id, int nr_probe);
int __init early_platform_driver_probe(char *class_str,int nr_probe, int user_only);
void __init early_platform_cleanup(void);

在我的2.6.35.7的内核中,只有console是用了early_platform总线的。这个因为应用比较少,这里先不分析,等有空了我学习了再补充这个博客。

1.platform_bus的实现

struct device platform_bus = {
	.init_name	= "platform",
};

/* platform属于总线的一种,所以需要注册他的bus */
struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_attrs	= platform_dev_attrs,
	.match		= platform_match,            /* 整platform总线上,drvice和driver匹配的机制 */
	.uevent		= platform_uevent,
	.pm		= &platform_dev_pm_ops,
};

int __init platform_bus_init(void)
{
	int error;

	early_platform_cleanup();        /* 清除早期的platform数据和信息(早期的platform建立时sysfs等都没建立,不完善,所以要这里清掉之前的建立),重新注册platform */

	error = device_register(&platform_bus);    /* 在sysfs的device下注册一个位platform的空文件夹 */
	if (error)
		return error;
	error =  bus_register(&platform_bus_type);    /* 在sysfs的bus下注册一个platform的文件夹 ,同时使用platform总线注册的设备的bus都会被赋值为platform_bus_type,即将来所有设备会在bus下的platform里出现*/
	if (error)
		device_unregister(&platform_bus);
	return error;
}

platform总线上设备和驱动的匹配规则

/**
 * platform_match - bind platform device to platform driver.
 * @dev: device.
 * @drv: driver.
 *
 * Platform device IDs are assumed to be encoded like this:
 * "<name><instance>", where <name> is a short description of the type of
 * device, like "pci" or "floppy", and <instance> is the enumerated
 * instance of the device, like '0' or '42'.  Driver IDs are simply
 * "<name>".  So, extract the <name> from the platform_device structure,
 * and compare it against the name of the driver. Return whether they match
 * or not.
 */
static int platform_match(struct device *dev, struct device_driver *drv)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct platform_driver *pdrv = to_platform_driver(drv);

	/* match against the id table first */
	if (pdrv->id_table)        /* 如果driver的id_table存在,则只匹配id_table */
		return platform_match_id(pdrv->id_table, pdev) != NULL;

	/* fall-back to driver name match */
	return (strcmp(pdev->name, drv->name) == 0);    /* 如果drv的id_table不存在,则比较name是否相等 */
}

id的匹配规则

static const struct platform_device_id *platform_match_id(
			const struct platform_device_id *id,
			struct platform_device *pdev)
{
        /* 其实id也是匹配name,只不过可能driver要支持多个device,所以有多个名字,就做成一个id_table比较 */
	while (id->name[0]) {
		if (strcmp(pdev->name, id->name) == 0) {    
			pdev->id_entry = id;
			return id;
		}
		id++;
	}
	return NULL;
}

2.platform_device_register的实现

platform_device主要是基于下面这个结构体实现

struct platform_device {
	const char	* name;                            /* 匹配driver使用,重要 */
	int		id;                                /* 区分使用相同driver的同种设备,从0开始,-1为自动分配 */
	struct device	dev;                               /* 具体的设备,无论是何种总线里的设备,都是对dev再次增加信息封装 */
	u32		num_resources;                     /* 多少个资源,为下面准备的 */
	struct resource	* resource;                        /* 存放资源的地址 */

	const struct platform_device_id	*id_entry;         /* 区分统一厂家的不同版本设备 */
    
	/* arch specific additions */
	struct pdev_archdata	archdata;                  /* 为特殊架构准备,基本不用 */
};

通常我们自定义一个静态的platform_device并填充。

当然内核为了绝对的可裁剪性,也提供了通过动态分配的方法。

struct platform_object {
	struct platform_device pdev;       /* obj包含有platform_device  */
	char name[1];
};


/**
 * platform_device_alloc - create a platform device
 * @name: base name of the device we're adding
 * @id: instance id
 *
 * Create a platform device object which can have other objects attached
 * to it, and which will have attached objects freed when it is released.
 */
struct platform_device *platform_device_alloc(const char *name, int id)
{
	struct platform_object *pa;

	pa = kzalloc(sizeof(struct platform_object) + strlen(name), GFP_KERNEL);
	if (pa) {
		strcpy(pa->name, name);
		pa->pdev.name = pa->name;
		pa->pdev.id = id;
		device_initialize(&pa->pdev.dev);
		pa->pdev.dev.release = platform_device_release;      /* release放在dev里面unregister的时候只要device_del(dev)就可以 */
	}

	return pa ? &pa->pdev : NULL;
}

/**
 * platform_device_add_resources - add resources to a platform device
 * @pdev: platform device allocated by platform_device_alloc to add resources to
 * @res: set of resources that needs to be allocated for the device
 * @num: number of resources
 *
 * Add a copy of the resources to the platform device.  The memory
 * associated with the resources will be freed when the platform device is
 * released.
 */
int platform_device_add_resources(struct platform_device *pdev,
				  const struct resource *res, unsigned int num)
{
	struct resource *r;

	r = kmalloc(sizeof(struct resource) * num, GFP_KERNEL);
	if (r) {
		memcpy(r, res, sizeof(struct resource) * num);
		pdev->resource = r;
		pdev->num_resources = num;
	}
	return r ? 0 : -ENOMEM;
}

static void platform_device_release(struct device *dev)
{
	struct platform_object *pa = container_of(dev, struct platform_object,
						  pdev.dev);

	kfree(pa->pdev.dev.platform_data);       /* 这部分由自己具体的设备实现具体的数据结构 */
	kfree(pa->pdev.resource);                /* 释放存放resource的内存 */
	kfree(pa);                               /* 释放platform_device_alloc中申请的obj */
}

无论是静态还是动态定义的platform_device,都需要platform_device添加进platform_bus。

即使用下面这个函数就可以了。

/**
 * platform_device_register - add a platform-level device
 * @pdev: platform device we're adding
 */
int platform_device_register(struct platform_device *pdev)
{
	device_initialize(&pdev->dev);        /* 初始化device结构体里面的通用数据 */
	return platform_device_add(pdev);     /* 真正添加device到platform_bus */
}

系统也提供了可以注册多个device的接口,起始就是多次调用上面的。


/**
 * platform_add_devices - add a numbers of platform devices
 * @devs: array of platform devices to add
 * @num: number of platform devices in array
 */
int platform_add_devices(struct platform_device **devs, int num)
{
	int i, ret = 0;

	for (i = 0; i < num; i++) {
		ret = platform_device_register(devs[i]);
		if (ret) {
			while (--i >= 0)
				platform_device_unregister(devs[i]);
			break;
		}
	}

	return ret;
}

下面这个函数就是注册platform总线的核心。


/**
 * platform_device_add - add a platform device to device hierarchy
 * @pdev: platform device we're adding
 *
 * This is part 2 of platform_device_register(), though may be called
 * separately _iff_ pdev was allocated by platform_device_alloc().
 */
int platform_device_add(struct platform_device *pdev)
{
	int i, ret = 0;

	if (!pdev)
		return -EINVAL;

	if (!pdev->dev.parent)
		pdev->dev.parent = &platform_bus;       /* 如果设备没指定parent的话,默认就是platform(通常都是) */

	pdev->dev.bus = &platform_bus_type;              /* 绑定总线类型,sysfs体现 */

	if (pdev->id != -1)                              /* 指定了具体id */
		dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);
	else                                             /* -1,自动分配id */
		dev_set_name(&pdev->dev, "%s", pdev->name);

        /* 对资源进行处理和检查 */
	for (i = 0; i < pdev->num_resources; i++) {
		struct resource *p, *r = &pdev->resource[i];

		if (r->name == NULL)                          /* 该resouce没name的话,则使用该dev的name */
			r->name = dev_name(&pdev->dev);

        
		p = r->parent;
		if (!p) {
			if (resource_type(r) == IORESOURCE_MEM)
				p = &iomem_resource;     /* 没指定resouce的父项,则用系统默认的 PCI MEM */
			else if (resource_type(r) == IORESOURCE_IO)
				p = &ioport_resource;    /* 没指定resouce的父项,则用系统默认的 PCI IO */
		}
                /* 把该soucurce插入到source tree中 */
		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));

        /* 把该设备增加到设备节点中去,对应的在sysfs的bus,platform下和sysfs的device,platform会出现响应设备 */
	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;
}

删除和注册刚好相反,就不分析了

/**
 * platform_device_del - remove a platform-level device
 * @pdev: platform device we're removing
 *
 * Note that this function will also release all memory- and port-based
 * resources owned by the device (@dev->resource).  This function must
 * _only_ be externally called in error cases.  All other usage is a bug.
 */
void platform_device_del(struct platform_device *pdev)
{
	int i;

	if (pdev) {
		device_del(&pdev->dev);

		for (i = 0; i < pdev->num_resources; i++) {
			struct resource *r = &pdev->resource[i];
			unsigned long type = resource_type(r);

			if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
				release_resource(r);
		}
	}
}

2.driver部分

struct platform_driver {
	int (*probe)(struct platform_device *);        /* 负责使用device传递过来的platform_data初始化具体的device */
	int (*remove)(struct platform_device *);
	void (*shutdown)(struct platform_device *);
	int (*suspend)(struct platform_device *, pm_message_t state);
	int (*resume)(struct platform_device *);
	struct device_driver driver;                   /* 驱动程序共有接口,也是实现在sysfs接口的核心 */
	const struct platform_device_id *id_table;
};

驱动的注册很简单,主要是调用系统提供的driver_regiser接口。

/**
 * platform_driver_register - register a driver for platform-level devices
 * @drv: platform driver structure
 */
int platform_driver_register(struct platform_driver *drv)
{
	drv->driver.bus = &platform_bus_type;                /* 驱动的总线类型也是platfrom */
	if (drv->probe)
		drv->driver.probe = platform_drv_probe;      /* 在驱动层没实现probe函数的情况下,则掉用系统默认的probe函数 */
	if (drv->remove)
		drv->driver.remove = platform_drv_remove;
	if (drv->shutdown)
		drv->driver.shutdown = platform_drv_shutdown;

	return driver_register(&drv->driver);
}

driver的卸载就更简单了

/**
 * platform_driver_unregister - unregister a driver for platform-level devices
 * @drv: platform driver structure
 */
void platform_driver_unregister(struct platform_driver *drv)
{
	driver_unregister(&drv->driver);
}

我们发现,其实platform总线的device和driver本身注册并没有做任何实质性的东西,真正实质性的注册为driver_register和device_add函数。这两个也是各种总线来实现驱动和设备注册的根本函数。我在下一个博客继续学习分析。

猜你喜欢

转载自blog.csdn.net/qq_16777851/article/details/81350037