mirror of
https://xff.cz/git/u-boot/
synced 2025-09-01 16:52:14 +02:00
event: Add basic support for events
Add a way to create and dispatch events without needing to allocate memory. Also add a way to 'spy' on events, thus allowing 'hooks' to be created. Use a linker list for static events, which we can use to replace functions like arch_cpu_init_f(). Allow an EVENT_DEBUG option which makes it easier to see what is going on at runtime, but uses more code space. Dynamic events allow the creation of a spy at runtime. This is not always necessary, but can be enabled with EVENT_DYNAMIC if needed. A 'test' event is the only option for now. Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
186
common/event.c
Normal file
186
common/event.c
Normal file
@@ -0,0 +1,186 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Events provide a general-purpose way to react to / subscribe to changes
|
||||
* within U-Boot
|
||||
*
|
||||
* Copyright 2021 Google LLC
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*/
|
||||
|
||||
#define LOG_CATEGORY LOGC_EVENT
|
||||
|
||||
#include <common.h>
|
||||
#include <event.h>
|
||||
#include <event_internal.h>
|
||||
#include <log.h>
|
||||
#include <linker_lists.h>
|
||||
#include <malloc.h>
|
||||
#include <asm/global_data.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#if CONFIG_IS_ENABLED(EVENT_DEBUG)
|
||||
const char *const type_name[] = {
|
||||
"none",
|
||||
"test",
|
||||
};
|
||||
|
||||
_Static_assert(ARRAY_SIZE(type_name) == EVT_COUNT, "event type_name size");
|
||||
#endif
|
||||
|
||||
static const char *event_type_name(enum event_t type)
|
||||
{
|
||||
#if CONFIG_IS_ENABLED(EVENT_DEBUG)
|
||||
return type_name[type];
|
||||
#else
|
||||
return "(unknown)";
|
||||
#endif
|
||||
}
|
||||
|
||||
static int notify_static(struct event *ev)
|
||||
{
|
||||
struct evspy_info *start =
|
||||
ll_entry_start(struct evspy_info, evspy_info);
|
||||
const int n_ents = ll_entry_count(struct evspy_info, evspy_info);
|
||||
struct evspy_info *spy;
|
||||
|
||||
for (spy = start; spy != start + n_ents; spy++) {
|
||||
if (spy->type == ev->type) {
|
||||
int ret;
|
||||
|
||||
log_debug("Sending event %x/%s to spy '%s'\n", ev->type,
|
||||
event_type_name(ev->type), event_spy_id(spy));
|
||||
ret = spy->func(NULL, ev);
|
||||
|
||||
/*
|
||||
* TODO: Handle various return codes to
|
||||
*
|
||||
* - claim an event (no others will see it)
|
||||
* - return an error from the event
|
||||
*/
|
||||
if (ret)
|
||||
return log_msg_ret("spy", ret);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int notify_dynamic(struct event *ev)
|
||||
{
|
||||
struct event_state *state = gd_event_state();
|
||||
struct event_spy *spy, *next;
|
||||
|
||||
list_for_each_entry_safe(spy, next, &state->spy_head, sibling_node) {
|
||||
if (spy->type == ev->type) {
|
||||
int ret;
|
||||
|
||||
log_debug("Sending event %x/%s to spy '%s'\n", ev->type,
|
||||
event_type_name(ev->type), spy->id);
|
||||
ret = spy->func(spy->ctx, ev);
|
||||
|
||||
/*
|
||||
* TODO: Handle various return codes to
|
||||
*
|
||||
* - claim an event (no others will see it)
|
||||
* - return an error from the event
|
||||
*/
|
||||
if (ret)
|
||||
return log_msg_ret("spy", ret);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int event_notify(enum event_t type, void *data, int size)
|
||||
{
|
||||
struct event event;
|
||||
int ret;
|
||||
|
||||
event.type = type;
|
||||
if (size > sizeof(event.data))
|
||||
return log_msg_ret("size", -E2BIG);
|
||||
memcpy(&event.data, data, size);
|
||||
|
||||
ret = notify_static(&event);
|
||||
if (ret)
|
||||
return log_msg_ret("dyn", ret);
|
||||
|
||||
if (CONFIG_IS_ENABLED(EVENT_DYNAMIC)) {
|
||||
ret = notify_dynamic(&event);
|
||||
if (ret)
|
||||
return log_msg_ret("dyn", ret);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int event_notify_null(enum event_t type)
|
||||
{
|
||||
return event_notify(type, NULL, 0);
|
||||
}
|
||||
|
||||
void event_show_spy_list(void)
|
||||
{
|
||||
struct evspy_info *start =
|
||||
ll_entry_start(struct evspy_info, evspy_info);
|
||||
const int n_ents = ll_entry_count(struct evspy_info, evspy_info);
|
||||
struct evspy_info *spy;
|
||||
const int size = sizeof(ulong) * 2;
|
||||
|
||||
printf("Seq %-24s %*s %s\n", "Type", size, "Function", "ID");
|
||||
for (spy = start; spy != start + n_ents; spy++) {
|
||||
printf("%3x %-3x %-20s %*p %s\n", (uint)(spy - start),
|
||||
spy->type, event_type_name(spy->type), size, spy->func,
|
||||
event_spy_id(spy));
|
||||
}
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(EVENT_DYNAMIC)
|
||||
static void spy_free(struct event_spy *spy)
|
||||
{
|
||||
list_del(&spy->sibling_node);
|
||||
}
|
||||
|
||||
int event_register(const char *id, enum event_t type, event_handler_t func, void *ctx)
|
||||
{
|
||||
struct event_state *state = gd_event_state();
|
||||
struct event_spy *spy;
|
||||
|
||||
if (!CONFIG_IS_ENABLED(EVENT_DYNAMIC))
|
||||
return -ENOSYS;
|
||||
spy = malloc(sizeof(*spy));
|
||||
if (!spy)
|
||||
return log_msg_ret("alloc", -ENOMEM);
|
||||
|
||||
spy->id = id;
|
||||
spy->type = type;
|
||||
spy->func = func;
|
||||
spy->ctx = ctx;
|
||||
list_add_tail(&spy->sibling_node, &state->spy_head);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int event_uninit(void)
|
||||
{
|
||||
struct event_state *state = gd_event_state();
|
||||
struct event_spy *spy, *next;
|
||||
|
||||
list_for_each_entry_safe(spy, next, &state->spy_head, sibling_node)
|
||||
spy_free(spy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int event_init(void)
|
||||
{
|
||||
struct event_state *state = gd_event_state();
|
||||
|
||||
INIT_LIST_HEAD(&state->spy_head);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* EVENT_DYNAMIC */
|
Reference in New Issue
Block a user