1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Events provide a general-purpose way to react to / subscribe to changes
4 * within U-Boot
5 *
6 * Copyright 2021 Google LLC
7 * Written by Simon Glass <sjg@chromium.org>
8 */
9
10#define LOG_CATEGORY	LOGC_EVENT
11
12#include <common.h>
13#include <event.h>
14#include <event_internal.h>
15#include <log.h>
16#include <linker_lists.h>
17#include <malloc.h>
18#include <asm/global_data.h>
19#include <linux/list.h>
20#include <relocate.h>
21
22DECLARE_GLOBAL_DATA_PTR;
23
24#if CONFIG_IS_ENABLED(EVENT_DEBUG)
25const char *const type_name[] = {
26	"none",
27	"test",
28
29	/* Events related to driver model */
30	"dm_post_init_f",
31	"dm_post_init_r",
32	"dm_pre_probe",
33	"dm_post_probe",
34	"dm_pre_remove",
35	"dm_post_remove",
36
37	/* init hooks */
38	"misc_init_f",
39	"fsp_init_r",
40	"settings_r",
41	"last_stage_init",
42
43	/* Fpga load hook */
44	"fpga_load",
45
46	/* fdt hooks */
47	"ft_fixup",
48
49	/* main loop events */
50	"main_loop",
51};
52
53_Static_assert(ARRAY_SIZE(type_name) == EVT_COUNT, "event type_name size");
54#endif
55
56const char *event_type_name(enum event_t type)
57{
58#if CONFIG_IS_ENABLED(EVENT_DEBUG)
59	if (type < ARRAY_SIZE(type_name))
60		return type_name[type];
61	else
62		return "(unknown)";
63#else
64	return "(unknown)";
65#endif
66}
67
68static int notify_static(struct event *ev)
69{
70	struct evspy_info *start =
71		ll_entry_start(struct evspy_info, evspy_info);
72	const int n_ents = ll_entry_count(struct evspy_info, evspy_info);
73	struct evspy_info *spy;
74
75	for (spy = start; spy != start + n_ents; spy++) {
76		if (spy->type == ev->type) {
77			int ret;
78
79			log_debug("Sending event %x/%s to spy '%s'\n", ev->type,
80				  event_type_name(ev->type), event_spy_id(spy));
81			if (spy->flags & EVSPYF_SIMPLE) {
82				const struct evspy_info_simple *simple;
83
84				simple = (struct evspy_info_simple *)spy;
85				ret = simple->func();
86			} else {
87				ret = spy->func(NULL, ev);
88			}
89
90			/*
91			 * TODO: Handle various return codes to
92			 *
93			 * - claim an event (no others will see it)
94			 * - return an error from the event
95			 */
96			if (ret)
97				return log_msg_ret("spy", ret);
98		}
99	}
100
101	return 0;
102}
103
104static int notify_dynamic(struct event *ev)
105{
106	struct event_state *state = gd_event_state();
107	struct event_spy *spy, *next;
108
109	list_for_each_entry_safe(spy, next, &state->spy_head, sibling_node) {
110		if (spy->type == ev->type) {
111			int ret;
112
113			log_debug("Sending event %x/%s to spy '%s'\n", ev->type,
114				  event_type_name(ev->type), spy->id);
115			ret = spy->func(spy->ctx, ev);
116
117			/*
118			 * TODO: Handle various return codes to
119			 *
120			 * - claim an event (no others will see it)
121			 * - return an error from the event
122			 */
123			if (ret)
124				return log_msg_ret("spy", ret);
125		}
126	}
127
128	return 0;
129}
130
131int event_notify(enum event_t type, void *data, int size)
132{
133	struct event event;
134	int ret;
135
136	event.type = type;
137	if (size > sizeof(event.data))
138		return log_msg_ret("size", -E2BIG);
139	memcpy(&event.data, data, size);
140
141	ret = notify_static(&event);
142	if (ret)
143		return log_msg_ret("sta", ret);
144
145	if (CONFIG_IS_ENABLED(EVENT_DYNAMIC)) {
146		ret = notify_dynamic(&event);
147		if (ret)
148			return log_msg_ret("dyn", ret);
149	}
150
151	return 0;
152}
153
154int event_notify_null(enum event_t type)
155{
156	return event_notify(type, NULL, 0);
157}
158
159void event_show_spy_list(void)
160{
161	struct evspy_info *start =
162		ll_entry_start(struct evspy_info, evspy_info);
163	const int n_ents = ll_entry_count(struct evspy_info, evspy_info);
164	struct evspy_info *spy;
165	const int size = sizeof(ulong) * 2;
166
167	printf("Seq  %-24s  %*s  %s\n", "Type", size, "Function", "ID");
168	for (spy = start; spy != start + n_ents; spy++) {
169		printf("%3x  %-3x %-20s  %*p  %s\n", (uint)(spy - start),
170		       spy->type, event_type_name(spy->type), size, spy->func,
171		       event_spy_id(spy));
172	}
173}
174
175#if CONFIG_IS_ENABLED(EVENT_DYNAMIC)
176static void spy_free(struct event_spy *spy)
177{
178	list_del(&spy->sibling_node);
179}
180
181int event_register(const char *id, enum event_t type, event_handler_t func, void *ctx)
182{
183	struct event_state *state = gd_event_state();
184	struct event_spy *spy;
185
186	spy = malloc(sizeof(*spy));
187	if (!spy)
188		return log_msg_ret("alloc", -ENOMEM);
189
190	spy->id = id;
191	spy->type = type;
192	spy->func = func;
193	spy->ctx = ctx;
194	list_add_tail(&spy->sibling_node, &state->spy_head);
195
196	return 0;
197}
198
199int event_uninit(void)
200{
201	struct event_state *state = gd_event_state();
202	struct event_spy *spy, *next;
203
204	list_for_each_entry_safe(spy, next, &state->spy_head, sibling_node)
205		spy_free(spy);
206
207	return 0;
208}
209
210int event_init(void)
211{
212	struct event_state *state = gd_event_state();
213
214	INIT_LIST_HEAD(&state->spy_head);
215
216	return 0;
217}
218#endif /* EVENT_DYNAMIC */
219