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