[PATCH rtems v2] Add the Regulator Interface and test
Chris Johns
chrisj at rtems.org
Wed Jul 19 02:32:43 UTC 2023
On 15/7/2023 9:48 am, Joel Sherrill wrote:
> 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.
> ---
> cpukit/include/rtems/regulator.h | 439 ++++++
> cpukit/include/rtems/regulatorimpl.h | 103 ++
> cpukit/libmisc/regulator/regulator.c | 522 ++++++++
> spec/build/cpukit/librtemscpu.yml | 2 +
> spec/build/cpukit/objregulator.yml | 18 +
> spec/build/testsuites/libtests/grp.yml | 2 +
> .../build/testsuites/libtests/regulator01.yml | 21 +
> testsuites/libtests/regulator01/regulator01.c | 1192 +++++++++++++++++
> .../libtests/regulator01/regulator01.doc | 68 +
> .../libtests/regulator01/rtems_config.c | 59 +
> 10 files changed, 2426 insertions(+)
> create mode 100644 cpukit/include/rtems/regulator.h
> create mode 100644 cpukit/include/rtems/regulatorimpl.h
> create mode 100644 cpukit/libmisc/regulator/regulator.c
> create mode 100644 spec/build/cpukit/objregulator.yml
> create mode 100644 spec/build/testsuites/libtests/regulator01.yml
> create mode 100644 testsuites/libtests/regulator01/regulator01.c
> create mode 100644 testsuites/libtests/regulator01/regulator01.doc
> create mode 100644 testsuites/libtests/regulator01/rtems_config.c
>
> diff --git a/cpukit/include/rtems/regulator.h b/cpukit/include/rtems/regulator.h
> new file mode 100644
> index 0000000000..74f0d00f2e
> --- /dev/null
> +++ b/cpukit/include/rtems/regulator.h
> @@ -0,0 +1,439 @@
> +/* SPDX-License-Identifier: BSD-2-Clause */
> +
> +/**
> + * @defgroup RegulatorAPI Regulator API
> + *
> + * @brief Regulator APIs
> + *
> + * The Regulator provides a set of APIs to manage input sources which
> + * produces bursts of message traffic.
> + */
> +
> +/**
> + * @mainpage
> + *
> + * The regulator is designed to sit logically between two entities -- a
> + * source and a sink/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 output sink.
> + *
> + * 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:
> + *
> + * @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)
> + * // rtems_regulator_send() will release the buffer automatically when done
OK however ...
> + * @endcode
> + *
> + * 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 Sink.
> + *
> + * The Delivery function is provided by the application for this specific
> + * Regulator instance. Depending on the Sink, it may use a method 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
> + * Sink which copies the buffer contents, the buffer can be released
> + * via @a rtems_regulator_release_buffer() as soon as the method 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 deliverate 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 Sink.
> + *
> + * 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.
> + * Destination by the internal Delivery thread.
> + */
> +
> +/**
> + * @addtogroup RegulatorAPI
> + *
> + * @file
> + *
> + * @brief This header file defines the Regulator API.
> + *
> + */
> +
> +/*
> + * 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.
> + */
> +
> +#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 output thread created as part
> + * of @a rtems_regulator_create(). The priority and stack size of the
> + * output 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);
... you have this?
Chris
> + * // 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 method like @a write() to deliver the
> + * message to the sink, then the buffer can be released immediately after the
> + * call. If the delivery method 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, output_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 method to invoke to output a message to the destination*/
> + rtems_regulator_deliverer deliverer;
> +
> + /** Context pointer to pass to deliver method */
> + 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 output thread */
> + rtems_task_priority output_thread_priority;
> +
> + /** Stack size of output thread */
> + size_t output_thread_stack_size;
> +
> + /** Period (in ticks) of output thread */
> + rtems_interval output_thread_period;
> +
> + /** Maximum messages to dequeue per period */
> + size_t maximum_to_dequeue_per_period;
> +
> +} rtems_regulator_attributes;
> +
> +/**
> + * @ingroup RegulatorAPI
> + *
> + * @brief Regulator Instance
> + */
> +typedef void *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 Output Thread.
> + *
> + * The @a attributes structure defines the priority and stack size of
> + * the output thread dedicated to this regulator instance. It also
> + * defines the period of the output thread and the maximum number of
> + * messages that may be delivered per period via invocation of the
> + * delivery function.
> + *
> + * Each regulator instance will allocate the following resources:
> + *
> + * - 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 size_t value.
> + * - 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 method allocates memory for the buffers holding messages,
> + * an output thread and an RTEMS partition. When it executes, the
> + * output 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 method is used to delete an instance of a @a regulator.
> + *
> + * 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 before deleting the regulator instance.
> + *
> + * @param[in] regulator is the instance to delete
> + *
> + * @return an RTEMS status code indicating success or failure.
> + *
> + * @note This method deallocates the resources allocated during
> + * rtems_regulator_create().
> + */
> +rtems_status_code rtems_regulator_delete(
> + rtems_regulator_instance *regulator
> +);
> +
> +/**
> + * @ingroup RegulatorAPI
> + *
> + * @brief Obtain Buffer from Regulator
> + *
> + * This method 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 will be automatically released. If the @a buffer is not sent,
> + * then it should be returned using @a rtems_regulator_release_buffer().
> + *
> + * The 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 method 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 method is used to release a buffer from 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 the @a rtems_regulator_send() is successful, the buffer will eventually
> + * be processed by the delivery thread and automatically released.
> + *
> + * 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
> + *
> + * @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 method 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 method is used by the producer to send a @a message to the
> + * @a regulator for later delivery by the output 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, the buffer is automatically freed.
> + *
> + * If @a rtems_regulator_send() is unsuccessful, it is the application's
> + * responsibility to * release the buffer.
> + *
> + * @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
> +);
> +
> +#endif /* REGULATOR_H */
> diff --git a/cpukit/include/rtems/regulatorimpl.h b/cpukit/include/rtems/regulatorimpl.h
> new file mode 100644
> index 0000000000..f46f942d69
> --- /dev/null
> +++ b/cpukit/include/rtems/regulatorimpl.h
> @@ -0,0 +1,103 @@
> +/* SPDX-License-Identifier: BSD-2-Clause */
> +
> +/**
> + * @defgroup RegulatorInternalAPI Regulator API Internals
> + *
> + * @brief Regulator Internal Information
> + *
> + * This concerns implementation information about the Regulator.
> + */
> +
> +/**
> + * @ingroup RegulatorInternalAPI
> + *
> + * @file
> + *
> + * @brief Regulator Library Implementation Support
> + */
> +
> +/*
> + * 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.
> + */
> +
> +#ifndef RTEMS_REGULATORIMPL_H
> +#define RTEMS_REGULATORIMPL_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 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 output_thread_id;
> +
> + /** Id of period used by output thread */
> + rtems_id output_thread_period_id;
> +
> +} _Regulator_Control;
> +
> +#endif /* RTEMS_REGULATORIMPL_H */
> diff --git a/cpukit/libmisc/regulator/regulator.c b/cpukit/libmisc/regulator/regulator.c
> new file mode 100644
> index 0000000000..5460590007
> --- /dev/null
> +++ b/cpukit/libmisc/regulator/regulator.c
> @@ -0,0 +1,522 @@
> +/* 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
> + */
> +static rtems_task _Regulator_Output_task_body(
> + rtems_task_argument arg
> +)
> +{
> + _Regulator_Control *the_regulator;
> + rtems_status_code sc;
> + size_t to_dequeue;
> + _Regulator_Message_t regulator_message;
> + size_t regulator_message_size;
> + bool release_it;
> +
> + /*
> + * The argument passed in cannot be NULL if the regulator_create worked.
> + */
> + the_regulator = (_Regulator_Control *) arg;
> +
> + /**
> + * 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->output_thread_period_id
> + );
> + if (sc != RTEMS_SUCCESSFUL) {
> + rtems_task_exit();
> + }
> +
> + /**
> + * Loop on the rate_monotonic_period() based on the specified period.
> + */
> + while (1) {
> + sc = rtems_rate_monotonic_period(
> + the_regulator->output_thread_period_id,
> + the_regulator->Attributes.output_thread_period
> + );
> + _Assert_Unused_variable_equals(sc, RTEMS_SUCCESSFUL);
> +
> + /**
> + * 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(
Does this mean the releasing is optional?
> + the_regulator->Attributes.deliverer_context,
> + regulator_message.buffer,
> + regulator_message.length
> + );
> +
> + /**
> + * The message was successfully delivered. If the delivery function
> + * wants the buffer returned, do it now. The delivery to the sink
> + * 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) {
> + sc = rtems_partition_return_buffer(
> + the_regulator->messages_partition_id,
> + regulator_message.buffer
> + );
> + _Assert_Unused_variable_equals(sc, RTEMS_SUCCESSFUL);
> + }
> + }
> + }
> +
> +}
> +
> +/**
> + * @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
> + */
> +static void _Regulator_Free_helper(
> + _Regulator_Control *the_regulator
> +)
> +{
> + (void) rtems_rate_monotonic_delete(the_regulator->output_thread_period_id);
> +
> + (void) rtems_task_delete(the_regulator->output_thread_id);
> +
> + /*
> + * The regulator's message_queue_storage is implicitly freed by this call.
> + */
> + (void) rtems_message_queue_delete(the_regulator->queue_id);
I am not comfortable with these deletes. What happens if the delivery handler
has been called and has not returned and it holds the allocator mutex, a
networking mutex or some other resource?
I think you need to ask the delivery thread to exit and you need to wait for it
to finish.
Chris
> +
> + (void) rtems_partition_delete(the_regulator->messages_partition_id);
> +
> + if (the_regulator->message_memory) {
> + free(the_regulator->message_memory);
> + }
> +
> + the_regulator->initialized = 0;
> + free(the_regulator);
> +}
> +
> +/**
> + * @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) {
> + return RTEMS_INVALID_ADDRESS;
> + }
> +
> + if (!regulator) {
> + return RTEMS_INVALID_ADDRESS;
> + }
> +
> + /**
> + * Verify attributes are OK. Some are checked by calls to object create
> + * methods. Specifically the following are not checked:
> + *
> + * - output_thread_priority by rtems_task_create()
> + * - output_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->output_thread_period == 0) {
> + return RTEMS_INVALID_NUMBER;
> + }
> +
> + /**
> + * Allocate memory for regulator instance
> + */
> + the_regulator = (_Regulator_Control *) calloc(sizeof(_Regulator_Control), 1);
> + if (!the_regulator) {
> + return RTEMS_NO_MEMORY;
> + }
> +
> + /**
> + * We do NOT want the output_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->output_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) {
> + _Regulator_Free_helper(the_regulator);
> + 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);
> + 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);
> + 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);
> + 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->output_thread_priority,
> + attributes->output_thread_stack_size,
> + RTEMS_DEFAULT_MODES,
> + RTEMS_DEFAULT_ATTRIBUTES,
> + &the_regulator->output_thread_id
> + );
> + if (sc != RTEMS_SUCCESSFUL) {
> + _Regulator_Free_helper(the_regulator);
> + 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.
> + */
> + sc = rtems_task_start(
> + the_regulator->output_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;
> +}
> +
> +/**
> + * @ingroup RegulatorInternalAPI
> + */
> +rtems_status_code rtems_regulator_delete(
> + rtems_regulator_instance *regulator
> +)
> +{
> + _Regulator_Control *the_regulator;
> +
> + the_regulator = (_Regulator_Control *) regulator;
> +
> + /**
> + * Validate the arguments and ensure the regulator was successfully
> + * initialized.
> + */
> + if (!the_regulator) {
> + return RTEMS_INVALID_ADDRESS;
> + }
> +
> + if (the_regulator->initialized != REGULATOR_INITIALIZED) {
> + return RTEMS_INCORRECT_STATE;
> + }
> +
> + /**
> + * Free the resources associated with this regulator instance.
> + */
> + _Regulator_Free_helper(the_regulator);
> +
> + 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 sc;
> +
> + the_regulator = (_Regulator_Control *) regulator;
> +
> + /**
> + * Validate the arguments and ensure the regulator was successfully
> + * initialized.
> + */
> + if (!the_regulator) {
> + return RTEMS_INVALID_ADDRESS;
> + }
> +
> + if (the_regulator->initialized != REGULATOR_INITIALIZED) {
> + return RTEMS_INCORRECT_STATE;
> + }
> +
> + /**
> + * Allocate a buffer for the user application from the buffer pool managed
> + * by an Classic API partition.
> + */
> + sc = rtems_partition_get_buffer(
> + the_regulator->messages_partition_id,
> + buffer
> + );
> +
> + return sc;
> +}
> +
> +/**
> + * @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 sc;
> +
> + the_regulator = (_Regulator_Control *) regulator;
> +
> + /**
> + * Validate the arguments and ensure the regulator was successfully
> + * initialized.
> + */
> + if (!the_regulator) {
> + return RTEMS_INVALID_ADDRESS;
> + }
> +
> + if (the_regulator->initialized != REGULATOR_INITIALIZED) {
> + return RTEMS_INCORRECT_STATE;
> + }
> +
> + /**
> + * Deallocate the buffer to the buffer pool managed by a Classic
> + * API partition.
> + */
> + sc = rtems_partition_return_buffer(
> + the_regulator->messages_partition_id,
> + buffer
> + );
> +
> + return sc;
> +}
> +
> +/**
> + * @ingroup RegulatorInternalAPI
> + */
> +rtems_status_code rtems_regulator_send(
> + rtems_regulator_instance *regulator,
> + void *message,
> + size_t length
> +)
> +{
> + _Regulator_Control *the_regulator;
> + rtems_status_code sc;
> + _Regulator_Message_t regulator_message;
> +
> + the_regulator = (_Regulator_Control *) regulator;
> +
> + /**
> + * Validate the arguments and ensure the regulator was successfully
> + * initialized.
> + */
> + if (!message) {
> + return RTEMS_INVALID_ADDRESS;
> + }
> +
> + if (length == 0) {
> + return RTEMS_INVALID_NUMBER;
> + }
> +
> + if (!the_regulator) {
> + return RTEMS_INVALID_ADDRESS;
> + }
> +
> + if (the_regulator->initialized != REGULATOR_INITIALIZED) {
> + return RTEMS_INCORRECT_STATE;
> + }
> +
> + /**
> + * 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.
> + */
> + sc = rtems_message_queue_send(
> + the_regulator->queue_id,
> + ®ulator_message,
> + sizeof(_Regulator_Message_t)
> + );
> + if (sc != RTEMS_SUCCESSFUL) {
> + return sc;
> + }
> +
> + return sc;
> +}
> diff --git a/spec/build/cpukit/librtemscpu.yml b/spec/build/cpukit/librtemscpu.yml
> index 3654e7f94a..4074ca1fcb 100644
> --- a/spec/build/cpukit/librtemscpu.yml
> +++ b/spec/build/cpukit/librtemscpu.yml
> @@ -514,6 +514,8 @@ links:
> uid: objmpci
> - role: build-dependency
> uid: objpci
> +- role: build-dependency
> + uid: objregulator
> - role: build-dependency
> uid: objpsxsgnl
> - role: build-dependency
> diff --git a/spec/build/cpukit/objregulator.yml b/spec/build/cpukit/objregulator.yml
> new file mode 100644
> index 0000000000..56d6154de9
> --- /dev/null
> +++ b/spec/build/cpukit/objregulator.yml
> @@ -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
> diff --git a/spec/build/testsuites/libtests/grp.yml b/spec/build/testsuites/libtests/grp.yml
> index be340c8ab6..7ca0f7dfa1 100644
> --- a/spec/build/testsuites/libtests/grp.yml
> +++ b/spec/build/testsuites/libtests/grp.yml
> @@ -230,6 +230,8 @@ links:
> uid: record01
> - role: build-dependency
> uid: record02
> +- role: build-dependency
> + uid: regulator01
> - role: build-dependency
> uid: rtmonuse
> - role: build-dependency
> diff --git a/spec/build/testsuites/libtests/regulator01.yml b/spec/build/testsuites/libtests/regulator01.yml
> new file mode 100644
> index 0000000000..776d0ae34b
> --- /dev/null
> +++ b/spec/build/testsuites/libtests/regulator01.yml
> @@ -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: []
> diff --git a/testsuites/libtests/regulator01/regulator01.c b/testsuites/libtests/regulator01/regulator01.c
> new file mode 100644
> index 0000000000..739af05fd6
> --- /dev/null
> +++ b/testsuites/libtests/regulator01/regulator01.c
> @@ -0,0 +1,1192 @@
> +/* SPDX-License-Identifier: BSD-2-Clause */
> +
> +/**
> + * @defgroup RegulatorTests Regulator Test Cases
> + *
> + * @brief Unit test cases for the Regulator
> + *
> + * This is a set of unit test cases for the regulator.
> + */
> +
> +/**
> + * @ingroup RegulatorTests
> + *
> + * @file
> + *
> + * @brief Test 01 for Regulator Library
> + */
> +
> +/*
> + * 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 <stdio.h>
> +#include <string.h>
> +#include <unistd.h>
> +
> +#include <rtems.h>
> +#include <rtems/test-info.h>
> +#include <tmacros.h>
> +
> +#include <rtems/regulator.h>
> +
> +/**
> + * @brief Regulator Test Name
> + */
> +const char rtems_test_name[] = "Regulator 1";
> +
> +/*
> + * Prototypes for wrapped functions
> + */
> +void *__wrap_malloc(size_t size);
> +void *__real_malloc(size_t size);
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Calloc Wrapper Trigger Count
> + */
> +static int malloc_trigger_count;
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Calloc Wrapper Call Count
> + */
> +static int malloc_call_count;
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Calloc Wrapper Trigger enable
> + */
> +static bool malloc_trigger_enabled;
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Enable Calloc Wrapper Trigger
> + */
> +static void malloc_trigger_enable(
> + int trigger_count
> +)
> +{
> + malloc_trigger_enabled = true;
> + malloc_trigger_count = trigger_count;
> + malloc_call_count = 0;
> +}
> +/**
> + * @ingroup RegulatorTests
> + * @brief Reset Calloc Wrapper Trigger and Count
> + */
> +static void malloc_trigger_reset(void)
> +{
> + malloc_trigger_enabled = 0;
> + malloc_trigger_count = 0;
> + malloc_call_count = 0;
> +}
> +/**
> + * @ingroup RegulatorTests
> + * @brief Calloc Wrapper to Trigger Allocation Errors
> + */
> +void *__wrap_malloc(size_t size)
> +{
> + if (malloc_trigger_enabled) {
> + malloc_call_count++;
> + if (malloc_call_count == malloc_trigger_count) {
> + return NULL;
> + }
> + }
> +
> + return __real_malloc(size);
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Empty Deliver Method for Testing
> + */
> +static bool test_regulator_deliverer(
> + void *context,
> + void *message,
> + size_t length
> +)
> +{
> + (void) context;
> + (void) message;
> + (void) length;
> +
> + return true;
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Maximum length of a test message that is delivered
> + */
> +#define MAXIMUM_MESSAGE_LENGTH 32
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Maximum number of test messages to buffer
> + */
> +#define MAXIMUM_MESSAGES_TO_BUFFER 10
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Structure for capturing messages as delivered
> + */
> +typedef struct {
> + rtems_interval processed;
> + char message[MAXIMUM_MESSAGE_LENGTH];
> +} message_log_t;
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Set of Delivered messages
> + */
> +message_log_t delivered_messages[MAXIMUM_MESSAGES_TO_BUFFER];
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Count of Delivered messages
> + */
> +int delivered_message_count;
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Reset Delivered Message Set
> + *
> + * This is used at the beginning of a test case which is going to
> + * check that message contents and delivery times were as expected.
> + */
> +static void delivered_messages_reset(void)
> +{
> + delivered_message_count = 0;
> + memset(delivered_messages, 0xc5, sizeof(delivered_messages));
> +}
> +
> +/**
> + * @brief Context for Logger Delivery Function
> + */
> +typedef struct {
> + /** Regulator instance being operated on */
> + rtems_regulator_instance *regulator;
> +} deliverer_logger_context_t;
> +
> +/**
> + * @brief Context Instance for Logger Delivery Function
> + */
> +static deliverer_logger_context_t deliverer_logger_context;
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Empty Deliver Method for Testing
> + *
> + * This deliverer method implementation logs the messages along with
> + * their time of arrival. This is used by the test cases to verify
> + * proper delivery.
> + */
> +static bool test_regulator_deliverer_logger(
> + void *context,
> + void *message,
> + size_t length
> +)
> +{
> + (void) context;
> +
> + static bool caller_releases_buffer = true;
> +
> + size_t len;
> + rtems_interval ticks;
> +
> + len = strnlen(message, MAXIMUM_MESSAGE_LENGTH) + 1;
> + rtems_test_assert(len = length);
> +
> + ticks = rtems_clock_get_ticks_since_boot();
> +
> + delivered_messages[delivered_message_count].processed = ticks;
> +
> + strcpy(delivered_messages[delivered_message_count].message, message);
> +
> + delivered_message_count++;
> +
> + /*
> + * Alternate releasing the buffer here and allowing the calling Delivery
> + * Thread to do it. This increases coverage of that logic.
> + */
> + if (caller_releases_buffer == true) {
> + caller_releases_buffer = false;
> + return true;
> + }
> +
> + return false;
> +}
> +
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Helper to create a Regulator instance
> + *
> + * This helper creates a regulator instance with some arbitrary attributes.
> + * This is used in multiple test cases to have a valie regulator instance to
> + * trigger error cases.
> + */
> +static rtems_regulator_instance *test_regulator_create_regulator_OK(void)
> +{
> + rtems_status_code sc;
> + rtems_regulator_attributes attributes = {
> + .deliverer = test_regulator_deliverer,
> + .deliverer_context = NULL,
> + .maximum_message_size = 16,
> + .maximum_messages = 10,
> + .output_thread_priority = 16,
> + .output_thread_stack_size = 0,
> + .output_thread_period = RTEMS_MILLISECONDS_TO_TICKS(1000),
> + .maximum_to_dequeue_per_period = 3
> + };
> + rtems_regulator_instance *regulator;
> +
> + regulator = NULL;
> +
> + sc = rtems_regulator_create(&attributes, ®ulator);
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> + rtems_test_assert(regulator != NULL);
> +
> + return regulator;
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Verify rtems_regulator_create() maximum_to_dequeue_per_period
> + * attributes error
> + *
> + * This unit test verifies that rtems_regulator_create() returns an error when
> + * the maximum_to_dequeue_per_period attribute is zero.
> + */
> +static void test_regulator_create_max_dequeue_zero(void)
> +{
> + rtems_status_code sc;
> + rtems_regulator_attributes attributes = {
> + .deliverer = test_regulator_deliverer,
> + .deliverer_context = NULL,
> + .maximum_message_size = 16,
> + .maximum_messages = 10,
> + .output_thread_priority = 16,
> + .output_thread_stack_size = 0,
> + .output_thread_period = RTEMS_MILLISECONDS_TO_TICKS(1000),
> + .maximum_to_dequeue_per_period = 0
> + };
> + rtems_regulator_instance *regulator;
> +
> + regulator = NULL;
> +
> + sc = rtems_regulator_create(&attributes, ®ulator);
> + rtems_test_assert(sc == RTEMS_INVALID_NUMBER);
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Verify rtems_regulator_create() NULL attributes error
> + *
> + * This unit test verifies that rtems_regulator_create() returns an error when
> + * the attributes argument is NULL.
> + */
> +static void test_regulator_create_null_attributes(void)
> +{
> + rtems_status_code sc;
> + rtems_regulator_instance *regulator;
> +
> + sc = rtems_regulator_create(NULL, ®ulator);
> + rtems_test_assert(sc == RTEMS_INVALID_ADDRESS);
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Verify rtems_regulator_create NULL regulator error
> + *
> + * This unit test verifies that rtems_regulator_create() returns an error when
> + * the regulator argument is NULL.
> + */
> +static void test_regulator_create_null_regulator(void)
> +{
> + rtems_status_code sc;
> + rtems_regulator_attributes attributes;
> +
> + sc = rtems_regulator_create(&attributes, NULL);
> + rtems_test_assert(sc == RTEMS_INVALID_ADDRESS);
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Verify rtems_regulator_create deliverer is NULL
> + *
> + * This unit test verifies that rtems_regulator_create() returns an error when
> + * the the attributes deliverer field is NULL.
> + */
> +static void test_regulator_create_deliverer_is_null(void)
> +{
> + rtems_status_code sc;
> + rtems_regulator_attributes attributes;
> + rtems_regulator_instance *regulator;
> +
> + (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));
> +
> + attributes.deliverer = NULL;
> + attributes.maximum_messages = 0;
> + attributes.maximum_message_size = 16;
> + attributes.maximum_to_dequeue_per_period = 1;
> +
> + sc = rtems_regulator_create(&attributes, ®ulator);
> + rtems_test_assert(sc == RTEMS_INVALID_ADDRESS);
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Verify rtems_regulator_create maximum_messages is 0 error
> + *
> + * This unit test verifies that rtems_regulator_create() returns an error when
> + * the the attributes maximum_messages field is 0.
> + */
> +static void test_regulator_create_maximum_messages_is_zero(void)
> +{
> + rtems_status_code sc;
> + rtems_regulator_attributes attributes;
> + rtems_regulator_instance *regulator;
> +
> + (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));
> +
> + attributes.deliverer = test_regulator_deliverer;
> + attributes.maximum_messages = 0;
> + attributes.maximum_message_size = 16;
> + attributes.maximum_to_dequeue_per_period = 1;
> +
> + sc = rtems_regulator_create(&attributes, ®ulator);
> + rtems_test_assert(sc == RTEMS_INVALID_NUMBER);
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Verify rtems_regulator_create maximum_message_size is 0 error
> + *
> + * This unit test verifies that rtems_regulator_create() returns an error when
> + * the the attributes maximum_message_size field is 0.
> + */
> +static void test_regulator_create_maximum_message_size_is_zero(void)
> +{
> + rtems_status_code sc;
> + rtems_regulator_attributes attributes;
> + rtems_regulator_instance *regulator;
> +
> + (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));
> +
> + attributes.deliverer = test_regulator_deliverer;
> + attributes.maximum_messages = 10;
> + attributes.maximum_message_size = 0;
> + attributes.maximum_to_dequeue_per_period = 1;
> +
> + sc = rtems_regulator_create(&attributes, ®ulator);
> + rtems_test_assert(sc == RTEMS_INVALID_SIZE);
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Verify rtems_regulator_create maximum_to_dequeue_per_period is 0 error
> + *
> + * This unit test verifies that rtems_regulator_create() returns an error when
> + * the the attributes maximum_to_dequeue_per_period field is 0.
> + */
> +static void test_regulator_create_maximum_to_dequeue_per_period_is_zero(void)
> +{
> + rtems_status_code sc;
> + rtems_regulator_attributes attributes;
> + rtems_regulator_instance *regulator;
> +
> + (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));
> +
> + attributes.deliverer = test_regulator_deliverer;
> + attributes.maximum_messages = 10;
> + attributes.maximum_message_size = 0;
> +
> + sc = rtems_regulator_create(&attributes, ®ulator);
> + rtems_test_assert(sc == RTEMS_INVALID_SIZE);
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Verify rtems_regulator_create returns error on failure to allocate regulator
> + *
> + * This unit test verifies that rtems_regulator_create() returns an error when
> + * it is unable to allocate the mmemory for the regulator instance.
> + */
> +static void test_regulator_create_malloc_regulator_fails(void)
> +{
> + rtems_status_code sc;
> + rtems_regulator_attributes attributes;
> + rtems_regulator_instance *regulator;
> +
> + (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));
> +
> + attributes.deliverer = test_regulator_deliverer;
> + attributes.maximum_messages = 10;
> + attributes.maximum_message_size = 16;
> + attributes.output_thread_priority = 32;
> + attributes.maximum_to_dequeue_per_period = 1;
> + attributes.output_thread_period = RTEMS_MILLISECONDS_TO_TICKS(1000);
> +
> + malloc_trigger_enable(1);
> +
> + sc = rtems_regulator_create(&attributes, ®ulator);
> + rtems_test_assert(sc == RTEMS_NO_MEMORY);
> +
> + malloc_trigger_reset();
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Verify rtems_regulator_create returns error on failure to allocate buffers
> + *
> + * This unit test verifies that rtems_regulator_create() returns an error when
> + * it is unable to allocate the mmemory for the regulator buffers.
> + */
> +static void test_regulator_create_malloc_buffers_fails(void)
> +{
> + rtems_status_code sc;
> + rtems_regulator_attributes attributes;
> + rtems_regulator_instance *regulator;
> +
> + (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));
> +
> + attributes.deliverer = test_regulator_deliverer;
> + attributes.maximum_messages = 10;
> + attributes.maximum_message_size = 16;
> + attributes.output_thread_priority = 32;
> + attributes.maximum_to_dequeue_per_period = 1;
> + attributes.output_thread_period = RTEMS_MILLISECONDS_TO_TICKS(1000);
> +
> + malloc_trigger_enable(2);
> +
> + sc = rtems_regulator_create(&attributes, ®ulator);
> + rtems_test_assert(sc == RTEMS_NO_MEMORY);
> +
> + malloc_trigger_reset();
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Verify rtems_regulator_create and delete work
> + *
> + * This unit test verifies that rtems_regulator_create() can successfully create
> + * the the attributes output_thread_priority field is 0.
> + */
> +static void test_regulator_create_output_thread_priority_is_zero(void)
> +{
> + rtems_status_code sc;
> + rtems_regulator_attributes attributes;
> + rtems_regulator_instance *regulator;
> +
> + (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));
> +
> + attributes.deliverer = test_regulator_deliverer;
> + attributes.maximum_messages = 10;
> + attributes.maximum_message_size = 16;
> + attributes.output_thread_priority = 0;
> + attributes.maximum_to_dequeue_per_period = 1;
> + attributes.output_thread_period = RTEMS_MILLISECONDS_TO_TICKS(1000);
> +
> + sc = rtems_regulator_create(&attributes, ®ulator);
> + rtems_test_assert(sc == RTEMS_INVALID_PRIORITY);
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Verify rtems_regulator_create rtems_partition_create error
> + *
> + * This unit test verifies that rtems_regulator_create() correctly returns an
> + * error when the call to rtems_partition_create() fails.
> + */
> +static void test_regulator_create_partition_create_fails(void)
> +{
> + rtems_status_code sc;
> + rtems_id partition_id;
> + unsigned long partition_area[16];
> + rtems_regulator_attributes attributes;
> + rtems_regulator_instance *regulator;
> +
> + sc = rtems_partition_create(
> + rtems_build_name('T', 'P', 'T', 'P'),
> + partition_area,
> + 16 * sizeof(unsigned long),
> + 2 * sizeof(unsigned long),
> + RTEMS_DEFAULT_ATTRIBUTES,
> + &partition_id
> + );
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> +
> + (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));
> +
> + attributes.deliverer = test_regulator_deliverer;
> + attributes.maximum_messages = 10;
> + attributes.maximum_message_size = 16;
> + attributes.output_thread_priority = 8;
> + attributes.maximum_to_dequeue_per_period = 1;
> + attributes.output_thread_period = RTEMS_MILLISECONDS_TO_TICKS(1000);
> +
> + sc = rtems_regulator_create(&attributes, ®ulator);
> + rtems_test_assert(sc == RTEMS_TOO_MANY);
> +
> + sc = rtems_partition_delete(partition_id);
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Verify rtems_regulator_create rtems_message_queue_create error
> + *
> + * This unit test verifies that rtems_regulator_create() correctly returns an
> + * error when the call to rtems_message_queue_create() fails.
> + */
> +static void test_regulator_create_message_queue_create_fails(void)
> +{
> + rtems_status_code sc;
> + rtems_id queue_id;
> + rtems_regulator_attributes attributes;
> + rtems_regulator_instance *regulator;
> +
> + sc = rtems_message_queue_create(
> + rtems_build_name('T', 'Q', 'T', 'Q'),
> + 4,
> + sizeof(unsigned long),
> + RTEMS_DEFAULT_ATTRIBUTES,
> + &queue_id
> + );
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> +
> + (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));
> +
> + attributes.deliverer = test_regulator_deliverer;
> + attributes.maximum_messages = 10;
> + attributes.maximum_message_size = 16;
> + attributes.output_thread_priority = 8;
> + attributes.maximum_to_dequeue_per_period = 1;
> + attributes.output_thread_period = RTEMS_MILLISECONDS_TO_TICKS(1000);
> +
> + sc = rtems_regulator_create(&attributes, ®ulator);
> + rtems_test_assert(sc == RTEMS_TOO_MANY);
> +
> + sc = rtems_message_queue_delete(queue_id);
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Verify rtems_regulator_create rtems_task_create error
> + *
> + * This unit test verifies that rtems_regulator_create() correctly returns an
> + * error when the call to rtems_task_create() fails.
> + */
> +static void test_regulator_create_task_create_fails(void)
> +{
> + rtems_status_code sc;
> + rtems_id task_id;
> + rtems_regulator_attributes attributes;
> + rtems_regulator_instance *regulator;
> +
> + sc = rtems_task_create(
> + rtems_build_name('T', 'T', 'T', 'T'),
> + 80,
> + 0,
> + RTEMS_DEFAULT_MODES,
> + RTEMS_DEFAULT_ATTRIBUTES,
> + &task_id
> + );
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> +
> + (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));
> +
> + attributes.deliverer = test_regulator_deliverer;
> + attributes.maximum_messages = 10;
> + attributes.maximum_message_size = 16;
> + attributes.output_thread_priority = 8;
> + attributes.maximum_to_dequeue_per_period = 1;
> + attributes.output_thread_period = RTEMS_MILLISECONDS_TO_TICKS(1000);
> +
> + sc = rtems_regulator_create(&attributes, ®ulator);
> + rtems_test_assert(sc == RTEMS_TOO_MANY);
> +
> + sc = rtems_task_delete(task_id);
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Verify Regulator Output Thread Handles Error on Period Create
> + *
> + * This unit test verifies that regulator output thread correctly exits
> + * when the call to rtems_rate_monotonic_create() fails.
> + *
> + * This error condition/path cannot be directly detected via a return code,
> + * It is verified via a debugger or code coverage reports.
> + */
> +static void test_regulator_create_rate_monotonic_create_fails(void)
> +{
> + rtems_status_code sc;
> + rtems_id period_id;
> + rtems_regulator_attributes attributes;
> + rtems_regulator_instance *regulator;
> +
> + sc = rtems_rate_monotonic_create(
> + rtems_build_name('T', 'S', 'T', 'P'),
> + &period_id
> + );
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> +
> + (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));
> +
> + attributes.deliverer = test_regulator_deliverer;
> + attributes.maximum_messages = 10;
> + attributes.maximum_message_size = 16;
> + attributes.output_thread_priority = 8;
> + attributes.maximum_to_dequeue_per_period = 1;
> + attributes.output_thread_period = RTEMS_MILLISECONDS_TO_TICKS(1000);
> +
> + sc = rtems_regulator_create(&attributes, ®ulator);
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> +
> + /*
> + * Let the output thread execute and encounter the create error.
> + */
> +
> + sleep(1);
> +
> + /*
> + * Now deallocate the resources allocated earlier
> + */
> + sc = rtems_regulator_delete(regulator);
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> +
> + sc = rtems_rate_monotonic_delete(period_id);
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Verify rtems_regulator_delete NULL regulator error
> + *
> + * This unit test verifies that rtems_regulator_delete() returns an error when
> + * the regulator argument is NULL.
> + */
> +static void test_regulator_delete_null_regulator(void)
> +{
> + rtems_status_code sc;
> +
> + sc = rtems_regulator_delete(NULL);
> + rtems_test_assert(sc == RTEMS_INVALID_ADDRESS);
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Verify rtems_regulator_delete uninitialized regulator error
> + *
> + * This unit test verifies that rtems_regulator_delete() returns an error when
> + * the regulator argument is uninitialized.
> + */
> +static void test_regulator_delete_uninitialized_regulator(void)
> +{
> + rtems_status_code sc;
> + rtems_regulator_instance regulator;
> +
> + (void) memset(®ulator, 0, sizeof(regulator));
> +
> + sc = rtems_regulator_delete(®ulator);
> + rtems_test_assert(sc == RTEMS_INCORRECT_STATE);
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Verify rtems_regulator_delete successful case
> + *
> + * This unit test verifies that rtems_regulator_delete() can be successfully
> + * deleted.
> + */
> +static void test_regulator_delete_OK(void)
> +{
> + rtems_status_code sc;
> + rtems_regulator_instance *regulator;
> +
> + regulator = test_regulator_create_regulator_OK();
> + rtems_test_assert(regulator != NULL);
> +
> + sc = rtems_regulator_delete(regulator);
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Verify rtems_regulator_obtain_buffer NULL regulator error
> + *
> + * This unit test verifies that rtems_regulator_obtain_buffer() returns an error when
> + * the regulator argument is NULL.
> + */
> +static void test_regulator_obtain_buffer_null_regulator(void)
> +{
> + rtems_status_code sc;
> + void *buffer;
> +
> + sc = rtems_regulator_obtain_buffer(NULL, &buffer);
> + rtems_test_assert(sc == RTEMS_INVALID_ADDRESS);
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Verify rtems_regulator_obtain_buffer uninitialized regulator error
> + *
> + * This unit test verifies that rtems_regulator_obtain_buffer() returns an error when
> + * the regulator argument is uninitialized.
> + */
> +static void test_regulator_obtain_buffer_uninitialized_regulator(void)
> +{
> + rtems_status_code sc;
> + rtems_regulator_instance regulator;
> + void *buffer;
> +
> + (void) memset(®ulator, 0, sizeof(regulator));
> +
> + sc = rtems_regulator_obtain_buffer(®ulator, &buffer);
> + rtems_test_assert(sc == RTEMS_INCORRECT_STATE);
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Verify rtems_regulator_obtain_buffer successful case
> + *
> + * This unit test verifies that rtems_regulator_obtain_buffer() can be successfully
> + * obtained from an initialized regulator.
> + */
> +static void test_regulator_obtain_buffer_OK(void)
> +{
> + rtems_status_code sc;
> + rtems_regulator_instance *regulator;
> + void *buffer;
> +
> + regulator = test_regulator_create_regulator_OK();
> + rtems_test_assert(regulator != NULL);
> +
> + sc = rtems_regulator_obtain_buffer(regulator, &buffer);
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> + rtems_test_assert(buffer != NULL);
> +
> + /*
> + * Not really testing this here but cannot delete underlying partition
> + * if there are buffers outstanding.
> + */
> + sc = rtems_regulator_release_buffer(regulator, buffer);
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> + rtems_test_assert(buffer != NULL);
> +
> + sc = rtems_regulator_delete(regulator);
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Verify rtems_regulator_release_buffer NULL regulator error
> + *
> + * This unit test verifies that rtems_regulator_release_buffer() returns an error when
> + * the regulator argument is NULL.
> + */
> +static void test_regulator_release_buffer_null_regulator(void)
> +{
> + rtems_status_code sc;
> + void *buffer;
> +
> + sc = rtems_regulator_release_buffer(NULL, &buffer);
> + rtems_test_assert(sc == RTEMS_INVALID_ADDRESS);
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Verify rtems_regulator_release_buffer uninitialized regulator error
> + *
> + * This unit test verifies that rtems_regulator_release_buffer() returns an
> + * error when the regulator argument is uninitialized.
> + */
> +static void test_regulator_release_buffer_uninitialized_regulator(void)
> +{
> + rtems_status_code sc;
> + rtems_regulator_instance regulator;
> + void *buffer;
> +
> + (void) memset(®ulator, 0, sizeof(regulator));
> +
> + sc = rtems_regulator_release_buffer(®ulator, &buffer);
> + rtems_test_assert(sc == RTEMS_INCORRECT_STATE);
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Verify rtems_regulator_release_buffer successful case
> + *
> + * This unit test verifies that rtems_regulator_release_buffer() can be successfully
> + * invoked with a buffer previously allocated by rtems_regulator_obtain_buffer().
> + */
> +static void test_regulator_release_buffer_OK(void)
> +{
> + rtems_status_code sc;
> + rtems_regulator_instance *regulator;
> + void *buffer;
> +
> + regulator = test_regulator_create_regulator_OK();
> + rtems_test_assert(regulator != NULL);
> +
> + sc = rtems_regulator_obtain_buffer(regulator, &buffer);
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> + rtems_test_assert(buffer != NULL);
> +
> + sc = rtems_regulator_release_buffer(regulator, buffer);
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> +
> + sc = rtems_regulator_delete(regulator);
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Verify rtems_regulator_send NULL regulator error
> + *
> + * This unit test verifies that rtems_regulator_send() returns an error when
> + * the regulator argument is NULL.
> + */
> +static void test_regulator_send_null_regulator(void)
> +{
> + rtems_status_code sc;
> + void *buffer;
> + size_t length;
> +
> + buffer = &length;
> + length = sizeof(size_t);
> +
> + sc = rtems_regulator_send(NULL, buffer, length);
> + rtems_test_assert(sc == RTEMS_INVALID_ADDRESS);
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Verify rtems_regulator_send NULL message error
> + *
> + * This unit test verifies that rtems_regulator_send() returns an error when
> + * the message argument is NULL.
> + */
> +static void test_regulator_send_null_message(void)
> +{
> + rtems_status_code sc;
> + size_t length;
> + rtems_regulator_instance regulator;
> +
> + length = sizeof(size_t);
> +
> + sc = rtems_regulator_send(®ulator, NULL, length);
> + rtems_test_assert(sc == RTEMS_INVALID_ADDRESS);
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Verify rtems_regulator_send zero length message error
> + *
> + * This unit test verifies that rtems_regulator_send() returns an
> + * error when the message length is 0.
> + */
> +static void test_regulator_send_length_is_zero(void)
> +{
> + rtems_status_code sc;
> + rtems_regulator_instance regulator;
> + void *buffer;
> + size_t length;
> +
> + buffer = &length;
> + length = 0;
> +
> + sc = rtems_regulator_send(®ulator, buffer, length);
> + rtems_test_assert(sc == RTEMS_INVALID_NUMBER);
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Verify rtems_regulator_send uninitialized regulator error
> + *
> + * This unit test verifies that rtems_regulator_send() returns an
> + * error when the regulator argument is uninitialized.
> + */
> +static void test_regulator_send_uninitialized_regulator(void)
> +{
> + rtems_status_code sc;
> + rtems_regulator_instance regulator;
> + void *buffer;
> + size_t length;
> +
> + buffer = &length;
> + length = sizeof(size_t);
> +
> + (void) memset(®ulator, 0, sizeof(regulator));
> +
> + sc = rtems_regulator_send(®ulator, buffer, length);
> + rtems_test_assert(sc == RTEMS_INCORRECT_STATE);
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Verify rtems_regulator_send and output thread delivers message
> + *
> + * This unit test verifies that when the regulator is successfully initialized
> + * and used as expected, a message sent via rtems_regulator_send() is delivered as
> + * expected.
> + */
> +static void test_regulator_send_one_message_OK(void)
> +{
> + rtems_status_code sc;
> + rtems_regulator_instance *regulator;
> + char message[MAXIMUM_MESSAGE_LENGTH];
> + void *buffer;
> + size_t length;
> + int match;
> + rtems_regulator_attributes attributes = {
> + .deliverer = test_regulator_deliverer_logger,
> + .deliverer_context = &deliverer_logger_context,
> + .maximum_message_size = 16,
> + .maximum_messages = 10,
> + .output_thread_priority = 16,
> + .output_thread_stack_size = 0,
> + .output_thread_period = RTEMS_MILLISECONDS_TO_TICKS(250),
> + .maximum_to_dequeue_per_period = 3
> + };
> +
> + sc = rtems_regulator_create(&attributes, ®ulator);
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> + rtems_test_assert(regulator != NULL);
> +
> + deliverer_logger_context.regulator = regulator;
> +
> + delivered_messages_reset();
> +
> + sc = rtems_regulator_obtain_buffer(regulator, &buffer);
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> + rtems_test_assert(buffer != NULL);
> +
> + length = snprintf(message, MAXIMUM_MESSAGE_LENGTH, "message %d", 1024) + 1;
> + strcpy(buffer, message);
> +
> + sc = rtems_regulator_send(regulator, buffer, length);
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> +
> + sleep(1);
> +
> + rtems_test_assert(delivered_message_count == 1);
> + match = strncmp(
> + delivered_messages[0].message,
> + message,
> + MAXIMUM_MESSAGE_LENGTH
> + );
> + rtems_test_assert(match == 0);
> +
> + sc = rtems_regulator_delete(regulator);
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> +
> + deliverer_logger_context.regulator = NULL;
> +}
> +
> +/**
> + * @ingroup RegulatorTests
> + * @brief Verify rtems_regulator_send and output thread delivers messages
> + *
> + * This unit test verifies that when the regulator is successfully initialized
> + * and used as expected, and multiple messages are sent via rtems_regulator_send()
> + * that they are delivered as expected.
> + */
> +#include <stdio.h>
> +static void test_regulator_send_multiple_messages_OK(void)
> +{
> + rtems_status_code sc;
> + rtems_regulator_instance *regulator;
> + char message[MAXIMUM_MESSAGE_LENGTH];
> + void *buffer;
> + size_t length;
> + int match;
> + int i;
> + time_t base_time;
> + time_t tmp_time;
> + rtems_interval base_ticks;
> + rtems_interval ticks;
> + rtems_interval ticks_per_second;
> +
> + rtems_regulator_attributes attributes = {
> + .deliverer = test_regulator_deliverer_logger,
> + .deliverer_context = &deliverer_logger_context,
> + .maximum_message_size = MAXIMUM_MESSAGE_LENGTH,
> + .maximum_messages = 10,
> + .output_thread_priority = 16,
> + .output_thread_stack_size = 0,
> + .output_thread_period = RTEMS_MILLISECONDS_TO_TICKS(1000),
> + .maximum_to_dequeue_per_period = 2
> + };
> +
> + delivered_messages_reset();
> +
> + sc = rtems_regulator_create(&attributes, ®ulator);
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> + rtems_test_assert(regulator != NULL);
> +
> + deliverer_logger_context.regulator = regulator;
> +
> + /*
> + * Ensure the messages are sent on a second boundary to ensure the
> + * output thread will process them as expected.
> + */
> + tmp_time = time(NULL);
> + do {
> + base_time = time(NULL);
> + } while (tmp_time == base_time);
> +
> + /**
> + * Send five messages as a burst which will need to be smoothly sent at
> + * the configured rate.
> + */
> + for (i=1 ; i <= 5 ; i++) {
> + sc = rtems_regulator_obtain_buffer(regulator, &buffer);
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> + rtems_test_assert(buffer != NULL);
> +
> + length = snprintf(message, MAXIMUM_MESSAGE_LENGTH, "message %d", i);
> + strcpy(buffer, message);
> +
> + sc = rtems_regulator_send(regulator, buffer, length);
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> + }
> +
> + /*
> + * Let the output thread executed and deliver the messages.
> + */
> + sleep(5);
> +
> + /**
> + * Ensure the five messages were delivered as follows:
> + *
> + * - deliver all 5
> + * - contents are "message N" where N is 1 to 5
> + * - message 1 and 2 delivered during the first second
> + * - message 3 and 4 delivered during the second second
> + * - message 5 delivered during the third second
> + * - no further messages delivered
> + */
> +
> + rtems_test_assert(delivered_message_count == 5);
> +
> + for (i=0 ; i < 5 ; i++) {
> + (void) snprintf(message, MAXIMUM_MESSAGE_LENGTH, "message %d", i+1);
> +// printf("%d %s\n", i, delivered_messages[i].message);
> + match = strncmp(
> + delivered_messages[i].message,
> + message,
> + MAXIMUM_MESSAGE_LENGTH
> + );
> + rtems_test_assert(match == 0);
> + }
> +
> + /**
> + * Verify that messages were delivered in the proper groups. Check that
> + * the delivery time matches expectations.
> + */
> + rtems_test_assert(delivered_messages[0].processed == delivered_messages[1].processed);
> + rtems_test_assert(delivered_messages[1].processed != delivered_messages[2].processed);
> + rtems_test_assert(delivered_messages[2].processed == delivered_messages[3].processed);
> + rtems_test_assert(delivered_messages[3].processed != delivered_messages[4].processed);
> +
> + /**
> + * Verify that the message groups were properly spaced temporally. They
> + * should be one second apart.
> + */
> + ticks_per_second = rtems_clock_get_ticks_per_second();
> +
> + base_ticks = delivered_messages[1].processed;
> + ticks = delivered_messages[2].processed;
> + rtems_test_assert(ticks_per_second == ticks - base_ticks);
> +
> + base_ticks = delivered_messages[3].processed;
> + ticks = delivered_messages[4].processed;
> + rtems_test_assert(ticks_per_second == ticks - base_ticks);
> +
> + sc = rtems_regulator_delete(regulator);
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> +
> + deliverer_logger_context.regulator = NULL;
> +}
> +
> +/* Necessary prototype */
> +rtems_task test_regulator(rtems_task_argument);
> +
> +/**
> + * @ingroup RegulatorTestshttps://devel.rtems.org/milestone/6.1
> + * @brief Test entry task which invokes test cases
> + */
> +rtems_task test_regulator(rtems_task_argument arg)
> +{
> + (void) arg;
> +
> + TEST_BEGIN();
> +
> + malloc_trigger_reset();
> +
> + test_regulator_create_max_dequeue_zero();
> + test_regulator_create_null_attributes();
> + test_regulator_create_null_regulator();
> + test_regulator_create_deliverer_is_null();
> + test_regulator_create_maximum_messages_is_zero();
> + test_regulator_create_maximum_message_size_is_zero();
> + test_regulator_create_maximum_to_dequeue_per_period_is_zero();
> + test_regulator_create_malloc_regulator_fails();
> + test_regulator_create_malloc_buffers_fails();
> + test_regulator_create_output_thread_priority_is_zero();
> + test_regulator_create_partition_create_fails();
> + test_regulator_create_message_queue_create_fails();
> + test_regulator_create_task_create_fails();
> + test_regulator_create_rate_monotonic_create_fails();
> +
> + test_regulator_delete_null_regulator();
> + test_regulator_delete_uninitialized_regulator();
> + test_regulator_delete_OK();
> +
> + test_regulator_obtain_buffer_null_regulator();
> + test_regulator_obtain_buffer_uninitialized_regulator();
> + test_regulator_obtain_buffer_OK();
> +
> + test_regulator_release_buffer_null_regulator();
> + test_regulator_release_buffer_uninitialized_regulator();
> + test_regulator_release_buffer_OK();
> +
> + test_regulator_send_null_regulator();
> + test_regulator_send_null_message();
> + test_regulator_send_length_is_zero();
> + test_regulator_send_uninitialized_regulator();
> +
> + test_regulator_send_one_message_OK();
> +
> + test_regulator_send_multiple_messages_OK();
> +
> + TEST_END();
> +
> + rtems_test_exit(0);
> +}
> diff --git a/testsuites/libtests/regulator01/regulator01.doc b/testsuites/libtests/regulator01/regulator01.doc
> new file mode 100644
> index 0000000000..5685e4b857
> --- /dev/null
> +++ b/testsuites/libtests/regulator01/regulator01.doc
> @@ -0,0 +1,68 @@
> +# 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 output thread delivers messages
> +
> diff --git a/testsuites/libtests/regulator01/rtems_config.c b/testsuites/libtests/regulator01/rtems_config.c
> new file mode 100644
> index 0000000000..ca96e1b1dd
> --- /dev/null
> +++ b/testsuites/libtests/regulator01/rtems_config.c
> @@ -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>
More information about the devel
mailing list