uClinux下移植Ne2000兼容的网卡驱动程序(转)
生活随笔
收集整理的這篇文章主要介紹了
uClinux下移植Ne2000兼容的网卡驱动程序(转)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
原文地址:http://blog.chinaunix.net/u2/66822/showart_706889.html
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
日期:2006-08-14?
? 我是linux的新手,可以說從來沒有在linux下寫過程序,對于linux內核也是相當陌生,前一段時間,拿著tpu一個移植好了的uClinux在 S3C44B0(ARM7TDMI核的嵌入式處理器)上的版本,把它成功的跑在了我自己的S3C44B0的板子上,這也就算是平生在uClinux下作的 第一個工作吧。接下來就是添加網卡驅動,我用的是RTL8019AS--比較常用的ISA接口的以太網芯片。下面就從一個新手的角度來說說我的移植過程 吧,其實很簡單,我的整個摸索+移植的過程也就花了2天的時間,我盡量寫的詳細(羅嗦?)一點,希望對像我這樣的新手入門有所幫助,錯誤之處在所難免,歡 迎指正。
? 開始的時候,我也是摸不著頭腦,不知道該從什么地方入手。用SoureInsight把整個uClinux內核的源碼都添加進來,熟悉一下linux的內 核(其實就是在里面瞎撞,也不怎么能看懂)。按照linux內核目錄的分類,很自然的就找到Ne2000網卡的驅動就是./drivers/net /ne.c,和它相關的還有8390.h和8390.c。看看代碼,逐漸的就明白了:
? 首先,在Ne.c中函數ne_probe就是網卡的檢測函數,如果檢測到Ne2000兼容的網卡就return 0。那個函數沒有什么具體的工作,就是搭了一個架子。看的有前人在這個函數開始寫到:
#if defined (CONFIG_NETtel) && defined (CONFIG_M5307)
…………
#elif defined(CONFIG_COLDFIRE)
static int once = 0;
if (once)
return -ENXIO;
if (base_addr == 0) {
dev->base_addr = base_addr = NE2000_ADDR;
dev->irq = NE2000_IRQ_VECTOR;
once++;
}
#endif
就明白了,可以把網卡的基地址、中斷號都放到這里面定義。我也跟著照葫蘆畫瓢,添
加了一個:
#elif defined(CONFIG_ARCH_S3C44B0) //--by threewater
static int once = 0;
if (once)
return -ENXIO;
if (base_addr == 0) {
dev->base_addr = base_addr = ARM_NE2000_BASE;
dev->irq = ARM_NE2000_IRQ;
once++;
}
其中:ARM_NE2000_BASE和ARM_NE2000_IRQ是在配置內核的時候定義的,這個以后再說。
接下來,具體的工作就轉移到了ne_probe1函數里面做。用SourceInight跟進來看(這個軟件太好用了,忍不住在這里再坐一會廣告)。Ne_probe1中,一開始就是
reg0 = inb_p(ioaddr);
if (reg0 == 0xFF) {
ret = -ENODEV;
goto err_out;
}
很容易理解,就是讀一下網卡的基地址,對我來說也就是RTL8019的REG0,如果是0xff,說明沒有檢測到網卡,返回錯誤。好了,在下面添加一行
printk("begin find Ne2000 Net Card...\tbase address=0x%X\n",ioaddr);
//--add by threewater
來 證明我們的想法是正確的,程序如果能讀取8019的REG0,就應該顯示出這一行。可是,那個ne_probe是誰調用的呢?還是用 SourceInsight去找,用jamp to caller,哈哈,太容易了,立刻就看到了,網卡的檢測是從./drivers/net/Space.c的ethif_probe函數
中實現的,關鍵代碼:
if (probe_list(dev, eisa_probes) == 0)
return 0;
eisa_probes在前面定義成全局:
static struct devprobe eisa_probes[] __initdata = {
#ifdef CONFIG_DE4X5 /* DEC DE425, DE434, DE435 adapters */
{de4x5_probe, 0},
#endif
…………
{NULL, 0},
};
我也照著添加了:
if (probe_list(dev, arm_probes) == 0)
return 0;
并定義:
static struct devprobe arm_probes[] __initdata = {
#ifdef CONFIG_ARM
{ne_probe, 0},
#endif
{NULL, 0},
};
這樣,編譯內核啟動,果然,顯示出了輸出結果。
繼續分析修改ne.c中ne_probe1的代碼(關鍵的東東全在這里面呢)。接下來就是
? outb_p(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD);
? regd = inb_p(ioaddr + 0x0d);
? outb_p(0xff, ioaddr + 0x0d);
讀取REGD中的數據,這里,再仔細跟蹤一下outb_p這個函數,在x86中,這個就是一個IO口的輸出函數,在S3C44B0是存儲器和IO統一編址的(或者說不分存儲器還是IO),經過了幾次宏定義以后,很快找到如下宏代碼:
? (*(volatile unsigned char *)(a))
和我想的一樣,就是靠這個訪問外部總線的。我的8019在S3C44B0的Bank 5上,工作在跳線模式,算了一下,起始基地址就是0x0a000600。
這 里,需要說明一下我的硬件配置和連接,8019工作在16位模式下,S3C44B0的Bank5配置成16位模式,數據線一對一的連接,地址線錯開一位 --8019的A0連接S3C44B0的A1……這樣,8019的基地址(Reg0的地址)是0x0a000600,Reg1的地址就是 0x0a000602……地址不是連續增加的,所以,對應的驅動程序要做相應的修改。查找E8390_CMD的定義,發現,在8390.h中有:
#define E8390_CMD EI_SHIFT(0x00) /* The command register (for all pages)
*/
/* Page 0 register offsets. */
#define EN0_CLDALO EI_SHIFT(0x01) /* Low byte of current local dma addr
RD */
#define EN0_STARTPG EI_SHIFT(0x01) /* Starting page of ring bfr WR */
……
而EI_SHIFT根據不同的配置有兩種定義,如下:
#if defined(CONFIG_MAC) || defined(CONFIG_AMIGA_PCMCIA) || \
? defined(CONFIG_ARIADNE2) || defined(CONFIG_ARIADNE2_MODULE) || \
? defined(CONFIG_HYDRA) || defined(CONFIG_HYDRA_MODULE) || \
? defined(CONFIG_ARM_ETHERH) || defined(CONFIG_ARM_ETHERH_MODULE)
#define EI_SHIFT(x) (ei_local->reg_offset[x])
#else
#define EI_SHIFT(x) (x)
#endif
看來,在8390的驅動中已經考慮到了不連續增長的地址的問題了,繼續跟蹤查看ei_local->regoffset[x]的定義就比較麻煩了。干脆,我用一個笨方法,直接添加:
#elif defined(CONFIG_ARM) || defined(CONFIG_ARM_MODULE) //--by
threewater
#define EI_SHIFT(x) ((x)*2)
對應的,在ne.c也有類似的定義問題:
#define NE_CMD 0x00
#define NE_DATAPORT 0x10 /* NatSemi-defined port window offset. */
#define NE_RESET 0x1f /* Issue a read to reset, a write to clear. */
#define NE_IO_EXTENT 0x20
添加成:
#ifdef CONFIG_ARM //--by threewater
#define NE_CMD 0x00
#define NE_DATAPORT 0x20 /* NatSemi-defined port window offset. */
#define NE_RESET 0x3e /* Issue a read to reset, a write to clear. */
#define NE_IO_EXTENT 0x40
#else
……
這樣,地址偏移的問題就基本解決了。當然,在Ne.c中,也有直接訪問reg的代碼,比如上面說的代碼也相應的添加成:
#ifdef CONFIG_ARM //--add by threewater
? regd = inb_p(ioaddr + 0x0d*2);
? outb_p(0xff, ioaddr + 0x0d*2);
#else
? regd = inb_p(ioaddr + 0x0d);
? outb_p(0xff, ioaddr + 0x0d);
我沒有看過linux編程的規范,也不知的修改內核有什么規矩,不過,我都是用預處理來添加我自己的代碼,從來不直接在原有的代碼上修改,我覺得這樣更可以保證代碼的完整性和可移植性,而且,還容易比較,容易找出問題(當然,如果#if嵌套多了,也很難看的:()。
接 下來的初始化8019,就沒有什么問題了,然后就是配置網卡的物理地址了。在我的系統上,沒有使用8019的初始化配置芯片,物理地址需要在程序中直接寫 入(其實,就是使用配置芯片,也需要用程序讀出再寫入的),物理地址可以編譯到代碼里,也可以存儲到flash的一個固定地址中。可以參考 ne_probe1里面的例子,照著勒就可以了。剩下注冊中斷什么的,也就是算好了中斷號,照著添加自己的代碼。很容易的。
到這里,似乎就沒有什么工作了。編譯內核,啟動,恩Ne2000兼容的網卡找到了,接下來就不正常了。系統報告,反復陷入那個網卡的中斷……
反復陷入中斷,很容易想到就是中斷模式配置的問題,8019的中斷是高電平有效,看看S3C44B0上的配置,果然不對。這個配置是在Bootloader中做好了的,改一下,就好了。我把他改成了上升沿觸發。
別人的批注:
最好改為高電平觸發,我就吃過這樣的苦頭,當時我那個驅動不太穩定,一旦有錯誤,就死活不工作,后來發現是8019的中斷線一直為高,顯示有中斷,但CPU不知什么原因,開始的時候沒有檢測到上升沿,因此以后就再也收不到中斷了,把觸發方式改了以后,就非常穩定了
另 外,因為S3C44B0是IO空間和存儲器統一編址。這就容易忽視一個問題,就是緩沖。對于存儲器,加上片內的緩沖可以提高效率,不過對于外部設備比如這 個8019,就不能使用緩沖。記住,緩沖的范圍一定要配置正確,我開始就弄錯了,產生了一些莫名其妙的問題,耽誤了不少時間。
上述問題都 解決了,啟動的時候可以找到網卡,可以配置好物理地址,啟動以后ifconfig eth0也沒有問題,這次應該沒有問題了吧。可是,結果還是ping通。這次就比較麻煩了,沒有內核跟蹤調試的手段,只能靠printk來輸出?不知道應 該從哪里入手了。不過還好,調試以太網,有Sniffer(一個功能強大的抓包軟件,在局域網內的數據包都是抓到)。就靠它了,在我的PC上運行,抓包。 在uClinux下ping主機的IP。果然能抓到數據包。分析抓取的數據包發現問題。
按理說,ping的時候,第一次不知道目的主機的 Mac地址,所以,應該發送ARP廣播,發送的數據大概的格式開頭應該是FF FF FF FF FF FF AA BB CC DD EE FF…………(AABB CC DD EE FF表示發送方的Mac地址),可是我抓到的數據包是FF FF FF FF FF FF AA AA BB BB CC CC DD DD EE EE FF FF…… 看明白了,這個問題應該是網卡發送的時候,向網卡寫入數據連續寫了兩次。這個問題最容易讓人想到是S3C44B0的掛8019的那個Bank的數據寬度配 置錯了。可是,我仔細的看了,不是這個問題。那就只有再仔細看看源碼了。還是在drivers/net/ne.c里面,ne_block_output函 數--這個就是8019發送時候調用的函數了,里面有代碼:
if (ei_status.word16) {
outsw(NE_BASE + NE_DATAPORT, (void*)buf, count>>1);
} else {
outsb(NE_BASE + NE_DATAPORT, (void*)buf, count);
}
我 跟蹤了一下,ei_status.word16=1,這個沒有問題。那么,問題就出在outsw函數上了。用SourceInsight一層層的跟蹤(做 一個函數右一個宏的,定義的可真多,好多不同模式或者處理器下的相同定義,要看清楚自己的),最后,終于把目標鎖定在了arch/armnommu /lib/ io-writesw-armv3.S和io-writesw-armv4.S兩個匯編文件。到底是哪個呢?
熟悉ARM家族 的人應該知道ARMv3和ARMv4的一些區別,看看這兩匯編,就可以開出來他們對16位數讀寫操作的不同,按照道理S3C44B0應該是ARMv4(我 記得應該是,不到出處了,至少看了那個兩匯編文件,我認定應該用ARMv4那個),可是,看了一下便一輸出的.o文件,是io-writesw- armv3.o,顯然弄錯了,這里就是問題了。那么為什么要編譯ARMv3而不是ARMv4這個文件呢?在Makefile和Config.in中經過一 番尋找,終于找,原來在定義arch/armnommu/config.in中,定義CONFIG_ARCH_S3C44B0的時候,沒有定義
CONFIG_CPU_32v4
那么,默認情況下,就定義CONFIG_CPU_32v3,用它來編譯。好了。把ARMv4的定義添上。順便把前面說的ARM_NE2000_BASE和ARM_NE2000_IRQ的定義以添加到這里,讓用戶可以自己定義裁剪。
hex 'Base Address for NE2000 ethernet' ARM_NE2000_BASE youraddr
hex 'IRQ for NE2000 ethernet' ARM_NE2000_IRQ yourinterrupt
好 了,編譯通過。運行,果然沒有問題了。Ping可以,telnet可以,在內核中把NFS打開,mount -t nfs ……也好用。哈哈。太好了。至此,8019在S344B0組成的uclinux平臺上的驅動,移植成功。相信其他的網卡芯片移植驅動程序應該也基本是這個 思路。現在寫出來與大家共享。希望對新手入門有所幫助,同時文章中有我理解錯誤的地方,也希望高手指教。
最后,再總結一下,移植過程中需要注意的幾個問題:
確定網卡的基地址、中斷無誤
注意網卡的數據總線寬度,地址是否連續,如果不連續,如何映射
注意網卡的中斷的模式和處理對應的外部中斷是不是一致
對于IO和RAM統一編址的處理器,注意緩沖區范圍的設置
注意ARMv3和ARMv4等一些和處理器結構相關的底層函數庫帶來的問題
用抓包軟件可以幫助分析定位問題所在
Btw, 我的PC平臺是在WindowsXP+Virtual PC下安裝的Red Hat linux 8.0,我覺得這樣調試起來比較方便,可以用SoureInsigh來閱讀,編寫代碼,可以在Linux編譯。充分發揮兩個操作系統的優勢。很適合于像我 這樣的,不熟悉Linux人開發。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
日期:2006-08-14?
? 我是linux的新手,可以說從來沒有在linux下寫過程序,對于linux內核也是相當陌生,前一段時間,拿著tpu一個移植好了的uClinux在 S3C44B0(ARM7TDMI核的嵌入式處理器)上的版本,把它成功的跑在了我自己的S3C44B0的板子上,這也就算是平生在uClinux下作的 第一個工作吧。接下來就是添加網卡驅動,我用的是RTL8019AS--比較常用的ISA接口的以太網芯片。下面就從一個新手的角度來說說我的移植過程 吧,其實很簡單,我的整個摸索+移植的過程也就花了2天的時間,我盡量寫的詳細(羅嗦?)一點,希望對像我這樣的新手入門有所幫助,錯誤之處在所難免,歡 迎指正。
? 開始的時候,我也是摸不著頭腦,不知道該從什么地方入手。用SoureInsight把整個uClinux內核的源碼都添加進來,熟悉一下linux的內 核(其實就是在里面瞎撞,也不怎么能看懂)。按照linux內核目錄的分類,很自然的就找到Ne2000網卡的驅動就是./drivers/net /ne.c,和它相關的還有8390.h和8390.c。看看代碼,逐漸的就明白了:
? 首先,在Ne.c中函數ne_probe就是網卡的檢測函數,如果檢測到Ne2000兼容的網卡就return 0。那個函數沒有什么具體的工作,就是搭了一個架子。看的有前人在這個函數開始寫到:
#if defined (CONFIG_NETtel) && defined (CONFIG_M5307)
…………
#elif defined(CONFIG_COLDFIRE)
static int once = 0;
if (once)
return -ENXIO;
if (base_addr == 0) {
dev->base_addr = base_addr = NE2000_ADDR;
dev->irq = NE2000_IRQ_VECTOR;
once++;
}
#endif
就明白了,可以把網卡的基地址、中斷號都放到這里面定義。我也跟著照葫蘆畫瓢,添
加了一個:
#elif defined(CONFIG_ARCH_S3C44B0) //--by threewater
static int once = 0;
if (once)
return -ENXIO;
if (base_addr == 0) {
dev->base_addr = base_addr = ARM_NE2000_BASE;
dev->irq = ARM_NE2000_IRQ;
once++;
}
其中:ARM_NE2000_BASE和ARM_NE2000_IRQ是在配置內核的時候定義的,這個以后再說。
接下來,具體的工作就轉移到了ne_probe1函數里面做。用SourceInight跟進來看(這個軟件太好用了,忍不住在這里再坐一會廣告)。Ne_probe1中,一開始就是
reg0 = inb_p(ioaddr);
if (reg0 == 0xFF) {
ret = -ENODEV;
goto err_out;
}
很容易理解,就是讀一下網卡的基地址,對我來說也就是RTL8019的REG0,如果是0xff,說明沒有檢測到網卡,返回錯誤。好了,在下面添加一行
printk("begin find Ne2000 Net Card...\tbase address=0x%X\n",ioaddr);
//--add by threewater
來 證明我們的想法是正確的,程序如果能讀取8019的REG0,就應該顯示出這一行。可是,那個ne_probe是誰調用的呢?還是用 SourceInsight去找,用jamp to caller,哈哈,太容易了,立刻就看到了,網卡的檢測是從./drivers/net/Space.c的ethif_probe函數
中實現的,關鍵代碼:
if (probe_list(dev, eisa_probes) == 0)
return 0;
eisa_probes在前面定義成全局:
static struct devprobe eisa_probes[] __initdata = {
#ifdef CONFIG_DE4X5 /* DEC DE425, DE434, DE435 adapters */
{de4x5_probe, 0},
#endif
…………
{NULL, 0},
};
我也照著添加了:
if (probe_list(dev, arm_probes) == 0)
return 0;
并定義:
static struct devprobe arm_probes[] __initdata = {
#ifdef CONFIG_ARM
{ne_probe, 0},
#endif
{NULL, 0},
};
這樣,編譯內核啟動,果然,顯示出了輸出結果。
繼續分析修改ne.c中ne_probe1的代碼(關鍵的東東全在這里面呢)。接下來就是
? outb_p(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD);
? regd = inb_p(ioaddr + 0x0d);
? outb_p(0xff, ioaddr + 0x0d);
讀取REGD中的數據,這里,再仔細跟蹤一下outb_p這個函數,在x86中,這個就是一個IO口的輸出函數,在S3C44B0是存儲器和IO統一編址的(或者說不分存儲器還是IO),經過了幾次宏定義以后,很快找到如下宏代碼:
? (*(volatile unsigned char *)(a))
和我想的一樣,就是靠這個訪問外部總線的。我的8019在S3C44B0的Bank 5上,工作在跳線模式,算了一下,起始基地址就是0x0a000600。
這 里,需要說明一下我的硬件配置和連接,8019工作在16位模式下,S3C44B0的Bank5配置成16位模式,數據線一對一的連接,地址線錯開一位 --8019的A0連接S3C44B0的A1……這樣,8019的基地址(Reg0的地址)是0x0a000600,Reg1的地址就是 0x0a000602……地址不是連續增加的,所以,對應的驅動程序要做相應的修改。查找E8390_CMD的定義,發現,在8390.h中有:
#define E8390_CMD EI_SHIFT(0x00) /* The command register (for all pages)
*/
/* Page 0 register offsets. */
#define EN0_CLDALO EI_SHIFT(0x01) /* Low byte of current local dma addr
RD */
#define EN0_STARTPG EI_SHIFT(0x01) /* Starting page of ring bfr WR */
……
而EI_SHIFT根據不同的配置有兩種定義,如下:
#if defined(CONFIG_MAC) || defined(CONFIG_AMIGA_PCMCIA) || \
? defined(CONFIG_ARIADNE2) || defined(CONFIG_ARIADNE2_MODULE) || \
? defined(CONFIG_HYDRA) || defined(CONFIG_HYDRA_MODULE) || \
? defined(CONFIG_ARM_ETHERH) || defined(CONFIG_ARM_ETHERH_MODULE)
#define EI_SHIFT(x) (ei_local->reg_offset[x])
#else
#define EI_SHIFT(x) (x)
#endif
看來,在8390的驅動中已經考慮到了不連續增長的地址的問題了,繼續跟蹤查看ei_local->regoffset[x]的定義就比較麻煩了。干脆,我用一個笨方法,直接添加:
#elif defined(CONFIG_ARM) || defined(CONFIG_ARM_MODULE) //--by
threewater
#define EI_SHIFT(x) ((x)*2)
對應的,在ne.c也有類似的定義問題:
#define NE_CMD 0x00
#define NE_DATAPORT 0x10 /* NatSemi-defined port window offset. */
#define NE_RESET 0x1f /* Issue a read to reset, a write to clear. */
#define NE_IO_EXTENT 0x20
添加成:
#ifdef CONFIG_ARM //--by threewater
#define NE_CMD 0x00
#define NE_DATAPORT 0x20 /* NatSemi-defined port window offset. */
#define NE_RESET 0x3e /* Issue a read to reset, a write to clear. */
#define NE_IO_EXTENT 0x40
#else
……
這樣,地址偏移的問題就基本解決了。當然,在Ne.c中,也有直接訪問reg的代碼,比如上面說的代碼也相應的添加成:
#ifdef CONFIG_ARM //--add by threewater
? regd = inb_p(ioaddr + 0x0d*2);
? outb_p(0xff, ioaddr + 0x0d*2);
#else
? regd = inb_p(ioaddr + 0x0d);
? outb_p(0xff, ioaddr + 0x0d);
我沒有看過linux編程的規范,也不知的修改內核有什么規矩,不過,我都是用預處理來添加我自己的代碼,從來不直接在原有的代碼上修改,我覺得這樣更可以保證代碼的完整性和可移植性,而且,還容易比較,容易找出問題(當然,如果#if嵌套多了,也很難看的:()。
接 下來的初始化8019,就沒有什么問題了,然后就是配置網卡的物理地址了。在我的系統上,沒有使用8019的初始化配置芯片,物理地址需要在程序中直接寫 入(其實,就是使用配置芯片,也需要用程序讀出再寫入的),物理地址可以編譯到代碼里,也可以存儲到flash的一個固定地址中。可以參考 ne_probe1里面的例子,照著勒就可以了。剩下注冊中斷什么的,也就是算好了中斷號,照著添加自己的代碼。很容易的。
到這里,似乎就沒有什么工作了。編譯內核,啟動,恩Ne2000兼容的網卡找到了,接下來就不正常了。系統報告,反復陷入那個網卡的中斷……
反復陷入中斷,很容易想到就是中斷模式配置的問題,8019的中斷是高電平有效,看看S3C44B0上的配置,果然不對。這個配置是在Bootloader中做好了的,改一下,就好了。我把他改成了上升沿觸發。
別人的批注:
最好改為高電平觸發,我就吃過這樣的苦頭,當時我那個驅動不太穩定,一旦有錯誤,就死活不工作,后來發現是8019的中斷線一直為高,顯示有中斷,但CPU不知什么原因,開始的時候沒有檢測到上升沿,因此以后就再也收不到中斷了,把觸發方式改了以后,就非常穩定了
另 外,因為S3C44B0是IO空間和存儲器統一編址。這就容易忽視一個問題,就是緩沖。對于存儲器,加上片內的緩沖可以提高效率,不過對于外部設備比如這 個8019,就不能使用緩沖。記住,緩沖的范圍一定要配置正確,我開始就弄錯了,產生了一些莫名其妙的問題,耽誤了不少時間。
上述問題都 解決了,啟動的時候可以找到網卡,可以配置好物理地址,啟動以后ifconfig eth0也沒有問題,這次應該沒有問題了吧。可是,結果還是ping通。這次就比較麻煩了,沒有內核跟蹤調試的手段,只能靠printk來輸出?不知道應 該從哪里入手了。不過還好,調試以太網,有Sniffer(一個功能強大的抓包軟件,在局域網內的數據包都是抓到)。就靠它了,在我的PC上運行,抓包。 在uClinux下ping主機的IP。果然能抓到數據包。分析抓取的數據包發現問題。
按理說,ping的時候,第一次不知道目的主機的 Mac地址,所以,應該發送ARP廣播,發送的數據大概的格式開頭應該是FF FF FF FF FF FF AA BB CC DD EE FF…………(AABB CC DD EE FF表示發送方的Mac地址),可是我抓到的數據包是FF FF FF FF FF FF AA AA BB BB CC CC DD DD EE EE FF FF…… 看明白了,這個問題應該是網卡發送的時候,向網卡寫入數據連續寫了兩次。這個問題最容易讓人想到是S3C44B0的掛8019的那個Bank的數據寬度配 置錯了。可是,我仔細的看了,不是這個問題。那就只有再仔細看看源碼了。還是在drivers/net/ne.c里面,ne_block_output函 數--這個就是8019發送時候調用的函數了,里面有代碼:
if (ei_status.word16) {
outsw(NE_BASE + NE_DATAPORT, (void*)buf, count>>1);
} else {
outsb(NE_BASE + NE_DATAPORT, (void*)buf, count);
}
我 跟蹤了一下,ei_status.word16=1,這個沒有問題。那么,問題就出在outsw函數上了。用SourceInsight一層層的跟蹤(做 一個函數右一個宏的,定義的可真多,好多不同模式或者處理器下的相同定義,要看清楚自己的),最后,終于把目標鎖定在了arch/armnommu /lib/ io-writesw-armv3.S和io-writesw-armv4.S兩個匯編文件。到底是哪個呢?
熟悉ARM家族 的人應該知道ARMv3和ARMv4的一些區別,看看這兩匯編,就可以開出來他們對16位數讀寫操作的不同,按照道理S3C44B0應該是ARMv4(我 記得應該是,不到出處了,至少看了那個兩匯編文件,我認定應該用ARMv4那個),可是,看了一下便一輸出的.o文件,是io-writesw- armv3.o,顯然弄錯了,這里就是問題了。那么為什么要編譯ARMv3而不是ARMv4這個文件呢?在Makefile和Config.in中經過一 番尋找,終于找,原來在定義arch/armnommu/config.in中,定義CONFIG_ARCH_S3C44B0的時候,沒有定義
CONFIG_CPU_32v4
那么,默認情況下,就定義CONFIG_CPU_32v3,用它來編譯。好了。把ARMv4的定義添上。順便把前面說的ARM_NE2000_BASE和ARM_NE2000_IRQ的定義以添加到這里,讓用戶可以自己定義裁剪。
hex 'Base Address for NE2000 ethernet' ARM_NE2000_BASE youraddr
hex 'IRQ for NE2000 ethernet' ARM_NE2000_IRQ yourinterrupt
好 了,編譯通過。運行,果然沒有問題了。Ping可以,telnet可以,在內核中把NFS打開,mount -t nfs ……也好用。哈哈。太好了。至此,8019在S344B0組成的uclinux平臺上的驅動,移植成功。相信其他的網卡芯片移植驅動程序應該也基本是這個 思路。現在寫出來與大家共享。希望對新手入門有所幫助,同時文章中有我理解錯誤的地方,也希望高手指教。
最后,再總結一下,移植過程中需要注意的幾個問題:
確定網卡的基地址、中斷無誤
注意網卡的數據總線寬度,地址是否連續,如果不連續,如何映射
注意網卡的中斷的模式和處理對應的外部中斷是不是一致
對于IO和RAM統一編址的處理器,注意緩沖區范圍的設置
注意ARMv3和ARMv4等一些和處理器結構相關的底層函數庫帶來的問題
用抓包軟件可以幫助分析定位問題所在
Btw, 我的PC平臺是在WindowsXP+Virtual PC下安裝的Red Hat linux 8.0,我覺得這樣調試起來比較方便,可以用SoureInsigh來閱讀,編寫代碼,可以在Linux編譯。充分發揮兩個操作系統的優勢。很適合于像我 這樣的,不熟悉Linux人開發。
總結
以上是生活随笔為你收集整理的uClinux下移植Ne2000兼容的网卡驱动程序(转)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何修改OpenWrt路由IP地址
- 下一篇: 要市场导向,不要销售导向,更不要个人导向