区分设备驱动模型和平台设备驱动模型。
设备驱动模型 可以理解为 总线、设备、驱动。
平台设备驱动模型 就是那些 Linux 内核管理没有物理总线(即是不需要特殊时序控制的设备)(也是Linux内核没有自动创建相应驱动总线的设备类型)的设备的一套 Linux 平台总线、平台模型、平台驱动的模型。
为解决驱动代码和设备信息耦合问题,linux提出了设备驱动模型。
注意,前面的总线、设备、驱动是一个软件层面的抽象,与 SOC 中物理总线概念不一样。
物理总线:芯片与各个功能外设之间传送信息的公共通信干线,包括数据总线、地址总线和控制总线,以此来传输各种通信时序。
驱动总线:负责管理驱动和设备。制定设备和驱动的匹配规则。一旦总线上注册了新设备/驱动,总线便执行匹配程序。
对于常见的 I2C、SPI、USB等 物理总线,Linux 内核都会自动创建与之对应的 驱动总线。所以 I2C设备、SPI设备、USB设备都会挂在在相应的总线上。
相对的,实际项目开发中还有很多结构简单的设备是不需要特殊的时序控制的。也就没有相应的物理总线,Linux 也不会为它们创建相应的驱动总线。如 LED、RTC时钟、按键等等。
但是为了这些简单的设备也能遵循设备驱动模型,Linux 内核引入了一种虚拟总线--平台总线。
平台总线 用于管理、挂在那些没有物理总线的设备,且,这些设备被称为平台设备,对应的设备驱动被称为平台驱动。
平台设备使用 platform_device 结构体进行表示,继承了设备驱动模型中的 device 结构体。
平台驱动使用 platform_driver 结构体进行表示,继承了设备驱动模型中的 device_driver 结构体。
Linux内核只有一条平台总线,用于图一管理简单设备--platform_bus_type。
最先比较:
最先比较 platform_device.driver_override 和 platform_driver.driver.name。
可以设置 platform_device 的 driver_override,强制选择某个 platform_driver。
其次比较:
其次比较 platform_device.name 和 platform_driver.id_table[i].name。
platform_driver.id_table 是 platform_device_id 指针,表示该 drv 支持若干个 device,它里面列出了各个 device 的 {.name, .driver_data},其中的 name 表示该 drv 支持的设备的名字,driver_data是些提供给该 device 的私有数据。
最后比较:
最后比较 platform_device.name 和 platform_driver.driver.name。
由于 platform_driver.id_table 可能为空,所以,接下来就可以使用 platform_driver.driver.name 来匹配。
平台总线:
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);
位于 内核源码/driver/base/platform.c。
该总线在Linux内核启动的时候自动进行注册。
平台总线初始化函数源码:
int __init platform_bus_init(void){
int error;
...
error = bus_register(&platform_bus_type);
...
return error;}
error = bus_register(&platform_bus_type);
就是向Linux内核注册 plateform_bus_type 平台总线。建议:以上匹配规则及源码,可以追踪函数 platform_match 源码来分析。
平台设备:
struct platform_device {
const char *name;
int id;
struct device dev;
u32 num_resources;
struct resource *resource;
const struct platform_device_id *id_entry;
/* 省略部分成员 */
};
平台设备的工作是为 驱动程序 提供 设备信息。包括 硬件信息 和 软件信息。
硬件信息:
/**
* Resources are tree-like, allowing nesting etc..
*/
struct resource {
resource_size_t start;
resource_size_t end;
const char *name;
unsigned long flags;
/* 省略部分成员 */
};
设备驱动程序主要目的还是操作设备的寄存器。
不同架构的计算机提供不同的操作接口,主要有IO端口映射和IO内存映射两种方式。
IO端口映射方式:
IO内存映射方式:
软件信息:
注册:platform_device_register:
int platform_device_register(struct platform_device *pdev)
;位于 内核源码/drivers/base/platform.c。
注销:platform_device_unregister:
void platform_device_unregister(struct platform_device *pdev)
;位于 **平台驱动:
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
};
位于 内核源码/include/platform_device.h
probe:匹配成功后执行的回调函数。
remove:移除某个平台设备是的回调函数。
driver:Linux 设备模型中用于抽象驱动的 device_driver 结构体,platform_driver 继承该结构体,也就获取了设备模型驱动对象的特性。
id_table:表示该驱动能够兼容的设备类型。
platform_device_id
struct platform_device_id
{
char name[PLATFORM_NAME_SIZE];
kernel_ulong_t driver_data;
};
注册:platform_driver_register:
int platform_driver_register(struct platform_driver *drv)
。
注销:platform_driver_unregister:
void platform_driver_unregister(struct platform_driver *drv)
;位于 **首先要知道的是,平台设备的硬件信息保存在 resource 结构体中。而软件信息则保存在 platform_data 中。
获取硬件信息:platform_get_resource:
获取平台设备提供的资源结构体。一般用在 probe 函数中。、
函数原型:struct resource *platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num);
dev:指定获取哪个平台设备的资源;
type:指定获取资源的类型,如IORESOURCE_MEM、IORESOURCE_IO等;
num:指定要获取的资源编号。每个设备所需要的资源的个数是不一样的。且不同资源的编号是不一样的。
返回:
若要获取的资源类型为 IORESOURCE_IRQ,平台设备驱动还提供以下函数接口,来获取中断引脚。
int platform_get_irq(struct platform_device *pdev, unsigned int num)
获取软件信息:dev_get_platdata:
* dev:struct device 结构体类型指针。
static inline void *dev_get_platdata(const struct device *dev)
{
return dev->platform_data;
}
原文:https://www.cnblogs.com/lizhuming/p/14605803.html