1. DMA 启用的时机
e1000e 驱动在 设备初始化阶段 启用 DMA,具体步骤如下:
(1) PCIe 设备初始化
-
调用路径:
e1000_probe()
→e1000_sw_init()
→e1000_init_hw()
→e1000_configure()
-
关键操作:
-
启用 PCIe 设备的 DMA 主控模式(通过设置 PCI Command Register 的
PCI_COMMAND_MASTER
位)。 -
分配 DMA 缓冲区(如描述符环和数据缓冲区)。
-
(2) DMA 引擎启动
-
调用路径:
e1000_open()
→e1000_configure_tx()
/e1000_configure_rx()
-
关键操作:
-
配置发送(TX)和接收(RX)描述符环的 DMA 地址(通过 MMIO 寄存器写入)。
-
启动 DMA 引擎(通过设置控制寄存器
TCTL
和RCTL
)。
-
2. 配置 MMIO 寄存器
e1000e 通过 MMIO 寄存器控制 DMA 行为,关键寄存器包括:
寄存器名 | 地址偏移 | 作用 |
---|---|---|
TCTL | 0x0400 | 发送控制寄存器,启用 TX DMA(设置 TCTL.EN 位)。 |
RCTL | 0x0100 | 接收控制寄存器,启用 RX DMA(设置 RCTL.EN 位)。 |
TDBAL/TDBAH | 0x3800 | TX 描述符环基地址(低32位/高32位),指向 DMA 缓冲区。 |
RDBAL/RDBAH | 0x2800 | RX 描述符环基地址(低32位/高32位),指向 DMA 缓冲区。 |
TDH/TDT | 0x3810 | TX 描述符头/尾指针,用于 DMA 环形队列管理。 |
RDH/RDT | 0x2810 | RX 描述符头/尾指针,用于 DMA 环形队列管理。 |
3. 代码示例:DMA 启用流程
(1) 启用 PCIe DMA 主控模式
// drivers/net/ethernet/intel/e1000e/netdev.c static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) {pci_enable_device_mem(pdev); // 启用设备内存和 DMApci_set_master(pdev); // 设置 PCI_COMMAND_MASTER 位 }
(2) 配置 TX/RX 描述符环的 DMA 地址
// drivers/net/ethernet/intel/e1000e/netdev.c static void e1000_configure_tx(struct e1000_adapter *adapter) {struct e1000_hw *hw = &adapter->hw;// 写入 TX 描述符环的 DMA 地址ew32(TDBAL, (u32)(adapter->tx_ring->dma));ew32(TDBAH, (u32)((u64)adapter->tx_ring->dma >> 32));// 启用 TX DMAew32(TCTL, adapter->tx_ring->tctl | E1000_TCTL_EN); }
(3) 启动 RX DMA
static void e1000_configure_rx(struct e1000_adapter *adapter) {struct e1000_hw *hw = &adapter->hw;// 写入 RX 描述符环的 DMA 地址ew32(RDBAL, (u32)(adapter->rx_ring->dma));ew32(RDBAH, (u32)((u64)adapter->rx_ring->dma >> 32));// 启用 RX DMAew32(RCTL, adapter->rx_ring->rctl | E1000_RCTL_EN); }
(注:ew32()
是 e1000e 驱动中用于写 MMIO 寄存器的宏)
4. DMA 数据传输流程
-
发送数据:
-
驱动将数据包存入内存缓冲区,更新 TX 描述符环。
-
网卡通过 DMA 从内存读取数据并发送。
-
-
接收数据:
-
网卡通过 DMA 将数据写入内存缓冲区,触发中断通知驱动。
-
驱动从 RX 描述符环读取数据。
-
5. 关键点总结
步骤 | 操作 |
---|---|
PCIe DMA 启用 | 调用 pci_set_master() 设置 PCI_COMMAND_MASTER 。 |
MMIO 配置 | 通过 TCTL /RCTL 寄存器启用 DMA,通过 TDBAL /RDBAL 设置缓冲区。 |
缓冲区分配 | 使用 dma_alloc_coherent() 分配 DMA 内存。 |
数据传输 | 描述符环管理 DMA 的起始/结束位置。 |
6. 调试技巧
-
查看寄存器状态:
通过ethtool -d ethX
可以 dump e1000e 的寄存器值,确认TCTL.EN
和RCTL.EN
是否已置位。 -
DMA 地址验证:
在驱动代码中打印adapter->tx_ring->dma
和adapter->rx_ring->dma
,确保地址有效。