Merge branch 'upstream'
[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         char buf[32];   /* Not the nicest way to do it but I need it correct first */
121         int flip = 0, send_break = 0;
122         struct pollfd pfd;
123         pfd.fd = 0;
124         pfd.events = POLLIN;
125
126         if (V850_SIM_SYSCALL (poll, &pfd, 1, 0) > 0) {
127                 if (pfd.revents & POLLIN) {
128                         /* Real block hardware knows the transfer size before
129                            transfer so the new tty buffering doesn't try to handle
130                            this rather weird simulator specific case well */
131                         int rd = V850_SIM_SYSCALL (read, 0, buf, 32);
132                         if (rd > 0) {
133                                 tty_insert_flip_string(tty, buf, rd);
134                                 flip = 1;
135                         } else
136                                 send_break = 1;
137                 } else if (pfd.revents & POLLERR)
138                         send_break = 1;
139         }
140
141         if (send_break) {
142                 tty_insert_flip_char (tty, 0, TTY_BREAK);               
143                 flip = 1;
144         }
145
146         if (flip)
147                 tty_schedule_flip (tty);
148 }
149
150 void simcons_poll_ttys (void)
151 {
152         if (tty_driver && tty_driver->ttys[0])
153                 simcons_poll_tty (tty_driver->ttys[0]);
154 }
155 \f
156 void simcons_setup (void)
157 {
158         V850_SIM_SYSCALL (make_raw, 0);
159         register_console (&simcons);
160         printk (KERN_INFO "Console: GDB V850E simulator stdio\n");
161 }