外观
网卡裸驱模式操作指南
2025-11-13
功能概述
网卡裸驱模式允许应用程序直接处理网络数据帧,绕过协议栈实现高性能网络通信。
API 说明
设置网卡为裸驱模式
#define SET_CALBACK 0x10
rt_device_control("name", SET_CALBACK, recv_callback);参数说明:
- name: 网卡名
- recv_callback: 回调函数,网卡收到数据时会在中断中调用该函数(请不要在该回调函数中做数据解析)。
注意:如果 recv_callback 等于 NULL 裸驱模式将不会被设置
取消网卡裸驱模式
rt_device_control("name", SET_CALBACK, NULL);参数说明:
- name: 网卡名
在裸驱模式下发送数据
rt_device_write("name", 0, data, len);参数说明:
- name: 网卡名
- data: 要发送的数据
- len: 发送数据的长度
注意:单包数据长度不应超过标准以太网 MTU(1518 字节)
在裸驱模式下接收数据
len = rt_device_read("name", 0, recv_data, size);参数说明:
- name: 网卡名
- recv_data: 接收数据的 buffer
- size: recv_data 的大小,最大接收数据的长度
- len: 实际接收的数据长度
网卡裸驱收发示例
功能描述
本示例演示了如何在裸驱模式下使用网卡接收和发送数据。
代码实现
#include <rtthread.h>
#include <rtdevice.h>
/* 假设网卡设备名为 "e0" */
#define NET_DEVICE_NAME "e0"
/* 自定义控制命令:设置裸驱回调函数 */
#define SET_CALLBACK 0x10
/* 网络帧缓冲区结构 */
struct net_frm_buf
{
rt_uint8_t buffer[1540]; /* 数据缓冲区,略大于MTU */
rt_size_t len; /* 数据长度 */
rt_bool_t valid; /* 数据是否有效 */
};
static struct net_frm_buf netfrmbuf[100]; /* 循环缓冲区 */
static volatile int netfrm_head = 0; /* 缓冲区头部索引(读取位置) */
static volatile int netfrm_tail = 0; /* 缓冲区尾部索引(写入位置) */
static volatile rt_uint32_t data_count = 0; /* 有效数据包计数 */
static rt_device_t net_dev = RT_NULL; /* 网络设备句柄 */
static rt_timer_t print_timer = RT_NULL; /* 打印定时器 */
/* 接收回调函数 - 在中断上下文中被调用 */
static void recv_callback(void *dev)
{
rt_base_t level;
// 从设备读取数据
rt_ssize_t len = rt_device_read(net_dev, 0, netfrmbuf[netfrm_tail].buffer,
sizeof(netfrmbuf[netfrm_tail].buffer));
if (len > 0)
{
netfrmbuf[netfrm_tail].len = len;
netfrmbuf[netfrm_tail].valid = RT_TRUE;
/* 更新尾部索引,循环使用缓冲区 */
int next_tail = (netfrm_tail + 1) % (sizeof(netfrmbuf) / sizeof(netfrmbuf[0]));
/* 检查缓冲区是否已满 */
if (next_tail != netfrm_head)
{
netfrm_tail = next_tail;
data_count++;
}
else
{
/* 缓冲区满,丢弃最旧的数据 */
netfrm_head = (netfrm_head + 1) % (sizeof(netfrmbuf) / sizeof(netfrmbuf[0]));
netfrm_tail = next_tail;
rt_kprintf("Warning: Receive buffer full, dropped oldest packet.\n");
}
}
}
/* 定时器回调函数 - 每隔1ms打印接收到的数据 */
static void print_timer_callback(void *parameter)
{
rt_base_t level;
static rt_uint32_t last_count = 0;
/* 检查是否有新数据 */
if (data_count > last_count)
{
/* 遍历所有有效数据并打印 */
while (netfrm_head != netfrm_tail)
{
if (netfrmbuf[netfrm_head].valid)
{
rt_kprintf("[%d] Received %d bytes: ", data_count, netfrmbuf[netfrm_head].len);
/* 打印前16字节 */
rt_size_t print_len = netfrmbuf[netfrm_head].len > 16 ? 16 : netfrmbuf[netfrm_head].len;
for (rt_size_t i = 0; i < print_len; i++)
{
rt_kprintf("%02X ", netfrmbuf[netfrm_head].buffer[i]);
}
if (netfrmbuf[netfrm_head].len > 16)
{
rt_kprintf("...");
}
rt_kprintf("\n");
/* 标记数据为已处理 */
netfrmbuf[netfrm_head].valid = RT_FALSE;
netfrm_head = (netfrm_head + 1) % (sizeof(netfrmbuf) / sizeof(netfrmbuf[0]));
}
}
last_count = data_count;
}
}
/* 初始化网络裸驱模式 */
int net_raw_driver_init(void)
{
rt_base_t level;
// 查找网络设备
net_dev = rt_device_find(NET_DEVICE_NAME);
if (net_dev == RT_NULL)
{
rt_kprintf("Error: Find network device %s failed!\n", NET_DEVICE_NAME);
return -RT_ERROR;
}
// 打开网络设备
if (rt_device_open(net_dev, RT_DEVICE_FLAG_RDWR) != RT_EOK)
{
rt_kprintf("Error: Open network device %s failed!\n", NET_DEVICE_NAME);
return -RT_ERROR;
}
/* 创建打印定时器 - 周期1ms */
print_timer = rt_timer_create("net_print",
print_timer_callback,
RT_NULL,
RT_TICK_PER_SECOND / 1000, /* 1ms */
RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_SOFT_TIMER);
if (print_timer == RT_NULL)
{
rt_kprintf("Error: Create print timer failed!\n");
rt_device_close(net_dev);
return -RT_ERROR;
}
/* 初始化缓冲区 */
for (int i = 0; i < sizeof(netfrmbuf) / sizeof(netfrmbuf[0]); i++)
{
netfrmbuf[i].valid = RT_FALSE;
netfrmbuf[i].len = 0;
}
netfrm_head = 0;
netfrm_tail = 0;
data_count = 0;
// 设置裸驱回调函数
level = rt_hw_interrupt_disable();
if (rt_device_control(net_dev, SET_CALLBACK, recv_callback) != RT_EOK)
{
rt_hw_interrupt_enable(level);
rt_kprintf("Error: Set raw driver callback failed!\n");
rt_timer_delete(print_timer);
rt_device_close(net_dev);
return -RT_ERROR;
}
rt_hw_interrupt_enable(level);
/* 启动定时器 */
rt_timer_start(print_timer);
rt_kprintf("Network raw driver mode initialized successfully!\n");
rt_kprintf("Printing received data every 1ms...\n");
return RT_EOK;
}
/* 发送网络数据示例 */
int net_raw_driver_send(rt_uint8_t *data, rt_size_t len)
{
if (len > 1518)
{
rt_kprintf("Warning: Packet size (%d) exceeds standard MTU (1518 bytes).\n", len);
}
// 调用设备写接口发送数据
rt_size_t sent_len = rt_device_write(net_dev, 0, data, len);
if (sent_len != len)
{
rt_kprintf("Error: Send data failed, expect %d, sent %d!\n", len, sent_len);
return -RT_ERROR;
}
return RT_EOK;
}
/* 取消网络裸驱模式并清理资源 */
void net_raw_driver_cleanup(void)
{
rt_base_t level;
if (net_dev == RT_NULL)
return;
/* 停止定时器 */
if (print_timer != RT_NULL)
{
rt_timer_stop(print_timer);
rt_timer_delete(print_timer);
print_timer = RT_NULL;
}
// 取消回调设置
level = rt_hw_interrupt_disable();
rt_device_control(net_dev, SET_CALLBACK, RT_NULL);
rt_hw_interrupt_enable(level);
// 关闭设备
rt_device_close(net_dev);
net_dev = RT_NULL;
rt_kprintf("Network raw driver mode cleaned up.\n");
}
/* 导出到 MSH 命令,方便测试 */
#ifdef RT_USING_FINSH
#include <finsh.h>
/* 初始化命令 */
MSH_CMD_EXPORT(net_raw_driver_init, Initialize network raw driver mode);
/* 发送测试数据命令 */
static void net_raw_send_test(int argc, char **argv)
{
rt_uint8_t test_data[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99};
net_raw_driver_send(test_data, sizeof(test_data));
rt_kprintf("Sent test data: %d bytes\n", sizeof(test_data));
}
MSH_CMD_EXPORT(net_raw_send_test, Send test data via raw driver);
/* 清理命令 */
static void net_raw_cleanup(int argc, char **argv)
{
net_raw_driver_cleanup();
}
MSH_CMD_EXPORT(net_raw_cleanup, Cleanup network raw driver mode);
#endif /* RT_USING_FINSH */网卡模式切换示例
功能描述
本示例演示了网卡动态切换裸驱模式与标准 LWIP 模式。
代码实现
#include <rtthread.h>
#include <rtdevice.h>
#include <lwip/pbuf.h>
#include <lwip/netif.h>
#include <stdlib.h>
#define DEV_NAME "e0"
#define RX_BUF_SIZE 1540
#define SET_CALBACK 0x10 // 网卡模式切换命令
// 全局网卡设备 & 接收缓冲区
static rt_device_t g_net_dev = RT_NULL;
static rt_uint8_t rx_buf[RX_BUF_SIZE];
// 裸驱模式接收回调(直接收原始以太网帧)
void eth_raw_callback(void)
{
rt_int32_t len;
// 读取裸驱模式原始数据包
len = rt_device_read(g_net_dev, 0, rx_buf, RX_BUF_SIZE);
if (len > 0)
{
rt_kprintf("[ETH RAW] rx done, length = %d bytes\n", len);
rt_kprintf("[ETH RAW] rx data: ");
for (int i = 0; i < len; i++)
{
rt_kprintf("%02X ", rx_buf[i]);
}
rt_kprintf("\n");
}
else
{
rt_kprintf("[ETH RAW] no data received, len = %d\n", len);
}
}
// 命令:eth_mode_switch 1 切换到 裸驱模式(RAW Driver Mode)
// eth_mode_switch 0 切换到 LWIP 协议栈模式
void eth_mode_switch(int argc, char *argv[])
{
rt_err_t ret;
int mode = 0;
// 查找网卡
g_net_dev = rt_device_find(DEV_NAME);
if (g_net_dev == RT_NULL)
{
rt_kprintf("[ETH] device %s not found!\n", DEV_NAME);
return;
}
// 参数检查
if (argc < 2)
{
rt_kprintf("\nusage: eth_mode_switch <1|0>\n");
rt_kprintf(" 1 - switch to ETH RAW DRIVER mode\n");
rt_kprintf(" 0 - switch to LWIP protocol mode\n\n");
return;
}
// 解析模式
mode = atoi(argv[1]);
if (mode == 1)
{
// 切换到 裸驱模式
ret = rt_device_control(g_net_dev, SET_CALBACK, eth_raw_callback);
if (ret == RT_EOK)
rt_kprintf("[ETH] switch to RAW driver mode success!\n");
else
rt_kprintf("[ETH] switch RAW mode failed! ret=%d\n", ret);
}
else if (mode == 0)
{
// 切换回 LWIP 模式
ret = rt_device_control(g_net_dev, SET_CALBACK, RT_NULL);
if (ret == RT_EOK)
rt_kprintf("[ETH] switch to LWIP mode success!\n");
else
rt_kprintf("[ETH] switch LWIP mode failed! ret=%d\n", ret);
}
else
{
rt_kprintf("[ETH] invalid parameter! only 1 or 0\n");
}
}
MSH_CMD_EXPORT(eth_mode_switch, ETH mode switch: 1=RAW 0=LWIP);