diff --git a/kernel/init.c b/kernel/init.c index dd9f3d7258f5..b004eea2d588 100644 --- a/kernel/init.c +++ b/kernel/init.c @@ -313,13 +313,24 @@ static int do_device_init(const struct device *dev) if (dev->ops.init != NULL) { rc = dev->ops.init(dev); - /* Mark device initialized. If initialization - * failed, record the error condition. + /* If initialization failed, record in dev->state->init_res + * the POSITIVE value of the resulting errno */ if (rc != 0) { + /* device's init function should return: + * 0 on success + * a negative value on failure (-errno) + * errno value maps to an uint8_t range as of now. + */ + __ASSERT(rc >= -UINT8_MAX && rc < 0, "device %s init: invalid error (%d)", + dev->name, rc); + if (rc < 0) { rc = -rc; } + /* handle error value overflow in production + * this is likely a bug in the device's init function. Signals it + */ if (rc > UINT8_MAX) { rc = UINT8_MAX; } @@ -327,6 +338,7 @@ static int do_device_init(const struct device *dev) } } + /* device initialization has been invoked */ dev->state->initialized = true; if (rc == 0) { @@ -334,7 +346,10 @@ static int do_device_init(const struct device *dev) (void)pm_device_runtime_auto_enable(dev); } - return rc; + /* here, the value of rc is either 0 or +errno + * flip the sign to return a negative value on failure as expected + */ + return -rc; } /** diff --git a/tests/kernel/device/app.overlay b/tests/kernel/device/app.overlay index ac77f271cc55..e15aa106e377 100644 --- a/tests/kernel/device/app.overlay +++ b/tests/kernel/device/app.overlay @@ -66,6 +66,13 @@ zephyr,deferred-init; }; + fakedeferdriver@F9000000 { + compatible = "fakedeferdriver"; + reg = <0xF9000000 0x2000>; + status = "okay"; + zephyr,deferred-init; + }; + fakedomain_0: fakedomain_0 { compatible = "fakedomain"; status = "okay"; diff --git a/tests/kernel/device/src/main.c b/tests/kernel/device/src/main.c index 2479a366ec9e..7afd19c229d5 100644 --- a/tests/kernel/device/src/main.c +++ b/tests/kernel/device/src/main.c @@ -24,6 +24,7 @@ #define FAKEDEFERDRIVER0 DEVICE_DT_GET(DT_PATH(fakedeferdriver_e7000000)) #define FAKEDEFERDRIVER1 DEVICE_DT_GET(DT_PATH(fakedeferdriver_e8000000)) +#define FAKEDEFERDRIVER2 DEVICE_DT_GET(DT_PATH(fakedeferdriver_f9000000)) /* A device without init call */ DEVICE_DEFINE(dummy_noinit, DUMMY_NOINIT, NULL, NULL, NULL, NULL, @@ -39,6 +40,12 @@ DEVICE_DT_DEFINE(DT_INST(1, fakedeferdriver), NULL, NULL, NULL, NULL, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &fakedeferdriverapi); +/* fake devices used to test deferred initialization failure */ +static int fakedeferdriver_init(const struct device *dev); + +DEVICE_DT_DEFINE(DT_INST(2, fakedeferdriver), fakedeferdriver_init, NULL, NULL, NULL, POST_KERNEL, + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, NULL); + /** * @brief Test cases to verify device objects * @@ -415,6 +422,38 @@ ZTEST(device, test_deferred_init) zassert_true(device_is_ready(FAKEDEFERDRIVER0)); } +static int fakedeferdriver_init(const struct device *dev) +{ + return -EIO; +} + +/** + * @brief Test deferred initialization error + * + * @details Verify device_init error cases and expected device states + * + * - case -errno: if the device initialization fails + * - case -EALREADY: if the device is already initialized. + * + * @see device_init + * @ingroup kernel_device_tests + */ +ZTEST(device, test_deferred_init_failure) +{ + int ret; + const struct device *dev = FAKEDEFERDRIVER2; + + zassert_false(device_is_ready(dev)); + ret = device_init(dev); + zassert_equal(ret, -EIO); + zassert_false(device_is_ready(dev)); + zassert_equal(dev->state->init_res, EIO); + + ret = device_init(dev); + zassert_equal(ret, -EALREADY); + zassert_equal(dev->state->init_res, EIO); +} + ZTEST(device, test_device_api) { const struct device *dev;