ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Linux 驱动开发:如何添加以太网驱动

Linux 驱动开发:如何添加以太网驱动 1. 引言在 Linux 系统中以太网驱动是网络功能正常工作的基础。无论是开发板、嵌入式设备还是服务器网卡都需要通过驱动与内核网络协议栈交互。本文将从驱动框架、核心数据结构、注册流程到实际代码示例系统讲解如何在 Linux 中编写并添加一个以太网驱动。2. 以太网驱动框架概述Linux 内核为网络设备驱动提供了统一的抽象层核心是struct net_device结构体。驱动开发者需要完成设备探测、资源初始化、打开/关闭接口、数据收发等操作并把这些操作注册到内核网络子系统。一个完整的以太网驱动通常包含以下组成部分设备探测与初始化通过平台总线、PCI 或设备树匹配硬件。网络设备注册分配并初始化net_device设置操作函数集。数据发送实现ndo_start_xmit回调把 skb 交给硬件。数据接收通过中断或轮询方式接收数据并上交给协议栈。中断处理处理收发完成、链路状态变化等事件。3. 核心数据结构编写以太网驱动前需要先理解几个关键的内核数据结构。3.1 struct net_device这是网络设备的核心结构体描述一个网络接口的全部属性包括设备名、MAC 地址、MTU、操作函数集等。驱动通过alloc_etherdev或alloc_netdev分配。3.2 struct net_device_ops该结构体定义了设备的各种操作回调例如打开设备、停止设备、发送数据、修改 MAC 地址等。驱动必须实现其中必要的回调并赋值给net_device的netdev_ops字段。3.3 struct sk_buff内核网络协议栈中用于承载数据包的核心结构体。发送时驱动从sk_buff中取出数据交给硬件接收时驱动把硬件数据封装成sk_buff并交给协议栈。4. 驱动注册流程以太网驱动的注册流程通常分为以下几个步骤定义平台驱动或 PCI 驱动结构体并注册到内核总线。在探测函数中读取硬件资源寄存器地址、中断号等。调用alloc_etherdev分配网络设备结构体。初始化net_device的字段和操作函数集。调用register_netdev把设备注册到内核网络子系统。在移除函数中调用unregister_netdev和free_netdev释放资源。5. 代码示例一个简单的平台以太网驱动下面以平台设备为例演示一个最小可用的以太网驱动骨架。该示例使用虚拟寄存器模拟硬件重点展示驱动框架的搭建过程。#include linux/module.h #include linux/platform_device.h #include linux/netdevice.h #include linux/etherdevice.h #include linux/skbuff.h #include linux/interrupt.h #define DRV_NAME demo_eth struct demo_eth_priv { struct net_device *netdev; void __iomem *base; int irq; }; static int demo_eth_open(struct net_device *netdev) { netif_start_queue(netdev); return 0; } static int demo_eth_stop(struct net_device *netdev) { netif_stop_queue(netdev); return 0; } static netdev_tx_t demo_eth_start_xmit(struct sk_buff *skb, struct net_device *netdev) { /* 实际驱动在此处把 skb 数据写入硬件发送 FIFO */ dev_kfree_skb(skb); return NETDEV_TX_OK; } static const struct net_device_ops demo_eth_netdev_ops { .ndo_open demo_eth_open, .ndo_stop demo_eth_stop, .ndo_start_xmit demo_eth_start_xmit, }; static int demo_eth_probe(struct platform_device *pdev) { struct demo_eth_priv *priv; struct net_device *netdev; struct resource *res; int ret; netdev alloc_etherdev(sizeof(struct demo_eth_priv)); if (!netdev) return -ENOMEM; priv netdev_priv(netdev); priv-netdev netdev; res platform_get_resource(pdev, IORESOURCE_MEM, 0); priv-base devm_ioremap_resource(pdev-dev, res); if (IS_ERR(priv-base)) { ret PTR_ERR(priv-base); goto err_free; } priv-irq platform_get_irq(pdev, 0); if (priv-irq 0) { ret priv-irq; goto err_free; } netdev-netdev_ops demo_eth_netdev_ops; eth_hw_addr_set(netdev, (u8[]){0x00, 0x11, 0x22, 0x33, 0x44, 0x55}); ret register_netdev(netdev); if (ret) goto err_free; platform_set_drvdata(pdev, priv); return 0; err_free: free_netdev(netdev); return ret; } static void demo_eth_remove(struct platform_device *pdev) { struct demo_eth_priv *priv platform_get_drvdata(pdev); unregister_netdev(priv-netdev); free_netdev(priv-netdev); } static struct platform_driver demo_eth_driver { .probe demo_eth_probe, .remove demo_eth_remove, .driver { .name DRV_NAME, .of_match_table demo_eth_of_match, }, }; module_platform_driver(demo_eth_driver); MODULE_LICENSE(GPL); MODULE_DESCRIPTION(Demo Ethernet Driver);6. 设备树匹配在设备树中需要为硬件节点指定 compatible 属性与驱动中的of_match_table对应。示例设备树节点如下demo_eth: ethernet10000000 { compatible vendor,demo-eth; reg 0x10000000 0x1000; interrupts 31; };驱动中需要补充对应的匹配表static const struct of_device_id demo_eth_of_match[] { { .compatible vendor,demo-eth }, { } }; MODULE_DEVICE_TABLE(of, demo_eth_of_match);7. 编译与加载将驱动源码放入内核源码树的drivers/net/ethernet/目录并修改对应的 Kconfig 和 Makefile然后编译内核或编译为模块。如果编译为模块加载命令如下insmod demo_eth.ko加载成功后可以通过以下命令查看设备是否注册成功ip link show如果看到类似eth0的接口说明驱动注册成功。8. 总结添加以太网驱动的核心在于理解net_device框架、实现必要的操作回调并正确完成设备注册流程。实际项目中还需要处理 DMA、中断、链路状态检测、NAPI 等复杂逻辑但整体框架与本文示例一致。建议读者在真实硬件上结合内核文档逐步调试加深对网络驱动子系统的理解。
返回列表