Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

504 lines
14 KiB

/***************************************************************************\
*
* ************************
* * MINIPORT SAMPLE CODE *
* ************************
*
* Module Name:
*
* interupt.c
*
* Abstract:
*
* This module contains code to control interrupts for Permedia 3.
*
* Environment:
*
* Kernel mode
*
*
* Copyright (c) 1994-1999 3Dlabs Inc. Ltd. All rights reserved.
* Copyright (c) 1995-2003 Microsoft Corporation. All Rights Reserved.
*
\***************************************************************************/
#include "perm3.h"
#pragma alloc_text(PAGE,Perm3InitializeInterruptBlock)
BOOLEAN
Perm3VideoInterrupt(
PVOID HwDeviceExtension
)
/*++
Routine Description:
Permedia3 interrupt service routine.
THIS ROUTINE CANNOT BE PAGED
Arguments:
HwDeviceExtension
Supplies a pointer to the miniport's device extension.
Return Value:
Return FALSE if it is not our interrupt. Otherwise, we'll dismiss the
interrupt on Permedia 3 before returning TRUE.
--*/
{
PHW_DEVICE_EXTENSION hwDeviceExtension = HwDeviceExtension;
PINTERRUPT_CONTROL_BLOCK pBlock;
ULONG intrFlags;
ULONG enableFlags;
ULONG backIndex;
ULONG bHaveCommandBlockMutex = FALSE;
ULONG errFlags = 0;
pPerm3ControlRegMap pCtrlRegs = hwDeviceExtension->ctrlRegBase[0];
pBlock = &hwDeviceExtension->InterruptControl.ControlBlock;
if(!hwDeviceExtension->InterruptControl.bInterruptsInitialized) {
//
// This is not our interrupt since we don't generate interrupt
// before the interrpt block got initialized
//
return(FALSE);
}
if (hwDeviceExtension->PreviousPowerState != VideoPowerOn) {
//
// We reach here because we are sharing IRQ with other devices
// and another device on the chain is in D0 and functioning
//
return(FALSE);
}
//
// Find out what caused the interrupt. We AND with the enabled interrupts
// since the flags are set if the event occurred even though no interrupt
// was enabled.
//
intrFlags = VideoPortReadRegisterUlong(INT_FLAGS);
enableFlags = VideoPortReadRegisterUlong(INT_ENABLE);
intrFlags &= enableFlags;
if (intrFlags == 0) {
return FALSE;
}
//
// Clear the interrupts we detected.
//
VideoPortWriteRegisterUlong(INT_FLAGS, intrFlags);
VideoPortReadRegisterUlong(INT_FLAGS);
if((pBlock->Control & PXRX_CHECK_VFIFO_IN_VBLANK) ||
(intrFlags & INTR_ERROR_SET)) {
errFlags = VideoPortReadRegisterUlong(ERROR_FLAGS);
//
// Keep a record of the errors. It will help us to debug
// hardware issues
//
if (errFlags & ERROR_VFIFO_UNDERRUN) {
hwDeviceExtension->UnderflowErrors++;
}
if (errFlags & ERROR_OUT_FIFO) {
hwDeviceExtension->OutputFifoErrors++;
}
if (errFlags & ERROR_IN_FIFO) {
hwDeviceExtension->InputFifoErrors++;
}
if (errFlags) {
hwDeviceExtension->TotalErrors++;
}
}
//
// Handle VBLANK interrupt
//
if (intrFlags & INTR_VBLANK_SET) {
//
// Need this only on very first interrupt but it's not a big thing.
//
pBlock->Control |= VBLANK_INTERRUPT_AVAILABLE;
//
// Get General Mutex
//
REQUEST_INTR_CMD_BLOCK_MUTEX((&(pBlock->General)), bHaveCommandBlockMutex);
if(bHaveCommandBlockMutex) {
ULONG ulValue;
//
// DirectDraw needs to have it's VBLANK flag set, when it
// sets the DIRECTDRAW_VBLANK_ENABLED bit.
//
if( pBlock->Control & (DIRECTDRAW_VBLANK_ENABLED | PXRX_SEND_ON_VBLANK_ENABLED | PXRX_CHECK_VFIFO_IN_VBLANK) ) {
if( pBlock->Control & DIRECTDRAW_VBLANK_ENABLED ) {
pBlock->DDRAW_VBLANK = TRUE;
}
//
// Don't need to do anything here. The actual processing is
// lower down, outside of the VBlank mutex.
//
} else {
//
// Disable VBLANK interrupts. DD enables them when it needs to
//
VideoPortWriteRegisterUlong(INT_ENABLE, (enableFlags & ~INTR_ENABLE_VBLANK));
}
//
// If DMA was suspended till VBLANK then simulate a DMA interrupt
// to start it off again.
//
if (pBlock->Control & SUSPEND_DMA_TILL_VBLANK) {
pBlock->Control &= ~SUSPEND_DMA_TILL_VBLANK;
//
// execute the DMA interrupt code
//
intrFlags |= INTR_ERROR_SET;
}
RELEASE_INTR_CMD_BLOCK_MUTEX((&(pBlock->General)));
}
if( (pBlock->Control & PXRX_CHECK_VFIFO_IN_VBLANK) &&
(--hwDeviceExtension->VideoFifoControlCountdown == 0) ) {
//
// It's time to check the error flags for an underrun (we don't
// keep the error interrupt turned on for long because Perm3
// generates a lot of spurious host-in DMA errors)
//
if(enableFlags & INTR_ERROR_SET) {
//
// Turn off the error interrupts now and rely on the periodic VBLANK check
//
enableFlags &= ~INTR_ERROR_SET;
VideoPortWriteRegisterUlong(INT_ENABLE, enableFlags);
}
//
// Set-up counter for our periodic check
//
hwDeviceExtension->VideoFifoControlCountdown = NUM_VBLANKS_BETWEEN_VFIFO_CHECKS;
if(errFlags & ERROR_VFIFO_UNDERRUN) {
if((enableFlags & INTR_ERROR_SET) == 0) {
//
// We've got a video FIFO underrun error: turn on error
// interrupts for a little while in order to catch any
// other errors ASAP
//
hwDeviceExtension->VideoFifoControlCountdown = NUM_VBLANKS_AFTER_VFIFO_ERROR;
VideoPortWriteRegisterUlong(INT_ENABLE,
enableFlags | INTR_ERROR_SET);
}
}
}
}
//
// Handle underrun error
//
if(errFlags & ERROR_VFIFO_UNDERRUN) {
ULONG highWater, lowWater;
//
// Clear the error
//
VideoPortWriteRegisterUlong(ERROR_FLAGS, ERROR_VFIFO_UNDERRUN);
//
// Lower the video FIFO thresholds. If the new upper threshold is 0
// (indicating the thresholds are currently both 1) then we can't
// go any lower, so just leave the underrun bit set (that way at
// least we won't get any more error interrupts)
//
highWater = ((hwDeviceExtension->VideoFifoControl >> 8) & 0xff) - 1;
if(highWater) {
//
// Load up the new FIFO control and clear the underrun bit.
// The lower threshold is set to 8 if the upper threshold
// is >= 15, otherwise it's set to 1/2 the upper threshold
//
lowWater = highWater >= 15 ? 8 : ((highWater + 1) >> 1);
hwDeviceExtension->VideoFifoControl = (1 << 16) |
(highWater << 8) |
lowWater;
do {
VideoPortWriteRegisterUlong(VIDEO_FIFO_CTL,
hwDeviceExtension->VideoFifoControl);
} while(VideoPortReadRegisterUlong(VIDEO_FIFO_CTL) & (1 << 16));
VideoDebugPrint((3, "Perm3: Setting new Video Fifo thresholds to %d and %d\n", highWater, lowWater));
}
}
//
// Handle outfifo error
//
if(errFlags & ERROR_OUT_FIFO) {
//
// If we got here by generating an OutputFIFO error, clear it
//
VideoPortWriteRegisterUlong(ERROR_FLAGS, ERROR_OUT_FIFO);
#ifdef MASK_OUTFIFO_ERROR_INTERRUPT
enableFlags &= ~INTR_ERROR_SET;
VideoPortWriteRegisterUlong(INT_ENABLE, enableFlags);
#endif
}
//
// The error interrupt occurs each time the display driver adds an entry
// to the queue. We treat it exactly as though we had received a DMA
// interrupt.
//
if (intrFlags & (INTR_DMA_SET | INTR_ERROR_SET)) {
//
// if suspended till VBLANK we can't do any DMA.
//
if (pBlock->Control & SUSPEND_DMA_TILL_VBLANK) {
VideoDebugPrint(( 1, "Perm3: DMA suspended till VBLANK\n"));
return(TRUE);
}
//
// If the previous DMA has not completed we can't do anything. We've
// cleared the interrupt flag for this interrupt so even if the DMA
// completes before we return, we'll immediately get another
// interrupt. Since we will be getting another interrupt we do not
// have to clear the InterruptPending flag.
//
if (VideoPortReadRegisterUlong(DMA_COUNT) != 0) {
//
// DMA in progress, leaving
//
return(TRUE);
}
//
// We may return without starting any DMA and hence not expecting any
// interrupt so clear the InterruptPending flag. This will force the
// display driver to wake us. MUST DO THIS BEFORE CHECKING THE QUEUE.
// Since the display driver adds entries and then checks the flag, we
// must do it in the reverse order.
//
pBlock->InterruptPending = 0;
//
// if the DMA queue is empty then we have nothing to do
//
backIndex = pBlock->backIndex;
if (pBlock->frontIndex == backIndex) {
//
// Queue is empty, leaving.
//
return(TRUE);
}
//
// Since we know we'll get a DMA interrupt, we don't need a wakeup so
// set the InterruptPending flag to true.
//
pBlock->InterruptPending = 1;
//
// kick off DMA for the next Q entry and remove it. DO NOT remove from
// the queue first because on a multi-processor machine the display
// driver could modify the now free queue entry before we read it.
//
VideoPortWriteRegisterUlong(DMA_ADDRESS, pBlock->dmaQueue[backIndex].address);
VideoPortWriteRegisterUlong(DMA_COUNT, pBlock->dmaQueue[backIndex].count);
//
// Keep track of where the last DMA to start was from:
//
pBlock->lastAddr = pBlock->dmaQueue[backIndex].address;
//
// now remove the entry from the queue
//
if (++backIndex == pBlock->endIndex)
backIndex = 0;
pBlock->backIndex = backIndex;
}
return TRUE;
}
BOOLEAN
Perm3InitializeInterruptBlock(
PHW_DEVICE_EXTENSION hwDeviceExtension
)
/*++
Routine Description:
Do any initialization needed for interrupts, such as allocating the shared
memory control block.
Arguments:
HwDeviceExtension - Supplies a pointer to the miniport's device extension.
Return Value:
TRUE
--*/
{
PVOID HwDeviceExtension = (PVOID)hwDeviceExtension;
PINTERRUPT_CONTROL_BLOCK pBlock;
PVOID SavedPtr;
PVOID pkdpc;
//
// This is set to zero since it is on longer used
//
hwDeviceExtension->InterruptControl.PhysAddress.LowPart =
hwDeviceExtension->InterruptControl.PhysAddress.HighPart = 0;
//
// Set up the control block
//
pBlock = &hwDeviceExtension->InterruptControl.ControlBlock;
//
// Initialize the circular DMA queue
//
pBlock->frontIndex = pBlock->backIndex = 0;
pBlock->maximumIndex = MAX_DMA_QUEUE_ENTRIES - 1;
//
// The size of the queue we actually use is dynamically configurable but
// initialize it to be as small as possible. This default size will work
// for all interrupt driven DMA buffers regardless of how many buffers
// are actually available.
//
pBlock->endIndex = 2;
//
// Initially no interrupts are available. Later we try to enable the
// interrupts and if they happen the interrupt handler will set the
// available bits in this word. So it's a sort of auto-sensing mechanism.
//
pBlock->Control = 0;
pBlock->InterruptPending = 0;
//
// Initialize the VBLANK interrupt command field
//
pBlock->VBCommand = NO_COMMAND;
//
// Initialize the General update in VBLANK fields (only used by P2)
//
pBlock->General.bDisplayDriverHasAccess = FALSE;
pBlock->General.bMiniportHasAccess = FALSE;
VideoPortZeroMemory( &pBlock->pxrxDMA, sizeof(pBlock->pxrxDMA) );
hwDeviceExtension->InterruptControl.bInterruptsInitialized = TRUE;
hwDeviceExtension->OutputFifoErrors = 0;
hwDeviceExtension->InputFifoErrors = 0;
hwDeviceExtension->UnderflowErrors = 0;
hwDeviceExtension->TotalErrors = 0;
return(TRUE);
}