vidconsole-uclass.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. /*
  2. * Copyright (c) 2015 Google, Inc
  3. * (C) Copyright 2001-2015
  4. * DENX Software Engineering -- wd@denx.de
  5. * Compulab Ltd - http://compulab.co.il/
  6. * Bernecker & Rainer Industrieelektronik GmbH - http://www.br-automation.com
  7. *
  8. * SPDX-License-Identifier: GPL-2.0+
  9. */
  10. #include <common.h>
  11. #include <linux/ctype.h>
  12. #include <dm.h>
  13. #include <video.h>
  14. #include <video_console.h>
  15. #include <video_font.h> /* Bitmap font for code page 437 */
  16. /*
  17. * Structure to describe a console color
  18. */
  19. struct vid_rgb {
  20. u32 r;
  21. u32 g;
  22. u32 b;
  23. };
  24. /* By default we scroll by a single line */
  25. #ifndef CONFIG_CONSOLE_SCROLL_LINES
  26. #define CONFIG_CONSOLE_SCROLL_LINES 1
  27. #endif
  28. int vidconsole_putc_xy(struct udevice *dev, uint x, uint y, char ch)
  29. {
  30. struct vidconsole_ops *ops = vidconsole_get_ops(dev);
  31. if (!ops->putc_xy)
  32. return -ENOSYS;
  33. return ops->putc_xy(dev, x, y, ch);
  34. }
  35. int vidconsole_move_rows(struct udevice *dev, uint rowdst, uint rowsrc,
  36. uint count)
  37. {
  38. struct vidconsole_ops *ops = vidconsole_get_ops(dev);
  39. if (!ops->move_rows)
  40. return -ENOSYS;
  41. return ops->move_rows(dev, rowdst, rowsrc, count);
  42. }
  43. int vidconsole_set_row(struct udevice *dev, uint row, int clr)
  44. {
  45. struct vidconsole_ops *ops = vidconsole_get_ops(dev);
  46. if (!ops->set_row)
  47. return -ENOSYS;
  48. return ops->set_row(dev, row, clr);
  49. }
  50. static int vidconsole_entry_start(struct udevice *dev)
  51. {
  52. struct vidconsole_ops *ops = vidconsole_get_ops(dev);
  53. if (!ops->entry_start)
  54. return -ENOSYS;
  55. return ops->entry_start(dev);
  56. }
  57. /* Move backwards one space */
  58. static int vidconsole_back(struct udevice *dev)
  59. {
  60. struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
  61. struct vidconsole_ops *ops = vidconsole_get_ops(dev);
  62. int ret;
  63. if (ops->backspace) {
  64. ret = ops->backspace(dev);
  65. if (ret != -ENOSYS)
  66. return ret;
  67. }
  68. priv->xcur_frac -= VID_TO_POS(priv->x_charsize);
  69. if (priv->xcur_frac < priv->xstart_frac) {
  70. priv->xcur_frac = (priv->cols - 1) *
  71. VID_TO_POS(priv->x_charsize);
  72. priv->ycur -= priv->y_charsize;
  73. if (priv->ycur < 0)
  74. priv->ycur = 0;
  75. }
  76. video_sync(dev->parent);
  77. return 0;
  78. }
  79. /* Move to a newline, scrolling the display if necessary */
  80. static void vidconsole_newline(struct udevice *dev)
  81. {
  82. struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
  83. struct udevice *vid_dev = dev->parent;
  84. struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
  85. const int rows = CONFIG_CONSOLE_SCROLL_LINES;
  86. int i;
  87. priv->xcur_frac = priv->xstart_frac;
  88. priv->ycur += priv->y_charsize;
  89. /* Check if we need to scroll the terminal */
  90. if ((priv->ycur + priv->y_charsize) / priv->y_charsize > priv->rows) {
  91. vidconsole_move_rows(dev, 0, rows, priv->rows - rows);
  92. for (i = 0; i < rows; i++)
  93. vidconsole_set_row(dev, priv->rows - i - 1,
  94. vid_priv->colour_bg);
  95. priv->ycur -= rows * priv->y_charsize;
  96. }
  97. priv->last_ch = 0;
  98. video_sync(dev->parent);
  99. }
  100. static const struct vid_rgb colors[VID_COLOR_COUNT] = {
  101. { 0x00, 0x00, 0x00 }, /* black */
  102. { 0xc0, 0x00, 0x00 }, /* red */
  103. { 0x00, 0xc0, 0x00 }, /* green */
  104. { 0xc0, 0x60, 0x00 }, /* brown */
  105. { 0x00, 0x00, 0xc0 }, /* blue */
  106. { 0xc0, 0x00, 0xc0 }, /* magenta */
  107. { 0x00, 0xc0, 0xc0 }, /* cyan */
  108. { 0xc0, 0xc0, 0xc0 }, /* light gray */
  109. { 0x80, 0x80, 0x80 }, /* gray */
  110. { 0xff, 0x00, 0x00 }, /* bright red */
  111. { 0x00, 0xff, 0x00 }, /* bright green */
  112. { 0xff, 0xff, 0x00 }, /* yellow */
  113. { 0x00, 0x00, 0xff }, /* bright blue */
  114. { 0xff, 0x00, 0xff }, /* bright magenta */
  115. { 0x00, 0xff, 0xff }, /* bright cyan */
  116. { 0xff, 0xff, 0xff }, /* white */
  117. };
  118. u32 vid_console_color(struct video_priv *priv, unsigned int idx)
  119. {
  120. switch (priv->bpix) {
  121. case VIDEO_BPP16:
  122. return ((colors[idx].r >> 3) << 11) |
  123. ((colors[idx].g >> 2) << 5) |
  124. ((colors[idx].b >> 3) << 0);
  125. case VIDEO_BPP32:
  126. return (colors[idx].r << 16) |
  127. (colors[idx].g << 8) |
  128. (colors[idx].b << 0);
  129. default:
  130. /*
  131. * For unknown bit arrangements just support
  132. * black and white.
  133. */
  134. if (idx)
  135. return 0xffffff; /* white */
  136. else
  137. return 0x000000; /* black */
  138. }
  139. }
  140. static char *parsenum(char *s, int *num)
  141. {
  142. char *end;
  143. *num = simple_strtol(s, &end, 10);
  144. return end;
  145. }
  146. /*
  147. * Process a character while accumulating an escape string. Chars are
  148. * accumulated into escape_buf until the end of escape sequence is
  149. * found, at which point the sequence is parsed and processed.
  150. */
  151. static void vidconsole_escape_char(struct udevice *dev, char ch)
  152. {
  153. struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
  154. if (!IS_ENABLED(CONFIG_VIDEO_ANSI))
  155. goto error;
  156. /* Sanity checking for bogus ESC sequences: */
  157. if (priv->escape_len >= sizeof(priv->escape_buf))
  158. goto error;
  159. if (priv->escape_len == 0 && ch != '[')
  160. goto error;
  161. priv->escape_buf[priv->escape_len++] = ch;
  162. /*
  163. * Escape sequences are terminated by a letter, so keep
  164. * accumulating until we get one:
  165. */
  166. if (!isalpha(ch))
  167. return;
  168. /*
  169. * clear escape mode first, otherwise things will get highly
  170. * surprising if you hit any debug prints that come back to
  171. * this console.
  172. */
  173. priv->escape = 0;
  174. switch (ch) {
  175. case 'H':
  176. case 'f': {
  177. int row, col;
  178. char *s = priv->escape_buf;
  179. /*
  180. * Set cursor position: [%d;%df or [%d;%dH
  181. */
  182. s++; /* [ */
  183. s = parsenum(s, &row);
  184. s++; /* ; */
  185. s = parsenum(s, &col);
  186. priv->ycur = row * priv->y_charsize;
  187. priv->xcur_frac = priv->xstart_frac +
  188. VID_TO_POS(col * priv->x_charsize);
  189. break;
  190. }
  191. case 'J': {
  192. int mode;
  193. /*
  194. * Clear part/all screen:
  195. * [J or [0J - clear screen from cursor down
  196. * [1J - clear screen from cursor up
  197. * [2J - clear entire screen
  198. *
  199. * TODO we really only handle entire-screen case, others
  200. * probably require some additions to video-uclass (and
  201. * are not really needed yet by efi_console)
  202. */
  203. parsenum(priv->escape_buf + 1, &mode);
  204. if (mode == 2) {
  205. video_clear(dev->parent);
  206. video_sync(dev->parent);
  207. priv->ycur = 0;
  208. priv->xcur_frac = priv->xstart_frac;
  209. } else {
  210. debug("unsupported clear mode: %d\n", mode);
  211. }
  212. break;
  213. }
  214. case 'm': {
  215. struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
  216. char *s = priv->escape_buf;
  217. char *end = &priv->escape_buf[priv->escape_len];
  218. /*
  219. * Set graphics mode: [%d;...;%dm
  220. *
  221. * Currently only supports the color attributes:
  222. *
  223. * Foreground Colors:
  224. *
  225. * 30 Black
  226. * 31 Red
  227. * 32 Green
  228. * 33 Yellow
  229. * 34 Blue
  230. * 35 Magenta
  231. * 36 Cyan
  232. * 37 White
  233. *
  234. * Background Colors:
  235. *
  236. * 40 Black
  237. * 41 Red
  238. * 42 Green
  239. * 43 Yellow
  240. * 44 Blue
  241. * 45 Magenta
  242. * 46 Cyan
  243. * 47 White
  244. */
  245. s++; /* [ */
  246. while (s < end) {
  247. int val;
  248. s = parsenum(s, &val);
  249. s++;
  250. switch (val) {
  251. case 0:
  252. /* all attributes off */
  253. video_set_default_colors(vid_priv);
  254. break;
  255. case 1:
  256. /* bold */
  257. vid_priv->fg_col_idx |= 8;
  258. vid_priv->colour_fg = vid_console_color(
  259. vid_priv, vid_priv->fg_col_idx);
  260. break;
  261. case 30 ... 37:
  262. /* foreground color */
  263. vid_priv->fg_col_idx &= ~7;
  264. vid_priv->fg_col_idx |= val - 30;
  265. vid_priv->colour_fg = vid_console_color(
  266. vid_priv, vid_priv->fg_col_idx);
  267. break;
  268. case 40 ... 47:
  269. /* background color */
  270. vid_priv->colour_bg = vid_console_color(
  271. vid_priv, val - 40);
  272. break;
  273. default:
  274. /* ignore unsupported SGR parameter */
  275. break;
  276. }
  277. }
  278. break;
  279. }
  280. default:
  281. debug("unrecognized escape sequence: %*s\n",
  282. priv->escape_len, priv->escape_buf);
  283. }
  284. return;
  285. error:
  286. /* something went wrong, just revert to normal mode: */
  287. priv->escape = 0;
  288. }
  289. int vidconsole_put_char(struct udevice *dev, char ch)
  290. {
  291. struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
  292. int ret;
  293. if (priv->escape) {
  294. vidconsole_escape_char(dev, ch);
  295. return 0;
  296. }
  297. switch (ch) {
  298. case '\x1b':
  299. priv->escape_len = 0;
  300. priv->escape = 1;
  301. break;
  302. case '\a':
  303. /* beep */
  304. break;
  305. case '\r':
  306. priv->xcur_frac = priv->xstart_frac;
  307. break;
  308. case '\n':
  309. vidconsole_newline(dev);
  310. vidconsole_entry_start(dev);
  311. break;
  312. case '\t': /* Tab (8 chars alignment) */
  313. priv->xcur_frac = ((priv->xcur_frac / priv->tab_width_frac)
  314. + 1) * priv->tab_width_frac;
  315. if (priv->xcur_frac >= priv->xsize_frac)
  316. vidconsole_newline(dev);
  317. break;
  318. case '\b':
  319. vidconsole_back(dev);
  320. priv->last_ch = 0;
  321. break;
  322. default:
  323. /*
  324. * Failure of this function normally indicates an unsupported
  325. * colour depth. Check this and return an error to help with
  326. * diagnosis.
  327. */
  328. ret = vidconsole_putc_xy(dev, priv->xcur_frac, priv->ycur, ch);
  329. if (ret == -EAGAIN) {
  330. vidconsole_newline(dev);
  331. ret = vidconsole_putc_xy(dev, priv->xcur_frac,
  332. priv->ycur, ch);
  333. }
  334. if (ret < 0)
  335. return ret;
  336. priv->xcur_frac += ret;
  337. priv->last_ch = ch;
  338. if (priv->xcur_frac >= priv->xsize_frac)
  339. vidconsole_newline(dev);
  340. break;
  341. }
  342. return 0;
  343. }
  344. static void vidconsole_putc(struct stdio_dev *sdev, const char ch)
  345. {
  346. struct udevice *dev = sdev->priv;
  347. vidconsole_put_char(dev, ch);
  348. video_sync(dev->parent);
  349. }
  350. static void vidconsole_puts(struct stdio_dev *sdev, const char *s)
  351. {
  352. struct udevice *dev = sdev->priv;
  353. while (*s)
  354. vidconsole_put_char(dev, *s++);
  355. video_sync(dev->parent);
  356. }
  357. /* Set up the number of rows and colours (rotated drivers override this) */
  358. static int vidconsole_pre_probe(struct udevice *dev)
  359. {
  360. struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
  361. struct udevice *vid = dev->parent;
  362. struct video_priv *vid_priv = dev_get_uclass_priv(vid);
  363. priv->xsize_frac = VID_TO_POS(vid_priv->xsize);
  364. return 0;
  365. }
  366. /* Register the device with stdio */
  367. static int vidconsole_post_probe(struct udevice *dev)
  368. {
  369. struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
  370. struct stdio_dev *sdev = &priv->sdev;
  371. if (!priv->tab_width_frac)
  372. priv->tab_width_frac = VID_TO_POS(priv->x_charsize) * 8;
  373. if (dev->seq) {
  374. snprintf(sdev->name, sizeof(sdev->name), "vidconsole%d",
  375. dev->seq);
  376. } else {
  377. strcpy(sdev->name, "vidconsole");
  378. }
  379. sdev->flags = DEV_FLAGS_OUTPUT;
  380. sdev->putc = vidconsole_putc;
  381. sdev->puts = vidconsole_puts;
  382. sdev->priv = dev;
  383. return stdio_register(sdev);
  384. }
  385. UCLASS_DRIVER(vidconsole) = {
  386. .id = UCLASS_VIDEO_CONSOLE,
  387. .name = "vidconsole0",
  388. .pre_probe = vidconsole_pre_probe,
  389. .post_probe = vidconsole_post_probe,
  390. .per_device_auto_alloc_size = sizeof(struct vidconsole_priv),
  391. };
  392. void vidconsole_position_cursor(struct udevice *dev, unsigned col, unsigned row)
  393. {
  394. struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
  395. struct udevice *vid_dev = dev->parent;
  396. struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
  397. priv->xcur_frac = VID_TO_POS(min_t(short, col, vid_priv->xsize - 1));
  398. priv->ycur = min_t(short, row, vid_priv->ysize - 1);
  399. }
  400. static int do_video_setcursor(cmd_tbl_t *cmdtp, int flag, int argc,
  401. char *const argv[])
  402. {
  403. unsigned int col, row;
  404. struct udevice *dev;
  405. if (argc != 3)
  406. return CMD_RET_USAGE;
  407. if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
  408. return CMD_RET_FAILURE;
  409. col = simple_strtoul(argv[1], NULL, 10);
  410. row = simple_strtoul(argv[2], NULL, 10);
  411. vidconsole_position_cursor(dev, col, row);
  412. return 0;
  413. }
  414. static int do_video_puts(cmd_tbl_t *cmdtp, int flag, int argc,
  415. char *const argv[])
  416. {
  417. struct udevice *dev;
  418. const char *s;
  419. if (argc != 2)
  420. return CMD_RET_USAGE;
  421. if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
  422. return CMD_RET_FAILURE;
  423. for (s = argv[1]; *s; s++)
  424. vidconsole_put_char(dev, *s);
  425. video_sync(dev->parent);
  426. return 0;
  427. }
  428. U_BOOT_CMD(
  429. setcurs, 3, 1, do_video_setcursor,
  430. "set cursor position within screen",
  431. " <col> <row> in character"
  432. );
  433. U_BOOT_CMD(
  434. lcdputs, 2, 1, do_video_puts,
  435. "print string on video framebuffer",
  436. " <string>"
  437. );