d7a55515328452c52109246bfbc17ae57dee66b7
[linux-flexiantxendom0-3.2.10.git] / drivers / char / hvc_console.c
1 /*
2  * Copyright (C) 2001 Anton Blanchard <anton@au.ibm.com>, IBM
3  * Copyright (C) 2001 Paul Mackerras <paulus@au.ibm.com>, IBM
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18  */
19
20 #include <linux/init.h>
21 #include <linux/module.h>
22 #include <linux/console.h>
23 #include <linux/major.h>
24 #include <linux/kernel.h>
25 #include <linux/sysrq.h>
26 #include <linux/tty.h>
27 #include <linux/tty_flip.h>
28 #include <linux/sched.h>
29 #include <linux/kbd_kern.h>
30 #include <asm/uaccess.h>
31 #include <linux/spinlock.h>
32
33 extern int hvc_count(int *);
34 extern int hvc_get_chars(int index, char *buf, int count);
35 extern int hvc_put_chars(int index, const char *buf, int count);
36
37 #define HVC_MAJOR       229
38 #define HVC_MINOR       0
39
40 #define MAX_NR_HVC_CONSOLES     4
41
42 #define TIMEOUT         ((HZ + 99) / 100)
43
44 struct tty_driver hvc_driver;
45 static int hvc_refcount;
46 static struct tty_struct *hvc_table[MAX_NR_HVC_CONSOLES];
47 static struct termios *hvc_termios[MAX_NR_HVC_CONSOLES];
48 static struct termios *hvc_termios_locked[MAX_NR_HVC_CONSOLES];
49 static int hvc_offset;
50 #ifdef CONFIG_MAGIC_SYSRQ
51 static int sysrq_pressed;
52 #endif
53
54 #define N_OUTBUF        16
55
56 #define __ALIGNED__     __attribute__((__aligned__(8)))
57
58 struct hvc_struct {
59         spinlock_t lock;
60         int index;
61         struct tty_struct *tty;
62         unsigned int count;
63         int do_wakeup;
64         char outbuf[N_OUTBUF] __ALIGNED__;
65         int n_outbuf;
66 };
67
68 struct hvc_struct hvc_struct[MAX_NR_HVC_CONSOLES];
69
70 static int hvc_open(struct tty_struct *tty, struct file * filp)
71 {
72         int line = tty->index;
73         struct hvc_struct *hp;
74         unsigned long flags;
75
76         if (line < 0 || line >= MAX_NR_HVC_CONSOLES)
77                 return -ENODEV;
78         hp = &hvc_struct[line];
79
80         tty->driver_data = hp;
81         spin_lock_irqsave(&hp->lock, flags);
82         hp->tty = tty;
83         hp->count++;
84         spin_unlock_irqrestore(&hp->lock, flags);
85
86         return 0;
87 }
88
89 static void hvc_close(struct tty_struct *tty, struct file * filp)
90 {
91         struct hvc_struct *hp = tty->driver_data;
92         unsigned long flags;
93
94         if (tty_hung_up_p(filp))
95                 return;
96         spin_lock_irqsave(&hp->lock, flags);
97         if (--hp->count == 0)
98                 hp->tty = NULL;
99         else if (hp->count < 0)
100                 printk(KERN_ERR "hvc_close %lu: oops, count is %d\n",
101                        hp - hvc_struct, hp->count);
102         spin_unlock_irqrestore(&hp->lock, flags);
103 }
104
105 static void hvc_hangup(struct tty_struct *tty)
106 {
107         struct hvc_struct *hp = tty->driver_data;
108
109         hp->count = 0;
110         hp->tty = NULL;
111 }
112
113 /* called with hp->lock held */
114 static void hvc_push(struct hvc_struct *hp)
115 {
116         int n;
117
118         n = hvc_put_chars(hp->index + hvc_offset, hp->outbuf, hp->n_outbuf);
119         if (n <= 0) {
120                 if (n == 0)
121                         return;
122                 /* throw away output on error; this happens when
123                    there is no session connected to the vterm. */
124                 hp->n_outbuf = 0;
125         } else
126                 hp->n_outbuf -= n;
127         if (hp->n_outbuf > 0)
128                 memmove(hp->outbuf, hp->outbuf + n, hp->n_outbuf);
129         else
130                 hp->do_wakeup = 1;
131 }
132
133 static int hvc_write(struct tty_struct *tty, int from_user,
134                      const unsigned char *buf, int count)
135 {
136         struct hvc_struct *hp = tty->driver_data;
137         char *p;
138         int todo, written = 0;
139         unsigned long flags;
140
141         spin_lock_irqsave(&hp->lock, flags);
142         while (count > 0 && (todo = N_OUTBUF - hp->n_outbuf) > 0) {
143                 if (todo > count)
144                         todo = count;
145                 p = hp->outbuf + hp->n_outbuf;
146                 if (from_user) {
147                         todo -= copy_from_user(p, buf, todo);
148                         if (todo == 0) {
149                                 if (written == 0)
150                                         written = -EFAULT;
151                                 break;
152                         }
153                 } else
154                         memcpy(p, buf, todo);
155                 count -= todo;
156                 buf += todo;
157                 hp->n_outbuf += todo;
158                 written += todo;
159                 hvc_push(hp);
160         }
161         spin_unlock_irqrestore(&hp->lock, flags);
162
163         return written;
164 }
165
166 static int hvc_write_room(struct tty_struct *tty)
167 {
168         struct hvc_struct *hp = tty->driver_data;
169
170         return N_OUTBUF - hp->n_outbuf;
171 }
172
173 static int hvc_chars_in_buffer(struct tty_struct *tty)
174 {
175         struct hvc_struct *hp = tty->driver_data;
176
177         return hp->n_outbuf;
178 }
179
180 static void hvc_poll(int index)
181 {
182         struct hvc_struct *hp = &hvc_struct[index];
183         struct tty_struct *tty;
184         int i, n;
185         char buf[16] __ALIGNED__;
186         unsigned long flags;
187
188         spin_lock_irqsave(&hp->lock, flags);
189
190         if (hp->n_outbuf > 0)
191                 hvc_push(hp);
192
193         tty = hp->tty;
194         if (tty) {
195                 for (;;) {
196                         if (TTY_FLIPBUF_SIZE - tty->flip.count < sizeof(buf))
197                                 break;
198                         n = hvc_get_chars(index + hvc_offset, buf, sizeof(buf));
199                         if (n <= 0)
200                                 break;
201                         for (i = 0; i < n; ++i) {
202 #ifdef CONFIG_MAGIC_SYSRQ               /* Handle the SysRq Hack */
203                                 if (buf[i] == '\x0f') { /* ^O -- should support a sequence */
204                                         sysrq_pressed = 1;
205                                         continue;
206                                 } else if (sysrq_pressed) {
207                                         handle_sysrq(buf[i], NULL, tty);
208                                         sysrq_pressed = 0;
209                                         continue;
210                                 }
211 #endif
212                                 tty_insert_flip_char(tty, buf[i], 0);
213                         }
214                 }
215                 if (tty->flip.count)
216                         tty_schedule_flip(tty);
217
218                 if (hp->do_wakeup) {
219                         hp->do_wakeup = 0;
220                         if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
221                             && tty->ldisc.write_wakeup)
222                                 (tty->ldisc.write_wakeup)(tty);
223                         wake_up_interruptible(&tty->write_wait);
224                 }
225         }
226
227         spin_unlock_irqrestore(&hp->lock, flags);
228 }
229
230 #if defined (CONFIG_XMON)
231 extern unsigned long cpus_in_xmon;
232 #else
233 unsigned long cpus_in_xmon=0;
234 #endif
235
236
237 int khvcd(void *unused)
238 {
239         int i;
240
241         daemonize("khvcd");
242
243         for (;;) {
244                 if (!cpus_in_xmon) {
245                         for (i = 0; i < MAX_NR_HVC_CONSOLES; ++i)
246                                 hvc_poll(i);
247                 }
248                 set_current_state(TASK_INTERRUPTIBLE);
249                 schedule_timeout(TIMEOUT);
250         }
251 }
252
253 int __init hvc_init(void)
254 {
255         int i;
256
257         memset(&hvc_driver, 0, sizeof(struct tty_driver));
258
259         hvc_driver.magic = TTY_DRIVER_MAGIC;
260         hvc_driver.driver_name = "hvc";
261         hvc_driver.name = "hvc/";
262         hvc_driver.major = HVC_MAJOR;
263         hvc_driver.minor_start = HVC_MINOR;
264         hvc_driver.num = hvc_count(&hvc_offset);
265         if (hvc_driver.num > MAX_NR_HVC_CONSOLES)
266                 hvc_driver.num = MAX_NR_HVC_CONSOLES;
267         hvc_driver.type = TTY_DRIVER_TYPE_SYSTEM;
268         hvc_driver.init_termios = tty_std_termios;
269         hvc_driver.flags = TTY_DRIVER_REAL_RAW;
270         hvc_driver.refcount = &hvc_refcount;
271         hvc_driver.table = hvc_table;
272         hvc_driver.termios = hvc_termios;
273         hvc_driver.termios_locked = hvc_termios_locked;
274
275         hvc_driver.open = hvc_open;
276         hvc_driver.close = hvc_close;
277         hvc_driver.write = hvc_write;
278         hvc_driver.hangup = hvc_hangup;
279         hvc_driver.write_room = hvc_write_room;
280         hvc_driver.chars_in_buffer = hvc_chars_in_buffer;
281
282         for (i = 0; i < hvc_driver.num; i++) {
283                 hvc_struct[i].lock = SPIN_LOCK_UNLOCKED;
284                 hvc_struct[i].index = i;
285                 tty_register_device(&hvc_driver, i);
286         }
287
288         if (tty_register_driver(&hvc_driver))
289                 panic("Couldn't register hvc console driver\n");
290
291         if (hvc_driver.num > 0)
292                 kernel_thread(khvcd, NULL, CLONE_KERNEL);
293
294         return 0;
295 }
296
297 static void __exit hvc_exit(void)
298 {
299 }
300
301 void hvc_console_print(struct console *co, const char *b, unsigned count)
302 {
303         char c[16] __ALIGNED__;
304         unsigned i, n;
305         int r, donecr = 0;
306
307         i = n = 0;
308         while (count > 0 || i > 0) {
309                 if (count > 0 && i < sizeof(c)) {
310                         if (b[n] == '\n' && !donecr) {
311                                 c[i++] = '\r';
312                                 donecr = 1;
313                         } else {
314                                 c[i++] = b[n++];
315                                 donecr = 0;
316                                 --count;
317                         }
318                 } else {
319                         r = hvc_put_chars(co->index + hvc_offset, c, i);
320                         if (r < 0) {
321                                 /* throw away chars on error */
322                                 i = 0;
323                         } else if (r > 0) {
324                                 i -= r;
325                                 if (i > 0)
326                                         memmove(c, c+r, i);
327                         }
328                 }
329         }
330 }
331
332 static struct tty_driver *hvc_console_device(struct console *c, int *index)
333 {
334         *index = c->index;
335         return &hvc_driver;
336 }
337
338 static int __init hvc_console_setup(struct console *co, char *options)
339 {
340         if (co->index < 0 || co->index >= MAX_NR_HVC_CONSOLES
341             || co->index >= hvc_count(&hvc_offset))
342                 return -1;
343         return 0;
344 }
345
346 struct console hvc_con_driver = {
347         .name           = "hvc",
348         .write          = hvc_console_print,
349         .device         = hvc_console_device,
350         .setup          = hvc_console_setup,
351         .flags          = CON_PRINTBUFFER,
352         .index          = -1,
353 };
354
355 static int __init hvc_console_init(void)
356 {
357         register_console(&hvc_con_driver);
358         return 0;
359 }
360 console_initcall(hvc_console_init);
361
362 module_init(hvc_init);
363 module_exit(hvc_exit);