加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
    • 一、網(wǎng)卡
    •  
    • 二、DM9000
    •  
    • 三、SROM 控制器
    •  
    • 四、DM9000A驅(qū)動(dòng)分析
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請入駐 產(chǎn)業(yè)圖譜

從0學(xué)ARM-網(wǎng)卡DM9000詳解-基于uboot

2021/05/13
1485
閱讀需 43 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

從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. 分類

  1. 按總線接口類型分 按網(wǎng)卡的總線接口類型來分一般可分bai為ISA接口網(wǎng)卡、PCI接口網(wǎng)卡以及在服務(wù)器上使用的PCI-X總線接口類型的網(wǎng)卡,筆記本電腦所使用的網(wǎng)卡是PCMCIA接口類型的。
  • (1)ISA總線網(wǎng)卡(2)PCI總線網(wǎng)卡(3)PCI-X總線網(wǎng)卡(4)PCMCIA總線網(wǎng)卡(5)USB總線接口網(wǎng)卡
  1. 按網(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)卡
  1. 按帶寬劃分 隨著網(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 步:

  1. 向 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è)最重要的寄存器的使用即可。

  1. 網(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ā)送流程

  1. 清中斷,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;

Arm

Arm

ARM公司是一家知識(shí)產(chǎn)權(quán)(IP)供應(yīng)商,主要為國際上其他的電子公司提供高性能RISC處理器、外設(shè)和系統(tǒng)芯片技術(shù)授權(quán)。目前,ARM公司的處理器內(nèi)核已經(jīng)成為便攜通訊、手持計(jì)算設(shè)備、多媒體數(shù)字消費(fèi)品等方案的RISC標(biāo)準(zhǔn)。公司1990年11月由Acorn、Apple和VLSI合并而成。

ARM公司是一家知識(shí)產(chǎn)權(quán)(IP)供應(yīng)商,主要為國際上其他的電子公司提供高性能RISC處理器、外設(shè)和系統(tǒng)芯片技術(shù)授權(quán)。目前,ARM公司的處理器內(nèi)核已經(jīng)成為便攜通訊、手持計(jì)算設(shè)備、多媒體數(shù)字消費(fèi)品等方案的RISC標(biāo)準(zhǔn)。公司1990年11月由Acorn、Apple和VLSI合并而成。收起

查看更多

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

公眾號(hào)『一口Linux』號(hào)主彭老師,擁有15年嵌入式開發(fā)經(jīng)驗(yàn)和培訓(xùn)經(jīng)驗(yàn)。曾任職ZTE,某研究所,華清遠(yuǎn)見教學(xué)總監(jiān)。擁有多篇網(wǎng)絡(luò)協(xié)議相關(guān)專利和軟件著作。精通計(jì)算機(jī)網(wǎng)絡(luò)、Linux系統(tǒng)編程、ARM、Linux驅(qū)動(dòng)、龍芯、物聯(lián)網(wǎng)。原創(chuàng)內(nèi)容基本從實(shí)際項(xiàng)目出發(fā),保持原理+實(shí)踐風(fēng)格,適合Linux驅(qū)動(dòng)新手入門和技術(shù)進(jìn)階。