淺析uboot網絡程序結構

這篇文章主要講解uboo/net目錄下的部分源代碼。主要是 net.c,eth.c,ip3912.c 中的代碼。本例用的是xxxx公司yyyy系列的zzzzCPU, 網卡是IP173(和IP3912兼容)。

 

本文主要分三部分  網口設備的檢測,網口設備的註冊,應用程序(ping)的執行流程

(檢測網口設備

先從Arch/arm/lib/board.c講起,uboot執行完彙編程序後,會跳轉到該文件中的start_armboot函數。該函數先會爲全局變量gd分配空間,並清零。然後執行函數指針數組init_sequence裏的各種初始化函數。初始化函數一般都是和具體的開發板相關聯的,所以這些函數的源碼是在board目錄下,本例就是在borad/xxxx/yyyy下。

 

init_sequence數組中有個指針指向board_init,一般可以board_init函數內,初始化CPUIP173相連的引腳,reset網卡後,可以通過讀寫CPU的相關寄存器向IP173發送讀寫命令來檢測IP173是否正常。

 

例如:IP173寄存器23是芯片ID寄存器(5PHY共用)(見圖一),可以通過讀寫寄存器 23來判斷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\n",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會繼續完成其他初始化工作,如FLASHSDRAM,串口、中斷、環境變量等等,期間會設置IP地址

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

然後調用eth_initialize()函數。之後等待用戶輸入命令或者啓動kernel 

 

eth_initialize()大致流程是調用具體的網卡驅動程序(ip3912_send,ip3912_recv)來初始化網口設備虛擬層的框架(eth_device->send,eth_device->recv),這些程序爲應用程序如pingtftpboot提供服務(如PingSendTftpSend等)

 

 

eth_initialize()位於net/eth.c文件內,eth.c就是網口設備虛擬層的源碼,其提供eth_initializeeth_registereth_get_deveth_initeth_halteth_sendeth_rxeth_receiveeth_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.\n");
        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("\nWarning: eth device name has a space!\n");
 
        eth_getenv_enetaddr_by_index(eth_number, env_enetaddr);
 
        if (memcmp(env_enetaddr, "\0\0\0\0\0\0", 6)) {
            if (memcmp(dev->enetaddr, "\0\0\0\0\0\0", 6) &&
               memcmp(dev->enetaddr, env_enetaddr, 6))
            {
                printf ("\nWarning: %s MAC addresses don't match:\n",dev->name);
                printf ("Address in SROM is         %pM\n",dev->enetaddr);
                printf ("Address in environment is  %pM\n",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 ('\n');
    }
    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_haltip3912_recvip3912_sendip3912_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'\n", 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] = '\0';
    // 將新的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\n", 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\n", 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\n", 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\n", argv[1]);
        return 1;
    }
    printf("host %s is alive\n", 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 ("\nAbort\n"); 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)\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); } }}



註釋1

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

調用網口設備虛擬層的halt函數,該函數實際是具體網口設備IP3912halt函數,

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, we're called multiple times! */
    if (!rxbuf)
       rxbuf = malloc(MAX_ETH_FRAME_SIZE * CONFIG_SYS_ETN_RX_DESCRIPTOR_NUMBER);
    if (!rxbuf) {
       puts("ERROR: couldn't allocate rx buffers!\n");
       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;
}


 

註釋

static void PingStart(void)
{
    #if defined(CONFIG_NET_MULTI)
    printf ("Using %s device\n", 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\n", 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);	/* Don't 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\n", 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\n");
            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\n");
 
    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\n");
 
    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\n");
        /* 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\n", 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 server's or the gateway's ethernet
             *   address; so if we receive such a packet, we set
             *   the server ethernet address
             */
            debug("Got ARP\n");
 
            arp = (ARP_t *)ip;
            if (len < ARP_HDR_SIZE) {
                printf("bad length %d < %d\n", 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\n");
                    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)\n",arp->ar_data);
                    tmp = NetReadIP(&arp->ar_data[6]);
 
                    /* matched waiting packet's address */
                    /*
                     * 如果收到ARP reply消息,就從裏面獲取到目標硬件地址,
                     * 並填寫到正在等待發送的Ping Echo request數據包內,併發送
                     *  Ping Echo request數據包。之後會收到ping echo reply 消息,
                     *  仍會執行NetReceive()函數。
                     */
                     if (tmp == NetArpWaitReplyIP) { 
                         debug("Got it\n");
                         /* 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\n", ntohs(arp->ar_op));
                     return;
             }
             break;
         case PROT_RARP:
             debug("Got RARP\n");
             arp = (ARP_t *)ip;
             if (len < ARP_HDR_SIZE) {
                 printf("bad length %d < %d\n", 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\n");
             } 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\n");
             /* Before we start poking the header, make sure it is there */
             if (len < IP_HDR_SIZE) {
                 debug("len bad %d < %lu\n", len, (ulong)IP_HDR_SIZE);
                 return;
             }
             /* Check the packet length */
             if (len < ntohs(ip->ip_len)) {
                 printf("len bad %d < %d\n", len, ntohs(ip->ip_len));
                 return;
             }
             len = ntohs(ip->ip_len);
             debug("len=%d, v=%02x\n", len, ip->ip_hl_v & 0xff);
 
             /* Can't deal with anything except IPv4 */
             if ((ip->ip_hl_v & 0xf0) != 0x40) {
                 return;
             }
             /* Can't 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\n");
             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 it's 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
           * ([email protected]), 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 I'm not really
           * sure if there aren't 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 \n",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

一個IP1735PHY,算一個設備,還是多個設備?

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\n");
 
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\n");
 
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\n");
 
/* 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\n", 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 server's or the gateway's ethernet
 *   address; so if we receive such a packet, we set
 *   the server ethernet address
 */
debug("Got ARP\n");
 
arp = (ARP_t *)ip;
if (len < ARP_HDR_SIZE) {
printf("bad length %d < %d\n", 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\n");
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)\n",
arp->ar_data);
 
tmp = NetReadIP(&arp->ar_data[6]);
 
/* matched waiting packet's address */
/*
* 如果收到ARP reply消息,就從裏面獲取到目標硬件地址,
* 並填寫到正在等待發送的Ping Echo request數據包內,併發送
            *  Ping Echo request數據包。之後會收到ping echo reply 消息,
*  仍會執行NetReceive()函數。
*/
if (tmp == NetArpWaitReplyIP) { 
debug("Got it\n");
/* 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\n", ntohs(arp->ar_op));
return;
}
break;
 
case PROT_RARP:
debug("Got RARP\n");
arp = (ARP_t *)ip;
if (len < ARP_HDR_SIZE) {
printf("bad length %d < %d\n", 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\n");
} 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\n");
/* Before we start poking the header, make sure it is there */
if (len < IP_HDR_SIZE) {
debug("len bad %d < %lu\n", len, (ulong)IP_HDR_SIZE);
return;
}
/* Check the packet length */
if (len < ntohs(ip->ip_len)) {
printf("len bad %d < %d\n", len, ntohs(ip->ip_len));
return;
}
len = ntohs(ip->ip_len);
debug("len=%d, v=%02x\n", len, ip->ip_hl_v & 0xff);
 
/* Can't deal with anything except IPv4 */
if ((ip->ip_hl_v & 0xf0) != 0x40) {
return;
}
/* Can't 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\n");
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 it's 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
 * ([email protected]), 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 I'm not really
 * sure if there aren't 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 \n",
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

 

 

 

發佈了24 篇原創文章 · 獲贊 1 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章