Board logo

标题: [原创]遍历PCI设备 [打印本页]

作者: whowin     时间: 2008-5-9 11:33    标题: [原创]遍历PCI设备

文章是从我的网志中贴过来的,其中的图片可能过不来,看完整内容,请访问我的网志:
点击进入《DOS编程技术》

1、安装环境

    主板:GX1-C19工控主板,AMD GX1 233MHz CPU,64M内存,8GB IBM 3.5英寸硬盘,支持USB

    软件:安装前,硬盘中已安装完整的DOS 6.22,DOS下的USB驱动
              我的config.sys文件内容如下:

                     DEVICE=C:\DOS\SETVER.EXE

                     DEVICE=C:\DOS\HIMEM.SYS

                     DOS=HIGH

                     FILES=30

                     LASTDRIVE=Z

                     DEVICE=C:\USB\ASPIOHCI.SYS

                     DEVICE=C:\USB\DI1000DD.SYS

              我的autoexec.bat文件内容如下:

                     @ECHO OFF

                     PROMPT=$P$G

                     set PATH=C:\DOS

                     set TEMP=C:\DOS

                     doskey

              USB驱动程序:如果您需要,请与我联系:hengch@263.net

2、下载所需的DJGPP安装包

    * 打开网页:http://www.delorie.com/djgpp/zip-picker.html

    * 做如下选择,其中下划线部分为我的选择,其他为提示。

FTP Site
          Select a suitable FTP site:
                Pick one for me


Basic Functionality
           Pick one of the following:
                Build and run programs with DJGP

    Which operating system will you be using?
                MS-DOS

    Do you want to be able to read the on-line documentation?
                Yes

    Which programming languages will you be using?
                C
                C++
                Assembler

    Which IDE(s) would you like?
                RHIDE, similar to Borland's IDE, including a built-in editor and debugger.

    Would you like gdb, the text-mode GNU debugger? You don't need it if you get RHIDE.
                No

Extra Stuff

    Please check off each extra thing that you want.

    选择完毕后点击“Tell me which files I need”按钮,则给出如下内容:

         unzip32.exe to unzip the zip files 95 kb

         v2/copying.dj DJGPP Copyright info                      3 kb
         v2/djdev203.zip DJGPP Basic Development Kit     1.5 mb
         v2/faq230b.zip Frequently Asked Questions          664 kb
         v2/readme.1st Installation instructions                   22 kb

         v2apps/rhid15ab.zip RHIDE                                    6.0 mb

         v2gnu/bnu217b.zip Basic assembler, linker             3.9 mb
         v2gnu/gcc423b.zip Basic GCC compiler                  4.3 mb
         v2gnu/gpp423b.zip C++ compiler                            4.5 mb
         v2gnu/mak3791b.zip Make (processes makefiles)   267 kb
         v2gnu/txi411b.zip Info file viewer                              888 kb

  Total bytes to download:                        23,102,842

大致需要下载23MB的安装包
3、安装

下载内容通过U盘放到硬盘中。

* 在硬盘中建立目录:c:\djgpp

    c:\>md djgpp

* 拷贝安装包到c:\djgpp下
            c:\copy g:. c:\djgpp          (我的USB盘为g)

* 解压缩所有安装包

    c:\>cd\djgpp

    c:\djgpp>unzip32 *.zip

    这个过程比较长,请耐心等待。

* 修改配置

    c:\djgpp>cd\

    c:\>edit autoexec.bat

    增加一行:set DJGPP=C:\DJGPP\DJGPP.ENV

    把原来的:set PATH=c:\DOS 改成:set PATH=c:\DOS;c:\djgpp\bin

    存盘退出。

* 重新启动

* 测试DJGPP的安装情况

    重新启动后

    c:\>go32-v2

    显示错误提示:Load error: no DPMI - Get csdpmi*b.zip

    这是因为go32-v2.exe这个程序是32位保护模式下的程序,而我们没有安装DPMI服务,所以不能运行。

* 下载v2misc/csdpmi5b.zip

    下载地址:http://www.delorie.com/pub/djgpp/current/v2misc/csdpmi5b.zip

* 安装CWSDPMI

    将下载的cwdpmi5b.zip拷贝到硬盘的c:\djgpp下:

    c:\>copy g:cwdpmi5b.zip c:\djgpp

    解压缩

    c:\>cd djgpp

    c:\djgpp>unzip32 cwdpmi.zip

* 再次测试DJGPP

    c:\djgpp>cd\

    c:\>go32-v2

    我们得到提示:

    DPMI memory available: 62401 kb

    DPMI swap space available: 129919 kb

    说明DJGPP安装成功。


4、其他

    安装过程中,实际上我们已经安装了一个开发环境:rhide

    可以这样测试rhide已经安装成功:

        c:\>rhide

    你可以看到一个类似turbo C的界面,这就是RHIDE,rhide和djgpp配合十分默契。

    至此,你已经可以开发C/C++的32位保护模式下的程序了。

更多关于DOS编程的文章看我的网志

点击进入《DOS编程技术》
PC机在启动的时候,都会看到一个PCI设备清单,可以看到机器中的所有PCI设备,其实搜索PCI设备的程序并不难编,本文通过一个实例说明如何遍历PCI设备。

    工作环境:MS-DOS 6.22,djgpp+RHIDE

1、了解PCI设备

    PCI的含义是外设部件互连(Peripheral Component Interconnect),PCI局部总线(Local Bus)是1991年由Intel定义的,现在PCI局部总线已经成为了PC机中不可缺少的外围设备总线,几乎所有的外部设备都连接到PCI局部总线上,我们说的PCI设备,实际上就是指连接在PCI局部总线上的设备。

2、你的BIOS是否支持PCI BIOS服务

    在我的另一篇网志文章:32位BIOS说明(http://hengch.blog.163.com/blog/static/10780067200821801532871/)中,说明了如何判断BIOS是否是32位的,以及是否支持PCI BIOS服务,实际上,并没有那么麻烦,因为在绝大多数有PCI插槽的PC机中,BIOS都是32位的,同时也是支持PCI BIOS的,在我的另一篇网志文章:调用PCI BIOS(http://hengch.blog.163.com/blog/static/10780067200821814231310/)中,介绍了PCI BIOS中常用的功能调用,其中的第一个调用就是检查“PCI BIOS的存在性”,通常我们利用这个功能调用来作为PCI BIOS的存在检测已经足够了,方法如下:

    汇编语言:

        MOV  AX, 0B01H
        INT 1AH

    C语言(DJGPP下):

        long unsigned int    i;
        __dpmi_regs           r;

        r.x.ax = 0xb101;
        __dpmi_int(0x1a, &r);
        i = r.x.flags;
        if ((i & 0x01) == 0) printf("\nSupport PCI BIOS");
        else printf("\nNot Suport PCI BIOS");

    根据返回参数就可以判断PCI BIOS是否存在,具体方法请参阅:调用PCI BIOS(http://hengch.blog.163.com/blog/static/10780067200821814231310/)这篇网志文章,如果你的BIOS不支持PCI BIOS,那么本文介绍的方法可能不完全适用你。

3、了解PCI配置空间

    学习PCI编程,不了解PCI的配置空间是不可能的,配置空间是一块容量为256字节并具有特定记录结构或模型的地址空间,通过配置空间,我们可以了解该PCI设备的一些配置情况,进而控制该设备,除主总线桥以外的所有PCI设备都必须事先配置空间,本节仅就一些配置空间的共有的规定作一些说明,更加具体和详细的信息请参阅其他书籍及相应的芯片手册。

    配置空间的前64个字节叫头标区,头标区又分成两个部分,第一部分为前16个字节,在各种类型的设备中定义都是一样的,其他字节随各设备支持的功能不同而有所不同,位于偏移0EH的投标类型字段规定了是何种布局,目前有三种头标类型,头标类型1用于PCI-PCI桥,头标类型2用于PCI-CARDBUS桥,头标类型0用于其他PCI设备,下图为头标类型0的头标区布局。



    头标区中有5个字段涉及设备的识别。

    (1) 供应商识别字段(Vendor ID)
        该字段用一标明设备的制造者。一个有效的供应商标识由PCI SIG来分配,以保证它的唯一性。0FFFFH是该字段的无效值。

    (2) 设备识别字段(Device ID)
        用以标明特定的设备,具体代码由供应商来分配。

    (3) 版本识别字段(Revision ID)
        用来指定一个设备特有的版本识别代码,其值由供应商提供,可以是0。

    (4) 头标类型字段(Header Type)
        该字段有两个作用,一是用来表示配置空间头标区第二部分的布局类型;二是用以指定设备是否包含多功能。位7用来标识一个多功能设备,位7为0表明是单功能设备,位7为1表明是多功能设备。位0-位6表明头标区类型。

    (5) 分类代码字段(Class Code)
        标识设备的总体功能和特定的寄存器级编程接口。该字节分三部分,每部分占一个字节,第一部分是基本分类代码,位于偏移0BH,第二部分叫子分类代码,位于偏移0AH处,第三部分用于标识一个特定的寄存器级编程接口(如果有的话)。

        这部分的代码定义很多,请自行查阅PCI规范相关文档。我们在实际应用中会对一些用到的代码加以说明。

4、配置寄存器的读写

    x86的CPU只有内存和I/O两种空间,没有专用的配置空间,PCI协议规定利用特定的I/O空间操作驱动PCI桥路转换成配置空间的操作。目前存在两种转换机制,即配置机制1#和配置机制2#。配置机制2#在新的设计中将不再被采用,新的设计应使用配置机制1#来产生配置空间的物理操作。这种机制使用了两个特定的32位I/O空间,即CF8h和CFCh。这两个空间对应于PCI桥路的两个寄存器,当桥路看到CPU在局部总线对这两个I/O空间进行双字操作时,就将该I/O操作转变为PCI总线的配置操作。寄存器CF8h用于产生配置空间的地址(CONFIG-ADDRESS),寄存器CFCh用于保存配置空间的读写数据(CONFIG-DATA)。

    将要访问配置空间寄存器的总线号、设备号、功能号和寄存器号以一个双字的格式写到配置地址端口 (CF8H-CFBH),接着执行配置数据端口 (CFCH)的读和写,向配置数据口写数据即向配置空间写数据,从配置数据口读数据即从配置空间读数据。

    配置地址端口(CF8H)的格式定义如下:

    bit  32  31...24 23......16 15......11 10......8 7..........2   1  0
            |    保留     总线号     设备号    功能号  寄存器号  0  0
            +----使能位,1有效,0无效

    寄存器号:选择配置空间中的一个双字(32位)
    功能号:选择多功能设备中的某一个功能,有八种功能,0--7
    设备号:在一条给定的总线上选择32个设备中的一个。0--31
    总线号:从系统中的256条总线中选择一条,0--255

    尽管理论上可以有256条总线,但实际上PC机上PCI插槽的总线号都是1,有些工控机的总线号是2或者3,所以我们只需要查找0--4号总线就足够了。

    PCI规范规定,功能0是必须实现的,所以,如果功能0的头标类型字段的位7为0,表明这是一个单功能设备,则没有必要再去查其他功能,否则要查询所有其他功能。

5、遍历PCI设备

    至此,我们掌握的有关PCI的知识已经足够我们遍历PCI设备了,其实便利方法非常简单就是按照总线号、设备号、功能号的顺序依次罗列所有的可能性,读取配置空间头标区的供应商代码、及设备代码,进而找到所有PCI设备。

  printf("\nNo.  Vendor/Device  Bus No.  Dev No.  Func No.  Class");
  i = 0;
  for (busNo = 0; busNo < 5; busNo++) {                 // bus No
    for(deviceNo = 0; deviceNo < 32; deviceNo++) {      // device no
      for (funcNo = 0; funcNo < 8; funcNo++) {          // Function No
        //j = 0x80000000 + i * 2048;
        regVal = 0x80000000                             // bit31 使能
                 + (busNo << 16)                        // Bus No
                 + (deviceNo << 11)                     // Device No
                 + (funcNo << 8);                       // Function No
        outportl(0xCF8, regVal);
        retVal = inportl(0xCFC);                        // 得到配置空间偏移为0的双字
        if (retVal != 0xffffffff) {                     // 设备存在
          i++;
          vendorID = retVal & 0xffff;                   // 得到供应商代码
          devID    = (retVal >> 16) & 0xffff;           // 得到设备代码
          regVal += 0x08;                               // 得到配置空间偏移为08H的双字
          outportl(0xCF8, regVal);
          retVal = inportl(0xCFC);
          retVal = retVal >> 8;                         // 滤掉版本号
          class3 = retVal & 0x0FF;                      // 得到三个分类代码
          class2 = (retVal >> 8) &0x0FF;
          class1 = (retVal >> 8) &0x0FF;
          printf("\n%02d   %04x/%04x       %02x       %02x       %02x      %02x-%02x-%02x",
                 i, vendorID, devID, busNo, deviceNo, funcNo, class1, class2, class3);
          if (funcNo == 0) {                          // 如果是单功能设备,则不再查funcNo>0的设备
            regVal = (regVal & 0xFFFFFFF0) + 0x0C;
            outportl(0xCF8, regVal);
            retVal = inportl(0xCFC);
            retVal = retVal >> 16;
            if ((retVal & 0x80) == 0) funcNo = 8;
          }
        }
      }
    }
  }

    两部分程序加起来基本构成了一个完整的遍历PCI设备的程序,如需完整源代码,请与我联系,本代码可以DJGPP下编译通过并正常执行(测试环境:DOS 6.22 DJGPP2.2+RHIDE1.5)。

更多关于DOS编程的文章看我的网志

点击进入《DOS编程技术》

[ Last edited by whowin on 2008-5-9 at 11:46 AM ]
作者: netwinxp     时间: 2008-5-9 21:18    标题: [原创]检索PCI 2.0设备HWID

我在无忧发表过一个在DOS,依据硬件库匹配PCI设备驱动的程序,硬件库可以后来定制(建议link /tiny生成.com文件,酱紫可以直接用UE把硬件库插到HWDBSTART:后面),每条硬件记录以$分割,硬件库以ASC(FF)为结束,硬件hwid和xp一致。方法和楼主差不多(其实Func num通常在r/w数据的时候用,一般检测不考虑,还是让所有设备都显示为佳,事实上windows也是酱紫的,有多个内存控制器、OHCIUSB或UHCIUSB控制器,另外不同版本号有不兼容的情况不应被忽略)。所以顺便把源码贴上(为了方便他人,已删除未匹配硬件的显示)。最近正在研究ACPI,发现不懂的东西太多了,想抄都没得抄@_@。
**采用读一条PCI硬件匹配一次方法,主要考虑,当透过桥接长时间读取(BusNum不为0通常就是通过桥接片)有可能会导致其他任务被堵塞,还有pcicfg对齐4的倍数就是防止内存访问被掰成两个甚至四个内存访问周期,国内目前的读取pcicfg程序大多有BUG**
;文件名:CHKPCI.ASM
;作者:netwinxp
;免责任声明:CHKPCI是个开放源码的自由软件,注意事项请参照http://www.gnu.org/licenses/
;         本人不对使用或修改该软件而可能导致的任何问题负责(即使事先被告知)。
;编译环境:masm6.11
;使用平台:DOS
.model tiny
.386
code    segment
          assume cs:code,ds:code,es:code
org     100H
START:  mov  ax,cs
        mov  ds,ax
        mov  es,ax
        mov  ax,0B101H;支持PCI 2.0?
        int  1AH
        jc   PCIERR;否
        cmp  ah,0
        jne  PCIERR;否
        cmp  edx,' ICP'
        jne  PCIERR;否
;========读取PCI配置空间数据
        mov  ecx,7FFFFF00H
READPCI:add  ecx,100H
        cmp  ecx,80FFFF00H;全读完了?
        jnb  EXIT;是
        mov  dx,0CF8H
        mov  eax,ecx
        cli
        out  dx,eax
        mov  dx,0CFCH
        in   eax,dx
        sti
        cmp  eax,0FFFFFFFFH;无效设备?
        je   READPCI;是
        push ecx
        mov  di,offset venid
        cld
PCISTR: mov  eax,ecx
        mov  dx,0CF8H
        cli
        out  dx,eax
        mov  dx,0CFCH
        insd
        sti
        add  cl,4
        cmp  cl,0FCH;读完256字节数据?
        jb   PCISTR;否
;=======查找匹配的记录
        mov  bx,offset hwdb
L0:     xor  di,di;指向新记录首字节
        mov  chkok,di;匹配标志清零
L1:     mov  al,[bx][di]
        cmp  al,0FFH;读完所有记录?
        je   UNKNOW;是
        mov  dx,chkok
        cmp  al,'$';本记录尾部?
        jne  L3;否
        cmp  dx,1;该记录匹配?
        je   DISP;是
L2:     inc  di
        add  bx,di;bx指向下一记录
        jmp  L0
L3:     cmp  dx,0FFFFH;本记录不匹配?
        jne  L5;否。是的话直到下一条记录之前不再进行匹配尝试
L4:     inc  di
        jmp  L1
L5:     cmp  al,'_';有可能是硬件信息?
        jne  L4;否
        mov  eax,[bx][di-3]
        cmp  eax,'_NEV';后面的是VEN信息?
        je   CHKVEN
        cmp  eax,'_VED';后面的是DEV信息?
        je   CHKDEV
        cmp  eax,'_SYS';后面的是SUBSYS信息?
        je   CHKSUB
        cmp  eax,'_VER';后面的是REV信息?
        je   CHKREV
        and  eax,0FFFFFF00H
        cmp  eax,5F434300H;高三位为'_CC';后面的是CLASS信息?
        jne  L4;都不是
CHKCC:  mov  eax,[bx][di+1]
        call asctohex
        mov  cx,classid
        cmp  ax,cx
        jz   L12
        jmp  L11
CHKVEN: mov  eax,[bx][di+1]
        call asctohex
        mov  cx,venid
        cmp  ax,cx;与检测到的venid比较
        jz   L12
        jmp  L11
CHKDEV: mov  eax,[bx][di+1]
        call asctohex
        mov  cx,devid
        cmp  ax,cx;与检测到的devid比较
        jz   L12
        jmp  L11
CHKSUB: mov  eax,[bx][di+5]
        call asctohex
        push di
        mov  di,offset subsys
        mov  cx,[di]
        pop  di
        cmp  ax,cx;与检测到的subdid比较
        jnz  L11
        mov  eax,[bx][di+1]
        call asctohex
        push di
        mov  di,offset subsys
        mov  cx,[di+2]
        pop  di
        cmp  ax,cx;与检测到的subvid比较
        jnz  L11
        jmp  L12
CHKREV: mov  eax,30303030H;为了兼容子过程的处理
        mov  ax,[bx][di+1]
        call asctohex
        mov  cl,revid
        cmp  ah,cl;与检测到的rid比较
        jz   L12
L11:    mov  dx,0FFFFH
        mov  chkok,dx;填充不匹配标志
        jmp  L4
L12:    mov  dx,1;填充匹配标志
        mov  chkok,dx
        jmp  L4
DISP:   mov  dx,bx
        mov  ah,9;主要方便输出转向。否则的话稍作修改可以脱离DOS运行。
        int  21H
UNKNOW: pop  ecx;跳过未匹配设备
        jmp  READPCI
PCIERR: mov  ax,4C01H;非PCI2.0以上版本返回ERRORLEVEL=1
        int  21H
EXIT:   mov  ax,4C00H
        int  21H
;把eax的四字节HEX字符串转换成ax数据
asctohex proc near;会破坏edx、ecx、eax内容
;输入:eax待转换值
;返回:ax
        push bx
        mov  cx,4
        mov  edx,eax
        xor  eax,eax
S11:    mov  bl,dl
        cmp  bl,'9';不做更多判断请务必保证待匹配字串由'0-9、A-F、a-f'组成
        jbe  S12
        and  bl,0DFH;转大写字母
        sub  bl,7
S12:    and  bl,0FH
        shl  ax,4
        add  al,bl
        shr  edx,8
        loop S11
        pop  bx
        ret
asctohex endp
;       db 'XXX';用来使后面对齐4的倍数
        venid            dw   0;pcicfg offset 00H
        devid            dw   0;pcicfg offset 02H
        command_reg      dw   0
        status_reg       dw   0
        revid            db   0;pcicfg offset 08H
        progif           db   0;pcicfg offset 09H
        classid          dw   0;pcicfg offset 0AH
        cacheline_size   db   0
        latency          db   0
        headtype         db   0;pcicfg offset 0EH,=00时SUBSYS有效
        bist             db   0
        baseaddr0        dd   0
        baseaddr1        dd   0
        baseaddr2        dd   0
        baseaddr3        dd   0
        baseaddr4        dd   0
        baseaddr5        dd   0
        cardbus_cis      dd   0
        subsys           dd   0;pcicfg offset 2CH
        expansion_ROM    dd   0
        cap_ptr          db   0
        reserved1        db   3 dup(0)
        reserved2        dd   0
        interrupt_line   db   0
        interrupt_pin    db   0
        min_grant        db   0
        max_latency      db   0
        specific         db   192 dup(0)
chkok     dw 0;匹配判定标志
hwdbstart db 'HWDBSTART:'
hwdb      db 16 dup(' ')
hwdbend   db 0FFH
code      ends
end       START
下面是nforce3-250和mcp51磁盘硬件库示范:

  Quote:
PCI\VEN_10DE&DEV_00E5="pciide";CK8S PATA
$GenNvRaidDisk="nvraid"
*_NVRAIDBUS="nvraid"
PCI\VEN_10DE&DEV_00E3="nvatabus";CK8S SATA
$GenNvRaidDisk="nvraid"
*_NVRAIDBUS="nvraid"
PCI\VEN_10DE&DEV_00EE="nvatabus";CK8S SATA
$PCI\VEN_10DE&DEV_0265="pciide";MCP51 PATA
$SCSI\NVIDIA__Raid_Disk________="nvrd32"
SCSI\__NVIDIA_______Raid_Disk="nvrd32"
SCSI\NVIDIA__Raid_Disk_20_____="nvrd32"
SCSI\__NVIDIA____Raid_Disk_20="nvrd32"
*NVRAID20="nvrd32"
*NVRAIDBUS="nvraid32"
*_NVRAIDBUS="nvraid32"
GenNvRaidDisk="nvraid32"
PCI\VEN_10DE&DEV_0266="nvgts";MCP51 SATA
$SCSI\NVIDIA__Raid_Disk________="nvrd32"
SCSI\__NVIDIA_______Raid_Disk="nvrd32"
SCSI\NVIDIA__Raid_Disk_20_____="nvrd32"
SCSI\__NVIDIA____Raid_Disk_20="nvrd32"
*NVRAID20="nvrd32"
*NVRAIDBUS="nvraid32"
*_NVRAIDBUS="nvraid32"
GenNvRaidDisk="nvraid32"
PCI\VEN_10DE&DEV_0267="nvgts";MCP51 SATA
$

[ Last edited by netwinxp on 2008-5-11 at 02:10 PM ]