LWIP中netif结构体的介绍

网络接口

网络接口(以太网接口)是硬件接口(网络接口又可以称之为网卡)LWIP是软件那么而怎样让硬件和软件无缝连接起来呢?而且网卡又多种多样,怎样才能让LWIP使用同样的软件兼容不同的硬件平台?

LWIP中使用了一个netif结构体来描述网卡但是网卡是直接和硬件平台打交道的,硬件不同则处理基本上是不同的,所以由用户提供最底层的接口函数,LWIP提供统一的接口,但是底层的实现需要用户自己去完成,比如网卡的初始化和网卡的收发数据,当LWIP底层得到数据之后,才会传入到内核中去处理,同理,LWIP内核需要发送数据包的时候,也需要调用网卡的发送函数,这样才能把数据从硬件接口到软件内核无缝接连接起来,LWIP中的etherneif.c文件的函数通常为硬件打交道的底层函数,当有数据需要通过网卡接收或者发送数据的时候就会被调用,通过LWIP的协议栈的内部进行处理后,从应用层就能得到数据或者可以发送数据。

简单来说,netif是LWIP抽象出来的网卡,LWIP协议栈可以使用多种不同接口,而etherneif.c文件则提供了netif访问各种不同的网卡,每个网卡有不同的实方式,每户只需要修改ethernetif.c文件即可。

在单网卡中,这个netif结构体只有一个,可能还有人会问,那么一个设备中有多个网卡怎么办?很简单,LWIP会将每个用netif描述的网卡连接成成一个连接成一个链表。该链表就记录每个网卡的netif屏蔽硬件接口的差异,完成了对不同网卡的抽象,因此了解netif结构体是移植LWIP的关键。

我们可以理解将整个网络的数据传输理解为物流,那么网卡就是不同的传输工具,我们可以选择骑车,飞机,轮船等传输工具,不同的运输工具的速度是不一样的,但是对于一个物流公司而言,可能同时存在很多种运输的工具,这就需要物流公司去记录这些运输工具,当有一个包裹需要通过飞机运输出去,那么物流公司就会将这个通过飞机发送出去,这就好比我们得网卡,需要那个网卡发送或者接收网络数据得时候,就会让对应的网卡去工作。

1 struct netif
2 {
3 #if !LWIP_SINGLE_NETIF
4 /* 指向 netif 链表中的下一个 */
5 struct netif *next;                                                             (1)
6 #endif
7
8 #if LWIP_IPV4
9 /* 网络字节中的 IP 地址、子网掩码、默认网关配置 */
10 ip_addr_t ip_addr;
11 ip_addr_t netmask;
12 ip_addr_t gw;                                                                  (2)
13 #endif /* LWIP_IPV4 */
14
15 /* 此函数由网络设备驱动程序调用,将数据包传递到 TCP/IP 协议栈。
16 * 对于以太网物理层,这通常是 ethernet_input()*/
17 netif_input_fn input;                                                          (3)
18
19 #if LWIP_IPV4
20
21 /* 此函数由 IP 层调用,在接口上发送数据包。通常这个功能,
22 * 首先解析硬件地址,然后发送数据包。
23 * 对于以太网物理层,这通常是 etharp_output() */
24 netif_output_fn output;                                                       (4)
25
26 #endif /* LWIP_IPV4 */
27 /* 此函数由 ethernet_output()调用,当需要在网卡上发送一个数据包时。
28 * 底层硬件输出数据函数,一般是调用自定义函数 low_level_output*/
29 netif_linkoutput_fn linkoutput;                                                 (5)
30
31 #if LWIP_NETIF_STATUS_CALLBACK
32 /* 当 netif 状态设置为 up 或 down 时调用此函数 */
33 netif_status_callback_fn status_callback;                                       (6)
34 #endif /* LWIP_NETIF_STATUS_CALLBACK */
35
36 #if LWIP_NETIF_LINK_CALLBACK
37 /* 当 netif 链接设置为 up 或 down 时,将调用此函数 */
38 netif_status_callback_fn link_callback;                                         (7)
39 #endif /* LWIP_NETIF_LINK_CALLBACK */
40
41 #if LWIP_NETIF_REMOVE_CALLBACK
42 /* 当 netif 被删除时调用此函数 */
43 netif_status_callback_fn remove_callback;                                       (8)
44 #endif /* LWIP_NETIF_REMOVE_CALLBACK */
45
46 /* 此字段可由设备驱动程序设置并指向设备的状态信息。
47 * 主要是将网卡的某些私有数据传递给上层,用户可以自由发挥,也可以不用。*/
48 void *state;                                                                     (9)
49
50 #ifdef netif_get_client_data
51 void* client_data[LWIP_NETIF_CLIENT_DATA_INDEX_MAX + LWIP_NUM_NETIF_CLIENT_DATA];
52 #endif
53 #if LWIP_NETIF_HOSTNAME
54 /* 这个 netif 的主机名,NULL 也是一个有效值 */
55 const char* hostname;
56 #endif /* LWIP_NETIF_HOSTNAME */
57
58 #if LWIP_CHECKSUM_CTRL_PER_NETIF
59 u16_t chksum_flags;
60 #endif /* LWIP_CHECKSUM_CTRL_PER_NETIF*/
61
62 /** 最大传输单位(以字节为单位),对于以太网一般设为 1500 */
63 u16_t mtu;                                                                 (10)
64
65 /** 此网卡的链路层硬件地址 */
66 u8_t hwaddr[NETIF_MAX_HWADDR_LEN];                                         (11)
67
68 /** 硬件地址长度,对于以太网就是 MAC 地址长度,为 6 字节 */
69 u8_t hwaddr_len;                                                           (12)
70
71 /* 网卡状态信息标志位,是很重要的控制字段,
72 * 它包括网卡功能使能、广播使能、 ARP 使能等等重要控制位。 */
73 u8_t flags;                                                                (13)
75 /* 字段用于保存每一个网卡的名字。用两个字符的名字来标识网络接
76 * 口使用的设备驱动的种类,名字由设备驱动来设置并且应该反映通过网卡
77 * 表示的硬件的种类。比如蓝牙设备( bluetooth)的网卡名字可以是 bt,
78 * 而 IEEE 802.11b WLAN 设备的名字就可以是 wl,当然设置什么名字用户是可
79 * 以自由发挥的,这并不影响用户对网卡的使用。当然,如果两个网卡
80 * 具有相同的网络名字,我们就用 num 字段来区分相同类别的不同网卡*/
81 char name[2];                                                               (14)
82
83 /* 用来标示使用同种驱动类型的不同网卡 */
84 u8_t num;                                                                    (15)
85
86 #if MIB2_STATS
87 /* 连接类型 */
88 u8_t link_type;
89 /* 连接速度 */
90 u32_t link_speed;
91 /* 最后一次更改的时间戳 */
92 u32_t ts;
93 /** counters */
94 struct stats_mib2_netif_ctrs mib2_counters;
95 #endif /* MIB2_STATS */
96
97 #if LWIP_IPV4 && LWIP_IGMP
98 /** 可以调用此函数来添加或删除多播中的条目
99 以太网 MAC 的过滤表。*/
100 netif_igmp_mac_filter_fn igmp_mac_filter;
101 #endif /* LWIP_IPV4 && LWIP_IGMP */
102
103 #if LWIP_NETIF_USE_HINTS
104 struct netif_hint *hints;
105 #endif /* LWIP_NETIF_USE_HINTS */
106
107 #if ENABLE_LOOPBACK
108 /* List of packets to be queued for ourselves. */
109 struct pbuf *loop_first;
110 struct pbuf *loop_last;
111
112 #if LWIP_LOOPBACK_MAX_PBUFS
113 u16_t loop_cnt_current;
114 #endif /* LWIP_LOOPBACK_MAX_PBUFS */
115
116 #endif /* ENABLE_LOOPBACK */
117 };

我们挑一些比较重要的netif字段进行讲解:

(1)LWIP使用链表来统一管理同一设备的多个网卡netif.c文件中定义两个全局指针struct netif *netif_list 和 struct netif *netif_default,其中 netif_list 就是网卡链表 指针,指向网卡链表的首节点(第一个网卡),后者表示默认情况下(有多网口时)使用 哪个网卡。next 字段指向下一个 netif 结构体指针,在一个设备中有多个网卡时,才使用该 字段。

(2)ip_addr 字段记录的是网络中的 IP 地址,netmask 字段记录的是子网 掩码,gw 记录的是网关地址,这些字段是用于描述网卡的网络地址属性。IP地址必须和网卡对应,即设备拥有多少个网卡那就必须有多少个IP地址,子网掩码可以用来判断某个IP地址与当前网卡是否处于同一子网中,IP在发送数据包的时候会选择与目标IP地址处于同于子网的网卡来发送,网关地址在数据包的发送,转发过程非常重要,如果要向不属于同一子网的主机(主机目标IP地址与网卡不属于同一子网)发送一个数据包,那么LWIP就会将数据包发送到网关中,网关设备会对数据包进行正确的转发,除此之外,网关还提供很多高级功能,如DNS  DHCP等

(3)input是一个函数指针,指向一个函数,该函数由网络设备驱动程序调用,将数据包传递到TCP/IP协议栈(IP层),这通常是ethernet_input(),参数为pbuf和netif类型,其中pbuf为接收到的数据包。

(4)output也是一个函数指针,指向一个函数,此函数由IP层调用,在接口上发送数据包。用户需要编写该函数并使output指向它,用户需要编写该函数并且使output指向它,通这个函数的处理步骤是首先解析硬件地址,然后发送数据包,对于以太网物理层,该函数通常是etharp_output(),参数是pbuf,netif,和ip_addr类型,其中ipaddr代表要将数据包发送到的地址,但不一定数据包最终到达的ip地址,比如要发送ip数据报到一个并不在本网络的主机上,该数据包要被发送到一个路由器上,这里的ipaddr就是路由器的ip地址

(5)linkoutput字段和output类似,也需要用户自己实现一个函数,但是只有两个参数,它是由ARP模块调用的,一般是自定义函数low_level_output()。当需要在网卡上发送一个数据包时,该函数会被ethernet_output()函数调用。

(6)当netif状态设置为up或down时,将调用此函数。

(7)当netif状态设置为up或down时,将调用此函数。

(8)当netif被删除时调用此函数

(9)此字段可由设备驱动程序设置并指向设备的状态信息,主要将网卡的某些私有数据传递给上层,用户可以自由发挥,也可以不用。

(10)最大传输单位(以字节为单位),对于以太网一般为1500,在IP层发送数据的时候,LWIP会使用该字段决定是都需要对数据包进行分片处理,为什么是在进行分片处理?因为链路层不提供任何的差错处理机制,如果在网卡中接收的数据包不满足网卡的属性,那么网卡可能会直接丢弃该数据包,也可能在底层进行分包发送,但是这种分包ip层看来是不接受的,因为它打乱了数据的结构,所以只能由ip层进行分片处理

(11)此网卡的链路层硬件地址。

(12)硬件地址长度,对于以太网就是MAC地址长度,为6个字节

(13)网卡状态信息标志位,是很重要的控制字段,它包括网卡的功能使能,广播使能,ARP使能等重要控制位。

(14)name字段用于保存每一个网卡的名字,用两个字符的名字来标识,网卡使用的设备驱动的种类,名字由设备驱动来设置并且应该反映通过网卡表示的硬件的种类。比如蓝牙设备(bluetooth)的网卡名字可以是 bt,而 IEEE 802.11b WLAN 设备的名 字就可以是 wl,当然设置什么名字用户是可以自由发挥的,这并不影响用户对网卡的使用。 当然,如果两个网卡具有相同的网络名字,我们就用 num 字段来区分相同类别的不同网卡。

(15)用来标识使用同种驱动类型的不同网卡。

netif使用

那么 netif 具体该如何使用呢?其实使用还是非常简单的。首先我们需要根据我们的网 卡定义一个 netif 结构体变量 struct netif gnetif,我们首先要把网卡挂载到 netif_list 链表上 才能使用,因为 LwIP 是通过链表来管理所有的网卡,所有第一步是通过 netif_add()函数将 我们的网卡挂载到 netif_list 链表上,netif_add()函数具体见代码清单

1 struct netif *
2 netif_add(struct netif *netif,const ip4_addr_t *ipaddr,
3 const ip4_addr_t *netmask, const ip4_addr_t *gw,
4 void *state, netif_init_fn init, netif_input_fn input)
5 {
6 LWIP_ASSERT_CORE_LOCKED();
7
8 if (ipaddr == NULL)
9 {
10 ipaddr = ip_2_ip4(IP4_ADDR_ANY);
11 }
12 if (netmask == NULL)
13 {
14 netmask = ip_2_ip4(IP4_ADDR_ANY);
15 }
16 if (gw == NULL)
17 {
18 gw = ip_2_ip4(IP4_ADDR_ANY);
19 }
20
21 /* reset new interface configuration state */
22 ip_addr_set_zero_ip4(&netif->ip_addr);                                     (1)
23 ip_addr_set_zero_ip4(&netif->netmask);
24 ip_addr_set_zero_ip4(&netif->gw);
25 netif->output = netif_null_output_ip4;
26
27 NETIF_SET_CHECKSUM_CTRL(netif, NETIF_CHECKSUM_ENABLE_ALL);
28 netif->mtu = 0;
29 netif->flags = 0;
30
31 memset(netif->client_data, 0, sizeof(netif->client_data));
32
33 /* remember netif specific state information data */
34 netif->state = state;
35 netif->num = netif_num;
36 netif->input = input;                                                        (2)
37
38 NETIF_RESET_HINTS(netif);
39
40
41 netif_set_addr(netif, ipaddr, netmask, gw);                                     (3)
42
43
44 /* call user specified initialization function for netif */
45 if (init(netif) != ERR_OK)                                                     (4)
46 {
47 return NULL;
48 }
49
50 {
51 struct netif *netif2;
52 int num_netifs;
53 do
54 {
55 if (netif->num == 255)
56 {
57 netif->num = 0;
58 }
59 num_netifs = 0;
60 for(netif2 = netif_list; netif2 != NULL; netif2 = netif2->next)
61 {
62 num_netifs++;
63 if (netif2->num == netif->num)
64 {
65 netif->num++;
66 break;
67 }
68 }
69 }
70 while (netif2 != NULL);
71 }
72 if (netif->num == 254)
73 {
74 netif_num = 0;
75 }
76 else
77 {
78 netif_num = (u8_t)(netif->num + 1);                                             (5)
79 }
80
81 /* add this netif to the list */
82 netif->next = netif_list;
83 netif_list = netif;                                                             (6)
84
85 mib2_netif_added(netif);
86
87
88 ip4_addr_debug_print(NETIF_DEBUG, ipaddr);
89
90 ip4_addr_debug_print(NETIF_DEBUG, netmask);
91
92 ip4_addr_debug_print(NETIF_DEBUG, gw);
93
94
95 netif_invoke_ext_callback(netif, LWIP_NSC_NETIF_ADDED, NULL);
96
97 return netif;
98 }

(1)清空主机ip地址,子网掩码,网关等字段信息

(2)根据传递进来的参数填写网卡state   input 等字段的相关信息

(3)调用网卡设置函数netif_set_addr()设置网卡ip地址,子网掩码,网关等信息

(4)通过传递进来的回调函数init()进行网卡的初始化操作,所以该函数是由用户实现的,对于不同网卡就使用不一样的初始化,而此处是以太网,则该回调函数一般etherneif_init()

(5)初始化网卡成功,则遍历当前设备拥有多少个网卡,并为当前网卡分配唯一标识num

(6)将当前网卡插入netif_list链表中

在使用之前需要进行初始化主机ip地址,子网掩码,网关等,并且在调用netif_add()函数之后会触发netif_init_fn回调函数

总之一句话,在开始使用 LwIP 协议栈的时候,我们就需要将网卡底层移植完成,才 能开始使用,而移植的第一步,就是将网络进行初始化,并且设置该网卡为默认网卡,让 LwIP 能通过网卡进行收发数据

1 /*Static IP ADDRESS: IP_ADDR0.IP_ADDR1.IP_ADDR2.IP_ADDR3 */
2 #define IP_ADDR0 192
3 #define IP_ADDR1 168
4 #define IP_ADDR2 1
5 #define IP_ADDR3 122
6
7 /*NETMASK*/
8 #define NETMASK_ADDR0 255
9 #define NETMASK_ADDR1 255
10 #define NETMASK_ADDR2 255
11 #define NETMASK_ADDR3 0
12
13 /*Gateway Address*/
14 #define GW_ADDR0 192
15 #define GW_ADDR1 168
16 #define GW_ADDR2 1
17 #define GW_ADDR3 1
18
19 struct netif gnetif;
20 ip4_addr_t ipaddr;
21 ip4_addr_t netmask;
22 ip4_addr_t gw;
23 uint8_t IP_ADDRESS[4];
24 uint8_t NETMASK_ADDRESS[4];
25 uint8_t GATEWAY_ADDRESS[4];
26
27 void TCPIP_Init(void)
28 {
29
30 tcpip_init(NULL, NULL);
31
32 /* IP addresses initialization */
33 /* USER CODE BEGIN 0 */
34 #ifdef USE_DHCP
35 ip_addr_set_zero_ip4(&ipaddr);
36 ip_addr_set_zero_ip4(&netmask);
37 ip_addr_set_zero_ip4(&gw);
38 #else
39 IP4_ADDR(&ipaddr,IP_ADDR0,IP_ADDR1,IP_ADDR2,IP_ADDR3);
40 IP4_ADDR(&netmask,NETMASK_ADDR0,NETMASK_ADDR1,NETMASK_ADDR2,NETMASK_ADDR3);
41 IP4_ADDR(&gw,GW_ADDR0,GW_ADDR1,GW_ADDR2,GW_ADDR3);
42 #endif /* USE_DHCP */
43 /* USER CODE END 0 */
44 /* Initilialize the LwIP stack without RTOS */
45 /* add the network interface (IPv4/IPv6) without RTOS */
46 netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, &ethernetif_init,
47 &tcpip_input);
48 /* Registers the default network interface */
49 netif_set_default(&gnetif);
50
51 if (netif_is_link_up(&gnetif))
52 {
53 /* When the netif is fully configured this function must be called */
54 netif_set_up(&gnetif);
55 }
56 else
57 {
58 /* When the netif link is down this function must be called */
59 netif_set_down(&gnetif);
60 }

挂载网卡的过程是非常简单的,如果一个设备当前是还没有网卡的,当调用netif_add()函数挂载网卡后,其过程如下图所示,

当设备需要挂载多个网卡的时候,就多次调用netif_add()函数即可,新挂载的网卡会在链表的最前面

与netif相关的底层函数

我们知道每个netif接口都需要一个底层接口文件提供访问硬件的支持,而LWIP作者将这种支持做成一个框架供我们参考,如ethernetif.c文件就是实现为一个框架的形式,我们在移植的时候只需要根据实际的网卡特性完善这里面的函数即可。框架中的函数名,参数都已经实现,我们只需要往里面填充完善即可,当然,网卡的驱动与这些函数名字我们也可以进行修改,只要LWIP内核能够正确识别网卡中的功能即可,为了方便,我们还是使用LWIP作者提供的框架进行移植操作,当一个设备使用多个网卡的时候那就需要编写多个不同的网卡驱动,与网卡驱动密切相关的函数有三个

1 static void low_level_init(struct netif *netif);
2 static err_t low_level_output(struct netif *netif, struct pbuf *p);
3 static struct pbuf * low_level_input(struct netif *netif);

low_level_init()为网卡初始化函数,它主要完成网卡的复位及参数初始化,根据实际的网卡属性进行配置netif中与网卡相关的字段,例如网卡的 MAC 地址、长度,最大发送单 元等。

low_level_output()函数为网卡的发送函数,它主要将内核的数据包发送出去,数据包采用pbuf数据结构 进行描述,该数据结构是一个比较复杂的数据结构。

low_level_input()函数为网卡的数据接收函数,该函数会接受一个数据包,为了内核易于对数据包的管理,该函数必须将接收的数据封装成pbuf的形式。

 

还有两个与网卡有关的函数

1 err_t ethernetif_init(struct netif *netif);
2 void ethernetif_input(void *pParams);

ethernetif_init()函数是在上层管理网卡netif的时候会被调用的函数,如使用netif_add添加网卡的时候,就会调用ethernetif_init()函数对网卡进行初始化,其实该函数 的最终调用的初始化函数就是low_level_init()函数,如果只有一个网卡,就不用对该函数进行改写,直接使用就是,它内部会将网卡的name output  linkoutput等字段进行初始化,这样子就能将内核和网卡无缝连接起来。

ethernetif_input()函数的主要作用就是调用low_level_input()函数从网卡中读取一个数据包,然后解析该数据包的类型是属于ARP数据包还是IP数据包,再将包递交给上层。在没有操作系统的ethernetif_input()就是一个可以直接使用的函数,已经无需我们自己去修 改,内核会周期性处理该接收函数。而在多线程操作系统的情况下,我们一般将其改写成一个线程的形式,可以周期性的去调用low_level_input()网卡接收函数;也可以使用中断的形式去处理,当这个线程尚未接收到数据包的时候,处于阻塞状态;当接收到数据包的时候,中断利用操作系统的IPC通信机制来唤醒线程去处理接收的数据包,并将数据包递交上层。

ethernetif.c 文件内容

ethernetif 数据结构

既然已经了解了与网卡有关的底层驱动函数,那么我们先看看 ethernetif.c 文件到底是 怎么样子的,又是怎么将网卡与 LwIP 内核连接在一起

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

ethernetif数据结构用来描述底层硬件设备的私有信息,如MAC地址等,该结构体唯一不可缺少的就是MAC地址,它是LWIP用于相应ARP查询的核心数据,用户可以对该结构进行添加其他的网卡描述信息,如果没有特殊需要,就不需要添加其他成员数据,该 数据结构在初始化的时候,会通过 netif 的 state 成员变量将这些硬件的私有信息传递给上 层。

ethernetif_init()

该函数是直接拿来用即可,如果没有特别的需求,基本不需要怎么修改它,它是 LwIP 中默认的网卡初始化函数,内部封装了 low_level_init()函数。具体代码如下

1
2 err_t ethernetif_init(struct netif *netif)
3 {
4 struct ethernetif *ethernetif;
5
6 // LWIP_ASSERT("netif != NULL", (netif != NULL));
7
8 ethernetif = mem_malloc(sizeof(struct ethernetif));
9
10 if (ethernetif == NULL)
11 {
12 PRINT_ERR("ethernetif_init: out of memory\n");
13 return ERR_MEM;
14 }
15
16 LWIP_ASSERT("netif != NULL", (netif != NULL));
17 //
18 #if LWIP_NETIF_HOSTNAME
19 /* Initialize interface hostname */
20 netif->hostname = "lwip";
21 #endif /* LWIP_NETIF_HOSTNAME */
22 netif->state = ethernetif;                                                     (1)
23 netif->name[0] = IFNAME0;
24 netif->name[1] = IFNAME1;
25
26 #if LWIP_IPV4
27 #if LWIP_ARP || LWIP_ETHERNET
28 #if LWIP_ARP
29 netif->output = etharp_output;
30 #else
31
32 netif->output = low_level_output_arp_off;
33 #endif /* LWIP_ARP */
34 #endif /* LWIP_ARP || LWIP_ETHERNET */
35 #endif /* LWIP_IPV4 */
36
37 #if LWIP_IPV6
38 netif->output_ip6 = ethip6_output;
39 #endif /* LWIP_IPV6 */
40
41 netif->linkoutput = low_level_output;
42
43 /* initialize the hardware */
44 low_level_init(netif);                                                         (2)
45 ethernetif->ethaddr = (struct eth_addr *) &(netif->hwaddr[0]);
46
47 return ERR_OK;
48 }

(1)通过 netif 的 state 成员变量将 ethernetif 结构传递给上层。

(2)调用low_level_init()函数对网卡进行初始化,该函数我们需要根据实际情况进行编写。

low_level_init()

该函数主要是根据实际情况对网卡进行一系列的初始化工作,例如初始化MAC地址,长度,设置最大传输包的大小,设置网卡的属性字段,支持广播,多播,ARP等功能,如果是使用操作系统的话,还需要建立接收数据,发送数据的任务以及一些需要的消息队 列、信号量等,此处讲解的是裸机底层驱动的编写,low_level_init()源码具体见代码清单

1 static void low_level_init(struct netif *netif)
2 {
3 HAL_StatusTypeDef hal_eth_init_status;
4
5 //初始化 bsp—eth
6 hal_eth_init_status = Bsp_Eth_Init(); (1)
7
8 if (hal_eth_init_status == HAL_OK)
9 {
10 /* Set netif link flag */
11 netif->flags |= NETIF_FLAG_LINK_UP;
12 }
13
14 #if LWIP_ARP || LWIP_ETHERNET
15
16 /* set MAC hardware address length */
17 netif->hwaddr_len = ETH_HWADDR_LEN;
18
19 /* set MAC hardware address */
20 netif->hwaddr[0] = heth.Init.MACAddr[0];
21 netif->hwaddr[1] = heth.Init.MACAddr[1];
22 netif->hwaddr[2] = heth.Init.MACAddr[2];
23 netif->hwaddr[3] = heth.Init.MACAddr[3];
24 netif->hwaddr[4] = heth.Init.MACAddr[4];
25 netif->hwaddr[5] = heth.Init.MACAddr[5];
26
27 /* maximum transfer unit */
28 netif->mtu = NETIF_MTU;
29
30 #if LWIP_ARP
31 netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
32 #else
33 netif->flags |= NETIF_FLAG_BROADCAST;
34 #endif /* LWIP_ARP */
35
36 #endif /* LWIP_ARP || LWIP_ETHERNET */
37
38 HAL_ETH_Start(&heth); (2)
39 }

(1)调用我们自己实现的以太网驱动初始化函数 Bsp_Eth_Init(),这是根 据网卡的驱动所编写的函数,不同的网卡是不一样的.

(2)初始化完成就需要启动网卡,才能进行数据的收发操作。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值