lwIP no-sys示例流程解析
本文将网络接口(network interface)
简称为网口
还有文章可供参考:《LwIP应用开发实战指南》
no-sys 无系统示例
中断部分:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 以太网帧接收中断处理程序
void eth_mac_irq()
{
/* Service MAC IRQ here */
// 假设收到了eth_data_count长度的数据
// 从lwip内的内存池中分配空间来存放该数据
/* Allocate pbuf from pool (avoid using heap in interrupts) */
struct pbuf *p = pbuf_alloc(PBUF_RAW, eth_data_count, PBUF_POOL);
if (p != NULL)
{
/* Copy ethernet frame into pbuf */
pbuf_take(p, eth_data, eth_data_count);
/* Put in a queue which is processed in main loop */
// 将该帧置于queue帧队列中
if (!queue_try_put(&queue, p))
{
/* queue is full -> packet loss */
pbuf_free(p);
}
}
}
应用部分:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
static void
netif_status_callback(struct netif *netif)
{
printf("netif status changed %s\n", ip4addr_ntoa(netif_ip4_addr(netif)));
}
// 初始化配置
static err_t
netif_init(struct netif *netif)
{
netif->linkoutput = netif_output;
netif->output = etharp_output;
netif->output_ip6 = ethip6_output;
netif->mtu = ETHERNET_MTU;
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET | NETIF_FLAG_IGMP | NETIF_FLAG_MLD6;
MIB2_INIT_NETIF(netif, snmp_ifType_ethernet_csmacd, 100000000);
SMEMCPY(netif->hwaddr, your_mac_address_goes_here, ETH_HWADDR_LEN);
netif->hwaddr_len = ETH_HWADDR_LEN;
return ERR_OK;
}
void main(void)
{
struct netif netif;
// lwip初始化
lwip_init();
// 绑定网口(网络接口),需要ipv4地址,子网掩码,网关,网口初始化回调函数,上层输入回调函数
netif_add(&netif, IP4_ADDR_ANY, IP4_ADDR_ANY, IP4_ADDR_ANY, NULL, netif_init, netif_input);
netif.name[0] = 'e';
netif.name[1] = '0';
netif_create_ip6_linklocal_address(&netif, 1);
// 注册状态改变回调,包括网口link up/down,ip地址变动等都会触发回调
netif_set_status_callback(&netif, netif_status_callback);
// 将该网口设置为默认网口
netif_set_default(&netif);
// 启用网口
netif_set_up(&netif);
/* Start DHCP and HTTPD */
dhcp_start(&netif);
// 启动httpd服务
httpd_init();
while (1)
{
/* Check link state, e.g. via MDIO communication with PHY */
// 同步一次物理链路状态
if (link_state_changed())
{
if (link_is_up())
{
netif_set_link_up(&netif);
}
else
{
netif_set_link_down(&netif);
}
}
/* Check for received frames, feed them to lwIP */
// 为临界资源queue上锁
lock_interrupts();
// 检查链路层收到的帧队列,并取出一条
struct pbuf *p = queue_try_get(&queue);
unlock_interrupts();
if (p != NULL)
{
// 增加链路通信统计计数
LINK_STATS_INC(link.recv);
/* Update SNMP stats (only if you use SNMP) */
MIB2_STATS_NETIF_ADD(netif, ifinoctets, p->tot_len);
int unicast = ((p->payload[0] & 0x01) == 0);
if (unicast)
{
MIB2_STATS_NETIF_INC(netif, ifinucastpkts);
}
else
{
MIB2_STATS_NETIF_INC(netif, ifinnucastpkts);
}
if (netif.input(p, &netif) != ERR_OK)
{
pbuf_free(p);
}
}
/* Cyclic lwIP timers check */
// lwIP内部定时器,用于处理内部定时任务
sys_check_timeouts();
/* your application goes here */
// 如果原来的无系统应用是大循环式的,可以直接内嵌到这
}
}
接收网口提供的帧
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
err_t
netif_input(struct pbuf *p, struct netif *inp)
{
LWIP_ASSERT_CORE_LOCKED();
LWIP_ASSERT("netif_input: invalid pbuf", p != NULL);
LWIP_ASSERT("netif_input: invalid netif", inp != NULL);
// 如果启用了以太网
#if LWIP_ETHERNET
// 如果当前网卡支持以太网和以太网ARP帧,
// 那么其输出就是以太网类型的帧,使用以太网处理程序
if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
return ethernet_input(p, inp);
} else
#endif /* LWIP_ETHERNET */
// 否则就直接是网络层ip协议报文
return ip_input(p, inp);
}
以太网中常用的 Ethernet Ⅱ
帧格式:
1
2
3
4
5
6
| 6B | 6B | 2B | 46-1500B | 4B |
|DST.MAC|SRC.MAC| TYPE | DATA | FCS |
/ \
/ \
| 0x0800 IP |
| 0x0806 ARP|
以太网链路层处理程序
View Code
```c /** * @ingroup lwip_nosys * Process received ethernet frames. Using this function instead of directly * calling ip_input and passing ARP frames through etharp in ethernetif_input, * the ARP cache is protected from concurrent access.* Don't call directly, pass to netif_add() and call netif->input(). * * @param p the received packet, p->payload pointing to the ethernet header * @param netif the network interface on which the packet was received * * @see LWIP_HOOK_UNKNOWN_ETH_PROTOCOL * @see ETHARP_SUPPORT_VLAN * @see LWIP_HOOK_VLAN_CHECK */ err_t ethernet_input(struct pbuf *p, struct netif *netif) { struct eth_hdr *ethhdr; u16_t type; #if LWIP_ARP || ETHARP_SUPPORT_VLAN || LWIP_IPV6 u16_t next_hdr_offset = SIZEOF_ETH_HDR; #endif /* LWIP_ARP || ETHARP_SUPPORT_VLAN */ LWIP_ASSERT_CORE_LOCKED(); // Ethernet Ⅱ协议帧头(SIZEOF_ETH_HDR)为 6+6+2=14 字节 if (p->len <= SIZEOF_ETH_HDR) { /* a packet with only an ethernet header (or less) is not valid for us */ ETHARP_STATS_INC(etharp.proterr); ETHARP_STATS_INC(etharp.drop); MIB2_STATS_NETIF_INC(netif, ifinerrors); goto free_and_return; } /* points to packet payload, which starts with an Ethernet header */ ethhdr = (struct eth_hdr *)p->payload;// 提取帧头 LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("ethernet_input: dest:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", src:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", type:%"X16_F"\n", (unsigned char)ethhdr->dest.addr[0], (unsigned char)ethhdr->dest.addr[1], (unsigned char)ethhdr->dest.addr[2], (unsigned char)ethhdr->dest.addr[3], (unsigned char)ethhdr->dest.addr[4], (unsigned char)ethhdr->dest.addr[5], (unsigned char)ethhdr->src.addr[0], (unsigned char)ethhdr->src.addr[1], (unsigned char)ethhdr->src.addr[2], (unsigned char)ethhdr->src.addr[3], (unsigned char)ethhdr->src.addr[4], (unsigned char)ethhdr->src.addr[5], lwip_htons(ethhdr->type))); type = ethhdr->type; #if ETHARP_SUPPORT_VLAN if (type == PP_HTONS(ETHTYPE_VLAN)) { struct eth_vlan_hdr *vlan = (struct eth_vlan_hdr *)(((char *)ethhdr) + SIZEOF_ETH_HDR); next_hdr_offset = SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR; if (p->len <= SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) { /* a packet with only an ethernet/vlan header (or less) is not valid for us */ ETHARP_STATS_INC(etharp.proterr); ETHARP_STATS_INC(etharp.drop); MIB2_STATS_NETIF_INC(netif, ifinerrors); goto free_and_return; } #if defined(LWIP_HOOK_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) /* if not, allow all VLANs */ #ifdef LWIP_HOOK_VLAN_CHECK if (!LWIP_HOOK_VLAN_CHECK(netif, ethhdr, vlan)) { #elif defined(ETHARP_VLAN_CHECK_FN) if (!ETHARP_VLAN_CHECK_FN(ethhdr, vlan)) { #elif defined(ETHARP_VLAN_CHECK) if (VLAN_ID(vlan) != ETHARP_VLAN_CHECK) { #endif /* silently ignore this packet: not for our VLAN */ pbuf_free(p); return ERR_OK; } #endif /* defined(LWIP_HOOK_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) */ type = vlan->tpid; } #endif /* ETHARP_SUPPORT_VLAN */ #if LWIP_ARP_FILTER_NETIF netif = LWIP_ARP_FILTER_NETIF_FN(p, netif, lwip_htons(type)); #endif /* LWIP_ARP_FILTER_NETIF*/ if (p->if_idx == NETIF_NO_INDEX) { p->if_idx = netif_get_index(netif); } // 多播广播识别 if (ethhdr->dest.addr[0] & 1) { /* this might be a multicast or broadcast packet */ if (ethhdr->dest.addr[0] == LL_IP4_MULTICAST_ADDR_0) { #if LWIP_IPV4 if ((ethhdr->dest.addr[1] == LL_IP4_MULTICAST_ADDR_1) && (ethhdr->dest.addr[2] == LL_IP4_MULTICAST_ADDR_2)) { /* mark the pbuf as link-layer multicast */ p->flags |= PBUF_FLAG_LLMCAST; } #endif /* LWIP_IPV4 */ } #if LWIP_IPV6 else if ((ethhdr->dest.addr[0] == LL_IP6_MULTICAST_ADDR_0) && (ethhdr->dest.addr[1] == LL_IP6_MULTICAST_ADDR_1)) { /* mark the pbuf as link-layer multicast */ p->flags |= PBUF_FLAG_LLMCAST; } #endif /* LWIP_IPV6 */ else if (eth_addr_cmp(ðhdr->dest, ðbroadcast)) { /* mark the pbuf as link-layer broadcast */ p->flags |= PBUF_FLAG_LLBCAST; } } switch (type) { #if LWIP_IPV4 && LWIP_ARP /* IP packet? */ // Data为IP数据包 case PP_HTONS(ETHTYPE_IP): if (!(netif->flags & NETIF_FLAG_ETHARP)) { goto free_and_return; } /* skip Ethernet header (min. size checked above) */ if (pbuf_remove_header(p, next_hdr_offset)) { LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("ethernet_input: IPv4 packet dropped, too short (%"U16_F"/%"U16_F")\n", p->tot_len, next_hdr_offset)); LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("Can't move over header in packet\n")); goto free_and_return; } else { /* pass to IP layer */ // 作为ipv4处理函数的输入 ip4_input(p, netif); } break; // Data为ARP数据包 case PP_HTONS(ETHTYPE_ARP): if (!(netif->flags & NETIF_FLAG_ETHARP)) { goto free_and_return; } /* skip Ethernet header (min. size checked above) */ if (pbuf_remove_header(p, next_hdr_offset)) { LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("ethernet_input: ARP response packet dropped, too short (%"U16_F"/%"U16_F")\n", p->tot_len, next_hdr_offset)); LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("Can't move over header in packet\n")); ETHARP_STATS_INC(etharp.lenerr); ETHARP_STATS_INC(etharp.drop); goto free_and_return; } else { /* pass p to ARP module */ etharp_input(p, netif); } break; #endif /* LWIP_IPV4 && LWIP_ARP */ #if PPPOE_SUPPORT case PP_HTONS(ETHTYPE_PPPOEDISC): /* PPP Over Ethernet Discovery Stage */ pppoe_disc_input(netif, p); break; case PP_HTONS(ETHTYPE_PPPOE): /* PPP Over Ethernet Session Stage */ pppoe_data_input(netif, p); break; #endif /* PPPOE_SUPPORT */ #if LWIP_IPV6 case PP_HTONS(ETHTYPE_IPV6): /* IPv6 */ /* skip Ethernet header */ if ((p->len < next_hdr_offset) || pbuf_remove_header(p, next_hdr_offset)) { LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("ethernet_input: IPv6 packet dropped, too short (%"U16_F"/%"U16_F")\n", p->tot_len, next_hdr_offset)); goto free_and_return; } else { /* pass to IPv6 layer */ ip6_input(p, netif); } break; #endif /* LWIP_IPV6 */ default: #ifdef LWIP_HOOK_UNKNOWN_ETH_PROTOCOL if (LWIP_HOOK_UNKNOWN_ETH_PROTOCOL(p, netif) == ERR_OK) { break; } #endif ETHARP_STATS_INC(etharp.proterr); ETHARP_STATS_INC(etharp.drop); MIB2_STATS_NETIF_INC(netif, ifinunknownprotos); goto free_and_return; } /* This means the pbuf is freed or consumed, so the caller doesn't have to free it again */ return ERR_OK; free_and_return: pbuf_free(p); return ERR_OK; } ```
RAW API 回调管理
lwIP 开放了一些回调,来让用户能够在其中插入执行部分代码,通过使用注册函数来注册回调。
回调分为两类:
- 状态回调(status callback)
- 额外状态回调(ext status callback)
状态回调
当网口状态发生变化时触发回调,相关的状态如下:
- 设置ip地址
- 网络接口启用
- 网络接口关闭
- ipv6地址状态切换(INVALID, TEMPTATIVE, PREFERRED, DEPRECATED)
回调原型,参数为网口对象:
1
typedef void (*netif_status_callback_fn)(struct netif *netif);
注册函数(每个网口只能注册唯一回调):
1
void netif_set_status_callback(struct netif *netif, netif_status_callback_fn status_callback);
网口状态改变时触发回调,参数只有对应的网口对象,没有触发原因,需要在回调函数中自行判断当前状态。
额外状态回调
额外状态回调是对状态回调的补充,扩展了如下状态:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* @ingroup netif
* Extended netif status callback (NSC) reasons flags.
* May be extended in the future!
*/
typedef u16_t netif_nsc_reason_t;
/* used for initialization only */
#define LWIP_NSC_NONE 0x0000
/** netif was added. arg: NULL. Called AFTER netif was added. */
#define LWIP_NSC_NETIF_ADDED 0x0001
/** netif was removed. arg: NULL. Called BEFORE netif is removed. */
#define LWIP_NSC_NETIF_REMOVED 0x0002
/** link changed */
#define LWIP_NSC_LINK_CHANGED 0x0004
/** netif administrative status changed.<br>
* up is called AFTER netif is set up.<br>
* down is called BEFORE the netif is actually set down. */
#define LWIP_NSC_STATUS_CHANGED 0x0008
/** IPv4 address has changed */
#define LWIP_NSC_IPV4_ADDRESS_CHANGED 0x0010
/** IPv4 gateway has changed */
#define LWIP_NSC_IPV4_GATEWAY_CHANGED 0x0020
/** IPv4 netmask has changed */
#define LWIP_NSC_IPV4_NETMASK_CHANGED 0x0040
/** called AFTER IPv4 address/gateway/netmask changes have been applied */
#define LWIP_NSC_IPV4_SETTINGS_CHANGED 0x0080
/** IPv6 address was added */
#define LWIP_NSC_IPV6_SET 0x0100
/** IPv6 address state has changed */
#define LWIP_NSC_IPV6_ADDR_STATE_CHANGED 0x0200
/** IPv4 settings: valid address set, application may start to communicate */
#define LWIP_NSC_IPV4_ADDR_VALID 0x0400
回调原型:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/**
* @ingroup netif
* Function used for extended netif status callbacks
* Note: When parsing reason argument, keep in mind that more reasons may be added in the future!
* @param netif netif that is affected by change
* @param reason change reason
* @param args depends on reason, see reason description
*/
typedef void (*netif_ext_callback_fn)(struct netif* netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t* args);
// 链表节点 netif_ext_callback_t
typedef struct netif_ext_callback
{
netif_ext_callback_fn callback_fn;
struct netif_ext_callback* next;
} netif_ext_callback_t;
// 额外参数 netif_ext_callback_args_t
/** @ingroup netif
* Argument supplied to netif_ext_callback_fn.
*/
typedef union
{
/** Args to LWIP_NSC_LINK_CHANGED callback */
struct link_changed_s
{
/** 1: up; 0: down */
u8_t state;
} link_changed;
/** Args to LWIP_NSC_STATUS_CHANGED callback */
struct status_changed_s
{
/** 1: up; 0: down */
u8_t state;
} status_changed;
/** Args to LWIP_NSC_IPV4_ADDRESS_CHANGED|LWIP_NSC_IPV4_GATEWAY_CHANGED|LWIP_NSC_IPV4_NETMASK_CHANGED|LWIP_NSC_IPV4_SETTINGS_CHANGED callback */
struct ipv4_changed_s
{
/** Old IPv4 address */
const ip_addr_t* old_address;
const ip_addr_t* old_netmask;
const ip_addr_t* old_gw;
} ipv4_changed;
/** Args to LWIP_NSC_IPV6_SET callback */
struct ipv6_set_s
{
/** Index of changed IPv6 address */
s8_t addr_index;
/** Old IPv6 address */
const ip_addr_t* old_address;
} ipv6_set;
/** Args to LWIP_NSC_IPV6_ADDR_STATE_CHANGED callback */
struct ipv6_addr_state_changed_s
{
/** Index of affected IPv6 address */
s8_t addr_index;
/** Old IPv6 address state */
u8_t old_state;
/** Affected IPv6 address */
const ip_addr_t* address;
} ipv6_addr_state_changed;
} netif_ext_callback_args_t;
注册函数:
1
2
void netif_add_ext_callback(netif_ext_callback_t* callback, netif_ext_callback_fn fn);
void netif_remove_ext_callback(netif_ext_callback_t* callback);
lwIP 内部使用链表来管理额外状态回调,其中netif_ext_callback_t
结构用来存放回调函数和 next 指针,用以形成链表:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* @ingroup netif
* Add extended netif events listener
* @param callback pointer to listener structure
* @param fn callback function
*/
void
netif_add_ext_callback(netif_ext_callback_t *callback, netif_ext_callback_fn fn)
{
LWIP_ASSERT_CORE_LOCKED();
LWIP_ASSERT("callback must be != NULL", callback != NULL);
LWIP_ASSERT("fn must be != NULL", fn != NULL);
callback->callback_fn = fn;
// 链表追加
callback->next = ext_callback;
ext_callback = callback;
}
当发生一个事件时,执行额外状态回调,为回调函数传递参数: 网口对象、触发原因、额外参数。
本文由作者按照 CC BY 4.0 进行授权