1 /** 2 * @file 3 * Stack-internal timers implementation. 4 * This file includes timer callbacks for stack-internal timers as well as 5 * functions to set up or stop timers and check for expired timers. 6 * 7 */ 8 9 /* 10 * Copyright (c) 2001-2004 Swedish Institute of Computer Science. 11 * All rights reserved. 12 * 13 * Redistribution and use in source and binary forms, with or without modification, 14 * are permitted provided that the following conditions are met: 15 * 16 * 1. Redistributions of source code must retain the above copyright notice, 17 * this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright notice, 19 * this list of conditions and the following disclaimer in the documentation 20 * and/or other materials provided with the distribution. 21 * 3. The name of the author may not be used to endorse or promote products 22 * derived from this software without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 25 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 26 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 27 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 29 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 32 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 33 * OF SUCH DAMAGE. 34 * 35 * This file is part of the lwIP TCP/IP stack. 36 * 37 * Author: Adam Dunkels <adam (at) sics.se> 38 * Simon Goldschmidt 39 * 40 */ 41 42 #include "lwip/opt.h" 43 44 #include "lwip/timers.h" 45 #include "lwip/tcp_impl.h" 46 47 #if LWIP_TIMERS 48 49 #include "lwip/def.h" 50 #include "lwip/memp.h" 51 #include "lwip/tcpip.h" 52 53 #include "lwip/ip_frag.h" 54 #include "netif/etharp.h" 55 #include "lwip/dhcp.h" 56 #include "lwip/autoip.h" 57 #include "lwip/igmp.h" 58 #include "lwip/dns.h" 59 60 61 /** The one and only timeout list */ 62 static struct sys_timeo *next_timeout; 63 #if NO_SYS 64 static u32_t timeouts_last_time; 65 #endif /* NO_SYS */ 66 67 #if LWIP_TCP 68 /** global variable that shows if the tcp timer is currently scheduled or not */ 69 static int tcpip_tcp_timer_active; 70 71 /** 72 * Timer callback function that calls tcp_tmr() and reschedules itself. 73 * 74 * @param arg unused argument 75 */ 76 static void 77 tcpip_tcp_timer(void *arg) 78 { 79 LWIP_UNUSED_ARG(arg); 80 81 /* call TCP timer handler */ 82 tcp_tmr(); 83 /* timer still needed? */ 84 if (tcp_active_pcbs || tcp_tw_pcbs) { 85 /* restart timer */ 86 sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL); 87 } else { 88 /* disable timer */ 89 tcpip_tcp_timer_active = 0; 90 } 91 } 92 93 /** 94 * Called from TCP_REG when registering a new PCB: 95 * the reason is to have the TCP timer only running when 96 * there are active (or time-wait) PCBs. 97 */ 98 void 99 tcp_timer_needed(void) 100 { 101 /* timer is off but needed again? */ 102 if (!tcpip_tcp_timer_active && (tcp_active_pcbs || tcp_tw_pcbs)) { 103 /* enable and start timer */ 104 tcpip_tcp_timer_active = 1; 105 sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL); 106 } 107 } 108 #endif /* LWIP_TCP */ 109 110 #if IP_REASSEMBLY 111 /** 112 * Timer callback function that calls ip_reass_tmr() and reschedules itself. 113 * 114 * @param arg unused argument 115 */ 116 static void 117 ip_reass_timer(void *arg) 118 { 119 LWIP_UNUSED_ARG(arg); 120 LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: ip_reass_tmr()\n")); 121 ip_reass_tmr(); 122 sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL); 123 } 124 #endif /* IP_REASSEMBLY */ 125 126 #if LWIP_ARP 127 /** 128 * Timer callback function that calls etharp_tmr() and reschedules itself. 129 * 130 * @param arg unused argument 131 */ 132 static void 133 arp_timer(void *arg) 134 { 135 LWIP_UNUSED_ARG(arg); 136 LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: etharp_tmr()\n")); 137 etharp_tmr(); 138 undiarp_tmr(); 139 sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); 140 } 141 #endif /* LWIP_ARP */ 142 143 #if LWIP_DHCP 144 /** 145 * Timer callback function that calls dhcp_coarse_tmr() and reschedules itself. 146 * 147 * @param arg unused argument 148 */ 149 static void 150 dhcp_timer_coarse(void *arg) 151 { 152 LWIP_UNUSED_ARG(arg); 153 LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_coarse_tmr()\n")); 154 dhcp_coarse_tmr(); 155 sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL); 156 } 157 158 /** 159 * Timer callback function that calls dhcp_fine_tmr() and reschedules itself. 160 * 161 * @param arg unused argument 162 */ 163 static void 164 dhcp_timer_fine(void *arg) 165 { 166 LWIP_UNUSED_ARG(arg); 167 LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_fine_tmr()\n")); 168 dhcp_fine_tmr(); 169 sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL); 170 } 171 #endif /* LWIP_DHCP */ 172 173 #if LWIP_AUTOIP 174 /** 175 * Timer callback function that calls autoip_tmr() and reschedules itself. 176 * 177 * @param arg unused argument 178 */ 179 static void 180 autoip_timer(void *arg) 181 { 182 LWIP_UNUSED_ARG(arg); 183 LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: autoip_tmr()\n")); 184 autoip_tmr(); 185 sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL); 186 } 187 #endif /* LWIP_AUTOIP */ 188 189 #if LWIP_IGMP 190 /** 191 * Timer callback function that calls igmp_tmr() and reschedules itself. 192 * 193 * @param arg unused argument 194 */ 195 static void 196 igmp_timer(void *arg) 197 { 198 LWIP_UNUSED_ARG(arg); 199 LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: igmp_tmr()\n")); 200 igmp_tmr(); 201 sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL); 202 } 203 #endif /* LWIP_IGMP */ 204 205 #if LWIP_DNS 206 /** 207 * Timer callback function that calls dns_tmr() and reschedules itself. 208 * 209 * @param arg unused argument 210 */ 211 static void 212 dns_timer(void *arg) 213 { 214 LWIP_UNUSED_ARG(arg); 215 LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dns_tmr()\n")); 216 dns_tmr(); 217 sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL); 218 } 219 #endif /* LWIP_DNS */ 220 221 /** Initialize this module */ 222 void sys_timeouts_init(void) 223 { 224 #if IP_REASSEMBLY 225 sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL); 226 #endif /* IP_REASSEMBLY */ 227 #if LWIP_ARP 228 sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); 229 #endif /* LWIP_ARP */ 230 #if LWIP_DHCP 231 sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL); 232 sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL); 233 #endif /* LWIP_DHCP */ 234 #if LWIP_AUTOIP 235 sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL); 236 #endif /* LWIP_AUTOIP */ 237 #if LWIP_IGMP 238 sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL); 239 #endif /* LWIP_IGMP */ 240 #if LWIP_DNS 241 sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL); 242 #endif /* LWIP_DNS */ 243 244 #if NO_SYS 245 /* Initialise timestamp for sys_check_timeouts */ 246 timeouts_last_time = sys_now(); 247 #endif 248 } 249 250 /** 251 * Create a one-shot timer (aka timeout). Timeouts are processed in the 252 * following cases: 253 * - while waiting for a message using sys_timeouts_mbox_fetch() 254 * - by calling sys_check_timeouts() (NO_SYS==1 only) 255 * 256 * @param msecs time in milliseconds after that the timer should expire 257 * @param handler callback function to call when msecs have elapsed 258 * @param arg argument to pass to the callback function 259 */ 260 #if LWIP_DEBUG_TIMERNAMES 261 void 262 sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name) 263 #else /* LWIP_DEBUG_TIMERNAMES */ 264 void 265 sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg) 266 #endif /* LWIP_DEBUG_TIMERNAMES */ 267 { 268 struct sys_timeo *timeout, *t; 269 270 timeout = (struct sys_timeo *)memp_malloc(MEMP_SYS_TIMEOUT); 271 if (timeout == NULL) { 272 LWIP_ASSERT("sys_timeout: timeout != NULL, pool MEMP_SYS_TIMEOUT is empty", timeout != NULL); 273 return; 274 } 275 timeout->next = NULL; 276 timeout->h = handler; 277 timeout->arg = arg; 278 timeout->time = msecs; 279 #if LWIP_DEBUG_TIMERNAMES 280 timeout->handler_name = handler_name; 281 LWIP_DEBUGF(TIMERS_DEBUG, ("sys_timeout: %p msecs=%"U32_F" handler=%s arg=%p\n", 282 (void *)timeout, msecs, handler_name, (void *)arg)); 283 #endif /* LWIP_DEBUG_TIMERNAMES */ 284 285 if (next_timeout == NULL) { 286 next_timeout = timeout; 287 return; 288 } 289 290 if (next_timeout->time > msecs) { 291 next_timeout->time -= msecs; 292 timeout->next = next_timeout; 293 next_timeout = timeout; 294 } else { 295 for(t = next_timeout; t != NULL; t = t->next) { 296 timeout->time -= t->time; 297 if (t->next == NULL || t->next->time > timeout->time) { 298 if (t->next != NULL) { 299 t->next->time -= timeout->time; 300 } 301 timeout->next = t->next; 302 t->next = timeout; 303 break; 304 } 305 } 306 } 307 } 308 309 /** 310 * Go through timeout list (for this task only) and remove the first matching 311 * entry, even though the timeout has not triggered yet. 312 * 313 * @note This function only works as expected if there is only one timeout 314 * calling 'handler' in the list of timeouts. 315 * 316 * @param handler callback function that would be called by the timeout 317 * @param arg callback argument that would be passed to handler 318 */ 319 void 320 sys_untimeout(sys_timeout_handler handler, void *arg) 321 { 322 struct sys_timeo *prev_t, *t; 323 324 if (next_timeout == NULL) { 325 return; 326 } 327 328 for (t = next_timeout, prev_t = NULL; t != NULL; prev_t = t, t = t->next) { 329 if ((t->h == handler) && (t->arg == arg)) { 330 /* We have a match */ 331 /* Unlink from previous in list */ 332 if (prev_t == NULL) { 333 next_timeout = t->next; 334 } else { 335 prev_t->next = t->next; 336 } 337 /* If not the last one, add time of this one back to next */ 338 if (t->next != NULL) { 339 t->next->time += t->time; 340 } 341 memp_free(MEMP_SYS_TIMEOUT, t); 342 return; 343 } 344 } 345 return; 346 } 347 348 #if NO_SYS 349 350 /** Handle timeouts for NO_SYS==1 (i.e. without using 351 * tcpip_thread/sys_timeouts_mbox_fetch(). Uses sys_now() to call timeout 352 * handler functions when timeouts expire. 353 * 354 * Must be called periodically from your main loop. 355 */ 356 void 357 sys_check_timeouts(void) 358 { 359 struct sys_timeo *tmptimeout; 360 u32_t diff; 361 sys_timeout_handler handler; 362 void *arg; 363 int had_one; 364 u32_t now; 365 366 now = sys_now(); 367 if (next_timeout) { 368 /* this cares for wraparounds */ 369 diff = LWIP_U32_DIFF(now, timeouts_last_time); 370 do 371 { 372 had_one = 0; 373 tmptimeout = next_timeout; 374 if (tmptimeout->time <= diff) { 375 /* timeout has expired */ 376 had_one = 1; 377 timeouts_last_time = now; 378 diff -= tmptimeout->time; 379 next_timeout = tmptimeout->next; 380 handler = tmptimeout->h; 381 arg = tmptimeout->arg; 382 #if LWIP_DEBUG_TIMERNAMES 383 if (handler != NULL) { 384 LWIP_DEBUGF(TIMERS_DEBUG, ("sct calling h=%s arg=%p\n", 385 tmptimeout->handler_name, arg)); 386 } 387 #endif /* LWIP_DEBUG_TIMERNAMES */ 388 memp_free(MEMP_SYS_TIMEOUT, tmptimeout); 389 if (handler != NULL) { 390 handler(arg); 391 } 392 } 393 /* repeat until all expired timers have been called */ 394 }while(had_one); 395 } 396 } 397 398 /** Set back the timestamp of the last call to sys_check_timeouts() 399 * This is necessary if sys_check_timeouts() hasn't been called for a long 400 * time (e.g. while saving energy) to prevent all timer functions of that 401 * period being called. 402 */ 403 void 404 sys_restart_timeouts(void) 405 { 406 timeouts_last_time = sys_now(); 407 } 408 409 #else /* NO_SYS */ 410 411 /** 412 * Wait (forever) for a message to arrive in an mbox. 413 * While waiting, timeouts are processed. 414 * 415 * @param mbox the mbox to fetch the message from 416 * @param msg the place to store the message 417 */ 418 void 419 sys_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg) 420 { 421 u32_t time_needed; 422 struct sys_timeo *tmptimeout; 423 sys_timeout_handler handler; 424 void *arg; 425 426 again: 427 if (!next_timeout) { 428 time_needed = sys_arch_mbox_fetch(mbox, msg, 0); 429 } else { 430 if (next_timeout->time > 0) { 431 time_needed = sys_arch_mbox_fetch(mbox, msg, next_timeout->time); 432 } else { 433 time_needed = SYS_ARCH_TIMEOUT; 434 } 435 436 if (time_needed == SYS_ARCH_TIMEOUT) { 437 /* If time == SYS_ARCH_TIMEOUT, a timeout occured before a message 438 could be fetched. We should now call the timeout handler and 439 deallocate the memory allocated for the timeout. */ 440 tmptimeout = next_timeout; 441 next_timeout = tmptimeout->next; 442 handler = tmptimeout->h; 443 arg = tmptimeout->arg; 444 #if LWIP_DEBUG_TIMERNAMES 445 if (handler != NULL) { 446 LWIP_DEBUGF(TIMERS_DEBUG, ("stmf calling h=%s arg=%p\n", 447 tmptimeout->handler_name, arg)); 448 } 449 #endif /* LWIP_DEBUG_TIMERNAMES */ 450 memp_free(MEMP_SYS_TIMEOUT, tmptimeout); 451 if (handler != NULL) { 452 /* For LWIP_TCPIP_CORE_LOCKING, lock the core before calling the 453 timeout handler function. */ 454 LOCK_TCPIP_CORE(); 455 handler(arg); 456 UNLOCK_TCPIP_CORE(); 457 } 458 LWIP_TCPIP_THREAD_ALIVE(); 459 460 /* We try again to fetch a message from the mbox. */ 461 goto again; 462 } else { 463 /* If time != SYS_ARCH_TIMEOUT, a message was received before the timeout 464 occured. The time variable is set to the number of 465 milliseconds we waited for the message. */ 466 if (time_needed < next_timeout->time) { 467 next_timeout->time -= time_needed; 468 } else { 469 next_timeout->time = 0; 470 } 471 } 472 } 473 } 474 475 #endif /* NO_SYS */ 476 477 #else /* LWIP_TIMERS */ 478 /* Satisfy the TCP code which calls this function */ 479 void 480 tcp_timer_needed(void) 481 { 482 } 483 #endif /* LWIP_TIMERS */ 484