Merge /scratch/Ksrc/linux-git/
[linux-2.6] / arch / v850 / kernel / simcons.c
1 /*
2  * arch/v850/kernel/simcons.c -- Console I/O for GDB v850e simulator
3  *
4  *  Copyright (C) 2001,02,03  NEC Electronics Corporation
5  *  Copyright (C) 2001,02,03  Miles Bader <miles@gnu.org>
6  *
7  * This file is subject to the terms and conditions of the GNU General
8  * Public License.  See the file COPYING in the main directory of this
9  * archive for more details.
10  *
11  * Written by Miles Bader <miles@gnu.org>
12  */
13
14 #include <linux/kernel.h>
15 #include <linux/console.h>
16 #include <linux/tty.h>
17 #include <linux/tty_flip.h>
18 #include <linux/tty_driver.h>
19 #include <linux/init.h>
20
21 #include <asm/poll.h>
22 #include <asm/string.h>
23 #include <asm/simsyscall.h>
24
25 \f
26 /*  Low-level console. */
27
28 static void simcons_write (struct console *co, const char *buf, unsigned len)
29 {
30         V850_SIM_SYSCALL (write, 1, buf, len);
31 }
32
33 static int simcons_read (struct console *co, char *buf, unsigned len)
34 {
35         return V850_SIM_SYSCALL (read, 0, buf, len);
36 }
37
38 static struct tty_driver *tty_driver;
39 static struct tty_driver *simcons_device (struct console *c, int *index)
40 {
41         *index = c->index;
42         return tty_driver;
43 }
44
45 static struct console simcons =
46 {
47     .name       = "simcons",
48     .write      = simcons_write,
49     .read       = simcons_read,
50     .device     = simcons_device,
51     .flags      = CON_PRINTBUFFER,
52     .index      = -1,
53 };
54 \f
55 /* Higher level TTY interface.  */
56
57 int simcons_tty_open (struct tty_struct *tty, struct file *filp)
58 {
59         return 0;
60 }
61
62 int simcons_tty_write (struct tty_struct *tty,
63                        const unsigned char *buf, int count)
64 {
65         return V850_SIM_SYSCALL (write, 1, buf, count);
66 }
67
68 int simcons_tty_write_room (struct tty_struct *tty)
69 {
70         /* Completely arbitrary.  */
71         return 0x100000;
72 }
73
74 int simcons_tty_chars_in_buffer (struct tty_struct *tty)
75 {
76         /* We have no buffer.  */
77         return 0;
78 }
79
80 static struct tty_operations ops = {
81         .open = simcons_tty_open,
82         .write = simcons_tty_write,
83         .write_room = simcons_tty_write_room,
84         .chars_in_buffer = simcons_tty_chars_in_buffer,
85 };
86
87 int __init simcons_tty_init (void)
88 {
89         struct tty_driver *driver = alloc_tty_driver(1);
90         int err;
91         if (!driver)
92                 return -ENOMEM;
93         driver->name = "simcons";
94         driver->major = TTY_MAJOR;
95         driver->minor_start = 64;
96         driver->type = TTY_DRIVER_TYPE_SYSCONS;
97         driver->init_termios = tty_std_termios;
98         tty_set_operations(driver, &ops);
99         err = tty_register_driver(driver);
100         if (err) {
101                 put_tty_driver(driver);
102                 return err;
103         }
104         tty_driver = driver;
105         return 0;
106 }
107 /* We use `late_initcall' instead of just `__initcall' as a workaround for
108    the fact that (1) simcons_tty_init can't be called before tty_init,
109    (2) tty_init is called via `module_init', (3) if statically linked,
110    module_init == device_init, and (4) there's no ordering of init lists.
111    We can do this easily because simcons is always statically linked, but
112    other tty drivers that depend on tty_init and which must use
113    `module_init' to declare their init routines are likely to be broken.  */
114 late_initcall(simcons_tty_init);
115 \f
116 /* Poll for input on the console, and if there's any, deliver it to the
117    tty driver.  */
118 void simcons_poll_tty (struct tty_struct *tty)
119 {
120         int flip = 0, send_break = 0;
121         struct pollfd pfd;
122         pfd.fd = 0;
123         pfd.events = POLLIN;
124
125         if (V850_SIM_SYSCALL (poll, &pfd, 1, 0) > 0) {
126                 if (pfd.revents & POLLIN) {
127                         int left = TTY_FLIPBUF_SIZE - tty->flip.count;
128
129                         if (left > 0) {
130                                 unsigned char *buf = tty->flip.char_buf_ptr;
131                                 int rd = V850_SIM_SYSCALL (read, 0, buf, left);
132
133                                 if (rd > 0) {
134                                         tty->flip.count += rd;
135                                         tty->flip.char_buf_ptr += rd;
136                                         memset (tty->flip.flag_buf_ptr, 0, rd);
137                                         tty->flip.flag_buf_ptr += rd;
138                                         flip = 1;
139                                 } else
140                                         send_break = 1;
141                         }
142                 } else if (pfd.revents & POLLERR)
143                         send_break = 1;
144         }
145
146         if (send_break) {
147                 tty_insert_flip_char (tty, 0, TTY_BREAK);               
148                 flip = 1;
149         }
150
151         if (flip)
152                 tty_schedule_flip (tty);
153 }
154
155 void simcons_poll_ttys (void)
156 {
157         if (tty_driver && tty_driver->ttys[0])
158                 simcons_poll_tty (tty_driver->ttys[0]);
159 }
160 \f
161 void simcons_setup (void)
162 {
163         V850_SIM_SYSCALL (make_raw, 0);
164         register_console (&simcons);
165         printk (KERN_INFO "Console: GDB V850E simulator stdio\n");
166 }