mirror of
https://gitlab.rtems.org/rtems/rtos/rtems.git
synced 2025-11-16 12:34:45 +00:00
Add the Regulator Interface and test
Updates #4924. The Regulator is an application support class which is used to deal with the scenario where there is a bursty input source which needs to be metered out to a destination sink. The maximum size of bursts needs to be known and the delivery method must be configured to deliver messages at a rate that allows the traffic to not overflow.
This commit is contained in:
502
cpukit/include/rtems/regulator.h
Normal file
502
cpukit/include/rtems/regulator.h
Normal file
@@ -0,0 +1,502 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @ingroup RegulatorAPI
|
||||
*
|
||||
* @brief This header file defines the Regulator API.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup RegulatorAPI Regulator API
|
||||
*
|
||||
* @brief Regulator APIs
|
||||
*
|
||||
* The Regulator provides a set of APIs to manage input sources which
|
||||
* produces bursts of message traffic.
|
||||
*
|
||||
* The regulator is designed to sit logically between two entities -- a
|
||||
* source and a destination, where it limits the traffic sent to the
|
||||
* destination to prevent it from being flooded with messages from the
|
||||
* source. This can be used to accommodate bursts of input from a source
|
||||
* and meter it out to a destination. The maximum number of messages
|
||||
* which can be buffered in the regulator is specified by the
|
||||
* @a maximum_messages field in the @a rtems_regulator_attributes
|
||||
* structure passed as an argument to @a rtems_regulator_create().
|
||||
*
|
||||
* The regulator library accepts an input stream of messages from a
|
||||
* source and delivers them to a destination. The regulator assumes that the
|
||||
* input stream from the source contains sporadic bursts of data which can
|
||||
* exceed the acceptable rate of the destination. By limiting the message rate,
|
||||
* the regulator prevents an overflow of messages.
|
||||
*
|
||||
* The regulator can be configured for the input buffering required to manage
|
||||
* the maximum burst and for the metering rate for the output. The output rate
|
||||
* is in messages per second. If the sender produces data too fast, the
|
||||
* regulator will buffer the configured number of messages.
|
||||
*
|
||||
* A configuration capability is provided to allow for adaptation to different
|
||||
* message streams. The regulator can also support running multiple instances,
|
||||
* which could be used on independent message streams.
|
||||
*
|
||||
* The regulator provides a simple interface to the application for avoiding
|
||||
* bursts of input from a fast source overflowing a slower destination.
|
||||
*
|
||||
* It is assumed that the application has a design limit on the number of
|
||||
* messages which may be buffered. All messages accepted by the regulator,
|
||||
* assuming no overflow on input, will eventually be output by the Delivery
|
||||
* thread.
|
||||
*
|
||||
* A regulator instance is used as follows from the producer/source side:
|
||||
*
|
||||
* @code
|
||||
* while (1)
|
||||
* use rtems_regulator_obtain_buffer to obtain a buffer
|
||||
* input operation to fetch data into the buffer
|
||||
* rtems_regulator_send(buffer, size of message)
|
||||
* @endcode
|
||||
*
|
||||
* The delivery of message buffers to the Destination and subsequent
|
||||
* release is performed in the context of the delivery thread by either
|
||||
* the delivery function or delivery thread. Details are below.
|
||||
*
|
||||
* The sequence diagram below shows the interaction between a message Source,
|
||||
* a Regulator instance, and RTEMS, given the usage described in the above
|
||||
* paragraphs.
|
||||
*
|
||||
* \startuml "Regulator Application Input Source Usage"
|
||||
* Source -> Regulator : rtems_regulator_obtain_buffer(regulator, buffer)
|
||||
* Regulator -> RTEMS : rtems_partition_get_buffer(id, buffer)
|
||||
* RTEMS --> Regulator : rtems_status_code
|
||||
* Regulator --> Source : rtems_status_code
|
||||
* Source -> Regulator : rtems_regulator_send(regulator, message, length)
|
||||
* Regulator -> RTEMS : rtems_message_queue_send(id, message, size)
|
||||
* RTEMS --> Regulator : rtems_status_code
|
||||
* Regulator --> Source : rtems_status_code
|
||||
* \enduml
|
||||
*
|
||||
* As illustrated in the sequence diagram, the Source usually corresponds
|
||||
* to application software reading a system input. The Source obtains a
|
||||
* buffer from the Regulator instance and fills it with incoming data.
|
||||
* The application explicitly obtaining a buffer and filling it in allows
|
||||
* for zero copy operations on the Source side.
|
||||
*
|
||||
* The Source then sends the buffer to the Regulator instance. The Regulator
|
||||
* the sends the buffer via a message queue which to the Delivery thread.
|
||||
* The Delivery thread executes periodically at a rate specified at
|
||||
* Regulation creation. At each period, the Delivery thread attempts to
|
||||
* receive up to a configured number of buffers and invoke the Delivery
|
||||
* function to deliver them to the Destination.
|
||||
*
|
||||
* The Delivery function is provided by the application for this
|
||||
* specific Regulator instance. Depending on the Destination, it may use
|
||||
* a function which copies the buffer contents (e.g., write()) or which
|
||||
* operates directly on the buffer contents (e.g. DMA from buffer). In
|
||||
* the case of a Destination which copies the buffer contents, the buffer
|
||||
* can be released via @a rtems_regulator_release_buffer() as soon as the
|
||||
* function or copying completes. In the case where the delivery uses the
|
||||
* buffer and returns, the call to @a rtems_regulator_release_buffer()
|
||||
* will occur when the use of the buffer is complete (e.g. completion
|
||||
* of DMA transfer). This explicit and deliberate exposure of buffering
|
||||
* provides the application with the ability to avoid copying the contents.
|
||||
*
|
||||
* After the Source has sent the message to the Regulator instance,
|
||||
* the Source is free to process another input and the Regulator
|
||||
* instance will ensure that the buffer is delivered to the Delivery
|
||||
* function and Destination.
|
||||
*
|
||||
* The Regulator implementation uses the RTEMS Classic API Partition Manager
|
||||
* to manage the buffer pool and the RTEMS Classic API Message Queue
|
||||
* Manager to send the buffer to the Delivery thread.
|
||||
*/
|
||||
|
||||
#ifndef REGULATOR_H
|
||||
#define REGULATOR_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <rtems.h>
|
||||
|
||||
/**
|
||||
* @ingroup RegulatorAPI
|
||||
*
|
||||
* @brief Regulator Delivery Function Type
|
||||
*
|
||||
* The user provides a function which is invoked to deliver a message
|
||||
* to the output. It is invoked by the Delivery thread created as part
|
||||
* of @a rtems_regulator_create(). The priority and stack size of the
|
||||
* Delivery thread are specified in the regulator attribute set.
|
||||
*
|
||||
* It takes three parameters:
|
||||
*
|
||||
* @param[in] context is an untyped pointer to a user context
|
||||
* @param[in] message points to the message
|
||||
* @param[in] length is the message size
|
||||
*
|
||||
* The following is an example deliverer function. It assumes that the
|
||||
* application has defined the my_context_t structure and it has at least
|
||||
* the socket field. The @a message passed in originated with an
|
||||
* application source which obtained the @a message buffer using
|
||||
* @a rtems_regulator_obtain_buffer(), filled it in with source data,
|
||||
* and used @a rtems_regulator_send() to hand to the regulator instance
|
||||
* for later delivery.
|
||||
*
|
||||
* @code
|
||||
* bool my_deliverer(
|
||||
* void *context,
|
||||
* void *message,
|
||||
* size_t length
|
||||
* )
|
||||
* {
|
||||
* my_context_t *my_context;
|
||||
*
|
||||
* my_context = (my_context_t *)context;
|
||||
*
|
||||
* write(my_context->socket, message, length);
|
||||
* rtems_regulator_release_buffer(message);
|
||||
* // return false to indicate we released the buffer
|
||||
* return false;
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* The delivery function returns true to indicate that the delivery thread
|
||||
* should release the buffer or false to indicate that it released the
|
||||
* buffer. If the delivery function invokes a function like @a write()
|
||||
* to deliver the message to the destination, then the buffer can be
|
||||
* released immediately after the call. If the delivery function does
|
||||
* something like setting up a DMA transfer of the buffer, it cannot be
|
||||
* released until after the DMA is complete.
|
||||
*
|
||||
* The following sequence diagram shows the behavior of the Delivery thread
|
||||
* body and its interaction with the user-supplied deliverer() function.
|
||||
*
|
||||
* \startuml "Regulator Delivery Thread Body"
|
||||
* loop while (1)
|
||||
* "Delivery Thread" -> RTEMS : rtems_rate_monotonic_period(id, delivery_thread_period)
|
||||
* loop for 0 : maximum_to_dequeue_per_period
|
||||
* "Delivery Thread" -> RTEMS : rtems_message_queue_receive(id, message, size, wait, 0)
|
||||
* RTEMS --> "Delivery Thread" : rtems_status_code
|
||||
* group if [rtems_status_code != RTEMS_SUCCESSFUL]
|
||||
* RTEMS -> "Delivery Thread" : break
|
||||
* end
|
||||
* "Delivery Thread" -> Application : deliverer(context, buffer, length)
|
||||
* "Delivery Thread" -> RTEMS : rtems_partition_return_buffer(id, buffer)
|
||||
* RTEMS --> "Delivery Thread" : rtems_status_code
|
||||
* end
|
||||
* end
|
||||
* \enduml
|
||||
*
|
||||
* In the above sequence diagram, the key points are:
|
||||
*
|
||||
* -# The Delivery Thread Body is periodically executed.
|
||||
* -# During each period, up to the instance configuration parameter
|
||||
* @a maximum_to_dequeue_per_period may be dequeued and
|
||||
* passed the application's delivery function for processing.
|
||||
*
|
||||
* Note that the application explicitly obtains buffers from the
|
||||
* regulator instance but that the release may be done by Delivery
|
||||
* Thread, the Delivery function, or later when the buffer contents
|
||||
* are transferred.
|
||||
*/
|
||||
typedef bool (*rtems_regulator_deliverer)(
|
||||
void *context,
|
||||
void *message,
|
||||
size_t length
|
||||
);
|
||||
|
||||
/**
|
||||
* @ingroup RegulatorAPI
|
||||
*
|
||||
* @brief Attributes for Regulator Instance
|
||||
*
|
||||
* An instance of this structure must be populated by the application
|
||||
* before creating an instance of the regulator. These settings tailor
|
||||
* the behavior of the regulator instance.
|
||||
*/
|
||||
typedef struct {
|
||||
/** Application function to invoke to output a message to the destination*/
|
||||
rtems_regulator_deliverer deliverer;
|
||||
|
||||
/** Context pointer to pass to deliver function */
|
||||
void *deliverer_context;
|
||||
|
||||
/** Maximum size message to process */
|
||||
size_t maximum_message_size;
|
||||
|
||||
/** Maximum number of messages to be able to buffer */
|
||||
size_t maximum_messages;
|
||||
|
||||
/** Priority of Delivery thread */
|
||||
rtems_task_priority delivery_thread_priority;
|
||||
|
||||
/** Stack size of Delivery thread */
|
||||
size_t delivery_thread_stack_size;
|
||||
|
||||
/** Period (in ticks) of Delivery thread */
|
||||
rtems_interval delivery_thread_period;
|
||||
|
||||
/** Maximum messages to dequeue per period */
|
||||
size_t maximum_to_dequeue_per_period;
|
||||
|
||||
} rtems_regulator_attributes;
|
||||
|
||||
/**
|
||||
* @ingroup RegulatorAPI
|
||||
*
|
||||
* @brief Statistics for Regulator Instance
|
||||
*
|
||||
* An instance of this structure is provided to the directive
|
||||
* @a rtems_regulator_get_statistics and is filled in by that service.
|
||||
*/
|
||||
typedef struct {
|
||||
/** Number of successfully obtained buffers. */
|
||||
size_t obtained;
|
||||
|
||||
/** Number of successfully released buffers. */
|
||||
size_t released;
|
||||
|
||||
/** Number of successfully delivered buffers. */
|
||||
size_t delivered;
|
||||
|
||||
/** Rate Monotonic Period statistics for Delivery Thread */
|
||||
rtems_rate_monotonic_period_statistics period_statistics;
|
||||
|
||||
} rtems_regulator_statistics;
|
||||
|
||||
/**
|
||||
* @ingroup RegulatorAPI
|
||||
*
|
||||
* @brief Regulator Internal Structure
|
||||
*/
|
||||
struct _Regulator_Control;
|
||||
|
||||
/**
|
||||
* @ingroup RegulatorAPI
|
||||
*
|
||||
* @brief Regulator Instance
|
||||
*
|
||||
* This is used by the application as the handle to a Regulator instance.
|
||||
*/
|
||||
typedef struct _Regulator_Control *rtems_regulator_instance;
|
||||
|
||||
/**
|
||||
* @ingroup RegulatorAPI
|
||||
*
|
||||
* @brief Create a regulator
|
||||
*
|
||||
* This function creates an instance of a regulator. It uses the provided
|
||||
* @a attributes to create the instance return in @a regulator. This instance
|
||||
* will allocate the buffers associated with the regulator instance as well
|
||||
* as the Delivery thread.
|
||||
*
|
||||
* The @a attributes structure defines the priority and stack size of
|
||||
* the Delivery thread dedicated to this regulator instance. It also
|
||||
* defines the period of the Delivery thread and the maximum number of
|
||||
* messages that may be delivered per period via invocation of the
|
||||
* delivery function.
|
||||
*
|
||||
* For each regulator instance, the following resources are allocated:
|
||||
*
|
||||
* - A memory area for the regulator control block using @a malloc().
|
||||
* - A RTEMS Classic API Message Queue is constructed with message
|
||||
* buffer memory allocated using @a malloc(). Each message consists
|
||||
* of a pointer and a length.
|
||||
* - A RTEMS Classic API Partition.
|
||||
* - A RTEMS Classic API Rate Monotonic Period.
|
||||
*
|
||||
* @param[in] attributes specify the regulator instance attributes
|
||||
* @param[inout] regulator will point to the regulator instance
|
||||
*
|
||||
* @return an RTEMS status code indicating success or failure.
|
||||
*
|
||||
* @note This function allocates memory for the buffers holding messages,
|
||||
* an Delivery thread and an RTEMS partition. When it executes, the
|
||||
* Delivery thread will create an RTEMS rate monotonic period.
|
||||
*/
|
||||
rtems_status_code rtems_regulator_create(
|
||||
rtems_regulator_attributes *attributes,
|
||||
rtems_regulator_instance **regulator
|
||||
);
|
||||
|
||||
/**
|
||||
* @ingroup RegulatorAPI
|
||||
*
|
||||
* @brief Delete a regulator
|
||||
*
|
||||
* This function is used to delete the specified @a regulator instance.
|
||||
*
|
||||
* It is the responsibility of the user to ensure that any resources
|
||||
* such as sockets or open file descriptors used by the delivery
|
||||
* function are also deleted. It is likely safer to delete those
|
||||
* delivery resources after deleting the regulator instance rather than
|
||||
* before.
|
||||
*
|
||||
* @param[in] regulator is the instance to delete
|
||||
* @param[in] ticks is the maximum number of ticks to wait for
|
||||
* the delivery thread to shutdown.
|
||||
*
|
||||
* @return an RTEMS status code indicating success or failure.
|
||||
*
|
||||
* @note This function deallocates the resources allocated during
|
||||
* @a rtems_regulator_create().
|
||||
*/
|
||||
rtems_status_code rtems_regulator_delete(
|
||||
rtems_regulator_instance *regulator,
|
||||
rtems_interval ticks
|
||||
);
|
||||
|
||||
/**
|
||||
* @ingroup RegulatorAPI
|
||||
*
|
||||
* @brief Obtain Buffer from Regulator
|
||||
*
|
||||
* This function is used to obtain a buffer from the regulator's pool. The
|
||||
* @a buffer returned is assumed to be filled in with contents and used
|
||||
* in a subsequent call to @a rtems_regulator_send(). When the @a buffer is
|
||||
* delivered, it is expected to be released. If the @a buffer is not
|
||||
* successfully accepted by this function, then it should be returned
|
||||
* using @a rtems_regulator_release_buffer() or used to send another message.
|
||||
*
|
||||
* The @a buffer is of the maximum_message_size specified in the attributes
|
||||
* passed in to @a rtems_regulator_create().
|
||||
*
|
||||
* @param[in] regulator is the regulator instance to operate upon
|
||||
* @param[out] buffer will point to the allocated buffer
|
||||
*
|
||||
* @return an RTEMS status code indicating success or failure.
|
||||
*
|
||||
* @note This function does not perform dynamic allocation. It obtains a
|
||||
* buffer from the pool allocated during @a rtems_regulator_create().
|
||||
*
|
||||
* @note Any attempt to write outside the buffer area is undefined.
|
||||
*/
|
||||
rtems_status_code rtems_regulator_obtain_buffer(
|
||||
rtems_regulator_instance *regulator,
|
||||
void **buffer
|
||||
);
|
||||
|
||||
/**
|
||||
* @ingroup RegulatorAPI
|
||||
*
|
||||
* @brief Release Previously Obtained Regulator Buffer
|
||||
*
|
||||
* This function is used to release a buffer to the regulator's pool. It is
|
||||
* assumed that the @a buffer returned will not be used by the application
|
||||
* anymore. The @a buffer must have previously been allocated by
|
||||
* @a rtems_regulator_obtain_buffer() and NOT passed to
|
||||
* @a rtems_regulator_send().
|
||||
*
|
||||
* If a subsequent @a rtems_regulator_send() using this @a buffer is
|
||||
* successful, the @a buffer will eventually be processed by the delivery
|
||||
* thread and released.
|
||||
*
|
||||
* @param[in] regulator is the regulator instance to operate upon
|
||||
* @param[out] buffer will point to the buffer to release
|
||||
*
|
||||
* @return an RTEMS status code indicating success or failure.
|
||||
*
|
||||
* @note This function does not perform dynamic deallocation. It releases a
|
||||
* buffer to the pool allocated during @a rtems_regulator_create().
|
||||
*/
|
||||
rtems_status_code rtems_regulator_release_buffer(
|
||||
rtems_regulator_instance *regulator,
|
||||
void *buffer
|
||||
);
|
||||
|
||||
/**
|
||||
* @ingroup RegulatorAPI
|
||||
*
|
||||
* @brief Send to regulator instance
|
||||
*
|
||||
* This function is used by the producer to send a @a message to the
|
||||
* @a regulator for later delivery by the Delivery thread. The message is
|
||||
* contained in the memory pointed to by @a message and is @a length
|
||||
* bytes in length.
|
||||
*
|
||||
* It is required that the @a message buffer was obtained via
|
||||
* @a rtems_regulator_obtain_buffer().
|
||||
*
|
||||
* It is assumed that the @a message buffer has been filled in with
|
||||
* application content to deliver.
|
||||
*
|
||||
* If the @a rtems_regulator_send() is successful, the buffer is enqueued
|
||||
* inside the regulator instance for subsequent delivery. After the
|
||||
* @a message is delivered, it may be released by either delivery
|
||||
* function or the application code depending on the implementation.
|
||||
*
|
||||
* The status @a RTEMS_TOO_MANY is returned if the regulator's
|
||||
* internal queue is full. This indicates that the configured
|
||||
* maximum number of messages was insufficient. It is the
|
||||
* responsibility of the caller to decide whether to hold messages,
|
||||
* drop them, or print a message that the maximum number of messages
|
||||
* should be increased.
|
||||
*
|
||||
* If @a rtems_regulator_send() is unsuccessful, it is the application's
|
||||
* responsibility to release the buffer. If it is successfully sent,
|
||||
* then it becomes the responsibility of the delivery function to
|
||||
* release it.
|
||||
*
|
||||
* @param[in] regulator is the regulator instance to operate upon
|
||||
* @param[out] message points to the message to deliver
|
||||
* @param[out] length is the size of the message in bytes
|
||||
*
|
||||
* @return an RTEMS status code indicating success or failure.
|
||||
*
|
||||
*/
|
||||
rtems_status_code rtems_regulator_send(
|
||||
rtems_regulator_instance *regulator,
|
||||
void *message,
|
||||
size_t length
|
||||
);
|
||||
|
||||
/**
|
||||
* @ingroup RegulatorAPI
|
||||
*
|
||||
* @brief Obtain statistics for regulator instance
|
||||
*
|
||||
* This function is used by the application to obtain statistics
|
||||
* information about the regulator instance.
|
||||
*
|
||||
* If the @a obtained and @a released fields in the returned
|
||||
* @a statistics structure are equal, then there are no buffers
|
||||
* outstanding from this regulator instance.
|
||||
*
|
||||
* @param[in] regulator is the regulator instance to operate upon
|
||||
* @param[inout] statistics points to the statistics structure to fill in
|
||||
*
|
||||
* @return an RTEMS status code indicating success or failure.
|
||||
*
|
||||
*/
|
||||
rtems_status_code rtems_regulator_get_statistics(
|
||||
rtems_regulator_instance *regulator,
|
||||
rtems_regulator_statistics *statistics
|
||||
);
|
||||
|
||||
#endif /* REGULATOR_H */
|
||||
135
cpukit/include/rtems/regulatorimpl.h
Normal file
135
cpukit/include/rtems/regulatorimpl.h
Normal file
@@ -0,0 +1,135 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @ingroup RegulatorInternalAPI
|
||||
*
|
||||
* @brief Regulator Library Implementation Support
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup RegulatorInternalAPI Regulator API Internals
|
||||
*
|
||||
* @brief Regulator Internal Information
|
||||
*
|
||||
* This concerns implementation information about the Regulator.
|
||||
*/
|
||||
|
||||
#ifndef RTEMS_REGULATORIMPL_H
|
||||
#define RTEMS_REGULATORIMPL_H
|
||||
|
||||
#include <stdatomic.h>
|
||||
|
||||
#include <rtems/chain.h>
|
||||
|
||||
|
||||
/**
|
||||
* @ingroup RegulatorInternalAPI
|
||||
*
|
||||
* This constant is used to indicate the regulator instance is initialized.
|
||||
*/
|
||||
#define REGULATOR_INITIALIZED 0xDeadF00d
|
||||
|
||||
/**
|
||||
* @ingroup RegulatorInternalAPI
|
||||
*
|
||||
* @brief Regulator Message Instance Management Structure
|
||||
*/
|
||||
typedef struct {
|
||||
/** This points to the message contents. */
|
||||
void *buffer;
|
||||
/** This is the length of the message. */
|
||||
size_t length;
|
||||
} _Regulator_Message_t;
|
||||
|
||||
/**
|
||||
* @ingroup RegulatorInternalAPI
|
||||
*
|
||||
* @brief Regulator Statistics Private Structure
|
||||
*
|
||||
* An instance of this structure is allocated per regulator instance.
|
||||
*/
|
||||
typedef struct {
|
||||
/** Number of successfully obtained buffers. */
|
||||
atomic_size_t obtained;
|
||||
|
||||
/** Number of successfully released buffers. */
|
||||
atomic_size_t released;
|
||||
|
||||
/** Number of successfully delivered buffers. */
|
||||
atomic_size_t delivered;
|
||||
} _Regulator_Statistics;
|
||||
|
||||
/**
|
||||
* @ingroup RegulatorInternalAPI
|
||||
*
|
||||
* @brief Regulator Instance Private Structure
|
||||
*
|
||||
* An instance of this structure is allocated per regulator instance.
|
||||
*/
|
||||
typedef struct {
|
||||
/** Has magic value when instance is usable */
|
||||
uint32_t initialized;
|
||||
|
||||
/** Attributes for this instance -- copied from user */
|
||||
rtems_regulator_attributes Attributes;
|
||||
|
||||
/** Pointer to allocated message memory */
|
||||
void *message_memory;
|
||||
|
||||
/** Pointer to allocated memory for RTEMS Message Queue for pending buffers*/
|
||||
void *message_queue_storage;
|
||||
|
||||
/** RTEMS Message Queue of pending outgoing messages */
|
||||
rtems_id queue_id;
|
||||
|
||||
/** RTEMS Partition for pool of unused messages */
|
||||
rtems_id messages_partition_id;
|
||||
|
||||
/** RTEMS Task for performing output */
|
||||
rtems_id delivery_thread_id;
|
||||
|
||||
/** Id of period used by output thread */
|
||||
rtems_id delivery_thread_period_id;
|
||||
|
||||
/** Indicates Delivery thread is running */
|
||||
bool delivery_thread_is_running;
|
||||
|
||||
/** Indicates Delivery thread has been requested to exit */
|
||||
bool delivery_thread_request_exit;
|
||||
|
||||
/** Indicates Delivery thread has exited */
|
||||
bool delivery_thread_has_exited;
|
||||
|
||||
/** Internal Statistics */
|
||||
_Regulator_Statistics Statistics;
|
||||
|
||||
} _Regulator_Control;
|
||||
|
||||
#endif /* RTEMS_REGULATORIMPL_H */
|
||||
679
cpukit/libmisc/regulator/regulator.c
Normal file
679
cpukit/libmisc/regulator/regulator.c
Normal file
@@ -0,0 +1,679 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @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 <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,
|
||||
®ulator_message,
|
||||
®ulator_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(sizeof(_Regulator_Control), 1);
|
||||
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,
|
||||
®ulator_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;
|
||||
}
|
||||
@@ -510,6 +510,8 @@ links:
|
||||
uid: objmpci
|
||||
- role: build-dependency
|
||||
uid: objpci
|
||||
- role: build-dependency
|
||||
uid: objregulator
|
||||
- role: build-dependency
|
||||
uid: objpsxsgnl
|
||||
- role: build-dependency
|
||||
|
||||
18
spec/build/cpukit/objregulator.yml
Normal file
18
spec/build/cpukit/objregulator.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
|
||||
build-type: objects
|
||||
cflags: []
|
||||
copyrights:
|
||||
- Copyright (C) 2023 OAR Corporatoin
|
||||
cppflags: []
|
||||
cxxflags: []
|
||||
enabled-by: true
|
||||
includes: []
|
||||
install:
|
||||
- destination: ${BSP_INCLUDEDIR}/rtems
|
||||
source:
|
||||
- cpukit/include/rtems/regulator.h
|
||||
- cpukit/include/rtems/regulatorimpl.h
|
||||
links: []
|
||||
source:
|
||||
- cpukit/libmisc/regulator/regulator.c
|
||||
type: build
|
||||
@@ -228,6 +228,8 @@ links:
|
||||
uid: record01
|
||||
- role: build-dependency
|
||||
uid: record02
|
||||
- role: build-dependency
|
||||
uid: regulator01
|
||||
- role: build-dependency
|
||||
uid: rtmonuse
|
||||
- role: build-dependency
|
||||
|
||||
21
spec/build/testsuites/libtests/regulator01.yml
Normal file
21
spec/build/testsuites/libtests/regulator01.yml
Normal file
@@ -0,0 +1,21 @@
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
|
||||
build-type: test-program
|
||||
cflags: []
|
||||
copyrights:
|
||||
- Copyright (C) 2023 OAR Corporation
|
||||
cppflags: []
|
||||
cxxflags: []
|
||||
enabled-by: true
|
||||
features: c cprogram
|
||||
includes: []
|
||||
ldflags:
|
||||
- -Wl,--wrap=malloc
|
||||
links: []
|
||||
source:
|
||||
- testsuites/libtests/regulator01/regulator01.c
|
||||
- testsuites/libtests/regulator01/rtems_config.c
|
||||
stlib: []
|
||||
target: testsuites/libtests/regulator01.exe
|
||||
type: build
|
||||
use-after: []
|
||||
use-before: []
|
||||
1310
testsuites/libtests/regulator01/regulator01.c
Normal file
1310
testsuites/libtests/regulator01/regulator01.c
Normal file
File diff suppressed because it is too large
Load Diff
67
testsuites/libtests/regulator01/regulator01.doc
Normal file
67
testsuites/libtests/regulator01/regulator01.doc
Normal file
@@ -0,0 +1,67 @@
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
# Copyright (c) 2023 OAR Corporation
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
This file describes the directives and concepts tested by this test set.
|
||||
|
||||
test set name: regulator01
|
||||
|
||||
directives:
|
||||
+ rtems_regulator_create
|
||||
+ rtems_regulator_delete
|
||||
+ rtems_regulator_obtain_buffer
|
||||
+ rtems_regulator_release_buffer
|
||||
+ rtems_regulator_send
|
||||
|
||||
concepts
|
||||
+ Verify rtems_regulator_create() maximum_to_dequeue_per_period
|
||||
+ Verify rtems_regulator_create() NULL attributes error
|
||||
+ Verify rtems_regulator_create NULL regulator error
|
||||
+ Verify rtems_regulator_create deliverer is NULL
|
||||
+ Verify rtems_regulator_create maximum_messages is 0 error
|
||||
+ Verify rtems_regulator_create maximum_message_size is 0 error
|
||||
+ Verify rtems_regulator_create maximum_to_dequeue_per_period is 0 error
|
||||
+ Verify rtems_regulator_create returns error on failure to allocate regulator
|
||||
+ Verify rtems_regulator_create returns error on failure to allocate buffers
|
||||
+ Verify rtems_regulator_create and delete work
|
||||
+ Verify rtems_regulator_create rtems_partition_create error
|
||||
+ Verify rtems_regulator_create rtems_message_queue_create error
|
||||
+ Verify rtems_regulator_create rtems_task_create error
|
||||
+ Verify Regulator Output Thread Handles Error on Period Create
|
||||
+ Verify rtems_regulator_delete NULL regulator error
|
||||
+ Verify rtems_regulator_delete uninitialized regulator error
|
||||
+ Verify rtems_regulator_delete successful case
|
||||
+ Verify rtems_regulator_obtain_buffer NULL regulator error
|
||||
+ Verify rtems_regulator_obtain_buffer uninitialized regulator error
|
||||
+ Verify rtems_regulator_obtain_buffer successful case
|
||||
+ Verify rtems_regulator_release_buffer NULL regulator error
|
||||
+ Verify rtems_regulator_release_buffer uninitialized regulator error
|
||||
+ Verify rtems_regulator_release_buffer successful case
|
||||
+ Verify rtems_regulator_send NULL regulator error
|
||||
+ Verify rtems_regulator_send NULL message error
|
||||
+ Verify rtems_regulator_send zero length message error
|
||||
+ Verify rtems_regulator_send uninitialized regulator error
|
||||
+ Verify rtems_regulator_send and output thread delivers message
|
||||
+ Verify rtems_regulator_send and cannot delete with outstanding messages
|
||||
59
testsuites/libtests/regulator01/rtems_config.c
Normal file
59
testsuites/libtests/regulator01/rtems_config.c
Normal file
@@ -0,0 +1,59 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @brief RTEMS Configuration for regulator tests
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 <rtems.h>
|
||||
|
||||
rtems_task test_regulator(rtems_task_argument);
|
||||
|
||||
#include <bsp.h> /* for device driver prototypes */
|
||||
|
||||
/* NOTICE: the clock driver is explicitly disabled */
|
||||
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
|
||||
#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
|
||||
|
||||
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
|
||||
#define CONFIGURE_INIT_TASK_ENTRY_POINT test_regulator
|
||||
#define CONFIGURE_INIT_TASK_ATTRIBUTES RTEMS_FLOATING_POINT
|
||||
|
||||
/* Use hard limits to make it easier to trip object creation errors */
|
||||
#define CONFIGURE_MAXIMUM_TASKS 2
|
||||
#define CONFIGURE_MAXIMUM_MESSAGE_QUEUES 1
|
||||
#define CONFIGURE_MAXIMUM_PARTITIONS 1
|
||||
#define CONFIGURE_MAXIMUM_PERIODS 1
|
||||
|
||||
#define CONFIGURE_UNIFIED_WORK_AREAS
|
||||
#define CONFIGURE_MINIMUM_TASK_STACK_SIZE (8 * 1024)
|
||||
|
||||
#define CONFIGURE_INIT
|
||||
#include <rtems/confdefs.h>
|
||||
Reference in New Issue
Block a user