slide1 n.
Download
Skip this Video
Loading SlideShow in 5 Seconds..
Windows98/2000 驱动程序编写方法 PowerPoint Presentation
Download Presentation
Windows98/2000 驱动程序编写方法

Loading in 2 Seconds...

play fullscreen
1 / 71

Windows98/2000 驱动程序编写方法 - PowerPoint PPT Presentation


  • 112 Views
  • Uploaded on

Windows98/2000 驱动程序编写方法. (下). 4.Driver Works 的使用. 1 )生成简单框架. 工程文件名. 工程文件目录. 选择驱动类型. 创建功能驱动程序. 创建过滤器驱动程序. 本例不驱动硬件. 选择相应总线. 驱动类名称. 驱动类文件名. 选择需要处理的消息句柄. 添加和应用程序之间通信的控制代码. 测试用应用程序名称. 驱动类. 设备类. 驱动类文件. 设备类文件. 驱动安装指导文件. 测试用的控制台程序文件.

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about 'Windows98/2000 驱动程序编写方法' - gigi


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
slide2

4.Driver Works的使用

1)生成简单框架

slide3

工程文件名

工程文件目录

slide5

创建功能驱动程序

创建过滤器驱动程序

slide6

本例不驱动硬件

选择相应总线

slide7

驱动类名称

驱动类文件名

slide18

驱动类

设备类

slide19

驱动类文件

设备类文件

驱动安装指导文件

测试用的控制台程序文件

slide20

此时已经具备了一个驱动程序以及做测试用的应用程序的基本框架,我们可以在VC集成环境下修改有关程序,增加相关的具体操作代码,然后就可以编译和调试了。此时已经具备了一个驱动程序以及做测试用的应用程序的基本框架,我们可以在VC集成环境下修改有关程序,增加相关的具体操作代码,然后就可以编译和调试了。

slide21

该驱动程序框架包含了几个最基本的类,这些类是:该驱动程序框架包含了几个最基本的类,这些类是:

class Sample : public KDriver // 驱动程序类,用于初始化驱动程序

{

SAFE_DESTRUCTORS

public:

// 以下成员函数注意和WDM中有关例程联系起来看

virtual NTSTATUS DriverEntry(PUNICODE_STRING RegistryPath);

virtual NTSTATUS AddDevice(PDEVICE_OBJECT Pdo);

void LoadRegistryParameters(KRegistryKey &Params);

int m_Unit;

// The following data members are loaded from the registry during DriverEntry

ULONG m_bBreakOnEntry;

};

slide22

class SampleDevice : public KPnpDevice // 是设备类KDvice的派生类,用于在WDM环境下支持即插即用设备

{

// Constructors

public:

SAFE_DESTRUCTORS;

SampleDevice(PDEVICE_OBJECT Pdo, ULONG Unit);

~SampleDevice();

// Member Functions 注意和PNP的次功能代码联系起来看

public:

DEVMEMBER_DISPATCHERS

virtual NTSTATUS OnStartDevice(KIrp I);

virtual NTSTATUS OnStopDevice(KIrp I);

virtual NTSTATUS OnRemoveDevice(KIrp I);

virtual NTSTATUS DefaultPnp(KIrp I);

virtual NTSTATUS DefaultPower(KIrp I);

virtual NTSTATUS OnDevicePowerUp(KIrp I);

virtual NTSTATUS OnDeviceSleep(KIrp I);

void SerialRead(KIrp I);

void SerialWrite(KIrp I);

slide23

NTSTATUS SAMPLE_IOCTL_Read_Handler(KIrp I);

NTSTATUS SAMPLE_IOCTL_Write_Handler(KIrp I);

NTSTATUS SAMPLE_IOCTL_ReadWrite_Handler(KIrp I);

#ifdef _COMMENT_ONLY

virtual NTSTATUS Create(KIrp I);

virtual NTSTATUS Close(KIrp I);

virtual NTSTATUS DeviceControl(KIrp I);

virtual NTSTATUS SystemControl(KIrp I);

virtual NTSTATUS Read(KIrp I); virtual NTSTATUS Write(KIrp I);

#endif

// Member Data

protected:

// Unit number for this device (0-9)

ULONG m_Unit;

KPnpLowerDevice m_Lower;

SampleDevice_DriverManagedQueue m_DriverManagedQueue;

// TODO: Create additional driver managed queues. These might be

// of the same class (SampleDevice_DriverManagedQueue),

// or you might choose to derive another class.

};

slide24

下面我们讲解编译、执行和调试这个驱动程序。下面我们讲解编译、执行和调试这个驱动程序。

先编译驱动程序工程

在VC的集成环境中

slide28

下面使用DriverStudio带的工具加载驱动程序和查看调试信息。下面使用DriverStudio带的工具加载驱动程序和查看调试信息。

驱动程序监视,可实时看到驱动程序发出的调试输出语句

驱动程序装载器,可动态调用驱动程序

slide37

2)完成应用程序和驱动程序之间的信息交换

下面我们来修改有关代码,以便增加驱动程序和应用程序之间相互通信的内容。需要增加的内容包括:

使用Read和Write方式分别从驱动程序读入字符和 向驱动程序写字符。

使用IO控制代码方式分别从驱动程序读入字符和 向驱动程序写字符。

使用IO控制代码方式向驱动程序写字符串再从驱动程序中读出该字符串,并返回反馈串信息。

注意:程序中暗红色显示的部分是我们添加或修改过的语句,其他是DriverWorks自动生成的。语句中“t<< xxxxx”这样的语句是向调试软件输出信息,该信息可以再DriverMonitor或其他调试监视器中看到。

slide38

使用Read和Write方式分别读写

  • SampleDevice.cpp
  • void SampleDevice::SerialRead(KIrp I)
  • {
  • t << "Entering SampleDevice::SerialRead, " << I << EOL;
  • NTSTATUS status = STATUS_SUCCESS;
  • PUCHAR pBuffer = (PUCHAR) I.BufferedReadDest();//取得返回数据BUFF的指针
  • ULONG dwTotalSize = I.ReadSize(CURRENT); // Requested read size
  • char buff[512];
  • int n =512, j = (n % 26);
  • for (int i=0; i<n; i++, j=(j + 1)%26)
  • { buff[i] = 'a' + j; }
  • buff[dwTotalSize]=‘\0’; //指定串尾
  • strcpy((char *)pBuffer,buff);
  • // 把给应用程序的数据拷贝给返回BUFF
  • t << “The string you will read is \”“ << buff << ”\“” << EOL; // 输出调试信息
  • ULONG dwBytesRead = strlen(buff); // Count of bytes read
  • I.Information() = dwBytesRead; // 返回给应用程序的信息的字节个数
  • I.Status() = status;
  • m_DriverManagedQueue.PnpNextIrp(I);
  • }
slide39

void SampleDevice::SerialWrite(KIrp I)

{

t << "Entering SampleDevice::SerialWrite, " << I << EOL;

NTSTATUS status = STATUS_SUCCESS;

PUCHAR pBuffer = (PUCHAR)I.BufferedWriteSource();//取得存放应用程序写给驱动程序的数据的BUFF的指针

ULONG dwTotalSize = I.WriteSize(CURRENT);// 获得应用程序写给驱动程序的信息的字节数。

ULONG dwBytesSent = dwTotalSize;

char buff[512];

strcpy(buff, (char *)pBuffer); // 应用程序写给驱动程序的数据在I.BufferedWriteSource()返回的指针中。

buff[dwBytesSent] = '\0';

t << "Write to driver is \"" << buff << "\"" << EOL;

I.Information() = dwBytesSent; // 返回用户实际写的字节数

I.Status() = status;

m_DriverManagedQueue.PnpNextIrp(I);

}

slide40

Test_Sample.cpp

void doRead(int n) // 从驱动程序中读数据

{

char *buf;

ULONG nRead;

int i, j;

buf = (char *) malloc(n);

if (buf == NULL)

{

printf("Failed to allocate buffer for read");

Exit(1);

}

// Read data from driver

printf("Reading from device - ");

ReadFile(hDevice, buf, n, &nRead, NULL);

// 参数分别是设备句柄、输入缓冲地址、缓冲大小(字节数)、实际读的数据字节数、覆盖结构指针。

slide41

printf("%d bytes read from device (%d requested).\n", nRead, nRead);

// Print what was read

while(i < nRead)

{

// j = min((i+26),n);

// for(; i < j; i++)

// {

// printf("%c, ", buf[i]);

// }

// printf("\n");

printf("%c, ",buf[i++]);

}

printf("\n");

free(buf);

}

这几句删除

slide42

void doWrite(int n) // 向驱动程序中写数据

{

char *buf;

ULONG nWritten;

int i, j;

buf = (char *) malloc(n);

if (buf == NULL)

{

printf("Failed to allocate buffer for write");

Exit(1);

}

// start with the mod26 letter of the number of bytes to write

j = (n % 26);

// load buffer with dummy data (abcdefg...)

for (i=0; i<n; i++, j=(j + 1)%26)

{

buf[i] = 'a' + j;

}

slide43

// Write data to driver

printf("Writing to device - ");

WriteFile(hDevice, buf, n, &nWritten, NULL);

// 写数据,参数的含义是驱动程序句柄、写缓冲、写缓冲大小、实际驱动程序得到的信息的字节数、覆盖结构指针。

printf("%d bytes written to device (%d attempted).\n", nWritten, n);

i = 0; // Print what was written

while(i < n)

{

j = min((i+26),n);

for(; i < j; i++) {

printf("%c, ", buf[i]);

}

printf("\n");

}

free(buf);

}

slide44

b. 使用IO控制代码方式分别读写

SampleDevice.cpp

NTSTATUS SampleDevice::SAMPLE_IOCTL_Read_Handler(KIrp I)

{ // 对应用程序读驱动程序的请求作响应

NTSTATUS status = STATUS_SUCCESS;

t << "Entering SampleDevice::SAMPLE_IOCTL_Read_Handler, " << I << EOL;

char buff1[512];

ULONG fwLength=0;

strcpy(buff1,"Welcome to driver!");

fwLength = strlen(buff1)+1;

if (I.IoctlOutputBufferSize() >= fwLength) {// 如果读入缓冲够长

strcpy((PCHAR)I.IoctlBuffer(),buff1); // 将信息拷给应用程序读入缓冲

I.Information() = fwLength; // 返回信息长度

}

else {

I.Information() = 0; // 否则信息长度为0

t << "buff size too small" << EOL;

}

return status;

}

slide45

NTSTATUS SampleDevice::SAMPLE_IOCTL_Write_Handler(KIrp I)

{ // 接受从应用程序中来的信息

NTSTATUS status = STATUS_SUCCESS;

t << "Entering SampleDevice :: SAMPLE_IOCTL_Write_Handler , " << I << EOL;

char buff[512];

ULONG fwLength=0;

strcpy(buff,(PCHAR)I.IoctlBuffer()); // 拷贝从应用程序得到的命令串到驱动程序局部数据区

t << “InputPut Data is \”“ << buff << ”\“” <<EOL; // 显示从应用程序得到的命令串。

I.Information() = 0;

return status;

}

slide46

Test_Sample.cpp

void Test_SAMPLE_IOCTL_Read(void)

{

CHAR bufInput[IOCTL_INBUF_SIZE]; // Input to device

CHAR bufOutput[IOCTL_OUTBUF_SIZE]; // Output from device

ULONG nOutput; // Count written to bufOutput

// Call device IO Control interface (SAMPLE_IOCTL_Read) in driver

printf("Issuing Ioctl to device - ");

strcpy(bufInput,"This is a sample.");

if (!DeviceIoControl(hDevice,SAMPLE_IOCTL_Read, NULL,0,

bufOutput, IOCTL_OUTBUF_SIZE, &nOutput, NULL))

{

printf("ERROR: DeviceIoControl returns %0x.", GetLastError());

Exit(1);

}

else

printf("Return from driver is \"%s\"(%d)",bufOutput,nOutput);

}

slide47

void Test_SAMPLE_IOCTL_Write(void)

{

CHAR bufInput[IOCTL_INBUF_SIZE]; // Input to device

CHAR bufOutput[IOCTL_OUTBUF_SIZE];// Output from device

ULONG nOutput; // Count written to bufOutput

// Call device IO Control interface (SAMPLE_IOCTL_Write) in driver

printf("Issuing Ioctl to device - ");

strcpy(bufInput,"Now let us write this string.");

if (!DeviceIoControl(hDevice, SAMPLE_IOCTL_Write, bufInput,

strlen(bufInput), NULL, 0, &nOutput, NULL)) // 该函数参数分别是设备句柄、IO控制命令、写缓冲、写缓冲大小、读缓冲、读缓冲大小、实际读的字节数、覆盖结构指针(注意,这里的bufInput是指Input到设备,所以对应用软件这是写缓冲, bufOutput是从设备output到应用程序,是读缓冲。

{

printf("ERROR: DeviceIoControl returns %0x.",

GetLastError());

Exit(1);

}

}

slide48

c. 使用IO控制代码方式写并且读

SampleDevice.cpp

NTSTATUS SampleDevice::SAMPLE_IOCTL_ReadWrite_Handler(KIrp I)

{

NTSTATUS status = STATUS_SUCCESS;

t << "Entering SampleDevice::SAMPLE_IOCTL_ReadWrite_Handler, " << I << EOL;

char buff[512],buff1[512];

ULONG fwLength=0;

strcpy(buff,(PCHAR)I.IoctlBuffer());// 拷贝应用程序来的信息

t << "InputPut Data is \"" << buff << "\"" <<EOL;

strcpy(buff1,"this is feedback from driver! Application give me this string \"");

strcat(buff1,buff);

strcat(buff1,“\”“); // 以上是组织反馈的信息

slide49

fwLength = strlen(buff1)+1;

if (I.IoctlOutputBufferSize() >= fwLength)

{

strcpy((PCHAR)I.IoctlBuffer(),buff1);// 拷贝反馈信息

I.Information() = fwLength; // 设置反馈信息字节数

}

else

{

I.Information() = 0;

t << "buff size too small" << EOL;

}

return status;

}

slide50

Test_Sample.cpp

void Test_SAMPLE_IOCTL_ReadWrite(void)

{

bufOutput is written by the device to return data to this application

CHAR bufInput[IOCTL_INBUF_SIZE]; // Input to device

CHAR bufOutput[IOCTL_OUTBUF_SIZE]; // Output from device

ULONG nOutput; // Count written to bufOutput

// Call device IO Control interface (SAMPLE_IOCTL_ReadWrite) in driver

printf("Issuing Ioctl to device - ");

strcpy(bufInput,"This is a sample.");

if (!DeviceIoControl(hDevice, SAMPLE_IOCTL_ReadWrite, bufInput,

strlen(bufInput),bufOutput, IOCTL_OUTBUF_SIZE, &nOutput, NULL) )

{

printf("ERROR: DeviceIoControl returns %0x.", GetLastError());

Exit(1);

}

else

printf("Feedback is \"%s\"(%d)",bufOutput,nOutput);

}

slide53

3)直接对端口寄存器读写

DriverStudio提供了KIoRange类来将外部总线的I/O地址空间范围映射到处理器总线的地址空间范围。

该类的成员函数主要有

KIoRange构造函数 (4 种格式)

Initialize初始化和重新初始化一个实例 (3种格式)

~KIoRange析构函数

Invalidate从已初始化状态删除该对象

IsValid 测试该对象是否已经初始化

inb读一个或多个字节 (2 种形式)

Outb写一个或多个字节 (2 种形式)

Inw 读一个或多个字 (2 种形式)

Outw 写一个或多个字 (2 种形式)

ind 读一个或多个双字 (2 种形式)

outd写一个或多个双字 (2 种形式)

slide54

KIoRange::KIoRange(只介绍WDM形式)

FORM 3 (WDM):

KIoRange(   ULONGLONG CpuPhysicalAddress, //转换成外围设备地址的CPU总线上的物理地址

BOOLEAN InCpuIoSpace, //如果IO范围是在CPU总线的IO空间中为TRUE,否则为FALSE

   ULONG Count, //以字节计的区域的大小

BOOLEAN MapToSystemVirtual =TRUE//指定是否需要构造函数创建一个非页系统空间的地址空间映射,如果驱动程序读写设备中的数据,就需要这种映射

);

FORM 4 (WDM): (注意: 这种形式不被DriverStudio 2.0支持。)

KIoRange(   PCM_RESOURCE_LIST pTranslatedResourceList, //指向转换资源表的指针

ULONG Ordinal=0, //指定pTranslatedResourceList指向的资源列表中的一个特殊端口资源

BOOLEAN MapToSystemVirtual =TRUE);

FORM 5 (WDM):

KIoRange(PCM_RESOURCE_LISTpTranslatedResourceList, //可通过KIrp::TranslatedResources获得

PCM_RESOURCE_LISTpRawResourceList, //指向原始资源表的指针

ULONGOrdinal=0,BOOLEANMapToSystemVirtual =TRUE);构造 KIoRange类。

slide55

KIoRange::Initialize(只介绍WDM形式)

FORM 2 (WDM):

NTSTATUS Initialize(   ULONGLONG CpuPhysicalAddress,   BOOLEAN InCpuIoSpace,   ULONG Count,   BOOLEAN MapToSystemVirtual=TRUE);

FORM 3 (WDM): (注意: 这种形式不被DriverStudio 2.0支持。)

NTSTATUS Initialize(   PCM_RESOURCE_LIST pTranslatedResourceList,   ULONG Ordinal=0,   BOOLEAN MapToSystemVirtual =TRUE);

FORM 4 (WDM):

Initialize(PCM_RESOURCE_LISTpTranslatedResourceList,PCM_RESOURCE_LISTpRawResourceList,ULONGOrdinal=0,BOOLEANMapToSystemVirtual =TRUE

);

初始化或重新初始化KIoRange的实例。

slide56

KIoRange::inb

FORM 1:

UCHAR inb( ULONG ByteOffset );

FORM 2:

VOID inb(   ULONG ByteOffset,   PUCHAR Buffer,   ULONG Count);

从映射空间读一个或多个字节。

slide57

KIoRange::outb

FORM 1:

VOID outb(   ULONG ByteOffset,//以字节为单位的目标位置到IO空间开始位置的偏移值

UCHAR Data //要写的一个字节数据

);

FORM 2:

VOID outb(   ULONG ByteOffset,   PUCHAR Buffer,//指向包含要写数据的缓冲的指针

ULONG Count //缓冲中要写数据的字节数

);

写一个或多个字节到映射的IO空间。

slide58

下面我们来访问CMOS的数据。

  • 首先定义类KIoRange的一个实例,以定义相关地址空间。KIoRange m_ParPortIos;
  • 初始化实例(指定CMOS的端口首地址,并映射)status = m_ParPortIos.Initialize( 0x70, // CMOS端口首地址是70H TRUE, //在CPU I/O空间内8, // 设备读写数据的字节宽度TRUE // 映射到系统空间);
  • 写端口( 索引信息, 地址70H)m_ParPortIos.outb(0,0x02); // 准备读分钟信息
  • 读端口(读分钟信息,地址71H)UCHAR data = m_ParPortIos.inb(1);
slide59

4)截获中断和挂接中断服务例程

DriverStudio提供了KInterrupt类来截获和挂接中断。

该类的成员函数主要有

KInterrupt构造函数(3种格式)

Initialize在无效状态下初始化一个对象 (3种格式)

Connect绑定ISR(中断服务例程)到中断

InitializeAndConnect一步完成初始化与绑定工作,要用资源列表作为输入。

~KInterrupt析构函数

Invalidate在初始化状态下删除对象

IsValid检查对象是否初始化

Disconnect使中断和ISR与中断分离

Synchronize当得到一个中断自旋锁时请求同步功能

slide60

KInterrupt::KInterrupt(只介绍WDM形式)

FORM 3: (WDM)

KInterrupt(   KIRQL irql, //即插即用设备提供的IRQL值

ULONG vector, //即插即用设备提供的向量值

KINTERRUPT_MODE Mode,//LevelSensitive或 Latched中选一.

   BOOLEAN bShareVector=FALSE, //该向量是否被几个设备共享

KAFFINITY affinity=1, //this is the processor affinity mask.

   BOOLEAN bSaveFloat =FALSE //是否需要在中断到来使保存浮点处理器上下文,X86平台下必须使FALSE);

构造类Kinterrupt的实例。

slide61

KInterrupt::Initialize(只介绍WDM形式)

FORM 2: (WDM)

VOID   Initialize(   KIRQL irql,   ULONG vector,   KINTERRUPT_MODE Mode,   BOOLEAN bShareVector=FALSE,   KAFFINITY affinity=1,   BOOLEAN bSaveFloat=FALSE);

初始化对象。只在对象没有初始化的时候使用。

slide62

KInterrupt::Connect

FORM 1:

NTSTATUS Connect(   PKSERVICE_ROUTINE Isr, //作为ISR服务的函数的地址

PVOID Context //当系统调用ISR的时候传递给他的无类型的参数

);

FORM 2:

NTSTATUS Connect(   PKSERVICE_ROUTINE Isr,   PVOID Context,   PKSPIN_LOCK pSpin,

   KIRQL SynchIrql);

绑定一个中断到ISR(中断处理程序)。

slide63

KInterrupt::InitializeAndConnect

  • NTSTATUS InitializeAndConnect(   PCM_RESOURCE_LIST pResourceList, //指向资源列表的指针
  • PKSERVICE_ROUTINE Isr,   PVOID IsrContext,   ULONG Ordinal=0,   BOOLEAN bSaveFloat=FALSE);
  • 初始化一个中断并绑定到一个ISR上。
    • 对于 WDM 驱动程序,pResourceList必须是一个转换资源表,例如是KIrp::TranslatedResources的返回值。
slide64

下面我们来举例说明。

  • 首先定义类KInterrupt的一个实例KInterrupt m_TheInterrupt;
  • 在设备类中声明一个成员函数TheIsr作为中断服务例程ISR。class SampleDevice : public KPnpDevice{ …… public: MEMBER_ISR (SampleDevice, TheIsr); ……#ifdef _COMMENT_ONLY BOOLEAN TheIsr(void){ return TRUE ; };#endif ……}
slide65

在OnStartDevice例程中获取包括中断的设备资源并初始化中断和挂接ISR

  • SampleDevice ::OnStartDevice(KIrp I)
  • {
  • ……
  • PCM_RESOURCE_LIST pResList = I.TranslatedResources(); //获取设备资源
  • //初始化中断并挂接中断服务例程TheIsr
  • status = m_TheInterrupt.InitializeAndConnect(
  • pResList,
  • LinkTo(TheIsr),
  • this
  • );
  • ……
  • }
slide66

5)如何写Win32类型的驱动程序的测试程序

DriverStudio自动生成的驱动程序的测试程序是一个控制台程序,下面我们将利用该控制台程序来写一个Win32的程序。

第一步,在由DriverStudio自动生成一个驱动程序工作区(Workspace)中添加一个新的子工程,该子工程指定为一个MFC的EXE程序。

右键点击

slide68

第二步,手工在新的工程中添加几个文件

这几个程序需要手工添加进去,实际上这几个程序都在DriverStudio自动生成的原来的两个工程文件中(这个例子是MyIOPort和Test_MyIOPort工程)。将他们添加到新工程中,方法是右键点击新工程中的相关文件夹,在弹出菜单中选择Add Files to Folder…,然后找到要添加的文件并添加。

slide69

第三步,打开OpenByIntf.cpp文件,在开始第一行添加第三步,打开OpenByIntf.cpp文件,在开始第一行添加

#include "stdafx.h"

第四步,

1

3

4

5

2

C:\PROGRAM FILES\COMPUWARE\SOFTICE DRIVER SUITE\DRIVERWORKS\INCLUDE

slide70

1

第五步,

3

4

2

setupapi.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib

可以直接从自动生成的控制台工程的设置中拷贝这些库的名称

slide71

最后,在控制台程序的Test_XXXX.CPP程序基础上进行必要的修改。最后,在控制台程序的Test_XXXX.CPP程序基础上进行必要的修改。

注意要保留的是打开设备、关闭设备、读写设备有关的函数。界面部分应该全部换成Windows界面的代码,而不是保留控制台代码。