目录
6.1、以leds-s3c24xx.c为例来分析platform设备和驱动的注册过程
8.1、检查mach-x210.c中是否有led相关的platform_device
8.2、参考mach-mini2440.c中添加led的platform_device定义
8.3、测试只有platform_device没有platform_driver时是怎样的
9.1、测试platform_device和platform_driver相遇时会怎样
四.platform平台总线工作原理1
4.1、何为平台总线
(1)相对于usb、pci、i2c等物理总线来说,platform总线是虚拟的、抽象出来的。
(2)回顾裸机中讲的,CPU与外部通信的2种方式:地址总线式连接和专用接口式连接。平台总线对应地址总线式连接设备,也就是SoC内部集成的各种内部外设。思考:为什么要有平台总线?
专用接口其实就是对应usb、pci、i2c等物理总线,就是通过这些物理总线跟外部连接的。那么扩展到CPU32位地址空间的设备
怎么办?扩展到CPU32位地址空间的设备是不对应具体的一种的这种物理总线,所以就给他发明了平台总线
总线一开始是为了USB、I2C、PCI这些设备设计的,扩展到CPU32位地址空间的设备并没有总线这种概念。为了避免有一部分设备有总线有一部分设备没有总线的管理不便,所以给所有的设备规定都有总线。即平台总线的发明其实就是为了给那些本来不需要总线的设备来连接
4.2、平台总线下管理的2员大将
(1)platform工作体系都定义在drivers/base/platform.c中
(2)两个结构体:platform_device( 平台总线的设备结构体) 和platform_driver( 平台总线的驱动结构体) 。
(3)两个接口函数:platform_device_register和platform_driver_register
\include\linux\platform_device.h
struct platform_device {
const char * name;//平台总线下设备的名字,每个不一样
int id;
struct device dev;//内核中通用的设备属性部分
u32 num_resources;//设备使用到的resources(资源)个数
struct resource * resource;//设备使用到的资源数组的首地址
const struct platform_device_id *id_entry;//指向数组,此数组是设备的ID表
/* arch specific additions */
struct pdev_archdata archdata;//自留地,用来提供扩展的
};
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表
};
五.platform平台总线工作原理2
5.1、平台总线体系的工作流程
(2)第二步:内核移植的人负责提供platform_device
(3)第三步:写驱动的人负责提供platform_driver,一般都是放在 Mach-x210.c 中。 或者对应相关的地方, 例如:x210的触摸屏驱动
(4)第四步:platform的match函数发现driver和device匹配后,调用driver的probe函数来完成驱动的初始化和安装,然后设备就工作起来了
我们将以platform总线为例,来看看,/sys/bus/platform是如何建立
参考:https://www.cnblogs.com/Ph-one/p/5052191.html
platform总线的注册是由platform_bus_init函数完成的。该函数在内核启动阶段被调用,我们来简单看下调用过程:
start_kernel() -> rest_init() ->kernel_init() -> do_basic_setup() -> driver_init() -> platform_bus_init()。
5.2、代码分析:platform本身注册
(1)每种总线(不光是platform,usb、i2c那些也是)都会带一个match方法,match方法用来对总线下的device和driver进行匹配。理论上每种总线的匹配算法是不同的,但是实际上一般都是看name的。
platform 的注册如下:\kernel\drivers\base\platform.c
int __init platform_bus_init(void)
{
int error;
early_platform_cleanup();
error = device_register(&platform_bus);//注册platform_bus
if (error)
return error;
error = bus_register(&platform_bus_type);//在总线平台下注册platform_bus_type
if (error)
device_unregister(&platform_bus);
return error;
}
struct bus_type platform_bus_type = {
.name = "platform", //总线平台的名字
.dev_attrs = platform_dev_attrs,
.match = platform_match, //对总线下面的device和driver进行匹配的
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
(2)platform_match函数就是平台总线的匹配方法。该函数的工作方法是:如果有id_table就说明驱动可能支持多个设备,所以这时候要去对比id_table中所有的name,只要找到一个相同的就匹配上了不再找了,如果找完id_table都还没找到就说明每匹配上;如果没有id_table或者每匹配上,那就直接对比device和driver的name,如果匹配上就匹配上了,如果还没匹配上那就匹配失败。
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);//这里使用了container_of宏,作用就是知道某个结构体变量成员的指针,反推整个结构体变量的指针
/* match against the id table first */ 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); } |
container_of宏,作用是知道某个结构体变量成员的指针,反推整个结构体变量的指针
#define to_platform_driver(drv)
(container_of((drv), struct platform_driver, \
driver))