mirror of
https://gitlab.rtems.org/rtems/rtos/rtems.git
synced 2026-08-29 19:51:52 +08:00
iodev: Add iodev driver
Add iodev driver and related test
This commit is contained in:
committed by
Kinsey Moore
parent
4a513030e2
commit
ed072a2501
@@ -0,0 +1,353 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
|
||||
/*
|
||||
* Copyright (C) 2026 Aaron Nyholm
|
||||
*
|
||||
* 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <dev/io/iodev.h>
|
||||
|
||||
#include <rtems/imfs.h>
|
||||
#include <rtems/score/assert.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static int rtems_iodev_do_init(
|
||||
rtems_iodev *iodev,
|
||||
void ( *destroy )( rtems_iodev *iodev )
|
||||
);
|
||||
|
||||
static int rtems_iodev_ioctl(
|
||||
rtems_libio_t *iop,
|
||||
ioctl_command_t command,
|
||||
void *arg
|
||||
);
|
||||
|
||||
static int rtems_iodev_mmap(
|
||||
rtems_libio_t *iop,
|
||||
void **addr,
|
||||
size_t len,
|
||||
int prot,
|
||||
off_t off
|
||||
);
|
||||
|
||||
static int rtems_iodev_get_region( struct rtems_iodev *iodev, void *arg );
|
||||
|
||||
static int rtems_iodev_get_event_info( struct rtems_iodev *iodev, void *arg );
|
||||
|
||||
static int rtems_iodev_event_wait_on( struct rtems_iodev *iodev, void *arg );
|
||||
|
||||
static void rtems_iodev_node_destroy( IMFS_jnode_t *node );
|
||||
|
||||
static const rtems_filesystem_file_handlers_r rtems_iodev_handler = {
|
||||
.open_h = rtems_filesystem_default_open,
|
||||
.close_h = rtems_filesystem_default_close,
|
||||
.read_h = rtems_filesystem_default_read,
|
||||
.write_h = rtems_filesystem_default_write,
|
||||
.ioctl_h = rtems_iodev_ioctl,
|
||||
.lseek_h = rtems_filesystem_default_lseek,
|
||||
.fstat_h = IMFS_stat,
|
||||
.ftruncate_h = rtems_filesystem_default_ftruncate,
|
||||
.fsync_h = rtems_filesystem_default_fsync_or_fdatasync,
|
||||
.fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync,
|
||||
.fcntl_h = rtems_filesystem_default_fcntl,
|
||||
.kqfilter_h = rtems_filesystem_default_kqfilter,
|
||||
.mmap_h = rtems_iodev_mmap,
|
||||
.poll_h = rtems_filesystem_default_poll,
|
||||
.readv_h = rtems_filesystem_default_readv,
|
||||
.writev_h = rtems_filesystem_default_writev
|
||||
};
|
||||
|
||||
static const IMFS_node_control
|
||||
rtems_iodev_node_control = IMFS_GENERIC_INITIALIZER(
|
||||
&rtems_iodev_handler,
|
||||
IMFS_node_initialize_generic,
|
||||
rtems_iodev_node_destroy
|
||||
);
|
||||
|
||||
static void rtems_iodev_node_destroy( IMFS_jnode_t *node )
|
||||
{
|
||||
rtems_iodev *iodev;
|
||||
|
||||
iodev = IMFS_generic_get_context_by_node( node );
|
||||
|
||||
( *iodev->destroy )( iodev );
|
||||
|
||||
IMFS_node_destroy_default( node );
|
||||
}
|
||||
|
||||
static void rtems_iodev_obtain( rtems_iodev *iodev )
|
||||
{
|
||||
rtems_recursive_mutex_lock( &iodev->mutex );
|
||||
}
|
||||
|
||||
static void rtems_iodev_release( rtems_iodev *iodev )
|
||||
{
|
||||
rtems_recursive_mutex_unlock( &iodev->mutex );
|
||||
}
|
||||
|
||||
static int rtems_iodev_ioctl(
|
||||
rtems_libio_t *iop,
|
||||
ioctl_command_t command,
|
||||
void *arg
|
||||
)
|
||||
{
|
||||
rtems_iodev *iodev = IMFS_generic_get_context_by_iop( iop );
|
||||
int err = 0;
|
||||
|
||||
rtems_iodev_obtain( iodev );
|
||||
|
||||
switch ( command ) {
|
||||
case RTEMS_IODEV_IOCTL_OBTAIN:
|
||||
rtems_iodev_obtain( iodev );
|
||||
err = 0;
|
||||
break;
|
||||
case RTEMS_IODEV_IOCTL_RELEASE:
|
||||
rtems_iodev_release( iodev );
|
||||
err = 0;
|
||||
break;
|
||||
case RTEMS_IODEV_IOCTL_DEVICE_INFO:
|
||||
err = iodev->get_device_info( iodev, arg );
|
||||
break;
|
||||
case RTEMS_IODEV_IOCTL_REGION_COUNT:
|
||||
err = iodev->get_region_count( iodev, arg );
|
||||
break;
|
||||
case RTEMS_IODEV_IOCTL_REGION_GET:
|
||||
err = rtems_iodev_get_region( iodev, arg );
|
||||
break;
|
||||
case RTEMS_IODEV_IOCTL_EVENT_COUNT:
|
||||
err = iodev->get_event_count( iodev, arg );
|
||||
break;
|
||||
case RTEMS_IODEV_IOCTL_EVENT_INFO:
|
||||
err = rtems_iodev_get_event_info( iodev, arg );
|
||||
break;
|
||||
case RTEMS_IODEV_IOCTL_EVENT_WAIT:
|
||||
err = rtems_iodev_event_wait_on( iodev, arg );
|
||||
break;
|
||||
default:
|
||||
err = EINVAL;
|
||||
}
|
||||
|
||||
rtems_iodev_release( iodev );
|
||||
if ( err != 0 ) {
|
||||
rtems_set_errno_and_return_minus_one( err );
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int rtems_iodev_mmap(
|
||||
rtems_libio_t *iop,
|
||||
void **addr,
|
||||
size_t len,
|
||||
int prot,
|
||||
off_t off
|
||||
)
|
||||
{
|
||||
rtems_iodev *iodev = IMFS_generic_get_context_by_iop( iop );
|
||||
rtems_iodev_region *regions;
|
||||
size_t region_count = 0;
|
||||
|
||||
if ( len == 0 || *addr != NULL || off < 0 ) {
|
||||
rtems_set_errno_and_return_minus_one( EINVAL );
|
||||
}
|
||||
|
||||
if ( prot != ( PROT_READ | PROT_WRITE ) ) {
|
||||
rtems_set_errno_and_return_minus_one( EINVAL );
|
||||
}
|
||||
|
||||
iodev->get_region_count( iodev, ®ion_count );
|
||||
if ( (size_t) off > region_count ) {
|
||||
rtems_set_errno_and_return_minus_one( EOVERFLOW );
|
||||
}
|
||||
|
||||
iodev->get_regions( iodev, ®ions );
|
||||
if ( len > regions[ off ].size ) {
|
||||
rtems_set_errno_and_return_minus_one( EINVAL );
|
||||
}
|
||||
|
||||
*addr = regions[ off ].address;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtems_iodev_get_region( struct rtems_iodev *iodev, void *arg )
|
||||
{
|
||||
rtems_iodev_region *regions;
|
||||
rtems_iodev_region *region;
|
||||
size_t region_count = 0;
|
||||
|
||||
region = (rtems_iodev_region *) arg;
|
||||
|
||||
iodev->get_region_count( iodev, ®ion_count );
|
||||
if ( region->index >= region_count ) {
|
||||
rtems_set_errno_and_return_minus_one( EINVAL );
|
||||
}
|
||||
|
||||
iodev->get_regions( iodev, ®ions );
|
||||
region->address = regions[ region->index ].address;
|
||||
region->size = regions[ region->index ].size;
|
||||
region->name = regions[ region->index ].name;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtems_iodev_get_event_info( struct rtems_iodev *iodev, void *arg )
|
||||
{
|
||||
rtems_iodev_event_info *e_info;
|
||||
int status;
|
||||
size_t event_count;
|
||||
|
||||
e_info = (rtems_iodev_event_info *) arg;
|
||||
|
||||
iodev->get_event_count( iodev, &event_count );
|
||||
if ( e_info->index >= event_count ) {
|
||||
rtems_set_errno_and_return_minus_one( EINVAL );
|
||||
}
|
||||
|
||||
if ( iodev->get_event_info == NULL ) {
|
||||
rtems_set_errno_and_return_minus_one( ENODEV );
|
||||
}
|
||||
|
||||
status = ( *iodev->get_event_info )( iodev, e_info );
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int rtems_iodev_event_wait_on( struct rtems_iodev *iodev, void *arg )
|
||||
{
|
||||
rtems_iodev_event_args *e_args;
|
||||
int status;
|
||||
size_t event_count;
|
||||
|
||||
e_args = (rtems_iodev_event_args *) arg;
|
||||
|
||||
iodev->get_event_count( iodev, &event_count );
|
||||
if ( e_args->index >= event_count ) {
|
||||
rtems_set_errno_and_return_minus_one( EINVAL );
|
||||
}
|
||||
|
||||
if ( iodev->event_wait == NULL ) {
|
||||
rtems_set_errno_and_return_minus_one( ENODEV );
|
||||
}
|
||||
|
||||
status = ( *iodev->event_wait )( iodev, e_args );
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int rtems_iodev_register( rtems_iodev *iodev, const char *iodev_path )
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = IMFS_make_generic_node(
|
||||
iodev_path,
|
||||
S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO,
|
||||
&rtems_iodev_node_control,
|
||||
iodev
|
||||
);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int rtems_iodev_unregister_and_destroy( const char *iodev_path )
|
||||
{
|
||||
return unlink( iodev_path );
|
||||
}
|
||||
|
||||
static int rtems_iodev_do_init(
|
||||
rtems_iodev *iodev,
|
||||
void ( *destroy )( rtems_iodev *iodev )
|
||||
)
|
||||
{
|
||||
rtems_recursive_mutex_init( &iodev->mutex, "RTEMS_IODEV device" );
|
||||
iodev->destroy = destroy;
|
||||
/* other fields are guaranteed nulled by caller */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rtems_iodev_destroy_unregistered( rtems_iodev *iodev )
|
||||
{
|
||||
( *iodev->destroy )( iodev );
|
||||
}
|
||||
|
||||
static void iodev_destroy_internal( rtems_iodev *iodev )
|
||||
{
|
||||
if ( iodev->priv_destroy != NULL ) {
|
||||
( *iodev->priv_destroy )( iodev );
|
||||
}
|
||||
|
||||
rtems_recursive_mutex_destroy( &iodev->mutex );
|
||||
}
|
||||
|
||||
static void iodev_destroy_and_free_internal( rtems_iodev *iodev )
|
||||
{
|
||||
if ( iodev == NULL ) {
|
||||
return;
|
||||
}
|
||||
iodev_destroy_internal( iodev );
|
||||
free( iodev );
|
||||
iodev = NULL;
|
||||
}
|
||||
|
||||
int rtems_iodev_init( rtems_iodev *iodev )
|
||||
{
|
||||
memset( iodev, 0, sizeof( *iodev ) );
|
||||
|
||||
return rtems_iodev_do_init( iodev, iodev_destroy_internal );
|
||||
}
|
||||
|
||||
rtems_iodev *rtems_iodev_alloc_and_init( size_t size )
|
||||
{
|
||||
rtems_iodev *iodev = NULL;
|
||||
|
||||
if ( size >= sizeof( *iodev ) ) {
|
||||
iodev = calloc( 1, size );
|
||||
if ( iodev != NULL ) {
|
||||
int rv;
|
||||
|
||||
rv = rtems_iodev_do_init( iodev, iodev_destroy_and_free_internal );
|
||||
if ( rv != 0 ) {
|
||||
rtems_recursive_mutex_destroy( &iodev->mutex );
|
||||
free( iodev );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return iodev;
|
||||
}
|
||||
@@ -0,0 +1,360 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @ingroup RTEMSGenericIODevAPI
|
||||
*
|
||||
* @brief Generic IO Device API
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2026 Aaron Nyholm
|
||||
*
|
||||
* 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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.
|
||||
*/
|
||||
|
||||
#ifndef _DEV_IODEV_H
|
||||
#define _DEV_IODEV_H
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <rtems.h>
|
||||
#include <rtems/thread.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct rtems_iodev rtems_iodev;
|
||||
|
||||
/**
|
||||
* @defgroup RTEMSGenericIOSpaceAPI Generic IO Space API
|
||||
*
|
||||
* @ingroup RTEMSDeviceDrivers
|
||||
*
|
||||
* @brief Generic IO Space API to wrap IO memory regions.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/* IOCTL Calls */
|
||||
|
||||
/**
|
||||
* @brief Obtains the iodev device.
|
||||
*
|
||||
* This command has no argument.
|
||||
*/
|
||||
#define RTEMS_IODEV_IOCTL_OBTAIN 0
|
||||
/**
|
||||
* @brief Releases the iodev device.
|
||||
*
|
||||
* This command has no argument.
|
||||
*/
|
||||
#define RTEMS_IODEV_IOCTL_RELEASE 1
|
||||
|
||||
/**
|
||||
* @brief Get the device information, defined by the driver.
|
||||
*
|
||||
* @param[out] info pointer to struct containing device information.
|
||||
*/
|
||||
#define RTEMS_IODEV_IOCTL_DEVICE_INFO 2
|
||||
|
||||
/**
|
||||
* @brief Get number of events in iodev.
|
||||
*
|
||||
* @param[out] count size_t containing the number of regions.
|
||||
*/
|
||||
#define RTEMS_IODEV_IOCTL_REGION_COUNT 3
|
||||
|
||||
/**
|
||||
* @brief Get number of events in iodev.
|
||||
*
|
||||
* @param[in,out] arg Pointer to rtems_iodev_region struct
|
||||
* with offset and length for return values.
|
||||
*/
|
||||
#define RTEMS_IODEV_IOCTL_REGION_GET 4
|
||||
|
||||
/**
|
||||
* @brief Get number of events in iodev.
|
||||
*
|
||||
* @param[out] count size_t containing the number of events.
|
||||
*/
|
||||
#define RTEMS_IODEV_IOCTL_EVENT_COUNT 5
|
||||
|
||||
/**
|
||||
* @brief Get information for given event ID.
|
||||
*
|
||||
* @param[in,out] event_info Pointer to rtems_iodev_event_info struct
|
||||
* containing event index and returned information.
|
||||
*/
|
||||
#define RTEMS_IODEV_IOCTL_EVENT_INFO 6
|
||||
|
||||
/**
|
||||
* @brief Wait or poll on event. Set timeout to zero for indefinite wait.
|
||||
*
|
||||
* @param[in,out] event_args Pointer to rtems_iodev_event_args struct
|
||||
* containing event index, timeout length and iodev driver return value.
|
||||
*/
|
||||
#define RTEMS_IODEV_IOCTL_EVENT_WAIT 7
|
||||
|
||||
/**
|
||||
* @brief The maximum number of IO regions.
|
||||
*/
|
||||
#define RTEMS_IODEV_MAX_REGIONS 32
|
||||
|
||||
/**
|
||||
* @brief General definition for an iodev region.
|
||||
*/
|
||||
typedef struct rtems_iodev_region {
|
||||
/**
|
||||
* @brief Region index.
|
||||
*/
|
||||
size_t index;
|
||||
/**
|
||||
* @brief Region address.
|
||||
*/
|
||||
void *address;
|
||||
/**
|
||||
* @brief Length of region.
|
||||
*/
|
||||
size_t size;
|
||||
/**
|
||||
* @brief Name of region.
|
||||
*/
|
||||
const char *name;
|
||||
} rtems_iodev_region;
|
||||
|
||||
/**
|
||||
* @brief General description of iodev event.
|
||||
*/
|
||||
typedef struct rtems_iodev_event_info {
|
||||
/**
|
||||
* @brief Event index.
|
||||
*/
|
||||
uint32_t index;
|
||||
/**
|
||||
* @brief Name of event.
|
||||
*/
|
||||
const char *name;
|
||||
/**
|
||||
* @brief Additional argument;
|
||||
*/
|
||||
void *args;
|
||||
} rtems_iodev_event_info;
|
||||
|
||||
/**
|
||||
* @brief General definition of iodev event arguments.
|
||||
*/
|
||||
typedef struct rtems_iodev_event_args {
|
||||
/**
|
||||
* @brief Event index.
|
||||
*/
|
||||
uint32_t index;
|
||||
/**
|
||||
* @brief Timeout length.
|
||||
*/
|
||||
struct timespec timeout;
|
||||
/**
|
||||
* @brief Returns if event timed out.
|
||||
*/
|
||||
bool timedout;
|
||||
/**
|
||||
* @brief Additional argument.
|
||||
*/
|
||||
void *args;
|
||||
} rtems_iodev_event_args;
|
||||
|
||||
/**
|
||||
* @brief iodev device.
|
||||
*/
|
||||
struct rtems_iodev {
|
||||
/**
|
||||
* @brief Call to the device driver to get device information.
|
||||
*
|
||||
* @param[in] iodev Pointer to iodev device.
|
||||
* @param[out] info Pointer to device information.
|
||||
*
|
||||
* @retval 0 Successful operation.
|
||||
* @retval non-zero Failed operation.
|
||||
*/
|
||||
int ( *get_device_info )( rtems_iodev *iodev, void *info );
|
||||
/**
|
||||
*
|
||||
* @brief Call to the device driver to get number of events.
|
||||
*
|
||||
* @param[in] iodev Pointer to iodev device.
|
||||
* @param[in,out] event_count Pointer to number of events.
|
||||
*
|
||||
* @retval 0 Successful operation.
|
||||
* @retval non-zero Failed operation.
|
||||
*/
|
||||
int ( *get_event_count )( rtems_iodev *iodev, size_t *event_count );
|
||||
|
||||
/**
|
||||
* @brief Call to the device driver to get info on event.
|
||||
*
|
||||
* @param[in] iodev Pointer to iodev device.
|
||||
* @param[in,out] event_args Arguments for wait operation.
|
||||
*
|
||||
* @retval 0 Successful operation.
|
||||
* @retval non-zero Failed operation.
|
||||
*/
|
||||
int ( *get_event_info )(
|
||||
rtems_iodev *iodev,
|
||||
rtems_iodev_event_info *event_info
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Call to the device driver to wait on event.
|
||||
*
|
||||
* @param[in] iodev Pointer to iodev device.
|
||||
* @param[in,out] event_args Arguments for wait operation.
|
||||
*
|
||||
* @retval 0 Successful operation.
|
||||
* @retval non-zero Failed operation.
|
||||
*/
|
||||
int ( *event_wait )(
|
||||
rtems_iodev *iodev,
|
||||
rtems_iodev_event_args *event_args
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Call to the device driver to get array of regions.
|
||||
*
|
||||
* @param[in] iodev Pointer to iodev device.
|
||||
* @param[in,out] regions Pointer to array of regions.
|
||||
*
|
||||
* @retval 0 Successful operation.
|
||||
* @retval non-zero Failed operation.
|
||||
*/
|
||||
int ( *get_regions )( rtems_iodev *iodev, rtems_iodev_region **regions );
|
||||
|
||||
/**
|
||||
* @brief Call to the device driver to get number of regions.
|
||||
*
|
||||
* @param[in] iodev Pointer to iodev device.
|
||||
* @param[in,out] region_count Pointer to number of regions.
|
||||
*
|
||||
* @retval 0 Successful operation.
|
||||
* @retval non-zero Failed operation.
|
||||
*/
|
||||
int ( *get_region_count )( rtems_iodev *iodev, size_t *region_count );
|
||||
|
||||
/**
|
||||
* @brief Destroys the iodev device.
|
||||
*
|
||||
* @param[in] iodev Pointer to iodev device.
|
||||
*/
|
||||
void ( *destroy )( rtems_iodev *iodev );
|
||||
|
||||
/**
|
||||
* @brief Callback to destroy private data not owned directly by the iodev
|
||||
* framework.
|
||||
*
|
||||
* @param[in] iodev Pointer to iodev device.
|
||||
*/
|
||||
void ( *priv_destroy )( rtems_iodev *iodev );
|
||||
|
||||
/**
|
||||
* @brief Pointer to device driver.
|
||||
*/
|
||||
void *driver;
|
||||
|
||||
/**
|
||||
* @brief Mutex to protect the iodev device access.
|
||||
*/
|
||||
rtems_recursive_mutex mutex;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Allocate and initialize the iodev device.
|
||||
*
|
||||
* After a successful allocation and initialization of the iodev device
|
||||
* it must be destroyed via rtems_iodev_destroy_unregistered().
|
||||
*
|
||||
* @param[in] size The number of bytes to allocate.
|
||||
*
|
||||
* @retval NULL Failed to set up iodev device.
|
||||
* @retval non-NULL The new iodev device.
|
||||
*/
|
||||
rtems_iodev *rtems_iodev_alloc_and_init( size_t size );
|
||||
|
||||
/**
|
||||
* @brief Initialize the iodev device.
|
||||
*
|
||||
* After a successful initialization of the iodev device it must be
|
||||
* destroyed via rtems_iodev_destroy_unregistered().
|
||||
*
|
||||
* @param[in] iodev The iodev device to initialize.
|
||||
*
|
||||
* @retval non-zero Failed to set up iodev device.
|
||||
* @retval 0 Successful set up of iodev device.
|
||||
*/
|
||||
int rtems_iodev_init( rtems_iodev *iodev );
|
||||
|
||||
/**
|
||||
* @brief Register the iodev device.
|
||||
*
|
||||
* This function always claims ownership of the iodev device passed to it. Once
|
||||
* successfully registered, the iodev device can only be destroyed by
|
||||
* unregistering it.
|
||||
*
|
||||
* After initialization and before registration rtems_iodev device driver
|
||||
* functions need to be set.
|
||||
*
|
||||
* @param[in] iodev The iodev device.
|
||||
* @param[in] iodev_path The path to the iodev device file.
|
||||
*
|
||||
* @retval 0 Successful operation.
|
||||
* @retval non-zero Failed operation.
|
||||
*/
|
||||
int rtems_iodev_register( rtems_iodev *iodev, const char *iodev_path );
|
||||
|
||||
/**
|
||||
* @brief Unregister and destroy the iodev device.
|
||||
*
|
||||
* @param[in] iodev The iodev device.
|
||||
*
|
||||
* @retval 0 Successful operation.
|
||||
* @retval non-zero Failed operation.
|
||||
*/
|
||||
int rtems_iodev_unregister_and_destroy( const char *iodev_path );
|
||||
|
||||
/**
|
||||
* @brief Destroys the iodev device. Frees the iodev if initialised with
|
||||
* rtems_iodev_alloc_and_init.
|
||||
*
|
||||
* This function must only be used on rtems_iodev instances that have not been
|
||||
* successfully registered with rtems_iodev_register.
|
||||
*
|
||||
* @param[in] iodev The iodev device.
|
||||
*/
|
||||
void rtems_iodev_destroy_unregistered( rtems_iodev *iodev );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
||||
#endif /* _DEV_IODEV_H */
|
||||
@@ -71,6 +71,9 @@ install:
|
||||
- destination: ${BSP_INCLUDEDIR}/dev/flash
|
||||
source:
|
||||
- cpukit/include/dev/flash/flashdev.h
|
||||
- destination: ${BSP_INCLUDEDIR}/dev/io
|
||||
source:
|
||||
- cpukit/include/dev/io/iodev.h
|
||||
- destination: ${BSP_INCLUDEDIR}/linux
|
||||
source:
|
||||
- cpukit/include/linux/i2c-dev.h
|
||||
@@ -571,6 +574,7 @@ source:
|
||||
- cpukit/dev/i2c/xilinx-axi-i2c.c
|
||||
- cpukit/dev/ioprintf.c
|
||||
- cpukit/dev/iorelax.c
|
||||
- cpukit/dev/io/iodev.c
|
||||
- cpukit/dev/serial/sc16is752-spi.c
|
||||
- cpukit/dev/serial/sc16is752.c
|
||||
- cpukit/dev/spi/spi-bus.c
|
||||
|
||||
@@ -174,6 +174,8 @@ links:
|
||||
uid: iconvclose
|
||||
- role: build-dependency
|
||||
uid: iconvopen
|
||||
- role: build-dependency
|
||||
uid: iodev01
|
||||
- role: build-dependency
|
||||
uid: irqs01
|
||||
- role: build-dependency
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
|
||||
build-type: test-program
|
||||
cflags: []
|
||||
copyrights:
|
||||
- Copyright (C) 2026 Aaron Nyholm
|
||||
cppflags: []
|
||||
cxxflags: []
|
||||
enabled-by: true
|
||||
features: c cprogram
|
||||
includes: []
|
||||
ldflags: []
|
||||
links: []
|
||||
source:
|
||||
- testsuites/libtests/iodev01/init.c
|
||||
- testsuites/libtests/iodev01/test_iodev.c
|
||||
stlib: []
|
||||
target: testsuites/libtests/iodev01.exe
|
||||
type: build
|
||||
use-after: []
|
||||
use-before: []
|
||||
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (C) 2026 Aaron Nyholm
|
||||
*
|
||||
* 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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 "tmacros.h"
|
||||
|
||||
#include <rtems/libcsupport.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "test_iodev.h"
|
||||
|
||||
#define TEST_NAME_LENGTH 10
|
||||
|
||||
#define IODEV_PATH "/dev/iodev0"
|
||||
|
||||
const char rtems_test_name[] = "IODEV 1";
|
||||
|
||||
static void run_test( void );
|
||||
|
||||
static void assert_timing( struct timespec *start_time, int expected_secs )
|
||||
{
|
||||
struct timespec end_time;
|
||||
struct timespec duration;
|
||||
|
||||
clock_gettime( CLOCK_REALTIME, &end_time );
|
||||
_Timespec_Subtract( start_time, &end_time, &duration );
|
||||
rtems_test_assert( duration.tv_sec >= expected_secs - 1 );
|
||||
rtems_test_assert( duration.tv_sec <= expected_secs );
|
||||
}
|
||||
|
||||
static void run_test( void )
|
||||
{
|
||||
rtems_iodev *iodev;
|
||||
rtems_resource_snapshot snapshot;
|
||||
int status;
|
||||
FILE *file;
|
||||
int fd;
|
||||
size_t count;
|
||||
rtems_iodev_region region;
|
||||
rtems_iodev_event_info event;
|
||||
rtems_iodev_event_args event_args;
|
||||
size_t i;
|
||||
void *map_addr;
|
||||
struct timespec start_time;
|
||||
struct test_iodev_dev_info test_dev_info;
|
||||
|
||||
/* Check resource usage on creation and deletion */
|
||||
rtems_resource_snapshot_take( &snapshot );
|
||||
|
||||
iodev = test_iodev_init();
|
||||
rtems_test_assert( iodev != NULL );
|
||||
|
||||
rtems_iodev_destroy_unregistered( iodev );
|
||||
iodev = NULL;
|
||||
|
||||
rtems_test_assert( rtems_resource_snapshot_check( &snapshot ) );
|
||||
|
||||
/* Check resource usage on registration */
|
||||
rtems_resource_snapshot_take( &snapshot );
|
||||
|
||||
iodev = test_iodev_init();
|
||||
rtems_test_assert( iodev != NULL );
|
||||
|
||||
status = rtems_iodev_register( iodev, IODEV_PATH );
|
||||
rtems_test_assert( !status );
|
||||
|
||||
status = rtems_iodev_unregister_and_destroy( IODEV_PATH );
|
||||
rtems_test_assert( !status );
|
||||
|
||||
rtems_test_assert( rtems_resource_snapshot_check( &snapshot ) );
|
||||
|
||||
/* Initialize the iodev device driver and iodev */
|
||||
iodev = test_iodev_init();
|
||||
rtems_test_assert( iodev != NULL );
|
||||
|
||||
/* Register the iodev as a device */
|
||||
status = rtems_iodev_register( iodev, IODEV_PATH );
|
||||
rtems_test_assert( !status );
|
||||
|
||||
file = fopen( IODEV_PATH, "r+" );
|
||||
rtems_test_assert( file != NULL );
|
||||
|
||||
fd = fileno( file );
|
||||
|
||||
status = ioctl( fd, RTEMS_IODEV_IOCTL_DEVICE_INFO, &test_dev_info );
|
||||
rtems_test_assert( !status );
|
||||
rtems_test_assert( test_dev_info.id == TEST_IODEV_TEST_DEV_ID );
|
||||
|
||||
/* Test valid regions */
|
||||
status = ioctl( fd, RTEMS_IODEV_IOCTL_REGION_COUNT, &count );
|
||||
rtems_test_assert( !status );
|
||||
rtems_test_assert( count == TEST_IODEV_REGION_COUNT );
|
||||
|
||||
for ( i = 0; i < count; i++ ) {
|
||||
region.index = i;
|
||||
region.address = NULL;
|
||||
region.size = 0;
|
||||
region.name = NULL;
|
||||
status = ioctl( fd, RTEMS_IODEV_IOCTL_REGION_GET, ®ion );
|
||||
rtems_test_assert( !status );
|
||||
rtems_test_assert( region.size == TEST_IODEV_REGION_SIZE );
|
||||
rtems_test_assert( region.address != NULL );
|
||||
rtems_test_assert( region.name != NULL );
|
||||
|
||||
map_addr = mmap(
|
||||
NULL,
|
||||
region.size,
|
||||
( PROT_READ | PROT_WRITE ),
|
||||
MAP_SHARED,
|
||||
fd,
|
||||
region.index
|
||||
);
|
||||
rtems_test_assert( map_addr != MAP_FAILED );
|
||||
rtems_test_assert( map_addr == region.address );
|
||||
|
||||
status = munmap( map_addr, region.size );
|
||||
rtems_test_assert( !status );
|
||||
}
|
||||
|
||||
/* Test invalid region failure */
|
||||
map_addr = mmap(
|
||||
NULL,
|
||||
region.size,
|
||||
( PROT_READ | PROT_WRITE ),
|
||||
MAP_SHARED,
|
||||
fd,
|
||||
TEST_IODEV_REGION_COUNT
|
||||
);
|
||||
rtems_test_assert( map_addr == MAP_FAILED );
|
||||
|
||||
/* Test event info */
|
||||
status = ioctl( fd, RTEMS_IODEV_IOCTL_EVENT_COUNT, &count );
|
||||
rtems_test_assert( !status );
|
||||
rtems_test_assert( count == TEST_IODEV_EVENT_COUNT );
|
||||
|
||||
for ( i = 0; i < count; i++ ) {
|
||||
event.index = i;
|
||||
status = ioctl( fd, RTEMS_IODEV_IOCTL_EVENT_INFO, &event );
|
||||
rtems_test_assert( !status );
|
||||
rtems_test_assert( event.name != NULL );
|
||||
rtems_test_assert( event.index == i );
|
||||
}
|
||||
|
||||
/* Test TEST_IODEV_EVENT_SECS second event */
|
||||
event_args.index = 0;
|
||||
event_args.timeout.tv_sec = 0;
|
||||
event_args.timeout.tv_nsec = 0;
|
||||
event_args.args = NULL;
|
||||
|
||||
clock_gettime( CLOCK_REALTIME, &start_time );
|
||||
|
||||
status = ioctl( fd, RTEMS_IODEV_IOCTL_EVENT_WAIT, &event_args );
|
||||
|
||||
rtems_test_assert( !status );
|
||||
assert_timing( &start_time, TEST_IODEV_EVENT_SECS );
|
||||
rtems_test_assert( event_args.timedout == false );
|
||||
|
||||
/* Test large timeout */
|
||||
event_args.index = 0;
|
||||
event_args.timeout.tv_sec = 8;
|
||||
event_args.timeout.tv_nsec = 0;
|
||||
event_args.args = NULL;
|
||||
|
||||
clock_gettime( CLOCK_REALTIME, &start_time );
|
||||
|
||||
status = ioctl( fd, RTEMS_IODEV_IOCTL_EVENT_WAIT, &event_args );
|
||||
|
||||
rtems_test_assert( !status );
|
||||
assert_timing( &start_time, TEST_IODEV_EVENT_SECS );
|
||||
rtems_test_assert( event_args.timedout == false );
|
||||
|
||||
event_args.index = 1;
|
||||
event_args.timeout.tv_sec = 8;
|
||||
event_args.timeout.tv_nsec = 0;
|
||||
event_args.args = NULL;
|
||||
|
||||
clock_gettime( CLOCK_REALTIME, &start_time );
|
||||
|
||||
status = ioctl( fd, RTEMS_IODEV_IOCTL_EVENT_WAIT, &event_args );
|
||||
|
||||
rtems_test_assert( !status );
|
||||
assert_timing( &start_time, 8 );
|
||||
rtems_test_assert( event_args.timedout == true );
|
||||
|
||||
/* Test timeout feature */
|
||||
event_args.index = 0;
|
||||
event_args.timeout.tv_sec = 2;
|
||||
event_args.timeout.tv_nsec = 0;
|
||||
event_args.args = NULL;
|
||||
|
||||
clock_gettime( CLOCK_REALTIME, &start_time );
|
||||
|
||||
status = ioctl( fd, RTEMS_IODEV_IOCTL_EVENT_WAIT, &event_args );
|
||||
|
||||
rtems_test_assert( !status );
|
||||
assert_timing( &start_time, 2 );
|
||||
rtems_test_assert( event_args.timedout == true );
|
||||
|
||||
/* Test invalid event */
|
||||
event_args.index = TEST_IODEV_EVENT_COUNT;
|
||||
event_args.timeout.tv_sec = 2;
|
||||
event_args.timeout.tv_nsec = 0;
|
||||
event_args.args = NULL;
|
||||
|
||||
status = ioctl( fd, RTEMS_IODEV_IOCTL_EVENT_WAIT, &event_args );
|
||||
rtems_test_assert( status );
|
||||
|
||||
/* Test MAP_PRIVATE failure */
|
||||
map_addr =
|
||||
mmap( NULL, region.size, ( PROT_READ | PROT_WRITE ), MAP_PRIVATE, fd, 0 );
|
||||
rtems_test_assert( map_addr == MAP_FAILED );
|
||||
|
||||
/* Test MAP_FIXED failure */
|
||||
map_addr =
|
||||
mmap( NULL, region.size, ( PROT_READ | PROT_WRITE ), MAP_FIXED, fd, 0 );
|
||||
rtems_test_assert( map_addr == MAP_FAILED );
|
||||
|
||||
/* Test PROT_READ and PROT_WRITE requirement */
|
||||
map_addr = mmap( NULL, region.size, ( PROT_READ ), MAP_SHARED, fd, 0 );
|
||||
rtems_test_assert( map_addr == MAP_FAILED );
|
||||
|
||||
map_addr = mmap( NULL, region.size, ( PROT_WRITE ), MAP_SHARED, fd, 0 );
|
||||
rtems_test_assert( map_addr == MAP_FAILED );
|
||||
|
||||
map_addr = mmap( NULL, region.size, ( PROT_NONE ), MAP_SHARED, fd, 0 );
|
||||
rtems_test_assert( map_addr == MAP_FAILED );
|
||||
|
||||
map_addr = mmap( NULL, region.size, ( PROT_EXEC ), MAP_SHARED, fd, 0 );
|
||||
rtems_test_assert( map_addr == MAP_FAILED );
|
||||
|
||||
map_addr = mmap(
|
||||
NULL,
|
||||
region.size,
|
||||
( PROT_READ | PROT_WRITE | PROT_EXEC ),
|
||||
MAP_SHARED,
|
||||
fd,
|
||||
0
|
||||
);
|
||||
rtems_test_assert( map_addr == MAP_FAILED );
|
||||
|
||||
status = fclose( file );
|
||||
rtems_test_assert( !status );
|
||||
|
||||
status = rtems_iodev_unregister_and_destroy( IODEV_PATH );
|
||||
rtems_test_assert( !status );
|
||||
}
|
||||
|
||||
static void Init( rtems_task_argument arg )
|
||||
{
|
||||
(void) arg;
|
||||
|
||||
TEST_BEGIN();
|
||||
|
||||
run_test();
|
||||
|
||||
TEST_END();
|
||||
rtems_test_exit( 0 );
|
||||
}
|
||||
|
||||
#define CONFIGURE_MICROSECONDS_PER_TICK 2000
|
||||
|
||||
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
|
||||
|
||||
#define CONFIGURE_MAXIMUM_FILE_DESCRIPTORS 7
|
||||
|
||||
#define CONFIGURE_MAXIMUM_TASKS 2
|
||||
|
||||
#define CONFIGURE_MAXIMUM_SEMAPHORES 1
|
||||
|
||||
#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
|
||||
|
||||
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
|
||||
|
||||
#define CONFIGURE_INIT
|
||||
|
||||
#include <rtems/confdefs.h>
|
||||
@@ -0,0 +1,15 @@
|
||||
This file describes the directives and concepts tested by this test set.
|
||||
|
||||
test set name: iodev01
|
||||
|
||||
directives:
|
||||
|
||||
- rtems_iodev_alloc_and_init
|
||||
- rtems_iodev_init
|
||||
- rtems_iodev_register
|
||||
- rtems_iodev_unregister_and_destroy
|
||||
- rtems_iodev_destroy_unregistered
|
||||
|
||||
concepts:
|
||||
|
||||
- Ensure that the iodev driver API works.
|
||||
@@ -0,0 +1,2 @@
|
||||
*** BEGIN OF TEST IODEV 1 ***
|
||||
*** END OF TEST IODEV 1 ***
|
||||
@@ -0,0 +1,275 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
|
||||
/*
|
||||
* Copyright (C) 2026 Aaron Nyholm
|
||||
*
|
||||
* 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <rtems.h>
|
||||
#include <rtems/seterr.h>
|
||||
#include <rtems/timespec.h>
|
||||
|
||||
#include "test_iodev.h"
|
||||
|
||||
#define TIMED_RETURN_SEC TEST_IODEV_EVENT_SECS
|
||||
|
||||
typedef struct test_iodev {
|
||||
rtems_iodev *iodev;
|
||||
rtems_iodev_region regions[ TEST_IODEV_REGION_COUNT ];
|
||||
rtems_id server_id;
|
||||
rtems_id client_id;
|
||||
rtems_mutex lock;
|
||||
bool waiting;
|
||||
} test_iodev;
|
||||
|
||||
static test_iodev tiodev;
|
||||
|
||||
int test_iodev_get_device_info( rtems_iodev *iodev, void *event_info );
|
||||
|
||||
int test_iodev_get_event_info(
|
||||
rtems_iodev *iodev,
|
||||
struct rtems_iodev_event_info *event_info
|
||||
);
|
||||
|
||||
int test_iodev_event_wait(
|
||||
rtems_iodev *iodev,
|
||||
struct rtems_iodev_event_args *event_args
|
||||
);
|
||||
|
||||
int test_iodev_get_regions( rtems_iodev *iodev, rtems_iodev_region **regions );
|
||||
|
||||
int test_iodev_get_region_count( rtems_iodev *iodev, size_t *region_count );
|
||||
|
||||
int test_iodev_get_event_count( rtems_iodev *iodev, size_t *event_count );
|
||||
|
||||
void test_iodev_priv_destroy( rtems_iodev *iodev );
|
||||
|
||||
static void test_iodev_server()
|
||||
{
|
||||
rtems_status_code sc;
|
||||
rtems_event_set event_in;
|
||||
|
||||
for ( ;; ) {
|
||||
sc = rtems_event_receive(
|
||||
RTEMS_EVENT_0,
|
||||
RTEMS_WAIT,
|
||||
RTEMS_NO_TIMEOUT,
|
||||
&event_in
|
||||
);
|
||||
if ( sc != RTEMS_SUCCESSFUL ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
rtems_task_wake_after(
|
||||
RTEMS_MILLISECONDS_TO_TICKS( 1000 * TIMED_RETURN_SEC )
|
||||
);
|
||||
|
||||
rtems_mutex_lock( &tiodev.lock );
|
||||
if ( tiodev.waiting ) {
|
||||
rtems_event_send( tiodev.client_id, RTEMS_EVENT_0 );
|
||||
}
|
||||
rtems_mutex_unlock( &tiodev.lock );
|
||||
}
|
||||
|
||||
rtems_task_exit();
|
||||
}
|
||||
|
||||
int test_iodev_get_device_info( rtems_iodev *iodev, void *event_info )
|
||||
{
|
||||
(void) iodev;
|
||||
struct test_iodev_dev_info *info;
|
||||
|
||||
info = event_info;
|
||||
info->id = TEST_IODEV_TEST_DEV_ID;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_iodev_get_event_info(
|
||||
rtems_iodev *iodev,
|
||||
struct rtems_iodev_event_info *event_info
|
||||
)
|
||||
{
|
||||
(void) iodev;
|
||||
|
||||
if ( event_info->index == 0 ) {
|
||||
event_info->name = "timed_return";
|
||||
event_info->args = NULL;
|
||||
} else if ( event_info->index == 1 ) {
|
||||
event_info->name = "noreturn";
|
||||
event_info->args = NULL;
|
||||
} else {
|
||||
rtems_set_errno_and_return_minus_one( EINVAL );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_iodev_event_wait(
|
||||
rtems_iodev *iodev,
|
||||
struct rtems_iodev_event_args *event_args
|
||||
)
|
||||
{
|
||||
(void) iodev;
|
||||
|
||||
rtems_status_code sc;
|
||||
rtems_event_set event_in;
|
||||
rtems_interval timeout;
|
||||
rtems_event_set event_out;
|
||||
|
||||
if ( event_args->index == 0 ) {
|
||||
event_in = RTEMS_EVENT_0;
|
||||
rtems_event_send( tiodev.server_id, RTEMS_EVENT_0 );
|
||||
} else if ( event_args->index == 1 ) {
|
||||
event_in = RTEMS_EVENT_1;
|
||||
} else {
|
||||
rtems_set_errno_and_return_minus_one( EINVAL );
|
||||
}
|
||||
|
||||
if ( event_args->timeout.tv_sec == 0 && event_args->timeout.tv_nsec == 0 ) {
|
||||
timeout = RTEMS_NO_TIMEOUT;
|
||||
} else {
|
||||
timeout = rtems_timespec_to_ticks( &event_args->timeout );
|
||||
}
|
||||
|
||||
rtems_mutex_lock( &tiodev.lock );
|
||||
tiodev.client_id = rtems_task_self();
|
||||
tiodev.waiting = true;
|
||||
rtems_mutex_unlock( &tiodev.lock );
|
||||
|
||||
sc = rtems_event_receive( event_in, RTEMS_WAIT, timeout, &event_out );
|
||||
|
||||
rtems_mutex_lock( &tiodev.lock );
|
||||
tiodev.waiting = false;
|
||||
rtems_mutex_unlock( &tiodev.lock );
|
||||
|
||||
event_args->timedout = ( sc == RTEMS_TIMEOUT );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_iodev_get_regions( rtems_iodev *iodev, rtems_iodev_region **regions )
|
||||
{
|
||||
(void) iodev;
|
||||
|
||||
*regions = tiodev.regions;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_iodev_get_region_count( rtems_iodev *iodev, size_t *region_count )
|
||||
{
|
||||
(void) iodev;
|
||||
|
||||
*region_count = TEST_IODEV_REGION_COUNT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_iodev_get_event_count( rtems_iodev *iodev, size_t *event_count )
|
||||
{
|
||||
(void) iodev;
|
||||
|
||||
*event_count = TEST_IODEV_EVENT_COUNT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void test_iodev_priv_destroy( rtems_iodev *iodev )
|
||||
{
|
||||
(void) iodev;
|
||||
int i;
|
||||
|
||||
for ( i = 0; i < TEST_IODEV_REGION_COUNT; i++ ) {
|
||||
free( tiodev.regions[ i ].address );
|
||||
}
|
||||
|
||||
rtems_task_delete( tiodev.server_id );
|
||||
rtems_mutex_destroy( &tiodev.lock );
|
||||
}
|
||||
|
||||
rtems_iodev *test_iodev_init( void )
|
||||
{
|
||||
rtems_status_code sc;
|
||||
|
||||
tiodev.iodev = rtems_iodev_alloc_and_init( sizeof( rtems_iodev ) );
|
||||
|
||||
if ( tiodev.iodev == NULL ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rtems_mutex_init( &tiodev.lock, "test_iodev" );
|
||||
rtems_mutex_lock( &tiodev.lock );
|
||||
tiodev.waiting = false;
|
||||
rtems_mutex_unlock( &tiodev.lock );
|
||||
|
||||
tiodev.regions[ 0 ].index = 0;
|
||||
tiodev.regions[ 0 ].address = malloc( TEST_IODEV_REGION_SIZE );
|
||||
if ( tiodev.regions[ 0 ].address == NULL ) {
|
||||
rtems_iodev_destroy_unregistered( tiodev.iodev );
|
||||
return NULL;
|
||||
}
|
||||
tiodev.regions[ 0 ].size = TEST_IODEV_REGION_SIZE;
|
||||
tiodev.regions[ 0 ].name = "iodev_region_zero";
|
||||
|
||||
tiodev.regions[ 1 ].index = 1;
|
||||
tiodev.regions[ 1 ].address = malloc( TEST_IODEV_REGION_SIZE );
|
||||
if ( tiodev.regions[ 1 ].address == NULL ) {
|
||||
free( tiodev.regions[ 0 ].address );
|
||||
rtems_iodev_destroy_unregistered( tiodev.iodev );
|
||||
return NULL;
|
||||
}
|
||||
tiodev.regions[ 1 ].size = TEST_IODEV_REGION_SIZE;
|
||||
tiodev.regions[ 1 ].name = "iodev_region_one";
|
||||
|
||||
sc = rtems_task_create(
|
||||
rtems_build_name( 'T', 'I', 'O', 'S' ),
|
||||
100,
|
||||
RTEMS_MINIMUM_STACK_SIZE,
|
||||
RTEMS_DEFAULT_MODES,
|
||||
RTEMS_DEFAULT_ATTRIBUTES,
|
||||
&tiodev.server_id
|
||||
);
|
||||
if ( sc != RTEMS_SUCCESSFUL ) {
|
||||
free( tiodev.regions[ 0 ].address );
|
||||
free( tiodev.regions[ 1 ].address );
|
||||
rtems_iodev_destroy_unregistered( tiodev.iodev );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sc = rtems_task_start(
|
||||
tiodev.server_id,
|
||||
(rtems_task_entry) test_iodev_server,
|
||||
(rtems_task_argument) NULL
|
||||
);
|
||||
|
||||
tiodev.iodev->get_device_info = test_iodev_get_device_info;
|
||||
tiodev.iodev->event_wait = test_iodev_event_wait;
|
||||
tiodev.iodev->get_regions = test_iodev_get_regions;
|
||||
tiodev.iodev->get_region_count = test_iodev_get_region_count;
|
||||
tiodev.iodev->get_event_count = test_iodev_get_event_count;
|
||||
tiodev.iodev->get_event_info = test_iodev_get_event_info;
|
||||
tiodev.iodev->priv_destroy = test_iodev_priv_destroy;
|
||||
|
||||
return tiodev.iodev;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
|
||||
/*
|
||||
* Copyright (C) 2026 Aaron Nyholm
|
||||
*
|
||||
* 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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.
|
||||
*/
|
||||
|
||||
#ifndef __TEST_IODEV_H
|
||||
#define __TEST_IODEV_H
|
||||
|
||||
#include <dev/io/iodev.h>
|
||||
|
||||
#define TEST_IODEV_REGION_COUNT 2
|
||||
#define TEST_IODEV_REGION_SIZE 0x1000
|
||||
#define TEST_IODEV_EVENT_COUNT 2
|
||||
|
||||
#define TEST_IODEV_TEST_DEV_ID 0x12345678
|
||||
|
||||
#define TEST_IODEV_EVENT_SECS 5
|
||||
|
||||
struct test_iodev_dev_info {
|
||||
uint32_t id;
|
||||
};
|
||||
|
||||
rtems_iodev *test_iodev_init( void );
|
||||
|
||||
#endif /* __TEST_IODEV_H */
|
||||
Reference in New Issue
Block a user