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