//-------------------------------------------------------------------------
 
// 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);
 
}