98887ab802
At present there is a confusing array of functions that handle the device tree fix-ups needed for booting an OS. We should be able to switch to using events to clean this up. As a first step, create a new event type and call it from the standard place. Note that this event uses the ofnode interface only, since this can support live tree which is more efficient when making lots of updates. Signed-off-by: Simon Glass <sjg@chromium.org>
213 lines
4.3 KiB
C
213 lines
4.3 KiB
C
// 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>
|
|
#include <relocate.h>
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
#if CONFIG_IS_ENABLED(EVENT_DEBUG)
|
|
const char *const type_name[] = {
|
|
"none",
|
|
"test",
|
|
|
|
/* Events related to driver model */
|
|
"dm_post_init",
|
|
"dm_pre_probe",
|
|
"dm_post_probe",
|
|
"dm_pre_remove",
|
|
"dm_post_remove",
|
|
|
|
/* init hooks */
|
|
"misc_init_f",
|
|
|
|
/* fdt hooks */
|
|
"ft_fixup",
|
|
};
|
|
|
|
_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(NEEDS_MANUAL_RELOC)
|
|
int event_manual_reloc(void)
|
|
{
|
|
struct evspy_info *spy, *end;
|
|
|
|
spy = ll_entry_start(struct evspy_info, evspy_info);
|
|
end = ll_entry_end(struct evspy_info, evspy_info);
|
|
for (; spy < end; spy++)
|
|
MANUAL_RELOC(spy->func);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#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;
|
|
|
|
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 */
|