/** ----------------------------------------------------------------------
 *
 * Copyright (C) 2016 ST Microelectronics S.A.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *
 ----------------------------------------------------------------------*/
#define LOG_TAG "NfcHal"


#include <hardware/nfc.h>
#include "halcore_private.h"
#include "android_logmsg.h"
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>

pthread_mutex_t debugOutputSem = PTHREAD_MUTEX_INITIALIZER;
bool halTraceMask = true;
extern int I2cWriteCmd(const uint8_t* x, size_t len);
extern void DispHal(const char* title, const void* data, size_t length);

extern uint32_t ScrProtocolTraceFlag;  // = SCR_PROTO_TRACE_ALL;

// HAL WRAPPER
static void HalStopTimer(HalInstance* inst);

typedef struct {
    struct nfc_nci_device nci_device;  // nci_device must be first struct member
    // below declarations are private variables within HAL
    nfc_stack_callback_t* p_cback;
    nfc_stack_data_callback_t* p_data_cback;
    HALHANDLE hHAL;
} st21nfc_dev_t;  // beware, is a duplication of structure in nfc_nci_st21nfc.c

/**************************************************************************************************
 *
 *                                      Private API Declaration
 *
 **************************************************************************************************/

static void* HalWorkerThread(void* arg);
static inline int sem_wait_nointr(sem_t *sem);

static void HalOnNewUpstreamFrame(HalInstance* inst, const uint8_t* data,
                                  size_t length);
static void HalTriggerNextDsPacket(HalInstance* inst);
static bool HalEnqueueThreadMessage(HalInstance* inst, ThreadMesssage* msg);
static bool HalDequeueThreadMessage(HalInstance* inst, ThreadMesssage* msg);
static HalBuffer* HalAllocBuffer(HalInstance* inst);
static HalBuffer* HalFreeBuffer(HalInstance* inst, HalBuffer* b);
static uint32_t HalSemWait(sem_t* pSemaphore, uint32_t timeout);

/**************************************************************************************************
 *
 *                                      Public API Entry-Points
 *
 **************************************************************************************************/

/**
 * Callback of HAL Core protocol layer.
 * Invoked by HAL worker thread according to if message is received from NCI
 * stack or posted by
 * I2C worker thread.
 * <p>@param context NFC callbacks for control/data
 * @param event Next HAL state machine action (send msg to I2C layer or report
 * data/control/error
 * to NFC task)
 * @param length Configure if debug and trace allowed, trace level
 */
void HalCoreCallback(void* context, uint32_t event, const void* d,
                     size_t length)
{
    const uint8_t* data = (const uint8_t*)d;
    uint8_t cmd = 'W';

    st21nfc_dev_t* dev = (st21nfc_dev_t*)context;

    switch (event) {
        case HAL_EVENT_DSWRITE:
            STLOG_HAL_V("!! got event HAL_EVENT_DSWRITE for %zu bytes\n", length);
            DispHal("TX DATA", (data), length);

            // Send write command to IO thread
            cmd = 'W';
            I2cWriteCmd(&cmd, sizeof(cmd));
            I2cWriteCmd((const uint8_t*)&length, sizeof(length));
            I2cWriteCmd(data, length);
            break;

        case HAL_EVENT_DATAIND:
            STLOG_HAL_V("!! got event HAL_EVENT_DATAIND for %zu bytes\n", length);

            if ((length >= 3) && (data[2] != (length - 3))) {
                STLOG_HAL_W("length is illogical. Header length is %d, packet length %zu\n",
                      data[2], length);
            }

            dev->p_data_cback(length, (uint8_t*)data);
            break;

        case HAL_EVENT_ERROR:
            STLOG_HAL_E("!! got event HAL_EVENT_ERROR\n");
            DispHal("Received unexpected HAL message !!!", data, length);
            break;

        case HAL_EVENT_LINKLOST:
            STLOG_HAL_E("!! got event HAL_EVENT_LINKLOST or HAL_EVENT_ERROR\n");

            dev->p_cback(HAL_NFC_ERROR_EVT, HAL_NFC_STATUS_ERR_CMD_TIMEOUT);

            // Write terminate command
            cmd = 'X';
            I2cWriteCmd(&cmd, sizeof(cmd));
            break;

        case HAL_EVENT_TIMER_TIMEOUT:
            STLOG_HAL_D("!! got event HAL_EVENT_TIMER_TIMEOUT \n");
            dev->p_cback(HAL_WRAPPER_TIMEOUT_EVT, HAL_NFC_STATUS_OK);

            //            dev->p_data_cback(0, NULL);
            break;
    }
}

/**
 * Connection to the HAL Core layer.
 * Set-up HAL context and create HAL worker thread.
 * <p>@param context NFC NCI device context, NFC callbacks for control/data, HAL
 * handle
 * @param callback HAL callback function pointer
 * @param flags Configure if debug and trace allowed, trace level
 */
HALHANDLE HalCreate(void* context, HAL_CALLBACK callback, uint32_t flags)
{
    halTraceMask = true;

    if (flags & HAL_FLAG_NO_DEBUG) {
        halTraceMask = false;
    }

    STLOG_HAL_V("HalCreate enter\n");

    HalInstance* inst = calloc(1, sizeof(HalInstance));

    if (!inst) {
        STLOG_HAL_E("!out of memory\n");
        return NULL;
    }

    // We need a semaphore to wakeup our protocol thread
    if (0 != sem_init(&inst->semaphore, 0, 0)) {
        STLOG_HAL_E("!sem_init failed\n");
        free(inst);
        return NULL;
    }

    // We need a semaphore to manage buffers
    if (0 != sem_init(&inst->bufferResourceSem, 0, NUM_BUFFERS)) {
        STLOG_HAL_E("!sem_init failed\n");
        sem_destroy(&inst->semaphore);
        free(inst);
        return NULL;
    }

    // We need a semaphore to block upstream data indications
    if (0 != sem_init(&inst->upstreamBlock, 0, 0)) {
        STLOG_HAL_E("!sem_init failed\n");
        sem_destroy(&inst->semaphore);
        sem_destroy(&inst->bufferResourceSem);
        free(inst);
        return NULL;
    }

    // Initialize remaining data-members
    inst->context = context;
    inst->callback = callback;
    inst->flags = flags;
    inst->freeBufferList = 0;
    inst->pendingNciList = 0;
    inst->nciBuffer = 0;
    inst->ringReadPos = 0;
    inst->ringWritePos = 0;
    inst->timeout = HAL_SLEEP_TIMER_DURATION;

    inst->bufferData = calloc(NUM_BUFFERS, sizeof(HalBuffer));
    if (!inst->bufferData) {
        STLOG_HAL_E("!failed to allocate memory\n");
        sem_destroy(&inst->semaphore);
        sem_destroy(&inst->bufferResourceSem);
        sem_destroy(&inst->upstreamBlock);
        free(inst);
        return NULL;
    }

    // Concatenate the buffers into a linked list for easy access
    size_t i;
    for (i = 0; i < NUM_BUFFERS; i++) {
        HalBuffer* b = &inst->bufferData[i];
        b->next = inst->freeBufferList;
        inst->freeBufferList = b;
    }

    if (0 != pthread_mutex_init(&inst->hMutex, 0))
      {
        STLOG_HAL_E("!failed to initialize Mutex \n");
        sem_destroy(&inst->semaphore);
        sem_destroy(&inst->bufferResourceSem);
        sem_destroy(&inst->upstreamBlock);
        free(inst->bufferData);
        free(inst);
        return NULL;
      }

    // Spawn the thread
    if (0 != pthread_create(&inst->thread, NULL, HalWorkerThread, inst)) {
        STLOG_HAL_E("!failed to spawn workerthread \n");
        sem_destroy(&inst->semaphore);
        sem_destroy(&inst->bufferResourceSem);
        sem_destroy(&inst->upstreamBlock);
        pthread_mutex_destroy(&inst->hMutex);
        free(inst->bufferData);
        free(inst);
        return NULL;
    }

    STLOG_HAL_V("HalCreate exit\n");
    return (HALHANDLE)inst;
}

/**
 * Disconnection of the HAL protocol layer.
 * Send message to stop the HAL worker thread and wait for it to finish. Free
 * resources.
 * @param hHAL HAL handle
 */
void HalDestroy(HALHANDLE hHAL)
{
    HalInstance* inst = (HalInstance*)hHAL;
    // Tell the thread that we want to finish
    ThreadMesssage msg;
    msg.command = MSG_EXIT_REQUEST;
    msg.payload = 0;
    msg.length = 0;

    HalEnqueueThreadMessage(inst, &msg);

    // Wait for thread to finish
    pthread_join(inst->thread, NULL);

    // Cleanup and exit
    sem_destroy(&inst->semaphore);
    sem_destroy(&inst->upstreamBlock);
    sem_destroy(&inst->bufferResourceSem);
    pthread_mutex_destroy(&inst->hMutex);

    // Free resources
    free(inst->bufferData);
    free(inst);

    STLOG_HAL_V("HalDestroy done\n");
}

/**
 * Send an NCI message downstream to HAL protocol layer (DH->NFCC transfer).
 * Block if more than NUM_BUFFERS (10) transfers are outstanding, otherwise will return immediately.
 * @param hHAL HAL handle
 * @param data Data message
 * @param size Message size
 */ bool HalSendDownstream(HALHANDLE hHAL, const uint8_t* data, size_t size)
{
    // Send an NCI frame downstream. will
    HalInstance* inst = (HalInstance*)hHAL;

    if ((size <= MAX_BUFFER_SIZE) && (size > 0)) {
        ThreadMesssage msg;
        HalBuffer* b = HalAllocBuffer(inst);

        if (!b) {
            // Should never be reachable
            return false;
        }

        memcpy(b->data, data, size);
        b->length = size;

        msg.command = MSG_TX_DATA;
        msg.payload = 0;
        msg.length = 0;
        msg.buffer = b;

        return HalEnqueueThreadMessage(inst, &msg);

    } else {
        STLOG_HAL_E("HalSendDownstream size to large %zu instead of %d\n", size,
              MAX_BUFFER_SIZE);
        return false;
    }
}

// HAL WRAPPER
/**
 * Send an NCI message downstream to HAL protocol layer (DH->NFCC transfer).
 * Block if more than NUM_BUFFERS (10) transfers are outstanding, otherwise will return immediately.
 * @param hHAL HAL handle
 * @param data Data message
 * @param size Message size
 */ bool HalSendDownstreamTimer(HALHANDLE hHAL, const uint8_t* data,
                                size_t size, uint8_t duration)
{
    // Send an NCI frame downstream. will
    HalInstance* inst = (HalInstance*)hHAL;

    if ((size <= MAX_BUFFER_SIZE) && (size > 0)) {
        ThreadMesssage msg;
        HalBuffer* b = HalAllocBuffer(inst);

        if (!b) {
            // Should never be reachable
            return false;
        }

        memcpy(b->data, data, size);
        b->length = size;

        msg.command = MSG_TX_DATA_TIMER_START;
        msg.payload = 0;
        msg.length = duration;
        msg.buffer = b;

        return HalEnqueueThreadMessage(inst, &msg);

    } else {
        STLOG_HAL_E("HalSendDownstreamTimer size to large %zu instead of %d\n", size,
              MAX_BUFFER_SIZE);
        return false;
    }
}

/**
 * Send an NCI message downstream to HAL protocol layer (DH->NFCC transfer).
 * Block if more than NUM_BUFFERS (10) transfers are outstanding, otherwise will
 * return immediately.
 * @param hHAL HAL handle
 * @param data Data message
 * @param size Message size
 */
bool HalSendDownstreamStopTimer(HALHANDLE hHAL)
{
    // Send an NCI frame downstream. will
    HalInstance* inst = (HalInstance*)hHAL;

    HalStopTimer(inst);
    return 1;


}

/**
 * Send an NCI message upstream to NFC NCI layer (NFCC->DH transfer).
 * @param hHAL HAL handle
 * @param data Data message
 * @param size Message size
 */ bool HalSendUpstream(HALHANDLE hHAL, const uint8_t* data, size_t size)
{
    HalInstance* inst = (HalInstance*)hHAL;
    if ((size <= MAX_BUFFER_SIZE) && (size > 0)) {
        ThreadMesssage msg;
        msg.command = MSG_RX_DATA;
        msg.payload = data;
        msg.length = size;

        if (HalEnqueueThreadMessage(inst, &msg)) {
            // Block until the protocol has taken a copy of the data
            sem_wait_nointr(&inst->upstreamBlock);
            return true;
        }
        return false;
    } else {
        STLOG_HAL_E("HalSendUpstream size to large %zu instead of %d\n", size,
              MAX_BUFFER_SIZE);
        return false;
    }
}

/**************************************************************************************************
 *
 *                                      Private API Definition
 *
 **************************************************************************************************/
/*
 * Get current time stamp
 */
struct timespec HalGetTimestamp(void)
{
    struct timespec tm;
    clock_gettime(CLOCK_REALTIME, &tm);
    return tm;
}

int HalTimeDiffInMs(struct timespec start, struct timespec end)
{
    struct timespec temp;
    if ((end.tv_nsec - start.tv_nsec) < 0) {
        temp.tv_sec = end.tv_sec - start.tv_sec - 1;
        temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec;
    } else {
        temp.tv_sec = end.tv_sec - start.tv_sec;
        temp.tv_nsec = end.tv_nsec - start.tv_nsec;
    }

    return (temp.tv_nsec / 1000000) + (temp.tv_sec * 1000);
}


/**
 * Determine the next shortest sleep to fulfill the pending timer requirements.
 * @param inst HAL instance
 * @param now timespec structure for time definition
 */
static uint32_t HalCalcSemWaitingTime(HalInstance* inst, struct timespec* now)
{
    // Default to infinite wait time
    uint32_t result = OS_SYNC_INFINITE;

    if (inst->timer.active) {
        int delta =
            inst->timer.duration - HalTimeDiffInMs(inst->timer.startTime, *now);

        if (delta < 0) {
            // If we have a timer that has already expired, pick a zero wait time
            result = 0;

        } else if ((uint32_t)delta < result) {
            // Smaller time difference? If so take it
            result = delta;
        }
    }

    if (result != OS_SYNC_INFINITE) {
        // Add one millisecond on top of that, so the waiting semaphore will time
        // out just a moment
        // after the timer should expire
        result += 1;
    }

    return result;
}

/**************************************************************************************************
 *
 *                                     Timer Management
 *
 **************************************************************************************************/

static void HalStopTimer(HalInstance* inst)
{
    inst->timer.active = false;
    STLOG_HAL_D("HalStopTimer \n");
}

static void HalStartTimer(HalInstance* inst, uint32_t duration)
{
    STLOG_HAL_D("HalStartTimer \n");
    inst->timer.startTime = HalGetTimestamp();
    inst->timer.active = true;
    inst->timer.duration = duration;
}

/**************************************************************************************************
 *
 *                                     Thread Message Queue
 *
 **************************************************************************************************/

/**
 * Write message pointer to small ring buffer for queuing HAL messages.
 * @param inst HAL instance
 * @param msg Message to send
 * @return true if message properly copied in ring buffer
 */
static bool HalEnqueueThreadMessage(HalInstance* inst, ThreadMesssage* msg)
{
    // Put a message to the queue
    int nextWriteSlot;
    bool result = true;

    pthread_mutex_lock(&inst->hMutex);

    nextWriteSlot = inst->ringWritePos + 1;

    if (nextWriteSlot == HAL_QUEUE_MAX) {
        nextWriteSlot = 0;
    }

    // Check that we don't overflow the queue entries
    if (nextWriteSlot == inst->ringReadPos) {
        STLOG_HAL_E("HAL thread message ring: RNR (implement me!!)");
        result = false;
    }

    if (result) {
        // inst->ring[nextWriteSlot] = *msg;
        memcpy(&(inst->ring[nextWriteSlot]), msg, sizeof(ThreadMesssage));
        inst->ringWritePos = nextWriteSlot;
    }

    pthread_mutex_unlock(&inst->hMutex);

    if (result) {
        sem_post(&inst->semaphore);
    }

    return result;
}

/**
 * Remove message pointer from stored ring buffer.
 * @param inst HAL instance
 * @param msg Message received
 * @return true if there is a new message to pull, false otherwise.
 */
static bool HalDequeueThreadMessage(HalInstance* inst, ThreadMesssage* msg)
{
    int nextCmdIndex;
    bool result = true;
    // New data available
    pthread_mutex_lock(&inst->hMutex);

    // Get new timer read index
    nextCmdIndex = inst->ringReadPos + 1;

    if (nextCmdIndex == HAL_QUEUE_MAX) {
        nextCmdIndex = 0;
    }
     //check if ring buffer is empty
    if (inst->ringReadPos == inst->ringWritePos)
      {
        STLOG_HAL_E("HAL thread message ring: already read last valid data");
        result = false;
      }

    // Get new element from ringbuffer
    if (result) {
    memcpy(msg, &(inst->ring[nextCmdIndex]), sizeof(ThreadMesssage));
    inst->ringReadPos = nextCmdIndex;
    }

    pthread_mutex_unlock(&inst->hMutex);

    return result;
}

/**************************************************************************************************
 *
 *                                     Buffer/Memory Management
 *
 **************************************************************************************************/

/**
 * Allocate buffer from pre-allocated pool.
 * @param inst HAL instance
 * @return Pointer to allocated HAL buffer
 */
static HalBuffer* HalAllocBuffer(HalInstance* inst)
{
    HalBuffer* b;

    // Wait until we have a buffer resource
    sem_wait_nointr(&inst->bufferResourceSem);

    pthread_mutex_lock(&inst->hMutex);

    b = inst->freeBufferList;
    if (b) {
        inst->freeBufferList = b->next;
        b->next = 0;
    }

    pthread_mutex_unlock(&inst->hMutex);

    if (!b) {
        STLOG_HAL_E(
            "! unable to allocate buffer resource."
            "check bufferResourceSem\n");
    }

    return b;
}

/**
 * Return buffer to pool.
 * @param inst HAL instance
 * @param b Pointer of HAL buffer to free
 * @return Pointer of freed HAL buffer
 */
static HalBuffer* HalFreeBuffer(HalInstance* inst, HalBuffer* b)
{
    pthread_mutex_lock(&inst->hMutex);

    b->next = inst->freeBufferList;
    inst->freeBufferList = b;

    pthread_mutex_unlock(&inst->hMutex);

    // Unblock treads waiting for a buffer
    sem_post(&inst->bufferResourceSem);

    return b;
}

/**************************************************************************************************
 *
 *                                     State Machine
 *
 **************************************************************************************************/

/**
 * Event handler for HAL message
 * @param inst HAL instance
 * @param e HAL event
 */
static void Hal_event_handler(HalInstance* inst, HalEvent e)
{
    switch (e) {
        case EVT_RX_DATA: {
            // New data packet arrived
            const uint8_t* nciData;
            size_t nciLength;

            // Extract raw NCI data from frame
            nciData = inst->lastUsFrame;
            nciLength = inst->lastUsFrameSize;

            // Pass received raw NCI data to stack
            inst->callback(inst->context, HAL_EVENT_DATAIND, nciData, nciLength);
        }
        break;

        case EVT_TX_DATA:
            // NCI data arrived from stack
            // Send data
            inst->callback(inst->context, HAL_EVENT_DSWRITE, inst->nciBuffer->data,
                           inst->nciBuffer->length);

            // Free the buffer
            HalFreeBuffer(inst, inst->nciBuffer);
            inst->nciBuffer = 0;
            break;

        // HAL WRAPPER
        case EVT_TIMER:
            inst->callback(inst->context, HAL_EVENT_TIMER_TIMEOUT, NULL, 0);
            break;
    }
}

/**************************************************************************************************
 *
 *                                     HAL Worker Thread
 *
 **************************************************************************************************/

/**
 * HAL worker thread to serialize all actions into a single thread.
 * RX/TX/TIMER are dispatched from here.
 * @param arg HAL instance arguments
 */
static void* HalWorkerThread(void* arg)
{
    HalInstance* inst = (HalInstance*)arg;
    inst->exitRequest = false;

    STLOG_HAL_V("thread running\n");

    while (!inst->exitRequest) {
        struct timespec now = HalGetTimestamp();
        uint32_t waitResult =
            HalSemWait(&inst->semaphore, HalCalcSemWaitingTime(inst, &now));

        switch (waitResult) {
            case OS_SYNC_TIMEOUT: {
                // One or more times have expired
                STLOG_HAL_W("OS_SYNC_TIMEOUT\n");
                now = HalGetTimestamp();

                // HAL WRAPPER
                // callback to hal wrapper
                // Unblock
                sem_post(&inst->upstreamBlock);

                // Data frame
                Hal_event_handler(inst, EVT_TIMER);
            }
            break;

            case OS_SYNC_RELEASED: {
                // A message arrived
                ThreadMesssage msg;

                if (HalDequeueThreadMessage(inst, &msg)) {
                    switch (msg.command) {
                        case MSG_EXIT_REQUEST:

                            STLOG_HAL_V("received exit request from upper layer\n");
                            inst->exitRequest = true;
                            break;

                        case MSG_TX_DATA:
                            STLOG_HAL_V("received new NCI data from stack\n");

                            // Attack to end of list
                            if (!inst->pendingNciList) {
                                inst->pendingNciList = msg.buffer;
                                inst->pendingNciList->next = 0;
                            } else {
                                // Find last element of the list. b->next is zero for this
                                // element
                                HalBuffer* b;
                                for (b = inst->pendingNciList; b->next; b = b->next) {
                                };

                                // Concatenate to list
                                b->next = msg.buffer;
                                msg.buffer->next = 0;
                            }

                            // Start transmitting if we're in the correct state
                            HalTriggerNextDsPacket(inst);
                            break;

                        // HAL WRAPPER
                        case MSG_TX_DATA_TIMER_START:
                            STLOG_HAL_V("received new NCI data from stack, need timer start\n");

                            // Attack to end of list
                            if (!inst->pendingNciList) {
                                inst->pendingNciList = msg.buffer;
                                inst->pendingNciList->next = 0;
                            } else {
                                // Find last element of the list. b->next is zero for this
                                // element
                                HalBuffer* b;
                                for (b = inst->pendingNciList; b->next; b = b->next) {
                                };

                                // Concatenate to list
                                b->next = msg.buffer;
                                msg.buffer->next = 0;
                            }

                            // Start timer
                            HalStartTimer(inst, msg.length);

                            // Start transmitting if we're in the correct state
                            HalTriggerNextDsPacket(inst);
                            break;

                        case MSG_RX_DATA:
                            STLOG_HAL_D("received new data from CLF\n");
                            HalOnNewUpstreamFrame(inst, msg.payload, msg.length);
                            break;

                        default:
                            STLOG_HAL_E("!received unkown thread message?\n");
                            break;
                    }
                } else {
                    STLOG_HAL_E("!got wakeup in workerthread, but no message here? ?\n");

            }
            }
            break;

            case OS_SYNC_FAILED:

              STLOG_HAL_E(
                    "!Something went horribly wrong.. The semaphore wait function "
                    "failed\n");
                inst->exitRequest = true;
                break;
        }
    }

    STLOG_HAL_D("thread about to exit\n");
    return NULL;
}

/**************************************************************************************************
 *
 *                                     Misc. Functions
 *
 **************************************************************************************************/
/**
 *  helper to make sem_t interrupt safe
 * @param sem_t  semaphore
 * @return sem_wait return value.
 */

static inline int sem_wait_nointr(sem_t *sem) {
  while (sem_wait(sem))
    if (errno == EINTR) errno = 0;
    else return -1;
  return 0;
}

/**
 * Handle RX frames here first in HAL context.
 * @param inst HAL instance
 * @param data HAL data received from I2C worker thread
 * @param length Size of HAL data
 */
static void HalOnNewUpstreamFrame(HalInstance* inst, const uint8_t* data,
                                  size_t length)
{
    memcpy(inst->lastUsFrame, data, length);
    inst->lastUsFrameSize = length;

    // Data frame
    Hal_event_handler(inst, EVT_RX_DATA);
    // Allow the I2C thread to get the next message (if done early, it may
    // overwrite before handled)
    sem_post(&inst->upstreamBlock);
}

/**
 * Send out the next queued up buffer for TX if any.
 * @param inst HAL instance
 */
static void HalTriggerNextDsPacket(HalInstance* inst)
{
    // Check if we have something to transmit downstream
    HalBuffer* b = inst->pendingNciList;

    if (b) {
        // Get the buffer from the pending list
        inst->pendingNciList = b->next;
        inst->nciBuffer = b;

        STLOG_HAL_V("trigger transport of next NCI data downstream\n");
        // Process the new nci frame
        Hal_event_handler(inst, EVT_TX_DATA);

    } else {
        STLOG_HAL_V("no new NCI data to transmit, enter wait..\n");
    }
}

/*
 * Wait for given semaphore signaling a specific time or ever
 * param sem_t * pSemaphore
 * param uint32_t timeout
 * return uint32_t
 */
static uint32_t HalSemWait(sem_t* pSemaphore, uint32_t timeout)
{
    uint32_t result = OS_SYNC_RELEASED;
    bool gotResult = false;

    if (timeout == OS_SYNC_INFINITE) {
        while (!gotResult) {
            if (sem_wait(pSemaphore) == -1) {
                int e = errno;
                char msg[200];

                if (e == EINTR) {
                    STLOG_HAL_W(
                        "! semaphore (infin) wait interrupted by system signal. re-enter "
                        "wait");
                    continue;
                }

                strerror_r(e, msg, sizeof(msg) - 1);
                STLOG_HAL_E("! semaphore (infin) wait failed. sem=0x%p, %s", pSemaphore, msg);
                gotResult = true;
                result = OS_SYNC_FAILED;
            } else {
                gotResult = true;
            }
        };
    } else {
        struct timespec tm;
        long oneSecInNs = (int)1e9;

        clock_gettime(CLOCK_REALTIME, &tm);

        /* add timeout (can't overflow): */
        tm.tv_sec += (timeout / 1000);
        tm.tv_nsec += ((timeout % 1000) * 1000000);

        /* make sure nanoseconds are below a million */
        if (tm.tv_nsec >= oneSecInNs) {
            tm.tv_sec++;
            tm.tv_nsec -= oneSecInNs;
        }

        while (!gotResult) {
            if (sem_timedwait(pSemaphore, &tm) == -1) {
                int e = errno;

                if (e == EINTR) {
                    /* interrupted by signal? repeat sem_wait again */
                    continue;
                }

                if (e == ETIMEDOUT) {
                    result = OS_SYNC_TIMEOUT;
                    gotResult = true;
                } else {
                    result = OS_SYNC_FAILED;
                    gotResult = true;
                }
            } else {
                gotResult = true;
            }
        }
    }
    return result;
}