JavaScript is required to for searching.
跳过导航链接
退出打印视图
编写设备驱动程序     Oracle Solaris 11.1 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.  Oracle Solaris DDI/DKI 服务汇总

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

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

E.  pci.conf 文件

索引

请告诉我们如何提高我们的文档:
过于简略
不易阅读或难以理解
重要信息缺失
错误的内容
需要翻译的版本
其他
Your rating has been updated
感谢您的反馈!

您的反馈将非常有助于我们提供更好的文档。 您是否愿意参与我们的内容改进并提供进一步的意见?

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

本节介绍一种执行异步 I/O 传输的方法。驱动程序将对 I/O 请求进行排队,然后将控制权返回到调用方。还是假设硬件是一次可以传输一个缓冲区的简单磁盘设备。当数据传输完成时,设备中断。如果发生错误,也会产生中断。执行异步数据传输的基本步骤如下:

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

  2. 对请求进行排队。

  3. 开始第一个传输。

  4. 处理中断的设备。

检查是否有无效的 buf 请求

正如同步传输示例中所示,设备驱动程序会检查传递到 strategy(9E)buf(9S) 结构是否有效。有关更多详细信息,请参见同步数据传输(块驱动程序)

对请求进行排队

与同步数据传输不同,驱动程序不等待异步请求完成,而是向队列中添加请求。队列的开头可以是当前传输,也可以是保留活动请求的状态结构中的独立字段,如示例 16-5 中所示。

如果队列开始是空的,那么硬件不忙,并且 strategy(9E) 在返回之前开始传输。否则,如果在队列非空的情况下完成一个传输,则中断例程会开始一个新的传输。为方便起见,示例 16-5 仅在一个单独的例程中决定是否开始新的传输。

驱动程序可以使用 buf(9S) 结构中的 av_forwav_back 成员来管理传输请求列表。可以使用单个指针管理单链接表,也可以同时使用两个指针建立双链接表。设备硬件规格指定哪种类型的列表管理(如插入策略)用于优化设备的性能。传输列表是按设备提供的列表,因此列表的头和尾都存储在状态结构中。

以下示例提供了对驱动程序共享数据(如传输列表)有访问权限的多个线程。您必须标识共享数据,并且必须用互斥锁保护这些数据。有关互斥锁的更多详细信息,请参见第 3 章

示例 16-5 对块驱动程序的数据传输请求进行排队

static int
xxstrategy(struct buf *bp)
{
    struct xxstate *xsp;
    minor_t instance;
    instance = getminor(bp->b_edev);
    xsp = ddi_get_soft_state(statep, instance);
    /* ... */
    /* validate transfer request */
    /* ... */
    /*
     * Add the request to the end of the queue. Depending on the device, a sorting
     * algorithm, such as disksort(9F) can be used if it improves the
     * performance of the device.
     */
    mutex_enter(&xsp->mu);
    bp->av_forw = NULL;
    if (xsp->list_head) {
        /* Non-empty transfer list */
        xsp->list_tail->av_forw = bp;
        xsp->list_tail = bp;
    } else {
        /* Empty Transfer list */
        xsp->list_head = bp;
        xsp->list_tail = bp;
    }
    mutex_exit(&xsp->mu);
    /* Start the transfer if possible */
    (void) xxstart((caddr_t)xsp);
    return (0);
}

开始第一个传输

可实现排队的设备驱动程序通常具有 start() 例程。start() 可将下一个请求从队列中删除,并开始出入设备的数据传输。在以下示例中,不管设备状态是忙还是空闲,start() 将处理所有请求。


注 - 必须写入 start() 以便从任何上下文都可以对其进行调用。内核上下文中的策略例程与中断上下文中的中断例程都可以调用 start()


每次 strategy() 对请求排队时,将由 strategy(9E) 调用 start(),以便可以启动空闲设备。如果设备忙,则 start() 立即返回。

在处理程序从声明的中断返回之前,也可以由中断处理程序调用 start(),以便可以为非空队列提供服务。如果队列是空的,则 start() 立即返回。

由于 start() 是一个专用驱动程序例程,因此 start() 可以采用任何参数并返回任何类型。将写入以下代码样例用作 DMA 回调,尽管没有显示那一部分。相应地,此示例必须采用 caddr_t 作为参数并返回 int。有关 DMA 回调例程的更多信息,请参见处理资源分配故障

示例 16-6 开始块驱动程序的第一个数据请求

static int
xxstart(caddr_t arg)
{
    struct xxstate *xsp = (struct xxstate *)arg;
    struct buf *bp;

    mutex_enter(&xsp->mu);
    /*
     * If there is nothing more to do, or the device is
     * busy, return.
     */
    if (xsp->list_head == NULL || xsp->busy) {
       mutex_exit(&xsp->mu);
       return (0);
    }
    xsp->busy = 1;
    /* Get the first buffer off the transfer list */
    bp = xsp->list_head;
    /* Update the head and tail pointer */
    xsp->list_head = xsp->list_head->av_forw;
    if (xsp->list_head == NULL)
       xsp->list_tail = NULL;
    bp->av_forw = NULL;
    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;
    ddi_put32(xsp->data_access_handle, &xsp->regp->dma_addr,
        cookie.dmac_address);
    ddi_put32(xsp->data_access_handle, &xsp->regp->dma_size,
        (uint32_t)cookie.dmac_size);
    ddi_put8(xsp->data_access_handle, &xsp->regp->csr,
        ENABLE_INTERRUPTS | START_TRANSFER);
    return (0);
}

处理中断的设备

中断例程与异步版本类似,只是增加了对 start() 的调用并删除了对 cv_signal(9F) 的调用。

示例 16-7 异步中断的块驱动程序例程

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;
    mutex_exit(&xsp->mu);
    (void) xxstart((caddr_t)xsp);
    return (DDI_INTR_CLAIMED);
}