From: Valentin Ghita <[email protected]>
Add option to allow for connecting device GPIOs. This is useful when
adding a peripheral device from the command line which uses an
interrupt.
It takes the following options:
* in-dev-path, out-dev-path - required, the device canonical object
path (e.g. /machine/peripheral-anon/device[0],
/machine/iotkit/cluster0/armv7m0) for the devices that should have
their in <-> out GPIOs connected
* in-gpio-name, out-gpio-name - optional, the name of the GPIO list;
if not specified, the unnamed GPIO list is used
* in-gpio-index, out-gpio-index - optional, the index in the GPIO list
that identifies the GPIO to be used; if not specified 0 (the first
GPIO in the list) is used
Usage example:
# add the tmp105 sensor and connects its irq line to the CPU
qemu-system-arm \
--machine mps2-an505 \
--device tmp105,bus=/versatile_i2c/i2c,address=0x50 \
--connect-gpios out-dev-path=/machine/peripheral-anon/device[0],\
in-dev-path=/machine/iotkit/cluster0/armv7m0,in-gpio-index=100
Signed-off-by: Valentin Ghita <[email protected]>
[tavip: pass device path, gpio name and gpio index as explicit
options, use --connect-gpios instead of --connect-pins, use
object_resolve_path instead of custom search based on qbus_find]
Signed-off-by: Octavian Purdila <[email protected]>
---
include/monitor/qdev.h | 3 ++
include/sysemu/sysemu.h | 1 +
monitor/qmp-cmds.c | 3 ++
system/qdev-monitor.c | 103 ++++++++++++++++++++++++++++++++++++++++
system/vl.c | 24 ++++++++++
qemu-options.hx | 31 ++++++++++++
6 files changed, 165 insertions(+)
diff --git a/include/monitor/qdev.h b/include/monitor/qdev.h
index 1d57bf6577..dc05b70146 100644
--- a/include/monitor/qdev.h
+++ b/include/monitor/qdev.h
@@ -6,6 +6,7 @@
void hmp_info_qtree(Monitor *mon, const QDict *qdict);
void hmp_info_qdm(Monitor *mon, const QDict *qdict);
void qmp_device_add(QDict *qdict, QObject **ret_data, Error **errp);
+void qmp_connect_gpios(QDict *qdict, QObject **ret_data, Error **errp);
int qdev_device_help(QemuOpts *opts);
DeviceState *qdev_device_add(QemuOpts *opts, Error **errp);
@@ -36,4 +37,6 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts,
*/
const char *qdev_set_id(DeviceState *dev, char *id, Error **errp);
+int qdev_connect_gpios(QemuOpts *opts, Error **errp);
+
#endif
diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
index 5b4397eeb8..66c5f5129e 100644
--- a/include/sysemu/sysemu.h
+++ b/include/sysemu/sysemu.h
@@ -106,6 +106,7 @@ extern QemuOptsList qemu_drive_opts;
extern QemuOptsList bdrv_runtime_opts;
extern QemuOptsList qemu_chardev_opts;
extern QemuOptsList qemu_device_opts;
+extern QemuOptsList qemu_connect_gpios_opts;
extern QemuOptsList qemu_netdev_opts;
extern QemuOptsList qemu_nic_opts;
extern QemuOptsList qemu_net_opts;
diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c
index f84a0dc523..3333598a5b 100644
--- a/monitor/qmp-cmds.c
+++ b/monitor/qmp-cmds.c
@@ -203,6 +203,9 @@ static void __attribute__((__constructor__))
monitor_init_qmp_commands(void)
qmp_register_command(&qmp_commands, "device_add",
qmp_device_add, 0, 0);
+ qmp_register_command(&qmp_commands, "connect_gpios",
+ qmp_connect_gpios, 0, 0);
+
QTAILQ_INIT(&qmp_cap_negotiation_commands);
qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities",
qmp_marshal_qmp_capabilities,
diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c
index 44994ea0e1..16d417610a 100644
--- a/system/qdev-monitor.c
+++ b/system/qdev-monitor.c
@@ -735,6 +735,68 @@ err_del_dev:
return NULL;
}
+static DeviceState *qdev_from_opt(QemuOpts *opts, const char *name,
+ Error **errp)
+{
+ Object *obj;
+ const char *path = qemu_opt_get(opts, name);
+
+ if (!path) {
+ error_setg(errp, QERR_MISSING_PARAMETER, name);
+ return NULL;
+ }
+
+ obj = object_resolve_path(path, NULL);
+ if (!obj) {
+ error_setg(errp, "Could not resolve path: %s", path);
+ return NULL;
+ }
+
+ if (!object_dynamic_cast(obj, TYPE_DEVICE)) {
+ error_setg(errp, "%s is not a device", path);
+ return NULL;
+ }
+
+ return DEVICE(obj);
+}
+
+int qdev_connect_gpios(QemuOpts *opts, Error **errp)
+{
+ qemu_irq in_irq;
+ const char *in_gpio_name, *out_gpio_name;
+ int in_gpio_idx = 0, out_gpio_idx = 0;
+ DeviceState *dev;
+
+ dev = qdev_from_opt(opts, "in-dev-path", errp);
+ if (!dev) {
+ return -1;
+ }
+
+ in_gpio_name = qemu_opt_get(opts, "in-gpio-name");
+ out_gpio_name = qemu_opt_get(opts, "out-gpio-name");
+ in_gpio_idx = qemu_opt_get_number(opts, "out-gpio-index", 0);
+ out_gpio_idx = qemu_opt_get_number(opts, "out-gpio-index", 0);
+
+ if (in_gpio_name) {
+ in_irq = qdev_get_gpio_in_named(dev, in_gpio_name, in_gpio_idx);
+ } else {
+ in_irq = qdev_get_gpio_in(dev, in_gpio_idx);
+ }
+
+ dev = qdev_from_opt(opts, "out-dev-path", errp);
+ if (!dev) {
+ return -1;
+ }
+
+ if (out_gpio_name) {
+ qdev_connect_gpio_out_named(dev, out_gpio_name, out_gpio_idx, in_irq);
+ } else {
+ qdev_connect_gpio_out(dev, out_gpio_idx, in_irq);
+ }
+
+ return 0;
+}
+
/* Takes ownership of @opts on success */
DeviceState *qdev_device_add(QemuOpts *opts, Error **errp)
{
@@ -885,6 +947,20 @@ void qmp_device_add(QDict *qdict, QObject **ret_data,
Error **errp)
object_unref(OBJECT(dev));
}
+void qmp_connect_gpios(QDict *qdict, QObject **ret_data, Error **errp)
+{
+ QemuOpts *opts;
+
+ opts = qemu_opts_from_qdict(qemu_find_opts("connect-gpios"), qdict, errp);
+ if (!opts) {
+ return;
+ }
+
+ qdev_connect_gpios(opts, errp);
+
+ qemu_opts_del(opts);
+}
+
static DeviceState *find_device_state(const char *id, Error **errp)
{
Object *obj = object_resolve_path_at(qdev_get_peripheral(), id);
@@ -1102,6 +1178,33 @@ QemuOptsList qemu_device_opts = {
},
};
+QemuOptsList qemu_connect_gpios_opts = {
+ .name = "connect-gpios",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_connect_gpios_opts.head),
+ .desc = {
+ {
+ .name = "in-dev-path",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "in-gpio-name",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "in-gpio-index",
+ .type = QEMU_OPT_NUMBER,
+ },{
+ .name = "out-dev-path",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "out-gpio-name",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "out-gpio-index",
+ .type = QEMU_OPT_NUMBER,
+ },
+ { /* end of list */ }
+ },
+};
+
QemuOptsList qemu_global_opts = {
.name = "global",
.head = QTAILQ_HEAD_INITIALIZER(qemu_global_opts.head),
diff --git a/system/vl.c b/system/vl.c
index fe547ca47c..c36562fbeb 100644
--- a/system/vl.c
+++ b/system/vl.c
@@ -1222,6 +1222,19 @@ static int device_init_func(void *opaque, QemuOpts
*opts, Error **errp)
return 0;
}
+static int connect_gpios_func(void *opaque, QemuOpts *opts, Error **errp)
+{
+ int err;
+
+ err = qdev_connect_gpios(opts, errp);
+ if (err != 0 && *errp) {
+ error_report_err(*errp);
+ return err;
+ }
+
+ return 0;
+}
+
static int chardev_init_func(void *opaque, QemuOpts *opts, Error **errp)
{
Error *local_err = NULL;
@@ -2665,6 +2678,10 @@ static void qemu_create_cli_devices(void)
object_unref(OBJECT(dev));
loc_pop(&opt->loc);
}
+
+ qemu_opts_foreach(qemu_find_opts("connect-gpios"),
+ connect_gpios_func, NULL, &error_fatal);
+
rom_reset_order_override();
}
@@ -2765,6 +2782,7 @@ void qemu_init(int argc, char **argv)
qemu_add_drive_opts(&bdrv_runtime_opts);
qemu_add_opts(&qemu_chardev_opts);
qemu_add_opts(&qemu_device_opts);
+ qemu_add_opts(&qemu_connect_gpios_opts);
qemu_add_opts(&qemu_netdev_opts);
qemu_add_opts(&qemu_nic_opts);
qemu_add_opts(&qemu_net_opts);
@@ -3373,6 +3391,12 @@ void qemu_init(int argc, char **argv)
}
}
break;
+ case QEMU_OPTION_connect_gpios:
+ if (!qemu_opts_parse_noisily(qemu_find_opts("connect-gpios"),
+ optarg, false)) {
+ exit(1);
+ }
+ break;
case QEMU_OPTION_smp:
machine_parse_property_opt(qemu_find_opts("smp-opts"),
"smp", optarg);
diff --git a/qemu-options.hx b/qemu-options.hx
index 20a1ce0d43..011aa66569 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1187,6 +1187,37 @@ SRST
ERST
+DEF("connect-gpios", HAS_ARG, QEMU_OPTION_connect_gpios,
+ "-connect-gpios
in-dev-path=path,out-dev-path=path[,in-gpio-name=name][,out-gpio-name=name][,in-gpio-index=index][,out-gpio-index=index]\n"
+ " connect an input and an output gpio.\n",
+ QEMU_ARCH_ALL)
+SRST
+``-connect-gpios
in-dev-path=path,out-dev-path=path[,in-gpio-name=name][,out-gpio-name=name][,in-gpio-index=index][,out-gpio-index=index]``
+ Connect an input and an output gpio.
+
+ ``in-dev-path``, ``out-dev-path`` required, the device canonical
+ object path (e.g. /machine/peripheral-anon/device[0],
+ /machine/iotkit/cluster0/armv7m0) for the devices that should
+ have their in <-> out GPIOs connected
+
+ ``in-gpio-name``, ``out-gpio-name`` optional, the name of the GPIO list;
+ if not specified, the unnamed GPIO list is used
+
+ ``in-gpio-index``, ``out-gpio-index`` optional, the index in the GPIO list
+ that identifies the GPIO to be used; if not specified 0 (the first
+ GPIO in the list) is used
+
+ Examples:
+
+ .. parsed-literal::
+
+ # add the tmp105 sensor and connects its irq line to the CPU
+ qemu-system-arm \\
+ --machine mps2-an505 \\
+ --device tmp105,bus=/versatile_i2c/i2c,address=0x50 \\
+ --connect-gpios
out-dev-path=/machine/peripheral-anon/device[0],in-dev-path=/machine/iotkit/cluster0/armv7m0,in-gpio-index=100
+ERST
+
DEF("name", HAS_ARG, QEMU_OPTION_name,
"-name string1[,process=string2][,debug-threads=on|off]\n"
" set the name of the guest\n"
--
2.47.0.rc1.288.g06298d1525-goog