blob: ed3867983b8138ad98f05cc27b441c9174b00334 [file] [log] [blame]
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -07001/*
2 * Copyright (c) 2011-2012, 2014-2015 Wind River Systems, Inc.
3 *
Javier B Perez Hernandezf7fffae2015-10-06 11:00:37 -05004 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -07007 *
Javier B Perez Hernandezf7fffae2015-10-06 11:00:37 -05008 * http://www.apache.org/licenses/LICENSE-2.0
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -07009 *
Javier B Perez Hernandezf7fffae2015-10-06 11:00:37 -050010 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070015 */
16
Anas Nashif275ca602015-12-04 10:09:39 -050017/**
18 * @file
19 * @brief UART-driven console
20 *
Dan Kalowskyda67b292015-10-20 09:42:33 -070021 *
22 * Serial console driver.
23 * Hooks into the printk and fputc (for printf) modules. Poll driven.
Anas Nashifea0d0b22015-07-01 17:22:39 -040024 */
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070025
Andrei Emeltchenko139c8562015-04-20 11:04:22 +030026#include <nanokernel.h>
Dan Kalowskyc02dd342015-05-28 10:56:47 -070027#include <arch/cpu.h>
Andrei Emeltchenko139c8562015-04-20 11:04:22 +030028
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070029#include <stdio.h>
30#include <stdint.h>
Andrei Emeltchenko139c8562015-04-20 11:04:22 +030031#include <errno.h>
Johan Hedbergf88cccd2015-12-08 10:35:04 +020032#include <ctype.h>
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070033
Daniel Leungad2d2962015-08-12 10:17:35 -070034#include <device.h>
35#include <init.h>
36
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070037#include <board.h>
Tomasz Bursztyka17e06fb2015-10-15 09:48:03 +030038#include <uart.h>
Tomasz Bursztykaae09a482015-04-23 13:12:51 +030039#include <console/uart_console.h>
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070040#include <toolchain.h>
41#include <sections.h>
Johan Hedberg8683dc42015-12-09 11:36:25 +020042#include <atomic.h>
Johan Hedberga9f6f892015-12-08 22:45:12 +020043#include <misc/printk.h>
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070044
Daniel Leung08b4fd42015-12-01 08:42:20 -080045static struct device *uart_console_dev;
46
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070047#if 0 /* NOTUSED */
Anas Nashifea0d0b22015-07-01 17:22:39 -040048/**
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070049 *
Anas Nashiff367f072015-07-01 17:51:40 -040050 * @brief Get a character from UART
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070051 *
Anas Nashif1362e3c2015-07-01 17:29:04 -040052 * @return the character or EOF if nothing present
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070053 */
54
Daniel Leung08b4fd42015-12-01 08:42:20 -080055static int console_in(void)
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070056{
57 unsigned char c;
Dan Kalowskyda67b292015-10-20 09:42:33 -070058
Daniel Leung08b4fd42015-12-01 08:42:20 -080059 if (uart_poll_in(uart_console_dev, &c) < 0)
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070060 return EOF;
61 else
62 return (int)c;
63}
64#endif
65
66#if defined(CONFIG_PRINTK) || defined(CONFIG_STDOUT_CONSOLE)
Anas Nashifea0d0b22015-07-01 17:22:39 -040067/**
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070068 *
Anas Nashiff367f072015-07-01 17:51:40 -040069 * @brief Output one character to UART
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070070 *
71 * Outputs both line feed and carriage return in the case of a '\n'.
72 *
Daniel Leung08b4fd42015-12-01 08:42:20 -080073 * @param c Character to output
74 *
Anas Nashif1362e3c2015-07-01 17:29:04 -040075 * @return The character passed as input.
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070076 */
77
Daniel Leung08b4fd42015-12-01 08:42:20 -080078static int console_out(int c)
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070079{
Daniel Leung08b4fd42015-12-01 08:42:20 -080080 uart_poll_out(uart_console_dev, (unsigned char)c);
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070081 if ('\n' == c) {
Daniel Leung08b4fd42015-12-01 08:42:20 -080082 uart_poll_out(uart_console_dev, (unsigned char)'\r');
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070083 }
84 return c;
85}
Daniel Leung08b4fd42015-12-01 08:42:20 -080086
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070087#endif
88
89#if defined(CONFIG_STDOUT_CONSOLE)
90extern void __stdout_hook_install(int (*hook)(int));
91#else
92#define __stdout_hook_install(x) \
93 do {/* nothing */ \
94 } while ((0))
95#endif
96
97#if defined(CONFIG_PRINTK)
98extern void __printk_hook_install(int (*fn)(int));
99#else
100#define __printk_hook_install(x) \
101 do {/* nothing */ \
102 } while ((0))
103#endif
104
Andrei Emeltchenko139c8562015-04-20 11:04:22 +0300105#if defined(CONFIG_CONSOLE_HANDLER)
Andrei Emeltchenko879541a2015-05-04 14:43:36 +0300106static struct nano_fifo *avail_queue;
107static struct nano_fifo *lines_queue;
Andrei Emeltchenko139c8562015-04-20 11:04:22 +0300108
Johan Hedbergf88cccd2015-12-08 10:35:04 +0200109/* Control characters */
110#define ESC 0x1b
111#define DEL 0x7f
112
113/* ANSI escape sequences */
114#define ANSI_ESC '['
115#define ANSI_UP 'A'
116#define ANSI_DOWN 'B'
117#define ANSI_FORWARD 'C'
118#define ANSI_BACKWARD 'D'
Johan Hedbergf88cccd2015-12-08 10:35:04 +0200119
Daniel Leung1ad2a562015-08-05 12:13:36 -0700120static int read_uart(struct device *uart, uint8_t *buf, unsigned int size)
Andrei Emeltchenko139c8562015-04-20 11:04:22 +0300121{
122 int rx;
123
124 rx = uart_fifo_read(uart, buf, size);
125 if (rx < 0) {
126 /* Overrun issue. Stop the UART */
127 uart_irq_rx_disable(uart);
128
129 return -EIO;
130 }
131
132 return rx;
133}
134
Johan Hedberga9f6f892015-12-08 22:45:12 +0200135static inline void cursor_forward(unsigned int count)
Johan Hedbergf88cccd2015-12-08 10:35:04 +0200136{
Johan Hedberga9f6f892015-12-08 22:45:12 +0200137 printk("\x1b[%uC", count);
138}
139
140static inline void cursor_backward(unsigned int count)
141{
142 printk("\x1b[%uD", count);
143}
144
Johan Hedbergceba31a2015-12-09 12:40:29 +0200145static inline void cursor_save(void)
146{
147 printk("\x1b[s");
148}
149
150static inline void cursor_restore(void)
151{
152 printk("\x1b[u");
153}
154
Johan Hedberga9f6f892015-12-08 22:45:12 +0200155static void insert_char(char *pos, char c, uint8_t end)
156{
Johan Hedberga9f6f892015-12-08 22:45:12 +0200157 char tmp;
158
159 /* Echo back to console */
160 uart_poll_out(uart_console_dev, c);
161
162 if (end == 0) {
163 *pos = c;
164 return;
Johan Hedbergf88cccd2015-12-08 10:35:04 +0200165 }
Johan Hedberga9f6f892015-12-08 22:45:12 +0200166
167 tmp = *pos;
168 *(pos++) = c;
169
Johan Hedbergceba31a2015-12-09 12:40:29 +0200170 cursor_save();
171
172 while (end-- > 0) {
Johan Hedberga9f6f892015-12-08 22:45:12 +0200173 uart_poll_out(uart_console_dev, tmp);
Johan Hedbergceba31a2015-12-09 12:40:29 +0200174 c = *pos;
175 *(pos++) = tmp;
Johan Hedberga9f6f892015-12-08 22:45:12 +0200176 tmp = c;
177 }
178
179 /* Move cursor back to right place */
Johan Hedbergceba31a2015-12-09 12:40:29 +0200180 cursor_restore();
Johan Hedberga9f6f892015-12-08 22:45:12 +0200181}
182
183static void del_char(char *pos, uint8_t end)
184{
Johan Hedberga9f6f892015-12-08 22:45:12 +0200185 uart_poll_out(uart_console_dev, '\b');
186
187 if (end == 0) {
188 uart_poll_out(uart_console_dev, ' ');
189 uart_poll_out(uart_console_dev, '\b');
190 return;
191 }
192
Johan Hedbergceba31a2015-12-09 12:40:29 +0200193 cursor_save();
194
195 while (end-- > 0) {
196 *pos = *(pos + 1);
197 uart_poll_out(uart_console_dev, *(pos++));
Johan Hedberga9f6f892015-12-08 22:45:12 +0200198 }
199
200 uart_poll_out(uart_console_dev, ' ');
201
202 /* Move cursor back to right place */
Johan Hedbergceba31a2015-12-09 12:40:29 +0200203 cursor_restore();
Johan Hedbergf88cccd2015-12-08 10:35:04 +0200204}
205
Johan Hedberg8683dc42015-12-09 11:36:25 +0200206enum {
207 ESC_ESC,
208 ESC_ANSI,
209 ESC_ANSI_FIRST,
210 ESC_ANSI_VAL,
211 ESC_ANSI_VAL_2
212};
213
214static atomic_t esc_state;
215static unsigned int ansi_val, ansi_val_2;
216static uint8_t cur, end;
217
218static void handle_ansi(uint8_t byte)
219{
220 if (atomic_test_and_clear_bit(&esc_state, ESC_ANSI_FIRST)) {
221 if (!isdigit(byte)) {
222 ansi_val = 1;
223 goto ansi_cmd;
224 }
225
226 atomic_set_bit(&esc_state, ESC_ANSI_VAL);
227 ansi_val = byte - '0';
228 ansi_val_2 = 0;
229 return;
230 }
231
232 if (atomic_test_bit(&esc_state, ESC_ANSI_VAL)) {
233 if (isdigit(byte)) {
234 if (atomic_test_bit(&esc_state, ESC_ANSI_VAL_2)) {
235 ansi_val_2 *= 10;
236 ansi_val_2 += byte - '0';
237 } else {
238 ansi_val *= 10;
239 ansi_val += byte - '0';
240 }
241 return;
242 }
243
244 /* Multi value sequence, e.g. Esc[Line;ColumnH */
245 if (byte == ';' &&
246 !atomic_test_and_set_bit(&esc_state, ESC_ANSI_VAL_2)) {
247 return;
248 }
249
250 atomic_clear_bit(&esc_state, ESC_ANSI_VAL);
251 atomic_clear_bit(&esc_state, ESC_ANSI_VAL_2);
252 }
253
254ansi_cmd:
255 switch (byte) {
256 case ANSI_BACKWARD:
257 if (ansi_val > cur) {
258 break;
259 }
260
261 end += ansi_val;
262 cur -= ansi_val;
263 cursor_backward(ansi_val);
264 break;
265 case ANSI_FORWARD:
266 if (ansi_val > end) {
267 break;
268 }
269
270 end -= ansi_val;
271 cur += ansi_val;
272 cursor_forward(ansi_val);
273 break;
274 default:
275 break;
276 }
277
278 atomic_clear_bit(&esc_state, ESC_ANSI);
279}
280
Andrei Emeltchenkodfa5f232015-04-23 10:20:32 +0300281void uart_console_isr(void *unused)
Andrei Emeltchenko139c8562015-04-20 11:04:22 +0300282{
283 ARG_UNUSED(unused);
284
Johan Hedberg5ad78032015-12-07 15:03:20 +0200285 while (uart_irq_update(uart_console_dev) &&
286 uart_irq_is_pending(uart_console_dev)) {
287 static struct uart_console_input *cmd;
288 uint8_t byte;
289 int rx;
290
291 if (!uart_irq_rx_ready(uart_console_dev)) {
292 continue;
293 }
294
Andrei Emeltchenko139c8562015-04-20 11:04:22 +0300295 /* Character(s) have been received */
Andrei Emeltchenko879541a2015-05-04 14:43:36 +0300296
Johan Hedberg5ad78032015-12-07 15:03:20 +0200297 rx = read_uart(uart_console_dev, &byte, 1);
298 if (rx < 0) {
299 return;
300 }
301
302 if (uart_irq_input_hook(uart_console_dev, byte) != 0) {
303 /*
304 * The input hook indicates that no further processing
305 * should be done by this handler.
306 */
307 return;
308 }
309
310 if (!cmd) {
311 cmd = nano_isr_fifo_get(avail_queue);
312 if (!cmd)
Peter Mitsisb1c10202015-09-17 13:22:00 -0400313 return;
Johan Hedberg5ad78032015-12-07 15:03:20 +0200314 }
Peter Mitsisb1c10202015-09-17 13:22:00 -0400315
Johan Hedbergf88cccd2015-12-08 10:35:04 +0200316 /* Handle ANSI escape mode */
Johan Hedberg8683dc42015-12-09 11:36:25 +0200317 if (atomic_test_bit(&esc_state, ESC_ANSI)) {
318 handle_ansi(byte);
Johan Hedbergf88cccd2015-12-08 10:35:04 +0200319 continue;
320 }
321
322 /* Handle escape mode */
Johan Hedberg8683dc42015-12-09 11:36:25 +0200323 if (atomic_test_and_clear_bit(&esc_state, ESC_ESC)) {
Johan Hedbergf88cccd2015-12-08 10:35:04 +0200324 switch (byte) {
325 case ANSI_ESC:
Johan Hedberg8683dc42015-12-09 11:36:25 +0200326 atomic_set_bit(&esc_state, ESC_ANSI);
327 atomic_set_bit(&esc_state, ESC_ANSI_FIRST);
Johan Hedbergf88cccd2015-12-08 10:35:04 +0200328 break;
329 default:
330 break;
331 }
332
333 continue;
334 }
335
336 /* Handle special control characters */
337 if (!isprint(byte)) {
338 switch (byte) {
339 case DEL:
Johan Hedberga9f6f892015-12-08 22:45:12 +0200340 if (cur > 0) {
341 del_char(&cmd->line[--cur], end);
Johan Hedbergf88cccd2015-12-08 10:35:04 +0200342 }
343 break;
344 case ESC:
Johan Hedberg8683dc42015-12-09 11:36:25 +0200345 atomic_set_bit(&esc_state, ESC_ESC);
Johan Hedbergf88cccd2015-12-08 10:35:04 +0200346 break;
347 case '\r':
Johan Hedberga9f6f892015-12-08 22:45:12 +0200348 cmd->line[cur + end] = '\0';
Johan Hedbergf88cccd2015-12-08 10:35:04 +0200349 uart_poll_out(uart_console_dev, '\n');
Johan Hedberga9f6f892015-12-08 22:45:12 +0200350 cur = 0;
351 end = 0;
Johan Hedbergf88cccd2015-12-08 10:35:04 +0200352 nano_isr_fifo_put(lines_queue, cmd);
353 cmd = NULL;
354 break;
355 default:
356 break;
357 }
358
359 continue;
360 }
361
Johan Hedberg6147fc62015-12-08 09:26:31 +0200362 /* Ignore characters if there's no more buffer space */
Johan Hedberga9f6f892015-12-08 22:45:12 +0200363 if (cur + end < sizeof(cmd->line) - 1) {
364 insert_char(&cmd->line[cur++], byte, end);
Johan Hedberg6147fc62015-12-08 09:26:31 +0200365 }
Andrei Emeltchenko139c8562015-04-20 11:04:22 +0300366 }
367}
368
Dmitriy Korovkin8d065342015-06-04 11:42:35 -0400369IRQ_CONNECT_STATIC(console, CONFIG_UART_CONSOLE_IRQ,
Daniel Leung08b4fd42015-12-01 08:42:20 -0800370 CONFIG_UART_CONSOLE_IRQ_PRI, uart_console_isr, 0,
Dmitriy Korovkinf1420512015-11-02 18:06:08 -0500371 UART_IRQ_FLAGS);
Dmitriy Korovkin8d065342015-06-04 11:42:35 -0400372
Andrei Emeltchenko879541a2015-05-04 14:43:36 +0300373static void console_input_init(void)
Andrei Emeltchenko139c8562015-04-20 11:04:22 +0300374{
375 uint8_t c;
376
Daniel Leung08b4fd42015-12-01 08:42:20 -0800377 uart_irq_rx_disable(uart_console_dev);
378 uart_irq_tx_disable(uart_console_dev);
Juan Manuel Cruzbc1a79c2015-11-30 11:21:13 -0600379 IRQ_CONFIG(console, uart_irq_get(uart_console_dev));
Daniel Leung08b4fd42015-12-01 08:42:20 -0800380 irq_enable(uart_irq_get(uart_console_dev));
Andrei Emeltchenko139c8562015-04-20 11:04:22 +0300381
382 /* Drain the fifo */
Daniel Leung08b4fd42015-12-01 08:42:20 -0800383 while (uart_irq_rx_ready(uart_console_dev)) {
384 uart_fifo_read(uart_console_dev, &c, 1);
Daniel Leung1ad2a562015-08-05 12:13:36 -0700385 }
Andrei Emeltchenko139c8562015-04-20 11:04:22 +0300386
Daniel Leung08b4fd42015-12-01 08:42:20 -0800387 uart_irq_rx_enable(uart_console_dev);
Andrei Emeltchenko139c8562015-04-20 11:04:22 +0300388}
389
Andrei Emeltchenko879541a2015-05-04 14:43:36 +0300390void uart_register_input(struct nano_fifo *avail, struct nano_fifo *lines)
Andrei Emeltchenko139c8562015-04-20 11:04:22 +0300391{
Andrei Emeltchenko879541a2015-05-04 14:43:36 +0300392 avail_queue = avail;
393 lines_queue = lines;
394
395 console_input_init();
Andrei Emeltchenko139c8562015-04-20 11:04:22 +0300396}
397#else
Andrei Emeltchenko879541a2015-05-04 14:43:36 +0300398#define console_input_init(x) \
Andrei Emeltchenko139c8562015-04-20 11:04:22 +0300399 do {/* nothing */ \
400 } while ((0))
Andrei Emeltchenko879541a2015-05-04 14:43:36 +0300401#define uart_register_input(x) \
Andrei Emeltchenko139c8562015-04-20 11:04:22 +0300402 do {/* nothing */ \
403 } while ((0))
404#endif
405
Anas Nashifea0d0b22015-07-01 17:22:39 -0400406/**
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700407 *
Daniel Leungad2d2962015-08-12 10:17:35 -0700408 * @brief Install printk/stdout hook for UART console output
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700409 *
Anas Nashif1362e3c2015-07-01 17:29:04 -0400410 * @return N/A
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700411 */
412
Daniel Leungad2d2962015-08-12 10:17:35 -0700413void uart_console_hook_install(void)
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700414{
Daniel Leung08b4fd42015-12-01 08:42:20 -0800415 __stdout_hook_install(console_out);
416 __printk_hook_install(console_out);
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700417}
Daniel Leungad2d2962015-08-12 10:17:35 -0700418
419/**
420 *
421 * @brief Initialize one UART as the console/debug port
422 *
423 * @return DEV_OK if successful, otherwise failed.
424 */
425static int uart_console_init(struct device *arg)
426{
427 ARG_UNUSED(arg);
428
Daniel Leung08b4fd42015-12-01 08:42:20 -0800429 uart_console_dev = device_get_binding(CONFIG_UART_CONSOLE_ON_DEV_NAME);
430
Daniel Leungad2d2962015-08-12 10:17:35 -0700431 uart_console_hook_install();
432
433 return DEV_OK;
434}
435DECLARE_DEVICE_INIT_CONFIG(uart_console, "", uart_console_init, NULL);
Dmitriy Korovkin57f27412015-10-26 15:56:02 -0400436
437/* UART consloe initializes after the UART device itself */
438#if defined(CONFIG_EARLY_CONSOLE)
439SYS_DEFINE_DEVICE(uart_console, NULL, PRIMARY, CONFIG_UART_CONSOLE_PRIORITY);
440#else
441SYS_DEFINE_DEVICE(uart_console, NULL, SECONDARY, CONFIG_UART_CONSOLE_PRIORITY);
442#endif