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)

10.  映射设备和内核内存

11.  设备上下文管理

12.  电源管理

13.  强化 Oracle Solaris 驱动程序

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

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

15.  字符设备驱动程序

16.  块设备驱动程序

块驱动程序结构概述

文件 I/O

块设备自动配置

控制设备访问

open() 入口点(块驱动程序)

close() 入口点(块驱动程序)

strategy() 入口点

buf 结构

bp_mapin 结构

同步数据传输(块驱动程序)

异步数据传输(块驱动程序)

检查是否有无效的 buf 请求

对请求进行排队

开始第一个传输

处理中断的设备

dump()print() 入口点

dump() 入口点(块驱动程序)

print() 入口点(块驱动程序)

磁盘设备驱动程序

磁盘 ioctl

磁盘性能

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 文件

索引

同步数据传输(块驱动程序)

本节介绍一种执行同步 I/O 传输的简单方法。此方法假设硬件是使用 DMA 一次只能传输一个数据缓冲区的简单磁盘设备。另一个假设是磁盘可以通过软件命令启动和停止。设备驱动程序的 strategy(9E) 例程等待当前请求完成,然后再接受新的请求。当传输完成时,设备中断。如果发生错误,设备也中断。

执行块驱动程序的同步数据传输的步骤如下:

  1. 检查是否有无效的 buf(9S) 请求。

    检查传递到 strategy(9E)buf(9S) 结构是否有效。所有驱动程序应检查以下条件:

    • 请求起始于有效的块。驱动程序将 b_blkno 字段转换为正确的设备偏移,然后确定该偏移对设备而言是否有效。

    • 请求不能超出设备上的最后一个块。

    • 满足特定于设备的要求。

    如果遇到错误,驱动程序应使用 bioerror(9F) 指示相应的错误。然后,驱动程序通过调用 biodone(9F) 来完成请求。biodone() 会通知 strategy(9E) 的调用方传输已完成。在本例中,传输因错误而停止。

  2. 检查此设备是否忙。

    同步数据传输允许对设备进行单线程访问。设备驱动程序通过两种方式执行这种访问:

    • 驱动程序保持由互斥锁保护的忙标志。

    • 当设备忙时,驱动程序等待 cv_wait(9F) 的条件变量。

    如果设备忙,线程会一直等待,直到中断处理程序指示设备不再忙。cv_broadcast(9F)cv_signal(9F) 函数可以指示可用的状态。有关条件变量的详细信息,请参见第 3 章

    当设备不再忙时,strategy(9E) 例程将设备标记为可用。然后,strategy() 为传输准备缓冲区和设备。

  3. 为 DMA 设置缓冲区。

    通过使用 ddi_dma_alloc_handle(9F) 为 DMA 传送准备数据缓冲区,以便分配 DMA 句柄。使用 ddi_dma_buf_bind_handle(9F) 将数据缓冲区绑定到该句柄。有关设置 DMA 资源及相关数据结构的信息,请参见第 9 章

  4. 开始传输。

    此时,指向 buf(9S) 结构的指针将保存在设备的状态结构中。然后中断例程通过调用 biodone(9F) 来完成传输。

    设备驱动程序随后访问设备寄存器以启动数据传输。在大多数情况下,驱动程序应通过使用互斥锁来保护设备寄存器免受其他线程干扰。在本例中,由于 strategy(9E) 是单线程的,因此没有必要保护设备寄存器。有关数据锁定的详细信息,请参见第 3 章

    当执行线程已经启动设备的 DMA 引擎时,驱动程序可以将执行控制权返回到正在调用的例程,如下所示:

    static int
    xxstrategy(struct buf *bp)
    {
        struct xxstate *xsp;
        struct device_reg *regp;
        minor_t instance;
        ddi_dma_cookie_t cookie;
        instance = getminor(bp->b_edev);
        xsp = ddi_get_soft_state(statep, instance);
        if (xsp == NULL) {
           bioerror(bp, ENXIO);
           biodone(bp);
           return (0);
        }
        /* validate the transfer request */
        if ((bp->b_blkno >= xsp->Nblocks) || (bp->b_blkno < 0)) {
           bioerror(bp, EINVAL);    
           biodone(bp);
           return (0);
        }
        /*
         * Hold off all threads until the device is not busy.
         */
        mutex_enter(&xsp->mu);
        while (xsp->busy) {
           cv_wait(&xsp->cv, &xsp->mu);
        }
        xsp->busy = 1;
        mutex_exit(&xsp->mu);
        /* 
         * If the device has power manageable components, 
         * mark the device busy with pm_busy_components(9F),
         * and then ensure that the device 
         * is powered up by calling pm_raise_power(9F).
         *
         * Set up DMA resources with ddi_dma_alloc_handle(9F) and
         * ddi_dma_buf_bind_handle(9F).
         */
        xsp->bp = bp;
        regp = xsp->regp;
        ddi_put32(xsp->data_access_handle, &regp->dma_addr,
            cookie.dmac_address);
        ddi_put32(xsp->data_access_handle, &regp->dma_size,
             (uint32_t)cookie.dmac_size);
        ddi_put8(xsp->data_access_handle, &regp->csr,
             ENABLE_INTERRUPTS | START_TRANSFER);
        return (0);
    }
  5. 处理中断的设备。

    当设备完成数据传输时,设备会生成中断,最终导致驱动程序的中断例程被调用。注册中断时,大多数驱动程序将设备的状态结构指定为中断例程的参数。请参见 ddi_add_intr(9F) 手册页和注册中断。随后,中断例程可以访问正在被传输的 buf(9S) 结构,以及状态结构提供的任何其他信息。

    中断处理程序会检查设备的状态寄存器,来确定是否在未发生任何错误的情况下完成传输。如果发生错误,处理程序应该使用 bioerror(9F) 指示相应的错误。处理程序还应该清除设备的挂起中断,然后通过调用 biodone(9F) 来完成传输。

    最后一项任务是处理程序清除忙标志。处理程序随后对条件变量调用 cv_signal(9F)cv_broadcast(9F),发出设备不再忙的信号。此通知会使在 strategy(9E) 中等待设备的其他线程继续进行下一个数据传输。

    以下示例说明了一个同步中断例程。

示例 16-4 块驱动程序的同步中断例程

static u_int
xxintr(caddr_t arg)
{
    struct xxstate *xsp = (struct xxstate *)arg;
    struct buf *bp;
    uint8_t status;
    mutex_enter(&xsp->mu);
    status = ddi_get8(xsp->data_access_handle, &xsp->regp->csr);
    if (!(status & INTERRUPTING)) {
       mutex_exit(&xsp->mu);
       return (DDI_INTR_UNCLAIMED);
    }
    /* Get the buf responsible for this interrupt */
    bp = xsp->bp;
    xsp->bp = NULL;
    /*
     * This example is for a simple device which either
     * succeeds or fails the data transfer, indicated in the
     * command/status register.
     */
    if (status & DEVICE_ERROR) {
       /* failure */
       bp->b_resid = bp->b_bcount;
       bioerror(bp, EIO);
    } else {
       /* success */
       bp->b_resid = 0;
    }
    ddi_put8(xsp->data_access_handle, &xsp->regp->csr,
       CLEAR_INTERRUPT);
    /* The transfer has finished, successfully or not */
    biodone(bp);
    /*
     * If the device has power manageable components that were
     * marked busy in strategy(9F), mark them idle now with
     * pm_idle_component(9F)
     * Release any resources used in the transfer, such as DMA
     * resources ddi_dma_unbind_handle(9F) and
     * ddi_dma_free_handle(9F).
     *
     * Let the next I/O thread have access to the device.
     */
    xsp->busy = 0;
    cv_signal(&xsp->cv);
    mutex_exit(&xsp->mu);
    return (DDI_INTR_CLAIMED);
}