Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

459 lines
11 KiB

/*
* stream.c
*
* 32-bit Video Capture driver
* hardware-specific streaming routines for Bravado board.
*
*
*
* Geraint Davies, Feb 93
*/
#include <vckernel.h>
#include <bravado.h>
#include "hardware.h"
#include "vidcio.h"
#include "profile.h"
#if DBG
profiling prf_core, prf_line;
#endif
/*
* outline of data streaming interface:
*
*
* StreamInit will be called to initialise for streaming.
* This will be followed by a call to StreamStart to start
* streaming (immediately). Thus any time-consuming setup or cueing should
* be done in the StreamInit call.
*
* After StreamStart has enabled interrupts, our InterruptAck routine
* will be called on each interrupt. This is responsible for:
* -- clearing and re-enabling interrupts
* -- determining if it is time yet to capture a frame (based on the
* microsecs/frame argument to StreamInit).
*
* If InterruptAck returns TRUE to request capture service, then the
* CaptureService function will be called at some later point - IF there is
* a user-supplied buffer to copy into. At this point, the buffer will be
* locked down and the data can be copied in, subject to any conversion.
* If there is no buffer, the capture service routine will be called with
* a NULL buffer pointer, for it to discard any data and re-arm for capture:
* we dont currently use this in the bravado driver.
*
*
* Mutual exclusion outside of this module ensures that only one
* of the init/fini/start/stop functions is called at one time. However,
* there is nothing to prevent the InterruptAcknowledge or the
* CaptureService being called at any time. The interrupt ack routine
* needs to package up all code that needs to be synchronised (in
* our case, access to the PCV index register) in calls to
* VC_SynchronizeExecution.
*
* The capture service routine only accesses the frame buffer, and no other
* routine does that, so there is no contention. If there were any
* contention, we would have to package the code into calls to
* VC_SynchronizeDPC (for sync between the CaptureService and the
* passive-level functions) or VC_SynchronizeExecution (for sync with
* the interrupt routine as well as everything else).
*/
VOID HW_EnableInts(PDEVICE_INFO pDevInfo, BOOL bInIsr);
VOID HW_ReEnableInts(PDEVICE_INFO pDevInfo, BOOL bInIsr);
VOID HW_DisableInts(PDEVICE_INFO pDevInfo, BOOL bInIsr);
/*
* initialise and cue ready for streaming from the framebuffer into
* memory. The argument gives the desired frame rate.
*
* We have very little to do at this point except store the msperframe arg.
*
*/
BOOLEAN
HW_StreamInit(PDEVICE_INFO pDevInfo, ULONG microsecperframe)
{
PHWINFO pHw;
pHw = VC_GetHWInfo(pDevInfo);
/* remember the frame rate. Keep it in microsec per frame
* to reduce the rounding errors that would be introduced if we
* did everything in millisecs
*/
pHw->dwMicroPerFrame = microsecperframe;
/* check that a format has been set */
if (pHw->Format == FmtInvalid) {
dprintf1(("trying to capture without setting format"));
return(FALSE);
}
return(TRUE);
}
/* clean up after streaming. nothing to do here */
BOOLEAN
HW_StreamFini(PDEVICE_INFO pDevInfo)
{
return (TRUE);
}
/*
* start streaming data into the users buffers.
*
* Reset the counters and flag that capture is in progress.
* Then enable interrupts (per frame) and wait for the right number
* of interrupts.
*/
BOOLEAN
HW_StreamStart(PDEVICE_INFO pDevInfo)
{
/* we set the microsecs per vsync interrupt based on the
* PAl/NTSC field rate, and we use this to update the video
* clock every interrupt. When this reaches the next frame time,
* we capture the frame.
*
*/
PHWINFO pHw = VC_GetHWInfo(pDevInfo);
pHw->dwNextFrameNr = 0;
pHw->dwMicroPerInterrupt = (pHw->VideoStd == PAL) ? PAL_MICROSPERFRAME : NTSC_MICROSPERFRAME;
pHw->dwMicroPerInterrupt /= 2;
pHw->dwNextFrameTime = 0;
pHw->dwVideoTime = 0;
pHw->dwInterruptCount = 0;
pHw->bVideoIn = TRUE;
pHw->bCapturing = FALSE;
pHw->iNotBusy = 0;
INIT_PROFILING(&prf_core);
INIT_PROFILING(&prf_line);
/* enable acquisition into the buffer */
if (!HW_Capture(pDevInfo, TRUE)) {
return(FALSE);
}
HW_EnableInts(pDevInfo, FALSE);
return(TRUE);
}
/*
* stop streaming - simply disable interrupts on the PCVideo chip.
*
*/
BOOLEAN
HW_StreamStop(PDEVICE_INFO pDevInfo)
{
PHWINFO pHw = VC_GetHWInfo(pDevInfo);
pHw->bVideoIn = FALSE;
HW_DisableInts(pDevInfo, FALSE);
#if DBG
if (PROFILING_COUNT(&prf_core) > 1) {
dprintf1(("core loop (%d) %d usecs/frame",
PROFILING_COUNT(&prf_core), PROFILING_TIME(&prf_core)));
dprintf1(("line loop (%d) %d usecs/line",
PROFILING_COUNT(&prf_line), PROFILING_TIME(&prf_line)));
}
#endif
return(TRUE);
}
/*
* get the position - for this we return the number of millisecs
* since capture began, according to the video vsync clock
*/
ULONG
HW_StreamGetPosition(PDEVICE_INFO pDevInfo)
{
PHWINFO pHw = VC_GetHWInfo(pDevInfo);
return( (ULONG) pHw->dwVideoTime);
}
/*
* interrupt acknowledge routine. This is called to ack the interrupt
* and re-enable it for next time. It should return TRUE if it is time
* to capture a frame.
*
* If we are not currently capturing (bVideoIn is false), then disable
* interrupts and stop. Inc the interrupt count in any case, since the
* interrupt may have been generated by HW_Init to test the hardware.
*/
BOOLEAN
HW_InterruptAcknowledge(PDEVICE_INFO pDevInfo)
{
PHWINFO pHw = VC_GetHWInfo(pDevInfo);
pHw->dwInterruptCount++;
/* clear and re-enable interrupts */
HW_ReEnableInts(pDevInfo, TRUE);
/* are we actually streaming ? */
if (!pHw->bVideoIn) {
HW_DisableInts(pDevInfo, TRUE);
return(FALSE);
}
pHw->dwVideoTime = (pHw->dwInterruptCount * pHw->dwMicroPerInterrupt) / 1000;
// we only capture on even field interrupts
if ((pHw->dwInterruptCount & 1) ||
(pHw->dwVideoTime < pHw->dwNextFrameTime)) {
if (pHw->bCapturing) {
pHw->iNotBusy = 0;
} else {
pHw->iNotBusy++;
}
return(FALSE);
} else {
// check that we are not overrunning
if (pHw->bCapturing) {
//dprintf(("overrun"));
pHw->iNotBusy = 0;
return(FALSE);
}
/* check that we have had enough fields with
* acquire enabled for a frame to be captured
*
* - check the acquisition mode to see if we are capturing
* 1 or 2 fields.
*/
if ((pHw->iNotBusy < 1) ||
((pHw->iNotBusy < 2) && !(pHw->bRegPCVideo[REG_ACQMODE] & 0x4))) {
// we didn't re-enable soon enough to get this frame
return(FALSE);
}
// reset the count of safely captured fields
pHw->iNotBusy = 0;
/* advance next frame time */
pHw->dwNextFrameNr++;
pHw->dwNextFrameTime = (pHw->dwNextFrameNr * pHw->dwMicroPerFrame) / 1000;
return(TRUE);
}
}
/*
* Capture a frame - copy the frame buffer into the buffer passed
* as argument. The user buffer is at this point locked down and
* mapped into system memory.
*
* returns bytes written to buffer PUCHAR (whose length is ULONG)
* called with null if no buffer - nothing to do in this case.
*
* returns the time stamp in pTimeStamp (the millisec count
* since capture began - based on the number of video syncs and the
* video standard (PAL, NTSC...).
*/
ULONG
HW_CaptureService(
PDEVICE_INFO pDevInfo,
PUCHAR pBuffer,
PULONG pTimeStamp,
ULONG BufferLength)
{
PHWINFO pHw = VC_GetHWInfo(pDevInfo);
PUCHAR pFrame = VC_GetFrameBuffer(pDevInfo);
ULONG BitmapSize;
/*
* do nothing if no buffer is ready
*/
if (pBuffer == NULL) {
return(0);
}
*pTimeStamp = (ULONG) pHw->dwVideoTime;
/*
* disable acquisition: we can't access the frame buffer at the
* same time as acquisition
*/
pHw->bCapturing = TRUE;
HW_Capture(pDevInfo, FALSE);
START_PROFILING(&prf_core);
switch(pHw->Format) {
case FmtPal8:
/*
* convert to palettised data (using the translation table) while
* copying out of the frame buffer
*/
CopyYUVToPal8(pBuffer,
pFrame,
pHw->pXlate,
pHw->dwWidth,
pHw->dwHeight,
FRAMEBUFFERWIDTH);
BitmapSize = pHw->dwHeight * pHw->dwWidth;
break;
case FmtRGB555:
/*
* convert to 16-bit RGB while copying from the buffer (using
* the translation table)
*/
CopyYUVToRGB555(pBuffer,
pFrame,
(PWORD) pHw->pXlate,
pHw->dwWidth,
pHw->dwHeight,
FRAMEBUFFERWIDTH);
BitmapSize = pHw->dwHeight * pHw->dwWidth * 2;
break;
case FmtRGB24:
/*
* convert to 24-bit RGB while copying from the buffer*/
CopyYUVToRGB24(pBuffer,
pFrame,
pHw->dwWidth,
pHw->dwHeight,
FRAMEBUFFERWIDTH);
BitmapSize = pHw->dwHeight * pHw->dwWidth * 3;
break;
case FmtYUV:
/* straight copy of the rectangle. call the generic
* rectangle copy routine - tell it sizes in bytes,
* not pixels
*/
CopyRectFromIOMemory(pBuffer,
pFrame,
pHw->dwWidth * 2, // width in bytes, not pixels
pHw->dwHeight,
FRAMEBUFFERWIDTH);
BitmapSize = pHw->dwHeight * pHw->dwWidth * 2;
break;
}
STOP_PROFILING(&prf_core);
/*
* now that we have finished with the frame buffer, re-start
* acquisition
*/
HW_Capture(pDevInfo, TRUE);
pHw->bCapturing = FALSE;
return(BitmapSize);
}
/* -- internal functions ----------------------------------------------*/
/*
* enable vertical-sync interrupts. We use one interrupt per field
*/
VOID
HW_EnableInts(PDEVICE_INFO pDevInfo, BOOL bInIsr)
{
if (bInIsr) {
SYNC_REG_ARG SyncArg;
SyncArg.pDevInfo = pDevInfo;
SyncArg.bRegister = REG_INTERRUPT;
SyncArg.bData = 0x3;
HW_SetPCVideoReg_Sync(&SyncArg);
} else {
HW_SetPCVideoReg(pDevInfo, REG_INTERRUPT, 0x3);
}
}
/*
* clear an interrupt and re-enable
*/
VOID
HW_ReEnableInts(PDEVICE_INFO pDevInfo, BOOL bInIsr)
{
if (bInIsr) {
SYNC_REG_ARG SyncArg;
SyncArg.pDevInfo = pDevInfo;
SyncArg.bRegister = REG_INTERRUPT;
SyncArg.bData = 0x0;
HW_SetPCVideoReg_Sync(&SyncArg);
SyncArg.bData = 0x3;
HW_SetPCVideoReg_Sync(&SyncArg);
} else {
// clear the interrupt
HW_SetPCVideoReg(pDevInfo, REG_INTERRUPT, 0);
// re-enable frame-based interrupts
HW_SetPCVideoReg(pDevInfo, REG_INTERRUPT, 0x3);
}
}
/* disable interrupts on the card */
VOID
HW_DisableInts(PDEVICE_INFO pDevInfo, BOOL bInIsr)
{
if (bInIsr) {
SYNC_REG_ARG SyncArg;
SyncArg.pDevInfo = pDevInfo;
SyncArg.bRegister = REG_INTERRUPT;
SyncArg.bData = 0x0;
HW_SetPCVideoReg_Sync(&SyncArg);
} else {
HW_SetPCVideoReg(pDevInfo, REG_INTERRUPT, 0);
}
}