lwip协议以太网驱动层的软件数据结构详细解析

1、概述

       本文详细介绍了LWIP协议在rt-thread操作系统上的驱动层结构,rt thread操作系统的硬件驱动层采用标准的设备驱动结构,网络接口对应的网络设备netdev。网络设备下面对应的是以太网驱动程序,同时网络设备向应用层提供网络的各种功能接口,实现了如linux操作系统的ifconfig, ping命令等功能。整个lwip协议到底层硬件的结构如下,具体的参考代码可以查看rt-thread 3.1.3的正点原子阿波罗bsp工程。

lwip协议的层次结构
lwip协议的软件层次结构

 

2、网络接口驱动层的数据结构

      网络驱动部分的接口由硬件驱动接口层drv_eth.c,drv_eth.h,netif接口层ethernetif.c, ethernetif.h,netfi.c,netif.h,网络设备层netdev.c, netdev.h这些函数组成。每一层的程序都有一个数据结构来表示程序的使用的数据状态。请看下图,先对各个层对应的数据结构有一个初步的印象,混个脸熟先。

2.1 硬件驱动接口层

      硬件驱动接口层drv_eth.c,  drv_eth.h两个文件组成,drv_eth.c是操作系统的网络设备驱动程序,程序内部实现了设备驱动底层的open,close, read,write,ioctl这几个函数的功能。 对于网络设备只实现了ioctl函数的功能,由于读取网络的mac地址,其他几个函数全部为空,不执行具体的操作。以太网底层的数据收发,由驱动程序数据结构上新增加eth_tx, eth_rx两个函数实现mac数据的接收与发送。下图是drv_eth.c硬件驱动接口层的使用的数据结构,以太网底层的驱动函数全部由struct eth_device结构来管理。

struct rt_stm32_eth
{
    /* inherit from ethernet device */
    struct eth_device parent;

    /* interface address info. */
    rt_uint8_t  dev_addr[MAX_ADDR_LEN];       /* hw address   */

    uint32_t    ETH_Speed; /*!< @ref ETH_Speed */
    uint32_t    ETH_Mode;  /*!< @ref ETH_Duplex_Mode */
};

 2.2  网络接口层

     网络接口层由netif.c, netif.h,ethernetif.c, ethernetif.h这4个程序组成,程序内部使用的主要数据结构是struct eth_device。可以看出来,eth device interface接口里面有一个eth_rx, eth_tx的以太网接收与发送的函数指针,这两个指针指向drv_eth.c中的以太网MAC数据收发函数。结构体中的成员struct netif *netif指向lwip协议的网络接口数据结构。struct rt_device parent指向操作系统的设备驱动数据结构。

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);
};

       struct netif接口的数据结构如下:这个数据结构的第一个成员struct netif *next用于指向下一个netif结构,即可以支持多个网络接口,此结构中同时还保存了ip地址,mac地址,tcpip协议层的数据发送函数,数据接收函数。其中的void *state指针用于指向strcut rt_stm32_eth数据结构,以太网硬件驱动层的数据产生了关联。

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_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;
#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;
#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;
  /** number of bytes used in hwaddr */
  u8_t hwaddr_len;
  /** link level hardware address of this interface */
  u8_t hwaddr[NETIF_MAX_HWADDR_LEN];
  /** flags (@see @ref netif_flags) */
  u8_t flags;
  /** descriptive abbreviation */
  char name[2];
  /** 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 */
}

2.3  网络设备层

      网络设备层netdev.c, netdev.h为应用层程序提供接口,配置和显示网络设备的ip地址,mac地址,dns服务器,dhcp功能开关等。其中提供一个成员void *user_data,这个成员指向struct netif *netif结构,即可以访问netif的相关的功能。这样子就实现了通过netdev接口访问到了整个驱动层。

struct netdev
{
    rt_slist_t list; 
    char name[RT_NAME_MAX];                            /* network interface device name */
    ip_addr_t ip_addr;                                 /* IP address */
    ip_addr_t netmask;                                 /* subnet mask */
    ip_addr_t gw;                                      /* gateway */
    ip_addr_t dns_servers[NETDEV_DNS_SERVERS_NUM];     /* DNS server */
    uint8_t hwaddr_len;                                /* hardware address length */
    uint8_t hwaddr[NETDEV_HWADDR_MAX_LEN];             /* hardware address */
    
    uint16_t flags;                                    /* network interface device status flag */
    uint16_t mtu;                                      /* maximum transfer unit (in bytes) */
    const struct netdev_ops *ops;                      /* network interface device operations */
    
    netdev_callback_fn status_callback;                /* network interface device flags change callback */
    netdev_callback_fn addr_callback;                  /* network interface device address information change callback */

#ifdef RT_USING_SAL
    void *sal_user_data;                               /* user-specific data for SAL */
#endif /* RT_USING_SAL */
    void *user_data;                                   /* user-specific data */
};

2.4  驱动层数据结构的关联关系

      以太网的驱动层分成了硬件接口,网络接口层,网络设备层三层,这三层之间是要进行数据的交互与调用 ,所以这三个层的数据结构是具有一定的关联性,请看上图中的“数据结构关联关系”图,这个图中画彩色线部分指示了数据结构之间的相互指向。从图中可以看出,struct netdev.userdata--->struct eth_device.struct netif , struct netif.state-->struct rt_stm32_eth.strcut eth_device。可以看出这三个数据结构产生了互相连环的关联关系,可以通过任何一个数据结构访问另外两个数据结构。

3、以太网数据的在驱动程序中的流动

      从上面的介绍可以看出,以太网底层程序分成三层,三个数据结构。以太网低层的数据包是在这三层中流动的,流动的数据分成以太网接收数据和以太网发送数据,数据由2个线程进行处理。数据流动过程请见下图。

    

 

  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值