当前位置: 首页 > article >正文

【LwIP源码学习5】网口接收数据处理过程

前言

本文对lwip中TCPIP_MSG_INPKT类型消息的处理过程进行分析。

正文

在 【LwIP源码学习4】主线程tcpip_thread一文中提到。lwip主线程tcpip_thread会先从邮箱tcpip_mbox中取出消息,然后调用tcpip_thread_handle_msg函数根据消息类型对消息进行处理。

static void
tcpip_thread_handle_msg(struct tcpip_msg *msg)
{
  switch (msg->type) {
  ......
    case TCPIP_MSG_INPKT:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg));
      if (msg->msg.inp.input_fn(msg->msg.inp.p, msg->msg.inp.netif) != ERR_OK) {
        pbuf_free(msg->msg.inp.p);
      }
      memp_free(MEMP_TCPIP_MSG_INPKT, msg);
      break;
  ......
  }
}

在判断到消息类型msg->typeTCPIP_MSG_INPKT时,会执行这个消息对应的msg->msg.inp.input_fn函数,并传入参数msg->msg.inp.pmsg->msg.inp.netif
TCPIP_MSG_INPKT类型的消息是在tcpip_inpkt函数中被创建的。

err_t
tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn)
{
......
  msg->type = TCPIP_MSG_INPKT;
  msg->msg.inp.p = p;//网卡接收到的数据
  msg->msg.inp.netif = inp;//网卡对应的结构体
  msg->msg.inp.input_fn = input_fn;//处理本包数据的函数
  //向tcpip_mbox邮箱中发送这个消息
  if (sys_mbox_trypost(&tcpip_mbox, msg) != ERR_OK) {
    memp_free(MEMP_TCPIP_MSG_INPKT, msg);
    return ERR_MEM;
  }
......
}

tcpip_inpkt函数在tcpip_input函数中被调用

err_t
tcpip_input(struct pbuf *p, struct netif *inp)
{
#if LWIP_ETHERNET
  //判断是否支持ARP协议
  if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
    return tcpip_inpkt(p, inp, ethernet_input);
  } else
#endif /* LWIP_ETHERNET */
    return tcpip_inpkt(p, inp, ip_input);
}

如果判断相应网卡支持ARP就将ethernet_input函数设置为这包数据的处理函数。
如果不支持就把ip_input设置为处理函数,ip_input就是ip4_input函数,将这包数据作为IP数据报进行处理。
接下来是ethernet_input部分源码:

err_t
ethernet_input(struct pbuf *p, struct netif *netif)
{
  struct eth_hdr *ethhdr;
  u16_t type;
......
  //获取网卡接受数据的以太网首部
  ethhdr = (struct eth_hdr *)p->payload;
......
  //在以太网首部中获取本包数据的类型
  type = ethhdr->type;
......
  switch (type) {
    ......
    case PP_HTONS(ETHTYPE_IP):  //如果本包数据是IP数据报
    ......    
      ip4_input(p, netif);
    ...
      break;
    case PP_HTONS(ETHTYPE_ARP): //如果本包数据是ARP数据报
    ......
      etharp_input(p, netif);
    ...
      break;
    ......
    default:
    ...
    }

  return ERR_OK;
}

以上代码中先从数据获取以太网首部,结构体eth_hdr内容为:

struct eth_hdr {
  PACK_STRUCT_FLD_S(struct eth_addr dest);
  PACK_STRUCT_FLD_S(struct eth_addr src);
  PACK_STRUCT_FIELD(u16_t type);
} PACK_STRUCT_STRUCT;

刚好对应以太网帧结构:
在这里插入图片描述
然后获取本包数据类型,并进行相应的处理。

再回到tcpip_input函数。
TCPIP_Init函数中的

netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, &ethernetif_init, &tcpip_input);

tcpip_input与网卡绑定起来,其中gnetif是网卡对应结构体,ipaddr是IP地址,netmask是子网掩码,gw是网关,ethernetif_init是网卡的初始化函数,tcpip_input是网卡消息处理函数。
netif_add函数里通过

netif->input = input;

完成绑定。之后netif->input就代表了tcpip_input函数。
ethernetif_input函数中取出网卡数据,并调用netif->input进行处理。

void ethernetif_input(void *pParams) {
	struct netif *netif;
	struct pbuf *p = NULL;
	netif = (struct netif*) pParams;
  LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
  
	while(1) 
  {
    //等待信号量s_xSemaphore,有说明网卡有数据需要处理
    if(xSemaphoreTake( s_xSemaphore, portMAX_DELAY ) == pdTRUE)
    {
      ......
      p = low_level_input(netif);//取出网卡接受到的数据
      ...
      if(p != NULL)
      {
        ...
        //调用tcpip_input函数进行处理
        if (netif->input(p, netif) != ERR_OK)
        {
          LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
          pbuf_free(p);
          p = NULL;
        }
       ...
      }
    }
  }
}

ethernetif_input函数在low_level_init函数中被创建成一个任务的执行函数。它的主要任务是等待信号量s_xSemaphore,有信号量说明网卡接受到了数据,然后取出数据做进一步处理。
s_xSemaphore信号量是在以太网接受完数据触发中断后,在回调函数里释放的。

void HAL_ETH_RxCpltCallback(ETH_HandleTypeDef *heth)
{
  portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
  xSemaphoreGiveFromISR( s_xSemaphore, &xHigherPriorityTaskWoken );
  portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

以上分析涉及到lwip处理数据的3个任务:

任务功能
网口接受数据完成中断任务用于释放网卡有数据需要处理的信号。
ethernetif_input任务用于等待网卡有消息信号,并从网卡取出数据封装成消息放入邮箱中。
tcpip_thread任务用于从邮箱中取出消息,并做进一步协议栈的处理(ARP、IP、TCP、UDP)。

在这里插入图片描述


http://www.kler.cn/a/379681.html

相关文章:

  • DCN网络进行新冠肺炎影像分类
  • 使用AWS Lambda构建无服务器应用程序
  • LeetCode46. 全排列(2024秋季每日一题 57)
  • 云轴科技ZStack在CID大会上分享VF网卡热迁移技术
  • PHP JSON 教程
  • YOLOv11改进策略【卷积层】| CGblock 内容引导网络 利用不同层次信息,提高多类别分类能力 (含二次创新)
  • 【Python+Pycharm】2024-Python安装配置教程
  • STM32:IIC详解
  • opencv学习笔记(6):图像预处理(直方图、图像去噪)
  • Git 常用命令与开发流程总结
  • 【优选算法】——二分查找!
  • C++转python语法训练 算法模板02
  • Arduino平台软硬件原理及使用——热释电传感器的使用
  • gRPC-集成Springboot
  • 001-Kotlin界面开发之Jetpack Compose Desktop学习路径
  • 并发编程(6)——future、promise、async,线程池
  • 【Mars3d】targetPosition支持动态属性坐标
  • ctfshow——web(总结持续更新)
  • 《向量数据库指南》——BGE-M3:引领多模态RAG系统新风尚!
  • Docker容器消耗资源过多导致宿主机死机解决方案
  • openGauss开源数据库实战十五
  • 企业数据泄露安全演练(分享)
  • 飞牛OS在Docker中安装ODOO ERP系统
  • 书签管理工具使用技巧
  • Transformer和BERT的区别
  • Springboot 整合 Java DL4J 实现情感分析系统