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