以uboot-1.3.3为例,我们来看看其tftp下载实现的方法。
先看发送接受缓存区的内存分布:
32bytes NetTxPacket 1536bytes
|-----------|-------------------------------------|
NetRxPackets[0]|-------------------------------------|
NetRxPackets[1]|-------------------------------------|
NetRxPackets[2]|-------------------------------------|
NetRxPackets[3]|-------------------------------------|
32bytes NetArpWaitTxPacket 1536bytes
|-----------|-------------------------------------|
再来看几个函数的实现:
NetLoop():
设置NetOurEther,NetOurIP,NetourGateWayIp,NetOurSubnetMark,NetOurVLAN,NetOurNativeVlan,NetserveIP.的值,--->TftpStart()发送一个TFTP读请求或者ARP请求--->eth_rx()接受一个网络包--->ArpTimeoutcheck()arp超时检查,
TftpStart():
设置超时处理函数TftpTimeout--->设置接受到一个tftp包的处理函数TftpHander---->服务器端口号设为69--->板子的端口号根据时间随机产生-->TftpState = STATE_RRQ;TftpBlock = 0;TftpBlkSize = TFTP_BLOCK_SIZE;-->TftpSend ()
TftpSend():
设置tftp包头,设为tftp读请求--->NetSendUDPPacket()
RRQ请求的包头:
2 bytes string 1 byte string 1 byte
------------------------------------------------
| Opcode | Filename | 0 | Mode | 0 |
------------------------------------------------
操作码Opcode设为TFTP_RRQ,Filename和0合起来就是个以\0结束的字符串.模式Mode包括了字符串"netascii","octet"或"mail",名称不分大小写。netascii格式数据的主机必须将数据转换为本地格式。octet模式用于传输文件,这种文件在源机上以8位格式存储。同样Mode和0合起来就是个以\0结束的字符串
NetSendUDPPacket():
如果知道服务器的mac,则有NetSetEther()--->NetSetIP()--->eth_send()。否则将先前带发送的数据包复制到NetArpWaitTxPacket中,然后执行一次arp请求。待收到arp应答之后,再填充NetArpWaitTxPacket的各协议头,发送出去。
NetSetEther():
设置帧头,其格式为:
------------------------------------------
|目的mac |源mac |类型 |
------------------------------------------
如果为一般的ip数据包,这类型字段为0x0800 , 如为arp请求/应答,则为0x0806,如为rarp请求应答,则为0x8035。。
如果是要arp请求,则目的mac设为: 0xff:0xff:0xff:0xff:0xff:0xff
帧头占14个字节(以太网封装,RFC894)
NetSetIP():
设置IP首部和UDP首部。IP首部需要源IP和目的IP,UDP首部需要源端口和目的端口。
如果TFTP数据报的长度是奇数,这在数据报后补0,使为偶数,目的是为了好计算校验和。
以下是uboot-1.3.3中NetSetIP函数的片断:
void
NetSetIP(volatile uchar * xip, IPaddr_t dest, int dport, int sport, int len)
{
volatile IP_t *ip = (IP_t *)xip;
/*
* If the data is an odd number of bytes, zero the
* byte after the last byte so that the checksum
* will work.
*/
if (len & 1)
xip[IP_HDR_SIZE + len] = 0;
/*
* Construct an IP and UDP header.
* (need to set no fragment bit - XXX)
*/
ip->ip_hl_v = 0x45; /* IP_HDR_SIZE / 4 (not including UDP) */
ip->ip_tos = 0;
ip->ip_len = htons(IP_HDR_SIZE + len);
ip->ip_id = htons(NetIPID++);
...
...
}
|
用arm-linux-gcc 2.95.3编译器编译通过,运行的时候却卡在了这个函数中,百思不得其解为什么。后来参考kn所做的vivi_tftp的代码,将volatile IP_t *ip = (IP_t *)xip;改为IP_t *ip = (IP_t *)xip;问题解决。。
ArpRequest():
填充一个arp包.
arp包格式:
--------------------------------------------------------------------------------------------------------
|以太网首部|硬件类型|协议类型|硬件地址长度|协议地址长度|op|发送端以太网地址|发送端ip|目的以太网地址|目的IP地址
--------------------------------------------------------------------------------------------------------
NetReceive():
当用eth_rx接受到一个包之后就会调用NetReceive对包进行分析并处理。
通过对以太网帧头的分析得出此包是个IP数据报,arp请求/应答包还是rarp请求/应答包.然后对不同类型的包,有不同的处理方法。
tcp/ip协议结构图
阅读(2076) | 评论(1) | 转发(0) |