USB现在已经成为PC机必不可少的接口之一,几乎所有的设备都可以接在USB设备上,USB键盘、鼠标、打印机、摄像头,还有常用的U盘等等,从本篇文章开始,将集中篇幅介绍一下在DOS中使用USB设备的方法,具体会有几篇暂不好定,写到哪里算哪里吧,三、四篇总是少不了的。
本文介绍如何使用我以前文章中介绍过的知识在你的机器中找到USB设备,并判定设备类型。
一个USB系统一般由一个USB主机(HOST)、一个或多个USB集线器(HUB,但不是局域网里的集线器)和一个或多个USB设备节点(NODE)组成,一个系统中只有一个HOST,我们PC机里的USB实际上就是HOST和HUB两部分,你的PC机可能会有4个USB口,其实是一个HOST,一个HUB,HUB为你提供了4个端口,我们插在USB口上的器件,一般是USB设备,比如U盘,USB打印机等,当然我们也可以插一个集线器上去,使你的一个USB口扩展成多个。
实际上我们说在DOS下使用USB,就是对USB系统中的HOST进行编程管理,根据USB的规范,HOST将对连接在上面的HUB和USB设备进行管理,不用我们操心。HOST器件目前有三个规范,OHCI(Open Host Controller Interface)、UHCI(Universal Host Controller Interface)支持USB1.1,EHCI(Enhanced Host Controller Interface)支持USB2.0,以后的文章中,我们将侧重介绍OHCI和EHCI。
学习USB编程,读规范是少不了的,以下是一些应该阅读的规范下载:
OHCI规范:http://blog.hengch.com/specification/usb_ohci_r10a.pdf
EHCI规范:http://blog.hengch.com/specification/usb_ehci_r10.pdf
USB规范1.1:http://blog.hengch.com/specification/usb_spec11.pdf
USB规范2.0:http://blog.hengch.com/specification/usb_spec20.pdf
本文介绍的内容不需要学习规范。
下面进入正题,列出你的USB设备,USB的HOST是挂接在PCI总线上的,所以通过PCI设备的遍历就可以找到你的机器上的所有USB设备,在以前介绍PCI的配置空间时,曾经介绍过在配置空间中有一个占三个字节的分类代码字段(如果不知道,请参阅我以前的博文《遍历PCI设备》),在偏移为0x0B的字节叫基本分类代码,在偏移为0x0A的字节叫子分类代码,在偏移为0x09的字节叫编程接口代码,对于USB设备类说,基本分类代码为0x0C,子分类代码为0x03,对于符合不同规范的HOST器件而言,编程接口代码是不同的,UHCI的编程接口代码是0x00,OHCI的编程接口代码是0x10,EHCI的编程接口代码是0x20,我想了解这些就足够了。
下面列出USB设备的源程序。
#include <stdio.h> #include <stdlib.h> #include <dpmi.h> typedef unsigned long UDWORD; typedef short int WORD; typedef unsigned short int UWORD; typedef unsigned char UBYTE; typedef union { struct { UDWORD edi; UDWORD esi; UDWORD ebp; UDWORD res; UDWORD ebx; UDWORD edx; UDWORD ecx; UDWORD eax; } d; struct { UWORD di, di_hi; UWORD si, si_hi; UWORD bp, bp_hi; UWORD res, res_hi; UWORD bx, bx_hi; UWORD dx, dx_hi; UWORD cx, cx_hi; UWORD ax, ax_hi; UWORD flags; UWORD es; UWORD ds; UWORD fs; UWORD gs; UWORD ip; UWORD cs; UWORD sp; UWORD ss; } x; struct { UBYTE edi[4]; UBYTE esi[4]; UBYTE ebp[4]; UBYTE res[4]; UBYTE bl, bh, ebx_b2, ebx_b3; UBYTE dl, dh, edx_b2, edx_b3; UBYTE cl, ch, ecx_b2, ecx_b3; UBYTE al, ah, eax_b2, eax_b3; } h; } X86_REGS; /************************************************************* * Excute soft interrupt in real mode *************************************************************/ int x86_int(int int_num, X86_REGS *x86_reg) { __dpmi_regs d_regs; int return_value; d_regs.d.edi = x86_reg->d.edi; d_regs.d.esi = x86_reg->d.esi; d_regs.d.ebp = x86_reg->d.ebp; d_regs.d.res = x86_reg->d.res; d_regs.d.ebx = x86_reg->d.ebx; d_regs.d.ecx = x86_reg->d.ecx; d_regs.d.edx = x86_reg->d.edx; d_regs.d.eax = x86_reg->d.eax; d_regs.x.flags = x86_reg->x.flags; d_regs.x.es = x86_reg->x.es; d_regs.x.ds = x86_reg->x.ds; d_regs.x.fs = x86_reg->x.fs; d_regs.x.gs = x86_reg->x.gs; d_regs.x.ip = x86_reg->x.ip; d_regs.x.cs = x86_reg->x.cs; d_regs.x.sp = x86_reg->x.sp; d_regs.x.ss = x86_reg->x.ss; return_value = __dpmi_int(int_num, &d_regs); x86_reg->d.edi = d_regs.d.edi; x86_reg->d.esi = d_regs.d.esi; x86_reg->d.ebp = d_regs.d.ebp; x86_reg->d.res = d_regs.d.res; x86_reg->d.ebx = d_regs.d.ebx; x86_reg->d.ecx = d_regs.d.ecx; x86_reg->d.edx = d_regs.d.edx; x86_reg->d.eax = d_regs.d.eax; x86_reg->x.flags = d_regs.x.flags; x86_reg->x.es = d_regs.x.es; x86_reg->x.ds = d_regs.x.ds; x86_reg->x.fs = d_regs.x.fs; x86_reg->x.gs = d_regs.x.gs; x86_reg->x.ip = d_regs.x.ip; x86_reg->x.cs = d_regs.x.cs; x86_reg->x.sp = d_regs.x.sp; x86_reg->x.ss = d_regs.x.ss; return return_value; } /********************************** * Read Configuration WORD if PCI **********************************/ UWORD ReadConfigWORD(WORD pciAddr, int reg) { X86_REGS inregs; inregs.x.ax = 0xB109; // Read Configuration word inregs.x.bx = pciAddr; inregs.x.di = reg; // Register number x86_int(0x1A, &inregs); return inregs.d.ecx; // the value } // main program int main(void) { UWORD pciAddr; UWORD subClass; int ehciCount = 0, ohciCount = 0, uhciCount = 0; for (pciAddr = 0; pciAddr < 0xffff; pciAddr++) { if (ReadConfigWORD(pciAddr, 0) != 0xFFFF) { // Read Class Code if (ReadConfigWORD(pciAddr, 0x000a ) == 0x0c03) { // Usb Host Controller // Read SubClass Code subClass = ReadConfigWORD(pciAddr, 0x0008); if ((subClass & 0xff00) == 0x2000) { // uhci ehciCount++; } else if ((subClass & 0xff00) == 0x1000) { // ohci ohciCount++; } else if ((subClass & 0xff00) == 0x00) { // uhci uhciCount++; } } } } printf("There are %d ohci device(s).\n", ohciCount); printf("There are %d ehci device(s).\n", ehciCount); printf("There are %d uhci device(s).\n", uhciCount); }
程序非常简单,所有概念在以前的博文中均有过介绍,其中的子程序大多是以前程序范例中使用过的,所以在这里就不做更多的解释了,程序中,我们仅仅列出了设备的数量,但很显然,用这种方法,我们可以从配置空间里读出基地址等信息,这些在以后的文章中会用到。