uboot ping doesn’t work
Ping doesn't work
Ping from U-Boot to a host should work. Ping from a host to U-Boot should not.
U-Boot uses a polling interface for networking - if you have not run a command, it will not respond to any packets.
uboot-ping代码学习笔记
2011-02-20, root
最近这两天学习了uboot的ping源代码,现在做一下笔记。
uboot :v1.1.6
uboot从start.s启动后会跳转到lib_arm/board.c里的start_armboot函数,从这以后都
是C语言代码。start_armboot做了一些初始化化然后在死循环里调用main_loop函数 :
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {
main_loop ();
}
main_loop是在;common/main.c中定义的,下面是从串中读取命令然后执行的一部分代码:
for (;;) {
len = readline (CFG_PROMPT); //从串口读入命令
flag = 0; /* assume no special flags for now */
if (len > 0)
strcpy (lastcommand, console_buffer);
else if (len == 0)
flag |= CMD_FLAG_REPEAT;
}
if (len == -1)
puts ("<INTERRUPT>/n");
else
rc = run_command (lastcommand, flag); // 执行命令
if (rc <= 0) {
/* invalid command or not repeatable, forget it */
lastcommand[0] = 0;
}
}
run_command首先调用find_command在命令列表里查找到输入的命令:
/* Look up command in command table */
if ((cmdtp = find_cmd(argv[0])) == NULL) {
printf ("Unknown command '%s' - try 'help'/n", argv[0]);
rc = -1; /* give up after bad command */
continue;
}
find_cmd返回的是cmd_tbl_t类型,cmd_tbl_t里有命令对应的执行函数,再调用具体的
执行函数来执行命令具体的操作:
/* OK - call function to do the command */
if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {
rc = -1;
}
uboot所有的命令都定义在commom目录下,ping命令在cmd_net.c文件里定义,定义如下:
U_BOOT_CMD(
ping, 2, 1, do_ping,
"ping/t- send ICMP ECHO_REQUEST to network host/n",
"pingAddress/n"
);
U_BOOT_CMD定义如下:
/*
* Monitor Command
*
* All commands use a common argument format:
*
* void function (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
*/
struct cmd_tbl_s {
char *name; // Command Name
int maxargs; // maximum number of arguments
int repeatable; // autorepeat allowed?
// Implementation function
int (*cmd)(struct cmd_tbl_s *, int, int, char *[]);
char *usage; // Usage message (short)
#ifdef CFG_LONGHELP
char *help; // Help message (long)
#endif
#ifdef CONFIG_AUTO_COMPLETE
// do auto completion on the arguments
int (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
#endif
};
typedef struct cmd_tbl_s cmd_tbl_t;
#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) /
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage}
ping命令定义的展开:
cmd_tbl_t __u_boot_cmd_ping __attribute__ ((unused, section (".u_boot_cmd"))) = {
"ping", // name
2, // maxargs
1, // repeatable
do_ping,// (*cmd)(struct cmd_tbl_s *, int, int, char *[]);
"ping/t- send ICMP ECHO_REQUEST to network host/n", // *usage
"pingAddress/n", // *help
}
从这里可以看出,ping命令其实是调用的do_ping函数来实行具体的ping操作,do_ping的定义如下:
int do_ping (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
if (argc < 2)
return -1;
NetPingIP = string_to_ip(argv[1]);
if (NetPingIP == 0) {
printf ("Usage:/n%s/n", cmdtp->usage);
return -1;
}
if (NetLoop(PING) < 0) {
printf("ping failed; host %s is not alive/n", argv[1]);
return 1;
}
printf("host %s is alive/n", argv[1]);
return 0;
}
执行ping命令:
EmbedSky> ping 192.168.1.1
dm9000 i/o: 0x20000300, id: 0x90000a46
MAC: 0a:1b:2c:3d:4e:5f
ping failed; host 192.168.1.1 is not alive
do_ping首先将192.168.1.1存在NetPingIP变量里边,然后调用NetLoop函数,如果执行成功
显示:
host 192.168.1.1 is alive
如果不成功显示:
host 192.168.1.1 is not alive
然后 退出。
NetLoop主要的工作是打包各命令对应的数据包,通过eth_send函数发送数据包,然后在死循
环里调用eth_rx来接收一个完整的ethernet帧,eth_rx收到的帧会传给NetRecive函数分析处
理。
对于ping命令,NetLoop调用PingStart
static void PingStart(void)
{
#if defined(CONFIG_NET_MULTI)
printf ("Using %s device/n", eth_get_name());
#endif /* CONFIG_NET_MULTI */
NetSetTimeout (10 * CFG_HZ, PingTimeout);
NetSetHandler (PingHandler);
PingSend();
}
PingStart首先设置接收到Ping数据包后调用的处理函数,然后调用PingSeng
发送ICMP数据包。
NetReceive处理接收到的ICMP数据包最后会调用PingHandler函数来结束Ping
操作,PingHandler只是将NetState改为NETLOOP_SUCCESS,在NetLoop里会一直
检测NetState的状态(刚进入NetLoop函数的时候NetState为NETLOOP_CONTINUE),
如果为 NETLOOP_SUCCESS 或NETLOOP_FAIL则NetLoop就会退出:
switch (NetState) {
case NETLOOP_RESTART:
#ifdef CONFIG_NET_MULTI
NetRestarted = 1;
#endif
goto restart;
case NETLOOP_SUCCESS:
if (NetBootFileXferSize > 0) {
char buf[10];
printf("Bytes transferred = %ld (%lx hex)/n",
NetBootFileXferSize,
NetBootFileXferSize);
sprintf(buf, "%lx", NetBootFileXferSize);
setenv("filesize", buf);
sprintf(buf, "%lX", (unsigned long)load_addr);
setenv("fileaddr", buf);
}
eth_halt();
return NetBootFileXferSize;
case NETLOOP_FAIL:
return (-1);
}
PingHandler (uchar * pkt, unsigned dest, unsigned src, unsigned len)
{
IPaddr_t tmp;
volatile IP_t *ip = (volatile IP_t *)pkt;
tmp = NetReadIP((void *)&ip->ip_src);
if (tmp != NetPingIP)
return;
NetState = NETLOOP_SUCCESS;
}
int PingSend(void)
{
static uchar mac[6];
volatile IP_t *ip;
volatile ushort *s;
uchar *pkt;
/* XXX always send arp request */
memcpy(mac, NetEtherNullAddr, 6);
#ifdef ET_DEBUG
printf("sending ARP for %08lx/n", NetPingIP);
#endif
NetArpWaitPacketIP = NetPingIP;
NetArpWaitPacketMAC = mac;
pkt = NetArpWaitTxPacket;
pkt += NetSetEther(pkt, mac, PROT_IP);
ip = (volatile IP_t *)pkt;
/*
* Construct an IP and ICMP 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_NO_UDP + 8);
ip->ip_id = htons(NetIPID++);
ip->ip_off = htons(0x4000); /* No fragmentation */
ip->ip_ttl = 255;
ip->ip_p = IPPROTO_ICMP; /* ICMP */
ip->ip_sum = 0;
NetCopyIP((void*)&ip->ip_src, &NetOurIP); /* already in network byte order */
NetCopyIP((void*)&ip->ip_dst, &NetPingIP); /* - "" - */
ip->ip_sum = ~NetCksum((uchar *)ip, IP_HDR_SIZE_NO_UDP / 2);
s = &ip->udp_src; /* ICMP starts here */
s[0] = htons(0x0800); /* echo-request, code */
s[1] = 0; /* checksum */
s[2] = 0; /* identifier */
s[3] = htons(PingSeqNo++); /* sequence number */
s[1] = ~NetCksum((uchar *)s, 8/2);
/* size of the waiting packet */
NetArpWaitTxPacketSize = (pkt - NetArpWaitTxPacket) + IP_HDR_SIZE_NO_UDP + 8;
/* and do the ARP request */
NetArpWaitTry = 1;
NetArpWaitTimerStart = get_timer(0);
ArpRequest();
return 1; /* waiting */
}
PingSend调用NetSetEther封装ethernet帧的头部,NetSetEther参数:
mac :为目的host的mac,在这里全为0,因为我们只知道目的主机的ip,
现在还不知道它的mac地址
PROT_IP: 说明该ethernet帧里封装的是IP包。ethernet帧分三种类型,根据
ethernet的type来判断是哪种类型。
ethernet帧的封装:
|destination addr| source addr |type| data |crc|
6 6 2 46-1500 4 bytes
type:
PROT_IP 0x0800 /* IP protocol */
PROT_ARP 0x0806 /* IP ARP protocol */
PROT_RARP 0x8035 /* IP ARP protocol */
PingSend再将IP包封装成ICMP包:
ip->ip_p = IPPROTO_ICMP; /* ICMP */
IP包跟ethernet帧一样,也是复用的数据包,所有IP头部用1字节的proto标示来说明
IP包是哪类:
IPPROTO_ICMP 1 /* Internet Control Message Protocol */
IPPROTO_UDP 17 /* User Datagram Protocol */
IPPROTO_TCP 6 /* Transmission Control Protocol */
IPPROTO_IGMP 2 /* Internet Group Message Protocol */
ICMP将该ICMP包类型设置为echo request:
s[0] = htons(0x0800); /* echo-request, code */
封装完IP包和ICMP包后,没有立即将该ethernet帧发出去,因为现在我们只知道目的主机的
IP地址,并不知道它的ethernet地址,所以先调用ArpRequest函数取得目的主机的ethernet
地址,再ping封装的ethernet帧发送出去。
ArpRequest函数将ethernet帧封装为ARP request帧,然后广播该帧。
void ArpRequest (void)
{
int i;
volatile uchar *pkt;
ARP_t *arp;
#ifdef ET_DEBUG
printf ("ARP broadcast %d/n", NetArpWaitTry);
#endif
pkt = NetTxPacket;
pkt += NetSetEther (pkt, NetBcastAddr, PROT_ARP); // PORT_ARP说明该
// 帧为ARP帧
arp = (ARP_t *) pkt;
arp->ar_hrd = htons (ARP_ETHER);// 物理地址类型,在此为ethernet地址
arp->ar_pro = htons (PROT_IP); // 逻辑地址类型,在此为IP地址
arp->ar_hln = 6; // 物理地址类型的长度
arp->ar_pln = 4; // 逻辑地址类型的长度
arp->ar_op = htons (ARPOP_REQUEST);
memcpy (&arp->ar_data[0], NetOurEther, 6); /* source ET addr */
NetWriteIP ((uchar *) & arp->ar_data[6], NetOurIP); /* source IP addr */
for (i = 10; i < 16; ++i) {
arp->ar_data[i] = 0; /* dest ET addr = 0 */ //广播该帧
}
if ((NetArpWaitPacketIP & NetOurSubnetMask) !=
(NetOurIP & NetOurSubnetMask)) {
if (NetOurGatewayIP == 0) {
puts ("## Warning: gatewayip needed but not set/n");
NetArpWaitReplyIP = NetArpWaitPacketIP;
} else {
NetArpWaitReplyIP = NetOurGatewayIP;
}
} else {
NetArpWaitReplyIP = NetArpWaitPacketIP;
}
NetWriteIP ((uchar *) & arp->ar_data[16], NetArpWaitReplyIP); /* dest ip addr */
(void) eth_send (NetTxPacket, (pkt - NetTxPacket) + ARP_HDR_SIZE); //通过底层硬件发送该ethernet帧
}
接收ARP Replay帧和icmp echo replay帧是在NetLoop里的eth_rx进行的,eth_rx收到一个完整的帧后将该帧
传送给NetReceive处理。
NetReceive处理ARP Replay帧:
case ARPOP_REPLY: /* arp reply */
/* are we waiting for a reply */
if (!NetArpWaitPacketIP || !NetArpWaitPacketMAC)
break;
tmp = NetReadIP(&arp->ar_data[6]);
/* matched waiting packet's address */
if (tmp == NetArpWaitReplyIP) {
/* save address for later use */
memcpy(NetArpWaitPacketMAC, &arp->ar_data[0], 6);
/* modify header, and transmit it */
// 将返回的目标主机的mac地址放入PingSend封装的ethernet帧,然后发送该ping帧
memcpy(((Ethernet_t *)NetArpWaitTxPacket)->et_dest, NetArpWaitPacketMAC, 6);
(void) eth_send(NetArpWaitTxPacket, NetArpWaitTxPacketSize);
/* no arp request pending now */
NetArpWaitPacketIP = 0;
NetArpWaitTxPacketSize = 0;
NetArpWaitPacketMAC = NULL;
}
return;
NetReceive处理icmp echo replay帧:
case ICMP_ECHO_REPLY:
/*
* IP header OK. Pass the packet to the current handler.
*/
/* XXX point to ip packet */
(*packetHandler)((uchar *)ip, 0, 0, 0);
return;
只是调用PingHandler函数将NetState设置为NETLOOP_SUCCESS,结束NetLoop循环,
打印ping结果,然后退出do_ping函数。
uboot ping doesn’t work的更多相关文章
- I.MX6 U-Boot ping网络
/********************************************************************* * I.MX6 U-Boot ping网络 * 说明: * ...
- ARM学习日记
2012-05-15 1.ARM开发板环境的搭建,nor启动,通过suppervivi,下载vivi---下载Kernel----下载文件系统,然后Nandflash启动即可. 2.在/etc/ini ...
- u-boot-2012.10移植到AT91RM9200(包括NAND FLASH)
基于中嵌SRM9204 目 录 1 配置 1.1修改顶层Makefile(可选) 1.2配置 1.3下载.运行.测试 2 修改内存配置参数(根据芯片手册修改) 2.1 修改配置参数 2.2 编译 2 ...
- tiny210 u-boot 网络ping不通主机解决方案
站在巨人的肩膀上: http://blog.csdn.net/liukun321/article/details/7438880 http://www.arm9home.net/read.php?ti ...
- 三者互ping,PC,虚拟机,uboot,nfs网络文件系统搭建
要想实现三者互ping,韦老师虽然专门出了视频说明,但是在自己配置过程还是出现了问题,这里记录一下解决办法,虽然我也不知道原因,但是解决了出现的问题也实现了三者互ping. 首先,我的硬件设备是PC通 ...
- u-boot可ping通PC,PC不可ping通u-boot
http://blog.csdn.net/ce123_zhouwei/article/details/7339134 开发板运行U-Boot,在终端下使用Ping命令是能Ping通PC机,但PC机Pi ...
- uboot能ping通本机无法ping通本机上搭建的虚拟机
注意 转载请注明出处:https://www.cnblogs.com/dakewei 一.背景 1.1 uboot不能被其它主机ping通,这是由于uboot没有对其它主机发送过来的arp包进行响应, ...
- 主机、虚拟机、开发板(u-boot)之间的连接 - ping测试
1.设置主机的IP地址(这里注意,设置一定要设置网线宽带IP,不要选成无线网络的) 查看重点是否本地以太网卡(Realtek PCIe……) 2.修改本地连接3个IP地址,一定主机.虚拟机.开发板 三 ...
- uboot
******************************************day:2014/10/14**************************uboot************* ...
随机推荐
- 探讨:你真的会用Android的Dialog吗?
一个Bug前几日出现这样一个Bug是一个RuntimeException,详细信息是这样子的: 复制代码代码如下: java.lang.IllegalArgumentException: View n ...
- MySQL数据库行去重复
1.创立数据表
- VC++ 实现窗口抖动
RECT rect; int x, y, nWidth, nHeight; GetWindowRect(&rect); x = rect.left; y = rect.top; nWidth ...
- linux中,如何设置每隔2个小时就执行一次某个脚本?
需求描述: 今天同事问了一个linux上crontab定时任务的问题,说,如何调整一个定时任务每2个小时 执行一次,在此记录下. 操作过程: 1.通过以下的方式设置,每2个小时执行一次脚本 */ * ...
- 第四章 Spring.Net 如何管理您的类___让对象了解自己的容器
我们在开发中,经常需要让对象了解自己所在的容器的信息,例如,有时我们需要让对象知道,对象所在容器的引用是什么,或者是对象在容器中的名称是什么 .Spring.Net 中提供了两个接口,我们使用这两个接 ...
- Nginx 链接
Nginx反向代理以及负载均衡配置:http://www.cnblogs.com/Miss-mickey/p/6734831.html
- python2.0_s12_day13_javascript&Dom&jQuery
今天主要内容:JavaScriptDomjQuery http://www.cnblogs.com/wupeiqi/articles/5369773.html 今天主要内容大致了解:javascrip ...
- Python 入门(九)迭代
什么是迭代 在Python中,如果给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历我们成为迭代(Iteration). 在Python中,迭代是通过 for ...
- list的下标【python】
转自:http://www.cnblogs.com/dyllove98/archive/2013/07/20/3202785.html list的下表从零开始,和C语言挺类似的,但是增加了负下标的使用 ...
- MongoDB 用户角色
Read:允许用户读取指定数据库 readWrite:允许用户读写指定数据库 dbAdmin:允许用户在指定数据库中执行管理函数,如索引创建.删除,查看统计或访问system.profile user ...