return 0;
}
+
+int of_add_subnode(struct device_node *parent, const char *name, int len,
+ struct device_node **childp)
+{
+ struct device_node *child, *new, *last_sibling = NULL;
+ char *new_name, *full_name;
+ int parent_fnl;
+
+ if (len == -1)
+ len = strlen(name);
+ __for_each_child_of_node(parent, child) {
+ /*
+ * make sure we don't use a child called "trevor" when we are
+ * searching for "trev".
+ */
+ if (!strncmp(child->name, name, len) && strlen(name) == len) {
+ *childp = child;
+ return -EEXIST;
+ }
+ last_sibling = child;
+ }
+
+ /* Subnode does not exist -> append new subnode */
+ new = calloc(1, sizeof(struct device_node));
+ if (!new)
+ return -ENOMEM;
+
+ new_name = memdup(name, len + 1);
+ if (!new_name) {
+ free(new);
+ return -ENOMEM;
+ }
+ new_name[len] = '\0';
+
+ /*
+ * if the parent is the root node (named "") we don't need to prepend
+ * its full path
+ */
+ parent_fnl = *parent->name ? strlen(parent->full_name) : 0;
+ full_name = calloc(1, parent_fnl + 1 + len + 1);
+ if (!full_name) {
+ free(new_name);
+ free(new);
+ return -ENOMEM;
+ }
+ new->name = new_name; /* assign to constant pointer */
+
+ strcpy(full_name, parent->full_name); /* "" for root node */
+ full_name[parent_fnl] = '/';
+ strlcpy(&full_name[parent_fnl + 1], name, len + 1);
+ new->full_name = full_name;
+
+ /* Add as last sibling of the parent */
+ if (last_sibling)
+ last_sibling->sibling = new;
+ if (!parent->child)
+ parent->child = new;
+ new->parent = parent;
+
+ *childp = new;
+
+ return 0;
+}
return PHY_INTERFACE_MODE_NA;
}
+
+int ofnode_add_subnode(ofnode node, const char *name, ofnode *subnodep)
+{
+ ofnode subnode;
+ int ret = 0;
+
+ assert(ofnode_valid(node));
+
+ if (ofnode_is_np(node)) {
+ struct device_node *np, *child;
+
+ np = (struct device_node *)ofnode_to_np(node);
+ ret = of_add_subnode(np, name, -1, &child);
+ if (ret && ret != -EEXIST)
+ return ret;
+ subnode = np_to_ofnode(child);
+ } else {
+ void *fdt = (void *)gd->fdt_blob;
+ int poffset = ofnode_to_offset(node);
+ int offset;
+
+ offset = fdt_add_subnode(fdt, poffset, name);
+ if (offset == -FDT_ERR_EXISTS) {
+ offset = fdt_subnode_offset(fdt, poffset, name);
+ ret = -EEXIST;
+ }
+ if (offset < 0)
+ return -EINVAL;
+ subnode = offset_to_ofnode(offset);
+ }
+
+ *subnodep = subnode;
+
+ return ret; /* 0 or -EEXIST */
+}
int of_write_prop(struct device_node *np, const char *propname, int len,
const void *value);
+/**
+ * of_add_subnode() - add a new subnode to a node
+ *
+ * @node: parent node to add to
+ * @name: name of subnode
+ * @len: length of name (so the caller does not need to nul-terminate a
+ * partial string), or -1 for strlen(@name)
+ * @subnodep: returns pointer to new subnode (valid if the function returns 0
+ * or -EEXIST)
+ * Returns 0 if OK, -EEXIST if already exists, -ENOMEM if out of memory, other
+ * -ve on other error
+ */
+int of_add_subnode(struct device_node *node, const char *name, int len,
+ struct device_node **subnodep);
+
#endif
{
return NULL;
}
+
#endif /* CONFIG_DM */
+/**
+ * of_add_subnode() - add a new subnode to a node
+ *
+ * @parent: parent node to add to
+ * @name: name of subnode
+ * @nodep: returns pointer to new subnode (valid if the function returns 0
+ * or -EEXIST)
+ * Returns 0 if OK, -EEXIST if already exists, -ENOMEM if out of memory, other
+ * -ve on other error
+ */
+int ofnode_add_subnode(ofnode parent, const char *name, ofnode *nodep);
+
#endif
}
DM_TEST(dm_test_ofnode_u32,
UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT | UT_TESTF_LIVE_OR_FLAT);
+
+static int dm_test_ofnode_add_subnode(struct unit_test_state *uts)
+{
+ ofnode node, check, subnode;
+ char buf[128];
+
+ /* temporarily disable this test due to a failure fixed later */
+ if (!of_live_active())
+ return 0;
+
+ node = ofnode_path("/lcd");
+ ut_assert(ofnode_valid(node));
+ ut_assertok(ofnode_add_subnode(node, "edmund", &subnode));
+ check = ofnode_path("/lcd/edmund");
+ ut_asserteq(subnode.of_offset, check.of_offset);
+ ut_assertok(ofnode_get_path(subnode, buf, sizeof(buf)));
+ ut_asserteq_str("/lcd/edmund", buf);
+
+ if (of_live_active()) {
+ struct device_node *child;
+
+ ut_assertok(of_add_subnode((void *)ofnode_to_np(node), "edmund",
+ 2, &child));
+ ut_asserteq_str("ed", child->name);
+ ut_asserteq_str("/lcd/ed", child->full_name);
+ check = ofnode_path("/lcd/ed");
+ ut_asserteq_ptr(child, check.np);
+ ut_assertok(ofnode_get_path(np_to_ofnode(child), buf,
+ sizeof(buf)));
+ ut_asserteq_str("/lcd/ed", buf);
+ }
+
+ /* An existing node should be returned with -EEXIST */
+ ut_asserteq(-EEXIST, ofnode_add_subnode(node, "edmund", &check));
+ ut_asserteq(subnode.of_offset, check.of_offset);
+
+ /* add a root node */
+ node = ofnode_path("/");
+ ut_assert(ofnode_valid(node));
+ ut_assertok(ofnode_add_subnode(node, "lcd2", &subnode));
+ check = ofnode_path("/lcd2");
+ ut_asserteq(subnode.of_offset, check.of_offset);
+ ut_assertok(ofnode_get_path(subnode, buf, sizeof(buf)));
+ ut_asserteq_str("/lcd2", buf);
+
+ if (of_live_active()) {
+ ulong start;
+ int i;
+
+ /*
+ * Make sure each of the three malloc()checks in
+ * of_add_subnode() work
+ */
+ for (i = 0; i < 3; i++) {
+ malloc_enable_testing(i);
+ start = ut_check_free();
+ ut_asserteq(-ENOMEM, ofnode_add_subnode(node, "anthony",
+ &check));
+ ut_assertok(ut_check_delta(start));
+ }
+
+ /* This should pass since we allow 3 allocations */
+ malloc_enable_testing(3);
+ ut_assertok(ofnode_add_subnode(node, "anthony", &check));
+ malloc_disable_testing();
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_add_subnode,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT | UT_TESTF_LIVE_OR_FLAT);