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);
}
程序非常簡單,所有概念在以前的博文中均有過介紹,其中的子程序大多是以前程序范例中使用過的,所以在這里就不做更多的解釋了,程序中,我們僅僅列出了設備的數量,但很顯然,用這種方法,我們可以從配置空間里讀出基地址等信息,這些在以后的文章中會用到。