一、USB概述
USB的英文全称为Universal Serial Bus,中文含义是通用串行总线,是由Conpaq、DEC、IBM、Inter、Microsoft、NEC和Northen Telecom等公司为简化PC与外设之间的互连而共同研究开发的一种免费的标准化连接器,它支持各种PC与外设之间的连接,还可实现数字多媒体集成。现在生产的PC几乎都配备了USB接口,Microsoft 的Windows98、NT以及MacOS、Linux、FreeBSD等流行操作系统都增加了对 USB的支持。它是一种快速的,双向的,同步传输的廉价的并可以进行热拔插的串行接口。USB接口使用方便,它可以连接多个不同的设备,而过去的串口和并口只能接一个设备。速度快是USB技术的突出特点之一。全速USB接口的最高传输率可达12Mb/s,比串口快了整整100倍,USB总线标准由1.1版升级到2.0版后,传输率由12Mbps增加到了480Mbps,.这使得高分辨率、真彩色的大容量图象的实时传送成为可能。USB接口支持多个不同设备的串列连接,一个USB接口理论上可以连接127个USB设备。连接方式也十分灵活,既可以使用串行连接,也可以使用集线器(Hub)把多个设备连接在一起,再同PC机的USB接口相接。普通的使用串口、并口的设备都需要单独的供电系统,而USB设备则不需要。正是由于USB的这些特点,使其获得了广泛的应用。
二、USB的总线结构
USB的总线结构是采用阶梯式星形(tiered star)的拓扑(topology)结构,如上图和下图所示。每一个星形的中心是集线器,而每一个设备可以通过集线器上的接口来加以连接。从图中可以看到USB的设各包含了两种类型:USB集线器与USB设备。位于最顶端的就是Host(主机端)。从Host的联机往下连接至Hub(集线器),再由集线器按阶梯式以一层或一阶的方式往下扩展出去,连接在下一层的设备或另一个集线器上。事实上,集线器也可视为一种设备。而其中最大层数为6层(包括计算机内部的根集线器)。每一个星形的外接点的数目可加以变化,一般集线器具有2、4或7个接口。
在此的主机端通常是指PC主机。当然,主机端因具有根集线器,因此也含有集线器的功能。而集线器是在USB规范中特别定义出来的外围设备,除了扩增系统的连接点外,还负责中继(repeat)上端/下端的信号以及控制各个下端端口的电源管理。至于另一个设备,即是用户常见的外围设备。但在USB规范书中,称这种设备为“功能”(functions),意味着此系统提供了某些“能力”,例如具有键盘或鼠标等功能。当然不同的外围设各可以具有不同的功能。通过这种阶梯式星形的连接方式,最多可同时连接到127个设备。
三、USB结构与工作原理
一个USB系统可以从三个方面加以描述:
(1) USB互联。
(2) USB设备。
(3) USB主机。
USB互联是指一个USB设备与USB主机相联并和其通信的方式,它包括:
(1) 总线拓扑结构:USB主机和USB设备的连接模型。
(2) 层间关系:USB在系统中的每一层都要完成一定的任务。
(3) 数据流模型:USB系统中信源和信息之间的数据传送方式。
(4) 任务规划:USB提供可以共享的互联机制。通过规划对互连机制的访问,可以支持同步数据传输。
四、USB硬件结构
一个USB系统包含三类硬件设备: USB主机(USB HOST)、 USB设备(USB DEVICE)、USB集线器(USB HUB)。
(1) USB HOST - 一般是指有USB接口的电脑。
在一个USB系统中,当且仅当有一个USB HOST时,USB HOST有以下功能:
Ø 管理USB系统;
Ø 每毫秒产生一帧数据;
Ø 发送配置请求对USB设备进行配置操作;
Ø 对总线上的错误进行管理和恢复。
(2) USB DEVICE - 一般是指的是优盘、手机等设备。
在一个USB系统中,USB DEVICE和USB HUB总数不能超过127个。USB DEVICE接收USB总线上的所有数据包,通过数据包的地址域来判断是不是发给自己的数据包:若地址不符,则简单地丢弃该数据包;若地址相符,则通过响应USB HOST的数据包与USB HOST进行数据传输。
(3)USB HUB - 一般指一转多的USB设备。
USB HUB用于设备扩展连接,所有USB DEVICE都连接在USB HUB的端口上。一个USB HOST总与一个根HUB (USB ROOT HUB)相连。USB HUB为其每个端口提供100mA电流供设备使用。同时,USB HUB可以通过端口的电气变化诊断出设备的插拔操作,并通过响应USB HOST的数据包把端口状态汇报给USB HOST。一般来说,USB设备与USB HUB间的连线长度不超过5m,USB系统的级联不能超过6级(包括ROOT HUB)。
USB总线最多可支持127个USB外设连接到计算机系统。USB的拓扑是树形结构,有1个USB根集线器(root hub),下面还可有若干集线器。1个集线器下面可接若干USB接口。USB线缆包括4条线:Vbus(USB电源)、D+(数据)、D-(数据)和 Gnd(USB地)。线缆最大长度不超过5m。USB1.1的传输速率最高为12Mb/s(低速外设的标准速率为1.5Mb/s,高速外设的标准速率为 12Mb/s)。USB外设可以采用计算机里的电源(+5V,500mA),也可外接USB电源。在所有的USB信道之间动态地分配带宽是USB总线的特征之一,这大大地提高了USB带宽的利用率。当一台USB外设长时间(3ms以上)不使用时,就处于挂起状态,这时只消耗0.5mA 电流。按USB1.0/1.1标准,USB的标准脉冲时钟频率为12MHz,而其总线时脉冲时钟为1ms(1kHz),即每隔1ms,USB器件应为 USB线缆产生1个时钟脉冲序列。这个脉冲系列称为帧开始数据包(SOF)。高速外设长度为每帧12000bit(位),而低速外设长度只有每帧 1500bit。1个USB数据包可包含0~1023字节数据。每个数据包的传送都以1个同步字段开始。
五、USB的数据流
主控制器负责主机和USB设备间数据流的传输。这些传输数据被当作连续的比特流。每个设备提供了一个或多个可以与客户程序通信的接口,每个接口由0个或多个管道组成,它们分别独立地在客户程序和设备的特定终端间传输数据。USBD为主机软件的现实需求建立了接口和管道,当提出配置请求时,主控制器根据主机软件提供的参数提供服务。
USB支持四种基本的数据传输模式:控制传输,等时传输,中断传输及数据块传输。每种传输模式应用到具有相同名字的终端,则具有不同的性质。
控制传输类型:支持外设与主机之间的控制,状态,配置等信息的传输,为外设与主机之间提供一个控制通道。每种外设都支持控制传输类型,这样主机与外设之间就可以传送配置和命令/状态信息。
等时传输类型:支持有周期性,有限的时延和带宽且数据传输速率不变的外设与主机间的数据传输。该类型无差错校验,故不能保证正确的数据传输,支持像计算机-电话集成系统(CTI)和音频系统与主机的数据传输。
中断传输类型:支持像游戏手柄,鼠标和键盘等输入设备,这些设备与主机间数据传输量小,无周期性,但对响应时间敏感,要求马上响应。
数据块传输类型:支持打印机,扫描仪,数码相机等外设,这些外设与主机间传输的数据量大,USB在满足带宽的情况下才进行该类型的数据传输。
USB采用分块带宽分配方案,若外设超过当前带宽分配或潜在的要求,则不能进入该设备。同步和中断传输类型的终端保留带宽,并保证数据按一定的速率传送。集中和控制终端按可用的最佳带宽来传输传输数据。
六、USB外设控制器的两种实现方式
USB芯片在外设领域的应用面很广。USB外设控制芯片通常包括USB收发器、串行接口引擎(SIE)、USB控制器和外设功能等四个模块(SIE 主要以硬件方式处理大多数USB协议,USB控制器负责与PC交互通信信息)。USB控制器一般有两种类型:一种是MCU集成在芯片里面的,如 Intel的8X930AX、CYPRESS的EZ-USB、SIEMENS的C541U以及 MOTOLORA、National Semiconductors等公司的产品;另一种就是纯粹的USB接口芯片,仅处理 USB通信,如PHILIPS的PDIUSBD11(I2C接口)、 PDIUSBP11A、PDIUSBD12(并行接口),National Semiconductor的USBN9602、USBN9603、USBN9*等。
集成MCU的USB控制芯片优点是CPU与控制器在同一片芯片里,CPU只需要访问一系列寄存器和存储器,便可实现USB口的数据传输,最大限度的发挥 USB高速的特点。而且简化了程序的设计,极大地降低了USB外设的开发难度。缺点是灵活性不够高,开发成本较大。
纯粹的USB接口芯片的优点是系统组成灵活,可根据不同的系统需求,搭配不同的MCU,具有较高的性能价格比。但因为USB控制器是通过串行口或并行口与MCU连接,在传输速度方面和开发难度方面不如集成了MCU的控制芯片。
不同的实现方式在设计开销、上市时间、元器件开销和引脚数方面各有优劣,选择不同的方案意味着在以上各项指标中进行取舍。如PHILIPS公司的PDIUSBD12器件。该芯片是一款性价比很高的USB器件,它通常用作微控制器系统中实现与微控制器进行通信的高速通用并行接口,设计者可根据需要选择合适的微控制器,灵活性较大,适用于开发低成本且高效的USB外围设备。
七、USB设备的枚举过程
USB架构中, hub负责检测设备的连接和断开,利用其中断IN端点(Interrupt IN Endpoint)来向主机(Host)报告。在系统启动时,主机轮询它的根hub(Root Hub)的状态看是否有设备(包括子hub和子hub上的设备)连接。
一旦获悉有新设备连接上来,主机就会发送一系列的请求(Resqusts)给设备所挂载到的hub,再由hub建立起一条连接主机(Host)和设备(Device)之间的通信通道。然后主机以控制传输(Control Transfer)的方式,通过端点0(Endpoint 0)对设备发送各种请求,设备收到主机发来的请求后回复相应的信息,进行枚举(Enumerate)操作。所有的USB设备必须支持标准请求(Standard Requests),控制传输方式(Control Transfer)和端点0(Endpoint 0)。
当枚举完成后,这个新添加的设备可在Windows的设备管理器里面看到,当用户删除这个设备/硬件时,系统把这个设备从设备管理器里删除。
对于一般的设备,固件(Firmware)内包含主机所要请求的信息,而有些设备则是完全由硬件来负责响应主机的请求。在主机方面则是由操作系统而非应用程序负责处理相关枚举操作。
USB协议定义了设备的6种状态,仅在枚举过程种,设备就经历了4个状态 的迁移:上电状态(Powered),默认状态(Default),地址状态(Address)和配置状态(Configured)(其他两种是连接状态和挂起状态(Suspend))。
USB协议定义了设备的6种状态,仅在枚举过程种,设备就经历了4个状态的迁移:上电状态(Powered),默认状态(Default),地址状态(Address)和配置状态(Configured)(其他两种是连接状态和挂起状态(Suspend))。
下面步骤是Windows系统下典型的枚举过程,但是固件不能依此就认为所有的枚举操作都是按照这样一个流程行进。设备必须在任何时候都能正确处理所有的主机请求。
1. 用户把USB设备插入USB端口或给系统启动时设备上电。
这里指的USB端口指的是主机下的根hub或主机下行端口上的hub端口。Hub给端口供电,连接着的设备处于上电状态。
2. Hub监测它各个端口数据线上(D+/D-)的电压
在hub端,数据线D+和D-都有一个阻值在14.25k到24.8k的下拉电阻Rpd,而在设备端,D+(全速,高速)和D-(低速)上有一个1.5k的上拉电阻Rpu。当设备插入到hub端口时,有上拉电阻的一根数据线被拉高到幅值的90%的电压(大致是3V)。hub检测到它的一根数据线是高电平,就认为是有设备插入,并能根据是D+还是D-被拉高来判断到底是什么设备(全速/低速)插入端口(全速、高速设备的区分在我将来的文章中描述)。如下图。
检测到设备后,hub继续给设备供电,但并不急于与设备进行USB传输。
3. Host了解连接的设备。
每个hub利用它自己的中断端点向主机报告它的各个端口的状态(对于这个过程,设备是看不到的,也不必关心),报告的内容只是hub端口的设备连接/断开的事件。如果有连接/断开事件发生,那么host会发送一个 Get_Port_Status请求(request)以了解更多hub上的信息。Get_Port_Status等请求属于所有hub都要求支持的hub类标准请求(standard hub-classrequests)。
4. Hub检测所插入的设备是高速还是低速设备。
hub通过检测USB总线空闲(Idle)时差分线的高低电压来判断所连接设备的速度类型,当host发来Get_Port_Status请求时,hub就可以将此设备的速度类型信息回复给host。(USB 2.0规范要求速度检测要先于复位(Reset)操作)。
5. hub复位设备。
当主机获悉一个新的设备后,主机控制器就向hub发出一个 Set_Port_Feature请求让hub复位其管理的端口。hub通过驱动数据线到复位状态(D+和D-全为低电平 ),并持续至少10ms。当然,hub不会把这样的复位信号发送给其他已有设备连接的端口,所以其他连在该hub上的设备自然看不到复位信号,不受影响。
6. Host检测所连接的全速设备是否是支持高速模式。
因为根据USB 2.0协议,高速(High Speed)设备在初始时是默认全速(Full Speed )状态运行,所以对于一个支持USB 2.0的高速hub,当它发现它的端口连接的是一个全速设备时,会进行高速检测,看看目前这个设备是否还支持高速传输,如果是,那就切到高速信号模式,否则就一直在全速状态下工作。
同样的,从设备的角度来看,如果是一个高速设备,在刚连接bub或上电时只能用全速信号模式运行(根据USB 2.0协议,高速设备必须向下兼容USB 1.1的全速模式)。随后hub会进行高速检测,之后这个设备才会切换到告诉模式下工作。假如所连接的hub不支持USB 2.0,即不是高速hub,不能进行高速检测,设备将一直以全速工作。
7. Hub建立设备和主机之间的信息通道。
主机不停得向hub发送 Get_Port_Status请求,以查询设备是否复位成功。Hub返回的报告信息中有专门的一位用来标志设备的复位状态。
当hub撤销了复位信号,设备就处于默认/空闲状态(Default state),准备着主机发来的请求。设备和主机之间的通信通过控制传输,默认地址0,端点号0进行。在此时,设备能从总线上得到的最大电流是100mA。
8. 主机发送Get_Descriptor请求获取默认管道的最大包长度。
默认管道(Default Pipe)在设备一端来看就是端点0。主机此时发送的请求是默认地址0,端点0,虽然所有位分配地址的设备都是通过地址0来获取主机发来的信息,但由于枚举过程不是多个设备并行处理,而是一次枚举一个设备的方式进行,所以不会发生多个设备同时响应主机发来的请求。
设备描述符的第8字节代表设备端点0的最大包大小。对于Windows系统来说,Get_Descriptor请求中的wLength一项都会设为64,虽然说设备所返回的设备描述符(Device Descriptor)长度只有18字节,但系统也不在乎,此时,描述符的长度信息对它来说是最重要的,其他的瞄一眼就过了。Windows系统还有个怪癖,当完成第一次的控制传输后,也就是完成控制传输的状态阶段,系统会要求hub对设备进行再一次的复位操作(USB规范里面可没这要求)。再次复位的目的是使设备进入一个确定的状态。
9. 主机给设备分配一个地址。
主机控制器通过Set_Address请求向设备分配一个唯一的地址。在完成这次传输之后,设备进入地址状态(Address state),之后就启用新地址继续与主机通信。这个地址对于设备来说是终生制的,设备在,地址在;设备消失(被拔出,复位,系统重启),地址被收回。同一个设备当再次被枚举后得到的地址不一定是上次那个了。
10. 主机获取设备的信息。
主机发送 Get_Descriptor请求到新地址读取设备描述符,这次主机发送Get_Descriptor请求可算是诚心,它会认真解析设备描述符的内容。设备描述符内信息包括端点0的最大包长度,设备所支持的配置(Configuration)个数,设备类型,VID(Vendor ID,由USB-IF分配), PID(Product ID,由厂商自己定制)等信息。Get_Descriptor请求(Device type)和设备描述符(已抹去VID,PID等信息)之后主机发送Get_Descriptor请求,读取配置描述符(Configuration Descriptor),字符串等,逐一了解设备更详细的信息。事实上,对于配置描述符的标准请求中,有时wLength一项会大于实际配置描述符的长度(9字节),比如255。这样的效果便是:主机发送了一个Get_Descriptor_Configuration 的请求,设备会把接口描述符,端点描述符等后续描述符一并回给主机,主机则根据描述符头部的标志判断送上来的具体是何种描述符。
11. 主机给设备挂载驱动(复合设备除外)。
主机通过解析描述符后对设备有了足够的了解,会选择一个最合适的驱动给设备。在驱动的选择过程中,Windows系统会和系统inf文件里的厂商ID,产品ID,有时甚至用到设备返回来的产品版本号进行匹配。如果没有匹配的选项,Windows会根据设备返回来的类,子类,协议值信息选择。如果该设备以前在系统上成功枚举过,操作系统会根据以前记录的登记信息而非inf文件挂载驱动。当操作系统给设备指定了驱动之后,就由驱动来负责对设备的访问。对于复合设备,通常应该是不同的接口(Interface)配置给不同的驱动,因此,需要等到当设备被配置并把接口使能后才可以把驱动挂载上去。
设备-配置-接口-端点关系见下图:
实际情况没有上述关系复杂。一般来说,一个设备就一个配置,一个接口,如果设备是多功能符合设备,则有多个接口。端点一般都有好几个,比如Mass Storage设备一般就有两个端点(控制端点0除外)。
12. 设备驱动选择一个配置。
驱动(注意,这里是驱动,之后的事情都是有驱动来接管负责与设备的通信)根据前面设备回复的信息,发送Set_Configuration请求来正式确定选择设备的哪个配置(Configuration)作为工作配置(对于大多数设备来说,一般只有一个配置被定义)。至此,设备处于配置状态,当然,设备也应该使能它的各个接口(Interface)。
对于复合设备,主机会在这个时候根据设备接口信息,给它们挂载驱动。
13. 设备可使用。
至此,步骤完成,设备可用了。
八、Windows驱动程序模型
Windows环境下驱动程序共有三类,一类是VxD( Virtual Device Driver,虚拟设备驱动程序),起源于Windows 3.1 时代,用于Windows 95/98/Me操作系统中;一类是KMD( Kernel Mode Driver,内核模式驱动程序),用于Windows NT下;还有一类就是WDM(Win32 Driver Mode,Win32驱动程序模型),是微软从Windows 98开始,推出的一个新的驱动类型,它是一个跨平台的驱动程序模型,不仅如此WDM驱动程序还可以在不修改源代码的情况下经过重新编译后在非Intel平台上运行,毫不夸张地讲,WDM算得上是21世纪的驱动程序框架。
WMD驱动程序模型
应用程序通过API函数调用Win32系统子函数,驱动程序分为设备驱动程序,总线驱动程序(USBD)和主控制器驱动程序(HCD)三层,它们均运行在系统的内核模式。设备驱动程序使用IRP(I/ORequest Packet)通过总线驱动程序提供的软件接口(USBDI,USB Driver Interface)向总线驱动程序发出I/O请求,并根据数据传输方向提供一个或空或满的内存缓冲区;USBD负责管理数据的总线传输,也有设备驱动程序与其他软件接口的功能单元进行通信,没有直接调用USBD,但总有一个更低层的驱动软件发生USBD调用。主控制器驱动程序处在USB系统软件的最底层,直接与主控制器的硬件通信,它提供了只有总线驱动程序才能访问的主控制器驱动程序软件接口HCDI(Host CONTROL Driver Interface)。其中,总线驱动程序和主控制器驱动程序是系统的底层驱动程序。设备驱动程序是针对某一USB设备的专用驱动程序。
Windows为USB设备提供了底层驱动程序,与底层驱动程序接口的是I/O请求包(IRP),Windows为应用程序提供的接口则是API函数。因此必须在它们之间建立一个驱动程序,在底层驱动与Win32应用程序之间传递消息,即设备驱动程序。VC++、VB等软件开发的应用程序,在设备驱动程序的支持下,都可以调用ReadFile()、WriteFile()、DeviceIoCONTROL()等API函数向设备传递主机请求。Windows系统自动将API调用转化为IRP,设备驱动程序把它向下层驱动传递。直到完成其所指定的功能再沿驱动程序栈返回主机。
WDM还引入了功能设备对象FDO(Functional Device Object)与物理设备对象PDO(Physical Device Object)两个新类来描述硬件,一个PDO对应一个真实硬件。一个硬件只允许有一个PDO,但却可以拥有多个FDO,而在驱动程序中我们不是直接操作硬件而是操作相应的PDO与FDO。驱动程序和设备对象的分层情况如图所示。
其中总线驱动程序(Bus Driver)位于最底层,控制对总线上所有设备的访问,创建PDO代表发现的设备。功能驱动程序(Function Driver)控制设备的主要功能,分层在总线驱动的上面,负责创建FDO。在USB情况下,功能驱动程序必须使用USB类驱动程序访问设备。
九、USB设备驱动程序开发工具
开发USB设备驱动程序需要专门的开发工具,目前应用广泛的工具主要有两大类。开发设备驱动程序一般采用以下几种方法:1)直接使用Windows DDK,这种方法开发难度较大,设计者必须对整个体系结构有很好的理解和把握。2)使用Driver Studio,该工具软件可为设计者提供驱动程序的整体框架,设计者只需要专心于功能代码设计。3)使用win Driver,这种方法开发驱动程序很容易,但工作效率不是很高。
1.Microsoft公司提供的Windows DDK(Device Driver Kit)。
它有Windows 98 DDK和Windows 2000 DDK两个版本。Windows 98 DDK能够开发Windows 95/98/Me/NT下的VxD、KMD和WDM驱动程序。Windows 2000 DDK 能够开发Windows 98/Me/NT/2000下的KMD和WDM驱动程序。由于DDK基于汇编语言的编程方式和内核模式的调用,对没有深厚的OS原理和编程水平的人员来说,任务相当艰巨。
2.NuMega公司提供的DriverStudio。
它是一个大的开发工具包,包含VtoolsD、SoftICE和DriverWorks等开发工具。 VtoolsD开发包提供了对VxD编程的C/C++类库支持,利用VtoolsD中的QuickVxD工具可以快速生成VxD的C/C++代码框架,开发者可以在此基础上根据各自的需要添加自己的代码。DriverWorks用于开发KMD和WDM驱动程序,并且对DDK函数进行了类的封装,从而为开发Windows NT、Windows 2000和Widnwos98 WDM设备驱动程序提供了一个自动化的方法。
DriverWorks,提供了VC++下的开发向导Driver Wizard,按照它的提示可以迅速地生成驱动程序的框架。这个框架结构提供可以正确执行WDM动态环境中IRP的请求,而且,也包含用于简化系统提供的标准类驱动程序(如HID、流)和总线驱动程序(如PCI和USB)接口的类等。总之,利用DriverWorks开发WDM驱动程序,可以大大简化开发人员的工作量、缩短开发周期以及降低开发驱动程序的难度。
十、USB设备驱动程序的设计
使用DriverStudio3.2开发USB设备驱动程序。
该驱动程序的主要功能包括:从控制端点0读取规定个数的数据、向端点0发出控制命令、从端点2批量读数据、向端点2批量写数据,驱动程序的开发采用DriverStudio3.2驱动程序开发包及VC++6.0,使用开发包中的向导程序DriverWizard就可以方便的生成驱动程序框架、模块及部分程序源代码,开发者只需要在功能模块中加入自己的实现程序就能完成复杂的USB设备驱动程序设计。
1. DriverWizard生成一ISP1581驱动程序的过程:
(1)启动DriverWizard,选择DriverWorks Project创造一个名为USBDIO的 VC++项目;
(2)在驱动程序类型中选择WDM Driver,WDM Function Driver,在硬件设备所支持的总线类型中选择USB(WDM Only),在USB Vendor ID(厂商识别码)中填写0741,在USB Product ID(产品识别码)中填写0821;
(3)增加USB设备端点,设置端点2为批量输入/输出传输方式;
(4)在驱动程序支持的功能项中选择Read、Write、Device Control、Cleanup;
(5)选择自动产生批量读及批量写程序代码;
(6)在I/O请求IRP处理方式中选择None,即IRP不排队;
(7)在接口的打开方式中选择Symbolic link:UsbdioDevice,即应用程序以符号链接名打开设备;
(8)定义应用程序调用DeviceIo Control 函数对WDM驱动程序通信的控制命令。
(9)最后选择完成并确认生成新的项目信息,向导程序就会在usbdio目录中生成一个名为USBDIO的项目文件,其中包括了ISP1581驱动程序框架、模块及部分源代码。
2. USB设备驱动程序的编程
在使用DriverWizard生成驱动程序框架、模块及部分程序源代码后,开发者只需完成三个控制代码所对应的三个功能模块的编程:模块USBDIO_IOCTL_ ID_CODE_Handler的功能是从控制端点0读取数据,模块USBDIO_IOCTL_ TEST_COMMAND_Handler的功能是向控制端点0发送一个控制命令,模块USBDIO_IOCTL_DMA_COMMAND _Handler的功能是向控制端点0发送一个要求USB设备进行DMA传输的控制命令,下面是第一个模块的编程实例。
NTSTATUS USBDIODevice::USBDIO_IOCTL_ID_CODE_Handler(KIrp I) {
NTSTATUS status =STATUS_SUCCESS;
t << "Entering USBDIODevice::USBDIO _IOCTL_ID_ CODE_Handler, " << I << EOL;
PURB pUrb;
ULONG numData;
numData=*(PUCHAR)I.IoctlBuffer();
//设置读取的数据个数
pUrb=m_Lower.BuildVendorRequest((PUCHAR)I.IoctlBuffer(),//驱动程序存放读取的数据的内存区
numData,//wLength,读取的数据个数
0,0x0c,//bRequest 0,//wValue
TRUE,//input
TRUE,
NULL,
0x0472,//wIndex,传输到固件程序的读数命令码
URB_FUNCTION_VENDOR_ENDPOINT,
NULL);
if(pUrb==NULL) {
I.Information() =0;
status=STATUS_INSUFFICIENT_
RESOURCES;
} else {
I.Information() =numData;
tatus=m_Lower.SubmitUrb(pUrb,NULL,NULL,0);
delete pUrb;
}
return status;
}
对象I包含了应用程序下传的IRP内容,包括命令或数据等参数,函数BuildVendorRequest用来分配并初始化一个用于厂商请求的URB(USB Request Block),该URB将作为下传IRP的一个参数,通过函数SubmitUrb发送给总线驱动程序,以便完成与硬件的通信。
在初始化URB时需要了解USB的传输方式及传输协议,该功能使用了USB的控制传输方式,该方式包括三个阶段:设置阶段、数据阶段和状态阶段,其中数据阶段可选,开发者主要关注设置阶段中的8个关键字节的定义,8字节分成了5个字段,定义了传输请求及相关信息。
BmRequestType:1字节,用来指定数据流动的方向,请求的类型,以及接收者。
bRequest:1字节,用来指定请求。
wValue:2字节,主机用来传输信息给设备,开发者可以根据情况自己定义。
wIndex:2字节,主机用来传输信息给设备,开发者可以根据情况自己定义。
wLength:2字节,包含数据阶段中接下来要传输的数据字节数目。
十一、USB设备驱动程序的安装及调用
1. USB设备驱动程序的安装
驱动程序编译完成后会生成一个名为USBDIO.SYS的文件,即USB设备驱动程序,另外在使用向导程序WizardDriver生成驱动程序时会产生一个名为USBDIO.INF的驱动程序安装程序,对此程序只需稍做修改就能正常使用,具体是将类改为USB,即Class=USB,由于本驱动程序使用符号链接名打开设备,所以删除ClassGUID选项,注意设备标识符必需为:%DeviceDesc%=USBDIO_DDI, USBVID_0471&PID_0821,其中0471是USB控制芯片的厂商识别码,0821是USB设备标识码。
驱动程序安装过程是:将USB设备加电,连入计算机的USB接口,这时候会看到Windows操作系统提示发现新硬件,提问是否安装驱动程序,选择是,然后选择驱动程序所在文件夹,选择文件USBDIO.INF即可完成安装.
2. USB设备驱动程序的调用
为了完成对驱动程序的调用,使用VC++6.0编写USB应用程序包,程序包共由五个功能模块组成,用户通过调用这些模块即可方便的完成对USB外设的控制及读写,这些模块如下。
int CTRLReadData(unsigned char usbSelect,unsigned char *rbuffer,unsigned char numData),主要功能是读取ISP1581控制端点0发来的数据,数据存放在缓冲区rbuffer中。
int CTRLSendTestCommand (unsigned char usbSelect,unsigned short int testCommand),主要功能是发送测试命令,变量testCommand定义了测试命令。
int CTRLSendDMACommand (unsigned char usbSelect,unsigned char dmaDirection,unsigned char ramSelect,unsigned long dmaLength),主要功能是发送DMA传输命令,变量dmaDirection定义数据传输方向,ramSelect定义将要操作的USB外设的存储器,dmaLength定义了数据传输总数。
int DMARead(unsigned char usbSelect,unsigned char *rbuffer,int len,int waitTime),主要功能是计算机批量读取ISP1581中的数据,而ISP1581以DMA方式从外部RAM读取数据。
int DMAWrite(unsigned char usbSelect,unsigned char *rbuffer,int len, int waitTime),主要功能是计算机批量写数据到ISP1581,而ISP1581将以DMA方式写数据到外部RAM。