blob: 2f66016894cc75d68f3fe6d9b95b09464e8f0722 [file] [log] [blame]
/**
* Copyright (c) 2015 Runtime Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "os/os.h"
#include "hal/hal_uart.h"
#include "bsp/bsp.h"
#ifdef MN_LINUX
#include <pty.h>
#endif
#ifdef MN_OSX
#include <util.h>
#endif
#include <stdio.h>
#include <fcntl.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#define UART_MAX_BYTES_PER_POLL 64
#define UART_POLLER_STACK_SZ (1024)
#define UART_POLLER_PRIO 0
struct uart {
int u_open;
int u_fd;
int u_tx_run;
int u_rx_char;
hal_uart_rx_char u_rx_func;
hal_uart_tx_char u_tx_func;
hal_uart_tx_done u_tx_done;
void *u_func_arg;
};
/*
* XXXX should try to use O_ASYNC/SIGIO for byte arrival notification,
* so we wouldn't need an OS for pseudo ttys.
*/
static struct uart uarts[UART_CNT];
static int uart_poller_running;
static struct os_task uart_poller_task;
static os_stack_t uart_poller_stack[UART_POLLER_STACK_SZ];
static void
uart_poller(void *arg)
{
int i;
int rc;
int bytes;
int sr;
char ch;
struct uart *uart;
while (1) {
for (i = 0; i < UART_CNT; i++) {
if (!uarts[i].u_open) {
continue;
}
uart = &uarts[i];
for (bytes = 0; bytes < UART_MAX_BYTES_PER_POLL; bytes++) {
if (uart->u_tx_run) {
OS_ENTER_CRITICAL(sr);
rc = uart->u_tx_func(uart->u_func_arg);
if (rc < 0) {
/*
* No more data to send.
*/
uart->u_tx_run = 0;
if (uart->u_tx_done) {
uart->u_tx_done(uart->u_func_arg);
}
OS_EXIT_CRITICAL(sr);
break;
}
OS_EXIT_CRITICAL(sr);
ch = rc;
rc = write(uart->u_fd, &ch, 1);
if (rc <= 0) {
/* XXX EOF/error, what now? */
assert(0);
break;
}
}
}
for (bytes = 0; bytes < UART_MAX_BYTES_PER_POLL; bytes++) {
if (uart->u_rx_char < 0) {
rc = read(uart->u_fd, &ch, 1);
if (rc == 0) {
/* XXX EOF, what now? */
assert(0);
} else if (rc < 0) {
break;
} else {
uart->u_rx_char = ch;
}
}
if (uart->u_rx_char >= 0) {
OS_ENTER_CRITICAL(sr);
rc = uart->u_rx_func(uart->u_func_arg, uart->u_rx_char);
/* Delivered */
if (rc >= 0) {
uart->u_rx_char = -1;
}
OS_EXIT_CRITICAL(sr);
}
}
}
os_time_delay(10);
}
}
static void
set_nonblock(int fd)
{
int flags;
flags = fcntl(fd, F_GETFL);
if (flags == -1) {
const char msg[] = "fcntl(F_GETFL) fail";
write(1, msg, sizeof(msg));
return;
}
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
const char msg[] = "fcntl(F_SETFL) fail";
write(1, msg, sizeof(msg));
return;
}
}
static int
uart_set_attr(int fd)
{
struct termios tios;
if (tcgetattr(fd, &tios)) {
const char msg[] = "tcgetattr() failed";
write(1, msg, sizeof(msg));
return -1;
}
tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB);
tios.c_cflag |= CS8 | CREAD;
tios.c_iflag = IGNPAR | IXON;
tios.c_oflag = 0;
tios.c_lflag = 0;
if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) {
const char msg[] = "tcsetattr() failed";
write(1, msg, sizeof(msg));
return -1;
}
return 0;
}
static int
uart_pty(int port)
{
int fd;
int loop_slave;
char pty_name[32];
char msg[64];
if (openpty(&fd, &loop_slave, pty_name, NULL, NULL) < 0) {
const char msg[] = "openpty() failed";
write(1, msg, sizeof(msg));
return -1;
}
if (uart_set_attr(loop_slave)) {
goto err;
}
snprintf(msg, sizeof(msg), "uart%d at %s\n", port, pty_name);
write(1, msg, strlen(msg));
return fd;
err:
close(fd);
close(loop_slave);
return -1;
}
void
hal_uart_start_tx(int port)
{
int sr;
if (port >= UART_CNT || uarts[port].u_open == 0) {
return;
}
OS_ENTER_CRITICAL(sr);
uarts[port].u_tx_run = 1;
OS_EXIT_CRITICAL(sr);
}
void
hal_uart_start_rx(int port)
{
/* nothing to do here */
}
void
hal_uart_blocking_tx(int port, uint8_t data)
{
if (port >= UART_CNT || uarts[port].u_open == 0) {
return;
}
/* XXX: Count statistics and add error checking here. */
(void) write(uarts[port].u_fd, &data, sizeof(data));
}
int
hal_uart_init_cbs(int port, hal_uart_tx_char tx_func, hal_uart_tx_done tx_done,
hal_uart_rx_char rx_func, void *arg)
{
struct uart *uart;
int rc;
if (port >= UART_CNT) {
return -1;
}
uart = &uarts[port];
if (uart->u_open) {
return -1;
}
uart->u_tx_func = tx_func;
uart->u_tx_done = tx_done;
uart->u_rx_func = rx_func;
uart->u_func_arg = arg;
uart->u_rx_char = -1;
if (!uart_poller_running) {
uart_poller_running = 1;
rc = os_task_init(&uart_poller_task, "uart_poller", uart_poller, NULL,
UART_POLLER_PRIO, OS_WAIT_FOREVER, uart_poller_stack,
UART_POLLER_STACK_SZ);
assert(rc == 0);
}
return 0;
}
int
hal_uart_config(int port, int32_t baudrate, uint8_t databits, uint8_t stopbits,
enum hal_uart_parity parity, enum hal_uart_flow_ctl flow_ctl)
{
struct uart *uart;
if (port >= UART_CNT) {
return -1;
}
uart = &uarts[port];
if (uart->u_open) {
return -1;
}
uart->u_fd = uart_pty(port);
if (uart->u_fd < 0) {
return -1;
}
set_nonblock(uart->u_fd);
uart->u_open = 1;
return 0;
}