blob: 4dd3caab209e8c4c6c979ff5ddae522446f57a28 [file] [log] [blame]
/*
* Copyright 2017 NXP
* All rights reserved.
*
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include "fsl_log.h"
#include "fsl_debug_console_conf.h"
#include "fsl_io.h"
#ifdef FSL_RTOS_FREE_RTOS
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#endif
/*******************************************************************************
* Definitions
******************************************************************************/
#ifndef BACKSPACE
/*! @brief character backspace ASCII value */
#define BACKSPACE 127
#endif
#ifdef DEBUG_CONSOLE_TRANSFER_NON_BLOCKING
/*! @brief increase pop member */
#define LOG_CHECK_BUFFER_INDEX_OVERFLOW(index) \
{ \
if (index >= DEBUG_CONSOLE_TRANSMIT_BUFFER_LEN) \
{ \
index -= DEBUG_CONSOLE_TRANSMIT_BUFFER_LEN; \
} \
\
\
}
/*! @brief get current runing environment is ISR or not */
#ifdef __CA7_REV
#define IS_RUNNING_IN_ISR() SystemGetIRQNestingLevel()
#else
#define IS_RUNNING_IN_ISR() __get_IPSR()
#endif /* __CA7_REV */
#else
#define IS_RUNNING_IN_ISR() (0U)
#endif /* DEBUG_CONSOLE_TRANSFER_NON_BLOCKING */
/* define for rtos */
#if (DEBUG_CONSOLE_SYNCHRONIZATION_MODE == DEBUG_CONSOLE_SYNCHRONIZATION_FREERTOS)
/* metex semaphore */
#define LOG_CREATE_MUTEX_SEMAPHORE(mutex) (mutex = xSemaphoreCreateMutex())
#define LOG_GIVE_MUTEX_SEMAPHORE(mutex) \
\
{ \
if (IS_RUNNING_IN_ISR() == 0U) \
{ \
xSemaphoreGive(mutex); \
} \
\
}
#define LOG_TAKE_MUTEX_SEMAPHORE_BLOCKING(mutex) \
\
{ \
if (IS_RUNNING_IN_ISR() == 0U) \
{ \
xSemaphoreTake(mutex, portMAX_DELAY); \
} \
\
}
#define LOG_TAKE_MUTEX_SEMAPHORE_NONBLOCKING(mutex, result) \
\
{ \
if (IS_RUNNING_IN_ISR() == 0U) \
{ \
result = xSemaphoreTake(mutex, 0U); \
} \
else \
{ \
result = 1U; \
} \
\
}
/* Binary semaphore */
#define LOG_CREATE_BINARY_SEMAPHORE(binary) (binary = xSemaphoreCreateBinary())
#define LOG_TAKE_BINARY_SEMAPHORE_BLOCKING(binary) (xSemaphoreTake(binary, portMAX_DELAY))
#define LOG_GIVE_BINARY_SEMAPHORE_FROM_ISR(binary) (xSemaphoreGiveFromISR(binary, NULL))
#elif(DEBUG_CONSOLE_SYNCHRONIZATION_MODE == DEBUG_CONSOLE_SYNCHRONIZATION_BM)
#define LOG_CREATE_MUTEX_SEMAPHORE(mutex)
#define LOG_TAKE_MUTEX_SEMAPHORE_BLOCKING(mutex)
#define LOG_GIVE_MUTEX_SEMAPHORE(mutex)
#define LOG_CREATE_BINARY_SEMAPHORE(binary)
#define LOG_TAKE_MUTEX_SEMAPHORE_NONBLOCKING(mutex, result) (result = 1U)
#ifdef DEBUG_CONSOLE_TRANSFER_NON_BLOCKING
#define LOG_TAKE_BINARY_SEMAPHORE_BLOCKING(binary) \
\
{ \
while (!binary) \
; \
binary = false; \
\
\
}
#define LOG_GIVE_BINARY_SEMAPHORE_FROM_ISR(binary) (binary = true)
#else
#define LOG_TAKE_BINARY_SEMAPHORE_BLOCKING(binary)
#define LOG_GIVE_BINARY_SEMAPHORE_FROM_ISR(binary)
#endif /* DEBUG_CONSOLE_TRANSFER_NON_BLOCKING */
/* add other implementation here
*such as :
* #elif(DEBUG_CONSOLE_SYNCHRONIZATION_MODE == DEBUG_CONSOLE_SYNCHRONIZATION_xxx)
*/
#else
#define LOG_CREATE_MUTEX_SEMAPHORE(mutex)
#define LOG_TAKE_MUTEX_SEMAPHORE_BLOCKING(mutex)
#define LOG_TAKE_MUTEX_SEMAPHORE_NONBLOCKING(mutex, result) (result = 1U)
#define LOG_GIVE_MUTEX_SEMAPHORE(mutex)
#define LOG_CREATE_BINARY_SEMAPHORE(binary)
#define LOG_TAKE_BINARY_SEMAPHORE_BLOCKING(binary)
#define LOG_GIVE_BINARY_SEMAPHORE(binary)
#endif /* DEBUG_CONSOLE_SYNCHRONIZATION_MODE == DEBUG_CONSOLE_SYNCHRONIZATION_FREERTOS */
#ifdef DEBUG_CONSOLE_TRANSFER_NON_BLOCKING
/*! @brief Define the buffer
* The total buffer size should be calucate as (BUFFER_SUPPORT_LOG_LENGTH + 1) * BUFFER_SUPPORT_LOG_NUM * 4
*/
typedef struct _log_buffer
{
volatile uint16_t totalIndex; /*!< indicate the total usage of the buffer */
volatile uint16_t pushIndex; /*!< indicate the next push index */
volatile uint16_t popIndex; /*!< indicate the pop index */
uint8_t txBuf[DEBUG_CONSOLE_TRANSMIT_BUFFER_LEN]; /*!< buffer to store printf log */
uint8_t rxBuf[DEBUG_CONSOLE_RECEIVE_BUFFER_LEN]; /*!< buffer to store scanf log */
} log_buffer_t;
#endif /* DEBUG_CONSOLE_TRANSFER_NON_BLOCKING */
/*******************************************************************************
* Variables
******************************************************************************/
#ifdef DEBUG_CONSOLE_TRANSFER_NON_BLOCKING
/* A global log buffer */
static log_buffer_t s_log_buffer;
#endif /* DEBUG_CONSOLE_TRANSFER_NON_BLOCKING */
/* lock definition */
#if (DEBUG_CONSOLE_SYNCHRONIZATION_MODE == DEBUG_CONSOLE_SYNCHRONIZATION_FREERTOS)
#ifdef DEBUG_CONSOLE_TRANSFER_NON_BLOCKING
static SemaphoreHandle_t s_logPushSemaphore = NULL;
#endif /* DEBUG_CONSOLE_TRANSFER_NON_BLOCKING */
static SemaphoreHandle_t s_logPopSemaphore = NULL;
static SemaphoreHandle_t s_logReadSemaphore = NULL;
#elif(DEBUG_CONSOLE_SYNCHRONIZATION_MODE == DEBUG_CONSOLE_SYNCHRONIZATION_BM)
#ifdef DEBUG_CONSOLE_TRANSFER_NON_BLOCKING
static volatile bool s_logReadSemaphore = false; /* transferred event from ISR for bare-metal + interrupt */
#endif /* DEBUG_CONSOLE_TRANSFER_NON_BLOCKING */
#else
#endif /* DEBUG_CONSOLE_TRANSFER_NON_BLOCKING */
/*******************************************************************************
* Prototypes
******************************************************************************/
#ifdef DEBUG_CONSOLE_TRANSFER_NON_BLOCKING
/*!
* @brief callback function for IO layer to notify LOG
*
* @param size last transfer data size
* @param receive indicate a RX transfer
* @param transmit indicate a TX transfer
*
*/
static void LOG_Transferred(size_t *size, bool receive, bool transmit);
/*!
* @brief log push function
*
* @param buf target buffer
* @param size log size
*
*/
static int LOG_BufPush(uint8_t *buf, size_t size);
/*!
* @brief Get next avaliable log
*
* @param next avaliable size
* @return next avaliable address
*/
static uint8_t *LOG_BufGetNextAvaliableLog(size_t *size);
/*!
* @brief buf pop
*
* @param size log size popped and next available log size
* @return next avaliable address
*/
static uint8_t *LOG_BufPop(size_t *size);
#endif /* DEBUG_CONSOLE_TRANSFER_NON_BLOCKING */
/*!
* @brief read one character
*
* @param ch character address
* @return indicate the read status
*
*/
static status_t LOG_ReadOneCharacter(uint8_t *ch);
#if DEBUG_CONSOLE_ENABLE_ECHO_FUNCTION
/*!
* @brief echo one character
*
* @param ch character address
* @param isGetchar flag to distinguish getchar from scanf
* @param index special for scanf to support backspace
* @return indicate the read status
*
*/
static status_t LOG_EchoCharacter(uint8_t *ch, bool isGetChar, int *index);
#endif
/*******************************************************************************
* Code
******************************************************************************/
status_t LOG_Init(uint32_t baseAddr, uint8_t device, uint32_t baudRate, uint32_t clkSrcFreq)
{
io_state_t io;
/* init io */
io.ioBase = (void *)baseAddr;
io.ioType = device;
#ifdef DEBUG_CONSOLE_TRANSFER_NON_BLOCKING
/* memset the global queue */
memset(&s_log_buffer, 0U, sizeof(s_log_buffer));
/* init callback for NON-BLOCKING */
io.callBack = LOG_Transferred;
/* io init function */
IO_Init(&io, baudRate, clkSrcFreq, s_log_buffer.rxBuf);
/* Debug console buffer push lock create */
LOG_CREATE_MUTEX_SEMAPHORE(s_logPushSemaphore);
#else
IO_Init(&io, baudRate, clkSrcFreq, NULL);
#endif /* DEBUG_CONSOLE_TRANSFER_NON_BLOCKING */
/* Debug console lock create */
LOG_CREATE_MUTEX_SEMAPHORE(s_logPopSemaphore);
LOG_CREATE_BINARY_SEMAPHORE(s_logReadSemaphore);
return kStatus_Success;
}
void LOG_Deinit(void)
{
#ifdef DEBUG_CONSOLE_TRANSFER_NON_BLOCKING
/* memset the global queue */
memset(&s_log_buffer, 0U, sizeof(s_log_buffer));
#endif /*DEBUG_CONSOLE_TRANSFER_NON_BLOCKING*/
/* Deinit IO */
IO_Deinit();
}
status_t LOG_WaitIdle(void)
{
#ifdef DEBUG_CONSOLE_TRANSFER_NON_BLOCKING
/* wait buffer empty */
while (!(s_log_buffer.totalIndex == 0U))
;
#endif /*DEBUG_CONSOLE_TRANSFER_NON_BLOCKING*/
/* wait IO idle */
IO_WaitIdle();
return kStatus_Success;
}
int LOG_Push(uint8_t *buf, size_t size)
{
assert(buf != NULL);
#ifdef DEBUG_CONSOLE_TRANSFER_NON_BLOCKING
/* push to buffer */
LOG_BufPush(buf, size);
buf = LOG_BufGetNextAvaliableLog(&size);
#endif /* DEBUG_CONSOLE_TRANSFER_NON_BLOCKING */
/* pop log */
return LOG_Pop(buf, size);
}
int LOG_Pop(uint8_t *buf, size_t size)
{
uint8_t getLock = 0U;
if ((0 != size) && (NULL != buf))
{
/* take POP lock, should be non-blocking */
LOG_TAKE_MUTEX_SEMAPHORE_NONBLOCKING(s_logPopSemaphore, getLock);
if (getLock)
{
/* call IO transfer function */
if (IO_Transfer(buf, size, true) != kStatus_Success)
{
size = 0U;
}
/* release POP lock */
LOG_GIVE_MUTEX_SEMAPHORE(s_logPopSemaphore);
}
}
return size;
}
int LOG_ReadLine(uint8_t *buf, size_t size)
{
assert(buf != NULL);
int i = 0;
/* take mutex lock function */
LOG_TAKE_MUTEX_SEMAPHORE_BLOCKING(s_logPushSemaphore);
for (i = 0; i < size; i++)
{
/* recieve one char every time */
if (LOG_ReadOneCharacter(&buf[i]) != kStatus_Success)
{
return -1;
}
#if DEBUG_CONSOLE_ENABLE_ECHO_FUNCTION
LOG_EchoCharacter(&buf[i], false, &i);
#endif
/* analysis data */
if ((buf[i] == '\r') || (buf[i] == '\n'))
{
/* End of Line. */
if (i == 0)
{
buf[i] = '\0';
i = -1;
}
else
{
break;
}
}
}
/* get char should not add '\0'*/
if (i == size)
{
buf[i] = '\0';
}
else
{
buf[i + 1] = '\0';
}
/* release mutex lock function */
LOG_GIVE_MUTEX_SEMAPHORE(s_logPushSemaphore);
return i;
}
int LOG_ReadCharacter(uint8_t *ch)
{
assert(ch != NULL);
int ret = 0;
/* take mutex lock function */
LOG_TAKE_MUTEX_SEMAPHORE_BLOCKING(s_logPushSemaphore);
/* read one character */
if (LOG_ReadOneCharacter(ch) == kStatus_Success)
{
ret = 1;
#if DEBUG_CONSOLE_ENABLE_ECHO_FUNCTION
LOG_EchoCharacter(ch, true, NULL);
#endif
}
else
{
ret = -1;
}
/* release mutex lock function */
LOG_GIVE_MUTEX_SEMAPHORE(s_logPushSemaphore);
return ret;
}
static status_t LOG_ReadOneCharacter(uint8_t *ch)
{
/* recieve one char every time */
if (IO_Transfer(ch, 1U, false) != kStatus_Success)
{
return kStatus_Fail;
}
/* wait release from ISR */
LOG_TAKE_BINARY_SEMAPHORE_BLOCKING(s_logReadSemaphore);
return kStatus_Success;
}
#if DEBUG_CONSOLE_ENABLE_ECHO_FUNCTION
static status_t LOG_EchoCharacter(uint8_t *ch, bool isGetChar, int *index)
{
/* Due to scanf take \n and \r as end of string,should not echo */
if (((*ch != '\r') && (*ch != '\n')) || (isGetChar))
{
/* recieve one char every time */
if (IO_Transfer(ch, 1U, true) != kStatus_Success)
{
return kStatus_Fail;
}
}
if (!isGetChar)
{
if ((*index > 0) && (*ch == BACKSPACE))
{
*index -= 2;
}
}
return kStatus_Success;
}
#endif
#ifdef DEBUG_CONSOLE_TRANSFER_NON_BLOCKING
static int LOG_BufPush(uint8_t *buf, size_t size)
{
uint32_t pushIndex = 0U, i = 0U;
bool pushAvaliable = false;
/* take mutex lock function */
LOG_TAKE_MUTEX_SEMAPHORE_BLOCKING(s_logPushSemaphore);
if (size <= (DEBUG_CONSOLE_TRANSMIT_BUFFER_LEN - s_log_buffer.totalIndex))
{
/* get push index */
pushIndex = s_log_buffer.pushIndex;
s_log_buffer.pushIndex += size;
/* check index overflow */
LOG_CHECK_BUFFER_INDEX_OVERFLOW(s_log_buffer.pushIndex);
/* update push/total index value */
s_log_buffer.totalIndex += size;
pushAvaliable = true;
}
/* release mutex lock function */
LOG_GIVE_MUTEX_SEMAPHORE(s_logPushSemaphore);
/* check the buffer if have enough space to store the log */
if (pushAvaliable)
{
for (i = size; i > 0; i--)
{
/* copy log to buffer, the buffer only support a fixed length argument, if the log argument
is longer than the fixed length, the left argument will be losed */
s_log_buffer.txBuf[pushIndex] = *buf++;
/* increase index */
pushIndex++;
/* check index overflow */
LOG_CHECK_BUFFER_INDEX_OVERFLOW(pushIndex);
}
}
else
{
size = 0U;
}
return size;
}
static uint8_t *LOG_BufGetNextAvaliableLog(size_t *size)
{
uint16_t popIndex = s_log_buffer.popIndex;
/* get avaliable size */
if (s_log_buffer.totalIndex > (DEBUG_CONSOLE_TRANSMIT_BUFFER_LEN - popIndex))
{
*size = (DEBUG_CONSOLE_TRANSMIT_BUFFER_LEN - popIndex);
}
else
{
*size = s_log_buffer.totalIndex;
}
/* return address */
return (&(s_log_buffer.txBuf[popIndex]));
}
static uint8_t *LOG_BufPop(size_t *size)
{
if (s_log_buffer.totalIndex >= *size)
{
/* decrease the log total member */
s_log_buffer.totalIndex -= *size;
/* there is more log in the queue to be pushed */
if (s_log_buffer.totalIndex > 0U)
{
/* update the pop index */
s_log_buffer.popIndex += *size;
/* check index overflow */
LOG_CHECK_BUFFER_INDEX_OVERFLOW(s_log_buffer.popIndex);
return LOG_BufGetNextAvaliableLog(size);
}
else
{
/* reset push and pop */
s_log_buffer.popIndex = 0U;
s_log_buffer.pushIndex = 0U;
*size = 0U;
}
}
return NULL;
}
static void LOG_Transferred(size_t *size, bool receive, bool transmit)
{
uint8_t *addr = NULL;
if (transmit)
{
addr = LOG_BufPop(size);
/* continue pop log from buffer */
LOG_Pop(addr, *size);
}
if (receive)
{
/* release from ISR */
LOG_GIVE_BINARY_SEMAPHORE_FROM_ISR(s_logReadSemaphore);
}
}
#endif /* DEBUG_CONSOLE_TRANSFER_NON_BLOCKING */