zoukankan      html  css  js  c++  java
  • 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函数。

  • 相关阅读:
    485串口接线
    mvc3 升级mvc5
    VB连接ACCESS数据库,使用 LIKE 通配符问题
    VB6 读写西门子PLC
    可用的 .net core 支持 RSA 私钥加密工具类
    解决 Win7 远程桌面 已停止工作的问题
    解决 WinForm 重写 CreateParams 隐藏窗口以后的显示问题
    解决安装 .net framework 发生 extracting files error 问题
    CentOS7 安装配置笔记
    通过特殊处理 Resize 事件解决 WinForm 加载时闪烁问题的一个方法
  • 原文地址:https://www.cnblogs.com/cute/p/4711350.html
Copyright © 2011-2022 走看看