[RFC PATCH 2/2] Add support for YAML output

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]



DO NOT MERGE! This is prototype code and the YAML encoding has not been
finalized yet.

Signed-off-by: Grant Likely <grant.likely@xxxxxxx>
---
 Documentation/manual.txt |   2 +
 Makefile                 |   1 +
 Makefile.dtc             |   1 +
 dtc.c                    |   5 +
 dtc.h                    |   4 +
 yamltree.c               | 355 +++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 368 insertions(+)
 create mode 100644 yamltree.c

diff --git a/Documentation/manual.txt b/Documentation/manual.txt
index 72403ac..5bedf05 100644
--- a/Documentation/manual.txt
+++ b/Documentation/manual.txt
@@ -78,6 +78,8 @@ The currently supported Output Formats are:
         then simply be added to your Makefile.  Additionally, the
         assembly file exports some symbols that can be used.
 
+     - "yaml": DT encoded in YAML
+
 
 3) Command Line
 
diff --git a/Makefile b/Makefile
index e1e138a..a1db301 100644
--- a/Makefile
+++ b/Makefile
@@ -35,6 +35,7 @@ INCLUDEDIR = $(PREFIX)/include
 HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
 	    sed -e 's/\(cygwin\|msys\).*/\1/')
 
+LDFLAGS = -L/usr/local/lib -lyaml
 ifeq ($(HOSTOS),darwin)
 SHAREDLIB_EXT     = dylib
 SHAREDLIB_CFLAGS  = -fPIC
diff --git a/Makefile.dtc b/Makefile.dtc
index bece49b..f97239d 100644
--- a/Makefile.dtc
+++ b/Makefile.dtc
@@ -8,6 +8,7 @@ DTC_SRCS = \
 	data.c \
 	dtc.c \
 	flattree.c \
+	yamltree.c \
 	fstree.c \
 	livetree.c \
 	srcpos.c \
diff --git a/dtc.c b/dtc.c
index c36994e..4926757 100644
--- a/dtc.c
+++ b/dtc.c
@@ -95,6 +95,7 @@ static const char * const usage_opts_help[] = {
 	"\n\tOutput formats are:\n"
 	 "\t\tdts - device tree source text\n"
 	 "\t\tdtb - device tree blob\n"
+	 "\t\tyaml - device tree encoded as YAML\n"
 	 "\t\tasm - assembler source",
 	"\n\tBlob version to produce, defaults to "stringify(DEFAULT_FDT_VERSION)" (for dtb and asm output)",
 	"\n\tOutput dependency file",
@@ -128,6 +129,8 @@ static const char *guess_type_by_name(const char *fname, const char *fallback)
 		return fallback;
 	if (!strcasecmp(s, ".dts"))
 		return "dts";
+	if (!strcasecmp(s, ".yaml"))
+		return "yaml";
 	if (!strcasecmp(s, ".dtb"))
 		return "dtb";
 	return fallback;
@@ -350,6 +353,8 @@ int main(int argc, char *argv[])
 
 	if (streq(outform, "dts")) {
 		dt_to_source(outf, dti);
+	} else if (streq(outform, "yaml")) {
+		dt_to_yaml(outf, dti);
 	} else if (streq(outform, "dtb")) {
 		dt_to_blob(outf, dti, outversion);
 	} else if (streq(outform, "asm")) {
diff --git a/dtc.h b/dtc.h
index 32a7655..8db4814 100644
--- a/dtc.h
+++ b/dtc.h
@@ -295,6 +295,10 @@ struct dt_info *dt_from_blob(const char *fname);
 void dt_to_source(FILE *f, struct dt_info *dti);
 struct dt_info *dt_from_source(const char *f);
 
+/* YAML source */
+
+void dt_to_yaml(FILE *f, struct dt_info *dti);
+
 /* FS trees */
 
 struct dt_info *dt_from_fs(const char *dirname);
diff --git a/yamltree.c b/yamltree.c
new file mode 100644
index 0000000..cfdc9f3
--- /dev/null
+++ b/yamltree.c
@@ -0,0 +1,355 @@
+/*
+ * (C) Copyright Arm Holdings.  2017
+ * (C) Copyright David Gibson <dwg@xxxxxxxxxxx>, IBM Corporation.  2005.
+ *
+ * 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 <stdlib.h>
+#include <yaml.h>
+#include "dtc.h"
+#include "srcpos.h"
+
+char *yaml_error_name[] = {
+	[YAML_NO_ERROR] = "no error",
+	[YAML_MEMORY_ERROR] = "memory error",
+	[YAML_READER_ERROR] = "reader error",
+	[YAML_SCANNER_ERROR] = "scanner error",
+	[YAML_PARSER_ERROR] = "parser error",
+	[YAML_COMPOSER_ERROR] = "composer error",
+	[YAML_WRITER_ERROR] = "writer error",
+	[YAML_EMITTER_ERROR] = "emitter error",
+};
+
+#define yaml_die(emitter) die("yaml '%s': %s in %s, line %i", yaml_error_name[(emitter)->error], (emitter)->problem, __func__, __LINE__)
+
+static void yaml_propval_ref(yaml_emitter_t *emitter, struct node *np, char *hint, bool phandle)
+{
+	yaml_event_t event;
+	struct label *l;
+	struct node *root = np;
+	char *ref = NULL;
+
+	while (root->parent)
+		root = root->parent;
+
+	/* If possible, use the hint string as the reference */
+	if (hint && np == get_node_by_ref(root, hint))
+		ref = hint;
+
+	/* Check if the node has a usable label */
+	if (!ref) {
+		for_each_label(np->labels, l) {
+			ref = l->label;
+			break;
+		}
+	}
+
+	if (!ref)
+		ref = np->fullpath;
+
+	yaml_scalar_event_initialize(&event, NULL,
+		(yaml_char_t *)(phandle ? "!phandle" : "!path"), (yaml_char_t*)ref,
+		strlen(ref), 0, 0, YAML_DOUBLE_QUOTED_SCALAR_STYLE);
+	if (!yaml_emitter_emit(emitter, &event))
+		yaml_die(emitter);
+}
+
+static void yaml_propval_int(yaml_emitter_t *emitter, const char *p, int len, int width)
+{
+	yaml_event_t event;
+	const char *end = p + len;
+	char buf[32];
+
+	assert(len % width == 0);
+	for (; p < end; p += width) {
+		switch(width) {
+		case 1:
+			sprintf(buf, "0x%"PRIx8, *(const uint8_t*)p);
+			break;
+		case 2:
+			sprintf(buf, "0x%"PRIx16, fdt16_to_cpu(*(const fdt16_t*)p));
+			break;
+		case 4:
+			sprintf(buf, "0x%"PRIx32, fdt32_to_cpu(*(const fdt32_t*)p));
+			break;
+		case 8:
+			sprintf(buf, "0x%"PRIx64, fdt64_to_cpu(*(const fdt64_t*)p));
+			break;
+		}
+
+		yaml_scalar_event_initialize(&event, NULL,
+			(yaml_char_t*)YAML_INT_TAG, (yaml_char_t *)buf,
+			strlen(buf), 1, 1, YAML_PLAIN_SCALAR_STYLE);
+		if (!yaml_emitter_emit(emitter, &event))
+			yaml_die(emitter);
+	}
+}
+
+static void yaml_propval_string(yaml_emitter_t *emitter, char *str, int len)
+{
+	yaml_event_t event;
+	int i;
+
+	assert(str[len-1] == '\0');
+
+	/* Make sure the entire string is in the lower 7-bit ascii range */
+	for (i = 0; i < len; i++) {
+		if (!isascii(str[i])) {
+			fprintf(stderr, "Warning: non-ASCII character(s) in property string\n");
+			yaml_propval_int(emitter, str, len, 1);
+			return;
+		}
+	}
+
+	yaml_scalar_event_initialize(&event, NULL,
+		(yaml_char_t *)YAML_STR_TAG, (yaml_char_t*)str,
+		len-1, 0, 1, YAML_DOUBLE_QUOTED_SCALAR_STYLE);
+	if (!yaml_emitter_emit(emitter, &event))
+		yaml_die(emitter);
+}
+
+static char *group_tags[NUM_MARKERS] = {
+	[MARKER_BLOB] = "!u8",
+	[MARKER_UINT8] = "!u8",
+	[MARKER_UINT16] = "!u16",
+	[MARKER_UINT32] = YAML_SEQ_TAG,
+	[MARKER_UINT16] = "!u64",
+};
+
+static void yaml_propval(yaml_emitter_t *emitter, struct node *root, struct property *prop)
+{
+	yaml_event_t event;
+	int len = prop->val.len;
+	struct marker *m = prop->val.markers;
+	struct marker dummy_marker = {
+		.offset = 0, .type = MARKER_NONE, .next = m, .ref = NULL
+	};
+	enum markertype emit_type = MARKER_NONE;
+	struct node *np;
+	cell_t phandle;
+	unsigned int nesting = 0;
+
+	/* Emit the property name */
+	yaml_scalar_event_initialize(&event, NULL,
+		(yaml_char_t *)YAML_STR_TAG, (yaml_char_t*)prop->name,
+		strlen(prop->name), 1, 1, YAML_PLAIN_SCALAR_STYLE);
+	if (!yaml_emitter_emit(emitter, &event))
+		yaml_die(emitter);
+
+	/* Boolean properties are easiest to deal with. Length is zero, so just emit 'true' */
+	if (len == 0) {
+		yaml_scalar_event_initialize(&event, NULL,
+			(yaml_char_t *)YAML_BOOL_TAG,
+			(yaml_char_t*)"true",
+			strlen("true"), 1, 0, YAML_PLAIN_SCALAR_STYLE);
+		if (!yaml_emitter_emit(emitter, &event))
+			yaml_die(emitter);
+		return;
+	}
+
+	dummy_marker.type = guess_propval_type(prop);
+	if (dummy_marker.type != MARKER_NONE)
+		m = &dummy_marker;
+
+	yaml_sequence_start_event_initialize(&event, NULL,
+		(yaml_char_t*)YAML_SEQ_TAG, 1, YAML_FLOW_SEQUENCE_STYLE);
+	if (!yaml_emitter_emit(emitter, &event))
+		yaml_die(emitter);
+	nesting++;
+
+	for_each_marker(m) {
+		size_t chunk_len = (m->next ? m->next->offset : len) - m->offset;
+		char *p = &prop->val.val[m->offset];
+
+		switch (m->type) {
+		case MARKER_NONE:
+			if (!nesting)
+				break;
+			yaml_sequence_end_event_initialize(&event);
+			if (!yaml_emitter_emit(emitter, &event))
+				yaml_die(emitter);
+			emit_type = MARKER_NONE;
+			nesting--;
+			break;
+		case MARKER_STRING:
+			emit_type = m->type;
+			break;
+		case MARKER_BLOB:
+		case MARKER_UINT8:
+		case MARKER_UINT16:
+		case MARKER_UINT32:
+		case MARKER_UINT64:
+			emit_type = m->type;
+			yaml_sequence_start_event_initialize(&event, NULL,
+				(yaml_char_t*)group_tags[emit_type], m->type == MARKER_UINT32, YAML_FLOW_SEQUENCE_STYLE);
+			if (!yaml_emitter_emit(emitter, &event))
+				yaml_die(emitter);
+			nesting++;
+			break;
+		case REF_PHANDLE:
+			assert(emit_type == MARKER_UINT32);
+			assert(chunk_len >= sizeof(fdt32_t));
+			phandle = fdt32_to_cpu(*(const fdt32_t*)p);
+			np = get_node_by_phandle(root, phandle);
+			if (np) {
+				yaml_propval_ref(emitter, np, NULL, true);
+				chunk_len -= sizeof(fdt32_t);
+				p += sizeof(fdt32_t);
+			}
+			break;
+		case REF_PATH:
+			assert(emit_type == MARKER_NONE);
+			assert(strnlen(p, chunk_len) == chunk_len - 1);
+			np = get_node_by_ref(root, p);
+			if (np) {
+				yaml_propval_ref(emitter, np, m->ref, false);
+				p += chunk_len;
+				chunk_len = 0;
+			}
+			break;
+		default:
+			break;
+		}
+
+		if (chunk_len == 0)
+			continue;
+
+		switch (emit_type) {
+			case MARKER_BLOB:
+			case MARKER_UINT8:
+				yaml_propval_int(emitter, p, chunk_len, 1);
+				break;
+			case MARKER_UINT16:
+				yaml_propval_int(emitter, p, chunk_len, 2);
+				break;
+			case MARKER_UINT32:
+				yaml_propval_int(emitter, p, chunk_len, 4);
+				break;
+			case MARKER_UINT64:
+				yaml_propval_int(emitter, p, chunk_len, 8);
+				break;
+			case MARKER_STRING:
+				yaml_propval_string(emitter, p, chunk_len);
+				break;
+			default:
+				break;
+		}
+	}
+
+	while (nesting) {
+		yaml_sequence_end_event_initialize(&event);
+		if (!yaml_emitter_emit(emitter, &event))
+			yaml_die(emitter);
+		nesting--;
+	}
+}
+
+
+static void yaml_tree(struct node *root, struct node *tree, yaml_emitter_t *emitter)
+{
+	struct property *prop;
+	struct node *child;
+	struct label *l;
+	yaml_event_t event;
+
+	if (tree->deleted)
+		return;
+
+	yaml_mapping_start_event_initialize(&event, NULL,
+		(yaml_char_t *)YAML_MAP_TAG, 1, YAML_ANY_MAPPING_STYLE);
+	if (!yaml_emitter_emit(emitter, &event))
+		yaml_die(emitter);
+
+	if (tree->labels) {
+		yaml_scalar_event_initialize(&event, NULL,
+			(yaml_char_t *)YAML_STR_TAG, (yaml_char_t*)"$labels",
+			strlen("$labels"), 1, 0, YAML_PLAIN_SCALAR_STYLE);
+		if (!yaml_emitter_emit(emitter, &event))
+			yaml_die(emitter);
+
+		yaml_sequence_start_event_initialize(&event, NULL,
+			(yaml_char_t*)YAML_SEQ_TAG, 1, YAML_FLOW_SEQUENCE_STYLE);
+		if (!yaml_emitter_emit(emitter, &event))
+			yaml_die(emitter);
+
+		for_each_label(tree->labels, l) {
+			yaml_scalar_event_initialize(&event, NULL,
+				(yaml_char_t *)YAML_STR_TAG, (yaml_char_t*)l->label,
+				strlen(l->label), 1, 0, YAML_PLAIN_SCALAR_STYLE);
+			if (!yaml_emitter_emit(emitter, &event))
+				yaml_die(emitter);
+		}
+
+		yaml_sequence_end_event_initialize(&event);
+		if (!yaml_emitter_emit(emitter, &event))
+			yaml_die(emitter);
+	}
+
+	for_each_property(tree, prop)
+		yaml_propval(emitter, root, prop);
+
+	/* Loop over all the children, emitting them into the map */
+	for_each_child(tree, child) {
+		yaml_scalar_event_initialize(&event, NULL,
+			(yaml_char_t *)YAML_STR_TAG, (yaml_char_t*)child->name,
+			strlen(child->name), 1, 0, YAML_PLAIN_SCALAR_STYLE);
+		if (!yaml_emitter_emit(emitter, &event))
+			yaml_die(emitter);
+		yaml_tree(root, child, emitter);
+	}
+
+	yaml_mapping_end_event_initialize(&event);
+	if (!yaml_emitter_emit(emitter, &event))
+		yaml_die(emitter);
+}
+
+void dt_to_yaml(FILE *f, struct dt_info *dti)
+{
+	yaml_emitter_t emitter;
+	yaml_event_t event;
+
+	yaml_emitter_initialize(&emitter);
+	yaml_emitter_set_output_file(&emitter, f);
+	yaml_stream_start_event_initialize(&event, YAML_UTF8_ENCODING);
+	if (!yaml_emitter_emit(&emitter, &event))
+		yaml_die(&emitter);
+
+	yaml_document_start_event_initialize(&event, NULL, NULL, NULL, 0);
+	if (!yaml_emitter_emit(&emitter, &event))
+		yaml_die(&emitter);
+
+	yaml_sequence_start_event_initialize(&event, NULL, (yaml_char_t *)YAML_SEQ_TAG, 1, YAML_ANY_SEQUENCE_STYLE);
+	if (!yaml_emitter_emit(&emitter, &event))
+		yaml_die(&emitter);
+
+	yaml_tree(dti->dt, dti->dt, &emitter);
+
+	yaml_sequence_end_event_initialize(&event);
+	if (!yaml_emitter_emit(&emitter, &event))
+		yaml_die(&emitter);
+
+	yaml_document_end_event_initialize(&event, 0);
+	if (!yaml_emitter_emit(&emitter, &event))
+		yaml_die(&emitter);
+
+	yaml_stream_end_event_initialize(&event);
+	if (!yaml_emitter_emit(&emitter, &event))
+		yaml_die(&emitter);
+
+	yaml_emitter_delete(&emitter);
+}
+
-- 
2.11.0

--
To unsubscribe from this list: send the line "unsubscribe devicetree-spec" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Device Tree]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux Audio Users]     [Photos]     [Yosemite Photos]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]

  Powered by Linux