linux移植的重要数据结构

本文介绍嵌入式Linux内核移植的基本概念和技术细节,包括如何针对不同硬件平台进行内核配置与编译,以及如何利用现有目标板作为模板进行开发。

对于嵌入式 Linux 系统来说,有各种体系结构的处理器和硬件平台,并且用户需要根据需求自己定制硬件板。只要是硬件平台有些变化,即使非常小,可能也需要做一些移植工作。内核移植是嵌入式 Linux 系统中最常见的一项工作。

内核移植工作主要是修改跟硬件平台相关的代码,一般不涉及 Linux 内核通用的程序

移植的难度也取决于两种硬件平台的差异。 Linux 对于特定的硬件平台的软件就叫作 BSP

Board Support Package )。

由于 Linux 内核具备可移植性的特点,并且已经支持了各种体系结构的很多种目标板,

我们很容易从中找到跟自己硬件类似的目标板。参考内核已经支持的目标板来移植 BSP ,就

如同使用模板开发程序。

因此,移植 linux 内核的过程大多数情况下就是移植 BSP 的过程。三星公司提供了 smdk24xx 开发板的 BSP 。对于 mini2440 开发板来说,移植 linux 内核,只要修改 smdk24xx 开发板的 BSP 使该 linux 支持 mini2440 开发板就可以了。

linux 内核源代码的 ARCH 目录存放的是体系结构相关的代码,对于每个架构的 CPU arm 目录下都有一个对应的目录,比如 arch/arm arch/i386 。而 arm 架构的处理器种类又有很多,所以,在 arch/arm 目录下对于每种 arm 架构处理器也有一个对应的子目录,比如 arch/arm/mach-s3c2440 arch/arm/mach-s3c2410 等。在 arch/arm 目录下有一个 plat-s3c24xx 目录,根据目录名它应该是与 s3c24xx 系列处理器的平台设备相关的一个目录。注意,所谓的 平台设备 并不是与字符设备、块设备和网络设备并列的概念,而是 linux 系统描述设备的一个附加手段。在 plat-s3c24xx 目录下有一个 common-smdk.c 文件,根据文件名,它应该是三星公司的 smdk24xx 系列开发板都需要的一个文件。在移植驱动的时候经常需要修改 arch/arm/plat-s3c24xx/common-smdk.c 文件。对于 arch/arm/mach-s3c2440 目录,它是专门用来保存 S3C2410 系列处理器平台相关程序,其中 Kconfig Makefile 是用于内核配置编译的。其他文件分为 2 类,一类是处理器通

用的,例如: clock.c clock.h cpu.c cpu.h s3c2410.c s3c2410.h 等;另一类是目标板相关的,例如: bast.h bast-irq.c mach-bast.c 等。在这些文件中,实现了处理器和目标板相关的一些定义和初始化函数。还有些相关的定义包含在 include/asm-arm/arch-s3c2410/ 下的头文件中。

    linux 内核中对于每种支持的开发板都会使用宏 MACHINE_START MACHINE_END 来定义一个 machine_desc 结构。 MACHINE_START MCHINE_END 的定义如下:

1 /*include/asm-arm/mach/arch.h */

#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             /

};

arch/arm/mach-s3c2410/mach-smdk2440.c 中可以找到SMDK2440 开发板的定义如下:

 

MACHINE_START(S3C2440, "SMDK2440")

    .phys_io    = S3C2410_PA_UART,

    .io_pg_offst    = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,

    .boot_params    = S3C2410_SDRAM_PA + 0x100,

 

    .init_irq   = s3c24xx_init_irq,

    .map_io     = smdk2440_map_io,

    .init_machine   = smdk2440_machine_init,

    .timer      = &s3c24xx_timer,

MACHINE_END

MACHINE_STARTMACHINE_END 扩展开来就是定义了一个名为__mach_desc_S3C2440 的结构体变量:

const struct machine_desc __mach_desc_S3C2440 __used

 __attribute__((__section__(".arch.info.init"))) =

{  

    .nr    = MACH_TYPE_S3C2440,             // 开发板的机器类型ID

    .name    = "SMDK2440",                   // 开发板名称

    .phys_io    = S3C2410_PA_UART,              // 起始IO 物理地址

    .io_pg_offst    = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,

    .boot_params    = S3C2410_SDRAM_PA + 0x100, // 内核启动参数的地址

    .init_irq   = s3c24xx_init_irq,         // 中断初始化函数

    .map_io     = smdk2440_map_io,          //IO 映射函数(在这里修改时钟频率)

    .init_machine   = smdk2440_machine_init,

    .timer      = &s3c24xx_timer,    

};

MACH_TYPE_S3C2440 可以看作是系统平台号,它包含在include/asm- arm/mach-types.h 头文件中,不过这个头文件是在配置内核或编译内核时自动生成的,所以不能更改。。真正系统平台号

的定义位置在arch/arm/tools/mach-types 文件中。

# machine_is_xxx        CONFIG_xxxx      MACH_TYPE_xxx       number

s3c2440          ARCH_S3C2440           S3C2440          362

arch/arm/tools/mach-types 中每一行定义一个系统平台号。 “machine_is_xxx ”是用来判断当前的平台号是否正确的函数; “CONFIG_xxxx ”是在内核配置时生成的; “MACH_TYPE_xxx

是系统平台号的定义; “number ”是系统平台的值。 __mach_desc_S3C2440 结构体中的函数将在内核启动过程中,完成系统平台的初始化工作

    对于具有相同处理器的系统平台,并不需要对每一个平台都编写一个BSP ,如果他们的外围接口电路基本相同,也许只需修改一些数据的定义,修改几个函数的参数就可以了。

 

 

2 linux 设计了一个通用的数据结构 resource 来描述各种 I/O 资源(比如, IO 端口, DMA ,中断等)

include/linux/ioport.h

struct resource

{
 resource_size_t start;
 resource_size_t end;
 const char *name;
 unsigned long flags;
 struct resource *parent, *sibling, *child;
};

flags :资源标记,用于标识各种资源,例如 IORESOURCE_MEM 表示内存资源, IORESOURCE_IRQ 表示中断资源

对于内存资源, start 表示内存起始物理地址 ,end: 表示内存末尾物理地址

对于中断资源, start 表示起始中断号, end 表示最后一个中断号

常用资源数组来表示一个设备所拥有的各类资源,比如 s3c2440 的片内 LCD 控制器拥有的资源如下

static struct resource s3c_lcd_resource[]=

{

[0]={

    .start=S3C24XX_PA_LCD,

    .end=S3C24XX_PA_LCD+S3C24XX_SZ_LCD-1,

    .flags=IORESOURCE_MEM,

}

[1]={

    .start=IRQ_LCD,

    .end=IRQ_LCD,

    .flags=IORESOURCE_IRQ,

    }

};

其中 S3C24XX_PA_LCD 被定义为 0x4D000000 S3C24XX_SZ_LCD 被定义为 1M 。所以,在这里给 LCD 控制器分配的物理地址空间范围为 0x4D000000 0x4D0FFFFF ,这些是 LCD 控制器各寄存器使用的地址,但实际上 LCD 控制器的寄存器地址的范围为 0x4D000000~0x4D000060, 使用 0x4D000000 0x4D000060 给他们赋值也应该是可以的。 IRQ_LCD 算得是 32 ,它会将 GPG4 引脚设为 LCD_PWREN 功能,因为 GPG4 LCD_PWREN/EINT12 复用。

 

3 )在内核文件 include/linux/platform_device.h 中,定义了两个数据结构来表示设备和驱动程序: platform_device 结构用来描述设备的名称、 ID 、所占用的资源(比如内存地址 / 大小、中断号)等; platform_driver 结构用来描述各种操作函数,比如枚举函数、移除设备函数、驱动名称等。

 

 

// 平台设备

struct platform_device     

{

    const char * name;         // 设备名

    int     id;

    struct device   dev;

    u32     num_resources;      //   设备所使用的各类资源数量

    struct resource * resource; //   设备的资源数组

    struct platform_device_id   *id_entry;

    struct pdev_archdata    archdata;

};

// 平台驱动

struct platform_driver     

{

    int (*probe)(struct platform_device *);     // 探测

    int (*remove)(struct platform_device *);        // 移除

    void (*shutdown)(struct platform_device *); // 关闭

    int (*suspend)(struct platform_device *, pm_message_t state);// 挂起

    int (*resume)(struct platform_device *);        // 恢复

    // 描述驱动的名称(name) 和属主(owner) 等信息

    struct device_driver driver;                   

    struct platform_device_id *id_table;

};

内核启动后,首先构造链表将描述设备的platform_device 构造组织起来,得到一个设备的列表;当加载某个驱动程序的platform_driver 结构时,使用一些匹配函数来检查驱动程序能否支持这些设备,常用的检查方法很简单:比较驱动程序和设备的名称。

S3C2440 开发板为例,在arch/arm/mach-s3c2440/mach-smdk2440.c 中定义了如下设备:

static struct platform_device *smdk2440_devices[] __initdata =

{

    &s3c_device_usb,        //USB 控制器

    &s3c_device_lcd,        //LCD 控制器

    &s3c_device_wdt,        // 看门狗

    &s3c_device_i2c,       

    &s3c_device_iis,       

};

arch/arm/plat-s3c24xx/common-smdk.c 中定义了如下设备:

static struct platform_device __initdata *smdk_devs[] =

{

    &s3c_device_nand,   //NAND FLASH

    &smdk_led4,

    &smdk_led5,

    &smdk_led6,

    &smdk_led7,

};

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值