diff --git a/commands/of_property.c b/commands/of_property.c index ae6bfd4..2bc08f2 100644 --- a/commands/of_property.c +++ b/commands/of_property.c @@ -278,6 +278,7 @@ if (pp) { free(pp->value); + pp->value_const = NULL; /* limit property data to the actual size */ if (len) { diff --git a/drivers/of/base.c b/drivers/of/base.c index 23e5e22..152dbe7 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -129,6 +129,11 @@ } EXPORT_SYMBOL(of_find_property); +const void *of_property_get_value(struct property *pp) +{ + return pp->value ? pp->value : pp->value_const; +} + static void of_alias_add(struct alias_prop *ap, struct device_node *np, int id, const char *stem, int stem_len) { @@ -178,7 +183,7 @@ !of_prop_cmp(pp->name, "linux,phandle")) continue; - np = of_find_node_by_path(pp->value); + np = of_find_node_by_path(of_property_get_value(pp)); if (!np) continue; @@ -374,7 +379,10 @@ { struct property *pp = of_find_property(np, name, lenp); - return pp ? pp->value : NULL; + if (!pp) + return NULL; + + return of_property_get_value(pp); } EXPORT_SYMBOL(of_get_property); @@ -678,19 +686,21 @@ * property data isn't large enough. * */ -static void *of_find_property_value_of_size(const struct device_node *np, +static const void *of_find_property_value_of_size(const struct device_node *np, const char *propname, u32 len) { struct property *prop = of_find_property(np, propname, NULL); + const void *value; if (!prop) return ERR_PTR(-EINVAL); - if (!prop->value) + value = of_property_get_value(prop); + if (!value) return ERR_PTR(-ENODATA); if (len > prop->length) return ERR_PTR(-EOVERFLOW); - return prop->value; + return value; } /** @@ -867,13 +877,16 @@ const char **out_string) { struct property *prop = of_find_property(np, propname, NULL); + const void *value; + if (!prop) return -EINVAL; - if (!prop->value) + value = of_property_get_value(prop); + if (!value) return -ENODATA; - if (strnlen(prop->value, prop->length) >= prop->length) + if (strnlen(value, prop->length) >= prop->length) return -EILSEQ; - *out_string = prop->value; + *out_string = value; return 0; } EXPORT_SYMBOL_GPL(of_property_read_string); @@ -903,15 +916,17 @@ int i = 0; size_t l = 0, total = 0; const char *p; + const void *value; if (!prop) return -EINVAL; - if (!prop->value) + value = of_property_get_value(prop); + if (!value) return -ENODATA; - if (strnlen(prop->value, prop->length) >= prop->length) + if (strnlen(value, prop->length) >= prop->length) return -EILSEQ; - p = prop->value; + p = value; for (i = 0; total < prop->length; total += l, p += l) { l = strlen(p) + 1; @@ -943,10 +958,11 @@ if (!prop) return -EINVAL; - if (!prop->value) + + p = of_property_get_value(prop); + if (!p) return -ENODATA; - p = prop->value; end = p + prop->length; for (i = 0; p < end; i++, p += l) { @@ -979,15 +995,17 @@ int i = 0; size_t l = 0, total = 0; const char *p; + const void *value; if (!prop) return -EINVAL; - if (!prop->value) + value = of_property_get_value(prop); + if (!value) return -ENODATA; - if (strnlen(prop->value, prop->length) >= prop->length) + if (strnlen(value, prop->length) >= prop->length) return -EILSEQ; - p = prop->value; + p = value; for (i = 0; total < prop->length; total += l, p += l, i++) l = strlen(p) + 1; @@ -1000,17 +1018,20 @@ u32 *pu) { const void *curv = cur; + const void *value; if (!prop) return NULL; + value = of_property_get_value(prop); + if (!cur) { - curv = prop->value; + curv = value; goto out_val; } curv += sizeof(*cur); - if (curv >= prop->value + prop->length) + if (curv >= value + prop->length) return NULL; out_val: @@ -1022,15 +1043,18 @@ const char *of_prop_next_string(struct property *prop, const char *cur) { const void *curv = cur; + const void *value; if (!prop) return NULL; + value = of_property_get_value(prop); + if (!cur) - return prop->value; + return value; curv += strlen(cur) + 1; - if (curv >= prop->value + prop->length) + if (curv >= value + prop->length) return NULL; return curv; @@ -1777,7 +1801,7 @@ printf("%s", p->name); if (p->length) { printf(" = "); - of_print_property(p->value, p->length); + of_print_property(of_property_get_value(p), p->length); } printf(";\n"); } @@ -1817,6 +1841,18 @@ return node; } +/** + * of_new_property - Add a new property to a node + * @node: device node to which the property is added + * @name: Name of the new property + * @data: Value of the property (can be NULL) + * @len: Length of the value + * + * This adds a new property to a device node. @data is copied and no longer needed + * after calling this function. + * + * Return: A pointer to the new property + */ struct property *of_new_property(struct device_node *node, const char *name, const void *data, int len) { @@ -1835,6 +1871,35 @@ return prop; } +/** + * of_new_property_const - Add a new property to a node + * @node: device node to which the property is added + * @name: Name of the new property + * @data: Value of the property (can be NULL) + * @len: Length of the value + * + * This adds a new property to a device node. @data is used directly in the + * property and must be valid until the property is deleted again or set to + * another value. Normally you shouldn't use this function, use of_new_property() + * instead. + * + * Return: A pointer to the new property + */ +struct property *of_new_property_const(struct device_node *node, const char *name, + const void *data, int len) +{ + struct property *prop; + + prop = xzalloc(sizeof(*prop)); + prop->name = xstrdup(name); + prop->length = len; + prop->value_const = data; + + list_add_tail(&prop->list, &node->properties); + + return prop; +} + void of_delete_property(struct property *pp) { if (!pp) diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 614e136..1edb35f 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -117,7 +117,7 @@ * Parse a flat device tree binary blob and return a pointer to the * unflattened tree. */ -struct device_node *of_unflatten_dtb(const void *infdt) +struct device_node *__of_unflatten_dtb(const void *infdt, bool constprops) { const void *nodep; /* property node pointer */ uint32_t tag; /* tag */ @@ -221,7 +221,11 @@ goto err; } - p = of_new_property(node, name, nodep, len); + if (constprops) + p = of_new_property_const(node, name, nodep, len); + else + p = of_new_property(node, name, nodep, len); + if (!strcmp(name, "phandle") && len == 4) node->phandle = be32_to_cpup(p->value); @@ -255,6 +259,34 @@ return ERR_PTR(ret); } +/** + * of_unflatten_dtb - unflatten a dtb binary blob + * @infdt - the fdt blob to unflatten + * + * Parse a flat device tree binary blob and return a pointer to the unflattened + * tree. The tree must be freed after use with of_delete_node(). + */ +struct device_node *of_unflatten_dtb(const void *infdt) +{ + return __of_unflatten_dtb(infdt, false); +} + +/** + * of_unflatten_dtb_const - unflatten a dtb binary blob + * @infdt - the fdt blob to unflatten + * + * Parse a flat device tree binary blob and return a pointer to the unflattened + * tree. The tree must be freed after use with of_delete_node(). Unlike the + * above version this function uses the property data directly from the input + * flattened tree instead of copying the data, thus @infdt must be valid for the + * whole lifetime of the returned tree. This is normally not what you want, so + * use of_unflatten_dtb() instead. + */ +struct device_node *of_unflatten_dtb_const(const void *infdt) +{ + return __of_unflatten_dtb(infdt, true); +} + struct fdt { void *dt; uint32_t dt_nextofs; diff --git a/drivers/pinctrl/pinctrl.c b/drivers/pinctrl/pinctrl.c index bef4fcd..0e42a31 100644 --- a/drivers/pinctrl/pinctrl.c +++ b/drivers/pinctrl/pinctrl.c @@ -99,8 +99,7 @@ int of_pinctrl_select_state(struct device_node *np, const char *name) { int state, ret; - char *propname; - struct property *prop; + char propname[sizeof("pinctrl-4294967295")]; const __be32 *list; int size, config; phandle phandle; @@ -113,18 +112,13 @@ /* For each defined state ID */ for (state = 0; ; state++) { /* Retrieve the pinctrl-* property */ - propname = basprintf("pinctrl-%d", state); - prop = of_find_property(np, propname, NULL); - free(propname); - - if (!prop) { + sprintf(propname, "pinctrl-%d", state); + list = of_get_property(np, propname, &size); + if (!list) { ret = -ENODEV; break; } - size = prop->length; - - list = prop->value; size /= sizeof(*list); /* Determine whether pinctrl-names property names the state */ @@ -137,7 +131,7 @@ */ if (ret < 0) { /* strlen("pinctrl-") == 8 */ - statename = prop->name + 8; + statename = &propname[8]; } if (strcmp(name, statename)) @@ -151,7 +145,7 @@ np_config = of_find_node_by_phandle(phandle); if (!np_config) { pr_err("prop %s %s index %i invalid phandle\n", - np->full_name, prop->name, config); + np->full_name, propname, config); ret = -EINVAL; goto err; } diff --git a/include/of.h b/include/of.h index 4564f48..fec51bb 100644 --- a/include/of.h +++ b/include/of.h @@ -20,6 +20,7 @@ char *name; int length; void *value; + const void *value_const; struct list_head list; }; @@ -100,6 +101,7 @@ int of_probe(void); int of_parse_dtb(struct fdt_header *fdt); struct device_node *of_unflatten_dtb(const void *fdt); +struct device_node *of_unflatten_dtb_const(const void *infdt); struct cdev; @@ -117,6 +119,9 @@ const void *val, int len, int create); extern struct property *of_new_property(struct device_node *node, const char *name, const void *data, int len); +extern struct property *of_new_property_const(struct device_node *node, + const char *name, + const void *data, int len); extern void of_delete_property(struct property *pp); extern struct device_node *of_find_node_by_name(struct device_node *from,