Chinaunix首页 | 论坛 | 博客
  • 博客访问: 363236
  • 博文数量: 112
  • 博客积分: 5245
  • 博客等级: 大校
  • 技术积分: 1120
  • 用 户 组: 普通用户
  • 注册时间: 2007-11-07 09:20
个人简介

静下来,定好方向,好好干。

文章分类
文章存档

2017年(1)

2012年(1)

2011年(5)

2010年(6)

2009年(16)

2008年(59)

2007年(24)

我的朋友

分类: WINDOWS

2008-05-12 18:00:08

Here is an explanation of buffers and DeviceIoControl.

First, here are the parameters,

BOOL DeviceIoControl(
    HANDLE hDevice,          // handle to device of interest
    DWORD dwIoControlCode,   // control code of operation 
                             //  to perform
    LPVOID lpInBuffer,       // pointer to buffer to supply 
                             //  input data
    DWORD nInBufferSize,     // size of input buffer
    LPVOID lpOutBuffer,      // pointer to buffer to receive
                             //  output data
    DWORD nOutBufferSize,    // size of output buffer
    LPDWORD lpBytesReturned, // pointer to variable to receive
                             //  output byte count
    LPOVERLAPPED lpOverlapped // pointer to overlapped structure
                             //  for asynchronous operation
   );

METHOD_BUFFERED

user-mode perspective

lpInBuffer - optional, contains data that is written to the driver
lpOutBuffer - optional, contains data that is read from the driver after the call has completed

lpInBuffer and lpOutBuffer can be two buffers or a single shared buffer. If a shared buffer, lpInBuffer is overwritten by lpOutBuffer.

I/O Manager perspective

examines nInBufferSize and nOutBufferSize. Allocates memory from non-paged pool and puts the address of this pool in Irp->AssociatedIrp.SystemBuffer. The size of this buffer is equal to the size of the larger of the two bufferes. This buffer is accessible at any IRQL.

copies nInBufferSize to irpSp->Parameters.DeviceIoControl.InputBufferLength
copies nOutBufferSize to
 irpSp->Parameters.DeviceIoControl.OutputBufferLength
copies contents of lpInBuffer to SystemBuffer allocated above
calls your driver

Device Driver perspective

you have one buffer, Irp->AssociatedIrp.SystemBuffer. You read input data from this buffer and you write output data to the same buffer, overwriting the input data.

Before calling IoCompleteRequest, you must
- set IoStatus.Status to an approriate NtStatus
- if IoStatus.Status == STATUS_SUCCESS
    set IoStatus.Information to the
    number of bytes you want copied
    from the SystemBuffer back into
    lpOutBuffer.

I/O Manager Completion Routine perspective

looks at IoStatus block, if IoStatus.Status = STATUS_SUCCESS, then
 copies the number of bytes specified by IoStatus.Information from
 Irp->AssociatedIrp.SystemBuffer into lpOutBuffer
completes the request

METHOD_IN_DIRECT

user-mode perspective

lpInBuffer - optional, contains data that is written to the driver. This buffer is used in the exact same fashion as METHOD_BUFFERED. To avoid confusion, mentally rename this buffer to lpControlBuffer. This is typically a small, optional buffer that might contain a control structure with useful information for the device driver. This buffer is small and is double buffered.

lpOutBuffer - NOT OPTIONAL, This LARGE buffer contains data that is read by the driver. To avoid confusion, mentally rename this buffer to lpDataTransferBuffer. This is physically the same buffer that the device driver will read from. There is no double buffering. Technically, this buffer is still optional, but since you are using this buffering method, what would be the point???

I/O Manager perspective

If lpInBuffer exists, allocates memory from non-paged pool and puts the address of this pool in Irp->AssociatedIrp.SystemBuffer. This buffer is accessible at any IRQL.

copies nInBufferSize to irpSp->Parameters.DeviceIoControl.InputBufferLength
copies nOutBufferSize to
 irpSp->Parameters.DeviceIoControl.OutputBufferLength
copies contents of lpInBuffer to SystemBuffer allocated above

So far this is completely identical to METHOD_BUFFERED. Most likely lpInBuffer (mentally renamed to lpControlBuffer) is very small in size.

For lpOutBuffer (mentally renamed to lpDataTransferBuffer), an MDL is allocated. lpOutBuffer is probed and locked into memory. Then, the user buffer virtual addresses are checked to be sure they are readable in the caller's access mode.

The MDL is address is stored in Irp->MdlAddress.
Your driver is called.

Device Driver perspective

The device driver can read the copy of lpOutBuffer [should be lpInBuffer--jeh] via Irp->AssociatedIrp.SystemBuffer. Anything written by the device driver to this buffer is lost. The I/O Manager does not copy any data back to the user-mode buffers as it did in the completion routine for METHOD_BUFFERED.

Art Baker's book is wrong in this respect (page 168, "data going from the driver back to the caller is passed through an intermediate system-space buffer" and page 177, "When the IOCTL IRP is completed, the contents of the system buffer will be copied back into the callers original output buffer".

The device driver accesses the Win32 buffer [lpOutBuffer -- jeh] directly via Irp->MdlAddress. The driver uses whatever Mdl API's to read the buffer. Usually, this buffer is to be written to some mass storage media or some similar operation. Since this is a large data transfer, assume a completion routine is required.

mark the Irp pending
queue it
return status pending

Device Driver Completion Routine perspective

standard completion routine operations
set IoStatus.Status to an approriate NtStatus
IoStatus.Information is not needed
complete the request

[I disagree with the "IoStatus.Information is not needed" comment. This longword is passed back to the caller as the returned lpBytesReturned value, and the application may want to see this. This represents the number of bytes of the lpOutBuffer actually written to the device. -- jeh]

I/O Manager Completion Routine perspective

standard I/O Manager completion routine operations
unmap the pages
deallocate the Mdl
complete the request

METHOD_OUT_DIRECT

user-mode perspective

lpInBuffer - optional, contains data that is written to the driver. This buffer is used in the exact same fashion as METHOD_BUFFERED. To avoid confusion, mentally rename this buffer to lpControlBuffer. This is typically a small, optional buffer that might contain a control structure with useful information for the device driver. This buffer is smal and is double buffered.

lpOutBuffer - NOT OPTIONAL, This LARGE buffer contains data that is written by the driver and read by the user-mode application when the request is completed. To avoid confusion, mentally rename this buffer to lpDataTransferBuffer. This is physically the same buffer that the device driver will write to. There is no double buffering. Technically, this buffer is still optional, but since you are using this buffering method, what would be the point???

I/O Manager perspective

If lpInBuffer exists, allocates memory from non-paged pool and puts the address of this pool in Irp->AssociatedIrp.SystemBuffer. This buffer is accessible at any IRQL.

copies nInBufferSize to irpSp->Parameters.DeviceIoControl.InputBufferLength
copies nOutBufferSize to irpSp->Parameters.DeviceIoControl.OutputBufferLength
copies contents of lpInBuffer to SystemBuffer allocated above

So far this is completely identical to METHOD_BUFFERED. Most likely lpInBuffer (mentally renamed to lpControlBuffer) is very small in size.

For lpOutBuffer (mentally renamed to lpDataTransferBuffer), an MDL is allocated. lpOutBuffer is probed and locked into memory. Then the user buffer's addresses are checked to make sure the caller could write to them in the caller's access mode.

The MDL is address is stored in Irp->MdlAddress.
Your driver is called.

Device Driver perspective

The device driver can read the copy of lpOutBuffer [should be lpInBuffer--jeh] via Irp->AssociatedIrp.SystemBuffer. Anything written by the device driver to this buffer is lost.

The device driver accesses the Win32 buffer [lpOutBuffer -- jeh] directly via Irp->MdlAddress. The driver uses whatever Mdl API's to write data to the buffer. Usually, this buffer is to be read from some mass storage media or some similar operation. Since this is a large data transfer, assume a completion routine is required.

mark the Irp pending
queue it
return status pending

Device Driver Completion Routine perspective

standard completion routine operations
set IoStatus.Status to an approriate NtStatus
IoStatus.Information is not needed
complete the request

[I disagree with the "IoStatus.Information is not needed" comment. This longword is passed back to the caller as the returned lpBytesReturned value, and the application may want to see this. This represents the number of bytes of the lpOutBuffer actually read from the device. -- jeh]

I/O Manager Completion Routine perspective

standard I/O Manager completion routine operations
unmap the pages
deallocate the Mdl
complete the request

METHOD_NEITHER

I/O Manager perspective

Irp->UserBuffer = lpOutputBuffer;
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer = lpInputBuffer;

No comments here. Don't use METHOD_NEITHER unless you know what you are doing. Simple rule.

If your IOCtl involves no data transfer buffers, then METHOD_NEITHER is the fastest path through the I/O Manager that involves an Irp.

Final Comment

Don't touch Irp->UserBuffer. This is a bookmark for the I/O Manager. Two major problems can occur. 1 - page fault at high IRQL, or 2 - you write something to Irp->UserBuffer and the I/O Manager overwrites you in its completion routine. File systems access Irp->UserBuffer, but FSD writers know all of the above and know when it is safe to touch Irp->UserBuffer.

阅读(828) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~