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