這篇文章主要講解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;
#ifdef CONFIG_NET_MULTI
NetRestarted = 0; NetDevExists = 0; #endif /* XXX problem with bss workaround */ NetArpWaitPacketMAC = NULL; NetArpWaitTxPacket = NULL; NetArpWaitPacketIP = 0; NetArpWaitReplyIP = 0; NetArpWaitTxPacket = NULL; NetTxPacket = NULL; NetTryCount
= 1; // 初始化各類發送,接收buffer指針。 if (!NetTxPacket) { int i; /* * Setup packet buffers, aligned correctly. */ NetTxPacket = &PktBuf[0] + (PKTALIGN - 1); NetTxPacket -= (ulong)NetTxPacket % PKTALIGN; for (i = 0; i < PKTBUFSRX; i++) { NetRxPackets[i] = NetTxPacket
+ (i+1)*PKTSIZE_ALIGN; } } if (!NetArpWaitTxPacket) { NetArpWaitTxPacket = &NetArpWaitPacketBuf[0] + (PKTALIGN - 1); NetArpWaitTxPacket -= (ulong)NetArpWaitTxPacket % PKTALIGN; NetArpWaitTxPacketSize = 0; } eth_halt(); //註釋1 #ifdef CONFIG_NET_MULTI eth_set_current();
//設置環境變量"ethact"。如果已經設置,就直接退出。 #endif if (eth_init(bd) < 0) { //註釋2 eth_halt(); return(-1); } restart: #ifdef CONFIG_NET_MULTI memcpy (NetOurEther, eth_get_dev()->enetaddr, 6); // 設置 NetOurEther #else eth_getenv_enetaddr("ethaddr", NetOurEther); #endif NetState
= NETLOOP_CONTINUE; // 設置 NetOurEther /* * Start the ball rolling with the given start function. From * here on, this code is a state machine driven by received * packets and timer events. */ // 設置 NetOurIP NetOurGatewayIP NetOurSubnetMask NetServerIP NetOurNativeVLAN
// NetOurVLAN NetOurDNSIP NetInitLoop(protocol); switch (net_check_prereq (protocol)) { // 檢查所需要的參數是否都有合適的值 case 1: /* network not configured */ eth_halt(); return (-1); #ifdef CONFIG_NET_MULTI case 2: /* network device not configured */ break; #endif /* CONFIG_NET_MULTI
*/ case 0: #ifdef CONFIG_NET_MULTI NetDevExists = 1; #endif switch (protocol) { case TFTP: /* always use ARP to get server ethernet address */ TftpStart(); break; #if defined(CONFIG_CMD_DHCP) case DHCP: BootpTry = 0; NetOurIP = 0; DhcpRequest(); /* Basically
same as BOOTP */ break; #endif case BOOTP: BootpTry = 0; NetOurIP = 0; BootpRequest (); break; case RARP: RarpTry = 0; NetOurIP = 0; RarpRequest (); break; #if defined(CONFIG_CMD_PING) case PING: // 註釋3,執行ping 的實質函數。會發送ECHO REQUEST 數據包,並設置接收數據包時的處理函數和超時函數。
PingStart(); break; #endif #if defined(CONFIG_CMD_NFS) case NFS: NfsStart(); break; #endif #if defined(CONFIG_CMD_CDP) case CDP: CDPStart(); break; #endif #ifdef CONFIG_NETCONSOLE case NETCONS: NcStart(); break; #endif #if defined(CONFIG_CMD_SNTP) case SNTP:
SntpStart(); break; #endif #if defined(CONFIG_CMD_DNS) case DNS: DnsStart(); break; #endif default: break; } NetBootFileXferSize = 0; break; } /* * Main packet reception loop. Loop receiving packets until * someone sets `NetState' to a state that terminates.
*/ for (;;) { WATCHDOG_RESET();/* * Check the ethernet for a new packet. The ethernet * receive routine will process it. */ eth_rx(); // 循環接受數據包,註釋4/* * Abort if ctrl-c was pressed. */ if (ctrlc()) { eth_halt(); puts ("\nAbort\n"); return (-1); } ArpTimeoutCheck();
/* * Check for a timeout, and run the timeout handler * if we have one. */ if (timeHandler && ((get_timer(0) - timeStart) > timeDelta)) { thand_f *x; x = timeHandler; timeHandler = (thand_f *)0; (*x)(); } switch (NetState) { case NETLOOP_RESTART: #ifdef CONFIG_NET_MULTI
NetRestarted = 1; #endif goto restart; case NETLOOP_SUCCESS: //若有接收文件,則打印文件大小,並設置環境變量filesize,fileaddr if (NetBootFileXferSize > 0) { char buf[20]; printf("Bytes transferred = %ld (%lx hex)\n", NetBootFileXferSize, NetBootFileXferSize); sprintf(buf, "%lX",
NetBootFileXferSize); setenv("filesize", buf); sprintf(buf, "%lX", (unsigned long)load_addr); setenv("fileaddr", buf); } eth_halt(); return NetBootFileXferSize; case NETLOOP_FAIL: return (-1); } }}
註釋1
void eth_halt(void)
{
if (!eth_current)
return;
eth_current->halt(eth_current);
eth_current->state = ETH_STATE_PASSIVE;
}
調用網口設備虛擬層的halt函數,該函數實際是具體網口設備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 模塊章節
int eth_send(volatile void *packet, int length)
{
if (!eth_current)
return -1;
return eth_current->send(eth_current, packet, length);
}
static int ip3912_send(struct eth_device *netdev, volatile void *packet, int length)
{
略
}
註釋4
可以只看註釋4.1
int eth_rx(void)
{
if (!eth_current)
return -1;
return eth_current->recv(eth_current);
}
//IP3912接收函數的原理,可以參照CPU Ethernet 模塊章節
/* Check for received packets */
static int ip3912_recv(struct eth_device *netdev)
{
略
}
註釋4.1
void NetReceive(volatile uchar * inpkt, int len)
{
Ethernet_t *et;
IP_t *ip;
ARP_t *arp;
IPaddr_t tmp;
int x;
uchar *pkt;
ushort cti = 0, vlanid = VLAN_NONE, myvlanid, mynvlanid;
debug("packet received\n");
NetRxPacket = inpkt;
NetRxPacketLen = len;
et = (Ethernet_t *)inpkt;
/* too small packet? */
if (len < ETHER_HDR_SIZE)
return;
myvlanid = ntohs(NetOurVLAN);
if (myvlanid == (ushort)-1)
myvlanid = VLAN_NONE;
mynvlanid = ntohs(NetOurNativeVLAN);
if (mynvlanid == (ushort)-1)
mynvlanid = VLAN_NONE;
x = ntohs(et->et_protlen);
debug("packet received\n");
if (x < 1514) {
/*
* Got a 802 packet. Check the other protocol field.
*/
x = ntohs(et->et_prot);
ip = (IP_t *)(inpkt + E802_HDR_SIZE);
len -= E802_HDR_SIZE;
} else if (x != PROT_VLAN) { /* normal packet */
ip = (IP_t *)(inpkt + ETHER_HDR_SIZE);
len -= ETHER_HDR_SIZE;
} else { /* VLAN packet */
VLAN_Ethernet_t *vet = (VLAN_Ethernet_t *)et;
debug("VLAN packet received\n");
/* too small packet? */
if (len < VLAN_ETHER_HDR_SIZE)
return;
/* if no VLAN active */
if ((ntohs(NetOurVLAN) & VLAN_IDMASK) == VLAN_NONE)
return;
cti = ntohs(vet->vet_tag);
vlanid = cti & VLAN_IDMASK;
x = ntohs(vet->vet_type);
ip = (IP_t *)(inpkt + VLAN_ETHER_HDR_SIZE);
len -= VLAN_ETHER_HDR_SIZE;
}
debug("Receive from protocol 0x%x\n", x);
if ((myvlanid & VLAN_IDMASK) != VLAN_NONE) {
if (vlanid == VLAN_NONE)
vlanid = (mynvlanid & VLAN_IDMASK);
/* not matched? */
if (vlanid != (myvlanid & VLAN_IDMASK))
return;
}
switch (x) {
case PROT_ARP:
/*
* We have to deal with two types of ARP packets:
* - REQUEST packets will be answered by sending our
* IP address - if we know it.
* - REPLY packates are expected only after we asked
* for the TFTP server's or the gateway's ethernet
* address; so if we receive such a packet, we set
* the server ethernet address
*/
debug("Got ARP\n");
arp = (ARP_t *)ip;
if (len < ARP_HDR_SIZE) {
printf("bad length %d < %d\n", len, ARP_HDR_SIZE);
return;
}
if (ntohs(arp->ar_hrd) != ARP_ETHER) {
return;
}
if (ntohs(arp->ar_pro) != PROT_IP) {
return;
}
if (arp->ar_hln != 6) {
return;
}
if (arp->ar_pln != 4) {
return;
}
if (NetOurIP == 0) {
return;
}
if (NetReadIP(&arp->ar_data[16]) != NetOurIP) {
return;
}
switch (ntohs(arp->ar_op)) {
case ARPOP_REQUEST: /* reply with our IP address */
debug("Got ARP REQUEST, return our IP\n");
pkt = (uchar *)et;
pkt += NetSetEther(pkt, et->et_src, PROT_ARP);
arp->ar_op = htons(ARPOP_REPLY);
memcpy (&arp->ar_data[10], &arp->ar_data[0], 6);
NetCopyIP(&arp->ar_data[16], &arp->ar_data[6]);
memcpy (&arp->ar_data[ 0], NetOurEther, 6);
NetCopyIP(&arp->ar_data[ 6], &NetOurIP);
(void) eth_send((uchar *)et, (pkt - (uchar *)et) + ARP_HDR_SIZE);
return;
case ARPOP_REPLY: /* arp reply */
/* are we waiting for a reply */
if (!NetArpWaitPacketIP || !NetArpWaitPacketMAC)
break;
debug("Got ARP REPLY, set server/gtwy eth addr (%pM)\n",arp->ar_data);
tmp = NetReadIP(&arp->ar_data[6]);
/* matched waiting packet's address */
/*
* 如果收到ARP reply消息,就從裏面獲取到目標硬件地址,
* 並填寫到正在等待發送的Ping Echo request數據包內,併發送
* Ping Echo request數據包。之後會收到ping echo reply 消息,
* 仍會執行NetReceive()函數。
*/
if (tmp == NetArpWaitReplyIP) {
debug("Got it\n");
/* save address for later use */
memcpy(NetArpWaitPacketMAC, &arp->ar_data[0], 6);
#ifdef CONFIG_NETCONSOLE
(*packetHandler)(0,0,0,0);
#endif
/* modify header, and transmit it */
memcpy(((Ethernet_t *)NetArpWaitTxPacket)->et_dest, NetArpWaitPacketMAC, 6);
(void) eth_send(NetArpWaitTxPacket, NetArpWaitTxPacketSize);
/* no arp request pending now */
NetArpWaitPacketIP = 0;
NetArpWaitTxPacketSize = 0;
NetArpWaitPacketMAC = NULL;
}
return;
default:
debug("Unexpected ARP opcode 0x%x\n", ntohs(arp->ar_op));
return;
}
break;
case PROT_RARP:
debug("Got RARP\n");
arp = (ARP_t *)ip;
if (len < ARP_HDR_SIZE) {
printf("bad length %d < %d\n", len, ARP_HDR_SIZE);
return;
}
if ((ntohs(arp->ar_op) != RARPOP_REPLY) ||
(ntohs(arp->ar_hrd) != ARP_ETHER) ||
(ntohs(arp->ar_pro) != PROT_IP) ||
(arp->ar_hln != 6) || (arp->ar_pln != 4)) {
puts ("invalid RARP header\n");
} else {
NetCopyIP(&NetOurIP, &arp->ar_data[16]);
if (NetServerIP == 0)
NetCopyIP(&NetServerIP, &arp->ar_data[ 6]);
memcpy (NetServerEther, &arp->ar_data[ 0], 6);
(*packetHandler)(0,0,0,0);
}
break;
case PROT_IP:
debug("Got IP\n");
/* Before we start poking the header, make sure it is there */
if (len < IP_HDR_SIZE) {
debug("len bad %d < %lu\n", len, (ulong)IP_HDR_SIZE);
return;
}
/* Check the packet length */
if (len < ntohs(ip->ip_len)) {
printf("len bad %d < %d\n", len, ntohs(ip->ip_len));
return;
}
len = ntohs(ip->ip_len);
debug("len=%d, v=%02x\n", len, ip->ip_hl_v & 0xff);
/* Can't deal with anything except IPv4 */
if ((ip->ip_hl_v & 0xf0) != 0x40) {
return;
}
/* Can't deal with IP options (headers != 20 bytes) */
if ((ip->ip_hl_v & 0x0f) > 0x05) {
return;
}
/* Check the Checksum of the header */
if (!NetCksumOk((uchar *)ip, IP_HDR_SIZE_NO_UDP / 2)) {
puts ("checksum bad\n");
return;
}
/* If it is not for us, ignore it */
tmp = NetReadIP(&ip->ip_dst);
if (NetOurIP && tmp != NetOurIP && tmp != 0xFFFFFFFF) {
return;
}
/*
* The function returns the unchanged packet if it's not
* a fragment, and either the complete packet or NULL if
* it is a fragment (if !CONFIG_IP_DEFRAG, it returns NULL)
*/
if (!(ip = NetDefragment(ip, &len)))
return;
/*
* watch for ICMP host redirects
*
* There is no real handler code (yet). We just watch
* for ICMP host redirect messages. In case anybody
* sees these messages: please contact me
* ([email protected]), or - even better - send me the
* necessary fixes :-)
*
* Note: in all cases where I have seen this so far
* it was a problem with the router configuration,
* for instance when a router was configured in the
* BOOTP reply, but the TFTP server was on the same
* subnet. So this is probably a warning that your
* configuration might be wrong. But I'm not really
* sure if there aren't any other situations.
*/
if (ip->ip_p == IPPROTO_ICMP) {
ICMP_t *icmph = (ICMP_t *)&(ip->udp_src);
switch (icmph->type) {
case ICMP_REDIRECT:
if (icmph->code != ICMP_REDIR_HOST)
return;
printf (" ICMP Host Redirect to %pI4 ", &icmph->un.gateway);
return;
case ICMP_ECHO_REPLY:
/* 收到ping echo reply 消息,執行pingStart()函數中註冊的處理函數 * PingHandler()。 PingHandler()判斷是否ping成功。若成功,流程返
* 回 Netloop中while循環,接着返回do_ping(),打印成功消息後,
* 流程結束
* static void
* PingHandler (uchar * pkt, unsigned dest, unsigned src, unsigned len)
* {
* IPaddr_t tmp;
* volatile IP_t *ip = (volatile IP_t *)pkt;
* tmp = NetReadIP((void *)&ip->ip_src);
* if (tmp != NetPingIP)
* return;
* NetState = NETLOOP_SUCCESS;
* }
*/
/*
* IP header OK. Pass the packet to the current handler.
*/
/* XXX point to ip packet */
(*packetHandler)((uchar *)ip, 0, 0, 0);
return;
case ICMP_ECHO_REQUEST:
debug("Got ICMP ECHO REQUEST, return %d bytes \n",ETHER_HDR_SIZE + len);
memcpy (&et->et_dest[0], &et->et_src[0], 6);
memcpy (&et->et_src[ 0], NetOurEther, 6);
ip->ip_sum = 0;
ip->ip_off = 0;
NetCopyIP((void*)&ip->ip_dst, &ip->ip_src);
NetCopyIP((void*)&ip->ip_src, &NetOurIP);
ip->ip_sum = ~NetCksum((uchar *)ip, IP_HDR_SIZE_NO_UDP >> 1);
icmph->type = ICMP_ECHO_REPLY;
icmph->checksum = 0;
icmph->checksum = ~NetCksum((uchar *)icmph,(len - IP_HDR_SIZE_NO_UDP) >> 1);
(void) eth_send((uchar *)et, ETHER_HDR_SIZE + len);
return;
default:
return;
}
} else if (ip->ip_p != IPPROTO_UDP) { /* Only UDP packets */
return;
}
/*
* IP header OK. Pass the packet to the current handler.
*/
(*packetHandler)((uchar *)ip +IP_HDR_SIZE,
ntohs(ip->udp_dst),
ntohs(ip->udp_src),
ntohs(ip->udp_len) - 8);
break;
}
}
附錄一 CONFIG_NET_MULTI
搜素整個源碼,可以找個很多關於CONFIG_NET_MULTI的註釋,
#define CONFIG_NET_MULTI /* Multi ethernet cards support */
或者
#define CONFIG_NET_MULTI /*specify more that one ports available */
或者
#define CONFIG_NET_MULTI /* Support for multiple network interfaces */
從上面的註釋可以猜出當有多個網口設備時,需要定義CONFIG_NET_MULTI 。
一個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
* ([email protected]), or - even better - send me the
* necessary fixes :-)
*
* Note: in all cases where I have seen this so far
* it was a problem with the router configuration,
* for instance when a router was configured in the
* BOOTP reply, but the TFTP server was on the same
* subnet. So this is probably a warning that your
* configuration might be wrong. But I'm not really
* sure if there aren't any other situations.
*/
if (ip->ip_p == IPPROTO_ICMP) {
ICMP_t *icmph = (ICMP_t *)&(ip->udp_src);
switch (icmph->type) {
case ICMP_REDIRECT:
if (icmph->code != ICMP_REDIR_HOST)
return;
printf (" ICMP Host Redirect to %pI4 ", &icmph->un.gateway);
return;
case ICMP_ECHO_REPLY:
/* 收到ping echo reply 消息,執行pingStart()函數中註冊的處理函數 * PingHandler()。 PingHandler()判斷是否ping成功。若成功,流程返
* 回 Netloop中while循環,接着返回do_ping(),打印成功消息後,
* 流程結束
* static void
* PingHandler (uchar * pkt, unsigned dest, unsigned src, unsigned len)
* {
* IPaddr_t tmp;
* volatile IP_t *ip = (volatile IP_t *)pkt;
* tmp = NetReadIP((void *)&ip->ip_src);
* if (tmp != NetPingIP)
* return;
* NetState = NETLOOP_SUCCESS;
* }
*/
/*
* IP header OK. Pass the packet to the current handler.
*/
/* XXX point to ip packet */
(*packetHandler)((uchar *)ip, 0, 0, 0);
return;
case ICMP_ECHO_REQUEST:
debug("Got ICMP ECHO REQUEST, return %d bytes \n",
ETHER_HDR_SIZE + len);
memcpy (&et->et_dest[0], &et->et_src[0], 6);
memcpy (&et->et_src[ 0], NetOurEther, 6);
ip->ip_sum = 0;
ip->ip_off = 0;
NetCopyIP((void*)&ip->ip_dst, &ip->ip_src);
NetCopyIP((void*)&ip->ip_src, &NetOurIP);
ip->ip_sum = ~NetCksum((uchar *)ip, IP_HDR_SIZE_NO_UDP >> 1);
icmph->type = ICMP_ECHO_REPLY;
icmph->checksum = 0;
icmph->checksum = ~NetCksum((uchar *)icmph,
(len - IP_HDR_SIZE_NO_UDP) >> 1);
(void) eth_send((uchar *)et, ETHER_HDR_SIZE + len);
return;
default:
return;
}
} else if (ip->ip_p != IPPROTO_UDP) { /* Only UDP packets */
return;
}
/*
* IP header OK. Pass the packet to the current handler.
*/
(*packetHandler)((uchar *)ip +IP_HDR_SIZE,
ntohs(ip->udp_dst),
ntohs(ip->udp_src),
ntohs(ip->udp_len) - 8);
break;
}
}
=> setenv ethact FEC
=> setenv ethact FEC
=> ping 192.168.0.1 # traffic sent on FEC
=> setenv ethact SCC
=> ping 10.0.0.1 # traffic sent on SCC