41、网络接口卡(NIC)驱动开发指南

网络接口卡(NIC)驱动开发指南

1. 驱动加载与接口创建

当模块加载且设备匹配成功后,系统会创建一个以太网接口。以下是具体操作及示例:
- 查看 dmesg 信息 :使用 dmesg 命令可查看相关信息,例如:

# dmesg
[...]
[146698.060074] fake eth device initialized
[146698.087297] IPv6: ADDRCONF(NETDEV_UP): eth0: link is not ready
  • 查看接口信息 :运行 ifconfig -a 命令,接口信息将显示在屏幕上:
# ifconfig -a
[...]
eth0 Link encap:Ethernet HWaddr 00:00:00:00:00:00
BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
  • 配置接口 IP 地址 :使用 ifconfig 命令为接口分配 IP 地址:
# ifconfig eth0 192.168.1.45
# ifconfig
[...]
eth0 Link encap:Ethernet HWaddr 00:00:00:00:00:00
inet addr:192.168.1.45 Bcast:192.168.1.255 Mask:255.255.255.0
BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
2. 设备状态与控制

设备控制指内核主动或响应用户操作改变接口属性的情况。可通过 struct net_device_ops 结构的操作,或使用 ethtool 工具。而状态则反映接口的当前状态。

3. 中断处理程序

如今的硬件接口更智能,能报告状态,网络接口可生成中断以指示错误、链路状态变化等,这些都需在中断处理程序中处理。以下是中断处理程序的示例代码:

static irqreturn_t my_netdev_irq(int irq, void *dev_id)
{
   struct priv_net_struct *priv = dev_id;
   /*
    * Can't do anything in interrupt context because we need to
    * block (spi_sync() is blocking) so fire of the interrupt
    * handling workqueue.
    * Remember, we access our netdev registers through SPI bus
    * via spi_sync() call.
    */
   schedule_work(&priv->irq_work);
   return IRQ_HANDLED;
}

static void my_netdev_irq_work_handler(struct work_struct *work)
{
   struct priv_net_struct *priv =
         container_of(work, struct priv_net_struct, irq_work);
   struct net_device *ndev = priv->netdev;
   int intflags, loop;
   /* disable further interrupts */
   my_netdev_reg_bitclear(priv, EIE, EIE_INTIE);
   do {
         loop = 0;
         intflags = my_netdev_regb_read(priv, EIR);
         /* DMA interrupt handler (not currently used) */
         if ((intflags & EIR_DMAIF) != 0) {
               loop++;
               handle_dma_complete();
               clear_dma_interrupt_flag();
         }
         /* LINK changed handler */
         if ((intflags & EIR_LINKIF) != 0) {
               loop++;
               my_netdev_check_link_status(ndev);
               clear_link_interrupt_flag();
         }
         /* TX complete handler */
         if ((intflags & EIR_TXIF) != 0) {
               bool err = false;
               loop++;
               priv->tx_retry_count = 0;
               if (locked_regb_read(priv, ESTAT) & ESTAT_TXABRT)
                     clear_tx_interrupt_flag();
         }
         /* TX Error handler */
         if ((intflags & EIR_TXERIF) != 0) {
               loop++;
               /*
                * Reset TX logic by setting/clearing appropriate
                 * bit in the right register
                 */
               [...]
               /* Transmit Late collision check for retransmit */
               if (my_netdev_cpllision_bit_set())
                     /* Handlecollision */
                     [...]
         }
         /* RX Error handler */
         if ((intflags & EIR_RXERIF) != 0) {
               loop++;
               /* Check free FIFO space to flag RX overrun */
               [...]
         }
         /* RX handler */
         if (my_rx_interrupt(ndev))
               loop++;
   } while (loop);
   /* re-enable interrupts */
   my_netdev_reg_bitset(priv, EIE, EIE_INTIE);
}
4. ethtool 支持

ethtool 是用于检查和调整以太网网络接口设置的小工具。借助它可控制多种参数,如速度、媒体类型、双工操作等。需要 ethtool 支持的驱动应包含 <linux/ethtool.h> ,并填充 ethtool_ops 结构,将其分配给 struct net_device 结构的 .ethtool_ops 字段,示例如下:

my_netdev->ethtool_ops = &my_ethtool_ops;

也可使用 SET_ETHTOOL_OPS 宏。部分实现 ethtool 支持的驱动示例:
- drivers/net/ethernet/microchip/enc28j60.c
- drivers/net/ethernet/freescale/fec.c
- drivers/net/usb/rtl8150.c

5. 驱动方法

驱动方法主要是 probe() remove() 函数,负责向内核注册和注销网络设备。下面是网络接口可执行的操作:

static const struct net_device_ops my_netdev_ops = {
   .ndo_open         = my_netdev_open,
   .ndo_stop         = my_netdev_close,
   .ndo_start_xmit   = my_netdev_start_xmit,
   .ndo_set_rx_mode  = my_netdev_set_multicast_list,
   .ndo_set_mac_address    = my_netdev_set_mac_address,
   .ndo_tx_timeout   = my_netdev_tx_timeout,
   .ndo_change_mtu   = eth_change_mtu,
   .ndo_validate_addr      = eth_validate_addr,
};
6. 探测函数

探测函数需完成设备的早期初始化,并向内核注册网络设备,具体步骤如下:
1. 使用 alloc_etherdev() 函数(借助 netdev_priv() )分配网络设备及其私有数据。
2. 初始化私有数据字段(如互斥锁、自旋锁、工作队列等)。若设备位于可能睡眠的总线(如 SPI),应使用工作队列;若为 MMIO 设备,可使用自旋锁。
3. 初始化特定总线的参数和功能(如 SPI、USB、PCI 等)。
4. 请求并映射资源(I/O 内存、DMA 通道和 IRQ)。
5. 必要时,生成随机 MAC 地址并分配给设备。
6. 填充必要的 netdev 属性(如 if_port irq netdev_ops ethtool_ops 等)。
7. 将设备置于低功耗状态( open() 函数会将其移除该模式)。
8. 调用 register_netdev() 注册设备。

以下是 SPI 网络设备的探测函数示例:

static int my_netdev_probe(struct spi_device *spi)
{
   struct net_device *dev;
   struct priv_net_struct *priv;
   int ret = 0;
   /* Allocate network interface */
   dev = alloc_etherdev(sizeof(struct priv_net_struct));
   if (!dev)
         [...] /* handle -ENOMEM error */
   /* Private data */
   priv = netdev_priv(dev);
   /* set private data and bus-specific parameter */
   [...]
   /* Initialize some works */
   INIT_WORK(&priv->tx_work, data_tx_work_handler);
   [...]
   /* Devicerealy init, only few things */
   if (!my_netdev_chipset_init(dev))
         [...] /* handle -EIO error */
   /* Generate and assign random MAC address to the device */
   eth_hw_addr_random(dev);
   my_netdev_set_hw_macaddr(dev);
   /* Board setup must set the relevant edge trigger type;
    * level triggers won't currently work.
    */
   ret = request_irq(spi->irq, my_netdev_irq, 0, DRV_NAME, priv);
   if (ret < 0)
         [...]; /* Handle irq request failure */
   /* Fill some netdev mandatory or useful properties */
   dev->if_port = IF_PORT_10BASET;
   dev->irq = spi->irq;
   dev->netdev_ops = &my_netdev_ops;
   dev->ethtool_ops = &my_ethtool_ops;
   /* Put device into sleep mode */
   My_netdev_lowpower(priv, true);
   /* Register our device with the kernel */
   if (register_netdev(dev))
         [...]; /* Handle registration failure error */
   dev_info(&dev->dev, DRV_NAME " driver registered\n");
   return 0;
}
7. 模块卸载

模块卸载是清理函数,依赖 unregister_netdev() free_netdev() 函数,示例代码如下:

static int my_netdev_remove(struct spi_device *spi)
{
   struct priv_net_struct *priv = spi_get_drvdata(spi);
   unregister_netdev(priv->netdev);
   free_irq(spi->irq, priv);
   free_netdev(priv->netdev);
   return 0;
}
8. 总结

本文详细介绍了编写 NIC 设备驱动所需的知识,虽以 SPI 总线的网络接口为例,但 USB 或 PCI 网络接口原理相同。通过阅读本文,NIC 驱动开发应不再神秘。

以下是探测函数步骤的 mermaid 流程图:

graph TD
    A[开始] --> B[分配网络设备及私有数据]
    B --> C[初始化私有数据字段]
    C --> D[初始化总线参数和功能]
    D --> E[请求并映射资源]
    E --> F[生成并分配 MAC 地址]
    F --> G[填充 netdev 属性]
    G --> H[将设备置于低功耗状态]
    H --> I[注册设备]
    I --> J[结束]

以下是中断处理流程的表格:
| 中断类型 | 处理操作 |
| ---- | ---- |
| DMA 中断 | 调用 handle_dma_complete() 并清除标志 |
| 链路状态改变中断 | 检查链路状态并清除标志 |
| 发送完成中断 | 重置重试计数,必要时清除标志 |
| 发送错误中断 | 重置发送逻辑,处理碰撞 |
| 接收错误中断 | 检查 FIFO 空间 |
| 接收中断 | 处理接收操作 |

网络接口卡(NIC)驱动开发指南

9. 设备加载与配置操作总结

在网络接口卡(NIC)驱动开发中,设备加载与配置是基础且关键的环节。以下是具体操作的总结:
- 驱动加载与接口创建
1. 使用 dmesg 命令查看模块加载和设备匹配后的信息,确认以太网接口是否成功初始化。
2. 运行 ifconfig -a 命令查看接口信息。
3. 使用 ifconfig 命令为接口分配 IP 地址,使其可正常工作。
- 设备状态与控制 :内核可通过 struct net_device_ops 结构操作或 ethtool 工具改变接口属性,状态则反映接口当前情况。

10. 中断处理与 ethtool 支持要点
  • 中断处理 :现代硬件接口能报告多种状态,网络接口产生的中断(如错误、链路状态变化等)需在中断处理程序中处理。示例代码展示了中断处理的基本流程,将操作延迟到工作队列中执行,避免在中断上下文进行阻塞操作。
  • ethtool 支持 ethtool 工具可控制以太网网络接口的多种参数。驱动需包含 <linux/ethtool.h> ,填充 ethtool_ops 结构并分配给 struct net_device 结构的 .ethtool_ops 字段,以实现 ethtool 支持。
11. 驱动方法及模块管理
  • 驱动方法 probe() remove() 函数分别负责网络设备的注册和注销。网络接口的操作通过 struct net_device_ops 结构定义,包含打开、停止、发送、设置接收模式等操作。
  • 模块管理
    • 探测函数 :按特定步骤完成设备早期初始化和注册,包括分配设备和私有数据、初始化字段、映射资源、生成 MAC 地址等。SPI 网络设备的探测函数示例展示了具体实现。
    • 模块卸载 :使用 unregister_netdev() 移除接口, free_netdev() 释放相关内存,确保资源正确清理。
12. 开发注意事项
  • 设备命名 :避免使用重复的设备名称,否则可能导致注册失败。
  • 资源管理 :在驱动开发过程中,要正确管理资源,如请求和释放 IRQ、分配和释放内存等,防止资源泄漏。
  • 中断处理 :由于中断处理程序运行在中断上下文,应避免进行阻塞操作,可将耗时操作延迟到工作队列中执行。
13. 开发建议
  • 参考示例代码 :可参考 drivers/net/ethernet/microchip/enc28j60.c 等示例代码,学习成熟的驱动开发实现。
  • 测试驱动 :使用提供的虚拟驱动进行测试,确保驱动在不同场景下的稳定性和正确性。
14. 总结回顾

本文全面介绍了网络接口卡(NIC)驱动开发的各个方面,包括设备加载与配置、中断处理、 ethtool 支持、驱动方法和模块管理等。通过详细的代码示例和操作步骤,帮助开发者理解和掌握 NIC 驱动开发的关键技术。

以下是设备加载与配置操作的列表:
1. 加载模块并匹配设备。
2. 使用 dmesg 查看初始化信息。
3. 用 ifconfig -a 查看接口信息。
4. 用 ifconfig 配置接口 IP 地址。

以下是开发注意事项的 mermaid 流程图:

graph TD
    A[开始] --> B[检查设备命名]
    B --> C{是否重复?}
    C -- 是 --> D[修改名称]
    C -- 否 --> E[管理资源]
    D --> E
    E --> F[处理中断]
    F --> G[结束]

通过以上内容,开发者可以系统地学习和实践 NIC 驱动开发,为网络设备的稳定运行提供支持。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值