Files
OpenCellular/chip/host/uart.c
Shawn Nematbakhsh 7bc128f7d1 chip/host: uart: Run uart_monitor_stdin() before task scheduling
After a call to pthread_create(), it is indeterminate which thread  the
caller or the new thread  will next execute. Synchronize with the new
thread and allow it to initialize (and print to console, before the
print can potentially interfere with other prints) before proceeding.

BUG=chromium:715011
BRANCH=None
TEST=Run 'make runtests', verify 'Console input initialized' is seen
before '--- Emulator initialized after reboot ---':

====== Emulator output ======
No flash storage found. Initializing to 0xff.
No RAM data found. Initializing to 0x00.
Console input initialized

--- Emulator initialized after reboot ---

Signed-off-by: Shawn Nematbakhsh <shawnn@chromium.org>
Change-Id: Ieb622e9b7eea2d11d4a11a98bb503a44534f676c
Reviewed-on: https://chromium-review.googlesource.com/854989
Commit-Ready: Shawn N <shawnn@chromium.org>
Tested-by: Shawn N <shawnn@chromium.org>
Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
2018-01-10 18:20:01 -08:00

185 lines
3.6 KiB
C

/* Copyright (c) 2013 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.
*/
/* UART driver for emulator */
#include <pthread.h>
#include <stdio.h>
#include <termio.h>
#include <unistd.h>
#include "common.h"
#include "queue.h"
#include "task.h"
#include "test_util.h"
#include "uart.h"
#include "util.h"
static int stopped = 1;
static int init_done;
static pthread_t input_thread;
#define INPUT_BUFFER_SIZE 16
static int char_available;
static struct queue const cached_char = QUEUE_NULL(INPUT_BUFFER_SIZE, char);
#define CONSOLE_CAPTURE_SIZE 2048
static char capture_buf[CONSOLE_CAPTURE_SIZE];
static int capture_size;
static int capture_enabled;
void test_capture_console(int enabled)
{
if (enabled == capture_enabled)
return;
if (enabled)
capture_size = 0;
else
capture_buf[capture_size] = '\0';
capture_enabled = enabled;
}
static void test_capture_char(char c)
{
if (capture_size == CONSOLE_CAPTURE_SIZE)
return;
capture_buf[capture_size++] = c;
}
const char *test_get_captured_console(void)
{
return (const char *)capture_buf;
}
static void uart_interrupt(void)
{
uart_process_input();
uart_process_output();
}
int uart_init_done(void)
{
return init_done;
}
void uart_tx_start(void)
{
stopped = 0;
task_trigger_test_interrupt(uart_interrupt);
}
void uart_tx_stop(void)
{
stopped = 1;
}
int uart_tx_stopped(void)
{
return stopped;
}
void uart_tx_flush(void)
{
/* Nothing */
}
int uart_tx_ready(void)
{
return 1;
}
int uart_rx_available(void)
{
return char_available;
}
void uart_write_char(char c)
{
if (capture_enabled)
test_capture_char(c);
printf("%c", c);
fflush(stdout);
}
int uart_read_char(void)
{
char ret;
ASSERT(in_interrupt_context());
queue_remove_unit(&cached_char, &ret);
--char_available;
return ret;
}
void uart_inject_char(char *s, int sz)
{
int i;
int num_char;
for (i = 0; i < sz; i += INPUT_BUFFER_SIZE - 1) {
num_char = MIN(INPUT_BUFFER_SIZE - 1, sz - i);
if (queue_space(&cached_char) < num_char)
return;
queue_add_units(&cached_char, s + i, num_char);
char_available = num_char;
task_trigger_test_interrupt(uart_interrupt);
}
}
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t uart_monitor_initialized = PTHREAD_COND_INITIALIZER;
void *uart_monitor_stdin(void *d)
{
struct termios org_settings, new_settings;
char buf[INPUT_BUFFER_SIZE];
int rv;
pthread_mutex_lock(&mutex);
tcgetattr(0, &org_settings);
new_settings = org_settings;
new_settings.c_lflag &= ~(ECHO | ICANON);
new_settings.c_cc[VTIME] = 0;
new_settings.c_cc[VMIN] = 1;
printf("Console input initialized\n");
/* Allow uart_init to proceed now that UART monitor is initialized. */
pthread_cond_signal(&uart_monitor_initialized);
pthread_mutex_unlock(&mutex);
while (1) {
tcsetattr(0, TCSANOW, &new_settings);
rv = read(0, buf, INPUT_BUFFER_SIZE);
if (queue_space(&cached_char) >= rv) {
queue_add_units(&cached_char, buf, rv);
char_available = rv;
}
tcsetattr(0, TCSANOW, &org_settings);
/*
* Trigger emulated interrupt to process input. Keyboard
* input while interrupt handler runs is queued by the
* system.
*/
task_trigger_test_interrupt(uart_interrupt);
}
return 0;
}
void uart_init(void)
{
/* Create UART monitor thread and wait for it to initialize. */
pthread_mutex_lock(&mutex);
pthread_create(&input_thread, NULL, uart_monitor_stdin, NULL);
pthread_cond_wait(&uart_monitor_initialized, &mutex);
pthread_mutex_unlock(&mutex);
stopped = 1; /* Not transmitting yet */
init_done = 1;
}