To add a mapping of static SYSINTR values to IRQs on a target device, you can call the OALIntrStaticTranslate function during system initialization. For example, the Device Emulator BSP calls OALIntrStaticTranslate in the BSPIntrInit function to register a custom SYSINTR value for the built-in Open Host Controller Interface (OHCI) in the kernel's interrupt mapping arrays (g_oalSysIntr2Irq and g_oalIrq2SysIntr). However, static SYSINTR values and mappings are not a common way to associate IRQs with SYSINTRs because it is difficult and requires OAL code changes to implement custom interrupt handling. Static SYSINTR values are typically used for core hardware components of a target device where there is no explicit device driver and the ISR resides in the OAL.
Dynamic Interrupt Mappings
The good news is that you do not need to hardcode SYSINTR values into the OAL if you call KernelIoControl in your device drivers with an IO control code of IOCTL_HAL_REQUEST_SYSINTR to register IRQ/SYSINTR mappings. The call eventually ends in the OALIntrRequestSysIntr function, which dynamically allocates a new SYSINTR for the given IRQ, and then registers the IRQ and SYSINTR mappings in the kernel's interrupt mapping arrays. Locating a free SYSINTR value up to SYSINTR_MAXUMUM is more flexible than static SYSINTR assignments because this mechanism does not require any modifications to the OAL when you add new drivers to the BSP.
When calling KernelIoControl with IOCTL_HAL_REQUEST_SYSINTR, you establish a 1:1 relationship between IRQ and SYSINTR. If the IRQ-SYSINTR mapping table already has an entry for the specified IRQ, OALIntrRequestSysIntr will not create a second entry. To remove an entry from the interrupt mapping tables, such as when unloading a driver, call KernelIoControl with an IO control code of IOCTL_HAL_REQUEST_SYSINTR. IOCTL_HAL_RELEASE_SYSINTR dissociates the IRQ from the SYSINTR value.
The following code sample illustrates the use of IOCTL_HAL_REQUEST_SYSINTR and IOCTL_HAL_RELEASE_SYSINTR. It takes a custom value (dwLogintr) and passes this value to the OAL to be translated into a SYSINTR value, and then associates this SYSINTR with an IST event.
DWORD dwLogintr = IRQ_VALUE;
DWORD dwSysintr = 0;
HANDLE hEvent = NULL;
BOOL bResult = TRUE;
// Create event to associate with the interrupt
m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (m_hDetectionEvent == NULL) {
return ERROR_VALUE;
}
// Ask the kernel (OAL) to associate an SYSINTR value to an IRQ
bResult = KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR,
&dwLogintr, sizeof(dwLogintr),
&dwSysintr, sizeof(dwSysintr), 0);
if (bResult == FALSE) {
return ERROR_VALUE;
}
// Initialize interrupt and associate the SYSINTR value with the event.
bResult = InterruptInitialize(dwSysintr, hEvent,0,0);
if (bResult == FALSE) {
return ERROR_VALUE;
}
// Interrupt management loop
while(!m_bTerminateDetectionThread) {
// Wait for the event associated to the interrupt
WaitForSingleObject(hEvent,INFINITE);
// Add actual IST processing here
// Acknowledge the interrupt
InterruptDone(m_dwSysintr);
}
// Deinitialize interrupts will mask the interrupt
bResult = InterruptDisable(dwSysintr);
// Unregister SYSINTR
bResult = KernelIoControl(IOCTL_HAL_RELEASE_SYSINTR,
&dwSysintr, sizeof(dwSysintr), NULL, 0, 0);
// Close the event object
CloseHandle(hEvent);
Shared Interrupt Mappings
The 1:1 relationship between IRQ and SYSINTR implies that you cannot register multiple ISRs for an IRQ directly to implement interrupt sharing, but you can map multiple ISRs indirectly. The interrupt mapping tables only map an IRQ to one static ISR, yet within this ISR, you can call the NKCallIntChain function to iterate through the chain of ISRs, registered dynamically through LoadIntChainHandler. NKCallIntChain goes through the ISRs registered for the shared interrupt and returns the first SYSINTR value that is not equal to SYSINTR_CHAIN. Having determined the appropriate SYSINTR for the current interrupt source, the static ISR can pass this logical interrupt identifier to the kernel to signal the corresponding IST event. The LoadIntChainHandler function and installable ISRs are covered in more detail later in this lesson.
Communication between an ISR and an IST
Because ISR and IST run at different times and in different contexts, you must take extra care of physical and virtual memory mappings if an ISR must pass data to an IST. For example, an ISR might copy individual bytes from a peripheral device into an input buffer, returning SYSINTR_NOP until the buffer is full. The ISR returns the actual SYSINTR value only when the input buffer is ready for the IST. The kernel signals the corresponding IST event and the IST runs to copy the data into a process buffer.
One way to accomplish this data transfer is to reserve a physical memory section in a .bib file. Config.bib contains several examples for the serial and debug drivers. The ISR can then call the OALPAtoVA function to translate the physical address of the reserved memory section into a virtual address. Because the ISR runs in kernel mode, the ISR can access the reserved memory to buffer data from the peripheral device. The IST, on the other hand, calls MmMapIoSpace outside the kernel to map the physical memory to a process-specific virtual address. MmMapIoSpace uses VirtualAlloc and VirtualCopy to map the physical memory into virtual memory, yet you can also call VirtualAlloc and VirtualCopy directly if you need more control over the address mapping process.
Another option to pass data from an ISR to an IST is to allocate physical memory in SDRAM dynamically by using the AllocPhysMem function in the device driver, which is particularly useful for installable ISRs loaded into the kernel on an as-needed basis. AllocPhysMem allocates a physically contiguous memory area and returns the physical address (or fails if the allocation size is not available). The device driver can communicate the physical address to the ISR in a call to KernelIoControl based on a user-defined IO control code. The ISR then uses the OALPAtoVA function to translate the physical address into a virtual address. The IST uses MmMapIoSpace or the VirtualAlloc and VirtualCopy functions, as already explained for statically reserved memory regions.
Installable ISRs