//------------------------------------------------------------------------------
static tOplkError timerHdlSlotCb(tTimerEventArg* pEventArg_p)
{
    tOplkError      ret = kErrorOk;
    tEdrvTxBuffer*  pTxBuffer = NULL;

    if (pEventArg_p->timerHdl != edrvcyclicInstance_l.timerHdlSlot)
    {   // zombie callback
        // just exit
        goto Exit;
    }

#if CONFIG_EDRV_CYCLIC_USE_DIAGNOSTICS != FALSE
    edrvcyclicInstance_l.lastSlotTimeStamp = target_getCurrentTimestamp();
#endif

    pTxBuffer = edrvcyclicInstance_l.ppTxBufferList[edrvcyclicInstance_l.curTxBufferEntry];
    ret = edrv_sendTxBuffer(pTxBuffer);
    if (ret != kErrorOk)
    {
        goto Exit;
    }

    edrvcyclicInstance_l.curTxBufferEntry++;

    ret = processTxBufferList();

Exit:
    if (ret != kErrorOk)
    {
        if (edrvcyclicInstance_l.pfnErrorCb != NULL)
        {
            ret = edrvcyclicInstance_l.pfnErrorCb(ret, pTxBuffer);
        }
    }
    return ret;
}
//------------------------------------------------------------------------------
static tOplkError processTxBufferList(void)
{
    tOplkError          ret = kErrorOk;
    tEdrvTxBuffer*      pTxBuffer = NULL;
#if (EDRV_USE_TTTX == TRUE)
    BOOL                fFirstPacket = TRUE;
    UINT64              launchTime;
    UINT64              cycleMin;
    UINT64              cycleMax;
    UINT64              currentMacTime = 0;
#endif

#if (EDRV_USE_TTTX == TRUE)
    edrv_getMacTime(&currentMacTime);
    if (!edrvcyclicInstance_l.fNextCycleValid)
    {
        edrvcyclicInstance_l.nextCycleTime = currentMacTime + EDRV_SHIFT;
        launchTime = edrvcyclicInstance_l.nextCycleTime;
        edrvcyclicInstance_l.fNextCycleValid = TRUE;
    }
    else
    {
        launchTime = edrvcyclicInstance_l.nextCycleTime;
        if (currentMacTime > launchTime)
        {
            ret = kErrorEdrvTxListNotFinishedYet;
            goto Exit;
        }
    }

    cycleMin = launchTime;
    cycleMax = launchTime + (edrvcyclicInstance_l.cycleTimeUs * 1000ULL);

    while ((pTxBuffer = edrvcyclicInstance_l.ppTxBufferList[edrvcyclicInstance_l.curTxBufferEntry]) != NULL)
    {
        if (pTxBuffer == NULL)
        {
            ret = kErrorEdrvBufNotExisting;
            goto Exit;
        }

        if (fFirstPacket)
        {
            pTxBuffer->launchTime = launchTime ;
            fFirstPacket = FALSE;
        }
        else
        {
            launchTime = launchTime + (UINT64)pTxBuffer->timeOffsetNs;
            pTxBuffer->launchTime = launchTime;
        }

        if ((pTxBuffer->launchTime - cycleMin) >  (cycleMax - cycleMin))
        {
            ret = kErrorEdrvTxListNotFinishedYet;
            goto Exit;
        }

        ret = edrv_sendTxBuffer(pTxBuffer);
        if (ret != kErrorOk)
            goto Exit;

        pTxBuffer->launchTime = 0;
        edrvcyclicInstance_l.curTxBufferEntry++;
    }

#else

    while ((pTxBuffer = edrvcyclicInstance_l.ppTxBufferList[edrvcyclicInstance_l.curTxBufferEntry]) != NULL)
    {
        if (pTxBuffer->timeOffsetNs == 0)
        {
            ret = edrv_sendTxBuffer(pTxBuffer);
            if (ret != kErrorOk)
            {
                goto Exit;
            }
        }
        else
        {
            ret = hrestimer_modifyTimer(&edrvcyclicInstance_l.timerHdlSlot,
                                        pTxBuffer->timeOffsetNs,
                                        timerHdlSlotCb,
                                        0L,
                                        FALSE);

            break;
        }

        edrvcyclicInstance_l.curTxBufferEntry++;
    }
#endif

Exit:
    if (ret != kErrorOk)
    {
        if (edrvcyclicInstance_l.pfnErrorCb != NULL)
        {
            ret = edrvcyclicInstance_l.pfnErrorCb(ret, pTxBuffer);
        }
    }
    return ret;
}
//------------------------------------------------------------------------------
static tOplkError timerHdlCycleCb(tTimerEventArg* pEventArg_p)
{
    tOplkError      ret = kErrorOk;
#if CONFIG_EDRV_CYCLIC_USE_DIAGNOSTICS != FALSE
    UINT32          cycleTime;
    UINT32          usedCycleTime;
    UINT32          spareCycleTime;
    ULONGLONG       startNewCycleTimeStamp;
#endif

    if (pEventArg_p->timerHdl != edrvcyclicInstance_l.timerHdlCycle)
    {   // zombie callback
        // just exit
        goto Exit;
    }

#if CONFIG_EDRV_CYCLIC_USE_DIAGNOSTICS != FALSE
    startNewCycleTimeStamp = target_getCurrentTimestamp();
#endif

    if (edrvcyclicInstance_l.ppTxBufferList[edrvcyclicInstance_l.curTxBufferEntry] != NULL)
    {
        ret = kErrorEdrvTxListNotFinishedYet;
        goto Exit;
    }

    edrvcyclicInstance_l.ppTxBufferList[edrvcyclicInstance_l.curTxBufferList] = NULL;

    // enter new cycle -> switch Tx buffer list
    edrvcyclicInstance_l.curTxBufferList ^= edrvcyclicInstance_l.maxTxBufferCount;
    edrvcyclicInstance_l.curTxBufferEntry = edrvcyclicInstance_l.curTxBufferList;

    if (edrvcyclicInstance_l.ppTxBufferList[edrvcyclicInstance_l.curTxBufferEntry] == NULL)
    {
        ret = kErrorEdrvCurTxListEmpty;
        goto Exit;
    }

    ret = processTxBufferList();
    if (ret != kErrorOk)
    {
        goto Exit;
    }

    if (edrvcyclicInstance_l.pfnSyncCb != NULL)
    {
        ret = edrvcyclicInstance_l.pfnSyncCb();
    }

#if CONFIG_EDRV_CYCLIC_USE_DIAGNOSTICS != FALSE
    if (edrvcyclicInstance_l.startCycleTimeStamp != 0)
    {
        // calculate time diffs of previous cycle
        cycleTime = (UINT32)(startNewCycleTimeStamp - edrvcyclicInstance_l.startCycleTimeStamp);
        if (edrvcyclicInstance_l.diagnostics.cycleTimeMin > cycleTime)
        {
            edrvcyclicInstance_l.diagnostics.cycleTimeMin = cycleTime;
        }
        if (edrvcyclicInstance_l.diagnostics.cycleTimeMax < cycleTime)
        {
            edrvcyclicInstance_l.diagnostics.cycleTimeMax = cycleTime;
        }

        if (edrvcyclicInstance_l.lastSlotTimeStamp != 0)
        {
            usedCycleTime  = (UINT32)(edrvcyclicInstance_l.lastSlotTimeStamp - edrvcyclicInstance_l.startCycleTimeStamp);
            spareCycleTime = (UINT32)(startNewCycleTimeStamp - edrvcyclicInstance_l.lastSlotTimeStamp);

            if (edrvcyclicInstance_l.diagnostics.usedCycleTimeMin > usedCycleTime)
            {
                edrvcyclicInstance_l.diagnostics.usedCycleTimeMin = usedCycleTime;
            }
            if (edrvcyclicInstance_l.diagnostics.usedCycleTimeMax < usedCycleTime)
            {
                edrvcyclicInstance_l.diagnostics.usedCycleTimeMax = usedCycleTime;
            }
            if (edrvcyclicInstance_l.diagnostics.spareCycleTimeMin > spareCycleTime)
            {
                edrvcyclicInstance_l.diagnostics.spareCycleTimeMin = spareCycleTime;
            }
            if (edrvcyclicInstance_l.diagnostics.spareCycleTimeMax < spareCycleTime)
            {
                edrvcyclicInstance_l.diagnostics.spareCycleTimeMax = spareCycleTime;
            }
        }
        else
        {
            usedCycleTime = 0;
            spareCycleTime = cycleTime;
        }

        edrvcyclicInstance_l.diagnostics.cycleTimeMeanSum      += cycleTime;
        edrvcyclicInstance_l.diagnostics.usedCycleTimeMeanSum  += usedCycleTime;
        edrvcyclicInstance_l.diagnostics.spareCycleTimeMeanSum += spareCycleTime;
        edrvcyclicInstance_l.diagnostics.cycleCount++;

        // sample previous cycle if deviations exceed threshold
        if ((edrvcyclicInstance_l.diagnostics.sampleNum == 0) || /* sample first cycle for start time */
            (abs(cycleTime - edrvcyclicInstance_l.cycleTimeUs * 1000) > EDRV_CYCLIC_SAMPLE_TH_CYCLE_TIME_DIFF_US * 1000) ||
            (spareCycleTime < EDRV_CYCLIC_SAMPLE_TH_SPARE_TIME_US * 1000))
        {
        UINT uiSampleNo = edrvcyclicInstance_l.sampleCount;

            edrvcyclicInstance_l.diagnostics.aSampleTimeStamp[uiSampleNo] = edrvcyclicInstance_l.startCycleTimeStamp;
            edrvcyclicInstance_l.diagnostics.aCycleTime[uiSampleNo]       = cycleTime;
            edrvcyclicInstance_l.diagnostics.aUsedCycleTime[uiSampleNo]   = usedCycleTime;
            edrvcyclicInstance_l.diagnostics.aSpareCycleTime[uiSampleNo]  = spareCycleTime;

            edrvcyclicInstance_l.diagnostics.sampleNum++;
            if (edrvcyclicInstance_l.diagnostics.sampleBufferedNum != EDRV_CYCLIC_SAMPLE_NUM)
            {
                edrvcyclicInstance_l.diagnostics.sampleBufferedNum++;
            }

            edrvcyclicInstance_l.sampleCount++;
            if (edrvcyclicInstance_l.sampleCount == EDRV_CYCLIC_SAMPLE_NUM)
            {
                edrvcyclicInstance_l.sampleCount = 1;
            }
        }
    }

    edrvcyclicInstance_l.startCycleTimeStamp = startNewCycleTimeStamp;
    edrvcyclicInstance_l.lastSlotTimeStamp = 0;
#endif

Exit:
    if (ret != kErrorOk)
    {
        if (edrvcyclicInstance_l.pfnErrorCb != NULL)
        {
            ret = edrvcyclicInstance_l.pfnErrorCb(ret, NULL);
        }
    }

#if (EDRV_USE_TTTX == TRUE)
    edrvcyclicInstance_l.nextCycleTime += (edrvcyclicInstance_l.cycleTimeUs * 1000ULL);
#endif

    return ret;
}