LwIP 之 ethernetif.c

简介

  LwIP的网络驱动有一定的模型,/src/netif/ethernetif.c文件即为驱动的模版,用户为自己的网络设备实现驱动时应参照此模块。在 LwIP中可以有多个网络接口,每个网络接口都对应了一个struct netif。这个netif包含了相应网络接口的属性、收发函数。
  该文件中的函数通常为与硬件打交道的函数,当有数据接收的时候被调用,以使接收到的数据进入tcpip协议栈。

netif 结构

  在LwIP中,是通过结构体netif来描述一个硬件网络接口的,在单网卡中,这个结构体只有一个;多网卡中可有何网卡数目相同的netif结构体,它们构成一个数据链。该结构体的定义在文件netif.h中,具体如下:

/** Generic data structure used for all lwIP network interfaces.
 *  The following fields should be filled in by the initialization
 *  function for the device driver: hwaddr_len, hwaddr[], mtu, flags */
struct netif {
  /** pointer to next in linked list */
  struct netif *next;

#if LWIP_IPV4
  /** IP address configuration in network byte order */
  ip_addr_t ip_addr;       /* IP 地址 */
  ip_addr_t netmask;       /* 子网掩码 */
  ip_addr_t gw;            /* 网关 */
#endif /* LWIP_IPV4 */
#if LWIP_IPV6
  /** Array of IPv6 addresses for this netif. */
  ip_addr_t ip6_addr[LWIP_IPV6_NUM_ADDRESSES];
  /** The state of each IPv6 address (Tentative, Preferred, etc).
   * @see ip6_addr.h */
  u8_t ip6_addr_state[LWIP_IPV6_NUM_ADDRESSES];
#endif /* LWIP_IPV6 */
  /** This function is called by the network device driver
   *  to pass a packet up the TCP/IP stack. */
  netif_input_fn input;   /* 函数指针,从网卡中接收一包数据 */
#if LWIP_IPV4
  /** This function is called by the IP module when it wants
   *  to send a packet on the interface. This function typically
   *  first resolves the hardware address, then sends the packet.
   *  For ethernet physical layer, this is usually etharp_output() */
  netif_output_fn output;   /* IP层调用此函数向网卡发送一包数据 */
#endif /* LWIP_IPV4 */
  /** This function is called by ethernet_output() when it wants
   *  to send a packet on the interface. This function outputs
   *  the pbuf as-is on the link medium. */
  netif_linkoutput_fn linkoutput;    /* ARP模块调用这个函数向网卡发送一包数据 */
#if LWIP_IPV6
  /** This function is called by the IPv6 module when it wants
   *  to send a packet on the interface. This function typically
   *  first resolves the hardware address, then sends the packet.
   *  For ethernet physical layer, this is usually ethip6_output() */
  netif_output_ip6_fn output_ip6;
#endif /* LWIP_IPV6 */
#if LWIP_NETIF_STATUS_CALLBACK
  /** This function is called when the netif state is set to up or down
   */
  netif_status_callback_fn status_callback;
#endif /* LWIP_NETIF_STATUS_CALLBACK */
#if LWIP_NETIF_LINK_CALLBACK
  /** This function is called when the netif link is set to up or down
   */
  netif_status_callback_fn link_callback;
#endif /* LWIP_NETIF_LINK_CALLBACK */
#if LWIP_NETIF_REMOVE_CALLBACK
  /** This function is called when the netif has been removed */
  netif_status_callback_fn remove_callback;
#endif /* LWIP_NETIF_REMOVE_CALLBACK */
  /** This field can be set by the device driver and could point
   *  to state information for the device. */
  void *state;          /* 这个成员变量注意以下,主要是将 网卡的某些私有数据传递给上层,用户可以自由发挥,也可以不用。下面会有详细说明。*/
#ifdef netif_get_client_data
  void* client_data[LWIP_NETIF_CLIENT_DATA_INDEX_MAX + LWIP_NUM_NETIF_CLIENT_DATA];
#endif
#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 LWIP_NETIF_HOSTNAME
  /* the hostname for this netif, NULL is a valid value */
  const char*  hostname;
#endif /* LWIP_NETIF_HOSTNAME */
#if LWIP_CHECKSUM_CTRL_PER_NETIF
  u16_t chksum_flags;
#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF*/
  /** maximum transfer unit (in bytes) */
  u16_t mtu;   /* 网络一次可以传送的最大字节数,对于以太网一般设为 1500 */
  /** number of bytes used in hwaddr */
  u8_t hwaddr_len;  /* 硬件地址长度,对于以太网就是 MAC 地址长度,为 6 各字节 */
  /** link level hardware address of this interface */
  u8_t hwaddr[NETIF_MAX_HWADDR_LEN];
  /** flags (@see @ref netif_flags) */
  u8_t flags;   /* 网卡状态信息标志位,是很重要的控制字段,它包括网卡功能使能、广播使能、 ARP 使能等等重要控制位。*/
  /** descriptive abbreviation */
  char name[2]; /* 字段用于保存每一个网络网络接口的名字。用两个字符的名字来标识网络接口使用的设备驱动的种类,名字由设备驱动来设置并且应该反映通过网络接口表示的硬件的种类。
  比如蓝牙设备( bluetooth)的网络接口名字可以是 bt,而 IEEE 802.11b WLAN 设备的名字就可以是 wl,当然设置什么名字用户是可以自由发挥的,这并不影响用户对网络接口的使用。
  当然,如果两个网络接口具有相同的网络名字,我们就用 num 字段来区分相同类别的不同网络接口。 */
  /** number of this interface */
  u8_t num;  /* 用来标示使用同种驱动类型的不同网络接口 */
#if MIB2_STATS
  /** link type (from "snmp_ifType" enum from snmp_mib2.h) */
  u8_t link_type;
  /** (estimate) link speed */
  u32_t link_speed;
  /** timestamp at last change made (up/down) */
  u32_t ts;
  /** counters */
  struct stats_mib2_netif_ctrs mib2_counters;
#endif /* MIB2_STATS */
#if LWIP_IPV4 && LWIP_IGMP
  /** This function could be called to add or delete an entry in the multicast
      filter table of the ethernet 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_HWADDRHINT
  u8_t *addr_hint;
#endif /* LWIP_NETIF_HWADDRHINT */
#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 */
};

各函数

  在LwIP提供的模板ethernetif.c文件中,上来就是一个如下的结构体:

struct ethernetif {
  struct eth_addr *ethaddr;
  /* Add whatever per-interface state that is needed here. */
};

  该结构用来描述底层硬件设备,该结构体唯一不可或缺的是MAC地址,它是LWIP用于相应ARP查询的核心数据。其他如果没有特殊需要,可以不添加其他成员数据。主要用于将底层硬件设备的一些私有数据通过netif ->state传递给上层(在ethernetif_init函数中赋值netif->state = ethernetif;)。一般用不到改结构。因为在netif结构中,基本包含了所有需要的信息。
  ethernetif.c文件中提供的函数主要有以下这么几个:

  • static void low_level_init(struct netif *netif);
  • static struct pbuf * low_level_input(struct netif *netif);
  • static err_t low_level_output(struct netif *netif, struct pbuf *p);
  • void ethernetif_input( void * pvParameters )- low_level_output;
  • err_t ethernetif_init(struct netif *netif);

  其中对外的接口只有ethernetif_init函数。

ethernetif_init

  该函数完成对于网络接口的初始化工作。它是在对LwIP初始化函数中通过netif_add( &EMAC_if, &xIpAddr, &xNetMast, &xGateway, NULL, ethernetif_init, tcpip_input );来被调用的。具体见以下注释:

err_t ethernetif_init(struct netif *netif)
{
    LWIP_ASSERT("netif != NULL", (netif != NULL));

    #if LWIP_NETIF_HOSTNAME
    netif->hostname = "lwip";       /* Initialize interface hostname */
    #endif /* LWIP_NETIF_HOSTNAME */

    netif->name[0] = IFNAME0;
    netif->name[1] = IFNAME1;

    #if LWIP_IPV4
    #if LWIP_ARP || LWIP_ETHERNET
    #if LWIP_ARP
        netif->output = etharp_output;              /* 数据发送函数,其最终还是会调用 netif->linkoutput */
    #else
        /* The user should write ist own code in low_level_output_arp_off function */
        netif->output = low_level_output_arp_off;
    #endif /* LWIP_ARP */
    #endif /* LWIP_ARP || LWIP_ETHERNET */
    #endif /* LWIP_IPV4 */

    #if LWIP_IPV6 //0
        netif->output_ip6 = ethip6_output;
    #endif /* LWIP_IPV6 */
    netif->linkoutput = low_level_output;           /* 这个才是最底层发送函数(将数据交给硬件发送)*/

    /* initialize the hardware */
    low_level_init(netif);

//  etharp_init();

//  sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL);

    return ERR_OK;
}

low_level_init

  该函数主要是对网卡进行一系列的初始化工作,例如:初始化MAC地址、建立接收数据的任务等。

low_level_input

  该函数负责从网卡中接收数据。正常从LwIP的缓冲池中申请一个 pbuf结构,存放数据。

ethernetif_input

  该函数即为网卡数据接收任务。LwIP中是通过一个task(ethernetif_input)轮询检查DMA控制器的状态以判断是否有数据接收到。接收的数据传输流程如下:
硬件接口取数 -> low_level_input -> tcpip_input(通过函数指针s_pxNetIf->input)

void ethernetif_input( void * pvParameters )
{
    struct pbuf *p;

    for( ;; )
    {
        /* 1. 该信号量会在网卡的中断中有数据时被发送 */
        if (xSemaphoreTake( s_xSemaphore, emacBLOCK_TIME_WAITING_FOR_INPUT)==pdTRUE)
        {
            TRY_GET_NEXT_FRAME:
            p = low_level_input( s_pxNetIf );                       /* 2. 从网卡中取数据 */
            if(p != NULL)   /* 有数据 */
            {
                /* 3. 将数据(pbuf的地址)传递到 TCP/IP协议栈(s_pxNetIf->input在netif_add函数中赋值为函数tcpip_input ) */
                if (ERR_OK != s_pxNetIf->input( p, s_pxNetIf))
                {
                    pbuf_free(p);
                }
                else
                {
                    goto TRY_GET_NEXT_FRAME;                        /* 4. 接收成功后,返回继续接收数据,直到网卡中没有数据了 */
                }
            }
            /* 5. 如果没有数据了,则会再次返回第 1 步收信号量 */
        }
    }
}

low_level_output

  Output过程是由应用程序以主动方式触发的。在TCP/IP协议栈中,要发送的发送的数据最终被传递给了ip_output_if函数。查看代码可知,该函数中直接调用了netif->output函数,在上面的ethernetif_init函数中有对这个变量【函数指针】赋值,它就是etharp_output
  要发送的数据的额传输流程如下:
ip_output_if -> etharp_output(通过函数指针netif->output)-> ethernet_output -> low_level_output(通过函数指针netif->linkoutput) -> 硬件设备

猜你喜欢

转载自blog.csdn.net/zcshoucsdn/article/details/80184732