예제 #1
0
/*
 * Set the number of interrupts requested from IRM
 */
int
ddi_intr_set_nreq(dev_info_t *dip, int nreq)
{
	int	curr_type, nintrs;

	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_nreq: dip %p, nreq %d\n",
	    (void *)dip, nreq));

	ASSERT(dip != NULL);
	ASSERT(nreq > 0);

	/* Sanity check inputs */
	if ((dip == NULL) || (nreq < 1))
		return (DDI_EINVAL);

	curr_type = i_ddi_intr_get_current_type(dip);

	/* Only valid for IRM drivers actively using interrupts */
	if ((curr_type == 0) ||
	    (i_ddi_irm_supported(dip, curr_type) != DDI_SUCCESS))
		return (DDI_ENOTSUP);

	/* Range check */
	if (ddi_intr_get_nintrs(dip, curr_type, &nintrs) != DDI_SUCCESS)
		return (DDI_FAILURE);
	if (nreq > nintrs)
		return (DDI_EINVAL);

	return (i_ddi_irm_modify(dip, nreq));
}
예제 #2
0
static int
virtio_fixed_intr_setup(virtionet_state_t *sp, ddi_intr_handler_t inthandler)
{
	int			rc;
	int			nintr;
	uint_t			pri;

	rc = ddi_intr_get_nintrs(sp->dip, DDI_INTR_TYPE_FIXED, &nintr);
	if (rc != DDI_SUCCESS) {
		return (DDI_FAILURE);
	}

	ASSERT(nintr == 1);

	rc = ddi_intr_alloc(sp->dip, &sp->ihandle, DDI_INTR_TYPE_FIXED, 0, 1,
	    &nintr, DDI_INTR_ALLOC_NORMAL);
	if (rc != DDI_SUCCESS) {
		return (DDI_FAILURE);
	}

	ASSERT(nintr == 1);

	rc = ddi_intr_get_pri(sp->ihandle, &pri);
	if (rc != DDI_SUCCESS) {
		(void) ddi_intr_free(sp->ihandle);
		return (DDI_FAILURE);
	}

	/* Test for high level mutex */
	if (pri >= ddi_intr_get_hilevel_pri()) {
		cmn_err(CE_WARN, "Hi level interrupt not supported");
		(void) ddi_intr_free(sp->ihandle);
		return (DDI_FAILURE);
	}

	rc = ddi_intr_add_handler(sp->ihandle, inthandler, sp, NULL);
	if (rc != DDI_SUCCESS) {
		(void) ddi_intr_free(sp->ihandle);
		return (DDI_FAILURE);
	}

	rc = ddi_intr_enable(sp->ihandle);
	if (rc != DDI_SUCCESS) {
		(void) ddi_intr_remove_handler(sp->ihandle);
		(void) ddi_intr_free(sp->ihandle);
		return (DDI_FAILURE);
	}
	return (DDI_SUCCESS);
}
예제 #3
0
int
ddi_dev_nintrs(dev_info_t *dip, int *result)
{
	DDI_INTR_APIDBG((CE_CONT, "ddi_dev_nintrs: name=%s%d dip=0x%p\n",
	    ddi_driver_name(dip), ddi_get_instance(dip), (void *)dip));

	if (ddi_intr_get_nintrs(dip, DDI_INTR_TYPE_FIXED,
	    result) != DDI_SUCCESS) {
		DDI_INTR_APIDBG((CE_CONT, "ddi_dev_nintrs: "
		    "ddi_intr_get_nintrs failed\n"));
		*result = 0;
	}

	return (DDI_SUCCESS);
}
예제 #4
0
/*
 * Autoconfiguration entry points.
 */
int
efe_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
	ddi_acc_handle_t pci;
	int types;
	int count;
	int actual;
	uint_t pri;
	efe_t *efep;
	mac_register_t *macp;

	switch (cmd) {
	case DDI_ATTACH:
		break;

	case DDI_RESUME:
		efep = ddi_get_driver_private(dip);
		return (efe_resume(efep));

	default:
		return (DDI_FAILURE);
	}

	/*
	 * PCI configuration.
	 */
	if (pci_config_setup(dip, &pci) != DDI_SUCCESS) {
		efe_error(dip, "unable to setup PCI configuration!");
		return (DDI_FAILURE);
	}

	pci_config_put16(pci, PCI_CONF_COMM,
	    pci_config_get16(pci, PCI_CONF_COMM) | PCI_COMM_MAE | PCI_COMM_ME);

	pci_config_teardown(&pci);

	if (ddi_intr_get_supported_types(dip, &types)
	    != DDI_SUCCESS || !(types & DDI_INTR_TYPE_FIXED)) {
		efe_error(dip, "fixed interrupts not supported!");
		return (DDI_FAILURE);
	}

	if (ddi_intr_get_nintrs(dip, DDI_INTR_TYPE_FIXED, &count)
	    != DDI_SUCCESS || count != 1) {
		efe_error(dip, "no fixed interrupts available!");
		return (DDI_FAILURE);
	}

	/*
	 * Initialize soft state.
	 */
	efep = kmem_zalloc(sizeof (efe_t), KM_SLEEP);
	ddi_set_driver_private(dip, efep);

	efep->efe_dip = dip;

	if (ddi_regs_map_setup(dip, 1, (caddr_t *)&efep->efe_regs, 0, 0,
	    &efe_regs_acc_attr, &efep->efe_regs_acch) != DDI_SUCCESS) {
		efe_error(dip, "unable to setup register mapping!");
		goto failure;
	}

	efep->efe_rx_ring = efe_ring_alloc(efep->efe_dip, RXDESCL);
	if (efep->efe_rx_ring == NULL) {
		efe_error(efep->efe_dip, "unable to allocate rx ring!");
		goto failure;
	}

	efep->efe_tx_ring = efe_ring_alloc(efep->efe_dip, TXDESCL);
	if (efep->efe_tx_ring == NULL) {
		efe_error(efep->efe_dip, "unable to allocate tx ring!");
		goto failure;
	}

	if (ddi_intr_alloc(dip, &efep->efe_intrh, DDI_INTR_TYPE_FIXED, 0,
	    count, &actual, DDI_INTR_ALLOC_STRICT) != DDI_SUCCESS ||
	    actual != count) {
		efe_error(dip, "unable to allocate fixed interrupt!");
		goto failure;
	}

	if (ddi_intr_get_pri(efep->efe_intrh, &pri) != DDI_SUCCESS ||
	    pri >= ddi_intr_get_hilevel_pri()) {
		efe_error(dip, "unable to get valid interrupt priority!");
		goto failure;
	}

	mutex_init(&efep->efe_intrlock, NULL, MUTEX_DRIVER,
	    DDI_INTR_PRI(pri));

	mutex_init(&efep->efe_txlock, NULL, MUTEX_DRIVER,
	    DDI_INTR_PRI(pri));

	/*
	 * Initialize device.
	 */
	mutex_enter(&efep->efe_intrlock);
	mutex_enter(&efep->efe_txlock);

	efe_reset(efep);

	mutex_exit(&efep->efe_txlock);
	mutex_exit(&efep->efe_intrlock);

	/* Use factory address as default */
	efe_getaddr(efep, efep->efe_macaddr);

	/*
	 * Enable the ISR.
	 */
	if (ddi_intr_add_handler(efep->efe_intrh, efe_intr, efep, NULL)
	    != DDI_SUCCESS) {
		efe_error(dip, "unable to add interrupt handler!");
		goto failure;
	}

	if (ddi_intr_enable(efep->efe_intrh) != DDI_SUCCESS) {
		efe_error(dip, "unable to enable interrupt!");
		goto failure;
	}

	/*
	 * Allocate MII resources.
	 */
	if ((efep->efe_miih = mii_alloc(efep, dip, &efe_mii_ops)) == NULL) {
		efe_error(dip, "unable to allocate mii resources!");
		goto failure;
	}

	/*
	 * Allocate MAC resources.
	 */
	if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
		efe_error(dip, "unable to allocate mac resources!");
		goto failure;
	}

	macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
	macp->m_driver = efep;
	macp->m_dip = dip;
	macp->m_src_addr = efep->efe_macaddr;
	macp->m_callbacks = &efe_m_callbacks;
	macp->m_min_sdu = 0;
	macp->m_max_sdu = ETHERMTU;
	macp->m_margin = VLAN_TAGSZ;

	if (mac_register(macp, &efep->efe_mh) != 0) {
		efe_error(dip, "unable to register with mac!");
		goto failure;
	}
	mac_free(macp);

	ddi_report_dev(dip);

	return (DDI_SUCCESS);

failure:
	if (macp != NULL) {
		mac_free(macp);
	}

	if (efep->efe_miih != NULL) {
		mii_free(efep->efe_miih);
	}

	if (efep->efe_intrh != NULL) {
		(void) ddi_intr_disable(efep->efe_intrh);
		(void) ddi_intr_remove_handler(efep->efe_intrh);
		(void) ddi_intr_free(efep->efe_intrh);
	}

	mutex_destroy(&efep->efe_txlock);
	mutex_destroy(&efep->efe_intrlock);

	if (efep->efe_tx_ring != NULL) {
		efe_ring_free(&efep->efe_tx_ring);
	}
	if (efep->efe_rx_ring != NULL) {
		efe_ring_free(&efep->efe_rx_ring);
	}

	if (efep->efe_regs_acch != NULL) {
		ddi_regs_map_free(&efep->efe_regs_acch);
	}

	kmem_free(efep, sizeof (efe_t));

	return (DDI_FAILURE);
}
예제 #5
0
static int
virtio_register_msi(struct virtio_softc *sc,
    struct virtio_int_handler *config_handler,
    struct virtio_int_handler vq_handlers[], int intr_types)
{
	int count, actual;
	int int_type;
	int i;
	int handler_count;
	int ret;

	/* If both MSI and MSI-x are reported, prefer MSI-x. */
	int_type = DDI_INTR_TYPE_MSI;
	if (intr_types & DDI_INTR_TYPE_MSIX)
		int_type = DDI_INTR_TYPE_MSIX;

	/* Walk the handler table to get the number of handlers. */
	for (handler_count = 0;
	    vq_handlers && vq_handlers[handler_count].vh_func;
	    handler_count++)
		;

	/* +1 if there is a config change handler. */
	if (config_handler != NULL)
		handler_count++;

	/* Number of MSIs supported by the device. */
	ret = ddi_intr_get_nintrs(sc->sc_dev, int_type, &count);
	if (ret != DDI_SUCCESS) {
		dev_err(sc->sc_dev, CE_WARN, "ddi_intr_get_nintrs failed");
		return (ret);
	}

	/*
	 * Those who try to register more handlers then the device
	 * supports shall suffer.
	 */
	ASSERT(handler_count <= count);

	sc->sc_intr_htable = kmem_zalloc(sizeof (ddi_intr_handle_t) *
	    handler_count, KM_SLEEP);

	ret = ddi_intr_alloc(sc->sc_dev, sc->sc_intr_htable, int_type, 0,
	    handler_count, &actual, DDI_INTR_ALLOC_NORMAL);
	if (ret != DDI_SUCCESS) {
		dev_err(sc->sc_dev, CE_WARN, "Failed to allocate MSI: %d", ret);
		goto out_msi_alloc;
	}

	if (actual != handler_count) {
		dev_err(sc->sc_dev, CE_WARN,
		    "Not enough MSI available: need %d, available %d",
		    handler_count, actual);
		goto out_msi_available;
	}

	sc->sc_intr_num = handler_count;
	sc->sc_intr_config = B_FALSE;
	if (config_handler != NULL) {
		sc->sc_intr_config = B_TRUE;
	}

	/* Assume they are all same priority */
	ret = ddi_intr_get_pri(sc->sc_intr_htable[0], &sc->sc_intr_prio);
	if (ret != DDI_SUCCESS) {
		dev_err(sc->sc_dev, CE_WARN, "ddi_intr_get_pri failed");
		goto out_msi_prio;
	}

	/* Add the vq handlers */
	for (i = 0; vq_handlers[i].vh_func; i++) {
		ret = ddi_intr_add_handler(sc->sc_intr_htable[i],
		    vq_handlers[i].vh_func, sc, vq_handlers[i].vh_priv);
		if (ret != DDI_SUCCESS) {
			dev_err(sc->sc_dev, CE_WARN,
			    "ddi_intr_add_handler failed");
			/* Remove the handlers that succeeded. */
			while (--i >= 0) {
				(void) ddi_intr_remove_handler(
				    sc->sc_intr_htable[i]);
			}
			goto out_add_handlers;
		}
	}

	/* Don't forget the config handler */
	if (config_handler != NULL) {
		ret = ddi_intr_add_handler(sc->sc_intr_htable[i],
		    config_handler->vh_func, sc, config_handler->vh_priv);
		if (ret != DDI_SUCCESS) {
			dev_err(sc->sc_dev, CE_WARN,
			    "ddi_intr_add_handler failed");
			/* Remove the handlers that succeeded. */
			while (--i >= 0) {
				(void) ddi_intr_remove_handler(
				    sc->sc_intr_htable[i]);
			}
			goto out_add_handlers;
		}
	}

	/* We know we are using MSI, so set the config offset. */
	sc->sc_config_offset = VIRTIO_CONFIG_DEVICE_CONFIG_MSI;

	ret = ddi_intr_get_cap(sc->sc_intr_htable[0], &sc->sc_intr_cap);
	/* Just in case. */
	if (ret != DDI_SUCCESS)
		sc->sc_intr_cap = 0;

out_add_handlers:
out_msi_prio:
out_msi_available:
	for (i = 0; i < actual; i++)
		(void) ddi_intr_free(sc->sc_intr_htable[i]);
out_msi_alloc:
	kmem_free(sc->sc_intr_htable, sizeof (ddi_intr_handle_t) * count);

	return (ret);
}
예제 #6
0
/**
 * Sets IRQ for VMMDev.
 *
 * @returns Solaris error code.
 * @param   pDip     Pointer to the device info structure.
 */
static int VBoxGuestSolarisAddIRQ(dev_info_t *pDip)
{
    LogFlow((DEVICE_NAME "::AddIRQ: pDip=%p\n", pDip));

    int IntrType = 0;
    int rc = ddi_intr_get_supported_types(pDip, &IntrType);
    if (rc == DDI_SUCCESS)
    {
        /* We won't need to bother about MSIs. */
        if (IntrType & DDI_INTR_TYPE_FIXED)
        {
            int IntrCount = 0;
            rc = ddi_intr_get_nintrs(pDip, IntrType, &IntrCount);
            if (   rc == DDI_SUCCESS
                && IntrCount > 0)
            {
                int IntrAvail = 0;
                rc = ddi_intr_get_navail(pDip, IntrType, &IntrAvail);
                if (   rc == DDI_SUCCESS
                    && IntrAvail > 0)
                {
                    /* Allocated kernel memory for the interrupt handles. The allocation size is stored internally. */
                    g_pIntr = RTMemAlloc(IntrCount * sizeof(ddi_intr_handle_t));
                    if (g_pIntr)
                    {
                        int IntrAllocated;
                        rc = ddi_intr_alloc(pDip, g_pIntr, IntrType, 0, IntrCount, &IntrAllocated, DDI_INTR_ALLOC_NORMAL);
                        if (   rc == DDI_SUCCESS
                            && IntrAllocated > 0)
                        {
                            g_cIntrAllocated = IntrAllocated;
                            uint_t uIntrPriority;
                            rc = ddi_intr_get_pri(g_pIntr[0], &uIntrPriority);
                            if (rc == DDI_SUCCESS)
                            {
                                /* Initialize the mutex. */
                                mutex_init(&g_IrqMtx, NULL, MUTEX_DRIVER, DDI_INTR_PRI(uIntrPriority));

                                /* Assign interrupt handler functions and enable interrupts. */
                                for (int i = 0; i < IntrAllocated; i++)
                                {
                                    rc = ddi_intr_add_handler(g_pIntr[i], (ddi_intr_handler_t *)VBoxGuestSolarisISR,
                                                            NULL /* No Private Data */, NULL);
                                    if (rc == DDI_SUCCESS)
                                        rc = ddi_intr_enable(g_pIntr[i]);
                                    if (rc != DDI_SUCCESS)
                                    {
                                        /* Changing local IntrAllocated to hold so-far allocated handles for freeing. */
                                        IntrAllocated = i;
                                        break;
                                    }
                                }
                                if (rc == DDI_SUCCESS)
                                    return rc;

                                /* Remove any assigned handlers */
                                LogRel((DEVICE_NAME ":failed to assign IRQs allocated=%d\n", IntrAllocated));
                                for (int x = 0; x < IntrAllocated; x++)
                                    ddi_intr_remove_handler(g_pIntr[x]);
                            }
                            else
                                LogRel((DEVICE_NAME "::AddIRQ: failed to get priority of interrupt. rc=%d\n", rc));

                            /* Remove allocated IRQs, too bad we can free only one handle at a time. */
                            for (int k = 0; k < g_cIntrAllocated; k++)
                                ddi_intr_free(g_pIntr[k]);
                        }
                        else
                            LogRel((DEVICE_NAME "::AddIRQ: failed to allocated IRQs. count=%d\n", IntrCount));
                        RTMemFree(g_pIntr);
                    }
                    else
                        LogRel((DEVICE_NAME "::AddIRQ: failed to allocated IRQs. count=%d\n", IntrCount));
                }
                else
                    LogRel((DEVICE_NAME "::AddIRQ: failed to get or insufficient available IRQs. rc=%d IntrAvail=%d\n", rc, IntrAvail));
            }
            else
                LogRel((DEVICE_NAME "::AddIRQ: failed to get or insufficient number of IRQs. rc=%d IntrCount=%d\n", rc, IntrCount));
        }
        else
            LogRel((DEVICE_NAME "::AddIRQ: invalid irq type. IntrType=%#x\n", IntrType));
    }
    else
        LogRel((DEVICE_NAME "::AddIRQ: failed to get supported interrupt types\n"));
    return rc;
}
예제 #7
0
/**
 * Sets IRQ for VMMDev.
 *
 * @returns Solaris error code.
 * @param   pDip     Pointer to the device info structure.
 */
static int vgdrvSolarisAddIRQ(dev_info_t *pDip)
{
    LogFlow(("vgdrvSolarisAddIRQ: pDip=%p\n", pDip));

    /* Get the types of interrupt supported for this hardware. */
    int fIntrType = 0;
    int rc = ddi_intr_get_supported_types(pDip, &fIntrType);
    if (rc == DDI_SUCCESS)
    {
        /* We only support fixed interrupts at this point, not MSIs. */
        if (fIntrType & DDI_INTR_TYPE_FIXED)
        {
            /* Verify the number of interrupts supported by this device. There can only be one fixed interrupt. */
            int cIntrCount = 0;
            rc = ddi_intr_get_nintrs(pDip, fIntrType, &cIntrCount);
            if (   rc == DDI_SUCCESS
                && cIntrCount == 1)
            {
                /* Allocated kernel memory for the interrupt handle. The allocation size is stored internally. */
                g_pahIntrs = RTMemAllocZ(cIntrCount * sizeof(ddi_intr_handle_t));
                if (g_pahIntrs)
                {
                    /* Allocate the interrupt for this device and verify the allocation. */
                    int cIntrAllocated;
                    rc = ddi_intr_alloc(pDip, g_pahIntrs, fIntrType, 0 /* interrupt number */, cIntrCount, &cIntrAllocated,
                                        DDI_INTR_ALLOC_NORMAL);
                    if (   rc == DDI_SUCCESS
                        && cIntrAllocated == 1)
                    {
                        /* Get the interrupt priority assigned by the system. */
                        uint_t uIntrPriority;
                        rc = ddi_intr_get_pri(g_pahIntrs[0], &uIntrPriority);
                        if (rc == DDI_SUCCESS)
                        {
                            /* Check if the interrupt priority is scheduler level or above, if so we need to use a high-level
                               and low-level interrupt handlers with corresponding mutexes. */
                            cmn_err(CE_CONT, "!vboxguest: uIntrPriority=%d hilevel_pri=%d\n", uIntrPriority, ddi_intr_get_hilevel_pri());
                            if (uIntrPriority >= ddi_intr_get_hilevel_pri())
                            {
                                /* Initialize the high-level mutex. */
                                mutex_init(&g_HighLevelIrqMtx, NULL /* pszDesc */, MUTEX_DRIVER, DDI_INTR_PRI(uIntrPriority));

                                /* Assign interrupt handler function to the interrupt handle. */
                                rc = ddi_intr_add_handler(g_pahIntrs[0], (ddi_intr_handler_t *)&vgdrvSolarisHighLevelISR,
                                                          NULL /* pvArg1 */, NULL /* pvArg2 */);

                                if (rc == DDI_SUCCESS)
                                {
                                    /* Add the low-level interrupt handler. */
                                    rc = ddi_intr_add_softint(pDip, &g_hSoftIntr, DDI_INTR_SOFTPRI_MAX,
                                                              (ddi_intr_handler_t *)&vgdrvSolarisISR, NULL /* pvArg1 */);
                                    if (rc == DDI_SUCCESS)
                                    {
                                        /* Initialize the low-level mutex at the corresponding level. */
                                        mutex_init(&g_IrqMtx, NULL /* pszDesc */,  MUTEX_DRIVER,
                                                   DDI_INTR_PRI(DDI_INTR_SOFTPRI_MAX));

                                        g_fSoftIntRegistered = true;
                                        /* Enable the high-level interrupt. */
                                        rc = ddi_intr_enable(g_pahIntrs[0]);
                                        if (rc == DDI_SUCCESS)
                                            return rc;

                                        LogRel((DEVICE_NAME "::AddIRQ: failed to enable interrupt. rc=%d\n", rc));
                                        mutex_destroy(&g_IrqMtx);
                                    }
                                    else
                                        LogRel((DEVICE_NAME "::AddIRQ: failed to add soft interrupt handler. rc=%d\n", rc));

                                    ddi_intr_remove_handler(g_pahIntrs[0]);
                                }
                                else
                                    LogRel((DEVICE_NAME "::AddIRQ: failed to add high-level interrupt handler. rc=%d\n", rc));

                                mutex_destroy(&g_HighLevelIrqMtx);
                            }
                            else
                            {
                                /* Interrupt handler runs at reschedulable level, initialize the mutex at the given priority. */
                                mutex_init(&g_IrqMtx, NULL /* pszDesc */, MUTEX_DRIVER, DDI_INTR_PRI(uIntrPriority));

                                /* Assign interrupt handler function to the interrupt handle. */
                                rc = ddi_intr_add_handler(g_pahIntrs[0], (ddi_intr_handler_t *)vgdrvSolarisISR,
                                                          NULL /* pvArg1 */, NULL /* pvArg2 */);
                                if (rc == DDI_SUCCESS)
                                {
                                    /* Enable the interrupt. */
                                    rc = ddi_intr_enable(g_pahIntrs[0]);
                                    if (rc == DDI_SUCCESS)
                                        return rc;

                                    LogRel((DEVICE_NAME "::AddIRQ: failed to enable interrupt. rc=%d\n", rc));
                                    mutex_destroy(&g_IrqMtx);
                                }
                            }
                        }
                        else
                            LogRel((DEVICE_NAME "::AddIRQ: failed to get priority of interrupt. rc=%d\n", rc));

                        Assert(cIntrAllocated == 1);
                        ddi_intr_free(g_pahIntrs[0]);
                    }
                    else
                        LogRel((DEVICE_NAME "::AddIRQ: failed to allocated IRQs. count=%d\n", cIntrCount));
                    RTMemFree(g_pahIntrs);
                }
                else
                    LogRel((DEVICE_NAME "::AddIRQ: failed to allocated IRQs. count=%d\n", cIntrCount));
            }
            else
                LogRel((DEVICE_NAME "::AddIRQ: failed to get or insufficient number of IRQs. rc=%d cIntrCount=%d\n", rc, cIntrCount));
        }
        else
            LogRel((DEVICE_NAME "::AddIRQ: fixed-type interrupts not supported. IntrType=%#x\n", fIntrType));
    }
    else
        LogRel((DEVICE_NAME "::AddIRQ: failed to get supported interrupt types. rc=%d\n", rc));
    return rc;
}
예제 #8
0
/*
 * Interrupt allocate/free functions
 */
int
ddi_intr_alloc(dev_info_t *dip, ddi_intr_handle_t *h_array, int type, int inum,
    int count, int *actualp, int behavior)
{
	ddi_intr_handle_impl_t	*hdlp, tmp_hdl;
	int			i, ret, cap = 0, curr_type, nintrs;
	uint_t			pri, navail, curr_nintrs = 0;

	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: name %s dip 0x%p "
	    "type %x inum %x count %x behavior %x\n", ddi_driver_name(dip),
	    (void *)dip, type, inum, count, behavior));

	/* Validate parameters */
	if ((dip == NULL) || (h_array == NULL) || (inum < 0) || (count < 1) ||
	    (actualp == NULL) || !DDI_INTR_BEHAVIOR_FLAG_VALID(behavior)) {
		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: "
		    "Invalid input args\n"));
		return (DDI_EINVAL);
	}

	/* Validate interrupt type */
	if (!DDI_INTR_TYPE_FLAG_VALID(type) ||
	    !(i_ddi_intr_get_supported_types(dip) & type)) {
		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: type %x not "
		    "supported\n", type));
		return (DDI_EINVAL);
	}

	/* Validate inum not previously allocated */
	if ((type == DDI_INTR_TYPE_FIXED) &&
	    (i_ddi_get_intr_handle(dip, inum) != NULL)) {
		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: inum %d is already "
		    "in use, cannot allocate again!!\n", inum));
		return (DDI_EINVAL);
	}

	/* Get how many interrupts the device supports */
	if ((nintrs = i_ddi_intr_get_supported_nintrs(dip, type)) == 0) {
		if (ddi_intr_get_nintrs(dip, type, &nintrs) != DDI_SUCCESS) {
			DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: no "
			    "interrupts found of type %d\n", type));
			return (DDI_INTR_NOTFOUND);
		}
	}

	/* Get how many interrupts the device is already using */
	if ((curr_type = i_ddi_intr_get_current_type(dip)) != 0) {
		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: type %x "
		    "is already being used\n", curr_type));
		curr_nintrs = i_ddi_intr_get_current_nintrs(dip);
	}

	/* Validate interrupt type consistency */
	if ((curr_type != 0) && (type != curr_type)) {
		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: Requested "
		    "interrupt type %x is different from interrupt type %x"
		    "already in use\n", type, curr_type));
		return (DDI_EINVAL);
	}

	/* Validate count does not exceed what device supports */
	if (count > nintrs) {
		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: no of interrupts "
		    "requested %d is more than supported %d\n", count, nintrs));
		return (DDI_EINVAL);
	} else if ((count + curr_nintrs) > nintrs) {
		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: count %d "
		    "+ intrs in use %d exceeds supported %d intrs\n",
		    count, curr_nintrs, nintrs));
		return (DDI_EINVAL);
	}

	/* Validate power of 2 requirements for MSI */
	if ((type == DDI_INTR_TYPE_MSI) && !ISP2(curr_nintrs + count)) {
		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: "
		    "MSI count %d is not a power of two\n", count));
		return (DDI_EINVAL);
	}

	/*
	 * Initialize the device's interrupt information structure,
	 * and establish an association with IRM if it is supported.
	 *
	 * NOTE: IRM checks minimum support, and can return DDI_EAGAIN.
	 */
	if (curr_nintrs == 0) {
		i_ddi_intr_devi_init(dip);
		if (i_ddi_irm_insert(dip, type, count) == DDI_EAGAIN) {
			cmn_err(CE_WARN, "ddi_intr_alloc: "
			    "cannot fit into interrupt pool\n");
			return (DDI_EAGAIN);
		}
	}

	/* Synchronously adjust IRM associations for non-IRM aware drivers */
	if (curr_nintrs && (i_ddi_irm_supported(dip, type) != DDI_SUCCESS))
		(void) i_ddi_irm_modify(dip, count + curr_nintrs);

	/* Get how many interrupts are currently available */
	navail = i_ddi_intr_get_current_navail(dip, type);

	/* Validate that requested number of interrupts are available */
	if (curr_nintrs == navail) {
		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: max # of intrs %d "
		    "already allocated\n", navail));
		return (DDI_EAGAIN);
	}
	if ((count + curr_nintrs) > navail) {
		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: requested # of "
		    "intrs %d exceeds # of available intrs %d\n", count,
		    navail - curr_nintrs));
		if (behavior == DDI_INTR_ALLOC_STRICT) {
			DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: "
			    "DDI_INTR_ALLOC_STRICT flag is passed, "
			    "return failure\n"));
			if (curr_nintrs == 0)
				i_ddi_intr_devi_fini(dip);
			else if (i_ddi_irm_supported(dip, type) != DDI_SUCCESS)
				(void) i_ddi_irm_modify(dip, curr_nintrs);
			return (DDI_EAGAIN);
		}
		count = navail - curr_nintrs;
	}

	/* Now allocate required number of interrupts */
	bzero(&tmp_hdl, sizeof (ddi_intr_handle_impl_t));
	tmp_hdl.ih_type = type;
	tmp_hdl.ih_inum = inum;
	tmp_hdl.ih_scratch1 = count;
	tmp_hdl.ih_scratch2 = (void *)(uintptr_t)behavior;
	tmp_hdl.ih_dip = dip;

	if (i_ddi_intr_ops(dip, dip, DDI_INTROP_ALLOC,
	    &tmp_hdl, (void *)actualp) != DDI_SUCCESS) {
		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: allocation "
		    "failed\n"));
		i_ddi_intr_devi_fini(dip);
		return (*actualp ? DDI_EAGAIN : DDI_INTR_NOTFOUND);
	}

	if ((ret = i_ddi_intr_ops(dip, dip, DDI_INTROP_GETPRI,
	    &tmp_hdl, (void *)&pri)) != DDI_SUCCESS) {
		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: get priority "
		    "failed\n"));
		goto fail;
	}

	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: getting capability\n"));

	if ((ret = i_ddi_intr_ops(dip, dip, DDI_INTROP_GETCAP,
	    &tmp_hdl, (void *)&cap)) != DDI_SUCCESS) {
		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: get capability "
		    "failed\n"));
		goto fail;
	}

	/*
	 * Save current interrupt type, supported and current intr count.
	 */
	i_ddi_intr_set_current_type(dip, type);
	i_ddi_intr_set_supported_nintrs(dip, nintrs);
	i_ddi_intr_set_current_nintrs(dip,
	    i_ddi_intr_get_current_nintrs(dip) + *actualp);

	/* Now, go and handle each "handle" */
	for (i = inum; i < (inum + *actualp); i++) {
		hdlp = (ddi_intr_handle_impl_t *)kmem_zalloc(
		    (sizeof (ddi_intr_handle_impl_t)), KM_SLEEP);
		rw_init(&hdlp->ih_rwlock, NULL, RW_DRIVER, NULL);
		h_array[i] = (struct __ddi_intr_handle *)hdlp;
		hdlp->ih_type = type;
		hdlp->ih_pri = pri;
		hdlp->ih_cap = cap;
		hdlp->ih_ver = DDI_INTR_VERSION;
		hdlp->ih_state = DDI_IHDL_STATE_ALLOC;
		hdlp->ih_dip = dip;
		hdlp->ih_inum = i;
		i_ddi_alloc_intr_phdl(hdlp);
		if (type & DDI_INTR_TYPE_FIXED)
			i_ddi_set_intr_handle(dip, hdlp->ih_inum,
			    (ddi_intr_handle_t)hdlp);

		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: hdlp = 0x%p\n",
		    (void *)h_array[i]));
	}

	return (DDI_SUCCESS);

fail:
	(void) i_ddi_intr_ops(tmp_hdl.ih_dip, tmp_hdl.ih_dip,
	    DDI_INTROP_FREE, &tmp_hdl, NULL);
	i_ddi_intr_devi_fini(dip);

	return (ret);
}