看看pci_map_single和pci_unmap_single分别是怎么实现的: --------------------------------------------------------------------------- linux/include/asm-generic/pci-dma-compact.h static inline dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr, size_t size, int direction) { return dma_map_single(hwdev == NULL ? NULL : &hwdev->dev, ptr, size, (enum dma_data_direction)direction); } /linux/include/asm-i386/dma-mapping.h ------------------------------------------------------------ static inline dma_addr_t dma_map_single(struct device *dev, void *ptr, size_t size, enum dma_data_direction direction) { BUG_ON(direction == DMA_NONE); flush_write_buffers(); return virt_to_phys(ptr); } static inline dma_addr_t dma_map_single(struct device *dev, void *ptr, size_t size, enum dma_data_direction direction) { BUG_ON(direction == DMA_NONE); flush_write_buffers(); return virt_to_phys(ptr); } static inline void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, enum dma_data_direction direction) { BUG_ON(direction == DMA_NONE); } dma_map_single返回ptr的物理地址,dma_unmap_single正好相反,执行这个之后,就可以操作DMA缓存区的 数据。 因为外设在访问内存的时候需要的是物理地址,所以driver才需要做用函数pci_map_single将虚地址转成物理地址。这段地址是由CPU和外设共享的。一般情况下CPU只做读操作,写入是由外设完成的。 现在的网卡大多数都是采用主动DMA的方式。也就是当网卡从网线上接收到数据之后,就会自己启动dma将数据从网卡内部的FIFO传送到配置寄存器指定的 内存地址。当一个数据包接收完成之后,产生一个中断通知driver进行处理。整个过程不需要cpu进行干涉。当driver接收到中断的时候,数据已经 在内存里存放好了。 对linux系统来说,大多数网卡driver为了提高处理效率,都会将skb->data指向这块共享内存中,这样可以减少一次内存拷贝操作。 在e1000_clean_rx_irq函数里在调用 netif_rx 之前直接访问skb->data就可以了...这就是收到的数据包。 如果真的要自己指定地址,修改 alloc_rx_buf 里面的实现代码就好了 |
|
来自: langhuayipian > 《linux内核》