diff options
Diffstat (limited to 'drivers/char/vt.c')
-rw-r--r-- | drivers/char/vt.c | 688 |
1 files changed, 620 insertions, 68 deletions
diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 6c94879e0b99..fb75da940b59 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c | |||
@@ -63,6 +63,13 @@ | |||
63 | * | 63 | * |
64 | * Removed console_lock, enabled interrupts across all console operations | 64 | * Removed console_lock, enabled interrupts across all console operations |
65 | * 13 March 2001, Andrew Morton | 65 | * 13 March 2001, Andrew Morton |
66 | * | ||
67 | * Fixed UTF-8 mode so alternate charset modes always work according | ||
68 | * to control sequences interpreted in do_con_trol function | ||
69 | * preserving backward VT100 semigraphics compatibility, | ||
70 | * malformed UTF sequences represented as sequences of replacement glyphs, | ||
71 | * original codes or '?' as a last resort if replacement glyph is undefined | ||
72 | * by Adam Tla/lka <atlka@pg.gda.pl>, Aug 2006 | ||
66 | */ | 73 | */ |
67 | 74 | ||
68 | #include <linux/module.h> | 75 | #include <linux/module.h> |
@@ -79,7 +86,6 @@ | |||
79 | #include <linux/mm.h> | 86 | #include <linux/mm.h> |
80 | #include <linux/console.h> | 87 | #include <linux/console.h> |
81 | #include <linux/init.h> | 88 | #include <linux/init.h> |
82 | #include <linux/devfs_fs_kernel.h> | ||
83 | #include <linux/vt_kern.h> | 89 | #include <linux/vt_kern.h> |
84 | #include <linux/selection.h> | 90 | #include <linux/selection.h> |
85 | #include <linux/tiocl.h> | 91 | #include <linux/tiocl.h> |
@@ -87,7 +93,6 @@ | |||
87 | #include <linux/consolemap.h> | 93 | #include <linux/consolemap.h> |
88 | #include <linux/timer.h> | 94 | #include <linux/timer.h> |
89 | #include <linux/interrupt.h> | 95 | #include <linux/interrupt.h> |
90 | #include <linux/config.h> | ||
91 | #include <linux/workqueue.h> | 96 | #include <linux/workqueue.h> |
92 | #include <linux/bootmem.h> | 97 | #include <linux/bootmem.h> |
93 | #include <linux/pm.h> | 98 | #include <linux/pm.h> |
@@ -98,7 +103,22 @@ | |||
98 | #include <asm/system.h> | 103 | #include <asm/system.h> |
99 | #include <asm/uaccess.h> | 104 | #include <asm/uaccess.h> |
100 | 105 | ||
106 | #define MAX_NR_CON_DRIVER 16 | ||
107 | |||
108 | #define CON_DRIVER_FLAG_MODULE 1 | ||
109 | #define CON_DRIVER_FLAG_INIT 2 | ||
110 | |||
111 | struct con_driver { | ||
112 | const struct consw *con; | ||
113 | const char *desc; | ||
114 | struct class_device *class_dev; | ||
115 | int node; | ||
116 | int first; | ||
117 | int last; | ||
118 | int flag; | ||
119 | }; | ||
101 | 120 | ||
121 | static struct con_driver registered_con_driver[MAX_NR_CON_DRIVER]; | ||
102 | const struct consw *conswitchp; | 122 | const struct consw *conswitchp; |
103 | 123 | ||
104 | /* A bitmap for codes <32. A bit of 1 indicates that the code | 124 | /* A bitmap for codes <32. A bit of 1 indicates that the code |
@@ -115,8 +135,8 @@ const struct consw *conswitchp; | |||
115 | #define DEFAULT_BELL_PITCH 750 | 135 | #define DEFAULT_BELL_PITCH 750 |
116 | #define DEFAULT_BELL_DURATION (HZ/8) | 136 | #define DEFAULT_BELL_DURATION (HZ/8) |
117 | 137 | ||
118 | extern void vcs_make_devfs(struct tty_struct *tty); | 138 | extern void vcs_make_sysfs(struct tty_struct *tty); |
119 | extern void vcs_remove_devfs(struct tty_struct *tty); | 139 | extern void vcs_remove_sysfs(struct tty_struct *tty); |
120 | 140 | ||
121 | extern void console_map_init(void); | 141 | extern void console_map_init(void); |
122 | #ifdef CONFIG_PROM_CONSOLE | 142 | #ifdef CONFIG_PROM_CONSOLE |
@@ -717,7 +737,8 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */ | |||
717 | visual_init(vc, currcons, 1); | 737 | visual_init(vc, currcons, 1); |
718 | if (!*vc->vc_uni_pagedir_loc) | 738 | if (!*vc->vc_uni_pagedir_loc) |
719 | con_set_default_unimap(vc); | 739 | con_set_default_unimap(vc); |
720 | vc->vc_screenbuf = kmalloc(vc->vc_screenbuf_size, GFP_KERNEL); | 740 | if (!vc->vc_kmalloced) |
741 | vc->vc_screenbuf = kmalloc(vc->vc_screenbuf_size, GFP_KERNEL); | ||
721 | if (!vc->vc_screenbuf) { | 742 | if (!vc->vc_screenbuf) { |
722 | kfree(vc); | 743 | kfree(vc); |
723 | vc_cons[currcons].d = NULL; | 744 | vc_cons[currcons].d = NULL; |
@@ -865,14 +886,24 @@ int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int lines) | |||
865 | return err; | 886 | return err; |
866 | } | 887 | } |
867 | 888 | ||
889 | int vc_lock_resize(struct vc_data *vc, unsigned int cols, unsigned int lines) | ||
890 | { | ||
891 | int rc; | ||
892 | |||
893 | acquire_console_sem(); | ||
894 | rc = vc_resize(vc, cols, lines); | ||
895 | release_console_sem(); | ||
896 | return rc; | ||
897 | } | ||
868 | 898 | ||
869 | void vc_disallocate(unsigned int currcons) | 899 | void vc_deallocate(unsigned int currcons) |
870 | { | 900 | { |
871 | WARN_CONSOLE_UNLOCKED(); | 901 | WARN_CONSOLE_UNLOCKED(); |
872 | 902 | ||
873 | if (vc_cons_allocated(currcons)) { | 903 | if (vc_cons_allocated(currcons)) { |
874 | struct vc_data *vc = vc_cons[currcons].d; | 904 | struct vc_data *vc = vc_cons[currcons].d; |
875 | vc->vc_sw->con_deinit(vc); | 905 | vc->vc_sw->con_deinit(vc); |
906 | module_put(vc->vc_sw->owner); | ||
876 | if (vc->vc_kmalloced) | 907 | if (vc->vc_kmalloced) |
877 | kfree(vc->vc_screenbuf); | 908 | kfree(vc->vc_screenbuf); |
878 | if (currcons >= MIN_NR_CONSOLES) | 909 | if (currcons >= MIN_NR_CONSOLES) |
@@ -1991,17 +2022,23 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co | |||
1991 | /* Do no translation at all in control states */ | 2022 | /* Do no translation at all in control states */ |
1992 | if (vc->vc_state != ESnormal) { | 2023 | if (vc->vc_state != ESnormal) { |
1993 | tc = c; | 2024 | tc = c; |
1994 | } else if (vc->vc_utf) { | 2025 | } else if (vc->vc_utf && !vc->vc_disp_ctrl) { |
1995 | /* Combine UTF-8 into Unicode */ | 2026 | /* Combine UTF-8 into Unicode */ |
1996 | /* Incomplete characters silently ignored */ | 2027 | /* Malformed sequences as sequences of replacement glyphs */ |
2028 | rescan_last_byte: | ||
1997 | if(c > 0x7f) { | 2029 | if(c > 0x7f) { |
1998 | if (vc->vc_utf_count > 0 && (c & 0xc0) == 0x80) { | 2030 | if (vc->vc_utf_count) { |
1999 | vc->vc_utf_char = (vc->vc_utf_char << 6) | (c & 0x3f); | 2031 | if ((c & 0xc0) == 0x80) { |
2000 | vc->vc_utf_count--; | 2032 | vc->vc_utf_char = (vc->vc_utf_char << 6) | (c & 0x3f); |
2001 | if (vc->vc_utf_count == 0) | 2033 | if (--vc->vc_utf_count) { |
2002 | tc = c = vc->vc_utf_char; | 2034 | vc->vc_npar++; |
2003 | else continue; | 2035 | continue; |
2036 | } | ||
2037 | tc = c = vc->vc_utf_char; | ||
2038 | } else | ||
2039 | goto replacement_glyph; | ||
2004 | } else { | 2040 | } else { |
2041 | vc->vc_npar = 0; | ||
2005 | if ((c & 0xe0) == 0xc0) { | 2042 | if ((c & 0xe0) == 0xc0) { |
2006 | vc->vc_utf_count = 1; | 2043 | vc->vc_utf_count = 1; |
2007 | vc->vc_utf_char = (c & 0x1f); | 2044 | vc->vc_utf_char = (c & 0x1f); |
@@ -2018,14 +2055,15 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co | |||
2018 | vc->vc_utf_count = 5; | 2055 | vc->vc_utf_count = 5; |
2019 | vc->vc_utf_char = (c & 0x01); | 2056 | vc->vc_utf_char = (c & 0x01); |
2020 | } else | 2057 | } else |
2021 | vc->vc_utf_count = 0; | 2058 | goto replacement_glyph; |
2022 | continue; | 2059 | continue; |
2023 | } | 2060 | } |
2024 | } else { | 2061 | } else { |
2062 | if (vc->vc_utf_count) | ||
2063 | goto replacement_glyph; | ||
2025 | tc = c; | 2064 | tc = c; |
2026 | vc->vc_utf_count = 0; | ||
2027 | } | 2065 | } |
2028 | } else { /* no utf */ | 2066 | } else { /* no utf or alternate charset mode */ |
2029 | tc = vc->vc_translate[vc->vc_toggle_meta ? (c | 0x80) : c]; | 2067 | tc = vc->vc_translate[vc->vc_toggle_meta ? (c | 0x80) : c]; |
2030 | } | 2068 | } |
2031 | 2069 | ||
@@ -2040,31 +2078,33 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co | |||
2040 | * direct-to-font zone in UTF-8 mode. | 2078 | * direct-to-font zone in UTF-8 mode. |
2041 | */ | 2079 | */ |
2042 | ok = tc && (c >= 32 || | 2080 | ok = tc && (c >= 32 || |
2043 | (!vc->vc_utf && !(((vc->vc_disp_ctrl ? CTRL_ALWAYS | 2081 | !(vc->vc_disp_ctrl ? (CTRL_ALWAYS >> c) & 1 : |
2044 | : CTRL_ACTION) >> c) & 1))) | 2082 | vc->vc_utf || ((CTRL_ACTION >> c) & 1))) |
2045 | && (c != 127 || vc->vc_disp_ctrl) | 2083 | && (c != 127 || vc->vc_disp_ctrl) |
2046 | && (c != 128+27); | 2084 | && (c != 128+27); |
2047 | 2085 | ||
2048 | if (vc->vc_state == ESnormal && ok) { | 2086 | if (vc->vc_state == ESnormal && ok) { |
2049 | /* Now try to find out how to display it */ | 2087 | /* Now try to find out how to display it */ |
2050 | tc = conv_uni_to_pc(vc, tc); | 2088 | tc = conv_uni_to_pc(vc, tc); |
2051 | if ( tc == -4 ) { | 2089 | if (tc & ~charmask) { |
2090 | if ( tc == -4 ) { | ||
2052 | /* If we got -4 (not found) then see if we have | 2091 | /* If we got -4 (not found) then see if we have |
2053 | defined a replacement character (U+FFFD) */ | 2092 | defined a replacement character (U+FFFD) */ |
2054 | tc = conv_uni_to_pc(vc, 0xfffd); | 2093 | replacement_glyph: |
2055 | 2094 | tc = conv_uni_to_pc(vc, 0xfffd); | |
2056 | /* One reason for the -4 can be that we just | 2095 | if (!(tc & ~charmask)) |
2057 | did a clear_unimap(); | 2096 | goto display_glyph; |
2058 | try at least to show something. */ | 2097 | } else if ( tc != -3 ) |
2059 | if (tc == -4) | 2098 | continue; /* nothing to display */ |
2060 | tc = c; | 2099 | /* no hash table or no replacement -- |
2061 | } else if ( tc == -3 ) { | 2100 | * hope for the best */ |
2062 | /* Bad hash table -- hope for the best */ | 2101 | if ( c & ~charmask ) |
2063 | tc = c; | 2102 | tc = '?'; |
2064 | } | 2103 | else |
2065 | if (tc & ~charmask) | 2104 | tc = c; |
2066 | continue; /* Conversion failed */ | 2105 | } |
2067 | 2106 | ||
2107 | display_glyph: | ||
2068 | if (vc->vc_need_wrap || vc->vc_decim) | 2108 | if (vc->vc_need_wrap || vc->vc_decim) |
2069 | FLUSH | 2109 | FLUSH |
2070 | if (vc->vc_need_wrap) { | 2110 | if (vc->vc_need_wrap) { |
@@ -2088,6 +2128,15 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co | |||
2088 | vc->vc_x++; | 2128 | vc->vc_x++; |
2089 | draw_to = (vc->vc_pos += 2); | 2129 | draw_to = (vc->vc_pos += 2); |
2090 | } | 2130 | } |
2131 | if (vc->vc_utf_count) { | ||
2132 | if (vc->vc_npar) { | ||
2133 | vc->vc_npar--; | ||
2134 | goto display_glyph; | ||
2135 | } | ||
2136 | vc->vc_utf_count = 0; | ||
2137 | c = orig; | ||
2138 | goto rescan_last_byte; | ||
2139 | } | ||
2091 | continue; | 2140 | continue; |
2092 | } | 2141 | } |
2093 | FLUSH | 2142 | FLUSH |
@@ -2484,7 +2533,7 @@ static int con_open(struct tty_struct *tty, struct file *filp) | |||
2484 | tty->winsize.ws_col = vc_cons[currcons].d->vc_cols; | 2533 | tty->winsize.ws_col = vc_cons[currcons].d->vc_cols; |
2485 | } | 2534 | } |
2486 | release_console_sem(); | 2535 | release_console_sem(); |
2487 | vcs_make_devfs(tty); | 2536 | vcs_make_sysfs(tty); |
2488 | return ret; | 2537 | return ret; |
2489 | } | 2538 | } |
2490 | } | 2539 | } |
@@ -2497,7 +2546,7 @@ static int con_open(struct tty_struct *tty, struct file *filp) | |||
2497 | * and taking a ref against the tty while we're in the process of forgetting | 2546 | * and taking a ref against the tty while we're in the process of forgetting |
2498 | * about it and cleaning things up. | 2547 | * about it and cleaning things up. |
2499 | * | 2548 | * |
2500 | * This is because vcs_remove_devfs() can sleep and will drop the BKL. | 2549 | * This is because vcs_remove_sysfs() can sleep and will drop the BKL. |
2501 | */ | 2550 | */ |
2502 | static void con_close(struct tty_struct *tty, struct file *filp) | 2551 | static void con_close(struct tty_struct *tty, struct file *filp) |
2503 | { | 2552 | { |
@@ -2510,7 +2559,7 @@ static void con_close(struct tty_struct *tty, struct file *filp) | |||
2510 | vc->vc_tty = NULL; | 2559 | vc->vc_tty = NULL; |
2511 | tty->driver_data = NULL; | 2560 | tty->driver_data = NULL; |
2512 | release_console_sem(); | 2561 | release_console_sem(); |
2513 | vcs_remove_devfs(tty); | 2562 | vcs_remove_sysfs(tty); |
2514 | mutex_unlock(&tty_mutex); | 2563 | mutex_unlock(&tty_mutex); |
2515 | /* | 2564 | /* |
2516 | * tty_mutex is released, but we still hold BKL, so there is | 2565 | * tty_mutex is released, but we still hold BKL, so there is |
@@ -2557,7 +2606,7 @@ static int __init con_init(void) | |||
2557 | { | 2606 | { |
2558 | const char *display_desc = NULL; | 2607 | const char *display_desc = NULL; |
2559 | struct vc_data *vc; | 2608 | struct vc_data *vc; |
2560 | unsigned int currcons = 0; | 2609 | unsigned int currcons = 0, i; |
2561 | 2610 | ||
2562 | acquire_console_sem(); | 2611 | acquire_console_sem(); |
2563 | 2612 | ||
@@ -2569,6 +2618,22 @@ static int __init con_init(void) | |||
2569 | return 0; | 2618 | return 0; |
2570 | } | 2619 | } |
2571 | 2620 | ||
2621 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { | ||
2622 | struct con_driver *con_driver = ®istered_con_driver[i]; | ||
2623 | |||
2624 | if (con_driver->con == NULL) { | ||
2625 | con_driver->con = conswitchp; | ||
2626 | con_driver->desc = display_desc; | ||
2627 | con_driver->flag = CON_DRIVER_FLAG_INIT; | ||
2628 | con_driver->first = 0; | ||
2629 | con_driver->last = MAX_NR_CONSOLES - 1; | ||
2630 | break; | ||
2631 | } | ||
2632 | } | ||
2633 | |||
2634 | for (i = 0; i < MAX_NR_CONSOLES; i++) | ||
2635 | con_driver_map[i] = conswitchp; | ||
2636 | |||
2572 | init_timer(&console_timer); | 2637 | init_timer(&console_timer); |
2573 | console_timer.function = blank_screen_t; | 2638 | console_timer.function = blank_screen_t; |
2574 | if (blankinterval) { | 2639 | if (blankinterval) { |
@@ -2632,7 +2697,6 @@ int __init vty_init(void) | |||
2632 | if (!console_driver) | 2697 | if (!console_driver) |
2633 | panic("Couldn't allocate console driver\n"); | 2698 | panic("Couldn't allocate console driver\n"); |
2634 | console_driver->owner = THIS_MODULE; | 2699 | console_driver->owner = THIS_MODULE; |
2635 | console_driver->devfs_name = "vc/"; | ||
2636 | console_driver->name = "tty"; | 2700 | console_driver->name = "tty"; |
2637 | console_driver->name_base = 1; | 2701 | console_driver->name_base = 1; |
2638 | console_driver->major = TTY_MAJOR; | 2702 | console_driver->major = TTY_MAJOR; |
@@ -2656,38 +2720,53 @@ int __init vty_init(void) | |||
2656 | } | 2720 | } |
2657 | 2721 | ||
2658 | #ifndef VT_SINGLE_DRIVER | 2722 | #ifndef VT_SINGLE_DRIVER |
2723 | #include <linux/device.h> | ||
2659 | 2724 | ||
2660 | /* | 2725 | static struct class *vtconsole_class; |
2661 | * If we support more console drivers, this function is used | ||
2662 | * when a driver wants to take over some existing consoles | ||
2663 | * and become default driver for newly opened ones. | ||
2664 | */ | ||
2665 | 2726 | ||
2666 | int take_over_console(const struct consw *csw, int first, int last, int deflt) | 2727 | static int bind_con_driver(const struct consw *csw, int first, int last, |
2728 | int deflt) | ||
2667 | { | 2729 | { |
2668 | int i, j = -1; | 2730 | struct module *owner = csw->owner; |
2669 | const char *desc; | 2731 | const char *desc = NULL; |
2670 | struct module *owner; | 2732 | struct con_driver *con_driver; |
2733 | int i, j = -1, k = -1, retval = -ENODEV; | ||
2671 | 2734 | ||
2672 | owner = csw->owner; | ||
2673 | if (!try_module_get(owner)) | 2735 | if (!try_module_get(owner)) |
2674 | return -ENODEV; | 2736 | return -ENODEV; |
2675 | 2737 | ||
2676 | acquire_console_sem(); | 2738 | acquire_console_sem(); |
2677 | 2739 | ||
2678 | desc = csw->con_startup(); | 2740 | /* check if driver is registered */ |
2679 | if (!desc) { | 2741 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { |
2680 | release_console_sem(); | 2742 | con_driver = ®istered_con_driver[i]; |
2681 | module_put(owner); | 2743 | |
2682 | return -ENODEV; | 2744 | if (con_driver->con == csw) { |
2745 | desc = con_driver->desc; | ||
2746 | retval = 0; | ||
2747 | break; | ||
2748 | } | ||
2749 | } | ||
2750 | |||
2751 | if (retval) | ||
2752 | goto err; | ||
2753 | |||
2754 | if (!(con_driver->flag & CON_DRIVER_FLAG_INIT)) { | ||
2755 | csw->con_startup(); | ||
2756 | con_driver->flag |= CON_DRIVER_FLAG_INIT; | ||
2683 | } | 2757 | } |
2758 | |||
2684 | if (deflt) { | 2759 | if (deflt) { |
2685 | if (conswitchp) | 2760 | if (conswitchp) |
2686 | module_put(conswitchp->owner); | 2761 | module_put(conswitchp->owner); |
2762 | |||
2687 | __module_get(owner); | 2763 | __module_get(owner); |
2688 | conswitchp = csw; | 2764 | conswitchp = csw; |
2689 | } | 2765 | } |
2690 | 2766 | ||
2767 | first = max(first, con_driver->first); | ||
2768 | last = min(last, con_driver->last); | ||
2769 | |||
2691 | for (i = first; i <= last; i++) { | 2770 | for (i = first; i <= last; i++) { |
2692 | int old_was_color; | 2771 | int old_was_color; |
2693 | struct vc_data *vc = vc_cons[i].d; | 2772 | struct vc_data *vc = vc_cons[i].d; |
@@ -2701,15 +2780,17 @@ int take_over_console(const struct consw *csw, int first, int last, int deflt) | |||
2701 | continue; | 2780 | continue; |
2702 | 2781 | ||
2703 | j = i; | 2782 | j = i; |
2704 | if (CON_IS_VISIBLE(vc)) | 2783 | |
2784 | if (CON_IS_VISIBLE(vc)) { | ||
2785 | k = i; | ||
2705 | save_screen(vc); | 2786 | save_screen(vc); |
2787 | } | ||
2788 | |||
2706 | old_was_color = vc->vc_can_do_color; | 2789 | old_was_color = vc->vc_can_do_color; |
2707 | vc->vc_sw->con_deinit(vc); | 2790 | vc->vc_sw->con_deinit(vc); |
2708 | vc->vc_origin = (unsigned long)vc->vc_screenbuf; | 2791 | vc->vc_origin = (unsigned long)vc->vc_screenbuf; |
2709 | vc->vc_visible_origin = vc->vc_origin; | ||
2710 | vc->vc_scr_end = vc->vc_origin + vc->vc_screenbuf_size; | ||
2711 | vc->vc_pos = vc->vc_origin + vc->vc_size_row * vc->vc_y + 2 * vc->vc_x; | ||
2712 | visual_init(vc, i, 0); | 2792 | visual_init(vc, i, 0); |
2793 | set_origin(vc); | ||
2713 | update_attr(vc); | 2794 | update_attr(vc); |
2714 | 2795 | ||
2715 | /* If the console changed between mono <-> color, then | 2796 | /* If the console changed between mono <-> color, then |
@@ -2718,36 +2799,506 @@ int take_over_console(const struct consw *csw, int first, int last, int deflt) | |||
2718 | */ | 2799 | */ |
2719 | if (old_was_color != vc->vc_can_do_color) | 2800 | if (old_was_color != vc->vc_can_do_color) |
2720 | clear_buffer_attributes(vc); | 2801 | clear_buffer_attributes(vc); |
2721 | |||
2722 | if (CON_IS_VISIBLE(vc)) | ||
2723 | update_screen(vc); | ||
2724 | } | 2802 | } |
2803 | |||
2725 | printk("Console: switching "); | 2804 | printk("Console: switching "); |
2726 | if (!deflt) | 2805 | if (!deflt) |
2727 | printk("consoles %d-%d ", first+1, last+1); | 2806 | printk("consoles %d-%d ", first+1, last+1); |
2728 | if (j >= 0) | 2807 | if (j >= 0) { |
2808 | struct vc_data *vc = vc_cons[j].d; | ||
2809 | |||
2729 | printk("to %s %s %dx%d\n", | 2810 | printk("to %s %s %dx%d\n", |
2730 | vc_cons[j].d->vc_can_do_color ? "colour" : "mono", | 2811 | vc->vc_can_do_color ? "colour" : "mono", |
2731 | desc, vc_cons[j].d->vc_cols, vc_cons[j].d->vc_rows); | 2812 | desc, vc->vc_cols, vc->vc_rows); |
2732 | else | 2813 | |
2814 | if (k >= 0) { | ||
2815 | vc = vc_cons[k].d; | ||
2816 | update_screen(vc); | ||
2817 | } | ||
2818 | } else | ||
2733 | printk("to %s\n", desc); | 2819 | printk("to %s\n", desc); |
2734 | 2820 | ||
2821 | retval = 0; | ||
2822 | err: | ||
2735 | release_console_sem(); | 2823 | release_console_sem(); |
2824 | module_put(owner); | ||
2825 | return retval; | ||
2826 | }; | ||
2827 | |||
2828 | #ifdef CONFIG_VT_HW_CONSOLE_BINDING | ||
2829 | static int con_is_graphics(const struct consw *csw, int first, int last) | ||
2830 | { | ||
2831 | int i, retval = 0; | ||
2832 | |||
2833 | for (i = first; i <= last; i++) { | ||
2834 | struct vc_data *vc = vc_cons[i].d; | ||
2835 | |||
2836 | if (vc && vc->vc_mode == KD_GRAPHICS) { | ||
2837 | retval = 1; | ||
2838 | break; | ||
2839 | } | ||
2840 | } | ||
2736 | 2841 | ||
2842 | return retval; | ||
2843 | } | ||
2844 | |||
2845 | static int unbind_con_driver(const struct consw *csw, int first, int last, | ||
2846 | int deflt) | ||
2847 | { | ||
2848 | struct module *owner = csw->owner; | ||
2849 | const struct consw *defcsw = NULL; | ||
2850 | struct con_driver *con_driver = NULL, *con_back = NULL; | ||
2851 | int i, retval = -ENODEV; | ||
2852 | |||
2853 | if (!try_module_get(owner)) | ||
2854 | return -ENODEV; | ||
2855 | |||
2856 | acquire_console_sem(); | ||
2857 | |||
2858 | /* check if driver is registered and if it is unbindable */ | ||
2859 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { | ||
2860 | con_driver = ®istered_con_driver[i]; | ||
2861 | |||
2862 | if (con_driver->con == csw && | ||
2863 | con_driver->flag & CON_DRIVER_FLAG_MODULE) { | ||
2864 | retval = 0; | ||
2865 | break; | ||
2866 | } | ||
2867 | } | ||
2868 | |||
2869 | if (retval) { | ||
2870 | release_console_sem(); | ||
2871 | goto err; | ||
2872 | } | ||
2873 | |||
2874 | retval = -ENODEV; | ||
2875 | |||
2876 | /* check if backup driver exists */ | ||
2877 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { | ||
2878 | con_back = ®istered_con_driver[i]; | ||
2879 | |||
2880 | if (con_back->con && | ||
2881 | !(con_back->flag & CON_DRIVER_FLAG_MODULE)) { | ||
2882 | defcsw = con_back->con; | ||
2883 | retval = 0; | ||
2884 | break; | ||
2885 | } | ||
2886 | } | ||
2887 | |||
2888 | if (retval) { | ||
2889 | release_console_sem(); | ||
2890 | goto err; | ||
2891 | } | ||
2892 | |||
2893 | if (!con_is_bound(csw)) { | ||
2894 | release_console_sem(); | ||
2895 | goto err; | ||
2896 | } | ||
2897 | |||
2898 | first = max(first, con_driver->first); | ||
2899 | last = min(last, con_driver->last); | ||
2900 | |||
2901 | for (i = first; i <= last; i++) { | ||
2902 | if (con_driver_map[i] == csw) { | ||
2903 | module_put(csw->owner); | ||
2904 | con_driver_map[i] = NULL; | ||
2905 | } | ||
2906 | } | ||
2907 | |||
2908 | if (!con_is_bound(defcsw)) { | ||
2909 | const struct consw *defconsw = conswitchp; | ||
2910 | |||
2911 | defcsw->con_startup(); | ||
2912 | con_back->flag |= CON_DRIVER_FLAG_INIT; | ||
2913 | /* | ||
2914 | * vgacon may change the default driver to point | ||
2915 | * to dummycon, we restore it here... | ||
2916 | */ | ||
2917 | conswitchp = defconsw; | ||
2918 | } | ||
2919 | |||
2920 | if (!con_is_bound(csw)) | ||
2921 | con_driver->flag &= ~CON_DRIVER_FLAG_INIT; | ||
2922 | |||
2923 | release_console_sem(); | ||
2924 | /* ignore return value, binding should not fail */ | ||
2925 | bind_con_driver(defcsw, first, last, deflt); | ||
2926 | err: | ||
2737 | module_put(owner); | 2927 | module_put(owner); |
2928 | return retval; | ||
2929 | |||
2930 | } | ||
2931 | |||
2932 | static int vt_bind(struct con_driver *con) | ||
2933 | { | ||
2934 | const struct consw *defcsw = NULL, *csw = NULL; | ||
2935 | int i, more = 1, first = -1, last = -1, deflt = 0; | ||
2936 | |||
2937 | if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE) || | ||
2938 | con_is_graphics(con->con, con->first, con->last)) | ||
2939 | goto err; | ||
2940 | |||
2941 | csw = con->con; | ||
2942 | |||
2943 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { | ||
2944 | struct con_driver *con = ®istered_con_driver[i]; | ||
2945 | |||
2946 | if (con->con && !(con->flag & CON_DRIVER_FLAG_MODULE)) { | ||
2947 | defcsw = con->con; | ||
2948 | break; | ||
2949 | } | ||
2950 | } | ||
2951 | |||
2952 | if (!defcsw) | ||
2953 | goto err; | ||
2954 | |||
2955 | while (more) { | ||
2956 | more = 0; | ||
2957 | |||
2958 | for (i = con->first; i <= con->last; i++) { | ||
2959 | if (con_driver_map[i] == defcsw) { | ||
2960 | if (first == -1) | ||
2961 | first = i; | ||
2962 | last = i; | ||
2963 | more = 1; | ||
2964 | } else if (first != -1) | ||
2965 | break; | ||
2966 | } | ||
2967 | |||
2968 | if (first == 0 && last == MAX_NR_CONSOLES -1) | ||
2969 | deflt = 1; | ||
2970 | |||
2971 | if (first != -1) | ||
2972 | bind_con_driver(csw, first, last, deflt); | ||
2973 | |||
2974 | first = -1; | ||
2975 | last = -1; | ||
2976 | deflt = 0; | ||
2977 | } | ||
2978 | |||
2979 | err: | ||
2738 | return 0; | 2980 | return 0; |
2739 | } | 2981 | } |
2740 | 2982 | ||
2741 | void give_up_console(const struct consw *csw) | 2983 | static int vt_unbind(struct con_driver *con) |
2984 | { | ||
2985 | const struct consw *csw = NULL; | ||
2986 | int i, more = 1, first = -1, last = -1, deflt = 0; | ||
2987 | |||
2988 | if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE) || | ||
2989 | con_is_graphics(con->con, con->first, con->last)) | ||
2990 | goto err; | ||
2991 | |||
2992 | csw = con->con; | ||
2993 | |||
2994 | while (more) { | ||
2995 | more = 0; | ||
2996 | |||
2997 | for (i = con->first; i <= con->last; i++) { | ||
2998 | if (con_driver_map[i] == csw) { | ||
2999 | if (first == -1) | ||
3000 | first = i; | ||
3001 | last = i; | ||
3002 | more = 1; | ||
3003 | } else if (first != -1) | ||
3004 | break; | ||
3005 | } | ||
3006 | |||
3007 | if (first == 0 && last == MAX_NR_CONSOLES -1) | ||
3008 | deflt = 1; | ||
3009 | |||
3010 | if (first != -1) | ||
3011 | unbind_con_driver(csw, first, last, deflt); | ||
3012 | |||
3013 | first = -1; | ||
3014 | last = -1; | ||
3015 | deflt = 0; | ||
3016 | } | ||
3017 | |||
3018 | err: | ||
3019 | return 0; | ||
3020 | } | ||
3021 | #else | ||
3022 | static inline int vt_bind(struct con_driver *con) | ||
3023 | { | ||
3024 | return 0; | ||
3025 | } | ||
3026 | static inline int vt_unbind(struct con_driver *con) | ||
3027 | { | ||
3028 | return 0; | ||
3029 | } | ||
3030 | #endif /* CONFIG_VT_HW_CONSOLE_BINDING */ | ||
3031 | |||
3032 | static ssize_t store_bind(struct class_device *class_device, | ||
3033 | const char *buf, size_t count) | ||
3034 | { | ||
3035 | struct con_driver *con = class_get_devdata(class_device); | ||
3036 | int bind = simple_strtoul(buf, NULL, 0); | ||
3037 | |||
3038 | if (bind) | ||
3039 | vt_bind(con); | ||
3040 | else | ||
3041 | vt_unbind(con); | ||
3042 | |||
3043 | return count; | ||
3044 | } | ||
3045 | |||
3046 | static ssize_t show_bind(struct class_device *class_device, char *buf) | ||
3047 | { | ||
3048 | struct con_driver *con = class_get_devdata(class_device); | ||
3049 | int bind = con_is_bound(con->con); | ||
3050 | |||
3051 | return snprintf(buf, PAGE_SIZE, "%i\n", bind); | ||
3052 | } | ||
3053 | |||
3054 | static ssize_t show_name(struct class_device *class_device, char *buf) | ||
3055 | { | ||
3056 | struct con_driver *con = class_get_devdata(class_device); | ||
3057 | |||
3058 | return snprintf(buf, PAGE_SIZE, "%s %s\n", | ||
3059 | (con->flag & CON_DRIVER_FLAG_MODULE) ? "(M)" : "(S)", | ||
3060 | con->desc); | ||
3061 | |||
3062 | } | ||
3063 | |||
3064 | static struct class_device_attribute class_device_attrs[] = { | ||
3065 | __ATTR(bind, S_IRUGO|S_IWUSR, show_bind, store_bind), | ||
3066 | __ATTR(name, S_IRUGO, show_name, NULL), | ||
3067 | }; | ||
3068 | |||
3069 | static int vtconsole_init_class_device(struct con_driver *con) | ||
2742 | { | 3070 | { |
2743 | int i; | 3071 | int i; |
2744 | 3072 | ||
2745 | for(i = 0; i < MAX_NR_CONSOLES; i++) | 3073 | class_set_devdata(con->class_dev, con); |
3074 | for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++) | ||
3075 | class_device_create_file(con->class_dev, | ||
3076 | &class_device_attrs[i]); | ||
3077 | |||
3078 | return 0; | ||
3079 | } | ||
3080 | |||
3081 | static void vtconsole_deinit_class_device(struct con_driver *con) | ||
3082 | { | ||
3083 | int i; | ||
3084 | |||
3085 | for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++) | ||
3086 | class_device_remove_file(con->class_dev, | ||
3087 | &class_device_attrs[i]); | ||
3088 | } | ||
3089 | |||
3090 | /** | ||
3091 | * con_is_bound - checks if driver is bound to the console | ||
3092 | * @csw: console driver | ||
3093 | * | ||
3094 | * RETURNS: zero if unbound, nonzero if bound | ||
3095 | * | ||
3096 | * Drivers can call this and if zero, they should release | ||
3097 | * all resources allocated on con_startup() | ||
3098 | */ | ||
3099 | int con_is_bound(const struct consw *csw) | ||
3100 | { | ||
3101 | int i, bound = 0; | ||
3102 | |||
3103 | for (i = 0; i < MAX_NR_CONSOLES; i++) { | ||
2746 | if (con_driver_map[i] == csw) { | 3104 | if (con_driver_map[i] == csw) { |
2747 | module_put(csw->owner); | 3105 | bound = 1; |
2748 | con_driver_map[i] = NULL; | 3106 | break; |
3107 | } | ||
3108 | } | ||
3109 | |||
3110 | return bound; | ||
3111 | } | ||
3112 | EXPORT_SYMBOL(con_is_bound); | ||
3113 | |||
3114 | /** | ||
3115 | * register_con_driver - register console driver to console layer | ||
3116 | * @csw: console driver | ||
3117 | * @first: the first console to take over, minimum value is 0 | ||
3118 | * @last: the last console to take over, maximum value is MAX_NR_CONSOLES -1 | ||
3119 | * | ||
3120 | * DESCRIPTION: This function registers a console driver which can later | ||
3121 | * bind to a range of consoles specified by @first and @last. It will | ||
3122 | * also initialize the console driver by calling con_startup(). | ||
3123 | */ | ||
3124 | int register_con_driver(const struct consw *csw, int first, int last) | ||
3125 | { | ||
3126 | struct module *owner = csw->owner; | ||
3127 | struct con_driver *con_driver; | ||
3128 | const char *desc; | ||
3129 | int i, retval = 0; | ||
3130 | |||
3131 | if (!try_module_get(owner)) | ||
3132 | return -ENODEV; | ||
3133 | |||
3134 | acquire_console_sem(); | ||
3135 | |||
3136 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { | ||
3137 | con_driver = ®istered_con_driver[i]; | ||
3138 | |||
3139 | /* already registered */ | ||
3140 | if (con_driver->con == csw) | ||
3141 | retval = -EINVAL; | ||
3142 | } | ||
3143 | |||
3144 | if (retval) | ||
3145 | goto err; | ||
3146 | |||
3147 | desc = csw->con_startup(); | ||
3148 | |||
3149 | if (!desc) | ||
3150 | goto err; | ||
3151 | |||
3152 | retval = -EINVAL; | ||
3153 | |||
3154 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { | ||
3155 | con_driver = ®istered_con_driver[i]; | ||
3156 | |||
3157 | if (con_driver->con == NULL) { | ||
3158 | con_driver->con = csw; | ||
3159 | con_driver->desc = desc; | ||
3160 | con_driver->node = i; | ||
3161 | con_driver->flag = CON_DRIVER_FLAG_MODULE | | ||
3162 | CON_DRIVER_FLAG_INIT; | ||
3163 | con_driver->first = first; | ||
3164 | con_driver->last = last; | ||
3165 | retval = 0; | ||
3166 | break; | ||
2749 | } | 3167 | } |
3168 | } | ||
3169 | |||
3170 | if (retval) | ||
3171 | goto err; | ||
3172 | |||
3173 | con_driver->class_dev = class_device_create(vtconsole_class, NULL, | ||
3174 | MKDEV(0, con_driver->node), | ||
3175 | NULL, "vtcon%i", | ||
3176 | con_driver->node); | ||
3177 | |||
3178 | if (IS_ERR(con_driver->class_dev)) { | ||
3179 | printk(KERN_WARNING "Unable to create class_device for %s; " | ||
3180 | "errno = %ld\n", con_driver->desc, | ||
3181 | PTR_ERR(con_driver->class_dev)); | ||
3182 | con_driver->class_dev = NULL; | ||
3183 | } else { | ||
3184 | vtconsole_init_class_device(con_driver); | ||
3185 | } | ||
3186 | err: | ||
3187 | release_console_sem(); | ||
3188 | module_put(owner); | ||
3189 | return retval; | ||
3190 | } | ||
3191 | EXPORT_SYMBOL(register_con_driver); | ||
3192 | |||
3193 | /** | ||
3194 | * unregister_con_driver - unregister console driver from console layer | ||
3195 | * @csw: console driver | ||
3196 | * | ||
3197 | * DESCRIPTION: All drivers that registers to the console layer must | ||
3198 | * call this function upon exit, or if the console driver is in a state | ||
3199 | * where it won't be able to handle console services, such as the | ||
3200 | * framebuffer console without loaded framebuffer drivers. | ||
3201 | * | ||
3202 | * The driver must unbind first prior to unregistration. | ||
3203 | */ | ||
3204 | int unregister_con_driver(const struct consw *csw) | ||
3205 | { | ||
3206 | int i, retval = -ENODEV; | ||
3207 | |||
3208 | acquire_console_sem(); | ||
3209 | |||
3210 | /* cannot unregister a bound driver */ | ||
3211 | if (con_is_bound(csw)) | ||
3212 | goto err; | ||
3213 | |||
3214 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { | ||
3215 | struct con_driver *con_driver = ®istered_con_driver[i]; | ||
3216 | |||
3217 | if (con_driver->con == csw && | ||
3218 | con_driver->flag & CON_DRIVER_FLAG_MODULE) { | ||
3219 | vtconsole_deinit_class_device(con_driver); | ||
3220 | class_device_destroy(vtconsole_class, | ||
3221 | MKDEV(0, con_driver->node)); | ||
3222 | con_driver->con = NULL; | ||
3223 | con_driver->desc = NULL; | ||
3224 | con_driver->class_dev = NULL; | ||
3225 | con_driver->node = 0; | ||
3226 | con_driver->flag = 0; | ||
3227 | con_driver->first = 0; | ||
3228 | con_driver->last = 0; | ||
3229 | retval = 0; | ||
3230 | break; | ||
3231 | } | ||
3232 | } | ||
3233 | err: | ||
3234 | release_console_sem(); | ||
3235 | return retval; | ||
3236 | } | ||
3237 | EXPORT_SYMBOL(unregister_con_driver); | ||
3238 | |||
3239 | /* | ||
3240 | * If we support more console drivers, this function is used | ||
3241 | * when a driver wants to take over some existing consoles | ||
3242 | * and become default driver for newly opened ones. | ||
3243 | * | ||
3244 | * take_over_console is basically a register followed by unbind | ||
3245 | */ | ||
3246 | int take_over_console(const struct consw *csw, int first, int last, int deflt) | ||
3247 | { | ||
3248 | int err; | ||
3249 | |||
3250 | err = register_con_driver(csw, first, last); | ||
3251 | |||
3252 | if (!err) | ||
3253 | bind_con_driver(csw, first, last, deflt); | ||
3254 | |||
3255 | return err; | ||
3256 | } | ||
3257 | |||
3258 | /* | ||
3259 | * give_up_console is a wrapper to unregister_con_driver. It will only | ||
3260 | * work if driver is fully unbound. | ||
3261 | */ | ||
3262 | void give_up_console(const struct consw *csw) | ||
3263 | { | ||
3264 | unregister_con_driver(csw); | ||
3265 | } | ||
3266 | |||
3267 | static int __init vtconsole_class_init(void) | ||
3268 | { | ||
3269 | int i; | ||
3270 | |||
3271 | vtconsole_class = class_create(THIS_MODULE, "vtconsole"); | ||
3272 | if (IS_ERR(vtconsole_class)) { | ||
3273 | printk(KERN_WARNING "Unable to create vt console class; " | ||
3274 | "errno = %ld\n", PTR_ERR(vtconsole_class)); | ||
3275 | vtconsole_class = NULL; | ||
3276 | } | ||
3277 | |||
3278 | /* Add system drivers to sysfs */ | ||
3279 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { | ||
3280 | struct con_driver *con = ®istered_con_driver[i]; | ||
3281 | |||
3282 | if (con->con && !con->class_dev) { | ||
3283 | con->class_dev = | ||
3284 | class_device_create(vtconsole_class, NULL, | ||
3285 | MKDEV(0, con->node), NULL, | ||
3286 | "vtcon%i", con->node); | ||
3287 | |||
3288 | if (IS_ERR(con->class_dev)) { | ||
3289 | printk(KERN_WARNING "Unable to create " | ||
3290 | "class_device for %s; errno = %ld\n", | ||
3291 | con->desc, PTR_ERR(con->class_dev)); | ||
3292 | con->class_dev = NULL; | ||
3293 | } else { | ||
3294 | vtconsole_init_class_device(con); | ||
3295 | } | ||
3296 | } | ||
3297 | } | ||
3298 | |||
3299 | return 0; | ||
2750 | } | 3300 | } |
3301 | postcore_initcall(vtconsole_class_init); | ||
2751 | 3302 | ||
2752 | #endif | 3303 | #endif |
2753 | 3304 | ||
@@ -3249,6 +3800,7 @@ EXPORT_SYMBOL(default_blu); | |||
3249 | EXPORT_SYMBOL(update_region); | 3800 | EXPORT_SYMBOL(update_region); |
3250 | EXPORT_SYMBOL(redraw_screen); | 3801 | EXPORT_SYMBOL(redraw_screen); |
3251 | EXPORT_SYMBOL(vc_resize); | 3802 | EXPORT_SYMBOL(vc_resize); |
3803 | EXPORT_SYMBOL(vc_lock_resize); | ||
3252 | EXPORT_SYMBOL(fg_console); | 3804 | EXPORT_SYMBOL(fg_console); |
3253 | EXPORT_SYMBOL(console_blank_hook); | 3805 | EXPORT_SYMBOL(console_blank_hook); |
3254 | EXPORT_SYMBOL(console_blanked); | 3806 | EXPORT_SYMBOL(console_blanked); |