event: Add basic support for events
[platform/kernel/u-boot.git] / common / event.c
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
21 DECLARE_GLOBAL_DATA_PTR;
22
23 #if CONFIG_IS_ENABLED(EVENT_DEBUG)
24 const char *const type_name[] = {
25         "none",
26         "test",
27 };
28
29 _Static_assert(ARRAY_SIZE(type_name) == EVT_COUNT, "event type_name size");
30 #endif
31
32 static const char *event_type_name(enum event_t type)
33 {
34 #if CONFIG_IS_ENABLED(EVENT_DEBUG)
35         return type_name[type];
36 #else
37         return "(unknown)";
38 #endif
39 }
40
41 static int notify_static(struct event *ev)
42 {
43         struct evspy_info *start =
44                 ll_entry_start(struct evspy_info, evspy_info);
45         const int n_ents = ll_entry_count(struct evspy_info, evspy_info);
46         struct evspy_info *spy;
47
48         for (spy = start; spy != start + n_ents; spy++) {
49                 if (spy->type == ev->type) {
50                         int ret;
51
52                         log_debug("Sending event %x/%s to spy '%s'\n", ev->type,
53                                   event_type_name(ev->type), event_spy_id(spy));
54                         ret = spy->func(NULL, ev);
55
56                         /*
57                          * TODO: Handle various return codes to
58                          *
59                          * - claim an event (no others will see it)
60                          * - return an error from the event
61                          */
62                         if (ret)
63                                 return log_msg_ret("spy", ret);
64                 }
65         }
66
67         return 0;
68 }
69
70 static int notify_dynamic(struct event *ev)
71 {
72         struct event_state *state = gd_event_state();
73         struct event_spy *spy, *next;
74
75         list_for_each_entry_safe(spy, next, &state->spy_head, sibling_node) {
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), spy->id);
81                         ret = spy->func(spy->ctx, ev);
82
83                         /*
84                          * TODO: Handle various return codes to
85                          *
86                          * - claim an event (no others will see it)
87                          * - return an error from the event
88                          */
89                         if (ret)
90                                 return log_msg_ret("spy", ret);
91                 }
92         }
93
94         return 0;
95 }
96
97 int event_notify(enum event_t type, void *data, int size)
98 {
99         struct event event;
100         int ret;
101
102         event.type = type;
103         if (size > sizeof(event.data))
104                 return log_msg_ret("size", -E2BIG);
105         memcpy(&event.data, data, size);
106
107         ret = notify_static(&event);
108         if (ret)
109                 return log_msg_ret("dyn", ret);
110
111         if (CONFIG_IS_ENABLED(EVENT_DYNAMIC)) {
112                 ret = notify_dynamic(&event);
113                 if (ret)
114                         return log_msg_ret("dyn", ret);
115         }
116
117         return 0;
118 }
119
120 int event_notify_null(enum event_t type)
121 {
122         return event_notify(type, NULL, 0);
123 }
124
125 void event_show_spy_list(void)
126 {
127         struct evspy_info *start =
128                 ll_entry_start(struct evspy_info, evspy_info);
129         const int n_ents = ll_entry_count(struct evspy_info, evspy_info);
130         struct evspy_info *spy;
131         const int size = sizeof(ulong) * 2;
132
133         printf("Seq  %-24s  %*s  %s\n", "Type", size, "Function", "ID");
134         for (spy = start; spy != start + n_ents; spy++) {
135                 printf("%3x  %-3x %-20s  %*p  %s\n", (uint)(spy - start),
136                        spy->type, event_type_name(spy->type), size, spy->func,
137                        event_spy_id(spy));
138         }
139 }
140
141 #if CONFIG_IS_ENABLED(EVENT_DYNAMIC)
142 static void spy_free(struct event_spy *spy)
143 {
144         list_del(&spy->sibling_node);
145 }
146
147 int event_register(const char *id, enum event_t type, event_handler_t func, void *ctx)
148 {
149         struct event_state *state = gd_event_state();
150         struct event_spy *spy;
151
152         if (!CONFIG_IS_ENABLED(EVENT_DYNAMIC))
153                 return -ENOSYS;
154         spy = malloc(sizeof(*spy));
155         if (!spy)
156                 return log_msg_ret("alloc", -ENOMEM);
157
158         spy->id = id;
159         spy->type = type;
160         spy->func = func;
161         spy->ctx = ctx;
162         list_add_tail(&spy->sibling_node, &state->spy_head);
163
164         return 0;
165 }
166
167 int event_uninit(void)
168 {
169         struct event_state *state = gd_event_state();
170         struct event_spy *spy, *next;
171
172         list_for_each_entry_safe(spy, next, &state->spy_head, sibling_node)
173                 spy_free(spy);
174
175         return 0;
176 }
177
178 int event_init(void)
179 {
180         struct event_state *state = gd_event_state();
181
182         INIT_LIST_HEAD(&state->spy_head);
183
184         return 0;
185 }
186 #endif /* EVENT_DYNAMIC */