Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1315331
  • 博文数量: 554
  • 博客积分: 10425
  • 博客等级: 上将
  • 技术积分: 7555
  • 用 户 组: 普通用户
  • 注册时间: 2006-11-09 09:49
文章分类

全部博文(554)

文章存档

2012年(1)

2011年(1)

2009年(8)

2008年(544)

分类:

2008-04-11 15:35:12


Solaris 环境中的USB
第19 章• USB 驱动程序461
绑定客户机驱动程序
本节讨论如何将驱动程序绑定到设备,还讨论具有单个接口的设备和具有多个接口的设备
的兼容设备名称。
USB 设备如何显示在系统中
一个USB 设备可以支持多种配置。在任何给定时间,只有一种配置处于活动状态。活动配
置称为当前配置。
一种配置可以有多个接口。一种配置的所有接口同时处于活动状态。不同的接口可由不同
的设备驱动程序操作。
接口可以使用替代设置以不同的方式在主机系统中呈现自己。对于任何给定接口只能有一
种替代设置处于活动状态。
每种替代设置通过端点提供设备访问。每个端点都有特定用途。主机系统通过建立到端点
的通信通道来与设备通信。此通信通道称为管道。
USB 设备和Solaris 设备树
如果USB 设备具有一种配置、一个接口,没有设备类,则可将该设备表示为单个设备节
点。如果USB 设备具有多个接口,则可将该设备表示为分层设备结构。在分层设备结构
中,每个接口的设备节点是顶层设备节点的子节点。例如,音频设备即是具有多个接口的
设备,该设备向主机同时呈现为音频控制和音频流两种接口。音频控制接口和音频流接口
可以分别由各自的驱动程序控制。
兼容设备名称
Solaris 软件基于每个设备中存储的标识信息为USB 绑定生成有序的兼容设备名称列表。此
信息包括设备类、子类、供应商ID、产品ID、修订版和协议。有关USB 类和子类的列表,
请参见。
采用此名称分层结构,可以在没有特定于设备的驱动程序时,绑定到相对较常用的驱动程
序。特定于类的驱动程序即是常规驱动程序。以usbif 开头的设备名称指定单个接口的设
备。有关示例,请参见示例19–1。USBA2.0 框架定义设备的所有兼容名称。使用prtconf
命令可显示这些设备名称,如示例19–2 中所示。
以下示例显示了USB 鼠标设备的兼容设备名称。此鼠标设备表示完全由单个驱动程序操作
的组合节点。USBA2.0 框架为此设备节点指定了示例中所示的名称(按所示顺序)。
示例19–1USB鼠标的兼容设备名称
1. ’usb430,100.102’ Vendor 430, product 100, revision 102
2. ’usb430,100’ Vendor 430, product 100
绑定客户机驱动程序
462 编写设备驱动程序• 2006 年11 月
示例19–1 USB 鼠标的兼容设备名称(续)
3. ’usbif430,class3.1.2’ Vendor 430, class 3, subclass 1, protocol 2
4. ’usbif430,class3.1’ Vendor 430, class 3, subclass 1
5. ’usbif430,class3’ Vendor 430, class 3
6. ’usbif,class3.1.2’ Class 3, subclass 1, protocol 2
7. ’usbif,class3.1’ Class 3, subclass 1
8. ’usbif,class3’ Class 3
请注意,上面示例中的名称按从最具特殊性到最常规的顺序进行排列。第1 项仅绑定到特
定供应商的特定产品的特定修订版。第3、4 和5 项用于由供应商430 生产的类3 设备。第
6、7 和8 项用于任何供应商的类3 设备。绑定过程将按从上到下的顺序查找名称匹配项。
要进行绑定,必须将驱动程序添加到其别名与上述其中一个名称匹配的系统。要获取在添
加驱动程序时要绑定到的兼容设备名称的列表,请在prtconf -vp 命令的输出中检查设备的
compatible 属性。
以下示例显示了键盘和鼠标的兼容属性列表。使用prtconf -D 命令可显示绑定的驱动程
序。
示例19–2列显配置命令显示的兼容设备名称
# prtconf -vD | grep compatible
compatible: ’usb430,5.200’ + ’usb430,5’ + ’usbif430,class3.1.1’
+ ’usbif430,class3.1’ + ’usbif430,class3’ + ’usbif,class3.1.1’ +
’usbif,class3.1’ + ’usbif,class3’
compatible: ’usb2222,2071.200’ + ’usb2222,2071’ +
’usbif2222,class3.1.2’ + ’usbif2222,class3.1’ + ’usbif2222,class3’ +
’usbif,class3.1.2’ + ’usbif,class3.1’ + ’usbif,class3’
使用最具体的名称就能更准确地确定一个设备或一组设备的驱动程序。要绑定为特定产品
的特定修订版编写的驱动程序,请尽可能使用最具体的名称匹配项。例如,如果您有由供
应商为其产品100 的修订版102 编写的USB 鼠标驱动程序,则可以使用以下命令将该驱动程
序添加到系统中:
add_drv -n -i ’"usb430,100.102"’ specific_mouse_driver
绑定客户机驱动程序
第19 章• USB 驱动程序463
要添加为供应商430 的任何USB 鼠标(类3、子类1、协议2)编写的驱动程序,请使用以
下命令:
add_drv -n -i ’"usbif430,class3.1.2"’ more_generic_mouse_driver
如果安装这两个驱动程序并连接兼容设备,则系统会将正确的驱动程序绑定到所连接的设
备。例如,如果安装这两个驱动程序,并连接供应商430、型号100、修订版102 的设备,
则此设备将绑定到specific_mouse_driver。如果连接供应商430、型号98 的设备,则此设
备将绑定到more_generic_mouse_driver。如果连接其他供应商的鼠标,则此设备也将绑定
到more_generic_mouse_driver。如果有多个驱动程序可供特定设备使用,则驱动程序绑定
框架将选择与兼容名称列表中第一个兼容名称匹配的驱动程序。
具有多个接口的设备
复合设备是支持多个接口的设备。复合设备的每个接口都有一个兼容名称列表。此兼容名
称列表可确保将最有效的驱动程序绑定到该接口。最常规的多接口项是usb,device。
对于USB 音频复合设备,兼容名称如下:
1. ’usb471,101.100’ Vendor 471, product 101, revision 100
2. ’usb471,101’ Vendor 471, product 101
3. ’usb,device’ Generic USB device
名称usb,device 是表示任何整个USB 设备的兼容名称。如果没有其他驱动程序请求该整个
设备,则usb_mid(7D) 驱动程序(USB 多接口驱动程序)将绑定到usb,device 设备节点。
usb_mid 驱动程序为物理设备的每个接口创建一个子设备节点。usb_mid 驱动程序还为每个
接口生成一组兼容名称。生成的所有这些兼容名称都以usbif 开头。系统将使用生成的这些
兼容名称为每一个接口查找最佳的驱动程序。通过这种方法,可以将一个物理设备的不同
接口绑定到不同的驱动程序。
例如,usb_mid 驱动程序通过多接口音频设备的usb,device 节点名称绑定到该音频设备。然
后usb_mid 驱动程序创建特定于接口的设备节点。这些特定于接口的设备节点中的每个节点
都有各自的兼容名称列表。对于音频控制接口节点,兼容名称列表可能类似于下例中所示
的列表。
示例19–3USB音频兼容设备名称
1. ’usbif471,101.100.config1.0’ Vend 471, prod 101, rev 100, cnfg 1, iface 0
2. ’usbif471,101.config1.0’ Vend 471, product 101, config 1, interface 0
3. ’usbif471,class1.1.0’ Vend 471, class 1, subclass 1, protocol 0
4. ’usbif471,class1.1’ Vend 471, class 1, subclass 1
绑定客户机驱动程序
464 编写设备驱动程序• 2006 年11 月
示例19–3 USB 音频兼容设备名称(续)
5. ’usbif471,class1’ Vend 471, class 1
6. ’usbif,class1.1.0’ Class 1, subclass 1, protocol 0
7. ’usbif,class1.1’ Class 1, subclass 1
8. ’usbif,class1’ Class 1
使用以下命令可将特定于供应商、特定于设备的客户机驱动程序(名为
vendor_model_audio_usb)绑定到特定于供应商、特定于设备的配置1、接口0 的接口兼容
名称,如示例19–3 中所示。
add_drv -n -i ’"usbif471,101.config1.0"’ vendor_model_audio_usb
使用以下命令可将名为audio_class_usb_if_driver 的类驱动程序绑定到较常规的类1、子
类1 的接口兼容名称,如示例19–3 中所示:
add_drv -n -i ’"usbif,class1.1"’ audio_class_usb_if_driver
使用prtconf -D 命令可显示设备及其驱动程序的列表。在以下示例中,prtconf -D 命令显示
usb_mid 驱动程序管理audio 设备。usb_mid 驱动程序将audio 设备拆分为多个接口。每个接
口在audio 设备名称下以缩进方式列出。对于缩进列表中所示的每个接口,prtconf -D 命令
显示了哪个驱动程序管理该接口。
audio, instance #0 (driver name: usb_mid)
sound-control, instance #2 (driver name: usb_ac)
sound, instance #2 (driver name: usb_as)
input, instance #8 (driver name: hid)
检查设备驱动程序绑定
文件/etc/driver_aliases 包含对应于系统中已存在的绑定的项。/etc/driver_aliases 文
件的每一行都有一个驱动程序名称,后面依次跟随一个空格和一个设备名称。使用此文件
可检查现有的设备驱动程序绑定。
注– 请不要手动编辑/etc/driver_aliases 文件。使用add_drv(1M) 命令可建立绑定。使用
update_drv(1M) 命令可更改绑定。
绑定客户机驱动程序
第19 章• USB 驱动程序465
基本设备访问
本节介绍如何访问USB 设备以及如何注册客户机驱动程序。本节还将讨论描述符树。
连接客户机驱动程序之前
在连接客户机驱动程序之前发生下列事件:
1. PROM (OBP/BIOS) 和USBA框架在连接任何客户机驱动程序之前获取访问设备的权限。
2. 集线器驱动程序将在其集线器的每个端口上探测设备的标识和配置。
3. 将打开每个设备的缺省控制管道,并探测每个设备的设备描述符。
4. 使用设备和接口描述符为每个设备构建兼容名称属性。
兼容名称属性定义可单独绑定到客户机驱动程序的设备的不同部分。可以将客户机驱动程
序绑定到整个设备或仅绑定到一个接口。请参见第462 页中的“绑定客户机驱动程序”。
描述符树
解析描述符涉及在自然边界对齐结构成员,以及将结构成员转换为主机CPU 的字节序。解
析后的标准USB 配置描述符、接口描述符和端点描述符可用于每种配置的分层树格式的客
户机驱动程序。任何特定于原始类或特定于供应商的描述符信息也可用于同一分层树中的
客户机驱动程序。
调用usb_get_dev_data(9F) 函数可检索分层描述符树。usb_get_dev_data(9F) 手册页的“另请
参见”部分列出了每个标准USB 描述符的手册页。使用usb_parse_data(9F) 函数可解析原始
描述符信息。
具有两种配置的设备的描述符树可能与下图中所示的树类似。
基本设备访问
466 编写设备驱动程序• 2006 年11 月
cfg_if[0]
cfg_if[1]
if_alt[0]
if_alt[1]
if_alt[0]
if_alt[1]
cfg_if[0]
cfg_if[1]
dev_cfg[0]
dev_cfg[1]
altif_ep[0]
altif_ep[1]
altif_cvs[0]
altif_ep[0]
altif_cvs[0]
if_alt[0]
ep_cvs[0]
altif_ep[0]
if_alt[0]
altif_ep[0]
altif_cvs[0]
图19–3 分层USB 描述符树
上图中所示的dev_cfg 数组包含对应于相应配置的节点。每个节点包含以下信息:
 解析的配置描述符
 指向描述符数组的指针,这些描述符对应于该配置的接口
 指向特定于类或特定于供应商的原始数据数组(如果存在)的指针
表示第二个索引配置的第二个接口的节点位于图中的dev_cfg[1].cfg_if[1] 位置。该节点
包含表示该接口的替代设置的节点数组。USB 描述符的分层结构通过该树传播。字符串描
述符数据中的ASCII 字符串连接到USB 规范说明的存在这些字符串的位置。
配置数组是非稀疏数组,按配置索引进行索引。第一个有效配置(配置1)是dev_cfg[0]。
接口和替代设置具有与其编号对齐的索引。对于每个替代设置的端点,都以连续方式进行
索引。每个替代设置的第一个端点位于索引0 位置。
此编号方案使得很容易对树进行遍历。例如,端点索引为0、替代项为0、接口为1、配置
索引为1 的原始描述符数据位于以下路径定义的节点:
dev_cfg[1].cfg_if[1].if_alt[0].altif_ep[0].ep_descr
另一种直接使用描述符树的方法是使用usb_lookup_ep_data(9F) 函
数。usb_lookup_ep_data(9F) 函数采用接口、替代项、端点、端点类型和指令作为参数。您
可以使用usb_lookup_ep_data(9F) 函数遍历描述符树以获取特定端点。有关更多信息,请参
见usb_get_dev_data(9F) 手册页。
基本设备访问
第19 章• USB 驱动程序467
注册驱动程序以获取设备访问权限
在USBA2.0 框架中,客户机驱动程序执行的前两个调用是对usb_client_attach(9F) 函数和
usb_get_dev_data(9F) 函数的调用。这两个调用来自客户机驱动程序的attach(9E) 入口点。
在调用usb_get_dev_data(9F) 函数之前,必须先调用usb_client_attach(9F) 函数。
usb_client_attach(9F) 函数用于注册采用USBA2.0 框架的客户机驱动程
序。usb_client_attach(9F) 函数用于强制执行版本控制。所有客户机驱动程序源文件必须
使用下列行开头:
#define USBDRV_MAJOR_VER 2
#define USBDRV_MINOR_VER minor-version
#include
minor-version 的值必须小于或等于USBA_MINOR_VER。符号USBA_MINOR_VER 在
头文件中定义。 头文件通过 头文件
包括进来。
USBDRV_VERSION 是根据USBDRV_MAJOR_VERSION 和USBDRV_MINOR_VERSION 生成版本号的宏。
usb_client_attach() 的第二个参数必须是USBDRV_VERSION。如果第二个参数不是
USBDRV_VERSION,或者如果USBDRV_VERSION 反映的是无效的版本,则usb_client_attach()
函数将失败。此限制可确保编程接口兼容性。
usb_get_dev_data() 函数返回正确管理USB 设备所需的信息。例如,usb_get_dev_data()
函数返回以下信息:
 缺省控制管道
 互斥锁初始化中使用的iblock_cookie(请参见mutex_init(9F))
 解析的设备描述符
 ID 字符串
 第466 页中的“描述符树”中所述的树分层结构
必须调用usb_get_dev_data() 函数。调用usb_get_dev_data() 是检索缺省控制管道以及检
索互斥锁初始化所需的iblock_cookie 的唯一方法。
调用usb_get_dev_data() 后,客户机驱动程序的attach(9E) 例程通常会将所需的描述符和
数据从描述符树复制到驱动程序的软状态。复制到软状态的端点描述符在以后打开到这些
端点的管道时会用到。attach(9E) 例程通常在复制描述符后调用usb_free_descr_tree(9F)
以释放描述符树。或者,可以选择保留描述符树,且不复制描述符。
可以为usb_get_dev_data(9F) 函数指定以下三个解析级别之一,以请求想要返回的描述符树
的广度。如果驱动程序需要绑定到的对象不仅仅是设备,则需要更大的树广度。
 USB_PARSE_LVL_IF。如果客户机驱动程序绑定到特定接口,则驱动程序仅需要对应于该
接口的描述符。在usb_get_dev_data() 调用中指定USB_PARSE_LVL_IF 作为解析级别将仅
检索这些描述符。
 USB_PARSE_LVL_CFG。如果客户机驱动程序绑定到整个设备,请指定USB_PARSE_LVL_CFG
以检索当前配置的所有描述符。
基本设备访问
468 编写设备驱动程序• 2006 年11 月
 USB_PARSE_LVL_ALL。指定USB_PARSE_LVL_ALL 可检索所有配置的所有描述符。例如,要
使用usb_print_descr_tree(9F) 列显设备的所有配置的描述符转储,需要此最大的树广
度。
客户机驱动程序的detach(9E) 例程必须调用usb_free_dev_data(9F) 函数以释放由
usb_get_dev_data() 函数分配的所有资源。usb_free_dev_data() 函数接受已使用
usb_free_descr_tree() 函数释放了描述符树的句柄。客户机驱动程序的detach() 例程还必
须调用usb_client_detach(9F) 函数以释放由usb_client_attach(9F) 函数分配的所有资源。
设备通信
USB 设备的工作方式是通过称为管道的通信通道来传递请求。只有管道处于打开状态时您
才能提交请求。也可以刷新、查询和关闭管道。本节讨论管道、数据传输和回调以及数据
请求。
USB 端点
与四种类型的USB 端点通信的四种类型的管道包括:
 控制。控制管道主要用于发送命令和检索状态。控制管道适用于小型结构化数据的非定
期、主机启动的请求和响应通信。控制管道是双向的。缺省管道为控制管道。请参见第
469 页中的“缺省管道”。
 批量传输。批量传输管道主要用于数据传输。批量传输管道可实现大量数据的可靠传
输。批量传输管道并不一定会进行数据的及时传输。批量传输管道是单向的。
 中断。中断管道可为少量的非结构数据提供及时而可靠的通信。通常,会对中断输入管
道启动定期轮询。当设备中存在数据时,中断输入管道将数据返回到主机。一些设备具
有中断输出管道。中断输出管道采用与中断输入管道相同的及时而可靠的“中断管道”特
性将数据传输到设备。中断管道是单向的。
 等时。等时管道为传输速率恒定、与时间相关的数据(如音频设备数据)提供通道。出
现错误时不会尝试重新传输数据。等时管道是单向的。
有关对应于这些端点的传输类型的更多信息,请参见USB 2.0 规范的第5 章或参见第472 页
中的“请求”。
缺省管道
每个USB 设备都有称为缺省端点的特殊控制端点。其通信通道称为缺省管道。大多数(可
能并非所有)设备的设置都通过此管道进行。许多USB 设备使用此管道作为其唯一的控制
管道。
usb_get_dev_data(9F) 函数为客户机驱动程序提供缺省控制管道。此管道将会被预先打开以
适应在打开其他管道之前需要的任何特殊设置。此缺省控制管道的特殊性表现在以下方面

设备通信
第19 章• USB 驱动程序469
 此管道是共享的。操作同一设备其他接口的驱动程序使用相同的缺省控制管道。
USBA2.0 框架仲裁此管道在不同驱动程序之间的使用。
 此管道不能由客户机驱动程序打开、关闭或重置。之所以存在此限制,是因为管道是共
享的。
 出现异常时将会自动清除此管道。
其他管道(包括其他控制管道)必须明确打开且仅限独占打开。
管道状态
管道处于以下状态之一:
 USB_PIPE_STATE_IDLE
 所有控制管道、批量传输管道、中断输出管道和等时输出管道:没有正在进行的请
求。
 中断输入管道和等时输入管道:没有正在进行的轮询。
 USB_PIPE_STATE_ACTIVE
 所有控制管道、批量传输管道、中断输出管道和等时输出管道:管道正在传输数据
或I/O 请求处于活动状态。
 中断输入管道和等时输入管道:轮询处于活动状态。
 USB_PIPE_STATE_ERROR。出现错误。如果此管道不是缺省管道,而且未启用自动清除,
则客户机驱动程序必须调用usb_pipe_reset(9F) 函数。
 USB_PIPE_STATE_CLOSING。正在关闭管道。
 USB_PIPE_STATE_CLOSED。已关闭管道。
调用usb_pipe_get_state(9F) 函数可获取管道的状态。
打开管道
要打开管道,请将对应于要打开的管道的端点描述符传递给usb_pipe_open(9F) 函数。使用
usb_get_dev_data(9F) 和usb_lookup_ep_data(9F) 函数可检索描述符树的端点描述
符。usb_pipe_open(9F) 函数将句柄返回给管道。
打开管道时必须指定管道策略。管道策略包含并发异步操作的估计数量,这些并发异步操
作要求使用此管道将需要的独立线程。线程的估计数量是回调期间可能发生的并行操作的
数量。此估计值必须至少为2。有关管道策略的更多信息,请参见usb_pipe_open(9F) 手册
页。
设备通信
470 编写设备驱动程序• 2006 年11 月
关闭管道
驱动程序必须使用usb_pipe_close(9F) 函数关闭缺省管道之外的管道。usb_pipe_close(9F)
函数使管道中的所有剩余请求得以完成。然后该函数还留出一秒钟的时间让这些请求的所
有回调得以完成。
数据传输
对于所有管道类型,编程模型如下:
1. 分配请求。
2. 使用管道传输函数之一提交该请求。请参见
usb_pipe_bulk_xfer(9F)、usb_pipe_ctrl_xfer(9F)、usb_pipe_intr_xfer(9F) 和
usb_pipe_isoc_xfer(9F) 手册页。
3. 等待完成通知。
4. 释放请求。
有关请求的更多信息,请参见第472 页中的“请求”。以下各节介绍各种请求类型的特
性。
同步传输、异步传输和回调
传输分为同步传输和异步传输。同步传输在完成之前将会一直阻塞。异步传输完成时,将
向客户机驱动程序发送回调。在flags 参数中设置了USB_FLAGS_SLEEP 标志时调用的传输函数
大多数是同步的。
连续传输(如轮询)和等时传输不能是同步的。为了进行连续传输而对传输函数进行的调
用(设置了USB_FLAGS_SLEEP 标志)将会阻塞,目的只是等待资源,然后开始传输。
同步传输是要设置的最简单的传输,因为同步传输不要求任何回调函数。同步传输函数将
返回传输开始状态,即使同步传输函数在完成传输前一直阻塞也是如此。完成时,可以在
请求的完成原因字段和回调标志字段中查找有关传输状态的其他信息。下面将讨论完成原
因字段和回调标志字段。
如果未在flags 参数中指定USB_FLAGS_SLEEP 标志,则该传输操作是异步的。此规则的例外是
等时传输。异步传输操作将设置并启动传输,然后在传输完成前返回。异步传输操作将返
回传输开始状态。客户机驱动程序通过回调处理程序接收传输完成状态。
回调处理程序是在异步传输完成时调用的函数。不要设置不进行回调的异步传输。两种类
型的回调处理程序是正常完成处理程序和异常处理程序。您可以指定一个在这两种情况下
要调用的处理程序。
 正常完成。可调用正常完成回调处理程序以通知传输正常完成。
 异常。可调用异常回调处理程序以通知传输未正常完成,然后对错误进行处理。
完成处理程序和异常处理程序将传输请求作为参数接收。异常处理程序在请求中使用完成
原因和回调状态来了解所发生的情况。完成原因(usb_cr_t) 指示原始事务是如何完成的。
例如,完成原因USB_CR_TIMEOUT 指示传输超时。又如,如果USB 设备在使用时被移除,则
设备通信
第19 章• USB 驱动程序471
客户机驱动程序可能接收USB_CR_DEV_NOT_RESP 作为其未完成请求的完成原因。回调状态
(usb_cb_flags_t) 指示USBA框架为修正这种情况所执行的操作。例如,回调状态
USB_CB_STALL_CLEARED 指示USBA框架清除了运行延迟条件。有关完成原因的更多信息,请
参见usb_completion_reason(9S) 手册页。有关回调状态标志的更多信息,请参见
usb_callback_flags(9S) 手册页。
运行请求的回调上下文和管道策略会对在回调中可以执行的操作实施一些限制。
 回调上下文。大多数回调在内核上下文中执行,通常可以阻塞。一些回调在中断上下文
中执行,它们不能阻塞。可在回调标志中设置USB_CB_INTR_CONTEXT 标志以表示中断上
下文。有关回调上下文的更多信息和有关阻塞的详细信息,请参见
usb_callback_flags(9S) 手册页。
 管道策略。管道策略对并发异步操作的提示限制可以并行运行的操作数,包括通过回调
处理程序执行的操作。阻塞同步操作将计为一个操作。有关管道策略的更多信息,请参
见usb_pipe_open(9F) 手册页。
请求
本节讨论请求结构,以及分配和取消分配不同类型的请求。
分配和取消分配请求
请求以初始化的请求结构的形式实现。每种不同的端点类型接受不同类型的请求。每种类
型的请求有不同的请求结构类型。下表显示了每种类型请求的结构类型。此表还列出了可
用于分配和释放每种类型结构的函数。
表19–1请求初始化
管道或端点类型请求结构请求结构分配函数请求结构释放函数
控制usb_ctrl_req_t(请参见
usb_ctrl_request(9S) 手册
页)
usb_alloc_ctrl_req(9F) usb_free_ctrl_req(9F)
批量传输usb_bulk_req_t(请参见
usb_bulk_request(9S) 手册
页)
usb_alloc_bulk_req(9F) usb_free_bulk_req(9F)
中断usb_intr_req_t(请参见
usb_intr_request(9S) 手册
页)
usb_alloc_intr_req(9F) usb_free_intr_req(9F)
等时usb_isoc_req_t(请参见
usb_isoc_request(9S) 手册
页)
usb_alloc_isoc_req(9F) usb_free_isoc_req(9F)
下表列出了可用于每种类型请求的传输函数。
设备通信
472 编写设备驱动程序• 2006 年11 月
表19–2请求传输设置
管道或端点类型传输函数
控制usb_pipe_ctrl_xfer(9F)、usb_pipe_ctrl_xfer_wait(9F)
批量传输usb_pipe_bulk_xfer(9F)
中断usb_pipe_intr_xfer(9F)、usb_pipe_stop_intr_polling(9F)
等时usb_pipe_isoc_xfer(9F)、usb_pipe_stop_isoc_polling(9F)
分配和取消分配请求的过程如下:
1. 使用相应的分配函数为所需的请求类型分配请求结构。表19–1 中列出了请求结构分配
函数的手册页。
2. 初始化结构中所需的任何字段。有关更多信息,请参见第473 页中的“请求特性和字段
”或相应的请求结构手册页。表19–1 中列出了请求结构的手册页。
3. 完成数据传输时,使用相应的释放函数释放请求结构。表19–1 中列出了请求结构释放
函数的手册页。
请求特性和字段
所有请求的数据都以消息块的形式传递,这样,无论驱动程序是STREAMS 驱动程序、字符
驱动程序还是块驱动程序,都将统一地对数据进行处理。消息块类型mblk_t 在mblk(9S) 手
册页中进行了介绍。DDI 提供了多个用于处理消息块的例程。这样的例程包括allocb(9F)
和freemsg(9F)。要了解用于处理消息块的其他例程,请参见allocb(9F) 和freemsg(9F) 手册
页的“另请参见”部分。此外,还可以参见《STREAMS Programming Guide》。
所有传输类型中都包括以下请求字段。在每个字段名称中,xxxx 的可能值包括:ctrl、
bulk、intr 或isoc。
xxxx_client_private 此字段值是一个指针,适用于将与请求一起在客户机驱动程序
中传递的内部数据。此指针不用于将数据传输到设备。
xxxx_attributes 此字段值是一组传输属性。虽然此字段对所有请求结构通用,
但对于每种传输类型,此字段的初始化稍有不同。有关更多信
息,请参见相应的请求结构手册页。表19–1 中列出了这些手册
页。另请参见usb_request_attributes(9S) 手册页。
xxxx_cb 此字段值是正常传输完成的回调函数。如果异步传输在没有错
误的情况下完成,则会调用此函数。
xxxx_exc_cb 此字段值是错误处理的回调函数。仅当异步传输完成且出现错
误时,才会调用此函数。
设备通信
第19 章• USB 驱动程序473
xxxx_completion_reason 此字段存储传输本身的完成状态。如果出现错误,此字段将显
示出现错误的内容。有关更多信息,请参见
usb_completion_reason(9S) 手册页。此字段由USBA2.0 框架更
新。
xxxx_cb_flags 此字段列出在调用回调处理程序之前USBA2.0 框架所采取的恢
复操作。USB_CB_INTR_CONTEXT 标志指示回调是否在中断上下文
中运行。有关更多信息,请参见usb_callback_flags(9S) 手册
页。此字段由USBA2.0 框架更新。
以下各节介绍针对四种不同传输类型的不同请求字段。其中介绍如何初始化这些结构字
段,还介绍有关属性和参数的各种组合的限制。
控制请求
使用控制请求可沿控制管道向下启动消息传输。您可以手动设置传输,如下所示。也可以
使用usb_pipe_ctrl_xfer_wait(9F) 包装函数设置并发送同步传输。
客户机驱动程序必须初始化ctrl_bmRequestType、ctrl_bRequest、ctrl_wValue、ctrl_wIndex 和
ctrl_wLength 字段,如USB 2.0 规范中所述。
必须将请求的ctrl_data 字段初始化为指向数据缓冲区。将一个正值作为缓冲区len 传递
时,usb_alloc_ctrl_req(9F) 函数将初始化此字段。当然,还必须初始化缓冲区以便进行任
何外发传输。在所有情况下,完成传输时,客户机驱动程序必须释放请求。
可以对多个控制请求进行排队。排队的请求中可以包括同步请求和异步请求。
ctrl_timeout 字段定义等待要被处理的请求的最长时间,但不包括在队列中的等待时间。此
字段适用于同步和异步请求。ctrl_timeout 字段中指定的值以秒为单位。
如果出现异常,ctrl_exc_cb 字段接受要调用的函数的地址。usb_ctrl_request(9S) 手册页中
指定了此异常处理程序的参数。异常处理程序的第二个参数是usb_ctrl_req_t 结构。通过
将请求结构作为参数传递,异常处理程序可以检查该请求的ctrl_completion_reason 和
ctrl_cb_flags 字段,以确定最佳恢复操作。
USB_ATTRS_ONE_XFER 和USB_ATTRS_ISOC_* 标志对所有控制请求而言都是无效属性。
USB_ATTRS_SHORT_XFER_OK 标志仅对主机绑定的请求有效。
批量传输请求
使用批量传输请求可发送非时间关键数据。批量传输请求可以接纳多个要完成的USB 帧,
具体取决于总体总线负载。
所有请求必须接收已初始化的消息块。有关mblk_t 消息块类型的说明,请参见mblk(9S) 手
册页。此消息块将提供数据或存储数据,具体取决于传输方向。有关更多详细信息,请参
阅usb_bulk_request(9S) 手册页。
USB_ATTRS_ONE_XFER 和USB_ATTRS_ISOC_* 标志对所有批量传输请求而言都是无效属性。
USB_ATTRS_SHORT_XFER_OK 标志仅对主机绑定的请求有效。
设备通信
474 编写设备驱动程序• 2006 年11 月
usb_pipe_get_max_bulk_transfer_size(9F) 函数指定每个请求的最大字节数。检索到的值可
以是客户机驱动程序的minphys(9F) 例程中使用的最大值。
可以对多个批量传输请求进行排队。
中断请求
中断请求通常用于定期传入数据。中断请求定期在设备中轮询数据。但是,USBA2.0 框架
支持一次性的传入中断数据请求以及外发中断数据请求。所有中断请求可以利用USB 中断
传输的及时与重试特性。
USB_ATTRS_ISOC_* 标志对所有中断请求而言都是无效属性。USB_ATTRS_SHORT_XFER_OK 和
USB_ATTRS_ONE_XFER 标志仅对主机绑定的请求有效。
只有一次性轮询可以作为同步中断传输执行。如果在请求中指定USB_ATTRS_ONE_XFER 属
性,将进行一次性轮询。
定期轮询是作为异步中断传输启动。原始中断请求将被传递到usb_pipe_intr_xfer(9F)。当
轮询操作找到要返回的新数据时,将从原始请求克隆一个新的usb_intr_req_t 结构,并使
用已初始化的数据块填充该结构。分配请求时,请为usb_alloc_intr_req(9F) 函数的len 参
数指定零。len 参数为零是因为USBA2.0 框架将为每个回调分配新请求,并填充该请求。分
配请求结构后,请填充intr_len 字段,以指定希望框架为每个轮询分配的字节数。将不会返
回超出intr_len 字节的数据。
客户机驱动程序必须释放它接收的每个请求。如果逆向发送消息块,请在逆向发送消息块
之前从请求中分离该消息块。要从请求中分离消息块,请将该请求的数据指针设置为
NULL。将请求的数据指针设置为NULL,可在取消分配请求时阻塞释放消息块。
调用usb_pipe_stop_intr_polling(9F) 函数可取消定期轮询。停止轮询或关闭管道时,将通
过异常回调返回原始请求结构。此返回的请求结构的完成原因将被设置为
USB_CR_STOPPED_POLLING。
请不要在轮询进行过程中启动轮询。也不要在调用usb_pipe_stop_intr_polling(9F) 的过程
中启动轮询。
等时请求
等时请求用于速率恒定、与时间相关的流数据。出现错误时不会进行重试。等时请求具有
以下特定于请求的字段:
设备通信
第19 章• USB 驱动程序475
isoc_frame_no 当必须从特定的帧编号启动总体传输时,请指定此字段。此字段的值必
须大于当前帧编号。使用usb_get_current_frame_number(9F) 可查找当前
帧编号。请注意,当前帧编号为活动目标。对于低速和全速总线,将每1
毫秒更新一次当前帧。对于高速总线,将每0.125 毫秒更新一次当前帧。
应设置USB_ATTR_ISOC_START_FRAME 属性以便可以识别isoc_frame_no 字
段。
要忽略此帧编号字段并尽快启动,请设置USB_ATTR_ISOC_XFER_ASAP 标
志。
isoc_pkts_count 此字段是请求中的包数。此值受由
usb_get_max_pkts_per_isoc_request(9F) 函数返回的值和isoc_pkt_descr
数组(参见下面的内容)的大小限制。该请求中可传输的字节数等于此
isoc_pkts_count 值与端点的wMaxPacketSize 值的乘积。
isoc_pkts_length 此字段是请求的所有包的长度之和。此值由启动器设置。
isoc_error_count 此字段是完成时出现错误的包数。此值由USBA2.0 框架设置。
isoc_pkt_descr 此字段指向定义每个包传输数据量的包描述符的数组。对于传出请求,
此值定义要处理的子请求的专用队列。对于传入请求,此值描述数据块
的到达方式。客户机驱动程序将为传出请求分配这些描述符。框架将为
传入请求分配和初始化这些描述符。此数组中的描述符包含框架初始化
的字段,这些字段存储实际传输的字节数和传输的状态。有关更多详细
信息,请参见usb_isoc_request(9S) 手册页。
所有请求必须接收已初始化的消息块。此消息块提供数据或存储数据。有关mblk_t 消息块
类型的说明,请参见mblk(9S) 手册页。
USB_ATTR_ONE_XFER 标志是非法属性,因为系统将决定如何通过可用包数改变数据量。
USB_ATTR_SHORT_XFER_OK 标志仅对主机绑定的数据有效。
usb_pipe_isoc_xfer(9F) 函数可使所有等时传输变为异步传输,无论是否设置了
USB_FLAGS_SLEEP 标志。所有等时输入请求都将启动轮询。
调用usb_pipe_stop_isoc_polling(9F) 函数可取消定期轮询。停止轮询或关闭管道时,将通
过异常回调返回原始请求结构。此返回的请求结构的完成原因将被设置为
USB_CR_STOPPED_POLLING。
轮询会一直继续,直到发生以下某个事件:
 收到usb_pipe_stop_isoc_polling(9F) 调用。
 通过异常回调报告设备断开连接。
 收到usb_pipe_close(9F) 调用。
刷新管道
您可能需要在出现错误后清理管道,或者可能想要等待管道清除。可使用下列方法之一刷
新或清除管道:
设备通信
476 编写设备驱动程序• 2006 年11 月
 usb_pipe_reset(9F) 函数重置管道并刷新其所有请求。如果未对处于错误状态的管道启
用自动清除,则对这些管道执行上述操作。使用usb_pipe_get_state(9F) 可确定管道的
状态。
 usb_pipe_drain_reqs(9F) 函数将阻塞以等待所有挂起的请求完成,然后继续。此函数可
以无限期等待,也可以在指定的时间段后超时。usb_pipe_drain_reqs(9F) 函数既不关闭
管道也不刷新管道。
设备状态管理
管理USB 设备具体涉及到热插拔、系统电源管理(检查点和恢复)以及设备电源管理这几
方面。所有客户机驱动程序应实现下图中所示的基本状态机。有关更多信息,请参见
/usr/include/sys/usb/usbai.h。
图19–4USB设备状态机
可以使用特定于驱动程序的状态扩充此状态机及其四种状态。可以定义设备状态0x80 到
0xff,且只有客户机驱动程序可以使用这些状态。
热插拔USB 设备
USB 设备支持热插拔。可以随时插入或移除USB 设备。客户机驱动程序必须处理打开的设
备的移除和重新插入。使用热插拔回调可处理打开的设备。关闭的设备的插入和移除由
attach(9E) 和detach(9E) 入口点处理。
设备状态管理
第19 章• USB 驱动程序477
热插拔回调
USBA2.0 框架支持以下事件通知:
 在热移除设备时,客户机驱动程序将收到回调。
 在热移除之后放回设备时,客户机驱动程序将收到回调。如果未使设备的驱动程序实例
脱机,则当用户将设备放回其原始端口时,可能会发生此事件回调。如果驱动程序实例
保持打开状态,则不能使该驱动程序实例脱机。
客户机驱动程序必须在其attach(9E) 例程中调用usb_register_hotplug_cbs(9F) 以注册事件
回调。在注销之前,驱动程序必须在其detach(9E) 例程中调用
usb_unregister_hotplug_cbs(9F)。
热插入
USB 设备的热插入的事件顺序如下:
1. 集线器驱动程序hubd(7D) 等待端口连接状态发生变化。
2. hubd 驱动程序检测到端口连接。
3. hubd 驱动程序枚举设备,创建子设备节点,然后连接客户机驱动程序。有关兼容名称的
定义,请参阅第462 页中的“绑定客户机驱动程序”。
4. 客户机驱动程序管理设备。驱动程序处于ONLINE 状态。
热移除
USB 设备的热移除的事件顺序如下:
1. 集线器驱动程序hubd(7D) 等待端口连接状态发生变化。
2. hubd 驱动程序检测到端口断开连接。
3. hubd 驱动程序将断开连接事件发送到子客户机驱动程序。如果子客户机驱动程序是hubd
驱动程序或usb_mid(7D) 多接口驱动程序,则子客户机驱动程序将该事件传播到其子
级。
4. 客户机驱动程序在内核线程上下文中接收断开连接事件通知。内核线程上下文使驱动程
序的断开连接处理程序进入阻塞状态。
5. 客户机驱动程序将转为DISCONNECTED 状态。未完成的I/O 传输将失败,完成原因为
device not responding。所有新I/O 传输以及打开设备节点的尝试也将失败。要关闭管
道,不需要客户机驱动程序。而要保存设备以及重新连接设备时需要恢复的驱动程序上
下文,需要客户机驱动程序。
6. hubd 驱动程序试图按照从下到上的顺序使OS 设备节点及其子节点脱机。
如果在hubd 驱动程序试图使设备节点脱机时,未打开该设备节点,则会发生以下事件:
1. 将调用客户机驱动程序的detach(9E) 入口点。
2. 销毁设备节点。
3. 新设备可以使用相应端口。
4. 重新开始热插拔事件序列。hubd 驱动程序等待端口连接状态发生变化。
设备状态管理
478 编写设备驱动程序• 2006 年11 月
如果在hubd 驱动程序试图使设备节点脱机时,已打开该设备节点,则会发生以下事件:
1. hubd 驱动程序将脱机请求放入定期脱机重试队列。
2. 新设备仍然不可使用相应端口。
如果在hubd 驱动程序试图使设备节点脱机时,已打开该设备节点,但用户稍后关闭了该设
备节点,则hubd 驱动程序定期使该设备节点脱机将成功,且会发生以下事件:
1. 将调用客户机驱动程序的detach(9E) 入口点。
2. 销毁设备节点。
3. 新设备可以使用相应端口。
4. 重新开始热插拔事件序列。hubd 驱动程序等待端口连接状态发生变化。
如果用户关闭使用该设备的所有应用程序,则端口将重新变为可用。如果应用程序未终止
或未关闭该设备,则端口仍然不可用。
热重新插入
如果将先前移除的设备重新插入同一端口,同时该设备的设备节点仍处于打开状态,则会
发生以下事件:
1. 集线器驱动程序hubd(7D) 检测到端口连接。
2. hubd 驱动程序恢复总线地址和设备配置。
3. hubd 驱动程序取消脱机重试请求。
4. hubd 驱动程序将连接事件发送到客户机驱动程序。
5. 客户机驱动程序收到连接事件。
6. 客户机驱动程序确定新设备是否与先前连接的设备相同。客户机驱动程序首先通过比较
设备描述符来进行此项确定。客户机驱动程序也可以比较序列号和配置描述符群。
如果客户机驱动程序确定当前设备与先前连接的设备不同,则可能会发生以下事件:
1. 客户机驱动程序可能向控制台发出警告消息。
2. 用户可能再次移除该设备。如果用户再次移除该设备,则将重新开始热移除事件序列。
hubd 驱动程序检测到端口断开连接。如果用户没有再次移除该设备,则会发生以下事件

a. 客户机驱动程序仍然保持DISCONNECTED 状态,所有请求和打开操作将失败。
b. 端口仍然不可用。用户必须关闭设备并断开其连接以释放端口。
c. 释放端口时,将重新开始热插拔事件序列。hubd 驱动程序等待端口连接状态发生变
化。
如果客户机驱动程序确定当前设备与先前连接的设备相同,则可能会发生以下事件:
1. 客户机驱动程序可能恢复其状态,并继续正常操作。此策略由客户机驱动程序负责。音
频扬声器就是客户机驱动程序可继续操作的典型示例。
2. 如果使用重新连接的设备继续操作是安全的,则将重新开始热插拔事件序列。hubd 驱动
程序等待端口连接状态发生变化。设备再次可用。
设备状态管理
第19 章• USB 驱动程序479
电源管理
本节讨论设备电源管理和系统电源管理。
设备电源管理根据各个USB 设备的I/O 是处于活动状态还是空闲状态来管理这些设备。
系统电源管理使用检查点和恢复机制在文件中设置系统状态的检查点,然后完全关闭系
统。(检查点有时称为“系统暂停”。)再次打开系统电源时,系统将恢复为其暂停前的状
态。
设备电源管理
下面简要列出了要对USB 设备进行电源管理时驱动程序需要执行的操作。后面对电源管理
进行了较详细的说明。
1. 在attach(9E) 期间创建电源管理组件。请参见usb_create_pm_components(9F) 手册页。
2. 实现power(9E) 入口点。
3. 在访问设备之前调用pm_busy_component(9F) 和pm_raise_power(9F)。
4. 完成设备访问后调用pm_idle_component(9F)。
USBA2.0 框架支持USB 接口电源管理规范指定的四种电源级别。有关USB 电源级别与操作
系统电源级别对应关系的信息,请参见/usr/include/sys/usb/usbai.h。
当设备进入USB_DEV_OS_PWR_OFF 状态时,hubd 驱动程序将暂停端口。当设备进入
USB_DEV_OS_PWR_1 及以上状态时,hubd 驱动程序将恢复端口。请注意,端口暂停不同于系
统暂停。端口暂停时,将仅关闭USB 端口。第482 页中的“系统电源管理”中定义了系统
暂停。
客户机驱动程序可以选择在设备上启用远程唤醒。请参见usb_handle_remote_wakeup(9F) 手
册页。hubd 驱动程序在端口上发现远程唤醒时,就将完成唤醒操作,并调用
pm_raise_power(9F) 以通知子级。
下图显示了电源管理的不同部分之间的关系。
设备状态管理
480 编写设备驱动程序• 2006 年11 月
图19–5USB电源管理
驱动程序可以实现图19–5 底部说明的两种电源管理方案之一。被动方案比主动方案简单,
这是因为被动方案在设备传输期间不进行电源管理。
主动电源管理
本节介绍实现主动电源管理方案需使用的函数。
在驱动程序的attach(9E) 入口点执行以下工作:
1. 调用usb_create_pm_components(9F)。
2. 可选择调用usb_handle_remote_wakeup(9F)(使用USB_REMOTE_WAKEUP_ENABLE 作为第二
个参数),以在设备上启用远程唤醒。
3. 调用pm_busy_component(9F)。
4. 调用pm_raise_power(9F) 以使功耗达到USB_DEV_OS_FULL_PWR 级别。
5. 与设备通信以初始化该设备。
6. 调用pm_idle_component(9F)。
在驱动程序的detach(9E) 入口点执行以下工作:
设备状态管理
第19 章• USB 驱动程序481
1. 调用pm_busy_component(9F)。
2. 调用pm_raise_power(9F) 以使功耗达到USB_DEV_OS_FULL_PWR 级别。
3. 如果在attach(9E) 入口点中调用了usb_handle_remote_wakeup(9F) 函数,请在此处调用
usb_handle_remote_wakeup(9F)(使用USB_REMOTE_WAKEUP_DISABLE 作为第二个参数)。
4. 与设备通信以干净地关闭该设备。
5. 调用pm_lower_power(9F) 以使功耗达到USB_DEV_OS_PWR_OFF 级别。
这是唯一一次客户机驱动程序调用pm_lower_power(9F)。
6. 调用pm_idle_component(9F)。
当驱动程序线程要启动在设备上执行I/O 操作时,该线程将执行以下任务:
1. 调用pm_busy_component(9F)。
2. 调用pm_raise_power(9F) 以使功耗达到USB_DEV_OS_FULL_PWR 级别。
3. 开始I/O 传输。
当驱动程序收到I/O 传输已完成的通知时,驱动程序将调用pm_idle_component(9F)。
在驱动程序的power(9E) 入口点中,检查您要转到的电源级别是否有效。此外,还可能需要
考虑同时调用power(9E) 的不同线程。
如果设备已空闲一段时间或者系统正在关闭,则可以调用power(9E) 例程以使设备进入
USB_DEV_OS_PWR_OFF 状态。此状态对应于图19–4 中所示的PWRED_DWN 状态。如果设备将进
入USB_DEV_OS_PWR_OFF 状态,请在power(9E) 例程中执行以下工作:
1. 使所有打开的管道进入空闲状态。例如,停止对中断管道进行的轮询。
2. 保存任何设备或需要保存的驱动程序上下文。
在完成power(9E) 调用后,将暂停设备所连接到的端口。
收到设备启动的远程唤醒或系统启动的唤醒时,可以调用power(9E) 例程以打开设备电源。
由于超出空闲时间或系统暂停而关闭设备电源后,将会发生唤醒通知。如果设备将进入
USB_DEV_OS_PWR_1 或以上状态,请在power(9E) 例程中执行以下工作:
1. 恢复任何所需的设备和驱动程序上下文。
2. 在管道中重新启动适合指定电源级别的活动。例如,对中断管道启动轮询。
如果先前暂停了设备所连接到的端口,则在调用power(9E) 之前将恢复该端口。
被动电源管理
被动电源管理方案比上面介绍的主动电源管理方案简单。在此被动方案中,在传输期间不
执行任何电源管理。要实现此被动方案,请在打开设备时调用pm_busy_component(9F) 和
pm_raise_power(9F)。然后在关闭设备时调用pm_idle_component(9F)。
系统电源管理
系统电源管理包括:在保存整个系统的状态后关闭系统,以及在重新打开系统后恢复状
态。此过程称为CPR(checkpoint and resume,检查点和恢复)。在CPR 相关方面,USB 客
户机驱动程序的运行方式与其他客户机驱动程序相同。要暂停设备,请在cmd 参数为
设备状态管理
482 编写设备驱动程序• 2006 年11 月
DDI_SUSPEND 的情况下调用驱动程序的detach(9E) 入口点。要恢复设备,请在cmd 参数为
DDI_RESUME 的情况下调用驱动程序的attach(9E) 入口点。处理detach(9E) 例程中的
DDI_SUSPEND 命令时,请尽可能地清理设备状态和驱动程序状态,以满足后面清理恢复操作
的需要。(请注意,这对应于图19–4 中的SUSPENDED 状态。)处理attach(9E) 例程中的
DDI_RESUME 命令时,务必使设备达到全功率状态,以使设备与系统同步。
对于USB 设备,暂停和恢复的处理与热插拔断开连接和重新连接类似(请参见第477 页中
的“热插拔USB 设备”)。CPR 与热插拔之间的重要差别是,在CPR 的情况下,如果设备
处于不可暂停的状态,驱动程序的检查点过程可能会失败。例如,如果设备正在进行错误
恢复,则无法暂停设备。如果设备正忙,无法安全将其停止,也无法暂停该设备。
序列化
通常,驱动程序在持有互斥锁时不应调用USBA函数。因此,客户机驱动程序中的竞态条
件可能很难防止。
不允许在处理异步事件(如断开连接或CPR)的同时运行正常操作代码。这些类型的异步
事件通常会清理和中断管道,可能会破坏正常操作代码。
一种管理竞态条件和保护正常操作代码的方法是,编写可以获取和释放独占访问同步对象
的序列化工具。您可以按以下方法编写序列化工具:通过调用USBA函数安全地持有同步
对象。usbskel 驱动程序样例中就采用了这种方法。有关usbskel 驱动程序的信息,请参见
第486 页中的“USB 设备驱动程序样例”。
实用程序函数
本节介绍几个常规用途的函数。
设备配置工具
本节介绍与设备配置相关的函数。
获取接口编号
如果您使用的是多接口设备,usb_mid(7D) 驱动程序只会使其接口之一可用于调用驱动程
序,此时您可能需要知道调用驱动程序所绑定到的接口的编号。使用
usb_get_if_number(9F) 函数执行以下任一任务:
 返回调用驱动程序所绑定到的接口的编号。在这种情况下,usb_get_if_number(9F) 函数
返回大于零的接口编号。
 发现调用驱动程序管理整个多接口设备。驱动程序在设备级别绑定,因此usb_mid 没有
拆分设备。在这种情况下,usb_get_if_number(9F) 函数返回USB_DEVICE_NODE。
 发现调用驱动程序通过管理设备在其当前配置中提供的唯一接口来管理整个设备。在这
种情况下,usb_get_if_number(9F) 函数返回USB_COMBINED_NODE。
实用程序函数
第19 章• USB 驱动程序483
管理整个设备
如果驱动程序管理整个复合设备,则可以使用包含供应商ID、产品ID 和修订版ID 的兼容
名称将该驱动程序绑定到整个设备。绑定到整个复合设备的驱动程序必须与结点驱动程序
一样管理设备的所有接口。通常,不应将驱动程序绑定到整个复合设备。应改为使用一般
的多接口驱动程序usb_mid(7D)。
使用usb_owns_device(9F) 函数可确定驱动程序是否拥有整个设备。设备可以是复合设备。
如果驱动程序拥有整个设备,则usb_owns_device(9F) 函数将返回TRUE。
多配置设备
在任何特定时间,主机上只能使用USB 设备的一种配置。大多数设备仅支持一种配置。但
是,少数USB 设备支持多种配置。
对于具有多种配置的任何设备,都是采用可使用某驱动程序的第一种配置。查找匹配项
时,设备配置以数字顺序处理。如果未找到任何匹配的驱动程序,则设备将被设置采用第
一种配置。在这种情况下,usb_mid 驱动程序将接管该设备,并将设备拆分为多个接口节
点。使用usb_get_cfg(9F) 函数可返回设备的当前配置。
您可以使用以下两种方法中的任何一种来请求采用其他配置。使用其中任何一种方法修改
设备配置,均可确保USBA模块保持与设备同步。
 使用cfgadm_usb(1M) 命令。
 从驱动程序调用usb_set_cfg(9F) 函数。
由于更改设备配置会影响整个设备,因此客户机驱动程序必须满足以下所有条件,才能
成功调用usb_set_cfg(9F) 函数:
 客户机驱动程序必须拥有整个设备。
 设备不能有子节点,因为其他驱动程序可能会通过这些子节点驱动该设备。
 必须关闭除缺省管道之外的所有管道。
 设备必须具有多种配置。
注意– 不要通过手动执行SET_CONFIGURATION USB 请求来更改设备配置。不支持使用
SET_CONFIGURATION 请求更改配置。
修改或获取替代设置
客户机驱动程序可以调用usb_set_alt_if(9F) 函数以更改当前选定接口的选定替代设置。
请确保关闭已明确打开的所有管道。切换替代设置时,usb_set_alt_if(9F) 函数将验证是否
仅打开了缺省管道。确保在调用usb_set_alt_if(9F) 之前已正确设置了设备。
更改替代设置可能会影响对驱动程序可用的端点以及特定于类和特定于供应商的描述符。
有关端点和描述符的更多信息,请参见第466 页中的“描述符树”。
调用usb_get_alt_if(9F) 函数可检索当前替代设置的编号。
实用程序函数
484 编写设备驱动程序• 2006 年11 月
注– 请求新替代设置、新配置或新接口时,必须关闭设备的除缺省管道外的所有管道。这是
因为更改替代设置、配置或接口会更改设备的运行模式。此外,更改替代设置、配置或接
口还会更改设备在系统中的呈现方式。
其他实用程序函数
本节介绍在USB 设备驱动程序中有用的其他函数。
检索字符串描述符
调用usb_get_string_descr(9F) 函数可检索给定了索引的字符串描述符。一些配置、接口或
设备描述符具有关联的字符串ID。这样的描述符包含具有非零值的字符串索引字段。将字
符串索引字段值传递给usb_get_string_descr(9F) 可检索对应的字符串。
管道专用数据工具
每个管道都有一个空间指针,专供客户机驱动程序使用。使用usb_pipe_set_private(9F) 函
数可安装一个值。使用usb_pipe_get_private(9F) 函数可检索该值。当管道可能需要将其自
己的客户机定义状态传递到回调,以进行特定处理时,此工具在回调中很有用。
清除USB 条件
使用usb_clr_feature(9F) 函数可执行以下任务:
 发出USB CLEAR_FEATURE 请求以清除端点的停止条件。
 清除设备的远程唤醒条件。
 在设备级别、接口级别或端点级别清除特定于设备的条件。
获取设备、接口或端点状态
使用usb_get_status(9F) 函数可发出USB GET_STATUS 请求,以检索设备、接口或端点的状
态。
 设备状态。自备电源并启用远程唤醒。
 接口状态。根据USB 2.0 规范返回零。
 端点状态。已停止端点。此状态指示运行延迟。必须清除停止状态才能重新运行设备。
协议延迟指示发出了不支持的控制管道请求。在下一个控制传输开始时将会自动清除协
议延迟。
获取设备的总线地址
使用usb_get_addr(9F) 函数可获取设备的USB 总线地址以用于调试目的。此地址映射到特
定的USB 端口。
实用程序函数
第19 章• USB 驱动程序485
USB 设备驱动程序样例
本节介绍使用Solaris 环境的USBA2.0 框架的USB 设备驱动程序模板。此驱动程序演示了本
章中讨论的许多功能。此模板或框架驱动程序的名称为usbskel。
usbskel 驱动程序是可用于启动您自己的USB 设备驱动程序的模板。usbskel 驱动程序演示
了以下功能:
 读取设备的原始配置数据。每个USB 设备需要能够报告设备的原始配置数据。
 管理管道。usbskel 驱动程序打开中断管道以显示如何管理管道。
 轮询。usbskel 驱动程序中的注释讨论如何进行轮询。
 USB 版本管理和注册。
 USB 日志。
 支持USB 热插拔。
 支持Solaris 暂停和恢复。
 支持电源管理。
 USB 序列化。
 使用USB 回调。
此usbskel 驱动程序可在SunWeb 站点()
上获取。
有关其他USB 驱动程序的源代码,请参见OpenSolarisWeb 站点。请访问
,然后单击页面左侧菜单中的"Source Browser"。
有关Solaris OS 的设备驱动程序的更多信息和帮助,请参见Sun 开发者网络
http://developers.sun.com/prodtech/solaris/ 上的Solaris OS 页面。要专门获取有关设备
驱动程序的信息,请单击Driver Development。
USB 设备驱动程序样例
486 编写设备驱动程序• 2006 年11 月
生成设备驱动程序
本书的第三部分在为Solaris 操作系统生成设备驱动程序方面提供了建议:
 第20 章提供了有关编译、链接和安装驱动程序的信息。
 第21 章介绍了有关调试、测试和调优驱动程序的技术。
 第22 章介绍了推荐的用于编写驱动程序的编码惯例。
第3 部分
487
488
编译、装入、打包和测试驱动程序
本章介绍驱动程序的开发过程,包括代码布局、编译、打包和测试。本章提供有关以下主
题的信息:
 第490 页中的“驱动程序代码布局”
 第491 页中的“准备安装驱动程序”
 第494 页中的“安装、更新和删除驱动程序”
 第496 页中的“装入和卸载驱动程序”
 第496 页中的“驱动程序打包”
 第500 页中的“驱动程序测试条件”
驱动程序开发摘要
本章以及后面的两章-第21 章和第22 章,提供了有关开发设备驱动程序的详细信息。驱动
程序开发者可采用以下步骤来生成设备驱动程序:
1. 编写、编译和链接新代码。
有关文件的命名约定,请参见第490 页中的“驱动程序代码布局”。使用C 编译器编译
驱动程序。使用ld(1) 链接驱动程序。请参见第492 页中的“编译和链接驱动程序”和
第493 页中的“模块相关性”。
2. 创建必需的硬件配置文件。
您需要创建一个特定于名为xx.conf 的设备的硬件配置文件,其中xx 为设备的前缀。该
文件用于更新driver.conf(4) 文件。请参见第493 页中的“编写硬件配置文件”。对于
伪设备驱动程序,需要创建一个pseudo(4) 文件。
3. 将驱动程序复制到相应的模块目录。
请参见第494 页中的“将驱动程序复制到模块目录”。
4. 使用add_drv(1M) 安装设备驱动程序。
使用add_drv 安装驱动程序通常是作为后安装脚本的一部分来执行的。请参见第495 页
中的“使用add_drv 安装驱动程序”。update_drv(1M) 命令用于对驱动程序进行更改。
请参见第495 页中的“更新驱动程序信息”。
20 第2 0 章
489
5. 装入驱动程序。
通过打开设备的特殊文件,即可以编程方式装入驱动程序。请参见第496 页中的“装入
和卸载驱动程序”和第496 页中的“软件包后安装”。另外,也可以使用modload(1M)
命令装入驱动程序。modload 命令不会调用模块中的任何例程,因此实际上更适合测
试。请参见第508 页中的“装入和卸载测试模块”。
6. 测试驱动程序。
驱动程序应在以下方面进行严格的测试:
 第500 页中的“配置测试”
 第500 页中的“功能测试”
 第501 页中的“错误处理”
 第501 页中的“测试装入和卸载”
 第501 页中的“负荷、性能和互操作性测试”
 第502 页中的“DDI/DKI 兼容性测试”
 第502 页中的“安装和打包测试”
有关其他特定于驱动程序的测试,请参见第503 页中的“测试特定类型驱动程序”。
7. 删除驱动程序(如有必要)。
通过rem_drv(1M) 命令可删除设备驱动程序。请参见第495 页中的“删除驱动程序”和
第498 页中的“软件包预删除”。
驱动程序代码布局
设备驱动程序代码通常分为以下文件:
 头文件(.h 文件)
 源文件(.c 文件)
 可选配置文件(driver.conf 文件)
头文件
头文件提供以下定义:
 特定于设备的数据结构,如表示设备寄存器的结构
 驱动程序定义的用于维护状态信息的数据结构
 定义的常数,如表示设备寄存器位的常数
 宏,如定义次要设备号与实例编号之间的静态映射的宏
某些头文件定义(如状态结构)可能只有设备驱动程序才需要。这些信息应该放在设备驱
动程序本身所包含的专用头文件中。
应用程序可能需要的任何信息(如I/O 控制命令)均应放在公共头文件中。这些文件包含在
驱动程序和任何需要设备相关信息的应用程序中。
虽然专用文件和公共文件并没有命名标准,但一种约定是将专用头文件命名为xximpl.h,
将公共头文件命名为xxio.h。
驱动程序代码布局
490 编写设备驱动程序• 2006 年11 月
.c 文件
设备驱动程序的.c 文件具有以下职责:
 包含驱动程序入口点的代码和数据声明
 包含驱动程序所需的#include 语句
 声明外部引用
 声明局部数据
 设置cb_ops 和dev_ops 结构
 声明并初始化模块配置部分,即modlinkage(9S) 和modldrv(9S) 结构
 进行任何其他必要的声明
 定义驱动程序入口点
driver.conf 文件
非自标识的设备需要driver.conf 文件。driver.conf 文件中的项指定了驱动程序可以探测
其存在情况的可能设备实例。有关更多信息,请参见driver.conf(4) 手册页。
driver.conf 文件中的项还可以设置驱动程序的全局属性。对于自标识设备(self-identifying
device, SID),driver.conf 文件是可选的,其中的项可用于向SID 节点添加属性。
driver.conf 文件通常会定义驱动程序所需的全部属性,但也存在例外情况。
使用S 总线外围总线的驱动程序一般从S 总线卡获取属性信息。如果需要其他属性,
driver.conf 文件可以包含由sbus(4) 定义的属性。
PCI 总线的属性通常可以从PCI 配置空间派生而来。如果需要专用驱动程序属性,
driver.conf 文件可以包含由pci(4) 定义的属性。
ISA 总线上的驱动程序可以使用由isa(4) 定义的其他属性。
准备安装驱动程序
安装驱动程序之前,需要执行以下步骤:
1. 编译驱动程序。
2. 创建配置文件(如有必要)。
3. 通过以下任一备选方法,在系统中标识驱动程序模块:
 将驱动程序的名称与设备节点的名称匹配。
 使用add_drv(1M) 或update_drv (1M) 将模块名称通知给系统。
系统维护驱动程序模块名称与dev_info 节点名称之间的一对一关联。例如,假设名为
wombat 的设备包含一个dev_info 节点。处理设备wombat 的驱动程序模块也名为wombat。
wombat 模块驻留在名为drv 的子目录中,该子目录位于模块路径下。在这种情况下,如果
使用32 位内核,则可以在drv/wombat 中找到该模块;如果使用64 位内核,则可以在
drv/sparcv9/wombat 中找到该模块。
准备安装驱动程序
第20 章• 编译、装入、打包和测试驱动程序491
如果驱动程序是一个STREAMS 网络驱动程序,则驱动程序名称需要满足以下约束:
 只允许使用字母数字字符(a-z, A-Z, 0-9) 以及下划线(’_’)。
 名称的第一个字符和最后一个字符都不能是数字。
 名称的长度不能超过16 个字符。最好使用长度在3 到8 个字符范围内的名称。
如果驱动程序应该用不同的名称管理dev_info 节点,则add_drv(1M) 实用程序可以创建别
名。-i 标志指定驱动程序处理的其他dev_info 节点的名称。update_drv() 函数也可以修改
已安装的设备驱动程序的别名。
编译和链接驱动程序
您需要编译每个驱动程序源文件,并将生成的对象文件链接到驱动程序模块中。Solaris OS
既与Sun Studio C 编译器兼容,又与Free Software Foundation, Inc. 提供的GCC 编译器兼容。
/usr/sfw 目录中提供了GCC 编译器。有关Sun Studio 和C 编译器的更多信息,请参见《Sun
Studio 11: C User’s Guide》以及http://developers.sun.com/prodtech/cc/ Web 站点。有关
GCC 编译器的更多信息,请参见,或者查看/usr/sfw/man 中的手册
页。除非另有说明,否则本节中的示例均使用Sun Studio C Compiler 5.7。
以下示例显示一个名为xx 的驱动程序,该驱动程序包含两个C 语言源文件。生成的驱动程
序模块名为xx。本示例中创建的驱动程序适用于32 位内核:
% cc -D_KERNEL -c xx1.c
% cc -D_KERNEL -c xx2.c
% ld -r -o xx xx1.o xx2.o
编译内核(驱动程序)代码时,必须定义_KERNEL 符号。除了驱动程序专用符号以外,不应
定义任何其他符号,如sun4m。为了启用对assert(9F) 的调用,还可以定义DEBUG。对于标
准头文件,不必使用-I 标志。
适用于64 位SPARC 内核的驱动程序应指定-xarch=v9 选项。请使用以下行进行编译:
% cc -D_KERNEL -xarch=v9 -c xx1.c
驱动程序稳定后,可以使用优化标志来生成符合生产质量要求的驱动程序。对于Sun Studio
C 编译器,可以使用标准的-O 标志,也可以使用等效的-xON 标志。对于设备驱动程序而
言,将所有全局变量视为volatile 是一种比较好的做法。volatile 标记将在第540 页中的
“将变量声明为可变变量”中详细介绍。标志的使用取决于平台,如下所示:
 SPARC 体系结构:-xO2 标志将所有全局变量均视为volatile。对于-xO2 以上的优化级
别,需要将全局变量明确标记为volatile。
 x86 体系结构:除非使用-g 选项,否则对于所有优化级别,都需要将全局变量明确标记
为volatile。
有关优化和其他编译问题的更具体信息,请参见cc(1) 手册页。
准备安装驱动程序
492 编写设备驱动程序• 2006 年11 月
以下编译行使用C Compiler 5.7 创建适用于Solaris 操作系统的64 位SPARC 驱动程序:
% cc -D_KERNEL -xarch=v9 -xcode=abs32 -xO3 -c xx1.c
-xcode=abs32 的使用使得代码更为紧凑。
注– 即使只有一个对象模块存在,也必须运行ld -r。
以下编译行使用C Compiler 5.7 创建适用于Solaris 操作系统的64 位x86 驱动程序:
% cc -D_KERNEL -xarch=amd64 -xmodel=kernel -c xx1.c
另外,也可使用GCC 编译器创建64 位x86 驱动程序,如下所示:
% gcc -D_KERNEL -m64 -mcmodel=kernel -mno-red-zone -ffreestanding -c xx1.c
模块相关性
如果驱动程序模块依赖于其他内核模块导出的符号,则可以通过ld 的-dy 和-N 选项指定相
关性。如果驱动程序依赖于misc/mySymbol 导出的符号,则应使用以下示例创建驱动程序二
进制文件。请参见ld(1) 手册页。
% ld -dy -r -o xx xx1.o xx2.o -N misc/mySymbol
编写硬件配置文件
如果设备是非自标识的设备,则内核需要该设备的硬件配置文件。如果驱动程序名为xx,
则该驱动程序的硬件配置文件应该名为xx.conf。有关硬件配置文件的更多信息,请参见
driver.conf(4)、pseudo(4)、sbus(4)、scsi_free_consistent_buf (9F) 和update_drv (1M)
手册页。在x86 平台上,设备信息现在由引导系统提供。即使是非自标识的设备,也应该
不再需要硬件配置文件。
在硬件配置文件中可以定义任意属性。配置文件中的项的形式为property=value,其中
property 是属性名称,value 是其初始值。借助配置文件方法,可以通过更改属性值来配置设
备。
准备安装驱动程序
第20 章• 编译、装入、打包和测试驱动程序493
安装、更新和删除驱动程序
必须先将驱动程序已存在的信息通知系统,然后才能使用该驱动程序。必须使用
add_drv(1M) 实用程序来正确安装设备驱动程序。安装驱动程序之后,便可以从内存中装入
和卸载该驱动程序,而无需再次使用add_drv(1M)。
将驱动程序复制到模块目录
设备驱动程序模块的路径取决于以下三个条件:
 运行驱动程序的平台
 编译驱动程序时采用的体系结构
 引导时是否需要该路径
设备驱动程序驻留在以下位置:
/platform/‘uname -i‘/kernel/drv
包含仅在特定平台上运行的32 位驱动程序。
/platform/‘uname -i‘/kernel/drv/sparcv9
包含仅在基于SPARC 的特定平台上运行的64 位驱动程序。
/platform/‘uname -i‘/kernel/drv/amd64
包含仅在基于x86 的特定平台上运行的64 位驱动程序。
/platform/‘uname -m‘/kernel/drv
包含仅在特定平台系列上运行的32 位驱动程序。
/platform/‘uname -m‘/kernel/drv/sparcv9
包含仅在基于SPARC 的特定平台系列上运行的64 位驱动程序。
/platform/‘uname -m‘/kernel/drv/amd64
包含仅在基于x86 的特定平台系列上运行的64 位驱动程序。
/usr/kernel/drv
包含与平台无关的32 位驱动程序。
/usr/kernel/drv/sparcv9
包含基于SPARC 的系统上与平台无关的64 位驱动程序。
/usr/kernel/drv/amd64
包含基于x86 的系统上与平台无关的64 位驱动程序。
要安装32 位驱动程序,必须将驱动程序及其配置文件复制到模块路径中的drv 目录。例
如,要将驱动程序复制到/usr/kernel/drv,请键入:
$ su
# cp xx /usr/kernel/drv
# cp xx.conf /usr/kernel/drv
安装、更新和删除驱动程序
494 编写设备驱动程序• 2006 年11 月
要安装64 位SPARC 驱动程序,请将驱动程序复制到模块路径中的drv/sparcv9 目录。将驱
动程序配置文件复制到模块路径中的drv 目录。例如,要将驱动程序复制到
/usr/kernel/drv,应键入:
$ su
# cp xx /usr/kernel/drv/sparcv9# cp xx.conf /usr/kernel/drv
注– 所有驱动程序配置文件(.conf 文件)都必须放入模块路径中的drv 目录。即使是在64
位系统上,.conf 文件也应放入drv 目录,而非drv/sparcv9 目录。
使用add_drv 安装驱动程序
运行add_drv 可将驱动程序安装到系统中。如果驱动程序成功安装,add_drv 将运行
devfsadm (1M),以便在/dev 中创建逻辑名称。
# add_drv xx
在本例中,设备将自身标识为xx。设备的特殊文件具有缺省的拥有权和权限(0600 root
sys)。add_drv(1M) 也允许为设备指定其他名称(别名)。有关显式添加别名和设置文件权
限的信息,请参见add_drv(1M) 手册页。
注– 安装STREAMS 模块时,不应运行add_drv(1M)。有关详细信息,请参见《STREAMS
Programming Guide》。
如果驱动程序创建了不表示终端设备(即磁盘、磁带或端口)的次要节点,则可以修改
/etc/devlink.tab,以使devfsadm(1M) 在/dev 中创建逻辑设备名称。
另外,也可以通过安装驱动程序时运行的程序来创建逻辑名称。
更新驱动程序信息
使用update_drv(1M) 命令可以通知系统对已安装的设备驱动程序所做的任何更改。缺省情
况下,系统将会重新读取driver.conf(4) 文件,并重新装入驱动程序二进制模块。
删除驱动程序
要将驱动程序从系统中删除,请使用rem_drv(1M),然后从模块路径中删除驱动程序模块和
配置文件。使用add_drv(1M) 重新安装驱动程序之前,无法再使用该驱动程序。删除SCSI
HBA驱动程序需要重新引导才能生效。
安装、更新和删除驱动程序
第20 章• 编译、装入、打包和测试驱动程序495
装入和卸载驱动程序
打开与设备驱动程序关联的特定文件即可装入该驱动程序。modload(1M) 也可用于将驱动程
序装入内存,但modload() 不会调用模块中的任何例程。首选方法是打开设备。
通常,系统会自动卸载不再使用的设备驱动程序。在开发过程中,要显式卸载驱动程序,
可能需要使用modunload(1M)。为了成功执行modunload(1M),设备驱动程序必须处于非活
动状态。对设备的任何未完成引用(如通过open(2) 或mmap(2) 的引用)均不应存在。
modunload 将与运行时相关的module_id 用作参数。要查找module_id,请使用grep 在
modinfo 输出中搜索相关的驱动程序名称。然后检查第一列。
# modunload -i module-id
要卸载当前无法装入的所有模块,请将模块ID 指定为零:
# modunload -i 0
要成功执行modunload (1M),驱动程序需要处于非活动状态,而且还必须包含正常的
detach(9E) 和_fini(9E) 例程。
驱动程序打包
软件的标准发布方式是创建一个包含所有软件组件的软件包。软件包为软件产品所有组件
的安装和删除提供了一种受控机制。除了用于使用产品的文件以外,软件包还包括用于安
装和卸载应用程序的控制文件。后安装和预删除安装脚本即为两种此类控制文件。
软件包后安装
将包含驱动程序二进制文件的软件包安装到系统中之后,必须运行add_drv(1M) 命令。
add_drv() 将完成驱动程序的安装。通常,add_drv 是作为后安装脚本运行的,如以下示例
中所示。
#!/bin/sh
#
# @(#)postinstall 1.1
PATH="/usr/bin:/usr/sbin:${PATH}"
export PATH
装入和卸载驱动程序
496 编写设备驱动程序• 2006 年11 月
#
# Driver info
#
DRV=
DRVALIAS=","
DRVPERM=’* 0666 root sys’
ADD_DRV=/usr/sbin/add_drv
#
# Select the correct add_drv options to execute.
# add_drv touches /reconfigure to cause the
# next boot to be a reconfigure boot.
#
if [ "${BASEDIR}" = "/" ]; then
#
# On a running system, modify the
# system files and attach the driver
#
ADD_DRV_FLAGS=""
else
#
# On a client, modify the system files
驱动程序打包
第20 章• 编译、装入、打包和测试驱动程序497
# relative to BASEDIR
#
ADD_DRV_FLAGS="-b ${BASEDIR}"
fi
#
# Make sure add_drv has not been previously executed
# before attempting to add the driver.
#
grep "^${DRV} " $BASEDIR/etc/name_to_major > /dev/null 2>&1
if [ $? -ne 0 ]; then
${ADD_DRV} ${ADD_DRV_FLAGS} -m "${DRVPERM}" -i "${DRVALIAS}" ${DRV}
if [ $? -ne 0 ]; then
echo "postinstall: add_drv $DRV failed\n" >&2
exit 1
fi
fi
exit 0
软件包预删除
删除包含驱动程序的软件包时,必须先运行rem_drv(1M) 命令,然后删除驱动程序二进制
文件和其他组件。以下示例演示了一个使用rem_drv(1M) 删除驱动程序的preremove 脚本。
#!/bin/sh
#
# @(#)preremove 1.1
驱动程序打包
498 编写设备驱动程序• 2006 年11 月
PATH="/usr/bin:/usr/sbin:${PATH}"
export PATH
#
# Driver info
#
DRV=
REM_DRV=/usr/sbin/rem_drv
#
# Select the correct rem_drv options to execute.
# rem_drv touches /reconfigure to cause the
# next boot to be a reconfigure boot.
#
if [ "${BASEDIR}" = "/" ]; then
#
# On a running system, modify the
# system files and remove the driver
#
REM_DRV_FLAGS=""
else
#
# On a client, modify the system files
驱动程序打包
第20 章• 编译、装入、打包和测试驱动程序499
# relative to BASEDIR
#
REM_DRV_FLAGS="-b ${BASEDIR}"
fi
${REM_DRV} ${REM_DRV_FLAGS} ${DRV}
exit 0
驱动程序测试条件
设备驱动程序可以正常运行后,应在分发之前全面测试该驱动程序。除了测试传统UNIX
设备驱动程序中的功能以外,Solaris 驱动程序还需要测试电源管理功能,如驱动程序的动
态装入和卸载。
配置测试
驱动程序能否处理多种设备配置是测试过程的重要部分。驱动程序可以在一种简单(或缺
省)配置中正常工作后,还应该测试其他配置。根据设备不同,可以通过更改跳线或DIP
开关来完成配置测试。如果可能的配置数较少,则应尝试测试所有配置。如果可能的配置
数较多,则应为这些可能的配置定义不同的类,并抽样测试每类配置。定义这些类取决于
不同配置参数之间的潜在交互。这些交互是设备类型与驱动程序编写方式之间的配合。
对于每种设备配置,必须测试基本运行情况,包括装入、打开、读取、写入、关闭和卸载
驱动程序。任何取决于配置的运行情况都需要特别注意。例如,更改设备寄存器的基本内
存地址不可能影响大多数驱动程序函数的行为。如果驱动程序在一个地址上正常工作,则
该驱动程序在其他地址上也能正常工作。另一方面,特殊I/O 控制调用的结果可能会因特定
设备配置而异。
以不同配置装入驱动程序可确保probe(9E) 和attach(9E) 入口点能够在不同地址找到设备。
对于基本功能测试,使用常规的UNIX 命令(如cat(1) 或dd(1M))通常就可满足字符设备
的要求。对于块设备,可能需要挂载或引导。
功能测试
对驱动程序进行完面的配置测试之后,应全面测试驱动程序的所有功能。这些测试需要执
行驱动程序所有入口点的操作。
驱动程序测试条件
500 编写设备驱动程序• 2006 年11 月
许多驱动程序需要使用自定义的应用程序来测试功能。但是,对于磁盘、磁带或异步板等
设备的基本驱动程序,使用标准系统实用程序即可进行测试。在此过程中,应测试所有入
口点,包括devmap(9E)、chpoll(9E) 和ioctl(9E)(如果适用)。对于每种驱动程
序,ioctl(9E) 测试可能完全不同。对于非标准设备,通常需要使用自定义的测试应用程
序。
错误处理
在理想环境中,驱动程序也许可以正确执行,但如果出现错误(如操作错误或数据错
误),则可能会失败。因此,驱动程序测试的一个重要部分是测试驱动程序的错误处理。
应该执行驱动程序的所有可能的错误情况,包括实际硬件故障导致的错误情况。某些硬件
错误情况可能难于引发,但如有可能,应尽力强制引发或模拟此类错误。在实际使用时,
所有这些情况都有可能遇到。为了测试以找出这些错误的根源,应该拆除或松开电缆、拆
除板以及编写错误的用户应用程序代码。
注意– 测试时,请务必采取正确的电气预防措施。
测试装入和卸载
由于没有装入或卸载的驱动程序会导致意外的停机时间,因此必须全面测试装入和卸载。
与以下示例类似的脚本应该足够满足要求:
#!/bin/sh
cd
while [ 1 ]
do
modunload -i ’modinfo | grep " " | cut -cl-3’ &
modload &
done
负荷、性能和互操作性测试
{0>To help ensure that a driver performs well, that driver should be subjected to vigorous stress
testing.<}0{>为有助于确保驱动程序正常运行,应对该驱动程序进行强有力的负荷测试。
<0}{0>For example, running single threads through a driver does not test locking logic or
驱动程序测试条件
第20 章• 编译、装入、打包和测试驱动程序501
conditional variables that have to wait.<}0{>?例如,通过驱动程序运行单个线程并不会测试必
须等待的锁定逻辑或条件变量。?<0}{0>Device operations should be performed by multiple
processes at once to cause several threads to execute the same code simultaneously.<}0{>设备操作应
由多个进程同时执行,以使几个线程同时执行同一代码。<0}
{0>Techniques for performing simultaneous tests depends upon the driver.<}0{>执行同时测试的
方法取决于驱动程序。<0}{0>Some drivers require special testing applications, while starting
several UNIX commands in the background is suitable for others.<}0{>某些驱动程序需要使用特
殊的测试应用程序,而在后台启动多个UNIX 命令适用于其他驱动程序。<0}{0>Appropriate
testing depends upon where the particular driver uses locks and condition variables.<}0{>正确的测
试取决于特定驱动程序在何处使用锁定和条件变量。<0}{0>Testing a driver on a
multiprocessor machine is more likely to expose problems than testing on a single-processor
machine.<}0{>在多处理器计算机上测试驱动程序比在单处理器计算机上测试更有可能暴露
问题。<0}
此外,还必须测试驱动程序之间的互操作性,尤其是在不同的设备可以共享中断级别的情
况下。如有可能,请配置与正在测试的设备的中断级别相同的另一个设备。负荷测试可以
确定驱动程序是否正确请求其中断,以及是否按照预期目标运行。应该对两个设备同时运
行负荷测试。即使设备不共享中断级别,该测试仍然很重要。例如,假设在测试某个网络
驱动程序时,串行通信设备遇到错误。同一问题也可能会导致系统的其余部分遇到中断延
迟问题。
这些负荷测试下的驱动程序性能应使用UNIX 性能度量工具进行度量。此类测试与使用
time(1) 命令以及负荷测试所用命令一样简单。
DDI/DKI 兼容性测试
为确保与更高发行版的兼容性以及对当前发行版的可靠支持,每个驱动程序都应该与
DDI/DKI 兼容。确定驱动程序是否兼容的一种方法是进行检查。检查是否仅使用了《man
pages section 9: DDI and DKI Kernel Functions》中的内核例程以及《man pages section 9: DDI
and DKI Properties and Data Structures》中的数据结构。
Solaris 驱动程序开发者工具包(Driver Developer Kit, DDK) 包含DDI 兼容性工具(DDI
compliance tool, DDICT)。该工具可以检查设备驱动程序中C 源代码的非DDI/DKI 兼容性。
如果发现不兼容的代码,该工具将发出错误或警告消息。为了获得最佳结果,应将所有驱
动程序编写为要通过DDICT 检查。有关更多信息,请参见当前位于
的Solaris Developer Connection。
安装和打包测试
驱动程序是以软件包的形式提供给客户的。使用标准机制,可在系统中添加或删除软件包
(请参见《Application Packaging Developer’s Guide》)。
应对用户在系统中添加或删除软件包的能力进行测试。测试时,应从供发行使用的每种类
型的介质中安装和删除软件包。该测试应该包含多种系统配置。对于目标系统的目录环
境,软件包不能做出任何无根据的假设。但是,可以对标准内核文件的保留位置做出某些
驱动程序测试条件
502 编写设备驱动程序• 2006 年11 月
有效假设。此外,还要在新安装的、尚未修改开发环境的计算机上测试软件包的添加和删
除。常见的打包错误是软件包依赖于仅在开发时使用的工具或文件。例如,在驱动程序安
装程序中,不应使用源兼容性软件包SUNWscpu 中的任何工具。
驱动程序安装必须在不带任何可选软件包的最小Solaris 系统上测试。
测试特定类型驱动程序
本节提供有关如何测试某些类型的标准设备的一些信息。将各种不同类型设备的测试信息
全部列出是不可能的。
磁带驱动程序
磁带驱动程序应通过执行多次归档和恢复操作来测试。cpio(1) 和tar(1) 命令可用于此目
的。使用dd(1M) 命令可将整个磁盘分区写入磁带。接下来,读回数据,并将数据写入另一
个相同大小的分区。然后比较这两个副本。mt(1) 命令可以执行特定于磁带驱动程序的大多
数I/O 控制。请参见mtio(7I) 手册页。尝试使用所有选项。以下三种方法可以测试磁带驱动
程序的错误处理能力:
 移除磁带并尝试各种操作
 对磁带进行写保护并尝试写入
 在不同操作的执行过程中关闭电源
磁带驱动程序通常实现以独占方式访问的open(9E) 调用。可以通过打开设备,然后让另一
个进程尝试打开同一设备,来测试这些open() 调用。
磁盘驱动程序
磁盘驱动程序应在原始设备模式和块设备模式下进行测试。对于块设备测试,请在设备上
创建一个新的文件系统。然后,尝试挂载该新文件系统,并尝试执行多种文件操作。
注– 文件系统使用页缓存,因此重复读取相同文件实际上并不会执行驱动程序。通过使用
mmap(2) 对文件进行内存映射,可以强制页缓存次要设备中检索数据。然后使用msync(3C)
使内存中的副本无效。
将另一个相同大小的(未挂载)分区复制到原始设备。然后使用fsck(1M) 之类的命令检验
副本的正确性。新分区也可以挂载,然后与旧分区进行逐文件比较。
异步通信驱动程序
通过为串行端口设置一个login 连接线,可对异步驱动程序进行基本级别的测试。一种比较
好的测试方法是用户是否可以通过此连接线登录。但是,要充分测试异步驱动程序,必须
使用多个高速中断来测试所有I/O 控制函数。涉及回送串行电缆和较高数据传输速率的测试
有助于确定驱动程序的可靠性。您可以在该连接线上运行uucp(1C),以提供某些实践。但
是,由于uucp(1C) 执行自己的错误处理,因此请验证驱动程序不会报告uucp(1C) 进程的额
外错误数。
驱动程序测试条件
 
 
以上文章转自于 : http://developers.sun.com.cn/
阅读(717) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~