浅析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是否存在。

具体检测函数如下:

[objc]  view plain  copy
  1.    
  2. #define CONFIG_IP3912_ETN1_BASE  0xC1600000   //见图二  
  3. #define CONFIG_IP3912_ETN2_BASE  0xC1700000  
  4.    
  5. #define ETN1_BASE    CONFIG_IP3912_ETN1_BASE  
  6. #define ETN2_BASE    CONFIG_IP3912_ETN2_BASE  
  7.    
  8. #define ETN_MAC1     0x0000  
  9. #define ETN_MAC2     0x0004  
  10. #define ETN_IPGT         0x0008  
  11. #define ETN_IPGR         0x000c  
  12. #define ETN_CLRT     0x0010  
  13. #define ETN_MAXF     0x0014  
  14. #define ETN_SUPP     0x0018  
  15. #define ETN_TEST     0x001c  
  16. #define ETN_MCFG     0x0020  
  17. #define ETN_MCMD     0x0024  
  18. #define ETN_MADR     0x0028  
  19.    
  20.    
  21. #define VEGA_IP173_PHY_ADDR  0x01  
  22. #define ICPLUS_IP173T_PHYID1     0x02  
  23. #define ICPLUS_IP173T_PHYID2     0x03  
  24. #define ICPLUS_OUI_MASK  0x0000ffff  
  25. #define ICPLUS_OUI   0x90c3  
  26.    
  27. int board_init(void)  
  28. {  
  29.     ...  
  30.     board_detect();  
  31.     ...  
  32. }  
  33. void board_detect(void)  
  34. {  
  35.     init_etn0();  
  36.     detect_IC_PLUS_173T(VEGA_IP173_PHY_ADDR);  
  37. }  
  38.    
  39. static int detect_IC_PLUS_173T(int addr)  
  40. {  
  41.     u32 phyid = 0;  
  42.     u16 reg = 0;  
  43.    
  44.     clear_gpioc(28); //IP173 reset引脚  
  45.     udelay(12000);  
  46.     udelay(12000);  
  47.     udelay(12000);  
  48.    
  49.     set_gpioc(28);  //IP173 reset引脚  
  50.     udelay(1000);  
  51.     udelay(1000);  
  52.     udelay(1000);  
  53.    
  54.     reg = detect_phy_read(addr,ICPLUS_IP173T_PHYID1); // 读ID1寄存器  
  55.     phyid = (u32)reg << 6;  
  56.     reg = detect_phy_read(addr, ICPLUS_IP173T_PHYID2); // 读ID2寄存器  
  57.     phyid |= ((u32)reg)>>10;  
  58.     phyid &= ICPLUS_OUI_MASK;  
  59.     /* IC+ IP173T */  
  60.     printf("phyid = 0x%x\n",phyid );  
  61.     if (phyid == ICPLUS_OUI)  
  62.         return 1;  
  63.     return 0;  
  64. }  
  65. static void detect_phy_wait(void)  
  66. {  
  67.         int i, status;  
  68.         for (i = 0; i < 1000; i++) {  
  69.         status = readl((voidvoid *)(ETN1_BASE + ETN_MIND)) & 0x7//  
  70.         if (!status)  
  71.             return;  
  72.         udelay(1);  
  73.     }  
  74. }  
  75.    
  76. static u16 detect_phy_read(u8 address, u8 reg)  
  77. {  
  78.     u16 value;  
  79.     writel((address << 8) | reg, (voidvoid *)(ETN1_BASE + ETN_MADR)); //PHY地址右移或上  
  80.                                                             //reg地址  
  81.     writel(0x00000001, (voidvoid *)(ETN1_BASE + ETN_MCMD));  
  82.     detect_phy_wait();  
  83.    
  84.     value = readl((voidvoid *)(ETN1_BASE + ETN_MRDD));  
  85.     writel(0x00000000, (voidvoid *)(ETN1_BASE + ETN_MCMD));  
  86.     return value;  
  87. }  
  88.  int detect_phy_write(u8 address, u8 reg,int value)  
  89. {  
  90.     writel(address << 8 | reg,(voidvoid *)(ETN1_BASE + ETN_MADR));  
  91.     __raw_writel(value, (voidvoid *)(ETN1_BASE + ETN_MWTD));  
  92.     detect_phy_wait();  
  93. }  

解释

 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的主要数据结构如下:

[objc]  view plain  copy
  1. struct eth_device {  
  2.     char name[NAMESIZE]; //设备名  
  3.     unsigned char enetaddr[6];  // mac 地址  
  4.     int iobase;  
  5.     int state;   
  6.     int  (*init) (struct eth_device*, bd_t*);  
  7.     int  (*send) (struct eth_device*, volatile void* packet, int length);  
  8.     int  (*recv) (struct eth_device*);  
  9.     void (*halt) (struct eth_device*);  
  10.     #ifdef CONFIG_MCAST_TFTP  
  11.         int (*mcast) (struct eth_device*, u32 ip, u8 set);  
  12.     #endif  
  13.     int  (*write_hwaddr) (struct eth_device*);  //设置MAC的函数  
  14.     struct eth_device *next; // 指向下一个网口设备  
  15.     voidvoid *priv;  
  16. };  


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

下面来具体看下 eth_initialize函数

[objc]  view plain  copy
  1. int eth_initialize(bd_t *bis)  
  2. {  
  3.     unsigned char env_enetaddr[6];  
  4.     int eth_number = 0;  
  5.    
  6.     eth_devices = NULL;  
  7.     eth_current = NULL;  //注释1  
  8.    
  9.     show_boot_progress (64);  
  10.       
  11.     miiphy_init(); //注释2  
  12.    
  13.     //设置 eth_devices =eth_current = netdev, netdev含有IP3912的 init,send ,recv ,priv等    
  14.     //信息,也会设置 mii_devs和 current_mii   
  15.     if (board_eth_init(bis) < 0)  //注释3   
  16.         cpu_eth_init(bis);  
  17.     if (!eth_devices) {  
  18.         puts ("No ethernet found.\n");  
  19.         show_boot_progress (-64);  
  20.     } else {  
  21.         struct eth_device *dev = eth_devices;  
  22.         charchar *ethprime = getenv ("ethprime");   
  23.         show_boot_progress (65);  
  24.     do {   
  25.          // 比较dev->enetaddr中MAC地址和environment中的MAC地址是否一致,  
  26.          // 不一致则调用dev->write_hwaddr 函数来修改MAC地址,以environment中的    
  27.          // 为准,如果dev->write_hwaddr 为空,或者设置了环境变量ethmacskip就会跳   
  28.          //过该步骤。如果有多个网口设备,会循环执行上述步骤。  
  29.          if (eth_number)  
  30.              puts (", ");  
  31.          printf("%s", dev->name);  
  32.          if (ethprime && strcmp (dev->name, ethprime) == 0) {  
  33.             eth_current = dev;  
  34.             puts (" [PRIME]");  
  35.         }  
  36.         if (strchr(dev->name, ' '))  
  37.             puts("\nWarning: eth device name has a space!\n");  
  38.    
  39.         eth_getenv_enetaddr_by_index(eth_number, env_enetaddr);  
  40.    
  41.         if (memcmp(env_enetaddr, "\0\0\0\0\0\0"6)) {  
  42.             if (memcmp(dev->enetaddr, "\0\0\0\0\0\0"6) &&  
  43.                memcmp(dev->enetaddr, env_enetaddr, 6))  
  44.             {  
  45.                 printf ("\nWarning: %s MAC addresses don't match:\n",dev->name);  
  46.                 printf ("Address in SROM is         %pM\n",dev->enetaddr);  
  47.                 printf ("Address in environment is  %pM\n",env_enetaddr);  
  48.             }  
  49.             memcpy(dev->enetaddr, env_enetaddr, 6);  
  50.         }  
  51.         if (dev->write_hwaddr &&  
  52.             !eth_mac_skip(eth_number) &&  
  53.             is_valid_ether_addr(dev->enetaddr)) {  
  54.                  dev->write_hwaddr(dev);  
  55.         }  
  56.         eth_number++;  
  57.         dev = dev->next;  
  58.     } while(dev != eth_devices);  
  59.     /* update current ethernet name */  
  60.     if (eth_current) {  
  61.         charchar *act = getenv("ethact");  
  62.         if (act == NULL || strcmp(act, eth_current->name) != 0)  
  63.             setenv("ethact", eth_current->name);  
  64.         } else  
  65.             setenv("ethact"NULL);  
  66.          putc ('\n');  
  67.     }  
  68.     return eth_number;  
  69. }  


执行完该函数后,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中的函数。

[objc]  view plain  copy
  1. int eth_send(volatile voidvoid *packet, int length)  
  2. {  
  3.     if (!eth_current)  
  4.         return -1;  
  5.     return eth_current->send(eth_current, packet, length);  
  6. }  


注释2

[objc]  view plain  copy
  1. void miiphy_init(void)  
  2. {  
  3.     INIT_LIST_HEAD (&mii_devs);  
  4.     current_mii = NULL;  
  5. }  
  6. struct mii_dev {  
  7.     struct list_head link;  
  8.     const charchar *name;  
  9.     int (*read) (const charchar *devname, unsigned char addr,unsigned char reg, unsigned shortshort *value);  
  10.     int (*write) (const charchar *devname, unsigned char addr,  
  11. };  

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

注释3

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

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

[objc]  view plain  copy
  1. int board_eth_init(bd_t *bis)  
  2. {  
  3.     ...  
  4.     ip3912_miiphy_initialize(gd->bd);  
  5.     ...  
  6.     ip3912_eth_initialize(0,CONFIG_IP3912_ETN1_BASE,CONFIG_IP3912_ETN1_BASE, 0x01);  
  7.     ...  
  8.     setenv("ethact""ETN1");  
  9.     eth_set_current(); //一般会在ip3912_eth_initialize这些具体设备的初始化函数中调用。  
  10. }  

int ip3912_miiphy_initialize(bd_t *bis)

[objc]  view plain  copy
  1. {  
  2.     miiphy_register("ip3912", ip3912_miiphy_read, ip3912_miiphy_write);  
  3.     return 0;  
  4. }  

[objc]  view plain  copy
  1. void miiphy_register(const charchar *name,  
  2.       int (*read) (const charchar *devname, unsigned char addr,  
  3.    unsigned char reg, unsigned shortshort *value),  
  4.       int (*write) (const charchar *devname, unsigned char addr,  
  5.     unsigned char reg, unsigned short value))  
  6. {  
  7.     ...  
  8.     //检查是否已经注册了一个同名的mii设备  
  9.     new_dev = miiphy_get_dev_by_name(name, 1);  
  10.     // 是则直接退出  
  11.     if (new_dev) {   
  12.         printf("miiphy_register: non unique device name '%s'\n", name);  
  13.         return;  
  14.     }  
  15.     // 反之,分配一个新的MII设备  
  16.     name_len = strlen (name);  
  17.     new_dev =  (struct mii_dev *)malloc (sizeof (struct mii_dev) + name_len + 1);  
  18.     //随后初始化这个新的MII设备, 如read ,write函数  
  19.     memset (new_dev, 0sizeof (struct mii_dev) + name_len);  
  20.    
  21.     /* initalize mii_dev struct fields */  
  22.     INIT_LIST_HEAD (&new_dev->link);  
  23.     new_dev->read = read;  
  24.     new_dev->write = write;  
  25.     new_dev->name = new_name = (charchar *)(new_dev + 1);  
  26.     strncpy (new_name, name, name_len);  
  27.     new_name[name_len] = '\0';  
  28.     // 将新的MII设备 添加到 mii_devs 列表中,并设置current_mii   
  29.     // mii_devs 和current_mii 在eth_initialize()中初始化  
  30.     list_add_tail (&new_dev->link, &mii_devs);  
  31.     if (!current_mii)  
  32.     current_mii = new_dev;  
  33. }  

接着分析函数 ip3912_eth_initialize (),其主要数据结构是

[objc]  view plain  copy
  1. struct ip3912_device {  
  2.     unsigned int     etn_base;  
  3.     unsigned int     phy_base;  
  4.     unsigned char    nr;  
  5.     unsigned char    phy_addr;  
  6.     unsigned char    autonegotiate;  
  7.     unsigned char    speed;  
  8.     unsigned char    duplex;  
  9.     unsigned char    rmii;  
  10.    
  11.     const struct device *  dev;  
  12.     struct eth_device *    netdev;  
  13. };  


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

 

[objc]  view plain  copy
  1. int ip3912_eth_initialize(unsigned char nr, unsigned int etn_base, unsigned int phy_base, unsigned char phy_addr, unsigned char rmii)  
  2. {  
  3.     struct ip3912_device *ip3912;   
  4.     struct eth_device *netdev;  
  5.    
  6.     // 分配 eth_device 和 ip3912_device设备所需内存,并初始化。  
  7.     netdev = malloc(sizeof(struct eth_device));  
  8.     ip3912 = malloc(sizeof(struct ip3912_device));  
  9.     if ((!ip3912) || (!netdev)) {  
  10.         printf("Error: Failed to allocate memory for ETN%d\n", nr + 1);  
  11.         return -1;  
  12.     }  
  13.    
  14.     memset(ip39120sizeof(struct ip3912_device));  
  15.     memset(netdev, 0sizeof(struct eth_device));  
  16.    
  17.     //初始化具体网口设备的私有数据  
  18.     ip3912->nr = nr;  
  19.     ip3912->etn_base = etn_base;  
  20.     ip3912->phy_base = phy_base;  
  21.     ip3912->phy_addr = phy_addr;  
  22.     ip3912->autonegotiate = 0;  
  23.     ip3912->rmii = rmii;  
  24.     ip3912->speed = 0;  
  25.     ip3912->duplex = 0;  
  26.     ip3912->netdev = netdev;  
  27.    
  28.     // 用具体网口设备IP3912的操作函数来初始化网口设备虚拟层netdev,  
  29.     // 注意最后一项  
  30.     sprintf(netdev->name, "ETN%d", nr + 1);  
  31.     netdev->init = ip3912_init;  
  32.     netdev->send = ip3912_send;  
  33.     netdev->recv = ip3912_recv;  
  34.     netdev->halt = ip3912_halt;  
  35.     netdev->priv = (voidvoid *)ip3912;   
  36.    
  37.     //将eth_current 设置为新的netdev,并设置“ethact”,并设置状态state = ETH_STATE_INIT  
  38.     eth_register(netdev); // 注释3.1   
  39.     eth_set_current();  
  40.     ip3912_macreset(); // 初始化CPU Ethernet Mac模块   
  41.     /* we have to set the mac address, because we have no SROM */  
  42.     //读取环境变量,设置MAC地址  
  43.     ip3912_setmac(netdev); // 注释3.2    
  44.     return 0;  
  45. }  


注释3.1

[objc]  view plain  copy
  1. int eth_register(struct eth_device* dev)  
  2. {  
  3.     struct eth_device *d;  
  4.    
  5.     if (!eth_devices) {  
  6.         eth_current = eth_devices = dev;  
  7.         {  
  8.         charchar *act = getenv("ethact");  
  9.         if (act == NULL || strcmp(act, eth_current->name) != 0)  
  10.         setenv("ethact", eth_current->name);  
  11.         }  
  12.     } else {  
  13.         for (d=eth_devices; d->next!=eth_devices; d=d->next)  
  14.         ;  
  15.         d->next = dev;  
  16.     }  
  17.    
  18.     dev->state = ETH_STATE_INIT;  
  19.     dev->next  = eth_devices;  
  20.     return 0;  
  21. }  

注释3.2

[objc]  view plain  copy
  1. void ip3912_setmac(struct eth_device *netdev)  
  2. {  
  3.     struct ip3912_device *ip3912;  
  4.     unsigned char i, use_etn1addr = 0;  
  5.     charchar *mac_string, *pmac, *end;  
  6.     char tmp[18];  
  7.    
  8.     ip3912 = netdev->priv;  
  9.    
  10.     mac_string = getenv("ethaddr");  
  11.     if (ip3912->nr) {  
  12.         /* we use ETN2 */  
  13.         mac_string = getenv("eth1addr");  
  14.         if (!mac_string) {  
  15.             mac_string = getenv("ethaddr");  
  16.             use_etn1addr = 1;  
  17.         }  
  18.     }  
  19.     pmac = mac_string;  
  20.     for (i = 0; i < 6; i++) {  
  21.         netdev->enetaddr[i] = pmac ? simple_strtoul(pmac, &end, 16) : 0;  
  22.         if (pmac)  
  23.             pmac = (*end) ? end + 1 : end;  
  24.     }  
  25.    
  26.     if (use_etn1addr) {  
  27.         /* flip last bit of mac address */  
  28.         debug("ip3912_setmac %s flipping last bit\n", netdev->name);  
  29.         if (netdev->enetaddr[5] & 1)  
  30.             netdev->enetaddr[5] &= 0xfe;  
  31.         else  
  32.             netdev->enetaddr[5] |= 0x01;  
  33.             sprintf(tmp, "%02X:%02X:%02X:%02X:%02X:%02X",  
  34.             netdev->enetaddr[0], netdev->enetaddr[1],  
  35.             netdev->enetaddr[2], netdev->enetaddr[3],  
  36.             netdev->enetaddr[4], netdev->enetaddr[5]);  
  37.             setenv("eth1addr", tmp);  
  38.             mac_string = tmp;  
  39.     }  
  40.     debug("ip3912_setmac set %s to address %s\n", netdev->name, mac_string);  
  41.    
  42.     writel((netdev->enetaddr[5] << 8) | netdev->enetaddr[4],  
  43.         (voidvoid *)(ip3912->etn_base + ETN_SA0));  
  44.     writel((netdev->enetaddr[3] << 8) | netdev->enetaddr[2],  
  45.         (voidvoid *)(ip3912->etn_base + ETN_SA1));  
  46.     writel((netdev->enetaddr[1] << 8) | netdev->enetaddr[0],  
  47.         (voidvoid *)(ip3912->etn_base + ETN_SA2));  
  48. }  

 

 

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

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

typedef enum 

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

 

 

先看下比较简单的ping

[objc]  view plain  copy
  1. U_BOOT_CMD(  
  2.     ping,   2,  1,  do_ping,  
  3.     "send ICMP ECHO_REQUEST to network host",  
  4.     "pingAddress"  
  5. );  

[objc]  view plain  copy
  1. int do_ping (cmd_tbl_t *cmdtp, int flag, int argc, charchar * const argv[])  
  2. {  
  3.     if (argc < 2)  
  4.         return -1;  
  5.     NetPingIP = string_to_ip(argv[1]);  
  6.     if (NetPingIP == 0)  
  7.         return cmd_usage(cmdtp);  
  8.     if (NetLoop(PING) < 0) {  
  9.         printf("ping failed; host %s is not alive\n", argv[1]);  
  10.         return 1;  
  11.     }  
  12.     printf("host %s is alive\n", argv[1]);  
  13. return 0;  
  14. }  


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

 

[objc]  view plain  copy
  1. #ifdef CONFIG_SYS_RX_ETH_BUFFER  
  2. # define PKTBUFSRX  CONFIG_SYS_RX_ETH_BUFFER  
  3. #else  
  4. # define PKTBUFSRX  4  
  5. #endif  
  6. #define PKTALIGN    32  
  7. #define PKTSIZE  1518  
  8. #define PKTSIZE_ALIGN    1536  
  9.    
  10. //静态分配一个buffer  
  11. volatile uchar  PktBuf[(PKTBUFSRX+1) * PKTSIZE_ALIGN + PKTALIGN];  
  12.    
  13. volatile uchar *NetRxPackets[PKTBUFSRX]; /* Receive packets  */  
  14.    
  15. volatile uchar *NetTxPacket = 0;    /* THE transmit packet   */  
  16.    
  17.    
  18.    
  19. Int NetLoop(proto_t protocol)  
  20. {  
  21.     bd_t *bd = gd->bd;  
  22.    
  23. <pre code_snippet_id="270100" snippet_file_name="blog_20140402_15_3725530" name="code" class="objc"><span style="font-family: Arial, Helvetica, sans-serif;">    #ifdef CONFIG_NET_MULTI</span></pre> NetRestarted = 0; NetDevExists = 0; #endif /* XXX problem with bss workaround */ NetArpWaitPacketMAC = NULL; NetArpWaitTxPacket = NULL; NetArpWaitPacketIP = 0; NetArpWaitReplyIP = 0; NetArpWaitTxPacket = NULL; NetTxPacket = NULL; NetTryCount  
  24.  = 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  
  25.  + (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();  
  26.  //设置环境变量"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  
  27.  = 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  
  28.  // 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  
  29.  */ 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 
  30.  same as BOOTP */ break; #endif case BOOTP: BootpTry = 0; NetOurIP = 0; BootpRequest (); breakcase RARP: RarpTry = 0; NetOurIP = 0; RarpRequest (); break; #if defined(CONFIG_CMD_PING) case PING// 注释3,执行ping 的实质函数。会发送ECHO REQUEST 数据包,并设置接收数据包时的处理函数和超时函数。  
  31.  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:  
  32.  SntpStart(); break; #endif #if defined(CONFIG_CMD_DNS) case DNS: DnsStart(); break; #endif defaultbreak; } NetBootFileXferSize = 0break; } /* * Main packet reception loop. Loop receiving packets until * someone sets `NetState' to a state that terminates. 
  33.  */ 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();  
  34.  /* * 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  
  35.  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",  
  36.  NetBootFileXferSize); setenv("filesize", buf); sprintf(buf, "%lX", (unsigned long)load_addr); setenv("fileaddr", buf); } eth_halt(); return NetBootFileXferSize; case NETLOOP_FAILreturn (-1); } }}<p></p>  
  37. <pre></pre>  
  38. <br>  
  39. <br>  
  40. <p></p>  
  41. <p>注释<span style="font-family:'Times New Roman'">1</span></p>  
  42. <p></p><pre code_snippet_id="270100" snippet_file_name="blog_20140402_16_8643763" name="code" class="objc">void eth_halt(void)  
  43. {  
  44.     if (!eth_current)  
  45.         return;  
  46.     eth_current->halt(eth_current);  
  47.     eth_current->state = ETH_STATE_PASSIVE;  
  48. }</pre><br>  
  49. <p></p>  
  50. <p>调用网口设备虚拟层的<span style="font-family:Times New Roman">halt</span><span style="font-family:宋体">函数,该函数实际是具体网口设备</span><span style="font-family:Times New Roman">IP3912</span><span style="font-family:宋体">的</span><span style="font-family:Times New Roman">halt</span><span style="font-family:宋体">函数,</span></p>  
  51. <p></p><pre code_snippet_id="270100" snippet_file_name="blog_20140402_17_738098" name="code" class="objc">static void ip3912_halt(struct eth_device *netdev)  
  52. {  
  53.     struct ip3912_device *ip3912 = netdev->priv;  
  54.    
  55. /* disable rx-path, tx-path, host registers reset 
  56.  * set FullDuplex, enable RMMI, disable rx+tx 
  57.  * no flow control, no frames<64b 
  58.  */  
  59.     writel(0x000006b8, (voidvoid *)(ip3912->etn_base + ETN_COMMAND));  
  60. }</pre><br>  
  61.  注释<span style="font-family:'Times New Roman'">2</span><p></p>  
  62. <p>同理,<span style="font-family:Times New Roman">eth_init()</span><span style="font-family:宋体">会调用 </span><span style="font-family:Times New Roman">ip3912_init</span><span style="font-family:宋体">。 </span><span style="font-family:Times New Roman">ip3912_init</span><span style="font-family:宋体">主要完成数据包发送和接受之前的初始化工作,要参考</span><span style="font-family:Times New Roman">CPU</span><span style="font-family:宋体">数据手册中</span><span style="font-family:Times New Roman">Ethernet Mac</span><span style="font-family:宋体">模块如何收发数据包的文档。其中,</span><span style="font-family:Times New Roman">ip3912_init_descriptors() </span><span style="font-family:宋体">和 </span><span style="font-family:Times New Roman">ip3912_mii_negotiate_phy()</span><span style="font-family:宋体">比较重要。</span></p>  
  63. <p>注释<span style="font-family:Times New Roman">2</span><span style="font-family:宋体">、注释</span><span style="font-family:Times New Roman">3.3</span><span style="font-family:宋体">和注释</span><span style="font-family:Times New Roman">4</span><span style="font-family:宋体">的一部分 都是和具体网口设备相关的,可以先不看</span><span style="font-family:Times New Roman">,</span><span style="font-family:宋体">先把注意力放在上层程序</span><span style="font-family:Times New Roman">Netloop</span><span style="font-family:宋体">的执行流程和框架上。</span></p>  
  64. <p></p><pre code_snippet_id="270100" snippet_file_name="blog_20140402_18_4194852" name="code" class="objc">int eth_init(bd_t *bis)  
  65. {  
  66.     ...  
  67.     eth_current->init(eth_current,bis)  
  68.     ...   
  69. }</pre><br>  
  70. <br>  
  71. <p></p>  
  72. <p></p><pre code_snippet_id="270100" snippet_file_name="blog_20140402_19_2352472" name="code" class="objc">static int ip3912_init(struct eth_device *netdev, bd_t *bd)  
  73. {  
  74.     struct ip3912_device *ip3912 = netdev->priv;  
  75.    
  76.     /* update mac address in boardinfo */  
  77.     ip3912_setmac(netdev);  
  78.    
  79.     /* before enabling the rx-path we need to set up rx-descriptors */  
  80.     if (ip3912_init_descriptors(netdev))  
  81.         return -1;  
  82.    
  83.     /* set max packet length to 1536 bytes */  
  84.     writel(MAX_ETH_FRAME_SIZE, (voidvoid *)(ip3912->etn_base + ETN_MAXF));  
  85.     /* full duplex */  
  86.     writel(0x00000023, (voidvoid *)(ip3912->etn_base + ETN_MAC2));  
  87.     /* inter packet gap register */  
  88.     writel(0x15, (voidvoid *)(ip3912->etn_base + ETN_IPGT));  
  89.     writel(0x12, (voidvoid *)(ip3912->etn_base + ETN_IPGR));  
  90.     /* enable rx, receive all frames */  
  91.     writel(0x00000003, (voidvoid *)(ip3912->etn_base + ETN_MAC1));  
  92.     /* accept all multicast, broadcast and station packets */  
  93.     writel(0x00000026, (voidvoid *)(ip3912->etn_base + ETN_RXFILTERCTRL));  
  94.    
  95.     if (!l2_switch_present()) {  
  96.         /* reset MII mgmt, set MII clock */  
  97.         writel(0x0000801c, (voidvoid *)(ip3912->etn_base + ETN_MCFG));  
  98.         writel(0x0000001c, (voidvoid *)(ip3912->etn_base + ETN_MCFG));  
  99.     }  
  100.    /* release rx-path, tx-path, host registers reset 
  101.     * set FullDuplex, enable RMMI, enable rx+tx 
  102.     * no flow control, no frames<64b 
  103.     */  
  104.     writel(0x00000683, (voidvoid *)(ip3912->etn_base + ETN_COMMAND));  
  105.     ip3912_init_descriptors(netdev);  
  106.    
  107.     #ifdef CONFIG_DISCOVER_PHY  
  108.     mii_discover_phy();  
  109.     #endif  
  110.    
  111.     if (!l2_switch_present())            
  112.         /* init phy */  
  113.         mii_init_phy(ip3912->phy_addr);  
  114.     /* check autonegotiation */  
  115.     ip3912_i2cl2switch_negotiate_phy();  
  116.    
  117.     #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)  
  118.     /* check autonegotiation */  
  119.     if (!l2_switch_present())                   
  120.         ip3912_mii_negotiate_phy();  // 用于协商传输速率  
  121.     #endif  
  122.    
  123.     return 0;  
  124. }  
  125. static int ip3912_init_descriptors(struct eth_device *netdev)  
  126. {  
  127.     struct ip3912_device *ip3912;  
  128.     static voidvoid *rxbuf;  
  129.     int i;  
  130.    
  131.     ip3912 = netdev->priv;  
  132.    
  133.     /* fill in pointer in regs */  
  134.     writel((unsigned long)etn_rxdescriptor, (voidvoid *)(ip3912->etn_base + ETN_RXDESCRIPTOR));  
  135.     writel((unsigned long)etn_rxstatus, (voidvoid *)(ip3912->etn_base + ETN_RXSTATUS));  
  136.     writel(0x00000000, (voidvoid *)(ip3912->etn_base + ETN_RXCONSUMEINDEX));  
  137.     writel(CONFIG_SYS_ETN_RX_DESCRIPTOR_NUMBER - 1,(voidvoid *)(ip3912->etn_base + ETN_RXDESCRIPTORNUMBER));  
  138.    
  139.     writel((unsigned long)etn_txdescriptor, (voidvoid *)(ip3912->etn_base + ETN_TXDESCRIPTOR));  
  140.     writel((unsigned long)etn_txstatus, (voidvoid *)(ip3912->etn_base + ETN_TXSTATUS));  
  141.     writel(0x00000000, (voidvoid *)(ip3912->etn_base + ETN_TXPRODUCEINDEX));  
  142.     writel(CONFIG_SYS_ETN_TX_DESCRIPTOR_NUMBER - 1,(voidvoid *)(ip3912->etn_base + ETN_TXDESCRIPTORNUMBER));  
  143.    
  144.     /* allocate rx-buffers, but only once, we're called multiple times! */  
  145.     if (!rxbuf)  
  146.        rxbuf = malloc(MAX_ETH_FRAME_SIZE * CONFIG_SYS_ETN_RX_DESCRIPTOR_NUMBER);  
  147.     if (!rxbuf) {  
  148.        puts("ERROR: couldn't allocate rx buffers!\n");  
  149.        return -1;  
  150.     }  
  151.    
  152.     for (i = 0; i < CONFIG_SYS_ETN_RX_DESCRIPTOR_NUMBER; i++) {  
  153.         etn_rxdescriptor[i].packet = rxbuf + i * MAX_ETH_FRAME_SIZE;  
  154.         etn_rxdescriptor[i].control = MAX_ETH_FRAME_SIZE - sizeof(unsigned long);  
  155.         etn_rxstatus[i].info = 0;  
  156.         etn_rxstatus[i].hashCRC = 0;  
  157.     }  
  158.     for (i = 0; i < CONFIG_SYS_ETN_TX_DESCRIPTOR_NUMBER; i++) {  
  159.         etn_txdescriptor[i].packet = 0;  
  160.         etn_txdescriptor[i].control = 0;  
  161.         etn_txstatus[i].info = 0;  
  162.    }  
  163.    return 0;  
  164. }</pre><br>  
  165. <br>  
  166. <p></p>  
  167. <p> </p>  
  168. <p>注释<span style="font-family:Times New Roman">3 </span></p>  
  169. <p></p><pre code_snippet_id="270100" snippet_file_name="blog_20140402_20_1415163" name="code" class="objc">static void PingStart(void)  
  170. {  
  171.     #if defined(CONFIG_NET_MULTI)  
  172.     printf ("Using %s device\n", eth_get_name());  
  173.     #endif  /* CONFIG_NET_MULTI */  
  174.     NetSetTimeout (10000UL, PingTimeout); //设置超时处理函数  
  175.     NetSetHandler (PingHandler);////设置数据包接收处理函数  
  176.    
  177.     PingSend(); //注释3.1  
  178. }  
  179. Void NetSetTimeout(ulong iv, thand_f * f)  
  180. {  
  181.     if (iv == 0) {  
  182.         timeHandler = (thand_f *)0;  
  183.     } else {  
  184.         timeHandler = f;  
  185.         timeStart = get_timer(0);  
  186.         timeDelta = iv;  
  187.     }  
  188. }  
  189. Void NetSetHandler(rxhand_f * f)  
  190. {  
  191.     packetHandler = f;  
  192. }</pre><br>  
  193. <br>  
  194. <p></p>  
  195. <p> //<span style="font-family:宋体">注释</span><span style="font-family:Times New Roman">3.1</span></p>  
  196. <p>发送<span style="font-family:Times New Roman">Echo request</span><span style="font-family:宋体">之前需要知道对方的</span><span style="font-family:Times New Roman">MAC</span><span style="font-family:宋体">地址,这需要发送</span><span style="font-family:Times New Roman">ARP</span><span style="font-family:宋体">数据包。所以</span><span style="font-family:Times New Roman">ARP</span><span style="font-family:宋体">数据包放在</span><span style="font-family:Times New Roman">NetTxPacket</span><span style="font-family:宋体">所指向的</span><span style="font-family:Times New Roman">buffer</span><span style="font-family:宋体">里,而</span><span style="font-family:Times New Roman">Echo request </span><span style="font-family:宋体">数据包放在</span><span style="font-family:Times New Roman">NetArpWaitTxPacket</span><span style="font-family:宋体">所指向的</span><span style="font-family:Times New Roman">buffer</span><span style="font-family:宋体">里,等查询到对方的</span><span style="font-family:Times New Roman">MAC</span><span style="font-family:宋体">地址后,再发送出去。</span></p>  
  197. <p></p><pre code_snippet_id="270100" snippet_file_name="blog_20140402_21_2221959" name="code" class="objc">int PingSend(void)   
  198. {  
  199.     static uchar mac[6];  
  200.     volatile IP_t *ip;  
  201.     volatile ushort *s;  
  202.     uchar *pkt;  
  203.     
  204.     /* XXX always send arp request */  
  205.    
  206.     /* 构造Echo  request  数据包,该数据包不会马上发出,因为目标硬件地址还是空的,   
  207.      * 会先发送ARP request数据包,当收到ARP reply 数据包后,再发送  Echo  request   
  208.      * 数据包 
  209.      */  
  210.     memcpy(mac, NetEtherNullAddr, 6);  
  211.     debug("sending ARP for %08lx\n", NetPingIP);  
  212.     NetArpWaitPacketIP = NetPingIP;  
  213.     NetArpWaitPacketMAC = mac;  
  214.    
  215.     /* 
  216.     *构造Ethernet II 协议头部,目标硬件地址暂定为00:00:00:00:00:00 
  217.     *源MAC地址是NetOurEther,是在NetLOOP()中初始化的。 
  218.     *帧类型是0x0806,PROT_ARP 
  219.     */  
  220.    
  221.     pkt = NetArpWaitTxPacket;  
  222.     pkt += NetSetEther(pkt, mac, PROT_IP);  
  223.    
  224.     ip = (volatile IP_t *)pkt;  
  225.    
  226.     /* 
  227.     * 构造IP协议和ICMP 数据包, 
  228.     */  
  229.    
  230.     /* 
  231.      *  Construct an IP and ICMP header.  (need to set no fragment bit - XXX) 
  232.      */  
  233.     ip->ip_hl_v  = 0x45;  /*版本号和 IP_HDR_SIZE / 4 (not including UDP) */  
  234.     ip->ip_tos   = 0;     //服务类型  
  235.     ip->ip_len   = htons(IP_HDR_SIZE_NO_UDP + 8); // 总长度  
  236.     ip->ip_id    = htons(NetIPID++); // 标示  
  237.     ip->ip_off   = htons(IP_FLAGS_DFRAG);    /* Don't fragment */ //片偏移  
  238.     ip->ip_ttl   = 255//生存时间  
  239.     ip->ip_p     = 0x01;  /* ICMP */ //协议类型  
  240.     ip->ip_sum   = 0;   
  241.     /*源IP地址和目的IP地址*/  
  242.     NetCopyIP((void*)&ip->ip_src, &NetOurIP); /* already in network byte order */  
  243.     NetCopyIP((void*)&ip->ip_dst, &NetPingIP);      /* - "" - */  
  244.     ip->ip_sum   = ~NetCksum((uchar *)ip, IP_HDR_SIZE_NO_UDP / 2); 校验和  
  245.    
  246.     s = &ip->udp_src;     /* XXX ICMP starts here */  
  247.     s[0] = htons(0x0800);    /* echo-request, code *///请求回显  
  248.     s[1] = 0;    /* checksum */  //校验和  
  249.     s[2] = 0;    /* identifier */  //标示符  
  250.     s[3] = htons(PingSeqNo++);  /* sequence number */  // 序列号  
  251.     s[1] = ~NetCksum((uchar *)s, 8/2);   
  252.    
  253.     /* size of the waiting packet */  
  254.     NetArpWaitTxPacketSize = (pkt - NetArpWaitTxPacket) + IP_HDR_SIZE_NO_UDP + 8;  
  255.    
  256.    /* 
  257.     *  设置Arp的超时时间的起始点,ArpTimeoutCheck()会处理ARP超时的问题 
  258.     */  
  259.     /* and do the ARP request */  
  260.     NetArpWaitTry = 1;  
  261.     NetArpWaitTimerStart = get_timer(0);  
  262.    /* 
  263.     *发送ARP request,退出pingsend(),程序会在NetLOOP中循环接收数据包,并调用处     
  264.     *理函数。 
  265.     */  
  266.     ArpRequest(); // 注释3.2  
  267.     return 1;   /* waiting */  
  268. }</pre><img src="https://img-blog.csdn.net/20140402111638250?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbmFuYW94dWU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt=""><br>  
  269. <br>  
  270. <p></p>  
  271. <p>                                                         图      将要发送的<span style="font-family:'Times New Roman'">ping echo request </span><span style="font-family:宋体">数据包</span></p>  
  272. <p>注释<span style="font-family:Times New Roman">3.2</span></p>  
  273. <p>//<span style="font-family:宋体">发送</span><span style="font-family:Times New Roman">ARP request</span></p>  
  274. <p></p><pre code_snippet_id="270100" snippet_file_name="blog_20140402_22_9125059" name="code" class="objc">void ArpRequest (void)  
  275. {  
  276.     int i;  
  277.     volatile uchar *pkt;  
  278.     ARP_t *arp;  
  279.    
  280.     debug("ARP broadcast %d\n", NetArpWaitTry);  
  281.    
  282.     pkt = NetTxPacket;  
  283.     /* 
  284.      *构造Ethernet II 协议头部,NetBcastAddr是广播地址,FF:FF:FF:FF:FF:FF 
  285.      *源MAC地址是NetOurEther,是在NetLOOP()中初始化的。 
  286.      *帧类型是0x0806,PROT_ARP 
  287.      */  
  288.     pkt += NetSetEther (pkt, NetBcastAddr, PROT_ARP);  
  289.     /* 
  290.     * 构造ARP协议数据包, 
  291.     */  
  292.     arp = (ARP_t *) pkt;  
  293.     
  294.     arp->ar_hrd = htons (ARP_ETHER); //硬件类型ARP_ETHER = 1  表示以太网  
  295.     arp->ar_pro = htons (PROT_IP);    // 协议类型 表示要映射的协议地址类型  
  296.     arp->ar_hln = 6;  //硬件地址长度 MAC地址字节数  
  297.     arp->ar_pln = 4;  //协议地址长度 IP地址字节数  
  298.     arp->ar_op = htons (ARPOP_REQUEST); //操作类型ARPOP_REQUEST=1表示ARP请求  
  299.    
  300.     memcpy (&arp->ar_data[0], NetOurEther, 6);    /* source ET addr  */  
  301.     NetWriteIP ((uchar *) & arp->ar_data[6], NetOurIP);  /* source IP addr   */  
  302.     for (i = 10; i < 16; ++i) {   
  303.         arp->ar_data[i] = 0;  /* dest ET addr = 0     */  
  304.     }  
  305.     // 接收方的IP地址,若不在同一网段,需要设置环境变量gatewayip  
  306.     if ((NetArpWaitPacketIP & NetOurSubnetMask) != (NetOurIP & NetOurSubnetMask)) {  
  307.         if (NetOurGatewayIP == 0) {  
  308.             puts ("## Warning: gatewayip needed but not set\n");  
  309.             NetArpWaitReplyIP = NetArpWaitPacketIP;  
  310.         } else {  
  311.             NetArpWaitReplyIP = NetOurGatewayIP;  
  312.         }  
  313.     } else {  
  314.         NetArpWaitReplyIP = NetArpWaitPacketIP;  
  315.     }  
  316.    
  317.     NetWriteIP ((uchar *) & arp->ar_data[16], NetArpWaitReplyIP);  
  318.     //调用发送函数,  
  319.     (void) eth_send (NetTxPacket, (pkt - NetTxPacket) + ARP_HDR_SIZE);注释3.3  
  320. }</pre><br>  
  321. <img src="https://img-blog.csdn.net/20140402112024453?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbmFuYW94dWU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt=""><br>  
  322. <p></p>  
  323. <p>                                                 图         发送的<span style="font-family:'Times New Roman'">ARP</span><span style="font-family:宋体">数据包</span></p>  
  324. <p>     </p>  
  325. <p> 注释<span style="font-family:'Times New Roman'">3.3</span></p>  
  326. <p>//IP3912<span style="font-family:宋体">发送函数的原理,可以参照</span><span style="font-family:Times New Roman">CPU Ethernet </span><span style="font-family:宋体">模块章节</span></p>  
  327. <p></p><pre code_snippet_id="270100" snippet_file_name="blog_20140402_23_2647853" name="code" class="objc">int eth_send(volatile voidvoid *packet, int length)  
  328. {  
  329.     if (!eth_current)  
  330.         return -1;  
  331.     return eth_current->send(eth_current, packet, length);  
  332. }</pre><span style="background-color:rgb(240,240,240)">static int ip3912_send(struct eth_device *netdev, volatile voidvoid *packet, int length)</span><p></p>  
  333. <p></p><pre code_snippet_id="270100" snippet_file_name="blog_20140402_24_6519310" name="code" class="objc">{  
  334.    略  
  335. }</pre><p></p>  
  336. <p> </p>  
  337. <p>注释<span style="font-family:'Times New Roman'">4</span></p>  
  338. <p>可以只看注释<span style="font-family:Times New Roman">4.1</span></p>  
  339. <p></p><pre code_snippet_id="270100" snippet_file_name="blog_20140402_25_5058614" name="code" class="objc">int eth_rx(void)  
  340. {  
  341.     if (!eth_current)  
  342.         return -1;  
  343.    
  344.     return eth_current->recv(eth_current);  
  345. }</pre><br>  
  346. <br>  
  347. <p></p>  
  348. <p>//IP3912<span style="font-family:宋体">接收函数的原理,可以参照</span><span style="font-family:Times New Roman">CPU Ethernet </span><span style="font-family:宋体">模块章节</span></p>  
  349. <p></p><pre code_snippet_id="270100" snippet_file_name="blog_20140402_26_5865409" name="code" class="objc">/* Check for received packets */  
  350. static int ip3912_recv(struct eth_device *netdev)  
  351. {  
  352. 略  
  353. }</pre><br>  
  354.    
  355.     return eth_current->recv(eth_current);  
  356. }</pre><br>  
  357. <br>  
  358. <p></p>  
  359. <p>//IP3912<span style="font-family:宋体">接收函数的原理,可以参照</span><span style="font-family:Times New Roman">CPU Ethernet </span><span style="font-family:宋体">模块章节</span></p>  
  360. <p></p><pre code_snippet_id="270100" snippet_file_name="blog_20140402_26_5865409" name="code" class="objc">/* Check for received packets */  
  361. static int ip3912_recv(struct eth_device *netdev)  
  362. {  
  363. 略  
  364. }</pre><br>  
  365. <p></p>  
  366. }</pre><br>  
  367. <br>  
  368. <p></p>  
  369. <p>//IP3912<span style="font-family:宋体">接收函数的原理,可以参照</span><span style="font-family:Times New Roman">CPU Ethernet </span><span style="font-family:宋体">模块章节</span></p>  
  370. <p></p><pre code_snippet_id="270100" snippet_file_name="blog_20140402_26_5865409" name="code" class="objc">/* Check for received packets */  
  371. static int ip3912_recv(struct eth_device *netdev)  
  372. {  
  373. 略  
  374. }</pre><br>  
  375. <p></p>  
  376. <p>注释<span style="font-family:Times New Roman">struct eth_device *netdev)  
相关文章
相关标签/搜索