Use latest version of conf.d/templates submodule.
[apps/agl-service-unicens.git] / ucs2-lib / src / ucs_epm.c
1 /*------------------------------------------------------------------------------------------------*/
2 /* UNICENS V2.1.0-3491                                                                            */
3 /* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG.                              */
4 /*                                                                                                */
5 /* This program is free software: you can redistribute it and/or modify                           */
6 /* it under the terms of the GNU General Public License as published by                           */
7 /* the Free Software Foundation, either version 2 of the License, or                              */
8 /* (at your option) any later version.                                                            */
9 /*                                                                                                */
10 /* This program is distributed in the hope that it will be useful,                                */
11 /* but WITHOUT ANY WARRANTY; without even the implied warranty of                                 */
12 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                  */
13 /* GNU General Public License for more details.                                                   */
14 /*                                                                                                */
15 /* You should have received a copy of the GNU General Public License                              */
16 /* along with this program.  If not, see <http://www.gnu.org/licenses/>.                          */
17 /*                                                                                                */
18 /* You may also obtain this software under a propriety license from Microchip.                    */
19 /* Please contact Microchip for further information.                                              */
20 /*------------------------------------------------------------------------------------------------*/
21
22 /*!
23  * \file
24  * \brief Implementation of the EndPoint Management.
25  *
26  * \cond UCS_INTERNAL_DOC
27  * \addtogroup G_EPM
28  * @{
29  */
30
31 /*------------------------------------------------------------------------------------------------*/
32 /* Includes                                                                                       */
33 /*------------------------------------------------------------------------------------------------*/
34 #include "ucs_epm.h"
35 #include "ucs_misc.h"
36
37 /*------------------------------------------------------------------------------------------------*/
38 /* Internal prototypes                                                                            */
39 /*------------------------------------------------------------------------------------------------*/
40 static void Epm_XrmReportCb (uint16_t node_address, uint16_t connection_label, Ucs_Xrm_Result_t result, void * user_arg);
41 static bool Epm_RsmReportSyncLost (Fac_Inst_t inst_type, void * inst_ptr, void *ud_ptr);
42
43 /*------------------------------------------------------------------------------------------------*/
44 /* Implementation of class CEndpointManagement                                                     */
45 /*------------------------------------------------------------------------------------------------*/
46 /*------------------------------------------------------------------------------------------------*/
47 /* Initialization Methods                                                                         */
48 /*------------------------------------------------------------------------------------------------*/
49 /*! \brief Constructor of the Remote Sync Manager class.
50  *  \param self        Instance pointer
51  *  \param init_ptr    init data_ptr
52  */
53 void Epm_Ctor(CEndpointManagement *self, Epm_InitData_t *init_ptr)
54 {
55     MISC_MEM_SET(self, 0, sizeof(CEndpointManagement));
56
57     /* Init all instances */
58     self->fac_ptr = init_ptr->fac_ptr;
59     self->base_ptr = init_ptr->base_ptr;
60     self->res_debugging_fptr = init_ptr->res_debugging_fptr;
61     self->check_unmute_fptr  = init_ptr->check_unmute_fptr;
62 }
63
64 /*! \brief Initializes the internal information of the given endpoint object.
65  *
66  *  Initialization is performed only if the magic number is not set.
67  *
68  *  \param self      Instance pointer
69  *  \param ep_ptr    Reference to the endpoint to be looked for
70  */
71 void Epm_InitInternalInfos(CEndpointManagement * self, Ucs_Rm_EndPoint_t * ep_ptr)
72 {
73     if ((self != NULL) && (ep_ptr != NULL))
74     {
75         if (ep_ptr->internal_infos.magic_number != (uint32_t)0x0BADC0DE)
76         {
77             MISC_MEM_SET(&ep_ptr->internal_infos, 0, sizeof(Ucs_Rm_EndPointInt_t));
78
79             ep_ptr->internal_infos.magic_number = (uint32_t)0x0BADC0DE;
80             Sub_Ctor(&ep_ptr->internal_infos.subject_obj, self->base_ptr->ucs_user_ptr);
81             /* Set the EndpointManagement instance */
82             ep_ptr->internal_infos.epm_inst = (Epm_Inst_t *)(void *)self;
83         }
84     }
85 }
86
87 /*! \brief Clears the internal information of the given endpoint object.
88  *
89  * Resetting the magic number of the given endpoint will enforce Its Re-Initialization.
90  *
91  *  \param self      Instance pointer
92  *  \param ep_ptr    Reference to the endpoint to be cleared.
93  */
94 void Epm_ClearIntInfos(CEndpointManagement * self, Ucs_Rm_EndPoint_t * ep_ptr)
95 {
96     MISC_UNUSED (self);
97     if (ep_ptr != NULL)
98     {
99         ep_ptr->internal_infos.magic_number = 0x0U;
100     }
101 }
102
103 /*------------------------------------------------------------------------------------------------*/
104 /* Service                                                                                        */
105 /*------------------------------------------------------------------------------------------------*/
106 /*! \brief Add an observer to the Endpoint's subject.
107  *  \param ep_ptr     Reference to the endpoint instance
108  *  \param obs_ptr    Reference to the observer object
109  */
110 void Epm_AddObserver(Ucs_Rm_EndPoint_t * ep_ptr, CObserver * obs_ptr)
111 {
112     Sub_Ret_t ret_val = SUB_UNKNOWN_OBSERVER;
113
114     ret_val = Sub_AddObserver(&ep_ptr->internal_infos.subject_obj, obs_ptr);
115     if (ret_val == SUB_OK)
116     {
117         if ((ep_ptr != NULL) && (ep_ptr->endpoint_type == UCS_RM_EP_SOURCE))
118         {
119             if ((ep_ptr->internal_infos.endpoint_state == UCS_RM_EP_BUILT) && (ep_ptr->internal_infos.reference_cnt > 0U))
120             {
121                 ep_ptr->internal_infos.reference_cnt++;
122             }
123         }
124     }
125 }
126
127 /*! \brief Removes an observer registered by Epm_AddObserver
128  *  \param ep_ptr     Reference to the endpoint instance
129  *  \param obs_ptr    Reference to the observer object
130  */
131 void Epm_DelObserver(Ucs_Rm_EndPoint_t * ep_ptr, CObserver * obs_ptr)
132 {
133     (void)Sub_RemoveObserver(&ep_ptr->internal_infos.subject_obj, obs_ptr);  
134 }
135
136 /*! \brief Processes the construction of the given endpoint
137  *  \param self               Instance pointer
138  *  \param ep_ptr             reference to an endpoint 
139  *  \return Possible return values are
140  *          - \c UCS_RET_ERR_API_LOCKED the API is locked. Endpoint is currently being processed.
141  *          - \c UCS_RET_SUCCESS the build process was set successfully
142  *          - \c UCS_RET_ERR_PARAM NULL pointer detected in the parameter list
143  *          - \c UCS_RET_ERR_ALREADY_SET the endpoint has already been set
144  */
145 Ucs_Return_t Epm_SetBuildProcess(CEndpointManagement * self, Ucs_Rm_EndPoint_t * ep_ptr)
146 {
147     Ucs_Return_t result = UCS_RET_ERR_PARAM;
148
149     if ((self != NULL)  && (ep_ptr != NULL))
150     {
151         /* Process Endpoint construction by XRM */
152         result = Xrm_Process(Fac_GetXrm(self->fac_ptr, ep_ptr->node_obj_ptr->signature_ptr->node_address, &Epm_XrmResDebugCb, self->check_unmute_fptr),
153                             ep_ptr->jobs_list_ptr, ep_ptr->internal_infos.connection_label, 
154                             (void *)ep_ptr, &Epm_XrmReportCb);
155         if (result == UCS_RET_SUCCESS)
156         {
157             if (ep_ptr->internal_infos.endpoint_state != UCS_RM_EP_BUILT)
158             {
159                 ep_ptr->internal_infos.endpoint_state = UCS_RM_EP_XRMPROCESSING;
160                 TR_INFO((self->base_ptr->ucs_user_ptr, "[EPM]", "XRM has been ordered to create following Endpoint: %X", 1U, ep_ptr));
161             }
162             else
163             {
164                 TR_INFO((self->base_ptr->ucs_user_ptr, "[EPM]", "Following Endpoint {%X} has already been built", 1U, ep_ptr));
165             }
166         }
167         else if (result == UCS_RET_ERR_ALREADY_SET)
168         {
169             if (ep_ptr->internal_infos.endpoint_state == UCS_RM_EP_IDLE)
170             {
171                 ep_ptr->internal_infos.endpoint_state = UCS_RM_EP_BUILT;          
172                 TR_INFO((self->base_ptr->ucs_user_ptr, "[EPM]", "Following Endpoint {%X} has already been built", 1U, ep_ptr));
173             }
174         }
175         else if (result == UCS_RET_ERR_NOT_AVAILABLE)
176         {
177             /* Set the internal error */
178             ep_ptr->internal_infos.endpoint_state  = UCS_RM_EP_IDLE;
179             ep_ptr->internal_infos.xrm_result.code = UCS_XRM_RES_ERR_BUILD;
180             ep_ptr->internal_infos.xrm_result.details.result_type = UCS_XRM_RESULT_TYPE_INT;
181             ep_ptr->internal_infos.xrm_result.details.int_result  = result;
182         }
183     }
184
185     return result;
186 }
187
188 /*! \brief Processes the destruction of the given endpoint
189  *  \param self               Instance pointer
190  *  \param ep_ptr             reference to an endpoint 
191  *  \return Possible return values are
192  *          - \c UCS_RET_ERR_API_LOCKED the API is locked. Endpoint is currently being processed.
193  *          - \c UCS_RET_SUCCESS the build process was set successfully
194  *          - \c UCS_RET_ERR_PARAM At least one parameter is not correct, either NULL pointer in the param list or reference_cnt of the endpoint is NULL.
195  *          - \c UCS_RET_ERR_ALREADY_SET the endpoint has already been set
196  *          - \c UCS_RET_ERR_NOT_AVAILABLE the endpoint cannot be destroyed since its reference_cnt is greater than 1, i.e. it's in use.
197  *          - \c UCS_RET_ERR_INVALID_SHADOW the endpoint cannot be destroyed since its reference_cnt is greater than 1, i.e. it's in use.
198  */
199 Ucs_Return_t Epm_SetDestroyProcess(CEndpointManagement * self, Ucs_Rm_EndPoint_t * ep_ptr)
200 {
201     Ucs_Return_t result = UCS_RET_ERR_PARAM;
202     bool can_be_destroyed = true;
203
204     if ((self != NULL)  && (ep_ptr != NULL) )
205     {
206         if (UCS_RM_EP_SOURCE == ep_ptr->endpoint_type)
207         {
208             if (ep_ptr->internal_infos.reference_cnt == 0U)
209             {
210                 can_be_destroyed = false;
211                 result = UCS_RET_ERR_PARAM;
212             }
213             else if (ep_ptr->internal_infos.reference_cnt > 1U)
214             {
215                 ep_ptr->internal_infos.reference_cnt--;
216                 can_be_destroyed = false;
217                 result = UCS_RET_ERR_INVALID_SHADOW;
218             }
219         }
220
221         if (can_be_destroyed)
222         {
223             result = Xrm_Destroy(Fac_GetXrmByJobList(self->fac_ptr, ep_ptr->jobs_list_ptr), ep_ptr->jobs_list_ptr);
224             if (result == UCS_RET_SUCCESS)
225             {
226                 ep_ptr->internal_infos.endpoint_state = UCS_RM_EP_XRMPROCESSING;
227                 TR_INFO((self->base_ptr->ucs_user_ptr, "[EPM]", "XRM has been ordered to destroy following Endpoint {%X}", 1U, ep_ptr));
228             }
229             else if (result == UCS_RET_ERR_ALREADY_SET)
230             {
231                 if (ep_ptr->internal_infos.endpoint_state == UCS_RM_EP_BUILT)
232                 {
233                     ep_ptr->internal_infos.endpoint_state = UCS_RM_EP_IDLE;          
234                     TR_INFO((self->base_ptr->ucs_user_ptr, "[EPM]", "Following Endpoint {%X} has already been destroyed", 1U, ep_ptr));
235                 }
236             }
237             else if (result == UCS_RET_ERR_NOT_AVAILABLE)
238             {
239                 if (ep_ptr->internal_infos.endpoint_state == UCS_RM_EP_BUILT)
240                 {
241                     ep_ptr->internal_infos.endpoint_state = UCS_RM_EP_IDLE;
242                     TR_INFO((self->base_ptr->ucs_user_ptr, "[EPM]", "Following Endpoint {%X} has already been destroyed", 1U, ep_ptr));
243                 }
244             }
245         }
246     }
247
248     return result;
249 }
250
251 /*! \brief Returns the state (idle, processing or built) of the given endpoint.
252  *  \param self   Instance pointer.
253  *  \param ep_ptr Reference to the endpoint to be looked for
254  *  \return state of the endpoint.
255  */
256 Ucs_Rm_EndPointState_t Epm_GetState(CEndpointManagement * self, Ucs_Rm_EndPoint_t * ep_ptr)
257 {
258     MISC_UNUSED (self);
259
260     return (ep_ptr != NULL) ? ep_ptr->internal_infos.endpoint_state:UCS_RM_EP_IDLE;
261 }
262
263 /*! \brief Forces EPM to reset the state of this endpoint.
264  *  \param self      Instance pointer.
265  *  \param ep_ptr    Reference to the endpoint to be looked for.
266  */
267 void Epm_ResetState(CEndpointManagement * self, Ucs_Rm_EndPoint_t * ep_ptr)
268 {
269     MISC_UNUSED (self);
270
271     if (ep_ptr != NULL)
272     {
273         ep_ptr->internal_infos.endpoint_state  = UCS_RM_EP_IDLE;
274         ep_ptr->internal_infos.xrm_result.code = UCS_XRM_RES_UNKNOWN;
275     }
276 }
277
278 /*! \brief Sets the connection label of the given endpoint.
279  *  \param self       Instance pointer.
280  *  \param ep_ptr     Reference to the endpoint to be looked for
281  *  \param conn_label connection label to be set
282  */
283 void Epm_SetConnectionLabel(CEndpointManagement * self, Ucs_Rm_EndPoint_t * ep_ptr, uint16_t conn_label)
284 {
285     MISC_UNUSED (self);
286
287     if (ep_ptr != NULL)
288     {
289         ep_ptr->internal_infos.connection_label = conn_label;
290     }
291 }
292
293 /*! \brief Returns the connection label of the given endpoint.
294  *  \param self   Instance pointer.
295  *  \param ep_ptr Reference to the endpoint to be looked for
296  *  \return connection label of the endpoint.
297  */
298 uint16_t Epm_GetConnectionLabel(CEndpointManagement * self, Ucs_Rm_EndPoint_t * ep_ptr)
299 {
300     MISC_UNUSED (self);
301
302     return (ep_ptr != NULL) ? ep_ptr->internal_infos.connection_label:0U;
303 }
304
305 /*! \brief  This function must be called when a device get invalid. 
306  *  \param  self                Reference to the MNS instance.
307  *  \param  destination_address MOST device address of the target.
308  */
309 void Epm_ReportInvalidDevice(CEndpointManagement *self, uint16_t destination_address)
310 {
311     if (MSG_ADDR_INIC != destination_address)
312     {
313         CRemoteSyncManagement * rsm_inst = Fac_FindRsm(self->fac_ptr, destination_address);
314         if (NULL != rsm_inst)
315         {
316             Rsm_ReportSyncLost(rsm_inst);
317         }
318     }
319 }
320
321 /*! \brief Whenever this function has been called, the EndpointManager has to inform his sub-modules that a shutdown occurred. 
322  *         This function forwards the Network "NotAvailable" information
323  *  \param self   Instance pointer.
324  */
325 void Epm_ReportShutDown(CEndpointManagement * self)
326 {
327     Fac_Foreach(self->fac_ptr, FAC_INST_RSM, &Epm_RsmReportSyncLost, NULL);
328 }
329
330 /*! \brief  Function signature used for monitoring the XRM resources.
331  *  \param  resource_type       The XRM resource type to be looked for
332  *  \param  resource_ptr        Reference to the resource to be looked for
333  *  \param  resource_infos      Resource information
334  *  \param  endpoint_inst_ptr   Reference to the endpoint object that encapsulates the given resource.
335  *  \param  user_ptr            User reference provided in \ref Ucs_InitData_t "Ucs_InitData_t::user_ptr"
336  */
337 void Epm_XrmResDebugCb (Ucs_Xrm_ResourceType_t resource_type, Ucs_Xrm_ResObject_t *resource_ptr, 
338                                Ucs_Xrm_ResourceInfos_t resource_infos, void *endpoint_inst_ptr, void *user_ptr)
339 {
340     Ucs_Rm_EndPoint_t * ep_ptr = (Ucs_Rm_EndPoint_t *)endpoint_inst_ptr;
341     if (ep_ptr != NULL)
342     {
343         CEndpointManagement * self = (CEndpointManagement *)(void *)ep_ptr->internal_infos.epm_inst;
344         if (self->res_debugging_fptr != NULL)
345         {
346             self->res_debugging_fptr(resource_type, resource_ptr, resource_infos, ep_ptr, user_ptr);
347         }
348     }
349 }
350
351 /*------------------------------------------------------------------------------------------------*/
352 /* Private Methods                                                                                */
353 /*------------------------------------------------------------------------------------------------*/
354 /*! \brief Reports "SyncLost" to the RSM instance returned.
355  *  \param inst_type  The instance type to be looked for.
356  *  \param inst_ptr   Reference to the instance to be looked for.
357  *  \param ud_ptr     Reference to the user data.
358  *  \return false in order to retrieve the next instance of the given type, otherwise false.
359  */
360 static bool Epm_RsmReportSyncLost(Fac_Inst_t inst_type, void * inst_ptr, void *ud_ptr)
361 {
362     bool ret_val = false;
363     MISC_UNUSED(ud_ptr);
364
365     switch (inst_type)
366     {
367         case FAC_INST_RSM:
368             Rsm_ReportSyncLost((CRemoteSyncManagement *)inst_ptr);
369             break;
370
371         default:
372             ret_val = true;
373             break;
374     }
375
376     return ret_val;
377 }
378
379 /*------------------------------------------------------------------------------------------------*/
380 /* Callback Functions                                                                             */
381 /*------------------------------------------------------------------------------------------------*/
382 /*! \brief  XRM report callback function.
383  *  \param  node_address        The node address from which the results come 
384  *  \param  connection_label    Returned MOST network connection label
385  *  \param  result              Result of the job
386  *  \param  user_arg            Reference to the user argument
387  */
388 static void Epm_XrmReportCb(uint16_t node_address, uint16_t connection_label, Ucs_Xrm_Result_t result, void * user_arg)
389 {
390     Ucs_Rm_EndPoint_t * ep_ptr = (Ucs_Rm_EndPoint_t *)user_arg;
391     uint8_t handle_not_found   = 0x32U;
392     uint8_t error_id = 2U;
393
394     MISC_UNUSED (node_address);
395
396     if (ep_ptr != NULL)
397     {
398         ep_ptr->internal_infos.xrm_result = result;
399         switch (result.code)
400         {
401         case UCS_XRM_RES_SUCCESS_BUILD:
402             ep_ptr->internal_infos.connection_label = connection_label;
403             ep_ptr->internal_infos.endpoint_state = UCS_RM_EP_BUILT;
404             if (ep_ptr->endpoint_type == UCS_RM_EP_SOURCE)
405             {
406                 ep_ptr->internal_infos.reference_cnt++;
407             }            
408             TR_INFO((((CEndpointManagement *)(void *)ep_ptr->internal_infos.epm_inst)->base_ptr->ucs_user_ptr, "[EPM]", "Following Endpoint {%X} has been successfully built", 1U, ep_ptr));
409             break;
410
411         case UCS_XRM_RES_SUCCESS_DESTROY:
412             ep_ptr->internal_infos.connection_label = 0xFFFFU;
413             ep_ptr->internal_infos.endpoint_state = UCS_RM_EP_IDLE;
414             if (ep_ptr->endpoint_type == UCS_RM_EP_SOURCE)
415             {
416                 if (ep_ptr->internal_infos.reference_cnt > 0U)
417                 {
418                     ep_ptr->internal_infos.reference_cnt--;
419                 }
420             }
421             TR_INFO((((CEndpointManagement *)(void *)ep_ptr->internal_infos.epm_inst)->base_ptr->ucs_user_ptr, "[EPM]", "Following Endpoint {%X} has been successfully destroyed", 1U, ep_ptr));
422             break;
423
424         case UCS_XRM_RES_RC_AUTO_DESTROYED:
425             TR_ERROR((((CEndpointManagement *)(void *)ep_ptr->internal_infos.epm_inst)->base_ptr->ucs_user_ptr, "[EPM]", "Following Endpoint {%X} has been auto destroyed.", 1U, ep_ptr));
426             ep_ptr->internal_infos.connection_label = 0xFFFFU;
427             ep_ptr->internal_infos.endpoint_state = UCS_RM_EP_IDLE;
428             if (ep_ptr->endpoint_type == UCS_RM_EP_SOURCE)
429             {
430                 ep_ptr->internal_infos.reference_cnt = 0U;
431             }
432             if(Sub_GetNumObservers(&ep_ptr->internal_infos.subject_obj) > 0U)
433             {
434                 Sub_Notify(&ep_ptr->internal_infos.subject_obj, (void *)ep_ptr);
435             }
436             break;
437
438         case UCS_XRM_RES_ERR_CONFIG:
439         case UCS_XRM_RES_ERR_SYNC:
440         case UCS_XRM_RES_ERR_BUILD:
441             ep_ptr->internal_infos.connection_label = 0xFFFFU;
442             ep_ptr->internal_infos.endpoint_state = UCS_RM_EP_IDLE;
443             TR_ERROR((((CEndpointManagement *)(void *)ep_ptr->internal_infos.epm_inst)->base_ptr->ucs_user_ptr, "[EPM]", "Building endpoint {%X} failed. Error_Code: 0x%02X", 2U, ep_ptr, result.code));
444             break;
445
446         case UCS_XRM_RES_ERR_DESTROY:
447             ep_ptr->internal_infos.endpoint_state = UCS_RM_EP_IDLE;
448             if (ep_ptr->internal_infos.xrm_result.details.result_type == UCS_XRM_RESULT_TYPE_TGT)
449             {
450                 if ((ep_ptr->internal_infos.xrm_result.details.inic_result.code == UCS_RES_ERR_CONFIGURATION) &&
451                     (ep_ptr->internal_infos.xrm_result.details.inic_result.info_ptr != NULL) && 
452                     (ep_ptr->internal_infos.xrm_result.details.inic_result.info_size > 2U))
453                 {
454                     if (ep_ptr->internal_infos.xrm_result.details.inic_result.info_ptr[error_id] == handle_not_found)
455                     {
456                         ep_ptr->internal_infos.xrm_result.code = UCS_XRM_RES_SUCCESS_DESTROY;
457                     }
458                 }
459             }
460             if (ep_ptr->endpoint_type == UCS_RM_EP_SOURCE)
461             {
462                 ep_ptr->internal_infos.reference_cnt = 0U;
463             }
464             TR_ERROR((((CEndpointManagement *)(void *)ep_ptr->internal_infos.epm_inst)->base_ptr->ucs_user_ptr, "[EPM]", "Destroying endpoint {%X} failed. Error_Code: 0x%02X", 2U, ep_ptr, result.code));
465             break;
466
467         case UCS_XRM_RES_ERR_INV_LIST:
468             TR_ERROR((((CEndpointManagement *)(void *)ep_ptr->internal_infos.epm_inst)->base_ptr->ucs_user_ptr, "[EPM]", "Request of invalid lists on endpoint {%X} failed.", 1U, ep_ptr));
469             if (ep_ptr->internal_infos.endpoint_state == UCS_RM_EP_BUILT)
470             {
471                 ep_ptr->internal_infos.connection_label = 0xFFFFU;
472                 ep_ptr->internal_infos.endpoint_state = UCS_RM_EP_IDLE;
473                 if(Sub_GetNumObservers(&ep_ptr->internal_infos.subject_obj) > 0U)
474                 {
475                     Sub_Notify(&ep_ptr->internal_infos.subject_obj, (void *)ep_ptr);
476                 }
477             }
478             break;
479
480         default:
481             TR_ERROR((((CEndpointManagement *)(void *)ep_ptr->internal_infos.epm_inst)->base_ptr->ucs_user_ptr, "[EPM]", "Processing endpoint {%X} failed. Unknown Error_Code: 0x%02X", 2U, ep_ptr, result.code));
482             break;
483         }
484     }
485 }
486
487 /*!
488  * @}
489  * \endcond
490  */
491
492 /*------------------------------------------------------------------------------------------------*/
493 /* End of file                                                                                    */
494 /*------------------------------------------------------------------------------------------------*/
495