diff options
author | Antonino A. Daplas <adaplas@gmail.com> | 2006-06-26 03:27:12 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-06-26 12:58:33 -0400 |
commit | 6db4063c5b72b46e9793b0f141a7a3984ac6facf (patch) | |
tree | 47e4295617e6c47aae2a1344923dcce6fd81166a /drivers/char/vt.c | |
parent | 79062a0d396272f5b103d5223f3c96c58fd27451 (diff) |
[PATCH] VT binding: Add sysfs control to the VT layer
Add sysfs control to the VT layer. A new sysfs class, 'vtconsole', and class
devices 'vtcon[n]' are added. Each class device file has the following
attributes:
/sys/class/vtconsole/vtcon[n]/name - read-only attribute showing the
name of the current backend
/sys/class/vtconsole/vtcon[n]/bind - read/write attribute
where: 0 - backend is unbound/unbind backend from the VT layer
1 - backend is bound/bind backend to the VT layer
In addition, if any of the consoles are in KD_GRAPHICS mode, binding and
unbinding will not succeed. KD_GRAPHICS mode usually indicates that the
underlying console hardware is used for other purposes other than displaying
text (ie X). This feature should prevent binding/unbinding from interfering
with a graphics application using the VT.
[akpm@osdl.org: warning fixes]
Signed-off-by: Antonino Daplas <adaplas@pol.net>
Cc: Greg KH <greg@kroah.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/char/vt.c')
-rw-r--r-- | drivers/char/vt.c | 379 |
1 files changed, 232 insertions, 147 deletions
diff --git a/drivers/char/vt.c b/drivers/char/vt.c index d788a0eaf26a..037d8a3f5882 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c | |||
@@ -100,12 +100,14 @@ | |||
100 | 100 | ||
101 | #define MAX_NR_CON_DRIVER 16 | 101 | #define MAX_NR_CON_DRIVER 16 |
102 | 102 | ||
103 | #define CON_DRIVER_FLAG_BIND 1 | 103 | #define CON_DRIVER_FLAG_MODULE 1 |
104 | #define CON_DRIVER_FLAG_INIT 2 | 104 | #define CON_DRIVER_FLAG_INIT 2 |
105 | 105 | ||
106 | struct con_driver { | 106 | struct con_driver { |
107 | const struct consw *con; | 107 | const struct consw *con; |
108 | const char *desc; | 108 | const char *desc; |
109 | struct class_device *class_dev; | ||
110 | int node; | ||
109 | int first; | 111 | int first; |
110 | int last; | 112 | int last; |
111 | int flag; | 113 | int flag; |
@@ -2685,6 +2687,25 @@ int __init vty_init(void) | |||
2685 | } | 2687 | } |
2686 | 2688 | ||
2687 | #ifndef VT_SINGLE_DRIVER | 2689 | #ifndef VT_SINGLE_DRIVER |
2690 | #include <linux/device.h> | ||
2691 | |||
2692 | static struct class *vtconsole_class; | ||
2693 | |||
2694 | static int con_is_graphics(const struct consw *csw, int first, int last) | ||
2695 | { | ||
2696 | int i, retval = 0; | ||
2697 | |||
2698 | for (i = first; i <= last; i++) { | ||
2699 | struct vc_data *vc = vc_cons[i].d; | ||
2700 | |||
2701 | if (vc && vc->vc_mode == KD_GRAPHICS) { | ||
2702 | retval = 1; | ||
2703 | break; | ||
2704 | } | ||
2705 | } | ||
2706 | |||
2707 | return retval; | ||
2708 | } | ||
2688 | 2709 | ||
2689 | static int bind_con_driver(const struct consw *csw, int first, int last, | 2710 | static int bind_con_driver(const struct consw *csw, int first, int last, |
2690 | int deflt) | 2711 | int deflt) |
@@ -2805,7 +2826,7 @@ static int unbind_con_driver(const struct consw *csw, int first, int last, | |||
2805 | con_driver = ®istered_con_driver[i]; | 2826 | con_driver = ®istered_con_driver[i]; |
2806 | 2827 | ||
2807 | if (con_driver->con == csw && | 2828 | if (con_driver->con == csw && |
2808 | con_driver->flag & CON_DRIVER_FLAG_BIND) { | 2829 | con_driver->flag & CON_DRIVER_FLAG_MODULE) { |
2809 | retval = 0; | 2830 | retval = 0; |
2810 | break; | 2831 | break; |
2811 | } | 2832 | } |
@@ -2816,12 +2837,14 @@ static int unbind_con_driver(const struct consw *csw, int first, int last, | |||
2816 | goto err; | 2837 | goto err; |
2817 | } | 2838 | } |
2818 | 2839 | ||
2840 | retval = -ENODEV; | ||
2841 | |||
2819 | /* check if backup driver exists */ | 2842 | /* check if backup driver exists */ |
2820 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { | 2843 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { |
2821 | con_back = ®istered_con_driver[i]; | 2844 | con_back = ®istered_con_driver[i]; |
2822 | 2845 | ||
2823 | if (con_back->con && | 2846 | if (con_back->con && |
2824 | !(con_back->flag & CON_DRIVER_FLAG_BIND)) { | 2847 | !(con_back->flag & CON_DRIVER_FLAG_MODULE)) { |
2825 | defcsw = con_back->con; | 2848 | defcsw = con_back->con; |
2826 | retval = 0; | 2849 | retval = 0; |
2827 | break; | 2850 | break; |
@@ -2849,21 +2872,177 @@ static int unbind_con_driver(const struct consw *csw, int first, int last, | |||
2849 | } | 2872 | } |
2850 | 2873 | ||
2851 | if (!con_is_bound(defcsw)) { | 2874 | if (!con_is_bound(defcsw)) { |
2875 | const struct consw *defconsw = conswitchp; | ||
2876 | |||
2852 | defcsw->con_startup(); | 2877 | defcsw->con_startup(); |
2853 | con_back->flag |= CON_DRIVER_FLAG_INIT; | 2878 | con_back->flag |= CON_DRIVER_FLAG_INIT; |
2879 | /* | ||
2880 | * vgacon may change the default driver to point | ||
2881 | * to dummycon, we restore it here... | ||
2882 | */ | ||
2883 | conswitchp = defconsw; | ||
2854 | } | 2884 | } |
2855 | 2885 | ||
2856 | if (!con_is_bound(csw)) | 2886 | if (!con_is_bound(csw)) |
2857 | con_driver->flag &= ~CON_DRIVER_FLAG_INIT; | 2887 | con_driver->flag &= ~CON_DRIVER_FLAG_INIT; |
2858 | 2888 | ||
2859 | release_console_sem(); | 2889 | release_console_sem(); |
2860 | retval = bind_con_driver(defcsw, first, last, deflt); | 2890 | /* ignore return value, binding should not fail */ |
2891 | bind_con_driver(defcsw, first, last, deflt); | ||
2861 | err: | 2892 | err: |
2862 | module_put(owner); | 2893 | module_put(owner); |
2863 | return retval; | 2894 | return retval; |
2864 | 2895 | ||
2865 | } | 2896 | } |
2866 | 2897 | ||
2898 | static int vt_bind(struct con_driver *con) | ||
2899 | { | ||
2900 | const struct consw *defcsw = NULL, *csw = NULL; | ||
2901 | int i, more = 1, first = -1, last = -1, deflt = 0; | ||
2902 | |||
2903 | if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE) || | ||
2904 | con_is_graphics(con->con, con->first, con->last)) | ||
2905 | goto err; | ||
2906 | |||
2907 | csw = con->con; | ||
2908 | |||
2909 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { | ||
2910 | struct con_driver *con = ®istered_con_driver[i]; | ||
2911 | |||
2912 | if (con->con && !(con->flag & CON_DRIVER_FLAG_MODULE)) { | ||
2913 | defcsw = con->con; | ||
2914 | break; | ||
2915 | } | ||
2916 | } | ||
2917 | |||
2918 | if (!defcsw) | ||
2919 | goto err; | ||
2920 | |||
2921 | while (more) { | ||
2922 | more = 0; | ||
2923 | |||
2924 | for (i = con->first; i <= con->last; i++) { | ||
2925 | if (con_driver_map[i] == defcsw) { | ||
2926 | if (first == -1) | ||
2927 | first = i; | ||
2928 | last = i; | ||
2929 | more = 1; | ||
2930 | } else if (first != -1) | ||
2931 | break; | ||
2932 | } | ||
2933 | |||
2934 | if (first == 0 && last == MAX_NR_CONSOLES -1) | ||
2935 | deflt = 1; | ||
2936 | |||
2937 | if (first != -1) | ||
2938 | bind_con_driver(csw, first, last, deflt); | ||
2939 | |||
2940 | first = -1; | ||
2941 | last = -1; | ||
2942 | deflt = 0; | ||
2943 | } | ||
2944 | |||
2945 | err: | ||
2946 | return 0; | ||
2947 | } | ||
2948 | |||
2949 | static int vt_unbind(struct con_driver *con) | ||
2950 | { | ||
2951 | const struct consw *csw = NULL; | ||
2952 | int i, more = 1, first = -1, last = -1, deflt = 0; | ||
2953 | |||
2954 | if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE) || | ||
2955 | con_is_graphics(con->con, con->first, con->last)) | ||
2956 | goto err; | ||
2957 | |||
2958 | csw = con->con; | ||
2959 | |||
2960 | while (more) { | ||
2961 | more = 0; | ||
2962 | |||
2963 | for (i = con->first; i <= con->last; i++) { | ||
2964 | if (con_driver_map[i] == csw) { | ||
2965 | if (first == -1) | ||
2966 | first = i; | ||
2967 | last = i; | ||
2968 | more = 1; | ||
2969 | } else if (first != -1) | ||
2970 | break; | ||
2971 | } | ||
2972 | |||
2973 | if (first == 0 && last == MAX_NR_CONSOLES -1) | ||
2974 | deflt = 1; | ||
2975 | |||
2976 | if (first != -1) | ||
2977 | unbind_con_driver(csw, first, last, deflt); | ||
2978 | |||
2979 | first = -1; | ||
2980 | last = -1; | ||
2981 | deflt = 0; | ||
2982 | } | ||
2983 | |||
2984 | err: | ||
2985 | return 0; | ||
2986 | } | ||
2987 | |||
2988 | static ssize_t store_bind(struct class_device *class_device, | ||
2989 | const char *buf, size_t count) | ||
2990 | { | ||
2991 | struct con_driver *con = class_get_devdata(class_device); | ||
2992 | int bind = simple_strtoul(buf, NULL, 0); | ||
2993 | |||
2994 | if (bind) | ||
2995 | vt_bind(con); | ||
2996 | else | ||
2997 | vt_unbind(con); | ||
2998 | |||
2999 | return count; | ||
3000 | } | ||
3001 | |||
3002 | static ssize_t show_bind(struct class_device *class_device, char *buf) | ||
3003 | { | ||
3004 | struct con_driver *con = class_get_devdata(class_device); | ||
3005 | int bind = con_is_bound(con->con); | ||
3006 | |||
3007 | return snprintf(buf, PAGE_SIZE, "%i\n", bind); | ||
3008 | } | ||
3009 | |||
3010 | static ssize_t show_name(struct class_device *class_device, char *buf) | ||
3011 | { | ||
3012 | struct con_driver *con = class_get_devdata(class_device); | ||
3013 | |||
3014 | return snprintf(buf, PAGE_SIZE, "%s %s\n", | ||
3015 | (con->flag & CON_DRIVER_FLAG_MODULE) ? "(M)" : "(S)", | ||
3016 | con->desc); | ||
3017 | |||
3018 | } | ||
3019 | |||
3020 | static struct class_device_attribute class_device_attrs[] = { | ||
3021 | __ATTR(bind, S_IRUGO|S_IWUSR, show_bind, store_bind), | ||
3022 | __ATTR(name, S_IRUGO, show_name, NULL), | ||
3023 | }; | ||
3024 | |||
3025 | static int vtconsole_init_class_device(struct con_driver *con) | ||
3026 | { | ||
3027 | int i; | ||
3028 | |||
3029 | class_set_devdata(con->class_dev, con); | ||
3030 | for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++) | ||
3031 | class_device_create_file(con->class_dev, | ||
3032 | &class_device_attrs[i]); | ||
3033 | |||
3034 | return 0; | ||
3035 | } | ||
3036 | |||
3037 | static void vtconsole_deinit_class_device(struct con_driver *con) | ||
3038 | { | ||
3039 | int i; | ||
3040 | |||
3041 | for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++) | ||
3042 | class_device_remove_file(con->class_dev, | ||
3043 | &class_device_attrs[i]); | ||
3044 | } | ||
3045 | |||
2867 | /** | 3046 | /** |
2868 | * con_is_bound - checks if driver is bound to the console | 3047 | * con_is_bound - checks if driver is bound to the console |
2869 | * @csw: console driver | 3048 | * @csw: console driver |
@@ -2934,7 +3113,8 @@ int register_con_driver(const struct consw *csw, int first, int last) | |||
2934 | if (con_driver->con == NULL) { | 3113 | if (con_driver->con == NULL) { |
2935 | con_driver->con = csw; | 3114 | con_driver->con = csw; |
2936 | con_driver->desc = desc; | 3115 | con_driver->desc = desc; |
2937 | con_driver->flag = CON_DRIVER_FLAG_BIND | | 3116 | con_driver->node = i; |
3117 | con_driver->flag = CON_DRIVER_FLAG_MODULE | | ||
2938 | CON_DRIVER_FLAG_INIT; | 3118 | CON_DRIVER_FLAG_INIT; |
2939 | con_driver->first = first; | 3119 | con_driver->first = first; |
2940 | con_driver->last = last; | 3120 | con_driver->last = last; |
@@ -2943,6 +3123,22 @@ int register_con_driver(const struct consw *csw, int first, int last) | |||
2943 | } | 3123 | } |
2944 | } | 3124 | } |
2945 | 3125 | ||
3126 | if (retval) | ||
3127 | goto err; | ||
3128 | |||
3129 | con_driver->class_dev = class_device_create(vtconsole_class, NULL, | ||
3130 | MKDEV(0, con_driver->node), | ||
3131 | NULL, "vtcon%i", | ||
3132 | con_driver->node); | ||
3133 | |||
3134 | if (IS_ERR(con_driver->class_dev)) { | ||
3135 | printk(KERN_WARNING "Unable to create class_device for %s; " | ||
3136 | "errno = %ld\n", con_driver->desc, | ||
3137 | PTR_ERR(con_driver->class_dev)); | ||
3138 | con_driver->class_dev = NULL; | ||
3139 | } else { | ||
3140 | vtconsole_init_class_device(con_driver); | ||
3141 | } | ||
2946 | err: | 3142 | err: |
2947 | release_console_sem(); | 3143 | release_console_sem(); |
2948 | module_put(owner); | 3144 | module_put(owner); |
@@ -2975,9 +3171,14 @@ int unregister_con_driver(const struct consw *csw) | |||
2975 | struct con_driver *con_driver = ®istered_con_driver[i]; | 3171 | struct con_driver *con_driver = ®istered_con_driver[i]; |
2976 | 3172 | ||
2977 | if (con_driver->con == csw && | 3173 | if (con_driver->con == csw && |
2978 | con_driver->flag & CON_DRIVER_FLAG_BIND) { | 3174 | con_driver->flag & CON_DRIVER_FLAG_MODULE) { |
3175 | vtconsole_deinit_class_device(con_driver); | ||
3176 | class_device_destroy(vtconsole_class, | ||
3177 | MKDEV(0, con_driver->node)); | ||
2979 | con_driver->con = NULL; | 3178 | con_driver->con = NULL; |
2980 | con_driver->desc = NULL; | 3179 | con_driver->desc = NULL; |
3180 | con_driver->class_dev = NULL; | ||
3181 | con_driver->node = 0; | ||
2981 | con_driver->flag = 0; | 3182 | con_driver->flag = 0; |
2982 | con_driver->first = 0; | 3183 | con_driver->first = 0; |
2983 | con_driver->last = 0; | 3184 | con_driver->last = 0; |
@@ -2985,7 +3186,6 @@ int unregister_con_driver(const struct consw *csw) | |||
2985 | break; | 3186 | break; |
2986 | } | 3187 | } |
2987 | } | 3188 | } |
2988 | |||
2989 | err: | 3189 | err: |
2990 | release_console_sem(); | 3190 | release_console_sem(); |
2991 | return retval; | 3191 | return retval; |
@@ -3006,7 +3206,7 @@ int take_over_console(const struct consw *csw, int first, int last, int deflt) | |||
3006 | err = register_con_driver(csw, first, last); | 3206 | err = register_con_driver(csw, first, last); |
3007 | 3207 | ||
3008 | if (!err) | 3208 | if (!err) |
3009 | err = bind_con_driver(csw, first, last, deflt); | 3209 | bind_con_driver(csw, first, last, deflt); |
3010 | 3210 | ||
3011 | return err; | 3211 | return err; |
3012 | } | 3212 | } |
@@ -3020,160 +3220,45 @@ void give_up_console(const struct consw *csw) | |||
3020 | unregister_con_driver(csw); | 3220 | unregister_con_driver(csw); |
3021 | } | 3221 | } |
3022 | 3222 | ||
3023 | /* | 3223 | static int __init vtconsole_class_init(void) |
3024 | * this function is intended to be called by the tty layer only | ||
3025 | */ | ||
3026 | int vt_bind(int index) | ||
3027 | { | 3224 | { |
3028 | const struct consw *defcsw = NULL, *csw = NULL; | 3225 | int i; |
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 | 3226 | ||
3040 | csw = con->con; | 3227 | vtconsole_class = class_create(THIS_MODULE, "vtconsole"); |
3228 | if (IS_ERR(vtconsole_class)) { | ||
3229 | printk(KERN_WARNING "Unable to create vt console class; " | ||
3230 | "errno = %ld\n", PTR_ERR(vtconsole_class)); | ||
3231 | vtconsole_class = NULL; | ||
3232 | } | ||
3041 | 3233 | ||
3234 | /* Add system drivers to sysfs */ | ||
3042 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { | 3235 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { |
3043 | struct con_driver *con = ®istered_con_driver[i]; | 3236 | struct con_driver *con = ®istered_con_driver[i]; |
3044 | 3237 | ||
3045 | if (con->con && !(con->flag & CON_DRIVER_FLAG_BIND)) { | 3238 | if (con->con && !con->class_dev) { |
3046 | defcsw = con->con; | 3239 | con->class_dev = |
3047 | break; | 3240 | class_device_create(vtconsole_class, NULL, |
3048 | } | 3241 | MKDEV(0, con->node), NULL, |
3049 | } | 3242 | "vtcon%i", con->node); |
3050 | 3243 | ||
3051 | if (!defcsw) | 3244 | if (IS_ERR(con->class_dev)) { |
3052 | goto err; | 3245 | printk(KERN_WARNING "Unable to create " |
3053 | 3246 | "class_device for %s; errno = %ld\n", | |
3054 | while (more) { | 3247 | con->desc, PTR_ERR(con->class_dev)); |
3055 | more = 0; | 3248 | con->class_dev = NULL; |
3056 | 3249 | } else { | |
3057 | for (i = con->first; i <= con->last; i++) { | 3250 | vtconsole_init_class_device(con); |
3058 | if (con_driver_map[i] == defcsw) { | 3251 | } |
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) | ||
3068 | deflt = 1; | ||
3069 | |||
3070 | if (first != -1) | ||
3071 | bind_con_driver(csw, first, last, deflt); | ||
3072 | |||
3073 | first = -1; | ||
3074 | last = -1; | ||
3075 | deflt = 0; | ||
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 | } | 3252 | } |
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 | } | 3253 | } |
3124 | 3254 | ||
3125 | err: | ||
3126 | return 0; | ||
3127 | } | ||
3128 | #else | ||
3129 | int vt_bind(int index) | ||
3130 | { | ||
3131 | return 0; | 3255 | return 0; |
3132 | } | 3256 | } |
3257 | postcore_initcall(vtconsole_class_init); | ||
3133 | 3258 | ||
3134 | int vt_unbind(int index) | ||
3135 | { | ||
3136 | return 0; | ||
3137 | } | ||
3138 | #endif | 3259 | #endif |
3139 | 3260 | ||
3140 | /* | 3261 | /* |
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 | /* | ||
3177 | * Screen blanking | 3262 | * Screen blanking |
3178 | */ | 3263 | */ |
3179 | 3264 | ||