openvela之网络驱动适配指南
本文介绍了openvela嵌入式操作系统的网络驱动框架及其适配方法。openvela采用"上下分层"架构,开发者只需关注硬件相关的下半部分驱动实现。文章详细解析了网络协议栈和驱动配置方法,包括Kconfig工具的使用。重点阐述了数据收发流程,包括发送和接收的具体步骤,并详细说明了核心接口如基础设备操作、无线扩展接口和NetPKT数据包操作等。通过这套框架,开发者可以快速完成网络
在嵌入式系统开发中,网络功能的实现往往是核心环节之一。openvela作为一款轻量级嵌入式操作系统,提供了一套简洁高效的网络驱动框架,帮助开发者快速完成网络设备的适配工作。本文将基于 openvela官方文档,详细解读网络驱动的适配流程、核心接口与实现方法,为开发者提供从配置到调试的全流程指导。
一、openvela 网络驱动框架简介
openvela 内置了轻量级 TCP/IP 协议栈,并采用 “上下分层” 的驱动架构设计,极大简化了厂商的适配工作:
- 上半部分(Upper Half):由系统提供通用实现,包含协议栈核心逻辑、数据流转控制等,无需开发者关注;
- 下半部分(Lower Half):需要厂商根据硬件特性实现,主要负责硬件交互(如数据包收发、设备启停等)。
通过这种设计,开发者只需聚焦于硬件相关的驱动逻辑,即可让设备具备联网能力,大幅降低了适配门槛。
二、核心配置说明
在进行驱动开发前,需通过 Kconfig 工具完成网络功能的基础配置。主要分为协议栈配置
和驱动配置
两类,开发者可根据硬件支持的功能按需启用。
- 网络协议栈配置
/* 网络协议栈核心配置,根据硬件支持的协议选择启用 */
CONFIG_NET // 启用网络功能
CONFIG_NET_TCP // 启用TCP协议
CONFIG_NET_UDP // 启用UDP协议
CONFIG_NET_ICMP // 启用ICMP协议(用于ping等功能)
CONFIG_NET_IPv4 // 启用IPv4支持
CONFIG_NET_IPv6 // 启用IPv6支持(可选)
CONFIG_NET_ETHERNET // 启用以太网支持(有线网络必备)
CONFIG_NET_ROUTE // 启用路由功能(多网段场景需启用)
- 驱动功能配置
/* 驱动功能配置,网络设备必选 */
CONFIG_NETDEVICES // 启用网络设备支持
CONFIG_NETDEV_IOCTL // 启用IO控制接口(用于设备管理命令)
CONFIG_NETDEV_WIRELESS_HANDLER // 启用无线网卡处理逻辑(无线设备需启用)
配置完成后,系统会根据选项编译对应的模块,减少不必要的资源占用。
三、数据收发流程解析
网络驱动的核心功能是实现数据包的收发,openvela定义了清晰的流程规范:
1. 发送流程
- 上层协议栈调用驱动的
transmit
接口,传递待发送的数据包(netpkt_t
类型); - 驱动将数据包通过硬件接口发送到物理网络;
- 发送完成后,驱动调用
netpkt_free
释放缓冲区,并通过netdev_lower_txdone
通知上层发送完成,恢复发送配额。
2. 接收流程
- 硬件接收到数据包后,触发中断或轮询机制;
- 驱动通过
receive
接口从硬件读取数据,封装为netpkt_t
结构; - 调用
netdev_lower_rxready
通知上层读取数据包,完成接收。
四、驱动适配核心接口
openvela 的驱动适配接口定义在 nuttx/net/netdev_lowerhalf.h
中,主要包含设备操作接口、无线扩展接口及数据包操作接口,开发者需根据硬件特性实现这些接口。
1. 基础设备操作接口(struct netdev_ops_s
)
该结构体定义了网络设备的核心操作,包含设备启停、数据收发等必选接口:
struct netdev_ops_s
{
CODE int (*ifup)(FAR struct netdev_lowerhalf_s *dev);
CODE int (*ifdown)(FAR struct netdev_lowerhalf_s *dev);
CODE int (*transmit)(FAR struct netdev_lowerhalf_s *dev, FAR netpkt_t *pkt);
CODE FAR netpkt_t (*receive)(FAR struct netdev_lowerhalf_s *dev);
CODE int (*addmac)(FAR struct netdev_lowerhalf_s *dev, FAR const uint8_t *mac);
CODE int (*rmmac)(FAR struct netdev_lowerhalf_s *dev, FAR const uint8_t *mac);
CODE int (*ioctl)(FAR struct netdev_lowerhalf_s *dev, int cmd, unsigned long arg);
CODE void (*reclaim)(FAR struct netdev_lowerhalf_s *dev);
}
接口名 | 功能描述 | 实现要求 |
---|---|---|
ifup | 启动网络设备(如初始化硬件、使能中断) | 必选 |
ifdown | 关闭网络设备(如禁用硬件、释放资源) | 必选 |
transmit | 发送数据包(从上层接收 netpkt_t 并通过硬件发送) | 必选 |
receive | 接收数据包(从硬件读取数据并封装为 netpkt_t) | 必选 |
addmac/rmmac | 添加 / 移除组播 MAC 地址(用于 MAC 地址过滤) | 可选(无需过滤则不实现) |
ioctl | 处理控制命令(如配置 MTU、查询设备状态) | 可选(无线设备建议实现) |
reclaim | 资源回收(缓冲区耗尽时触发,辅助释放资源) | 可选(及时释放缓冲区则无需实现) |
2. 无线设备扩展接口(struct wireless_ops_s
)
针对无线网络设备,需额外实现无线操作接口,支持连接管理、参数配置等功能:
struct wireless_ops_s
{
// 连接/断开无线网络
CODE int (*connect)(FAR struct netdev_lowerhalf_s *dev);
CODE int (*disconnect)(FAR struct netdev_lowerhalf_s *dev);
// 读写无线参数(ESSID、密码、信道等)
iw_handler_rw essid;
iw_handler_rw bssid;
iw_handler_rw passwd;
iw_handler_rw mode;
iw_handler_rw auth;
iw_handler_rw freq;
iw_handler_rw bitrate;
iw_handler_rw txpower;
iw_handler_rw country;
iw_handler_rw sensitivity;
iw_handler_rw scan;
iw_handler_ro range;
};
3. 网络设备的核心结构体 (netdev_lowerhalf_s
)
struct netdev_lowerhalf_s
{
FAR const struct netdev_ops_s *ops;
FAR const struct wireless_ops_s *iw_ops;
atomic_int quota[NETPKT_TYPENUM]; /* Max # of buffer held by driver */
...
};
驱动适配 API
以下是网络设备适配的主要 API:
int netdev_lower_register(FAR struct netdev_lowerhalf_s *dev,
enum net_lltype_e lltype);
int netdev_lower_unregister(FAR struct netdev_lowerhalf_s *dev);
void netdev_lower_carrier_on(FAR struct netdev_lowerhalf_s *dev);
void netdev_lower_carrier_off(FAR struct netdev_lowerhalf_s *dev);
void netdev_lower_rxready(FAR struct netdev_lowerhalf_s *dev);
void netdev_lower_txdone(FAR struct netdev_lowerhalf_s *dev);
4. 数据包操作接口(NetPKT
)
NetPKT Buffer 结构:
NetPKT 是 transmit 和 receive 接口与上层交换网络报文使用的数据结构,核心包括:
netpkt_alloc/netpkt_free
:分配 / 释放数据包缓冲区;netpkt_copyin/netpkt_copyout
:拷贝数据到缓冲区 / 从缓冲区拷贝数据(适用于非连续内存场景);netpkt_getdata
:直接获取缓冲区地址(适用于连续内存,减少拷贝开销)。
缓冲区默认大小为 1518
字节(可配置为 128~1600 字节),需根据硬件最大支持的帧长调整。
Buffer 接口
以下是与 NetPKT Buffer 操作相关的接口定义:
#define NETPKT_BUFLEN 1518 /* 不同产品上配置不同,128 ~ 1600 均有可能 */
enum netpkt_type_e
{
NETPKT_TX,
NETPKT_RX,
NETPKT_TYPENUM
};
FAR netpkt_t *netpkt_alloc(FAR struct netdev_lowerhalf_s *dev,
enum netpkt_type_e type);
void netpkt_free(FAR struct netdev_lowerhalf_s *dev, FAR netpkt_t *pkt,
enum netpkt_type_e type);
/* 与pkt buffer互相copy数据,任何场景可用,无需关心内部buffer结构 */
int netpkt_copyin(FAR struct netdev_lowerhalf_s *dev, FAR netpkt_t *pkt,
FAR const uint8_t *src, unsigned int len, int offset);
int netpkt_copyout(FAR struct netdev_lowerhalf_s *dev, FAR uint8_t *dest,
FAR const netpkt_t *pkt, unsigned int len, int offset);
/* 直接获取buffer中的地址,直接操作单个连续buffer(用来减少一次copy)时使用 */
FAR uint8_t *netpkt_getdata(FAR struct netdev_lowerhalf_s *dev,
FAR netpkt_t *pkt);
FAR uint8_t *netpkt_getbase(FAR netpkt_t *pkt);
/* 当前data长度 */
void netpkt_setdatalen(FAR struct netdev_lowerhalf_s *dev,
FAR netpkt_t *pkt, unsigned int len);
unsigned int netpkt_getdatalen(FAR struct netdev_lowerhalf_s *dev,
FAR netpkt_t *pkt);
/* 重设data起始点 */
void netpkt_reset_reserved(FAR struct netdev_lowerhalf_s *dev,
FAR netpkt_t *pkt, unsigned int len);
/* 判断是否连续(数据在一个还是多个buffer里) */
bool netpkt_is_fragmented(FAR netpkt_t *pkt);
五、驱动实现关键步骤
以无线网卡为例,驱动实现需完成数据结构定义、发送 / 接收流程处理及设备注册,以下是核心步骤与示例代码。
1. 驱动数据结构定义与初始化
首先定义驱动私有数据结构,包含设备句柄、硬件状态等信息,并初始化操作接口:
// 私有数据结构(包含设备句柄及硬件状态)
struct chip_priv_s {
struct netdev_lowerhalf_s dev; // 继承基础设备结构
void *hw_handle; // 硬件句柄(如寄存器基地址)
bool is_connected; // 连接状态标识
// ... 其他硬件相关状态(如信号强度、信道等)
};
// 实现设备操作接口
static const struct netdev_ops_s g_ops = {
.ifup = chip_ifup,
.ifdown = chip_ifdown,
.transmit = chip_transmit,
.receive = chip_receive,
.ioctl = chip_ioctl // 无线设备实现ioctl处理WAPI命令
};
// 驱动注册函数
int chip_netdev_init(struct chip_priv_s *priv) {
struct netdev_lowerhalf_s *dev = &priv->dev;
dev->ops = &g_ops; // 绑定操作接口
// 配置缓冲区配额(同时持有最大缓冲区数)
dev->quota[NETPKT_TX] = 4; // 发送缓冲区配额
dev->quota[NETPKT_RX] = 4; // 接收缓冲区配额
// 注册设备到系统
return netdev_lower_register(dev, NET_LL_ETHERNET);
}
2. 数据包发送实现(transmit
接口)
发送流程需处理缓冲区连续性(物理内存可能不连续),并在发送完成后释放资源:
struct <chip>_txhead_s; /* 假定硬件在数据前需要一些头部 */
static int <chip>_transmit(FAR struct netdev_lowerhalf_s *dev, FAR netpkt_t *pkt)
{
FAR struct <chip>_priv_s *priv = (FAR struct <chip>_priv_s *)dev;
unsigned int len = netpkt_getdatalen(dev, pkt);
if (netpkt_is_fragmented(pkt))
{
/* 内存不连续,需要用Copy出来的方式,直接向devbuf中copy L2数据 */
uint8_t devbuf[1600];
netpkt_copyout(dev, devbuf + sizeof(struct <chip>_txhead_s), pkt, len, 0);
/* Transmit */
}
else
{
/* 内存连续情况下也可以直接使用buffer(可选,可以一直用上面的分支copy) */
FAR uint8_t *databuf = netpkt_getdata(dev, pkt);
FAR uint8_t *devbuf = databuf - sizeof(struct <chip>_txhead_s);
/* 检查是否越界 或 不符合硬件对齐要求(取决于网卡硬件要求,可省略) */
if (devbuf < netpkt_getbase(pkt) || check_align(devbuf) != OK)
{
/* Fail or fallback to copyout */
}
/* Transmit */
}
return OK;
}
static void <chip>_txdone_interrupt(FAR struct <chip>_priv_s *priv)
{
FAR struct netdev_lowerhalf_s *dev = &priv->dev;
/* 驱动进行一些处理(如果需要) */
/* 释放buffer并通知上层 */
netpkt_free(dev, pkt, NETPKT_TX);
netdev_lower_txdone(dev);
}
3. 数据包接收实现(receive
接口)
接收流程需从硬件读取数据并封装为 netpkt_t
,再通知上层处理:
struct <chip>_rxhead_s;
/* 中断处理 */
static void <chip>_rxready_interrupt(FAR struct <chip>_priv_s *priv)
{
FAR struct netdev_lowerhalf_s *dev = &priv->dev;
netdev_lower_rxready(dev);
}
/* 数据包接收 */
static FAR netpkt_t *<chip>_receive(FAR struct netdev_lowerhalf_s *dev)
{
/* 也可以提前申请pkt,提前收完数据再调用rxready并通过receive返回 */
FAR netpkt_t *pkt = netpkt_alloc(dev, NETPKT_RX);
if (pkt)
{
#if NETPKT_BUFLEN < 15xx
uint8_t devbuf[1600];
/* 从src进行copy的方式,len对应L2的完整数据长度 */
len = receive_data_into(devbuf);
netpkt_copyin(dev, pkt, devbuf + sizeof(struct <chip>_rxhead_s), len, 0);
#else
/* 直接写入pkt对应buffer的方式,len对应L2的完整数据长度 */
len = receive_data_into(netpkt_getbase(pkt));
netpkt_resetreserved(&priv->dev, pkt, sizeof(struct <chip>_rxhead_s));
netpkt_setdatalen(&priv->dev, pkt, len);
#endif
}
return pkt;
}
4. 无线功能扩展(WAPI 命令对接)
WAPI(Wireless Application Protocol Interface) 命令依赖驱动的 ioctl() 接口实现。上层通过 WAPI 命令设置或获取 Wi-Fi 参数,控制 Wi-Fi 的行为。常用功能已被抽象为接口,在初始化时将其设置到 dev->iw_ops 即可。
WAPI 接口定义
typedef int (*iw_handler_rw)(FAR struct netdev_lowerhalf_s *dev,
FAR struct iwreq *iwr, bool set);
typedef int (*iw_handler_ro)(FAR struct netdev_lowerhalf_s *dev,
FAR struct iwreq *iwr);
struct wireless_ops_s
{
/* Connect / disconnect operation, should exist if essid or bssid exists */
int (*connect)(FAR struct netdev_lowerhalf_s *dev);
int (*disconnect)(FAR struct netdev_lowerhalf_s *dev);
/* The following attributes need both set and get. */
iw_handler_rw essid;
iw_handler_rw bssid;
iw_handler_rw passwd;
iw_handler_rw mode;
iw_handler_rw auth;
iw_handler_rw freq;
iw_handler_rw bitrate;
iw_handler_rw txpower;
iw_handler_rw country;
iw_handler_rw sensitivity;
/* Scan operation: start scan (set=1) / get scan result (set=0). */
iw_handler_rw scan;
/* Get-only attributes. */
iw_handler_ro range;
};
说明:
- 驱动需要完成列表中所有 WAPI 命令的适配,完成上述接口后,WAPI 命令即可用于验证。
WAPI 命令使用示例
以下是 WAPI 命令的常见使用场景:
AP 模式
wapi disconnect wlan0
ifup wlan0
wapi mode wlan0 3
wapi psk wlan0 <psk> 3
wapi essid wlan0 <ssid> 1
dhcpd wlan0 &
STA模式
- 通过 ESSID 连接:
# 配置STA模式并连接指定AP
ifup wlan0 # 启动设备
wapi mode wlan0 2 # 设置为STA模式
wapi psk wlan0 "password" 3 # 配置WPA2密码(加密类型3为CCMP)
wapi essid wlan0 "MyAP" 1 # 连接ESSID为"MyAP"的AP
renew wlan0 # 获取IP地址
- 通过 BSSID 连接:
ifup wlan0
wapi mode wlan0 2
wapi psk wlan0 <psk> 3
wapi freq wlan0 1 1
wapi ap wlan0 <bssid>
renew wlan0
配置保存与加载
wapi save_config wlan0 # 保存当前配置到 wapi.conf
wapi reconnect wlan0 # 从 wapi.conf 加载配置并重新联网
六、高级功能:双网卡(AP/STA 共存)实现
openvela 支持同时启用 AP 和 STA 模式(双网卡),实现设备既作为接入点(AP)供其他设备连接,又作为站点(STA)接入外部网络。核心实现要点:
- 驱动初始化:注册两个网络设备(如 wlan0 为 STA,wlan1 为 AP),分别绑定不同的操作接口;
- 功能隔离:
- STA 模式:启用 DHCP 客户端,从外部 AP 获取 IP;
- AP 模式:启用 DHCP 服务器,为接入设备分配 IP;
- 独立性保证:两个接口的状态(如 ifup/ifdown)互不影响,各自处理数据收发。
七、测试与调试工具
驱动开发完成后,需通过工具验证功能正确性:
ping
:测试网络连通性,支持自定义数据包大小和发送间隔;
ping -c 50 -i 100 -s 1400 -W 200 www.xiaomi.com # 发送50个1400字节数据包,间隔100ms
- iperf:测试网络吞吐量,验证带宽性能;
- tcpdump:抓包分析工具,用于定位数据包收发异常;
- 日志调试:启用 CONFIG_DEBUG_NET 等配置,通过 ninfo/nerr 打印日志,辅助排查问题。
总结
openvela 网络驱动框架通过分层设计和标准化接口,大幅降低了硬件适配难度。开发者只需聚焦于硬件交互逻辑,实现核心的 transmit/receive 接口,并根据需求扩展无线功能,即可快速完成网络驱动开发。结合测试工具的调试,可确保驱动的稳定性和性能。

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐
所有评论(0)