xref: /third_party/lwip/src/core/lowpower.c (revision 195972f6)
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