/*
|
* 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 "nimble_syscfg.h"
|
|
#if MYNEWT_VAL(BTTESTER_PIPE_UART)
|
|
#include "os/mynewt.h"
|
#include "uart/uart.h"
|
|
#include "bttester_pipe.h"
|
|
static uint8_t *recv_buf;
|
static size_t recv_buf_len;
|
static bttester_pipe_recv_cb app_cb;
|
static size_t recv_off;
|
|
struct uart_pipe_ring {
|
uint8_t head;
|
uint8_t tail;
|
uint16_t size;
|
uint8_t *buf;
|
};
|
|
static struct uart_dev *uart_dev;
|
static struct uart_pipe_ring cr_tx;
|
static uint8_t cr_tx_buf[MYNEWT_VAL(BTTESTER_BTP_DATA_SIZE_MAX)];
|
typedef void (*console_write_char)(struct uart_dev*, uint8_t);
|
static console_write_char write_char_cb;
|
|
static struct uart_pipe_ring cr_rx;
|
static uint8_t cr_rx_buf[MYNEWT_VAL(BTTESTER_BTP_DATA_SIZE_MAX)];
|
static volatile bool uart_console_rx_stalled;
|
|
struct os_event rx_ev;
|
|
static inline int
|
inc_and_wrap(int i, int max)
|
{
|
return (i + 1) & (max - 1);
|
}
|
|
static void
|
uart_pipe_ring_add_char(struct uart_pipe_ring *cr, char ch)
|
{
|
cr->buf[cr->head] = ch;
|
cr->head = inc_and_wrap(cr->head, cr->size);
|
}
|
|
static uint8_t
|
uart_pipe_ring_pull_char(struct uart_pipe_ring *cr)
|
{
|
uint8_t ch;
|
|
ch = cr->buf[cr->tail];
|
cr->tail = inc_and_wrap(cr->tail, cr->size);
|
return ch;
|
}
|
|
static bool
|
uart_pipe_ring_is_full(const struct uart_pipe_ring *cr)
|
{
|
return inc_and_wrap(cr->head, cr->size) == cr->tail;
|
}
|
|
static bool
|
uart_pipe_ring_is_empty(const struct uart_pipe_ring *cr)
|
{
|
return cr->head == cr->tail;
|
}
|
|
static void
|
uart_pipe_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_pipe_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_pipe_ring_add_char(&cr_tx, ch);
|
OS_EXIT_CRITICAL(sr);
|
}
|
|
/*
|
* 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_pipe_ring_is_empty(&cr_tx)) {
|
return -1;
|
}
|
return uart_pipe_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 (uart_pipe_ring_is_full(&cr_rx)) {
|
uart_console_rx_stalled = true;
|
return -1;
|
}
|
|
uart_pipe_ring_add_char(&cr_rx, byte);
|
|
if (!rx_ev.ev_queued) {
|
os_eventq_put(os_eventq_dflt_get(), &rx_ev);
|
}
|
|
return 0;
|
}
|
|
static int
|
uart_pipe_handle_char(int key)
|
{
|
recv_buf[recv_off] = (uint8_t) key;
|
recv_off++;
|
|
return 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 = uart_pipe_handle_char(b);
|
if (ret < 0) {
|
return;
|
}
|
}
|
|
while (!uart_pipe_ring_is_empty(&cr_rx)) {
|
OS_ENTER_CRITICAL(sr);
|
b = uart_pipe_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 = uart_pipe_handle_char(b);
|
if (ret < 0) {
|
return;
|
}
|
}
|
|
/*
|
* Call application callback with received data. Application
|
* may provide new buffer or alter data offset.
|
*/
|
recv_buf = app_cb(recv_buf, &recv_off);
|
|
b = -1;
|
}
|
|
int
|
bttester_pipe_send(const uint8_t *data, int len)
|
{
|
int i;
|
|
/* 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 -1;
|
}
|
|
for (i = 0; i < len; ++i) {
|
write_char_cb(uart_dev, data[i]);
|
}
|
|
uart_start_tx(uart_dev);
|
|
return 0;
|
}
|
|
int
|
bttester_pipe_send_buf(struct os_mbuf *buf)
|
{
|
int i, len;
|
struct os_mbuf *om;
|
|
/* 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 -1;
|
}
|
|
for (om = buf; om; om = SLIST_NEXT(om, om_next)) {
|
len = om->om_len;
|
for (i = 0; i < len; ++i) {
|
write_char_cb(uart_dev, om->om_data[i]);
|
}
|
}
|
|
uart_start_tx(uart_dev);
|
|
return 0;
|
}
|
|
int
|
bttester_pipe_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 = sizeof(cr_tx_buf);
|
cr_tx.buf = cr_tx_buf;
|
write_char_cb = uart_pipe_queue_char;
|
|
cr_rx.size = sizeof(cr_rx_buf);
|
cr_rx.buf = cr_rx_buf;
|
|
rx_ev.ev_cb = uart_console_rx_char_event;
|
|
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;
|
}
|
|
void
|
bttester_pipe_register(uint8_t *buf, size_t len, bttester_pipe_recv_cb cb)
|
{
|
recv_buf = buf;
|
recv_buf_len = len;
|
app_cb = cb;
|
}
|
#endif /* MYNEWT_VAL(BTTESTER_PIPE_UART) */
|