ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.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 #include <linux/cpumask.h>
33
34 extern int hvc_count(int *);
35 extern int hvc_get_chars(int index, char *buf, int count);
36 extern int hvc_put_chars(int index, const char *buf, int count);
37
38 #define HVC_MAJOR       229
39 #define HVC_MINOR       0
40
41 #define MAX_NR_HVC_CONSOLES     4
42
43 #define TIMEOUT         ((HZ + 99) / 100)
44
45 static struct tty_driver *hvc_driver;
46 static int hvc_offset;
47 #ifdef CONFIG_MAGIC_SYSRQ
48 static int sysrq_pressed;
49 #endif
50
51 #define N_OUTBUF        16
52
53 #define __ALIGNED__     __attribute__((__aligned__(8)))
54
55 struct hvc_struct {
56         spinlock_t lock;
57         int index;
58         struct tty_struct *tty;
59         unsigned int count;
60         int do_wakeup;
61         char outbuf[N_OUTBUF] __ALIGNED__;
62         int n_outbuf;
63 };
64
65 struct hvc_struct hvc_struct[MAX_NR_HVC_CONSOLES];
66
67 static int hvc_open(struct tty_struct *tty, struct file * filp)
68 {
69         int line = tty->index;
70         struct hvc_struct *hp;
71         unsigned long flags;
72
73         if (line < 0 || line >= MAX_NR_HVC_CONSOLES)
74                 return -ENODEV;
75         hp = &hvc_struct[line];
76
77         tty->driver_data = hp;
78         spin_lock_irqsave(&hp->lock, flags);
79         hp->tty = tty;
80         hp->count++;
81         spin_unlock_irqrestore(&hp->lock, flags);
82
83         return 0;
84 }
85
86 static void hvc_close(struct tty_struct *tty, struct file * filp)
87 {
88         struct hvc_struct *hp = tty->driver_data;
89         unsigned long flags;
90
91         if (tty_hung_up_p(filp))
92                 return;
93         spin_lock_irqsave(&hp->lock, flags);
94         if (--hp->count == 0)
95                 hp->tty = NULL;
96         else if (hp->count < 0)
97                 printk(KERN_ERR "hvc_close %lu: oops, count is %d\n",
98                        hp - hvc_struct, hp->count);
99         spin_unlock_irqrestore(&hp->lock, flags);
100 }
101
102 static void hvc_hangup(struct tty_struct *tty)
103 {
104         struct hvc_struct *hp = tty->driver_data;
105
106         hp->count = 0;
107         hp->tty = NULL;
108 }
109
110 /* called with hp->lock held */
111 static void hvc_push(struct hvc_struct *hp)
112 {
113         int n;
114
115         n = hvc_put_chars(hp->index + hvc_offset, hp->outbuf, hp->n_outbuf);
116         if (n <= 0) {
117                 if (n == 0)
118                         return;
119                 /* throw away output on error; this happens when
120                    there is no session connected to the vterm. */
121                 hp->n_outbuf = 0;
122         } else
123                 hp->n_outbuf -= n;
124         if (hp->n_outbuf > 0)
125                 memmove(hp->outbuf, hp->outbuf + n, hp->n_outbuf);
126         else
127                 hp->do_wakeup = 1;
128 }
129
130 static int hvc_write(struct tty_struct *tty, int from_user,
131                      const unsigned char *buf, int count)
132 {
133         struct hvc_struct *hp = tty->driver_data;
134         char *tbuf, *p;
135         int tbsize, rsize, written = 0;
136         unsigned long flags;
137
138         if (from_user) {
139                 tbsize = min(count, (int)PAGE_SIZE);
140                 if (!(tbuf = kmalloc(tbsize, GFP_KERNEL)))
141                         return -ENOMEM;
142
143                 while ((rsize = count - written) > 0) {
144                         int wsize;
145                         if (rsize > tbsize)
146                                 rsize = tbsize;
147
148                         p = tbuf;
149                         rsize -= copy_from_user(p, buf, rsize);
150                         if (!rsize) {
151                                 if (written == 0)
152                                         written = -EFAULT;
153                                 break;
154                         }
155                         buf += rsize;
156                         written += rsize;
157
158                         spin_lock_irqsave(&hp->lock, flags);
159                         for (wsize = N_OUTBUF - hp->n_outbuf; rsize && wsize;
160                                         wsize = N_OUTBUF - hp->n_outbuf) {
161                                 if (wsize > rsize)
162                                         wsize = rsize;
163                                 memcpy(hp->outbuf + hp->n_outbuf, p, wsize);
164                                 hp->n_outbuf += wsize;
165                                 hvc_push(hp);
166                                 rsize -= wsize;
167                                 p += wsize;
168                         }
169                         spin_unlock_irqrestore(&hp->lock, flags);
170
171                         if (rsize)
172                                 break;
173
174                         if (count < tbsize)
175                                 tbsize = count;
176                 }
177
178                 kfree(tbuf);
179         } else {
180                 spin_lock_irqsave(&hp->lock, flags);
181                 while (count > 0 && (rsize = N_OUTBUF - hp->n_outbuf) > 0) {
182                         if (rsize > count)
183                                 rsize = count;
184                         memcpy(hp->outbuf + hp->n_outbuf, buf, rsize);
185                         count -= rsize;
186                         buf += rsize;
187                         hp->n_outbuf += rsize;
188                         written += rsize;
189                         hvc_push(hp);
190                 }
191                 spin_unlock_irqrestore(&hp->lock, flags);
192         }
193
194         return written;
195 }
196
197 static int hvc_write_room(struct tty_struct *tty)
198 {
199         struct hvc_struct *hp = tty->driver_data;
200
201         return N_OUTBUF - hp->n_outbuf;
202 }
203
204 static int hvc_chars_in_buffer(struct tty_struct *tty)
205 {
206         struct hvc_struct *hp = tty->driver_data;
207
208         return hp->n_outbuf;
209 }
210
211 static void hvc_poll(int index)
212 {
213         struct hvc_struct *hp = &hvc_struct[index];
214         struct tty_struct *tty;
215         int i, n;
216         char buf[16] __ALIGNED__;
217         unsigned long flags;
218
219         spin_lock_irqsave(&hp->lock, flags);
220
221         if (hp->n_outbuf > 0)
222                 hvc_push(hp);
223
224         tty = hp->tty;
225         if (tty) {
226                 for (;;) {
227                         if (TTY_FLIPBUF_SIZE - tty->flip.count < sizeof(buf))
228                                 break;
229                         n = hvc_get_chars(index + hvc_offset, buf, sizeof(buf));
230                         if (n <= 0)
231                                 break;
232                         for (i = 0; i < n; ++i) {
233 #ifdef CONFIG_MAGIC_SYSRQ               /* Handle the SysRq Hack */
234                                 if (buf[i] == '\x0f') { /* ^O -- should support a sequence */
235                                         sysrq_pressed = 1;
236                                         continue;
237                                 } else if (sysrq_pressed) {
238                                         handle_sysrq(buf[i], NULL, tty);
239                                         sysrq_pressed = 0;
240                                         continue;
241                                 }
242 #endif
243                                 tty_insert_flip_char(tty, buf[i], 0);
244                         }
245                 }
246                 if (tty->flip.count)
247                         tty_schedule_flip(tty);
248
249                 if (hp->do_wakeup) {
250                         hp->do_wakeup = 0;
251                         if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
252                             && tty->ldisc.write_wakeup)
253                                 (tty->ldisc.write_wakeup)(tty);
254                         wake_up_interruptible(&tty->write_wait);
255                 }
256         }
257
258         spin_unlock_irqrestore(&hp->lock, flags);
259 }
260
261 #if defined(CONFIG_XMON) && defined(CONFIG_SMP)
262 extern cpumask_t cpus_in_xmon;
263 #else
264 static const cpumask_t cpus_in_xmon = CPU_MASK_NONE;
265 #endif
266
267
268 int khvcd(void *unused)
269 {
270         int i;
271
272         daemonize("khvcd");
273
274         for (;;) {
275                 if (cpus_empty(cpus_in_xmon)) {
276                         for (i = 0; i < MAX_NR_HVC_CONSOLES; ++i)
277                                 hvc_poll(i);
278                 }
279                 set_current_state(TASK_INTERRUPTIBLE);
280                 schedule_timeout(TIMEOUT);
281         }
282 }
283
284 static struct tty_operations hvc_ops = {
285         .open = hvc_open,
286         .close = hvc_close,
287         .write = hvc_write,
288         .hangup = hvc_hangup,
289         .write_room = hvc_write_room,
290         .chars_in_buffer = hvc_chars_in_buffer,
291 };
292
293 int __init hvc_init(void)
294 {
295         int num = hvc_count(&hvc_offset);
296         int i;
297
298         if (num > MAX_NR_HVC_CONSOLES)
299                 num = MAX_NR_HVC_CONSOLES;
300
301         hvc_driver = alloc_tty_driver(num);
302         if (!hvc_driver)
303                 return -ENOMEM;
304
305         hvc_driver->owner = THIS_MODULE;
306         hvc_driver->devfs_name = "hvc/";
307         hvc_driver->driver_name = "hvc";
308         hvc_driver->name = "hvc";
309         hvc_driver->major = HVC_MAJOR;
310         hvc_driver->minor_start = HVC_MINOR;
311         hvc_driver->type = TTY_DRIVER_TYPE_SYSTEM;
312         hvc_driver->init_termios = tty_std_termios;
313         hvc_driver->flags = TTY_DRIVER_REAL_RAW;
314         tty_set_operations(hvc_driver, &hvc_ops);
315         for (i = 0; i < num; i++) {
316                 hvc_struct[i].lock = SPIN_LOCK_UNLOCKED;
317                 hvc_struct[i].index = i;
318         }
319
320         if (tty_register_driver(hvc_driver))
321                 panic("Couldn't register hvc console driver\n");
322
323         if (num > 0)
324                 kernel_thread(khvcd, NULL, CLONE_KERNEL);
325
326         return 0;
327 }
328
329 static void __exit hvc_exit(void)
330 {
331 }
332
333 void hvc_console_print(struct console *co, const char *b, unsigned count)
334 {
335         char c[16] __ALIGNED__;
336         unsigned i, n;
337         int r, donecr = 0;
338
339         i = n = 0;
340         while (count > 0 || i > 0) {
341                 if (count > 0 && i < sizeof(c)) {
342                         if (b[n] == '\n' && !donecr) {
343                                 c[i++] = '\r';
344                                 donecr = 1;
345                         } else {
346                                 c[i++] = b[n++];
347                                 donecr = 0;
348                                 --count;
349                         }
350                 } else {
351                         r = hvc_put_chars(co->index + hvc_offset, c, i);
352                         if (r < 0) {
353                                 /* throw away chars on error */
354                                 i = 0;
355                         } else if (r > 0) {
356                                 i -= r;
357                                 if (i > 0)
358                                         memmove(c, c+r, i);
359                         }
360                 }
361         }
362 }
363
364 static struct tty_driver *hvc_console_device(struct console *c, int *index)
365 {
366         *index = c->index;
367         return hvc_driver;
368 }
369
370 static int __init hvc_console_setup(struct console *co, char *options)
371 {
372         if (co->index < 0 || co->index >= MAX_NR_HVC_CONSOLES
373             || co->index >= hvc_count(&hvc_offset))
374                 return -1;
375         return 0;
376 }
377
378 struct console hvc_con_driver = {
379         .name           = "hvc",
380         .write          = hvc_console_print,
381         .device         = hvc_console_device,
382         .setup          = hvc_console_setup,
383         .flags          = CON_PRINTBUFFER,
384         .index          = -1,
385 };
386
387 static int __init hvc_console_init(void)
388 {
389         register_console(&hvc_con_driver);
390         return 0;
391 }
392 console_initcall(hvc_console_init);
393
394 module_init(hvc_init);
395 module_exit(hvc_exit);