Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 | ||
b68e31d0 | 80 | static const struct tty_operations ops = { |
1da177e4 LT |
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 | { | |
81459169 | 120 | char buf[32]; /* Not the nicest way to do it but I need it correct first */ |
1da177e4 LT |
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) { | |
81459169 AC |
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; | |
1da177e4 LT |
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 | } |