To register a driver's interrupt handler, the driver typically performs the following steps in its attach(9E) entry point:
Use ddi_intr_get_supported_types(9F) to determine which types of interrupts are supported.
Use ddi_intr_get_nintrs(9F) to determine the number of supported MSI interrupt types.
Use ddi_intr_alloc(9F) to allocate memory for the MSI interrupts.
For each interrupt type that you allocate, take the following steps:
Use ddi_intr_get_pri(9F) to get the priority for the interrupt.
If you need to set a new priority for the interrupt, use ddi_intr_set_pri(9F).
Use mutex_init(9F) to initialize the lock.
Use ddi_intr_add_handler(9F) to register the handler for the interrupt.
Use one of the following functions to enable all the interrupts:
Use ddi_intr_block_enable(9F) to enable all the interrupts in a block.
Use ddi_intr_enable(9F) in a loop to enable each interrupt individually.
The following example illustrates how to register an MSI interrupt for a device called mydev.
/* Get supported interrupt types */ if (ddi_intr_get_supported_types(devinfo, &intr_types) != DDI_SUCCESS) { cmn_err(CE_WARN, "ddi_intr_get_supported_types failed"); goto attach_fail; } if (intr_types & DDI_INTR_TYPE_MSI) mydev_add_msi_intrs(mydevp); /* Check count, available and actual interrupts */ static int mydev_add_msi_intrs(mydev_t *mydevp) { dev_info_t *devinfo = mydevp->devinfo; int count, avail, actual; int x, y, rc, inum = 0; /* Get number of interrupts */ rc = ddi_intr_get_nintrs(devinfo, DDI_INTR_TYPE_MSI, &count); if ((rc != DDI_SUCCESS) || (count == 0)) { cmn_err(CE_WARN, "ddi_intr_get_nintrs() failure, rc: %d, " "count: %d", rc, count); return (DDI_FAILURE); } /* Get number of available interrupts */ rc = ddi_intr_get_navail(devinfo, DDI_INTR_TYPE_MSI, &avail); if ((rc != DDI_SUCCESS) || (avail == 0)) { cmn_err(CE_WARN, "ddi_intr_get_navail() failure, " "rc: %d, avail: %d\n", rc, avail); return (DDI_FAILURE); } if (avail < count) { cmn_err(CE_NOTE, "nitrs() returned %d, navail returned %d", count, avail); } /* Allocate memory for MSI interrupts */ mydevp->intr_size = count * sizeof (ddi_intr_handle_t); mydevp->htable = kmem_alloc(mydevp->intr_size, KM_SLEEP); rc = ddi_intr_alloc(devinfo, mydevp->htable, DDI_INTR_TYPE_MSI, inum, count, &actual, DDI_INTR_ALLOC_NORMAL); if ((rc != DDI_SUCCESS) || (actual == 0)) { cmn_err(CE_WARN, "ddi_intr_alloc() failed: %d", rc); kmem_free(mydevp->htable, mydevp->intr_size); return (DDI_FAILURE); } if (actual < count) { cmn_err(CE_NOTE, "Requested: %d, Received: %d", count, actual); } mydevp->intr_cnt = actual; /* * Get priority for first msi, assume remaining are all the same */ if (ddi_intr_get_pri(mydevp->htable[0], &mydev->intr_pri) != DDI_SUCCESS) { cmn_err(CE_WARN, "ddi_intr_get_pri() failed"); /* Free already allocated intr */ for (y = 0; y < actual; y++) { (void) ddi_intr_free(mydevp->htable[y]); } kmem_free(mydevp->htable, mydevp->intr_size); return (DDI_FAILURE); } /* Call ddi_intr_add_handler() */ for (x = 0; x < actual; x++) { if (ddi_intr_add_handler(mydevp->htable[x], mydev_intr, (caddr_t)mydevp, NULL) != DDI_SUCCESS) { cmn_err(CE_WARN, "ddi_intr_add_handler() failed"); /* Free already allocated intr */ for (y = 0; y < actual; y++) { (void) ddi_intr_free(mydevp->htable[y]); } kmem_free(mydevp->htable, mydevp->intr_size); return (DDI_FAILURE); } } (void) ddi_intr_get_cap(mydevp->htable[0], &mydevp->intr_cap); if (mydev->m_intr_cap & DDI_INTR_FLAG_BLOCK) { /* Call ddi_intr_block_enable() for MSI */ (void) ddi_intr_block_enable(mydev->m_htable, mydev->m_intr_cnt); } else { /* Call ddi_intr_enable() for MSI non block enable */ for (x = 0; x < mydev->m_intr_cnt; x++) { (void) ddi_intr_enable(mydev->m_htable[x]); } } return (DDI_SUCCESS); }
The following example shows how to remove MSI interrupts.
static void mydev_rem_intrs(mydev_t *mydev) { int x; /* Disable all interrupts */ if (mydev->m_intr_cap & DDI_INTR_FLAG_BLOCK) { /* Call ddi_intr_block_disable() */ (void) ddi_intr_block_disable(mydev->m_htable, mydev->m_intr_cnt); } else { for (x = 0; x < mydev->m_intr_cnt; x++) { (void) ddi_intr_disable(mydev->m_htable[x]); } } /* Call ddi_intr_remove_handler() */ for (x = 0; x < mydev->m_intr_cnt; x++) { (void) ddi_intr_remove_handler(mydev->m_htable[x]); (void) ddi_intr_free(mydev->m_htable[x]); } kmem_free(mydev->m_htable, mydev->m_intr_size); }