fedora core 6 1.2949 + vserver 2.2.0
[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 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  */
32
33 #include <linux/version.h>
34 #include <linux/module.h>
35 #include <linux/errno.h>
36 #include <linux/signal.h>
37 #include <linux/sched.h>
38 #include <linux/interrupt.h>
39 #include <linux/tty.h>
40 #include <linux/tty_flip.h>
41 #include <linux/vt.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/xenbus.h>
61 #include <xen/xencons.h>
62
63 /*
64  * Modes:
65  *  'xencons=off'  [XC_OFF]:     Console is disabled.
66  *  'xencons=tty'  [XC_TTY]:     Console attached to '/dev/tty[0-9]+'.
67  *  'xencons=ttyS' [XC_SERIAL]:  Console attached to '/dev/ttyS[0-9]+'.
68  *  'xencons=xvc'  [XC_XVC]:     Console attached to '/dev/xvc0'.
69  *                 [XC_DEFAULT]: DOM0 -> XC_SERIAL ; all others -> XC_TTY.
70  * 
71  * NB. In mode XC_TTY, we create dummy consoles for tty2-63. This suppresses
72  * warnings from standard distro startup scripts.
73  */
74 static enum {
75         XC_OFF, XC_DEFAULT, XC_TTY, XC_SERIAL, XC_XVC
76 } xc_mode = XC_DEFAULT;
77 static int xc_num = -1;
78
79 /* /dev/xvc0 device number allocated by lanana.org. */
80 #define XEN_XVC_MAJOR 204
81 #define XEN_XVC_MINOR 191
82
83 #ifdef CONFIG_MAGIC_SYSRQ
84 static unsigned long sysrq_requested;
85 #endif
86
87 static int __init xencons_setup(char *str)
88 {
89         char *q;
90         int n;
91
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         }
105
106         n = simple_strtol(str, &q, 10);
107         if (q != str)
108                 xc_num = n;
109
110         return 1;
111 }
112 __setup("xencons=", xencons_setup);
113
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 */
119
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);
132
133 /* This lock protects accesses to the common transmit buffer. */
134 static DEFINE_SPINLOCK(xencons_lock);
135
136 /* Common transmit-kick routine. */
137 static void __xencons_tx_flush(void);
138
139 static struct tty_driver *xencons_driver;
140
141 /******************** Kernel console driver ********************************/
142
143 static void kcons_write(struct console *c, const char *s, unsigned int count)
144 {
145         int           i = 0;
146         unsigned long flags;
147
148         spin_lock_irqsave(&xencons_lock, flags);
149
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                 }
157
158                 __xencons_tx_flush();
159         }
160
161         spin_unlock_irqrestore(&xencons_lock, flags);
162 }
163
164 static void kcons_write_dom0(struct console *c, const char *s, unsigned int count)
165 {
166
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 }
176
177 static struct tty_driver *kcons_device(struct console *c, int *index)
178 {
179         *index = 0;
180         return xencons_driver;
181 }
182
183 static struct console kcons_info = {
184         .device = kcons_device,
185         .flags  = CON_PRINTBUFFER | CON_ENABLED,
186         .index  = -1,
187 };
188
189 static int __init xen_console_init(void)
190 {
191         if (!is_running_on_xen())
192                 goto out;
193
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_XVC;
203                 kcons_info.write = kcons_write;
204         }
205
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;
212
213         case XC_SERIAL:
214                 strcpy(kcons_info.name, "ttyS");
215                 if (xc_num == -1)
216                         xc_num = 0;
217                 break;
218
219         case XC_TTY:
220                 strcpy(kcons_info.name, "tty");
221                 if (xc_num == -1)
222                         xc_num = 1;
223                 break;
224
225         default:
226                 goto out;
227         }
228
229         wbuf = alloc_bootmem(wbuf_size);
230
231         register_console(&kcons_info);
232
233  out:
234         return 0;
235 }
236 console_initcall(xen_console_init);
237
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];
244
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);
249
250         /* Send the processed output directly to Xen. */
251         kcons_write_dom0(NULL, printk_buf, printk_len);
252
253         return 0;
254 }
255
256 /*** Forcibly flush console data before dying. ***/
257 void xencons_force_flush(void)
258 {
259         int sz;
260
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;
266
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 }
277
278
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 }
312
313
314 /******************** User-space console driver (/dev/console) ************/
315
316 #define DRV(_d)         (_d)
317 #define DUMMY_TTY(_tty) ((xc_mode == XC_TTY) &&         \
318                          ((_tty)->index != (xc_num - 1)))
319
320 static struct ktermios *xencons_termios[MAX_NR_CONSOLES];
321 static struct ktermios *xencons_termios_locked[MAX_NR_CONSOLES];
322 static struct tty_struct *xencons_tty;
323 static int xencons_priv_irq;
324 static char x_char;
325
326 void xencons_rx(char *buf, unsigned len)
327 {
328         int           i;
329         unsigned long flags;
330
331         spin_lock_irqsave(&xencons_lock, flags);
332         if (xencons_tty == NULL)
333                 goto out;
334
335         for (i = 0; i < len; i++) {
336 #ifdef CONFIG_MAGIC_SYSRQ
337                 if (sysrq_on()) {
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], 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);
360
361  out:
362         spin_unlock_irqrestore(&xencons_lock, flags);
363 }
364
365 static void __xencons_tx_flush(void)
366 {
367         int sent, sz, work_done = 0;
368
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         }
379
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         }
395
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 }
403
404 void xencons_tx(void)
405 {
406         unsigned long flags;
407
408         spin_lock_irqsave(&xencons_lock, flags);
409         __xencons_tx_flush();
410         spin_unlock_irqrestore(&xencons_lock, flags);
411 }
412
413 /* Privileged receive callback and transmit kicker. */
414 static irqreturn_t xencons_priv_interrupt(int irq, void *dev_id)
415 {
416         static char rbuf[16];
417         int         l;
418
419         while ((l = HYPERVISOR_console_io(CONSOLEIO_read, 16, rbuf)) > 0)
420                 xencons_rx(rbuf, l);
421
422         xencons_tx();
423
424         return IRQ_HANDLED;
425 }
426
427 static int xencons_write_room(struct tty_struct *tty)
428 {
429         return wbuf_size - (wp - wc);
430 }
431
432 static int xencons_chars_in_buffer(struct tty_struct *tty)
433 {
434         return wp - wc;
435 }
436
437 static void xencons_send_xchar(struct tty_struct *tty, char ch)
438 {
439         unsigned long flags;
440
441         if (DUMMY_TTY(tty))
442                 return;
443
444         spin_lock_irqsave(&xencons_lock, flags);
445         x_char = ch;
446         __xencons_tx_flush();
447         spin_unlock_irqrestore(&xencons_lock, flags);
448 }
449
450 static void xencons_throttle(struct tty_struct *tty)
451 {
452         if (DUMMY_TTY(tty))
453                 return;
454
455         if (I_IXOFF(tty))
456                 xencons_send_xchar(tty, STOP_CHAR(tty));
457 }
458
459 static void xencons_unthrottle(struct tty_struct *tty)
460 {
461         if (DUMMY_TTY(tty))
462                 return;
463
464         if (I_IXOFF(tty)) {
465                 if (x_char != 0)
466                         x_char = 0;
467                 else
468                         xencons_send_xchar(tty, START_CHAR(tty));
469         }
470 }
471
472 static void xencons_flush_buffer(struct tty_struct *tty)
473 {
474         unsigned long flags;
475
476         if (DUMMY_TTY(tty))
477                 return;
478
479         spin_lock_irqsave(&xencons_lock, flags);
480         wc = wp = 0;
481         spin_unlock_irqrestore(&xencons_lock, flags);
482 }
483
484 static inline int __xencons_put_char(int ch)
485 {
486         char _ch = (char)ch;
487         if ((wp - wc) == wbuf_size)
488                 return 0;
489         wbuf[WBUF_MASK(wp++)] = _ch;
490         return 1;
491 }
492
493 static int xencons_write(
494         struct tty_struct *tty,
495         const unsigned char *buf,
496         int count)
497 {
498         int i;
499         unsigned long flags;
500
501         if (DUMMY_TTY(tty))
502                 return count;
503
504         spin_lock_irqsave(&xencons_lock, flags);
505
506         for (i = 0; i < count; i++)
507                 if (!__xencons_put_char(buf[i]))
508                         break;
509
510         if (i != 0)
511                 __xencons_tx_flush();
512
513         spin_unlock_irqrestore(&xencons_lock, flags);
514
515         return i;
516 }
517
518 static void xencons_put_char(struct tty_struct *tty, u_char ch)
519 {
520         unsigned long flags;
521
522         if (DUMMY_TTY(tty))
523                 return;
524
525         spin_lock_irqsave(&xencons_lock, flags);
526         (void)__xencons_put_char(ch);
527         spin_unlock_irqrestore(&xencons_lock, flags);
528 }
529
530 static void xencons_flush_chars(struct tty_struct *tty)
531 {
532         unsigned long flags;
533
534         if (DUMMY_TTY(tty))
535                 return;
536
537         spin_lock_irqsave(&xencons_lock, flags);
538         __xencons_tx_flush();
539         spin_unlock_irqrestore(&xencons_lock, flags);
540 }
541
542 static void xencons_wait_until_sent(struct tty_struct *tty, int timeout)
543 {
544         unsigned long orig_jiffies = jiffies;
545
546         if (DUMMY_TTY(tty))
547                 return;
548
549         while (DRV(tty->driver)->chars_in_buffer(tty)) {
550                 set_current_state(TASK_INTERRUPTIBLE);
551                 schedule_timeout(1);
552                 if (signal_pending(current))
553                         break;
554                 if (timeout && time_after(jiffies, orig_jiffies + timeout))
555                         break;
556         }
557
558         set_current_state(TASK_RUNNING);
559 }
560
561 static int xencons_open(struct tty_struct *tty, struct file *filp)
562 {
563         unsigned long flags;
564
565         if (DUMMY_TTY(tty))
566                 return 0;
567
568         spin_lock_irqsave(&xencons_lock, flags);
569         tty->driver_data = NULL;
570         if (xencons_tty == NULL)
571                 xencons_tty = tty;
572         __xencons_tx_flush();
573         spin_unlock_irqrestore(&xencons_lock, flags);
574
575         return 0;
576 }
577
578 static void xencons_close(struct tty_struct *tty, struct file *filp)
579 {
580         unsigned long flags;
581
582         if (DUMMY_TTY(tty))
583                 return;
584
585         mutex_lock(&tty_mutex);
586
587         if (tty->count != 1) {
588                 mutex_unlock(&tty_mutex);
589                 return;
590         }
591
592         /* Prevent other threads from re-opening this tty. */
593         set_bit(TTY_CLOSING, &tty->flags);
594         mutex_unlock(&tty_mutex);
595
596         tty->closing = 1;
597         tty_wait_until_sent(tty, 0);
598         if (DRV(tty->driver)->flush_buffer != NULL)
599                 DRV(tty->driver)->flush_buffer(tty);
600         if (tty->ldisc.flush_buffer != NULL)
601                 tty->ldisc.flush_buffer(tty);
602         tty->closing = 0;
603         spin_lock_irqsave(&xencons_lock, flags);
604         xencons_tty = NULL;
605         spin_unlock_irqrestore(&xencons_lock, flags);
606 }
607
608 static struct tty_operations xencons_ops = {
609         .open = xencons_open,
610         .close = xencons_close,
611         .write = xencons_write,
612         .write_room = xencons_write_room,
613         .put_char = xencons_put_char,
614         .flush_chars = xencons_flush_chars,
615         .chars_in_buffer = xencons_chars_in_buffer,
616         .send_xchar = xencons_send_xchar,
617         .flush_buffer = xencons_flush_buffer,
618         .throttle = xencons_throttle,
619         .unthrottle = xencons_unthrottle,
620         .wait_until_sent = xencons_wait_until_sent,
621 };
622
623 static int __init xencons_init(void)
624 {
625         int rc;
626
627         if (!is_running_on_xen())
628                 return -ENODEV;
629
630         if (xc_mode == XC_OFF)
631                 return 0;
632
633         if (!is_initial_xendomain()) {
634                 rc = xencons_ring_init();
635                 if (rc)
636                         return rc;
637         }
638
639         xencons_driver = alloc_tty_driver((xc_mode == XC_TTY) ?
640                                           MAX_NR_CONSOLES : 1);
641         if (xencons_driver == NULL)
642                 return -ENOMEM;
643
644         DRV(xencons_driver)->name            = "xencons";
645         DRV(xencons_driver)->major           = TTY_MAJOR;
646         DRV(xencons_driver)->type            = TTY_DRIVER_TYPE_SERIAL;
647         DRV(xencons_driver)->subtype         = SERIAL_TYPE_NORMAL;
648         DRV(xencons_driver)->init_termios    = tty_std_termios;
649         DRV(xencons_driver)->flags           =
650                 TTY_DRIVER_REAL_RAW |
651                 TTY_DRIVER_RESET_TERMIOS;
652         DRV(xencons_driver)->termios         = xencons_termios;
653         DRV(xencons_driver)->termios_locked  = xencons_termios_locked;
654
655         switch (xc_mode) {
656         case XC_XVC:
657                 DRV(xencons_driver)->name        = "xvc";
658                 DRV(xencons_driver)->major       = XEN_XVC_MAJOR;
659                 DRV(xencons_driver)->minor_start = XEN_XVC_MINOR;
660                 DRV(xencons_driver)->name_base   = xc_num;
661                 break;
662         case XC_SERIAL:
663                 DRV(xencons_driver)->name        = "ttyS";
664                 DRV(xencons_driver)->minor_start = 64 + xc_num;
665                 DRV(xencons_driver)->name_base   = xc_num;
666                 break;
667         default:
668                 DRV(xencons_driver)->name        = "tty";
669                 DRV(xencons_driver)->minor_start = 1;
670                 DRV(xencons_driver)->name_base   = 1;
671                 break;
672         }
673
674         tty_set_operations(xencons_driver, &xencons_ops);
675
676         if ((rc = tty_register_driver(DRV(xencons_driver))) != 0) {
677                 printk("WARNING: Failed to register Xen virtual "
678                        "console driver as '%s%d'\n",
679                        DRV(xencons_driver)->name,
680                        DRV(xencons_driver)->name_base);
681                 put_tty_driver(xencons_driver);
682                 xencons_driver = NULL;
683                 return rc;
684         }
685
686         if (is_initial_xendomain()) {
687                 xencons_priv_irq = bind_virq_to_irqhandler(
688                         VIRQ_CONSOLE,
689                         0,
690                         xencons_priv_interrupt,
691                         0,
692                         "console",
693                         NULL);
694                 BUG_ON(xencons_priv_irq < 0);
695         }
696
697         printk("Xen virtual console successfully installed as %s%d\n",
698                DRV(xencons_driver)->name, xc_num);
699
700         /* Check about framebuffer messing up the console */
701         if (!is_initial_xendomain() &&
702             !xenbus_exists(XBT_NIL, "device", "vfb")) {
703                 /* FIXME: this is ugly */
704                 unregister_console(&kcons_info);
705                 kcons_info.flags |= CON_CONSDEV;
706                 register_console(&kcons_info);
707         }
708
709         return 0;
710 }
711
712 module_init(xencons_init);
713
714 MODULE_LICENSE("Dual BSD/GPL");