Back to home page

LXR

 
 

    


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

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /**
0004  * @file
0005  *
0006  * @ingroup RtemsBarrierReqWait
0007  */
0008 
0009 /*
0010  * Copyright (C) 2021 embedded brains GmbH & Co. KG
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 /*
0035  * This file is part of the RTEMS quality process and was automatically
0036  * generated.  If you find something that needs to be fixed or
0037  * worded better please post a report or patch to an RTEMS mailing list
0038  * or raise a bug report:
0039  *
0040  * https://www.rtems.org/bugs.html
0041  *
0042  * For information on updating and regenerating please refer to the How-To
0043  * section in the Software Requirements Engineering chapter of the
0044  * RTEMS Software Engineering manual.  The manual is provided as a part of
0045  * a release.  For development sources please refer to the online
0046  * documentation at:
0047  *
0048  * https://docs.rtems.org
0049  */
0050 
0051 #ifdef HAVE_CONFIG_H
0052 #include "config.h"
0053 #endif
0054 
0055 #include <rtems.h>
0056 #include <string.h>
0057 
0058 #include "tx-support.h"
0059 
0060 #include <rtems/test.h>
0061 
0062 /**
0063  * @defgroup RtemsBarrierReqWait spec:/rtems/barrier/req/wait
0064  *
0065  * @ingroup TestsuitesValidationNoClock0
0066  *
0067  * @{
0068  */
0069 
0070 typedef enum {
0071   RtemsBarrierReqWait_Pre_Id_NoObj,
0072   RtemsBarrierReqWait_Pre_Id_Manual,
0073   RtemsBarrierReqWait_Pre_Id_Auto,
0074   RtemsBarrierReqWait_Pre_Id_NA
0075 } RtemsBarrierReqWait_Pre_Id;
0076 
0077 typedef enum {
0078   RtemsBarrierReqWait_Pre_Timeout_Ticks,
0079   RtemsBarrierReqWait_Pre_Timeout_Forever,
0080   RtemsBarrierReqWait_Pre_Timeout_NA
0081 } RtemsBarrierReqWait_Pre_Timeout;
0082 
0083 typedef enum {
0084   RtemsBarrierReqWait_Pre_Satisfy_Never,
0085   RtemsBarrierReqWait_Pre_Satisfy_Wait,
0086   RtemsBarrierReqWait_Pre_Satisfy_Release,
0087   RtemsBarrierReqWait_Pre_Satisfy_Delete,
0088   RtemsBarrierReqWait_Pre_Satisfy_NA
0089 } RtemsBarrierReqWait_Pre_Satisfy;
0090 
0091 typedef enum {
0092   RtemsBarrierReqWait_Post_Status_Ok,
0093   RtemsBarrierReqWait_Post_Status_InvId,
0094   RtemsBarrierReqWait_Post_Status_Timeout,
0095   RtemsBarrierReqWait_Post_Status_ObjDel,
0096   RtemsBarrierReqWait_Post_Status_NoReturn,
0097   RtemsBarrierReqWait_Post_Status_NA
0098 } RtemsBarrierReqWait_Post_Status;
0099 
0100 typedef struct {
0101   uint8_t Skip : 1;
0102   uint8_t Pre_Id_NA : 1;
0103   uint8_t Pre_Timeout_NA : 1;
0104   uint8_t Pre_Satisfy_NA : 1;
0105   uint8_t Post_Status : 3;
0106 } RtemsBarrierReqWait_Entry;
0107 
0108 /**
0109  * @brief Test context for spec:/rtems/barrier/req/wait test case.
0110  */
0111 typedef struct {
0112   rtems_id main_id;
0113 
0114   rtems_id high_worker_id;
0115 
0116   rtems_id low_worker_id;
0117 
0118   rtems_id manual_release_id;
0119 
0120   rtems_id auto_release_id;
0121 
0122   rtems_id id;
0123 
0124   rtems_interval timeout;
0125 
0126   rtems_status_code status;
0127 
0128   struct {
0129     /**
0130      * @brief This member defines the pre-condition indices for the next
0131      *   action.
0132      */
0133     size_t pci[ 3 ];
0134 
0135     /**
0136      * @brief This member defines the pre-condition states for the next action.
0137      */
0138     size_t pcs[ 3 ];
0139 
0140     /**
0141      * @brief If this member is true, then the test action loop is executed.
0142      */
0143     bool in_action_loop;
0144 
0145     /**
0146      * @brief This member contains the next transition map index.
0147      */
0148     size_t index;
0149 
0150     /**
0151      * @brief This member contains the current transition map entry.
0152      */
0153     RtemsBarrierReqWait_Entry entry;
0154 
0155     /**
0156      * @brief If this member is true, then the current transition variant
0157      *   should be skipped.
0158      */
0159     bool skip;
0160   } Map;
0161 } RtemsBarrierReqWait_Context;
0162 
0163 static RtemsBarrierReqWait_Context
0164   RtemsBarrierReqWait_Instance;
0165 
0166 static const char * const RtemsBarrierReqWait_PreDesc_Id[] = {
0167   "NoObj",
0168   "Manual",
0169   "Auto",
0170   "NA"
0171 };
0172 
0173 static const char * const RtemsBarrierReqWait_PreDesc_Timeout[] = {
0174   "Ticks",
0175   "Forever",
0176   "NA"
0177 };
0178 
0179 static const char * const RtemsBarrierReqWait_PreDesc_Satisfy[] = {
0180   "Never",
0181   "Wait",
0182   "Release",
0183   "Delete",
0184   "NA"
0185 };
0186 
0187 static const char * const * const RtemsBarrierReqWait_PreDesc[] = {
0188   RtemsBarrierReqWait_PreDesc_Id,
0189   RtemsBarrierReqWait_PreDesc_Timeout,
0190   RtemsBarrierReqWait_PreDesc_Satisfy,
0191   NULL
0192 };
0193 
0194 #define NAME rtems_build_name( 'T', 'E', 'S', 'T' )
0195 
0196 #define EVENT_TIMER_INACTIVE RTEMS_EVENT_0
0197 
0198 #define EVENT_WAIT RTEMS_EVENT_1
0199 
0200 #define EVENT_RELEASE RTEMS_EVENT_2
0201 
0202 #define EVENT_DELETE RTEMS_EVENT_3
0203 
0204 #define EVENT_TIMER_EXPIRE RTEMS_EVENT_4
0205 
0206 typedef RtemsBarrierReqWait_Context Context;
0207 
0208 static void Worker( rtems_task_argument arg )
0209 {
0210   Context *ctx;
0211 
0212   ctx = (Context *) arg;
0213 
0214   while ( true ) {
0215     rtems_status_code sc;
0216     rtems_event_set   events;
0217 
0218     events = ReceiveAnyEvents();
0219 
0220     if ( ( events & EVENT_TIMER_INACTIVE ) != 0 ) {
0221       T_eq_int(
0222         T_get_thread_timer_state( ctx->main_id ),
0223         T_THREAD_TIMER_INACTIVE
0224       );
0225     }
0226 
0227     if ( ( events & EVENT_WAIT ) != 0 ) {
0228       sc = rtems_barrier_wait( ctx->id, RTEMS_NO_TIMEOUT );
0229       T_rsc_success( sc );
0230     }
0231 
0232     if ( ( events & EVENT_RELEASE ) != 0 ) {
0233       uint32_t released;
0234 
0235       sc = rtems_barrier_release( ctx->id, &released );
0236       T_rsc_success( sc );
0237     }
0238 
0239     if ( ( events & EVENT_DELETE ) != 0 ) {
0240       rtems_attribute     attribute_set;
0241       uint32_t            maximum_waiters;
0242       rtems_id           *id;
0243       rtems_task_priority prio;
0244 
0245       if ( ctx->id == ctx->manual_release_id ) {
0246         attribute_set = RTEMS_BARRIER_MANUAL_RELEASE;
0247         maximum_waiters = 0;
0248         id = &ctx->manual_release_id;
0249       } else {
0250         attribute_set = RTEMS_BARRIER_AUTOMATIC_RELEASE;
0251         maximum_waiters = 2;
0252         id = &ctx->auto_release_id;
0253       }
0254 
0255       prio = SetSelfPriority( PRIO_HIGH );
0256       T_eq_u32( prio, PRIO_LOW );
0257 
0258       sc = rtems_barrier_delete( ctx->id );
0259       T_rsc_success( sc );
0260 
0261       sc = rtems_barrier_create( NAME, attribute_set, maximum_waiters, id );
0262       T_rsc_success( sc );
0263 
0264       prio = SetSelfPriority( prio );
0265       T_eq_u32( prio, PRIO_HIGH );
0266     }
0267 
0268     if ( ( events & EVENT_TIMER_EXPIRE ) != 0 ) {
0269       T_eq_int(
0270         T_get_thread_timer_state( ctx->main_id ),
0271         T_THREAD_TIMER_SCHEDULED
0272       );
0273       FinalClockTick();
0274     }
0275   }
0276 }
0277 
0278 static void RtemsBarrierReqWait_Pre_Id_Prepare(
0279   RtemsBarrierReqWait_Context *ctx,
0280   RtemsBarrierReqWait_Pre_Id   state
0281 )
0282 {
0283   switch ( state ) {
0284     case RtemsBarrierReqWait_Pre_Id_NoObj: {
0285       /*
0286        * While the ``id`` parameter is not associated with a barrier.
0287        */
0288       ctx->id = 0xffffffff;
0289       break;
0290     }
0291 
0292     case RtemsBarrierReqWait_Pre_Id_Manual: {
0293       /*
0294        * While the ``id`` parameter is associated with a manual release
0295        * barrier.
0296        */
0297       ctx->id = ctx->manual_release_id;
0298       break;
0299     }
0300 
0301     case RtemsBarrierReqWait_Pre_Id_Auto: {
0302       /*
0303        * While the ``id`` parameter is associated with an automatic release
0304        * barrier.
0305        */
0306       ctx->id = ctx->auto_release_id;
0307       break;
0308     }
0309 
0310     case RtemsBarrierReqWait_Pre_Id_NA:
0311       break;
0312   }
0313 }
0314 
0315 static void RtemsBarrierReqWait_Pre_Timeout_Prepare(
0316   RtemsBarrierReqWait_Context    *ctx,
0317   RtemsBarrierReqWait_Pre_Timeout state
0318 )
0319 {
0320   switch ( state ) {
0321     case RtemsBarrierReqWait_Pre_Timeout_Ticks: {
0322       /*
0323        * While the ``released`` parameter is a clock tick interval.
0324        */
0325       ctx->timeout = UINT32_MAX;
0326       break;
0327     }
0328 
0329     case RtemsBarrierReqWait_Pre_Timeout_Forever: {
0330       /*
0331        * While the ``released`` parameter is RTEMS_NO_TIMEOUT.
0332        */
0333       ctx->timeout = RTEMS_NO_TIMEOUT;
0334       break;
0335     }
0336 
0337     case RtemsBarrierReqWait_Pre_Timeout_NA:
0338       break;
0339   }
0340 }
0341 
0342 static void RtemsBarrierReqWait_Pre_Satisfy_Prepare(
0343   RtemsBarrierReqWait_Context    *ctx,
0344   RtemsBarrierReqWait_Pre_Satisfy state
0345 )
0346 {
0347   switch ( state ) {
0348     case RtemsBarrierReqWait_Pre_Satisfy_Never: {
0349       /*
0350        * While the calling task waits at the barrier, while the barrier is not
0351        * released or deleted.
0352        */
0353       if ( ctx->timeout == RTEMS_NO_TIMEOUT ) {
0354         SendEvents( ctx->low_worker_id, EVENT_TIMER_INACTIVE | EVENT_RELEASE );
0355       } else {
0356         SendEvents( ctx->low_worker_id, EVENT_TIMER_EXPIRE );
0357       }
0358       break;
0359     }
0360 
0361     case RtemsBarrierReqWait_Pre_Satisfy_Wait: {
0362       /*
0363        * While calling the directive releases the barrier.
0364        */
0365       SendEvents( ctx->high_worker_id, EVENT_WAIT );
0366       break;
0367     }
0368 
0369     case RtemsBarrierReqWait_Pre_Satisfy_Release: {
0370       /*
0371        * While the calling task waits at the barrier, while the barrier is
0372        * released.
0373        */
0374       SendEvents( ctx->low_worker_id, EVENT_RELEASE );
0375       break;
0376     }
0377 
0378     case RtemsBarrierReqWait_Pre_Satisfy_Delete: {
0379       /*
0380        * While the calling task waits at the barrier, while the barrier is
0381        * deleted.
0382        */
0383       SendEvents( ctx->low_worker_id, EVENT_DELETE );
0384       break;
0385     }
0386 
0387     case RtemsBarrierReqWait_Pre_Satisfy_NA:
0388       break;
0389   }
0390 }
0391 
0392 static void RtemsBarrierReqWait_Post_Status_Check(
0393   RtemsBarrierReqWait_Context    *ctx,
0394   RtemsBarrierReqWait_Post_Status state
0395 )
0396 {
0397   switch ( state ) {
0398     case RtemsBarrierReqWait_Post_Status_Ok: {
0399       /*
0400        * The return status of rtems_barrier_wait() shall be RTEMS_SUCCESSFUL.
0401        */
0402       T_rsc_success( ctx->status );
0403       break;
0404     }
0405 
0406     case RtemsBarrierReqWait_Post_Status_InvId: {
0407       /*
0408        * The return status of rtems_barrier_wait() shall be RTEMS_INVALID_ID.
0409        */
0410       T_rsc( ctx->status, RTEMS_INVALID_ID );
0411       break;
0412     }
0413 
0414     case RtemsBarrierReqWait_Post_Status_Timeout: {
0415       /*
0416        * The return status of rtems_barrier_wait() shall be RTEMS_TIMEOUT.
0417        */
0418       T_rsc( ctx->status, RTEMS_TIMEOUT );
0419       break;
0420     }
0421 
0422     case RtemsBarrierReqWait_Post_Status_ObjDel: {
0423       /*
0424        * The return status of rtems_barrier_wait() shall be
0425        * RTEMS_OBJECT_WAS_DELETED.
0426        */
0427       T_rsc( ctx->status, RTEMS_OBJECT_WAS_DELETED );
0428       break;
0429     }
0430 
0431     case RtemsBarrierReqWait_Post_Status_NoReturn: {
0432       /*
0433        * The call to rtems_barrier_wait() shall not return to the calling task.
0434        */
0435       T_rsc_success( ctx->status );
0436       break;
0437     }
0438 
0439     case RtemsBarrierReqWait_Post_Status_NA:
0440       break;
0441   }
0442 }
0443 
0444 static void RtemsBarrierReqWait_Setup( RtemsBarrierReqWait_Context *ctx )
0445 {
0446   rtems_status_code sc;
0447 
0448   memset( ctx, 0, sizeof( *ctx ) );
0449   ctx->main_id = rtems_task_self();
0450   SetSelfPriority( PRIO_NORMAL );
0451   ctx->high_worker_id = CreateTask( "WRKH", PRIO_HIGH );
0452   StartTask( ctx->high_worker_id, Worker, ctx );
0453   ctx->low_worker_id = CreateTask( "WRKL", PRIO_LOW );
0454   StartTask( ctx->low_worker_id, Worker, ctx );
0455 
0456   sc = rtems_barrier_create(
0457     NAME,
0458     RTEMS_BARRIER_MANUAL_RELEASE,
0459     0,
0460     &ctx->manual_release_id
0461   );
0462   T_assert_rsc_success( sc );
0463 
0464   sc = rtems_barrier_create(
0465     NAME,
0466     RTEMS_BARRIER_AUTOMATIC_RELEASE,
0467     2,
0468     &ctx->auto_release_id
0469   );
0470   T_assert_rsc_success( sc );
0471 }
0472 
0473 static void RtemsBarrierReqWait_Setup_Wrap( void *arg )
0474 {
0475   RtemsBarrierReqWait_Context *ctx;
0476 
0477   ctx = arg;
0478   ctx->Map.in_action_loop = false;
0479   RtemsBarrierReqWait_Setup( ctx );
0480 }
0481 
0482 static void RtemsBarrierReqWait_Teardown( RtemsBarrierReqWait_Context *ctx )
0483 {
0484   rtems_status_code sc;
0485 
0486   DeleteTask( ctx->high_worker_id );
0487   DeleteTask( ctx->low_worker_id );
0488 
0489   if ( ctx->manual_release_id != 0 ) {
0490     sc = rtems_barrier_delete( ctx->manual_release_id );
0491     T_rsc_success( sc );
0492   }
0493 
0494   if ( ctx->auto_release_id != 0 ) {
0495     sc = rtems_barrier_delete( ctx->auto_release_id );
0496     T_rsc_success( sc );
0497   }
0498 
0499   RestoreRunnerPriority();
0500 }
0501 
0502 static void RtemsBarrierReqWait_Teardown_Wrap( void *arg )
0503 {
0504   RtemsBarrierReqWait_Context *ctx;
0505 
0506   ctx = arg;
0507   ctx->Map.in_action_loop = false;
0508   RtemsBarrierReqWait_Teardown( ctx );
0509 }
0510 
0511 static void RtemsBarrierReqWait_Action( RtemsBarrierReqWait_Context *ctx )
0512 {
0513   ctx->status = rtems_barrier_wait( ctx->id, ctx->timeout );
0514 }
0515 
0516 static const RtemsBarrierReqWait_Entry
0517 RtemsBarrierReqWait_Entries[] = {
0518   { 0, 0, 1, 1, RtemsBarrierReqWait_Post_Status_InvId },
0519   { 0, 0, 0, 0, RtemsBarrierReqWait_Post_Status_Ok },
0520   { 0, 0, 0, 0, RtemsBarrierReqWait_Post_Status_ObjDel },
0521   { 0, 0, 0, 0, RtemsBarrierReqWait_Post_Status_Timeout },
0522   { 1, 0, 0, 0, RtemsBarrierReqWait_Post_Status_NA },
0523   { 0, 0, 0, 0, RtemsBarrierReqWait_Post_Status_NoReturn }
0524 };
0525 
0526 static const uint8_t
0527 RtemsBarrierReqWait_Map[] = {
0528   0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 1, 2, 5, 4, 1, 2, 3, 1, 1, 2, 5, 1, 1, 2
0529 };
0530 
0531 static size_t RtemsBarrierReqWait_Scope( void *arg, char *buf, size_t n )
0532 {
0533   RtemsBarrierReqWait_Context *ctx;
0534 
0535   ctx = arg;
0536 
0537   if ( ctx->Map.in_action_loop ) {
0538     return T_get_scope( RtemsBarrierReqWait_PreDesc, buf, n, ctx->Map.pcs );
0539   }
0540 
0541   return 0;
0542 }
0543 
0544 static T_fixture RtemsBarrierReqWait_Fixture = {
0545   .setup = RtemsBarrierReqWait_Setup_Wrap,
0546   .stop = NULL,
0547   .teardown = RtemsBarrierReqWait_Teardown_Wrap,
0548   .scope = RtemsBarrierReqWait_Scope,
0549   .initial_context = &RtemsBarrierReqWait_Instance
0550 };
0551 
0552 static inline RtemsBarrierReqWait_Entry RtemsBarrierReqWait_PopEntry(
0553   RtemsBarrierReqWait_Context *ctx
0554 )
0555 {
0556   size_t index;
0557 
0558   index = ctx->Map.index;
0559   ctx->Map.index = index + 1;
0560   return RtemsBarrierReqWait_Entries[
0561     RtemsBarrierReqWait_Map[ index ]
0562   ];
0563 }
0564 
0565 static void RtemsBarrierReqWait_SetPreConditionStates(
0566   RtemsBarrierReqWait_Context *ctx
0567 )
0568 {
0569   ctx->Map.pcs[ 0 ] = ctx->Map.pci[ 0 ];
0570 
0571   if ( ctx->Map.entry.Pre_Timeout_NA ) {
0572     ctx->Map.pcs[ 1 ] = RtemsBarrierReqWait_Pre_Timeout_NA;
0573   } else {
0574     ctx->Map.pcs[ 1 ] = ctx->Map.pci[ 1 ];
0575   }
0576 
0577   if ( ctx->Map.entry.Pre_Satisfy_NA ) {
0578     ctx->Map.pcs[ 2 ] = RtemsBarrierReqWait_Pre_Satisfy_NA;
0579   } else {
0580     ctx->Map.pcs[ 2 ] = ctx->Map.pci[ 2 ];
0581   }
0582 }
0583 
0584 static void RtemsBarrierReqWait_TestVariant( RtemsBarrierReqWait_Context *ctx )
0585 {
0586   RtemsBarrierReqWait_Pre_Id_Prepare( ctx, ctx->Map.pcs[ 0 ] );
0587   RtemsBarrierReqWait_Pre_Timeout_Prepare( ctx, ctx->Map.pcs[ 1 ] );
0588   RtemsBarrierReqWait_Pre_Satisfy_Prepare( ctx, ctx->Map.pcs[ 2 ] );
0589   RtemsBarrierReqWait_Action( ctx );
0590   RtemsBarrierReqWait_Post_Status_Check( ctx, ctx->Map.entry.Post_Status );
0591 }
0592 
0593 /**
0594  * @fn void T_case_body_RtemsBarrierReqWait( void )
0595  */
0596 T_TEST_CASE_FIXTURE( RtemsBarrierReqWait, &RtemsBarrierReqWait_Fixture )
0597 {
0598   RtemsBarrierReqWait_Context *ctx;
0599 
0600   ctx = T_fixture_context();
0601   ctx->Map.in_action_loop = true;
0602   ctx->Map.index = 0;
0603 
0604   for (
0605     ctx->Map.pci[ 0 ] = RtemsBarrierReqWait_Pre_Id_NoObj;
0606     ctx->Map.pci[ 0 ] < RtemsBarrierReqWait_Pre_Id_NA;
0607     ++ctx->Map.pci[ 0 ]
0608   ) {
0609     for (
0610       ctx->Map.pci[ 1 ] = RtemsBarrierReqWait_Pre_Timeout_Ticks;
0611       ctx->Map.pci[ 1 ] < RtemsBarrierReqWait_Pre_Timeout_NA;
0612       ++ctx->Map.pci[ 1 ]
0613     ) {
0614       for (
0615         ctx->Map.pci[ 2 ] = RtemsBarrierReqWait_Pre_Satisfy_Never;
0616         ctx->Map.pci[ 2 ] < RtemsBarrierReqWait_Pre_Satisfy_NA;
0617         ++ctx->Map.pci[ 2 ]
0618       ) {
0619         ctx->Map.entry = RtemsBarrierReqWait_PopEntry( ctx );
0620 
0621         if ( ctx->Map.entry.Skip ) {
0622           continue;
0623         }
0624 
0625         RtemsBarrierReqWait_SetPreConditionStates( ctx );
0626         RtemsBarrierReqWait_TestVariant( ctx );
0627       }
0628     }
0629   }
0630 }
0631 
0632 /** @} */