外观
网卡裸驱模式操作指南
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 */