/*
* arch/v850/kernel/simcons.c -- Console I/O for GDB v850e simulator
*
* Copyright (C) 2001,02,03 NEC Electronics Corporation
* Copyright (C) 2001,02,03 Miles Bader <miles@gnu.org>
*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file COPYING in the main directory of this
* archive for more details.
*
* Written by Miles Bader <miles@gnu.org>
*/
#include <linux/kernel.h>
#include <linux/console.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/tty_driver.h>
#include <linux/init.h>
#include <asm/poll.h>
#include <asm/string.h>
#include <asm/simsyscall.h>
/* Low-level console. */
static void simcons_write (struct console *co, const char *buf, unsigned len)
{
V850_SIM_SYSCALL (write, 1, buf, len);
}
static int simcons_read (struct console *co, char *buf, unsigned len)
{
return V850_SIM_SYSCALL (read, 0, buf, len);
}
static struct tty_driver *tty_driver;
static struct tty_driver *simcons_device (struct console *c, int *index)
{
*index = c->index;
return tty_driver;
}
static struct console simcons =
{
.name = "simcons",
.write = simcons_write,
.read = simcons_read,
.device = simcons_device,
.flags = CON_PRINTBUFFER,
.index = -1,
};
/* Higher level TTY interface. */
int simcons_tty_open (struct tty_struct *tty, struct file *filp)
{
return 0;
}
int simcons_tty_write (struct tty_struct *tty,
const unsigned char *buf, int count)
{
return V850_SIM_SYSCALL (write, 1, buf, count);
}
int simcons_tty_write_room (struct tty_struct *tty)
{
/* Completely arbitrary. */
return 0x100000;
}
int simcons_tty_chars_in_buffer (struct tty_struct *tty)
{
/* We have no buffer. */
return 0;
}
static const struct tty_operations ops = {
.open = simcons_tty_open,
.write = simcons_tty_write,
.write_room = simcons_tty_write_room,
.chars_in_buffer = simcons_tty_chars_in_buffer,
};
int __init simcons_tty_init (void)
{
struct tty_driver *driver = alloc_tty_driver(1);
int err;
if (!driver)
return -ENOMEM;
driver->name = "simcons";
driver->major = TTY_MAJOR;
driver->minor_start = 64;
driver->type = TTY_DRIVER_TYPE_SYSCONS;
driver->init_termios = tty_std_termios;
tty_set_operations(driver, &ops);
err = tty_register_driver(driver);
if (err) {
put_tty_driver(driver);
return err;
}
tty_driver = driver;
return 0;
}
/* We use `late_initcall' instead of just `__initcall' as a workaround for
the fact that (1) simcons_tty_init can't be called before tty_init,
(2) tty_init is called via `module_init', (3) if statically linked,
module_init == device_init, and (4) there's no ordering of init lists.
We can do this easily because simcons is always statically linked, but
other tty drivers that depend on tty_init and which must use
`module_init' to declare their init routines are likely to be broken. */
late_initcall(simcons_tty_init);
/* Poll for input on the console, and if there's any, deliver it to the
tty driver. */
void simcons_poll_tty (struct tty_struct *tty)
{
char buf[32]; /* Not the nicest way to do it but I need it correct first */
int flip = 0, send_break = 0;
struct pollfd pfd;
pfd.fd = 0;
pfd.events = POLLIN;
if (V850_SIM_SYSCALL (poll, &pfd, 1, 0) > 0) {
if (pfd.revents & POLLIN) {
/* Real block hardware knows the transfer size before
transfer so the new tty buffering doesn't try to handle
this rather weird simulator specific case well */
int rd = V850_SIM_SYSCALL (read, 0, buf, 32);
if (rd > 0) {
tty_insert_flip_string(tty, buf, rd);
flip = 1;
} else
send_break = 1;
} else if (pfd.revents & POLLERR)
send_break = 1;
}
if (send_break) {
tty_insert_flip_char (tty, 0, TTY_BREAK);
flip = 1;
}
if (flip)
tty_schedule_flip (tty);
}
void simcons_poll_ttys (void)
{
if (tty_driver && tty_driver->ttys[0])
simcons_poll_tty (tty_driver->ttys[0]);
}
void simcons_setup (void)
{
V850_SIM_SYSCALL (make_raw, 0);
register_console (&simcons);
printk (KERN_INFO "Console: GDB V850E simulator stdio\n");
}