virDomainHostdevDefParseXMLSubsys(xmlNodePtr node,
                                  xmlXPathContextPtr ctxt,
                                  const char *type,
                                  virDomainHostdevDefPtr def,
                                  unsigned int flags)
{
    xmlNodePtr sourcenode;
    int backend;
    virDomainHostdevSubsysPCIPtr pcisrc = &def->source.subsys.u.pci;
    virDomainHostdevSubsysSCSIPtr scsisrc = &def->source.subsys.u.scsi;
    virDomainHostdevSubsysSCSIVHostPtr scsihostsrc = &def->source.subsys.u.scsi_host;
    virDomainHostdevSubsysMediatedDevPtr mdevsrc = &def->source.subsys.u.mdev;
    g_autofree char *managed = NULL;
    g_autofree char *sgio = NULL;
    g_autofree char *rawio = NULL;
    g_autofree char *backendStr = NULL;
    g_autofree char *model = NULL;
    g_autofree char *display = NULL;
    g_autofree char *ramfb = NULL;

    /* @managed can be read from the xml document - it is always an
     * attribute of the toplevel element, no matter what type of
     * element that might be (pure hostdev, or higher level device
     * (e.g. <interface>) with type='hostdev')
     */
    if ((managed = virXMLPropString(node, "managed")) != NULL)
        ignore_value(virStringParseYesNo(managed, &def->managed));

    sgio = virXMLPropString(node, "sgio");
    rawio = virXMLPropString(node, "rawio");
    model = virXMLPropString(node, "model");
    display = virXMLPropString(node, "display");
    ramfb = virXMLPropString(node, "ramfb");

    /* @type is passed in from the caller rather than read from the
     * xml document, because it is specified in different places for
     * different kinds of defs - it is an attribute of
     * <source>/<address> for an intelligent hostdev (<interface>),
     * but an attribute of the toplevel element for a standard
     * <hostdev>.  (the functions we're going to call expect address
     * type to already be known).
     */
    if (type) {
        if ((def->source.subsys.type
             = virDomainHostdevSubsysTypeFromString(type)) < 0) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("unknown host device source address type '%s'"),
                           type);
            return -1;
        }
    } else {
        virReportError(VIR_ERR_XML_ERROR,
                       "%s", _("missing source address type"));
        return -1;
    }

    if (!(sourcenode = virXPathNode("./source", ctxt))) {
        virReportError(VIR_ERR_XML_ERROR, "%s",
                       _("Missing <source> element in hostdev device"));
        return -1;
    }

    if (def->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB &&
        virXPathBoolean("boolean(./source/@startupPolicy)", ctxt)) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("Setting startupPolicy is only allowed for USB"
                         " devices"));
        return -1;
    }

    if (sgio) {
        if (def->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) {
            virReportError(VIR_ERR_XML_ERROR, "%s",
                           _("sgio is only supported for scsi host device"));
            return -1;
        }

        if ((scsisrc->sgio = virDomainDeviceSGIOTypeFromString(sgio)) <= 0) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("unknown sgio mode '%s'"), sgio);
            return -1;
        }
    }

    if (rawio) {
        if (def->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) {
            virReportError(VIR_ERR_XML_ERROR, "%s",
                           _("rawio is only supported for scsi host device"));
            return -1;
        }

        if ((scsisrc->rawio = virTristateBoolTypeFromString(rawio)) <= 0) {
            virReportError(VIR_ERR_XML_ERROR,
                           _("unknown hostdev rawio setting '%s'"),
                           rawio);
            return -1;
        }
    }

    if (def->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV &&
        def->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST) {
        if (model) {
            virReportError(VIR_ERR_XML_ERROR,
                           _("'model' attribute in <hostdev> is only supported "
                             "when type='%s'"),
                           virDomainHostdevSubsysTypeToString(def->source.subsys.type));
            return -1;
        }
    }

    if (def->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST) {
        if (model &&
            ((scsihostsrc->model = virDomainHostdevSubsysSCSIVHostModelTypeFromString(model)) < 0)) {
            virReportError(VIR_ERR_XML_ERROR,
                           _("unknown hostdev model '%s'"),
                           model);
            return -1;
        }
    } else if (def->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV) {
        if (!model) {
            virReportError(VIR_ERR_XML_ERROR, "%s",
                           _("Missing 'model' attribute in mediated device's "
                             "<hostdev> element"));
            return -1;
        }

        if ((mdevsrc->model = virMediatedDeviceModelTypeFromString(model)) < 0) {
            virReportError(VIR_ERR_XML_ERROR,
                           _("unknown hostdev model '%s'"),
                           model);
            return -1;
        }

        if (display &&
            (mdevsrc->display = virTristateSwitchTypeFromString(display)) <= 0) {
            virReportError(VIR_ERR_XML_ERROR,
                           _("unknown value '%s' for <hostdev> attribute "
                             "'display'"),
                           display);
            return -1;
        }

        if (ramfb &&
            (mdevsrc->ramfb = virTristateSwitchTypeFromString(ramfb)) <= 0) {
            virReportError(VIR_ERR_XML_ERROR,
                           _("unknown value '%s' for <hostdev> attribute "
                             "'ramfb'"),
                           ramfb);
            return -1;
        }
    }

    switch (def->source.subsys.type) {
    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
        if (virDomainHostdevSubsysPCIDefParseXML(sourcenode, def, flags) < 0)
            return -1;

        backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT;
        if ((backendStr = virXPathString("string(./driver/@name)", ctxt)) &&
            (((backend = virDomainHostdevSubsysPCIBackendTypeFromString(backendStr)) < 0) ||
             backend == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT)) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("Unknown PCI device <driver name='%s'/> "
                             "has been specified"), backendStr);
            return -1;
        }
        pcisrc->backend = backend;

        break;

    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
        if (virDomainHostdevSubsysUSBDefParseXML(sourcenode, def) < 0)
            return -1;
        break;

    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI:
        if (virDomainHostdevSubsysSCSIDefParseXML(sourcenode, scsisrc, ctxt) < 0)
            return -1;
        break;

    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST:
        if (virDomainHostdevSubsysSCSIVHostDefParseXML(sourcenode, def) < 0)
            return -1;
        break;
    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV:
        if (virDomainHostdevSubsysMediatedDevDefParseXML(def, ctxt) < 0)
            return -1;
        break;

    default:
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("address type='%s' not supported in hostdev interfaces"),
                       virDomainHostdevSubsysTypeToString(def->source.subsys.type));
        return -1;
    }

    return 0;
}