s3c6410 uboot代码分析《二》

转自:http://hi.baidu.com/__eabi/blog/item/bf06b6c14c13434b0eb345d9.html?timeStamp=1313056638500


4)环境变量初始化

       环境变量初始化,即start_armboot函数第379行的env_relocate ()函数,这个函数实现体在env_common.c中,我们看真相:

s3c6410 uboot代码分析《二》_第1张图片

s3c6410 uboot代码分析《二》_第2张图片

 

       这个函数的功能其实就是让env_ptr指向存放环境变量的首地址,并且填充env_ptr->data成员变量。

       208和212的两个宏我们没有定义,所以直接看223行,223行malloc一个CFG_ENV_SIZE的空间用于存放环境变量

       230行是在环境变量被迁移到内存后,我们可以使用内存中的环境变量

       由于gd->env_valid我们前面没有赋值,所以232行if会执行,打印236行的内容

       240~244行是判断默认的环境变量是否大于我们限定的环境变量大小

       247行是把uboot代码中已经存在的default_environment指针所指向的环境变量拷贝到env_ptr->data中

       253行是crc32的校验,会对环境变量的内容使用CRC32校验表做每8字节的DO1校验,校验表定义在crc32.c的77行

       259行是把内存中可操作的环境变量记录在全局变量中

 

5)网络初始化

       网络初始化分为IP地址初始化和MAC地址初始化,看下图:

s3c6410 uboot代码分析《二》_第3张图片

       387行:IP地址初始化,getenv_IPaddr会返回IPaddr_t(unsigned long)类型的IP地址赋值给gd->bd->bi_ip_addr全局变量。

       进入getenv_IPaddr函数会执行return (string_to_ip(getenv(var))),我们先看getenv(var):s3c6410 uboot代码分析《二》_第4张图片

       497行:是给软件狗准备的,我的ZC6410板子上有硬件狗,所以这个WATCHDOG_RESET其实就是一个空的macro。

       499~510行:会查找环境变量中的name,name是传递进来的ipaddr,进入查找旅程env_get_char函数,如果我们没有忘记的话

       我们在env_relocate ()函数的230行执行过env_get_char = env_get_char_memory,所以会执行env_get_char_memory函数,

      这个函数在env_common.c中实现:

s3c6410 uboot代码分析《二》_第5张图片

       186行的值同样在env_relocate正确执行过后,被赋值为1,返回gd->env_addr+index,gd->env_addr指向default_environment

       因为default_environment是个指针数组,index代表数组的索引,即环境变量名得索引,看default_environment数组就能够明白

       如果你已经自己看过了这两段for循环,你可能知道外层for循环在遍历default_environment数组,内层判断是否超出环境变量的

       大小。后面外层循环会执行507行的envmatch函数,这个函数是真正匹配getenv传递进去的ipaddr参数的,里面使用while循环

       去找环境变量中的‘=’号前面的是否有ipaddr字符串,有则返回ipaddr在环境变量中的索引,比如ipaddr=“。。。”\0这个字符串

       被放在default_environment[1]中,则返回这个索引值1。

       getenv的509行返回这个索引所在的环境环境变量的地址,即return ( ((uchar *)(gd->env_addr + index)) )。

       我们返回getenv_IPaddr函数,执行return (string_to_ip(getenv(var))),把上面返回的地址所在的字符串(IP)转化为int类型的数据

       保存在gd->bd->bi_ip_addr中。

 

       390~403:MAC地址初始化

       和上面的IP初始化一样,做着换汤不换药的事情

 

6)设备列表初始化

       start_armboot函数的417行:devices_init ()函数。函数完成的主要功能是把设备放入list列表。这种方法是被推荐的,因为下次在查找

       设备的时候会通过这个列表来查询设备,方便统一管理。这个设备列表的接口的个二级指针变量devlist(list_t类型->二级指针)。

       下面不贴代码了,因为不难理解。这个函数的169~171行for循环在把标准输入、标准输出、标准错误的名字赋值到stdio_names全局

       数组变量中。176行比较关键,是为uboot中的设备建立了list列表,以后增加设备时,可以插入这个list中通过devlist指针。

       再往下面是很多宏定义,但是我们都没有定义,只有一个函数会执行,就是drv_system_init ()。这个函数注册了一个设备,设备名是

       serial,填充设备的device_t结构体,指明设备的输入和输出设备等,最后使用device_register (&dev),把自己注册到devlist列表中。

 

7)配置功能函数表

      start_armboot函数的423行执行jumptable_init (),为gd-jt全局变量定义了一些独立的函数接口,方便调用。

 

8)控制台初始化

       start_armboot函数的437行执行console_init_r (),设定一个控制台。我们知道,我们的uboot也是有printf之类输出、有命令可以

       让我们输入的,所以我们需要定义出一个控制台设备,让我们的输入和输出都指向它,下面看函数:

s3c6410 uboot代码分析《二》_第6张图片

s3c6410 uboot代码分析《二》_第7张图片

s3c6410 uboot代码分析《二》_第8张图片

        491行:从devlist设备列表中获得当前devlist中所列举的最大设备个数,我们前面已经对这个接口做过赋值,并配置到devlist中,且

       当初设置的值为0,代表第一个设备,那么这里items的值就应该是0了;

       493和500行的宏我没有定义,不执行;

       507~519行:查找输入输出设备,

       511行:定义一个能够代表控制台设备的结构体,并且从devlist中为自己获取一个列表位置,ListGetPtrToItem函数如下图所示:

s3c6410 uboot代码分析《二》_第9张图片

上面if和else if都没有得到执行,直接执行最后的return

return的值由最下面的define决定,我们看到list->itemList数组其实就是devlist的设备挂载点

我们将会把我们的设备列表都列举到这个数组中

但是需要注意一个问题,list->itemList定义是list->itemlist[1],只是分配了一个字段,如何能存下多个设备列表呢

gnu编译器对标准C做了扩展,支持动态大小的数组定义,所以我想你应该使用gnu编译器来编译uboot

       513和516行把这个获得的列表地址赋值给了inputdev和outputdev两个设备指针;

       522和528行是真正的给我们的标准输入和标准输入设备填充结构的函数,会把dev地址再赋值给全局结构体stdio_devices

       这个结构体是最终记录标准输入输出结构体的;

       534行:CFG_CONSOLE_INFO_QUIET宏没有定义,所以会执行下面的代码,我们在启动uboot之后,会打印

In:serial

Out:serial

Error:serial

       就是这段代码起的效果;

       559行:使用setenv把标准输入输出和标准错误设备变量设置到环境变量中。

 

9)开中断异常向量表

       start_armboot函数的433行执行enable_interrupts,内容如下:

       以上代码是内嵌汇编,这个我不解释,因为我前面有文章专门介绍__asm__ __volatile__用法的,作用就是清除cpsr中的

       [7]位。这位的含义是中断使能/禁止位,拥有中断最高优先级,这里代码用于开启中断。

 

10)网卡芯片初始化

       我的板子上使用的是DM9000AEP网卡,不是CS8900,所以代码有所改动,增加了对DM9000网卡的初始化支持,这里我调用了

       DM9000里的一个函数eth_set_mac,用于初始化网卡芯片,代码如下:

s3c6410 uboot代码分析《二》_第10张图片

       注释写的很好,最起码我们知道这段代码在干什么,首先打印出全局变量gd里面存放的网卡设备的MAC地址,这个值是我们在

       板子头文件里面定义的CONFIG_ETHADDR获取的;

       293行:在填充MAC地址的值到网卡芯片专门用于保存MAC地址的寄存器当中,所以这里我们定义了DM9000_PAR,它的值是0x10

       用于保存MAC地址,为什么是0x10这个地址保存MAC地址,我们当然不知道,得去找DM9000网卡芯片厂商的datasheet,如下:

s3c6410 uboot代码分析《二》_第11张图片

看倒数第二行,告诉你DM9000的物理网卡地址寄存器叫PAR,地址是10h-15h 6个字节用于保存MAC地址

       那么我们下面就把我们的物理网卡地址写到这个寄存器里面来吧,使用294行的函数(注意我们已经开启MMU了,不能

       不能直接访问内存和IO内存了)。DM9000_iow的函数实现体如下图所示:

DM9000_outb是个宏,向IO内存中写入值,例如DM9000_outb(reg,DM9000_IO)的含义是把reg的值写入DM9000_IO这个寄存器

那么DM9000_IO和DM9000_DATA的值是多少呢?即你现在需要具备以下的分析过程:

我们想让DM9000网卡能用,就必须接到板子上

不是随便接的,因为你需要能够控制DM9000的寄存器才能通过软件控制它

那么你又要想如何才能操作到DM9000网卡的寄存器?

操作DM9000网卡寄存器就有对它的读和写,必须通过总线访问到DM9000网卡寄存器的数据地址

访问数据地址必须通过CPU的内存映射(CPU访问外设的地址空间)

所以,最终我们得让CPU和DM9000连接,把DM9000接到CPU能访问的地址空间上

我们如何知道连接上了呢?得看DM9000网卡的连接原理图,如下:

s3c6410 uboot代码分析《二》_第12张图片
看上面的图,有一个叫NET_ncs的引脚,它就是访问片选引脚,它接到了CPU的CS1上,这张图写的是CS#

CS#代表什么意思?幸好我是知道接到CS1上的,这个‘#’应该是硬件工程师的失误了吧

不管怎么样,你要知道接到s3c6410cpu的CS1引脚上是什么意思,代表cpu可以通过这个地址来访问DM9000哦

接下来我们看一下,CS1的值是多少,即什么地址可以访问到DM9000,查看s3c6410 datasheet:

s3c6410 uboot代码分析《二》_第13张图片

看图中红线部分,原来接NET_nCS接到了s3c6410的SRAM1上,即CS1上,地址是0x18000000

好了,回到DM9000_iow函数继续分析,我们定义DM9000_IO和DM9000_DATA的值,如下图所示:

不难看出,DM9000_IO的值是0x18000300

?,为什么不是0x18000000呢?因为DM9000网卡采用pakeage机制,访问DM9000的内部寄存器被映射到了

CS1+0x300偏移地址处,DM9000_DATA寄存器的地址是DM9000_IO的值+4(都是32位)

DM9000_outb(reg, DM9000_IO);
 DM9000_outb(value, DM9000_DATA);

上面两行代码应该知道什么意思了吧,第一行是指定往DM9000网卡的哪一个地址写,reg是0x10,即是用于存储
MAC地址的DM9000内部PAR寄存器,那么DM9000_outb(reg, DM9000_IO)相当于说要向

0x18000310地址写入一个字节,DM9000_outb(value, DM9000_DATA)相当于说0x18000310被写入的内容是value

好了,通过eth_set_mac函数的293~294行,我们把MAC地址写入了DM9000的PAR寄存器中

       295~296行:向广播地址寄存器中赋值全1;

       299~301行:是用于调试的,看能不能从刚才的寄存器中读出我们写入的MAC地址。

       网卡分析比较复杂,关键是你要找到IO访问基地址,还要知道DM9000的中断线,因为我们读网卡数据,系统是通过中断的方式

       来获得的(linux系统,其他的不能太确定),还有DM9000网卡是数据和地址复用的,通过NET_CMD引脚来控制。我的DM9000

       中断引脚接的是EINT7,看上面的DM9000网卡的连接原理图就知道了。

 

11)一些后续初始化

       看下面的真相:

s3c6410 uboot代码分析《二》_第14张图片

       好吧,这个后续初始化工作我们好像都没有定义,等等,除了BOARD_LATE_INIT。不过board_late_init函数里面我什么都没写,

       哈哈,终于不用分析了
 

12)main_loop详解

       这个挺重要的,因为它是uboot和我们用户交互的接口。让我们一起进去分析吧(代码有点小长哦)

      s3c6410 uboot代码分析《二》_第15张图片

s3c6410 uboot代码分析《二》_第16张图片

s3c6410 uboot代码分析《二》_第17张图片

s3c6410 uboot代码分析《二》_第18张图片

s3c6410 uboot代码分析《二》_第19张图片

s3c6410 uboot代码分析《二》_第20张图片

s3c6410 uboot代码分析《二》_第21张图片

       297行:CFG_HUSH_PARSER没有定义,所以会定义全局变量lastcommand[CFG_CBSIZE],CFG_CBSIZE的值为256,用于记录

       console buffer size;

       304行:我们定义了CONFIG_BOOTDELAY在头文件中,默认是3,我改成了1。后面会定义指针s和int型bootdelay。用于uboot判断

       无任何按键按下,就执行CONFIG_BOOTCOMMAND宏所对应的命令,这个命令通常用于加载启动操作系统;

       308行:CONFIG_PREBOOT宏没有定义,不管;

       311行:CONFIG_BOOTCOUNT_LIMIT没有定义,不管;

       318行:没有定义CONFIG_VFD,所以也不管,但我后面会加上这个宏,因为我想在启动的时候在我的LCD上显示我的LOGO;

       329行:CONFIG_BOOTCOUNT_LIMIT没有定义,不管;

       339行:CONFIG_MODEM_SUPPORT没有定义,不管;

       350行:CONFIG_VERSION_VARIABLE没有定义,不管;

       。。。再没有定义的就不贴了,直接找我们定义的看;

       362行:其实CONFIG_AUTO_COMPLETE宏我也没有定义,但是这个东西挺好用的,可以帮助我们自动补齐命令

       390行:我们定义了CONFIG_BOOTDELAY,所以391行s的值将保存bootdelay环境变量的值得地址,392行把地址的值给bootdelay

       408行:s保存bootcmd等于号后面字符串的地址;

       412行:if判断为真,执行418行的run_command函数,这个函数很重要,是查找解析命令并执行的,所以我们还是看图吧:

s3c6410 uboot代码分析《二》_第22张图片

s3c6410 uboot代码分析《二》_第23张图片

s3c6410 uboot代码分析《二》_第24张图片

s3c6410 uboot代码分析《二》_第25张图片

run_command函数的1265行,设置ctrlc_was_pressed = 0,,这个变量标记ctrl+c没有被按下

1267行判断没有输入命令则返回-1

1271行判断我们输入的命令字符串是否大于我们定义的控制台buffer

1276行把命令拷贝到cmdbuf数组中

1285行是一个while循环,str指针指向cmdbuf

1291~1312用于查找“;”号,因为我们的命令可以使用“;”号来执行多条命令,就好像

在shell环境里你输入ls;cd,会先执行ls,然后又会执行cd

1324行process_macros是判断如果一条命令里面有两条命令,把第二条命令分离出来放到finaltoken数组中

1327行:parse_line函数解析字符串,并把解析出来的命令和参数放到argv中,parse_line是字符串解析,不贴图

1333行:是到.u_boot_cmd(u-boot.lds中定义)代码段中找匹配的命令

__u_boot_cmd_start和__u_boot_cmd_end两个全局变量是这个段的起始地址和结束地址

1340行:判断参数,如果所给的参数与实现命令的结构体中所指定的参数大小不一样,将调用命令中提供的usage函数

1346行如果定义了CFG_CMD_BOOTD,则在1348行判断执行的是do_bootd命令则执行1356行的flag

1363行,是真正去执行我们命令行所输入的命令

1370行,判断是否有ctrl+c按下,如果有run_command执行结束

       再次退回main_loop函数,

       449行:开始执行等待用户输入的操作,CFG_HUSH_PARSER没有定义,463行的代码会被执行,readline函数会答应命令行提示符

       比如我设置的CFG_PROMPT值为ZC6410 #,将会在命令行打印ZC6410 #,然后等待用户输入;

       467行:把console_buffer中的数据拷贝到lastcommand数组中,484行判断len是否为0,如果按下ctrl+c按键,这个值将会是-1,

       因为前面代码serial驱动代码中是有这部分代码判断的,如果判断ctrl+c按下,则返回-1,这里的len值自然就是-1,如果ctrl+c按键

       没有被按下,将执行run_command,执行用户输入的命令。

 

uboot执行流程的所有代码分析完毕


你可能感兴趣的:(c,IO,command,cmd,buffer,代码分析)