summaryrefslogtreecommitdiff
path: root/zephyr/shim/src/hooks.c
blob: 2ec19304d3d2a023aa534970fd0ce6f6868acd56 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
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 <kernel.h>
#include <zephyr.h>

#include "common.h"
#include "console.h"
#include "ec_tasks.h"
#include "hooks.h"
#include "task.h"
#include "timer.h"

int hook_call_deferred(const struct deferred_data *data, int us)
{
#if IS_ZEPHYR_VERSION(2, 6)
	struct k_work_delayable *work = data->work;
#else
	struct k_delayed_work *work = data->work;
#endif
	int rv = 0;

	if (us == -1) {
#if IS_ZEPHYR_VERSION(2, 6)
		k_work_cancel_delayable(work);
#else
		k_delayed_work_cancel(work);
#endif
	} else if (us >= 0) {
#if IS_ZEPHYR_VERSION(2, 6)
		rv = k_work_schedule(work, K_USEC(us));
#else
		rv = k_delayed_work_submit(work, K_USEC(us));
#endif
		if (rv == -EINVAL) {
			/* Already processing or completed. */
			return 0;
		} else if (rv < 0) {
			cprints(CC_HOOK,
				"Warning: deferred call not submitted, "
				"deferred_data=0x%pP, err=%d",
				data, rv);
		}
	} else {
		return EC_ERROR_PARAM2;
	}

	return rv;
}

static struct zephyr_shim_hook_list *hook_registry[HOOK_TYPE_COUNT];

static int zephyr_shim_setup_hooks(const struct device *unused)
{
	STRUCT_SECTION_FOREACH(zephyr_shim_hook_list, entry) {
		struct zephyr_shim_hook_list **loc = &hook_registry[entry->type];

		/* Find the correct place to put the entry in the registry. */
		while (*loc && (*loc)->priority < entry->priority)
			loc = &((*loc)->next);

		entry->next = *loc;

		/* Insert the entry. */
		*loc = entry;
	}

	return 0;
}

SYS_INIT(zephyr_shim_setup_hooks, APPLICATION, 1);

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 void check_hook_task_priority(k_tid_t thread)
{
	/*
	 * Numerically lower priorities take precedence, so verify the hook
	 * related threads cannot preempt any of the shimmed tasks.
	 */
	if (k_thread_priority_get(thread) < (TASK_ID_COUNT - 1))
		cprintf(CC_HOOK,
			"ERROR: %s has priority %d but must be >= %d\n",
			k_thread_name_get(thread),
			k_thread_priority_get(thread), (TASK_ID_COUNT - 1));
}

void hook_task(void *u)
{
	/* Periodic hooks will be called first time through the loop */
	static uint64_t last_second = -SECOND;
	static uint64_t last_tick = -HOOK_TICK_INTERVAL;

	/*
	 * Verify deferred routines are run at the lowest priority.
	 */
	check_hook_task_priority(&k_sys_work_q.thread);
	check_hook_task_priority(k_current_get());

	while (1) {
		uint64_t t = get_time().val;
		int next = 0;

		if (t - last_tick >= HOOK_TICK_INTERVAL) {
			hook_notify(HOOK_TICK);
			last_tick = t;
		}

		if (t - last_second >= SECOND) {
			hook_notify(HOOK_SECOND);
			last_second = t;
		}

		/* Calculate when next tick needs to occur */
		t = get_time().val;
		if (last_tick + HOOK_TICK_INTERVAL > t)
			next = last_tick + HOOK_TICK_INTERVAL - t;

		/*
		 * Sleep until next tick, unless we've already exceeded
		 * HOOK_TICK_INTERVAL.
		 */
		if (next > 0)
			task_wait_event(next);
	}
}