linux 初始化 驱动程序,Linux设备驱动初始化流程

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);

#else

iram_init(MX28_OCRAM_PHBASE, MX28_OCRAM_SIZE);

#endif

mx28_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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值