static int assign_intx(AssignedDevice *dev)

{

    AssignedIRQType new_type;

    PCIINTxRoute intx_route;

    bool intx_host_msi;

    int r;



    /* Interrupt PIN 0 means don't use INTx */

    if (assigned_dev_pci_read_byte(&dev->dev, PCI_INTERRUPT_PIN) == 0) {

        pci_device_set_intx_routing_notifier(&dev->dev, NULL);

        return 0;

    }



    if (!check_irqchip_in_kernel()) {

        return -ENOTSUP;

    }



    pci_device_set_intx_routing_notifier(&dev->dev,

                                         assigned_dev_update_irq_routing);



    intx_route = pci_device_route_intx_to_irq(&dev->dev, dev->intpin);

    assert(intx_route.mode != PCI_INTX_INVERTED);



    if (!pci_intx_route_changed(&dev->intx_route, &intx_route)) {

        return 0;

    }



    switch (dev->assigned_irq_type) {

    case ASSIGNED_IRQ_INTX_HOST_INTX:

    case ASSIGNED_IRQ_INTX_HOST_MSI:

        intx_host_msi = dev->assigned_irq_type == ASSIGNED_IRQ_INTX_HOST_MSI;

        r = kvm_device_intx_deassign(kvm_state, dev->dev_id, intx_host_msi);

        break;

    case ASSIGNED_IRQ_MSI:

        r = kvm_device_msi_deassign(kvm_state, dev->dev_id);

        break;

    case ASSIGNED_IRQ_MSIX:

        r = kvm_device_msix_deassign(kvm_state, dev->dev_id);

        break;

    default:

        r = 0;

        break;

    }

    if (r) {

        perror("assign_intx: deassignment of previous interrupt failed");

    }

    dev->assigned_irq_type = ASSIGNED_IRQ_NONE;



    if (intx_route.mode == PCI_INTX_DISABLED) {

        dev->intx_route = intx_route;

        return 0;

    }



retry:

    if (dev->features & ASSIGNED_DEVICE_PREFER_MSI_MASK &&

        dev->cap.available & ASSIGNED_DEVICE_CAP_MSI) {

        intx_host_msi = true;

        new_type = ASSIGNED_IRQ_INTX_HOST_MSI;

    } else {

        intx_host_msi = false;

        new_type = ASSIGNED_IRQ_INTX_HOST_INTX;

    }



    r = kvm_device_intx_assign(kvm_state, dev->dev_id, intx_host_msi,

                               intx_route.irq);

    if (r < 0) {

        if (r == -EIO && !(dev->features & ASSIGNED_DEVICE_PREFER_MSI_MASK) &&

            dev->cap.available & ASSIGNED_DEVICE_CAP_MSI) {

            /* Retry with host-side MSI. There might be an IRQ conflict and

             * either the kernel or the device doesn't support sharing. */

            error_report("Host-side INTx sharing not supported, "

                         "using MSI instead");

            error_printf("Some devices do not work properly in this mode.\n");

            dev->features |= ASSIGNED_DEVICE_PREFER_MSI_MASK;

            goto retry;

        }

        error_report("Failed to assign irq for \"%s\": %s",

                     dev->dev.qdev.id, strerror(-r));

        error_report("Perhaps you are assigning a device "

                     "that shares an IRQ with another device?");

        return r;

    }



    dev->intx_route = intx_route;

    dev->assigned_irq_type = new_type;

    return r;

}
