接着前一節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);
}