昨天把网卡的配置信息读进来了,利用配置信息中的class_code,我们找到了pc上的网卡设备。

昨天的教程:在自制操作系统上写网卡驱动(2): 网卡的I/O配置

既然找到了网卡设备,今天就可以来使用网卡了。

那么如何使用呢?

给网卡内部的寄存器写内容就可以控制网卡了。

控制网卡,就是使用网卡收发数据,触发中断等。

网卡内部的寄存器就是网卡的&34;遥控器&34;,网卡的&34;控制面板&34;,不同的寄存器就相当于遥控器上的按钮。

具体怎么做呢?

  1. 通过读取PCI配置中的基址寄存器base address register,BAR的值,来得到网卡内部的寄存器的地址net_card_base_addr,这就是网卡的内部寄存器的地址,往net_card_base_addr上写内容,就是往网卡内部的寄存器里写内容。
  2. 网卡内部都有啥寄存器?这个要看具体网卡的使用手册,就是根据网卡的型号找到对应的datasheet
  3. 在datasheet上,会详细描述各个寄存器的功能,通过设置这些寄存器的值,就可以完成数据的收发,是否触发CPU的中断等功能。

网卡的BAR就存在于PCI配置信息中,它是PCI配置信息的第4--9行,如下图所示:

PCI配置信息

第4--9行分别是BA0,BA1,BA2,BA3,BA4,BA5,一个6个BAR.

既然在配置信息内部了,仍然使用我们昨天的方法去读取配置信息即可使用代码:

通过以上代码,我们读取到了配置信息中的BA0,它的值显示出来为:

class_code=02时为网卡,其BAR为0xc101

我们查询到,网卡设备对应的bar为0x0000 c101.那么这个数值代表着什么呢?是怎样对应于网卡内部的“控制面板”呢?

BAR各bits的意思

先说结论:如上图bar=0x0000c101时,我们访问I/O端口 0xc100,就是访问网卡内部的寄存器。这个结论是如何得到的?

查看IO BAR Written With base Address字样下面的说明:

bit0为1,表示访问网卡内部的寄存器需要用CPU的I/O口;如果bit0为0,表示访问网卡内部的寄存器可以像访问内存那样,不用通过I/O口。

当bits0为1时,网卡内部寄存器对应于CPU的I/O的地址net_card_base_addr为:bar & 0xffff ff00

取bar的第8到31位作为net_card_base_addr的第8到31位,然后把net_card_base_addr的第0到7位设置为零即可。

所以,我们这里得到结论,只要访问I/O端口0xc100,就是访问网卡内部的寄存器。

具体的访问代码为:

这里为什么要用io_in8和io_out8,为什么要用8位的端口读写函数?不用16位的?不用32位的?

因为这款网卡的内部寄存器是8位的。

这是一款什么样的网卡呢?它的内部寄存器到底是怎样的呢?

我们要查询网卡的内部寄存器,首先要得到网卡的型号,怎么看呢?

通过PCI配置信息的第一行vonder_id和device_id。

比如我们这款网卡的vonder_id和device_id分别为:0x10ec和0x8029

经过搜索,在这里找到了具体解释:

10EC是Realtek公司

那么8029是什么?8029是芯片的型号,所以,连起来,就是Realtek 8029。

把&34;Realtek 8029&34;作为关键字搜索后,发现这款网卡的核心芯片就是 RTL8029芯片,

那么就可以去搜索RTL8029芯片的datasheet了。这个还是比较容易搜索到。

最终我找到了这个:

RTL8029AS的芯片手册。

虽然是8029AS,不是8029,但是估计也是8029芯片的升级版,应该跟8029操作起来差不多。这是总体介绍:

在这个芯片手册中,关于寄存器是这样说的:

8029AS的内部寄存器总览

表5.1.1. Register Table就是8029内部所有的寄存器了。我们要控制网卡收发信息,触发中断信号,就得给这些寄存器设置合适的值。

表中,每个寄存器的意义在datasheet中都有详细说明。

我们先来看一下如何访问表中的寄存器,比如访问某个单独的寄存器:CR寄存器。

先看表中的第1列No(Hex),它表示序号,CR寄存器的No为00,这说明通过0xc100+0x00就可以访问到CR寄存器了。

比如表中有个寄存器FIFO,它的No为06,这意味着通过0xc100+0x06就可以访问CR寄存器了。

但是注意到,这个表每一行的几个寄存器都同时对应着一个No,那么问题就来了,当我们in_io8(0xc100+0x06)时,具体读的是哪一个寄存器呢?

这个还要看CR寄存器内的值。如果CR寄存器的第8,7bits为00,我们此时执行的是读操作,也就是in_io8(0xc100+0x06),那么此时操作的就是FIFO,

如果此时执行的是写操作,那么此时操作寄存器就是TBCR1.

如果CR寄存器的第8,7bits为01,我们此时执行的是读操作,也就是in_io8(0xc100+0x06),那么此时操作的就是PAR5,如果此时执行的是写操作,那么此时操作寄存器就还是PAR5.

如果CR寄存器的第8,7bits为10,我们此时执行的是读操作,也就是in_io8(0xc100+0x06),那么此时操作的就是PAR5,如果此时执行的是写操作,那么此时操作寄存器就还是PAR5.

所以说:某个寄存器的方位方式,由这个寄存器所在的行头和列头的值共同决定。

比如PSTART,我们可以看其所在行01,其所在的列为:Page2-[R],那么要操作这个寄存器,要把CR寄存器的第8,7bit设置为2,即0x10,然后用io_in8(0xc100+0x01)来操作这个寄存器。

所以,我们在访问寄存器前,总是需要先设置一下CR寄存器,那么如何设置这个寄存器呢?

CR的行为00,CR存在于所以列,这意味着访问CR就只用io_in8(0xc100)和io_out8(0xc100,0x01)就可以了。

所以,想把CR寄存器的bit8,bit7的值设置为10时,可以这样操作:

由于CR寄存器的bit8,bit7对应着Page0,Page1,Page2,Page3,为了方便的操作各寄存器,我们写了一个改变page的函数page_select:

这样,当我们需要操作寄存器PSTART的时候,就可以

这就大大简化了代码的编写,当然还可以继续简化,比如

不过我们这里似乎没有必要做太复杂。太复杂了程序的可读性就变差了。如果以后有必要就再说吧。

到这里,我不仅感概,拿到这个datasheet真是太好了,终于可以实现对网卡的自由操作了。

先试试page_select韩式是否能够工作:先调用page_select(2),然后再查看0xc100处的值,看其bit8,bit7是否被设置为10.

设置CR

我们先调用page_select(2),然后再io_in8(base_addr)得到CR,然后把CR的值放到显示变量bar里进行显示。结果如下:

可以看到在表格的BAR列,最后一行的值为0x00000080,就是说CR的值为0x80,也就意味着CR的bit8,bit7位10.

那么page_select(3)之后是怎样的呢?

代码改为:

对应的结果为:0x000000c0.

这个结果说明我们对RTL8029网卡芯片内的寄存器CR设置成功了。

这就意味着,我们可以对RTL8029网卡芯片内的任何寄存器进行操作了。

这还意味着我们可以对不限于网卡,可以是显卡,也可以是其他卡比如显卡,比如声卡进行芯片内的寄存器设置了。

回到我们的RTL8029芯片。

是时候按照datasheet上的说明,来初始化网卡,获取网卡的mac地址,等操作了。

这些操作是数据接收和发送的基础步骤。

网卡的初始化init比较繁琐,需要设置一大堆的寄存器。

我先把代码放出来,然后再解释这些代码:

CR寄存器每一bit的作用

可以对照CR寄存器的详细解释,看0x21。 其中PS1,PS0=00,即page0.

RD2,RD1,RD0位100,即Abort/Complete remote DMA

然后TXP=0,即has no effect.

STA=0,这一位不控制任何事情,controls nothing.

STP=1,关闭网卡。即不再接收和发送任何的数据包。

PSTART设置了网卡内部用于接收数据的内存区域的开始地址。

PSTOP设置了网卡内部用于存储数据的内存区域的结束地址。

BNRY,接收的最后一个数据的地址。BNRY是boundary的缩写,表示接收数据的边界。

TPSR,发送数据的地址,它是Transmet Page Start Register的缩写。

这4个寄存器设置了收到数据的存放位置。

需要注意的是,这里的数据都是按page页为单位处理的。

每256个字节bytes为一页page.

所以PSTART=0x4C页,PSTOP=0x80页,那么这里一共包含0x80-0x4C=52页。

代码中设置了RCR=0xcc

这意味着MON=0,PRO=0,AM=1,AB=1,AR=0,SEP=0

MON=0, 数据校验关

PRO=0, MAC地址匹配的数据才接收

AM=1,接受有多个目的地的数据

AB=1,接受广播的数据

AR=0, 不接受少于64 bytes的数据

SEP=0,不接受有错误的数据。

这个寄存器果然如其名RCR,Receive Configuration Register,接收配置寄存器。用来配置哪些数据不接收,哪些数据接收。

那么再看一个寄存器TCR,发送数据的配置寄存器

0xe0意味着OFST=0,ATD=0,LB1=0,LB0=0,CRC=0,

OFST=0, 冲突偏移功能关闭

ATD=0,关闭自动transmitter

LB1=0,LB0=0,正常操作,没有Loopback

CRC=0,开启CRC校验

下一行代码对应的是DCR:

0xc8意味着:FT1=1,FT0=0,ARM=0,LS=1,LAS=0,BOS=0,WTS=0

FT1=1,FT0=0,FIFO的阈值设置寄存器的bit1和 bit0位

ARM=0, 发送没有被执行的包命令

LS=1,正常操作,不进行Loopback

LAS=0,16-位的DMA

BOS=0,MS byte placed on MD15-8,LS byte on MD7-0, 高位和低位的顺序

WTS=0: byte-wide DMA transfer ,DMA传输时的单位是byte还是word

DCR中的位,都是选择位。

下一行代码是是关于中断的寄存器:

IMR,与 ISR连用。每个bits对应一个中断interrupt.

既然与ISR有关了,我们就把ISR看了:

ISR配置在什么情况下触发中断的。

RST:reset或者接收数据满时触发中断

RDC: 远程DMA操作完成时,触发中断。

CNT:技术吻合时触发中断。

OVW: 接收数据太多,太快,数据缓冲区满时,触发中断

TXE:因为冲突造成发送数据取消,触发中断。

RXE:接收数据时,CRC错误,帧对齐错误,包丢失时触发中断

PTX:发送成功,产生中断

PRX:接收成功,产生中断

再往后的代码:

CUPR就相当于FIFO中P, 接收到下一个数据的时候,所存放的页地址。

关于FIFO可以看这里:30天自制操作系统day07:使鼠标指针可移动

MAR0-7:多播地址寄存器,过滤那些具有多地址的数据。

网卡的初始化工作终于完成了,可以读取网卡的MAC地址了,使用如下代码:

RSAR0,1,这两位设置了读取remote DMA的开始地址.

RBCR0,1, 这两位设置了从remote DMA读取多少个byte.

注意到,这个代码中,使用一个联合体数据结构:

还有,为什么丢掉一个byte,因为:

remote read时,存储在0x0000-0x000b里的网卡物理地址0x52544CC118CF是这样的:

525254544C4CC1C11818CFCF

这12字节把网卡地址重复存储了一次。

不过这样存储,单和双的地址存储的是一样的。

其实存储在0x000b后面的是生产厂商的代码和产品标识代码,也是单双地址重复存储。

我们就先不去读生成厂商的代码和产品表示了。

以上代码最后读取到的MAC地址结果为::

可以看到,读取到的Mac地址为54-52-12-00-56-34。

读取到了网卡的MAC地址,就可以在发送数据包的时候,加上这个地址了。

我们理使用网卡发送数据越来越近了。

今天利用配置信息中的Vendor_id=0x10ec和device_id=0x8029,我们对应了网卡的生产商为Realtek,网卡里芯片的具体型号为8029.

根据这写信息,我么找到了这款网卡芯片的操作手册datasheet.

芯片的操作手册就是芯片的使用说明了,里面详细地介绍了如何使用芯片里的寄存器来控制芯片接收数据,发送数据。一旦拿到芯片的操作手册,我们其实已经里操作网卡芯片发送数据很近了。

这里面涉及到两方面的知识:如果去操作网卡里的寄存器,以及网卡里都有哪些寄存器。

通过CPI配置信息中的BAR就可以获取到一个地址,通过这个地址,我们就可以用io_in8(地址)命令读取到网卡内的寄存器值。用io_out8(地址)往网卡的寄存器的值内写入内容,达到控制网卡的目的。

网卡的操作手册datasheet里有对网卡里的寄存器的详细说明。

我们参考着这份datasheet完成了对网卡打开,关闭,设置接收数据缓冲区,设置中断等初始化工作,并读取了属于这个网卡的mac地址。

今天的工作先到这里,后续就可以开始真正的使用网络传输协议来收发数据了。

记录一些写代码过程中的bug,以及debug的过程。

在读取网卡内部的寄存器时,一开始读取总是失败的,后来更改了地址变量的类型后

读取物理地址就成功了。

由unsigned char base_addr,

改为了unsigned int base_addr

就成功了。

写代码时,主要参考了RTL8019AS的--以太网协议:http://www.doczj.com/origin/doc/cb952888.html

这个代码让我把看到的一些介绍信息和具体的芯片联系起来,总算把datasheet看懂了个大概。

另外,对BAR的理解其实又用的知识挺多,但是我们这里只是用到了部分信息,我是通过一下地址以及图片学习了BAR的相关设置的:

一个介绍比较完整的资料是:http://www.pianshen.com/origin/article/40881826037/