| ===================== |
| System Time and Clock |
| ===================== |
| |
| Basic System Timer |
| ================== |
| |
| **System Timer** In most implementations, system time is provided |
| by a timer interrupt. That timer interrupt runs at rate determined |
| by ``CONFIG_USEC_PER_TICK`` (default 10000 microseconds or 100Hz. |
| If ``CONFIG_SCHED_TICKLESS`` is selected, the default is 100 |
| microseconds). The timer generates an interrupt each |
| ``CONFIG_USEC_PER_TICK`` microseconds and increments a counter |
| called ``g_system_ticks``. ``g_system_ticks`` then provides a |
| time-base for calculating *up-time* and elapsed time intervals in |
| units of ``CONFIG_USEC_PER_TICK``. The range of ``g_system_ticks`` |
| is, by default, 32-bits. However, if the MCU supports type |
| ``long long`` and ``CONFIG_SYSTEM_TIME16`` is selected, a 64-bit |
| system timer will be supported instead. |
| |
| **System Timer Accuracy** On many system, the exact timer interval |
| specified by ``CONFIG_USEC_PER_TICK`` cannot be achieved due to |
| limitations in frequencies or in dividers. As a result, the time |
| interval specified by ``CONFIG_USEC_PER_TICK`` may only be |
| approximate and there may be small errors in the apparent |
| *up-time* time. These small errors, however, will accumulate over |
| time and after a long period of time may have an unacceptably |
| large error in the apparent *up-time* of the MCU. |
| |
| If the timer tick period generated by the hardware is not exactly |
| ``CONFIG_USEC_PER_TICK`` *and* if there you require accurate |
| up-time for the MCU, then there are measures that you can take: |
| |
| - Perhaps you can adjust ``CONFIG_USEC_PER_TICK`` to a different |
| value so that an exactly ``CONFIG_USEC_PER_TICK`` can be |
| realized. |
| - Or you can use a technique known as *Delta-Sigma Modulation*. |
| (Suggested by Uros Platise). Consider the example below. |
| |
| **Delta-Sigma Modulation Example**. Consider this case: The system |
| timer is a count-up timer driven at 32.768KHz. There are dividers |
| that can be used, but a divider of one yields the highest |
| accuracy. This counter counts up until the count equals a match |
| value, then a timer interrupt is generated. The desire frequency |
| is 100Hz (``CONFIG_USEC_PER_TICK`` is 10000). |
| |
| This exact frequency of 100Hz cannot be obtained in this case. In |
| order to obtain that exact frequency a match value of 327.68 would |
| have to be provided. The closest integer value is 328 but the |
| ideal match value is between 327 and 328. The closest value, 328, |
| would yield an actual timer frequency of 99.9Hz! That will may |
| cause significant timing errors in certain usages. |
| |
| Use of Delta-Sigma Modulation can eliminate this error in the long |
| run. Consider this example implementation: |
| |
| #. Initially an accumulator is zero an the match value is |
| programmed to 328: |
| |
| .. code-block:: c |
| |
| accumulator = 0; |
| match = 328; |
| |
| #. On each timer interrupt, accumulator is updated with difference |
| that, in this reflects, 100\* the error in interval that just |
| passed. So on the first timer interrupt, the accumulator would |
| be updated like: |
| |
| .. code-block:: c |
| |
| if (match == 328) |
| { |
| accumulator += 32; // 100*(328 - 327.68) |
| } |
| else |
| { |
| accumulator -= 68; // (100*(327 - 327.68) |
| } |
| |
| #. And on that same timer interrupt a new match value would be |
| programmed: |
| |
| .. code-block:: c |
| |
| if (accumulator < 0) |
| { |
| match = 328; |
| } |
| else |
| { |
| match = 327; |
| } |
| |
| In this way, the timer interval is controlled from |
| interrupt-to-interrupt to produce an average frequency of exactly |
| 100Hz. |
| |
| Hardware |
| ======== |
| |
| To enable hardware module use the following configuration options: |
| |
| ``CONFIG_RTC`` |
| Enables general support for a hardware RTC. Specific |
| architectures may require other specific settings. |
| ``CONFIG_RTC_EXTERNAL`` |
| Most MCUs include RTC hardware built into the chip. Other RTCs, |
| *external* MCUs, may be provided as separate chips typically |
| interfacing with the MCU via a serial interface such as SPI or |
| I2C. These external RTCs differ from the built-in RTCs in that |
| they cannot be initialized until the operating system is fully |
| booted and can support the required serial communications. |
| ``CONFIG_RTC_EXTERNAL`` will configure the operating system so |
| that it defers initialization of its time facilities. |
| ``CONFIG_RTC_DATETIME`` |
| There are two general types of RTC: (1) A simple battery backed |
| counter that keeps the time when power is down, and (2) A full |
| date / time RTC the provides the date and time information, |
| often in BCD format. If ``CONFIG_RTC_DATETIME`` is selected, it |
| specifies this second kind of RTC. In this case, the RTC is |
| used to "seed"" the normal NuttX timer and the NuttX system |
| timer provides for higher resolution time. |
| ``CONFIG_RTC_HIRES`` |
| If ``CONFIG_RTC_DATETIME`` not selected, then the simple, |
| battery backed counter is used. There are two different |
| implementations of such simple counters based on the time |
| resolution of the counter: The typical RTC keeps time to |
| resolution of 1 second, usually supporting a 32-bit ``time_t`` |
| value. In this case, the RTC is used to "seed" the normal NuttX |
| timer and the NuttX timer provides for higher resolution time. |
| If ``CONFIG_RTC_HIRES`` is enabled in the NuttX configuration, |
| then the RTC provides higher resolution time and completely |
| replaces the system timer for purpose of date and time. |
| ``CONFIG_RTC_FREQUENCY`` |
| If ``CONFIG_RTC_HIRES`` is defined, then the frequency of the |
| high resolution RTC must be provided. If ``CONFIG_RTC_HIRES`` |
| is not defined, ``CONFIG_RTC_FREQUENCY`` is assumed to be one. |
| ``CONFIG_RTC_ALARM`` |
| Enable if the RTC hardware supports setting of an alarm. A |
| callback function will be executed when the alarm goes off |
| |
| which requires the following base functions to read and set time: |
| |
| - ``up_rtc_initialize()``. Initialize the built-in, MCU hardware |
| RTC per the selected configuration. This function is called |
| once very early in the OS initialization sequence. NOTE that |
| initialization of external RTC hardware that depends on the |
| availability of OS resources (such as SPI or I2C) must be |
| deferred until the system has fully booted. Other, RTC-specific |
| initialization functions are used in that case. |
| - ``up_rtc_time()``. Get the current time in seconds. This is |
| similar to the standard ``time()`` function. This interface is |
| only required if the low-resolution RTC/counter hardware |
| implementation selected. It is only used by the RTOS during |
| initialization to set up the system time when ``CONFIG_RTC`` is |
| set but neither ``CONFIG_RTC_HIRES`` nor |
| ``CONFIG_RTC_DATETIME`` are set. |
| - ``up_rtc_gettime()``. Get the current time from the high |
| resolution RTC clock/counter. This interface is only supported |
| by the high-resolution RTC/counter hardware implementation. It |
| is used to replace the system timer (``g_system_ticks``). |
| - ``up_rtc_settime()``. Set the RTC to the provided time. All RTC |
| implementations must be able to set their time based on a |
| standard timespec. |
| |
| System Tick and Time |
| ==================== |
| |
| The system tick is represented by ``g_system_ticks``. |
| |
| Running at rate of system base timer, used for time-slicing, and |
| so forth. |
| |
| If hardware RTC is present (``CONFIG_RTC``) and and |
| high-resolution timing is enabled (``CONFIG_RTC_HIRES``), then |
| after successful initialization variables are overridden by calls |
| to ``up_rtc_gettime()`` which is running continuously even in |
| power-down modes. |
| |
| In the case of ``CONFIG_RTC_HIRES`` is set the ``g_system_ticks`` |
| keeps counting at rate of a system timer, which however, is |
| disabled in power-down mode. By comparing this time and RTC |
| (actual time) one may determine the actual system active time. To |
| retrieve that variable use: |
| |
| Tickless OS |
| =========== |
| |
| **Default System Timer**. By default, a NuttX configuration uses a |
| periodic timer interrupt that drives all system timing. The timer |
| is provided by architecture-specific code that calls into NuttX at |
| a rate controlled by ``CONFIG_USEC_PER_TICK``. The default value |
| of ``CONFIG_USEC_PER_TICK`` is 10000 microseconds which |
| corresponds to a timer interrupt rate of 100 Hz. |
| |
| On each timer interrupt, NuttX does these things: |
| |
| - Increments a counter. This counter is the system time and has a |
| resolution of ``CONFIG_USEC_PER_TICK`` microseconds. |
| - Checks if it is time to perform time-slice operations on tasks |
| that have select round-robin scheduling. |
| - Checks for expiration of timed events. |
| |
| What is wrong with this default system timer? Nothing really. It |
| is reliable and uses only a small fraction of the CPU band width. |
| But we can do better. Some limitations of default system timer |
| are, in increasing order of importance: |
| |
| - **Overhead**: Although the CPU usage of the system timer |
| interrupt at 100Hz is really very low, it is still mostly |
| wasted processing time. On most timer interrupts, there is |
| really nothing that needs to be done other than incrementing the |
| counter. |
| - **Resolution**: Resolution of all system timing is also |
| determined by ``CONFIG_USEC_PER_TICK``. So nothing can be timed |
| with resolution finer than 10 milliseconds by default. To |
| increase this resolution, ``CONFIG_USEC_PER_TICK`` can be |
| reduced. However, then the system timer interrupts use more of |
| the CPU bandwidth processing useless interrupts. |
| - **Power Usage**: But the biggest issue is power usage. When the |
| system is IDLE, it enters a light, low-power mode (for ARMs, |
| this mode is entered with the ``wfi`` or ``wfe`` instructions |
| for example). But each interrupt awakens the system from this |
| low power mode. Therefore, higher rates of interrupts cause |
| greater power consumption. |
| |
| **Tickless OS**. The so-called *Tickless OS* provides one solution |
| to this issue. The basic concept here is that the periodic, timer |
| interrupt is eliminated and replaced with a one-shot, interval |
| timer. It becomes event driven instead of polled: The default |
| system timer is a polled design. On each interrupt, the NuttX |
| logic checks if it needs to do anything and, if so, it does it. |
| |
| Using an interval timer, one can anticipate when the next |
| interesting OS event will occur, program the interval time and |
| wait for it to fire. When the interval time fires, then the |
| scheduled activity is performed. |
| |
| Tickless Platform Support |
| ------------------------- |
| |
| In order to use the Tickless OS, one must provide special support |
| from the platform-specific code. Just as with the default system |
| timer, the platform-specific code must provide the timer resources |
| to support the OS behavior. Currently these timer resources are |
| only provided on a few platforms. An example implementation is for |
| the simulation is at ``nuttx/arch/sim/src/up_tickless.c``. There |
| is another example for the Atmel SAMA5 at |
| ``nuttx/arch/arm/src/sama5/sam_tickless.c``. These paragraphs will |
| explain how to provide the Tickless OS support to any platform. |
| |
| Tickless Configuration Options |
| ------------------------------ |
| |
| - ``CONFIG_ARCH_HAVE_TICKLESS``: If the platform provides |
| support for the *Tickless OS*, then this setting should be |
| selected in the ``Kconfig`` file for the architecture. Here is |
| what the selection looks in the ``arch/Kconfig`` file for the |
| simulated platform: |
| |
| .. code-block:: console |
| |
| config ARCH_SIM |
| bool "Simulation" |
| select ARCH_HAVE_TICKLESS |
| ---help--- |
| Linux/Cygwin user-mode simulation. |
| |
| When the simulation platform is selected, |
| ``ARCH_HAVE_TICKLESS`` is automatically selected, informing the |
| configuration system that *Tickless OS* options can be |
| selected. |
| |
| - ``CONFIG_SCHED_TICKLESS``: If ``CONFIG_ARCH_HAVE_TICKLESS`` is |
| selected, then you will be able to use this option to enable the |
| *Tickless OS* features in NuttX. |
| |
| - ``CONFIG_SCHED_TICKLESS_ALARM``: The tickless option can be |
| supported either via a simple interval timer (plus elapsed |
| time) or via an alarm. The interval timer allows programming |
| events to occur after an interval. With the alarm, you can set |
| a time in the future and get an event when that alarm goes off. |
| This option selects the use of an alarm. |
| |
| The advantage of an alarm is that it avoids some small timing |
| errors; the advantage of the use of the interval timer is that |
| the hardware requirement may be simpler. |
| |
| - ``CONFIG_USEC_PER_TICK``: This option is not unique to |
| *Tickless OS* operation, but changes its relevance when the |
| *Tickless OS* is selected. In the default configuration, where |
| system time is provided by a periodic timer interrupt, the |
| default system timer is configured for 100Hz, that is, |
| ``CONFIG_USEC_PER_TICK=10000``. If ``CONFIG_SCHED_TICKLESS`` is |
| selected, then there are no system timer interrupts. In this |
| case, ``CONFIG_USEC_PER_TICK`` does not control any timer |
| rates. Rather, it only determines the resolution of time |
| reported by ``clock_systime_ticks()`` and the resolution of |
| times that can be set for certain delays including watchdog |
| timers and delayed work. |
| |
| In this case there is still a trade-off: It is better to have |
| the ``CONFIG_USEC_PER_TICK`` as low as possible for higher |
| timing resolution. However, the time is currently held in |
| ``unsigned int``. On some systems, this may be 16-bits in width |
| but on most contemporary systems it will be 32-bits. In either |
| case, smaller values of ``CONFIG_USEC_PER_TICK`` will reduce |
| the range of values that delays that can be represented. So the |
| trade-off is between range and resolution (you could also |
| modify the code to use a 64-bit value if you really want both). |
| |
| The default, 100 microseconds, will provide for a range of |
| delays up to 120 hours. |
| |
| This value should never be less than the underlying resolution |
| of the timer. Errors may ensue. |
| |
| Tickless Imported Interfaces |
| ---------------------------- |
| |
| The interfaces that must be provided by the platform specified |
| code are defined in ``include/nuttx/arch.h``, listed below, and |
| summarized in the following paragraphs: |
| |
| - ``<arch>_timer_initialize()`` Initializes |
| the timer facilities. Called early in the initialization |
| sequence (by ``up_initialize()``). |
| - ``up_timer_gettime()``: Returns the |
| current time from the platform specific time source. |
| |
| The tickless option can be supported either via a simple interval |
| timer (plus elapsed time) or via an alarm. The interval timer |
| allows programming events to occur after an interval. With the |
| alarm, you can set a time in\* the future and get an event when |
| that alarm goes off. |
| |
| If ``CONFIG_SCHED_TICKLESS_ALARM`` is defined, then the platform |
| code must provide the following: |
| |
| - ``up_alarm_cancel()``: Cancels the alarm. |
| - ``up_alarm_start()``: Enables (or |
| re-enables) the alarm. |
| |
| If ``CONFIG_SCHED_TICKLESS_ALARM`` is *not*\ defined, then the |
| platform code must provide the following verify similar functions: |
| |
| - ``up_timer_cancel()``: Cancels the |
| interval timer. |
| - ``up_timer_start()``: Starts (or re-starts) |
| the interval timer. |
| |
| Note that a platform-specific implementation would probably require two |
| hardware timers: (1) A interval timer to satisfy the requirements of |
| ``up_timer_start()`` and ``up_timer_cancel()``, and (2) a counter to |
| handle the requirement of ``up_timer_gettime()``. Ideally, both timers |
| would run at the rate determined by ``CONFIG_USEC_PER_TICK`` (and |
| certainly never slower than that rate). |
| |
| Since timers are a limited resource, the use of two timers could be an |
| issue on some systems. The job could be done with a single timer if, for |
| example, the single timer were kept in a free-running mode at all times. |
| Some timer/counters have the capability to generate a compare interrupt |
| when the timer matches a comparison value but also to continue counting |
| without stopping. If your hardware supports such counters, one might use |
| the ``CONFIG_SCHED_TICKLESS_ALARM`` option and be able to simply set the |
| comparison count at the value of the free running timer *PLUS* the |
| desired delay. Then you could have both with a single timer: An alarm |
| and a free-running counter with the same timer! |
| |
| In addition to these imported interfaces, the RTOS will export the |
| following interfaces for use by the platform-specific interval |
| timer implementation: |
| |
| - ``nxsched_alarm_expiration()``: called by the platform-specific logic when the alarm expires. |
| - ``nxsched_timer_expiration()``: called by the platform-specific logic when the interval time expires. |
| |
| .. c:function:: void archname_timer_initialize(void) |
| |
| Initializes all platform-specific timer facilities. This function is |
| called early in the initialization sequence by up_initialize(). |
| On return, the current up-time should be available from up_timer_gettime() |
| and the interval timer is ready for use (but not actively timing). |
| The naming will depend on the architecture so for STM32 ``archname`` will |
| be ``stm32``. |
| |
| :return: Zero (OK) on success; a negated errno value on failure. |
| |
| **Assumptions**: Called early in the initialization sequence before |
| any special concurrency protections are required. |
| |
| .. c:function:: int up_timer_gettime(FAR struct timespec *ts) |
| |
| Return the elapsed time since power-up (or, more correctly, since |
| *<arch>*\ ``_timer_initialize()`` was called). This function is |
| functionally equivalent to ``clock_gettime()`` for the clock ID |
| ``CLOCK_MONOTONIC``. This function provides the basis for |
| reporting the current time and also is used to eliminate error |
| build-up from small errors in interval time calculations. |
| |
| :param ts: Provides the location in which to return the up-time.. |
| |
| :return: Zero (OK) on success; a negated errno value on failure. |
| |
| **Assumptions**: Called from the normal tasking context. The |
| implementation must provide whatever mutual exclusion is necessary |
| for correct operation. This can include disabling interrupts in |
| order to assure atomic register operations. |
| |
| .. c:function:: int up_alarm_cancel(FAR struct timespec *ts) |
| |
| Cancel the alarm and return the time of cancellation of the alarm. |
| These two steps need to be as nearly atomic as possible. |
| ``nxsched_timer_expiration()`` will not be called unless the alarm |
| is restarted with ``up_alarm_start()``. If, as a race condition, |
| the alarm has already expired when this function is called, then |
| time returned is the current time. |
| |
| :param ts: Location to return the expiration time. The current |
| time should be returned if the timer is not active. ``ts`` may |
| be ``NULL`` in which case the time is not returned |
| |
| :return: Zero (OK) on success; a negated errno value on failure. |
| |
| **Assumptions**: May be called from interrupt level handling or |
| from the normal tasking level. interrupts may need to be disabled |
| internally to assure non-reentrancy. |
| |
| .. c:function:: int up_alarm_start(FAR const struct timespec *ts) |
| |
| Start the alarm. ``nxsched_timer_expiration()`` will be called |
| when the alarm occurs (unless ``up_alarm_cancel`` is called to |
| stop it). |
| |
| :param ts: The time in the future at the alarm is expected to |
| occur. When the alarm occurs the timer logic will call |
| ``nxsched_timer_expiration()``. |
| |
| :return: Zero (OK) on success; a negated errno value on failure. |
| |
| **Assumptions**: May be called from interrupt level handling or |
| from the normal tasking level. Interrupts may need to be |
| disabled internally to assure non-reentrancy. |
| |
| .. c:function:: int up_timer_cancel(FAR struct timespec *ts) |
| |
| Cancel the interval timer and return the time remaining on the |
| timer. These two steps need to be as nearly atomic as possible. |
| ``nxsched_timer_expiration()`` will not be called unless the timer |
| is restarted with ``up_timer_start()``. If, as a race condition, |
| the timer has already expired when this function is called, then |
| that pending interrupt must be cleared so that |
| ``nxsched_timer_expiration()`` is not called spuriously and the |
| remaining time of zero should be returned. |
| |
| :param ts: Location to return the remaining time. Zero should be |
| returned if the timer is not active. |
| |
| :return: Zero (OK) on success; a negated errno value on failure. |
| |
| **Assumptions**: May be called from interrupt level handling or |
| from the normal tasking level. interrupts may need to be |
| disabled internally to assure non-reentrancy. |
| |
| .. c:function:: int up_timer_start(FAR const struct timespec *ts) |
| |
| Start the interval timer. ``nxsched_timer_expiration()`` will be |
| called at the completion of the timeout (unless |
| ``up_timer_cancel()`` is called to stop the timing). |
| |
| :param ts: Provides the time interval until |
| ``nxsched_timer_expiration()`` is called. |
| |
| :return: Zero (OK) on success; a negated errno value on failure. |
| |
| **Assumptions**: May be called from interrupt level handling |
| or from the normal tasking level. Interrupts may need to be |
| disabled internally to assure non-reentrancy. |
| |
| Watchdog Timer Interfaces |
| ========================= |
| |
| NuttX provides a general watchdog timer facility. This facility |
| allows the NuttX user to specify a watchdog timer function that |
| will run after a specified delay. The watchdog timer function will |
| run in the context of the timer interrupt handler. Because of |
| this, a limited number of NuttX interfaces are available to he |
| watchdog timer function. However, the watchdog timer function may |
| use ``mq_send()``, ``sigqueue()``, or ``kill()`` to communicate |
| with NuttX tasks. |
| |
| - :c:func:`wd_start` |
| - :c:func:`wd_cancel` |
| - :c:func:`wd_gettime` |
| - Watchdog Timer Callback |
| |
| .. c:function:: int wd_start(FAR struct wdog_s *wdog, int delay, \ |
| wdentry_t wdentry, wdparm_t arg) |
| |
| This function adds a watchdog to the timer queue. |
| The specified watchdog function will be called from the interrupt |
| level after the specified number of ticks has elapsed. Watchdog |
| timers may be started from the interrupt level. |
| |
| Watchdog times execute in the context of the timer interrupt |
| handler. |
| |
| Watchdog timers execute only once. |
| |
| To replace either the timeout delay or the function to be |
| executed, call wd_start again with the same wdog; only the most |
| recent wd_start() on a given watchdog ID has any effect. |
| |
| :param wdog: Watchdog ID |
| :param delay: Delay count in clock ticks |
| :param wdentry: Function to call on timeout |
| :param arg: The parameter to pass to wdentry. |
| |
| **NOTE**: The parameter must be of type ``wdparm_t``. |
| |
| :return: Zero (``OK``) is returned on success; a negated ``errno`` value |
| is return to indicate the nature of any failure. |
| |
| **Assumptions/Limitations:** The watchdog routine runs in the |
| context of the timer interrupt handler and is subject to all ISR |
| restrictions. |
| |
| **POSIX Compatibility:** This is a NON-POSIX interface. VxWorks |
| provides the following comparable interface: |
| |
| .. code-block:: c |
| |
| STATUS wdStart (WDOG_ID wdog, int delay, FUNCPTR wdentry, int parameter); |
| |
| Differences from the VxWorks interface include: |
| |
| - The present implementation supports multiple parameters passed |
| to wdentry; VxWorks supports only a single parameter. The |
| maximum number of parameters is determined by |
| |
| .. c:function:: int wd_cancel(FAR struct wdog_s *wdog) |
| |
| This function cancels a currently running |
| watchdog timer. Watchdog timers may be canceled from the interrupt |
| level. |
| |
| :param wdog: ID of the watchdog to cancel. |
| |
| :return: ``OK`` or ``ERROR`` |
| |
| **POSIX Compatibility:** This is a NON-POSIX interface. VxWorks |
| provides the following comparable interface: |
| |
| .. code-block:: c |
| |
| STATUS wdCancel (WDOG_ID wdog); |
| |
| .. c:function:: int wd_gettime(FAR struct wdog_s *wdog) |
| |
| Returns the time remaining before |
| the specified watchdog expires. |
| |
| :param wdog: Identifies the watchdog that the request is for. |
| |
| :return: The time in system ticks remaining until the |
| watchdog time expires. Zero means either that wdog is not valid or |
| that the wdog has already expired. |
| |
| .. c:type:: void (*wdentry_t)(wdparm_t arg) |
| |
| **Watchdog Timer Callback**: when a watchdog expires, |
| the callback function with this type is |
| called. |
| |
| The argument is passed as scalar ``wdparm_t`` values. For |
| systems where the ``sizeof(pointer) < sizeof(uint32_t)``, the |
| following union defines the alignment of the pointer within the |
| ``uint32_t``. For example, the SDCC MCS51 general pointer is |
| 24-bits, but ``uint32_t`` is 32-bits (of course). |
| |
| We always have ``sizeof(pointer) <= sizeof(uintptr_t)`` by |
| definition. |
| |
| .. code-block:: c |
| |
| union wdparm_u |
| { |
| FAR void *pvarg; /* The size one generic point */ |
| uint32_t dwarg; /* Big enough for a 32-bit value in any case */ |
| uintptr_t uiarg; /* sizeof(uintptr_t) >= sizeof(pointer) */ |
| }; |
| |
| #if UINTPTR_MAX >= UINT32_MAX |
| typedef uintptr_t wdparm_t; |
| #else |
| typedef uint32_t wdparm_t; |
| #endif |