diff mbox

[04/10] OF: Utility helper functions for dynamic nodes

Message ID 1406159064-21366-5-git-send-email-grant.likely@linaro.org
State Accepted
Commit 698433963b98d6de7b102c242805c99fda4fa1fb
Headers show

Commit Message

Grant Likely July 23, 2014, 11:44 p.m. UTC
From: Pantelis Antoniou <pantelis.antoniou@konsulko.com>

Introduce helper functions for working with the live DT tree,
all of them related to dynamically adding/removing nodes and
properties.

__of_prop_dup() copies a property dynamically
__of_node_alloc() creates an empty node

Bug fix about prop->len == 0 by Ionut Nicu <ioan.nicu.ext@nsn.com>

Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
[glikely: Added unittest for of_copy_property and dropped fine-grained allocations]
[glikely: removed name, type and phandle arguments from __of_node_alloc]
Signed-off-by: Grant Likely <grant.likely@linaro.org>
---
 drivers/of/dynamic.c    | 77 +++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/of/of_private.h | 10 +++++++
 drivers/of/selftest.c   | 28 ++++++++++++++++++
 3 files changed, 115 insertions(+)
diff mbox

Patch

diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c
index 125994330437..e0c4c6e25980 100644
--- a/drivers/of/dynamic.c
+++ b/drivers/of/dynamic.c
@@ -214,3 +214,80 @@  void of_node_release(struct kobject *kobj)
 	kfree(node->data);
 	kfree(node);
 }
+
+/**
+ * __of_prop_dup - Copy a property dynamically.
+ * @prop:	Property to copy
+ * @allocflags:	Allocation flags (typically pass GFP_KERNEL)
+ *
+ * Copy a property by dynamically allocating the memory of both the
+ * property stucture and the property name & contents. The property's
+ * flags have the OF_DYNAMIC bit set so that we can differentiate between
+ * dynamically allocated properties and not.
+ * Returns the newly allocated property or NULL on out of memory error.
+ */
+struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags)
+{
+	struct property *new;
+
+	new = kzalloc(sizeof(*new), allocflags);
+	if (!new)
+		return NULL;
+
+	/*
+	 * NOTE: There is no check for zero length value.
+	 * In case of a boolean property This will allocate a value
+	 * of zero bytes. We do this to work around the use
+	 * of of_get_property() calls on boolean values.
+	 */
+	new->name = kstrdup(prop->name, allocflags);
+	new->value = kmemdup(prop->value, prop->length, allocflags);
+	new->length = prop->length;
+	if (!new->name || !new->value)
+		goto err_free;
+
+	/* mark the property as dynamic */
+	of_property_set_flag(new, OF_DYNAMIC);
+
+	return new;
+
+ err_free:
+	kfree(new->name);
+	kfree(new->value);
+	kfree(new);
+	return NULL;
+}
+
+/**
+ * __of_node_alloc() - Create an empty device node dynamically.
+ * @full_name:	Full name of the new device node
+ * @allocflags:	Allocation flags (typically pass GFP_KERNEL)
+ *
+ * Create an empty device tree node, suitable for further modification.
+ * The node data are dynamically allocated and all the node flags
+ * have the OF_DYNAMIC & OF_DETACHED bits set.
+ * Returns the newly allocated node or NULL on out of memory error.
+ */
+struct device_node *__of_node_alloc(const char *full_name, gfp_t allocflags)
+{
+	struct device_node *node;
+
+	node = kzalloc(sizeof(*node), allocflags);
+	if (!node)
+		return NULL;
+
+	node->full_name = kstrdup(full_name, allocflags);
+	of_node_set_flag(node, OF_DYNAMIC);
+	of_node_set_flag(node, OF_DETACHED);
+	if (!node->full_name)
+		goto err_free;
+
+	of_node_init(node);
+
+	return node;
+
+ err_free:
+	kfree(node->full_name);
+	kfree(node);
+	return NULL;
+}
diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h
index c270f2037779..1799ed2b3808 100644
--- a/drivers/of/of_private.h
+++ b/drivers/of/of_private.h
@@ -51,4 +51,14 @@  static inline int of_property_notify(int action, struct device_node *np,
 }
 #endif /* CONFIG_OF_DYNAMIC */
 
+/**
+ * General utilities for working with live trees.
+ *
+ * All functions with two leading underscores operate
+ * without taking node references, so you either have to
+ * own the devtree lock or work on detached trees only.
+ */
+struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags);
+struct device_node *__of_node_alloc(const char *full_name, gfp_t allocflags);
+
 #endif /* _LINUX_OF_PRIVATE_H */
diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c
index 077314eebb95..ee2166f0f36a 100644
--- a/drivers/of/selftest.c
+++ b/drivers/of/selftest.c
@@ -16,6 +16,8 @@ 
 #include <linux/slab.h>
 #include <linux/device.h>
 
+#include "of_private.h"
+
 static struct selftest_results {
 	int passed;
 	int failed;
@@ -266,6 +268,31 @@  static void __init of_selftest_property_match_string(void)
 	selftest(rc == -EILSEQ, "unterminated string; rc=%i", rc);
 }
 
+#define propcmp(p1, p2) (((p1)->length == (p2)->length) && \
+			(p1)->value && (p2)->value && \
+			!memcmp((p1)->value, (p2)->value, (p1)->length) && \
+			!strcmp((p1)->name, (p2)->name))
+static void __init of_selftest_property_copy(void)
+{
+#ifdef CONFIG_OF_DYNAMIC
+	struct property p1 = { .name = "p1", .length = 0, .value = "" };
+	struct property p2 = { .name = "p2", .length = 5, .value = "abcd" };
+	struct property *new;
+
+	new = __of_prop_dup(&p1, GFP_KERNEL);
+	selftest(new && propcmp(&p1, new), "empty property didn't copy correctly\n");
+	kfree(new->value);
+	kfree(new->name);
+	kfree(new);
+
+	new = __of_prop_dup(&p2, GFP_KERNEL);
+	selftest(new && propcmp(&p2, new), "non-empty property didn't copy correctly\n");
+	kfree(new->value);
+	kfree(new->name);
+	kfree(new);
+#endif
+}
+
 static void __init of_selftest_parse_interrupts(void)
 {
 	struct device_node *np;
@@ -533,6 +560,7 @@  static int __init of_selftest(void)
 	of_selftest_dynamic();
 	of_selftest_parse_phandle_with_args();
 	of_selftest_property_match_string();
+	of_selftest_property_copy();
 	of_selftest_parse_interrupts();
 	of_selftest_parse_interrupts_extended();
 	of_selftest_match_node();