summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2012-04-03 15:14:50 +0200
committerSebastian Huber <sebastian.huber@embedded-brains.de>2012-04-03 17:17:34 +0200
commit92a35861d4795075dcff3ebab177812c7422753f (patch)
treef613d7e7795ec644ba279ddedbb97d7f0cebee96
parentMerge branch 'upstream' (diff)
downloadrtems-92a35861d4795075dcff3ebab177812c7422753f.tar.bz2
Filesystem: IO control based select() support
This is a hack. The Termios write facility has a severe issue with the lacking support of non-blocking writes.
-rw-r--r--cpukit/libcsupport/include/sys/ioccom.h25
-rw-r--r--cpukit/libcsupport/src/termios.c71
-rw-r--r--cpukit/libnetworking/rtems/rtems_select.c32
3 files changed, 124 insertions, 4 deletions
diff --git a/cpukit/libcsupport/include/sys/ioccom.h b/cpukit/libcsupport/include/sys/ioccom.h
index 09b4728e49..6d97c679f4 100644
--- a/cpukit/libcsupport/include/sys/ioccom.h
+++ b/cpukit/libcsupport/include/sys/ioccom.h
@@ -36,7 +36,7 @@
#ifndef _SYS_IOCCOM_H_
#define _SYS_IOCCOM_H_
-#include <sys/types.h>
+#include <rtems.h>
/*
* Ioctl's have the command encoded in the lower word, and the size of
@@ -76,6 +76,29 @@
#define RTEMS_IO_RCVWAKEUP 4
#define RTEMS_IO_SNDWAKEUP 5
+typedef enum {
+ RTEMS_IOCTL_SELECT_OTHER,
+ RTEMS_IOCTL_SELECT_READ,
+ RTEMS_IOCTL_SELECT_WRITE
+} rtems_ioctl_select_kind;
+
+/**
+ * @brief IO control request for select() support.
+ *
+ * The driver shall return
+ * - 1, when the request can be fullfilled immediately,
+ * - 0, when the request task must wait, and
+ * - -1, in case of an error.
+ */
+typedef struct {
+ rtems_ioctl_select_kind kind;
+ rtems_id request_task_id;
+} rtems_ioctl_select_request;
+
+#define RTEMS_IOCTL_SELECT _IOW('R', 0, rtems_ioctl_select_request)
+
+#define RTEMS_IOCTL_SELECT_EVENT RTEMS_EVENT_24
+
/* copied from libnetworking/sys/filio.h and commented out there */
/* Generic file-descriptor ioctl's. */
#define FIOCLEX _IO('f', 1) /* set close on exec on fd */
diff --git a/cpukit/libcsupport/src/termios.c b/cpukit/libcsupport/src/termios.c
index 17fa5ef91e..823a2cbe60 100644
--- a/cpukit/libcsupport/src/termios.c
+++ b/cpukit/libcsupport/src/termios.c
@@ -509,6 +509,73 @@ termios_set_flowctrl(struct rtems_termios_tty *tty)
}
}
+static bool
+rtems_termios_can_read (const struct rtems_termios_tty *tty)
+{
+ if (tty->cindex == tty->ccount) {
+ if (tty->device.outputUsesInterrupts == TERMIOS_IRQ_DRIVEN) {
+ return tty->rawInBuf.Head != tty->rawInBuf.Tail;
+ } else {
+ return true;
+ }
+ } else {
+ return tty->cindex < tty->ccount;
+ }
+}
+
+static bool
+rtems_termios_can_write (const struct rtems_termios_tty *tty)
+{
+ /*
+ * Termios has no non-blocking writes. In case the raw output buffer is
+ * full, we wait for the interrupt or poll.
+ */
+ return true;
+}
+
+static void
+rtems_termios_select_wakeup (struct termios *tty, void *arg)
+{
+ rtems_id task_id = (rtems_id) arg;
+ rtems_status_code sc = rtems_event_send (task_id, RTEMS_IOCTL_SELECT_EVENT);
+ if (sc != RTEMS_SUCCESSFUL)
+ rtems_fatal_error_occurred (sc);
+}
+
+static int
+rtems_termios_select (struct rtems_termios_tty *tty,
+ const rtems_ioctl_select_request *request)
+{
+ int rv = 0;
+
+ rtems_interrupt_level level;
+ rtems_interrupt_disable(level);
+ switch (request->kind) {
+ case RTEMS_IOCTL_SELECT_READ:
+ if (rtems_termios_can_read (tty)) {
+ rv = 1;
+ } else {
+ tty->tty_rcvwakeup = 0;
+ tty->tty_rcv.sw_pfn = rtems_termios_select_wakeup;
+ tty->tty_rcv.sw_arg = (void *) request->request_task_id;
+ }
+ break;
+ case RTEMS_IOCTL_SELECT_WRITE:
+ if (rtems_termios_can_write (tty)) {
+ rv = 1;
+ } else {
+ tty->tty_snd.sw_pfn = rtems_termios_select_wakeup;
+ tty->tty_snd.sw_arg = (void *) request->request_task_id;
+ }
+ break;
+ default:
+ break;
+ }
+ rtems_interrupt_enable(level);
+
+ return rv;
+}
+
rtems_status_code
rtems_termios_ioctl (void *arg)
{
@@ -532,6 +599,10 @@ rtems_termios_ioctl (void *arg)
}
break;
+ case RTEMS_IOCTL_SELECT:
+ args->ioctl_return = rtems_termios_select (tty, args->buffer);
+ break;
+
case RTEMS_IO_GET_ATTRIBUTES:
*(struct termios *)args->buffer = tty->termios;
break;
diff --git a/cpukit/libnetworking/rtems/rtems_select.c b/cpukit/libnetworking/rtems/rtems_select.c
index 927c07daa6..b8915b64df 100644
--- a/cpukit/libnetworking/rtems/rtems_select.c
+++ b/cpukit/libnetworking/rtems/rtems_select.c
@@ -30,6 +30,11 @@
#include <net/if.h>
#include <net/route.h>
+RTEMS_STATIC_ASSERT(RTEMS_IOCTL_SELECT_OTHER == 0, other);
+RTEMS_STATIC_ASSERT(RTEMS_IOCTL_SELECT_READ == FREAD, fread);
+RTEMS_STATIC_ASSERT(RTEMS_IOCTL_SELECT_WRITE == FWRITE, fwrite);
+RTEMS_STATIC_ASSERT(RTEMS_IOCTL_SELECT_EVENT == SBWAIT_EVENT, sbwait_event);
+
/*
*********************************************************************
* RTEMS implementation of select() system call *
@@ -88,6 +93,11 @@ selscan (rtems_id tid, fd_mask **ibits, fd_mask **obits, int nfd, int *retval)
fd_mask bits, bit;
int n = 0;
static int flag[3] = { FREAD, FWRITE, 0 };
+ int update_obits;
+ int rv;
+ rtems_ioctl_select_request select_request;
+
+ select_request.request_task_id = tid;
for (msk = 0; msk < 3; msk++) {
if (ibits[msk] == NULL)
@@ -98,10 +108,26 @@ selscan (rtems_id tid, fd_mask **ibits, fd_mask **obits, int nfd, int *retval)
if ((bits & bit) == 0)
continue;
bits &= ~bit;
+ update_obits = 0;
so = rtems_bsdnet_fdToSocket (fd);
- if (so == NULL)
- return (EBADF);
- if (socket_select (so, flag[msk], tid)) {
+ if (so != NULL) {
+ if (socket_select (so, flag[msk], tid)) {
+ update_obits = 1;
+ }
+ } else {
+ select_request.kind = flag[msk];
+
+ rtems_bsdnet_semaphore_release();
+ rv = ioctl (fd, RTEMS_IOCTL_SELECT, &select_request);
+ rtems_bsdnet_semaphore_obtain();
+ if (rv == 1) {
+ update_obits = 1;
+ } else if (rv != 0) {
+ return (EBADF);
+ }
+ }
+
+ if (update_obits) {
obits[msk][fd/NFDBITS] |=
(1 << (fd % NFDBITS));
n++;