Commit 3e795de7631b2366d7301182c8d91f6d2911467b

Authored by Antonino A. Daplas
Committed by Linus Torvalds
1 parent a4a73e1f02

[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>

Showing 2 changed files with 437 additions and 34 deletions Side-by-side Diff

... ... @@ -98,9 +98,21 @@
98 98 #include <asm/system.h>
99 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 115 const struct consw *conswitchp;
103   -static struct consw *defcsw; /* default console */
104 116  
105 117 /* A bitmap for codes <32. A bit of 1 indicates that the code
106 118 * corresponding to that bit number invokes some special action
... ... @@ -2558,7 +2570,7 @@
2558 2570 {
2559 2571 const char *display_desc = NULL;
2560 2572 struct vc_data *vc;
2561   - unsigned int currcons = 0;
  2573 + unsigned int currcons = 0, i;
2562 2574  
2563 2575 acquire_console_sem();
2564 2576  
... ... @@ -2570,6 +2582,22 @@
2570 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 2601 init_timer(&console_timer);
2574 2602 console_timer.function = blank_screen_t;
2575 2603 if (blankinterval) {
2576 2604  
2577 2605  
2578 2606  
2579 2607  
2580 2608  
2581 2609  
... ... @@ -2658,35 +2686,38 @@
2658 2686  
2659 2687 #ifndef VT_SINGLE_DRIVER
2660 2688  
2661   -/*
2662   - * If we support more console drivers, this function is used
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)
  2689 +static int bind_con_driver(const struct consw *csw, int first, int last,
  2690 + int deflt)
2668 2691 {
2669   - int i, j = -1, k = -1;
2670   - const char *desc;
2671   - struct module *owner;
  2692 + struct module *owner = csw->owner;
  2693 + const char *desc = NULL;
  2694 + struct con_driver *con_driver;
  2695 + int i, j = -1, k = -1, retval = -ENODEV;
2672 2696  
2673   - owner = csw->owner;
2674 2697 if (!try_module_get(owner))
2675 2698 return -ENODEV;
2676 2699  
2677   - /* save default console, for possible recovery later on */
2678   - if (!defcsw)
2679   - defcsw = (struct consw *) conswitchp;
2680   -
2681 2700 acquire_console_sem();
2682   - desc = csw->con_startup();
2683 2701  
2684   - if (!desc) {
2685   - release_console_sem();
2686   - module_put(owner);
2687   - return -ENODEV;
  2702 + /* check if driver is registered */
  2703 + for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
  2704 + con_driver = &registered_con_driver[i];
  2705 +
  2706 + if (con_driver->con == csw) {
  2707 + desc = con_driver->desc;
  2708 + retval = 0;
  2709 + break;
  2710 + }
2688 2711 }
2689 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;
  2719 + }
  2720 +
2690 2721 if (deflt) {
2691 2722 if (conswitchp)
2692 2723 module_put(conswitchp->owner);
... ... @@ -2695,6 +2726,9 @@
2695 2726 conswitchp = csw;
2696 2727 }
2697 2728  
  2729 + first = max(first, con_driver->first);
  2730 + last = min(last, con_driver->last);
  2731 +
2698 2732 for (i = first; i <= last; i++) {
2699 2733 int old_was_color;
2700 2734 struct vc_data *vc = vc_cons[i].d;
2701 2735  
2702 2736  
2703 2737  
2704 2738  
2705 2739  
2706 2740  
2707 2741  
2708 2742  
2709 2743  
2710 2744  
2711 2745  
... ... @@ -2746,31 +2780,398 @@
2746 2780 } else
2747 2781 printk("to %s\n", desc);
2748 2782  
  2783 + retval = 0;
  2784 +err:
2749 2785 release_console_sem();
2750 2786 module_put(owner);
2751   - return 0;
2752   -}
  2787 + return retval;
  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 = &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 2845 if (con_driver_map[i] == csw) {
2760   - if (first == -1)
2761   - first = i;
2762   - last = i;
2763 2846 module_put(csw->owner);
2764 2847 con_driver_map[i] = NULL;
2765 2848 }
  2849 + }
2766 2850  
2767   - if (first != -1 && defcsw) {
2768   - if (first == 0 && last == MAX_NR_CONSOLES - 1)
  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 = &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 +
  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 = &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 +
  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 = &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 + }
  3050 +
  3051 + if (!defcsw)
  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 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;
2772 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 = &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 +
  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;
  3137 +}
2773 3138 #endif
  3139 +
  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 = &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 +}
2774 3175  
2775 3176 /*
2776 3177 * Screen blanking
include/linux/console.h
... ... @@ -63,9 +63,11 @@
63 63 extern const struct consw newport_con; /* SGI Newport console */
64 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 69 int take_over_console(const struct consw *sw, int first, int last, int deflt);
67 70 void give_up_console(const struct consw *sw);
68   -
69 71 /* scroll */
70 72 #define SM_UP (1)
71 73 #define SM_DOWN (2)