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