<div dir="ltr"><div dir="ltr">While breaking for lunch, I made a couple of decisions. More inline. Sorry.</div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Fri, Jul 14, 2023 at 11:00 AM Joel Sherrill <<a href="mailto:joel@rtems.org">joel@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"><div dir="ltr"><div>Comments embedded. V2 coming. If your comments arrive in time, I might get them in without a V3.</div><div><br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Sun, Jul 9, 2023 at 7:48 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">Hi,<br>
<br>
Thanks for posting this code. My comments are below and I hope they help.<br>
<br>
Thanks<br>
Chris<br>
<br>
On 8/7/2023 6:55 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              |  360 +++++<br>
>  cpukit/include/rtems/regulatorimpl.h          |  100 ++<br>
>  cpukit/regulator/regulator.c                  |  487 +++++++<br>
<br>
I prefer we do not add pieces of functionality to cpukit at this level. You<br>
mention this is application support so would `libapp` work? It would allow for<br>
growth, eg logging. We have separate libraries for libdl etc so should this be<br>
separate or placed in libcpu? We also have libmisc and it could live there?<br></blockquote><div><br></div><div>Showing your age. libcpu is gone. :)</div><div><br></div><div>I will move it under libmisc for now. libapp would just be another dumping</div><div>ground. I wonder why shell is under libmisc other than history. lol</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>
FYI and unrelated I also think moving dtc, ftpd, mghttpd, compression and<br>
telnetd somewhere would be good.<br></blockquote><div><br></div><div>Compression is better than the hodge podge of locations that that code was</div><div>in before. :)</div><div><br></div><div>The network libraries should be on a path to move to rtems-net-services.</div><div><br></div><div>But dtc is core functionality for some BSPs so I'm not sure what to do with it.</div><div><br></div><div>My main concern in this area has not been to push things into external </div><div>repos or libraries as much as make sure the directory organization of</div><div>what we have makes sense.  Top of my list is a few things in score/ that</div><div>I think don't belong. </div><div><br></div><div>But you are right. This is a topic for another thread. </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>
>  spec/build/cpukit/librtemscpu.yml             |    3 +<br>
>  spec/build/testsuites/libtests/grp.yml        |    2 +<br>
>  .../build/testsuites/libtests/regulator01.yml |   21 +<br>
>  testsuites/libtests/regulator01/regulator01.c | 1156 +++++++++++++++++<br>
>  .../libtests/regulator01/regulator01.doc      |   37 +<br>
>  .../libtests/regulator01/rtems_config.c       |   59 +<br>
>  9 files changed, 2225 insertions(+)<br>
>  create mode 100644 cpukit/include/rtems/regulator.h<br>
>  create mode 100644 cpukit/include/rtems/regulatorimpl.h<br>
>  create mode 100644 cpukit/regulator/regulator.c<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..7344ced2ae<br>
> --- /dev/null<br>
> +++ b/cpukit/include/rtems/regulator.h<br>
> @@ -0,0 +1,360 @@<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 bursty message traffic.<br>
> + */<br>
> +<br>
> +/**<br>
> + * @mainpage<br>
> + *<br>
> + * The regulator is designed to sit logically between two entities -- a<br>
> + * source and a 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 bursty input from a source<br>
<br>
... bursts of input ...<br></blockquote><div><br></div><div>Changed in three places </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
> + * and meter it out to a destination.<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>
<br>
How is the maximum burst defined?<br></blockquote><div><br></div><div>I updated the main page description. The regulator buffers maximum_messages</div><div>which is the amount of buffering the delivery thread is allowed to get behind. This</div><div>could be one burst or multiple as they are delivered. </div><div><br></div><div>I think it was covered but later. I also added more to rtems_regulator_create()</div><div>about the attributes structure.</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>
> + * is in messages per second. If the sender produces data too fast, the<br>
> + * regulator will buffer the configured number of messages.<br>
<br>
What happens to the input data when the "configured number of messages" limit is<br>
reached?<br></blockquote><div><br></div><div>The rtems_regulator_send() returns RTEMS_TOO_MANY which is from the underlying</div><div>rtems_message_queue_send().  </div><div><br></div><div>I will be submitting a Classic API Guide chapter for the regulator after</div><div>the code is merged. Each error code will be listed as is practice. The Doxygen</div><div>doesn't list every error code.</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>
There is no discussion about error, error responiblity and recovery.<br></blockquote><div><br></div><div>I added some to rtems_regulator_send() but my recommendation to User #1</div><div>was to do a systems analysis on the source and sink and ensure the maximum</div><div>number of messages buffered was sufficient.</div><div><br></div><div>FWIW this is just a message queue under the hood and I don't think we </div><div>have guidance for sizing that. :)</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>
> + * 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>
> + * bursty 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 will eventually be output.<br>
<br>
Does the regulator provide any stats, latched states or alarms to help a user<br>
integrate and prove the data flows are operating as designed? I have<br>
successfully used high or low water marks as a way to help audit this type of<br>
functionality in systems. The marks are set at the system level and can be<br>
manually checked or monitored to make sure no system limits are beached. Slient<br>
drops or overflows can be difficult to resolve.<br></blockquote><div><br></div><div>User #1 had no requirements for any of that. We can discuss adding that as user</div><div>requirements arise. At this point, I wouldn't know what would be interesting for </div><div>statistics. I have a few ideas -- like maximum messages queued (0 would be the</div><div>low mark), reflecting the period statistics, and perhaps tracking number of messages</div><div>delivered per period. </div><div><br></div><div>Message queues do not track the high water mark or if they returned too many. </div><div>It would seem that a lot of what would be interesting could be information from the</div><div>underlying RTEMS objects.</div><div><br></div><div>Can we file a ticket for this as an enhancement? I can assure you that it won't happen</div><div>on this round of submission but it is an interesting thing to consider adding.</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>
> + *<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>
> + * @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 Source then sends the buffer to the Regulator instance. The<br>
> + * 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>
> + *<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 ultimate<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. <br>
<br>
What is the context this handler is called in? Are there any contriants, eg tack<br>
size, performance side effects, ...?<br></blockquote><div><br></div><div> Added. There is a dedicated thread whose stack size, period, and priority</div><div>are specified as part of the attributes passed to rtems_regulator_create().</div><div>I added some here and tidied up the information with rtems_regulator_create().</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>
> 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.<br>
> + *<br>
> + * @code<br>
> + *   void 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>
> + *       regulator release(message);<br>
<br>
What is `regulator release()`?<br></blockquote><div><br></div><div>Should have been rtems_regulator_release_buffer(). Looks like I tried</div><div>to write English rather than the method name for some reason.</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>
> + *     }<br>
> + * @endcode<br>
> + *<br>
> + * The following sequence diagram shows the behavior of the Delivery thread<br>
<br>
behaviour<br></blockquote><div><br></div><div>I thought we preferred US spellings but were not particularly picky. As long as the </div><div>flavor and color are correct. :)</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>
> + * 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 that the Delivery Thread<br>
> + * Body is periodically executed and that during each up to the instance<br>
> + * configuration parameter maximum_to_dequeue_per_period can be dequeued and<br>
> + * given to the application's deliverer method for processing.<br>
<br>
Could this paragraph be worded in a simpler way? It has `are that` and `and that<br>
during each up to the instance...`.<br></blockquote><div><br></div><div>I changed it to a lead-in and numbered bullets. </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
> + * Note that the application explicitly obtains buffers from the regulator<br>
> + * instance but that the Delivery Thread implicitly releases them to the<br>
> + * internal pool when delivered.<br>
<br>
What can a user do with a message pointer? Can they hold it, queue it or take<br>
any references to it? The example is not clear because a socket write is a copy<br>
by value and a release implies a separate action is required and the<br>
implemention automatically returns the buffer to the parition?<br></blockquote><div><br></div><div>The regulator is designed to not add copies of buffers. They obtain buffers,</div><div>fill them in, feed them to the regulator, and eventually process each via a</div><div>delivery function. </div><div><br></div><div>The use of a partition and a message queue which is only ptr/length is intentional</div><div>to avoid message buffer copies.  My thinking was that the input device would directly</div><div>fill in the buffer and it would not be copied until the delivery function handed it to </div><div>the sink.</div><div><br></div><div>I've added Doxygen comments on the lifespan and ownership of the buffer.</div><div>There was already some in a couple of places and I added to it.</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>
> +typedef void (*rtems_regulator_deliverer)(<br>
> +  void     *context,<br>
> +  void     *message,<br>
> +  size_t    length<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>
<br>
What happens if set to 0 or 1?<br></blockquote><div><br></div><div>1 is legal per RTEMS.</div><div><br></div><div>Good catch on 0. That is actually period status. I added a check to </div><div>rtems_regulator_create() for this and return RTEMS_INVALID_NUMBER.</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>
> +  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 task.<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 task and an RTEMS partition. When it executes, the<br>
> + *       output task will create an RTEMS rate monotonic period.<br>
<br>
I see a message queue create in the code?<br>
<br>
Is the memory worksapce of heap?<br></blockquote><div><br></div><div>Currently workspace. Do you think it should be changed to construct</div><div>and allocate this memory from the heap?  That would be V3. :)</div></div></div></blockquote><div><br></div><div>I am switching from rtems_message_queue_create() to </div><div>rtems_message_queue_construct(). This frees the user from a bit of</div><div>configuration.</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"><div dir="ltr"><div class="gmail_quote"><div><br></div><div>I added documentation to rtems_regulator_create() to list all the resources</div><div>it allocates.</div><div><br></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>
> + */<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>
> + * @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 rtems_regulator_send(). If the @a buffer is not sent,<br>
> + * then it should be returned using rtems_regulator_release_buffer().<br>
> + *<br>
> + * The buffer is of the maximum_message_size specified in the attributes<br>
> + * passed in to 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 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>
> + * rtems_regulator_obtain_buffer().<br>
> + *<br>
> + * If the rtems_regulator_send() is successful, the buffer is automatically<br>
> + * released to the pool. If unsuccessful or the application decides not<br>
> + * to send the message, it is the application's responsibility to<br>
> + * release the buffer.<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 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 task. 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 assumed that the @a message buffer has been filled in with<br>
> + * application content to deliver and that the @a message buffer was<br>
> + * obtained via rtems_regulator_obtain_buffer().<br>
<br>
It is a requirement the send call only passes memory from the<br>
rtems_regulator_obtain_buffer() call. Maybe the wording should clearly state this.<br></blockquote><div><br></div><div><br></div><div>Strengthened and split into two paragraphs.</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>
> + * If the rtems_regulator_send() is successful, the buffer is automatically<br>
> + * released to the pool. If unsuccessful or the application decides not<br>
> + * to send the message, it is the application's responsibility to<br>
> + * release the buffer.<br>
<br>
This is not true. The memory is not automatically returned to the pool by the<br>
send call. If the send is successful it is placed in an internal handle and<br>
queued on a message queue (I assume is sized to hold all buffers) and it is only<br>
released after the message has be read from the message queue and the delivery<br>
handler has returned.<br></blockquote><div><br></div><div>I've cleaned up the description.</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>
I find the automatic and manual interactions of the API confusing. I see a<br>
number of places a user can step on their own toes. It seems this code as<br>
evolved because the example shows a release call in delivery handler and yet<br>
there is an automatic release. I wonder if the automatic release is a good idea<br>
as the user needs to handle a few other cases manually? The automatic release<br>
also implies the handling of the buffer in the delivery handler is sychronous<br>
and that may not suite all applications requiring buffer buffering and copies.<br></blockquote><div><br></div><div>The API did not evolve other than to add rtems_ naming conventions when </div><div>User #1 asked for it to be submitted. It was conceived to not introduce yet another</div><div>buffer copy. Yes, that puts some responsibility on the user but that's the cost of</div><div>not copying. I thought I kept it pretty simple to use. User #1 didn't have any issues</div><div>incorporating it into their application.</div><div><br></div><div>The automatic release does assume that the delivery to the sink is by value and</div><div>the buffer is safe to release back to the regulator.</div><div><br></div><div>Do you think the delivery function should return a bool to indicate if the release</div><div>should happen or not? If it returns say false, the delivery code for the user would</div><div>have to release it on its own.</div></div></div></blockquote><div><br></div><div>I'm going to make this change. This way there can be zero copy on the source side </div><div>and the sink side if the delivery function is calling a method that needs to hold the</div><div>buffer.</div><div><br></div><div>The use case I thought of should have been obvious to me from the beginning:</div><div><br></div><div>Source Side (zero copy)</div><div>===================</div><div>+ obtain buffer from regulator<br></div><div>+ device DMAs into buffer<br></div><div>+ send buffer to regulator<br></div><div>....</div><div>Sink Side (zero copy)</div><div>================</div><div>+ delivery method sets up DMA to output the buffer to another device<br></div><div>   returns false</div><div>+ When DMA is complete, releases buffer to regulator.<br></div><div><br></div><div>Sink Side (copy)</div><div>=============</div><div>+ delivery method calls something like write() which copies the buffer<br></div><div>   returns true</div><div>+ Delivery thread releases buffer to regulator<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div class="gmail_quote"><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>
> + * @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..dfbcb04214<br>
> --- /dev/null<br>
> +++ b/cpukit/include/rtems/regulatorimpl.h<br>
> @@ -0,0 +1,100 @@<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>
> +  /** Pool of unused messages */<br>
> +  rtems_id  messages_partition_id;<br>
> +<br>
> +  /** Message queue of pending outgoing messages */<br>
> +  rtems_id  queue_id;<br>
> +<br>
> +  /** Id of thread 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/regulator/regulator.c b/cpukit/regulator/regulator.c<br>
> new file mode 100644<br>
> index 0000000000..33a6b33950<br>
> --- /dev/null<br>
> +++ b/cpukit/regulator/regulator.c<br>
> @@ -0,0 +1,487 @@<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/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>
> +<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>
> +    (void) sc;<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>
> +        &regulator_message,<br>
> +        &regulator_message_size,<br>
> +        RTEMS_NO_WAIT,<br>
> +        0<br>
> +      );<br>
> +      if (sc != RTEMS_SUCCESSFUL) {<br>
> +        break;<br>
> +      }<br>
> +<br>
> +      the_regulator->Attributes.deliverer(<br>
> +        the_regulator->Attributes.deliverer_context,<br>
> +        regulator_message.buffer,<br>
> +        regulator_message.length<br>
> +      );<br>
> +<br>
> +      /**<br>
> +       * The message was successfully delivered so return the buffer to the pool.<br>
> +       */<br>
> +      sc = rtems_partition_return_buffer(<br>
> +     the_regulator->messages_partition_id,<br>
> +        regulator_message.buffer<br>
> +      );<br>
> +      (void) sc;<br>
<br>
I am confused because the example has:<br>
<br>
  regulator release(message);<br></blockquote><div><br></div><div>Changed to rtems_regulator_release_buffer() where that occurred. </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
and I do not know what that is doing and yet here the buffer is being<br>
automatically released?<br></blockquote><div><br></div><div>Hopefully clearer now. Buffers are managed by a partition. I added some to</div><div>the comment to help tie it together. </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
There is no explanation on why the return status of the partition return call<br>
can be ignored?<br></blockquote><div><br></div><div>I have no idea what to do with a non-successful status. I suppose the user</div><div>could provide a function to call back but generically we have no way to</div><div>do anything.</div></div></div></blockquote><div><br></div><div>With this going into RTEMS, I can make it a debug assertion. I didn't have</div><div>that option before. </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div class="gmail_quote"><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>
> +    }<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 rtems_regulator_create() and in<br>
> + * 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>
The task holds and uses output_thread_period_id. Is this OK? Could the task end<br>
up spinning, locking things up?<br></blockquote><div><br></div><div>Periods have to be used by the creating thread. My experience using them is</div><div>that this ends up being the create/use pattern.</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>
I think it is wrong to delete the task like this. What if the deliver function<br>
is being called and that code in turn has called a socket write and with that a<br>
bunch of states are being held when the task context is killed?<br></blockquote><div><br></div><div>The user has to know it is safe to delete the instance. They likely need to close</div><div>the socket before deleting the regulator. I added a bit to rtems_regulator_delete()</div><div>to note that the user has to clean up for resources used by the delivery thread and</div><div>may need to delete them before calling this.</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>
> +<br>
> +  (void) rtems_message_queue_delete(the_regulator->queue_id);<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>
<br>
Not needed if you free the memory.<br></blockquote><div><br></div><div>The regulator instance is in the user's space. We don't free it. </div><div><br></div><div>I will consider adding a RTEMS_REGULATOR_INITIALIZER.</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>
> +  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>
> +  /**<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>
> +   * 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>
> +  sc = rtems_message_queue_create(<br>
> +    rtems_build_name('S', 'N', 'D', 'Q'),<br>
> +    attributes->maximum_messages,<br>
> +    sizeof(_Regulator_Message_t),<br>
> +    RTEMS_DEFAULT_ATTRIBUTES,<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>
> +  (void) sc;<br>
<br>
Huh?<br></blockquote><div><br></div><div><br></div><div>How would it fail? I can add a check and return it but it can't fail if the</div><div>code is correct.</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>
> +  /**<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>
I am a bit lost on the value of this variable and state?<br></blockquote><div><br></div><div>regulator instance is memory allocated by the called. Just making sure it</div><div>has been through create.</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>
> +<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>
These repeasted checks calls could be a single check type function.<br></blockquote><div><br></div><div>Maybe. Not sure it is worth it. The test had 100% statement and branch coverage. </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>
> +<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>
I would consider making this a handle the user also need to use. I would<br>
allocate a handle per buffer and change send, deliver etc to use handles. I have<br>
found the message pointer confusing to follow in each interface.<br></blockquote><div><br></div><div>Handle to what? They obtain a buffer of a known maximum size. Providing the</div><div>size is just to support cases where the entire buffer isn't needed.</div><div><br></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>
> +<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>
> +    &regulator_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..670c3cd479 100644<br>
> --- a/spec/build/cpukit/librtemscpu.yml<br>
> +++ b/spec/build/cpukit/librtemscpu.yml<br>
> @@ -150,6 +150,8 @@ install:<br>
>    - cpukit/include/rtems/pty.h<br>
>    - cpukit/include/rtems/qreslib.h<br>
>    - cpukit/include/rtems/ramdisk.h<br>
> +  - cpukit/include/rtems/regulator.h<br>
> +  - cpukit/include/rtems/regulatorimpl.h<br>
>    - cpukit/include/rtems/rbheap.h<br>
>    - cpukit/include/rtems/rbtree.h<br>
>    - cpukit/include/rtems/record.h<br>
> @@ -1183,6 +1185,7 @@ source:<br>
>  - cpukit/posix/src/vfork.c<br>
>  - cpukit/posix/src/wait.c<br>
>  - cpukit/posix/src/waitpid.c<br>
> +- cpukit/regulator/regulator.c<br>
>  - cpukit/rtems/src/barrier.c<br>
>  - cpukit/rtems/src/barriercreate.c<br>
>  - cpukit/rtems/src/barrierdelete.c<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..408a22dd48<br>
> --- /dev/null<br>
> +++ b/testsuites/libtests/regulator01/regulator01.c<br>
> @@ -0,0 +1,1156 @@<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 void 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>
> +<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>
> + * @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 void test_regulator_deliverer_logger(<br>
> +  void     *context,<br>
> +  void     *message,<br>
> +  size_t    length<br>
> +)<br>
> +{<br>
> +  (void) context;<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>
> +<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, &regulator);<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, &regulator);<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, &regulator);<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, &regulator);<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, &regulator);<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, &regulator);<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, &regulator);<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, &regulator);<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, &regulator);<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, &regulator);<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, &regulator);<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, &regulator);<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, &regulator);<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, &regulator);<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(&regulator, 0, sizeof(regulator));<br>
> +<br>
> +  sc = rtems_regulator_delete(&regulator);<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(&regulator, 0, sizeof(regulator));<br>
> +<br>
> +  sc = rtems_regulator_obtain_buffer(&regulator, &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(&regulator, 0, sizeof(regulator));<br>
> +<br>
> +  sc = rtems_regulator_release_buffer(&regulator, &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(&regulator, 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(&regulator, 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(&regulator, 0, sizeof(regulator));<br>
> +<br>
> +  sc = rtems_regulator_send(&regulator, 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 = 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(250),<br>
> +    .maximum_to_dequeue_per_period = 3<br>
> +  };<br>
> +<br>
> +  sc = rtems_regulator_create(&attributes, &regulator);<br>
> +  rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
> +  rtems_test_assert(regulator != NULL);<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>
> +<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 = NULL,<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, &regulator);<br>
> +  rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
> +  rtems_test_assert(regulator != NULL);<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>
> +<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..7c266dc406<br>
> --- /dev/null<br>
> +++ b/testsuites/libtests/regulator01/regulator01.doc<br>
> @@ -0,0 +1,37 @@<br>
> +# SPDX-License-Identifier: BSD-2-Clause<br>
> +<br>
> +# Copyright (c) 2013 Daniel Ramirez <<a href="mailto:javamonn@gmail.com" target="_blank">javamonn@gmail.com</a>><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: uid01<br>
> +<br>
> +directives:<br>
> +  + uid_read_message<br>
> +<br>
> +concepts:<br>
> +  + Tests that uid_read_message when called with a timeout greater than 0<br>
> +    ticks but less than 1 tick will correctly round this up to a 1 tick<br>
> +    timeout.<br></blockquote><div><br></div><div>This file was a bogus copy of another test. 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">
> 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>
</blockquote></div></div>