E2AP changes Part2
[o-du/l2.git] / src / ric_stub / ric_e2ap_msg_hdl.c
1 /*******************************************************************************
2 ################################################################################
3 #   Copyright (c) [2017-2019] [Radisys]                                        #
4 #                                                                              #
5 #   Licensed under the Apache License, Version 2.0 (the "License");            #
6 #   you may not use this file except in compliance with the License.           #
7 #   You may obtain a copy of the License at                                    #
8 #                                                                              #
9 #       http://www.apache.org/licenses/LICENSE-2.0                             #
10 #                                                                              #
11 #   Unless required by applicable law or agreed to in writing, software        #
12 #   distributed under the License is distributed on an "AS IS" BASIS,          #
13 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   #
14 #   See the License for the specific language governing permissions and        #
15 #   limitations under the License.                                             #
16 ################################################################################
17 *******************************************************************************/
18
19 /* This file contains E2AP message handler functions */
20
21 #include "ric_stub_sctp.h"
22 #include "ric_e2ap_msg_hdl.h"
23 #include "GlobalE2node-gNB-ID.h"
24 #include "odu_common_codec.h"
25
26 Bool ricSubsStatus;
27
28 /*******************************************************************
29 *
30 * @brief Sends E2 msg over SCTP
31 *
32 * @details
33 *
34 *    Function : SendE2APMsg
35 *
36 *    Functionality: Sends E2 msg over SCTP
37 *
38 * @params[in] Region region
39 *             Pool pool
40 * @return ROK     - success
41 *         RFAILED - failure
42 *
43 * ****************************************************************/
44
45 S16 SendE2APMsg(Region region, Pool pool)
46 {
47    Buffer *mBuf;
48
49    if(SGetMsg(region, pool, &mBuf) == ROK)
50    {
51       if(SAddPstMsgMult((Data *)encBuf, encBufSize, mBuf) == ROK)
52       {
53          SPrntMsg(mBuf, 0,0);
54  
55          if(sctpSend(mBuf) != ROK)
56          {
57             DU_LOG("\nE2AP : SCTP Send for E2  failed");
58             SPutMsg(mBuf);
59             return RFAILED;
60          }
61       }
62       else
63       {
64          DU_LOG("\nE2AP : SAddPstMsgMult failed");
65          SPutMsg(mBuf);
66          return RFAILED;
67       }
68       SPutMsg(mBuf);
69    }
70    else
71    {
72       DU_LOG("\nE2AP : Failed to allocate memory");
73       return RFAILED;
74    }
75  
76    return ROK;
77 } /* SendE2APMsg */
78
79 /*******************************************************************
80  *
81  * @brief Builds Global RIC Id Params
82  *
83  * @details
84  *
85  *    Function : BuildGlobalRicId
86  *
87  *    Functionality: Building the Plmn and ric id
88  *
89  * @params[in] GlobalRIC_ID_t *ricId
90  * @return ROK     - success
91  *         RFAILED - failure
92  *
93  * ****************************************************************/
94
95 S16 BuildGlobalRicId(GlobalRIC_ID_t *ricId)
96 {
97    U8 unused = 4;
98    U8 byteSize = 3;
99    U8 val = 1;
100    if(ricId != NULLP)
101    {
102       ricId->pLMN_Identity.size = byteSize * sizeof(U8);
103       RIC_ALLOC(ricId->pLMN_Identity.buf,  ricId->pLMN_Identity.size);
104       buildPlmnId(ricCfgParams.plmn , &ricId->pLMN_Identity);
105       /* fill ric Id */
106       ricId->ric_ID.size = byteSize * sizeof(U8);
107       RIC_ALLOC(ricId->ric_ID.buf, ricId->ric_ID.size);
108       fillBitString(&ricId->ric_ID, unused, byteSize, val);
109    }
110    return ROK;   
111 }
112
113 /*******************************************************************
114  *
115  * @brief Builds and sends the E2SetupResponse
116  *
117  * @details
118  *
119  *    Function : BuildAndSendE2SetupRsp
120  *
121  *    Functionality: Constructs the F1SetupResponse message and sends
122  *                   it back to the DU through SCTP.
123  *
124  * @params[in] void **buf,Buffer to which encoded pattern is written into
125  * @params[in] int *size,size of buffer
126  *
127  * @return ROK     - success
128  *         RFAILED - failure
129  *
130  * ****************************************************************/
131 S16 BuildAndSendE2SetupRsp()
132 {
133    E2AP_PDU_t         *e2apMsg = NULL;
134    E2setupResponse_t  *e2SetupRsp;
135    asn_enc_rval_t     encRetVal; 
136    U8 idx;
137    U8 elementCnt;
138
139  
140    DU_LOG("\nE2AP : Building E2 Setup Response\n");
141
142    RIC_ALLOC(e2apMsg, sizeof(E2AP_PDU_t)); 
143    if(e2apMsg == NULLP)
144    {
145       DU_LOG("\nE2AP : Memory allocation for E2AP-PDU failed");
146       return RFAILED;
147    }
148    e2apMsg->present =  E2AP_PDU_PR_successfulOutcome;
149    RIC_ALLOC(e2apMsg->choice.successfulOutcome, sizeof(SuccessfulOutcomeE2_t));
150    if(e2apMsg->choice.successfulOutcome == NULLP)
151    {
152       DU_LOG("\nE2AP : Memory allocation for E2AP-PDU failed");
153       RIC_FREE(e2apMsg, sizeof(E2AP_PDU_t));
154       return RFAILED;  
155    }
156
157    e2apMsg->choice.successfulOutcome->procedureCode = ProcedureCodeE2_id_E2setup;
158    e2apMsg->choice.successfulOutcome->criticality = CriticalityE2_reject;
159    e2apMsg->choice.successfulOutcome->value.present = \
160          SuccessfulOutcomeE2__value_PR_E2setupResponse;
161    e2SetupRsp = &e2apMsg->choice.successfulOutcome->value.choice.E2setupResponse;
162
163    elementCnt = 1;
164    e2SetupRsp->protocolIEs.list.count = elementCnt;
165    e2SetupRsp->protocolIEs.list.size  = elementCnt * sizeof(E2setupResponseIEs_t);
166
167    RIC_ALLOC(e2SetupRsp->protocolIEs.list.array, \
168               e2SetupRsp->protocolIEs.list.size);
169    if(e2SetupRsp->protocolIEs.list.array == NULLP)
170    {
171       DU_LOG("\nE2AP : Memory allocation for E2ResponseIEs failed");
172       RIC_FREE(e2apMsg->choice.successfulOutcome, sizeof(SuccessfulOutcomeE2_t));
173       RIC_FREE(e2apMsg, sizeof(E2AP_PDU_t));
174       return RFAILED;
175    }
176
177    for(idx=0; idx<elementCnt; idx++)
178    {
179       RIC_ALLOC(e2SetupRsp->protocolIEs.list.array[idx], \
180             sizeof(E2setupResponseIEs_t)); 
181       if(e2SetupRsp->protocolIEs.list.array[idx] == NULLP)
182       {  
183          RIC_FREE(e2SetupRsp->protocolIEs.list.array,\
184                    e2SetupRsp->protocolIEs.list.size);
185          RIC_FREE(e2apMsg->choice.successfulOutcome, \
186                sizeof(SuccessfulOutcomeE2_t));
187          RIC_FREE(e2apMsg, sizeof(E2AP_PDU_t));
188          return RFAILED;
189       }    
190    }
191    /* Global RIC ID */
192    idx = 0;
193    e2SetupRsp->protocolIEs.list.array[idx]->id = ProtocolIE_IDE2_id_GlobalRIC_ID;
194    e2SetupRsp->protocolIEs.list.array[idx]->criticality = CriticalityE2_reject;
195    e2SetupRsp->protocolIEs.list.array[idx]->value.present = \
196                                      E2setupResponseIEs__value_PR_GlobalRIC_ID;
197
198    BuildGlobalRicId(&(e2SetupRsp->protocolIEs.list.array[idx]->value.choice.GlobalRIC_ID));
199
200    xer_fprint(stdout, &asn_DEF_E2AP_PDU, e2apMsg);
201    cmMemset((U8 *)encBuf, 0, ENC_BUF_MAX_LEN);
202    encBufSize = 0;
203    encRetVal = aper_encode(&asn_DEF_E2AP_PDU, 0, e2apMsg, PrepFinalEncBuf, encBuf);
204
205    /* Check encode results */
206    if(encRetVal.encoded == ENCODE_FAIL)
207    {
208            DU_LOG("\nE2AP : Could not encode E2SetupResponse structure (at %s)\n",\
209                            encRetVal.failed_type ? encRetVal.failed_type->name : "unknown");
210            return RFAILED;   
211    } 
212    else 
213    {
214            DU_LOG("\nE2AP : Created APER encoded buffer for E2SetupResponse\n");
215            for(int i=0; i< encBufSize; i++)
216            {
217                    printf("%x",encBuf[i]);
218            } 
219    }
220
221
222    if(SendE2APMsg(RIC_APP_MEM_REG, RIC_POOL) != ROK)
223    {
224       DU_LOG("\nE2AP : Sending E2 Setup Response failed");      
225       return RFAILED;
226    }
227
228    return ROK;
229 }
230
231 /*******************************************************************
232  *
233  * @brief Builds Ric Request Id
234  *
235  * @details
236  *
237  *    Function : BuildRicRequestId
238  *
239  *    Functionality: Building the Ric Request Id
240  *
241  * @params[in] RICrequestID_t *ricReqId
242  * @return ROK     - success
243  *         RFAILED - failure
244  *
245  * ****************************************************************/
246
247 S16 BuildRicRequestId(RICrequestID_t *ricReqId)
248 {
249
250    if(ricReqId != NULLP)
251    {
252       ricReqId->ricRequestorID = 1;
253       ricReqId->ricInstanceID  = 1;
254    }
255    return ROK;
256 }
257
258 /*******************************************************************
259  *
260  * @brief Fills Ric Action To be Setup Item
261  *
262  * @details
263  *
264  *    Function : fillSetupItems
265  *
266  *    Functionality: Filling ricAction Id, RicActionType
267  *
268  * @params[in] RICaction_ToBeSetup_Item_t *setupItems
269  * @return pointer of type RICaction_ToBeSetup_Item_t
270  *
271  * ****************************************************************/
272
273 RICaction_ToBeSetup_Item_t* fillSetupItems(RICaction_ToBeSetup_Item_t *setupItems)
274 {
275    if(setupItems != NULLP)
276    {
277       setupItems->ricActionID = 0;
278       setupItems->ricActionType = RICactionType_report;
279    }
280
281    RETVALUE(setupItems);
282 }
283
284 /*******************************************************************
285  *
286  * @brief Fills RIC Subscription Details Item List
287  *
288  * @details
289  *
290  *    Function : fillSubsDetails
291  *
292  *    Functionality: Fill the RIC Subscription Details Items List
293  *
294  * @params[in] RICaction_ToBeSetup_ItemIEs_t *items
295  * @return ROK     - success
296  *         RFAILED - failure
297  *
298  * ****************************************************************/
299
300 S16 fillSubsDetails(RICaction_ToBeSetup_ItemIEs_t *items)
301 {
302    if(items != NULLP)
303    {
304       items->id = ProtocolIE_IDE2_id_RICaction_ToBeSetup_Item;
305       items->criticality   =  CriticalityE2_ignore;
306       items->value.present =  RICaction_ToBeSetup_ItemIEs__value_PR_RICaction_ToBeSetup_Item;
307       fillSetupItems(&(items->value.choice.RICaction_ToBeSetup_Item));
308    }
309    return ROK;
310 }
311
312 /*******************************************************************
313  *
314  * @brief builds RIC Subscription Details
315  *
316  * @details
317  *
318  *    Function : BuildsRicSubsDetails
319  *
320  *    Functionality: Builds the RIC Subscription Details
321  *
322  * @params[in] RICsubscriptionDetails_t *subsDetails
323  * @return ROK     - success
324  *         RFAILED - failure
325  *
326  * ****************************************************************/
327
328 S16 BuildRicSubsDetails(RICsubscriptionDetails_t *subsDetails)
329 {
330
331    U8 elementCnt;
332
333    if(subsDetails != NULLP)
334    {
335       /* Octet string to be build here */
336       /* Sending PLMN as Octect string */
337       U8 byteSize = 3;
338       subsDetails->ricEventTriggerDefinition.size = byteSize * sizeof(U8);
339       RIC_ALLOC(subsDetails->ricEventTriggerDefinition.buf,  subsDetails->ricEventTriggerDefinition.size);
340       buildPlmnId(ricCfgParams.plmn, &subsDetails->ricEventTriggerDefinition);
341       elementCnt = 1;
342       subsDetails->ricAction_ToBeSetup_List.list.count = elementCnt;
343       subsDetails->ricAction_ToBeSetup_List.list.size = \
344                       elementCnt * sizeof(RICaction_ToBeSetup_ItemIEs_t);
345       RIC_ALLOC(subsDetails->ricAction_ToBeSetup_List.list.array, \
346                 subsDetails->ricAction_ToBeSetup_List.list.size);
347       if(subsDetails->ricAction_ToBeSetup_List.list.array  == NULLP)
348       {
349          DU_LOG("\nE2AP : Memory allocation for RICactionToBeSetup Items failed");
350          return RFAILED;
351       } 
352       RIC_ALLOC(subsDetails->ricAction_ToBeSetup_List.list.array[0],\
353                    sizeof(RICaction_ToBeSetup_ItemIEs_t));
354       fillSubsDetails(subsDetails->ricAction_ToBeSetup_List.list.array[0]);
355    }
356    return ROK;
357 }
358
359 /*******************************************************************
360  *
361  * @brief Builds and Send the RicSubscriptionReq
362  *
363  * @details
364  *
365  *    Function : BuildAndSendRicSubscriptionReq
366  *
367  * Functionality:Fills the RicSubscriptionReq
368  *
369  * @return ROK     - success
370  *         RFAILED - failure
371  *
372  ******************************************************************/
373
374 S16 BuildAndSendRicSubscriptionReq()
375 {
376
377    E2AP_PDU_t   *e2apRicMsg = NULL;
378    RICsubscriptionRequest_t   *ricSubscriptionReq;
379    U8   elementCnt;
380    U8   idx;
381    U8   ieId;
382    S16  ret; 
383    asn_enc_rval_t             encRetVal;        /* Encoder return value */
384    ricSubsStatus = TRUE;
385
386    DU_LOG("\nE2AP : Building RIC Subscription Request\n");
387
388    RIC_ALLOC(e2apRicMsg, sizeof(E2AP_PDU_t));
389    if(e2apRicMsg == NULLP)
390    {
391       DU_LOG("\nE2AP : Memory allocation for E2AP-PDU failed");
392       return RFAILED;
393    }
394
395    e2apRicMsg->present = E2AP_PDU_PR_initiatingMessage;
396    RIC_ALLOC(e2apRicMsg->choice.initiatingMessage, sizeof(InitiatingMessageE2_t));
397    if(e2apRicMsg->choice.initiatingMessage == NULLP)
398    {
399       DU_LOG("\nE2AP : Memory allocation for E2AP-PDU failed");
400       RIC_FREE(e2apRicMsg, sizeof(E2AP_PDU_t));
401       return RFAILED;
402    }
403    e2apRicMsg->choice.initiatingMessage->procedureCode = ProcedureCodeE2_id_RICsubscription;
404    e2apRicMsg->choice.initiatingMessage->criticality = CriticalityE2_reject;
405    e2apRicMsg->choice.initiatingMessage->value.present = InitiatingMessageE2__value_PR_RICsubscriptionRequest;
406    
407    RIC_ALLOC(ricSubscriptionReq, sizeof(RICsubscriptionRequest_t));
408    ricSubscriptionReq = &e2apRicMsg->choice.initiatingMessage->value.choice.RICsubscriptionRequest;
409    
410    elementCnt = 3;
411    ricSubscriptionReq->protocolIEs.list.count = elementCnt;
412    ricSubscriptionReq->protocolIEs.list.size  = elementCnt * sizeof(RICsubscriptionRequest_IEs_t);
413
414    /* Initialize the subscription members */
415    RIC_ALLOC(ricSubscriptionReq->protocolIEs.list.array, \
416               ricSubscriptionReq->protocolIEs.list.size);
417    if(ricSubscriptionReq->protocolIEs.list.array == NULLP)
418    {
419       DU_LOG("\nE2AP : Memory allocation for RICSubscriptionRequestIEs failed");
420       RIC_FREE(e2apRicMsg->choice.initiatingMessage, sizeof(InitiatingMessageE2_t));
421       RIC_FREE(e2apRicMsg, (Size)sizeof(E2AP_PDU_t));
422       return RFAILED;
423    }
424    
425    for(idx=0; idx<elementCnt; idx++)
426    {
427       RIC_ALLOC(ricSubscriptionReq->protocolIEs.list.array[idx],\
428             sizeof(RICsubscriptionRequest_IEs_t));
429       if(ricSubscriptionReq->protocolIEs.list.array[idx] == NULLP)
430       {
431          for(ieId=0; ieId<idx; ieId++)
432          {
433             RIC_FREE(ricSubscriptionReq->protocolIEs.list.array[ieId],\
434                   sizeof(RICsubscriptionRequest_IEs_t));
435          }
436          RIC_FREE(ricSubscriptionReq->protocolIEs.list.array,\
437                   ricSubscriptionReq->protocolIEs.list.size);
438          RIC_FREE(e2apRicMsg->choice.initiatingMessage, \
439                sizeof(InitiatingMessageE2_t));
440          RIC_FREE(e2apRicMsg, sizeof(E2AP_PDU_t));
441          return RFAILED;
442       }
443    }
444
445    /* Filling RIC Request Id */
446    idx = 0;
447    ricSubscriptionReq->protocolIEs.list.array[idx]->id = ProtocolIE_IDE2_id_RICrequestID;
448    ricSubscriptionReq->protocolIEs.list.array[idx]->criticality = CriticalityE2_reject;
449    ricSubscriptionReq->protocolIEs.list.array[idx]->value.present =\
450                                     RICsubscriptionRequest_IEs__value_PR_RICrequestID;
451  
452    BuildRicRequestId(&ricSubscriptionReq->protocolIEs.list.array[idx]->value.choice.RICrequestID);
453
454
455    /* Filling RAN Function Id */
456    idx++;
457    ricSubscriptionReq->protocolIEs.list.array[idx]->id = ProtocolIE_IDE2_id_RANfunctionID;
458    ricSubscriptionReq->protocolIEs.list.array[idx]->criticality = CriticalityE2_reject;
459    ricSubscriptionReq->protocolIEs.list.array[idx]->value.present =\
460                                     RICsubscriptionRequest_IEs__value_PR_RANfunctionID;
461    ricSubscriptionReq->protocolIEs.list.array[idx]->value.choice.RANfunctionID = 1;
462
463
464    /* Filling RIC Subscription Details */
465    idx++;
466    ricSubscriptionReq->protocolIEs.list.array[idx]->id = ProtocolIE_IDE2_id_RICsubscriptionDetails;
467    ricSubscriptionReq->protocolIEs.list.array[idx]->criticality = CriticalityE2_reject;
468    ricSubscriptionReq->protocolIEs.list.array[idx]->value.present =\
469                                     RICsubscriptionRequest_IEs__value_PR_RICsubscriptionDetails;
470
471    BuildRicSubsDetails(&(ricSubscriptionReq->protocolIEs.list.array[idx]->value.choice.RICsubscriptionDetails));
472
473
474    /* Prints the Msg formed */
475    xer_fprint(stdout, &asn_DEF_E2AP_PDU, e2apRicMsg);
476
477    cmMemset((U8 *)encBuf, 0, ENC_BUF_MAX_LEN);
478    encBufSize = 0;
479    encRetVal = aper_encode(&asn_DEF_E2AP_PDU, 0, e2apRicMsg, PrepFinalEncBuf,\
480                encBuf);
481    if(encRetVal.encoded == ENCODE_FAIL)
482    {
483       DU_LOG("\nE2AP : Could not encode RicSubscriptionRequest structure (at %s)\n",\
484       encRetVal.failed_type ? encRetVal.failed_type->name : "unknown");
485       return RFAILED;
486    }
487    else
488    {
489       DU_LOG("\nE2AP : Created APER encoded buffer for RicSubscriptionRequest\n");
490       for(int i=0; i< encBufSize; i++)
491       {
492           printf("%x",encBuf[i]);
493       } 
494    }
495
496
497    /* Sending msg */
498    if(SendE2APMsg(RIC_APP_MEM_REG, RIC_POOL) != ROK)
499    {
500       DU_LOG("\nE2AP : Sending RIC subscription Request failed");
501       return RFAILED;
502    }
503
504    return ROK;
505 }
506
507  
508 /*******************************************************************
509 *
510 * @brief Handles received E2AP message and sends back response  
511 *
512 * @details
513 *
514 *    Function : E2APMsgHdlr
515 *
516 *    Functionality:
517 *         - Decodes received E2AP control message
518 *         - Prepares response message, encodes and sends to SCTP
519 *
520 * @params[in] 
521 * @return ROK     - success
522 *         RFAILED - failure
523 *
524 * ****************************************************************/
525 void E2APMsgHdlr(Buffer *mBuf)
526 {
527    int i;
528    char *recvBuf;
529    MsgLen copyCnt;
530    MsgLen recvBufLen;
531    E2AP_PDU_t *e2apMsg;
532    asn_dec_rval_t rval; /* Decoder return value */
533    E2AP_PDU_t e2apasnmsg ;
534  
535    DU_LOG("\nE2AP : Received E2AP message buffer");
536    SPrntMsg(mBuf, 0,0);
537  
538    /* Copy mBuf into char array to decode it */
539    SFndLenMsg(mBuf, &recvBufLen);
540    if(SGetSBuf(DFLT_REGION, DFLT_POOL, (Data **)&recvBuf, (Size)recvBufLen) != ROK)
541    {
542       DU_LOG("\nE2AP : Memory allocation failed");
543       return;
544    }
545    if(SCpyMsgFix(mBuf, 0, recvBufLen, (Data *)recvBuf, &copyCnt) != ROK)
546    {
547       DU_LOG("\nE2AP : Failed while copying %d", copyCnt);
548       return;
549    }
550
551    printf("\nE2AP : Received flat buffer to be decoded : ");
552    for(i=0; i< recvBufLen; i++)
553    {
554         printf("%x",recvBuf[i]);
555    }
556
557    /* Decoding flat buffer into E2AP messsage */
558    e2apMsg = &e2apasnmsg;
559    memset(e2apMsg, 0, sizeof(E2AP_PDU_t));
560
561    rval = aper_decode(0, &asn_DEF_E2AP_PDU, (void **)&e2apMsg, recvBuf, recvBufLen, 0, 0);
562    SPutSBuf(DFLT_REGION, DFLT_POOL, (Data *)recvBuf, (Size)recvBufLen);
563    if(rval.code == RC_FAIL || rval.code == RC_WMORE)
564    {
565       DU_LOG("\nE2AP : ASN decode failed");
566       return;
567    }
568    printf("\n");
569    xer_fprint(stdout, &asn_DEF_E2AP_PDU, e2apMsg);
570
571    switch(e2apMsg->present)
572    {
573       case E2AP_PDU_PR_initiatingMessage:
574       {
575          switch(e2apMsg->choice.initiatingMessage->value.present)
576          {
577             case InitiatingMessageE2__value_PR_E2setupRequest:
578             {
579                DU_LOG("\nE2AP : E2 setup request received");
580                BuildAndSendE2SetupRsp();
581                break;
582             }
583             case InitiatingMessageE2__value_PR_RICindication:
584             {
585                DU_LOG("\nE2AP : RIC Indication Acknowledged");
586                break;
587             }
588             default:
589             {
590                DU_LOG("\nE2AP : Invalid type of intiating message [%d]",e2apMsg->choice.initiatingMessage->value.present);
591                return;
592             }
593          }/* End of switch(initiatingMessage) */
594          break;
595       }
596       case E2AP_PDU_PR_successfulOutcome: 
597       {
598          switch(e2apMsg->choice.successfulOutcome->value.present)
599          {
600             case SuccessfulOutcomeE2__value_PR_RICsubscriptionResponse:  
601             {
602                DU_LOG("\nE2AP : RICsubscriptionResponse Msg Acknowledged");
603                break;
604             }
605             default:
606             {
607                DU_LOG("\nE2AP : Invalid type of successfulOutcome message [%d]",e2apMsg->choice.successfulOutcome->value.present);
608                return;
609             }
610             break;
611          }
612          break; 
613       }
614       default:
615       {
616          DU_LOG("\nE2AP : Invalid type message type ");
617          return;
618       }
619  
620     }/* End of switch(e2apMsg->present) */
621
622     if(!ricSubsStatus)
623       BuildAndSendRicSubscriptionReq(); 
624        
625 } /* End of E2APMsgHdlr */
626
627
628 /**********************************************************************
629   End of file
630  **********************************************************************/