blob: fc39f7168ec9fdaa734436a10824d5fbd6ef9572 [file] [log] [blame]
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +02001/*
2 * Copyright (c) 2017 Nordic Semiconductor ASA
3 *
4 * 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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * 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.
15 */
16
17#include <stdio.h>
18#include <uart.h>
19#include <assert.h>
20#include <string.h>
21#include <zephyr.h>
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +020022#include "bootutil/bootutil_log.h"
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020023
24#ifdef CONFIG_UART_CONSOLE
25#error Zephyr UART console must been disabled if serial_adapter module is used.
26#endif
27
28/** @brief Console input representation
29 *
30 * This struct is used to represent an input line from a serial interface.
31 */
32struct line_input {
Carles Cufi5ceeddb2018-06-15 12:43:22 +020033 /** Required to use sys_slist */
34 sys_snode_t node;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020035
Carles Cufi5ceeddb2018-06-15 12:43:22 +020036 int len;
37 /** Buffer where the input line is recorded */
38 char line[CONFIG_BOOT_MAX_LINE_INPUT_LEN];
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020039};
40
41static struct device *uart_dev;
42static struct line_input line_bufs[2];
43
Carles Cufi6400f0b2018-07-17 17:32:12 +020044static sys_slist_t avail_queue;
45static sys_slist_t lines_queue;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020046
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020047static u16_t cur;
48
49static int boot_uart_fifo_getline(char **line);
50static int boot_uart_fifo_init(void);
51
52int
53console_out(int c)
54{
Carles Cufi5ceeddb2018-06-15 12:43:22 +020055 uart_poll_out(uart_dev, c);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020056
Carles Cufi5ceeddb2018-06-15 12:43:22 +020057 return c;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020058}
59
60void
61console_write(const char *str, int cnt)
62{
Carles Cufi5ceeddb2018-06-15 12:43:22 +020063 int i;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020064
Carles Cufi5ceeddb2018-06-15 12:43:22 +020065 for (i = 0; i < cnt; i++) {
66 if (console_out((int)str[i]) == EOF) {
67 break;
68 }
69 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020070}
71
72int
73console_read(char *str, int str_size, int *newline)
74{
Carles Cufi5ceeddb2018-06-15 12:43:22 +020075 char *line;
76 int len;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020077
Carles Cufi5ceeddb2018-06-15 12:43:22 +020078 len = boot_uart_fifo_getline(&line);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020079
Carles Cufi5ceeddb2018-06-15 12:43:22 +020080 if (line == NULL) {
81 *newline = 0;
82 return 0;
83 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020084
Carles Cufi5ceeddb2018-06-15 12:43:22 +020085 if (len > str_size - 1) {
86 len = str_size - 1;
87 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020088
Carles Cufi5ceeddb2018-06-15 12:43:22 +020089 memcpy(str, line, len);
90 str[len] = '\0';
91 *newline = 1;
92 return len + 1;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020093}
94
95int
96boot_console_init(void)
97{
Carles Cufi5ceeddb2018-06-15 12:43:22 +020098 int i;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020099
Carles Cufi6400f0b2018-07-17 17:32:12 +0200100 /* Zephyr UART handler takes an empty buffer from avail_queue,
101 * stores UART input in it until EOL, and then puts it into
102 * lines_queue.
103 */
104 sys_slist_init(&avail_queue);
105 sys_slist_init(&lines_queue);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200106
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200107 for (i = 0; i < ARRAY_SIZE(line_bufs); i++) {
Carles Cufi6400f0b2018-07-17 17:32:12 +0200108 sys_slist_append(&avail_queue, &line_bufs[i].node);
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200109 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200110
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200111 return boot_uart_fifo_init();
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200112}
113
114static void
115boot_uart_fifo_callback(struct device *dev)
116{
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200117 static struct line_input *cmd;
118 u8_t byte;
119 int rx;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200120
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +0200121 uart_irq_update(uart_dev);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200122
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +0200123 if (!uart_irq_rx_ready(uart_dev)) {
124 return;
125 }
126
127 while (true) {
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200128 rx = uart_fifo_read(uart_dev, &byte, 1);
129 if (rx != 1) {
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +0200130 break;
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200131 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200132
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200133 if (!cmd) {
134 sys_snode_t *node;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200135
Carles Cufi6400f0b2018-07-17 17:32:12 +0200136 node = sys_slist_get(&avail_queue);
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200137 if (!node) {
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +0200138 BOOT_LOG_ERR("Not enough memory to store"
139 " incoming data!");
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200140 return;
141 }
142 cmd = CONTAINER_OF(node, struct line_input, node);
143 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200144
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200145 if (cur < CONFIG_BOOT_MAX_LINE_INPUT_LEN) {
146 cmd->line[cur++] = byte;
147 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200148
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200149 if (byte == '\n') {
150 cmd->len = cur;
Carles Cufi6400f0b2018-07-17 17:32:12 +0200151 sys_slist_append(&lines_queue, &cmd->node);
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200152 cur = 0;
Carles Cufib124e392018-07-17 17:27:50 +0200153 cmd = NULL;
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200154 }
155 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200156}
157
158static int
159boot_uart_fifo_getline(char **line)
160{
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200161 static struct line_input *cmd;
162 sys_snode_t *node;
Andrzej Puzdrowski30117142018-06-18 14:43:14 +0200163 int key;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200164
Andrzej Puzdrowski30117142018-06-18 14:43:14 +0200165 key = irq_lock();
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200166 /* Recycle cmd buffer returned previous time */
167 if (cmd != NULL) {
Carles Cufi6400f0b2018-07-17 17:32:12 +0200168 if (sys_slist_peek_tail(&avail_queue) != &cmd->node) {
169 sys_slist_append(&avail_queue, &cmd->node);
Andrzej Puzdrowski30117142018-06-18 14:43:14 +0200170 }
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200171 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200172
Carles Cufi6400f0b2018-07-17 17:32:12 +0200173 node = sys_slist_get(&lines_queue);
Andrzej Puzdrowski30117142018-06-18 14:43:14 +0200174 irq_unlock(key);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200175
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200176 if (node == NULL) {
177 cmd = NULL;
178 *line = NULL;
Andrzej Puzdrowski30117142018-06-18 14:43:14 +0200179
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200180 return 0;
181 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200182
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200183 cmd = CONTAINER_OF(node, struct line_input, node);
184 *line = cmd->line;
185 return cmd->len;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200186}
187
188static int
189boot_uart_fifo_init(void)
190{
Emanuele Di Santoc4bf7802018-07-20 11:39:57 +0200191#ifdef CONFIG_BOOT_SERIAL_UART
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200192 uart_dev = device_get_binding(CONFIG_UART_CONSOLE_ON_DEV_NAME);
Emanuele Di Santoc4bf7802018-07-20 11:39:57 +0200193#elif CONFIG_BOOT_SERIAL_CDC_ACM
194 uart_dev = device_get_binding(CONFIG_CDC_ACM_PORT_NAME);
195#endif
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200196 u8_t c;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200197
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200198 if (!uart_dev) {
199 return (-1);
200 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200201
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200202 uart_irq_callback_set(uart_dev, boot_uart_fifo_callback);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200203
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200204 /* Drain the fifo */
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +0200205 if (uart_irq_rx_ready(uart_dev)) {
206 while (uart_fifo_read(uart_dev, &c, 1)) {
207 ;
208 }
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200209 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200210
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200211 cur = 0;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200212
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200213 uart_irq_rx_enable(uart_dev);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200214
Carles Cufi5e48c552018-06-15 12:58:08 +0200215 /* Enable all interrupts unconditionally. Note that this is due
216 * to Zephyr issue #8393. This should be removed once the
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +0200217 * issue is fixed in upstream Zephyr.
218 */
Carles Cufi5e48c552018-06-15 12:58:08 +0200219 irq_unlock(0);
220
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200221 return 0;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200222}