原文地址: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人开发。

uClinux下移植Ne2000兼容的网卡驱动程序(转)相关推荐

  1. uClinux下移植Ne2000兼容的网卡驱动程序[转]

    原文链接: http://www.dzkf.cn/html/qianrushixitong/2006/0814/301.html 我是linux的新手,可以说从来没有在linux下写过程序,对于lin ...

  2. 在LINUX系统下安装RTL8111/8168网卡驱动程序

    1.下载RTL8111/8168的for LInux的驱动程序 http://www.zzgwu.com/driver/softdown.asp?softid=188260 2.解压缩 3.进入到sr ...

  3. S3C2440实现dm9000网卡驱动程序移植

    20150419 S3C2440实现dm9000网卡驱动程序移植 2015-04-19 Lover雪儿 首先附上厂家提供的完整的dm9000程序: 1 /* 2 3 dm9ks.c: Version ...

  4. linux网卡驱动 pdf,Linux下网卡驱动程序.pdf

    zekairecv 于 2015-10-04 00:58:57发表: 谢谢 weilee1 于 2015-04-19 17:41:05发表: 看看 雪语阑风 于 2014-12-04 11:03:39 ...

  5. linux网卡驱动开发视频,Linux下网卡驱动程序的开发.doc

    Linux下网卡驱动程序的开发 论文题目:Linux下网卡驱动程序的开发 专 业: 年 级: 学生学号: 学生姓名: 指导教师: 完成时间: Linux下网卡驱动程序的开发 八年经验 专业指导毕业设计 ...

  6. h16网卡linux驱动下载,【驱动】在LINUX(ubuntu)系统下安装RTL8111/8168网卡驱动程序(技嘉H61主板)...

    [驱动]在LINUX(ubuntu)系统下安装RTL8111/8168网卡驱动程序(技嘉H61主板) 4年前 (2017-07-15)    作者:Jiaozn    分类:Linux    阅读次数 ...

  7. 在ARMSYS(S3C44B0X开发板)上进行uClinux内核移植的总结

    标题 针对"如何在以S3C44B0X为核心的ARMSYS开发板上建立uClinux内核移植"的一个总结,其内容包括对Bootloader的功能分析和uClinux2.4.24发行版 ...

  8. linux网卡驱动程序的编译与安装,linux网卡驱动程序的编译与安装

    安装实例 linux网卡驱动程序的编译与安装 powered by KindGeorge 一般来说,目前新版的 Linux 预设可以支持的网络卡芯片组数量已经很完备了,很多网络卡芯片都已经被支持, 例 ...

  9. VMware 10.0上NetBSD-1.0的PCI网卡驱动程序

    之前在VMware 10.0上成功安装了NetBSD-1.0,虽然系统在每次重启的时候会弹出一个错误提示框,而且有时候系统启动还会提示"ffs vmalloc dup ..."这样 ...

最新文章

  1. 无人驾驶传感器融合技术
  2. 微信小程序的多选改变样式_微信小程序button选中改样式-实现单选/多选
  3. Python基础教程(五):数字、字符串
  4. 出道50年+!乘风破浪的编程语言们,能二次翻红吗?
  5. mysql ssh 导入时注意问题
  6. 【存储测试】vdbench存储性能测试工具
  7. blender 快捷键
  8. NLP数据预处理——同义词替换程序
  9. hashcat进行rar密码破解可gpu运算
  10. QGIS二次开发01---临时绘制图层实现
  11. python爬取相册_如何用python实现爬取微博相册所有图片 - 收获啦
  12. 什么是PaaS? 平台即服务的解释
  13. 脱壳笔记-手工脱FSG压缩壳
  14. Control.DataBinding数据绑定细解
  15. ❤Linux文件、目录与磁盘格式总结❤
  16. 跑步戴什么耳机比较好、精挑五款最佳跑步耳机推荐
  17. HDU1864 最大报销额 01背包
  18. Linux 中的 EOF 到底是什么?
  19. 在mysql数据库中如何导出数据库_MYSQL导出数据库的一些方法
  20. 中国民生银行 支付接口PHP DEMO

热门文章

  1. Kubernetes上的负载均衡详解
  2. 杭州人有福了!菜鸟配送升级新增24小时送药服务
  3. C++11 作用域内枚举
  4. Linux学习1——文件权限
  5. 排查访问Linux Server速度较慢的问题
  6. java中 8进制 10进制 2进制 16进制 相互转换
  7. 软件开发需要重视对异常的处理
  8. 部署App-V Client,应用程序虚拟化体验系列之三
  9. Oracle+BEA后的ESB
  10. 数据仓库、商业智能的体系结构