blob: e46fce840725291027bbfe03fd8b7834ec00a4d9 [file] [log] [blame]
/*
*
* Copyright (c) 2021 Project CHIP Authors
* Copyright 2023 NXP
*
* 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.
*/
/**
* @file
* Source implementation of an input / output stream for NXP targets.
*/
/* -------------------------------------------------------------------------- */
/* Includes */
/* -------------------------------------------------------------------------- */
#include <lib/shell/Engine.h>
#include <lib/shell/streamer.h>
#include <stdio.h>
#include <string.h>
#include "board.h"
#include "fsl_clock.h"
#include "fsl_component_serial_manager.h"
#include "fsl_os_abstraction.h"
#if CHIP_ENABLE_OPENTHREAD
#include "openthread-system.h"
#endif
/* -------------------------------------------------------------------------- */
/* Private macros */
/* -------------------------------------------------------------------------- */
#ifndef STREAMER_UART_SERIAL_MANAGER_RING_BUFFER_SIZE
#define STREAMER_UART_SERIAL_MANAGER_RING_BUFFER_SIZE (128U)
#endif
#ifndef STREAMER_UART_FLUSH_DELAY_MS
#define STREAMER_UART_FLUSH_DELAY_MS 2
#endif
#ifndef BOARD_APP_UART_INSTANCE
#if defined(RW612_SERIES) || defined(RW610_SERIES)
#define BOARD_APP_UART_INSTANCE 3
#else
#define BOARD_APP_UART_INSTANCE 1
#endif
#endif
#ifndef BOARD_APP_UART_CLK_FREQ
#if defined(RW612_SERIES) || defined(RW610_SERIES)
#define BOARD_APP_UART_CLK_FREQ CLOCK_GetFlexCommClkFreq(3)
#else
#define BOARD_APP_UART_CLK_FREQ BOARD_BT_UART_CLK_FREQ
#endif
#endif
// pw RPC uses UART DMA by default
#ifdef PW_RPC_ENABLED
#define CONSUMER_TASK_HANDLE RpcTaskHandle
#ifndef STREAMER_UART_USE_DMA
#define STREAMER_UART_USE_DMA 1
#endif
#else
#define CONSUMER_TASK_HANDLE AppMatterCliTaskHandle
#ifndef STREAMER_UART_USE_DMA
#define STREAMER_UART_USE_DMA 0
#endif
#endif // PW_RPC_ENABLED
#if STREAMER_UART_USE_DMA
typedef serial_port_uart_dma_config_t streamer_serial_port_uart_config_t;
#else
typedef serial_port_uart_config_t streamer_serial_port_uart_config_t;
#endif
/* -------------------------------------------------------------------------- */
/* Private prototypes */
/* -------------------------------------------------------------------------- */
static void Uart_RxCallBack(void * pData, serial_manager_callback_message_t * message, serial_manager_status_t status);
static void Uart_TxCallBack(void * pBuffer, serial_manager_callback_message_t * message, serial_manager_status_t status);
/* -------------------------------------------------------------------------- */
/* Private memory */
/* -------------------------------------------------------------------------- */
static SERIAL_MANAGER_HANDLE_DEFINE(streamerSerialHandle);
static SERIAL_MANAGER_WRITE_HANDLE_DEFINE(streamerSerialWriteHandle);
static SERIAL_MANAGER_READ_HANDLE_DEFINE(streamerSerialReadHandle);
static volatile int txCount = 0;
volatile static bool readDone = true;
static streamer_serial_port_uart_config_t uartConfig = { .clockRate = 0,
.baudRate = BOARD_DEBUG_UART_BAUDRATE,
.parityMode = kSerialManager_UartParityDisabled,
.stopBitCount = kSerialManager_UartOneStopBit,
.enableRx = 1,
.enableTx = 1,
.enableRxRTS = 0,
.enableTxCTS = 0,
.instance = BOARD_APP_UART_INSTANCE,
#if STREAMER_UART_USE_DMA
.dma_instance = 0,
.rx_channel = 1,
.tx_channel = 0
#endif
};
static uint8_t s_ringBuffer[STREAMER_UART_SERIAL_MANAGER_RING_BUFFER_SIZE];
static const serial_manager_config_t s_serialManagerConfig = {
.ringBuffer = &s_ringBuffer[0],
.ringBufferSize = STREAMER_UART_SERIAL_MANAGER_RING_BUFFER_SIZE,
#if STREAMER_UART_USE_DMA
.type = kSerialPort_UartDma,
#else
.type = BOARD_DEBUG_UART_TYPE,
#endif
.blockType = kSerialManager_NonBlocking,
.portConfig = (serial_port_uart_config_t *) &uartConfig,
};
OSA_MUTEX_HANDLE_DEFINE(streamerMutex);
/* -------------------------------------------------------------------------- */
/* Public functions */
/* -------------------------------------------------------------------------- */
namespace chip {
namespace Shell {
int streamer_nxp_init(streamer_t * streamer)
{
(void) streamer;
serial_manager_status_t status = kStatus_SerialManager_Error;
#if defined(RW612_SERIES) || defined(RW610_SERIES)
/* attach FRG3 clock to FLEXCOMM3 */
BOARD_CLIAttachClk();
#endif
uartConfig.clockRate = BOARD_APP_UART_CLK_FREQ;
#if STREAMER_UART_USE_DMA
dma_channel_mux_configure_t dma_channel_mux;
dma_channel_mux.dma_dmamux_configure.dma_tx_channel_mux = kDmaRequestLPUART1Tx;
dma_channel_mux.dma_dmamux_configure.dma_rx_channel_mux = kDmaRequestLPUART1Rx;
uartConfig.dma_channel_mux_configure = &dma_channel_mux;
#endif
/*
* Make sure to disable interrupts while initializating the serial manager interface
* Some issues could happen if a UART IRQ is firing during serial manager initialization
*/
OSA_InterruptDisable();
do
{
if (SerialManager_Init((serial_handle_t) streamerSerialHandle, &s_serialManagerConfig) != kStatus_SerialManager_Success)
break;
if (SerialManager_OpenWriteHandle((serial_handle_t) streamerSerialHandle,
(serial_write_handle_t) streamerSerialWriteHandle) != kStatus_SerialManager_Success)
break;
if (SerialManager_OpenReadHandle((serial_handle_t) streamerSerialHandle, (serial_read_handle_t) streamerSerialReadHandle) !=
kStatus_SerialManager_Success)
break;
if (SerialManager_InstallRxCallback((serial_read_handle_t) streamerSerialReadHandle, Uart_RxCallBack, NULL) !=
kStatus_SerialManager_Success)
break;
if (SerialManager_InstallTxCallback((serial_write_handle_t) streamerSerialWriteHandle, Uart_TxCallBack, NULL) !=
kStatus_SerialManager_Success)
break;
status = kStatus_SerialManager_Success;
} while (0);
OSA_InterruptEnable();
osa_status_t status_osa = OSA_MutexCreate((osa_mutex_handle_t) streamerMutex);
assert(status_osa == KOSA_StatusSuccess);
return status;
}
ssize_t streamer_nxp_read(streamer_t * streamer, char * buffer, size_t length)
{
uint32_t bytesRead = 0;
serial_manager_status_t status = kStatus_SerialManager_Success;
if (length != 0)
{
/**
* If the reading process is over,
* let CLI Task enter blocked state until notification
**/
if (readDone)
{
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
readDone = false;
}
status = SerialManager_TryRead((serial_read_handle_t) streamerSerialReadHandle, (uint8_t *) buffer, length, &bytesRead);
assert(status != kStatus_SerialManager_Error);
/**
* In certain cases such as a copy-paste of multiple commands, we may encounter '\n' or '\r' caracters
* although the buffer is not empty yet, so the reading process should be considered done only when the
* bytesRead return null,
* this is to ensure that all commands are processed before blocking the CLI task
*
**/
if (bytesRead == 0)
{
readDone = true;
}
}
return bytesRead;
}
ssize_t streamer_nxp_write(streamer_t * streamer, const char * buffer, size_t length)
{
uint32_t intMask;
serial_manager_status_t status = kStatus_SerialManager_Success;
size_t len = 0;
/* Mutex lock to ensure the streamer write is accessed by only one task at a time */
osa_status_t status_osa = OSA_MutexLock(streamerMutex, osaWaitForever_c);
// If length is 0 there will be an assert in Serial Manager. Some OT functions output 0 bytes, for example
// in SrpServer::Process<Cmd("service")> -> OutputLine(hasSubType ? "" : "(null)");
if (length > 0)
{
intMask = DisableGlobalIRQ();
txCount++;
status = SerialManager_WriteNonBlocking((serial_write_handle_t) streamerSerialWriteHandle, (uint8_t *) buffer,
(uint32_t) length);
EnableGlobalIRQ(intMask);
if (status == kStatus_SerialManager_Success)
{
len = length;
}
/* Wait for the serial manager task to empty the TX buffer */
while (txCount)
{
OSA_TimeDelay(STREAMER_UART_FLUSH_DELAY_MS);
}
}
status_osa = OSA_MutexUnlock(streamerMutex);
assert(status_osa == KOSA_StatusSuccess);
return len;
}
static streamer_t streamer_nxp = {
.init_cb = streamer_nxp_init,
.read_cb = streamer_nxp_read,
.write_cb = streamer_nxp_write,
};
streamer_t * streamer_get(void)
{
return &streamer_nxp;
}
} // namespace Shell
} // namespace chip
/* -------------------------------------------------------------------------- */
/* Private functions */
/* -------------------------------------------------------------------------- */
extern TaskHandle_t CONSUMER_TASK_HANDLE;
static void Uart_RxCallBack(void * pData, serial_manager_callback_message_t * message, serial_manager_status_t status)
{
if (CONSUMER_TASK_HANDLE != NULL)
{
/* notify the main loop that a RX buffer is available */
xTaskNotifyGive(CONSUMER_TASK_HANDLE);
}
}
static void Uart_TxCallBack(void * pBuffer, serial_manager_callback_message_t * message, serial_manager_status_t status)
{
txCount--;
}