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