4G杂项:lwip_网卡简述(以8910平台为例)

目录

🍅点击这里查看所有博文

  随着自己工作的进行,接触到的技术栈也越来越多。给我一个很直观的感受就是,某一项技术/经验在刚开始接触的时候都记得很清楚。往往过了几个月都会忘记的差不多了,只有经常会用到的东西才有可能真正记下来。存在很多在特殊情况下有一点用处的技巧,用的不多的技巧可能一个星期就忘了。

  想了很久想通过一些手段把这些事情记录下来。也尝试过在书上记笔记,这也只是一时的,书不在手边的时候那些笔记就和没记一样,不是很方便。

  很多时候我们遇到了问题,一般情况下都是选择在搜索引擎检索相关内容,这样来的也更快一点,除非真的找不到才会去选择翻书。后来就想到了写博客,博客作为自己的一个笔记平台倒是挺合适的。随时可以查阅,不用随身携带。

  同时由于写博客是对外的,既然是对外的就不能随便写,任何人都可以看到。经验对于我来说那就只是经验而已,公布出来说不一定我的一些经验可以帮助到其他的人。遇到和我相同问题时可以少走一些弯路。

  既然决定了要写博客,那就只能认真去写。不管写的好不好,尽力就行。千里之行始于足下,一步一个脚印,慢慢来 ,写的多了慢慢也会变好的。权当是记录自己的成长的一个过程,等到以后再往回看时,就会发现自己以前原来这么菜😂。

  本系列博客所述资料均来自互联网,并不是本人原创(只有博客是自己写的)。出于热心,本人将自己的所学笔记整理并推出相对应的使用教程,方面其他人学习。为国内的物联网事业发展尽自己的一份绵薄之力,没有为自己谋取私利的想法。若出现侵权现象,请告知本人,本人会立即停止更新,并删除相应的文章和代码。

简介

网络接口(如以太网接口)是硬件接口,如何和软件进行无缝衔接尼?软件中是通过定义了一个netif的结构体来描述网卡设备。
lwip中实现网卡驱动的模块是ethernetif.c,可以看成是链路层的驱动封装模块。

netif结构体

对于单网卡设备只有一个netif结构体,多网卡中对应着多个netif结构体。

struct netif {
#if !LWIP_SINGLE_NETIF
  /* 指向 netif 链表中的下一个 */
  struct netif *next;
#endif

#if LWIP_IPV4
  /**网络字节中的IP地址,子网掩码,网关 */
  ip_addr_t ip_addr;
  ip_addr_t netmask;
  ip_addr_t gw;
#endif /* LWIP_IPV4 */
#if LWIP_IPV6
  /** 网卡的IPV6地址 */
  ip_addr_t ip6_addr[LWIP_IPV6_NUM_ADDRESSES];
  /** 每个IPV6地址的状态
   * @see ip6_addr.h */
  u8_t ip6_addr_state[LWIP_IPV6_NUM_ADDRESSES];
#if LWIP_IPV6_ADDRESS_LIFETIMES
  /** 	IPv6 地址有两个生存期:首选生存期和有效生存期,而首选的生存期总是小于等于有效的生存期。
首选生存期到期后,如果有同样好的首选地址可用,那么该地址便不再用作新连接的源 IP 地址。 有效生存期到期后,该地址不再用作入局信息包的有效目标 IP 地址或源 IP 地址。 */
  u32_t ip6_addr_valid_life[LWIP_IPV6_NUM_ADDRESSES];
  u32_t ip6_addr_pref_life[LWIP_IPV6_NUM_ADDRESSES];
#endif /* LWIP_IPV6_ADDRESS_LIFETIMES */
#endif /* LWIP_IPV6 */
  /** 此函数由网络设备驱动程序调用,将数据包传递到 TCP/IP 协议栈。
    * 对于以太网物理层,这通常是 ethernet_input()*/
  netif_input_fn input;
#if LWIP_IPV4
  /**  此函数由 IP 层调用,在接口上发送数据包。通常这个功能,
    * 首先解析硬件地址,然后发送数据包。
    * 对于以太网物理层,这通常是 etharp_output() */
  netif_output_fn output;
#endif /* LWIP_IPV4 */
  /**  此函数由 ethernet_output() 调用,当需要在网卡上发送一个数据包时。
* 底层硬件输出数据函数,一般是调用自定义函数 low_level_output*/
  netif_linkoutput_fn linkoutput;
#if LWIP_IPV6
  /** 此函数由 IPV6 层调用,在接口上发送数据包。通常这个功能,
    * 首先解析硬件地址,然后发送数据包 ethip6_output() */
  netif_output_ip6_fn output_ip6;
#endif /* LWIP_IPV6 */
#if LWIP_NETIF_STATUS_CALLBACK
  /** 当 netif 状态设置为 up 或 down 时调用此函数*/
  netif_status_callback_fn status_callback;
#endif /* LWIP_NETIF_STATUS_CALLBACK */
#if LWIP_NETIF_LINK_CALLBACK
  /** 当 netif 链接设置为 up 或 down 时,将调用此函数*/
  netif_status_callback_fn link_callback;
#endif /* LWIP_NETIF_LINK_CALLBACK */
#if LWIP_NETIF_REMOVE_CALLBACK
  /** 当 netif 被删除时调用此函数 */
  netif_status_callback_fn remove_callback;
#endif /* LWIP_NETIF_REMOVE_CALLBACK */
  /** 此字段可由设备驱动程序设置并指向设备的状态信息。
* 主要是将网卡的某些私有数据传递给上层,用户可以自由发挥,也可以不用。 */
  void *state;
#ifdef netif_get_client_data
  void* client_data[LWIP_NETIF_CLIENT_DATA_INDEX_MAX + LWIP_NUM_NETIF_CLIENT_DATA];
#endif
#if LWIP_NETIF_HOSTNAME
  /* 这个 netif 的主机名, NULL 也是一个有效值 */
  const char*  hostname;
#endif /* LWIP_NETIF_HOSTNAME */
#if LWIP_CHECKSUM_CTRL_PER_NETIF
  u16_t chksum_flags;
#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF*/
  /** 最大传输单位(以字节为单位),对于以太网一般设为 1500 */
  u16_t mtu;
  /** link level hardware address of this interface */
  /* 此网卡的链路层硬件地址 16字节对齐 */
  u8_t hwaddr[NETIF_MAX_HWADDR_LEN];
  /** 硬件地址长度,对于以太网就是 MAC 地址长度,为 6 字节 */
  u8_t hwaddr_len;
  /** 网卡状态信息标志位,是很重要的控制字段,
* 它包括网卡功能使能、广播使能、 ARP 使能等等重要控制位。 */
  u8_t flags;
  /** 字段用于保存每一个网卡的名字。用两个字符的名字来标识网络接
* 口使用的设备驱动的种类,名字由设备驱动来设置并且应该反映通过网卡
* 表示的硬件的种类。比如蓝牙设备(bluetooth)的网卡名字可以是 bt,
* 而 IEEE 802.11b WLAN 设备的名字就可以是 wl,当然设置什么名字用户是可
* 以自由发挥的,这并不影响用户对网卡的使用。当然,如果两个网卡
* 具有相同的网络名字,我们就用 num 字段来区分相同类别的不同网卡 */
  char name[2];
  /**  用来标示使用同种驱动类型的不同网卡 */
  u8_t num;
  /* 与硬件层绑定的参数 */
  u8_t sim_cid;
  void *pspathIntf;
  u8_t channelId;
  u8_t pdnType;
#if LWIP_IPV6_AUTOCONFIG
  /** is this netif enabled for IPv6 autoconfiguration */
  u8_t ip6_autoconfig_enabled;
#endif /* LWIP_IPV6_AUTOCONFIG */
#if LWIP_IPV6_SEND_ROUTER_SOLICIT
  /** Number of Router Solicitation messages that remain to be sent. */
  u8_t rs_count;
#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
#if MIB2_STATS
  /** 连接类型 (from "snmp_ifType" enum from snmp_mib2.h) */
  u8_t link_type;
  /** 连接速度 */
  u32_t link_speed;
  /** 最后一次更改的时间戳 (up/down) */
  u32_t ts;
  /** counters */
  struct stats_mib2_netif_ctrs mib2_counters;
#endif /* MIB2_STATS */
#if LWIP_IPV4 && LWIP_IGMP
  /** 可以调用此函数来添加或删除多播中的条目
以太网 MAC 的过滤表*/
  netif_igmp_mac_filter_fn igmp_mac_filter;
#endif /* LWIP_IPV4 && LWIP_IGMP */
#if LWIP_IPV6 && LWIP_IPV6_MLD
  /** This function could be called to add or delete an entry in the IPv6 multicast
      filter table of the ethernet MAC. */
  netif_mld_mac_filter_fn mld_mac_filter;
#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
#if LWIP_NETIF_USE_HINTS
  struct netif_hint *hints;
#endif /* LWIP_NETIF_USE_HINTS */
#if ENABLE_LOOPBACK
  /* List of packets to be queued for ourselves. */
  struct pbuf *loop_first;
  struct pbuf *loop_last;
#if LWIP_LOOPBACK_MAX_PBUFS
  u16_t loop_cnt_current;
#endif /* LWIP_LOOPBACK_MAX_PBUFS */
#endif /* ENABLE_LOOPBACK */
  u8_t is_ppp_mode;  //To identify ppp mode
  u8_t link_mode;  //判断直接连接lwip还是nat
  u8_t is_used;
  /*lwip 上下行数据大小*/
  uint32_t u32LwipULSize;
  uint32_t u32LwipDLSize;
  /*PPP 上下行数据大小*/
  uint32_t u32PPPULSize;
  uint32_t u32PPPDLSize;
  /*rndis 上下行数据大小*/
  uint32_t u32RndisULSize;
  uint32_t u32RndisDLSize;
}

启动步骤

1.初始化

  1. net_init

建立net_thread线程,接收PDP的消息上报,如:ACT,DEACT等消息。

  1. tcpip_init

lwip协议栈的初始化,主要内容包括建立邮箱,建立tcpip_task,超时函数的初始化,以及在lwip_init里面各个网络协议的初始化。

  1. ip4_nat_init

初始化nat表管理定时器,定时检查更新nat表。

2.注册上网

1. 创建网卡

PDP激活后,向net_thread发送EV_TCPIP_CFW_GPRS_ACT消息,会携带cid和simid的消息。使用get_nat_enabled(nSimId, nCid)判断该通道是否打开了NAT转换功能。默认cid5通道是打开的。

在这里插入图片描述

  • TCPIP_nat_wan_netif_create(nCid, nSimId);
  1. 主要功能:

创建WAN的netif,并绑定PDP上下文,绑定物理层的网络收发接口。

  1. 主要功能函数
/*获取PDP上下文*/
CFW_GprsGetPdpCxtV2(nCid, &pdp_context, nSimId);
/*打开一个PS接口
- nSimId, nCid 作为通道标识
- lwip_nat_wan_pspathDataInput 收到数据的回调函数
- netif 网卡结构体
*/
drvPsIntfOpen(nSimId, nCid, lwip_nat_wan_pspathDataInput, (void *)netif);
/*
添加一个netif到lwip_netif链表中
- netif 网卡结构体
- ip4 从PDP上下文中获取的公网IP地址
- netif_gprs_nat_wan_init(初始化netif的一些参数,netif->name,netif->ouput,netif->mtu),该函数会在netif_add中被调用执行。
- tcpip_input netif->input函数
*/
netif_add(netif, &ip4, NULL, NULL, NULL, netif_gprs_nat_wan_init, tcpip_input);

  • TCPIP_nat_lan_lwip_netif_create
  1. 主要功能

创建一个LAN的netif,创建的过程与WAN的过程差不多,只是绑定的参数有些不一样。LAN的netif都维护在gprs_netif全局变量中,根据

  1. 主要功能函数
//1. 获取WAN的ip地址,并初始化网关地址和子网掩码。
//2. 添加LAN的netif初始化函数和LAN的数据输入接口,netif->link_mode = NETIF_LINK_MODE_NAT_LWIP_LAN;
netif_gprs_nat_lan_lwip_init(struct netif *netif);
/*
    1. 绑定netif->output[_ip6]为nat_lan_lwip_data_output
    2. 判断默认网卡netif_default是否为空
    3. 将该netif绑定为默认网卡,并从PDP上下文中获取DNS服务器地址
*/
  1. 初始化NAT上下文,维护在lwip_nat_entry全局变量中
  • TCPIP_netif_create
  1. 主要功能

该函数实现的功能和上面两个差不多,都是初始化netif,只不过因为上面开通了NAT功能,所以要经过NAT层转换一下。注意linkmode为:
netif->link_mode = NETIF_LINK_MODE_LWIP;

2. 接收数据

初始化netif的时候,创建了一个PS接口,该接口传入了一个数据接收的回调函数。

//使用NAT时调用的是:
drvPsIntfOpen(nSimId, nCid, lwip_nat_wan_pspathDataInput, (void *)netif);
/*
该函数的主要功能是将消息发送到net_thread线程中使用gprs_data_ipc_to_lwip_nat_wan执行
*/
//不使用NAT调用的是:
drvPsIntfOpen(nSimId, nCid, lwip_pspathDataInput, (void *)netif);
/*
该函数的主要功能是将消息发送到net_thread线程中使用gprs_data_ipc_to_lwip执行
*/
  • lwip_nat_wan_pspathDataInput
  1. 主要功能

从PS接口中读取数据,并传递到lwip中。

  1. 主要功能函数
//1. 从PS接口中读取数据
drvPsIntfRead(inp_netif->pspathIntf, pData, 1600);

//2. 建立pbuf,并将pbuf传递给TCPIP(主要介绍下IPV4的)
ip4_nat_input(struct pbuf *p);
/*该函数的主要作用是:
1. switch (IPH_PROTO(iphdr)) 区分IP包协议
2. ip4_nat_check_header 判断是否使用NAT转换,如果是获取相应的NAT上下文。
3. in_if = nat_entry.cmn->cfg->entry.in_if; 获取netif,
调用in_if->input将pbuf传入。

//3. 第二步中如果没有使用NAT,则走到这里,这里直接使用消息传入的netif,并将数据传入inp_netif->input
inp_netif->input
*/
  • gprs_data_ipc_to_lwip
  1. 主要功能
//1. 从PS接口中读取数据
drvPsIntfRead(inp_netif->pspathIntf, pData, 1600);

//2. 数据传入inp_netif->input

3.发送数据

发送主要是IP层调用netif->output进行数据发送,在netif创建的时候已经绑定了nat_wan_data_output,data_output,这两个都是通过调用drvPsIntfWrite((drvPsIntf_t *)netif->pspathIntf, pData, p->tot_len)直接将数据发送出去。

这里主要介绍下nat_lan_lwip_data_output

  • nat_lan_lwip_data_output
  1. 主要函数功能
1. 判断pbuf是否为空后判断是IPV6还是IPV4
2. IPV4调用ip4_wan_forward(如果是回环就直接再返回给netif->input)
3. 第2步不满足,调用ip4_nat_out
    1. 通过ip头获取nat_config
    2. 判断IP协议,并获取相应的协议头部
    3. 判断是否存在于NAT表中,如果存在就取出来
    4. 使用nat_entry.cmn->cfg->entry.out_if->output接口将数据包发送出去

  那么本篇博客就到此结束了,这里只是记录了一些我个人的学习笔记,其中存在大量我自己的理解。文中所述不一定是完全正确的,可能有的地方我自己也理解错了。如果有些错的地方,欢迎大家批评指正。如有问题直接在对应的博客评论区指出即可,不需要私聊我。我们交流的内容留下来也有助于其他人查看,说不一定也有其他人遇到了同样的问题呢😂。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LWIP_DEBUGF是一个宏定义,用于在lwIP协议栈中输出调试信息。它的定义如下: ```c #define LWIP_DEBUGF(debug, message) \ do { \ if ((debug) && (lwip_debug)) { \ LWIP_PLATFORM_DIAG((_U32)"lwIP: ", message); \ } \ } while (0) ``` 其中,debug是一个表示调试级别的参数,message则是要输出的调试信息。在使用LWIP_DEBUGF输出调试信息时,需要先定义LWIP_DEBUG宏来开启调试功能,并设置输出级别。在lwipopts.h文件中,可以找到如下代码: ```c #define LWIP_DEBUG 0 ``` 将LWIP_DEBUG的值设置为1,即可开启调试功能。同时,还需要根据需要设置输出级别,可以在lwipopts.h文件中找到类似如下代码: ```c #define ETHARP_DEBUG LWIP_DBG_OFF #define NETIF_DEBUG LWIP_DBG_ON #define PBUF_DEBUG LWIP_DBG_OFF #define API_LIB_DEBUG LWIP_DBG_OFF #define API_MSG_DEBUG LWIP_DBG_OFF #define SOCKETS_DEBUG LWIP_DBG_OFF #define ICMP_DEBUG LWIP_DBG_OFF #define IGMP_DEBUG LWIP_DBG_OFF #define INET_DEBUG LWIP_DBG_OFF #define IP_DEBUG LWIP_DBG_OFF #define IP_REASS_DEBUG LWIP_DBG_OFF #define RAW_DEBUG LWIP_DBG_OFF #define MEM_DEBUG LWIP_DBG_OFF #define MEMP_DEBUG LWIP_DBG_OFF #define SYS_DEBUG LWIP_DBG_OFF #define TCP_DEBUG LWIP_DBG_OFF #define TCP_INPUT_DEBUG LWIP_DBG_OFF #define TCP_FR_DEBUG LWIP_DBG_OFF #define TCP_RTO_DEBUG LWIP_DBG_OFF #define TCP_CWND_DEBUG LWIP_DBG_OFF #define TCP_WND_DEBUG LWIP_DBG_OFF #define TCP_OUTPUT_DEBUG LWIP_DBG_OFF #define TCP_RST_DEBUG LWIP_DBG_OFF #define TCP_QLEN_DEBUG LWIP_DBG_OFF #define UDP_DEBUG LWIP_DBG_OFF #define TCPIP_DEBUG LWIP_DBG_OFF #define SLIP_DEBUG LWIP_DBG_OFF #define DHCP_DEBUG LWIP_DBG_OFF #define AUTOIP_DEBUG LWIP_DBG_OFF #define DNS_DEBUG LWIP_DBG_OFF #define IP6_DEBUG LWIP_DBG_OFF #define DHCP6_DEBUG LWIP_DBG_OFF #define MLD6_DEBUG LWIP_DBG_OFF #define ICMP6_DEBUG LWIP_DBG_OFF #define ND6_DEBUG LWIP_DBG_OFF #define UDP6_DEBUG LWIP_DBG_OFF #define TCP6_DEBUG LWIP_DBG_OFF ``` 其中,每个宏定义了一个调试输出级别,可以根据需要将其设置为LWIP_DBG_ON、LWIP_DBG_OFF或LWIP_DBG_TRACE。如,将IP_DEBUG的值设置为LWIP_DBG_ON,则可以开启IP协议相关的调试输出。在需要输出调试信息的地方,可以使用LWIP_DEBUGF宏来输出信息。如: ```c LWIP_DEBUGF(IP_DEBUG, ("Received packet of length %d\n", p->tot_len)); ``` 这条语句将输出一个类似于“lwIP: Received packet of length 128”这样的调试信息,其中128是p->tot_len的值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值