[rtems-libbsd commit] Add poll() and select() support for Termios
Sebastian Huber
sebh at rtems.org
Tue Feb 28 09:55:38 UTC 2017
Module: rtems-libbsd
Branch: master
Commit: 045ff6e11cf21454c6216a1099b44a6fe692586f
Changeset: http://git.rtems.org/rtems-libbsd/commit/?id=045ff6e11cf21454c6216a1099b44a6fe692586f
Author: Sebastian Huber <sebastian.huber at embedded-brains.de>
Date: Tue Feb 21 15:26:31 2017 +0100
Add poll() and select() support for Termios
---
libbsd.py | 3 +-
libbsd_waf.py | 1 +
rtemsbsd/include/machine/rtems-bsd-config.h | 11 +
rtemsbsd/rtems/rtems-kernel-termioskqueuepoll.c | 283 ++++++++++++++++++++++++
testsuite/netshell01/test_main.c | 121 +++++++++-
5 files changed, 416 insertions(+), 3 deletions(-)
diff --git a/libbsd.py b/libbsd.py
index 43107ee..42a9f91 100755
--- a/libbsd.py
+++ b/libbsd.py
@@ -109,8 +109,9 @@ def rtems(mm):
'rtems/rtems-kernel-sysctlbyname.c',
'rtems/rtems-kernel-sysctl.c',
'rtems/rtems-kernel-sysctlnametomib.c',
- 'rtems/rtems-kernel-thread.c',
'rtems/rtems-kernel-timesupport.c',
+ 'rtems/rtems-kernel-termioskqueuepoll.c',
+ 'rtems/rtems-kernel-thread.c',
'rtems/rtems-kernel-vprintf.c',
'rtems/rtems-legacy-rtrequest.c',
'rtems/rtems-legacy-newproc.c',
diff --git a/libbsd_waf.py b/libbsd_waf.py
index 651645d..1dd6f81 100644
--- a/libbsd_waf.py
+++ b/libbsd_waf.py
@@ -1309,6 +1309,7 @@ def build(bld):
'rtemsbsd/rtems/rtems-kernel-sysctl.c',
'rtemsbsd/rtems/rtems-kernel-sysctlbyname.c',
'rtemsbsd/rtems/rtems-kernel-sysctlnametomib.c',
+ 'rtemsbsd/rtems/rtems-kernel-termioskqueuepoll.c',
'rtemsbsd/rtems/rtems-kernel-thread.c',
'rtemsbsd/rtems/rtems-kernel-timesupport.c',
'rtemsbsd/rtems/rtems-kernel-vprintf.c',
diff --git a/rtemsbsd/include/machine/rtems-bsd-config.h b/rtemsbsd/include/machine/rtems-bsd-config.h
index ccc65d7..72876d0 100644
--- a/rtemsbsd/include/machine/rtems-bsd-config.h
+++ b/rtemsbsd/include/machine/rtems-bsd-config.h
@@ -189,6 +189,15 @@ extern "C" {
#endif /* RTEMS_BSD_CONFIG_SERVICE_TELNETD */
/*
+ * Termios
+ */
+#if defined(RTEMS_BSD_CONFIG_TERMIOS_KQUEUE_AND_POLL)
+ #define RTEMS_BSD_CFGDECL_TERMIOS_KQUEUE_AND_POLL SYSINIT_REFERENCE(termioskqueuepoll)
+#else
+ #define RTEMS_BSD_CFGDECL_TERMIOS_KQUEUE_AND_POLL
+#endif /* RTEMS_BSD_CONFIG_TERMIOS_KQUEUE_AND_POLL */
+
+/*
* Configure the system.
*/
#if defined(RTEMS_BSD_CONFIG_INIT)
@@ -230,6 +239,8 @@ extern "C" {
RTEMS_BSD_CFGDECL_TELNETD;
RTEMS_BSD_CFGDECL_TELNETD_STACK_SIZE;
RTEMS_BSD_CFGDECL_FTPD;
+
+ RTEMS_BSD_CFGDECL_TERMIOS_KQUEUE_AND_POLL;
#endif /* RTEMS_BSD_CONFIG_INIT */
#ifdef __cplusplus
diff --git a/rtemsbsd/rtems/rtems-kernel-termioskqueuepoll.c b/rtemsbsd/rtems/rtems-kernel-termioskqueuepoll.c
new file mode 100644
index 0000000..7e8faeb
--- /dev/null
+++ b/rtemsbsd/rtems/rtems-kernel-termioskqueuepoll.c
@@ -0,0 +1,283 @@
+/**
+ * @file
+ *
+ * @ingroup rtems_bsd_rtems
+ *
+ * @brief TODO.
+ */
+
+/*
+ * Copyright (c) 2017 embedded brains GmbH.
+ * All rights reserved.
+ *
+ * embedded brains GmbH
+ * Dornierstr. 4
+ * 82178 Puchheim
+ * Germany
+ * <rtems at embedded-brains.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <machine/rtems-bsd-kernel-space.h>
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/event.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/pcpu.h>
+#include <sys/poll.h>
+#include <sys/selinfo.h>
+
+#include <rtems/termiostypes.h>
+#include <rtems/irq-extension.h>
+
+SYSINIT_REFERENCE(irqs);
+
+typedef struct {
+ rtems_termios_tty *tty;
+ struct selinfo sel;
+ rtems_interrupt_server_request request;
+} termios_selinfo;
+
+static bool
+termios_is_eol(const rtems_termios_tty *tty, char c)
+{
+
+ return (c == '\n' || c == tty->termios.c_cc[VEOF] ||
+ c == tty->termios.c_cc[VEOL] ||
+ c == tty->termios.c_cc[VEOL2]);
+}
+
+static bool
+termios_can_read(rtems_termios_tty *tty)
+{
+ rtems_termios_device_context *ctx;
+ rtems_interrupt_lock_context lock_context;
+ unsigned int i;
+ unsigned int size;
+ unsigned int raw_content_size;
+ bool can;
+
+ if (tty->handler.mode == TERMIOS_POLLED) {
+ return (true);
+ }
+
+ if (tty->cindex != tty->ccount) {
+ return (true);
+ }
+
+ ctx = tty->device_context;
+ rtems_termios_device_lock_acquire(ctx, &lock_context);
+ i = tty->rawInBuf.Head;
+ size = tty->rawInBuf.Size;
+ raw_content_size = (tty->rawInBuf.Tail - i) % size;
+
+ if ((tty->termios.c_lflag & ICANON) != 0) {
+ unsigned int todo = raw_content_size;
+
+ /*
+ * FIXME: What to do in case of a raw input buffer overflow?
+ * For now, indicated that we can read. However, this has
+ * problems in case an erase takes place.
+ */
+ can = raw_content_size == (size - 1);
+
+ while (todo > 0 && !can) {
+ char c;
+
+ i = (i + 1) % size;
+ c = tty->rawInBuf.theBuf[i];
+ can = termios_is_eol(tty, c);
+ --todo;
+ }
+ } else {
+ cc_t vmin = tty->termios.c_cc[VMIN];
+
+ if (vmin == 0) {
+ vmin = 1;
+ }
+
+ can = raw_content_size >= vmin;
+ }
+
+ if (!can) {
+ tty->tty_rcvwakeup = false;
+ }
+
+ rtems_termios_device_lock_release(ctx, &lock_context);
+ return (can);
+}
+
+static bool
+termios_can_write(const rtems_termios_tty *tty)
+{
+ rtems_termios_device_context *ctx;
+ rtems_interrupt_lock_context lock_context;
+ bool can;
+
+ if (tty->handler.mode == TERMIOS_POLLED) {
+ return (true);
+ }
+
+ ctx = tty->device_context;
+ rtems_termios_device_lock_acquire(ctx, &lock_context);
+ can = ((tty->rawOutBuf.Tail - tty->rawOutBuf.Head - 1) %
+ tty->rawOutBuf.Size) > 0;
+ rtems_termios_device_lock_release(ctx, &lock_context);
+ return (can);
+}
+
+static void
+termios_receive_wakeup(void *arg)
+{
+ termios_selinfo *ts;
+ rtems_termios_tty *tty;
+ rtems_status_code sc;
+
+ ts = arg;
+ tty = ts->tty;
+
+ sc = rtems_semaphore_obtain(tty->isem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ BSD_ASSERT(sc == RTEMS_SUCCESSFUL);
+
+ selwakeup(&ts->sel);
+
+ sc = rtems_semaphore_release(tty->isem);
+ BSD_ASSERT(sc == RTEMS_SUCCESSFUL);
+}
+
+static void
+termios_transmit_wakeup(void *arg)
+{
+ termios_selinfo *ts;
+ rtems_termios_tty *tty;
+ rtems_status_code sc;
+
+ ts = arg;
+ tty = ts->tty;
+
+ sc = rtems_semaphore_obtain(tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ BSD_ASSERT(sc == RTEMS_SUCCESSFUL);
+
+ selwakeup(&ts->sel);
+
+ sc = rtems_semaphore_release(tty->osem);
+ BSD_ASSERT(sc == RTEMS_SUCCESSFUL);
+}
+
+static void
+termios_wakeup(struct termios *term, void *arg)
+{
+ termios_selinfo *ts = arg;
+
+ rtems_interrupt_server_request_submit(RTEMS_ID_NONE, &ts->request);
+}
+
+static struct selinfo *
+termios_get_selinfo(rtems_termios_tty *tty, struct ttywakeup *wk,
+ rtems_interrupt_handler handler)
+{
+ termios_selinfo *ts = wk->sw_arg;
+
+ if (ts == NULL) {
+ BSD_ASSERT(wk->sw_pfn == NULL);
+ ts = malloc(sizeof(*ts), M_TEMP, M_WAITOK | M_ZERO);
+ ts->tty = tty;
+ rtems_interrupt_server_request_initialize(&ts->request,
+ handler, ts);
+ wk->sw_arg = ts;
+ wk->sw_pfn = termios_wakeup;
+ } else {
+ BSD_ASSERT(wk->sw_pfn == termios_wakeup);
+ }
+
+ return (&ts->sel);
+}
+
+int
+rtems_termios_kqfilter(rtems_libio_t *iop, struct knote *kn)
+{
+
+ return (EINVAL);
+}
+
+int
+rtems_termios_poll(rtems_libio_t *iop, int events)
+{
+ struct thread *td = rtems_bsd_get_curthread_or_wait_forever();
+ struct selinfo *sel;
+ rtems_termios_tty *tty;
+ rtems_status_code sc;
+ int revents;
+
+ revents = 0;
+ tty = iop->data1;
+
+ if ((events & (POLLIN | POLLRDNORM)) != 0) {
+ sel = termios_get_selinfo(tty, &tty->tty_rcv,
+ termios_receive_wakeup);
+
+ sc = rtems_semaphore_obtain(tty->isem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ BSD_ASSERT(sc == RTEMS_SUCCESSFUL);
+
+ if (termios_can_read(tty)) {
+ revents |= events & (POLLIN | POLLRDNORM);
+ } else {
+ selrecord(td, sel);
+ }
+
+ sc = rtems_semaphore_release(tty->isem);
+ BSD_ASSERT(sc == RTEMS_SUCCESSFUL);
+ }
+
+ if ((events & (POLLOUT | POLLWRNORM)) != 0) {
+ sel = termios_get_selinfo(tty, &tty->tty_snd,
+ termios_transmit_wakeup);
+
+ sc = rtems_semaphore_obtain(tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ BSD_ASSERT(sc == RTEMS_SUCCESSFUL);
+
+ if (termios_can_write(tty)) {
+ revents |= events & (POLLOUT | POLLWRNORM);
+ } else {
+ selrecord(td, sel);
+ }
+
+ sc = rtems_semaphore_release(tty->osem);
+ BSD_ASSERT(sc == RTEMS_SUCCESSFUL);
+ }
+
+ return (revents);
+}
+
+static void
+termioskqueuepoll_sysinit(void)
+{
+
+ /* Do nothing */
+}
+
+SYSINIT(termioskqueuepoll, SI_SUB_TUNABLES, SI_ORDER_ANY,
+ termioskqueuepoll_sysinit, NULL);
diff --git a/testsuite/netshell01/test_main.c b/testsuite/netshell01/test_main.c
index 36eeea7..1b68971 100644
--- a/testsuite/netshell01/test_main.c
+++ b/testsuite/netshell01/test_main.c
@@ -29,17 +29,132 @@
* that the target is alive after initializing the TCP/IP stack.
*/
-#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <errno.h>
#include <rtems/shell.h>
#define TEST_NAME "LIBBSD NETSHELL 1"
static void
+change_serial_settings(int fd, const struct termios *current, bool icanon)
+{
+ struct termios term = *current;
+
+ term.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR |
+ ICRNL | IXON);
+ term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOCTL |
+ ECHOKE | ICANON | ISIG | IEXTEN);
+ term.c_cflag &= ~(CSIZE | PARENB);
+ term.c_cflag |= CS8;
+ term.c_oflag &= ~(OPOST | ONLRET | ONLCR | OCRNL | ONLRET | TABDLY |
+ OLCUC);
+
+ term.c_cc[VMIN] = 0;
+ term.c_cc[VTIME] = 10;
+
+ if (icanon) {
+ term.c_iflag |= ICRNL;
+ term.c_lflag |= ICANON;
+ }
+
+ tcsetattr(fd, TCSANOW, &term);
+}
+
+static void
+do_read_select(int fd)
+{
+ int nfds = fd + 1;
+ struct fd_set read_set;
+ struct timeval timeout = {
+ .tv_sec = 10,
+ .tv_usec = 0
+ };
+ int rv;
+
+ FD_ZERO(&read_set);
+ FD_SET(fd, &read_set);
+
+ rv = select(nfds, &read_set, NULL, NULL, &timeout);
+ if (rv == 0) {
+ printf("timeout\n");
+ } else if (rv > 0) {
+ if (FD_ISSET(fd, &read_set)) {
+ char buf[512];
+ ssize_t n = read(fd, buf, sizeof(buf));
+ printf("read returned %zi\n", n);
+ }
+ } else {
+ perror("select failed");
+ }
+}
+
+static void
+do_write_select(int fd)
+{
+ int nfds = fd + 1;
+ struct fd_set write_set;
+ struct timeval to = {
+ .tv_sec = 0,
+ .tv_usec = 1
+ };
+ struct timeval *timeout = &to;
+ char buf[512];
+ int rv;
+ size_t i;
+
+ memset(buf, 'a', sizeof(buf));
+
+ for (i = 0; i < sizeof(buf); i += 24) {
+ buf[i] = '\r';
+ buf[i + 1] = '\n';
+ }
+
+ for (i = 0; i < 10; ++i) {
+ write(fd, buf, sizeof(buf));
+
+ FD_ZERO(&write_set);
+ FD_SET(fd, &write_set);
+ rv = select(nfds, NULL, &write_set, NULL, timeout);
+ if (rv == 0) {
+ printf("timeout\n");
+ } else {
+ printf("write set: %i\n", FD_ISSET(fd, &write_set));
+ }
+
+ timeout = NULL;
+ }
+}
+
+static int
+termiosselect_command(int argc, char *argv[])
+{
+ bool icanon = argc > 1 && strcmp(argv[1], "icanon") == 0;
+ int fd = STDIN_FILENO;
+ struct termios term;
+ int rv = tcgetattr(fd, &term);
+ assert(rv == 0);
+
+ change_serial_settings(fd, &term, icanon);
+ do_read_select(fd);
+ do_write_select(STDOUT_FILENO);
+ tcsetattr(fd, TCSANOW, &term);
+ return (0);
+}
+
+rtems_shell_cmd_t rtems_shell_ARP_Command = {
+ .name = "termiosselect",
+ .usage = "termiosselect [icanon]",
+ .topic = "net",
+ .command = termiosselect_command
+};
+
+static void
test_main(void)
{
rtems_shell_env_t env;
@@ -50,4 +165,6 @@ test_main(void)
exit(0);
}
+#define RTEMS_BSD_CONFIG_TERMIOS_KQUEUE_AND_POLL
+
#include <rtems/bsd/test/default-init.h>
More information about the vc
mailing list