blob: 14adb456b307a1a4a37826ba0873fc6a79793e93 [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
Emanuele Di Santo9f1933d2018-11-20 10:59:59 +010028MCUBOOT_LOG_MODULE_REGISTER(serial_adapter);
29
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020030/** @brief Console input representation
31 *
32 * This struct is used to represent an input line from a serial interface.
33 */
34struct line_input {
Carles Cufi5ceeddb2018-06-15 12:43:22 +020035 /** Required to use sys_slist */
36 sys_snode_t node;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020037
Carles Cufi5ceeddb2018-06-15 12:43:22 +020038 int len;
39 /** Buffer where the input line is recorded */
40 char line[CONFIG_BOOT_MAX_LINE_INPUT_LEN];
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020041};
42
43static struct device *uart_dev;
44static struct line_input line_bufs[2];
45
Carles Cufi6400f0b2018-07-17 17:32:12 +020046static sys_slist_t avail_queue;
47static sys_slist_t lines_queue;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020048
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020049static u16_t cur;
50
51static int boot_uart_fifo_getline(char **line);
52static int boot_uart_fifo_init(void);
53
54int
55console_out(int c)
56{
Carles Cufi5ceeddb2018-06-15 12:43:22 +020057 uart_poll_out(uart_dev, c);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020058
Carles Cufi5ceeddb2018-06-15 12:43:22 +020059 return c;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020060}
61
62void
63console_write(const char *str, int cnt)
64{
Carles Cufi5ceeddb2018-06-15 12:43:22 +020065 int i;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020066
Carles Cufi5ceeddb2018-06-15 12:43:22 +020067 for (i = 0; i < cnt; i++) {
68 if (console_out((int)str[i]) == EOF) {
69 break;
70 }
71 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020072}
73
74int
75console_read(char *str, int str_size, int *newline)
76{
Carles Cufi5ceeddb2018-06-15 12:43:22 +020077 char *line;
78 int len;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020079
Carles Cufi5ceeddb2018-06-15 12:43:22 +020080 len = boot_uart_fifo_getline(&line);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020081
Carles Cufi5ceeddb2018-06-15 12:43:22 +020082 if (line == NULL) {
83 *newline = 0;
84 return 0;
85 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020086
Carles Cufi5ceeddb2018-06-15 12:43:22 +020087 if (len > str_size - 1) {
88 len = str_size - 1;
89 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020090
Carles Cufi5ceeddb2018-06-15 12:43:22 +020091 memcpy(str, line, len);
92 str[len] = '\0';
93 *newline = 1;
94 return len + 1;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +020095}
96
97int
98boot_console_init(void)
99{
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200100 int i;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200101
Carles Cufi6400f0b2018-07-17 17:32:12 +0200102 /* Zephyr UART handler takes an empty buffer from avail_queue,
103 * stores UART input in it until EOL, and then puts it into
104 * lines_queue.
105 */
106 sys_slist_init(&avail_queue);
107 sys_slist_init(&lines_queue);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200108
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200109 for (i = 0; i < ARRAY_SIZE(line_bufs); i++) {
Carles Cufi6400f0b2018-07-17 17:32:12 +0200110 sys_slist_append(&avail_queue, &line_bufs[i].node);
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200111 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200112
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200113 return boot_uart_fifo_init();
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200114}
115
116static void
117boot_uart_fifo_callback(struct device *dev)
118{
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200119 static struct line_input *cmd;
120 u8_t byte;
121 int rx;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200122
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +0200123 uart_irq_update(uart_dev);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200124
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +0200125 if (!uart_irq_rx_ready(uart_dev)) {
126 return;
127 }
128
129 while (true) {
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200130 rx = uart_fifo_read(uart_dev, &byte, 1);
131 if (rx != 1) {
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +0200132 break;
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200133 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200134
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200135 if (!cmd) {
136 sys_snode_t *node;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200137
Carles Cufi6400f0b2018-07-17 17:32:12 +0200138 node = sys_slist_get(&avail_queue);
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200139 if (!node) {
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +0200140 BOOT_LOG_ERR("Not enough memory to store"
141 " incoming data!");
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200142 return;
143 }
144 cmd = CONTAINER_OF(node, struct line_input, node);
145 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200146
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200147 if (cur < CONFIG_BOOT_MAX_LINE_INPUT_LEN) {
148 cmd->line[cur++] = byte;
149 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200150
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200151 if (byte == '\n') {
152 cmd->len = cur;
Carles Cufi6400f0b2018-07-17 17:32:12 +0200153 sys_slist_append(&lines_queue, &cmd->node);
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200154 cur = 0;
Carles Cufib124e392018-07-17 17:27:50 +0200155 cmd = NULL;
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200156 }
157 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200158}
159
160static int
161boot_uart_fifo_getline(char **line)
162{
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200163 static struct line_input *cmd;
164 sys_snode_t *node;
Andrzej Puzdrowski30117142018-06-18 14:43:14 +0200165 int key;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200166
Andrzej Puzdrowski30117142018-06-18 14:43:14 +0200167 key = irq_lock();
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200168 /* Recycle cmd buffer returned previous time */
169 if (cmd != NULL) {
Carles Cufi6400f0b2018-07-17 17:32:12 +0200170 if (sys_slist_peek_tail(&avail_queue) != &cmd->node) {
171 sys_slist_append(&avail_queue, &cmd->node);
Andrzej Puzdrowski30117142018-06-18 14:43:14 +0200172 }
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200173 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200174
Carles Cufi6400f0b2018-07-17 17:32:12 +0200175 node = sys_slist_get(&lines_queue);
Andrzej Puzdrowski30117142018-06-18 14:43:14 +0200176 irq_unlock(key);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200177
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200178 if (node == NULL) {
179 cmd = NULL;
180 *line = NULL;
Andrzej Puzdrowski30117142018-06-18 14:43:14 +0200181
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200182 return 0;
183 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200184
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200185 cmd = CONTAINER_OF(node, struct line_input, node);
186 *line = cmd->line;
187 return cmd->len;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200188}
189
190static int
191boot_uart_fifo_init(void)
192{
Emanuele Di Santoc4bf7802018-07-20 11:39:57 +0200193#ifdef CONFIG_BOOT_SERIAL_UART
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200194 uart_dev = device_get_binding(CONFIG_UART_CONSOLE_ON_DEV_NAME);
Emanuele Di Santoc4bf7802018-07-20 11:39:57 +0200195#elif CONFIG_BOOT_SERIAL_CDC_ACM
196 uart_dev = device_get_binding(CONFIG_CDC_ACM_PORT_NAME);
197#endif
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200198 u8_t c;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200199
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200200 if (!uart_dev) {
201 return (-1);
202 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200203
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200204 uart_irq_callback_set(uart_dev, boot_uart_fifo_callback);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200205
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200206 /* Drain the fifo */
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +0200207 if (uart_irq_rx_ready(uart_dev)) {
208 while (uart_fifo_read(uart_dev, &c, 1)) {
209 ;
210 }
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200211 }
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200212
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200213 cur = 0;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200214
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200215 uart_irq_rx_enable(uart_dev);
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200216
Carles Cufi5e48c552018-06-15 12:58:08 +0200217 /* Enable all interrupts unconditionally. Note that this is due
218 * to Zephyr issue #8393. This should be removed once the
Emanuele Di Santod1fd3f92018-07-20 11:34:14 +0200219 * issue is fixed in upstream Zephyr.
220 */
Carles Cufi5e48c552018-06-15 12:58:08 +0200221 irq_unlock(0);
222
Carles Cufi5ceeddb2018-06-15 12:43:22 +0200223 return 0;
Andrzej Puzdrowskif6f652e2017-10-19 11:21:52 +0200224}