blob: de2e5c511784f422d7f99c17d2c4bf5d42e8c83d [file] [log] [blame]
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +02001/*
Jamie McCraecde36392023-02-17 08:44:38 +00002 * Copyright (c) 2017-2023 Nordic Semiconductor ASA
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +02003 *
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>
Gerard Marull-Paretas3cd2cec2022-05-09 12:10:05 +020018#include <zephyr/drivers/uart.h>
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020019#include <assert.h>
20#include <string.h>
Gerard Marull-Paretas34dd9e72022-05-09 12:13:12 +020021#include <zephyr/kernel.h>
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +020022#include "bootutil/bootutil_log.h"
Gerard Marull-Paretas3cd2cec2022-05-09 12:10:05 +020023#include <zephyr/usb/usb_device.h>
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020024
Dominik Ermel1090d8f2021-09-22 08:37:28 +000025#if defined(CONFIG_BOOT_SERIAL_UART) && defined(CONFIG_UART_CONSOLE) && \
26 (!DT_HAS_CHOSEN(zephyr_uart_mcumgr) || \
27 DT_SAME_NODE(DT_CHOSEN(zephyr_uart_mcumgr), DT_CHOSEN(zephyr_console)))
Jamie McCrae822b6cb2023-11-06 14:40:05 +000028#error Zephyr UART console must be disabled if serial_adapter module is used.
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020029#endif
30
Dominik Ermel1090d8f2021-09-22 08:37:28 +000031#if defined(CONFIG_BOOT_SERIAL_CDC_ACM) && \
Jamie McCrae822b6cb2023-11-06 14:40:05 +000032 defined(CONFIG_UART_CONSOLE) && (!DT_HAS_CHOSEN(zephyr_uart_mcumgr) || \
33 DT_SAME_NODE(DT_CHOSEN(zephyr_uart_mcumgr), DT_CHOSEN(zephyr_console)))
34#error Zephyr UART console must be disabled if CDC ACM is enabled and MCUmgr \
Dominik Ermel1090d8f2021-09-22 08:37:28 +000035 has not been redirected to other UART with DTS chosen zephyr,uart-mcumgr.
36#endif
37
Jamie McCraed5c963c2023-11-06 15:17:19 +000038#if defined(CONFIG_BOOT_SERIAL_CDC_ACM) && CONFIG_MAIN_THREAD_PRIORITY < 0
39#error CONFIG_MAIN_THREAD_PRIORITY must be preemptible to support USB CDC ACM \
40 (0 or above)
41#endif
42
Carlos Falgueras GarcĂ­aa4b4b0f2021-06-22 10:00:22 +020043BOOT_LOG_MODULE_REGISTER(serial_adapter);
Emanuele Di Santo9f1933d2018-11-20 10:59:59 +010044
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020045/** @brief Console input representation
46 *
47 * This struct is used to represent an input line from a serial interface.
48 */
49struct line_input {
Carles Cufi5ceeddb2018-06-15 12:43:22 +020050 /** Required to use sys_slist */
51 sys_snode_t node;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020052
Carles Cufi5ceeddb2018-06-15 12:43:22 +020053 int len;
54 /** Buffer where the input line is recorded */
55 char line[CONFIG_BOOT_MAX_LINE_INPUT_LEN];
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020056};
57
Dominik Ermel1422b4b2020-09-11 11:31:38 +000058static struct device const *uart_dev;
Jamie McCraecde36392023-02-17 08:44:38 +000059static struct line_input line_bufs[CONFIG_BOOT_LINE_BUFS];
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020060
Carles Cufi6400f0b2018-07-17 17:32:12 +020061static sys_slist_t avail_queue;
62static sys_slist_t lines_queue;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020063
Kumar Gala0813efe2020-05-27 12:25:41 -050064static uint16_t cur;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020065
66static int boot_uart_fifo_getline(char **line);
67static int boot_uart_fifo_init(void);
68
69int
70console_out(int c)
71{
Carles Cufi5ceeddb2018-06-15 12:43:22 +020072 uart_poll_out(uart_dev, c);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020073
Carles Cufi5ceeddb2018-06-15 12:43:22 +020074 return c;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020075}
76
77void
78console_write(const char *str, int cnt)
79{
Carles Cufi5ceeddb2018-06-15 12:43:22 +020080 int i;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020081
Carles Cufi5ceeddb2018-06-15 12:43:22 +020082 for (i = 0; i < cnt; i++) {
83 if (console_out((int)str[i]) == EOF) {
84 break;
85 }
86 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020087}
88
89int
90console_read(char *str, int str_size, int *newline)
91{
Carles Cufi5ceeddb2018-06-15 12:43:22 +020092 char *line;
93 int len;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020094
Carles Cufi5ceeddb2018-06-15 12:43:22 +020095 len = boot_uart_fifo_getline(&line);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020096
Carles Cufi5ceeddb2018-06-15 12:43:22 +020097 if (line == NULL) {
98 *newline = 0;
99 return 0;
100 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200101
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200102 if (len > str_size - 1) {
103 len = str_size - 1;
104 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200105
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200106 memcpy(str, line, len);
107 str[len] = '\0';
108 *newline = 1;
109 return len + 1;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200110}
111
112int
113boot_console_init(void)
114{
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200115 int i;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200116
Carles Cufi6400f0b2018-07-17 17:32:12 +0200117 /* Zephyr UART handler takes an empty buffer from avail_queue,
118 * stores UART input in it until EOL, and then puts it into
119 * lines_queue.
120 */
121 sys_slist_init(&avail_queue);
122 sys_slist_init(&lines_queue);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200123
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200124 for (i = 0; i < ARRAY_SIZE(line_bufs); i++) {
Carles Cufi6400f0b2018-07-17 17:32:12 +0200125 sys_slist_append(&avail_queue, &line_bufs[i].node);
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200126 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200127
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200128 return boot_uart_fifo_init();
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200129}
130
131static void
Dominik Ermel1422b4b2020-09-11 11:31:38 +0000132boot_uart_fifo_callback(const struct device *dev, void *user_data)
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200133{
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200134 static struct line_input *cmd;
Kumar Gala0813efe2020-05-27 12:25:41 -0500135 uint8_t byte;
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200136 int rx;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200137
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +0200138 uart_irq_update(uart_dev);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200139
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +0200140 if (!uart_irq_rx_ready(uart_dev)) {
141 return;
142 }
143
144 while (true) {
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200145 rx = uart_fifo_read(uart_dev, &byte, 1);
146 if (rx != 1) {
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +0200147 break;
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200148 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200149
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200150 if (!cmd) {
151 sys_snode_t *node;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200152
Carles Cufi6400f0b2018-07-17 17:32:12 +0200153 node = sys_slist_get(&avail_queue);
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200154 if (!node) {
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +0200155 BOOT_LOG_ERR("Not enough memory to store"
156 " incoming data!");
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200157 return;
158 }
159 cmd = CONTAINER_OF(node, struct line_input, node);
160 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200161
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200162 if (cur < CONFIG_BOOT_MAX_LINE_INPUT_LEN) {
163 cmd->line[cur++] = byte;
164 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200165
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200166 if (byte == '\n') {
167 cmd->len = cur;
Carles Cufi6400f0b2018-07-17 17:32:12 +0200168 sys_slist_append(&lines_queue, &cmd->node);
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200169 cur = 0;
Carles Cufib124e392018-07-17 17:27:50 +0200170 cmd = NULL;
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200171 }
172 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200173}
174
175static int
176boot_uart_fifo_getline(char **line)
177{
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200178 static struct line_input *cmd;
179 sys_snode_t *node;
Andrzej Puzdrowski30117142018-06-18 14:43:14 +0200180 int key;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200181
Andrzej Puzdrowski30117142018-06-18 14:43:14 +0200182 key = irq_lock();
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200183 /* Recycle cmd buffer returned previous time */
184 if (cmd != NULL) {
Carles Cufi6400f0b2018-07-17 17:32:12 +0200185 if (sys_slist_peek_tail(&avail_queue) != &cmd->node) {
186 sys_slist_append(&avail_queue, &cmd->node);
Andrzej Puzdrowski30117142018-06-18 14:43:14 +0200187 }
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200188 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200189
Carles Cufi6400f0b2018-07-17 17:32:12 +0200190 node = sys_slist_get(&lines_queue);
Andrzej Puzdrowski30117142018-06-18 14:43:14 +0200191 irq_unlock(key);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200192
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200193 if (node == NULL) {
194 cmd = NULL;
195 *line = NULL;
Andrzej Puzdrowski30117142018-06-18 14:43:14 +0200196
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200197 return 0;
198 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200199
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200200 cmd = CONTAINER_OF(node, struct line_input, node);
201 *line = cmd->line;
202 return cmd->len;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200203}
204
205static int
206boot_uart_fifo_init(void)
207{
Dominik Ermel5397c132023-04-18 16:42:30 +0000208#if defined(CONFIG_BOOT_SERIAL_UART)
209
Dominik Ermel1090d8f2021-09-22 08:37:28 +0000210#if DT_HAS_CHOSEN(zephyr_uart_mcumgr)
211 uart_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_uart_mcumgr));
212#else
Johann Fischera6e1e9e2021-07-30 16:01:03 +0200213 uart_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
Dominik Ermel5397c132023-04-18 16:42:30 +0000214#endif
215
Dominik Ermel1090d8f2021-09-22 08:37:28 +0000216#elif defined(CONFIG_BOOT_SERIAL_CDC_ACM)
217 uart_dev = DEVICE_DT_GET_ONE(zephyr_cdc_acm_uart);
Dominik Ermel5397c132023-04-18 16:42:30 +0000218#else
219#error No serial recovery device selected
Dominik Ermel1090d8f2021-09-22 08:37:28 +0000220#endif
221
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200222
Johann Fischera6e1e9e2021-07-30 16:01:03 +0200223 if (!device_is_ready(uart_dev)) {
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200224 return (-1);
225 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200226
Johann Fischera6e1e9e2021-07-30 16:01:03 +0200227#if CONFIG_BOOT_SERIAL_CDC_ACM
228 int rc = usb_enable(NULL);
229 if (rc) {
230 return (-1);
231 }
232#endif
233
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200234 uart_irq_callback_set(uart_dev, boot_uart_fifo_callback);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200235
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200236 /* Drain the fifo */
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +0200237 if (uart_irq_rx_ready(uart_dev)) {
Johann Fischera6e1e9e2021-07-30 16:01:03 +0200238 uint8_t c;
239
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +0200240 while (uart_fifo_read(uart_dev, &c, 1)) {
241 ;
242 }
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200243 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200244
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200245 cur = 0;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200246
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200247 uart_irq_rx_enable(uart_dev);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200248
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200249 return 0;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200250}