part/dev_desc: Add log2 of blocksize to block_dev_desc data struct
[kernel/u-boot.git] / common / env_callback.c
1 /*
2  * (C) Copyright 2012
3  * Joe Hershberger, National Instruments, joe.hershberger@ni.com
4  *
5  * See file CREDITS for list of people who contributed to this
6  * project.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  * MA 02111-1307 USA
22  */
23
24 #include <common.h>
25 #include <environment.h>
26
27 #if defined(CONFIG_NEEDS_MANUAL_RELOC)
28 DECLARE_GLOBAL_DATA_PTR;
29 #endif
30
31 /*
32  * Look up a callback function pointer by name
33  */
34 static struct env_clbk_tbl *find_env_callback(const char *name)
35 {
36         struct env_clbk_tbl *clbkp;
37         int i;
38         int num_callbacks = ll_entry_count(struct env_clbk_tbl, env_clbk);
39
40         if (name == NULL)
41                 return NULL;
42
43         /* look up the callback in the linker-list */
44         for (i = 0, clbkp = ll_entry_start(struct env_clbk_tbl, env_clbk);
45              i < num_callbacks;
46              i++, clbkp++) {
47                 if (strcmp(name, clbkp->name) == 0)
48                         return clbkp;
49         }
50
51         return NULL;
52 }
53
54 /*
55  * Look for a possible callback for a newly added variable
56  * This is called specifically when the variable did not exist in the hash
57  * previously, so the blanket update did not find this variable.
58  */
59 void env_callback_init(ENTRY *var_entry)
60 {
61         const char *var_name = var_entry->key;
62         const char *callback_list = getenv(ENV_CALLBACK_VAR);
63         char callback_name[256] = "";
64         struct env_clbk_tbl *clbkp;
65         int ret = 1;
66
67         /* look in the ".callbacks" var for a reference to this variable */
68         if (callback_list != NULL)
69                 ret = env_attr_lookup(callback_list, var_name, callback_name);
70
71         /* only if not found there, look in the static list */
72         if (ret)
73                 ret = env_attr_lookup(ENV_CALLBACK_LIST_STATIC, var_name,
74                         callback_name);
75
76         /* if an association was found, set the callback pointer */
77         if (!ret && strlen(callback_name)) {
78                 clbkp = find_env_callback(callback_name);
79                 if (clbkp != NULL)
80 #if defined(CONFIG_NEEDS_MANUAL_RELOC)
81                         var_entry->callback = clbkp->callback + gd->reloc_off;
82 #else
83                         var_entry->callback = clbkp->callback;
84 #endif
85         }
86 }
87
88 /*
89  * Called on each existing env var prior to the blanket update since removing
90  * a callback association should remove its callback.
91  */
92 static int clear_callback(ENTRY *entry)
93 {
94         entry->callback = NULL;
95
96         return 0;
97 }
98
99 /*
100  * Call for each element in the list that associates variables to callbacks
101  */
102 static int set_callback(const char *name, const char *value)
103 {
104         ENTRY e, *ep;
105         struct env_clbk_tbl *clbkp;
106
107         e.key   = name;
108         e.data  = NULL;
109         hsearch_r(e, FIND, &ep, &env_htab, 0);
110
111         /* does the env variable actually exist? */
112         if (ep != NULL) {
113                 /* the assocaition delares no callback, so remove the pointer */
114                 if (value == NULL || strlen(value) == 0)
115                         ep->callback = NULL;
116                 else {
117                         /* assign the requested callback */
118                         clbkp = find_env_callback(value);
119                         if (clbkp != NULL)
120 #if defined(CONFIG_NEEDS_MANUAL_RELOC)
121                                 ep->callback = clbkp->callback + gd->reloc_off;
122 #else
123                                 ep->callback = clbkp->callback;
124 #endif
125                 }
126         }
127
128         return 0;
129 }
130
131 static int on_callbacks(const char *name, const char *value, enum env_op op,
132         int flags)
133 {
134         /* remove all callbacks */
135         hwalk_r(&env_htab, clear_callback);
136
137         /* configure any static callback bindings */
138         env_attr_walk(ENV_CALLBACK_LIST_STATIC, set_callback);
139         /* configure any dynamic callback bindings */
140         env_attr_walk(value, set_callback);
141
142         return 0;
143 }
144 U_BOOT_ENV_CALLBACK(callbacks, on_callbacks);