162 lines
4.0 KiB
C
162 lines
4.0 KiB
C
/*
|
||
* 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");
|
||
}
|