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