aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/char/vt.c467
-rw-r--r--include/linux/console.h4
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
106struct con_driver {
107 const struct consw *con;
108 const char *desc;
109 int first;
110 int last;
111 int flag;
112};
113
114static struct con_driver registered_con_driver[MAX_NR_CON_DRIVER];
102const struct consw *conswitchp; 115const struct consw *conswitchp;
103static 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 = &registered_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/* 2689static 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
2667int 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 = &registered_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;
2784err:
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
2754void give_up_console(const struct consw *csw) 2790static 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 = &registered_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 = &registered_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);
2861err:
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 */
2876int 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}
2889EXPORT_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 */
2901int 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 = &registered_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 = &registered_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
2946err:
2947 release_console_sem();
2948 module_put(owner);
2949 return retval;
2950}
2951EXPORT_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 */
2964int 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 = &registered_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
2989err:
2990 release_console_sem();
2991 return retval;
2992}
2993EXPORT_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 */
3002int 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 */
3018void 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 */
3026int 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 = &registered_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 = &registered_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
3078err:
3079 return 0;
3080}
3081
3082/*
3083 * this function is intended to be called by the tty layer only
3084 */
3085int 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 = &registered_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
3125err:
3126 return 0;
3127}
3128#else
3129int vt_bind(int index)
3130{
3131 return 0;
3132}
3133
3134int 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 */
3143int 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 = &registered_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 */
63extern const struct consw newport_con; /* SGI Newport console */ 63extern const struct consw newport_con; /* SGI Newport console */
64extern const struct consw prom_con; /* SPARC PROM console */ 64extern const struct consw prom_con; /* SPARC PROM console */
65 65
66int con_is_bound(const struct consw *csw);
67int register_con_driver(const struct consw *csw, int first, int last);
68int unregister_con_driver(const struct consw *csw);
66int take_over_console(const struct consw *sw, int first, int last, int deflt); 69int take_over_console(const struct consw *sw, int first, int last, int deflt);
67void give_up_console(const struct consw *sw); 70void 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)