要注册驱动程序的中断处理程序,驱动程序通常会在其 attach(9E) 入口点执行以下步骤。
使用 ddi_intr_get_supported_types(9F) 确定支持的中断类型。
使用 ddi_intr_get_nintrs(9F) 确定支持的 MSI 中断类型的数量。
使用 ddi_intr_alloc(9F) 为 MSI 中断分配内存。
对于分配的每个中断类型,执行以下步骤:
使用 ddi_intr_get_pri(9F) 获取中断的优先级。
如果需要为中断设置新的优先级,请使用 ddi_intr_set_pri(9F)。
使用 mutex_init(9F) 将锁初始化。
使用 ddi_intr_add_handler(9F) 注册中断的处理程序。
使用以下函数之一启用所有中断:
使用 ddi_intr_block_enable(9F) 启用某个块中的所有中断。
在循环中使用 ddi_intr_enable(9F) 单独启用每个中断。
以下示例说明如何为名为 mydev 的设备注册 MSI 中断。
/* 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); }
以下示例说明如何删除 MSI 中断。
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); }