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

1527 lines
37 KiB
C

/**
* @file
* Real-Time Driver Model for RTAI, device operation multiplexing
*
* @note Copyright (C) 2005 Jan Kiszka <jan.kiszka@web.de>
* @note Copyright (C) 2005 Joerg Langenberg <joerg.langenberg@gmx.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 driverapi
* @defgroup interdrv Inter-Driver API
* @{
*/
#include <linux/delay.h>
#include "rtdm/internal.h"
#define CLOSURE_RETRY_PERIOD_MS 100
#define FD_BITMAP_SIZE ((RTDM_FD_MAX + BITS_PER_LONG-1) / BITS_PER_LONG)
struct rtdm_fildes fildes_table[RTDM_FD_MAX] =
{ [0 ... RTDM_FD_MAX-1] = { NULL } };
static unsigned long used_fildes[FD_BITMAP_SIZE];
int open_fildes; /* number of used descriptors */
static DECLARE_WORK_FUNC(close_callback);
static DECLARE_DELAYED_WORK_NODATA(close_work, close_callback);
static LIST_HEAD(cleanup_queue);
DEFINE_XNLOCK(rt_fildes_lock);
/**
* @brief Retrieve and lock a device context
*
* @param[in] fd File descriptor
*
* @return Pointer to associated device context, or NULL on error
*
* @note The device context has to be unlocked using rtdm_context_put() when
* it is no longer referenced.
*
* Environments:
*
* This service can be called from:
*
* - Kernel module initialization/cleanup code
* - Interrupt service routine
* - Kernel-based task
* - User-space task (RT, non-RT)
*
* Rescheduling: never.
*/
struct rtdm_dev_context *rtdm_context_get(int fd)
{
struct rtdm_dev_context *context;
spl_t s;
if ((unsigned int)fd >= RTDM_FD_MAX)
return NULL;
xnlock_get_irqsave(&rt_fildes_lock, s);
context = fildes_table[fd].context;
if (unlikely(!context)) {
xnlock_put_irqrestore(&rt_fildes_lock, s);
return NULL;
}
atomic_inc(&context->close_lock_count);
xnlock_put_irqrestore(&rt_fildes_lock, s);
return context;
}
EXPORT_SYMBOL(rtdm_context_get);
static int create_instance(struct rtdm_device *device,
struct rtdm_dev_context **context_ptr,
struct rtdm_fildes **fildes_ptr,
rtdm_user_info_t *user_info, int nrt_mem)
{
struct rtdm_dev_context *context;
int fd;
spl_t s;
/*
* Reset to NULL so that we can always use cleanup_files/instance to
* revert also partially successful allocations.
*/
*context_ptr = NULL;
*fildes_ptr = NULL;
/* Reserve a file descriptor */
xnlock_get_irqsave(&rt_fildes_lock, s);
if (unlikely(open_fildes >= RTDM_FD_MAX)) {
xnlock_put_irqrestore(&rt_fildes_lock, s);
return -ENFILE;
}
fd = find_first_zero_bit(used_fildes, RTDM_FD_MAX);
__set_bit(fd, used_fildes);
open_fildes++;
xnlock_put_irqrestore(&rt_fildes_lock, s);
*fildes_ptr = &fildes_table[fd];
context = device->reserved.exclusive_context;
if (context) {
xnlock_get_irqsave(&rt_dev_lock, s);
if (unlikely(context->device != NULL)) {
xnlock_put_irqrestore(&rt_dev_lock, s);
return -EBUSY;
}
context->device = device;
xnlock_put_irqrestore(&rt_dev_lock, s);
} else {
if (nrt_mem)
context = kmalloc(sizeof(struct rtdm_dev_context) +
device->context_size, GFP_KERNEL);
else
context = xnmalloc(sizeof(struct rtdm_dev_context) +
device->context_size);
if (unlikely(!context))
return -ENOMEM;
context->device = device;
}
*context_ptr = context;
context->fd = fd;
context->ops = &device->ops;
atomic_set(&context->close_lock_count, 0);
context->reserved.owner = NULL;
return 0;
}
static void __cleanup_fildes(struct rtdm_fildes *fildes)
{
__clear_bit((fildes - fildes_table), used_fildes);
fildes->context = NULL;
open_fildes--;
}
static void cleanup_fildes(struct rtdm_fildes *fildes)
{
spl_t s;
if (!fildes)
return;
xnlock_get_irqsave(&rt_fildes_lock, s);
__cleanup_fildes(fildes);
xnlock_put_irqrestore(&rt_fildes_lock, s);
}
static void cleanup_instance(struct rtdm_device *device,
struct rtdm_dev_context *context,
int nrt_mem)
{
if (context) {
if (device->reserved.exclusive_context)
context->device = NULL;
else {
if (nrt_mem)
kfree(context);
else
xnfree(context);
}
}
rtdm_dereference_device(device);
}
static DECLARE_WORK_FUNC(close_callback)
{
struct rtdm_dev_context *context;
LIST_HEAD(deferred_list);
int reschedule = 0;
int err;
spl_t s;
xnlock_get_irqsave(&rt_fildes_lock, s);
while (!list_empty(&cleanup_queue)) {
context = list_first_entry(&cleanup_queue,
struct rtdm_dev_context,
reserved.cleanup);
list_del(&context->reserved.cleanup);
atomic_inc(&context->close_lock_count);
xnlock_put_irqrestore(&rt_fildes_lock, s);
err = context->ops->close_nrt(context, NULL);
if (err == -EAGAIN ||
atomic_read(&context->close_lock_count) > 1) {
atomic_dec(&context->close_lock_count);
list_add_tail(&context->reserved.cleanup,
&deferred_list);
if (err == -EAGAIN)
reschedule = 1;
} else {
trace_mark(xn_rtdm, fd_closed, "fd %d", context->fd);
cleanup_instance(context->device, context,
test_bit(RTDM_CREATED_IN_NRT,
&context->context_flags));
}
xnlock_get_irqsave(&rt_fildes_lock, s);
}
list_splice(&deferred_list, &cleanup_queue);
xnlock_put_irqrestore(&rt_fildes_lock, s);
if (reschedule)
schedule_delayed_work(&close_work,
(HZ * CLOSURE_RETRY_PERIOD_MS) / 1000);
}
void rtdm_apc_handler(void *cookie)
{
schedule_delayed_work(&close_work, 0);
}
int __rt_dev_open(rtdm_user_info_t *user_info, const char *path, int oflag)
{
struct rtdm_device *device;
struct rtdm_fildes *fildes;
struct rtdm_dev_context *context;
int ret;
int nrt_mode = !rtdm_in_rt_context();
device = get_named_device(path);
trace_mark(xn_rtdm, open, "user_info %p path %s oflag %d device %p",
user_info, path, oflag, device);
ret = -ENODEV;
if (!device)
goto err_out;
ret = create_instance(device, &context, &fildes, user_info, nrt_mode);
if (ret != 0)
goto cleanup_out;
if (nrt_mode) {
context->context_flags = (1 << RTDM_CREATED_IN_NRT);
ret = device->open_nrt(context, user_info, oflag);
} else {
context->context_flags = 0;
ret = device->open_rt(context, user_info, oflag);
}
RTAI_ASSERT(RTDM, !rthal_local_irq_disabled(),
rthal_local_irq_enable(););
if (unlikely(ret < 0))
goto cleanup_out;
fildes->context = context;
trace_mark(xn_rtdm, fd_created,
"device %p fd %d", device, context->fd);
return context->fd;
cleanup_out:
cleanup_fildes(fildes);
cleanup_instance(device, context, nrt_mode);
err_out:
return ret;
}
EXPORT_SYMBOL(__rt_dev_open);
int __rt_dev_socket(rtdm_user_info_t *user_info, int protocol_family,
int socket_type, int protocol)
{
struct rtdm_device *device;
struct rtdm_fildes *fildes;
struct rtdm_dev_context *context;
int ret;
int nrt_mode = !rtdm_in_rt_context();
device = get_protocol_device(protocol_family, socket_type);
trace_mark(xn_rtdm, socket, "user_info %p protocol_family %d "
"socket_type %d protocol %d device %p",
user_info, protocol_family, socket_type, protocol, device);
ret = -EAFNOSUPPORT;
if (!device)
goto err_out;
ret = create_instance(device, &context, &fildes, user_info, nrt_mode);
if (ret != 0)
goto cleanup_out;
if (nrt_mode) {
context->context_flags = (1 << RTDM_CREATED_IN_NRT);
ret = device->socket_nrt(context, user_info, protocol);
} else {
context->context_flags = 0;
ret = device->socket_rt(context, user_info, protocol);
}
RTAI_ASSERT(RTDM, !rthal_local_irq_disabled(),
rthal_local_irq_enable(););
if (unlikely(ret < 0))
goto cleanup_out;
fildes->context = context;
trace_mark(xn_rtdm, fd_created,
"device %p fd %d", device, context->fd);
return context->fd;
cleanup_out:
cleanup_fildes(fildes);
cleanup_instance(device, context, nrt_mode);
err_out:
return ret;
}
EXPORT_SYMBOL(__rt_dev_socket);
int __rt_dev_close(rtdm_user_info_t *user_info, int fd)
{
struct rtdm_dev_context *context;
spl_t s;
int ret;
int nrt_mode = !rtdm_in_rt_context();
trace_mark(xn_rtdm, close, "user_info %p fd %d", user_info, fd);
ret = -EBADF;
if (unlikely((unsigned int)fd >= RTDM_FD_MAX))
goto err_out;
xnlock_get_irqsave(&rt_fildes_lock, s);
context = fildes_table[fd].context;
if (unlikely(!context)) {
xnlock_put_irqrestore(&rt_fildes_lock, s);
goto err_out; /* -EBADF */
}
/* Avoid asymmetric close context by switching to nrt. */
if (unlikely(test_bit(RTDM_CREATED_IN_NRT, &context->context_flags)) &&
!nrt_mode) {
xnlock_put_irqrestore(&rt_fildes_lock, s);
ret = -ENOSYS;
goto err_out;
}
set_bit(RTDM_CLOSING, &context->context_flags);
atomic_inc(&context->close_lock_count);
__cleanup_fildes(&fildes_table[fd]);
xnlock_put_irqrestore(&rt_fildes_lock, s);
if (nrt_mode)
ret = context->ops->close_nrt(context, user_info);
else
ret = context->ops->close_rt(context, user_info);
RTAI_ASSERT(RTDM, !rthal_local_irq_disabled(),
rthal_local_irq_enable(););
xnlock_get_irqsave(&rt_fildes_lock, s);
if (ret == -EAGAIN || atomic_read(&context->close_lock_count) > 2) {
atomic_dec(&context->close_lock_count);
list_add(&context->reserved.cleanup, &cleanup_queue);
xnlock_put_irqrestore(&rt_fildes_lock, s);
if (ret == -EAGAIN) {
rthal_apc_schedule(rtdm_apc);
ret = 0;
}
goto unlock_out;
}
xnlock_put_irqrestore(&rt_fildes_lock, s);
trace_mark(xn_rtdm, fd_closed, "fd %d", context->fd);
cleanup_instance(context->device, context,
test_bit(RTDM_CREATED_IN_NRT,
&context->context_flags));
return ret;
unlock_out:
rtdm_context_unlock(context);
err_out:
return ret;
}
EXPORT_SYMBOL(__rt_dev_close);
void cleanup_owned_contexts(void *owner)
{
struct rtdm_dev_context *context;
unsigned int fd;
int ret;
spl_t s;
for (fd = 0; fd < RTDM_FD_MAX; fd++) {
xnlock_get_irqsave(&rt_fildes_lock, s);
context = fildes_table[fd].context;
if (context && context->reserved.owner != owner)
context = NULL;
xnlock_put_irqrestore(&rt_fildes_lock, s);
if (context) {
if (RTAI_DEBUG(RTDM))
xnprintf("RTDM: closing file descriptor %d.\n",
fd);
ret = __rt_dev_close(NULL, fd);
RTAI_ASSERT(RTDM, ret == 0 || ret == -EBADF,
/* only warn here */;);
}
}
}
#define MAJOR_FUNCTION_WRAPPER_TH(operation, args...) \
do { \
struct rtdm_dev_context *context; \
struct rtdm_operations *ops; \
int ret; \
\
context = rtdm_context_get(fd); \
ret = -EBADF; \
if (unlikely(!context)) \
goto err_out; \
\
ops = context->ops; \
\
if (rtdm_in_rt_context()) \
ret = ops->operation##_rt(context, user_info, args); \
else \
ret = ops->operation##_nrt(context, user_info, args); \
\
RTAI_ASSERT(RTDM, !rthal_local_irq_disabled(), \
rthal_local_irq_enable();)
#define MAJOR_FUNCTION_WRAPPER_BH() \
rtdm_context_unlock(context); \
\
err_out: \
trace_mark(xn_rtdm, operation##_done, "result %d", ret); \
return ret; \
} while (0)
#define MAJOR_FUNCTION_WRAPPER(operation, args...) \
do { \
MAJOR_FUNCTION_WRAPPER_TH(operation, args); \
MAJOR_FUNCTION_WRAPPER_BH(); \
} while (0)
int __rt_dev_ioctl(rtdm_user_info_t *user_info, int fd, int request, ...)
{
va_list args;
void __user *arg;
va_start(args, request);
arg = va_arg(args, void __user *);
va_end(args);
trace_mark(xn_rtdm, ioctl, "user_info %p fd %d request %d arg %p",
user_info, fd, request, arg);
MAJOR_FUNCTION_WRAPPER_TH(ioctl, (unsigned int)request, arg);
if (unlikely(ret < 0) && (unsigned int)request == RTIOC_DEVICE_INFO) {
struct rtdm_device *dev = context->device;
struct rtdm_device_info dev_info;
dev_info.device_flags = dev->device_flags;
dev_info.device_class = dev->device_class;
dev_info.device_sub_class = dev->device_sub_class;
dev_info.profile_version = dev->profile_version;
ret = rtdm_safe_copy_to_user(user_info, arg, &dev_info,
sizeof(dev_info));
}
MAJOR_FUNCTION_WRAPPER_BH();
}
EXPORT_SYMBOL(__rt_dev_ioctl);
ssize_t __rt_dev_read(rtdm_user_info_t *user_info, int fd, void *buf,
size_t nbyte)
{
trace_mark(xn_rtdm, read, "user_info %p fd %d buf %p nbyte %zu",
user_info, fd, buf, nbyte);
MAJOR_FUNCTION_WRAPPER(read, buf, nbyte);
}
EXPORT_SYMBOL(__rt_dev_read);
ssize_t __rt_dev_write(rtdm_user_info_t *user_info, int fd, const void *buf,
size_t nbyte)
{
trace_mark(xn_rtdm, write, "user_info %p fd %d buf %p nbyte %zu",
user_info, fd, buf, nbyte);
MAJOR_FUNCTION_WRAPPER(write, buf, nbyte);
}
EXPORT_SYMBOL(__rt_dev_write);
ssize_t __rt_dev_recvmsg(rtdm_user_info_t *user_info, int fd,
struct msghdr *msg, int flags)
{
trace_mark(xn_rtdm, recvmsg, "user_info %p fd %d msg_name %p "
"msg_namelen %u msg_iov %p msg_iovlen %zu "
"msg_control %p msg_controllen %zu msg_flags %d",
user_info, fd, msg->msg_name, msg->msg_namelen,
msg->msg_iov, msg->msg_iovlen, msg->msg_control,
msg->msg_controllen, msg->msg_flags);
MAJOR_FUNCTION_WRAPPER(recvmsg, msg, flags);
}
EXPORT_SYMBOL(__rt_dev_recvmsg);
ssize_t __rt_dev_sendmsg(rtdm_user_info_t *user_info, int fd,
const struct msghdr *msg, int flags)
{
trace_mark(xn_rtdm, sendmsg, "user_info %p fd %d msg_name %p "
"msg_namelen %u msg_iov %p msg_iovlen %zu "
"msg_control %p msg_controllen %zu msg_flags %d",
user_info, fd, msg->msg_name, msg->msg_namelen,
msg->msg_iov, msg->msg_iovlen, msg->msg_control,
msg->msg_controllen, msg->msg_flags);
MAJOR_FUNCTION_WRAPPER(sendmsg, msg, flags);
}
EXPORT_SYMBOL(__rt_dev_sendmsg);
/**
* @brief Bind a selector to specified event types of a given file descriptor
* @internal
*
* This function is invoked by higher RTOS layers implementing select-like
* services. It shall not be called directly by RTDM drivers.
*
* @param[in] fd File descriptor to bind to
* @param[in,out] selector Selector object that shall be bound to the given
* event
* @param[in] type Event type the caller is interested in
* @param[in] fd_index Index in the file descriptor set of the caller
*
* @return 0 on success, otherwise:
*
* - -EBADF is returned if the file descriptor @a fd cannot be resolved.
*
* - -EINVAL is returned if @a type or @a fd_index are invalid.
*
* Environments:
*
* This service can be called from:
*
* - Kernel module initialization/cleanup code
* - Kernel-based task
* - User-space task (RT, non-RT)
*
* Rescheduling: never.
*/
int rtdm_select_bind(int fd, rtdm_selector_t *selector,
enum rtdm_selecttype type, unsigned fd_index)
{
struct rtdm_dev_context *context;
struct rtdm_operations *ops;
int ret;
context = rtdm_context_get(fd);
ret = -EBADF;
if (unlikely(!context))
goto err_out;
ops = context->ops;
ret = ops->select_bind(context, selector, type, fd_index);
RTAI_ASSERT(RTDM, !rthal_local_irq_disabled(),
rthal_local_irq_enable(););
rtdm_context_unlock(context);
err_out:
return ret;
}
EXPORT_SYMBOL(rtdm_select_bind);
#ifdef DOXYGEN_CPP /* Only used for doxygen doc generation */
/**
* @brief Increment context reference counter
*
* @param[in] context Device context
*
* @note rtdm_context_get() automatically increments the lock counter. You
* only need to call this function in special scenarios, e.g. when keeping
* additional references to the context structure that have different
* lifetimes. Only use rtdm_context_lock() on contexts that are currently
* locked via an earlier rtdm_context_get()/rtdm_contex_lock() or while
* running a device operation handler.
*
* Environments:
*
* This service can be called from:
*
* - Kernel module initialization/cleanup code
* - Interrupt service routine
* - Kernel-based task
* - User-space task (RT, non-RT)
*
* Rescheduling: never.
*/
void rtdm_context_lock(struct rtdm_dev_context *context);
/**
* @brief Decrement context reference counter
*
* @param[in] context Device context
*
* @note Every call to rtdm_context_locked() must be matched by a
* rtdm_context_unlock() invocation.
*
* Environments:
*
* This service can be called from:
*
* - Kernel module initialization/cleanup code
* - Interrupt service routine
* - Kernel-based task
* - User-space task (RT, non-RT)
*
* Rescheduling: never.
*/
void rtdm_context_unlock(struct rtdm_dev_context *context);
/**
* @brief Release a device context obtained via rtdm_context_get()
*
* @param[in] context Device context
*
* @note Every successful call to rtdm_context_get() must be matched by a
* rtdm_context_put() invocation.
*
* Environments:
*
* This service can be called from:
*
* - Kernel module initialization/cleanup code
* - Interrupt service routine
* - Kernel-based task
* - User-space task (RT, non-RT)
*
* Rescheduling: never.
*/
void rtdm_context_put(struct rtdm_dev_context *context);
/**
* @brief Open a device
*
* Refer to rt_dev_open() for parameters and return values
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*/
int rtdm_open(const char *path, int oflag, ...);
/**
* @brief Create a socket
*
* Refer to rt_dev_socket() for parameters and return values
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*/
int rtdm_socket(int protocol_family, int socket_type, int protocol);
/**
* @brief Close a device or socket
*
* Refer to rt_dev_close() for parameters and return values
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*/
int rtdm_close(int fd);
/**
* @brief Issue an IOCTL
*
* Refer to rt_dev_ioctl() for parameters and return values
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*/
int rtdm_ioctl(int fd, int request, ...);
/**
* @brief Read from device
*
* Refer to rt_dev_read() for parameters and return values
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*/
ssize_t rtdm_read(int fd, void *buf, size_t nbyte);
/**
* @brief Write to device
*
* Refer to rt_dev_write() for parameters and return values
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*/
ssize_t rtdm_write(int fd, const void *buf, size_t nbyte);
/**
* @brief Receive message from socket
*
* Refer to rt_dev_recvmsg() for parameters and return values
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*/
ssize_t rtdm_recvmsg(int fd, struct msghdr *msg, int flags);
/**
* @brief Receive message from socket
*
* Refer to rt_dev_recvfrom() for parameters and return values
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*/
ssize_t rtdm_recvfrom(int fd, void *buf, size_t len, int flags,
struct sockaddr *from, socklen_t *fromlen);
/**
* @brief Receive message from socket
*
* Refer to rt_dev_recv() for parameters and return values
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*/
ssize_t rtdm_recv(int fd, void *buf, size_t len, int flags);
/**
* @brief Transmit message to socket
*
* Refer to rt_dev_sendmsg() for parameters and return values
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*/
ssize_t rtdm_sendmsg(int fd, const struct msghdr *msg, int flags);
/**
* @brief Transmit message to socket
*
* Refer to rt_dev_sendto() for parameters and return values
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*/
ssize_t rtdm_sendto(int fd, const void *buf, size_t len, int flags,
const struct sockaddr *to, socklen_t tolen);
/**
* @brief Transmit message to socket
*
* Refer to rt_dev_send() for parameters and return values
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*/
ssize_t rtdm_send(int fd, const void *buf, size_t len, int flags);
/**
* @brief Bind to local address
*
* Refer to rt_dev_bind() for parameters and return values
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*/
int rtdm_bind(int fd, const struct sockaddr *my_addr, socklen_t addrlen);
/**
* @brief Connect to remote address
*
* Refer to rt_dev_connect() for parameters and return values
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*/
int rtdm_connect(int fd, const struct sockaddr *serv_addr, socklen_t addrlen);
/**
* @brief Listen for incomming connection requests
*
* Refer to rt_dev_listen() for parameters and return values
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*/
int rtdm_listen(int fd, int backlog);
/**
* @brief Accept a connection requests
*
* Refer to rt_dev_accept() for parameters and return values
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*/
int rtdm_accept(int fd, struct sockaddr *addr, socklen_t *addrlen);
/**
* @brief Shut down parts of a connection
*
* Refer to rt_dev_shutdown() for parameters and return values
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*/
int rtdm_shutdown(int fd, int how);
/**
* @brief Get socket option
*
* Refer to rt_dev_getsockopt() for parameters and return values
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*/
int rtdm_getsockopt(int fd, int level, int optname, void *optval,
socklen_t *optlen);
/**
* @brief Set socket option
*
* Refer to rt_dev_setsockopt() for parameters and return values
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*/
int rtdm_setsockopt(int fd, int level, int optname, const void *optval,
socklen_t optlen);
/**
* @brief Get local socket address
*
* Refer to rt_dev_getsockname() for parameters and return values
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*/
int rtdm_getsockname(int fd, struct sockaddr *name, socklen_t *namelen);
/**
* @brief Get socket destination address
*
* Refer to rt_dev_getpeername() for parameters and return values
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*/
int rtdm_getpeername(int fd, struct sockaddr *name, socklen_t *namelen);
/** @} */
/*!
* @addtogroup userapi
* @{
*/
/**
* @brief Open a device
*
* @param[in] path Device name
* @param[in] oflag Open flags
* @param ... Further parameters will be ignored.
*
* @return Positive file descriptor value on success, otherwise a negative
* error code.
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*
* @see @c open() in IEEE Std 1003.1,
* http://www.opengroup.org/onlinepubs/009695399
*/
int rt_dev_open(const char *path, int oflag, ...);
/**
* @brief Create a socket
*
* @param[in] protocol_family Protocol family (@c PF_xxx)
* @param[in] socket_type Socket type (@c SOCK_xxx)
* @param[in] protocol Protocol ID, 0 for default
*
* @return Positive file descriptor value on success, otherwise a negative
* error code.
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*
* @see @c socket() in IEEE Std 1003.1,
* http://www.opengroup.org/onlinepubs/009695399
*/
int rt_dev_socket(int protocol_family, int socket_type, int protocol);
/**
* @brief Close a device or socket
*
* @param[in] fd File descriptor as returned by rt_dev_open() or rt_dev_socket()
*
* @return 0 on success, otherwise a negative error code.
*
* @note If the matching rt_dev_open() or rt_dev_socket() call took place in
* non-real-time context, rt_dev_close() must be issued within non-real-time
* as well. Otherwise, the call will fail.
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*
* @see @c close() in IEEE Std 1003.1,
* http://www.opengroup.org/onlinepubs/009695399
*/
int rt_dev_close(int fd);
/**
* @brief Issue an IOCTL
*
* @param[in] fd File descriptor as returned by rt_dev_open() or rt_dev_socket()
* @param[in] request IOCTL code
* @param ... Optional third argument, depending on IOCTL function
* (@c void @c * or @c unsigned @c long)
*
* @return Positiv value on success, otherwise negative error code
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*
* @see @c ioctl() in IEEE Std 1003.1,
* http://www.opengroup.org/onlinepubs/009695399
*/
int rt_dev_ioctl(int fd, int request, ...);
/**
* @brief Read from device
*
* @param[in] fd File descriptor as returned by rt_dev_open()
* @param[out] buf Input buffer
* @param[in] nbyte Number of bytes to read
*
* @return Number of bytes read, otherwise negative error code
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*
* @see @c read() in IEEE Std 1003.1,
* http://www.opengroup.org/onlinepubs/009695399
*/
ssize_t rt_dev_read(int fd, void *buf, size_t nbyte);
/**
* @brief Write to device
*
* @param[in] fd File descriptor as returned by rt_dev_open()
* @param[in] buf Output buffer
* @param[in] nbyte Number of bytes to write
*
* @return Number of bytes written, otherwise negative error code
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*
* @see @c write() in IEEE Std 1003.1,
* http://www.opengroup.org/onlinepubs/009695399
*/
ssize_t rt_dev_write(int fd, const void *buf, size_t nbyte);
/**
* @brief Receive message from socket
*
* @param[in] fd File descriptor as returned by rt_dev_socket()
* @param[in,out] msg Message descriptor
* @param[in] flags Message flags
*
* @return Number of bytes received, otherwise negative error code
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*
* @see @c recvmsg() in IEEE Std 1003.1,
* http://www.opengroup.org/onlinepubs/009695399
*/
ssize_t rt_dev_recvmsg(int fd, struct msghdr *msg, int flags);
/**
* @brief Receive message from socket
*
* @param[in] fd File descriptor as returned by rt_dev_socket()
* @param[out] buf Message buffer
* @param[in] len Message buffer size
* @param[in] flags Message flags
* @param[out] from Buffer for message sender address
* @param[in,out] fromlen Address buffer size
*
* @return Number of bytes received, otherwise negative error code
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*
* @see @c recvfrom() in IEEE Std 1003.1,
* http://www.opengroup.org/onlinepubs/009695399
*/
ssize_t rt_dev_recvfrom(int fd, void *buf, size_t len, int flags,
struct sockaddr *from, socklen_t *fromlen);
/**
* @brief Receive message from socket
*
* @param[in] fd File descriptor as returned by rt_dev_socket()
* @param[out] buf Message buffer
* @param[in] len Message buffer size
* @param[in] flags Message flags
*
* @return Number of bytes received, otherwise negative error code
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*
* @see @c recv() in IEEE Std 1003.1,
* http://www.opengroup.org/onlinepubs/009695399
*/
ssize_t rt_dev_recv(int fd, void *buf, size_t len, int flags);
/**
* @brief Transmit message to socket
*
* @param[in] fd File descriptor as returned by rt_dev_socket()
* @param[in] msg Message descriptor
* @param[in] flags Message flags
*
* @return Number of bytes sent, otherwise negative error code
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*
* @see @c sendmsg() in IEEE Std 1003.1,
* http://www.opengroup.org/onlinepubs/009695399
*/
ssize_t rt_dev_sendmsg(int fd, const struct msghdr *msg, int flags);
/**
* @brief Transmit message to socket
*
* @param[in] fd File descriptor as returned by rt_dev_socket()
* @param[in] buf Message buffer
* @param[in] len Message buffer size
* @param[in] flags Message flags
* @param[in] to Buffer for message destination address
* @param[in] tolen Address buffer size
*
* @return Number of bytes sent, otherwise negative error code
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*
* @see @c sendto() in IEEE Std 1003.1,
* http://www.opengroup.org/onlinepubs/009695399
*/
ssize_t rt_dev_sendto(int fd, const void *buf, size_t len, int flags,
const struct sockaddr *to, socklen_t tolen);
/**
* @brief Transmit message to socket
*
* @param[in] fd File descriptor as returned by rt_dev_socket()
* @param[in] buf Message buffer
* @param[in] len Message buffer size
* @param[in] flags Message flags
*
* @return Number of bytes sent, otherwise negative error code
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*
* @see @c send() in IEEE Std 1003.1,
* http://www.opengroup.org/onlinepubs/009695399
*/
ssize_t rt_dev_send(int fd, const void *buf, size_t len, int flags);
/**
* @brief Bind to local address
*
* @param[in] fd File descriptor as returned by rt_dev_socket()
* @param[in] my_addr Address buffer
* @param[in] addrlen Address buffer size
*
* @return 0 on success, otherwise negative error code
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*
* @see @c bind() in IEEE Std 1003.1,
* http://www.opengroup.org/onlinepubs/009695399
*/
int rt_dev_bind(int fd, const struct sockaddr *my_addr, socklen_t addrlen);
/**
* @brief Connect to remote address
*
* @param[in] fd File descriptor as returned by rt_dev_socket()
* @param[in] serv_addr Address buffer
* @param[in] addrlen Address buffer size
*
* @return 0 on success, otherwise negative error code
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*
* @see @c connect() in IEEE Std 1003.1,
* http://www.opengroup.org/onlinepubs/009695399
*/
int rt_dev_connect(int fd, const struct sockaddr *serv_addr,
socklen_t addrlen);
/**
* @brief Listen for incomming connection requests
*
* @param[in] fd File descriptor as returned by rt_dev_socket()
* @param[in] backlog Maximum queue length
*
* @return 0 on success, otherwise negative error code
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*
* @see @c lsiten() in IEEE Std 1003.1,
* http://www.opengroup.org/onlinepubs/009695399
*/
int rt_dev_listen(int fd, int backlog);
/**
* @brief Accept a connection requests
*
* @param[in] fd File descriptor as returned by rt_dev_socket()
* @param[out] addr Buffer for remote address
* @param[in,out] addrlen Address buffer size
*
* @return 0 on success, otherwise negative error code
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*
* @see @c accept() in IEEE Std 1003.1,
* http://www.opengroup.org/onlinepubs/009695399
*/
int rt_dev_accept(int fd, struct sockaddr *addr, socklen_t *addrlen);
/**
* @brief Shut down parts of a connection
*
* @param[in] fd File descriptor as returned by rt_dev_socket()
* @param[in] how Specifies the part to be shut down (@c SHUT_xxx)
*
* @return 0 on success, otherwise negative error code
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*
* @see @c shutdown() in IEEE Std 1003.1,
* http://www.opengroup.org/onlinepubs/009695399
*/
int rt_dev_shutdown(int fd, int how);
/**
* @brief Get socket option
*
* @param[in] fd File descriptor as returned by rt_dev_socket()
* @param[in] level Addressed stack level
* @param[in] optname Option name ID
* @param[out] optval Value buffer
* @param[in,out] optlen Value buffer size
*
* @return 0 on success, otherwise negative error code
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*
* @see @c getsockopt() in IEEE Std 1003.1,
* http://www.opengroup.org/onlinepubs/009695399
*/
int rt_dev_getsockopt(int fd, int level, int optname, void *optval,
socklen_t *optlen);
/**
* @brief Set socket option
*
* @param[in] fd File descriptor as returned by rt_dev_socket()
* @param[in] level Addressed stack level
* @param[in] optname Option name ID
* @param[in] optval Value buffer
* @param[in] optlen Value buffer size
*
* @return 0 on success, otherwise negative error code
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*
* @see @c setsockopt() in IEEE Std 1003.1,
* http://www.opengroup.org/onlinepubs/009695399
*/
int rt_dev_setsockopt(int fd, int level, int optname, const void *optval,
socklen_t optlen);
/**
* @brief Get local socket address
*
* @param[in] fd File descriptor as returned by rt_dev_socket()
* @param[out] name Address buffer
* @param[in,out] namelen Address buffer size
*
* @return 0 on success, otherwise negative error code
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*
* @see @c getsockname() in IEEE Std 1003.1,
* http://www.opengroup.org/onlinepubs/009695399
*/
int rt_dev_getsockname(int fd, struct sockaddr *name, socklen_t *namelen);
/**
* @brief Get socket destination address
*
* @param[in] fd File descriptor as returned by rt_dev_socket()
* @param[out] name Address buffer
* @param[in,out] namelen Address buffer size
*
* @return 0 on success, otherwise negative error code
*
* Environments:
*
* Depends on driver implementation, see @ref profiles "Device Profiles".
*
* Rescheduling: possible.
*
* @see @c getpeername() in IEEE Std 1003.1,
* http://www.opengroup.org/onlinepubs/009695399
*/
int rt_dev_getpeername(int fd, struct sockaddr *name, socklen_t *namelen);
/** @} */
#endif /* DOXYGEN_CPP */
#ifdef CONFIG_RTAI_RTDM_SELECT
/*
RTAI extension to use select as any other usual RTDM rt_dev_xxx service.
At the moment selector kept and stack, initialised/destroyed at each call.
Usage is as for:
int rt_dev_select(int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds,
nanosecs_rel_t timeout);.
As it can be seen args are as for the standard select call execpt for the
timout which is in nanos, in place of timespec.
*/
#define SELECT_DIM XNSELECT_MAX_TYPES
/*
RTDM_FD_START kept in case we'll ever need a shift for fd being different from
fd_index in the call to:
int rtdm_select_bind(int fd, rtdm_selector_t *selector, enum rtdm_selecttype type, unsigned fd_index);
*/
#define RTDM_FD_START 0
int __rt_dev_select(int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, nanosecs_rel_t timeout, struct xnselector *selector, int space)
{
int i, fd, ret;
fd_set *fds[SELECT_DIM] = { rfds, wfds, efds };
fd_set *reqp[SELECT_DIM], req[SELECT_DIM];
fd_set *resp[SELECT_DIM], res[SELECT_DIM];
fd_set **bp, *cp;
for (ret = i = 0; i < SELECT_DIM; i++) {
if (fds[i] && find_first_bit(fds[i]->fds_bits, nfds) < nfds) {
ret = 1;
reqp[i] = &req[i];
resp[i] = &res[i];
if (space) {
memcpy((void *)reqp[i], (void *)fds[i], __FDELT__(nfds + __NFDBITS__ - 1)*sizeof(long));
} else {
rt_copy_from_user((void *)reqp[i], (void *)fds[i], __FDELT__(nfds + __NFDBITS__ - 1)*sizeof(long));
}
} else {
reqp[i] = resp[i] = NULL;
}
}
if (!ret && !timeout) {
return -EINVAL;
}
if (timeout) {
timeout = rt_get_time() + nano2count(timeout);
}
do {
for (bp = reqp, i = 0; i < SELECT_DIM; i++) {
if ((cp = bp[i])) {
for (fd = find_first_bit(cp->fds_bits, nfds); fd < nfds; fd = find_next_bit(cp->fds_bits, nfds, fd + 1)) {
if ((ret = rtdm_select_bind(fd - RTDM_FD_START, selector, i, fd))) {
return ret;
}
}
}
}
bp = resp;
ret = xnselect(selector, resp, reqp, nfds, timeout, XN_ABSOLUTE);
} while (ret == -ECHRNG);
if (ret > 0) {
for (i = 0; i < SELECT_DIM; i++) {
if (fds[i]) {
if (space) {
memcpy((void *)fds[i], (void *)resp[i], sizeof(fd_set));
} else {
rt_copy_to_user((void *)fds[i], (void *)resp[i], sizeof(fd_set));
}
}
}
}
return ret;
}
EXPORT_SYMBOL(__rt_dev_select);
#endif