1/* 2 * Copyright (c) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved. 3 * Copyright (c) 2023-2023 Huawei Device Co., Ltd. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without modification, 6 * are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this list of 9 * conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, this list 12 * of conditions and the following disclaimer in the documentation and/or other materials 13 * provided with the distribution. 14 * 15 * 3. Neither the name of the copyright holder nor the names of its contributors may be used 16 * to endorse or promote products derived from this software without specific prior written 17 * permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32#include "lwip/opt.h" 33 34#if LWIP_LOWPOWER 35#include "lwip/priv/tcp_priv.h" 36 37#include "lwip/def.h" 38#include "lwip/memp.h" 39#include "lwip/priv/tcpip_priv.h" 40 41#include "lwip/ip4_frag.h" 42#include "lwip/etharp.h" 43#include "lwip/dhcp.h" 44#include "lwip/autoip.h" 45#include "lwip/igmp.h" 46#include "lwip/dns.h" 47#include "lwip/nd6.h" 48#include "lwip/ip6_frag.h" 49#include "lwip/mld6.h" 50#include "lwip/dhcp6.h" 51#include "lwip/sys.h" 52#include "lwip/pbuf.h" 53#include "netif/lowpan6.h" 54#include "lwip/api.h" 55 56#include "lwip/lowpower.h" 57 58#define TIMEOUT_MAX 120000 /* two mins */ 59#define SYS_TIMEOUT_WAIT_TICKS 30 60#define SYS_TIMEOUT_WAIT_TIME_MS 3 61 62static u32_t g_wake_up_time = TIMEOUT_MAX; 63static enum lowpower_mod g_lowpower_switch = LOW_TMR_LOWPOWER_MOD; 64static struct timer_entry *g_timer_header = NULL; 65static struct timer_mng g_timer_mng = { LOW_TMR_TIMER_HANDLING, 0 }; 66static u32_t g_last_check_timeout = 0; 67 68static const struct timer_handler lowpower_timer_handler[] = { 69#if LWIP_TCP 70 /* 71 * The TCP timer is a special case: it does not have to run always and 72 * is triggered to start from TCP using tcp_timer_needed() 73 */ 74 {TCP_FAST_INTERVAL, tcp_fasttmr, tcp_fast_tmr_tick TCP_FASTTMR_NAME}, 75 {TCP_SLOW_INTERVAL, tcp_slowtmr, tcp_slow_tmr_tick TCP_SLOWTMR_NAME}, 76#endif /* LWIP_TCP */ 77 78#if LWIP_IPV4 79#if IP_REASSEMBLY 80 {IP_TMR_INTERVAL, ip_reass_tmr, ip_reass_tmr_tick IP_REASSTRM_NAME}, 81#endif /* IP_REASSEMBLY */ 82 83#if LWIP_ARP 84 {ARP_TMR_INTERVAL, etharp_tmr, etharp_tmr_tick ETHARPTMR_NAME}, 85#endif /* LWIP_ARP */ 86 87#if LWIP_DHCP 88 {DHCP_FINE_TIMER_MSECS, dhcp_fine_tmr, dhcp_fine_tmr_tick DHCP_FINETMR_NAME}, 89 {DHCP_COARSE_TIMER_MSECS, dhcp_coarse_tmr, dhcp_coarse_tmr_tick DHCP_COARSETMR_NAME}, 90#endif /* LWIP_DHCP */ 91 92#if LWIP_AUTOIP 93 {AUTOIP_TMR_INTERVAL, autoip_tmr, autoip_tmr_tick AUTOIPTMR_NAME}, 94#endif /* LWIP_AUTOIP */ 95 96#if LWIP_IGMP 97 {IGMP_TMR_INTERVAL, igmp_tmr, igmp_tmr_tick IGMPTMR_NAME}, 98#endif /* LWIP_IGMP */ 99#endif /* LWIP_IPV4 */ 100 101#if LWIP_DNS 102 {DNS_TMR_INTERVAL, dns_tmr, dns_tmr_tick DNSTMR_NAME}, 103#endif /* LWIP_DNS */ 104 105#if LWIP_IPV6 106 {ND6_TMR_INTERVAL, nd6_tmr, nd6_tmr_tick ND6TMR_NAME}, 107#if LWIP_IPV6_REASS 108 {IP6_REASS_TMR_INTERVAL, ip6_reass_tmr, ip6_reass_tmr_tick IP6_TREASSTMR_NAME}, 109#endif /* LWIP_IPV6_REASS */ 110 111#if LWIP_IPV6_MLD 112 {MLD6_TMR_INTERVAL, mld6_tmr, mld6_tmr_tick MLD6TMR_NAME}, 113#endif /* LWIP_IPV6_MLD */ 114 115#if LWIP_IPV6_DHCP6 116 {DHCP6_TIMER_MSECS, dhcp6_tmr, dhcp6_tmr_tick DHCP6TMR_NAME}, 117#endif /* LWIP_IPV6_DHCP6 */ 118 119#if LWIP_6LOWPAN 120 {LOWPAN6_TMR_INTERVAL, lowpan6_tmr, lowpan6_tmr_tick LOWPAN6TMR_NAME}, 121#endif /* LWIP_6LOWPAN */ 122#endif /* LWIP_IPV6 */ 123}; 124 125void 126set_timer_state(enum timer_state state, u32_t waiting_time) 127{ 128 g_timer_mng.waiting_time = waiting_time; 129 g_timer_mng.state = state; 130} 131 132/* should not call by tcpip_thread */ 133u8_t 134sys_timeout_waiting_long(void) 135{ 136 u8_t i = 0; 137 u8_t j = 0; 138 139 while (g_timer_mng.state == LOW_TMR_GETING_TICKS) { 140 i++; 141 if (i == SYS_TIMEOUT_WAIT_TICKS) { 142 i = 0; 143 j++; 144 if (j == SYS_TIMEOUT_WAIT_TIME_MS) { 145 break; 146 } 147 sys_msleep(1); 148 } 149 } 150 151 if (g_timer_mng.state == LOW_TMR_TIMER_WAITING) { 152 return ((g_timer_mng.waiting_time > TIMEOUT_TICK) ? 1 : 0); 153 } 154 155 return 0; 156} 157 158err_t 159sys_timeout_reg( 160 u32_t msec, 161 sys_timeout_handler handler, 162 void *arg, 163#if LOWPOWER_TIMER_DEBUG 164 char *name, 165#endif 166 get_next_timeout next_tick) 167{ 168 struct timer_entry *timeout = NULL; 169 struct timer_entry *temp = NULL; 170 171 if (handler == NULL) { 172 return -1; 173 } 174 175 timeout = (struct timer_entry *)memp_malloc(MEMP_SYS_TIMEOUT); 176 if (timeout == NULL) { 177 LWIP_DEBUGF(LOWPOWER_DEBUG, ("sys_timeout_reg: timeout != NULL, pool MEMP_SYS_TIMEOUT is empty")); 178 return -1; 179 } 180 181 timeout->handler = handler; 182 timeout->clock_max = msec / TIMEOUT_TICK; /* time interval */ 183 timeout->next_tick = next_tick; 184 timeout->timeout = sys_now(); 185 timeout->args = arg; 186 timeout->enable = 0; 187 188#if LOWPOWER_TIMER_DEBUG 189 timeout->name = name; 190#endif 191 192 /* add list tail */ 193 timeout->next = NULL; 194 if (g_timer_header == NULL) { 195 g_timer_header = timeout; 196 } else { 197 temp = g_timer_header; 198 while (temp->next != NULL) { 199 temp = temp->next; 200 } 201 temp->next = timeout; 202 } 203 204 return 0; 205} 206 207/* deal timeout and return next timer prev */ 208static void 209timeout_handler(struct timer_entry *t, u32_t now) 210{ 211#if LOWPOWER_TIMER_DEBUG 212 if (t->name != NULL) { 213 LWIP_DEBUGF(LOWPOWER_DEBUG, ("%s timeout: now:%u\n", t->name, now)); 214 } 215#endif 216 217 t->handler(t->args); 218 LWIP_UNUSED_ARG(now); 219 t->timeout += t->clock_max * TIMEOUT_TICK; 220} 221 222static u32_t 223get_timer_tick(struct timer_entry *t) 224{ 225 u32_t tick; 226 227 /* disable lowpower, need to timeout once a tick */ 228 if (g_lowpower_switch == LOW_TMR_NORMAL_MOD) { 229 return t->clock_max; 230 } 231 232 if (t->next_tick != NULL) { 233 tick = t->next_tick(); 234 } else { 235 tick = 1; 236 LWIP_DEBUGF(LOWPOWER_DEBUG, ("next->tick is NULL\n")); 237 } 238 tick *= t->clock_max; 239 return tick; 240} 241 242static void 243handle_timer_and_free(struct timer_entry **pt, struct timer_entry **pn, struct timer_entry *p) 244{ 245 struct timer_entry *t = *pt; 246 struct timer_entry *n = *pn; 247 /* insert after previous node or as the header */ 248 if (p == NULL) { 249 g_timer_header = n; 250 } else { 251 p->next = n; 252 } 253 254 t->next = NULL; 255 sys_timeout_handler handler = t->handler; 256 void *args = t->args; 257 memp_free(MEMP_SYS_TIMEOUT, t); 258 *pt = NULL; 259 handler(args); 260 261 /* the last entry */ 262 if ((n == NULL) && (p != NULL)) { 263 *pn = p->next; 264 } 265} 266 267static u32_t 268get_sleep_time(u32_t now) 269{ 270 struct timer_entry *t = NULL; 271 struct timer_entry *n = NULL; 272 struct timer_entry *p = NULL; 273 u32_t msec = TIMEOUT_MAX; 274 u32_t tick; 275 u32_t temp; 276 277 LWIP_DEBUGF(LOWPOWER_DEBUG, ("\n*******get_sleep_time*****************\n")); 278 279 for (t = g_timer_header, p = NULL; t != NULL; t = n) { 280 n = t->next; 281again: 282 tick = get_timer_tick(t); 283 if (tick == 0) { 284 t->enable = 0; 285 p = t; 286 continue; 287 } 288 289 if (t->enable == 0) { 290 t->timeout = now; 291 } 292 t->enable = 1; 293 temp = tick * TIMEOUT_TICK; 294 295 if (temp <= now - t->timeout) { 296 if (t->next_tick == NULL) { 297 handle_timer_and_free(&t, &n, p); 298 /* t is free p=p */ 299 continue; 300 } 301 timeout_handler(t, now); 302 goto again; 303 } 304 305 temp = temp - (now - t->timeout); 306 msec = msec > temp ? temp : msec; 307 p = t; 308 } 309 310 LWIP_DEBUGF(LOWPOWER_DEBUG, ("msec = %u now = %u\n", msec, now)); 311 return msec; 312} 313 314static void 315check_timeout(u32_t now) 316{ 317 struct timer_entry *t = NULL; 318 struct timer_entry *n = NULL; 319 struct timer_entry *p = NULL; 320 u32_t msec; 321 u32_t ticks; 322 u32_t i; 323 324 LWIP_DEBUGF(LOWPOWER_DEBUG, ("\n**********timeout**************\n")); 325 LWIP_DEBUGF(LOWPOWER_DEBUG, ("now = %u\n", now)); 326 327 for (t = g_timer_header, p = NULL; t != NULL; t = n) { 328 n = t->next; 329 if (t->enable == 0) { 330 p = t; 331 continue; 332 } 333 334 msec = now - t->timeout; 335 ticks = msec / TIMEOUT_TICK; 336 ticks = ticks / t->clock_max; 337 338 for (i = 0; i < ticks; i++) { 339 /* remove timer_entry form list */ 340 if (t->next_tick == NULL) { 341 handle_timer_and_free(&t, &n, p); 342 break; 343 } 344 timeout_handler(t, now); 345 PBUF_CHECK_FREE_OOSEQ(); 346 } 347 if (t != NULL) { 348 p = t; 349 } 350 } 351} 352 353void 354tcpip_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg) 355{ 356 u32_t sleeptime; 357 u32_t ret; 358 u32_t now; 359 360again: 361 if (g_timer_header == NULL) { 362 UNLOCK_TCPIP_CORE(); 363 (void)sys_arch_mbox_fetch(mbox, msg, 0); 364 LOCK_TCPIP_CORE(); 365 return; 366 } 367 368 set_timer_state(LOW_TMR_GETING_TICKS, 0); 369 sleeptime = get_sleep_time(sys_now()); 370 sleeptime = sleeptime > sys_timeout_get_wake_time() ? sys_timeout_get_wake_time() : sleeptime; 371 set_timer_state(LOW_TMR_TIMER_WAITING, sleeptime); 372 373 sys_timeout_set_wake_time(TIMEOUT_MAX); 374 UNLOCK_TCPIP_CORE(); 375 ret = sys_arch_mbox_fetch(mbox, msg, sleeptime); 376 LOCK_TCPIP_CORE(); 377 set_timer_state(LOW_TMR_TIMER_HANDLING, 0); 378 now = sys_now(); 379 if ((now - g_last_check_timeout) >= TIMEOUT_CHECK) { 380 check_timeout(sys_now()); 381 g_last_check_timeout = sys_now(); 382 } 383 384 if (ret == SYS_ARCH_TIMEOUT) { 385 goto again; 386 } 387} 388 389void 390lowpower_cycle_tmr(void *args) 391{ 392 struct timer_handler *handler = (struct timer_handler *)args; 393 394 handler->handler(); 395} 396 397err_t 398set_timer_interval(u8_t i, u32_t interval) 399{ 400 if (i >= LWIP_ARRAYSIZE(lowpower_timer_handler)) { 401 return -1; 402 } 403 return sys_timeout_reg(interval, lowpower_cycle_tmr, 404 (void *)(&lowpower_timer_handler[i]), 405#if LOWPOWER_TIMER_DEBUG 406 lowpower_timer_handler[i].name, 407#endif 408 lowpower_timer_handler[i].next_tick); 409} 410 411/* registed when init */ 412void 413tcp_timer_needed(void) 414{} 415 416void 417sys_timeouts_init(void) 418{ 419 u8_t i; 420 421 for (i = 0; i < LWIP_ARRAYSIZE(lowpower_timer_handler); i++) { 422 if (set_timer_interval(i, lowpower_timer_handler[i].interval) != 0) { 423 LWIP_DEBUGF(LOWPOWER_DEBUG, ("ERROR:regist timer faild! i = %u\n", i)); 424 } 425 } 426} 427 428void 429sys_untimeout(sys_timeout_handler handler, void *arg) 430{ 431 struct timer_entry *t = NULL; 432 struct timer_entry *p = NULL; 433 struct timer_entry *n = NULL; 434 435 for (t = g_timer_header, p = NULL; t != NULL; t = n) { 436 n = t->next; 437 if ((t->handler == handler) && (t->args == arg)) { 438 if (p == NULL) { 439 g_timer_header = t->next; 440 } else { 441 p->next = t->next; 442 } 443 t->next = NULL; 444 memp_free(MEMP_SYS_TIMEOUT, t); 445 t = NULL; 446 } 447 if (t != NULL) { 448 p = t; 449 } 450 } 451} 452void 453sys_restart_timeouts(void) 454{} 455 456err_t 457sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg) 458{ 459 return sys_timeout_reg(msecs, handler, arg, 460#if LOWPOWER_TIMER_DEBUG 461 NULL, 462#endif 463 NULL); 464} 465 466void 467sys_timeout_set_wake_time(u32_t val) 468{ 469 g_wake_up_time = val; 470} 471 472u32_t 473sys_timeout_get_wake_time(void) 474{ 475 return g_wake_up_time; 476} 477 478void 479set_lowpower_mod(enum lowpower_mod sw) 480{ 481 g_lowpower_switch = sw; 482} 483 484enum lowpower_mod 485get_lowpowper_mod(void) 486{ 487 return g_lowpower_switch; 488} 489#endif /* LWIP_LOWPOWER */ 490