osmocom-bb/src/target/firmware/lib/console.c

198 lines
4.5 KiB
C

/* Ringbuffer based serial console layer, imported from OpenPCD */
/* (C) 2006-2010 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <stdint.h>
#include <string.h>
#include <console.h>
#include <uart.h>
#include <asm/system.h>
struct cons {
char buf[CONS_RB_SIZE];
char *next_inbyte;
char *next_outbyte;
int initialized;
int uart_id;
};
static struct cons cons;
void cons_bind_uart(int uart)
{
cons.uart_id = uart;
}
int cons_get_uart(void)
{
return cons.uart_id;
}
void cons_init(void)
{
memset(cons.buf, 0, sizeof(cons.buf));
cons.next_inbyte = &cons.buf[0];
cons.next_outbyte = &cons.buf[0];
cons.initialized = 1;
}
/* determine how many bytes are left in the ringbuffer without overwriting
bytes that haven't been written to the console yet */
static int __cons_rb_space(void)
{
if (cons.next_inbyte == cons.next_outbyte)
return sizeof(cons.buf)-1;
else if (cons.next_outbyte > cons.next_inbyte)
return (cons.next_outbyte - cons.next_inbyte) -1;
else
return sizeof(cons.buf) - 1 - (cons.next_inbyte - cons.next_outbyte);
}
/* pull one char out of debug ring buffer */
static int cons_rb_pull(char *ret)
{
unsigned long flags;
local_irq_save(flags);
if (cons.next_outbyte == cons.next_inbyte) {
local_irq_restore(flags);
return -1;
}
*ret = *cons.next_outbyte;
cons.next_outbyte++;
if (cons.next_outbyte >= &cons.buf[0]+sizeof(cons.buf)) {
cons.next_outbyte = &cons.buf[0];
}
#if 0
else if (cons.next_outbyte > &cons.buf[0]+sizeof(cons.buf)) {
cons.next_outbyte -= sizeof(cons.buf);
}
#endif
local_irq_restore(flags);
return 0;
}
/* returns if everything was flushed (1) or if there's more to flush (0) */
static void __rb_flush_wait(void)
{
char ch;
while (cons_rb_pull(&ch) >= 0)
uart_putchar_wait(cons.uart_id, ch);
}
/* returns if everything was flushed (1) or if there's more to flush (0) */
static int __rb_flush(void)
{
while (!uart_tx_busy(cons.uart_id)) {
char ch;
if (cons_rb_pull(&ch) < 0) {
/* no more data to write, disable interest in Tx FIFO interrupts */
return 1;
}
uart_putchar_nb(cons.uart_id, ch);
}
/* if we reach here, UART Tx FIFO is busy again */
return 0;
}
/* flush pending data from debug ring buffer to serial port */
int cons_rb_flush(void)
{
return __rb_flush();
}
/* Append bytes to ring buffer, not more than we have left! */
static void __cons_rb_append(const char *data, int len)
{
if (cons.next_inbyte + len >= &cons.buf[0]+sizeof(cons.buf)) {
int before_tail = (&cons.buf[0]+sizeof(cons.buf)) - cons.next_inbyte;
/* copy the first part before we wrap */
memcpy(cons.next_inbyte, data, before_tail);
data += before_tail;
len -= before_tail;
/* reset the buffer */
cons.next_inbyte = &cons.buf[0];
}
memcpy(cons.next_inbyte, data, len);
cons.next_inbyte += len;
}
/* append bytes to the ringbuffer, do one wrap */
int cons_rb_append(const char *data, int len)
{
unsigned long flags;
int bytes_left;
const char *data_cur;
/* we will never be able to write more than the console buffer */
if (len > (int) sizeof(cons.buf))
len = sizeof(cons.buf);
local_irq_save(flags);
bytes_left = __cons_rb_space();
data_cur = data;
if (len > bytes_left) {
/* append what we can */
__cons_rb_append(data_cur, bytes_left);
/* busy-wait for all characters to be transmitted */
__rb_flush_wait();
/* fill it with the remaining bytes */
len -= bytes_left;
data_cur += bytes_left;
}
__cons_rb_append(data_cur, len);
/* we want to get Tx FIFO interrupts */
uart_irq_enable(cons.uart_id, UART_IRQ_TX_EMPTY, 1);
local_irq_restore(flags);
return len;
}
int cons_puts(const char *s)
{
if (cons.initialized) {
return cons_rb_append(s, strlen(s));
} else {
/* if the console is not active yet, we need to fall back */
int i = strlen(s);
while (i--)
uart_putchar_wait(cons.uart_id, *s++);
return i;
}
}
int cons_putchar(char c)
{
if (cons.initialized)
return cons_rb_append(&c, 1);
else {
/* if the console is not active yet, we need to fall back */
uart_putchar_wait(cons.uart_id, c);
return 0;
}
}