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.
 
 
 
 
 
 

333 lines
9.0 KiB

//***************************************************************************
//
// Module Name:
//
// interupt.c
//
// Abstract:
//
// This module contains code to control interrupts for Permedia2. The
// interrupt handler performs operations required by the display driver.
// To communicate between the two we set up a piece of non-paged shared
// memory to contain synchronization information and a queue for DMA
// buffers.
//
// Environment:
//
// Kernel mode
//
//
// Copyright (c) 1994-1998 3Dlabs Inc. Ltd. All rights reserved.
// Copyright (c) 1995-1999 Microsoft Corporation. All Rights Reserved.
//
//***************************************************************************
#include "permedia.h"
#pragma alloc_text(PAGE,Permedia2InitializeInterruptBlock)
#pragma optimize("x",on)
//
// *** THIS ROUTINE CANNOT BE PAGED ***
//
BOOLEAN
Permedia2VidInterrupt(
PVOID HwDeviceExtension
)
/*++
Routine Description:
Interrupts are enabled by the DD as and when they are needed. The miniport
simply indicates via the Capabilities flags whether interrupts are
available.
Arguments:
HwDeviceExtension - Supplies a pointer to the miniport's device extension.
Return Value:
--*/
{
PHW_DEVICE_EXTENSION hwDeviceExtension = HwDeviceExtension;
PINTERRUPT_CONTROL_BLOCK pBlock;
ULONG intrFlags;
ULONG enableFlags;
P2_DECL;
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;
}
intrFlags = VideoPortReadRegisterUlong(INT_FLAGS);
enableFlags = VideoPortReadRegisterUlong(INT_ENABLE);
//
// if this a SVGA interrupt, some one must have enabled SVGA interrupt
// by accident. We should first disable interrupt from SVGA unit and
// then dismiss then current interupt
//
if(intrFlags & INTR_SVGA_SET) {
USHORT usData;
UCHAR OldIndex;
//
// Disable interrupt from SVGA unit
//
OldIndex = VideoPortReadRegisterUchar(PERMEDIA_MMVGA_INDEX_REG);
VideoPortWriteRegisterUchar(PERMEDIA_MMVGA_INDEX_REG,
PERMEDIA_VGA_CTRL_INDEX);
usData = (USHORT)VideoPortReadRegisterUchar(PERMEDIA_MMVGA_DATA_REG);
usData &= ~PERMEDIA_VGA_INTERRUPT_ENABLE;
usData = (usData << 8) | PERMEDIA_VGA_CTRL_INDEX;
VideoPortWriteRegisterUshort(PERMEDIA_MMVGA_INDEX_REG, usData);
VideoPortWriteRegisterUchar(PERMEDIA_MMVGA_INDEX_REG,
OldIndex);
//
// Dismiss current SVGA interrupt
//
OldIndex = VideoPortReadRegisterUchar(PERMEDIA_MMVGA_CRTC_INDEX_REG);
VideoPortWriteRegisterUchar(PERMEDIA_MMVGA_CRTC_INDEX_REG,
PERMEDIA_VGA_CR11_INDEX);
usData = (USHORT)VideoPortReadRegisterUchar(PERMEDIA_MMVGA_CRTC_DATA_REG);
usData &= ~PERMEDIA_VGA_SYNC_INTERRUPT;
usData = (usData << 8) | PERMEDIA_VGA_CR11_INDEX;
VideoPortWriteRegisterUshort(PERMEDIA_MMVGA_CRTC_INDEX_REG, usData);
VideoPortWriteRegisterUchar(PERMEDIA_MMVGA_CRTC_INDEX_REG,
OldIndex);
return TRUE;
}
//
// 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 &= enableFlags;
if (intrFlags == 0)
return FALSE;
//
// select the interrupt control block for this board
//
pBlock = hwDeviceExtension->InterruptControl.ControlBlock;
VideoPortWriteRegisterUlong(INT_FLAGS, intrFlags);
if (pBlock == NULL) return TRUE;
#if DBG
//
// if this error handler bugchecks, we have a synchronization problem
// with the display driver
//
if (intrFlags & INTR_ERROR_SET)
{
ULONG ulError = VideoPortReadRegisterUlong (ERROR_FLAGS);
if (ulError & (0xf|0x700))
{
pBlock->ulLastErrorFlags=ulError;
pBlock->ulErrorCounter++;
DEBUG_PRINT((0, "***Error Interrupt!!!(%lxh)\n", ulError));
}
VideoPortWriteRegisterUlong ( ERROR_FLAGS, ulError);
}
pBlock->ulIRQCounter++;
if (intrFlags & INTR_VBLANK_SET)
{
pBlock->ulControl |= VBLANK_INTERRUPT_AVAILABLE;
pBlock->ulVSIRQCounter++;
}
#endif
if (intrFlags & INTR_DMA_SET)
{
pBlock->ulControl |= DMA_INTERRUPT_AVAILABLE;
//
// lock access to shared memory section first
//
if (VideoPortInterlockedExchange((LONG*)&pBlock->ulICBLock,TRUE))
{
return TRUE;
}
if (VideoPortReadRegisterUlong(DMA_COUNT) == 0)
{
if (pBlock->pDMAWritePos>pBlock->pDMANextStart)
{
VideoPortWriteRegisterUlong(DMA_ADDRESS,
(ULONG)(pBlock->liDMAPhysAddress.LowPart +
(pBlock->pDMANextStart-
pBlock->pDMABufferStart)
*sizeof(ULONG)));
VideoPortWriteRegisterUlong(DMA_COUNT,
(ULONG)(pBlock->pDMAWritePos-pBlock->pDMANextStart));
pBlock->pDMAWriteEnd = pBlock->pDMABufferEnd;
pBlock->pDMAPrevStart = pBlock->pDMANextStart;
pBlock->pDMANextStart = pBlock->pDMAWritePos;
} else if (pBlock->pDMAWritePos<pBlock->pDMANextStart)
{
VideoPortWriteRegisterUlong(DMA_ADDRESS,
(ULONG)(pBlock->liDMAPhysAddress.LowPart +
(pBlock->pDMANextStart-
pBlock->pDMABufferStart)
*sizeof(ULONG)));
VideoPortWriteRegisterUlong(DMA_COUNT,
(ULONG)(pBlock->pDMAActualBufferEnd-pBlock->pDMANextStart));
pBlock->pDMAActualBufferEnd=pBlock->pDMABufferEnd;
pBlock->pDMAPrevStart=pBlock->pDMANextStart;
pBlock->pDMAWriteEnd =pBlock->pDMANextStart-1;
pBlock->pDMANextStart=pBlock->pDMABufferStart;
} else //if (pBlock->pDMAWritePos==pBlock->pDMANextStart)
{
//
// turn off IRQ service if we are idle...
//
VideoPortWriteRegisterUlong(INT_ENABLE, enableFlags & ~INTR_DMA_SET);
}
}
//
// release lock, we are done
//
VideoPortInterlockedExchange((LONG*)&pBlock->ulICBLock,FALSE);
}
return TRUE;
}
#pragma optimize("",on)
BOOLEAN
Permedia2InitializeInterruptBlock(
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:
--*/
{
PINTERRUPT_CONTROL_BLOCK pBlock;
PVOID virtAddr;
ULONG size;
PHYSICAL_ADDRESS phyadd;
ULONG ActualSize;
//
// allocate a page of non-paged memory. This will be used as the shared
// memory between the display driver and the interrupt handler. Since
// it's only a page in size the physical addresses are contiguous. So
// we can use this as a small DMA buffer.
//
size = PAGE_SIZE;
virtAddr = VideoPortGetCommonBuffer( hwDeviceExtension,
size,
PAGE_SIZE,
&phyadd,
&ActualSize,
TRUE );
hwDeviceExtension->InterruptControl.ControlBlock = virtAddr;
hwDeviceExtension->InterruptControl.Size = ActualSize;
if ( (virtAddr == NULL) || (ActualSize < size) )
{
DEBUG_PRINT((1, "ExAllocatePool failed for interrupt control block\n"));
return(FALSE);
}
VideoPortZeroMemory( virtAddr, size);
//
// We can't flush the cache from the interrupt handler because we must be
// running at <= DISPATCH_LEVEL to call KeFlushIoBuffers. So we need a DPC
// to do the cache flush.
//
DEBUG_PRINT((2, "Initialized custom DPC routine for cache flushing\n"));
P2_ASSERT((sizeof(INTERRUPT_CONTROL_BLOCK) <= size),
"InterruptControlBlock is too big. Fix the driver!!\n");
//
// set up the control block
//
pBlock = virtAddr;
pBlock->ulMagicNo = P2_ICB_MAGICNUMBER;
//
// we rely on the pBlock data being set to zero after allocation!
//
return(TRUE);
}