在移植Linux时,有个结构体需要填写,它以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);
在MACHINE_START与MACHINE_END之间还要填写一些参数,参照结构体注释小心填写即可,最好找个例子参考参考。