diff --git a/include/init.h b/include/init.h index 14768782b48..f484afb6022 100644 --- a/include/init.h +++ b/include/init.h @@ -25,6 +25,21 @@ extern "C" { #define _SYS_INIT_LEVEL_POST_KERNEL 2 #define _SYS_INIT_LEVEL_APPLICATION 3 +extern s8_t z_sys_device_level; + +/** + * @brief Test whether startup is in the before-main-task phase. + * + * This impacts which services are available for use, and the context + * in which functions are run. + * + * @return true if and only if start up is still running pre-kernel + * initialization. + */ +static inline bool k_is_pre_kernel(void) +{ + return (z_sys_device_level < _SYS_INIT_LEVEL_POST_KERNEL); +} /* A counter is used to avoid issues when two or more system devices * are declared in the same C file with the same init function. diff --git a/kernel/device.c b/kernel/device.c index a7fa6aa1681..aff82511fec 100644 --- a/kernel/device.c +++ b/kernel/device.c @@ -23,6 +23,8 @@ extern u32_t __device_busy_end[]; #define DEVICE_BUSY_SIZE (__device_busy_end - __device_busy_start) #endif +s8_t z_sys_device_level; + /** * @brief Execute all the device initialization functions at a given level * @@ -46,6 +48,7 @@ void z_sys_device_do_config_level(s32_t level) __device_init_end, }; + z_sys_device_level = level; for (info = config_levels[level]; info < config_levels[level+1]; info++) { int retval; diff --git a/tests/kernel/device/src/main.c b/tests/kernel/device/src/main.c index 4c092c0f4e7..235b1622b72 100644 --- a/tests/kernel/device/src/main.c +++ b/tests/kernel/device/src/main.c @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -90,6 +91,81 @@ static void test_bogus_dynamic_name(void) zassert_true(mux == NULL, NULL); } +static struct init_record { + bool pre_kernel; + bool is_in_isr; + bool is_pre_kernel; +} init_records[4]; + +static struct init_record *rp = init_records; + +static int add_init_record(bool pre_kernel) +{ + rp->pre_kernel = pre_kernel; + rp->is_pre_kernel = k_is_pre_kernel(); + rp->is_in_isr = k_is_in_isr(); + ++rp; + return 0; +} + +static int pre1_fn(struct device *dev) +{ + return add_init_record(true); +} + +static int pre2_fn(struct device *dev) +{ + return add_init_record(true); +} + +static int post_fn(struct device *dev) +{ + return add_init_record(false); +} + +static int app_fn(struct device *dev) +{ + return add_init_record(false); +} + +SYS_INIT(pre1_fn, PRE_KERNEL_1, 0); +SYS_INIT(pre2_fn, PRE_KERNEL_2, 0); +SYS_INIT(post_fn, POST_KERNEL, 0); +SYS_INIT(app_fn, APPLICATION, 0); + +/** + * @brief Test detection of initialization before kernel services available. + * + * Confirms check is correct. + * + * @see k_is_pre_kernel() + */ +void test_pre_kernel_detection(void) +{ + struct init_record *rpe = rp; + + zassert_equal(rp - init_records, 4U, + "bad record count"); + rp = init_records; + while ((rp < rpe) && rp->pre_kernel) { + zassert_equal(rp->is_in_isr, false, + "rec %zu isr", rp - init_records); + zassert_equal(rp->is_pre_kernel, true, + "rec %zu pre-kernel", rp - init_records); + ++rp; + } + zassert_equal(rp - init_records, 2U, + "bad pre-kernel count"); + + while (rp < rpe) { + zassert_equal(rp->is_in_isr, false, + "rec %zu isr", rp - init_records); + zassert_equal(rp->is_pre_kernel, false, + "rec %zu post-kernel", rp - init_records); + ++rp; + } +} + #ifdef CONFIG_DEVICE_POWER_MANAGEMENT /** * @brief Test system device list query API with PM enabled. @@ -176,6 +252,7 @@ void test_main(void) ztest_unit_test(test_dummy_device_pm), ztest_unit_test(build_suspend_device_list), ztest_unit_test(test_dummy_device), + ztest_unit_test(test_pre_kernel_detection), ztest_user_unit_test(test_bogus_dynamic_name), ztest_user_unit_test(test_dynamic_name)); ztest_run_test_suite(device);