/src/u-boot/drivers/serial/sandbox.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0+ |
2 | | /* |
3 | | * Copyright (c) 2011 The Chromium OS Authors. |
4 | | */ |
5 | | |
6 | | /* |
7 | | * This provide a test serial port. It provides an emulated serial port where |
8 | | * a test program and read out the serial output and inject serial input for |
9 | | * U-Boot. |
10 | | */ |
11 | | |
12 | | #include <console.h> |
13 | | #include <dm.h> |
14 | | #include <os.h> |
15 | | #include <serial.h> |
16 | | #include <video.h> |
17 | | #include <asm/global_data.h> |
18 | | #include <linux/compiler.h> |
19 | | #include <asm/serial.h> |
20 | | #include <asm/state.h> |
21 | | |
22 | | DECLARE_GLOBAL_DATA_PTR; |
23 | | |
24 | | static size_t _sandbox_serial_written = 1; |
25 | | static bool sandbox_serial_enabled = true; |
26 | | |
27 | | size_t sandbox_serial_written(void) |
28 | 0 | { |
29 | 0 | return _sandbox_serial_written; |
30 | 0 | } |
31 | | |
32 | | void sandbox_serial_endisable(bool enabled) |
33 | 0 | { |
34 | 0 | sandbox_serial_enabled = enabled; |
35 | 0 | } |
36 | | |
37 | | /** |
38 | | * output_ansi_colour() - Output an ANSI colour code |
39 | | * |
40 | | * @colour: Colour to output (0-7) |
41 | | */ |
42 | | static void output_ansi_colour(int colour) |
43 | 0 | { |
44 | 0 | char ansi_code[] = "\x1b[1;3Xm"; |
45 | |
|
46 | 0 | ansi_code[5] = '0' + colour; |
47 | 0 | os_write(1, ansi_code, sizeof(ansi_code) - 1); |
48 | 0 | } |
49 | | |
50 | | static void output_ansi_reset(void) |
51 | 0 | { |
52 | 0 | os_write(1, "\x1b[0m", 4); |
53 | 0 | } |
54 | | |
55 | | static int sandbox_serial_probe(struct udevice *dev) |
56 | 0 | { |
57 | 0 | struct sandbox_state *state = state_get_current(); |
58 | 0 | struct sandbox_serial_priv *priv = dev_get_priv(dev); |
59 | |
|
60 | 0 | if (state->term_raw != STATE_TERM_COOKED) |
61 | 0 | os_tty_raw(0, state->term_raw == STATE_TERM_RAW_WITH_SIGS); |
62 | 0 | priv->start_of_line = 0; |
63 | |
|
64 | 0 | if (state->term_raw != STATE_TERM_RAW) |
65 | 0 | disable_ctrlc(1); |
66 | 0 | membuf_init(&priv->buf, priv->serial_buf, sizeof(priv->serial_buf)); |
67 | |
|
68 | 0 | return 0; |
69 | 0 | } |
70 | | |
71 | | static int sandbox_serial_remove(struct udevice *dev) |
72 | 0 | { |
73 | 0 | struct sandbox_serial_plat *plat = dev_get_plat(dev); |
74 | |
|
75 | 0 | if (plat->colour != -1) |
76 | 0 | output_ansi_reset(); |
77 | |
|
78 | 0 | return 0; |
79 | 0 | } |
80 | | |
81 | | static void sandbox_print_color(struct udevice *dev) |
82 | 0 | { |
83 | 0 | struct sandbox_serial_priv *priv = dev_get_priv(dev); |
84 | 0 | struct sandbox_serial_plat *plat = dev_get_plat(dev); |
85 | | |
86 | | /* With of-platdata we don't real the colour correctly, so disable it */ |
87 | 0 | if (!CONFIG_IS_ENABLED(OF_PLATDATA) && priv->start_of_line && |
88 | 0 | plat->colour != -1) { |
89 | 0 | priv->start_of_line = false; |
90 | 0 | output_ansi_colour(plat->colour); |
91 | 0 | } |
92 | 0 | } |
93 | | |
94 | | static int sandbox_serial_putc(struct udevice *dev, const char ch) |
95 | 0 | { |
96 | 0 | struct sandbox_serial_priv *priv = dev_get_priv(dev); |
97 | |
|
98 | 0 | if (ch == '\n') |
99 | 0 | priv->start_of_line = true; |
100 | |
|
101 | 0 | if (sandbox_serial_enabled) { |
102 | 0 | sandbox_print_color(dev); |
103 | 0 | os_write(1, &ch, 1); |
104 | 0 | } |
105 | 0 | _sandbox_serial_written += 1; |
106 | 0 | return 0; |
107 | 0 | } |
108 | | |
109 | | static ssize_t sandbox_serial_puts(struct udevice *dev, const char *s, |
110 | | size_t len) |
111 | 0 | { |
112 | 0 | struct sandbox_serial_priv *priv = dev_get_priv(dev); |
113 | 0 | ssize_t ret; |
114 | |
|
115 | 0 | if (len && s[len - 1] == '\n') |
116 | 0 | priv->start_of_line = true; |
117 | |
|
118 | 0 | if (sandbox_serial_enabled) { |
119 | 0 | sandbox_print_color(dev); |
120 | 0 | ret = os_write(1, s, len); |
121 | 0 | if (ret < 0) |
122 | 0 | return ret; |
123 | 0 | } else { |
124 | 0 | ret = len; |
125 | 0 | } |
126 | 0 | _sandbox_serial_written += ret; |
127 | 0 | return ret; |
128 | 0 | } |
129 | | |
130 | | static int sandbox_serial_pending(struct udevice *dev, bool input) |
131 | 0 | { |
132 | 0 | struct sandbox_serial_priv *priv = dev_get_priv(dev); |
133 | 0 | ssize_t count; |
134 | 0 | char *data; |
135 | 0 | int avail; |
136 | |
|
137 | 0 | if (!input) |
138 | 0 | return 0; |
139 | | |
140 | 0 | os_usleep(100); |
141 | 0 | avail = membuf_putraw(&priv->buf, 100, false, &data); |
142 | 0 | if (!avail) |
143 | 0 | return 1; /* buffer full */ |
144 | | |
145 | 0 | count = os_read(0, data, avail); |
146 | 0 | if (count > 0) |
147 | 0 | membuf_putraw(&priv->buf, count, true, &data); |
148 | |
|
149 | 0 | return membuf_avail(&priv->buf); |
150 | 0 | } |
151 | | |
152 | | static int sandbox_serial_getc(struct udevice *dev) |
153 | 0 | { |
154 | 0 | struct sandbox_serial_priv *priv = dev_get_priv(dev); |
155 | |
|
156 | 0 | if (!sandbox_serial_pending(dev, true)) |
157 | 0 | return -EAGAIN; /* buffer empty */ |
158 | | |
159 | 0 | return membuf_getbyte(&priv->buf); |
160 | 0 | } |
161 | | |
162 | | #ifdef CONFIG_DEBUG_UART_SANDBOX |
163 | | |
164 | | #include <debug_uart.h> |
165 | | |
166 | | static inline void _debug_uart_init(void) |
167 | 0 | { |
168 | 0 | } |
169 | | |
170 | | static inline void _debug_uart_putc(int ch) |
171 | 0 | { |
172 | 0 | os_putc(ch); |
173 | 0 | } |
174 | | |
175 | | DEBUG_UART_FUNCS |
176 | | |
177 | | #endif /* CONFIG_DEBUG_UART_SANDBOX */ |
178 | | |
179 | | static int sandbox_serial_getconfig(struct udevice *dev, uint *serial_config) |
180 | 0 | { |
181 | 0 | uint config = SERIAL_DEFAULT_CONFIG; |
182 | |
|
183 | 0 | if (!serial_config) |
184 | 0 | return -EINVAL; |
185 | | |
186 | 0 | *serial_config = config; |
187 | |
|
188 | 0 | return 0; |
189 | 0 | } |
190 | | |
191 | | static int sandbox_serial_setconfig(struct udevice *dev, uint serial_config) |
192 | 0 | { |
193 | 0 | u8 parity = SERIAL_GET_PARITY(serial_config); |
194 | 0 | u8 bits = SERIAL_GET_BITS(serial_config); |
195 | 0 | u8 stop = SERIAL_GET_STOP(serial_config); |
196 | |
|
197 | 0 | if (bits != SERIAL_8_BITS || stop != SERIAL_ONE_STOP || |
198 | 0 | parity != SERIAL_PAR_NONE) |
199 | 0 | return -ENOTSUPP; /* not supported in driver*/ |
200 | | |
201 | 0 | return 0; |
202 | 0 | } |
203 | | |
204 | | static int sandbox_serial_getinfo(struct udevice *dev, |
205 | | struct serial_device_info *serial_info) |
206 | 0 | { |
207 | 0 | struct serial_device_info info = { |
208 | 0 | .type = SERIAL_CHIP_UNKNOWN, |
209 | 0 | .addr_space = SERIAL_ADDRESS_SPACE_IO, |
210 | 0 | .addr = SERIAL_DEFAULT_ADDRESS, |
211 | 0 | .reg_width = 1, |
212 | 0 | .reg_offset = 0, |
213 | 0 | .reg_shift = 0, |
214 | 0 | .clock = SERIAL_DEFAULT_CLOCK, |
215 | 0 | }; |
216 | |
|
217 | 0 | if (!serial_info) |
218 | 0 | return -EINVAL; |
219 | | |
220 | 0 | *serial_info = info; |
221 | |
|
222 | 0 | return 0; |
223 | 0 | } |
224 | | |
225 | | static const char * const ansi_colour[] = { |
226 | | "black", "red", "green", "yellow", "blue", "megenta", "cyan", |
227 | | "white", |
228 | | }; |
229 | | |
230 | | static int sandbox_serial_of_to_plat(struct udevice *dev) |
231 | 0 | { |
232 | 0 | struct sandbox_serial_plat *plat = dev_get_plat(dev); |
233 | 0 | const char *colour; |
234 | 0 | int i; |
235 | |
|
236 | 0 | if (CONFIG_IS_ENABLED(OF_PLATDATA)) |
237 | 0 | return 0; |
238 | 0 | plat->colour = -1; |
239 | 0 | colour = dev_read_string(dev, "sandbox,text-colour"); |
240 | 0 | if (colour) { |
241 | 0 | for (i = 0; i < ARRAY_SIZE(ansi_colour); i++) { |
242 | 0 | if (!strcmp(colour, ansi_colour[i])) { |
243 | 0 | plat->colour = i; |
244 | 0 | break; |
245 | 0 | } |
246 | 0 | } |
247 | 0 | } |
248 | |
|
249 | 0 | return 0; |
250 | 0 | } |
251 | | |
252 | | static const struct dm_serial_ops sandbox_serial_ops = { |
253 | | .putc = sandbox_serial_putc, |
254 | | .puts = sandbox_serial_puts, |
255 | | .pending = sandbox_serial_pending, |
256 | | .getc = sandbox_serial_getc, |
257 | | .getconfig = sandbox_serial_getconfig, |
258 | | .setconfig = sandbox_serial_setconfig, |
259 | | .getinfo = sandbox_serial_getinfo, |
260 | | }; |
261 | | |
262 | | static const struct udevice_id sandbox_serial_ids[] = { |
263 | | { .compatible = "sandbox,serial" }, |
264 | | { } |
265 | | }; |
266 | | |
267 | | U_BOOT_DRIVER(sandbox_serial) = { |
268 | | .name = "sandbox_serial", |
269 | | .id = UCLASS_SERIAL, |
270 | | .of_match = sandbox_serial_ids, |
271 | | .of_to_plat = sandbox_serial_of_to_plat, |
272 | | .plat_auto = sizeof(struct sandbox_serial_plat), |
273 | | .priv_auto = sizeof(struct sandbox_serial_priv), |
274 | | .probe = sandbox_serial_probe, |
275 | | .remove = sandbox_serial_remove, |
276 | | .ops = &sandbox_serial_ops, |
277 | | .flags = DM_FLAG_PRE_RELOC, |
278 | | }; |
279 | | |
280 | | #if CONFIG_IS_ENABLED(OF_REAL) && !CONFIG_IS_ENABLED(OF_PLATDATA) |
281 | | static const struct sandbox_serial_plat platdata_non_fdt = { |
282 | | .colour = -1, |
283 | | }; |
284 | | |
285 | | U_BOOT_DRVINFO(serial_sandbox_non_fdt) = { |
286 | | .name = "sandbox_serial", |
287 | | .plat = &platdata_non_fdt, |
288 | | }; |
289 | | #endif |