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.  映射设备和内核内存

内存映射概述

导出映射

segmap(9E) 入口点

devmap(9E) 入口点

将设备内存与用户映射相关联

将内核内存与用户映射相关联

为用户访问分配内核内存

将内核内存导出到应用程序

释放为用户访问导出的内核内存

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.  Oracle Solaris DDI/DKI 服务汇总

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

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

E.  pci.conf 文件

索引

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

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

将内核内存与用户映射相关联

一些设备驱动程序可能需要分配可供用户程序通过 mmap(2) 进行访问的内核内存。一个示例是为两个应用程序间的通信设置共享内存。另一个示例是在驱动程序和应用程序之间共享内存。

将内核内存导出到用户应用程序时,请执行以下步骤:

  1. 使用 ddi_umem_alloc(9F) 分配内核内存。

  2. 使用 devmap_umem_setup(9F) 导出内存。

  3. 不再需要内存时,使用 ddi_umem_free(9F) 释放内存。

为用户访问分配内核内存

使用 ddi_umem_alloc(9F) 可以分配导出到应用程序的内核内存。 ddi_umem_alloc() 的语法如下所示:

void *ddi_umem_alloc(size_t size, int flag, ddi_umem_cookie_t 
*cookiep);

其中:

size

要分配的字节数。

flag

用于确定休眠条件和内存类型。

cookiep

指向内核内存 cookie 的指针。

ddi_umem_alloc(9F) 分配按页对齐的内核内存。ddi_umem_alloc () 返回一个指向所分配内存的指针。最初,内存被零填充。分配的字节数是系统页面大小的倍数,该页面大小是通过 size 参数向上舍入得到的。分配的内存可在内核中使用。此内存也可导出到应用程序。cookiep 是指向用来描述所分配的内核内存的内核内存 cookie 的指针。驱动程序将内核内存导出到用户应用程序时,devmap_umem_setup(9F) 中会使用 cookiep

flag 参数用于指示 ddi_umem_alloc(9F) 是立即阻塞还是返回,以及分配的内核内存是否可换页。flag 参数的值如下所示:

DDI_UMEM_NOSLEEP

驱动程序无需等待内存成为可用。如果内存不可用,则返回 NULL

DDI_UMEM_SLEEP

驱动程序可以无限等待,直到内存可用为止。

DDI_UMEM_PAGEABLE

驱动程序允许内存页被换出。如果未设置,则锁定内存。

ddi_umem_lock() 函数可以执行设备锁定内存检查。此函数针对 project.max-locked-memory 中指定的限制值进行检查。如果当前项目的锁定内存使用量低于限制,则会增加项目的锁定内存字节计数。进行限制检查后,内存将会锁定。ddi_umem_unlock() 函数可以解除锁定内存,从而减少项目的锁定内存字节计数。

其中所用的记帐方法是不严密的 "full price"(足价)模式。例如,对于同一项目中 umem_lockmemory() 的具有重叠内存区域的两个调用方会被计数两次。

有关 project.max-locked-memoryzone.max-locked_memory 对安装了区域的 Oracle Solaris 系统的资源控制的信息,请参见《Resource Management and Oracle Solaris Zones Developer’s Guide》resource_controls(5)

以下示例说明如何为应用程序访问分配内核内存。驱动程序会导出一页内核内存,它将被多个应用程序用作共享存储区。应用程序第一次映射共享页时,会在 segmap(9E) 中分配内存。如果驱动程序必须支持多个应用程序数据模型,则会再分配一页。例如,64 位驱动程序可能同时将内存导出到 64 位应用程序和 32 位应用程序。64 位应用程序共享第一页,32 位应用程序共享第二页。

示例 10-4 使用 ddi_umem_alloc() 例程

static int
xxsegmap(dev_t dev, off_t off, struct as *asp, caddr_t *addrp, off_t len,
    unsigned int prot, unsigned int maxprot, unsigned int flags, 
    cred_t *credp)
{
    int error;
    minor_t instance = getminor(dev);
    struct xxstate *xsp = ddi_get_soft_state(statep, instance);

    size_t mem_size;
        /* 64-bit driver supports 64-bit and 32-bit applications */
    switch (ddi_mmap_get_model()) {
        case DDI_MODEL_LP64:
             mem_size = ptob(2);
             break;
        case DDI_MODEL_ILP32:
             mem_size = ptob(1);
             break;
    }

    mutex_enter(&xsp->mu);
    if (xsp->umem == NULL) {
        /* allocate the shared area as kernel pageable memory */
        xsp->umem = ddi_umem_alloc(mem_size,
            DDI_UMEM_SLEEP | DDI_UMEM_PAGEABLE, &xsp->ucookie);
    }
    mutex_exit(&xsp->mu);
    /* Set up the user mapping */
    error = devmap_setup(dev, (offset_t)off, asp, addrp, len,
        prot, maxprot, flags, credp);
    return (error);
}

将内核内存导出到应用程序

使用 devmap_umem_setup(9F) 可将内核内存导出到用户应用程序。devmap_umem_setup() 必须通过驱动程序的 devmap(9E) 入口点进行调用 。devmap_umem_setup() 的语法如下所示:

int devmap_umem_setup(devmap_cookie_t handle, dev_info_t *dip,
    struct devmap_callback_ctl *callbackops, ddi_umem_cookie_t cookie,
    offset_t koff, size_t len, uint_t maxprot, uint_t flags,
    ddi_device_acc_attr_t *accattrp);

其中:

handle

用于描述映射的不透明结构。

dip

指向设备的 dev_info 结构的指针。

callbackops

指向 devmap_callback_ctl(9S) 结构的指针。

cookie

ddi_umem_alloc(9F) 返回的内核内存 cookie。

koff

cookie 指定的内核内存中的偏移。

len

导出的长度(以字节为单位)。

maxprot

用于为导出的映射指定可能的最大保护。

flags

必须设置为 DEVMAP_DEFAULTS

accattrp

指向 ddi_device_acc_attr(9S) 结构的指针。

handle 是系统用来标识映射的设备映射句柄。handle 通过 devmap(9E) 入口点传入。dip 是指向设备的 dev_info 结构的指针。callbackops 允许向驱动程序通知有关映射的用户事件。导出内核内存时,大多数驱动程序都会将 callbackops 设置为 NULL

kofflen 用于在 ddi_umem_alloc(9F) 分配的内核内存中指定一个范围。如果用户的应用程序映射位于通过 devmap(9E) 入口点传入的偏移上,则可对此范围进行访问。通常,驱动程序将 devmap(9E) 偏移直接传递给 devmap_umem_setup(9F)。然后,mmap(2) 的返回地址将映射到 ddi_umem_alloc(9F) 返回的内核地址。kofflen 必须按页对齐。

通过 maxprot,驱动程序可为导出的内核内存中的不同区域指定不同的保护。例如,通过仅设置 PROT_READPROT_USER,一个区域可能不允许写访问。

以下示例说明如何将内核内存导出到应用程序。驱动程序首先检查请求的映射是否位于分配的内核内存区域之内。如果 64 位驱动程序收到来自 32 位应用程序的映射请求,则会将该请求重定向到内核存储区的第二页。此重定向可确保仅有编译到相同数据模型的应用程序才能共享相同的页。

示例 10-5 devmap_umem_setup(9F) 例程

static int
xxdevmap(dev_t dev, devmap_cookie_t handle, offset_t off, size_t len,
    size_t *maplen, uint_t model)
{
    struct xxstate *xsp;
    int    error;

    /* round up len to a multiple of a page size */
    len = ptob(btopr(len));
    /* check if the requested range is ok */
    if (off + len > ptob(1))
        return (ENXIO);
    xsp = ddi_get_soft_state(statep, getminor(dev));
    if (xsp == NULL)
        return (ENXIO);

    if (ddi_model_convert_from(model) == DDI_MODEL_ILP32)
        /* request from 32-bit application. Skip first page */
        off += ptob(1);

    /* export the memory to the application */
    error = devmap_umem_setup(handle, xsp->dip, NULL, xsp->ucookie,
        off, len, PROT_ALL, DEVMAP_DEFAULTS, NULL);
    *maplen = len;
    return (error);
}

释放为用户访问导出的内核内存

卸载驱动程序时,必须通过调用 ddi_umem_alloc(9F) 释放 ddi_umem_free(9F) 分配的内存。

void ddi_umem_free(ddi_umem_cookie_t cookie);

cookieddi_umem_alloc(9F) 返回的内核内存 cookie。