+ -
当前位置:首页 → 问答吧 → RTL8139网卡驱动程序分析[转]

RTL8139网卡驱动程序分析[转]

时间:2009-03-30

来源:互联网

在xsfocus上看到一篇分析rtl8139网卡驱动分析的文章。特地转过来,供大家学习和讨论。
原文链接: http://www.xfocus.net/articles/200707/931.html

创建时间:2007-07-27 更新时间:2007-07-28
文章属性:原创
文章提交:Addylee (Addylee2004_at_163.com)

本文以Linux内核中8139网卡驱动为例,对驱动程序的工作过程进行详细的分析,为初学者拨开迷雾,走出雾里看花的迷茫。本文虽然以Linux驱动为例,但是技术总是相通的,为了给Windows驱动初学者同样的启发,我有意的借用了许多Windows驱动中的名词,同时顺便略述了Windows驱动中的一些容易让初学者感到迷惑的概念。

    根据sinister的建议,在接收部分加入了对NAPI和非NAPI方式的分析。 在此对sinister大虾表示感谢!

-----------------
后记:
      多谢accessory兄的建议以及提供两个关于介绍RTL8139的链接,这里放在1楼,方便大家的参考。
(1)  RTL8139
http://wiki.osdev.org/RTL8139
(2) Linux下Rtl8139too网卡设备驱动程序关键函数剖析
http://www.xxlinux.com/linux/e/DoPrint/?classid=13&id=11702

[ 本帖最后由 Godbach 于 2009-5-6 09:47 编辑 ]

RTL 8139网卡驱动分析.pdf (408.23 KB)

下载次数:4431

2009-03-30 10:14

作者: Godbach   发布时间: 2009-03-30

收藏了,多谢!

作者: wuasiam   发布时间: 2009-03-30



QUOTE:
原帖由 wuasiam 于 2009-3-30 13:24 发表
收藏了,多谢!



这个驱动分析的应该是咱们常用的那种台式机的网卡。如果有条件了,可以手动的调试一下这个驱动程序,可以加深对网卡驱动的理解。

作者: Godbach   发布时间: 2009-03-30



QUOTE:
原帖由 Godbach 于 2009-3-30 13:32 发表


这个驱动分析的应该是咱们常用的那种台式机的网卡。如果有条件了,可以手动的调试一下这个驱动程序,可以加深对网卡驱动的理解。



3ks,谢谢共享

作者: Minit   发布时间: 2009-03-30

顶一个。

作者: scutan   发布时间: 2009-03-30



QUOTE:
原帖由 scutan 于 2009-3-30 13:54 发表
顶一个。



找一个rtl 8139的网卡,插到PC上,估计就可以调试驱动程序了。有时间时尝试一下。

作者: Godbach   发布时间: 2009-03-30

= = 不用~  开个虚拟机就行了~

我现在QEMU用的也是RTL8139~

作者: superfight   发布时间: 2009-03-30



QUOTE:
原帖由 superfight 于 2009-3-30 16:02 发表
= = 不用~  开个虚拟机就行了~

我现在QEMU用的也是RTL8139~



你的意思把虚拟的网卡配置成使用RTL8139驱动的?

作者: Godbach   发布时间: 2009-03-30



QUOTE:
原帖由 Godbach 于 2009-3-30 16:10 发表


你的意思把虚拟的网卡配置成使用RTL8139驱动的?



应该说是QEMU提供了虚拟的RTL8139网卡吧~ = 3=

作者: superfight   发布时间: 2009-03-30



QUOTE:
原帖由 superfight 于 2009-3-30 19:01 发表


应该说是QEMU提供了虚拟的RTL8139网卡吧~ = 3=



恩,就是这个意思。不过我这里已经装了vm-tools,好像没法修改虚拟网卡了。

作者: Godbach   发布时间: 2009-03-30

http://www.linuxforum.net/forum/ ... rt=1&PHPSESSID=
这篇8139驱动是我的入门文章

作者: duanius   发布时间: 2009-03-30



QUOTE:
原帖由 duanius 于 2009-3-30 20:23 发表
http://www.linuxforum.net/forum/ ... rt=1&PHPSESSID=
这篇8139驱动是我的入门文章


多谢共享。

作者: scutan:   发布时间: 2009-03-30



QUOTE:
原帖由 duanius 于 2009-3-30 20:23 发表
http://www.linuxforum.net/forum/ ... rt=1&PHPSESSID=
这篇8139驱动是我的入门文章



不错啊。结合着学习一下。

作者: scutan   发布时间: 2009-03-30

源代码应该是内核中的8139too.c

作者: Godbach   发布时间: 2009-03-30

很好,很有用,谢谢分享

作者: Godbach   发布时间: 2009-03-31

duanius的这篇文章也很不错啊,不但讲了网卡驱动
还讲了一些pci的基本知识
学习了

作者: peimichael   发布时间: 2009-03-31

xiexie

作者: peimichael   发布时间: 2009-03-31

看看了。

作者: cheran   发布时间: 2009-04-03

驱动程序的学习方法实什么呢?大家能讨论一下吗?

作者: jhjx704   发布时间: 2009-04-06

好东西!
ldd3有一章实现了一个网卡驱动挺不错的,不用了解硬件的寄存器之类的东西。
还有'lo'接口的实现代码看看对于学习网卡驱动很有帮助

作者: jaminwm   发布时间: 2009-04-06

独孤求真,是独孤九剑的什么什么关系?

作者: hauto   发布时间: 2009-04-06



QUOTE:
原帖由 dreamice 于 2009-4-6 22:49 发表
独孤求真,是独孤九剑的什么什么关系?



姓氏一样

作者: dreamice   发布时间: 2009-04-07

楼主的签名相当之震撼~~~

作者: Godbach   发布时间: 2009-04-07

其他都好,就是错别字太多啦。

作者: yscholly   发布时间: 2009-04-08

RTL8139的网卡芯片用的是什么型号,网上就找到了RTL8139C(L)的datasheet

[ 本帖最后由 Godbach 于 2009-4-13 19:57 编辑 ]

作者: xdsnet   发布时间: 2009-04-13

网上搜索rtl8139C的datasheet,http://www.alldatasheet.com/view.jsp?Searchword=RTL8139C,有两个PDF文档,一个是RTL8100B,一个是RTL8139C。这两个可能是一个是一个是01年底,一个是02年初。

而RTL8100B中PID初始化为0x8139,而RTL8139C中初始化为0x8129。我本机读取的config中PID为0x8139. 看来应该参考第一个文档。

作者: Godbach   发布时间: 2009-04-13

网卡驱动中通常要读取设备的相关信息,由于网卡是标准的PCI设备,其前64bytes格式是固定的。配置信息也就是从前64bytes的空间去读取。
譬如,8139too.c中要读取网卡的Rev_ID,可以通过如下方式:
        pci_read_config_byte(pdev, PCI_REVISION_ID, &pci_rev);
其中 PCI_REVISION_ID即Rev_ID在配置空间中的偏移。
跟踪了一下这个函数的执行,调用的宏为:


QUOTE:
#define PCI_OP_READ(size,type,len) \
int pci_bus_read_config_##size \
        (struct pci_bus *bus, unsigned int devfn, int pos, type *value)        \
{                                                                        \
        int res;                                                        \
        unsigned long flags;                                                \
        u32 data = 0;                                                        \
        if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER;        \
        spin_lock_irqsave(&pci_lock, flags);                                \
        res = bus->ops->read(bus, devfn, pos, len, &data);                \
        *value = (type)data;                                                \
        spin_unlock_irqrestore(&pci_lock, flags);                        \
        return res;                                                        \
}


其中size替换为byte即可。

这里可以看出,真正配置信息是从这行代码中读取的:


QUOTE:
        res = bus->ops->read(bus, devfn, pos, len, &data);                \



bus是struct pci_bus类型的。各位有谁知道ops成员什么时候被赋值的。里面应该定义的是配置空间的读写函数。

作者: Godbach   发布时间: 2009-04-14

在arch/i386/pci/direct.c中定义了访问PCI配置空间的方法,这个直接对端口进行读写,应该是比较低层的实现
  1. /*
  2. * direct.c - Low-level direct PCI config space access
  3. */

  4. #include <linux/pci.h>
  5. #include <linux/init.h>
  6. #include <linux/dmi.h>
  7. #include "pci.h"

  8. /*
  9. * Functions for accessing PCI configuration space with type 1 accesses
  10. */

  11. #define PCI_CONF1_ADDRESS(bus, devfn, reg) \
  12.         (0x80000000 | (bus << 16) | (devfn << 8) | (reg & ~3))

  13. int pci_conf1_read(unsigned int seg, unsigned int bus,
  14.                           unsigned int devfn, int reg, int len, u32 *value)
  15. {
  16.         unsigned long flags;

  17.         if ((bus > 255) || (devfn > 255) || (reg > 255)) {
  18.                 *value = -1;
  19.                 return -EINVAL;
  20.         }

  21.         spin_lock_irqsave(&pci_config_lock, flags);

  22.         outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8);

  23.         switch (len) {
  24.         case 1:
  25.                 *value = inb(0xCFC + (reg & 3));
  26.                 break;
  27.         case 2:
  28.                 *value = inw(0xCFC + (reg & 2));
  29.                 break;
  30.         case 4:
  31.                 *value = inl(0xCFC);
  32.                 break;
  33.         }

  34.         spin_unlock_irqrestore(&pci_config_lock, flags);

  35.         return 0;
  36. }

  37. int pci_conf1_write(unsigned int seg, unsigned int bus,
  38.                            unsigned int devfn, int reg, int len, u32 value)
  39. {
  40.         unsigned long flags;

  41.         if ((bus > 255) || (devfn > 255) || (reg > 255))
  42.                 return -EINVAL;

  43.         spin_lock_irqsave(&pci_config_lock, flags);

  44.         outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8);

  45.         switch (len) {
  46.         case 1:
  47.                 outb((u8)value, 0xCFC + (reg & 3));
  48.                 break;
  49.         case 2:
  50.                 outw((u16)value, 0xCFC + (reg & 2));
  51.                 break;
  52.         case 4:
  53.                 outl((u32)value, 0xCFC);
  54.                 break;
  55.         }

  56.         spin_unlock_irqrestore(&pci_config_lock, flags);

  57.         return 0;
  58. }

  59. #undef PCI_CONF1_ADDRESS

  60. struct pci_raw_ops pci_direct_conf1 = {
  61.         .read =                pci_conf1_read,
  62.         .write =        pci_conf1_write,
  63. };


  64. /*
  65. * Functions for accessing PCI configuration space with type 2 accesses
  66. */

  67. #define PCI_CONF2_ADDRESS(dev, reg)        (u16)(0xC000 | (dev << 8) | reg)

  68. static int pci_conf2_read(unsigned int seg, unsigned int bus,
  69.                           unsigned int devfn, int reg, int len, u32 *value)
  70. {
  71.         unsigned long flags;
  72.         int dev, fn;

  73.         if ((bus > 255) || (devfn > 255) || (reg > 255)) {
  74.                 *value = -1;
  75.                 return -EINVAL;
  76.         }

  77.         dev = PCI_SLOT(devfn);
  78.         fn = PCI_FUNC(devfn);

  79.         if (dev & 0x10)
  80.                 return PCIBIOS_DEVICE_NOT_FOUND;

  81.         spin_lock_irqsave(&pci_config_lock, flags);

  82.         outb((u8)(0xF0 | (fn << 1)), 0xCF8);
  83.         outb((u8)bus, 0xCFA);

  84.         switch (len) {
  85.         case 1:
  86.                 *value = inb(PCI_CONF2_ADDRESS(dev, reg));
  87.                 break;
  88.         case 2:
  89.                 *value = inw(PCI_CONF2_ADDRESS(dev, reg));
  90.                 break;
  91.         case 4:
  92.                 *value = inl(PCI_CONF2_ADDRESS(dev, reg));
  93.                 break;
  94.         }

  95.         outb(0, 0xCF8);

  96.         spin_unlock_irqrestore(&pci_config_lock, flags);

  97.         return 0;
  98. }

  99. static int pci_conf2_write(unsigned int seg, unsigned int bus,
  100.                            unsigned int devfn, int reg, int len, u32 value)
  101. {
  102.         unsigned long flags;
  103.         int dev, fn;

  104.         if ((bus > 255) || (devfn > 255) || (reg > 255))
  105.                 return -EINVAL;

  106.         dev = PCI_SLOT(devfn);
  107.         fn = PCI_FUNC(devfn);

  108.         if (dev & 0x10)
  109.                 return PCIBIOS_DEVICE_NOT_FOUND;

  110.         spin_lock_irqsave(&pci_config_lock, flags);

  111.         outb((u8)(0xF0 | (fn << 1)), 0xCF8);
  112.         outb((u8)bus, 0xCFA);

  113.         switch (len) {
  114.         case 1:
  115.                 outb((u8)value, PCI_CONF2_ADDRESS(dev, reg));
  116.                 break;
  117.         case 2:
  118.                 outw((u16)value, PCI_CONF2_ADDRESS(dev, reg));
  119.                 break;
  120.         case 4:
  121.                 outl((u32)value, PCI_CONF2_ADDRESS(dev, reg));
  122.                 break;
  123.         }

  124.         outb(0, 0xCF8);   

  125.         spin_unlock_irqrestore(&pci_config_lock, flags);

  126.         return 0;
  127. }

  128. #undef PCI_CONF2_ADDRESS

  129. static struct pci_raw_ops pci_direct_conf2 = {
  130.         .read =                pci_conf2_read,
  131.         .write =        pci_conf2_write,
  132. };
复制代码

作者: Godbach   发布时间: 2009-04-14

好东西啊,下来学习学习!!

作者: Godbach   发布时间: 2009-04-15

读了一下本机RTL8139网卡的配置信息:


QUOTE:
[root@localhost 0000:00:0e.0]# od -x config
0000000 10ec 8139 0107 0290 0010 0200 4000 0000
0000020 c801 0000 b800 bfff 0000 0000 0000 0000
0000040 0000 0000 0000 0000 0000 0000 10ec 8139
0000060 0000 0000 0050 0000 0000 0000 010a 4020
0000100 0000 0000 0000 0000 0000 0000 0000 0000
0000120 0001 f7c2 0000 0000 0000 0000 0000 0000
0000140 0000 0000 0000 0000 0000 0000 0000 0000
*
0000400


不知道最前面的值代表什么,如果是字节书的话,MS也不连续啊

作者: fyx2008   发布时间: 2009-04-15

热门下载

更多