ia64/xen-unstable

view linux-2.6-xen-sparse/drivers/xen/console/console.c @ 12653:f32abd365ae0

[LINUX] console: Fix comment.
Signed-off-by: Keir Fraser <keir@xensource.com>
author kfraser@localhost.localdomain
date Wed Nov 29 12:04:49 2006 +0000 (2006-11-29)
parents 8832712b0b37
children 260426e3924f
line source
1 /******************************************************************************
2 * console.c
3 *
4 * Virtual console driver.
5 *
6 * Copyright (c) 2002-2004, K A Fraser.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License version 2
10 * as published by the Free Software Foundation; or, when distributed
11 * separately from the Linux kernel or incorporated into other
12 * software packages, subject to the following license:
13 *
14 * Permission is hereby granted, free of charge, to any person obtaining a copy
15 * of this source file (the "Software"), to deal in the Software without
16 * restriction, including without limitation the rights to use, copy, modify,
17 * merge, publish, distribute, sublicense, and/or sell copies of the Software,
18 * and to permit persons to whom the Software is furnished to do so, subject to
19 * the following conditions:
20 *
21 * The above copyright notice and this permission notice shall be included in
22 * all copies or substantial portions of the Software.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
30 * IN THE SOFTWARE.
31 */
33 #include <linux/config.h>
34 #include <linux/version.h>
35 #include <linux/module.h>
36 #include <linux/errno.h>
37 #include <linux/signal.h>
38 #include <linux/sched.h>
39 #include <linux/interrupt.h>
40 #include <linux/tty.h>
41 #include <linux/tty_flip.h>
42 #include <linux/serial.h>
43 #include <linux/major.h>
44 #include <linux/ptrace.h>
45 #include <linux/ioport.h>
46 #include <linux/mm.h>
47 #include <linux/slab.h>
48 #include <linux/init.h>
49 #include <linux/console.h>
50 #include <linux/bootmem.h>
51 #include <linux/sysrq.h>
52 #include <linux/screen_info.h>
53 #include <asm/io.h>
54 #include <asm/irq.h>
55 #include <asm/uaccess.h>
56 #include <xen/interface/xen.h>
57 #include <xen/interface/event_channel.h>
58 #include <asm/hypervisor.h>
59 #include <xen/evtchn.h>
60 #include <xen/xencons.h>
62 /*
63 * Modes:
64 * 'xencons=off' [XC_OFF]: Console is disabled.
65 * 'xencons=tty' [XC_TTY]: Console attached to '/dev/tty[0-9]+'.
66 * 'xencons=ttyS' [XC_SERIAL]: Console attached to '/dev/ttyS[0-9]+'.
67 * 'xencons=xvc' [XC_XVC]: Console attached to '/dev/xvc0'.
68 * [XC_DEFAULT]: DOM0 -> XC_SERIAL ; all others -> XC_TTY.
69 *
70 * NB. In mode XC_TTY, we create dummy consoles for tty2-63. This suppresses
71 * warnings from standard distro startup scripts.
72 */
73 static enum {
74 XC_OFF, XC_DEFAULT, XC_TTY, XC_SERIAL, XC_XVC
75 } xc_mode = XC_DEFAULT;
76 static int xc_num = -1;
78 /* /dev/xvc0 device number allocated by lanana.org. */
79 #define XEN_XVC_MAJOR 204
80 #define XEN_XVC_MINOR 191
82 #ifdef CONFIG_MAGIC_SYSRQ
83 static unsigned long sysrq_requested;
84 extern int sysrq_enabled;
85 #endif
87 static int __init xencons_setup(char *str)
88 {
89 char *q;
90 int n;
92 if (!strncmp(str, "ttyS", 4)) {
93 xc_mode = XC_SERIAL;
94 str += 4;
95 } else if (!strncmp(str, "tty", 3)) {
96 xc_mode = XC_TTY;
97 str += 3;
98 } else if (!strncmp(str, "xvc", 3)) {
99 xc_mode = XC_XVC;
100 str += 3;
101 } else if (!strncmp(str, "off", 3)) {
102 xc_mode = XC_OFF;
103 str += 3;
104 }
106 n = simple_strtol(str, &q, 10);
107 if (q != str)
108 xc_num = n;
110 return 1;
111 }
112 __setup("xencons=", xencons_setup);
114 /* The kernel and user-land drivers share a common transmit buffer. */
115 static unsigned int wbuf_size = 4096;
116 #define WBUF_MASK(_i) ((_i)&(wbuf_size-1))
117 static char *wbuf;
118 static unsigned int wc, wp; /* write_cons, write_prod */
120 static int __init xencons_bufsz_setup(char *str)
121 {
122 unsigned int goal;
123 goal = simple_strtoul(str, NULL, 0);
124 if (goal) {
125 goal = roundup_pow_of_two(goal);
126 if (wbuf_size < goal)
127 wbuf_size = goal;
128 }
129 return 1;
130 }
131 __setup("xencons_bufsz=", xencons_bufsz_setup);
133 /* This lock protects accesses to the common transmit buffer. */
134 static DEFINE_SPINLOCK(xencons_lock);
136 /* Common transmit-kick routine. */
137 static void __xencons_tx_flush(void);
139 static struct tty_driver *xencons_driver;
141 /******************** Kernel console driver ********************************/
143 static void kcons_write(struct console *c, const char *s, unsigned int count)
144 {
145 int i = 0;
146 unsigned long flags;
148 spin_lock_irqsave(&xencons_lock, flags);
150 while (i < count) {
151 for (; i < count; i++) {
152 if ((wp - wc) >= (wbuf_size - 1))
153 break;
154 if ((wbuf[WBUF_MASK(wp++)] = s[i]) == '\n')
155 wbuf[WBUF_MASK(wp++)] = '\r';
156 }
158 __xencons_tx_flush();
159 }
161 spin_unlock_irqrestore(&xencons_lock, flags);
162 }
164 static void kcons_write_dom0(struct console *c, const char *s, unsigned int count)
165 {
167 while (count > 0) {
168 int rc;
169 rc = HYPERVISOR_console_io( CONSOLEIO_write, count, (char *)s);
170 if (rc <= 0)
171 break;
172 count -= rc;
173 s += rc;
174 }
175 }
177 static struct tty_driver *kcons_device(struct console *c, int *index)
178 {
179 *index = 0;
180 return xencons_driver;
181 }
183 static struct console kcons_info = {
184 .device = kcons_device,
185 .flags = CON_PRINTBUFFER | CON_ENABLED,
186 .index = -1,
187 };
189 static int __init xen_console_init(void)
190 {
191 if (!is_running_on_xen())
192 goto out;
194 if (is_initial_xendomain()) {
195 if (xc_mode == XC_DEFAULT)
196 xc_mode = XC_SERIAL;
197 kcons_info.write = kcons_write_dom0;
198 } else {
199 if (!xen_start_info->console.domU.evtchn)
200 goto out;
201 if (xc_mode == XC_DEFAULT)
202 xc_mode = XC_TTY;
203 kcons_info.write = kcons_write;
204 }
206 switch (xc_mode) {
207 case XC_XVC:
208 strcpy(kcons_info.name, "xvc");
209 if (xc_num == -1)
210 xc_num = 0;
211 break;
213 case XC_SERIAL:
214 strcpy(kcons_info.name, "ttyS");
215 if (xc_num == -1)
216 xc_num = 0;
217 break;
219 case XC_TTY:
220 strcpy(kcons_info.name, "tty");
221 if (xc_num == -1)
222 xc_num = 1;
223 break;
225 default:
226 goto out;
227 }
229 wbuf = alloc_bootmem(wbuf_size);
231 register_console(&kcons_info);
233 out:
234 return 0;
235 }
236 console_initcall(xen_console_init);
238 /*** Useful function for console debugging -- goes straight to Xen. ***/
239 asmlinkage int xprintk(const char *fmt, ...)
240 {
241 va_list args;
242 int printk_len;
243 static char printk_buf[1024];
245 /* Emit the output into the temporary buffer */
246 va_start(args, fmt);
247 printk_len = vsnprintf(printk_buf, sizeof(printk_buf), fmt, args);
248 va_end(args);
250 /* Send the processed output directly to Xen. */
251 kcons_write_dom0(NULL, printk_buf, printk_len);
253 return 0;
254 }
256 /*** Forcibly flush console data before dying. ***/
257 void xencons_force_flush(void)
258 {
259 int sz;
261 /* Emergency console is synchronous, so there's nothing to flush. */
262 if (!is_running_on_xen() ||
263 is_initial_xendomain() ||
264 !xen_start_info->console.domU.evtchn)
265 return;
267 /* Spin until console data is flushed through to the daemon. */
268 while (wc != wp) {
269 int sent = 0;
270 if ((sz = wp - wc) == 0)
271 continue;
272 sent = xencons_ring_send(&wbuf[WBUF_MASK(wc)], sz);
273 if (sent > 0)
274 wc += sent;
275 }
276 }
279 void dom0_init_screen_info(const struct dom0_vga_console_info *info)
280 {
281 switch (info->video_type) {
282 case XEN_VGATYPE_TEXT_MODE_3:
283 screen_info.orig_video_mode = 3;
284 screen_info.orig_video_ega_bx = 3;
285 screen_info.orig_video_isVGA = 1;
286 screen_info.orig_video_lines = info->u.text_mode_3.rows;
287 screen_info.orig_video_cols = info->u.text_mode_3.columns;
288 screen_info.orig_x = info->u.text_mode_3.cursor_x;
289 screen_info.orig_y = info->u.text_mode_3.cursor_y;
290 screen_info.orig_video_points =
291 info->u.text_mode_3.font_height;
292 break;
293 case XEN_VGATYPE_VESA_LFB:
294 screen_info.orig_video_isVGA = VIDEO_TYPE_VLFB;
295 screen_info.lfb_width = info->u.vesa_lfb.width;
296 screen_info.lfb_height = info->u.vesa_lfb.height;
297 screen_info.lfb_depth = info->u.vesa_lfb.bits_per_pixel;
298 screen_info.lfb_base = info->u.vesa_lfb.lfb_base;
299 screen_info.lfb_size = info->u.vesa_lfb.lfb_size;
300 screen_info.lfb_linelength = info->u.vesa_lfb.bytes_per_line;
301 screen_info.red_size = info->u.vesa_lfb.red_size;
302 screen_info.red_pos = info->u.vesa_lfb.red_pos;
303 screen_info.green_size = info->u.vesa_lfb.green_size;
304 screen_info.green_pos = info->u.vesa_lfb.green_pos;
305 screen_info.blue_size = info->u.vesa_lfb.blue_size;
306 screen_info.blue_pos = info->u.vesa_lfb.blue_pos;
307 screen_info.rsvd_size = info->u.vesa_lfb.rsvd_size;
308 screen_info.rsvd_pos = info->u.vesa_lfb.rsvd_pos;
309 break;
310 }
311 }
314 /******************** User-space console driver (/dev/console) ************/
316 #define DRV(_d) (_d)
317 #define DUMMY_TTY(_tty) ((xc_mode == XC_TTY) && \
318 ((_tty)->index != (xc_num - 1)))
320 static struct termios *xencons_termios[MAX_NR_CONSOLES];
321 static struct termios *xencons_termios_locked[MAX_NR_CONSOLES];
322 static struct tty_struct *xencons_tty;
323 static int xencons_priv_irq;
324 static char x_char;
326 void xencons_rx(char *buf, unsigned len, struct pt_regs *regs)
327 {
328 int i;
329 unsigned long flags;
331 spin_lock_irqsave(&xencons_lock, flags);
332 if (xencons_tty == NULL)
333 goto out;
335 for (i = 0; i < len; i++) {
336 #ifdef CONFIG_MAGIC_SYSRQ
337 if (sysrq_enabled) {
338 if (buf[i] == '\x0f') { /* ^O */
339 sysrq_requested = jiffies;
340 continue; /* don't print the sysrq key */
341 } else if (sysrq_requested) {
342 unsigned long sysrq_timeout =
343 sysrq_requested + HZ*2;
344 sysrq_requested = 0;
345 if (time_before(jiffies, sysrq_timeout)) {
346 spin_unlock_irqrestore(
347 &xencons_lock, flags);
348 handle_sysrq(
349 buf[i], regs, xencons_tty);
350 spin_lock_irqsave(
351 &xencons_lock, flags);
352 continue;
353 }
354 }
355 }
356 #endif
357 tty_insert_flip_char(xencons_tty, buf[i], 0);
358 }
359 tty_flip_buffer_push(xencons_tty);
361 out:
362 spin_unlock_irqrestore(&xencons_lock, flags);
363 }
365 static void __xencons_tx_flush(void)
366 {
367 int sent, sz, work_done = 0;
369 if (x_char) {
370 if (is_initial_xendomain())
371 kcons_write_dom0(NULL, &x_char, 1);
372 else
373 while (x_char)
374 if (xencons_ring_send(&x_char, 1) == 1)
375 break;
376 x_char = 0;
377 work_done = 1;
378 }
380 while (wc != wp) {
381 sz = wp - wc;
382 if (sz > (wbuf_size - WBUF_MASK(wc)))
383 sz = wbuf_size - WBUF_MASK(wc);
384 if (is_initial_xendomain()) {
385 kcons_write_dom0(NULL, &wbuf[WBUF_MASK(wc)], sz);
386 wc += sz;
387 } else {
388 sent = xencons_ring_send(&wbuf[WBUF_MASK(wc)], sz);
389 if (sent == 0)
390 break;
391 wc += sent;
392 }
393 work_done = 1;
394 }
396 if (work_done && (xencons_tty != NULL)) {
397 wake_up_interruptible(&xencons_tty->write_wait);
398 if ((xencons_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
399 (xencons_tty->ldisc.write_wakeup != NULL))
400 (xencons_tty->ldisc.write_wakeup)(xencons_tty);
401 }
402 }
404 void xencons_tx(void)
405 {
406 unsigned long flags;
408 spin_lock_irqsave(&xencons_lock, flags);
409 __xencons_tx_flush();
410 spin_unlock_irqrestore(&xencons_lock, flags);
411 }
413 /* Privileged receive callback and transmit kicker. */
414 static irqreturn_t xencons_priv_interrupt(int irq, void *dev_id,
415 struct pt_regs *regs)
416 {
417 static char rbuf[16];
418 int l;
420 while ((l = HYPERVISOR_console_io(CONSOLEIO_read, 16, rbuf)) > 0)
421 xencons_rx(rbuf, l, regs);
423 xencons_tx();
425 return IRQ_HANDLED;
426 }
428 static int xencons_write_room(struct tty_struct *tty)
429 {
430 return wbuf_size - (wp - wc);
431 }
433 static int xencons_chars_in_buffer(struct tty_struct *tty)
434 {
435 return wp - wc;
436 }
438 static void xencons_send_xchar(struct tty_struct *tty, char ch)
439 {
440 unsigned long flags;
442 if (DUMMY_TTY(tty))
443 return;
445 spin_lock_irqsave(&xencons_lock, flags);
446 x_char = ch;
447 __xencons_tx_flush();
448 spin_unlock_irqrestore(&xencons_lock, flags);
449 }
451 static void xencons_throttle(struct tty_struct *tty)
452 {
453 if (DUMMY_TTY(tty))
454 return;
456 if (I_IXOFF(tty))
457 xencons_send_xchar(tty, STOP_CHAR(tty));
458 }
460 static void xencons_unthrottle(struct tty_struct *tty)
461 {
462 if (DUMMY_TTY(tty))
463 return;
465 if (I_IXOFF(tty)) {
466 if (x_char != 0)
467 x_char = 0;
468 else
469 xencons_send_xchar(tty, START_CHAR(tty));
470 }
471 }
473 static void xencons_flush_buffer(struct tty_struct *tty)
474 {
475 unsigned long flags;
477 if (DUMMY_TTY(tty))
478 return;
480 spin_lock_irqsave(&xencons_lock, flags);
481 wc = wp = 0;
482 spin_unlock_irqrestore(&xencons_lock, flags);
483 }
485 static inline int __xencons_put_char(int ch)
486 {
487 char _ch = (char)ch;
488 if ((wp - wc) == wbuf_size)
489 return 0;
490 wbuf[WBUF_MASK(wp++)] = _ch;
491 return 1;
492 }
494 static int xencons_write(
495 struct tty_struct *tty,
496 const unsigned char *buf,
497 int count)
498 {
499 int i;
500 unsigned long flags;
502 if (DUMMY_TTY(tty))
503 return count;
505 spin_lock_irqsave(&xencons_lock, flags);
507 for (i = 0; i < count; i++)
508 if (!__xencons_put_char(buf[i]))
509 break;
511 if (i != 0)
512 __xencons_tx_flush();
514 spin_unlock_irqrestore(&xencons_lock, flags);
516 return i;
517 }
519 static void xencons_put_char(struct tty_struct *tty, u_char ch)
520 {
521 unsigned long flags;
523 if (DUMMY_TTY(tty))
524 return;
526 spin_lock_irqsave(&xencons_lock, flags);
527 (void)__xencons_put_char(ch);
528 spin_unlock_irqrestore(&xencons_lock, flags);
529 }
531 static void xencons_flush_chars(struct tty_struct *tty)
532 {
533 unsigned long flags;
535 if (DUMMY_TTY(tty))
536 return;
538 spin_lock_irqsave(&xencons_lock, flags);
539 __xencons_tx_flush();
540 spin_unlock_irqrestore(&xencons_lock, flags);
541 }
543 static void xencons_wait_until_sent(struct tty_struct *tty, int timeout)
544 {
545 unsigned long orig_jiffies = jiffies;
547 if (DUMMY_TTY(tty))
548 return;
550 while (DRV(tty->driver)->chars_in_buffer(tty)) {
551 set_current_state(TASK_INTERRUPTIBLE);
552 schedule_timeout(1);
553 if (signal_pending(current))
554 break;
555 if (timeout && time_after(jiffies, orig_jiffies + timeout))
556 break;
557 }
559 set_current_state(TASK_RUNNING);
560 }
562 static int xencons_open(struct tty_struct *tty, struct file *filp)
563 {
564 unsigned long flags;
566 if (DUMMY_TTY(tty))
567 return 0;
569 spin_lock_irqsave(&xencons_lock, flags);
570 tty->driver_data = NULL;
571 if (xencons_tty == NULL)
572 xencons_tty = tty;
573 __xencons_tx_flush();
574 spin_unlock_irqrestore(&xencons_lock, flags);
576 return 0;
577 }
579 static void xencons_close(struct tty_struct *tty, struct file *filp)
580 {
581 unsigned long flags;
583 if (DUMMY_TTY(tty))
584 return;
586 down(&tty_sem);
588 if (tty->count != 1) {
589 up(&tty_sem);
590 return;
591 }
593 /* Prevent other threads from re-opening this tty. */
594 set_bit(TTY_CLOSING, &tty->flags);
595 up(&tty_sem);
597 tty->closing = 1;
598 tty_wait_until_sent(tty, 0);
599 if (DRV(tty->driver)->flush_buffer != NULL)
600 DRV(tty->driver)->flush_buffer(tty);
601 if (tty->ldisc.flush_buffer != NULL)
602 tty->ldisc.flush_buffer(tty);
603 tty->closing = 0;
604 spin_lock_irqsave(&xencons_lock, flags);
605 xencons_tty = NULL;
606 spin_unlock_irqrestore(&xencons_lock, flags);
607 }
609 static struct tty_operations xencons_ops = {
610 .open = xencons_open,
611 .close = xencons_close,
612 .write = xencons_write,
613 .write_room = xencons_write_room,
614 .put_char = xencons_put_char,
615 .flush_chars = xencons_flush_chars,
616 .chars_in_buffer = xencons_chars_in_buffer,
617 .send_xchar = xencons_send_xchar,
618 .flush_buffer = xencons_flush_buffer,
619 .throttle = xencons_throttle,
620 .unthrottle = xencons_unthrottle,
621 .wait_until_sent = xencons_wait_until_sent,
622 };
624 static int __init xencons_init(void)
625 {
626 int rc;
628 if (!is_running_on_xen())
629 return -ENODEV;
631 if (xc_mode == XC_OFF)
632 return 0;
634 if (!is_initial_xendomain()) {
635 rc = xencons_ring_init();
636 if (rc)
637 return rc;
638 }
640 xencons_driver = alloc_tty_driver((xc_mode == XC_TTY) ?
641 MAX_NR_CONSOLES : 1);
642 if (xencons_driver == NULL)
643 return -ENOMEM;
645 DRV(xencons_driver)->name = "xencons";
646 DRV(xencons_driver)->major = TTY_MAJOR;
647 DRV(xencons_driver)->type = TTY_DRIVER_TYPE_SERIAL;
648 DRV(xencons_driver)->subtype = SERIAL_TYPE_NORMAL;
649 DRV(xencons_driver)->init_termios = tty_std_termios;
650 DRV(xencons_driver)->flags =
651 TTY_DRIVER_REAL_RAW |
652 TTY_DRIVER_RESET_TERMIOS;
653 DRV(xencons_driver)->termios = xencons_termios;
654 DRV(xencons_driver)->termios_locked = xencons_termios_locked;
656 switch (xc_mode) {
657 case XC_XVC:
658 DRV(xencons_driver)->name = "xvc";
659 DRV(xencons_driver)->major = XEN_XVC_MAJOR;
660 DRV(xencons_driver)->minor_start = XEN_XVC_MINOR;
661 DRV(xencons_driver)->name_base = xc_num;
662 break;
663 case XC_SERIAL:
664 DRV(xencons_driver)->name = "ttyS";
665 DRV(xencons_driver)->minor_start = 64 + xc_num;
666 DRV(xencons_driver)->name_base = xc_num;
667 break;
668 default:
669 DRV(xencons_driver)->name = "tty";
670 DRV(xencons_driver)->minor_start = 1;
671 DRV(xencons_driver)->name_base = 1;
672 break;
673 }
675 tty_set_operations(xencons_driver, &xencons_ops);
677 if ((rc = tty_register_driver(DRV(xencons_driver))) != 0) {
678 printk("WARNING: Failed to register Xen virtual "
679 "console driver as '%s%d'\n",
680 DRV(xencons_driver)->name,
681 DRV(xencons_driver)->name_base);
682 put_tty_driver(xencons_driver);
683 xencons_driver = NULL;
684 return rc;
685 }
687 if (is_initial_xendomain()) {
688 xencons_priv_irq = bind_virq_to_irqhandler(
689 VIRQ_CONSOLE,
690 0,
691 xencons_priv_interrupt,
692 0,
693 "console",
694 NULL);
695 BUG_ON(xencons_priv_irq < 0);
696 }
698 printk("Xen virtual console successfully installed as %s%d\n",
699 DRV(xencons_driver)->name, xc_num);
701 return 0;
702 }
704 module_init(xencons_init);
706 MODULE_LICENSE("Dual BSD/GPL");