mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-28 02:35:28 +00:00
Signed-off-by: Randall Spangler <rspangler@chromium.org> BUG=chrome-os-partner:7490 TEST=timerinfo; numbers should look reasonable Change-Id: I698be99c87bf311013427ac0ed9e93e5687f40c0
690 lines
16 KiB
C
690 lines
16 KiB
C
/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
/* Common code to do UART buffering and printing */
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include "console.h"
|
|
#include "task.h"
|
|
#include "timer.h"
|
|
#include "uart.h"
|
|
#include "util.h"
|
|
|
|
/* Buffer sizes; should be power of 2 */
|
|
#define TX_BUF_SIZE 512
|
|
#define RX_BUF_SIZE 128 /* suggest larger than 80 to copy&paste script. */
|
|
#define HISTORY_SIZE 8
|
|
|
|
/* The size limit of single command */
|
|
#define RX_LINE_SIZE 80
|
|
|
|
/* Macros to advance in the circular buffers */
|
|
#define TX_BUF_NEXT(i) (((i) + 1) & (TX_BUF_SIZE - 1))
|
|
#define RX_BUF_NEXT(i) (((i) + 1) & (RX_BUF_SIZE - 1))
|
|
#define RX_BUF_PREV(i) (((i) - 1) & (RX_BUF_SIZE - 1))
|
|
#define CMD_HIST_NEXT(i) (((i) + 1) & (HISTORY_SIZE - 1))
|
|
#define CMD_HIST_PREV(i) (((i) - 1) & (HISTORY_SIZE - 1))
|
|
|
|
/* Macro to calculate difference of pointers in the circular receive buffer. */
|
|
#define RX_BUF_DIFF(i, j) (((i) - (j)) & (RX_BUF_SIZE - 1))
|
|
|
|
/* Transmit and receive buffers */
|
|
static volatile char tx_buf[TX_BUF_SIZE];
|
|
static volatile int tx_buf_head;
|
|
static volatile int tx_buf_tail;
|
|
static volatile char rx_buf[RX_BUF_SIZE];
|
|
static volatile int rx_buf_head;
|
|
static volatile int rx_buf_tail;
|
|
static volatile char rx_cur_buf[RX_LINE_SIZE];
|
|
static volatile int rx_cur_buf_tail;
|
|
static volatile int rx_cur_buf_head;
|
|
static volatile int rx_cur_buf_ptr;
|
|
static int last_rx_was_cr;
|
|
static int in_escape;
|
|
static char esc_seq_char;
|
|
|
|
/* Command history */
|
|
struct cmd_history_t {
|
|
volatile int head;
|
|
volatile int tail;
|
|
};
|
|
static struct cmd_history_t cmd_history[HISTORY_SIZE];
|
|
static volatile int cmd_history_head;
|
|
static volatile int cmd_history_tail;
|
|
static volatile int cmd_history_ptr;
|
|
|
|
static int console_mode = 1;
|
|
|
|
/* TODO: should have an API to set raw mode for the UART. In raw
|
|
* mode, we don't do CRLF translation or echo input. */
|
|
|
|
|
|
/* Put a single character into the transmit buffer. Does not enable
|
|
* the transmit interrupt; assumes that happens elsewhere. Returns
|
|
* zero if the character was transmitted, 1 if it was dropped. */
|
|
static int __tx_char(int c)
|
|
{
|
|
int tx_buf_next;
|
|
|
|
/* Do newline to CRLF translation */
|
|
if (console_mode && c == '\n' && __tx_char('\r'))
|
|
return 1;
|
|
|
|
tx_buf_next = TX_BUF_NEXT(tx_buf_head);
|
|
if (tx_buf_next == tx_buf_tail)
|
|
return 1;
|
|
|
|
tx_buf[tx_buf_head] = c;
|
|
tx_buf_head = tx_buf_next;
|
|
return 0;
|
|
}
|
|
|
|
static void move_rx_ptr_fwd(void)
|
|
{
|
|
if (rx_cur_buf_ptr != rx_cur_buf_head) {
|
|
++rx_cur_buf_ptr;
|
|
uart_write_char(0x1B);
|
|
uart_write_char('[');
|
|
uart_write_char('1');
|
|
uart_write_char('C');
|
|
}
|
|
}
|
|
|
|
static void move_rx_ptr_bwd(void)
|
|
{
|
|
if (rx_cur_buf_ptr != 0) {
|
|
--rx_cur_buf_ptr;
|
|
uart_write_char(0x1B);
|
|
uart_write_char('[');
|
|
uart_write_char('1');
|
|
uart_write_char('D');
|
|
}
|
|
}
|
|
|
|
static void repeat_char(char c, int cnt)
|
|
{
|
|
while (cnt--)
|
|
uart_write_char(c);
|
|
}
|
|
|
|
static void handle_backspace(void)
|
|
{
|
|
if (rx_cur_buf_ptr != 0) {
|
|
/* Move texts after cursor and also update rx buffer. */
|
|
int ptr;
|
|
for (ptr = rx_cur_buf_ptr; ptr < rx_cur_buf_head; ++ptr) {
|
|
uart_write_char(rx_cur_buf[ptr]);
|
|
rx_cur_buf[ptr - 1] = rx_cur_buf[ptr];
|
|
}
|
|
|
|
/* Space over last character and move cursor back to correct
|
|
* position.
|
|
*/
|
|
uart_write_char(' ');
|
|
repeat_char('\b', ptr - rx_cur_buf_ptr + 1);
|
|
|
|
--rx_cur_buf_head;
|
|
--rx_cur_buf_ptr;
|
|
}
|
|
else
|
|
/* Cursor moves pass the first character. Move it back. */
|
|
uart_write_char(' ');
|
|
}
|
|
|
|
static void insert_char(char c)
|
|
{
|
|
int ptr;
|
|
|
|
/* On overflow, discard input */
|
|
if (rx_cur_buf_head == RX_LINE_SIZE)
|
|
return;
|
|
|
|
/* Move buffer ptr to the end if 'c' is new line */
|
|
if (c == '\n')
|
|
rx_cur_buf_ptr = rx_cur_buf_head;
|
|
|
|
/* Move text after cursor. */
|
|
for (ptr = rx_cur_buf_ptr; ptr < rx_cur_buf_head; ++ptr)
|
|
uart_write_char(rx_cur_buf[ptr]);
|
|
|
|
/* Insert character to rx buffer and move cursor to correct
|
|
* position.
|
|
*/
|
|
repeat_char('\b', ptr - rx_cur_buf_ptr);
|
|
for (ptr = rx_cur_buf_head; ptr > rx_cur_buf_ptr; --ptr)
|
|
rx_cur_buf[ptr] = rx_cur_buf[ptr - 1];
|
|
rx_cur_buf[rx_cur_buf_ptr] = c;
|
|
++rx_cur_buf_head;
|
|
++rx_cur_buf_ptr;
|
|
|
|
/* Insert character directly into rx_buf if not in console mode. */
|
|
if (!console_mode) {
|
|
rx_buf[rx_buf_head] = c;
|
|
rx_buf_head = RX_BUF_NEXT(rx_buf_head);
|
|
if (rx_buf_tail == rx_buf_head)
|
|
rx_buf_tail = RX_BUF_NEXT(rx_buf_tail);
|
|
}
|
|
}
|
|
|
|
static int rx_buf_space_available(void)
|
|
{
|
|
if (cmd_history_head == cmd_history_tail)
|
|
return RX_BUF_SIZE;
|
|
return RX_BUF_DIFF(cmd_history[cmd_history_tail].tail,
|
|
cmd_history[CMD_HIST_PREV(cmd_history_head)].head);
|
|
}
|
|
|
|
static void history_save(void)
|
|
{
|
|
int ptr;
|
|
int tail, head;
|
|
int hist_id;
|
|
|
|
/* If there is not enough space in rx buffer, discard the oldest
|
|
* history. */
|
|
while (rx_buf_space_available() < rx_cur_buf_head)
|
|
cmd_history_tail = CMD_HIST_NEXT(cmd_history_tail);
|
|
|
|
/* If history buffer is full, discard the oldest one */
|
|
hist_id = cmd_history_head;
|
|
cmd_history_head = CMD_HIST_NEXT(cmd_history_head);
|
|
if (cmd_history_head == cmd_history_tail)
|
|
cmd_history_tail = CMD_HIST_NEXT(cmd_history_tail);
|
|
|
|
/* Copy the current command, but we do not save the '\n' */
|
|
if (hist_id == cmd_history_tail)
|
|
tail = 0;
|
|
else
|
|
tail = RX_BUF_NEXT(cmd_history[CMD_HIST_PREV(hist_id)].head);
|
|
head = tail;
|
|
for (ptr = 0; ptr < rx_cur_buf_head; ++ptr, head = RX_BUF_NEXT(head))
|
|
rx_buf[head] = rx_cur_buf[ptr];
|
|
if (rx_buf[RX_BUF_PREV(head)] == '\n') {
|
|
head = RX_BUF_PREV(head);
|
|
rx_buf[head] = '\0';
|
|
}
|
|
|
|
cmd_history[hist_id].head = head;
|
|
cmd_history[hist_id].tail = tail;
|
|
}
|
|
|
|
static void history_load(int id)
|
|
{
|
|
int head = cmd_history[id].head;
|
|
int tail = cmd_history[id].tail;
|
|
int ptr;
|
|
|
|
cmd_history_ptr = id;
|
|
|
|
/* Move cursor back to begin of the line. */
|
|
repeat_char('\b', rx_cur_buf_ptr);
|
|
|
|
/* Load command and print it. */
|
|
for (ptr = tail, rx_cur_buf_ptr = 0; ptr != head;
|
|
ptr = RX_BUF_NEXT(ptr), ++rx_cur_buf_ptr) {
|
|
rx_cur_buf[rx_cur_buf_ptr] = rx_buf[ptr];
|
|
uart_write_char(rx_buf[ptr]);
|
|
}
|
|
|
|
/* If needed, space over the remaining text. */
|
|
if (rx_cur_buf_ptr < rx_cur_buf_head) {
|
|
repeat_char(' ', rx_cur_buf_head - rx_cur_buf_ptr);
|
|
repeat_char('\b', rx_cur_buf_head - rx_cur_buf_ptr);
|
|
}
|
|
|
|
rx_cur_buf_head = rx_cur_buf_ptr;
|
|
}
|
|
|
|
static void history_prev(void)
|
|
{
|
|
if (cmd_history_ptr == cmd_history_tail)
|
|
return;
|
|
|
|
/* Stash the current command if we are not currently using history.
|
|
* Prevent loading history if there is no space to stash current
|
|
* command. */
|
|
if (cmd_history_ptr == cmd_history_head) {
|
|
int last_id = CMD_HIST_PREV(cmd_history_head);
|
|
int last_len = RX_BUF_DIFF(cmd_history[last_id].head,
|
|
cmd_history[last_id].tail);
|
|
if (last_len + rx_cur_buf_head > RX_BUF_SIZE)
|
|
return;
|
|
|
|
history_save();
|
|
}
|
|
|
|
cmd_history_ptr = CMD_HIST_PREV(cmd_history_ptr);
|
|
history_load(cmd_history_ptr);
|
|
}
|
|
|
|
static void history_next(void)
|
|
{
|
|
if (cmd_history_ptr == cmd_history_head)
|
|
return;
|
|
|
|
cmd_history_ptr = CMD_HIST_NEXT(cmd_history_ptr);
|
|
history_load(cmd_history_ptr);
|
|
|
|
/* Remove the stashed command if we just loaded it. */
|
|
if (cmd_history_ptr == CMD_HIST_PREV(cmd_history_head))
|
|
cmd_history_head = cmd_history_ptr;
|
|
}
|
|
|
|
/* Helper for UART processing */
|
|
void uart_process(void)
|
|
{
|
|
/* Copy input from buffer until RX fifo empty */
|
|
while (uart_rx_available()) {
|
|
int c = uart_read_char();
|
|
|
|
/* Handle console mode echoing and translation */
|
|
if (console_mode) {
|
|
/* Translate CR and CRLF to LF (newline) */
|
|
if (c == '\r') {
|
|
last_rx_was_cr = 1;
|
|
c = '\n';
|
|
} else if (c == '\n' && last_rx_was_cr) {
|
|
last_rx_was_cr = 0;
|
|
continue;
|
|
} else {
|
|
last_rx_was_cr = 0;
|
|
}
|
|
|
|
/* Handle left and right key, and eat other terminal
|
|
* escape sequences (ESC [ ...).
|
|
* Would be really cool if we used arrow keys to edit
|
|
* command history, but for now it's sufficient just to
|
|
* keep them from causing problems. */
|
|
if (c == 0x1B) {
|
|
in_escape = 1;
|
|
esc_seq_char = c;
|
|
continue;
|
|
} else if (in_escape) {
|
|
if (esc_seq_char == 0x1B && c == '[')
|
|
esc_seq_char = '[';
|
|
else if (esc_seq_char == '[') {
|
|
if (c == 'A') /* Up key */
|
|
history_prev();
|
|
else if (c == 'B') /* Down key */
|
|
history_next();
|
|
else if (c == 'C') /* Right key */
|
|
move_rx_ptr_fwd();
|
|
else if (c == 'D') /* Left key */
|
|
move_rx_ptr_bwd();
|
|
esc_seq_char = 0;
|
|
}
|
|
else
|
|
esc_seq_char = 0;
|
|
|
|
if (isalpha(c) || c == '~') {
|
|
esc_seq_char = 0;
|
|
in_escape = 0;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/* Echo characters directly to the transmit FIFO so we
|
|
* don't interfere with the transmit buffer. */
|
|
if (c == '\n')
|
|
uart_write_char('\r');
|
|
uart_write_char(c);
|
|
|
|
/* Handle backspace if we can */
|
|
if (c == '\b') {
|
|
handle_backspace();
|
|
continue;
|
|
}
|
|
}
|
|
|
|
insert_char(c);
|
|
|
|
/* Call console callback on newline, if in console mode */
|
|
if (console_mode && c == '\n')
|
|
console_has_input();
|
|
}
|
|
|
|
/* Copy output from buffer until TX fifo full or output buffer empty */
|
|
while (uart_tx_ready() && (tx_buf_head != tx_buf_tail)) {
|
|
uart_write_char(tx_buf[tx_buf_tail]);
|
|
tx_buf_tail = TX_BUF_NEXT(tx_buf_tail);
|
|
}
|
|
|
|
/* If output buffer is empty, disable transmit interrupt */
|
|
if (tx_buf_tail == tx_buf_head)
|
|
uart_tx_stop();
|
|
}
|
|
|
|
void uart_set_console_mode(int enable)
|
|
{
|
|
console_mode = enable;
|
|
|
|
if (!enable)
|
|
rx_cur_buf_ptr = rx_cur_buf_head;
|
|
}
|
|
|
|
|
|
int uart_puts(const char *outstr)
|
|
{
|
|
/* Put all characters in the output buffer */
|
|
while (*outstr) {
|
|
if (__tx_char(*outstr++) != 0)
|
|
break;
|
|
}
|
|
|
|
if (uart_tx_stopped())
|
|
uart_tx_start();
|
|
|
|
/* Successful if we consumed all output */
|
|
return *outstr ? EC_ERROR_OVERFLOW : EC_SUCCESS;
|
|
}
|
|
|
|
|
|
int uart_printf(const char *format, ...)
|
|
{
|
|
static const char int_chars[] = "0123456789abcdef";
|
|
static const char error_str[] = "ERROR";
|
|
char intbuf[33];
|
|
/* Longest uint64 in decimal = 20
|
|
* longest uint32 in binary = 32
|
|
*/
|
|
int dropped_chars = 0;
|
|
int is_left;
|
|
int pad_zero;
|
|
int pad_width;
|
|
va_list args;
|
|
char *vstr;
|
|
int vlen;
|
|
|
|
va_start(args, format);
|
|
|
|
while (*format && !dropped_chars) {
|
|
int c = *format++;
|
|
|
|
/* Copy normal characters */
|
|
if (c != '%') {
|
|
dropped_chars |= __tx_char(c);
|
|
continue;
|
|
}
|
|
|
|
/* Get first format character */
|
|
c = *format++;
|
|
|
|
/* Send "%" for "%%" input */
|
|
if (c == '%' || c == '\0') {
|
|
dropped_chars |= __tx_char('%');
|
|
continue;
|
|
}
|
|
|
|
/* Handle %c */
|
|
if (c == 'c') {
|
|
c = va_arg(args, int);
|
|
dropped_chars |= __tx_char(c);
|
|
continue;
|
|
}
|
|
|
|
/* Handle left-justification ("%-5s") */
|
|
is_left = (c == '-');
|
|
if (is_left)
|
|
c = *format++;
|
|
|
|
/* Handle padding with 0's */
|
|
pad_zero = (c == '0');
|
|
if (pad_zero)
|
|
c = *format++;
|
|
|
|
/* Count padding length */
|
|
pad_width = 0;
|
|
while (c >= '0' && c <= '9') {
|
|
pad_width = (10 * pad_width) + c - '0';
|
|
c = *format++;
|
|
}
|
|
if (pad_width > 80) {
|
|
/* Sanity check for width failed */
|
|
format = error_str;
|
|
continue;
|
|
}
|
|
|
|
if (c == 's') {
|
|
vstr = va_arg(args, char *);
|
|
if (vstr == NULL)
|
|
vstr = "(NULL)";
|
|
} else {
|
|
uint64_t v;
|
|
int is_negative = 0;
|
|
int is_64bit = 0;
|
|
int base = 10;
|
|
|
|
if (c == 'l') {
|
|
is_64bit = 1;
|
|
c = *format++;
|
|
}
|
|
|
|
/* Special-case: %T = current time */
|
|
if (c == 'T') {
|
|
v = get_time().val;
|
|
is_64bit = 1;
|
|
} else if (is_64bit) {
|
|
v = va_arg(args, uint64_t);
|
|
} else {
|
|
v = va_arg(args, uint32_t);
|
|
}
|
|
|
|
switch (c) {
|
|
case 'd':
|
|
if (is_64bit) {
|
|
if ((int64_t)v < 0) {
|
|
is_negative = 1;
|
|
if (v != (1ULL << 63))
|
|
v = -v;
|
|
}
|
|
} else {
|
|
if ((int)v < 0) {
|
|
is_negative = 1;
|
|
if (v != (1ULL << 31))
|
|
v = -(int)v;
|
|
}
|
|
}
|
|
break;
|
|
case 'u':
|
|
case 'T':
|
|
break;
|
|
case 'x':
|
|
case 'p':
|
|
base = 16;
|
|
break;
|
|
case 'b':
|
|
base = 2;
|
|
break;
|
|
default:
|
|
format = error_str;
|
|
}
|
|
if (format == error_str)
|
|
continue; /* Bad format specifier */
|
|
|
|
/* Convert integer to string, starting at end of
|
|
* buffer and working backwards. */
|
|
vstr = intbuf + sizeof(intbuf) - 1;
|
|
*(vstr) = '\0';
|
|
|
|
if (!v)
|
|
*(--vstr) = '0';
|
|
|
|
while (v)
|
|
*(--vstr) = int_chars[uint64divmod(&v, base)];
|
|
|
|
if (is_negative)
|
|
*(--vstr) = '-';
|
|
}
|
|
|
|
/* Copy string (or stringified integer) */
|
|
vlen = strlen(vstr);
|
|
while (vlen < pad_width && !is_left) {
|
|
dropped_chars |= __tx_char(pad_zero ? '0' : ' ');
|
|
vlen++;
|
|
}
|
|
while (*vstr)
|
|
dropped_chars |= __tx_char(*vstr++);
|
|
while (vlen < pad_width && is_left) {
|
|
dropped_chars |= __tx_char(' ');
|
|
vlen++;
|
|
}
|
|
}
|
|
va_end(args);
|
|
|
|
if (uart_tx_stopped())
|
|
uart_tx_start();
|
|
|
|
/* Successful if we consumed all output */
|
|
return dropped_chars ? EC_ERROR_OVERFLOW : EC_SUCCESS;
|
|
}
|
|
|
|
void uart_flush_output(void)
|
|
{
|
|
/* Wait for buffer to empty */
|
|
while (tx_buf_head != tx_buf_tail) {
|
|
/* It's possible we're in some other interrupt, and the
|
|
* previous context was doing a printf() or puts() but hadn't
|
|
* enabled the UART interrupt. Check if the interrupt is
|
|
* disabled, and if so, re-enable and trigger it. Note that
|
|
* this check is inside the while loop, so we'll be safe even
|
|
* if the context switches away from us to another partial
|
|
* printf() and back. */
|
|
if (uart_tx_stopped())
|
|
uart_tx_start();
|
|
}
|
|
|
|
/* Wait for transmit FIFO empty */
|
|
uart_tx_flush();
|
|
}
|
|
|
|
void uart_emergency_flush(void)
|
|
{
|
|
do {
|
|
/* Copy output from buffer until TX fifo full
|
|
* or output buffer empty
|
|
*/
|
|
while (uart_tx_ready() &&
|
|
(tx_buf_head != tx_buf_tail)) {
|
|
uart_write_char(tx_buf[tx_buf_tail]);
|
|
tx_buf_tail = TX_BUF_NEXT(tx_buf_tail);
|
|
}
|
|
/* Wait for transmit FIFO empty */
|
|
uart_tx_flush();
|
|
} while (tx_buf_head != tx_buf_tail);
|
|
}
|
|
|
|
|
|
void uart_flush_input(void)
|
|
{
|
|
/* Disable interrupts */
|
|
uart_disable_interrupt();
|
|
|
|
/* Empty the hardware FIFO */
|
|
uart_process();
|
|
|
|
/* Clear the input buffer */
|
|
rx_cur_buf_head = 0;
|
|
rx_buf_tail = rx_buf_head;
|
|
|
|
/* Re-enable interrupts */
|
|
uart_enable_interrupt();
|
|
}
|
|
|
|
|
|
int uart_peek(int c)
|
|
{
|
|
int index = -1;
|
|
int i = 0;
|
|
|
|
/* Disable interrupts while we pull characters out, because the
|
|
* interrupt handler can also modify the tail pointer. */
|
|
uart_disable_interrupt();
|
|
|
|
/* Call interrupt handler to empty the hardware FIFO. The minimum
|
|
* FIFO trigger depth is 1/8 (2 chars), so this is the only way to
|
|
* ensure we've pulled the very last character out of the FIFO. */
|
|
uart_process();
|
|
|
|
for (i = 0; i < rx_cur_buf_head; ++i) {
|
|
if (rx_cur_buf[i] == c) {
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Re-enable interrupts */
|
|
uart_enable_interrupt();
|
|
|
|
return index;
|
|
}
|
|
|
|
|
|
int uart_getc(void)
|
|
{
|
|
int c;
|
|
|
|
/* Disable interrupts */
|
|
uart_disable_interrupt();
|
|
|
|
/* Call interrupt handler to empty the hardware FIFO */
|
|
uart_process();
|
|
|
|
if (rx_buf_tail == rx_buf_head) {
|
|
c = -1; /* No pending input */
|
|
} else {
|
|
c = rx_buf[rx_buf_tail];
|
|
rx_buf_tail = RX_BUF_NEXT(rx_buf_tail);
|
|
}
|
|
|
|
/* Re-enable interrupts */
|
|
uart_enable_interrupt();
|
|
|
|
return c;
|
|
}
|
|
|
|
|
|
int uart_gets(char *dest, int size)
|
|
{
|
|
int got = 0;
|
|
int c;
|
|
|
|
/* Disable interrupts while we pull characters out, because the
|
|
* interrupt handler can also modify the tail pointer. */
|
|
uart_disable_interrupt();
|
|
|
|
/* Call interrupt handler to empty the hardware FIFO */
|
|
uart_process();
|
|
|
|
/* Remove the stashed command if any. */
|
|
if (cmd_history_ptr != cmd_history_head)
|
|
cmd_history_head = CMD_HIST_PREV(cmd_history_head);
|
|
|
|
/* Record last command. */
|
|
if (!(rx_cur_buf_head == 1 && rx_cur_buf[0] == '\n'))
|
|
history_save();
|
|
cmd_history_ptr = cmd_history_head;
|
|
|
|
/* Read characters */
|
|
while (got < size - 1 && got < rx_cur_buf_head) {
|
|
c = rx_cur_buf[got];
|
|
dest[got++] = c;
|
|
if (c == '\n')
|
|
break; /* Stop on newline */
|
|
}
|
|
rx_cur_buf_ptr = 0;
|
|
rx_cur_buf_head = 0;
|
|
rx_cur_buf_tail = rx_cur_buf_head;
|
|
|
|
/* Re-enable interrupts */
|
|
uart_enable_interrupt();
|
|
|
|
/* Null-terminate */
|
|
dest[got] = '\0';
|
|
|
|
/* Return the length we got */
|
|
return got;
|
|
}
|