//------------------------------------------------------------------------------
// Function:    ShbIpcAllocBuffer
//
// Description: Allocates memory for the shared buffers
//
// Parameters:
//   ulBufferSize_p          size of the shared buffer to allocate
//   pszBufferId_p           string containing the shared buffer identifier
//   ppShbInstance_p         pointer to store the instance of this shared
//                           buffer
//   pfShbNewCreated_p       pointer to store the buffer creation flag
//
// Return:      tShbError = error
//------------------------------------------------------------------------------
tShbError ShbIpcAllocBuffer (ULONG ulBufferSize_p, const char* pszBufferID_p,
                              tShbInstance* ppShbInstance_p,
                              UINT* pfShbNewCreated_p)
{
    tShbError               ShbError;
    UINT                    uiBufferKey;
    BOOL                    fShbNewCreated;
    ULONG                   ulShMemSize;
    tShbMemHeader*          pShbMemHeader;
    tShbMemInst*            pShbMemInst = NULL;
    tShbInstance            pShbInstance;

    ulShMemSize      = ulBufferSize_p + sizeof(tShbMemHeader);

    //create Buffer Key
    uiBufferKey = ShbIpcCrc32GetCrc(pszBufferID_p);

    EPL_DBGLVL_SHB_TRACE("%s() Allocate %lu Bytes, sBufferID:%s BufferKey:%08x\n",
    		          __func__, ulShMemSize, pszBufferID_p, (key_t)uiBufferKey);
    //---------------------------------------------------------------
    // (1) open an existing or create a new shared memory
    //---------------------------------------------------------------
    // try to create shared memory

    if ((pShbMemHeader = ShbIpcFindMem(uiBufferKey)) == NULL)
    {
        fShbNewCreated = TRUE;
        if ((pShbMemHeader = ShbIpcAlloc(uiBufferKey, ulShMemSize)) == NULL)
        {
            //unable to create mem
            EPL_DBGLVL_ERROR_TRACE("%s() Shared memory allocation error!\n",
                                    __func__);
            ShbError = kShbOutOfMem;
            goto Exit;
        }
        else
        {
            EPL_DBGLVL_SHB_TRACE("%s() Shared memory allocated, Addr:%p Key:%08x size:%ld\n",
            		__func__, (void *)pShbMemHeader, uiBufferKey, ulShMemSize);
        }
    }
    else
    {
        EPL_DBGLVL_SHB_TRACE("%s() Attached to shared memory, Addr:%p Key:%08x size:%ld\n",
        		     __func__, (void *)pShbMemHeader, uiBufferKey, ulShMemSize);
        fShbNewCreated = FALSE;
    }

    //---------------------------------------------------------------
    // (2) setup or update header and management information
    //---------------------------------------------------------------

    // allocate a memory block from process specific mempool to save
    // process local information to administrate/manage the shared buffer
    if ((pShbMemInst = (tShbMemInst*)ShbIpcAllocPrivateMem(sizeof(tShbMemInst)))
    		 == NULL)
    {
        EPL_DBGLVL_ERROR_TRACE("%s() Couldn't alloc private mem!\n", __func__);
        ShbError = kShbOutOfMem;
        goto Exit;
    }

    memset(pShbMemInst, 0, sizeof(tShbMemInst));

    // reset complete header to default values
    pShbMemInst->m_SbiMagicID = SBI_MAGIC_ID;
    pShbMemInst->m_uiSharedMemId = uiBufferKey;
    strncpy(pShbMemInst->m_sBufId, pszBufferID_p, MAX_LEN_BUFFER_ID - 1);
    pShbMemInst->m_pfnSigHndlrNewData = NULL;
    pShbMemInst->m_fNewDataThreadStarted = FALSE;
    pShbMemInst->m_ulTimeOutMsJobReady = 0;
    pShbMemInst->m_pfnSigHndlrJobReady = NULL;
    pShbMemInst->m_pShbMemHeader = pShbMemHeader;
    pShbMemInst->m_fThreadTermFlag = FALSE;

    ShbError = kShbOk;

    if (fShbNewCreated)
    {
        memset (pShbMemHeader, 0, sizeof(tShbMemHeader));

        // this process was the first who wanted to use the shared memory,
        // so a new shared memory was created
        // -> setup new header information inside the shared memory region
        //    itself
        pShbMemHeader->m_ulShMemSize = ulShMemSize;
        pShbMemHeader->m_ulRefCount = 1;
        pShbMemHeader->m_uiBufferKey = uiBufferKey;

        //create semaphores for buffer access and signal new data
        if ((pShbMemHeader->m_mutexBuffAccess =
        		       semMCreate(SEM_Q_PRIORITY | SEM_INVERSION_SAFE)) == NULL)
        {
            ShbError = kShbOutOfMem;
            goto Exit;
        }

        if ((pShbMemHeader->m_semNewData = semCCreate(SEM_Q_PRIORITY, 0)) == NULL)
        {
            ShbError = kShbOutOfMem;
            goto Exit;
        }

        if ((pShbMemHeader->m_semStopSignalingNewData =
        		                         semCCreate(SEM_Q_PRIORITY, 0)) == NULL)
        {
            ShbError = kShbOutOfMem;
            goto Exit;
        }

        if ((pShbMemHeader->m_semJobReady = semCCreate(SEM_Q_PRIORITY, 0)) == NULL)
        {
            ShbError = kShbOutOfMem;
            goto Exit;
        }
    }
    else
    {
        // any other process has created the shared memory and this
        // process has only attached to it
        // -> check and update existing header information inside the
        //    shared memory region itself
        if (pShbMemHeader->m_uiBufferKey != uiBufferKey)
        {
            EPL_DBGLVL_ERROR_TRACE("%s() Shared Mem mismatch buffer key %x:%x!\n",
            		__func__, uiBufferKey, pShbMemHeader->m_uiBufferKey);
            ShbError = kShbOpenMismatch;
            goto Exit;

        }
        //TRACEX("%s() Check mem size is:%ld should be:%ld \n", __func__,
        //       pShbMemHeader->m_ulShMemSize, ulShMemSize);
        if (pShbMemHeader->m_ulShMemSize != ulShMemSize)
        {
            EPL_DBGLVL_ERROR_TRACE("%s() Shared Mem mismatch size! %ld:%ld\n",
            		      __func__, ulShMemSize, pShbMemHeader->m_ulShMemSize);
            ShbError = kShbOpenMismatch;
            goto Exit;
        }
        pShbMemHeader->m_ulRefCount++;
    }

Exit:
    if (ShbError != kShbOk)
    {
        EPL_DBGLVL_ERROR_TRACE("%s() allocating shared buf failed!\n (%d)",
        		                __func__, ShbError);
        if (pShbMemInst != NULL)
        {
            ShbIpcReleasePrivateMem (pShbMemInst);
        }
        if ((pShbMemHeader != NULL) && (fShbNewCreated))
        {
            ShbIpcFree(uiBufferKey);
        }
    }

    pShbInstance = (tShbInstance*)pShbMemInst;
    *pfShbNewCreated_p = fShbNewCreated;
    *ppShbInstance_p   = pShbInstance;

    return (ShbError);
}
tShbError  ShbIpcAllocBuffer (
    unsigned long ulBufferSize_p,
    const char* pszBufferID_p,
    tShbInstance* ppShbInstance_p,
    unsigned int* pfShbNewCreated_p)
{
tShbError               ShbError;
int                     iBufferId=0;
unsigned long           ulCrc32=0;
unsigned int            uiFirstProcess=0;
unsigned long           ulShMemSize;
tShbMemHeader*          pShbMemHeader;
tShbMemInst*            pShbMemInst=NULL;
tShbInstance            pShbInstance;
unsigned int            fShMemNewCreated=FALSE;
void                    *pSharedMem=NULL;
struct sShbMemTable     *psMemTableElement;


    DEBUG_LVL_29_TRACE0("ShbIpcAllocBuffer \n");
    ulShMemSize      = ulBufferSize_p + sizeof(tShbMemHeader);

    //create Buffer ID
#ifndef CONFIG_CRC32
    {
        unsigned long           aulCrcTable[256];
        ShbIpcCrc32GenTable(aulCrcTable);
        ulCrc32 = ShbIpcCrc32GetCrc(pszBufferID_p, aulCrcTable);
    }
#else
    ulCrc32 = crc32(0xFFFFFFFF, pszBufferID_p, strlen(pszBufferID_p));
#endif

    iBufferId=ulCrc32;
    DEBUG_LVL_29_TRACE2("ShbIpcAllocBuffer BufferSize:%d sizeof(tShb..):%d\n",ulBufferSize_p,sizeof(tShbMemHeader));
    DEBUG_LVL_29_TRACE2("ShbIpcAllocBuffer BufferId:%d MemSize:%d\n",iBufferId,ulShMemSize);
    //---------------------------------------------------------------
    // (1) open an existing or create a new shared memory
    //---------------------------------------------------------------
    //test if buffer already exists
    if (ShbIpcFindListElement(iBufferId, &psMemTableElement) == 0)
    {
        //Buffer already exists
        fShMemNewCreated=FALSE;
        pSharedMem = psMemTableElement->m_pBuffer;
        DEBUG_LVL_29_TRACE1("ShbIpcAllocBuffer attach Buffer at:%p Id:%d\n",pSharedMem);
        uiFirstProcess=1;
    }
    else
    {
        //create new Buffer
        fShMemNewCreated = TRUE;
        uiFirstProcess=0;
        pSharedMem = kmalloc(ulShMemSize,GFP_KERNEL);
        DEBUG_LVL_29_TRACE2("ShbIpcAllocBuffer Create New Buffer at:%p Id:%d\n",pSharedMem,iBufferId);
        if (pSharedMem == NULL)
        {
            //unable to create mem
            ShbError = kShbOutOfMem;
            goto Exit;
        }
        DEBUG_LVL_29_TRACE0("ShbIpcAllocBuffer create semas\n");
        // append Element to Mem Table
        psMemTableElement = kmalloc(sizeof(struct sShbMemTable),GFP_KERNEL);
        psMemTableElement->m_iBufferId = iBufferId;
        psMemTableElement->m_pBuffer = pSharedMem;
        psMemTableElement->m_psNextMemTableElement = NULL;
        ShbIpcAppendListElement (psMemTableElement);
    }

    DEBUG_LVL_29_TRACE0("ShbIpcAllocBuffer update header\n");
    //update header
    pShbMemHeader = (tShbMemHeader*)pSharedMem;
    DEBUG_LVL_29_TRACE1("ShbIpcAllocBuffer 0 pShbMemHeader->m_ulShMemSize: %d\n",pShbMemHeader->m_ulShMemSize);
    // allocate a memory block from process specific mempool to save
    // process local information to administrate/manage the shared buffer
    DEBUG_LVL_29_TRACE0("ShbIpcAllocBuffer alloc private mem\n");
    pShbMemInst = (tShbMemInst*) ShbIpcAllocPrivateMem (sizeof(tShbMemInst));
    if (pShbMemInst == NULL)
    {
        ShbError = kShbOutOfMem;
        goto Exit;
    }

    // reset complete header to default values
    //pShbMemInst->m_SbiMagicID                             = SBI_MAGIC_ID;
//    pShbMemInst->m_pSharedMem                               = pSharedMem;
    pShbMemInst->m_tThreadNewDataId                         = INVALID_ID;
    pShbMemInst->m_tThreadJobReadyId                        = INVALID_ID;
    pShbMemInst->m_pfnSigHndlrNewData                       = NULL;
    pShbMemInst->m_ulTimeOutMsJobReady                      = 0;
    pShbMemInst->m_pfnSigHndlrJobReady                      = NULL;
    sema_init(&pShbMemInst->m_SemaphoreStopThreadJobReady, 1);
    pShbMemInst->m_pShbMemHeader                            = pShbMemHeader;

    ShbError         = kShbOk;
    if ( fShMemNewCreated )
    {
        // this process was the first who wanted to use the shared memory,
        // so a new shared memory was created
        // -> setup new header information inside the shared memory region
        //    itself
        pShbMemHeader->m_ulShMemSize = ulShMemSize;
        pShbMemHeader->m_ulRefCount  = 1;
        pShbMemHeader->m_iBufferId=iBufferId;
        pShbMemHeader->m_pShbInstMaster = NULL;
        // initialize spinlock
        spin_lock_init(&pShbMemHeader->m_SpinlockBuffAccess);
        // initialize wait queues
        init_waitqueue_head(&pShbMemHeader->m_WaitQueueNewData);
        init_waitqueue_head(&pShbMemHeader->m_WaitQueueJobReady);
    }
    else
    {
        // any other process has created the shared memory and this
        // process only has to attach to it
        // -> check and update existing header information inside the
        //    shared memory region itself
        if (pShbMemHeader->m_ulShMemSize != ulShMemSize)
        {
            ShbError = kShbOpenMismatch;
            goto Exit;
        }
        pShbMemHeader->m_ulRefCount++;
    }

    Exit:
    pShbInstance = (tShbInstance*)pShbMemInst;
    *pfShbNewCreated_p = fShMemNewCreated;
    *ppShbInstance_p   = pShbInstance;
    return (ShbError);

}
tShbError  ShbIpcAllocBuffer (
    unsigned long ulBufferSize_p,
    const char* pszBufferID_p,
    tShbInstance* ppShbInstance_p,
    unsigned int* pfShbNewCreated_p)
{

HANDLE          hSharedMem;
LPVOID          pSharedMem;
unsigned long   ulShMemSize;
tShbMemInst*    pShbMemInst = NULL;
tShbMemHeader*  pShbMemHeader;
tShbInstance    pShbInstance;
unsigned int    fShMemNewCreated;
const char*     pszObjectName;
HANDLE          hMutexBuffAccess;
HANDLE          hEventNewData;
HANDLE          hEventJobReady;
tShbError       ShbError;


    ulShMemSize      = ulBufferSize_p + sizeof(tShbMemHeader);
    pSharedMem       = NULL;
    pShbInstance     = NULL;
    fShMemNewCreated = FALSE;
    ShbError         = kShbOk;


    //---------------------------------------------------------------
    // (1) open an existing or create a new shared memory
    //---------------------------------------------------------------
    // try to open an already existing shared memory
    // (created by an another process)
    hSharedMem = OpenFileMapping (FILE_MAP_ALL_ACCESS,      // DWORD dwDesiredAccess
                                  FALSE,                    // BOOL bInheritHandle
                                  pszBufferID_p);           // LPCTSTR lpName
    if (hSharedMem != NULL)
    {
        // a shared memory already exists
        fShMemNewCreated = FALSE;
    }
    else
    {
        // it seams that this process is the first who wants to use the
        // shared memory, so it has to create a new shared memory
        hSharedMem = CreateFileMapping(INVALID_HANDLE_VALUE,// HANDLE hFile
                                       NULL,                // LPSECURITY_ATTRIBUTES lpAttributes
                                       PAGE_READWRITE,      // DWORD flProtect
                                       0,                   // DWORD dwMaximumSizeHigh
                                       ulShMemSize,         // DWORD dwMaximumSizeLow
                                       pszBufferID_p);      // LPCTSTR lpName

        fShMemNewCreated = TRUE;
    }

    if (hSharedMem == NULL)
    {
        ShbError = kShbOutOfMem;
        goto Exit;
    }


    //---------------------------------------------------------------
    // (2) get the pointer to the shared memory
    //---------------------------------------------------------------
    pSharedMem = MapViewOfFile (hSharedMem,                 // HANDLE hFileMappingObject
                                FILE_MAP_ALL_ACCESS,        // DWORD dwDesiredAccess,
                                0,                          // DWORD dwFileOffsetHigh,
                                0,                          // DWORD dwFileOffsetLow,
                                ulShMemSize);               // SIZE_T dwNumberOfBytesToMap

    if (pSharedMem == NULL)
    {
        ShbError = kShbOutOfMem;
        goto Exit;
    }


    //---------------------------------------------------------------
    // (3) setup or update header and management information
    //---------------------------------------------------------------
    pShbMemHeader = (tShbMemHeader*)pSharedMem;

    // allocate a memory block from process specific mempool to save
    // process local information to administrate/manage the shared buffer
    pShbMemInst = (tShbMemInst*) ShbIpcAllocPrivateMem (sizeof(tShbMemInst));
    if (pShbMemInst == NULL)
    {
        ShbError = kShbOutOfMem;
        goto Exit;
    }

    // reset complete header to default values
    pShbMemInst->m_SbiMagicID                          = SBI_MAGIC_ID;
    pShbMemInst->m_hSharedMem                          = hSharedMem;
    pShbMemInst->m_hMutexBuffAccess                    = INVALID_HANDLE_VALUE;
    pShbMemInst->m_hThreadNewData                      = INVALID_HANDLE_VALUE;
    pShbMemInst->m_ahEventNewData[IDX_EVENT_NEW_DATA]  = INVALID_HANDLE_VALUE;
    pShbMemInst->m_ahEventNewData[IDX_EVENT_TERM_REQU] = INVALID_HANDLE_VALUE;
    pShbMemInst->m_ahEventNewData[IDX_EVENT_TERM_RESP] = INVALID_HANDLE_VALUE;
    pShbMemInst->m_pfnSigHndlrNewData                  = NULL;
    pShbMemInst->m_hThreadJobReady                     = INVALID_HANDLE_VALUE;
    pShbMemInst->m_hEventJobReady                      = INVALID_HANDLE_VALUE;
    pShbMemInst->m_ulTimeOutMsJobReady                 = 0;
    pShbMemInst->m_pfnSigHndlrJobReady                 = NULL;
    pShbMemInst->m_pShbMemHeader                       = pShbMemHeader;

    #ifndef NDEBUG
    {
        pShbMemInst->m_ulThreadIDNewData  = 0;
        pShbMemInst->m_ulThreadIDJobReady = 0;
    }
    #endif

    // create mutex for buffer access
    pszObjectName = ShbIpcGetUniformObjectName (NAME_MUTEX_BUFF_ACCESS, pszBufferID_p, TRUE);
    hMutexBuffAccess = CreateMutex (NULL,                   // LPSECURITY_ATTRIBUTES lpMutexAttributes
                                    FALSE,                  // BOOL bInitialOwner
                                    pszObjectName);         // LPCTSTR lpName
    pShbMemInst->m_hMutexBuffAccess = hMutexBuffAccess;
    ASSERT(pShbMemInst->m_hMutexBuffAccess != NULL);

    // The EventNewData is used for signaling of new data after a write
    // operation (SetEvent) as well as for waiting for new data on the
    // reader side (WaitForMultipleObjects). Because it's not known if
    // this process will be read or write data, the event will be
    // always created here.
    pszObjectName = ShbIpcGetUniformObjectName (NAME_EVENT_NEW_DATA, pszBufferID_p, TRUE);
    hEventNewData = CreateEvent (NULL,                      // LPSECURITY_ATTRIBUTES lpEventAttributes
                                 FALSE,                     // BOOL bManualReset
                                 FALSE,                     // BOOL bInitialState
                                 pszObjectName);            // LPCTSTR lpName
    pShbMemInst->m_ahEventNewData[IDX_EVENT_NEW_DATA] = hEventNewData;
    ASSERT(pShbMemInst->m_ahEventNewData[IDX_EVENT_NEW_DATA] != NULL);

    // The EventJobReady is used for signaling that a job is done (SetEvent)
    // as well as for waiting for finishing of a job (WaitForMultipleObjects).
    // Because it's not known if this process will signal or wait, the event
    // will be always created here.
    pszObjectName = ShbIpcGetUniformObjectName (NAME_EVENT_JOB_READY, pszBufferID_p, TRUE);
    hEventJobReady = CreateEvent (NULL,                     // LPSECURITY_ATTRIBUTES lpEventAttributes
                                  FALSE,                    // BOOL bManualReset
                                  FALSE,                    // BOOL bInitialState
                                  pszObjectName);           // LPCTSTR lpName
    pShbMemInst->m_hEventJobReady = hEventJobReady;
    ASSERT(pShbMemInst->m_hEventJobReady != NULL);

    if ( fShMemNewCreated )
    {
        // this process was the first who wanted to use the shared memory,
        // so a new shared memory was created
        // -> setup new header information inside the shared memory region
        //    itself
        pShbMemHeader->m_SbhMagicID  = SBH_MAGIC_ID;
        pShbMemHeader->m_ulShMemSize = ulShMemSize;
        pShbMemHeader->m_ulRefCount  = 1;
        pShbMemHeader->m_pShbInstMaster = NULL;
        strcpy_s (pShbMemHeader->m_szBufferID, sizeof(pShbMemHeader->m_szBufferID), pszBufferID_p);

        #ifndef NDEBUG
        {
            pShbMemHeader->m_ulOwnerProcID = GetCurrentProcessId();
        }
        #endif
    }
    else
    {
        // any other process has created the shared memory and this
        // process has only attached to it
        // -> check and update existing header information inside the
        //    shared memory region itself
        if (pShbMemHeader->m_ulShMemSize != ulShMemSize)
        {
            ShbError = kShbOpenMismatch;
            goto Exit;
        }

        #ifndef NDEBUG
        {
            if ( strncmp(pShbMemHeader->m_szBufferID, pszBufferID_p, sizeof(pShbMemHeader->m_szBufferID)-1) )
            {
                ShbError = kShbOpenMismatch;
                goto Exit;
            }
        }
        #endif

        pShbMemHeader->m_ulRefCount++;
    }


    // set abstarct "handle" for returning to application
    pShbInstance = (tShbInstance*)pShbMemInst;


Exit:

    if (ShbError != kShbOk)
    {
        if (pShbMemInst != NULL)
        {
            ShbIpcReleasePrivateMem (pShbMemInst);
        }
        if (pSharedMem != NULL)
        {
            UnmapViewOfFile (pSharedMem);
        }
        if (hSharedMem != NULL)
        {
            CloseHandle (hSharedMem);
        }
    }

    *pfShbNewCreated_p = fShMemNewCreated;
    *ppShbInstance_p   = pShbInstance;

    return (ShbError);

}