Coverage Report

Created: 2026-10-13 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/barebox/drivers/video/fbconsole.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-only
2
3
#define pr_fmt(fmt)     "fbconsole: " fmt
4
5
#include <common.h>
6
#include <errno.h>
7
#include <malloc.h>
8
#include <getopt.h>
9
#include <fb.h>
10
#include <gui/image_renderer.h>
11
#include <gui/graphic_utils.h>
12
#include <linux/font.h>
13
#include <linux/ctype.h>
14
#include <charset.h>
15
16
enum state_t {
17
  LIT,        /* Literal input */
18
  ESC,        /* Start of escape sequence */
19
  CSI,        /* Reading arguments in "CSI Pn ;...*/
20
};
21
22
enum fbconsole_rotation {
23
  FBCONSOLE_ROTATE_0,
24
  FBCONSOLE_ROTATE_90,
25
  FBCONSOLE_ROTATE_180,
26
  FBCONSOLE_ROTATE_270,
27
};
28
29
static const char * const rotation_names[] = {
30
  [FBCONSOLE_ROTATE_0] = "0",
31
  [FBCONSOLE_ROTATE_90] = "90",
32
  [FBCONSOLE_ROTATE_180] = "180",
33
  [FBCONSOLE_ROTATE_270] = "270",
34
};
35
36
enum ansi_color {
37
  BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE,
38
  BRIGHT
39
};
40
41
0
#define DEFAULT_COLOR WHITE
42
0
#define DEFAULT_BGCOLOR BLACK
43
44
struct fbc_screen_state {
45
  unsigned int x, y; /* cursor position */
46
47
  int color;
48
  int bgcolor;
49
50
  u32 color_rgb;
51
  u32 bgcolor_rgb;
52
53
0
#define ANSI_FLAG_INVERT  (1 << 0)
54
0
#define ANSI_FLAG_BRIGHT  (1 << 1)
55
0
#define SGR_ATTRIBUTES    (ANSI_FLAG_INVERT | ANSI_FLAG_BRIGHT)
56
0
#define HIDE_CURSOR   (1 << 2)
57
  unsigned flags;
58
59
  bool pending_wrap;  /* deferred wrap: cursor at last column */
60
};
61
62
struct fbc_priv {
63
  struct console_device cdev;
64
  struct fb_info *fb;
65
  struct {
66
    u32 top;
67
    u32 left;
68
    u32 bottom;
69
    u32 right;
70
  } margin;
71
72
  struct screen *sc;
73
74
  struct param_d *par_font;
75
  int par_font_val;
76
77
  const struct font_desc *font;
78
79
  unsigned int cols, rows;
80
81
  struct fbc_screen_state cur;
82
  struct fbc_screen_state saved; /* DEC cursor save (\e7) */
83
84
  struct fbc_screen_state altscreen_saved;
85
  void *altscreen_buf;  /* pixel snapshot while in alternate screen */
86
  size_t altscreen_size;
87
88
  unsigned int rotation;
89
  enum state_t state;
90
91
  int csipos;
92
  u8 csi[256];
93
  unsigned char csi_cmd;
94
95
  int active;
96
  int in_console;
97
98
  char utf8_buf[5]; /* UTF-8 stream decoder buffer */
99
};
100
101
static int fbc_getc(struct console_device *cdev)
102
0
{
103
0
  return 0;
104
0
}
105
106
static int fbc_tstc(struct console_device *cdev)
107
0
{
108
0
  return 0;
109
0
}
110
111
static int fbc_get_size(struct console_device *cdev, int *width, int *height)
112
0
{
113
0
  struct fbc_priv *priv = container_of(cdev, struct fbc_priv, cdev);
114
115
0
  *width = priv->cols;
116
0
  *height = priv->rows;
117
0
  return 0;
118
0
}
119
120
static void cls(struct fbc_priv *priv)
121
0
{
122
0
  void *buf = gui_screen_render_buffer(priv->sc);
123
0
  struct fb_info *fb = priv->fb;
124
0
  int width = fb->xres - priv->margin.left - priv->margin.right;
125
0
  int height = fb->yres - priv->margin.top - priv->margin.bottom;
126
0
  void *adr;
127
128
0
  adr = buf + priv->fb->line_length * priv->margin.top;
129
130
0
  if (!priv->margin.left && !priv->margin.right) {
131
0
    memset(adr, 0, priv->fb->line_length * height);
132
0
  } else {
133
0
    int bpp = priv->fb->bits_per_pixel >> 3;
134
0
    int y;
135
136
0
    for (y = 0; y < height; y++) {
137
0
      memset(adr + priv->margin.left * bpp, 0, width * bpp);
138
0
      adr += priv->fb->line_length;
139
0
    }
140
0
  }
141
0
  gu_screen_blit_area(priv->sc, priv->margin.left, priv->margin.top,
142
0
          width, height);
143
0
}
144
145
struct rgb {
146
  u8 r, g, b;
147
};
148
149
static struct rgb colors[] = {
150
  [BLACK]     = { 0, 0, 0 },
151
  [RED]     = { 205, 0, 0 },
152
  [GREEN]     = { 0, 205, 0 },
153
  [YELLOW]    = { 205, 205, 0 },
154
  [BLUE]      = { 0, 0, 238 },
155
  [MAGENTA]   = { 205, 0, 205 },
156
  [CYAN]      = { 0, 205, 205 },
157
  [WHITE]     = { 205, 205, 205 },
158
  [BRIGHT + BLACK]  = { 127, 127, 127 },
159
  [BRIGHT + RED]    = { 255, 0, 0 },
160
  [BRIGHT + GREEN]  = { 0, 255, 0 },
161
  [BRIGHT + YELLOW] = { 255, 255, 0 },
162
  [BRIGHT + BLUE]   = { 92, 92, 255 },
163
  [BRIGHT + MAGENTA]  = { 255, 0, 255 },
164
  [BRIGHT + CYAN]   = { 0, 255, 255 },
165
  [BRIGHT + WHITE]  = { 255, 255, 255 },
166
};
167
168
static void fb_blit_area(struct fbc_priv *priv, int x, int y)
169
0
{
170
0
  int startx, starty, width, height, fw, fh;
171
0
  int mx, my;
172
173
0
  mx = priv->margin.left;
174
0
  my = priv->margin.top;
175
176
0
  fw = priv->font->width;
177
0
  fh = priv->font->height;
178
179
0
  switch (priv->rotation) {
180
0
  case FBCONSOLE_ROTATE_0:
181
0
    startx = mx + x * fw;
182
0
    starty = my + y * fh;
183
0
    width = fw;
184
0
    height = fh;
185
0
    break;
186
0
  case FBCONSOLE_ROTATE_90:
187
0
    startx = mx + (priv->rows - y - 1) * fh;
188
0
    starty = my + x * fw;
189
0
    width = fh;
190
0
    height = fw;
191
0
    break;
192
0
  case FBCONSOLE_ROTATE_180:
193
0
    startx = mx + (priv->cols - x - 1) * fw;
194
0
    starty = my + (priv->rows - y - 1) * fh;
195
0
    width = fw;
196
0
    height = fh;
197
0
    break;
198
0
  case FBCONSOLE_ROTATE_270:
199
0
    startx = mx + y * fh;
200
0
    starty = my + (priv->cols - x - 1) * fw;
201
0
    width = fh;
202
0
    height = fw;
203
0
    break;
204
0
  default:
205
0
    return;
206
0
  }
207
208
0
  gu_screen_blit_area(priv->sc, startx, starty, width, height);
209
0
}
210
211
static void drawchar(struct fbc_priv *priv, int x, int y, int c)
212
0
{
213
0
  void *buf;
214
0
  int bpp = priv->fb->bits_per_pixel >> 3;
215
0
  void *adr;
216
0
  int i;
217
0
  const uint8_t *inbuf;
218
0
  int line_length;
219
0
  int xstep;
220
0
  int ystep;
221
0
  int startx;
222
0
  int starty;
223
224
0
  buf = gui_screen_render_buffer(priv->sc);
225
226
0
  i = find_font_index(priv->font, c);
227
0
  inbuf = priv->font->data + i;
228
229
0
  line_length = priv->fb->line_length;
230
231
0
  adr = buf;
232
233
0
  switch (priv->rotation) {
234
0
  case FBCONSOLE_ROTATE_0:
235
0
    xstep = bpp;
236
0
    ystep = line_length;
237
0
    startx = x * priv->font->width;
238
0
    starty = y * priv->font->height;
239
0
    break;
240
0
  case FBCONSOLE_ROTATE_90:
241
0
    xstep = line_length;
242
0
    ystep = -bpp;
243
0
    startx = (priv->rows - y) * priv->font->height - 1;
244
0
    starty = x * priv->font->width;
245
0
    break;
246
0
  case FBCONSOLE_ROTATE_180:
247
0
    xstep = -bpp;
248
0
    ystep = -line_length;
249
0
    startx = (priv->cols - x) * priv->font->width - 1;
250
0
    starty = (priv->rows - y) * priv->font->height - 1;
251
0
    break;
252
0
  case FBCONSOLE_ROTATE_270:
253
0
    xstep = -line_length;
254
0
    ystep = bpp;
255
0
    startx = priv->font->height * y;
256
0
    starty = (priv->cols - x) * priv->font->width - 1;
257
0
    break;
258
0
  default:
259
0
    return;
260
0
  }
261
262
0
  adr += (priv->margin.left + startx) * bpp;
263
0
  adr += (priv->margin.top + starty) * line_length;
264
265
0
  for (i = 0; i < priv->font->height; i++) {
266
0
    uint8_t mask = 0x80;
267
0
    int j;
268
269
0
    for (j = 0; j < priv->font->width; j++) {
270
0
      if (!mask) {
271
0
        inbuf++;
272
0
        mask = 0x80;
273
0
      }
274
275
0
      if (*inbuf & mask)
276
0
        gu_set_pixel(priv->fb, adr + j * xstep,
277
0
               priv->cur.color_rgb);
278
0
      else
279
0
        gu_set_pixel(priv->fb, adr + j * xstep,
280
0
               priv->cur.bgcolor_rgb);
281
282
0
      mask >>= 1;
283
284
0
    }
285
286
0
    adr += ystep;
287
288
0
    inbuf++;
289
0
    mask = 0x80;
290
0
  }
291
292
0
  fb_blit_area(priv, x, y);
293
0
}
294
295
static void clear_chars(struct fbc_priv *priv,
296
      int start_x, int start_y, int end_x, int end_y)
297
0
{
298
0
  for (int y = start_y; y <= end_y; y++) {
299
0
    int xs = (y == start_y) ? start_x : 0;
300
0
    int xe = (y == end_y) ? end_x : (int)priv->cols - 1;
301
302
0
    for (int x = xs; x <= xe; x++)
303
0
      drawchar(priv, x, y, ' ');
304
0
  }
305
0
}
306
307
static void video_invertchar(struct fbc_priv *priv, int x, int y)
308
0
{
309
0
  int startx, starty, width, height, fw, fh;
310
0
  void *buf;
311
312
0
  buf = gui_screen_render_buffer(priv->sc);
313
0
  buf += priv->margin.top * priv->fb->line_length;
314
0
  buf += priv->margin.left * (priv->fb->bits_per_pixel >> 3);
315
316
0
  fw = priv->font->width;
317
0
  fh = priv->font->height;
318
319
0
  switch (priv->rotation) {
320
0
  case FBCONSOLE_ROTATE_0:
321
0
    startx = x * fw;
322
0
    starty = y * fh;
323
0
    width = fw;
324
0
    height = fh;
325
0
    break;
326
0
  case FBCONSOLE_ROTATE_90:
327
0
    startx = (priv->rows - y - 1) * fh;
328
0
    starty = x * fw;
329
0
    width = fh;
330
0
    height = fw;
331
0
    break;
332
0
  case FBCONSOLE_ROTATE_180:
333
0
    startx = (priv->cols - x - 1) * fw;
334
0
    starty = (priv->rows - y - 1) * fh;
335
0
    width = fw;
336
0
    height = fh;
337
0
    break;
338
0
  case FBCONSOLE_ROTATE_270:
339
0
    startx = y * fh;
340
0
    starty = (priv->cols - x - 1) * fw;
341
0
    width = fh;
342
0
    height = fw;
343
0
    break;
344
0
  default:
345
0
    return;
346
0
  }
347
348
0
  gu_invert_area(priv->fb, buf, startx, starty, width, height);
349
0
  fb_blit_area(priv, x, y);
350
0
}
351
352
static void toggle_cursor_visibility(struct fbc_priv *priv)
353
0
{
354
0
  if (!(priv->cur.flags & HIDE_CURSOR))
355
0
    video_invertchar(priv, priv->cur.x, priv->cur.y);
356
0
}
357
358
static void fb_scroll_up_0(struct fbc_priv *priv, void *adr, int width, int height)
359
0
{
360
0
  u32 line_length = priv->fb->line_length;
361
0
  int line_height = line_length * priv->font->height;
362
363
0
  if (!priv->margin.left && !priv->margin.right) {
364
0
    memcpy(adr, adr + line_height, line_height * (priv->rows - 1));
365
0
    memset(adr + line_height * (priv->rows - 1), 0, line_height);
366
0
  } else {
367
0
    int bpp = priv->fb->bits_per_pixel >> 3;
368
0
    int y;
369
370
0
    for (y = 0; y < height - priv->font->height; y++) {
371
0
      memcpy(adr, adr + line_height, width * bpp);
372
0
      adr += line_length;
373
0
    }
374
375
0
    for (y = height - priv->font->height; y < height; y++) {
376
0
      memset(adr, 0, width * bpp);
377
0
      adr += line_length;
378
0
    }
379
0
  }
380
0
}
381
382
static void fb_scroll_up_180(struct fbc_priv *priv, void *adr, int width, int height)
383
0
{
384
0
  u32 line_length = priv->fb->line_length;
385
0
  int line_height = line_length * priv->font->height;
386
387
0
  if (!priv->margin.left && !priv->margin.right) {
388
0
    memmove(adr + line_height, adr, line_height * (priv->rows - 1));
389
0
    memset(adr, 0, line_height);
390
0
  } else {
391
0
    int bpp = priv->fb->bits_per_pixel >> 3;
392
0
    int y;
393
394
0
    adr += (height - 1) * line_length;
395
396
0
    for (y = height; y > priv->font->height; y--) {
397
0
      memcpy(adr, adr - line_height, width * bpp);
398
0
      adr -= line_length;
399
0
    }
400
401
0
    for (y = 0; y < priv->font->height; y++) {
402
0
      memset(adr, 0, width * bpp);
403
0
      adr -= line_length;
404
0
    }
405
0
  }
406
0
}
407
408
static void fb_scroll_up_90(struct fbc_priv *priv, void *adr, int width, int height)
409
0
{
410
0
  u32 line_length = priv->fb->line_length;
411
0
  int bpp = priv->fb->bits_per_pixel >> 3;
412
0
  int y;
413
414
0
  for (y = 0; y < priv->cols * priv->font->width; y++) {
415
0
    memmove(adr + priv->font->height * bpp,
416
0
      adr,
417
0
      (priv->rows - 1) * priv->font->height * bpp);
418
0
    memset(adr, 0, priv->font->height * bpp);
419
0
    adr += line_length;
420
0
  }
421
0
}
422
423
static void fb_scroll_up_270(struct fbc_priv *priv, void *adr, int width, int height)
424
0
{
425
0
  u32 line_length = priv->fb->line_length;
426
0
  int bpp = priv->fb->bits_per_pixel >> 3;
427
0
  int y;
428
429
0
  for (y = 0; y < priv->cols * priv->font->width; y++) {
430
0
    memmove(adr,
431
0
      adr + priv->font->height * bpp,
432
0
      (priv->rows - 1) * priv->font->height * bpp);
433
0
    memset(adr + priv->font->height * bpp * (priv->rows - 1),
434
0
           0x0,
435
0
           priv->font->height * bpp);
436
0
    adr += line_length;
437
0
  }
438
0
}
439
440
static void fb_scroll_up(struct fbc_priv *priv)
441
0
{
442
0
  int width = priv->fb->xres - priv->margin.left - priv->margin.right;
443
0
  int height = priv->fb->yres - priv->margin.top - priv->margin.bottom;
444
0
  int bpp = priv->fb->bits_per_pixel >> 3;
445
0
  void *adr;
446
447
0
  adr = gui_screen_render_buffer(priv->sc);
448
0
  adr += priv->margin.top * priv->fb->line_length;
449
0
  adr += priv->margin.left * bpp;
450
451
0
  switch (priv->rotation) {
452
0
  case FBCONSOLE_ROTATE_0:
453
0
    fb_scroll_up_0(priv, adr, width, height);
454
0
    break;
455
0
  case FBCONSOLE_ROTATE_90:
456
0
    fb_scroll_up_90(priv, adr, width, height);
457
0
    break;
458
0
  case FBCONSOLE_ROTATE_180:
459
0
    fb_scroll_up_180(priv, adr, width, height);
460
0
    break;
461
0
  case FBCONSOLE_ROTATE_270:
462
0
    fb_scroll_up_270(priv, adr, width, height);
463
0
    break;
464
0
  }
465
466
0
  gu_screen_blit_area(priv->sc, priv->margin.left, priv->margin.top,
467
0
          width, height);
468
0
}
469
470
static void printchar(struct fbc_priv *priv, int c)
471
0
{
472
0
  struct fbc_screen_state *cur = &priv->cur;
473
474
0
  toggle_cursor_visibility(priv);
475
476
0
  switch (c) {
477
0
  case '\007': /* bell: ignore */
478
0
    break;
479
0
  case '\b':
480
0
    cur->pending_wrap = false;
481
0
    if (cur->x > 0) {
482
0
      cur->x--;
483
0
    } else if (cur->y > 0) {
484
0
      cur->x = priv->cols - 1;
485
0
      cur->y--;
486
0
    }
487
0
    break;
488
0
  case '\n':
489
0
  case '\013': /* Vertical tab is the same as Line Feed */
490
0
    cur->pending_wrap = false;
491
0
    cur->y++;
492
0
    break;
493
494
0
  case '\r':
495
0
    cur->pending_wrap = false;
496
0
    cur->x = 0;
497
0
    break;
498
499
0
  case '\t':
500
0
    cur->pending_wrap = false;
501
0
    cur->x = (cur->x + 8) & ~0x3;
502
0
    break;
503
504
0
  default:
505
    /*
506
     * VT100 deferred wrap: if the previous character landed at
507
     * the last column, perform the wrap NOW before drawing the
508
     * next character.  This prevents a scroll from occurring
509
     * merely by writing to the bottom-right corner cell.
510
     */
511
0
    if (cur->pending_wrap) {
512
0
      cur->pending_wrap = false;
513
0
      cur->x = 0;
514
0
      cur->y++;
515
516
0
      if (cur->y >= priv->rows) {
517
0
        fb_scroll_up(priv);
518
0
        cur->y = priv->rows - 1;
519
0
      }
520
0
    }
521
522
0
    drawchar(priv, cur->x, cur->y, c);
523
524
0
    cur->x++;
525
0
    if (cur->x >= priv->cols) {
526
      /* Defer the wrap to the next printable character */
527
0
      cur->x = priv->cols - 1;
528
0
      cur->pending_wrap = true;
529
0
    }
530
0
    break;
531
0
  }
532
533
0
  if (cur->y >= priv->rows) {
534
0
    fb_scroll_up(priv);
535
0
    cur->y = priv->rows - 1;
536
0
  }
537
538
0
  toggle_cursor_visibility(priv);
539
0
}
540
541
static void fbc_update_colors(struct fbc_priv *priv, int color, int bgcolor)
542
0
{
543
0
  struct fbc_screen_state *cur = &priv->cur;
544
0
  struct rgb *rgb;
545
546
0
  if (color >= 0)
547
0
    cur->color = color;
548
0
  if (bgcolor >= 0)
549
0
    cur->bgcolor = bgcolor;
550
551
0
  if (cur->flags & ANSI_FLAG_INVERT) {
552
0
    color = cur->bgcolor;
553
0
    bgcolor = cur->color;
554
0
  } else {
555
0
    color = cur->color;
556
0
    bgcolor = cur->bgcolor;
557
0
  }
558
559
0
  if (cur->flags & ANSI_FLAG_BRIGHT)
560
0
    color += BRIGHT;
561
562
0
  rgb = &colors[color];
563
0
  cur->color_rgb = gu_rgb_to_pixel(priv->fb, rgb->r, rgb->g, rgb->b, 0xff);
564
565
0
  rgb = &colors[bgcolor];
566
0
  cur->bgcolor_rgb = gu_rgb_to_pixel(priv->fb, rgb->r, rgb->g, rgb->b, 0x0);
567
0
}
568
569
static void fbc_reset_colors(struct fbc_priv *priv)
570
0
{
571
0
  fbc_update_colors(priv, DEFAULT_COLOR, DEFAULT_BGCOLOR);
572
0
}
573
574
static void fbc_parse_colors(struct fbc_priv *priv)
575
0
{
576
0
  struct fbc_screen_state *cur = &priv->cur;
577
0
  int color = -1, bgcolor = -1;
578
0
  int code;
579
0
  char *str;
580
581
0
  str = priv->csi;
582
583
0
  while (1) {
584
0
    code = simple_strtoul(str, &str, 10);
585
0
    switch (code) {
586
0
    case 0:
587
0
      cur->flags &= ~SGR_ATTRIBUTES;
588
0
      color = DEFAULT_COLOR;
589
0
      bgcolor = DEFAULT_BGCOLOR;
590
0
      break;
591
0
    case 1:
592
0
      cur->flags |= ANSI_FLAG_BRIGHT;
593
0
      break;
594
0
    case 7:
595
0
      cur->flags |= ANSI_FLAG_INVERT;
596
0
      break;
597
0
    case 30 ... 37:
598
0
      color = code - 30;
599
0
      break;
600
0
    case 39:
601
0
      color = DEFAULT_COLOR;
602
0
      break;
603
0
    case 40 ... 47:
604
0
      bgcolor = code - 40;
605
0
      break;
606
0
    case 49:
607
0
      bgcolor = DEFAULT_BGCOLOR;
608
0
      break;
609
0
    case 90 ... 97:
610
0
      cur->flags |= ANSI_FLAG_BRIGHT;
611
0
      color = code - 90;
612
0
      break;
613
0
    }
614
615
0
    if (*str != ';')
616
0
      break;
617
0
    str++;
618
0
  }
619
620
0
  fbc_update_colors(priv, color, bgcolor);
621
0
}
622
623
static void fbc_set_cursor_row(struct fbc_priv *priv, int y)
624
0
{
625
0
  priv->cur.y = clamp_t(int, y, 0, priv->rows - 1);
626
0
}
627
628
static void fbc_set_cursor_col(struct fbc_priv *priv, unsigned int x)
629
0
{
630
0
  priv->cur.x = clamp_t(int, x, 0, priv->cols - 1);
631
0
}
632
633
static bool fbc_parse_csi(struct fbc_priv *priv)
634
0
{
635
0
  char *end;
636
0
  unsigned char last;
637
0
  int pos;
638
639
0
  last = priv->csi[priv->csipos - 1];
640
641
0
  switch (last) {
642
0
  case 'm':
643
0
    fbc_parse_colors(priv);
644
0
    break;
645
0
  case 'h':
646
    /* suffix for vt100 "[?25h" */
647
0
    if (priv->csi_cmd != '?')
648
0
      break;
649
0
    pos = simple_strtoul(priv->csi + 1, NULL, 10);
650
0
    switch (pos) {
651
0
    case 25: /* cursor visible */
652
0
      if (!(priv->cur.flags & HIDE_CURSOR))
653
0
        break;
654
0
      priv->cur.flags &= ~HIDE_CURSOR;
655
      /* show cursor now */
656
0
      toggle_cursor_visibility(priv);
657
0
      return true;
658
0
    case 1049: /* alternate screen: save pixel buffer and state */
659
0
      if (!priv->altscreen_buf) {
660
0
        void *src = gui_screen_render_buffer(priv->sc);
661
0
        size_t sz = priv->fb->line_length * priv->fb->yres;
662
663
0
        priv->altscreen_buf = memdup(src, sz);
664
0
        if (priv->altscreen_buf) {
665
0
          priv->altscreen_size  = sz;
666
0
          priv->altscreen_saved = priv->cur;
667
0
        }
668
0
        priv->cur = (struct fbc_screen_state){};
669
0
        fbc_reset_colors(priv);
670
0
        cls(priv);
671
0
      }
672
0
      return true;
673
0
    }
674
0
    break;
675
0
  case 'l':
676
    /* suffix for vt100 "[?25l" */
677
0
    if (priv->csi_cmd != '?')
678
0
      break;
679
0
    pos = simple_strtoul(priv->csi + 1, NULL, 10);
680
0
    switch (pos) {
681
0
    case 25: /* cursor invisible */
682
0
      toggle_cursor_visibility(priv);
683
      /* hide cursor now */
684
0
      priv->cur.flags |= HIDE_CURSOR;
685
0
      return true;
686
0
    case 1049: /* alternate screen: restore pixel buffer and state */
687
0
      if (priv->altscreen_buf) {
688
0
        void *dst = gui_screen_render_buffer(priv->sc);
689
0
        size_t sz = priv->fb->line_length * priv->fb->yres;
690
691
0
        if (sz == priv->altscreen_size) {
692
0
          memcpy(dst, priv->altscreen_buf, sz);
693
0
          gu_screen_blit(priv->sc);
694
0
        } else {
695
0
          cls(priv);
696
0
        }
697
0
        free(priv->altscreen_buf);
698
0
        priv->altscreen_buf  = NULL;
699
0
        priv->altscreen_size = 0;
700
0
        priv->cur = priv->altscreen_saved;
701
0
      } else {
702
0
        priv->cur = (struct fbc_screen_state){};
703
0
        fbc_reset_colors(priv);
704
0
        cls(priv);
705
0
      }
706
0
      return true;
707
0
    }
708
0
    break;
709
0
  case 'J':
710
0
    pos = simple_strtoul(priv->csi, &end, 10);
711
0
    toggle_cursor_visibility(priv);
712
0
    switch (pos) {
713
0
    case 0: /* clear from cursor to end of screen */
714
0
      clear_chars(priv, priv->cur.x, priv->cur.y,
715
0
            priv->cols - 1, priv->rows - 1);
716
0
      break;
717
0
    case 1: /* clear from start of screen to cursor */
718
0
      clear_chars(priv, 0, 0, priv->cur.x, priv->cur.y);
719
0
      break;
720
0
    case 2: /* clear entire screen */
721
0
      cls(priv);
722
0
      break;
723
0
    }
724
0
    toggle_cursor_visibility(priv);
725
0
    return true;
726
0
  case 'H':
727
0
    toggle_cursor_visibility(priv);
728
0
    priv->cur.pending_wrap = false;
729
730
0
    pos = simple_strtoul(priv->csi, &end, 10);
731
0
    fbc_set_cursor_row(priv, pos - 1);
732
733
0
    pos = simple_strtoul(end + 1, NULL, 10);
734
0
    fbc_set_cursor_col(priv, pos - 1);
735
736
0
    toggle_cursor_visibility(priv);
737
0
    return true;
738
0
  case 'A' ... 'D': {
739
0
    pos = simple_strtoul(priv->csi, &end, 10) ?: 1;
740
0
    toggle_cursor_visibility(priv);
741
0
    priv->cur.pending_wrap = false;
742
743
0
    switch (last) {
744
0
    case 'A': /* cursor up */
745
0
      fbc_set_cursor_row(priv, priv->cur.y - pos);
746
0
      break;
747
0
    case 'B': /* cursor down */
748
0
      fbc_set_cursor_row(priv, priv->cur.y + pos);
749
0
      break;
750
0
    case 'C': /* cursor forward */
751
0
      fbc_set_cursor_col(priv, priv->cur.x + pos);
752
0
      break;
753
0
    case 'D': /* cursor back */
754
0
      fbc_set_cursor_col(priv, priv->cur.x - pos);
755
0
      break;
756
0
    }
757
758
0
    toggle_cursor_visibility(priv);
759
0
    return true;
760
0
  }
761
0
  case 'K':
762
0
    pos = simple_strtoul(priv->csi, &end, 10);
763
0
    toggle_cursor_visibility(priv);
764
0
    switch (pos) {
765
0
    case 0:
766
0
      clear_chars(priv, priv->cur.x, priv->cur.y,
767
0
            priv->cols - 1, priv->cur.y);
768
0
      break;
769
0
    case 1:
770
0
      clear_chars(priv, 0, priv->cur.y,
771
0
            priv->cur.x, priv->cur.y);
772
0
      break;
773
0
    case 2:
774
0
      clear_chars(priv, 0, priv->cur.y,
775
0
            priv->cols - 1, priv->cur.y);
776
0
      break;
777
0
    }
778
0
    toggle_cursor_visibility(priv);
779
0
    return true;
780
0
  }
781
782
0
  return false;
783
0
}
784
785
static void fbc_putc(struct console_device *cdev, char c)
786
0
{
787
0
  struct fbc_priv *priv = container_of(cdev,
788
0
          struct fbc_priv, cdev);
789
0
  struct fb_info *fb = priv->fb;
790
0
  bool queue_flush = false;
791
792
0
  if (priv->in_console)
793
0
    return;
794
0
  priv->in_console = 1;
795
796
0
  switch (priv->state) {
797
0
  case LIT:
798
0
    switch (c) {
799
0
    case '\033':
800
0
      priv->utf8_buf[0] = '\0';
801
0
      priv->state = ESC;
802
0
      break;
803
0
    case '\0':
804
0
      break;
805
0
    default: {
806
0
      int cp = c;
807
808
0
      if (IS_ENABLED(CONFIG_FRAMEBUFFER_CONSOLE_UTF8)) {
809
        /* All our fonts are CP437, so convert it
810
         * directly to that code page.
811
         */
812
0
        cp = utf8_to_cp437_stream(c, priv->utf8_buf);
813
0
        if (!cp)
814
0
          break;
815
0
      }
816
817
0
      printchar(priv, cp);
818
0
      queue_flush = true;
819
0
      break;
820
0
    }
821
0
    }
822
0
    break;
823
0
  case ESC:
824
0
    switch (c) {
825
0
    case '[':
826
0
      priv->state = CSI;
827
0
      priv->csipos = 0;
828
0
      memset(priv->csi, 0, 6);
829
0
      break;
830
0
    case '7': /* DEC save cursor position */
831
0
      priv->saved = priv->cur;
832
0
      priv->state = LIT;
833
0
      break;
834
0
    case '8': /* DEC restore cursor position */
835
0
      toggle_cursor_visibility(priv);
836
0
      priv->cur = priv->saved;
837
0
      toggle_cursor_visibility(priv);
838
0
      priv->state = LIT;
839
0
      queue_flush = true;
840
0
      break;
841
0
    default:
842
0
      priv->state = LIT;
843
0
      break;
844
0
    }
845
0
    break;
846
0
  case CSI:
847
0
    if (c == '\033') {
848
0
      priv->state = ESC;
849
0
      priv->csi_cmd = -1;
850
0
      break;
851
0
    }
852
853
0
    priv->csi[priv->csipos++] = c;
854
0
    if (priv->csipos == 255) {
855
0
      priv->csipos = 0;
856
0
      priv->state = LIT;
857
0
      priv->csi_cmd = -1;
858
0
      break;
859
0
    }
860
861
0
    switch (c) {
862
0
    case '?':
863
      /* DEC private sequences use '?' as a parameter prefix byte.
864
       * Record '?' in csi_cmd and continue CSI parameter
865
       * accumulation for digits and the final byte that follows.
866
       */
867
0
      priv->csi_cmd = c;
868
0
      break;
869
0
    case '0' ... '9':
870
0
    case ';':
871
0
    case ':':
872
0
      break;
873
0
    default:
874
0
      queue_flush = fbc_parse_csi(priv);
875
0
      priv->state = LIT;
876
0
      priv->csi_cmd = -1;
877
0
    }
878
0
    break;
879
0
  }
880
0
  priv->in_console = 0;
881
882
0
  if (queue_flush)
883
0
    fb_flush(fb);
884
0
}
885
886
static int setup_font(struct fbc_priv *priv)
887
0
{
888
0
  const struct font_desc *font;
889
0
  unsigned int height = priv->fb->yres - priv->margin.top - priv->margin.bottom;
890
0
  unsigned int width = priv->fb->xres - priv->margin.left - priv->margin.right;
891
0
  unsigned int newrows, newcols;
892
893
0
  font = find_font_enum(priv->par_font_val);
894
0
  if (!font) {
895
0
    return -ENOENT;
896
0
  }
897
898
0
  priv->font = font;
899
900
0
  switch (priv->rotation) {
901
0
  case FBCONSOLE_ROTATE_0:
902
0
  case FBCONSOLE_ROTATE_180:
903
0
    newrows = height / priv->font->height;
904
0
    newcols = width / priv->font->width;
905
0
    break;
906
0
  case FBCONSOLE_ROTATE_90:
907
0
  case FBCONSOLE_ROTATE_270:
908
0
    newrows = width / priv->font->height;
909
0
    newcols = height / priv->font->width;
910
0
    break;
911
0
  default:
912
0
    return -EINVAL;
913
0
  }
914
915
0
  if (priv->rows != newrows || priv->cols != newcols) {
916
0
    priv->rows = newrows;
917
0
    priv->cols = newcols;
918
0
    priv->cur.x = priv->cur.y = 0;
919
0
    priv->saved.x = priv->saved.y = 0;
920
0
  }
921
922
0
  return 0;
923
0
}
924
925
static int fbc_open(struct console_device *cdev, unsigned activate)
926
0
{
927
0
  struct fbc_priv *priv = container_of(cdev,
928
0
          struct fbc_priv, cdev);
929
0
  struct fb_info *fb = priv->fb;
930
0
  int ret;
931
932
0
  ret = setup_font(priv);
933
0
  if (ret)
934
0
    return ret;
935
936
0
  priv->sc = fb_create_screen(fb);
937
0
  if (IS_ERR(priv->sc))
938
0
    return PTR_ERR(priv->sc);
939
940
0
  fb_enable(fb);
941
942
0
  priv->state = LIT;
943
944
0
  priv->active = true;
945
946
0
  if (activate)
947
0
    dev_info(priv->cdev.dev, "framebuffer console %dx%d activated\n",
948
0
      priv->cols, priv->rows);
949
950
0
  if (activate & CONSOLE_STDERR)
951
0
    log_print(&priv->cdev, 0, GENMASK(LOGLEVEL, 0));
952
953
0
  return 0;
954
0
}
955
956
static int fbc_close(struct console_device *cdev)
957
0
{
958
0
  struct fbc_priv *priv = container_of(cdev,
959
0
          struct fbc_priv, cdev);
960
961
0
  if (priv->altscreen_buf) {
962
0
    free(priv->altscreen_buf);
963
0
    priv->altscreen_buf  = NULL;
964
0
    priv->altscreen_size = 0;
965
0
  }
966
967
0
  if (priv->active) {
968
0
    fb_close(priv->sc);
969
0
    priv->active = false;
970
971
0
    return 0;
972
0
  }
973
974
0
  return -EINVAL;
975
0
}
976
977
static int set_font(struct param_d *p, void *vpriv)
978
0
{
979
0
  struct fbc_priv *priv = vpriv;
980
0
  struct console_device *cdev = &priv->cdev;
981
982
0
  if (cdev->f_active & (CONSOLE_STDOUT | CONSOLE_STDERR)) {
983
0
    cls(priv);
984
0
    setup_font(priv);
985
0
  }
986
987
0
  return 0;
988
0
}
989
990
static int set_margin(struct param_d *p, void *vpriv)
991
0
{
992
0
  struct fbc_priv *priv = vpriv;
993
0
  struct console_device *cdev = &priv->cdev;
994
0
  int ret;
995
996
0
  if (!priv->font) {
997
0
    ret = setup_font(priv);
998
0
    if (ret)
999
0
      return ret;
1000
0
  }
1001
1002
0
  priv->margin.left = min(priv->margin.left,
1003
0
      priv->fb->xres - priv->margin.right - priv->font->width);
1004
0
  priv->margin.top = min(priv->margin.top,
1005
0
      priv->fb->yres - priv->margin.bottom - priv->font->height);
1006
0
  priv->margin.right = min(priv->margin.right,
1007
0
      priv->fb->xres - priv->margin.left - priv->font->width);
1008
0
  priv->margin.bottom = min(priv->margin.bottom,
1009
0
      priv->fb->yres - priv->margin.top - priv->font->height);
1010
1011
0
  if (cdev->f_active & (CONSOLE_STDOUT | CONSOLE_STDERR)) {
1012
0
    cls(priv);
1013
0
    setup_font(priv);
1014
0
  }
1015
1016
0
  return 0;
1017
0
}
1018
1019
static int set_rotation(struct param_d *p, void *vpriv)
1020
0
{
1021
0
  struct fbc_priv *priv = vpriv;
1022
1023
0
  cls(priv);
1024
0
  priv->cur.x = priv->cur.y = 0;
1025
0
  priv->saved.x = priv->saved.y = 0;
1026
0
  setup_font(priv);
1027
1028
0
  return 0;
1029
0
}
1030
1031
int register_fbconsole(struct fb_info *fb)
1032
0
{
1033
0
  struct fbc_priv *priv;
1034
0
  struct console_device *cdev;
1035
0
  int ret;
1036
0
  const char *fbname;
1037
1038
0
  priv = xzalloc(sizeof(*priv));
1039
1040
  /*
1041
   * fbconsoles are usually just numbered and happen to use the same
1042
   * number as the fb they are registered on, so we have a nice match.
1043
   * With overlays we can also have fb names like fb0_0 (for the first
1044
   * overlay on fb0), and we want to have this name in the corresponding
1045
   * fbconsole. Skip the "fb" though to keep the existing fbconsole name
1046
   * for the non overlay framebuffers for compatibility.
1047
   */
1048
0
  fbname = dev_name(&fb->dev);
1049
0
  if (strncmp(fbname, "fb", 2))
1050
0
    pr_notice("unexpected fb name %s\n", fbname);
1051
0
  else
1052
0
    fbname += 2;
1053
1054
0
  priv->fb = fb;
1055
0
  priv->cur.x = 0;
1056
0
  priv->cur.y = 0;
1057
1058
0
  fbc_reset_colors(priv);
1059
1060
0
  cdev = &priv->cdev;
1061
0
  cdev->dev = &fb->dev;
1062
0
  cdev->tstc = fbc_tstc;
1063
0
  cdev->putc = fbc_putc;
1064
0
  cdev->getc = fbc_getc;
1065
0
  cdev->get_size = fbc_get_size;
1066
0
  cdev->devname = basprintf("fbconsole%s", fbname);
1067
0
  cdev->devid = DEVICE_ID_SINGLE;
1068
0
  cdev->open = fbc_open;
1069
0
  cdev->close = fbc_close;
1070
1071
0
  ret = console_register(cdev);
1072
0
  if (ret) {
1073
0
    pr_err("registering failed with %pe\n", ERR_PTR(ret));
1074
0
    kfree(priv);
1075
0
    return ret;
1076
0
  }
1077
1078
0
  priv->par_font_val = 0;
1079
0
  priv->par_font = add_param_font(&cdev->class_dev,
1080
0
      set_font, NULL,
1081
0
      &priv->par_font_val, priv);
1082
1083
0
  dev_add_param_uint32(&cdev->class_dev, "margin.top", set_margin,
1084
0
      NULL, &priv->margin.top, "%u", priv);
1085
0
  dev_add_param_uint32(&cdev->class_dev, "margin.left", set_margin,
1086
0
      NULL, &priv->margin.left, "%u", priv);
1087
0
  dev_add_param_uint32(&cdev->class_dev, "margin.bottom", set_margin,
1088
0
      NULL, &priv->margin.bottom, "%u", priv);
1089
0
  dev_add_param_uint32(&cdev->class_dev, "margin.right", set_margin,
1090
0
      NULL, &priv->margin.right, "%u", priv);
1091
0
  dev_add_param_enum(&cdev->class_dev, "rotation", set_rotation, NULL,
1092
0
                           &priv->rotation, rotation_names,
1093
0
                           ARRAY_SIZE(rotation_names), priv);
1094
1095
0
  pr_info("registered as %s%d\n", cdev->class_dev.name, cdev->class_dev.id);
1096
1097
0
  return 0;
1098
0
}