这篇文章主要讲解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\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((voidvoid *)(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, (voidvoid *)(ETN1_BASE + ETN_MADR)); //PHY地址右移或上
- //reg地址
- writel(0x00000001, (voidvoid *)(ETN1_BASE + ETN_MCMD));
- detect_phy_wait();
-
- value = readl((voidvoid *)(ETN1_BASE + ETN_MRDD));
- writel(0x00000000, (voidvoid *)(ETN1_BASE + ETN_MCMD));
- return value;
- }
- int detect_phy_write(u8 address, u8 reg,int value)
- {
- writel(address << 8 | reg,(voidvoid *)(ETN1_BASE + ETN_MADR));
- __raw_writel(value, (voidvoid *)(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; // 指向下一个网口设备
- voidvoid *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;
- charchar *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) {
- charchar *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_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 voidvoid *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 charchar *name;
- int (*read) (const charchar *devname, unsigned char addr,unsigned char reg, unsigned shortshort *value);
- int (*write) (const charchar *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 charchar *name,
- int (*read) (const charchar *devname, unsigned char addr,
- unsigned char reg, unsigned shortshort *value),
- int (*write) (const charchar *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 = (charchar *)(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 = (voidvoid *)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;
- {
- charchar *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;
- charchar *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],
- (voidvoid *)(ip3912->etn_base + ETN_SA0));
- writel((netdev->enetaddr[3] << 8) | netdev->enetaddr[2],
- (voidvoid *)(ip3912->etn_base + ETN_SA1));
- writel((netdev->enetaddr[1] << 8) | netdev->enetaddr[0],
- (voidvoid *)(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, charchar * 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;
-
- <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
- = 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); } }}<p></p>
- <pre></pre>
- <br>
- <br>
- <p></p>
- <p>注释<span style="font-family:'Times New Roman'">1</span></p>
- <p></p><pre code_snippet_id="270100" snippet_file_name="blog_20140402_16_8643763" name="code" class="objc">void eth_halt(void)
- {
- if (!eth_current)
- return;
- eth_current->halt(eth_current);
- eth_current->state = ETH_STATE_PASSIVE;
- }</pre><br>
- <p></p>
- <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>
- <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)
- {
- 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, (voidvoid *)(ip3912->etn_base + ETN_COMMAND));
- }</pre><br>
- 注释<span style="font-family:'Times New Roman'">2</span><p></p>
- <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>
- <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>
- <p></p><pre code_snippet_id="270100" snippet_file_name="blog_20140402_18_4194852" name="code" class="objc">int eth_init(bd_t *bis)
- {
- ...
- eth_current->init(eth_current,bis)
- ...
- }</pre><br>
- <br>
- <p></p>
- <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)
- {
- 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, (voidvoid *)(ip3912->etn_base + ETN_MAXF));
- /* full duplex */
- writel(0x00000023, (voidvoid *)(ip3912->etn_base + ETN_MAC2));
- /* inter packet gap register */
- writel(0x15, (voidvoid *)(ip3912->etn_base + ETN_IPGT));
- writel(0x12, (voidvoid *)(ip3912->etn_base + ETN_IPGR));
- /* enable rx, receive all frames */
- writel(0x00000003, (voidvoid *)(ip3912->etn_base + ETN_MAC1));
- /* accept all multicast, broadcast and station packets */
- writel(0x00000026, (voidvoid *)(ip3912->etn_base + ETN_RXFILTERCTRL));
-
- if (!l2_switch_present()) {
- /* reset MII mgmt, set MII clock */
- writel(0x0000801c, (voidvoid *)(ip3912->etn_base + ETN_MCFG));
- writel(0x0000001c, (voidvoid *)(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, (voidvoid *)(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 voidvoid *rxbuf;
- int i;
-
- ip3912 = netdev->priv;
-
- /* fill in pointer in regs */
- writel((unsigned long)etn_rxdescriptor, (voidvoid *)(ip3912->etn_base + ETN_RXDESCRIPTOR));
- writel((unsigned long)etn_rxstatus, (voidvoid *)(ip3912->etn_base + ETN_RXSTATUS));
- writel(0x00000000, (voidvoid *)(ip3912->etn_base + ETN_RXCONSUMEINDEX));
- writel(CONFIG_SYS_ETN_RX_DESCRIPTOR_NUMBER - 1,(voidvoid *)(ip3912->etn_base + ETN_RXDESCRIPTORNUMBER));
-
- writel((unsigned long)etn_txdescriptor, (voidvoid *)(ip3912->etn_base + ETN_TXDESCRIPTOR));
- writel((unsigned long)etn_txstatus, (voidvoid *)(ip3912->etn_base + ETN_TXSTATUS));
- writel(0x00000000, (voidvoid *)(ip3912->etn_base + ETN_TXPRODUCEINDEX));
- writel(CONFIG_SYS_ETN_TX_DESCRIPTOR_NUMBER - 1,(voidvoid *)(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;
- }</pre><br>
- <br>
- <p></p>
- <p> </p>
- <p>注释<span style="font-family:Times New Roman">3 </span></p>
- <p></p><pre code_snippet_id="270100" snippet_file_name="blog_20140402_20_1415163" name="code" class="objc">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;
- }</pre><br>
- <br>
- <p></p>
- <p> //<span style="font-family:宋体">注释</span><span style="font-family:Times New Roman">3.1</span></p>
- <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>
- <p></p><pre code_snippet_id="270100" snippet_file_name="blog_20140402_21_2221959" name="code" class="objc">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 */
- }</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>
- <br>
- <p></p>
- <p> 图 将要发送的<span style="font-family:'Times New Roman'">ping echo request </span><span style="font-family:宋体">数据包</span></p>
- <p>注释<span style="font-family:Times New Roman">3.2</span></p>
- <p>//<span style="font-family:宋体">发送</span><span style="font-family:Times New Roman">ARP request</span></p>
- <p></p><pre code_snippet_id="270100" snippet_file_name="blog_20140402_22_9125059" name="code" class="objc">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
- }</pre><br>
- <img src="https://img-blog.csdn.net/20140402112024453?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbmFuYW94dWU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt=""><br>
- <p></p>
- <p> 图 发送的<span style="font-family:'Times New Roman'">ARP</span><span style="font-family:宋体">数据包</span></p>
- <p> </p>
- <p> 注释<span style="font-family:'Times New Roman'">3.3</span></p>
- <p>//IP3912<span style="font-family:宋体">发送函数的原理,可以参照</span><span style="font-family:Times New Roman">CPU Ethernet </span><span style="font-family:宋体">模块章节</span></p>
- <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)
- {
- if (!eth_current)
- return -1;
- return eth_current->send(eth_current, packet, length);
- }</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>
- <p></p><pre code_snippet_id="270100" snippet_file_name="blog_20140402_24_6519310" name="code" class="objc">{
- 略
- }</pre><p></p>
- <p> </p>
- <p>注释<span style="font-family:'Times New Roman'">4</span></p>
- <p>可以只看注释<span style="font-family:Times New Roman">4.1</span></p>
- <p></p><pre code_snippet_id="270100" snippet_file_name="blog_20140402_25_5058614" name="code" class="objc">int eth_rx(void)
- {
- if (!eth_current)
- return -1;
-
- return eth_current->recv(eth_current);
- }</pre><br>
- <br>
- <p></p>
- <p>//IP3912<span style="font-family:宋体">接收函数的原理,可以参照</span><span style="font-family:Times New Roman">CPU Ethernet </span><span style="font-family:宋体">模块章节</span></p>
- <p></p><pre code_snippet_id="270100" snippet_file_name="blog_20140402_26_5865409" name="code" class="objc">/* Check for received packets */
- static int ip3912_recv(struct eth_device *netdev)
- {
- 略
- }</pre><br>
-
- return eth_current->recv(eth_current);
- }</pre><br>
- <br>
- <p></p>
- <p>//IP3912<span style="font-family:宋体">接收函数的原理,可以参照</span><span style="font-family:Times New Roman">CPU Ethernet </span><span style="font-family:宋体">模块章节</span></p>
- <p></p><pre code_snippet_id="270100" snippet_file_name="blog_20140402_26_5865409" name="code" class="objc">/* Check for received packets */
- static int ip3912_recv(struct eth_device *netdev)
- {
- 略
- }</pre><br>
- <p></p>
- }</pre><br>
- <br>
- <p></p>
- <p>//IP3912<span style="font-family:宋体">接收函数的原理,可以参照</span><span style="font-family:Times New Roman">CPU Ethernet </span><span style="font-family:宋体">模块章节</span></p>
- <p></p><pre code_snippet_id="270100" snippet_file_name="blog_20140402_26_5865409" name="code" class="objc">/* Check for received packets */
- static int ip3912_recv(struct eth_device *netdev)
- {
- 略
- }</pre><br>
- <p></p>
- <p>注释<span style="font-family:Times New Roman">struct eth_device *netdev)