②分配用于发送和接收的缓冲区空间
发送数据包的过程是: 首先将数据包从应用程序复制到连续的内存(此内存是我们要在此处分配的缓冲区),然后将该内存的地址写入网络. 卡在数据传输地址寄存器(TSAD)中,该寄存器的偏移量为TxAddr0 = 0x20. 然后将该数据包的长度写入另一个寄存器(TSD),其偏移量为TxStatus0 = 0x10. 然后,该内存中的数据将发送到网卡的内部缓冲区(FIFO),最后,该发送缓冲区将数据发送到网络线路.
tp-> tx_bufs = pci_alloc_consistent(tp-> pci_dev,TX_BUF_TOT_LEN和tp-> tx_bufs_dma);
tp-> rx_ring = pci_alloc_consistent(tp-> pci_dev,RX_BUF_TOT_LEN和tp-> rx_ring_dma);
tp是指向net_device的priv的指针,tx_bufs是发送缓冲存储器的第一个地址,rx_ring是接收缓冲存储器的第一个地址,它们都是虚拟地址,最后一个参数tx_bufs_dma和rx_ring_dma是该内存的物理地址. 当CPU执行程序时,它使用虚拟地址. 网卡设备使用物理地址访问这些内存中的数据. pci_alloc_consistent的代码在Linux / arch / i386 / kernel / pci-dma.c中.
③发送和接收缓冲区初始化以及网卡操作
RTL8139具有4个发送描述符(包括4个发送缓冲区的基地址寄存器(TSAD0-TSAD3))和4个发送状态寄存器(TSD0-TSD3). 换句话说,我们分配的缓冲区被分为四个Divide,并将这四个空间的地址写入相关的寄存器. 以下代码完成了此操作.
对于(i = 0;我 (((structrtl8139_private *)dev-> priv)-> tx_buf [i] = &(((structrtl8139_private *)dev-> priv)-> tx_bufs [i * TX_BUF_SIZE]; 上面的代码分割了发送缓冲区的虚拟空间. 表示(i = 0;我 ritel(tp-> tx_bufs_dma +(tp-> tx_buf [i] tp-> tx_bufs),ioaddr + TxAddr0 +(i * 4)); readl(ioaddr + TxAddr0 +(i * 4)); } 上面的代码划分了发送缓冲区的物理空间,并将其写入相关的寄存器中,以便网卡开始工作后,可以快速定位并找到这些内存并访问它们的数据. writel(tp-> rx_ring_dma,ioaddr + RxBuf); 上面的代码行将接收缓冲区的物理地址写入相关的寄存器中,以便网卡在接收到数据之后可以将数据从网卡准确地移动到这些存储空间,等待CPU占用他们. writeb(((readb(ioaddr + ChipCmd)&ChipCmdClear)| CmdRxEnb | CmdTxEnb,ioaddr + ChipCmd); 重置设备后,我们需要激活设备的发送和接收功能. 上面的代码行是将相应的值写入相关的寄存器,从而激活设备的这些功能. writel((TX_DMA_BURST << TxDMAShift),ioaddr + TxConfig); 上面的代码行是将TX_DMA_BURST << TxDMAShift写入网卡的TxConfig(0x44移位)寄存器,它转换为6 << 8,这意味着第八到第十个位置是110. 文档6是110,表示一个DMA的数据量为1024字节. 此外,在此阶段,已设置用于接收数据的模式,启用中断等. 数据发送和接收当网络应用程序将数据发送到网络时,它需要使用Linux网络协议堆栈来解决一系列问题,找到代表网卡设备的net_device,并使用此结构来查找和控制网卡设备完成数据包的发送专门是对net_device的hard_start_xmit成员函数的调用. 这是一个函数指针. 在我们的驱动程序中,它指向rtl8139_start_xmit. 它进行发送. 它有四件事. ①检查要发送的数据包的长度. 如果未达到以太网帧的长度,则必须将其填充. if(skb-> len if(((skb-> data + ETH_ZLEN)<= skb-> end){ 内存集(skb->数据+ skb-> len,0x20,(ETH_ZLEN-skb-> len)); skb-> len =(skb-> len> = ETH_ZLEN)? skb-> len: ETH_ZLEN;} 其他{ printk(“%s: (skb->数据+ ETH_ZLEN)> skb-> end \ n”,__功能__); } } skb->数据和skb->结尾确定此软件包的内容. 如果此软件包的总长度(skb-> end-skb->数据)不符合要求,则无处可填充. 如果已填充,它将返回错误,否则将被填充. ②将数据包的数据复制到我们已建立的发送缓冲区中. memcpy(tp-> tx_buf [entry],skb->数据,skb-> len); 其中skb-> data是数据包数据的地址,而tp-> tx_buf [entry]是我们的发送缓冲区地址,因此复制完成. ③通过写传输状态寄存器(TSD),使网卡知道此包的长度. writel(tp-> tx_flag |(skb-> len> = ETH_ZLEN?skb-> len: ETH_ZLEN),ioaddr + TxStatus0 +(entry * 4)); 我们将此数据包的长度与一些控制信息一起写入状态寄存器网卡驱动代码,以便网卡的工作有基础. ④确定发送缓冲区是否已满. 如果已满,它将覆盖数据并停止发送. 如果((tp-> cur_tx -NUM_TX_DESC)== tp-> dirty_tx) netif_stop_queue(dev); 接收. 当数据来自网络电缆时,网卡将产生一个中断并调用中断服务程序rtl8139_interrupt. 它主要做三件事. ①从网卡的中断状态寄存器中读取状态值进行分析. 状态=读(ioaddr + IntrStatus); if((状态&(PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver | TxErr | TxOK | RxErr | RxOK)== 0) 出去; 上面的代码显示,如果以上9个条件都不满足,请退出. ②if(状态&(RxOK | RxUnderrun | RxOverflow | RxFIFOOver)) rtl8139_rx_interrupt(dev,tp,ioaddr); 如果是以上4种情况,则属于接收信号,请调用rtl8139_rx_interrupt进行接收处理. ③if(状态&(TxOK | TxErr)){ spin_lock(&tp->锁定); rtl8139_tx_interrupt(dev,tp,ioaddr); spin_unlock(&tp->锁定); } 如果这是传输完成的信号,请调用rtl8139_tx_interrupt进行后处理. 接收中断处理函数rtl8139_rx_interrupt可以完成以下四件事. ①此功能是一个大循环. 循环条件是,只要接收缓冲区不为空,就可以继续读取数据. 循环不会停止,并且在读取为空后将跳出循环. int ring_offset = cur_rx%RX_BUF_LEN; rx_status = le32_to_cpu(*(u32 *)(rx_ring + ring_offset)); rx_size = rx_status >> 16; 以上三行代码计算要接收的数据包的长度. ②根据该长度分配数据包的数据结构 skb = dev_alloc_skb(pkt_size +2); ③如果分配成功,则将数据从接收缓冲区复制到此程序包中 eth_copy_and_sum(skb和rx_ring [ring_offset + 4],pkt_size,0); 此函数在include /linux/etherdevice.h中调用memcpy(). 静态内联void eth_copy_and_sum(structsk_buff * dest,无符号字符* src,int len,int base) { memcpy(目标->数据,src,len); } &rx_ring [ring_offset +4]是接收缓冲区,也是源地址,而skb-> data是数据包的数据地址,也是目的地址. ④将此数据包发送到Linux协议栈以进行进一步处理. skb->协议= eth_type_trans(skb,dev); netif_rx(skb); 在执行netif_rx()函数之后,此数据包的数据离开网卡驱动程序并进入Linux网络协议堆栈. 这些数据包的以太网帧头,IP头和TCP头被删除,最后将数据发送到应用程序. netif_rx函数位于net / core / dev.c中. rtl8139_remove_one基本上是rtl8139_init_one的反向过程. 至此,已经概述了Linux驱动程序的框架.

本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-145264-2.html
一大波打假人事