| System Initialization and System Shutdown |
| ----------------------------------------- |
| |
| .. toctree:: |
| :hidden: |
| |
| Mynewt allows a package to designate startup and shutdown functions. |
| These functions are called automatically by the OS using facilities |
| called *sysinit* and *sysdown*. |
| |
| This guide: |
| |
| - Assumes you have read the |
| :ref:`concepts` section that describes |
| the Mynewt package hierarchy and its use of the ``pkg.yml`` and |
| ``syscfg.yml`` files. |
| - Assumes you have read the Mynewt :doc:`../../../newt/newt_operation` and are familiar with how newt |
| determines package dependencies for your target build. |
| |
| .. contents:: |
| :local: |
| :depth: 3 |
| |
| System Initialization |
| ~~~~~~~~~~~~~~~~~~~~~ |
| |
| During system startup, Mynewt creates a default event queue and a main |
| task to process events from this queue. You can override the |
| ``OS_MAIN_TASK_PRIO`` and ``OS_MAIN_TASK_STACK_SIZE`` setting values |
| defined by the ``kernel/os`` package to specify different task priority |
| and stack size values. |
| |
| Your application's ``main()`` function executes in the context of the |
| main task and must perform the following: |
| |
| - At the start of ``main()``, call the Mynewt ``sysinit()`` function to |
| initialize the packages before performing any other processing. |
| - At the end of ``main()``, wait for and dispatch events from the |
| default event queue in an infinite loop. |
| |
| **Note:** You must include the ``sysinit/sysinit.h`` header file to |
| access the ``sysinit()`` function. |
| |
| Here is an example of a ``main()`` function: |
| |
| .. code-block:: cpp |
| |
| int |
| main(int argc, char **argv) |
| { |
| /* First, call sysinit() to perform the system and package initialization */ |
| sysinit(); |
| |
| /* ... other application initialization processing ... */ |
| |
| /* Last, process events from the default event queue. */ |
| while (1) { |
| os_eventq_run(os_eventq_dflt_get()); |
| } |
| /* main never returns */ |
| } |
| |
| Specifying Package Initialization Functions |
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| |
| The ``sysinit()`` function calls the ``sysinit_app()`` function to |
| perform system initialization for the packages in the target. You can, |
| optionally, specify one or more package initialization functions that |
| ``sysinit_app()`` calls to initialize a package. |
| |
| A package initialization function must have the following prototype: |
| |
| .. code-block:: cpp |
| |
| void init_func_name(void) |
| |
| Sysinit functions are not expected to fail. If a sysinit function |
| encounters an unrecoverable failure, it should invoke one of the |
| ``SYSINIT_PANIC`` macros. By default, these macros trigger a crash, |
| but the behavior can be overridden by configuring the panic function |
| with ``sysinit_panic_set()``. |
| |
| Package initialization functions are called in stages to ensure that |
| lower priority packages are initialized before higher priority packages. |
| A stage is an integer value, 0 or higher, that specifies when an |
| initialization function is called. Mynewt calls the package |
| initialization functions in increasing stage number order. When |
| multiple init functions have the same stage number, they are called in |
| lexicographic order (by function name). |
| |
| You use the ``pkg.init`` parameter in the ``pkg.yml`` file to specify an |
| initialization function and the stage number to call the function. You |
| can specify multiple initialization functions with a different stage |
| number for each function for the parameter values. This feature allows |
| packages with interdependencies to perform initialization in multiple |
| stages. |
| |
| The ``pkg.init`` parameter has the following syntax in the ``pkg.yml`` |
| file: |
| |
| .. code-block:: yaml |
| |
| pkg.init: |
| pkg_init_func1_name: pkg_init_func1_stage |
| pkg_init_func2_name: pkg_init_func2_stage |
| |
| ... |
| |
| pkg_init_funcN_name: pkg_init_funcN_stage |
| |
| where ``pkg_init_func#_name`` is the C function name of an |
| initialization function, and ``pkg_init_func#_stage`` is an integer |
| value, 0 or higher, that indicates the stage when the |
| ``pkg_init_func#_name`` function is called. |
| |
| Generated sysinit_app() Function |
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| |
| The newt tool processes the ``pkg.init`` parameters in all the |
| ``pkg.yml`` files for a target, generates the ``sysinit_app()`` function |
| in the ``<target-path>/generated/src/<target-name>-sysinit_app.c`` file, |
| and includes the file in the build. Here is an example ``sysinit_app()`` |
| function: |
| |
| .. code-block:: cpp |
| |
| /** |
| * This file was generated by Apache Newt (incubating) version: 1.0.0-dev |
| */ |
| |
| #if !SPLIT_LOADER |
| |
| void split_app_init(void); |
| void os_pkg_init(void); |
| void imgmgr_module_init(void); |
| |
| /* ... */ |
| |
| void stats_module_init(void); |
| |
| void |
| sysinit_app(void) |
| { |
| |
| /*** Stage 0 */ |
| /* 0.0: kernel/os */ |
| os_pkg_init(); |
| |
| /*** Stage 2 */ |
| /* 2.0: sys/flash_map */ |
| flash_map_init(); |
| |
| /*** Stage 10 */ |
| /* 10.0: sys/stats/full */ |
| stats_module_init(); |
| |
| /*** Stage 20 */ |
| /* 20.0: sys/console/full */ |
| console_pkg_init(); |
| |
| /*** Stage 100 */ |
| /* 100.0: sys/log/full */ |
| log_init(); |
| /* 100.1: sys/mfg */ |
| mfg_init(); |
| |
| /* ... */ |
| |
| /*** Stage 300 */ |
| /* 300.0: sys/config */ |
| config_pkg_init(); |
| |
| /*** Stage 500 */ |
| /* 500.0: sys/id */ |
| id_init(); |
| /* 500.1: sys/shell */ |
| shell_init(); |
| |
| /* ... */ |
| |
| /* 500.4: mgmt/imgmgr */ |
| imgmgr_module_init(); |
| |
| /*** Stage 501 */ |
| /* 501.0: mgmt/newtmgr/transport/nmgr_shell */ |
| nmgr_shell_pkg_init(); |
| } |
| #endif |
| |
| System Shutdown |
| ~~~~~~~~~~~~~~~ |
| |
| A Mynewt package can specify a sequence of system shutdown ("sysdown") |
| function calls. As with sysinit, a stage number is associated with |
| each function. On shutdown, sysdown functions are executed in |
| ascending order of stage number. In the case of a tie, functions are |
| executed in lexicographic order (by function name). |
| |
| The sysdown procedure is only performed for a controlled |
| shutdown. It is executed when the system processes a newtmgr |
| "reset" command, for example. It is not be executed when the system |
| crashes, browns out, or restarts due to the hardware watchdog. |
| |
| Sysdown functions are specified in a ``pkg.yml`` file using the |
| ``pkg.down`` key. They use the following function type: |
| |
| .. code-block:: cpp |
| |
| int func(int reason) |
| |
| The ``reason`` parameter is currently unused and should be ignored. |
| |
| Each shutdown callback returns one of the following codes: |
| |
| - ``SYSDOWN_COMPLETE`` |
| - ``SYSDOWN_IN_PROGRESS`` |
| |
| If a sysdown function is able to complete its work synchronously, it |
| should return ``SYSDOWN_COMPLETE``. |
| |
| The "in progress" case is a bit more complicated. Sysdown functions |
| should not block on the current task (e.g., they should not tell the |
| default task to do something and then wait for the result). Blocking |
| like this will result in a deadlock since the current task is waiting |
| for the sysdown function to complete. |
| |
| Instead, if a sysdown function needs to do extra work in the current |
| task, it should do so asynchronously. That is, it should enqueue an |
| event to the task's event queue, then return ``SYSDOWN_IN_PROGRESS``. |
| When the sysdown procedure eventually completes, the task should call |
| ``sysdown_release``. |
| |
| When all sysdown procedures have completed, Mynewt proceeds to reset |
| the device. If it takes longer than ``MYNEWT_VAL(SYSDOWN_TIMEOUT_MS)`` |
| milliseconds (default: 10s) for all shutdown procedures to complete, a |
| crash is triggered and the device resets. |
| |
| As an example, the NimBLE BLE host package configures a sysdown |
| function that terminates all open connections. Its ``pkg.yml`` |
| contains the following map: |
| |
| .. code-block:: yaml |
| |
| pkg.down: |
| ble_hs_shutdown: 200 |
| |
| and ``ble_hs_shutdown`` is defined as follows: |
| |
| .. code-block:: cpp |
| |
| int |
| ble_hs_shutdown(int reason) |
| { |
| int rc; |
| |
| /* Ensure this function only gets called by sysdown. */ |
| SYSDOWN_ASSERT_ACTIVE(); |
| |
| /* Initiate a host stop procedure. */ |
| rc = ble_hs_stop(&ble_hs_shutdown_stop_listener, ble_hs_shutdown_stop_cb, |
| NULL); |
| switch (rc) { |
| case 0: |
| /* Stop initiated. Wait for result to be reported asynchronously. */ |
| return SYSDOWN_IN_PROGRESS; |
| |
| case BLE_HS_EBUSY: |
| /* Already stopping. Wait for result to be reported asynchronously. */ |
| return SYSDOWN_IN_PROGRESS; |
| |
| case BLE_HS_EALREADY: |
| /* Already stopped. Shutdown complete. */ |
| return SYSDOWN_COMPLETE; |
| |
| default: |
| BLE_HS_LOG(ERROR, "ble_hs_shutdown: failed to stop host; rc=%d\n", rc); |
| return SYSDOWN_COMPLETE; |
| } |
| } |
| |
| In the cases where this function returns ``SYSDOWN_IN_PROGRESS``, the |
| host eventually calls ``sysdown_release`` when the procedure completes. |