157580Smjacob// SPDX-License-Identifier: GPL-2.0+
257580Smjacob/*
357580Smjacob * (C) Copyright 2012
457580Smjacob * Joe Hershberger, National Instruments, joe.hershberger@ni.com
557580Smjacob */
657580Smjacob
757580Smjacob#include <common.h>
857580Smjacob#include <env.h>
957580Smjacob#include <env_internal.h>
1057580Smjacob#include <asm/global_data.h>
1157580Smjacob
1257580Smjacob/*
1357580Smjacob * Look up a callback function pointer by name
1457580Smjacob */
1557580Smjacobstatic struct env_clbk_tbl *find_env_callback(const char *name)
1657580Smjacob{
1757580Smjacob	struct env_clbk_tbl *clbkp;
1857580Smjacob	int i;
1957580Smjacob	int num_callbacks = ll_entry_count(struct env_clbk_tbl, env_clbk);
2057580Smjacob
2157580Smjacob	if (name == NULL)
2257580Smjacob		return NULL;
2357580Smjacob
2457580Smjacob	/* look up the callback in the linker-list */
2557580Smjacob	for (i = 0, clbkp = ll_entry_start(struct env_clbk_tbl, env_clbk);
2657580Smjacob	     i < num_callbacks;
2757580Smjacob	     i++, clbkp++) {
2857580Smjacob		if (strcmp(name, clbkp->name) == 0)
2957580Smjacob			return clbkp;
3057580Smjacob	}
3157580Smjacob
3257580Smjacob	return NULL;
3357580Smjacob}
3457580Smjacob
35293865Sallanjudestatic int first_call = 1;
36293865Sallanjudestatic const char *callback_list;
37293865Sallanjude
38293865Sallanjude/*
39293865Sallanjude * Look for a possible callback for a newly added variable
4057580Smjacob * This is called specifically when the variable did not exist in the hash
41235911Smav * previously, so the blanket update did not find this variable.
42235911Smav */
4357580Smjacobvoid env_callback_init(struct env_entry *var_entry)
4457580Smjacob{
4557580Smjacob	const char *var_name = var_entry->key;
46235911Smav	char callback_name[256] = "";
47235911Smav	struct env_clbk_tbl *clbkp;
4857580Smjacob	int ret = 1;
49198934Sdelphij
50198934Sdelphij	if (first_call) {
51293865Sallanjude		callback_list = env_get(ENV_CALLBACK_VAR);
52293865Sallanjude		first_call = 0;
53293865Sallanjude	}
54293865Sallanjude
55293865Sallanjude	var_entry->callback = NULL;
56293865Sallanjude
57293865Sallanjude	/* look in the ".callbacks" var for a reference to this variable */
5857580Smjacob	if (callback_list != NULL)
59198934Sdelphij		ret = env_attr_lookup(callback_list, var_name, callback_name);
6057580Smjacob
6157580Smjacob	/* only if not found there, look in the static list */
6257580Smjacob	if (ret)
6357580Smjacob		ret = env_attr_lookup(ENV_CALLBACK_LIST_STATIC, var_name,
64235911Smav			callback_name);
6557580Smjacob
6657580Smjacob	/* if an association was found, set the callback pointer */
67235911Smav	if (!ret && strlen(callback_name)) {
68235911Smav		clbkp = find_env_callback(callback_name);
6957580Smjacob		if (clbkp != NULL)
70235911Smav			var_entry->callback = clbkp->callback;
71235911Smav	}
7257580Smjacob}
73235911Smav
74235911Smav/*
7557580Smjacob * Called on each existing env var prior to the blanket update since removing
76235911Smav * a callback association should remove its callback.
77235911Smav */
7857580Smjacobstatic int clear_callback(struct env_entry *entry)
79235911Smav{
8057580Smjacob	entry->callback = NULL;
8157580Smjacob
82235911Smav	return 0;
8357580Smjacob}
8457580Smjacob
85235911Smav/*
86242621Smav * Call for each element in the list that associates variables to callbacks
8757580Smjacob */
88235911Smavstatic int set_callback(const char *name, const char *value, void *priv)
89235911Smav{
9057580Smjacob	struct env_entry e, *ep;
91235911Smav	struct env_clbk_tbl *clbkp;
92235911Smav
9357580Smjacob	e.key	= name;
94235911Smav	e.data	= NULL;
95235911Smav	e.callback = NULL;
9657580Smjacob	hsearch_r(e, ENV_FIND, &ep, &env_htab, 0);
97235911Smav
98235911Smav	/* does the env variable actually exist? */
99235911Smav	if (ep != NULL) {
100235911Smav		/* the assocaition delares no callback, so remove the pointer */
10157580Smjacob		if (value == NULL || strlen(value) == 0)
10257580Smjacob			ep->callback = NULL;
103235911Smav		else {
104235911Smav			/* assign the requested callback */
10557580Smjacob			clbkp = find_env_callback(value);
106235911Smav			if (clbkp != NULL)
107222336Smav				ep->callback = clbkp->callback;
108222336Smav		}
109235911Smav	}
110235911Smav
11157580Smjacob	return 0;
112235911Smav}
11357580Smjacob
11457580Smjacobstatic int on_callbacks(const char *name, const char *value, enum env_op op,
115235911Smav	int flags)
11657580Smjacob{
11757580Smjacob	/* remove all callbacks */
118235911Smav	hwalk_r(&env_htab, clear_callback);
11957580Smjacob
12057580Smjacob	/* configure any static callback bindings */
121235911Smav	env_attr_walk(ENV_CALLBACK_LIST_STATIC, set_callback, NULL);
12257580Smjacob	/* configure any dynamic callback bindings */
12357580Smjacob	env_attr_walk(value, set_callback, NULL);
124235911Smav
125235911Smav	return 0;
12657580Smjacob}
127235911SmavU_BOOT_ENV_CALLBACK(callbacks, on_callbacks);
128235911Smav