JavaScript is required to for searching.
跳过导航链接
退出打印视图
编写设备驱动程序     Oracle Solaris 10 1/13 Information Library (简体中文)
search filter icon
search icon

文档信息

前言

第 1 部分针对 Oracle Solaris 平台设计设备驱动程序

1.  Oracle Solaris 设备驱动程序概述

2.  Oracle Solaris 内核和设备树

3.  多线程

4.  属性

5.  管理事件和排队任务

6.  驱动程序自动配置

7.  设备访问:程控 I/O

8.  中断处理程序

9.  直接内存访问 (Direct Memory Access, DMA)

DMA 模型

设备 DMA 的类型

总线主控器 DMA

第三方 DMA

第一方 DMA

主机平台 DMA 的类型

DMA 软件组件:句柄、窗口和 Cookie

DMA 操作

执行总线主控器 DMA 传送

执行第一方 DMA 传送

执行第三方 DMA 传送

DMA 特性

ddi_dma_attr 结构

S 总线示例

ISA 总线示例

管理 DMA 资源

对象锁定

分配 DMA 句柄

分配 DMA 资源

设备寄存器结构

DMA 回调示例

确定最大突发流量大小

分配专用 DMA 缓冲区

处理资源分配故障

对 DMA 引擎进行编程

释放 DMA 资源

释放 DMA 句柄

取消 DMA 回调

同步内存对象

高速缓存

ddi_dma_sync() 函数

DMA 窗口

10.  映射设备和内核内存

11.  设备上下文管理

12.  电源管理

13.  强化 Oracle Solaris 驱动程序

14.  分层驱动程序接口 (Layered Driver Interface, LDI)

第 2 部分设计特定种类的设备驱动程序

15.  字符设备驱动程序

16.  块设备驱动程序

17.  SCSI 目标驱动程序

18.  SCSI 主机总线适配器驱动程序

19.  网络设备驱动程序

20.  USB 驱动程序

21.  SR-IOV 驱动程序

第 3 部分生成设备驱动程序

22.  编译、装入、打包和测试驱动程序

23.  调试、测试和调优设备驱动程序

24.  推荐的编码方法

第 4 部分附录

A.  硬件概述

B.  Solaris DDI/DKI 服务汇总

C.  使设备驱动程序支持 64 位

D.  控制台帧缓存器驱动程序

E.  pci.conf 文件

索引

DMA 窗口

如果对象不满足 DMA 引擎的限制,则必须将传送分为一系列较小的传送。驱动程序本身即可对传送进行拆分。或者,驱动程序也可以允许系统仅为对象的一部分分配资源,从而创建一系列 DMA 窗口。允许系统分配资源是首选解决方案,因为系统管理资源的效率比驱动程序高。

DMA 窗口有两个特性。offset 特性是从对象的开头度量的。length 特性是要分配的内存的字节数。在进行部分分配后,只有一系列在 offset 开始的 length 字节分配了资源。

请求 DMA 窗口的方法是将 DDI_DMA_PARTIAL 标志指定为 ddi_dma_buf_bind_handle(9F)ddi_dma_addr_bind_handle(9F) 的参数。如果可以建立窗口,则这两个函数都将返回 DDI_DMA_PARTIAL_MAP。但是,系统可能会为整个对象分配资源,此时将返回 DDI_DMA_MAPPED。驱动程序应检查返回值,以确定 DMA 窗口是否正在使用。请参见以下示例。

示例 9-7 设置 DMA 窗口

static int
xxstart (caddr_t arg)
{
    struct xxstate *xsp = (struct xxstate *)arg;
    struct device_reg *regp = xsp->reg;
    ddi_dma_cookie_t cookie;
    int status;
    mutex_enter(&xsp->mu);
    if (xsp->busy) {
        /* transfer in progress */
        mutex_exit(&xsp->mu);
        return (DDI_DMA_CALLBACK_RUNOUT);
    }
    xsp->busy = 1;
    mutex_exit(&xsp->mu);
    if ( /* transfer is a read */) {
        flags = DDI_DMA_READ;
    } else {
        flags = DDI_DMA_WRITE;
    }
    flags |= DDI_DMA_PARTIAL;
    status = ddi_dma_buf_bind_handle(xsp->handle, xsp->bp,
        flags, xxstart, (caddr_t)xsp, &cookie, &ccount);
    if (status != DDI_DMA_MAPPED &&
        status != DDI_DMA_PARTIAL_MAP)
        return (DDI_DMA_CALLBACK_RUNOUT);
    if (status == DDI_DMA_PARTIAL_MAP) {
        ddi_dma_numwin(xsp->handle, &xsp->nwin);
        xsp->partial = 1;
        xsp->windex = 0;
    } else {
        xsp->partial = 0;
    }
    /* Program the DMA engine. */
    return (DDI_DMA_CALLBACK_DONE);
}

有两个函数可对 DMA 窗口执行操作。第一个函数 ddi_dma_numwin(9F) 可为特定的 DMA 对象返回 DMA 窗口数。另一个函数 ddi_dma_getwin(9F) 允许在对象内重新定位,即重新分配系统资源。ddi_dma_getwin () 函数用于从当前窗口切换到对象中的新窗口。由于 ddi_dma_getwin() 会将系统资源重新分配给新窗口,因此前面的窗口将变为无效。


注意

注意 - 在向当前窗口的传送完成之前,请勿通过调用 ddi_dma_getwin() 来移动 DMA 窗口。请一直等待,直至向当前窗口的传送完成为止,即出现中断的时候。然后,调用 ddi_dma_getwin() 以避免数据损坏。


ddi_dma_getwin() 函数通常是从某个中断例程调用的,如示例 9-8 中所示。调用驱动程序会导致启动第一个 DMA 传送。后续传送将从中断例程中启动。

中断例程会检查设备的状态,以确定设备是否已成功完成传送。如果未成功完成传送,则会进行正常错误恢复。如果传送成功,例程必须确定逻辑传送是否已完成。完整的传送包括 buf(9S) 结构所指定的整个对象。在部分传送中,仅会移动一个 DMA 窗口。在部分传送中,中断例程将使用 ddi_dma_getwin(9F) 移动窗口、检索新 cookie 并启动其他 DMA 传送。

如果逻辑请求已完成,则中断例程将检查待处理的请求。如有必要,中断例程会启动传送。否则,例程将返回,而不调用其他 DMA 传送。以下示例说明了常见的流程控制。

示例 9-8 使用 DMA 窗口中断处理程序

static uint_t
xxintr(caddr_t arg)
{
    struct xxstate *xsp = (struct xxstate *)arg;
    uint8_t    status;
    volatile   uint8_t   temp;
    mutex_enter(&xsp->mu);
    /* read status */
    status = ddi_get8(xsp->access_hdl, &xsp->regp->csr);
    if (!(status & INTERRUPTING)) {
        mutex_exit(&xsp->mu);
        return (DDI_INTR_UNCLAIMED);
    }
    ddi_put8(xsp->access_hdl,&xsp->regp->csr, CLEAR_INTERRUPT);
    /* for store buffers */
    temp = ddi_get8(xsp->access_hdl, &xsp->regp->csr);
    if ( /* an error occurred during transfer */ ) {
        bioerror(xsp->bp, EIO);
        xsp->partial = 0;
    } else {
        xsp->bp->b_resid -= /* amount transferred */ ;
    }

    if (xsp->partial && (++xsp->windex < xsp->nwin)) {
        /* device still marked busy to protect state */
        mutex_exit(&xsp->mu);
        (void) ddi_dma_getwin(xsp->handle, xsp->windex,
            &offset, &len, &cookie, &ccount);
        /* Program the DMA engine with the new cookie(s). */
        return (DDI_INTR_CLAIMED);
    }
    ddi_dma_unbind_handle(xsp->handle);
    biodone(xsp->bp);
    xsp->busy = 0;
    xsp->partial = 0;
    mutex_exit(&xsp->mu);
    if ( /* pending transfers */ ) {
        (void) xxstart((caddr_t)xsp);
    }
    return (DDI_INTR_CLAIMED);
}