/* * 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 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"); }