368 lines
8.2 KiB
C
368 lines
8.2 KiB
C
/*
|
|
* Copyright (C) 2018 Intel Corporation. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include <hypervisor.h>
|
|
#include "serial_internal.h"
|
|
|
|
static struct uart *sio_ports[SERIAL_MAX_DEVS];
|
|
static uint8_t sio_initialized[SERIAL_MAX_DEVS];
|
|
|
|
static struct uart *get_uart_by_id(const char *uart_id, uint32_t *index)
|
|
{
|
|
/* Initialize the index to the start of array. */
|
|
*index = 0U;
|
|
|
|
while (sio_ports[*index] != NULL) {
|
|
if (strncmp(sio_ports[*index]->tgt_uart->uart_id, uart_id,
|
|
strnlen_s(sio_ports[*index]->tgt_uart->uart_id,
|
|
SERIAL_ID_MAX_LENGTH)) == 0) {
|
|
break;
|
|
}
|
|
|
|
/* No device is found if index reaches end of array. */
|
|
(*index)++;
|
|
if (*index == SERIAL_MAX_DEVS) {
|
|
return NULL;
|
|
}
|
|
|
|
}
|
|
return sio_ports[*index];
|
|
}
|
|
|
|
int serial_init(void)
|
|
{
|
|
uint32_t index = 0;
|
|
int status = 0;
|
|
|
|
while (index < SERIAL_MAX_DEVS) {
|
|
/* Allocate memory for generic control block of enabled UART */
|
|
sio_ports[index] = calloc(1, sizeof(struct uart));
|
|
|
|
if (sio_ports[index] == NULL) {
|
|
status = -ENOMEM;
|
|
break;
|
|
}
|
|
|
|
sio_ports[index]->tgt_uart = &(Tgt_Uarts[index]);
|
|
|
|
/*
|
|
* Set the open flag to false to indicate that UART port is
|
|
* not opened yet.
|
|
*/
|
|
sio_ports[index]->open_flag = false;
|
|
|
|
/* Reset the tx lock */
|
|
spinlock_init(&sio_ports[index]->tx_lock);
|
|
|
|
sio_ports[index]->rx_sio_queue = sbuf_allocate(
|
|
sio_ports[index]->tgt_uart->buffer_size,
|
|
sizeof(uint8_t));
|
|
if (sio_ports[index]->rx_sio_queue != NULL) {
|
|
sbuf_set_flags(sio_ports[index]->rx_sio_queue,
|
|
OVERWRITE_EN);
|
|
|
|
/* Call target specific initialization function */
|
|
status = sio_ports[index]->tgt_uart->
|
|
init(sio_ports[index]->tgt_uart);
|
|
|
|
if (status == 0) {
|
|
sio_initialized[index] = true;
|
|
}
|
|
} else {
|
|
status = -ENOMEM;
|
|
break;
|
|
}
|
|
|
|
index++;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
uint32_t serial_open(const char *uart_id)
|
|
{
|
|
int status = SERIAL_DEV_NOT_FOUND;
|
|
struct uart *uart;
|
|
uint32_t index;
|
|
|
|
/* Get UART control block from given character ID */
|
|
uart = get_uart_by_id(uart_id, &index);
|
|
|
|
if (uart != NULL && index < SERIAL_MAX_DEVS &&
|
|
sio_initialized[index] != 0U &&
|
|
(uart->open_flag == false)) {
|
|
/* Reset the buffer lock */
|
|
spinlock_init(&uart->buffer_lock);
|
|
|
|
/* Configure the UART port to default settings. */
|
|
uart->config.data_bits = DATA_8;
|
|
uart->config.stop_bits = STOP_1;
|
|
uart->config.parity_bits = PARITY_NONE;
|
|
uart->config.baud_rate = BAUD_115200;
|
|
uart->config.flow_control = FLOW_NONE;
|
|
uart->config.read_mode = SUSPEND;
|
|
|
|
/* Open the UART hardware with default configuration. */
|
|
status = uart->tgt_uart->open(uart->tgt_uart, &(uart->config));
|
|
|
|
if (status == 0) {
|
|
uart->open_flag = true;
|
|
}
|
|
}
|
|
|
|
/* Already open serial device */
|
|
else if (uart != NULL && uart->open_flag == true) {
|
|
/* Reset the buffer lock */
|
|
spinlock_init(&uart->buffer_lock);
|
|
status = 0;
|
|
}
|
|
|
|
return (status == 0) ?
|
|
SERIAL_ENCODE_INDEX(index) :
|
|
SERIAL_INVALID_HANDLE;
|
|
}
|
|
|
|
uint32_t serial_get_rx_data(uint32_t uart_handle)
|
|
{
|
|
uint32_t index;
|
|
struct uart *uart;
|
|
int data_avail;
|
|
uint32_t rx_byte_status;
|
|
uint32_t lsr_reg, bytes_read;
|
|
uint8_t ch;
|
|
uint32_t total_bytes_read = 0U;
|
|
|
|
if (!SERIAL_VALIDATE_HANDLE(uart_handle)) {
|
|
return 0U;
|
|
}
|
|
|
|
index = SERIAL_DECODE_INDEX(uart_handle);
|
|
if (index >= SERIAL_MAX_DEVS) {
|
|
return 0U;
|
|
}
|
|
|
|
uart = sio_ports[index];
|
|
if (uart == NULL) {
|
|
return 0U;
|
|
}
|
|
|
|
/* Place all the data available in RX FIFO, in circular buffer */
|
|
data_avail = uart->tgt_uart->rx_data_is_avail(
|
|
uart->tgt_uart, &lsr_reg);
|
|
while (data_avail != 0) {
|
|
|
|
/* Read the byte */
|
|
uart->tgt_uart->read(uart->tgt_uart, (void *)&ch, &bytes_read);
|
|
|
|
/* Get RX status for this byte */
|
|
rx_byte_status = uart->tgt_uart->get_rx_err(lsr_reg);
|
|
|
|
/*
|
|
* Check if discard errors in RX character
|
|
* (parity / framing errors)
|
|
*/
|
|
if (rx_byte_status >= SD_RX_PARITY_ERROR) {
|
|
/* Increase error status if bad data */
|
|
uart->rx_error.parity_errors +=
|
|
(rx_byte_status == SD_RX_PARITY_ERROR);
|
|
uart->rx_error.frame_errors +=
|
|
(rx_byte_status == SD_RX_FRAME_ERROR);
|
|
} else {
|
|
/* Update the overrun errors */
|
|
uart->rx_error.overrun_errors +=
|
|
(rx_byte_status == SD_RX_OVERRUN_ERROR);
|
|
|
|
/* Enter Critical Section */
|
|
spinlock_obtain(&uart->buffer_lock);
|
|
|
|
/* Put the item on circular buffer */
|
|
(void)sbuf_put(uart->rx_sio_queue, &ch);
|
|
|
|
/* Exit Critical Section */
|
|
spinlock_release(&uart->buffer_lock);
|
|
}
|
|
/* Update the total bytes read */
|
|
total_bytes_read += bytes_read;
|
|
data_avail = uart->tgt_uart->rx_data_is_avail(
|
|
uart->tgt_uart, &lsr_reg);
|
|
}
|
|
return total_bytes_read;
|
|
}
|
|
|
|
int serial_getc(uint32_t uart_handle)
|
|
{
|
|
uint8_t ch;
|
|
struct uart *port;
|
|
uint32_t index;
|
|
int status = SERIAL_DEV_NOT_FOUND;
|
|
|
|
if (!SERIAL_VALIDATE_HANDLE(uart_handle)) {
|
|
goto exit;
|
|
}
|
|
|
|
index = SERIAL_DECODE_INDEX(uart_handle);
|
|
|
|
if (index >= SERIAL_MAX_DEVS) {
|
|
goto exit;
|
|
}
|
|
|
|
port = sio_ports[index];
|
|
|
|
if (port == NULL) {
|
|
goto exit;
|
|
}
|
|
|
|
/* First read a character from the circular buffer regardless of the
|
|
* read mode of UART port. If status is not CBUFFER_EMPTY, character
|
|
* read from UART port is returned to the caller. Otherwise, if read
|
|
* mode is not NO_SUSPEND, thread is blocked until a character is read
|
|
* from the port. Serial target specific HISR unblocks the thread when
|
|
* a character is received and character is then read from the circular
|
|
* buffer.
|
|
*/
|
|
|
|
/* Disable interrupts for critical section */
|
|
spinlock_obtain(&port->buffer_lock);
|
|
|
|
status = sbuf_get(port->rx_sio_queue, &ch);
|
|
|
|
/* Restore interrupts to original level. */
|
|
spinlock_release(&port->buffer_lock);
|
|
|
|
exit:
|
|
/* Return the character read, otherwise return the error status */
|
|
return ((status > 0) ? (int)(ch) : SERIAL_EOF);
|
|
}
|
|
|
|
int serial_gets(uint32_t uart_handle, char *buffer, uint32_t length_arg)
|
|
{
|
|
char *data_read = buffer;
|
|
int c;
|
|
struct uart *port;
|
|
uint32_t index;
|
|
int status = 0;
|
|
uint32_t length = length_arg;
|
|
|
|
if ((buffer == NULL) || (length == 0U)) {
|
|
return 0;
|
|
}
|
|
|
|
if (!SERIAL_VALIDATE_HANDLE(uart_handle)) {
|
|
return 0;
|
|
}
|
|
|
|
index = SERIAL_DECODE_INDEX(uart_handle);
|
|
if (index >= SERIAL_MAX_DEVS) {
|
|
return 0;
|
|
}
|
|
|
|
port = sio_ports[index];
|
|
if ((port != NULL) && (port->open_flag == true)) {
|
|
for (; length > 0U; length--) {
|
|
/* Disable interrupts for critical section */
|
|
spinlock_obtain(&port->buffer_lock);
|
|
|
|
status = sbuf_get(port->rx_sio_queue, (uint8_t *)&c);
|
|
|
|
/* Restore interrupts to original level. */
|
|
spinlock_release(&port->buffer_lock);
|
|
|
|
if (status <= 0) {
|
|
break;
|
|
}
|
|
|
|
/* Save character in buffer */
|
|
*data_read = (char) c;
|
|
data_read++;
|
|
}
|
|
}
|
|
/* Return actual number of bytes read */
|
|
return (int)(data_read - buffer);
|
|
}
|
|
|
|
static int serial_putc(uint32_t uart_handle, int c)
|
|
{
|
|
uint32_t index, bytes_written = 0U;
|
|
struct uart *uart;
|
|
int busy;
|
|
|
|
if (!SERIAL_VALIDATE_HANDLE(uart_handle)) {
|
|
return SERIAL_EOF;
|
|
}
|
|
|
|
index = SERIAL_DECODE_INDEX(uart_handle);
|
|
|
|
if (index >= SERIAL_MAX_DEVS) {
|
|
return SERIAL_EOF;
|
|
}
|
|
|
|
uart = sio_ports[index];
|
|
|
|
if (uart == NULL) {
|
|
return SERIAL_EOF;
|
|
}
|
|
|
|
/* Wait for TX hardware to be ready */
|
|
do {
|
|
busy = uart->tgt_uart->tx_is_busy(uart->tgt_uart);
|
|
} while (busy != 0);
|
|
|
|
/* Transmit character */
|
|
uart->tgt_uart->write(uart->tgt_uart, &(c), &bytes_written);
|
|
|
|
/* Return character written or EOF for error */
|
|
return ((bytes_written > 0U) ? c : (SERIAL_EOF));
|
|
}
|
|
|
|
int serial_puts(uint32_t uart_handle, const char *s, uint32_t length_arg)
|
|
{
|
|
const char *old_data = s;
|
|
uint32_t index;
|
|
struct uart *port;
|
|
int retval = 0;
|
|
uint32_t length = length_arg;
|
|
|
|
if ((s == NULL) || (length == 0U)) {
|
|
return 0;
|
|
}
|
|
|
|
if (!SERIAL_VALIDATE_HANDLE(uart_handle)) {
|
|
return 0;
|
|
}
|
|
|
|
index = SERIAL_DECODE_INDEX(uart_handle);
|
|
|
|
if (index >= SERIAL_MAX_DEVS) {
|
|
return 0;
|
|
}
|
|
|
|
port = sio_ports[index];
|
|
|
|
if (port == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Grab the semaphore so that strings between threads do not
|
|
* get mixed.
|
|
*/
|
|
spinlock_obtain(&port->tx_lock);
|
|
|
|
/*
|
|
* Loop through the string until desired length of bytes have
|
|
* been written or SERIAL_EOF is returned.
|
|
*/
|
|
for (; length > 0U && retval != SERIAL_EOF; s++, length--) {
|
|
retval = serial_putc(uart_handle, (int) *s);
|
|
}
|
|
|
|
/* Allow other threads to use this service. */
|
|
spinlock_release(&port->tx_lock);
|
|
|
|
/* Return actual number of bytes written */
|
|
return (int)(s - old_data);
|
|
}
|