10649cd0dSSimon Glass /* 20649cd0dSSimon Glass * (C) Copyright 2012 30649cd0dSSimon Glass * Joe Hershberger, National Instruments, joe.hershberger@ni.com 40649cd0dSSimon Glass * 50649cd0dSSimon Glass * SPDX-License-Identifier: GPL-2.0+ 60649cd0dSSimon Glass */ 70649cd0dSSimon Glass 80649cd0dSSimon Glass #include <common.h> 90649cd0dSSimon Glass #include <environment.h> 100649cd0dSSimon Glass 110649cd0dSSimon Glass #if defined(CONFIG_NEEDS_MANUAL_RELOC) 120649cd0dSSimon Glass DECLARE_GLOBAL_DATA_PTR; 130649cd0dSSimon Glass #endif 140649cd0dSSimon Glass 150649cd0dSSimon Glass /* 160649cd0dSSimon Glass * Look up a callback function pointer by name 170649cd0dSSimon Glass */ 180649cd0dSSimon Glass static struct env_clbk_tbl *find_env_callback(const char *name) 190649cd0dSSimon Glass { 200649cd0dSSimon Glass struct env_clbk_tbl *clbkp; 210649cd0dSSimon Glass int i; 220649cd0dSSimon Glass int num_callbacks = ll_entry_count(struct env_clbk_tbl, env_clbk); 230649cd0dSSimon Glass 240649cd0dSSimon Glass if (name == NULL) 250649cd0dSSimon Glass return NULL; 260649cd0dSSimon Glass 270649cd0dSSimon Glass /* look up the callback in the linker-list */ 280649cd0dSSimon Glass for (i = 0, clbkp = ll_entry_start(struct env_clbk_tbl, env_clbk); 290649cd0dSSimon Glass i < num_callbacks; 300649cd0dSSimon Glass i++, clbkp++) { 310649cd0dSSimon Glass if (strcmp(name, clbkp->name) == 0) 320649cd0dSSimon Glass return clbkp; 330649cd0dSSimon Glass } 340649cd0dSSimon Glass 350649cd0dSSimon Glass return NULL; 360649cd0dSSimon Glass } 370649cd0dSSimon Glass 380649cd0dSSimon Glass static int first_call = 1; 390649cd0dSSimon Glass static const char *callback_list; 400649cd0dSSimon Glass 410649cd0dSSimon Glass /* 420649cd0dSSimon Glass * Look for a possible callback for a newly added variable 430649cd0dSSimon Glass * This is called specifically when the variable did not exist in the hash 440649cd0dSSimon Glass * previously, so the blanket update did not find this variable. 450649cd0dSSimon Glass */ 460649cd0dSSimon Glass void env_callback_init(ENTRY *var_entry) 470649cd0dSSimon Glass { 480649cd0dSSimon Glass const char *var_name = var_entry->key; 490649cd0dSSimon Glass char callback_name[256] = ""; 500649cd0dSSimon Glass struct env_clbk_tbl *clbkp; 510649cd0dSSimon Glass int ret = 1; 520649cd0dSSimon Glass 530649cd0dSSimon Glass if (first_call) { 54*00caae6dSSimon Glass callback_list = env_get(ENV_CALLBACK_VAR); 550649cd0dSSimon Glass first_call = 0; 560649cd0dSSimon Glass } 570649cd0dSSimon Glass 580649cd0dSSimon Glass /* look in the ".callbacks" var for a reference to this variable */ 590649cd0dSSimon Glass if (callback_list != NULL) 600649cd0dSSimon Glass ret = env_attr_lookup(callback_list, var_name, callback_name); 610649cd0dSSimon Glass 620649cd0dSSimon Glass /* only if not found there, look in the static list */ 630649cd0dSSimon Glass if (ret) 640649cd0dSSimon Glass ret = env_attr_lookup(ENV_CALLBACK_LIST_STATIC, var_name, 650649cd0dSSimon Glass callback_name); 660649cd0dSSimon Glass 670649cd0dSSimon Glass /* if an association was found, set the callback pointer */ 680649cd0dSSimon Glass if (!ret && strlen(callback_name)) { 690649cd0dSSimon Glass clbkp = find_env_callback(callback_name); 700649cd0dSSimon Glass if (clbkp != NULL) 710649cd0dSSimon Glass #if defined(CONFIG_NEEDS_MANUAL_RELOC) 720649cd0dSSimon Glass var_entry->callback = clbkp->callback + gd->reloc_off; 730649cd0dSSimon Glass #else 740649cd0dSSimon Glass var_entry->callback = clbkp->callback; 750649cd0dSSimon Glass #endif 760649cd0dSSimon Glass } 770649cd0dSSimon Glass } 780649cd0dSSimon Glass 790649cd0dSSimon Glass /* 800649cd0dSSimon Glass * Called on each existing env var prior to the blanket update since removing 810649cd0dSSimon Glass * a callback association should remove its callback. 820649cd0dSSimon Glass */ 830649cd0dSSimon Glass static int clear_callback(ENTRY *entry) 840649cd0dSSimon Glass { 850649cd0dSSimon Glass entry->callback = NULL; 860649cd0dSSimon Glass 870649cd0dSSimon Glass return 0; 880649cd0dSSimon Glass } 890649cd0dSSimon Glass 900649cd0dSSimon Glass /* 910649cd0dSSimon Glass * Call for each element in the list that associates variables to callbacks 920649cd0dSSimon Glass */ 930649cd0dSSimon Glass static int set_callback(const char *name, const char *value, void *priv) 940649cd0dSSimon Glass { 950649cd0dSSimon Glass ENTRY e, *ep; 960649cd0dSSimon Glass struct env_clbk_tbl *clbkp; 970649cd0dSSimon Glass 980649cd0dSSimon Glass e.key = name; 990649cd0dSSimon Glass e.data = NULL; 1000649cd0dSSimon Glass e.callback = NULL; 1010649cd0dSSimon Glass hsearch_r(e, FIND, &ep, &env_htab, 0); 1020649cd0dSSimon Glass 1030649cd0dSSimon Glass /* does the env variable actually exist? */ 1040649cd0dSSimon Glass if (ep != NULL) { 1050649cd0dSSimon Glass /* the assocaition delares no callback, so remove the pointer */ 1060649cd0dSSimon Glass if (value == NULL || strlen(value) == 0) 1070649cd0dSSimon Glass ep->callback = NULL; 1080649cd0dSSimon Glass else { 1090649cd0dSSimon Glass /* assign the requested callback */ 1100649cd0dSSimon Glass clbkp = find_env_callback(value); 1110649cd0dSSimon Glass if (clbkp != NULL) 1120649cd0dSSimon Glass #if defined(CONFIG_NEEDS_MANUAL_RELOC) 1130649cd0dSSimon Glass ep->callback = clbkp->callback + gd->reloc_off; 1140649cd0dSSimon Glass #else 1150649cd0dSSimon Glass ep->callback = clbkp->callback; 1160649cd0dSSimon Glass #endif 1170649cd0dSSimon Glass } 1180649cd0dSSimon Glass } 1190649cd0dSSimon Glass 1200649cd0dSSimon Glass return 0; 1210649cd0dSSimon Glass } 1220649cd0dSSimon Glass 1230649cd0dSSimon Glass static int on_callbacks(const char *name, const char *value, enum env_op op, 1240649cd0dSSimon Glass int flags) 1250649cd0dSSimon Glass { 1260649cd0dSSimon Glass /* remove all callbacks */ 1270649cd0dSSimon Glass hwalk_r(&env_htab, clear_callback); 1280649cd0dSSimon Glass 1290649cd0dSSimon Glass /* configure any static callback bindings */ 1300649cd0dSSimon Glass env_attr_walk(ENV_CALLBACK_LIST_STATIC, set_callback, NULL); 1310649cd0dSSimon Glass /* configure any dynamic callback bindings */ 1320649cd0dSSimon Glass env_attr_walk(value, set_callback, NULL); 1330649cd0dSSimon Glass 1340649cd0dSSimon Glass return 0; 1350649cd0dSSimon Glass } 1360649cd0dSSimon Glass U_BOOT_ENV_CALLBACK(callbacks, on_callbacks); 137