導航:首頁 > 編程語言 > 驅動測試程序的開發主要函數

驅動測試程序的開發主要函數

發布時間:2024-06-25 11:48:53

㈠ 如何開發Windows NT設備驅動程序

一、開發工具
1,虛擬機和操作系統
本人使用的win10操作系統,並安裝了「VMware Station11」,在虛擬機中暫時只安裝了一個「win7 x64」操作系統,後續將安裝一個win10虛擬機系統。一般情況下,我都是在虛擬機操作進行驅動的安裝和調試,這樣可以防止將本機的操作系統弄亂。
後續將嘗試「通過本機winDbg來調試虛擬機中的驅動」。這是一個比較高級的調試方法,點擊打開鏈接。
2,開發工具
本人主要使用「WDK7600」(點擊打開鏈接)和"VS2015+wdk10"(點擊打開鏈接)。我把前者安裝在「win7 X64」虛擬機中安裝,把後者安裝在本機和實驗室電腦上。由於我使用的教材《Windows驅動開發詳解》和學習博客都是用wdk7這個版本開發,為了環境一致,故選擇了在虛擬機上試驗這些教材上的示例代碼
關於wdk7,參考博客:點擊打開鏈接。
關於「VS2015+wdk10」,需要在win10系統下,先安裝VS2015,再安裝wdk10,此外還要安裝VS2015的update。(winDbg集成到了wdk10下,路徑:C:\Program Files (x86)\Windows Kits\10\Debuggers\x64)。
注意事項:
1)VS2015默認安裝,是不安裝c++和sdk的,需要選擇自定義安裝;
2)如果已經默認安裝了,這個時候再安裝wdk10,會給出警告;
3)此時,可以選擇先用VS2015新建一個c++項目,然後會提示你安裝c++部分的模塊;
4)安裝好後,就可以正常安裝wdk10了,如果wdk10安裝好後,還有編譯問題,也需要先去VS下看看是否卻c++相關模塊,並按上述3安裝完整。
另一個需要注意的是,編譯報「Inf2Cat error -2: "Inf2Cat, signability test failed. "Double click to see the tool output」,
參考博客:點擊打開鏈接
是因為inf文件的「DriverVer」的時間不對,VS2015默認的「Inf2cat」中的時間為UTF,需要在工程屬性-》「inf2cat」選擇中,將local時間打開。
3,調試工具
1)debugview
在驅動程序中,調用KdPrint函數(類似C語言中printf),然後通過debugview查看列印信息。這是最常用的一種調試方法。
需要注意的是:首先要在「capture」菜單中勾選「kernel」相關的選項,表示抓取內核的信息。然後,需要使用「管理員身份」運行該軟體。Debugview在win10下經常報錯:

需要到「C:\Windows\System32\drivers」中找到「dbgv.sys」,刪除它,再使用「管理員身份」運行該軟體。
2)

4,調試手段
1)驅動安裝階段,可以到「C:\Windows\System32\drivers」目錄下看相應的「sys」文件是否到位。
2)cmd->regedit打開注冊表,再在「編輯」菜單下「查找」對應的設備信息。
3)使用KdPrint函數列印log和DebugView軟體抓取log(最常用的手段);
4)在驅動代碼中寫log文件(理論上可行,待探索);
5)存儲mp信息。所謂mp信息,就是在系統奔潰之前,操作系統會將當前的調用堆棧記錄成一個mp文件。(詳細設置系統轉存mp信息,可以參考《Windows驅動開發技術詳解》最後一章,或博客:點擊打開鏈接)。設置好mp文件後,遇到藍屏,再將mp文件放到WinDbg中查看,這也是一個中常用的調試手段。
6)IRPTrace,這個軟體可以跟蹤IRP,但是win7及後續版本都不可用,可以嘗試自己寫程序跟蹤。
7)PCITree,查看設備掛載;
8)WinObject,查看驅動中的各種對象信息。
9)WinDbg調試虛擬機,這是一個高級應用。配合VS2015可以查看「內存」、「調用堆棧」、「線程」和「反匯編」。
註:在驅動的開發過程,需要逐漸掌握各種工具和調試手段。

二、開發框架
從我最近的瀏覽的資料來看,Windows驅動程序大致有三種類型:NT驅動、WDM驅動和WDF驅動。其中,NT驅動是非即插即用(Plug-in-and-Play,PNP)式的,它是一項系統服務,目前的設備類驅動大都不是這種類型,不是我的關注點,後面將不展開介紹。WDM驅動和WDF驅動都是即插即用的驅動,後者是前者的升級版。
1,WDM框架
WDM是早前的Windows驅動開發框架,雖然現在微軟推薦用WDF,但是,學習WDM一是能夠更對地了解操作系統的內部機制(WDF是對WDM更高層次的封裝),二是《Windows驅動開發技術詳解》以及網上的很多博文都是用的WDM,從學習角度出發也需要掌握一定的WDM知識。
WDM框架的基本知識,可以參考博文:點擊打開鏈接。後續我也用單獨的博文來講解這方面的內容,主要包括:
1)驅動對象與設備對象(DriverObject vs Device Object);
2)物理設備對象(PDO)和功能設備對象(FDO);
3)驅動的層次結構:水平層次(eg:FDO之間)和垂直層次(FDO到PDO);
4)入口函數(DriverEntry);
5)設備擴展(DRIVER_EXTENSION);
6)重要的常式(routine):AddDevice
7)IRP機制(I/O Request Package):MajorFunction(MJ))和MinorFunction(MN);

2,WDF框架
對於WDF框架,可以參考《Window7設備驅動開發》這本書。WDF框架可以分為KMDF(Kernel Model Driver Frame)和UMDF(User Model Driver Frame),其驅動模型如下:
1)WDF對象(屬性、方法和事件);
2)即插即用和電源管理的集成;
3)集成的I/O排隊和取消(queue);
4)I/O模型。在Windows中,IRP的功能不僅僅是向驅動程序提供傳統的I/O請求(讀、寫、創建等)。它是操作系統和驅動程序、驅動程序和驅動程序之間一種基於數據包的通信機制。

3,一個典型的KMDF驅動程序
通過VS2015新建一個項目,選擇「KMDF」,它會產生如下文件:

1)public.h中定義GUID和CTL_CODE,並提供給應用程序使用;
2)trace.h定義的調試宏和函數,暫不關注;
3)driver.h和driver.c定義了主要的框架代碼。包括:入口函數(DriverEntry)、載入設備的常式(KMDFDriver1EvtDeviceAdd)和清理上下文區的函數。該文件都是框架性的代碼,在驅動開發的過程中,可以選擇一個框架,選定框架後,一般不在該文件中添加功能,而是放到「device.c」和「queue.c」。
4)device.h和device.c,主要處理設備相關的功能,與設備交互的實現放在該文件中。主要包括設備初始化和資源釋放;
5)queue.h和queue.c,主要處理IRP,包括KMDFDriver1EvtIoDeviceControl;

㈡ 如何編寫驅動程序

代碼:

#include<linux/mole.h>

#include<linux/kernel.h>

#include<asm/io.h>

#include<linux/miscdevice.h>

#include<linux/fs.h>

#include<asm/uaccess.h>

//流水燈代碼

#define GPM4CON 0x110002e0

#define GPM4DAT 0x110002e4

static unsigned long*ledcon=NULL;

static unsigned long*leddat=NULL;

//自定義write文件操作(不自定義的話,內核有默認的一套文件操作函數)

static ssize_t test_write(struct file*filp,const char __user*buff,size_t count,loff_t*offset)

{

int value=0;

int ret=0;

ret=_from_user(&value,buff,4);

//底層驅動只定義基本操作動作,不定義功能

if(value==1)

{

*leddat|=0x0f;

*leddat&=0xfe;

}

if(value==2)

{

*leddat|=0x0f;

*leddat&=0xfd;

}

if(value==3)

{

*leddat|=0x0f;

*leddat&=0xfb;

}

if(value==4)

{

*leddat|=0x0f;

*leddat&=0xf7;

}

return 0;

}

//文件操作結構體初始化

static struct file_operations g_tfops={

.owner=THIS_MODULE,

.write=test_write,

};

//雜設備信息結構體初始化

static struct miscdevice g_tmisc={

.minor=MISC_DYNAMIC_MINOR,

.name="test_led",

.fops=&g_tfops,

};

//驅動入口函數雜設備初始化

static int __init test_misc_init(void)

{

//IO地址空間映射到內核的虛擬地址空間

ledcon=ioremap(GPM4CON,4);

leddat=ioremap(GPM4DAT,4);

//初始化led

*ledcon&=0xffff0000;

*ledcon|=0x00001111;

*leddat|=0x0f;

//雜設備注冊函數

misc_register(&g_tmisc);

return 0;

}

//驅動出口函數

static void __exit test_misc_exit(void)

{

//釋放地址映射

iounmap(ledcon);

iounmap(leddat);

}

//指定模塊的出入口函數

mole_init(test_misc_init);

mole_exit(test_misc_exit);

MODULE_LICENSE("GPL");

(2)驅動測試程序的開發主要函數擴展閱讀:

include用法:

#include命令預處理命令的一種,預處理命令可以將別的源代碼內容插入到所指定的位置;可以標識出只有在特定條件下才會被編譯的某一段程序代碼;可以定義類似標識符功能的宏,在編譯時,預處理器會用別的文本取代該宏。

插入頭文件的內容

#include命令告訴預處理器將指定頭文件的內容插入到預處理器命令的相應位置。有兩種方式可以指定插入頭文件:

1、#include<文件名>

2、#include"文件名"

如果需要包含標准庫頭文件或者實現版本所提供的頭文件,應該使用第一種格式。如下例所示:

#include<math.h>//一些數學函數的原型,以及相關的類型和宏

如果需要包含針對程序所開發的源文件,則應該使用第二種格式。

採用#include命令所插入的文件,通常文件擴展名是.h,文件包括函數原型、宏定義和類型定義。只要使用#include命令,這些定義就可被任何源文件使用。如下例所示:

#include"myproject.h"//用在當前項目中的函數原型、類型定義和宏

你可以在#include命令中使用宏。如果使用宏,該宏的取代結果必須確保生成正確的#include命令。例1展示了這樣的#include命令。

【例1】在#include命令中的宏

#ifdef _DEBUG_

#define MY_HEADER"myProject_dbg.h"

#else

#define MY_HEADER"myProject.h"

#endif

#include MY_HEADER

當上述程序代碼進入預處理時,如果_DEBUG_宏已被定義,那麼預處理器會插入myProject_dbg.h的內容;如果還沒定義,則插入myProject.h的內容。

㈢ 椹卞姩鍑芥暟鍜屽搷搴斿嚱鏁板尯鍒

涓昏佸尯鍒鍦ㄤ簬鍑芥暟鐨勮皟鐢ㄦ柟寮忓拰鎵ц屾椂鏈恆
鏍規嵁鏌ヨCSDN紺懼尯緗戠珯寰楃煡錛岄┍鍔ㄥ嚱鏁板拰鍝嶅簲鍑芥暟鏄涓ょ嶄笉鍚岀殑鍑芥暟綾誨瀷錛屼富瑕佸尯鍒鍦ㄤ簬鍑芥暟鐨勮皟鐢ㄦ柟寮忓拰鎵ц屾椂鏈恆傚叿浣撳備笅錛
椹卞姩鍑芥暟錛氶┍鍔ㄥ嚱鏁版槸鎸囩敱紼嬪簭鍛樹富鍔ㄨ皟鐢ㄧ殑鍑芥暟錛岀敤鏉ュ疄鐜版煇涓鐗瑰畾鐨勫姛鑳芥垨浠誨姟銆傞┍鍔ㄥ嚱鏁扮殑璋冪敤鏃舵満鏄鐢辯▼搴忓憳鎺у埗鐨勶紝鍙浠ユ牴鎹闇瑕佸湪浠諱綍鍦版柟璋冪敤銆傞┍鍔ㄥ嚱鏁扮殑鎵ц岀粨鏋滈氬父浼氳繑鍥炵粰璋冪敤鑰咃紝鎴栬呭獎鍝嶇▼搴忕殑鐘舵佹垨琛屼負銆備緥濡傦紝鎴戜滑鍙浠ョ紪鍐欎竴涓椹卞姩鍑芥暟鏉ユ墦寮涓涓鏂囦歡錛岃誨彇鏂囦歡鍐呭癸紝鐒跺悗鍏抽棴鏂囦歡銆
鍝嶅簲鍑芥暟錛氬搷搴斿嚱鏁版槸鎸囩敱緋葷粺鎴栧叾浠栨ā鍧楄嚜鍔ㄨ皟鐢ㄧ殑鍑芥暟錛岀敤鏉ュ搷搴旀煇涓浜嬩歡鎴栦俊鍙楓傚搷搴斿嚱鏁扮殑璋冪敤鏃舵満鏄鐢辯郴緇熸垨鍏朵粬妯″潡鍐沖畾鐨勶紝閫氬父鍦ㄤ簨浠舵垨淇″彿鍙戠敓鏃惰Е鍙戙傚搷搴斿嚱鏁扮殑鎵ц岀粨鏋滈氬父涓嶄細榪斿洖緇欒皟鐢ㄨ咃紝鑰屾槸鎵ц屼竴浜涙搷浣滄垨澶勭悊涓浜涢昏緫銆備緥濡傦紝鎴戜滑鍙浠ョ紪鍐欎竴涓鍝嶅簲鍑芥暟鏉ュ勭悊鐢ㄦ埛鐐瑰嚮鎸夐挳鐨勪簨浠躲
鎬諱箣錛岄┍鍔ㄥ嚱鏁板拰鍝嶅簲鍑芥暟鐨勫尯鍒灝辨槸涓誨姩璋冪敤鍜岃鍔ㄨ皟鐢ㄧ殑鍖哄埆錛屼篃鏄鍚屾ュ拰寮傛ョ殑鍖哄埆銆傞┍鍔ㄥ嚱鏁版槸紼嬪簭鍛樹富鍔ㄦ帶鍒剁殑鍚屾ヨ皟鐢錛岃屽搷搴斿嚱鏁版槸緋葷粺鎴栧叾浠栨ā鍧楄嚜鍔ㄨЕ鍙戠殑寮傛ヨ皟鐢ㄣ

㈣ 緙栧啓涓涓綆鍗曠殑瀛楃﹁懼囬┍鍔ㄧ▼搴忋傝佹眰璇ュ瓧絎﹁懼囧寘鎷瑂cull_open() scull_write() scull_read() scull_i

絎涓閮ㄥ垎 瀛楃﹁懼囬┍鍔ㄧ▼搴

1.1 鍑芥暟scull_open()

int scull_open(struct inode *inode錛宻truct file *filp) {

MOD_INC_USE_COUNT錛 // 澧炲姞璇ユā鍧楃殑鐢ㄦ埛鏁扮洰

printk(鈥淭his chrdev is in open\n鈥)錛

return 0錛

}

1.2 鍑芥暟scull_write()

int scull_write(struct inode *inode錛宻truct file *filp錛宑onst char *buffer錛宨nt count) {

if(count < 0)

return 鈥揈INVAL錛

if(scull.usage || scull.new_msg)

return 鈥揈BUSY錛

scull.usage = 1錛

kfree(scull.data)錛

data = kmalloc(sizeof(char)*(count+1)錛孏FP_KERNEL)錛

if(!scull.data) {

return 鈥揈NOMEM錛

}

_from_user(scull.data錛宐uffer錛宑ount + 1)錛

scull.usage = 0錛

scull.new_msg = 1錛

return count錛

}

1.3 鍑芥暟scull_read()

int scull_read(struct inode *inode錛宻truct file *filp錛宑har *buffer錛宨nt count) {

int length錛

if(count < 0)

return 鈥揈INVAL錛

if(scull.usage)

return 鈥揈BUSY錛

scull.usage = 1錛

if(scull.data == 0)

return 0錛

length = strlen(scull.data)錛

if(length < count)

count = length錛

_to_user(buf錛宻cull.data錛宑ount + 1)錛

scull.new_msg = 0錛

scull.usage = 0錛

return count錛

}

1.4 鍑芥暟scull_ioctl()

#include <linux/ioctl.h>

#define SCULL_MAJOR 0

#define SCULL_MAGIC SCULL_MAJOR

#define SCULL_RESET _IO(SCULL_MAGIC錛0) // reset the data

#define SCULL_QUERY_NEW_MSG _IO(SCULL_MAGIC,1) // check for new message

#define SCULL_QUERY_MSG_LENGTH _IO(SCULL_MAGIC,2) //get message length

#define IOC_NEW_MSG 1

static int usage錛宯ew_msg錛 // control flags

static char *data錛

int scull_ioctl(struct inode *inode錛宻truct file *filp錛寀nsigned long int cmd錛寀nsigned long arg) {

int ret=0錛

switch(cmd) {

case SCULL_RESET:

kfree(data)錛

data = NULL錛

usage = 0錛

new_msg = 0錛

break錛

case SCULL_QUERY_NEW_MSG:

if(new_msg)

return IOC_NEW_MSG錛

break錛

case SCULL_QUERY_MSG_LENGTH:

if(data == NULL){

return 0錛

}

else {

return strlen(data)錛

}

break錛

default:

return 鈥揈NOTTY錛

}

return ret錛

}

1.5 鍑芥暟scull_release()

void scull_release(struct inode *inode錛宻truct file *filp) {

MOD_DEC_USE_COUNT錛 // 璇ユā鍧楃殑鐢ㄦ埛鏁扮洰鍑1

printk(鈥淭his chrdev is in release\n鈥)錛

return 0錛

#ifdef DEBUG

printk(鈥渟cull_release(%p,%p)\n鈥濓紝inode錛宖ilp)錛

#endif

}

1.6 嫻嬭瘯鍑芥暟

鍦ㄨュ瓧絎﹁懼囬┍鍔ㄧ▼搴忕紪璇戝姞杞藉悗錛屽啀鍦/dev鐩褰曚笅鍒涘緩瀛楃﹁懼囨枃浠禼hrdev錛屼嬌鐢ㄥ懡浠: #mknod /dev/chrdev c major minor 錛屽叾涓鈥渃鈥濊〃紺篶hrdev鏄瀛楃﹁懼囷紝鈥渕ajor鈥濇槸chrdev鐨勪富璁懼囧彿銆傦紙璇ュ瓧絎﹁懼囬┍鍔ㄧ▼搴忕紪璇戝姞杞藉悗錛屽彲鍦/proc/devices鏂囦歡涓鑾峰緱涓昏懼囧彿錛屾垨鑰呬嬌鐢ㄥ懡浠: #cat /proc/devices | awk 鈥\\$2==鈥漜hrdev\鈥漿 print\\$1}鈥 鑾峰緱涓昏懼囧彿錛

#include <stdio.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <sys/ioctl.h>

#include <stdlib.h>

#include <string.h>

#include <fcntl.h>

#include <unistd.h>

#include <errno.h>

#include 鈥渃hardev.h鈥 // 瑙佸悗闈㈠畾涔

void write_proc(void)錛

void read_proc(void)錛

main(int argc錛宑har **argv) {

if(argc == 1) {

puts(鈥渟yntax: testprog[write|read]\n鈥)錛

exit(0)錛

}

if(!strcmp(argv[1],鈥渨rite鈥)) {

write_porc()錛

}

else if(!strcmp(argv[1]錛屸渞ead鈥)) {

read_proc()錛

}

else {

puts(鈥渢estprog: invalid command!\n鈥)錛

}

return 0錛

}

void write_proc() {

int fd錛宭en錛宷uit = 0錛

char buf[100]錛

fd = open(鈥/dev/chrdev鈥,O_WRONLY)錛

if(fd <= 0) {

printf(鈥淓rror opening device for writing!\n鈥)錛

exit(1)錛

}

while(!quit) {

printf(鈥\n Please write into錛氣)錛

gets(buf)錛

if(!strcmp(buf錛屸渆xit鈥))

quit = 1錛

while(ioctl(fd錛孌YNCHAR_QUERY_NEW_MSG))

usleep(100)錛

len = write(fd錛宐uf錛宻trlen(buf))錛

if(len < 0) {

printf(鈥淓rror writing to device!\n鈥)錛

close(fd)錛

exit(1)錛

}

printf(鈥\n There are %d bytes written to device!\n鈥濓紝len)錛

}

close(fd)錛

}

void read_proc() {

int fd錛宭en錛宷uit = 0錛

char *buf = NULL錛

fd=open(鈥/dev/chrdev鈥濓紝O_RDONLY)錛

if(fd < 0) {

printf(鈥淓rror opening device for reading!\n鈥)錛

exit(1)錛

}

while(!quit) {

printf(鈥\n Please read out錛氣)錛

while(!ioctl(fd錛孌YNCHAR_QUERY_NEW_MSG))

usleep(100)錛

// get the msg length

len = ioctl(fd錛孌YNCHAR_QUERY_MSG_LENGTH錛孨ULL)錛

if(len) {

if(buf != NULL)

free(buf)錛

buf = malloc(sizeof(char)*(len+1))錛

len = read(fd錛宐uf錛宭en)錛

if(len < 0) {

printf(鈥淓rror reading from device!\n鈥)錛

}

else {

if(!strcmp(buf錛屸渆xit鈥) {

ioctl(fd錛孌YNCHAR_RESET)錛 // reset

quit = 1錛

}

else

printf(鈥%s\n鈥,buf)錛

}

}

}

free(buf)錛

close(fd)錛

}

// 浠ヤ笅涓篶hrdev.h瀹氫箟

#ifndef _DYNCHAR_DEVICE_H

#define _DYNCHAR_DEVICE_H

#include <linux/ioctl.h>

#define DYNCHAR_MAJOR 42

#define DYNCHAR_MAGIC DYNCHAR_MAJOR

#define DYNCHAR_RESET _IO(DYNCHAR_MAGIC,0) // reset the data

#define DYNCHAR_QUERY_NEW_MSG _IO(DYNCHAR_MAGIC,1) // check for new message

#define DYNCHAR_QUERY_MSG_LENGTH _IO(DYNCHAR_MAGIC,2) // get message length

#define IOC_NEW_MSG 1

#endif

㈤ 如何在 Windows CE 5.0 中開發和測試設備驅動程序

本文介紹如何開發和測試 Windows CE 5.0 設備驅動程序。本文循序漸進地介紹如何創建流驅動程序,如何創建自定義 Windows CE Test Kit
(CETK) 測試,以及如何編寫應用程序來測試驅動程序。這要花費大約 60 分鍾來完成。

本頁內容

第一部分:建立設備驅動程序
第二部分:測試流驅動程序測試代碼
第三部分:檢驗驅動程序
第四部分:使用
Windows CE Test Kit
第五部分:創建自定義
CETK 測試
第六部分:確定誰擁有流驅動程序

小結

第一部分:建立設備驅動程序

在本練習中,您將使用 Platform Builder 來添加作為設備驅動程序的項目。

在 開始編寫驅動程序之前,您應該了解設備驅動程序的用途。驅動程序將基礎硬體從梁頃操作系統中抽象出來,使之更好地面對應用程序開發人員。應用程序開發人員無需
知道顯示硬體或串列硬體的詳細信息 — 例如,串列設備是用 Universal Asynchronous Receiver/Transmitter (UART)
實現的還是用 field-programmable gate array (FPGA)
實現的。在大多數情況下,應用程序開發人員根本不需要知道硬體是如何實現的。

Microsoft Windows 為開發人員公開了調用硬體的應用程序編程介面
(API),他們不需要知道物理硬體的情況。例如,為了向串列埠寫入數據,應用程序開發人員只需調用 COMx 上的 CreateFile( )(其中 x
表示您要打開的串列埠編號,例如 COM1 代表串列埠 1),再調用 WriteFile( ) 以將一些位元組數據寫入串列埠,然後調用
CloseHandle( ) 以關閉串列埠。不管基礎串列硬體是什麼(也不管您運行的是哪個 Windows 操作系統),API 都會以同樣的順序執行。

相同的情況也適用於其他 API:如果您希望在顯示表面畫一條線,那麼您只需調用 PolyLine( )、MoveToEx( ) 或 LineTo(
)。作為應用程序開發人員,大多數情況下您都不需要知道顯示硬體的情況。此處調用的 API 將返回顯示表面的維數、顏色深度等碼銷等。

好 消息是開發人員可以調用一個一致的、眾所周知的 API 集。這些 API
將他們的應用程序從基礎硬體中抽象出來。這至關重要,因為應用程序開發人員無法知道應用程序是運行在攜帶型計算機上,還是運行在 Tablet PC
上,抑或運行在桌面計算機上。無論電腦以 1024×768 還是 1600×1200
的解析度運行,應用程序開發人員都可以在運行時查詢屏幕解析度和顏色深度,因此不需要構建只在特定硬體上運行的應用程序。

驅動程序只是一 個動態鏈接庫(DLL)。將 DLL 載入到父進程地址空間;然後父進程就可以調用從該 DLL 公開的任何介面。通常,父進程通過調用
LoadLibrary( ) 或 LoadDriver( ) 來載入驅動程序。LoadDriver 不僅將 DLL 載入到父進程地址空間中,而且還要確保 DLL
沒有「paged out」。

調用進程如何知道從您的 DLL 或驅動程序公開了哪些 API 或函數呢?父進程調用 GetProcAddress( ),後者可以獲取函數名稱和所載入的
DLL 的 hInstance。如果函數存在,調用返回該函數指針;如果沒有從 DLL 公開該函數,則返回 NULL。

流驅動程序也公開了一個眾所周知的函數集。對於流驅動程序,您會希望能夠將位元組流寫入設備中,或者從設備中讀取位元組流。因此,在前面使用的串列埠示例中,您可能希望從您的驅動程序公開如下函數集:Open、Close、Read

Write。流驅動程序還公開一些其他函數:PowerUp、PowerDown、IOControl、Init
和 DeInit。

您可以將現有的操作系統映像用於模擬器平台(Basic Lab MyPlatform 平台最理想)。然後,您就可以將
DLL/驅動程序項目添加到該平台了。

橡模陸在構建並下載了該平台之後(這表明操作系統啟動並運行良好),您需要創建您的主幹驅動程序。您可以使用 File 菜單上的 Platform
Builder New Project or File 命令創建一個 Microsoft Windows CE DLL。創建用於公開函數或資源的
DLL 與創建用作驅動程序的 DLL 之間沒有什麼不同;唯一的不同之處在於 DLL 公開哪些函數,以及如何在平台上注冊或使用 DLL。

此 外,一種創建國際化應用程序的方法是,首先創建包含一組核心語言字元串、對話框和資源的基本應用程序,然後創建許多外部
DLL,其中每個都包含針對特定區域設置的對話框、字元串和資源。然後,應用程序就可以在運行時載入相應的語言資源。只需要添加 DLL
文件,您就可以將語言添加到應用程序中。在 Developing International
Software 一書中描述了與此相關的主題以及其他一些有趣的主題,可以在 Microsoft Press 網站上獲得此書。

添加一個作為設備驅動程序的項目

用 Platform Builder 打開現有的 MyPlatform 工作區。

在 File 菜單上,單擊 New Project or File。

選擇 WCE Dynamic-Link Library,給它一個合適的名稱(例如,StreamDrv),然後單擊
OK,如下圖所示。

在下圖所顯示的頁面中多少填寫一些您需要的信息,然後單擊 Next。

單擊 A simple Windows CE DLL project,如下圖所示。

單擊 Finish 完成此向導。

此時,DLL 只包含一個空的 DllMain
函數。您可以公開一些應用程序要調用的函數,並公開一些資源(可能使之成為識別語言/文化的應用程序的一部分),或者使之成為一個設備驅動程序。在本文中,您將使用
Windows CE Stream Driver Wizard 創建您的主幹流驅動程序。

在 Windows CE 中,打開流驅動程序就像打開文件一樣,只需根據唯一的三字母前綴(例如,COM)。

為您的驅動程序選擇一個唯一的三字母標識符。在 Location
框中輸入您之前創建的流驅動程序的完整路徑。或者使用「browse」按鈕定位到 Platform Builder 安裝中的 PBWorkspaces
目錄,找到您前面創建的平台,然後找到流驅動程序的名稱(在前面的示例中,此路徑為 PBWorkspaces\TuxPlat\StreamDrv)。

在 Driver Filename 框中輸入驅動程序的名稱。如下圖所示,使用與您前面使用名稱 (StreamDrv)
相同的名稱,以確保改寫在 Platform Builder 中創建的原始文件。

按 Go,將生成流驅動程序源代碼。

返回頁首

第二部分:測試流驅動程序測試代碼

現在您已經編寫了用於 Windows CE 的自定義流驅動程序的基本代碼。此時,驅動程序還沒有與任何硬體連接。

在 編寫完驅動程序之後,您需要為開發人員提供一種測試它的方法。Windows CE 附帶了 Windows CE Test Kit
(CETK),它提供了用於各種驅動程序類型的驅動程序測試,包含網路連接、藍牙、串列埠以及顯示。您編寫的驅動程序是一種自定義的流驅動程序,它沒有
公開與現有的驅動程序測試一樣的功能,因此您需要為該驅動程序編寫一個自定義測試。雖然您完全可以編寫一個應用程序來演練驅動程序,但提供一個 CETK
模塊或許更好些,在開發期間可以使用此模塊,並且還可以將此模塊提供給客戶,供他們在裝配硬體上測試驅動程序。

在這一部分的練習中,您將執行以下過程:

創建主幹 Tux 模塊

將自定義驅動程序的測試代碼添加到 Tux DLL 中

重新構建操作系統

設置斷點

創建主幹 Tux 模塊

在 Platform Builder 中,在 File 菜單上單擊 New Project or File。

選擇 WCE TUX Dynamic-Link Library,鍵入 TuxTest 作為項目名稱,輸入一個位置,單擊
Workspace Project,然後單擊 OK,如下圖所示。(實際上,您可以選擇任意一個項目類型;對於本文,單擊
Workspace Project)。

在下圖顯示的頁面中多少填寫一些您需要的信息,然後單擊 Next。

閱讀下圖所顯示的屏幕上的信息,然後單擊 Next。

在最後一頁上,您可以選擇選取 Release Type 下的
CETK,如下圖所示。該選項關閉了某些二進制的優化,以提高調試工作效率。單擊 Finish。

單擊 View | File View,然後展開 Projects 樹顯示 tux
源代碼,如下圖所示。

前圖中需要注意的重要文件是:

ft.h — 該文件包含 tux DLL 所用的函數表。

test.cpp — 該文件包含從該函數表中調用的測試過程。

TuxStreamTest.cpp — 該文件包含 DLLMain 和 ShellProc,後者是從 Tux.exe
調用的。

將自定義驅動程序測試代碼添加到 Tux DLL 中

打開源代碼 Test.cpp。

使用 CodeClip 來獲得 Tux_Custom_Test | TuxCode 源代碼。

用 CodeClip 中的代碼替代函數 TestProc 中的內容。

您會注意到,Test.cpp 中的代碼載入了一個名為 Demo.dll 的驅動程序。對於本文,您創建了一個名為 StreamDrv
的驅動程序。您需要修改源代碼以載入您的 StreamDrv.dll 驅動程序。

找到 Test.cpp 中調用 LoadLibrary 的源代碼的位置,然後將要從 Demo.dll
中載入的驅動程序的名稱修改為 StreamDrv.dll。

在 Platform Builder 文件視圖中,右鍵單擊 TuxTest 項目,然後單擊 Build Current
Project。

您還需要從該目錄中添加 Windows CE Test Kit 組件。

在 Device Drivers 下,找到該目錄中 Windows CE Test Kit 組件的位置,然後選擇
Add the Windows CE Test Kit,將該組件添加到您的平台中。

注 將該組件添加到您的平台上並沒有將任何文件添加到最後的操作系統映像中;它將 Clientside 文件添加到 build release
文件夾中。您可以從 Platform Builder 下載 Clientside 應用程序,並在目標設備上運行該應用程序。

現在您需要重新構建您的操作系統,以便合並這些變更。

重新構建操作系統

在 Platform Builder 中,選擇 Build OS | Sysgen。

構建過程將會花大約 5 分鍾完成。

當載入驅動程序時,在流驅動程序的入口點設置一個斷點來觀察非常有用。

設置斷點

單擊 File View,打開 StreamDrv 項目,然後打開 Source files。

找到並打開 StreamDrv.cpp。

找到 DllMain,然後找到並單擊 switch 語句。

按 F9 設置斷點。

單擊 Target | Attach,將操作系統下載到模擬環境中。

您會看到以下調試輸出,斷點將啟用。注意,在載入操作系統的用戶介面 (UI) 之前,這早就發生了。

4294780036 PID:23f767b6 TID:23f767e6 0x83fa6800: >>> Loading mole
streamdrv.dll at address 0x01ED0000-0x01ED5000

Loaded symbols for
'C:\WINCE500\PBWORKSPACES\DRVDEMO\RELDIR\EMULATOR_X86_DEBUG\STREAMDRV.DLL'

單擊 switch 語句,然後按 F9 禁用斷點。

按 F5,允許操作系統繼續載入。

現在,您已經構建了一個 Windows CE 5.0
操作系統,它包含一個自定義流驅動程序,並且您已經在操作系統引導順序的過程中看到了驅動程序載入。

返回頁首

第三部分:檢驗驅動程序

在這一部分的練習中,您將執行以下過程:

使用命令行工具查看從驅動程序公開的函數

使用遠程系統信息 (Remote System Information) 工具檢驗驅動程序

確定驅動程序已載入

檢驗您所創建的設備驅動程序的第一種方法是查看從該驅動程序公開的函數。Windows CE 附帶了一個名為 Dumpbin
的命令行工具,可以用於檢驗導入應用程序或模塊的內容,或者從 DLL(或驅動程序)導出的內容。

使用命令行工具查看從驅動程序公開的函數

在 Platform Builder 中,單擊 Build OS | Open Release
Directory。該操作為當前的工作區打開 build release 文件夾中的 Command Prompt 窗口。

鍵入 mpbin exports StreamDrv.dll

下圖顯示輸出。您可以看到,所有需要的流驅動程序函數都是從驅動程序公開的;函數是從 DLL 公開的(通過該項目的 .def 文件)。

鍵入 Exit 關閉 Command Prompt 窗口

StreamDrv.def 文件的內容如下所示。

LIBRARY DemoDriver

EXPORTS

DEM_Init

DEM_Deinit

DEM_Open

DEM_Close

DEM_IOControl

DEM_PowerUp

DEM_PowerDown

DEM_Read

DEM_Write

DEM_Seek

CustomFunction

CustomFunctionEx

您可以檢驗驅動程序的第二種方法是通過遠程系統信息工具。

通過遠程系統信息工具檢驗驅動程序

在 Platform Builder 中,單擊 Tools | Remote System
Information。

選擇 Windows CE Default Platform | Default Device,然後單擊
OK,如下圖所示。

此過程將遠程系統信息應用程序連接到 Platform Builder 正在使用的當前活動平台上。下圖顯示了結果。

您也可以使用載入模塊列表來確定已載入了您的驅動程序。

確定驅動程序已載入

在 Platform Builder 中,使用 Target Control 窗口 (gi mod) 或 View |
Debug Windows | Moles and Symbols。

下圖顯示了此過程的結果。

返回頁首

第四部分:使用 Windows CE Test Kit

Windows CE Test Kit 包含設備端組件和桌面組件。設備端組件叫做 Clientside.exe,通過從目錄中添加 CETK
組件,您可以將設備端組件添加到您的工作區中。注意,將 Clientside.exe
應用程序添加到工作區中並沒有將任何文件添加到最終操作系統映像中,但它卻將應用程序復制到 build release 文件夾中。

在桌面計算機上運行 CETK 之前,您需要啟動設備上的 Clientside.exe 應用程序。沒有鏈接工具(比如遠程工具)的原因在於,CETK
也將運行在裝配(零售)設備(比如 Pocket PC)上。

在這一部分的練習中,您將執行以下過程:

檢驗 Windows CE Test Kit 用戶介面

運行一個標准測試

檢驗 Windows CE Test Kit 用戶介面

在 Platform Builder 中,在 Tools 菜單上單擊 Windows CE Test
Kit。

這 一步啟動 Windows CE Test Kit 應用程序,如下圖所示。注意,這不是一個標準的遠程工具。Windows CE
附帶的大多數遠程工具都使用 Kernel Independent Transport Layer
(KITL),一種將工具從基礎通信硬體中抽象出來的傳輸,以便這些工具可以運行在乙太網、串列埠、1394、USB 或者其他傳輸上。

雖然對於 Windows CE 5.0,Windows CE Test Kit 通常通過套接字連接,但是也已經更新了工具來支持 KITL。

在 Windows CE Test Kit 中,單擊 Connection | Start
Client。

這一步顯示 Device Connection 對話框,其中您可以選擇是通過套接字連接還是通過 KITL 連接。

確保清除了 Use Windows Sockets for the client/server communication
復選框,如下圖所示。

單擊 Connect。

在遠程工具 (KITL) 的標准用戶界面中,選擇 Windows CE Default Platform | Default
Device,然後單擊 OK,如下圖所示。

該過程在目標設備上啟動 Clientside.exe,並連接到目標設備上。在完成連接之後,CETK 枚舉目標平台上支持的設備,並禁用 CETK
中不支持的設備。

在 CETK 連接到目標設備並枚舉設備之後,UI 如下圖所示。注意,禁用了某些硬體類別,比如 Bluetooth、IR
Port 和 Modem。

將自定義測試添加到 CETK 中之前,您可以運行一個標准測試,以查看測試工作如何進行。

運行標准測試

在 CETK 中,展開 Windows CE (x86)。

找到並展開 Serial Port。

右鍵單擊 Serial Port Driver Test,然後單擊 Quick Start。

這一步只運行了這一個測試,還沒有運行所選的其他測試。UI 指示測試正在進行,如下圖所示。

CETK 提供測試過程和測試輸出的更新。您也可以在 Platform Builder 中檢驗調試輸出,以便查看測試過程,如下例所示。

405910 PID:83d4ee4a TID:83ea5a8a *** Test Name: Set event mask and wait for
thread to close comm port handle

405920 PID:83d4ee4a TID:83ea5a8a *** Test ID: 1007

405920 PID:83d4ee4a TID:83ea5a8a *** Library Path: \serdrvbvt.dll

405920 PID:83d4ee4a TID:83ea5a8a *** Command Line:

405920 PID:83d4ee4a TID:83ea5a8a *** Result: Passed

405920 PID:83d4ee4a TID:83ea5a8a *** Random Seed: 15595

405930 PID:83d4ee4a TID:83ea5a8a *** Thread Count: 1

405930 PID:83d4ee4a TID:83ea5a8a *** Execution Time: 0:00:05.110

405930 PID:83d4ee4a TID:83ea5a8a ***
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

如果 CETK UI
指示模擬器上的串列埠測試已經失敗(如下圖所示),那麼失敗可能不是由於每個測試的完全失敗而導致的。它可能表明,全部測試套件只有一部分已經失敗,並且這部分實際上也是期望的行為。

右鍵單擊 Serial Port Driver Test [Failed],然後單擊 View
Results。

出現如下圖所示的窗口。

查看上圖所示的結果,您可以看到,已經運行了 10 個單獨的測試。除了 Set and verify receive timeout
以外,所有這些測試都已經通過。

要獲得更多信息,您可以單擊個別測試。

返回頁首

第五部分:創建自定義 CETK 測試

通過使用 Platform Builder User-Defined Test Wizard,您可以創建一個自定義 CETK
測試。該測試將驗證自定義流驅動程序(您也已經將其添加到平台中)的導出函數。

在這一部分的練習中,您將執行以下過程:

列出 CETK 中的自定義流驅動程序測試

運行自定義流驅動程序測試

列出 CETK 中的自定義流驅動程序測試

在 CETK 中,單擊 Tests | User Defined。

這一步啟動 User-Defined Test Wizard。該向導的第一頁只是一些信息。

單擊 Next,如下圖所示。

單擊 Add a New Test,然後單擊 Next,如下圖所示。

輸入下列信息,然後單擊 Next:

· 在 Name of Test 框中鍵入 Custom Stream Driver Test

· 在 Tux Mole (DLL) 框中,定位到
C:\Wince500\PBWorkspaces\MyPlatform\RelDir\Emulator_x86_Debug 目錄,然後選擇
test.dll 或 TuxTest.dll(這依賴於您在 Platform Builder 中所使用的 Tux
測試的名稱)。

· 在 Command Line 框中,保留當前測試的默認設置。

· 在 Processor 框中鍵入 x86

下圖顯示信息如何出現在當前的向導頁中。

單擊 Copy the files to the directory for user-defined tests,然後單擊
Next,如下圖所示。

您需要將自定義驅動程序測試(您的 DLL)復制到用戶定義的測試文件夾中。如果您要刪除現有的工作區,那麼自定義驅動程序測試仍然保持完好。

單擊 Next,如下圖所示。

單擊 Finish,如下圖所示。

CETK 應用程序不會用新的測試進行自動刷新。您需要重新同步桌面應用程序,以查看新添加的測試。

右鍵單擊 Windows CE (x86),然後單擊 Redetect Peripherals。

該過程添加了一個名為 User Tests 的新驅動程序類別。您只添加了一個測試,因此,當您展開這個項目時,您只能看到 Custom
Stream Driver Test。

注 已經將自定義流驅動程序測試的 DLL 復制到下列位置: C:\Program Files\Windows CE Platform
Builder\5.00\CEPB\wcetk\user\x86.

運行自定義流驅動程序測試

在可用的測試列表中展開 User Tests。

右鍵單擊 Custom Stream Driver Test,然後單擊 Quick Start。

㈥ linux下開發驅動程序是怎樣把應用程序和內核聯系在一起

驅動程序一般是通過模塊注入內核,用字元驅動程序舉個例子:
1.編寫字元驅動程序需要在內核中注冊設備和中斷程序,還有file_ops裡面的open,read,release等函數
2.注冊成功後在/proc/device文件裡面可以看到你注冊的設備名稱和主設備號,/proc/interrupt文件中可以看到注冊的中斷
3.為設備創建文件節點,mknod /dev/char_dev_test c 主設備號 次設備號,於是就在/dev/裡面生成一個char_dev_test 設備文件
4,應用程序通過文件操作函數,比如open,read等操作char_dev_test 文件
eg: FILE* p=open("/dev/char_dev_test","rb");
if(p==NULL) { printf("error,can't open dev file!"); return -1;}
char buf[1024];
read(p,buf,size_t);
//其中open是調用的注冊進入內核的file_ops的open函數,read是調用的file_ops的read函數,裡面一般有_to_user,將內核數據復制到用戶空間,也就是復制到了buf中。

關於驅動的知識,建議你還是學習一下linux驅動開發相關知識

祝你好運

閱讀全文

與驅動測試程序的開發主要函數相關的資料

熱點內容
cad2010無法載入配置文件 瀏覽:53
導出視頻導成四個文件 瀏覽:558
bluestacks注冊表文件使用 瀏覽:212
電腦怎麼編輯微信收藏文件 瀏覽:709
win10平板亮度調節軟體 瀏覽:565
win10自動應答文件教程 瀏覽:119
編程除號是什麼符號 瀏覽:768
asp報表工具 瀏覽:327
數控編程90度如何計算 瀏覽:763
為什麼要有多種編程語言 瀏覽:714
學編程哪個好用嗎 瀏覽:396
電腦文件恢復得多少錢 瀏覽:178
數控g71編程p1q2什麼意思 瀏覽:205
資料庫的事務性能優化 瀏覽:920
軟體和編程哪個容易學 瀏覽:744
怎麼看wifi是什麼網路 瀏覽:145
網站body怎麼設置 瀏覽:209
玉石原石在哪個app可以賣 瀏覽:406
如何修改樂視賬號密碼忘了怎麼辦 瀏覽:83
w10分區工具 瀏覽:654

友情鏈接