summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJack Rosenthal <jrosenth@chromium.org>2020-10-27 13:14:46 -0600
committerCommit Bot <commit-bot@chromium.org>2020-11-05 23:07:36 +0000
commit9d157db4aaa9cb04f33809f170712a244b627b56 (patch)
treec3d22d52c614cd0f8d38c9cc5ba2a1f08add80ee
parent1baff54e05a5d2f9179615f717e3514d37a594cb (diff)
downloadchrome-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.h37
-rw-r--r--zephyr/Kconfig8
-rw-r--r--zephyr/shim/include/zephyr_hooks_shim.h88
-rw-r--r--zephyr/shim/src/CMakeLists.txt1
-rw-r--r--zephyr/shim/src/hooks.c95
-rw-r--r--zephyr/test/hooks/CMakeLists.txt9
-rw-r--r--zephyr/test/hooks/hooks.c134
-rw-r--r--zephyr/test/hooks/prj.conf11
-rw-r--r--zephyr/test/hooks/zmake.yaml9
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