diff --git a/CODEOWNERS b/CODEOWNERS index 4521f2cd0b8..c538c0355eb 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -125,7 +125,7 @@ /doc/guides/dts/ @galak @mbolivar-nordic /doc/reference/bluetooth/ @joerchan @jhedberg @Vudentz /doc/reference/devicetree/ @galak @mbolivar-nordic -/doc/reference/kernel/other/resource_mgmt.rst @pabigot +/doc/reference/resource_management/ @pabigot /doc/reference/networking/can* @alexanderwachter /drivers/debug/ @nashif /drivers/*/*cc13xx_cc26xx* @bwitherspoon diff --git a/doc/reference/index.rst b/doc/reference/index.rst index 85cf257d1db..ef18bc7a612 100644 --- a/doc/reference/index.rst +++ b/doc/reference/index.rst @@ -9,6 +9,7 @@ API Reference overview.rst terminology.rst audio/index.rst + misc/notify.rst bluetooth/index.rst kconfig/index.rst crypto/index.rst @@ -23,6 +24,7 @@ API Reference peripherals/index.rst power_management/index.rst random/index.rst + resource_management/index.rst shell/index.rst storage/index.rst usb/index.rst diff --git a/doc/reference/kernel/index.rst b/doc/reference/kernel/index.rst index 9025b7e0b29..d77ec137c57 100644 --- a/doc/reference/kernel/index.rst +++ b/doc/reference/kernel/index.rst @@ -116,7 +116,6 @@ These pages cover other kernel services. other/atomic.rst other/float.rst other/ring_buffers.rst - other/resource_mgmt.rst other/cxx_support.rst other/version.rst other/fatal.rst diff --git a/doc/reference/misc/notify.rst b/doc/reference/misc/notify.rst new file mode 100644 index 00000000000..278fe302bcd --- /dev/null +++ b/doc/reference/misc/notify.rst @@ -0,0 +1,25 @@ +.. _async_notification: + +Asynchronous Notification APIs +############################## + +Zephyr APIs often include :ref:`api_term_async` functions where an +operation is initiated and the application needs to be informed when it +completes, and whether it succeeded. Using :cpp:func:`k_poll()` is +often a good method, but some application architectures may be more +suited to a callback notification, and operations like enabling clocks +and power rails may need to be invoked before kernel functions are +available so a busy-wait for completion may be needed. + +This API is intended to be embedded within specific subsystems such as +:ref:`resource_mgmt_onoff` and other APIs that support async +transactions. The subsystem wrappers are responsible for extracting +operation-specific data from requests that include a notification +element, and for invoking callbacks with the parameters required by the +API. + +API Reference +************* + +.. doxygengroup:: sys_notify_apis + :project: Zephyr diff --git a/doc/reference/kernel/other/resource_mgmt.rst b/doc/reference/resource_management/index.rst similarity index 99% rename from doc/reference/kernel/other/resource_mgmt.rst rename to doc/reference/resource_management/index.rst index 3cf284e0be5..48b24969e10 100644 --- a/doc/reference/kernel/other/resource_mgmt.rst +++ b/doc/reference/resource_management/index.rst @@ -15,6 +15,8 @@ suggests that a shared implementation is desirable. :depth: 2 +.. _resource_mgmt_onoff: + On-Off Services *************** diff --git a/include/sys/notify.h b/include/sys/notify.h new file mode 100644 index 00000000000..ce3791520f0 --- /dev/null +++ b/include/sys/notify.h @@ -0,0 +1,361 @@ +/* + * Copyright (c) 2019 Peter Bigot Consulting, LLC + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_SYS_NOTIFY_H_ +#define ZEPHYR_INCLUDE_SYS_NOTIFY_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct sys_notify; + +/* + * Flag value that overwrites the method field when the operation has + * completed. + */ +#define SYS_NOTIFY_METHOD_COMPLETED 0 + +/* + * Indicates that no notification will be provided. + * + * Callers must check for completions using + * sys_notify_fetch_result(). + * + * See sys_notify_init_spinwait(). + */ +#define SYS_NOTIFY_METHOD_SPINWAIT 1 + +/* + * Select notification through @ref k_poll signal + * + * See sys_notify_init_signal(). + */ +#define SYS_NOTIFY_METHOD_SIGNAL 2 + +/* + * Select notification through a user-provided callback. + * + * See sys_notify_init_callback(). + */ +#define SYS_NOTIFY_METHOD_CALLBACK 3 + +#define SYS_NOTIFY_METHOD_MASK 0x03 +#define SYS_NOTIFY_METHOD_POS 0 + +/** + * @brief Identify the region of sys_notify flags available for + * containing services. + * + * Bits of the flags field of the sys_notify structure at and above + * this position may be used by extensions to the sys_notify + * structure. + * + * These bits are intended for use by containing service + * implementations to record client-specific information. The bits + * are cleared by sys_notify_validate(). Use of these does not + * imply that the flags field becomes public API. + */ +#define SYS_NOTIFY_EXTENSION_POS 2 + +/* + * Mask isolating the bits of sys_notify::flags that are available + * for extension. + */ +#define SYS_NOTIFY_EXTENSION_MASK (~BIT_MASK(SYS_NOTIFY_EXTENSION_POS)) + +/** + * @defgroup sys_notify_apis Asynchronous Notification APIs + * @ingroup kernel_apis + * @{ + */ + +/** + * @brief Generic signature used to notify of result completion by + * callback. + * + * Functions with this role may be invoked from any context including + * pre-kernel, ISR, or cooperative or pre-emptible threads. + * Compatible functions must be isr-ok and not sleep. + * + * Parameters that should generally be passed to such functions include: + * + * * a pointer to a specific client request structure, i.e. the one + * that contains the sys_notify structure. + * * the result of the operation, either as passed to + * sys_notify_finalize() or extracted afterwards using + * sys_notify_fetch_result(). Expected values are + * service-specific, but the value shall be non-negative if the + * operation succeeded, and negative if the operation failed. + */ +typedef void (*sys_notify_generic_callback)(); + +/** + * @brief State associated with notification for an asynchronous + * operation. + * + * Objects of this type are allocated by a client, which must use an + * initialization function (e.g. sys_notify_init_signal()) to + * configure them. Generally the structure is a member of a + * service-specific client structure, such as onoff_client. + * + * Control of the containing object transfers to the service provider + * when a pointer to the object is passed to a service function that + * is documented to take control of the object, such as + * onoff_service_request(). While the service provider controls the + * object the client must not change any object fields. Control + * reverts to the client: + * * if the call to the service API returns an error; + * * when operation completion is posted. This may occur before the + * call to the service API returns. + * + * Operation completion is technically posted when the flags field is + * updated so that sys_notify_fetch_result() returns success. This + * will happen before the signal is posted or callback is invoked. + * Note that although the manager will no longer reference the + * sys_notify object past this point, the containing object may have + * state that will be referenced within the callback. Where callbacks + * are used control of the containing object does not revert to the + * client until the callback has been invoked. (Re-use within the + * callback is explicitly permitted.) + * + * After control has reverted to the client the notify object must be + * reinitialized for the next operation. + * + * The content of this structure is not public API to clients: all + * configuration and inspection should be done with functions like + * sys_notify_init_callback() and sys_notify_fetch_result(). + * However, services that use this structure may access certain + * fields directly. + */ +struct sys_notify { + union method { + /* Pointer to signal used to notify client. + * + * The signal value corresponds to the res parameter + * of sys_notify_callback. + */ + struct k_poll_signal *signal; + + /* Generic callback function for callback notification. */ + sys_notify_generic_callback callback; + } method; + + /* + * Flags recording information about the operation. + * + * Bits below SYS_NOTIFY_EXTENSION_POS are initialized by + * async notify API init functions like + * sys_notify_init_callback(), and must not by modified by + * extensions or client code. + * + * Bits at and above SYS_NOTIFY_EXTENSION_POS are available + * for use by service extensions while the containing object + * is managed by the service. They are not for client use, + * are zeroed by the async notify API init functions, and will + * be zeroed by sys_notify_finalize(). + */ + u32_t volatile flags; + + /* + * The result of the operation. + * + * This is the value that was (or would be) passed to the + * async infrastructure. This field is the sole record of + * success or failure for spin-wait synchronous operations. + */ + int volatile result; +}; + +/** @internal */ +static inline u32_t sys_notify_get_method(const struct sys_notify *notify) +{ + u32_t method = notify->flags >> SYS_NOTIFY_METHOD_POS; + + return method & SYS_NOTIFY_METHOD_MASK; +} + +/** + * @brief Validate and initialize the notify structure. + * + * This should be invoked at the start of any service-specific + * configuration validation. It ensures that the basic asynchronous + * notification configuration is consistent, and clears the result. + * + * Note that this function does not validate extension bits (zeroed by + * async notify API init functions like sys_notify_init_callback()). + * It may fail to recognize that an uninitialized structure has been + * passed because only method bits of flags are tested against method + * settings. To reduce the chance of accepting an uninititalized + * operation service validation of structures that contain an + * sys_notify instance should confirm that the extension bits are + * set or cleared as expected. + * + * @retval 0 on successful validation and reinitialization + * @retval -EINVAL if the configuration is not valid. + */ +int sys_notify_validate(struct sys_notify *notify); + +/** + * @brief Record and signal the operation completion. + * + * @param notify pointer to the notification state structure. + * + * @param res the result of the operation. Expected values are + * service-specific, but the value shall be non-negative if the + * operation succeeded, and negative if the operation failed. + * + * @return If the notification is to be done by callback this returns + * the generic version of the function to be invoked. The caller must + * immediately invoke that function with whatever arguments are + * expected by the callback. If notification is by spin-wait or + * signal, the notification has been completed by the point this + * function returns, and a null pointer is returned. + */ +sys_notify_generic_callback sys_notify_finalize(struct sys_notify *notify, + int res); + +/** + * @brief Check for and read the result of an asynchronous operation. + * + * @param notify pointer to the object used to specify asynchronous + * function behavior and store completion information. + * + * @param result pointer to storage for the result of the operation. + * The result is stored only if the operation has completed. + * + * @retval 0 if the operation has completed. + * @retval -EAGAIN if the operation has not completed. + */ +static inline int sys_notify_fetch_result(const struct sys_notify *notify, + int *result) +{ + __ASSERT_NO_MSG(notify != NULL); + __ASSERT_NO_MSG(result != NULL); + int rv = -EAGAIN; + + if (sys_notify_get_method(notify) == SYS_NOTIFY_METHOD_COMPLETED) { + rv = 0; + *result = notify->result; + } + + return rv; +} + +/** + * @brief Initialize a notify object for spin-wait notification. + * + * Clients that use this initialization receive no asynchronous + * notification, and instead must periodically check for completion + * using sys_notify_fetch_result(). + * + * On completion of the operation the client object must be + * reinitialized before it can be re-used. + * + * @param notify pointer to the notification configuration object. + */ +static inline void sys_notify_init_spinwait(struct sys_notify *notify) +{ + __ASSERT_NO_MSG(notify != NULL); + + *notify = (struct sys_notify){ + .flags = SYS_NOTIFY_METHOD_SPINWAIT, + }; +} + +/** + * @brief Initialize a notify object for (k_poll) signal notification. + * + * Clients that use this initialization will be notified of the + * completion of operations through the provided signal. + * + * On completion of the operation the client object must be + * reinitialized before it can be re-used. + * + * @note + * @rst + * This capability is available only when :option:`CONFIG_POLL` is + * selected. + * @endrst + * + * @param notify pointer to the notification configuration object. + * + * @param sigp pointer to the signal to use for notification. The + * value must not be null. The signal must be reset before the client + * object is passed to the on-off service API. + */ +static inline void sys_notify_init_signal(struct sys_notify *notify, + struct k_poll_signal *sigp) +{ + __ASSERT_NO_MSG(notify != NULL); + __ASSERT_NO_MSG(sigp != NULL); + + *notify = (struct sys_notify){ + .method = { + .signal = sigp, + }, + .flags = SYS_NOTIFY_METHOD_SIGNAL, + }; +} + +/** + * @brief Initialize a notify object for callback notification. + * + * Clients that use this initialization will be notified of the + * completion of operations through the provided callback. Note that + * callbacks may be invoked from various contexts depending on the + * specific service; see @ref sys_notify_generic_callback. + * + * On completion of the operation the client object must be + * reinitialized before it can be re-used. + * + * @param notify pointer to the notification configuration object. + * + * @param handler a function pointer to use for notification. + */ +static inline void sys_notify_init_callback(struct sys_notify *notify, + sys_notify_generic_callback handler) +{ + __ASSERT_NO_MSG(notify != NULL); + __ASSERT_NO_MSG(handler != NULL); + + *notify = (struct sys_notify){ + .method = { + .callback = handler, + }, + .flags = SYS_NOTIFY_METHOD_CALLBACK, + }; +} + +/** + * @brief Detect whether a particular notification uses a callback. + * + * The generic handler does not capture the signature expected by the + * callback, and the translation to a service-specific callback must + * be provided by the service. This check allows abstracted services + * to reject callback notification requests when the service doesn't + * provide a translation function. + * + * @return true if and only if a callback is to be used for notification. + */ +static inline bool sys_notify_uses_callback(const struct sys_notify *notify) +{ + __ASSERT_NO_MSG(notify != NULL); + + return sys_notify_get_method(notify) == SYS_NOTIFY_METHOD_CALLBACK; +} + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_SYS_NOTIFY_H_ */ diff --git a/include/sys/onoff.h b/include/sys/onoff.h index 9f49e791835..8c2653018bf 100644 --- a/include/sys/onoff.h +++ b/include/sys/onoff.h @@ -9,6 +9,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -192,44 +193,6 @@ struct onoff_service { int onoff_service_init(struct onoff_service *srv, const struct onoff_service_transitions *transitions); -/** @internal - * - * Flag fields used to specify on-off client behavior. - * - * These flags control whether calls to onoff_service_request() and - * onoff_service_release() are synchronous or asynchronous, and for - * asynchronous operations how the operation result is communicated to - * the client. - */ -enum onoff_client_flags { - /* Known-invalid field, used in validation */ - ONOFF_CLIENT_NOTIFY_INVALID = 0, - - /* - * Indicates that no notification will be provided. - * - * Callers must check for completions using - * onoff_client_fetch_result(). - * - * See onoff_client_init_spinwait(). - */ - ONOFF_CLIENT_NOTIFY_SPINWAIT = 1, - - /* - * Select notification through @ref k_poll signal - * - * See onoff_client_init_signal(). - */ - ONOFF_CLIENT_NOTIFY_SIGNAL = 2, - - /** - * Select notification through a user-provided callback. - * - * See onoff_client_init_callback(). - */ - ONOFF_CLIENT_NOTIFY_CALLBACK = 3, -}; - /* Forward declaration */ struct onoff_client; @@ -286,32 +249,11 @@ struct onoff_client { /* Links the client into the set of waiting service users. */ sys_snode_t node; - union async { - /* Pointer to signal used to notify client. - * - * The signal value corresponds to the res parameter - * of onoff_client_callback. - */ - struct k_poll_signal *signal; + /* Notification configuration. */ + struct sys_notify notify; - /* Handler and argument for callback notification. */ - struct callback { - onoff_client_callback handler; - void *user_data; - } callback; - } async; - - /* - * The result of the operation. - * - * This is the value that was (or would be) passed to the - * async infrastructure. This field is the sole record of - * success or failure for no-wait synchronous operations. - */ - int volatile result; - - /* Flags recording client state. */ - u32_t volatile flags; + /* User data for callback-based notification. */ + void *user_data; }; /** @@ -330,15 +272,8 @@ static inline int onoff_client_fetch_result(const struct onoff_client *op, int *result) { __ASSERT_NO_MSG(op != NULL); - __ASSERT_NO_MSG(result != NULL); - int rv = -EAGAIN; - - if (op->flags == 0U) { - rv = 0; - *result = op->result; - } - return rv; + return sys_notify_fetch_result(&op->notify, result); } /** @@ -358,9 +293,8 @@ static inline void onoff_client_init_spinwait(struct onoff_client *cli) { __ASSERT_NO_MSG(cli != NULL); - *cli = (struct onoff_client){ - .flags = ONOFF_CLIENT_NOTIFY_SPINWAIT, - }; + *cli = (struct onoff_client){}; + sys_notify_init_spinwait(&cli->notify); } /** @@ -390,16 +324,9 @@ static inline void onoff_client_init_signal(struct onoff_client *cli, struct k_poll_signal *sigp) { __ASSERT_NO_MSG(cli != NULL); - __ASSERT_NO_MSG(sigp != NULL); - *cli = (struct onoff_client){ -#ifdef CONFIG_POLL - .async = { - .signal = sigp, - }, -#endif /* CONFIG_POLL */ - .flags = ONOFF_CLIENT_NOTIFY_SIGNAL, - }; + *cli = (struct onoff_client){}; + sys_notify_init_signal(&cli->notify, sigp); } /** @@ -430,14 +357,9 @@ static inline void onoff_client_init_callback(struct onoff_client *cli, __ASSERT_NO_MSG(handler != NULL); *cli = (struct onoff_client){ - .async = { - .callback = { - .handler = handler, - .user_data = user_data, - }, - }, - .flags = ONOFF_CLIENT_NOTIFY_CALLBACK, + .user_data = user_data, }; + sys_notify_init_callback(&cli->notify, handler); } /** diff --git a/lib/os/CMakeLists.txt b/lib/os/CMakeLists.txt index 7918f78dc6d..c0e9de8aa27 100644 --- a/lib/os/CMakeLists.txt +++ b/lib/os/CMakeLists.txt @@ -11,6 +11,7 @@ zephyr_sources( fdtable.c hex.c mempool.c + notify.c printk.c onoff.c rb.c diff --git a/lib/os/notify.c b/lib/os/notify.c new file mode 100644 index 00000000000..ba5d2a3125f --- /dev/null +++ b/lib/os/notify.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2019 Peter Bigot Consulting, LLC + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +int sys_notify_validate(struct sys_notify *notify) +{ + int rv = 0; + + if (notify == NULL) { + return -EINVAL; + } + + /* Validate configuration based on mode */ + switch (sys_notify_get_method(notify)) { + case SYS_NOTIFY_METHOD_SPINWAIT: + break; + case SYS_NOTIFY_METHOD_CALLBACK: + if (notify->method.callback == NULL) { + rv = -EINVAL; + } + break; +#ifdef CONFIG_POLL + case SYS_NOTIFY_METHOD_SIGNAL: + if (notify->method.signal == NULL) { + rv = -EINVAL; + } + break; +#endif /* CONFIG_POLL */ + default: + rv = -EINVAL; + break; + } + + /* Clear the result here instead of in all callers. */ + if (rv == 0) { + notify->result = 0; + } + + return rv; +} + +sys_notify_generic_callback sys_notify_finalize(struct sys_notify *notify, + int res) +{ + struct k_poll_signal *sig = NULL; + sys_notify_generic_callback rv = 0; + u32_t method = sys_notify_get_method(notify); + + /* Store the result and capture secondary notification + * information. + */ + notify->result = res; + switch (method) { + case SYS_NOTIFY_METHOD_SPINWAIT: + break; + case SYS_NOTIFY_METHOD_CALLBACK: + rv = notify->method.callback; + break; + case SYS_NOTIFY_METHOD_SIGNAL: + sig = notify->method.signal; + break; + default: + __ASSERT_NO_MSG(false); + } + + /* Mark completion by clearing the flags field to the + * completed state, releasing any spin-waiters, then complete + * secondary notification. + */ + compiler_barrier(); + notify->flags = SYS_NOTIFY_METHOD_COMPLETED; + + if (IS_ENABLED(CONFIG_POLL) && (sig != NULL)) { + k_poll_signal_raise(sig, res); + } + + return rv; +} diff --git a/lib/os/onoff.c b/lib/os/onoff.c index 140527fb7cd..79332900b5d 100644 --- a/lib/os/onoff.c +++ b/lib/os/onoff.c @@ -6,10 +6,6 @@ #include #include -#include - -#define CLIENT_NOTIFY_METHOD_MASK 0x03 -#define CLIENT_VALID_FLAGS_MASK 0x07 #define SERVICE_CONFIG_FLAGS \ @@ -41,36 +37,11 @@ static int validate_args(const struct onoff_service *srv, return -EINVAL; } - int rv = 0; - u32_t mode = cli->flags; + int rv = sys_notify_validate(&cli->notify); - /* Reject unexpected flags. */ - if (mode != (cli->flags & CLIENT_VALID_FLAGS_MASK)) { - return -EINVAL; - } - - /* Validate configuration based on mode */ - switch (mode & CLIENT_NOTIFY_METHOD_MASK) { - case ONOFF_CLIENT_NOTIFY_SPINWAIT: - break; - case ONOFF_CLIENT_NOTIFY_CALLBACK: - if (cli->async.callback.handler == NULL) { - rv = -EINVAL; - } - break; - case ONOFF_CLIENT_NOTIFY_SIGNAL: - if (cli->async.signal == NULL) { - rv = -EINVAL; - } - break; - default: + if ((rv == 0) + && ((cli->notify.flags & SYS_NOTIFY_EXTENSION_MASK) != 0)) { rv = -EINVAL; - break; - } - - /* Clear the result here instead of in all callers. */ - if (rv == 0) { - cli->result = 0; } return rv; @@ -96,25 +67,11 @@ static void notify_one(struct onoff_service *srv, struct onoff_client *cli, int res) { - unsigned int flags = cli->flags; + onoff_client_callback cb = + (onoff_client_callback)sys_notify_finalize(&cli->notify, res); - /* Store the result, and notify if requested. */ - cli->result = res; - cli->flags = 0; - switch (flags & CLIENT_NOTIFY_METHOD_MASK) { - case ONOFF_CLIENT_NOTIFY_SPINWAIT: - break; - case ONOFF_CLIENT_NOTIFY_CALLBACK: - cli->async.callback.handler(srv, cli, - cli->async.callback.user_data, res); - break; -#ifdef CONFIG_POLL - case ONOFF_CLIENT_NOTIFY_SIGNAL: - k_poll_signal_raise(cli->async.signal, res); - break; -#endif /* CONFIG_POLL */ - default: - __ASSERT_NO_MSG(false); + if (cb) { + cb(srv, cli, cli->user_data, res); } } diff --git a/tests/lib/notify/CMakeLists.txt b/tests/lib/notify/CMakeLists.txt new file mode 100644 index 00000000000..abd9e6e13ba --- /dev/null +++ b/tests/lib/notify/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(sys_notify) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/tests/lib/notify/prj.conf b/tests/lib/notify/prj.conf new file mode 100644 index 00000000000..1948aaa649a --- /dev/null +++ b/tests/lib/notify/prj.conf @@ -0,0 +1,2 @@ +CONFIG_POLL=y +CONFIG_ZTEST=y diff --git a/tests/lib/notify/src/main.c b/tests/lib/notify/src/main.c new file mode 100644 index 00000000000..4f858cac392 --- /dev/null +++ b/tests/lib/notify/src/main.c @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2019 Peter Bigot Consulting, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +static u32_t get_extflags(const struct sys_notify *anp) +{ + u32_t flags = anp->flags & SYS_NOTIFY_EXTENSION_MASK; + + return flags >> SYS_NOTIFY_EXTENSION_POS; +} + +static void set_extflags(struct sys_notify *anp, + u32_t flags) +{ + anp->flags = (anp->flags & ~SYS_NOTIFY_EXTENSION_MASK) + | (flags << SYS_NOTIFY_EXTENSION_POS); +} + +static void callback(struct sys_notify *anp, + int *resp) +{ + zassert_equal(sys_notify_fetch_result(anp, resp), 0, + "failed callback fetch"); +} + +static void test_validate(void) +{ + struct sys_notify notify = { + .flags = 0, + }; + + zassert_equal(sys_notify_validate(NULL), -EINVAL, + "accepted null pointer"); + zassert_equal(sys_notify_validate(¬ify), -EINVAL, + "accepted bad method"); +} + + +static void test_spinwait(void) +{ + int rc; + int set_res = 423; + int res; + sys_notify_generic_callback cb; + struct sys_notify notify; + u32_t xflags = 0x1234; + + memset(¬ify, 0xac, sizeof(notify)); + rc = sys_notify_validate(¬ify); + zassert_equal(rc, -EINVAL, + "invalid not diagnosed"); + + sys_notify_init_spinwait(¬ify); + rc = sys_notify_validate(¬ify); + zassert_equal(rc, 0, + "init_spinwait invalid"); + + zassert_false(sys_notify_uses_callback(¬ify), + "uses callback"); + + zassert_equal(notify.flags, SYS_NOTIFY_METHOD_SPINWAIT, + "flags mismatch"); + + set_extflags(¬ify, xflags); + zassert_equal(sys_notify_get_method(¬ify), + SYS_NOTIFY_METHOD_SPINWAIT, + "method corrupted"); + zassert_equal(get_extflags(¬ify), xflags, + "xflags extract failed"); + + rc = sys_notify_fetch_result(¬ify, &res); + zassert_equal(rc, -EAGAIN, + "spinwait ready too soon"); + + zassert_not_equal(notify.flags, 0, + "flags cleared"); + + cb = sys_notify_finalize(¬ify, set_res); + zassert_equal(cb, (sys_notify_generic_callback)NULL, + "callback not null"); + zassert_equal(notify.flags, 0, + "flags not cleared"); + + rc = sys_notify_fetch_result(¬ify, &res); + zassert_equal(rc, 0, + "spinwait not ready"); + zassert_equal(res, set_res, + "result not set"); +} + +static void test_signal(void) +{ +#ifdef CONFIG_POLL + int rc; + int set_res = 423; + int res; + struct k_poll_signal sig; + sys_notify_generic_callback cb; + struct sys_notify notify; + u32_t xflags = 0x1234; + + memset(¬ify, 0xac, sizeof(notify)); + rc = sys_notify_validate(¬ify); + zassert_equal(rc, -EINVAL, + "invalid not diagnosed"); + + k_poll_signal_init(&sig); + k_poll_signal_check(&sig, &rc, &res); + zassert_equal(rc, 0, + "signal set"); + + sys_notify_init_signal(¬ify, &sig); + notify.method.signal = NULL; + rc = sys_notify_validate(¬ify); + zassert_equal(rc, -EINVAL, + "null signal not invalid"); + + memset(¬ify, 0xac, sizeof(notify)); + sys_notify_init_signal(¬ify, &sig); + rc = sys_notify_validate(¬ify); + zassert_equal(rc, 0, + "init_spinwait invalid"); + + zassert_false(sys_notify_uses_callback(¬ify), + "uses callback"); + + zassert_equal(notify.flags, SYS_NOTIFY_METHOD_SIGNAL, + "flags mismatch"); + zassert_equal(notify.method.signal, &sig, + "signal pointer mismatch"); + + set_extflags(¬ify, xflags); + zassert_equal(sys_notify_get_method(¬ify), + SYS_NOTIFY_METHOD_SIGNAL, + "method corrupted"); + zassert_equal(get_extflags(¬ify), xflags, + "xflags extract failed"); + + rc = sys_notify_fetch_result(¬ify, &res); + zassert_equal(rc, -EAGAIN, + "spinwait ready too soon"); + + zassert_not_equal(notify.flags, 0, + "flags cleared"); + + cb = sys_notify_finalize(¬ify, set_res); + zassert_equal(cb, (sys_notify_generic_callback)NULL, + "callback not null"); + zassert_equal(notify.flags, 0, + "flags not cleared"); + k_poll_signal_check(&sig, &rc, &res); + zassert_equal(rc, 1, + "signal not set"); + zassert_equal(res, set_res, + "signal result wrong"); + + rc = sys_notify_fetch_result(¬ify, &res); + zassert_equal(rc, 0, + "signal not ready"); + zassert_equal(res, set_res, + "result not set"); +#endif /* CONFIG_POLL */ +} + +static void test_callback(void) +{ + int rc; + int set_res = 423; + int res; + sys_notify_generic_callback cb; + struct sys_notify notify; + u32_t xflags = 0x8765432; + + memset(¬ify, 0xac, sizeof(notify)); + rc = sys_notify_validate(¬ify); + zassert_equal(rc, -EINVAL, + "invalid not diagnosed"); + + sys_notify_init_callback(¬ify, callback); + notify.method.callback = NULL; + rc = sys_notify_validate(¬ify); + zassert_equal(rc, -EINVAL, + "null callback not invalid"); + + memset(¬ify, 0xac, sizeof(notify)); + sys_notify_init_callback(¬ify, callback); + rc = sys_notify_validate(¬ify); + zassert_equal(rc, 0, + "init_spinwait invalid"); + + zassert_true(sys_notify_uses_callback(¬ify), + "not using callback"); + + zassert_equal(notify.flags, SYS_NOTIFY_METHOD_CALLBACK, + "flags mismatch"); + zassert_equal(notify.method.callback, + (sys_notify_generic_callback)callback, + "callback mismatch"); + + set_extflags(¬ify, xflags); + zassert_equal(sys_notify_get_method(¬ify), + SYS_NOTIFY_METHOD_CALLBACK, + "method corrupted"); + zassert_equal(get_extflags(¬ify), xflags, + "xflags extract failed"); + + rc = sys_notify_fetch_result(¬ify, &res); + zassert_equal(rc, -EAGAIN, + "callback ready too soon"); + + zassert_not_equal(notify.flags, 0, + "flags cleared"); + + cb = sys_notify_finalize(¬ify, set_res); + zassert_equal(cb, (sys_notify_generic_callback)callback, + "callback wrong"); + zassert_equal(notify.flags, 0, + "flags not cleared"); + + res = ~set_res; + ((sys_notify_generic_callback)cb)(¬ify, &res); + zassert_equal(res, set_res, + "result not set"); +} + +void test_main(void) +{ + ztest_test_suite(sys_notify_api, + ztest_unit_test(test_validate), + ztest_unit_test(test_spinwait), + ztest_unit_test(test_signal), + ztest_unit_test(test_callback)); + ztest_run_test_suite(sys_notify_api); +} diff --git a/tests/lib/notify/testcase.yaml b/tests/lib/notify/testcase.yaml new file mode 100644 index 00000000000..cb1ba154ca9 --- /dev/null +++ b/tests/lib/notify/testcase.yaml @@ -0,0 +1,3 @@ +tests: + libraries.data_structures: + tags: notify diff --git a/tests/lib/onoff/src/main.c b/tests/lib/onoff/src/main.c index 25c7e08c242..6158a060e07 100644 --- a/tests/lib/onoff/src/main.c +++ b/tests/lib/onoff/src/main.c @@ -233,7 +233,7 @@ static void test_client_init_validation(void) onoff_client_init_spinwait(&cli); zassert_equal(z_snode_next_peek(&cli.node), NULL, "cli node mismatch"); - zassert_equal(cli.flags, ONOFF_CLIENT_NOTIFY_SPINWAIT, + zassert_equal(cli.notify.flags, SYS_NOTIFY_METHOD_SPINWAIT, "cli spinwait flags"); struct k_poll_signal sig; @@ -242,20 +242,20 @@ static void test_client_init_validation(void) onoff_client_init_signal(&cli, &sig); zassert_equal(z_snode_next_peek(&cli.node), NULL, "cli signal node"); - zassert_equal(cli.flags, ONOFF_CLIENT_NOTIFY_SIGNAL, + zassert_equal(cli.notify.flags, SYS_NOTIFY_METHOD_SIGNAL, "cli signal flags"); - zassert_equal(cli.async.signal, &sig, + zassert_equal(cli.notify.method.signal, &sig, "cli signal async"); memset(&cli, 0xA5, sizeof(cli)); onoff_client_init_callback(&cli, callback, &sig); zassert_equal(z_snode_next_peek(&cli.node), NULL, "cli callback node"); - zassert_equal(cli.flags, ONOFF_CLIENT_NOTIFY_CALLBACK, + zassert_equal(cli.notify.flags, SYS_NOTIFY_METHOD_CALLBACK, "cli callback flags"); - zassert_equal(cli.async.callback.handler, callback, + zassert_equal(cli.notify.method.callback, callback, "cli callback handler"); - zassert_equal(cli.async.callback.user_data, &sig, + zassert_equal(cli.user_data, &sig, "cli callback user_data"); } @@ -305,7 +305,7 @@ static void test_validate_args(void) "validate req cli flags"); init_spinwait(&cli); - cli.flags = ONOFF_CLIENT_NOTIFY_INVALID; + cli.notify.flags = SYS_NOTIFY_METHOD_COMPLETED; rc = onoff_request(&srv, &cli); zassert_equal(rc, -EINVAL, "validate req cli mode"); @@ -315,7 +315,7 @@ static void test_validate_args(void) zassert_equal(rc, 0, "validate req cli signal: %d", rc); init_notify_sig(&cli, &sig); - cli.async.signal = NULL; + cli.notify.method.signal = NULL; rc = onoff_request(&srv, &cli); zassert_equal(rc, -EINVAL, "validate req cli signal null"); @@ -326,7 +326,7 @@ static void test_validate_args(void) "validate req cli callback"); init_notify_cb(&cli); - cli.async.callback.handler = NULL; + cli.notify.method.callback = NULL; rc = onoff_request(&srv, &cli); zassert_equal(rc, -EINVAL, "validate req cli callback null"); @@ -822,7 +822,7 @@ static void test_async(void) "rel to-off: %d", rc); /* Finalize queued start, gets us to on */ - cli[0].result = 1 + start_state.retval; + cli[0].notify.result = 1 + start_state.retval; zassert_equal(cli_result(&cli[0]), -EAGAIN, "fetch failed"); zassert_false(start_state.notify == NULL,