從0學(xué)arm系列繼續(xù)更新兩篇,這是第一篇,下一篇是uboot中的網(wǎng)絡(luò)協(xié)議棧詳解。之前系列請參考所有文章合集【墻裂建議收藏】:《從0學(xué)ARM》
一、網(wǎng)卡
1. 概念
網(wǎng)卡是一塊被設(shè)計(jì)用來允許計(jì)算機(jī)在計(jì)算機(jī)網(wǎng)絡(luò)上進(jìn)行通訊的計(jì)算機(jī)硬件。由于其擁有MAC地址,因此屬于OSI模型的第2層。它使得用戶可以通過電纜或無線相互連接。
每一個(gè)網(wǎng)卡都有一個(gè)被稱為MAC地址的獨(dú)一無二的48位串行號(hào),它被寫在卡上的一塊ROM中。在網(wǎng)絡(luò)上的每一個(gè)計(jì)算機(jī)都必須擁有一個(gè)獨(dú)一無二的MAC地址。沒有任何兩塊被生產(chǎn)出來的網(wǎng)卡擁有同樣的地址。這是因?yàn)殡姎怆娮?a class="article-link" target="_blank" href="/tag/%E5%B7%A5%E7%A8%8B%E5%B8%88/">工程師協(xié)會(huì)(IEEE)負(fù)責(zé)為網(wǎng)絡(luò)接口控制器(網(wǎng)卡)銷售商分配唯一的MAC地址。
網(wǎng)卡上面裝有處理器和存儲(chǔ)器(包括RAM和ROM)。網(wǎng)卡和局域網(wǎng)之間的通信是通過電纜或雙絞線以串行傳輸方式進(jìn)行的。而網(wǎng)卡和計(jì)算機(jī)之間的通信則是通過計(jì)算機(jī)主板上的I/O總線以并行傳輸方式進(jìn)行。
因此,網(wǎng)卡的一個(gè)重要功能就是要進(jìn)行串行/并行轉(zhuǎn)換。由于網(wǎng)絡(luò)上的數(shù)據(jù)率和計(jì)算機(jī)總線上的數(shù)據(jù)率并不相同,因此在網(wǎng)卡中必須裝有對(duì)數(shù)據(jù)進(jìn)行緩存的存儲(chǔ)芯片。
網(wǎng)卡以前是作為擴(kuò)展卡插到計(jì)算機(jī)總線上的,但是由于其價(jià)格低廉而且以太網(wǎng)標(biāo)準(zhǔn)普遍存在,大部分新的計(jì)算機(jī)都在主板上集成了網(wǎng)絡(luò)接口。
這些主板或是在主板芯片中集成了以太網(wǎng)的功能,或是使用一塊通過PCI (或者更新的PCI-Express總線)連接到主板上的廉價(jià)網(wǎng)卡。
除非需要多接口或者使用其它種類的網(wǎng)絡(luò),否則不再需要一塊獨(dú)立的網(wǎng)卡。甚至更新的主板可能含有內(nèi)置的雙網(wǎng)絡(luò)(以太網(wǎng))接口。
2. 主要功能
1、數(shù)據(jù)的封裝與解封 發(fā)送時(shí)將上一層傳遞來的數(shù)據(jù)加上首部和尾部,成為以太網(wǎng)的幀。接收時(shí)將以太網(wǎng)的幀剝?nèi)ナ撞亢臀膊?,然后送交上一?/p>
2、鏈路管理 主要是通過CSMA/CD(Carrier Sense Multiple Access with Collision Detection ,帶沖突檢測的載波監(jiān)聽多路訪問)協(xié)議來實(shí)現(xiàn)
3、數(shù)據(jù)編碼與譯碼 即曼徹斯特編碼與譯碼。其中曼徹斯特碼,又稱數(shù)字雙向碼、分相碼或相位編碼(PE),是一種常用的二元碼線路編碼方式之一,被物理層使用來編碼一個(gè)同步位流的時(shí)鐘和數(shù)據(jù)。在通信技術(shù)中,用來表示所要發(fā)送比特 流中的數(shù)據(jù)與定時(shí)信號(hào)所結(jié)合起來的代碼。常用在以太網(wǎng)通信,列車總線控制,工業(yè)總線等領(lǐng)域。
3. 分類
- 按總線接口類型分 按網(wǎng)卡的總線接口類型來分一般可分bai為ISA接口網(wǎng)卡、PCI接口網(wǎng)卡以及在服務(wù)器上使用的PCI-X總線接口類型的網(wǎng)卡,筆記本電腦所使用的網(wǎng)卡是PCMCIA接口類型的。
- 按網(wǎng)絡(luò)接口劃分 除了可以按網(wǎng)卡的總線接口類型劃分外,我們還可以按網(wǎng)卡的網(wǎng)絡(luò)接口類型來劃分。網(wǎng)卡最終是要與網(wǎng)絡(luò)進(jìn)行連接,所以也就必須有一個(gè)接口使網(wǎng)線通過它與其它計(jì)算機(jī)網(wǎng)絡(luò)設(shè)備連接起來。不同的網(wǎng)絡(luò)接口適用于不同的網(wǎng)絡(luò)類型,目前常見的接口主要有以太網(wǎng)的RJ-45接口、細(xì)同軸電纜的BNC接口和粗同軸電AUI接口、FDDI接口、ATM接口等。而且有的網(wǎng)卡為了適用于更廣泛的應(yīng)用環(huán)境,提供了兩種或多種類型的接口,如有的網(wǎng)卡會(huì)同時(shí)提供RJ-45、BNC接口或AUI接口。
- (1)RJ-45接口網(wǎng)卡(2)BNC接口網(wǎng)卡(3)AUI接口網(wǎng)卡(4)FDDI接口網(wǎng)卡(5)ATM接口網(wǎng)卡
- 按帶寬劃分 隨著網(wǎng)絡(luò)技術(shù)的發(fā)展,網(wǎng)絡(luò)帶寬也在不斷提高,但是不同帶寬的網(wǎng)卡所應(yīng)用的環(huán)境也有所不同,目前主流的網(wǎng)卡主要有10Mbps網(wǎng)卡、100Mbps以太網(wǎng)卡、10Mbps/100Mbps自適應(yīng)網(wǎng)卡、1000Mbps千兆以太網(wǎng)卡四種。
- (1)10Mbps網(wǎng)卡(2)100Mbps網(wǎng)卡(3)10Mbps/100Mbps網(wǎng)卡(4)1000Mbps以太網(wǎng)卡
二、DM9000
DM9000芯片是DAVICOM公司生產(chǎn),DM9000A 是一款完全集成的、性價(jià)比高、引腳數(shù)少、帶有通用處理器接口的單芯片快速以太網(wǎng)控制器。
一個(gè) 10/100M PHY 和 4K 雙字的 SRAM 。它是出于低功耗和高性能目的設(shè)計(jì)的,其 IO 端口支持 3.3V 與 5V 容限值。
DM9000A 為適應(yīng)各種處理器,提供了 8 位、16 位數(shù)據(jù)接口訪問內(nèi)部存儲(chǔ)器。
DM9000A物理協(xié)議層接口完全支持使用 10MBps 下 3 類、4 類、5 類非屏蔽雙絞線和 100MBps 下 5類非屏蔽雙絞線。這是完全遵照 IEEE 802.3u 標(biāo)準(zhǔn)。
它的自動(dòng)協(xié)商功能將自動(dòng)完成 DM9000AE配置以使其發(fā)揮出最佳性能。
它還支持 IEEE 802.3x 全雙工流量控制。
1. 模塊圖
圖1 DM9000內(nèi)部結(jié)構(gòu)框架
EEPROM Interface接口用于存放mac地址,Internal SRAM用于存放收發(fā)數(shù)據(jù),MII部分把MAC部分與PHY部分連接起來通信,AUTO-MDIX用于自適應(yīng)10/100M網(wǎng)絡(luò),在物理層上,MAC在PHY之下。
2. 引腳分析
(#:表示低電平有效)
開發(fā)板FS4412的網(wǎng)卡DM9000A連接到了SROM控制器,下面我們分析數(shù)據(jù)線、地址線和信號(hào)線連接
1) SD0~15
SD0~15: 16位數(shù)據(jù)線連接到引腳BUF_B_Xm0DATA[0:15],由CMD引腳決定訪問類型。
可見數(shù)據(jù)和地址線都連接到了SOC的XM0上。
數(shù)據(jù)線和信號(hào)線對(duì)應(yīng)的SROMC的引腳如上圖。
2) CMD
dm9000 外圍電路 轉(zhuǎn)換電路 soc
CMD--------BUF_B_Xm0ADDR2--------Xm0ADDR2-----Xm0ADDR2
如下圖所示:CMD: 命令線,當(dāng)CMD為高,表示SD 傳輸?shù)氖菙?shù)據(jù),CMD為低表示傳輸?shù)氖堑刂?,接在exynos4412的BUF_B_Xm0ADDR2上,可見CMD復(fù)用了地址線Xm0ADDR2引腳。
3) IOR#、IOW#
dm9000 外圍電路 轉(zhuǎn)換電路 soc
IOR--------BUF_Xm0OEn--------Xm0OEn-----Xm0OEn
IOW--------BUF_Xm0WEn--------Xm0WEn-----Xm0WEn
4) CS#
dm9000 外圍電路 轉(zhuǎn)換電路 soc
CS--------BUF_Xm0cs1--------Xm0cs1-----Xm0CSn1
CS#:片選,放在exynos4412的Bank1的片選上面,內(nèi)存基地址是0x05000000。
我們的DM9000A是放在exynos4412的Bank1(0X05000000)的片選上面。
而DM9000的CMD引腳接在Bank1的LADDR2上面
讀寫DM9000A的地址CMD拉低, 此時(shí)向0X05000000地址上讀寫的數(shù)據(jù)便是DM9000A的內(nèi)部寄存器地址
讀寫DM9000A的數(shù)據(jù)CMD拉高,此時(shí)向0X05000000+4地址上讀寫的數(shù)據(jù)便是DM9000A的數(shù)據(jù)
設(shè)置exynos4412的bank1的硬件位寬,時(shí)序,因?yàn)椴煌挠布?涉及的數(shù)據(jù)收發(fā)都不同。
5) INT#
中斷線DM9000_IRQ通過U8轉(zhuǎn)接到引腳XEINT6
由上圖可知中斷引腳INT,接在exynos4412的GPX0_6腳上。uboot中的DM9000A的驅(qū)動(dòng)沒有用到中斷。
3. 復(fù)用GPIO引腳
XM0引腳復(fù)用了GPIO引腳,所以需要初始化對(duì)應(yīng)的GPIO引腳來使能SROMC。
1) GPY0CON
2) GPY1CON
3)GPY3CON
4) GPY5CON
5) GPY6CON
三、SROM 控制器
1. 概念
SROM是高速存儲(chǔ)器,Cache技術(shù)就是通過在DROM和CPU之間插入一小塊SROM來減小CPU和存儲(chǔ)之間的速度差異的。
本篇參考開發(fā)板FS4412,DM9000掛接到exynos 4412的SROM控制器上
EXYNOS 4412包含了SROM控制器,特性如下:
- 外部 8/16位 NOR Flash/PROM/ SRAM memory.4組內(nèi)存,每塊內(nèi)存最多16 MB
首先我們要初始化 exynos4412的 SROM 控制器,設(shè)置總線寬度和相關(guān)時(shí)序。
針對(duì) SROM 控制器的每一個(gè) bank 只有2 個(gè)寄存器:SROM_BW 和 SROM_BC。
2. SROM_BW
在 SROM_BW 寄存器中,我們只關(guān)心與 bank1 相關(guān)的域。
上面分析過, DM9000A 的 16 根數(shù)據(jù)線全部接在 exynos 4412的數(shù)據(jù)線上,所以 DataWidth1 設(shè)置為 1; DM9000A 的地址是按字節(jié)存取的,所以 AddrMode1 設(shè)置為 1; 通過查看原理圖,沒有使用 Xm0WAITn和 Xm0BEn 引腳; 所以 WaitEnable1 和 ByteEnable1 均設(shè)置為 0。
SROM_BW[7:4]=0x3
3. SROM_BC1
SROM 控制器讀時(shí)序和 DM9000A 的讀時(shí)序主要通過SROM_BCn控制寄存器設(shè)置。
設(shè)置這些時(shí)序之前,首先來看DM9000A芯片手冊時(shí)序圖和exynos4412的時(shí)序圖
詳盡時(shí)序分析:,內(nèi)存控制器使用HCLK作為時(shí)鐘,在HCLK為100MHz時(shí),1個(gè)clock大約為10ns。信號(hào)值的設(shè)定如下:
信號(hào) | 含義 | 最低時(shí)間(ns) |
---|---|---|
Tacs | 地址發(fā)出后等多長時(shí)間發(fā)片選, DM9000AE 中 CS 和 CMD(地址)同時(shí)發(fā)出,所以 Tacs最低為0ns | 0 |
Tcos | 發(fā)出片選信號(hào)后等多長時(shí)間發(fā)出讀使能信號(hào)(nOW、 IOR),在 DM9000A 的時(shí)序圖上對(duì)應(yīng) T1,最小為 0 | 0 |
Tacc | 讀使能信號(hào)持續(xù)時(shí)間,access cycle ,讀寫使能后,多久才能訪問數(shù)據(jù),在 DM9000A 的時(shí)序圖上對(duì)應(yīng) T2 | 10 |
Tcoh | 當(dāng)DM9000A的寫信號(hào)取消后,數(shù)據(jù)線上的數(shù)據(jù)還需要至少3ns才消失(nOE讀寫取消后,片選需要維持多長時(shí)間)在 DM9000A 的時(shí)序圖中對(duì)應(yīng) T4 | 3 |
Tcah | 片選結(jié)束后,地址保存時(shí)間, DM9000A 中CS和cmd同時(shí)結(jié)束,所以 Tcah=0 | 0 |
Tacp | 頁模式,不管 | 0 |
PMC | 頁模式,不管 | 0 |
從DM9000A的讀寫時(shí)序圖中可以看出,T2+T6實(shí)際上構(gòu)成了DM9000A的一個(gè)訪問周期,因此還需要滿足:Tacs + Tcos + Tacc + Tcoh + Tcah>= T2+T6,最終使用下面的表達(dá)式來表達(dá):(Tacs >= 0 && Tacs <= 4) && (Tcos >= 0 && Tcos <= 4) && (Tacc >= 1 && Tacc <= 14 ) && (Tcoh >=1 && Tcoh <= 4 )
寄存器SROM_BCn (n = 0 to 3)定義如下:
故設(shè)置參考值為:
#define DM9000_Tacs (0x1) // address set-up
#define DM9000_Tcos (0x1) // chip selection set-up
#define DM9000_Tacc (0x5) // access cycle
#define DM9000_Tcoh (0x1) // chip selection hold
#define DM9000_Tah (0xC) // address holding time
#define DM9000_Tacp (0x9) // page mode access cycle
#define DM9000_PMC (0x1) // normal(1data)page mode configuration
4. SROM初始化
u-boot 已經(jīng)自帶了 DM9000系列網(wǎng)卡的驅(qū)動(dòng),在 u-boot 源碼中的 driver/net/dm9000x.c 的有一段說明:
06/03/2008 Remy Bohmer
- Fixed the driver to work with DM9000A.
(check on ISR receive status bit before reading the
FIFO as described in DM9000 programming guide and
application notes)
- Added autodetect of databus width.
- Made debug code compile again.
- Adapt eth_send such that it matches the DM9000*
application notes. Needed to make it work properly
for DM9000A.
- Adapted reset procedure to match DM9000 application
notes (i.e. double reset)
- some minor code cleanups
These changes are tested with DM9000{A,EP,E} together
with a 200MHz Atmel AT91SAM9261 core
可見,2008年Remy Bohmer已經(jīng)為 DM9000A 添加了驅(qū)動(dòng),但是我們?nèi)匀恍枰槍?duì)板子做一些修改。
前一章我們針對(duì)參考的fs4412開發(fā)板移植了DM9000A的驅(qū)動(dòng),下面我們來詳細(xì)分析DM9000A驅(qū)動(dòng)程序。
分析驅(qū)動(dòng)涉及到以下幾個(gè)文件:
arch/arm/lib/board.c
board/samsung/origen/origen.c
drivers/net/Dm9000x.c
drivers/net/Dm9000x.h
include/config_cmd_default.h
include/configs/origen.h
include/net.h
net/eth.c
5. 宏定義
在include/configs/origen.h中需要定義DM9000A基地址和編譯的宏。其中最重要的幾個(gè)宏如下:
名稱 | 說明 | 值 |
---|---|---|
CONFIG_DM9000_BASE | DM9000A 的基地址 | 0x05000000 |
DM9000_IO | DM9000A 的 INDEX 端口地址 | CONFIG_DM9000_BASE |
DM9000_DATA | DM9000A 的 DATA 端口地址 | (CONFIG_DM9000_BASE + 4) |
CONFIG_DRIVER_DM9000 | Makefile中用于控制dm9000驅(qū)動(dòng)是否編譯 | 1 |
CONFIG_DM9000_USE_16BIT | DM9000A數(shù)據(jù)寬度 | |
CONFIG_DM9000_NO_SROM | 表示沒有使用SROM | 1 |
其中DM9000_DATA 定義為基地址+0x4,剛好把 Xm0ADDR2 拉高,即把 CMD 拉高。
查看文件drivers/net/Makefile:
從 Makefile 得知,要把 DM9000A 的驅(qū)動(dòng)編譯進(jìn) u-boot中,需要定義 CONFIG_DRIVER_DM9000 這個(gè)宏。
宏定義如下:
#ifdef CONFIG_CMD_NET
#define CONFIG_NET_MULTI
#define CONFIG_DRIVER_DM9000 1
#define CONFIG_DM9000_BASE 0x05000000
#define DM9000_IO CONFIG_DM9000_BASE
#define DM9000_DATA (CONFIG_DM9000_BASE + 4)
#define CONFIG_DM9000_USE_16BIT
#define CONFIG_DM9000_NO_SROM 1
#define CONFIG_ETHADDR 11:22:33:44:55:66
#define CONFIG_IPADDR 192.168.6.187
#define CONFIG_SERVERIP 192.168.6.186
#define CONFIG_GATEWAYIP 192.168.6.1
#define CONFIG_NETMASK 255.255.255.0
#endif
除此以外我們還需要添加一些 u-boot 的命令,比如 ping 命令用來檢查網(wǎng)絡(luò)是否通暢,tftp用來下載文件。
uboot通過宏來控制是否編譯這些命令,include/configs/origen.h定義了一些宏,但是有的是undefine,我們要打開它們。
/* Command definition*/
#include
#define CONFIG_CMD_PING
#define CONFIG_CMD_ELF
#define CONFIG_CMD_DHCP
#define CONFIG_CMD_MMC
#define CONFIG_CMD_FAT
#define CONFIG_CMD_NET
#undef CONFIG_CMD_NFS
#define CONFIG_CMD_HELLO
#define CONFIG_CMD_LEDA
除此之外頭文件:u-boot-2013.01/include/config_cmd_all.h 也列出了一些可用的命令。
#define CONFIG_CMD_BDI /* bdinfo */
#define CONFIG_CMD_BOOTD /* bootd */
#define CONFIG_CMD_CONSOLE /* coninfo */
#define CONFIG_CMD_ECHO /* echo arguments */
#define CONFIG_CMD_EDITENV /* editenv */
#define CONFIG_CMD_FPGA /* FPGA configuration Support */
#define CONFIG_CMD_IMI /* iminfo */
#define CONFIG_CMD_ITEST /* Integer (and string) test */
#ifndef CONFIG_SYS_NO_FLASH
#define CONFIG_CMD_FLASH /* flinfo, erase, protect */
#define CONFIG_CMD_IMLS /* List all found images */
#endif
#define CONFIG_CMD_LOADB /* loadb */
#define CONFIG_CMD_LOADS /* loads */
#define CONFIG_CMD_MEMORY /* md mm nm mw cp cmp crc base loop mtest */
#define CONFIG_CMD_MISC /* Misc functions like sleep etc*/
#define CONFIG_CMD_NET /* bootp, tftpboot, rarpboot */
#define CONFIG_CMD_NFS /* NFS support */
#define CONFIG_CMD_RUN /* run command in env variable */
#define CONFIG_CMD_SAVEENV /* saveenv */
#define CONFIG_CMD_SETGETDCR /* DCR support on 4xx */
#define CONFIG_CMD_SOURCE /* "source" command support */
#define CONFIG_CMD_XIMG /* Load part of Multi Image */
6. 初始化srom
在arch/arm/lib/board.c的函數(shù)board_init_r中有如下代碼:
void board_init_r(gd_t *id, ulong dest_addr)
{
……
board_init(); /* Setup chipselects */
……
}
函數(shù)board_init()定義在board/samsung/origen/origen.c中,我們在該函數(shù)中添加了初始化srom代碼:
int board_init(void)
{
gpio1 = (struct exynos4_gpio_part1 *) EXYNOS4_GPIO_PART1_BASE;
gpio2 = (struct exynos4_gpio_part2 *) EXYNOS4_GPIO_PART2_BASE;
gd->bd->bi_boot_params = (PHYS_SDRAM_1 + 0x100UL);
#ifdef CONFIG_DRIVER_DM9000
dm9000aep_pre_init();
#endif
return 0;
}
函數(shù)dm9000aep_pre_init用來設(shè)置 SROM 控制器。
static void dm9000aep_pre_init(void)
{
unsigned int tmp;
unsigned char smc_bank_num = 1;
unsigned int smc_bw_conf=0;
unsigned int smc_bc_conf=0;
/* gpio configuration */
writel(0x00220020, 0x11000000 + 0x120);//GPY0CON
writel(0x00002222, 0x11000000 + 0x140);//GPY1CON
/* 16 Bit bus width */
writel(0x22222222, 0x11000000 + 0x180);//GPY3CON
writel(0x0000FFFF, 0x11000000 + 0x188);//GPY3PUD
writel(0x22222222, 0x11000000 + 0x1C0);//GPY5CON
writel(0x0000FFFF, 0x11000000 + 0x1C8);//GPY5PUD
writel(0x22222222, 0x11000000 + 0x1E0);//GPY6CON
writel(0x0000FFFF, 0x11000000 + 0x1E8);//GPY6PUD
smc_bw_conf &= ~(0xf<<4);
smc_bw_conf |= (1<<7) | (1<<6) | (1<<5) | (1<<4);
smc_bc_conf = ((DM9000_Tacs << 28)
| (DM9000_Tcos << 24)
| (DM9000_Tacc << 16)
| (DM9000_Tcoh << 12)
| (DM9000_Tah << 8)
| (DM9000_Tacp << 4)
| (DM9000_PMC));
exynos_config_sromc(smc_bank_num,smc_bw_conf,smc_bc_conf);
}
/*
* exynos_config_sromc() - select the proper SROMC Bank and configure the
* band width control and bank control registers
* srom_bank - SROM
* srom_bw_conf - SMC Band witdh reg configuration value
* srom_bc_conf - SMC Bank Control reg configuration value
*/
void exynos_config_sromc(u32 srom_bank, u32 srom_bw_conf, u32 srom_bc_conf)
{
unsigned int tmp;
struct exynos_sromc *srom = (struct exynos_sromc *)(EXYNOS4412_SROMC_BASE);
/* Configure SMC_BW register to handle proper SROMC
* bank */
tmp = srom->bw;
tmp &= ~(0xF << (srom_bank * 4));
tmp |= srom_bw_conf;
srom->bw = tmp;
/* Configure SMC_BC
* register */
srom->bc[srom_bank] = srom_bc_conf;
}
四、DM9000A驅(qū)動(dòng)分析
DM9000A所能支持的功能非常的多,驅(qū)動(dòng)的實(shí)現(xiàn)相對(duì)比較復(fù)雜,搞清楚裸機(jī)的網(wǎng)卡驅(qū)動(dòng),我們再去學(xué)習(xí)Linux內(nèi)核的DM9000驅(qū)動(dòng)就相對(duì)容易一些。本節(jié)將詳細(xì)講解DM9000A網(wǎng)卡的數(shù)據(jù)的收發(fā)操作的流程。
1. 相關(guān)結(jié)構(gòu)體
struct board_info
/* Structure/enum declaration ------------------------------- */
typedef struct board_info {
u32 runt_length_counter; /* counter: RX length < 64byte */
u32 long_length_counter; /* counter: RX length > 1514byte */
u32 reset_counter; /* counter: RESET */
u32 reset_tx_timeout; /* RESET caused by TX Timeout */
u32 reset_rx_status; /* RESET caused by RX Statsus wrong */
u16 tx_pkt_cnt;
u16 queue_start_addr;
u16 dbug_cnt;
u8 phy_addr;
u8 device_wait_reset; /* device state */
unsigned char srom[128];
void (*outblk)(volatile void *data_ptr, int count);
void (*inblk)(void *data_ptr, int count);
void (*rx_status)(u16 *RxStatus, u16 *RxLen);
struct eth_device netdev;
} board_info_t;
static board_info_t dm9000_info;
該結(jié)構(gòu)體是用來維護(hù)DM9000系列網(wǎng)卡的結(jié)構(gòu)體,所有和網(wǎng)卡DM9000A信息都保存到該結(jié)構(gòu)體中。struct eth_devicestruct board_info中有一個(gè)重要的成員 netdev,該成員是uboot提供的標(biāo)準(zhǔn)的統(tǒng)一的網(wǎng)卡設(shè)備接口。
struct eth_device {
char name[16];
unsigned char enetaddr[6];
int iobase;
int state;
int (*init) (struct eth_device *, bd_t *);
int (*send) (struct eth_device *, 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 *);
struct eth_device *next;
int index;
void *priv;
};
該結(jié)構(gòu)體維護(hù)了操作網(wǎng)卡的回調(diào)函數(shù)等信息,我們只需要把網(wǎng)口的收發(fā)數(shù)據(jù)操作封裝到對(duì)應(yīng)的回調(diào)函數(shù)中,然后注冊到系統(tǒng)即可。
2. 網(wǎng)卡注冊/注銷
進(jìn)入到arch/arm/lib/board.c 中的 board_init_r 函數(shù):
665 #if defined(CONFIG_CMD_NET)
666 puts("Net: ");
667 eth_initialize(gd->bd);
668 #if defined(CONFIG_RESET_PHY_R)
669 debug("Reset Ethernet PHYn");
670 reset_phy();
671 #endif
如果定義了 CONFIG_CMD_NET,就調(diào)用 eth_initialize(gd->bd)進(jìn)行網(wǎng)卡初始化。
這個(gè)宏在include/config_cmd_default.h 中定義,這個(gè)頭文件又被單板配置文件 include/configs/origen.h 所包含。
eth_initialize 函數(shù)在 net/eth.c 中定義,下面是該函數(shù)部分代碼:
308 /*
309 * If board-specific initialization exists, call it.
310 * If not, call a CPU-specific one
311 */
312 if (board_eth_init != __def_eth_init) {
313 if (board_eth_init(bis) < 0)
314 printf("Board Net Initialization Failedn");
315 } else if (cpu_eth_init != __def_eth_init) {
316 if (cpu_eth_init(bis) < 0)
317 printf("CPU Net Initialization Failedn");
318 } else
319 printf("Net Initialization Skippedn");
這段代碼功能是:如果定義了單板相關(guān)的初始化函數(shù)就調(diào)用它,否則調(diào)用 CPU 相關(guān)的初始化函數(shù)。
其中__def_eth_init 函數(shù),同樣在net/eth.c 中定義
105 * CPU and board-specific Ethernet initializations. Aliased function
106 * signals caller to move on
107 */
108 static int __def_eth_init(bd_t *bis)
109 {
110 return -1;
111 }
112 int cpu_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init")));
113 int board_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init")));
這里用到了 gcc 的弱符號(hào)和別名屬性。如果我們沒有定義自己的 board_eth_init 函數(shù),則 board_eth_init 就和__def_eth_init 相同,調(diào)用 board_eth_init 就相當(dāng)于調(diào)用__def_eth_init,現(xiàn)在就能明白上面的 if 判斷語句了。
board_eth_init 在board/samsung/origen/origen.c 中定義
264 #ifdef CONFIG_CMD_NET
265 int board_eth_init(bd_t *bis)
266 {
267
268 int rc = 0;
269 #ifdef CONFIG_DRIVER_DM9000
270 rc = dm9000_initialize(bis);
271 #endif
272 return rc;
273 }
274 #endif
這里通過配置宏來決定調(diào)用哪個(gè)網(wǎng)卡初始化函數(shù)。
我們使用的是 DM9000A,我們先查看下 DM9000A 的驅(qū)動(dòng)源文件drivers/net/DM9000x.c,初始化函數(shù)如下:
626 int dm9000_initialize(bd_t *bis)
627 {
628 struct eth_device *dev = &(dm9000_info.netdev);
629
630 /* Load MAC address from EEPROM */
631 dm9000_get_enetaddr(dev);
632
633 dev->init = dm9000_init;
634 dev->halt = dm9000_halt;
635 dev->send = dm9000_send;
636 dev->recv = dm9000_rx;
637 sprintf(dev->name, "dm9000");
638
639 eth_register(dev);
640
641 return 0;
642 }
該函數(shù)就是 DM9000A 的初始化函數(shù)。631行dm9000_get_enetaddr 從 EEPROM 加載MAC地址,
static void dm9000_get_enetaddr(struct eth_device *dev)
{
#if !defined(CONFIG_DM9000_NO_SROM)
int i;
for (i = 0; i < 3; i++)
dm9000_read_srom_word(i, dev->enetaddr + (2 * i));
#endif
}
該函數(shù)根據(jù)宏CONFIG_DM9000_NO_SROM 來決定是否從EEPROM 加載MAC地址, 參考的板子上的 DM9000A 沒有接 EEPROM,我們在 origen.h 中定義了這個(gè)宏,表示不從 EEPROM 加載 MAC地址。
633~636行是將網(wǎng)卡的初始化和收發(fā)數(shù)據(jù)的函數(shù)填充到dev中,用于注冊到系統(tǒng)中:
639行,函數(shù)eth_register()的參數(shù)是dev,該變量地址其實(shí)是dm9000_info.netdev的地址。dm9000_info定義在同一文件下:
108 static board_info_t dm9000_info;
函數(shù)eth_register()位于net/eth.c中;
- 功能:用于注冊網(wǎng)卡到系統(tǒng)中,如果之前網(wǎng)卡設(shè)備鏈表為空,則直接復(fù)制給全局指針變量eth_devices和eth_current ,如果不為空,則把當(dāng)前網(wǎng)卡插入到鏈表eth_devices中。
int eth_register(struct eth_device *dev)
{
struct eth_device *d;
static int index;
assert(strlen(dev->name) < sizeof(dev->name));
if (!eth_devices) {//網(wǎng)卡設(shè)備鏈表為空
eth_current = eth_devices = dev;
eth_current_changed();
} else {//找到表尾
for (d = eth_devices; d->next != eth_devices; d = d->next)
;
d->next = dev;//插入表尾
}
dev->state = ETH_STATE_INIT;
dev->next = eth_devices;//新的設(shè)備指向網(wǎng)卡設(shè)備表頭
dev->index = index++;
return 0;
}
其中
eth_devices:網(wǎng)卡設(shè)備的鏈表 eth_current:用于保存當(dāng)前使用的網(wǎng)卡
網(wǎng)卡注銷網(wǎng)卡注銷函數(shù)eth_unregister() 該函數(shù)會(huì)將網(wǎng)卡節(jié)點(diǎn)dev從鏈表eth_devices中刪除,并重新設(shè)置變量eth_current。
int eth_unregister(struct eth_device *dev)
{
struct eth_device *cur;
/* No device */
if (!eth_devices)
return -1;
for (cur = eth_devices; cur->next != eth_devices && cur->next != dev;
cur = cur->next)
;
/* Device not found */
if (cur->next != dev)
return -1;
cur->next = dev->next;
if (eth_devices == dev)
eth_devices = dev->next == eth_devices ? NULL : dev->next;
if (eth_current == dev) {
eth_current = eth_devices;
eth_current_changed();
}
return 0;
}
3. 寄存器
DM9000A 擁有一系列的控制和狀態(tài)寄存器,這些寄存器可以被處理器所訪問,這些寄存器是按字節(jié)對(duì)齊的。
所有的 CSRs 在軟件或者硬件復(fù)位后都將被置為默認(rèn)值,除非他們被另外標(biāo)識(shí)。
編號(hào) | 寄存器 | 描述 | 偏移地址 | 復(fù)位后默認(rèn)值 |
---|---|---|---|---|
1 | NCR | 網(wǎng)絡(luò)控制寄存器 | 00H | 00H |
2 | NSR | 網(wǎng)絡(luò)狀態(tài)寄存器 | 01H | 00H |
3 | TCR | 發(fā)送控制寄存器 | 02H | 00H |
4 | TSR I | 發(fā)送狀態(tài)寄存器 1 | 03H | 00H |
5 | TSR II | 發(fā)送狀態(tài)寄存器 2 | 04H | 00H |
6 | RCR | 接收控制寄存器 | 05H | 00H |
7 | RSR | 接收狀態(tài)寄存器 | 06H | 00H |
8 | ROCR | 接收溢出計(jì)數(shù)寄存器 | 07H | 00H |
9 | BPTR | 背壓閾值寄存器 | 08H | 37H |
10 | FCTR | 流控制閾值寄存器 | 09H | 38H |
11 | FCR | TX/RX 流控制寄存器 | 0AH | 00H |
12 | EPCR | EEPROM&PHY 控制寄存器 | 0BH | 00H |
13 | EPAR | EEPROM&PHY 地址寄存器 | 0CH | 40H |
14 | EPDRL | EEPROM&PHY 低字節(jié)數(shù)據(jù)寄存器 | 0DH | XXH |
15 | EPDRH | EEPROM&PHY 高字節(jié)數(shù)據(jù)寄存器 | 0EH | XXH |
16 | WCR | 喚醒控制寄存器 | 0FH | 00H |
17 | PAR | 物理地址寄存器 | 10H~15H | 由 EEPROM決定 |
18 | MAR | 廣播地址寄存器 | 16H~1DH | XXH |
19 | GPCR | 通用目的控制寄存器(8bit 模式) | 1EH | 01H |
20 | GPR | 通用目的寄存器 | 1FH | XXH |
21 | TRPAL | TX SRAM 讀指針地址低字節(jié) | 22H | 00H |
22 | TRPAH | TX SRAM 讀指針地址高字節(jié) | 23H | 00H |
23 | RWPAL | RX SRAM 寫指針地址低字節(jié) | 24H | 00H |
24 | RWPAH | RX SRAM 寫指針地址高字節(jié) | 25H | 0CH |
25 | VID | 廠家 ID | 28H~29H | 0A46H |
26 | PID | 產(chǎn)品 ID | 2AH~2BH | 9000H |
27 | CHIPR | 芯片版本 | 2CH | 18H |
28 | TCR2 | 發(fā)送控制寄存器 2 | 2DH | 00H |
29 | OCR | 操作控制寄存器 | 2EH | 00H |
30 | SMCR | 特殊模式控制寄存器 | 2FH | 00H |
31 | ETXCSR | 即將發(fā)送控制/狀態(tài)寄存器 | 30H | 00H |
32 | TCSCR | 發(fā)送校驗(yàn)和控制寄存器 | 31H | 00H |
33 | RCSCSR | 接收校驗(yàn)和控制狀態(tài)寄存器 | 32H | 00H |
34 | MRCMDX | 內(nèi)存數(shù)據(jù)預(yù)取讀命令寄存器(地址不加 1) | F0H | XXH |
35 | MRCMDX1 | 內(nèi)存數(shù)據(jù)讀命令寄存器(地址不加 1) | F1H | XXH |
36 | MRCMD | 內(nèi)存數(shù)據(jù)讀命令寄存器(地址加 1) | F2H | XXH |
37 | MRRL | 內(nèi)存數(shù)據(jù)讀地址寄存器低字節(jié) | F4H | 00H |
38 | MRRH | 內(nèi)存數(shù)據(jù)讀地址寄存器高字節(jié) | F5H | 00H |
39 | MWCMDX | 內(nèi)存數(shù)據(jù)寫命令寄存器(地址不加 1) | F6H | XXH |
40 | MWCMD | 內(nèi)存數(shù)據(jù)寫命令寄存器(地址加 1) | F8H | XXH |
41 | MWRL | 內(nèi)存數(shù)據(jù)寫地址寄存器低字節(jié) | FAH | 00H |
42 | MWRH | 內(nèi)存數(shù)據(jù)寫地址寄存器高字節(jié) | FBH | 00H |
43 | TXPLL | TX 數(shù)據(jù)包長度低字節(jié)寄存器 | FCH | XXH |
44 | TXPLH | TX 數(shù)據(jù)包長度高字節(jié)寄存器 | FDH | XXH |
45 | ISR | 中斷狀態(tài)寄存器 | FEH | 00H |
46 | IMR | 中斷屏蔽寄存器 | FFH | 00H |
關(guān)于默認(rèn)值的要點(diǎn)(Key to Default) 在下面寄存器描述中,默認(rèn)欄采用如下形式:
,
其中
1 該位設(shè)為邏輯 1
0 該位設(shè)為邏輯 0
X 沒有默認(rèn)值
P 電源復(fù)位恢復(fù)默認(rèn)值
H 硬件復(fù)位恢復(fù)默認(rèn)值
S 軟件復(fù)位恢復(fù)默認(rèn)值
E 從 EEPROM 得到默認(rèn)值
T 從捆綁引腳(strap pin)得到默認(rèn)值
:
RO = 只讀
RW = 可讀可寫
R/C = 可讀/擦除
RW/C1=可讀可寫/通過寫1擦除
WO = 只寫
保留位被隱藏且應(yīng)寫 0,在讀訪問時(shí)保留位沒有定義。
如何讀取 DM9000A 的寄存器 RSR?假設(shè)要讀取 DM9000A 的寄存器 RSR(RX Status Register),需要分 2 步:
- 向 INDEX 端口寫入 RSR 寄存器的地址(0x06) 條件:nGCS1 信號(hào)拉低、 Xm0WEn 信號(hào)拉低、 Xm0ADDR2 拉低, 或者說向下面的地址寫數(shù)據(jù) 0x06從 DATA 端口讀取 RSR 寄存器的值 條件:nGCS1 信號(hào)拉低、 Xm0OEn 信號(hào)拉低、 Xm0ADDR2 拉高, 或者說從下面的地址讀數(shù)據(jù)
DM9000A的寄存器很多,但是我們并需要都掌握,我們只需要掌握其中幾個(gè)最重要的寄存器的使用即可。
- 網(wǎng)絡(luò)控制寄存器(NCR)
網(wǎng)絡(luò)狀態(tài)寄存器(NSR)
在這里插入圖片描述
ISR
DAVICOM 指定配置和狀態(tài)寄存器(DSCSR)
4. 網(wǎng)卡的初始化
網(wǎng)卡的初始化函數(shù)入口位于文件net/eth.c下的函數(shù)eth_init():
404 int eth_init(bd_t *bis)
405 {
406 struct eth_device *old_current, *dev;
……
425 old_current = eth_current;
426 do {
427 debug("Trying %sn", eth_current->name);
428
429 if (eth_current->init(eth_current, bis) >= 0) {
430 eth_current->state = ETH_STATE_ACTIVE;
431
432 return 0;
433 }
434 debug("FAILn");
……
440 }
429行即調(diào)用我們注冊的dm9000A初始化函數(shù),從這也可以看出,整個(gè)架構(gòu)是把網(wǎng)卡的驅(qū)動(dòng)獨(dú)立分隔開,與硬件操作相關(guān)的代碼由用戶自己填充并注冊到系統(tǒng)中即可,便于擴(kuò)展。進(jìn)入dm9000_init():
290 static int dm9000_init(struct eth_device *dev, bd_t *bd)
291 {
292 int i, oft, lnk;
293 u8 io_mode;
294 struct board_info *db = &dm9000_info;
295
296 DM9000_DBG("%sn", __func__);
297
298 /* RESET device */
299 dm9000_reset();
300
301 if (dm9000_probe() < 0)
302 return -1;
303
304 /* Auto-detect 8/16/32 bit mode, ISR Bit 6+7 indicate bus width */
305 io_mode = DM9000_ior(DM9000_ISR) >> 6;
306
307 switch (io_mode) {
308 case 0x0: /* 16-bit mode */
309 printf("DM9000: running in 16 bit moden");
310 db->outblk = dm9000_outblk_16bit;
311 db->inblk = dm9000_inblk_16bit;
312 db->rx_status = dm9000_rx_status_16bit;
313 break;
314 case 0x01: /* 32-bit mode */
315 printf("DM9000: running in 32 bit moden");
316 db->outblk = dm9000_outblk_32bit;
317 db->inblk = dm9000_inblk_32bit;
318 db->rx_status = dm9000_rx_status_32bit;
319 break;
320 case 0x02: /* 8 bit mode */
321 printf("DM9000: running in 8 bit moden");
322 db->outblk = dm9000_outblk_8bit;
323 db->inblk = dm9000_inblk_8bit;
324 db->rx_status = dm9000_rx_status_8bit;
325 break;
326 default:
327 /* Assume 8 bit mode, will probably not work anyway */
328 printf("DM9000: Undefined IO-mode:0x%xn", io_mode);
329 db->outblk = dm9000_outblk_8bit;
330 db->inblk = dm9000_inblk_8bit;
331 db->rx_status = dm9000_rx_status_8bit;
332 break;
333 }
334
335 /* Program operating register, only internal phy supported */
336 DM9000_iow(DM9000_NCR, 0x0);
337 /* TX Polling clear */
338 DM9000_iow(DM9000_TCR, 0);
339 /* Less 3Kb, 200us */
340 DM9000_iow(DM9000_BPTR, BPTR_BPHW(3) | BPTR_JPT_600US);
341 /* Flow Control : High/Low Water */
342 DM9000_iow(DM9000_FCTR, FCTR_HWOT(3) | FCTR_LWOT(8));
343 /* SH FIXME: This looks strange! Flow Control */
344 DM9000_iow(DM9000_FCR, 0x0);
345 /* Special Mode */
346 DM9000_iow(DM9000_SMCR, 0);
347 /* clear TX status */
348 DM9000_iow(DM9000_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END);
349 /* Clear interrupt status */
350 DM9000_iow(DM9000_ISR, ISR_ROOS | ISR_ROS | ISR_PTS | ISR_PRS);
351
352 printf("MAC: %pMn", dev->enetaddr);
353
354 /* fill device MAC address registers */
355 for (i = 0, oft = DM9000_PAR; i < 6; i++, oft++)
356 DM9000_iow(oft, dev->enetaddr[i]);
357 for (i = 0, oft = 0x16; i < 8; i++, oft++)
358 DM9000_iow(oft, 0xff);
359
360 /* read back mac, just to be sure */
361 for (i = 0, oft = 0x10; i < 6; i++, oft++)
362 DM9000_DBG("%02x:", DM9000_ior(oft));
363 DM9000_DBG("n");
364
365 /* Activate DM9000 */
366 /* RX enable */
367 DM9000_iow(DM9000_RCR, RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN);
368 /* Enable TX/RX interrupt mask */
369 DM9000_iow(DM9000_IMR, IMR_PAR);
370
371 i = 0;
372 while (!(dm9000_phy_read(1) & 0x20)) { /* autonegation complete bit */
373 udelay(1000);
374 i++;
375 if (i == 10000) {
376 printf("could not establish linkn");
377 return 0;
378 }
379 }
380
381 /* see what we've got */
382 lnk = dm9000_phy_read(17) >> 12;
383 printf("operating at ");
384 switch (lnk) {
385 case 1:
386 printf("10M half duplex ");
387 break;
388 case 2:
389 printf("10M full duplex ");
390 break;
391 case 4:
392 printf("100M half duplex ");
393 break;
394 case 8:
395 printf("100M full duplex ");
396 break;
397 default:
398 printf("unknown: %d ", lnk);
399 break;
400 }
401 printf("moden");
402 return 0;
403 }
299行 函數(shù)DM9000_reset()是對(duì)dm9000A重置 301行 函數(shù)dm9000_probe()分別從寄存器VID、PID讀取廠家ID、產(chǎn)品ID 305行 讀取DM9000A的 ISR寄存器,根據(jù)bite[6:7]的值來決定最終從DM9000A中讀取數(shù)位數(shù),并將對(duì)應(yīng)的函數(shù)設(shè)置到db->outblk和db->inblk這兩個(gè)變量,最終上層服務(wù)想收發(fā)數(shù)據(jù)就通過這兩個(gè)函數(shù),對(duì)于16位模式,就分別賦值dm9000_outblk_16bit、dm9000_inblk_16bit;db->rx_status該函數(shù)用于從DM9000A中讀取網(wǎng)卡的狀態(tài)信息和數(shù)據(jù)包的長度,對(duì)于16位模式會(huì)賦值為dm9000_rx_status_16bit 336~350行 對(duì)DM9000A進(jìn)行初始化配置 355~358行 將mac地址寫入到DM9000A的PAR寄存器 367行 使能數(shù)據(jù)接收 369行 使能SRAM的讀/寫指針在指針地址超過SRAM的大小時(shí)自動(dòng)跳回起始位置 382行 讀取phy寄存器DSCSR,打印當(dāng)前網(wǎng)口的帶寬
通過讀 bit[15:12]來看經(jīng)過自動(dòng)協(xié)商后選擇的是哪一種模式。網(wǎng)卡自動(dòng)協(xié)商完成后,結(jié)果將被寫到該位。若該位為 1,意味著操作 1 模式是 100M 全雙工模式。
5. 數(shù)據(jù)的發(fā)送
發(fā)送流程
- 清中斷,ISR寄存器bit[1] = 1發(fā)送寫操作,操作MWCMD通過DM9000_DATA寫入數(shù)據(jù)設(shè)置數(shù)據(jù)幀的長度 TXPLL、TXPLH發(fā)送發(fā)送請求,TCR等待數(shù)據(jù)發(fā)送完畢,輪訓(xùn)檢查NSR清中斷,ISR寄存器bit[1] = 1
網(wǎng)卡數(shù)據(jù)的發(fā)送函數(shù)是dm9000_send()
405 /*
406 Hardware start transmission.
407 Send a packet to media from the upper layer.
408 */
409 static int dm9000_send(struct eth_device *netdev, void *packet, int length)
410 {
411 int tmo;
412 struct board_info *db = &dm9000_info;
413
414 DM9000_DMP_PACKET(__func__ , packet, length);
415
416 DM9000_iow(DM9000_ISR, IMR_PTM); /* Clear Tx bit in ISR */
417
418 /* Move data to DM9000 TX RAM */
419 DM9000_outb(DM9000_MWCMD, DM9000_IO); /* Prepare for TX-data */
420
421 /* push the data to the TX-fifo */
422 (db->outblk)(packet, length);
423
424 /* Set TX length to DM9000 */
425 DM9000_iow(DM9000_TXPLL, length & 0xff);
426 DM9000_iow(DM9000_TXPLH, (length >> 8) & 0xff);
427
428 /* Issue TX polling command */
429 DM9000_iow(DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */
430
431 /* wait for end of transmission */
432 tmo = get_timer(0) + 5 * CONFIG_SYS_HZ;
433 while ( !(DM9000_ior(DM9000_NSR) & (NSR_TX1END | NSR_TX2END)) ||
434 !(DM9000_ior(DM9000_ISR) & IMR_PTM) ) {
435 if (get_timer(0) >= tmo) {
436 printf("transmission timeoutn");
437 break;
438 }
439 }
440 DM9000_iow(DM9000_ISR, IMR_PTM); /* Clear Tx bit in ISR */
441
442 DM9000_DBG("transmit donenn");
443 return 0;
444 }
該函數(shù)的參數(shù)
struct eth_device *netdev:設(shè)備
void *packet :發(fā)送數(shù)據(jù)包存放的內(nèi)存的首地址
int length :發(fā)送的數(shù)據(jù)包長度
414行 打開debug開關(guān),該行會(huì)打印發(fā)送的數(shù)據(jù)包 416行 使能數(shù)據(jù)包發(fā)送,將寄存器ISR的bit[1]設(shè)置為1 419行 通過寄存器MWCMD寫入一個(gè)地址,并向該地址對(duì)應(yīng)的 SRAM 中寫數(shù)據(jù)。執(zhí)行寫該指令之后,寫指針會(huì)根據(jù)操作模式(8 位或 16 位)自動(dòng)增加 1 或 2。422行 調(diào)用上一節(jié)db->outblk所賦值的函數(shù)將數(shù)據(jù)包發(fā)送的DM9000A的發(fā)送fifo中 425~426行 將發(fā)送數(shù)據(jù)包長度寫入到寄存器TXPLL/TXPLH中,這兩個(gè)寄存器分別對(duì)應(yīng)低字節(jié)和高字節(jié) 429行 向寄存器TCR的bit[0]寫入1,來請求發(fā)送數(shù)據(jù),發(fā)送完畢該位自動(dòng)清0 432~440行 通過向寄存器ISR的bit[1]寫入1,來清楚發(fā)送標(biāo)記位
其中發(fā)送函數(shù)dm9000_outblk_16bit() 定義如下:
159 static void dm9000_outblk_16bit(volatile void *data_ptr, int count)
160 {
161 int i;
162 u32 tmplen = (count + 1) / 2;
163
164 for (i = 0; i < tmplen; i++)
165 DM9000_outw(((u16 *) data_ptr)[i], DM9000_DATA);
166 }
164~165行 就是循環(huán)從地址DM9000_DATA讀取數(shù)據(jù)并存儲(chǔ)到data_ptr執(zhí)行的內(nèi)存中 此處我們看到每次都是從相同的地址讀取數(shù)據(jù),為什么不需要做地址偏移呢?答:寄存器MWCMD已經(jīng)和我們說的很清楚了,寫該指令之后,指寫指針根據(jù)操作模式(8 位或 16 位)增 加 1 或 2。
6. 數(shù)據(jù)的接收
DM9000A的數(shù)據(jù)接收
464 static int dm9000_rx(struct eth_device *netdev)
465 {
466 u8 rxbyte, *rdptr = (u8 *) NetRxPackets[0];
467 u16 RxStatus, RxLen = 0;
468 struct board_info *db = &dm9000_info;
469
470 /* Check packet ready or not, we must check
471 the ISR status first for DM9000A */
472 if (!(DM9000_ior(DM9000_ISR) & 0x01)) /* Rx-ISR bit must be set. */
473 return 0;
474
475 DM9000_iow(DM9000_ISR, 0x01); /* clear PR status latched in bit 0 */
476
477 /* There is _at least_ 1 package in the fifo, read them all */
478 for (;;) {
479 DM9000_ior(DM9000_MRCMDX); /* Dummy read */
480
481 /* Get most updated data,
482 only look at bits 0:1, See application notes DM9000 */
483 rxbyte = DM9000_inb(DM9000_DATA) & 0x03;
484
485 /* Status check: this byte must be 0 or 1 */
486 if (rxbyte > DM9000_PKT_RDY) {
487 DM9000_iow(DM9000_RCR, 0x00); /* Stop Device */
488 DM9000_iow(DM9000_ISR, 0x80); /* Stop INT request */
489 printf("DM9000 error: status check fail: 0x%xn",
490 rxbyte);
491 return 0;
492 }
493
494 if (rxbyte != DM9000_PKT_RDY)
495 return 0; /* No packet received, ignore */
496
497 DM9000_DBG("receiving packetn");
498
499 /* A packet ready now & Get status/length */
500 (db->rx_status)(&RxStatus, &RxLen);
501
502 DM9000_DBG("rx status: 0x%04x rx len: %dn", RxStatus, RxLen);
503
504 /* Move data from DM9000 */
505 /* Read received packet from RX SRAM */
506 (db->inblk)(rdptr, RxLen);
507
508 if ((RxStatus & 0xbf00) || (RxLen < 0x40)
509 || (RxLen > DM9000_PKT_MAX)) {
510 if (RxStatus & 0x100) {
511 printf("rx fifo errorn");
512 }
513 if (RxStatus & 0x200) {
514 printf("rx crc errorn");
515 }
516 if (RxStatus & 0x8000) {
517 printf("rx length errorn");
518 }
519 if (RxLen > DM9000_PKT_MAX) {
520 printf("rx length too bign");
521 dm9000_reset();
522 }
523 } else {
524 DM9000_DMP_PACKET(__func__ , rdptr, RxLen);
525
526 DM9000_DBG("passing packet to upper layern");
527 NetReceive(NetRxPackets[0], RxLen);
528 }
529 }
530 return 0;
531 }
472行 DM9000A的寄存器ISR的bit[0]必須設(shè)置為1,否則無法接收數(shù)據(jù) 475行 將ISR的bit[0]設(shè)置為1 479行 讀取寄存器MRCMDX, 以從接收 SRAM 中讀數(shù)據(jù);執(zhí)行讀取該指令之后,指向內(nèi)部 SRAM的讀指針不變。DM9000A 開始預(yù)取 SRAM 中數(shù)據(jù)到內(nèi)部數(shù)據(jù)緩沖中 483~494行 從地址DM9000_DATA中讀取數(shù)據(jù),從SRAM中讀取的第一個(gè)數(shù)據(jù)的bit[0]必須是1,否則出錯(cuò) 500行 通過函數(shù)指針db->rx_status讀取網(wǎng)卡的狀態(tài)和接收到的數(shù)據(jù)包的長度 506行 通過函數(shù)指針db->inblk從網(wǎng)卡中讀取數(shù)據(jù) 527行 通過函數(shù)NetReceive()提交給上層協(xié)議棧
真正讀取數(shù)據(jù)的函數(shù)是dm9000_inblk_16bit(); 定義如下:
static void dm9000_inblk_16bit(void *data_ptr, int count)
{
int i;
u32 tmplen = (count + 1) / 2;
for (i = 0; i < tmplen; i++)
((u16 *) data_ptr)[i] = DM9000_inw(DM9000_DATA);
}
原理類似于函數(shù)dm9000_outblk_16bit,不再重復(fù)。
由此可見,要分析DM9000A的數(shù)據(jù)收發(fā)的原理和流程,就要分析我們注冊網(wǎng)卡的以下幾個(gè)函數(shù):
635 dev->send = dm9000_send;
636 dev->recv = dm9000_rx;
310 db->outblk = dm9000_outblk_16bit;
311 db->inblk = dm9000_inblk_16bit;