Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1315952
  • 博文数量: 554
  • 博客积分: 10425
  • 博客等级: 上将
  • 技术积分: 7555
  • 用 户 组: 普通用户
  • 注册时间: 2006-11-09 09:49
文章分类

全部博文(554)

文章存档

2012年(1)

2011年(1)

2009年(8)

2008年(544)

分类:

2008-04-11 15:17:56


设备中断
第8 章• 中断处理程序127
硬件中断处理程序必须快速执行其任务,因为它们可能必须在执行这些任务的同时暂停其
他系统活动。对于高级别中断处理程序,更需要满足此要求,这些处理程序在高于系统调
度程序的优先级别上运行。高级别中断处理程序将屏蔽所有较低优先级中断的操作,包括
系统时钟的中断操作。因此,该中断处理程序必须避免涉及到可能导致其休眠的活动,如
获取互斥锁。
如果处理程序休眠,则系统可能会挂起,因为时钟会屏蔽,从而无法调度休眠线程。因
此,高级别中断处理程序通常在高优先级别执行最少量的工作,并将其他任务委托给运行
优先级别低于高级别中断处理程序的软件中断。由于软件中断处理程序运行的优先级别低
于系统调度程序,因此软件中断处理程序可以执行高级别中断处理程序无法执行的操作。
DDI 中断函数更改
Solaris OS 目前提供了新框架用于注册和注消中断,并且支持消息告知中断(Message
Signalled Interrupt, MSI)。通过新的管理界面,可以处理优先级、功能和中断屏蔽,并可获
取待处理信息。
本节说明了自Solaris 10 OS FCS 发行版以来对中断函数所做的更改。
中断功能函数
以下函数是获取中断信息的首选方法:
ddi_intr_get_navail(9F) 返回可用于特定硬件设备和给定中断类型的中断的
数量。
ddi_intr_get_nintrs(9F) 获取设备支持的给定中断类型的中断的数量。
ddi_intr_get_supported_types(9F) 返回设备和主机均支持的硬件中断类型。
ddi_intr_get_cap(9F) 针对指定的中断返回中断功能标志。
中断初始化和销毁函数
在创建和删除中断的过程中可使用以下函数:
ddi_intr_alloc(9F) 为指定类型的中断分配系统资源和中断向量。
ddi_intr_free(9F) 针对指定的中断句柄释放系统资源和中断向量。
ddi_intr_set_cap(9F) 通过使用DDI_INTR_FLAG_LEVEL 和
DDI_INTR_FLAG_EDGE 标志来设置指定中断的功能。
ddi_intr_add_handler(9F) 添加中断处理程序。
ddi_intr_dup_handler(9F) 仅用于MSI-X,重用未分配的中断向量的MSI 地址和数据
对。
DDI 中断函数更改
128 编写设备驱动程序• 2006 年11 月
ddi_intr_remove_handler(9F) 删除指定的中断处理程序。
ddi_intr_enable(9F) 启用指定的中断。
ddi_intr_disable(9F) 禁用指定的中断。
ddi_intr_block_enable(9F) 启用指定范围的中断。仅用于MSI。
ddi_intr_block_disable(9F) 禁用指定范围的中断。仅用于MSI。
ddi_intr_set_mask(9F) 递增中断掩码中的内部计数器。
ddi_intr_clr_mask(9F) 如果值不为零,则递减中断掩码中的内部计数器。
ddi_intr_get_pending(9F) 如果主桥(host bridge) 或设备支持一个中断待处理位,则
读取此位。
优先级管理函数
以下函数用于获取和设置优先级信息:
ddi_intr_get_pri(9F) 返回指定中断的当前软件优先级设置。
ddi_intr_set_pri(9F) 设置指定中断的中断优先级别。
ddi_intr_get_hilevel_pri(9F) 返回高级别中断的最低优先级别。
软中断函数
以下函数用于处理软中断和软中断处理程序:
ddi_intr_add_softint(9F) 添加软中断处理程序。
ddi_intr_trigger_softint(9F) 触发指定的软中断。
ddi_intr_remove_softint(9F) 删除指定的软中断处理程序。
ddi_intr_get_softint_pri(9F) 返回指定中断的软中断优先级。
ddi_intr_set_softint_pri(9F) 更改指定软中断的相对软中断优先级。
新的中断函数示例
本节提供了执行以下任务的示例:
 更改软中断优先级
 检查待处理中断
 设置中断掩码
 清除中断掩码
DDI 中断函数更改
第8 章• 中断处理程序129
示例8–1 使用ddi_inter_set_softint_pri() 函数
/* Change the soft interrupt priority to 9 */
if (ddi_intr_set_softint_pri(mydev->mydev_softint_hdl, 9) !=
DDI_SUCCESS) {
cmn_err (CE_WARN, "ddi_intr_set_softint_pri failed");
}
示例8–2 使用ddi_intr_get_pending() 函数
/* Check if an interrupt is pending */
if (ddi_intr_get_pending(mydevp->htable[0], &pending) != DDI_SUCCESS) {
cmn_err(CE_WARN, "ddi_intr_get_pending() failed");
} else if (pending)
cmn_err(CE_NOTE, "ddi_intr_get_pending(): Interrupt pending");
示例8–3 使用ddi_intr_set_mask() 函数
/* Set interrupt masking to prevent device from receiving interrupts */
if ((ddi_intr_set_mask(mydevp->htable[0]) != DDI_SUCCESS))
cmn_err(CE_WARN, "ddi_intr_set_mask() failed");
示例8–4 使用ddi_intr_clr_mask() 函数
/* Clear interrupt masking. If successful device will start generating interrupts */
if (ddi_intr_clr_mask(mydevp->htable[0]) != DDI_SUCCESS)
cmn_err(CE_WARN, "ddi_intr_clr_mask() failed");
传统中断函数
要利用新框架的功能,开发者需要使用这些新接口并避免使用以下接口,以下接口仅保留
用于实现兼容性。
DDI 中断函数更改
130 编写设备驱动程序• 2006 年11 月
表8–1传统中断函数
传统中断函数替换
ddi_add_intr(9F) 过程包含三个步骤:
1. ddi_intr_alloc(9F)
2. ddi_intr_add_handler(9F)
3. ddi_intr_enable(9F)
ddi_add_softintr(9F) ddi_intr_add_softint(9F)
ddi_dev_nintrs(9F) ddi_intr_get_nintrs(9F)
ddi_get_iblock_cookie(9F) 过程包含三个步骤:
1. ddi_intr_alloc(9F)
2. ddi_intr_get_pri(9F)
3. ddi_intr_free(9F)
ddi_get_soft_iblock_cookie(9F) 过程包含三个步骤:
1. ddi_intr_add_softint(9F)
2. ddi_intr_get_softint_pri(9F)
3. ddi_intr_remove_softint(9F)
ddi_iblock_cookie(9S) (void *) (uintptr_t )
ddi_idevice_cookie(9S) 不适用
ddi_intr_hilevel(9F) ddi_intr_get_hilevel_pri(9F)
ddi_remove_intr(9F) ddi_intr_remove_handler(9F)
ddi_remove_softintr(9F) ddi_intr_remove_softint(9F)
ddi_trigger_softintr(9F) ddi_intr_trigger_softint(9F)
注册中断
设备驱动程序必须首先通过调用ddi_add_intr(9F) 向系统注册中断处理程序,然后才能接
收和服务中断。注册中断会为系统提供一种将中断处理程序与中断规格相关联的方法。如
果设备可能负责中断,则会调用中断处理程序。此处理程序负责确定其是否应处理中断,
如果是,则负责声明该中断。
注册中断
第8 章• 中断处理程序131
注册传统中断
要注册驱动程序的中断处理程序,驱动程序通常需要执行attach(9E) 中的以下步骤。
1. 使用ddi_intr_get_supported_types(9F) 确定支持的中断类型。
2. 使用ddi_intr_get_nintrs(9F) 确定支持的中断类型的数量。
3. 使用kmem_zalloc(9F) 为DDI 中断句柄分配内存。
4. 对于分配的每个中断类型,必须执行以下步骤:
a. 使用ddi_intr_get_pri(9F) 获取中断的优先级。
b. 如果需要为中断设置新的优先级,请使用ddi_intr_set_pri(9F)。
c. 使用mutex_init(9F) 将锁初始化。
d. 使用ddi_intr_add_handler(9F) 注册中断的处理程序。
e. 使用ddi_intr_enable(9F) 启用中断。
5. 要释放中断,必须针对每个中断执行以下步骤:
a. 使用ddi_intr_disable(9F) 禁用每个中断。
b. 使用ddi_intr_remove_handler(9F) 删除中断处理程序。
c. 使用mutex_destroy(9F) 删除锁。
d. 使用ddi_intr_free(9F) 和kmem_free(9F) 释放中断,从而释放为DDI 中断句柄分配
的内存。
示例8–5注册传统中断
以下示例说明如何为名为mydev 的设备安装中断处理程序。此示例假设mydev 仅支持一个
中断。
/* Determine which types of interrupts supported */
ret = ddi_intr_get_supported_types(mydevp->mydev_dip, &type);
if ((ret != DDI_SUCCESS) || (!(type & DDI_INTR_TYPE_FIXED))) {
cmn_err(CE_WARN, "Fixed type interrupt is not supported");
return (DDI_FAILURE);
}
/* Determine number of supported interrupts */
ret = ddi_intr_get_nintrs(mydevp->mydev_dip, DDI_INTR_TYPE_FIXED,
注册中断
132 编写设备驱动程序• 2006 年11 月
示例8–5 注册传统中断(续)
&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");
return (DDI_FAILURE);
}
/* Allocate memory for DDI interrupt handles */
mydevp->mydev_htable = kmem_zalloc(sizeof (ddi_intr_handle_t),
KM_SLEEP);
ret = ddi_intr_alloc(mydevp->mydev_dip, mydevp->mydev_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(mydevp->mydev_htable, sizeof (ddi_intr_handle_t));
return (DDI_FAILURE);
注册中断
第8 章• 中断处理程序133
示例8–5 注册传统中断(续)
}
/* Sanity check that count and available are the same. */
ASSERT(count == actual);
/* Get the priority of the interrupt */
if (ddi_intr_get_pri(mydevp->mydev_htable[0], &mydevp->mydev_intr_pri)) {
cmn_err(CE_WARN, "ddi_intr_alloc() failed 0x%x", ret);
(void) ddi_intr_free(mydevp->mydev_htable[0]);
kmem_free(mydevp->mydev_htable, sizeof (ddi_intr_handle_t));
return (DDI_FAILURE);
}
cmn_err(CE_NOTE, "Supported Interrupt priority = 0x%x", mydevp->mydev_intr_pri);
/* Test for high level mutex */
if (mydevp->mydev_intr_pri >= ddi_intr_get_hilevel_pri()) {
cmn_err(CE_WARN, "Hi level interrupt not supported");
(void) ddi_intr_free(mydevp->mydev_htable[0]);
kmem_free(mydevp->mydev_htable, sizeof (ddi_intr_handle_t));
注册中断
134 编写设备驱动程序• 2006 年11 月
示例8–5 注册传统中断(续)
return (DDI_FAILURE);
}
/* Initialize the mutex */
mutex_init(&mydevp->mydev_int_mutex, NULL, MUTEX_DRIVER,
(void *)(uintptr_t)mydevp->mydev_intr_pri);
/* Register the interrupt handler */
if (ddi_intr_add_handler(mydevp->mydev_htable[0], mydev_intr,
(caddr_t)mydevp, NULL) !=DDI_SUCCESS) {
cmn_err(CE_WARN, "ddi_intr_add_handler() failed");
mutex_destroy(&mydevp->mydev_int_mutex);
(void) ddi_intr_free(mydevp->mydev_htable[0]);
kmem_free(mydevp->mydev_htable, sizeof (ddi_intr_handle_t));
return (DDI_FAILURE);
}
/* Enable the interrupt */
if (ddi_intr_enable(mydevp->mydev_htable[0]) != DDI_SUCCESS) {
cmn_err(CE_WARN, "ddi_intr_enable() failed");
注册中断
第8 章• 中断处理程序135
示例8–5 注册传统中断(续)
(void) ddi_intr_remove_handler(mydevp->mydev_htable[0]);
mutex_destroy(&mydevp->mydev_int_mutex);
(void) ddi_intr_free(mydevp->mydev_htable[0]);
kmem_free(mydevp->mydev_htable, sizeof (ddi_intr_handle_t));
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
示例8–6删除传统中断
以下示例说明如何删除传统中断。
/* disable interrupt */
(void) ddi_intr_disable(mydevp->mydev_htable[0]);
/* Remove interrupt handler */
(void) ddi_intr_remove_handler(mydevp->mydev_htable[0]);
/* free interrupt handle */
(void) ddi_intr_free(mydevp->mydev_htable[0]);
/* free memory */
kmem_free(mydevp->mydev_htable, sizeof (ddi_intr_handle_t));
注册中断
136 编写设备驱动程序• 2006 年11 月
注册MSI 中断
要注册驱动程序的中断处理程序,驱动程序通常需要执行attach(9E) 中的以下步骤。
1. 使用ddi_intr_get_supported_types(9F) 确定支持的中断类型。
2. 使用ddi_intr_get_nintrs(9F) 确定支持的MSI 中断类型的数量。
3. 使用ddi_intr_alloc(9F) 为MSI 中断分配内存。
4. 对于分配的每个中断类型,必须执行以下步骤:
a. 使用ddi_intr_get_pri(9F) 获取中断的优先级。
b. 如果需要为中断设置新的优先级,请使用ddi_intr_set_pri()。
c. 使用mutex_init(9F) 将锁初始化。
d. 使用ddi_intr_add_handler(9F) 注册中断的处理程序。
5. 作为块启用所有中断(如果支持ddi_intr_block_enable(9F))或者在将
ddi_intr_enable(9F) 分别应用于每个中断的for() 循环中启用所有中断。
示例8–7 注册一组MSI 中断
以下示例说明如何为名为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;
注册中断
第8 章• 中断处理程序137
示例8–7 注册一组MSI 中断(续)
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);
}
注册中断
138 编写设备驱动程序• 2006 年11 月
示例8–7 注册一组MSI 中断(续)
/* 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
注册中断
第8 章• 中断处理程序139
示例8–7 注册一组MSI 中断(续)
*/
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]);
注册中断
140 编写设备驱动程序• 2006 年11 月
示例8–7 注册一组MSI 中断(续)
}
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);
}
示例8–8 删除MSI 中断
以下示例说明如何删除MSI 中断。
注册中断
第8 章• 中断处理程序141
示例8–8 删除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);
}
注册中断
142 编写设备驱动程序• 2006 年11 月
中断处理程序的职责
中断处理程序具有一组要执行的职责。某些职责是框架所必需的,而某些职责则是设备所
必需的。所有中断处理程序均要求执行以下任务:
 确定设备是否会中断并可能相应地拒绝此中断。
中断处理程序必须首先检查设备,确定其是否已发出中断。如果设备未发出中断,则处
理程序必须返回DDI_INTR_UNCLAIMED。通过此步骤可实现设备轮询。设备轮询将通知系
统在给定中断优先级别的多个设备中的此设备是否已发出中断。
 通知设备正在对其进行服务。
通知设备服务是大多数设备所需的特定于设备的操作。例如,需要将S 总线设备中断,
直到驱动程序通知S 总线设备停止。此方法可保证对在同一优先级别中断的所有S 总线
设备都进行服务。
 执行任何与I/O 请求相关的处理。
设备会由于不同原因而发生中断,如传送完成或传送错误。此步骤可涉及使用数据访问
函数来读取设备的数据缓冲区,检查设备的错误寄存器,以及在数据结构中相应地设置
状态字段。中断分发和处理相对比较耗时。
 执行可以防止其他中断的任何附加处理。
例如,次要设备中读取数据的下一项。
 返回DDI_INTR_CLAIMED。
 必须始终声明MSI 中断。
对于MSI-X 中断,声明中断是可选的。在任一情况下都无需检查中断的拥有权,因为
MSI 和MSI-X 中断不是与其他设备共享的。
 支持热插拔和多个MSI 或MSI-X 中断的驱动程序应针对热插拔事件保留单独的中断,并
针对此中断注册单独的ISR(interrupt service routine,中断服务例程)。
以下示例说明了名为mydev 的设备的中断例程。
示例8–9中断示例
static uint_t
mydev_intr(caddr_t arg1, caddr_t arg2)
{
struct mydevstate *xsp = (struct mydevstate *)arg1;
uint8_t status;
volatile uint8_t temp;
中断处理程序的职责
第8 章• 中断处理程序143
示例8–9 中断示例(续)
/*
* Claim or reject the interrupt.This example assumes
* that the device’s CSR includes this information.
*/
mutex_enter(&xsp->high_mu);
/* use data access routines to 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 */
}
/*
* Inform the device that it is being serviced, and re-enable
* interrupts. The example assumes that writing to the
* CSR accomplishes this. The driver must ensure that this data
* access operation makes it to the device before the interrupt
* service routine returns. For example, using the data access
* functions to read the CSR, if it does not result in unwanted
* effects, can ensure this.
*/
ddi_put8(xsp->data_access_handle, &xsp->regp->csr,
CLEAR_INTERRUPT | ENABLE_INTERRUPTS);
/* flush store buffers */
中断处理程序的职责
144 编写设备驱动程序• 2006 年11 月
示例8–9 中断示例(续)
temp = ddi_get8(xsp->data_access_handle, &xsp->regp->csr);
mutex_exit(&xsp->mu);
return (DDI_INTR_CLAIMED);
}
中断例程执行的大多数步骤都依赖于设备本身的特定信息。查询设备的硬件手册可确定中
断原因,检测错误状态并访问设备数据寄存器。
处理高级别中断
高级别中断是指中断在调度程序级别或更高级别的那类中断。此级别不允许运行调度程
序。因此,调度程序无法抢占高级别中断处理程序。高级别中断不能依赖于调度程序,也
即是说,它们不会因为调度程序而阻塞。高级别中断只能使用互斥锁进行锁定。
由于此情况,驱动程序必须使用ddi_intr_hilevel(9F) 来确定其是否使用高级别中断。如果
ddi_intr_hilevel(9F) 返回true,则表明驱动程序可能无法attach,或者会使用双级别方案
来处理中断。
建议的方法是添加高级别中断处理程序,此中断程序只需触发优先级较低的软件中断即可
处理设备。驱动程序通过使用单独的互斥锁来防止高级中断处理程序使用数据,可实现更
大的并行性。
高级互斥锁
通过表示高级别中断的中断块cookie 所初始化的互斥锁称为高级互斥锁。虽然持有高级互
斥锁,但是驱动程序仍会受到与高级别中断处理程序相同的限制。
高级别中断处理示例
在以下示例中,高级互斥锁(xsp->high_mu) 仅用于保护在高级别中断处理程序和软中断处
理程序之间共享的数据。受保护的数据包括高级别中断处理程序和低级处理程序使用的队
列,以及用于指示低级处理程序正在运行的标志。单独的低级互斥锁(xsp->low_mu) 可防止
软中断处理程序使用驱动程序的其余部分。
处理高级别中断
第8 章• 中断处理程序145
示例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);
}
处理高级别中断
146 编写设备驱动程序• 2006 年11 月
示例8–10 使用attach() 处理高级别中断(续)
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()) {
处理高级别中断
第8 章• 中断处理程序147
示例8–10 使用attach() 处理高级别中断(续)
mutex_init(&xsp->high_mu, NULL, MUTEX_DRIVER,
(void *) (uintptr_t)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);
处理高级别中断
148 编写设备驱动程序• 2006 年11 月
示例8–10 使用attach() 处理高级别中断(续)
}
xsp->low_iblock_cookie = DDI_INTR_SOFTPRI_MAX;
mutex_init(&xsp->low_mu, NULL, MUTEX_DRIVER,
(void *)xsp->low_iblock_cookie);
} else {
/*
* regular interrupt registration continues from here
* skip soft interrupt stuff
*/
}
return (DDI_SUCCESS);
}
高级别中断例程用于服务设备并对数据进行排队。如果低级例程未运行,则高级例程会触
发软件中断,如以下示例所示。
示例8–11高级别中断例程
static uint_t
mydevhigh_intr(caddr_t arg1, caddr_t arg2)
{
处理高级别中断
第8 章• 中断处理程序149
示例8–11 高级别中断例程(续)
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);
次要设备中读取数据,并对数据进行排队以供低级中断处理程序处理;
if (xsp->softint_running)
need_softint = 0;
else {
处理高级别中断
150 编写设备驱动程序• 2006 年11 月
示例8–11 高级别中断例程(续)
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)
处理高级别中断
第8 章• 中断处理程序151
示例8–12 低级软中断例程(续)
{
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 ( 队列为空) {
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);
将数据从高级队列中移除;
mutex_exit(&mydevp->high_mu);
处理高级别中断
152 编写设备驱动程序• 2006 年11 月
示例8–12 低级软中断例程(续)
标准中断处理
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);
}
处理高级别中断
第8 章• 中断处理程序153
154
直接内存访问(Direct MemoryAccess,DMA)
许多设备都可以临时控制总线。这些设备可以执行涉及主内存和其他设备的数据传送。由
于设备执行这些操作的过程中无需借助于CPU,因此该类型的数据传送称为直接内存访问
(direct memory access, DMA)。可以执行的DMA传送类型如下:
 两个设备之间
 设备和内存之间
 内存和内存之间
本章仅介绍设备和内存之间的传送。本章提供有关以下主题的信息:
 第155 页中的“DMA模型”
 第156 页中的“设备DMA的类型”
 第163 页中的“管理DMA资源”
 第157 页中的“DMA软件组件: 句柄、窗口和Cookie”
 第158 页中的“DMA操作”
 第163 页中的“管理DMA资源”
 第178 页中的“DMA窗口”
DMA模型
Solaris 设备驱动程序接口/驱动程序内核接口(Device Driver Interface/Driver-Kernel Interface,
DDI/DKI) 为DMA提供了独立于体系结构的高级别模型。通过此模型,框架(即DMA例
程)可以隐藏此类体系结构特定的详细信息,例如:
 设置DMA映射
 生成分散/集中列表
 确保I/O 和CPU 高速缓存一致
DDI/DKI 中使用了若干个抽象术语来描述DMA事务的各个方面:
 DMA对象-作为DMA传送的源或目标的内存。
 DMA 句柄-成功调用ddi_dma_alloc_handle (9F) 后返回的不透明对象。在后续的DMA
子例程调用中可以使用DMA句柄来引用此类DMA对象。
9第9 章
155
 DMAcookie-ddi_dma_cookie (9S) 结构(ddi_dma_cookie_t) 用于描述DMA对象中可由
设备完全寻址的连续部分。该cookie 包含对DMA引擎进行编程所需的DMA寻址信息。
设备驱动程序不会直接将对象映射到内存中,而是为内存对象分配DMA资源。然后,
DMA例程将执行为DMA访问设置对象时所需的任何特定于平台的操作。驱动程序将收到
一个DMA句柄,用于标识为该对象分配的DMA资源。此句柄对于设备驱动程序而言是不
透明的。驱动程序必须保存句柄并在后续调用中将其传递给DMA例程。驱动程序不应以任
何方式解释句柄。
针对DMA句柄定义的操作可提供以下服务:
 处理DMA资源
 同步DMA对象
 检索已分配资源的特性
设备DMA的类型
设备可执行以下三种类型的DMA:
 总线主控器DMA
 第三方DMA
 第一方DMA
总线主控器DMA
在设备用作实际总线主控器的情况下,驱动程序应直接对该设备的DMA寄存器进行编程。
例如,如果DMA引擎驻留在设备板上,则设备便会用作总线主控器。传送地址和计数从
DMAcookie 中获取,并将传递给设备。
第三方DMA
第三方DMA使用驻留在主系统板上的系统DMA引擎,该引擎中有若干个可供设备使用的
DMA通道。设备依赖于系统的DMA引擎来执行设备与内存之间的数据传送。驱动程序使
用DMA引擎例程(请参见ddi_dmae(9F))对DMA引擎进行初始化和编程。每次进行DMA
数据传送时,驱动程序都会对DMA引擎进行编程,然后会向设备发出命令,以便借助该引
擎来启动传送操作。
第一方DMA
执行第一方DMA时,设备使用系统DMA引擎中的通道来驱动该设备的DMA总线循
环。ddi_dmae_1stparty(9F) 函数用于在级联模式下对此通道进行配置,以免DMA引擎干扰
传送。
设备DMA的类型
156 编写设备驱动程序• 2006 年11 月
主机平台DMA的类型
设备运行的平台可提供直接内存访问(direct memory access, DMA) 或直接虚拟内存访问
(direct virtual memory access, DVMA)。
在支持DMA的平台上,系统会为设备提供物理地址以执行传送。在此情况下,DMA对象
的传送实际上会包含许多在物理上不连续的传送。例如,当应用程序传送跨越若干连续虚
拟页(但这些虚拟页映射到物理上不连续的页)的缓冲区时。要处理不连续的内存,用于
这些平台的设备通常需要具有特定种类的分散/集中DMA功能。通常,x86 系统会为直接内
存传送提供物理地址。
在支持DVMA的平台上,系统会为设备提供虚拟地址以执行传送。在此情况下,基础平台
提供的内存管理单元(memory management unit, MMU) 会将对这些虚拟地址的设备访问转换
为正确的物理地址。设备会与可映射到不连续物理页的连续虚拟映像之间来回进行传送。
在这些平台上运行的设备无需分散/集中DMA功能。通常,SPARC 平台会为直接内存传送
提供虚拟地址。
DMA软件组件: 句柄、窗口和Cookie
DMA句柄是表示对象(通常为内存缓冲区或地址)的不透明指针。设备通过DMA句柄可
执行DMA传送。对DMA例程的若干个不同调用可使用句柄来标识为对象分配的DMA资
源。
DMA句柄所表示的对象全部包含在一个或多个DMAcookie 中。DMAcookie 表示DMA引擎
在数据传送中使用的一段连续内存。系统会根据以下信息将对象划分为多个cookie:
 驱动程序提供的ddi_dma_attr(9S) 特性结构
 目标对象的内存位置
 目标对象的对齐
如果一个对象不满足DMA引擎的限制,则必须将该对象分为多个DMA窗口。一次只能为
一个窗口激活和分配资源。使用ddi_dma_getwin(9F) 函数可在一个对象内的多个窗口之间
切换。每个DMA窗口都包含一个或多个DMAcookie。有关更多信息,请参见第178 页中的
“DMA窗口”。
一些DMA引擎可以接受多个cookie。此类引擎不用借助系统即可执行分散/集中I/O。如果
从一个绑定中返回多个cookie,则驱动程序应重复调用ddi_dma_nextcookie (9F) 以检索每
个cookie。然后,必须将这些cookie 编程到引擎中。随后可对设备进行编程,以传送这些
DMAcookie 聚集所包含的总字节数。
DMA软件组件: 句柄、窗口和Cookie
 

以上文章转自于 : http://developers.sun.com.cn/
阅读(643) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~