/* * 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)); }
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); }
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); }
/* * 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); }
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); }
/** * 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; }
/** * 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; }
/* * 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); }