IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> i.MX6ULL驱动开发 | 23 - Linux下的驱动分离与分层——platform平台驱动模型 -> 正文阅读

[系统运维]i.MX6ULL驱动开发 | 23 - Linux下的驱动分离与分层——platform平台驱动模型

一、Linux驱动的分离

1. 为什么需要驱动分离?

在嵌入式开发中,无论处理器如何更换,外设模块的操作都是一致的,比如有三个不同的平台都要驱动MPU6050传感器,最简单的方法是针对每个平台都写一份驱动:

显然这种处理方式太low了,MPU6050都是使用I2C接口操作的,对于不同的平台,只是I2C操作方式不一样,所以这里可以将I2C接口抽象出来,给不同的平台用自己的库函数适配:

这样多种平台就可以共用同一份MPU6050驱动:

2. Linux内核中的驱动分离

在Linux内核中,一般SOC的主机控制器驱动已经由半导体厂家写好了,比如这里imx6ull的i2c控制器驱动已经由NXP写好了。

而对于具体的设备,比如MPU6050等设备,其驱动程序也由设备厂商写好了。

我们要做的是提供设备信息即可,比如:设备连接到了哪个I2C接口上?支持的速率是多少?设备在总线上的从机地址是多少?等等。

这样设计之下,SOC的外设驱动只负责外设驱动,某个设备的驱动只负责设备驱动,使用时只需要让内核将二者联系起来即可。


当我们向系统注册一个驱动的时候,内核就会在右侧的设备中查找有没有匹配的设备,同样,当我们向系统中注册一个设备的时候,内核就会在左侧的驱动中查找有没有匹配的驱动。

这个就是Linux内核中的总线(bus)、驱动(driver)、设备(device)模型,也称之为Linux内核中的驱动分离。

3. Linux内核中的驱动分层

分层的目的是为了在不同的层处理不同的内容

以input子系统为例,input子系统负责管理所有跟输入有关的驱动,包括键盘、鼠标、触摸板等。

  • 设备原始驱动层:负责获取输入设备的原始值。
  • input核心层:处理各种IO模型,并且提供file_operations操作集合。

这样分层设计之后,在编写输入设别的驱动时,只需要考虑到如何上报输入事件即可,至于如何处理这些上报的输入事件,是上层需要做的事情,无需关注。

二、platform平台驱动模型

1. 为什么需要platform?

Linux内核中的驱动程序分离为:总线(bus)、驱动(driver)、设备(device)模型,但是有些SOC中有些外设是没有总线这个概念的。

为了解决此问题,Linux内核中提出了platform平台模型作为虚拟总线,相应的有platform_driver和platform_device。

2. platform总线

2.1. platform总线的类型——bus_type结构体

Linux内核中使用bus_type结构体表示总线,定义在文件include/linux/device.h,如下:

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;
};

总线最重要的工作就是根据注册的设备来查找对应的驱动,或者根据注册的驱动来查找相应的设备,该任务主要依赖match函数指针,所以每一条总线都必须实现此函数。

int (*match)(struct device *dev, struct device_driver *drv);

可以看到,match函数有两个参数:dev和drv。

  • dev:device类型,表示设备
  • drv:device_driver类型,表示驱动

2.2. platform总线

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

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 就是匹配函数。

2.3. 驱动和设备如何匹配

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.
 */
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 */
	if (of_driver_match_device(dev, drv))
		return 1;

	/* Then try ACPI style match */
	if (acpi_driver_match_device(dev, drv))
		return 1;

	/* Then try to match against the id table */
	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);
}

从代码中可以看到,设备和驱动的匹配有四种方法。

(1)OF类型的匹配(设备树采用的匹配方式):根据设备节点的 compatible 属性和驱动的of_match_table表进行匹配;
(2)ACPI匹配方式
(3)id_table匹配
(4)直接比较驱动和设备的name字段是否相等

3. platform驱动(重点)

3.1. platform_driver结构体

platform_driver结构体表示platform驱动,定义在文件include/linux/platform_device.h中,如下:

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;
	bool prevent_deferred_probe;
};

(1)probe函数

当驱动与设备匹配成功以后probe函数就会执行,由驱动的提供者编写。

(2)remove函数

驱动卸载的时候会执行

(3)driver成员

device_driver相当于基类,提供了最基础的驱动框架,platform_driver继承了这个基类。

(4)id_table表

(5)of_match_table表:设备树匹配表

3.2. platform驱动可用API

(1)向内核注册一个platform驱动

/**
 * __platform_driver_register - register a driver for platform-level devices
 * @drv: platform driver structure
 * @owner: owning module/driver
 */
int __platform_driver_register(struct platform_driver *drv,
				struct module *owner);

/*
 * use a macro to avoid include chaining to get THIS_MODULE
 */
#define platform_driver_register(drv) \
	__platform_driver_register(drv, THIS_MODULE)

(2)从内核卸载一个platform驱动

extern void platform_driver_unregister(struct platform_driver *);

4. platform设备

如果内核支持设备树,platform设备用设备树描述。Linux内核启动的时候会从设备树中读取信息,然后将其组织成platform_device结构体形式。

如果内核不支持设备树,可以直接用platform_device结构体描述。

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-07-03 11:10:24  更:2022-07-03 11:10:36 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 13:01:33-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码