uboot/drivers/video/vidconsole-uclass.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (c) 2015 Google, Inc
   4 * (C) Copyright 2001-2015
   5 * DENX Software Engineering -- wd@denx.de
   6 * Compulab Ltd - http://compulab.co.il/
   7 * Bernecker & Rainer Industrieelektronik GmbH - http://www.br-automation.com
   8 */
   9
  10#define LOG_CATEGORY UCLASS_VIDEO_CONSOLE
  11
  12#include <common.h>
  13#include <command.h>
  14#include <console.h>
  15#include <log.h>
  16#include <dm.h>
  17#include <video.h>
  18#include <video_console.h>
  19#include <video_font.h>         /* Bitmap font for code page 437 */
  20#include <linux/ctype.h>
  21
  22/* By default we scroll by a single line */
  23#ifndef CONFIG_CONSOLE_SCROLL_LINES
  24#define CONFIG_CONSOLE_SCROLL_LINES 1
  25#endif
  26
  27int vidconsole_putc_xy(struct udevice *dev, uint x, uint y, char ch)
  28{
  29        struct vidconsole_ops *ops = vidconsole_get_ops(dev);
  30
  31        if (!ops->putc_xy)
  32                return -ENOSYS;
  33        return ops->putc_xy(dev, x, y, ch);
  34}
  35
  36int vidconsole_move_rows(struct udevice *dev, uint rowdst, uint rowsrc,
  37                         uint count)
  38{
  39        struct vidconsole_ops *ops = vidconsole_get_ops(dev);
  40
  41        if (!ops->move_rows)
  42                return -ENOSYS;
  43        return ops->move_rows(dev, rowdst, rowsrc, count);
  44}
  45
  46int vidconsole_set_row(struct udevice *dev, uint row, int clr)
  47{
  48        struct vidconsole_ops *ops = vidconsole_get_ops(dev);
  49
  50        if (!ops->set_row)
  51                return -ENOSYS;
  52        return ops->set_row(dev, row, clr);
  53}
  54
  55static int vidconsole_entry_start(struct udevice *dev)
  56{
  57        struct vidconsole_ops *ops = vidconsole_get_ops(dev);
  58
  59        if (!ops->entry_start)
  60                return -ENOSYS;
  61        return ops->entry_start(dev);
  62}
  63
  64/* Move backwards one space */
  65static int vidconsole_back(struct udevice *dev)
  66{
  67        struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
  68        struct vidconsole_ops *ops = vidconsole_get_ops(dev);
  69        int ret;
  70
  71        if (ops->backspace) {
  72                ret = ops->backspace(dev);
  73                if (ret != -ENOSYS)
  74                        return ret;
  75        }
  76
  77        priv->xcur_frac -= VID_TO_POS(priv->x_charsize);
  78        if (priv->xcur_frac < priv->xstart_frac) {
  79                priv->xcur_frac = (priv->cols - 1) *
  80                        VID_TO_POS(priv->x_charsize);
  81                priv->ycur -= priv->y_charsize;
  82                if (priv->ycur < 0)
  83                        priv->ycur = 0;
  84        }
  85        return video_sync(dev->parent, false);
  86}
  87
  88/* Move to a newline, scrolling the display if necessary */
  89static void vidconsole_newline(struct udevice *dev)
  90{
  91        struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
  92        struct udevice *vid_dev = dev->parent;
  93        struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
  94        const int rows = CONFIG_CONSOLE_SCROLL_LINES;
  95        int i, ret;
  96
  97        priv->xcur_frac = priv->xstart_frac;
  98        priv->ycur += priv->y_charsize;
  99
 100        /* Check if we need to scroll the terminal */
 101        if ((priv->ycur + priv->y_charsize) / priv->y_charsize > priv->rows) {
 102                vidconsole_move_rows(dev, 0, rows, priv->rows - rows);
 103                for (i = 0; i < rows; i++)
 104                        vidconsole_set_row(dev, priv->rows - i - 1,
 105                                           vid_priv->colour_bg);
 106                priv->ycur -= rows * priv->y_charsize;
 107        }
 108        priv->last_ch = 0;
 109
 110        ret = video_sync(dev->parent, false);
 111        if (ret) {
 112#ifdef DEBUG
 113                console_puts_select_stderr(true, "[vc err: video_sync]");
 114#endif
 115        }
 116}
 117
 118static char *parsenum(char *s, int *num)
 119{
 120        char *end;
 121        *num = simple_strtol(s, &end, 10);
 122        return end;
 123}
 124
 125void vidconsole_set_cursor_pos(struct udevice *dev, int x, int y)
 126{
 127        struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
 128
 129        priv->xcur_frac = VID_TO_POS(x);
 130        priv->xstart_frac = priv->xcur_frac;
 131        priv->ycur = y;
 132}
 133
 134/**
 135 * set_cursor_position() - set cursor position
 136 *
 137 * @priv:       private data of the video console
 138 * @row:        new row
 139 * @col:        new column
 140 */
 141static void set_cursor_position(struct vidconsole_priv *priv, int row, int col)
 142{
 143        /*
 144         * Ensure we stay in the bounds of the screen.
 145         */
 146        if (row >= priv->rows)
 147                row = priv->rows - 1;
 148        if (col >= priv->cols)
 149                col = priv->cols - 1;
 150
 151        priv->ycur = row * priv->y_charsize;
 152        priv->xcur_frac = priv->xstart_frac +
 153                          VID_TO_POS(col * priv->x_charsize);
 154}
 155
 156/**
 157 * get_cursor_position() - get cursor position
 158 *
 159 * @priv:       private data of the video console
 160 * @row:        row
 161 * @col:        column
 162 */
 163static void get_cursor_position(struct vidconsole_priv *priv,
 164                                int *row, int *col)
 165{
 166        *row = priv->ycur / priv->y_charsize;
 167        *col = VID_TO_PIXEL(priv->xcur_frac - priv->xstart_frac) /
 168               priv->x_charsize;
 169}
 170
 171/*
 172 * Process a character while accumulating an escape string.  Chars are
 173 * accumulated into escape_buf until the end of escape sequence is
 174 * found, at which point the sequence is parsed and processed.
 175 */
 176static void vidconsole_escape_char(struct udevice *dev, char ch)
 177{
 178        struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
 179
 180        if (!IS_ENABLED(CONFIG_VIDEO_ANSI))
 181                goto error;
 182
 183        /* Sanity checking for bogus ESC sequences: */
 184        if (priv->escape_len >= sizeof(priv->escape_buf))
 185                goto error;
 186        if (priv->escape_len == 0) {
 187                switch (ch) {
 188                case '7':
 189                        /* Save cursor position */
 190                        get_cursor_position(priv, &priv->row_saved,
 191                                            &priv->col_saved);
 192                        priv->escape = 0;
 193
 194                        return;
 195                case '8': {
 196                        /* Restore cursor position */
 197                        int row = priv->row_saved;
 198                        int col = priv->col_saved;
 199
 200                        set_cursor_position(priv, row, col);
 201                        priv->escape = 0;
 202                        return;
 203                }
 204                case '[':
 205                        break;
 206                default:
 207                        goto error;
 208                }
 209        }
 210
 211        priv->escape_buf[priv->escape_len++] = ch;
 212
 213        /*
 214         * Escape sequences are terminated by a letter, so keep
 215         * accumulating until we get one:
 216         */
 217        if (!isalpha(ch))
 218                return;
 219
 220        /*
 221         * clear escape mode first, otherwise things will get highly
 222         * surprising if you hit any debug prints that come back to
 223         * this console.
 224         */
 225        priv->escape = 0;
 226
 227        switch (ch) {
 228        case 'A':
 229        case 'B':
 230        case 'C':
 231        case 'D':
 232        case 'E':
 233        case 'F': {
 234                int row, col, num;
 235                char *s = priv->escape_buf;
 236
 237                /*
 238                 * Cursor up/down: [%dA, [%dB, [%dE, [%dF
 239                 * Cursor left/right: [%dD, [%dC
 240                 */
 241                s++;    /* [ */
 242                s = parsenum(s, &num);
 243                if (num == 0)                   /* No digit in sequence ... */
 244                        num = 1;                /* ... means "move by 1". */
 245
 246                get_cursor_position(priv, &row, &col);
 247                if (ch == 'A' || ch == 'F')
 248                        row -= num;
 249                if (ch == 'C')
 250                        col += num;
 251                if (ch == 'D')
 252                        col -= num;
 253                if (ch == 'B' || ch == 'E')
 254                        row += num;
 255                if (ch == 'E' || ch == 'F')
 256                        col = 0;
 257                if (col < 0)
 258                        col = 0;
 259                if (row < 0)
 260                        row = 0;
 261                /* Right and bottom overflows are handled in the callee. */
 262                set_cursor_position(priv, row, col);
 263                break;
 264        }
 265        case 'H':
 266        case 'f': {
 267                int row, col;
 268                char *s = priv->escape_buf;
 269
 270                /*
 271                 * Set cursor position: [%d;%df or [%d;%dH
 272                 */
 273                s++;    /* [ */
 274                s = parsenum(s, &row);
 275                s++;    /* ; */
 276                s = parsenum(s, &col);
 277
 278                /*
 279                 * Video origin is [0, 0], terminal origin is [1, 1].
 280                 */
 281                if (row)
 282                        --row;
 283                if (col)
 284                        --col;
 285
 286                set_cursor_position(priv, row, col);
 287
 288                break;
 289        }
 290        case 'J': {
 291                int mode;
 292
 293                /*
 294                 * Clear part/all screen:
 295                 *   [J or [0J - clear screen from cursor down
 296                 *   [1J       - clear screen from cursor up
 297                 *   [2J       - clear entire screen
 298                 *
 299                 * TODO we really only handle entire-screen case, others
 300                 * probably require some additions to video-uclass (and
 301                 * are not really needed yet by efi_console)
 302                 */
 303                parsenum(priv->escape_buf + 1, &mode);
 304
 305                if (mode == 2) {
 306                        int ret;
 307
 308                        video_clear(dev->parent);
 309                        ret = video_sync(dev->parent, false);
 310                        if (ret) {
 311#ifdef DEBUG
 312                                console_puts_select_stderr(true, "[vc err: video_sync]");
 313#endif
 314                        }
 315                        priv->ycur = 0;
 316                        priv->xcur_frac = priv->xstart_frac;
 317                } else {
 318                        debug("unsupported clear mode: %d\n", mode);
 319                }
 320                break;
 321        }
 322        case 'K': {
 323                struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
 324                int mode;
 325
 326                /*
 327                 * Clear (parts of) current line
 328                 *   [0K       - clear line to end
 329                 *   [2K       - clear entire line
 330                 */
 331                parsenum(priv->escape_buf + 1, &mode);
 332
 333                if (mode == 2) {
 334                        int row, col;
 335
 336                        get_cursor_position(priv, &row, &col);
 337                        vidconsole_set_row(dev, row, vid_priv->colour_bg);
 338                }
 339                break;
 340        }
 341        case 'm': {
 342                struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
 343                char *s = priv->escape_buf;
 344                char *end = &priv->escape_buf[priv->escape_len];
 345
 346                /*
 347                 * Set graphics mode: [%d;...;%dm
 348                 *
 349                 * Currently only supports the color attributes:
 350                 *
 351                 * Foreground Colors:
 352                 *
 353                 *   30 Black
 354                 *   31 Red
 355                 *   32 Green
 356                 *   33 Yellow
 357                 *   34 Blue
 358                 *   35 Magenta
 359                 *   36 Cyan
 360                 *   37 White
 361                 *
 362                 * Background Colors:
 363                 *
 364                 *   40 Black
 365                 *   41 Red
 366                 *   42 Green
 367                 *   43 Yellow
 368                 *   44 Blue
 369                 *   45 Magenta
 370                 *   46 Cyan
 371                 *   47 White
 372                 */
 373
 374                s++;    /* [ */
 375                while (s < end) {
 376                        int val;
 377
 378                        s = parsenum(s, &val);
 379                        s++;
 380
 381                        switch (val) {
 382                        case 0:
 383                                /* all attributes off */
 384                                video_set_default_colors(dev->parent, false);
 385                                break;
 386                        case 1:
 387                                /* bold */
 388                                vid_priv->fg_col_idx |= 8;
 389                                vid_priv->colour_fg = video_index_to_colour(
 390                                                vid_priv, vid_priv->fg_col_idx);
 391                                break;
 392                        case 7:
 393                                /* reverse video */
 394                                vid_priv->colour_fg = video_index_to_colour(
 395                                                vid_priv, vid_priv->bg_col_idx);
 396                                vid_priv->colour_bg = video_index_to_colour(
 397                                                vid_priv, vid_priv->fg_col_idx);
 398                                break;
 399                        case 30 ... 37:
 400                                /* foreground color */
 401                                vid_priv->fg_col_idx &= ~7;
 402                                vid_priv->fg_col_idx |= val - 30;
 403                                vid_priv->colour_fg = video_index_to_colour(
 404                                                vid_priv, vid_priv->fg_col_idx);
 405                                break;
 406                        case 40 ... 47:
 407                                /* background color, also mask the bold bit */
 408                                vid_priv->bg_col_idx &= ~0xf;
 409                                vid_priv->bg_col_idx |= val - 40;
 410                                vid_priv->colour_bg = video_index_to_colour(
 411                                                vid_priv, vid_priv->bg_col_idx);
 412                                break;
 413                        default:
 414                                /* ignore unsupported SGR parameter */
 415                                break;
 416                        }
 417                }
 418
 419                break;
 420        }
 421        default:
 422                debug("unrecognized escape sequence: %*s\n",
 423                      priv->escape_len, priv->escape_buf);
 424        }
 425
 426        return;
 427
 428error:
 429        /* something went wrong, just revert to normal mode: */
 430        priv->escape = 0;
 431}
 432
 433/* Put that actual character on the screen (using the CP437 code page). */
 434static int vidconsole_output_glyph(struct udevice *dev, char ch)
 435{
 436        struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
 437        int ret;
 438
 439        /*
 440         * Failure of this function normally indicates an unsupported
 441         * colour depth. Check this and return an error to help with
 442         * diagnosis.
 443         */
 444        ret = vidconsole_putc_xy(dev, priv->xcur_frac, priv->ycur, ch);
 445        if (ret == -EAGAIN) {
 446                vidconsole_newline(dev);
 447                ret = vidconsole_putc_xy(dev, priv->xcur_frac, priv->ycur, ch);
 448        }
 449        if (ret < 0)
 450                return ret;
 451        priv->xcur_frac += ret;
 452        priv->last_ch = ch;
 453        if (priv->xcur_frac >= priv->xsize_frac)
 454                vidconsole_newline(dev);
 455
 456        return 0;
 457}
 458
 459int vidconsole_put_char(struct udevice *dev, char ch)
 460{
 461        struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
 462        int ret;
 463
 464        if (priv->escape) {
 465                vidconsole_escape_char(dev, ch);
 466                return 0;
 467        }
 468
 469        switch (ch) {
 470        case '\x1b':
 471                priv->escape_len = 0;
 472                priv->escape = 1;
 473                break;
 474        case '\a':
 475                /* beep */
 476                break;
 477        case '\r':
 478                priv->xcur_frac = priv->xstart_frac;
 479                break;
 480        case '\n':
 481                vidconsole_newline(dev);
 482                vidconsole_entry_start(dev);
 483                break;
 484        case '\t':      /* Tab (8 chars alignment) */
 485                priv->xcur_frac = ((priv->xcur_frac / priv->tab_width_frac)
 486                                + 1) * priv->tab_width_frac;
 487
 488                if (priv->xcur_frac >= priv->xsize_frac)
 489                        vidconsole_newline(dev);
 490                break;
 491        case '\b':
 492                vidconsole_back(dev);
 493                priv->last_ch = 0;
 494                break;
 495        default:
 496                ret = vidconsole_output_glyph(dev, ch);
 497                if (ret < 0)
 498                        return ret;
 499                break;
 500        }
 501
 502        return 0;
 503}
 504
 505int vidconsole_put_string(struct udevice *dev, const char *str)
 506{
 507        const char *s;
 508        int ret;
 509
 510        for (s = str; *s; s++) {
 511                ret = vidconsole_put_char(dev, *s);
 512                if (ret)
 513                        return ret;
 514        }
 515
 516        return 0;
 517}
 518
 519static void vidconsole_putc(struct stdio_dev *sdev, const char ch)
 520{
 521        struct udevice *dev = sdev->priv;
 522        int ret;
 523
 524        ret = vidconsole_put_char(dev, ch);
 525        if (ret) {
 526#ifdef DEBUG
 527                console_puts_select_stderr(true, "[vc err: putc]");
 528#endif
 529        }
 530        ret = video_sync(dev->parent, false);
 531        if (ret) {
 532#ifdef DEBUG
 533                console_puts_select_stderr(true, "[vc err: video_sync]");
 534#endif
 535        }
 536}
 537
 538static void vidconsole_puts(struct stdio_dev *sdev, const char *s)
 539{
 540        struct udevice *dev = sdev->priv;
 541        int ret;
 542
 543        ret = vidconsole_put_string(dev, s);
 544        if (ret) {
 545#ifdef DEBUG
 546                char str[30];
 547
 548                snprintf(str, sizeof(str), "[vc err: puts %d]", ret);
 549                console_puts_select_stderr(true, str);
 550#endif
 551        }
 552        ret = video_sync(dev->parent, false);
 553        if (ret) {
 554#ifdef DEBUG
 555                console_puts_select_stderr(true, "[vc err: video_sync]");
 556#endif
 557        }
 558}
 559
 560/* Set up the number of rows and colours (rotated drivers override this) */
 561static int vidconsole_pre_probe(struct udevice *dev)
 562{
 563        struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
 564        struct udevice *vid = dev->parent;
 565        struct video_priv *vid_priv = dev_get_uclass_priv(vid);
 566
 567        priv->xsize_frac = VID_TO_POS(vid_priv->xsize);
 568
 569        return 0;
 570}
 571
 572/* Register the device with stdio */
 573static int vidconsole_post_probe(struct udevice *dev)
 574{
 575        struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
 576        struct stdio_dev *sdev = &priv->sdev;
 577
 578        if (!priv->tab_width_frac)
 579                priv->tab_width_frac = VID_TO_POS(priv->x_charsize) * 8;
 580
 581        if (dev_seq(dev)) {
 582                snprintf(sdev->name, sizeof(sdev->name), "vidconsole%d",
 583                         dev_seq(dev));
 584        } else {
 585                strcpy(sdev->name, "vidconsole");
 586        }
 587
 588        sdev->flags = DEV_FLAGS_OUTPUT;
 589        sdev->putc = vidconsole_putc;
 590        sdev->puts = vidconsole_puts;
 591        sdev->priv = dev;
 592
 593        return stdio_register(sdev);
 594}
 595
 596UCLASS_DRIVER(vidconsole) = {
 597        .id             = UCLASS_VIDEO_CONSOLE,
 598        .name           = "vidconsole0",
 599        .pre_probe      = vidconsole_pre_probe,
 600        .post_probe     = vidconsole_post_probe,
 601        .per_device_auto        = sizeof(struct vidconsole_priv),
 602};
 603
 604#ifdef CONFIG_VIDEO_COPY
 605int vidconsole_sync_copy(struct udevice *dev, void *from, void *to)
 606{
 607        struct udevice *vid = dev_get_parent(dev);
 608
 609        return video_sync_copy(vid, from, to);
 610}
 611
 612int vidconsole_memmove(struct udevice *dev, void *dst, const void *src,
 613                       int size)
 614{
 615        memmove(dst, src, size);
 616        return vidconsole_sync_copy(dev, dst, dst + size);
 617}
 618#endif
 619
 620void vidconsole_position_cursor(struct udevice *dev, unsigned col, unsigned row)
 621{
 622        struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
 623        struct udevice *vid_dev = dev->parent;
 624        struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
 625        short x, y;
 626
 627        x = min_t(short, col * priv->x_charsize, vid_priv->xsize - 1);
 628        y = min_t(short, row * priv->y_charsize, vid_priv->ysize - 1);
 629        vidconsole_set_cursor_pos(dev, x, y);
 630}
 631