DeviceIOcontrol

设备驱动程序可以被当作内核模式函数包来看待,

一 说明

功能:就是用来指定访问其中的哪个函数的。

DeviceIoControl函数的dwIoControlCode参数就是这个代码,它指出了我们需要进行的操作,以及如何进行操作。 
控制代码是32位数字型常量,可以CTL_CODE宏来定义,它们定义在winioctl.inc和ntddk.inc文件中。

控制代码中各数据位字段的含义如下: 
◎ DeviceType--设备类型(16bit)指出了设备的类型,微软保留了0-7FFFh的取值,剩下的8000h-0FFFFh供开发商定义新的内 核模式驱动程序。我们可以在/include/w2k/ntddk.inc文件中找到一组FILE_DEVICE_XXX符号常量,这些值都是微软保留的 值,我们可以使用其中的FILE_DEVICE_UNKNOWN。当然你也可以定义另外一个FILE_DEVICE_XXX值 
◎ Access--存取代码(2bit)指明应用程序存取设备的方式,由于这个字段只有2位,所以只有4种可能性: 
· FILE_ANY_ACCESS (0)--最大的存取权限,就是什么操作都可以 
· FILE_READ_ACCESS (1)--读权限,设备将数据传递到指定的缓冲区 
· FILE_WRITE_ACCESS (2)--写权限,可以从内存中向设备传递数据 
· FILE_READ_ACCESS or FILE_WRITE_ACCESS (3)--读写权限,设备和内存缓冲区之间可以互相传递数据 
◎ Function--功能代码(12bit)用来描述要进行的操作,我们可以用800h-0FFFh来定义自己的I/O控制代码,0-7FFh之间的值是被微软保留的,用来定义公用的I/O控制代码 
◎ Method--缓冲模式(2bit)表示I/O管理器如何对输入和输出的数据进行缓冲,这个字段的长度是2位,所以有4种可能性: 
· METHOD_BUFFERED (0)--对I/O进行缓冲 
· METHOD_IN_DIRECT (1)--对输入不进行缓冲 
· METHOD_OUT_DIRECT (2)--对输出不进行缓冲 
· METHOD_NEITHER (3)--都不缓冲

特点:缓冲区模式虽然会消耗一定的性能,但是其是安全的。

使用条件:在传输的数据小于一页(4Kb)的时候,驱动程序通常使用缓冲方式的I/O,因为对大量小块内存进行内存锁定带来的开销也是很大的。在 VirtToPhys驱动程序中,我们使用带缓冲的方式。


定义I/O控制代码,

(1)手工

(2)使用CTL_CODE宏

CTL_CODE   MACRO DeviceType:= <0> , Function:= <0> , Method:= <0> , Access:= <0> 
EXITM %(((DeviceType) SHL 16) OR ((Access) SHL 14) OR ((Function) SHL 2) OR (Method)) 
ENDM

CTL_CODE宏 定义    : 在winioctl.inc文件和ntddk.inc文件中都有。

在应用程序里定义的CTL_CODE和驱动中IRP_MJ_DEVICE_CONTROL的函数里控制码对应即可。在驱动程序里取出码值进行对比操作,如:
   dwIoControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode;
    switch (dwIoControlCode)
    {
     // 我们约定,缓冲区共两个DWORD,第一个DWORD为端口,第二个DWORD为数据
     // 一般做法是专门定义一个结构,此处简单化处理了
   

case IOCTL_MYPORT_READ_BYTE:   // 从端口读字节
      pvIOBuffer[1] = _inp(pvIOBuffer[0]);
      Irp->IoStatus.Information = 8;   // 输出长度为8
      break;
    

case IOCTL_MYPORT_WRITE_BYTE:   // 写字节到端口
      _outp(pvIOBuffer[0], pvIOBuffer[1]);
      break;
   

default:   // 不支持的IOCTL


Q 在NT/2000/XP中,我想用VC编写应用程序访问硬件设备,如获取磁盘参数、读写绝对扇区数据、测试光驱实际速度等,该从哪里入手呢?

A 在NT/2000/XP中,应用程序可以通过API函数DeviceIoControl来实现对设备的访问—获取信息,发送命令,交换数据等。利用该接口函数向指定的设备驱动发送正确的控制码及数据,然后分析它的响应,就可以达到我们的目的。

DeviceIoControl的函数原型为

BOOL DeviceIoControl(
    HANDLE hDevice,              // 设备句柄
    DWORD dwIoControlCode,       // 控制码
    LPVOID lpInBuffer,           // 输入数据缓冲区指针
    DWORD nInBufferSize,         // 输入数据缓冲区长度
    LPVOID lpOutBuffer,          // 输出数据缓冲区指针
    DWORD nOutBufferSize,        // 输出数据缓冲区长度
    LPDWORD lpBytesReturned,     // 输出数据实际长度单元长度
    LPOVERLAPPED lpOverlapped    // 重叠操作结构指针
);
设备句柄用来标识你所访问的设备。

发送不同的控制码,可以调用设备驱动程序的不同类型的功能。在头文件winioctl.h中,预定义的标准设备控制码,都以IOCTL或FSCTL开头。例如,IOCTL_DISK_GET_DRIVE_GEOMETRY是对物理驱动器取结构参数(介质类型、柱面数、每柱面磁道数、每磁道扇区数等)的控制码,FSCTL_LOCK_VOLUME是对逻辑驱动器的卷加锁的控制码。

Q 设备句柄是从哪里获得的?

A 设备句柄可以用API函数CreateFile获得。它的原型为

HANDLE CreateFile(
    LPCTSTR lpFileName,                         // 文件名/设备路径 设备的名称
    DWORD dwDesiredAccess,                      // 访问方式
    DWORD dwShareMode,                          // 共享方式
    LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 安全描述符指针
    DWORD dwCreationDisposition,                // 创建方式
    DWORD dwFlagsAndAttributes,                 // 文件属性及标志
    HANDLE hTemplateFile                        // 模板文件的句柄
);
打开:createFile

关闭:closehandle

与普通文件名有所不同,设备驱动的“文件名”(常称为“设备路径”)形式固定为“//./DeviceName”(注意在C程序中该字符串写法为“////.//DeviceName”),DeviceName必须与设备驱动程序内定义的设备名称一致。

一般地,调用CreateFile获得设备句柄时,访问方式参数设置为0或GENERIC_READ|GENERIC_WRITE,共享方式参数设置为FILE_SHARE_READ|FILE_SHARE_WRITE,创建方式参数设置为OPEN_EXISTING,其它参数设置为0或NULL。

应用程序------deviceiocontrol-----驱动

应用程序------readfile-----驱动

应用程序------writefile-----驱动

deviceiocontrol

{

句柄:createfile 创建的句柄

应用程序传递给驱动程序的 缓冲区地址

应用程序传递给驱动程序的 缓冲区大小

驱动程序传递给应用程序的 缓冲区地址

驱动程序传递给应用程序的 缓冲区大小

实际 驱动程序传递给应用程序的 缓冲区大小

重叠操作结构

}

DeviceIoControl函数向指定的设备驱动发送一个控制码,驱动程序通过这个控制码来完成特定的工作。该函数原型如下:

BOOL DeviceIoControl(
    HANDLE hDevice,
    DWORD dwIoControlCode,
    LPVOID lpInBuffer,
    DWORD nInBufferSize,
    LPVOID lpOutBuffer,
    DWORD nOutBufferSize,
    LPDWORD lpBytesReturned,
    LPOVERLAPPED lpOverlapped
);

参数说明

hDevice

要进行操作的设备句柄。

dwIoControlCode

要进行操作的控制码。驱动程序可以通过CTL_CODE宏来组合定义一个控制码,并在IRP_MJ_DEVICE_CONTROL的实现中进行控制码的操作。在驱动层,irpStack->Parameters.DeviceIoControl.IoControlCode表示了这个控制码。

lpInBuffer

由用户层发送的缓冲区数据。在驱动层,依传输类型的不同,输入缓冲区的位置亦不同,见下表。

传输类型 位置 
METHOD_IN_DIRECT irp->AssociatedIrp.SystemBuffer 
METHOD_OUT_DIRECT irp->AssociatedIrp.SystemBuffer 
METHOD_BUFFERED irp->AssociatedIrp.SystemBuffer 
METHOD_NEITHER irpStack->Parameters.DeviceIoControl.Type3InputBuffer

nInBufferSize

由用户层发送的缓冲区大小。在驱动层,这个值是irpStack->Parameters.DeviceIoControl.InputBufferLength。

lpOutBuffer

由用户层指定,用于接收驱动层返回数据的缓冲区。在驱动层,依传输类型的不同,输出缓冲区的位置亦不同,见下表。

传输类型 位置 
METHOD_IN_DIRECT irp->MdlAddress 
METHOD_OUT_DIRECT irp->MdlAddress 
METHOD_BUFFERED irp->AssociatedIrp.SystemBuffer 
METHOD_NEITHER irp->UserBuffer

nOutBufferSize

由用户层指定,用于接收驱动层返回数据的缓冲区大小。在驱动层,这个值是irpStack->Parameters.DeviceIoControl.OutputBufferLength。

lpBytesReturned

由用户层指定,用于接收驱动层实际返回数据大小。在驱动层,这个值是irp->IoStatus->Information。

lpOverlapped

用于异步操作。

----------传说中的分隔线----------

irp为PIRP类型,即PDRIVER_DISPATCH的入口参数。 
irpStack为PIO_STACK_LOCATION类型,即IoGetCurrentIrpStackLocation的返回值。

你可能感兴趣的:(device)