198 lines
4.5 KiB
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;
|
|
}
|
|
}
|