platform总线是Linux提供的一种虚拟总线,struct platform_device表示设备,struct platform_driver表示驱动
1> 内核初始化platform总线:
start_kernel()
rest_init()
kernel_init()
do_basic_setup() // init/main.c
driver_init() // drivers/base/init.c
platform_bus_init() // drivers/base/platform.c 初始化platform总线
device_register(&platform_bus);
bus_register(&platform_bus_type);
platform_bus_type ==> platform_match(dev和drv的配对原则)
2> 注册device
设备驱动程序调用platform_device_register(struct platform_device *pdev)向platform总线注册device
如hisfc300_module_init:
platform_device_register(&hisfc300_device_pltdev); // drivers\mtd\devices\hisfc300\hisfc300.c
platform_device_add(pdev) //
device_add(&pdev->dev);
bus_probe_device(struct device *dev)
device_attach(struct device *dev)
bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
__device_attach(struct device_driver *drv, void *data)
driver_match_device(drv, dev) // 匹配drv和dev,匹配成功,往下走
driver_probe_device(drv, dev);
really_probe(dev, drv);
dev->bus->probe(dev);
3> 注册driver
设备驱动程序实现调用platform_driver_register(struct platform_driver *drv)向platform总线注册driver
hisfc300_module_init()
platform_device_register(&hisfc300_device_pltdev);
----------------------------------------------------------------------------------------------------------
device层:
1. 理解两个重要的数据结构:
struct device{} 结构体:(include/linux/device.h)
内核表示设备的基础结构体;每个设备由一个struct device代表();
struct platform_device{}结构体:(include/linux/platform_device.h)
将硬件资源注册进入内核,该结构体中的struct resource(include/linux/ioport.h)结构体关联实际的硬件资源,比如中断号等,
问:谁用platform_device结构体呢?
答:在各个硬件驱动程序中都会有platform_device静态变量定义,在源码中搜索,可以搜索到很多,都是在各个设备的驱动.c文件中。
--------------- 以dm9000网卡驱动为例追踪platform_device怎么样注册platform总线的 ------------------------
问:platform_device定义在哪里?
答:dm9000网卡驱动platform_device在arch/arm/mach-s3c2440/mach-mini2440.c中定义如下:
static struct platform_device mini2440_device_eth = {
.name = "dm9000",
.id = -1,
.num_resources = ARRAY_SIZE(mini2440_dm9k_resource),
.resource = mini2440_dm9k_resource, // dm9000的resource
.dev = {
.platform_data = &mini2440_dm9k_pdata,
},
};
上面的定义中.resource定义了dm9k的硬件资源
问:mini2440_device_eth在哪里使用?
答:mini2440_device_eth包含了在了mini2440_devices数组中,该数组包含了mini2440外设的platform_device定义
static struct platform_device *mini2440_devices[] __initdata = {
&s3c_device_ohci,
&s3c_device_wdt,
&s3c_device_i2c0,
&s3c_device_rtc,
&s3c_device_usbgadget,
&mini2440_device_eth,
&mini2440_led1,
&mini2440_led2,
&mini2440_led3,
&mini2440_led4,
&mini2440_button_device,
&s3c_device_nand,
&s3c_device_sdi,
&s3c_device_iis,
&uda1340_codec,
&mini2440_audio,
&samsung_asoc_dma,
};
问:mini2440_devices[]数组在哪里使用?
答:mini2440_init()函数中作为platform_add_devices函数的参数被使用, platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));
platform_add_devices函数中,调用了platform_device_register对mini2440_devices每个元素调用platform_device_add,将设备加入到platform_bus_type中;
platform_device_add函数中继续调用device_add函数,device_add函数 -----
问:mini2440_init()函数在哪里调用?
答:
MACHINE_START(MINI2440, "MINI2440")
.boot_params = S3C2410_SDRAM_PA + 0x100,
.map_io = mini2440_map_io,
.init_machine = mini2440_init,
.init_irq = s3c24xx_init_irq,
.timer = &s3c24xx_timer,
MACHINE_END
MACHINE_START/MACHINE_END是什么?后面理解,从上面可以看出是赋值给我init_machine字段,搜索init_machine
init_machine在arch/arm/kernel/setup.c中被customize_machine调用;customize_machine放在了arch_initcall中,arch_initcall(customize_machine);
arch_initcall是什么?
arch_initcall是一个宏定义,定义在include/linux/init.h文件中
#define arch_initcall(fn) __define_initcall("3",fn,3)
__define_initcall也是一个宏定义,也定义在include/linux/init.h
#define __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" level ".init"))) = fn
__define_initcall的作用:将fn放在".initcall" level ".init"
arch_initcall(customize_machine)-> __define_initcall("3",customize_machine,3)->将customize_machine放在".initcall3.init"
.initcall3.init做什么用的?
学习这篇博客:https://blog.csdn.net/ooonebook/article/details/52690132
--------------- 以dm9000网卡驱动为例追踪platform_device怎么样注册platform总线的 ------------------------
总结:不同架构在将device注册进内核的方式不一样,像上面mini2440,内核启动时就会将设备注册到platform总线。还有一些则是在驱动的实现代码中将device加入到platform总线,例如hisfc300驱动
2. platform_device怎么样注册到platform?
驱动程序调用drivers/base/platform.c下的platform_device_register,platform_device_register又调用drivers/base/platform.c文件中的platform_device_add函数
xxx_module_init() //驱动模块入口函数
platform_device_register(struct platform_device *pdev)
platform_device_add(struct platform_device *pdev)
device_add(struct device *dev)
bus_probe_device(struct device *dev)
device_attach(struct device *dev)
bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
__device_attach(struct device_driver *drv, void *data)
driver_match_device(struct device_driver *drv, struct device *dev)
drv->bus->match(dev, drv) // 调用了struct device_driver下的struct bus_type *bus中的match函数,struct bus_type *bus在platform_device_add中被赋值。
driver_probe_device(drv, dev);
really_probe(struct device *dev, struct device_driver *drv)
dev->bus->probe(dev)或者drv->probe(dev); // 优先使用struct device中bus的probe函数,如果struct device中bus的probe为空,则使用struct device_driver中的probe函数
driver层:
struct platform_driver结构体中的probe函数
}
以hisfc300驱动为例理解内核platform驱动总线平台(drivers/mtd/devices/hisfc300/hisfc300.c)
----------------------------------------------------------------------------------------------------------
spi_master: spi_master代表一个主机控制器
spi_device: spi_device代表一个外围spi设备
spi_driver: pi_driver代表一个SPI协议驱动,
基于内核platform总线驱动框架、platform_device, platform_driver, spi_device, spi_driver
struct platform_driver
struct device_driver --->
platform_driver->向总线注册驱动,platform_device向总线挂载设备,总线根据name,选择device对应的driver,找到后执行dirver下的probe函数
drivers\spi\spi_s3c24xx.c
platform_driver_probe(&s3c24xx_spi_driver, s3c24xx_spi_probe); // drivers/base/platform.c, s3c24xx_spi_probe作用?
drv->probe = probe; // 将s3c24xx_spi_probe赋给s3c24xx_spi_driver的probe
platform_driver_register(drv); // drivers/base/platform.c
driver_register(&drv->driver); // 将s3c24xx_spi_driver注册到platform bus
bus_add_driver()
arch\arm\mach-s3c2440\mach-smdk2440.c
smdk2440_machine_init()
platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
platform_device_register
smdk2440_machine_init()在内核启动时调用
Start_kernel() -> setup_arch() -> setup_machine() -> lookup_machine_type()
内核启动的时候, platform_device 是优先于 platform_driver 注册的。 比如 platform_device A , 是在arch/arm/mach-XXXX/mach-XXXX.c 文件里注册的, 而这个文件的代码是 优先于 platform_driver_register 执行的。
所以在你platform_driver_register执行的时候, platform_device A已经被挂在platform_bus总线上了, 而platform_driver_register()有个功能是到platform_bus上去挨个找寻,找寻挂在上面的platform_bus上的platform_device。找到了就执行probe()。
内核初始化platform总线:
start_kernel
rest_init
kernel_init()
do_basic_setup() // init/main.c
driver_init() // drivers/base/init.c
platform_bus_init() // drivers/base/platform.c 初始化platform总线
device_register(&platform_bus);
bus_register(&platform_bus_type);
platform_bus_type ==> platform_match(dev和drv的配对原则)