您的位置:首页 > 运维架构 > Linux

ambarella s2l11m linux device_initcall

2015-11-23 19:44 741 查看
main.c

asmlinkage void __init start_kernel(void)

-- rest_init();

--kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);

--static int __ref kernel_init(void *unused)

--kernel_init_freeable();

--do_basic_setup();

--do_initcalls()

static void __init do_initcalls(void)

{

int level;

for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)

do_initcall_level(level);

}

static void __init do_initcall_level(int level)

{

extern const struct kernel_param __start___param[], __stop___param[];

initcall_t *fn;

strcpy(static_command_line, saved_command_line);

parse_args(initcall_level_names[level],

static_command_line, __start___param,

__stop___param - __start___param,

level, level,

&repair_env_string);

for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)

do_one_initcall(*fn);

}

init.h

#define __define_initcall(fn, id) \

static initcall_t __initcall_##fn##id __used \

__attribute__((__section__(".initcall" #id ".init"))) = fn

/*

* Early initcalls run before initializing SMP.

*

* Only for built-in code, not modules.

*/

#define early_initcall(fn) __define_initcall(fn, early)

/*

* A "pure" initcall has no dependencies on anything else, and purely

* initializes variables that couldn't be statically initialized.

*

* This only exists for built-in code, not for modules.

* Keep main.c:initcall_level_names[] in sync.

*/

#define pure_initcall(fn) __define_initcall(fn, 0)

#define core_initcall(fn) __define_initcall(fn, 1)

#define core_initcall_sync(fn) __define_initcall(fn, 1s)

#define postcore_initcall(fn) __define_initcall(fn, 2)

#define postcore_initcall_sync(fn) __define_initcall(fn, 2s)

#define arch_initcall(fn) __define_initcall(fn, 3)

#define arch_initcall_sync(fn) __define_initcall(fn, 3s)

#define subsys_initcall(fn) __define_initcall(fn, 4)

#define subsys_initcall_sync(fn) __define_initcall(fn, 4s)

#define fs_initcall(fn) __define_initcall(fn, 5)

#define fs_initcall_sync(fn) __define_initcall(fn, 5s)

#define rootfs_initcall(fn) __define_initcall(fn, rootfs)

#define device_initcall(fn) __define_initcall(fn, 6)

#define device_initcall_sync(fn) __define_initcall(fn, 6s)

#define late_initcall(fn) __define_initcall(fn, 7)

#define late_initcall_sync(fn) __define_initcall(fn, 7s)

#define __initcall(fn) device_initcall(fn)

#define module_init(x) __initcall(x);

i2c-ambarella.c

static int __init ambarella_i2c_init(void)

{

printk("ambarella_i2c_init\n");

return platform_driver_register(&ambarella_i2c_driver);

}

static void __exit ambarella_i2c_exit(void)

{

platform_driver_unregister(&ambarella_i2c_driver);

}

subsys_initcall(ambarella_i2c_init);

ambbus.c

static int __init amb_bus_init(void)

{

int error;

error = bus_register(&amb_bus_type);

if (!error) {

error = device_register(&amb_bus);

if (error)

bus_unregister(&amb_bus_type);

}

return error;

}

device_initcall(amb_bus_init);

static int __init pl011_init(void)

{

int ret;

printk(KERN_INFO "Serial: AMBA PL011 UART driver\n");

ret = uart_register_driver(&amba_reg);

if (ret == 0) {

ret = amba_driver_register(&pl011_driver);

if (ret)

uart_unregister_driver(&amba_reg);

}

return ret;

}

static void __exit pl011_exit(void)

{

amba_driver_unregister(&pl011_driver);

uart_unregister_driver(&amba_reg);

}

/*

* While this can be a module, if builtin it's most likely the console

* So let's leave module_exit but move module_init to an earlier place

*/

arch_initcall(pl011_init);

build.c

late_initcall(ubi_init);

在内核源代码中,platform 设备的初始化(注册)用<strong>arch_initcall()调用,它的initcall 的level为3</strong>;

而驱动的注册用<strong>module_init()调用,即device_initcall(),它的initcall 的level为6</strong>。
#define module_init(x) __initcall(x);
#define __initcall(fn) device_initcall(fn)

 kernel 初始化时(kernel_init@init/main.c),按照内核链接文件中(arm系统:kernel/arch/arm/vmlinux.lds)的<a target=_blank target="_blank" class="inner-link decor-none" href="http://zhidao.baidu.com/search?word=__init&fr=qb_search_exp&ie=utf8" rel="nofollow" style="color: rgb(51, 102, 153); text-decoration: none;">__init</a> call_start段的序列依次执行,
这样<strong>level小的初始化函数先于level大的初始化函数被调用</strong>。
所以platform设备先被注册,驱动加载时会调用驱动程序中的probe(),扫描系统中已注册的设备,找到匹配设备后将驱动和设备绑定。


比如I2C,适配器先需要 设备注册;


=============================

LINUX内核中有很多的初始化指示标志postcore_initcall(), arch_initcall(), subsys_initcall(), device_initcall(), etc. 这些起什么作用呢?

初始化标号

先看这些宏的定义(定义在文件include/linux/init.h中)

#define pure_initcall(fn) __define_initcall("0",fn,0)

#define core_initcall(fn) __define_initcall("1",fn,1)

#define core_initcall_sync(fn) __define_initcall("1s",fn,1s)

#define postcore_initcall(fn) __define_initcall("2",fn,2)

#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)

#define arch_initcall(fn) __define_initcall("3",fn,3)

#define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)

#define subsys_initcall(fn) __define_initcall("4",fn,4)

#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)

#define fs_initcall(fn) __define_initcall("5",fn,5)

#define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)

#define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)

#define device_initcall(fn) __define_initcall("6",fn,6)

#define device_initcall_sync(fn) __define_initcall("6s",fn,6s)

#define late_initcall(fn) __define_initcall("7",fn,7)

#define late_initcall_sync(fn) __define_initcall("7s",fn,7s)

2、__define_initcall

这些宏都用到了__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

这其中initcall_t是函数指针,原型如下,

typedef int (*initcall_t)(void);

而属性 __attribute__((__section__())) 则表示把对象放在一个这个由括号中的名称所指代的section中。

所以__define_initcall的含义是:

1) 声明一个名称为__initcall_##fn的函数指针;

2) 将这个函数指针初始化为fn;

3) 编译的时候需要把这个函数指针变量放置到名称为 ".initcall" level ".init"的section中。

3.放置.initcall.init SECTION

明确了__define_initcall的含义,就知道了是分别将这些初始化标号修饰的函数指针放到各自的section中的。

SECTION“.initcall”level”.init”被放入INITCALLS(include/asm-generic/vmlinux.lds.h)

#define INITCALLS \

*(.initcallearly.init) \

VMLINUX_SYMBOL(__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_start和__initcall_end以及INITCALLS中定义的SECTION都是在arch/xxx/kernel/vmlinux.lds.S中放在.init段的。

SECTIONS

{

.init : {

__initcall_start = .;

INITCALLS

__initcall_end = .;

}

}

4. 初始化.initcallxx.init里的函数

而这些SECTION里的函数在初始化时被顺序执行(init内核线程->do_basic_setup()[main.c#778]->do_initcalls())。

程序(init/main.c文件do_initcalls()函数)如下,do_initcalls()把.initcallXX.init中的函数按顺序都执行一遍。

for (call = __early_initcall_end; call < __initcall_end; call++)

do_one_initcall(*call);

=======================
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: