17accb6eaSSimon Glass /*
27accb6eaSSimon Glass * Copyright (c) 2011 The Chromium OS Authors.
37accb6eaSSimon Glass *
41a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+
57accb6eaSSimon Glass */
67accb6eaSSimon Glass
77accb6eaSSimon Glass /*
87accb6eaSSimon Glass * This provide a test serial port. It provides an emulated serial port where
97accb6eaSSimon Glass * a test program and read out the serial output and inject serial input for
107accb6eaSSimon Glass * U-Boot.
117accb6eaSSimon Glass */
127accb6eaSSimon Glass
137accb6eaSSimon Glass #include <common.h>
14890fcefeSSimon Glass #include <dm.h>
15890fcefeSSimon Glass #include <fdtdec.h>
167d95f2a3SSimon Glass #include <lcd.h>
177accb6eaSSimon Glass #include <os.h>
18cef46b77SMarek Vasut #include <serial.h>
193ade5bc4SSimon Glass #include <video.h>
20cef46b77SMarek Vasut #include <linux/compiler.h>
21ffb87905SSimon Glass #include <asm/state.h>
227accb6eaSSimon Glass
23890fcefeSSimon Glass DECLARE_GLOBAL_DATA_PTR;
24890fcefeSSimon Glass
25e101550aSTaylor Hutt /*
26e101550aSTaylor Hutt *
27e101550aSTaylor Hutt * serial_buf: A buffer that holds keyboard characters for the
28a187559eSBin Meng * Sandbox U-Boot.
29e101550aSTaylor Hutt *
30e101550aSTaylor Hutt * invariants:
31e101550aSTaylor Hutt * serial_buf_write == serial_buf_read -> empty buffer
32e101550aSTaylor Hutt * (serial_buf_write + 1) % 16 == serial_buf_read -> full buffer
33e101550aSTaylor Hutt */
34e101550aSTaylor Hutt static char serial_buf[16];
35e101550aSTaylor Hutt static unsigned int serial_buf_write;
36e101550aSTaylor Hutt static unsigned int serial_buf_read;
37e101550aSTaylor Hutt
3872e98228SSimon Glass struct sandbox_serial_platdata {
3972e98228SSimon Glass int colour; /* Text colour to use for output, -1 for none */
4072e98228SSimon Glass };
4172e98228SSimon Glass
4272e98228SSimon Glass struct sandbox_serial_priv {
4372e98228SSimon Glass bool start_of_line;
4472e98228SSimon Glass };
4572e98228SSimon Glass
4672e98228SSimon Glass /**
4772e98228SSimon Glass * output_ansi_colour() - Output an ANSI colour code
4872e98228SSimon Glass *
4972e98228SSimon Glass * @colour: Colour to output (0-7)
5072e98228SSimon Glass */
output_ansi_colour(int colour)5172e98228SSimon Glass static void output_ansi_colour(int colour)
5272e98228SSimon Glass {
5372e98228SSimon Glass char ansi_code[] = "\x1b[1;3Xm";
5472e98228SSimon Glass
5572e98228SSimon Glass ansi_code[5] = '0' + colour;
5672e98228SSimon Glass os_write(1, ansi_code, sizeof(ansi_code) - 1);
5772e98228SSimon Glass }
5872e98228SSimon Glass
output_ansi_reset(void)5972e98228SSimon Glass static void output_ansi_reset(void)
6072e98228SSimon Glass {
6172e98228SSimon Glass os_write(1, "\x1b[0m", 4);
6272e98228SSimon Glass }
6372e98228SSimon Glass
sandbox_serial_probe(struct udevice * dev)64890fcefeSSimon Glass static int sandbox_serial_probe(struct udevice *dev)
657accb6eaSSimon Glass {
66ffb87905SSimon Glass struct sandbox_state *state = state_get_current();
6772e98228SSimon Glass struct sandbox_serial_priv *priv = dev_get_priv(dev);
68ffb87905SSimon Glass
69ffb87905SSimon Glass if (state->term_raw != STATE_TERM_COOKED)
70ffb87905SSimon Glass os_tty_raw(0, state->term_raw == STATE_TERM_RAW_WITH_SIGS);
7172e98228SSimon Glass priv->start_of_line = 0;
7272e98228SSimon Glass
7372e98228SSimon Glass return 0;
7472e98228SSimon Glass }
7572e98228SSimon Glass
sandbox_serial_remove(struct udevice * dev)7672e98228SSimon Glass static int sandbox_serial_remove(struct udevice *dev)
7772e98228SSimon Glass {
7872e98228SSimon Glass struct sandbox_serial_platdata *plat = dev->platdata;
7972e98228SSimon Glass
8072e98228SSimon Glass if (plat->colour != -1)
8172e98228SSimon Glass output_ansi_reset();
82890fcefeSSimon Glass
837accb6eaSSimon Glass return 0;
847accb6eaSSimon Glass }
857accb6eaSSimon Glass
sandbox_serial_putc(struct udevice * dev,const char ch)86890fcefeSSimon Glass static int sandbox_serial_putc(struct udevice *dev, const char ch)
877accb6eaSSimon Glass {
8872e98228SSimon Glass struct sandbox_serial_priv *priv = dev_get_priv(dev);
8972e98228SSimon Glass struct sandbox_serial_platdata *plat = dev->platdata;
9072e98228SSimon Glass
9172e98228SSimon Glass if (priv->start_of_line && plat->colour != -1) {
9272e98228SSimon Glass priv->start_of_line = false;
9372e98228SSimon Glass output_ansi_colour(plat->colour);
9472e98228SSimon Glass }
9572e98228SSimon Glass
967accb6eaSSimon Glass os_write(1, &ch, 1);
9772e98228SSimon Glass if (ch == '\n')
9872e98228SSimon Glass priv->start_of_line = true;
997accb6eaSSimon Glass
100890fcefeSSimon Glass return 0;
1017accb6eaSSimon Glass }
1027accb6eaSSimon Glass
increment_buffer_index(unsigned int index)103e101550aSTaylor Hutt static unsigned int increment_buffer_index(unsigned int index)
1047accb6eaSSimon Glass {
105e101550aSTaylor Hutt return (index + 1) % ARRAY_SIZE(serial_buf);
1067accb6eaSSimon Glass }
1077accb6eaSSimon Glass
sandbox_serial_pending(struct udevice * dev,bool input)108890fcefeSSimon Glass static int sandbox_serial_pending(struct udevice *dev, bool input)
1097accb6eaSSimon Glass {
110e101550aSTaylor Hutt const unsigned int next_index =
111e101550aSTaylor Hutt increment_buffer_index(serial_buf_write);
112e101550aSTaylor Hutt ssize_t count;
113e101550aSTaylor Hutt
114890fcefeSSimon Glass if (!input)
115890fcefeSSimon Glass return 0;
116890fcefeSSimon Glass
117e101550aSTaylor Hutt os_usleep(100);
1180110f509SSimon Glass #ifndef CONFIG_SPL_BUILD
1193ade5bc4SSimon Glass video_sync_all();
1200110f509SSimon Glass #endif
121e101550aSTaylor Hutt if (next_index == serial_buf_read)
122e101550aSTaylor Hutt return 1; /* buffer full */
123e101550aSTaylor Hutt
124e101550aSTaylor Hutt count = os_read_no_block(0, &serial_buf[serial_buf_write], 1);
125e101550aSTaylor Hutt if (count == 1)
126e101550aSTaylor Hutt serial_buf_write = next_index;
127890fcefeSSimon Glass
128e101550aSTaylor Hutt return serial_buf_write != serial_buf_read;
129e101550aSTaylor Hutt }
130e101550aSTaylor Hutt
sandbox_serial_getc(struct udevice * dev)131890fcefeSSimon Glass static int sandbox_serial_getc(struct udevice *dev)
132e101550aSTaylor Hutt {
133e101550aSTaylor Hutt int result;
134e101550aSTaylor Hutt
135890fcefeSSimon Glass if (!sandbox_serial_pending(dev, true))
136890fcefeSSimon Glass return -EAGAIN; /* buffer empty */
137e101550aSTaylor Hutt
138e101550aSTaylor Hutt result = serial_buf[serial_buf_read];
139e101550aSTaylor Hutt serial_buf_read = increment_buffer_index(serial_buf_read);
140e101550aSTaylor Hutt return result;
1417accb6eaSSimon Glass }
142cef46b77SMarek Vasut
14372e98228SSimon Glass static const char * const ansi_colour[] = {
14472e98228SSimon Glass "black", "red", "green", "yellow", "blue", "megenta", "cyan",
14572e98228SSimon Glass "white",
14672e98228SSimon Glass };
14772e98228SSimon Glass
sandbox_serial_ofdata_to_platdata(struct udevice * dev)14872e98228SSimon Glass static int sandbox_serial_ofdata_to_platdata(struct udevice *dev)
14972e98228SSimon Glass {
15072e98228SSimon Glass struct sandbox_serial_platdata *plat = dev->platdata;
15172e98228SSimon Glass const char *colour;
15272e98228SSimon Glass int i;
15372e98228SSimon Glass
15472e98228SSimon Glass plat->colour = -1;
155*e160f7d4SSimon Glass colour = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
15672e98228SSimon Glass "sandbox,text-colour", NULL);
15772e98228SSimon Glass if (colour) {
15872e98228SSimon Glass for (i = 0; i < ARRAY_SIZE(ansi_colour); i++) {
15972e98228SSimon Glass if (!strcmp(colour, ansi_colour[i])) {
16072e98228SSimon Glass plat->colour = i;
16172e98228SSimon Glass break;
16272e98228SSimon Glass }
16372e98228SSimon Glass }
16472e98228SSimon Glass }
16572e98228SSimon Glass
16672e98228SSimon Glass return 0;
16772e98228SSimon Glass }
16872e98228SSimon Glass
169890fcefeSSimon Glass static const struct dm_serial_ops sandbox_serial_ops = {
170cef46b77SMarek Vasut .putc = sandbox_serial_putc,
171890fcefeSSimon Glass .pending = sandbox_serial_pending,
172cef46b77SMarek Vasut .getc = sandbox_serial_getc,
173cef46b77SMarek Vasut };
174cef46b77SMarek Vasut
175890fcefeSSimon Glass static const struct udevice_id sandbox_serial_ids[] = {
176890fcefeSSimon Glass { .compatible = "sandbox,serial" },
177890fcefeSSimon Glass { }
178890fcefeSSimon Glass };
179cef46b77SMarek Vasut
180890fcefeSSimon Glass U_BOOT_DRIVER(serial_sandbox) = {
181890fcefeSSimon Glass .name = "serial_sandbox",
182890fcefeSSimon Glass .id = UCLASS_SERIAL,
183890fcefeSSimon Glass .of_match = sandbox_serial_ids,
18472e98228SSimon Glass .ofdata_to_platdata = sandbox_serial_ofdata_to_platdata,
18572e98228SSimon Glass .platdata_auto_alloc_size = sizeof(struct sandbox_serial_platdata),
18672e98228SSimon Glass .priv_auto_alloc_size = sizeof(struct sandbox_serial_priv),
187890fcefeSSimon Glass .probe = sandbox_serial_probe,
18872e98228SSimon Glass .remove = sandbox_serial_remove,
189890fcefeSSimon Glass .ops = &sandbox_serial_ops,
190890fcefeSSimon Glass .flags = DM_FLAG_PRE_RELOC,
191890fcefeSSimon Glass };
192890fcefeSSimon Glass
19372e98228SSimon Glass static const struct sandbox_serial_platdata platdata_non_fdt = {
19472e98228SSimon Glass .colour = -1,
19572e98228SSimon Glass };
19672e98228SSimon Glass
197890fcefeSSimon Glass U_BOOT_DEVICE(serial_sandbox_non_fdt) = {
198890fcefeSSimon Glass .name = "serial_sandbox",
19972e98228SSimon Glass .platdata = &platdata_non_fdt,
200890fcefeSSimon Glass };
201