/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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/mynewt.h" #include "hal/hal_uart.h" #include "bsp/bsp.h" #ifdef MN_LINUX #include #endif #ifdef MN_OSX #include #endif #ifdef MN_FreeBSD #include #endif #include #include #include #include #include #include #include #include #include "mcu/mcu_sim.h" #include "native_uart_cfg_priv.h" #include "nimble_syscfg.h" #define UART_CNT 2 #if MYNEWT_VAL(CONSOLE_UART_TX_BUF_SIZE) #define UART_MAX_BYTES_PER_POLL MYNEWT_VAL(CONSOLE_UART_TX_BUF_SIZE) - 2 #else #define UART_MAX_BYTES_PER_POLL 64 #endif #define UART_POLLER_STACK_SZ OS_STACK_ALIGN(1024) 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; }; const char *native_uart_dev_strs[UART_CNT]; /* * XXXX should try to use O_ASYNC/SIGIO for byte arrival notification, * so we wouldn't need an OS for pseudo ttys. */ char *native_uart_log_file = NULL; static int uart_log_fd = -1; 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_open_log(void) { if (native_uart_log_file && uart_log_fd < 0) { uart_log_fd = open(native_uart_log_file, O_WRONLY | O_CREAT | O_TRUNC, 0666); assert(uart_log_fd >= 0); } } static void uart_log_data(struct uart *u, int istx, uint8_t data) { static struct { struct uart *uart; int istx; uint32_t time; int chars_in_line; } state = { .uart = NULL, .istx = 0 }; uint32_t now; char tmpbuf[32]; int len; if (uart_log_fd < 0) { return; } now = os_time_get(); if (state.uart) { if (u != state.uart || now != state.time || istx != state.istx) { /* * End current printout. */ if (write(uart_log_fd, "\n", 1) != 1) { assert(0); } state.uart = NULL; } else { if (state.chars_in_line == 8) { if (write(uart_log_fd, "\n\t", 2) != 2) { assert(0); } state.chars_in_line = 0; } len = snprintf(tmpbuf, sizeof(tmpbuf), "%c (%02x) ", isalnum(data) ? data : '?', data); if (write(uart_log_fd, tmpbuf, len) != len) { assert(0); } state.chars_in_line++; } } if (u && state.uart == NULL) { len = snprintf(tmpbuf, sizeof(tmpbuf), "%u:uart%d %s\n\t%c (%02x) ", now, u - uarts, istx ? "tx" : "rx", isalnum(data) ? data : '?', data); if (write(uart_log_fd, tmpbuf, len) != len) { assert(0); } state.chars_in_line = 1; state.uart = u; state.istx = istx; state.time = now; } } static int uart_transmit_char(struct uart *uart) { int sr; int rc; char ch; 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); return 0; } ch = rc; uart_log_data(uart, 1, ch); OS_EXIT_CRITICAL(sr); rc = write(uart->u_fd, &ch, 1); if (rc <= 0) { /* XXX EOF/error, what now? */ return -1; } return 0; } static void uart_poller(void *arg) { int i; int rc; int bytes; int sr; int didwork; unsigned 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++) { didwork = 0; if (uart->u_tx_run) { uart_transmit_char(uart); didwork = 1; } 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) { uart->u_rx_char = ch; } } if (uart->u_rx_char >= 0) { OS_ENTER_CRITICAL(sr); uart_log_data(uart, 0, uart->u_rx_char); rc = uart->u_rx_func(uart->u_func_arg, uart->u_rx_char); /* Delivered */ if (rc >= 0) { uart->u_rx_char = -1; didwork = 1; } OS_EXIT_CRITICAL(sr); } if (!didwork) { break; } } } uart_log_data(NULL, 0, 0); os_time_delay(OS_TICKS_PER_SEC / 100); } } 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_pty_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; 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_pty_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; } /** * Opens an external device terminal (/dev/cu.<...>). */ static int uart_open_dev(int port, int32_t baudrate, uint8_t databits, uint8_t stopbits, enum hal_uart_parity parity, enum hal_uart_flow_ctl flow_ctl) { const char *filename; int fd; int rc; filename = native_uart_dev_strs[port]; assert(filename != NULL); fd = open(filename, O_RDWR); if (fd < 0) { return -1; } rc = uart_dev_set_attr(fd, baudrate, databits, stopbits, parity, flow_ctl); if (rc != 0) { close(fd); return rc; } dprintf(1, "uart%d at %s\n", port, filename); return fd; } 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; if (!os_started()) { /* * XXX this is a hack. */ uart_transmit_char(&uarts[port]); } 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, "uartpoll", uart_poller, NULL, MYNEWT_VAL(MCU_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; } if (native_uart_dev_strs[port] == NULL) { uart->u_fd = uart_pty(port); } else { uart->u_fd = uart_open_dev(port, baudrate, databits, stopbits, parity, flow_ctl); } if (uart->u_fd < 0) { return -1; } set_nonblock(uart->u_fd); uart_open_log(); uart->u_open = 1; return 0; } int hal_uart_close(int port) { struct uart *uart; int rc; if (port >= UART_CNT) { rc = -1; goto err; } uart = &uarts[port]; if (!uart->u_open) { rc = -1; goto err; } close(uart->u_fd); uart->u_open = 0; return (0); err: return (rc); } int hal_uart_init(int port, void *arg) { return (0); } int uart_set_dev(int port, const char *dev_str) { if (port < 0 || port >= UART_CNT) { return SYS_EINVAL; } if (uarts[port].u_open) { return SYS_EBUSY; } native_uart_dev_strs[port] = dev_str; return 0; }