]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
lib/fonts: Store font data for user space with font_data_export()
authorThomas Zimmermann <tzimmermann@suse.de>
Mon, 9 Mar 2026 14:14:54 +0000 (15:14 +0100)
committerHelge Deller <deller@gmx.de>
Mon, 9 Mar 2026 14:47:21 +0000 (15:47 +0100)
Add font_data_export() and update consoles to use it.

The helper font_data_export() is based on code in fbcon_get_font().
It extends the size of a single glyph to match the requested vpitch,
which us usually 32 bytes for fonts from user space. Internal fonts
have a pitch according to the glyph's height.

The implementation of font_data_export() differs in several ways from
the original code. The original implementation distinguished between
different pitches of the font data. This is not necessary as the pitch
is a parameter in the copying.

There was also special handling for a font pitch of 3 bytes, which got
expanded to 4 bytes (with trailing bits on each scanline). The logic
originated from long before git history exists even in the historical
tree. So it is not clear why this was implemented. It is not what user
space expects. The setfont utitlity loads font with 3-bytes pitches and
expects to read such fonts with a 3-byte pitch. For any font width, the
font pitch is always the width extended to the next multiple of 8. See
[1] for the user-space font-reading code.

With the changes to handling the font pitches, font_data_export() replaces
the original code's various special cases with a single copying logic.

v3:
- fix typos (Helge)

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://github.com/legionus/kbd/blob/v2.9.0/src/libkfont/kdfontop.c#L73
Signed-off-by: Helge Deller <deller@gmx.de>
drivers/video/fbdev/core/fbcon.c
include/linux/font.h
lib/fonts/fonts.c

index 53677c09a0ec77a82f71cbac966c0b0b6823fc15..8641b0b3edc4cfa80c474d4e5291f4aad78debd8 100644 (file)
@@ -2282,68 +2282,15 @@ static bool fbcon_blank(struct vc_data *vc, enum vesa_blank_mode blank,
 
 static int fbcon_get_font(struct vc_data *vc, struct console_font *font, unsigned int vpitch)
 {
-       struct fbcon_display *p = &fb_display[vc->vc_num];
-       font_data_t *fontdata = p->fontdata;
-       u8 *data = font->data;
-       int i, j;
+       const struct fbcon_display *p = &fb_display[vc->vc_num];
 
        font->width = vc->vc_font.width;
        font->height = vc->vc_font.height;
        if (font->height > vpitch)
                return -ENOSPC;
        font->charcount = vc->vc_hi_font_mask ? 512 : 256;
-       if (!font->data)
-               return 0;
-
-       if (font->width <= 8) {
-               j = vc->vc_font.height;
-               if (font->charcount * j > font_data_size(fontdata))
-                       return -EINVAL;
 
-               for (i = 0; i < font->charcount; i++) {
-                       memcpy(data, fontdata, j);
-                       memset(data + j, 0, vpitch - j);
-                       data += vpitch;
-                       fontdata += j;
-               }
-       } else if (font->width <= 16) {
-               j = vc->vc_font.height * 2;
-               if (font->charcount * j > font_data_size(fontdata))
-                       return -EINVAL;
-
-               for (i = 0; i < font->charcount; i++) {
-                       memcpy(data, fontdata, j);
-                       memset(data + j, 0, 2*vpitch - j);
-                       data += 2*vpitch;
-                       fontdata += j;
-               }
-       } else if (font->width <= 24) {
-               if (font->charcount * (vc->vc_font.height * sizeof(u32)) > font_data_size(fontdata))
-                       return -EINVAL;
-
-               for (i = 0; i < font->charcount; i++) {
-                       for (j = 0; j < vc->vc_font.height; j++) {
-                               *data++ = fontdata[0];
-                               *data++ = fontdata[1];
-                               *data++ = fontdata[2];
-                               fontdata += sizeof(u32);
-                       }
-                       memset(data, 0, 3 * (vpitch - j));
-                       data += 3 * (vpitch - j);
-               }
-       } else {
-               j = vc->vc_font.height * 4;
-               if (font->charcount * j > font_data_size(fontdata))
-                       return -EINVAL;
-
-               for (i = 0; i < font->charcount; i++) {
-                       memcpy(data, fontdata, j);
-                       memset(data + j, 0, 4 * vpitch - j);
-                       data += 4 * vpitch;
-                       fontdata += j;
-               }
-       }
-       return 0;
+       return font_data_export(p->fontdata, font, vpitch);
 }
 
 /* set/clear vc_hi_font_mask and update vc attrs accordingly */
index 3eb4818402c507d60e2bcc4c0a2ca943bc54b5f7..d80db66a5c17f383528afc5f70689e45c3616f3f 100644 (file)
@@ -62,6 +62,7 @@ void font_data_get(font_data_t *fd);
 bool font_data_put(font_data_t *fd);
 unsigned int font_data_size(font_data_t *fd);
 bool font_data_is_equal(font_data_t *lhs, font_data_t *rhs);
+int font_data_export(font_data_t *fd, struct console_font *font, unsigned int vpitch);
 
 /*
  * Font description
index 16e75c3d2a0f2aa3856a52e59c2dd7ece2200090..a861b375e35db0a6e16e4388cf1efa53918392c0 100644 (file)
@@ -198,6 +198,46 @@ bool font_data_is_equal(font_data_t *lhs, font_data_t *rhs)
 }
 EXPORT_SYMBOL_GPL(font_data_is_equal);
 
+/**
+ * font_data_export - Stores font data for user space
+ * @fd: Font data
+ * @font: A font for user space
+ * @vpitch: The size of a single glyph in @font in bytes
+ *
+ * Store the font data given in @fd to the font in @font. Values and
+ * pointers in @font are pre-initialized. This helper mostly checks some
+ * corner cases and translates glyph sizes according to the value given
+ * @vpitch.
+ *
+ * Returns:
+ * 0 on success, or a negative errno code otherwise.
+ */
+int font_data_export(font_data_t *fd, struct console_font *font, unsigned int vpitch)
+{
+       const unsigned char *font_data = font_data_buf(fd);
+       unsigned char *data = font->data;
+       unsigned int pitch = console_font_pitch(font);
+       unsigned int glyphsize, i;
+
+       if (!font->width || !font->height || !font->charcount || !font->data)
+               return 0;
+
+       glyphsize = font->height * pitch;
+
+       if (font->charcount * glyphsize > font_data_size(fd))
+               return -EINVAL;
+
+       for (i = 0; i < font->charcount; i++) {
+               memcpy(data, font_data, glyphsize);
+               memset(data + glyphsize, 0, pitch * vpitch - glyphsize);
+               data += pitch * vpitch;
+               font_data += glyphsize;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(font_data_export);
+
 /*
  * Font lookup
  */