Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
SAL
【rt-thread网络连载】第4篇:源码分析之网卡数据的接收和发送
发布于 2023-05-01 11:44:29 浏览:887
订阅该版
[tocm] # 一、lwip网卡接口ethernetif.c ethernetif.c是lwip的网卡接口,在该接口中处理网卡的数据接收和发送,rt-thread在该接口文件中抽象了一个eth_device,管理网络数据的收发和向内核的netdev_list添加netdev。 # 二、网络设备eth_device eth_device是rt-thread实现的ethernetif。 ```c struct eth_device { /* inherit from rt_device */ struct rt_device parent; /* network interface for lwip */ struct netif *netif; struct rt_semaphore tx_ack; rt_uint16_t flags; rt_uint8_t link_changed; rt_uint8_t link_status; /* eth device interface */ struct pbuf* (*eth_rx)(rt_device_t dev); rt_err_t (*eth_tx)(rt_device_t dev, struct pbuf* p); }; ``` netif:lwip的网络接口。 eth_rx:底层数据接收接口。 eth_tx:底层数据发送接口。 # 三、网络设备数据的接收和发送 网络设备的接收和发送通过eth_device的eth_rx和eth_tx完成。在系统初始化时内核调用eth_system_device_init创建erx和etx两个线程,用于处理接收和发送。 ## 3.1 数据接收 当erx线程起来后,等待eth_rx_thread_mb,当网卡准备好或者改变网卡状态时,往下执行,进入while(1)处理网卡接收,调用网卡注册的eth_rx接收网卡数据,并传递给协议栈上层。 ```c static void eth_rx_thread_entry(void* parameter) { struct eth_device* device; while (1) { if (rt_mb_recv(ð_rx_thread_mb, (rt_ubase_t*)&device, RT_WAITING_FOREVER) == RT_EOK) { struct pbuf *p; /* check link status */ if (device->link_changed) { int status; rt_uint32_t level; level = rt_hw_interrupt_disable(); status = device->link_status; device->link_changed = 0x00; rt_hw_interrupt_enable(level); if (status) netifapi_netif_set_link_up(device->netif); else netifapi_netif_set_link_down(device->netif); } /* receive all of buffer */ while (1) { if(device->eth_rx == RT_NULL) break; p = device->eth_rx(&(device->parent)); if (p != RT_NULL) { /* notify to upper layer */ if( device->netif->input(p, device->netif) != ERR_OK ) { LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: Input error\n")); pbuf_free(p); p = NULL; } } else break; } } else { LWIP_ASSERT("Should not happen!\n",0); } } } ``` ## 3.2 数据发送 当协议栈需要发送数据时,调用netif的linkoutput接口,在linkoutput中,将数据封装成消息发送给etx线程,最终通过eth_device的eth_tx接口将数据发送出去。 ```c static void eth_tx_thread_entry(void* parameter) { struct eth_tx_msg* msg; while (1) { if (rt_mb_recv(ð_tx_thread_mb, (rt_ubase_t*)&msg, RT_WAITING_FOREVER) == RT_EOK) { struct eth_device* enetif; RT_ASSERT(msg->netif != RT_NULL); RT_ASSERT(msg->buf != RT_NULL); enetif = (struct eth_device*)msg->netif->state; if (enetif != RT_NULL) { /* call driver's interface */ if (enetif->eth_tx(&(enetif->parent), msg->buf) != RT_EOK) { /* transmit eth packet failed */ } } /* send ACK */ rt_sem_release(&(enetif->tx_ack)); } } } ``` # 四、wlan设备数据的接收和发送 wlan设备的数据接收和发送是通过rt_wlan_prot完成的,rt_wlan_prot是对不同协议簇的抽象,在rt_wlan_set_mode启动wlan设备时,**最终会调用rt_wlan_prot_attach将lwip协议簇挂载到wlan设备**,在挂载过程中,会根据协议簇的名称匹配注册的rt_wlan_prot,并调用rt_wlan_prot的register将wlan设备注册到内核。 ## 4.1 数据接收 在网卡的接收中断中,调用rt_wlan_dev_transfer_prot将网卡设备接收的数据传递给上层处理。 ```c rt_err_t rt_wlan_dev_transfer_prot(struct rt_wlan_device *wlan, void *buff, int len) { struct rt_wlan_prot *prot = wlan->prot; if (prot != RT_NULL) { return prot->ops->prot_recv(wlan, buff, len); } return -RT_ERROR; } ``` 注册到rt_wlan_prot的接收函数是rt_wlan_lwip_protocol_recv,在rt_wlan_lwip_protocol_recv中,通过lwip_prot_des获取eth_device(netif在eth_device_init_with_flag中被注册到eth_device),接着便可使用其中的netif的input将数据交给上层。 ```c static rt_err_t rt_wlan_lwip_protocol_recv(struct rt_wlan_device *wlan, void *buff, int len) { struct eth_device *eth_dev = &((struct lwip_prot_des *)wlan->prot)->eth;//eth在rt_wlan_lwip_protocol_register中注册 struct pbuf *p = RT_NULL; //...省略 { p = buff; if ((eth_dev->netif->input(p, eth_dev->netif)) != ERR_OK) { return -RT_ERROR; } return RT_EOK; } //...省略 } ``` ## 4.2 数据发送 当协议栈需要发送数据时,会调用rt_wlan_prot_transfer_dev将数据传递给wlan设备,在rt_wlan_prot_transfer_dev中,会调用wlan设备的发送接口将数据发送出去。 ## 4.3 以w601举例 w601的wlan驱动在driver文件夹下的drv_wifi.c。 ### 4.3.1 数据接收 在内核需要初始化wlan的时候,调用drv_wlan_init,在tls_ethernet_data_rx_callback函数中注册wm_ethernetif_input到wifi接收中断,在wm_ethernetif_input中调用rt_wlan_dev_transfer_prot将接收的数据传给协议栈上层。 ### 4.3.2 数据发送 在drv_wifi.c中,将drv_wlan_send函数注册到发送接口,在协议栈需要发送数据时,通过rt_wlan_prot_transfer_dev调用这个接口完成数据的发送。 ```c static const struct rt_wlan_dev_ops ops = { //...省略 .wlan_recv = drv_wlan_recv, .wlan_send = drv_wlan_send,/*向内核注册的发送接口*/ }; ``` # 五、参考 [LwIP 之 网络接口 netif(ethernetif.c、netif.c)](https://blog.csdn.net/zhaozhiyuan111/article/details/87934041 "LwIP 之 网络接口 netif(ethernetif.c、netif.c)") [WLAN 设备](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/wlan/wlan "WLAN 设备")
0
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
happycode999
这家伙很懒,什么也没写!
文章
28
回答
6
被采纳
0
关注TA
发私信
相关文章
1
SAL 不支持 PF_PACKET,如何修改可以支持?
2
socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
3
SAL组件好像并不支持DTLS
4
添加SAL组件后socket相关函数均提示declared implicitly
5
sal组件socket等函数无法使用
6
SAL关闭自带的一个socket连接
7
wiznet与libmodbus软件包衔接对接SAL层传参问题
8
sal验证link.rt-thread.org:8101失败
9
请问 AT-SAL,recv() 函数在服务端断开之后没有返回,是设计如此吗?
10
多网卡的出现创建socket失败
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
DMA
USB
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
I2C_IIC
ESP8266
UART
WIZnet_W5500
ota在线升级
cubemx
PWM
flash
freemodbus
BSP
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
编译报错
中断
Debug
rt_mq_消息队列_msg_queue
keil_MDK
SFUD
msh
ulog
C++_cpp
MicroPython
本月问答贡献
三世执戟
5
个答案
1
次被采纳
KunYi
4
个答案
1
次被采纳
RTT_逍遥
3
个答案
1
次被采纳
xiaorui
1
个答案
1
次被采纳
JonasWen
1
个答案
1
次被采纳
本月文章贡献
出出啊
1
篇文章
3
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
3
次点赞
crystal266
2
篇文章
2
次点赞
whj467467222
2
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部