Introduction to Message-Signaled Interrupts
Message-signaled interrupts (MSIs) were introduced in the PCI 2.2 specification as an alternative to line-based interrupts. Instead of using a dedicated pin to trigger interrupts, devices that use MSIs trigger an interrupt by writing a value to a particular memory address. PCI 3.0 defines an extended form of MSI, called MSI-X, that enables greater programmability. Windows Vista and later versions of Windows support MSI and MSI-X. A single device can support both MSI and MSI-X. For such a device, the operating system will automatically use MSI-X.
An interrupt message is a particular value that a device writes to a particular address to trigger an interrupt. Unlike line-based interrupts, message-signaled interrupts have edge semantics. The device sends a message but does not receive any hardware acknowledgment that the interrupt was received.
For PCI 2.2, a message consists of an address and a partially opaque 16-bit value. Each device is assigned a single address. To send multiple messages, the device can use the lower 4 bits of the message value to distinguish messages. Therefore, for PCI 2.2, devices can support up to 16 messages.
For PCI 3.0, a message consists of an address and an opaque 32-bit value. Each different message has its own unique address. Unlike for PCI 2.2, the device does not modify the value. For PCI 3.0, a device can support up to 2,048 different messages. Devices that support PCI 3.0 MSI-X feature a dynamically programmable hardware table that contains entries for each of the interrupt sources in the device. Each entry in this table can be programmed with one of the messages that are allocated to a device, and can be independently masked. Drivers can change the programming of an interrupt message into a table entry and whether an entry has been masked. For more information, see Dynamically Configuring MSI-X.
Drivers can handle MSIs that a device sends as follows:
During driver installation, enable MSIs in the registry. You can also use the registry to specify the number of messages to allocate for the device. For more information, see Enabling Message-Signaled Interrupts in the Registry.
Optionally, increase the number of interrupt messages and save some per-message settings by responding to an IRP_MN_FILTER_RESOURCE_REQUIREMENTS request. For more information, see Using Interrupt Resource Descriptors.
In the driver's dispatch routine for IRP_MN_START_DEVICE, call IoConnectInterruptEx to register an InterruptService or InterruptMessageService routine to service the device's interrupts. Use the CONNECT_FULLY_SPECIFIED version of IoConnectInterruptEx to register an InterruptService routine for a specific message or the CONNECT_MESSAGE_BASED version of IoConnectInterruptEx to register a single InterruptMessageService routine for all messages. For more information, see Using the CONNECT_MESSAGE_BASED Version of IoConnectInterruptEx and Using the CONNECT_FULLY_SPECIFIED Version of IoConnectInterruptEx.
After the driver no longer intends to service interrupts from the device, call IoDisconnectInterruptEx (after disabling the device's interrupts) to remove any registered interrupt service routines.
Drivers that are designed to use multiple messages should check that the expected number of messages is allocated. If the Plug and Play (PnP) manager cannot allocate the requested number of messages, it instead allocates exactly one message to the device. Drivers can check the number of messages that are actually allocated in one of the following ways:
The PnP manager reports the number of allocated messages in its list of raw resource descriptors. For more information, see Using Interrupt Resource Descriptors.
When IoConnectInterruptEx returns, it sets Parameters->MessageBased.ConnectContext.InterruptMessageTable->MessageCount to the number of allocated messages.