diff options
-rw-r--r-- | hw/qdev.c | 103 | ||||
-rw-r--r-- | hw/qdev.h | 28 |
2 files changed, 131 insertions, 0 deletions
@@ -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; +} + @@ -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 |