在以下示例中,高级互斥锁 (xsp->high_mu) 仅用于保护在高级别中断处理程序和软中断处理程序之间共享的数据。受保护的数据包括高级别中断处理程序和低级处理程序使用的队列,以及用于指示低级处理程序正在运行的标志。单独的低级互斥锁 (xsp->low_mu) 可防止软中断处理程序使用驱动程序的其余部分。
static int mydevattach(dev_info_t *dip, ddi_attach_cmd_t cmd) { struct mydevstate *xsp; /* ... */ ret = ddi_intr_get_supported_types(dip, &type); if ((ret != DDI_SUCCESS) || (!(type & DDI_INTR_TYPE_FIXED))) { cmn_err(CE_WARN, "ddi_intr_get_supported_types() failed"); return (DDI_FAILURE); } ret = ddi_intr_get_nintrs(dip, DDI_INTR_TYPE_FIXED, &count); /* * Fixed interrupts can only have one interrupt. Check to make * sure that number of supported interrupts and number of * available interrupts are both equal to 1. */ if ((ret != DDI_SUCCESS) || (count != 1)) { cmn_err(CE_WARN, "No fixed interrupts found"); return (DDI_FAILURE); } xsp->xs_htable = kmem_zalloc(count * sizeof (ddi_intr_handle_t), KM_SLEEP); ret = ddi_intr_alloc(dip, xsp->xs_htable, DDI_INTR_TYPE_FIXED, 0, count, &actual, 0); if ((ret != DDI_SUCCESS) || (actual != 1)) { cmn_err(CE_WARN, "ddi_intr_alloc failed 0x%x", ret"); kmem_free(xsp->xs_htable, sizeof (ddi_intr_handle_t)); return (DDI_FAILURE); } ret = ddi_intr_get_pri(xsp->xs_htable[0], &intr_pri); if (ret != DDI_SUCCESS) { cmn_err(CE_WARN, "ddi_intr_get_pri failed 0x%x", ret"); (void) ddi_intr_free(xsp->xs_htable[0]); kmem_free(xsp->xs_htable, sizeof (ddi_intr_handle_t)); return (DDI_FAILURE); } if (intr_pri >= ddi_intr_get_hilevel_pri()) { mutex_init(&xsp->high_mu, NULL, MUTEX_DRIVER, DDI_INTR_PRI(intr_pri)); ret = ddi_intr_add_handler(xsp->xs_htable[0], mydevhigh_intr, (caddr_t)xsp, NULL); if (ret != DDI_SUCCESS) { cmn_err(CE_WARN, "ddi_intr_add_handler failed 0x%x", ret"); mutex_destroy(&xsp>xs_int_mutex); (void) ddi_intr_free(xsp->xs_htable[0]); kmem_free(xsp->xs_htable, sizeof (ddi_intr_handle_t)); return (DDI_FAILURE); } /* add soft interrupt */ if (ddi_intr_add_softint(xsp->xs_dip, &xsp->xs_softint_hdl, DDI_INTR_SOFTPRI_MAX, xs_soft_intr, (caddr_t)xsp) != DDI_SUCCESS) { cmn_err(CE_WARN, "add soft interrupt failed"); mutex_destroy(&xsp->high_mu); (void) ddi_intr_remove_handler(xsp->xs_htable[0]); (void) ddi_intr_free(xsp->xs_htable[0]); kmem_free(xsp->xs_htable, sizeof (ddi_intr_handle_t)); return (DDI_FAILURE); } xsp->low_soft_pri = DDI_INTR_SOFTPRI_MAX; mutex_init(&xsp->low_mu, NULL, MUTEX_DRIVER, DDI_INTR_PRI(xsp->low_soft_pri)); } else { /* * regular interrupt registration continues from here * do not use a soft interrupt */ } return (DDI_SUCCESS); }
高级别中断例程用于服务设备并对数据进行排队。如果低级例程未运行,则高级例程会触发软件中断,如以下示例所示。
static uint_t mydevhigh_intr(caddr_t arg1, caddr_t arg2) { struct mydevstate *xsp = (struct mydevstate *)arg1; uint8_t status; volatile uint8_t temp; int need_softint; mutex_enter(&xsp->high_mu); /* read status */ status = ddi_get8(xsp->data_access_handle, &xsp->regp->csr); if (!(status & INTERRUPTING)) { mutex_exit(&xsp->high_mu); return (DDI_INTR_UNCLAIMED); /* dev not interrupting */ } ddi_put8(xsp->data_access_handle,&xsp->regp->csr, CLEAR_INTERRUPT | ENABLE_INTERRUPTS); /* flush store buffers */ temp = ddi_get8(xsp->data_access_handle, &xsp->regp->csr); /* read data from device, queue data for low-level interrupt handler */ if (xsp->softint_running) need_softint = 0; else { xsp->softint_count++; need_softint = 1; } mutex_exit(&xsp->high_mu); /* read-only access to xsp->id, no mutex needed */ if (need_softint) { ret = ddi_intr_trigger_softint(xsp->xs_softint_hdl, NULL); if (ret == DDI_EPENDING) { cmn_err(CE_WARN, "ddi_intr_trigger_softint() soft interrupt " "already pending for this handler"); } else if (ret != DDI_SUCCESS) { cmn_err(CE_WARN, "ddi_intr_trigger_softint() failed"); } } return (DDI_INTR_CLAIMED); }
低级中断例程由用于触发软件中断的高级别中断例程启动。低级中断例程会一直运行,直到没有其他要处理的对象为止,如以下示例所示。
static uint_t mydev_soft_intr(caddr_t arg1, caddr_t arg2) { struct mydevstate *mydevp = (struct mydevstate *)arg1; /* ... */ mutex_enter(&mydevp->low_mu); mutex_enter(&mydevp->high_mu); if (mydevp->softint_count > 1) { mydevp->softint_count--; mutex_exit(&mydevp->high_mu); mutex_exit(&mydevp->low_mu); return (DDI_INTR_CLAIMED); } if ( /* queue empty */ ) { mutex_exit(&mydevp->high_mu); mutex_exit(&mydevp->low_mu); return (DDI_INTR_UNCLAIMED); } mydevp->softint_running = 1; while (EMBEDDED COMMENT:data on queue) { ASSERT(mutex_owned(&mydevp->high_mu); /* Dequeue data from high-level queue. */ mutex_exit(&mydevp->high_mu); /* normal interrupt processing */ mutex_enter(&mydevp->high_mu); } mydevp->softint_running = 0; mydevp->softint_count = 0; mutex_exit(&mydevp->high_mu); mutex_exit(&mydevp->low_mu); return (DDI_INTR_CLAIMED); }