Internal ioctl communication between bus and port driver

Hi,

I'm practicing Internal IOCTL implementation for WDF.

I currently have a PCI hardware that uses a WDF bus driver and generates four ports. I want to call the bus driver's function on the port driver through internal ioctl.

At present, I know that two different drivers must communicate through the Interface, so I established an interface on the bus.

==============================================
typedef struct _PORT_DRIVER_INTERFACE {
INTERFACE InterfaceHeader;
EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL *EvtIoDeviceControl;
} PORT_DRIVER_INTERFACE, * PPORT_DRIVER_INTERFACE;

==============================================
RtlZeroMemory(&PortInterface, sizeof(PORT_DRIVER_INTERFACE));
PortInterface.InterfaceHeader.Size = sizeof(PORT_DRIVER_INTERFACE);
PortInterface.InterfaceHeader.Version = 1;
PortInterface.InterfaceHeader.Context = (PVOID)hChild;

PortInterface.InterfaceHeader.InterfaceReference =
    WdfDeviceInterfaceReferenceNoOp;
PortInterface.InterfaceHeader.InterfaceDereference =
    WdfDeviceInterfaceDereferenceNoOp;

PortInterface.EvtIoDeviceControl = Bus_EvtIoDeviceControl;

WDF_QUERY_INTERFACE_CONFIG_INIT(&qiConfig,
    (PINTERFACE)&PortInterface,
    &GUID_PORT_DRIVER_INTERFACE,
    WDF_NO_EVENT_CALLBACK);

status = WdfDeviceAddQueryInterface(hChild, &qiConfig);
if (!NT_SUCCESS(status)) {
    KdPrint(("WdfDeviceAddQueryInterface failed 0x%0x\n", status));
    goto Cleanup;
}

==============================================
On the port driver, I tried to use the following function to use the ioctl in EvtIoDeviceControl but could not correctly enter the IOCTL.
(This function will be used in the preparehardware phase to get some hwresource information from the bus driver)

As a result of the execution, the computer will get stuck in this function and cannot end it.

I have read some queryInterface articles, but because I haven’t seen the implementation related to internal ioctl, I am still a little confused. I would like to ask how I can modify it to achieve my goal.

Is the reason for the failure related to my misunderstanding of iotarget, or does the use of Interface need to be adjusted?

NTSTATUS GetBusPropertyValue(WDFDEVICE device, PDEVICE_SETTINGS *pSettings)
{
WDFIOTARGET ioTarget = NULL;
WDF_IO_TARGET_OPEN_PARAMS openParams;
NTSTATUS status = STATUS_SUCCESS;
PPORT_DRIVER_INTERFACE PortInterface;
// UNICODE_STRING deviceName;

// Generate IO target
status = WdfIoTargetCreate(device, WDF_NO_OBJECT_ATTRIBUTES, &ioTarget);
if (!NT_SUCCESS(status)) {
    KdPrint(("WdfIoTargetCreate failed 0x%x\n", status));
    return(status);
}

WDF_IO_TARGET_OPEN_PARAMS_INIT_EXISTING_DEVICE(&openParams, WdfDeviceWdmGetDeviceObject(device));
//ioTarget = WdfDeviceGetIoTarget(device);

status = WdfIoTargetOpen(ioTarget,
    &openParams);
if (!NT_SUCCESS(status)) {
    KdPrint(("WdfIoTargetOpen failed 0x%x\n", status));
    return(status);
}

status = WdfFdoQueryForInterface(device,
    &GUID_PORT_DRIVER_INTERFACE,
    (PINTERFACE)&PortInterface,
    sizeof(PORT_DRIVER_INTERFACE),
    1, // version
    NULL);

if (!NT_SUCCESS(status)) {
    KdPrint(("WdfFdoQueryForInterface failed 0x%0x\n", status));
    return(status);
}

DEVICE_SETTINGS* outputSettings = (DEVICE_SETTINGS*)ExAllocatePoolWithTag(NonPagedPool, sizeof(DEVICE_SETTINGS), 'Sett');
if (outputSettings == NULL) {
    return STATUS_INSUFFICIENT_RESOURCES;
}

WDF_MEMORY_DESCRIPTOR outputDescriptor;
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&outputDescriptor, outputSettings, sizeof(DEVICE_SETTINGS));

KdPrint(("WdfIoTargetSendInternalIoctlOthersSynchronously\n"));
status = WdfIoTargetSendInternalIoctlOthersSynchronously(
    ioTarget,
    NULL,
    IOCTL_TEST_INTERNAL_BASIC_SETTINGS,
    NULL,
    &outputDescriptor,
    NULL,
    NULL,
    NULL
);

if (NT_SUCCESS(status)) {
    *pSettings = outputSettings;

    KdPrint(("Controller: %x\n", (*pSettings)->Controller));
    KdPrint(("PortIndex: %x\n", (*pSettings)->PortIndex));
}
else {
    ExFreePoolWithTag(outputSettings, 'Sett');
}
WdfIoTargetClose(ioTarget);
WdfObjectDelete(ioTarget);


return status;

}

Many thanks.

Luke Chen

GetBusPropertyValue looks like it is opening a wdfiotarget against itself. That is overly complicated and not necessary.

Instead call WdfDeviceGetIoTarget to use the default wdfiotarget. You don't need to allocate a DEVICE_SETTINGS*, instead just use the provided pointer

WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&outputDescriptor, pSettings, sizeof(*pSettings));

PPORT_DRIVER_INTERFACE PortInterface is not correct. When you call query interface, the resulting interface will be copied into your buffer. You are not supplying the interface buffer, not the necessary storage. Change it to

PORT_DRIVER_INTERFACE PortInterface;

You are not using the returned filled inPortInterface though. All you are doing is sending an IOCTL down the stack that has nothing to do with PortInterface. To use PortInterface, you would call PortInterface.EvtDeviceIoControl.

This brings up the bigger problem that you don't expose WDF objects in the private interface, WDF handles are private to the driver that they are created for.

Hi Doron,

Many thanks for your fast reply.

====================================================
You are not using the returned filled inPortInterface though. All you are doing is sending an IOCTL down the stack that has nothing to do with PortInterface. To use PortInterface, you would call PortInterface.EvtDeviceIoControl .

====================================================
Is this means instead of using WdfIoTargetSendInternalIoctlOthersSynchronously, I should use PortInterface.EvtIoDeviceControl(NULL, NULL, 0, 0, IOCTL_INTERNAL_BASIC_SETTINGS);

The ioctl I used is from toaster bus and I create a new case.
Since my implementation is in preparehardware, I would like to ask if it is possible to just use default without creating queue and request in particular.

If the ioctl looks like this, how should I get my pSettings back?

============================
VOID
Bus_EvtIoDeviceControl(
IN WDFQUEUE Queue,
IN WDFREQUEST Request,
IN size_t OutputBufferLength,
IN size_t InputBufferLength,
IN ULONG IoControlCode
)

{
NTSTATUS status = STATUS_INVALID_PARAMETER;
WDFDEVICE hDevice;
size_t length = 0;
PFDO_DEVICE_DATA deviceData;
PDEVICE_SETTINGS pSettings;
PBUSENUM_PLUGIN_HARDWARE plugIn = NULL;
PBUSENUM_UNPLUG_HARDWARE unPlug = NULL;
PBUSENUM_EJECT_HARDWARE eject = NULL;
PVOID buffer;
PREQUEST_CONTEXT reqContext;

PAGED_CODE();

UNREFERENCED_PARAMETER(OutputBufferLength);

hDevice = WdfIoQueueGetDevice(Queue);

KdPrint(("Bus_EvtIoDeviceControl: 0x%p\n", hDevice));

switch (IoControlCode) {

case IOCTL_INTERNAL_BASIC_SETTINGS:
       
    status = WdfRequestRetrieveOutputBuffer(Request, sizeof(DEVICE_SETTINGS), &buffer, &length);
    if (!NT_SUCCESS(status)) {
        KdPrint(("Could not get request memory buffer %X\n", status));
        break;
    }
    pSettings = ExAllocatePoolWithTag(NonPagedPool, sizeof(DEVICE_SETTINGS), 'Sett');
    if (pSettings == NULL) {
        KdPrint(("Could not get request memory buffer2\n"));
        return;
    }
    RtlZeroMemory(pSettings, sizeof(DEVICE_SETTINGS));

    deviceData = FdoGetData(hDevice);
    pSettings->BoardIndex = deviceData->BoardIndex;
    pSettings->BoardType = deviceData->BoardType;
    pSettings->NumPorts = deviceData->NumPorts;
    pSettings->UsablePortMask = deviceData->UsablePortMask;
    pSettings->SpanOfController = 8;
    pSettings->InterruptStatus = deviceData->InterruptStatus;
    pSettings->SpanOfInterruptStatus = deviceData->SpanOfInterruptStatus;
    pSettings->Irql = deviceData->Irql;
    pSettings->IrqVector = deviceData->IrqVector;
    pSettings->IrqAffinity = deviceData->IrqAffinity;
    pSettings->MaxCanSetBaudRate = deviceData->MaxCanSetBaudRate;

    status = STATUS_SUCCESS;

    break;

case IOCTL_BUSENUM_PLUGIN_HARDWARE:
...

default:
    break; // default status is STATUS_INVALID_PARAMETER
}

WdfRequestCompleteWithInformation(Request, status, length);

}

In addition, for this paragraph
This brings up the bigger problem that you don't expose WDF objects in the private interface, WDF handles are private to the driver that they are created for.

I'm still a little confused, could you explain more?

Many thanks.

Best Regards,
Luke Chen