diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2012-04-03 15:14:50 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2012-04-03 17:17:34 +0200 |
commit | 92a35861d4795075dcff3ebab177812c7422753f (patch) | |
tree | f613d7e7795ec644ba279ddedbb97d7f0cebee96 | |
parent | Merge branch 'upstream' (diff) | |
download | rtems-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.h | 25 | ||||
-rw-r--r-- | cpukit/libcsupport/src/termios.c | 71 | ||||
-rw-r--r-- | cpukit/libnetworking/rtems/rtems_select.c | 32 |
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++; |