第 1 部分针对 Oracle Solaris 平台设计设备驱动程序
9. 直接内存访问 (Direct Memory Access, DMA)
14. 分层驱动程序接口 (Layered Driver Interface, LDI)
高级别中断是指中断在调度程序级别或更高级别的那类中断。此级别不允许运行调度程序。因此,调度程序无法抢占高级别中断处理程序。高级别中断不会因为调度程序而阻塞。高级别中断只能使用互斥锁进行锁定。
驱动程序必须确定设备是否在使用高级别中断。注册中断时,请在驱动程序的 attach(9E) 入口点进行此测试。请参见高级别中断处理示例。
如果从 ddi_intr_get_pri(9F) 返回的中断优先级高于或等于从 ddi_intr_get_hilevel_pri(9F) 返回的优先级,则表明驱动程序无法连接,或者驱动程序可能实现高级别的中断处理程序。高级别的中断处理程序可使用优先级较低的软件中断来处理该设备。要允许更大的并发性,请使用单独的互斥锁来防止高级中断处理程序使用数据。
如果从 ddi_intr_get_pri(9F) 返回的中断优先级低于从 ddi_intr_get_hilevel_pri(9F) 返回的优先级,则 attach(9E) 入口点将进行常规中断注册。在这种情况下不需要软中断。
使用表示高级别中断的中断优先级初始化的互斥锁称为高级互斥锁。虽然持有高级互斥锁,但是驱动程序仍会受到与高级别中断处理程序相同的限制。
在以下示例中,高级互斥锁 (xsp->high_mu) 仅用于保护在高级别中断处理程序和软中断处理程序之间共享的数据。受保护的数据包括高级别中断处理程序和低级处理程序使用的队列,以及用于指示低级处理程序正在运行的标志。单独的低级互斥锁 (xsp->low_mu) 可防止软中断处理程序使用驱动程序的其余部分。
示例 8-10 使用 attach() 处理高级别中断
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); }
高级别中断例程用于服务设备并对数据进行排队。如果低级例程未运行,则高级例程会触发软件中断,如以下示例所示。
示例 8-11 高级别中断例程
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); }
低级中断例程由用于触发软件中断的高级别中断例程启动。低级中断例程会一直运行,直到没有其他要处理的对象为止,如以下示例所示。
示例 8-12 低级软中断例程
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); }