142ca2baaac48a41ec10a295bea1d713cca2bb0b
[linux-2.6.git] / drivers / xen / console / console.c
1 /******************************************************************************
2  * console.c
3  * 
4  * Virtual console driver.
5  * 
6  * Copyright (c) 2002-2004, K A Fraser.
7  * 
8  * This file may be distributed separately from the Linux kernel, or
9  * incorporated into other software packages, subject to the following license:
10  * 
11  * Permission is hereby granted, free of charge, to any person obtaining a copy
12  * of this source file (the "Software"), to deal in the Software without
13  * restriction, including without limitation the rights to use, copy, modify,
14  * merge, publish, distribute, sublicense, and/or sell copies of the Software,
15  * and to permit persons to whom the Software is furnished to do so, subject to
16  * the following conditions:
17  * 
18  * The above copyright notice and this permission notice shall be included in
19  * all copies or substantial portions of the Software.
20  * 
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
27  * IN THE SOFTWARE.
28  */
29
30 #include <linux/config.h>
31 #include <linux/version.h>
32 #include <linux/module.h>
33 #include <linux/errno.h>
34 #include <linux/signal.h>
35 #include <linux/sched.h>
36 #include <linux/interrupt.h>
37 #include <linux/tty.h>
38 #include <linux/tty_flip.h>
39 #include <linux/serial.h>
40 #include <linux/major.h>
41 #include <linux/ptrace.h>
42 #include <linux/ioport.h>
43 #include <linux/mm.h>
44 #include <linux/slab.h>
45 #include <linux/init.h>
46 #include <linux/console.h>
47 #include <linux/bootmem.h>
48 #include <asm/io.h>
49 #include <asm/irq.h>
50 #include <asm/uaccess.h>
51 #include <asm-xen/xen-public/event_channel.h>
52 #include <asm-xen/hypervisor.h>
53 #include <asm-xen/evtchn.h>
54 #include <asm-xen/ctrl_if.h>
55
56 /*
57  * Modes:
58  *  'xencons=off'  [XC_OFF]:     Console is disabled.
59  *  'xencons=tty'  [XC_TTY]:     Console attached to '/dev/tty[0-9]+'.
60  *  'xencons=ttyS' [XC_SERIAL]:  Console attached to '/dev/ttyS[0-9]+'.
61  *                 [XC_DEFAULT]: DOM0 -> XC_SERIAL ; all others -> XC_TTY.
62  * 
63  * NB. In mode XC_TTY, we create dummy consoles for tty2-63. This suppresses
64  * warnings from standard distro startup scripts.
65  */
66 static enum { XC_OFF, XC_DEFAULT, XC_TTY, XC_SERIAL } xc_mode = XC_DEFAULT;
67 static int xc_num = -1;
68
69 static int __init xencons_setup(char *str)
70 {
71     char *q;
72     int n;
73
74     if ( !strncmp(str, "ttyS", 4) )
75         xc_mode = XC_SERIAL;
76     else if ( !strncmp(str, "tty", 3) )
77         xc_mode = XC_TTY;
78     else if ( !strncmp(str, "off", 3) )
79         xc_mode = XC_OFF;
80
81     switch ( xc_mode )
82     {
83     case XC_SERIAL:
84         n = simple_strtol( str+4, &q, 10 );
85         if ( q > (str + 4) ) xc_num = n;
86         break;
87     case XC_TTY:
88         n = simple_strtol( str+3, &q, 10 );
89         if ( q > (str + 3) ) xc_num = n;
90         break;
91     default:
92         break;
93     }
94
95     return 1;
96 }
97 __setup("xencons=", xencons_setup);
98
99 /* The kernel and user-land drivers share a common transmit buffer. */
100 static unsigned int wbuf_size = 4096;
101 #define WBUF_MASK(_i) ((_i)&(wbuf_size-1))
102 static char *wbuf;
103 static unsigned int wc, wp; /* write_cons, write_prod */
104
105 static int __init xencons_bufsz_setup(char *str)
106 {
107     unsigned int goal;
108     goal = simple_strtoul(str, NULL, 0);
109     while ( wbuf_size < goal )
110         wbuf_size <<= 1;
111     return 1;
112 }
113 __setup("xencons_bufsz=", xencons_bufsz_setup);
114
115 /* This lock protects accesses to the common transmit buffer. */
116 static spinlock_t xencons_lock = SPIN_LOCK_UNLOCKED;
117
118 /* Common transmit-kick routine. */
119 static void __xencons_tx_flush(void);
120
121 /* This task is used to defer sending console data until there is space. */
122 static void xencons_tx_flush_task_routine(void *data);
123
124 static DECLARE_TQUEUE(xencons_tx_flush_task, 
125                       xencons_tx_flush_task_routine,
126                       NULL);
127
128 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
129 static struct tty_driver *xencons_driver;
130 #else
131 static struct tty_driver xencons_driver;
132 #endif
133
134
135 /******************** Kernel console driver ********************************/
136
137 static void kcons_write(
138     struct console *c, const char *s, unsigned int count)
139 {
140     int           i;
141     unsigned long flags;
142
143     spin_lock_irqsave(&xencons_lock, flags);
144     
145     for ( i = 0; i < count; i++ )
146     {
147         if ( (wp - wc) >= (wbuf_size - 1) )
148             break;
149         if ( (wbuf[WBUF_MASK(wp++)] = s[i]) == '\n' )
150             wbuf[WBUF_MASK(wp++)] = '\r';
151     }
152
153     __xencons_tx_flush();
154
155     spin_unlock_irqrestore(&xencons_lock, flags);
156 }
157
158 static void kcons_write_dom0(
159     struct console *c, const char *s, unsigned int count)
160 {
161     int rc;
162
163     while ( (count > 0) &&
164             ((rc = HYPERVISOR_console_io(
165                 CONSOLEIO_write, count, (char *)s)) > 0) )
166     {
167         count -= rc;
168         s += rc;
169     }
170 }
171
172 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
173 static struct tty_driver *kcons_device(struct console *c, int *index)
174 {
175     *index = c->index;
176     return xencons_driver;
177 }
178 #else
179 static kdev_t kcons_device(struct console *c)
180 {
181     return MKDEV(TTY_MAJOR, (xc_mode == XC_SERIAL) ? 64 : 1);
182 }
183 #endif
184
185 static struct console kcons_info = {
186     device:  kcons_device,
187     flags:   CON_PRINTBUFFER,
188     index:   -1
189 };
190
191 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
192 #define __RETCODE 0
193 static int __init xen_console_init(void)
194 #else
195 #define __RETCODE
196 void xen_console_init(void)
197 #endif
198 {
199     if ( xen_start_info.flags & SIF_INITDOMAIN )
200     {
201         if ( xc_mode == XC_DEFAULT )
202             xc_mode = XC_SERIAL;
203         kcons_info.write = kcons_write_dom0;
204 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
205         if ( xc_mode == XC_SERIAL )
206             kcons_info.flags |= CON_ENABLED;
207 #endif
208     }
209     else
210     {
211         if ( xc_mode == XC_DEFAULT )
212             xc_mode = XC_TTY;
213         kcons_info.write = kcons_write;
214     }
215
216     switch ( xc_mode )
217     {
218     case XC_SERIAL:
219         strcpy(kcons_info.name, "ttyS");
220         if ( xc_num == -1 ) xc_num = 0;
221         break;
222
223     case XC_TTY:
224         strcpy(kcons_info.name, "tty");
225         if ( xc_num == -1 ) xc_num = 1;
226         break;
227
228     default:
229         return __RETCODE;
230     }
231
232     wbuf = alloc_bootmem(wbuf_size);
233
234     register_console(&kcons_info);
235
236     return __RETCODE;
237 }
238 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
239 console_initcall(xen_console_init);
240 #endif
241
242 /*** Useful function for console debugging -- goes straight to Xen. ***/
243 asmlinkage int xprintk(const char *fmt, ...)
244 {
245     va_list args;
246     int printk_len;
247     static char printk_buf[1024];
248     
249     /* Emit the output into the temporary buffer */
250     va_start(args, fmt);
251     printk_len = vsnprintf(printk_buf, sizeof(printk_buf), fmt, args);
252     va_end(args);
253
254     /* Send the processed output directly to Xen. */
255     kcons_write_dom0(NULL, printk_buf, printk_len);
256
257     return 0;
258 }
259
260 /*** Forcibly flush console data before dying. ***/
261 void xencons_force_flush(void)
262 {
263     ctrl_msg_t msg;
264     int        sz;
265
266     /* Emergency console is synchronous, so there's nothing to flush. */
267     if ( xen_start_info.flags & SIF_INITDOMAIN )
268         return;
269
270     /*
271      * We use dangerous control-interface functions that require a quiescent
272      * system and no interrupts. Try to ensure this with a global cli().
273      */
274     local_irq_disable(); /* XXXsmp */
275
276     /* Spin until console data is flushed through to the domain controller. */
277     while ( (wc != wp) && !ctrl_if_transmitter_empty() )
278     {
279         /* Interrupts are disabled -- we must manually reap responses. */
280         ctrl_if_discard_responses();
281
282         if ( (sz = wp - wc) == 0 )
283             continue;
284         if ( sz > sizeof(msg.msg) )
285             sz = sizeof(msg.msg);
286         if ( sz > (wbuf_size - WBUF_MASK(wc)) )
287             sz = wbuf_size - WBUF_MASK(wc);
288
289         msg.type    = CMSG_CONSOLE;
290         msg.subtype = CMSG_CONSOLE_DATA;
291         msg.length  = sz;
292         memcpy(msg.msg, &wbuf[WBUF_MASK(wc)], sz);
293             
294         if ( ctrl_if_send_message_noblock(&msg, NULL, 0) == 0 )
295             wc += sz;
296     }
297 }
298
299
300 /******************** User-space console driver (/dev/console) ************/
301
302 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
303 #define DRV(_d)         (_d)
304 #define TTY_INDEX(_tty) ((_tty)->index)
305 #else
306 static int xencons_refcount;
307 static struct tty_struct *xencons_table[MAX_NR_CONSOLES];
308 #define DRV(_d)         (&(_d))
309 #define TTY_INDEX(_tty) (MINOR((_tty)->device) - xencons_driver.minor_start)
310 #endif
311
312 static struct termios *xencons_termios[MAX_NR_CONSOLES];
313 static struct termios *xencons_termios_locked[MAX_NR_CONSOLES];
314 static struct tty_struct *xencons_tty;
315 static int xencons_priv_irq;
316 static char x_char;
317
318 /* Non-privileged receive callback. */
319 static void xencons_rx(ctrl_msg_t *msg, unsigned long id)
320 {
321     int           i;
322     unsigned long flags;
323
324     spin_lock_irqsave(&xencons_lock, flags);
325     if ( xencons_tty != NULL )
326     {
327         for ( i = 0; i < msg->length; i++ )
328             tty_insert_flip_char(xencons_tty, msg->msg[i], 0);
329         tty_flip_buffer_push(xencons_tty);
330     }
331     spin_unlock_irqrestore(&xencons_lock, flags);
332
333     msg->length = 0;
334     ctrl_if_send_response(msg);
335 }
336
337 /* Privileged and non-privileged transmit worker. */
338 static void __xencons_tx_flush(void)
339 {
340     int        sz, work_done = 0;
341     ctrl_msg_t msg;
342
343     if ( xen_start_info.flags & SIF_INITDOMAIN )
344     {
345         if ( x_char )
346         {
347             kcons_write_dom0(NULL, &x_char, 1);
348             x_char = 0;
349             work_done = 1;
350         }
351
352         while ( wc != wp )
353         {
354             sz = wp - wc;
355             if ( sz > (wbuf_size - WBUF_MASK(wc)) )
356                 sz = wbuf_size - WBUF_MASK(wc);
357             kcons_write_dom0(NULL, &wbuf[WBUF_MASK(wc)], sz);
358             wc += sz;
359             work_done = 1;
360         }
361     }
362     else
363     {
364         while ( x_char )
365         {
366             msg.type    = CMSG_CONSOLE;
367             msg.subtype = CMSG_CONSOLE_DATA;
368             msg.length  = 1;
369             msg.msg[0]  = x_char;
370
371             if ( ctrl_if_send_message_noblock(&msg, NULL, 0) == 0 )
372                 x_char = 0;
373             else if ( ctrl_if_enqueue_space_callback(&xencons_tx_flush_task) )
374                 break;
375
376             work_done = 1;
377         }
378
379         while ( wc != wp )
380         {
381             sz = wp - wc;
382             if ( sz > sizeof(msg.msg) )
383                 sz = sizeof(msg.msg);
384             if ( sz > (wbuf_size - WBUF_MASK(wc)) )
385                 sz = wbuf_size - WBUF_MASK(wc);
386
387             msg.type    = CMSG_CONSOLE;
388             msg.subtype = CMSG_CONSOLE_DATA;
389             msg.length  = sz;
390             memcpy(msg.msg, &wbuf[WBUF_MASK(wc)], sz);
391             
392             if ( ctrl_if_send_message_noblock(&msg, NULL, 0) == 0 )
393                 wc += sz;
394             else if ( ctrl_if_enqueue_space_callback(&xencons_tx_flush_task) )
395                 break;
396
397             work_done = 1;
398         }
399     }
400
401     if ( work_done && (xencons_tty != NULL) )
402     {
403         wake_up_interruptible(&xencons_tty->write_wait);
404         if ( (xencons_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
405              (xencons_tty->ldisc.write_wakeup != NULL) )
406             (xencons_tty->ldisc.write_wakeup)(xencons_tty);
407     }
408 }
409
410 /* Non-privileged transmit kicker. */
411 static void xencons_tx_flush_task_routine(void *data)
412 {
413     unsigned long flags;
414     spin_lock_irqsave(&xencons_lock, flags);
415     __xencons_tx_flush();
416     spin_unlock_irqrestore(&xencons_lock, flags);
417 }
418
419 /* Privileged receive callback and transmit kicker. */
420 static irqreturn_t xencons_priv_interrupt(int irq, void *dev_id,
421                                           struct pt_regs *regs)
422 {
423     static char   rbuf[16];
424     int           i, l;
425     unsigned long flags;
426
427     spin_lock_irqsave(&xencons_lock, flags);
428
429     if ( xencons_tty != NULL )
430     {
431         /* Receive work. */
432         while ( (l = HYPERVISOR_console_io(CONSOLEIO_read, 16, rbuf)) > 0 )
433             for ( i = 0; i < l; i++ )
434                 tty_insert_flip_char(xencons_tty, rbuf[i], 0);
435         if ( xencons_tty->flip.count != 0 )
436             tty_flip_buffer_push(xencons_tty);
437     }
438
439     /* Transmit work. */
440     __xencons_tx_flush();
441
442     spin_unlock_irqrestore(&xencons_lock, flags);
443
444     return IRQ_HANDLED;
445 }
446
447 static int xencons_write_room(struct tty_struct *tty)
448 {
449     return wbuf_size - (wp - wc);
450 }
451
452 static int xencons_chars_in_buffer(struct tty_struct *tty)
453 {
454     return wp - wc;
455 }
456
457 static void xencons_send_xchar(struct tty_struct *tty, char ch)
458 {
459     unsigned long flags;
460
461     if ( TTY_INDEX(tty) != 0 )
462         return;
463
464     spin_lock_irqsave(&xencons_lock, flags);
465     x_char = ch;
466     __xencons_tx_flush();
467     spin_unlock_irqrestore(&xencons_lock, flags);
468 }
469
470 static void xencons_throttle(struct tty_struct *tty)
471 {
472     if ( TTY_INDEX(tty) != 0 )
473         return;
474
475     if ( I_IXOFF(tty) )
476         xencons_send_xchar(tty, STOP_CHAR(tty));
477 }
478
479 static void xencons_unthrottle(struct tty_struct *tty)
480 {
481     if ( TTY_INDEX(tty) != 0 )
482         return;
483
484     if ( I_IXOFF(tty) )
485     {
486         if ( x_char != 0 )
487             x_char = 0;
488         else
489             xencons_send_xchar(tty, START_CHAR(tty));
490     }
491 }
492
493 static void xencons_flush_buffer(struct tty_struct *tty)
494 {
495     unsigned long flags;
496
497     if ( TTY_INDEX(tty) != 0 )
498         return;
499
500     spin_lock_irqsave(&xencons_lock, flags);
501     wc = wp = 0;
502     spin_unlock_irqrestore(&xencons_lock, flags);
503 }
504
505 static inline int __xencons_put_char(int ch)
506 {
507     char _ch = (char)ch;
508     if ( (wp - wc) == wbuf_size )
509         return 0;
510     wbuf[WBUF_MASK(wp++)] = _ch;
511     return 1;
512 }
513
514 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
515 static int xencons_write(
516     struct tty_struct *tty,
517     const unsigned char *buf,
518     int count)
519 {
520     int i;
521     unsigned long flags;
522
523     if ( TTY_INDEX(tty) != 0 )
524         return count;
525
526     spin_lock_irqsave(&xencons_lock, flags);
527
528     for ( i = 0; i < count; i++ )
529         if ( !__xencons_put_char(buf[i]) )
530             break;
531
532     if ( i != 0 )
533         __xencons_tx_flush();
534
535     spin_unlock_irqrestore(&xencons_lock, flags);
536
537     return i;
538 }
539 #else
540 static int xencons_write(
541     struct tty_struct *tty, 
542     int from_user,
543     const u_char *buf, 
544     int count)
545 {
546     int i;
547     unsigned long flags;
548
549     if ( from_user && verify_area(VERIFY_READ, buf, count) )
550         return -EINVAL;
551
552     if ( TTY_INDEX(tty) != 0 )
553         return count;
554
555     spin_lock_irqsave(&xencons_lock, flags);
556
557     for ( i = 0; i < count; i++ )
558     {
559         char ch;
560         if ( from_user )
561             __get_user(ch, buf + i);
562         else
563             ch = buf[i];
564         if ( !__xencons_put_char(ch) )
565             break;
566     }
567
568     if ( i != 0 )
569         __xencons_tx_flush();
570
571     spin_unlock_irqrestore(&xencons_lock, flags);
572
573     return i;
574 }
575 #endif
576
577 static void xencons_put_char(struct tty_struct *tty, u_char ch)
578 {
579     unsigned long flags;
580
581     if ( TTY_INDEX(tty) != 0 )
582         return;
583
584     spin_lock_irqsave(&xencons_lock, flags);
585     (void)__xencons_put_char(ch);
586     spin_unlock_irqrestore(&xencons_lock, flags);
587 }
588
589 static void xencons_flush_chars(struct tty_struct *tty)
590 {
591     unsigned long flags;
592
593     if ( TTY_INDEX(tty) != 0 )
594         return;
595
596     spin_lock_irqsave(&xencons_lock, flags);
597     __xencons_tx_flush();
598     spin_unlock_irqrestore(&xencons_lock, flags);    
599 }
600
601 static void xencons_wait_until_sent(struct tty_struct *tty, int timeout)
602 {
603     unsigned long orig_jiffies = jiffies;
604
605     if ( TTY_INDEX(tty) != 0 )
606         return;
607
608     while ( DRV(tty->driver)->chars_in_buffer(tty) )
609     {
610         set_current_state(TASK_INTERRUPTIBLE);
611         schedule_timeout(1);
612         if ( signal_pending(current) )
613             break;
614         if ( (timeout != 0) && time_after(jiffies, orig_jiffies + timeout) )
615             break;
616     }
617     
618     set_current_state(TASK_RUNNING);
619 }
620
621 static int xencons_open(struct tty_struct *tty, struct file *filp)
622 {
623     unsigned long flags;
624
625     if ( TTY_INDEX(tty) != 0 )
626         return 0;
627
628     spin_lock_irqsave(&xencons_lock, flags);
629     tty->driver_data = NULL;
630     if ( xencons_tty == NULL )
631         xencons_tty = tty;
632     __xencons_tx_flush();
633     spin_unlock_irqrestore(&xencons_lock, flags);    
634
635     return 0;
636 }
637
638 static void xencons_close(struct tty_struct *tty, struct file *filp)
639 {
640     unsigned long flags;
641
642     if ( TTY_INDEX(tty) != 0 )
643         return;
644
645     if ( tty->count == 1 )
646     {
647         tty->closing = 1;
648         tty_wait_until_sent(tty, 0);
649         if ( DRV(tty->driver)->flush_buffer != NULL )
650             DRV(tty->driver)->flush_buffer(tty);
651         if ( tty->ldisc.flush_buffer != NULL )
652             tty->ldisc.flush_buffer(tty);
653         tty->closing = 0;
654         spin_lock_irqsave(&xencons_lock, flags);
655         xencons_tty = NULL;
656         spin_unlock_irqrestore(&xencons_lock, flags);    
657     }
658 }
659
660 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
661 static struct tty_operations xencons_ops = {
662     .open = xencons_open,
663     .close = xencons_close,
664     .write = xencons_write,
665     .write_room = xencons_write_room,
666     .put_char = xencons_put_char,
667     .flush_chars = xencons_flush_chars,
668     .chars_in_buffer = xencons_chars_in_buffer,
669     .send_xchar = xencons_send_xchar,
670     .flush_buffer = xencons_flush_buffer,
671     .throttle = xencons_throttle,
672     .unthrottle = xencons_unthrottle,
673     .wait_until_sent = xencons_wait_until_sent,
674 };
675
676 #ifdef CONFIG_XEN_PRIVILEGED_GUEST
677 static const char *xennullcon_startup(void)
678 {
679     return NULL;
680 }
681
682 static int xennullcon_dummy(void)
683 {
684     return 0;
685 }
686
687 #define DUMMY (void *)xennullcon_dummy
688
689 /*
690  *  The console `switch' structure for the dummy console
691  *
692  *  Most of the operations are dummies.
693  */
694
695 const struct consw xennull_con = {
696     .owner =            THIS_MODULE,
697     .con_startup =      xennullcon_startup,
698     .con_init =         DUMMY,
699     .con_deinit =       DUMMY,
700     .con_clear =        DUMMY,
701     .con_putc =         DUMMY,
702     .con_putcs =        DUMMY,
703     .con_cursor =       DUMMY,
704     .con_scroll =       DUMMY,
705     .con_bmove =        DUMMY,
706     .con_switch =       DUMMY,
707     .con_blank =        DUMMY,
708     .con_font_set =     DUMMY,
709     .con_font_get =     DUMMY,
710     .con_font_default = DUMMY,
711     .con_font_copy =    DUMMY,
712     .con_set_palette =  DUMMY,
713     .con_scrolldelta =  DUMMY,
714 };
715 #endif
716 #endif
717
718 static int __init xencons_init(void)
719 {
720     int rc;
721
722     if ( xc_mode == XC_OFF )
723         return 0;
724
725 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
726     xencons_driver = alloc_tty_driver((xc_mode == XC_SERIAL) ? 
727                                       1 : MAX_NR_CONSOLES);
728     if ( xencons_driver == NULL )
729         return -ENOMEM;
730 #else
731     memset(&xencons_driver, 0, sizeof(struct tty_driver));
732     xencons_driver.magic       = TTY_DRIVER_MAGIC;
733     xencons_driver.refcount    = &xencons_refcount;
734     xencons_driver.table       = xencons_table;
735     xencons_driver.num         = (xc_mode == XC_SERIAL) ? 1 : MAX_NR_CONSOLES;
736 #endif
737
738     DRV(xencons_driver)->major           = TTY_MAJOR;
739     DRV(xencons_driver)->type            = TTY_DRIVER_TYPE_SERIAL;
740     DRV(xencons_driver)->subtype         = SERIAL_TYPE_NORMAL;
741     DRV(xencons_driver)->init_termios    = tty_std_termios;
742     DRV(xencons_driver)->flags           = 
743         TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_NO_DEVFS;
744     DRV(xencons_driver)->termios         = xencons_termios;
745     DRV(xencons_driver)->termios_locked  = xencons_termios_locked;
746
747     if ( xc_mode == XC_SERIAL )
748     {
749         DRV(xencons_driver)->name        = "ttyS";
750         DRV(xencons_driver)->minor_start = 64 + xc_num;
751         DRV(xencons_driver)->name_base   = 0 + xc_num;
752     }
753     else
754     {
755         DRV(xencons_driver)->name        = "tty";
756         DRV(xencons_driver)->minor_start = xc_num;
757         DRV(xencons_driver)->name_base   = xc_num;
758     }
759
760 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
761     tty_set_operations(xencons_driver, &xencons_ops);
762 #else
763     xencons_driver.open            = xencons_open;
764     xencons_driver.close           = xencons_close;
765     xencons_driver.write           = xencons_write;
766     xencons_driver.write_room      = xencons_write_room;
767     xencons_driver.put_char        = xencons_put_char;
768     xencons_driver.flush_chars     = xencons_flush_chars;
769     xencons_driver.chars_in_buffer = xencons_chars_in_buffer;
770     xencons_driver.send_xchar      = xencons_send_xchar;
771     xencons_driver.flush_buffer    = xencons_flush_buffer;
772     xencons_driver.throttle        = xencons_throttle;
773     xencons_driver.unthrottle      = xencons_unthrottle;
774     xencons_driver.wait_until_sent = xencons_wait_until_sent;
775 #endif
776
777     if ( (rc = tty_register_driver(DRV(xencons_driver))) != 0 )
778     {
779         printk("WARNING: Failed to register Xen virtual "
780                "console driver as '%s%d'\n",
781                DRV(xencons_driver)->name, DRV(xencons_driver)->name_base);
782 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
783         put_tty_driver(xencons_driver);
784         xencons_driver = NULL;
785 #endif
786         return rc;
787     }
788
789 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
790     tty_register_device(xencons_driver, 0, NULL);
791 #endif
792
793     if ( xen_start_info.flags & SIF_INITDOMAIN )
794     {
795         xencons_priv_irq = bind_virq_to_irq(VIRQ_CONSOLE);
796         (void)request_irq(xencons_priv_irq,
797                           xencons_priv_interrupt, 0, "console", NULL);
798     }
799     else
800     {
801         (void)ctrl_if_register_receiver(CMSG_CONSOLE, xencons_rx, 0);
802     }
803
804     printk("Xen virtual console successfully installed as %s%d\n",
805            DRV(xencons_driver)->name,
806            DRV(xencons_driver)->name_base );
807     
808     return 0;
809 }
810
811 module_init(xencons_init);