of_create_node() creates device node and apply to base tree dynamically. The parent device node and full name are required for creating the node. And the caller can also provide a property array for the node. Inside this function, it creates a changeset. Then the new device node and properties are added to the changeset and applied to base tree. The pointer of this changeset is saved in device node private data. of_destroy_node() removes the node created by of_create_node() from the base tree and free it. It gets the changeset pointer from device node private data and call of_changeset_destroy() to free everything. Signed-off-by: Lizhi Hou <lizhi.hou@xxxxxxx> Signed-off-by: Sonal Santan <sonal.santan@xxxxxxx> Signed-off-by: Max Zhen <max.zhen@xxxxxxx> Signed-off-by: Brian Xu <brian.xu@xxxxxxx> --- drivers/of/dynamic.c | 80 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/of.h | 4 +++ 2 files changed, 84 insertions(+) diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index cd3821a6444f..eca28b723706 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -934,3 +934,83 @@ int of_changeset_action(struct of_changeset *ocs, unsigned long action, return 0; } EXPORT_SYMBOL_GPL(of_changeset_action); + +/** + * of_create_node - Dynamically create a device node and apply it to base tree + * + * @parent: Pointer to parent device node + * @full_name: Full name of device node + * @props: Pointer to property array + * + * Return: Pointer to the created device node or NULL in case of an error. + */ +struct device_node *of_create_node(struct device_node *parent, + const char *full_name, + struct property *props) +{ + struct of_changeset *cset; + struct property *new_pp; + struct device_node *np; + int ret, i; + + cset = kzalloc(sizeof(*cset), GFP_KERNEL); + if (!cset) + return NULL; + + of_changeset_init(cset); + + np = __of_node_dup(NULL, full_name); + if (!np) + goto failed; + np->parent = parent; + + ret = of_changeset_attach_node(cset, np); + if (ret) + goto failed; + + if (props) { + for (i = 0; props[i].name; i++) { + new_pp = __of_prop_dup(&props[i], GFP_KERNEL); + if (!new_pp) + goto failed; + ret = of_changeset_add_property(cset, np, new_pp); + if (ret) { + kfree(new_pp->name); + kfree(new_pp->value); + kfree(new_pp); + goto failed; + } + } + } + + ret = of_changeset_apply(cset); + if (ret) + goto failed; + + np->data = cset; + + return np; + +failed: + of_changeset_destroy(cset); + if (np) + of_node_put(np); + + return NULL; +} + +/** + * of_destroy_node - Destroy a dynamically created device node + * + * @np: Pointer to dynamically created device node + * + */ +void of_destroy_node(struct device_node *np) +{ + struct of_changeset *cset; + + cset = (struct of_changeset *)np->data; + of_changeset_destroy(cset); + of_node_put(np); + kfree(cset); +} diff --git a/include/linux/of.h b/include/linux/of.h index 766d002bddb9..493ef957c1a8 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -1475,6 +1475,10 @@ extern int of_changeset_revert(struct of_changeset *ocs); extern int of_changeset_action(struct of_changeset *ocs, unsigned long action, struct device_node *np, struct property *prop); +struct device_node *of_create_node(struct device_node *parent, + const char *full_name, + struct property *props); +void of_destroy_node(struct device_node *np); static inline int of_changeset_attach_node(struct of_changeset *ocs, struct device_node *np) -- 2.17.1