继续分析,u-boot怎么实现从网页加载固件实现web升级呢!是嵌入式了uip小型web服务器,可以参看manfeel的博文,在u-boot上移植uip的过程:
https://blog.csdn.net/manfeel/article/details/13096075
现分析u-boot_mod中的httpd的代码:
u-boot上电初始化之后,进入board_init_r
,如果想要通过web加载内核镜像,则需要初始化网络设备,在初始化完成后进入main_loop
循环中:
#if defined(CONFIG_CMD_NET)
all_led_on();
eth_initialize(gd->bd);
all_led_off();
#endif
/* main_loop() can return to retry autoboot, if so just run it again */
for (;;)
main_loop();
在main_loop
,没有u-boot命令执行,则加载网络循环NetLoopHttpd();
:
#if defined(CONFIG_CMD_HTTPD)
puts(" Starting web server for update...\n\n");
NetLoopHttpd();
#else
puts("\n");
#endif
NetLoopHttpd()
函数,对网络进行初始化,加入uip web服务器,连接终端:
/* *************************************
*
* HTTP web server for web failsafe mode
*
***************************************/
int NetLoopHttpd(void){
bd_t *bd = gd->bd;
unsigned short int ip[2];
unsigned char ethinit_attempt = 0;
struct uip_eth_addr eaddr;
#ifdef CONFIG_NET_MULTI
NetRestarted = 0;
NetDevExists = 0;
#endif
/* XXX problem with bss workaround */
//初始化网络参数
NetArpWaitPacketMAC = NULL;
NetArpWaitTxPacket = NULL;
NetArpWaitPacketIP = 0;
NetArpWaitReplyIP = 0;
NetArpWaitTxPacket = NULL;
NetTxPacket = NULL;
if(!NetTxPacket){
int i;
// Setup packet buffers, aligned correctly.
NetTxPacket = &PktBuf[0] + (PKTALIGN - 1);
NetTxPacket -= (ulong)NetTxPacket % PKTALIGN;
for(i = 0; i < PKTBUFSRX; i++){
NetRxPackets[i] = NetTxPacket + (i + 1) * PKTSIZE_ALIGN;
}
}
if(!NetArpWaitTxPacket){
NetArpWaitTxPacket = &NetArpWaitPacketBuf[0] + (PKTALIGN - 1);
NetArpWaitTxPacket -= (ulong)NetArpWaitTxPacket % PKTALIGN;
NetArpWaitTxPacketSize = 0;
}
// restart label
restart:
eth_halt();
#ifdef CONFIG_NET_MULTI
eth_set_current();
#endif
// eth_init初始化网络设备
while(ethinit_attempt < 10){
if(eth_init(bd)){
ethinit_attempt = 0;
break;
} else {
ethinit_attempt++;
eth_halt();
milisecdelay(1000);
}
}
if(ethinit_attempt > 0){
eth_halt();
printf_err("couldn't initialize eth (cable disconnected?)!\n\n");
return(-1);
}
// get MAC address
//获取mac地址
#ifdef CONFIG_NET_MULTI
memcpy(NetOurEther, eth_get_dev()->enetaddr, 6);
#else
eth_getenv_enetaddr("ethaddr", NetOurEther);
#endif
eaddr.addr[0] = NetOurEther[0];
eaddr.addr[1] = NetOurEther[1];
eaddr.addr[2] = NetOurEther[2];
eaddr.addr[3] = NetOurEther[3];
eaddr.addr[4] = NetOurEther[4];
eaddr.addr[5] = NetOurEther[5];
// set MAC address 设置uip web的mac地址
uip_setethaddr(eaddr);
// set ip and other addresses
// TODO: do we need this with uIP stack?
NetCopyIP(&NetOurIP, &bd->bi_ip_addr);
NetOurGatewayIP = getenv_IPaddr("gatewayip");
NetOurSubnetMask = getenv_IPaddr("netmask");
NetOurVLAN = getenv_VLAN("vlan");
NetOurNativeVLAN = getenv_VLAN("nvlan");
// start server...
printf("HTTP server is starting at IP: %ld.%ld.%ld.%ld\n", (bd->bi_ip_addr & 0xff000000) >> 24, (bd->bi_ip_addr & 0x00ff0000) >> 16, (bd->bi_ip_addr & 0x0000ff00) >> 8, (bd->bi_ip_addr & 0x000000ff));
HttpdStart(); // HttpdStart()函数初开始进入web的http服务
// set local host ip address 为连接的主机终端分配IP地址
ip[0] = ((bd->bi_ip_addr & 0xFFFF0000) >> 16);
ip[1] = (bd->bi_ip_addr & 0x0000FFFF);
uip_sethostaddr(ip);
// set network mask (255.255.255.0 -> local network)
ip[0] = ((0xFFFFFF00 & 0xFFFF0000) >> 16);
ip[1] = (0xFFFFFF00 & 0x0000FFFF);
uip_setnetmask(ip);
// should we also set default router ip address?
//uip_setdraddr();
// show current progress of the process
do_http_progress(WEBFAILSAFE_PROGRESS_START);
webfailsafe_is_running = 1;
int led_off = 0;
int cnt_up = 1;
int cnt = 0;
// infinite loop
for(;;){
if (cnt == led_off)
all_led_off();
else if (cnt == 0)
all_led_on();
cnt++;
if (cnt == 1024) {
cnt = 0;
if (cnt_up) {
led_off++;
if (led_off == 1024)
cnt_up = 0;
} else {
led_off--;
if (led_off == 0)
cnt_up = 1;
}
}
/*
* Check the ethernet for a new packet.
* The ethernet receive routine will process it.
*/
if(eth_rx() > 0){
HttpdHandler();
}
// if CTRL+C was pressed -> return!
if(ctrlc()){
eth_halt();
// reset global variables to default state
webfailsafe_is_running = 0;
webfailsafe_ready_for_upgrade = 0;
webfailsafe_upgrade_type = WEBFAILSAFE_UPGRADE_TYPE_FIRMWARE;
/* Invalidate the last protocol */
eth_set_last_protocol(BOOTP);
all_led_off();
printf("\nWeb failsafe mode aborted!\n\n");
return(-1);
}
// until upload is not completed, get back to the start of the loop
if(!webfailsafe_ready_for_upgrade){
continue;
}
// stop eth interface
eth_halt();
// show progress
do_http_progress(WEBFAILSAFE_PROGRESS_UPLOAD_READY);
// try to make upgrade!
if(do_http_upgrade(NetBootFileXferSize, webfailsafe_upgrade_type) >= 0){
milisecdelay(500);
do_http_progress(WEBFAILSAFE_PROGRESS_UPGRADE_READY);
milisecdelay(500);
/* reset the board */
do_reset(NULL, 0, 0, NULL);
}
break;
}
// reset global variables to default state
webfailsafe_is_running = 0;
webfailsafe_ready_for_upgrade = 0;
webfailsafe_upgrade_type = WEBFAILSAFE_UPGRADE_TYPE_FIRMWARE;
NetBootFileXferSize = 0;
do_http_progress(WEBFAILSAFE_PROGRESS_UPGRADE_FAILED);
all_led_off();
// go to restart
goto restart;
return(-1);
}
HttpdStart()
函数,启动了uip http服务的初始化:
// start http daemon
void HttpdStart(void){
uip_init(); //初始化uip的基本属性和参数
httpd_init(); //
}
httpd_init()
函数开始初始化web服务器,并设置端口号80:
// http server init
void httpd_init(void){
fs_init();
uip_listen(HTONS(80));
}
初始化完成之后,使用函数do_http_progress()
监控当前进程的运行状态, WEBFAILSAFE_PROGRESS_START
为启动进程:
// show current progress of the process
do_http_progress(WEBFAILSAFE_PROGRESS_START);
进入for(;;)
循环之后,在HttpdHandler()
函数中调用uip_periodic()
函数,从这个函数开始,启动web服务器,向终端发送数据,进行交互操作:
for(i = 0; i < UIP_CONNS; i++){
uip_periodic(i);
if(uip_len > 0){
uip_arp_out();
NetSendHttpd();
}
}
if(++arptimer == 20){
uip_arp_timer();
arptimer = 0;
}
而实际uip_periodic()
开启了uip TCP的网络进程uip_process
:
#define uip_periodic(conn) do { uip_conn = &uip_conns[conn]; \
uip_process(UIP_TIMER); } while (0)
uip_process()
中进行一些列检测后通过调用回调函数http_appcall
,从主机终端获取通过tftp传输过来的固件数据。
// called for http server app
void httpd_appcall(void)
httpd_appcall()
中httpd_findandstore_firstchunk()
加载固件,
if(httpd_findandstore_firstchunk()){
data_start_found = 1;
} else {
data_start_found = 0;
}
如果能够获取固件,并进行检测和校验成功,则在继续在NetLoopHttpd
中执行下一步,将有效固件NetBootFileXferSize
传入do_http_upgrade
开始执行upgrade
// try to make upgrade!
if(do_http_upgrade(NetBootFileXferSize, webfailsafe_upgrade_type) >= 0){
milisecdelay(500);
do_http_progress(WEBFAILSAFE_PROGRESS_UPGRADE_READY);
milisecdelay(500);
/* reset the board */
do_reset(NULL, 0, 0, NULL);
}
do_http_progress()
通过执行u-boot命令行命令erase擦除数据,cp.d 命令写入数据,至此,upgrade完成,