diff options
Diffstat (limited to 'common')
| -rw-r--r-- | common/Makefile | 4 | ||||
| -rw-r--r-- | common/env_attr.c | 222 | ||||
| -rw-r--r-- | common/env_callback.c | 144 | 
3 files changed, 370 insertions, 0 deletions
| diff --git a/common/Makefile b/common/Makefile index ce3035937..04812e9b2 100644 --- a/common/Makefile +++ b/common/Makefile @@ -44,6 +44,8 @@ COBJS-y += cmd_nvedit.o  COBJS-y += cmd_version.o  # environment +COBJS-y += env_attr.o +COBJS-y += env_callback.o  COBJS-y += env_common.o  COBJS-$(CONFIG_ENV_IS_IN_DATAFLASH) += env_dataflash.o  COBJS-$(CONFIG_ENV_IS_IN_EEPROM) += env_eeprom.o @@ -207,6 +209,8 @@ COBJS-y += env_common.o  COBJS-$(CONFIG_ENV_IS_IN_FLASH) += env_flash.o  COBJS-$(CONFIG_SPL_YMODEM_SUPPORT) += xyzModem.o  COBJS-$(CONFIG_SPL_NET_SUPPORT) += cmd_nvedit.o +COBJS-$(CONFIG_SPL_NET_SUPPORT) += env_attr.o +COBJS-$(CONFIG_SPL_NET_SUPPORT) += env_callback.o  COBJS-$(CONFIG_SPL_NET_SUPPORT) += env_common.o  COBJS-$(CONFIG_SPL_NET_SUPPORT) += env_nowhere.o  COBJS-$(CONFIG_SPL_NET_SUPPORT) += miiphyutil.o diff --git a/common/env_attr.c b/common/env_attr.c new file mode 100644 index 000000000..7d330a58b --- /dev/null +++ b/common/env_attr.c @@ -0,0 +1,222 @@ +/* + * (C) Copyright 2012 + * Joe Hershberger, National Instruments, joe.hershberger@ni.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <env_attr.h> +#include <errno.h> +#include <linux/string.h> +#include <malloc.h> + +/* + * Iterate through the whole list calling the callback for each found element. + * "attr_list" takes the form: + *	attributes = [^,:\s]* + *	entry = name[:attributes] + *	list = entry[,list] + */ +int env_attr_walk(const char *attr_list, +	int (*callback)(const char *name, const char *attributes)) +{ +	const char *entry, *entry_end; +	char *name, *attributes; + +	if (!attr_list) +		/* list not found */ +		return 1; + +	entry = attr_list; +	do { +		char *entry_cpy = NULL; + +		entry_end = strchr(entry, ENV_ATTR_LIST_DELIM); +		/* check if this is the last entry in the list */ +		if (entry_end == NULL) { +			int entry_len = strlen(entry); + +			if (entry_len) { +				/* +				 * allocate memory to copy the entry into since +				 * we will need to inject '\0' chars and squash +				 * white-space before calling the callback +				 */ +				entry_cpy = malloc(entry_len + 1); +				if (entry_cpy) +					/* copy the rest of the list */ +					strcpy(entry_cpy, entry); +				else +					return -ENOMEM; +			} +		} else { +			int entry_len = entry_end - entry; + +			if (entry_len) { +				/* +				 * allocate memory to copy the entry into since +				 * we will need to inject '\0' chars and squash +				 * white-space before calling the callback +				 */ +				entry_cpy = malloc(entry_len + 1); +				if (entry_cpy) { +					/* copy just this entry and null term */ +					strncpy(entry_cpy, entry, entry_len); +					entry_cpy[entry_len] = '\0'; +				} else +					return -ENOMEM; +			} +		} + +		/* check if there is anything to process (e.g. not ",,,") */ +		if (entry_cpy != NULL) { +			attributes = strchr(entry_cpy, ENV_ATTR_SEP); +			/* check if there is a ':' */ +			if (attributes != NULL) { +				/* replace the ':' with '\0' to term name */ +				*attributes++ = '\0'; +				/* remove white-space from attributes */ +				attributes = strim(attributes); +			} +			/* remove white-space from name */ +			name = strim(entry_cpy); + +			/* only call the callback if there is a name */ +			if (strlen(name) != 0) { +				int retval = 0; + +				retval = callback(name, attributes); +				if (retval) { +					free(entry_cpy); +					return retval; +				} +			} +		} + +		free(entry_cpy); +		entry = entry_end + 1; +	} while (entry_end != NULL); + +	return 0; +} + +/* + * Search for the last matching string in another string with the option to + * start looking at a certain point (i.e. ignore anything beyond that point). + */ +static char *reverse_strstr(const char *searched, const char *search_for, +	const char *searched_start) +{ +	char *result = NULL; + +	if (*search_for == '\0') +		return (char *)searched; + +	for (;;) { +		char *match = strstr(searched, search_for); + +		/* +		 * Stop looking if no new match is found or looking past the +		 * searched_start pointer +		 */ +		if (match == NULL || (searched_start != NULL && +		    match + strlen(search_for) > searched_start)) +			break; + +		result = match; +		searched = match + 1; +	} + +	return result; +} + +/* + * Retrieve the attributes string associated with a single name in the list + * There is no protection on attributes being too small for the value + */ +int env_attr_lookup(const char *attr_list, const char *name, char *attributes) +{ +	const char *entry = NULL; + +	if (!attributes) +		/* bad parameter */ +		return -1; +	if (!attr_list) +		/* list not found */ +		return 1; + +	entry = reverse_strstr(attr_list, name, NULL); +	while (entry != NULL) { +		const char *prevch = entry - 1; +		const char *nextch = entry + strlen(name); + +		/* Skip spaces */ +		while (*prevch == ' ') +			prevch--; +		while (*nextch == ' ') +			nextch++; + +		/* check for an exact match */ +		if ((entry == attr_list || +		     *prevch == ENV_ATTR_LIST_DELIM) && +		    (*nextch == ENV_ATTR_SEP || +		     *nextch == ENV_ATTR_LIST_DELIM || +		     *nextch == '\0')) +			break; + +		entry = reverse_strstr(attr_list, name, entry); +	} +	if (entry != NULL) { +		int len; + +		/* skip the name */ +		entry += strlen(name); +		/* skip spaces */ +		while (*entry == ' ') +			entry++; +		if (*entry != ENV_ATTR_SEP) +			len = 0; +		else { +			const char *delim; +			static const char delims[] = { +				ENV_ATTR_LIST_DELIM, ' ', '\0'}; + +			/* skip the attr sep */ +			entry += 1; +			/* skip spaces */ +			while (*entry == ' ') +				entry++; + +			delim = strpbrk(entry, delims); +			if (delim == NULL) +				len = strlen(entry); +			else +				len = delim - entry; +			memcpy(attributes, entry, len); +		} +		attributes[len] = '\0'; + +		/* success */ +		return 0; +	} + +	/* not found in list */ +	return 2; +} diff --git a/common/env_callback.c b/common/env_callback.c new file mode 100644 index 000000000..78ca3674f --- /dev/null +++ b/common/env_callback.c @@ -0,0 +1,144 @@ +/* + * (C) Copyright 2012 + * Joe Hershberger, National Instruments, joe.hershberger@ni.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <environment.h> + +#if defined(CONFIG_NEEDS_MANUAL_RELOC) +DECLARE_GLOBAL_DATA_PTR; +#endif + +/* + * Look up a callback function pointer by name + */ +struct env_clbk_tbl *find_env_callback(const char *name) +{ +	struct env_clbk_tbl *clbkp; +	int i; +	int num_callbacks = ll_entry_count(struct env_clbk_tbl, env_clbk); + +	if (name == NULL) +		return NULL; + +	/* look up the callback in the linker-list */ +	for (i = 0, clbkp = ll_entry_start(struct env_clbk_tbl, env_clbk); +	     i < num_callbacks; +	     i++, clbkp++) { +		if (strcmp(name, clbkp->name) == 0) +			return clbkp; +	} + +	return NULL; +} + +/* + * Look for a possible callback for a newly added variable + * This is called specifically when the variable did not exist in the hash + * previously, so the blanket update did not find this variable. + */ +void env_callback_init(ENTRY *var_entry) +{ +	const char *var_name = var_entry->key; +	const char *callback_list = getenv(ENV_CALLBACK_VAR); +	char callback_name[256] = ""; +	struct env_clbk_tbl *clbkp; +	int ret = 1; + +	/* look in the ".callbacks" var for a reference to this variable */ +	if (callback_list != NULL) +		ret = env_attr_lookup(callback_list, var_name, callback_name); + +	/* only if not found there, look in the static list */ +	if (ret) +		ret = env_attr_lookup(ENV_CALLBACK_LIST_STATIC, var_name, +			callback_name); + +	/* if an association was found, set the callback pointer */ +	if (!ret && strlen(callback_name)) { +		clbkp = find_env_callback(callback_name); +		if (clbkp != NULL) +#if defined(CONFIG_NEEDS_MANUAL_RELOC) +			var_entry->callback = clbkp->callback + gd->reloc_off; +#else +			var_entry->callback = clbkp->callback; +#endif +	} +} + +/* + * Called on each existing env var prior to the blanket update since removing + * a callback association should remove its callback. + */ +static int clear_callback(ENTRY *entry) +{ +	entry->callback = NULL; + +	return 0; +} + +/* + * Call for each element in the list that associates variables to callbacks + */ +static int set_callback(const char *name, const char *value) +{ +	ENTRY e, *ep; +	struct env_clbk_tbl *clbkp; + +	e.key	= name; +	e.data	= NULL; +	hsearch_r(e, FIND, &ep, &env_htab, 0); + +	/* does the env variable actually exist? */ +	if (ep != NULL) { +		/* the assocaition delares no callback, so remove the pointer */ +		if (value == NULL || strlen(value) == 0) +			ep->callback = NULL; +		else { +			/* assign the requested callback */ +			clbkp = find_env_callback(value); +			if (clbkp != NULL) +#if defined(CONFIG_NEEDS_MANUAL_RELOC) +				ep->callback = clbkp->callback + gd->reloc_off; +#else +				ep->callback = clbkp->callback; +#endif +		} +	} + +	return 0; +} + +static int on_callbacks(const char *name, const char *value, enum env_op op, +	int flags) +{ +	/* remove all callbacks */ +	hwalk_r(&env_htab, clear_callback); + +	/* configure any static callback bindings */ +	env_attr_walk(ENV_CALLBACK_LIST_STATIC, set_callback); +	/* configure any dynamic callback bindings */ +	env_attr_walk(value, set_callback); + +	return 0; +} +U_BOOT_ENV_CALLBACK(callbacks, on_callbacks); |