U-boot 没有 TCP 协议栈,不支持 TCP(提出要在 U-boot 里面支持 TCP 的协议的 PM 你给我出去)。但是UDP 还是有的。使用 U-boot 配合 UDP 可以做很多底层的功能。甚至我以前做过的项目中,计划在产品生产的时候,先对产品中的 NOR-Flash 编程,然后通过 NOR-Flash 中的 U-boot 来烧写 NAND-Flash,这样可以在产品早期节省一笔 NAND 烧录器的开支。
本文章没啥参考资料,完全是看着U-Boot的代码写出来的。以下说明具体的修改过程。
本文章采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。
原文发布于:https://segmentfault.com/a/1190000005273491,也是作者本人的专栏。
准备用于NetLoop的软件程序
要给U-Boot添加一个UDP处理工具,那么首先应该准备用于这个程序的.c和.h文件。比如我就写了amc_udp.c
和amc_udp.h
准备UDP send函数
如何构建发送函数和接口视需求决定。但最终都是需要在函数内调用这个U-Boot API:
代码语言:txt复制NetSendUDPPacket(uchar *ether,
IPaddr_t dest,
int dport,
int sport,
int len);
以下是各参数说明:
- ether:目标的以太网地址。如果是广播,则使用全局变量
NetBroadcastAddr
;如果未知,则指定全零的char[6]={0,0,0,0,0,0}
- dest:目标的IP地址。如果是广播,则指定0;如果是单播,则制定一个ulong值。可以指定已经设置好了的全局变量
NetServerIP
- dport:包发送的目的端口,即远端端口
- sport:不是“sport”,而是“source-port”。包发送的源端口
- len:UDP包正文的长度。发送之前需要事先设置好正文,参见下文说明
上面提到了发送之前必须设置好正文,那么正文在哪呢?正文在net.c的一个全局变量中,应这样获得:
代码语言:txt复制uchar *context = (uchar *)(NetTxPacket NetEthHdrSize() IP_HDR_SIZE);
准备UDP Timeout的处理函数
超时函数貌如static void amc_udp_timeout()
。主要做一些超时时需要处理,然后设置
NetState = NETLOOP_FAIL
然后返回,这样NetLoop
会提示错误退出。
准备接收处理函数
接收函数形式如:
代码语言:txt复制static void amc_udp_handler(uchar *pkt, unsigned dest, unsigned srt, unsigned len);
下面列出可以在函数中获得的信息:
- IP报头:
IP_t *ipPky = pkt - (IP_HDR_SIZE);
这一句将pkt所代表的正文前推一段距离,以获得IP报文头,此时你可以完整获取报文信息,比如:
IPAddr_t ipFrom = ipPkg->ip_src; // 可以用于UDP response
而其他的一些信息,参见IP报文的格式可知。
- 端口信息:dest表示目标port,可就是远端发往本地的port;相对应地,
src
代表远端port。 - IP报文正文:pkt本身就是,使用len获得长度
- 完成处理:NetLoop的完成处理是看全局变量NetState的。一般设置为NETLOOP_FAIL或NETLOOP_SUCCESS都导导致NetLoop()结束。其他的暂时未研究。
准备NetLoop()调用接口
撰写一个诸如void amc_udp_start()
的函数,开始整个功能。在开始之前,需要以下两句:
NetSetHandler(amc_udp_handler);
NetSetTimeout(AMC_UDP_TIMEOUT_SEC * CFG_HZ, amc_udp_timeout);
配置好handler和timeout回调之后,就可以send udp了。
另:传输过程中,建议设置NetBootFileXferSize
代表传输大小,也就是NetLoop的返回值。
修改net.c和net.h
首先要在net.h中添加一个协议名,如下“AMCUDP”:
代码语言:txt复制typedef enum {
BOOTP, RARP, ARP, TFTP, DHCP, PING, DNS, NFS,
CDP, NETCONS, SNTP, AMCUDP
} proto_t;
然后在NetLoop
中添加执行分支:
......
switch (protocol) {
case TFTP:
...
break;
......
case AMCUDP:
amc_udp_start();
break;
default:
break;
}
......
后面U-boot自然会按照你写的函数处理网络接收和相应的动作。
修改UDP checksum
在net.c中搜索if(0 == strcmp(getenv("udpsum"), "on"))
,或者是直接搜udpsum
可以找到UDP校验的开关。这里建议改为强制打开。
有一些U-boot是这样写的:
代码语言:txt复制ip->xsum = ~NetCksum((uchar *)ip, IP_HDR_SIZE_NO_UDP / 2);
启动自定义网络通信
NetLoop()函数的传入参数非常少,所以经常需要使用别的方法/函数或者是全局变量来配置。配置完成之后再开始NetLoop()。调用NetLoop监听的方法为:
代码语言:txt复制netLoopRet = NetLoop(AMCUDP);
返回值小于0代表失败,可以重试
关于ARP
理论上,当调用NetSendUDPPacket
时,如果传入的MAC地址为全0的话,U-boot会自动完成ARP过程之后再发出自定义的ARP包。但我手头项目的U-Boot代码不是这样的,它直接获得MAC地址之后就什么都不做了,这可能是一个未完成的bug。
其实是可以解决这个问题嗒。修改方法参见下一篇文章
本文章采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。
原文发布于:https://segmentfault.com/a/1190000005273491,也是作者本人的专栏。