Home | History | Annotate | Download | only in common
      1 // SPDX-License-Identifier: GPL-2.0+
      2 /*
      3  * An inteface for configuring a hardware via u-boot environment.
      4  *
      5  * Copyright (c) 2009  MontaVista Software, Inc.
      6  * Copyright 2011 Freescale Semiconductor, Inc.
      7  *
      8  * Author: Anton Vorontsov <avorontsov (at) ru.mvista.com>
      9  */
     10 
     11 #ifndef HWCONFIG_TEST
     12 #include <config.h>
     13 #include <common.h>
     14 #include <exports.h>
     15 #include <hwconfig.h>
     16 #include <linux/types.h>
     17 #include <linux/string.h>
     18 #else
     19 #include <stdio.h>
     20 #include <stdlib.h>
     21 #include <string.h>
     22 #include <assert.h>
     23 #define min(a, b) (((a) < (b)) ? (a) : (b))
     24 #endif /* HWCONFIG_TEST */
     25 
     26 DECLARE_GLOBAL_DATA_PTR;
     27 
     28 static const char *hwconfig_parse(const char *opts, size_t maxlen,
     29 				  const char *opt, char *stopchs, char eqch,
     30 				  size_t *arglen)
     31 {
     32 	size_t optlen = strlen(opt);
     33 	char *str;
     34 	const char *start = opts;
     35 	const char *end;
     36 
     37 next:
     38 	str = strstr(opts, opt);
     39 	end = str + optlen;
     40 	if (end - start > maxlen)
     41 		return NULL;
     42 
     43 	if (str && (str == opts || strpbrk(str - 1, stopchs) == str - 1) &&
     44 			(strpbrk(end, stopchs) == end || *end == eqch ||
     45 			 *end == '\0')) {
     46 		const char *arg_end;
     47 
     48 		if (!arglen)
     49 			return str;
     50 
     51 		if (*end != eqch)
     52 			return NULL;
     53 
     54 		arg_end = strpbrk(str, stopchs);
     55 		if (!arg_end)
     56 			*arglen = min(maxlen, strlen(str)) - optlen - 1;
     57 		else
     58 			*arglen = arg_end - end - 1;
     59 
     60 		return end + 1;
     61 	} else if (str) {
     62 		opts = end;
     63 		goto next;
     64 	}
     65 	return NULL;
     66 }
     67 
     68 const char cpu_hwconfig[] __attribute__((weak)) = "";
     69 const char board_hwconfig[] __attribute__((weak)) = "";
     70 
     71 static const char *__hwconfig(const char *opt, size_t *arglen,
     72 			      const char *env_hwconfig)
     73 {
     74 	const char *ret;
     75 
     76 	/* if we are passed a buffer use it, otherwise try the environment */
     77 	if (!env_hwconfig) {
     78 		if (!(gd->flags & GD_FLG_ENV_READY)) {
     79 			printf("WARNING: Calling __hwconfig without a buffer "
     80 					"and before environment is ready\n");
     81 			return NULL;
     82 		}
     83 		env_hwconfig = env_get("hwconfig");
     84 	}
     85 
     86 	if (env_hwconfig) {
     87 		ret = hwconfig_parse(env_hwconfig, strlen(env_hwconfig),
     88 				      opt, ";", ':', arglen);
     89 		if (ret)
     90 			return ret;
     91 	}
     92 
     93 	ret = hwconfig_parse(board_hwconfig, strlen(board_hwconfig),
     94 			opt, ";", ':', arglen);
     95 	if (ret)
     96 		return ret;
     97 
     98 	return hwconfig_parse(cpu_hwconfig, strlen(cpu_hwconfig),
     99 			opt, ";", ':', arglen);
    100 }
    101 
    102 /*
    103  * hwconfig_f - query if a particular hwconfig option is specified
    104  * @opt:	a string representing an option
    105  * @buf:	if non-NULL use this buffer to parse, otherwise try env
    106  *
    107  * This call can be used to find out whether U-Boot should configure
    108  * a particular hardware option.
    109  *
    110  * Returns non-zero value if the hardware option can be used and thus
    111  * should be configured, 0 otherwise.
    112  *
    113  * This function also returns non-zero value if CONFIG_HWCONFIG is
    114  * undefined.
    115  *
    116  * Returning non-zero value without CONFIG_HWCONFIG has its crucial
    117  * purpose: the hwconfig() call should be a "transparent" interface,
    118  * e.g. if a board doesn't need hwconfig facility, then we assume
    119  * that the board file only calls things that are actually used, so
    120  * hwconfig() will always return true result.
    121  */
    122 int hwconfig_f(const char *opt, char *buf)
    123 {
    124 	return !!__hwconfig(opt, NULL, buf);
    125 }
    126 
    127 /*
    128  * hwconfig_arg_f - get hwconfig option's argument
    129  * @opt:	a string representing an option
    130  * @arglen:	a pointer to an allocated size_t variable
    131  * @buf:	if non-NULL use this buffer to parse, otherwise try env
    132  *
    133  * Unlike hwconfig_f() function, this function returns a pointer to the
    134  * start of the hwconfig arguments, if option is not found or it has
    135  * no specified arguments, the function returns NULL pointer.
    136  *
    137  * If CONFIG_HWCONFIG is undefined, the function returns "", and
    138  * arglen is set to 0.
    139  */
    140 const char *hwconfig_arg_f(const char *opt, size_t *arglen, char *buf)
    141 {
    142 	return __hwconfig(opt, arglen, buf);
    143 }
    144 
    145 /*
    146  * hwconfig_arg_cmp_f - compare hwconfig option's argument
    147  * @opt:	a string representing an option
    148  * @arg:	a string for comparing an option's argument
    149  * @buf:	if non-NULL use this buffer to parse, otherwise try env
    150  *
    151  * This call is similar to hwconfig_arg_f, but instead of returning
    152  * hwconfig argument and its length, it is comparing it to @arg.
    153  *
    154  * Returns non-zero value if @arg matches, 0 otherwise.
    155  *
    156  * If CONFIG_HWCONFIG is undefined, the function returns a non-zero
    157  * value, i.e. the argument matches.
    158  */
    159 int hwconfig_arg_cmp_f(const char *opt, const char *arg, char *buf)
    160 {
    161 	const char *argstr;
    162 	size_t arglen;
    163 
    164 	argstr = hwconfig_arg_f(opt, &arglen, buf);
    165 	if (!argstr || arglen != strlen(arg))
    166 		return 0;
    167 
    168 	return !strncmp(argstr, arg, arglen);
    169 }
    170 
    171 /*
    172  * hwconfig_sub_f - query if a particular hwconfig sub-option is specified
    173  * @opt:	a string representing an option
    174  * @subopt:	a string representing a sub-option
    175  * @buf:	if non-NULL use this buffer to parse, otherwise try env
    176  *
    177  * This call is similar to hwconfig_f(), except that it takes additional
    178  * argument @subopt. In this example:
    179  * 	"dr_usb:mode=peripheral"
    180  * "dr_usb" is an option, "mode" is a sub-option, and "peripheral" is its
    181  * argument.
    182  */
    183 int hwconfig_sub_f(const char *opt, const char *subopt, char *buf)
    184 {
    185 	size_t arglen;
    186 	const char *arg;
    187 
    188 	arg = __hwconfig(opt, &arglen, buf);
    189 	if (!arg)
    190 		return 0;
    191 	return !!hwconfig_parse(arg, arglen, subopt, ",;", '=', NULL);
    192 }
    193 
    194 /*
    195  * hwconfig_subarg_f - get hwconfig sub-option's argument
    196  * @opt:	a string representing an option
    197  * @subopt:	a string representing a sub-option
    198  * @subarglen:	a pointer to an allocated size_t variable
    199  * @buf:	if non-NULL use this buffer to parse, otherwise try env
    200  *
    201  * This call is similar to hwconfig_arg_f(), except that it takes an
    202  * additional argument @subopt, and so works with sub-options.
    203  */
    204 const char *hwconfig_subarg_f(const char *opt, const char *subopt,
    205 			      size_t *subarglen, char *buf)
    206 {
    207 	size_t arglen;
    208 	const char *arg;
    209 
    210 	arg = __hwconfig(opt, &arglen, buf);
    211 	if (!arg)
    212 		return NULL;
    213 	return hwconfig_parse(arg, arglen, subopt, ",;", '=', subarglen);
    214 }
    215 
    216 /*
    217  * hwconfig_arg_cmp_f - compare hwconfig sub-option's argument
    218  * @opt:	a string representing an option
    219  * @subopt:	a string representing a sub-option
    220  * @subarg:	a string for comparing an sub-option's argument
    221  * @buf:	if non-NULL use this buffer to parse, otherwise try env
    222  *
    223  * This call is similar to hwconfig_arg_cmp_f, except that it takes an
    224  * additional argument @subopt, and so works with sub-options.
    225  */
    226 int hwconfig_subarg_cmp_f(const char *opt, const char *subopt,
    227 			  const char *subarg, char *buf)
    228 {
    229 	const char *argstr;
    230 	size_t arglen;
    231 
    232 	argstr = hwconfig_subarg_f(opt, subopt, &arglen, buf);
    233 	if (!argstr || arglen != strlen(subarg))
    234 		return 0;
    235 
    236 	return !strncmp(argstr, subarg, arglen);
    237 }
    238 
    239 #ifdef HWCONFIG_TEST
    240 int main()
    241 {
    242 	const char *ret;
    243 	size_t len;
    244 
    245 	env_set("hwconfig", "key1:subkey1=value1,subkey2=value2;key2:value3;;;;"
    246 			   "key3;:,:=;key4", 1);
    247 
    248 	ret = hwconfig_arg("key1", &len);
    249 	printf("%zd %.*s\n", len, (int)len, ret);
    250 	assert(len == 29);
    251 	assert(hwconfig_arg_cmp("key1", "subkey1=value1,subkey2=value2"));
    252 	assert(!strncmp(ret, "subkey1=value1,subkey2=value2", len));
    253 
    254 	ret = hwconfig_subarg("key1", "subkey1", &len);
    255 	printf("%zd %.*s\n", len, (int)len, ret);
    256 	assert(len == 6);
    257 	assert(hwconfig_subarg_cmp("key1", "subkey1", "value1"));
    258 	assert(!strncmp(ret, "value1", len));
    259 
    260 	ret = hwconfig_subarg("key1", "subkey2", &len);
    261 	printf("%zd %.*s\n", len, (int)len, ret);
    262 	assert(len == 6);
    263 	assert(hwconfig_subarg_cmp("key1", "subkey2", "value2"));
    264 	assert(!strncmp(ret, "value2", len));
    265 
    266 	ret = hwconfig_arg("key2", &len);
    267 	printf("%zd %.*s\n", len, (int)len, ret);
    268 	assert(len == 6);
    269 	assert(hwconfig_arg_cmp("key2", "value3"));
    270 	assert(!strncmp(ret, "value3", len));
    271 
    272 	assert(hwconfig("key3"));
    273 	assert(hwconfig_arg("key4", &len) == NULL);
    274 	assert(hwconfig_arg("bogus", &len) == NULL);
    275 
    276 	unenv_set("hwconfig");
    277 
    278 	assert(hwconfig(NULL) == 0);
    279 	assert(hwconfig("") == 0);
    280 	assert(hwconfig("key3") == 0);
    281 
    282 	return 0;
    283 }
    284 #endif /* HWCONFIG_TEST */
    285