<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
</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">
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));
"tablet",
"keyboard",
"passthrough",
+ "evdev",
);
VIR_ENUM_IMPL(virDomainInputBus,
"xen",
"parallels",
"virtio",
+ "none",
);
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",
return NULL;
case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH:
+ case VIR_DOMAIN_INPUT_TYPE_EVDEV:
return input->source.evdev;
}
return NULL;
{
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);
} 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;
}
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),
{
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 &&
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);
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);
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;
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;
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;
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);
}
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);
virDomainInputDefFind;
virDomainInputDefFree;
virDomainInputDefGetPath;
+virDomainInputSourceGrabTypeFromString;
+virDomainInputSourceGrabTypeToString;
virDomainInputTypeToString;
virDomainIOMMUModelTypeFromString;
virDomainIOMMUModelTypeToString;
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);
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);
return NULL;
}
break;
+ case VIR_DOMAIN_INPUT_TYPE_EVDEV:
case VIR_DOMAIN_INPUT_TYPE_LAST:
default:
virReportEnumRangeError(virDomainInputType, dev->type);
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;
}
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;
}
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"),
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,
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"),
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;
switch ((virDomainInputType)input->type) {
case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH:
+ case VIR_DOMAIN_INPUT_TYPE_EVDEV:
ret = virSecurityDACRestoreFileLabel(mgr, input->source.evdev);
break;
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;
switch ((virDomainInputType)input->type) {
case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH:
+ case VIR_DOMAIN_INPUT_TYPE_EVDEV:
rc = virSecuritySELinuxRestoreFileLabel(mgr, input->source.evdev, true);
break;
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;
}
--- /dev/null
+<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>
+ <cpu mode='custom' match='exact' check='none'>
+ <model fallback='forbid'>qemu64</model>
+ </cpu>
+ <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' model='piix3-uhci'>
+ <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>
--- /dev/null
+../qemuxml2argvdata/input-linux.xml
\ No newline at end of file
DO_TEST("virtio-input-passthrough",
QEMU_CAPS_VIRTIO_INPUT_HOST);
+ DO_TEST_CAPS_LATEST("input-linux");
+
DO_TEST("memorybacking-set", NONE);
DO_TEST("memorybacking-unset", NONE);