本文所讲的uboot是基于海思芯片的uboot(如HI3520D)。因为TFTP协议是基于UDP协议,所以本文大部分代码是直接使用TFTP的。在写代码之前复习一下TFTP报文和UDP报文的结构。
每行占64位(bit),共8字节(byte)。
UDP报文:太网包头+IP包头+UDP包头+数据
TFTP报文:太网包头+IP包头+UDP包头+TFTP包头+数据
填充太网包头+IP包头+UDP包头的话直接调用TFTP报文的就可以了。
步骤:
一、像TFTP命令一样写一个UDP命令
我直接在cmd_net.c中添加如下代码(当然也可以新建一个cmd_udp.c,那样的话要在u-boot-2010.06/include/configs/hi3520d.h文件里面添加
#define CONFIG_CMD_UDP,,并且在common目录的Makefile里面添加COBJS-$(CONFIG_CMD_UDP) += cmd_udp.o):
在下面实现netboot_UDP_TCP 函数,记得定义一下这个函数。
static int netboot_UDP_TCP (proto_t proto, cmd_tbl_t *cmdtp, int argc, char *argv[]) { extern ulong upload_addr; extern ulong upload_size; char *s; char *end; int rcode = 0; int size; ulong addr; /* pre-set load_addr */ if ((s = getenv("loadaddr")) != NULL) { load_addr = simple_strtoul(s, NULL, 16); } switch (argc) { case 1: break; case 2: pkt_data = argv[1]; break; default: cmd_usage(cmdtp); show_boot_progress (-80); return 1; } show_boot_progress (80); if ((size = NetLoop(proto)) < 0) { show_boot_progress (-81); return 1; } show_boot_progress (81); /* NetLoop ok, update environment */ netboot_update_env(); /* done if no file was loaded (no errors though) */ if (size == 0) { show_boot_progress (-82); return 0; } /* Loading ok, check if we should attempt an auto-start */ if (((s = getenv("autostart")) != NULL) && (strcmp(s,"yes") == 0)) { char *local_args[2]; local_args[0] = argv[0]; local_args[1] = NULL; printf ("Automatic boot of image at addr 0x%08lX ...\n", load_addr); show_boot_progress (82); rcode = do_bootm (cmdtp, 0, 1, local_args); } if (rcode < 0) show_boot_progress (-83); else show_boot_progress (84); return rcode; }
二、为了清楚一点我在net目录下touch一个udp.c和udp.h文件
第一步之后会跳到net目录下的net.c的NetLoop函数,但是在switch(protocol)里面添加
case UDP:
UdpStart();
break;
UDP协议在net.h里面添加
Udpstart函数在udp.c里面实现,所以接下来会跳到net目录下的udp.c文件里面,udp.c和udp.h代码如下:
udp.c(主要是发送已经填充好的报文,这里发送函数直接调用TFTP协议使用的发送函数):
#include <common.h> #include <command.h> #include <net.h> #include "tftp.h" #include "bootp.h" #include "udp.h" #define TIMEOUT 1000 /* Seconds to timeout for a lost pkt */ #ifndef CONFIG_NET_RETRY_COUNT # define TIMEOUT_COUNT 10 /* # of timeouts before giving up */ #else # define TIMEOUT_COUNT (CONFIG_NET_RETRY_COUNT * 2) #endif static ulong UdpTimeoutMSecs = TIMEOUT; static int UdpTimeoutCountMax = TIMEOUT_COUNT; /* * These globals govern the timeout behavior when attempting a connection to a * TFTP server. UdpRRQTimeoutMSecs specifies the number of milliseconds to * wait for the server to respond to initial connection. Second global, * UdpRRQTimeoutCountMax, gives the number of such connection retries. * UdpRRQTimeoutCountMax must be non-negative and UdpRRQTimeoutMSecs must be * positive. The globals are meant to be set (and restored) by code needing * non-standard timeout behavior when initiating a TFTP transfer. */ ulong UdpRRQTimeoutMSecs = TIMEOUT; int UdpRRQTimeoutCountMax = TIMEOUT_COUNT; static IPaddr_t UdpServerIP; static int UdpServerPort; /* The UDP port at their end */ static int UdpOurPort; /* The UDP port at our end */ static int UdpTimeoutCount; static void UdpSend (void); static void UdpTimeout (void); /**********************************************************************/ static void UdpSend (void) { volatile uchar * pkt; volatile uchar * xp; int len = 0; int uplen=0; volatile ushort *s; /* * We will always be sending some sort of packet, so * cobble together the packet headers now. */ pkt = NetTxPacket + NetEthHdrSize() + IP_HDR_SIZE; len = strlen(pkt_data); memcpy(pkt, pkt_data, len); printf("pkt_data=%s,len=%d\n", pkt_data,len); NetSendUDPPacket(NetServerEther, UdpServerIP, UdpServerPort, UdpOurPort, len); } static void UdpHandler (uchar * pkt, unsigned dest, unsigned src, unsigned len) { ushort proto; ushort *s; int i; printf("receive udp packet\n"); s = (uchar *)pkt; printf("len=%d\n",len); printf("%.*s\n", len, s); } static void UdpTimeout (void) { if (++UdpTimeoutCount > UdpTimeoutCountMax) { puts ("\nRetry count exceeded; starting again\n"); NetStartAgain (); } else { puts ("T "); NetSetTimeout (UdpTimeoutMSecs * CFG_HZ, UdpTimeout); UdpSend (); } } void UdpStart (void) { char *ep; /* Environment pointer */ /* * Allow the user to choose UDP blocksize and timeout. * UDP protocol has a minimal timeout of 1 second. */ if ((ep = getenv("udptimeout")) != NULL) UdpTimeoutMSecs = simple_strtol(ep, NULL, 10); if (UdpTimeoutMSecs < 1000) { printf("UDP timeout (%ld ms) too low, " "set minimum = 1000 ms\n", UdpTimeoutMSecs); UdpTimeoutMSecs = 1000; } UdpServerIP = NetServerIP; #if defined(CONFIG_NET_MULTI) printf ("Using %s device\n", eth_get_name()); #endif UdpTimeoutCountMax = UdpRRQTimeoutCountMax; NetSetTimeout (UdpTimeoutMSecs * CFG_HZ, UdpTimeout); NetSetHandler (UdpHandler); UdpServerPort = 75;//WELL_KNOWN_PORT; UdpTimeoutCount = 0; UdpOurPort = 1024; UdpPktLen = 0; /* zero out server ether in case the server ip has changed */ memset(NetServerEther, 0, 6); UdpSend ();
udp.h:
#ifndef __UDP_H__ #define __UDP_H__ extern void UdpStart (void); #endif
三、代码基本实现完了,最后是测试
在这里千万别像TFTP命令一样用69端口和TFTP服务器测试UDP协议。否则Wireshark抓到的包是TFTP协议的包,因为TFTP服务器把收到的UDP包当成是TFTP包了,并且TFTP包都会有一个操作选项(已经定义好的),所以抓到是unknown的包。
这里最好是使用能接收UDP包的工具当服务器,比如在绿软家园里面下载的YAT、TCP&UDP测试工具 V1.02等等。这次测试使用的是YAT,打开YAT并设置uboot的IP、端口号1024、服务器端口号75
现在在uboot中开始发包,数据为字符串:funanfengfunanfengfunanfeng
Wireshark抓到的包:
YAT服务器端收到的数据:
现在测试在uboot里面接收服务器发过来的数据:
从服务器向uboot发一个字符串:abcdefgabcdefg
Wireshark抓到的包:
在uboot中收到接收到服务器发过来的字符串,但是不知为什么发完数据包之后还发过来一个空包,在其他的测试工具就没这种现象,有待解决。