u-boot分析三

u-boot分析

Posted by hades on April 28, 2018

u-boot分析三

继续分析,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完成,