hc
2024-01-05 071106ecf68c401173c58808b1cf5f68cc50d390
kernel/drivers/video/fbdev/core/fbcon.c
....@@ -76,6 +76,7 @@
7676 #include <linux/init.h>
7777 #include <linux/interrupt.h>
7878 #include <linux/crc32.h> /* For counting font checksums */
79
+#include <linux/uaccess.h>
7980 #include <asm/fb.h>
8081 #include <asm/irq.h>
8182
....@@ -87,13 +88,32 @@
8788 # define DPRINTK(fmt, args...)
8889 #endif
8990
91
+/*
92
+ * FIXME: Locking
93
+ *
94
+ * - fbcon state itself is protected by the console_lock, and the code does a
95
+ * pretty good job at making sure that lock is held everywhere it's needed.
96
+ *
97
+ * - access to the registered_fb array is entirely unprotected. This should use
98
+ * proper object lifetime handling, i.e. get/put_fb_info. This also means
99
+ * switching from indices to proper pointers for fb_info everywhere.
100
+ *
101
+ * - fbcon doesn't bother with fb_lock/unlock at all. This is buggy, since it
102
+ * means concurrent access to the same fbdev from both fbcon and userspace
103
+ * will blow up. To fix this all fbcon calls from fbmem.c need to be moved out
104
+ * of fb_lock/unlock protected sections, since otherwise we'll recurse and
105
+ * deadlock eventually. Aside: Due to these deadlock issues the fbdev code in
106
+ * fbmem.c cannot use locking asserts, and there's lots of callers which get
107
+ * the rules wrong, e.g. fbsysfs.c entirely missed fb_lock/unlock calls too.
108
+ */
109
+
90110 enum {
91111 FBCON_LOGO_CANSHOW = -1, /* the logo can be shown */
92112 FBCON_LOGO_DRAW = -2, /* draw the logo to a console */
93113 FBCON_LOGO_DONTSHOW = -3 /* do not show the logo */
94114 };
95115
96
-static struct display fb_display[MAX_NR_CONSOLES];
116
+static struct fbcon_display fb_display[MAX_NR_CONSOLES];
97117
98118 static signed char con2fb_map[MAX_NR_CONSOLES];
99119 static signed char con2fb_map_boot[MAX_NR_CONSOLES];
....@@ -103,10 +123,9 @@
103123 enums. */
104124 static int logo_shown = FBCON_LOGO_CANSHOW;
105125 /* console mappings */
106
-static int first_fb_vc;
107
-static int last_fb_vc = MAX_NR_CONSOLES - 1;
126
+static unsigned int first_fb_vc;
127
+static unsigned int last_fb_vc = MAX_NR_CONSOLES - 1;
108128 static int fbcon_is_default = 1;
109
-static int fbcon_has_exited;
110129 static int primary_device = -1;
111130 static int fbcon_has_console_bind;
112131
....@@ -144,8 +163,6 @@
144163
145164 #define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row)
146165
147
-static int fbcon_set_origin(struct vc_data *);
148
-
149166 static int fbcon_cursor_noblink;
150167
151168 #define divides(a, b) ((!(a) || (b)%(a)) ? 0 : 1)
....@@ -177,11 +194,11 @@
177194 static __inline__ void ywrap_down(struct vc_data *vc, int count);
178195 static __inline__ void ypan_up(struct vc_data *vc, int count);
179196 static __inline__ void ypan_down(struct vc_data *vc, int count);
180
-static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx,
197
+static void fbcon_bmove_rec(struct vc_data *vc, struct fbcon_display *p, int sy, int sx,
181198 int dy, int dx, int height, int width, u_int y_break);
182199 static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,
183200 int unit);
184
-static void fbcon_redraw_move(struct vc_data *vc, struct display *p,
201
+static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p,
185202 int line, int count, int dy);
186203 static void fbcon_modechanged(struct fb_info *info);
187204 static void fbcon_set_all_vcs(struct fb_info *info);
....@@ -212,7 +229,7 @@
212229 fb_info = registered_fb[con2fb_map[ops->currcon]];
213230
214231 if (info == fb_info) {
215
- struct display *p = &fb_display[ops->currcon];
232
+ struct fbcon_display *p = &fb_display[ops->currcon];
216233
217234 if (rotate < 4)
218235 p->con_rotate = rotate;
....@@ -227,7 +244,7 @@
227244 {
228245 struct fbcon_ops *ops = info->fbcon_par;
229246 struct vc_data *vc;
230
- struct display *p;
247
+ struct fbcon_display *p;
231248 int i;
232249
233250 if (!ops || ops->currcon < 0 || rotate > 3)
....@@ -276,8 +293,7 @@
276293 struct fbcon_ops *ops = info->fbcon_par;
277294
278295 return (info->state != FBINFO_STATE_RUNNING ||
279
- vc->vc_mode != KD_TEXT || ops->graphics) &&
280
- !vt_force_oops_output(vc);
296
+ vc->vc_mode != KD_TEXT || ops->graphics);
281297 }
282298
283299 static int get_color(struct vc_data *vc, struct fb_info *info,
....@@ -456,10 +472,12 @@
456472 options += 3;
457473 if (*options)
458474 first_fb_vc = simple_strtoul(options, &options, 10) - 1;
459
- if (first_fb_vc < 0)
475
+ if (first_fb_vc >= MAX_NR_CONSOLES)
460476 first_fb_vc = 0;
461477 if (*options++ == '-')
462478 last_fb_vc = simple_strtoul(options, &options, 10) - 1;
479
+ if (last_fb_vc < first_fb_vc || last_fb_vc >= MAX_NR_CONSOLES)
480
+ last_fb_vc = MAX_NR_CONSOLES - 1;
463481 fbcon_is_default = 0;
464482 continue;
465483 }
....@@ -485,6 +503,20 @@
485503 continue;
486504 }
487505 #endif
506
+
507
+ if (!strncmp(options, "logo-pos:", 9)) {
508
+ options += 9;
509
+ if (!strcmp(options, "center"))
510
+ fb_center_logo = true;
511
+ continue;
512
+ }
513
+
514
+ if (!strncmp(options, "logo-count:", 11)) {
515
+ options += 11;
516
+ if (*options)
517
+ fb_logo_count = simple_strtol(options, &options, 0);
518
+ continue;
519
+ }
488520 }
489521 return 1;
490522 }
....@@ -577,11 +609,11 @@
577609 if (scr_readw(r) != vc->vc_video_erase_char)
578610 break;
579611 if (r != q && new_rows >= rows + logo_lines) {
580
- save = kmalloc(array3_size(logo_lines, new_cols, 2),
612
+ save = kzalloc(array3_size(logo_lines, new_cols, 2),
581613 GFP_KERNEL);
582614 if (save) {
583615 int i = cols < new_cols ? cols : new_cols;
584
- scr_memsetw(save, erase, logo_lines * new_cols * 2);
616
+ scr_memsetw(save, erase, array3_size(logo_lines, new_cols, 2));
585617 r = q - step;
586618 for (cnt = 0; cnt < logo_lines; cnt++, r += i)
587619 scr_memcpyw(save + cnt * new_cols, r, 2 * i);
....@@ -597,11 +629,11 @@
597629 }
598630 if (!save) {
599631 int lines;
600
- if (vc->vc_y + logo_lines >= rows)
601
- lines = rows - vc->vc_y - 1;
632
+ if (vc->state.y + logo_lines >= rows)
633
+ lines = rows - vc->state.y - 1;
602634 else
603635 lines = logo_lines;
604
- vc->vc_y += lines;
636
+ vc->state.y += lines;
605637 vc->vc_pos += lines * vc->vc_size_row;
606638 }
607639 }
....@@ -618,17 +650,20 @@
618650 q = (unsigned short *) (vc->vc_origin +
619651 vc->vc_size_row *
620652 rows);
621
- scr_memcpyw(q, save, logo_lines * new_cols * 2);
622
- vc->vc_y += logo_lines;
653
+ scr_memcpyw(q, save, array3_size(logo_lines, new_cols, 2));
654
+ vc->state.y += logo_lines;
623655 vc->vc_pos += logo_lines * vc->vc_size_row;
624656 kfree(save);
625657 }
658
+
659
+ if (logo_shown == FBCON_LOGO_DONTSHOW)
660
+ return;
626661
627662 if (logo_lines > vc->vc_bottom) {
628663 logo_shown = FBCON_LOGO_CANSHOW;
629664 printk(KERN_INFO
630665 "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n");
631
- } else if (logo_shown != FBCON_LOGO_DONTSHOW) {
666
+ } else {
632667 logo_shown = FBCON_LOGO_DRAW;
633668 vc->vc_top = logo_lines;
634669 }
....@@ -812,7 +847,7 @@
812847 int oldidx = con2fb_map[unit];
813848 struct fb_info *info = registered_fb[newidx];
814849 struct fb_info *oldinfo = NULL;
815
- int found, err = 0;
850
+ int found, err = 0;
816851
817852 WARN_CONSOLE_UNLOCKED();
818853
....@@ -834,38 +869,37 @@
834869
835870 con2fb_map[unit] = newidx;
836871 if (!err && !found)
837
- err = con2fb_acquire_newinfo(vc, info, unit, oldidx);
838
-
872
+ err = con2fb_acquire_newinfo(vc, info, unit, oldidx);
839873
840874 /*
841875 * If old fb is not mapped to any of the consoles,
842876 * fbcon should release it.
843877 */
844
- if (!err && oldinfo && !search_fb_in_map(oldidx))
845
- err = con2fb_release_oldinfo(vc, oldinfo, info, unit, oldidx,
846
- found);
878
+ if (!err && oldinfo && !search_fb_in_map(oldidx))
879
+ err = con2fb_release_oldinfo(vc, oldinfo, info, unit, oldidx,
880
+ found);
847881
848
- if (!err) {
849
- int show_logo = (fg_console == 0 && !user &&
850
- logo_shown != FBCON_LOGO_DONTSHOW);
882
+ if (!err) {
883
+ int show_logo = (fg_console == 0 && !user &&
884
+ logo_shown != FBCON_LOGO_DONTSHOW);
851885
852
- if (!found)
853
- fbcon_add_cursor_timer(info);
854
- con2fb_map_boot[unit] = newidx;
855
- con2fb_init_display(vc, info, unit, show_logo);
886
+ if (!found)
887
+ fbcon_add_cursor_timer(info);
888
+ con2fb_map_boot[unit] = newidx;
889
+ con2fb_init_display(vc, info, unit, show_logo);
856890 }
857891
858892 if (!search_fb_in_map(info_idx))
859893 info_idx = newidx;
860894
861
- return err;
895
+ return err;
862896 }
863897
864898 /*
865899 * Low Level Operations
866900 */
867901 /* NOTE: fbcon cannot be __init: it may be called from do_take_over_console later */
868
-static int var_to_display(struct display *disp,
902
+static int var_to_display(struct fbcon_display *disp,
869903 struct fb_var_screeninfo *var,
870904 struct fb_info *info)
871905 {
....@@ -890,7 +924,7 @@
890924 }
891925
892926 static void display_to_var(struct fb_var_screeninfo *var,
893
- struct display *disp)
927
+ struct fbcon_display *disp)
894928 {
895929 fb_videomode_to_var(var, disp->mode);
896930 var->xres_virtual = disp->xres_virtual;
....@@ -911,7 +945,7 @@
911945 static const char *fbcon_startup(void)
912946 {
913947 const char *display_desc = "frame buffer device";
914
- struct display *p = &fb_display[fg_console];
948
+ struct fbcon_display *p = &fb_display[fg_console];
915949 struct vc_data *vc = vc_cons[fg_console].d;
916950 const struct font_desc *font = NULL;
917951 struct module *owner;
....@@ -990,24 +1024,30 @@
9901024 info->var.bits_per_pixel);
9911025
9921026 fbcon_add_cursor_timer(info);
993
- fbcon_has_exited = 0;
9941027 return display_desc;
9951028 }
9961029
9971030 static void fbcon_init(struct vc_data *vc, int init)
9981031 {
999
- struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1032
+ struct fb_info *info;
10001033 struct fbcon_ops *ops;
10011034 struct vc_data **default_mode = vc->vc_display_fg;
10021035 struct vc_data *svc = *default_mode;
1003
- struct display *t, *p = &fb_display[vc->vc_num];
1036
+ struct fbcon_display *t, *p = &fb_display[vc->vc_num];
10041037 int logo = 1, new_rows, new_cols, rows, cols, charcnt = 256;
10051038 int cap, ret;
10061039
1007
- if (info_idx == -1 || info == NULL)
1040
+ if (WARN_ON(info_idx == -1))
10081041 return;
10091042
1043
+ if (con2fb_map[vc->vc_num] == -1)
1044
+ con2fb_map[vc->vc_num] = info_idx;
1045
+
1046
+ info = registered_fb[con2fb_map[vc->vc_num]];
10101047 cap = info->flags;
1048
+
1049
+ if (logo_shown < 0 && console_loglevel <= CONSOLE_LOGLEVEL_QUIET)
1050
+ logo_shown = FBCON_LOGO_DONTSHOW;
10111051
10121052 if (vc != svc || logo_shown == FBCON_LOGO_DONTSHOW ||
10131053 (info->fix.type == FB_TYPE_TEXT))
....@@ -1053,7 +1093,6 @@
10531093 if (p->userfont)
10541094 charcnt = FNTCHARCNT(p->fontdata);
10551095
1056
- vc->vc_panic_force_write = !!(info->flags & FBINFO_CAN_FORCE_OUTPUT);
10571096 vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
10581097 vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
10591098 if (charcnt == 256) {
....@@ -1110,11 +1149,13 @@
11101149
11111150 ops->graphics = 0;
11121151
1152
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION
11131153 if ((cap & FBINFO_HWACCEL_COPYAREA) &&
11141154 !(cap & FBINFO_HWACCEL_DISABLED))
11151155 p->scrollmode = SCROLL_MOVE;
11161156 else /* default to something safe */
11171157 p->scrollmode = SCROLL_REDRAW;
1158
+#endif
11181159
11191160 /*
11201161 * ++guenther: console.c:vc_allocate() relies on initializing
....@@ -1138,7 +1179,7 @@
11381179 ops->p = &fb_display[fg_console];
11391180 }
11401181
1141
-static void fbcon_free_font(struct display *p, bool freefont)
1182
+static void fbcon_free_font(struct fbcon_display *p, bool freefont)
11421183 {
11431184 if (freefont && p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0))
11441185 kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int));
....@@ -1150,7 +1191,7 @@
11501191
11511192 static void fbcon_deinit(struct vc_data *vc)
11521193 {
1153
- struct display *p = &fb_display[vc->vc_num];
1194
+ struct fbcon_display *p = &fb_display[vc->vc_num];
11541195 struct fb_info *info;
11551196 struct fbcon_ops *ops;
11561197 int idx;
....@@ -1226,7 +1267,7 @@
12261267 struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
12271268 struct fbcon_ops *ops = info->fbcon_par;
12281269
1229
- struct display *p = &fb_display[vc->vc_num];
1270
+ struct fbcon_display *p = &fb_display[vc->vc_num];
12301271 u_int y_break;
12311272
12321273 if (fbcon_is_inactive(vc, info))
....@@ -1262,7 +1303,7 @@
12621303 int count, int ypos, int xpos)
12631304 {
12641305 struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1265
- struct display *p = &fb_display[vc->vc_num];
1306
+ struct fbcon_display *p = &fb_display[vc->vc_num];
12661307 struct fbcon_ops *ops = info->fbcon_par;
12671308
12681309 if (!fbcon_is_inactive(vc, info))
....@@ -1299,7 +1340,7 @@
12991340 if (fbcon_is_inactive(vc, info) || vc->vc_deccm != 1)
13001341 return;
13011342
1302
- if (vc->vc_cursor_type & 0x10)
1343
+ if (vc->vc_cursor_type & CUR_SW)
13031344 fbcon_del_cursor_timer(info);
13041345 else
13051346 fbcon_add_cursor_timer(info);
....@@ -1320,7 +1361,7 @@
13201361 static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,
13211362 int unit)
13221363 {
1323
- struct display *p, *t;
1364
+ struct fbcon_display *p, *t;
13241365 struct vc_data **default_mode, *vc;
13251366 struct vc_data *svc;
13261367 struct fbcon_ops *ops = info->fbcon_par;
....@@ -1387,7 +1428,7 @@
13871428 {
13881429 struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
13891430 struct fbcon_ops *ops = info->fbcon_par;
1390
- struct display *p = &fb_display[vc->vc_num];
1431
+ struct fbcon_display *p = &fb_display[vc->vc_num];
13911432
13921433 p->yscroll += count;
13931434 if (p->yscroll >= p->vrows) /* Deal with wrap */
....@@ -1406,7 +1447,7 @@
14061447 {
14071448 struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
14081449 struct fbcon_ops *ops = info->fbcon_par;
1409
- struct display *p = &fb_display[vc->vc_num];
1450
+ struct fbcon_display *p = &fb_display[vc->vc_num];
14101451
14111452 p->yscroll -= count;
14121453 if (p->yscroll < 0) /* Deal with wrap */
....@@ -1424,7 +1465,7 @@
14241465 static __inline__ void ypan_up(struct vc_data *vc, int count)
14251466 {
14261467 struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1427
- struct display *p = &fb_display[vc->vc_num];
1468
+ struct fbcon_display *p = &fb_display[vc->vc_num];
14281469 struct fbcon_ops *ops = info->fbcon_par;
14291470
14301471 p->yscroll += count;
....@@ -1449,7 +1490,7 @@
14491490 {
14501491 struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
14511492 struct fbcon_ops *ops = info->fbcon_par;
1452
- struct display *p = &fb_display[vc->vc_num];
1493
+ struct fbcon_display *p = &fb_display[vc->vc_num];
14531494
14541495 p->yscroll += count;
14551496
....@@ -1472,7 +1513,7 @@
14721513 static __inline__ void ypan_down(struct vc_data *vc, int count)
14731514 {
14741515 struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1475
- struct display *p = &fb_display[vc->vc_num];
1516
+ struct fbcon_display *p = &fb_display[vc->vc_num];
14761517 struct fbcon_ops *ops = info->fbcon_par;
14771518
14781519 p->yscroll -= count;
....@@ -1497,7 +1538,7 @@
14971538 {
14981539 struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
14991540 struct fbcon_ops *ops = info->fbcon_par;
1500
- struct display *p = &fb_display[vc->vc_num];
1541
+ struct fbcon_display *p = &fb_display[vc->vc_num];
15011542
15021543 p->yscroll -= count;
15031544
....@@ -1517,7 +1558,7 @@
15171558 scrollback_current = 0;
15181559 }
15191560
1520
-static void fbcon_redraw_move(struct vc_data *vc, struct display *p,
1561
+static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p,
15211562 int line, int count, int dy)
15221563 {
15231564 unsigned short *s = (unsigned short *)
....@@ -1552,7 +1593,7 @@
15521593 }
15531594
15541595 static void fbcon_redraw_blit(struct vc_data *vc, struct fb_info *info,
1555
- struct display *p, int line, int count, int ycount)
1596
+ struct fbcon_display *p, int line, int count, int ycount)
15561597 {
15571598 int offset = ycount * vc->vc_cols;
15581599 unsigned short *d = (unsigned short *)
....@@ -1601,7 +1642,7 @@
16011642 }
16021643 }
16031644
1604
-static void fbcon_redraw(struct vc_data *vc, struct display *p,
1645
+static void fbcon_redraw(struct vc_data *vc, struct fbcon_display *p,
16051646 int line, int count, int offset)
16061647 {
16071648 unsigned short *d = (unsigned short *)
....@@ -1660,7 +1701,7 @@
16601701 enum con_scroll dir, unsigned int count)
16611702 {
16621703 struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1663
- struct display *p = &fb_display[vc->vc_num];
1704
+ struct fbcon_display *p = &fb_display[vc->vc_num];
16641705 int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK;
16651706
16661707 if (fbcon_is_inactive(vc, info))
....@@ -1678,9 +1719,7 @@
16781719 case SM_UP:
16791720 if (count > vc->vc_rows) /* Maximum realistic size */
16801721 count = vc->vc_rows;
1681
- if (logo_shown >= 0)
1682
- goto redraw_up;
1683
- switch (p->scrollmode) {
1722
+ switch (fb_scrollmode(p)) {
16841723 case SCROLL_MOVE:
16851724 fbcon_redraw_blit(vc, info, p, t, b - t - count,
16861725 count);
....@@ -1691,7 +1730,6 @@
16911730 vc->vc_video_erase_char,
16921731 vc->vc_size_row * count);
16931732 return true;
1694
- break;
16951733
16961734 case SCROLL_WRAP_MOVE:
16971735 if (b - t - count > 3 * vc->vc_rows >> 2) {
....@@ -1769,9 +1807,7 @@
17691807 case SM_DOWN:
17701808 if (count > vc->vc_rows) /* Maximum realistic size */
17711809 count = vc->vc_rows;
1772
- if (logo_shown >= 0)
1773
- goto redraw_down;
1774
- switch (p->scrollmode) {
1810
+ switch (fb_scrollmode(p)) {
17751811 case SCROLL_MOVE:
17761812 fbcon_redraw_blit(vc, info, p, b - 1, b - t - count,
17771813 -count);
....@@ -1782,7 +1818,6 @@
17821818 vc->vc_video_erase_char,
17831819 vc->vc_size_row * count);
17841820 return true;
1785
- break;
17861821
17871822 case SCROLL_WRAP_MOVE:
17881823 if (b - t - count > 3 * vc->vc_rows >> 2) {
....@@ -1862,7 +1897,7 @@
18621897 int height, int width)
18631898 {
18641899 struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1865
- struct display *p = &fb_display[vc->vc_num];
1900
+ struct fbcon_display *p = &fb_display[vc->vc_num];
18661901
18671902 if (fbcon_is_inactive(vc, info))
18681903 return;
....@@ -1881,7 +1916,7 @@
18811916 p->vrows - p->yscroll);
18821917 }
18831918
1884
-static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx,
1919
+static void fbcon_bmove_rec(struct vc_data *vc, struct fbcon_display *p, int sy, int sx,
18851920 int dy, int dx, int height, int width, u_int y_break)
18861921 {
18871922 struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
....@@ -1923,12 +1958,12 @@
19231958 height, width);
19241959 }
19251960
1926
-static void updatescrollmode(struct display *p,
1961
+static void updatescrollmode_accel(struct fbcon_display *p,
19271962 struct fb_info *info,
19281963 struct vc_data *vc)
19291964 {
1965
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION
19301966 struct fbcon_ops *ops = info->fbcon_par;
1931
- int fh = vc->vc_font.height;
19321967 int cap = info->flags;
19331968 u16 t = 0;
19341969 int ypan = FBCON_SWAP(ops->rotate, info->fix.ypanstep,
....@@ -1949,12 +1984,6 @@
19491984 int fast_imageblit = (cap & FBINFO_HWACCEL_IMAGEBLIT) &&
19501985 !(cap & FBINFO_HWACCEL_DISABLED);
19511986
1952
- p->vrows = vyres/fh;
1953
- if (yres > (fh * (vc->vc_rows + 1)))
1954
- p->vrows -= (yres - (fh * vc->vc_rows)) / fh;
1955
- if ((yres % fh) && (vyres % fh < yres % fh))
1956
- p->vrows--;
1957
-
19581987 if (good_wrap || good_pan) {
19591988 if (reading_fast || fast_copyarea)
19601989 p->scrollmode = good_wrap ?
....@@ -1968,6 +1997,27 @@
19681997 else
19691998 p->scrollmode = SCROLL_REDRAW;
19701999 }
2000
+#endif
2001
+}
2002
+
2003
+static void updatescrollmode(struct fbcon_display *p,
2004
+ struct fb_info *info,
2005
+ struct vc_data *vc)
2006
+{
2007
+ struct fbcon_ops *ops = info->fbcon_par;
2008
+ int fh = vc->vc_font.height;
2009
+ int yres = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
2010
+ int vyres = FBCON_SWAP(ops->rotate, info->var.yres_virtual,
2011
+ info->var.xres_virtual);
2012
+
2013
+ p->vrows = vyres/fh;
2014
+ if (yres > (fh * (vc->vc_rows + 1)))
2015
+ p->vrows -= (yres - (fh * vc->vc_rows)) / fh;
2016
+ if ((yres % fh) && (vyres % fh < yres % fh))
2017
+ p->vrows--;
2018
+
2019
+ /* update scrollmode in case hardware acceleration is used */
2020
+ updatescrollmode_accel(p, info, vc);
19712021 }
19722022
19732023 #define PITCH(w) (((w) + 7) >> 3)
....@@ -1978,7 +2028,7 @@
19782028 {
19792029 struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
19802030 struct fbcon_ops *ops = info->fbcon_par;
1981
- struct display *p = &fb_display[vc->vc_num];
2031
+ struct fbcon_display *p = &fb_display[vc->vc_num];
19822032 struct fb_var_screeninfo var = info->var;
19832033 int x_diff, y_diff, virt_w, virt_h, virt_fw, virt_fh;
19842034
....@@ -2041,7 +2091,7 @@
20412091 {
20422092 struct fb_info *info, *old_info = NULL;
20432093 struct fbcon_ops *ops;
2044
- struct display *p = &fb_display[vc->vc_num];
2094
+ struct fbcon_display *p = &fb_display[vc->vc_num];
20452095 struct fb_var_screeninfo var;
20462096 int i, ret, prev_console, charcnt = 256;
20472097
....@@ -2128,7 +2178,7 @@
21282178
21292179 updatescrollmode(p, info, vc);
21302180
2131
- switch (p->scrollmode) {
2181
+ switch (fb_scrollmode(p)) {
21322182 case SCROLL_WRAP_MOVE:
21332183 scrollback_phys_max = p->vrows - vc->vc_rows;
21342184 break;
....@@ -2171,8 +2221,6 @@
21712221 static void fbcon_generic_blank(struct vc_data *vc, struct fb_info *info,
21722222 int blank)
21732223 {
2174
- struct fb_event event;
2175
-
21762224 if (blank) {
21772225 unsigned short charmask = vc->vc_hi_font_mask ?
21782226 0x1ff : 0xff;
....@@ -2183,14 +2231,6 @@
21832231 fbcon_clear(vc, 0, 0, vc->vc_rows, vc->vc_cols);
21842232 vc->vc_video_erase_char = oldc;
21852233 }
2186
-
2187
-
2188
- if (!lock_fb_info(info))
2189
- return;
2190
- event.info = info;
2191
- event.data = &blank;
2192
- fb_notifier_call_chain(FB_EVENT_CONBLANK, &event);
2193
- unlock_fb_info(info);
21942234 }
21952235
21962236 static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
....@@ -2204,7 +2244,8 @@
22042244 ops->graphics = 1;
22052245
22062246 if (!blank) {
2207
- var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
2247
+ var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE |
2248
+ FB_ACTIVATE_KD_TEXT;
22082249 fb_set_var(info, &var);
22092250 ops->graphics = 0;
22102251 ops->var = info->var;
....@@ -2217,9 +2258,8 @@
22172258 fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW);
22182259 ops->cursor_flash = (!blank);
22192260
2220
- if (!(info->flags & FBINFO_MISC_USEREVENT))
2221
- if (fb_blank(info, blank))
2222
- fbcon_generic_blank(vc, info, blank);
2261
+ if (fb_blank(info, blank))
2262
+ fbcon_generic_blank(vc, info, blank);
22232263 }
22242264
22252265 if (!blank)
....@@ -2388,7 +2428,7 @@
23882428 {
23892429 struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
23902430 struct fbcon_ops *ops = info->fbcon_par;
2391
- struct display *p = &fb_display[vc->vc_num];
2431
+ struct fbcon_display *p = &fb_display[vc->vc_num];
23922432 int resize;
23932433 int cnt;
23942434 char *old_data = NULL;
....@@ -2431,7 +2471,7 @@
24312471
24322472 static int fbcon_copy_font(struct vc_data *vc, int con)
24332473 {
2434
- struct display *od = &fb_display[con];
2474
+ struct fbcon_display *od = &fb_display[con];
24352475 struct console_font *f = &vc->vc_font;
24362476
24372477 if (od->fontdata == f->data)
....@@ -2468,9 +2508,17 @@
24682508 if (charcount != 256 && charcount != 512)
24692509 return -EINVAL;
24702510
2511
+ /* font bigger than screen resolution ? */
2512
+ if (w > FBCON_SWAP(info->var.rotate, info->var.xres, info->var.yres) ||
2513
+ h > FBCON_SWAP(info->var.rotate, info->var.yres, info->var.xres))
2514
+ return -EINVAL;
2515
+
2516
+ if (font->width > 32 || font->height > 32)
2517
+ return -EINVAL;
2518
+
24712519 /* Make sure drawing engine can handle the font */
2472
- if (!(info->pixmap.blit_x & (1 << (font->width - 1))) ||
2473
- !(info->pixmap.blit_y & (1 << (font->height - 1))))
2520
+ if (!(info->pixmap.blit_x & BIT(font->width - 1)) ||
2521
+ !(info->pixmap.blit_y & BIT(font->height - 1)))
24742522 return -EINVAL;
24752523
24762524 /* Make sure driver can handle the font length */
....@@ -2574,7 +2622,7 @@
25742622 fb_set_cmap(&palette_cmap, info);
25752623 }
25762624
2577
-static u16 *fbcon_screen_pos(struct vc_data *vc, int offset)
2625
+static u16 *fbcon_screen_pos(const struct vc_data *vc, int offset)
25782626 {
25792627 return (u16 *) (vc->vc_origin + offset);
25802628 }
....@@ -2621,12 +2669,7 @@
26212669 }
26222670 }
26232671
2624
-static int fbcon_set_origin(struct vc_data *vc)
2625
-{
2626
- return 0;
2627
-}
2628
-
2629
-static void fbcon_suspended(struct fb_info *info)
2672
+void fbcon_suspended(struct fb_info *info)
26302673 {
26312674 struct vc_data *vc = NULL;
26322675 struct fbcon_ops *ops = info->fbcon_par;
....@@ -2639,7 +2682,7 @@
26392682 fbcon_cursor(vc, CM_ERASE);
26402683 }
26412684
2642
-static void fbcon_resumed(struct fb_info *info)
2685
+void fbcon_resumed(struct fb_info *info)
26432686 {
26442687 struct vc_data *vc;
26452688 struct fbcon_ops *ops = info->fbcon_par;
....@@ -2655,7 +2698,7 @@
26552698 {
26562699 struct fbcon_ops *ops = info->fbcon_par;
26572700 struct vc_data *vc;
2658
- struct display *p;
2701
+ struct fbcon_display *p;
26592702 int rows, cols;
26602703
26612704 if (!ops || ops->currcon < 0)
....@@ -2693,7 +2736,7 @@
26932736 {
26942737 struct fbcon_ops *ops = info->fbcon_par;
26952738 struct vc_data *vc;
2696
- struct display *p;
2739
+ struct fbcon_display *p;
26972740 int i, rows, cols, fg = -1;
26982741
26992742 if (!ops || ops->currcon < 0)
....@@ -2724,11 +2767,49 @@
27242767 fbcon_modechanged(info);
27252768 }
27262769
2727
-static int fbcon_mode_deleted(struct fb_info *info,
2728
- struct fb_videomode *mode)
2770
+
2771
+void fbcon_update_vcs(struct fb_info *info, bool all)
2772
+{
2773
+ if (all)
2774
+ fbcon_set_all_vcs(info);
2775
+ else
2776
+ fbcon_modechanged(info);
2777
+}
2778
+EXPORT_SYMBOL(fbcon_update_vcs);
2779
+
2780
+/* let fbcon check if it supports a new screen resolution */
2781
+int fbcon_modechange_possible(struct fb_info *info, struct fb_var_screeninfo *var)
2782
+{
2783
+ struct fbcon_ops *ops = info->fbcon_par;
2784
+ struct vc_data *vc;
2785
+ unsigned int i;
2786
+
2787
+ WARN_CONSOLE_UNLOCKED();
2788
+
2789
+ if (!ops)
2790
+ return 0;
2791
+
2792
+ /* prevent setting a screen size which is smaller than font size */
2793
+ for (i = first_fb_vc; i <= last_fb_vc; i++) {
2794
+ vc = vc_cons[i].d;
2795
+ if (!vc || vc->vc_mode != KD_TEXT ||
2796
+ registered_fb[con2fb_map[i]] != info)
2797
+ continue;
2798
+
2799
+ if (vc->vc_font.width > FBCON_SWAP(var->rotate, var->xres, var->yres) ||
2800
+ vc->vc_font.height > FBCON_SWAP(var->rotate, var->yres, var->xres))
2801
+ return -EINVAL;
2802
+ }
2803
+
2804
+ return 0;
2805
+}
2806
+EXPORT_SYMBOL_GPL(fbcon_modechange_possible);
2807
+
2808
+int fbcon_mode_deleted(struct fb_info *info,
2809
+ struct fb_videomode *mode)
27292810 {
27302811 struct fb_info *fb_info;
2731
- struct display *p;
2812
+ struct fbcon_display *p;
27322813 int i, j, found = 0;
27332814
27342815 /* before deletion, ensure that mode is not in use */
....@@ -2751,7 +2832,7 @@
27512832 }
27522833
27532834 #ifdef CONFIG_VT_HW_CONSOLE_BINDING
2754
-static int fbcon_unbind(void)
2835
+static void fbcon_unbind(void)
27552836 {
27562837 int ret;
27572838
....@@ -2760,25 +2841,21 @@
27602841
27612842 if (!ret)
27622843 fbcon_has_console_bind = 0;
2763
-
2764
- return ret;
27652844 }
27662845 #else
2767
-static inline int fbcon_unbind(void)
2768
-{
2769
- return -EINVAL;
2770
-}
2846
+static inline void fbcon_unbind(void) {}
27712847 #endif /* CONFIG_VT_HW_CONSOLE_BINDING */
27722848
27732849 /* called with console_lock held */
2774
-static int fbcon_fb_unbind(int idx)
2850
+void fbcon_fb_unbind(struct fb_info *info)
27752851 {
27762852 int i, new_idx = -1, ret = 0;
2853
+ int idx = info->node;
27772854
27782855 WARN_CONSOLE_UNLOCKED();
27792856
27802857 if (!fbcon_has_console_bind)
2781
- return 0;
2858
+ return;
27822859
27832860 for (i = first_fb_vc; i <= last_fb_vc; i++) {
27842861 if (con2fb_map[i] != idx &&
....@@ -2811,26 +2888,24 @@
28112888 idx, 0);
28122889 if (ret) {
28132890 con2fb_map[i] = idx;
2814
- return ret;
2891
+ return;
28152892 }
28162893 }
28172894 }
28182895 }
2819
- ret = fbcon_unbind();
2896
+ fbcon_unbind();
28202897 }
2821
-
2822
- return ret;
28232898 }
28242899
28252900 /* called with console_lock held */
2826
-static int fbcon_fb_unregistered(struct fb_info *info)
2901
+void fbcon_fb_unregistered(struct fb_info *info)
28272902 {
28282903 int i, idx;
28292904
28302905 WARN_CONSOLE_UNLOCKED();
28312906
28322907 if (deferred_takeover)
2833
- return 0;
2908
+ return;
28342909
28352910 idx = info->node;
28362911 for (i = first_fb_vc; i <= last_fb_vc; i++) {
....@@ -2859,21 +2934,18 @@
28592934
28602935 if (!num_registered_fb)
28612936 do_unregister_con_driver(&fb_con);
2862
-
2863
- return 0;
28642937 }
28652938
2866
-/* called with console_lock held */
2867
-static void fbcon_remap_all(int idx)
2939
+void fbcon_remap_all(struct fb_info *info)
28682940 {
2869
- int i;
2941
+ int i, idx = info->node;
28702942
2871
- WARN_CONSOLE_UNLOCKED();
2872
-
2943
+ console_lock();
28732944 if (deferred_takeover) {
28742945 for (i = first_fb_vc; i <= last_fb_vc; i++)
28752946 con2fb_map_boot[i] = idx;
28762947 fbcon_map_override();
2948
+ console_unlock();
28772949 return;
28782950 }
28792951
....@@ -2886,6 +2958,7 @@
28862958 first_fb_vc + 1, last_fb_vc + 1);
28872959 info_idx = idx;
28882960 }
2961
+ console_unlock();
28892962 }
28902963
28912964 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY
....@@ -2919,7 +2992,7 @@
29192992 #endif /* CONFIG_FRAMEBUFFER_DETECT_PRIMARY */
29202993
29212994 /* called with console_lock held */
2922
-static int fbcon_fb_registered(struct fb_info *info)
2995
+int fbcon_fb_registered(struct fb_info *info)
29232996 {
29242997 int ret = 0, i, idx;
29252998
....@@ -2953,7 +3026,7 @@
29533026 return ret;
29543027 }
29553028
2956
-static void fbcon_fb_blanked(struct fb_info *info, int blank)
3029
+void fbcon_fb_blanked(struct fb_info *info, int blank)
29573030 {
29583031 struct fbcon_ops *ops = info->fbcon_par;
29593032 struct vc_data *vc;
....@@ -2975,7 +3048,7 @@
29753048 ops->blank_state = blank;
29763049 }
29773050
2978
-static void fbcon_new_modelist(struct fb_info *info)
3051
+void fbcon_new_modelist(struct fb_info *info)
29793052 {
29803053 int i;
29813054 struct vc_data *vc;
....@@ -2996,11 +3069,11 @@
29963069 }
29973070 }
29983071
2999
-static void fbcon_get_requirement(struct fb_info *info,
3000
- struct fb_blit_caps *caps)
3072
+void fbcon_get_requirement(struct fb_info *info,
3073
+ struct fb_blit_caps *caps)
30013074 {
30023075 struct vc_data *vc;
3003
- struct display *p;
3076
+ struct fbcon_display *p;
30043077
30053078 if (caps->flags) {
30063079 int i, charcnt;
....@@ -3032,78 +3105,45 @@
30323105 }
30333106 }
30343107
3035
-static int fbcon_event_notify(struct notifier_block *self,
3036
- unsigned long action, void *data)
3108
+int fbcon_set_con2fb_map_ioctl(void __user *argp)
30373109 {
3038
- struct fb_event *event = data;
3039
- struct fb_info *info = event->info;
3040
- struct fb_videomode *mode;
3041
- struct fb_con2fbmap *con2fb;
3042
- struct fb_blit_caps *caps;
3043
- int idx, ret = 0;
3110
+ struct fb_con2fbmap con2fb;
3111
+ int ret;
30443112
3045
- /*
3046
- * ignore all events except driver registration and deregistration
3047
- * if fbcon is not active
3048
- */
3049
- if (fbcon_has_exited && !(action == FB_EVENT_FB_REGISTERED ||
3050
- action == FB_EVENT_FB_UNREGISTERED))
3051
- goto done;
3052
-
3053
- switch(action) {
3054
- case FB_EVENT_SUSPEND:
3055
- fbcon_suspended(info);
3056
- break;
3057
- case FB_EVENT_RESUME:
3058
- fbcon_resumed(info);
3059
- break;
3060
- case FB_EVENT_MODE_CHANGE:
3061
- fbcon_modechanged(info);
3062
- break;
3063
- case FB_EVENT_MODE_CHANGE_ALL:
3064
- fbcon_set_all_vcs(info);
3065
- break;
3066
- case FB_EVENT_MODE_DELETE:
3067
- mode = event->data;
3068
- ret = fbcon_mode_deleted(info, mode);
3069
- break;
3070
- case FB_EVENT_FB_UNBIND:
3071
- idx = info->node;
3072
- ret = fbcon_fb_unbind(idx);
3073
- break;
3074
- case FB_EVENT_FB_REGISTERED:
3075
- ret = fbcon_fb_registered(info);
3076
- break;
3077
- case FB_EVENT_FB_UNREGISTERED:
3078
- ret = fbcon_fb_unregistered(info);
3079
- break;
3080
- case FB_EVENT_SET_CONSOLE_MAP:
3081
- /* called with console lock held */
3082
- con2fb = event->data;
3083
- ret = set_con2fb_map(con2fb->console - 1,
3084
- con2fb->framebuffer, 1);
3085
- break;
3086
- case FB_EVENT_GET_CONSOLE_MAP:
3087
- con2fb = event->data;
3088
- con2fb->framebuffer = con2fb_map[con2fb->console - 1];
3089
- break;
3090
- case FB_EVENT_BLANK:
3091
- fbcon_fb_blanked(info, *(int *)event->data);
3092
- break;
3093
- case FB_EVENT_NEW_MODELIST:
3094
- fbcon_new_modelist(info);
3095
- break;
3096
- case FB_EVENT_GET_REQ:
3097
- caps = event->data;
3098
- fbcon_get_requirement(info, caps);
3099
- break;
3100
- case FB_EVENT_REMAP_ALL_CONSOLE:
3101
- idx = info->node;
3102
- fbcon_remap_all(idx);
3103
- break;
3113
+ if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
3114
+ return -EFAULT;
3115
+ if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
3116
+ return -EINVAL;
3117
+ if (con2fb.framebuffer >= FB_MAX)
3118
+ return -EINVAL;
3119
+ if (!registered_fb[con2fb.framebuffer])
3120
+ request_module("fb%d", con2fb.framebuffer);
3121
+ if (!registered_fb[con2fb.framebuffer]) {
3122
+ return -EINVAL;
31043123 }
3105
-done:
3124
+
3125
+ console_lock();
3126
+ ret = set_con2fb_map(con2fb.console - 1,
3127
+ con2fb.framebuffer, 1);
3128
+ console_unlock();
3129
+
31063130 return ret;
3131
+}
3132
+
3133
+int fbcon_get_con2fb_map_ioctl(void __user *argp)
3134
+{
3135
+ struct fb_con2fbmap con2fb;
3136
+
3137
+ if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
3138
+ return -EFAULT;
3139
+ if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
3140
+ return -EINVAL;
3141
+
3142
+ console_lock();
3143
+ con2fb.framebuffer = con2fb_map[con2fb.console - 1];
3144
+ console_unlock();
3145
+
3146
+ return copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;
31073147 }
31083148
31093149 /*
....@@ -3127,17 +3167,12 @@
31273167 .con_font_default = fbcon_set_def_font,
31283168 .con_font_copy = fbcon_copy_font,
31293169 .con_set_palette = fbcon_set_palette,
3130
- .con_set_origin = fbcon_set_origin,
31313170 .con_invert_region = fbcon_invert_region,
31323171 .con_screen_pos = fbcon_screen_pos,
31333172 .con_getxy = fbcon_getxy,
31343173 .con_resize = fbcon_resize,
31353174 .con_debug_enter = fbcon_debug_enter,
31363175 .con_debug_leave = fbcon_debug_leave,
3137
-};
3138
-
3139
-static struct notifier_block fbcon_event_notifier = {
3140
- .notifier_call = fbcon_event_notify,
31413176 };
31423177
31433178 static ssize_t store_rotate(struct device *device,
....@@ -3147,9 +3182,6 @@
31473182 struct fb_info *info;
31483183 int rotate, idx;
31493184 char **last = NULL;
3150
-
3151
- if (fbcon_has_exited)
3152
- return count;
31533185
31543186 console_lock();
31553187 idx = con2fb_map[fg_console];
....@@ -3173,9 +3205,6 @@
31733205 int rotate, idx;
31743206 char **last = NULL;
31753207
3176
- if (fbcon_has_exited)
3177
- return count;
3178
-
31793208 console_lock();
31803209 idx = con2fb_map[fg_console];
31813210
....@@ -3196,9 +3225,6 @@
31963225 struct fb_info *info;
31973226 int rotate = 0, idx;
31983227
3199
- if (fbcon_has_exited)
3200
- return 0;
3201
-
32023228 console_lock();
32033229 idx = con2fb_map[fg_console];
32043230
....@@ -3218,9 +3244,6 @@
32183244 struct fb_info *info;
32193245 struct fbcon_ops *ops;
32203246 int idx, blink = -1;
3221
-
3222
- if (fbcon_has_exited)
3223
- return 0;
32243247
32253248 console_lock();
32263249 idx = con2fb_map[fg_console];
....@@ -3247,9 +3270,6 @@
32473270 struct fb_info *info;
32483271 int blink, idx;
32493272 char **last = NULL;
3250
-
3251
- if (fbcon_has_exited)
3252
- return count;
32533273
32543274 console_lock();
32553275 idx = con2fb_map[fg_console];
....@@ -3314,6 +3334,9 @@
33143334
33153335 console_lock();
33163336
3337
+ deferred_takeover = false;
3338
+ logo_shown = FBCON_LOGO_DONTSHOW;
3339
+
33173340 for_each_registered_fb(i)
33183341 fbcon_fb_registered(registered_fb[i]);
33193342
....@@ -3331,8 +3354,6 @@
33313354 pr_info("fbcon: Taking over console\n");
33323355
33333356 dummycon_unregister_output_notifier(&fbcon_output_nb);
3334
- deferred_takeover = false;
3335
- logo_shown = FBCON_LOGO_DONTSHOW;
33363357
33373358 /* We may get called in atomic context */
33383359 schedule_work(&fbcon_deferred_takeover_work);
....@@ -3373,9 +3394,6 @@
33733394 struct fb_info *info;
33743395 int i, j, mapped;
33753396
3376
- if (fbcon_has_exited)
3377
- return;
3378
-
33793397 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
33803398 if (deferred_takeover) {
33813399 dummycon_unregister_output_notifier(&fbcon_output_nb);
....@@ -3397,7 +3415,7 @@
33973415 for (j = first_fb_vc; j <= last_fb_vc; j++) {
33983416 if (con2fb_map[j] == i) {
33993417 mapped = 1;
3400
- break;
3418
+ con2fb_map[j] = -1;
34013419 }
34023420 }
34033421
....@@ -3420,8 +3438,6 @@
34203438 info->queue.func = NULL;
34213439 }
34223440 }
3423
-
3424
- fbcon_has_exited = 1;
34253441 }
34263442
34273443 void __init fb_console_init(void)
....@@ -3429,7 +3445,6 @@
34293445 int i;
34303446
34313447 console_lock();
3432
- fb_register_client(&fbcon_event_notifier);
34333448 fbcon_device = device_create(fb_class, NULL, MKDEV(0, 0), NULL,
34343449 "fbcon");
34353450
....@@ -3465,7 +3480,6 @@
34653480 void __exit fb_console_exit(void)
34663481 {
34673482 console_lock();
3468
- fb_unregister_client(&fbcon_event_notifier);
34693483 fbcon_deinit_device();
34703484 device_destroy(fb_class, MKDEV(0, 0));
34713485 fbcon_exit();