summary refs log tree commit diff stats
path: root/tests/libqos
diff options
context:
space:
mode:
Diffstat (limited to 'tests/libqos')
-rw-r--r--tests/libqos/arm-imx25-pdk-machine.c92
-rw-r--r--tests/libqos/arm-n800-machine.c92
-rw-r--r--tests/libqos/i2c-imx.c40
-rw-r--r--tests/libqos/i2c-omap.c70
-rw-r--r--tests/libqos/i2c.c74
-rw-r--r--tests/libqos/i2c.h63
-rw-r--r--tests/libqos/qgraph.c12
-rw-r--r--tests/libqos/qgraph.h15
8 files changed, 394 insertions, 64 deletions
diff --git a/tests/libqos/arm-imx25-pdk-machine.c b/tests/libqos/arm-imx25-pdk-machine.c
new file mode 100644
index 0000000000..25066fb8a9
--- /dev/null
+++ b/tests/libqos/arm-imx25-pdk-machine.c
@@ -0,0 +1,92 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2019 Red Hat, Inc.
+ *
+ * Author: Paolo Bonzini <pbonzini@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "libqos/malloc.h"
+#include "libqos/qgraph.h"
+#include "libqos/i2c.h"
+
+#define ARM_PAGE_SIZE            4096
+#define IMX25_PDK_RAM_START      0x80000000
+#define IMX25_PDK_RAM_END        0x88000000
+
+typedef struct QIMX25PDKMachine QIMX25PDKMachine;
+
+struct QIMX25PDKMachine {
+    QOSGraphObject obj;
+    QGuestAllocator alloc;
+    IMXI2C i2c_1;
+};
+
+static void *imx25_pdk_get_driver(void *object, const char *interface)
+{
+    QIMX25PDKMachine *machine = object;
+    if (!g_strcmp0(interface, "memory")) {
+        return &machine->alloc;
+    }
+
+    fprintf(stderr, "%s not present in arm/imx25_pdk\n", interface);
+    g_assert_not_reached();
+}
+
+static QOSGraphObject *imx25_pdk_get_device(void *obj, const char *device)
+{
+    QIMX25PDKMachine *machine = obj;
+    if (!g_strcmp0(device, "imx.i2c")) {
+        return &machine->i2c_1.obj;
+    }
+
+    fprintf(stderr, "%s not present in arm/imx25_pdk\n", device);
+    g_assert_not_reached();
+}
+
+static void imx25_pdk_destructor(QOSGraphObject *obj)
+{
+    QIMX25PDKMachine *machine = (QIMX25PDKMachine *) obj;
+    alloc_destroy(&machine->alloc);
+}
+
+static void *qos_create_machine_arm_imx25_pdk(QTestState *qts)
+{
+    QIMX25PDKMachine *machine = g_new0(QIMX25PDKMachine, 1);
+
+    alloc_init(&machine->alloc, 0,
+               IMX25_PDK_RAM_START,
+               IMX25_PDK_RAM_END,
+               ARM_PAGE_SIZE);
+    machine->obj.get_device = imx25_pdk_get_device;
+    machine->obj.get_driver = imx25_pdk_get_driver;
+    machine->obj.destructor = imx25_pdk_destructor;
+
+    imx_i2c_init(&machine->i2c_1, qts, 0x43f80000);
+    return &machine->obj;
+}
+
+static void imx25_pdk_register_nodes(void)
+{
+    QOSGraphEdgeOptions edge = {
+        .extra_device_opts = "bus=i2c-bus.0"
+    };
+    qos_node_create_machine("arm/imx25-pdk", qos_create_machine_arm_imx25_pdk);
+    qos_node_contains("arm/imx25-pdk", "imx.i2c", &edge, NULL);
+}
+
+libqos_init(imx25_pdk_register_nodes);
diff --git a/tests/libqos/arm-n800-machine.c b/tests/libqos/arm-n800-machine.c
new file mode 100644
index 0000000000..87279bdb26
--- /dev/null
+++ b/tests/libqos/arm-n800-machine.c
@@ -0,0 +1,92 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2019 Red Hat, Inc.
+ *
+ * Author: Paolo Bonzini <pbonzini@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "libqos/malloc.h"
+#include "libqos/qgraph.h"
+#include "libqos/i2c.h"
+
+#define ARM_PAGE_SIZE            4096
+#define N800_RAM_START      0x80000000
+#define N800_RAM_END        0x88000000
+
+typedef struct QN800Machine QN800Machine;
+
+struct QN800Machine {
+    QOSGraphObject obj;
+    QGuestAllocator alloc;
+    OMAPI2C i2c_1;
+};
+
+static void *n800_get_driver(void *object, const char *interface)
+{
+    QN800Machine *machine = object;
+    if (!g_strcmp0(interface, "memory")) {
+        return &machine->alloc;
+    }
+
+    fprintf(stderr, "%s not present in arm/n800\n", interface);
+    g_assert_not_reached();
+}
+
+static QOSGraphObject *n800_get_device(void *obj, const char *device)
+{
+    QN800Machine *machine = obj;
+    if (!g_strcmp0(device, "omap_i2c")) {
+        return &machine->i2c_1.obj;
+    }
+
+    fprintf(stderr, "%s not present in arm/n800\n", device);
+    g_assert_not_reached();
+}
+
+static void n800_destructor(QOSGraphObject *obj)
+{
+    QN800Machine *machine = (QN800Machine *) obj;
+    alloc_destroy(&machine->alloc);
+}
+
+static void *qos_create_machine_arm_n800(QTestState *qts)
+{
+    QN800Machine *machine = g_new0(QN800Machine, 1);
+
+    alloc_init(&machine->alloc, 0,
+               N800_RAM_START,
+               N800_RAM_END,
+               ARM_PAGE_SIZE);
+    machine->obj.get_device = n800_get_device;
+    machine->obj.get_driver = n800_get_driver;
+    machine->obj.destructor = n800_destructor;
+
+    omap_i2c_init(&machine->i2c_1, qts, 0x48070000);
+    return &machine->obj;
+}
+
+static void n800_register_nodes(void)
+{
+    QOSGraphEdgeOptions edge = {
+        .extra_device_opts = "bus=i2c-bus.0"
+    };
+    qos_node_create_machine("arm/n800", qos_create_machine_arm_n800);
+    qos_node_contains("arm/n800", "omap_i2c", &edge, NULL);
+}
+
+libqos_init(n800_register_nodes);
diff --git a/tests/libqos/i2c-imx.c b/tests/libqos/i2c-imx.c
index 0945f2ecdc..f33ece55a3 100644
--- a/tests/libqos/i2c-imx.c
+++ b/tests/libqos/i2c-imx.c
@@ -30,13 +30,6 @@ enum IMXI2CDirection {
     IMX_I2C_WRITE,
 };
 
-typedef struct IMXI2C {
-    I2CAdapter parent;
-
-    uint64_t addr;
-} IMXI2C;
-
-
 static void imx_i2c_set_slave_addr(IMXI2C *s, uint8_t addr,
                                    enum IMXI2CDirection direction)
 {
@@ -47,7 +40,7 @@ static void imx_i2c_set_slave_addr(IMXI2C *s, uint8_t addr,
 static void imx_i2c_send(I2CAdapter *i2c, uint8_t addr,
                          const uint8_t *buf, uint16_t len)
 {
-    IMXI2C *s = (IMXI2C *)i2c;
+    IMXI2C *s = container_of(i2c, IMXI2C, parent);
     uint8_t data;
     uint8_t status;
     uint16_t size = 0;
@@ -107,7 +100,7 @@ static void imx_i2c_send(I2CAdapter *i2c, uint8_t addr,
 static void imx_i2c_recv(I2CAdapter *i2c, uint8_t addr,
                          uint8_t *buf, uint16_t len)
 {
-    IMXI2C *s = (IMXI2C *)i2c;
+    IMXI2C *s = container_of(i2c, IMXI2C, parent);
     uint8_t data;
     uint8_t status;
     uint16_t size = 0;
@@ -193,16 +186,31 @@ static void imx_i2c_recv(I2CAdapter *i2c, uint8_t addr,
     g_assert((status & I2SR_IBB) == 0);
 }
 
-I2CAdapter *imx_i2c_create(QTestState *qts, uint64_t addr)
+static void *imx_i2c_get_driver(void *obj, const char *interface)
 {
-    IMXI2C *s = g_malloc0(sizeof(*s));
-    I2CAdapter *i2c = (I2CAdapter *)s;
+    IMXI2C *s = obj;
+    if (!g_strcmp0(interface, "i2c-bus")) {
+        return &s->parent;
+    }
+    fprintf(stderr, "%s not present in imx-i2c\n", interface);
+    g_assert_not_reached();
+}
 
+void imx_i2c_init(IMXI2C *s, QTestState *qts, uint64_t addr)
+{
     s->addr = addr;
 
-    i2c->send = imx_i2c_send;
-    i2c->recv = imx_i2c_recv;
-    i2c->qts = qts;
+    s->obj.get_driver = imx_i2c_get_driver;
 
-    return i2c;
+    s->parent.send = imx_i2c_send;
+    s->parent.recv = imx_i2c_recv;
+    s->parent.qts = qts;
 }
+
+static void imx_i2c_register_nodes(void)
+{
+    qos_node_create_driver("imx.i2c", NULL);
+    qos_node_produces("imx.i2c", "i2c-bus");
+}
+
+libqos_init(imx_i2c_register_nodes);
diff --git a/tests/libqos/i2c-omap.c b/tests/libqos/i2c-omap.c
index 1ef6e7b200..9ae8214fa8 100644
--- a/tests/libqos/i2c-omap.c
+++ b/tests/libqos/i2c-omap.c
@@ -40,12 +40,6 @@ enum OMAPI2CCONBits {
     OMAP_I2C_CON_I2C_EN = 1 << 15,
 };
 
-typedef struct OMAPI2C {
-    I2CAdapter parent;
-
-    uint64_t addr;
-} OMAPI2C;
-
 
 static void omap_i2c_set_slave_addr(OMAPI2C *s, uint8_t addr)
 {
@@ -59,7 +53,7 @@ static void omap_i2c_set_slave_addr(OMAPI2C *s, uint8_t addr)
 static void omap_i2c_send(I2CAdapter *i2c, uint8_t addr,
                           const uint8_t *buf, uint16_t len)
 {
-    OMAPI2C *s = (OMAPI2C *)i2c;
+    OMAPI2C *s = container_of(i2c, OMAPI2C, parent);
     uint16_t data;
 
     omap_i2c_set_slave_addr(s, addr);
@@ -103,8 +97,9 @@ static void omap_i2c_send(I2CAdapter *i2c, uint8_t addr,
 static void omap_i2c_recv(I2CAdapter *i2c, uint8_t addr,
                           uint8_t *buf, uint16_t len)
 {
-    OMAPI2C *s = (OMAPI2C *)i2c;
+    OMAPI2C *s = container_of(i2c, OMAPI2C, parent);
     uint16_t data, stat;
+    uint16_t orig_len = len;
 
     omap_i2c_set_slave_addr(s, addr);
 
@@ -116,16 +111,24 @@ static void omap_i2c_recv(I2CAdapter *i2c, uint8_t addr,
            OMAP_I2C_CON_STT |
            OMAP_I2C_CON_STP;
     qtest_writew(i2c->qts, s->addr + OMAP_I2C_CON, data);
-    data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CON);
-    g_assert((data & OMAP_I2C_CON_STP) == 0);
 
     data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT);
     g_assert((data & OMAP_I2C_STAT_NACK) == 0);
 
-    data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CNT);
-    g_assert_cmpuint(data, ==, len);
-
     while (len > 0) {
+        data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CON);
+        if (len <= 4) {
+            g_assert((data & OMAP_I2C_CON_STP) == 0);
+
+            data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CNT);
+            g_assert_cmpuint(data, ==, orig_len);
+        } else {
+            g_assert((data & OMAP_I2C_CON_STP) != 0);
+
+            data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CNT);
+            g_assert_cmpuint(data, ==, len - 4);
+        }
+
         data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT);
         g_assert((data & OMAP_I2C_STAT_RRDY) != 0);
         g_assert((data & OMAP_I2C_STAT_ROVR) == 0);
@@ -152,21 +155,42 @@ static void omap_i2c_recv(I2CAdapter *i2c, uint8_t addr,
     g_assert((data & OMAP_I2C_CON_STP) == 0);
 }
 
-I2CAdapter *omap_i2c_create(QTestState *qts, uint64_t addr)
+static void *omap_i2c_get_driver(void *obj, const char *interface)
+{
+    OMAPI2C *s = obj;
+    if (!g_strcmp0(interface, "i2c-bus")) {
+        return &s->parent;
+    }
+    fprintf(stderr, "%s not present in omap_i2c\n", interface);
+    g_assert_not_reached();
+}
+
+static void omap_i2c_start_hw(QOSGraphObject *object)
 {
-    OMAPI2C *s = g_malloc0(sizeof(*s));
-    I2CAdapter *i2c = (I2CAdapter *)s;
+    OMAPI2C *s = (OMAPI2C *) object;
     uint16_t data;
 
+    /* verify the mmio address by looking for a known signature */
+    data = qtest_readw(s->parent.qts, s->addr + OMAP_I2C_REV);
+    g_assert_cmphex(data, ==, 0x34);
+}
+
+void omap_i2c_init(OMAPI2C *s, QTestState *qts, uint64_t addr)
+{
     s->addr = addr;
 
-    i2c->send = omap_i2c_send;
-    i2c->recv = omap_i2c_recv;
-    i2c->qts = qts;
+    s->obj.get_driver = omap_i2c_get_driver;
+    s->obj.start_hw = omap_i2c_start_hw;
 
-    /* verify the mmio address by looking for a known signature */
-    data = qtest_readw(qts, addr + OMAP_I2C_REV);
-    g_assert_cmphex(data, ==, 0x34);
+    s->parent.send = omap_i2c_send;
+    s->parent.recv = omap_i2c_recv;
+    s->parent.qts = qts;
+}
 
-    return i2c;
+static void omap_i2c_register_nodes(void)
+{
+    qos_node_create_driver("omap_i2c", NULL);
+    qos_node_produces("omap_i2c", "i2c-bus");
 }
+
+libqos_init(omap_i2c_register_nodes);
diff --git a/tests/libqos/i2c.c b/tests/libqos/i2c.c
index 23bc2a3eb2..156114e745 100644
--- a/tests/libqos/i2c.c
+++ b/tests/libqos/i2c.c
@@ -10,14 +10,76 @@
 #include "libqos/i2c.h"
 #include "libqtest.h"
 
-void i2c_send(I2CAdapter *i2c, uint8_t addr,
-              const uint8_t *buf, uint16_t len)
+void i2c_send(QI2CDevice *i2cdev, const uint8_t *buf, uint16_t len)
 {
-    i2c->send(i2c, addr, buf, len);
+    i2cdev->bus->send(i2cdev->bus, i2cdev->addr, buf, len);
 }
 
-void i2c_recv(I2CAdapter *i2c, uint8_t addr,
-              uint8_t *buf, uint16_t len)
+void i2c_recv(QI2CDevice *i2cdev, uint8_t *buf, uint16_t len)
 {
-    i2c->recv(i2c, addr, buf, len);
+    i2cdev->bus->recv(i2cdev->bus, i2cdev->addr, buf, len);
+}
+
+void i2c_read_block(QI2CDevice *i2cdev, uint8_t reg,
+                    uint8_t *buf, uint16_t len)
+{
+    i2c_send(i2cdev, &reg, 1);
+    i2c_recv(i2cdev, buf, len);
+}
+
+void i2c_write_block(QI2CDevice *i2cdev, uint8_t reg,
+                     const uint8_t *buf, uint16_t len)
+{
+    uint8_t *cmd = g_malloc(len + 1);
+    cmd[0] = reg;
+    memcpy(&cmd[1], buf, len);
+    i2c_send(i2cdev, cmd, len + 1);
+    g_free(cmd);
+}
+
+uint8_t i2c_get8(QI2CDevice *i2cdev, uint8_t reg)
+{
+    uint8_t resp[1];
+    i2c_read_block(i2cdev, reg, resp, sizeof(resp));
+    return resp[0];
+}
+
+uint16_t i2c_get16(QI2CDevice *i2cdev, uint8_t reg)
+{
+    uint8_t resp[2];
+    i2c_read_block(i2cdev, reg, resp, sizeof(resp));
+    return (resp[0] << 8) | resp[1];
+}
+
+void i2c_set8(QI2CDevice *i2cdev, uint8_t reg, uint8_t value)
+{
+    i2c_write_block(i2cdev, reg, &value, 1);
+}
+
+void i2c_set16(QI2CDevice *i2cdev, uint8_t reg, uint16_t value)
+{
+    uint8_t data[2];
+
+    data[0] = value >> 8;
+    data[1] = value & 255;
+    i2c_write_block(i2cdev, reg, data, sizeof(data));
+}
+
+void *i2c_device_create(void *i2c_bus, QGuestAllocator *alloc, void *addr)
+{
+    QI2CDevice *i2cdev = g_new0(QI2CDevice, 1);
+
+    i2cdev->bus = i2c_bus;
+    if (addr) {
+        i2cdev->addr = ((QI2CAddress *)addr)->addr;
+    }
+    return &i2cdev->obj;
+}
+
+void add_qi2c_address(QOSGraphEdgeOptions *opts, QI2CAddress *addr)
+{
+    g_assert(addr);
+
+    opts->arg = addr;
+    opts->size_arg = sizeof(QI2CAddress);
 }
diff --git a/tests/libqos/i2c.h b/tests/libqos/i2c.h
index cc01358a9f..945b65b34c 100644
--- a/tests/libqos/i2c.h
+++ b/tests/libqos/i2c.h
@@ -10,6 +10,7 @@
 #define LIBQOS_I2C_H
 
 #include "libqtest.h"
+#include "libqos/qgraph.h"
 
 typedef struct I2CAdapter I2CAdapter;
 struct I2CAdapter {
@@ -21,17 +22,61 @@ struct I2CAdapter {
     QTestState *qts;
 };
 
-#define OMAP2_I2C_1_BASE 0x48070000
+typedef struct QI2CAddress QI2CAddress;
+struct QI2CAddress {
+    uint8_t addr;
+};
+
+typedef struct QI2CDevice QI2CDevice;
+struct QI2CDevice {
+    /*
+     * For now, all devices are simple enough that there is no need for
+     * them to define their own constructor and get_driver functions.
+     * Therefore, QOSGraphObject is included directly in QI2CDevice;
+     * the tests expect to get a QI2CDevice rather than doing something
+     * like obj->get_driver("i2c-device").
+     *
+     * In fact there is no i2c-device interface even, because there are
+     * no generic I2C tests).
+     */
+    QOSGraphObject obj;
+    I2CAdapter *bus;
+    uint8_t addr;
+};
+
+void *i2c_device_create(void *i2c_bus, QGuestAllocator *alloc, void *addr);
+void add_qi2c_address(QOSGraphEdgeOptions *opts, QI2CAddress *addr);
+
+void i2c_send(QI2CDevice *dev, const uint8_t *buf, uint16_t len);
+void i2c_recv(QI2CDevice *dev, uint8_t *buf, uint16_t len);
+
+void i2c_read_block(QI2CDevice *dev, uint8_t reg,
+                    uint8_t *buf, uint16_t len);
+void i2c_write_block(QI2CDevice *dev, uint8_t reg,
+                     const uint8_t *buf, uint16_t len);
+uint8_t i2c_get8(QI2CDevice *dev, uint8_t reg);
+uint16_t i2c_get16(QI2CDevice *dev, uint8_t reg);
+void i2c_set8(QI2CDevice *dev, uint8_t reg, uint8_t value);
+void i2c_set16(QI2CDevice *dev, uint8_t reg, uint16_t value);
+
+/* i2c-omap.c */
+typedef struct OMAPI2C {
+    QOSGraphObject obj;
+    I2CAdapter parent;
+
+    uint64_t addr;
+} OMAPI2C;
+
+void omap_i2c_init(OMAPI2C *s, QTestState *qts, uint64_t addr);
 
-void i2c_send(I2CAdapter *i2c, uint8_t addr,
-              const uint8_t *buf, uint16_t len);
-void i2c_recv(I2CAdapter *i2c, uint8_t addr,
-              uint8_t *buf, uint16_t len);
+/* i2c-imx.c */
+typedef struct IMXI2C {
+    QOSGraphObject obj;
+    I2CAdapter parent;
 
-/* libi2c-omap.c */
-I2CAdapter *omap_i2c_create(QTestState *qts, uint64_t addr);
+    uint64_t addr;
+} IMXI2C;
 
-/* libi2c-imx.c */
-I2CAdapter *imx_i2c_create(QTestState *qts, uint64_t addr);
+void imx_i2c_init(IMXI2C *s, QTestState *qts, uint64_t addr);
 
 #endif
diff --git a/tests/libqos/qgraph.c b/tests/libqos/qgraph.c
index b149caaaa9..7a7ae2a19e 100644
--- a/tests/libqos/qgraph.c
+++ b/tests/libqos/qgraph.c
@@ -632,15 +632,19 @@ void qos_node_create_driver(const char *name, QOSCreateDriverFunc function)
 }
 
 void qos_node_contains(const char *container, const char *contained,
-                       ...)
+                       QOSGraphEdgeOptions *opts, ...)
 {
     va_list va;
-    va_start(va, contained);
-    QOSGraphEdgeOptions *opts;
 
+    if (opts == NULL) {
+        add_edge(container, contained, QEDGE_CONTAINS, NULL);
+        return;
+    }
+
+    va_start(va, opts);
     do {
-        opts = va_arg(va, QOSGraphEdgeOptions *);
         add_edge(container, contained, QEDGE_CONTAINS, opts);
+        opts = va_arg(va, QOSGraphEdgeOptions *);
     } while (opts != NULL);
 
     va_end(va);
diff --git a/tests/libqos/qgraph.h b/tests/libqos/qgraph.h
index e799095b30..3a25dda4b2 100644
--- a/tests/libqos/qgraph.h
+++ b/tests/libqos/qgraph.h
@@ -453,14 +453,16 @@ void qos_node_create_machine_args(const char *name,
 void qos_node_create_driver(const char *name, QOSCreateDriverFunc function);
 
 /**
- * qos_node_contains(): creates an edge of type QEDGE_CONTAINS and
- * adds it to the edge list mapped to @container in the
+ * qos_node_contains(): creates one or more edges of type QEDGE_CONTAINS
+ * and adds them to the edge list mapped to @container in the
  * edge hash table.
  *
- * This edge will have @container as source and @contained as destination.
+ * The edges will have @container as source and @contained as destination.
  *
- * It also has the possibility to add optional NULL-terminated
- * @opts parameters (see %QOSGraphEdgeOptions)
+ * If @opts is NULL, a single edge will be added with no options.
+ * If @opts is non-NULL, the arguments after @contained represent a
+ * NULL-terminated list of %QOSGraphEdgeOptions structs, and an
+ * edge will be added for each of them.
  *
  * This function can be useful when there are multiple devices
  * with the same node name contained in a machine/other node
@@ -480,7 +482,8 @@ void qos_node_create_driver(const char *name, QOSCreateDriverFunc function);
  * For contains, op1.arg and op1.size_arg represent the arg to pass
  * to @contained constructor to properly initialize it.
  */
-void qos_node_contains(const char *container, const char *contained, ...);
+void qos_node_contains(const char *container, const char *contained,
+                       QOSGraphEdgeOptions *opts, ...);
 
 /**
  * qos_node_produces(): creates an edge of type QEDGE_PRODUCES and