Loading...
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 | /*
* Copyright (c) 2018 Oticon A/S
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <ctype.h>
#include "init.h"
#include "kernel.h"
#include "console/console.h"
#include "posix_board_if.h"
#include <string.h>
#include <sys/time.h>
#include <sys/select.h>
#include <unistd.h>
#define DEBUG_ECHO 0
#if (DEBUG_ECHO)
#define ECHO(...) printf(__VA_ARGS__)
#else
#define ECHO(...)
#endif
#if defined(CONFIG_NATIVE_POSIX_STDOUT_CONSOLE)
/**
*
* @brief Initialize the driver that provides the printk output
*
*/
static void native_posix_stdout_init(void)
{
/* Let's ensure that even if we are redirecting to a file, we get stdout
* and stderr line buffered (default for console). Note that glibc
* ignores size. But just in case we set a reasonable number in case
* somebody tries to compile against a different library
*/
setvbuf(stdout, NULL, _IOLBF, 512);
setvbuf(stderr, NULL, _IOLBF, 512);
extern void __printk_hook_install(int (*fn)(int));
__printk_hook_install(putchar);
}
/**
* Ensure that whatever was written thru printk is displayed now
*/
void posix_flush_stdout(void)
{
fflush(stdout);
}
#endif /* CONFIG_NATIVE_POSIX_STDOUT_CONSOLE */
#if defined(CONFIG_NATIVE_POSIX_STDIN_CONSOLE)
#define VALID_DIRECTIVES \
"Valid native console driver directives:\n" \
" !wait %%u\n" \
" !quit\n"
static struct k_fifo *avail_queue;
static struct k_fifo *lines_queue;
static uint8_t (*completion_cb)(char *line, uint8_t len);
static bool stdin_is_tty;
static K_KERNEL_STACK_DEFINE(stack, CONFIG_ARCH_POSIX_RECOMMENDED_STACK_SIZE);
static struct k_thread native_stdio_thread;
static inline void found_eof(void)
{
/*
* Once stdin is closed or the input file has ended,
* there is no need to try again
*/
ECHO("Got EOF\n");
k_thread_abort(&native_stdio_thread);
}
/*
* Check if the command is a directive the driver handles on its own
* and if it is, handle it.
* If not return 0 (so it can be passed to the shell)
*
* Inputs
* s Command string
* towait Pointer to the amount of time wait until attempting to receive
* the next command
*
* return 0 if it is not a directive
* return > 0 if it was a directive (command starts with '!')
* return 2 if the driver directive requires to pause processing input
*/
static int catch_directive(char *s, int32_t *towait)
{
while (*s != 0 && isspace(*s)) {
s++;
}
if (*s != '!') {
return 0;
}
if (strncmp(s, "!wait", 5) == 0) {
int ret;
ret = sscanf(&s[5], "%i", towait);
if (ret != 1) {
posix_print_error_and_exit("%s(): '%s' not understood, "
"!wait syntax: !wait %%i\n",
__func__, s);
}
return 2;
} else if (strcmp(s, "!quit") == 0) {
posix_exit(0);
}
posix_print_warning("%s(): '%s' not understood\n" VALID_DIRECTIVES,
__func__, s);
return 1;
}
/**
* Check if there is data ready in stdin
*/
static int stdin_not_ready(void)
{
int ready;
fd_set readfds;
struct timeval timeout;
timeout.tv_usec = 0;
timeout.tv_sec = 0;
FD_ZERO(&readfds);
FD_SET(STDIN_FILENO, &readfds);
ready = select(STDIN_FILENO+1, &readfds, NULL, NULL, &timeout);
if (ready == 0) {
return 1;
} else if (ready == -1) {
posix_print_error_and_exit("%s: Error on select ()\n",
__func__);
}
return 0;
}
/**
* Check if there is any line in the stdin buffer,
* if there is and we have available shell buffers feed it to the shell
*
* This function returns how long the thread should wait in ms,
* before checking again the stdin buffer
*/
static int32_t attempt_read_from_stdin(void)
{
static struct console_input *cmd;
int32_t towait = CONFIG_NATIVE_STDIN_POLL_PERIOD;
while (1) {
char *ret;
int last;
int is_directive;
if (feof(stdin)) {
found_eof();
}
/*
* If stdin comes from a terminal, we check if the user has
* input something, and if not we pause the process.
*
* If stdin is not coming from a terminal, but from a file or
* pipe, we always proceed to try to get data and block until
* we do
*/
if (stdin_is_tty && stdin_not_ready()) {
return towait;
}
/* Pick next available shell line buffer */
if (!cmd) {
cmd = k_fifo_get(avail_queue, K_NO_WAIT);
if (!cmd) {
return towait;
}
}
/*
* By default stdin is (_IOLBF) line buffered when connected to
* a terminal and fully buffered (_IOFBF) when connected to a
* pipe/file.
* If we got a terminal: we already checked for it to be ready
* and therefore a full line should be there for us.
*
* If we got a pipe or file we will block until we get a line,
* or we reach EOF
*/
ret = fgets(cmd->line, CONSOLE_MAX_LINE_LEN, stdin);
if (ret == NULL) {
if (feof(stdin)) {
found_eof();
}
/*
* Otherwise this was an unexpected error we do
* not try to handle
*/
return towait;
}
/* Remove a possible end of line and other trailing spaces */
last = (int)strlen(cmd->line) - 1;
while ((last >= 0) && isspace(cmd->line[last])) {
cmd->line[last--] = 0;
}
ECHO("Got: \"%s\"\n", cmd->line);
/*
* This console has a special set of directives which start with
* "!" which we capture here
*/
is_directive = catch_directive(cmd->line, &towait);
if (is_directive == 2) {
return towait;
} else if (is_directive > 0) {
continue;
}
/* Let's give it to the shell to handle */
k_fifo_put(lines_queue, cmd);
cmd = NULL;
}
return towait;
}
/**
* This thread will check if there is any new line in the stdin buffer
* every CONFIG_NATIVE_STDIN_POLL_PERIOD ms
*
* If there is, it will feed it to the shell
*/
static void native_stdio_runner(void *p1, void *p2, void *p3)
{
stdin_is_tty = isatty(STDIN_FILENO);
while (1) {
int32_t wait_time = attempt_read_from_stdin();
k_sleep(wait_time);
}
}
#endif /* CONFIG_NATIVE_POSIX_STDIN_CONSOLE */
static int native_posix_console_init(const struct device *arg)
{
ARG_UNUSED(arg);
#if defined(CONFIG_NATIVE_POSIX_STDOUT_CONSOLE)
native_posix_stdout_init();
#endif
return 0;
}
SYS_INIT(native_posix_console_init, PRE_KERNEL_1,
CONFIG_NATIVE_POSIX_CONSOLE_INIT_PRIORITY);
|