设为首页收藏本站
网站公告 | 这是第一条公告
     

 找回密码
 立即注册
缓存时间23 现在时间23 缓存数据 荣耀也罢,屈辱也罢,都要以平和的心态去面对,少一些无奈与感慨,多一份从容和淡然。晚安!

荣耀也罢,屈辱也罢,都要以平和的心态去面对,少一些无奈与感慨,多一份从容和淡然。晚安!

查看: 540|回复: 0

Linux之platform平台设备驱动详解

[复制链接]

  离线 

TA的专栏

  • 打卡等级:热心大叔
  • 打卡总天数:204
  • 打卡月天数:0
  • 打卡总奖励:3189
  • 最近打卡:2023-08-27 09:08:14
等级头衔

等級:晓枫资讯-上等兵

在线时间
0 小时

积分成就
威望
0
贡献
412
主题
384
精华
0
金钱
4389
积分
823
注册时间
2022-12-20
最后登录
2025-8-30

发表于 2025-8-29 22:38:43 | 显示全部楼层 |阅读模式
在 Linux 设备驱动模型中,总线(Bus)是连接处理器与设备的桥梁,而 Platform 总线是一种虚拟总线,专门用于管理那些不依赖于物理总线(如 I2C、PCI、USB 等)的嵌入式设备(如 SoC 内部的硬件外设)。
所以platform 总线的主要作用就是统一设备模型,将未挂载到物理总线的设备纳入统一的设备驱动框架。
通过 platform_bus_type 虚拟一条总线,使得这些设备可以像物理总线设备一样被管理。

platform驱动注册

结构体是struct platform_driver,主要包含probe、remove等接口。
  1. struct platform_driver {
  2.         int (*probe)(struct platform_device *);

  3.         /*
  4.          * Traditionally the remove callback returned an int which however is
  5.          * ignored by the driver core. This led to wrong expectations by driver
  6.          * authors who thought returning an error code was a valid error
  7.          * handling strategy. To convert to a callback returning void, new
  8.          * drivers should implement .remove_new() until the conversion it done
  9.          * that eventually makes .remove() return void.
  10.          */
  11.         int (*remove)(struct platform_device *);
  12.         void (*remove_new)(struct platform_device *);

  13.         void (*shutdown)(struct platform_device *);
  14.         int (*suspend)(struct platform_device *, pm_message_t state);
  15.         int (*resume)(struct platform_device *);
  16.         struct device_driver driver;
  17.         const struct platform_device_id *id_table;
  18.         bool prevent_deferred_probe;
  19.         /*
  20.          * For most device drivers, no need to care about this flag as long as
  21.          * all DMAs are handled through the kernel DMA API. For some special
  22.          * ones, for example VFIO drivers, they know how to manage the DMA
  23.          * themselves and set this flag so that the IOMMU layer will allow them
  24.          * to setup and manage their own I/O address space.
  25.          */
  26.         bool driver_managed_dma;
  27. };
复制代码
驱动注册接口是platform_driver_register,主要是把bus配成platform_bus_type后调用driver_register注册。
代码示例:
  1. int zsl_drv_probe(struct platform_device *dev)
  2. {
  3.         struct property *pp = NULL;

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

  5.         dump_stack();  // 打印堆栈

  6.     return 0;
  7. }

  8. int zsl_drv_remove(struct platform_device *dev)
  9. {
  10.         printk(KERN_INFO "%s: \n",__func__);
  11.     return 0;
  12. }

  13. struct platform_driver zsl_drv =
  14. {
  15.     .driver =
  16.     {
  17.         .name = "zsltest",                       
  18.     },
  19.      
  20.     .probe = zsl_drv_probe,
  21.     .remove = zsl_drv_remove,
  22. };
复制代码
再使用platform_driver_register(&zsl_drv)注册这个驱动。

platform设备注册

结构体是struct platform_device,主要包含probe、remove等接口。
  1. struct platform_device {
  2.         const char        *name;
  3.         int                id;
  4.         bool                id_auto;
  5.         struct device        dev;
  6.         u64                platform_dma_mask;
  7.         struct device_dma_parameters dma_parms;
  8.         u32                num_resources;
  9.         struct resource        *resource;

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

  16.         /* MFD cell pointer */
  17.         struct mfd_cell *mfd_cell;

  18.         /* arch specific additions */
  19.         struct pdev_archdata        archdata;
  20. };
复制代码
注册接口是platform_device_register,主要是把设备属性填充后,后调用device_add注册。
代码示例:
  1. struct platform_device zsl_dev =
  2. {
  3.     .name = "zsltest",
  4.     .dev =
  5.     {
  6.         .release = zsl_dev_release,
  7.     },
  8. };
复制代码
再使用platform_device_register(&zsl_dev)注册这个设备,其中zsl_dev里的name和zsl_drv的name保持一样,才能让platform device和platform driver匹配上,从而调用zsl_drv.probe。
跟platform驱动注册配套使用后,运行打印如下,可以看到zsl_drv.probe会被调用到。
运行结果:

  • 先注册dev,再注册drv
1.jpeg


  • 先注册drv,再注册dev
2.jpeg


设备树

支持设备的内核里,更推荐使用设备树的方式,而不是platform设备注册的方式。
去掉zsl_dev设备的注册代码,在zsl_drv变量里增加.of_match_table = zsl_of_match,并且zsl_of_match表里增加.compatible = "rockchip,zslzsl",然后在设备树里增加以下代码。
保持两边的compatible一致,并且status是okay的。
这样就会调用zsl_drv.probe,并且可以拿到设备树里的属性内容。
  1.        zsl: zsl {
  2.               compatible = "rockchip,zslzsl";
  3.               status = "okay";
  4.               testdata = "test";
  5.        };
复制代码
如下修改zsl_drv_probe接口,增加拿testdata属性的代码
  1. int zsl_drv_probe(struct platform_device *dev)
  2. {
  3.        struct property *pp = NULL;

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

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

  8.        dump_stack();  // 打印堆栈

  9.     return 0;
  10. }
复制代码
编译运行后如下,可以看到zsl_drv.probe会被调用到,并且能拿到设备树里的testdata属性。
3.jpeg


Platform驱动和设备的关系

根据堆栈打印跟踪代码,调用调用关系如下
  1. platform_driver_register
  2.     driver_register
  3.         bus_add_driver   
  4.             klist_add_tail
  5.             driver_attach
  6.                 driver_match_device(struct device *dev, void *data)=platform_match(struct device *dev, struct device_driver *drv) //找dev
  7.                 driver_probe_device
  8.                     really_probe
  9.                         dev->bus->probe=platform_probe
  10.                             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函数。
  1. platform_device_register
  2.     platform_device_add
  3.         device_add
  4.             bus_probe_device
  5.                 device_initial_probe=__device_attach
  6.                     __device_attach_driver
  7.                         driver_match_device(struct device *dev, void *data)=platform_match(struct device *dev, struct device_driver *drv)  //找drv
  8.                         driver_probe_device
  9.                             really_probe
  10.                                 dev->bus->probe=platform_probe
  11.                                     drv->probe=zsl_drv_probe
  12.             klist_add_tail
复制代码
Device注册时通过device_add将device加入总线(klist_add_tail到总线的device列表)触发bus_probe_device,遍历总线的driver列表,通过platform_match匹配已有驱动,匹配成功则调用driver的probe函数。
这种双向注册机制确保了无论driver和device的注册顺序如何,都能正确触发匹配和初始化。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持晓枫资讯。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
晓枫资讯-科技资讯社区-免责声明
免责声明:以上内容为本网站转自其它媒体,相关信息仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同其观点或证实其内容的真实性。
      1、注册用户在本社区发表、转载的任何作品仅代表其个人观点,不代表本社区认同其观点。
      2、管理员及版主有权在不事先通知或不经作者准许的情况下删除其在本社区所发表的文章。
      3、本社区的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,举报反馈:点击这里给我发消息进行删除处理。
      4、本社区一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
      5、以上声明内容的最终解释权归《晓枫资讯-科技资讯社区》所有。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~
严禁发布广告,淫秽、色情、赌博、暴力、凶杀、恐怖、间谍及其他违反国家法律法规的内容。!晓枫资讯-社区
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|晓枫资讯--科技资讯社区 本站已运行

CopyRight © 2022-2025 晓枫资讯--科技资讯社区 ( BBS.yzwlo.com ) . All Rights Reserved .

晓枫资讯--科技资讯社区

本站内容由用户自主分享和转载自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。

如有侵权、违反国家法律政策行为,请联系我们,我们会第一时间及时清除和处理! 举报反馈邮箱:点击这里给我发消息

Powered by Discuz! X3.5

快速回复 返回顶部 返回列表