blob: 42af95265102269c9ce20966338f4eb690c68be4 [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)))
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020028#error Zephyr UART console must been disabled if serial_adapter module is used.
29#endif
30
Dominik Ermel1090d8f2021-09-22 08:37:28 +000031#if defined(CONFIG_BOOT_SERIAL_CDC_ACM) && \
32 defined(CONFIG_UART_CONSOLE) && !DT_HAS_CHOSEN(zephyr_uart_mcumgr)
33#error Zephyr UART console must been disabled if CDC ACM is enabled and MCUmgr \
34 has not been redirected to other UART with DTS chosen zephyr,uart-mcumgr.
35#endif
36
Carlos Falgueras GarcĂ­aa4b4b0f2021-06-22 10:00:22 +020037BOOT_LOG_MODULE_REGISTER(serial_adapter);
Emanuele Di Santo9f1933d2018-11-20 10:59:59 +010038
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020039/** @brief Console input representation
40 *
41 * This struct is used to represent an input line from a serial interface.
42 */
43struct line_input {
Carles Cufi5ceeddb2018-06-15 12:43:22 +020044 /** Required to use sys_slist */
45 sys_snode_t node;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020046
Carles Cufi5ceeddb2018-06-15 12:43:22 +020047 int len;
48 /** Buffer where the input line is recorded */
49 char line[CONFIG_BOOT_MAX_LINE_INPUT_LEN];
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020050};
51
Dominik Ermel1422b4b2020-09-11 11:31:38 +000052static struct device const *uart_dev;
Jamie McCraecde36392023-02-17 08:44:38 +000053static struct line_input line_bufs[CONFIG_BOOT_LINE_BUFS];
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020054
Carles Cufi6400f0b2018-07-17 17:32:12 +020055static sys_slist_t avail_queue;
56static sys_slist_t lines_queue;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020057
Kumar Gala0813efe2020-05-27 12:25:41 -050058static uint16_t cur;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020059
60static int boot_uart_fifo_getline(char **line);
61static int boot_uart_fifo_init(void);
62
63int
64console_out(int c)
65{
Carles Cufi5ceeddb2018-06-15 12:43:22 +020066 uart_poll_out(uart_dev, c);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020067
Carles Cufi5ceeddb2018-06-15 12:43:22 +020068 return c;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020069}
70
71void
72console_write(const char *str, int cnt)
73{
Carles Cufi5ceeddb2018-06-15 12:43:22 +020074 int i;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020075
Carles Cufi5ceeddb2018-06-15 12:43:22 +020076 for (i = 0; i < cnt; i++) {
77 if (console_out((int)str[i]) == EOF) {
78 break;
79 }
80 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020081}
82
83int
84console_read(char *str, int str_size, int *newline)
85{
Carles Cufi5ceeddb2018-06-15 12:43:22 +020086 char *line;
87 int len;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020088
Carles Cufi5ceeddb2018-06-15 12:43:22 +020089 len = boot_uart_fifo_getline(&line);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020090
Carles Cufi5ceeddb2018-06-15 12:43:22 +020091 if (line == NULL) {
92 *newline = 0;
93 return 0;
94 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020095
Carles Cufi5ceeddb2018-06-15 12:43:22 +020096 if (len > str_size - 1) {
97 len = str_size - 1;
98 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020099
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200100 memcpy(str, line, len);
101 str[len] = '\0';
102 *newline = 1;
103 return len + 1;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200104}
105
106int
107boot_console_init(void)
108{
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200109 int i;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200110
Carles Cufi6400f0b2018-07-17 17:32:12 +0200111 /* Zephyr UART handler takes an empty buffer from avail_queue,
112 * stores UART input in it until EOL, and then puts it into
113 * lines_queue.
114 */
115 sys_slist_init(&avail_queue);
116 sys_slist_init(&lines_queue);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200117
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200118 for (i = 0; i < ARRAY_SIZE(line_bufs); i++) {
Carles Cufi6400f0b2018-07-17 17:32:12 +0200119 sys_slist_append(&avail_queue, &line_bufs[i].node);
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200120 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200121
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200122 return boot_uart_fifo_init();
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200123}
124
125static void
Dominik Ermel1422b4b2020-09-11 11:31:38 +0000126boot_uart_fifo_callback(const struct device *dev, void *user_data)
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200127{
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200128 static struct line_input *cmd;
Kumar Gala0813efe2020-05-27 12:25:41 -0500129 uint8_t byte;
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200130 int rx;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200131
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +0200132 uart_irq_update(uart_dev);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200133
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +0200134 if (!uart_irq_rx_ready(uart_dev)) {
135 return;
136 }
137
138 while (true) {
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200139 rx = uart_fifo_read(uart_dev, &byte, 1);
140 if (rx != 1) {
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +0200141 break;
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200142 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200143
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200144 if (!cmd) {
145 sys_snode_t *node;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200146
Carles Cufi6400f0b2018-07-17 17:32:12 +0200147 node = sys_slist_get(&avail_queue);
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200148 if (!node) {
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +0200149 BOOT_LOG_ERR("Not enough memory to store"
150 " incoming data!");
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200151 return;
152 }
153 cmd = CONTAINER_OF(node, struct line_input, node);
154 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200155
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200156 if (cur < CONFIG_BOOT_MAX_LINE_INPUT_LEN) {
157 cmd->line[cur++] = byte;
158 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200159
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200160 if (byte == '\n') {
161 cmd->len = cur;
Carles Cufi6400f0b2018-07-17 17:32:12 +0200162 sys_slist_append(&lines_queue, &cmd->node);
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200163 cur = 0;
Carles Cufib124e392018-07-17 17:27:50 +0200164 cmd = NULL;
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200165 }
166 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200167}
168
169static int
170boot_uart_fifo_getline(char **line)
171{
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200172 static struct line_input *cmd;
173 sys_snode_t *node;
Andrzej Puzdrowski30117142018-06-18 14:43:14 +0200174 int key;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200175
Andrzej Puzdrowski30117142018-06-18 14:43:14 +0200176 key = irq_lock();
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200177 /* Recycle cmd buffer returned previous time */
178 if (cmd != NULL) {
Carles Cufi6400f0b2018-07-17 17:32:12 +0200179 if (sys_slist_peek_tail(&avail_queue) != &cmd->node) {
180 sys_slist_append(&avail_queue, &cmd->node);
Andrzej Puzdrowski30117142018-06-18 14:43:14 +0200181 }
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200182 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200183
Carles Cufi6400f0b2018-07-17 17:32:12 +0200184 node = sys_slist_get(&lines_queue);
Andrzej Puzdrowski30117142018-06-18 14:43:14 +0200185 irq_unlock(key);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200186
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200187 if (node == NULL) {
188 cmd = NULL;
189 *line = NULL;
Andrzej Puzdrowski30117142018-06-18 14:43:14 +0200190
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200191 return 0;
192 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200193
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200194 cmd = CONTAINER_OF(node, struct line_input, node);
195 *line = cmd->line;
196 return cmd->len;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200197}
198
199static int
200boot_uart_fifo_init(void)
201{
Dominik Ermel1090d8f2021-09-22 08:37:28 +0000202#if DT_HAS_CHOSEN(zephyr_uart_mcumgr)
203 uart_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_uart_mcumgr));
204#else
205
206#if defined(CONFIG_BOOT_SERIAL_UART)
Johann Fischera6e1e9e2021-07-30 16:01:03 +0200207 uart_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
Dominik Ermel1090d8f2021-09-22 08:37:28 +0000208#elif defined(CONFIG_BOOT_SERIAL_CDC_ACM)
209 uart_dev = DEVICE_DT_GET_ONE(zephyr_cdc_acm_uart);
210#endif
211
Emanuele Di Santoc4bf7802018-07-20 11:39:57 +0200212#endif
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200213
Johann Fischera6e1e9e2021-07-30 16:01:03 +0200214 if (!device_is_ready(uart_dev)) {
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200215 return (-1);
216 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200217
Johann Fischera6e1e9e2021-07-30 16:01:03 +0200218#if CONFIG_BOOT_SERIAL_CDC_ACM
219 int rc = usb_enable(NULL);
220 if (rc) {
221 return (-1);
222 }
223#endif
224
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200225 uart_irq_callback_set(uart_dev, boot_uart_fifo_callback);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200226
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200227 /* Drain the fifo */
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +0200228 if (uart_irq_rx_ready(uart_dev)) {
Johann Fischera6e1e9e2021-07-30 16:01:03 +0200229 uint8_t c;
230
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +0200231 while (uart_fifo_read(uart_dev, &c, 1)) {
232 ;
233 }
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200234 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200235
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200236 cur = 0;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200237
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200238 uart_irq_rx_enable(uart_dev);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200239
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200240 return 0;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200241}