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