当前位置: 首页 > 工具软件 > Platform > 使用案例 >

Linux Platform 平台设备驱动模型简介

颜瀚漠
2023-12-01

内核版本:linux-3.10.61

目录

1、Platform介绍

2、主要结构体和函数说明

2-1、struct bus_type platform_bus_type;

2-2、platform_device 结构体

2-3、platform_driver 结构体。

2-4、主要操作函数。

3、platform_device_register() 分析

4、platform_driver_register() 分析

5、例子 


本文主要通过分析注册函数platform_device_register()和platform_driver_register(),得到platform_device和platform_driver之间如何匹配成功。具体的匹配过程和小蝌蚪找妈妈类似,匹配每个对象,直到小蝌蚪找到妈妈。

1、Platform介绍

Platform 平台设备设备驱动模型的作用是将驱动的实现和资源分离,是一个虚拟的总线平台。这其中存在三个成员platform_bus,platform_device,platform_driver。platform_device和platform_driver注册不分先后顺序,因为在注册时都会去匹配对方,这一点可以在我们分析注册函数时证实。

platform_bus:由链表实现,不对应实际的物理总线。

platform_device:驱动的资源比如一些 I/O端口,中断号之类的。

platform_driver:驱动的功能实现比如 注册驱动,实现file_operations 等

2、主要结构体和函数说明

2-1、struct bus_type platform_bus_type;

这是一个全局结构体用于描述一个platform_bus。platform_driver和platform_device会被挂到这个bus结构体里面。其中match成员用于platform_device和platform_driver之间的匹配,详情请看后面对注册函数的分析。

struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_attrs	= platform_dev_attrs,
	.match		= platform_match,
	.uevent		= platform_uevent,
	.pm		= &platform_dev_pm_ops,
};

2-2、platform_device 结构体

主要用于保存硬件资源。name成员用于匹配driver。resource 用于存放需要用到的资源。

struct platform_device {
	const char	*name;            // 用于匹配platform_driver->id_table->name
	int		id;
	bool		id_auto;
	struct device	dev;          // 设备结构体
	u32		num_resources;        // 资源个数    
	struct resource	*resource;    // 资源数据(通常用数组表示)

	const struct platform_device_id	*id_entry;

	/* MFD cell pointer */
	struct mfd_cell *mfd_cell;

	/* arch specific additions */
	struct pdev_archdata	archdata;
};

struct resource {
	resource_size_t start;  // 资源的起始地址
	resource_size_t end;    // 资源结束地址
	const char *name;       // 名称
	unsigned long flags;    // 资源类型:IRQ,MEM,IO等,使用宏 ‘IORESOURCE_*’ 表示
	struct resource *parent, *sibling, *child;
};

2-3、platform_driver 结构体。

用于描述驱动的实现。通过platform_driver 的name成员匹配上device后probe成语会被调用。在设备拔出时系统会调用remove成员做清理工作。

struct platform_driver {
	int (*probe)(struct platform_device *);    // device和driver的name匹配成功后调用probe函数
	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;                // id_table->name没有设置时,使用driver->name 进行匹配
	const struct platform_device_id *id_table;
};

struct platform_device_id {
	char name[PLATFORM_NAME_SIZE];    // 用来和platform_device->name进行匹配
	kernel_ulong_t driver_data;
};

2-4、主要操作函数。

/* platform_device注册和卸载函数。 */
int platform_device_register(struct platform_device *pdev);
void platform_device_unregister(struct platform_device *pdev);
/* platform_driver注册和卸载函数。 */
int platform_driver_register(struct platform_driver *drv);
void platform_driver_unregister(struct platform_driver *drv);
/* 获取platform_device中保存的资源 */
struct resource *platform_get_resource(struct platform_device *dev,
				       unsigned int type, unsigned int num);
/* 获取platform_device的中断资源 */
int platform_get_irq(struct platform_device *dev, unsigned int num)
/* 用于批量注册平台设备 */
int platform_add_devices(struct platform_device **devs, int num) 

3、platform_device_register() 分析

platform_device_register()用于注册一个platform_device,让我们一起来看看struct platform_device是如何注册进总线的。

int platform_device_register(struct platform_device *pdev)
{
	device_initialize(&pdev->dev);
	arch_setup_pdev_archdata(pdev);
	return platform_device_add(pdev);
}

int platform_device_add(struct platform_device *pdev){
	if (!pdev->dev.parent)
		pdev->dev.parent = &platform_bus;
    /* ...略 */
    pdev->dev.bus = &platform_bus_type;    // 用在2-1提到的bus全局结构体初始化pdev->dev.bus

    /* ...略 */
    ret = device_add(&pdev->dev);         // 来看看它做了点什么
    /* ...略 */
}

int device_add(struct device *dev){
     /* ...略 */
    error = bus_add_device(dev);       // 将device 添加到bus总线
    bus_probe_device(dev);             // 匹配总线中同名的driver           
     /* ...略 */
}

/* device 在这里挂接到bus中 */
int bus_add_device(struct device *dev){
    klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);    // device 挂接到platform bus
}

void bus_probe_device(struct device *dev)
{
	ret = device_attach(dev);    // 在这里去匹配总线上有没有同名的driver
}

int device_attach(struct device *dev){
    /* ...略 */
        /* 使用 __device_attach 和总线上的所有driver匹配*/ 
        ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
}


static int __device_attach(struct device_driver *drv, void *data)
{
	struct device *dev = data;

	if (!driver_match_device(drv, dev))    // 在这里调用platform_bus_type.match 函数用于匹配总线上的driver
		return 0;
    /* 匹配成功调用 driver_probe_device*/
	return driver_probe_device(drv, dev);  // 先买个关子,先不分析它,因为我猜platform_driver_register()也会调用到它
}

static inline int driver_match_device(struct device_driver *drv,
				      struct device *dev)
{
	return drv->bus->match ? drv->bus->match(dev, drv) : 1;    // 这个match函数指向platform_match()
}

platform_match()用于匹配device和driver。一起来看看device和driver是怎么互相匹配的。

static int platform_match(struct device *dev, struct device_driver *drv){
	/* Then try to match against the id table */
	if (pdrv->id_table)    // 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);    // id_table 未设置使用driver->name进行匹配
}

static const struct platform_device_id *platform_match_id(
			const struct platform_device_id *id,
			struct platform_device *pdev){

        if (strcmp(pdev->name, id->name) == 0)    // device->name和driver->id_table->name 进行匹配
            ;
}

4、platform_driver_register() 分析

platform_driver_register()用于注册platform_driver。关于platform_driver 是怎么通过platform_driver_register() 注册进总线的,让我们一层一层给它剥出来。看完前面剥好的platform_device_register(),下边你就能看到很多熟人了。

int platform_driver_register(struct platform_driver *drv)
{
	drv->driver.bus = &platform_bus_type;
	if (drv->probe)
		drv->driver.probe = platform_drv_probe;    // 第一个熟人driver.probe,device和driver匹配成功后就会调用它
	if (drv->remove)
		drv->driver.remove = platform_drv_remove;
	if (drv->shutdown)
		drv->driver.shutdown = platform_drv_shutdown;

	return driver_register(&drv->driver);    // 这是个新人,我们盘它
}

int driver_register(struct device_driver *drv){
    ret = bus_add_driver(drv);    // 把driver 添加到 platform bus。和bus_add_device()是两兄弟
}

int bus_add_driver(struct device_driver *drv){
    struct driver_private *priv;
    priv = kzalloc(sizeof(*priv), GFP_KERNEL);
    klist_init(&priv->klist_devices, NULL, NULL);
	priv->driver = drv;
	drv->p = priv;
    priv->kobj.kset = bus->p->drivers_kset;
    klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
    error = driver_attach(drv);    // 这儿去匹配device, 和device_attach() 是两兄弟
}

int driver_attach(struct device_driver *drv)
{
    /* 使用__driver_attach和总线上的所有device匹配 */
	return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}

static int __driver_attach(struct device *dev, void *data){
    if (!driver_match_device(drv, dev))    // 同样的在这里调用platform_bus_type.match 函数用于匹配driver
		return 0;        // 匹配失败返回

    /*
     * 果然和我们猜的一样这里也调用到了driver_probe_device()
     * 看到这里就已经离最终的答案很近了,再坚持一下,谜底很快就要揭晓了
     */
    driver_probe_device(drv, dev);        
}


让我们来看一下穿山甲到底说了什么,一起揭晓谜底。

int driver_probe_device(struct device_driver *drv, struct device *dev){
    /* ...略 */
    ret = really_probe(dev, drv);    // 调用drv的prob函数
}

static int really_probe(struct device *dev, struct device_driver *drv){
    /* ...略 */
	if (dev->bus->probe) {
		ret = dev->bus->probe(dev);
		if (ret)
			goto probe_failed;
	} else if (drv->probe) {
		ret = drv->probe(dev);    // 调用到driver的probe
		if (ret)
			goto probe_failed;
	}
}

static int platform_drv_probe(struct device *_dev)
{
	struct platform_driver *drv = to_platform_driver(_dev->driver);
	struct platform_device *dev = to_platform_device(_dev);

	ret = drv->probe(dev);    // 最终我们调用到了 platform_driver->probe()函数,也就是我们自己定义的probe函数

	return ret;
}

5、例子 

让我们在内核找一个设备驱动代码来解释一下

注册一个platform_device

static struct platform_device am79c961_device = {
	.name			= "am79c961",
	.id			= -1,
	.num_resources		= ARRAY_SIZE(am79c961_resources),
	.resource		= am79c961_resources,
};
static struct platform_device *ebsa110_devices[] = {
	&serial_device,
	&am79c961_device,
};
static int __init ebsa110_init(void)
{
	arm_pm_idle = ebsa110_idle;
    /*platform_add_devices 函数内部也是调用platform_device_register() */
	return platform_add_devices(ebsa110_devices, ARRAY_SIZE(ebsa110_devices));
}

注册一个platform_driver

static struct platform_driver am79c961_driver = {
	.probe		= am79c961_probe,    // 匹配成功后调用
	.driver		= {
		.name	= "am79c961",
	},
};

static int __init am79c961_init(void)
{
	return platform_driver_register(&am79c961_driver);
}

 匹配成功后调用驱动自己的probe函数初始化。

static int am79c961_probe(struct platform_device *pdev){

	res = platform_get_resource(pdev, IORESOURCE_IO, 0);    // 获取device端定义的资源

    ret = platform_get_irq(pdev, 0);    // 获取device端定义中断资源

    ret = register_netdev(dev);    // 注册设备(网络设备)

}

恭喜获得知识点+1

若有讲解不妥之处烦请在评论区指正!

如果有收获那就一键三连鼓励一下吧!

 类似资料: