blob: bd40ff7fd9c8c26755326582872f236785e98d5c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#include "os/mynewt.h"
#if MYNEWT_VAL(CONSOLE_UART)
#include <ctype.h>
#include <assert.h>
#include "uart/uart.h"
#include "bsp/bsp.h"
#include "console/console.h"
#include "console_priv.h"
struct console_ring {
uint8_t head;
uint8_t tail;
uint16_t size;
uint8_t *buf;
};
static struct uart_dev *uart_dev;
static struct console_ring cr_tx;
static uint8_t cr_tx_buf[MYNEWT_VAL(CONSOLE_UART_TX_BUF_SIZE)];
typedef void (*console_write_char)(struct uart_dev*, uint8_t);
static console_write_char write_char_cb;
#if MYNEWT_VAL(CONSOLE_UART_RX_BUF_SIZE) > 0
static struct console_ring cr_rx;
static uint8_t cr_rx_buf[MYNEWT_VAL(CONSOLE_UART_RX_BUF_SIZE)];
static volatile bool uart_console_rx_stalled;
struct os_event rx_ev;
#endif
static inline int
inc_and_wrap(int i, int max)
{
return (i + 1) & (max - 1);
}
static void
uart_console_ring_add_char(struct console_ring *cr, char ch)
{
cr->buf[cr->head] = ch;
cr->head = inc_and_wrap(cr->head, cr->size);
}
static uint8_t
uart_console_ring_pull_char(struct console_ring *cr)
{
uint8_t ch;
ch = cr->buf[cr->tail];
cr->tail = inc_and_wrap(cr->tail, cr->size);
return ch;
}
static bool
uart_console_ring_is_full(const struct console_ring *cr)
{
return inc_and_wrap(cr->head, cr->size) == cr->tail;
}
static bool
uart_console_ring_is_empty(const struct console_ring *cr)
{
return cr->head == cr->tail;
}
static void
uart_console_queue_char(struct uart_dev *uart_dev, uint8_t ch)
{
int sr;
if ((uart_dev->ud_dev.od_flags & OS_DEV_F_STATUS_OPEN) == 0) {
return;
}
OS_ENTER_CRITICAL(sr);
while (uart_console_ring_is_full(&cr_tx)) {
/* TX needs to drain */
uart_start_tx(uart_dev);
OS_EXIT_CRITICAL(sr);
if (os_started()) {
os_time_delay(1);
}
OS_ENTER_CRITICAL(sr);
}
uart_console_ring_add_char(&cr_tx, ch);
OS_EXIT_CRITICAL(sr);
}
/*
* Flush cnt characters from console output queue.
*/
static void
uart_console_tx_flush(int cnt)
{
int i;
uint8_t byte;
for (i = 0; i < cnt; i++) {
if (uart_console_ring_is_empty(&cr_tx)) {
break;
}
byte = uart_console_ring_pull_char(&cr_tx);
uart_blocking_tx(uart_dev, byte);
}
}
void
uart_console_blocking_mode(void)
{
int sr;
OS_ENTER_CRITICAL(sr);
if (write_char_cb) {
write_char_cb = uart_blocking_tx;
uart_console_tx_flush(MYNEWT_VAL(CONSOLE_UART_TX_BUF_SIZE));
}
OS_EXIT_CRITICAL(sr);
}
void
uart_console_non_blocking_mode(void)
{
int sr;
OS_ENTER_CRITICAL(sr);
if (write_char_cb) {
write_char_cb = uart_console_queue_char;
}
OS_EXIT_CRITICAL(sr);
}
int
console_out(int c)
{
if (g_silence_console) {
return c;
}
/* Assure that there is a write cb installed; this enables to debug
* code that is faulting before the console was initialized.
*/
if (!write_char_cb) {
return c;
}
if ('\n' == c) {
write_char_cb(uart_dev, '\r');
console_is_midline = 0;
} else {
console_is_midline = 1;
}
write_char_cb(uart_dev, c);
uart_start_tx(uart_dev);
return c;
}
void
console_rx_restart(void)
{
#if MYNEWT_VAL(CONSOLE_UART_RX_BUF_SIZE) > 0
os_eventq_put(os_eventq_dflt_get(), &rx_ev);
#else
uart_start_rx(uart_dev);
#endif
}
/*
* Interrupts disabled when console_tx_char/console_rx_char are called.
* Characters sent only in blocking mode.
*/
static int
uart_console_tx_char(void *arg)
{
if (uart_console_ring_is_empty(&cr_tx)) {
return -1;
}
return uart_console_ring_pull_char(&cr_tx);
}
/*
* Interrupts disabled when console_tx_char/console_rx_char are called.
*/
static int
uart_console_rx_char(void *arg, uint8_t byte)
{
#if MYNEWT_VAL(CONSOLE_UART_RX_BUF_SIZE) > 0
if (uart_console_ring_is_full(&cr_rx)) {
uart_console_rx_stalled = true;
return -1;
}
uart_console_ring_add_char(&cr_rx, byte);
if (!rx_ev.ev_queued) {
os_eventq_put(os_eventq_dflt_get(), &rx_ev);
}
return 0;
#else
return console_handle_char(byte);
#endif
}
#if MYNEWT_VAL(CONSOLE_UART_RX_BUF_SIZE) > 0
static void
uart_console_rx_char_event(struct os_event *ev)
{
static int b = -1;
int sr;
int ret;
/* We may have unhandled character - try it first */
if (b >= 0) {
ret = console_handle_char(b);
if (ret < 0) {
return;
}
}
while (!uart_console_ring_is_empty(&cr_rx)) {
OS_ENTER_CRITICAL(sr);
b = uart_console_ring_pull_char(&cr_rx);
OS_EXIT_CRITICAL(sr);
/* If UART RX was stalled due to a full receive buffer, restart RX now
* that we have removed a byte from the buffer.
*/
if (uart_console_rx_stalled) {
uart_console_rx_stalled = false;
uart_start_rx(uart_dev);
}
ret = console_handle_char(b);
if (ret < 0) {
return;
}
}
b = -1;
}
#endif
int
uart_console_is_init(void)
{
return uart_dev != NULL;
}
int
uart_console_init(void)
{
struct uart_conf uc = {
.uc_speed = MYNEWT_VAL(CONSOLE_UART_BAUD),
.uc_databits = 8,
.uc_stopbits = 1,
.uc_parity = UART_PARITY_NONE,
.uc_flow_ctl = MYNEWT_VAL(CONSOLE_UART_FLOW_CONTROL),
.uc_tx_char = uart_console_tx_char,
.uc_rx_char = uart_console_rx_char,
};
cr_tx.size = MYNEWT_VAL(CONSOLE_UART_TX_BUF_SIZE);
cr_tx.buf = cr_tx_buf;
write_char_cb = uart_console_queue_char;
#if MYNEWT_VAL(CONSOLE_UART_RX_BUF_SIZE) > 0
cr_rx.size = MYNEWT_VAL(CONSOLE_UART_RX_BUF_SIZE);
cr_rx.buf = cr_rx_buf;
rx_ev.ev_cb = uart_console_rx_char_event;
#endif
if (!uart_dev) {
uart_dev = (struct uart_dev *)os_dev_open(MYNEWT_VAL(CONSOLE_UART_DEV),
OS_TIMEOUT_NEVER, &uc);
if (!uart_dev) {
return -1;
}
}
return 0;
}
#endif /* MYNEWT_VAL(CONSOLE_UART) */