mail archive of the barebox mailing list
 help / color / mirror / Atom feed
From: Sascha Hauer <s.hauer@pengutronix.de>
To: barebox@lists.infradead.org
Subject: [PATCH 2/3] initial oftree command support
Date: Wed, 24 Aug 2011 22:46:02 +0200	[thread overview]
Message-ID: <1314218763-31058-3-git-send-email-s.hauer@pengutronix.de> (raw)
In-Reply-To: <1314218763-31058-1-git-send-email-s.hauer@pengutronix.de>

This adds basic device tree command support. So far we can
parse a flat device tree (-p), which also stores the tree
in memory, dump it (-d) and free (-f) the internally stored tree.

The chosen node can be updated with barebox bootargs, no other
device tree manipulation is implemented yet.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 commands/Kconfig  |    6 +
 commands/Makefile |    1 +
 commands/oftree.c |  352 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 include/of.h      |    6 +
 lib/Kconfig       |    2 +
 5 files changed, 367 insertions(+), 0 deletions(-)
 create mode 100644 commands/oftree.c
 create mode 100644 include/of.h

diff --git a/commands/Kconfig b/commands/Kconfig
index 39bdb0f..a2f4cd9 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -336,6 +336,12 @@ config CMD_GO
 	tristate
 	prompt "go"
 
+config CMD_OFTREE
+	tristate
+	select OFTREE
+	prompt "oftree"
+	select FDT
+
 endmenu
 
 config CMD_TIMEOUT
diff --git a/commands/Makefile b/commands/Makefile
index 40fb26c..5c51916 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -57,3 +57,4 @@ obj-$(CONFIG_CMD_LED)		+= led.o
 obj-$(CONFIG_CMD_LED_TRIGGER)	+= trigger.o
 obj-$(CONFIG_CMD_USB)		+= usb.o
 obj-$(CONFIG_CMD_TIME)		+= time.o
+obj-$(CONFIG_CMD_OFTREE)	+= oftree.o
diff --git a/commands/oftree.c b/commands/oftree.c
new file mode 100644
index 0000000..786929e
--- /dev/null
+++ b/commands/oftree.c
@@ -0,0 +1,352 @@
+/*
+ * oftree.c - device tree command support
+ *
+ * Copyright (c) 2011 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+ *
+ * based on U-Boot code by:
+ *
+ * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com
+ * Pantelis Antoniou <pantelis.antoniou@gmail.com> and
+ * Matthew McClintock <msm@freescale.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 version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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>
+#include <fdt.h>
+#include <of.h>
+#include <command.h>
+#include <fs.h>
+#include <malloc.h>
+#include <libfdt.h>
+#include <linux/ctype.h>
+#include <asm/byteorder.h>
+#include <errno.h>
+#include <getopt.h>
+
+#define MAX_LEVEL	32		/* how deeply nested we will go */
+
+static int is_printable_string(const void *data, int len)
+{
+	const char *s = data;
+
+	/* zero length is not */
+	if (len == 0)
+		return 0;
+
+	/* must terminate with zero */
+	if (s[len - 1] != '\0')
+		return 0;
+
+	/* printable or a null byte (concatenated strings) */
+	while (((*s == '\0') || isprint(*s)) && (len > 0)) {
+		/*
+		 * If we see a null, there are three possibilities:
+		 * 1) If len == 1, it is the end of the string, printable
+		 * 2) Next character also a null, not printable.
+		 * 3) Next character not a null, continue to check.
+		 */
+		if (s[0] == '\0') {
+			if (len == 1)
+				return 1;
+			if (s[1] == '\0')
+				return 0;
+		}
+		s++;
+		len--;
+	}
+
+	/* Not the null termination, or not done yet: not printable */
+	if (*s != '\0' || (len != 0))
+		return 0;
+
+	return 1;
+}
+
+/*
+ * Print the property in the best format, a heuristic guess.  Print as
+ * a string, concatenated strings, a byte, word, double word, or (if all
+ * else fails) it is printed as a stream of bytes.
+ */
+static void print_data(const void *data, int len)
+{
+	int j;
+
+	/* no data, don't print */
+	if (len == 0)
+		return;
+
+	/*
+	 * It is a string, but it may have multiple strings (embedded '\0's).
+	 */
+	if (is_printable_string(data, len)) {
+		puts("\"");
+		j = 0;
+		while (j < len) {
+			if (j > 0)
+				puts("\", \"");
+			puts(data);
+			j    += strlen(data) + 1;
+			data += strlen(data) + 1;
+		}
+		puts("\"");
+		return;
+	}
+
+	if ((len % 4) == 0) {
+		const u32 *p;
+
+		printf("<");
+		for (j = 0, p = data; j < len/4; j ++)
+			printf("0x%x%s", fdt32_to_cpu(p[j]), j < (len/4 - 1) ? " " : "");
+		printf(">");
+	} else { /* anything else... hexdump */
+		const u8 *s;
+
+		printf("[");
+		for (j = 0, s = data; j < len; j++)
+			printf("%02x%s", s[j], j < len - 1 ? " " : "");
+		printf("]");
+	}
+}
+
+static void printf_indent(int level, const char *fmt, ...)
+{
+	va_list args;
+
+	printf("%*s", level * 8, "");
+
+	va_start (args, fmt);
+	vprintf(fmt, args);
+	va_end (args);
+}
+
+static int fdt_print(struct fdt_header *working_fdt, const char *pathp)
+{
+	const void *nodep;	/* property node pointer */
+	int  nodeoffset;	/* node offset from libfdt */
+	int  nextoffset;	/* next node offset from libfdt */
+	uint32_t tag;		/* tag */
+	int  len;		/* length of the property */
+	int  level = 0;		/* keep track of nesting level */
+	const struct fdt_property *fdt_prop;
+
+	nodeoffset = fdt_path_offset(working_fdt, pathp);
+	if (nodeoffset < 0) {
+		/*
+		 * Not found or something else bad happened.
+		 */
+		printf("libfdt fdt_path_offset() returned %s\n",
+			fdt_strerror(nodeoffset));
+		return 1;
+	}
+
+	while (level >= 0) {
+		tag = fdt_next_tag(working_fdt, nodeoffset, &nextoffset);
+		switch (tag) {
+		case FDT_BEGIN_NODE:
+			pathp = fdt_get_name(working_fdt, nodeoffset, NULL);
+			if (pathp == NULL)
+				pathp = "/* NULL pointer error */";
+			if (*pathp == '\0')
+				pathp = "/";	/* root is nameless */
+			printf_indent(level, "%s {\n",pathp);
+			level++;
+			if (level >= MAX_LEVEL) {
+				printf("Nested too deep, aborting.\n");
+				return 1;
+			}
+			break;
+		case FDT_END_NODE:
+			level--;
+			printf_indent(level, "};\n");
+			if (level == 0) {
+				level = -1;		/* exit the loop */
+			}
+			break;
+		case FDT_PROP:
+			fdt_prop = fdt_offset_ptr(working_fdt, nodeoffset,
+					sizeof(*fdt_prop));
+			pathp    = fdt_string(working_fdt,
+					fdt32_to_cpu(fdt_prop->nameoff));
+			len      = fdt32_to_cpu(fdt_prop->len);
+			nodep    = fdt_prop->data;
+			if (len < 0) {
+				printf("libfdt fdt_getprop(): %s\n",
+					fdt_strerror(len));
+				return 1;
+			} else if (len == 0) {
+				/* the property has no value */
+				printf_indent(level, "%s;\n", pathp);
+			} else {
+				printf_indent(level, "%s = ", pathp);
+				print_data(nodep, len);
+				printf(";\n");
+			}
+			break;
+		case FDT_NOP:
+			printf_indent(level, "/* NOP */\n");
+			break;
+		case FDT_END:
+			return 1;
+		default:
+			printf("Unknown tag 0x%08X\n", tag);
+			return 1;
+		}
+		nodeoffset = nextoffset;
+	}
+	return 0;
+}
+
+static struct fdt_header *barebox_fdt;
+
+static int of_fixup_bootargs(struct fdt_header *fdt)
+{
+	int nodeoffset;
+	const char *str;
+	int err;
+
+	nodeoffset = fdt_path_offset (fdt, "/chosen");
+	if (nodeoffset < 0) {
+		nodeoffset = fdt_add_subnode(fdt, 0, "chosen");
+		if (nodeoffset < 0) {
+			printf("WARNING: could not create /chosen %s.\n",
+				fdt_strerror(nodeoffset));
+                        return -EINVAL;
+                }
+        }
+
+	str = getenv("bootargs");
+	if (str) {
+		err = fdt_setprop(fdt, nodeoffset,
+				"bootargs", str, strlen(str)+1);
+		if (err < 0)
+			printf("WARNING: could not set bootargs %s.\n",
+					fdt_strerror(err));
+        }
+
+	return 0;
+}
+
+struct fdt_header *of_get_fixed_tree(void)
+{
+	int ret;
+
+	if (!barebox_fdt)
+		return NULL;
+
+	ret = of_fixup_bootargs(barebox_fdt);
+	if (ret)
+		return NULL;
+
+	return barebox_fdt;
+}
+
+static int do_oftree(struct command *cmdtp, int argc, char *argv[])
+{
+	struct fdt_header *fdt;
+	int size;
+	int opt;
+	char *file = NULL;
+	const char *node = "/";
+	int dump = 0;
+	int parse = 0;
+
+	while ((opt = getopt(argc, argv, "dpfn:")) > 0) {
+		switch (opt) {
+		case 'd':
+			dump = 1;
+			break;
+		case 'p':
+			parse = 1;
+			break;
+		case 'f':
+			free(barebox_fdt);
+			barebox_fdt = NULL;
+			return 0;
+		case 'n':
+			node = optarg;
+			break;
+		}
+	}
+
+	if (optind < argc)
+		file = argv[optind];
+
+	if (!dump && !parse)
+		return COMMAND_ERROR_USAGE;
+
+	if (dump) {
+		if (file) {
+			fdt = read_file(file, &size);
+			if (!fdt) {
+				printf("unable to read %s\n", file);
+				return 1;
+			}
+
+			fdt_print(fdt, node);
+			free(fdt);
+		} else {
+			if (barebox_fdt) {
+				fdt_print(barebox_fdt, node);
+				return 0;
+			} else {
+				return 1;
+			}
+		}
+		return 0;
+	}
+
+	if (parse) {
+		if (!file)
+			return COMMAND_ERROR_USAGE;
+
+		fdt = read_file(file, &size);
+		if (!fdt) {
+			perror("open");
+			return 1;
+		}
+
+		fdt = xrealloc(fdt, size + 0x8000);
+		fdt_open_into(fdt, fdt, size + 0x8000);
+		if (!fdt) {
+			printf("unable to read %s\n", file);
+			return 1;
+		}
+
+		if (barebox_fdt)
+			free(barebox_fdt);
+
+		barebox_fdt = fdt;
+	}
+
+	return 0;
+}
+
+BAREBOX_CMD_HELP_START(oftree)
+BAREBOX_CMD_HELP_USAGE("oftree [OPTIONS]\n")
+BAREBOX_CMD_HELP_OPT  ("-p <FILE>",  "parse and store oftree from <file>\n")
+BAREBOX_CMD_HELP_OPT  ("-d [FILE]",  "dump oftree from [FILE] or the parsed tree if no file is given\n")
+BAREBOX_CMD_HELP_OPT  ("-f",  "free stored oftree\n")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(oftree)
+	.cmd		= do_oftree,
+	.usage		= "handle oftrees",
+	BAREBOX_CMD_HELP(cmd_oftree_help)
+BAREBOX_CMD_END
diff --git a/include/of.h b/include/of.h
new file mode 100644
index 0000000..bd50a78
--- /dev/null
+++ b/include/of.h
@@ -0,0 +1,6 @@
+#ifndef __OF_H
+#define __OF_H
+
+struct fdt_header *of_get_fixed_tree(void);
+
+#endif /* __OF_H */
diff --git a/lib/Kconfig b/lib/Kconfig
index 532d240..0b6bec4 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -37,3 +37,5 @@ source lib/lzo/Kconfig
 config FDT
 	bool
 
+config OFTREE
+	bool
-- 
1.7.5.4


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

  parent reply	other threads:[~2011-08-24 20:46 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-08-24 20:46 basic devicetree support Sascha Hauer
2011-08-24 20:46 ` [PATCH 1/3] initial libfdt support Sascha Hauer
2011-08-24 20:46 ` Sascha Hauer [this message]
2011-08-24 20:46 ` [PATCH 3/3] ARM Linux: Add device tree support Sascha Hauer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1314218763-31058-3-git-send-email-s.hauer@pengutronix.de \
    --to=s.hauer@pengutronix.de \
    --cc=barebox@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox