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函数。

  • 相关阅读:
    Java Web之Servlet入门篇(二)
    『一本通』KMP算法
    P2865 [USACO06NOV]Roadblocks (次短路)
    字符串算法
    『一本通』哈希和哈希表
    『一本通』广搜的优化技巧
    [SCOI2011]糖果(差分约束系统)
    『一本通』二分与三分
    『一本通』贪心
    『P1122』最大子树和
  • 原文地址:https://www.cnblogs.com/cute/p/4711350.html
Copyright © 2011-2022 走看看