RTAI/addons/rtdm/select.c
Alec Ari dc658435df Initial checkout (vulcano)
Signed-off-by: Alec Ari <neotheuser@ymail.com>
2014-08-17 10:08:41 -05:00

466 lines
12 KiB
C

/*!\file select.c
* \brief file descriptors events multiplexing.
* \author Gilles Chanteperdrix
*
* Copyright (C) 2008 Efixo <gilles.chanteperdrix@laposte.net>
*
* with adaptions for RTAI by Paolo Mantegazza <mantegazza@aero.polimi.it>
*
* RTAI is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* RTAI is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RTAI; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* \ingroup select
*/
/*!
* \ingroup nucleus
* \defgroup select File descriptors events multiplexing services.
*
* File descriptors events multiplexing services.
*
* This module implements the services needed for implementing the posix
* "select" service, or any other events multiplexing services.
*
* Following the implementation of the posix select service, this module defines
* three types of events:
* - \a XNSELECT_READ meaning that a file descriptor is ready for reading;
* - \a XNSELECT_WRITE meaning that a file descriptor is ready for writing;
* - \a XNSELECT_EXCEPT meaning that a file descriptor received an exceptional
* event.
*
* It works by defining two structures:
* - a @a struct @a xnselect structure, which should be added to every file
* descriptor for every event type (read, write, or except);
* - a @a struct @a xnselector structure, the selection structure, passed by
* the thread calling the xnselect service, where this service does all its
* housekeeping.
*@{*/
#include "select.h"
#ifdef CONFIG_RTAI_RTDM_SELECT
#include <linux/types.h>
#include <linux/bitops.h> /* For hweight_long */
static xnqueue_t xnselectors;
static int xnselect_apc;
#define link2binding(baddr, memb) \
container_of(baddr, struct xnselect_binding, memb)
/**
* Initialize a @a struct @a xnselect structure.
*
* This service must be called to initialize a @a struct @a xnselect structure
* before it is bound to a selector by the means of xnselect_bind().
*
* @param select_block pointer to the xnselect structure to be initialized
*/
void xnselect_init(struct xnselect *select_block)
{
initq(&select_block->bindings);
}
EXPORT_SYMBOL_GPL(xnselect_init);
static inline int xnselect_wakeup(struct xnselector *selector)
{
return xnsynch_flush(&selector->synchbase, 0) == XNSYNCH_RESCHED;
}
/**
* Bind a file descriptor (represented by its @a xnselect structure) to a
* selector block.
*
* @param select_block pointer to the @a struct @a xnselect to be bound;
*
* @param binding pointer to a newly allocated (using xnmalloc) @a struct
* @a xnselect_binding;
*
* @param selector pointer to the selector structure;
*
* @param type type of events (@a XNSELECT_READ, @a XNSELECT_WRITE, or @a
* XNSELECT_EXCEPT);
*
* @param index index of the file descriptor (represented by @a select_block) in the bit fields used by the @a selector structure;
*
* @param state current state of the file descriptor>.
*
* @a select_block must have been initialized with xnselect_init(),
* the @a xnselector structure must have been initialized with
* xnselector_init(), @a binding may be uninitialized.
*
* This service must be called with nklock locked, irqs off. For this reason,
* the @a binding parameter must have been allocated by the caller outside the
* locking section.
*
* @retval -EINVAL if @a type or @a index is invalid;
* @retval 0 otherwise.
*/
int xnselect_bind(struct xnselect *select_block,
struct xnselect_binding *binding,
struct xnselector *selector,
unsigned type,
unsigned index,
unsigned state)
{
if (type >= XNSELECT_MAX_TYPES || index > __FD_SETSIZE)
return -EINVAL;
binding->selector = selector;
binding->fd = select_block;
binding->type = type;
binding->bit_index = index;
inith(&binding->link);
inith(&binding->slink);
appendq(&selector->bindings, &binding->slink);
appendq(&select_block->bindings, &binding->link);
__FD_SET__(index, &selector->fds[type].expected);
if (state) {
__FD_SET__(index, &selector->fds[type].pending);
if (xnselect_wakeup(selector))
xnpod_schedule();
} else
__FD_CLR__(index, &selector->fds[type].pending);
return 0;
}
EXPORT_SYMBOL_GPL(xnselect_bind);
/* Must be called with nklock locked irqs off */
int __xnselect_signal(struct xnselect *select_block, unsigned state)
{
xnholder_t *holder;
int resched;
for(resched = 0, holder = getheadq(&select_block->bindings);
holder; holder = nextq(&select_block->bindings, holder)) {
struct xnselect_binding *binding;
struct xnselector *selector;
binding = link2binding(holder, link);
selector = binding->selector;
if (state) {
if (!__FD_ISSET__(binding->bit_index,
&selector->fds[binding->type].pending)) {
__FD_SET__(binding->bit_index,
&selector->fds[binding->type].pending);
if (xnselect_wakeup(selector))
resched = 1;
}
} else
__FD_CLR__(binding->bit_index,
&selector->fds[binding->type].pending);
}
return resched;
}
EXPORT_SYMBOL_GPL(__xnselect_signal);
/**
* Destroy the @a xnselect structure associated with a file descriptor.
*
* Any binding with a @a xnselector block is destroyed.
*
* @param select_block pointer to the @a xnselect structure associated with a file descriptor
*/
void xnselect_destroy(struct xnselect *select_block)
{
xnholder_t *holder;
int resched = 0;
spl_t s;
xnlock_get_irqsave(&nklock, s);
while ((holder = getq(&select_block->bindings))) {
struct xnselect_binding *binding;
struct xnselector *selector;
binding = link2binding(holder, link);
selector = binding->selector;
__FD_CLR__(binding->bit_index,
&selector->fds[binding->type].expected);
if (!__FD_ISSET__(binding->bit_index,
&selector->fds[binding->type].pending)) {
__FD_SET__(binding->bit_index,
&selector->fds[binding->type].pending);
if (xnselect_wakeup(selector))
resched = 1;
}
removeq(&selector->bindings, &binding->slink);
xnlock_put_irqrestore(&nklock, s);
xnfree(binding);
xnlock_get_irqsave(&nklock, s);
}
if (resched)
xnpod_schedule();
xnlock_put_irqrestore(&nklock, s);
}
EXPORT_SYMBOL_GPL(xnselect_destroy);
static unsigned
fd_set_andnot(fd_set *result, fd_set *first, fd_set *second, unsigned n)
{
unsigned i, not_empty = 0;
for (i = 0; i < __FDELT__(n); i++)
if((result->fds_bits[i] =
first->fds_bits[i] & ~(second->fds_bits[i])))
not_empty = 1;
if (i < __FDSET_LONGS__
&& (result->fds_bits[i] =
first->fds_bits[i] & ~(second->fds_bits[i]) & (__FDMASK__(n) - 1)))
not_empty = 1;
return not_empty;
}
static unsigned
fd_set_and(fd_set *result, fd_set *first, fd_set *second, unsigned n)
{
unsigned i, not_empty = 0;
for (i = 0; i < __FDELT__(n); i++)
if((result->fds_bits[i] =
first->fds_bits[i] & second->fds_bits[i]))
not_empty = 1;
if (i < __FDSET_LONGS__
&& (result->fds_bits[i] =
first->fds_bits[i] & second->fds_bits[i] & (__FDMASK__(n) - 1)))
not_empty = 1;
return not_empty;
}
static void fd_set_zeropad(fd_set *set, unsigned n)
{
unsigned i;
i = __FDELT__(n);
if (i < __FDSET_LONGS__)
set->fds_bits[i] &= (__FDMASK__(n) - 1);
for(i++; i < __FDSET_LONGS__; i++)
set->fds_bits[i] = 0;
}
static unsigned fd_set_popcount(fd_set *set, unsigned n)
{
unsigned count = 0, i;
for (i = 0; i < __FDELT__(n); i++)
if (set->fds_bits[i])
count += hweight_long(set->fds_bits[i]);
if (i < __FDSET_LONGS__ && (set->fds_bits[i] & (__FDMASK__(n) - 1)))
count += hweight_long(set->fds_bits[i] & (__FDMASK__(n) - 1));
return count;
}
/**
* Initialize a selector structure.
*
* @param selector The selector structure to be initialized.
*
* @retval 0
*/
int xnselector_init(struct xnselector *selector)
{
unsigned i;
xnsynch_init(&selector->synchbase, XNSYNCH_FIFO, NULL);
for (i = 0; i < XNSELECT_MAX_TYPES; i++) {
__FD_ZERO__(&selector->fds[i].expected);
__FD_ZERO__(&selector->fds[i].pending);
}
initq(&selector->bindings);
return 0;
}
EXPORT_SYMBOL_GPL(xnselector_init);
/**
* Check the state of a number of file descriptors, wait for a state change if
* no descriptor is ready.
*
* @param selector structure to check for pending events
* @param out_fds The set of descriptors with pending events if a strictly positive number is returned, or the set of descriptors not yet bound if -ECHRNG is returned;
* @param in_fds the set of descriptors which events should be checked
* @param nfds the highest-numbered descriptor in any of the @a in_fds sets, plus 1;
* @param timeout the timeout, whose meaning depends on @a timeout_mode, note
* that xnselect() pass @a timeout and @a timeout_mode unchanged to
* xnsynch_sleep_on, so passing a relative value different from XN_INFINITE as a
* timeout with @a timeout_mode set to XN_RELATIVE, will cause a longer sleep
* than expected if the sleep is interrupted.
* @param timeout_mode the mode of @a timeout.
*
* @retval -EINVAL if @a nfds is negative;
* @retval -ECHRNG if some of the descriptors passed in @a in_fds have not yet
* been registered with xnselect_bind(), @a out_fds contains the set of such
* descriptors;
* @retval -EINTR if @a xnselect was interrupted while waiting;
* @retval 0 in case of timeout.
* @retval the number of file descriptors having received an event.
*/
int xnselect(struct xnselector *selector,
fd_set *out_fds[XNSELECT_MAX_TYPES],
fd_set *in_fds[XNSELECT_MAX_TYPES],
int nfds,
xnticks_t timeout, xntmode_t timeout_mode)
{
unsigned i, not_empty = 0;
xnthread_t *thread;
spl_t s;
if ((unsigned) nfds > __FD_SETSIZE)
return -EINVAL;
thread = xnpod_current_thread();
for (i = 0; i < XNSELECT_MAX_TYPES; i++)
if (out_fds[i])
fd_set_zeropad(out_fds[i], nfds);
xnlock_get_irqsave(&nklock, s);
for (i = 0; i < XNSELECT_MAX_TYPES; i++)
if (out_fds[i]
&& fd_set_andnot(out_fds[i], in_fds[i],
&selector->fds[i].expected, nfds))
not_empty = 1;
xnlock_put_irqrestore(&nklock, s);
if (not_empty)
return -ECHRNG;
xnlock_get_irqsave(&nklock, s);
for (i = 0; i < XNSELECT_MAX_TYPES; i++)
if (out_fds[i]
&& fd_set_and(out_fds[i], in_fds[i],
&selector->fds[i].pending, nfds))
not_empty = 1;
while (!not_empty) {
xnsynch_sleep_on(&selector->synchbase, timeout, timeout_mode);
for (i = 0; i < XNSELECT_MAX_TYPES; i++)
if (out_fds[i]
&& fd_set_and(out_fds[i], in_fds[i],
&selector->fds[i].pending, nfds))
not_empty = 1;
if (xnthread_test_info(thread, XNBREAK | XNTIMEO))
break;
}
xnlock_put_irqrestore(&nklock, s);
if (not_empty) {
unsigned count;
for (count = 0, i = 0; i < XNSELECT_MAX_TYPES; i++)
if (out_fds[i])
count += fd_set_popcount(out_fds[i], nfds);
return count;
}
if (xnthread_test_info(thread, XNBREAK))
return -EINTR;
return 0; /* Timeout */
}
EXPORT_SYMBOL_GPL(xnselect);
/**
* Destroy a selector block.
*
* All bindings with file descriptor are destroyed.
*
* @param selector the selector block to be destroyed
*/
void xnselector_destroy(struct xnselector *selector)
{
spl_t s;
inith(&selector->destroy_link);
xnlock_get_irqsave(&nklock, s);
appendq(&xnselectors, &selector->destroy_link);
xnlock_put_irqrestore(&nklock, s);
rthal_apc_schedule(xnselect_apc);
}
EXPORT_SYMBOL_GPL(xnselector_destroy);
static void xnselector_destroy_loop(void *cookie)
{
struct xnselector *selector;
xnholder_t *holder;
int resched;
spl_t s;
xnlock_get_irqsave(&nklock, s);
while ((holder = getq(&xnselectors))) {
selector = container_of(holder, struct xnselector, destroy_link);
while ((holder = getq(&selector->bindings))) {
struct xnselect_binding *binding;
struct xnselect *fd;
binding = link2binding(holder, slink);
fd = binding->fd;
removeq(&fd->bindings, &binding->link);
xnlock_put_irqrestore(&nklock, s);
xnfree(binding);
xnlock_get_irqsave(&nklock, s);
}
resched =
xnsynch_destroy(&selector->synchbase) == XNSYNCH_RESCHED;
xnlock_put_irqrestore(&nklock, s);
xnfree(selector);
if (resched)
xnpod_schedule();
xnlock_get_irqsave(&nklock, s);
}
xnlock_put_irqrestore(&nklock, s);
}
int xnselect_mount(void)
{
initq(&xnselectors);
xnselect_apc = rthal_apc_alloc("xnselectors_destroy",
xnselector_destroy_loop, NULL);
if (xnselect_apc < 0)
return xnselect_apc;
return 0;
}
int xnselect_umount(void)
{
rthal_apc_free(xnselect_apc);
return 0;
}
#endif
/*@}*/