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.
