网卡移植拼图的最后一块就是完成网卡接收函数,把网卡收到的数据推送给协议栈 。其伪代码实现如下:
//* 网卡接收函数,可以是接收中断服务子函数,也可以是普通函数,普通函数必须确保能够在数据到达的第一时间就能读取并推送给协议栈void ethernet_recv(void){EN_ONPSERR enErr;unsigned int unPacketLen;unsigned char *pubRcvedPacket;//* 在这里添加与具体网卡相关的代码,等待接收报文到达,如果数据到达将报文长度赋值unPacketLen变量,将报文首地址赋值给pubRcvedPacket…………//* 读取到达报文并将其推送给协议栈进行处理,首先利用协议栈mmu模块动态申请一块内存用于保存到达的报文unsigned char *pubPacket = (UCHAR *)buddy_alloc(sizeof(ST_SLINKEDLIST_NODE) + unPacketLen, &enErr);//* 申请成功,根据协议栈要求,刚才申请的内存按照PST_SLINKEDLIST_NODE链表节点方式组织并保存刚刚收到的报文PST_SLINKEDLIST_NODE pstNode = (PST_SLINKEDLIST_NODE)pubPacket;pstNode->uniData.unVal = unPacketLen;memcpy(pubPacket + sizeof(ST_SLINKEDLIST_NODE), (UCHAR *)pubRcvedPacket, unPacketLen);//* 将上面组织好的报文节点放入接收链表,这个接收链表由协议栈管理,ethernet_put_packet()函数由协议栈提供//* thread_ethernet_ii_recv()接收线程负责等待ethernet_put_packet()函数投递的信号并读取这个链表//* 参数l_pstNetifEth为前面注册网卡时由协议栈返回的PST_NETIF指针值ethernet_put_packet(l_pstNetifEth, pstNode);}
其中buddy_alloc()函数在功能上与c语言的标准库函数malloc()完全相同,都是动态分配一块指定大小的内存给调用者使用,使用完毕后再由用户通过buddy_free()函数释放 。这两个函数由协议栈的内存管理(mmu)模块提供 。ethernet_put_packet()函数需要重点解释一下 。这个函数由协议栈提供 。它完成的工作非常重要,它在网卡接收函数与协议栈以太网接收线程thread_ethernet_ii_recv()之间搭建了一个数据流通的“桥” 。接收函数收到报文后按照协议栈要求,将报文封装成ST_SLINKEDLIST_NODE类型的链表节点,然后传递给ethernet_put_packet()函数 。该函数将立即把传递过来的节点挂载到由协议栈管理的以太网接收链表的尾部,然后投递一个“有新报文到达”的信号量 。前文提到的以太网接收线程thread_ethernet_ii_recv()会轮询等待这个信号量 。一旦信号到达,接收线程将立即读取链表并取出报文交给协议栈处理 。
至此,ethernet网卡相关的移植工作完成 。
4.2 ppp拨号网卡在Linux系统,2g/4g/5g模块作为一个通讯终端,驱动层会把它当作一个tty设备来看待 。Linux下ppp栈也是围绕着操作一个标准的tty设备来实现底层通讯逻辑的 。至于如何操作这个ppp拨号终端进行实际的数据收发tty层并不关心 。所以,协议栈完全借鉴了这个成功的设计思想,在底层驱动与拨号终端之间增加了一个tty层,将具体的设备操作与上层的业务逻辑进行了剥离 。ppp拨号网卡的的移植工作其实就是完成tty层到底层驱动的封装工作 。协议栈利用句柄来唯一的标识一个tty设备 。在os_datatype.h文件中定义了这个句柄类型:
#if SUPPORT_PPPtypedef INT HTTY;//* tty终端句柄#define INVALID_HTTY -1 //* 无效的tty终端句柄#endif
这个句柄类型非常重要,所有与tty操作相关的函数都要用到这个句柄类型 。tty层要完成的驱动封装工作涉及的函数原型定义依然是在os_adapter.h文件中:
#if SUPPORT_PPP//* 打开 tty 设备,返回 tty 设备句柄,参数 pszTTYName 指定要打开的 tty 设备的名称OS_ADAPTER_EXT HTTY os_open_tty(const CHAR *pszTTYName);//* 关闭 tty 设备,参数 hTTY 为要关闭的 tty 设备的句柄OS_ADAPTER_EXT void os_close_tty(HTTY hTTY);//* 向 hTTY 指定的 tty 设备发送数据,返回实际发送的数据长度//*hTTY:设备句柄//*pubData:指针,指向要发送的数据的指针//* nDataLen:要发送的数据长度//* 返回值为实际发送的字节数OS_ADAPTER_EXT INT os_tty_send(HTTY hTTY, UCHAR *pubData, INT nDataLen);//* 从参数 hTTY 指定的 tty 设备等待接收数据,阻塞型//*hTTY:设备句柄//*pubRcvBuf:指针,指向数据接收缓冲区的指针,用于保存收到的数据//* nRcvBufLen:接收缓冲区的长度//*nWaitSecs:等待的时长,单位:秒 。0 一直等待;直至收到数据或报错,大于 0,等待指定秒数;小于 0,不支持//* 返回值为实际收到的数据长度,单位:字节OS_ADAPTER_EXT INT os_tty_recv(HTTY hTTY, UCHAR *pubRcvBuf, INT nRcvBufLen, INT nWaitSecs); //* 复位 tty 设备,这个函数名称体现了2g/4g/5g模块作为tty设备的特殊性,其功能从本质上看就是一个 modem,modem 设备出现通讯//* 故障时,最好的修复故障的方式就是直接复位,复位可以修复绝大部分的因软件问题产生的故障OS_ADAPTER_EXT void os_modem_reset(HTTY hTTY);#endif
经验总结扩展阅读
- 2 onps栈移植说明——编译器及os适配层移植
- 1 onps栈移植说明——onps栈的配置及裁剪
- 亲爱的客栈杨紫乔欣是哪期?
- <一>从指令角度了解函数堆栈调用过程
- stm32h750移植lvgl
- 3 Python全栈工程师之从网页搭建入门到Flask全栈项目实战 - 入门Flask微框架
- 都卷Java,你看看你得学多少技术栈才能工作!
- flutter系列之:flutter中可以建索引的栈布局IndexedStack
- 官栈花胶多久吃一次 官栈即食花胶要加热吗
- 官栈即食花胶是不是真的 官栈即食花胶有激素吗