這篇文章主要講解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)