NT4/private/windows/media/vidcap/vckernel/vcisr.c

309 lines
9.2 KiB
C
Raw Normal View History

2001-01-01 00:00:00 +01:00
/*
* vcisr.c
*
*
* 32-bit Video Capture driver
* kernel-mode support library - interrupt dispatch
*
* Geraint Davies, Feb 93
*/
#include <vckernel.h>
#include "vckpriv.h"
#include "profile.h"
#if DBG
extern profiling looptime;
#endif
/* interrupt service routine - returns TRUE if interrupt handled.
* all interrupts come in here and are then dispatched to hw ack routine
* the Context pointer is a pointer to DEVICE_INFO.
*/
BOOLEAN
VC_InterruptService(
IN PKINTERRUPT pInterruptObject,
IN PVOID Context
)
{
PDEVICE_INFO pDevInfo;
pDevInfo = (PDEVICE_INFO)Context;
/*
* make sure that the hardware-specific code has installed
* an ack routine
*/
if (pDevInfo->Callback.InterruptAcknowledge == NULL) {
/* no isr - and yet we are getting interrupts -
* we should disconnect the interrupt
*/
dprintf(("interrupt occured without h/w isr"));
//IoDisconnectInterrupt(pDevInfo->InterruptObject);
return(FALSE);
}
/*
* call the acknowledge. this will not do any service, but will
* return TRUE if the service routine is to be called.
*/
if (!pDevInfo->Callback.InterruptAcknowledge(pDevInfo)) {
return(FALSE);
}
/* the isr reckons that it is time to schedule the service
* routine. This is done on a DPC.
*/
if (pDevInfo->DpcRequested) {
//dprintf5(("dpc overrun"));
} else {
pDevInfo->DpcRequested = TRUE;
IoRequestDpc(pDevInfo->pDeviceObject, NULL, pDevInfo);
}
/* everything else is done in dpc routine */
return(TRUE);
}
/*
* DPC routine to do work for interrupt service. Scheduled because
* hardware acknowledge routine said that it was time to call its
* service routine.
*
* If there is an outstanding buffer, pass this to the h/w service
* routine to fill, and then complete the irp.
*
* if there is no buffer, then we have to miss a frame. if there is an
* outstanding wait-error request, then we can complete that to report
* the overrun. If not, we just count one more unreported overrun
* to save up for the next wait-error.
*
* If we have no buffer, we must still call the hardware capture routine,
* with a NULL buffer pointer, so it can discard any data and re-arm for
* capture.
*
* Note that the pIrpNotUsed is an arg passed from the IoRequestDpc call.
* We don't work out which Irp to complete until this function, so
* we ignore that. The context argument is a PDEVICE_INFO.
*
* The buffered parameter to the add-buffer irp is the CAPTUREBUFFER structure.
* We need to store the bytes written, and the timestamp there. The
* virtual address for the buffer has been locked down and is
* represented by the Irp->MdlAddress. We mapped this ourselves but
* we don't need to free it ourselves since we added it to the irp. the i/o
* subsystem will unlock and free it when freeing the irp.
*
* Contention: see comment at top of vcdisp.c. We use the cancel spinlock
* to protect accesses to the irp queues against contention from
* passive-level requests, but we do not hold it for the entire
* service routine.
*/
VOID
VC_Deferred(
PKDPC pDpc,
PDEVICE_OBJECT pDeviceObject,
PIRP pIrpNotUsed,
PVOID Context
)
{
PDEVICE_INFO pDevInfo;
PIRP pIrp = NULL;
PUCHAR pData;
ULONG ulLength;
PCAPTUREBUFFER pCapBuf;
pDevInfo = (PDEVICE_INFO)Context;
pDevInfo->DpcRequested = FALSE;
/*
* check that the h/w specific code has installed a service handler
* some kind of internal error otherwise.
*/
if (pDevInfo->Callback.CaptureService == NULL) {
dprintf(("dpc requested but no service routine"));
return;
}
/*
* if there is a system buffer and it is in use, then
* we should fail to capture anything else
*/
if ((pDevInfo->pSystemBuffer != NULL) && (pDevInfo->SysBufInUse != 0)) {
/* report overrun, same as for no-buffer cases */
/*
* notify the hardware portion that there is no buffer, so
* he must throw away everything and re-arm.
*/
pDevInfo->Callback.CaptureService(pDevInfo, NULL, NULL, 0);
/*
* access to the skip count needs to be locked together
* with access to the queue of wait-error requests. This function
* increments the skip-count and completes a wait-error request
* if there is one.
*/
VC_ReportSkip(pDevInfo);
}
/*
* We need to interlock access to the queue using the Cancel
* spinlock (see vcqueue.c) - but we don't need to interlock
* the whole of this function.
*
* Any further contention between this code and the
* passive-level code will be resolved by the h/wspecific code.
*/
/*
* get the next irp from the queue
*/
pIrp = VC_ExtractNextIrp(&pDevInfo->BufferHead, FALSE);
if (pIrp) {
PIO_STACK_LOCATION pIoStack;
ULONG IoCode;
/*
* is this cap_to_sysbuf or a real one ?
*/
pIoStack = IoGetCurrentIrpStackLocation(pIrp);
IoCode = pIoStack->Parameters.DeviceIoControl.IoControlCode;
if(IoCode == IOCTL_VIDC_ADD_BUFFER) {
/* get the address of the callers buffer, mapped into
* system memory
*/
pData = (PUCHAR) MmGetSystemAddressForMdl(pIrp->MdlAddress);
if (pData == NULL) {
dprintf(("MmGetSystemAddressforMdl failed in dpc"));
pIrp->IoStatus.Information = 0;
pIrp->IoStatus.Status = STATUS_NO_MEMORY;
} else {
/* get a pointer to the (buffered) CAPTUREBUFFER header */
pCapBuf = (PCAPTUREBUFFER) pIrp->AssociatedIrp.SystemBuffer;
/*
* find the length of the data buffer
*/
ulLength = pCapBuf->BufferLength;
//profile this call
START_PROFILING(&looptime);
pCapBuf->BytesUsed = (DWORD)
pDevInfo->Callback.CaptureService(pDevInfo,
pData,
(PULONG) &pCapBuf->TimeCaptured,
ulLength);
STOP_PROFILING(&looptime);
/* if a capture service routine returns 0, it means
* that it has not completed this capture, and would like
* the same buffer back for the next field if possible
* - in this case, return the buffer to the start of the
* queue
*/
if (pCapBuf->BytesUsed == 0) {
if (VC_ReplaceRequest(pIrp, &pDevInfo->BufferHead, VC_Cancel)) {
// nothing more to do till he gets another crack
return;
} else {
// this irp has been cancelled
pIrp->IoStatus.Status = STATUS_CANCELLED;
pIrp->IoStatus.Information = 0;
}
} else {
/* we copy back the CAPTUREBUFFER struct */
pIrp->IoStatus.Information = sizeof(CAPTUREBUFFER);
pIrp->IoStatus.Status = STATUS_SUCCESS;
}
}
} else if (IoCode == IOCTL_VIDC_CAP_TO_SYSBUF) {
if (pDevInfo->pSystemBuffer == NULL) {
pIrp->IoStatus.Information = 0;
pIrp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE;
} else {
/*
* capture to system buffer and complete request
*/
pDevInfo->SysBufInUse = 1;
if (pDevInfo->Callback.CaptureService(
pDevInfo,
pDevInfo->pSystemBuffer,
&pDevInfo->SysBufTimeStamp,
pDevInfo->ImageSize) == 0) {
// 0 return means please give me another go at this request
if (VC_ReplaceRequest(pIrp, &pDevInfo->BufferHead, VC_Cancel)) {
// nothing more to do till he gets another crack
return;
} else {
// this irp has been cancelled
pIrp->IoStatus.Status = STATUS_CANCELLED;
}
} else {
pIrp->IoStatus.Status = STATUS_SUCCESS;
}
pIrp->IoStatus.Information = 0;
}
} else {
/* unexpected irp */
pIrp->IoStatus.Status = STATUS_DRIVER_INTERNAL_ERROR;
}
IoCompleteRequest(pIrp, IO_SOUND_INCREMENT);
} else {
/*
* notify the hardware portion that there is no buffer, so
* he must throw away everything and re-arm.
*/
pDevInfo->Callback.CaptureService(pDevInfo, NULL, NULL, 0);
/*
* access to the skip count needs to be locked together
* with access to the queue of wait-error requests. This function
* increments the skip-count and completes a wait-error request
* if there is one.
*/
VC_ReportSkip(pDevInfo);
}
}