//---------------------------------------------------------------------------
// Function:    EplTimerHighReskAddInstance()
//
// Description: initializes the high resolution timer module.
//
// Parameters:  void
//
// Return:      tEplKernel      = error code
//---------------------------------------------------------------------------
tEplKernel PUBLIC EplTimerHighReskAddInstance(void)
{
    tEplKernel                   Ret;
    UINT                         uiIndex;
    struct sched_param           schedParam;
    tEplTimerHighReskTimerInfo*  pTimerInfo;

    Ret = kEplSuccessful;

    EPL_MEMSET(&EplTimerHighReskInstance_l, 0, sizeof (EplTimerHighReskInstance_l));

    /* Initialize timer threads for all usable timers. */
    for (uiIndex = 0; uiIndex < TIMER_COUNT; uiIndex++)
    {
        pTimerInfo = &EplTimerHighReskInstance_l.m_aTimerInfo[uiIndex];
        pTimerInfo->m_fTerminate = FALSE;

#ifdef HIGH_RESK_TIMER_LATENCY_DEBUG
        pTimerInfo->m_maxLatency = 0;
        pTimerInfo->m_minLatency = 999999999;
#endif

        if (sem_init(&pTimerInfo->m_syncSem, 0, 0) != 0)
        {
            EPL_DBGLVL_ERROR_TRACE("%s() Couldn't init semaphore!\n", __func__);
            Ret = kEplNoResource;
            goto Exit;
        }

        if (pthread_create(&pTimerInfo->m_timerThread, NULL, EplTimerHighReskProcessThread,
                           (void *)uiIndex) != 0)
        {
            Ret = kEplNoResource;
            sem_destroy(&pTimerInfo->m_syncSem);
            goto Exit;
        }

        schedParam.__sched_priority = EPL_THREAD_PRIORITY_HIGH;
        if (pthread_setschedparam(pTimerInfo->m_timerThread, SCHED_FIFO, &schedParam) != 0)
        {
            EPL_DBGLVL_ERROR_TRACE("%s() Couldn't set thread scheduling parameters!\n", __func__);
            Ret = kEplNoResource;
            sem_destroy(&pTimerInfo->m_syncSem);
            pthread_cancel(pTimerInfo->m_timerThread);
            goto Exit;
        }
    }

Exit:
    return Ret;
}
//------------------------------------------------------------------------------
// Function:    ShbIpcSignalNewData
//
// Description: Signal new data (called from writing process)
//
// Parameters:  pShbInstance_p        pointer to shared buffer instance
//
// Return:      tShbError      = error code
//------------------------------------------------------------------------------
tShbError ShbIpcSignalNewData(tShbInstance pShbInstance_p)
{
    tShbMemInst*        pShbMemInst;
    tShbMemHeader*      pShbMemHeader;
    tShbError           ShbError;

    //TRACEX("%s() pShbInstance_p=%p\n", __func__, pShbInstance_p);

    if (pShbInstance_p == NULL)
    {
        //TRACEX("%s() Invalid instance!\n", __func__);
        return (kShbInvalidArg);
    }
    pShbMemInst = ShbIpcGetShbMemInst (pShbInstance_p);
    pShbMemHeader = ShbIpcGetShbMemHeader (pShbMemInst);

    ShbError = kShbOk;

    //set semaphore
    if (semGive(pShbMemHeader->m_semNewData) < 0)
    {
        EPL_DBGLVL_ERROR_TRACE("%s() sem_post failed! (%s)\n", __func__,
        		                strerror(errno));
    }

    /* if this shared buffer is connected to a master buffer, signal the
     * data also to the master buffer.
     */
    if (pShbMemHeader->m_pShbInstMaster != NULL)
    {
        return ShbIpcSignalNewData(pShbMemHeader->m_pShbInstMaster);
    }

    return ShbError;
}
//------------------------------------------------------------------------------
// Function:    ShbIpcStartSignalingNewData
//
// Description: Start signaling of new data (called from reading process)
//
// Parameters:  pShbInstance_p                  pointer to shared buffer instance
//              pfnSignalHandlerNewData_p       pointer to master buffer instance
//
// Return:      tShbError      = error code
//------------------------------------------------------------------------------
tShbError  ShbIpcStartSignalingNewData(tShbInstance pShbInstance_p,
                                       tSigHndlrNewData pfnSignalHandlerNewData_p,
                                       tShbPriority ShbPriority_p)
{
    tShbMemInst*        pShbMemInst;
    tShbMemHeader*      pShbMemHeader;
    tShbError           ShbError;
    INT                 iSchedPriority;

    if ((pShbInstance_p == NULL) || (pfnSignalHandlerNewData_p == NULL))
    {
        return (kShbInvalidArg);
    }

    pShbMemInst   = ShbIpcGetShbMemInst   (pShbInstance_p);
    pShbMemHeader = ShbIpcGetShbMemHeader (pShbInstance_p);
    ShbError = kShbOk;

    if ((pShbMemInst->m_fNewDataThreadStarted) ||
        (pShbMemInst->m_pfnSigHndlrNewData != NULL))
    {
        ShbError = kShbAlreadySignaling;
        EPL_DBGLVL_ERROR_TRACE("%s() Thread already started!\n", __func__);
        goto Exit;
    }

    pShbMemInst->m_pfnSigHndlrNewData = pfnSignalHandlerNewData_p;

    iSchedPriority = EPL_TASK_PRIORITY_SHB;
    switch (ShbPriority_p)
    {
        case kShbPriorityLow:
            iSchedPriority += 5;
            break;

        case kShbPriorityNormal:
            iSchedPriority += 0;
            break;

        case kShbPriorityHigh:
            iSchedPriority -= 5;
            break;
    }

    if ((pShbMemInst->m_tThreadNewDataId =
    		taskSpawn ("tShbIpc", iSchedPriority,
                       0, EPL_TASK_STACK_SIZE, &ShbIpcThreadSignalNewData,
                       (int)pShbInstance_p, 0, 0, 0, 0, 0, 0, 0, 0, 0)) == ERROR)
    {
        pShbMemInst->m_pfnSigHndlrNewData = NULL;
        ShbError = kShbInvalidSigHndlr;
        goto Exit;
    }

Exit:
    return (ShbError);
}
//------------------------------------------------------------------------------
// Function:    ShbIpcStartSignalingJobReady
//
// Description: Start signaling for job ready (called from waiting process)
//
// Parameters:
//      pShbInstance_p                  pointer to shared buffer instance
//      ulTimeOut_p                     job ready timeout
//      pfnSignalHandlerJobReady_p      function pointer to callback function
//
// Return:      tShbError      = error code
//------------------------------------------------------------------------------
tShbError ShbIpcStartSignalingJobReady(tShbInstance pShbInstance_p,
                                       unsigned long ulTimeOut_p,
                                       tSigHndlrJobReady pfnSignalHandlerJobReady_p)
{
    tShbMemInst*    pShbMemInst;
    tShbMemHeader*  pShbMemHeader;
    tShbError       ShbError;
    struct sched_param  schedParam;

    if ((pShbInstance_p == NULL) || (pfnSignalHandlerJobReady_p == NULL))
    {
        return (kShbInvalidArg);
    }

    pShbMemInst   = ShbIpcGetShbMemInst   (pShbInstance_p);
    pShbMemHeader = ShbIpcGetShbMemHeader (pShbInstance_p);
    ShbError = kShbOk;

    if ((pShbMemInst->m_fJobReadyThreadStarted) ||
        (pShbMemInst->m_pfnSigHndlrJobReady != NULL))
    {
        ShbError = kShbAlreadySignaling;
        goto Exit;
    }

    pShbMemInst->m_ulTimeOutMsJobReady = ulTimeOut_p;
    pShbMemInst->m_pfnSigHndlrJobReady = pfnSignalHandlerJobReady_p;

    //create thread for job ready signaling
    if (pthread_create(&pShbMemInst->m_tThreadJobReadyId, NULL,
                   &ShbIpcThreadSignalJobReady, pShbInstance_p) != 0)
    {
        pShbMemInst->m_pfnSigHndlrJobReady = NULL;
        ShbError = kShbInvalidSigHndlr;
        goto Exit;
    }

    schedParam.__sched_priority = EPL_THREAD_PRIORITY_LOW;
    if (pthread_setschedparam(pShbMemInst->m_tThreadNewDataId, SCHED_FIFO,
                              &schedParam) != 0)
    {
        EPL_DBGLVL_ERROR_TRACE("%s(): couldn't set thread scheduling parameters! %d\n",
               __func__, schedParam.__sched_priority);
    }

Exit:

    return ShbError;
}
//------------------------------------------------------------------------------
// Function:    ShbIpcStopSignalingNewData
//
// Description: Stop signaling of new data (called from reading process)
//
// Parameters:  pShbInstance_p        pointer to shared buffer instance
//
// Return:      tShbError      = error code
//------------------------------------------------------------------------------
tShbError ShbIpcStopSignalingNewData(tShbInstance pShbInstance_p)
{
    tShbMemInst         *pShbMemInst;
    tShbMemHeader       *pShbMemHeader;
    tShbError           ShbError;
    INT                 iRetVal = -1;
    sem_t*              pSemStopSignalingNewData;
    sem_t*              pSemNewData;
    struct timespec     curTime, timeout;

    if (pShbInstance_p == NULL)
    {
        return (kShbInvalidArg);
    }

    pShbMemHeader = ShbIpcGetShbMemHeader (pShbInstance_p);
    pShbMemInst = ShbIpcGetShbMemInst (pShbInstance_p);

    pSemNewData = &pShbMemHeader->m_semNewData;
    pSemStopSignalingNewData = &pShbMemHeader->m_semStopSignalingNewData;
    ShbError = kShbOk;

    if (!pShbMemInst->m_fNewDataThreadStarted)
    {
        ShbError = kShbBufferAlreadyCompleted;
        goto Exit;
    }

    //set termination flag and signal new data to terminate thread
    pShbMemInst->m_fThreadTermFlag = TRUE;
    sem_post(pSemNewData);

    // waiting for thread to terminate
    clock_gettime(CLOCK_REALTIME, &curTime);
    timeout.tv_sec = 1;
    timeout.tv_nsec = TIMEOUT_WAITING_THREAD * 1000;
    timespecadd(&timeout, &curTime);
    iRetVal = sem_timedwait(pSemStopSignalingNewData, &timeout);
    if (iRetVal != 0)
    {
        EPL_DBGLVL_ERROR_TRACE("%s() Stop Sem TIMEOUT %d (%s)\n", __func__, iRetVal, strerror(errno));
    }

Exit:

    return (ShbError);
}
//------------------------------------------------------------------------------
// Function:    ShbIpcStopSignalingNewData
//
// Description: Stop signaling of new data (called from reading process)
//
// Parameters:  pShbInstance_p        pointer to shared buffer instance
//
// Return:      tShbError      = error code
//------------------------------------------------------------------------------
tShbError ShbIpcStopSignalingNewData(tShbInstance pShbInstance_p)
{
    tShbMemInst         *pShbMemInst;
    tShbMemHeader       *pShbMemHeader;
    tShbError           ShbError;
    INT                 iRetVal = -1;

    if (pShbInstance_p == NULL)
    {
        return (kShbInvalidArg);
    }

    pShbMemHeader = ShbIpcGetShbMemHeader (pShbInstance_p);
    pShbMemInst = ShbIpcGetShbMemInst (pShbInstance_p);

    ShbError = kShbOk;

    if (!pShbMemInst->m_fNewDataThreadStarted)
    {
        ShbError = kShbBufferAlreadyCompleted;
        goto Exit;
    }

    //set termination flag and signal new data to terminate thread
    pShbMemInst->m_fThreadTermFlag = TRUE;
    semGive(pShbMemHeader->m_semNewData);

    iRetVal = semTake(pShbMemHeader->m_semStopSignalingNewData,
    		          TIMEOUT_WAITING_THREAD);
    if (iRetVal == ERROR)
    {
        EPL_DBGLVL_ERROR_TRACE("%s() Stop Sem TIMEOUT %d (%s)\n", __func__,
        		                iRetVal, strerror(errno));
    }

Exit:

    return (ShbError);
}
//------------------------------------------------------------------------------
// 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);
}
//---------------------------------------------------------------------------
// Function:    EplTimerHighReskProcessThread()
//
// Description: Main function of the high-resolution timer thread.
//
//              EplTimerHighReskProcessThread() waits for a timer start
//              signal. When it is received it reads the high-resolution
//              timer information structure and calculates the timeout value.
//              It sleeps by calling clock_nanosleep() until the timeout is
//              reached. When the timeout is reached the callback function
//              registered in the timer info structure is called. If the
//              flag m_fContinuously is set the thread loops until the
//              timer is deleted.
//
// Parameters:  pArgument_p *   = pointer to timer info structure
//
// Return:      void *          = return value is specified by the pthread
//                                interface but is not used!
//---------------------------------------------------------------------------
static void * EplTimerHighReskProcessThread(void *pArgument_p)
{
    INT                                 iRet;
    tEplTimerHighReskTimerInfo          *pTimerInfo;
    struct timespec                     startTime, curTime, timeout;
    ULONGLONG                           ullPeriod;
    tEplTimerHdl                        timerHdl;
#ifdef HIGH_RESK_TIMER_LATENCY_DEBUG
    struct timespec                     debugtime;
#endif

    EPL_DBGLVL_TIMERH_TRACE("%s(): ThreadId:%ld\n", __func__, syscall(SYS_gettid));
    EPL_DBGLVL_TIMERH_TRACE("%s(): timer:%lx\n", __func__, (unsigned long)pArgument_p);

    /* thread parameter contains the address of the timer information structure */
    pTimerInfo = &EplTimerHighReskInstance_l.m_aTimerInfo[(int)pArgument_p];

    /* loop forever until thread will be canceled */
    while (1)
    {
        /* wait for semaphore which signals a timer start */
        sem_wait(&pTimerInfo->m_syncSem);

        /* check if thread should terminate */
        if (pTimerInfo->m_fTerminate)
        {
            EPL_DBGLVL_TIMERH_TRACE("%s() Exiting signal received!\n", __func__);
            break;
        }
        else
        {
            /* save timer information into local variables */
            startTime = pTimerInfo->m_startTime;
            timerHdl = pTimerInfo->m_EventArg.m_TimerHdl;
            ullPeriod = pTimerInfo->m_ullTime;

            /* calculate the timeout value for the timer cycle */
            timespec_add(&startTime, ullPeriod, &timeout);

#ifdef HIGH_RESK_TIMER_LATENCY_DEBUG
            clock_gettime(CLOCK_MONOTONIC, &curTime);
#endif
            do
            {
                /* sleep until timeout */
                iRet = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &timeout, NULL);
                if (iRet < 0)
                {
                    EPL_DBGLVL_ERROR_TRACE("%s(): Error in clock_nanosleep!\n",
                                            __func__);
                    /* todo how to signal that timeout wasn't correct? */
                }
                FTRACE_MARKER("HighReskTimer(%d) expired (%d ns)",
                              (int)pArgument_p, ullPeriod);

#ifdef HIGH_RESK_TIMER_LATENCY_DEBUG
                clock_gettime(CLOCK_MONOTONIC, &curTime);
                timespec_sub(&timeout, &curTime, &debugtime);
                FTRACE_MARKER("%s latency=%ld:%ld", __func__, debugtime.tv_sec,
                              debugtime.tv_nsec);
                if (debugtime.tv_nsec > pTimerInfo->m_maxLatency)
                {
                    EPL_DBGLVL_TIMERH_TRACE("%s() Timer elapsed: max latency=%ld ns\n",
                                             __func__, debugtime.tv_nsec);
                    pTimerInfo->m_maxLatency = debugtime.tv_nsec;
                }
                if (timeout.tv_nsec < pTimerInfo->m_minLatency)
                {
                    EPL_DBGLVL_TIMERH_TRACE("%s() Timer elapsed: min latency=%ld ns\n",
                                             __func__, debugtime.tv_nsec);
                    pTimerInfo->m_minLatency = debugtime.tv_nsec;
                }
#endif

                /* check if timer handle is valid */
                if (timerHdl == pTimerInfo->m_EventArg.m_TimerHdl)
                {
                    /* call callback function */
                    if (pTimerInfo->m_pfnCallback != NULL)
                    {
                        pTimerInfo->m_pfnCallback(&pTimerInfo->m_EventArg);
                    }
                }

                /* check if timer handle is still valid. Could be modified in callback! */
                if (timerHdl == pTimerInfo->m_EventArg.m_TimerHdl)
                {
                    if (pTimerInfo->m_fContinuously)
                    {
                        /* calculate timeout value for next timer cycle */
                        timespec_add(&timeout, ullPeriod, &timeout);
                    }
                }
            } while ((pTimerInfo->m_fContinuously) &&
                     (timerHdl == pTimerInfo->m_EventArg.m_TimerHdl));
        }
    }
    return NULL;
}
//---------------------------------------------------------------------------
//
// Function:            main
//
// Description:         main function of demo application
//
// Parameters:
//
// Returns:
//---------------------------------------------------------------------------
int  main (int argc, char **argv)
{
    tEplKernel                  EplRet = kEplSuccessful;
    static tEplApiInitParam     EplApiInitParam;
    char*                       sHostname = HOSTNAME;
    char                        cKey = 0;

#ifdef CONFIG_POWERLINK_USERSTACK
    // variables for Pcap
    char                        sErr_Msg[ PCAP_ERRBUF_SIZE ];
    char                        devName[128];
    pcap_if_t *                 alldevs;
    pcap_if_t *                 seldev;
    int                         i = 0;
    int                         inum;
#endif

    int                         opt;

#if (TARGET_SYSTEM == _LINUX_)
    /* get command line parameters */
    while ((opt = getopt(argc, argv, "c:l:")) != -1)
    {
        switch (opt)
        {
        case 'c':
            uiCycleLen_g = strtoul(optarg, NULL, 10);
            break;

        case 'l':
            pLogFile_g = optarg;
            break;

        default: /* '?' */
            fprintf (stderr, "Usage: %s [-c CYCLE_TIME] [-l LOGFILE]\n", argv[0]);
            goto Exit;
        }
    }
#endif

#ifdef CONFIG_POWERLINK_USERSTACK

#if (TARGET_SYSTEM == _LINUX_)
    struct sched_param          schedParam;

    /* adjust process priority */
    if (nice (-20) == -1)         // push nice level in case we have no RTPreempt
    {
        EPL_DBGLVL_ERROR_TRACE("%s() couldn't set nice value! (%s)\n", __func__, strerror(errno));
    }
    schedParam.__sched_priority = MAIN_THREAD_PRIORITY;
    if (pthread_setschedparam(pthread_self(), SCHED_RR, &schedParam) != 0)
    {
        EPL_DBGLVL_ERROR_TRACE("%s() couldn't set thread scheduling parameters! %d\n",
                __func__, schedParam.__sched_priority);
    }

    /* Initialize target specific stuff */
    EplTgtInit();

#elif (TARGET_SYSTEM == _WIN32_)

    // activate realtime priority class
    SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
    // lower the priority of this thread
    SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE);

#endif // (TARGET_SYSTEM == _WIN32_)

#endif // CONFIG_POWERLINK_USERSTACK

#if (TARGET_SYSTEM == _LINUX_)
#ifdef SET_CPU_AFFINITY
    {
        /* binds all openPOWERLINK threads to the second CPU core */
        cpu_set_t                   affinity;

        CPU_ZERO(&affinity);
        CPU_SET(1, &affinity);
        sched_setaffinity(0, sizeof(cpu_set_t), &affinity);
    }
#endif
#endif


    /* Enabling ftrace for debugging */
    FTRACE_OPEN();
    FTRACE_ENABLE(TRUE);

    /*
    EPL_DBGLVL_ALWAYS_TRACE("%s(): Main Thread Id:%ld\n", __func__,
                             syscall(SYS_gettid));
                             */
    printf("----------------------------------------------------\n");
    printf("openPOWERLINK console MN DEMO application\n");
    printf("----------------------------------------------------\n");

    EPL_MEMSET(&EplApiInitParam, 0, sizeof (EplApiInitParam));
    EplApiInitParam.m_uiSizeOfStruct = sizeof (EplApiInitParam);

#ifdef CONFIG_POWERLINK_USERSTACK

    /* Retrieve the device list on the local machine */
    if (pcap_findalldevs(&alldevs, sErr_Msg) == -1)
    {
        fprintf(stderr, "Error in pcap_findalldevs: %s\n", sErr_Msg);
        EplRet = kEplNoResource;
        goto Exit;
    }

    PRINTF("--------------------------------------------------\n");
    PRINTF("List of Ethernet Cards Found in this System: \n");
    PRINTF("--------------------------------------------------\n");

     /* Print the list */
    for (seldev = alldevs; seldev != NULL; seldev = seldev->next)
    {
        PRINTF("%d. ", ++i);

        if (seldev->description)
        {
            PRINTF("%s\n      %s\n", seldev->description, seldev->name);
        }
        else
        {
            PRINTF("%s\n", seldev->name);
        }
    }

    if (i == 0)
    {
        PRINTF("\nNo interfaces found! Make sure pcap library is installed.\n");
        EplRet = kEplNoResource;
        goto Exit;
    }

    PRINTF("--------------------------------------------------\n");
    PRINTF("Select the interface to be used for POWERLINK (1-%d):",i);
    if (scanf("%d", &inum) == EOF)
    {
        pcap_freealldevs(alldevs);
        EplRet = kEplNoResource;
        goto Exit;
    }
    PRINTF("--------------------------------------------------\n");
    if ((inum < 1) || (inum > i))
    {
        PRINTF("\nInterface number out of range.\n");
        /* Free the device list */
        pcap_freealldevs(alldevs);
        EplRet = kEplNoResource;
        goto Exit;
    }

    /* Jump to the selected adapter */
    for (seldev = alldevs, i = 0;
         i < (inum - 1);
         seldev = seldev->next, i++)
    {   // do nothing
    }

    strncpy(devName, seldev->name, 127);
    // pass selected device name to Edrv
    EplApiInitParam.m_HwParam.m_pszDevName = devName;

#endif

    EplApiInitParam.m_uiNodeId = uiNodeId_g = NODEID;
    EplApiInitParam.m_dwIpAddress = (0xFFFFFF00 & IP_ADDR) | EplApiInitParam.m_uiNodeId;

    /* write 00:00:00:00:00:00 to MAC address, so that the driver uses the real hardware address */
    EPL_MEMCPY(EplApiInitParam.m_abMacAddress, abMacAddr, sizeof (EplApiInitParam.m_abMacAddress));

    EplApiInitParam.m_fAsyncOnly = FALSE;

    EplApiInitParam.m_dwFeatureFlags            = -1;
    EplApiInitParam.m_dwCycleLen                = uiCycleLen_g;     // required for error detection
    EplApiInitParam.m_uiIsochrTxMaxPayload      = 256;              // const
    EplApiInitParam.m_uiIsochrRxMaxPayload      = 256;              // const
    EplApiInitParam.m_dwPresMaxLatency          = 50000;            // const; only required for IdentRes
    EplApiInitParam.m_uiPreqActPayloadLimit     = 36;               // required for initialisation (+28 bytes)
    EplApiInitParam.m_uiPresActPayloadLimit     = 36;               // required for initialisation of Pres frame (+28 bytes)
    EplApiInitParam.m_dwAsndMaxLatency          = 150000;           // const; only required for IdentRes
    EplApiInitParam.m_uiMultiplCycleCnt         = 0;                // required for error detection
    EplApiInitParam.m_uiAsyncMtu                = 1500;             // required to set up max frame size
    EplApiInitParam.m_uiPrescaler               = 2;                // required for sync
    EplApiInitParam.m_dwLossOfFrameTolerance    = 500000;
    EplApiInitParam.m_dwAsyncSlotTimeout        = 3000000;
    EplApiInitParam.m_dwWaitSocPreq             = 150000;
    EplApiInitParam.m_dwDeviceType              = -1;               // NMT_DeviceType_U32
    EplApiInitParam.m_dwVendorId                = -1;               // NMT_IdentityObject_REC.VendorId_U32
    EplApiInitParam.m_dwProductCode             = -1;               // NMT_IdentityObject_REC.ProductCode_U32
    EplApiInitParam.m_dwRevisionNumber          = -1;               // NMT_IdentityObject_REC.RevisionNo_U32
    EplApiInitParam.m_dwSerialNumber            = -1;               // NMT_IdentityObject_REC.SerialNo_U32

    EplApiInitParam.m_dwSubnetMask              = SUBNET_MASK;
    EplApiInitParam.m_dwDefaultGateway          = 0;
    EPL_MEMCPY(EplApiInitParam.m_sHostname, sHostname, sizeof(EplApiInitParam.m_sHostname));
    EplApiInitParam.m_uiSyncNodeId              = EPL_C_ADR_SYNC_ON_SOA;
    EplApiInitParam.m_fSyncOnPrcNode            = FALSE;

    // set callback functions
    EplApiInitParam.m_pfnCbEvent = AppCbEvent;

#ifdef CONFIG_POWERLINK_USERSTACK
    EplApiInitParam.m_pfnObdInitRam = EplObdInitRam;
    EplApiInitParam.m_pfnCbSync  = AppCbSync;
#else
    EplApiInitParam.m_pfnCbSync = NULL;
#endif


    printf("\n\nHello, I'm a Userspace POWERLINK node running as %s!\n  (build: %s / %s)\n\n",
            (uiNodeId_g == EPL_C_ADR_MN_DEF_NODE_ID ?
                "Managing Node" : "Controlled Node"),
            __DATE__, __TIME__);

    // initialize POWERLINK stack
    printf ("Initializing openPOWERLINK stack...\n");
    EplRet = EplApiInitialize(&EplApiInitParam);
    if(EplRet != kEplSuccessful)
    {
        printf("EplApiInitialize() failed (Error:0x%x!\n", EplRet);
        goto Exit;
    }

    // initialize application
    printf ("Initializing openPOWERLINK application...\n");
    EplRet = AppInit();
    if(EplRet != kEplSuccessful)
    {
        printf("ApiInit() failed!\n");
        goto Exit;
    }


#ifdef CONFIG_POWERLINK_USERSTACK
    /* At this point, we don't need any more the device list. Free it */
    pcap_freealldevs(alldevs);

    EplRet = EplApiSetCdcFilename(pszCdcFilename_g);
    if(EplRet != kEplSuccessful)
    {
        goto Exit;
    }
#else
    // create event thread
    if (pthread_create(&eventThreadId, NULL,
                   &powerlinkEventThread, NULL) != 0)
    {
        goto Exit;
    }

    // create sync thread
    if (pthread_create(&syncThreadId, NULL,
                   &powerlinkSyncThread, NULL) != 0)
    {
        goto Exit;
    }
#endif

    printf("Initializing process image...\n");
    printf("Size of input process image: %ld\n", sizeof(AppProcessImageIn_g));
    printf("Size of output process image: %ld\n", sizeof (AppProcessImageOut_g));
    AppProcessImageCopyJob_g.m_fNonBlocking = FALSE;
    AppProcessImageCopyJob_g.m_uiPriority = 0;
    AppProcessImageCopyJob_g.m_In.m_pPart = &AppProcessImageIn_g;
    AppProcessImageCopyJob_g.m_In.m_uiOffset = 0;
    AppProcessImageCopyJob_g.m_In.m_uiSize = sizeof (AppProcessImageIn_g);
    AppProcessImageCopyJob_g.m_Out.m_pPart = &AppProcessImageOut_g;
    AppProcessImageCopyJob_g.m_Out.m_uiOffset = 0;
    AppProcessImageCopyJob_g.m_Out.m_uiSize = sizeof (AppProcessImageOut_g);

    EplRet = EplApiProcessImageAlloc(sizeof (AppProcessImageIn_g), sizeof (AppProcessImageOut_g), 2, 2);
    if (EplRet != kEplSuccessful)
    {
        goto Exit;
    }

    EplRet = EplApiProcessImageSetup();
    if (EplRet != kEplSuccessful)
    {
        goto Exit;
    }

    // start processing
    EplRet = EplApiExecNmtCommand(kEplNmtEventSwReset);
    if (EplRet != kEplSuccessful)
    {
        goto ExitShutdown;
    }

    printf("\n-------------------------------\n");
    printf("Press Esc to leave the program\n");
    printf("Press r to reset the node\n");
    printf("-------------------------------\n\n");
    // wait for key hit
    while (cKey != 0x1B)
    {
        if( EplTgtKbhit() )
        {
            cKey    = (BYTE) EplTgtGetch();

            switch (cKey)
            {
                case 'r':
                {
                    EplRet = EplApiExecNmtCommand(kEplNmtEventSwReset);
                    if (EplRet != kEplSuccessful)
                    {
                        goto ExitShutdown;
                    }
                    break;
                }

                case 'c':
                {
                    EplRet = EplApiExecNmtCommand(kEplNmtEventNmtCycleError);
                    if (EplRet != kEplSuccessful)
                    {
                        goto ExitShutdown;
                    }
                    break;
                }

                default:
                {
                    break;
                }
            }
        }

        EplTgtMilliSleep( 1500 );
    }

    FTRACE_ENABLE(FALSE);

ExitShutdown:
    // halt the NMT state machine
    // so the processing of POWERLINK frames stops
    EplRet = EplApiExecNmtCommand(kEplNmtEventSwitchOff);

    // delete process image
    EplRet = EplApiProcessImageFree();

    // delete instance for all modules
    EplRet = EplApiShutdown();

Exit:
    PRINTF("main(): returns 0x%X\n", EplRet);

#if (TARGET_SYSTEM == _WIN32_)
    PRINTF("Press Enter to quit!\n");
    EplTgtGetch();
#endif

    return EplRet;
}
//------------------------------------------------------------------------------
// Function:    ShbIpcStartSignalingNewData
//
// Description: Start signaling of new data (called from reading process)
//
// Parameters:  pShbInstance_p                  pointer to shared buffer instance
//              pfnSignalHandlerNewData_p       pointer to master buffer instance
//
// Return:      tShbError      = error code
//------------------------------------------------------------------------------
tShbError  ShbIpcStartSignalingNewData(tShbInstance pShbInstance_p,
                                       tSigHndlrNewData pfnSignalHandlerNewData_p,
                                       tShbPriority ShbPriority_p)
{
    tShbMemInst*        pShbMemInst;
    tShbMemHeader*      pShbMemHeader;
    tShbError           ShbError;
    struct sched_param  schedParam;
    INT                 iSchedPriority;

    if ((pShbInstance_p == NULL) || (pfnSignalHandlerNewData_p == NULL))
    {
        return (kShbInvalidArg);
    }

    pShbMemInst   = ShbIpcGetShbMemInst   (pShbInstance_p);
    pShbMemHeader = ShbIpcGetShbMemHeader (pShbInstance_p);
    ShbError = kShbOk;

    if ((pShbMemInst->m_fNewDataThreadStarted) ||
        (pShbMemInst->m_pfnSigHndlrNewData != NULL))
    {
        ShbError = kShbAlreadySignaling;
        EPL_DBGLVL_ERROR_TRACE("%s() Thread already started!\n", __func__);
        goto Exit;
    }

    pShbMemInst->m_pfnSigHndlrNewData = pfnSignalHandlerNewData_p;

    iSchedPriority = EPL_THREAD_PRIORITY_MEDIUM;
    switch (ShbPriority_p)
    {
        case kShbPriorityLow:
            iSchedPriority -= 5;
            break;

        case kShbPriorityNormal:
            iSchedPriority += 0;
            break;

        case kShbPriorityHigh:
            iSchedPriority += 5;
            break;
    }

    //create thread for signaling new data
    if (pthread_create(&pShbMemInst->m_tThreadNewDataId, NULL,
                   &ShbIpcThreadSignalNewData, pShbInstance_p) != 0)
    {
        pShbMemInst->m_pfnSigHndlrNewData = NULL;
        ShbError = kShbInvalidSigHndlr;
        goto Exit;
    }

    schedParam.__sched_priority = iSchedPriority;
    if (pthread_setschedparam(pShbMemInst->m_tThreadNewDataId, SCHED_FIFO,
                              &schedParam) != 0)
    {
        EPL_DBGLVL_ERROR_TRACE("%s(): couldn't set thread scheduling parameters! %d\n",
               __func__, schedParam.__sched_priority);
    }

Exit:
    return (ShbError);
}