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