Machine_desc & boot & Kernel_init & initcall & module_init

跳转到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都是定义的宏,代码如下

/*
 * 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				\
};

这两个宏一起定义了一个类型为struct machine_desc的变量,结构体定义如下

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

这个类型的变量放在内核代码段.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);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值