345 lines
8.2 KiB
C
345 lines
8.2 KiB
C
/*
|
|
* \brief Serial and console driver to access Genode's terminal service
|
|
* \author Stefan Kalkowski <stefan.kalkowski@genode-labs.com>
|
|
* \date 2011-09-16
|
|
*
|
|
* Based on l4ser.c from TU-Dresden's L4Linux.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2011-2013 Genode Labs GmbH
|
|
*
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
* under the terms of the GNU General Public License version 2.
|
|
*/
|
|
|
|
#if defined(CONFIG_MAGIC_SYSRQ)
|
|
#define SUPPORT_SYSRQ
|
|
#endif
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/init.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/console.h>
|
|
#include <linux/sysrq.h>
|
|
#include <linux/device.h>
|
|
#include <linux/tty.h>
|
|
#include <linux/tty_flip.h>
|
|
#include <linux/serial_core.h>
|
|
#include <linux/serial.h>
|
|
|
|
#include <genode/terminal.h>
|
|
|
|
enum { MAX_PORTS = 10 };
|
|
|
|
static struct genode_uart_port {
|
|
struct uart_port port;
|
|
l4_cap_idx_t irq_cap;
|
|
int initialized;
|
|
unsigned idx;
|
|
} genode_serial_port[MAX_PORTS];
|
|
|
|
|
|
static void genode_serial_stop_tx(struct uart_port *port) { }
|
|
|
|
|
|
static void genode_serial_stop_rx(struct uart_port *port) { }
|
|
|
|
|
|
static void genode_serial_enable_ms(struct uart_port *port) { }
|
|
|
|
|
|
static void genode_serial_rx_chars(struct uart_port *port) {
|
|
struct genode_uart_port *l4port = (struct genode_uart_port *)port;
|
|
struct tty_port *tty_port = &port->state->port;
|
|
unsigned long flags;
|
|
unsigned int flg;
|
|
char buf[64];
|
|
unsigned cnt, i;
|
|
|
|
while (1) {
|
|
local_irq_save(flags);
|
|
cnt = genode_terminal_readchar(l4port->idx, buf, sizeof(buf));
|
|
local_irq_restore(flags);
|
|
|
|
if (!cnt)
|
|
break;
|
|
|
|
for (i = 0; i < cnt; i++) {
|
|
port->icount.rx++;
|
|
flg = TTY_NORMAL;
|
|
if (uart_handle_sysrq_char(port, buf[i]))
|
|
continue;
|
|
|
|
tty_insert_flip_char(tty_port, buf[i], flg);
|
|
}
|
|
|
|
if (cnt < sizeof(buf))
|
|
break;
|
|
}
|
|
tty_flip_buffer_push(tty_port);
|
|
return;
|
|
}
|
|
|
|
static void genode_serial_tx_chars(struct uart_port *port) {
|
|
struct genode_uart_port *l4port = (struct genode_uart_port *)port;
|
|
struct circ_buf *xmit = &port->state->xmit;
|
|
unsigned long flags;
|
|
unsigned c;
|
|
|
|
if (port->x_char) {
|
|
local_irq_save(flags);
|
|
genode_terminal_writechar(l4port->idx, &port->x_char, sizeof(char));
|
|
local_irq_restore(flags);
|
|
port->icount.tx++;
|
|
port->x_char = 0;
|
|
return;
|
|
}
|
|
|
|
while (!uart_circ_empty(xmit)) {
|
|
c = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
|
|
local_irq_save(flags);
|
|
genode_terminal_writechar(l4port->idx, &xmit->buf[xmit->tail], c);
|
|
local_irq_restore(flags);
|
|
xmit->tail = (xmit->tail + c) & (UART_XMIT_SIZE - 1);
|
|
port->icount.tx += c;
|
|
}
|
|
}
|
|
|
|
|
|
static void genode_serial_start_tx(struct uart_port *port) {
|
|
genode_serial_tx_chars(port); }
|
|
|
|
|
|
static irqreturn_t genode_serial_int(int irq, void *dev_id) {
|
|
struct uart_port *sport = dev_id;
|
|
genode_serial_rx_chars(sport);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
|
|
static unsigned int genode_serial_tx_empty(struct uart_port *port) {
|
|
return TIOCSER_TEMT; }
|
|
|
|
|
|
static unsigned int genode_serial_get_mctrl(struct uart_port *port) {
|
|
return 0; }
|
|
|
|
|
|
static void genode_serial_set_mctrl(struct uart_port *port, unsigned int mctrl) { }
|
|
|
|
|
|
static void genode_serial_break_ctl(struct uart_port *port, int break_state) { }
|
|
|
|
|
|
static int genode_serial_startup(struct uart_port *port) {
|
|
if (port->irq) {
|
|
int retval = request_irq(port->irq, genode_serial_int, 0, "Genode uart", port);
|
|
if (retval)
|
|
return retval;
|
|
genode_serial_rx_chars(port);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void genode_serial_shutdown(struct uart_port *port) {
|
|
if (port->irq)
|
|
free_irq(port->irq, port);
|
|
}
|
|
|
|
|
|
static void genode_serial_set_termios(struct uart_port *port, struct ktermios *termios,
|
|
struct ktermios *old) { }
|
|
|
|
|
|
static const char *genode_serial_type(struct uart_port *port) {
|
|
return port->type == PORT_SA1100 ? "L4" : NULL; }
|
|
|
|
|
|
static int genode_serial_request_port(struct uart_port *port) {
|
|
return 0; }
|
|
|
|
|
|
static void genode_serial_release_port(struct uart_port *port) { }
|
|
|
|
|
|
static void genode_serial_config_port(struct uart_port *port, int flags) {
|
|
if (flags & UART_CONFIG_TYPE)
|
|
port->type = PORT_SA1100;
|
|
}
|
|
|
|
|
|
static int genode_serial_verify_port(struct uart_port *port, struct serial_struct *ser) {
|
|
return 0; }
|
|
|
|
|
|
static struct uart_ops genode_serial_pops = {
|
|
.tx_empty = genode_serial_tx_empty,
|
|
.set_mctrl = genode_serial_set_mctrl,
|
|
.get_mctrl = genode_serial_get_mctrl,
|
|
.stop_tx = genode_serial_stop_tx,
|
|
.start_tx = genode_serial_start_tx,
|
|
.stop_rx = genode_serial_stop_rx,
|
|
.enable_ms = genode_serial_enable_ms,
|
|
.break_ctl = genode_serial_break_ctl,
|
|
.startup = genode_serial_startup,
|
|
.shutdown = genode_serial_shutdown,
|
|
.set_termios = genode_serial_set_termios,
|
|
.type = genode_serial_type,
|
|
.release_port = genode_serial_release_port,
|
|
.request_port = genode_serial_request_port,
|
|
.config_port = genode_serial_config_port,
|
|
.verify_port = genode_serial_verify_port,
|
|
};
|
|
|
|
|
|
static int __init genode_serial_init_port(int num) {
|
|
int irq;
|
|
unsigned long flags;
|
|
|
|
if (genode_serial_port[num].initialized)
|
|
return 0;
|
|
genode_serial_port[num].initialized = 1;
|
|
|
|
local_irq_save(flags);
|
|
genode_serial_port[num].irq_cap = genode_terminal_irq(num);
|
|
local_irq_restore(flags);
|
|
|
|
if((irq = l4x_register_irq(genode_serial_port[num].irq_cap)) < 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
genode_serial_port[num].port.uartclk = 3686400;
|
|
genode_serial_port[num].port.ops = &genode_serial_pops;
|
|
genode_serial_port[num].port.fifosize = 8;
|
|
genode_serial_port[num].port.line = num;
|
|
genode_serial_port[num].port.iotype = UPIO_MEM;
|
|
genode_serial_port[num].port.membase = (void *)1;
|
|
genode_serial_port[num].port.mapbase = 1;
|
|
genode_serial_port[num].port.flags = UPF_BOOT_AUTOCONF;
|
|
genode_serial_port[num].port.irq = irq;
|
|
genode_serial_port[num].idx = num;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int __init
|
|
genode_console_setup(struct console *co, char *options) {
|
|
struct uart_port *up;
|
|
|
|
if (co->index >= 1 + MAX_PORTS)
|
|
co->index = 0;
|
|
up = &genode_serial_port[co->index].port;
|
|
if (!up)
|
|
return -ENODEV;
|
|
|
|
return uart_set_options(up, co, 115200, 'n', 8, 'n');
|
|
}
|
|
|
|
|
|
/*
|
|
* Interrupts are disabled on entering
|
|
*/
|
|
static void
|
|
genode_console_write(struct console *co, const char *s, unsigned int count) {
|
|
unsigned long flags;
|
|
local_irq_save(flags);
|
|
genode_terminal_writechar(genode_serial_port[co->index].idx, s, count);
|
|
local_irq_restore(flags);
|
|
}
|
|
|
|
|
|
static struct uart_driver genode_reg;
|
|
static struct console genode_console = {
|
|
.name = "ttyS",
|
|
.write = genode_console_write,
|
|
.device = uart_console_device,
|
|
.setup = genode_console_setup,
|
|
.flags = CON_PRINTBUFFER,
|
|
.index = -1,
|
|
.data = &genode_reg,
|
|
};
|
|
|
|
|
|
static int __init genode_rs_console_init(void)
|
|
{
|
|
unsigned long flags;
|
|
local_irq_save(flags);
|
|
if (!genode_terminal_count()) {
|
|
local_irq_restore(flags);
|
|
return -ENODEV;
|
|
}
|
|
local_irq_restore(flags);
|
|
|
|
if (genode_serial_init_port(0))
|
|
return -ENODEV;
|
|
|
|
register_console(&genode_console);
|
|
return 0;
|
|
}
|
|
console_initcall(genode_rs_console_init);
|
|
|
|
|
|
static struct uart_driver genode_reg = {
|
|
.owner = THIS_MODULE,
|
|
.driver_name = "ttyS",
|
|
.dev_name = "ttyS",
|
|
.major = 204,
|
|
.minor = 5,
|
|
.nr = 0,
|
|
.cons = &genode_console,
|
|
.state = 0,
|
|
.tty_driver = 0,
|
|
};
|
|
|
|
|
|
static int __init genode_serial_init(void)
|
|
{
|
|
unsigned i;
|
|
unsigned long flags;
|
|
local_irq_save(flags);
|
|
genode_reg.nr = (genode_terminal_count() > MAX_PORTS)
|
|
? MAX_PORTS : genode_terminal_count();
|
|
local_irq_restore(flags);
|
|
|
|
if (genode_reg.nr == 0)
|
|
return 0;
|
|
|
|
if (uart_register_driver(&genode_reg))
|
|
return -ENODEV;
|
|
|
|
for (i = 0; i < genode_reg.nr; i++) {
|
|
if (genode_serial_init_port(i))
|
|
return -ENODEV;
|
|
uart_add_one_port(&genode_reg, &genode_serial_port[i].port);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void __exit genode_serial_exit(void)
|
|
{
|
|
unsigned i;
|
|
unsigned long flags;
|
|
|
|
for (i = 0; i < genode_reg.nr; i++) {
|
|
uart_remove_one_port(&genode_reg, &genode_serial_port[i].port);
|
|
local_irq_save(flags);
|
|
genode_terminal_stop(i);
|
|
local_irq_restore(flags);
|
|
}
|
|
if (genode_reg.nr)
|
|
uart_unregister_driver(&genode_reg);
|
|
|
|
}
|
|
|
|
|
|
module_init(genode_serial_init);
|
|
module_exit(genode_serial_exit);
|
|
|
|
MODULE_AUTHOR("Stefan Kalkowski <stefan.kalkowski@genode-labs.com");
|
|
MODULE_DESCRIPTION("Genode serial driver");
|
|
MODULE_LICENSE("GPL");
|