//-------------------------------------------------------------------------
// WINNT driver for PCIVME interface from ARW Elektronik, Germany ---------
// functions for a fast fifo (first in first out) implementation
//
// (c) 1999-2004 ARW Elektronik
//
// this source code is published under GPL (Open Source). You can use, redistrubute and
// modify it unless this header is not modified or deleted. No warranty is given that
// this software will work like expected.
// This product is not authorized for use as critical component in life support systems
// wihout the express written approval of ARW Elektronik Germany.
//
// Please announce changes and hints to ARW Elektronik
//
// $Log: pcivme_fifo.c,v $
// Revision 1.3 2004/07/24 07:07:26 klaus
// Update copyright to 2004
//
// Revision 1.2 2003/11/15 19:12:50 klaus
// Update copyright to 2003
//
// Revision 1.1.1.1 2003/11/14 23:16:33 klaus
// First put into repository
//
// Revision 1.3 2002/10/27 16:17:48 klaus
// Typing bug fixed caused at log addition
//
// Revision 1.2 2002/10/27 16:11:02 klaus
// Added CVS log into header
//
// what who when
// started AR 06.10.1999
// first release 1.0 AR 17.10.1999
//
//-------------------------------------------------------------------------
// INCLUDES
//
#include <ntddk.h>
#include <pcivme_fifo.h>
//------------------------------------------------------------------------
// DEFINES
//
//------------------------------------------------------------------------
// TYPEDEFS
//
typedef struct
{
USHORT wLevel; // actual Number of Elements
UCHAR *pbBegin; // points to begin of the buffer
UCHAR *pbEnd; // points to the (end of the buffer + 1)
UCHAR *pbWrite; // next write here
UCHAR *pbRead; // next read (if any) here
KSPIN_LOCK Lock; // to guard access to the fifo
BOOLEAN bOverflow; // indicates a overflow to that FIFO
} FIFO_MANAGER;
//------------------------------------------------------------------------
// FUNCTIONS
//
// the global functions to manage the fifo
// first: init the fifo and allocate nonpaged memory
NTSTATUS InitializeFIFO(USHORT CountOfElements, PVOID *pHandle)
{
ULONG allocSize;
register FIFO_MANAGER *fifo_manager;
*pHandle = NULL;
allocSize = sizeof(FIFO_MANAGER) + sizeof(UCHAR) * CountOfElements + 16; // cause alignment
*pHandle = ExAllocatePool(NonPagedPool, allocSize);
if(*pHandle == NULL) return STATUS_NO_MEMORY;
RtlZeroMemory(*pHandle, allocSize);
fifo_manager = (FIFO_MANAGER *)*pHandle;
// init the fifo_manager
KeInitializeSpinLock(&fifo_manager->Lock);
fifo_manager->wLevel = 0;
fifo_manager->bOverflow = FALSE;
fifo_manager->pbBegin = (UCHAR *)(((ULONG)(fifo_manager + 1) + 8) & ~7); //align
fifo_manager->pbEnd = (UCHAR *)((ULONG)fifo_manager->pbBegin + sizeof(UCHAR) * CountOfElements);
fifo_manager->pbWrite =
fifo_manager->pbRead = fifo_manager->pbBegin;
// KdPrint(("fifo_manager: 0x%08x, begin: 0x%08x, end: 0x%08x\n",
// fifo_manager, fifo_manager->pbBegin, fifo_manager->pbEnd));
return STATUS_SUCCESS;
}
// second: push elements to the FIFO
int PushElement(PVOID Handle, UCHAR bElement)
{
register FIFO_MANAGER *fifo_manager = (FIFO_MANAGER *)Handle;
KIRQL oldIrql;
KeAcquireSpinLock(&fifo_manager->Lock, &oldIrql);
if ((fifo_manager->wLevel) && (fifo_manager->pbWrite == fifo_manager->pbRead))
{
KeReleaseSpinLock(&fifo_manager->Lock, oldIrql);
return fifo_manager->wLevel;
}
*(fifo_manager->pbWrite)++ = bElement;
// wrap around
if (fifo_manager->pbWrite >= fifo_manager->pbEnd)
fifo_manager->pbWrite = fifo_manager->pbBegin;
// check for overflow - indicate - and reset pointer to same before
if (fifo_manager->pbWrite == fifo_manager->pbRead)
{
fifo_manager->bOverflow = TRUE;
fifo_manager->pbWrite--;
if (fifo_manager->pbWrite < fifo_manager->pbBegin)
fifo_manager->pbWrite = fifo_manager->pbEnd - 1;
}
else
(fifo_manager->wLevel)++;
KeReleaseSpinLock(&fifo_manager->Lock, oldIrql);
return fifo_manager->wLevel;
}
// third: pull elements from the FIFO
int PullElement(PVOID Handle, UCHAR *pbElement)
{
register FIFO_MANAGER *fifo_manager = (FIFO_MANAGER *)Handle;
KIRQL oldIrql;
if (!(fifo_manager->wLevel)) return 0;
KeAcquireSpinLock(&fifo_manager->Lock, &oldIrql);
if (fifo_manager->wLevel)
{
*pbElement = *(fifo_manager->pbRead)++;
(fifo_manager->wLevel)--;
}
else
*pbElement = 0; // the caller tries to get more than available?
// wrap around
if (fifo_manager->pbRead >= fifo_manager->pbEnd)
fifo_manager->pbRead = fifo_manager->pbBegin;
KeReleaseSpinLock(&fifo_manager->Lock, oldIrql);
return fifo_manager->wLevel;
}
// test how many elements are in the FIFO
int NumberOfElements(PVOID Handle)
{
register FIFO_MANAGER *fifo_manager = (FIFO_MANAGER *)Handle;
return fifo_manager->wLevel;
}
// check and clear the overflow flag
BOOLEAN CheckAndClearOverflow(PVOID Handle)
{
register FIFO_MANAGER *fifo_manager = (FIFO_MANAGER *)Handle;
KIRQL oldIrql;
BOOLEAN helper;
KeAcquireSpinLock(&fifo_manager->Lock, &oldIrql);
helper = fifo_manager->bOverflow;
fifo_manager->bOverflow = FALSE;
KeReleaseSpinLock(&fifo_manager->Lock, oldIrql);
return helper;
}
// close the FIFO and free the allocated memory
void DestroyFIFO(PVOID Handle)
{
if (Handle != NULL) ExFreePool(Handle);
}