System Initialization and System Shutdown¶
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 Concepts section that describes the Mynewt package hierarchy and its use of the
pkg.yml
andsyscfg.yml
files.Assumes you have read the Mynewt Theory of Operations and are familiar with how newt determines package dependencies for your target build.
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 Mynewtsysinit()
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:
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:
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:
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:
/**
* 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:
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:
pkg.down:
ble_hs_shutdown: 200
and ble_hs_shutdown
is defined as follows:
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.