Question

I'm having a lot of trouble getting MSI-X interrupts implemented in a Windows bus driver we're writing in WDF / KMDF. I've read the MSDN documentation, and there's not really a lot of helpful info there. My understanding is that it should really just "work".

I've changed our driver's INF file to add the appropriate registry keys and confirmed that they are being set working properly on install. I'm properly querying the PCI config space and determining whether MSI-X interrupts are supported.

The problem is that once I have this information, I don't know how to change my code to specifically set up the interrupt to be MSI-X. I do the standard calls to configure a WDF_INTERRUPT_CONFIG_INIT stuct and call WdfInterruptCreate, but the interrupt that is created is not message-signaled, and I don't know what needs to be done to actually make this happen.

Is there a WDF version of the steps here, or should I just be doing the standard WDFINTERRUPT creation steps here?

Does anyone have any experience doing this? Can anyone provide a source example?

Was it helpful?

Solution

I have been struggling with something similar in the last week. My application is slightly different because I am trying to get plain MSI interrupts rather than MSI-X interrupts to work. But I am using the WDF framework in pretty much the same way.

I realize you posted your original question around April of 2013, but there are no updates. Have you solved your original problem? If so, I would like to see your own solution.

In my WDF driver, I am mapping an incoming hardware resource list and parsing it using the WdfCmResourceListGetDescriptor() function to examine each PCM_PARTIAL_RESOURCE_DESCRIPTOR item in the WDFCMRESLIST list. I was able to obtain a single CmResourceTypeInterrupt descriptor, but it was always for a legacy interrupt instead of MSI interrupt. My device does support MSI. I did not understand why the MSI descriptor was not in the resource list, even after setting up the extra registry entries in my .inf file as you described.

It turns out I had a typo in my .inf file. I had forgotten to append the ".HW" suffix in my device install section. Adding this suffix allows the Bus manager to modify the pcie config addresses in the device and create the appropriate MSI interrupt resource descriptor, which must be hooked by the driver.

[Standard.NT$ARCH$]
%tdvr.DeviceDesc%=tdvr_Device, PCI\VEN_104C&DEV_B800 

[tdvr_Device.NT]
CopyFiles=Drivers_Dir

[tdvr_Device.NT.HW]
AddReg=MSI_Interrupts

[Drivers_Dir]
tdvr.sys

;http://msdn.microsoft.com/en-us/library/windows/hardware/ff544246(v=vs.85).aspx
;To receive message-signaled interrupts (MSIs), a driver's INF file must enable MSIs in the 
;registry during installation. Use the Interrupt Management\MessageSignaledInterruptProperties 
;subkey of the device's hardware key to enable MSI support.

[MSI_Interrupts]
HKR,Interrupt Management,,0x00000010
HKR,Interrupt Management\MessageSignaledInterruptProperties,,0x00000010
HKR,Interrupt Management\MessageSignaledInterruptProperties,MSISupported,0x00010001,1

When the MSI interrupt resource descriptor is found in the resource list (for example in a evtMapHWResources callback), the descriptor is used as an input to the WdfInterruptCreate() function. The "tdvr" prefixes here are for my own driver naming convention.

NTSTATUS
tdvrConfigureInterrupts(
    _Inout_ PDEVICE_CONTEXT deviceContext,
    _In_ PCM_PARTIAL_RESOURCE_DESCRIPTOR interruptDescRaw,
    _In_ PCM_PARTIAL_RESOURCE_DESCRIPTOR interruptDescTranslated
    )
{
    NTSTATUS status;
    PAGED_CODE();
    FuncEntry();

    // What kind of interrupt has been provided?
    if (CM_RESOURCE_INTERRUPT_MESSAGE & interruptDescTranslated->Flags)
    {
        TraceInterrupt(TRACE_LEVEL_INFORMATION, 
            "Message Interrupt level 0x%0x, Vector 0x%0x, MessageCount %u\n"
            ,interruptDescTranslated->u.MessageInterrupt.Translated.Level
            ,interruptDescTranslated->u.MessageInterrupt.Translated.Vector
            ,interruptDescTranslated->u.MessageInterrupt.Raw.MessageCount
            );
    }
    else
    {
        TraceInterrupt(TRACE_LEVEL_INFORMATION,
            "Legacy Interrupt level: 0x%0x, Vector: 0x%0x\n"
            ,interruptDescTranslated->u.Interrupt.Level
            ,interruptDescTranslated->u.Interrupt.Vector
            );
    }

    //
    // Create WDFINTERRUPT object.
    //
    WDF_INTERRUPT_CONFIG interruptConfig;
    WDF_INTERRUPT_CONFIG_INIT(
        &interruptConfig,
        tdvrEvtInterruptIsr,
        tdvrEvtInterruptDpc
        );

    // Each interrupt has some context data
    WDF_OBJECT_ATTRIBUTES interruptAttributes;
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(
        &interruptAttributes,
        INTERRUPT_CONTEXT
        );

    //
    // These first two callbacks will be called at DIRQL.  Their job is to
    // enable and disable interrupts.
    //
    interruptConfig.EvtInterruptEnable  = tdvrEvtInterruptEnable;
    interruptConfig.EvtInterruptDisable = tdvrEvtInterruptDisable;

    // If the driver calls WdfInterruptCreate from EvtDriverDeviceAdd, the InterruptRaw and
    // InterruptTranslated members of the WDF_INTERRUPT_CONFIG structure must be NULL. 
    // the driver calls WdfInterruptCreate from EvtDevicePrepareHardware, these members must both be valid.
    interruptConfig.InterruptTranslated = interruptDescTranslated;
    interruptConfig.InterruptRaw = interruptDescRaw;

    // Our driver must call WdfInterruptCreate once for each interrupt vector that its device requires. 
    // If the device supports message-signaled interrupts (MSI), the driver must create an interrupt object 
    // for each message that the device can support.
    status = WdfInterruptCreate(
        deviceContext->WdfDevice,
        &interruptConfig,
        &interruptAttributes,
        &deviceContext->WdfInterrupt
        );

    if (!NT_SUCCESS(status))
    {
        TraceInterrupt(TRACE_LEVEL_ERROR, "WdfInterruptCreate FAILED: %!STATUS!\n", status);
    }
    else
    {
        PINTERRUPT_CONTEXT interruptContext = InterruptGetContext(deviceContext->WdfInterrupt);
        // do whatever with context info
    }

    FuncExit();
    return status;
}

Like I said hopefully by now you have this all figured out, but I thought I would post something anyway to contribute.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top