<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁(yè) > 模擬技術(shù) > 設(shè)計(jì)應(yīng)用 > PCI設(shè)備WINDOWS驅(qū)動(dòng)程序的開(kāi)發(fā)

          PCI設(shè)備WINDOWS驅(qū)動(dòng)程序的開(kāi)發(fā)

          作者: 時(shí)間:2012-04-18 來(lái)源:網(wǎng)絡(luò) 收藏
          統(tǒng)BIOS功能調(diào)用,通過(guò)供應(yīng)商識(shí)別號(hào)(VendorID)和設(shè)備識(shí)別號(hào) (DeviceID)直接訪問(wèn)設(shè)備,也可以利用配置管理器(Configuration Manager)封裝的功能函數(shù),根據(jù)供應(yīng)商識(shí)別號(hào)(VendorID)和設(shè)備識(shí)別號(hào) (DeviceID)搜索設(shè)備結(jié)點(diǎn)樹(shù),查詢。由于編寫(xiě)PCI系統(tǒng)BIOS功能調(diào)用程序更為簡(jiǎn)捷,所以本文采用這種方法。
          PCI系統(tǒng)BIOS功能提供了BIOS的訪問(wèn)與控制的具體方法,所有軟件(設(shè)備、擴(kuò)展ROM碼)將通過(guò)標(biāo)準(zhǔn)中斷號(hào)1AH調(diào)用BIOS功能訪問(wèn)特殊部件。在中調(diào)用VtoolsD系統(tǒng)服務(wù)Exec_VxD_Int()來(lái)實(shí)現(xiàn)PCI系統(tǒng)BIOS的1AH中斷。
          首先,通過(guò)的供應(yīng)商識(shí)別號(hào)(VendorID)、 設(shè)備識(shí)別號(hào) (DeviceID)和索引號(hào)(Index)查找特定設(shè)備所在的總線號(hào)(Bus Num)、設(shè)備號(hào)(Device NUM)、功能號(hào)(Function Num)和寄存器號(hào)(Register Num)。總線號(hào)是從0到255的數(shù)值,在一個(gè)系統(tǒng)中,可把多達(dá)256條的PCI總線用橋連接在一起。由于編號(hào)是從0開(kāi)始的,所以當(dāng)系統(tǒng)有N條總線時(shí),總線號(hào)會(huì)達(dá)到N-1;設(shè)備號(hào)是在0到31之間分配的任意值,并不拘于從0開(kāi)始按順序分配;功能號(hào)分配從0到7的值。代碼如下:

          ALLREGS* pRegisters; // pRegisters是指向寄存器結(jié)構(gòu)體的指針
          pRegisters->REAX =0xb102; // 0xb102是功能號(hào)
          pRegisters->RECX =0x1001; // 假設(shè)Device ID=0x1001
          pRegisters->REDX=0x102b; // 假設(shè)Vendor ID=0x102b
          Exec_VxD_Int(0x1a,pRegisters); // 調(diào)用1AH中斷

          返回值是pRegisters->REBX。BH寄存器是總線號(hào),BL寄存器的高5位是設(shè)備號(hào),低3位是功能號(hào)。
          然后,向配置空間地址寄存器CF8h寫(xiě)入總線號(hào)、設(shè)備號(hào)、功能號(hào)、索引號(hào), 從配置空間數(shù)據(jù)寄存器CFCh讀出配置空間的內(nèi)容。
          配置空間地址寄存器(CF8h)格式如下:
          Bit31 30-24 23-16 15-11 10-8 7-2 10
          使能位 保 留 總線號(hào) 設(shè)備號(hào) 功能號(hào) 寄存器號(hào) 00

          使能位為“1”表示允許訪問(wèn)

          配置空間數(shù)據(jù)寄存器(CFCh)存放要讀寫(xiě)的數(shù)據(jù)。
          代碼如下:

          DWORD d=0;
          d=pRegisters->REBX;
          (d=8)|=0x80000000;
          for (short i=0;i16;i++) // 讀取64字節(jié)配置空間
          {
          _outpd(0xcf8,d+4*i); // 按DWORD類(lèi)型一次讀取四個(gè)字節(jié)
          dprintf("%8x",_inpd(0xcfc)); // 打印輸出
          }

          3、內(nèi)存的讀寫(xiě)
          Winsows工作在32位保護(hù)模式下,保護(hù)模式與實(shí)模式的根本區(qū)別在于CPU尋址方式上的不同,這也是Windows設(shè)計(jì)中需要著重解決的問(wèn)題。Windows采用了分段、分頁(yè)機(jī)制,這樣使應(yīng)用程序產(chǎn)生一種錯(cuò)覺(jué),好象程序中可以使用非常大的物理存儲(chǔ)空間。這樣做最大的好處就是一個(gè)程序可以很容易地在物理內(nèi)存容量不一樣的、配置范圍差別很大的計(jì)算機(jī)上運(yùn)行,編程人員使用虛擬存儲(chǔ)器可以寫(xiě)出比任何實(shí)際配置的物理存儲(chǔ)器都大得多的程序。每個(gè)虛擬地址由16位的段選擇字和32位段偏移量組成。通過(guò)分段機(jī)制,系統(tǒng)由虛擬地址產(chǎn)生線性地址。再通過(guò)分頁(yè)機(jī)制,由線性地址產(chǎn)生物理地址(如圖2)。線性地址被分割成頁(yè)目錄(Page Directory)、頁(yè)表(Page Table)和頁(yè)偏移(Offset)三個(gè)部分。當(dāng)建立一個(gè)新的Win32進(jìn)程時(shí),操作系統(tǒng)會(huì)為它分配一塊內(nèi)存,并建立它自己的頁(yè)目錄、頁(yè)表,頁(yè)目錄的地址也同時(shí)放入進(jìn)程的現(xiàn)場(chǎng)信息中。當(dāng)計(jì)算一個(gè)地址時(shí),系統(tǒng)首先從CPU控制器CR3中讀出頁(yè)目錄所在的地址,然后根據(jù)頁(yè)目錄得到頁(yè)表所在的地址,再根據(jù)頁(yè)表得到實(shí)際代碼/數(shù)據(jù)頁(yè)的頁(yè)幀,最后再根據(jù)頁(yè)偏移訪問(wèn)特定的單元。硬件設(shè)備讀寫(xiě)的是物理內(nèi)存,但應(yīng)用程序讀寫(xiě)的是虛擬地址,所以存在著將物理內(nèi)存地址映射到用戶程序線性地址的問(wèn)題。

          15 0 31 0 31 0 31 0


          圖2 虛擬地址轉(zhuǎn)換為物理地址

          從物理地址到線性地址的轉(zhuǎn)換工作是由驅(qū)動(dòng)程序來(lái)完成的。驅(qū)動(dòng)程序的內(nèi)存映射部分主要是調(diào)用VxD的系統(tǒng)服務(wù)MapPhysToLinear。在VtoolsD中這個(gè)函數(shù)的定義如下:

          PVOID MapPhysToLineag(CONST VOID * PhysAddr,DWORD nBytes,DWORD Flags);

          其中第一個(gè)參數(shù)PhysAddr就是要映射的內(nèi)存的物理地址的起始位置,這個(gè)物理地址可以從PCI配置空間的基址寄存器中獲得,nBytes是內(nèi)存區(qū)域的長(zhǎng)度,F(xiàn)lags必須設(shè)置為0。這個(gè)函數(shù)返回的就是這段物理地址映射的線性內(nèi)存地址。如果指定的內(nèi)存不能存取,函數(shù)將返回FFFFFFFFH。
          如:PDWORD pBase = (PDWORD)MapPhysToLinear((PVOID)PhysAddress,PhysSize,0);
          將pBase傳遞給調(diào)用驅(qū)動(dòng)的用戶程序,用戶程序就可以像使用指針一樣利用pBase訪問(wèn)內(nèi)存。

          4、I/O端口的操作
          在PC機(jī)上,I/O尋址方式與內(nèi)存尋址方式不同,所以處理方法也不同。I/O空間是一個(gè)64K字節(jié)的尋址空間,I/O尋址沒(méi)有實(shí)模式與保護(hù)模式之分,在各種模式下尋址方式相同。在Windows9x系統(tǒng)下,運(yùn)行于第3級(jí)的應(yīng)用程序也可以直接使用I/O指令訪問(wèn)I/O空間。在設(shè)備初始化訪問(wèn)配置空間時(shí),已用到了I/O指令,在對(duì)硬件進(jìn)行配置時(shí),也可以根據(jù)從配置空間基址寄存器PCI Base Address 1中返回的I/O端口基地址使用I/O指令。

          5、響應(yīng)中斷
          VTOOLSD提供了VHardwareInt類(lèi),虛擬IRQ,處理硬件中斷。在Windows9x中,VPICD虛擬了物理可編程中斷控制器的端口,從而可以控制物理中斷控制器。虛擬IRQ的編程思路:首先從VHardwareInt類(lèi)中派生出一個(gè)類(lèi),重載OnHardwareInt函數(shù);然后,動(dòng)態(tài)創(chuàng)建一個(gè)派生類(lèi)對(duì)象實(shí)例;最后鉤掛處理程序,這就是需要編寫(xiě)的中斷服務(wù)程序。(關(guān)于VHardwareInt類(lèi)可參考VTOOLSD)

          四、驅(qū)動(dòng)程序的調(diào)用和封裝
          編寫(xiě)設(shè)備驅(qū)動(dòng)并不是最終的目的,需要由用戶程序來(lái)調(diào)用驅(qū)動(dòng)并實(shí)現(xiàn)一定的功能。一般調(diào)用設(shè)備驅(qū)動(dòng)是使用CreateFile函數(shù)打開(kāi)設(shè)備文件,得到一個(gè)文件句柄。使用如下的語(yǔ)句就可以打開(kāi)文件。

          HANDLE hVxD=CreateFile("\\.\PCIDEVICE.VXD",0,0,0, OPEN_EXISTING,
          FILE_FLAG_DELETE_ON_CLOSE,
          0);
          打開(kāi)設(shè)備文件后,調(diào)用DeviceIoControl函數(shù)就可以實(shí)現(xiàn)應(yīng)用程序與設(shè)備驅(qū)動(dòng)程序的通信。完成硬件操作之后,可以調(diào)用函數(shù)CloseHandle(hVxD);關(guān)閉設(shè)備驅(qū)動(dòng)。(關(guān)于這三個(gè)函數(shù)的詳細(xì)說(shuō)明請(qǐng)參考MSDN)
          至此,我們完成了對(duì)驅(qū)動(dòng)程序的初步設(shè)計(jì),考慮到有的函數(shù)(如DeviceIoControl)調(diào)用起來(lái)非常復(fù)雜,為了提高程序的通用性,要對(duì)部分函數(shù)進(jìn)行封裝。因?yàn)閯?dòng)態(tài)鏈接庫(kù)(DLL)可以在多數(shù)軟件開(kāi)發(fā)平臺(tái)中調(diào)用,所以常用DLL封裝形式。另外需要注意的是由于驅(qū)動(dòng)程序具有與操作系統(tǒng)相同的特權(quán),并且直接操作硬件,如果程序工作不穩(wěn)定,會(huì)造成死機(jī)甚至系統(tǒng)崩潰,所以要對(duì)驅(qū)動(dòng)程序進(jìn)行全面細(xì)致的測(cè)試。
          參考文獻(xiàn)

          c++相關(guān)文章:c++教程




          關(guān)鍵詞: PCI設(shè)備 WINDOWS 驅(qū)動(dòng)程序

          評(píng)論


          相關(guān)推薦

          技術(shù)專(zhuān)區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();