USB在WINDOWS下的驱动开发

来源:互联网 发布:ms和mrs 知乎 编辑:程序博客网 时间:2024/04/30 10:34
 

编程架构

--------------------------------------------------------------------------------

USB可以使程序员在不了解总线电气特性的情况下写出主机和设备的驱动软件。USB规范的第五章“USB数据流模型”和第九章“USB设备框架”描述了许多对于驱动程序作者有用的特征。在这节中,我将概述这些章。

设备层次
图11-1演示了一个简单的USB配置拓扑。USB主控制器与其它I/O设备一样直接连接到系统总线上。操作系统与主控制器通信使用I/O口或内存寄存器,通过普通的中断信号,系统可以接受主控制器的事件通知。主控制器连接一棵USB设备树。一种称为hub的设备作为其它设备的连接点。多个hub能以菊链方式连接,可以连接到USB规范中定义的最大深度。其它设备,如照相机、麦克风、键盘等等,直接连到hub上。为了精确地表达概念,USB使用术语function来描述非hub设备。

图11-1. USB设备层次结构

高速和低速设备
USB规范中定义了两种设备,高速设备和低速设备。低速设备以1.5Mb/sec速率通信,高速设备以12Mb/sec速率通信。hub能用电子方式区分这两种设备。发生在总线上的通讯通常都是高速的,hub一般不向低速设备发送数据。操作系统把任何发往低速设备的消息前加上一个前导包,这将使hub临时降为低速,并完成低速设备的数据发送。

电源
USB电缆中含有两条电源线。hub可以为连接在其上的设备提供电力,USB规范限定了总线供电设备所消耗的电流。这个限定会因为设备插入可供电hub或与最近可供电hub的远近而改变。另外,USB允许设备有睡眠状态,处于睡眠状态下的设备仅消耗非常低的电力并足够支持唤醒和配置信号。另外,设备还可以不用总线电力而直接使用外接电源。

USB设备可以唤醒睡眠中的系统。当系统进入节能状态后,操作系统也把USB总线置入节能状态。一个有远程唤醒特征的设备能发出唤醒信号,信号首先被送到hub,然后又被hub送到主控制器,最后由主控制器送往系统。

USB设备设计者应该知道唤醒特征的一些限制。首先,远程唤醒功能仅能工作在有高级电源配置接口(ACPI)BIOS的计算机上,而不能工作在早期的系统上,这些早期系统或者支持APM或者根本就没有电源管理特征。另一个限定在驱动程序的通知上。WDM提供了一种方法—— IRP_MN_WAIT_WAKE电源管理IRP,当设备唤醒系统时该IRP被发往驱动程序。然而,当USB设备从节能状态恢复到正常状态时,而此时系统又处于正常工作状态,该IRP将不出现。

设备中有什么?
一般,每个USB设备有一个或多个配置(configuration)来控制其行为,如图11-2。使用多配置的一个原因是对操作系统的支持,例如,系统BIOS可以使用一个简单的配置而操作系统中的驱动程序则使用另一个更复杂的配置。

图11-2. USB设备的配置、接口、端点。

设备的每个配置中都含有一个或更多的接口(interface),接口指出软件应该怎样访问硬件。接口的概念与第二章(“WDM驱动程序的基本结构”)中讨论的连接命名设备中的概念类似,即支持相同接口的设备本质上可以互换,因为它们以相同的方式响应相同的命令。另外,接口一般都有替换设置(alternate setting)以适应不同的带宽需求。

设备的接口露出一个或多个端点(endpoint),端点作为通信管道的一个终点。图11-3显示了一个多层次结构的通信模型,它表明了端点和管道所扮演的角色。在最低一级,USB电缆把主控制器与设备的总线接口连接起来。在第二级,一个控制管道把系统软件与逻辑设备连接起来。在第三级,一捆数据管道把客户软件与一组接口连接起来,这些接口组成设备的function。信息实际上是在图中两侧垂直流动,但把它理解为在这些分层的管道中水平流动更清晰。

图11-3. USB的多层次通信模型

一组由Microsoft提供的驱动程序占据了图中系统软件方块中的底部。这些驱动程序包括主控制器驱动程序(OPENHCI.SYS或者UHCD.SYS),hub驱动程序(USBHUB.SYS),和一个类驱动程序(USBD.SYS),由控制器驱动程序使用。为了方便,我把USBD下面的所有驱动程序看成一个整体,我们的驱动程序主要就是与这个整体进行交互,它们管理着硬件连接和管道通信。WDM驱动程序,就是你和我将要写的,占据系统软件方块中的顶部。广义地说,WDM驱动程序的工作就是把客户软件的请求翻译成USBD能执行的事务(transaction)。客户软件处理实际的设备功能。例如,一个图象生成程序占据的客户软件槽可能与一个静态图象function(如数码相机)对应。

信息流动
USB定义了四种数据传输方式,如表11-1所示。它们的不同之处有:单个事务能携带的数据量(下一段将解释术语“事务”transaction)、能否保证特定的周期或延迟,能否自动校正错误。每种传输方式对应特定类型的端点。实际上,给定类型的端点(控制、批量、中断、等时)总是使用对应类型的传输。

表11-1. 数据传输类型

传输类型 描述 纠错
 包容量(字节) 延迟保证? 
控制 用于发送和接收USB定义的结构化信息 是 少于或等于8,16,32,64 尽最大能力保证不延迟 
批量 用于发送或接收小块无结构数据 是 少于或等于8,16,32,64 无 
中断 与批量管道相似,但包括一个最大延迟 是 少于或等于64 以保证的最小速率轮询 
等时 用于发送或接收有周期保证的大块无结构数据 否 少于或等于1023 每1毫秒帧中的固定部分 

端点除了传输类型外还有其它几个属性。其中一个属性是单一事务中端点能够提供或消耗的最大数据量。控制和批量端点必须指定某个离散值,而中断和等时端点能指定少于或等于最大值的任何值。端点的另一个属性是传输方向,输入(数据从设备到主控制器)或输出(数据从主控制器到设备)。最后,每个端点都有一个端点号,其中包含输入输出方向,作为端点的地址使用。

当主控制器要求设备执行某些多少有些规则的功能时,USB使用一个轮检(polling)协议。当一个设备需要向主控制器发送数据时,主控制器必须注意到并且向要发送数据的设备发出一个请求使其发送数据。即USB设备不用传统方式中断主计算机,而是提供中断端点,主机周期轮检中断端点。

信息打包
当客户程序通过USB管道发送或接收数据时,它首先调用Win32 API,调用最终将使function的驱动程序收到一个IRP。而驱动程序的工作就是把客户的请求引导到有正确端点的管道上。它把请求提交到总线驱动程序,总线驱动程序再把请求分解成多个事务(transaction),然后这些事务被送往总线。总线上的信息流以每毫秒一帧数据的形式流动。总线驱动程序必须安排好多个事务以使它们能被装入同一帧中,图11-4显示了这个过程。

图11-4. 信息流中的事务和帧模型

在USB中,事务由一个或多个阶段(phase)组成。阶段有令牌(token)、数据(data)、握手(ack)三种类型。根据不同的类型,事务有一个令牌阶段、一个可选的数据阶段、和一个可选的握手阶段组成,如图11-5所示。在令牌阶段,主控制器向所有已配置的设备广播该令牌包。令牌包中含有设备地址,通常还有端点号,仅有被寻址的设备才会处理事务;当事务寻址设备时,任何设备都不读写总线。在数据阶段,数据被放到总线上。对于输出事务,主机把数据放到总线上,而被寻址的设备消耗这些数据。对于输入事务,情况相反,设备把数据放到总线上由主机消耗。在握手阶段,由设备或主机把握手包放到总线上,包中含有状态信息。当设备发出握手包时,ACK包指出成功地接收了信息,NAK包指出忙并且不试图接收信息,STALL包指出事务被正确接收但在逻辑上无效。当主机发送握手包时,它仅能发送ACK包。

图11-5. 总线事务的阶段

你也许会注意到,没有发出握手包的事务就代表“在这个事务中出现了一个传输错误”。正在等待握手包的一方应认为缺少握手包可能是发生了错误并重试刚才的事务。USB的设计者确信这种错误很少发生,由于重试而造成的偶然延迟对于总线吞吐量不会有大的影响。

关于设备寻址的更多内容 
上文提到所有被配置的设备都接收每个事务中的电子信号。这几乎是正确的,但一个真正的程序员应该了解更详细的内容。当一个USB设备第一次接入时,它使用默认地址(碰巧是0,你不必知道)响应。然后,某个电子信号通知总线驱动程序有一个新设备插入总线,于是总线驱动程序找出一个未用的设备地址并发送一个控制事务告诉“0号设备”什么才是它的真实地址。这之后,设备就放弃使用默认地址0,而用真实地址来应答。

另一些细节涉及到低速设备。低速设备的电气特征使它在面对传输率是其八倍的总线时会发生信号混乱。另外,低速设备的电缆没有电磁屏蔽,遇到高速信号会产生电子干扰。结果,低速设备在大部分时间并不连接到总线上。当总线进行高速传输时,hub就隔离低速设备。当主机需要与低速设备通信时,它就发送一个特殊的前导包把总线临时切换到低速操作,所以,低速设备只能看到低速事务,而高速设备能看到所有事务。

端点的状态
一般,端点可以进入图11-6中所示的任何一种状态。在空闲状态中,端点准备处理主机发起的新事务。在忙碌状态中,端点忙于处理手中的事务而不能接受新事务。如果主机试图向一个忙端点发起新事务(不是控制端点,下段描述),设备将用NAK握手包响应,主机将在以后重发刚才的包。如果设备发现自己内部出现错误(不包括传输错误),设备将在当前事务中发送一个STALL握手包然后进入停止状态。控制端点在接到新事务时会自动从停止状态恢复,但其它三种端点必须由主机明确发送一个清除特征(feature)控制请求后才能从停止状态中恢复。

图11-6. 端点的三种状态

控制传输
控制传输在主机和控制端点之间传送控制信息。例如,在操作系统配置USB设备过程中,有一步就是用控制传输从物理设备中读出各种描述符(descriptor),配置过程的另一步是利用控制传输设置一种可能的配置并使能一个或多个接口。控制传输是一种纠错传输,在遇到传输错误时重试三遍,如果错误仍存在就放弃传输并向上层软件报告错误。如表11-1中所指出的,控制端点所能指定的最大数据传输长度为8、16、32、64字节。一个单独的事务可以包含少于最大数据传输长度的数据但不能多于。

控制事务在USB中具有最高优先级。设备必须处理控制事务。此外,总线驱动程序为控制事务保留了10%的带宽。对于轻量级的负载,主机能确保在1毫秒内完成一个控制事务。对于重量级的负载,未完成的控制传输将被强制到下一帧中传输,因此会产生一些延迟。

每个USB设备至少应有一个编号为0的控制端点以响应控制事务的输入输出。严格地讲,端点应属于配置,但端点0是一个例外,因为它是设备默认控制管道的终点。端点0甚至在设备被配置前就被激活而不管其它端点是否有效。除了端点0,一个设备没有必要拥有另外的控制端点(尽管USB规范中允许有这种可能),因为端点0对于大部分控制请求都可以很好地完成。如果你定义了一个厂商专用的请求并且该请求不能在一帧中完成,你应该创建额外的控制端点以防止设备接收器被新事务抢先。

每个控制事务包括一个SETUP令牌,之后可以带一个可选的数据阶段和一个握手阶段,在握手阶段设备会发出ACK包或者STALL包,或者根本就不响应,如图11-7。设备必须在任何时间都能接受控制传输,并且不能用NAK响应控制端点忙。向控制端点发送一个无效请求将导致STALL响应,但当设备再次接收到一个SETUP包时会自动清除停止状态。这个特殊的STALL在USB规范(8.5.2.4段)中被称为协议停止(protocol stall)。

图11-7. 控制传输中的三个阶段

开始控制传输的SETUP令牌由8个字节组成,如图11-8所示。在下面这个和后面的数据结构图中,我按照数据在USB导线上的传输顺序列出其中的数据字节,但是每个字节中的位是以高位开始。在传输线上的字节传输是从低位开始的,但主机软件和设备固件通常以相反的方向使用字节。Intel计算机和USB总线协议使用小结尾的数据表达,即低位字节占用低地址。许多USB芯片集包括Anchor Chips芯片集使用8051微处理器,这个处理器使用大结尾的数据表达。固件程序设计者必须注意这一点。

 

图11-8. SETUP令牌的内容

SETUP令牌的第一字节指出信息流的方向、请求的类型、和接收控制传输的实体类型。请求类型有标准类型(由USB规范定义)、类类型(由USB工作组定义的一类设备),和厂商类型(由设备制造者定义)。控制请求可以发到整个设备、一个指定接口、一个指定的端点,或者到设备上的厂商专有实体。SETUP令牌中的第二字节指出具体的请求,其类型由第一字节指出。表11-2列出了当前定义的标准请求。对于类专用请求,应参考对应设备类的说明(http://www.usb.org/developers/)。设备制造者可以自由定义自己专用的请求代码。例如,Anchor Chips使用请求代码A0h表示从主机下载固件程序。

注意 
--------------------------------------------------------------------------------
改变端点状态的控制请求将发往控制端点而不是发往被改变状态的端点。 
表11-2 标准设备请求

请求代码 符号名 描述 可能的接受者 
0 GET_STATUS 获得状态信息 任何 
1 CLEAR_FEATURE 清除一个双态特征 任何 
2  (保留)  
3 SET_FEATURE 设置一个双态特征 任何 
4  (保留)  
5 SET_ADDRESS 设置设备地址 设备 
6 GET_DESCRIPTOR 取设备、配置,或串描述符 设备 
7 SET_DESCRIPTOR 设置一个描述符(可选) 设备 
8 GET_CONFIGURATION 取当前配置索引 设备 
9 SET_CONFIGURATION 设置一个新的当前配置 设备 
10 GET_INTERFACE 取当前的alt接口索引 接口 
11 SET_INTERFACE 使能alt接口设置 接口 
12 SYNCH_FRAME 报告同步帧号 (等时)端点 

SETUP包中其余的内容包括一个value代码(其含义与具体的请求相关)、一个index值(当控制请求寻址端点或接口时,index值指出具体地址)、一个length域(指出控制事务的数据阶段要传输的数据量。为0表示该事务没有数据阶段)。

我并不想详细描述各种控制请求的细节;你可以参考USB规范的第9.4段。但我确实想简要地讨论一下设备特征(feature)这个概念,USB认为,属于设备的任何可寻址实体都可以有1个特征位。已有两个特征实现了标准化。

DEVICE_REMOTE_WAKEUP特征 —— 属于设备整体的特征,它指出当外部事件发生时设备能否用这个能力(如果有)唤醒计算机。主机软件(尤其是总线驱动程序)通过SET_FEATURE或CLEAR_FEATURE命令可以允许或禁止这个特征,用value代码为1来指出允许唤醒特征。DDK中用符号名USB_FEATURE_REMOTE_WAKEUP代表这个特征码。

ENDPOINT_HALT特征 —— 属于单个端点的特征,指出端点是否处于停止状态。主机软件可以向端点发送SET_FEATURE命令并且value为0(指定ENDPOINT_HALT)来强制端点进入停止状态。当然,管理端点的设备固件也能使端点进入停止状态。如果端点被固件设为停止状态,主机软件(总线驱动程序)也可以发送一个value为0的CLEAR_FEATURE命令清除该端点的停止状态。DDK使用符号名USB_FEATURE_ENDPOINT_STALL代表这个特征码。

USB规范中并没有为厂商使用的设备或端点的特征代码规定范围。为了避免后来的标准化问题,你应该避免定义设备级或端点级的特征,而仅应该定义自己的厂商类型控制事务。在本章后面我将演示一个例子驱动程序(FEATURE),它能控制Anchor Chips开发板上的7段LED显示。我在这个例子中定义了一个编号为42的接口级特征。(USB现在为电源管理定义了一些接口级特征,所以你不应效仿我的例子,除非你想知道特征是怎样工作的)

尽管Anchor Chips(现在是Cypress Semiconductor)的EZ-USB可以容易地从驱动程序下载新固件,但你不应在产品级设备上使用这个特征。你需要开发一个“Loader”驱动程序和一个function驱动程序,前者用于下载固件到USB设备,后者用于管理设备。不幸的是,如果计算机进入节能状态,操作系统也把USB总线置入节能状态,设备的固件内容将丢失。当回到正常电源状态时,系统卸载function驱动程序并重新装载“Loader”驱动程序,Loader再次下载固件并重新装载function驱动程序。而节能前的function驱动程序的状态信息将全部丢失,所以当你使用这个芯片时应考虑把固件放到EEPROM上。

批量传输
批量(bulk)传输能在主机和批量端点间一次传输最多64字节的数据。就象控制传输一样,批量传输是纠错传输,不同的是批量传输没有任何延迟保证。如果主机发现帧中除了预定带宽外还有剩余,它就把等待的批量传输送往总线。

图11-9显示了组成批量传输的各个阶段。传输开始于表明传输方向的IN或OUT令牌,这些令牌同时还指定目标设备和端点。对于输出事务,后面是一个数据阶段,把数据从主机送到设备,最后在握手阶段设备向主机提供状态反馈。如果端点正忙不能接收新数据,它就在握手阶段发出一个NAK包,主机以后会重试该事务。如果端点处于停止(STALL)状态,它就在握手阶段发出一个STALL包,主机在重试前必须清除端点的停止状态。如果端点正确地接收并处理了数据,将在握手阶段返回一个ACK包。最后一种情况是端点因为某种原因没有正确地接收数据并且在握手阶段也没有发出应答包,主机将检查端点是否应答,并且自动重试三次原事务。

IN令牌后面是一个批量输入传输。如果端点将数据准备好,设备将把数据发往主机,主机或者回应一个ACK包指出数据无误地接收,或者保持沉默以指出发生了某种错误。如果主机查出一个错误,ACK的缺发将使设备数据保持有效,之后主机将重试输入操作;如果端点忙或停止,设备将会在数据阶段发出NAK或STALL握手包而不是数据。NAK指出主机应在以后重试该输入操作,STALL需要主机先清除端点的停止特征再重试输入操作。

图11-9. 批量传输和中断传输中的阶段

中断传输
中断(interrupt)传输在总线操作和涉及设备方面上几乎与批量传输完全相同。它能把最大64字节的数据无误地在主机和中断端点间传输。中断传输和批量传输仅有的不同是它必须考虑延迟。中断端点需指定一个范围在1-255毫秒内的询查周期。主机保留足够的带宽以确保在指定频率上直接向中断端点发出IN或OUT事务。

注意 
--------------------------------------------------------------------------------
注意USB设备不生成异步中断,它们总是响应循检,Microsoft的主控制器驱动程序把中断端点描述符中指定的循检周期简化为不大于32的2的幂。例如,一个指定循检周期为31毫秒的端点实际上是以16毫秒进行循检。指定周期在32-255毫秒之间的循检实际上是以32毫秒为循检周期。 
等时传输
等时(isochronous)传输可以在一个总线帧内最多传输1023字节数据。因为等时传输有周期保证,因此特别适用于时间敏感的数据传输,如音频信号,但这种周期保证是有代价的,等时传输在数据出错时不会自动重试。USB设计者假定等时数据流的接收者允许偶尔的数据丢失。

等时传输由IN或OUT令牌阶段后跟一个数据阶段组成。因为不进行数据纠错,所以等时传输没有握手阶段。如图11-10。

图11-10. 等时传输中的阶段

主机为等时传输和中断传输保留最多90%的带宽。实际上,系统软件应提前保留带宽以确保总线能适应所有活动的设备。

描述符
USB设备硬件中的数据结构称为描述符,可以被主机软件识别。表11-3列出了不同种类的描述符。每个描述符开始于一个两字节的头,头中指出该描述符的字节长度(包括头)和描述符类型。事实上,如果我们不讨论特殊的串描述符,描述符的长度对于相同的描述符类型是固定的,即所有给定类型的描述符长度相同。在描述符头中保存明确的长度便于描述符将来的扩展。

下面我将使用DDK(位于USB100.H文件中)中定义的数据结构描述每一种描述符。官方的解释见USB规范第9.6段。

表11-3. 描述符表

描述符类型 描述 
设备 描述整个设备 
配置 描述设备的一个配置 
接口 描述配置中的一个接口 
端点 描述接口中的一个端点 
串 一个Unicode串,该串用自然语言描述设备、配置、接口,或端点 
电源配置 描述电源管理能力 
接口电源 描述function的电源管理能力 

设备描述符
每个设备都有一个唯一的设备描述符,它向主机软件标识该设备。主机使用GET_DESCRIPTOR控制事务直接从设备的0号端点读取该描述符。该描述符在DDK中的定义如下:

typedef struct _USB_DEVICE_DESCRIPTOR {
    UCHAR bLength;
    UCHAR bDescriptorType;
    USHORT bcdUSB;
    UCHAR bDeviceClass;
    UCHAR bDeviceSubClass;
    UCHAR bDeviceProtocol;
    UCHAR bMaxPacketSize0;
    USHORT idVendor;
    USHORT idProduct;
    USHORT bcdDevice;
    UCHAR iManufacturer;
    UCHAR iProduct;
    UCHAR iSerialNumber;
    UCHAR bNumConfigurations;
} USB_DEVICE_DESCRIPTOR, *PUSB_DEVICE_DESCRIPTOR;
 

设备描述符的bLength域应等于18,bDescriptorType域应等于1以指出该结构是一个设备描述符。bcdUSB域包含该描述符遵循的USB规范的版本号(以BCD编码)。现在,设备可以使用值0x0100或0x0110来指出它所遵循的是1.0版本还是1.1版本的USB规范。

bDeviceClass、bDeviceSubClass、bDeviceProtocol指出设备类型。可能的设备类代码在USB规范中定义,本书所包括的类代码在表11-4中列出。USB委员会的独立设备类工作组为每个设备类定义子类和协议代码。例如,音频类有控制、流,和MIDI流接口的子类代码。大容量存储类为使用各种端点的数据传输方法定义了协议代码。

你可以为整个设备或仅在接口级指定一个类,但事实上,设备类、子类、和协议代码通常出现在接口描述符中而不是出现在设备描述符中。USB还为特殊类型的设备指定了一个特殊的设备类代码255。厂商可以使用这个代码指出其设备是一个非标准设备,并且在子类和协议域中填入厂商设定的值。例如,使用Anchor Chips芯片集的设备,其设备描述符中的类、子类,和协议代码全为255。

设备描述符的bMaxPacketSize0域给出了默认控制端点(端点0)上的数据包容量的最大值。每个设备都必须提供0号控制端点,由于USB规范并没有为该端点规定一个单独的端点描述符,所以这个域是唯一描述这个端点的地方。因为这个域在设备描述符的偏移7处,所以即使该端点使用最小的传输容量(8字节)主机也能读到这个域。一旦主机知道了端点0的最大传输容量,它就可以分块读出整个描述符。

idVendor和idProduct域指定厂商代码和厂商专用的产品标识。bcdDevice指出设备的发行版本号(0x0100对应版本1.0)。当主机软件检测设备时,这三个域决定了主机应该装入哪个驱动程序。USB组织提供厂商代码,厂商提供产品代码。

表11-4. USB设备类代码

符号名 类代码 描述 
USB_DEVICE_CLASS_RESERVED 0 指出类代码存在于接口描述符中 
USB_DEVICE_CLASS_AUDIO 1 操作模拟或数字音频、语音、和其它与声音相关的数字设备 
USB_DEVICE_CLASS_COMMUNICATIONS 2 电讯设备,如调制解调器、电话、应答机,等等 
USB_DEVICE_CLASS_HUMAN_INTERFACE 3 人类接口设备,如键盘、鼠标、麦克风,等等 
USB_DEVICE_CLASS_MONITOR 4 显示器 
USB_DEVICE_CLASS_PHYSICAL_INTERFACE 5 含有实时物理反馈的人类接口设备,如力反馈游戏杆 
USB_DEVICE_CLASS_POWER 6 执行电源管理的人类接口设备,如电池、充电器,等等 
USB_DEVICE_CLASS_PRINTER  7 打印机 
USB_DEVICE_CLASS_STORAGE 8 大容量存储设备,如磁盘和CD-ROM 
USB_DEVICE_CLASS_HUB 9 USB hubs 
USB_DEVICE_CLASS_VENDOR_SPECIFIC 255 厂商定义的设备类 

设备版本号 
Microsoft强烈建议厂商在硬件或固件的修订版中增加设备版本号以便于下层软件更新。一般,厂商发行新版本硬件的同时也带来驱动程序的修订版。同样,硬件升级应该使以前用于掩盖硬件错误的软件补丁或过滤器驱动程序无效。系统的自动升级机制在遇到一个版本不明确的硬件时会失败。

iManufacturer、iProduct、和iSerialNumber域指向一个串描述符,该串描述符用人类可读的语言描述设备生产厂商、产品、和序列号。这些串是可选的,0值代表没有描述串。如果你在设备上放入了序列号串,Microsoft建议应使每个物理设备的序列号唯一。

最后,bNumConfigurations指出该设备能实现多少种配置。Microsoft的驱动程序仅工作于设备的第一种配置(1号配置)。我将在后面解释怎样使用设备的多个配置。

配置描述符
每个设备有一个或多个配置描述符,它们描述了设备能实行的各种配置方式。DDK中定义的配置描述符结构如下:

typedef struct _USB_CONFIGURATION_DESCRIPTOR {
    UCHAR bLength;
    UCHAR bDescriptorType;
    USHORT wTotalLength;
    UCHAR bNumInterfaces;
    UCHAR bConfigurationvalue;
    UCHAR iConfiguration;
    UCHAR bmAttributes;
    UCHAR MaxPower;
} USB_CONFIGURATION_DESCRIPTOR, *PUSB_CONFIGURATION_DESCRIPTOR;
 

bLength和bDescriptorType域应为9和2,即是一个9字节长的配置描述符。wTotalLength域为该配置描述符长度加上该配置内所有接口和端点描述符长度的总和。通常,主机在发出一个GET_DESCRIPTOR请求并正确接收到9字节长的配置描述符后,就会再发出一个GET_DESCRIPTOR请求并指定这个总长度。第二个请求把这个大联合描述符传输回来。(注意这个传输不会把不属于这个配置的接口和端点描述符传输回来)

bNumInterfaces指出该配置有多少个接口。这个值仅是接口的数量,不包括接口中的替换设置。这个域的目的是允许多功能设备存在,如一个有定位器(类似于鼠标)的键盘。

bConfigurationvalue域是该配置的索引值。你可以用这个值在SET_CONFIGURATION控制请求中选择这个配置。注意设备的第一个配置描述符的索引为1。(选择配置0将把设备置入未配置状态,此时仅有端点0是活动的)

iConfiguration域是一个可选的串描述符索引,指向描述该配置的Unicode字符串。此值为0表明该配置没有串描述符。

bmAttributes字节包含描述该配置中设备电源和其它特性的的位掩码,见表11-5。未提到的位为未来标准保留。一个支持远程唤醒的配置应有远程唤醒属性位。该字节最高两位与MaxPower域一起描述配置中的电源特性。基本上,设置了最高位的配置都同时在MaxPower域中指出要从USB总线上获取的最大电流量(单位为2mA)。使用外接电源的配置需要设置自供电属性位。

表11-5. 配置的属性位

位掩码 符号名称 描述 
80h USB_CONFIG_BUS_POWERED 废弃 -- 应总为1 
40h USB_CONFIG_SELF_POWERED 该配置为自供电 
20h USB_CONFIG_REMOTE_WAKEUP 该配置有远程唤醒特征 

接口描述符
每个配置有一个或多个接口描述符,它们描述了设备提供功能的接口。

DDK中的接口描述符结构定义如下:

typedef struct _USB_INTERFACE_DESCRIPTOR {
    UCHAR bLength;
    UCHAR bDescriptorType;
    UCHAR bInterfaceNumber;
    UCHAR bAlternateSetting;
    UCHAR bNumEndpoints;
    UCHAR bInterfaceClass;
    UCHAR bInterfaceSubClass;
    UCHAR bInterfaceProtocol;
    UCHAR iInterface;
} USB_INTERFACE_DESCRIPTOR, *PUSB_INTERFACE_DESCRIPTOR;
 

bLength和bDescriptorType域应为9和4。bInterfaceNumber和bAlternateSetting是索引值,用在SET_INTERFACE控制事务中以指定要激活的接口。这些值可以是任意的,但习惯上,配置中的接口号从0开始,每个接口中的替换设置也是从0开始的。

bNumEndpoints域指出该接口有多少个端点,不包括端点0,端点0被认为是总存在的,并且是接口的一部分。

bInterfaceClass、bInterfaceSubClass、和bInterfaceProtocol域描述了接口提供的功能。一个非0的类代码应该是上面讨论的类代码中的一个,同时子类和协议代码也必须有与该类相类似的含义。这些域不允许有0值,0值为未来标准保留。

最后,iInterface是一个串描述符的索引,0表示该接口无描述串。

端点描述符
接口可以没有或有多个端点描述符,它们描述了处理事务的端点。

DDK中定义的端点描述符结构如下:

typedef struct _USB_ENDPOINT_DESCRIPTOR {
    UCHAR bLength;
    UCHAR bDescriptorType;
    UCHAR bEndpointAddress;
    UCHAR bmAttributes;
    USHORT wMaxPacketSize;
    UCHAR bInterval;
} USB_ENDPOINT_DESCRIPTOR, *PUSB_ENDPOINT_DESCRIPTOR;
 

bLength和bDescriptorType域应为7和5。bEndpointAddress域编码端点的方向性和端点号,如图11-11所示。例如,地址值0x82指出该端点是一个端点号为2的IN端点,而0x02地址指出一个端点号为2的OUT端点。除了端点0,两个端点可以有相同的端点号但方向相反。

图11-11. 端点描述符地址域的位排列

bmAttributes的低两位指出端点的类型。见表11-6。其余的位保留给将来使用,应设为0。

表11-6. 端点类型代码

符号名称 值 端点类型 
USB_ENDPOINT_TYPE_CONTROL 0 控制端点 
USB_ENDPOINT_TYPE_ISOCHRONOUS 1 等时端点 
USB_ENDPOINT_TYPE_BULK 2 批量端点 
USB_ENDPOINT_TYPE_INTERRUPT 3 中断端点 

wMaxPacketSize值指出该端点在一个事务中能传输的最大数据量,表11-1列出了每种端点的可能值。

中断端点和等时端点描述符还有一个用于指定循检间隔时间的bInterval域,时间单位为毫秒。这个数指出主机以多长的周期循检这些端点,以查看是否有可能的数据传输。对于中断端点,该值的范围为1到255毫秒,代表两次循检间的最大时间间隔。对于等时端点,该值应该为1,因为帧周期固定为1毫秒,每帧都应该循检。

串描述符
设备、配置、端点描述符都可以包含一个指向人类可读串的指针。串本身以USB串描述符的形式保存在设备中,串字符使用Unicode编码。

DDK中的串描述符结构声明如下:

typedef struct _USB_STRING_DESCRIPTOR {
    UCHAR bLength;
    UCHAR bDescriptorType;
    WCHAR bString[1];
} USB_STRING_DESCRIPTOR, *PUSB_STRING_DESCRIPTOR;
 

bLength值根据串数据长度可变。bDescriptorType域的值应为3。bString域包含串数据本身。注意,串的空结尾符应包含在描述符长度内。

USB设备可以以多种语言支持串描述符。0号串描述符是设备所支持语言的标识符数组,它不是一个真正的串描述符。(用在其它描述符中代表无描述串指定的值0,在这里被用于索引串语言标识数组)语言标识与Win32程序中使用的LANGID相同。例如,0x409是美国英语的代码。如果向设备询问串描述符的某种未支持语言的表达,其结果在USB规范中没有规定,所以你应该先读取串0数组。关于语言标识请参考USB规范第9.6.5段。

其它描述符
USB是一个发展的规范,我能表达的仅仅是它的概要。例如,一个USB工作组最近完成了一个接口级的电源管理规范。你可以在USB网站上读到这些内容,并且DDK头文件USB100.H中包含了这个规范的定义。时间不允许我探索这些新功能。幸好,对于WDM驱动程序的作者并不需要了解这些,解释接口特征描述符是hub驱动程序的任务而不是WDM功能驱动程序或过滤器驱动程序的任务。

 

 

原创粉丝点击