1*0649cd0dSSimon Glass /* 2*0649cd0dSSimon Glass * (C) Copyright 2012 3*0649cd0dSSimon Glass * Joe Hershberger, National Instruments, joe.hershberger@ni.com 4*0649cd0dSSimon Glass * 5*0649cd0dSSimon Glass * SPDX-License-Identifier: GPL-2.0+ 6*0649cd0dSSimon Glass */ 7*0649cd0dSSimon Glass 8*0649cd0dSSimon Glass #include <common.h> 9*0649cd0dSSimon Glass #include <environment.h> 10*0649cd0dSSimon Glass 11*0649cd0dSSimon Glass #if defined(CONFIG_NEEDS_MANUAL_RELOC) 12*0649cd0dSSimon Glass DECLARE_GLOBAL_DATA_PTR; 13*0649cd0dSSimon Glass #endif 14*0649cd0dSSimon Glass 15*0649cd0dSSimon Glass /* 16*0649cd0dSSimon Glass * Look up a callback function pointer by name 17*0649cd0dSSimon Glass */ 18*0649cd0dSSimon Glass static struct env_clbk_tbl *find_env_callback(const char *name) 19*0649cd0dSSimon Glass { 20*0649cd0dSSimon Glass struct env_clbk_tbl *clbkp; 21*0649cd0dSSimon Glass int i; 22*0649cd0dSSimon Glass int num_callbacks = ll_entry_count(struct env_clbk_tbl, env_clbk); 23*0649cd0dSSimon Glass 24*0649cd0dSSimon Glass if (name == NULL) 25*0649cd0dSSimon Glass return NULL; 26*0649cd0dSSimon Glass 27*0649cd0dSSimon Glass /* look up the callback in the linker-list */ 28*0649cd0dSSimon Glass for (i = 0, clbkp = ll_entry_start(struct env_clbk_tbl, env_clbk); 29*0649cd0dSSimon Glass i < num_callbacks; 30*0649cd0dSSimon Glass i++, clbkp++) { 31*0649cd0dSSimon Glass if (strcmp(name, clbkp->name) == 0) 32*0649cd0dSSimon Glass return clbkp; 33*0649cd0dSSimon Glass } 34*0649cd0dSSimon Glass 35*0649cd0dSSimon Glass return NULL; 36*0649cd0dSSimon Glass } 37*0649cd0dSSimon Glass 38*0649cd0dSSimon Glass static int first_call = 1; 39*0649cd0dSSimon Glass static const char *callback_list; 40*0649cd0dSSimon Glass 41*0649cd0dSSimon Glass /* 42*0649cd0dSSimon Glass * Look for a possible callback for a newly added variable 43*0649cd0dSSimon Glass * This is called specifically when the variable did not exist in the hash 44*0649cd0dSSimon Glass * previously, so the blanket update did not find this variable. 45*0649cd0dSSimon Glass */ 46*0649cd0dSSimon Glass void env_callback_init(ENTRY *var_entry) 47*0649cd0dSSimon Glass { 48*0649cd0dSSimon Glass const char *var_name = var_entry->key; 49*0649cd0dSSimon Glass char callback_name[256] = ""; 50*0649cd0dSSimon Glass struct env_clbk_tbl *clbkp; 51*0649cd0dSSimon Glass int ret = 1; 52*0649cd0dSSimon Glass 53*0649cd0dSSimon Glass if (first_call) { 54*0649cd0dSSimon Glass callback_list = getenv(ENV_CALLBACK_VAR); 55*0649cd0dSSimon Glass first_call = 0; 56*0649cd0dSSimon Glass } 57*0649cd0dSSimon Glass 58*0649cd0dSSimon Glass /* look in the ".callbacks" var for a reference to this variable */ 59*0649cd0dSSimon Glass if (callback_list != NULL) 60*0649cd0dSSimon Glass ret = env_attr_lookup(callback_list, var_name, callback_name); 61*0649cd0dSSimon Glass 62*0649cd0dSSimon Glass /* only if not found there, look in the static list */ 63*0649cd0dSSimon Glass if (ret) 64*0649cd0dSSimon Glass ret = env_attr_lookup(ENV_CALLBACK_LIST_STATIC, var_name, 65*0649cd0dSSimon Glass callback_name); 66*0649cd0dSSimon Glass 67*0649cd0dSSimon Glass /* if an association was found, set the callback pointer */ 68*0649cd0dSSimon Glass if (!ret && strlen(callback_name)) { 69*0649cd0dSSimon Glass clbkp = find_env_callback(callback_name); 70*0649cd0dSSimon Glass if (clbkp != NULL) 71*0649cd0dSSimon Glass #if defined(CONFIG_NEEDS_MANUAL_RELOC) 72*0649cd0dSSimon Glass var_entry->callback = clbkp->callback + gd->reloc_off; 73*0649cd0dSSimon Glass #else 74*0649cd0dSSimon Glass var_entry->callback = clbkp->callback; 75*0649cd0dSSimon Glass #endif 76*0649cd0dSSimon Glass } 77*0649cd0dSSimon Glass } 78*0649cd0dSSimon Glass 79*0649cd0dSSimon Glass /* 80*0649cd0dSSimon Glass * Called on each existing env var prior to the blanket update since removing 81*0649cd0dSSimon Glass * a callback association should remove its callback. 82*0649cd0dSSimon Glass */ 83*0649cd0dSSimon Glass static int clear_callback(ENTRY *entry) 84*0649cd0dSSimon Glass { 85*0649cd0dSSimon Glass entry->callback = NULL; 86*0649cd0dSSimon Glass 87*0649cd0dSSimon Glass return 0; 88*0649cd0dSSimon Glass } 89*0649cd0dSSimon Glass 90*0649cd0dSSimon Glass /* 91*0649cd0dSSimon Glass * Call for each element in the list that associates variables to callbacks 92*0649cd0dSSimon Glass */ 93*0649cd0dSSimon Glass static int set_callback(const char *name, const char *value, void *priv) 94*0649cd0dSSimon Glass { 95*0649cd0dSSimon Glass ENTRY e, *ep; 96*0649cd0dSSimon Glass struct env_clbk_tbl *clbkp; 97*0649cd0dSSimon Glass 98*0649cd0dSSimon Glass e.key = name; 99*0649cd0dSSimon Glass e.data = NULL; 100*0649cd0dSSimon Glass e.callback = NULL; 101*0649cd0dSSimon Glass hsearch_r(e, FIND, &ep, &env_htab, 0); 102*0649cd0dSSimon Glass 103*0649cd0dSSimon Glass /* does the env variable actually exist? */ 104*0649cd0dSSimon Glass if (ep != NULL) { 105*0649cd0dSSimon Glass /* the assocaition delares no callback, so remove the pointer */ 106*0649cd0dSSimon Glass if (value == NULL || strlen(value) == 0) 107*0649cd0dSSimon Glass ep->callback = NULL; 108*0649cd0dSSimon Glass else { 109*0649cd0dSSimon Glass /* assign the requested callback */ 110*0649cd0dSSimon Glass clbkp = find_env_callback(value); 111*0649cd0dSSimon Glass if (clbkp != NULL) 112*0649cd0dSSimon Glass #if defined(CONFIG_NEEDS_MANUAL_RELOC) 113*0649cd0dSSimon Glass ep->callback = clbkp->callback + gd->reloc_off; 114*0649cd0dSSimon Glass #else 115*0649cd0dSSimon Glass ep->callback = clbkp->callback; 116*0649cd0dSSimon Glass #endif 117*0649cd0dSSimon Glass } 118*0649cd0dSSimon Glass } 119*0649cd0dSSimon Glass 120*0649cd0dSSimon Glass return 0; 121*0649cd0dSSimon Glass } 122*0649cd0dSSimon Glass 123*0649cd0dSSimon Glass static int on_callbacks(const char *name, const char *value, enum env_op op, 124*0649cd0dSSimon Glass int flags) 125*0649cd0dSSimon Glass { 126*0649cd0dSSimon Glass /* remove all callbacks */ 127*0649cd0dSSimon Glass hwalk_r(&env_htab, clear_callback); 128*0649cd0dSSimon Glass 129*0649cd0dSSimon Glass /* configure any static callback bindings */ 130*0649cd0dSSimon Glass env_attr_walk(ENV_CALLBACK_LIST_STATIC, set_callback, NULL); 131*0649cd0dSSimon Glass /* configure any dynamic callback bindings */ 132*0649cd0dSSimon Glass env_attr_walk(value, set_callback, NULL); 133*0649cd0dSSimon Glass 134*0649cd0dSSimon Glass return 0; 135*0649cd0dSSimon Glass } 136*0649cd0dSSimon Glass U_BOOT_ENV_CALLBACK(callbacks, on_callbacks); 137