跳转到start_kernel中,与本题相关的主要是setup_arch()和rest_init(),其中前者执行的较早,而后者做为start_kernel执行的最后一个函数。
函数start_kernel()和rest_init()定义在kernel/init/main.c中,函数setup_arch()定义在kernel/arch/arch_name/kernel/setup.c中。
1.setup_arch()在.init.text中,会执行machine_desc.init_very_early和machine_desc.init_early,代码如下,
- 915void __init setup_arch(char **cmdline_p)
- 916{
- 917 struct machine_desc *mdesc;
- 918
- 919 unwind_init();
- 920
- 921 setup_processor();
- 922 mdesc = setup_machine_fdt(__atags_pointer);
- 923 if (!mdesc)
- 924 mdesc = setup_machine_tags(machine_arch_type);
- 925 machine_desc = mdesc;
- 926 machine_name = mdesc->name;
- 927
- 928 if (mdesc->soft_reboot)
- 929 reboot_setup("s");
- 930
- 931 init_mm.start_code = (unsigned long) _text;
- 932 init_mm.end_code = (unsigned long) _etext;
- 933 init_mm.end_data = (unsigned long) _edata;
- 934 init_mm.brk = (unsigned long) _end;
- 935
- 936 /* populate cmd_line too for later use, preserving boot_command_line */
- 937 strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
- 938 *cmdline_p = cmd_line;
- 939
- 940 parse_early_param();
- 941
- 942 if (mdesc->init_very_early)
- 943 mdesc->init_very_early();
- 944
- 945 sanity_check_meminfo();
- 946 arm_memblock_init(&meminfo, mdesc);
- 947
- 948 paging_init(mdesc);
- 949 request_standard_resources(mdesc);
- 950
- 951 unflatten_device_tree();
- 952
- 953#ifdef CONFIG_SMP
- 954 if (is_smp())
- 955 smp_init_cpus();
- 956#endif
- 957 reserve_crashkernel();
- 958
- 959 cpu_init();
- 960 tcm_init();
- 961
- 962#ifdef CONFIG_MULTI_IRQ_HANDLER
- 963 handle_arch_irq = mdesc->handle_irq;
- 964#endif
- 965
- 966#ifdef CONFIG_VT
- 967#if defined(CONFIG_VGA_CONSOLE)
- 968 conswitchp = &vga_con;
- 969#elif defined(CONFIG_DUMMY_CONSOLE)
- 970 conswitchp = &dummy_con;
- 971#endif
- 972#endif
- 973 early_trap_init();
- 974
- 975 if (mdesc->init_early)
- 976 mdesc->init_early();
- 977}
2. rest_init()执行后,创建内核线程kernel_init,这也是内核第一个线程,kernel_init做的与本题相关的初始化是do_pre_smp_initcalls()和do_basic_setup(),然后变成用户态进程init。代码如下,
- 841static int __init kernel_init(void * unused)
- 842{
- 843 /*
- 844 * Wait until kthreadd is all set-up.
- 845 */
- 846 wait_for_completion(&kthreadd_done);
- 847 /*
- 848 * init can allocate pages on any node
- 849 */
- 850 set_mems_allowed(node_states[N_HIGH_MEMORY]);
- 851 /*
- 852 * init can run on any cpu.
- 853 */
- 854 set_cpus_allowed_ptr(current, cpu_all_mask);
- 855
- 856 cad_pid = task_pid(current);
- 857
- 858 smp_prepare_cpus(setup_max_cpus);
- 859
- 860 do_pre_smp_initcalls();
- 861 lockup_detector_init();
- 862
- 863 smp_init();
- 864 sched_init_smp();
- 865
- 866 do_basic_setup();
- 867
- 868 /* Open the /dev/console on the rootfs, this should never fail */
- 869 if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
- 870 printk(KERN_WARNING "Warning: unable to open an initial console.\n");
- 871
- 872 (void) sys_dup(0);
- 873 (void) sys_dup(0);
- 874 /*
- 875 * check if there is an early userspace init. If yes, let it do all
- 876 * the work
- 877 */
- 878
- 879 if (!ramdisk_execute_command)
- 880 ramdisk_execute_command = "/init";
- 881
- 882 if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
- 883 ramdisk_execute_command = NULL;
- 884 prepare_namespace();
- 885 }
- 886
- 887 /*
- 888 * Ok, we have completed the initial bootup, and
- 889 * we're essentially up and running. Get rid of the
- 890 * initmem segments and start the user-mode stuff..
- 891 */
- 892
- 893 init_post();
- 894 return 0;
- 895}
do_pre_smp_initcalls()做.initcallearly.init section函数的执行,早于.initcall0.init-.initcall7.init;在初始化完smp调度后,.initcalln.init函数在do_basic_setup()被调用。
do_pre_smp_initcalls()代码如下,
- 786static void __init do_pre_smp_initcalls(void)
- 787{
- 788 initcall_t *fn;
- 789
- 790 for (fn = __initcall_start; fn < __initcall0_start; fn++)
- 791 do_one_initcall(*fn);
- 792}
do_basic_setup()及调用.initcalln.init的代码如下,
- 743static void __init do_initcall_level(int level)
- 744{
- 745 extern const struct kernel_param __start___param[], __stop___param[];
- 746 initcall_t *fn;
- 747
- 748 strcpy(static_command_line, saved_command_line);
- 749 parse_args(initcall_level_names[level],
- 750 static_command_line, __start___param,
- 751 __stop___param - __start___param,
- 752 level, level,
- 753 repair_env_string);
- 754
- 755 for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
- 756 do_one_initcall(*fn);
- 757}
- 758
- 759static void __init do_initcalls(void)
- 760{
- 761 int level;
- 762
- 763 for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
- 764 do_initcall_level(level);
- 765}
- 766
- 767/*
- 768 * Ok, the machine is now initialized. None of the devices
- 769 * have been touched yet, but the CPU subsystem is up and
- 770 * running, and memory and process management works.
- 771 *
- 772 * Now we can finally start doing some real work..
- 773 */
- 774static void __init do_basic_setup(void)
- 775{
- 776 cpuset_init_smp();
- 777 usermodehelper_init();
- 778 shmem_init();
- 779 driver_init();
- 780 init_irq_proc();
- 781 do_ctors();
- 782 usermodehelper_enable();
- 783 do_initcalls();
- 784}
各.initcalln.init的宏定义如下
- 168/* initcalls are now grouped by functionality into separate
- 169 * subsections. Ordering inside the subsections is determined
- 170 * by link order.
- 171 * For backwards compatibility, initcall() puts the call in
- 172 * the device init subsection.
- 173 *
- 174 * The `id' arg to __define_initcall() is needed so that multiple initcalls
- 175 * can point at the same handler without causing duplicate-symbol build errors.
- 176 */
- 177
- 178#define __define_initcall(level,fn,id) \
- 179 static initcall_t __initcall_##fn##id __used \
- 180 __attribute__((__section__(".initcall" level ".init"))) = fn
- 181
- 182/*
- 183 * Early initcalls run before initializing SMP.
- 184 *
- 185 * Only for built-in code, not modules.
- 186 */
- 187#define early_initcall(fn) __define_initcall("early",fn,early)
- 188
- 189/*
- 190 * A "pure" initcall has no dependencies on anything else, and purely
- 191 * initializes variables that couldn't be statically initialized.
- 192 *
- 193 * This only exists for built-in code, not for modules.
- 194 */
- 195#define pure_initcall(fn) __define_initcall("0",fn,0)
- 196
- 197#define core_initcall(fn) __define_initcall("1",fn,1)
- 198#define core_initcall_sync(fn) __define_initcall("1s",fn,1s)
- 199#define postcore_initcall(fn) __define_initcall("2",fn,2)
- 200#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
- 201#define arch_initcall(fn) __define_initcall("3",fn,3)
- 202#define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)
- 203#define subsys_initcall(fn) __define_initcall("4",fn,4)
- 204#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
- 205#define fs_initcall(fn) __define_initcall("5",fn,5)
- 206#define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)
- 207#define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)
- 208#define device_initcall(fn) __define_initcall("6",fn,6)
- 209#define device_initcall_sync(fn) __define_initcall("6s",fn,6s)
- 210#define late_initcall(fn) __define_initcall("7",fn,7)
- 211#define late_initcall_sync(fn) __define_initcall("7s",fn,7s)
- 212
- 213#define __initcall(fn) device_initcall(fn)
- 214
- 215#define __exitcall(fn) \
- 216 static exitcall_t __exitcall_##fn __exit_call = fn
- 217
- 218#define console_initcall(fn) \
- 219 static initcall_t __initcall_##fn \
- 220 __used __section(.con_initcall.init) = fn
- 221
- 222#define security_initcall(fn) \
- 223 static initcall_t __initcall_##fn \
- 224 __used __section(.security_initcall.init) = fn
machine_desc.init_machine在customize_machine() @ kernel/arch/arm/kernel/setup.c中被调用,而customize_machine指针引用在.initcall3.init中。customize_machine()代码如下,
- 842static int __init customize_machine(void)
- 843{
- 844 /* customizes platform devices, or adds new ones */
- 845 if (machine_desc->init_machine)
- 846 machine_desc->init_machine();
- 847 return 0;
- 848}
- 849arch_initcall(customize_machine);
各module_init(fn)设备驱动在.initcall6.init中被调用,由以下module_init的宏定义可以看出,
- 259/**
- 260 * module_init() - driver initialization entry point
- 261 * @x: function to be run at kernel boot time or module insertion
- 262 *
- 263 * module_init() will either be called during do_initcalls() (if
- 264 * builtin) or at module insertion time (if a module). There can only
- 265 * be one per module.
- 266 */
- 267#define module_init(x) __initcall(x);
而__initcall(x)宏定义为
- 213#define __initcall(fn) device_initcall(fn)
综上,题中提及各初始化的顺序是machine_desc.init_very_early, machine_desc.init_early, .initcallearly.init, machine_desc.init_machine(.initcall3.init), module_init(.initcall6.init) 。
[End]
[参考文章]
http://blog.csdn.net/paomadi/article/details/8611408
一、定义
- #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 \
- };
MACHINE_START和MACHINE_END框起了一个machine_desc结构体的声明并根据MACHINE_START宏的参数初始化其.nr和.name成员
并将该结构体标记编译到.arch.info.init段
在MACHINE_START和MACHINE_END宏之间可以初始化machine_desc结构体的剩余成员
machine_desc结构体的定义
- struct machine_desc {
- unsigned int nr; /* architecture number 编号 */
- const char *name; /* architecture name 名字 */
- unsigned long boot_params; /* tagged list */
- unsigned int nr_irqs; /* number of IRQs 中断数 */
- unsigned int video_start; /* start of video RAM */
- unsigned int video_end; /* end of video RAM */
- unsigned int reserve_lp0 :1; /* never has lp0 */
- unsigned int reserve_lp1 :1; /* never has lp1 */
- unsigned int reserve_lp2 :1; /* never has lp2 */
- unsigned int soft_reboot :1; /* soft reboot */
- void (*fixup)(struct machine_desc *,struct tag *, char **,struct meminfo *);
- void (*reserve)(void); /* reserve mem blocks */
- void (*map_io)(void); /* IO mapping function io映射函数 */
- void (*init_irq)(void); /* 中断初始化函数 */
- struct sys_timer *timer; /* system tick timer 滴答定时器 */
- void (*init_machine)(void); /* 初始化函数 */
- };
使用例子:
- MACHINE_START(SMDKC110, "SMDKC110")
- /* Maintainer: Kukjin Kim <kgene.kim@samsung.com> */
- .boot_params = S5P_PA_SDRAM + 0x100,
- .init_irq = s5pv210_init_irq,
- .map_io = smdkc110_map_io,
- .init_machine = smdkc110_machine_init,
- .timer = &s3c24xx_timer,
- MACHINE_END
这里smdkc110_machine_init就是对应的板级初始化函数,s5pv210_init_irq就是板级中断初始化函数,smdkc110_map_io就是板级io初始化函数...
二、调用关系
MACHINE_START宏将machine_desc标记编译到.arch.info.init段, 而/arch/arm/kernel/vmlinux.lds中
- __arch_info_begin = .;
- *(.arch.info.init)
- __arch_info_end = .;
当系统启动时在linux启动函数start_kernel中调用了setup_arch(&command_line);
- void __init setup_arch(char **cmdline_p)
- {
- struct tag *tags = (struct tag *)&init_tags;
- struct machine_desc *mdesc; //声明了一个machine_desc结构体指针
- char *from = default_command_line;
- init_tags.mem.start = PHYS_OFFSET;
- unwind_init();
- setup_processor();
- mdesc = setup_machine(machine_arch_type); //0根据machine_arch_type获取machine_desc
- machine_name = mdesc->name; //设置名字
- if (mdesc->soft_reboot) //需要软重启?
- reboot_setup("s");
- if (__atags_pointer)
- tags = phys_to_virt(__atags_pointer);
- else if (mdesc->boot_params) { //处理启动参数
- #ifdef CONFIG_MMU
- if (mdesc->boot_params < PHYS_OFFSET ||
- mdesc->boot_params >= PHYS_OFFSET + SZ_1M) {
- printk(KERN_WARNING"Default boot params at physical 0x%08lx out of reach\n",mdesc->boot_params);
- } else
- #endif
- {
- tags = phys_to_virt(mdesc->boot_params);
- }
- }
- #if defined(CONFIG_DEPRECATED_PARAM_STRUCT)
- if (tags->hdr.tag != ATAG_CORE)
- convert_to_tag_list(tags);
- #endif
- if (tags->hdr.tag != ATAG_CORE)
- tags = (struct tag *)&init_tags;
- if (mdesc->fixup) //若存在fixup方法则调用其方法
- mdesc->fixup(mdesc, tags, &from, &meminfo);
- if (tags->hdr.tag == ATAG_CORE) {
- if (meminfo.nr_banks != 0)
- squash_mem_tags(tags);
- save_atags(tags);
- parse_tags(tags);
- }
- init_mm.start_code = (unsigned long) _text;
- init_mm.end_code = (unsigned long) _etext;
- init_mm.end_data = (unsigned long) _edata;
- init_mm.brk = (unsigned long) _end;
- /* parse_early_param needs a boot_command_line */
- strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);
- /* populate cmd_line too for later use, preserving boot_command_line */
- strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
- *cmdline_p = cmd_line;
- parse_early_param();
- arm_memblock_init(&meminfo, mdesc); //这里可能会调用reserve方法
- paging_init(mdesc); //->devicemaps_init(mdesc)->map_io方法
- request_standard_resources(&meminfo, mdesc); //这里可能会调用video_start方法
- #ifdef CONFIG_SMP
- if (is_smp())
- smp_init_cpus();
- #endif
- reserve_crashkernel();
- cpu_init();
- tcm_init();
- arch_nr_irqs = mdesc->nr_irqs; //1设置全局变量 中断个数
- init_arch_irq = mdesc->init_irq; //2设置全局变量 中断初始化函数
- system_timer = mdesc->timer; //3设置全局变量 sys_timer结构体
- init_machine = mdesc->init_machine; //4设置全局变量 板级初始化函数
- #ifdef CONFIG_VT
- #if defined(CONFIG_VGA_CONSOLE)
- conswitchp = &vga_con;
- #elif defined(CONFIG_DUMMY_CONSOLE)
- conswitchp = &dummy_con;
- #endif
- #endif
- early_trap_init();
- }
0. 结构machine_desc安装
- static struct machine_desc * __init setup_machine(unsigned int nr)
- {
- extern struct machine_desc __arch_info_begin[], __arch_info_end[];
- struct machine_desc *p;
- for (p = __arch_info_begin; p < __arch_info_end; p++)//遍历__arch_info_begin和__arch_info_end之间的machine_desc结构体
- if (nr == p->nr) { //找到对应的板
- printk("Machine: %s\n", p->name);//打印板级信息
- return p;
- }
- early_print("\n"
- "Error: unrecognized/unsupported machine ID (r1 = 0x%08x).\n\n"
- "Available machine support:\n\nID (hex)\tNAME\n", nr);
- for (p = __arch_info_begin; p < __arch_info_end; p++)
- early_print("%08x\t%s\n", p->nr, p->name);
- early_print("\nPlease check your kernel config and/or bootloader.\n");
- while (true)
- /* can't use cpu_relax() here as it may require MMU setup */;
- }
1.中断个数
start_kernel->early_irq_init->arch_probe_nr_irqs函数中nr_irqs = arch_nr_irqs ? arch_nr_irqs : NR_IRQS;设置全局nr_irqs变量
2.中断初始化函数
start_kernel->init_IRQ->init_arch_irq()
3.sys_timer结构体
start_kernel->time_init()调用system_timer->init()方法既sys_timer->init()
4.板级初始化函数
- static void (*init_machine)(void) __initdata;
- static int __init customize_machine(void)
- {
- /* customizes platform devices, or adds new ones */
- if (init_machine)//全局函数init_machine存在
- init_machine();//则调用,既mdesc->init_machine()
- return 0;
- }
- arch_initcall(customize_machine);//用arch_initcall修饰customize_machine函数
arch_iniitcall函数在/include/linux/init.h中定义
- #define arch_initcall(fn) __define_initcall("3",fn,3)
__define_initcall的定义
- #define __define_initcall(level,fn,id) \
- static initcall_t __initcall_##fn##id __used \
- __attribute__((__section__(".initcall" level ".init"))) = fn
展开就是static initcall_t __initcall_customize_machine3 __used __attribute__((__section__(".initcall3.init")))=customize_machine
在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 = .;
标注为.initcall3.init的函数编译进__initcall_start和__initcall_end框起的section中
而在系统启动的时候start_kernel->rest_init()->kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);创建了kernel线程
kernel_init->do_pre_smp_initcalls()
- static void __init do_pre_smp_initcalls(void)
- {
- initcall_t *fn;
- for (fn = __initcall_start; fn < __early_initcall_end; fn++)
- do_one_initcall(*fn);
- }
该函数遍历__initcall_start和__early_initcall_end中的函数,并调用do_one_initcall
- int __init_or_module do_one_initcall(initcall_t fn)
- {
- int count = preempt_count();
- int ret;
- if (initcall_debug)
- ret = do_one_initcall_debug(fn);
- else
- ret = fn();//执行了fn函数也就是customize_machine
- msgbuf[0] = 0;
- if (ret && ret != -ENODEV && initcall_debug)
- sprintf(msgbuf, "error code %d ", ret);
- if (preempt_count() != count) {
- strlcat(msgbuf, "preemption imbalance ", sizeof(msgbuf));
- preempt_count() = count;
- }
- if (irqs_disabled()) {
- strlcat(msgbuf, "disabled interrupts ", sizeof(msgbuf));
- local_irq_enable();
- }
- if (msgbuf[0]) {
- printk("initcall %pF returned with %s\n", fn, msgbuf);
- }
- return ret;
- }
http://blog.csdn.net/cxw3506/article/details/8475965
Machine定义以MACHINE_START开始并以MACHINE_END结束,如下mini2440开发板的移植为示例
- MACHINE_START(MINI2440, "MINI2440")
- .phys_io = S3C2410_PA_UART,
- .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
- .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都是定义的宏,代码如下
- <span style="font-size:12px;">/*
- * Set of macros to define architecture features. This is built into
- * a table by the linker.
- */
- #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 \
- };</span>
这两个宏一起定义了一个类型为struct machine_desc的变量,结构体定义如下
- <span style="font-size:12px;">struct machine_desc {
- /*
- * Note! The first four elements are used
- * by assembler code in head.S, head-common.S
- */
- unsigned int nr; /* architecture number */
- unsigned int phys_io; /* start of physical io */
- unsigned int io_pg_offst; /* byte offset for io
- * page tabe entry */
- const char *name; /* architecture name */
- unsigned long boot_params; /* tagged list */
- unsigned int video_start; /* start of video RAM */
- unsigned int video_end; /* end of video RAM */
- unsigned int reserve_lp0 :1; /* never has lp0 */
- unsigned int reserve_lp1 :1; /* never has lp1 */
- unsigned int reserve_lp2 :1; /* never has lp2 */
- unsigned int soft_reboot :1; /* soft reboot */
- void (*fixup)(struct machine_desc *,
- struct tag *, char **,
- struct meminfo *);
- void (*map_io)(void);/* IO mapping function */
- void (*init_irq)(void);
- struct sys_timer *timer; /* system tick timer */
- void (*init_machine)(void);
- };</span>
这个类型的变量放在内核代码段.arch.info.init中,在内核运行初期,被函数lookup_machine_type(此函数用汇编实现,在汇编文件中)取出,读取流程为
Start_kernel() -> setup_arch() -> setup_machine() -> lookup_machine_type()
在函数setup_machine()中,利用这个结构体类型的变量初始化一些全局变量,以备内核运行时使用,比如
init_arch_irq = mdesc->init_irq;
system_timer = mdesc->timer;
init_machine = mdesc->init_machine;
这个结构体中,成员init_machine保存的是开发板资源注册的初始化代码,init_irq保存的是中断初始化指针,timer保存的是一个struct sys_timer类型的指针…..如果我们要给自己的开发板定制内核,那么我们必须自己实现以上成员函数,其中函数init_machine()是我们向内核传递开发板设备信息的重要的常规途径,分析mini2440开发板内核移植代码知道,在这个函数中,注册了开发板所用到的所有设备的相关硬件信息!
- static void __init mini2440_init(void)
- {
- struct mini2440_features_t features = { 0 };
- int i;
- printk(KERN_INFO "MINI2440: Option string mini2440=%s\n",
- mini2440_features_str);
- /* Parse the feature string */
- mini2440_parse_features(&features, mini2440_features_str);
- /* turn LCD on */
- s3c_gpio_cfgpin(S3C2410_GPC(0), S3C2410_GPC0_LEND);
- /* Turn the backlight early on */
- WARN_ON(gpio_request(S3C2410_GPG(4), "backlight"));
- gpio_direction_output(S3C2410_GPG(4), 1);
- /* remove pullup on optional PWM backlight -- unused on 3.5 and 7"s */
- s3c_gpio_setpull(S3C2410_GPB(1), S3C_GPIO_PULL_UP);
- s3c2410_gpio_setpin(S3C2410_GPB(1), 0);
- s3c_gpio_cfgpin(S3C2410_GPB(1), S3C2410_GPIO_INPUT);
- /* Make sure the D+ pullup pin is output */
- WARN_ON(gpio_request(S3C2410_GPC(5), "udc pup"));
- gpio_direction_output(S3C2410_GPC(5), 0);
- /* mark the key as input, without pullups (there is one on the board) */
- for (i = 0; i < ARRAY_SIZE(mini2440_buttons); i++) {
- s3c_gpio_setpull(mini2440_buttons[i].gpio, S3C_GPIO_PULL_UP);
- s3c_gpio_cfgpin(mini2440_buttons[i].gpio, S3C2410_GPIO_INPUT);
- }
- if (features.lcd_index != -1) {
- int li;
- mini2440_fb_info.displays =
- &mini2440_lcd_cfg[features.lcd_index];
- printk(KERN_INFO "MINI2440: LCD");
- for (li = 0; li < ARRAY_SIZE(mini2440_lcd_cfg); li++)
- if (li == features.lcd_index)
- printk(" [%d:%dx%d]", li,
- mini2440_lcd_cfg[li].width,
- mini2440_lcd_cfg[li].height);
- else
- printk(" %d:%dx%d", li,
- mini2440_lcd_cfg[li].width,
- mini2440_lcd_cfg[li].height);
- printk("\n");
- s3c24xx_fb_set_platdata(&mini2440_fb_info);
- }
- s3c24xx_udc_set_platdata(&mini2440_udc_cfg);
- s3c24xx_mci_set_platdata(&mini2440_mmc_cfg);
- s3c_nand_set_platdata(&mini2440_nand_info);
- s3c_i2c0_set_platdata(NULL);
- i2c_register_board_info(0, mini2440_i2c_devs,
- ARRAY_SIZE(mini2440_i2c_devs));
- platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));
- if (features.count) /* the optional features */
- platform_add_devices(features.optional, features.count);
- }
那么成员函数init_machine什么时候被调用呢?
在函数setup_machine()中有一条语句init_machine = mdesc->init_machine;其中init_machine为全局函数指针变量,此变量在函数customize_machine()中被调用,代码如下所示:
- static int __init customize_machine(void)
- {
- /* customizes platform devices, or adds new ones */
- if (init_machine)
- init_machine();
- return 0;
- }
- arch_initcall(customize_machine);