USB系列之一:列出你的USB设备

2008-05-23 11:04:56

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.whowin.net/specification/usb_ohci_r10a.pdf
  • EHCI规范:http://blog.whowin.net/specification/usb_ehci_r10.pdf
  • USB规范1.1:http://blog.whowin.net/specification/usb_spec11.pdf
  • USB规范2.0:http://blog.whowin.net/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设备的源程序。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#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);
    return 0;
}

程序非常简单,所有概念在以前的博文中均有过介绍,其中的子程序大多是以前程序范例中使用过的,所以在这里就不做更多的解释了,程序中,我们仅仅列出了设备的数量,但很显然,用这种方法,我们可以从配置空间里读出基地址等信息,这些在以后的文章中会用到。