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



 

 

                        

 

原创粉丝点击