diff options
author | Jack Rosenthal <jrosenth@chromium.org> | 2020-10-27 13:14:46 -0600 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-11-05 23:07:36 +0000 |
commit | 9d157db4aaa9cb04f33809f170712a244b627b56 (patch) | |
tree | c3d22d52c614cd0f8d38c9cc5ba2a1f08add80ee | |
parent | 1baff54e05a5d2f9179615f717e3514d37a594cb (diff) | |
download | chrome-ec-9d157db4aaa9cb04f33809f170712a244b627b56.tar.gz |
zephyr: shim in hooks and deferred
Implement deferred calls using the Zephyr's delayed work queues.
Implement hooks using SYS_INIT and a hooks registry created during
init.
BUG=b:168030971
BRANCH=none
TEST=provided unit tests
Build instructions for unit tests:
zmake configure -B/tmp/test \
~/trunk/src/platform/ec/zephyr/test/hooks
zmake build /tmp/test
Signed-off-by: Jack Rosenthal <jrosenth@chromium.org>
Change-Id: Id019cd1fe7bb3354377773d171036767e7efa706
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2504489
Reviewed-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Jett Rink <jettrink@chromium.org>
-rw-r--r-- | include/hooks.h | 37 | ||||
-rw-r--r-- | zephyr/Kconfig | 8 | ||||
-rw-r--r-- | zephyr/shim/include/zephyr_hooks_shim.h | 88 | ||||
-rw-r--r-- | zephyr/shim/src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | zephyr/shim/src/hooks.c | 95 | ||||
-rw-r--r-- | zephyr/test/hooks/CMakeLists.txt | 9 | ||||
-rw-r--r-- | zephyr/test/hooks/hooks.c | 134 | ||||
-rw-r--r-- | zephyr/test/hooks/prj.conf | 11 | ||||
-rw-r--r-- | zephyr/test/hooks/zmake.yaml | 9 |
9 files changed, 385 insertions, 7 deletions
diff --git a/include/hooks.h b/include/hooks.h index 39f558fe12..400713e794 100644 --- a/include/hooks.h +++ b/include/hooks.h @@ -235,6 +235,22 @@ enum hook_type { * USB PD cc connection event. */ HOOK_USB_PD_CONNECT, + +#ifdef TEST_BUILD + /* + * Special hook types to be used by unit tests of the hooks + * implementation only. + */ + HOOK_TEST_1, + HOOK_TEST_2, + HOOK_TEST_3, +#endif /* TEST_BUILD */ + + /* + * Not a hook type (instead the number of hooks). This should + * always be placed at the end of this enumeration. + */ + HOOK_TYPE_COUNT, }; struct hook_data { @@ -258,6 +274,14 @@ struct hook_data { */ void hook_notify(enum hook_type type); +/* + * CONFIG_PLATFORM_EC_HOOKS is enabled by default during a Zephyr + * build, but can be disabled via Kconfig if desired (leaving the stub + * implementation at the bottom of this file). + */ +#if defined(CONFIG_PLATFORM_EC_HOOKS) +#include "zephyr_hooks_shim.h" +#elif defined(CONFIG_COMMON_RUNTIME) struct deferred_data { /* Deferred function pointer */ void (*routine)(void); @@ -279,12 +303,6 @@ struct deferred_data { */ int hook_call_deferred(const struct deferred_data *data, int us); -/* - * Hooks are not currently supported by the Zephyr shim. - * TODO(b/168799177): Implement compatible DECLARE_HOOK macro for - * Zephyr OS. - */ -#if defined(CONFIG_COMMON_RUNTIME) && !defined(CONFIG_ZEPHYR) /** * Register a hook routine. * @@ -341,7 +359,12 @@ int hook_call_deferred(const struct deferred_data *data, int us); CONCAT2(routine, _data) \ __attribute__((section(".rodata.deferred"))) \ = {routine} -#else /* !defined(CONFIG_COMMON_RUNTIME) || defined(CONFIG_ZEPHYR) */ +#else +/* + * Stub implementation in case hooks are disabled (neither + * CONFIG_COMMON_RUNTIME nor CONFIG_PLATFORM_EC_HOOKS is defined) + */ +#define hook_call_deferred(unused1, unused2) -1 #define DECLARE_HOOK(t, func, p) \ void CONCAT2(unused_hook_, func)(void) { func(); } #define DECLARE_DEFERRED(func) \ diff --git a/zephyr/Kconfig b/zephyr/Kconfig index e2a5f62ff4..8a534f8eb6 100644 --- a/zephyr/Kconfig +++ b/zephyr/Kconfig @@ -56,4 +56,12 @@ config PLATFORM_EC_TIMER_CMD_WAITMS endif # PLATFORM_EC_TIMER +config PLATFORM_EC_HOOKS + bool "Enable hooks and deferred compatibility shim" + default y + help + Enable translation of DECLARE_DEFERRED and + hook_call_deferred to Zephyr's work queues, and a compatible + DECLARE_HOOK implementation. + endif # PLATFORM_EC diff --git a/zephyr/shim/include/zephyr_hooks_shim.h b/zephyr/shim/include/zephyr_hooks_shim.h new file mode 100644 index 0000000000..3bb0854b21 --- /dev/null +++ b/zephyr/shim/include/zephyr_hooks_shim.h @@ -0,0 +1,88 @@ +/* Copyright 2020 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#if !defined(__CROS_EC_HOOKS_H) || defined(__CROS_EC_ZEPHYR_HOOKS_SHIM_H) +#error "This file must only be included from hooks.h. Include hooks.h directly." +#endif +#define __CROS_EC_ZEPHYR_HOOKS_SHIM_H + +#include <init.h> +#include <kernel.h> +#include <zephyr.h> + +#include "common.h" + +/** + * The internal data structure stored for a deferred function. + */ +struct deferred_data { + void (*routine)(void); + struct k_delayed_work delayed_work; +}; + +/** + * See include/hooks.h for documentation. + */ +int hook_call_deferred(struct deferred_data *data, int us); + +/** + * Runtime helper to setup deferred data. + * + * @param data The struct deferred_data. + */ +void zephyr_shim_setup_deferred(struct deferred_data *data); + +/** + * See include/hooks.h for documentation. + */ +#define DECLARE_DEFERRED(routine) _DECLARE_DEFERRED(routine) +#define _DECLARE_DEFERRED(_routine) \ + static __maybe_unused struct deferred_data _routine##_data = { \ + .routine = _routine, \ + }; \ + static int _setup_deferred_##_routine(const struct device *unused) \ + { \ + ARG_UNUSED(unused); \ + zephyr_shim_setup_deferred(&_routine##_data); \ + return 0; \ + } \ + SYS_INIT(_setup_deferred_##_routine, APPLICATION, 1) + +/** + * Internal linked-list structure used to store hook lists. + */ +struct zephyr_shim_hook_list { + void (*routine)(void); + int priority; + struct zephyr_shim_hook_list *next; +}; + +/** + * Runtime helper for DECLARE_HOOK setup data. + * + * @param type The type of hook. + * @param routine The handler for the hook. + * @param priority The priority (smaller values are executed first). + * @param entry A statically allocated list entry. + */ +void zephyr_shim_setup_hook(enum hook_type type, void (*routine)(void), + int priority, struct zephyr_shim_hook_list *entry); + +/** + * See include/hooks.h for documentation. + */ +#define DECLARE_HOOK(hooktype, routine, priority) \ + _DECLARE_HOOK_1(hooktype, routine, priority, __LINE__) +#define _DECLARE_HOOK_1(hooktype, routine, priority, line) \ + _DECLARE_HOOK_2(hooktype, routine, priority, line) +#define _DECLARE_HOOK_2(hooktype, routine, priority, line) \ + static int _setup_hook_##line(const struct device *unused) \ + { \ + ARG_UNUSED(unused); \ + static struct zephyr_shim_hook_list lst; \ + zephyr_shim_setup_hook(hooktype, routine, priority, &lst); \ + return 0; \ + } \ + SYS_INIT(_setup_hook_##line, APPLICATION, 1) diff --git a/zephyr/shim/src/CMakeLists.txt b/zephyr/shim/src/CMakeLists.txt index 57ade258a3..3d7f4b3099 100644 --- a/zephyr/shim/src/CMakeLists.txt +++ b/zephyr/shim/src/CMakeLists.txt @@ -5,6 +5,7 @@ zephyr_sources(console.c) zephyr_sources(util.c) zephyr_sources(gpio.c) +zephyr_sources_ifdef(CONFIG_PLATFORM_EC_HOOKS hooks.c) zephyr_sources_ifdef(CONFIG_CROS_EC system.c) zephyr_sources_ifdef(CONFIG_SHIMMED_TASKS tasks.c) zephyr_sources_ifdef(CONFIG_PLATFORM_EC_TIMER hwtimer.c) diff --git a/zephyr/shim/src/hooks.c b/zephyr/shim/src/hooks.c new file mode 100644 index 0000000000..04b6b59461 --- /dev/null +++ b/zephyr/shim/src/hooks.c @@ -0,0 +1,95 @@ +/* Copyright 2020 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <kernel.h> +#include <zephyr.h> + +#include "common.h" +#include "console.h" +#include "hooks.h" + +#define DEFERRED_STACK_SIZE 1024 + +/* + * Deferred thread is always the lowest priority, and preemptive if + * available. + */ +#ifdef CONFIG_PREEMPT_ENABLED +#define DEFERRED_THREAD_PRIORITY (CONFIG_NUM_PREEMPT_PRIORITIES - 1) +#else +#define DEFERRED_THREAD_PRIORITY -1 +#endif + +static K_THREAD_STACK_DEFINE(deferred_thread, DEFERRED_STACK_SIZE); +static struct k_work_q deferred_work_queue; + +static void deferred_work_queue_handler(struct k_work *work) +{ + struct deferred_data *data = + CONTAINER_OF(work, struct deferred_data, delayed_work.work); + + data->routine(); +} + +static int init_deferred_work_queue(const struct device *unused) +{ + ARG_UNUSED(unused); + k_work_q_start(&deferred_work_queue, deferred_thread, + DEFERRED_STACK_SIZE, DEFERRED_THREAD_PRIORITY); + return 0; +} +SYS_INIT(init_deferred_work_queue, APPLICATION, 0); + +void zephyr_shim_setup_deferred(struct deferred_data *data) +{ + k_delayed_work_init(&data->delayed_work, deferred_work_queue_handler); +} + +int hook_call_deferred(struct deferred_data *data, int us) +{ + int rv; + + rv = k_delayed_work_submit_to_queue(&deferred_work_queue, + &data->delayed_work, K_USEC(us)); + if (rv < 0) + cprints(CC_HOOK, "Warning: deferred call not submitted."); + return rv; +} + +static struct zephyr_shim_hook_list *hook_registry[HOOK_TYPE_COUNT]; + +void zephyr_shim_setup_hook(enum hook_type type, void (*routine)(void), + int priority, struct zephyr_shim_hook_list *entry) +{ + struct zephyr_shim_hook_list **loc = &hook_registry[type]; + + /* Find the correct place to put the entry in the registry. */ + while (*loc && (*loc)->priority < priority) + loc = &((*loc)->next); + + /* Setup the entry. */ + entry->routine = routine; + entry->priority = priority; + entry->next = *loc; + + /* Insert the entry. */ + *loc = entry; +} + +void hook_notify(enum hook_type type) +{ + struct zephyr_shim_hook_list *p; + + for (p = hook_registry[type]; p; p = p->next) + p->routine(); +} + +static int run_init_hooks(const struct device *unused) +{ + ARG_UNUSED(unused); + hook_notify(HOOK_INIT); + return 0; +} +SYS_INIT(run_init_hooks, APPLICATION, 2); diff --git a/zephyr/test/hooks/CMakeLists.txt b/zephyr/test/hooks/CMakeLists.txt new file mode 100644 index 0000000000..9c6f0058fe --- /dev/null +++ b/zephyr/test/hooks/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright 2020 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +cmake_minimum_required(VERSION 3.13.1) +project(hooks) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +target_sources(app PRIVATE hooks.c) diff --git a/zephyr/test/hooks/hooks.c b/zephyr/test/hooks/hooks.c new file mode 100644 index 0000000000..6b482b30cf --- /dev/null +++ b/zephyr/test/hooks/hooks.c @@ -0,0 +1,134 @@ +/* Copyright 2020 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <stdbool.h> +#include <ztest.h> + +#include "hooks.h" + +static bool h1_called; +static bool h2_called; +static bool h3_called; + +static void h1(void) +{ + zassert_false(h1_called, "h1 was called, but should not have been"); + zassert_false(h2_called, "h2 was called, but should not have been"); + zassert_false(h3_called, "h3 was called, but should not have been"); + h1_called = true; +} +DECLARE_HOOK(HOOK_TEST_1, h1, HOOK_PRIO_FIRST); + +static void h2(void) +{ + zassert_true(h1_called, "h1 was not called, but should have been"); + zassert_false(h2_called, "h2 was called, but should not have been"); + zassert_false(h3_called, "h3 was called, but should not have been"); + h2_called = true; +} +DECLARE_HOOK(HOOK_TEST_1, h2, HOOK_PRIO_DEFAULT); + +static void h3(void) +{ + zassert_true(h1_called, "h1 was not called, but should have been"); + zassert_true(h2_called, "h2 was not called, but should have been"); + zassert_false(h3_called, "h3 was called, but should not have been"); + h3_called = true; +} +DECLARE_HOOK(HOOK_TEST_1, h3, HOOK_PRIO_LAST); + +static void test_hook_list_multiple(void) +{ + hook_notify(HOOK_TEST_1); + zassert_true(h1_called, "h1 was not called, but should have been"); + zassert_true(h2_called, "h2 was not called, but should have been"); + zassert_true(h3_called, "h3 was not called, but should have been"); +} + +static bool h4_called; + +static void h4(void) +{ + zassert_false(h4_called, "h4 was called, but should not have been"); + h4_called = true; +} +DECLARE_HOOK(HOOK_TEST_2, h4, HOOK_PRIO_DEFAULT); + +static void test_hook_list_single(void) +{ + hook_notify(HOOK_TEST_2); + zassert_true(h4_called, "h4 was not called, but should have been"); +} + +static void test_hook_list_empty(void) +{ + hook_notify(HOOK_TEST_3); +} + +static bool deferred_func_called; + +#define DEFERRED_DELAY_US (250 * 1000) +static void deferred_func(void) +{ + deferred_func_called = true; +} +DECLARE_DEFERRED(deferred_func); + +static void test_deferred_func(void) +{ + zassert_false( + deferred_func_called, + "The deferred function was called, but should not have been"); + hook_call_deferred(&deferred_func_data, DEFERRED_DELAY_US); + zassert_false( + deferred_func_called, + "The deferred function was called, but should not have been"); + k_usleep(DEFERRED_DELAY_US * 2); + zassert_true( + deferred_func_called, + "The deferred function was not called, but should have been"); +} + +static bool deferred_func_2_called; + +static void deferred_func_2(void) +{ + deferred_func_2_called = true; +} +DECLARE_DEFERRED(deferred_func_2); + +/* + * Test that repeated calls to hook_call_deferred result in the + * function being pushed out. + */ +static void test_deferred_func_push_out(void) +{ + zassert_false( + deferred_func_2_called, + "The deferred function was called, but should not have been"); + hook_call_deferred(&deferred_func_2_data, DEFERRED_DELAY_US); + hook_call_deferred(&deferred_func_2_data, DEFERRED_DELAY_US * 3); + k_usleep(DEFERRED_DELAY_US * 2); + zassert_false( + deferred_func_2_called, + "The deferred function was called, but should not have been"); + k_usleep(DEFERRED_DELAY_US * 2); + zassert_true( + deferred_func_called, + "The deferred function was not called, but should have been"); +} + +void test_main(void) +{ + ztest_test_suite( + hooks_tests, + ztest_unit_test(test_hook_list_multiple), + ztest_unit_test(test_hook_list_single), + ztest_unit_test(test_hook_list_empty), + ztest_unit_test(test_deferred_func), + ztest_unit_test(test_deferred_func_push_out)); + + ztest_run_test_suite(hooks_tests); +} diff --git a/zephyr/test/hooks/prj.conf b/zephyr/test/hooks/prj.conf new file mode 100644 index 0000000000..43c0c9b8e5 --- /dev/null +++ b/zephyr/test/hooks/prj.conf @@ -0,0 +1,11 @@ +# Copyright 2020 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +CONFIG_ZTEST=y +CONFIG_PLATFORM_EC=y + +# TODO(b/172512307): timer introduces dependency on system module, +# which has build issues right now for native_posix board. Remove +# this once the system module has been corrected. +CONFIG_PLATFORM_EC_TIMER=n diff --git a/zephyr/test/hooks/zmake.yaml b/zephyr/test/hooks/zmake.yaml new file mode 100644 index 0000000000..c784549bb0 --- /dev/null +++ b/zephyr/test/hooks/zmake.yaml @@ -0,0 +1,9 @@ +# Copyright 2020 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +board: native_posix +supported-zephyr-versions: + - v2.4 +toolchain: zephyr +output-type: elf |