📄 interrupt.c
字号:
//
// Permedia3 Sample Display Driver
// interrupt.c
//
// Copyright (c) 2000 Microsoft Corporation. All rights reserved.
//
// This module manages the interrupts coming from the Permedia3. It sets
// up the software necessary to process those interrupts, sets the
// Permedia3 to genertae the desired interrupts, and calls the appropriate
// helper code when an interrupt occurs.
#include "pch.h" // Precompiled header support.
#include "debug.h"
#include "struct.h"
#include "global.h"
#include "const.h"
#include "proto.h"
#include "register.h"
// This structure is passed to the kernel IO control to turn a PCI IRQ to
// a system IRQ.
typedef struct t_HAL_TRANSLATE_IRQ_BUFFER {
ULONG InterruptNumber;
ULONG InterruptVector;
} HAL_TRANSLATE_IRQ_BUFFER;
// We need a thread to act as the IST, and event for the kernel to set to
// activate the IST thread.
static HANDLE l_IstThread;
static HANDLE l_IstEvent;
// The interrupt's system identifier.
static ULONG l_Irq;
// These are used when we have a thread waiting for the vertical retrace
// interrupt.
static BOOL l_IsVsyncEvent;
static HANDLE l_VsyncEvent;
// These are used when we have a pending flip.
static BOOL l_IsFlipWaiting;
static LPVOID l_NewPrimary;
// Internal prototypes. These functions are not exposed outside this module.
static DWORD
IstThreadProcess(
LPVOID Parameter
);
BOOL
InitializeInterrupts()
{
// InitializeInterrupts
// This function sets up the thread that will respond to Permedia3
// interrupts, and configures the card to generate interrupts on the
// events we need.
// Local variables.
BOOL FnRetVal = FALSE;
HAL_TRANSLATE_IRQ_BUFFER Buffer;
ULONG BytesReturned = 0;
Enter(L"InitializeInterrupts");
// Bind the event and thread to the interrupt. First, map the PCI IRQ
// to a system IRQ.
Buffer.InterruptNumber = g_Config.PciCommonConfig.u.type0.InterruptLine;
Buffer.InterruptVector = 0;
if (KernelIoControl(IOCTL_HAL_TRANSLATE_IRQ,
&Buffer,
sizeof(Buffer),
&l_Irq,
sizeof(l_Irq),
&BytesReturned)) {
// Assert(BytesReturned == sizeof(l_Irq));
// Create the kernel objects necessary for interrupt support.
l_IstEvent = CreateEvent(NULL, // No security descriptors in CE
FALSE, // Auto reset
FALSE, // Initially not signaled
NULL); // No name
if (l_IstEvent != NULL) {
// This is a system call, don't confuse with InitializeInterrupts,
// the call we are in now. This converts the regular event into an
// interrupt event. This must happen before the IST is created/started.
if (InterruptInitialize(l_Irq,
l_IstEvent,
NULL, // No data block
0)) { // No data block
l_IstThread = CreateThread(NULL, // No security descriptor on CE
0, // default stack size
IstThreadProcess,
NULL, // No parameters to IstThreadProcess
0, // Run immeditatly
NULL); // No thread id
if (l_IstThread != NULL) {
// Initialize command variables to reasonable defaults.
l_IsFlipWaiting = FALSE;
l_NewPrimary = NULL;
l_IsVsyncEvent = FALSE;
l_VsyncEvent = CreateEvent(NULL, // No security descriptor on CE
FALSE, // Auto-reset
FALSE, // Initially not signaled
NULL); // No name
if (l_VsyncEvent != NULL) {
// This causes the IST to run, and block on the IstEvent, waiting
// for the first interrupt.
CeSetThreadPriority(l_IstThread, IST_PRIORITY);
WaitForInputFIFO(1);
// Enable the vertical blank interrupts.
WriteRegUlong(r_IntEnable,
b_IntEnable_VerticalRetraceInterruptEnable);
FnRetVal = TRUE;
Message(L"Vertical retrace interrupt enabled.\n");
}
else {
Error(L"Unable to create event for waiting on vsync.\n");
}
}
else {
Error(L"Unable to crate thread for IST.\n");
}
}
else {
Error(L"Unable to get system IRQ value.\n");
}
}
else {
Error(L"Unable to create event for IST.\n");
}
}
else {
Error(L"Unable to bind interrupt event.\n");
}
Exit(L"InitializeInterrupts");
return FnRetVal;
}
DWORD
IstThreadProcess(
LPVOID Parameter
)
{
// IstThreadProcess
// This is the function that the IST runs. It spends it's time waiting for
// an interrupt to occur. When one does (the interrupt event is signaled,)
// this thread handles it.
// Local variables.
ULONG IntFlags;
// Check parameters.
Assert(Parameter == NULL);
Enter(L"IstThreadProcess");
// !TODO! FIFO space checks?
while (1) {
WaitForSingleObject(l_IstEvent, INFINITE);
// An interrupt has occured! Process it.
IntFlags = ReadRegUlong(r_IntFlags);
if (IntFlags & b_IntFlags_VerticalRetrace) {
// Do DMA processing.
DMAInterrupt();
// Check to see if we have a thread waiting on the vertical retrace
// interrupt.
if (l_IsVsyncEvent) {
SetEvent(l_VsyncEvent);
}
// If we have a pending flip, do it now.
if (l_IsFlipWaiting) {
SetScreenBase(l_NewPrimary);
l_IsFlipWaiting = FALSE;
}
}
// Simply clear all pending interrupts. Read the register to flush the
// write.
WriteRegUlong(r_IntFlags, IntFlags);
IntFlags = ReadRegUlong(r_IntFlags);
// We're finished with this interrupt.
InterruptDone(l_Irq);
}
Exit(L"IstThreadProcess");
return 0; // We do not use the return value from this function.
}
BOOL
WaitForVerticalSync()
{
// WaitForVerticalSync
// This function is called to make the current thread sleep until the next
// vertical retrace interrupt. It returns FALSE if another thread is already
// waiting on the interrupt.
// Local variables.
BOOL FnRetVal = FALSE;
DWORD WaitReturn;
Enter(L"WaitForVeticalSync");
if (!l_IsVsyncEvent) {
// Worse case : we are prempted right after l_IsVsyncEvent is set to
// TRUE, and then we hit a vertical retrace. This causes l_VsyncEvent to
// be harmlessly set with no threads waiting. We also miss that vsync.
l_IsVsyncEvent = TRUE;
WaitReturn = WaitForSingleObject(l_VsyncEvent,
20000);
l_IsVsyncEvent = FALSE;
switch (WaitReturn) {
case WAIT_OBJECT_0:
FnRetVal = TRUE;
break;
case WAIT_TIMEOUT:
Error(L"Timeout waiting for vertical retrace!\n");
break;
case WAIT_FAILED:
Error(L"Wait on vertical retrace failed\n");
break;
}
}
else {
Message(L"Another thread is already waiting on the vsync!\n");
}
Exit(L"WaitForVerticalSync");
return FnRetVal;
}
BOOL
FlipPrimaryOnVSync(
LPVOID NewPrimary
)
{
// FlipPrimaryOnVSync
// This function is called with a pointer into video memory to use as the
// new primary surface. (We assume it is the right size and format.) This
// function schedules the flip to take place on the next vsync. It returns
// FALSE if a flip is already pending.
// Local variables.
BOOL FnRetVal = FALSE;
// Check parameters.
// !TODO!
Enter(L"FlipPrimaryOnVSync");
if (!l_IsFlipWaiting) {
l_NewPrimary = NewPrimary;
l_IsFlipWaiting = TRUE;
FnRetVal = TRUE;
}
else {
Message(L"Flip already pending.\n");
}
Exit(L"FlipPrimaryOnVSync");
return FnRetVal;
}
BOOL
IsFlipPending()
{
// IsFlipPending
// This function returns TRUE if a flip is still pending, FALSE otherwise.
// No Enter/Exit semantics for inline candidates.
return (l_IsFlipWaiting);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -