rtl8139_open(打开启动设备函数)
8139 有一个接收缓冲寄存器,用于存放接收缓存的首地址,网卡一边把网线上的发出
的数据放到内部FIFO,一边从FIFO 中把数据通过DMA 传送到由接收寄存器指定的内存
地址中,接收到的数据依次排放,当长度超过默认的缓冲区长度时,会回过头来放到开
始的地方,所以接收缓冲区被称为环形缓冲区。
8139 有四个发送地址寄存器,CPU 将要发送的数据在内存中的地址写入这四个寄存器
中的任何一个,网卡就会通过DMA 操作把数据发送出去。
当发送或者接送完成后,网卡会发出中断,中断处理程序通过读取网卡的中断状态寄存
器来识别出是发送完成发出的中断,接收到数据包的中断,还是错误中断。
当运行ifconfig ethx up 的时候,rtl8139_open 得到调用。该函数的任务就是分配和初始
化接收及发送缓冲区,分配中断号,挂接中断处理程序等。
当网卡发生中断(如数据到达)时,中断控制器8259A 把中断号发给CPU, CPU 根据这个
中断号找到处理程序,这里就是rtl8139_interrupt,然后执行。rtl8139_interrupt 也是在我们
的程序中定义 好了的,这是驱动程序的一个重要的义务,也是一个基本的功能。request_irq
的代码在arch/i386/kernel/irq.c 中。
① 注册这个设备的中断处理函数。当网卡发送数据完成或者接收到数据时,是用中断
的形式来告知的,比如有数据从网线传来,中断也通知了我们,那么必须要有一个
处理这个中断的函数来完成数据的接收。就是中断号的分配,和内存地址映射一样,
中断号也是BIOS 在初始化阶段分配并写入设备的配置空间的,然后Linux 在建立
pci_dev 时从配置空间读 出这个中断号然后写入pci_dev 的irq 成员中,所以我们注
册中断程序需要中断号就是直接从pci_dev 里取就可以了。
request_irq 的代码在arch/i386/kernel/irq.c 中
② 分配发送和接收的缓存空间
根据官方文档,发送一个数据包的过程是这样的:先从应用程序中把数据包拷贝到
一段连续的内存中(这段内存就是我们这里要分配的缓存),然后把这段内存的地
址写进网卡的数据发送地址寄存器(TSAD)中,这个寄存器的偏移量是TxAddr0 =
0x20。在把这个数据包的长度写进另一个寄存器(TSD)中,它的偏移量是TxStatus0
= 0x10。然后就把这段内存的数据发送到网卡内部的发送缓冲中(FIFO),最后由这个
发送缓冲区把数据发送到网线上。
tx_bufs 是发送缓冲内存的首地址,rx_ring 是接收缓存内存的首地址,他们都是虚
拟地址,而最后 一个参数tx_bufs_dma 和rx_ring_dma 均是这一段内存的物理地址。
为什么同一个事物,既用虚拟地址来表示它还要用物理地址呢,是这样 的,CPU
执行程序用到这个地址时,用虚拟地址,而网卡设备向这些内存中存取数据时用的
是物理地址( 因为网卡相对CPU 属于头脑比较简单型的)。
pci_alloc_consistent 的代码在Linux/arch/i386/kernel/pci‐dma.c
中。
③发送和接收缓冲区初始化和网卡开始工作的操作
RTL8139 有4 个发送描述符(包括4 个发送缓冲区的基地址寄存器(TSAD0‐TSAD3)
和4 个发送状态寄存器(TSD0‐TSD3)。也就是说我们分配的缓冲区要分成四个等分
并把这四个空间的地址都写到相关寄存器里去,下面这段代码完成了这个操作。
for (i = 0; i < NUM_TX_DESC; i++)
((struct rtl8139_private*)dev‐>priv)‐>tx_buf[i] =
&((struct rtl8139_private*)dev‐>priv)‐>tx_bufs[i * TX_BUF_SIZE];
上面这段代码负责把发送缓冲区虚拟空间进行了分割。
for (i = 0; i < NUM_TX_DESC; i++)
{
writel(tp‐>tx_bufs_dma+(tp‐>tx_buf[i]tp‐>tx_bufs),ioaddr+TxAddr0+(i*4));
readl(ioad