event: Add basic support for events
[platform/kernel/u-boot.git] / include / event.h
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 #ifndef __event_h
11 #define __event_h
12
13 /**
14  * enum event_t - Types of events supported by U-Boot
15  *
16  * @EVT_DM_PRE_PROBE: Device is about to be probed
17  */
18 enum event_t {
19         EVT_NONE,
20         EVT_TEST,
21
22         EVT_COUNT
23 };
24
25 union event_data {
26         /**
27          * struct event_data_test  - test data
28          *
29          * @signal: A value to update the state with
30          */
31         struct event_data_test {
32                 int signal;
33         } test;
34 };
35
36 /**
37  * struct event - an event that can be sent and received
38  *
39  * @type: Event type
40  * @data: Data for this particular event
41  */
42 struct event {
43         enum event_t type;
44         union event_data data;
45 };
46
47 /** Function type for event handlers */
48 typedef int (*event_handler_t)(void *ctx, struct event *event);
49
50 /**
51  * struct evspy_info - information about an event spy
52  *
53  * @func: Function to call when the event is activated (must be first)
54  * @type: Event type
55  * @id: Event id string
56  */
57 struct evspy_info {
58         event_handler_t func;
59         enum event_t type;
60 #if CONFIG_IS_ENABLED(EVENT_DEBUG)
61         const char *id;
62 #endif
63 };
64
65 /* Declare a new event spy */
66 #if CONFIG_IS_ENABLED(EVENT_DEBUG)
67 #define _ESPY_REC(_type, _func)   { _func, _type, #_func, }
68 #else
69 #define _ESPY_REC(_type, _func)   { _func, _type, }
70 #endif
71
72 static inline const char *event_spy_id(struct evspy_info *spy)
73 {
74 #if CONFIG_IS_ENABLED(EVENT_DEBUG)
75         return spy->id;
76 #else
77         return "?";
78 #endif
79 }
80
81 /*
82  * It seems that LTO will drop list entries if it decides they are not used,
83  * although the conditions that cause this are unclear.
84  *
85  * The example found is the following:
86  *
87  * static int sandbox_misc_init_f(void *ctx, struct event *event)
88  * {
89  *    return sandbox_early_getopt_check();
90  * }
91  * EVENT_SPY(EVT_MISC_INIT_F, sandbox_misc_init_f);
92  *
93  * where EVENT_SPY uses ll_entry_declare()
94  *
95  * In this case, LTO decides to drop the sandbox_misc_init_f() function
96  * (which is fine) but then drops the linker-list entry too. This means
97  * that the code no longer works, in this case sandbox no-longer checks its
98  * command-line arguments properly.
99  *
100  * Without LTO, the KEEP() command in the .lds file is enough to keep the
101  * entry around. But with LTO it seems that the entry has already been
102  * dropped before the link script is considered.
103  *
104  * The only solution I can think of is to mark linker-list entries as 'used'
105  * using an attribute. This should be safe, since we don't actually want to drop
106  * any of these. However this does slightly limit LTO's optimisation choices.
107  */
108 #define EVENT_SPY(_type, _func) \
109         static __attribute__((used)) ll_entry_declare(struct evspy_info, \
110                                                       _type, evspy_info) = \
111         _ESPY_REC(_type, _func)
112
113 /**
114  * event_register - register a new spy
115  *
116  * @id: Spy ID
117  * @type: Event type to subscribe to
118  * @func: Function to call when the event is sent
119  * @ctx: Context to pass to the function
120  * @return 0 if OK, -ve on error
121  */
122 int event_register(const char *id, enum event_t type, event_handler_t func,
123                    void *ctx);
124
125 #if CONFIG_IS_ENABLED(EVENT)
126 /**
127  * event_notify() - notify spies about an event
128  *
129  * It is possible to pass in union event_data here but that may not be
130  * convenient if the data is elsewhere, or is one of the members of the union.
131  * So this uses a void * for @data, with a separate @size.
132  *
133  * @type: Event type
134  * @data: Event data to be sent (e.g. union_event_data)
135  * @size: Size of data in bytes
136  * @return 0 if OK, -ve on error
137  */
138 int event_notify(enum event_t type, void *data, int size);
139
140 /**
141  * event_notify_null() - notify spies about an event
142  *
143  * Data is NULL and the size is 0
144  *
145  * @type: Event type
146  * @return 0 if OK, -ve on error
147  */
148 int event_notify_null(enum event_t type);
149 #else
150 static inline int event_notify(enum event_t type, void *data, int size)
151 {
152         return 0;
153 }
154
155 static inline int event_notify_null(enum event_t type)
156 {
157         return 0;
158 }
159 #endif
160
161 #if CONFIG_IS_ENABLED(EVENT_DYNAMIC)
162 /**
163  * event_uninit() - Clean up dynamic events
164  *
165  * This removes all dynamic event handlers
166  */
167 int event_uninit(void);
168
169 /**
170  * event_uninit() - Set up dynamic events
171  *
172  * Init a list of dynamic event handlers, so that these can be added as
173  * needed
174  */
175 int event_init(void);
176 #else
177 static inline int event_uninit(void)
178 {
179         return 0;
180 }
181
182 static inline int event_init(void)
183 {
184         return 0;
185 }
186 #endif
187
188 #endif