这篇文章主要讲解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((void *)(ETN1_BASE + ETN_MIND)) & 0x7; //
if (!status)
return;
udelay(1);
}
}
static u16 detect_phy_read(u8 address, u8 reg)
{
u16 value;
writel((address << 8) | reg, (void *)(ETN1_BASE + ETN_MADR)); //PHY地址右移或上
//reg地址
writel(0x00000001, (void *)(ETN1_BASE + ETN_MCMD));
detect_phy_wait();
value = readl((void *)(ETN1_BASE + ETN_MRDD));
writel(0x00000000, (void *)(ETN1_BASE + ETN_MCMD));
return value;
}
int detect_phy_write(u8 address, u8 reg,int value)
{
writel(address << 8 | reg,(void *)(ETN1_BASE + ETN_MADR));
__raw_writel(value, (void *)(ETN1_BASE + ETN_MWTD));
detect_phy_wait();
}
解释
init_etn0();主要工作是初始化CPU Ethernet Mac模块,GPIO口等等.
detect_IC_PLUS_173T检测IP173,成功返回1,反之返回0.
读寄存器流程:
将PHY地址右移或上地址写入地址寄存器(见图三),因为zzzz有两种读,循环读和单次读,所以需要写1到命令寄存器。等待读完成。从读寄存器读出值。
写寄存器流程:
将PHY地址右移或上地址写入地址寄存器,将要写的值写入写寄存器。等待写完成。
图(一)
图(二) CPU Ethernet Mac 模块的寄存器(部分)
图(三)
(二) 向网口设备虚拟层eth_device注册各类操作函数和数据。
检测到IP173之后,start_armboot会继续完成其他初始化工作,如FLASH,SDRAM,串口、中断、环境变量等等,期间会设置IP地址
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
然后调用eth_initialize()函数。之后等待用户输入命令或者启动kernel 。
eth_initialize()大致流程是调用具体的网卡驱动程序(如ip3912_send,ip3912_recv)来初始化网口设备虚拟层的框架(eth_device->send,eth_device->recv),这些程序为应用程序如ping、tftpboot提供服务(如PingSend,TftpSend等)
eth_initialize()位于net/eth.c文件内,eth.c就是网口设备虚拟层的源码,其提供eth_initialize,eth_register,eth_get_dev,eth_init,eth_halt,eth_send,eth_rx,eth_receive,eth_try_another,
eth_set_current等函数。
eth.c文件被#ifdef CONFIG_NET_MULTI分成两部分,关于CONFIG_NET_MULTI的含义参考附录一。
eth.c的主要数据结构如下:
struct eth_device {
char name[NAMESIZE]; //设备名
unsigned char enetaddr[6]; // mac 地址
int iobase;
int state;
int (*init) (struct eth_device*, bd_t*);
int (*send) (struct eth_device*, volatile void* packet, int length);
int (*recv) (struct eth_device*);
void (*halt) (struct eth_device*);
#ifdef CONFIG_MCAST_TFTP
int (*mcast) (struct eth_device*, u32 ip, u8 set);
#endif
int (*write_hwaddr) (struct eth_device*); //设置MAC的函数
struct eth_device *next; // 指向下一个网口设备
void *priv;
};
当要支持多播TFTP时,必须开启CONFIG_MCAST_TFTP,并定义mcast()函数(见readme)。
下面来具体看下 eth_initialize函数
int eth_initialize(bd_t *bis)
{
unsigned char env_enetaddr[6];
int eth_number = 0;
eth_devices = NULL;
eth_current = NULL; //注释1
show_boot_progress (64);
miiphy_init(); //注释2
//设置 eth_devices =eth_current = netdev, netdev含有IP3912的 init,send ,recv ,priv等
//信息,也会设置 mii_devs和 current_mii
if (board_eth_init(bis) < 0) //注释3
cpu_eth_init(bis);
if (!eth_devices) {
puts ("No ethernet found.\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_halt,ip3912_recv,ip3912_send,ip3912_init等
网口设备虚拟层eth_device所需要的函数。若需要注册MII设备,则还需要编写ip3912_miiphy_write ,ip3912_miiphy_read等函数。
注释1
eth_current比较重要,因为tftpsend等函数最终会调用该变量指向的eth_device中的函数。
int eth_send(volatile void *packet, int length)
{
if (!eth_current)
return -1;
return eth_current->send(eth_current, packet, length);
}
注释2
void miiphy_init(void)
{
INIT_LIST_HEAD (&mii_devs);
current_mii = NULL;
}
struct mii_dev {
struct list_head link;
const char *name;
int (*read) (const char *devname, unsigned char addr,unsigned char reg, unsigned short *value);
int (*write) (const char *devname, unsigned char addr,
};
IP3912 属于 PHY 层,其驱动程序会调用 miiphy_register 注册一个 mii_dev 。
注释3
board_eth_init() 是属于具体开发板范畴,本例位于board/dspg/firetux/firetux.c内,
主要工作是调用板子所用网口芯片(IP3912)的初始化函数。
int board_eth_init(bd_t *bis)
{
...
ip3912_miiphy_initialize(gd->bd);
...
ip3912_eth_initialize(0,CONFIG_IP3912_ETN1_BASE,CONFIG_IP3912_ETN1_BASE, 0x0, 1);
...
setenv("ethact", "ETN1");
eth_set_current(); //一般会在ip3912_eth_initialize这些具体设备的初始化函数中调用。
}
int ip3912_miiphy_initialize(bd_t *bis)
{
miiphy_register("ip3912", ip3912_miiphy_read, ip3912_miiphy_write);
return 0;
}
void miiphy_register(const char *name,
int (*read) (const char *devname, unsigned char addr,
unsigned char reg, unsigned short *value),
int (*write) (const char *devname, unsigned char addr,
unsigned char reg, unsigned short value))
{
...
//检查是否已经注册了一个同名的mii设备
new_dev = miiphy_get_dev_by_name(name, 1);
// 是则直接退出
if (new_dev) {
printf("miiphy_register: non unique device name '%s'\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;
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); } }}#ifdef CONFIG_NET_MULTI
注释1
void eth_halt(void) { if (!eth_current) return; eth_current->halt(eth_current); eth_current->state = ETH_STATE_PASSIVE; }
调用网口设备虚拟层的halt函数,该函数实际是具体网口设备IP3912的halt函数,
static void ip3912_halt(struct eth_device *netdev) { struct ip3912_device *ip3912 = netdev->priv; /* disable rx-path, tx-path, host registers reset * set FullDuplex, enable RMMI, disable rx+tx * no flow control, no frames<64b */ writel(0x000006b8, (void *)(ip3912->etn_base + ETN_COMMAND)); }
注释2同理,eth_init()会调用 ip3912_init。 ip3912_init主要完成数据包发送和接受之前的初始化工作,要参考CPU数据手册中Ethernet Mac模块如何收发数据包的文档。其中,ip3912_init_descriptors() 和 ip3912_mii_negotiate_phy()比较重要。
注释2、注释3.3和注释4的一部分 都是和具体网口设备相关的,可以先不看,先把注意力放在上层程序Netloop的执行流程和框架上。
int eth_init(bd_t *bis) { ... eth_current->init(eth_current,bis) ... }
static int ip3912_init(struct eth_device *netdev, bd_t *bd) { struct ip3912_device *ip3912 = netdev->priv; /* update mac address in boardinfo */ ip3912_setmac(netdev); /* before enabling the rx-path we need to set up rx-descriptors */ if (ip3912_init_descriptors(netdev)) return -1; /* set max packet length to 1536 bytes */ writel(MAX_ETH_FRAME_SIZE, (void *)(ip3912->etn_base + ETN_MAXF)); /* full duplex */ writel(0x00000023, (void *)(ip3912->etn_base + ETN_MAC2)); /* inter packet gap register */ writel(0x15, (void *)(ip3912->etn_base + ETN_IPGT)); writel(0x12, (void *)(ip3912->etn_base + ETN_IPGR)); /* enable rx, receive all frames */ writel(0x00000003, (void *)(ip3912->etn_base + ETN_MAC1)); /* accept all multicast, broadcast and station packets */ writel(0x00000026, (void *)(ip3912->etn_base + ETN_RXFILTERCTRL)); if (!l2_switch_present()) { /* reset MII mgmt, set MII clock */ writel(0x0000801c, (void *)(ip3912->etn_base + ETN_MCFG)); writel(0x0000001c, (void *)(ip3912->etn_base + ETN_MCFG)); } /* release rx-path, tx-path, host registers reset * set FullDuplex, enable RMMI, enable rx+tx * no flow control, no frames<64b */ writel(0x00000683, (void *)(ip3912->etn_base + ETN_COMMAND)); ip3912_init_descriptors(netdev); #ifdef CONFIG_DISCOVER_PHY mii_discover_phy(); #endif if (!l2_switch_present()) /* init phy */ mii_init_phy(ip3912->phy_addr); /* check autonegotiation */ ip3912_i2cl2switch_negotiate_phy(); #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) /* check autonegotiation */ if (!l2_switch_present()) ip3912_mii_negotiate_phy(); // 用于协商传输速率 #endif return 0; } static int ip3912_init_descriptors(struct eth_device *netdev) { struct ip3912_device *ip3912; static void *rxbuf; int i; ip3912 = netdev->priv; /* fill in pointer in regs */ writel((unsigned long)etn_rxdescriptor, (void *)(ip3912->etn_base + ETN_RXDESCRIPTOR)); writel((unsigned long)etn_rxstatus, (void *)(ip3912->etn_base + ETN_RXSTATUS)); writel(0x00000000, (void *)(ip3912->etn_base + ETN_RXCONSUMEINDEX)); writel(CONFIG_SYS_ETN_RX_DESCRIPTOR_NUMBER - 1,(void *)(ip3912->etn_base + ETN_RXDESCRIPTORNUMBER)); writel((unsigned long)etn_txdescriptor, (void *)(ip3912->etn_base + ETN_TXDESCRIPTOR)); writel((unsigned long)etn_txstatus, (void *)(ip3912->etn_base + ETN_TXSTATUS)); writel(0x00000000, (void *)(ip3912->etn_base + ETN_TXPRODUCEINDEX)); writel(CONFIG_SYS_ETN_TX_DESCRIPTOR_NUMBER - 1,(void *)(ip3912->etn_base + ETN_TXDESCRIPTORNUMBER)); /* allocate rx-buffers, but only once, 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; }
注释3
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 模块章节
static int ip3912_send(struct eth_device *netdev, volatile void *packet, int length)int eth_send(volatile void *packet, int length) { if (!eth_current) return -1; return eth_current->send(eth_current, packet, 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 * (wd@denx.de), or - even better - send me the * necessary fixes :-) * * Note: in all cases where I have seen this so far * it was a problem with the router configuration, * for instance when a router was configured in the * BOOTP reply, but the TFTP server was on the same * subnet. So this is probably a warning that your * configuration might be wrong. But 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 。
一个IP173有5个PHY,算一个设备,还是多个设备?
Readme文档中也讲到两个和CONFIG_NET_MULTI 有关的环境变量。
ethprime - When CONFIG_NET_MULTI is enabled controls which
interface is used first.
ethact - When CONFIG_NET_MULTI is enabled controls which
interface is currently active. For example you
can do the following
void NetReceive(volatile uchar * inpkt, int len) { Ethernet_t *et; IP_t *ip; ARP_t *arp; IPaddr_t tmp; int x; uchar *pkt; ushort cti = 0, vlanid = VLAN_NONE, myvlanid, mynvlanid; debug("packet received\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 * (wd@denx.de), or - even better - send me the * necessary fixes :-) * * Note: in all cases where I have seen this so far * it was a problem with the router configuration, * for instance when a router was configured in the * BOOTP reply, but the TFTP server was on the same * subnet. So this is probably a warning that your * configuration might be wrong. But 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