138 lines
2.9 KiB
C
138 lines
2.9 KiB
C
/* Serial console layer, layered on top of sercomm HDLC */
|
|
|
|
/* (C) 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 <errno.h>
|
|
#include <string.h>
|
|
|
|
#include <asm/system.h>
|
|
|
|
#include <uart.h>
|
|
|
|
#include <console.h>
|
|
#include <osmocom/core/msgb.h>
|
|
#include <comm/sercomm.h>
|
|
#include <comm/sercomm_cons.h>
|
|
|
|
static struct {
|
|
struct msgb *cur_msg;
|
|
} scons;
|
|
|
|
static void raw_puts(const char *s)
|
|
{
|
|
int i = strlen(s);
|
|
int uart_id = sercomm_get_uart();
|
|
while (i--)
|
|
uart_putchar_wait(uart_id, *s++);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
#define raw_putd(x) raw_puts(x)
|
|
#else
|
|
#define raw_putd(x)
|
|
#endif
|
|
|
|
int sercomm_puts(const char *s)
|
|
{
|
|
unsigned long flags;
|
|
const int len = strlen(s);
|
|
unsigned int bytes_left = len;
|
|
|
|
if (!sercomm_initialized()) {
|
|
raw_putd("sercomm not initialized: ");
|
|
raw_puts(s);
|
|
return len - 1;
|
|
}
|
|
|
|
/* This function is called from any context: Supervisor, IRQ, FIQ, ...
|
|
* as such, we need to ensure re-entrant calls are either supported or
|
|
* avoided. */
|
|
local_irq_save(flags);
|
|
local_fiq_disable();
|
|
|
|
while (bytes_left > 0) {
|
|
unsigned int write_num, space_left, flush;
|
|
uint8_t *data;
|
|
|
|
if (!scons.cur_msg)
|
|
scons.cur_msg = sercomm_alloc_msgb(SERCOMM_CONS_ALLOC);
|
|
|
|
if (!scons.cur_msg) {
|
|
raw_putd("cannot allocate sercomm msgb: ");
|
|
raw_puts(s);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* space left in the current msgb */
|
|
space_left = msgb_tailroom(scons.cur_msg);
|
|
|
|
if (space_left <= bytes_left) {
|
|
write_num = space_left;
|
|
/* flush buffer when it is full */
|
|
flush = 1;
|
|
} else {
|
|
write_num = bytes_left;
|
|
flush = 0;
|
|
}
|
|
|
|
/* obtain pointer where to copy the data */
|
|
data = msgb_put(scons.cur_msg, write_num);
|
|
|
|
/* copy data while looking for \n line termination */
|
|
{
|
|
unsigned int i;
|
|
for (i = 0; i < write_num; i++) {
|
|
/* flush buffer at end of line, but skip
|
|
* flushing if we have a backlog in order to
|
|
* increase efficiency of msgb filling */
|
|
if (*s == '\n' &&
|
|
sercomm_tx_queue_depth(SC_DLCI_CONSOLE) < 4)
|
|
flush = 1;
|
|
*data++ = *s++;
|
|
}
|
|
}
|
|
bytes_left -= write_num;
|
|
|
|
if (flush) {
|
|
sercomm_sendmsg(SC_DLCI_CONSOLE, scons.cur_msg);
|
|
/* reset scons.cur_msg pointer to ensure we allocate
|
|
* a new one next round */
|
|
scons.cur_msg = NULL;
|
|
}
|
|
}
|
|
|
|
local_irq_restore(flags);
|
|
|
|
return len - 1;
|
|
}
|
|
|
|
int sercomm_putchar(int c)
|
|
{
|
|
char s[2];
|
|
int rc;
|
|
|
|
s[0] = c & 0xff;
|
|
s[1] = '\0';
|
|
|
|
rc = sercomm_puts(s);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
return c;
|
|
}
|