diff options
-rw-r--r-- | drivers/char/vt.c | 467 | ||||
-rw-r--r-- | include/linux/console.h | 4 |
2 files changed, 437 insertions, 34 deletions
diff --git a/drivers/char/vt.c b/drivers/char/vt.c index d7ff7fdb6fe3..d788a0eaf26a 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c | |||
@@ -98,9 +98,21 @@ | |||
98 | #include <asm/system.h> | 98 | #include <asm/system.h> |
99 | #include <asm/uaccess.h> | 99 | #include <asm/uaccess.h> |
100 | 100 | ||
101 | #define MAX_NR_CON_DRIVER 16 | ||
101 | 102 | ||
103 | #define CON_DRIVER_FLAG_BIND 1 | ||
104 | #define CON_DRIVER_FLAG_INIT 2 | ||
105 | |||
106 | struct con_driver { | ||
107 | const struct consw *con; | ||
108 | const char *desc; | ||
109 | int first; | ||
110 | int last; | ||
111 | int flag; | ||
112 | }; | ||
113 | |||
114 | static struct con_driver registered_con_driver[MAX_NR_CON_DRIVER]; | ||
102 | const struct consw *conswitchp; | 115 | const struct consw *conswitchp; |
103 | static struct consw *defcsw; /* default console */ | ||
104 | 116 | ||
105 | /* A bitmap for codes <32. A bit of 1 indicates that the code | 117 | /* A bitmap for codes <32. A bit of 1 indicates that the code |
106 | * corresponding to that bit number invokes some special action | 118 | * corresponding to that bit number invokes some special action |
@@ -2558,7 +2570,7 @@ static int __init con_init(void) | |||
2558 | { | 2570 | { |
2559 | const char *display_desc = NULL; | 2571 | const char *display_desc = NULL; |
2560 | struct vc_data *vc; | 2572 | struct vc_data *vc; |
2561 | unsigned int currcons = 0; | 2573 | unsigned int currcons = 0, i; |
2562 | 2574 | ||
2563 | acquire_console_sem(); | 2575 | acquire_console_sem(); |
2564 | 2576 | ||
@@ -2570,6 +2582,22 @@ static int __init con_init(void) | |||
2570 | return 0; | 2582 | return 0; |
2571 | } | 2583 | } |
2572 | 2584 | ||
2585 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { | ||
2586 | struct con_driver *con_driver = ®istered_con_driver[i]; | ||
2587 | |||
2588 | if (con_driver->con == NULL) { | ||
2589 | con_driver->con = conswitchp; | ||
2590 | con_driver->desc = display_desc; | ||
2591 | con_driver->flag = CON_DRIVER_FLAG_INIT; | ||
2592 | con_driver->first = 0; | ||
2593 | con_driver->last = MAX_NR_CONSOLES - 1; | ||
2594 | break; | ||
2595 | } | ||
2596 | } | ||
2597 | |||
2598 | for (i = 0; i < MAX_NR_CONSOLES; i++) | ||
2599 | con_driver_map[i] = conswitchp; | ||
2600 | |||
2573 | init_timer(&console_timer); | 2601 | init_timer(&console_timer); |
2574 | console_timer.function = blank_screen_t; | 2602 | console_timer.function = blank_screen_t; |
2575 | if (blankinterval) { | 2603 | if (blankinterval) { |
@@ -2658,33 +2686,36 @@ int __init vty_init(void) | |||
2658 | 2686 | ||
2659 | #ifndef VT_SINGLE_DRIVER | 2687 | #ifndef VT_SINGLE_DRIVER |
2660 | 2688 | ||
2661 | /* | 2689 | static int bind_con_driver(const struct consw *csw, int first, int last, |
2662 | * If we support more console drivers, this function is used | 2690 | int deflt) |
2663 | * when a driver wants to take over some existing consoles | ||
2664 | * and become default driver for newly opened ones. | ||
2665 | */ | ||
2666 | |||
2667 | int take_over_console(const struct consw *csw, int first, int last, int deflt) | ||
2668 | { | 2691 | { |
2669 | int i, j = -1, k = -1; | 2692 | struct module *owner = csw->owner; |
2670 | const char *desc; | 2693 | const char *desc = NULL; |
2671 | struct module *owner; | 2694 | struct con_driver *con_driver; |
2695 | int i, j = -1, k = -1, retval = -ENODEV; | ||
2672 | 2696 | ||
2673 | owner = csw->owner; | ||
2674 | if (!try_module_get(owner)) | 2697 | if (!try_module_get(owner)) |
2675 | return -ENODEV; | 2698 | return -ENODEV; |
2676 | 2699 | ||
2677 | /* save default console, for possible recovery later on */ | ||
2678 | if (!defcsw) | ||
2679 | defcsw = (struct consw *) conswitchp; | ||
2680 | |||
2681 | acquire_console_sem(); | 2700 | acquire_console_sem(); |
2682 | desc = csw->con_startup(); | ||
2683 | 2701 | ||
2684 | if (!desc) { | 2702 | /* check if driver is registered */ |
2685 | release_console_sem(); | 2703 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { |
2686 | module_put(owner); | 2704 | con_driver = ®istered_con_driver[i]; |
2687 | return -ENODEV; | 2705 | |
2706 | if (con_driver->con == csw) { | ||
2707 | desc = con_driver->desc; | ||
2708 | retval = 0; | ||
2709 | break; | ||
2710 | } | ||
2711 | } | ||
2712 | |||
2713 | if (retval) | ||
2714 | goto err; | ||
2715 | |||
2716 | if (!(con_driver->flag & CON_DRIVER_FLAG_INIT)) { | ||
2717 | csw->con_startup(); | ||
2718 | con_driver->flag |= CON_DRIVER_FLAG_INIT; | ||
2688 | } | 2719 | } |
2689 | 2720 | ||
2690 | if (deflt) { | 2721 | if (deflt) { |
@@ -2695,6 +2726,9 @@ int take_over_console(const struct consw *csw, int first, int last, int deflt) | |||
2695 | conswitchp = csw; | 2726 | conswitchp = csw; |
2696 | } | 2727 | } |
2697 | 2728 | ||
2729 | first = max(first, con_driver->first); | ||
2730 | last = min(last, con_driver->last); | ||
2731 | |||
2698 | for (i = first; i <= last; i++) { | 2732 | for (i = first; i <= last; i++) { |
2699 | int old_was_color; | 2733 | int old_was_color; |
2700 | struct vc_data *vc = vc_cons[i].d; | 2734 | struct vc_data *vc = vc_cons[i].d; |
@@ -2746,33 +2780,400 @@ int take_over_console(const struct consw *csw, int first, int last, int deflt) | |||
2746 | } else | 2780 | } else |
2747 | printk("to %s\n", desc); | 2781 | printk("to %s\n", desc); |
2748 | 2782 | ||
2783 | retval = 0; | ||
2784 | err: | ||
2749 | release_console_sem(); | 2785 | release_console_sem(); |
2750 | module_put(owner); | 2786 | module_put(owner); |
2751 | return 0; | 2787 | return retval; |
2752 | } | 2788 | }; |
2753 | 2789 | ||
2754 | void give_up_console(const struct consw *csw) | 2790 | static int unbind_con_driver(const struct consw *csw, int first, int last, |
2791 | int deflt) | ||
2755 | { | 2792 | { |
2756 | int i, first = -1, last = -1, deflt = 0; | 2793 | struct module *owner = csw->owner; |
2794 | const struct consw *defcsw = NULL; | ||
2795 | struct con_driver *con_driver = NULL, *con_back = NULL; | ||
2796 | int i, retval = -ENODEV; | ||
2757 | 2797 | ||
2758 | for (i = 0; i < MAX_NR_CONSOLES; i++) | 2798 | if (!try_module_get(owner)) |
2799 | return -ENODEV; | ||
2800 | |||
2801 | acquire_console_sem(); | ||
2802 | |||
2803 | /* check if driver is registered and if it is unbindable */ | ||
2804 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { | ||
2805 | con_driver = ®istered_con_driver[i]; | ||
2806 | |||
2807 | if (con_driver->con == csw && | ||
2808 | con_driver->flag & CON_DRIVER_FLAG_BIND) { | ||
2809 | retval = 0; | ||
2810 | break; | ||
2811 | } | ||
2812 | } | ||
2813 | |||
2814 | if (retval) { | ||
2815 | release_console_sem(); | ||
2816 | goto err; | ||
2817 | } | ||
2818 | |||
2819 | /* check if backup driver exists */ | ||
2820 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { | ||
2821 | con_back = ®istered_con_driver[i]; | ||
2822 | |||
2823 | if (con_back->con && | ||
2824 | !(con_back->flag & CON_DRIVER_FLAG_BIND)) { | ||
2825 | defcsw = con_back->con; | ||
2826 | retval = 0; | ||
2827 | break; | ||
2828 | } | ||
2829 | } | ||
2830 | |||
2831 | if (retval) { | ||
2832 | release_console_sem(); | ||
2833 | goto err; | ||
2834 | } | ||
2835 | |||
2836 | if (!con_is_bound(csw)) { | ||
2837 | release_console_sem(); | ||
2838 | goto err; | ||
2839 | } | ||
2840 | |||
2841 | first = max(first, con_driver->first); | ||
2842 | last = min(last, con_driver->last); | ||
2843 | |||
2844 | for (i = first; i <= last; i++) { | ||
2759 | if (con_driver_map[i] == csw) { | 2845 | if (con_driver_map[i] == csw) { |
2760 | if (first == -1) | ||
2761 | first = i; | ||
2762 | last = i; | ||
2763 | module_put(csw->owner); | 2846 | module_put(csw->owner); |
2764 | con_driver_map[i] = NULL; | 2847 | con_driver_map[i] = NULL; |
2765 | } | 2848 | } |
2849 | } | ||
2850 | |||
2851 | if (!con_is_bound(defcsw)) { | ||
2852 | defcsw->con_startup(); | ||
2853 | con_back->flag |= CON_DRIVER_FLAG_INIT; | ||
2854 | } | ||
2855 | |||
2856 | if (!con_is_bound(csw)) | ||
2857 | con_driver->flag &= ~CON_DRIVER_FLAG_INIT; | ||
2858 | |||
2859 | release_console_sem(); | ||
2860 | retval = bind_con_driver(defcsw, first, last, deflt); | ||
2861 | err: | ||
2862 | module_put(owner); | ||
2863 | return retval; | ||
2864 | |||
2865 | } | ||
2866 | |||
2867 | /** | ||
2868 | * con_is_bound - checks if driver is bound to the console | ||
2869 | * @csw: console driver | ||
2870 | * | ||
2871 | * RETURNS: zero if unbound, nonzero if bound | ||
2872 | * | ||
2873 | * Drivers can call this and if zero, they should release | ||
2874 | * all resources allocated on con_startup() | ||
2875 | */ | ||
2876 | int con_is_bound(const struct consw *csw) | ||
2877 | { | ||
2878 | int i, bound = 0; | ||
2879 | |||
2880 | for (i = 0; i < MAX_NR_CONSOLES; i++) { | ||
2881 | if (con_driver_map[i] == csw) { | ||
2882 | bound = 1; | ||
2883 | break; | ||
2884 | } | ||
2885 | } | ||
2886 | |||
2887 | return bound; | ||
2888 | } | ||
2889 | EXPORT_SYMBOL(con_is_bound); | ||
2890 | |||
2891 | /** | ||
2892 | * register_con_driver - register console driver to console layer | ||
2893 | * @csw: console driver | ||
2894 | * @first: the first console to take over, minimum value is 0 | ||
2895 | * @last: the last console to take over, maximum value is MAX_NR_CONSOLES -1 | ||
2896 | * | ||
2897 | * DESCRIPTION: This function registers a console driver which can later | ||
2898 | * bind to a range of consoles specified by @first and @last. It will | ||
2899 | * also initialize the console driver by calling con_startup(). | ||
2900 | */ | ||
2901 | int register_con_driver(const struct consw *csw, int first, int last) | ||
2902 | { | ||
2903 | struct module *owner = csw->owner; | ||
2904 | struct con_driver *con_driver; | ||
2905 | const char *desc; | ||
2906 | int i, retval = 0; | ||
2907 | |||
2908 | if (!try_module_get(owner)) | ||
2909 | return -ENODEV; | ||
2910 | |||
2911 | acquire_console_sem(); | ||
2912 | |||
2913 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { | ||
2914 | con_driver = ®istered_con_driver[i]; | ||
2915 | |||
2916 | /* already registered */ | ||
2917 | if (con_driver->con == csw) | ||
2918 | retval = -EINVAL; | ||
2919 | } | ||
2920 | |||
2921 | if (retval) | ||
2922 | goto err; | ||
2923 | |||
2924 | desc = csw->con_startup(); | ||
2925 | |||
2926 | if (!desc) | ||
2927 | goto err; | ||
2928 | |||
2929 | retval = -EINVAL; | ||
2930 | |||
2931 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { | ||
2932 | con_driver = ®istered_con_driver[i]; | ||
2933 | |||
2934 | if (con_driver->con == NULL) { | ||
2935 | con_driver->con = csw; | ||
2936 | con_driver->desc = desc; | ||
2937 | con_driver->flag = CON_DRIVER_FLAG_BIND | | ||
2938 | CON_DRIVER_FLAG_INIT; | ||
2939 | con_driver->first = first; | ||
2940 | con_driver->last = last; | ||
2941 | retval = 0; | ||
2942 | break; | ||
2943 | } | ||
2944 | } | ||
2945 | |||
2946 | err: | ||
2947 | release_console_sem(); | ||
2948 | module_put(owner); | ||
2949 | return retval; | ||
2950 | } | ||
2951 | EXPORT_SYMBOL(register_con_driver); | ||
2952 | |||
2953 | /** | ||
2954 | * unregister_con_driver - unregister console driver from console layer | ||
2955 | * @csw: console driver | ||
2956 | * | ||
2957 | * DESCRIPTION: All drivers that registers to the console layer must | ||
2958 | * call this function upon exit, or if the console driver is in a state | ||
2959 | * where it won't be able to handle console services, such as the | ||
2960 | * framebuffer console without loaded framebuffer drivers. | ||
2961 | * | ||
2962 | * The driver must unbind first prior to unregistration. | ||
2963 | */ | ||
2964 | int unregister_con_driver(const struct consw *csw) | ||
2965 | { | ||
2966 | int i, retval = -ENODEV; | ||
2967 | |||
2968 | acquire_console_sem(); | ||
2969 | |||
2970 | /* cannot unregister a bound driver */ | ||
2971 | if (con_is_bound(csw)) | ||
2972 | goto err; | ||
2973 | |||
2974 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { | ||
2975 | struct con_driver *con_driver = ®istered_con_driver[i]; | ||
2976 | |||
2977 | if (con_driver->con == csw && | ||
2978 | con_driver->flag & CON_DRIVER_FLAG_BIND) { | ||
2979 | con_driver->con = NULL; | ||
2980 | con_driver->desc = NULL; | ||
2981 | con_driver->flag = 0; | ||
2982 | con_driver->first = 0; | ||
2983 | con_driver->last = 0; | ||
2984 | retval = 0; | ||
2985 | break; | ||
2986 | } | ||
2987 | } | ||
2988 | |||
2989 | err: | ||
2990 | release_console_sem(); | ||
2991 | return retval; | ||
2992 | } | ||
2993 | EXPORT_SYMBOL(unregister_con_driver); | ||
2994 | |||
2995 | /* | ||
2996 | * If we support more console drivers, this function is used | ||
2997 | * when a driver wants to take over some existing consoles | ||
2998 | * and become default driver for newly opened ones. | ||
2999 | * | ||
3000 | * take_over_console is basically a register followed by unbind | ||
3001 | */ | ||
3002 | int take_over_console(const struct consw *csw, int first, int last, int deflt) | ||
3003 | { | ||
3004 | int err; | ||
3005 | |||
3006 | err = register_con_driver(csw, first, last); | ||
3007 | |||
3008 | if (!err) | ||
3009 | err = bind_con_driver(csw, first, last, deflt); | ||
3010 | |||
3011 | return err; | ||
3012 | } | ||
3013 | |||
3014 | /* | ||
3015 | * give_up_console is a wrapper to unregister_con_driver. It will only | ||
3016 | * work if driver is fully unbound. | ||
3017 | */ | ||
3018 | void give_up_console(const struct consw *csw) | ||
3019 | { | ||
3020 | unregister_con_driver(csw); | ||
3021 | } | ||
3022 | |||
3023 | /* | ||
3024 | * this function is intended to be called by the tty layer only | ||
3025 | */ | ||
3026 | int vt_bind(int index) | ||
3027 | { | ||
3028 | const struct consw *defcsw = NULL, *csw = NULL; | ||
3029 | struct con_driver *con; | ||
3030 | int i, more = 1, first = -1, last = -1, deflt = 0; | ||
3031 | |||
3032 | if (index >= MAX_NR_CON_DRIVER) | ||
3033 | goto err; | ||
3034 | |||
3035 | con = ®istered_con_driver[index]; | ||
3036 | |||
3037 | if (!con->con || !(con->flag & CON_DRIVER_FLAG_BIND)) | ||
3038 | goto err; | ||
3039 | |||
3040 | csw = con->con; | ||
3041 | |||
3042 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { | ||
3043 | struct con_driver *con = ®istered_con_driver[i]; | ||
3044 | |||
3045 | if (con->con && !(con->flag & CON_DRIVER_FLAG_BIND)) { | ||
3046 | defcsw = con->con; | ||
3047 | break; | ||
3048 | } | ||
3049 | } | ||
2766 | 3050 | ||
2767 | if (first != -1 && defcsw) { | 3051 | if (!defcsw) |
2768 | if (first == 0 && last == MAX_NR_CONSOLES - 1) | 3052 | goto err; |
3053 | |||
3054 | while (more) { | ||
3055 | more = 0; | ||
3056 | |||
3057 | for (i = con->first; i <= con->last; i++) { | ||
3058 | if (con_driver_map[i] == defcsw) { | ||
3059 | if (first == -1) | ||
3060 | first = i; | ||
3061 | last = i; | ||
3062 | more = 1; | ||
3063 | } else if (first != -1) | ||
3064 | break; | ||
3065 | } | ||
3066 | |||
3067 | if (first == 0 && last == MAX_NR_CONSOLES -1) | ||
2769 | deflt = 1; | 3068 | deflt = 1; |
2770 | take_over_console(defcsw, first, last, deflt); | 3069 | |
3070 | if (first != -1) | ||
3071 | bind_con_driver(csw, first, last, deflt); | ||
3072 | |||
3073 | first = -1; | ||
3074 | last = -1; | ||
3075 | deflt = 0; | ||
2771 | } | 3076 | } |
3077 | |||
3078 | err: | ||
3079 | return 0; | ||
3080 | } | ||
3081 | |||
3082 | /* | ||
3083 | * this function is intended to be called by the tty layer only | ||
3084 | */ | ||
3085 | int vt_unbind(int index) | ||
3086 | { | ||
3087 | const struct consw *csw = NULL; | ||
3088 | struct con_driver *con; | ||
3089 | int i, more = 1, first = -1, last = -1, deflt = 0; | ||
3090 | |||
3091 | if (index >= MAX_NR_CON_DRIVER) | ||
3092 | goto err; | ||
3093 | |||
3094 | con = ®istered_con_driver[index]; | ||
3095 | |||
3096 | if (!con->con || !(con->flag & CON_DRIVER_FLAG_BIND)) | ||
3097 | goto err; | ||
3098 | |||
3099 | csw = con->con; | ||
3100 | |||
3101 | while (more) { | ||
3102 | more = 0; | ||
3103 | |||
3104 | for (i = con->first; i <= con->last; i++) { | ||
3105 | if (con_driver_map[i] == csw) { | ||
3106 | if (first == -1) | ||
3107 | first = i; | ||
3108 | last = i; | ||
3109 | more = 1; | ||
3110 | } else if (first != -1) | ||
3111 | break; | ||
3112 | } | ||
3113 | |||
3114 | if (first == 0 && last == MAX_NR_CONSOLES -1) | ||
3115 | deflt = 1; | ||
3116 | |||
3117 | if (first != -1) | ||
3118 | unbind_con_driver(csw, first, last, deflt); | ||
3119 | |||
3120 | first = -1; | ||
3121 | last = -1; | ||
3122 | deflt = 0; | ||
3123 | } | ||
3124 | |||
3125 | err: | ||
3126 | return 0; | ||
3127 | } | ||
3128 | #else | ||
3129 | int vt_bind(int index) | ||
3130 | { | ||
3131 | return 0; | ||
3132 | } | ||
3133 | |||
3134 | int vt_unbind(int index) | ||
3135 | { | ||
3136 | return 0; | ||
2772 | } | 3137 | } |
2773 | #endif | 3138 | #endif |
2774 | 3139 | ||
2775 | /* | 3140 | /* |
3141 | * this function is intended to be called by the tty layer only | ||
3142 | */ | ||
3143 | int vt_show_drivers(char *buf) | ||
3144 | { | ||
3145 | int i, j, read, offset = 0, cnt = PAGE_SIZE; | ||
3146 | |||
3147 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { | ||
3148 | struct con_driver *con_driver = ®istered_con_driver[i]; | ||
3149 | |||
3150 | if (con_driver->con != NULL) { | ||
3151 | int sys = 0; | ||
3152 | |||
3153 | if (con_driver->flag & CON_DRIVER_FLAG_BIND) { | ||
3154 | sys = 2; | ||
3155 | |||
3156 | for (j = 0; j < MAX_NR_CONSOLES; j++) { | ||
3157 | if (con_driver_map[j] == | ||
3158 | con_driver->con) { | ||
3159 | sys = 1; | ||
3160 | break; | ||
3161 | } | ||
3162 | } | ||
3163 | } | ||
3164 | |||
3165 | read = snprintf(buf + offset, cnt, "%i %s: %s\n", | ||
3166 | i, (sys) ? ((sys == 1) ? "B" : "U") : | ||
3167 | "S", con_driver->desc); | ||
3168 | offset += read; | ||
3169 | cnt -= read; | ||
3170 | } | ||
3171 | } | ||
3172 | |||
3173 | return offset; | ||
3174 | } | ||
3175 | |||
3176 | /* | ||
2776 | * Screen blanking | 3177 | * Screen blanking |
2777 | */ | 3178 | */ |
2778 | 3179 | ||
diff --git a/include/linux/console.h b/include/linux/console.h index d0f8a8009490..3bdf2155e565 100644 --- a/include/linux/console.h +++ b/include/linux/console.h | |||
@@ -63,9 +63,11 @@ extern const struct consw vga_con; /* VGA text console */ | |||
63 | extern const struct consw newport_con; /* SGI Newport console */ | 63 | extern const struct consw newport_con; /* SGI Newport console */ |
64 | extern const struct consw prom_con; /* SPARC PROM console */ | 64 | extern const struct consw prom_con; /* SPARC PROM console */ |
65 | 65 | ||
66 | int con_is_bound(const struct consw *csw); | ||
67 | int register_con_driver(const struct consw *csw, int first, int last); | ||
68 | int unregister_con_driver(const struct consw *csw); | ||
66 | int take_over_console(const struct consw *sw, int first, int last, int deflt); | 69 | int take_over_console(const struct consw *sw, int first, int last, int deflt); |
67 | void give_up_console(const struct consw *sw); | 70 | void give_up_console(const struct consw *sw); |
68 | |||
69 | /* scroll */ | 71 | /* scroll */ |
70 | #define SM_UP (1) | 72 | #define SM_UP (1) |
71 | #define SM_DOWN (2) | 73 | #define SM_DOWN (2) |