Problem with PCI-Card reading

Hello,

I currently have a problem with reading from a PCI-Card memory. The driver uses a parallel queue and buffered IO. The mapping worked fine, when I print the requested memory part via kdprint I always get the correct result (for example 0xa55aa55a) but the buffer content returned by DeviceIoControl varies between 0xa55a0000, 0xa55aa500 and 0xa55aa55a.

The return value of the call to DeviceIoControl returns “TRUE” always…

I thought without using an overlapped event the call would block? Do I miss something? Do I have to use overlapped events in case of a parallel queue to be sure to get everything in question?

I would be very glad to get some directions here…

Greetings,
Torben

IOCTL:
CTL_CODE(FILE_DEVICE_NETWORK, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)

Queue creation:
ntStatus = WdfIoQueueCreate(
wdDevice,
&IoQConfig,
WDF_NO_OBJECT_ATTRIBUTES,
&(pDevCtx->IoDefaultQueue));

Opening of the device:
hDrv = CreateFile(
pInterfDevDetail->DevicePath,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);

Call:
bRetVal = DeviceIoControl(
hDrv,
IOCTL_READ_MEM_BUFFERED,
pbSendCommBuffer,
dwSendCommBufferSize,
pbRecvCommBuffer,
dwRecvCommBufferSize,
&ulReturnedLength,
NULL);

You are going to need to show us the code that fills the buffer and
completes the call in the driver. Also, what is the returned length
from the DeviceIoControl call?

Don Burn
Windows Filesystem and Driver Consulting
Website: http://www.windrvr.com
Blog: http://msmvps.com/blogs/WinDrvr

xxxxx@conplement.de” wrote in
message news:xxxxx@ntdev:

> Hello,
>
> I currently have a problem with reading from a PCI-Card memory. The driver uses a parallel queue and buffered IO. The mapping worked fine, when I print the requested memory part via kdprint I always get the correct result (for example 0xa55aa55a) but the buffer content returned by DeviceIoControl varies between 0xa55a0000, 0xa55aa500 and 0xa55aa55a.
>
> The return value of the call to DeviceIoControl returns “TRUE” always…
>
> I thought without using an overlapped event the call would block? Do I miss something? Do I have to use overlapped events in case of a parallel queue to be sure to get everything in question?
>
> I would be very glad to get some directions here…
>
> Greetings,
> Torben
>
> IOCTL:
> CTL_CODE(FILE_DEVICE_NETWORK, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)
>
> Queue creation:
> ntStatus = WdfIoQueueCreate(
> wdDevice,
> &IoQConfig,
> WDF_NO_OBJECT_ATTRIBUTES,
> &(pDevCtx->IoDefaultQueue));
>
> Opening of the device:
> hDrv = CreateFile(
> pInterfDevDetail->DevicePath,
> GENERIC_READ | GENERIC_WRITE,
> FILE_SHARE_READ | FILE_SHARE_WRITE,
> NULL,
> OPEN_EXISTING,
> FILE_ATTRIBUTE_NORMAL,
> NULL);
>
> Call:
> bRetVal = DeviceIoControl(
> hDrv,
> IOCTL_READ_MEM_BUFFERED,
> pbSendCommBuffer,
> dwSendCommBufferSize,
> pbRecvCommBuffer,
> dwRecvCommBufferSize,
> &ulReturnedLength,
> NULL);

Hello,

Okay, this is a little bit comlicated because I have several requests and suchs, but I shortened the code to the code handling this special case. The data needed in the communication contains a header, which is 12 bytes long. It contains a field how many elements are requested and what the size of these elements is. In the test case I request a single DWORD. Therefor the number of elements is “1”, the size of the element sizeof(DWORD) (== 4).
For control purposes the driver not only returns the requested data but also returnes the received header currently.

The KdPrint command returnes a returned length of “16”. This is also returned DeviceIoControl and therefor correct.

The returned header is correct.

Code:

VOID EvtIoDeviceControl(
WDFQUEUE Queue,
WDFREQUEST Request,
size_t OutBufLen,
size_t InBufLen,
ULONG ulIoControlCode)
{
[…]
size_t FilledBuffer;
WDFMEMORY InMemory;
WDFMEMORY OutMemory;

switch(ulIoControlCode)
{
[…]
case IOCTL_WRITE_MEM_BUFFERED:
nStatus = WdfRequestRetrieveInputMemory(Request, &InMemory);
nStatus = WdfRequestRetrieveOutputMemory(Request, &OutMemory);

nStatus = HandleRequest(
ulIoControlCode,
&InMemory,
InBufLen,
&OutMemory,
OutBufLen,
pMappedResources); /* Contains the address and the size of the mapped memory*/

FilledBuffer = OutBufLen;

KdPrint((“Returned length: %d\n”, FilledBuffer));
WdfRequestSetInformation(Request, FilledBuffer);
nsStatus = STATUS_SUCCESS;
break;
[…]
}

NTSTATUS SDNHHandleRequest(
ULONG ulIoControlCode,
WDFMEMORY *pInMemory,
size_t InMemLen,
WDFMEMORY *pOutMemory,
size_t OutMemLen,
PADMINISTRATION pMappedResources)
{
NTSTATUS nRetVal = STATUS_SUCCESS;

MESSAGE_HEADER InMessageHeader;

nRetVal = WdfMemoryCopyToBuffer(*pInMemory, 0, &InMessageHeader, sizeof(MESSAGE_HEADER)) /* Size is “12”*/;

[…]
nRetVal = HandleRead(
&InMessageHeader,
pInMemory,
InMemLen,
pOutMemory,
OutMemLen,
pMappedResources);
[…]
return nRetVal;
}

NTSTATUS HandleRead(
PMESSAGE_HEADER pInMessageHeader,
WDFMEMORY *pInMemory,
size_t InMemLen,
WDFMEMORY *pOutMemory,
size_t OutMemLen,
PADMINISTRATION pMappedResources)
{
NTSTATUS nRetVal = STATUS_SUCCESS;

size_t OutMessageBufferSize = OutMemLen - sizeof(MESSAGE_HEADER);
ULONG ulOffsetInBytes = pInMessageHeader->ulElementOffset * pInMessageHeader->ulElementSize;
ULONG ulMemoryChunkSizeInBytes = pIntMessageHeader->ulElementNum * pInMessageHeader->ulElementSize;
BYTE *pbMappedMem = pMappedResources->pbMappedMemory; /* Abbreviation*/
BYTE *pbChunkAddress = NULL;

[…]
pbChunkAddress = &(pbMappedMem[ulOffsetInBytes]);
KdPrint((“Address: 0x%08X\n”, pbChunkAddress));
KdPrint((“Content: 0x%x\n”, *((DWORD *)pbChunkAddress)));
nRetVal = WdfMemoryCopyFromBuffer(
*pOutMemory,
sizeof(MESSAGE_HEADER),
pbChunkAddress,
(size_t)ulMemoryChunkSizeInBytes);

nsRetVal = WdfMemoryCopyFromBuffer(*pOutMemory, 0, pInMessageHeader, sizeof(MESSAGE_HEADER));
[…]
return nRetVal;
}

Hmmmm…

I don’t see where you’re calling WdfRequestComplete anywhere?

Not saying this is necessarily the problem, but does “pbMappedMem” point to memory on your DEVICE? If so, you’re supposed to be reading it with READ_REGISTER_xxxx not directly de-referencing it.

What are you getting in your application for “ulReturnedLength”??

Peter
OSR

Sorry, I shortened the code to much accendentially… WdfRequestComplete ist the last line in EvtIoDeviceControl:

WdfRequestComplete(Request, nStatus);

pbMappedMem points to the mapped memory, this means to some kernel memory where the PCI-card memory is mapped to:

pAdministration->pbMappedMemory = MmMapIoSpace(
pCurrentPartialResDescriptor->u.Memory.Start,
pCurrentPartialResDescriptor->u.Memory.Length,
MmNonCached);

ulReturnedLength contains “16” which is correct (sorry, maybe I was a little bit unclear about this in the posting above).

OK, to start, as I said, you technically can’t access device memory the way you’re doing it. You’re *required* to use the READ_REGISTER_xxxx and WRITE_REGISTER_xxxx calls to do your hardware access. So that’s a problem in your driver. I’m not saying it’s THE problem, but it is an issue.

So, to make sure I understand, the summary of your problem is: You can see that you’re reading correct data from your device (your output from your “Address” and “Contents” KdPrints show the data is correct), but that data does not seem to be returned in your application’s buffer. However, the IOCTL is completing with success, and the “ulReturnedLength” value is also correct.

Is that a good summary of the problem?

This code seems to be overwriting the outbuffer with the second copy:

Peter
OSR

Ah, I take it back… I didn’t read carefully enough. I see you’re doing the header SECOND, and copying it to offset zero of the buffer.

Hmmmm… I don’t see any problem other than that.

Could the contents of the shared memory block change between the KdPrint and you’re copying it?

Peter
OSR

Thanks for the quick response. The summary is nearly correct. Only that the returned buffer seems to be filled just to a certain level as always zero to four of the LAST BYTES are missing.

For me it looks like the call returnes without the IO manager completing the copy…

Thanks for the access hint. I will correct this part and see what happens.

Hmmm… OK, the I/O Manager will always complete the copy. But keep in mind that for buffered I/O the I/O Manager (which is what you’re using) the I/O Manager completely relies on the length value you return in the Information field. So, whatever value you specify when you call WdfRequestSetInformation or WdfRequestCompleteWithInforation, that’s the number of bytes the I/O Manger will copy. Always. Always.

Also, in case this matters in your specific instance (it’s not clear to me from the code) and you don’t already know this: When you use METHOD_BUFFERED, the OutBuffer and the InBuffer use THE SAME BUFFER in non-paged pool. Thus, your driver *must* consume the contents of the InBuffer before it starts writing to the OutBuffer.

Hope that helps,

Peter
OSR

xxxxx@conplement.de wrote:

[…]
case IOCTL_WRITE_MEM_BUFFERED:
nStatus = WdfRequestRetrieveInputMemory(Request, &InMemory);
nStatus = WdfRequestRetrieveOutputMemory(Request, &OutMemory);

nStatus = HandleRequest(
ulIoControlCode,
&InMemory,
InBufLen,
&OutMemory,
OutBufLen,
pMappedResources); /* Contains the address and the size of the mapped memory*/

It should never be necessary to pass WDF handles by reference like
that. These are handles – the size of an int. Because C has a
tendency to believe all pointers are the same, it is very easy to cause
trouble by passing a ** where a * is expected, or vice versa.

You have double-checked that the compiler believes
sizeof(MESSAGE_HEADER) is always 12, with no padding, even in 64-bit
builds? Can you show us the “struct” without editing?


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

I noticed the same thing. I find the way the OP did this (passing a pointer to a handle, instead of passing the handle itself by value) to be confusing… and at first I thought it was a bug, but instead it’s just a code smell. MUCH too easy to use the wrong thing, and no advantage at all.

Peter
OSR

Sorry for the code smell, never realised that this are handles (never thought about it actually).

Still changing the driver according to the tips I got here and a few other problems I run into.

Concerning the header size I’m not sure if this is the case under any circumstances. It was just a snapshot on the current situation on the current system. I didn’t write “12 bytes” because I calculated this value myself but used printf’s in the application and KdPrint’s in the driver to get the actual sizes.
Also when printing the buffer everything accept the missing bytes are in place without any stuffed bytes between.

Anyhow, this is the header definition:
typedef struct _MESSAGE_HEADER
{
ULONG ulElementOffset;
ULONG ulElementNum;
ULONG ulElementSize;
} MESSAGE_HEADER, *PMESSAGE_HEADER;

Hello,

using READ_REGISTER_xxx does the trick. Thanks very much.

And thanks for the other tips. I removed the pointer to handle stuff and are currently checking my code for other unnecessarities.

YAY! I’m glad we could help fix that one for you.

Peter
OSR