summaryrefslogtreecommitdiff
path: root/zephyr/shim/src/console.c
blob: f8051d863888486a46e0ed037d442b06ec0c3679 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
/* Copyright 2020 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include <zephyr/device.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/shell/shell.h>
#ifdef CONFIG_SHELL_BACKEND_DUMMY /* nocheck */
#include <zephyr/shell/shell_dummy.h> /* nocheck */
#endif
#include <zephyr/shell/shell_uart.h>
#include <stdbool.h>
#include <string.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/ring_buffer.h>
#include <zephyr/zephyr.h>
#include <zephyr/logging/log.h>

#include "console.h"
#include "printf.h"
#include "task.h"
#include "uart.h"
#include "usb_console.h"
#include "zephyr_console_shim.h"

#if !defined(CONFIG_SHELL_BACKEND_SERIAL) && \
	!defined(CONFIG_SHELL_BACKEND_DUMMY) /* nocheck */
#error Must select either CONFIG_SHELL_BACKEND_SERIAL or \
	CONFIG_SHELL_BACKEND_DUMMY /* nocheck */
#endif
#if defined(CONFIG_SHELL_BACKEND_SERIAL) && \
	defined(CONFIG_SHELL_BACKEND_DUMMY) /* nocheck */
#error Must select only one shell backend
#endif

LOG_MODULE_REGISTER(shim_console, LOG_LEVEL_ERR);

static const struct device *uart_shell_dev =
	DEVICE_DT_GET(DT_CHOSEN(zephyr_shell_uart));
static const struct shell *shell_zephyr;
static struct k_poll_signal shell_uninit_signal;
static struct k_poll_signal shell_init_signal;
/*
 * A flag is kept to indicate if the shell has been (or is about
 * to be) stopped, so that output won't be sent via zephyr_fprintf()
 * (which requires locking the shell).
 */
static bool shell_stopped;
RING_BUF_DECLARE(rx_buffer, CONFIG_UART_RX_BUF_SIZE);

static void uart_rx_handle(const struct device *dev)
{
	static uint8_t scratch;
	static uint8_t *data;
	static uint32_t len, rd_len;

	do {
		/* Get some bytes on the ring buffer */
		len = ring_buf_put_claim(&rx_buffer, &data, rx_buffer.size);
		if (len > 0) {
			/* Read from the FIFO up to `len` bytes */
			rd_len = uart_fifo_read(dev, data, len);

			/* Put `rd_len` bytes on the ring buffer */
			ring_buf_put_finish(&rx_buffer, rd_len);
		} else {
			/*
			 * There's no room on the ring buffer, throw away 1
			 * byte.
			 */
			rd_len = uart_fifo_read(dev, &scratch, 1);
		}
	} while (rd_len != 0 && rd_len == len);
}

static void uart_callback(const struct device *dev, void *user_data)
{
	uart_irq_update(dev);

	if (uart_irq_rx_ready(dev))
		uart_rx_handle(dev);
}

static void shell_uninit_callback(const struct shell *shell, int res)
{
	if (!res) {
		/* Set the new callback */
		uart_irq_callback_user_data_set(uart_shell_dev, uart_callback,
						NULL);

		/*
		 * Disable TX interrupts. We don't actually use TX but for some
		 * reason none of this works without this line.
		 */
		uart_irq_tx_disable(uart_shell_dev);

		/* Enable RX interrupts */
		uart_irq_rx_enable(uart_shell_dev);
	}

	/* Notify the uninit signal that we finished */
	k_poll_signal_raise(&shell_uninit_signal, res);
}

int uart_shell_stop(void)
{
	struct k_poll_event event = K_POLL_EVENT_INITIALIZER(
		K_POLL_TYPE_SIGNAL, K_POLL_MODE_NOTIFY_ONLY,
		&shell_uninit_signal);

	/*
	 * Set the shell_stopped flag so that no output will
	 * be sent to the uart via zephyr_fprintf after this point.
	 */
	shell_stopped = true;
	/* Clear all pending input */
	uart_clear_input();

	/* Disable RX and TX interrupts */
	uart_irq_rx_disable(uart_shell_dev);
	uart_irq_tx_disable(uart_shell_dev);

	/* Initialize the uninit signal */
	k_poll_signal_init(&shell_uninit_signal);

	/* Stop the shell */
	shell_uninit(shell_zephyr, shell_uninit_callback);

	/* Wait for the shell to be turned off, the signal will wake us */
	k_poll(&event, 1, K_FOREVER);

	/* Event was signaled, return the result */
	return event.signal->result;
}

static const struct shell_backend_config_flags shell_cfg_flags =
	SHELL_DEFAULT_BACKEND_CONFIG_FLAGS;

static void shell_init_from_work(struct k_work *work)
{
	bool log_backend = 1;
	uint32_t level = CONFIG_LOG_MAX_LEVEL;
	ARG_UNUSED(work);

#ifdef CONFIG_SHELL_BACKEND_SERIAL
	log_backend = CONFIG_SHELL_BACKEND_SERIAL_LOG_LEVEL > 0;
	if (CONFIG_SHELL_BACKEND_SERIAL_LOG_LEVEL <= LOG_LEVEL_DBG)
		level = CONFIG_SHELL_BACKEND_SERIAL_LOG_LEVEL;
#endif

	/* Initialize the shell and re-enable both RX and TX */
	shell_init(shell_zephyr, uart_shell_dev,
		   shell_cfg_flags, log_backend, level);

	/*
	 * shell_init() always resets the priority back to the default.
	 * Update the priority as setup by the shimmed task code.
	 */
	k_thread_priority_set(shell_zephyr->ctx->tid,
			      EC_TASK_PRIORITY(EC_SHELL_PRIO));

	uart_irq_rx_enable(uart_shell_dev);
	uart_irq_tx_enable(uart_shell_dev);

	/* Notify the init signal that initialization is complete */
	k_poll_signal_raise(&shell_init_signal, 0);
}

void uart_shell_start(void)
{
	static struct k_work shell_init_work;
	struct k_poll_event event = K_POLL_EVENT_INITIALIZER(
		K_POLL_TYPE_SIGNAL, K_POLL_MODE_NOTIFY_ONLY,
		&shell_init_signal);

	/* Disable RX and TX interrupts */
	uart_irq_rx_disable(uart_shell_dev);
	uart_irq_tx_disable(uart_shell_dev);

	/* Initialize k_work to call shell init (this makes it thread safe) */
	k_work_init(&shell_init_work, shell_init_from_work);

	/* Initialize the init signal to make sure we're read to listen */
	k_poll_signal_init(&shell_init_signal);

	/* Submit the work to be run by the kernel */
	k_work_submit(&shell_init_work);

	/* Wait for initialization to be run, the signal will wake us */
	k_poll(&event, 1, K_FOREVER);
	shell_stopped = false;
}

#ifdef CONFIG_SHELL_HELP
static void print_console_help(char *name,
			       const struct zephyr_console_command *command)
{
	if (command->help)
		printk("%s\n", command->help);
	if (command->argdesc)
		printk("Usage: %s %s\n", name, command->argdesc);
}
#endif

int zshim_run_ec_console_command(const struct zephyr_console_command *command,
				 size_t argc, char **argv)
{
	int ret;

	/*
	 * The Zephyr shell only displays the help string and not
	 * the argument descriptor when passing "-h" or "--help".  Mimic the
	 * cros-ec behavior by displaying both the user types "<command> help",
	 */
#ifdef CONFIG_SHELL_HELP
	for (int i = 1; i < argc; i++) {
		if (!command->help && !command->argdesc)
			break;
		if (!strcmp(argv[i], "help")) {
			print_console_help(argv[0], command);
			return 0;
		}
	}
#endif

	ret = command->handler(argc, argv);
	if (ret == EC_SUCCESS)
		return ret;

	/* Print common parameter error conditions and help on error */
	if (ret >= EC_ERROR_PARAM1 && ret < EC_ERROR_PARAM_COUNT)
		printk("Parameter %d invalid\n", ret - EC_ERROR_PARAM1 + 1);
	else if (ret == EC_ERROR_PARAM_COUNT)
		printk("Wrong number of parameters\n");
	else
		printk("Command returned error: %d\n", ret);

#ifdef CONFIG_SHELL_HELP
	print_console_help(argv[0], command);
#endif
	return ret;
}

#if defined(CONFIG_CONSOLE_CHANNEL) && DT_NODE_EXISTS(DT_PATH(ec_console))
#define EC_CONSOLE DT_PATH(ec_console)

static const char * const disabled_channels[] = DT_PROP(EC_CONSOLE, disabled);
static const size_t disabled_channel_count = DT_PROP_LEN(EC_CONSOLE, disabled);
static int init_ec_console(const struct device *unused)
{
	for (size_t i = 0; i < disabled_channel_count; i++)
		console_channel_disable(disabled_channels[i]);

	return 0;
} SYS_INIT(init_ec_console, PRE_KERNEL_1, 50);
#endif /* CONFIG_CONSOLE_CHANNEL && DT_NODE_EXISTS(DT_PATH(ec_console)) */

static int init_ec_shell(const struct device *unused)
{
#if defined(CONFIG_SHELL_BACKEND_SERIAL)
		shell_zephyr = shell_backend_uart_get_ptr();
#elif defined(CONFIG_SHELL_BACKEND_DUMMY) /* nocheck */
		shell_zephyr = shell_backend_dummy_get_ptr(); /* nocheck */
#else
#error A shell backend must be enabled
#endif
	return 0;
} SYS_INIT(init_ec_shell, PRE_KERNEL_1, 50);

#ifdef TEST_BUILD
const struct shell *get_ec_shell(void)
{
	return shell_zephyr;
}
#endif

void uart_tx_start(void)
{
}

int uart_tx_ready(void)
{
	return 1;
}

int uart_tx_char_raw(void *context, int c)
{
	uart_write_char(c);
	return 0;
}

void uart_write_char(char c)
{
	uart_poll_out(uart_shell_dev, c);

	if (IS_ENABLED(CONFIG_PLATFORM_EC_HOSTCMD_CONSOLE))
		console_buf_notify_chars(&c, 1);
}

void uart_flush_output(void)
{
	uart_tx_flush();
}

void uart_tx_flush(void)
{
	while (!uart_irq_tx_complete(uart_shell_dev))
		;
}

int uart_getc(void)
{
	uint8_t c;

	if (ring_buf_get(&rx_buffer, &c, 1)) {
		return c;
	}
	return -1;
}

void uart_clear_input(void)
{
	/* Reset the input ring buffer */
	ring_buf_reset(&rx_buffer);
}

static void handle_sprintf_rv(int rv, size_t *len)
{
	if (rv < 0) {
		LOG_ERR("Print buffer is too small");
		*len = CONFIG_SHELL_PRINTF_BUFF_SIZE;
	} else {
		*len += rv;
	}
}

static void zephyr_print(const char *buff, size_t size)
{
	/*
	 * shell_* functions can not be used in ISRs so use printk instead.
	 * If the shell is about to be (or is) stopped, use printk, since the
	 * output may be stalled and the shell mutex held.
	 * Also, console_buf_notify_chars uses a mutex, which may not be
	 * locked in ISRs.
	 */
	if (k_is_in_isr() || shell_stopped ||
			shell_zephyr->ctx->state != SHELL_STATE_ACTIVE) {
		printk("%s", buff);
	} else {
		shell_fprintf(shell_zephyr, SHELL_NORMAL, "%s", buff);
		if (IS_ENABLED(CONFIG_PLATFORM_EC_HOSTCMD_CONSOLE))
			console_buf_notify_chars(buff, size);
		if (IS_ENABLED(CONFIG_PLATFORM_EC_CONSOLE_DEBUG))
			printk("%s", buff);
	}
}

#if defined(CONFIG_USB_CONSOLE) || defined(CONFIG_USB_CONSOLE_STREAM)
BUILD_ASSERT(0, "USB console is not supported with Zephyr");
#endif /* defined(CONFIG_USB_CONSOLE) || defined(CONFIG_USB_CONSOLE_STREAM) */

int cputs(enum console_channel channel, const char *outstr)
{
	/* Filter out inactive channels */
	if (console_channel_is_disabled(channel))
		return EC_SUCCESS;

	zephyr_print(outstr, strlen(outstr));

	return 0;
}

int cprintf(enum console_channel channel, const char *format, ...)
{
	int rv;
	va_list args;
	size_t len = 0;
	char buff[CONFIG_SHELL_PRINTF_BUFF_SIZE];

	/* Filter out inactive channels */
	if (console_channel_is_disabled(channel))
		return EC_SUCCESS;

	va_start(args, format);
	rv = crec_vsnprintf(buff, CONFIG_SHELL_PRINTF_BUFF_SIZE, format, args);
	va_end(args);
	handle_sprintf_rv(rv, &len);

	zephyr_print(buff, len);

	return rv > 0 ? EC_SUCCESS : rv;
}

int cprints(enum console_channel channel, const char *format, ...)
{
	int rv;
	va_list args;
	char buff[CONFIG_SHELL_PRINTF_BUFF_SIZE];
	size_t len = 0;

	/* Filter out inactive channels */
	if (console_channel_is_disabled(channel))
		return EC_SUCCESS;

	rv = crec_snprintf(buff, CONFIG_SHELL_PRINTF_BUFF_SIZE, "[%pT ",
			 PRINTF_TIMESTAMP_NOW);
	handle_sprintf_rv(rv, &len);

	va_start(args, format);
	rv = crec_vsnprintf(buff + len, CONFIG_SHELL_PRINTF_BUFF_SIZE - len,
			  format, args);
	va_end(args);
	handle_sprintf_rv(rv, &len);

	rv = crec_snprintf(buff + len, CONFIG_SHELL_PRINTF_BUFF_SIZE - len,
			 "]\n");
	handle_sprintf_rv(rv, &len);

	zephyr_print(buff, len);

	return rv > 0 ? EC_SUCCESS : rv;
}