diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2017-02-21 15:26:31 +0100 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2017-02-28 10:54:50 +0100 |
commit | 045ff6e11cf21454c6216a1099b44a6fe692586f (patch) | |
tree | c23d93b2e49eb4805a4efdb0c9032a2f06f5916e /rtemsbsd/rtems/rtems-kernel-termioskqueuepoll.c | |
parent | netshell01: Add license, fix format (diff) | |
download | rtems-libbsd-045ff6e11cf21454c6216a1099b44a6fe692586f.tar.bz2 |
Add poll() and select() support for Termios
Diffstat (limited to 'rtemsbsd/rtems/rtems-kernel-termioskqueuepoll.c')
-rw-r--r-- | rtemsbsd/rtems/rtems-kernel-termioskqueuepoll.c | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/rtemsbsd/rtems/rtems-kernel-termioskqueuepoll.c b/rtemsbsd/rtems/rtems-kernel-termioskqueuepoll.c new file mode 100644 index 00000000..7e8faeb4 --- /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@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); |