RTL8139的硬件接口由256个字节寄存器组成,这些寄存器按可读性分为只读和可读写两类;按实现收发功能分为发送相关的和接收相关的;按实现收发原理的角色分为,配置寄存器、命令寄存器(或者叫控制寄存器)和状态寄存器;没有数据寄存器,因为RTL8139是PCI设备,使用了一片DMAble的缓冲区作“数据寄存器”。
本文简要地介绍一下RTL8139收发原理,和实现原理的基本寄存器,其它特色功能寄存器请查阅手册。本文先按实现收包原理、发包原理和全局原理,三类精要地介绍相关实现的寄存器。最后简述收包发包过程。
1.寄存器类型
- CMD:命令类型寄存器
- CFG:配置类型寄存器
- STA:状态类型寄存器
“命令”和“配置”的操作语义需要区分,用户发命令,管理员配置,因此,像软重置、开启发送功能等被一贯认为是命令操作,个人认为划归为配置类比较确切。判别“命令”和“配置”的一条简单的方法就是看这些操作是用在设备驱动哪个函数上 ,例如,开启发送功能的(CmdTxEnb)操作是在初始化函数里,所以CmdTxEnb是配置操作,而非命令操作。另一种方法是寄存器(对于主机CPU)的可读写性,STA必定是只读的,CFG必定是只写的,而CMD是可读可写的。
2. 全局原理
2.1 命令寄存器(CR)
Command Register (Offset 0037h, R/W, CMD) /* CR register commands */ #define RxBufEmpty 0x01 //STA | 收包缓冲区是否空 #define CmdTxEnb 0x04 //CFG | 激活发送状态机 #define CmdRxEnb 0x08 //CFG | 激活接包状态机 #define CmdReset 0x10 //CFG | 软件重置RTL8139
2.2 中断状态寄存器(ISR)
当RTL8139运行中出现各种事件需要报告给主机CPU,RTL8139置相应中断状态位,并发出中断请求信号。当然信号是否能发出,还取决于中断屏蔽的配置。
Interrupt Status Register (Offset 003Eh-003Fh, R/W, STA) /* ISR Bits */ #define RxOK 0x01 #define RxErr 0x02 #define TxOK 0x04 #define TxErr 0x08 #define RxOverFlow 0x10 #define RxUnderrun 0x20 #define RxFIFOOver 0x40 #define CableLen 0x2000 #define TimeOut 0x4000 #define SysErr 0x8000
2.3 中断屏蔽寄存器(IMR)
Interrupt Mask Register (Offset 003Ch-003Dh, R/W, CFG) PciErr = (1 << 15), /* System error on the PCI bus */ TimerIntr = (1 << 14), /* Asserted when TCTR reaches TimerInt value */ LenChg = (1 << 13), /* Cable length change */ SWInt = (1 << 8), /* Software-requested interrupt */ TxEmpty = (1 << 7), /* No Tx descriptors available */ RxFIFOOvr = (1 << 6), /* Rx FIFO Overflow */ LinkChg = (1 << 5), /* Packet underrun, or link change */ RxEmpty = (1 << 4), /* No Rx descriptors available */ TxErr = (1 << 3), /* Tx error */ TxOK = (1 << 2), /* Tx packet sent */ RxErr = (1 << 1), /* Rx error */ RxOK = (1 << 0), /* Rx packet received */
3.发包原理
RTL8139被设计使用四块DMAble的缓冲区作“发包数据寄存器”,保存驱动程序发来的数据包。这些缓冲区的地址被配置到RTL8139称为发送描述符(Transmission Descriptors or TD)的寄存器上,共四个,如下图。实际上TD由两个单独的寄存器组成,TSAD保存缓冲区地址,TSD是发送状态,下面会详细介绍。

发包大致过程是,驱动程序首先将数据包拷进一块发送缓冲区,然后将发送状态数据(数据包大小、发送阈值和PCI操作命令)写入缓冲区对应的TSD来启动数据发送;当RTL8139检测到发送命令后[注],RTL8139通过PCI的DMA操作将该数据包读进内部的[Transmit FIFO]。[Transmit FIFO]是芯片内部的2K缓冲区,当[Transmit FIFO]达到由TSD配置的阈值(early transmit threshold)或已将整个数据包拷入时,数据包开始被发到网线上。
注:发包原理中目前存有疑点,在手册上找不到说法,就是是主机主动通过TSD.OWN命令8139执行发送,还是TSD.OWN只是被动的命令配置位,8139循环检测TSD.OWN(四个),自动执行发送操作。目前的理解倾向于后者,因为如果前者的话,发送操作是主机与设备同步的,显然没必要设计四块缓冲区。如果是后者,那么“命令”与“配置”的语义界限变得模糊。这里用我的假设——8139循环检测命令位自动执行发送操作——来描述发包原理,有待进一步确认。
四个TD以循环制(round-robin)的方式使用。驱动程序必须使用两个全局变量——write_buff(记录最新可用缓冲区号)和read_buff(记录最后发送缓冲区号)——来协调循环制的方式使用。驱动程序将数据包拷进[最新可用]的一块发送缓冲区后,更新write_buff,如果write_buff等于read_buff,证明缓冲区已满,必须通知协议栈停止发送;当8139发送完一个数据包后发出“发送完中断请求”,中断处理必须更新read_buff,并唤醒发送队列 。

3.1 发送状态寄存器(TSD)
这是一个非常重要的寄存器,手册很多描述发送原理细节的地方使用了[发送状态描述符(Transmit Status Descriptors or TSD)]概念,但标准标定义却是TSR,看来RTL8139的设计师不只一位,并且没有沟通好。不管名字是什么,反正它就是一四字节寄存器。TSD很混杂,兼有状态、配置和命令的功能,当然只是针对单次发送的。
Transmit Status Register (TSD0-3)(Offset 0010h-001Fh, R/W, STA|CFG|CMD ) //STA Bit-14: TxUnderrun Bit-15: TxStatOK Bit-29: TxOutOfWindow Bit-30: TxAborted Bit-31: TxCarrierLost //CFG Bit-0~12: Transmit Byte Count Bit-16~21:threshold level in the Tx FIFO //CMD Bit-13: TxHostOwns
3.2 发送起始地址寄存器(TSAD)
这就是一个32位地址指针,指向一PCI设备能DAM访问的内存区,名曰发包缓冲区。而这一段缓冲区正是设备——建立在RTL8139硬件之上逻辑网络设备的“数据寄存器”。TSAD(四个)只在设备驱动的初始化代码配置好,一般不会再改写,证明了这一点。
Transmit Start Address of Descriptors (TSAD0-3)(Offset 0020h-002Fh, R/W, CFG )
3.3 发送配置寄存器(TCR)
配置8139的发送功能,如错误重发、两帧间的时间间隙、添加CRC头和DMA突发访问的数据大小等。
Transmit Configuration Register
(Offset 0040h-0043h, R/W)
/* Bits in TxConfig. */
enum tx_config_bits {
/* Interframe Gap Time. Only TxIFG96 doesn't violate IEEE 802.3 */
TxIFGShift = 24,
TxIFG84 = (0 << TxIFGShift), /* 8.4us / 840ns (10 / 100Mbps) */
TxIFG88 = (1 << TxIFGShift), /* 8.8us / 880ns (10 / 100Mbps) */
TxIFG92 = (2 << TxIFGShift), /* 9.2us / 920ns (10 / 100Mbps) */
TxIFG96 = (3 << TxIFGShift), /* 9.6us / 960ns (10 / 100Mbps) */
TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */
TxCRC = (1 << 16), /* DISABLE Tx pkt CRC append */
TxClearAbt = (1 << 0), /* Clear abort (WO) */
TxDMAShift = 8, /* DMA burst value (0-7) is shifted X many bits */
TxRetryShift = 4, /* TXRR value (0-15) is shifted X many bits */
TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */
};
4. 收包原理
RTL8139被设计成使用环形缓冲区(ring buffer)作为“收包数据寄存器”来接收数据包。环形缓冲区也必须是DMAble,也就是它是一段物理上连续的内存。RTL8139内置了三个寄存器——RBSTART、CAPR和CBA——实现与主机协作读写环形缓冲区来传递数据。RBSTART是缓冲区的首地址,外部数据包首先通过网线进入RTL8139内部的 Receive FIFO,然后当数据量达到预设的阀值,RTL8139将数据通过PCI拷到缓冲区CBA地址处,完了更新CBA;驱动程序的收包中断则从CAPR地址处读取数据,完了后更CAPR。所以,寄存器CBA(Current Buffer Address)是环形缓冲区的写指针,寄存器CAPR(Current Address of Packet Read)是环形缓冲区的读指针。整个过程是由RTL8139主导的,它负责缓冲区溢出管理。

4.1 接收状态寄存器
这不是一个寄存器,而是对[预-接收状态]的一个备份,附加在数据包的头,给驱动程序接收,以便后续处理。
Receive Status Register in Rx Packet Header
enum RxStatusBits {
RxMulticast = 0x8000,
RxPhysical = 0x4000,
RxBroadcast = 0x2000,
RxBadSymbol = 0x0020,
RxRunt = 0x0010,
RxTooLong = 0x0008,
RxCRCErr = 0x0004,
RxBadAlign = 0x0002,
RxStatusOK = 0x0001,
};
4.2 预-接收状态寄存器(ERSR)
Early Rx Status Register (Offset 0036h, R, STA)
4.3 接收配置寄存器(RCR)
Receive Configuration Register
(Offset 0044h-0047h, R/W, CFG)
//接收模式配置位
enum rx_mode_bits {
AcceptErr = 0x20,
AcceptRunt = 0x10,
AcceptBroadcast = 0x08,
AcceptMulticast = 0x04,
AcceptMyPhys = 0x02,
AcceptAllPhys = 0x01,
};
//FIFO阀值、DMA burst大小和环缓冲长度的[配置值]
enum RxConfigBits {
/* rx fifo threshold */
RxCfgFIFOShift = 13,
RxCfgFIFONone = (7 << RxCfgFIFOShift),
/* Max DMA burst */
RxCfgDMAShift = 8,
RxCfgDMAUnlimited = (7 << RxCfgDMAShift),
/* rx ring buffer length */
RxCfgRcv8K = 0,
RxCfgRcv16K = (1 << 11),
RxCfgRcv32K = (1 << 12),
RxCfgRcv64K = (1 << 11) | (1 << 12),
/* Disable packet wrap at end of Rx buffer. (not possible with 64k) */
RxNoWrap = (1 << 7),
};
5.发包过程
此过程假设已经配置好发包缓冲区。数据包的发送过程:
- 驱动程序将数据包拷到四块缓冲区中空闲的一块上;
- 驱动程序写入TSD,包括数据包大小、发送阈值(early transmit threshold)和PCI操作命令(清除OWN位);
- RTL8139通过PCI读取数据,在读取过程中,如果RTL8139检测到FIFO达到了阈值,或者发现已经读取了整个数据包,RTL8139开始将FIFO上的数据发到网线上;
- 当RTL8139将缓冲区上的数据包整个拷进了FIFO后,RTL8139置OWN为1;
- 当RTL8139将整个数据包发到网线上后,RTL8139置ISR.TOK为1,发出“发送完成中断”,如果IMR.TOK 是1,中断请求才成功发出;
- 驱动程序的中断处理函数被调用,处理函数过程中必须清除ISR.TOK。
发送过程的状态转换图 (ISR.TOK,ISR.OWN):
6.收包过程
此过程假设已经配置好收包缓冲区。数据包的接收过程:
- RTL8139启动后开始监测网线上数据;
- 如果有数据包,外部数据包首先通过网线进入芯片内部的 Receive FIFO;
- 当数据量达到预设的阀值,RTL8139将数据通过PCI拷到环形缓冲区;
- 当整个数据包都拷缓冲区后,RTL8139会在数据包的前面写数据头(包括接收状态和包长度信息),然后更新缓冲区的写指针CBA;
- RTL8139置CR.BUFE为1,收包缓冲不为空;然后置ISR.ROK,发出收包中断;
- RTL8139计算收包缓冲大小(比对CAPR和CBA),如果太小(如<3K),对外发出暂停控制帧,否则回到第一步;
- 驱动程序的中断处理函数被调用,处理收包,完了后清除ISR.ROK,还有更新CAPR。

