Linux之platform平台设备驱动详解

 更新时间:2025年07月22日 15:08:22   作者:大肥周  
Linux设备驱动模型中,Platform总线作为虚拟总线统一管理无物理总线依赖的嵌入式设备,通过platform_driver和platform_device注册,结合设备树的of_match_table匹配机制,实现驱动与设备的自动绑定,确保无论注册顺序如何均能正确触发probe函数

在 Linux 设备驱动模型中,总线(Bus)是连接处理器与设备的桥梁,而 Platform 总线是一种虚拟总线,专门用于管理那些不依赖于物理总线(如 I2C、PCI、USB 等)的嵌入式设备(如 SoC 内部的硬件外设)。

所以platform 总线的主要作用就是统一设备模型,将未挂载到物理总线的设备纳入统一的设备驱动框架。

通过 platform_bus_type 虚拟一条总线,使得这些设备可以像物理总线设备一样被管理。

platform驱动注册

结构体是struct platform_driver,主要包含probe、remove等接口。

struct platform_driver {
	int (*probe)(struct platform_device *);

	/*
	 * Traditionally the remove callback returned an int which however is
	 * ignored by the driver core. This led to wrong expectations by driver
	 * authors who thought returning an error code was a valid error
	 * handling strategy. To convert to a callback returning void, new
	 * drivers should implement .remove_new() until the conversion it done
	 * that eventually makes .remove() return void.
	 */
	int (*remove)(struct platform_device *);
	void (*remove_new)(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;
	const struct platform_device_id *id_table;
	bool prevent_deferred_probe;
	/*
	 * For most device drivers, no need to care about this flag as long as
	 * all DMAs are handled through the kernel DMA API. For some special
	 * ones, for example VFIO drivers, they know how to manage the DMA
	 * themselves and set this flag so that the IOMMU layer will allow them
	 * to setup and manage their own I/O address space.
	 */
	bool driver_managed_dma;
};

驱动注册接口是platform_driver_register,主要是把bus配成platform_bus_type后调用driver_register注册。

代码示例:

int zsl_drv_probe(struct platform_device *dev)
{
	struct property *pp = NULL;

	printk(KERN_INFO "%s: \n",__func__);

	dump_stack();  // 打印堆栈

    return 0;
}

int zsl_drv_remove(struct platform_device *dev)
{
	printk(KERN_INFO "%s: \n",__func__);
    return 0;
}

struct platform_driver zsl_drv =
{
    .driver =
    {
        .name = "zsltest",			
    },
     
    .probe = zsl_drv_probe,
    .remove = zsl_drv_remove,
};

再使用platform_driver_register(&zsl_drv)注册这个驱动。

platform设备注册

结构体是struct platform_device,主要包含probe、remove等接口。

struct platform_device {
	const char	*name;
	int		id;
	bool		id_auto;
	struct device	dev;
	u64		platform_dma_mask;
	struct device_dma_parameters dma_parms;
	u32		num_resources;
	struct resource	*resource;

	const struct platform_device_id	*id_entry;
	/*
	 * Driver name to force a match.  Do not set directly, because core
	 * frees it.  Use driver_set_override() to set or clear it.
	 */
	const char *driver_override;

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

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

注册接口是platform_device_register,主要是把设备属性填充后,后调用device_add注册。

代码示例:

struct platform_device zsl_dev =
{
    .name = "zsltest",
    .dev =
    {
        .release = zsl_dev_release,
    },
};

再使用platform_device_register(&zsl_dev)注册这个设备,其中zsl_dev里的name和zsl_drv的name保持一样,才能让platform device和platform driver匹配上,从而调用zsl_drv.probe。

跟platform驱动注册配套使用后,运行打印如下,可以看到zsl_drv.probe会被调用到。

运行结果:

  • 先注册dev,再注册drv

  • 先注册drv,再注册dev

设备树

支持设备的内核里,更推荐使用设备树的方式,而不是platform设备注册的方式。

去掉zsl_dev设备的注册代码,在zsl_drv变量里增加.of_match_table = zsl_of_match,并且zsl_of_match表里增加.compatible = "rockchip,zslzsl",然后在设备树里增加以下代码。

保持两边的compatible一致,并且status是okay的。

这样就会调用zsl_drv.probe,并且可以拿到设备树里的属性内容。

       zsl: zsl {
              compatible = "rockchip,zslzsl";
              status = "okay";
              testdata = "test";
       };

如下修改zsl_drv_probe接口,增加拿testdata属性的代码

int zsl_drv_probe(struct platform_device *dev)
{
       struct property *pp = NULL;

       printk(KERN_INFO "%s: \n",__func__);

       pp = of_find_property(dev->dev.of_node, "testdata", NULL);
       if (pp)
              printk(KERN_INFO "%s: %d:%s \n",__func__,pp->length,(char *)pp->value);

       dump_stack();  // 打印堆栈

    return 0;
}

编译运行后如下,可以看到zsl_drv.probe会被调用到,并且能拿到设备树里的testdata属性。

Platform驱动和设备的关系

根据堆栈打印跟踪代码,调用调用关系如下

platform_driver_register
    driver_register 
        bus_add_driver   
            klist_add_tail
            driver_attach
                driver_match_device(struct device *dev, void *data)=platform_match(struct device *dev, struct device_driver *drv) //找dev
                driver_probe_device
                    really_probe
                        dev->bus->probe=platform_probe
                            drv->probe=zsl_drv_probe

Driver注册时通过bus_add_driver将driver加入总线(klist_add_tail到总线的driver列表),触发driver_attach,遍历总线的device列表,通过platform_match匹配已有设备。

基本就是按顺序对设备树、id_table name的字符串匹配。匹配成功后,通过really_probe调用总线默认的platform_probe,最终执行driver的probe函数。

platform_device_register
    platform_device_add
        device_add
            bus_probe_device
                device_initial_probe=__device_attach
                    __device_attach_driver
                        driver_match_device(struct device *dev, void *data)=platform_match(struct device *dev, struct device_driver *drv)  //找drv
                        driver_probe_device
                            really_probe
                                dev->bus->probe=platform_probe
                                    drv->probe=zsl_drv_probe
            klist_add_tail

Device注册时通过device_add将device加入总线(klist_add_tail到总线的device列表)触发bus_probe_device,遍历总线的driver列表,通过platform_match匹配已有驱动,匹配成功则调用driver的probe函数。

这种双向注册机制确保了无论driver和device的注册顺序如何,都能正确触发匹配和初始化。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • ubuntu下使用sublime text时如何完美输入中文

    ubuntu下使用sublime text时如何完美输入中文

    网上的很多方法都不能让ubuntu下的sublime输入中文,使用Input Helper也很是别扭,这个方法是某大神通过启动sublime程序前运行脚本修改sublime来实现中文输入。
    2015-12-12
  • Linux如何查看进程栈信息示例

    Linux如何查看进程栈信息示例

    本篇文章主要介绍了Linux如何查看进程栈信息示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-03-03
  • Linux检测远程服务器端口的几种方法

    Linux检测远程服务器端口的几种方法

    作为系统管理员、网络工程师或应用程序开发人员,需要检查远程服务器上的端口是否打开,以便您可以判断所检查的服务是否正在运行,所以本文给大家介绍了Linux检测远程服务器端口的几种方法,需要的朋友可以参考下
    2024-06-06
  • 关于linux分区,文件系统,目录结构的概述

    关于linux分区,文件系统,目录结构的概述

    下面小编就为大家带来一篇关于linux分区,文件系统,目录结构的概述。小编觉得挺不错的。现在就分享给大家。也给大家做个参考。一起跟随小编过来看看吧
    2016-12-12
  • linux常用命令之zip/unzip命令(压缩文件/解压缩文件)详解

    linux常用命令之zip/unzip命令(压缩文件/解压缩文件)详解

    这篇文章主要介绍了linux常用命令之zip/unzip命令(压缩文件/解压缩文件),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-04-04
  • Apache 新站点目录配置 SELinux 的方法

    Apache 新站点目录配置 SELinux 的方法

    本文详细介绍了如何使用SELinux保护Apache新站点目录,包括确定默认上下文、创建和设置新目录的上下文、允许网络连接以及验证配置,感兴趣的朋友跟随小编一起看看吧
    2024-11-11
  • Linux如何查看、修改文件读写权限

    Linux如何查看、修改文件读写权限

    本文介绍了Linux文件权限的基本概念和查看、修改权限的方法,通过解析权限字符串(如"drwxrwxrwx")和使用chmod命令,可以详细设置用户、组和其他人的读、写、执行权限
    2024-12-12
  • 关于安装LNMP集成包后上传图片报500错误的解决方法

    关于安装LNMP集成包后上传图片报500错误的解决方法

    这篇文章主要给大家介绍了关于安装LNMP集成包后上传图片报500错误的解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2018-01-01
  • Linux自动化构建工具-make/Makefile使用解读

    Linux自动化构建工具-make/Makefile使用解读

    Linux项目自动化构建工具Make/Makefile指南,涵盖基础语法、高级特性及实战技巧,如变量、伪目标、模式规则、条件编译等,提升开发效率
    2025-09-09
  • 简单谈谈centos7中配置php

    简单谈谈centos7中配置php

    windows server2008下搭建PHP运行环境,用IIS+PHP或者phpstudy,或wamp。今天试了试,在linux下搭建php运行环境。感觉还不错。
    2018-06-06

最新评论