1/*opyright (c) 2011 The Chromium OS Authors. All rights reserved.
2 *
3 *  * Portions from U-Boot cmd_fdt.c (C) Copyright 2007
4 *   * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com
5 *    * Based on code written by:
6 *     *   Pantelis Antoniou <pantelis.antoniou@gmail.com> and
7 *      *   Matthew McClintock <msm@freescale.com>
8 *       *
9 *        * This program is free software; you can redistribute it and/or
10 *         * modify it under the terms of the GNU General Public License as
11 *          * published by the Free Software Foundation; either version 2 of
12 *           * the License, or (at your option) any later version.
13 *            *
14 *             * This program is distributed in the hope that it will be useful,
15 *              * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *               * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 *                * GNU General Public License for more details.
18 *                 *
19 *                  * You should have received a copy of the GNU General Public License
20 *                   * along with this program; if not, write to the Free Software
21 *                    * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
22 *                     * MA 02111-1307 USA
23 *                      */
24
25#include <assert.h>
26#include <ctype.h>
27#include <getopt.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31
32#include <libfdt.h>
33
34//#include "util.h"
35#include <ctype.h>
36
37
38#define FALIGN(x, a)    (((x) + ((a) - 1)) & ~((a) - 1))
39#define PALIGN(p, a)    ((void *)(FALIGN((unsigned long)(p), (a))))
40#define GET_CELL(p)     (p += 4, *((const uint32_t *)(p-4)))
41
42
43static void report_error(const char *where, int err)
44{
45	fprintf(stderr, "Error at '%s': %s\n", where, fdt_strerror(err));
46}
47
48int util_is_printable_string(const void *data, int len)
49{
50	const char *s = data;
51	const char *ss, *se;
52
53	/* zero length is not */
54	if (len == 0)
55		return 0;
56
57	/* must terminate with zero */
58	if (s[len - 1] != '\0')
59		return 0;
60
61	se = s + len;
62
63	while (s < se) {
64		ss = s;
65		while (s < se && *s && isprint((unsigned char)*s))
66			s++;
67
68		/* not zero, or not done yet */
69		if (*s != '\0' || s == ss)
70			return 0;
71
72		s++;
73	}
74
75	return 1;
76}
77
78
79/**
80 *  * Displays data of a given length according to selected options
81 *   *
82 *    * If a specific data type is provided in disp, then this is used. Otherwise
83 *     * we try to guess the data type / size from the contents.
84 *      *
85 *       * @param disp		Display information / options
86 *        * @param data		Data to display
87 *         * @param len		Maximum length of buffer
88 *          * @return 0 if ok, -1 if data does not match format
89 *           */
90int show_data(struct display_info *disp, const char *data, int len)
91{
92	int i, size;
93	const uint8_t *p = (const uint8_t *)data;
94	const char *s;
95	int value;
96	int is_string;
97	char fmt[3];
98
99	/* no data, don't print */
100	if (len == 0)
101		return 0;
102
103	is_string = (disp->type) == 's' ||
104		(!disp->type && util_is_printable_string(data, len));
105	if (is_string) {
106		if (data[len - 1] != '\0') {
107			fprintf(stderr, "Unterminated string\n");
108			return -1;
109		}
110		for (s = data; s - data < len; s += strlen(s) + 1) {
111			if (s != data)
112				printf(" ");
113			printf("%s", (const char *)s);
114		}
115		return 0;
116	}
117	size = disp->size;
118	if (size == -1) {
119		size = (len % 4) == 0 ? 4 : 1;
120	} else if (len % size) {
121		fprintf(stderr, "Property length must be a multiple of "
122				"selected data size\n");
123		return -1;
124	}
125	fmt[0] = '%';
126	fmt[1] = disp->type ? disp->type : 'x';
127	fmt[2] = '\0';
128	for (i = 0; i < len; i += size, p += size) {
129		if (i)
130			printf(" ");
131		value = size == 4 ? fdt32_to_cpu(*(const uint32_t *)p) :
132			size == 2 ? (*p << 8) | p[1] : *p;
133		//printf(" Sid in libfdtget\n" );
134		printf(fmt, value);
135	}
136	return 0;
137}
138
139/**
140 *  * List all properties in a node, one per line.
141 *   *
142 *    * @param blob		FDT blob
143 *     * @param node		Node to display
144 *      * @return 0 if ok, or FDT_ERR... if not.
145 *       */
146int list_properties(const void *blob, int node)
147{
148	const struct fdt_property *data;
149	const char *name;
150	int prop;
151
152	prop = fdt_first_property_offset(blob, node);
153	do {
154		/* Stop silently when there are no more properties */
155		if (prop < 0)
156			return prop == -FDT_ERR_NOTFOUND ? 0 : prop;
157		data = fdt_get_property_by_offset(blob, prop, NULL);
158		name = fdt_string(blob, fdt32_to_cpu(data->nameoff));
159		if (name)
160			puts(name);
161		prop = fdt_next_property_offset(blob, prop);
162	} while (1);
163}
164
165#define MAX_LEVEL	32		/* how deeply nested we will go */
166
167/**
168 *  * List all subnodes in a node, one per line
169 *   *
170 *    * @param blob		FDT blob
171 *     * @param node		Node to display
172 *      * @return 0 if ok, or FDT_ERR... if not.
173 *       */
174int list_subnodes(const void *blob, int node)
175{
176	int nextoffset;		/* next node offset from libfdt */
177	uint32_t tag;		/* current tag */
178	int level = 0;		/* keep track of nesting level */
179	const char *pathp;
180	int depth = 1;		/* the assumed depth of this node */
181
182	while (level >= 0) {
183		tag = fdt_next_tag(blob, node, &nextoffset);
184		switch (tag) {
185			case FDT_BEGIN_NODE:
186				pathp = fdt_get_name(blob, node, NULL);
187				if (level <= depth) {
188					if (pathp == NULL)
189						pathp = "/* NULL pointer error */";
190					if (*pathp == '\0')
191						pathp = "/";	/* root is nameless */
192					if (level == 1)
193						puts(pathp);
194				}
195				level++;
196				if (level >= MAX_LEVEL) {
197					printf("Nested too deep, aborting.\n");
198					return 1;
199				}
200				break;
201			case FDT_END_NODE:
202				level--;
203				if (level == 0)
204					level = -1;		/* exit the loop */
205				break;
206			case FDT_END:
207				return 1;
208			case FDT_PROP:
209				break;
210			default:
211				if (level <= depth)
212					printf("Unknown tag 0x%08X\n", tag);
213				return 1;
214		}
215		node = nextoffset;
216	}
217	return 0;
218}
219
220/**
221 *  * Show the data for a given node (and perhaps property) according to the
222 *   * display option provided.
223 *    *
224 *     * @param blob		FDT blob
225 *      * @param disp		Display information / options
226 *       * @param node		Node to display
227 *        * @param property	Name of property to display, or NULL if none
228 *         * @return 0 if ok, -ve on error
229 *          */
230int show_data_for_item(const void *blob, struct display_info *disp,
231		int node, const char *property)
232{
233	const void *value = NULL;
234	int len, err = 0;
235
236	switch (disp->mode) {
237		case MODE_LIST_PROPS:
238			err = list_properties(blob, node);
239			break;
240
241		case MODE_LIST_SUBNODES:
242			err = list_subnodes(blob, node);
243			break;
244
245		default:
246			assert(property);
247			value = fdt_getprop(blob, node, property, &len);
248			if (value) {
249				if (show_data(disp, value, len))
250					err = -1;
251				else
252					printf("\n");
253			} else if (disp->default_val) {
254				puts(disp->default_val);
255			} else {
256				report_error(property, len);
257				err = -1;
258			}
259			break;
260	}
261
262	return err;
263}
264
265/**
266 *  * Run the main fdtget operation, given a filename and valid arguments
267 *   *
268 *    * @param disp		Display information / options
269 *     * @param filename	Filename of blob file
270 *      * @param arg		List of arguments to process
271 *       * @param arg_count	Number of arguments
272 *        * @param return 0 if ok, -ve on error
273 *         */
274int do_fdtget(struct display_info *disp, const char *filename,
275		char **arg, int arg_count, int args_per_step)
276{
277	char *blob;
278	const char *prop;
279	int i, node;
280
281	//blob = utilfdt_read(filename);
282	blob = NULL;
283	if (!blob)
284		return -1;
285
286	for (i = 0; i + args_per_step <= arg_count; i += args_per_step) {
287		node = fdt_path_offset(blob, arg[i]);
288		if (node < 0) {
289			if (disp->default_val) {
290				puts(disp->default_val);
291				continue;
292			} else {
293				report_error(arg[i], node);
294				return -1;
295			}
296		}
297		prop = args_per_step == 1 ? NULL : arg[i + 1];
298
299		if (show_data_for_item(blob, disp, node, prop))
300			return -1;
301	}
302	return 0;
303}
304
305static const char *usage_msg =
306"fdtget - read values from device tree\n"
307"\n"
308"Each value is printed on a new line.\n\n"
309"Usage:\n"
310"	fdtget <options> <dt file> [<node> <property>]...\n"
311"	fdtget -p <options> <dt file> [<node> ]...\n"
312"Options:\n"
313"\t-t <type>\tType of data\n"
314"\t-p\t\tList properties for each node\n"
315"\t-l\t\tList subnodes for each node\n"
316"\t-d\t\tDefault value to display when the property is "
317"missing\n"
318"\t-h\t\tPrint this help\n\n"
319;
320static void usage(const char *msg)
321{
322	if (msg)
323		fprintf(stderr, "Error: %s\n\n", msg);
324
325	fprintf(stderr, "%s", usage_msg);
326	exit(2);
327}
328
329int test_main(int argc, char *argv[])
330{
331	char *filename = NULL;
332	struct display_info disp;
333	int args_per_step = 2;
334
335	/* set defaults */
336	memset(&disp, '\0', sizeof(disp));
337	disp.size = -1;
338	disp.mode = MODE_SHOW_VALUE;
339	for (;;) {
340		int c = getopt(argc, argv, "d:hlpt:");
341		if (c == -1)
342			break;
343
344		switch (c) {
345			case 'h':
346			case '?':
347				usage(NULL);
348
349			case 't':
350				/*if (utilfdt_decode_type(optarg, &disp.type,
351							&disp.size))
352					usage("Invalid type string");*/
353				break;
354
355			case 'p':
356				disp.mode = MODE_LIST_PROPS;
357				args_per_step = 1;
358				break;
359
360			case 'l':
361				disp.mode = MODE_LIST_SUBNODES;
362				args_per_step = 1;
363				break;
364
365			case 'd':
366				disp.default_val = optarg;
367				break;
368		}
369	}
370
371	if (optind < argc)
372		filename = argv[optind++];
373	if (!filename)
374		usage("Missing filename");
375
376	argv += optind;
377	argc -= optind;
378
379	/* Allow no arguments, and silently succeed */
380	if (!argc)
381		return 0;
382
383	/* Check for node, property arguments */
384	if (args_per_step == 2 && (argc % 2))
385		usage("Must have an even number of arguments");
386
387	if (do_fdtget(&disp, filename, argv, argc, args_per_step))
388		return 1;
389	return 0;
390}
391
392