Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:18

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /**
0004  * @file
0005  *
0006  * @brief Regulator Library Implementation
0007  */
0008 
0009 /*
0010  * Copyright (C) 2022 On-Line Applications Research Corporation (OAR)
0011  *
0012  * Redistribution and use in source and binary forms, with or without
0013  * modification, are permitted provided that the following conditions
0014  * are met:
0015  * 1. Redistributions of source code must retain the above copyright
0016  *    notice, this list of conditions and the following disclaimer.
0017  * 2. Redistributions in binary form must reproduce the above copyright
0018  *    notice, this list of conditions and the following disclaimer in the
0019  *    documentation and/or other materials provided with the distribution.
0020  *
0021  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0022  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0023  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0024  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0025  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0026  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0027  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0028  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0029  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0030  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0031  * POSSIBILITY OF SUCH DAMAGE.
0032  */
0033 
0034 #include <stdlib.h>
0035 
0036 #include <rtems.h>
0037 #include <rtems/regulator.h>
0038 #include <string.h>
0039 
0040 #include <rtems/regulatorimpl.h>
0041 
0042 /**
0043  * @ingroup RegulatorInternalAPI
0044  *
0045  * This method is the body for the task which delivers the output for
0046  * this regulator instance at the configured rate.
0047  *
0048  * @param[in] arg points to the regulator instance this thread
0049  *                is associated with
0050  *
0051  * @note The argument passed in cannot be NULL if the
0052  *       rtems_regulator_create worked.
0053  */
0054 static rtems_task _Regulator_Output_task_body(
0055   rtems_task_argument arg
0056 )
0057 {
0058   _Regulator_Control   *the_regulator = (_Regulator_Control *)arg;
0059   rtems_status_code     sc;
0060   size_t                to_dequeue;
0061   _Regulator_Message_t  regulator_message;
0062   size_t                regulator_message_size;
0063   bool                  release_it;
0064 
0065   the_regulator->delivery_thread_is_running   = true;
0066 
0067   /**
0068    * This thread uses a rate monotonic period object instance. A rate
0069    * monotonic period object must be created by the thread using it.
0070    * It can be deleted by any thread which simplifies clean up.
0071    *
0072    * The rate_monotonic_create() call can fail if the application
0073    * is incorrectly configured. This thread has no way to report the
0074    * failure. If it continues with an invalid id, then the thread will
0075    * not block on the period and spin continuously consuming CPU. The only
0076    * alternatives are to invoke rtems_fatal_error_occurred() or silently
0077    * exit the thread.
0078    */
0079   sc = rtems_rate_monotonic_create(
0080     rtems_build_name('P', 'E', 'R', 'D'),
0081     &the_regulator->delivery_thread_period_id
0082   );
0083   if (sc != RTEMS_SUCCESSFUL) {
0084     goto exit_delivery_thread;
0085   }
0086 
0087   /**
0088    * Loop on the rate_monotonic_period() based on the specified period.
0089    */
0090   while (1) {
0091     sc = rtems_rate_monotonic_period(
0092       the_regulator->delivery_thread_period_id,
0093       the_regulator->Attributes.delivery_thread_period
0094     );
0095      _Assert_Unused_variable_equals(sc, RTEMS_SUCCESSFUL);
0096 
0097     /**
0098      * If the delivery thread has been requested to exit, then
0099      * quit processing messages, break out of this loop, and exit
0100      * this thread.
0101      */
0102     if (the_regulator->delivery_thread_request_exit) {
0103       break;
0104     }
0105 
0106     /**
0107      * Loop for the configured number of messages to deliver per period.
0108      * If we reach the point, there are no more messages, block for the
0109      * rest of this period. If there are messages, deliver them.
0110      */
0111     for (to_dequeue = 0;
0112          to_dequeue < the_regulator->Attributes.maximum_to_dequeue_per_period;
0113          to_dequeue++) {
0114       regulator_message_size = sizeof(_Regulator_Message_t);
0115       sc = rtems_message_queue_receive(
0116         the_regulator->queue_id,
0117         &regulator_message,
0118         &regulator_message_size,
0119         RTEMS_NO_WAIT,
0120         0
0121       );
0122      _Assert_Unused_variable_equals(sc, RTEMS_SUCCESSFUL);
0123       if (sc != RTEMS_SUCCESSFUL) {
0124         break;
0125       }
0126 
0127       release_it = the_regulator->Attributes.deliverer(
0128         the_regulator->Attributes.deliverer_context,
0129         regulator_message.buffer,
0130         regulator_message.length
0131       );
0132 
0133       the_regulator->Statistics.delivered++;
0134 
0135       /**
0136        * The message was successfully delivered. If the delivery function
0137        * wants the buffer returned, do it now. The delivery to the Destination
0138        * may involve handing the buffer off to something like DMA
0139        * and need to wait for it to complete before releasing the buffer.
0140        *
0141        * Note that this is the underlying RTEMS service
0142        * used by @a rtems_regulator_obtain_buffer() and @a
0143        * rtems_regulator_release_buffer().
0144        */
0145       if (release_it == true) {
0146         the_regulator->Statistics.released++;
0147         sc = rtems_partition_return_buffer(
0148           the_regulator->messages_partition_id,
0149           regulator_message.buffer
0150         );
0151         _Assert_Unused_variable_equals(sc, RTEMS_SUCCESSFUL);
0152       }
0153     }
0154   }
0155 
0156   /**
0157    * This thread was requested to exit. Do so.
0158    */
0159 exit_delivery_thread:
0160   the_regulator->delivery_thread_is_running   = false;
0161   the_regulator->delivery_thread_has_exited   = true;
0162 
0163   (void) rtems_rate_monotonic_delete(the_regulator->delivery_thread_period_id);
0164 
0165   rtems_task_exit();
0166 }
0167 
0168 /**
0169  * @ingroup RegulatorInternalAPI
0170  *
0171  * This method frees the resources associated with a regulator instance.
0172  * The resources are freed in the opposite of the order in which they are
0173  * allocated. This is used on error cases in @a rtems_regulator_create() and in
0174  * @a rtems_regulator_delete().
0175  *
0176  * @param[in] the_regulator is the instance to operate upon
0177  * @param[in] ticks is the length of time to wait for the delivery thread
0178  *            to exit
0179  *
0180  * @return This method returns true is successful and false on timeout.
0181  */
0182 static bool _Regulator_Free_helper(
0183   _Regulator_Control *the_regulator,
0184   rtems_interval      ticks
0185 )
0186 {
0187   rtems_status_code  sc;
0188 
0189 
0190   /*
0191    * If the output thread has not started running, then we can just delete it.
0192    */
0193 
0194   if (ticks == 0 || the_regulator->delivery_thread_is_running == false) {
0195     sc = rtems_task_delete(the_regulator->delivery_thread_id);
0196     _Assert_Unused_variable_equals(sc, RTEMS_SUCCESSFUL);
0197   } else {
0198     rtems_interval remaining = ticks;
0199 
0200     the_regulator->delivery_thread_request_exit = true;
0201 
0202     while (1) {
0203       if (the_regulator->delivery_thread_has_exited) {
0204         break;
0205       }
0206     
0207       if (remaining == 0) {
0208         return false;
0209       }
0210 
0211       (void) rtems_task_wake_after(1);
0212       remaining--;
0213     }
0214   }
0215 
0216   /*
0217    * The output thread deletes the rate monotonic period that it created.
0218    */
0219 
0220   /*
0221    * The regulator's message_queue_storage is implicitly freed by this call.
0222    */
0223   sc = rtems_message_queue_delete(the_regulator->queue_id);
0224   _Assert_Unused_variable_equals(sc, RTEMS_SUCCESSFUL);
0225 
0226   sc = rtems_partition_delete(the_regulator->messages_partition_id);
0227   _Assert_Unused_variable_equals(sc, RTEMS_SUCCESSFUL);
0228 
0229   if (the_regulator->message_memory) {
0230     free(the_regulator->message_memory);
0231   }
0232 
0233   the_regulator->initialized = 0;
0234   free(the_regulator);
0235   return true;
0236 }
0237 
0238 /**
0239  * @ingroup RegulatorInternalAPI
0240  */
0241 rtems_status_code rtems_regulator_create(
0242   rtems_regulator_attributes  *attributes,
0243   rtems_regulator_instance   **regulator
0244 )
0245 {
0246   _Regulator_Control *the_regulator;
0247   rtems_status_code  sc;
0248   size_t             alloc_size;
0249 
0250   /**
0251    * Perform basic validation of parameters
0252    */
0253   if (attributes == NULL) {
0254     return RTEMS_INVALID_ADDRESS;
0255   }
0256 
0257   if (regulator == NULL) {
0258     return RTEMS_INVALID_ADDRESS;
0259   }
0260 
0261   /**
0262    * Verify attributes are OK. Some are checked by calls to object create
0263    * methods. Specifically the following are not checked:
0264    *
0265    * - delivery_thread_priority by rtems_task_create()
0266    * - delivery_thread_stack_size can be any value
0267    */
0268   if (attributes->deliverer == NULL) {
0269     return RTEMS_INVALID_ADDRESS;
0270   }
0271 
0272   if (attributes->maximum_messages == 0) {
0273     return RTEMS_INVALID_NUMBER;
0274   }
0275 
0276   if (attributes->maximum_message_size == 0) {
0277     return RTEMS_INVALID_SIZE;
0278   }
0279 
0280   if (attributes->maximum_to_dequeue_per_period == 0) {
0281     return RTEMS_INVALID_NUMBER;
0282   }
0283 
0284   if (attributes->delivery_thread_period == 0) {
0285     return RTEMS_INVALID_NUMBER;
0286   }
0287 
0288   /**
0289    * Allocate memory for regulator instance
0290    */
0291   the_regulator = (_Regulator_Control *) calloc(sizeof(_Regulator_Control), 1);
0292   if (the_regulator == NULL) {
0293     return RTEMS_NO_MEMORY;
0294   }
0295 
0296   /**
0297    * We do NOT want the delivery_thread_id field to be initialized to 0. If the
0298    * @a rtems_task_create() fails, then the field will not be overwritten.
0299    * This results in an attempt to rtems_task_delete(0) during clean
0300    * up. The thread ID of 0 is self which results in the calling thread
0301    * accidentally deleting itself.
0302    */
0303   the_regulator->delivery_thread_id = (rtems_id) -1;
0304 
0305   /**
0306    * Copy the attributes to an internal area for later use
0307    */
0308   the_regulator->Attributes = *attributes;
0309 
0310   /**
0311    * Allocate memory for the messages. There is no need to zero out the
0312    * message memory because the user should fill that in.
0313    */
0314   alloc_size = attributes->maximum_message_size * attributes->maximum_messages;
0315   the_regulator->message_memory = calloc(alloc_size, 1);
0316   if (the_regulator->message_memory == NULL) {
0317     _Regulator_Free_helper(the_regulator, 0);
0318     return RTEMS_NO_MEMORY;
0319   }
0320 
0321   /**
0322    * Associate message memory with a partition so allocations are atomic
0323    */
0324   sc = rtems_partition_create(
0325     rtems_build_name('P', 'O', 'O', 'L'),
0326     the_regulator->message_memory,
0327     alloc_size,
0328     attributes->maximum_message_size,
0329     RTEMS_DEFAULT_ATTRIBUTES,
0330     &the_regulator->messages_partition_id
0331   );
0332   if (sc != RTEMS_SUCCESSFUL) {
0333     _Regulator_Free_helper(the_regulator, 0);
0334     return sc;
0335   }
0336 
0337   /**
0338    * Create the message queue between the sender and output thread
0339    */
0340   RTEMS_MESSAGE_QUEUE_BUFFER(sizeof(_Regulator_Message_t)) regulator_message_t;
0341 
0342   size_t storage_size = sizeof(regulator_message_t) * attributes->maximum_messages;
0343 
0344   the_regulator->message_queue_storage = malloc(storage_size);
0345   if (the_regulator->message_queue_storage == NULL) {
0346     _Regulator_Free_helper(the_regulator, 0);
0347     return RTEMS_NO_MEMORY;
0348   }
0349 
0350   rtems_message_queue_config mq_config = {
0351     .name = rtems_build_name('S', 'N', 'D', 'Q'),
0352     .maximum_pending_messages =  attributes->maximum_messages,
0353     .maximum_message_size = sizeof(_Regulator_Message_t),
0354     .storage_area = the_regulator->message_queue_storage,
0355     .storage_size = storage_size,
0356     .storage_free = free,
0357     .attributes = RTEMS_DEFAULT_ATTRIBUTES
0358   };
0359   sc = rtems_message_queue_construct(
0360     &mq_config,
0361     &the_regulator->queue_id
0362   );
0363   if (sc != RTEMS_SUCCESSFUL) {
0364     _Regulator_Free_helper(the_regulator, 0);
0365     return sc;
0366   }
0367 
0368   /**
0369    * @note A rate monotonic period object must be created by the thread
0370    *       using it. Thus that specific create operation is not included
0371    *       in this method. All other resources are allocated here.
0372    */
0373 
0374   /**
0375    * Create the output thread Using the priority and stack size attributes
0376    * specified by the user.
0377    */
0378   sc = rtems_task_create(
0379     rtems_build_name('R', 'E', 'G', 'U'),
0380     attributes->delivery_thread_priority,
0381     attributes->delivery_thread_stack_size,
0382     RTEMS_DEFAULT_MODES,
0383     RTEMS_DEFAULT_ATTRIBUTES,
0384     &the_regulator->delivery_thread_id
0385   );
0386   if (sc != RTEMS_SUCCESSFUL) {
0387     _Regulator_Free_helper(the_regulator, 0);
0388     return sc;
0389   }
0390 
0391   /**
0392    * Start the output thread.
0393    *
0394    * @note There should be no way this call can fail. The task id is valid,
0395    *       the regulator output thread entry point is valid, and the argument
0396    *       is valid.
0397    */
0398   the_regulator->delivery_thread_is_running   = true;
0399   the_regulator->delivery_thread_request_exit = false;
0400   the_regulator->delivery_thread_has_exited   = false;
0401 
0402   sc = rtems_task_start(
0403     the_regulator->delivery_thread_id,
0404     _Regulator_Output_task_body,
0405     (rtems_task_argument) the_regulator
0406   );
0407  _Assert_Unused_variable_equals(sc, RTEMS_SUCCESSFUL);
0408 
0409   /**
0410    * The regulator is successfully initialized. Set the initialized field
0411    * to reflect this and return the instance pointer.
0412    */
0413   the_regulator->initialized = REGULATOR_INITIALIZED;
0414 
0415   *regulator = (void *)the_regulator;
0416 
0417   return RTEMS_SUCCESSFUL;
0418 }
0419 
0420 /**
0421  * @brief Validate the regulator instance provided by the user
0422  *
0423  * Validate the regulator instance provided by the user
0424  *
0425  * @param[in] regulator is the instance provided by the user
0426  * @param[inout] status will contain the RTEMS status for this check
0427  *
0428  * @return This method returns a @a _Regulator_Control instance pointer
0429  *         which is NULL if invalid or points to the internal regulator
0430  *         control structure if valid.
0431  */
0432 static inline _Regulator_Control *_Regulator_Get(
0433   rtems_regulator_instance *regulator,
0434   rtems_status_code        *status
0435 )
0436 {
0437   _Regulator_Control *the_regulator = (_Regulator_Control *) regulator;
0438 
0439   if (the_regulator == NULL) {
0440     *status = RTEMS_INVALID_ADDRESS;
0441     return NULL;
0442   }
0443 
0444   if (the_regulator->initialized != REGULATOR_INITIALIZED) {
0445     *status = RTEMS_INCORRECT_STATE;
0446     return NULL;
0447   }
0448 
0449   status = RTEMS_SUCCESSFUL;
0450   return the_regulator;
0451 }
0452 
0453 /**
0454  * @ingroup RegulatorInternalAPI
0455  */
0456 rtems_status_code rtems_regulator_delete(
0457   rtems_regulator_instance    *regulator,
0458   rtems_interval               ticks
0459 )
0460 {
0461   _Regulator_Control *the_regulator;
0462   rtems_status_code   status;
0463 
0464   /**
0465    * Convert external handle to internal instance pointer
0466    */
0467   the_regulator = _Regulator_Get(regulator, &status);
0468   if (the_regulator == NULL) {
0469     return status;
0470   }
0471 
0472   /**
0473    * There can be no buffers outstanding
0474    */
0475   _Regulator_Statistics *stats = &the_regulator->Statistics;
0476 
0477   if (stats->obtained != stats->released ) {
0478     return RTEMS_RESOURCE_IN_USE;
0479   }
0480 
0481   /**
0482    * Free the resources associated with this regulator instance.
0483    */
0484   bool bc;
0485   bc = _Regulator_Free_helper(the_regulator, ticks);
0486   if (bc == false) {
0487     return RTEMS_TIMEOUT;
0488   }
0489 
0490   return RTEMS_SUCCESSFUL;
0491 }
0492 
0493 /**
0494  * @ingroup RegulatorInternalAPI
0495  *
0496  * Allocate a buffer for the caller using the internal partition.
0497  */
0498 rtems_status_code rtems_regulator_obtain_buffer(
0499   rtems_regulator_instance    *regulator,
0500   void                       **buffer
0501 )
0502 {
0503   _Regulator_Control *the_regulator;
0504   rtems_status_code   status;
0505 
0506   /**
0507    * Convert external handle to internal instance pointer
0508    */
0509   the_regulator = _Regulator_Get(regulator, &status);
0510   if (the_regulator == NULL) {
0511     return status;
0512   }
0513 
0514   /**
0515    * Allocate a buffer for the user application from the buffer pool managed
0516    * by an Classic API partition.
0517    */
0518   status = rtems_partition_get_buffer(
0519     the_regulator->messages_partition_id,
0520     buffer
0521   );
0522 
0523   if (status == RTEMS_SUCCESSFUL) {
0524     the_regulator->Statistics.obtained++;
0525   }
0526 
0527   return status;
0528 }
0529 
0530 /**
0531  * @ingroup RegulatorInternalAPI
0532  *
0533  * Allocate a buffer for the caller using the internal partition.
0534  */
0535 rtems_status_code rtems_regulator_release_buffer(
0536   rtems_regulator_instance  *regulator,
0537   void                      *buffer
0538 )
0539 {
0540   _Regulator_Control *the_regulator;
0541   rtems_status_code   status;
0542 
0543   /**
0544    * Convert external handle to internal instance pointer
0545    */
0546   the_regulator = _Regulator_Get(regulator, &status);
0547   if (the_regulator == NULL) {
0548     return status;
0549   }
0550 
0551   /**
0552    * Deallocate the buffer to the buffer pool managed by a Classic
0553    * API partition.
0554    */
0555   status = rtems_partition_return_buffer(
0556     the_regulator->messages_partition_id,
0557     buffer
0558   );
0559 
0560   if (status == RTEMS_SUCCESSFUL) {
0561     the_regulator->Statistics.released++;
0562   }
0563 
0564   return status;
0565 }
0566 
0567 /**
0568  * @ingroup RegulatorInternalAPI
0569  */
0570 rtems_status_code rtems_regulator_send(
0571   rtems_regulator_instance *regulator,
0572   void                     *message,
0573   size_t                    length
0574 )
0575 {
0576   _Regulator_Control   *the_regulator;
0577   rtems_status_code     status;
0578   _Regulator_Message_t  regulator_message;
0579 
0580   the_regulator = (_Regulator_Control *) regulator;
0581 
0582   /**
0583    * Validate the arguments and ensure the regulator was successfully
0584    * initialized.
0585    */
0586   if (message == NULL) {
0587     return RTEMS_INVALID_ADDRESS;
0588   }
0589 
0590   if (length == 0) {
0591     return RTEMS_INVALID_NUMBER;
0592   }
0593 
0594   /**
0595    * Convert external handle to internal instance pointer
0596    */
0597   the_regulator = _Regulator_Get(regulator, &status);
0598   if (the_regulator == NULL) {
0599     return status;
0600   }
0601 
0602   /**
0603    * Place the message pointer and length into a temporary structure. This
0604    * lets the implementation internally send the message by reference and
0605    * have a zero-copy implementation.
0606    */
0607   regulator_message.buffer = message;
0608   regulator_message.length = length;
0609 
0610   /**
0611    * Send the application message to the output thread for delivery using
0612    * a Classic API message queue.
0613    */
0614   status = rtems_message_queue_send(
0615     the_regulator->queue_id,
0616     &regulator_message,
0617     sizeof(_Regulator_Message_t)
0618   );
0619   if (status != RTEMS_SUCCESSFUL) {
0620     return status;
0621   }
0622 
0623   return status;
0624 }
0625 
0626 /**
0627  * @ingroup RegulatorInternalAPI
0628  */
0629 rtems_status_code rtems_regulator_get_statistics(
0630   rtems_regulator_instance   *regulator,
0631   rtems_regulator_statistics *statistics
0632 )
0633 {
0634   _Regulator_Control   *the_regulator;
0635   rtems_status_code     status;
0636 
0637   /**
0638    * Validate the arguments and ensure the regulator was successfully
0639    * initialized.
0640    */
0641   if (statistics == NULL) {
0642     return RTEMS_INVALID_ADDRESS;
0643   }
0644 
0645   /**
0646    * Convert external handle to internal instance pointer
0647    */
0648   the_regulator = _Regulator_Get(regulator, &status);
0649   if (the_regulator == NULL) {
0650     return status;
0651   }
0652 
0653   /**
0654    * Zero out the statistics structure in case the get period statistics
0655    * fails below.
0656    */
0657   memset(statistics, 0, sizeof(rtems_regulator_statistics)); 
0658 
0659   /**
0660    * Fill in the caller's statistics structure from information
0661    * maintained by the regulator instance about buffers processed.
0662    */
0663   statistics->obtained  = the_regulator->Statistics.obtained;
0664   statistics->released  = the_regulator->Statistics.released;
0665   statistics->delivered = the_regulator->Statistics.delivered;
0666 
0667   /**
0668    * Attempt to retrieve the delivery thread's period's statistics.
0669    *
0670    * NOTE; If the Delivery Thread has not run yet, the period will not
0671    *       exist yet. We should not fail for this reason but it is why
0672    *       we zeroed out the entire structure above.
0673    */
0674   (void) rtems_rate_monotonic_get_statistics(
0675     the_regulator->delivery_thread_period_id,
0676     &statistics->period_statistics
0677   );
0678 
0679   return RTEMS_SUCCESSFUL;
0680 }