上一期在写入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节:
关于Frame Engine 这一节的内容还是比较多的,而且比较复杂,如果讲详细,我相信可以出一本书了,我这里讲解一下分析思路,希望大家再进行更深层次的拓展学习。
----------------------------------------------------
SDK下载地址: https://github.com/aggresss/RFDemo