blob: bff34a2507618c91dcb7351da432708c10199517 [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
Carlos Falgueras GarcĂ­aa4b4b0f2021-06-22 10:00:22 +020038BOOT_LOG_MODULE_REGISTER(serial_adapter);
Emanuele Di Santo9f1933d2018-11-20 10:59:59 +010039
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020040/** @brief Console input representation
41 *
42 * This struct is used to represent an input line from a serial interface.
43 */
44struct line_input {
Carles Cufi5ceeddb2018-06-15 12:43:22 +020045 /** Required to use sys_slist */
46 sys_snode_t node;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020047
Carles Cufi5ceeddb2018-06-15 12:43:22 +020048 int len;
49 /** Buffer where the input line is recorded */
50 char line[CONFIG_BOOT_MAX_LINE_INPUT_LEN];
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020051};
52
Dominik Ermel1422b4b2020-09-11 11:31:38 +000053static struct device const *uart_dev;
Jamie McCraecde36392023-02-17 08:44:38 +000054static struct line_input line_bufs[CONFIG_BOOT_LINE_BUFS];
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020055
Carles Cufi6400f0b2018-07-17 17:32:12 +020056static sys_slist_t avail_queue;
57static sys_slist_t lines_queue;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020058
Kumar Gala0813efe2020-05-27 12:25:41 -050059static uint16_t cur;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020060
61static int boot_uart_fifo_getline(char **line);
62static int boot_uart_fifo_init(void);
63
64int
65console_out(int c)
66{
Carles Cufi5ceeddb2018-06-15 12:43:22 +020067 uart_poll_out(uart_dev, c);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020068
Carles Cufi5ceeddb2018-06-15 12:43:22 +020069 return c;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020070}
71
72void
73console_write(const char *str, int cnt)
74{
Carles Cufi5ceeddb2018-06-15 12:43:22 +020075 int i;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020076
Carles Cufi5ceeddb2018-06-15 12:43:22 +020077 for (i = 0; i < cnt; i++) {
78 if (console_out((int)str[i]) == EOF) {
79 break;
80 }
81 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020082}
83
84int
85console_read(char *str, int str_size, int *newline)
86{
Carles Cufi5ceeddb2018-06-15 12:43:22 +020087 char *line;
88 int len;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020089
Carles Cufi5ceeddb2018-06-15 12:43:22 +020090 len = boot_uart_fifo_getline(&line);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020091
Carles Cufi5ceeddb2018-06-15 12:43:22 +020092 if (line == NULL) {
93 *newline = 0;
94 return 0;
95 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020096
Carles Cufi5ceeddb2018-06-15 12:43:22 +020097 if (len > str_size - 1) {
98 len = str_size - 1;
99 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200100
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200101 memcpy(str, line, len);
102 str[len] = '\0';
103 *newline = 1;
104 return len + 1;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200105}
106
107int
108boot_console_init(void)
109{
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200110 int i;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200111
Carles Cufi6400f0b2018-07-17 17:32:12 +0200112 /* Zephyr UART handler takes an empty buffer from avail_queue,
113 * stores UART input in it until EOL, and then puts it into
114 * lines_queue.
115 */
116 sys_slist_init(&avail_queue);
117 sys_slist_init(&lines_queue);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200118
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200119 for (i = 0; i < ARRAY_SIZE(line_bufs); i++) {
Carles Cufi6400f0b2018-07-17 17:32:12 +0200120 sys_slist_append(&avail_queue, &line_bufs[i].node);
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200121 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200122
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200123 return boot_uart_fifo_init();
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200124}
125
126static void
Dominik Ermel1422b4b2020-09-11 11:31:38 +0000127boot_uart_fifo_callback(const struct device *dev, void *user_data)
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200128{
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200129 static struct line_input *cmd;
Kumar Gala0813efe2020-05-27 12:25:41 -0500130 uint8_t byte;
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200131 int rx;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200132
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +0200133 uart_irq_update(uart_dev);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200134
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +0200135 if (!uart_irq_rx_ready(uart_dev)) {
136 return;
137 }
138
139 while (true) {
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200140 rx = uart_fifo_read(uart_dev, &byte, 1);
141 if (rx != 1) {
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +0200142 break;
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200143 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200144
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200145 if (!cmd) {
146 sys_snode_t *node;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200147
Carles Cufi6400f0b2018-07-17 17:32:12 +0200148 node = sys_slist_get(&avail_queue);
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200149 if (!node) {
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +0200150 BOOT_LOG_ERR("Not enough memory to store"
151 " incoming data!");
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200152 return;
153 }
154 cmd = CONTAINER_OF(node, struct line_input, node);
155 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200156
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200157 if (cur < CONFIG_BOOT_MAX_LINE_INPUT_LEN) {
158 cmd->line[cur++] = byte;
159 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200160
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200161 if (byte == '\n') {
162 cmd->len = cur;
Carles Cufi6400f0b2018-07-17 17:32:12 +0200163 sys_slist_append(&lines_queue, &cmd->node);
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200164 cur = 0;
Carles Cufib124e392018-07-17 17:27:50 +0200165 cmd = NULL;
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200166 }
167 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200168}
169
170static int
171boot_uart_fifo_getline(char **line)
172{
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200173 static struct line_input *cmd;
174 sys_snode_t *node;
Andrzej Puzdrowski30117142018-06-18 14:43:14 +0200175 int key;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200176
Andrzej Puzdrowski30117142018-06-18 14:43:14 +0200177 key = irq_lock();
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200178 /* Recycle cmd buffer returned previous time */
179 if (cmd != NULL) {
Carles Cufi6400f0b2018-07-17 17:32:12 +0200180 if (sys_slist_peek_tail(&avail_queue) != &cmd->node) {
181 sys_slist_append(&avail_queue, &cmd->node);
Andrzej Puzdrowski30117142018-06-18 14:43:14 +0200182 }
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200183 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200184
Carles Cufi6400f0b2018-07-17 17:32:12 +0200185 node = sys_slist_get(&lines_queue);
Andrzej Puzdrowski30117142018-06-18 14:43:14 +0200186 irq_unlock(key);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200187
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200188 if (node == NULL) {
189 cmd = NULL;
190 *line = NULL;
Andrzej Puzdrowski30117142018-06-18 14:43:14 +0200191
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200192 return 0;
193 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200194
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200195 cmd = CONTAINER_OF(node, struct line_input, node);
196 *line = cmd->line;
197 return cmd->len;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200198}
199
200static int
201boot_uart_fifo_init(void)
202{
Dominik Ermel5397c132023-04-18 16:42:30 +0000203#if defined(CONFIG_BOOT_SERIAL_UART)
204
Dominik Ermel1090d8f2021-09-22 08:37:28 +0000205#if DT_HAS_CHOSEN(zephyr_uart_mcumgr)
206 uart_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_uart_mcumgr));
207#else
Johann Fischera6e1e9e2021-07-30 16:01:03 +0200208 uart_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
Dominik Ermel5397c132023-04-18 16:42:30 +0000209#endif
210
Dominik Ermel1090d8f2021-09-22 08:37:28 +0000211#elif defined(CONFIG_BOOT_SERIAL_CDC_ACM)
212 uart_dev = DEVICE_DT_GET_ONE(zephyr_cdc_acm_uart);
Dominik Ermel5397c132023-04-18 16:42:30 +0000213#else
214#error No serial recovery device selected
Dominik Ermel1090d8f2021-09-22 08:37:28 +0000215#endif
216
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200217
Johann Fischera6e1e9e2021-07-30 16:01:03 +0200218 if (!device_is_ready(uart_dev)) {
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200219 return (-1);
220 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200221
Johann Fischera6e1e9e2021-07-30 16:01:03 +0200222#if CONFIG_BOOT_SERIAL_CDC_ACM
223 int rc = usb_enable(NULL);
224 if (rc) {
225 return (-1);
226 }
227#endif
228
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200229 uart_irq_callback_set(uart_dev, boot_uart_fifo_callback);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200230
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200231 /* Drain the fifo */
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +0200232 if (uart_irq_rx_ready(uart_dev)) {
Johann Fischera6e1e9e2021-07-30 16:01:03 +0200233 uint8_t c;
234
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +0200235 while (uart_fifo_read(uart_dev, &c, 1)) {
236 ;
237 }
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200238 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200239
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200240 cur = 0;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200241
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200242 uart_irq_rx_enable(uart_dev);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200243
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200244 return 0;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200245}