Search code examples
windowsdriverkmdfwdf

DeviceIoControl error 1 incorrect function


I have created a device in kernel space and the access it in user space using CreateFile I am able to send ioctl to the driver and they are executed properly. The don't know how to trace what happens after WdfRequestComplete and upon return I end with error 1 (invalid function). Before this is flagged as dup please note there is a difference with this in that I write my driver ioctl and in that I am using synch io not asynch.

In user space:

fd = CreateFile(dev_path, 
    (FILE_GENERIC_READ | FILE_GENERIC_WRITE),
    (FILE_SHARE_READ | FILE_SHARE_WRITE),
    NULL, OPEN_EXISTING, 0, NULL);
// ... error checking code here
DeviceIoControl(fd, // device handler
    VIRTQC_CMD_MMAP, // command to send
    &inputBuffer,
    inputBufferLength,
    &outputBuffer,
    outputBufferLength,
    &returnLength,
    (LPOVERLAPPED)NULL); // overlapped structure not needed using sync io

and in Kernel space

status = WdfRequestRetrieveInputBuffer(Request, InputBufferLength, &inputBuffer, NULL);
if (!NT_SUCCESS(status)) 
{
    WdfRequestComplete(Request, STATUS_INVALID_PARAMETER);
    return;
}
inputVirtArg = (VirtioQCArg*)inputBuffer;

status = WdfRequestRetrieveOutputBuffer(Request, OutputBufferLength, &outputBuffer, NULL);
if (!NT_SUCCESS(status))
{
    WdfRequestComplete(Request, STATUS_INVALID_PARAMETER);
    return;
}
outputVirtArg = (VirtioQCArg*)outputBuffer;

switch (IoControlCode)
{
case VIRTQC_CMD_MMAP:
    if (PsGetCurrentThread() == irp->Tail.Overlay.Thread)
    {
        status = CreateAndMapMemory(device, &(inputVirtArg), &(outputVirtArg));
        outputVirtArg->flag = (!NT_SUCCESS(status)) ? 0 : 1;
    } 
    else 
        status = STATUS_UNSUCCESSFUL;
    break;
default:
    status = STATUS_INVALID_DEVICE_REQUEST;
    break;
}
WdfRequestComplete(Request, status);

Update 1:

I have tried WdfRequestCompleteWithInformation(Request, status, OutputBufferLength); but same result.

Also, I notice that the address of inputBuffer and outputBuffer are the same.

Update 2: I tried doing

temp = ExAllocatePoolWithTag(
    NonPagedPool,
    PAGE_SIZE,
    MEMORY_TAG
    );

// other code to
RtlCopyMemory((VirtioQCArg*)outputBuffer, temp, OutputBufferLength);

still get error 1


Solution

  • I had defined my ioctl cmds as enums in my linux driver (which works fine) and when implementing the driver in windows I used the same enum definition.

    enum
    {
        // Module & Execution control (driver API)
        VIRTIQC_SET = 200,
        VIRTIQC_UNSET,
        // more cmds.....
    }
    

    In windows defining control codes take a bit more. As explained here the CTL_CODE macro should be used to define new IOCTL control codes.

    #define IOCTL_Device_Function CTL_CODE(DeviceType, Function, Method, Access)

    In my case I ended up with defining this:

    #define VIRTQC_MAP CTL_CODE(FILE_DEVICE_NETWORK, 0xC8, METHOD_IN_DIRECT, FILE_READ_DATA | FILE_WRITE_DATA)
    #define VIRTQC_UNMAP CTL_CODE(FILE_DEVICE_NETWORK, 0xC9, METHOD_OUT_DIRECT, FILE_READ_DATA)
    

    I know the function code less than 0x800 are reserved for MS but the device on my host requires this codes so I'm just providing what is being asked.