This is more confusing than it is complicated. And this is yet ANOTHER reason why you should be writing your drivers using WDF, which abstracts all this stupidity.
It takes 30 minutes to explain this in driver class. And lots of people remain confused.
It might help to understand that the original intent of the IN buffer was for passing parameters and small data structures into the driver. The original intent of the OUT buffer was to be the PAYLOAD buffer, for large data transfers, IN EITHER DIRECTION.
Now… read this: *IGNORING METHOD_NEITHER in everything below*
First: IN and OUT for IOCTLs… refer to the direction FROM THE DRIVER’S PERSPECTIVE. A OUT buffer is one to which the driver writes (conventionally, a user read operation). An IN buffer is one from which the driver reads (conventionally, a user’s write operation).
Second: Not all IOCTLs need to use both buffers, or either buffer.
Third: If there is an IN buffer specified, and its length is non-zero, it uses Buffered I/O. Which means the data is automagically copied by the I/O Manager between the user’s buffer within his/her process address space and a system buffer in non-paged pool. This system buffer is automatically allocated by the I/O Manager when the I/O Request is issued, and freed by the I/O Manager when the I/O Request is completed.
The pointer (kernel virtual address, in the high half of KV address space) to this system buffer is passed to your driver in your IRP at
Irp->AssociatedIrp.SystemBuffer, and the length of the buffer (the length the user specified on his/her DeviceIoControl call) is at Parameters.DeviceIoControl.InputBufferLength.
Fourth: If you specify METHOD_BUFFERED for your IOCTL, and if the call to DeviceIoControl specifies an OUT buffer and its length is non-zero, the out data is automagically copied by the I/O Manager between the user’s buffer within his/her process address space and a system buffer in non-paged pool. This system buffer is automatically allocated by the I/O Manager when the I/O Request is issued, and freed by the I/O Manager when the I/O Request is completed. The length of this buffer (the length the user specified on his/her DeviceIoControl call) is passed to your driver at Parameters.DeviceIoControl.OutputBufferLength. The pointer to the buffer (again, KVA in high half of the address space) is passed to your driver at Irp->AssociatedIrp.SystemBuffer.
Continuing for METHOD_BUFFERED… If the user who calls DeviceIoControl specifies BOTH an IN buffer and an OUT buffer (of non-zero length) the I/O Manager allocates a buffer that is the size of the larger of the two (in other words max(InBufferLength, OutBufferLength). This buffer is used for BOTH to pass data to your driver from the user’s IN buffer, and for your driver to return data to the user’s OUT buffer (this was considered clever and memory efficient in 1989 when this was designed). Thus, your driver must consume any data from the system buffer that was passed to your from the IN buffer, before you start writing data to (that exact same) system buffer that you want returned in the OUT buffer.
Ugly, confusing, silly by 2017 standards… but “it is what it is”… celebrate the fact that we routinely have systems with more than 16MB of memory (which was the minimum officially supported when NT was released).
Fifth: METHOD_xxxx_DIRECT only has to deal with the OUT buffer, and when selected, causes the IN buffer to be described using an MDL. The address of this MDL is at Irp->MdlAddress. You can use it to call MmGetSystemAddressForMdlSafe, to map the buffer described by the MDL into (the high half of) Kernel Virtual Address space. The length of the buffer (the length the user specified on his/her DeviceIoControl call) will be at Parameters.DeviceIoControl.OutputBufferLength.
Now… here’s a cute thing: When you specify METHOD_OUT_DIRECT the get WRITE access to the OUT buffer, and you can (in fact) use the OUT buffer in either (or both) directions (read and/or write). When you specify METHOD_IN_DIRECT you get READ access to the OUT buffer… this treating the IN buffer as read-only.
This 15 year old article might help: http://www.osronline.com/article.cfm?id=92
Peter
OSR
@OSRDrivers