分类: WINDOWS
2011-11-30 16:15:25
可以从其官方网站上下载DOGUSB的最新版本,当前版本是1.1.1。或者在下面在下面网址下载这个版本的DOSUSB。
http://blog.hengch.com/software/dosusb/dosusb.zip
DOSUSB可以在非商业领域免费使用,如果肯花费费用,可以购买到源代码,从其官方网站的论坛上看到,在2006年9月作者开出的源代码的价格是1000欧元。
DOSUSB的安装十分简便,只需要解压缩到某一个目录下即可,比如放在c:\dosusb目录下,请自行阅读DOSUSB自带的文档,使用也非常简单,在DOS提示符下键入dosusb执行即可。
c:\dosusb>dosusb
缺省情况下,DOSUSB使用int 65h作为其驱动的调用软中断,如果和你的系统有冲突,在运行dosusb时可以加参数/I,请自行阅读DOSUSB的文档。
DOSUSB通过一个叫做URB(USB Request Block)的数据结构与客户端驱动程序进行通讯,这一点和linux非常相似,估计作者参考了linux下的源代码,在DOSUSB文档里给出了这个结构的定义,如下:
struct {
BYTE transaction_type; // 设置事务(控制传输)(2Dh),输入事务(69h)输出事务(E1h)
BYTE chain_end_flag; // 备用
BYTE dev_add; // 设备地址
BYTE end_point; // 端点号
BYTE error_code; // 错误吗
BYTE status; // 设备状态
WORD transaction_flags; // 备用
WORD buffer_off; // 接收/发送缓冲区偏移地址
WORD buffer_seg; // 接收/发送缓冲区段地址
WORD buffer_length; // 接收/发送缓冲区长度
WORD actual_length; // 接收/发送时每个包的最大长度
WORD setup_buffer_off; // setup_request结构的偏移地址
WORD setup_buffer_seg; // setup_request结构的段地址
WORD start_frame; // 备用
WORD nr_of_packets; // >0时会启动实时传输
WORD int_interval; // 备用
WORD error_count; // 重试的次数
WORD timeout; // 备用
WORD next_urb_off; // 备用
WORD next_urb_seg; // 备用
} urb // 32字节
之所以列出这个结构,是因为我们将使用这个结构与USBD进行交互。关于结构中字段的定义,在DOSUSB的文档中有详细的说明。除备用字段不需要填以外,error_code和status由DOSUSB返回,故填0即可,后面还会介绍更详细的填写方法。
在DOSUSB的发行包中,有一个sample目录,里面有很多例子,但大多是使用power basic写的,不过仍然有很好的参考价值。
3、USB 1.1协议中的一些内容
USB协议为USB设备定义了一套描述设备功能和属性的固有结构的描述符,包括标准描述符(设备描述符、配置描述符、接口描述符、端点描述符和字符串描述符),还有非标准描述符,如类描述符等。按照协议,设备描述符下可以有若干个配置描述符,每个配置描述符可以有若干个接口描述符,每个接口描述符下又可以有若干个端点描述符,字符串描述符主要用于描述一些文字信息,比如厂家名称,产品名称等。这篇文章的目的就是要读出这些描述符。
实际上,所谓描述符就是一个数据结构,不管是什么描述符,其前两个字节的含义都是一样的,第一个字节是描述符的长度,第二个字节是描述符的类型。
struct {
BYTE bLength; // 描述符的长度,以字节为单位
BYTE bDescriptorType; // 设备描述符类型,0x01
WORD bcdUSB; // 设备支持的USB协议版本,BCD码
BYTE bDeviceClass; // 设备类代码(由USB-IF分配)
BYTE bDeviceSubClass; // 子类代码
BYTE bDeviceProtocol; // 协议码
BYTE bMaxPacketSize0; // 端点0的最大包长度(仅为8,16,32,64)
WORD idVendor; // 厂商ID(由USB-IF分配)
WORD idProduct; // 产品ID(由制造商定义)
WORD bcdDevice; // 设备发行号(BCD码)
BYTE iManufacture; // 描述厂商信息的字符串描述符的索引值
BYTE iProduct; // 描述产品信息的字符串描述符的索引值
BYTE iSerialNumber; // 描述设备序列号信息的字符串描述符的索引值
BYTE bNumConfigurations; // 可能的配置描述符的数目
} device_descriptor
struct {
BYTE bLength; // 描述符的长度,以字节为单位
BYTE bDescriptorType; // 配置描述符类型,0x02
WORD wTotalLength; // 配置信息的总长
BYTE bNumInterfaces; // 该配置所支持的接口数目
BYTE bConfigurationValue; // 被SetCongiguration()请求用做参数来选定该配置
BYTE bConfiguration; // 描述该配置的字符串描述符的索引值
BYTE bmAttributes; // 配置特性
BYTE MaxPower; // 该配置下的总线电源耗费量,以2mA为一个单位
}configuration_descriptor;
bmAttributes :b7:备用,b6:自供电,b5:远程唤醒,b4--b0:备用
另外,在读取配置描述符时可以把该配置下的所有描述符全部读出,这些描述符的总长度就是wTotalLength字段的值,读出所有描述符后,在一个一个地拆分。
struct {
BYTE bLength; // 描述符的长度,以字节为单位
BYTE bDescriptorType; // 接口描述符类型,0x04
BYTE bInterfaceNumber; // 接口号,从0开始
BYTE bAlternateSetting; // 可选设置的索引值.
BYTE bNumEndpoints; // 此接口的端点数量。
BYTE bInterfaceClass; // 接口类编码(由USB-IF分配)
BYTE bInterfaceSubClass; // 接口子类编码(由USB-IF分配)
BYTE bInterfaceProtocol; // 协议码(由USB-IF分配)
BYTE iInterface; // 描述该接口的字符串描述符的索引值
}interface_descriptor;
bInterfaceClass:USB协议根据功能将不同的接口划分成不同的类,如下:
1:音频类,2:CDC控制类,3:人机接口类(HID),5:物理类,6:图像类,7:打印机类,8:大数据存储类,9:集线器类,10:CDC数据类,11:智能卡类,13:安全类,220:诊断设备类,224:无线控制类,254:特定应用类,255厂商定义的设备。
struct {
BYTE bLength; // 描述符的长度,以字节为单位
BYTE bDescriptorType; // 端点描述符类型,0x05
BYTE bEndpointAddress; // 端点地址
BYTE bmAttributes; // 在bconfigurationValue所指的配置下的端点特性.
WORD wMaxPacketSize; // 接收/发送的最大数据报长度.
BYTE bInterval; // 周期数据传输端点的时间间隙.
}endpoint_descriptor;
bmAttributes:bit 1:0--传送类型,00=控制传输,01=实时传输,10=批量传输,11=中断传输;所有其他位均保留。
struct {
BYTE bLength; // 描述符的长度,以字节为单位
BYTE bDescriptorType; // 字符串描述符类型,0x03
char bString[]; // UNICODE编码的字符串
}string_descriptor;
为了更好地协调USB主机与设备之间的数据通信,USB规范定义了一套命令请求,用于完成主机对总线上所有USB设备的统一控制,USB命令请求由统一的格式,其结构如下:
struct {
BYTE bmRequestType; // 请求类型
BYTE bRequest; // 命令请求的编码
WORD wValue; // 命令不同,含义不同
WORD wIndex; // 命令不同,含义不同
WORD wLength; // 如果有数据阶段,此字段为数据字节数
} setup_request;
后面我们向设备发出指令就全靠这个结构了。作为我们本文要用到的读取USB设备描述符的命令请求,该结构各字段的填法如下。
bmRequestType : b7--数据传输方向,0=主机到设备,1=设备到主机;b6:5--命令的类型,0=标准命令,1=类命令,2=厂商提供的命令,3=保留;b4:0--接收对象,0=设备,1=接口,2=端点,3=其他。我们发出的得到描述符的命令,数据传输方向为设备到主机,标准命令,接收对象为设备,所以该字段填0x80。
bRequest : 标准命令的编码如下,GET_STATUS=0;CLEAR_FEATURE=1;SET_FEATURE=3;SET_ADDRESS=5;GET_DESCRIPTOR=6;SET_DESCRIPTOR=7;GET_CONFIGURATION=8;SET_CONFIGURATION=9;GET_INTERFACE=10;SET_INTERFACE=11;SYNCH_FRAME=12。我们的命令是GET_DESCRIPTOR,所以应该填6。
wValue : 高字节表示描述符的类型,0x01=设备描述符,0x02=配置描述符,0x03=字符串描述符,0x04=接口描述符,0x05=端点描述符,0x29=集线器类描述符,0x21=人机接口类描述符,0xFF=厂商定义的描述符。
低字节表示表示描述符的索引值。所以,当读取设备描述符时,该字段值为0x100,当读取配置描述符是,应为0x03yy,其中yy为描述符的索引值。
wIndex : 当读取字符串描述符时,填0x0409,表示希望获得英文字符串,其他情况下填0。
wLength : 数据长度,一般应该填写,描述符的第一个字节,bLength。由于我们在读描述符时,并不知道其实际长度,通常的做法是先读取8个字节,然后根据第一个字节bLength的值在重新读取一次完整的描述符;注意,当读取配置描述符的钱8个字节后,应该使用wTotalLength字段的值作为长度读取与该配置有关的所有描述符。
4、读取设备描述符的范例程序
按照我们文章的习惯,几乎每篇文章都有一个范例程序,本文也不例外,本文的范例程序请在下面地址下载:
http://blog.hengch.com/source/usbview.zip
程序用C++写成,在DJGPP下编译通过,所以是32位保护模式下的代码,要注意的是,DOSUSB是实模式下的驱动,所以在申请内存块时要申请1M以内实模式可以读取的内存,否则,在使用int 65h调用DOSUSB时一定会出现问题。
有4个头文件,public.h中定义了一些方便使用的数据类型,比如BYTE为char,WORD为short int等等,可以不必太关注;x86int.h中定义了调用DOS中断所需的函数和数据结构,直到怎么使用就可以了;dosmem.h中定义了一个DOS_MEM类,主要是为了在保护模式下申请和使用DOS内存块更为方便,也是知道其中的方法,能够明白程序中的意义就可以了;usb.h定义了与USB协议有关的所有常数,这些常数与前面介绍的各种数据结构一一对应,由于我们是在保护模式下使用DOS内存,所以把一个内存块映射到一个数据结构上有一些麻烦,读取各个字段主要靠在usb.h中定义的这些常数。
主要程序在usbview.cc中,主要思路如下: