]>
Commit | Line | Data |
---|---|---|
7accb6ea SG |
1 | /* |
2 | * Copyright (c) 2011 The Chromium OS Authors. | |
7accb6ea | 3 | * |
1a459660 | 4 | * SPDX-License-Identifier: GPL-2.0+ |
7accb6ea SG |
5 | */ |
6 | ||
7 | /* | |
8 | * This provide a test serial port. It provides an emulated serial port where | |
9 | * a test program and read out the serial output and inject serial input for | |
10 | * U-Boot. | |
11 | */ | |
12 | ||
13 | #include <common.h> | |
890fcefe SG |
14 | #include <dm.h> |
15 | #include <fdtdec.h> | |
7d95f2a3 | 16 | #include <lcd.h> |
7accb6ea | 17 | #include <os.h> |
cef46b77 | 18 | #include <serial.h> |
3ade5bc4 | 19 | #include <video.h> |
cef46b77 | 20 | #include <linux/compiler.h> |
ffb87905 | 21 | #include <asm/state.h> |
7accb6ea | 22 | |
890fcefe SG |
23 | DECLARE_GLOBAL_DATA_PTR; |
24 | ||
e101550a TH |
25 | /* |
26 | * | |
27 | * serial_buf: A buffer that holds keyboard characters for the | |
a187559e | 28 | * Sandbox U-Boot. |
e101550a TH |
29 | * |
30 | * invariants: | |
31 | * serial_buf_write == serial_buf_read -> empty buffer | |
32 | * (serial_buf_write + 1) % 16 == serial_buf_read -> full buffer | |
33 | */ | |
34 | static char serial_buf[16]; | |
35 | static unsigned int serial_buf_write; | |
36 | static unsigned int serial_buf_read; | |
37 | ||
72e98228 SG |
38 | struct sandbox_serial_platdata { |
39 | int colour; /* Text colour to use for output, -1 for none */ | |
40 | }; | |
41 | ||
42 | struct sandbox_serial_priv { | |
43 | bool start_of_line; | |
44 | }; | |
45 | ||
46 | /** | |
47 | * output_ansi_colour() - Output an ANSI colour code | |
48 | * | |
49 | * @colour: Colour to output (0-7) | |
50 | */ | |
51 | static void output_ansi_colour(int colour) | |
52 | { | |
53 | char ansi_code[] = "\x1b[1;3Xm"; | |
54 | ||
55 | ansi_code[5] = '0' + colour; | |
56 | os_write(1, ansi_code, sizeof(ansi_code) - 1); | |
57 | } | |
58 | ||
59 | static void output_ansi_reset(void) | |
60 | { | |
61 | os_write(1, "\x1b[0m", 4); | |
62 | } | |
63 | ||
890fcefe | 64 | static int sandbox_serial_probe(struct udevice *dev) |
7accb6ea | 65 | { |
ffb87905 | 66 | struct sandbox_state *state = state_get_current(); |
72e98228 | 67 | struct sandbox_serial_priv *priv = dev_get_priv(dev); |
ffb87905 SG |
68 | |
69 | if (state->term_raw != STATE_TERM_COOKED) | |
70 | os_tty_raw(0, state->term_raw == STATE_TERM_RAW_WITH_SIGS); | |
72e98228 SG |
71 | priv->start_of_line = 0; |
72 | ||
73 | return 0; | |
74 | } | |
75 | ||
76 | static int sandbox_serial_remove(struct udevice *dev) | |
77 | { | |
78 | struct sandbox_serial_platdata *plat = dev->platdata; | |
79 | ||
80 | if (plat->colour != -1) | |
81 | output_ansi_reset(); | |
7accb6ea | 82 | |
890fcefe | 83 | return 0; |
7accb6ea SG |
84 | } |
85 | ||
890fcefe | 86 | static int sandbox_serial_putc(struct udevice *dev, const char ch) |
7accb6ea | 87 | { |
72e98228 SG |
88 | struct sandbox_serial_priv *priv = dev_get_priv(dev); |
89 | struct sandbox_serial_platdata *plat = dev->platdata; | |
90 | ||
91 | if (priv->start_of_line && plat->colour != -1) { | |
92 | priv->start_of_line = false; | |
93 | output_ansi_colour(plat->colour); | |
94 | } | |
95 | ||
7accb6ea | 96 | os_write(1, &ch, 1); |
72e98228 SG |
97 | if (ch == '\n') |
98 | priv->start_of_line = true; | |
7accb6ea | 99 | |
890fcefe | 100 | return 0; |
7accb6ea SG |
101 | } |
102 | ||
e101550a TH |
103 | static unsigned int increment_buffer_index(unsigned int index) |
104 | { | |
105 | return (index + 1) % ARRAY_SIZE(serial_buf); | |
106 | } | |
107 | ||
890fcefe | 108 | static int sandbox_serial_pending(struct udevice *dev, bool input) |
7accb6ea | 109 | { |
e101550a TH |
110 | const unsigned int next_index = |
111 | increment_buffer_index(serial_buf_write); | |
ec8f0b90 | 112 | ssize_t count; |
7accb6ea | 113 | |
890fcefe SG |
114 | if (!input) |
115 | return 0; | |
116 | ||
e101550a | 117 | os_usleep(100); |
0110f509 | 118 | #ifndef CONFIG_SPL_BUILD |
3ade5bc4 | 119 | video_sync_all(); |
0110f509 | 120 | #endif |
e101550a TH |
121 | if (next_index == serial_buf_read) |
122 | return 1; /* buffer full */ | |
123 | ||
124 | count = os_read_no_block(0, &serial_buf[serial_buf_write], 1); | |
125 | if (count == 1) | |
126 | serial_buf_write = next_index; | |
890fcefe | 127 | |
e101550a | 128 | return serial_buf_write != serial_buf_read; |
7accb6ea SG |
129 | } |
130 | ||
890fcefe | 131 | static int sandbox_serial_getc(struct udevice *dev) |
7accb6ea | 132 | { |
e101550a TH |
133 | int result; |
134 | ||
890fcefe SG |
135 | if (!sandbox_serial_pending(dev, true)) |
136 | return -EAGAIN; /* buffer empty */ | |
e101550a TH |
137 | |
138 | result = serial_buf[serial_buf_read]; | |
139 | serial_buf_read = increment_buffer_index(serial_buf_read); | |
140 | return result; | |
7accb6ea | 141 | } |
cef46b77 | 142 | |
72e98228 SG |
143 | static const char * const ansi_colour[] = { |
144 | "black", "red", "green", "yellow", "blue", "megenta", "cyan", | |
145 | "white", | |
146 | }; | |
147 | ||
148 | static int sandbox_serial_ofdata_to_platdata(struct udevice *dev) | |
149 | { | |
150 | struct sandbox_serial_platdata *plat = dev->platdata; | |
151 | const char *colour; | |
152 | int i; | |
153 | ||
154 | plat->colour = -1; | |
e160f7d4 | 155 | colour = fdt_getprop(gd->fdt_blob, dev_of_offset(dev), |
72e98228 SG |
156 | "sandbox,text-colour", NULL); |
157 | if (colour) { | |
158 | for (i = 0; i < ARRAY_SIZE(ansi_colour); i++) { | |
159 | if (!strcmp(colour, ansi_colour[i])) { | |
160 | plat->colour = i; | |
161 | break; | |
162 | } | |
163 | } | |
164 | } | |
165 | ||
166 | return 0; | |
167 | } | |
168 | ||
890fcefe SG |
169 | static const struct dm_serial_ops sandbox_serial_ops = { |
170 | .putc = sandbox_serial_putc, | |
171 | .pending = sandbox_serial_pending, | |
172 | .getc = sandbox_serial_getc, | |
cef46b77 MV |
173 | }; |
174 | ||
890fcefe SG |
175 | static const struct udevice_id sandbox_serial_ids[] = { |
176 | { .compatible = "sandbox,serial" }, | |
177 | { } | |
178 | }; | |
cef46b77 | 179 | |
890fcefe SG |
180 | U_BOOT_DRIVER(serial_sandbox) = { |
181 | .name = "serial_sandbox", | |
182 | .id = UCLASS_SERIAL, | |
183 | .of_match = sandbox_serial_ids, | |
72e98228 SG |
184 | .ofdata_to_platdata = sandbox_serial_ofdata_to_platdata, |
185 | .platdata_auto_alloc_size = sizeof(struct sandbox_serial_platdata), | |
186 | .priv_auto_alloc_size = sizeof(struct sandbox_serial_priv), | |
890fcefe | 187 | .probe = sandbox_serial_probe, |
72e98228 | 188 | .remove = sandbox_serial_remove, |
890fcefe SG |
189 | .ops = &sandbox_serial_ops, |
190 | .flags = DM_FLAG_PRE_RELOC, | |
191 | }; | |
192 | ||
72e98228 SG |
193 | static const struct sandbox_serial_platdata platdata_non_fdt = { |
194 | .colour = -1, | |
195 | }; | |
196 | ||
890fcefe SG |
197 | U_BOOT_DEVICE(serial_sandbox_non_fdt) = { |
198 | .name = "serial_sandbox", | |
72e98228 | 199 | .platdata = &platdata_non_fdt, |
890fcefe | 200 | }; |