diff options
Diffstat (limited to 'drivers/video/console/fbcon.c')
-rw-r--r-- | drivers/video/console/fbcon.c | 361 |
1 files changed, 311 insertions, 50 deletions
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 47ba1a79adcd..5dc4083552d8 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c | |||
@@ -125,6 +125,8 @@ static int softback_lines; | |||
125 | static int first_fb_vc; | 125 | static int first_fb_vc; |
126 | static int last_fb_vc = MAX_NR_CONSOLES - 1; | 126 | static int last_fb_vc = MAX_NR_CONSOLES - 1; |
127 | static int fbcon_is_default = 1; | 127 | static int fbcon_is_default = 1; |
128 | static int fbcon_has_exited; | ||
129 | |||
128 | /* font data */ | 130 | /* font data */ |
129 | static char fontname[40]; | 131 | static char fontname[40]; |
130 | 132 | ||
@@ -140,7 +142,6 @@ static const struct consw fb_con; | |||
140 | 142 | ||
141 | #define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row) | 143 | #define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row) |
142 | 144 | ||
143 | static void fbcon_free_font(struct display *); | ||
144 | static int fbcon_set_origin(struct vc_data *); | 145 | static int fbcon_set_origin(struct vc_data *); |
145 | 146 | ||
146 | #define CURSOR_DRAW_DELAY (1) | 147 | #define CURSOR_DRAW_DELAY (1) |
@@ -194,6 +195,9 @@ static void fbcon_redraw_move(struct vc_data *vc, struct display *p, | |||
194 | int line, int count, int dy); | 195 | int line, int count, int dy); |
195 | static void fbcon_modechanged(struct fb_info *info); | 196 | static void fbcon_modechanged(struct fb_info *info); |
196 | static void fbcon_set_all_vcs(struct fb_info *info); | 197 | static void fbcon_set_all_vcs(struct fb_info *info); |
198 | static void fbcon_start(void); | ||
199 | static void fbcon_exit(void); | ||
200 | static struct class_device *fbcon_class_device; | ||
197 | 201 | ||
198 | #ifdef CONFIG_MAC | 202 | #ifdef CONFIG_MAC |
199 | /* | 203 | /* |
@@ -252,7 +256,7 @@ static void fbcon_rotate_all(struct fb_info *info, u32 rotate) | |||
252 | if (!ops || ops->currcon < 0 || rotate > 3) | 256 | if (!ops || ops->currcon < 0 || rotate > 3) |
253 | return; | 257 | return; |
254 | 258 | ||
255 | for (i = 0; i < MAX_NR_CONSOLES; i++) { | 259 | for (i = first_fb_vc; i <= last_fb_vc; i++) { |
256 | vc = vc_cons[i].d; | 260 | vc = vc_cons[i].d; |
257 | if (!vc || vc->vc_mode != KD_TEXT || | 261 | if (!vc || vc->vc_mode != KD_TEXT || |
258 | registered_fb[con2fb_map[i]] != info) | 262 | registered_fb[con2fb_map[i]] != info) |
@@ -389,15 +393,18 @@ static void fb_flashcursor(void *private) | |||
389 | int c; | 393 | int c; |
390 | int mode; | 394 | int mode; |
391 | 395 | ||
392 | if (ops->currcon != -1) | 396 | acquire_console_sem(); |
397 | if (ops && ops->currcon != -1) | ||
393 | vc = vc_cons[ops->currcon].d; | 398 | vc = vc_cons[ops->currcon].d; |
394 | 399 | ||
395 | if (!vc || !CON_IS_VISIBLE(vc) || | 400 | if (!vc || !CON_IS_VISIBLE(vc) || |
396 | fbcon_is_inactive(vc, info) || | 401 | fbcon_is_inactive(vc, info) || |
397 | registered_fb[con2fb_map[vc->vc_num]] != info || | 402 | registered_fb[con2fb_map[vc->vc_num]] != info || |
398 | vc_cons[ops->currcon].d->vc_deccm != 1) | 403 | vc_cons[ops->currcon].d->vc_deccm != 1) { |
404 | release_console_sem(); | ||
399 | return; | 405 | return; |
400 | acquire_console_sem(); | 406 | } |
407 | |||
401 | p = &fb_display[vc->vc_num]; | 408 | p = &fb_display[vc->vc_num]; |
402 | c = scr_readw((u16 *) vc->vc_pos); | 409 | c = scr_readw((u16 *) vc->vc_pos); |
403 | mode = (!ops->cursor_flash || ops->cursor_state.enable) ? | 410 | mode = (!ops->cursor_flash || ops->cursor_state.enable) ? |
@@ -528,7 +535,7 @@ static int search_fb_in_map(int idx) | |||
528 | { | 535 | { |
529 | int i, retval = 0; | 536 | int i, retval = 0; |
530 | 537 | ||
531 | for (i = 0; i < MAX_NR_CONSOLES; i++) { | 538 | for (i = first_fb_vc; i <= last_fb_vc; i++) { |
532 | if (con2fb_map[i] == idx) | 539 | if (con2fb_map[i] == idx) |
533 | retval = 1; | 540 | retval = 1; |
534 | } | 541 | } |
@@ -539,7 +546,7 @@ static int search_for_mapped_con(void) | |||
539 | { | 546 | { |
540 | int i, retval = 0; | 547 | int i, retval = 0; |
541 | 548 | ||
542 | for (i = 0; i < MAX_NR_CONSOLES; i++) { | 549 | for (i = first_fb_vc; i <= last_fb_vc; i++) { |
543 | if (con2fb_map[i] != -1) | 550 | if (con2fb_map[i] != -1) |
544 | retval = 1; | 551 | retval = 1; |
545 | } | 552 | } |
@@ -561,6 +568,7 @@ static int fbcon_takeover(int show_logo) | |||
561 | 568 | ||
562 | err = take_over_console(&fb_con, first_fb_vc, last_fb_vc, | 569 | err = take_over_console(&fb_con, first_fb_vc, last_fb_vc, |
563 | fbcon_is_default); | 570 | fbcon_is_default); |
571 | |||
564 | if (err) { | 572 | if (err) { |
565 | for (i = first_fb_vc; i <= last_fb_vc; i++) { | 573 | for (i = first_fb_vc; i <= last_fb_vc; i++) { |
566 | con2fb_map[i] = -1; | 574 | con2fb_map[i] = -1; |
@@ -795,8 +803,8 @@ static int set_con2fb_map(int unit, int newidx, int user) | |||
795 | if (oldidx == newidx) | 803 | if (oldidx == newidx) |
796 | return 0; | 804 | return 0; |
797 | 805 | ||
798 | if (!info) | 806 | if (!info || fbcon_has_exited) |
799 | err = -EINVAL; | 807 | return -EINVAL; |
800 | 808 | ||
801 | if (!err && !search_for_mapped_con()) { | 809 | if (!err && !search_for_mapped_con()) { |
802 | info_idx = newidx; | 810 | info_idx = newidx; |
@@ -832,6 +840,9 @@ static int set_con2fb_map(int unit, int newidx, int user) | |||
832 | con2fb_init_display(vc, info, unit, show_logo); | 840 | con2fb_init_display(vc, info, unit, show_logo); |
833 | } | 841 | } |
834 | 842 | ||
843 | if (!search_fb_in_map(info_idx)) | ||
844 | info_idx = newidx; | ||
845 | |||
835 | release_console_sem(); | 846 | release_console_sem(); |
836 | return err; | 847 | return err; |
837 | } | 848 | } |
@@ -1034,6 +1045,7 @@ static const char *fbcon_startup(void) | |||
1034 | #endif /* CONFIG_MAC */ | 1045 | #endif /* CONFIG_MAC */ |
1035 | 1046 | ||
1036 | fbcon_add_cursor_timer(info); | 1047 | fbcon_add_cursor_timer(info); |
1048 | fbcon_has_exited = 0; | ||
1037 | return display_desc; | 1049 | return display_desc; |
1038 | } | 1050 | } |
1039 | 1051 | ||
@@ -1061,17 +1073,36 @@ static void fbcon_init(struct vc_data *vc, int init) | |||
1061 | 1073 | ||
1062 | /* If we are not the first console on this | 1074 | /* If we are not the first console on this |
1063 | fb, copy the font from that console */ | 1075 | fb, copy the font from that console */ |
1064 | t = &fb_display[svc->vc_num]; | 1076 | t = &fb_display[fg_console]; |
1065 | if (!vc->vc_font.data) { | 1077 | if (!p->fontdata) { |
1066 | vc->vc_font.data = (void *)(p->fontdata = t->fontdata); | 1078 | if (t->fontdata) { |
1067 | vc->vc_font.width = (*default_mode)->vc_font.width; | 1079 | struct vc_data *fvc = vc_cons[fg_console].d; |
1068 | vc->vc_font.height = (*default_mode)->vc_font.height; | 1080 | |
1069 | p->userfont = t->userfont; | 1081 | vc->vc_font.data = (void *)(p->fontdata = |
1070 | if (p->userfont) | 1082 | fvc->vc_font.data); |
1071 | REFCOUNT(p->fontdata)++; | 1083 | vc->vc_font.width = fvc->vc_font.width; |
1084 | vc->vc_font.height = fvc->vc_font.height; | ||
1085 | p->userfont = t->userfont; | ||
1086 | |||
1087 | if (p->userfont) | ||
1088 | REFCOUNT(p->fontdata)++; | ||
1089 | } else { | ||
1090 | const struct font_desc *font = NULL; | ||
1091 | |||
1092 | if (!fontname[0] || !(font = find_font(fontname))) | ||
1093 | font = get_default_font(info->var.xres, | ||
1094 | info->var.yres); | ||
1095 | vc->vc_font.width = font->width; | ||
1096 | vc->vc_font.height = font->height; | ||
1097 | vc->vc_font.data = (void *)(p->fontdata = font->data); | ||
1098 | vc->vc_font.charcount = 256; /* FIXME Need to | ||
1099 | support more fonts */ | ||
1100 | } | ||
1072 | } | 1101 | } |
1102 | |||
1073 | if (p->userfont) | 1103 | if (p->userfont) |
1074 | charcnt = FNTCHARCNT(p->fontdata); | 1104 | charcnt = FNTCHARCNT(p->fontdata); |
1105 | |||
1075 | vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1); | 1106 | vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1); |
1076 | vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; | 1107 | vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; |
1077 | if (charcnt == 256) { | 1108 | if (charcnt == 256) { |
@@ -1145,13 +1176,47 @@ static void fbcon_init(struct vc_data *vc, int init) | |||
1145 | ops->p = &fb_display[fg_console]; | 1176 | ops->p = &fb_display[fg_console]; |
1146 | } | 1177 | } |
1147 | 1178 | ||
1179 | static void fbcon_free_font(struct display *p) | ||
1180 | { | ||
1181 | if (p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0)) | ||
1182 | kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int)); | ||
1183 | p->fontdata = NULL; | ||
1184 | p->userfont = 0; | ||
1185 | } | ||
1186 | |||
1148 | static void fbcon_deinit(struct vc_data *vc) | 1187 | static void fbcon_deinit(struct vc_data *vc) |
1149 | { | 1188 | { |
1150 | struct display *p = &fb_display[vc->vc_num]; | 1189 | struct display *p = &fb_display[vc->vc_num]; |
1190 | struct fb_info *info; | ||
1191 | struct fbcon_ops *ops; | ||
1192 | int idx; | ||
1151 | 1193 | ||
1152 | if (info_idx != -1) | ||
1153 | return; | ||
1154 | fbcon_free_font(p); | 1194 | fbcon_free_font(p); |
1195 | idx = con2fb_map[vc->vc_num]; | ||
1196 | |||
1197 | if (idx == -1) | ||
1198 | goto finished; | ||
1199 | |||
1200 | info = registered_fb[idx]; | ||
1201 | |||
1202 | if (!info) | ||
1203 | goto finished; | ||
1204 | |||
1205 | ops = info->fbcon_par; | ||
1206 | |||
1207 | if (!ops) | ||
1208 | goto finished; | ||
1209 | |||
1210 | if (CON_IS_VISIBLE(vc)) | ||
1211 | fbcon_del_cursor_timer(info); | ||
1212 | |||
1213 | ops->flags &= ~FBCON_FLAGS_INIT; | ||
1214 | finished: | ||
1215 | |||
1216 | if (!con_is_bound(&fb_con)) | ||
1217 | fbcon_exit(); | ||
1218 | |||
1219 | return; | ||
1155 | } | 1220 | } |
1156 | 1221 | ||
1157 | /* ====================================================================== */ | 1222 | /* ====================================================================== */ |
@@ -2099,12 +2164,11 @@ static int fbcon_switch(struct vc_data *vc) | |||
2099 | if (info->fbops->fb_set_par) | 2164 | if (info->fbops->fb_set_par) |
2100 | info->fbops->fb_set_par(info); | 2165 | info->fbops->fb_set_par(info); |
2101 | 2166 | ||
2102 | if (old_info != info) { | 2167 | if (old_info != info) |
2103 | fbcon_del_cursor_timer(old_info); | 2168 | fbcon_del_cursor_timer(old_info); |
2104 | fbcon_add_cursor_timer(info); | ||
2105 | } | ||
2106 | } | 2169 | } |
2107 | 2170 | ||
2171 | fbcon_add_cursor_timer(info); | ||
2108 | set_blitting_type(vc, info); | 2172 | set_blitting_type(vc, info); |
2109 | ops->cursor_reset = 1; | 2173 | ops->cursor_reset = 1; |
2110 | 2174 | ||
@@ -2222,14 +2286,6 @@ static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch) | |||
2222 | return 0; | 2286 | return 0; |
2223 | } | 2287 | } |
2224 | 2288 | ||
2225 | static void fbcon_free_font(struct display *p) | ||
2226 | { | ||
2227 | if (p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0)) | ||
2228 | kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int)); | ||
2229 | p->fontdata = NULL; | ||
2230 | p->userfont = 0; | ||
2231 | } | ||
2232 | |||
2233 | static int fbcon_get_font(struct vc_data *vc, struct console_font *font) | 2289 | static int fbcon_get_font(struct vc_data *vc, struct console_font *font) |
2234 | { | 2290 | { |
2235 | u8 *fontdata = vc->vc_font.data; | 2291 | u8 *fontdata = vc->vc_font.data; |
@@ -2443,7 +2499,7 @@ static int fbcon_set_font(struct vc_data *vc, struct console_font *font, unsigne | |||
2443 | 2499 | ||
2444 | FNTSUM(new_data) = csum; | 2500 | FNTSUM(new_data) = csum; |
2445 | /* Check if the same font is on some other console already */ | 2501 | /* Check if the same font is on some other console already */ |
2446 | for (i = 0; i < MAX_NR_CONSOLES; i++) { | 2502 | for (i = first_fb_vc; i <= last_fb_vc; i++) { |
2447 | struct vc_data *tmp = vc_cons[i].d; | 2503 | struct vc_data *tmp = vc_cons[i].d; |
2448 | 2504 | ||
2449 | if (fb_display[i].userfont && | 2505 | if (fb_display[i].userfont && |
@@ -2768,7 +2824,7 @@ static void fbcon_set_all_vcs(struct fb_info *info) | |||
2768 | if (!ops || ops->currcon < 0) | 2824 | if (!ops || ops->currcon < 0) |
2769 | return; | 2825 | return; |
2770 | 2826 | ||
2771 | for (i = 0; i < MAX_NR_CONSOLES; i++) { | 2827 | for (i = first_fb_vc; i <= last_fb_vc; i++) { |
2772 | vc = vc_cons[i].d; | 2828 | vc = vc_cons[i].d; |
2773 | if (!vc || vc->vc_mode != KD_TEXT || | 2829 | if (!vc || vc->vc_mode != KD_TEXT || |
2774 | registered_fb[con2fb_map[i]] != info) | 2830 | registered_fb[con2fb_map[i]] != info) |
@@ -2830,22 +2886,57 @@ static int fbcon_mode_deleted(struct fb_info *info, | |||
2830 | return found; | 2886 | return found; |
2831 | } | 2887 | } |
2832 | 2888 | ||
2889 | static int fbcon_fb_unregistered(int idx) | ||
2890 | { | ||
2891 | int i; | ||
2892 | |||
2893 | for (i = first_fb_vc; i <= last_fb_vc; i++) { | ||
2894 | if (con2fb_map[i] == idx) | ||
2895 | con2fb_map[i] = -1; | ||
2896 | } | ||
2897 | |||
2898 | if (idx == info_idx) { | ||
2899 | info_idx = -1; | ||
2900 | |||
2901 | for (i = 0; i < FB_MAX; i++) { | ||
2902 | if (registered_fb[i] != NULL) { | ||
2903 | info_idx = i; | ||
2904 | break; | ||
2905 | } | ||
2906 | } | ||
2907 | } | ||
2908 | |||
2909 | if (info_idx != -1) { | ||
2910 | for (i = first_fb_vc; i <= last_fb_vc; i++) { | ||
2911 | if (con2fb_map[i] == -1) | ||
2912 | con2fb_map[i] = info_idx; | ||
2913 | } | ||
2914 | } | ||
2915 | |||
2916 | if (!num_registered_fb) | ||
2917 | unregister_con_driver(&fb_con); | ||
2918 | |||
2919 | return 0; | ||
2920 | } | ||
2921 | |||
2833 | static int fbcon_fb_registered(int idx) | 2922 | static int fbcon_fb_registered(int idx) |
2834 | { | 2923 | { |
2835 | int ret = 0, i; | 2924 | int ret = 0, i; |
2836 | 2925 | ||
2837 | if (info_idx == -1) { | 2926 | if (info_idx == -1) { |
2838 | for (i = 0; i < MAX_NR_CONSOLES; i++) { | 2927 | for (i = first_fb_vc; i <= last_fb_vc; i++) { |
2839 | if (con2fb_map_boot[i] == idx) { | 2928 | if (con2fb_map_boot[i] == idx) { |
2840 | info_idx = idx; | 2929 | info_idx = idx; |
2841 | break; | 2930 | break; |
2842 | } | 2931 | } |
2843 | } | 2932 | } |
2933 | |||
2844 | if (info_idx != -1) | 2934 | if (info_idx != -1) |
2845 | ret = fbcon_takeover(1); | 2935 | ret = fbcon_takeover(1); |
2846 | } else { | 2936 | } else { |
2847 | for (i = 0; i < MAX_NR_CONSOLES; i++) { | 2937 | for (i = first_fb_vc; i <= last_fb_vc; i++) { |
2848 | if (con2fb_map_boot[i] == idx) | 2938 | if (con2fb_map_boot[i] == idx && |
2939 | con2fb_map[i] == -1) | ||
2849 | set_con2fb_map(i, idx, 0); | 2940 | set_con2fb_map(i, idx, 0); |
2850 | } | 2941 | } |
2851 | } | 2942 | } |
@@ -2882,7 +2973,7 @@ static void fbcon_new_modelist(struct fb_info *info) | |||
2882 | struct fb_var_screeninfo var; | 2973 | struct fb_var_screeninfo var; |
2883 | struct fb_videomode *mode; | 2974 | struct fb_videomode *mode; |
2884 | 2975 | ||
2885 | for (i = 0; i < MAX_NR_CONSOLES; i++) { | 2976 | for (i = first_fb_vc; i <= last_fb_vc; i++) { |
2886 | if (registered_fb[con2fb_map[i]] != info) | 2977 | if (registered_fb[con2fb_map[i]] != info) |
2887 | continue; | 2978 | continue; |
2888 | if (!fb_display[i].mode) | 2979 | if (!fb_display[i].mode) |
@@ -2910,6 +3001,14 @@ static int fbcon_event_notify(struct notifier_block *self, | |||
2910 | struct fb_con2fbmap *con2fb; | 3001 | struct fb_con2fbmap *con2fb; |
2911 | int ret = 0; | 3002 | int ret = 0; |
2912 | 3003 | ||
3004 | /* | ||
3005 | * ignore all events except driver registration and deregistration | ||
3006 | * if fbcon is not active | ||
3007 | */ | ||
3008 | if (fbcon_has_exited && !(action == FB_EVENT_FB_REGISTERED || | ||
3009 | action == FB_EVENT_FB_UNREGISTERED)) | ||
3010 | goto done; | ||
3011 | |||
2913 | switch(action) { | 3012 | switch(action) { |
2914 | case FB_EVENT_SUSPEND: | 3013 | case FB_EVENT_SUSPEND: |
2915 | fbcon_suspended(info); | 3014 | fbcon_suspended(info); |
@@ -2930,6 +3029,9 @@ static int fbcon_event_notify(struct notifier_block *self, | |||
2930 | case FB_EVENT_FB_REGISTERED: | 3029 | case FB_EVENT_FB_REGISTERED: |
2931 | ret = fbcon_fb_registered(info->node); | 3030 | ret = fbcon_fb_registered(info->node); |
2932 | break; | 3031 | break; |
3032 | case FB_EVENT_FB_UNREGISTERED: | ||
3033 | ret = fbcon_fb_unregistered(info->node); | ||
3034 | break; | ||
2933 | case FB_EVENT_SET_CONSOLE_MAP: | 3035 | case FB_EVENT_SET_CONSOLE_MAP: |
2934 | con2fb = event->data; | 3036 | con2fb = event->data; |
2935 | ret = set_con2fb_map(con2fb->console - 1, | 3037 | ret = set_con2fb_map(con2fb->console - 1, |
@@ -2945,16 +3047,9 @@ static int fbcon_event_notify(struct notifier_block *self, | |||
2945 | case FB_EVENT_NEW_MODELIST: | 3047 | case FB_EVENT_NEW_MODELIST: |
2946 | fbcon_new_modelist(info); | 3048 | fbcon_new_modelist(info); |
2947 | break; | 3049 | break; |
2948 | case FB_EVENT_SET_CON_ROTATE: | ||
2949 | fbcon_rotate(info, *(int *)event->data); | ||
2950 | break; | ||
2951 | case FB_EVENT_GET_CON_ROTATE: | ||
2952 | ret = fbcon_get_rotate(info); | ||
2953 | break; | ||
2954 | case FB_EVENT_SET_CON_ROTATE_ALL: | ||
2955 | fbcon_rotate_all(info, *(int *)event->data); | ||
2956 | } | 3050 | } |
2957 | 3051 | ||
3052 | done: | ||
2958 | return ret; | 3053 | return ret; |
2959 | } | 3054 | } |
2960 | 3055 | ||
@@ -2992,27 +3087,181 @@ static struct notifier_block fbcon_event_notifier = { | |||
2992 | .notifier_call = fbcon_event_notify, | 3087 | .notifier_call = fbcon_event_notify, |
2993 | }; | 3088 | }; |
2994 | 3089 | ||
2995 | static int __init fb_console_init(void) | 3090 | static ssize_t store_rotate(struct class_device *class_device, |
3091 | const char *buf, size_t count) | ||
2996 | { | 3092 | { |
2997 | int i; | 3093 | struct fb_info *info; |
3094 | int rotate, idx; | ||
3095 | char **last = NULL; | ||
3096 | |||
3097 | if (fbcon_has_exited) | ||
3098 | return count; | ||
2998 | 3099 | ||
2999 | acquire_console_sem(); | 3100 | acquire_console_sem(); |
3000 | fb_register_client(&fbcon_event_notifier); | 3101 | idx = con2fb_map[fg_console]; |
3102 | |||
3103 | if (idx == -1 || registered_fb[idx] == NULL) | ||
3104 | goto err; | ||
3105 | |||
3106 | info = registered_fb[idx]; | ||
3107 | rotate = simple_strtoul(buf, last, 0); | ||
3108 | fbcon_rotate(info, rotate); | ||
3109 | err: | ||
3001 | release_console_sem(); | 3110 | release_console_sem(); |
3111 | return count; | ||
3112 | } | ||
3002 | 3113 | ||
3003 | for (i = 0; i < MAX_NR_CONSOLES; i++) | 3114 | static ssize_t store_rotate_all(struct class_device *class_device, |
3004 | con2fb_map[i] = -1; | 3115 | const char *buf, size_t count) |
3116 | { | ||
3117 | struct fb_info *info; | ||
3118 | int rotate, idx; | ||
3119 | char **last = NULL; | ||
3120 | |||
3121 | if (fbcon_has_exited) | ||
3122 | return count; | ||
3123 | |||
3124 | acquire_console_sem(); | ||
3125 | idx = con2fb_map[fg_console]; | ||
3126 | |||
3127 | if (idx == -1 || registered_fb[idx] == NULL) | ||
3128 | goto err; | ||
3005 | 3129 | ||
3130 | info = registered_fb[idx]; | ||
3131 | rotate = simple_strtoul(buf, last, 0); | ||
3132 | fbcon_rotate_all(info, rotate); | ||
3133 | err: | ||
3134 | release_console_sem(); | ||
3135 | return count; | ||
3136 | } | ||
3137 | |||
3138 | static ssize_t show_rotate(struct class_device *class_device, char *buf) | ||
3139 | { | ||
3140 | struct fb_info *info; | ||
3141 | int rotate = 0, idx; | ||
3142 | |||
3143 | if (fbcon_has_exited) | ||
3144 | return 0; | ||
3145 | |||
3146 | acquire_console_sem(); | ||
3147 | idx = con2fb_map[fg_console]; | ||
3148 | |||
3149 | if (idx == -1 || registered_fb[idx] == NULL) | ||
3150 | goto err; | ||
3151 | |||
3152 | info = registered_fb[idx]; | ||
3153 | rotate = fbcon_get_rotate(info); | ||
3154 | err: | ||
3155 | release_console_sem(); | ||
3156 | return snprintf(buf, PAGE_SIZE, "%d\n", rotate); | ||
3157 | } | ||
3158 | |||
3159 | static struct class_device_attribute class_device_attrs[] = { | ||
3160 | __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate), | ||
3161 | __ATTR(rotate_all, S_IWUSR, NULL, store_rotate_all), | ||
3162 | }; | ||
3163 | |||
3164 | static int fbcon_init_class_device(void) | ||
3165 | { | ||
3166 | int i; | ||
3167 | |||
3168 | for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++) | ||
3169 | class_device_create_file(fbcon_class_device, | ||
3170 | &class_device_attrs[i]); | ||
3171 | return 0; | ||
3172 | } | ||
3173 | |||
3174 | static void fbcon_start(void) | ||
3175 | { | ||
3006 | if (num_registered_fb) { | 3176 | if (num_registered_fb) { |
3177 | int i; | ||
3178 | |||
3179 | acquire_console_sem(); | ||
3180 | |||
3007 | for (i = 0; i < FB_MAX; i++) { | 3181 | for (i = 0; i < FB_MAX; i++) { |
3008 | if (registered_fb[i] != NULL) { | 3182 | if (registered_fb[i] != NULL) { |
3009 | info_idx = i; | 3183 | info_idx = i; |
3010 | break; | 3184 | break; |
3011 | } | 3185 | } |
3012 | } | 3186 | } |
3187 | |||
3188 | release_console_sem(); | ||
3013 | fbcon_takeover(0); | 3189 | fbcon_takeover(0); |
3014 | } | 3190 | } |
3191 | } | ||
3192 | |||
3193 | static void fbcon_exit(void) | ||
3194 | { | ||
3195 | struct fb_info *info; | ||
3196 | int i, j, mapped; | ||
3197 | |||
3198 | if (fbcon_has_exited) | ||
3199 | return; | ||
3200 | |||
3201 | #ifdef CONFIG_ATARI | ||
3202 | free_irq(IRQ_AUTO_4, fbcon_vbl_handler); | ||
3203 | #endif | ||
3204 | #ifdef CONFIG_MAC | ||
3205 | if (MACH_IS_MAC && vbl_detected) | ||
3206 | free_irq(IRQ_MAC_VBL, fbcon_vbl_handler); | ||
3207 | #endif | ||
3208 | |||
3209 | kfree((void *)softback_buf); | ||
3210 | softback_buf = 0UL; | ||
3211 | |||
3212 | for (i = 0; i < FB_MAX; i++) { | ||
3213 | mapped = 0; | ||
3214 | info = registered_fb[i]; | ||
3215 | |||
3216 | if (info == NULL) | ||
3217 | continue; | ||
3218 | |||
3219 | for (j = first_fb_vc; j <= last_fb_vc; j++) { | ||
3220 | if (con2fb_map[j] == i) | ||
3221 | mapped = 1; | ||
3222 | } | ||
3223 | |||
3224 | if (mapped) { | ||
3225 | if (info->fbops->fb_release) | ||
3226 | info->fbops->fb_release(info, 0); | ||
3227 | module_put(info->fbops->owner); | ||
3228 | |||
3229 | if (info->fbcon_par) { | ||
3230 | fbcon_del_cursor_timer(info); | ||
3231 | kfree(info->fbcon_par); | ||
3232 | info->fbcon_par = NULL; | ||
3233 | } | ||
3015 | 3234 | ||
3235 | if (info->queue.func == fb_flashcursor) | ||
3236 | info->queue.func = NULL; | ||
3237 | } | ||
3238 | } | ||
3239 | |||
3240 | fbcon_has_exited = 1; | ||
3241 | } | ||
3242 | |||
3243 | static int __init fb_console_init(void) | ||
3244 | { | ||
3245 | int i; | ||
3246 | |||
3247 | acquire_console_sem(); | ||
3248 | fb_register_client(&fbcon_event_notifier); | ||
3249 | fbcon_class_device = | ||
3250 | class_device_create(fb_class, NULL, MKDEV(0, 0), NULL, "fbcon"); | ||
3251 | |||
3252 | if (IS_ERR(fbcon_class_device)) { | ||
3253 | printk(KERN_WARNING "Unable to create class_device " | ||
3254 | "for fbcon; errno = %ld\n", | ||
3255 | PTR_ERR(fbcon_class_device)); | ||
3256 | fbcon_class_device = NULL; | ||
3257 | } else | ||
3258 | fbcon_init_class_device(); | ||
3259 | |||
3260 | for (i = 0; i < MAX_NR_CONSOLES; i++) | ||
3261 | con2fb_map[i] = -1; | ||
3262 | |||
3263 | release_console_sem(); | ||
3264 | fbcon_start(); | ||
3016 | return 0; | 3265 | return 0; |
3017 | } | 3266 | } |
3018 | 3267 | ||
@@ -3020,12 +3269,24 @@ module_init(fb_console_init); | |||
3020 | 3269 | ||
3021 | #ifdef MODULE | 3270 | #ifdef MODULE |
3022 | 3271 | ||
3272 | static void __exit fbcon_deinit_class_device(void) | ||
3273 | { | ||
3274 | int i; | ||
3275 | |||
3276 | for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++) | ||
3277 | class_device_remove_file(fbcon_class_device, | ||
3278 | &class_device_attrs[i]); | ||
3279 | } | ||
3280 | |||
3023 | static void __exit fb_console_exit(void) | 3281 | static void __exit fb_console_exit(void) |
3024 | { | 3282 | { |
3025 | acquire_console_sem(); | 3283 | acquire_console_sem(); |
3026 | fb_unregister_client(&fbcon_event_notifier); | 3284 | fb_unregister_client(&fbcon_event_notifier); |
3285 | fbcon_deinit_class_device(); | ||
3286 | class_device_destroy(fb_class, MKDEV(0, 0)); | ||
3287 | fbcon_exit(); | ||
3027 | release_console_sem(); | 3288 | release_console_sem(); |
3028 | give_up_console(&fb_con); | 3289 | unregister_con_driver(&fb_con); |
3029 | } | 3290 | } |
3030 | 3291 | ||
3031 | module_exit(fb_console_exit); | 3292 | module_exit(fb_console_exit); |