嵌入式linux开发uboot移植(六)——uboot环境变量

嵌入式linux开发uboot移植(六)——uboot环境变量

一、uboot环境变量简介

    u-boot的缺省情况下会有一些基本的环境变量,当执行saveenv时,环境变量会保存到flash存储设备中。如果环境变量的值为空,则uboot会使用uboot代码中的值;如果环境变量不为空,则优先使用环境变量的值。默认环境变量在uboot源码中common/Env_common.c文件中。

 

  1. uchar default_environment[CFG_ENV_SIZE] = {

  2. #ifdef CONFIG_BOOTARGS

  3. "bootargs="CONFIG_BOOTARGS"\0"

  4. #endif

  5. #ifde fCONFIG_BOOTCOMMAND

  6. "bootcmd="CONFIG_BOOTCOMMAND"\0"

  7. #endif

  8. #ifdef CONFIG_MTDPARTITION

  9. "mtdpart="CONFIG_MTDPARTITION"\0"

  10. #endif

  11. #ifdef CONFIG_RAMBOOTCOMMAND

  12. "ramboot="CONFIG_RAMBOOTCOMMAND"\0"

  13. #endif

  14. #ifdef CONFIG_NFSBOOTCOMMAND

  15. "nfsboot="CONFIG_NFSBOOTCOMMAND"\0"

  16. #endif

  17. #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)

  18. "bootdelay="MK_STR(CONFIG_BOOTDELAY)"\0"

  19. #endif

  20. #if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0)

  21. "baudrate="MK_STR(CONFIG_BAUDRATE)"\0"

  22. #endif

  23. #ifdef CONFIG_ETHADDR

  24. "ethaddr="MK_STR(CONFIG_ETHADDR)"\0"

  25. #endif

  26. #ifdef CONFIG_IPADDR

  27. "ipaddr="MK_STR(CONFIG_IPADDR)"\0"

  28. #endif

  29. #ifdef CONFIG_SERVERIP

  30. "serverip="MK_STR(CONFIG_SERVERIP)"\0"

  31. #endif

  32. #ifdef CONFIG_GATEWAYIP

  33. "gatewayip="MK_STR(CONFIG_GATEWAYIP)"\0"

  34. #endif

  35. #ifdef CONFIG_NETMASK

  36. "netmask="MK_STR(CONFIG_NETMASK)"\0"

  37. #endif

  38. #ifdef CONFIG_HOSTNAME

  39. "hostname="MK_STR(CONFIG_HOSTNAME)"\0"

  40. #endif

  41. #ifdef  CONFIG_EXTRA_ENV_SETTINGS

  42. CONFIG_EXTRA_ENV_SETTINGS

  43. #endif

  44. "\0"

  45. };

    所有的环境变量存储在一个16KB大小的一维数组中,每个环境变量以"\0"结束。uboot在启动时,在遍历调用执行init_sequence函数指针数组中的env_init函数时已经对环境变量进行校验,通过调用env_relocate函数,将环境变量从Flash启动设备重定位到SDRAM中。uboot启动时环境变量的初始化如下:

嵌入式linux开发uboot移植(六)——uboot环境变量

环境变量的使用示例:

setenv  bootcmd "nand read 0x20008000 0x80000 0x500000;bootm 0x30008000"

saveenv

常见的uboot环境变量:

bootdelay    执行自动启动的等候秒数
 baudrate    串口控制台的波特率
 netmask     以太网接口的掩码
 ethaddr     以太网卡的网卡物理地址
 bootfile    缺省的下载文件
 bootargs    传递给内核的启动参数
 bootcmd     自动启动时执行的命令
 serverip    服务器端的ip地址
 ipaddr     本地ip 地址
 stdin      标准输入设备
 stdout     标准输出设备
 stderr     标准出错设备

 

二、环境变量操作的源码分析

    在对环境变量的操作中,u-boot调用的对环境变量操作的通用函数接口位于common/env_common.c文件,环境变量存储在不同的启动设备时的操作函数接口存放在不同的文件,如common/env_epprom.c、common/env_flash.c、common/env_nand.c,对环境变量的操作函数接口存放在common/env_nvedit.c。

1、printenv

 

  1. int do_printenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])

  2. {

  3. int i, j, k, nxt;

  4. int rcode = 0;

  5. if (argc == 1) {//printenv命令没有参数时打印出所有变量信息

  6. for (i=0; env_get_char(i) != '\0'; i=nxt+1) {//遍历环境变量数组中的变量

  7. for (nxt=i; env_get_char(nxt) != '\0'; ++nxt)//遍历变量获取字符数量

  8. ;

  9. for (k=i; k<nxt; ++k)遍历打印出变量的信息

  10. putc(env_get_char(k));

  11. putc  ('\n');//变量信息输出完毕打印换行

  12. if (ctrlc()) {

  13. puts ("\n ** Abort\n");

  14. return 1;

  15. }

  16. }

  17. printf("\nEnvironment size: %d/%ld bytes\n",

  18. i, (ulong)ENV_SIZE);

  19. return 0;

  20. }

  21. for (i=1; i<argc; ++i) {//遍历打印单个或多个变量的信息

  22. char *name = argv[i];

  23. k = -1;

  24. for (j=0; env_get_char(j) != '\0'; j=nxt+1) {//遍历打印的多个环境变量

  25. for (nxt=j; env_get_char(nxt) != '\0'; ++nxt)//遍历计算变量的字符数

  26. ;

  27. k = envmatch((uchar *)name, j);//查找变量是否存在

  28. if (k < 0) {

  29. continue;

  30. }

  31. puts (name);

  32. putc ('=');

  33. while (k < nxt)//打印变量信息

  34. putc(env_get_char(k++));

  35. putc ('\n');

  36. break;

  37. }

  38. if (k < 0) {

  39. printf ("## Error: \"%s\" not defined\n", name);

  40. rcode ++;

  41. }

  42. }

  43. return rcode;

  44. }

2、setenv

 

  1. int do_setenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])

  2. {

  3. if (argc < 2) {//setenv命令无参数输入处理情况

  4. printf ("Usage:\n%s\n", cmdtp->usage);

  5. return 1;

  6. }

  7. return _do_setenv (flag, argc, argv);

  8. }

3、saveenv

 

  1. int do_saveenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])

  2. {

  3. extern char * env_name_spec;

  4. printf ("Saving Environment to %s...\n", env_name_spec);

  5. return (saveenv() ? 1 : 0);

  6. }

  7. int saveenv(void)

  8. {

  9. movi_write_env(virt_to_phys((ulong)env_ptr));//将所列环境变量写入SD卡

  10. puts ("done\n");

  11. return 1;

  12. }

4、字符串解析

uboot中对环境变量的操作充分利用了对环境变量字符串的解析。本部分将字符串的解析提取出来进行分析。

环境变量存储在default_environment[CFG_ENV_SIZE]数组中,每个环境变量以"\0"结束。通过对环境变量数组default_environment[CFG_ENV_SIZE]进行解析,将环境变量分别打印出来。

实验源码:

 

  1. #include <stdio.h>

  2. unsigned char environment[] = 

  3. {

  4.     "bootdelay="    "10"    "\0"

  5.     "ipaddr="   "192.168.1.210" "\0"

  6.     "serverip=" "192.168.1.200" "\0"

  7.     "bootcmd="  "tftp 0x20008000 uImage;bootm 0x20008000"   "\0"

  8.     "baudrate=" "115200"    "\0"

  9. };

  10. int main(int argc, char **argv)

  11. {

  12.     int i, s,k;

  13.     for(i = 0; *(environment+i) != '\0'; i = s + 1) //按照变量遍历环境变量数组

  14.     {   //

  15.         for(s = i; *(environment + s) != '\0'; s++)//计算出变量的字符数量

  16.             ;   

  17.         for(k = i; k < s; k++)//打印出变量的信息

  18.             putc( *(environment + k), stdout);

  19.         putc('\n', stdout);

  20.     }   

  21.     return 0;

  22. }

编译运行结果:

bootdelay=10

ipaddr=192.168.1.210

serverip=192.168.1.200

bootcmd=tftp 0x20008000 uImage;bootm 0x20008000

baudrate=115200

三、uboot中的SD/MMC设备驱动

1、uboot中的SD/MMC设备的初始化

    uboot在启动的第二阶段BL2的start_armboot函数中在对开发板初始化部分使用mmc_initialize函数对SD/MMC设备进行初始化,初始化分为两部分:

 

A、cpu_mmc_init

cpu_mmc_init函数完成对SoC内部SD/MMC控制器的初始化,包括使用setup_hsmmc_clock函数初始化SD/MMC控制器的时钟,setup_hsmmc_cfg_gpio函数初始化SoC中SD/MMC控制器的GPIO引脚。

返回时调用了smdk_s3c_hsmmc_init函数,smdk_s3c_hsmmc_init根据SD/MMC通道调用函数s3c_hsmmc_initialize初始化struct mmc结构体实例的属性和操作方法。struct mmc结构体实例的操作方法指定了SD/MMC设备操作硬件设备的方法函数。

 

B、mmc_init

mmc_init函数完成对SD/MMC硬件设备的初始化。mmc_init函数内部通过调用mmc_go_idle、mmc_send_if_cond、mmc_send_app_op_cond、mmc_send_cmd等函数最终调用struct mmc结构体实例中操作方法send_cmd、set_ios、init挂接的s3c_hsmmc_send_command、s3c_hsmmc_set_ios、s3c_hsmmc_init硬件操作函数,最终完成对SD/MMC硬件设备的初始化。

SD/MMC设备驱动stuct mmc结构体封装了SD/MMC设备的属性和操作方法。

 

  1. struct mmc {

  2. struct list_head link;

  3. char name[32];

  4. void *priv;

  5. uint voltages;

  6. uint version;

  7. uint f_min;

  8. uint f_max;

  9. int high_capacity;

  10. uint bus_width;

  11. uint clock;

  12. uint card_caps;

  13. uint host_caps;

  14. uint ocr;

  15. uint scr[2];

  16. uint csd[4];

  17. uint cid[4];

  18. ushort rca;

  19. uint tran_speed;

  20. uint read_bl_len;

  21. uint write_bl_len;

  22. u32 capacity;

  23. struct mmc_ext_csdext_csd;/* mmc v4 extended card specific */

  24. block_dev_desc_t block_dev;

  25. int (*send_cmd)(struct mmc *mmc,

  26. struct mmc_cmd *cmd, struct mmc_data *data);

  27. void (*set_ios)(struct mmc *mmc);

  28. int (*init)(struct mmc *mmc);

  29. };

SD/MMC设备驱动构建就是对SD/MMC设备结构体struct mmc类型实例的初始化。

 

2、uboot中对SD/MMC设备的操作

saveenv命令保存环境变量到Flash设备时需要调用驱动程序操作Flash设备。uboot硬件驱动程序是从linux kernel移植的。saveenv的命令函数为do_saveenv。saveenv命令保存环境变量的工作流程如下:

A、调用common/cmd_nvedit.c中do_saveenv函数

int do_saveenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])

{

extern char * env_name_spec;

printf ("Saving Environment to %s...\n", env_name_spec);

return (saveenv() ? 1 : 0);

}

B、调用common/env_auto.c文件中saveenv函数:

 

  1. int saveenv(void)

  2. {

  3. #if defined(CONFIG_S5PC100) || defined(CONFIG_S5PC110) || defined(CONFIG_S5P6442)

  4. if (INF_REG3_REG == 2)

  5. saveenv_nand();//环境变量保存到nand设备

  6. else if (INF_REG3_REG == 3)

  7. saveenv_movinand();//环境变量保存到SD/MMC设备

  8. else if (INF_REG3_REG == 1)

  9. saveenv_onenand();

  10. else if (INF_REG3_REG == 4)

  11. saveenv_nor();

  12. #elifdefined(CONFIG_SMDK6440)

  13. if (INF_REG3_REG == 3)

  14. saveenv_nand();

  15. else if (INF_REG3_REG == 4 || INF_REG3_REG == 5 || INF_REG3_REG == 6)

  16. saveenv_nand_adv();

  17. else if (INF_REG3_REG == 0 || INF_REG3_REG == 1 || INF_REG3_REG == 7)

  18. saveenv_movinand();

  19. #else   // others

  20. if (INF_REG3_REG == 2 || INF_REG3_REG == 3)

  21. saveenv_nand();

  22. else if (INF_REG3_REG == 4 || INF_REG3_REG == 5 || INF_REG3_REG == 6)

  23. saveenv_nand_adv();

  24. else if (INF_REG3_REG == 0 || INF_REG3_REG == 7)

  25. saveenv_movinand();

  26. else if (INF_REG3_REG == 1)

  27. saveenv_onenand();

  28. #endif

  29. else

  30. printf("Unknown boot device\n");

  31. return 0;

  32. }


 

C、调用common/env_auto.c文件中saveenv_movinand函数

int saveenv_movinand(void)

{

#if defined(CONFIG_CMD_MOVINAND)

        movi_write_env(virt_to_phys((ulong)env_ptr));

        puts("done\n");

 

        return 1;

#else

return 0;

#endif/* CONFIG_CMD_MOVINAND */

}

 

E、调用cpu/s5pc11x/movi.c文件中的movi_write_env函数

void movi_write_env(ulong addr)

{

movi_write(raw_area_control.p_w_picpath[2].start_blk,

   raw_area_control.p_w_picpath[2].used_blk, addr);

}

 

F、调用drivers/mmc/mmc.c文件中movi_write函数

ulong movi_write(ulong start, lbaint_t blkcnt, void *src)

{

return mmc_bwrite(0, start, blkcnt, src);

}

 

G、调用drivers/mmc/mmc.c文件中mmc_bwrite函数,最终调用drivers/mmc/s3c_hsmmc.c文件中s3c_hsmmc_send_command函数完成数据保存写入。

3、uboot中的驱动分层

    uboot中与SD/MMC设备有关的操作接口函数位于drivers/mmc/mmc.c文件中,这些接口函数最终指向了struct mmc结构体的函数指针成员。与SD/MMC设备硬件有关的操作函数接口位于drivers/mmc/s3c_hsmmc.c文件中,主要是SoC内部的SD/MMC控制器对SD/MMC设备的操作,如SD/MMC设备的命令发送函数s3c_hsmmc_send_command、SD/MMC设备的数据读写函数s3c_hsmmc_set_ios。SD/MMC设备驱动在驱动构建初始化函数s3c_hsmmc_initialize中将struct mmc结构体实例中的函数指针与对相对应的SD/MMC设备操作方法接口进行了挂接。

    uboot将对SoC内部与SD/MMC设备有关的初始化操作函数如setup_hsmmc_clock、setup_hsmmc_cfg_gpio放在了与SoC相关的文件目录cpu/s5pc11x/setup_hsmmc.c文件中。

uboot中对于linux设备驱动分层的继承移植极大方便了开发者对uboot的移植。

 

上一篇:嵌入式linux开发uboot移植(七)——三星官方uboot的移植


下一篇:移植uboot-支持yaffs烧写,打补丁