Presentation is loading. Please wait.

Presentation is loading. Please wait.

Windows XP驱动程序编写方法 ——Step by Step

Similar presentations


Presentation on theme: "Windows XP驱动程序编写方法 ——Step by Step"— Presentation transcript:

1 Windows XP驱动程序编写方法 ——Step by Step
VS.NET+WIN XP DDK+DriverStudio3.2开发环境版 东南大学计算机科学与工程学院 杨全胜

2 1. 驱动程序的开发环境 本电子讲义可以作为几年前本人所写的《驱动开发》上、下电子讲义的后续篇,主要是将开发平台从Windows98/2000,DriverStudio 2.7升级到以下环境: Windows XP SP2 Visual Studio .NET(VC++.NET 2002) 简体中文版* Windows XP DDK** Driver Studio 3.2 以上四项中,前3项为Microsoft公司产品,可以只用2,3来开发驱动程序。为了方便起见,也可以使用第三方的开发工具Driver Studio ,它将DDK的内容封装成类,而且提供一个快速方便地生成驱动框架的工具。3.2版本可能是Compuware公司推出的最后一个版本。

3 *本电子讲义假设大家已经会VC++编程及熟悉VS IDE的使用。
**通常,开发不同操作系统下的驱动程序需要不同的DDK做支持: Windows 2000 DDK适合开发Windows 2000/98/Me的WDM驱动程序,Windows 2000下NT4型驱动程序。 Windows XP DDK适合开发IA64下的驱动程序或Windows XP/2000/Me的WDM驱动程序,Windows XP下NT4型驱动程序。 Windows 2003 DDK适合开发AMD64/IA64下的驱动程序或Windows 2003/XP/2000/Me的WDM驱动程序,Windows 2003/XP/2000下NT4型驱动程序。

4 2. 驱动程序开发工具包DriverStudio
DriverWorks     DriverWorks提供针对Windows NT 4和Win32驱动模型(WDM)的设备驱动程序开发的完全支持。DriverWorks中包含一个非常完善的源代码生成工具(DriverWizard) 以及相应的类库和驱动程序样本,它提供了在C++下进行设备驱动程序开发的支持。它可以集成到msvc6和msvc.net中,还需要最新的Windows DDK的支持。 VToolsD     VToolsD 是一个用来开发针对Win9X (Windows 95 和 Windows 98)操作系统下设备驱动程序(VxD)的工具。VToolsD 中包括生成驱动程序源代码的工具,run-time 和 interface 库,以及一些可以用来作为各种类型的设备驱动程序基础的驱动程序样本。

5 DriverNetworks     DriverNetworks 是针对Windows网络驱动开发人员的一个模块。它的核心部分,是一个针对NDIS drivers 和 TDI clients (DriverSockets)的 C++ 的类库。DriverNetworks 中也有Quick Miniport Wizard 用来直接开始一个NDIS Miniport ,Intermediate 或协议驱动程序工程。它可以让你在采用DriverNetworks C++ 类库编写NDIS驱动程序的时候,快速的生成编译、安装和调试所需要的所有文件 。它可以集成到msvc6和msvc.net中,还需要最新的Windows DDK的支持。

6 SoftICE系列调试器     SoftICE 系列调试器包含了可以调试各种代码的多种工具。它可以调试诸如BIOS代码、中断例程以及系统I/O。这些工具与强大的硬件调试板一起支持符号级调试,可以显示源码、全局或局部数据。其中: SoftICE 是单机调试器,调试本机代码。 Visual SoftICE是双机调试器,支持64位和32位平台上的微软操作系统。

7 DriverMonitor     DriverMonitor不仅可以显示WDM和VxD在操作系统核心层次输出的调试语句,还可以装载和卸载VxD驱动和NT4系统的驱动程序。
EZDriverInstaller     这是一个无需经过设备管理器或“添加新硬件”功能就能为Windows 2000/XP动态加载和卸载WDM驱动程序的小实用程序。 SetDDKGo     用来设置设备驱动程序创建的环境。当我们用Visual Studio(VC++)编译驱动程序源程序的时候,需要用SetDDKGo来设置环境变量,之后SetDDKGo会自动启动Visual Studio(VC++)编译环境。

8 DriverWorkbench     这是DriverStudio以及用户工具的集成环境和宿主。DS的大多数工具全部被集成到这个开发环境中。
BoundsChecker Driver Edition    它提供了参数验证和系统测试来检测和跟踪不同的设备驱动程序与其他操作系统模块之间的交互。配置 TrueTime Driver Edition     这是一个能让Windows NT/2000/XP设备驱动程序的编写者确定驱动程序性能瓶颈的性能分析工具。对于编写设备驱动程序或核心代码的程序员,这很有用。 TrueCoverage Driver Edition     它能帮助程序员检测其代码的哪部分被测试过,哪部分还需要测试。可帮助程序员提高程序的稳定性。

9 2.2 DriverStudio 3.2的安装 安装需要的软硬件环境 Intel x86兼容系统或X64系统(包含IA64和AMD64以及Itanium) Windows XP 内存: 最少256MB,推荐使用512 MB 硬盘:完全安装需要大约182 MB 针对 SoftICE的远程调试: NE2000-兼容网卡或 3Com 网卡 针对 DriverWorks: Microsoft XP DDK, Visual C++.NET

10 安装步骤: 在安装DriverWorks之前,首先要保证你的计算机上已经安装了Microsoft Visual C++.NET以及Windows XP DDK。 所有这些包括DriverStudio的安装都必须以系统管理员身份启动系统。并且要按照下面的顺序安装。

11 第一步: 安装Visual Studio C++.NET 第二步: 安装Windows XP DDK(Driver Development Kits)。 注意: 1)在安装DDK的时候请选择完全安装。 2)安装中,不需要安装64BIT IA64Binaries 3)安装好后,对于 XP DDK不需要手动配置环境变量,只需在开始菜单中点击Checked Build Envirment 则DDK会自动调用setenv配置环境变量,并监测相应的SDK以及Visual Studio.NET IDE 第三步: 安装DriverStudio 3.2(按照安装提示安装)。

12 Driver Studio 3.2支持单机调试或双机调试两种模式。在安装的时候也有Host和Target两种模式。

13 DriverStudio安装后的设置: 1)使用DDK Build Setting工具定义BASEDIR环境变量并启动MSVC.NET,

14

15

16

17

18 2)打开下列地址上的建立库文件工程VdwLibs2002.sln 如果是VS.NET2003, 则打开 VdwLibs2003.sln

19 3)选择“生成->批生成”,打开下面的窗口,从中选则需要编译的配置。
Checked是调试版本,Free是发布版本

20 4)点击“重新生成”编译所选择的库文件。 注意:库文件只需在安装完成后第一次使用前编译一次即可。以后要使用DriverWorks,只需通过SetDDKGo进入MSVC.NET即可。 或者直接从MSVC.NET中启动DriverWorks。

21 3.Driver Works的使用 1)生成简单框架(VS.NET中启动DriverWizard)

22

23 工程文件名 工程文件目录

24 选择驱动类型 选择框架类型

25 创建功能驱动程序 创建过滤器驱动程序

26 本例不驱动硬件 选择相应总线

27 选择需要处理的消息句柄 要测试的是IRP_MJ_DEVICE_CONTROL, IRP_MJ_READ和IRP_MJWRITE,所以要选择。
但要注意,IRP_MJCREATE一定要选择,否则APP程序无法用CreateFile函数打开设备对象。

28 添加和应用程序之间通信的控制代码

29

30

31

32

33

34

35

36

37

38

39 设备类 驱动类 队列管理类

40 驱动类文件 设备类文件 队列管理类 驱动安装指导文件 测试用的控制台程序文件

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

42 该驱动程序框架包含了几个最基本的类,这些类是:
class SampleDriver : public KDriver // 驱动程序类,用于初始化驱动程序 { SAFE_DESTRUCTORS public: // 以下成员函数注意和WDM中有关例程联系起来看 virtual NTSTATUS DriverEntry(PUNICODE_STRING RegistryPath); virtual NTSTATUS AddDevice(PDEVICE_OBJECT Pdo); virtual VOID Unload(VOID); void LoadRegistryParameters(PUNICODE_STRING RegistryPath); protected: // 成员数据 int m_Unit; };

43 class SampleDevice : public KPnpDevice // 是设备类KDvice的派生类,用于在WDM环境下支持即插即用设备
{ // Constructors public: SAFE_DESTRUCTORS; SampleDevice(PDEVICE_OBJECT Pdo, ULONG Unit); ~SampleDevice(); VOID Invalidate(void); // Member Functions 注意和PNP的次功能代码联系起来看 DEVMEMBER_DISPATCHERS virtual NTSTATUS OnStartDevice(KIrp I); virtual NTSTATUS OnStopDevice(KIrp I); virtual NTSTATUS OnRemoveDevice(KIrp I); virtual NTSTATUS OnDevicePowerUp(KIrp I); virtual NTSTATUS OnDeviceSleep(KIrp I); virtual NTSTATUS DefaultPnp(KIrp I); virtual NTSTATUS DefaultPower(KIrp I); void LoadRegistryParameters(); // 取注册表信息

44 void SerialRead(KIrp I);
void SerialWrite(KIrp I); NTSTATUS SAMPLE_IOCTL_Read_Handler(KIrp I); NTSTATUS SAMPLE_IOCTL_Write_Handler(KIrp I); NTSTATUS SAMPLE_IOCTL_ReadWrite_Handler(KIrp I); protected:// Member Data KPnpLowerDevice m_Lower; sampleQueue ReadQueue; // Driver managed IRP queue sampleQueue WriteQueue; // Driver managed IRP queue #ifdef __COMMENT_ONLY virtual NTSTATUS Create(KIrp I); // COMMENT_ONLY virtual NTSTATUS Close(KIrp I); // COMMENT_ONLY virtual NTSTATUS Read(KIrp I); // COMMENT_ONLY virtual NTSTATUS Write(KIrp I); // COMMENT_ONLY virtual NTSTATUS DeviceControl(KIrp I);// COMMENT_ONLY virtual NTSTATUS SystemControl(KIrp I);// COMMENT_ONLY #endif //__COMMENT_ONLY };

45 由于一个可能是DriverStudio 3.2中的BUG,所以及时生成的一个空工程项目也无法编译通过,需要对生成的工程文件做以下手工修改:
把sample项目中的sources文件中的:  TARGETLIBS=$ (DDK_LIB_PATH)\ntstrsafe.lib $ (DDK_LIB_PATH)\csq.lib   这一行去掉就可以编译通过了

46 下面我们讲解编译、执行和调试这个驱动程序。
先编译驱动程序工程 生成目标文件 在VS2002的集成环境中

47 确认生成的是驱动程序

48 再编译测试应用程序工程 生成目标文件 在VS2002的集成环境中

49 确认生成的是测试用应用程序

50 驱动程序监视,可实时看到驱动程序发出的调试输出语句
下面使用DriverStudio带的工具加载驱动程序和查看调试信息。 驱动程序监视,可实时看到驱动程序发出的调试输出语句 驱动程序装载器,可动态调用驱动程序

51 驱动程序监视器界面

52 为了防止其他驱动程序的干扰,在Filter Message对话框中设置消息过滤规则,只让有sample的消息通过。

53 驱动程序装载器界面

54

55

56

57 驱动程序已经加载并且启动

58 YANGQS

59 DriverStudio 3.2给出的驱动测试软件是一个Win32的窗口程序,而不是先前版本的控制台程序。为了在调试输出的时候有所区别我们将测试程序的调试输出语句的句头由原来的sample:改成sampleAPP: (在sampleAPP.cpp文件中) VOID sampleOutputText(LPCTSTR Format, ...) { TCHAR str[MAX_STRING_LENGTH]; va_list vaList; va_start(vaList, Format); _vstprintf(str, Format, vaList); OutputDebugString(_T("sampleAPP: ")); OutputDebugString(str); OutputDebugString(_T("\n")); va_end(vaList); return; }

60 运行编译好的sampleAPP.exe

61

62 如果在执行sampleAPP. exe之前,驱动程序sample
如果在执行sampleAPP.exe之前,驱动程序sample.sys还没有加载到内存中,则在DriverMonitor程序中就可以看到以下信息:

63 APP中打开与驱动程序联系

64

65 结束后一定要卸载驱动程序

66 驱动程序已经卸载

67 下面我们来修改有关代码,以便增加驱动程序和应用程序之间相互通信的内容。需要增加的内容包括:
2)完成应用程序和驱动程序之间的信息交换 下面我们来修改有关代码,以便增加驱动程序和应用程序之间相互通信的内容。需要增加的内容包括: 使用Read和Write方式分别从驱动程序读入字符和 向驱动程序写字符。 使用IO控制代码方式分别从驱动程序读入字符和 向驱动程序写字符。 使用IO控制代码方式向驱动程序写字符串再从驱动程序中读出该字符串,并返回反馈串信息。 注意:程序中暗红色显示的部分是我们添加或修改过的语句,其他是DriverWorks自动生成的。蓝色显示的部分是要删除的语句。省略号的部分是不变的。语句中T.Trace(TraceInfo, __FUNCTION__“xxxx”)这样的语句是向调试软件输出信息,该信息可在DriverMonitor或其他调试监视器中看到。

68 a.1 使用Read方式读 SampleDevice.cpp void SampleDevice::SerialRead(KIrp I) { T.Trace(TraceInfo, __FUNCTION__"++. IRP %p\n", I); NTSTATUS status = STATUS_SUCCESS; PUCHAR pBuffer = (PUCHAR) I.BufferedReadDest();//取得返回数据BUFF的指针 ULONG readSize = I.ReadSize( ); //获得应用程序希望读驱动程序信息的字节数。 ULONG bytesRead = 0; 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[readSize]='\0'; //指定串尾 strcpy((char *)pBuffer,buff); // 把给应用程序的数据拷贝给返回BUFF T.Trace(TraceInfo,__FUNCTION__ " The string you will read is %s\n", buff ); // 输出调试信息 bytesRead = strlen(buff); // Count of bytes read I.Information() = bytesRead; // 返回给应用程序的信息的字节个数 I.Status() = status; m_DriverManagedQueue.PnpNextIrp(I); }

69 控件IDC_OP_TYPE_COMBO及其选择项,我们这次选ReadFile

70 sampleIorw.cpp中有关读数据的代码:
ULONG sampleExecuteIo(HWND hDlg) { PSAMPLE_LIST_ITEM ioItem; …… // 获得需要读的字节数 GetDlgItemText(hDlg, IDC_OUT_SIZE_EDIT, str, MAX_STRING_LENGTH); ioItem->OutSize = _ttol(str); // 设置控件IDC_OP_TYPE_COMBO的句柄 hWnd = GetDlgItem(hDlg, IDC_OP_TYPE_COMBO); // 获得当前被选中项目的索引 itemIndex = (DWORD)SendMessage(hWnd, CB_GETCURSEL, 0, 0); // 获得被选中的项目的字符串 SendMessage(hWnd, CB_GETLBTEXT, (WPARAM)itemIndex, (LPARAM)str); if ((!_tcscmp(str, _T(“ReadFile”))) && (ioItem->OutSize > 0)) // 如是读端口 // 设置读的缓冲区 ioItem->OutBuffer = (PCHAR)malloc(ioItem->OutSize);

71 // 从驱动程序读数据 if ((!ReadFile( g_hDevice, // 设备句柄 ioItem->OutBuffer, // 输入缓冲地址 ioItem->OutSize, // 缓冲大小(字节数) NULL, // 实际读的数据字节数 &ioItem->IoOverlapped)) && //覆盖结构指针,表示异步读 (GetLastError() != ERROR_IO_PENDING)) { //如果读出错,给出出错的调试信息 error = GetLastError(); sampleOutputText(_T("ReadFile failed with error (%d)"), error); break; } // 设置回调函数,因为是异步读,所以读出数据后回调函数会启动 ioItem->Callback = sampleReadCompleteCallback; …… } // if ((!_tcscmp(str, _T(“ReadFile”) ……

72 VOID sampleReadCompleteCallback(PVOID Context)
{ // 读驱动程序的回调函数 PSAMPLE_LIST_ITEM ioItem = (PSAMPLE_LIST_ITEM)Context; // 因VS.net隐含采用Unicode编码,每个字符16位,下面做8位到16位字符转换 char wstr[1025]; for(ULONG i=0;i<ioItem->OutSize;i++) { wstr[i*2]=ioItem->OutBuffer[i]; wstr[i*2+1]=0x0; } wstr[i*2]=wstr[i*2+1]='\0'; sampleOutputText( _T("Executed ReadFile: buffer size (%d), return length (%d) error (%d) The string I read is %s"), ioItem->OutSize, ioItem->ReturnLength, ioItem->Error, wstr); // 输出读到的字符串 sampleOutputBuffer(ioItem->OutBuffer, ioItem->ReturnLength); // 释放缓冲空间 free(ioItem->OutBuffer); // 关闭重叠事件句柄 CloseHandle(ioItem->IoOverlapped.hEvent); // 释放 ioItem 空间 free(ioItem); return; }

73

74 a.2 使用Write方式写 SampleDevice.cpp void SampleDevice::SerialWrite(KIrp I) { T.Trace(TraceInfo, __FUNCTION__"++. IRP %p\n", I); NTSTATUS status = STATUS_SUCCESS; PUCHAR pBuffer = (PUCHAR)I.BufferedWriteSource();//取得存放应用程序写给驱动程序的数据的BUFF的指针 ULONG writeSize = I.WriteSize( );// 获得应用程序写给驱动程序的信息的字节数。 ULONG bytesSent = 0; bytesSent = writeSize; char buff[512]; strcpy(buff, (char *)pBuffer); // 应用程序写给驱动程序的数据在I.BufferedWriteSource()返回的指针中。 buff[bytesSent] = '\0'; T.Trace(TraceInfo,__FUNCTION__ "Write to driver is %s\n", buff ); I.Information() = bytesSent; // 返回用户实际写的字节数 I.Status() = status; m_DriverManagedQueue.PnpNextIrp(I); }

75 控件IDC_OP_TYPE_COMBO及其选择项,我们这次选WriteFile

76 sampleIorw.cpp中有关写数据的代码:
ULONG sampleExecuteIo(HWND hDlg) { PSAMPLE_LIST_ITEM ioItem; …… // 获得需要写的字节数 //GetDlgItemText(hDlg, IDC_IN_SIZE_EDIT, str, MAX_STRING_LENGTH); // ioItem->InSize = _ttol(str); // 获得要写的字符串和要写的字节数 GetDlgItemText(hDlg, IDC_IN_DATA_EDIT, str, MAX_STRING_LENGTH); // (VOID)_stscanf(str, _T("%x"), &inPattern); char instr[MAX_STRING_LENGTH]; //TCHAR转换成char* for(int i=0;i<MAX_STRING_LENGTH;i++) instr[i]=(char)str[i]; ioItem->InSize=strlen(instr); // 设置控件IDC_OP_TYPE_COMBO的句柄 hWnd = GetDlgItem(hDlg, IDC_OP_TYPE_COMBO); // 获得当前被选中项目的索引 itemIndex = (DWORD)SendMessage(hWnd, CB_GETCURSEL, 0, 0); // 获得被选中的项目的字符串 SendMessage(hWnd, CB_GETLBTEXT, (WPARAM)itemIndex, (LPARAM)str);

77 // Write data to driver if ((!_tcscmp(str, _T(“ReadFile”))) && (ioItem->OutSize > 0)) // 如是读端口 { // 设置写的缓冲区 ioItem->InBuffer = (PCHAR)malloc(ioItem->InSize); …… strcpy((char *)ioItem->InBuffer,instr); // for (ii = 0; ii < ioItem->InSize; ii += sizeof(DWORD)) // { // CopyMemory(ioItem->InBuffer + ii, &inPattern, // min(sizeof(DWORD), ioItem->InSize - ii)); // } if ((!WriteFile(g_hDevice,ioItem->InBuffer,ioItem->InSize,NULL, &ioItem->IoOverlapped)) &&(GetLastError() != ERROR_IO_PENDING)) {//如果读出错,给出出错的调试信息 error = GetLastError(); sampleOutputText(_T("WriteFile failed with error (%d)"), error); break; } // 设置回调函数,因为是异步读,所以读出数据后回调函数会启动 ioItem->Callback = sampleWriteCompleteCallback; } // if ((!_tcscmp(str, _T(“WriteFile”) ……

78

79 b.1 使用IO控制代码方式读 SampleDevice.cpp
NTSTATUS sampleDevice::SAMPLE_IOCTL_Read_Handler(KIrp I) { T.Trace(TraceInfo, __FUNCTION__"++. IRP %p\n", I); NTSTATUS status = STATUS_SUCCESS; ULONG inputSize = I.IoctlInputBufferSize(); ULONG outputSize = I.IoctlOutputBufferSize(); char buff1[512]; ULONG fwLength=0; strcpy(buff1,“Welcome to driver!”); //这是应用程序将要读到的字符串 fwLength = strlen(buff1)+1; if (outputSize >= fwLength) {// 如果读入缓冲够长 strcpy((PCHAR)I.IoctlBuffer(),buff1); // 将信息拷给应用程序读入缓冲 I.Information() = fwLength; // 返回信息长度 } else { I.Information() = 0; // 否则信息长度为0 T.Trace(TraceInfo, __FUNCTION__"buff size too small\n");

80 // Buffered ioctl - using the same buffer so read the buffer before writing the buffer
/* PVOID inputBuffer = I.IoctlBuffer(); PVOID outputBuffer = I.IoctlBuffer(); if (FALSE) { status = STATUS_INVALID_PARAMETER; I.Information() = 0; } else */ T.Trace(NT_SUCCESS(status)?TraceInfo:TraceWarning, __FUNCTION__"--. IRP %p, STATUS %x\n", I, status); return status;

81 sampleIorw.cpp中有关写数据的代码:
ULONG sampleExecuteIo(HWND hDlg) { PSAMPLE_LIST_ITEM ioItem; …… if ((!_tcscmp(str, _T("SAMPLE_IOCTL_Read"))) && ((ioItem->OutSize > 0) || (ioItem->InSize > 0))) // Setup the out buffer if specified if (ioItem->OutSize > 0) ioItem->OutBuffer = (PCHAR)malloc(ioItem->OutSize); }

82 if ((!DeviceIoControl(
g_hDevice, // 设备句柄 SAMPLE_IOCTL_Read, // IO控制命令 ioItem->InBuffer, //写缓冲 ioItem->InSize, //写缓冲大小 ioItem->OutBuffer, //读缓冲 ioItem->OutSize, //读缓冲大小 NULL, //实际读的字节数 &ioItem->IoOverlapped)) && //覆盖结构指针 (GetLastError() != ERROR_IO_PENDING)) { error = GetLastError(); sampleOutputText(_T("SAMPLE_IOCTL_Read failed with error (%d)"), error); break; } // Setup the entry ioItem->Callback = sampleSAMPLE_IOCTL_ReadCompleteCallback; …… } //if ((!_tcscmp(str, _T("SAMPLE_IOCTL_Read")……

83 VOID sampleSAMPLE_IOCTL_ReadCompleteCallback(PVOID Context)
{ PSAMPLE_LIST_ITEM ioItem = (PSAMPLE_LIST_ITEM)Context; char wstr[1025]; for(ULONG i=0;i<ioItem->OutSize;i++) wstr[i*2]=ioItem->OutBuffer[i]; wstr[i*2+1]=0x0; } wstr[i*2]=wstr[i*2+1]='\0'; sampleOutputText( _T(“Executed SAMPLE_IOCTL_Read request: in buffer size (%d), out buffer size(%d), \nreturn length (%d) error (%d) The string I readed is: %s"), ioItem->InSize, ioItem->OutSize, ioItem->ReturnLength, ioItem->Error, wstr); ……

84

85 b.2 使用IO控制代码方式写 SampleDevice.cpp
NTSTATUS sampleDevice::SAMPLE_IOCTL_Write_Handler(KIrp I) { T.Trace(TraceInfo, __FUNCTION__"++. IRP %p\n", I); NTSTATUS status = STATUS_SUCCESS; ULONG inputSize = I.IoctlInputBufferSize(); ULONG outputSize = I.IoctlOutputBufferSize(); char buff[512]; strcpy(buff, (char *)I.IoctlBuffer()); // 应用程序写给驱动程序的数据在 I.BufferedWriteSource()返回的指针中。 buff[inputSize] = '\0'; T.Trace(TraceInfo,__FUNCTION__" Write to driver is %s\n", buff ); I.Information() = 0;

86 // Buffered ioctl - using the same buffer so read the buffer before writing the buffer
// PVOID inputBuffer = I.IoctlBuffer(); // PVOID outputBuffer = I.IoctlBuffer(); /*if (FALSE) { status = STATUS_INVALID_PARAMETER; I.Information() = 0; } else T.Trace(NT_SUCCESS(status)?TraceInfo:TraceWarning, __FUNCTION__"--. IRP %p, STATUS %x\n", I, status); return status;

87 sampleIorw.cpp中有关写数据的代码:
ULONG sampleExecuteIo(HWND hDlg) //本页的修改实际上前面Write时已经改好 { PSAMPLE_LIST_ITEM ioItem; …… // 获得需要写的字节数 //GetDlgItemText(hDlg, IDC_IN_SIZE_EDIT, str, MAX_STRING_LENGTH); // ioItem->InSize = _ttol(str); // 获得要写的字符串和要写的字节数 GetDlgItemText(hDlg, IDC_IN_DATA_EDIT, str, MAX_STRING_LENGTH); // (VOID)_stscanf(str, _T("%x"), &inPattern); char instr[MAX_STRING_LENGTH]; //TCHAR转换成char* for(int i=0;i<MAX_STRING_LENGTH;i++) instr[i]=(char)str[i]; ioItem->InSize=strlen(instr); // 设置控件IDC_OP_TYPE_COMBO的句柄 hWnd = GetDlgItem(hDlg, IDC_OP_TYPE_COMBO); // 获得当前被选中项目的索引 itemIndex = (DWORD)SendMessage(hWnd, CB_GETCURSEL, 0, 0); // 获得被选中的项目的字符串 SendMessage(hWnd, CB_GETLBTEXT, (WPARAM)itemIndex, (LPARAM)str);

88 if ((!_tcscmp(str, _T(“SAMPLEIOCTL_Write”))) && ((ioItem->OutSize > 0) || (ioItem->InSize > 0)))// 如是写端口,本页的部分需要修改 { // 设置写的缓冲区 ioItem->InBuffer = (PCHAR)malloc(ioItem->InSize); …… strcpy((char *)ioItem->InBuffer,instr); // for (ii = 0; ii < ioItem->InSize; ii += sizeof(DWORD)) // { // CopyMemory(ioItem->InBuffer + ii, &inPattern, // min(sizeof(DWORD), ioItem->InSize - ii)); // } if ((!DeviceIoControl(g_hDevice, SAMPLE_IOCTL_Write, ioItem->InBuffer, ioItem->InSize, ioItem->OutBuffer, ioItem->OutSize, NULL, &ioItem->IoOverlapped)) && (GetLastError() != ERROR_IO_PENDING)) { …… } // 设置回调函数,因为是异步读,所以读出数据后回调函数会启动 ioItem->Callback = sampleSAMPLEIOCTL_WriteCompleteCallback; } //if ((!_tcscmp(str, _T("SAMPLEIOCTL_Write"))) ……

89

90 c. 使用IO控制代码方式写并且读 SampleDevice.cpp
NTSTATUS sampleDevice::IOCTL_ReadWrite_Handler(KIrp I) { Trace(TraceInfo, __FUNCTION__"++. IRP %p\n", I); NTSTATUS status = STATUS_SUCCESS; ULONG inputSize = I.IoctlInputBufferSize(); ULONG outputSize = I.IoctlOutputBufferSize(); // Buffered ioctl - using the same buffer so read the buffer before writing the buffer // PVOID inputBuffer = I.IoctlBuffer(); // PVOID outputBuffer = I.IoctlBuffer(); char buff[512]; strcpy(buff, (char *)I.IoctlBuffer()); // 取应用程序写给驱动程序的数据 buff[inputSize] = '\0'; T.Trace(TraceInfo,__FUNCTION__" Application write to driver is %s\n", buff ); char buff1[512]; ULONG fwLength=0; strcpy(buff1,"This is feedback from driver! Feedback string is "); strcat(buff1,buff); strcat(buff1,"\n"); fwLength = strlen(buff1)+1;

91 if (outputSize >= fwLength) { // 如果读入缓冲够长
strcpy((PCHAR)I.IoctlBuffer(),buff1); // 将信息拷给应用程序读入缓冲 I.Information() = fwLength; // 返回信息长度 } else { I.Information() = 0; // 否则信息长度为0 T.Trace(TraceInfo, __FUNCTION__"buff size too small\n"); /* if (FALSE) { status = STATUS_INVALID_PARAMETER; I.Information() = 0; else } */ T.Trace(NT_SUCCESS(status)?TraceInfo:TraceWarning, __FUNCTION__"--. IRP %p, STATUS %x\n", I, status); return status;

92 sampleIorw.cpp中有关读写数据的代码:
ULONG sampleExecuteIo(HWND hDlg) //本页的修改实际上前面Write时已经改好 { PSAMPLE_LIST_ITEM ioItem; …… // 获得需要写的字节数 //GetDlgItemText(hDlg, IDC_IN_SIZE_EDIT, str, MAX_STRING_LENGTH); // ioItem->InSize = _ttol(str); // 获得要写的字符串和要写的字节数 GetDlgItemText(hDlg, IDC_IN_DATA_EDIT, str, MAX_STRING_LENGTH); // (VOID)_stscanf(str, _T("%x"), &inPattern); char instr[MAX_STRING_LENGTH]; //TCHAR转换成char* for(int i=0;i<MAX_STRING_LENGTH;i++) instr[i]=(char)str[i]; ioItem->InSize=strlen(instr); // 设置控件IDC_OP_TYPE_COMBO的句柄 hWnd = GetDlgItem(hDlg, IDC_OP_TYPE_COMBO); // 获得当前被选中项目的索引 itemIndex = (DWORD)SendMessage(hWnd, CB_GETCURSEL, 0, 0); // 获得被选中的项目的字符串 SendMessage(hWnd, CB_GETLBTEXT, (WPARAM)itemIndex, (LPARAM)str);

93 if ((!_tcscmp(str, _T(“IOCTL_ReadWrite”))) && ((ioItem->OutSize > 0) || (ioItem->InSize > 0))) //如是读写端口,本页的的内容需要对照着修改,注意if语句的条件 { // 设置写的缓冲区 ioItem->InBuffer = (PCHAR)malloc(ioItem->InSize); …… strcpy((char *)ioItem->InBuffer,instr); // for (ii = 0; ii < ioItem->InSize; ii += sizeof(DWORD)) // { // CopyMemory(ioItem->InBuffer + ii, &inPattern, // min(sizeof(DWORD), ioItem->InSize - ii)); // } if ((!DeviceIoControl(g_hDevice, IOCTL_ReadWrite, ioItem->InBuffer, ioItem->InSize, ioItem->OutBuffer, ioItem->OutSize, NULL, &ioItem->IoOverlapped)) && (GetLastError() != ERROR_IO_PENDING)) { …… } // 设置回调函数,因为是异步读,所以读出数据后回调函数会启动 ioItem->Callback = sampleIOCTL_ReadWriteCompleteCallback; } //if ((!_tcscmp(str, _T("SAMPLEIOCTL_Write"))) ……

94 VOID sampleIOCTL_ReadWriteCompleteCallback(PVOID Context)
{ PSAMPLE_LIST_ITEM ioItem = (PSAMPLE_LIST_ITEM)Context; // 因VS.net隐含采用Unicode编码,每个字符16位,下面做8位到16位字符转换 char wstr[1025]; for(ULONG i=0;i<ioItem->OutSize;i++) wstr[i*2]=ioItem->OutBuffer[i]; wstr[i*2+1]=0x0; } wstr[i*2]=wstr[i*2+1]='\0'; sampleOutputText( _T("Executed IOCTL_ReadWrite request: in buffer size (%d), out buffer size (%d), \nreturn length (%d) error (%d) The string I readed is: %s"), ioItem->InSize, ioItem->OutSize, ioItem->ReturnLength,ioItem->Error, wstr ); ……

95

96 3)直接对端口寄存器读写 DriverStudio提供了KIoRange类来将外部总线的I/O地址空间范围映射到处理器总线的地址空间范围。 该类的成员函数主要有 KIoRange 构造函数 (4 种格式) Initialize 初始化和重新初始化一个实例 (3种格式) ~KIoRange 析构函数 Invalidate 从已初始化状态删除该对象 IsValid 测试该对象是否已经初始化 inb 读一个或多个字节 (2 种形式) Outb 写一个或多个字节 (2 种形式) Inw 读一个或多个字 (2 种形式) Outw 写一个或多个字 (2 种形式) ind 读一个或多个双字 (2 种形式) outd 写一个或多个双字 (2 种形式)

97 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_LIST pTranslatedResourceList, //可通过KIrp::TranslatedResources获得    PCM_RESOURCE_LIST pRawResourceList, //指向原始资源表的指针    ULONG Ordinal=0,    BOOLEAN MapToSystemVirtual =TRUE );构造 KIoRange类。

98 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_LIST pTranslatedResourceList,    PCM_RESOURCE_LIST pRawResourceList,    ULONG Ordinal=0,    BOOLEAN MapToSystemVirtual =TRUE ); 初始化或重新初始化KIoRange的实例。

99 KIoRange::inb FORM 1: UCHAR inb( ULONG ByteOffset ); FORM 2: VOID inb(    ULONG ByteOffset,    PUCHAR Buffer,    ULONG Count ); 从映射空间读一个或多个字节。

100 KIoRange::outb FORM 1: VOID outb(    ULONG ByteOffset,//以字节为单位的目标位置到IO空间开始位置的偏移值   UCHAR Data //要写的一个字节数据 ); FORM 2: VOID outb(    ULONG ByteOffset,    PUCHAR Buffer,//指向包含要写数据的缓冲的指针   ULONG Count //缓冲中要写数据的字节数 写一个或多个字节到映射的IO空间。

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

102 4)截获中断和挂接中断服务例程 DriverStudio提供了KInterrupt类来截获和挂接中断。 该类的成员函数主要有 KInterrupt 构造函数(3种格式) Initialize 在无效状态下初始化一个对象 (3种格式) Connect 绑定ISR(中断服务例程)到中断 InitializeAndConnect 一步完成初始化与绑定工作,要用资源列表作为输入。 ~KInterrupt 析构函数 Invalidate 在初始化状态下删除对象 IsValid 检查对象是否初始化 Disconnect 使中断和ISR与中断分离 Synchronize 当得到一个中断自旋锁时请求同步功能

103 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的实例。

104 KInterrupt::Initialize(只介绍WDM形式)
FORM 2: (WDM) VOID    Initialize(    KIRQL irql,    ULONG vector,    KINTERRUPT_MODE Mode,    BOOLEAN bShareVector=FALSE,    KAFFINITY affinity=1,    BOOLEAN bSaveFloat=FALSE ); 初始化对象。只在对象没有初始化的时候使用。

105 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(中断处理程序)。

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

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

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

109 本讲义参考了Driver Studio有关文档


Download ppt "Windows XP驱动程序编写方法 ——Step by Step"

Similar presentations


Ads by Google