/*
 * Copyright 2018, NXP
 * All rights reserved.
 *
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "srtm_message_pool.h"
#include "srtm_heap.h"
#include "srtm_list.h"
#include "fsl_common.h"

/*******************************************************************************
 * Definitions
 ******************************************************************************/
/* Simple algorithm:
 * Here we suppose most SRTM messages data are small.
 * By default we set each message buffer to 96 (0x60) bytes (including struct _srtm_message
 * which occupies 52 bytes). So we have 44 bytes for the SRTM message data (10bytes header +
 * 34 bytes payload which is sufficient for all current SRTM category).
 */
/* Total buffer size for messages in the pool. */
#ifndef SRTM_MESSAGE_POOL_SIZE
#define SRTM_MESSAGE_POOL_SIZE (0x1000)
#endif

/* Each message buffer size */
#ifndef SRTM_MESSAGE_BUF_SIZE
#define SRTM_MESSAGE_BUF_SIZE (0x60)
#endif

/*******************************************************************************
 * Prototypes
 ******************************************************************************/
/* Define a structure to hold SRTM_MESSAGE_BUF_SIZE buffer. */
typedef struct
{
    srtm_list_t node;
    uint8_t buf[SRTM_MESSAGE_BUF_SIZE - sizeof(srtm_list_t)];
} srtm_message_buf_t;

/*******************************************************************************
 * Variables
 ******************************************************************************/
static srtm_message_buf_t srtmMsgs[SRTM_MESSAGE_POOL_SIZE / sizeof(srtm_message_buf_t)];
static srtm_list_t srtmMsgList;
#ifdef SRTM_DEBUG_MESSAGE_FUNC
/* Used for probe current and minimum free message buffer count in debugger. */
static uint32_t freeMsgCount;
static uint32_t minFreeMsgCount;
#endif

/*******************************************************************************
 * Code
 ******************************************************************************/
void *SRTM_MessagePool_Alloc(uint32_t size)
{
    uint32_t i;
    void *buf;
    uint32_t primask;

    if (!srtmMsgList.next)
    {
        primask = DisableGlobalIRQ();
        if (!srtmMsgList.next)
        {
            /* Message list not initialized, initialize now */
            SRTM_List_Init(&srtmMsgList);
            for (i = 0; i < sizeof(srtmMsgs) / sizeof(srtm_message_buf_t); i++)
            {
                SRTM_List_AddTail(&srtmMsgList, &srtmMsgs[i].node);
            }
#ifdef SRTM_DEBUG_MESSAGE_FUNC
            freeMsgCount    = sizeof(srtmMsgs) / sizeof(srtm_message_buf_t);
            minFreeMsgCount = freeMsgCount;
#endif
        }
        EnableGlobalIRQ(primask);
    }

    if (size > sizeof(srtm_message_buf_t))
    {
        SRTM_DEBUG_MESSAGE(SRTM_DEBUG_VERBOSE_INFO,
                           "Message size larger than SRTM_MESSAGE_BUF_SIZE %d, allocated in heap.\r\n",
                           SRTM_MESSAGE_BUF_SIZE);
        buf = SRTM_Heap_Malloc(size);
    }
    else
    {
        primask = DisableGlobalIRQ();
        if (SRTM_List_IsEmpty(&srtmMsgList))
        {
            EnableGlobalIRQ(primask);
            SRTM_DEBUG_MESSAGE(SRTM_DEBUG_VERBOSE_WARN, "Message pool (size %d) used up, allocated in heap.\r\n",
                               SRTM_MESSAGE_POOL_SIZE);
            buf = SRTM_Heap_Malloc(size);
        }
        else
        {
            buf = (void *)srtmMsgList.next;
            SRTM_List_Remove(srtmMsgList.next);
#ifdef SRTM_DEBUG_MESSAGE_FUNC
            freeMsgCount--;
            if (freeMsgCount < minFreeMsgCount)
            {
                minFreeMsgCount = freeMsgCount;
            }
#endif
            EnableGlobalIRQ(primask);
        }
    }

    return buf;
}

void SRTM_MessagePool_Free(void *buf)
{
    srtm_message_buf_t *msgBuf;
    uint32_t primask;

    if ((uint32_t *)buf >= (uint32_t *)&srtmMsgs[0] &&
        (uint32_t *)buf < (uint32_t *)&srtmMsgs[sizeof(srtmMsgs) / sizeof(srtm_message_buf_t)])
    {
        /* buffer locates in message pool */
        assert(((uint32_t)buf - (uint32_t)&srtmMsgs[0]) % sizeof(srtm_message_buf_t) == 0);
        msgBuf  = (srtm_message_buf_t *)buf;
        primask = DisableGlobalIRQ();
        SRTM_List_AddTail(&srtmMsgList, &msgBuf->node);
#ifdef SRTM_DEBUG_MESSAGE_FUNC
        freeMsgCount++;
#endif
        EnableGlobalIRQ(primask);
    }
    else
    {
        SRTM_Heap_Free(buf);
    }
}
