aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAntonino A. Daplas <adaplas@gmail.com>2006-06-26 03:27:08 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-06-26 12:58:33 -0400
commit3e795de7631b2366d7301182c8d91f6d2911467b (patch)
treee119d2eec9825ad86c2b07e43d2bb06e7d558858
parenta4a73e1f0283850edc143d28502c1c517c6ab49c (diff)
[PATCH] VT binding: Add binding/unbinding support for the VT console
The framebuffer console is now able to dynamically bind and unbind from the VT console layer. Due to the way the VT console layer works, the drivers themselves decide when to bind or unbind. However, it was decided that binding must be controlled, not by the drivers themselves, but by the VT console layer. With this, dynamic binding is possible for all VT console drivers, not just fbcon. Thus, the VT console layer will impose the following to all VT console drivers: - all registered VT console drivers will be entered in a private list - drivers can register themselves to the VT console layer, but they cannot decide when to bind or unbind. (Exception: To maintain backwards compatibility, take_over_console() will automatically bind the driver after registration.) - drivers can remove themselves from the list by unregistering from the VT console layer. A prerequisite for unregistration is that the driver must not be bound. The following functions are new in the vt.c: register_con_driver() - public function, this function adds the VT console driver to an internal list maintained by the VT console bind_con_driver() - private function, it binds the driver to the console take_over_console() is changed to call register_con_driver() followed by a bind_con_driver(). This is the only time drivers can decide when to bind to the VT layer. This is to maintain backwards compatibility. unbind_con_driver() - private function, it unbinds the driver from its console. The vacated consoles will be taken over by the default boot console driver. unregister_con_driver() - public function, removes the driver from the internal list maintained by the VT console. It will only succeed if the driver is currently unbound. con_is_bound() checks if the driver is currently bound or not give_up_console() is just a wrapper to unregister_con_driver(). There are also 3 additional functions meant to be called only by the tty layer for sysfs control: vt_bind() - calls bind_con_driver() vt_unbind() - calls unbind_con_driver() vt_show_drivers() - shows the list of registered drivers Most VT console drivers will continue to work as is, but might have problems when unbinding or binding which should be fixable with minimal changes. Signed-off-by: Antonino Daplas <adaplas@pol.net> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-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)