<div dir="ltr"><div dir="ltr">Comments are interspersed but I am starting another thread for discussion of delete.<div><br></div><div>Your comments made me realize something we both missed and it will take a bit to address it.</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, Jul 18, 2023 at 9:32 PM Chris Johns <<a href="mailto:chrisj@rtems.org" target="_blank">chrisj@rtems.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><br>
<br>
On 15/7/2023 9:48 am, Joel Sherrill wrote:<br>
> Updates #4924.<br>
> <br>
> The Regulator is an application support class which is used to<br>
> deal with the scenario where there is a bursty input source<br>
> which needs to be metered out to a destination sink. The maximum<br>
> size of bursts needs to be known and the delivery method must<br>
> be configured to deliver messages at a rate that allows the<br>
> traffic to not overflow.<br>
> ---<br>
> cpukit/include/rtems/regulator.h | 439 ++++++<br>
> cpukit/include/rtems/regulatorimpl.h | 103 ++<br>
> cpukit/libmisc/regulator/regulator.c | 522 ++++++++<br>
> spec/build/cpukit/librtemscpu.yml | 2 +<br>
> spec/build/cpukit/objregulator.yml | 18 +<br>
> spec/build/testsuites/libtests/grp.yml | 2 +<br>
> .../build/testsuites/libtests/regulator01.yml | 21 +<br>
> testsuites/libtests/regulator01/regulator01.c | 1192 +++++++++++++++++<br>
> .../libtests/regulator01/regulator01.doc | 68 +<br>
> .../libtests/regulator01/rtems_config.c | 59 +<br>
> 10 files changed, 2426 insertions(+)<br>
> create mode 100644 cpukit/include/rtems/regulator.h<br>
> create mode 100644 cpukit/include/rtems/regulatorimpl.h<br>
> create mode 100644 cpukit/libmisc/regulator/regulator.c<br>
> create mode 100644 spec/build/cpukit/objregulator.yml<br>
> create mode 100644 spec/build/testsuites/libtests/regulator01.yml<br>
> create mode 100644 testsuites/libtests/regulator01/regulator01.c<br>
> create mode 100644 testsuites/libtests/regulator01/regulator01.doc<br>
> create mode 100644 testsuites/libtests/regulator01/rtems_config.c<br>
> <br>
> diff --git a/cpukit/include/rtems/regulator.h b/cpukit/include/rtems/regulator.h<br>
> new file mode 100644<br>
> index 0000000000..74f0d00f2e<br>
> --- /dev/null<br>
> +++ b/cpukit/include/rtems/regulator.h<br>
> @@ -0,0 +1,439 @@<br>
> +/* SPDX-License-Identifier: BSD-2-Clause */<br>
> +<br>
> +/**<br>
> + * @defgroup RegulatorAPI Regulator API<br>
> + *<br>
> + * @brief Regulator APIs<br>
> + *<br>
> + * The Regulator provides a set of APIs to manage input sources which <br>
> + * produces bursts of message traffic.<br>
> + */<br>
> +<br>
> +/**<br>
> + * @mainpage<br>
> + *<br>
> + * The regulator is designed to sit logically between two entities -- a<br>
> + * source and a sink/destination, where it limits the traffic sent to the<br>
> + * destination to prevent it from being flooded with messages from the<br>
> + * source. This can be used to accommodate bursts of input from a source<br>
> + * and meter it out to a destination. The maximum number of messages<br>
> + * which can be buffered in the regulator is specified by the<br>
> + * @a maximum_messages field in the @a rtems_regulator_attributes<br>
> + * structure passed as an argument to @a rtems_regulator_create().<br>
> + *<br>
> + * The regulator library accepts an input stream of messages from a<br>
> + * source and delivers them to a destination. The regulator assumes that the<br>
> + * input stream from the source contains sporadic bursts of data which can<br>
> + * exceed the acceptable rate of the destination. By limiting the message rate,<br>
> + * the regulator prevents an overflow of messages.<br>
> + *<br>
> + * The regulator can be configured for the input buffering required to manage<br>
> + * the maximum burst and for the metering rate for the output. The output rate<br>
> + * is in messages per second. If the sender produces data too fast, the<br>
> + * regulator will buffer the configured number of messages.<br>
> + *<br>
> + * A configuration capability is provided to allow for adaptation to different<br>
> + * message streams. The regulator can also support running multiple instances,<br>
> + * which could be used on independent message streams.<br>
> + *<br>
> + * The regulator provides a simple interface to the application for avoiding<br>
> + * bursts of input from a fast source overflowing a slower output sink.<br>
> + *<br>
> + * It is assumed that the application has a design limit on the number of<br>
> + * messages which may be buffered. All messages accepted by the regulator,<br>
> + * assuming no overflow on input, will eventually be output by the delivery<br>
> + * thread.<br>
> + *<br>
> + * A regulator instance is used as follows:<br>
> + *<br>
> + * @code<br>
> + * while (1)<br>
> + * use rtems_regulator_obtain_buffer to obtain a buffer<br>
> + * input operation to fetch data into the buffer<br>
> + * rtems_regulator_send(buffer, size of message)<br>
> + * // rtems_regulator_send() will release the buffer automatically when done<br>
<br>
OK however ...<br></blockquote><div><br></div><div>Comment is out of date with the addition of the delivery function returning a bool.</div><div><br></div><div>I noticed a few other places like this while starting the chapter for the manual.</div><div><br></div><div>Fixed.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
> + * @endcode<br>
> + *<br>
> + * The sequence diagram below shows the interaction between a message Source,<br>
> + * a Regulator instance, and RTEMS, given the usage described in the above<br>
> + * paragraphs.<br>
> + *<br>
> + * \startuml "Regulator Application Input Source Usage"<br>
> + * Source -> Regulator : rtems_regulator_obtain_buffer(regulator, buffer)<br>
> + * Regulator -> RTEMS : rtems_partition_get_buffer(id, buffer)<br>
> + * RTEMS --> Regulator : rtems_status_code<br>
> + * Regulator --> Source : rtems_status_code<br>
> + * Source -> Regulator : rtems_regulator_send(regulator, message, length)<br>
> + * Regulator -> RTEMS : rtems_message_queue_send(id, message, size)<br>
> + * RTEMS --> Regulator : rtems_status_code<br>
> + * Regulator --> Source : rtems_status_code<br>
> + * \enduml<br>
> + *<br>
> + * As illustrated in the sequence diagram, the Source usually corresponds<br>
> + * to application software reading a system input. The Source obtains a<br>
> + * buffer from the Regulator instance and fills it with incoming data.<br>
> + * The application explicitly obtaining a buffer and filling it in allows<br>
> + * for zero copy operations on the Source side.<br>
> + *<br>
> + * The Source then sends the buffer to the Regulator instance. The Regulator<br>
> + * the sends the buffer via a message queue which to the Delivery thread.<br>
> + * The Delivery thread executes periodically at a rate specified at<br>
> + * Regulation creation. At each period, the Delivery thread attempts to<br>
> + * receive up to a configured number of buffers and invoke the Delivery<br>
> + * function to deliver them to the Sink.<br>
> + *<br>
> + * The Delivery function is provided by the application for this specific<br>
> + * Regulator instance. Depending on the Sink, it may use a method which<br>
> + * copies the buffer contents (e.g., write()) or which operates directly<br>
> + * on the buffer contents (e.g. DMA from buffer). In the case of a <br>
> + * Sink which copies the buffer contents, the buffer can be released<br>
> + * via @a rtems_regulator_release_buffer() as soon as the method or copying<br>
> + * completes. In the case where the delivery uses the buffer and returns,<br>
> + * the call to @a rtems_regulator_release_buffer() will occur when the<br>
> + * use of the buffer is complete (e.g. completion of DMA transfer).<br>
> + * This explicit and deliverate exposure of buffering provides the<br>
> + * application with the ability to avoid copying the contents.<br>
> + *<br>
> + * After the Source has sent the message to the Regulator instance,<br>
> + * the Source is free to process another input and the Regulator<br>
> + * instance will ensure that the buffer is delivered to the Delivery<br>
> + * function and Sink.<br>
> + *<br>
> + * The Regulator implementation uses the RTEMS Classic API Partition Manager<br>
> + * to manage the buffer pool and the RTEMS Classic API Message Queue<br>
> + * Manager to send the buffer to the Delivery thread.<br>
> + * Destination by the internal Delivery thread.<br>
> + */<br>
> +<br>
> +/**<br>
> + * @addtogroup RegulatorAPI<br>
> + *<br>
> + * @file<br>
> + *<br>
> + * @brief This header file defines the Regulator API.<br>
> + *<br>
> + */<br>
> +<br>
> +/*<br>
> + * Copyright (C) 2022 On-Line Applications Research Corporation (OAR)<br>
> + *<br>
> + * Redistribution and use in source and binary forms, with or without<br>
> + * modification, are permitted provided that the following conditions<br>
> + * are met:<br>
> + * 1. Redistributions of source code must retain the above copyright<br>
> + * notice, this list of conditions and the following disclaimer.<br>
> + * 2. Redistributions in binary form must reproduce the above copyright<br>
> + * notice, this list of conditions and the following disclaimer in the<br>
> + * documentation and/or other materials provided with the distribution.<br>
> + *<br>
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"<br>
> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE<br>
> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE<br>
> + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE<br>
> + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br>
> + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF<br>
> + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS<br>
> + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN<br>
> + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)<br>
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE<br>
> + * POSSIBILITY OF SUCH DAMAGE.<br>
> + */<br>
> +<br>
> +#ifndef REGULATOR_H<br>
> +#define REGULATOR_H<br>
> +<br>
> +#include <stdlib.h><br>
> +<br>
> +#include <rtems.h><br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorAPI<br>
> + *<br>
> + * @brief Regulator Delivery Function Type<br>
> + *<br>
> + * The user provides a function which is invoked to deliver a message<br>
> + * to the output. It is invoked by the output thread created as part<br>
> + * of @a rtems_regulator_create(). The priority and stack size of the<br>
> + * output thread are specified in the regulator attribute set.<br>
> + *<br>
> + * It takes three parameters:<br>
> + *<br>
> + * @param[in] context is an untyped pointer to a user context<br>
> + * @param[in] message points to the message<br>
> + * @param[in] length is the message size<br>
> + *<br>
> + * The following is an example deliverer function. It assumes that the<br>
> + * application has defined the my_context_t structure and it has at least<br>
> + * the socket field. The @a message passed in originated with an<br>
> + * application source which obtained the @a message buffer using<br>
> + * @a rtems_regulator_obtain_buffer(), filled it in with source data,<br>
> + * and used @a rtems_regulator_send() to hand to the regulator instance<br>
> + * for later delivery.<br>
> + *<br>
> + * @code<br>
> + * bool my_deliverer(<br>
> + * void *context,<br>
> + * void *message,<br>
> + * size_t length<br>
> + * )<br>
> + * {<br>
> + * my_context_t *my_context;<br>
> + *<br>
> + * my_context = (my_context_t *)context;<br>
> + *<br>
> + * write(my_context->socket, message, length);<br>
> + * rtems_regulator_release_buffer(message);<br>
<br>
... you have this?<br></blockquote><div><br></div><div> Note that just below this, it says return false to indicate that it</div><div>released the buffer. Returning true tells the delivery thread to</div><div>do it.</div><div><br></div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
<br>
Chris<br>
<br>
> + * // return false to indicate we released the buffer<br>
> + * return false;<br>
> + * }<br>
> + * @endcode<br>
> + *<br>
> + * The delivery function returns true to indicate that the delivery thread<br>
> + * should release the buffer or false to indicate that it released the buffer.<br>
> + * If the delivery function invokes a method like @a write() to deliver the<br>
> + * message to the sink, then the buffer can be released immediately after the<br>
> + * call. If the delivery method does something like setting up a DMA transfer<br>
> + * of the buffer, it cannot be released until after the DMA is complete.<br>
> + *<br>
> + * The following sequence diagram shows the behavior of the Delivery thread<br>
> + * body and its interaction with the user-supplied deliverer() function.<br>
> + *<br>
> + * \startuml "Regulator Delivery Thread Body"<br>
> + * loop while (1)<br>
> + * "Delivery Thread" -> RTEMS : rtems_rate_monotonic_period(id, output_thread_period)<br>
> + * loop for 0 : maximum_to_dequeue_per_period<br>
> + * "Delivery Thread" -> RTEMS : rtems_message_queue_receive(id, message, size, wait, 0)<br>
> + * RTEMS --> "Delivery Thread" : rtems_status_code<br>
> + * group if [rtems_status_code != RTEMS_SUCCESSFUL]<br>
> + * RTEMS -> "Delivery Thread" : break<br>
> + * end<br>
> + * "Delivery Thread" -> Application : deliverer(context, buffer, length)<br>
> + * "Delivery Thread" -> RTEMS : rtems_partition_return_buffer(id, buffer)<br>
> + * RTEMS --> "Delivery Thread" : rtems_status_code<br>
> + * end<br>
> + * end<br>
> + * \enduml<br>
> + *<br>
> + * In the above sequence diagram, the key points are:<br>
> + *<br>
> + * -# The Delivery Thread Body is periodically executed.<br>
> + * -# During each period, up to the instance configuration parameter<br>
> + * @a maximum_to_dequeue_per_period may be dequeued and<br>
> + * passed the application's delivery function for processing.<br>
> + *<br>
> + * Note that the application explicitly obtains buffers from the<br>
> + * regulator instance but that the release may be done by Delivery<br>
> + * Thread, the Delivery function, or later when the buffer contents<br>
> + * are transferred.<br>
> + */<br>
> +typedef bool (*rtems_regulator_deliverer)(<br>
> + void *context,<br>
> + void *message,<br>
> + size_t length<br>
> +);<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorAPI<br>
> + *<br>
> + * @brief Attributes for Regulator Instance<br>
> + *<br>
> + * An instance of this structure must be populated by the application<br>
> + * before creating an instance of the regulator. These settings tailor<br>
> + * the behavior of the regulator instance.<br>
> + */<br>
> +typedef struct {<br>
> + /** Application method to invoke to output a message to the destination*/<br>
> + rtems_regulator_deliverer deliverer;<br>
> +<br>
> + /** Context pointer to pass to deliver method */<br>
> + void *deliverer_context;<br>
> +<br>
> + /** Maximum size message to process */<br>
> + size_t maximum_message_size;<br>
> +<br>
> + /** Maximum number of messages to be able to buffer */<br>
> + size_t maximum_messages;<br>
> +<br>
> + /** Priority of output thread */<br>
> + rtems_task_priority output_thread_priority;<br>
> +<br>
> + /** Stack size of output thread */<br>
> + size_t output_thread_stack_size;<br>
> +<br>
> + /** Period (in ticks) of output thread */<br>
> + rtems_interval output_thread_period;<br>
> +<br>
> + /** Maximum messages to dequeue per period */<br>
> + size_t maximum_to_dequeue_per_period;<br>
> +<br>
> +} rtems_regulator_attributes;<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorAPI<br>
> + *<br>
> + * @brief Regulator Instance<br>
> + */<br>
> +typedef void *rtems_regulator_instance;<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorAPI<br>
> + *<br>
> + * @brief Create a regulator<br>
> + *<br>
> + * This function creates an instance of a regulator. It uses the provided<br>
> + * @a attributes to create the instance return in @a regulator. This instance<br>
> + * will allocate the buffers associated with the regulator instance as well<br>
> + * as the Output Thread.<br>
> + *<br>
> + * The @a attributes structure defines the priority and stack size of<br>
> + * the output thread dedicated to this regulator instance. It also<br>
> + * defines the period of the output thread and the maximum number of<br>
> + * messages that may be delivered per period via invocation of the<br>
> + * delivery function. <br>
> + *<br>
> + * Each regulator instance will allocate the following resources:<br>
> + *<br>
> + * - A memory area for the regulator control block using @a malloc()<br>
> + * - A RTEMS Classic API Message Queue is constructed with message <br>
> + * buffer memory allocated using @a malloc(). Each message consists<br>
> + * of a pointer and a size_t value.<br>
> + * - A RTEMS Classic API Partition.<br>
> + * - A RTEMS Classic API Rate Monotonic Period.<br>
> + *<br>
> + * @param[in] attributes specify the regulator instance attributes<br>
> + * @param[inout] regulator will point to the regulator instance<br>
> + *<br>
> + * @return an RTEMS status code indicating success or failure.<br>
> + *<br>
> + * @note This method allocates memory for the buffers holding messages,<br>
> + * an output thread and an RTEMS partition. When it executes, the<br>
> + * output thread will create an RTEMS rate monotonic period.<br>
> + */<br>
> +rtems_status_code rtems_regulator_create(<br>
> + rtems_regulator_attributes *attributes,<br>
> + rtems_regulator_instance **regulator<br>
> +);<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorAPI<br>
> + *<br>
> + * @brief Delete a regulator<br>
> + *<br>
> + * This method is used to delete an instance of a @a regulator.<br>
> + *<br>
> + * It is the responsibility of the user to ensure that any resources<br>
> + * such as sockets or open file descriptors used by the delivery<br>
> + * function are also deleted. It is likely safer to delete those <br>
> + * delivery resources before deleting the regulator instance.<br>
> + *<br>
> + * @param[in] regulator is the instance to delete<br>
> + *<br>
> + * @return an RTEMS status code indicating success or failure.<br>
> + *<br>
> + * @note This method deallocates the resources allocated during<br>
> + * rtems_regulator_create().<br>
> + */<br>
> +rtems_status_code rtems_regulator_delete(<br>
> + rtems_regulator_instance *regulator<br>
> +);<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorAPI<br>
> + *<br>
> + * @brief Obtain Buffer from Regulator<br>
> + *<br>
> + * This method is used to obtain a buffer from the regulator's pool. The<br>
> + * @a buffer returned is assumed to be filled in with contents and used<br>
> + * in a subsequent call to @a rtems_regulator_send(). When the @a buffer is<br>
> + * delivered, it will be automatically released. If the @a buffer is not sent,<br>
> + * then it should be returned using @a rtems_regulator_release_buffer().<br>
> + *<br>
> + * The buffer is of the maximum_message_size specified in the attributes<br>
> + * passed in to @a rtems_regulator_create().<br>
> + *<br>
> + * @param[in] regulator is the regulator instance to operate upon<br>
> + * @param[out] buffer will point to the allocated buffer<br>
> + *<br>
> + * @return an RTEMS status code indicating success or failure.<br>
> + *<br>
> + * @note This method does not perform dynamic allocation. It obtains a<br>
> + * buffer from the pool allocated during @a rtems_regulator_create().<br>
> + *<br>
> + * @note Any attempt to write outside the buffer area is undefined.<br>
> + */<br>
> +rtems_status_code rtems_regulator_obtain_buffer(<br>
> + rtems_regulator_instance *regulator,<br>
> + void **buffer<br>
> +);<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorAPI<br>
> + *<br>
> + * @brief Release Previously Obtained Regulator Buffer<br>
> + *<br>
> + * This method is used to release a buffer from the regulator's pool. It is<br>
> + * assumed that the @a buffer returned will not be used by the application<br>
> + * anymore. The @a buffer must have previously been allocated by<br>
> + * @a rtems_regulator_obtain_buffer() and NOT passed to<br>
> + * @a rtems_regulator_send().<br>
> + *<br>
> + * If the @a rtems_regulator_send() is successful, the buffer will eventually<br>
> + * be processed by the delivery thread and automatically released.<br>
> + *<br>
> + * The status @a RTEMS_TOO_MANY is returned if the regulator's<br>
> + * internal queue is full. This indicates that the configured<br>
> + * maximum number of messages was insufficient. It is the<br>
> + * responsibility of the caller to decide whether to hold messages,<br>
> + * drop them, or print a message that the maximum number of messages<br>
> + * should be increased<br>
> + *<br>
> + * @param[in] regulator is the regulator instance to operate upon<br>
> + * @param[out] buffer will point to the buffer to release<br>
> + *<br>
> + * @return an RTEMS status code indicating success or failure.<br>
> + *<br>
> + * @note This method does not perform dynamic deallocation. It releases a<br>
> + * buffer to the pool allocated during @a rtems_regulator_create().<br>
> + */<br>
> +rtems_status_code rtems_regulator_release_buffer(<br>
> + rtems_regulator_instance *regulator,<br>
> + void *buffer<br>
> +);<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorAPI<br>
> + *<br>
> + * @brief Send to regulator instance<br>
> + *<br>
> + * This method is used by the producer to send a @a message to the<br>
> + * @a regulator for later delivery by the output thread. The message is<br>
> + * contained in the memory pointed to by @a message and is @a length<br>
> + * bytes in length.<br>
> + *<br>
> + * It is required that the @a message buffer was obtained via<br>
> + * @a rtems_regulator_obtain_buffer().<br>
> + *<br>
> + * It is assumed that the @a message buffer has been filled in with<br>
> + * application content to deliver<br>
> + *<br>
> + * If the @a rtems_regulator_send() is successful, the buffer is enqueued<br>
> + * inside the regulator instance for subsequent delivery. After the<br>
> + * @a message is delivered, the buffer is automatically freed.<br>
> + *<br>
> + * If @a rtems_regulator_send() is unsuccessful, it is the application's<br>
> + * responsibility to * release the buffer.<br>
> + *<br>
> + * @param[in] regulator is the regulator instance to operate upon<br>
> + * @param[out] message points to the message to deliver<br>
> + * @param[out] length is the size of the message in bytes<br>
> + *<br>
> + * @return an RTEMS status code indicating success or failure.<br>
> + *<br>
> + */<br>
> +rtems_status_code rtems_regulator_send(<br>
> + rtems_regulator_instance *regulator,<br>
> + void *message,<br>
> + size_t length<br>
> +);<br>
> +<br>
> +#endif /* REGULATOR_H */<br>
> diff --git a/cpukit/include/rtems/regulatorimpl.h b/cpukit/include/rtems/regulatorimpl.h<br>
> new file mode 100644<br>
> index 0000000000..f46f942d69<br>
> --- /dev/null<br>
> +++ b/cpukit/include/rtems/regulatorimpl.h<br>
> @@ -0,0 +1,103 @@<br>
> +/* SPDX-License-Identifier: BSD-2-Clause */<br>
> +<br>
> +/**<br>
> + * @defgroup RegulatorInternalAPI Regulator API Internals<br>
> + *<br>
> + * @brief Regulator Internal Information<br>
> + *<br>
> + * This concerns implementation information about the Regulator.<br>
> + */<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorInternalAPI<br>
> + *<br>
> + * @file<br>
> + *<br>
> + * @brief Regulator Library Implementation Support<br>
> + */<br>
> +<br>
> +/*<br>
> + * Copyright (C) 2022 On-Line Applications Research Corporation (OAR)<br>
> + *<br>
> + * Redistribution and use in source and binary forms, with or without<br>
> + * modification, are permitted provided that the following conditions<br>
> + * are met:<br>
> + * 1. Redistributions of source code must retain the above copyright<br>
> + * notice, this list of conditions and the following disclaimer.<br>
> + * 2. Redistributions in binary form must reproduce the above copyright<br>
> + * notice, this list of conditions and the following disclaimer in the<br>
> + * documentation and/or other materials provided with the distribution.<br>
> + *<br>
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"<br>
> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE<br>
> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE<br>
> + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE<br>
> + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br>
> + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF<br>
> + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS<br>
> + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN<br>
> + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)<br>
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE<br>
> + * POSSIBILITY OF SUCH DAMAGE.<br>
> + */<br>
> +<br>
> +#ifndef RTEMS_REGULATORIMPL_H<br>
> +#define RTEMS_REGULATORIMPL_H<br>
> +<br>
> +#include <rtems/chain.h><br>
> +<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorInternalAPI<br>
> + *<br>
> + * This constant is used to indicate the regulator instance is initialized.<br>
> + */<br>
> +#define REGULATOR_INITIALIZED 0xDeadF00d<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorInternalAPI<br>
> + *<br>
> + * @brief Regulator Message Instance Management Structure<br>
> + */<br>
> +typedef struct {<br>
> + /** This points to the message contents. */<br>
> + void *buffer;<br>
> + /** This is the length of the message. */<br>
> + size_t length;<br>
> +} _Regulator_Message_t;<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorInternalAPI<br>
> + *<br>
> + * @brief Regulator Instance Private Structure<br>
> + *<br>
> + * An instance of this structure is allocated per regulator instance.<br>
> + */<br>
> +typedef struct {<br>
> + /** Has magic value when instance is usable */<br>
> + uint32_t initialized;<br>
> +<br>
> + /** Attributes for this instance -- copied from user */<br>
> + rtems_regulator_attributes Attributes;<br>
> +<br>
> + /** Pointer to allocated message memory */<br>
> + void *message_memory;<br>
> +<br>
> + /** Pointer to allocated memory for RTEMS Message Queue for pending buffers*/<br>
> + void *message_queue_storage;<br>
> +<br>
> + /** RTEMS Message Queue of pending outgoing messages */<br>
> + rtems_id queue_id;<br>
> +<br>
> + /** RTEMS Partition for pool of unused messages */<br>
> + rtems_id messages_partition_id;<br>
> +<br>
> + /** RTEMS Task for performing output */<br>
> + rtems_id output_thread_id;<br>
> +<br>
> + /** Id of period used by output thread */<br>
> + rtems_id output_thread_period_id;<br>
> +<br>
> +} _Regulator_Control;<br>
> +<br>
> +#endif /* RTEMS_REGULATORIMPL_H */<br>
> diff --git a/cpukit/libmisc/regulator/regulator.c b/cpukit/libmisc/regulator/regulator.c<br>
> new file mode 100644<br>
> index 0000000000..5460590007<br>
> --- /dev/null<br>
> +++ b/cpukit/libmisc/regulator/regulator.c<br>
> @@ -0,0 +1,522 @@<br>
> +/* SPDX-License-Identifier: BSD-2-Clause */<br>
> +<br>
> +/**<br>
> + * @file<br>
> + *<br>
> + * @brief Regulator Library Implementation<br>
> + */<br>
> +<br>
> +/*<br>
> + * Copyright (C) 2022 On-Line Applications Research Corporation (OAR)<br>
> + *<br>
> + * Redistribution and use in source and binary forms, with or without<br>
> + * modification, are permitted provided that the following conditions<br>
> + * are met:<br>
> + * 1. Redistributions of source code must retain the above copyright<br>
> + * notice, this list of conditions and the following disclaimer.<br>
> + * 2. Redistributions in binary form must reproduce the above copyright<br>
> + * notice, this list of conditions and the following disclaimer in the<br>
> + * documentation and/or other materials provided with the distribution.<br>
> + *<br>
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"<br>
> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE<br>
> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE<br>
> + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE<br>
> + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br>
> + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF<br>
> + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS<br>
> + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN<br>
> + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)<br>
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE<br>
> + * POSSIBILITY OF SUCH DAMAGE.<br>
> + */<br>
> +<br>
> +#include <stdlib.h><br>
> +<br>
> +#include <rtems.h><br>
> +#include <rtems/regulator.h><br>
> +<br>
> +#include <rtems/regulatorimpl.h><br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorInternalAPI<br>
> + *<br>
> + * This method is the body for the task which delivers the output for<br>
> + * this regulator instance at the configured rate.<br>
> + *<br>
> + * @param[in] arg points to the regulator instance this thread<br>
> + * is associated with<br>
> + */<br>
> +static rtems_task _Regulator_Output_task_body(<br>
> + rtems_task_argument arg<br>
> +)<br>
> +{<br>
> + _Regulator_Control *the_regulator;<br>
> + rtems_status_code sc;<br>
> + size_t to_dequeue;<br>
> + _Regulator_Message_t regulator_message;<br>
> + size_t regulator_message_size;<br>
> + bool release_it;<br>
> +<br>
> + /*<br>
> + * The argument passed in cannot be NULL if the regulator_create worked.<br>
> + */<br>
> + the_regulator = (_Regulator_Control *) arg;<br>
> +<br>
> + /**<br>
> + * This thread uses a rate monotonic period object instance. A rate<br>
> + * monotonic period object must be created by the thread using it.<br>
> + * It can be deleted by any thread which simplifies clean up.<br>
> + *<br>
> + * The rate_monotonic_create() call can fail if the application<br>
> + * is incorrectly configured. This thread has no way to report the<br>
> + * failure. If it continues with an invalid id, then the thread will<br>
> + * not block on the period and spin continuously consuming CPU. The only<br>
> + * alternatives are to invoke rtems_fatal_error_occurred() or silently<br>
> + * exit the thread.<br>
> + */<br>
> + sc = rtems_rate_monotonic_create(<br>
> + rtems_build_name('P', 'E', 'R', 'D'),<br>
> + &the_regulator->output_thread_period_id<br>
> + );<br>
> + if (sc != RTEMS_SUCCESSFUL) {<br>
> + rtems_task_exit();<br>
> + }<br>
> +<br>
> + /**<br>
> + * Loop on the rate_monotonic_period() based on the specified period.<br>
> + */<br>
> + while (1) {<br>
> + sc = rtems_rate_monotonic_period(<br>
> + the_regulator->output_thread_period_id,<br>
> + the_regulator->Attributes.output_thread_period<br>
> + );<br>
> + _Assert_Unused_variable_equals(sc, RTEMS_SUCCESSFUL);<br>
> +<br>
> + /**<br>
> + * Loop for the configured number of messages to deliver per period.<br>
> + * If we reach the point, there are no more messages, block for the<br>
> + * rest of this period. If there are messages, deliver them.<br>
> + */<br>
> + for (to_dequeue = 0;<br>
> + to_dequeue < the_regulator->Attributes.maximum_to_dequeue_per_period;<br>
> + to_dequeue++) {<br>
> + regulator_message_size = sizeof(_Regulator_Message_t);<br>
> + sc = rtems_message_queue_receive(<br>
> + the_regulator->queue_id,<br>
> + ®ulator_message,<br>
> + ®ulator_message_size,<br>
> + RTEMS_NO_WAIT,<br>
> + 0<br>
> + );<br>
> + _Assert_Unused_variable_equals(sc, RTEMS_SUCCESSFUL);<br>
> + if (sc != RTEMS_SUCCESSFUL) {<br>
> + break;<br>
> + }<br>
> +<br>
> + release_it = the_regulator->Attributes.deliverer(<br>
<br>
Does this mean the releasing is optional?<br></blockquote><div><br></div><div>Not as much optional as allows it to be deferred. If the delivery function</div><div>is setitng up a DMA transfer, it can set that up and return immediately.</div><div>When the transfer is complete, it can release the buffer.</div><div><br></div><div>This scenario is actually discussed in a comment a few lines below. :)</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
> + the_regulator->Attributes.deliverer_context,<br>
> + regulator_message.buffer,<br>
> + regulator_message.length<br>
> + );<br>
> +<br>
> + /**<br>
> + * The message was successfully delivered. If the delivery function<br>
> + * wants the buffer returned, do it now. The delivery to the sink<br>
> + * may involve handing the buffer off to something like DMA<br>
> + * and need to wait for it to complete before releasing the buffer.<br>
> + *<br>
> + * Note that this is the underlying RTEMS service<br>
> + * used by @a rtems_regulator_obtain_buffer() and @a<br>
> + * rtems_regulator_release_buffer().<br>
> + */<br>
> + if (release_it == true) {<br>
> + sc = rtems_partition_return_buffer(<br>
> + the_regulator->messages_partition_id,<br>
> + regulator_message.buffer<br>
> + );<br>
> + _Assert_Unused_variable_equals(sc, RTEMS_SUCCESSFUL);<br>
> + }<br>
> + }<br>
> + }<br>
> +<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorInternalAPI<br>
> + *<br>
> + * This method frees the resources associated with a regulator instance.<br>
> + * The resources are freed in the opposite of the order in which they are<br>
> + * allocated. This is used on error cases in @a rtems_regulator_create() and in<br>
> + * @a rtems_regulator_delete().<br>
> + *<br>
> + * @param[in] the_regulator is the instance to operate upon<br>
> + */<br>
> +static void _Regulator_Free_helper(<br>
> + _Regulator_Control *the_regulator<br>
> +)<br>
> +{<br>
> + (void) rtems_rate_monotonic_delete(the_regulator->output_thread_period_id);<br>
> +<br>
> + (void) rtems_task_delete(the_regulator->output_thread_id);<br>
> +<br>
> + /*<br>
> + * The regulator's message_queue_storage is implicitly freed by this call.<br>
> + */<br>
> + (void) rtems_message_queue_delete(the_regulator->queue_id);<br>
<br>
I am not comfortable with these deletes. What happens if the delivery handler<br>
has been called and has not returned and it holds the allocator mutex, a<br>
networking mutex or some other resource?<br>
<br>
I think you need to ask the delivery thread to exit and you need to wait for it<br>
to finish.<br></blockquote><div><br></div><div>Another thread for this one. One issue neither of us caught is that you can't </div><div>delete a partition with buffers outstanding. In order to delete the regulator, </div><div>you will have to coordinate it having all buffers released. I have a couple of</div><div>ideas on this and will start another thread. </div><div> <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
Chris<br>
<br>
> +<br>
> + (void) rtems_partition_delete(the_regulator->messages_partition_id);<br>
> +<br>
> + if (the_regulator->message_memory) {<br>
> + free(the_regulator->message_memory);<br>
> + }<br>
> +<br>
> + the_regulator->initialized = 0;<br>
> + free(the_regulator);<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorInternalAPI<br>
> + */<br>
> +rtems_status_code rtems_regulator_create(<br>
> + rtems_regulator_attributes *attributes,<br>
> + rtems_regulator_instance **regulator<br>
> +)<br>
> +{<br>
> + _Regulator_Control *the_regulator;<br>
> + rtems_status_code sc;<br>
> + size_t alloc_size;<br>
> +<br>
> + /**<br>
> + * Perform basic validation of parameters<br>
> + */<br>
> + if (!attributes) {<br>
> + return RTEMS_INVALID_ADDRESS;<br>
> + }<br>
> +<br>
> + if (!regulator) {<br>
> + return RTEMS_INVALID_ADDRESS;<br>
> + }<br>
> +<br>
> + /**<br>
> + * Verify attributes are OK. Some are checked by calls to object create<br>
> + * methods. Specifically the following are not checked:<br>
> + *<br>
> + * - output_thread_priority by rtems_task_create()<br>
> + * - output_thread_stack_size can be any value<br>
> + */<br>
> + if (attributes->deliverer == NULL) {<br>
> + return RTEMS_INVALID_ADDRESS;<br>
> + }<br>
> +<br>
> + if (attributes->maximum_messages == 0) {<br>
> + return RTEMS_INVALID_NUMBER;<br>
> + }<br>
> +<br>
> + if (attributes->maximum_message_size == 0) {<br>
> + return RTEMS_INVALID_SIZE;<br>
> + }<br>
> +<br>
> + if (attributes->maximum_to_dequeue_per_period == 0) {<br>
> + return RTEMS_INVALID_NUMBER;<br>
> + }<br>
> +<br>
> + if (attributes->output_thread_period == 0) {<br>
> + return RTEMS_INVALID_NUMBER;<br>
> + }<br>
> +<br>
> + /**<br>
> + * Allocate memory for regulator instance<br>
> + */<br>
> + the_regulator = (_Regulator_Control *) calloc(sizeof(_Regulator_Control), 1);<br>
> + if (!the_regulator) {<br>
> + return RTEMS_NO_MEMORY;<br>
> + }<br>
> +<br>
> + /**<br>
> + * We do NOT want the output_thread_id field to be initialized to 0. If the<br>
> + * @a rtems_task_create() fails, then the field will not be overwritten.<br>
> + * This results in an attempt to rtems_task_delete(0) during clean<br>
> + * up. The thread ID of 0 is self which results in the calling thread<br>
> + * accidentally deleting itself.<br>
> + */<br>
> + the_regulator->output_thread_id = (rtems_id) -1;<br>
> +<br>
> + /**<br>
> + * Copy the attributes to an internal area for later use<br>
> + */<br>
> + the_regulator->Attributes = *attributes;<br>
> +<br>
> + /**<br>
> + * Allocate memory for the messages. There is no need to zero out the<br>
> + * message memory because the user should fill that in.<br>
> + */<br>
> + alloc_size = attributes->maximum_message_size * attributes->maximum_messages;<br>
> + the_regulator->message_memory = calloc(alloc_size, 1);<br>
> + if (!the_regulator->message_memory) {<br>
> + _Regulator_Free_helper(the_regulator);<br>
> + return RTEMS_NO_MEMORY;<br>
> + }<br>
> +<br>
> + /**<br>
> + * Associate message memory with a partition so allocations are atomic<br>
> + */<br>
> + sc = rtems_partition_create(<br>
> + rtems_build_name('P', 'O', 'O', 'L'),<br>
> + the_regulator->message_memory,<br>
> + alloc_size,<br>
> + attributes->maximum_message_size,<br>
> + RTEMS_DEFAULT_ATTRIBUTES,<br>
> + &the_regulator->messages_partition_id<br>
> + );<br>
> + if (sc != RTEMS_SUCCESSFUL) {<br>
> + _Regulator_Free_helper(the_regulator);<br>
> + return sc;<br>
> + }<br>
> +<br>
> + /**<br>
> + * Create the message queue between the sender and output thread<br>
> + */<br>
> + RTEMS_MESSAGE_QUEUE_BUFFER(sizeof(_Regulator_Message_t)) regulator_message_t;<br>
> +<br>
> + size_t storage_size = sizeof(regulator_message_t) * attributes->maximum_messages;<br>
> +<br>
> + the_regulator->message_queue_storage = malloc(storage_size);<br>
> + if (the_regulator->message_queue_storage == NULL) {<br>
> + _Regulator_Free_helper(the_regulator);<br>
> + return RTEMS_NO_MEMORY;<br>
> + }<br>
> +<br>
> + rtems_message_queue_config mq_config = {<br>
> + .name = rtems_build_name('S', 'N', 'D', 'Q'),<br>
> + .maximum_pending_messages = attributes->maximum_messages,<br>
> + .maximum_message_size = sizeof(_Regulator_Message_t),<br>
> + .storage_area = the_regulator->message_queue_storage,<br>
> + .storage_size = storage_size,<br>
> + .storage_free = free,<br>
> + .attributes = RTEMS_DEFAULT_ATTRIBUTES<br>
> + };<br>
> + sc = rtems_message_queue_construct(<br>
> + &mq_config,<br>
> + &the_regulator->queue_id<br>
> + );<br>
> + if (sc != RTEMS_SUCCESSFUL) {<br>
> + _Regulator_Free_helper(the_regulator);<br>
> + return sc;<br>
> + }<br>
> +<br>
> + /**<br>
> + * @note A rate monotonic period object must be created by the thread<br>
> + * using it. Thus that specific create operation is not included<br>
> + * in this method. All other resources are allocated here.<br>
> + */<br>
> +<br>
> + /**<br>
> + * Create the output thread Using the priority and stack size attributes<br>
> + * specified by the user.<br>
> + */<br>
> + sc = rtems_task_create(<br>
> + rtems_build_name('R', 'E', 'G', 'U'),<br>
> + attributes->output_thread_priority,<br>
> + attributes->output_thread_stack_size,<br>
> + RTEMS_DEFAULT_MODES,<br>
> + RTEMS_DEFAULT_ATTRIBUTES,<br>
> + &the_regulator->output_thread_id<br>
> + );<br>
> + if (sc != RTEMS_SUCCESSFUL) {<br>
> + _Regulator_Free_helper(the_regulator);<br>
> + return sc;<br>
> + }<br>
> +<br>
> + /**<br>
> + * Start the output thread.<br>
> + *<br>
> + * @note There should be no way this call can fail. The task id is valid,<br>
> + * the regulator output thread entry point is valid, and the argument<br>
> + * is valid.<br>
> + */<br>
> + sc = rtems_task_start(<br>
> + the_regulator->output_thread_id,<br>
> + _Regulator_Output_task_body,<br>
> + (rtems_task_argument) the_regulator<br>
> + );<br>
> + _Assert_Unused_variable_equals(sc, RTEMS_SUCCESSFUL);<br>
> +<br>
> + /**<br>
> + * The regulator is successfully initialized. Set the initialized field<br>
> + * to reflect this and return the instance pointer.<br>
> + */<br>
> + the_regulator->initialized = REGULATOR_INITIALIZED;<br>
> +<br>
> + *regulator = (void *)the_regulator;<br>
> +<br>
> + return RTEMS_SUCCESSFUL;<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorInternalAPI<br>
> + */<br>
> +rtems_status_code rtems_regulator_delete(<br>
> + rtems_regulator_instance *regulator<br>
> +)<br>
> +{<br>
> + _Regulator_Control *the_regulator;<br>
> +<br>
> + the_regulator = (_Regulator_Control *) regulator;<br>
> +<br>
> + /**<br>
> + * Validate the arguments and ensure the regulator was successfully<br>
> + * initialized.<br>
> + */<br>
> + if (!the_regulator) {<br>
> + return RTEMS_INVALID_ADDRESS;<br>
> + }<br>
> +<br>
> + if (the_regulator->initialized != REGULATOR_INITIALIZED) {<br>
> + return RTEMS_INCORRECT_STATE;<br>
> + }<br>
> +<br>
> + /**<br>
> + * Free the resources associated with this regulator instance.<br>
> + */<br>
> + _Regulator_Free_helper(the_regulator);<br>
> +<br>
> + return RTEMS_SUCCESSFUL;<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorInternalAPI<br>
> + *<br>
> + * Allocate a buffer for the caller using the internal partition.<br>
> + */<br>
> +rtems_status_code rtems_regulator_obtain_buffer(<br>
> + rtems_regulator_instance *regulator,<br>
> + void **buffer<br>
> +)<br>
> +{<br>
> + _Regulator_Control *the_regulator;<br>
> + rtems_status_code sc;<br>
> +<br>
> + the_regulator = (_Regulator_Control *) regulator;<br>
> +<br>
> + /**<br>
> + * Validate the arguments and ensure the regulator was successfully<br>
> + * initialized.<br>
> + */<br>
> + if (!the_regulator) {<br>
> + return RTEMS_INVALID_ADDRESS;<br>
> + }<br>
> +<br>
> + if (the_regulator->initialized != REGULATOR_INITIALIZED) {<br>
> + return RTEMS_INCORRECT_STATE;<br>
> + }<br>
> +<br>
> + /**<br>
> + * Allocate a buffer for the user application from the buffer pool managed<br>
> + * by an Classic API partition.<br>
> + */<br>
> + sc = rtems_partition_get_buffer(<br>
> + the_regulator->messages_partition_id,<br>
> + buffer<br>
> + );<br>
> +<br>
> + return sc;<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorInternalAPI<br>
> + *<br>
> + * Allocate a buffer for the caller using the internal partition.<br>
> + */<br>
> +rtems_status_code rtems_regulator_release_buffer(<br>
> + rtems_regulator_instance *regulator,<br>
> + void *buffer<br>
> +)<br>
> +{<br>
> + _Regulator_Control *the_regulator;<br>
> + rtems_status_code sc;<br>
> +<br>
> + the_regulator = (_Regulator_Control *) regulator;<br>
> +<br>
> + /**<br>
> + * Validate the arguments and ensure the regulator was successfully<br>
> + * initialized.<br>
> + */<br>
> + if (!the_regulator) {<br>
> + return RTEMS_INVALID_ADDRESS;<br>
> + }<br>
> +<br>
> + if (the_regulator->initialized != REGULATOR_INITIALIZED) {<br>
> + return RTEMS_INCORRECT_STATE;<br>
> + }<br>
> +<br>
> + /**<br>
> + * Deallocate the buffer to the buffer pool managed by a Classic<br>
> + * API partition.<br>
> + */<br>
> + sc = rtems_partition_return_buffer(<br>
> + the_regulator->messages_partition_id,<br>
> + buffer<br>
> + );<br>
> +<br>
> + return sc;<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorInternalAPI<br>
> + */<br>
> +rtems_status_code rtems_regulator_send(<br>
> + rtems_regulator_instance *regulator,<br>
> + void *message,<br>
> + size_t length<br>
> +)<br>
> +{<br>
> + _Regulator_Control *the_regulator;<br>
> + rtems_status_code sc;<br>
> + _Regulator_Message_t regulator_message;<br>
> +<br>
> + the_regulator = (_Regulator_Control *) regulator;<br>
> +<br>
> + /**<br>
> + * Validate the arguments and ensure the regulator was successfully<br>
> + * initialized.<br>
> + */<br>
> + if (!message) {<br>
> + return RTEMS_INVALID_ADDRESS;<br>
> + }<br>
> +<br>
> + if (length == 0) {<br>
> + return RTEMS_INVALID_NUMBER;<br>
> + }<br>
> +<br>
> + if (!the_regulator) {<br>
> + return RTEMS_INVALID_ADDRESS;<br>
> + }<br>
> +<br>
> + if (the_regulator->initialized != REGULATOR_INITIALIZED) {<br>
> + return RTEMS_INCORRECT_STATE;<br>
> + }<br>
> +<br>
> + /**<br>
> + * Place the message pointer and length into a temporary structure. This<br>
> + * lets the implementation internally send the message by reference and<br>
> + * have a zero-copy implementation.<br>
> + */<br>
> + regulator_message.buffer = message;<br>
> + regulator_message.length = length;<br>
> +<br>
> + /**<br>
> + * Send the application message to the output thread for delivery using<br>
> + * a Classic API message queue.<br>
> + */<br>
> + sc = rtems_message_queue_send(<br>
> + the_regulator->queue_id,<br>
> + ®ulator_message,<br>
> + sizeof(_Regulator_Message_t)<br>
> + );<br>
> + if (sc != RTEMS_SUCCESSFUL) {<br>
> + return sc;<br>
> + }<br>
> +<br>
> + return sc;<br>
> +}<br>
> diff --git a/spec/build/cpukit/librtemscpu.yml b/spec/build/cpukit/librtemscpu.yml<br>
> index 3654e7f94a..4074ca1fcb 100644<br>
> --- a/spec/build/cpukit/librtemscpu.yml<br>
> +++ b/spec/build/cpukit/librtemscpu.yml<br>
> @@ -514,6 +514,8 @@ links:<br>
> uid: objmpci<br>
> - role: build-dependency<br>
> uid: objpci<br>
> +- role: build-dependency<br>
> + uid: objregulator<br>
> - role: build-dependency<br>
> uid: objpsxsgnl<br>
> - role: build-dependency<br>
> diff --git a/spec/build/cpukit/objregulator.yml b/spec/build/cpukit/objregulator.yml<br>
> new file mode 100644<br>
> index 0000000000..56d6154de9<br>
> --- /dev/null<br>
> +++ b/spec/build/cpukit/objregulator.yml<br>
> @@ -0,0 +1,18 @@<br>
> +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause<br>
> +build-type: objects<br>
> +cflags: []<br>
> +copyrights:<br>
> +- Copyright (C) 2023 OAR Corporatoin<br>
> +cppflags: []<br>
> +cxxflags: []<br>
> +enabled-by: true<br>
> +includes: []<br>
> +install:<br>
> +- destination: ${BSP_INCLUDEDIR}/rtems<br>
> + source:<br>
> + - cpukit/include/rtems/regulator.h<br>
> + - cpukit/include/rtems/regulatorimpl.h<br>
> +links: []<br>
> +source:<br>
> +- cpukit/libmisc/regulator/regulator.c<br>
> +type: build<br>
> diff --git a/spec/build/testsuites/libtests/grp.yml b/spec/build/testsuites/libtests/grp.yml<br>
> index be340c8ab6..7ca0f7dfa1 100644<br>
> --- a/spec/build/testsuites/libtests/grp.yml<br>
> +++ b/spec/build/testsuites/libtests/grp.yml<br>
> @@ -230,6 +230,8 @@ links:<br>
> uid: record01<br>
> - role: build-dependency<br>
> uid: record02<br>
> +- role: build-dependency<br>
> + uid: regulator01<br>
> - role: build-dependency<br>
> uid: rtmonuse<br>
> - role: build-dependency<br>
> diff --git a/spec/build/testsuites/libtests/regulator01.yml b/spec/build/testsuites/libtests/regulator01.yml<br>
> new file mode 100644<br>
> index 0000000000..776d0ae34b<br>
> --- /dev/null<br>
> +++ b/spec/build/testsuites/libtests/regulator01.yml<br>
> @@ -0,0 +1,21 @@<br>
> +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause<br>
> +build-type: test-program<br>
> +cflags: []<br>
> +copyrights:<br>
> +- Copyright (C) 2023 OAR Corporation<br>
> +cppflags: []<br>
> +cxxflags: []<br>
> +enabled-by: true<br>
> +features: c cprogram<br>
> +includes: []<br>
> +ldflags:<br>
> +- -Wl,--wrap=malloc<br>
> +links: []<br>
> +source:<br>
> +- testsuites/libtests/regulator01/regulator01.c<br>
> +- testsuites/libtests/regulator01/rtems_config.c<br>
> +stlib: []<br>
> +target: testsuites/libtests/regulator01.exe<br>
> +type: build<br>
> +use-after: []<br>
> +use-before: []<br>
> diff --git a/testsuites/libtests/regulator01/regulator01.c b/testsuites/libtests/regulator01/regulator01.c<br>
> new file mode 100644<br>
> index 0000000000..739af05fd6<br>
> --- /dev/null<br>
> +++ b/testsuites/libtests/regulator01/regulator01.c<br>
> @@ -0,0 +1,1192 @@<br>
> +/* SPDX-License-Identifier: BSD-2-Clause */<br>
> +<br>
> +/**<br>
> + * @defgroup RegulatorTests Regulator Test Cases<br>
> + *<br>
> + * @brief Unit test cases for the Regulator<br>
> + *<br>
> + * This is a set of unit test cases for the regulator.<br>
> + */<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + *<br>
> + * @file<br>
> + *<br>
> + * @brief Test 01 for Regulator Library<br>
> + */<br>
> +<br>
> +/*<br>
> + * Copyright (C) 2022 On-Line Applications Research Corporation (OAR)<br>
> + *<br>
> + * Redistribution and use in source and binary forms, with or without<br>
> + * modification, are permitted provided that the following conditions<br>
> + * are met:<br>
> + * 1. Redistributions of source code must retain the above copyright<br>
> + * notice, this list of conditions and the following disclaimer.<br>
> + * 2. Redistributions in binary form must reproduce the above copyright<br>
> + * notice, this list of conditions and the following disclaimer in the<br>
> + * documentation and/or other materials provided with the distribution.<br>
> + *<br>
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"<br>
> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE<br>
> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE<br>
> + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE<br>
> + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br>
> + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF<br>
> + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS<br>
> + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN<br>
> + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)<br>
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE<br>
> + * POSSIBILITY OF SUCH DAMAGE.<br>
> + */<br>
> +<br>
> +#include <stdio.h><br>
> +#include <string.h><br>
> +#include <unistd.h><br>
> +<br>
> +#include <rtems.h><br>
> +#include <rtems/test-info.h><br>
> +#include <tmacros.h><br>
> +<br>
> +#include <rtems/regulator.h><br>
> +<br>
> +/**<br>
> + * @brief Regulator Test Name<br>
> + */<br>
> +const char rtems_test_name[] = "Regulator 1";<br>
> +<br>
> +/*<br>
> + * Prototypes for wrapped functions<br>
> + */<br>
> +void *__wrap_malloc(size_t size);<br>
> +void *__real_malloc(size_t size);<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Calloc Wrapper Trigger Count<br>
> + */<br>
> +static int malloc_trigger_count;<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Calloc Wrapper Call Count<br>
> + */<br>
> +static int malloc_call_count;<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Calloc Wrapper Trigger enable<br>
> + */<br>
> +static bool malloc_trigger_enabled;<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Enable Calloc Wrapper Trigger<br>
> + */<br>
> +static void malloc_trigger_enable(<br>
> + int trigger_count<br>
> +)<br>
> +{<br>
> + malloc_trigger_enabled = true;<br>
> + malloc_trigger_count = trigger_count;<br>
> + malloc_call_count = 0;<br>
> +}<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Reset Calloc Wrapper Trigger and Count<br>
> + */<br>
> +static void malloc_trigger_reset(void)<br>
> +{<br>
> + malloc_trigger_enabled = 0;<br>
> + malloc_trigger_count = 0;<br>
> + malloc_call_count = 0;<br>
> +}<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Calloc Wrapper to Trigger Allocation Errors<br>
> + */<br>
> +void *__wrap_malloc(size_t size)<br>
> +{<br>
> + if (malloc_trigger_enabled) {<br>
> + malloc_call_count++;<br>
> + if (malloc_call_count == malloc_trigger_count) {<br>
> + return NULL;<br>
> + }<br>
> + }<br>
> +<br>
> + return __real_malloc(size);<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Empty Deliver Method for Testing<br>
> + */<br>
> +static bool test_regulator_deliverer(<br>
> + void *context,<br>
> + void *message,<br>
> + size_t length<br>
> +)<br>
> +{<br>
> + (void) context;<br>
> + (void) message;<br>
> + (void) length;<br>
> +<br>
> + return true;<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Maximum length of a test message that is delivered<br>
> + */<br>
> +#define MAXIMUM_MESSAGE_LENGTH 32<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Maximum number of test messages to buffer<br>
> + */<br>
> +#define MAXIMUM_MESSAGES_TO_BUFFER 10<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Structure for capturing messages as delivered<br>
> + */<br>
> +typedef struct {<br>
> + rtems_interval processed;<br>
> + char message[MAXIMUM_MESSAGE_LENGTH];<br>
> +} message_log_t;<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Set of Delivered messages<br>
> + */<br>
> +message_log_t delivered_messages[MAXIMUM_MESSAGES_TO_BUFFER];<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Count of Delivered messages<br>
> + */<br>
> +int delivered_message_count;<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Reset Delivered Message Set<br>
> + *<br>
> + * This is used at the beginning of a test case which is going to<br>
> + * check that message contents and delivery times were as expected.<br>
> + */<br>
> +static void delivered_messages_reset(void)<br>
> +{<br>
> + delivered_message_count = 0;<br>
> + memset(delivered_messages, 0xc5, sizeof(delivered_messages));<br>
> +}<br>
> +<br>
> +/**<br>
> + * @brief Context for Logger Delivery Function<br>
> + */<br>
> +typedef struct {<br>
> + /** Regulator instance being operated on */<br>
> + rtems_regulator_instance *regulator;<br>
> +} deliverer_logger_context_t;<br>
> +<br>
> +/**<br>
> + * @brief Context Instance for Logger Delivery Function<br>
> + */<br>
> +static deliverer_logger_context_t deliverer_logger_context;<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Empty Deliver Method for Testing<br>
> + *<br>
> + * This deliverer method implementation logs the messages along with<br>
> + * their time of arrival. This is used by the test cases to verify<br>
> + * proper delivery.<br>
> + */<br>
> +static bool test_regulator_deliverer_logger(<br>
> + void *context,<br>
> + void *message,<br>
> + size_t length<br>
> +)<br>
> +{<br>
> + (void) context;<br>
> +<br>
> + static bool caller_releases_buffer = true;<br>
> +<br>
> + size_t len;<br>
> + rtems_interval ticks;<br>
> +<br>
> + len = strnlen(message, MAXIMUM_MESSAGE_LENGTH) + 1;<br>
> + rtems_test_assert(len = length);<br>
> +<br>
> + ticks = rtems_clock_get_ticks_since_boot();<br>
> +<br>
> + delivered_messages[delivered_message_count].processed = ticks;<br>
> +<br>
> + strcpy(delivered_messages[delivered_message_count].message, message);<br>
> +<br>
> + delivered_message_count++;<br>
> +<br>
> + /*<br>
> + * Alternate releasing the buffer here and allowing the calling Delivery<br>
> + * Thread to do it. This increases coverage of that logic.<br>
> + */<br>
> + if (caller_releases_buffer == true) {<br>
> + caller_releases_buffer = false;<br>
> + return true;<br>
> + }<br>
> +<br>
> + return false;<br>
> +}<br>
> +<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Helper to create a Regulator instance<br>
> + *<br>
> + * This helper creates a regulator instance with some arbitrary attributes.<br>
> + * This is used in multiple test cases to have a valie regulator instance to<br>
> + * trigger error cases.<br>
> + */<br>
> +static rtems_regulator_instance *test_regulator_create_regulator_OK(void)<br>
> +{<br>
> + rtems_status_code sc;<br>
> + rtems_regulator_attributes attributes = {<br>
> + .deliverer = test_regulator_deliverer,<br>
> + .deliverer_context = NULL,<br>
> + .maximum_message_size = 16,<br>
> + .maximum_messages = 10,<br>
> + .output_thread_priority = 16,<br>
> + .output_thread_stack_size = 0,<br>
> + .output_thread_period = RTEMS_MILLISECONDS_TO_TICKS(1000),<br>
> + .maximum_to_dequeue_per_period = 3<br>
> + };<br>
> + rtems_regulator_instance *regulator;<br>
> +<br>
> + regulator = NULL;<br>
> +<br>
> + sc = rtems_regulator_create(&attributes, ®ulator);<br>
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
> + rtems_test_assert(regulator != NULL);<br>
> +<br>
> + return regulator;<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Verify rtems_regulator_create() maximum_to_dequeue_per_period<br>
> + * attributes error<br>
> + *<br>
> + * This unit test verifies that rtems_regulator_create() returns an error when<br>
> + * the maximum_to_dequeue_per_period attribute is zero.<br>
> + */<br>
> +static void test_regulator_create_max_dequeue_zero(void)<br>
> +{<br>
> + rtems_status_code sc;<br>
> + rtems_regulator_attributes attributes = {<br>
> + .deliverer = test_regulator_deliverer,<br>
> + .deliverer_context = NULL,<br>
> + .maximum_message_size = 16,<br>
> + .maximum_messages = 10,<br>
> + .output_thread_priority = 16,<br>
> + .output_thread_stack_size = 0,<br>
> + .output_thread_period = RTEMS_MILLISECONDS_TO_TICKS(1000),<br>
> + .maximum_to_dequeue_per_period = 0<br>
> + };<br>
> + rtems_regulator_instance *regulator;<br>
> +<br>
> + regulator = NULL;<br>
> +<br>
> + sc = rtems_regulator_create(&attributes, ®ulator);<br>
> + rtems_test_assert(sc == RTEMS_INVALID_NUMBER);<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Verify rtems_regulator_create() NULL attributes error<br>
> + *<br>
> + * This unit test verifies that rtems_regulator_create() returns an error when<br>
> + * the attributes argument is NULL.<br>
> + */<br>
> +static void test_regulator_create_null_attributes(void)<br>
> +{<br>
> + rtems_status_code sc;<br>
> + rtems_regulator_instance *regulator;<br>
> +<br>
> + sc = rtems_regulator_create(NULL, ®ulator);<br>
> + rtems_test_assert(sc == RTEMS_INVALID_ADDRESS);<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Verify rtems_regulator_create NULL regulator error<br>
> + *<br>
> + * This unit test verifies that rtems_regulator_create() returns an error when<br>
> + * the regulator argument is NULL.<br>
> + */<br>
> +static void test_regulator_create_null_regulator(void)<br>
> +{<br>
> + rtems_status_code sc;<br>
> + rtems_regulator_attributes attributes;<br>
> +<br>
> + sc = rtems_regulator_create(&attributes, NULL);<br>
> + rtems_test_assert(sc == RTEMS_INVALID_ADDRESS);<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Verify rtems_regulator_create deliverer is NULL<br>
> + *<br>
> + * This unit test verifies that rtems_regulator_create() returns an error when<br>
> + * the the attributes deliverer field is NULL.<br>
> + */<br>
> +static void test_regulator_create_deliverer_is_null(void)<br>
> +{<br>
> + rtems_status_code sc;<br>
> + rtems_regulator_attributes attributes;<br>
> + rtems_regulator_instance *regulator;<br>
> +<br>
> + (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));<br>
> +<br>
> + attributes.deliverer = NULL;<br>
> + attributes.maximum_messages = 0;<br>
> + attributes.maximum_message_size = 16;<br>
> + attributes.maximum_to_dequeue_per_period = 1;<br>
> +<br>
> + sc = rtems_regulator_create(&attributes, ®ulator);<br>
> + rtems_test_assert(sc == RTEMS_INVALID_ADDRESS);<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Verify rtems_regulator_create maximum_messages is 0 error<br>
> + *<br>
> + * This unit test verifies that rtems_regulator_create() returns an error when<br>
> + * the the attributes maximum_messages field is 0.<br>
> + */<br>
> +static void test_regulator_create_maximum_messages_is_zero(void)<br>
> +{<br>
> + rtems_status_code sc;<br>
> + rtems_regulator_attributes attributes;<br>
> + rtems_regulator_instance *regulator;<br>
> +<br>
> + (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));<br>
> +<br>
> + attributes.deliverer = test_regulator_deliverer;<br>
> + attributes.maximum_messages = 0;<br>
> + attributes.maximum_message_size = 16;<br>
> + attributes.maximum_to_dequeue_per_period = 1;<br>
> +<br>
> + sc = rtems_regulator_create(&attributes, ®ulator);<br>
> + rtems_test_assert(sc == RTEMS_INVALID_NUMBER);<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Verify rtems_regulator_create maximum_message_size is 0 error<br>
> + *<br>
> + * This unit test verifies that rtems_regulator_create() returns an error when<br>
> + * the the attributes maximum_message_size field is 0.<br>
> + */<br>
> +static void test_regulator_create_maximum_message_size_is_zero(void)<br>
> +{<br>
> + rtems_status_code sc;<br>
> + rtems_regulator_attributes attributes;<br>
> + rtems_regulator_instance *regulator;<br>
> +<br>
> + (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));<br>
> +<br>
> + attributes.deliverer = test_regulator_deliverer;<br>
> + attributes.maximum_messages = 10;<br>
> + attributes.maximum_message_size = 0;<br>
> + attributes.maximum_to_dequeue_per_period = 1;<br>
> +<br>
> + sc = rtems_regulator_create(&attributes, ®ulator);<br>
> + rtems_test_assert(sc == RTEMS_INVALID_SIZE);<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Verify rtems_regulator_create maximum_to_dequeue_per_period is 0 error<br>
> + *<br>
> + * This unit test verifies that rtems_regulator_create() returns an error when<br>
> + * the the attributes maximum_to_dequeue_per_period field is 0.<br>
> + */<br>
> +static void test_regulator_create_maximum_to_dequeue_per_period_is_zero(void)<br>
> +{<br>
> + rtems_status_code sc;<br>
> + rtems_regulator_attributes attributes;<br>
> + rtems_regulator_instance *regulator;<br>
> +<br>
> + (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));<br>
> +<br>
> + attributes.deliverer = test_regulator_deliverer;<br>
> + attributes.maximum_messages = 10;<br>
> + attributes.maximum_message_size = 0;<br>
> +<br>
> + sc = rtems_regulator_create(&attributes, ®ulator);<br>
> + rtems_test_assert(sc == RTEMS_INVALID_SIZE);<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Verify rtems_regulator_create returns error on failure to allocate regulator<br>
> + *<br>
> + * This unit test verifies that rtems_regulator_create() returns an error when<br>
> + * it is unable to allocate the mmemory for the regulator instance.<br>
> + */<br>
> +static void test_regulator_create_malloc_regulator_fails(void)<br>
> +{<br>
> + rtems_status_code sc;<br>
> + rtems_regulator_attributes attributes;<br>
> + rtems_regulator_instance *regulator;<br>
> +<br>
> + (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));<br>
> +<br>
> + attributes.deliverer = test_regulator_deliverer;<br>
> + attributes.maximum_messages = 10;<br>
> + attributes.maximum_message_size = 16;<br>
> + attributes.output_thread_priority = 32;<br>
> + attributes.maximum_to_dequeue_per_period = 1;<br>
> + attributes.output_thread_period = RTEMS_MILLISECONDS_TO_TICKS(1000);<br>
> +<br>
> + malloc_trigger_enable(1);<br>
> +<br>
> + sc = rtems_regulator_create(&attributes, ®ulator);<br>
> + rtems_test_assert(sc == RTEMS_NO_MEMORY);<br>
> +<br>
> + malloc_trigger_reset();<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Verify rtems_regulator_create returns error on failure to allocate buffers<br>
> + *<br>
> + * This unit test verifies that rtems_regulator_create() returns an error when<br>
> + * it is unable to allocate the mmemory for the regulator buffers.<br>
> + */<br>
> +static void test_regulator_create_malloc_buffers_fails(void)<br>
> +{<br>
> + rtems_status_code sc;<br>
> + rtems_regulator_attributes attributes;<br>
> + rtems_regulator_instance *regulator;<br>
> +<br>
> + (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));<br>
> +<br>
> + attributes.deliverer = test_regulator_deliverer;<br>
> + attributes.maximum_messages = 10;<br>
> + attributes.maximum_message_size = 16;<br>
> + attributes.output_thread_priority = 32;<br>
> + attributes.maximum_to_dequeue_per_period = 1;<br>
> + attributes.output_thread_period = RTEMS_MILLISECONDS_TO_TICKS(1000);<br>
> +<br>
> + malloc_trigger_enable(2);<br>
> +<br>
> + sc = rtems_regulator_create(&attributes, ®ulator);<br>
> + rtems_test_assert(sc == RTEMS_NO_MEMORY);<br>
> +<br>
> + malloc_trigger_reset();<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Verify rtems_regulator_create and delete work<br>
> + *<br>
> + * This unit test verifies that rtems_regulator_create() can successfully create<br>
> + * the the attributes output_thread_priority field is 0.<br>
> + */<br>
> +static void test_regulator_create_output_thread_priority_is_zero(void)<br>
> +{<br>
> + rtems_status_code sc;<br>
> + rtems_regulator_attributes attributes;<br>
> + rtems_regulator_instance *regulator;<br>
> +<br>
> + (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));<br>
> +<br>
> + attributes.deliverer = test_regulator_deliverer;<br>
> + attributes.maximum_messages = 10;<br>
> + attributes.maximum_message_size = 16;<br>
> + attributes.output_thread_priority = 0;<br>
> + attributes.maximum_to_dequeue_per_period = 1;<br>
> + attributes.output_thread_period = RTEMS_MILLISECONDS_TO_TICKS(1000);<br>
> +<br>
> + sc = rtems_regulator_create(&attributes, ®ulator);<br>
> + rtems_test_assert(sc == RTEMS_INVALID_PRIORITY);<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Verify rtems_regulator_create rtems_partition_create error<br>
> + *<br>
> + * This unit test verifies that rtems_regulator_create() correctly returns an<br>
> + * error when the call to rtems_partition_create() fails.<br>
> + */<br>
> +static void test_regulator_create_partition_create_fails(void)<br>
> +{<br>
> + rtems_status_code sc;<br>
> + rtems_id partition_id;<br>
> + unsigned long partition_area[16];<br>
> + rtems_regulator_attributes attributes;<br>
> + rtems_regulator_instance *regulator;<br>
> +<br>
> + sc = rtems_partition_create(<br>
> + rtems_build_name('T', 'P', 'T', 'P'),<br>
> + partition_area,<br>
> + 16 * sizeof(unsigned long),<br>
> + 2 * sizeof(unsigned long),<br>
> + RTEMS_DEFAULT_ATTRIBUTES,<br>
> + &partition_id<br>
> + );<br>
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
> +<br>
> + (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));<br>
> +<br>
> + attributes.deliverer = test_regulator_deliverer;<br>
> + attributes.maximum_messages = 10;<br>
> + attributes.maximum_message_size = 16;<br>
> + attributes.output_thread_priority = 8;<br>
> + attributes.maximum_to_dequeue_per_period = 1;<br>
> + attributes.output_thread_period = RTEMS_MILLISECONDS_TO_TICKS(1000);<br>
> +<br>
> + sc = rtems_regulator_create(&attributes, ®ulator);<br>
> + rtems_test_assert(sc == RTEMS_TOO_MANY);<br>
> +<br>
> + sc = rtems_partition_delete(partition_id);<br>
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Verify rtems_regulator_create rtems_message_queue_create error<br>
> + *<br>
> + * This unit test verifies that rtems_regulator_create() correctly returns an<br>
> + * error when the call to rtems_message_queue_create() fails.<br>
> + */<br>
> +static void test_regulator_create_message_queue_create_fails(void)<br>
> +{<br>
> + rtems_status_code sc;<br>
> + rtems_id queue_id;<br>
> + rtems_regulator_attributes attributes;<br>
> + rtems_regulator_instance *regulator;<br>
> +<br>
> + sc = rtems_message_queue_create(<br>
> + rtems_build_name('T', 'Q', 'T', 'Q'),<br>
> + 4,<br>
> + sizeof(unsigned long),<br>
> + RTEMS_DEFAULT_ATTRIBUTES,<br>
> + &queue_id<br>
> + );<br>
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
> +<br>
> + (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));<br>
> +<br>
> + attributes.deliverer = test_regulator_deliverer;<br>
> + attributes.maximum_messages = 10;<br>
> + attributes.maximum_message_size = 16;<br>
> + attributes.output_thread_priority = 8;<br>
> + attributes.maximum_to_dequeue_per_period = 1;<br>
> + attributes.output_thread_period = RTEMS_MILLISECONDS_TO_TICKS(1000);<br>
> +<br>
> + sc = rtems_regulator_create(&attributes, ®ulator);<br>
> + rtems_test_assert(sc == RTEMS_TOO_MANY);<br>
> +<br>
> + sc = rtems_message_queue_delete(queue_id);<br>
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Verify rtems_regulator_create rtems_task_create error<br>
> + *<br>
> + * This unit test verifies that rtems_regulator_create() correctly returns an<br>
> + * error when the call to rtems_task_create() fails.<br>
> + */<br>
> +static void test_regulator_create_task_create_fails(void)<br>
> +{<br>
> + rtems_status_code sc;<br>
> + rtems_id task_id;<br>
> + rtems_regulator_attributes attributes;<br>
> + rtems_regulator_instance *regulator;<br>
> +<br>
> + sc = rtems_task_create(<br>
> + rtems_build_name('T', 'T', 'T', 'T'),<br>
> + 80,<br>
> + 0,<br>
> + RTEMS_DEFAULT_MODES,<br>
> + RTEMS_DEFAULT_ATTRIBUTES,<br>
> + &task_id<br>
> + );<br>
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
> +<br>
> + (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));<br>
> +<br>
> + attributes.deliverer = test_regulator_deliverer;<br>
> + attributes.maximum_messages = 10;<br>
> + attributes.maximum_message_size = 16;<br>
> + attributes.output_thread_priority = 8;<br>
> + attributes.maximum_to_dequeue_per_period = 1;<br>
> + attributes.output_thread_period = RTEMS_MILLISECONDS_TO_TICKS(1000);<br>
> +<br>
> + sc = rtems_regulator_create(&attributes, ®ulator);<br>
> + rtems_test_assert(sc == RTEMS_TOO_MANY);<br>
> +<br>
> + sc = rtems_task_delete(task_id);<br>
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Verify Regulator Output Thread Handles Error on Period Create<br>
> + *<br>
> + * This unit test verifies that regulator output thread correctly exits<br>
> + * when the call to rtems_rate_monotonic_create() fails.<br>
> + *<br>
> + * This error condition/path cannot be directly detected via a return code,<br>
> + * It is verified via a debugger or code coverage reports.<br>
> + */<br>
> +static void test_regulator_create_rate_monotonic_create_fails(void)<br>
> +{<br>
> + rtems_status_code sc;<br>
> + rtems_id period_id;<br>
> + rtems_regulator_attributes attributes;<br>
> + rtems_regulator_instance *regulator;<br>
> +<br>
> + sc = rtems_rate_monotonic_create(<br>
> + rtems_build_name('T', 'S', 'T', 'P'),<br>
> + &period_id<br>
> + );<br>
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
> +<br>
> + (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));<br>
> +<br>
> + attributes.deliverer = test_regulator_deliverer;<br>
> + attributes.maximum_messages = 10;<br>
> + attributes.maximum_message_size = 16;<br>
> + attributes.output_thread_priority = 8;<br>
> + attributes.maximum_to_dequeue_per_period = 1;<br>
> + attributes.output_thread_period = RTEMS_MILLISECONDS_TO_TICKS(1000);<br>
> +<br>
> + sc = rtems_regulator_create(&attributes, ®ulator);<br>
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
> +<br>
> + /*<br>
> + * Let the output thread execute and encounter the create error.<br>
> + */<br>
> +<br>
> + sleep(1);<br>
> +<br>
> + /*<br>
> + * Now deallocate the resources allocated earlier<br>
> + */<br>
> + sc = rtems_regulator_delete(regulator);<br>
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
> +<br>
> + sc = rtems_rate_monotonic_delete(period_id);<br>
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Verify rtems_regulator_delete NULL regulator error<br>
> + *<br>
> + * This unit test verifies that rtems_regulator_delete() returns an error when<br>
> + * the regulator argument is NULL.<br>
> + */<br>
> +static void test_regulator_delete_null_regulator(void)<br>
> +{<br>
> + rtems_status_code sc;<br>
> +<br>
> + sc = rtems_regulator_delete(NULL);<br>
> + rtems_test_assert(sc == RTEMS_INVALID_ADDRESS);<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Verify rtems_regulator_delete uninitialized regulator error<br>
> + *<br>
> + * This unit test verifies that rtems_regulator_delete() returns an error when<br>
> + * the regulator argument is uninitialized.<br>
> + */<br>
> +static void test_regulator_delete_uninitialized_regulator(void)<br>
> +{<br>
> + rtems_status_code sc;<br>
> + rtems_regulator_instance regulator;<br>
> +<br>
> + (void) memset(®ulator, 0, sizeof(regulator));<br>
> +<br>
> + sc = rtems_regulator_delete(®ulator);<br>
> + rtems_test_assert(sc == RTEMS_INCORRECT_STATE);<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Verify rtems_regulator_delete successful case<br>
> + *<br>
> + * This unit test verifies that rtems_regulator_delete() can be successfully<br>
> + * deleted.<br>
> + */<br>
> +static void test_regulator_delete_OK(void)<br>
> +{<br>
> + rtems_status_code sc;<br>
> + rtems_regulator_instance *regulator;<br>
> +<br>
> + regulator = test_regulator_create_regulator_OK();<br>
> + rtems_test_assert(regulator != NULL);<br>
> +<br>
> + sc = rtems_regulator_delete(regulator);<br>
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Verify rtems_regulator_obtain_buffer NULL regulator error<br>
> + *<br>
> + * This unit test verifies that rtems_regulator_obtain_buffer() returns an error when<br>
> + * the regulator argument is NULL.<br>
> + */<br>
> +static void test_regulator_obtain_buffer_null_regulator(void)<br>
> +{<br>
> + rtems_status_code sc;<br>
> + void *buffer;<br>
> +<br>
> + sc = rtems_regulator_obtain_buffer(NULL, &buffer);<br>
> + rtems_test_assert(sc == RTEMS_INVALID_ADDRESS);<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Verify rtems_regulator_obtain_buffer uninitialized regulator error<br>
> + *<br>
> + * This unit test verifies that rtems_regulator_obtain_buffer() returns an error when<br>
> + * the regulator argument is uninitialized.<br>
> + */<br>
> +static void test_regulator_obtain_buffer_uninitialized_regulator(void)<br>
> +{<br>
> + rtems_status_code sc;<br>
> + rtems_regulator_instance regulator;<br>
> + void *buffer;<br>
> +<br>
> + (void) memset(®ulator, 0, sizeof(regulator));<br>
> +<br>
> + sc = rtems_regulator_obtain_buffer(®ulator, &buffer);<br>
> + rtems_test_assert(sc == RTEMS_INCORRECT_STATE);<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Verify rtems_regulator_obtain_buffer successful case<br>
> + *<br>
> + * This unit test verifies that rtems_regulator_obtain_buffer() can be successfully<br>
> + * obtained from an initialized regulator.<br>
> + */<br>
> +static void test_regulator_obtain_buffer_OK(void)<br>
> +{<br>
> + rtems_status_code sc;<br>
> + rtems_regulator_instance *regulator;<br>
> + void *buffer;<br>
> +<br>
> + regulator = test_regulator_create_regulator_OK();<br>
> + rtems_test_assert(regulator != NULL);<br>
> +<br>
> + sc = rtems_regulator_obtain_buffer(regulator, &buffer);<br>
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
> + rtems_test_assert(buffer != NULL);<br>
> +<br>
> + /*<br>
> + * Not really testing this here but cannot delete underlying partition<br>
> + * if there are buffers outstanding.<br>
> + */<br>
> + sc = rtems_regulator_release_buffer(regulator, buffer);<br>
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
> + rtems_test_assert(buffer != NULL);<br>
> +<br>
> + sc = rtems_regulator_delete(regulator);<br>
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Verify rtems_regulator_release_buffer NULL regulator error<br>
> + *<br>
> + * This unit test verifies that rtems_regulator_release_buffer() returns an error when<br>
> + * the regulator argument is NULL.<br>
> + */<br>
> +static void test_regulator_release_buffer_null_regulator(void)<br>
> +{<br>
> + rtems_status_code sc;<br>
> + void *buffer;<br>
> +<br>
> + sc = rtems_regulator_release_buffer(NULL, &buffer);<br>
> + rtems_test_assert(sc == RTEMS_INVALID_ADDRESS);<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Verify rtems_regulator_release_buffer uninitialized regulator error<br>
> + *<br>
> + * This unit test verifies that rtems_regulator_release_buffer() returns an<br>
> + * error when the regulator argument is uninitialized.<br>
> + */<br>
> +static void test_regulator_release_buffer_uninitialized_regulator(void)<br>
> +{<br>
> + rtems_status_code sc;<br>
> + rtems_regulator_instance regulator;<br>
> + void *buffer;<br>
> +<br>
> + (void) memset(®ulator, 0, sizeof(regulator));<br>
> +<br>
> + sc = rtems_regulator_release_buffer(®ulator, &buffer);<br>
> + rtems_test_assert(sc == RTEMS_INCORRECT_STATE);<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Verify rtems_regulator_release_buffer successful case<br>
> + *<br>
> + * This unit test verifies that rtems_regulator_release_buffer() can be successfully<br>
> + * invoked with a buffer previously allocated by rtems_regulator_obtain_buffer().<br>
> + */<br>
> +static void test_regulator_release_buffer_OK(void)<br>
> +{<br>
> + rtems_status_code sc;<br>
> + rtems_regulator_instance *regulator;<br>
> + void *buffer;<br>
> +<br>
> + regulator = test_regulator_create_regulator_OK();<br>
> + rtems_test_assert(regulator != NULL);<br>
> +<br>
> + sc = rtems_regulator_obtain_buffer(regulator, &buffer);<br>
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
> + rtems_test_assert(buffer != NULL);<br>
> +<br>
> + sc = rtems_regulator_release_buffer(regulator, buffer);<br>
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
> +<br>
> + sc = rtems_regulator_delete(regulator);<br>
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Verify rtems_regulator_send NULL regulator error<br>
> + *<br>
> + * This unit test verifies that rtems_regulator_send() returns an error when<br>
> + * the regulator argument is NULL.<br>
> + */<br>
> +static void test_regulator_send_null_regulator(void)<br>
> +{<br>
> + rtems_status_code sc;<br>
> + void *buffer;<br>
> + size_t length;<br>
> +<br>
> + buffer = &length;<br>
> + length = sizeof(size_t);<br>
> +<br>
> + sc = rtems_regulator_send(NULL, buffer, length);<br>
> + rtems_test_assert(sc == RTEMS_INVALID_ADDRESS);<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Verify rtems_regulator_send NULL message error<br>
> + *<br>
> + * This unit test verifies that rtems_regulator_send() returns an error when<br>
> + * the message argument is NULL.<br>
> + */<br>
> +static void test_regulator_send_null_message(void)<br>
> +{<br>
> + rtems_status_code sc;<br>
> + size_t length;<br>
> + rtems_regulator_instance regulator;<br>
> +<br>
> + length = sizeof(size_t);<br>
> +<br>
> + sc = rtems_regulator_send(®ulator, NULL, length);<br>
> + rtems_test_assert(sc == RTEMS_INVALID_ADDRESS);<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Verify rtems_regulator_send zero length message error<br>
> + *<br>
> + * This unit test verifies that rtems_regulator_send() returns an<br>
> + * error when the message length is 0.<br>
> + */<br>
> +static void test_regulator_send_length_is_zero(void)<br>
> +{<br>
> + rtems_status_code sc;<br>
> + rtems_regulator_instance regulator;<br>
> + void *buffer;<br>
> + size_t length;<br>
> +<br>
> + buffer = &length;<br>
> + length = 0;<br>
> +<br>
> + sc = rtems_regulator_send(®ulator, buffer, length);<br>
> + rtems_test_assert(sc == RTEMS_INVALID_NUMBER);<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Verify rtems_regulator_send uninitialized regulator error<br>
> + *<br>
> + * This unit test verifies that rtems_regulator_send() returns an<br>
> + * error when the regulator argument is uninitialized.<br>
> + */<br>
> +static void test_regulator_send_uninitialized_regulator(void)<br>
> +{<br>
> + rtems_status_code sc;<br>
> + rtems_regulator_instance regulator;<br>
> + void *buffer;<br>
> + size_t length;<br>
> +<br>
> + buffer = &length;<br>
> + length = sizeof(size_t);<br>
> +<br>
> + (void) memset(®ulator, 0, sizeof(regulator));<br>
> +<br>
> + sc = rtems_regulator_send(®ulator, buffer, length);<br>
> + rtems_test_assert(sc == RTEMS_INCORRECT_STATE);<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Verify rtems_regulator_send and output thread delivers message<br>
> + *<br>
> + * This unit test verifies that when the regulator is successfully initialized<br>
> + * and used as expected, a message sent via rtems_regulator_send() is delivered as<br>
> + * expected.<br>
> + */<br>
> +static void test_regulator_send_one_message_OK(void)<br>
> +{<br>
> + rtems_status_code sc;<br>
> + rtems_regulator_instance *regulator;<br>
> + char message[MAXIMUM_MESSAGE_LENGTH];<br>
> + void *buffer;<br>
> + size_t length;<br>
> + int match;<br>
> + rtems_regulator_attributes attributes = {<br>
> + .deliverer = test_regulator_deliverer_logger,<br>
> + .deliverer_context = &deliverer_logger_context,<br>
> + .maximum_message_size = 16,<br>
> + .maximum_messages = 10,<br>
> + .output_thread_priority = 16,<br>
> + .output_thread_stack_size = 0,<br>
> + .output_thread_period = RTEMS_MILLISECONDS_TO_TICKS(250),<br>
> + .maximum_to_dequeue_per_period = 3<br>
> + };<br>
> +<br>
> + sc = rtems_regulator_create(&attributes, ®ulator);<br>
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
> + rtems_test_assert(regulator != NULL);<br>
> +<br>
> + deliverer_logger_context.regulator = regulator;<br>
> +<br>
> + delivered_messages_reset();<br>
> +<br>
> + sc = rtems_regulator_obtain_buffer(regulator, &buffer);<br>
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
> + rtems_test_assert(buffer != NULL);<br>
> +<br>
> + length = snprintf(message, MAXIMUM_MESSAGE_LENGTH, "message %d", 1024) + 1;<br>
> + strcpy(buffer, message);<br>
> +<br>
> + sc = rtems_regulator_send(regulator, buffer, length);<br>
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
> +<br>
> + sleep(1);<br>
> +<br>
> + rtems_test_assert(delivered_message_count == 1);<br>
> + match = strncmp(<br>
> + delivered_messages[0].message,<br>
> + message,<br>
> + MAXIMUM_MESSAGE_LENGTH<br>
> + );<br>
> + rtems_test_assert(match == 0);<br>
> +<br>
> + sc = rtems_regulator_delete(regulator);<br>
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
> +<br>
> + deliverer_logger_context.regulator = NULL;<br>
> +}<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTests<br>
> + * @brief Verify rtems_regulator_send and output thread delivers messages<br>
> + *<br>
> + * This unit test verifies that when the regulator is successfully initialized<br>
> + * and used as expected, and multiple messages are sent via rtems_regulator_send()<br>
> + * that they are delivered as expected.<br>
> + */<br>
> +#include <stdio.h><br>
> +static void test_regulator_send_multiple_messages_OK(void)<br>
> +{<br>
> + rtems_status_code sc;<br>
> + rtems_regulator_instance *regulator;<br>
> + char message[MAXIMUM_MESSAGE_LENGTH];<br>
> + void *buffer;<br>
> + size_t length;<br>
> + int match;<br>
> + int i;<br>
> + time_t base_time;<br>
> + time_t tmp_time;<br>
> + rtems_interval base_ticks;<br>
> + rtems_interval ticks;<br>
> + rtems_interval ticks_per_second;<br>
> +<br>
> + rtems_regulator_attributes attributes = {<br>
> + .deliverer = test_regulator_deliverer_logger,<br>
> + .deliverer_context = &deliverer_logger_context,<br>
> + .maximum_message_size = MAXIMUM_MESSAGE_LENGTH,<br>
> + .maximum_messages = 10,<br>
> + .output_thread_priority = 16,<br>
> + .output_thread_stack_size = 0,<br>
> + .output_thread_period = RTEMS_MILLISECONDS_TO_TICKS(1000),<br>
> + .maximum_to_dequeue_per_period = 2<br>
> + };<br>
> +<br>
> + delivered_messages_reset();<br>
> +<br>
> + sc = rtems_regulator_create(&attributes, ®ulator);<br>
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
> + rtems_test_assert(regulator != NULL);<br>
> +<br>
> + deliverer_logger_context.regulator = regulator;<br>
> +<br>
> + /*<br>
> + * Ensure the messages are sent on a second boundary to ensure the<br>
> + * output thread will process them as expected.<br>
> + */<br>
> + tmp_time = time(NULL);<br>
> + do {<br>
> + base_time = time(NULL);<br>
> + } while (tmp_time == base_time);<br>
> +<br>
> + /**<br>
> + * Send five messages as a burst which will need to be smoothly sent at<br>
> + * the configured rate.<br>
> + */<br>
> + for (i=1 ; i <= 5 ; i++) {<br>
> + sc = rtems_regulator_obtain_buffer(regulator, &buffer);<br>
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
> + rtems_test_assert(buffer != NULL);<br>
> +<br>
> + length = snprintf(message, MAXIMUM_MESSAGE_LENGTH, "message %d", i);<br>
> + strcpy(buffer, message);<br>
> +<br>
> + sc = rtems_regulator_send(regulator, buffer, length);<br>
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
> + }<br>
> +<br>
> + /*<br>
> + * Let the output thread executed and deliver the messages.<br>
> + */<br>
> + sleep(5);<br>
> +<br>
> + /**<br>
> + * Ensure the five messages were delivered as follows:<br>
> + *<br>
> + * - deliver all 5<br>
> + * - contents are "message N" where N is 1 to 5<br>
> + * - message 1 and 2 delivered during the first second<br>
> + * - message 3 and 4 delivered during the second second<br>
> + * - message 5 delivered during the third second<br>
> + * - no further messages delivered<br>
> + */<br>
> +<br>
> + rtems_test_assert(delivered_message_count == 5);<br>
> +<br>
> + for (i=0 ; i < 5 ; i++) {<br>
> + (void) snprintf(message, MAXIMUM_MESSAGE_LENGTH, "message %d", i+1);<br>
> +// printf("%d %s\n", i, delivered_messages[i].message);<br>
> + match = strncmp(<br>
> + delivered_messages[i].message,<br>
> + message,<br>
> + MAXIMUM_MESSAGE_LENGTH<br>
> + );<br>
> + rtems_test_assert(match == 0);<br>
> + }<br>
> +<br>
> + /**<br>
> + * Verify that messages were delivered in the proper groups. Check that<br>
> + * the delivery time matches expectations.<br>
> + */<br>
> + rtems_test_assert(delivered_messages[0].processed == delivered_messages[1].processed);<br>
> + rtems_test_assert(delivered_messages[1].processed != delivered_messages[2].processed);<br>
> + rtems_test_assert(delivered_messages[2].processed == delivered_messages[3].processed);<br>
> + rtems_test_assert(delivered_messages[3].processed != delivered_messages[4].processed);<br>
> +<br>
> + /**<br>
> + * Verify that the message groups were properly spaced temporally. They<br>
> + * should be one second apart.<br>
> + */<br>
> + ticks_per_second = rtems_clock_get_ticks_per_second();<br>
> +<br>
> + base_ticks = delivered_messages[1].processed;<br>
> + ticks = delivered_messages[2].processed;<br>
> + rtems_test_assert(ticks_per_second == ticks - base_ticks);<br>
> +<br>
> + base_ticks = delivered_messages[3].processed;<br>
> + ticks = delivered_messages[4].processed;<br>
> + rtems_test_assert(ticks_per_second == ticks - base_ticks);<br>
> +<br>
> + sc = rtems_regulator_delete(regulator);<br>
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
> +<br>
> + deliverer_logger_context.regulator = NULL;<br>
> +}<br>
> +<br>
> +/* Necessary prototype */<br>
> +rtems_task test_regulator(rtems_task_argument);<br>
> +<br>
> +/**<br>
> + * @ingroup RegulatorTestshttps://<a href="http://devel.rtems.org/milestone/6.1" rel="noreferrer" target="_blank">devel.rtems.org/milestone/6.1</a><br>
> + * @brief Test entry task which invokes test cases<br>
> + */<br>
> +rtems_task test_regulator(rtems_task_argument arg)<br>
> +{<br>
> + (void) arg;<br>
> +<br>
> + TEST_BEGIN();<br>
> +<br>
> + malloc_trigger_reset();<br>
> +<br>
> + test_regulator_create_max_dequeue_zero();<br>
> + test_regulator_create_null_attributes();<br>
> + test_regulator_create_null_regulator();<br>
> + test_regulator_create_deliverer_is_null();<br>
> + test_regulator_create_maximum_messages_is_zero();<br>
> + test_regulator_create_maximum_message_size_is_zero();<br>
> + test_regulator_create_maximum_to_dequeue_per_period_is_zero();<br>
> + test_regulator_create_malloc_regulator_fails();<br>
> + test_regulator_create_malloc_buffers_fails();<br>
> + test_regulator_create_output_thread_priority_is_zero();<br>
> + test_regulator_create_partition_create_fails();<br>
> + test_regulator_create_message_queue_create_fails();<br>
> + test_regulator_create_task_create_fails();<br>
> + test_regulator_create_rate_monotonic_create_fails();<br>
> +<br>
> + test_regulator_delete_null_regulator();<br>
> + test_regulator_delete_uninitialized_regulator();<br>
> + test_regulator_delete_OK();<br>
> +<br>
> + test_regulator_obtain_buffer_null_regulator();<br>
> + test_regulator_obtain_buffer_uninitialized_regulator();<br>
> + test_regulator_obtain_buffer_OK();<br>
> +<br>
> + test_regulator_release_buffer_null_regulator();<br>
> + test_regulator_release_buffer_uninitialized_regulator();<br>
> + test_regulator_release_buffer_OK();<br>
> +<br>
> + test_regulator_send_null_regulator();<br>
> + test_regulator_send_null_message();<br>
> + test_regulator_send_length_is_zero();<br>
> + test_regulator_send_uninitialized_regulator();<br>
> +<br>
> + test_regulator_send_one_message_OK();<br>
> +<br>
> + test_regulator_send_multiple_messages_OK();<br>
> +<br>
> + TEST_END();<br>
> +<br>
> + rtems_test_exit(0);<br>
> +}<br>
> diff --git a/testsuites/libtests/regulator01/regulator01.doc b/testsuites/libtests/regulator01/regulator01.doc<br>
> new file mode 100644<br>
> index 0000000000..5685e4b857<br>
> --- /dev/null<br>
> +++ b/testsuites/libtests/regulator01/regulator01.doc<br>
> @@ -0,0 +1,68 @@<br>
> +# SPDX-License-Identifier: BSD-2-Clause<br>
> +<br>
> +# Copyright (c) 2023 OAR Corporation<br>
> +#<br>
> +# Redistribution and use in source and binary forms, with or without<br>
> +# modification, are permitted provided that the following conditions<br>
> +# are met:<br>
> +# 1. Redistributions of source code must retain the above copyright<br>
> +# notice, this list of conditions and the following disclaimer.<br>
> +# 2. Redistributions in binary form must reproduce the above copyright<br>
> +# notice, this list of conditions and the following disclaimer in the<br>
> +# documentation and/or other materials provided with the distribution.<br>
> +#<br>
> +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"<br>
> +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE<br>
> +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE<br>
> +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE<br>
> +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br>
> +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF<br>
> +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS<br>
> +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN<br>
> +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)<br>
> +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE<br>
> +# POSSIBILITY OF SUCH DAMAGE.<br>
> +#<br>
> +<br>
> +This file describes the directives and concepts tested by this test set.<br>
> +<br>
> +test set name: regulator01<br>
> +<br>
> +directives:<br>
> + + rtems_regulator_create<br>
> + + rtems_regulator_delete<br>
> + + rtems_regulator_obtain_buffer<br>
> + + rtems_regulator_release_buffer<br>
> + + rtems_regulator_send<br>
> +<br>
> +concepts<br>
> + + Verify rtems_regulator_create() maximum_to_dequeue_per_period<br>
> + + Verify rtems_regulator_create() NULL attributes error<br>
> + + Verify rtems_regulator_create NULL regulator error<br>
> + + Verify rtems_regulator_create deliverer is NULL<br>
> + + Verify rtems_regulator_create maximum_messages is 0 error<br>
> + + Verify rtems_regulator_create maximum_message_size is 0 error<br>
> + + Verify rtems_regulator_create maximum_to_dequeue_per_period is 0 error<br>
> + + Verify rtems_regulator_create returns error on failure to allocate regulator<br>
> + + Verify rtems_regulator_create returns error on failure to allocate buffers<br>
> + + Verify rtems_regulator_create and delete work<br>
> + + Verify rtems_regulator_create rtems_partition_create error<br>
> + + Verify rtems_regulator_create rtems_message_queue_create error<br>
> + + Verify rtems_regulator_create rtems_task_create error<br>
> + + Verify Regulator Output Thread Handles Error on Period Create<br>
> + + Verify rtems_regulator_delete NULL regulator error<br>
> + + Verify rtems_regulator_delete uninitialized regulator error<br>
> + + Verify rtems_regulator_delete successful case<br>
> + + Verify rtems_regulator_obtain_buffer NULL regulator error<br>
> + + Verify rtems_regulator_obtain_buffer uninitialized regulator error<br>
> + + Verify rtems_regulator_obtain_buffer successful case<br>
> + + Verify rtems_regulator_release_buffer NULL regulator error<br>
> + + Verify rtems_regulator_release_buffer uninitialized regulator error<br>
> + + Verify rtems_regulator_release_buffer successful case<br>
> + + Verify rtems_regulator_send NULL regulator error<br>
> + + Verify rtems_regulator_send NULL message error<br>
> + + Verify rtems_regulator_send zero length message error<br>
> + + Verify rtems_regulator_send uninitialized regulator error<br>
> + + Verify rtems_regulator_send and output thread delivers message<br>
> + + Verify rtems_regulator_send and output thread delivers messages<br>
> +<br>
> diff --git a/testsuites/libtests/regulator01/rtems_config.c b/testsuites/libtests/regulator01/rtems_config.c<br>
> new file mode 100644<br>
> index 0000000000..ca96e1b1dd<br>
> --- /dev/null<br>
> +++ b/testsuites/libtests/regulator01/rtems_config.c<br>
> @@ -0,0 +1,59 @@<br>
> +/* SPDX-License-Identifier: BSD-2-Clause */<br>
> +<br>
> +/**<br>
> + * @file<br>
> + *<br>
> + * @brief RTEMS Configuration for regulator tests<br>
> + */<br>
> +<br>
> +/*<br>
> + * COPYRIGHT (c) 2022. * On-Line Applications Research Corporation (OAR).<br>
> + *<br>
> + * Redistribution and use in source and binary forms, with or without<br>
> + * modification, are permitted provided that the following conditions<br>
> + * are met:<br>
> + * 1. Redistributions of source code must retain the above copyright<br>
> + * notice, this list of conditions and the following disclaimer.<br>
> + * 2. Redistributions in binary form must reproduce the above copyright<br>
> + * notice, this list of conditions and the following disclaimer in the<br>
> + * documentation and/or other materials provided with the distribution.<br>
> + *<br>
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"<br>
> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE<br>
> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE<br>
> + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE<br>
> + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br>
> + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF<br>
> + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS<br>
> + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN<br>
> + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)<br>
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE<br>
> + * POSSIBILITY OF SUCH DAMAGE.<br>
> + */<br>
> +<br>
> +<br>
> +#include <rtems.h><br>
> +<br>
> +rtems_task test_regulator(rtems_task_argument);<br>
> +<br>
> +#include <bsp.h> /* for device driver prototypes */<br>
> +<br>
> +/* NOTICE: the clock driver is explicitly disabled */<br>
> +#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER<br>
> +#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER<br>
> +<br>
> +#define CONFIGURE_RTEMS_INIT_TASKS_TABLE<br>
> +#define CONFIGURE_INIT_TASK_ENTRY_POINT test_regulator<br>
> +#define CONFIGURE_INIT_TASK_ATTRIBUTES RTEMS_FLOATING_POINT<br>
> +<br>
> +/* Use hard limits to make it easier to trip object creation errors */<br>
> +#define CONFIGURE_MAXIMUM_TASKS 2<br>
> +#define CONFIGURE_MAXIMUM_MESSAGE_QUEUES 1<br>
> +#define CONFIGURE_MAXIMUM_PARTITIONS 1<br>
> +#define CONFIGURE_MAXIMUM_PERIODS 1<br>
> +<br>
> +#define CONFIGURE_UNIFIED_WORK_AREAS<br>
> +#define CONFIGURE_MINIMUM_TASK_STACK_SIZE (8 * 1024)<br>
> +<br>
> +#define CONFIGURE_INIT<br>
> +#include <rtems/confdefs.h><br>
</blockquote></div></div>