Bonjour/Zerconf library

Dependencies:   mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers timers.c Source File

timers.c

Go to the documentation of this file.
00001 /**
00002  * @file
00003  * Stack-internal timers implementation.
00004  * This file includes timer callbacks for stack-internal timers as well as
00005  * functions to set up or stop timers and check for expired timers.
00006  *
00007  */
00008 
00009 /*
00010  * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
00011  * All rights reserved.
00012  *
00013  * Redistribution and use in source and binary forms, with or without modification,
00014  * are permitted provided that the following conditions are met:
00015  *
00016  * 1. Redistributions of source code must retain the above copyright notice,
00017  *    this list of conditions and the following disclaimer.
00018  * 2. Redistributions in binary form must reproduce the above copyright notice,
00019  *    this list of conditions and the following disclaimer in the documentation
00020  *    and/or other materials provided with the distribution.
00021  * 3. The name of the author may not be used to endorse or promote products
00022  *    derived from this software without specific prior written permission.
00023  *
00024  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
00025  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00026  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
00027  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00028  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
00029  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00030  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00031  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
00032  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
00033  * OF SUCH DAMAGE.
00034  *
00035  * This file is part of the lwIP TCP/IP stack.
00036  *
00037  * Author: Adam Dunkels <adam@sics.se>
00038  *         Simon Goldschmidt
00039  *
00040  */
00041 
00042 #include "lwip/opt.h"
00043 
00044 #include "lwip/timers.h"
00045 #include "lwip/def.h"
00046 #include "lwip/memp.h"
00047 #include "lwip/tcpip.h"
00048 
00049 #include "lwip/tcp_impl.h"
00050 #include "lwip/ip_frag.h"
00051 #include "netif/etharp.h"
00052 #include "lwip/dhcp.h "
00053 #include "lwip/autoip.h"
00054 #include "lwip/igmp.h"
00055 #include "lwip/dns.h"
00056 
00057 
00058 /** The one and only timeout list */
00059 static struct sys_timeo *next_timeout;
00060 #if NO_SYS
00061 static u32_t timeouts_last_time;
00062 #endif /* NO_SYS */
00063 
00064 #if LWIP_TCP
00065 /** global variable that shows if the tcp timer is currently scheduled or not */
00066 static int tcpip_tcp_timer_active;
00067 
00068 /**
00069  * Timer callback function that calls tcp_tmr() and reschedules itself.
00070  *
00071  * @param arg unused argument
00072  */
00073 static void
00074 tcpip_tcp_timer(void *arg)
00075 {
00076   LWIP_UNUSED_ARG(arg);
00077 
00078   /* call TCP timer handler */
00079   tcp_tmr();
00080   /* timer still needed? */
00081   if (tcp_active_pcbs || tcp_tw_pcbs) {
00082     /* restart timer */
00083     sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL);
00084   } else {
00085     /* disable timer */
00086     tcpip_tcp_timer_active = 0;
00087   }
00088 }
00089 
00090 /**
00091  * Called from TCP_REG when registering a new PCB:
00092  * the reason is to have the TCP timer only running when
00093  * there are active (or time-wait) PCBs.
00094  */
00095 void
00096 tcp_timer_needed(void)
00097 {
00098   /* timer is off but needed again? */
00099   if (!tcpip_tcp_timer_active && (tcp_active_pcbs || tcp_tw_pcbs)) {
00100     /* enable and start timer */
00101     tcpip_tcp_timer_active = 1;
00102     sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL);
00103   }
00104 }
00105 #endif /* LWIP_TCP */
00106 
00107 #if IP_REASSEMBLY
00108 /**
00109  * Timer callback function that calls ip_reass_tmr() and reschedules itself.
00110  *
00111  * @param arg unused argument
00112  */
00113 static void
00114 ip_reass_timer(void *arg)
00115 {
00116   LWIP_UNUSED_ARG(arg);
00117   LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: ip_reass_tmr()\n"));
00118   ip_reass_tmr();
00119   sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL);
00120 }
00121 #endif /* IP_REASSEMBLY */
00122 
00123 #if LWIP_ARP
00124 /**
00125  * Timer callback function that calls etharp_tmr() and reschedules itself.
00126  *
00127  * @param arg unused argument
00128  */
00129 static void
00130 arp_timer(void *arg)
00131 {
00132   LWIP_UNUSED_ARG(arg);
00133   LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: etharp_tmr()\n"));
00134   etharp_tmr();
00135   sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL);
00136 }
00137 #endif /* LWIP_ARP */
00138 
00139 #if LWIP_DHCP
00140 /**
00141  * Timer callback function that calls dhcp_coarse_tmr() and reschedules itself.
00142  *
00143  * @param arg unused argument
00144  */
00145 static void
00146 dhcp_timer_coarse(void *arg)
00147 {
00148   LWIP_UNUSED_ARG(arg);
00149   LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_coarse_tmr()\n"));
00150   dhcp_coarse_tmr();
00151   sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL);
00152 }
00153 
00154 /**
00155  * Timer callback function that calls dhcp_fine_tmr() and reschedules itself.
00156  *
00157  * @param arg unused argument
00158  */
00159 static void
00160 dhcp_timer_fine(void *arg)
00161 {
00162   LWIP_UNUSED_ARG(arg);
00163   LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_fine_tmr()\n"));
00164   dhcp_fine_tmr();
00165   sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL);
00166 }
00167 #endif /* LWIP_DHCP */
00168 
00169 #if LWIP_AUTOIP
00170 /**
00171  * Timer callback function that calls autoip_tmr() and reschedules itself.
00172  *
00173  * @param arg unused argument
00174  */
00175 static void
00176 autoip_timer(void *arg)
00177 {
00178   LWIP_UNUSED_ARG(arg);
00179   LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: autoip_tmr()\n"));
00180   autoip_tmr();
00181   sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL);
00182 }
00183 #endif /* LWIP_AUTOIP */
00184 
00185 #if LWIP_IGMP
00186 /**
00187  * Timer callback function that calls igmp_tmr() and reschedules itself.
00188  *
00189  * @param arg unused argument
00190  */
00191 static void
00192 igmp_timer(void *arg)
00193 {
00194   LWIP_UNUSED_ARG(arg);
00195   LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: igmp_tmr()\n"));
00196   igmp_tmr();
00197   sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL);
00198 }
00199 #endif /* LWIP_IGMP */
00200 
00201 #if LWIP_DNS
00202 /**
00203  * Timer callback function that calls dns_tmr() and reschedules itself.
00204  *
00205  * @param arg unused argument
00206  */
00207 static void
00208 dns_timer(void *arg)
00209 {
00210   LWIP_UNUSED_ARG(arg);
00211   LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dns_tmr()\n"));
00212   dns_tmr();
00213   sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL);
00214 }
00215 #endif /* LWIP_DNS */
00216 
00217 /** Initialize this module */
00218 void sys_timeouts_init(void)
00219 {
00220 #if IP_REASSEMBLY
00221   sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL);
00222 #endif /* IP_REASSEMBLY */
00223 #if LWIP_ARP
00224   sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL);
00225 #endif /* LWIP_ARP */
00226 #if LWIP_DHCP
00227   sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL);
00228   sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL);
00229 #endif /* LWIP_DHCP */
00230 #if LWIP_AUTOIP
00231   sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL);
00232 #endif /* LWIP_AUTOIP */
00233 #if LWIP_IGMP
00234   sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL);
00235 #endif /* LWIP_IGMP */
00236 #if LWIP_DNS
00237   sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL);
00238 #endif /* LWIP_DNS */
00239 
00240 #if NO_SYS
00241   /* Initialise timestamp for sys_check_timeouts */
00242   timeouts_last_time = sys_now();
00243 #endif
00244 }
00245 
00246 /**
00247  * Create a one-shot timer (aka timeout). Timeouts are processed in the
00248  * following cases:
00249  * - while waiting for a message using sys_timeouts_mbox_fetch()
00250  * - by calling sys_check_timeouts() (NO_SYS==1 only)
00251  *
00252  * @param msecs time in milliseconds after that the timer should expire
00253  * @param h callback function to call when msecs have elapsed
00254  * @param arg argument to pass to the callback function
00255  */
00256 #if LWIP_DEBUG_TIMERNAMES
00257 void
00258 sys_timeout_debug(u32_t msecs, sys_timeout_handler h, void *arg, const char* handler_name)
00259 #else /* LWIP_DEBUG_TIMERNAMES */
00260 void
00261 sys_timeout(u32_t msecs, sys_timeout_handler h, void *arg)
00262 #endif /* LWIP_DEBUG_TIMERNAMES */
00263 {
00264   struct sys_timeo *timeout, *t;
00265 
00266   timeout = (struct sys_timeo *)memp_malloc(MEMP_SYS_TIMEOUT);
00267   if (timeout == NULL) {
00268     LWIP_ASSERT("sys_timeout: timeout != NULL, pool MEMP_SYS_TIMEOUT is empty", timeout != NULL);
00269     return;
00270   }
00271   timeout->next = NULL;
00272   timeout->h = h;
00273   timeout->arg = arg;
00274   timeout->time = msecs;
00275 #if LWIP_DEBUG_TIMERNAMES
00276   timeout->handler_name = handler_name;
00277 #endif /* LWIP_DEBUG_TIMERNAMES */
00278 
00279 #if LWIP_DEBUG_TIMERNAMES
00280   LWIP_DEBUGF(TIMERS_DEBUG, ("sys_timeout: %p msecs=%"U32_F" h=%p arg=%p name=%s\n",
00281     (void *)timeout, msecs, *(void**)&h, (void *)arg, handler_name));
00282 #else /* LWIP_DEBUG_TIMERNAMES */
00283   LWIP_DEBUGF(TIMERS_DEBUG, ("sys_timeout: %p msecs=%"U32_F" h=%p arg=%p\n",
00284     (void *)timeout, msecs, *(void**)&h, (void *)arg));
00285 #endif /* LWIP_DEBUG_TIMERNAMES */
00286 
00287   if (next_timeout == NULL) {
00288     next_timeout = timeout;
00289     return;
00290   }
00291 
00292   if (next_timeout->time > msecs) {
00293     next_timeout->time -= msecs;
00294     timeout->next = next_timeout;
00295     next_timeout = timeout;
00296   } else {
00297     for(t = next_timeout; t != NULL; t = t->next) {
00298       timeout->time -= t->time;
00299       if (t->next == NULL || t->next->time > timeout->time) {
00300         if (t->next != NULL) {
00301           t->next->time -= timeout->time;
00302         }
00303         timeout->next = t->next;
00304         t->next = timeout;
00305         break;
00306       }
00307     }
00308   }
00309 }
00310 
00311 /**
00312  * Go through timeout list (for this task only) and remove the first matching
00313  * entry, even though the timeout has not triggered yet.
00314  *
00315  * @note This function only works as expected if there is only one timeout
00316  * calling 'h' in the list of timeouts.
00317  *
00318  * @param h callback function that would be called by the timeout
00319  * @param arg callback argument that would be passed to h
00320 */
00321 void
00322 sys_untimeout(sys_timeout_handler h, void *arg)
00323 {
00324   struct sys_timeo *prev_t, *t;
00325 
00326   if (next_timeout == NULL) {
00327     return;
00328   }
00329 
00330   for (t = next_timeout, prev_t = NULL; t != NULL; prev_t = t, t = t->next) {
00331     if ((t->h == h) && (t->arg == arg)) {
00332       /* We have a match */
00333       /* Unlink from previous in list */
00334       if (prev_t == NULL) {
00335         next_timeout = t->next;
00336       } else {
00337         prev_t->next = t->next;
00338       }
00339       /* If not the last one, add time of this one back to next */
00340       if (t->next != NULL) {
00341         t->next->time += t->time;
00342       }
00343       memp_free(MEMP_SYS_TIMEOUT, t);
00344       return;
00345     }
00346   }
00347   return;
00348 }
00349 
00350 #if NO_SYS
00351 
00352 /** Handle timeouts for NO_SYS==1 (i.e. without using
00353  * tcpip_thread/sys_timeouts_mbox_fetch(). Uses sys_now() to call timeout
00354  * handler functions when timeouts expire.
00355  *
00356  * Must be called periodically from your main loop.
00357  */
00358 void
00359 sys_check_timeouts(void)
00360 {
00361   struct sys_timeo *tmptimeout;
00362   u32_t diff;
00363   sys_timeout_handler h;
00364   void *arg;
00365   int had_one;
00366   u32_t now;
00367 #if LWIP_DEBUG_TIMERNAMES
00368   const char *handler_name;
00369 #endif /* LWIP_DEBUG_TIMERNAMES */
00370 
00371   now = sys_now();
00372   if (next_timeout) {
00373     /* @todo: wrap around? */
00374     diff = now - timeouts_last_time;
00375     do
00376     {
00377       had_one = 0;
00378       tmptimeout = next_timeout;
00379       if (tmptimeout->time <= diff) {
00380         /* timeout has expired */
00381         had_one = 1;
00382         timeouts_last_time = now;
00383         diff -= tmptimeout->time;
00384         next_timeout = tmptimeout->next;
00385         h   = tmptimeout->h;
00386         arg = tmptimeout->arg;
00387 #if LWIP_DEBUG_TIMERNAMES
00388         handler_name = tmptimeout->handler_name;
00389 #endif /* LWIP_DEBUG_TIMERNAMES */
00390         memp_free(MEMP_SYS_TIMEOUT, tmptimeout);
00391         if (h != NULL) {
00392 #if LWIP_DEBUG_TIMERNAMES
00393           LWIP_DEBUGF(TIMERS_DEBUG, ("sct calling h=%p(%p) (%s)\n", *(void**)&h, arg, handler_name));
00394 #else /* LWIP_DEBUG_TIMERNAMES */
00395           LWIP_DEBUGF(TIMERS_DEBUG, ("sct calling h=%p(%p)\n", *(void**)&h, arg));
00396 #endif /* LWIP_DEBUG_TIMERNAMES */
00397           h(arg);
00398         }
00399       }
00400     /* repeat until all expired timers have been called */
00401     }while(had_one);
00402   }
00403 }
00404 
00405 /** Set back the timestamp of the last call to sys_check_timeouts()
00406  * This is necessary if sys_check_timeouts() hasn't been called for a long
00407  * time (e.g. while saving energy) to prevent all timer functions of that
00408  * period being called.
00409  */
00410 void
00411 sys_restart_timeouts(void)
00412 {
00413   timeouts_last_time = sys_now();
00414 }
00415 
00416 #else /* NO_SYS */
00417 
00418 /**
00419  * Wait (forever) for a message to arrive in an mbox.
00420  * While waiting, timeouts are processed.
00421  *
00422  * @param mbox the mbox to fetch the message from
00423  * @param msg the place to store the message
00424  */
00425 void
00426 sys_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg)
00427 {
00428   u32_t time_needed;
00429   struct sys_timeo *tmptimeout;
00430   sys_timeout_handler h;
00431   void *arg;
00432 #if LWIP_DEBUG_TIMERNAMES
00433   const char *handler_name;
00434 #endif /* LWIP_DEBUG_TIMERNAMES */
00435 
00436  again:
00437   if (!next_timeout) {
00438     time_needed = sys_arch_mbox_fetch(mbox, msg, 0);
00439   } else {
00440     if (next_timeout->time > 0) {
00441       time_needed = sys_arch_mbox_fetch(mbox, msg, next_timeout->time);
00442     } else {
00443       time_needed = SYS_ARCH_TIMEOUT;
00444     }
00445 
00446     if (time_needed == SYS_ARCH_TIMEOUT) {
00447       /* If time == SYS_ARCH_TIMEOUT, a timeout occured before a message
00448          could be fetched. We should now call the timeout handler and
00449          deallocate the memory allocated for the timeout. */
00450       tmptimeout = next_timeout;
00451       next_timeout = tmptimeout->next;
00452       h   = tmptimeout->h;
00453       arg = tmptimeout->arg;
00454 #if LWIP_DEBUG_TIMERNAMES
00455       handler_name = tmptimeout->handler_name;
00456 #endif /* LWIP_DEBUG_TIMERNAMES */
00457       memp_free(MEMP_SYS_TIMEOUT, tmptimeout);
00458       if (h != NULL) {
00459 #if LWIP_DEBUG_TIMERNAMES
00460         LWIP_DEBUGF(TIMERS_DEBUG, ("stmf calling h=%p(%p) (%s)\n", *(void**)&h, arg, handler_name));
00461 #else /* LWIP_DEBUG_TIMERNAMES */
00462         LWIP_DEBUGF(TIMERS_DEBUG, ("stmf calling h=%p(%p)\n", *(void**)&h, arg));
00463 #endif /* LWIP_DEBUG_TIMERNAMES */
00464         /* For LWIP_TCPIP_CORE_LOCKING, lock the core before calling the
00465            timeout handler function. */
00466         LOCK_TCPIP_CORE();
00467         h(arg);
00468         UNLOCK_TCPIP_CORE();
00469       }
00470 
00471       /* We try again to fetch a message from the mbox. */
00472       goto again;
00473     } else {
00474       /* If time != SYS_ARCH_TIMEOUT, a message was received before the timeout
00475          occured. The time variable is set to the number of
00476          milliseconds we waited for the message. */
00477       if (time_needed < next_timeout->time) {
00478         next_timeout->time -= time_needed;
00479       } else {
00480         next_timeout->time = 0;
00481       }
00482     }
00483   }
00484 }
00485 
00486 #endif /* NO_SYS */