一、platform 基本概念整理

Linux 系统要考虑到驱动的可重用性,因此提出了驱动的分离分层这样的软件思路。因此提出驱动总线设备的驱动架构,总线负责管理驱动和设备。系统中有很多的物理总线:I2c、SPI、USB等,有部分设备不存在物理总线,为了驱动架构统一,因此引入 platform 虚拟总线

platform 虚拟总线用 platform_driver 结构体表示驱动,用 platform_device 结构体描述设备。对于所有设备都使用 bus_type 结构体类型。

1、bus_type

/*** struct bus_type - The bus type of the device 设备的总线类型** @name:  The name of the bus.* @dev_name:   Used for subsystems to enumerate devices like ("foo%u", dev->id). 用于子系统枚举设备。* @dev_root:  Default device to use as the parent. 作为父设备使用的默认设备。* @dev_attrs:    Default attributes of the devices on the bus. 总线上设备的默认属性。* @bus_groups:    Default attributes of the bus. 总线的默认属性。* @dev_groups:  Default attributes of the devices on the bus. 总线上设备的默认属性。* @drv_groups: Default attributes of the device drivers on the bus. 总线上设备驱动程序的默认属性。* @match: Called, perhaps multiple times, whenever a new device or driver*        is added for this bus. It should return a nonzero value if the*     given device can be handled by the given driver.* 每当为该总线添加新设备或驱动程序时,可能会多次调用。如果给定的设备可以被给定的驱动程序处理,它应该返回一个非零值。* @uevent:    Called when a device is added, removed, or a few other things*      that generate uevents to add the environment variables.* 在添加、删除设备或生成 uevents 以添加环境变量的其他一些内容时调用。* @probe:   Called when a new device or driver add to this bus, and callback*       the specific driver's probe to initial the matched device.* 当一个新设备或驱动添加到总线时调用,并回调特定驱动的探测来初始化匹配的设备。* @remove:   Called when a device removed from this bus. 当设备从总线上移除时调用。* @shutdown:  Called at shut-down time to quiesce the device. 在关机时调用,以停止设备。** @online:    Called to put the device back online (after offlining it). 调用以使设备重新联机(在它脱机之后)。* @offline:  Called to put the device offline for hot-removal. May fail. 调用将设备离线进行热移除。** @suspend:  Called when a device on this bus wants to go to sleep mode.* 当总线上的设备想要进入睡眠模式时调用。* @resume: Called to bring a device on this bus out of sleep mode.* 调用以将此总线上的设备从睡眠模式中唤醒。* @pm:        Power management operations of this bus, callback the specific*     device driver's pm-ops. 此总线的电源管理操作,回调特定设备驱动程序的 pm-ops。* @iommu_ops:  IOMMU specific operations for this bus, used to attach IOMMU*              driver implementations to a bus and allow the driver to do*              bus-specific setup* 此总线的 IOMMU 特定操作,用于将 IOMMU 驱动程序实现附加到总线,并允许驱动程序执行特定于总线的设置。* @p:      The private data of the driver core, only the driver core can*      touch this. 驱动核心的私有数据,只有驱动核心才能使用。* @lock_key:   Lock class key for use by the lock validator 锁验证器使用的锁类键** A bus is a channel between the processor and one or more devices. For the* purposes of the device model, all devices are connected via a bus, even if* it is an internal, virtual, "platform" bus. Buses can plug into each other.* A USB controller is usually a PCI device, for example. The device model* represents the actual connections between buses and the devices they control.* A bus is represented by the bus_type structure. It contains the name, the* default attributes, the bus' methods, PM operations, and the driver core's* private data.* 总线是处理器与一个或多个设备之间的通道。* 就设备型号而言,所有设备都通过总线连接,即使它是内部虚拟的"platform"总线。总线可以相互插入。* 例如,USB 控制器通常是 PCI 设备。* 设备模型表示总线与其控制的设备之间的实际连接。* 总线由bus_type结构表示。它包含名称、默认属性、总线的方法、PM 操作和驱动程序核心的私有数据。*/
struct bus_type {const char     *name;  /* 总线名字 */const char        *dev_name;struct device     *dev_root;struct device_attribute   *dev_attrs; /* use dev_groups instead */const struct attribute_group **bus_groups;  /* 总线属性 */const struct attribute_group **dev_groups;    /* 设备属性 */const struct attribute_group **drv_groups;    /* 驱动属性 */int (*match)(struct device *dev, struct device_driver *drv);int (*uevent)(struct device *dev, struct kobj_uevent_env *env);int (*probe)(struct device *dev);int (*remove)(struct device *dev);void (*shutdown)(struct device *dev);int (*online)(struct device *dev);int (*offline)(struct device *dev);int (*suspend)(struct device *dev, pm_message_t state);int (*resume)(struct device *dev);const struct dev_pm_ops *pm;const struct iommu_ops *iommu_ops;struct subsys_private *p;struct lock_class_key lock_key;
};

platform总线是 bus_type 的一个具体实例,定义在文件 drivers/base/platform.c,platform总线定义如下:

struct bus_type platform_bus_type = {.name      = "platform",.dev_groups = platform_dev_groups,.match       = platform_match,.uevent       = platform_uevent,.pm      = &platform_dev_pm_ops,
};
EXPORT_SYMBOL_GPL(platform_bus_type);

platform_bus_type 就是 platform平台总线,其中 platform_match 就是匹配函数

platform_match 函数定义在文件 drivers/base/platform.c 中,函数内容如下所示:

/*** 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.* 假定平台设备 ID 的编码方式如下:* "<name><instance>", 其中name是设备类型的简短描述,如pci或floppy,而instance是设备的枚举实例,如'0'或'42'。* 驱动程序 ID 只是简单"<name>"。* 因此,<name>从platform_device结构中提取 ,并将其与驱动程序的名称进行比较。* 无论它们是否匹配,都返回。*/
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);/* When driver_override is set, only bind to the matching driver *//* 当设置驱动程序覆盖时,只绑定到匹配的驱动程序 */if (pdev->driver_override)return !strcmp(pdev->driver_override, drv->name);/* Attempt an OF style match first 首先尝试 OF 样式匹配(设备树方式) */if (of_driver_match_device(dev, drv))return 1;/* Then try ACPI style match 然后尝试 ACPI 样式匹配 */if (acpi_driver_match_device(dev, drv))return 1;/* Then try to match against the id table 然后尝试与 id 表匹配 */if (pdrv->id_table)return platform_match_id(pdrv->id_table, pdev) != NULL;/* fall-back to driver name match 回退到驱动程序名称匹配 */return (strcmp(pdev->name, drv->name) == 0);
}

platform_match 函数讲述驱动和设备的匹配有四种方法:

1、OF 类型的匹配,也就是设备树采用的匹配方式,of_driver_match_device 函数定义在文件 include/linux/of_device.h 中。

2、ACPI 匹配方式。

3、id_table 匹配,每个 platform_driver 结构体有一个 id_table 成员变量。

4、直接比较驱动和设备的 name 字段,看看是不是相等,如果相等的话就匹配成功。

2、platform_driver

struct platform_driver {int (*probe)(struct platform_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;const struct platform_device_id *id_table;    /* id_table 表(是一个数组) */bool prevent_deferred_probe;
};
/*** struct device_driver - The basic device driver structure 基本设备驱动程序结构* @name:    Name of the device driver. 设备驱动程序的名称。* @bus:   The bus which the device of this driver belongs to. 此驱动程序的设备所属的总线。* @owner:    The module owner. 模块所有者。* @mod_name:   Used for built-in modules. 用于内置模块。* @suppress_bind_attrs: Disables bind/unbind via sysfs. 通过 sysfs 禁用绑定/取消绑定。* @of_match_table: The open firmware table. 打开的固件表。* @acpi_match_table: The ACPI match table. ACPI 匹配表。* @probe:   Called to query the existence of a specific device,*        whether this driver can work with it, and bind the driver*      to a specific device. 调用该函数可以查询特定设备是否存在,该驱动是否可以使用该设备,以及将该驱动绑定到特定设备。* @remove:    Called when the device is removed from the system to*       unbind a device from this driver. 当设备从系统中移除时调用,以从此驱动程序中解绑定设备。* @shutdown:   Called at shut-down time to quiesce the device. 在关闭时间调用,以停止设备。* @suspend:   Called to put the device to sleep mode. Usually to a*       low power state. 调用将设备置于睡眠模式。通常为低功率状态。* @resume:   Called to bring a device from sleep mode. 调用以使设备退出睡眠模式。* @groups:  Default attributes that get created by the driver core*     automatically. 由驱动程序核心自动创建的默认属性。* @pm:     Power management operations of the device which matched*        this driver. 与此驱动程序匹配的设备的电源管理操作。* @p:      Driver core's private data, no one other than the driver*      core can touch this. 驱动核心的私有数据,除了驱动核心没有人可以碰它。** The device driver-model tracks all of the drivers known to the system.* The main reason for this tracking is to enable the driver core to match* up drivers with new devices. Once drivers are known objects within the* system, however, a number of other things become possible. Device drivers* can export information and configuration variables that are independent* of any specific device.* 设备驱动程序模型跟踪系统已知的所有驱动程序。* 这种跟踪的主要原因是使驱动程序核心能够将驱动程序与新设备匹配起来。* 然而,一旦驱动程序成为系统中的已知对象,许多其他事情就成为可能。* 设备驱动程序可以导出独立于任何特定设备的信息和配置变量。*/
struct device_driver {const char        *name;struct bus_type       *bus;struct module      *owner;const char       *mod_name;  /* used for built-in modules */bool suppress_bind_attrs;    /* disables bind/unbind via sysfs */const struct of_device_id   *of_match_table;const struct acpi_device_id *acpi_match_table;int (*probe) (struct device *dev);int (*remove) (struct device *dev);void (*shutdown) (struct device *dev);int (*suspend) (struct device *dev, pm_message_t state);int (*resume) (struct device *dev);const struct attribute_group **groups;const struct dev_pm_ops *pm;struct driver_private *p;
};

3、platform_device

struct platform_device {const char   *name;  /* 设备名字(用于和驱动匹配) */int      id;bool     id_auto;struct device   dev;u32     num_resources;  /* 表示 resource 大小 */struct resource *resource; /* 表示资源,也就是设备信息,比如外设寄存器等 */const struct platform_device_id *id_entry;char *driver_override; /* Driver name to force a match *//* MFD cell pointer */struct mfd_cell *mfd_cell;/* arch specific additions */struct pdev_archdata    archdata;
};
/** Resources are tree-like, allowing* nesting etc..*/
struct resource {resource_size_t start; /* 资源的起始 */resource_size_t end; /* 资源的终止 */const char *name;        /* 资源名字 */unsigned long flags;  /* 资源类型 */struct resource *parent, *sibling, *child;
};

二、platform 驱动相关 API

1、platform_driver_register

/* 注册驱动程序* * drv:要注册的 platform 驱动* 返回值:负数,失败;0,成功。*/
int platform_driver_register(struct platform_driver *drv);

2、platform_driver_unregister

/* 注销驱动程序* * drv:要注销的 platform 驱动* 返回值:无*/
void platform_driver_unregister(struct platform_driver *drv)

三、platform 驱动源码编写思路

1、定义 platform 驱动结构体。

2、在 platform 驱动结构体中指定 probe 函数(驱动和设备匹配成功后执行)和 remove 函数(设备注销或驱动注销)。

3、在驱动模块加载函数中调用 platform_driver_register 函数。

4、在驱动模块卸载函数中调用 platform_driver_unregister 函数。

5、probe 函数完成驱动初始化。

#include "linux/init.h"
#include "linux/module.h"
#include "linux/platform_device.h"/** @description       : flatform驱动的probe函数,当驱动与设备匹配以后此函数就会执行* @param - dev    : platform设备* @return          : 0,成功;其他负值,失败*/
static int led_probe(struct platform_device *dev)
{printk("led probe!\r\n");return 0;
}/** @description      : platform驱动的remove函数,移除platform驱动的时候此函数会执行* @param - dev   : platform设备* @return          : 0,成功;其他负值,失败*/
static int led_remove(struct platform_device *dev)
{printk("led remove!\r\n");return 0;
}/* platform驱动结构体 */
static struct platform_driver led_driver = {.driver        = {.name   = "imx6ul-led",          /* 驱动名字,用于和设备匹配 */},.probe       = led_probe,.remove        = led_remove,
};/** @description : 驱动模块加载函数* @param         : 无* @return       : 无*/
static int __init led_driver_init(void)
{return platform_driver_register(&led_driver);
}/** @description  : 驱动模块卸载函数* @param         : 无* @return       : 无*/
static void __exit led_driver_exit(void)
{platform_driver_unregister(&led_driver);
}module_init(led_driver_init);
module_exit(led_driver_exit);
MODULE_LICENSE("GPL");

四、platform 设备相关 API

1、platform_device_register

/* 注册设备* * drv:要注册的 platform 设备* 返回值:负数,失败;0,成功*/
int platform_device_register(struct platform_device *pdev)

2、platform_device_unregister

/* 注销设备* * drv:要注销的 platform 设备* 返回值:无*/
void platform_device_unregister(struct platform_device *pdev)

五、platform 设备源码编写思路

1、定义 platform 设备结构体(包括设备资源信息)。

2、在设备模块加载函数中调用 platform_device_register 函数。

3、在设备模块注销函数中调用 leddevice_exit 函数。

#include "linux/init.h"
#include "linux/module.h"
#include "linux/platform_device.h"/*  * 设备资源信息,也就是LED0所使用的所有寄存器*/
static struct resource led_resources[] = {};/* @description       : 释放flatform设备模块的时候此函数会执行* @param - dev    : 要释放的设备* @return          : 无*/
static void led_release(struct device *dev)
{printk("led device released!\r\n");
}/** platform设备结构体 */
static struct platform_device led_device = {.name = "imx6ul-led",.id = -1,.dev = {.release = &led_release,},.num_resources = ARRAY_SIZE(led_resources),.resource = led_resources,
};/** @description : 设备模块加载 * @param      : 无* @return       : 无*/
static int __init led_device_init(void)
{return platform_device_register(&led_device);
}/** @description  : 设备模块注销* @param       : 无* @return       : 无*/
static void __exit led_device_exit(void)
{platform_device_unregister(&led_device);
}module_init(led_device_init);
module_exit(led_device_exit);
MODULE_LICENSE("GPL");

六、platform 总结

platform 平台包括驱动总线设备 3 部分。总线负责管理驱动和设备,这部分有内核进行维护。设备主要描述硬件相关信息,和设备树作用相同,因此有设备树可以不需要这部分。驱动主要用于实现对硬件的操作,向应用层提供一些 API 接口,供上层应用使用。

Linux 驱动开发 四十三:platform 设备驱动实验(一)相关推荐

  1. 嵌入式Linux驱动笔记(五)------学习platform设备驱动

    你好!这里是风筝的博客, 欢迎和我一起交流. 设备是设备,驱动是驱动. 如果把两个糅合写一起,当设备发生变化时,势必要改写整个文件,这是非常愚蠢的做法.如果把他们分开来,当设备发生变化时,只要改写设备 ...

  2. Linux 驱动开发 四十四:platform 设备驱动实验(二)

    驱动测试通过操作 led 完成. 一.原理图 二.无设备树源码实现 无设备树时候通过 platform_device.name 和 platform_driver.driver.name 进行匹配. ...

  3. Linux 驱动开发 三:字符设备驱动框架

    一.参考 (3条消息) Linux 字符设备驱动结构(一)-- cdev 结构体.设备号相关知识解析_知秋一叶-CSDN博客 (3条消息) linux设备驱动框架_不忘初心-CSDN博客_linux设 ...

  4. Linux驱动开发--写一个块设备驱动

    原文地址:[原创] 写一个块设备驱动 http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=2017377&fromuid=288 ...

  5. linux驱动开发5之字符设备驱动新接口

    1.新接口与老接口 1)老接口:register_chrdev static inline int register_chrdev(unsigned int major, const char *na ...

  6. Linux嵌入式学习(简单 platform 设备驱动的实现)

    文章目录 编辑device.c 编辑driver.c 编辑Makefile 编辑app.c 编译.加载和测试 话不多说,直接开始吧- 工作环境:VMware14+Ubuntu18.04 内核版本:5. ...

  7. i.MX6ULL驱动开发 | 24 - 基于platform平台驱动模型点亮LED

    一.编写基本设备驱动模块 编写驱动模块源码: #include <linux/module.h> #include <linux/init.h>static int __ini ...

  8. Linux 设备驱动开发 —— platform 设备驱动

    一.platform总线.设备与驱动         在Linux 2.6 的设备驱动模型中,关心总线.设备和驱动3个实体,总线将设备和驱动绑定.在系统每注册一个设备的时候,会寻找与之匹配的驱动:相反 ...

  9. linux 块设备驱动 (三)块设备驱动开发

    linux 块设备驱动 (三)块设备驱动开发 一: 块设备驱动注册与注销 块设备驱动中的第1个工作通常是注册它们自己到内核,完成这个任务的函数是 register_blkdev(),其原型为: int ...

最新文章

  1. LeetCode简单题之图像渲染
  2. Windows API入门系列之五 -一个正儿八经的SDK程序
  3. ...python の 学习
  4. Redis数据库操作指令
  5. Flask 离线脚本
  6. 2020笔记本性价比之王_2020轻薄本性价比之王_2020轻薄本哪款好
  7. 收藏!目标检测优质综述论文总结!
  8. arm跑操作系统的意义_不太远的猜想:当ARM和鸿蒙OS在笔记本领域相遇,颠覆已无可避免...
  9. 通过adb查看当前页面中显示的窗口Activity名称
  10. ​​​​​​​​CloudMounter:挂载云存储作为在 Mac 的本地磁盘
  11. python之父名言_Python之父:为什么操作符很有用?
  12. 计算机二级excel数据有效性,Excel数据有效性,最全面的教程在这里(2)
  13. Gamefi+山海经,流行与古风的结合,带你边玩边赚
  14. Inna and Alarm Clock
  15. iOS WKWebView 那些坑
  16. 好书推荐,电子人的入门好书
  17. python公开课乐博学院_乐搏学院 - 主页
  18. 次世代zbrush骷颅头高模雕刻 艺术头骨SP模型材质贴图讲解
  19. Qt在Win下调用系统的软键盘,区分win7\win8\win10
  20. nodejs+vue基于决策树算法的大学生就业预测系统

热门文章

  1. CAD设置在标题中显示完整路径
  2. 数学-线性代数3(相关性、基、维数、四个基本子空间)
  3. template模板的使用及文章详情跳转
  4. php mpt文件,skyline制作mpt
  5. 计算机毕业设计(附源码)python裕民镇养老院信息管理系统
  6. PMP考试经验总结分享
  7. python爬取歌词生成词云图_爬取毛不易歌词作词云展示
  8. 企业律师事务官方介绍网站源码
  9. CSS--ps的常见操作
  10. 运算放大器使用六原则