vidconsole-uclass.c 16 KB

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