488 lines
17 KiB
C
488 lines
17 KiB
C
/*
|
|
* Copyright (c) CompanyNameMagicTag 2018-2020. All rights reserved.
|
|
* Description: BT CODELOADER UART MODULE
|
|
* Author:
|
|
* Create: 2018-10-15
|
|
*/
|
|
|
|
#include "non_os.h"
|
|
#include "uart.h"
|
|
#include "codeloader_common.h" // codeloader_common_generic_buffer_t
|
|
#include "codeloader_uart.h"
|
|
#include <string.h> // memcpy
|
|
#ifdef SUPPORT_ONE_LINE_UART_BURN
|
|
#include "chip_io.h"
|
|
#endif
|
|
|
|
/** Codeloader UART baud rate Configuration */
|
|
#define CODELOADER_UART_DEFAULT_BAUD_RATE 115200
|
|
/** Codeloader UART data bits configuration */
|
|
#define CODELOADER_UART_DATA_BITS UART_DATA_BIT_8
|
|
/** Codeloader UART parity mode configuration */
|
|
#define CODELOADER_UART_PARITY UART_PARITY_NONE
|
|
/** Codeloader UART stop bits configuration */
|
|
#define CODELOADER_UART_STOP_BITS UART_STOP_BIT_1
|
|
/** Codeloader UART Transmission PIN to use */
|
|
#define CODELOADER_UART_CTS_PIN PIN_NONE
|
|
/** Codeloader UART Reception PIN to use */
|
|
#define CODELOADER_UART_RTS_PIN PIN_NONE
|
|
/** Codeloader UART Transmission buffer size (bytes) */
|
|
#define CODELOADER_UART_TX_MAX_BUFFER_SIZE CODELOADER_TX_BUFFER_SIZE // Maximum output message must fit here
|
|
/** Codeloader UART Internal Reception buffer size (bytes) */
|
|
#define CODELOADER_UART_RX_MAX_BUFFER_SIZE 1 // Set to 1 because we want to check every byte
|
|
/** Beginning of frame flag */
|
|
#define CODELOADER_UART_FLAG_CHAR_START 's'
|
|
/** End of frame flag */
|
|
#define CODELOADER_UART_FLAG_CHAR_END 'e'
|
|
/** Escape flag */
|
|
#define CODELOADER_UART_FLAG_CHAR_ESCAPE 0x5C // 0x5C = '\'
|
|
#define NIBBLE_LENGTH 10
|
|
#define BIN_BYTE_RIGHT_SHIFT_BIT 4
|
|
#define HEX_LENGTH 2
|
|
|
|
/**
|
|
* Codeloader Transmission Buffer Type
|
|
*/
|
|
//lint -esym(754, codeloader_tx_buf_t::buffer_size)
|
|
typedef struct {
|
|
volatile bool in_use;
|
|
volatile uint16_t buffer_size;
|
|
uint8_t buffer[CODELOADER_UART_TX_MAX_BUFFER_SIZE];
|
|
} __attribute__((packed)) codeloader_tx_buf_t;
|
|
|
|
|
|
/** Codeloader UART Internal Reception buffer */
|
|
static codeloader_tx_buf_t g_codeloader_tx_uart_buffer __attribute__((aligned(4)));
|
|
/** Codeloader Callback to invoke when an event has ocurred in the line or a valid frame has been received */
|
|
static CODELOADER_UART_RX_DATA_CALLBACK g_codeloader_uart_received_data_callback;
|
|
/** Codeloader Callback to invoke when the data to send has been sent completelly */
|
|
static CODELOADER_UART_TX_DATA_CALLBACK g_codeloader_uart_sent_data_callback;
|
|
/** Codeloader UART Handle to interact with the driver */
|
|
static uart_bus_t g_codeload_uart = CODELOADER_UART_BUS;
|
|
/** Pointer to the external RX buffer */
|
|
static codeloader_rx_buffer_t *g_cl_uart_ex_rx_buffer;
|
|
/** External RX buffer size */
|
|
static uint32_t g_codeloader_uart_external_rx_buffer_size;
|
|
/** Internal buffer for the driver to write the data received */
|
|
static uint8_t g_codeloader_uart_internal_buffer[CODELOADER_UART_RX_MAX_BUFFER_SIZE];
|
|
/** Buffer pointing to the last data sent */
|
|
static codeloader_common_generic_buffer_t g_codeloader_uart_last_data_sent;
|
|
/** Flag to indicate if the reception is enabled or not. If it is not enabled any received data will be ignored. */
|
|
static bool g_codeloader_uart_reception_enabled;
|
|
#ifdef SUPPORT_ONE_LINE_UART_BURN
|
|
static bool g_codeloader_first_msg = true;
|
|
static bool g_codeloader_one_line_callback = false;
|
|
#endif
|
|
/*
|
|
* Private function declarations
|
|
*/
|
|
/**
|
|
* Callback to handle the data received from the uart driver
|
|
* @param buffer buffer received
|
|
* @param length length of the data received
|
|
* @param error indicates if an error has been detected in the uart
|
|
*/
|
|
static void codeloader_uart_callback(const void *buffer, uint16_t length, bool error);
|
|
#ifdef SUPPORT_ONE_LINE_UART_BURN
|
|
static void codeloader_one_line_uart_callback(const void *buffer, uint16_t length, bool error);
|
|
#endif
|
|
|
|
/*
|
|
* Public function definitions
|
|
*/
|
|
uart_bus_t get_codeloader_uart(void)
|
|
{
|
|
return g_codeload_uart;
|
|
}
|
|
|
|
#ifdef SUPPORT_ONE_LINE_UART_BURN
|
|
void codeloader_uart_switch_trx(bool switch_trx)
|
|
{
|
|
uint32_t cfg = readl(UART_SWITCH_TRX_REG_ADDR);
|
|
|
|
if (switch_trx) {
|
|
cfg = cfg | BIT(ONE_LINE_UART_SWITCH_BIT_OFFSET);
|
|
} else {
|
|
cfg = cfg & (~BIT(ONE_LINE_UART_SWITCH_BIT_OFFSET));
|
|
}
|
|
|
|
writel(UART_SWITCH_TRX_REG_ADDR, cfg);
|
|
}
|
|
#endif
|
|
|
|
static void codeloader_uart_write_immediately_char(uart_bus_t uart, char c)
|
|
{
|
|
char buffer[2];
|
|
buffer[0] = c;
|
|
buffer[1] = 0;
|
|
UNUSED(buffer);
|
|
#ifdef SUPPORT_ONE_LINE_UART_BURN
|
|
if (g_codeloader_first_msg) {
|
|
/* for init message send to both uart. */
|
|
uapi_uart_write(CODELOADER_ONE_LINE_UART_BUS, (const void *)buffer, 1, 0);
|
|
}
|
|
#endif
|
|
uapi_uart_write(uart, (const void *)buffer, 1, 0);
|
|
}
|
|
|
|
static void codeloader_uart_write_scatter(uart_bus_t uart, const codeloader_common_generic_buffer_t *data_to_send)
|
|
{
|
|
uint8_t *buffer = NULL;
|
|
uint16_t length;
|
|
|
|
g_codeloader_uart_last_data_sent.buffer = data_to_send->buffer;
|
|
g_codeloader_uart_last_data_sent.length = data_to_send->length;
|
|
|
|
buffer = data_to_send->buffer;
|
|
length = (uint16_t)data_to_send->length;
|
|
#ifdef SUPPORT_ONE_LINE_UART_BURN
|
|
/* switch uart tx mode when sending with one_line uart */
|
|
if (uart == CODELOADER_ONE_LINE_UART_BUS || g_codeloader_first_msg) {
|
|
codeloader_uart_switch_trx(false);
|
|
}
|
|
#endif
|
|
|
|
// send the start sequence
|
|
codeloader_uart_write_immediately_char(uart, CODELOADER_UART_FLAG_CHAR_ESCAPE);
|
|
codeloader_uart_write_immediately_char(uart, CODELOADER_UART_FLAG_CHAR_START);
|
|
|
|
// send the frame
|
|
while (length > 0) {
|
|
codeloader_uart_write_immediately_char(uart, *buffer);
|
|
if (*buffer == CODELOADER_UART_FLAG_CHAR_ESCAPE) { // if it is the scape character send it again
|
|
codeloader_uart_write_immediately_char(uart, *buffer);
|
|
}
|
|
buffer++;
|
|
length--;
|
|
}
|
|
|
|
// send the end sequence
|
|
codeloader_uart_write_immediately_char(uart, CODELOADER_UART_FLAG_CHAR_ESCAPE);
|
|
|
|
// for the last character make it atomic with the callback
|
|
non_os_enter_critical();
|
|
codeloader_uart_write_immediately_char(uart, CODELOADER_UART_FLAG_CHAR_END);
|
|
|
|
g_codeloader_tx_uart_buffer.in_use = false;
|
|
|
|
if (g_codeloader_uart_sent_data_callback) {
|
|
g_codeloader_uart_sent_data_callback();
|
|
}
|
|
|
|
non_os_exit_critical();
|
|
|
|
#ifdef SUPPORT_ONE_LINE_UART_BURN
|
|
if (uart == CODELOADER_ONE_LINE_UART_BUS || g_codeloader_first_msg) {
|
|
while (uapi_uart_has_pending_transmissions(CODELOADER_ONE_LINE_UART_BUS)) {};
|
|
codeloader_uart_switch_trx(true);
|
|
}
|
|
g_codeloader_first_msg = false;
|
|
#endif
|
|
}
|
|
|
|
void codeloader_uart_write(const codeloader_common_generic_buffer_t *data_to_send)
|
|
{
|
|
codeloader_uart_write_scatter(g_codeload_uart, data_to_send);
|
|
}
|
|
|
|
static void bin_to_hex(uint8_t bin_byte, uint8_t *hex_output, const uint8_t max_hex_output_len)
|
|
{
|
|
UNUSED(max_hex_output_len);
|
|
uint8_t nibble;
|
|
|
|
nibble = (bin_byte & 0x0f);
|
|
if (nibble < NIBBLE_LENGTH) {
|
|
*(hex_output + 1) = nibble + '0';
|
|
} else {
|
|
*(hex_output + 1) = (nibble - 0x0a) + 'A';
|
|
}
|
|
nibble = bin_byte >> BIN_BYTE_RIGHT_SHIFT_BIT;
|
|
if (nibble < NIBBLE_LENGTH) {
|
|
*hex_output = nibble + '0';
|
|
} else {
|
|
*hex_output = (nibble - 0x0a) + 'A';
|
|
}
|
|
}
|
|
|
|
void codeloader_uart_write_raw_bin_to_hex(const uint8_t *buffer, uint16_t length)
|
|
{
|
|
uint8_t hex_output[HEX_LENGTH];
|
|
|
|
// send the buffer
|
|
while (length > 0) {
|
|
bin_to_hex(*buffer, hex_output, sizeof(hex_output));
|
|
codeloader_uart_write_immediately_char(g_codeload_uart, hex_output[1]);
|
|
codeloader_uart_write_immediately_char(g_codeload_uart, hex_output[0]);
|
|
buffer++;
|
|
length--;
|
|
}
|
|
}
|
|
|
|
void codeloader_uart_write_crlf(void)
|
|
{
|
|
#define ASCII_LF 0x0a
|
|
#define ASCII_CR 0x0d
|
|
codeloader_uart_write_immediately_char(g_codeload_uart, ASCII_CR);
|
|
codeloader_uart_write_immediately_char(g_codeload_uart, ASCII_LF);
|
|
}
|
|
|
|
bool codeloader_uart_get_last_message(codeloader_common_generic_buffer_t *output_buffer)
|
|
{
|
|
if (g_codeloader_uart_last_data_sent.buffer != NULL && g_codeloader_uart_last_data_sent.length > 0) {
|
|
output_buffer->buffer = g_codeloader_uart_last_data_sent.buffer;
|
|
output_buffer->length = g_codeloader_uart_last_data_sent.length - 1;
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static void codeloader_uart_init(uart_bus_t uart, uart_rx_callback_t callback)
|
|
{
|
|
uart_pin_config_t codeload_uart_pins;
|
|
uart_attr_t uart_line_config;
|
|
uart_buffer_config_t uart_buffer_config;
|
|
|
|
// Claim the UART here
|
|
codeload_uart_pins.tx_pin = CODELOADER_UART_TX_PIN;
|
|
codeload_uart_pins.rx_pin = CODELOADER_UART_RX_PIN;
|
|
#ifdef SUPPORT_ONE_LINE_UART_BURN
|
|
if (uart == CODELOADER_ONE_LINE_UART_BUS) {
|
|
codeload_uart_pins.tx_pin = CODELOADER_ONE_LINE_UART_TX_PIN;
|
|
codeload_uart_pins.rx_pin = CODELOADER_ONE_LINE_UART_TX_PIN;
|
|
}
|
|
#endif
|
|
|
|
codeload_uart_pins.rts_pin = CODELOADER_UART_RTS_PIN;
|
|
codeload_uart_pins.cts_pin = CODELOADER_UART_CTS_PIN;
|
|
|
|
// UART LINE Configuration
|
|
uart_line_config.baud_rate = CODELOADER_UART_DEFAULT_BAUD_RATE;
|
|
uart_line_config.data_bits = CODELOADER_UART_DATA_BITS;
|
|
uart_line_config.parity = CODELOADER_UART_PARITY;
|
|
uart_line_config.stop_bits = CODELOADER_UART_STOP_BITS;
|
|
|
|
uart_buffer_config.rx_buffer = g_codeloader_uart_internal_buffer;
|
|
uart_buffer_config.rx_buffer_size = CODELOADER_UART_RX_MAX_BUFFER_SIZE;
|
|
|
|
(void)uapi_uart_init(uart, &codeload_uart_pins, &uart_line_config, NULL, &uart_buffer_config);
|
|
uapi_uart_register_rx_callback(uart, UART_RX_CONDITION_FULL_OR_SUFFICIENT_DATA_OR_IDLE,
|
|
CODELOADER_UART_RX_MAX_BUFFER_SIZE, callback);
|
|
}
|
|
|
|
void codeloader_uart_default_init(codeloader_rx_buffer_t *external_rx_buffer,
|
|
CODELOADER_UART_RX_DATA_CALLBACK rx_callback, CODELOADER_UART_TX_DATA_CALLBACK tx_callback)
|
|
{
|
|
g_cl_uart_ex_rx_buffer = external_rx_buffer;
|
|
g_codeloader_uart_external_rx_buffer_size = external_rx_buffer->length;
|
|
external_rx_buffer->length = 0;
|
|
g_codeloader_uart_last_data_sent.buffer = NULL;
|
|
g_codeloader_uart_last_data_sent.length = 0;
|
|
g_codeloader_tx_uart_buffer.in_use = false;
|
|
|
|
g_codeloader_uart_received_data_callback = rx_callback;
|
|
g_codeloader_uart_sent_data_callback = tx_callback;
|
|
|
|
#ifndef SUPPORT_ONE_LINE_UART_BURN
|
|
codeloader_uart_init(g_codeload_uart, codeloader_uart_callback);
|
|
#else
|
|
if (g_codeloader_first_msg) {
|
|
/* Before send init message, to distinguish which serial port responds to the handshake information,
|
|
we initialize two UARTs with different callbacks. */
|
|
codeloader_uart_init(g_codeload_uart, codeloader_uart_callback);
|
|
codeloader_uart_init(CODELOADER_ONE_LINE_UART_BUS, codeloader_one_line_uart_callback);
|
|
} else {
|
|
/* After init handshake, g_codeload_uart equal to connected uart_bus.
|
|
And there is no need to register different callback. */
|
|
codeloader_uart_init(g_codeload_uart, codeloader_uart_callback);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void codeloader_uart_deinit(void)
|
|
{
|
|
while (uapi_uart_has_pending_transmissions(g_codeload_uart)) {};
|
|
|
|
uapi_uart_deinit(g_codeload_uart);
|
|
#ifdef SUPPORT_ONE_LINE_UART_BURN
|
|
codeloader_uart_switch_trx(false);
|
|
uapi_uart_deinit(CODELOADER_ONE_LINE_UART_BUS);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
void codeloader_uart_change_baud(uint32_t new_baud)
|
|
{
|
|
/*
|
|
* this is the same as the de-init code, but I did not want to copy that in-case the
|
|
* de-init changed to release buffers as well
|
|
*/
|
|
while (uapi_uart_has_pending_transmissions(g_codeload_uart)) {};
|
|
uapi_uart_deinit(g_codeload_uart);
|
|
|
|
// now re-init the uart with the new settings
|
|
uart_attr_t uart_line_config;
|
|
uart_buffer_config_t uart_buffer_config;
|
|
uart_pin_config_t codeload_uart_pins;
|
|
|
|
// TX configuration
|
|
codeload_uart_pins.tx_pin = CODELOADER_UART_TX_PIN;
|
|
codeload_uart_pins.rts_pin = CODELOADER_UART_RTS_PIN;
|
|
// RX configuration
|
|
codeload_uart_pins.rx_pin = CODELOADER_UART_RX_PIN;
|
|
codeload_uart_pins.cts_pin = CODELOADER_UART_CTS_PIN;
|
|
#ifdef SUPPORT_ONE_LINE_UART_BURN
|
|
if (g_codeload_uart == CODELOADER_ONE_LINE_UART_BUS) {
|
|
codeload_uart_pins.tx_pin = CODELOADER_ONE_LINE_UART_TX_PIN;
|
|
codeload_uart_pins.rx_pin = CODELOADER_ONE_LINE_UART_TX_PIN;
|
|
}
|
|
#endif
|
|
|
|
// UART LINE Configuration
|
|
uart_line_config.baud_rate = new_baud;
|
|
uart_line_config.data_bits = CODELOADER_UART_DATA_BITS;
|
|
uart_line_config.parity = CODELOADER_UART_PARITY;
|
|
uart_line_config.stop_bits = CODELOADER_UART_STOP_BITS;
|
|
|
|
uart_buffer_config.rx_buffer = g_codeloader_uart_internal_buffer;
|
|
uart_buffer_config.rx_buffer_size = CODELOADER_UART_RX_MAX_BUFFER_SIZE;
|
|
|
|
uapi_uart_init(g_codeload_uart, &codeload_uart_pins, &uart_line_config, NULL, &uart_buffer_config);
|
|
uapi_uart_register_rx_callback(g_codeload_uart, UART_RX_CONDITION_FULL_OR_SUFFICIENT_DATA_OR_IDLE,
|
|
CODELOADER_UART_RX_MAX_BUFFER_SIZE, codeloader_uart_callback);
|
|
}
|
|
|
|
/* get buffer for transmission */
|
|
void codeloader_uart_get_tx_buffer(codeloader_common_generic_buffer_t *output_buffer, uint16_t length)
|
|
{
|
|
non_os_enter_critical();
|
|
g_codeloader_tx_uart_buffer.in_use = true;
|
|
non_os_exit_critical();
|
|
|
|
output_buffer->buffer = g_codeloader_tx_uart_buffer.buffer;
|
|
output_buffer->length = length;
|
|
}
|
|
|
|
/* enables the recording of the received messages */
|
|
void codeloader_uart_enable_reception(void)
|
|
{
|
|
non_os_enter_critical();
|
|
g_codeloader_uart_reception_enabled = true;
|
|
non_os_exit_critical();
|
|
}
|
|
|
|
/* Ignores any received messages */
|
|
void codeloader_uart_disble_reception(void)
|
|
{
|
|
non_os_enter_critical();
|
|
g_codeloader_uart_reception_enabled = false;
|
|
non_os_exit_critical();
|
|
}
|
|
|
|
void codeloader_uart_wait_to_finish_transmission(void)
|
|
{
|
|
while (uapi_uart_has_pending_transmissions(g_codeload_uart)) {};
|
|
}
|
|
|
|
static bool codeloader_uart_callback_check_end(const void *buffer, uint16_t length, codeloader_callback_cause_e *cause)
|
|
{
|
|
bool invoke_external_callback = false;
|
|
uint8_t *received_buffer = (uint8_t *)buffer;
|
|
static bool escaped_flag = false;
|
|
bool write_to_buffer = true;
|
|
while (length > 0) {
|
|
write_to_buffer = true;
|
|
uint8_t received_data = *received_buffer;
|
|
|
|
if (!escaped_flag) {
|
|
if (received_data == CODELOADER_UART_FLAG_CHAR_ESCAPE) {
|
|
escaped_flag = true;
|
|
write_to_buffer = false;
|
|
}
|
|
} else { // the previous one was a escape
|
|
escaped_flag = false;
|
|
if (received_data == CODELOADER_UART_FLAG_CHAR_ESCAPE) { // double escape
|
|
} else if (received_data == CODELOADER_UART_FLAG_CHAR_END) {
|
|
write_to_buffer = false;
|
|
invoke_external_callback = true;
|
|
*cause = CODELOADER_CALLBACK_CAUSE_VALID_BUFFER;
|
|
} else if (received_data == CODELOADER_UART_FLAG_CHAR_START) {
|
|
g_cl_uart_ex_rx_buffer->length = 0;
|
|
write_to_buffer = false;
|
|
} else { // if any other flag restart ( this should not happen )
|
|
g_cl_uart_ex_rx_buffer->length = 0;
|
|
write_to_buffer = false;
|
|
invoke_external_callback = true; // buffer full
|
|
*cause = CODELOADER_CALLBACK_CAUSE_FRAME_ERROR;
|
|
}
|
|
}
|
|
|
|
if (write_to_buffer) {
|
|
*(g_cl_uart_ex_rx_buffer->buffer + g_cl_uart_ex_rx_buffer->length) = *received_buffer;
|
|
g_cl_uart_ex_rx_buffer->length++;
|
|
if (g_cl_uart_ex_rx_buffer->length >= g_codeloader_uart_external_rx_buffer_size) {
|
|
invoke_external_callback = true; // buffer full
|
|
*cause = CODELOADER_CALLBACK_CAUSE_BUFFER_FULL;
|
|
}
|
|
}
|
|
|
|
if (invoke_external_callback) {
|
|
escaped_flag = false; // and not write anything to the buffer
|
|
break;
|
|
}
|
|
received_buffer++;
|
|
length--;
|
|
}
|
|
return invoke_external_callback;
|
|
}
|
|
|
|
//lint -esym(459, codeloader_uart_callback)
|
|
static void codeloader_uart_callback(const void *buffer, uint16_t length, bool error)
|
|
{
|
|
if (error) {
|
|
if (g_codeloader_uart_received_data_callback != NULL) {
|
|
g_codeloader_uart_received_data_callback(g_cl_uart_ex_rx_buffer, CODELOADER_CALLBACK_CAUSE_UART_ERROR);
|
|
g_cl_uart_ex_rx_buffer->length = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!g_codeloader_uart_reception_enabled) { // if the buffer is in use discard the data
|
|
return;
|
|
}
|
|
|
|
// iterate to check for the end of message
|
|
// Initially assume the frame is incorrect, parsing will prove otherwise
|
|
codeloader_callback_cause_e cause = CODELOADER_CALLBACK_CAUSE_FRAME_ERROR;
|
|
bool invoke_external_callback = codeloader_uart_callback_check_end(buffer, length, &cause);
|
|
if (invoke_external_callback && g_codeloader_uart_received_data_callback != NULL) {
|
|
g_codeloader_uart_received_data_callback(g_cl_uart_ex_rx_buffer, cause);
|
|
g_cl_uart_ex_rx_buffer->length = 0;
|
|
#ifdef SUPPORT_ONE_LINE_UART_BURN
|
|
} else {
|
|
g_codeloader_one_line_callback = false;
|
|
}
|
|
#else
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef SUPPORT_ONE_LINE_UART_BURN
|
|
|
|
bool codeloader_uart_get_rx_status(void)
|
|
{
|
|
return g_codeloader_one_line_callback;
|
|
}
|
|
|
|
void codeloader_uart_set_global_uart(uart_bus_t id)
|
|
{
|
|
g_codeload_uart = id;
|
|
}
|
|
|
|
static void codeloader_one_line_uart_callback(const void *buffer, uint16_t length, bool error)
|
|
{
|
|
g_codeloader_one_line_callback = true;
|
|
codeloader_uart_callback(buffer, length, error);
|
|
}
|
|
#endif
|