[PATCH v3 02/70] dm: core: Support sorting devices with dm tree

Simon Glass sjg at chromium.org
Tue Jan 17 18:47:12 CET 2023


Add a -s flag to sort the top-level devices in order of uclass ID.

Signed-off-by: Simon Glass <sjg at chromium.org>
---

(no changes since v1)

 cmd/dm.c                 | 10 +++++--
 doc/usage/cmd/dm.rst     |  5 +++-
 drivers/core/dump.c      | 65 ++++++++++++++++++++++++++++++++++++----
 include/dm/util.h        |  8 +++--
 test/py/tests/test_dm.py | 38 +++++++++++++++++++++++
 5 files changed, 114 insertions(+), 12 deletions(-)

diff --git a/cmd/dm.c b/cmd/dm.c
index 218be85795d..979cd36061e 100644
--- a/cmd/dm.c
+++ b/cmd/dm.c
@@ -59,7 +59,11 @@ static int do_dm_dump_static_driver_info(struct cmd_tbl *cmdtp, int flag,
 static int do_dm_dump_tree(struct cmd_tbl *cmdtp, int flag, int argc,
 			   char *const argv[])
 {
-	dm_dump_tree();
+	bool sort;
+
+	sort = argc > 1 && !strcmp(argv[1], "-s");
+
+	dm_dump_tree(sort);
 
 	return 0;
 }
@@ -87,7 +91,7 @@ static char dm_help_text[] =
 	"dm drivers       Dump list of drivers with uclass and instances\n"
 	DM_MEM_HELP
 	"dm static        Dump list of drivers with static platform data\n"
-	"dm tree          Dump tree of driver model devices ('*' = activated)\n"
+	"dm tree [-s]     Dump tree of driver model devices (-s=sort)\n"
 	"dm uclass        Dump list of instances for each uclass"
 	;
 #endif
@@ -98,5 +102,5 @@ U_BOOT_CMD_WITH_SUBCMDS(dm, "Driver model low level access", dm_help_text,
 	U_BOOT_SUBCMD_MKENT(drivers, 1, 1, do_dm_dump_drivers),
 	DM_MEM
 	U_BOOT_SUBCMD_MKENT(static, 1, 1, do_dm_dump_static_driver_info),
-	U_BOOT_SUBCMD_MKENT(tree, 1, 1, do_dm_dump_tree),
+	U_BOOT_SUBCMD_MKENT(tree, 2, 1, do_dm_dump_tree),
 	U_BOOT_SUBCMD_MKENT(uclass, 1, 1, do_dm_dump_uclass));
diff --git a/doc/usage/cmd/dm.rst b/doc/usage/cmd/dm.rst
index 7bc1962a754..236cd02bd62 100644
--- a/doc/usage/cmd/dm.rst
+++ b/doc/usage/cmd/dm.rst
@@ -12,7 +12,7 @@ Synopis
     dm devres
     dm drivers
     dm static
-    dm tree
+    dm tree [-s]
     dm uclass
 
 Description
@@ -123,6 +123,9 @@ Name
     Shows the device name as well as the tree structure, since child devices are
     shown attached to their parent.
 
+If -s is given, the top-level devices (those which are children of the root
+device) are shown sorted in order of uclass ID, so it is easier to find a
+particular device type.
 
 dm uclass
 ~~~~~~~~~
diff --git a/drivers/core/dump.c b/drivers/core/dump.c
index 1c1f7e4d308..0c7d2ec4d0c 100644
--- a/drivers/core/dump.c
+++ b/drivers/core/dump.c
@@ -5,12 +5,34 @@
 
 #include <common.h>
 #include <dm.h>
+#include <malloc.h>
 #include <mapmem.h>
+#include <sort.h>
 #include <dm/root.h>
 #include <dm/util.h>
 #include <dm/uclass-internal.h>
 
-static void show_devices(struct udevice *dev, int depth, int last_flag)
+/**
+ * struct sort_info - information used for sorting
+ *
+ * @dev: List of devices
+ * @alloced: Maximum number of devices in @dev
+ */
+struct sort_info {
+	struct udevice **dev;
+	int size;
+};
+
+static int h_cmp_uclass_id(const void *d1, const void *d2)
+{
+	const struct udevice *const *dev1 = d1;
+	const struct udevice *const *dev2 = d2;
+
+	return device_get_uclass_id(*dev1) - device_get_uclass_id(*dev2);
+}
+
+static void show_devices(struct udevice *dev, int depth, int last_flag,
+			 struct udevice **devs)
 {
 	int i, is_last;
 	struct udevice *child;
@@ -39,21 +61,52 @@ static void show_devices(struct udevice *dev, int depth, int last_flag)
 
 	printf("%s\n", dev->name);
 
-	device_foreach_child(child, dev) {
-		is_last = list_is_last(&child->sibling_node, &dev->child_head);
-		show_devices(child, depth + 1, (last_flag << 1) | is_last);
+	if (devs) {
+		int count;
+		int i;
+
+		count = 0;
+		device_foreach_child(child, dev)
+			devs[count++] = child;
+		qsort(devs, count, sizeof(struct udevice *), h_cmp_uclass_id);
+
+		for (i = 0; i < count; i++) {
+			show_devices(devs[i], depth + 1,
+				     (last_flag << 1) | (i == count - 1),
+				     devs + count);
+		}
+	} else {
+		device_foreach_child(child, dev) {
+			is_last = list_is_last(&child->sibling_node,
+					       &dev->child_head);
+			show_devices(child, depth + 1,
+				     (last_flag << 1) | is_last, NULL);
+		}
 	}
 }
 
-void dm_dump_tree(void)
+void dm_dump_tree(bool sort)
 {
 	struct udevice *root;
 
 	root = dm_root();
 	if (root) {
+		int dev_count, uclasses;
+		struct udevice **devs = NULL;
+
+		dm_get_stats(&dev_count, &uclasses);
+
 		printf(" Class     Index  Probed  Driver                Name\n");
 		printf("-----------------------------------------------------------\n");
-		show_devices(root, -1, 0);
+		if (sort) {
+			devs = calloc(dev_count, sizeof(struct udevice *));
+			if (!devs) {
+				printf("(out of memory)\n");
+				return;
+			}
+		}
+		show_devices(root, -1, 0, devs);
+		free(devs);
 	}
 }
 
diff --git a/include/dm/util.h b/include/dm/util.h
index e10c6060ce0..4bb49e9e8c0 100644
--- a/include/dm/util.h
+++ b/include/dm/util.h
@@ -26,8 +26,12 @@ struct list_head;
  */
 int list_count_items(struct list_head *head);
 
-/* Dump out a tree of all devices */
-void dm_dump_tree(void);
+/**
+ * Dump out a tree of all devices
+ *
+ * @sort: Sort by uclass name
+ */
+void dm_dump_tree(bool sort);
 
 /* Dump out a list of uclasses and their devices */
 void dm_dump_uclass(void);
diff --git a/test/py/tests/test_dm.py b/test/py/tests/test_dm.py
index ea93061fdfa..68d4ea12235 100644
--- a/test/py/tests/test_dm.py
+++ b/test/py/tests/test_dm.py
@@ -16,6 +16,44 @@ def test_dm_compat(u_boot_console):
     for driver in drivers:
         assert driver in response
 
+    # check sorting - output looks something like this:
+    #  testacpi      0  [   ]   testacpi_drv          |-- acpi-test
+    #  testacpi      1  [   ]   testacpi_drv          |   `-- child
+    #  pci_emul_p    1  [   ]   pci_emul_parent_drv   |-- pci-emul2
+    #  pci_emul      5  [   ]   sandbox_swap_case_em  |   `-- emul2 at 1f,0
+
+    # The number of '|   ' and '--' matches indicate the indent level. We start
+    # checking sorting only after UCLASS_AXI_EMUL after which the names should
+    # be sorted.
+
+    response = u_boot_console.run_command('dm tree -s')
+    lines = response.split('\n')[2:]
+    stack = []   # holds where we were up to at the previous indent level
+    prev = ''    # uclass name of previous line
+    start = False
+    for line in lines:
+        indent = line.count('|   ') + ('--' in line)
+        cur = line.split()[0]
+        if not start:
+            if cur != 'axi_emul':
+                continue
+            start = True
+
+        # Handle going up or down an indent level
+        if indent > len(stack):
+            stack.append(prev)
+            prev = ''
+        elif indent < len(stack):
+            prev = stack.pop()
+
+        # Check that the current uclass name is not alphabetically before the
+        # previous one
+        if 'emul' not in cur and cur < prev:
+            print('indent', cur >= prev, indent, prev, cur, stack)
+            assert cur >= prev
+            prev = cur
+
+
 @pytest.mark.buildconfigspec('cmd_dm')
 def test_dm_drivers(u_boot_console):
     """Test that each driver in `dm compat` is also listed in `dm drivers`."""
-- 
2.39.0.246.g2a6d74b583-goog



More information about the U-Boot mailing list