aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/qdev.c103
-rw-r--r--hw/qdev.h28
2 files changed, 131 insertions, 0 deletions
diff --git a/hw/qdev.c b/hw/qdev.c
index 79849c9..2519f00 100644
--- a/hw/qdev.c
+++ b/hw/qdev.c
@@ -1221,3 +1221,106 @@ gchar *qdev_get_canonical_path(DeviceState *dev)
return newpath;
}
+
+static DeviceState *qdev_resolve_abs_path(DeviceState *parent,
+ gchar **parts,
+ int index)
+{
+ DeviceProperty *prop;
+ DeviceState *child;
+
+ if (parts[index] == NULL) {
+ return parent;
+ }
+
+ if (strcmp(parts[index], "") == 0) {
+ return qdev_resolve_abs_path(parent, parts, index + 1);
+ }
+
+ prop = qdev_property_find(parent, parts[index]);
+ if (prop == NULL) {
+ return NULL;
+ }
+
+ child = NULL;
+ if (strstart(prop->type, "link<", NULL)) {
+ DeviceState **pchild = prop->opaque;
+ if (*pchild) {
+ child = *pchild;
+ }
+ } else if (strstart(prop->type, "child<", NULL)) {
+ child = prop->opaque;
+ }
+
+ if (!child) {
+ return NULL;
+ }
+
+ return qdev_resolve_abs_path(child, parts, index + 1);
+}
+
+static DeviceState *qdev_resolve_partial_path(DeviceState *parent,
+ gchar **parts,
+ bool *ambiguous)
+{
+ DeviceState *dev;
+ DeviceProperty *prop;
+
+ dev = qdev_resolve_abs_path(parent, parts, 0);
+
+ QTAILQ_FOREACH(prop, &parent->properties, node) {
+ DeviceState *found;
+
+ if (!strstart(prop->type, "child<", NULL)) {
+ continue;
+ }
+
+ found = qdev_resolve_partial_path(prop->opaque, parts, ambiguous);
+ if (found) {
+ if (dev) {
+ if (ambiguous) {
+ *ambiguous = true;
+ }
+ return NULL;
+ }
+ dev = found;
+ }
+
+ if (ambiguous && *ambiguous) {
+ return NULL;
+ }
+ }
+
+ return dev;
+}
+
+DeviceState *qdev_resolve_path(const char *path, bool *ambiguous)
+{
+ bool partial_path = true;
+ DeviceState *dev;
+ gchar **parts;
+
+ parts = g_strsplit(path, "/", 0);
+ if (parts == NULL || parts[0] == NULL) {
+ g_strfreev(parts);
+ return qdev_get_root();
+ }
+
+ if (strcmp(parts[0], "") == 0) {
+ partial_path = false;
+ }
+
+ if (partial_path) {
+ if (ambiguous) {
+ *ambiguous = false;
+ }
+ dev = qdev_resolve_partial_path(qdev_get_root(), parts, ambiguous);
+ } else {
+ dev = qdev_resolve_abs_path(qdev_get_root(), parts, 1);
+ }
+
+ g_strfreev(parts);
+
+ return dev;
+}
+
diff --git a/hw/qdev.h b/hw/qdev.h
index 0f00497..641d134 100644
--- a/hw/qdev.h
+++ b/hw/qdev.h
@@ -499,4 +499,32 @@ DeviceState *qdev_get_root(void);
*/
gchar *qdev_get_canonical_path(DeviceState *dev);
+/**
+ * @qdev_resolve_path - resolves a path returning a device
+ *
+ * There are two types of supported paths--absolute paths and partial paths.
+ *
+ * Absolute paths are derived from the root device and can follow child<> or
+ * link<> properties. Since they can follow link<> properties, they can be
+ * arbitrarily long. Absolute paths look like absolute filenames and are
+ * prefixed with a leading slash.
+ *
+ * Partial paths look like relative filenames. They do not begin with a
+ * prefix. The matching rules for partial paths are subtle but designed to make
+ * specifying devices easy. At each level of the composition tree, the partial
+ * path is matched as an absolute path. The first match is not returned. At
+ * least two matches are searched for. A successful result is only returned if
+ * only one match is founded. If more than one match is found, a flag is
+ * return to indicate that the match was ambiguous.
+ *
+ * @path - the path to resolve
+ *
+ * @ambiguous - returns true if the path resolution failed because of an
+ * ambiguous match
+ *
+ * Returns:
+ * The matched device or NULL on path lookup failure.
+ */
+DeviceState *qdev_resolve_path(const char *path, bool *ambiguous);
+
#endif