virDomainInputDefParseXML(virDomainXMLOptionPtr xmlopt,
                          const virDomainDef *dom,
                          xmlNodePtr node,
                          xmlXPathContextPtr ctxt,
                          unsigned int flags)
{
    VIR_XPATH_NODE_AUTORESTORE(ctxt);
    virDomainInputDefPtr def;
    g_autofree char *evdev = NULL;
    g_autofree char *type = NULL;
    g_autofree char *bus = NULL;
    g_autofree char *model = NULL;

    if (VIR_ALLOC(def) < 0)
        return NULL;

    ctxt->node = node;

    type = virXMLPropString(node, "type");
    bus = virXMLPropString(node, "bus");
    model = virXMLPropString(node, "model");

    if (!type) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("missing input device type"));
        goto error;
    }

    if ((def->type = virDomainInputTypeFromString(type)) < 0) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("unknown input device type '%s'"), type);
        goto error;
    }

    if (model &&
        ((def->model = virDomainInputModelTypeFromString(model)) < 0 ||
         def->model == VIR_DOMAIN_INPUT_MODEL_DEFAULT)) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("unknown input model '%s'"), model);
        goto error;
    }

    if (bus) {
        if ((def->bus = virDomainInputBusTypeFromString(bus)) < 0) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("unknown input bus type '%s'"), bus);
            goto error;
        }

        if (dom->os.type == VIR_DOMAIN_OSTYPE_HVM) {
            if (def->bus == VIR_DOMAIN_INPUT_BUS_PS2 &&
                def->type != VIR_DOMAIN_INPUT_TYPE_MOUSE &&
                def->type != VIR_DOMAIN_INPUT_TYPE_KBD) {
                virReportError(VIR_ERR_INTERNAL_ERROR,
                               _("ps2 bus does not support %s input device"),
                               type);
                goto error;
            }
            if (def->bus == VIR_DOMAIN_INPUT_BUS_XEN) {
                virReportError(VIR_ERR_INTERNAL_ERROR,
                               _("unsupported input bus %s"),
                               bus);
                goto error;
            }
        } else if (dom->os.type == VIR_DOMAIN_OSTYPE_XEN ||
                   dom->os.type == VIR_DOMAIN_OSTYPE_XENPVH) {
            if (def->bus != VIR_DOMAIN_INPUT_BUS_XEN) {
                virReportError(VIR_ERR_INTERNAL_ERROR,
                               _("unsupported input bus %s"),
                               bus);
                goto error;
            }
            if (def->type != VIR_DOMAIN_INPUT_TYPE_MOUSE &&
                def->type != VIR_DOMAIN_INPUT_TYPE_KBD) {
                virReportError(VIR_ERR_INTERNAL_ERROR,
                               _("xen bus does not support %s input device"),
                               type);
                goto error;
            }
        } else {
            if (dom->virtType == VIR_DOMAIN_VIRT_VZ ||
                dom->virtType == VIR_DOMAIN_VIRT_PARALLELS) {
                if (def->bus != VIR_DOMAIN_INPUT_BUS_PARALLELS) {
                    virReportError(VIR_ERR_INTERNAL_ERROR,
                                   _("parallels containers don't support "
                                     "input bus %s"),
                                   bus);
                    goto error;
                }

                if (def->type != VIR_DOMAIN_INPUT_TYPE_MOUSE &&
                    def->type != VIR_DOMAIN_INPUT_TYPE_KBD) {
                    virReportError(VIR_ERR_INTERNAL_ERROR,
                                   _("parallels bus does not support "
                                     "%s input device"),
                                   type);
                    goto error;
                }
            } else {
                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                               _("Input devices are not supported by this "
                                 "virtualization driver."));
                goto error;
            }
        }
    } else {
        if (dom->os.type == VIR_DOMAIN_OSTYPE_HVM) {
            if ((def->type == VIR_DOMAIN_INPUT_TYPE_MOUSE ||
                def->type == VIR_DOMAIN_INPUT_TYPE_KBD) &&
                (ARCH_IS_X86(dom->os.arch) || dom->os.arch == VIR_ARCH_NONE)) {
                def->bus = VIR_DOMAIN_INPUT_BUS_PS2;
            } else if (ARCH_IS_S390(dom->os.arch) ||
                       def->type == VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH) {
                def->bus = VIR_DOMAIN_INPUT_BUS_VIRTIO;
            } else {
                def->bus = VIR_DOMAIN_INPUT_BUS_USB;
            }
        } else if (dom->os.type == VIR_DOMAIN_OSTYPE_XEN ||
                   dom->os.type == VIR_DOMAIN_OSTYPE_XENPVH) {
            def->bus = VIR_DOMAIN_INPUT_BUS_XEN;
        } else {
            if ((dom->virtType == VIR_DOMAIN_VIRT_VZ ||
                 dom->virtType == VIR_DOMAIN_VIRT_PARALLELS))
                def->bus = VIR_DOMAIN_INPUT_BUS_PARALLELS;
        }
    }

    if (virDomainDeviceInfoParseXML(xmlopt, node, &def->info, flags) < 0)
        goto error;

    if (def->bus == VIR_DOMAIN_INPUT_BUS_USB &&
        def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE &&
        def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB) {
        virReportError(VIR_ERR_XML_ERROR, "%s",
                       _("Invalid address for a USB device"));
        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 (virDomainVirtioOptionsParseXML(virXPathNode("./driver", ctxt),
                                       &def->virtio) < 0)
        goto error;

    return def;

 error:
    virDomainInputDefFree(def);
    return NULL;
}