pr_debug("allnodes list size (%i); sibling lists size (%i)\n", allnode_count, child_count);
}
+static void __init of_unittest_printf_one(struct device_node *np, const char *fmt,
+ const char *expected)
+{
+ unsigned char buf[strlen(expected)+10];
+ int size, i;
+
+ /* Baseline; check conversion with a large size limit */
+ memset(buf, 0xff, sizeof(buf));
+ size = snprintf(buf, sizeof(buf) - 2, fmt, np);
+
+ /* use strcmp() instead of strncmp() here to be absolutely sure strings match */
+ unittest((strcmp(buf, expected) == 0) && (buf[size+1] == 0xff),
+ "sprintf failed; fmt='%s' expected='%s' rslt='%s'\n",
+ fmt, expected, buf);
+
+ /* Make sure length limits work */
+ size++;
+ for (i = 0; i < 2; i++, size--) {
+ /* Clear the buffer, and make sure it works correctly still */
+ memset(buf, 0xff, sizeof(buf));
+ snprintf(buf, size+1, fmt, np);
+ unittest(strncmp(buf, expected, size) == 0 && (buf[size+1] == 0xff),
+ "snprintf failed; size=%i fmt='%s' expected='%s' rslt='%s'\n",
+ size, fmt, expected, buf);
+ }
+}
+
+static void __init of_unittest_printf(void)
+{
+ struct device_node *np;
+ const char *full_name = "/testcase-data/platform-tests/test-device@1/dev@100";
+ char phandle_str[16] = "";
+
+ np = of_find_node_by_path(full_name);
+ if (!np) {
+ unittest(np, "testcase data missing\n");
+ return;
+ }
+
+ num_to_str(phandle_str, sizeof(phandle_str), np->phandle);
+
+ of_unittest_printf_one(np, "%pOF", full_name);
+ of_unittest_printf_one(np, "%pOFf", full_name);
+ of_unittest_printf_one(np, "%pOFp", phandle_str);
+ of_unittest_printf_one(np, "%pOFP", "dev@100");
+ of_unittest_printf_one(np, "ABC %pOFP ABC", "ABC dev@100 ABC");
+ of_unittest_printf_one(np, "%10pOFP", " dev@100");
+ of_unittest_printf_one(np, "%-10pOFP", "dev@100 ");
+ of_unittest_printf_one(of_root, "%pOFP", "/");
+ of_unittest_printf_one(np, "%pOFF", "----");
+ of_unittest_printf_one(np, "%pOFPF", "dev@100:----");
+ of_unittest_printf_one(np, "%pOFPFPc", "dev@100:----:dev@100:test-sub-device");
+ of_unittest_printf_one(np, "%pOFc", "test-sub-device");
+ of_unittest_printf_one(np, "%pOFC",
+ "\"test-sub-device\",\"test-compat2\",\"test-compat3\"");
+}
+
struct node_hash {
struct hlist_node node;
struct device_node *np;
of_unittest_find_node_by_name();
of_unittest_dynamic();
of_unittest_parse_phandle_with_args();
+ of_unittest_printf();
of_unittest_property_string();
of_unittest_property_copy();
of_unittest_changeset();
#include <linux/dcache.h>
#include <linux/cred.h>
#include <linux/uuid.h>
+#include <linux/of.h>
#include <net/addrconf.h>
#ifdef CONFIG_BLOCK
#include <linux/blkdev.h>
return format_flags(buf, end, flags, names);
}
+static const char *device_node_name_for_depth(const struct device_node *np, int depth)
+{
+ for ( ; np && depth; depth--)
+ np = np->parent;
+
+ return kbasename(np->full_name);
+}
+
+static noinline_for_stack
+char *device_node_gen_full_name(const struct device_node *np, char *buf, char *end)
+{
+ int depth;
+ const struct device_node *parent = np->parent;
+ static const struct printf_spec strspec = {
+ .field_width = -1,
+ .precision = -1,
+ };
+
+ /* special case for root node */
+ if (!parent)
+ return string(buf, end, "/", strspec);
+
+ for (depth = 0; parent->parent; depth++)
+ parent = parent->parent;
+
+ for ( ; depth >= 0; depth--) {
+ buf = string(buf, end, "/", strspec);
+ buf = string(buf, end, device_node_name_for_depth(np, depth),
+ strspec);
+ }
+ return buf;
+}
+
+static noinline_for_stack
+char *device_node_string(char *buf, char *end, struct device_node *dn,
+ struct printf_spec spec, const char *fmt)
+{
+ char tbuf[sizeof("xxxx") + 1];
+ const char *p;
+ int ret;
+ char *buf_start = buf;
+ struct property *prop;
+ bool has_mult, pass;
+ static const struct printf_spec num_spec = {
+ .flags = SMALL,
+ .field_width = -1,
+ .precision = -1,
+ .base = 10,
+ };
+
+ struct printf_spec str_spec = spec;
+ str_spec.field_width = -1;
+
+ if (!IS_ENABLED(CONFIG_OF))
+ return string(buf, end, "(!OF)", spec);
+
+ if ((unsigned long)dn < PAGE_SIZE)
+ return string(buf, end, "(null)", spec);
+
+ /* simple case without anything any more format specifiers */
+ fmt++;
+ if (fmt[0] == '\0' || strcspn(fmt,"fnpPFcC") > 0)
+ fmt = "f";
+
+ for (pass = false; strspn(fmt,"fnpPFcC"); fmt++, pass = true) {
+ if (pass) {
+ if (buf < end)
+ *buf = ':';
+ buf++;
+ }
+
+ switch (*fmt) {
+ case 'f': /* full_name */
+ buf = device_node_gen_full_name(dn, buf, end);
+ break;
+ case 'n': /* name */
+ buf = string(buf, end, dn->name, str_spec);
+ break;
+ case 'p': /* phandle */
+ buf = number(buf, end, (unsigned int)dn->phandle, num_spec);
+ break;
+ case 'P': /* path-spec */
+ p = kbasename(of_node_full_name(dn));
+ if (!p[1])
+ p = "/";
+ buf = string(buf, end, p, str_spec);
+ break;
+ case 'F': /* flags */
+ tbuf[0] = of_node_check_flag(dn, OF_DYNAMIC) ? 'D' : '-';
+ tbuf[1] = of_node_check_flag(dn, OF_DETACHED) ? 'd' : '-';
+ tbuf[2] = of_node_check_flag(dn, OF_POPULATED) ? 'P' : '-';
+ tbuf[3] = of_node_check_flag(dn, OF_POPULATED_BUS) ? 'B' : '-';
+ tbuf[4] = 0;
+ buf = string(buf, end, tbuf, str_spec);
+ break;
+ case 'c': /* major compatible string */
+ ret = of_property_read_string(dn, "compatible", &p);
+ if (!ret)
+ buf = string(buf, end, p, str_spec);
+ break;
+ case 'C': /* full compatible string */
+ has_mult = false;
+ of_property_for_each_string(dn, "compatible", prop, p) {
+ if (has_mult)
+ buf = string(buf, end, ",", str_spec);
+ buf = string(buf, end, "\"", str_spec);
+ buf = string(buf, end, p, str_spec);
+ buf = string(buf, end, "\"", str_spec);
+
+ has_mult = true;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return widen_string(buf, buf - buf_start, end, spec);
+}
+
int kptr_restrict __read_mostly;
/*
* p page flags (see struct page) given as pointer to unsigned long
* g gfp flags (GFP_* and __GFP_*) given as pointer to gfp_t
* v vma flags (VM_*) given as pointer to unsigned long
+ * - 'O' For a kobject based struct. Must be one of the following:
+ * - 'OF[fnpPcCF]' For a device tree object
+ * Without any optional arguments prints the full_name
+ * f device node full_name
+ * n device node name
+ * p device node phandle
+ * P device node path spec (name + @unit)
+ * F device node flags
+ * c major compatible string
+ * C full compatible string
*
* ** Please update also Documentation/printk-formats.txt when making changes **
*
case 'G':
return flags_string(buf, end, ptr, fmt);
+ case 'O':
+ switch (fmt[1]) {
+ case 'F':
+ return device_node_string(buf, end, ptr, spec, fmt + 1);
+ }
}
spec.flags |= SMALL;
if (spec.field_width == -1) {