zoukankan      html  css  js  c++  java
  • DeviceIoControl与驱动交互

    与驱动程序通信的函数,除了ReadFile和WriteFile函数还有DeviceIoControl函数,而且DeviceIoControl函数那是相当的彪悍。因为它可以自定义控制码,你只要在IRP_MJ_DEVICE_CONTROL对应的派遣函数中读取控制码,然后针对控制码,你就可以实现自定义的功能了。

    函数原型:

    BOOL WINAPI DeviceIoControl(

     __in         HANDLEhDevice,

     __in         DWORDdwIoControlCode,

     __in_opt     LPVOID lpInBuffer,

     __in         DWORDnInBufferSize,

     __out_opt    LPVOID lpOutBuffer,

     __in         DWORDnOutBufferSize,

     __out_opt    LPDWORD lpBytesReturned,

     __inout_opt  LPOVERLAPPED lpOverlapped

    );

    其中lpBytesReturned的值来自于IRP结构中的pIRP->IoStatus.Information。DeviceIoControl的第二个参数就是控制码,控制码是一个32为无符号整型,需要符合DDK的规定。

    控制代码中各数据位字段的含义如下: 
    ◎ DeviceType--设备类型(31-16bit指 出了设备的类型,微软保留了0-7FFFh的取值,剩下的8000h-0FFFFh   供开发商定义新的内核模式驱动程序。我们可以在\include\w2k\ntddk.inc文件中找到一组FILE_DEVICE_XXX  符号常量,这些值都是微软保留的 值,我们可以使用其中的FILE_DEVICE_UNKNOWN。当然你也可以定义另外一个FILE_DEVICE_XXX值

    ◎ Access--存取代码(15-14bit指明应用程序存取设备的方式,由于这个字段只有2位,所以只有4种可能性: 

    · FILE_ANY_ACCESS (0)--最大的存取权限,就是什么操作都可以 

    · FILE_READ_ACCESS (1)--读权限,设备将数据传递到指定的缓冲区 

    · FILE_WRITE_ACCESS (2)--写权限,可以从内存中向设备传递数据 

    · FILE_READ_ACCESS or FILE_WRITE_ACCESS (3)--读写权限,设备和内存缓冲区之间可以互相传递数据 

    ◎ Function--功能代码(13-2bit)用来描述要进行的操作,我们可以用800h-0FFFh来定义自己的I/O控制代码,

       0-7FFh之间的值是被微软保留的,用来定义公用的I/O控制代码 

    ◎ Method--缓冲模式(0-1bit)表示I/O管理器如何对输入和输出的数据进行缓冲,这个字段的长度是2位,所以有

       4种可能性: 

    ·METHOD_BUFFERED (0)--对I/O进行缓冲 

    ·METHOD_IN_DIRECT (1)--对输入不进行缓冲 

    ·METHOD_OUT_DIRECT (2)--对输出不进行缓冲 

    ·METHOD_NEITHER (3)--都不缓冲 

    缓冲区模式虽然会损失点性能,但是其安全性好。

    下面将分别讲述这几种模式。

    缓冲内存模式(对应代码中的IOCTL_TEST1)

    首先要将控制码中的Method设置为METHOD_BUFFERED。

    往驱动中Input数据:在Win32 APIDeviceIoControl函数的内部,用户提供的输入缓冲区的内容被复制到IRP的pIRP->AssociatedIrp.SystemBuffer的内存地址,复制的字节是有DeviceControl指定的输入字节数。从驱动中Output数据:派遣函数可以向pIRP->AssociatedIrp.SystemBuffer写入数据,被当做是设备输出的数据。操作系统会将AssociatedIrp.SystemBuffer的数据再次复制到DeviceIoControl提供的输出缓冲区,复制的字节数有pIrp->IoStatus.Information指定,DeviceIoControl也可以通过参数lpBytesReturned得到复制的字节数。       

    原理就是这样了,理论上就可以实现读和写的双向操作了。

    直接内存模式(对应代码中的IOCTL_TEST2)

    首先将Method设置为METHOD_IN_DIRECT 或METHOD_OUT_DIRECT ,这两者的不同只是体现在打开设备的权限上,当以只读权限打开设备时,METHOD_IN_DIRECT 就可以顺利操作,而METHOD_OUT_DIRECT 就会失败。如果以读写权限打开时,两者都可以执行成功。

    往驱动中Input数据:这部分和上面的缓冲内存模式一样,输入缓冲区的数据复制到pIrp->AssociateIrp.SystemBuffer内存地址,复制的字节数是按照DeviceIoControl指定的。

    从驱动中Output数据:操作系统会为DeviceIoControl指定的输出缓冲区锁定,然后在内核模式地址下重新映射到一段地址。在派遣函数中可以先获取DeviceIoControl指定的输出缓冲区(lpOutBufferb被记录在pIrp->AssociateIrp.SystemBuffer),然后再通过MmGetSystemAddressForMdlSafe获取其在核地址中的映射值。

    其他内存模式(对应代码中的IOCTL_TEST3)

    个人觉得这种方式挺麻烦的而且少被用到,由于它是直接访问用户模式地址,要求调用DeviceIoControl的线程和派遣函数运行在同一个线程设备上下文中,自己有个印象就行了。

    首先将指定的Method参数设置为METHOD_NEITHER。

    往驱动中Input数据:通过I/O堆栈的Parameters.DeviceIoControl.Type3InputBuffer得到DeviceIoControl提供的输入缓冲区地址,Parameters.DeviceIoControl.InputBufferLength得到其长度。由于不能保证传递过来的地址合法,所以需要先要结果ProbeRead函数进行判断。

    从驱动中Output数据:通过pIrp->UserBuffer得到DeviceIoControl函数提供的输出缓冲区地址,再通过Parameters.DeviceIoControl.OutputBufferLength得到输出缓冲区大小。同样的要用ProbeWrite函数先进行判断。

    下面给出一个实例代码,来自于张帆的《Windows驱动开发详解》

    首先是控制码设置:

    #define IOCTL_TEST1 CTL_CODE(\
    
                            FILE_DEVICE_UNKNOWN, \
    
                            0x800, \
    
                            METHOD_BUFFERED, \
    
                            FILE_ANY_ACCESS)
    
     
    
    #define IOCTL_TEST2 CTL_CODE(\
    
                            FILE_DEVICE_UNKNOWN, \
    
                           0x801, \
    
                            METHOD_IN_DIRECT, \
    
                            FILE_ANY_ACCESS)
    
     
    
    #define IOCTL_TEST3 CTL_CODE(\
    
                            FILE_DEVICE_UNKNOWN, \
    
                            0x802, \
    
                            METHOD_NEITHER, \
    
                            FILE_ANY_ACCESS)
    
     
    
    再是IRP_MJ_DEVICE_CONTROL派遣函数:
    
    NTSTATUSIOCTRLDRIVER_DispatchDeviceControl(
    
           IN PDEVICE_OBJECT              DeviceObject,
    
           IN PIRP                                pIrp
    
           )
    
    {
    
           NTSTATUS status = STATUS_SUCCESS;
    
     
    
           PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
    
           //得到输入缓冲区大小
    
           ULONG cbin = stack->Parameters.DeviceIoControl.InputBufferLength;
    
           //得到输出缓冲区大小
    
           ULONG cbout = stack->Parameters.DeviceIoControl.OutputBufferLength;
    
           //得到IOCTL码
    
           ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;
    
           ULONG info = 0;
    
           switch(code)
    
           {
    
           case IOCTL_TEST1:
    
                    {
    
                            KdPrint(("zhui:IOCTL_TEST1\n"));
    
                           
    
                            UCHAR* InputBuffer =(UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
    
                            for (ULONGi=0;i<cbin;i++)
    
                            {
    
                                    KdPrint(("zhui:%X\n",InputBuffer[i]));
    
                            }
    
     
    
                            //操作输出缓冲区
    
                            UCHAR* OutputBuffer =(UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
    
                           memset(OutputBuffer,0xAA,cbout);
    
                            //设置实际操作输出缓冲区长度
    
                            info = cbout;
    
                            break;
    
                    }
    
           case IOCTL_TEST2:
    
                    {
    
                            KdPrint(("zhui:IOCTL_TEST2\n"));
    
                           
    
                            UCHAR* InputBuffer =(UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
    
                            for (ULONGi=0;i<cbin;i++)
    
                            {
    
                                   KdPrint(("zhui:%X\n",InputBuffer[i]));
    
                            }
    
     
    
                            //pIrp->MdlAddress为DeviceIoControl输出缓冲区地址相同
    
                            KdPrint(("zhui:UserAddress:0X%08X\n",MmGetMdlVirtualAddress(pIrp->MdlAddress)));
    
     
    
                            UCHAR* OutputBuffer =(UCHAR*)MmGetSystemAddressForMdlSafe(pIrp->MdlAddress,NormalPagePriority);
    
                            //InputBuffer被映射到内核模式下的内存地址,必定在0X80000000-0XFFFFFFFF之间
    
                           memset(OutputBuffer,0xAA,cbout);
    
                            //设置实际操作输出缓冲区长度
    
                            info = cbout;
    
                            break;
    
                    }
    
           case IOCTL_TEST3:
    
                    {
    
                            KdPrint(("zhui:IOCTL_TEST3\n"));
    
                           
    
                            UCHAR* UserInputBuffer= (UCHAR*)stack->Parameters.DeviceIoControl.Type3InputBuffer;
    
                            KdPrint(("zhui:UserInputBuffer:0X%0X\n",UserInputBuffer));
    
     
    
                            //得到用户模式地址
    
                            PVOID UserOutputBuffer= pIrp->UserBuffer;
    
     
    
                            KdPrint(("zhui:UserOutputBuffer:0X%0X\n",UserOutputBuffer));
    
     
    
                            __try
    
                            {
    
                                   KdPrint(("zhui:Enter __try block\n"));
    
     
    
                                    //判断指针是否可读
    
                                   ProbeForRead(UserInputBuffer,cbin,4);
    
                                    //显示输入缓冲区内容
    
                                    for (ULONGi=0;i<cbin;i++)
    
                                    {
    
                                           KdPrint(("zhui:%X\n",UserInputBuffer[i]));
    
                                    }
    
     
    
                                    //判断指针是否可写
    
                                   ProbeForWrite(UserOutputBuffer,cbout,4);
    
     
    
                                    //操作输出缓冲区
    
                                   memset(UserOutputBuffer,0xAA,cbout);
    
     
    
                                    //由于在上面引发异常,所以以后语句不会被执行!
    
                                    info = cbout;
    
     
    
                                   KdPrint(("zhui:Leave __try block\n"));
    
                            }
    
                           __except(EXCEPTION_EXECUTE_HANDLER)
    
                            {
    
                                   KdPrint(("zhui:Catch the exception\n"));
    
                                   KdPrint(("zhui:The program will keep going\n"));
    
                                    status =STATUS_UNSUCCESSFUL;
    
                            }
    
     
    
                            info = cbout;
    
                            break;
    
                    }
    
           default:
    
                    status =STATUS_INVALID_DEVICE_REQUEST;
    
                    break;
    
           }
    
           pIrp->IoStatus.Status = status;
    
           pIrp->IoStatus.Information = info;
    
           IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    
           return status;
    
    }



    测试的main函数:

    int main()
    
    {
    
           HANDLE hDevice =
    
                   CreateFile("\\\\.\\HelloDDK",
    
                                           GENERIC_READ | GENERIC_WRITE,
    
                                            0,              // share mode none
    
                                           NULL,   // no security
    
                                           OPEN_EXISTING,
    
                                           FILE_ATTRIBUTE_NORMAL,
    
                                            NULL);         // no template
    
     
    
           if (hDevice == INVALID_HANDLE_VALUE)
    
           {
    
                    printf("Failed to obtainfile handle to device: "
    
                            "%s with Win32error code: %d\n",
    
                           "MyWDMDevice", GetLastError() );
    
                    return 1;
    
           }
    
     
    
           UCHAR InputBuffer[10];
    
           UCHAR OutputBuffer[10];
    
           //将输入缓冲区全部置成0XBB
    
           memset(InputBuffer,0xBB,10);
    
           DWORD dwOutput;
    
           //输入缓冲区作为输入,输出缓冲区作为输出
    
     
    
           BOOL bRet;
    
           bRet = DeviceIoControl(hDevice, IOCTL_TEST1, InputBuffer, 10,&OutputBuffer, 10, &dwOutput, NULL);
    
           if (bRet)
    
           {
    
                    printf("Output buffer:%dbytes\n",dwOutput);
    
                    for (inti=0;i<(int)dwOutput;i++)
    
                    {
    
                            printf("%02X",OutputBuffer[i]);
    
                    }
    
                    printf("\n");
    
           }
    
     
    
           bRet = DeviceIoControl(hDevice, IOCTL_TEST2, InputBuffer, 10,&OutputBuffer, 10, &dwOutput, NULL);
    
           if (bRet)
    
           {
    
                    printf("Output buffer:%dbytes\n",dwOutput);
    
                    for (inti=0;i<(int)dwOutput;i++)
    
                    {
    
                            printf("%02X",OutputBuffer[i]);
    
                    }
    
                   printf("\n");
    
           }
    
     
    
           bRet = DeviceIoControl(hDevice, IOCTL_TEST3, InputBuffer, 10,&OutputBuffer, 10, &dwOutput, NULL);
    
           if (bRet)
    
           {
    
                    printf("Output buffer:%dbytes\n",dwOutput);
    
                    for (int i=0;i<(int)dwOutput;i++)
    
                    {
    
                            printf("%02X",OutputBuffer[i]);
    
                    }
    
                    printf("\n");
    
           }
    
     
    
           CloseHandle(hDevice);
    
     
    
           return 0;
    
    }



    首先是控制码设置:

    #define IOCTL_TEST1 CTL_CODE(\
    			FILE_DEVICE_UNKNOWN, \
    			0x800, \
    			METHOD_BUFFERED, \
    			FILE_ANY_ACCESS)
    
    #define IOCTL_TEST2 CTL_CODE(\
    			FILE_DEVICE_UNKNOWN, \
    			0x801, \
    			METHOD_IN_DIRECT, \
    			FILE_ANY_ACCESS)
    
    #define IOCTL_TEST3 CTL_CODE(\
    			FILE_DEVICE_UNKNOWN, \
    			0x802, \
    			METHOD_NEITHER, \
    			FILE_ANY_ACCESS)


    再是IRP_MJ_DEVICE_CONTROL派遣函数:

    NTSTATUS IOCTRLDRIVER_DispatchDeviceControl(
    	IN PDEVICE_OBJECT		DeviceObject,
    	IN PIRP					pIrp
    	)
    {
    	NTSTATUS status = STATUS_SUCCESS;
    
    	PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
    	//得到输入缓冲区大小
    	ULONG cbin = stack->Parameters.DeviceIoControl.InputBufferLength;
    	//得到输出缓冲区大小
    	ULONG cbout = stack->Parameters.DeviceIoControl.OutputBufferLength;
    	//得到IOCTL码
    	ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;
    	ULONG info = 0;
    	switch(code)
    	{
    	case IOCTL_TEST1:
    		{
    			KdPrint(("zhui:IOCTL_TEST1\n"));
    			
    			UCHAR* InputBuffer = (UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
    			for (ULONG i=0;i<cbin;i++)
    			{
    				KdPrint(("zhui:%X\n",InputBuffer[i]));
    			}
    
    			//操作输出缓冲区
    			UCHAR* OutputBuffer = (UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
    			memset(OutputBuffer,0xAA,cbout);
    			//设置实际操作输出缓冲区长度
    			info = cbout;
    			break;
    		}
    	case IOCTL_TEST2:
    		{
    			KdPrint(("zhui:IOCTL_TEST2\n"));
    			
    			UCHAR* InputBuffer = (UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
    			for (ULONG i=0;i<cbin;i++)
    			{
    				KdPrint(("zhui:%X\n",InputBuffer[i]));
    			}
    
    			//pIrp->MdlAddress为DeviceIoControl输出缓冲区地址相同
    			KdPrint(("zhui:User Address:0X%08X\n",MmGetMdlVirtualAddress(pIrp->MdlAddress)));
    
    			UCHAR* OutputBuffer = (UCHAR*)MmGetSystemAddressForMdlSafe(pIrp->MdlAddress,NormalPagePriority);
    			//InputBuffer被映射到内核模式下的内存地址,必定在0X80000000-0XFFFFFFFF之间
    			memset(OutputBuffer,0xAA,cbout);
    			//设置实际操作输出缓冲区长度
    			info = cbout;
    			break;
    		}
    	case IOCTL_TEST3:
    		{
    			KdPrint(("zhui:IOCTL_TEST3\n"));
    			
    			UCHAR* UserInputBuffer = (UCHAR*)stack->Parameters.DeviceIoControl.Type3InputBuffer;
    			KdPrint(("zhui:UserInputBuffer:0X%0X\n",UserInputBuffer));
    
    			//得到用户模式地址
    			PVOID UserOutputBuffer = pIrp->UserBuffer;
    
    			KdPrint(("zhui:UserOutputBuffer:0X%0X\n",UserOutputBuffer));
    
    			__try
    			{
    				KdPrint(("zhui:Enter __try block\n"));
    
    				//判断指针是否可读
    				ProbeForRead(UserInputBuffer,cbin,4);
    				//显示输入缓冲区内容
    				for (ULONG i=0;i<cbin;i++)
    				{
    					KdPrint(("zhui:%X\n",UserInputBuffer[i]));
    				}
    
    				//判断指针是否可写
    				ProbeForWrite(UserOutputBuffer,cbout,4);
    
    				//操作输出缓冲区
    				memset(UserOutputBuffer,0xAA,cbout);
    
    				//由于在上面引发异常,所以以后语句不会被执行!
    				info = cbout;
    
    				KdPrint(("zhui:Leave __try block\n"));
    			}
    			__except(EXCEPTION_EXECUTE_HANDLER)
    			{
    				KdPrint(("zhui:Catch the exception\n"));
    				KdPrint(("zhui:The program will keep going\n"));
    				status = STATUS_UNSUCCESSFUL;
    			}
    
    			info = cbout;
    			break;
    		}
    	default:
    		status = STATUS_INVALID_DEVICE_REQUEST;
    		break;
    	}
    	pIrp->IoStatus.Status = status;
    	pIrp->IoStatus.Information = info;
    	IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    	return status;
    }


    测试的main函数:

    int main()
    {
    	HANDLE hDevice = 
    		CreateFile("\\\\.\\HelloDDK",
    					GENERIC_READ | GENERIC_WRITE,
    					0,		// share mode none
    					NULL,	// no security
    					OPEN_EXISTING,
    					FILE_ATTRIBUTE_NORMAL,
    					NULL );		// no template
    
    	if (hDevice == INVALID_HANDLE_VALUE)
    	{
    		printf("Failed to obtain file handle to device: "
    			"%s with Win32 error code: %d\n",
    			"MyWDMDevice", GetLastError() );
    		return 1;
    	}
    
    	UCHAR InputBuffer[10];
    	UCHAR OutputBuffer[10];
    	//将输入缓冲区全部置成0XBB
    	memset(InputBuffer,0xBB,10);
    	DWORD dwOutput;
    	//输入缓冲区作为输入,输出缓冲区作为输出
    
    	BOOL bRet;
    	bRet = DeviceIoControl(hDevice, IOCTL_TEST1, InputBuffer, 10, &OutputBuffer, 10, &dwOutput, NULL);
    	if (bRet)
    	{
    		printf("Output buffer:%d bytes\n",dwOutput);
    		for (int i=0;i<(int)dwOutput;i++)
    		{
    			printf("%02X ",OutputBuffer[i]);
    		}
    		printf("\n");
    	}
    
    	bRet = DeviceIoControl(hDevice, IOCTL_TEST2, InputBuffer, 10, &OutputBuffer, 10, &dwOutput, NULL);
    	if (bRet)
    	{
    		printf("Output buffer:%d bytes\n",dwOutput);
    		for (int i=0;i<(int)dwOutput;i++)
    		{
    			printf("%02X ",OutputBuffer[i]);
    		}
    		printf("\n");
    	}
    
     	bRet = DeviceIoControl(hDevice, IOCTL_TEST3, InputBuffer, 10, &OutputBuffer, 10, &dwOutput, NULL);
    	if (bRet)
    	{
    		printf("Output buffer:%d bytes\n",dwOutput);
    		for (int i=0;i<(int)dwOutput;i++)
    		{
    			printf("%02X ",OutputBuffer[i]);
    		}
    		printf("\n");
    	}
    
    	CloseHandle(hDevice);
    
    	return 0;
    }


  • 相关阅读:
    wpf 命令
    wpf 事件
    wpf 依赖属性介绍
    wpf binging (六)多绑定
    ISI的晶圆级MRAM测试仪
    常见存储器分类
    存储器SDRAM简要历史
    当前MRAM市场以及专用MRAM设备测试的重要性
    IPUS SQPI PSRAM为STM32单片机提供RAM扩展方案
    单端口SRAM与双端口SRAM电路结构
  • 原文地址:https://www.cnblogs.com/java20130722/p/3207162.html
Copyright © 2011-2022 走看看