接着前一节uboot移植,继续做移植工作。
3.5 支持网络DM9000
第一步:查找uboot源码中drivers/net/目录是否支持DM9000芯片。经查:U-BOOT-2010.06版本已经对CS8900和DM9000X网卡有比较完善的代码支持。
第二步:修改配置文件inlude/configs/tx2440.h。 why?因为s3c24xx默认支持cs8900,所以需要将代码修改成支持DM9000。需要修改哪些地方?通过查看drivers/net中dm9000x.c和dm9000x.h可知:
1、 dm9000x.h需要CONFIG_DRIVER_DM9000宏定义依赖
2、 dm9000x.c需要CONFIG_DM9000_BASE、DM9000_DATA、DM9000_IO、CONFIG_DM9000_NO_SROM等
修改后代码:
#if 0 注释到cs8900
#define CONFIG_NET_MULTI
#define CONFIG_CS8900 /* we have a CS8900 on-board */
#define CONFIG_CS8900_BASE 0x19000300
#define CONFIG_CS8900_BUS16 /* the Linux driver does accesses as shorts */
#endif
#define CONFIG_DRIVER_DM9000 1
#define CONFIG_NET_MULTI 1
#define CONFIG_DM9000_NO_SROM 1
#define CONFIG_DM9000_BASE 0x20000300
#define DM9000_IO CONFIG_DM9000_BASE
#define DM9000_DATA (CONFIG_DM9000_BASE + 4)
3、给u-boot加上ping命令,用来测试网络通不通
#define CONFIG_CMD_PING
4、恢复被注释掉的网卡MAC地址和修改你合适的开发板IP地址
#define CONFIG_ETHADDR 12:34:56:78:90:ab
#define CONFIG_NETMASK 255.255.255.0
#define CONFIG_IPADDR 192.168.1.200 开发板的IP地址
#define CONFIG_SERVERIP 192.168.1.100 windows XP服务器的IP地址
说明:CONFIG_IPADDR指的是开发板的IP地址,CONFIG_SERVERIP指的是windows XP服务器的IP地址,这两个IP地址必须在同一网段上,在使用网络时,必须保证电脑服务器端的IP地址和CONFIG_SERVERIP的值保持一致。
第三步:增加板级网络设置。修改board/samsung/tx2440/tx2440.c
int board_eth_init(bd_t *bis)
{ int rc = 0;
#ifdef CONFIG_CS8900
rc = cs8900_initialize(0, CONFIG_CS8900_BASE);
#endif
#ifdef CONFIG_DRIVER_DM9000
rc = dm9000_initialize(bis);
#endif
return rc;
}
第四步:编译uboot.bin make –j4
编译运行时,ping 网关即 ping192.168.1.1
tx2440 # ping 192.168.1.106
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 08:00:3e:26:0a:5b
could not establish link
Using dm9000 device
ping failed; host 192.168.1.106 isnot alive 上述现象是无法ping通。
第五步:根据第四步提示无法ping通信息可知:无法建立link连接。解决办法:修改dm9000x.c相关代码。
1、找到“could not establish link”相关代码并屏蔽
#if 0
i = 0;
while (!(phy_read(1) & 0x20)) { /* autonegation complete bit */
udelay(1000);
i++;
if (i == 10000) {
printf("could not establish link\n");
return 0;
}
}
#endif
屏蔽掉dm9000_halt函数中的内容:
static void dm9000_halt(struct eth_device *netdev)
{
#if 0
DM9000_DBG("%s\n", __func__);
/* RESET devie */
phy_write(0, 0x8000); /* PHY RESET */
DM9000_iow(DM9000_GPR, 0x01); /* Power-Down PHY */
DM9000_iow(DM9000_IMR, 0x80); /* Disable all interrupt */
DM9000_iow(DM9000_RCR, 0x00); /* Disable RX */
#endif
再次编译后运行。
tx2440 # ping 192.168.1.106
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 08:00:3e:26:0a:5b
operating at unknown: 0 mode
Using dm9000 device
host 192.168.1.106 is alive 表示可以ping通
下面截图是:使用TFTP工具网络下载程序
3.6加入MTD(NAND)分区
#define CONFIG_MTD_DEVICE /*使用MTD设备*/
#define CONFIG_MTD_PARTITIONS
#define MTDPARTS_DEFAULT /*默认的MTD分区表*/
"mtdparts=nandflash0:1m@0(bios)," \
"128k(params)," \
"4m(kernel)," \
"-(root)"
序号 | 分区名 | 分区大小 | 偏移地址 | 作用 |
0 | bios | 0x0010 0000(1M) | 0x0000 0000 | 存放uboot |
1 | params | 0x0002 0000(128K) | 0x0010 0000 | 存储环境变量 |
2 | kernel | 0x0040 0000(4M) | 0x0012 0000 | 存放内核 |
3 | root | 0xfae0 0000(其他) | 0x0052 0000 | 存放文件系统 |
install_auto_complete();
#endif
extern int mtdparts_init(void); //该函数在common/cmd_mtdparts.c中定义
if (!(s=getenv("mtdparts")))//获取mtdparts环境变量值
{
run_command("mtdparts default", 0); //运行命令
}
else
{
mtdparts_init();
}
#endif
3.7支持内核启动
#include <common.h>
#include <command.h>
#include <image.h>
#include <u-boot/zlib.h>
#include <asm/byteorder.h>
#include <asm/arch-s3c24x0/s3c2410.h> /*modify by yedapeng */
#include "asm/mach-types.h"/*modify by yedapeng */
#define LINUX_KERNEL_OFFSET 0x8000
#define LINUX_PARAM_OFFSET 0x100
#define LINUX_PAGE_SIZE 0x00001000
#define LINUX_PAGE_SHIFT 12
#define LINUX_ZIMAGE_MAGIC 0x016f2818
#define DRAM_SIZE 0x04000000
extern int nand_copy(unsigned char*, unsigned long, int);
/*
* Disable IRQs
*/
#define local_irq_disable() \
({ \
unsigned long temp; \
__asm__ __volatile__( \
"mrs %0, cpsr @ local_irq_disable\n" \
" orr %0, %0, #128\n" \
" msr cpsr_c, %0" \
: "=r" (temp) \
: \
: "memory", "cc"); \
})
static inline void cpu_arm920_cache_clean_invalidate_all(void)
{
__asm__(
" mov r1, #0\n"
" mov r1, #7 << 5\n" /* 8 segments */
"1: orr r3, r1, #63 << 26\n" /* 64 entries */
"2: mcr p15, 0, r3, c7, c14, 2\n" /* clean & invalidate D index */
" subs r3, r3, #1 << 26\n"
" bcs 2b\n" /* entries 64 to 0 */
" subs r1, r1, #1 << 5\n"
" bcs 1b\n" /* segments 7 to 0 */
" mcr p15, 0, r1, c7, c5, 0\n" /* invalidate I cache */
" mcr p15, 0, r1, c7, c10, 4\n" /* drain WB */
);
}
void cache_clean_invalidate(void)
{
cpu_arm920_cache_clean_invalidate_all();
}
static inline void cpu_arm920_tlb_invalidate_all(void)
{
__asm__(
"mov r0, #0\n"
"mcr p15, 0, r0, c7, c10, 4\n" /* drain WB */
"mcr p15, 0, r0, c8, c7, 0\n" /* invalidate I & D TLBs */
);
}
void tlb_invalidate(void)
{
cpu_arm920_tlb_invalidate_all();
}
void call_linux(long a0, long a1, long a2)
{
local_irq_disable(); /*禁止终端*/
cache_clean_invalidate(); /*使cache失效*/
tlb_invalidate(); /*刷新cache*/
__asm__(
"mov r0, %0\n"
"mov r1, %1\n"
"mov r2, %2\n"
"mov ip, #0\n"
"mcr p15, 0, ip, c13, c0, 0\n" /* zero PID */
"mcr p15, 0, ip, c7, c7, 0\n" /* invalidate I,D caches */
"mcr p15, 0, ip, c7, c10, 4\n" /* drain write buffer */
"mcr p15, 0, ip, c8, c7, 0\n" /* invalidate I,D TLBs */
"mrc p15, 0, ip, c1, c0, 0\n" /* get control register */
"bic ip, ip, #0x0001\n" /* disable MMU */
"mcr p15, 0, ip, c1, c0, 0\n" /* write control register */
"mov pc, r2\n"
"nop\n"
"nop\n"
: /* no outpus */
: "r" (a0), "r" (a1), "r" (a2) /*参数传递*/
: "r0","r1","r2","ip"
);
}
/*
* pram_base: base address of linux paramter 存放linux启动参数的地址即0x3000 0100
*/
static void setup_linux_param(ulong param_base)
{
struct param_struct *params = (struct param_struct *)param_base;
char *linux_cmd;
// printf("Setup linux parameters at 0x%08lx\n", param_base);
memset(params, 0, sizeof(struct param_struct));
params->u1.s.page_size = LINUX_PAGE_SIZE; /* LINUX_PAGE_SIZE=0x00001000=4Kbyte 为什么是该值? */
params->u1.s.nr_pages = (DRAM_SIZE >> LINUX_PAGE_SHIFT); /* why? */
/* set linux command line */
/*bootargs--uboot传递给内核启动参数,具体定义在tx2440文件下:
CONFIG_BOOTARGS "noinitrd root=/dev/mtdblock2 init=/linuxrc console=ttySAC0 mem=64M"
*/
linux_cmd = getenv ("bootargs");
if (linux_cmd == NULL) {
printf("Wrong magic: could not found linux command line\n");
} else {
memcpy(params->commandline, linux_cmd, strlen(linux_cmd) + 1);
// printf("linux command line is: \"%s\"\n", linux_cmd);
}
}
/*
* dst: destination address
* src: source
* size: size to copy
* mt: type of storage device
*/
static inline int copy_kernel_img(ulong dst, const char *src, size_t size)
{
int ret = 0;
ret = nand_copy((unsigned char *)dst,
(unsigned long)src, (int)size);
return ret;
}
int boot_zImage(ulong from, size_t size)
{
int ret;
ulong boot_mem_base; /* base address of bootable memory */
ulong to;
ulong mach_type;
boot_mem_base = 0x30000000;
/* copy kerne image */
to = boot_mem_base + LINUX_KERNEL_OFFSET; /*=0x3000 0000 + 0x8000*/
printf("Copy linux kernel from 0x%08lx to 0x%08lx, size = 0x%08lx ... ",
from, to, size);
/*函数原型:copy_kernel_img()
功能:将nand flash地址0x120000,长度为4Mbytekernel源码,复制到内存SDRAM地址0x3000 8000开始处
参数: 1、to---目的地址(即内存地址)
2、form-源地址(即nand flash地址)
3、size-复制的长度
返回值:0--表示成功复制,-1表示失败
*/
ret = copy_kernel_img(to, (char *)from, size);
if (ret) {
printf("failed\n");
return -1;
} else {
printf("Copy Kernel to SDRAM done,");
}
if (*(ulong *)(to + 9*4) != LINUX_ZIMAGE_MAGIC) {
printf("Warning: this binary is not compressed linux kernel image\n");
printf("zImage magic = 0x%08lx\n", *(ulong *)(to + 9*4));
} else {
// printf("zImage magic = 0x%08lx\n", *(ulong *)(to + 9*4));
;
}
/* Setup linux parameters and linux command line */
/*1、设置启动参数地址=0x3000 0000 + 0x100=0x3000 0100*/
setup_linux_param(boot_mem_base + LINUX_PARAM_OFFSET);
/* 2、设置CPU机器型号 Get machine type */
mach_type = MACH_TYPE_S3C2440;
// printf("MACH_TYPE = %d\n", mach_type);
/* Go Go Go */
printf("NOW, Booting Linux......\n");
call_linux(0, mach_type, to);
return 0;
}
int do_boot_zImage (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
boot_zImage(0x120000,0x400000);
return 0;
}
U_BOOT_CMD(
boot_zImage, 3, 0, do_boot_zImage,
"boot_zImage - boot Linux 's zImage\n",
" - boot Linux 's zImage"
);
注:用nand_copy函数位于board/samsung/tx2440/nand_read.c中。
3.8 支持yaffs2启动
#define ENABLE_CMD_NAND_YAFFS_SKIPFB 1 /*支持烧写Yaffs时,跳过第一个好bolck*/
else
ret = nand_write_skip_bad(nand, off, &size,
(u_char *)addr);
/* hailin add start*/
#if defined(ENABLE_CMD_NAND_YAFFS)
}
else if ( s != NULL && !strcmp(s, ".yaffs") || !strcmp(s, ".yaffs2"))
{
if(read)
{ /*不支持 nand read.yaffs2 命令*/
printf("nand read.yaffs[2] is not provide temporarily!");
}
else
{
nand->rw_oob = 1;//支持yaff文件的OBB区的读写
#if defined(ENABLE_CMD_NAND_YAFFS_SKIPFB)
nand->skipfirstblk = 1; //跳过第一个好block
#else
nand->skipfirstblk = 0;
#endif
/**********************************************
**函数原型:int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
u_char *buffer)
**功能:向nand flash中写入镜像文件。写入过程中,遇到bad块跳过,写入下一个新的块。
**参数: 1、nand表示 nand_info_t型结构体
2、offset表示目的地址 即写入nand flash起始地址
3、*length表示写入镜像文件长度
4、*buffer表示源地址 即需要写入镜像文件在内存存储地址
**返回值: 0表示成功
***********************************************/
ret = nand_write_skip_bad(nand,off,&size,(u_char *)addr);
#if defined(ENABLE_CMD_NAND_YAFFS_SKIPFB)
nand->skipfirstblk = 0;
#endif
nand->rw_oob = 0;
}
#endif
/*hailin add end*/
#if defined(ENABLE_CMD_NAND_YAFFS) /*hailin add start */
"nand read[.yaffs[2]] is not provide temporarily!\n"
"nand write[.yaffs[2]] addr off size - write the `size' byte yaffs image starting\n"
" at offset `off' from memory address `addr' (.yaffs2 for 2048+64 NAND)\n"
#endif /*hailin add end */
加入了烧写yaffs文件系统命令后,我们即可执行nand write.yaffs命令来烧写 yaffs文件系统镜像。
struct mtd_info {
/* hailin add start*/
#if defined(ENABLE_CMD_NAND_YAFFS)
u_char rw_oob;//支持OOB区的读取
u_char skipfirstblk;//跳过第一个好block
#endif
/*hailin add end*/
size_t len_incl_bad;
u_char *p_buffer = buffer;
/* hailin add start 添加OOB操作的支持*/
#if defined(ENABLE_CMD_NAND_YAFFS)
if(nand->rw_oob==1) {
size_t oobsize = nand->oobsize;
size_t datasize = nand->writesize;
int datapages = 0;
/*测试烧写yaffs文件印象是不是(64+2048byte)整数倍,如果是表示烧写数据正确*/
if (((*length)%(nand->oobsize+nand->writesize)) != 0) {
printf ("Attempt to write error length data!\n");
return -EINVAL;
}
datapages = *length/(datasize+oobsize); //烧写所需页数
*length = datapages*datasize; //计算烧写到nand flash数据区中长度
left_to_write = *length;
}
#endif
/*hailin add end*/
/*检测烧写文件在nand占用长度(包括坏块长度)*/
len_incl_bad = get_len_incl_bad (nand, offset, *length);
if ((offset + len_incl_bad) > nand->size) {//烧写长度+烧写地址 大于需要烧写印象文件,表示无可用空间
printf ("Attempt to write outside the flash area\n");
return -EINVAL;
}
#if !defined(ENABLE_CMD_NAND_YAFFS) //add by hailin
/*烧写非yaffs的镜像文件,例如uboot、kernel文件*/
if (len_incl_bad == *length) {//当在烧写文件在nand占用长度 == 烧写文件长度,表示在nand这段储存空间无bad block
rval = nand_write (nand, offset, length, buffer);
if (rval != 0)
printf ("NAND write to offset %llx failed %d\n",
offset, rval);
return rval;
}
#endif //add by hailin
while (left_to_write > 0) {
size_t block_offset = offset & (nand->erasesize - 1);
size_t write_size;
WATCHDOG_RESET ();
if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) {
printf ("Skip bad block 0x%08llx\n",
offset & ~(nand->erasesize - 1));
offset += nand->erasesize - block_offset;
continue;
}
/*hailin add start*/
#if defined(ENABLE_CMD_NAND_YAFFS)
if(nand->skipfirstblk==1) {//跳过第一个好block
nand->skipfirstblk=0;
printf ("Skip the first good block %08lx\n",
offset & ~(nand->erasesize - 1));
offset += nand->erasesize - block_offset;
continue;
}
#endif
/*hailin add end*/
if (left_to_write < (nand->erasesize - block_offset))
write_size = left_to_write;
else
write_size = nand->erasesize - block_offset;
printf("\rWriting at 0x%08lx --",offset); //hailin add
rval = nand_write (nand, offset, &write_size, p_buffer);
if (rval != 0) {
printf ("NAND write to offset %llx failed %d\n",
offset, rval);
*length -= left_to_write;
return rval;
}
left_to_write -= write_size;
printf("%d%% is complete.",100-(left_to_write/(*length/100))); //add by yedapeng
offset += write_size;
//p_buffer += write_size;
/* add for hailin start*/
#if defined(ENABLE_CMD_NAND_YAFFS)
if(nand->rw_oob==1) {
p_buffer += write_size+(write_size/nand->writesize*nand->oobsize);
}
else {
p_buffer += write_size;
}
#else
p_buffer += write_size;
#endif
}
/* add for hailin end*/
return 0;
}
static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const uint8_t *buf)
{
struct nand_chip *chip = mtd->priv;
int ret;
/*add by hailin begin*/
/*
以K9F2G08U0B为例,该nand flash一页为(2K+64)字节(2K 表示的是 main 区容量,
64 表示的是 spare 区容量),它的一块为 64 页,而整个设备包括了2048个块。
则mtd->oobsize=64,mtd->writesize=2048
*/
#if defined(ENABLE_CMD_NAND_YAFFS)
int oldopsmode = 0;
u_char data_buffer[len] ,oob_buffer[len/(mtd->writesize)*mtd->oobsize];
if(mtd->rw_oob==1) {
size_t oobsize = mtd->oobsize;
size_t datasize = mtd->writesize;
int i = 0;
uint8_t oobtemp[oobsize];
int datapages = 0;
datapages = len/(datasize);
/*
下面for循环函数作用:将buf内存中yaffs文件中data区和oob区,
分别存储在data_buffer和obb_buffer两个内存块。
具体步骤:
1、将buf内存地址中data数据复制到data_buffer中
2、将buf内存地址中oob数据复制到oob_buffer中
3、复制次数=页数datapage
*/
for(i=0;i<(datapages);i++) {
memcpy((void *)(data_buffer+i*datasize),
(void *)(buf+datasize*i+i*oobsize),
datasize);
memcpy((void *)(oob_buffer+i*oobsize),
(void *)(buf+datasize*(i+1)+i*oobsize),
oobsize);
}
}
#endif
/* Do not allow reads past end of device */
if ((to + len) > mtd->size)
return -EINVAL;
if (!len)
return 0;
nand_get_device(chip, mtd, FL_WRITING);
chip->ops.len = len;
/* hailin add start*/
if(mtd->rw_oob!=1) {//是否支持OOB区读写?
chip->ops.datbuf = (uint8_t *)buf; //
}
else{
chip->ops.datbuf = (uint8_t *)data_buffer;//支持OOB区读写,设置data区
}
#if defined(ENABLE_CMD_NAND_YAFFS)
if(mtd->rw_oob!=1) {//是否支持OOB区读写?
chip->ops.oobbuf = NULL;
} else {
//chip->ops.oobbuf = (uint8_t *)(buf+len);
chip->ops.oobbuf = (uint8_t *)oob_buffer;//设置OOB区
chip->ops.ooblen = mtd->oobsize;//设置OOB区长度
oldopsmode = chip->ops.mode;
chip->ops.mode = MTD_OOB_RAW;
}
#else
chip->ops.oobbuf = NULL; //未定义NAND_YAFFS命令,则不使用OOB区
#endif
/*hailin add end*/
//chip->ops.datbuf = (uint8_t *)buf; /*此处是否需要屏蔽,需要屏蔽*/
//chip->ops.oobbuf = NULL;/*此处是否需要屏蔽,需要屏蔽*/
ret = nand_do_write_ops(mtd, to, &chip->ops);//执行写操作
*retlen = chip->ops.retlen;
nand_release_device(mtd);//释放nand flash
/*hailin add start*/
#if defined(ENABLE_CMD_NAND_YAFFS)
chip->ops.mode = oldopsmode;
#endif
/*hailin add end*/
return ret;
}
将存放yaffs文件的buf中 data和OOB数据的分离出来,方便向nand flash写入 。(该代码参考天祥电子)
for(i=0;i<(datapages);i++)
{
memcpy( (void *)(data_buffer+i*datasize), (void *)(buf+datasize*i+i*oobsize), datasize);
memcpy( (void *)(oob_buffer+i*oobsize), (void *)(buf+datasize*(i+1)+i*oobsize),oobsize);
}
执行过程如下:
3.9 支持menu菜单选项
下面以添加menu命令(启动菜单)为例讲解U-Boot添加命令的方法。
(1) 建立common/cmd_menu.c
习惯上通用命令源代码放在common目录下,与开发板专有命令源代码则放在board/<board_dir>目录下,并且习惯以“cmd_<命令名>.c”为文件名。
源码:
#include <common.h>
#include <command.h>
static char awaitkey(unsigned long delay, int* error_p)
{
int i;
char c;
if (delay == -1) {
while (1) {
if (tstc()) /* we got a key press */
return getc();
}
}
else {
for (i = 0; i < delay; i++) {
if (tstc()) /* we got a key press */
return getc();
udelay (10*1000);
}
}
if (error_p)
*error_p = -1;
return 0;
}
void main_menu_usage(void)
{
printf("\r\n######## Hotips TFTP DownLoad for TX2440A ########\r\n");
printf("\r\n");
printf("[1] 下载 u-boot.bin 写入 Nand Flash\r\n");
printf("[2] 下载 Linux(zImage) 内核镜像写入 Nand Flash\r\n");
printf("[3] 下载 yaffs2(fs.yaffs2) 文件系统镜像写入 Nand Flash\r\n");
printf("[4] 下载 Linux(uImage) 内核镜像到内存并运行\r\n");
printf("[5] 重启设备\r\n");
printf("[6] 擦除nand flash\r\n");
printf("[q] 退出菜单\r\n");
printf("\r\n");
printf("输入选择: ");
}
void menu_shell(void)
{
char cmd_buf[200];
char c;
//unsigned char j=1;
while (1)
{
main_menu_usage(); //打印菜单
c = awaitkey(-1, NULL);
printf("%c\n", c);
switch (c)
{
case '1': //输入‘1’ 下载 u-boot.bin
{ /* 1、将镜像通过tftp下载到开始地址0x3000 0000的内存中
2、擦除需要写入空间
3、将存储在内存中镜像 烧写到起始地址为0x0的nand flash中
*/
strcpy(cmd_buf,"tftp 0x30000000 u-boot.bin;nand erase 0 0x100000;nand write 0x30000000 0x0 0x100000");
run_command(cmd_buf,0);
break;
}
case '2': //输入‘2’ 下载 Linux(zImage_2.6.31.bin)
{
strcpy(cmd_buf,"tftp 0x30000000 zImage_2.6.31.bin;nand erase 0x120000 0x400000;nand write 0x30000000 0x120000 0x400000");
run_command(cmd_buf,0);
break;
}
case '3': //输入‘3’ 下载 yaffs2(root_qtfs.bin)
{
strcpy(cmd_buf,"tftp 0x30000000 root_qtfs.bin;nand erase 0x520000 0x3ffffc0;nand write.yaffs 0x30000000 0x520000 0x3ffffc0");
run_command(cmd_buf,0);
break;
}
case '4': //输入‘4’ 下载 Linux(uImage) 内核镜像到内存并运行
{
strcpy(cmd_buf,"tftp 0x30000000 zImage_2.6.31.bin;bootm 0x30000000");
run_command(cmd_buf,0);
break;
}
case '5': //输入‘5’ 重启系统
{
strcpy(cmd_buf,"boot_zImage");
run_command(cmd_buf,0);
break;
}
case '6': //输入‘6’ 擦除nand flash
{
strcpy(cmd_buf,"nand erase 0x120000 0xc800000");
run_command(cmd_buf,0);
break;
}
default:break;
}
if(c=='q') { break;} //退出菜单
}
}
int do_menu (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
menu_shell();
return 0;
}
U_BOOT_CMD(
menu, 3, 0, do_menu,
"menu - display a menu, to select the items to do something\n",
" - display a menu, to select the items to do something"
);
(2) 定义“menu”命令
在cmd_menu.c中使用如下的代码定义“menu”命令:
_BOOT_CMD(
menu, 3, 0, do_menu,
"menu - display a menu, to select the items to do something\n",
" - display a menu, to select the items to do something"
);
其中U_BOOT_CMD命令格式如下:
U_BOOT_CMD(name,maxargs,rep,cmd,usage,help)
各个参数的意义如下:
name:命令名,非字符串,但在U_BOOT_CMD中用“#”符号转化为字符串
maxargs:命令的最大参数个数
rep:是否自动重复(按Enter键是否会重复执行)
cmd:该命令对应的响应函数
usage:简短的使用说明(字符串)
help:较详细的使用说明(字符串)
在内存中保存命令的help字段会占用一定的内存,通过配置U-Boot可以选择是否保存help字段。若在include/configs/tx2440.h中定义了CONFIG_SYS_LONGHELP宏,则在U-Boot中使用help命令查看某个命令的帮助信息时将显示usage和help字段的内容,否则就只显示usage字段的内容。
U_BOOT_CMD宏在include/command.h中定义:
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
“##”与“#”都是预编译操作符,“##”有字符串连接的功能,“#”表示后面紧接着的是一个字符串。
其中的cmd_tbl_t在include/command.h中定义如下:
struct cmd_tbl_s {
char *name; /* 命令名 */
int maxargs; /* 最大参数个数 */
int repeatable; /* 是否自动重复 */
int (*cmd)(struct cmd_tbl_s *, int, int, char *[]); /* 响应函数 */
char *usage; /* 简短的帮助信息 */
#ifdef CONFIG_SYS_LONGHELP
char *help; /* 较详细的帮助信息 */
#endif
#ifdef CONFIG_AUTO_COMPLETE
/* 自动补全参数 */
int (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
#endif
};
typedef struct cmd_tbl_s cmd_tbl_t;
一个cmd_tbl_t结构体变量包含了调用一条命令的所需要的信息。
其中Struct_Section在include/command.h中定义如下:
#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))
凡是带有__attribute__ ((unused,section (".u_boot_cmd"))属性声明的变量都将被存放在".u_boot_cmd"段中,并且即使该变量没有在代码中显式的使用编译器也不产生警告信息。
在U-Boot连接脚本u-boot.lds中定义了".u_boot_cmd"段:
. = .;
__u_boot_cmd_start = .; /*将 __u_boot_cmd_start指定为当前地址 */
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .; /* 将__u_boot_cmd_end指定为当前地址 */
这表明带有“.u_boot_cmd”声明的函数或变量将存储在“u_boot_cmd”段。这样只要将U-Boot所有命令对应的cmd_tbl_t变量加上“.u_boot_cmd”声明,编译器就会自动将其放在“u_boot_cmd”段,查找cmd_tbl_t变量时只要在__u_boot_cmd_start与__u_boot_cmd_end之间查找就可以了。
因此“menu”命令的定义经过宏展开后如下:
cmd_tbl_t __u_boot_cmd_menu __attribute__ ((unused,section (".u_boot_cmd"))) = {menu, 3, 0, do_menu, "menu - display a menu, to select the items to do something\n", " - display a menu, to select the items to do something"}
实质上就是用U_BOOT_CMD宏定义的信息构造了一个cmd_tbl_t类型的结构体。编译器将该结构体放在“u_boot_cmd”段,执行命令时就可以在“u_boot_cmd”段查找到对应的cmd_tbl_t类型结构体。
(3) 实现命令的函数
在cmd_menu.c中添加“menu”命令的响应函数的实现。具体的实现代码略:
int do_menu (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
/* 实现代码略 */
}
(4) 将common/cmd_menu.c编译进u-boot.bin
在common/Makefile中加入如下代码:
COBJS-$( CONFIG_CMD_MENU) += cmd_menu.o
在include/configs/tx440.h加入如代码:
#define CONFIG_CMD_MENU
重新编译下载U-Boot就可以使用menu命令了
(5)menu命令执行的过程
在U-Boot中输入“menu”命令执行时,U-Boot接收输入的字符串“menu”,传递给run_command函数。run_command函数调用common/command.c中实现的find_cmd函数在__u_boot_cmd_start与__u_boot_cmd_end间查找命令,并返回menu命令的cmd_tbl_t结构。然后run_command函数使用返回的cmd_tbl_t结构中的函数指针调用menu命令的响应函数do_menu,从而完成了命令的执行。
3.10 其他
3.10.1支持Tab补全命令和上下键调用历史命令
1、include/configs/TX2440.h中添加代码
#define CONFIG_CMDLINE_EDITING 支持上下键调用历史命令
#define CONFIG_AUTO_COMPLETE 支持Tab补全命令
2、再次编译,建议每次编译前都清除一下上次编译留下的中间代码:
#make distclean
#make tx2440_config
#make
3.10.2输出FLCK HCLK PCLK频率
在\arch\arm\lib\board.c中添加
void print_cpuinfo(void)
{ unsigned long fclk,hclk,pclk,uclk;
fclk=get_FCLK();
hclk=get_HCLK();
pclk=get_PCLK();
uclk=get_UCLK();
printf("FCLK=%ldMHz,HCLK=%ldMHz,PCLK=%ldMHz,UCLK=%ldMHz\n",fclk/1000000,hclk/1000000,pclk/1000000,uclk/1000000);
}