diff --git a/boards/native/Makefile.features b/boards/native/Makefile.features index 3779afa382dfd..582df9dc1187c 100644 --- a/boards/native/Makefile.features +++ b/boards/native/Makefile.features @@ -3,6 +3,8 @@ FEATURES_PROVIDED += periph_cpuid FEATURES_PROVIDED += periph_random FEATURES_PROVIDED += periph_rtc FEATURES_PROVIDED += periph_timer +FEATURES_PROVIDED += periph_uart +FEATURES_PROVIDED += periph_gpio # Various other features (if any) FEATURES_PROVIDED += config diff --git a/cpu/native/async_read.c b/cpu/native/async_read.c new file mode 100644 index 0000000000000..5e6faa34b69b3 --- /dev/null +++ b/cpu/native/async_read.c @@ -0,0 +1,160 @@ +/** + * Multiple asynchronus read on file descriptors + * + * Copyright (C) 2015 Ludwig Knüpfer , + * Martine Lenders + * Kaspar Schleiser + * Ell-i open source co-operative + * Takuo Yonezawa + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + * + * @ingroup native_cpu + * @{ + * @file + * @author Takuo Yonezawa + */ + +#include +#include +#include +#include +#include + +#include "async_read.h" +#include "native_internal.h" + +static int _next_index; +static int _fds[ASYNC_READ_NUMOF]; +static native_async_read_callback_t _native_async_read_callbacks[ASYNC_READ_NUMOF]; + +#ifdef __MACH__ +static pid_t _sigio_child_pids[ASYNC_READ_NUMOF]; +static void _sigio_child(int fd); +#endif + +static void _async_io_isr(void) { + fd_set rfds; + + FD_ZERO(&rfds); + + int max_fd = 0; + + for (int i = 0; i < _next_index; i++) { + FD_SET(_fds[i], &rfds); + + if (max_fd < _fds[i]) { + max_fd = _fds[i]; + } + } + + if (real_select(max_fd + 1, &rfds, NULL, NULL, NULL) > 0) { + for (int i = 0; i < _next_index; i++) { + if (FD_ISSET(_fds[i], &rfds)) { + _native_async_read_callbacks[i](_fds[i]); + } + } + } +} + +void native_async_read_setup(void) { + register_interrupt(SIGIO, _async_io_isr); +} + +void native_async_read_cleanup(void) { + unregister_interrupt(SIGIO); + +#ifdef __MACH__ + for (int i = 0; i < _next_index; i++) { + kill(_sigio_child_pids[i], SIGKILL); + } +#endif +} + +void native_async_read_continue(int fd) { + (void) fd; +#ifdef __MACH__ + for (int i = 0; i < _next_index; i++) { + if (_fds[i] == fd) { + kill(_sigio_child_pids[i], SIGCONT); + } + } +#endif +} + +void native_async_read_add_handler(int fd, native_async_read_callback_t handler) { + if (_next_index >= ASYNC_READ_NUMOF) { + err(EXIT_FAILURE, "native_async_read_add_handler(): too many callbacks"); + } + + _fds[_next_index] = fd; + _native_async_read_callbacks[_next_index] = handler; + +#ifdef __MACH__ + /* tuntap signalled IO is not working in OSX, + * * check http://sourceforge.net/p/tuntaposx/bugs/17/ */ + _sigio_child(_next_index); +#else + /* configure fds to send signals on io */ + if (fcntl(fd, F_SETOWN, _native_pid) == -1) { + err(EXIT_FAILURE, "native_async_read_add_handler(): fcntl(F_SETOWN)"); + } + /* set file access mode to non-blocking */ + if (fcntl(fd, F_SETFL, O_NONBLOCK | O_ASYNC) == -1) { + err(EXIT_FAILURE, "native_async_read_add_handler(): fcntl(F_SETFL)"); + } +#endif /* not OSX */ + + _next_index++; +} + +#ifdef __MACH__ +static void _sigio_child(int index) +{ + int fd = _fds[index]; + pid_t parent = _native_pid; + pid_t child; + if ((child = real_fork()) == -1) { + err(EXIT_FAILURE, "sigio_child: fork"); + } + if (child > 0) { + _sigio_child_pids[index] = child; + + /* return in parent process */ + return; + } + + sigset_t sigmask; + + sigemptyset(&sigmask); + sigaddset(&sigmask, SIGCONT); + sigprocmask(SIG_BLOCK, &sigmask, NULL); + + /* watch tap interface and signal parent process if data is + * available */ + fd_set rfds; + while (1) { + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + if (real_select(fd + 1, &rfds, NULL, NULL, NULL) == 1) { + kill(parent, SIGIO); + } + else { + kill(parent, SIGKILL); + err(EXIT_FAILURE, "osx_sigio_child: select"); + } + + // If SIGCONT is sent before calling suspend(), the process stops + // forever, so using sigwait instead. + + int sig; + + sigemptyset(&sigmask); + sigaddset(&sigmask, SIGCONT); + sigwait(&sigmask, &sig); + } +} +#endif +/** @} */ diff --git a/cpu/native/include/async_read.h b/cpu/native/include/async_read.h new file mode 100644 index 0000000000000..0a1edf237cd75 --- /dev/null +++ b/cpu/native/include/async_read.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2015 Takuo Yonezawa + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for + * more details. + */ + +/** + * @ingroup native_cpu + * @{ + * + * @file + * @brief Multiple asynchronus read on file descriptors + * + * @author Takuo Yonezawa + */ +#ifndef ASYNC_READ_H +#define ASYNC_READ_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Maximum number of file descriptors + */ +#ifndef ASYNC_READ_NUMOF +#define ASYNC_READ_NUMOF 2 +#endif + +/** + * @brief asynchronus read callback type + */ +typedef void (*native_async_read_callback_t)(int fd); + +/** + * @brief initialize asynchronus read system + * + * This registers SIGIO signal handler. + */ +void native_async_read_setup(void); + +/** + * @brief shutdown asynchronus read system + * + * This deregisters SIGIO signal handler. + */ +void native_async_read_cleanup(void); + +/** + * @brief resume monitoring of file descriptors + * + * Call this function after reading file descriptors. + * + * @param[in] fd The file descriptor to monitor + */ +void native_async_read_continue(int fd); + +/** + * @brief start monitoring of file descriptor + * + * @param[in] fd The file descriptor to monitor + * @param[in] handler The callback function to be called when the file + * descriptor is ready to read. + */ +void native_async_read_add_handler(int fd, native_async_read_callback_t handler); + +#ifdef __cplusplus +} +#endif + +#endif +/** @} */ diff --git a/cpu/native/include/periph_conf.h b/cpu/native/include/periph_conf.h index 55fb32f781829..d575b4b215889 100644 --- a/cpu/native/include/periph_conf.h +++ b/cpu/native/include/periph_conf.h @@ -64,6 +64,15 @@ /** @} */ +/** + * @brief UART configuration + * @{ + */ +#ifndef UART_NUMOF +#define UART_NUMOF (1U) +#endif +/** @} */ + #ifdef __cplusplus } #endif diff --git a/cpu/native/include/tty_uart.h b/cpu/native/include/tty_uart.h new file mode 100644 index 0000000000000..0c9feaef41cd6 --- /dev/null +++ b/cpu/native/include/tty_uart.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2015 Takuo Yonezawa + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for + * more details. + */ + +/** + * @ingroup native_cpu + * @{ + * + * @file + * @brief UART implementation based on /dev/tty devices on host + * + * @author Takuo Yonezawa + */ + +#ifndef TTY_UART_H +#define TTY_UART_H + +#include "periph/uart.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief register /dev/tty device to be used for UART + * + * @param[in] uart UART id + * @param[in] name path name for /dev/tty device + */ +void tty_uart_setup(uart_t uart, const char *name); + +/** + * @brief closes files opened + */ +void uart_cleanup(void); + +#ifdef __cplusplus +} +#endif + +#endif +/** @} */ diff --git a/cpu/native/native_cpu.c b/cpu/native/native_cpu.c index 41c4d2656085f..a3c8fe2cd7ea9 100644 --- a/cpu/native/native_cpu.c +++ b/cpu/native/native_cpu.c @@ -59,6 +59,8 @@ extern netdev2_tap_t netdev2_tap; #endif +#include "tty_uart.h" + #include "native_internal.h" #define ENABLE_DEBUG (0) diff --git a/cpu/native/netdev2_tap/netdev2_tap.c b/cpu/native/netdev2_tap/netdev2_tap.c index 671681cc4d0a5..8ba09d2fe56fc 100644 --- a/cpu/native/netdev2_tap/netdev2_tap.c +++ b/cpu/native/netdev2_tap/netdev2_tap.c @@ -48,6 +48,8 @@ #include "native_internal.h" +#include "async_read.h" + #include "net/eui64.h" #include "net/netdev2.h" #include "net/netdev2_eth.h" @@ -63,11 +65,6 @@ /* support one tap interface for now */ netdev2_tap_t netdev2_tap; -#ifdef __MACH__ -pid_t _sigio_child_pid; -static void _sigio_child(netdev2_tap_t *dev); -#endif - /* netdev2 interface */ static int _init(netdev2_t *netdev); static int _send(netdev2_t *netdev, const struct iovec *vector, int n); @@ -209,9 +206,9 @@ static int _recv(netdev2_t *netdev2, char *buf, int len) "That's not me => Dropped\n", hdr->dst[0], hdr->dst[1], hdr->dst[2], hdr->dst[3], hdr->dst[4], hdr->dst[5]); -#ifdef __MACH__ - kill(_sigio_child_pid, SIGCONT); -#endif + + native_async_read_continue(dev->tap_fd); + return 0; } /* work around lost signals */ @@ -232,9 +229,7 @@ static int _recv(netdev2_t *netdev2, char *buf, int len) DEBUG("netdev2_tap: sigpend++\n"); } else { -#ifdef __MACH__ - kill(_sigio_child_pid, SIGCONT); -#endif + native_async_read_continue(dev->tap_fd); } _native_in_syscall--; @@ -269,7 +264,9 @@ void netdev2_tap_setup(netdev2_tap_t *dev, const char *name) { strncpy(dev->tap_name, name, IFNAMSIZ); } -static void _tap_isr(void) { +static void _tap_isr(int fd) { + (void) fd; + netdev2_t *netdev = (netdev2_t *)&netdev2_tap; if (netdev->event_callback) { @@ -350,22 +347,11 @@ static int _init(netdev2_t *netdev) DEBUG("gnrc_tapnet_init(): dev->addr = %02x:%02x:%02x:%02x:%02x:%02x\n", dev->addr[0], dev->addr[1], dev->addr[2], dev->addr[3], dev->addr[4], dev->addr[5]); + /* configure signal handler for fds */ - register_interrupt(SIGIO, _tap_isr); -#ifdef __MACH__ - /* tuntap signalled IO is not working in OSX, - * * check http://sourceforge.net/p/tuntaposx/bugs/17/ */ - _sigio_child(dev); -#else - /* configure fds to send signals on io */ - if (fcntl(dev->tap_fd, F_SETOWN, _native_pid) == -1) { - err(EXIT_FAILURE, "gnrc_tapnet_init(): fcntl(F_SETOWN)"); - } - /* set file access mode to non-blocking */ - if (fcntl(dev->tap_fd, F_SETFL, O_NONBLOCK | O_ASYNC) == -1) { - err(EXIT_FAILURE, "gnrc_tabnet_init(): fcntl(F_SETFL)"); - } -#endif /* not OSX */ + native_async_read_setup(); + native_async_read_add_handler(dev->tap_fd, _tap_isr); + DEBUG("gnrc_tapnet: initialized.\n"); return 0; } @@ -378,40 +364,8 @@ void netdev2_tap_cleanup(netdev2_tap_t *dev) } /* cleanup signal handling */ - unregister_interrupt(SIGIO); -#ifdef __MACH__ - kill(_sigio_child_pid, SIGKILL); -#endif + native_async_read_cleanup(); /* close the tap device */ real_close(dev->tap_fd); } - -#ifdef __MACH__ -static void _sigio_child(netdev2_tap_t *dev) -{ - pid_t parent = _native_pid; - if ((_sigio_child_pid = real_fork()) == -1) { - err(EXIT_FAILURE, "sigio_child: fork"); - } - if (_sigio_child_pid > 0) { - /* return in parent process */ - return; - } - /* watch tap interface and signal parent process if data is - * available */ - fd_set rfds; - while (1) { - FD_ZERO(&rfds); - FD_SET(dev->tap_fd, &rfds); - if (real_select(dev->tap_fd + 1, &rfds, NULL, NULL, NULL) == 1) { - kill(parent, SIGIO); - } - else { - kill(parent, SIGKILL); - err(EXIT_FAILURE, "osx_sigio_child: select"); - } - pause(); - } -} -#endif diff --git a/cpu/native/periph/gpio.c b/cpu/native/periph/gpio.c new file mode 100644 index 0000000000000..d2b912ac6fa81 --- /dev/null +++ b/cpu/native/periph/gpio.c @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2015 Takuo Yonezawa + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for + * more details. + */ + +/** + * @ingroup native_cpu + * @{ + * + * @file + * @brief empty GPIO implementation + * + * @author Takuo Yonezawa + */ + +#include "periph/gpio.h" + +int gpio_init(gpio_t pin, gpio_dir_t dir, gpio_pp_t pullup) { + (void) pin; + (void) dir; + (void) pullup; + + return -1; +} + +int gpio_init_int(gpio_t pin, gpio_pp_t pullup, gpio_flank_t flank, + gpio_cb_t cb, void *arg){ + (void) pin; + (void) pullup; + (void) flank; + (void) cb; + (void) arg; + + return -1; +} + +void gpio_irq_enable(gpio_t pin) { + (void) pin; +} + +void gpio_irq_disable(gpio_t pin) { + (void) pin; +} + +int gpio_read(gpio_t pin) { + (void) pin; + + return 0; +} + +void gpio_set(gpio_t pin) { + (void) pin; +} + +void gpio_clear(gpio_t pin) { + (void) pin; +} + +void gpio_toggle(gpio_t pin) { + (void) pin; +} + +void gpio_write(gpio_t pin, int value) { + (void) pin; + (void) value; +} +/** @} */ diff --git a/cpu/native/periph/uart.c b/cpu/native/periph/uart.c new file mode 100644 index 0000000000000..d482822f6cddd --- /dev/null +++ b/cpu/native/periph/uart.c @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2015 Takuo Yonezawa + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for + * more details. + */ + +/** + * @ingroup native_cpu + * @{ + * + * @file + * @brief UART implementation based on /dev/tty devices on host + * + * @author Takuo Yonezawa + */ + +#include +#include +#include +#include +#include +#include + +#include "thread.h" +#include "periph/uart.h" +#include "native_internal.h" +#include "async_read.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/** + * @brief callback function and its argument + */ +static uart_isr_ctx_t uart_config[UART_NUMOF]; + +/** + * @brief filenames of /dev/tty + */ +static char *tty_device_filenames[UART_NUMOF]; + +/** + * @brief file descriptors of /dev/tty + */ +static int tty_fds[UART_NUMOF]; + +void tty_uart_setup(uart_t uart, const char *filename) +{ + tty_device_filenames[uart] = strndup(filename, PATH_MAX - 1); +} + +static void io_signal_handler(int fd) +{ + uart_t uart; + + for (uart = 0; uart < UART_NUMOF; uart++) { + if (tty_fds[uart] == fd) { + break; + } + } + + int is_first = 1; + + while (1) { + char c; + int status = real_read(fd, &c, 1); + + if (status == 1) { + if (is_first) { + is_first = 0; + DEBUG("read char from serial port"); + } + + DEBUG(" %02x", (unsigned char) c); + + uart_config[uart].rx_cb(uart_config[uart].arg, c); + } else { + if (status == -1 && errno != EAGAIN) { + DEBUG("error: cannot read from serial port\n"); + + uart_config[uart].rx_cb = NULL; + } + + break; + } + } + + if (!is_first) { + DEBUG("\n"); + } + + native_async_read_continue(fd); +} + +int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) +{ + if (uart >= UART_NUMOF) { + return -1; + } + + struct termios termios; + + memset(&termios, 0, sizeof(termios)); + + termios.c_iflag = 0; + termios.c_oflag = 0; + termios.c_cflag = CS8 | CREAD | CLOCAL; + termios.c_lflag = 0; + + speed_t speed; + + switch (baudrate) { + case 0: speed = B0; break; + case 50: speed = B50; break; + case 75: speed = B75; break; + case 110: speed = B110; break; + case 134: speed = B134; break; + case 150: speed = B150; break; + case 200: speed = B200; break; + case 300: speed = B300; break; + case 600: speed = B600; break; + case 1200: speed = B1200; break; + case 1800: speed = B1800; break; + case 2400: speed = B2400; break; + case 4800: speed = B4800; break; + case 9600: speed = B9600; break; + case 19200: speed = B19200; break; + case 38400: speed = B38400; break; + case 57600: speed = B57600; break; + case 115200: speed = B115200; break; + case 230400: speed = B230400 ; break; + default: + return -1; + break; + } + + cfsetospeed(&termios, speed); + cfsetispeed(&termios, speed); + + tty_fds[uart] = real_open(tty_device_filenames[uart], O_RDWR | O_NONBLOCK); + + if (tty_fds[uart] < 0) { + return -3; + } + + tcsetattr(tty_fds[uart], TCSANOW, &termios); + + uart_config[uart].rx_cb = rx_cb; + uart_config[uart].arg = arg; + + native_async_read_setup(); + native_async_read_add_handler(tty_fds[uart], io_signal_handler); + + return 0; +} + +void uart_write(uart_t uart, const uint8_t *data, size_t len) +{ + DEBUG("writing to serial port "); + +#if ENABLE_DEBUG + for (size_t i = 0; i < len; i++) { + DEBUG("%02x ", (unsigned char) data[i]); + } + for (size_t i = 0; i < len; i++) { + DEBUG("%c", (char) data[i]); + } +#endif + + DEBUG("\n"); + + real_write(tty_fds[uart], data, len); +} + +void uart_cleanup(void) { + native_async_read_cleanup(); + + for (uart_t uart = 0; uart < UART_NUMOF; uart++) { + if (uart_config[uart].rx_cb != NULL) { + real_close(tty_fds[uart]); + } + } +} + +/** @} */ diff --git a/cpu/native/reboot.c b/cpu/native/reboot.c index 6641a9ed95a89..069dbc6b416e0 100644 --- a/cpu/native/reboot.c +++ b/cpu/native/reboot.c @@ -29,6 +29,8 @@ void reboot(void) netdev2_tap_cleanup(&netdev2_tap); #endif + uart_cleanup(); + if (real_execve(_native_argv[0], _native_argv, NULL) == -1) { err(EXIT_FAILURE, "reboot: execve"); } diff --git a/cpu/native/startup.c b/cpu/native/startup.c index 2d138daeec6ca..16468a7dac725 100644 --- a/cpu/native/startup.c +++ b/cpu/native/startup.c @@ -35,6 +35,7 @@ #include "board_internal.h" #include "native_internal.h" +#include "tty_uart.h" int _native_null_in_pipe[2]; int _native_null_out_file; @@ -200,7 +201,7 @@ void usage_exit(void) real_printf(" "); #endif - real_printf(" [-i ] [-d] [-e|-E] [-o]\n"); + real_printf(" [-i ] [-d] [-e|-E] [-o] [-c ]\n"); real_printf(" help: %s -h\n", _progname); @@ -216,7 +217,8 @@ void usage_exit(void) -E do not redirect stderr (i.e. leave sterr unchanged despite\n\ daemon/socket io)\n\ -o redirect stdout to file (/tmp/riot.stdout.PID) when not attached\n\ - to socket\n"); + to socket\n\ +-c specify TTY device for UART\n"); real_printf("\n\ The order of command line arguments matters.\n"); @@ -239,6 +241,7 @@ __attribute__((constructor)) static void startup(int argc, char **argv) char *stderrtype = "stdio"; char *stdouttype = "stdio"; char *stdiotype = "stdio"; + int uart = 0; #if defined(MODULE_NETDEV2_TAP) if ( @@ -297,6 +300,16 @@ __attribute__((constructor)) static void startup(int argc, char **argv) else if (strcmp("-o", arg) == 0) { stdouttype = "file"; } + else if (strcmp("-c", arg) == 0) { + if (argp + 1 < argc) { + argp++; + } + else { + usage_exit(); + } + + tty_uart_setup(uart++, argv[argp]); + } else { usage_exit(); }