Linux设备驱动初始化流程
来源:互联网 发布:淘宝的药店靠谱吗 编辑:程序博客网 时间:2024/05/01 22:51
Linux设备驱动初始化的流程一直不是很清楚,今天仔细看了一下linux初始化部分的代码才真正的搞明白,记录下来。
做过驱动的同学都知道,在arch/arm/目录下有和板级配置相关的文件,我使用的是Fresscale i.MX28开发板,在arch/arm/mach-mx28/目录下有mx28evk.c文件,该文件中有开发板初始化需要调用的函数。
MACHINE_START(MX28EVK, "Freescale MX28EVK board").phys_io= 0x80000000,.io_pg_offst= ((0xf0000000) >> 18) & 0xfffc,.boot_params= 0x40000100,.fixup= fixup_board,.map_io= mx28_map_io,.init_irq= mx28_irq_init,.init_machine= mx28evk_init_machine,.timer= &mx28_timer.timer,MACHINE_END
看MACHINE_START的定义才发现,上面的这几行代码其实就是定义一个 machine_desc的结构体变量,并对其中部分成员赋值。
#define MACHINE_START(_type,_name)\static const struct machine_desc __mach_desc_##_type\ __used\ __attribute__((__section__(".arch.info.init"))) = {\.nr= MACH_TYPE_##_type,\.name= _name,#define MACHINE_END\};
函数mx28evk_init_machine主要初始化开发板的一些gpio、特殊引脚、平台设备,并将这些平台设备添加到内核。这个函数名被赋值给init_machine这个指针。
static void __init mx28evk_init_machine(void){mx28_pinctrl_init();/* Init iram allocate */#ifdef CONFIG_VECTORS_PHY_ADDR/* reserve the first page for irq vector table*/iram_init(MX28_OCRAM_PHBASE + PAGE_SIZE, MX28_OCRAM_SIZE - PAGE_SIZE);#elseiram_init(MX28_OCRAM_PHBASE, MX28_OCRAM_SIZE);#endifmx28_gpio_init();mx28evk_pins_init();mx28_device_init();mx28evk_device_init();}
我现在要关注的就是这个函数在什么地方调用,查阅源码发现在arch/arm/kernel/setup.c文件中的 setup_arch函数中对init_machine有赋值操作。仔细阅读源码
void __init setup_arch(char **cmdline_p){struct tag *tags = (struct tag *)&init_tags;struct machine_desc *mdesc;char *from = default_command_line;unwind_init();setup_processor();mdesc = setup_machine(machine_arch_type);machine_name = mdesc->name; *******************(省略部分代码) /* * Set up various architecture-specific pointers */init_arch_irq = mdesc->init_irq;system_timer = mdesc->timer;init_machine = mdesc->init_machine;early_trap_init();}
通过setup_machine获得这个开发板对应的machine_desc结构体变量,machine_arch_type其实是一个宏,定义在include/generated/mach-types.h,这个文件是编译生成的。
#ifdef CONFIG_MACH_MX28EVK# ifdef machine_arch_type# undef machine_arch_type# define machine_arch_type__machine_arch_type# else# define machine_arch_typeMACH_TYPE_MX28EVK# endif# define machine_is_mx28evk()(machine_arch_type == MACH_TYPE_MX28EVK)#else# define machine_is_mx28evk()(0)#endif
MACH_TYPE_MX28EVK宏为
#define MACH_TYPE_MX28EVK 2531
在setup_arch函数的后面将mdesc->init_machine赋值给函数指针init_machine
static void (*init_machine)(void) __initdata;static int __init customize_machine(void){/* customizes platform devices, or adds new ones */if (init_machine)init_machine();return 0;}arch_initcall(customize_machine);
看到这里终于知道mx28evk_init_machine函数是在哪里调用的了
但它没有被明显的调用,而是被放进arch_initcall()里面,去看看arch_initcall()是怎么定义的。在include/linux/init.h文件中有
#define arch_initcall(fn)__define_initcall("3",fn,3)
#define __define_initcall(level,fn,id) \static initcall_t __initcall_##fn##id __used \__attribute__((__section__(".initcall" level ".init"))) = fn
凭着看代码的经验,customize_machine函数被放在.initcall3.init里,.initcall3.init定义在arch/arm/kernel/vmlinux.lds中
__initcall_start = .; *(.initcallearly.init) __early_initcall_end = .; *(.initcall0.init) *(.initcall0s.init) *(.initcall1.init) *(.initcall1s.init) *(.initcall2.init) *(.initcall2s.init) *(.initcall3.init) *(.initcall3s.init) *(.initcall4.init) *(.initcall4s.init) *(.initcall5.init) *(.initcall5s.init) *(.initcallrootfs.init) *(.initcall6.init) *(.initcall6s.init) *(.initcall7.init) *(.initcall7s.init) __initcall_end = .;
在init/main.c do_initcalls函数中被调用
extern initcall_t __initcall_start[], __initcall_end[], __early_initcall_end[];static void __init do_initcalls(void){initcall_t *fn;for (fn = __early_initcall_end; fn < __initcall_end; fn++)do_one_initcall(*fn);/* Make sure there is no pending stuff from the initcall sequence */flush_scheduled_work();}
do_initcalls中有一个for循环,customize_machine在这里被调用,也就是说mx28evk_init_machine函数在这for循环期间被调用。mx28evk_init_machine函数执行后,platform_device全部被添加到内核中。接下来就该看看driver是在什么时候加载的。
还记得我们写驱动时都要使用 module_init()将driver入口函数告诉内核。其实module_init是一个宏,也定义在include/linux/init.h文件中
/** * module_init() - driver initialization entry point * @x: function to be run at kernel boot time or module insertion * * module_init() will either be called during do_initcalls() (if * builtin) or at module insertion time (if a module). There can only * be one per module. */#define module_init(x)__initcall(x);
#define __initcall(fn) device_initcall(fn)
#define device_initcall(fn)__define_initcall("6",fn,6)
#define __define_initcall(level,fn,id) \static initcall_t __initcall_##fn##id __used \__attribute__((__section__(".initcall" level ".init"))) = fn
根据上面这些代码我们知道,使用module_init()的驱动,入口函数被放在 .initcall6.init里,也是在init/main.c do_initcalls函数中被调用。相比platform_device,各种driver相对要靠后一点才会被调用。
总结一下:设备驱动初始化的大概流程是这样
Start_kernel()----->Reset_init()------->Kernel_init()------->do_basic_setup------->do_initcalls
- Linux设备驱动初始化流程
- Linux设备驱动工作流程
- linux I2C设备驱动流程
- Linux设备驱动开发流程
- Linux设备驱动工作流程转
- Linux设备驱动开发大致流程
- linux设备驱动的大致流程
- 嵌入式Linux设备驱动的大致流程
- Linux字符设备驱动编写流程
- 【Linux操作系统分析】设备驱动处理流程
- Linux字符设备驱动编写基本流程
- Linux Kernel设备驱动模型之设备初始化
- Linux Kernel设备驱动模型之平台设备初始化
- Linux Kernel设备驱动模型之驱动模型初始化
- 字符设备初始化流程
- Linux设备驱动之一 ---- 驱动的框架及其操作流程
- Linux设备驱动之pci设备的枚举(linux初始化时PCI设备识别)
- Linux设备驱动程序学习笔记14:中断的初始化流程
- JQuery插件Uploadify的使用文档帮助教程
- 一网打尽当下NoSQL类型、适用场景及使用公司
- nginx模块开发-定时器模型
- ios本地通知UILocalNotification以及区分谁触发了通知
- 使用Lvs DR模式实现负载均衡
- Linux设备驱动初始化流程
- inout buffer
- dede头部导航和底部的调用
- JAVA同步代码块
- 敲不出代码,但是上课听的明白,怎么办?
- html5 游戏研究 参考
- Server 2012 Hyper-v新功能之三:Hyper-V 副本
- 整合discuz X2.5后用户免激活设置
- Constructor/Destructor/Copy Constructor/operator =