第十八期 U-Boot 网络原理分析《路由器就是开发板》

上一期在写入flash时用到了tftp服务tftpboot 0x80100000 uboot.bin,也就是通过网络传输协议,这一期我们来分析一下U-Boot是怎么控制hg255d进行网络传输的。
首先,在common/cmd_net.c 文件中找到tftpboot的定义,
U_BOOT_CMD(
	tftpboot,	3,	1,	do_tftpb,
	"tftpboot- boot image via network using TFTP protocol\n",
	"[loadAddress] [bootfilename]\n"
);
这个命令是通过调用do_tftpb函数来完成的
int do_tftpb (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
#ifdef DEBUG
   printf("File: %s, Func: %s, Line: %d\n", __FILE__,__FUNCTION__ , __LINE__);
#endif   
	return netboot_common (TFTP, cmdtp, argc, argv);
}
do_tftpb又是通过调用netboot_common来实现的,看一下它的原型:
static int netboot_common (int proto, cmd_tbl_t *cmdtp, int argc, char *argv[]);
这个函数的第一个参数是网络启动的协议类型,第190行 if ((size = NetLoop(proto)) < 0) 中发现,NetLoop函数来调用相应的协议,跟进NetLoop函数,在函数的注释部分说明为:Main network processing loop. 可见他就是U-Boot实现网络功能的主要函数。这个函数比较长,大约有400行,这里就不贴出来了,这个函数的大体原理就是首先进行一些变量的初始化,读取系统变量中的IP配置信息,然后根据传入参数选择具体的协议,最后在517行进入一个for循环,在循环中可以发现eth_rx()这个函数用来接收数据,果断跟进发现在/net/eth.c中定义,开始分析这个文件:
int eth_send(volatile void *packet, int length)
{
	if (!eth_current)
		return -1;

	return eth_current->send(eth_current, packet, length);
}

int eth_rx(void)
{
	if (!eth_current)
		return -1;

	return eth_current->recv(eth_current);
}
数据的收发都是通过这两个函数来进行的,可以发现网卡设备也就是eth_device在U-Boot中被抽象成一个结构体:
struct eth_device {
	char name[NAMESIZE];
	unsigned char enetaddr[6];
	int iobase;
	int state;

	int  (*init) (struct eth_device*, bd_t*);
	int  (*send) (struct eth_device*, volatile void* pachet, int length);
	int  (*recv) (struct eth_device*);
	void (*halt) (struct eth_device*);

	struct eth_device *next;
	void *priv;
};
这个结构体包含名称,mac地址,寄存器基址,状态信息,和初始化,发送,接收,暂停的函数指针,具体这个结构体是怎么被实例化的我们还要分析U-Boot的初始化过程,在board.c文件第1627行的board_init_r()函数中eth_initialize()函数来实现这个网络设备初始化的操作,这个函数主要作用是根据CONFIG_的配置信息来选择具体的初始化函数,因为RT3052内部集成了ehernet控制器,所以调用rt2880_eth_initialize(bis);函数来初始化网络控制器:
int rt2880_eth_initialize(bd_t *bis)
{
	struct	eth_device* 	dev;
	int	i;
	u32	regValue;

	if (!(dev = (struct eth_device *) malloc (sizeof *dev))) {
		printf("Failed to allocate memory\n");
		return 0;
	}

	memset(dev, 0, sizeof(*dev));

	sprintf(dev->name, "Eth0 (10/100-M)");

	dev->iobase = RALINK_FRAME_ENGINE_BASE;
	dev->init   = rt2880_eth_init;
	dev->halt   = rt2880_eth_halt;
	dev->send   = rt2880_eth_send;
	dev->recv   = rt2880_eth_recv;

	eth_register(dev);
	eth_loopback_mode = 0;
	rt2880_pdev = dev;
	loopback_protect = 0;

	force_queue_n = 3;
	sdp0_alig_16n_x = 0;
	sdp1_alig_16n_x = 0;
	rt2880_eth_initd =0;
	rt2880_size_of_mem = 0;
	rt2880_esram_gear = ESRAM_OFF;
	internal_loopback_test = INTERNAL_LOOPBACK_DISABLE;
	header_payload_scatter_en = DISABLE;
	rt2880_buf_in_esram_en = DISABLE;
	rt2880_desc_in_esram = DISABLE;
	rt2880_sdp0_buf_in_esram_en = DISABLE;
	PktBuf = Pkt_Buf_Pool;
	PKT_HEADER_Buf = PKT_HEADER_Buf_Pool;
	is_internal_loopback_test = 0;
	rt2880_hdrlen = 20;
	NetTxPacket = NULL;
	rt2880_debug_en = DISABLE;
	rx_ring = (struct PDMA_rxdesc *)KSEG1ADDR((ulong)&rx_ring_cache[0]);
	tx_ring0 = (struct PDMA_txdesc *)KSEG1ADDR((ulong)&tx_ring0_cache[0]);

	rt2880_free_buf_list.head = NULL;
	rt2880_free_buf_list.tail = NULL;

	rt2880_busing_buf_list.head = NULL;
	rt2880_busing_buf_list.tail = NULL;

	//2880_free_buf

	/*
	 *	Setup packet buffers, aligned correctly.
	 */
	rt2880_free_buf[0].pbuf = (unsigned char *)(&PktBuf[0] + (PKTALIGN - 1));
	rt2880_free_buf[0].pbuf -= (ulong)rt2880_free_buf[0].pbuf % PKTALIGN;
	rt2880_free_buf[0].next = NULL;

	rt2880_free_buf_entry_enqueue(&rt2880_free_buf_list,&rt2880_free_buf[0]);

#ifdef DEBUG
	printf("\n rt2880_free_buf[0].pbuf = 0x%08X \n",rt2880_free_buf[0].pbuf);
#endif
	for (i = 1; i < PKTBUFSRX; i++) {
		rt2880_free_buf[i].pbuf = rt2880_free_buf[0].pbuf + (i)*PKTSIZE_ALIGN;
		rt2880_free_buf[i].next = NULL;
#ifdef DEBUG
		printf("\n rt2880_free_buf[%d].pbuf = 0x%08X\n",i,rt2880_free_buf[i].pbuf);
#endif
		rt2880_free_buf_entry_enqueue(&rt2880_free_buf_list,&rt2880_free_buf[i]);
	}

	for (i = 0; i < PKTBUFSRX; i++)
	{
		rt2880_free_buf[i].tx_idx = NUM_TX_DESC;
#ifdef DEBUG
		printf("\n rt2880_free_buf[%d] = 0x%08X,rt2880_free_buf[%d].next=0x%08X \n",i,&rt2880_free_buf[i],i,rt2880_free_buf[i].next);
#endif
	}
		
	
	//set clock resolution
	extern unsigned long mips_bus_feq;
	regValue = le32_to_cpu(*(volatile u_long *)(RALINK_FRAME_ENGINE_BASE + 0x0008));
	regValue |=  ((mips_bus_feq/1000000) << 8);
	*((volatile u_long *)(RALINK_FRAME_ENGINE_BASE + 0x0008)) = cpu_to_le32(regValue);
	
	return 1;
}
到这里,如果您一直在跟进,大概就能了解顶层函数是怎样一步一步的最底层的硬件进行控制的,分析U-Boot这样的软件对于将来做架构方面的转型很有帮助,因为U-Boot中的universual就是靠出色的架构来兼容各种硬件设备。
像ethernet这种设备都是分层工作的我们只要控制寄存器就行,具体的phy层次的操作都是有芯片厂商通过硬件有限状态机的模式实现。
#define RALINK_FRAME_ENGINE_BASE 0xB0100000
我们来查看ralink_RT3052的Datasheet的3.17节:
第十八期 U-Boot 网络原理分析《路由器就是开发板》_第1张图片
第十八期 U-Boot 网络原理分析《路由器就是开发板》_第2张图片
关于Frame Engine 这一节的内容还是比较多的,而且比较复杂,如果讲详细,我相信可以出一本书了,我这里讲解一下分析思路,希望大家再进行更深层次的拓展学习。
----------------------------------------------------
SDK下载地址:   https://github.com/aggresss/RFDemo

你可能感兴趣的:(路由器就是开发板)