5.20 Net 设备
2026/7/20大约 7 分钟设备驱动驱动设备网络NetNAPIEthernet
5.20 Net 设备
📚 本节导读
学习时长: 约 45 分钟
难度级别: ⭐⭐⭐⭐⭐
前置知识: 5.1 设备驱动框架、TCP/IP 协议栈基础、网络接口概念
🎯 学习目标
- 理解 OneOS Net 网络设备的抽象架构与分层设计
- 掌握网络设备的注册、MAC 地址获取与数据收发流程
- 理解 NAPI 轮询机制的原理与使用
- 了解网络协议栈操作接口(protocol_ops)的作用
- 能够编写基本的网络设备驱动注册代码
一、概述
Net(网络)设备是 OneOS 中网络接口的抽象层,为上层 TCP/IP 协议栈(如 LwIP)提供统一的硬件操作接口。Net 设备驱动位于 drivers/net/ 目录下,核心头文件为 net_dev.h。
1.1 Net 设备在协议栈中的位置
┌─────────────────────────────────────────┐
│ 应用层 (Socket / MQTT / HTTP) │
├─────────────────────────────────────────┤
│ TCP/IP 协议栈 (LwIP / OneOS-Net) │
├─────────────────────────────────────────┤
│ Net 设备框架 (net_dev.h) │ ← 统一抽象层
├──────────┬──────────┬───────────────────┤
│ Ethernet │ Wi-Fi │ Bluetooth ... │ ← 物理网络接口
│ (ETH) │ (WLAN) │ (BT) │
└──────────┴──────────┴───────────────────┘1.2 核心特点
- 统一抽象:将不同类型的网络接口(Ethernet、Wi-Fi、Bluetooth)统一为
os_net_device结构体 - 分层设计:设备操作(
ops)与协议栈操作(protocol_ops)分离,硬件驱动与协议栈解耦 - NAPI 支持:支持 NAPI(New API)轮询机制,在高吞吐场景下减少中断开销
- 链路管理:提供链路状态变化通知机制,支持上/下线事件处理
二、核心数据结构
2.1 网络接口类型
typedef enum
{
net_dev_intf_ether = 0, // 以太网
net_dev_intf_bt = 1, // 蓝牙
net_dev_intf_br = 2, // 桥接
net_dev_intf_unknown // 未知类型
} os_net_intf_type_t;2.2 网络模式
typedef enum
{
net_dev_mode_ap = 0, // AP 模式(接入点)
net_dev_mode_sta = 1, // STA 模式(客户端)
net_dev_mode_unknown // 未知模式
} os_net_mode_t;2.3 设备操作接口 os_net_device_ops
struct os_net_device_ops
{
os_err_t (*init)(struct os_net_device *net_dev); // 设备初始化
os_err_t (*deinit)(struct os_net_device *net_dev); // 设备去初始化
os_err_t (*send)(struct os_net_device *net_dev, // 发送数据包
uint8_t *data, int len);
os_err_t (*get_mac)(struct os_net_device *net_dev, // 获取 MAC 地址
uint8_t *addr);
os_err_t (*set_filter)(struct os_net_device *net_dev, // 设置 MAC 过滤器
uint8_t *buff, os_bool_t enable);
};2.4 协议栈操作接口 os_net_protocol_ops
struct os_net_protocol_ops
{
os_err_t (*init)(struct os_net_device *net_dev); // 协议栈初始化
os_err_t (*deinit)(struct os_net_device *net_dev); // 协议栈去初始化
os_err_t (*recv)(struct os_net_device *net_dev, // 接收数据包(传给协议栈)
uint8_t *data, int len);
os_err_t (*linkchange)(struct os_net_device *net_dev, // 链路状态变化通知
os_bool_t status);
};2.5 网络设备信息 os_net_device_info
struct os_net_device_info
{
uint32_t MTU; // 最大传输单元
uint32_t MRU; // 最大接收单元
os_net_mode_t mode; // 网络模式 (AP/STA)
os_bool_t linkstatus; // 链路状态 (UP/DOWN)
os_net_intf_type_t intf_type; // 接口类型 (Ethernet/BT/BR)
char *ip_addr; // IP 地址
char *gw_addr; // 网关地址
char *ip_mask; // 子网掩码
uint8_t dhcp_start_id; // DHCP 起始 ID
uint8_t dhcp_end_id; // DHCP 结束 ID
};2.6 网络设备结构体 os_net_device
struct os_net_device
{
os_device_t dev; // 继承自 os_device_t
struct os_net_device_info info; // 设备信息
const struct os_net_device_ops *ops; // 硬件操作接口
const struct os_net_protocol_ops *protocol_ops; // 协议栈操作接口
void *tx_buff; // 发送缓冲区
void *rx_buff; // 接收缓冲区
void *userdata; // 用户自定义数据
};设计要点:
dev继承自os_device_t,复用设备框架的注册/查找/打开机制ops指向硬件驱动实现的操作函数,protocol_ops指向协议栈实现的操作函数- 通过
tx_buff和rx_buff提供零拷贝支持
三、API 参考
3.1 设备注册与注销
/* 注册网络设备 */
os_err_t os_net_device_register(struct os_net_device *net_dev,
const char *name);
/* 注销网络设备 */
os_err_t os_net_device_unregister(struct os_net_device *net_dev);3.2 数据收发
/* 发送数据(由协议栈调用,通过 ops->send 发送) */
os_err_t os_net_tx_data(struct os_net_device *net_dev,
uint8_t *data, int len);
/* 接收数据通知(由硬件驱动调用,通知协议栈有新数据) */
os_err_t os_net_rx_data(struct os_net_device *net_dev,
uint8_t *data, int len);
/* 接收通知(由硬件驱动调用,通知协议栈处理接收队列) */
os_err_t os_net_rx_notify(struct os_net_device *net_dev);3.3 MAC 地址与过滤
/* 获取 MAC 地址 */
os_err_t os_net_get_macaddr(struct os_net_device *net_dev,
uint8_t *addr);
/* 设置 MAC 地址过滤器 */
os_err_t os_net_set_filter(struct os_net_device *net_dev,
uint8_t *addr, os_bool_t enable);
/* MAC 地址字符串转换 */
os_err_t os_net_get_mac_string2addr(char *string, uint8_t *addr);3.4 链路状态管理
/* 通知协议栈链路状态变化 */
os_err_t os_net_linkchange(struct os_net_device *net_dev,
os_bool_t status);四、NAPI 轮询机制
4.1 NAPI 简介
NAPI(New API)是 Linux 内核中引入的一种网络数据包处理机制,在 OneOS 中也被采纳。它的核心思想是:
- 低负载时:使用中断方式处理数据包,响应快
- 高负载时:切换到轮询模式,减少中断开销,提高吞吐量
4.2 NAPI 数据结构
struct napi_struct
{
int weight; // 轮询权重(一次最多处理多少包)
unsigned long state; // NAPI 状态位
os_list_node_t list; // 链表节点
struct os_net_device *dev; // 所属网络设备
int (*poll)(struct napi_struct *napi, int budget); // 轮询函数
};4.3 NAPI 状态
enum {
NAPI_STATE_SCHED, // 轮询已调度
NAPI_STATE_MISSED, // 重新调度
NAPI_STATE_DISABLE, // 禁用
NAPI_STATE_NPSVC, // Netpoll 模式
NAPI_STATE_HASHED, // 已加入哈希表
NAPI_STATE_NO_BUSY_POLL, // 不使用 busy polling
NAPI_STATE_IN_BUSY_POLL, // 正在 busy polling
};4.4 NAPI API
/* 注册 NAPI 实例 */
void netif_napi_add(struct os_net_device *dev,
struct napi_struct *napi,
int (*poll)(struct napi_struct *napi, int budget),
int weight);
/* 检查是否可以调度 NAPI */
os_bool_t napi_schedule_prep(struct napi_struct *n);
/* 调度 NAPI 轮询 */
void napi_schedule(struct napi_struct *n);
/* 完成 NAPI 轮询 */
os_bool_t napi_complete_done(struct napi_struct *n, int work_done);4.5 NAPI 使用流程
中断到达
│
├─ 禁用网卡中断
│
├─ napi_schedule_prep() ← 检查是否可以调度
│ │
│ └─ __napi_schedule() ← 将 NAPI 加入调度队列
│
├─ 轮询线程执行 poll(state, budget)
│ │
│ ├─ 处理数据包 (最多 budget 个)
│ │
│ └─ napi_complete_done(state, work_done)
│ │
│ ├─ work_done < budget: 包已处理完
│ │ └─ 重新启用网卡中断
│ │
│ └─ work_done >= budget: 预算用完
│ └─ 继续轮询五、实现示例
5.1 以太网设备注册
#include <net_dev.h>
#include <driver.h>
/* 以太网硬件操作实现 */
static os_err_t eth_init(struct os_net_device *net_dev)
{
/* 初始化以太网 MAC 控制器 */
ETH_MAC_Init();
ETH_DMA_Init();
return OS_SUCCESS;
}
static os_err_t eth_send(struct os_net_device *net_dev,
uint8_t *data, int len)
{
/* 通过 DMA 发送数据包 */
ETH_DMA_Send(data, len);
return OS_SUCCESS;
}
static os_err_t eth_get_mac(struct os_net_device *net_dev,
uint8_t *addr)
{
/* 读取芯片内置 MAC 地址 */
ETH_GetMACAddr(addr);
return OS_SUCCESS;
}
static const struct os_net_device_ops eth_ops = {
.init = eth_init,
.deinit = OS_NULL,
.send = eth_send,
.get_mac = eth_get_mac,
.set_filter = OS_NULL,
};
/* 网络设备实例 */
static struct os_net_device eth_device;
os_err_t eth_device_register(void)
{
os_err_t ret;
/* 初始化设备信息 */
eth_device.info.MTU = 1500;
eth_device.info.intf_type = net_dev_intf_ether;
eth_device.info.linkstatus = OS_FALSE;
eth_device.ops = ð_ops;
/* 注册到 Net 设备框架 */
ret = os_net_device_register(ð_device, "eth0");
if (ret != OS_SUCCESS)
{
os_kprintf("eth0 register failed!\n");
return ret;
}
/* 获取 MAC 地址 */
uint8_t mac[6];
os_net_get_macaddr(ð_device, mac);
os_kprintf("eth0 MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
return OS_SUCCESS;
}5.2 接收数据上报
/* 以太网中断处理函数 */
void ETH_IRQHandler(void)
{
os_ubase_t level = os_irq_lock();
if (ETH_DMA_RxDone())
{
/* 从 DMA 描述符获取数据 */
uint8_t *data;
int len = ETH_DMA_GetRxPacket(&data);
if (len > 0)
{
/* 上报给协议栈 */
os_net_rx_data(ð_device, data, len);
}
}
os_irq_unlock(level);
}5.3 链路状态管理
/* 网线插入检测 */
void eth_link_up(void)
{
eth_device.info.linkstatus = OS_TRUE;
os_net_linkchange(ð_device, OS_TRUE);
os_kprintf("eth0: link up\n");
}
void eth_link_down(void)
{
eth_device.info.linkstatus = OS_FALSE;
os_net_linkchange(ð_device, OS_FALSE);
os_kprintf("eth0: link down\n");
}六、数据收发流程
6.1 发送流程
协议栈调用 os_net_tx_data()
│
├─ 调用 net_dev->ops->send(data, len)
│ │
│ └─ 硬件驱动将数据写入发送队列/DMA
│
└─ 硬件完成发送(中断或轮询确认)6.2 接收流程
硬件中断到达
│
├─ 驱动从硬件获取数据包
│
├─ 调用 os_net_rx_data(net_dev, data, len)
│ │
│ └─ 调用 net_dev->protocol_ops->recv(data, len)
│ │
│ └─ 协议栈处理数据包 (IP/TCP/UDP...)
│
└─ 或使用 NAPI 轮询模式批量处理七、注意事项
- 线程安全:数据收发函数可能在不同上下文(中断、任务)中调用,需保证
ops实现的线程安全 - 缓冲区管理:
tx_buff和rx_buff所有权在驱动和协议栈之间传递,需明确谁负责释放 - MTU 限制:发送数据不应超过
info.MTU,驱动应检查数据长度 - NAPI 权重:
weight需根据实际吞吐量调整,太小影响吞吐,太大会占用 CPU 时间 - 协议栈解耦:
protocol_ops由协议栈模块(如 LwIP 适配层)设置,驱动不应直接操作
📖 相关资源
- 5.1 设备驱动框架 — 设备驱动核心框架
- 7.5 网络协议栈 — 上层网络协议栈
- 附录:net_dev.h 源码 — Net