]>
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); |
3ade5bc4 | 118 | video_sync_all(); |
e101550a TH |
119 | if (next_index == serial_buf_read) |
120 | return 1; /* buffer full */ | |
121 | ||
122 | count = os_read_no_block(0, &serial_buf[serial_buf_write], 1); | |
123 | if (count == 1) | |
124 | serial_buf_write = next_index; | |
890fcefe | 125 | |
e101550a | 126 | return serial_buf_write != serial_buf_read; |
7accb6ea SG |
127 | } |
128 | ||
890fcefe | 129 | static int sandbox_serial_getc(struct udevice *dev) |
7accb6ea | 130 | { |
e101550a TH |
131 | int result; |
132 | ||
890fcefe SG |
133 | if (!sandbox_serial_pending(dev, true)) |
134 | return -EAGAIN; /* buffer empty */ | |
e101550a TH |
135 | |
136 | result = serial_buf[serial_buf_read]; | |
137 | serial_buf_read = increment_buffer_index(serial_buf_read); | |
138 | return result; | |
7accb6ea | 139 | } |
cef46b77 | 140 | |
72e98228 SG |
141 | static const char * const ansi_colour[] = { |
142 | "black", "red", "green", "yellow", "blue", "megenta", "cyan", | |
143 | "white", | |
144 | }; | |
145 | ||
146 | static int sandbox_serial_ofdata_to_platdata(struct udevice *dev) | |
147 | { | |
148 | struct sandbox_serial_platdata *plat = dev->platdata; | |
149 | const char *colour; | |
150 | int i; | |
151 | ||
152 | plat->colour = -1; | |
153 | colour = fdt_getprop(gd->fdt_blob, dev->of_offset, | |
154 | "sandbox,text-colour", NULL); | |
155 | if (colour) { | |
156 | for (i = 0; i < ARRAY_SIZE(ansi_colour); i++) { | |
157 | if (!strcmp(colour, ansi_colour[i])) { | |
158 | plat->colour = i; | |
159 | break; | |
160 | } | |
161 | } | |
162 | } | |
163 | ||
164 | return 0; | |
165 | } | |
166 | ||
890fcefe SG |
167 | static const struct dm_serial_ops sandbox_serial_ops = { |
168 | .putc = sandbox_serial_putc, | |
169 | .pending = sandbox_serial_pending, | |
170 | .getc = sandbox_serial_getc, | |
cef46b77 MV |
171 | }; |
172 | ||
890fcefe SG |
173 | static const struct udevice_id sandbox_serial_ids[] = { |
174 | { .compatible = "sandbox,serial" }, | |
175 | { } | |
176 | }; | |
cef46b77 | 177 | |
890fcefe SG |
178 | U_BOOT_DRIVER(serial_sandbox) = { |
179 | .name = "serial_sandbox", | |
180 | .id = UCLASS_SERIAL, | |
181 | .of_match = sandbox_serial_ids, | |
72e98228 SG |
182 | .ofdata_to_platdata = sandbox_serial_ofdata_to_platdata, |
183 | .platdata_auto_alloc_size = sizeof(struct sandbox_serial_platdata), | |
184 | .priv_auto_alloc_size = sizeof(struct sandbox_serial_priv), | |
890fcefe | 185 | .probe = sandbox_serial_probe, |
72e98228 | 186 | .remove = sandbox_serial_remove, |
890fcefe SG |
187 | .ops = &sandbox_serial_ops, |
188 | .flags = DM_FLAG_PRE_RELOC, | |
189 | }; | |
190 | ||
72e98228 SG |
191 | static const struct sandbox_serial_platdata platdata_non_fdt = { |
192 | .colour = -1, | |
193 | }; | |
194 | ||
890fcefe SG |
195 | U_BOOT_DEVICE(serial_sandbox_non_fdt) = { |
196 | .name = "serial_sandbox", | |
72e98228 | 197 | .platdata = &platdata_non_fdt, |
890fcefe | 198 | }; |