Signed-off-by: Kristina Hanicova <khanicov(a)redhat.com>
---
docs/formatdomain.rst | 37 ++++++++-----
docs/schemas/domaincommon.rng | 20 +++++++
src/conf/domain_audit.c | 1 +
src/conf/domain_conf.c | 69 ++++++++++++++++++++----
src/conf/domain_conf.h | 12 +++++
src/conf/domain_validate.c | 8 +++
src/libvirt_private.syms | 2 +
src/qemu/qemu_cgroup.c | 2 +
src/qemu/qemu_command.c | 1 +
src/qemu/qemu_domain_address.c | 1 +
src/qemu/qemu_hotplug.c | 1 +
src/qemu/qemu_validate.c | 6 +++
src/security/security_apparmor.c | 1 +
src/security/security_dac.c | 2 +
src/security/security_selinux.c | 2 +
src/security/virt-aa-helper.c | 3 +-
tests/qemuxml2argvdata/input-linux.xml | 31 +++++++++++
tests/qemuxml2xmloutdata/input-linux.xml | 1 +
tests/qemuxml2xmltest.c | 2 +
19 files changed, 179 insertions(+), 23 deletions(-)
create mode 100644 tests/qemuxml2argvdata/input-linux.xml
create mode 120000 tests/qemuxml2xmloutdata/input-linux.xml
diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst
index fa5c14febc..e115165ad2 100644
--- a/docs/formatdomain.rst
+++ b/docs/formatdomain.rst
@@ -5728,26 +5728,37 @@ to provide a graphics tablet for absolute cursor movement.
<input type='passthrough' bus='virtio'>
<source evdev='/dev/input/event1'/>
</input>
+ <input type='evdev'>
+ <source dev='/dev/input/event1234' grab='all'
repeat='on'/>
+ </input>
</devices>
...
``input``
The ``input`` element has one mandatory attribute, the ``type`` whose value
- can be 'mouse', 'tablet', ( :since:`since 1.2.2` ) 'keyboard'
or (
- :since:`since 1.3.0` ) 'passthrough'. The tablet provides absolute cursor
- movement, while the mouse uses relative movement. The optional ``bus``
- attribute can be used to refine the exact device type. It takes values
"xen"
- (paravirtualized), "ps2" and "usb" or ( :since:`since 1.3.0` )
"virtio".
+ can be 'mouse', 'tablet', ( :since:`since 1.2.2` ) 'keyboard',
(
+ :since:`since 1.3.0` ) 'passthrough' or ( :since:`since 7.4.0` )
'evdev'.
+ The tablet provides absolute cursor movement, while the mouse uses relative
+ movement. The optional ``bus`` attribute can be used to refine the exact
+ device type. It takes values "xen" (paravirtualized), "ps2" and
"usb" or (
+ :since:`since 1.3.0` ) "virtio".
The ``input`` element has an optional sub-element ``<address>`` which can tie
-the device to a particular PCI slot, `documented above <#elementsAddress>`__. On
-S390, ``address`` can be used to provide a CCW address for an input device (
-:since:`since 4.2.0` ). For type ``passthrough``, the mandatory sub-element
-``source`` must have an ``evdev`` attribute containing the absolute path to the
-event device passed through to guests. (KVM only) :since:`Since 5.2.0` , the
-``input`` element accepts a ``model`` attribute which has the values 'virtio',
-'virtio-transitional' and 'virtio-non-transitional'. See `Virtio
transitional
-devices <#elementsVirtioTransitional>`__ for more details.
+the device to a particular PCI slot, `documented above <#elementsAddress>`__.
+On S390, ``address`` can be used to provide a CCW address for an input device (
+:since:`since 4.2.0` ). For types ``passthrough`` and ``evdev``, the mandatory
+sub-element ``source`` must have an ``evdev`` (for ``passthrough``) or ``dev``
+(for ``evdev``) attribute containing the absolute path to the event device
+passed through to guests.
+For type ``evdev``, ``source`` can have two optional attributes ``grab`` with
+value 'all' which when enabled grabs all input devices instead of just one and
+``repeat`` with value 'on'/'off' to enable/disable auto-repeat events (
+:since:`Since 7.4.0`).
+``input`` type ``evdev`` is currently supported only on linux devices.
+(KVM only) :since:`Since 5.2.0` , the ``input`` element accepts a
+``model`` attribute which has the values 'virtio', 'virtio-transitional'
and
+'virtio-non-transitional'. See `Virtio transitional devices
+<#elementsVirtioTransitional>`__ for more details.
The subelement ``driver`` can be used to tune the virtio options of the device:
`Virtio-specific options <#elementsVirtio>`__ can also be set. ( :since:`Since
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index a2e5c50c1d..16e1c549c2 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -5443,6 +5443,26 @@
</attribute>
</element>
</group>
+ <group>
+ <attribute name="type">
+ <value>evdev</value>
+ </attribute>
+ <element name="source">
+ <attribute name="dev">
+ <ref name="absFilePath"/>
+ </attribute>
+ <optional>
+ <attribute name="grab">
+ <value>all</value>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="repeat">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ </element>
+ </group>
</choice>
<optional>
<attribute name="model">
diff --git a/src/conf/domain_audit.c b/src/conf/domain_audit.c
index b1226fd5dc..b08233fa49 100644
--- a/src/conf/domain_audit.c
+++ b/src/conf/domain_audit.c
@@ -956,6 +956,7 @@ virDomainAuditInput(virDomainObj *vm,
break;
case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH:
+ case VIR_DOMAIN_INPUT_TYPE_EVDEV:
VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success,
"virt=%s resrc=evdev reason=%s %s uuid=%s path=%s",
virt, reason, vmname, uuidstr, VIR_AUDIT_STR(input->source.evdev));
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index e35c38caa3..bb21c9ca50 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -864,6 +864,7 @@ VIR_ENUM_IMPL(virDomainInput,
"tablet",
"keyboard",
"passthrough",
+ "evdev",
);
VIR_ENUM_IMPL(virDomainInputBus,
@@ -873,6 +874,7 @@ VIR_ENUM_IMPL(virDomainInputBus,
"xen",
"parallels",
"virtio",
+ "none",
);
VIR_ENUM_IMPL(virDomainInputModel,
@@ -883,6 +885,12 @@ VIR_ENUM_IMPL(virDomainInputModel,
"virtio-non-transitional",
);
+VIR_ENUM_IMPL(virDomainInputSourceGrab,
+ VIR_DOMAIN_INPUT_SOURCE_GRAB_LAST,
+ "default",
+ "all",
+);
+
VIR_ENUM_IMPL(virDomainGraphics,
VIR_DOMAIN_GRAPHICS_TYPE_LAST,
"sdl",
@@ -1879,6 +1887,7 @@ const char *virDomainInputDefGetPath(virDomainInputDef *input)
return NULL;
case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH:
+ case VIR_DOMAIN_INPUT_TYPE_EVDEV:
return input->source.evdev;
}
return NULL;
@@ -11819,10 +11828,10 @@ virDomainInputDefParseXML(virDomainXMLOption *xmlopt,
{
VIR_XPATH_NODE_AUTORESTORE(ctxt)
virDomainInputDef *def;
- g_autofree char *evdev = NULL;
g_autofree char *type = NULL;
g_autofree char *bus = NULL;
g_autofree char *model = NULL;
+ xmlNodePtr source = NULL;
def = g_new0(virDomainInputDef, 1);
@@ -11924,6 +11933,8 @@ virDomainInputDefParseXML(virDomainXMLOption *xmlopt,
} else if (ARCH_IS_S390(dom->os.arch) ||
def->type == VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH) {
def->bus = VIR_DOMAIN_INPUT_BUS_VIRTIO;
+ } else if (def->type == VIR_DOMAIN_INPUT_TYPE_EVDEV) {
+ def->bus = VIR_DOMAIN_INPUT_BUS_NONE;
} else {
def->bus = VIR_DOMAIN_INPUT_BUS_USB;
}
@@ -11948,12 +11959,36 @@ virDomainInputDefParseXML(virDomainXMLOption *xmlopt,
goto error;
}
- if ((evdev = virXPathString("string(./source/@evdev)", ctxt)))
- def->source.evdev = virFileSanitizePath(evdev);
- if (def->type == VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH &&
!def->source.evdev) {
- virReportError(VIR_ERR_XML_ERROR, "%s",
- _("Missing evdev path for input device passthrough"));
- goto error;
+ if ((source = virXPathNode("./source", ctxt))) {
+ g_autofree char *evdev = NULL;
+
+ if (def->type == VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH)
+ evdev = virXMLPropString(source, "evdev");
+ else if (def->type == VIR_DOMAIN_INPUT_TYPE_EVDEV)
+ evdev = virXMLPropString(source, "dev");
+
+ if (evdev)
+ def->source.evdev = virFileSanitizePath(evdev);
+
+ if (def->type == VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH ||
+ def->type == VIR_DOMAIN_INPUT_TYPE_EVDEV) {
+ if (!def->source.evdev) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Missing evdev path for input device"));
+ goto error;
+ }
+ }
+
+ if (def->type == VIR_DOMAIN_INPUT_TYPE_EVDEV) {
+ if (virXMLPropEnum(source, "grab",
+ virDomainInputSourceGrabTypeFromString,
+ VIR_XML_PROP_NONZERO, &def->source.grab) < 0)
+ goto error;
+
+ if (virXMLPropTristateSwitch(source, "repeat",
+ VIR_XML_PROP_NONE, &def->source.repeat)
< 0)
+ goto error;
+ }
}
if (virDomainVirtioOptionsParseXML(virXPathNode("./driver", ctxt),
@@ -25912,9 +25947,12 @@ virDomainInputDefFormat(virBuffer *buf,
{
const char *type = virDomainInputTypeToString(def->type);
const char *bus = virDomainInputBusTypeToString(def->bus);
+ const char *grab = virDomainInputSourceGrabTypeToString(def->source.grab);
+ const char *repeat = virTristateSwitchTypeToString(def->source.repeat);
g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf);
g_auto(virBuffer) driverAttrBuf = VIR_BUFFER_INITIALIZER;
+ g_auto(virBuffer) sourceAttrBuf = VIR_BUFFER_INITIALIZER;
/* don't format keyboard into migratable XML for backward compatibility */
if (flags & VIR_DOMAIN_DEF_FORMAT_MIGRATABLE &&
@@ -25934,7 +25972,9 @@ virDomainInputDefFormat(virBuffer *buf,
return -1;
}
- virBufferAsprintf(&attrBuf, " type='%s' bus='%s'",
type, bus);
+ virBufferAsprintf(&attrBuf, " type='%s'", type);
+ if (def->bus != VIR_DOMAIN_INPUT_BUS_NONE)
+ virBufferAsprintf(&attrBuf, " bus='%s'", bus);
if (def->model) {
const char *model = virDomainInputModelTypeToString(def->model);
@@ -25952,7 +25992,18 @@ virDomainInputDefFormat(virBuffer *buf,
virXMLFormatElement(&childBuf, "driver", &driverAttrBuf, NULL);
- virBufferEscapeString(&childBuf, "<source
evdev='%s'/>\n", def->source.evdev);
+ if (def->type == VIR_DOMAIN_INPUT_TYPE_EVDEV)
+ virBufferEscapeString(&sourceAttrBuf, " dev='%s'",
def->source.evdev);
+ else
+ virBufferEscapeString(&sourceAttrBuf, " evdev='%s'",
def->source.evdev);
+
+ if (def->source.grab)
+ virBufferAsprintf(&sourceAttrBuf, " grab='%s'", grab);
+ if (def->source.repeat)
+ virBufferAsprintf(&sourceAttrBuf, " repeat='%s'", repeat);
+
+ virXMLFormatElement(&childBuf, "source", &sourceAttrBuf, NULL);
+
virDomainDeviceInfoFormat(&childBuf, &def->info, flags);
virXMLFormatElement(buf, "input", &attrBuf, &childBuf);
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index fab856a5c7..8d64d4a1e2 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -1387,6 +1387,7 @@ typedef enum {
VIR_DOMAIN_INPUT_TYPE_TABLET,
VIR_DOMAIN_INPUT_TYPE_KBD,
VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH,
+ VIR_DOMAIN_INPUT_TYPE_EVDEV,
VIR_DOMAIN_INPUT_TYPE_LAST
} virDomainInputType;
@@ -1397,6 +1398,7 @@ typedef enum {
VIR_DOMAIN_INPUT_BUS_XEN,
VIR_DOMAIN_INPUT_BUS_PARALLELS, /* pseudo device for VNC in containers */
VIR_DOMAIN_INPUT_BUS_VIRTIO,
+ VIR_DOMAIN_INPUT_BUS_NONE,
VIR_DOMAIN_INPUT_BUS_LAST
} virDomainInputBus;
@@ -1410,12 +1412,21 @@ typedef enum {
VIR_DOMAIN_INPUT_MODEL_LAST
} virDomainInputModel;
+typedef enum {
+ VIR_DOMAIN_INPUT_SOURCE_GRAB_DEFAULT,
+ VIR_DOMAIN_INPUT_SOURCE_GRAB_ALL,
+
+ VIR_DOMAIN_INPUT_SOURCE_GRAB_LAST
+} virDomainInputSourceGrab;
+
struct _virDomainInputDef {
int type;
int bus;
int model; /* virDomainInputModel */
struct {
char *evdev;
+ virDomainInputSourceGrab grab;
+ virTristateSwitch repeat;
} source;
virDomainDeviceInfo info;
virDomainVirtioOptions *virtio;
@@ -3844,6 +3855,7 @@ VIR_ENUM_DECL(virDomainRedirdevBus);
VIR_ENUM_DECL(virDomainInput);
VIR_ENUM_DECL(virDomainInputBus);
VIR_ENUM_DECL(virDomainInputModel);
+VIR_ENUM_DECL(virDomainInputSourceGrab);
VIR_ENUM_DECL(virDomainGraphics);
VIR_ENUM_DECL(virDomainGraphicsListen);
VIR_ENUM_DECL(virDomainGraphicsAuthConnected);
diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c
index 686b9e8d16..98202a3adc 100644
--- a/src/conf/domain_validate.c
+++ b/src/conf/domain_validate.c
@@ -1961,6 +1961,14 @@ virDomainInputDefValidate(const virDomainInputDef *input)
}
break;
+ case VIR_DOMAIN_INPUT_TYPE_EVDEV:
+ if (input->bus != VIR_DOMAIN_INPUT_BUS_NONE) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("input evdev doesn't support bus
element"));
+ return -1;
+ }
+ break;
+
case VIR_DOMAIN_INPUT_TYPE_LAST:
default:
virReportEnumRangeError(virDomainInputType, input->type);
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 6fbdee4124..7c7627052b 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -475,6 +475,8 @@ virDomainInputBusTypeToString;
virDomainInputDefFind;
virDomainInputDefFree;
virDomainInputDefGetPath;
+virDomainInputSourceGrabTypeFromString;
+virDomainInputSourceGrabTypeToString;
virDomainInputTypeToString;
virDomainIOMMUModelTypeFromString;
virDomainIOMMUModelTypeToString;
diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c
index 0e8835fb86..038d6478b2 100644
--- a/src/qemu/qemu_cgroup.c
+++ b/src/qemu/qemu_cgroup.c
@@ -362,6 +362,7 @@ qemuSetupInputCgroup(virDomainObj *vm,
switch (dev->type) {
case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH:
+ case VIR_DOMAIN_INPUT_TYPE_EVDEV:
VIR_DEBUG("Process path '%s' for input device",
dev->source.evdev);
ret = virCgroupAllowDevicePath(priv->cgroup, dev->source.evdev,
VIR_CGROUP_DEVICE_RW, false);
@@ -385,6 +386,7 @@ qemuTeardownInputCgroup(virDomainObj *vm,
switch (dev->type) {
case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH:
+ case VIR_DOMAIN_INPUT_TYPE_EVDEV:
VIR_DEBUG("Process path '%s' for input device",
dev->source.evdev);
ret = virCgroupDenyDevicePath(priv->cgroup, dev->source.evdev,
VIR_CGROUP_DEVICE_RWM, false);
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index dcc060bde9..9954bb0678 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -4017,6 +4017,7 @@ qemuBuildVirtioInputDevStr(const virDomainDef *def,
return NULL;
}
break;
+ case VIR_DOMAIN_INPUT_TYPE_EVDEV:
case VIR_DOMAIN_INPUT_TYPE_LAST:
default:
virReportEnumRangeError(virDomainInputType, dev->type);
diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c
index a11e40d9b2..10cf5eb6f4 100644
--- a/src/qemu/qemu_domain_address.c
+++ b/src/qemu/qemu_domain_address.c
@@ -973,6 +973,7 @@ qemuDomainDeviceCalculatePCIConnectFlags(virDomainDeviceDef *dev,
case VIR_DOMAIN_INPUT_BUS_USB:
case VIR_DOMAIN_INPUT_BUS_XEN:
case VIR_DOMAIN_INPUT_BUS_PARALLELS:
+ case VIR_DOMAIN_INPUT_BUS_NONE:
case VIR_DOMAIN_INPUT_BUS_LAST:
return 0;
}
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index a64cddb9e7..c6f275e11d 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -5805,6 +5805,7 @@ qemuDomainDetachPrepInput(virDomainObj *vm,
case VIR_DOMAIN_INPUT_BUS_LAST:
case VIR_DOMAIN_INPUT_BUS_USB:
case VIR_DOMAIN_INPUT_BUS_VIRTIO:
+ case VIR_DOMAIN_INPUT_BUS_NONE:
break;
}
diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c
index e6ddb43113..168b302904 100644
--- a/src/qemu/qemu_validate.c
+++ b/src/qemu/qemu_validate.c
@@ -4563,6 +4563,7 @@ qemuValidateDomainDeviceDefInput(const virDomainInputDef *input,
case VIR_DOMAIN_INPUT_TYPE_MOUSE:
case VIR_DOMAIN_INPUT_TYPE_TABLET:
case VIR_DOMAIN_INPUT_TYPE_KBD:
+ case VIR_DOMAIN_INPUT_TYPE_EVDEV:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("virtio (non-)transitional models are not "
"supported for input type=%s"),
@@ -4608,6 +4609,11 @@ qemuValidateDomainDeviceDefInput(const virDomainInputDef *input,
cap = QEMU_CAPS_VIRTIO_INPUT_HOST;
ccwCap = QEMU_CAPS_LAST;
break;
+ case VIR_DOMAIN_INPUT_TYPE_EVDEV:
+ baseName = "input-linux";
+ cap = QEMU_CAPS_INPUT_LINUX;
+ ccwCap = QEMU_CAPS_LAST;
+ break;
case VIR_DOMAIN_INPUT_TYPE_LAST:
default:
virReportEnumRangeError(virDomainInputType,
diff --git a/src/security/security_apparmor.c b/src/security/security_apparmor.c
index a2a8435fe4..84363015dc 100644
--- a/src/security/security_apparmor.c
+++ b/src/security/security_apparmor.c
@@ -720,6 +720,7 @@ AppArmorSetInputLabel(virSecurityManager *mgr,
switch ((virDomainInputType)input->type) {
case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH:
+ case VIR_DOMAIN_INPUT_TYPE_EVDEV:
if (input->source.evdev == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("%s: passthrough input device has no source"),
diff --git a/src/security/security_dac.c b/src/security/security_dac.c
index e973964735..76bfce7762 100644
--- a/src/security/security_dac.c
+++ b/src/security/security_dac.c
@@ -1815,6 +1815,7 @@ virSecurityDACSetInputLabel(virSecurityManager *mgr,
switch ((virDomainInputType)input->type) {
case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH:
+ case VIR_DOMAIN_INPUT_TYPE_EVDEV:
if (virSecurityDACGetIds(seclabel, priv, &user, &group, NULL, NULL) <
0)
return -1;
@@ -1843,6 +1844,7 @@ virSecurityDACRestoreInputLabel(virSecurityManager *mgr,
switch ((virDomainInputType)input->type) {
case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH:
+ case VIR_DOMAIN_INPUT_TYPE_EVDEV:
ret = virSecurityDACRestoreFileLabel(mgr, input->source.evdev);
break;
diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c
index 29628d8953..b50f4463cc 100644
--- a/src/security/security_selinux.c
+++ b/src/security/security_selinux.c
@@ -1513,6 +1513,7 @@ virSecuritySELinuxSetInputLabel(virSecurityManager *mgr,
switch ((virDomainInputType)input->type) {
case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH:
+ case VIR_DOMAIN_INPUT_TYPE_EVDEV:
if (virSecuritySELinuxSetFilecon(mgr, input->source.evdev,
seclabel->imagelabel, true) < 0)
return -1;
@@ -1543,6 +1544,7 @@ virSecuritySELinuxRestoreInputLabel(virSecurityManager *mgr,
switch ((virDomainInputType)input->type) {
case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH:
+ case VIR_DOMAIN_INPUT_TYPE_EVDEV:
rc = virSecuritySELinuxRestoreFileLabel(mgr, input->source.evdev, true);
break;
diff --git a/src/security/virt-aa-helper.c b/src/security/virt-aa-helper.c
index e0d78ae309..52cfebf6e0 100644
--- a/src/security/virt-aa-helper.c
+++ b/src/security/virt-aa-helper.c
@@ -1149,7 +1149,8 @@ get_files(vahControl * ctl)
for (i = 0; i < ctl->def->ninputs; i++) {
if (ctl->def->inputs[i] &&
- ctl->def->inputs[i]->type == VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH)
{
+ (ctl->def->inputs[i]->type == VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH
||
+ ctl->def->inputs[i]->type == VIR_DOMAIN_INPUT_TYPE_EVDEV)) {
if (vah_add_file(&buf, ctl->def->inputs[i]->source.evdev,
"rw") != 0)
goto cleanup;
}
diff --git a/tests/qemuxml2argvdata/input-linux.xml
b/tests/qemuxml2argvdata/input-linux.xml
new file mode 100644
index 0000000000..cbc28830ef
--- /dev/null
+++ b/tests/qemuxml2argvdata/input-linux.xml
@@ -0,0 +1,31 @@
+<domain type='qemu'>
+ <name>QEMUGuest1</name>
+ <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+ <memory unit='KiB'>219100</memory>
+ <currentMemory unit='KiB'>219100</currentMemory>
+ <vcpu placement='static'>1</vcpu>
+ <os>
+ <type arch='i686' machine='pc'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <devices>
+ <emulator>/usr/bin/qemu-system-i386</emulator>
+ <controller type='pci' index='0' model='pci-root'/>
+ <controller type='usb' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x01' function='0x2'/>
+ </controller>
+ <input type='evdev'>
+ <source dev='/dev/input/event1234' grab='all'
repeat='on'/>
+ </input>
+ <input type='mouse' bus='ps2'/>
+ <input type='keyboard' bus='ps2'/>
+ <audio id='1' type='none'/>
+ <memballoon model='virtio'>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x02' function='0x0'/>
+ </memballoon>
+ </devices>
+</domain>
diff --git a/tests/qemuxml2xmloutdata/input-linux.xml
b/tests/qemuxml2xmloutdata/input-linux.xml
new file mode 120000
index 0000000000..84e1e79b51
--- /dev/null
+++ b/tests/qemuxml2xmloutdata/input-linux.xml
@@ -0,0 +1 @@
+../qemuxml2argvdata/input-linux.xml
\ No newline at end of file
diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c
index 7af6f90aee..93ee1a9f1b 100644
--- a/tests/qemuxml2xmltest.c
+++ b/tests/qemuxml2xmltest.c
@@ -1290,6 +1290,8 @@ mymain(void)
DO_TEST("virtio-input-passthrough",
QEMU_CAPS_VIRTIO_INPUT_HOST);
+ DO_TEST("input-linux", QEMU_CAPS_INPUT_LINUX);
+
DO_TEST("memorybacking-set", NONE);
DO_TEST("memorybacking-unset", NONE);
--
2.31.1