tcpip_init就是LWIP的初始化函数.一般传递tcpip_init( NULL, NULL ),也就是没有用户的init函数.
/**
* @ingroup lwip_os
* Initialize this module:
* - initialize all sub modules
* - start the tcpip_thread
*
* @param initfunc a function to call when tcpip_thread is running and finished initializing
* @param arg argument to pass to initfunc
*/
void
tcpip_init(tcpip_init_done_fn initfunc, void *arg)
{
lwip_init();
tcpip_init_done = initfunc;
tcpip_init_done_arg = arg;
if (sys_mbox_new(&mbox, TCPIP_MBOX_SIZE) != ERR_OK) { /* 1)申请一个mbox */
LWIP_ASSERT("failed to create tcpip_thread mbox", 0);
}
#if LWIP_TCPIP_CORE_LOCKING
if (sys_mutex_new(&lock_tcpip_core) != ERR_OK) { /* 2)申请一个线程总锁 */
LWIP_ASSERT("failed to create lock_tcpip_core", 0);
}
#endif /* LWIP_TCPIP_CORE_LOCKING */
/* 真正任务处理.实际上数据包input解锁他. */
sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);
}
是其中因为没有传递inifunc,也没arg,所以前面init_done和init_done_arg两句就没用的.如果给他传递了,会在tcpip_thread里面先执行init_done的钩子函数.属于初始化后的结果.
而最关键的lwip_init,有很多操作其实也是没用的(非关键)
/**
* @ingroup lwip_nosys
* Initialize all modules.
* Use this in NO_SYS mode. Use tcpip_init() otherwise.
*/
void
lwip_init(void)
{
#ifndef LWIP_SKIP_CONST_CHECK
int a = 0;
LWIP_UNUSED_ARG(a);
LWIP_ASSERT("LWIP_CONST_CAST not implemented correctly. Check your lwIP port.", LWIP_CONST_CAST(void*, &a) == &a);
#endif
#ifndef LWIP_SKIP_PACKING_CHECK
LWIP_ASSERT("Struct packing not implemented correctly. Check your lwIP port.", sizeof(struct packed_struct_test) == PACKED_STRUCT_TEST_EXPECTED_SIZE);
#endif
/* Modules initialization */
stats_init();
#if !NO_SYS
sys_init();
#endif /* !NO_SYS */
mem_init();
memp_init();
pbuf_init();
netif_init();
#if LWIP_IPV4
ip_init();
#if LWIP_ARP
etharp_init();
#endif /* LWIP_ARP */
#endif /* LWIP_IPV4 */
#if LWIP_RAW
raw_init();
#endif /* LWIP_RAW */
#if LWIP_UDP
udp_init();
#endif /* LWIP_UDP */
#if LWIP_TCP
tcp_init();
#endif /* LWIP_TCP */
#if LWIP_IGMP
igmp_init();
#endif /* LWIP_IGMP */
#if LWIP_DNS
dns_init();
#endif /* LWIP_DNS */
#if PPP_SUPPORT
ppp_init();
#endif
#if LWIP_TIMERS
sys_timeouts_init();
#endif /* LWIP_TIMERS */
}
实际上做过事情的代码:
void
lwip_init(void)
{
sys_init(); /* 初始化系统的锁 */
mem_init(); /* 初始化内存指针 */
memp_init(); /* 初始化内存池 */
sys_timeouts_init(); /* 定时任务 */
}
sys_init初始化系统锁,SYS_ARCH_UNPROTECT和SYS_ARCH_PROTECT用的就是这个锁,基本等于最底层的锁.
mem_init初始化了ram和ram_end定义,一般来说,ram只能在ram到ram_end - 1区申请,因为ram_end总是used,如果ram_end一使用,就溢出,他是最后一个元素+1,永远不能用.

memp_init就是循环遍历所有的mem_pool,然后用memp_init_pool来初始化.其中还分两种方法,一种是malloc方法,使用系统的RAM,一种是使用它自己的RAM.
/**
* Initialize custom memory pool.
* Related functions: memp_malloc_pool, memp_free_pool
*
* @param desc pool to initialize
*/
void
memp_init_pool(const struct memp_desc *desc)
{
#if MEMP_MEM_MALLOC
LWIP_UNUSED_ARG(desc);
#else
int i;
struct memp *memp;
*desc->tab = NULL;
memp = (struct memp*)LWIP_MEM_ALIGN(desc->base);
/* create a linked list of memp elements */
for (i = 0; i < desc->num; ++i) {
memp->next = *desc->tab;
*desc->tab = memp;
#if MEMP_OVERFLOW_CHECK
memp_overflow_init_element(memp, desc);
#endif /* MEMP_OVERFLOW_CHECK */
/* cast through void* to get rid of alignment warnings */
memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + desc->size
#if MEMP_OVERFLOW_CHECK
+ MEMP_SANITY_REGION_AFTER_ALIGNED
#endif
);
}
#if MEMP_STATS
desc->stats->avail = desc->num;
#endif /* MEMP_STATS */
#endif /* !MEMP_MEM_MALLOC */
#if MEMP_STATS && (defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY)
desc->stats->name = desc->desc;
#endif /* MEMP_STATS && (defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY) */
}
可见,根据desc->size + MEMP_SIZE(不检查溢出时为0),赋值memp所在地址.

所以实际上有用代码只有这么点.
for (i = 0; i < desc->num; ++i) {
memp->next = *desc->tab;
*desc->tab = memp;
/* cast through void* to get rid of alignment warnings */
memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + desc->size
);
}
那大小哪里定义呢:

他们都是一一对应的.

总体来说他的初始化就是个赋值操作.
最后,sys_timeouts_init就涉及定时任务了.
/** Initialize this module */
void sys_timeouts_init(void)
{
size_t i;
/* tcp_tmr() at index 0 is started on demand */
for (i = (LWIP_TCP ? 1 : 0); i < LWIP_ARRAYSIZE(lwip_cyclic_timers); i++) {
/* we have to cast via size_t to get rid of const warning
(this is OK as cyclic_timer() casts back to const* */
sys_timeout(lwip_cyclic_timers[i].interval_ms, cyclic_timer, LWIP_CONST_CAST(void*, &lwip_cyclic_timers[i]));
}
/* Initialise timestamp for sys_check_timeouts */
timeouts_last_time = sys_now();
}
看出来,最主要就是循环sys_timeout,而这个正是定时任务,网络应用里面很多定时应用.最关键肯定是lwip_cyclic_timer,第二个参数对应的是执行用的fn,然后是参数.
我们看看需要定时的任务有很多.
/** This array contains all stack-internal cyclic timers. To get the number of
* timers, use LWIP_ARRAYSIZE() */
const struct lwip_cyclic_timer lwip_cyclic_timers[] = {
#if LWIP_TCP
/* The TCP timer is a special case: it does not have to run always and
is triggered to start from TCP using tcp_timer_needed() */
{TCP_TMR_INTERVAL, HANDLER(tcp_tmr)},
#endif /* LWIP_TCP */
#if LWIP_IPV4
#if IP_REASSEMBLY
{IP_TMR_INTERVAL, HANDLER(ip_reass_tmr)},
#endif /* IP_REASSEMBLY */
#if LWIP_ARP
{ARP_TMR_INTERVAL, HANDLER(etharp_tmr)},
#endif /* LWIP_ARP */
#if LWIP_DHCP
{DHCP_COARSE_TIMER_MSECS, HANDLER(dhcp_coarse_tmr)},
{DHCP_FINE_TIMER_MSECS, HANDLER(dhcp_fine_tmr)},
#endif /* LWIP_DHCP */
#if LWIP_AUTOIP
{AUTOIP_TMR_INTERVAL, HANDLER(autoip_tmr)},
#endif /* LWIP_AUTOIP */
#if LWIP_IGMP
{IGMP_TMR_INTERVAL, HANDLER(igmp_tmr)},
#endif /* LWIP_IGMP */
#endif /* LWIP_IPV4 */
#if LWIP_DNS
{DNS_TMR_INTERVAL, HANDLER(dns_tmr)},
#endif /* LWIP_DNS */
#if LWIP_IPV6
{ND6_TMR_INTERVAL, HANDLER(nd6_tmr)},
#if LWIP_IPV6_REASS
{IP6_REASS_TMR_INTERVAL, HANDLER(ip6_reass_tmr)},
#endif /* LWIP_IPV6_REASS */
#if LWIP_IPV6_MLD
{MLD6_TMR_INTERVAL, HANDLER(mld6_tmr)},
#endif /* LWIP_IPV6_MLD */
#endif /* LWIP_IPV6 */
};
比如tcp_tmr,在定时器内做的肯定是关于TCP的定时器.TCP比如断开连接等等,都是需要定时器参与的.就知道这一点,足够了.
接下来的初始化,就是之前说过的内容了.回顾下.
/**
* 初始化总入口
*/
void MX_LWIP_Init(void)
{
/* 初始化协议栈,其实就是申请线程申请锁. */
tcpip_init( NULL, NULL );
/* 默认清零地址(DHCP方式申请) */
ipaddr.addr = 0;
netmask.addr = 0;
gw.addr = 0;
/* 增加一个网络接口,ethernetif_init是底层初始化,tcpip_input是协议栈入口,这个有点复杂.在ethernetif.c中调用ethernetif_input,这个来调用netif->input,最后引用了tcpip_input函数. */
netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, &tcpip_input);
/* 注册默认网络接口. */
netif_set_default(&gnetif);
if (netif_is_link_up(&gnetif))
{
/* 如果网络是连上,就介入网络好了. */
netif_set_up(&gnetif);
}
else
{
/* 网络没连上就关闭网络. */
netif_set_down(&gnetif);
}
/* DHCP其实完全可以开一条线程来守护,不然只能先插网线后上电. */
dhcp_start(&gnetif);
}