调试准备方法
第22 章• 推荐的编码方法535
#define dcmn_err(X) /* nothing */
#endif
[...]
/* Note:double parentheses are required when using dcmn_err. */
dcmn_err((CE_NOTE, "Error!"));
可以采用多种方法扩展此技术。一种方法是根据xxdebug 的值指定来自cmn_err(9F) 的不同
消息。但在此类情况下,必须注意不要用大量的调试信息使代码变得晦涩难懂。
另一种常见方案是编写xxlog() 函数,以便使用vsprintf(9F) 或vcmn_err(9F) 来处理变量参
数列表。
防御性编程
以下防御性编程方法有助于避免以下问题: 系统崩溃或挂起、浪费系统资源或传播已损坏
数据。
所有Solaris 驱动程序都应遵守以下编码做法:
每个硬件都应由设备驱动程序的单独实例来控制。(请参见第95 页中的“设备配置概
念”。)
程控I/O (Programmed I/O, PIO) 只能使用适当的数据访问句柄借助DDI 访问函数来执
行。(请参见第7 章。)
设备驱动程序必须假定从该设备接收的数据可能已损坏。在使用数据之前驱动程序必须
检查数据的完整性。
驱动程序必须避免向系统的其余部分释放错误数据。
在驱动程序中仅使用记录的DDI 函数和接口。
驱动程序必须确保设备仅向完全由该驱动程序控制的DMA缓冲区(DDI_DMA_READ) 的内
存页中写入内容。此方法可以防止DMA故障损坏系统主内存的任意部分。
如果设备已锁定,设备驱动程序必须不能无限使用系统资源。如果设备声明连续处于忙
状态,该驱动程序应超时。驱动程序还应检测异常(有问题的)中断请求,并执行适当
操作。
在Solaris OS 中,设备驱动程序必须支持热插拔。
设备驱动程序必须使用回调,而非等待资源。
发生故障后,驱动程序必须释放资源。例如,即使在硬件出现故障后,系统也必须能够
关闭所有次要设备并分离驱动程序实例。
防御性编程
536 编写设备驱动程序• 2006 年11 月
使用单独的设备驱动程序实例
Solaris 内核允许一个驱动程序具有多个实例。每个实例都有自己的数据空间,但与其他实
例共享文本和某些全局数据。设备是基于每个实例进行管理的。除非将驱动程序设计用于
在内部处理任何故障转移,否则驱动程序应针对每个硬件使用单独的实例。一个插槽可能
具有一个驱动程序的多个实例,例如,多功能卡。
独占使用DDI 访问句柄
驱动程序进行的所有PIO 访问都必须使用以下系列例程中的Solaris DDI 访问函数:
ddi_getX
ddi_putX
ddi_rep_getX
ddi_rep_putX
驱动程序不应根据ddi_regs_map_setup(9F) 返回的地址直接访问已映射的寄存器。请避免使
用ddi_peek(9F) 和ddi_poke(9F) 例程,因为这些例程不使用访问句柄。
由于DDI 访问提供了对数据读入内核的方式进行控制的机会,因此DDI 访问机制很重要。
检测已损坏的数据
以下各节介绍可能发生数据损坏的位置,重点介绍如何检测损坏。
设备管理和控制数据的损坏
驱动程序应假设由PIO 或DMA次要设备获取的数据可能已损坏。需要特别指出的是,对于
基于设备数据的指针、内存偏移以及数组索引要格外小心。此类值可以是恶性的,因为取
消引用这些值时会导致内核发生混乱。在使用之前,应针对所有此类值执行范围和对齐检
查(如果需要)。
即使是非恶性指针,仍然可能具有误导性。例如,此类指针可能指向某个对象有效但错误
的实例。驱动程序应尽量交叉检查指针以及该指针所指向的对象,或者对通过该指针获得
的数据进行验证。
其他类型的数据也可能具有误导性,如包长度、状态字或通道ID。应尽可能地对这些数据
类型进行检查。可对包长度进行范围检查,以确保该长度既不为负,也不比包含缓冲区
大。可针对“不可能”的位对状态字进行检查。可将通道ID 与有效ID 的列表进行匹配。
其中,一个值标识一个流,驱动程序必须确保该流仍然存在。处理STREAMS 的异步性质意
味着可在设备中断仍未完成时中断流。
驱动程序不应次要设备中重新读取数据。数据应只读取一次,然后进行验证并以驱动程序
的本地状态进行存储。此方法可避免数据在初始读取时正确但以后重新读取时错误的风
险。
防御性编程
第22 章• 推荐的编码方法537
驱动程序还应确保已限制所有循环。例如,返回连续BUSY 状态的设备不能锁定整个系统。
已接收数据的损坏
设备错误可能导致将损坏的数据放置在接收缓冲区中。此类损坏与设备域之外(例如,网
络中)发生的损坏几乎没有区别。通常可利用现有软件处理此类问题。例如,在协议栈的
传输层进行完整性检查,或者在使用该设备的应用程序内进行完整性检查。
如果不打算在较高层对已接收的数据进行完整性检查,则可在驱动程序自身内对数据进行
完整性检查。对已接收数据中的损坏进行检测的方法通常特定于设备,即校验和、CRC
等。
DMA隔离
有缺陷的设备可能通过总线启动错误的DMA传输。此类数据传输可能会损坏以前传送的正
常数据。发生故障的设备可能会生成损坏的地址,该地址可能会污染甚至不属于自己的驱
动器的内存。
在具有IOMMU 的系统中,设备只能写入映射为对于DMA可写入的页。因此,此类页只应
归一个驱动程序实例所有。不应将这些页与其他任何内核结构共享。尽管该页被映射为对
于DMA可写入的页,但驱动程序应怀疑该页中的数据。在将页传递到驱动程序之外以及对
数据进行验证之前,必须从IOMMU 取消映射页。
可以使用ddi_umem_alloc(9F) 来保证已分配整个对齐的页,或分配多页并忽略第一个页边
界之下的内存。可使用ddi_ptob(9F) 确定IOMMU 页的大小。
或者,驱动程序可以选择在处理数据之前将其复制到内存中较安全的部分。如果已执行此
操作,则必须先使用ddi_dma_sync(9F) 同步数据。
对ddi_dma_sync(9F) 的调用应在使用DMA向设备传送数据之前指定SYNC_FOR_DEV,并在使
用DMA次要设备向内存传送数据之后指定SYNC_FOR_CPU。
在某些基于PCI 且具有IOMMU 的系统中,设备可以使用PCI 双地址循环(64 位地址)绕
过IOMMU。此功能使设备可能损坏主内存的任何区域。设备驱动程序不得尝试使用此类模
式,并应禁用此类模式。
处理有问题的中断
驱动程序必须标识有问题的中断,因为不断声明中断会严重影响系统性能,几乎一定会使
单处理器计算机产生延迟。
有时,驱动程序可能很难将特定中断标识为无效。对于网络驱动程序,如果指示接收中
断,但没有新缓冲区可用,则无需执行任何操作。当此情况为孤立事件时,这并不是一个
问题,因为另一个例程(如读取服务)可能已完成实际工作。
另一方面,出现连续中断但不需要驱动程序处理任何工作,这表示一个有问题的中断行。
因此,所有平台都允许在执行防御性操作之前发生许多明显无效的中断。
防御性编程
538 编写设备驱动程序• 2006 年11 月
当显示有工作需要处理时,挂起的设备可能无法更新其缓冲区描述符。驱动程序应阻止此
类重复请求。
在某些情况下,平台特定总线驱动程序可能能够识别一直未请求的中断,并且可以禁用违
例设备。但是,这依赖于驱动程序识别有效中断并返回相应值的能力。因此,除非驱动程
序检测到设备合法声明中断,即实际上设备要求驱动程序执行一些有用的工作,否则该驱
动程序应返回一个DDI_INTR_UNCLAIMED 结果。
其他更偶然的中断的合法性更难认证。预期中断标志是评估中断是否有效的有用工具。请
考虑一个中断,如描述符释放,如果先前已分配设备的所有描述符,则可生成该中断。如
果驱动程序检测到它已从卡中获取最后一个描述符,便可以设置一个预期中断标志。如果
传送关联的中断时未设置此标志,则该中断为可疑中断。
有些提示性中断可能无法预测,例如指示介质已断开连接或帧同步已丢失的中断。检测此
类中断是否有问题的最简单方法是:第一次出现中断时屏蔽此特定源,直到下一个轮询周
期。
如果在禁用期间再次发生中断,则该中断无效。有些设备具有即使掩码寄存器已禁用关联
源并且不可能引起中断时仍可读取的中断状态位。驱动程序设计者可以设计更适合的、特
定于设备的算法。
应避免对中断状态位进行无限循环。如果传送开始时设置的状态位都不要求任何实际工
作,请中断此类循环。
其他编程注意事项
除前面各节讨论的要求外,驱动程序开发者还必须考虑其他一些问题,如:
线程交互
自上而下请求的威胁
自适应策略
线程交互
设备驱动程序中内核发生混乱通常是由设备发生故障后内核线程的意外交互引起的。设备
出现故障时,线程可以设计者预期之外的方法进行交互。
如果处理例程较早终止,则条件变量等待程序将由于从未给定预期信号而被阻塞。尝试向
其他模块通知故障或处理意外回调会导致不需要的线程交互。请考虑设备发生故障期间获
取和释放互斥锁的顺序。
如果源自上游STREAMS 模块的线程意外返回该模块,则这些线程可能得出自相矛盾的结
果。可以使用备用线程来处理异常消息。例如,wput 过程使用读端服务例程与M_ERROR 进
行通信,而不直接使用读端putnext 处理错误。
在关闭期间由于故障而无法处于静态的发生故障的STREAMS 设备会在流终止后生成中断。
中断处理程序不得尝试使用过时的流指针来处理消息。
防御性编程
第22 章• 推荐的编码方法539
自上而下请求的威胁
针对有缺陷的硬件为系统提供保护的同时,驱动程序设计者还需要针对驱动程序误用提供
保护。尽管驱动程序可以假定内核基础结构始终正确(受信任的核心),但传递给它的用
户请求可能具有破坏性。
例如,用户可以请求对用户提供的数据块(M_IOCTL) 执行某一操作,该数据块小于消息的控
制部分所指示的块大小。驱动程序绝不应该信任用户应用程序。
设计应从可能引起的潜在危害的角度考虑驱动程序可以接收的每种类型的ioctl 的构造。驱
动程序应进行检查,以确保它不处理格式错误的ioctl。
自适应策略
驱动程序可以使用发生故障的硬件来继续提供服务,尝试使用访问设备的备选策略来解决
已确定的问题。假定损坏的硬件不可预测并且已知与其他设计复杂性关联的风险,则自适
应策略并不总是明智的选择。这些策略最多应限制为定期中断轮询和重试尝试。定期重试
设备可使驱动程序了解设备恢复的时间。强制驱动程序禁用中断后,定期轮询可以控制中
断机制。
理论上,系统始终有一个备用设备来提供重要的系统服务。内核或用户空间中的服务多路
复用程序可在设备出现时提供维护系统服务的最佳方法。此类做法将不在本章中进行介
绍。
将变量声明为可变变量
volatile 是声明任何引用设备寄存器的变量时必须应用的一个关键字。如果不使用
volatile,编译时优化程序可能会意外删除重要访问。省略使用volatile 可能会生成很难
跟踪的错误。
要防止出现难懂的错误,必须正确使用volatile。volatile 关键字指示编译器对已声明的
对象使用精确语义,特别指示不能删除或重新排序对对象的访问。设备驱动程序必须使用
volatile 限定符的两个实例为:
当数据引用外部硬件设备寄存器(即除了存储功能之外还具有负面影响的内存)时。但
请注意,如果使用DDI 数据访问函数访问设备寄存器,则无需使用volatile。
当数据引用的全局内存可由多个线程访问、不受锁定保护并且依赖于内存访问的序列
时。与使用锁定相比,使用volatile 使用的资源较少。
以下示例使用volatile。忙标志用于防止线程在设备忙时继续执行,该标志不受锁定保护
:
while (busy) {
/* do something else */
}
将变量声明为可变变量
540 编写设备驱动程序• 2006 年11 月
测试线程将在另一个线程关闭busy 标志时继续执行:
busy = 0;
由于busy 会在测试线程中被频繁地访问,因此编译器可能通过将busy 的值放在寄存器中来
优化测试,并测试寄存器的内容,而无需在每次测试前都读取内存中的busy 值。测试线程
将永远无法看到busy 的更改,其他线程将只更改内存中的busy 值,从而导致死锁。将busy
标志声明为volatile 会强制在每次测试前读取其值。
注– busy 标志的一种替代方法是使用条件变量。请参见第64 页中的“线程同步中的条件变
量”。
使用volatile 限定符时,请避免意外省略的风险。例如,以下代码
struct device_reg {
volatile uint8_t csr;
volatile uint8_t data;
};
struct device_reg *regp;
比下一个示例更可取:
struct device_reg {
uint8_t csr;
uint8_t data;
};
volatile struct device_reg *regp;
尽管这两个示例在功能上等效,但第二个示例要求编写人员确保类型struct device_reg 的
每个声明中都使用volatile。第一个示例将导致所有声明中都将数据视为可变数据,因此
首选该示例。如上所述,使用DDI 数据访问函数访问设备寄存器可使限定变量(如
volatile)不再必要。
将变量声明为可变变量
第22 章• 推荐的编码方法541
可维护性
为了确保可维护性,必须使驱动程序可以执行以下操作:
检测有故障的设备并报告故障
移除Solaris 热插拔模型支持的设备
添加Solaris 热插拔模型支持的新设备
执行定期的运行状况检查,以启用潜在故障检测
定期运行状况检查
潜在故障是在其他操作发生前不会出现的故障。例如,冷待机设备中出现的硬件故障在主
设备出现故障之前无法检测到。此时,系统包含两个有缺陷的设备,并且可能无法继续运
行。
允许保持无法检测状态的潜在故障通常最终会引起系统故障。如果不执行潜在故障检查,
冗余系统的整体可用性将受到危害。为避免出现这种情况,设备驱动程序必须检测潜在故
障,并以与报告其他故障的方法相同的方法来报告这些故障。
应为驱动程序提供对设备进行定期运行状况检查的机制。在容错情况(其中,设备可以是
辅助设备或故障转移设备)下,较早地检测到发生故障的辅助设备是确保在主设备出现故
障前可以修复或更换辅助设备所必需的。
定期运行状况检查可用来执行以下活动:
检查自上次轮询后值已更改的设备中任何寄存器或内存的位置。
通常表现确定行为的设备功能包括心跳信号、设备计时器(例如,下载使用的本地
lbolt)以及事件计数器。次要设备中读取更新的可预测值可提供一切事项的进行程度
令人满意的置信度。
时间标记外发请求,如传输块或驱动程序发出的命令。
定期运行状况检查可以查找尚未完成的任何可疑请求。
在设备上启动应在下一次预定检查前完成的操作。
如果此操作为中断操作,则此检查是确保硬件设备能够送出中断的理想方法。
可维护性
542 编写设备驱动程序• 2006 年11 月
附录
附录提供了以下背景材料:
附录A介绍了设备驱动程序的多平台硬件问题。
附录B 提供了设备驱动程序内核功能表。同时指出了过时的功能。
附录C 提供了更新设备驱动程序以在64 位环境中运行的指导原则。
第4 部分
543
544
硬件概述
本附录介绍有关可以支持Solaris OS 的硬件的一般问题。其中包括Solaris OS 支持的处理
器、总线体系结构以及内存模型。另外,还介绍了各种设备问题以及Sun 平台中使用的
PROM。
注– 本附录中的材料仅用于提供信息。此信息在调试驱动程序的过程中可能会有用。但是,
DDI/DKI 接口会对设备驱动程序隐藏其中的许多实现详细信息。
本附录提供有关以下主题的信息:
第545 页中的“SPARC 处理器问题”
第547 页中的“x86 处理器问题”
第547 页中的“字节存储顺序”
第548 页中的“存储缓冲区”
第549 页中的“系统内存模型”
第549 页中的“总线体系结构”
第550 页中的“总线特定信息”
第555 页中的“设备问题”
第556 页中的“SPARC 计算机上的PROM”
SPARC 处理器问题
本节介绍了许多特定于SPARC 处理器的主题,如数据对齐、字节排序、寄存器窗口以及浮
点指令的可用性。有关特定于x86 处理器主题的信息,请参见第547 页中的“x86 处理器问
题”。
注– 驱动程序决不能执行浮点操作,因为内核中不支持这些操作。
A附录A
545
SPARC 数据对齐
所有数量均必须使用标准C 数据类型与其自然边界对齐:
short 整数在16 位边界上对齐。
int 整数在32 位边界上对齐。
long 整数在32 位边界或64 位边界上对齐,具体取决于内核的数据模型是64 位还是32
位。有关数据模型的信息,请参见附录C。
long long 整数在64 位边界上对齐。
通常,编译器会处理所有对齐问题。但是,驱动程序编写者可能更关心对齐,因为只有使
用正确的数据类型才能访问设备。由于设备寄存器通常是通过指针引用来访问的,因此驱
动程序必须确保在访问设备时正确对齐指针。
SPARC 结构中的成员对齐
由于SPARC 处理器强加的数据对齐限制,因此,C 结构也具有对齐要求。结构对齐要求是
由最严格对齐的结构组件强加的。例如,仅包含字符的结构没有对齐限制,而对于包含
long long 成员的结构,则必须对其结构进行设置,保证此成员在64 位边界上对齐。
SPARC 字节排序
SPARC 处理器使用大端字节序进行字节排序。整数的最高有效字节(most significant byte,
MSB) 存储在该整数的最低地址上。最低有效字节存储在此处理器中字的最高地址上。例
如,字节63 是64 位处理器的最低有效字节。
SPARC 寄存器窗口
SPARC 处理器使用寄存器窗口。每个寄存器窗口包含八个输入寄存器、八个局部寄存器、
八个输出寄存器以及八个全局寄存器。输出寄存器是下一个窗口的输入寄存器。寄存器窗
口的数量范围从2 到32,具体取决于处理器实现。
由于驱动程序通常是使用C 语言编写的,因此编译器通常不会指明使用了寄存器窗口。但
是,当调试驱动程序时,可能必须使用寄存器窗口。
SPARC 处理器问题
546 编写设备驱动程序• 2006 年11 月
SPARC 乘法和除法指令
SPARC 版本7 处理器没有乘法或除法指令。乘法和除法指令是在软件中模拟实现的。由于
驱动程序可能在版本7、版本8 或者版本9 处理器中运行,因此请避免进行大量整数乘除。
相反,请使用按位向左和向右移位来以2 的幂进行相乘和相除。
《SPARCArchitecture Manual, Version 9》介绍了有关SPARC CPU 的更具体信息。《SPARC
Compliance Definition》(版本2.4)介绍了SPARC V9 的应用程序二进制接口(application
binary interface, ABI) 的详细信息。本手册介绍了32 位SPARC V8ABI 和64 位SPARC V9
ABI。可以从SPARC International 的网站 上获取本文档。
x86 处理器问题
数据类型没有对齐限制。但是,x86 处理器可能需要额外的存储周期来正确处理未对齐的数
据传送。
注– 驱动程序不应执行浮点操作,因为内核中不支持这些操作。
x86 字节排序
x86 处理器使用小端字节序进行字节排序。整数的最低有效字节(least significant byte, LSB) 存
储在该整数的最低地址上。最高有效字节存储在此处理器中数据项的最高地址上。例如,
字节7 是64 位处理器的最高有效字节。
x86 体系结构手册
Intel Corporation 和AMD都发布了大量有关x86 系列处理器的书籍。请参见
和。
字节存储顺序
为实现多平台、多指令集体系结构可移植性的目标,驱动程序中删除了主机总线的相关组
件。要解决的第一个相关性问题是处理器的字节存储顺序,即字节排序。例如,x86 处理器
系列采用小端字节序,而SPARC 体系结构则采用大端字节序。
总线体系结构显示了与处理器相同类型的字节存储顺序。例如,PCI 局部总线采用小端字节
序,S 总线采用大端字节序,ISA 总线采用小端字节序等。
字节存储顺序
附录A • 硬件概述547
要维持处理器和总线之间的可移植性,兼容DDI 的驱动程序必须不采用任何端字节序。虽
然驱动程序可以通过运行时检查或源代码中的预处理程序指令(如#ifdef
_LITTLE_ENDIAN)管理其字节存储顺序,但是长期维护可能会很麻烦。在某些情况下,DDI
框架会使用软件方法来执行字节交换。在另外一些情况下,可以像内存管理单元(memory
management unit, MMU) 中那样通过硬件页级交换来执行字节交换,也可通过特殊计算机指
令来执行字节交换。DDI 框架可以利用这些硬件功能来提高性能。
图A–1主机总线相关性所需的字节排序
除了不采用任何端字节排序之外,可移植驱动程序还必须独立于处理器的数据排序。在大
多数情况下,必须按照驱动程序指示的顺序进行数据传送。但是,有时可以通过合并、批
处理或者重新排列数据来简化数据传送,如下图中所示。例如,可以将数据合并应用于加
速图形卡缓存上的图形显示。驱动程序可以选择建议DDI 框架在数据传送过程中使用其他
最优传送机制。
图A–2数据排序主机总线相关性
存储缓冲区
为提高性能,CPU 会使用内部存储缓冲区临时存储数据。使用内部缓冲区可能会对设备I/O
操作的同步造成影响。因此,驱动程序需要执行明确的步骤来确保在适当的时间完成对寄
存器的写入。
例如,假设通过锁来同步对设备空间(如寄存器或图形卡缓存)的访问。驱动程序需要检
查在释放锁之前是否实际完成了向设备空间中的数据存储。释放锁时并不一定会刷新I/O 缓
冲区。
另一个示例是,确认中断时,驱动程序通常会在设备控制寄存器中设置或清除一位。驱动
程序必须确保在中断程序返回之前,已开始在设备上对控制寄存器进行写入。同样,在向
控制寄存器写入了某一命令之后,设备可能要求延迟,即驱动程序繁忙,需要等待。在这
种情况下,驱动程序必须确保在设备延迟之前已开始在该设备上进行写入。
存储缓冲区
548 编写设备驱动程序• 2006 年11 月
当读取设备寄存器不会产生不良负面影响时,只需在写入之后立即读取就可以对写入进行
验证了。如果无法在不产生不良负面影响的情况下读取该特定寄存器,则可以使用同一寄
存器集中的其他设备寄存器。
系统内存模型
系统内存模型用于定义内存操作(如装入和存储)的语义,并指定处理器执行这些操作的
顺序与操作到达内存的顺序之间的关系。内存模型可同时适用于单处理器和共享内存多处
理器。支持两种内存模型:全存储序顺序(total store ordering, TSO) 和部分存储排序(partial
store ordering, PSO)。
全存储序顺序(Total Store Ordering, TSO)
TSO 可保证存储、FLUSH 以及原子装入存储指令出现在给定处理器的内存中的顺序与该处
理器发出这些指令的顺序相同。
x86 和SPARC 处理器均支持TSO。
部分存储排序(Partial Store Ordering, PSO)
PSO 无法保证存储、FLUSH 以及原子装入存储指令出现在给定处理器的内存中的顺序与该
处理器发出这些指令的顺序相同。处理器可以对存储的指令重新排序,以使内存的存储指
令顺序与CPU 发出的存储指令顺序不同。
SPARC 处理器支持PSO;x86 处理器则不支持。
对于SPARC 处理器,指令的发出顺序和内存存储之间的一致性是由系统框架使用STBAR 指
令实现的。如果以上指令中的两条指令按处理器的发出顺序由STBAR 指令分隔,或者指令
引用同一位置,则这两条指令的内存存储顺序与发出顺序相同。使用
ddi_regs_map_setup(9F) 接口可强制执行兼容DDI 的驱动程序中的强数据排序。兼容的驱动
程序不能直接使用STBAR 指令。
有关SPARC 内存模型的更多详细信息,请参见《SPARCArchitecture Manual, Version 9》。
总线体系结构
本节介绍设备标识、设备寻址和中断。
设备标识
设备标识是确定系统中存在哪些设备的过程。某些设备是自标识设备,意味着设备本身向
系统提供信息,以便系统可以标识需要使用的设备驱动程序。S 总线和PCI 局部总线设备是
总线体系结构
附录A • 硬件概述549
自标识设备的示例。在S 总线上,信息通常是次要设备上FCode PROM 中存储的小Forth 程
序派生而来。大多数PCI 设备都会提供包含设备配置信息的配置空间。有关更多信息,请
参见sbus(4) 和pci(4) 手册页。
所有现代总线体系结构都要求设备进行自标识。
支持的中断类型
Solaris 平台支持轮询中断和向量化中断。对于这两种中断类型,Solaris DDI/DKI 中断模型
均相同。有关中断处理的更多信息,请参见第8 章。
总线特定信息
本节介绍特定于Solaris 平台支持的总线的寻址问题和设备配置问题。
PCI 局部总线
PCI 局部总线是旨在实现高速数据传送的高性能总线。PCI 总线驻留在系统板上。此总线通
常用作高度集成的外围组件、外围附件板以及主机处理器或内存系统之间的互连机制。主
机处理器、主内存以及PCI 总线本身都通过PCI 主桥(host bridge) 连接,如图A–3 中所示。
互连的I/O 总线树结构通过一系列PCI 总线网桥进行支持。可以在PCI 主桥(host bridge) 下
扩展从属PCI 总线网桥,以使单总线系统扩展为带有多条辅助总线的复杂系统。PCI 设备可
以连接到其中的一条或多条辅助总线。此外,还可以连接其他总线网桥,如SCSI 或USB。
每个PCI 设备都具有唯一的供应商ID 和设备ID。相同种类的多台设备会通过其驻留的总线
上的唯一设备号进一步标识。
总线特定信息
550 编写设备驱动程序• 2006 年11 月
图A–3计算机结构图
PCI 主桥(host bridge) 用于提供处理器和外围组件之间的互连。通过PCI 主桥(host bridge),
处理器可以直接访问独立于其他PCI 总线主控器的主内存。例如,当CPU 正在从主桥(host
bridge) 中的高速缓存控制器中提取数据时,其他PCI 设备也可以通过该主桥(host bridge) 访
问系统内存。这种体系结构的优点在于其分隔了I/O 总线与处理器的主机总线。
PCI 主桥(host bridge) 还可提供CPU 和外围I/O 设备之间的数据访问映射。该主桥(host
bridge) 会将每个外围设备映射到主机地址域,以便处理器可以通过程序I/O 访问此设备。
在局部总线端,PCI 主桥(host bridge) 会将系统内存映射到PCI 地址域,以便PCI 设备可以
访问作为总线主控器的主机内存。图A–3 显示了两种地址域。
PCI 地址域
PCI 地址域包含三种不同的地址空间:配置、内存以及I/O 空间。
PCI 配置地址空间
配置空间按地理位置定义。外围设备的位置通过它在互连的PCI 总线网桥树中的物理位置
确定。设备按其总线编号和设备(插槽)编号进行定位。每个外围设备在其PCI 配置空间
中都包含一组明确定义的配置寄存器。这些寄存器不仅用于标识设备,还用于为配置框架
提供设备配置信息。例如,必须首先映射设备配置空间中的基址寄存器,然后设备才能响
应数据访问。
生成配置周期的方法取决于主机。x86 计算机中使用的是特殊的I/O 端口。在其他平台上,
可以将PCI 配置空间内存映射到对应于主机地址域中PCI 主桥(host bridge) 的某些地址位
置。处理器访问设备配置寄存器时,会将请求路由到PCI 主桥(host bridge)。然后,该桥会
在总线上将访问转换为正确的配置周期。
总线特定信息
附录A • 硬件概述551
PCI 配置基址寄存器
PCI 配置空间针对每台设备最多包含六个32 位基址寄存器。这些寄存器可同时提供大小和
数据类型信息。系统固件会将PCI 地址域中的基本地址分配给这些寄存器。
每个可寻址区域既可以是内存空间,也可以是I/O 空间。基址寄存器的位0 包含的值用于标
识类型。位0 中的值为0 表示内存空间,值为1 表示I/O 空间。下图显示了两种基址寄存器
:一种表示内存类型,另一种表示I/O 类型。
图A–4 内存和I/O 的基址寄存器
PCI 内存地址空间
PCI 同时支持32 位和64 位的内存空间地址。系统固件会将PCI 地址域中的内存空间区域分
配给PCI 外围设备。区域的基本地址存储在设备的PCI 配置空间的基址寄存器中。每个区
域的大小必须是2 的幂,并且所分配的基本地址必须在与区域大小相等的边界上对齐。内
存空间中的设备地址会内存映射到主机地址域中,以便处理器的本机装入指令或存储指令
可以对任何设备执行数据访问。
PCI I/O 地址空间
PCI 支持32 位I/O 空间。可以在不同的平台上以不同方式访问I/O 空间。带有特殊I/O 指令
的处理器(如Intel 处理器系列)使用in 和out 指令访问I/O 空间。没有特殊I/O 指令的计
算机将映射到对应于主机地址域中PCI 主桥(host bridge) 的地址位置。处理器访问内存映射
的地址时,会向PCI 主桥(host bridge) 发送一个I/O 请求,该主桥(host bridge) 随后会将地址
转换为I/O 周期并将其放置在PCI 总线上。内存映射的I/O 通过处理器的本机装入/存储指
令执行。
PCI 硬件配置文件
对于PCI 局部总线设备,硬件配置文件应是不必要的。但是在某些情况下,PCI 设备的驱动
程序需要使用硬件配置文件来增加驱动程序的专用信息。有关更多详细信息,请参见
driver.conf(4) 和pci(4) 手册页。
PCI Express
标准PCI 总线已发展为PCI Express。PCI Express 是下一代高性能I/O 总线,用于连接桌面、
移动设备、工作站、服务器以及嵌入式计算和通信平台之类的应用程序中的外围设备。
总线特定信息
552 编写设备驱动程序• 2006 年11 月
PCI Express 可提高总线性能,减少整体系统支出,并可利用计算机设计中新的发展成果。
PCI Express 使用串行的点对点类型互连在两台设备之间实现通信。通过交换机,可以在某
个系统中将大量设备连接在一起。串行互连意味着每台设备软件包的管脚更少,这可降低
成本并使性能具有高度可伸缩性。
PCI Express 总线具有内置功能,可以适应以下技术:
QoS(Quality of Service,服务质量)
热插拔和热交换
高级电源管理
RAS(Reliability,Available, Serviceable,可靠性、可用性、可维护性)
改进的错误处理
MSI 中断
将两台设备连接在一起的PCI Express 互连称为链路。链路可以是x1、x2、x4、x8、x12、
x16 或x32 双向的信号对。这些信号称为道。双工模式中每条道的带宽(x1) 为500 MB/秒。
虽然PCI-X 和PCI Express 具有不同的硬件连接,但是对驱动程序编写者来说,两种总线是
相同的。PCI-X 是共享总线。例如,总线上的所有设备都共享单独的一组数据线和信号线。
PCI-Express 是交换总线,通过它可以更有效地使用设备和系统总线之间的带宽。
有关PCI Express 的更多信息,请参阅以下Web 站点:
S 总线
典型的S 总线系统由主板(包含CPU 和S 总线接口逻辑)、主板本身上的大量S 总线设备
以及大量S 总线扩展插槽组成。另外,还可以通过相应的总线网桥将S 总线连接到其他类型
的总线。
S 总线按地理位置进行寻址。每个S 总线插槽位于系统中固定的物理地址上。S 总线卡具有
不同的地址,具体取决于其插入的插槽。将S 总线设备移动到新插槽会导致系统将此设备
视为新设备。
S 总线使用轮询中断。S 总线设备中断时,系统仅知道若干设备中的哪些设备可能发出该中
断。系统中断处理程序必须询问每台设备的驱动程序此设备是否负责中断。
S 总线物理地址空间
下表显示了Sun UltraSPARC 2 计算机的物理地址空间布局。UltraSPARC 2 模型上的物理地址
包含41 位。该41 位的物理地址空间会进一步分为多个通过PA(40:33) 标识的33 位地址空
间。
表A–1 Ultra 2 中的设备物理空间
PA(40:33) 33 位空间使用情况
0x0 0x000000000 - 0x07FFFFFFF 2GB主内存
总线特定信息
附录A • 硬件概述553
表A–1 Ultra 2 中的设备物理空间(续)
PA(40:33) 33 位空间使用情况
0x80 – 0xDF Reserved on Ultra 2 在Ultra 2 上保留
0xE0 Processor 0 处理器0
0xE1 Processor 1 处理器1
0xE2 – 0xFD Reserved on Ultra 2 在Ultra 2 上保留
0xFE 0x000000000 - 0x1FFFFFFFF 从属UPA(FFB)
0xFF 0x000000000 - 0x0FFFFFFFF 系统I/O 空间
0x100000000 - 0x10FFFFFFF S 总线插槽0
0x110000000 - 0x11FFFFFFF S 总线插槽1
0x120000000 - 0x12FFFFFFF S 总线插槽2
0x130000000 - 0x13FFFFFFF S 总线插槽3
0x1D0000000 - 0x1DFFFFFFF S 总线插槽D
0x1E0000000 - 0x1EFFFFFFF S 总线插槽E
0x1F0000000 - 0x1FFFFFFFF S 总线插槽F
物理S 总线地址
S 总线具有32 个地址位,如SBus Specification中所述。下表介绍Ultra 2 如何使用地址位。
表A–2 Ultra 2 S 总线地址位
位说明
0 - 27 这些位是S 总线卡用于寻址该卡的内容的S 总线地址行。
28 - 31 供CPU 用于选择其中一个S 总线插槽。这些位会生成SlaveSelect 行。
此寻址方案将生成表A–1 中显示的Ultra 2 地址。其他实现可能会使用不同数量的地址位。
Ultra 2 具有七个S 总线插槽,其中四个是物理插槽。插槽0 到3 可供S 总线卡使用。插槽
4-12 为保留插槽。插槽的使用情况如下:
插槽0 到3 是具有DMA主控制器功能的物理插槽。
插槽D、E 以及F 并非实际物理插槽,而是指板载直接内存访问(direct memory access,
DMA) 控制器、SCSI 控制器、以太网控制器以及音频控制器。为方便起见,会将这些设
备类视为插入了插槽D、E 以及F。
总线特定信息
554 编写设备驱动程序• 2006 年11 月
注– 某些S 总线插槽是仅从属插槽。需要DMA功能的驱动程序应使用ddi_slaveonly(9F)
来确定其设备是否位于具有DMA功能的插槽中。有关此函数的示例,请参见第101 页
中的“attach() 入口点”。
S 总线硬件配置文件
通常,S 总线设备不需要硬件配置文件。但是在某些情况下,S 总线设备的驱动程序需要使
用硬件配置文件来增加S 总线卡所提供的信息。有关更多详细信息,请参见driver.conf(4)
和sbus(4) 手册页。
设备问题
本节介绍特殊设备的问题。
时间关键型部分
虽然在有了锁定原语所提供的同步和保护机制的情况下可以执行大多数驱动程序操作,但
是对于某些设备而言,必须在没有中断的情况下按顺序发生一系列事件。函数
ddi_enter_critical(9F) 与锁定原语一起将请求系统尽可能保证不会抢占或中断当前线程。
在进行对ddi_exit_critical(9F) 的关闭调用之前,此保证将一直有效。有关详细信息,请
参见ddi_enter_critical(9F) 手册页。
延迟
许多芯片指定只能在指定间隔对其进行访问。例如,Zilog Z8530 SCC 具有1.6 微秒的“写入
恢复时间”。此规范意味着通过8530 写入字符时,必须使用drv_usecwait(9F) 强制延迟。在
某些情况下,规范不会明确指示所需的延迟,因此必须根据经验来确定延迟。
请注意不要组合可能大量存在的设备部件的延迟,例如数以千计的SCSI 磁盘驱动器。
内部顺序逻辑
具有内部顺序逻辑的设备会将多个内部寄存器映射到同一外部地址。各种内部顺序逻辑包
括以下类型:
Intel 8251A和Signetics 2651 可在两个内部模式寄存器之间替换同一外部寄存器。通过向
外部寄存器进行写入可实现对第一个内部寄存器的写入。但是,这种写入操作有不利的
一面,即要在芯片中设置顺序逻辑,以使下一个读/写操作是在另一个内部寄存器上进
行。
设备问题
附录A • 硬件概述555
NEC PD7201 PCC 具有多个内部数据寄存器。要将字节写入特定寄存器,必须执行两个
步骤。第一步是将以下数据字节将进入的寄存器的编号写入寄存器零。然后,将数据写
入指定的数据寄存器。顺序逻辑会自动设置芯片,以便发送的下一字节进入数据寄存器
零。
AMD9513 计时器具有一个数据指针寄存器,指向数据字节将进入的数据寄存器。向数
据寄存器发送字节时,该指针会递增。无法读取指针寄存器的当前值。
中断问题
请注意以下常见的与中断相关的问题:
控制器中断不一定会指示控制器及其从属设备之一均已就绪。对于某些控制器,中断可
指示控制器或其设备之一已就绪,但不是均已就绪。
并非所有设备都会在禁用中断时打开电源,也不是所有设备都可随时开始中断。
某些设备不提供用于确定板是否已经生成中断的方法。
并非所有中断的板都会在被告知关闭中断时或在总线重置之后关闭中断。
SPARC 计算机上的PROM
某些平台具有PROM 监视器,支持在没有操作系统的情况下调试设备。本节介绍如何使用
SPARC 计算机上的PROM 来映射设备寄存器,以便可对其进行访问。通常,可以使用
PROM 命令对设备进行充分测试,以确定设备是否正常工作。
有关x86 引导子系统的说明,请参见boot(1M) 手册页。
PROM 具有多种用途,包括:
通过打开电源或硬重置PROM reset 命令初启计算机
提供交互工具来检查和设置内存、设备寄存器以及内存映射
引导Solaris 系统。
仅打开计算机电源并尝试使用其PROM 来检查设备寄存器会失败。虽然可能正确安装了
设备,但是这些映射特定于Solaris 操作系统,并且直到引导Solaris 内核之后才会变为活
动状态。打开电源之后,PROM 仅映射基本系统设备,如键盘。
使用sync 命令执行系统崩溃转储
Open BootPROM3
有关Open Boot PROM 的完整文档,请参见《Open Boot PROM Toolkit User’s Guide》和
monitor(1M) 手册页。本节中的示例引用的是Sun4UTM 体系结构。其他体系结构可能要求不
同的命令来执行操作。
SPARC 计算机上的PROM
556 编写设备驱动程序• 2006 年11 月
注– Open BootPROM当前在具有S 总线或UPA/PCI 的Sun 计算机上使用。Open BootPROM
使用"ok" 提示符。在早期计算机上,可能必须键入‘n’ 才能获取"ok" 提示符。
如果PROM 处于安全模式(security-mode 参数并未设置为无),则可能需要PROM 口令
(在security-password 参数中设置)。
printenv 命令用于显示所有参数及其值。
使用help 命令可以获取帮助信息。
可以使用EMACS 样式的命令行历史记录。使用Ctrl-N (下一步)和Ctrl-P (上一步)可以
遍历历史记录列表。
Forth 命令
Open Boot PROM 使用Forth 编程语言。Forth 是一种基于栈的语言。必须将参数推送到栈
上,然后再运行正确的命令(称为字),并且结果将留在栈上。
要对栈进行编号,请键入其值。
ok 57
ok 68
要在栈上添加两个顶部值,请使用+ 运算符。
ok +
结果会保留在栈上。栈使用字.s 来进行显示。
ok .s
bf
缺省基值为十六进制。可以使用字hex 和decimal 来切换基值。
ok decimal
ok .s
191
有关更多信息,请参见《Forth User’s Guide》。
SPARC 计算机上的PROM
附录A • 硬件概述557
遍历PROM设备树
pwd、cd 和ls 命令将遍历PROM 设备树以查找设备。必须首先使用cd 命令在树中建立一个
位置,然后pwd 才能运行。本示例为在S 总线上带有cgsix 图形卡缓存的Ultra 1 工作站。
ok cd /
要在树中查看连接到当前节点的设备,请使用ls 命令。
ok ls
f006a064 SUNW,UltraSPARC@0,0
f00598b0
f00592dc
f004eec8 virtual-memory
f004e8e8
f002ca28 aliases
f002c9b8 options
f002c880 openprom
f002c814 chosen
f002c7a4 packages
可以使用全节点名称:
ok cd
ok ls
f006a4e4
f0068194 SUNW,bpp@e,c800000
f0065370
f006120c
f005a448 SUNW,pll@f,1304000
f005a394
SPARC 计算机上的PROM
558 编写设备驱动程序• 2006 年11 月
f005a24c
f005a174
f005a0c0
f0059f8c SUNW,fdtwo@f,1400000
f0059ec4
f0059e34
f0059d28 SUNW,CS4231@d,c000000
如果不使用前一个示例中的全节点名称,则还可以使用缩写。缩写命令行项类似于以下示
例:
ok cd sbus
对于S 总线设备,名称实际为
device@slot,offset。cgsix 设备位于插槽2 中,并在偏移0
处开始。如果该树中显示了S 总线设备,则表明PROM 已经识别了此设备。
.properties 命令用于显示设备的PROM 属性。通过检查这些属性可以确定设备导出的属
性。以后可以使用此信息来确保驱动程序查找的是正确的硬件属性。这些属性与可以使用
ddi_getprop(9F) 检索的属性相同。
ok cd cgsix
ok .properties
character-set ISO8859-1
intr 00000005 00000000
interrupts 00000005
reg 00000002 00000000 01000000
dblbuf 00 00 00 00
vmsize 00 00 00 01
...
reg 属性用于定义包含以下字段的寄存器说明结构的数组:
uint_t bustype; /* cookie for related bus type*/
uint_t addr; /* address of reg relative to bus */
SPARC 计算机上的PROM
附录A • 硬件概述559
uint_t size; /* size of this register set */
对于cgsix 示例,地址为0。
映射设备
必须将设备映射到内存中才能进行测试。然后,可以使用PROM 来验证设备是否正确操
作,方法是使用数据传送命令来传送字节、字以及长字。如果可以通过PROM 操作设备
(即使使用受限的方法),则驱动程序也应该可以操作设备。
要设置设备以进行初始测试,请执行以下步骤:
1. 确定设备所在的S 总线插槽编号。
在本示例中,cgsix 设备位于插槽2 中。
2. 确定设备使用的物理地址空间中的偏移。
所使用的偏移特定于设备。在cgsix 示例中,视频内存恰好在偏移0x800000 开始。
3. 使用字select-dev 可选择S 总线设备以及在其中映射此设备的字map-in。
字select-dev 采用设备路径的字符串作为其参数。字map-in 采用偏移、插槽编号以及
大小作为映射的参数。与偏移一样,字节传送的大小也特定于设备。在cgsix 示例中,
大小设置为0x100000 字节。
在以下代码示例中,S 总线路径显示为字select-dev 的参数,图形卡缓存的偏移、插槽
编号以及大小值显示为字map-in 的参数。请注意select-dev 参数中起始引号和/ 之间的
空格。要使用的虚拟地址保留在栈的顶部。栈通过使用字.s 进行显示。通过constant
操作可为该栈分配一个名称。
ok " " select-dev
ok 800000 2 100000 map-in
ok .s
ffe98000
ok constant fb
读取和写入
PROM 提供了许多8 位、16 位以及32 位操作。通常,c(字符)前缀表示8 位(一字节)操
作;w(字)前缀表示16 位(二字节)操作;L(长字)前缀表示32 位(四字节)操作。
后缀! 表示写入操作。写入操作用于从栈中取出前两项。第一项是地址,第二项是值。
ok 55 ffe98000 c!
后缀@ 表示读取操作。读取操作用于从栈中取出地址。
SPARC 计算机上的PROM
560 编写设备驱动程序• 2006 年11 月
ok ffe98000 c@
ok .s
55
后缀? 用于显示值,并且不会影响栈。
ok ffe98000 c?
55
尝试查询设备时,请务必谨慎。如果未正确设置映射,则尝试读取或写入可能会导致错
误。为处理这些情况,提供了特殊字。例如cprobe、wprobe 以及lprobe 会从给定地址进行
读取,但是如果此位置不响应,则会返回零;如果此位置响应,则返回非零值。
ok fffa4000 c@
Data Access Error
ok fffa4000 cprobe
ok .s0
ok ffe98000 cprobe
ok .s
0 ffffffffffffffff
使用字dump 可以显示内存的区域。这会采用address 和length,并以字节为单位显示内存区
域的内容。
在以下示例中,字fill 用于使用某种模式填充视频内存。fill 会采用地址、要填充的字节
数以及要使用的字节。对于字和长字,请分别使用wfill 和Lfill。此填充示例会导致cgsix
基于传递的字节显示简单模式。
ok " /sbus" select-dev
ok 800000 2 100000 map-in
ok constant fb
ok fb 10000 ff fill
SPARC 计算机上的PROM
附录A • 硬件概述561
ok fb 20000 0 fill
ok fb 18000 55 fill
ok fb 15000 3 fill
ok fb 10000 5 fillok fb 5000 f9 fill
SPARC 计算机上的PROM
562 编写设备驱动程序• 2006 年11 月
Solaris DDI/DKI 服务汇总
本附录介绍了Solaris DDI/DKI 所提供的接口。这些说明并不具有完整性和确定性,也未提
供详细的使用指导,而是旨在使用一般术语介绍函数功能。有关更多详细信息,请参见
physio(9F)。介绍的类别如下:
第564 页中的“模块函数”
第564 页中的“设备信息树节点(dev_info_t) 函数”
第564 页中的“设备(dev_t) 函数”
第565 页中的“属性函数”
第566 页中的“设备软件状态函数”
第566 页中的“内存分配和取消分配函数”
第567 页中的“内核线程控制和同步函数”
第568 页中的“中断函数”
第570 页中的“程控I/O 函数”
第575 页中的“直接内存访问(Direct Memory Access, DMA) 函数”
第577 页中的“用户空间访问函数”
第578 页中的“用户进程事件函数”
第578 页中的“用户进程信息函数”
第578 页中的“用户应用程序内核和设备访问函数”
第579 页中的“与时间有关的函数”
第580 页中的“电源管理函数”
第581 页中的“内核统计信息函数”
第581 页中的“内核日志记录和列显函数”
第581 页中的“缓存I/O 函数”
第582 页中的“虚拟内存函数”
第583 页中的“设备ID 函数”
第583 页中的“SCSI 函数”
第585 页中的“资源映射管理函数”
第585 页中的“系统全局状态”
第586 页中的“实用程序函数”
本附录未介绍STREAMS 接口;要了解有关网络驱动程序的更多信息,请参见《STREAMS
Programming Guide》。
B附录B
563
模块函数
模块函数包括:
mod_info 查询可装入模块
mod_install 添加可装入模块
mod_remove 删除可装入模块
设备信息树节点(dev_info_t) 函数
设备信息树节点函数包括:
ddi_binding_name() 返回驱动程序绑定名称
ddi_dev_is_sid() 指示设备是否能自我识别
ddi_driver_major() 返回驱动程序主设备号
ddi_driver_name() 返回标准化驱动程序名称
ddi_node_name() 返回devinfo 节点名称
ddi_get_devstate() 检查设备状态
ddi_get_instance() 获取设备实例编号
ddi_get_name() 返回驱动程序绑定名称
ddi_get_parent() 查找设备信息结构的父节点
ddi_root_node() 获取dev_info 树的根
设备(dev_t) 函数
设备函数包括:
ddi_create_minor_node() 为设备创建次要节点
ddi_getiminor() 从外部dev_t 中获取内核内部次要设备号
ddi_remove_minor_node() 删除设备的次要节点
getmajor() 获取主设备号
getminor() 获取次要设备号
makedevice() 根据主设备号和次要设备号生成设备编号
模块函数
564 编写设备驱动程序• 2006 年11 月
属性函数
属性函数包括:
ddi_prop_exists() 检查属性是否存在
ddi_prop_free() 释放属性查找使用的资源
ddi_prop_get_int() 查找整数属性
ddi_prop_get_int64() 查找64 位整数属性
ddi_prop_lookup_byte_array() 查找字节数组属性
ddi_prop_lookup_int_array() 查找整数数组属性
ddi_prop_lookup_int64_array() 查找64 位整数数组属性
ddi_prop_lookup_string() 查找字符串属性
ddi_prop_lookup_string_array() 查找字符串数组属性
ddi_prop_remove() 删除设备的一个属性
ddi_prop_remove_all() 删除设备的所有属性
ddi_prop_undefine() 隐藏设备的一个属性
ddi_prop_update_byte_array() 创建或更新字节数组属性
ddi_prop_update_int() 创建或更新整数属性
ddi_prop_update_int64() 创建或更新64 位整数属性
ddi_prop_update_int_array() 创建或更新整数数组属性
ddi_prop_update_int64_array() 创建或更新64 位整数数组属性
ddi_prop_update_string() 创建或更新字符串属性
ddi_prop_update_string_array() 创建或更新字符串数组属性
表B–1过时的属性函数
过时的函数替代函数
ddi_getlongprop() 请参见ddi_prop_lookup
ddi_getlongprop_buf() ddi_prop_lookup()
ddi_getprop() ddi_prop_get_int()
ddi_getproplen() ddi_prop_lookup()
ddi_prop_create() ddi_prop_lookup()
ddi_prop_modify() ddi_prop_lookup()
属性函数
附录B • Solaris DDI/DKI 服务汇总565
表B–1 过时的属性函数(续)
过时的函数替代函数
ddi_prop_op() ddi_prop_lookup()
设备软件状态函数
设备软件状态函数包括:
ddi_get_driver_private() 获取设备的专用数据区的地址
ddi_get_soft_state() 获取指向实例软状态结构的指针
ddi_set_driver_private() 设置设备的专用数据区的地址
ddi_soft_state_fini() 销毁驱动程序软状态结构
ddi_soft_state_free() 释放实例软状态结构
ddi_soft_state_init() 初始化驱动程序软状态结构
ddi_soft_state_zalloc() 分配实例软状态结构
内存分配和取消分配函数
内存分配和取消分配函数包括:
kmem_alloc() 分配内核内存
kmem_free() 释放内核内存
kmem_zalloc() 分配零填充的内核内存
以下函数可以分配和释放用于DMA的内存。请参见第575 页中的“直接内存访问(Direct
Memory Access, DMA) 函数”。
ddi_dma_mem_alloc() 为DMA传送操作分配内存
ddi_dma_mem_free() 释放以前分配的DMA内存
以下函数可以分配和释放用于导出到用户空间的内存。请参见第577 页中的“用户空间访
问函数”。
ddi_umem_alloc() 分配按页对齐的内核内存
ddi_umem_free() 释放按页对齐的内核内存
设备软件状态函数
566 编写设备驱动程序• 2006 年11 月
表B–2过时的内存分配和取消分配函数
过时的函数替代函数
ddi_iopb_alloc() ddi_dma_mem_alloc()
ddi_iopb_free() ddi_dma_mem_free()
ddi_mem_alloc() ddi_dma_mem_alloc()
ddi_mem_free() ddi_dma_mem_free()
内核线程控制和同步函数
内核线程控制和同步函数包括:
cv_broadcast() 唤醒所有等待线程
cv_destroy() 释放已分配的条件变量
cv_init() 分配条件变量
cv_signal() 唤醒一个等待线程
cv_timedwait() 等待事件,具有超时设置
cv_timedwait_sig() 等待事件或信号,具有超时设置
cv_wait() 等待事件
cv_wait_sig() 等待事件或信号
ddi_can_receive_sig() 确定当前线程是否可以接收信号
ddi_enter_critical() 进入关键控制区
ddi_exit_critical() 退出关键控制区
mutex_destroy() 销毁互斥锁
mutex_enter() 获取互斥锁
mutex_exit() 释放互斥锁
mutex_init() 初始化互斥锁
mutex_owned() 确定当前线程是否持有互斥锁
mutex_tryenter() 尝试获取互斥锁,但不等待
rw_destroy() 销毁读取器/写入器锁
rw_downgrade() 将持有的读取器/写入器锁从写入器降级为读取器
rw_enter() 获取读取器/写入器锁
rw_exit() 释放读取器/写入器锁
内核线程控制和同步函数
附录B • Solaris DDI/DKI 服务汇总567
rw_init() 初始化读取器/写入器锁
rw_read_locked() 确定持有的读取器/写入器锁是用于读取还是用于写入
rw_tryenter() 尝试获取读取器/写入器锁,但不等待
rw_tryupgrade() 尝试将持有的读取器/写入器锁从读取器升级为写入器
sema_destroy() 销毁信号
sema_init() 初始化信号
sema_p() 递减信号并可能阻塞
sema_p_sig() 递减信号,但信号待处理时不阻塞
sema_tryp() 尝试递减信号,但不阻塞
sema_v() 递增信号并可能解除阻塞等待程序
中断函数
中断函数包括:
ddi_intr_add_handler(9F) 添加中断处理程序。
ddi_intr_add_softint(9F) 添加软中断处理程序。
ddi_intr_alloc(9F) 为指定类型的中断分配系统资源和中断向量。
ddi_intr_block_disable(9F) 禁用指定范围的中断。仅适用于MSI。
ddi_intr_block_enable(9F) 启用指定范围的中断。仅适用于MSI。
ddi_intr_clr_mask(9F) 如果值不为零,则递减中断掩码中的内部计数器。
ddi_intr_disable(9F) 禁用指定的中断。
ddi_intr_dup_handler(9F) 针对未分配的中断向量重用MSI-X 地址和数据对,
仅适用于MSI-X。
ddi_intr_enable(9F) 启用指定的中断。
ddi_intr_free(9F) 针对指定的中断句柄释放系统资源和中断向量。
ddi_intr_get_cap(9F) 针对指定的中断返回中断功能标志。
ddi_intr_get_hilevel_pri(9F) 返回高级别中断的最低优先级别。
ddi_intr_get_navail(9F) 返回可用于特定硬件设备和给定中断类型的中断
数。
ddi_intr_get_nintrs(9F) 针对给定的中断类型获取设备支持的中断数。
中断函数
568 编写设备驱动程序• 2006 年11 月
ddi_intr_get_pending(9F) 读取中断待处理位(如果主桥(host bridge) 或设备支
持)。
ddi_intr_get_pri(9F) 返回指定中断的当前软件优先级设置。
ddi_intr_get_softint_pri(9F) 返回指定中断的软中断优先级。
ddi_intr_get_supported_types(9F) 返回设备和主机都支持的硬件中断类型。
ddi_intr_remove_handler(9F) 删除指定的中断处理程序。
ddi_intr_remove_softint(9F) 删除指定的软中断处理程序。
ddi_intr_set_cap(9F) 为指定的中断设置DDI_INTR_FLAG_LEVEL 或
DDI_INTR_FLAG_EDGE 标志。
ddi_intr_set_mask(9F) 递增中断掩码中的内部计数器。
ddi_intr_set_pri(9F) 为指定的中断设置中断优先级。
ddi_intr_set_softint_pri(9F) 更改指定软中断的相对软中断优先级。
ddi_intr_trigger_softint(9F) 触发指定的软中断。
要利用新框架的功能,开发者需要使用上述接口,并避免使用以下接口(保留这些接口是
出于兼容性原因)。
表B–3传统中断函数
传统中断函数替代函数
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) 不适用
中断函数
附录B • Solaris DDI/DKI 服务汇总569
表B–3 传统中断函数(续)
传统中断函数替代函数
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)
程控I/O 函数
程控I/O 函数包括:
ddi_dev_nregs() 返回设备的寄存器集数
ddi_dev_regsize() 返回设备寄存器的大小
ddi_regs_map_setup() 为寄存器地址空间设置映射
ddi_regs_map_free() 释放以前映射的寄存器地址空间
ddi_device_copy() 在设备寄存器之间复制数据
ddi_device_zero() 零填充设备
ddi_check_acc_handle() 检查数据访问句柄
ddi_get8() 从映射的内存、设备寄存器或DMA内存中读取一个8 位数据
ddi_get16() 从映射的内存、设备寄存器或DMA内存中读取一个16 位数据
ddi_get32() 从映射的内存、设备寄存器或DMA内存中读取一个32 位数据
ddi_get64() 从映射的内存、设备寄存器或DMA内存中读取一个64 位数据
ddi_put8() 向映射的内存、设备寄存器或DMA内存中写入一个8 位数据
ddi_put16() 向映射的内存、设备寄存器或DMA内存中写入一个16 位数据
ddi_put32() 向映射的内存、设备寄存器或DMA内存中写入一个32 位数据
ddi_put64() 向映射的内存、设备寄存器或DMA内存中写入一个64 位数据
ddi_rep_get8() 从映射的内存、设备寄存器或DMA内存中读取多个8 位数据
ddi_rep_get16() 从映射的内存、设备寄存器或DMA内存中读取多个16 位数据
ddi_rep_get32() 从映射的内存、设备寄存器或DMA内存中读取多个32 位数据
ddi_rep_get64() 从映射的内存、设备寄存器或DMA内存中读取多个64 位数据
ddi_rep_put8() 向映射的内存、设备寄存器或DMA内存中写入多个8 位数据
ddi_rep_put16() 向映射的内存、设备寄存器或DMA内存中写入多个16 位数据
程控I/O 函数
570 编写设备驱动程序• 2006 年11 月
ddi_rep_put32() 向映射的内存、设备寄存器或DMA内存中写入多个32 位数据
ddi_rep_put64() 向映射的内存、设备寄存器或DMA内存中写入多个64 位数据
ddi_peek8() 从某一位置慎重读取一个8 位的值
ddi_peek16() 从某一位置慎重读取一个16 位的值
ddi_peek32() 从某一位置慎重读取一个32 位的值
ddi_peek64() 从某一位置慎重读取一个64 位的值
ddi_poke8() 向某一位置慎重写入一个8 位的值
ddi_poke16() 向某一位置慎重写入一个16 位的值
ddi_poke32() 向某一位置慎重写入一个32 位的值
ddi_poke64() 向某一位置慎重写入一个64 位的值
可以始终使用上面列出的一般程控I/O 函数,而不必使用下面的mem、io 和pci_config 函
数。但如果编译时已知访问类型,以下函数可作为备用函数。
ddi_io_get8() 从I/O 空间的映射设备寄存器中读取一个8 位数据
ddi_io_get16() 从I/O 空间的映射设备寄存器中读取一个16 位数据
ddi_io_get32() 从I/O 空间的映射设备寄存器中读取一个32 位数据
ddi_io_put8() 向I/O 空间的映射设备寄存器中写入一个8 位数据
ddi_io_put16() 向I/O 空间的映射设备寄存器中写入一个16 位数据
ddi_io_put32() 向I/O 空间的映射设备寄存器中写入一个32 位数据
ddi_io_rep_get8() 从I/O 空间的映射设备寄存器中读取多个8 位数据
ddi_io_rep_get16() 从I/O 空间的映射设备寄存器中读取多个16 位数据
ddi_io_rep_get32() 从I/O 空间的映射设备寄存器中读取多个32 位数据
ddi_io_rep_put8() 向I/O 空间的映射设备寄存器中写入多个8 位数据
ddi_io_rep_put16() 向I/O 空间的映射设备寄存器中写入多个16 位数据
ddi_io_rep_put32() 向I/O 空间的映射设备寄存器中写入多个32 位数据
ddi_mem_get8() 从内存空间的映射设备或DMA内存中读取一个8 位数据
ddi_mem_get16() 从内存空间的映射设备或DMA内存中读取一个16 位数据
ddi_mem_get32() 从内存空间的映射设备或DMA内存中读取一个32 位数据
ddi_mem_get64() 从内存空间的映射设备或DMA内存中读取一个64 位数据
ddi_mem_put8() 向内存空间的映射设备或DMA内存中写入一个8 位数据
ddi_mem_put16() 向内存空间的映射设备或DMA内存中写入一个16 位数据
程控I/O 函数
附录B • Solaris DDI/DKI 服务汇总571
ddi_mem_put32() 向内存空间的映射设备或DMA内存中写入一个32 位数据
ddi_mem_put64() 向内存空间的映射设备或DMA内存中写入一个64 位数据
ddi_mem_rep_get8() 从内存空间的映射设备或DMA内存中读取多个8 位数据
ddi_mem_rep_get16() 从内存空间的映射设备或DMA内存中读取多个16 位数据
ddi_mem_rep_get32() 从内存空间的映射设备或DMA内存中读取多个32 位数据
ddi_mem_rep_get64() 从内存空间的映射设备或DMA内存中读取多个64 位数据
ddi_mem_rep_put8() 向内存空间的映射设备或DMA内存中写入多个8 位数据
ddi_mem_rep_put16() 向内存空间的映射设备或DMA内存中写入多个16 位数据
ddi_mem_rep_put32() 向内存空间的映射设备或DMA内存中写入多个32 位数据
ddi_mem_rep_put64() 向内存空间的映射设备或DMA内存中写入多个64 位数据
pci_config_setup() 设置对PCI 本地总线配置空间的访问
pci_config_teardown() 销毁对PCI 本地总线配置空间的访问
pci_config_get8() 从PCI 本地总线配置空间中读取一个8 位数据
pci_config_get16() 从PCI 本地总线配置空间中读取一个16 位数据
pci_config_get32() 从PCI 本地总线配置空间中读取一个32 位数据
pci_config_get64() 从PCI 本地总线配置空间中读取一个64 位数据
pci_config_put8() 向PCI 本地总线配置空间中写入一个8 位数据
pci_config_put16() 向PCI 本地总线配置空间中写入一个16 位数据
pci_config_put32() 向PCI 本地总线配置空间中写入一个32 位数据
pci_config_put64() 向PCI 本地总线配置空间中写入一个64 位数据
表B–4 过时的程控I/O 函数
过时的函数替代函数
ddi_getb() ddi_get8()
ddi_getl() ddi_get32()
ddi_getll() ddi_get64()
ddi_getw() ddi_get16()
ddi_io_getb() ddi_io_get8()
ddi_io_getl() ddi_io_get32()
ddi_io_getw() ddi_io_get16()
程控I/O 函数
572 编写设备驱动程序• 2006 年11 月
表B–4 过时的程控I/O 函数(续)
过时的函数替代函数
ddi_io_putb() ddi_io_put8()
ddi_io_putl() ddi_io_put32()
ddi_io_putw() ddi_io_put16()
ddi_io_rep_getb() ddi_io_rep_get8()
ddi_io_rep_getl() ddi_io_rep_get32()
ddi_io_rep_getw() ddi_io_rep_get16()
ddi_io_rep_putb() ddi_io_rep_put8()
ddi_io_rep_putl() ddi_io_rep_put32()
ddi_io_rep_putw() ddi_io_rep_put16()
ddi_map_regs() ddi_regs_map_setup()
ddi_mem_getb() ddi_mem_get8()
ddi_mem_getl() ddi_mem_get32()
ddi_mem_getll() ddi_mem_get64()
ddi_mem_getw() ddi_mem_get16()
ddi_mem_putb() ddi_mem_put8()
ddi_mem_putl() ddi_mem_put32()
ddi_mem_putll() ddi_mem_put64()
ddi_mem_putw() ddi_mem_put16()
ddi_mem_rep_getb() ddi_mem_rep_get8()
ddi_mem_rep_getl() ddi_mem_rep_get32()
ddi_mem_rep_getll() ddi_mem_rep_get64()
ddi_mem_rep_getw() ddi_mem_rep_get16()
ddi_mem_rep_putb() ddi_mem_rep_put8()
ddi_mem_rep_putl() ddi_mem_rep_put32()
ddi_mem_rep_putll() ddi_mem_rep_put64()
ddi_mem_rep_putw() ddi_mem_rep_put16()
ddi_peekc() ddi_peek8()
ddi_peekd() ddi_peek64()
程控I/O 函数
附录B • Solaris DDI/DKI 服务汇总573
表B–4 过时的程控I/O 函数(续)
过时的函数替代函数
ddi_peekl() ddi_peek32()
ddi_peeks() ddi_peek16()
ddi_pokec() ddi_poke8()
ddi_poked() ddi_poke64()
ddi_pokel() ddi_poke32()
ddi_pokes() ddi_poke16()
ddi_putb() ddi_put8()
ddi_putl() ddi_put32()
ddi_putll() ddi_put64()
ddi_putw() ddi_put16()
ddi_rep_getb() ddi_rep_get8()
ddi_rep_getl() ddi_rep_get32()
ddi_rep_getll() ddi_rep_get64()
ddi_rep_getw() ddi_rep_get16()
ddi_rep_putb() ddi_rep_put8()
ddi_rep_putl() ddi_rep_put32()
ddi_rep_putll() ddi_rep_put64()
ddi_rep_putw() ddi_rep_put16()
ddi_unmap_regs() ddi_regs_map_free()
inb() ddi_io_get8()
inl() ddi_io_get32()
inw() ddi_io_get16()
outb() ddi_io_put8()
outl() ddi_io_put32()
outw() ddi_io_put16()
pci_config_getb() pci_config_get8()
pci_config_getl() pci_config_get32()
pci_config_getll() pci_config_get64()
程控I/O 函数
574 编写设备驱动程序• 2006 年11 月
表B–4 过时的程控I/O 函数(续)
过时的函数替代函数
pci_config_getw() pci_config_get16()
pci_config_putb() pci_config_put8()
pci_config_putl() pci_config_put32()
pci_config_putll() pci_config_put64()
pci_config_putw() pci_config_put16()
repinsb() ddi_io_rep_get8()
repinsd() ddi_io_rep_get32()
repinsw() ddi_io_rep_get16()
repoutsb() ddi_io_rep_put8()
repoutsd() ddi_io_rep_put32()
repoutsw() ddi_io_rep_put16()
直接内存访问(Direct MemoryAccess, DMA) 函数
DMA函数包括:
ddi_dma_alloc_handle() 分配DMA句柄
ddi_dma_free_handle() 释放DMA句柄
ddi_dma_mem_alloc() 为DMA传送操作分配内存
ddi_dma_mem_free() 释放以前分配的DMA内存
ddi_dma_addr_bind_handle() 将地址绑定到DMA句柄
ddi_dma_buf_bind_handle() 将系统缓冲区绑定到DMA句柄
ddi_dma_unbind_handle() 取消绑定DMA句柄中的地址
ddi_dma_nextcookie() 检索后续的DMAcookie
ddi_dma_getwin() 激活新DMA窗口
ddi_dma_numwin() 检索DMA窗口数
ddi_dma_sync() 同步CPU 和I/O 内存视图
ddi_check_dma_handle() 检查DMA句柄
ddi_dma_set_sbus64() 允许在S 总线上进行64 位传送
ddi_slaveonly() 报告设备是否安装在只允许从属访问的位置
直接内存访问(Direct MemoryAccess,DMA) 函数
附录B • Solaris DDI/DKI 服务汇总575
ddi_iomin() 查找DMA的最小对齐和传送大小
ddi_dma_burstsizes() 查找DMA映射的允许突发大小
ddi_dma_devalign() 查找DMA映射对齐和最小传送大小
ddi_dmae_alloc() 获取DMA通道
ddi_dmae_release() 释放DMA通道
ddi_dmae_getattr() 获取DMA引擎属性
ddi_dmae_prog() 对DMA通道编程
ddi_dmae_stop() 终止DMA引擎操作
ddi_dmae_disable() 禁用DMA通道
ddi_dmae_enable() 启用DMA通道
ddi_dmae_getcnt() 获取剩余的DMA引擎计数
ddi_dmae_1stparty() 配置DMA通道层叠模式
ddi_dma_coff() 将DMAcookie 转换为DMA句柄内的偏移
表B–5 过时的直接内存访问(Direct MemoryAccess,DMA)函数
过时的函数替代函数
ddi_dma_addr_setup() ddi_dma_alloc_handle()、
ddi_dma_addr_bind_handle()
ddi_dma_buf_setup() ddi_dma_alloc_handle()、ddi_dma_buf_bind_handle()
ddi_dma_curwin() ddi_dma_getwin()
ddi_dma_free() ddi_dma_free_handle()
ddi_dma_htoc() ddi_dma_addr_bind_handle()、
ddi_dma_buf_bind_handle()
ddi_dma_movwin() ddi_dma_getwin()
ddi_dma_nextseg() ddi_dma_nextcookie()
ddi_dma_segtocookie() ddi_dma_nextcookie()
ddi_dma_setup() ddi_dma_alloc_handle()、
ddi_dma_addr_bind_handle()、
ddi_dma_buf_bind_handle()
ddi_dmae_getlim() ddi_dmae_getattr()
ddi_iopb_alloc() ddi_dma_mem_alloc()
ddi_iopb_free() ddi_dma_mem_free()
直接内存访问(Direct MemoryAccess,DMA) 函数
576 编写设备驱动程序• 2006 年11 月
表B–5 过时的直接内存访问(Direct MemoryAccess,DMA)函数(续)
过时的函数替代函数
ddi_mem_alloc() ddi_dma_mem_alloc()
ddi_mem_free() ddi_dma_mem_free()
hat_getkpfnum() ddi_dma_addr_bind_handle()、
ddi_dma_buf_bind_handle()、ddi_dma_nextcookie()
用户空间访问函数
用户空间访问函数包括:
ddi_copyin() 将数据复制到驱动程序缓冲区
ddi_copyout() 从驱动程序中复制数据
uiomove() 使用uio 结构复制内核数据
ureadc() 向uio 结构中添加字符
uwritec() 从uio 结构中删除字符
ddi_getminor() 从外部dev_t 中获取内核内部次要设备号
ddi_model_convert_from() 确定数据模型类型是否不匹配
IOC_CONVERT_FROM() 确定是否需要转换M_IOCTL内容
STRUCT_DECL() 在栈上声明结构句柄并在该栈上分配其本机形式实例
STRUCT_HANDLE() 在栈上声明结构句柄但不在该栈上分配其本机形式实例
STRUCT_INIT() 声明并初始化指向已分配实例的句柄
STRUCT_SET_HANDLE() 声明并初始化指向本机形式结构实例的结构句柄
SIZEOF_PTR() 返回指定数据模型中指针的大小
SIZEOF_STRUCT() 返回指定数据模型中结构的大小
STRUCT_SIZE() 返回应用程序数据模型中结构的大小
STRUCT_BUF() 返回指向结构的本机模式实例的指针
STRUCT_FADDR() 返回指向结构的指定字段的指针
STRUCT_FGET() 返回应用程序数据模型中结构的指定字段
STRUCT_FGETP() 返回应用程序数据模型中结构的指定指针字段
STRUCT_FSET() 设置应用程序数据模型中结构的指定字段
STRUCT_FSETP() 设置应用程序数据模型中结构的指定指针字段
用户空间访问函数
附录B • Solaris DDI/DKI 服务汇总577
表B–6过时的用户空间访问函数
过时的函数替代函数
copyin() ddi_copyin()
copyout() ddi_copyout()
用户进程事件函数
用户进程事件函数包括:
pollwakeup() 通知进程事件已发生
proc_ref() 获取进程中指向信号的句柄
proc_unref() 释放进程中指向信号的句柄
proc_signal() 向进程发送信号
用户进程信息函数
用户进程信息函数包括:
ddi_get_cred() 返回指向呼叫者的凭证结构的指针
drv_priv() 确定进程凭证权限
ddi_get_pid() 返回进程ID
表B–7过时的用户进程信息函数
过时的函数替代函数
drv_getparm() ddi_get_pid()、ddi_get_cred()
用户应用程序内核和设备访问函数
用户应用程序内核和设备访问函数包括:
ddi_dev_nregs() 返回设备的寄存器集数
ddi_dev_regsize() 返回设备寄存器的大小
ddi_devmap_segmap()、devmap_setup() 使用devmap 框架设置用户与设备内存之间的映
射
devmap_devmem_setup() 将设备内存导出到用户空间
devmap_load() 验证内存地址转换
用户进程事件函数
578 编写设备驱动程序• 2006 年11 月
devmap_unload() 使内存地址转换无效
devmap_do_ctxmgt() 对映射执行设备上下文切换
devmap_set_ctx_timeout() 为上下文管理回叫设置超时值
devmap_default_access() 缺省驱动程序内存访问函数
ddi_umem_alloc() 分配按页对齐的内核内存
ddi_umem_free() 释放按页对齐的内核内存
ddi_umem_lock() 锁定内存页
ddi_umem_unlock() 解除锁定内存页
ddi_umem_iosetup() 设置对应用程序内存的I/O 请求
devmap_umem_setup() 将内核内存导出到用户空间
ddi_model_convert_from() 确定数据模型类型是否不匹配
表B–8过时的用户应用程序内核和设备访问函数
过时的函数替代函数
ddi_mapdev() devmap_setup()
ddi_mapdev_intercept() devmap_load()
ddi_mapdev_nointercept() devmap_unload()
ddi_mapdev_set_device_acc_attr() devmap()
ddi_segmap() devmap()
ddi_segmap_setup() devmap_setup()
hat_getkpfnum() devmap()
ddi_mmap_get_model() devmap()
与时间有关的函数
与时间有关的函数包括:
ddi_get_lbolt() 返回自重新引导以来的时钟周期数
ddi_get_time() 返回当前时间(以秒为单位)
delay() 使执行延迟指定的时钟周期数
drv_hztousec() 将时钟周期转换为微秒
drv_usectohz() 将微秒转换为时钟周期
与时间有关的函数
附录B • Solaris DDI/DKI 服务汇总579
drv_usecwait() 繁忙-等待指定的时间间隔
gethrtime() 获取高分辨率时间
gethrvtime() 获取高分辨率LWP 虚拟时间
timeout() 在指定的时间长度后执行函数
untimeout() 取消以前的超时函数调用
drv_getparm() ddi_get_lbolt()、ddi_get_time()
表B–9过时的与时间有关的函数
过时的函数替代函数
drv_getparm() ddi_get_lbolt()、ddi_get_time()
电源管理函数
该类函数包括:
ddi_removing_power() 使用DDI_SUSPEND 检查设备是否断电
pci_report_pmcap() 报告PCI 设备的电源管理功能
pm_busy_component() 将组件标记为繁忙
pm_idle_component() 将组件标记为空闲
pm_raise_power() 提高组件的电源级别
pm_lower_power() 降低组件的电源级别
pm_power_has_changed() 向电源管理框架通知有关自治电源级别的更改信息
pm_trans_check() 设备电源开关建议检查
表B–10过时的电源管理函数
函数名说明
ddi_dev_is_needed() 通知系统需要某一设备组件
pm_create_components() 创建可管理电源的组件
pm_destroy_components() 销毁可管理电源的组件
pm_get_normal_power() 获取设备组件的正常电源级别
pm_set_normal_power() 设置设备组件的正常电源级别
电源管理函数
580 编写设备驱动程序• 2006 年11 月
内核统计信息函数
内核统计信息函数包括:
kstat_create() 创建并初始化新的kstat
kstat_delete() 从系统中移除kstat
kstat_install() 向系统中添加完全初始化的kstat
kstat_named_init() 初始化已命名的kstat
kstat_runq_back_to_waitq() 记录从运行队列到等待队列的事务迁移
kstat_runq_enter() 记录向运行队列中添加的事务
kstat_runq_exit() 记录从运行队列中移除的事务
kstat_waitq_enter() 记录向等待队列中添加的事务
kstat_waitq_exit() 记录从等待队列中移除的事务
kstat_waitq_to_runq() 记录从等待队列到运行队列的事务迁移
内核日志记录和列显函数
内核日志记录和列显函数包括:
cmn_err()、vcmn_err() 显示错误消息
ddi_report_dev() 通知设备
strlog() 将消息提交至日志驱动程序
ddi_dev_report_fault() 报告硬件故障
scsi_errmsg() 显示SCSI 请求检测消息
scsi_log() 显示与SCSI 设备有关的消息
scsi_vu_errmsg() 显示SCSI 请求检测消息
缓存I/O 函数
缓存I/O 函数包括:
physio() 执行物理I/O
aphysio() 执行异步物理I/O
anocancel() 禁止取消异步I/O 请求
缓存I/O 函数
附录B • Solaris DDI/DKI 服务汇总581
minphys() 限制physio() 缓冲区大小
biowait() 暂停以待处理方式完成块I/O 的进程
biodone() 在完成缓冲区I/O 传送后释放缓冲区并通知阻塞的线程
bioerror() 指示缓冲区头中的错误
geterror() 返回I/O 错误
bp_mapin() 分配虚拟地址空间
bp_mapout() 取消分配虚拟地址空间
disksort() 使用单向电梯查找策略对缓冲区排序
getrbuf() 获取原始缓冲区头
freerbuf() 释放原始缓冲区头
biosize() 返回缓冲区结构的大小
bioinit() 初始化缓冲区结构
biofini() 取消初始化缓冲区结构
bioreset() 在I/O 完成后重用专用的缓冲区头
bioclone() 克隆另一个缓冲区
biomodified() 检查缓冲区是否已修改
clrbuf() 删除缓冲区的内容
虚拟内存函数
虚拟内存函数包括:
ddi_btop() 将设备字节转换为页(向下舍入)
ddi_btopr() 将设备字节转换为页(向上舍入)
ddi_ptob() 将设备页转换为字节
btop() 将以字节表示的大小转换为以页表示的大小(向下舍入)
btopr() 将以字节表示的大小转换为以页表示的大小(向上舍入)
ptob() 将以页表示的大小转换为以字节表示的大小
虚拟内存函数
582 编写设备驱动程序• 2006 年11 月
表B–11过时的虚拟内存函数
过时的函数替代函数
hat_getkpfnum() devmap()、ddi_dma_*_bind_handle()、
ddi_dma_nextcookie()
设备ID 函数
设备ID 函数包括:
ddi_devid_init() 分配设备ID 结构
ddi_devid_free() 释放设备ID 结构
ddi_devid_register() 注册设备ID
ddi_devid_unregister() 注销设备ID
ddi_devid_compare() 比较两个设备ID
ddi_devid_sizeof() 返回设备ID 的大小
ddi_devid_valid() 验证设备ID
ddi_devid_str_encode() 将设备ID 和minor_name 编码为以null 结尾的ASCII 字符串,
返回指向该字符串的指针
ddi_devid_str_decode() 从以前编码的字符串中解码设备ID 和minor_name,分配并返
回指向提取部分的指针
ddi_devid_str_free() 释放ddi_devid_* 函数返回的所有字符串
SCSI 函数
SCSI 函数包括:
scsi_probe() 探测SCSI 设备
scsi_unprobe() 释放在初始探测期间分配的资源
scsi_alloc_consistent_buf() 为SCSIDMA分配I/O 缓冲区
scsi_free_consistent_buf() 释放以前分配的SCSIDMAI/O 缓冲区
scsi_init_pkt() 准备完整的SCSI 包
scsi_destroy_pkt() 释放已分配的SCSI 包及其DMA资源
scsi_setup_cdb() 设置SCSI 命令描述符块(command descriptor block, CDB)
scsi_transport() 启动SCSI 命令
SCSI 函数
附录B • Solaris DDI/DKI 服务汇总583
scsi_poll() 运行轮询SCSI 命令
scsi_ifgetcap() 获取SCSI 传输功能
scsi_ifsetcap() 设置SCSI 传输功能
scsi_sync_pkt() 同步CPU 和I/O 内存视图
scsi_abort() 异常中止SCSI 命令
scsi_reset() 重置SCSI 总线或目标
scsi_reset_notify() 向目标驱动程序通知总线重置
scsi_cname() 解码SCSI 命令
scsi_dname() 解码SCSI 外围设备类型
scsi_mname() 解码SCSI 消息
scsi_rname() 解码SCSI 包完成原因
scsi_sname() 解码SCSI 感知密钥
scsi_errmsg() 显示SCSI 请求检测消息
scsi_log() 显示与SCSI 设备有关的消息
scsi_vu_errmsg() 显示SCSI 请求检测消息
scsi_hba_init() SCSI HBA系统初始化例程
scsi_hba_fini() SCSI HBA系统完成例程
scsi_hba_attach_setup() SCSI HBA连接例程
scsi_hba_detach() SCSI HBA分离例程
scsi_hba_probe() 缺省SCSI HBA探测函数
scsi_hba_tran_alloc() 分配传输结构
scsi_hba_tran_free() 释放传输结构
scsi_hba_pkt_alloc() 分配scsi_pkt 结构
scsi_hba_pkt_free() 释放scsi_pkt 结构
scsi_hba_lookup_capstr() 返回索引匹配功能字符串
表B–12 过时的SCSI 函数
过时的函数替代函数
free_pktiopb() scsi_free_consistent_buf()
get_pktiopb() scsi_alloc_consistent_buf()
SCSI 函数
584 编写设备驱动程序• 2006 年11 月
表B–12 过时的SCSI 函数(续)
过时的函数替代函数
makecom_g0() scsi_setup_cdb()
makecom_g0_s() scsi_setup_cdb()
makecom_g1() scsi_setup_cdb()
makecom_g5() scsi_setup_cdb()
scsi_dmafree() scsi_destroy_pkt()
scsi_dmaget() scsi_init_pkt()
scsi_hba_attach() scsi_hba_attach_setup()
scsi_pktalloc() scsi_init_pkt()
scsi_pktfree() scsi_destroy_pkt()
scsi_resalloc() scsi_init_pkt()
scsi_resfree() scsi_destroy_pkt()
scsi_slave() scsi_probe()
scsi_unslave() scsi_unprobe()
资源映射管理函数
资源映射管理函数包括:
rmallocmap() 分配资源映射
rmallocmap_wait() 分配资源映射,必要时等待
rmfreemap() 释放资源映射
rmalloc() 从资源映射中分配空间
rmalloc_wait() 从资源映射中分配空间,必要时等待
rmfree() 将空间重新释放到资源映射中
系统全局状态
ddi_in_panic() 确定系统是否处于紧急状态
系统全局状态
附录B • Solaris DDI/DKI 服务汇总585
实用程序函数
实用程序函数包括:
nulldev() 零返回函数
nodev() 错误返回函数
nochpoll() 不可轮询设备的错误返回函数
ASSERT() 表达式验证
bcopy() 在内核的地址位置之间复制数据
bzero() 清除给定字节数的内存
bcmp() 比较两个字节数组
ddi_ffs() 查找长整数中设置的第一位
ddi_fls() 查找长整数中设置的最后一位
swab() 以16 位半字交换字节
strcmp() 比较两个以null 结尾的字符串
strncmp() 比较两个以null 结尾的字符串,长度有限制
strlen() 确定字符串中的非空字节数
strcpy() 将字符串从一个位置复制到另一个位置
strncpy() 将字符串从一个位置复制到另一个位置,长度有限制
strchr() 在字符串中查找字符
sprintf()、vsprintf() 格式化内存中的字符
numtos() 将整数转换为十进制字符串
stoi() 将十进制字符串转换为整数
max() 返回两个整数中的较大值
min() 返回两个整数中的较小值
va_arg() 查找变量参数列表中的下一个值
va_copy() 复制变量参数列表的状态
va_end() 删除指向变量参数列表的指针
va_start() 查找指向变量参数列表开头的指针
实用程序函数
586 编写设备驱动程序• 2006 年11 月
使设备驱动程序支持64 位
本附录为要将设备驱动程序转换为支持64 位内核的设备驱动程序编写人员提供信息。本附
录还介绍了32 位设备驱动程序和64 位设备驱动程序之间的区别,并说明了将32 位设备驱
动程序转换为64 位设备驱动程序的步骤。这些信息仅适用于常规字符设备驱动程序和块设
备驱动程序。
本附录提供有关以下主题的信息:
第587 页中的“64 位驱动程序设计简介”
第588 页中的“常规转换步骤”
第596 页中的“已知的ioctl 接口”
64 位驱动程序设计简介
对于仅需支持32 位内核的驱动程序,现有32 位设备驱动程序将仍然有效,无需重新编译。
但是,大多数设备驱动程序需要进行一些更改才能在64 位内核中正确运行,且所有设备驱
动程序都需要重新编译以创建64 位驱动程序模块。本附录中的信息旨在指导您利用通用源
代码来生成32 位和64 位环境的驱动程序,从而提高代码可移植性并降低维护工作量。
开始清理设备驱动程序以便使用64 位环境之前,应了解32 位环境与64 位环境之间的区
别。特别是必须熟悉C 语言数据类型模型ILP32 和LP64。请参见下表。
表C–1 ILP32 与LP64 数据类型对比
C 类型ILP32 LP64
char 8 8
short 16 16
int 32 32
long 32 64
C附录C
587
表C–1 ILP32 与LP64 数据类型对比(续)
C 类型ILP32 LP64
long long 64 64
float 32 32
double 64 64
long double 96 128
pointer 32 64
因ILP32 与LP64 之间的差异而导致的特定于驱动程序的问题是本附录的主题。更多常规主
题将在《Solaris 64-bit Developer’s Guide》中介绍。
除了清理常规代码以支持LP64 的数据模型更改,驱动程序编写人员还必须提供对32 位和
64 位应用程序的支持。
ioctl(9E)、devmap(9E) 和mmap(9E) 入口点使应用程序和设备驱动程序之间可直接共享数据
结构。如果这些数据结构在32 位环境与64 位环境中的大小不同,则必须修改入口点,以便
驱动程序可确定应用程序的数据模型与内核的数据模型是否相同。如果数据模型不同,则
可对数据结构进行调整。请参见第298 页中的“对有64 位处理能力的设备驱动程序的I/O
控制支持”、第303 页中的“32 位和64 位数据结构宏”和第187 页中的“将内核内存与用
户映射相关联”。
在许多驱动程序中,只有少量ioctl 需要这种处理。其他ioctl 无需更改即可应用,只要这
些ioctl 传递的数据结构大小不变。
常规转换步骤
以下各节提供了有关转换驱动程序以在64 位环境中运行的信息。驱动程序编写人员可能需
要执行以下一项或多项任务:
1. 使用硬件寄存器的固定宽度类型。
2. 使用固定宽度的公共访问函数。
3. 检查并扩展派生类型的用法。
4. 检查DDI 数据结构中更改的字段。
5. 检查DDI 函数中更改的参数。
6. 根据需要修改用于处理用户数据的驱动程序入口点。
7. 检查x86 平台上使用64 位long 类型的结构。
下面详细说明这些步骤。
完成每一步骤后,请修复所有编译器警告,然后使用lint 查找其他问题。对于SC5.0(或
更高)版本的lint,要想找出64 位问题,必须在使用该命令时指定-Xarch=v9 和
-errchk=longptr64 选项。请参见《Solaris 64-bit Developer’s Guide》中有关使用和解释lint
输出的说明。
常规转换步骤
588 编写设备驱动程序• 2006 年11 月
注– 请勿忽略LP64 转换期间出现的编译警告。以前在ILP32 环境中可安全忽略的警告现在
可能表示比较严重的问题。
完成所有步骤后,同时将驱动程序作为32 位和64 位模块进行编译和测试。
使用硬件寄存器的固定宽度类型
许多处理硬件设备的设备驱动程序使用C 数据结构说明硬件的布局。在LP64 数据模型中,
使用long 或unsigned long 类型定义硬件寄存器的数据结构几乎肯定不正确,因为long 类型
现在是64 位。首先包括
,然后将此类数据结构更新为使用int32_t 或
uint32_t,而不是32 位设备数据的long。此方法可保留32 位数据结构的二进制布局。例
如,将以下代码:
struct device_regs {
ulong_t addr;
uint_t count;
}; /* Only works for ILP32 compilation */
更改为:
struct device_regs {
uint32_t addr;
uint32_t count;
}; /* Works for any data model */
使用固定宽度的公共访问函数
Solaris DDI 允许通过访问函数访问设备寄存器,以便可在多个平台间移植。DDI 公共访问
函数以前以字节和字等单位指定数据大小。例如,ddi_getl(9F) 用于访问32 位。此函数不
存在于64 位DDI 环境中,且已被替换为可指定要处理的位数的函数版本。
这些例程已添加到Solaris 2.6 操作环境的32 位内核中,以允许驱动程序编写人员采用其早期
版本。例如,要在32 位和64 位内核间进行移植,驱动程序必须使用ddi_get32(9F) 而不是
ddi_getl(9F) 来访问32 位数据。
所有公共访问例程都被替换为其固定宽度的对等例程。有关详细信息,请参见
ddi_get8(9F)、ddi_put8(9F)、ddi_rep_get8(9F) 和ddi_rep_put8(9F) 手册页。
常规转换步骤
附录C • 使设备驱动程序支持64 位589
检查并扩展派生类型的用法
应尽可能使用系统派生的类型(如size_t),以使产生的变量在各种函数间传递时都有
效。而新的派生类型uintptr_t 或intptr_t 是整数类型,应该用于指针。
固定宽度的整数类型用于表示二进制数据结构或硬件寄存器的显式大小,而基础C 语言数
据类型(如int)仍然可用于循环计数器或文件描述符。
一些系统派生类型在32 位系统上表示32 位,但是在64 位系统上表示64 位。以此方式更改
大小的派生类型包括:clock_t、daddr_t、dev_t、ino_t、intptr_t、off_t、size_t、
ssize_t、time_t、uintptr_t 和timeout_id_t。
设计使用这些派生类型的驱动程序时,请特别注意这些类型的用法,尤其是驱动程序将这
些值指定给其他类型(如固定宽度类型)的变量时。
检查DDI 数据结构中更改的字段
DDI 数据结构中某些字段的数据类型(如buf(9S))已被更改。使用这些数据结构的驱动程
序应确保正确使用这些字段。下面列出了变动很大的数据结构及字段。
buf 结构更改
以下列出的字段与传输大小(现在可超过4GB)有关。
size_t b_bcount; /* was type unsigned int */
size_t b_resid; /* was type unsigned int */
size_t b_bufsize; /* was type long */
ddi_dma_attr
ddi_dma_attr(9S) 结构定义DMA引擎和设备的属性。因为这些属性指定寄存器大小,所以
使用了固定宽度的数据类型而不是基本类型。
ddi_dma_cookie 结构更改
uint32_t dmac_address; /* was type unsigned long */
size_t dmac_size; /* was type u_int */
ddi_dma_cookie(9S) 结构包含32 位DMA地址,因此使用了固定宽度的数据类型来定义该地
址。其大小已重新定义为size_t。
csi_arq_status 结构更改
uint_t sts_rqpkt_state; /* was type u_long */
uint_t sts_rqpkt_statistics; /* was type u_long */
常规转换步骤
590 编写设备驱动程序• 2006 年11 月
此结构中的这些字段无需增大,已重新定义为32 位。
scsi_pkt 结构更改
uint_t pkt_flags; /* was type u_long */
int pkt_time; /* was type long */
ssize_t pkt_resid; /* was type long */
uint_t pkt_state; /* was type u_long */
uint_t pkt_statistics; /* was type u_long */
由于scsi_pkt(9S) 结构中的pkt_flags、pkt_state 和pkt_statistics 字段无需增大,因此
这些字段已重新定义为32 位整数。数据传输大小pkt_resid 字段需要增大,已重新定义为
ssize_t。
检查DDI 函数中更改的参数
本节介绍已更改的DDI 函数参数数据类型。
getrbuf() 参数更改
struct buf *getrbuf(int sleepflag);
在以前的发行版中,sleepflag 被定义为long 类型。
drv_getparm() 参数更改
int drv_getparm(unsigned int parm, void *value_p);
在以前的发行版中,value_p 被定义为unsigned long 类型。在64 位内核中,
drv_getparm(9F) 可提取32 位和64 位。此接口未定义这些量的数据类型,可能会发生简单
的编程错误。
以下新例程提供更安全的替代方法:
clock_t ddi_get_lbolt(void);
time_t ddi_get_time(void);
cred_t *ddi_get_cred(void);
pid_t ddi_get_pid(void);
强烈要求驱动程序编写人员使用这些例程而不要使用drv_getparm(9F)。
常规转换步骤
附录C • 使设备驱动程序支持64 位591
delay() 和timeout() 参数更改
void delay(clock_t ticks);
timeout_id_t timeout(void (*func)(void *), void *arg, clock_t ticks);
delay(9F) 和timeout(9F) 例程的ticks 参数已从long 更改为clock_t。
rmallocmap() 和rmallocmap_wait() 参数更改
struct map *rmallocmap(size_t mapsize);
struct map *rmallocmap_wait(size_t mapsize);
rmallocmap(9F) 和rmallocmap_wait(9F) 例程的mapsize 参数已从ulong_t 更改为size_t。
scsi_alloc_consistent_buf() 参数更改
struct buf *scsi_alloc_consistent_buf(struct scsi_address *ap,
struct buf *bp, size_t datalen, uint_t bflags,
int (*callback )(caddr_t), caddr_t arg);
在以前的发行版中,datalen 被定义为int 类型,bflags 被定义为ulong 类型。
uiomove() 参数更改
int uiomove(caddr_t address, size_t nbytes,
enum uio_rw rwflag, uio_t *uio_p);
nbytes 参数被定义为long 类型,但是由于nbytes 表示字节大小,因此size_t 更适合。
cv_timedwait() 和cv_timedwait_sig() 参数更改
int cv_timedwait(kcondvar_t *cvp, kmutex_t *mp, clock_t timeout);
int cv_timedwait_sig(kcondvar_t *cvp, kmutex_t *mp, clock_t timeout);
在以前的发行版中,cv_timedwait(9F) 和cv_timedwait_sig(9F) 例程的timeout 参数被定义
为long 类型。由于这些例程表示时间周期,因此clock_t 更适合。
ddi_device_copy() 参数更改
int ddi_device_copy(ddi_acc_handle_t src_handle,
caddr_t src_addr, ssize_t src_advcnt,
常规转换步骤
592 编写设备驱动程序• 2006 年11 月
ddi_acc_handle_t dest_handle, caddr_t dest_addr,
ssize_t dest_advcnt, size_t bytecount, uint_t dev_datasz);
src_advcnt、dest_advcnt、dev_datasz 参数的类型已更改。这些参数以前分别被定义为long、
long 和ulong_t 类型。
ddi_device_zero() 参数更改
int ddi_device_zero(ddi_acc_handle_t handle,
caddr_t dev_addr, size_t bytecount, ssize_t dev_advcnt,
uint_t dev_datasz):
在以前的发行版中,dev_advcnt 被定义为long 类型,dev_datasz 被定义为ulong_t 类型。
ddi_dma_mem_alloc() 参数更改
int ddi_dma_mem_alloc(ddi_dma_handle_t handle,
size_t length, ddi_device_acc_attr_t *accattrp,
uint_t flags, int (*waitfp)(caddr_t), caddr_t arg,
caddr_t *kaddrp, size_t *real_length,
ddi_acc_handle_t *handlep);
在以前的发行版中,length、flags 和real_length 分别被定义为uint_t、ulong_t 和uint_t *
类型。
修改处理数据共享的例程
如果设备驱动程序共享的数据结构包含使用ioctl(9E)、devmap(9E) 或mmap(9E) 的32 位应用
程序的long 或指针类型,并且已针对64 位内核重新编译驱动程序,则数据结构的二进制布
局将不兼容。如果当前已按long 类型定义了字段,并且未使用64 位数据项,请更改数据结
构,以使用仍为32 位的数据类型(int 和unsigned int)。否则,驱动程序需要识别ILP32
和LP64 的不同结构形式,并确定应用程序与内核之间是否出现模型不匹配。
要处理潜在的数据模型差异,需要编写可直接与用户应用程序交互的ioctl()、devmap() 和
mmap() 驱动程序入口点,以确定参数是否来自使用与内核具有相同数据模型的应用程序。
ioctl() 中的数据共享
要确定应用程序与驱动程序之间是否存在模型不匹配,驱动程序可使用FMODELS 掩码确定
ioctl() mode 参数的模型类型。在mode 中采用以下值之一来标识应用程序的数据模型:
FLP64-应用程序使用LP64 数据模型
常规转换步骤
附录C • 使设备驱动程序支持64 位593
FILP32-应用程序使用ILP32 数据模型
第298 页中的“对有64 位处理能力的设备驱动程序的I/O 控制支持” 中的代码示例说明如
何使用ddi_model_convert_from(9F) 处理此情况。
devmap() 中的数据共享
要使64 位驱动程序和32 位应用程序共享内存,64 位驱动程序生成的二进制布局必须与32
位应用程序使用的布局相同。要导出到应用程序的映射内存可能需要包含与数据模型有关
的数据结构。
很少内存映射设备会面临此问题,因为在内核数据模型发生变化时设备寄存器不会改变大
小。但是,一些将映射导出到用户地址空间的伪设备可能要将不同数据结构导出到ILP32 或
LP64 应用程序。要确定是否出现了数据模型不匹配,devmap(9E) 可使用model 参数说明应
用程序期望的数据模型。model 参数被设置为以下值之一:
DDI_MODEL_ILP32应用程序使用ILP32 数据模型
DDI_MODEL_LP64应用程序使用LP64 数据模型
可将未经转换的模型参数传递到ddi_model_convert_from(9F) 例程或STRUCT_INIT()。请参
见第303 页中的“32 位和64 位数据结构宏”。
mmap() 中的数据共享
由于mmap(9E) 没有可用于传递数据模型信息的参数,因此可编写驱动程序的mmap(9E) 入口
点,以使用新的DDI 函数ddi_model_convert_from(9F)。此函数返回以下值之一,以指示应
用程序的数据类型模型:
DDI_MODEL_ILP32-应用程序要求ILP32 数据模型
DDI_MODEL_ILP64-应用程序要求LP64 数据模型
DDI_FAILURE-未从mmap(9E) 调用函数
与ioctl() 和devmap() 一样,可将模型位传递到ddi_model_convert_from(9F) 以确定是否需
要进行数据转换,或可将模型传递到STRUCT_INIT()。
或者,迁移设备驱动程序以支持devmap(9E) 入口点。
检查x86 平台上64 位Long 数据类型的结构
您应认真检查x86 平台上使用64 位long 类型(如uint64_t)的结构。32 位模式编译与64 位
模式编译的对齐方式和大小可能不同。请参考以下示例。
常规转换步骤
594 编写设备驱动程序• 2006 年11 月
#include <studio>
#include <sys>
struct myTestStructure {
uint32_t my1stInteger;
uint64_t my2ndInteger;
};
main()
{
struct myTestStructure a;
printf("sizeof myTestStructure is: %d\n", sizeof(a));
printf("offset to my2ndInteger is: %d\n", (uintptr_t)&a.bar - (uintptr_t)&a);
}
在32 位系统中,该示例显示以下结果:
sizeof myTestStructure is: 12
offset to my2ndInteger is: 4
而在64 位系统中,该示例显示以下结果:
sizeof myTestStructure is: 16
offset to my2ndInteger is: 8
因此,32 位应用程序与64 位应用程序对结构的理解不同。这样,尝试在32 位和64 位两种
环境中使用同一结构可能会导致问题。这种情况经常发生,尤其是在通过ioctl() 调用将结
构传入或传出内核的情况下。
常规转换步骤
附录C • 使设备驱动程序支持64 位595
已知的ioctl 接口
许多ioctl(9E) 操作对一类设备驱动程序通用。例如,大多数磁盘驱动程序实现dkio(7I) 系
列的众多ioctls。这些接口中有许多将数据结构复制到内核中,或从内核中复制出数据结
构,在LP64 数据模型中这些数据结构的一部分已更改了大小。以下部分列出了对于dkio,
fdio(7I)、fbio(7I)、cdio(7I) 和mtio(7I) 系列的ioctls,现在需要在64 位驱动程序ioctl 例
程中显式进行转换的ioctls。
ioctl 命令受影响的数据结构参考
DKIOCGAPART
DKIOCSAPART
dk_map
dk_allmap
dkio(7I)
DKIOGVTOC
DKIOSVTOC
partition
vtoc
dkio(7I)
FBIOPUTCMAP
FBIOGETCMAP
fbcmap fbio(7I)
FBIOPUTCMAPI
FBIOGETCMAPI
fbcmap_i fbio(7I)
FBIOCCURSOR
FBIOSCURSOR
fbcursor fbio(7I)
CDROMREADMODE1
CDROMREADMODE2
cdrom_read cdio(7I)
CDROMCDDA cdrom_cdda cdio(7I)
CDROMCDXA cdrom_cdxa cdio(7I)
CDROMSUBCODE cdrom_subcode cdio(7I)
FDIOCMD fd_cmd fdio(7I)
FDRAW fd_raw fdio(7I)
MTIOCTOPmtop mtio(7I)
MTIOCGET mtget mtio(7I)
MTIOCGETDRIVETYPE mtdrivetype_request mtio(7I)
USCSICMD uscsi_cmd scsi(4)
已知的ioctl 接口
596 编写设备驱动程序• 2006 年11 月
设备大小
nblocks 属性按块设备的每一片导出。此属性包含512 字节块的数量,设备的每一片都支持
这些块。nblocks 属性被定义为带符号的32 位量,这就将每一片的最大大小限制为1TB。
每个磁盘提供1TB以上存储空间的磁盘设备必须定义Nblocks 属性,该属性仍应包含设备
可支持的512 字节块的数量。但是,Nblocks 是带符号的64 位量,它除去了对磁盘空间的任
何实际限制。
nblocks 属性现在已过时。所有磁盘设备应提供Nblocks 属性。
已知的ioctl 接口
附录C • 使设备驱动程序支持64 位597
598
索引
数字和符号
64 位设备驱动程序, 298, 587
A
add_drv 命令, 259, 465-466
设备名称, 463
说明, 495
allocb() 函数, 473-474
aphysio() 函数, 285
aread() 入口点, 异步数据传输, 280
ASSERT(9F) 宏, 534
attach() 入口点, 468-469, 480-482
说明, 101-109
网络驱动程序, 438
系统电源管理, 482-483
主动电源管理, 481
autoconfiguration(自动配置)
SCSI HBA驱动程序, 382
概述, 85
awrite() 入口点, 异步数据传输, 280
B
biodone() 函数, 318
buf 结构
更改, 590
说明, 316
C
cb_ops 结构, 说明, 89
cfgadm_usb 命令, 484
close() 入口点
块驱动程序, 314
说明, 27 7
cmn_err() 函数, 260
调试, 533
示例, 330
说明, 46
.conf 文件, 请参见硬件配置文件
cookie, DMA, 156
CPR(CheckPoint and Resume,检查点和恢复)
, 482-483
crash(1M) 命令, 515
csi_arq_status 结构, 更改, 591
cv_timedwait_sig() 函数, 更改, 592
cv_timedwait() 函数, 更改, 592
D
DDI/DKI
另请参见LDI
和磁盘性能, 331
概述, 51-52
设计注意事项, 44
在内核中的用途, 50
ddi_dma_attr 结构, 159
ddi_eventcookie_t, 239-240
DDI_INFO_DEVT2DEVINFO, 111
DDI_INFO_DEVT2INSTANCE, 111
DDI_RESUME, detach() 函数, 224
DDI_SUSPEND, detach() 函数, 223
599
DDI 功能表, 563-586
DDI 函数
ddi_add_intr() 函数, 131
ddi_create_minor_node() 函数, 102
ddi_device_copy() 函数, 593
ddi_device_zero() 函数, 593
ddi_devid_free() 函数, 238
ddi_dma_getwin() 函数, 157
ddi_dma_mem_alloc() 函数, 593
ddi_dma_nextseg() 函数, 157
ddi_driver_major() 函数, 309
ddi_enter_critical(), 555
ddi_get_cred() 函数, 591, 594
ddi_get_driver_private() 函数, 337, 443
ddi_get_instance() 函数, 448
ddi_get_lbolt() 函数, 591
ddi_get_pid() 函数, 591
ddi_get_time() 函数, 591
ddi_get()X, 537
ddi_log_sysevent() 函数, 7 8
ddi_model_convert_from() 函数, 594
ddi_prop_free() 函数, 241
ddi_prop_get_int() 函数, 434
ddi_prop_lookup_string() 函数, 241
ddi_prop_lookup() 函数, 7 3
ddi_prop_op(), 7 4
ddi_put()X, 537
ddi_regs_map_setup() 函数, 118
ddi_removing_power() 函数, 224
ddi_rep_get()X, 537
ddi_rep_put()X, 537
ddi_set_driver_private() 函数, 337
ddi_umem_alloc(), 538
ddi_umem_alloc() 函数, 187
ddi_umem_free() 函数, 192
delay() 函数, 592
timeout() 函数, 592
uiomove() 函数, 592
uiomove() 示例, 281
DDI 兼容驱动程序
兼容性测试, 502
字节排序, 548
DDI 数据结构
buf 结构, 590
ddi_dma_attr 结构, 590
ddi_dma_cookie 结构, 590
delay() 函数, 更改, 592
dest_adcent 参数, ddi_device_copy(), 更改, 593
detach() 入口点
热移除, 478-479
说明, 109-111
系统电源管理, 482-483
主动电源管理, 481
dev_advcnt 参数, ddi_device_zero(), 更改, 593
dev_datasz 参数, ddi_device_copy(), 更改, 593
dev_datasz 参数, ddi_device_zero(), 更改, 593
dev_info_t 函数, 564
dev_ops 结构, 说明, 87-88
dev_t 函数, 564
devfsadm 命令, 495
device-dependency, power.conf 项, 215
device-dependency-property, power.conf 项, 215
/devices 目录
说明, 51
显示设备树, 56
devmap_ 函数
devmap_devmem_setup() 函数, 184
devmap_load() 函数, 209
devmap_umem_setup() 函数, 190
devmap_unload() 函数, 210
devmap_ 入口点
devmap_access() 函数, 198-199, 210
devmap_contextmgt() 函数, 200
devmap_dup() 函数, 202-203
devmap_map() 函数, 196
devmap_unmap() 函数, 203-207
devmap() 函数, 184
DKI, 请参见DDI/DKI
DL_CLDLS, DLPI 符号, 440
DL_ETHER
Ethernet V2 包处理, 438-439
GLD 支持, 438
ISO 8802-3 (IEEE 802.3) 包处理, 438-439
网络统计信息, 444
DL_FDDI
GLD 支持, 438, 439
SNAP 处理, 439
DL_STYLE1, DLPI 符号, 440
DL_STYLE2, DLPI 符号, 440
DL_TPR
GLD 支持, 438, 439
SNAP 处理, 439
索引
600 编写设备驱动程序• 2006 年11 月
DL_TPR (续)
源路由, 439
DL_VERSION_2, DLPI 符号, 440
DLIOCRAW, ioctl() 函数, 441
DLPI 符号
DL_CLDLS, 440
DL_STYLE1, 440
DL_STYLE2, 440
DL_VERSION_2, 440
DLPI 提供者, 440
DLPI 原语, 440-441
DL_ATTACH_REQ, 440
DL_BIND_REQ, 440
DL_DETACH_REQ, 440
DL_DISABMULTI_REQ, 440
DL_ENABMULTI_REQ, 440
DL_GET_STATISTICS_ACK, 441
DL_GET_STATISTICS_REQ, 441, 443
DL_INFO_ACK, 440
DL_INFO_REQ, 440
DL_PHYS_ADDR_ACK, 441
DL_PHYS_ADDR_REQ, 441
DL_PROMISCOFF_REQ, 440
DL_PROMISCON_REQ, 440
DL_SET_PHYS_ADDR_REQ, 441
DL_UNATTACHED_REQ, 440
DL_UNBIND_REQ, 440
DL_UNITDATA_IND, 441
DL_UNITDATA_REQ, 441
DMA
cookie, 156, 157
操作, 158-163
窗口, 157, 178
对象, 155
对象锁定, 163
缓冲区分配, 168
回调, 17 3
寄存器结构, 165
句柄, 155, 157, 163
释放句柄, 17 3
释放资源, 172-173
突发流量大小, 167
物理地址, 157
限制, 159
虚拟地址, 157
专用缓冲区分配, 168-170
DMA(续)
传输, 284-285
传送, 158
资源分配, 164-167
DMA函数, 575-577
过时, 576-577
driver.conf 文件, 请参见硬件配置文件
drv_getparm() 函数, 更改, 591
drv_usecwait(9F), 555
DTrace, 532
dump() 入口点, 块驱动程序, 329
DVMA
S 总线插槽支持, 554
虚拟地址, 157
E
EHCI(Enhanced Host Controller Interface,增强型主
机控制器接口), 460
system 文件, 507
/etc/driver_aliases 文件, 465-466
/etc/power.conf 文件, 设备相关性, 215
Ethernet V2, 请参见DL_ETHER
F
_fini() 入口点
示例, 94
实现时必需的, 35
flags 参数, ddi_dma_mem_alloc(), 更改, 593
freemsg() 函数, 473-474
fuser 命令, 显示设备使用信息, 267-268
G
getinfo() 入口点, 111
getmajor() 函数, 309
getrbuf() 函数, 更改, 591
GLD
驱动程序, 437-458
支持的设备类型, 438
gld_intr() 函数, 458
GLD ioctl 函数, 441
索引
601
gld_mac_alloc() 函数, 456
gld_mac_free() 函数, 456
gld_mac_info 结构
GLD 参数, 451
说明, 446-449
网络驱动程序, 438, 442
在gld_intr() 函数中使用, 458
gld_recv() 函数, 457
gld_register() 函数, 456
gld_sched() 函数, 457
gld_stats 结构, 网络驱动程序, 444
gld_unregister() 函数, 457
GLD 服务例程
gld_intr() 函数, 458
gld_mac_alloc() 函数, 456
gld_mac_free() 函数, 456
gld_recv() 函数, 457
gld_register() 函数, 456
gld_sched() 函数, 457
gld_unregister() 函数, 457
GLD 符号
GLD_BADARG, 455
GLD_FAILURE, 455
GLD_MAC_PROMISC_MULTI, 451
GLD_MAC_PROMISC_NONE, 451
GLD_MAC_PROMISC_PHYS, 451
GLD_MULTI_DISABLE, 453
GLD_MULTI_ENABLE, 453
GLD_NOLINK, 454
GLD_NORESOURCES, 457
GLD_NOTSUPPORTED, 453
GLD_SUCCESS, 455
GLD 入口点
gldm_get_stats(), 455
gldm_intr(), 454-455
gldm_ioctl(), 455
gldm_reset(), 452
gldm_send(), 454
gldm_set_mac_addr(), 453
gldm_set_multicast(), 453
gldm_set_promiscuous(), 453-454
gldm_start(), 452
gldm_stop(), 452
GLD 数据结构
gld_mac_info, 446-449
gld_stats, 449-451
GLD 网络统计信息, 443-445
gld(9E) 入口点, 网络驱动程序, 438
gld(9F) 函数, 438
网络驱动程序, 443
gldm_get_stats(), 说明, 444
gldm_private 结构, 447
H
HBA驱动程序, 请参见SCSI HBA驱动程序
hubd USB 集线器驱动程序, 47 8
I
I/O
DMA传输, 284
程控传输, 281
磁盘控制, 330
多路复用, 291
分散/集中式结构, 27 9
其他控制, 295-303
同步数据传输, 280, 318
文件系统结构, 308
异步数据传输, 280, 323
字节流, 38
IEEE 802.3, 请参见DL_ETHER
IEEE 802.5, 请参见DL_TPR
ILP32
在devmap() 中使用, 594
在ioctl() 中使用, 594
在mmap() 中使用, 594
ILP64, 在mmap() 中使用, 594
_info() 入口点
示例, 94
实现时必需的, 35
_init() 入口点
示例, 93
实现时必需的, 35
ioctl() 函数
DLIOCRAW, 441
命令, 596
字符驱动程序, 295-298
iovec 结构, 27 9
ISO 8802-3, 请参见DL_ETHER
索引
602 编写设备驱动程序• 2006 年11 月
ISO 9314-2, 请参见DL_TPR
K
kmdb 调试程序, 515-517
宏, 516-517
设置断点, 516
在SPARC 系统中引导, 515
在x86 系统中引导, 515
kmem_alloc() 函数, 46
kmem_flags 内核变量, 509-510
kmem_free() 函数, 238
kstat, 成员, 530
kstat 结构, 530-532
kstat 结构, 网络统计信息, 443
L
LDI, 235-268
fuser 命令, 267-268
libdevinfo 接口, 262-268
prtconf 命令, 263-267
定义, 50
分层标识符, 236, 241-258
分层驱动程序, 235
分层驱动程序句柄, 237-240, 241-258
目标设备, 235, 237-240
内核设备消费方, 235
设备访问, 236
设备分层, 262-268
设备使用情况, 236, 262-268, 267-268
设备消费方, 235
设备信息, 236
事件通知接口, 239-240
LDI 函数
ldi_add_event_handler() 函数, 239-240
ldi_aread() 函数, 237-238
ldi_awrite() 函数, 237-238
ldi_close() 函数, 237, 241
ldi_devmap() 函数, 237-238
ldi_dump() 函数, 237-238
ldi_get_dev() 函数, 238
ldi_get_devid() 函数, 238
ldi_get_eventcookie() 函数, 239-240
LDI 函数(续)
ldi_get_minor_name() 函数, 238
ldi_get_otyp() 函数, 238
ldi_get_size() 函数, 238
ldi_getmsg() 函数, 237-238
ldi_ident_from_dev() 函数, 236, 241
ldi_ident_from_dip() 函数, 236
ldi_ident_from_stream() 函数, 236
ldi_ident_release() 函数, 236, 241
ldi_ioctl() 函数, 237-238
ldi_open_by_dev() 函数, 237
ldi_open_by_devid() 函数, 237
ldi_open_by_name() 函数, 237, 241
ldi_poll() 函数, 237-238
ldi_prop_exists() 函数, 239
ldi_prop_get_int() 函数, 239
ldi_prop_get_int64() 函数, 239
ldi_prop_lookup_byte_array() 函数, 239
ldi_prop_lookup_int_array() 函数, 239
ldi_prop_lookup_int64_array() 函数, 239
ldi_prop_lookup_string_array() 函数, 239
ldi_prop_lookup_string() 函数, 239
ldi_putmsg() 函数, 237-238
ldi_read() 函数, 237-238
ldi_remove_event_handler() 函数, 239-240
ldi_strategy() 函数, 237-238
ldi_write() 函数, 237-238, 241
LDI 类型
ldi_callback_id_t, 239-240
ldi_handle_t, 237-240
ldi_ident_t, 236
length 参数, ddi_dma_mem_alloc(), 更改, 593
libdevinfo(), 显示设备树, 54
libdevinfo 设备信息库, 262-268
lint 命令, 64 位环境, 588
lnode, 262-263
LP64
在devmap() 中使用, 594
在ioctl() 中使用, 593
LUN 位, 353
M
M_ERROR, 539
makedevice() 函数, 309
索引
603
mapsize 参数, rmallocmap(), 更改, 592
mdb
编写命令, 522
检测内核内存泄漏, 521-522
mdb 调试程序, 517-519
导航设备树, 525-529
检索软状态信息, 529
正在运行, 518-519
minphys() 函数, 287
批量传输请求, 474-475
mmap() 函数, 驱动程序通知, 207
moddebug 内核变量, 509
modinfo 命令, 261, 508-509
modldrv 结构, 说明, 87
modlinkage 结构, 说明, 86-87
modload 命令, 508-509
module_info 结构, 网络驱动程序, 442
modunload 命令, 508-509
说明, 496
mount() 函数, 块驱动程序, 312
msgb() 结构, 474-475, 476
MSI-X 中断
实现, 127
已定义, 126
MSI 中断
实现, 127
已定义, 126
mutex_init() 函数, 468
mutex_owned() 函数, 示例, 534
N
Nblocks 属性
需要的定义, 597
用于块设备驱动程序, 309
nblocks 属性, 用于块设备驱动程序, 309
nbytes 参数, uiomove(), 更改, 592
no-involuntary-power-cycles 属性, 217
nvlist_alloc 结构, 说明, 80
O
OHCI(Open Host Controller Interface,开放式主机控
制器接口), 460
open() 入口点
块驱动程序, 312
网络驱动程序, 440
字符驱动程序, 27 5
P
PCI 配置函数, 备用访问机制, 57 1
PCI 设备, 550
PCI 总线, 550
I/O 地址空间, 552
内存地址空间, 552
配置地址空间, 551
配置基址寄存器, 552
硬件配置文件, 552
physio() 函数, 说明, 284
pm_busy_component() 函数, 480-482
pm_idle_component() 函数, 480-482
pm_lower_power() 函数, 481
pm_raise_power() 函数, 480-482
power.conf 文件, 请参见/etc/power.conf 文件
power() 入口点, 480-482, 482
print() 入口点, 块驱动程序, 330
probe() 入口点
SCSI 目标驱动程序, 340
说明, 96-101
PROM 命令, 556
prop_op() 入口点, 说明, 7 4
prtconf 命令
显示绑定的驱动程序, 463
显示接口, 465
显示内核设备使用信息, 263-267
显示设备名称, 462-464
显示设备树, 54
显示属性, 7 2
putnext, 539
R
read() 入口点, 同步数据传输, 280
real_length 参数, ddi_dma_mem_alloc(), 更改, 593
reg 属性, 7 1
removable-media, 215
rmallocmap_wait() 函数, 更改, 592
索引
604 编写设备驱动程序• 2006 年11 月
rmallocmap() 函数, 更改, 592
S
S_IFCHR, 102
S 总线
地理寻址, 553
地址位, 554
物理地址空间, 553
硬件配置文件, 555
支持DVMA的插槽, 554
SAP, 定义, 438
SCSA, 334, 366
HBA传输层, 366
界面, 367
全局数据定义, 362
SCSI
体系结构, 334
总线, 333
scsi_ 函数
scsi_alloc_consistent_buf() 函数, 351
scsi_destroy_pkt() 函数, 351
scsi_dmafree() 函数, 356
scsi_free_consistent_buf() 函数, 352
scsi_ifgetcap() 函数, 353
scsi_ifsetcap() 函数, 353
scsi_init_pkt() 函数, 350
scsi_probe() 函数, 388
scsi_setup_cdb() 函数, 352
scsi_sync_pkt() 函数, 351, 356
scsi_transport() 函数, 354
scsi_unprobe() 函数, 388
摘要, 335
scsi_ 结构
scsi_address 结构, 37 1
scsi_device 结构, 37 2
scsi_hba_tran 结构, 368
scsi_pkt 结构, 37 3
scsi_alloc_consistent_buf() 函数, 更改, 592
scsi_device 结构, 337
scsi_hba_ 函数
scsi_hba_attach_setup() 函数, 434
scsi_hba_lookup_capstr() 函数, 419
scsi_hba_pkt_alloc() 函数, 390
scsi_hba_ 函数, scsi_hba_pkt_free() 函数, 404
scsi_hba_ 函数
scsi_hba_probe() 函数, 388
摘要列表, 37 6
scsi_hba_tran 结构, scsi_pkt 结构, 37 4
SCSI HBA驱动程序
autoconfiguration(自动配置), 382
DMA资源, 395
和热插拔, 433-434
与热插拔, 47
安装, 434
初始化传输结构, 383
概述, 366-367
功能管理, 419
克隆, 37 5
命令超时, 418
命令传输, 406
命令状态结构, 37 7
配置属性, 434
驱动程序实例初始化, 388
入口点摘要, 367
属性, 435
数据结构, 368
头文件, 37 7
中断处理, 411
中止和重置管理, 429
资源分配, 390
SCSI HBA驱动程序入口点
tran_abort() 函数, 429
tran_dmafree() 函数, 405
tran_getcap() 函数, 419
tran_init_pkt() 函数, 390
tran_reset_notify() 函数, 430
tran_reset() 函数, 430
tran_setcap() 函数, 423
tran_start() 函数, 407
tran_sync_pkt() 函数, 405
tran_tgt_free() 函数, 389
tran_tgt_init() 函数, 388
tran_tgt_probe() 函数, 388
按类别, 387
scsi_pkt 结构, 338
更改, 591
SCSI 函数, 583-585
过时, 584-585
SCSI 目标驱动程序
SCSI 例程, 335
索引
605
SCSI 目标驱动程序(续)
初始化命令描述符块, 352
概述, 333
回调例程, 354
生成命令, 352
属性, 336, 344, 435
数据结构, 337
重新使用包, 356
传输命令, 353
资源分配, 350
自动配置, 340
自动请求检测模式, 357
segmap() 入口点
驱动程序通知, 207
说明, 290
size 属性, 27 3
SNAP
DL_FDDI, 439
DL_TPR, 439
定义, 439
snoop 命令, 网络驱动程序, 441
Solaris 内核, 请参见内核
SPARC 处理器
乘法和除法指令, 547
浮点操作, 545
寄存器窗口, 546
结构成员对齐, 546
数据对齐, 546
字节排序, 546
SPARC 的数据对齐, 546
src_advcnt 参数, ddi_device_copy(), 更改, 593
strategy() 入口点
块驱动程序, 315
字符驱动程序, 288
STREAMS
cb_ops 结构, 89
驱动程序, 39
网络驱动程序支持, 437
T
ticks 参数, delay(), 更改, 592
ticks 参数, timeout(), 更改, 592
timeout 参数, cv_timedwait(), 更改, 592
timeout() 函数, 更改, 592
tip 连接, 505
tran_abort() 入口点, SCSI HBA驱动程序, 429
tran_destroy_pkt() 入口点, SCSI HBA驱动程序, 403
tran_dmafree() 入口点, SCSI HBA驱动程序, 405
tran_getcap() 入口点, SCSI HBA驱动程序, 419
tran_init_pkt() 入口点, SCSI HBA驱动程序, 390
tran_reset_notify() 入口点, SCSI HBA驱动程序, 430
tran_reset() 入口点, SCSI HBA驱动程序, 430
tran_setcap() 入口点, SCSI HBA驱动程序, 423
tran_start() 入口点, SCSI HBA驱动程序, 407
tran_sync_pkt() 入口点, SCSI HBA驱动程序, 405
U
UHCI(Universal Host Controller Interface,通用主机
控制器接口), 460
uiomove() 函数
更改, 592
示例, 281
update_drv() 函数, 说明, 495
update_drv 命令, 261, 465-466
usb_mid USB 多接口驱动程序, 464, 478-479, 483
USB 函数
cfgadm_usb 命令, 484
usb_alloc_bulk_req() 函数, 47 2
usb_alloc_ctrl_req() 函数, 47 2
usb_alloc_intr_req() 函数, 47 2
usb_alloc_isoc_req() 函数, 47 2
usb_client_attach() 函数, 468-469
usb_client_detach() 函数, 469
usb_clr_feature() 函数, 485
usb_create_pm_components() 函数, 480-482
usb_free_bulk_req() 函数, 47 2
usb_free_ctrl_req() 函数, 47 2
usb_free_descr_tree() 函数, 468
usb_free_dev_data() 函数, 469
usb_free_intr_req() 函数, 47 2
usb_free_isoc_req() 函数, 47 2
usb_get_addr() 函数, 485
usb_get_alt_if() 函数, 484-485
usb_get_cfg() 函数, 484
usb_get_current_frame_number() 函数, 47 6
usb_get_dev_data() 函数, 466-467, 468-469,
469-470
usb_get_if_number() 函数, 483
索引
606 编写设备驱动程序• 2006 年11 月
USB 函数(续)
usb_get_max_pkts_per_isoc_request() 函数, 47 6
usb_get_status() 函数, 485
usb_get_string_descr() 函数, 485
usb_handle_remote_wakeup() 函数, 480, 481
usb_lookup_ep_data() 函数, 467, 470
usb_owns_device() 函数, 484
usb_parse_data() 函数, 466-467
usb_pipe_bulk_xfer() 函数, 471-476
usb_pipe_close() 函数, 471, 476
usb_pipe_ctrl_xfer_wait() 函数, 473, 474
usb_pipe_ctrl_xfer() 函数, 471-476
usb_pipe_drain_reqs() 函数, 476-477
usb_pipe_get_max_bulk_transfer_ size() 函
数, 474-475
usb_pipe_get_private() 函数, 485
usb_pipe_get_state() 函数, 470, 476-477
usb_pipe_intr_xfer() 函数, 471-476, 475
usb_pipe_isoc_xfer() 函数, 471-476
usb_pipe_open() 函数, 470, 472
usb_pipe_reset() 函数, 470, 476-477
usb_pipe_set_private() 函数, 485
usb_pipe_stop_intr_polling() 函数, 473, 475
usb_pipe_stop_isoc_polling() 函数, 473, 476
usb_print_descr_tree() 函数, 469
usb_register_hotplug_cbs() 函数, 47 8
usb_set_alt_if() 函数, 484-485
usb_set_cfg() 函数, 484
usb_unregister_hotplug_cbs() 函数, 47 8
USB 结构
usb_alloc_intr_request, 47 5
usb_bulk_request, 472, 474-475
usb_callback_flags, 471, 474
usb_completion_reason, 471, 474
usb_ctrl_request, 472, 474
usb_intr_request, 47 2
usb_isoc_request, 472, 476
usb_request_attributes, 47 3
USB 驱动程序, 460-461
hubd USB 集线器驱动程序, 47 8
usb_mid USB 多接口驱动程序, 464, 478-479, 483
版本控制, 468
等时数据传输请求, 475-476
管道, 462, 468, 469-477
打开, 47 0
关闭, 47 1
USB 驱动程序, 管道(续)
缺省控制, 466, 468, 469-470
刷新, 476-477
互斥锁初始化, 468
接口, 460
控制数据传输请求, 47 4
描述符树, 466-467, 468
批量传输数据传输请求, 474-475
设置配置, 484
设置替代, 484-485
事件通知, 47 8
数据传输
回调状态标志, 471, 474
完成原因, 471, 474
数据传输请求, 472-476
同步控制请求, 47 4
消息块, 473-474
异步传输回调, 47 1
中断数据传输请求, 47 5
注册, 468-469
注册事件, 47 8
USB 设备
拆分接口, 465, 484
当前配置, 462
电源管理, 480-483
被动, 482
设备, 480-482
系统, 482-483
主动, 481-482
端点, 462
等时, 469
控制, 469
批量传输, 469
缺省, 469-470
中断, 469
多种配置, 462
复合, 464-465, 484
兼容设备名称, 462-464
接口, 462
接口编号, 483
配置描述符, 466-467
热插拔, 477-479
插入, 47 8
回调, 47 8
移除, 478-479
重新插入, 47 9
索引
607
USB 设备(续)
替代设置, 462
远程唤醒, 480
状态, 477-483
USB 2.0 规范, 459-460
USBA2.0 框架, 459-486
USBA(Solaris USBArchitecture,Solaris USB 体系结
构), 459-486
V
volatile 关键字, 540
W
wput, 539
write() 函数
同步数据传输, 280
用户地址示例, 27 7
X
x86 处理器
浮点操作, 547
数据对齐, 547
字节排序, 547
包
包处理
Ethernet V2, 438-439
ISO 8802-3 (IEEE 802.3), 438-439
保
保存崩溃转储, 512
备
备用访问机制, 57 1
崩
崩溃转储, 保存, 512
编
编译和链接驱动程序, 492
标
标记排队, 436
部
部分存储排序, 549
测
测试
DDI 兼容性, 502
安装和打包, 502
磁带驱动程序, 503
磁盘驱动程序, 503
功能, 500
配置, 500
设备驱动程序, 500
网络驱动程序, 504
异步通信驱动程序, 503
测试调试程序, 避免数据丢失, 510-513
测试模块, 507
测试设备驱动程序, 505-514
程
程控I/O, 281
用于DDI 访问例程, 537
程控I/O 函数, 570-575
过时, 572-575
索引
608 编写设备驱动程序• 2006 年11 月
处
处理器问题
SPARC, 545, 546, 547
x86, 547
串
串行连接, 505
窗
窗口, DMA, 178
磁
磁带驱动程序, 测试, 503
磁盘
I/O 控制, 330
性能, 331
磁盘驱动程序测试, 503
次
次要设备号, 51
次要设备节点, 102
修改权限, 495
存
存储缓冲区, 548-549
存储类, 驱动程序数据, 61
错
错误处理, 501
错误消息, 列显, 46, 330
打
打包, 496
单
单个设备节点, 462
地
地址空间, 说明, 51
第
第三方DMA, 156, 158
第一方DMA, 156, 158
电
电源管理
另请参见设备电源管理
另请参见系统电源管理
USB 设备, 480-483
控制流程, 232
电源管理函数, 580-581
过时, 580-581
电源管理控制流程, 232
电源管理中的设备状态, 223
电源管理中的硬件状态, 223
调
调试
ASSERT(9F) 宏, 534
system 文件, 507
kmdb 调试程序, 515-517
kmem_flags, 509-510
mdb 调试程序, 517-519
moddebug, 508-509
编码提示, 533
编写mdb 命令, 522
常见任务, 519-530
索引
609
调试(续)
工具, 514
检测内核内存泄漏, 521-522
设置SPARC 测试系统, 506-507
设置x86 测试系统, 507
设置串行连接, 505
使用SPARC PROM 进行设备调试, 556
使用内核变量, 529-530
事后, 514-515
条件编译, 535
为灾难做准备, 510
系统寄存器, 519-521
显示内核数据结构, 522-525
引导替代内核, 510-512
调试设备驱动程序, 505-532
调优设备驱动程序, 530-532
DTrace, 532
kstat 结构, 530-532
动
动态内存分配, 46
读
读取器/写入器锁, 63
处理, 567
对
对象锁定, 163
多
多处理器注意事项, 194
多路复用I/O, 291
多线程
cb_ops 结构中的D_MP 标志, 89
和条件变量, 64
和锁定原语, 61
线程同步, 64
执行环境, 50
二
二进制兼容性
潜在问题, 593
说明, 51
发
发出警告音, 539
分
分层标识符, 请参见LDI
分层驱动程序接口, 请参见LDI
分层驱动程序句柄, 请参见LDI
分散/集中,DMA引擎, 157
分散/集中式, I/O, 279
复
复制数据
copyin() 函数, 27 8
copyout() 函数, 27 8
高
高级互斥锁, 中断, 145
高速缓存, 说明, 17 6
故
故障, 潜在故障, 定义, 542
管
管道
USB 设备, 462
USB 设备通信, 469-477
策略, 47 2
打开, 47 0
关闭, 47 1
索引
610 编写设备驱动程序• 2006 年11 月
管道(续)
互斥锁初始化, 468
缺省控制, 468, 469-470
刷新, 476-477
替代设置, 484-485
在attach() 之前使用, 466
光
光纤分布式数据接口, 请参见DL_FDDI
过
过时的DMA函数, 576-577
过时的SCSI 函数, 584-585
过时的程控I/O 函数, 572-575
过时的电源管理函数, 580-581
过时的内存分配函数, 567
过时的设备访问函数, 57 9
过时的属性函数, 565-566
过时的虚拟内存函数, 583
过时的用户进程信息函数, 57 8
过时的用户空间访问函数, 57 8
过时的用户应用程序内核函数, 57 9
过时的与时间有关的函数, 580
过时的中断函数, 131, 569-570
函
函数
另请参见DDI 函数
另请参见LDI 函数
请参见单独的函数
另请参见设备电源管理
请参见特定函数名称
另请参见条件变量函数
互
互斥锁
请参见互斥锁
函数, 62
互斥锁(续)
例程, 61
锁, 61-62
处理, 567
相关系统崩溃信息, 69
缓
缓冲区分配, DMA, 168
缓存I/O 函数, 581-582
恢
恢复设备目录, 513-514
回
回调函数
示例, 165
说明, 44
获
获取主设备号, 示例, 309
集
集线器驱动程序, 460-461
寄
寄存器结构, DMA, 165
兼
兼容属性, 说明, 58
索引
611
将
将内核内存与用户应用程序相关联, 187
将驱动程序绑定到USB 设备, 462-464
将驱动程序绑定到设备, 57
将设备内存导出到用户应用程序, 184
结
结点, 请参见总线结点设备驱动程序
结点驱动程序, 460-461
句
句柄, DMA, 155, 163, 173
开
开关设备电源, 217
可
可热插拔的驱动程序, 请参见热插拔
可维护性
报告故障, 542
检测有故障的设备, 542
添加新设备, 542
移除有故障的设备, 542
执行定期的“运行状况检查”, 542
可装入模块函数, 564
克
克隆SCSI HBA驱动程序, 37 5
块
块驱动程序
buf 结构, 316
cb_ops 结构, 89
概述, 37
块驱动程序(续)
片编号, 309
自动配置, 309
块驱动程序入口点, 308
close() 函数, 314
open() 函数, 312
strategy() 函数, 315
块设备的片编号, 309
链
链接驱动程序, 492
列
列显函数, 581
列显消息, 46
流
流, 539
流访问, 169
描
描述符树, 466-467, 468
名
名称属性, 说明, 58
模
模块
调试程序
请参见mdb 调试程序
模块函数, 564
模块目录, 494-495
索引
612 编写设备驱动程序• 2006 年11 月
内
内部模式寄存器, 555
内部顺序逻辑, 555
内存分配, 说明, 46
内存分配函数, 566-567
过时, 567
内存管理单元, 说明, 51
内存模型
SPARC, 549
存储缓冲区, 548-549
内存泄漏, 用mdb 检测, 521-522
内存映射
设备内存管理, 40, 183-192, 290
设备上下文管理, 193
内核
调试程序
请参见kmdb 调试程序
概述, 49
模块目录, 494-495
内存
分配, 46
检测泄漏mdb, 521-522
与用户应用程序相关联, 187
设备树, 50
内核变量
设置, 507-508
使用, 507
用于调试程序, 529-530
内核日志记录函数, 581
内核数据结构, 522-525
内核统计信息, 请参见kstat 结构, 530-532
内核统计信息函数, 581
内核线程函数, 567-568
排
排队, 436
配
配置, 测试设备驱动程序, 505-514
配置描述符群, 47 9
配置入口点
attach() 函数, 101
配置入口点(续)
detach() 函数, 109
getinfo() 函数, 111
配置文件, 硬件, 请参见硬件配置文件
潜
潜在故障, 定义, 542
驱
驱动程序绑定名称, 58
驱动程序模块入口点, 请参见入口点
驱动程序入口点, attach() 函数, 227
全
全存储序顺序, 549
热
热插拔, 47
请参见热插拔
USB 设备, 477-479
和SCSI HBA驱动程序, 433-434
与SCSI HBA驱动程序, 47
入
入口点
attach() 函数, 101-109, 468-469, 480-482
系统电源管理, 482-483
主动电源管理, 481
块驱动程序, 308
字符设备, 27 2
detach() 函数, 109-111, 223, 481
热移除, 478-479
系统电源管理, 482-483
用于设备电源管理, 217
ioctl() 函数, 295
power() 函数, 218, 480-482, 482
索引
613
入口点(续)
probe() 函数, 96-101
SCSAHBA摘要, 367
系统电源管理, 223
定义, 34
设备配置的, 95
设备上下文管理, 196
用于网络驱动程序, 452-455
软
软件状态函数, 566
软中断, 127
软状态信息
LDI, 241-258
USB, 468
在mdb 中检索, 529
上
上下文管理, 请参见设备上下文管理
设
设备
拆分接口, 465, 484
端点, 462
复合, 464-465, 484
接口, 462
接口编号, 483
配置, 462
替代设置, 462
设备ID 函数, 583
设备编号, 说明, 51
设备电源管理
pm_busy_component() 函数, 216, 480-482
pm_idle_component() 函数, 217, 480-482
pm_lower_power() 函数, 481
pm_raise_power() 函数, 480-482
power() 函数, 218
power() 入口点, 480-482, 482
usb_create_pm_components() 函数, 480-482
USB 设备, 480-482
设备电源管理(续)
电源级别, 213-215
定义, 211-212
接口, 216
模型, 212
入口点, 217
相关项, 215
状态转换, 216
组件, 212
设备访问函数
表, 578-579
过时, 57 9
块驱动程序, 312
字符驱动程序, 275-277
设备分层, 请参见LDI
设备寄存器, 映射, 101
设备节点, 462
设备轮询
在字符驱动程序中, 292
chpoll() 函数, 292
poll() 函数, 291
设备目录, 恢复, 513-514
设备内存
cb_ops 中的D_DEVMAP 标志, 89
映射, 40, 183-192
设备配置, 入口点, 95
设备驱动程序
另请参见装入驱动程序
64 位驱动程序, 298, 587
hubd USB 集线器驱动程序, 47 8
usb_mid USB 多接口驱动程序, 464, 478-479, 483
USB 驱动程序, 459-486
绑定, 465-466
绑定到设备节点, 57, 462-464
标准字符驱动程序, 38-39
别名, 495
测试, 500, 505-514
从内核中访问, 235
错误处理, 501
打包, 496
调试, 505-532
编码提示, 533
工具, 514
设置串行连接, 505
使用PROM, 556
调优, 530-532
索引
614 编写设备驱动程序• 2006 年11 月
设备驱动程序(续)
定义, 33
可装入接口, 89
块驱动程序, 37
列显消息, 46
模块配置, 491
配置描述符群, 47 9
入口点, 34
上下文, 45
使用kstat 结构, 530-532
使用update_drv() 修改信息, 495
头文件, 490
脱机, 47 8
网络驱动程序, 437-458
修改权限, 495
源文件, 491
在内核中的用途, 49
设备驱动程序上下文, 45
设备驱动程序头文件, 490
设备驱动程序源文件, 491
设备上下文管理, 193
操作, 195
模型, 194
入口点, 196
设备使用情况, 236
请参见LDI
设备树
导航, 在调试程序中, 525-529
概述, 52
显示, 53
在内核中的用途, 50
设备信息
di_link_next_by_lnode() 函数, 262
di_link_next_by_node() 函数, 262
di_link_private_get() 函数, 263
di_link_private_set() 函数, 263
di_link_spectype() 函数, 262
di_link_t, 262
di_link_to_lnode() 函数, 262
di_lnode_devinfo() 函数, 262
di_lnode_devt() 函数, 262
di_lnode_name() 函数, 262
di_lnode_next() 函数, 262
di_lnode_private_get() 函数, 263
di_lnode_private_set() 函数, 263
di_lnode_t, 262
设备信息(续)
di_node_t, 262
di_walk_link() 函数, 262
di_walk_lnode() 函数, 262
DINFOLYR, 262
LDI, 238
lnode, 262-263
nblocks 属性, 597
兼容设备名称, 462-464
将驱动程序绑定到USB 设备, 462-464
将驱动程序绑定到设备, 57
属性值, 239
树结构, 52
自标识, 549
设备中断, 请参见中断; 中断处理
实
实例编号, 95
实用程序函数, 表, 586
事
事后调试, 514-515
事件
热插拔通知, 47 8
说明, 77-78
特性, 80-83
异步通知, 239-240
属
属性
class 属性, 336
ddi_prop_op, 7 4
LDI, 239
nblocks 属性, 309
no-involuntary-power-cycles, 217
pm-hardware-state 属性, 223, 227, 344
prtconf, 7 2
reg 属性, 223
removable-media, 215
SCSI HBA属性, 434
索引
615
属性(续)
SCSI 目标驱动程序, 435
size 属性, 27 3
报告设备属性, 7 4
概述, 44, 71
类型, 7 1
设备节点名称属性, 58
属性函数, 565-566
数
数据存储类, 61
数据共享
使用devmap(), 594
使用ioctl(), 593
使用mmap(), 594
数据结构
dev_ops 结构, 87-88
GLD, 446, 449-451
modldrv 结构, 87
数据损坏
恶性, 定义, 537
检测, 537-538
控制数据, 537-538
设备管理数据, 537-538
误导性, 定义, 537
已接收数据, 538
数据传输, 字符驱动程序, 27 7
锁
锁
处理, 567-568
读取器/写入器, 63
方案, 68
互斥锁, 61-62
锁定原语, 类型, 61
特
特殊文件, 说明, 51
条
条件变量
和互斥锁, 64
例程, 64
条件变量函数, 567-568
cv_broadcast(), 65
cv_destroy(), 64
cv_init(), 64
cv_timedwait(), 66
cv_timedwait_sig(), 68
cv_wait(), 65
cv_wait_sig(), 67
通
通用设备名称, 58
同
同步数据传输
USB, 471-472
块驱动程序, 318
字符驱动程序, 280
突
突发流量大小, DMA, 167
图
图形设备, 设备上下文管理, 193
脱
脱机, 47 8
外
外部寄存器, 555
索引
616 编写设备驱动程序• 2006 年11 月
网
网络驱动程序
测试, 504
使用GLD, 437-458
网络统计信息
DL_ETHER, 444
gld_stats, 444
gldm_get_stats(), 444
kstat, 443
伪
伪设备驱动程序, 33
文
文件系统I/O, 308
无
无标记排队, 436
物
物理DMA, 157
系
系统电源管理
USB 设备, 482-483
保存硬件状态, 223
策略, 223
模型, 222
入口点, 223
说明, 212
系统调用, 49
系统寄存器, 读取和写入, 519-521
系统全局状态函数, 585
线
线程, 抢占, 61
线程同步
mutex_init, 62
读取器/写入器锁, 63
互斥锁, 61-62
每实例互斥锁, 101
条件变量, 64-66
相
相关项, 215
消
消息告知中断, 已定义, 126
卸
卸载测试模块, 508-509
卸载驱动程序, 496
虚
虚拟DMA, 157
虚拟地址, 说明, 51
虚拟内存
地址空间, 51
内存管理单元(memory management unit,
MMU), 51
虚拟内存函数
表, 582-583
过时, 583
样
样式1 DLPI 提供者, 440
样式2 DLPI 提供者, 440
索引
617
叶
叶设备, 说明, 52
异
异步数据传输
USB, 471-472
块驱动程序, 323
字符驱动程序, 280
异步通信驱动程序, 测试, 503
引
引导kmdb 调试程序
在SPARC 系统中, 515
在x86 系统中, 515
引导替代内核, 510-512
硬
硬件配置文件, 491, 493
PCI 设备, 552
S 总线设备, 555
SCSI 目标设备, 336
放置位置, 495
硬件上下文, 193
用
用mdb 检测内核内存泄漏, 521-522
用户进程事件函数, 57 8
用户进程信息函数, 57 8
过时, 57 8
用户空间访问函数, 577-578
过时, 57 8
用户应用程序内核函数
表, 578-579
过时, 57 9
与
与时间有关的函数, 579-580
过时, 580
源
源代码兼容性, 说明, 51
灾
灾难恢复, 513-514
在
在测试时避免数据丢失, 510-513
中
中断
MSI-X 实现, 127
MSI-X 已定义, 126
MSI 实现, 127
MSI 已定义, 126
编写处理程序, 125-153
常见问题, 556
初始化和销毁函数, 128
处理低级中断示例, 151
处理高级别中断的示例, 145-153
高级互斥锁, 145
更改软中断优先级示例, 129
功能函数, 128
检查待处理中断示例, 129
类型, 125
清除中断掩码示例, 129
软中断函数, 129
删除MSI 中断示例, 141-142
删除传统中断示例, 136
设置中断掩码示例, 129
使用传统, 127
说明, 125
网络驱动程序, 442
消息告知已定义, 126
索引
618 编写设备驱动程序• 2006 年11 月
中断(续)
优先级别, 126
优先级管理函数, 129
有问题的中断, 538-539
中断处理示例, 143
注册MSI 中断, 137-142
注册MSI 中断示例, 137-141
注册传统中断, 132-136
注册传统中断示例, 132-136
传统函数和替换, 130
传统已定义, 126
中断处理, 125-153
ddi_add_intr() 函数, 131
gld_intr() 函数, 458
概述, 44
高级别中断, 126, 128, 145
软件中断, 127, 145
注册中断处理程序, 131
中断处理程序, 职责, 143
中断函数, 568-570
中断属性, 定义, 44
主
主机总线适配器传输层, 366
主设备号
示例, 309
说明, 51
传
传统中断
使用, 127
替换函数, 130
已定义, 126
装
装入测试模块, 508-509
装入模块, 35, 494-495
装入驱动程序
add_drv 命令, 495
编译驱动程序, 492-493
装入驱动程序(续)
链接驱动程序, 492-493
硬件配置文件, 493
状
状态结构, 45, 101, 241-258
资
资源映射函数, 585
字
字符设备驱动程序
aphysio() 函数, 285
cb_ops 结构, 89
close() 入口点, 27 7
I/O 控制机制, 295
minphys() 函数, 287
open() 入口点, 275-277
physio() 函数, 284
strategy() 入口点, 288
概述, 38-39
内存映射, 290
入口点, 27 2
设备轮询, 291
数据传输, 27 7
自动配置, 27 3
字节排序, 547
自
自标识设备, 549
自动关闭阈值, 222
自动配置
块设备, 309-311
字符设备, 27 3
SCSI 目标驱动程序, 340
例程, 35
自动请求检测模式, 357
自动向量化中断, 125
索引
619
总
总线
PCI 体系结构, 550
S 总线体系结构, 553
SCSI, 333
体系结构, 549
总线结点设备驱动程序, 说明, 52
总线主控器DMA, 156, 158
索引
620 编写设备驱动程序• 2006 年11 月