@@ -3840,9 +3840,6 @@ static int con_font_get(struct vc_data *vc, struct console_font_op *op)
int rc = -EINVAL;
int c;
- if (vc->vc_mode != KD_TEXT)
- return -EINVAL;
-
if (op->data) {
font.data = kmalloc(max_font_size, GFP_KERNEL);
if (!font.data)
@@ -3895,8 +3892,6 @@ static int con_font_set(struct vc_data *vc, struct console_font_op *op)
int rc = -EINVAL;
int size;
- if (vc->vc_mode != KD_TEXT)
- return -EINVAL;
if (!op->data)
return -EINVAL;
if (op->charcount > 512)
@@ -3953,9 +3948,6 @@ static int con_font_default(struct vc_data *vc, struct console_font_op *op)
char *s = name;
int rc;
- if (vc->vc_mode != KD_TEXT)
- return -EINVAL;
-
if (!op->data)
s = NULL;
else if (strncpy_from_user(name, op->data, MAX_FONT_NAME - 1) < 0)
@@ -3981,9 +3973,6 @@ static int con_font_copy(struct vc_data *vc, struct console_font_op *op)
int con = op->height;
int rc;
- if (vc->vc_mode != KD_TEXT)
- return -EINVAL;
-
acquire_console_sem();
if (!vc->vc_sw->con_font_copy)
rc = -ENOSYS;
@@ -1602,29 +1602,8 @@ static void complete_change_console(struct vc_data *vc)
*/
old_vc_mode = oldvc->vc_mode;
-#if defined(CONFIG_VGA_CONSOLE)
- if (old_vc_mode == KD_TEXT && oldvc->vc_sw == &vga_con &&
- oldvc->vc_sw->con_font_get) {
- if (!oldvc->vc_font.data)
- oldvc->vc_font.data = kmalloc(max_font_size,
- GFP_KERNEL);
- lock_kernel();
- oldvc->vc_sw->con_font_get(oldvc, &oldvc->vc_font);
- unlock_kernel();
- }
-#endif
switch_screen(vc);
-#if defined(CONFIG_VGA_CONSOLE)
- if (vc->vc_mode == KD_TEXT && vc->vc_sw == &vga_con &&
- vc->vc_sw->con_font_set) {
- if (vc->vc_font.data) {
- lock_kernel();
- vc->vc_sw->con_font_set(vc, &vc->vc_font, 0);
- unlock_kernel();
- }
- }
-#endif
/*
* This can't appear below a successful kill_pid(). If it did,
* then the *blank_screen operation could occur while X, having
@@ -70,6 +70,11 @@ static struct vgastate state;
*/
#undef TRIDENT_GLITCH
#define VGA_FONTWIDTH 8 /* VGA does not support fontwidths != 8 */
+
+/* Font; borrowed from fbcon.c */
+#define REFCOUNT(fd) (((int *)(fd))[-1])
+#define FONT_EXTRA_WORDS 1
+
/*
* Interface used by the world
*/
@@ -80,6 +85,8 @@ static void vgacon_deinit(struct vc_data *c);
static void vgacon_cursor(struct vc_data *c, int mode);
static int vgacon_switch(struct vc_data *c);
static int vgacon_blank(struct vc_data *c, int blank, int mode_switch);
+static int vgacon_do_font_op(struct vgastate *state, char *arg, int set,
+ int ch512);
static int vgacon_set_palette(struct vc_data *vc, unsigned char *table);
static int vgacon_scrolldelta(struct vc_data *c, int lines);
static int vgacon_set_origin(struct vc_data *c);
@@ -103,6 +110,9 @@ static unsigned int vga_default_font_height __read_mostly; /* Height of default
static unsigned char vga_video_type __read_mostly; /* Card type */
static unsigned char vga_hardscroll_enabled __read_mostly;
static unsigned char vga_hardscroll_user_enable __read_mostly = 1;
+#if defined(CAN_LOAD_EGA_FONTS) && defined(BROKEN_GRAPHICS_PROGRAMS)
+static unsigned char * vga_default_font;
+#endif
static unsigned char vga_font_is_default = 1;
static int vga_vesa_blanked;
static int vga_palette_blanked;
@@ -546,6 +556,21 @@ static const char *vgacon_startup(void)
vgacon_xres = screen_info.orig_video_cols * VGA_FONTWIDTH;
vgacon_yres = vga_scan_lines;
+#if defined(CAN_LOAD_EGA_FONTS) && defined(BROKEN_GRAPHICS_PROGRAMS)
+ /* Remember the default font so that we can restore it. Otherwise,
+ * switching VC from one with a custom font to one with the default
+ * font will fail.
+ */
+ /* Default font is always 256 characters */
+ vga_default_font = kmalloc(FONT_EXTRA_WORDS * sizeof(int) + 256 * 32,
+ GFP_USER);
+ if (vga_default_font) {
+ vga_default_font += FONT_EXTRA_WORDS * sizeof(int);
+ REFCOUNT(vga_default_font) = 1; /* should never go to 0 */
+ vgacon_do_font_op(&state, vga_default_font, 0, 0);
+ }
+#endif
+
if (!vga_init_done) {
vgacon_scrollback_startup();
vga_init_done = 1;
@@ -574,6 +599,13 @@ static void vgacon_init(struct vc_data *c, int init)
c->vc_scan_lines = vga_scan_lines;
c->vc_font.height = vga_video_font_height;
+ c->vc_font.charcount = 256;
+#if defined(CAN_LOAD_EGA_FONTS) && defined(BROKEN_GRAPHICS_PROGRAMS)
+ c->vc_font.data = vga_default_font;
+ REFCOUNT(vga_default_font)++;
+#else
+ c->vc_font.data = NULL;
+#endif
c->vc_complement_mask = 0x7700;
if (vga_512_chars)
c->vc_hi_font_mask = 0x0800;
@@ -587,6 +619,13 @@ static void vgacon_init(struct vc_data *c, int init)
con_set_default_unimap(c);
}
+static void vgacon_free_font(struct vc_data *c)
+{
+ if (c->vc_font.data && --REFCOUNT(c->vc_font.data) == 0)
+ kfree(c->vc_font.data - FONT_EXTRA_WORDS * sizeof(int));
+ c->vc_font.data = NULL;
+}
+
static void vgacon_deinit(struct vc_data *c)
{
/* When closing the active console, reset video origin */
@@ -599,6 +638,8 @@ static void vgacon_deinit(struct vc_data *c)
con_free_unimap(c);
c->vc_uni_pagedir_loc = &c->vc_uni_pagedir;
con_set_default_unimap(c);
+
+ vgacon_free_font(c);
}
static u8 vgacon_build_attr(struct vc_data *c, u8 color, u8 intensity,
@@ -838,6 +879,13 @@ static int vgacon_switch(struct vc_data *c)
vga_video_num_columns <= screen_info.orig_video_cols &&
vga_video_num_lines <= rows))
vgacon_doresize(c, c->vc_cols, c->vc_rows);
+
+#ifdef CAN_LOAD_EGA_FONTS
+ lock_kernel();
+ vgacon_do_font_op(&state, c->vc_font.data, 1,
+ c->vc_font.charcount == 512);
+ unlock_kernel();
+#endif
}
vgacon_scrollback_init(c->vc_size_row);
@@ -1076,15 +1124,20 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512)
beg = 0x0a;
}
+ if (set) {
+ vga_font_is_default = !arg;
+ if (!arg)
+ ch512 = 0; /* Default font is always 256 */
+ }
+
#ifdef BROKEN_GRAPHICS_PROGRAMS
/*
* All fonts are loaded in slot 0 (0:1 for 512 ch)
*/
- if (!arg)
- return -EINVAL; /* Return to default font not supported */
+ if (!arg && set)
+ arg = vga_default_font; /* Return to default font */
- vga_font_is_default = 0;
font_select = ch512 ? 0x04 : 0x00;
#else
/*
@@ -1092,12 +1145,8 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512)
* A custom font is loaded in slot 2 (256 ch) or 2:3 (512 ch)
*/
- if (set) {
- vga_font_is_default = !arg;
- if (!arg)
- ch512 = 0; /* Default font is always 256 */
+ if (set)
font_select = arg ? (ch512 ? 0x0e : 0x0a) : 0x00;
- }
if (!vga_font_is_default)
charmap += 4 * cmapsz;
@@ -1169,12 +1218,6 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512)
/* if 512 char mode is already enabled don't re-enable it. */
if ((set) && (ch512 != vga_512_chars)) {
- /* attribute controller */
- for (i = 0; i < MAX_NR_CONSOLES; i++) {
- struct vc_data *c = vc_cons[i].d;
- if (c && c->vc_sw == &vga_con)
- c->vc_hi_font_mask = ch512 ? 0x0800 : 0;
- }
vga_512_chars = ch512;
/* 256-char: enable intensity bit
512-char: disable intensity bit */
@@ -1197,7 +1240,7 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512)
static int vgacon_adjust_height(struct vc_data *vc, unsigned fontheight)
{
unsigned char ovr, vde, fsr;
- int rows, maxscan, i;
+ int rows, maxscan;
rows = vc->vc_scan_lines / fontheight; /* Number of video rows we end up with */
maxscan = rows * fontheight - 1; /* Scan lines to actually display-1 */
@@ -1234,27 +1277,16 @@ static int vgacon_adjust_height(struct vc_data *vc, unsigned fontheight)
spin_unlock_irq(&vga_lock);
vga_video_font_height = fontheight;
- for (i = 0; i < MAX_NR_CONSOLES; i++) {
- struct vc_data *c = vc_cons[i].d;
-
- if (c && c->vc_sw == &vga_con) {
- if (CON_IS_VISIBLE(c)) {
- /* void size to cause regs to be rewritten */
- cursor_size_lastfrom = 0;
- cursor_size_lastto = 0;
- c->vc_sw->con_cursor(c, CM_DRAW);
- }
- c->vc_font.height = fontheight;
- vc_resize(c, 0, rows); /* Adjust console size */
- }
- }
return 0;
}
static int vgacon_font_set(struct vc_data *c, struct console_font *font, unsigned flags)
{
unsigned charcount = font->charcount;
- int rc;
+ int h = font->height;
+ int i;
+ u8 *new_data, *data = font->data;
+ int rc = 0;
if (vga_video_type < VIDEO_TYPE_EGAM)
return -EINVAL;
@@ -1263,26 +1295,78 @@ static int vgacon_font_set(struct vc_data *c, struct console_font *font, unsigne
(charcount != 256 && charcount != 512))
return -EINVAL;
- rc = vgacon_do_font_op(&state, font->data, 1, charcount == 512);
- if (rc)
- return rc;
+ new_data = kmalloc(FONT_EXTRA_WORDS * sizeof(int) + charcount * 32,
+ GFP_USER);
+ if (!new_data)
+ return -ENOMEM;
+
+ new_data += FONT_EXTRA_WORDS * sizeof(int);
+ REFCOUNT(new_data) = 0; /* usage counter */
+ memcpy(new_data, data, charcount * 32);
+
+ /* Check if the same font is on some other console already */
+ for (i = 0; i < MAX_NR_CONSOLES; i++) {
+ struct vc_data *other = vc_cons[i].d;
+ if (i != c->vc_num && other &&
+ other->vc_font.height == h &&
+ other->vc_font.charcount == charcount &&
+ !memcmp(other->vc_font.data, new_data, charcount * 32)) {
+ kfree(new_data - FONT_EXTRA_WORDS * sizeof(int));
+ new_data = (u8 *)other->vc_font.data;
+ break;
+ }
+ }
+
+ if (CON_IS_VISIBLE(c)) {
+ rc = vgacon_do_font_op(&state, new_data, 1, charcount == 512);
+ if (rc)
+ return rc;
+ }
+
+ c->vc_font.height = h;
+ c->vc_font.charcount = charcount;
+ c->vc_font.data = new_data;
+ REFCOUNT(new_data)++;
+ /* attribute controller */
+ c->vc_hi_font_mask = (charcount == 512) ? 0x0800 : 0;
if (!(flags & KD_FONT_FLAG_DONT_RECALC))
rc = vgacon_adjust_height(c, font->height);
+ if (CON_IS_VISIBLE(c)) {
+ /* void size to cause regs to be rewritten */
+ cursor_size_lastfrom = 0;
+ cursor_size_lastto = 0;
+ c->vc_sw->con_cursor(c, CM_DRAW);
+ }
+ /* Adjust console size */
+ vc_resize(c, 0, c->vc_scan_lines / font->height);
return rc;
}
static int vgacon_font_get(struct vc_data *c, struct console_font *font)
{
+ u8 *fontdata = c->vc_font.data;
+ u8 *data = font->data;
+ int i, j;
+
if (vga_video_type < VIDEO_TYPE_EGAM)
return -EINVAL;
font->width = VGA_FONTWIDTH;
font->height = c->vc_font.height;
- font->charcount = vga_512_chars ? 512 : 256;
- if (!font->data)
+ font->charcount = c->vc_font.charcount;
+ if (fontdata) {
+ j = font->height;
+ for (i = 0; i < font->charcount; i++) {
+ memcpy(data, fontdata, j);
+ memset(data + j, 0, 32 - j);
+ data += 32;
+ fontdata += j;
+ }
return 0;
- return vgacon_do_font_op(&state, font->data, 0, vga_512_chars);
+ } else
+ return vgacon_do_font_op(&state, font->data, 0,
+ font->charcount);
}
#else
I'm planning to send something along these lines upstream after a little more work. Before I submit myself to their tender mercies, could somebody do me a favour and give this a once-over to check that I'm working along the right lines? The background to this change is that I am truly, madly, deeply fed up of fixing the userspace side of console font setup every two releases or so. I think this is the third or fourth time I've had to do it, and the reason it's so delicate is because of a kernel limitation: even though there's an ioctl to let you set the font on a non-foreground virtual console, you can't really rely on it because vgacon has a single font for all virtual consoles and stores it only in video memory and nowhere else. The font ioctls only work on KD_TEXT consoles, which I believe is to stop vgacon trashing your graphics by scribbling over video memory. As a result, if you happen to be in graphics mode to show a splash screen, you can't use the font ioctls at all; or, for that matter, if you find yourself racing to start X and X wins, you're hosed because any attempt to set the font on a vgacon-based system will write to video memory and confuse the living daylights out of X. As far as I can see, the other console implementations don't have this problem: they have per-console fonts and don't write anything to video memory when changing the font, only when actually visible and displaying text. vgacon was quite a long way from this, but fixing it there seemed the best way to get rid of this problem permanently. I don't often program in kernelspace, and am somewhat reliant on cargo-culting. For example, I'm not sure I need to take the BKL while setting the font in vgacon_switch, but: that's what the comparable code in complete_change_console did; I think the console semaphore is sometimes held when calling vgacon_switch but I'm not sure if it always is; I don't think I can recursively take the console semaphore; and my system locked up when I had no locking there at all. Advice on the Right Thing To Do would be greatly appreciated. I've tested that basic font setting and VC switching works. I made an initial stab at fixing VC resizing when setting a font with a different height, but this isn't working properly yet. I haven't yet tested getting from KD_GRAPHICS to KD_TEXT either by ioctl(KDSETMODE) or by VC switching, although I thought fairly hard about it and tried to make sure it would work. I don't like the horrible FONT_EXTRA_WORDS thing, but it's in line with the other console implementations; what's wrong with structs, eh? Is it safe to write to the VGA sequence and graphics registers from vgacon_startup? It seems to work here ... Is there anything else I should be considering? commit eb6ebc903f4b048b4ecab0ec6c0dc1292fae40cb Author: Colin Watson <cjwatson@canonical.com> Date: Thu Feb 25 21:23:32 2010 +0000 Allow changing font on inactive consoles Console font changes were only permitted when the console was in KD_TEXT mode. This was apparently because vgacon stored the font only in video memory, so it could only be changed on an active text console. Both of these are inconvenient for userspace at boot time; they mean that, if you want to set the console font, you must do so when no splash screen is running and before X starts, and trying to make console set-up asynchronous is fraught with difficulty. Other than a little extra memory use for the font data (which can be shared if multiple VTs use the same font), there's no reason why vgacon can't store the font in kernel memory and set it on VT switches, as other console implementations already do. This change does so and lifts the restrictions on the font ioctls. In order to support BROKEN_GRAPHICS_PROGRAMS (the default), we save the default font on vgacon startup. As bonuses, we get per-console fonts in vgacon, and we can get rid of the nasty vgacon-specific code in complete_change_console. The downsides of this are 32KB extra memory for the default font (when BROKEN_GRAPHICS_PROGRAMS is defined), and a font change on every VC switch since by the time vgacon_switch is called we no longer know the previous active VC (but it doesn't seem to be perceptible here). Signed-off-by: Colin Watson <cjwatson@canonical.com> Thanks,