Files
rtems/cpukit/libmisc/regulator/regulator.c
T
Sebastian Huber b4d322971c cpukit: Group the last ungrouped files
Seventy-three files had a Doxygen block but no @ingroup line.  Doxygen
documented them, but no group page listed them.  The PCI headers, the
driver manager headers and 46 headers under include/rtems were affected.

Give each file the group of the component it belongs to.  The seven
default file system files follow the table they fill.  A file which
provides an entry of rtems_filesystem_operations_table joins LibIOFSOps.
A file which provides an entry of rtems_filesystem_file_handlers_r joins
LibIOFSHandler.

Every file in cpukit with a Doxygen block names a group.  The files
malloc.h, todimpl.h and printk_plugin.c name two groups.  The other
files name one.

Updates #5717.

Assisted-by: Claude:claude-opus-5 claude-code
Signed-off-by: Sebastian Huber <sebastian.huber@embedded-brains.de>
2026-09-01 20:17:20 +00:00

684 lines
19 KiB
C

/* SPDX-License-Identifier: BSD-2-Clause */
/**
* @file
*
* @ingroup RegulatorInternalAPI
*
* @brief Regulator Library Implementation
*/
/*
* Copyright (C) 2022 On-Line Applications Research Corporation (OAR)
*
* 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 <rtems.h>
#include <rtems/regulator.h>
#include <string.h>
#include <rtems/regulatorimpl.h>
/**
* @ingroup RegulatorInternalAPI
*
* This method is the body for the task which delivers the output for
* this regulator instance at the configured rate.
*
* @param[in] arg points to the regulator instance this thread
* is associated with
*
* @note The argument passed in cannot be NULL if the
* rtems_regulator_create worked.
*/
static rtems_task _Regulator_Output_task_body( rtems_task_argument arg )
{
_Regulator_Control *the_regulator = (_Regulator_Control *) arg;
rtems_status_code sc;
size_t to_dequeue;
_Regulator_Message_t regulator_message;
size_t regulator_message_size;
bool release_it;
the_regulator->delivery_thread_is_running = true;
/**
* This thread uses a rate monotonic period object instance. A rate
* monotonic period object must be created by the thread using it.
* It can be deleted by any thread which simplifies clean up.
*
* The rate_monotonic_create() call can fail if the application
* is incorrectly configured. This thread has no way to report the
* failure. If it continues with an invalid id, then the thread will
* not block on the period and spin continuously consuming CPU. The only
* alternatives are to invoke rtems_fatal_error_occurred() or silently
* exit the thread.
*/
sc = rtems_rate_monotonic_create(
rtems_build_name( 'P', 'E', 'R', 'D' ),
&the_regulator->delivery_thread_period_id
);
if ( sc != RTEMS_SUCCESSFUL ) {
goto exit_delivery_thread;
}
/**
* Loop on the rate_monotonic_period() based on the specified period.
*/
while ( 1 ) {
sc = rtems_rate_monotonic_period(
the_regulator->delivery_thread_period_id,
the_regulator->Attributes.delivery_thread_period
);
_Assert_Unused_variable_equals( sc, RTEMS_SUCCESSFUL );
/**
* If the delivery thread has been requested to exit, then
* quit processing messages, break out of this loop, and exit
* this thread.
*/
if ( the_regulator->delivery_thread_request_exit ) {
break;
}
/**
* Loop for the configured number of messages to deliver per period.
* If we reach the point, there are no more messages, block for the
* rest of this period. If there are messages, deliver them.
*/
for (
to_dequeue = 0;
to_dequeue < the_regulator->Attributes.maximum_to_dequeue_per_period;
to_dequeue++
) {
regulator_message_size = sizeof( _Regulator_Message_t );
sc = rtems_message_queue_receive(
the_regulator->queue_id,
&regulator_message,
&regulator_message_size,
RTEMS_NO_WAIT,
0
);
_Assert_Unused_variable_equals( sc, RTEMS_SUCCESSFUL );
if ( sc != RTEMS_SUCCESSFUL ) {
break;
}
release_it = the_regulator->Attributes.deliverer(
the_regulator->Attributes.deliverer_context,
regulator_message.buffer,
regulator_message.length
);
the_regulator->Statistics.delivered++;
/**
* The message was successfully delivered. If the delivery function
* wants the buffer returned, do it now. The delivery to the Destination
* may involve handing the buffer off to something like DMA
* and need to wait for it to complete before releasing the buffer.
*
* Note that this is the underlying RTEMS service
* used by @a rtems_regulator_obtain_buffer() and @a
* rtems_regulator_release_buffer().
*/
if ( release_it == true ) {
the_regulator->Statistics.released++;
sc = rtems_partition_return_buffer(
the_regulator->messages_partition_id,
regulator_message.buffer
);
_Assert_Unused_variable_equals( sc, RTEMS_SUCCESSFUL );
}
}
}
/**
* This thread was requested to exit. Do so.
*/
exit_delivery_thread:
the_regulator->delivery_thread_is_running = false;
the_regulator->delivery_thread_has_exited = true;
(void) rtems_rate_monotonic_delete(
the_regulator->delivery_thread_period_id
);
rtems_task_exit();
}
/**
* @ingroup RegulatorInternalAPI
*
* This method frees the resources associated with a regulator instance.
* The resources are freed in the opposite of the order in which they are
* allocated. This is used on error cases in @a rtems_regulator_create() and in
* @a rtems_regulator_delete().
*
* @param[in] the_regulator is the instance to operate upon
* @param[in] ticks is the length of time to wait for the delivery thread
* to exit
*
* @return This method returns true is successful and false on timeout.
*/
static bool _Regulator_Free_helper(
_Regulator_Control *the_regulator,
rtems_interval ticks
)
{
rtems_status_code sc;
/*
* If the output thread has not started running, then we can just delete it.
*/
if ( ticks == 0 || the_regulator->delivery_thread_is_running == false ) {
sc = rtems_task_delete( the_regulator->delivery_thread_id );
_Assert_Unused_variable_equals( sc, RTEMS_SUCCESSFUL );
} else {
rtems_interval remaining = ticks;
the_regulator->delivery_thread_request_exit = true;
while ( 1 ) {
if ( the_regulator->delivery_thread_has_exited ) {
break;
}
if ( remaining == 0 ) {
return false;
}
(void) rtems_task_wake_after( 1 );
remaining--;
}
}
/*
* The output thread deletes the rate monotonic period that it created.
*/
/*
* The regulator's message_queue_storage is implicitly freed by this call.
*/
sc = rtems_message_queue_delete( the_regulator->queue_id );
_Assert_Unused_variable_equals( sc, RTEMS_SUCCESSFUL );
sc = rtems_partition_delete( the_regulator->messages_partition_id );
_Assert_Unused_variable_equals( sc, RTEMS_SUCCESSFUL );
if ( the_regulator->message_memory ) {
free( the_regulator->message_memory );
}
the_regulator->initialized = 0;
free( the_regulator );
return true;
}
/**
* @ingroup RegulatorInternalAPI
*/
rtems_status_code rtems_regulator_create(
rtems_regulator_attributes *attributes,
rtems_regulator_instance **regulator
)
{
_Regulator_Control *the_regulator;
rtems_status_code sc;
size_t alloc_size;
/**
* Perform basic validation of parameters
*/
if ( attributes == NULL ) {
return RTEMS_INVALID_ADDRESS;
}
if ( regulator == NULL ) {
return RTEMS_INVALID_ADDRESS;
}
/**
* Verify attributes are OK. Some are checked by calls to object create
* methods. Specifically the following are not checked:
*
* - delivery_thread_priority by rtems_task_create()
* - delivery_thread_stack_size can be any value
*/
if ( attributes->deliverer == NULL ) {
return RTEMS_INVALID_ADDRESS;
}
if ( attributes->maximum_messages == 0 ) {
return RTEMS_INVALID_NUMBER;
}
if ( attributes->maximum_message_size == 0 ) {
return RTEMS_INVALID_SIZE;
}
if ( attributes->maximum_to_dequeue_per_period == 0 ) {
return RTEMS_INVALID_NUMBER;
}
if ( attributes->delivery_thread_period == 0 ) {
return RTEMS_INVALID_NUMBER;
}
/**
* Allocate memory for regulator instance
*/
the_regulator = (_Regulator_Control *)
calloc( 1, sizeof( _Regulator_Control ) );
if ( the_regulator == NULL ) {
return RTEMS_NO_MEMORY;
}
/**
* We do NOT want the delivery_thread_id field to be initialized to 0. If the
* @a rtems_task_create() fails, then the field will not be overwritten.
* This results in an attempt to rtems_task_delete(0) during clean
* up. The thread ID of 0 is self which results in the calling thread
* accidentally deleting itself.
*/
the_regulator->delivery_thread_id = (rtems_id) -1;
/**
* Copy the attributes to an internal area for later use
*/
the_regulator->Attributes = *attributes;
/**
* Allocate memory for the messages. There is no need to zero out the
* message memory because the user should fill that in.
*/
alloc_size = attributes->maximum_message_size * attributes->maximum_messages;
the_regulator->message_memory = calloc( alloc_size, 1 );
if ( the_regulator->message_memory == NULL ) {
_Regulator_Free_helper( the_regulator, 0 );
return RTEMS_NO_MEMORY;
}
/**
* Associate message memory with a partition so allocations are atomic
*/
sc = rtems_partition_create(
rtems_build_name( 'P', 'O', 'O', 'L' ),
the_regulator->message_memory,
alloc_size,
attributes->maximum_message_size,
RTEMS_DEFAULT_ATTRIBUTES,
&the_regulator->messages_partition_id
);
if ( sc != RTEMS_SUCCESSFUL ) {
_Regulator_Free_helper( the_regulator, 0 );
return sc;
}
/**
* Create the message queue between the sender and output thread
*/
RTEMS_MESSAGE_QUEUE_BUFFER( sizeof( _Regulator_Message_t ) )
regulator_message_t;
size_t storage_size = sizeof( regulator_message_t ) *
attributes->maximum_messages;
the_regulator->message_queue_storage = malloc( storage_size );
if ( the_regulator->message_queue_storage == NULL ) {
_Regulator_Free_helper( the_regulator, 0 );
return RTEMS_NO_MEMORY;
}
rtems_message_queue_config mq_config = {
.name = rtems_build_name( 'S', 'N', 'D', 'Q' ),
.maximum_pending_messages = attributes->maximum_messages,
.maximum_message_size = sizeof( _Regulator_Message_t ),
.storage_area = the_regulator->message_queue_storage,
.storage_size = storage_size,
.storage_free = free,
.attributes = RTEMS_DEFAULT_ATTRIBUTES
};
sc = rtems_message_queue_construct( &mq_config, &the_regulator->queue_id );
if ( sc != RTEMS_SUCCESSFUL ) {
_Regulator_Free_helper( the_regulator, 0 );
return sc;
}
/**
* @note A rate monotonic period object must be created by the thread
* using it. Thus that specific create operation is not included
* in this method. All other resources are allocated here.
*/
/**
* Create the output thread Using the priority and stack size attributes
* specified by the user.
*/
sc = rtems_task_create(
rtems_build_name( 'R', 'E', 'G', 'U' ),
attributes->delivery_thread_priority,
attributes->delivery_thread_stack_size,
RTEMS_DEFAULT_MODES,
RTEMS_DEFAULT_ATTRIBUTES,
&the_regulator->delivery_thread_id
);
if ( sc != RTEMS_SUCCESSFUL ) {
_Regulator_Free_helper( the_regulator, 0 );
return sc;
}
/**
* Start the output thread.
*
* @note There should be no way this call can fail. The task id is valid,
* the regulator output thread entry point is valid, and the argument
* is valid.
*/
the_regulator->delivery_thread_is_running = true;
the_regulator->delivery_thread_request_exit = false;
the_regulator->delivery_thread_has_exited = false;
sc = rtems_task_start(
the_regulator->delivery_thread_id,
_Regulator_Output_task_body,
(rtems_task_argument) the_regulator
);
_Assert_Unused_variable_equals( sc, RTEMS_SUCCESSFUL );
/**
* The regulator is successfully initialized. Set the initialized field
* to reflect this and return the instance pointer.
*/
the_regulator->initialized = REGULATOR_INITIALIZED;
*regulator = (void *) the_regulator;
return RTEMS_SUCCESSFUL;
}
/**
* @brief Validate the regulator instance provided by the user
*
* Validate the regulator instance provided by the user
*
* @param[in] regulator is the instance provided by the user
* @param[inout] status will contain the RTEMS status for this check
*
* @return This method returns a @a _Regulator_Control instance pointer
* which is NULL if invalid or points to the internal regulator
* control structure if valid.
*/
static inline _Regulator_Control *_Regulator_Get(
rtems_regulator_instance *regulator,
rtems_status_code *status
)
{
_Regulator_Control *the_regulator = (_Regulator_Control *) regulator;
if ( the_regulator == NULL ) {
*status = RTEMS_INVALID_ADDRESS;
return NULL;
}
if ( the_regulator->initialized != REGULATOR_INITIALIZED ) {
*status = RTEMS_INCORRECT_STATE;
return NULL;
}
status = RTEMS_SUCCESSFUL;
return the_regulator;
}
/**
* @ingroup RegulatorInternalAPI
*/
rtems_status_code rtems_regulator_delete(
rtems_regulator_instance *regulator,
rtems_interval ticks
)
{
_Regulator_Control *the_regulator;
rtems_status_code status;
/**
* Convert external handle to internal instance pointer
*/
the_regulator = _Regulator_Get( regulator, &status );
if ( the_regulator == NULL ) {
return status;
}
/**
* There can be no buffers outstanding
*/
_Regulator_Statistics *stats = &the_regulator->Statistics;
if ( stats->obtained != stats->released ) {
return RTEMS_RESOURCE_IN_USE;
}
/**
* Free the resources associated with this regulator instance.
*/
bool bc;
bc = _Regulator_Free_helper( the_regulator, ticks );
if ( bc == false ) {
return RTEMS_TIMEOUT;
}
return RTEMS_SUCCESSFUL;
}
/**
* @ingroup RegulatorInternalAPI
*
* Allocate a buffer for the caller using the internal partition.
*/
rtems_status_code rtems_regulator_obtain_buffer(
rtems_regulator_instance *regulator,
void **buffer
)
{
_Regulator_Control *the_regulator;
rtems_status_code status;
/**
* Convert external handle to internal instance pointer
*/
the_regulator = _Regulator_Get( regulator, &status );
if ( the_regulator == NULL ) {
return status;
}
/**
* Allocate a buffer for the user application from the buffer pool managed
* by an Classic API partition.
*/
status = rtems_partition_get_buffer(
the_regulator->messages_partition_id,
buffer
);
if ( status == RTEMS_SUCCESSFUL ) {
the_regulator->Statistics.obtained++;
}
return status;
}
/**
* @ingroup RegulatorInternalAPI
*
* Allocate a buffer for the caller using the internal partition.
*/
rtems_status_code rtems_regulator_release_buffer(
rtems_regulator_instance *regulator,
void *buffer
)
{
_Regulator_Control *the_regulator;
rtems_status_code status;
/**
* Convert external handle to internal instance pointer
*/
the_regulator = _Regulator_Get( regulator, &status );
if ( the_regulator == NULL ) {
return status;
}
/**
* Deallocate the buffer to the buffer pool managed by a Classic
* API partition.
*/
status = rtems_partition_return_buffer(
the_regulator->messages_partition_id,
buffer
);
if ( status == RTEMS_SUCCESSFUL ) {
the_regulator->Statistics.released++;
}
return status;
}
/**
* @ingroup RegulatorInternalAPI
*/
rtems_status_code rtems_regulator_send(
rtems_regulator_instance *regulator,
void *message,
size_t length
)
{
_Regulator_Control *the_regulator;
rtems_status_code status;
_Regulator_Message_t regulator_message;
the_regulator = (_Regulator_Control *) regulator;
/**
* Validate the arguments and ensure the regulator was successfully
* initialized.
*/
if ( message == NULL ) {
return RTEMS_INVALID_ADDRESS;
}
if ( length == 0 ) {
return RTEMS_INVALID_NUMBER;
}
/**
* Convert external handle to internal instance pointer
*/
the_regulator = _Regulator_Get( regulator, &status );
if ( the_regulator == NULL ) {
return status;
}
/**
* Place the message pointer and length into a temporary structure. This
* lets the implementation internally send the message by reference and
* have a zero-copy implementation.
*/
regulator_message.buffer = message;
regulator_message.length = length;
/**
* Send the application message to the output thread for delivery using
* a Classic API message queue.
*/
status = rtems_message_queue_send(
the_regulator->queue_id,
&regulator_message,
sizeof( _Regulator_Message_t )
);
if ( status != RTEMS_SUCCESSFUL ) {
return status;
}
return status;
}
/**
* @ingroup RegulatorInternalAPI
*/
rtems_status_code rtems_regulator_get_statistics(
rtems_regulator_instance *regulator,
rtems_regulator_statistics *statistics
)
{
_Regulator_Control *the_regulator;
rtems_status_code status;
/**
* Validate the arguments and ensure the regulator was successfully
* initialized.
*/
if ( statistics == NULL ) {
return RTEMS_INVALID_ADDRESS;
}
/**
* Convert external handle to internal instance pointer
*/
the_regulator = _Regulator_Get( regulator, &status );
if ( the_regulator == NULL ) {
return status;
}
/**
* Zero out the statistics structure in case the get period statistics
* fails below.
*/
memset( statistics, 0, sizeof( rtems_regulator_statistics ) );
/**
* Fill in the caller's statistics structure from information
* maintained by the regulator instance about buffers processed.
*/
statistics->obtained = the_regulator->Statistics.obtained;
statistics->released = the_regulator->Statistics.released;
statistics->delivered = the_regulator->Statistics.delivered;
/**
* Attempt to retrieve the delivery thread's period's statistics.
*
* NOTE; If the Delivery Thread has not run yet, the period will not
* exist yet. We should not fail for this reason but it is why
* we zeroed out the entire structure above.
*/
(void) rtems_rate_monotonic_get_statistics(
the_regulator->delivery_thread_period_id,
&statistics->period_statistics
);
return RTEMS_SUCCESSFUL;
}