浅析uboot网络程序结构

这篇文章主要讲解uboo/net目录下的部分源代码。主要是 net.c,eth.c,ip3912.c 中的代码。本例用的是xxxx公司yyyy系列的zzzz的CPU, 网卡是IP173(和IP3912兼容)。

本文主要分三部分 网口设备的检测,网口设备的注册,应用程序(ping)的执行流程

(一) 检测网口设备

先从Arch/arm/lib/board.c讲起,uboot执行完汇编程序后,会跳转到该文件中的start_armboot函数。该函数先会为全局变量gd分配空间,并清零。然后执行函数指针数组init_sequence里的各种初始化函数。初始化函数一般都是和具体的开发板相关联的,所以这些函数的源码是在board目录下,本例就是在borad/xxxx/yyyy下。

init_sequence数组中有个指针指向board_init,一般可以board_init函数内,初始化CPU和IP173相连的引脚,reset网卡后,可以通过读写CPU的相关寄存器向IP173发送读写命令来检测IP173是否正常。

例如:IP173寄存器2和3是芯片ID寄存器(5个PHY共用)(见图一),可以通过读写寄存器 2和3来判断IP173是否存在。

具体检测函数如下:

#define CONFIG_IP3912_ETN1_BASE	 0xC1600000   //见图二
#define CONFIG_IP3912_ETN2_BASE	 0xC1700000
 
#define ETN1_BASE	 CONFIG_IP3912_ETN1_BASE
#define ETN2_BASE	 CONFIG_IP3912_ETN2_BASE
 
#define ETN_MAC1	 0x0000
#define ETN_MAC2	 0x0004
#define ETN_IPGT	     0x0008
#define ETN_IPGR	     0x000c
#define ETN_CLRT	 0x0010
#define ETN_MAXF	 0x0014
#define ETN_SUPP	 0x0018
#define ETN_TEST	 0x001c
#define ETN_MCFG	 0x0020
#define ETN_MCMD	 0x0024
#define ETN_MADR	 0x0028
 
 
#define VEGA_IP173_PHY_ADDR	 0x01
#define ICPLUS_IP173T_PHYID1	 0x02
#define ICPLUS_IP173T_PHYID2	 0x03
#define ICPLUS_OUI_MASK	 0x0000ffff
#define ICPLUS_OUI	 0x90c3
 
int board_init(void)
{
    ...
    board_detect();
    ...
}
void board_detect(void)
{
    init_etn0();
    detect_IC_PLUS_173T(VEGA_IP173_PHY_ADDR);
}
 
static int detect_IC_PLUS_173T(int addr)
{
    u32 phyid = 0;
    u16 reg = 0;
 
    clear_gpioc(28); //IP173 reset引脚
    udelay(12000);
    udelay(12000);
    udelay(12000);
 
    set_gpioc(28);  //IP173 reset引脚
    udelay(1000);
    udelay(1000);
    udelay(1000);
 
    reg = detect_phy_read(addr,ICPLUS_IP173T_PHYID1); // 读ID1寄存器
    phyid = (u32)reg << 6;
    reg = detect_phy_read(addr, ICPLUS_IP173T_PHYID2); // 读ID2寄存器
    phyid |= ((u32)reg)>>10;
    phyid &= ICPLUS_OUI_MASK;
    /* IC+ IP173T */
    printf("phyid = 0x%x
",phyid );
    if (phyid == ICPLUS_OUI)
        return 1;
    return 0;
}
static void detect_phy_wait(void)
{
        int i, status;
        for (i = 0; i < 1000; i++) {
        status = readl((void *)(ETN1_BASE + ETN_MIND)) & 0x7; //
        if (!status)
            return;
        udelay(1);
    }
}
 
static u16 detect_phy_read(u8 address, u8 reg)
{
    u16 value;
    writel((address << 8) | reg, (void *)(ETN1_BASE + ETN_MADR)); //PHY地址右移或上
                                                            //reg地址
    writel(0x00000001, (void *)(ETN1_BASE + ETN_MCMD));
    detect_phy_wait();
 
    value = readl((void *)(ETN1_BASE + ETN_MRDD));
    writel(0x00000000, (void *)(ETN1_BASE + ETN_MCMD));
    return value;
}
 int detect_phy_write(u8 address, u8 reg,int value)
{
    writel(address << 8 | reg,(void *)(ETN1_BASE + ETN_MADR));
    __raw_writel(value, (void *)(ETN1_BASE + ETN_MWTD));
    detect_phy_wait();
}

解释

init_etn0();主要工作是初始化CPU Ethernet Mac模块,GPIO口等等.

detect_IC_PLUS_173T检测IP173,成功返回1,反之返回0.

读寄存器流程:

将PHY地址右移或上地址写入地址寄存器(见图三),因为zzzz有两种读,循环读和单次读,所以需要写1到命令寄存器。等待读完成。从读寄存器读出值。

写寄存器流程:

将PHY地址右移或上地址写入地址寄存器,将要写的值写入写寄存器。等待写完成。

图(一)

图(二) CPU Ethernet Mac 模块的寄存器(部分)

图(三)

(二) 向网口设备虚拟层eth_device注册各类操作函数和数据。

检测到IP173之后,start_armboot会继续完成其他初始化工作,如FLASH,SDRAM,串口、中断、环境变量等等,期间会设置IP地址

gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");

然后调用eth_initialize()函数。之后等待用户输入命令或者启动kernel 。

eth_initialize()大致流程是调用具体的网卡驱动程序(如ip3912_send,ip3912_recv)来初始化网口设备虚拟层的框架(eth_device->send,eth_device->recv),这些程序为应用程序如ping、tftpboot提供服务(如PingSend,TftpSend等)

eth_initialize()位于net/eth.c文件内,eth.c就是网口设备虚拟层的源码,其提供eth_initialize,eth_register,eth_get_dev,eth_init,eth_halt,eth_send,eth_rx,eth_receive,eth_try_another,

eth_set_current等函数。

eth.c文件被#ifdef CONFIG_NET_MULTI分成两部分,关于CONFIG_NET_MULTI的含义参考附录一。

eth.c的主要数据结构如下:

struct eth_device {
    char name[NAMESIZE]; //设备名
    unsigned char enetaddr[6];  // mac 地址
    int iobase;
    int state; 
    int  (*init) (struct eth_device*, bd_t*);
    int  (*send) (struct eth_device*, volatile void* packet, int length);
    int  (*recv) (struct eth_device*);
    void (*halt) (struct eth_device*);
    #ifdef CONFIG_MCAST_TFTP
        int (*mcast) (struct eth_device*, u32 ip, u8 set);
    #endif
    int  (*write_hwaddr) (struct eth_device*);  //设置MAC的函数
    struct eth_device *next; // 指向下一个网口设备
    void *priv;
};


当要支持多播TFTP时,必须开启CONFIG_MCAST_TFTP,并定义mcast()函数(见readme)。

下面来具体看下 eth_initialize函数

int eth_initialize(bd_t *bis)
{
    unsigned char env_enetaddr[6];
    int eth_number = 0;
 
    eth_devices = NULL;
    eth_current = NULL;  //注释1
 
    show_boot_progress (64);
    
    miiphy_init(); //注释2
 
    //设置 eth_devices =eth_current = netdev, netdev含有IP3912的 init,send ,recv ,priv等  
    //信息,也会设置 mii_devs和 current_mii 
    if (board_eth_init(bis) < 0)	 //注释3 
        cpu_eth_init(bis);
    if (!eth_devices) {
        puts ("No ethernet found.
");
        show_boot_progress (-64);
    } else {
        struct eth_device *dev = eth_devices;
        char *ethprime = getenv ("ethprime"); 
        show_boot_progress (65);
    do { 
         // 比较dev->enetaddr中MAC地址和environment中的MAC地址是否一致,
         // 不一致则调用dev->write_hwaddr 函数来修改MAC地址,以environment中的  
         // 为准,如果dev->write_hwaddr 为空,或者设置了环境变量ethmacskip就会跳 
         //过该步骤。如果有多个网口设备,会循环执行上述步骤。
         if (eth_number)
             puts (", ");
         printf("%s", dev->name);
         if (ethprime && strcmp (dev->name, ethprime) == 0) {
            eth_current = dev;
            puts (" [PRIME]");
        }
        if (strchr(dev->name,  ))
            puts("
Warning: eth device name has a space!
");
 
        eth_getenv_enetaddr_by_index(eth_number, env_enetaddr);
 
        if (memcmp(env_enetaddr, "", 6)) {
            if (memcmp(dev->enetaddr, "", 6) &&
               memcmp(dev->enetaddr, env_enetaddr, 6))
            {
                printf ("
Warning: %s MAC addresses dont match:
",dev->name);
                printf ("Address in SROM is         %pM
",dev->enetaddr);
                printf ("Address in environment is  %pM
",env_enetaddr);
            }
            memcpy(dev->enetaddr, env_enetaddr, 6);
        }
        if (dev->write_hwaddr &&
            !eth_mac_skip(eth_number) &&
            is_valid_ether_addr(dev->enetaddr)) {
                 dev->write_hwaddr(dev);
        }
        eth_number++;
        dev = dev->next;
    } while(dev != eth_devices);
    /* update current ethernet name */
    if (eth_current) {
        char *act = getenv("ethact");
        if (act == NULL || strcmp(act, eth_current->name) != 0)
            setenv("ethact", eth_current->name);
        } else
            setenv("ethact", NULL);
         putc (
);
    }
    return eth_number;
}


执行完该函数后,eth_current指向正在使用的eth_device,该结构体里有init,send,recv 等函数以及MAC地址enetaddr,状态state,设备名name等等。如果有多个网口设备,则他们的eth_device会依次存放在eth_device->next所指向的链表里。

浏览IP3912.c会发现若要编写网口的驱动程序,需要编写ip3912_eth_initialize初始化函数,用来向网口设备虚拟层注册,编写ip3912_halt,ip3912_recv,ip3912_send,ip3912_init等

网口设备虚拟层eth_device所需要的函数。若需要注册MII设备,则还需要编写ip3912_miiphy_write ,ip3912_miiphy_read等函数。

注释1

eth_current比较重要,因为tftpsend等函数最终会调用该变量指向的eth_device中的函数。

int eth_send(volatile void *packet, int length)
{
    if (!eth_current)
        return -1;
    return eth_current->send(eth_current, packet, length);
}


注释2

void miiphy_init(void)
{
    INIT_LIST_HEAD (&mii_devs);
    current_mii = NULL;
}
struct mii_dev {
    struct list_head link;
    const char *name;
    int (*read) (const char *devname, unsigned char addr,unsigned char reg, unsigned short *value);
    int (*write) (const char *devname, unsigned char addr,
};

IP3912 属于 PHY 层,其驱动程序会调用 miiphy_register  注册一个 mii_dev

注释3

board_eth_init() 是属于具体开发板范畴,本例位于board/dspg/firetux/firetux.c内,

主要工作是调用板子所用网口芯片(IP3912)的初始化函数。

int board_eth_init(bd_t *bis)
{
    ...
    ip3912_miiphy_initialize(gd->bd);
    ...
    ip3912_eth_initialize(0,CONFIG_IP3912_ETN1_BASE,CONFIG_IP3912_ETN1_BASE, 0x0, 1);
    ...
    setenv("ethact", "ETN1");
    eth_set_current(); //一般会在ip3912_eth_initialize这些具体设备的初始化函数中调用。
}

int ip3912_miiphy_initialize(bd_t *bis)
{
    miiphy_register("ip3912", ip3912_miiphy_read, ip3912_miiphy_write);
    return 0;
}
void miiphy_register(const char *name,
      int (*read) (const char *devname, unsigned char addr,
   unsigned char reg, unsigned short *value),
      int (*write) (const char *devname, unsigned char addr,
    unsigned char reg, unsigned short value))
{
    ...
    //检查是否已经注册了一个同名的mii设备
    new_dev = miiphy_get_dev_by_name(name, 1);
    // 是则直接退出
    if (new_dev) { 
        printf("miiphy_register: non unique device name %s
", name);
        return;
    }
    // 反之,分配一个新的MII设备
    name_len = strlen (name);
    new_dev =  (struct mii_dev *)malloc (sizeof (struct mii_dev) + name_len + 1);
    //随后初始化这个新的MII设备, 如read ,write函数
    memset (new_dev, 0, sizeof (struct mii_dev) + name_len);
 
    /* initalize mii_dev struct fields */
    INIT_LIST_HEAD (&new_dev->link);
    new_dev->read = read;
    new_dev->write = write;
    new_dev->name = new_name = (char *)(new_dev + 1);
    strncpy (new_name, name, name_len);
    new_name[name_len] = ;
    // 将新的MII设备 添加到 mii_devs 列表中,并设置current_mii 
    // mii_devs 和current_mii 在eth_initialize()中初始化
    list_add_tail (&new_dev->link, &mii_devs);
    if (!current_mii)
    current_mii = new_dev;
}

接着分析函数 ip3912_eth_initialize (),其主要数据结构是
struct ip3912_device {
    unsigned int	 etn_base;
    unsigned int	 phy_base;
    unsigned char	 nr;
    unsigned char	 phy_addr;
    unsigned char	 autonegotiate;
    unsigned char	 speed;
    unsigned char	 duplex;
    unsigned char	 rmii;
 
    const struct device *  dev;
    struct eth_device *	   netdev;
};


可以看到ip3912_device包含了基本的eth_device结构。

int ip3912_eth_initialize(unsigned char nr, unsigned int etn_base, unsigned int phy_base, unsigned char phy_addr, unsigned char rmii)
{
    struct ip3912_device *ip3912; 
    struct eth_device *netdev;
 
    // 分配 eth_device 和 ip3912_device设备所需内存,并初始化。
    netdev = malloc(sizeof(struct eth_device));
    ip3912 = malloc(sizeof(struct ip3912_device));
    if ((!ip3912) || (!netdev)) {
        printf("Error: Failed to allocate memory for ETN%d
", nr + 1);
        return -1;
    }
 
    memset(ip3912, 0, sizeof(struct ip3912_device));
    memset(netdev, 0, sizeof(struct eth_device));
 
    //初始化具体网口设备的私有数据
    ip3912->nr = nr;
    ip3912->etn_base = etn_base;
    ip3912->phy_base = phy_base;
    ip3912->phy_addr = phy_addr;
    ip3912->autonegotiate = 0;
    ip3912->rmii = rmii;
    ip3912->speed = 0;
    ip3912->duplex = 0;
    ip3912->netdev = netdev;
 
    // 用具体网口设备IP3912的操作函数来初始化网口设备虚拟层netdev,
    // 注意最后一项
    sprintf(netdev->name, "ETN%d", nr + 1);
    netdev->init = ip3912_init;
    netdev->send = ip3912_send;
    netdev->recv = ip3912_recv;
    netdev->halt = ip3912_halt;
    netdev->priv = (void *)ip3912; 
 
    //将eth_current 设置为新的netdev,并设置“ethact”,并设置状态state = ETH_STATE_INIT
    eth_register(netdev); // 注释3.1 
    eth_set_current();
    ip3912_macreset(); // 初始化CPU Ethernet Mac模块 
    /* we have to set the mac address, because we have no SROM */
    //读取环境变量,设置MAC地址
    ip3912_setmac(netdev); // 注释3.2  
    return 0;
}


注释3.1

int eth_register(struct eth_device* dev)
{
    struct eth_device *d;
 
    if (!eth_devices) {
        eth_current = eth_devices = dev;
        {
        char *act = getenv("ethact");
        if (act == NULL || strcmp(act, eth_current->name) != 0)
        setenv("ethact", eth_current->name);
        }
    } else {
        for (d=eth_devices; d->next!=eth_devices; d=d->next)
        ;
        d->next = dev;
    }
 
    dev->state = ETH_STATE_INIT;
    dev->next  = eth_devices;
    return 0;
}

注释3.2

void ip3912_setmac(struct eth_device *netdev)
{
    struct ip3912_device *ip3912;
    unsigned char i, use_etn1addr = 0;
    char *mac_string, *pmac, *end;
    char tmp[18];
 
    ip3912 = netdev->priv;
 
    mac_string = getenv("ethaddr");
    if (ip3912->nr) {
        /* we use ETN2 */
        mac_string = getenv("eth1addr");
        if (!mac_string) {
            mac_string = getenv("ethaddr");
            use_etn1addr = 1;
        }
    }
    pmac = mac_string;
    for (i = 0; i < 6; i++) {
        netdev->enetaddr[i] = pmac ? simple_strtoul(pmac, &end, 16) : 0;
        if (pmac)
            pmac = (*end) ? end + 1 : end;
    }
 
    if (use_etn1addr) {
        /* flip last bit of mac address */
        debug("ip3912_setmac %s flipping last bit
", netdev->name);
        if (netdev->enetaddr[5] & 1)
            netdev->enetaddr[5] &= 0xfe;
        else
            netdev->enetaddr[5] |= 0x01;
            sprintf(tmp, "%02X:%02X:%02X:%02X:%02X:%02X",
            netdev->enetaddr[0], netdev->enetaddr[1],
            netdev->enetaddr[2], netdev->enetaddr[3],
            netdev->enetaddr[4], netdev->enetaddr[5]);
            setenv("eth1addr", tmp);
            mac_string = tmp;
    }
    debug("ip3912_setmac set %s to address %s
", netdev->name, mac_string);
 
    writel((netdev->enetaddr[5] << 8) | netdev->enetaddr[4],
        (void *)(ip3912->etn_base + ETN_SA0));
    writel((netdev->enetaddr[3] << 8) | netdev->enetaddr[2],
        (void *)(ip3912->etn_base + ETN_SA1));
    writel((netdev->enetaddr[1] << 8) | netdev->enetaddr[0],
        (void *)(ip3912->etn_base + ETN_SA2));
}

 

(三)应用层执行流程分析

Uboot支持的网络协议有以下几种

typedef enum

{ BOOTP, RARP, ARP, TFTP, DHCP, PING, DNS, NFS, CDP, NETCONS, SNTP } proto_t;

先看下比较简单的ping,

U_BOOT_CMD(
    ping,	2,	1,	do_ping,
    "send ICMP ECHO_REQUEST to network host",
    "pingAddress"
);
int do_ping (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
    if (argc < 2)
        return -1;
    NetPingIP = string_to_ip(argv[1]);
    if (NetPingIP == 0)
        return cmd_usage(cmdtp);
    if (NetLoop(PING) < 0) {
        printf("ping failed; host %s is not alive
", argv[1]);
        return 1;
    }
    printf("host %s is alive
", argv[1]);
return 0;
}


NetLoop像是一个应用层和网口设备虚拟层之间的一个接口,很多协议的处理都要经过该函数,如do_ping()函数中的NetLoop(PING),do_cdp()函数中的NetLoop(CDP), do_sntp()函数中的NetLoop(SNTP),.do_dns()函数中的NetLoop(DNS)。另外do_bootp() 会调用 netboot_common (BOOTP, cmdtp, argc, argv); do_tftpb() 会调用 netboot_common (TFTP, cmdtp, argc, argv);do_rarpb() 会调用netboot_common (RARP, cmdtp, argc, argv);do_dhcp()会调用netboot_common(DHCP, cmdtp, argc, argv);do_nfs ()会调用 netboot_common(NFS, cmdtp, argc, argv);而netboot_common( (proto_t proto ,.....)仍会调用 NetLoop(proto)。

do_ping()函数从命令行读取IP地址后,存放在NetPingIP中。

接着执行NetLoop(PING)函数

下面分析int NetLoop(proto_t protocol)函数。

#ifdef CONFIG_SYS_RX_ETH_BUFFER
# define PKTBUFSRX	CONFIG_SYS_RX_ETH_BUFFER
#else
# define PKTBUFSRX	4
#endif
#define PKTALIGN	32
#define PKTSIZE	 1518
#define PKTSIZE_ALIGN	 1536
 
//静态分配一个buffer
volatile uchar	PktBuf[(PKTBUFSRX+1) * PKTSIZE_ALIGN + PKTALIGN];
 
volatile uchar *NetRxPackets[PKTBUFSRX]; /* Receive packets	 */
 
volatile uchar *NetTxPacket = 0;	/* THE transmit packet	 */
 
 
 
Int NetLoop(proto_t protocol)
{
    bd_t *bd = gd->bd; #ifdef CONFIG_NET_MULTI NetRestarted = 0; NetDevExists = 0; #endif /* XXX problem with bss workaround */ NetArpWaitPacketMAC = NULL; NetArpWaitTxPacket = NULL; NetArpWaitPacketIP = 0; NetArpWaitReplyIP = 0; NetArpWaitTxPacket = NULL; NetTxPacket = NULL; NetTryCount
 = 1; // 初始化各类发送,接收buffer指针。 if (!NetTxPacket) { int i; /* * Setup packet buffers, aligned correctly. */ NetTxPacket = &PktBuf[0] + (PKTALIGN - 1); NetTxPacket -= (ulong)NetTxPacket % PKTALIGN; for (i = 0; i < PKTBUFSRX; i++) { NetRxPackets[i] = NetTxPacket
 + (i+1)*PKTSIZE_ALIGN; } } if (!NetArpWaitTxPacket) { NetArpWaitTxPacket = &NetArpWaitPacketBuf[0] + (PKTALIGN - 1); NetArpWaitTxPacket -= (ulong)NetArpWaitTxPacket % PKTALIGN; NetArpWaitTxPacketSize = 0; } eth_halt(); //注释1 #ifdef CONFIG_NET_MULTI eth_set_current();
 //设置环境变量"ethact"。如果已经设置,就直接退出。 #endif if (eth_init(bd) < 0) { //注释2 eth_halt(); return(-1); } restart: #ifdef CONFIG_NET_MULTI memcpy (NetOurEther, eth_get_dev()->enetaddr, 6); // 设置 NetOurEther #else eth_getenv_enetaddr("ethaddr", NetOurEther); #endif NetState
 = NETLOOP_CONTINUE; // 设置 NetOurEther /* * Start the ball rolling with the given start function. From * here on, this code is a state machine driven by received * packets and timer events. */ // 设置 NetOurIP NetOurGatewayIP NetOurSubnetMask NetServerIP NetOurNativeVLAN
 // NetOurVLAN NetOurDNSIP NetInitLoop(protocol); switch (net_check_prereq (protocol)) { // 检查所需要的参数是否都有合适的值 case 1: /* network not configured */ eth_halt(); return (-1); #ifdef CONFIG_NET_MULTI case 2: /* network device not configured */ break; #endif /* CONFIG_NET_MULTI
 */ case 0: #ifdef CONFIG_NET_MULTI NetDevExists = 1; #endif switch (protocol) { case TFTP: /* always use ARP to get server ethernet address */ TftpStart(); break; #if defined(CONFIG_CMD_DHCP) case DHCP: BootpTry = 0; NetOurIP = 0; DhcpRequest(); /* Basically
 same as BOOTP */ break; #endif case BOOTP: BootpTry = 0; NetOurIP = 0; BootpRequest (); break; case RARP: RarpTry = 0; NetOurIP = 0; RarpRequest (); break; #if defined(CONFIG_CMD_PING) case PING: // 注释3,执行ping 的实质函数。会发送ECHO REQUEST 数据包,并设置接收数据包时的处理函数和超时函数。
 PingStart(); break; #endif #if defined(CONFIG_CMD_NFS) case NFS: NfsStart(); break; #endif #if defined(CONFIG_CMD_CDP) case CDP: CDPStart(); break; #endif #ifdef CONFIG_NETCONSOLE case NETCONS: NcStart(); break; #endif #if defined(CONFIG_CMD_SNTP) case SNTP:
 SntpStart(); break; #endif #if defined(CONFIG_CMD_DNS) case DNS: DnsStart(); break; #endif default: break; } NetBootFileXferSize = 0; break; } /* * Main packet reception loop. Loop receiving packets until * someone sets `NetState to a state that terminates.
 */ for (;;) { WATCHDOG_RESET();/* * Check the ethernet for a new packet. The ethernet * receive routine will process it. */ eth_rx(); // 循环接受数据包,注释4/* * Abort if ctrl-c was pressed. */ if (ctrlc()) { eth_halt(); puts ("
Abort
"); return (-1); } ArpTimeoutCheck();
 /* * Check for a timeout, and run the timeout handler * if we have one. */ if (timeHandler && ((get_timer(0) - timeStart) > timeDelta)) { thand_f *x; x = timeHandler; timeHandler = (thand_f *)0; (*x)(); } switch (NetState) { case NETLOOP_RESTART: #ifdef CONFIG_NET_MULTI
 NetRestarted = 1; #endif goto restart; case NETLOOP_SUCCESS: //若有接收文件,则打印文件大小,并设置环境变量filesize,fileaddr if (NetBootFileXferSize > 0) { char buf[20]; printf("Bytes transferred = %ld (%lx hex)
", 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); } }}
 
 
 

 注释1
 void eth_halt(void)
{
    if (!eth_current)
        return;
    eth_current->halt(eth_current);
    eth_current->state = ETH_STATE_PASSIVE;
} 

 调用网口设备虚拟层的halt函数,该函数实际是具体网口设备IP3912的halt函数,
 static void ip3912_halt(struct eth_device *netdev)
{
    struct ip3912_device *ip3912 = netdev->priv;
 
/* disable rx-path, tx-path, host registers reset
 * set FullDuplex, enable RMMI, disable rx+tx
 * no flow control, no frames<64b
 */
    writel(0x000006b8, (void *)(ip3912->etn_base + ETN_COMMAND));
} 
 注释2
 同理,eth_init()会调用 ip3912_init。 ip3912_init主要完成数据包发送和接受之前的初始化工作,要参考CPU数据手册中Ethernet Mac模块如何收发数据包的文档。其中,ip3912_init_descriptors() 和 ip3912_mii_negotiate_phy()比较重要。
 注释2、注释3.3和注释4的一部分 都是和具体网口设备相关的,可以先不看,先把注意力放在上层程序Netloop的执行流程和框架上。
 int eth_init(bd_t *bis)
{
    ...
    eth_current->init(eth_current,bis)
    ... 
} 
 

 static int ip3912_init(struct eth_device *netdev, bd_t *bd)
{
    struct ip3912_device *ip3912 = netdev->priv;
 
    /* update mac address in boardinfo */
    ip3912_setmac(netdev);
 
    /* before enabling the rx-path we need to set up rx-descriptors */
    if (ip3912_init_descriptors(netdev))
        return -1;
 
    /* set max packet length to 1536 bytes */
    writel(MAX_ETH_FRAME_SIZE, (void *)(ip3912->etn_base + ETN_MAXF));
    /* full duplex */
    writel(0x00000023, (void *)(ip3912->etn_base + ETN_MAC2));
    /* inter packet gap register */
    writel(0x15, (void *)(ip3912->etn_base + ETN_IPGT));
    writel(0x12, (void *)(ip3912->etn_base + ETN_IPGR));
    /* enable rx, receive all frames */
    writel(0x00000003, (void *)(ip3912->etn_base + ETN_MAC1));
    /* accept all multicast, broadcast and station packets */
    writel(0x00000026, (void *)(ip3912->etn_base + ETN_RXFILTERCTRL));
 
    if (!l2_switch_present()) {
        /* reset MII mgmt, set MII clock */
        writel(0x0000801c, (void *)(ip3912->etn_base + ETN_MCFG));
        writel(0x0000001c, (void *)(ip3912->etn_base + ETN_MCFG));
    }
   /* release rx-path, tx-path, host registers reset
    * set FullDuplex, enable RMMI, enable rx+tx
    * no flow control, no frames<64b
    */
    writel(0x00000683, (void *)(ip3912->etn_base + ETN_COMMAND));
    ip3912_init_descriptors(netdev);
 
    #ifdef CONFIG_DISCOVER_PHY
    mii_discover_phy();
    #endif
 
    if (!l2_switch_present())          
        /* init phy */
        mii_init_phy(ip3912->phy_addr);
    /* check autonegotiation */
    ip3912_i2cl2switch_negotiate_phy();
 
    #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
    /* check autonegotiation */
    if (!l2_switch_present())                 
        ip3912_mii_negotiate_phy();  // 用于协商传输速率
    #endif
 
    return 0;
}
static int ip3912_init_descriptors(struct eth_device *netdev)
{
    struct ip3912_device *ip3912;
    static void *rxbuf;
    int i;
 
    ip3912 = netdev->priv;
 
    /* fill in pointer in regs */
    writel((unsigned long)etn_rxdescriptor, (void *)(ip3912->etn_base + ETN_RXDESCRIPTOR));
    writel((unsigned long)etn_rxstatus, (void *)(ip3912->etn_base + ETN_RXSTATUS));
    writel(0x00000000, (void *)(ip3912->etn_base + ETN_RXCONSUMEINDEX));
    writel(CONFIG_SYS_ETN_RX_DESCRIPTOR_NUMBER - 1,(void *)(ip3912->etn_base + ETN_RXDESCRIPTORNUMBER));
 
    writel((unsigned long)etn_txdescriptor, (void *)(ip3912->etn_base + ETN_TXDESCRIPTOR));
    writel((unsigned long)etn_txstatus, (void *)(ip3912->etn_base + ETN_TXSTATUS));
    writel(0x00000000, (void *)(ip3912->etn_base + ETN_TXPRODUCEINDEX));
    writel(CONFIG_SYS_ETN_TX_DESCRIPTOR_NUMBER - 1,(void *)(ip3912->etn_base + ETN_TXDESCRIPTORNUMBER));
 
    /* allocate rx-buffers, but only once, were called multiple times! */
    if (!rxbuf)
       rxbuf = malloc(MAX_ETH_FRAME_SIZE * CONFIG_SYS_ETN_RX_DESCRIPTOR_NUMBER);
    if (!rxbuf) {
       puts("ERROR: couldnt allocate rx buffers!
");
       return -1;
    }
 
    for (i = 0; i < CONFIG_SYS_ETN_RX_DESCRIPTOR_NUMBER; i++) {
        etn_rxdescriptor[i].packet = rxbuf + i * MAX_ETH_FRAME_SIZE;
        etn_rxdescriptor[i].control = MAX_ETH_FRAME_SIZE - sizeof(unsigned long);
        etn_rxstatus[i].info = 0;
        etn_rxstatus[i].hashCRC = 0;
    }
    for (i = 0; i < CONFIG_SYS_ETN_TX_DESCRIPTOR_NUMBER; i++) {
        etn_txdescriptor[i].packet = 0;
        etn_txdescriptor[i].control = 0;
        etn_txstatus[i].info = 0;
   }
   return 0;
} 
 

  
 注释3 
 static void PingStart(void)
{
    #if defined(CONFIG_NET_MULTI)
    printf ("Using %s device
", eth_get_name());
    #endif	/* CONFIG_NET_MULTI */
    NetSetTimeout (10000UL, PingTimeout); //设置超时处理函数
    NetSetHandler (PingHandler);设置数据包接收处理函数
 
    PingSend(); //注释3.1
}
Void NetSetTimeout(ulong iv, thand_f * f)
{
    if (iv == 0) {
        timeHandler = (thand_f *)0;
    } else {
        timeHandler = f;
        timeStart = get_timer(0);
        timeDelta = iv;
    }
}
Void NetSetHandler(rxhand_f * f)
{
    packetHandler = f;
} 
 

  //注释3.1
 发送Echo request之前需要知道对方的MAC地址,这需要发送ARP数据包。所以ARP数据包放在NetTxPacket所指向的buffer里,而Echo request 数据包放在NetArpWaitTxPacket所指向的buffer里,等查询到对方的MAC地址后,再发送出去。
 int PingSend(void) 
{
    static uchar mac[6];
    volatile IP_t *ip;
    volatile ushort *s;
    uchar *pkt;
  
    /* XXX always send arp request */
 
    /* 构造Echo  request  数据包,该数据包不会马上发出,因为目标硬件地址还是空的,  
     * 会先发送ARP request数据包,当收到ARP reply 数据包后,再发送  Echo  request  
     * 数据包
     */
    memcpy(mac, NetEtherNullAddr, 6);
    debug("sending ARP for %08lx
", NetPingIP);
    NetArpWaitPacketIP = NetPingIP;
    NetArpWaitPacketMAC = mac;
 
    /*
    *构造Ethernet II 协议头部,目标硬件地址暂定为00:00:00:00:00:00
    *源MAC地址是NetOurEther,是在NetLOOP()中初始化的。
    *帧类型是0x0806,PROT_ARP
    */
 
    pkt = NetArpWaitTxPacket;
    pkt += NetSetEther(pkt, mac, PROT_IP);
 
    ip = (volatile IP_t *)pkt;
 
    /*
    * 构造IP协议和ICMP 数据包,
    */
 
    /*
     *	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(IP_FLAGS_DFRAG);	/* Dont fragment */ //片偏移
    ip->ip_ttl   = 255; //生存时间
    ip->ip_p     = 0x01;	 /* ICMP */ //协议类型
    ip->ip_sum   = 0; 
    /*源IP地址和目的IP地址*/
    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;	 /* XXX 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;
 
   /*
    *  设置Arp的超时时间的起始点,ArpTimeoutCheck()会处理ARP超时的问题
    */
    /* and do the ARP request */
    NetArpWaitTry = 1;
    NetArpWaitTimerStart = get_timer(0);
   /*
    *发送ARP request,退出pingsend(),程序会在NetLOOP中循环接收数据包,并调用处    
    *理函数。
    */
    ArpRequest(); // 注释3.2
    return 1;	/* waiting */
} 
 

                                                          图      将要发送的ping echo request 数据包
 注释3.2
 //发送ARP request
 void ArpRequest (void)
{
    int i;
    volatile uchar *pkt;
    ARP_t *arp;
 
    debug("ARP broadcast %d
", NetArpWaitTry);
 
    pkt = NetTxPacket;
    /*
     *构造Ethernet II 协议头部,NetBcastAddr是广播地址,FF:FF:FF:FF:FF:FF
     *源MAC地址是NetOurEther,是在NetLOOP()中初始化的。
     *帧类型是0x0806,PROT_ARP
     */
    pkt += NetSetEther (pkt, NetBcastAddr, PROT_ARP);
    /*
    * 构造ARP协议数据包,
    */
    arp = (ARP_t *) pkt;
  
    arp->ar_hrd = htons (ARP_ETHER); //硬件类型ARP_ETHER = 1  表示以太网
    arp->ar_pro = htons (PROT_IP);    // 协议类型 表示要映射的协议地址类型
    arp->ar_hln = 6;	 //硬件地址长度 MAC地址字节数
    arp->ar_pln = 4;	 //协议地址长度 IP地址字节数
    arp->ar_op = htons (ARPOP_REQUEST); //操作类型ARPOP_REQUEST=1表示ARP请求
 
    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     */
    }
    // 接收方的IP地址,若不在同一网段,需要设置环境变量gatewayip
    if ((NetArpWaitPacketIP & NetOurSubnetMask) != (NetOurIP & NetOurSubnetMask)) {
        if (NetOurGatewayIP == 0) {
            puts ("## Warning: gatewayip needed but not set
");
            NetArpWaitReplyIP = NetArpWaitPacketIP;
        } else {
            NetArpWaitReplyIP = NetOurGatewayIP;
        }
    } else {
        NetArpWaitReplyIP = NetArpWaitPacketIP;
    }
 
    NetWriteIP ((uchar *) & arp->ar_data[16], NetArpWaitReplyIP);
    //调用发送函数,
    (void) eth_send (NetTxPacket, (pkt - NetTxPacket) + ARP_HDR_SIZE);注释3.3
} 
 

                                                  图         发送的ARP数据包
      
  注释3.3
 //IP3912发送函数的原理,可以参照CPU Ethernet 模块章节
 int eth_send(volatile void *packet, int length)
{
    if (!eth_current)
        return -1;
    return eth_current->send(eth_current, packet, length);
}static int ip3912_send(struct eth_device *netdev, volatile void *packet, int length)
 {
   略
}
  
 注释4
 可以只看注释4.1
 int eth_rx(void)
{
    if (!eth_current)
        return -1;
 
    return eth_current->recv(eth_current);
} 
 

 //IP3912接收函数的原理,可以参照CPU Ethernet 模块章节
 /* Check for received packets */
static int ip3912_recv(struct eth_device *netdev)
{
略
} 

 注释4.1
 void NetReceive(volatile uchar * inpkt, int len)
{
    Ethernet_t *et;
    IP_t	*ip;
    ARP_t	*arp;
    IPaddr_t tmp;
    int	   x;
    uchar *pkt;
 
    ushort cti = 0, vlanid = VLAN_NONE, myvlanid, mynvlanid;
 
    debug("packet received
");
 
    NetRxPacket = inpkt;
    NetRxPacketLen = len;
    et = (Ethernet_t *)inpkt;
 
    /* too small packet? */
    if (len < ETHER_HDR_SIZE)
        return;
 
    myvlanid = ntohs(NetOurVLAN);
    if (myvlanid == (ushort)-1)
        myvlanid = VLAN_NONE;
    mynvlanid = ntohs(NetOurNativeVLAN);
    if (mynvlanid == (ushort)-1)
        mynvlanid = VLAN_NONE;
 
    x = ntohs(et->et_protlen);
 
    debug("packet received
");
 
    if (x < 1514) {
        /*
         *	Got a 802 packet.  Check the other protocol field.
         */
        x = ntohs(et->et_prot);
 
        ip = (IP_t *)(inpkt + E802_HDR_SIZE);
        len -= E802_HDR_SIZE;
 
    } else if (x != PROT_VLAN) {	/* normal packet */
        ip = (IP_t *)(inpkt + ETHER_HDR_SIZE);
        len -= ETHER_HDR_SIZE;
 
    } else {	 /* VLAN packet */
        VLAN_Ethernet_t *vet = (VLAN_Ethernet_t *)et;
 
        debug("VLAN packet received
");
        /* too small packet? */
        if (len < VLAN_ETHER_HDR_SIZE)
            return;
        /* if no VLAN active */
        if ((ntohs(NetOurVLAN) & VLAN_IDMASK) == VLAN_NONE)
            return;
        cti = ntohs(vet->vet_tag);
        vlanid = cti & VLAN_IDMASK;
        x = ntohs(vet->vet_type);
 
        ip = (IP_t *)(inpkt + VLAN_ETHER_HDR_SIZE);
        len -= VLAN_ETHER_HDR_SIZE;
    }
 
    debug("Receive from protocol 0x%x
", x);
 
    if ((myvlanid & VLAN_IDMASK) != VLAN_NONE) {
        if (vlanid == VLAN_NONE)
            vlanid = (mynvlanid & VLAN_IDMASK);
            /* not matched? */
        if (vlanid != (myvlanid & VLAN_IDMASK))
            return;
    }
 
    switch (x) {
        case PROT_ARP:
            /*
             * We have to deal with two types of ARP packets:
             * - REQUEST packets will be answered by sending  our
             *   IP address - if we know it.
             * - REPLY packates are expected only after we asked
             *   for the TFTP servers or the gateways ethernet
             *   address; so if we receive such a packet, we set
             *   the server ethernet address
             */
            debug("Got ARP
");
 
            arp = (ARP_t *)ip;
            if (len < ARP_HDR_SIZE) {
                printf("bad length %d < %d
", len, ARP_HDR_SIZE);
                return;
            }
            if (ntohs(arp->ar_hrd) != ARP_ETHER) {
                return;
            }
            if (ntohs(arp->ar_pro) != PROT_IP) {
                return;
            }
            if (arp->ar_hln != 6) {
                return;
            }
            if (arp->ar_pln != 4) {
                return;
            }
 
            if (NetOurIP == 0) {
                return;
            }
 
            if (NetReadIP(&arp->ar_data[16]) != NetOurIP) {
                return;
            }
            switch (ntohs(arp->ar_op)) {
                case ARPOP_REQUEST:	 /* reply with our IP address	*/
                    debug("Got ARP REQUEST, return our IP
");
                    pkt = (uchar *)et;
                    pkt += NetSetEther(pkt, et->et_src, PROT_ARP);
                    arp->ar_op = htons(ARPOP_REPLY);
                    memcpy   (&arp->ar_data[10], &arp->ar_data[0], 6);
                    NetCopyIP(&arp->ar_data[16], &arp->ar_data[6]);
                    memcpy   (&arp->ar_data[ 0], NetOurEther, 6);
                    NetCopyIP(&arp->ar_data[ 6], &NetOurIP);
                    (void) eth_send((uchar *)et, (pkt - (uchar *)et) + ARP_HDR_SIZE);
                    return;
                case ARPOP_REPLY:	 /* arp reply */
                    /* are we waiting for a reply */
                    if (!NetArpWaitPacketIP || !NetArpWaitPacketMAC)
                        break;
 
                    debug("Got ARP REPLY, set server/gtwy eth addr (%pM)
",arp->ar_data);
                    tmp = NetReadIP(&arp->ar_data[6]);
 
                    /* matched waiting packets address */
                    /*
                     * 如果收到ARP reply消息,就从里面获取到目标硬件地址,
                     * 并填写到正在等待发送的Ping Echo request数据包内,并发送
                     *  Ping Echo request数据包。之后会收到ping echo reply 消息,
                     *  仍会执行NetReceive()函数。
                     */
                     if (tmp == NetArpWaitReplyIP) { 
                         debug("Got it
");
                         /* save address for later use */
                         memcpy(NetArpWaitPacketMAC, &arp->ar_data[0], 6);
 
                         #ifdef CONFIG_NETCONSOLE
                             (*packetHandler)(0,0,0,0);
                         #endif
                         /* modify header, and transmit it */
                         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;
                 default:
                     debug("Unexpected ARP opcode 0x%x
", ntohs(arp->ar_op));
                     return;
             }
             break;
         case PROT_RARP:
             debug("Got RARP
");
             arp = (ARP_t *)ip;
             if (len < ARP_HDR_SIZE) {
                 printf("bad length %d < %d
", len, ARP_HDR_SIZE);
                 return;
             }
 
             if ((ntohs(arp->ar_op) != RARPOP_REPLY) ||
                   (ntohs(arp->ar_hrd) != ARP_ETHER)   ||
                       (ntohs(arp->ar_pro) != PROT_IP)     ||
                          (arp->ar_hln != 6) || (arp->ar_pln != 4)) {
                 puts ("invalid RARP header
");
             } else {
                 NetCopyIP(&NetOurIP,    &arp->ar_data[16]);
                 if (NetServerIP == 0)
                     NetCopyIP(&NetServerIP, &arp->ar_data[ 6]);
                 memcpy (NetServerEther, &arp->ar_data[ 0], 6);
 
                 (*packetHandler)(0,0,0,0);
             }
             break;
         case PROT_IP:
             debug("Got IP
");
             /* Before we start poking the header, make sure it is there */
             if (len < IP_HDR_SIZE) {
                 debug("len bad %d < %lu
", len, (ulong)IP_HDR_SIZE);
                 return;
             }
             /* Check the packet length */
             if (len < ntohs(ip->ip_len)) {
                 printf("len bad %d < %d
", len, ntohs(ip->ip_len));
                 return;
             }
             len = ntohs(ip->ip_len);
             debug("len=%d, v=%02x
", len, ip->ip_hl_v & 0xff);
 
             /* Cant deal with anything except IPv4 */
             if ((ip->ip_hl_v & 0xf0) != 0x40) {
                 return;
             }
             /* Cant deal with IP options (headers != 20 bytes) */
             if ((ip->ip_hl_v & 0x0f) > 0x05) {
                 return;
             }
             /* Check the Checksum of the header */
             if (!NetCksumOk((uchar *)ip, IP_HDR_SIZE_NO_UDP / 2)) {
                 puts ("checksum bad
");
             return;
         }
         /* If it is not for us, ignore it */
         tmp = NetReadIP(&ip->ip_dst);
         if (NetOurIP && tmp != NetOurIP && tmp != 0xFFFFFFFF) {
             return;
         }
         /*
          * The function returns the unchanged packet if its not
          * a fragment, and either the complete packet or NULL if
          * it is a fragment (if !CONFIG_IP_DEFRAG, it returns NULL)
          */
         if (!(ip = NetDefragment(ip, &len)))
             return;
          /*
           * watch for ICMP host redirects
           *
           * There is no real handler code (yet). We just watch
           * for ICMP host redirect messages. In case anybody
           * sees these messages: please contact me
           * (wd@denx.de), or - even better - send me the
           * necessary fixes :-)
           *
           * Note: in all cases where I have seen this so far
           * it was a problem with the router configuration,
           * for instance when a router was configured in the
           * BOOTP reply, but the TFTP server was on the same
           * subnet. So this is probably a warning that your
           * configuration might be wrong. But Im not really
           * sure if there arent any other situations.
           */
           if (ip->ip_p == IPPROTO_ICMP) {
               ICMP_t *icmph = (ICMP_t *)&(ip->udp_src);
 
           switch (icmph->type) {
               case ICMP_REDIRECT:
                   if (icmph->code != ICMP_REDIR_HOST)
                       return;
                   printf (" ICMP Host Redirect to %pI4 ", &icmph->un.gateway);
                   return;
               case ICMP_ECHO_REPLY:
                   /* 收到ping echo reply 消息,执行pingStart()函数中注册的处理函数	                                                                                      *  PingHandler()。 PingHandler()判断是否ping成功。若成功,流程返 
                    *  回 Netloop中while循环,接着返回do_ping(),打印成功消息后,
                    *  流程结束
                    *  static void
                    * 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;
                    * }
                    */
 
                    /*
                     *	IP header OK.  Pass the packet to the current handler.
                     */
                    /* XXX point to ip packet */
                    (*packetHandler)((uchar *)ip, 0, 0, 0);
                    return;
                case ICMP_ECHO_REQUEST:
                    debug("Got ICMP ECHO REQUEST, return %d bytes 
",ETHER_HDR_SIZE + len);
 
                    memcpy (&et->et_dest[0], &et->et_src[0], 6);
                    memcpy (&et->et_src[ 0], NetOurEther, 6);
                    ip->ip_sum = 0;
                    ip->ip_off = 0;
                    NetCopyIP((void*)&ip->ip_dst, &ip->ip_src);
                    NetCopyIP((void*)&ip->ip_src, &NetOurIP);
                    ip->ip_sum = ~NetCksum((uchar *)ip, IP_HDR_SIZE_NO_UDP >> 1);
 
                    icmph->type = ICMP_ECHO_REPLY;
                    icmph->checksum = 0;
                    icmph->checksum = ~NetCksum((uchar *)icmph,(len - IP_HDR_SIZE_NO_UDP) >> 1);
                    (void) eth_send((uchar *)et, ETHER_HDR_SIZE + len);
                    return;
                default:
                    return;
            }
        } else if (ip->ip_p != IPPROTO_UDP) {	/* Only UDP packets */
            return;
        }
 
 
        /*
         *	IP header OK.  Pass the packet to the current handler.
         */
        (*packetHandler)((uchar *)ip +IP_HDR_SIZE,
            ntohs(ip->udp_dst),
            ntohs(ip->udp_src),
            ntohs(ip->udp_len) - 8);
        break;
    }
} 
 

  
                 附录一  CONFIG_NET_MULTI
  
 搜素整个源码,可以找个很多关于CONFIG_NET_MULTI的注释, 
 #define CONFIG_NET_MULTI  /* Multi ethernet cards support */
 或者
 #define CONFIG_NET_MULTI  /*specify more that one ports available */
 或者
 #define CONFIG_NET_MULTI /* Support for multiple network interfaces */
 从上面的注释可以猜出当有多个网口设备时,需要定义CONFIG_NET_MULTI 。
 一个IP173有5个PHY,算一个设备,还是多个设备?
 Readme文档中也讲到两个和CONFIG_NET_MULTI 有关的环境变量。
   ethprime - When CONFIG_NET_MULTI is enabled controls which
   interface is used first.
   ethact - When CONFIG_NET_MULTI is enabled controls which
   interface is currently active. For example you
   can do the following
   void
NetReceive(volatile uchar * inpkt, int len)
{
Ethernet_t *et;
IP_t	*ip;
ARP_t	*arp;
IPaddr_t tmp;
int	x;
uchar *pkt;
 
ushort cti = 0, vlanid = VLAN_NONE, myvlanid, mynvlanid;
 
debug("packet received
");
 
NetRxPacket = inpkt;
NetRxPacketLen = len;
et = (Ethernet_t *)inpkt;
 
/* too small packet? */
if (len < ETHER_HDR_SIZE)
return;
 
 
 
myvlanid = ntohs(NetOurVLAN);
if (myvlanid == (ushort)-1)
myvlanid = VLAN_NONE;
mynvlanid = ntohs(NetOurNativeVLAN);
if (mynvlanid == (ushort)-1)
mynvlanid = VLAN_NONE;
 
x = ntohs(et->et_protlen);
 
debug("packet received
");
 
if (x < 1514) {
/*
 *	Got a 802 packet.  Check the other protocol field.
 */
x = ntohs(et->et_prot);
 
ip = (IP_t *)(inpkt + E802_HDR_SIZE);
len -= E802_HDR_SIZE;
 
} else if (x != PROT_VLAN) {	/* normal packet */
ip = (IP_t *)(inpkt + ETHER_HDR_SIZE);
len -= ETHER_HDR_SIZE;
 
} else {	 /* VLAN packet */
VLAN_Ethernet_t *vet = (VLAN_Ethernet_t *)et;
 
debug("VLAN packet received
");
 
/* too small packet? */
if (len < VLAN_ETHER_HDR_SIZE)
return;
 
/* if no VLAN active */
if ((ntohs(NetOurVLAN) & VLAN_IDMASK) == VLAN_NONE
)
return;
 
cti = ntohs(vet->vet_tag);
vlanid = cti & VLAN_IDMASK;
x = ntohs(vet->vet_type);
 
ip = (IP_t *)(inpkt + VLAN_ETHER_HDR_SIZE);
len -= VLAN_ETHER_HDR_SIZE;
}
 
debug("Receive from protocol 0x%x
", x);
 
if ((myvlanid & VLAN_IDMASK) != VLAN_NONE) {
if (vlanid == VLAN_NONE)
vlanid = (mynvlanid & VLAN_IDMASK);
/* not matched? */
if (vlanid != (myvlanid & VLAN_IDMASK))
return;
}
 
switch (x) {
 
case PROT_ARP:
/*
 * We have to deal with two types of ARP packets:
 * - REQUEST packets will be answered by sending  our
 *   IP address - if we know it.
 * - REPLY packates are expected only after we asked
 *   for the TFTP servers or the gateways ethernet
 *   address; so if we receive such a packet, we set
 *   the server ethernet address
 */
debug("Got ARP
");
 
arp = (ARP_t *)ip;
if (len < ARP_HDR_SIZE) {
printf("bad length %d < %d
", len, ARP_HDR_SIZE);
return;
}
if (ntohs(arp->ar_hrd) != ARP_ETHER) {
return;
}
if (ntohs(arp->ar_pro) != PROT_IP) {
return;
}
if (arp->ar_hln != 6) {
return;
}
if (arp->ar_pln != 4) {
return;
}
 
if (NetOurIP == 0) {
return;
}
 
if (NetReadIP(&arp->ar_data[16]) != NetOurIP) {
return;
}
 
switch (ntohs(arp->ar_op)) {
case ARPOP_REQUEST:	 /* reply with our IP address	*/
debug("Got ARP REQUEST, return our IP
");
pkt = (uchar *)et;
pkt += NetSetEther(pkt, et->et_src, PROT_ARP);
arp->ar_op = htons(ARPOP_REPLY);
memcpy   (&arp->ar_data[10], &arp->ar_data[0], 6);
NetCopyIP(&arp->ar_data[16], &arp->ar_data[6]);
memcpy   (&arp->ar_data[ 0], NetOurEther, 6);
NetCopyIP(&arp->ar_data[ 6], &NetOurIP);
(void) eth_send((uchar *)et, (pkt - (uchar *)et) + ARP_HDR_SIZE);
return;
 
case ARPOP_REPLY:	 /* arp reply */
/* are we waiting for a reply */
if (!NetArpWaitPacketIP || !NetArpWaitPacketMAC)
break;
 
debug("Got ARP REPLY, set server/gtwy eth addr (%pM)
",
arp->ar_data);
 
tmp = NetReadIP(&arp->ar_data[6]);
 
/* matched waiting packets address */
/*
* 如果收到ARP reply消息,就从里面获取到目标硬件地址,
* 并填写到正在等待发送的Ping Echo request数据包内,并发送
            *  Ping Echo request数据包。之后会收到ping echo reply 消息,
*  仍会执行NetReceive()函数。
*/
if (tmp == NetArpWaitReplyIP) { 
debug("Got it
");
/* save address for later use */
memcpy(NetArpWaitPacketMAC, &arp->ar_data[0], 6);
 
#ifdef CONFIG_NETCONSOLE
(*packetHandler)(0,0,0,0);
#endif
/* modify header, and transmit it */
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;
default:
debug("Unexpected ARP opcode 0x%x
", ntohs(arp->ar_op));
return;
}
break;
 
case PROT_RARP:
debug("Got RARP
");
arp = (ARP_t *)ip;
if (len < ARP_HDR_SIZE) {
printf("bad length %d < %d
", len, ARP_HDR_SIZE);
return;
}
 
if ((ntohs(arp->ar_op) != RARPOP_REPLY) ||
(ntohs(arp->ar_hrd) != ARP_ETHER)   ||
(ntohs(arp->ar_pro) != PROT_IP)     ||
(arp->ar_hln != 6) || (arp->ar_pln != 4)) {
 
puts ("invalid RARP header
");
} else {
NetCopyIP(&NetOurIP,    &arp->ar_data[16]);
if (NetServerIP == 0)
NetCopyIP(&NetServerIP, &arp->ar_data[ 6]);
memcpy (NetServerEther, &arp->ar_data[ 0], 6);
 
(*packetHandler)(0,0,0,0);
}
break;
 
case PROT_IP:
debug("Got IP
");
/* Before we start poking the header, make sure it is there */
if (len < IP_HDR_SIZE) {
debug("len bad %d < %lu
", len, (ulong)IP_HDR_SIZE);
return;
}
/* Check the packet length */
if (len < ntohs(ip->ip_len)) {
printf("len bad %d < %d
", len, ntohs(ip->ip_len));
return;
}
len = ntohs(ip->ip_len);
debug("len=%d, v=%02x
", len, ip->ip_hl_v & 0xff);
 
/* Cant deal with anything except IPv4 */
if ((ip->ip_hl_v & 0xf0) != 0x40) {
return;
}
/* Cant deal with IP options (headers != 20 bytes) */
if ((ip->ip_hl_v & 0x0f) > 0x05) {
return;
}
/* Check the Checksum of the header */
if (!NetCksumOk((uchar *)ip, IP_HDR_SIZE_NO_UDP / 2)) {
puts ("checksum bad
");
return;
}
/* If it is not for us, ignore it */
tmp = NetReadIP(&ip->ip_dst);
if (NetOurIP && tmp != NetOurIP && tmp != 0xFFFFFFFF) {
return;
}
/*
 * The function returns the unchanged packet if its not
 * a fragment, and either the complete packet or NULL if
 * it is a fragment (if !CONFIG_IP_DEFRAG, it returns NULL)
 */
if (!(ip = NetDefragment(ip, &len)))
return;
/*
 * watch for ICMP host redirects
 *
 * There is no real handler code (yet). We just watch
 * for ICMP host redirect messages. In case anybody
 * sees these messages: please contact me
 * (wd@denx.de), or - even better - send me the
 * necessary fixes :-)
 *
 * Note: in all cases where I have seen this so far
 * it was a problem with the router configuration,
 * for instance when a router was configured in the
 * BOOTP reply, but the TFTP server was on the same
 * subnet. So this is probably a warning that your
 * configuration might be wrong. But Im not really
 * sure if there arent any other situations.
 */
if (ip->ip_p == IPPROTO_ICMP) {
ICMP_t *icmph = (ICMP_t *)&(ip->udp_src);
 
switch (icmph->type) {
case ICMP_REDIRECT:
if (icmph->code != ICMP_REDIR_HOST)
return;
printf (" ICMP Host Redirect to %pI4 ", &icmph->un.gateway);
return;
case ICMP_ECHO_REPLY:
/* 收到ping echo reply 消息,执行pingStart()函数中注册的处理函数	  *  PingHandler()。 PingHandler()判断是否ping成功。若成功,流程返 
 *  回 Netloop中while循环,接着返回do_ping(),打印成功消息后,
     *  流程结束
                 *  static void
                 * 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;
                 * }
 */
 
/*
 *	IP header OK.  Pass the packet to the current handler.
 */
/* XXX point to ip packet */
(*packetHandler)((uchar *)ip, 0, 0, 0);
return;
case ICMP_ECHO_REQUEST:
debug("Got ICMP ECHO REQUEST, return %d bytes 
",
ETHER_HDR_SIZE + len);
 
memcpy (&et->et_dest[0], &et->et_src[0], 6);
memcpy (&et->et_src[ 0], NetOurEther, 6);
 
ip->ip_sum = 0;
ip->ip_off = 0;
NetCopyIP((void*)&ip->ip_dst, &ip->ip_src);
NetCopyIP((void*)&ip->ip_src, &NetOurIP);
ip->ip_sum = ~NetCksum((uchar *)ip, IP_HDR_SIZE_NO_UDP >> 1);
 
icmph->type = ICMP_ECHO_REPLY;
icmph->checksum = 0;
icmph->checksum = ~NetCksum((uchar *)icmph,
(len - IP_HDR_SIZE_NO_UDP) >> 1);
(void) eth_send((uchar *)et, ETHER_HDR_SIZE + len);
return;
default:
return;
}
} else if (ip->ip_p != IPPROTO_UDP) {	/* Only UDP packets */
return;
}
 
 
/*
 *	IP header OK.  Pass the packet to the current handler.
 */
(*packetHandler)((uchar *)ip +IP_HDR_SIZE,
ntohs(ip->udp_dst),
ntohs(ip->udp_src),
ntohs(ip->udp_len) - 8);
break;
}
} 
 => setenv ethact FEC
   => setenv ethact FEC
   => ping 192.168.0.1 # traffic sent on FEC
   => setenv ethact SCC
   => ping 10.0.0.1 # traffic sent on SCC
  
  
  
经验分享 程序员 微信小程序 职场和发展