From 6829faae0604b475303b523caddffe2d5638acc1 Mon Sep 17 00:00:00 2001 From: Richard Mudgett Date: Wed, 26 May 2010 16:01:10 +0000 Subject: [PATCH] Add Call Completion Suppplementary Service Call Completion Supplementary Service (CCSS) added for the following switch types: ETSI PTMP, ETSI PTP, Q.SIG. Specifications: ETS 300 359 CCBS for PTMP and PTP ETS 301 065 CCNR for PTMP and PTP ECMA-186 Call Completion for Q.SIG Several support services were added to support CC: Dummy Call Reference. Q.931 REGISTER message. Dynamic expansion of the number of available timers (up to 8192). Enhanced facility message handling. Current implementation limitations preclude the following: CC service retention is not supported. Q.SIG path reservation is not supported. (closes issue #14292) Reported by: tomaso Tested by: rmudgett JIRA SWP-1493 Review: https://reviewboard.asterisk.org/r/522/ git-svn-id: https://origsvn.digium.com/svn/libpri/branches/1.4@1714 2fbb986a-6c06-0410-b554-c9c1f0a7f128 --- Makefile | 6 + doc/cc_ptmp_agent.fsm | 366 ++ doc/cc_ptmp_agent_flattened.fsm | 483 ++ doc/cc_ptmp_monitor.fsm | 181 + doc/cc_ptmp_monitor_flattened.fsm | 225 + doc/cc_ptp_agent.fsm | 144 + doc/cc_ptp_agent_flattened.fsm | 191 + doc/cc_ptp_monitor.fsm | 168 + doc/cc_ptp_monitor_flattened.fsm | 217 + doc/cc_qsig_agent.fsm | 136 + doc/cc_qsig_agent_flattened.fsm | 183 + doc/cc_qsig_monitor.fsm | 249 + doc/cc_qsig_monitor_flattened.fsm | 327 ++ libpri.h | 250 +- pri.c | 134 +- pri_cc.c | 7872 +++++++++++++++++++++++++++++ pri_facility.c | 666 ++- pri_facility.h | 60 +- pri_internal.h | 379 +- pri_q931.h | 7 + prisched.c | 148 +- q931.c | 690 ++- rose.c | 350 ++ rose.h | 606 +++ rose_etsi_cc.c | 1853 +++++++ rose_internal.h | 187 + rose_qsig_cc.c | 984 ++++ rosetest.c | 577 +++ 28 files changed, 17327 insertions(+), 312 deletions(-) create mode 100644 doc/cc_ptmp_agent.fsm create mode 100644 doc/cc_ptmp_agent_flattened.fsm create mode 100644 doc/cc_ptmp_monitor.fsm create mode 100644 doc/cc_ptmp_monitor_flattened.fsm create mode 100644 doc/cc_ptp_agent.fsm create mode 100644 doc/cc_ptp_agent_flattened.fsm create mode 100644 doc/cc_ptp_monitor.fsm create mode 100644 doc/cc_ptp_monitor_flattened.fsm create mode 100644 doc/cc_qsig_agent.fsm create mode 100644 doc/cc_qsig_agent_flattened.fsm create mode 100644 doc/cc_qsig_monitor.fsm create mode 100644 doc/cc_qsig_monitor_flattened.fsm create mode 100644 pri_cc.c create mode 100644 rose_etsi_cc.c create mode 100644 rose_qsig_cc.c diff --git a/Makefile b/Makefile index effd676..b16a252 100644 --- a/Makefile +++ b/Makefile @@ -47,16 +47,19 @@ STATIC_OBJS= \ q921.o \ prisched.o \ q931.o \ + pri_cc.o \ pri_facility.o \ asn1_primitive.o \ rose.o \ rose_address.o \ rose_etsi_aoc.o \ + rose_etsi_cc.o \ rose_etsi_diversion.o \ rose_etsi_ect.o \ rose_other.o \ rose_q931.o \ rose_qsig_aoc.o \ + rose_qsig_cc.o \ rose_qsig_ct.o \ rose_qsig_diversion.o \ rose_qsig_mwi.o \ @@ -68,16 +71,19 @@ DYNAMIC_OBJS= \ q921.lo \ prisched.lo \ q931.lo \ + pri_cc.lo \ pri_facility.lo \ asn1_primitive.lo \ rose.lo \ rose_address.lo \ rose_etsi_aoc.lo \ + rose_etsi_cc.lo \ rose_etsi_diversion.lo \ rose_etsi_ect.lo \ rose_other.lo \ rose_q931.lo \ rose_qsig_aoc.lo \ + rose_qsig_cc.lo \ rose_qsig_ct.lo \ rose_qsig_diversion.lo \ rose_qsig_mwi.lo \ diff --git a/doc/cc_ptmp_agent.fsm b/doc/cc_ptmp_agent.fsm new file mode 100644 index 0000000..962ac51 --- /dev/null +++ b/doc/cc_ptmp_agent.fsm @@ -0,0 +1,366 @@ +/* + * FSM pseudo code used in the design/implementation of the CC PTMP agent. + */ +FSM CC_PTMP_Agent +{ + State CC_STATE_IDLE { + Init { + } + Prolog { + Action Set_Selfdestruct; + } + Stimulus CC_EVENT_AVAILABLE { + Next_State CC_STATE_PENDING_AVAILABLE; + } + Stimulus CC_EVENT_CANCEL { + Action Set_Selfdestruct; + } + } + State CC_STATE_PENDING_AVAILABLE { + Stimulus CC_EVENT_MSG_ALERTING { + Action Send_CC_Available(Q931_ALERTING); + Next_State CC_STATE_AVAILABLE; + } + Stimulus CC_EVENT_MSG_DISCONNECT { + Action Send_CC_Available(Q931_DISCONNECT); + Next_State CC_STATE_AVAILABLE; + } + Stimulus CC_EVENT_CANCEL { + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_AVAILABLE { + Epilog { + Action Stop_T_RETENTION; + } + Stimulus CC_EVENT_MSG_RELEASE { + Action Stop_T_RETENTION; + Action Start_T_RETENTION; + } + Stimulus CC_EVENT_MSG_RELEASE_COMPLETE { + Action Stop_T_RETENTION; + Action Start_T_RETENTION; + } + Stimulus CC_EVENT_CC_REQUEST { + Action Pass_Up_CC_Request; + Next_State CC_STATE_REQUESTED; + } + Stimulus CC_EVENT_TIMEOUT_T_RETENTION { + Action Send_EraseCallLinkageID; + Action Relese_LinkID; + Action Pass_Up_CC_Cancel; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Send_EraseCallLinkageID; + Action Relese_LinkID; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_REQUESTED { + Epilog { + Action Send_EraseCallLinkageID; + Action Relese_LinkID; + } + Stimulus CC_EVENT_CC_REQUEST_ACCEPT { + Next_State CC_STATE_ACTIVATED; + } + Stimulus CC_EVENT_CANCEL { + Next_State CC_STATE_IDLE; + } + } +/* + * Pass_Up_A_Status passes up the current final status of A. + * Does nothing if status is invalid. + * + * Pass_Up_A_Status_Indirect is the same as Pass_Up_A_Status but + * sets a timer to expire immediately to pass up the event. + * Does nothing if status is invalid. + * + * Pass_Up_Status_Rsp_A passes up the current accumulated status of A. + * Does nothing if status is invalid. + * + * Pass_Up_Status_Rsp_A_Indirect is the same as Pass_Up_Status_Rsp_A but + * sets a timer to expire immediately to pass up the event. + * Does nothing if status is invalid. + */ + State CC_STATE_ACTIVATED { + Prolog { + Action Reset_A_Status; + Action Raw_Status_Count_Reset; + } + Stimulus CC_EVENT_RECALL { + Action Send_Error_Recall(ROSE_ERROR_CCBS_NotReadyForCall); + Action Set_Call_To_Hangup; + } + Stimulus CC_EVENT_B_FREE { + Action Send_CCBSBFree; + } + Stimulus CC_EVENT_REMOTE_USER_FREE { + Test = Get_A_Status; + Test == Invalid { + Next_State CC_STATE_B_AVAILABLE; + } + Test == Busy { + Action Pass_Up_A_Status_Indirect; + Action Send_CCBSBFree; + Next_State CC_STATE_SUSPENDED; + } + Test == Free { + //Action Pass_Up_A_Status_Indirect; + Action Send_RemoteUserFree; + Next_State CC_STATE_WAIT_CALLBACK; + } + } + Stimulus CC_EVENT_A_STATUS { + Test = Get_T_CCBS1_Status; + Test == Active { + Action Pass_Up_Status_Rsp_A_Indirect; + Next_State $; + } + Test != Active { + Action Reset_A_Status; + Action Reset_Raw_A_Status; + Action Send_CCBSStatusRequest; + Action Start_T_CCBS1; + Action Stop_Extended_T_CCBS1; + Action Start_Extended_T_CCBS1; + Next_State $; + } + } + Stimulus CC_EVENT_A_FREE { + Action Raw_Status_Count_Reset; + Action Set_Raw_A_Status_Free; + Action Promote_Raw_A_Status; + Action Pass_Up_Status_Rsp_A; + Action Stop_T_CCBS1; + } + Stimulus CC_EVENT_A_BUSY { + Action Add_Raw_A_Status_Busy; + Action Pass_Up_Status_Rsp_A; + } + Stimulus CC_EVENT_TIMEOUT_T_CCBS1 { + Action Promote_Raw_A_Status; + Test = Get_A_Status; + Test != Invalid { + /* Only received User A busy. */ + Action Raw_Status_Count_Reset; + } + Test == Invalid { + /* Did not get any responses. */ + Action Raw_Status_Count_Increment; + Test = Get_Raw_Status_Count; + Test >= RAW_STATUS_COUNT_MAX { + /* User A no longer present. */ + Action Send_CCBSErase(Normal_Unspecified); + Action Pass_Up_CC_Cancel; + Next_State CC_STATE_IDLE; + } + } + } + Stimulus CC_EVENT_TIMEOUT_EXTENDED_T_CCBS1 { + Action Reset_A_Status; + Action Raw_Status_Count_Reset; + } + } + State CC_STATE_B_AVAILABLE { + /* A status is always invalid on entry. */ + Prolog { + Test = Get_T_CCBS1_Status; + Test != Active { + Action Reset_Raw_A_Status; + Action Send_CCBSStatusRequest; + Action Start_T_CCBS1; + } + } + Stimulus CC_EVENT_RECALL { + Action Send_Error_Recall(ROSE_ERROR_CCBS_NotReadyForCall); + Action Set_Call_To_Hangup; + } + Stimulus CC_EVENT_A_STATUS { + Action Stop_Extended_T_CCBS1; + Action Start_Extended_T_CCBS1; + Action Pass_Up_Status_Rsp_A_Indirect; + } + Stimulus CC_EVENT_A_FREE { + Action Send_RemoteUserFree; + Action Set_Raw_A_Status_Free; + //Action Promote_Raw_A_Status; + //Action Pass_Up_A_Status; + Test = Get_Extended_T_CCBS1_Status; + Test == Active { + Action Pass_Up_Status_Rsp_A; + } + Next_State CC_STATE_WAIT_CALLBACK; + } + Stimulus CC_EVENT_A_BUSY { + Action Add_Raw_A_Status_Busy; + Test = Get_Extended_T_CCBS1_Status; + Test == Active { + Action Pass_Up_Status_Rsp_A; + } + } + Stimulus CC_EVENT_TIMEOUT_T_CCBS1 { + Test = Get_Raw_A_Status; + Test != Invalid { + /* Only received User A is busy. */ + Action Raw_Status_Count_Reset; + Action Send_CCBSBFree; + Action Promote_Raw_A_Status; + Action Pass_Up_A_Status; + Next_State CC_STATE_SUSPENDED; + } + Test == Invalid { + /* Did not get any responses. */ + Action Raw_Status_Count_Increment; + Test = Get_Raw_Status_Count; + Test >= RAW_STATUS_COUNT_MAX { + /* User A no longer present. */ + Action Send_CCBSErase(Normal_Unspecified); + Action Pass_Up_CC_Cancel; + Next_State CC_STATE_IDLE; + } + //Action Reset_Raw_A_Status; + Action Send_CCBSStatusRequest; + Action Start_T_CCBS1; + } + } + } + State CC_STATE_SUSPENDED { + Prolog { + Test = Get_T_CCBS1_Status; + Test != Active { + Action Reset_Raw_A_Status; + Action Send_CCBSStatusRequest; + Action Start_T_CCBS1; + } + } + Stimulus CC_EVENT_RECALL { + Action Send_Error_Recall(ROSE_ERROR_CCBS_NotReadyForCall); + Action Set_Call_To_Hangup; + } + Stimulus CC_EVENT_A_STATUS { + Action Stop_Extended_T_CCBS1; + Action Start_Extended_T_CCBS1; + Action Pass_Up_Status_Rsp_A_Indirect; + } + Stimulus CC_EVENT_A_FREE { + Action Set_Raw_A_Status_Free; + Action Promote_Raw_A_Status; + Action Pass_Up_A_Status; + Test = Get_Extended_T_CCBS1_Status; + Test == Active { + Action Pass_Up_Status_Rsp_A; + } + Action Stop_T_CCBS1; + Action Stop_Extended_T_CCBS1; + Next_State CC_STATE_ACTIVATED; + } + Stimulus CC_EVENT_A_BUSY { + Action Add_Raw_A_Status_Busy; + Test = Get_Extended_T_CCBS1_Status; + Test == Active { + Action Pass_Up_Status_Rsp_A; + } + } + Stimulus CC_EVENT_TIMEOUT_T_CCBS1 { + Test = Get_Raw_A_Status; + Test != Invalid { + /* Only received User A is busy. */ + Action Raw_Status_Count_Reset; + } + Test == Invalid { + /* Did not get any responses. */ + Action Raw_Status_Count_Increment; + Test = Get_Raw_Status_Count; + Test >= RAW_STATUS_COUNT_MAX { + /* User A no longer present. */ + Action Send_CCBSErase(Normal_Unspecified); + Action Pass_Up_CC_Cancel; + Next_State CC_STATE_IDLE; + } + } + Action Reset_Raw_A_Status; + Action Send_CCBSStatusRequest; + Action Start_T_CCBS1; + } + } + State CC_STATE_WAIT_CALLBACK { + Prolog { + /* Start T_CCBS3 */ + Action Start_T_RECALL; + } + Epilog { + Action Stop_T_RECALL; + } + Stimulus CC_EVENT_TIMEOUT_T_RECALL { + Action Pass_Up_CC_Cancel; + Action Send_CCBSErase(T_CCBS3_TIMEOUT); + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_STOP_ALERTING { + /* + * If an earlier link can send us this event then we + * really should be configured for globalRecall like + * the earlier link. + */ + Test = Get_Recall_Mode; + Test == globalRecall { + Action Send_CCBSStopAlerting; + } + Next_State CC_STATE_ACTIVATED; + } + Stimulus CC_EVENT_RECALL { + Action Pass_Up_CC_Call; + Action Set_Original_Call_Parameters; + Test = Get_Recall_Mode; + Test == globalRecall { + Action Send_CCBSStopAlerting; + } + Next_State CC_STATE_CALLBACK; + } + Stimulus CC_EVENT_A_STATUS { + Action Set_Raw_A_Status_Free; + Action Pass_Up_Status_Rsp_A_Indirect; + } + } + State CC_STATE_CALLBACK { + Stimulus CC_EVENT_RECALL { + Action Send_Error_Recall(ROSE_ERROR_CCBS_AlreadyAccepted); + Action Set_Call_To_Hangup; + } + Stimulus CC_EVENT_A_STATUS { + Action Set_Raw_A_Status_Free; + Action Pass_Up_Status_Rsp_A_Indirect; + } + } + Superstate CC_ACTIVE(CC_STATE_ACTIVATED, CC_STATE_B_AVAILABLE, CC_STATE_SUSPENDED, CC_STATE_WAIT_CALLBACK, CC_STATE_CALLBACK) { + Prolog { + /* Start T_CCBS2 or T_CCNR2 depending upon CC mode. */ + Action Start_T_SUPERVISION; + } + Epilog { + Action Stop_T_SUPERVISION; + } + Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION { + Action Pass_Up_CC_Cancel; + Action Send_CCBSErase(T_CCBS2_TIMEOUT); + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_LINK_CANCEL { + Action Pass_Up_CC_Cancel; + Action Send_CCBSErase(Normal_Unspecified); + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Send_CCBSErase(Normal_Unspecified); + Next_State CC_STATE_IDLE; + } + } + Superstate CC_STATUS(CC_STATE_ACTIVATED, CC_STATE_B_AVAILABLE, CC_STATE_SUSPENDED) { + Epilog { + Action Stop_T_CCBS1; + Action Stop_Extended_T_CCBS1; + } + } +} diff --git a/doc/cc_ptmp_agent_flattened.fsm b/doc/cc_ptmp_agent_flattened.fsm new file mode 100644 index 0000000..3b05289 --- /dev/null +++ b/doc/cc_ptmp_agent_flattened.fsm @@ -0,0 +1,483 @@ +/* + * FSM pseudo code used in the design/implementation of the CC PTMP agent. + */ +FSM CC_PTMP_Agent +{ + State CC_STATE_IDLE { + Stimulus CC_EVENT_AVAILABLE { + Next_State CC_STATE_PENDING_AVAILABLE; + } + Stimulus CC_EVENT_CANCEL { + Action Set_Selfdestruct; + } + } + State CC_STATE_PENDING_AVAILABLE { + Stimulus CC_EVENT_MSG_ALERTING { + Action Send_CC_Available(Q931_ALERTING); + Next_State CC_STATE_AVAILABLE; + } + Stimulus CC_EVENT_MSG_DISCONNECT { + Action Send_CC_Available(Q931_DISCONNECT); + Next_State CC_STATE_AVAILABLE; + } + Stimulus CC_EVENT_CANCEL { + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_AVAILABLE { + Stimulus CC_EVENT_MSG_RELEASE { + Action Stop_T_RETENTION; + Action Start_T_RETENTION; + } + Stimulus CC_EVENT_MSG_RELEASE_COMPLETE { + Action Stop_T_RETENTION; + Action Start_T_RETENTION; + } + Stimulus CC_EVENT_CC_REQUEST { + Action Pass_Up_CC_Request; + Action Stop_T_RETENTION; + Next_State CC_STATE_REQUESTED; + } + Stimulus CC_EVENT_TIMEOUT_T_RETENTION { + Action Send_EraseCallLinkageID; + Action Relese_LinkID; + Action Pass_Up_CC_Cancel; + Action Stop_T_RETENTION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Send_EraseCallLinkageID; + Action Relese_LinkID; + Action Stop_T_RETENTION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_REQUESTED { + Stimulus CC_EVENT_CC_REQUEST_ACCEPT { + Action Send_EraseCallLinkageID; + Action Relese_LinkID; + /* Start T_CCBS2 or T_CCNR2 depending upon CC mode. */ + Action Start_T_SUPERVISION; + Action Reset_A_Status; + Action Raw_Status_Count_Reset; + Next_State CC_STATE_ACTIVATED; + } + Stimulus CC_EVENT_CANCEL { + Action Send_EraseCallLinkageID; + Action Relese_LinkID; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } +/* + * Pass_Up_A_Status passes up the current final status of A. + * Does nothing if status is invalid. + * + * Pass_Up_A_Status_Indirect is the same as Pass_Up_A_Status but + * sets a timer to expire immediately to pass up the event. + * Does nothing if status is invalid. + * + * Pass_Up_Status_Rsp_A passes up the current accumulated status of A. + * Does nothing if status is invalid. + * + * Pass_Up_Status_Rsp_A_Indirect is the same as Pass_Up_Status_Rsp_A but + * sets a timer to expire immediately to pass up the event. + * Does nothing if status is invalid. + */ + State CC_STATE_ACTIVATED { + Stimulus CC_EVENT_RECALL { + Action Send_Error_Recall(ROSE_ERROR_CCBS_NotReadyForCall); + Action Set_Call_To_Hangup; + } + Stimulus CC_EVENT_B_FREE { + Action Send_CCBSBFree; + } + Stimulus CC_EVENT_REMOTE_USER_FREE { + Test = Get_A_Status; + Test == Invalid { + Test = Get_T_CCBS1_Status; + Test != Active { + Action Reset_Raw_A_Status; + Action Send_CCBSStatusRequest; + Action Start_T_CCBS1; + } + Next_State CC_STATE_B_AVAILABLE; + } + Test == Busy { + Action Pass_Up_A_Status_Indirect; + Action Send_CCBSBFree; + Test = Get_T_CCBS1_Status; + Test != Active { + Action Reset_Raw_A_Status; + Action Send_CCBSStatusRequest; + Action Start_T_CCBS1; + } + Next_State CC_STATE_SUSPENDED; + } + Test == Free { + //Action Pass_Up_A_Status_Indirect; + Action Send_RemoteUserFree; + Action Stop_T_CCBS1; + Action Stop_Extended_T_CCBS1; + /* Start T_CCBS3 */ + Action Start_T_RECALL; + Next_State CC_STATE_WAIT_CALLBACK; + } + } + Stimulus CC_EVENT_A_STATUS { + Test = Get_T_CCBS1_Status; + Test == Active { + Action Pass_Up_Status_Rsp_A_Indirect; + Next_State $; + } + Test != Active { + Action Reset_A_Status; + Action Reset_Raw_A_Status; + Action Send_CCBSStatusRequest; + Action Start_T_CCBS1; + Action Stop_Extended_T_CCBS1; + Action Start_Extended_T_CCBS1; + Next_State $; + } + } + Stimulus CC_EVENT_A_FREE { + Action Raw_Status_Count_Reset; + Action Set_Raw_A_Status_Free; + Action Promote_Raw_A_Status; + Action Pass_Up_Status_Rsp_A; + Action Stop_T_CCBS1; + } + Stimulus CC_EVENT_A_BUSY { + Action Add_Raw_A_Status_Busy; + Action Pass_Up_Status_Rsp_A; + } + Stimulus CC_EVENT_TIMEOUT_T_CCBS1 { + Action Promote_Raw_A_Status; + Test = Get_A_Status; + Test != Invalid { + /* Only received User A busy. */ + Action Raw_Status_Count_Reset; + } + Test == Invalid { + /* Did not get any responses. */ + Action Raw_Status_Count_Increment; + Test = Get_Raw_Status_Count; + Test >= RAW_STATUS_COUNT_MAX { + /* User A no longer present. */ + Action Send_CCBSErase(Normal_Unspecified); + Action Pass_Up_CC_Cancel; + Action Stop_T_CCBS1; + Action Stop_Extended_T_CCBS1; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + } + Stimulus CC_EVENT_TIMEOUT_EXTENDED_T_CCBS1 { + Action Reset_A_Status; + Action Raw_Status_Count_Reset; + } + Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION { + Action Pass_Up_CC_Cancel; + Action Send_CCBSErase(T_CCBS2_TIMEOUT); + Action Stop_T_CCBS1; + Action Stop_Extended_T_CCBS1; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_LINK_CANCEL { + Action Pass_Up_CC_Cancel; + Action Send_CCBSErase(Normal_Unspecified); + Action Stop_T_CCBS1; + Action Stop_Extended_T_CCBS1; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Send_CCBSErase(Normal_Unspecified); + Action Stop_T_CCBS1; + Action Stop_Extended_T_CCBS1; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_B_AVAILABLE { + /* A status is always invalid on entry. */ + Stimulus CC_EVENT_RECALL { + Action Send_Error_Recall(ROSE_ERROR_CCBS_NotReadyForCall); + Action Set_Call_To_Hangup; + } + Stimulus CC_EVENT_A_STATUS { + Action Stop_Extended_T_CCBS1; + Action Start_Extended_T_CCBS1; + Action Pass_Up_Status_Rsp_A_Indirect; + } + Stimulus CC_EVENT_A_FREE { + Action Send_RemoteUserFree; + Action Set_Raw_A_Status_Free; + //Action Promote_Raw_A_Status; + //Action Pass_Up_A_Status; + Test = Get_Extended_T_CCBS1_Status; + Test == Active { + Action Pass_Up_Status_Rsp_A; + } + Action Stop_T_CCBS1; + Action Stop_Extended_T_CCBS1; + /* Start T_CCBS3 */ + Action Start_T_RECALL; + Next_State CC_STATE_WAIT_CALLBACK; + } + Stimulus CC_EVENT_A_BUSY { + Action Add_Raw_A_Status_Busy; + Test = Get_Extended_T_CCBS1_Status; + Test == Active { + Action Pass_Up_Status_Rsp_A; + } + } + Stimulus CC_EVENT_TIMEOUT_T_CCBS1 { + Test = Get_Raw_A_Status; + Test != Invalid { + /* Only received User A is busy. */ + Action Raw_Status_Count_Reset; + Action Send_CCBSBFree; + Action Promote_Raw_A_Status; + Action Pass_Up_A_Status; + /* Optimization due to flattening */ + //Test = Get_T_CCBS1_Status; + //Test != Active + { + Action Reset_Raw_A_Status; + Action Send_CCBSStatusRequest; + Action Start_T_CCBS1; + } + Next_State CC_STATE_SUSPENDED; + } + Test == Invalid { + /* Did not get any responses. */ + Action Raw_Status_Count_Increment; + Test = Get_Raw_Status_Count; + Test >= RAW_STATUS_COUNT_MAX { + /* User A no longer present. */ + Action Send_CCBSErase(Normal_Unspecified); + Action Pass_Up_CC_Cancel; + Action Stop_T_CCBS1; + Action Stop_Extended_T_CCBS1; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + //Action Reset_Raw_A_Status; + Action Send_CCBSStatusRequest; + Action Start_T_CCBS1; + } + } + Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION { + Action Pass_Up_CC_Cancel; + Action Send_CCBSErase(T_CCBS2_TIMEOUT); + Action Stop_T_CCBS1; + Action Stop_Extended_T_CCBS1; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_LINK_CANCEL { + Action Pass_Up_CC_Cancel; + Action Send_CCBSErase(Normal_Unspecified); + Action Stop_T_CCBS1; + Action Stop_Extended_T_CCBS1; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Send_CCBSErase(Normal_Unspecified); + Action Stop_T_CCBS1; + Action Stop_Extended_T_CCBS1; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_SUSPENDED { + Stimulus CC_EVENT_RECALL { + Action Send_Error_Recall(ROSE_ERROR_CCBS_NotReadyForCall); + Action Set_Call_To_Hangup; + } + Stimulus CC_EVENT_A_STATUS { + Action Stop_Extended_T_CCBS1; + Action Start_Extended_T_CCBS1; + Action Pass_Up_Status_Rsp_A_Indirect; + } + Stimulus CC_EVENT_A_FREE { + Action Set_Raw_A_Status_Free; + Action Promote_Raw_A_Status; + Action Pass_Up_A_Status; + Test = Get_Extended_T_CCBS1_Status; + Test == Active { + Action Pass_Up_Status_Rsp_A; + } + Action Stop_T_CCBS1; + Action Stop_Extended_T_CCBS1; + Action Reset_A_Status; + Action Raw_Status_Count_Reset; + Next_State CC_STATE_ACTIVATED; + } + Stimulus CC_EVENT_A_BUSY { + Action Add_Raw_A_Status_Busy; + Test = Get_Extended_T_CCBS1_Status; + Test == Active { + Action Pass_Up_Status_Rsp_A; + } + } + Stimulus CC_EVENT_TIMEOUT_T_CCBS1 { + Test = Get_Raw_A_Status; + Test != Invalid { + /* Only received User A is busy. */ + Action Raw_Status_Count_Reset; + } + Test == Invalid { + /* Did not get any responses. */ + Action Raw_Status_Count_Increment; + Test = Get_Raw_Status_Count; + Test >= RAW_STATUS_COUNT_MAX { + /* User A no longer present. */ + Action Send_CCBSErase(Normal_Unspecified); + Action Pass_Up_CC_Cancel; + Action Stop_T_CCBS1; + Action Stop_Extended_T_CCBS1; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + Action Reset_Raw_A_Status; + Action Send_CCBSStatusRequest; + Action Start_T_CCBS1; + } + Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION { + Action Pass_Up_CC_Cancel; + Action Send_CCBSErase(T_CCBS2_TIMEOUT); + Action Stop_T_CCBS1; + Action Stop_Extended_T_CCBS1; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_LINK_CANCEL { + Action Pass_Up_CC_Cancel; + Action Send_CCBSErase(Normal_Unspecified); + Action Stop_T_CCBS1; + Action Stop_Extended_T_CCBS1; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Send_CCBSErase(Normal_Unspecified); + Action Stop_T_CCBS1; + Action Stop_Extended_T_CCBS1; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_WAIT_CALLBACK { + Stimulus CC_EVENT_TIMEOUT_T_RECALL { + Action Pass_Up_CC_Cancel; + Action Send_CCBSErase(T_CCBS3_TIMEOUT); + Action Stop_T_RECALL; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_STOP_ALERTING { + /* + * If an earlier link can send us this event then we + * really should be configured for globalRecall like + * the earlier link. + */ + Test = Get_Recall_Mode; + Test == globalRecall { + Action Send_CCBSStopAlerting; + } + Action Stop_T_RECALL; + Action Reset_A_Status; + Action Raw_Status_Count_Reset; + Next_State CC_STATE_ACTIVATED; + } + Stimulus CC_EVENT_RECALL { + Action Pass_Up_CC_Call; + Action Set_Original_Call_Parameters; + Test = Get_Recall_Mode; + Test == globalRecall { + Action Send_CCBSStopAlerting; + } + Action Stop_T_RECALL; + Next_State CC_STATE_CALLBACK; + } + Stimulus CC_EVENT_A_STATUS { + Action Set_Raw_A_Status_Free; + Action Pass_Up_Status_Rsp_A_Indirect; + } + Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION { + Action Pass_Up_CC_Cancel; + Action Send_CCBSErase(T_CCBS2_TIMEOUT); + Action Stop_T_RECALL; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_LINK_CANCEL { + Action Pass_Up_CC_Cancel; + Action Send_CCBSErase(Normal_Unspecified); + Action Stop_T_RECALL; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Send_CCBSErase(Normal_Unspecified); + Action Stop_T_RECALL; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_CALLBACK { + Stimulus CC_EVENT_RECALL { + Action Send_Error_Recall(ROSE_ERROR_CCBS_AlreadyAccepted); + Action Set_Call_To_Hangup; + } + Stimulus CC_EVENT_A_STATUS { + Action Set_Raw_A_Status_Free; + Action Pass_Up_Status_Rsp_A_Indirect; + } + Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION { + Action Pass_Up_CC_Cancel; + Action Send_CCBSErase(T_CCBS2_TIMEOUT); + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_LINK_CANCEL { + Action Pass_Up_CC_Cancel; + Action Send_CCBSErase(Normal_Unspecified); + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Send_CCBSErase(Normal_Unspecified); + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } +} diff --git a/doc/cc_ptmp_monitor.fsm b/doc/cc_ptmp_monitor.fsm new file mode 100644 index 0000000..d339e2c --- /dev/null +++ b/doc/cc_ptmp_monitor.fsm @@ -0,0 +1,181 @@ +/* + * FSM pseudo code used in the design/implementation of the CC PTMP monitor. + * + * The CCBSStatusRequest messages are handled independently from this FSM. + * + * The CCBSInterrogate/CCNRInterrogate messages are initiated by a dialplan + * application/AMI/CLI (future) and are handled outside of this FSM. + */ +FSM CC_PTMP_Monitor +{ + State CC_STATE_IDLE { + Init { + } + Prolog { + Action Set_Selfdestruct; + } + Stimulus CC_EVENT_AVAILABLE { + /* + * Before event is posted: + * Received CallInfoRetain + * Created cc_record + * Saved CallLinkageID + */ + Action Pass_Up_CC_Available; + Next_State CC_STATE_AVAILABLE; + } + Stimulus CC_EVENT_CANCEL { + Action Set_Selfdestruct; + } + } + State CC_STATE_AVAILABLE { + /* + * The upper layer is responsible for canceling the CC available + * offering as a safeguard in case the network cable is disconnected. + * The timer should be set much longer than the network T_RETENTION + * timer so normally the CC records will be cleaned up by network + * activity. + */ + Stimulus CC_EVENT_CC_REQUEST { + /* cc_record->is_ccnr is set before event posted. */ + Action Queue_CC_Request; + Action Start_T_ACTIVATE; + Next_State CC_STATE_REQUESTED; + } + Stimulus CC_EVENT_TIMEOUT_T_RETENTION { + /* + * Received EraseCallLinkageID + * T_RETENTION expired on the network side so we will pretend + * that it expired on our side. + */ + Action Pass_Up_CC_Cancel; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_REQUESTED { + Stimulus CC_EVENT_CC_REQUEST_ACCEPT { + /* + * Before event is posted: + * Received CCBSRequest/CCNRRequest response + * Saved CCBSReference + */ + Action Relese_LinkID; + Action Pass_Up_CC_Req_Rsp_Success; + Action Stop_T_ACTIVATE; + Next_State CC_STATE_ACTIVATED; + } + Stimulus CC_EVENT_CC_REQUEST_FAIL { + Action Pass_Up_CC_Req_Rsp_Fail(error/reject, code); + Action Pass_Up_CC_Cancel; + Action Stop_T_ACTIVATE; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_TIMEOUT_T_ACTIVATE { + Action Pass_Up_CC_Req_Rsp_Timeout; + Action Pass_Up_CC_Cancel; + Action Stop_T_ACTIVATE; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_LINK_CANCEL { + /* Received CCBSErase */ + /* Claim it was a timeout */ + Action Pass_Up_CC_Req_Rsp_Timeout; + Action Pass_Up_CC_Cancel; + Action Stop_T_ACTIVATE; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Next_State CC_STATE_WAIT_DESTRUCTION; + } + } + State CC_STATE_WAIT_DESTRUCTION { + /* We were in the middle of a cc-request when we were asked to cancel. */ + Epilog { + Action Stop_T_ACTIVATE; + } + Stimulus CC_EVENT_CC_REQUEST_ACCEPT { + /* + * Before event is posted: + * Received CCBSRequest/CCNRRequest response + * Saved CCBSReference + */ + Action Send_CC_Deactivate_Req; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CC_REQUEST_FAIL { + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_TIMEOUT_T_ACTIVATE { + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_LINK_CANCEL { + /* Received CCBSErase */ + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_ACTIVATED { + Stimulus CC_EVENT_B_FREE { + /* Received CCBSBFree */ + Action Pass_Up_B_Free; + } + Stimulus CC_EVENT_REMOTE_USER_FREE { + /* Received CCBSRemoteUserFree */ + Action Pass_Up_Remote_User_Free; + Next_State CC_STATE_WAIT_CALLBACK; + } + } + State CC_STATE_WAIT_CALLBACK { + Stimulus CC_EVENT_STOP_ALERTING { + Action Pass_Up_Stop_Alerting; + Next_State CC_STATE_ACTIVATED; + } + Stimulus CC_EVENT_RECALL { + /* The original call parameters have already been set. */ + Action Queue_SETUP_Recall; + Next_State CC_STATE_CALLBACK; + } + } + State CC_STATE_CALLBACK { + /* + * We are waiting for the CC records to be torn down because + * CC is complete. + * This state is mainly to block CC_EVENT_STOP_ALERTING since + * we are the one doing the CC recall so we do not need to stop + * alerting. + */ + } + Superstate CC_ACTIVE(CC_STATE_ACTIVATED, CC_STATE_WAIT_CALLBACK, CC_STATE_CALLBACK) { + Prolog { + /* + * Start T_CCBS2 or T_CCNR2 depending upon CC mode. + * For PTMP TE mode these timers are not defined. However, + * we will use them anyway to protect our resources from leaks + * caused by the network cable being disconnected. These + * timers should be set much longer than the network + * so normally the CC records will be cleaned up by network + * activity. + */ + Action Start_T_SUPERVISION; + } + Epilog { + Action Stop_T_SUPERVISION; + } + Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION { + Action Send_CC_Deactivate_Req; + Action Pass_Up_CC_Cancel; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_LINK_CANCEL { + /* Received CCBSErase */ + Action Pass_Up_CC_Cancel; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Send_CC_Deactivate_Req; + Next_State CC_STATE_IDLE; + } + } +} diff --git a/doc/cc_ptmp_monitor_flattened.fsm b/doc/cc_ptmp_monitor_flattened.fsm new file mode 100644 index 0000000..2adebf0 --- /dev/null +++ b/doc/cc_ptmp_monitor_flattened.fsm @@ -0,0 +1,225 @@ +/* + * FSM pseudo code used in the design/implementation of the CC PTMP monitor. + * + * The CCBSStatusRequest messages are handled independently from this FSM. + * + * The CCBSInterrogate/CCNRInterrogate messages are initiated by a dialplan + * application/AMI/CLI (future) and are handled outside of this FSM. + */ +FSM CC_PTMP_Monitor +{ + State CC_STATE_IDLE { + Stimulus CC_EVENT_AVAILABLE { + /* + * Before event is posted: + * Received CallInfoRetain + * Created cc_record + * Saved CallLinkageID + */ + Action Pass_Up_CC_Available; + Next_State CC_STATE_AVAILABLE; + } + Stimulus CC_EVENT_CANCEL { + Action Set_Selfdestruct; + } + } + State CC_STATE_AVAILABLE { + /* + * The upper layer is responsible for canceling the CC available + * offering as a safeguard in case the network cable is disconnected. + * The timer should be set much longer than the network T_RETENTION + * timer so normally the CC records will be cleaned up by network + * activity. + */ + Stimulus CC_EVENT_CC_REQUEST { + /* cc_record->is_ccnr is set before event posted. */ + Action Queue_CC_Request; + Action Start_T_ACTIVATE; + Next_State CC_STATE_REQUESTED; + } + Stimulus CC_EVENT_TIMEOUT_T_RETENTION { + /* + * Received EraseCallLinkageID + * T_RETENTION expired on the network side so we will pretend + * that it expired on our side. + */ + Action Pass_Up_CC_Cancel; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_REQUESTED { + Stimulus CC_EVENT_CC_REQUEST_ACCEPT { + /* + * Before event is posted: + * Received CCBSRequest/CCNRRequest response + * Saved CCBSReference + */ + Action Relese_LinkID; + Action Pass_Up_CC_Req_Rsp_Success; + Action Stop_T_ACTIVATE; + /* + * Start T_CCBS2 or T_CCNR2 depending upon CC mode. + * For PTMP TE mode these timers are not defined. However, + * we will use them anyway to protect our resources from leaks + * caused by the network cable being disconnected. These + * timers should be set much longer than the network + * so normally the CC records will be cleaned up by network + * activity. + */ + Action Start_T_SUPERVISION; + Next_State CC_STATE_ACTIVATED; + } + Stimulus CC_EVENT_CC_REQUEST_FAIL { + Action Pass_Up_CC_Req_Rsp_Fail(error/reject, code); + Action Pass_Up_CC_Cancel; + Action Stop_T_ACTIVATE; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_TIMEOUT_T_ACTIVATE { + Action Pass_Up_CC_Req_Rsp_Timeout; + Action Pass_Up_CC_Cancel; + Action Stop_T_ACTIVATE; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_LINK_CANCEL { + /* Received CCBSErase */ + /* Claim it was a timeout */ + Action Pass_Up_CC_Req_Rsp_Timeout; + Action Pass_Up_CC_Cancel; + Action Stop_T_ACTIVATE; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Next_State CC_STATE_WAIT_DESTRUCTION; + } + } + State CC_STATE_WAIT_DESTRUCTION { + /* We were in the middle of a cc-request when we were asked to cancel. */ + Stimulus CC_EVENT_CC_REQUEST_ACCEPT { + /* + * Before event is posted: + * Received CCBSRequest/CCNRRequest response + * Saved CCBSReference + */ + Action Send_CC_Deactivate_Req; + Action Stop_T_ACTIVATE; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CC_REQUEST_FAIL { + Action Stop_T_ACTIVATE; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_TIMEOUT_T_ACTIVATE { + Action Stop_T_ACTIVATE; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_LINK_CANCEL { + /* Received CCBSErase */ + Action Stop_T_ACTIVATE; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_ACTIVATED { + Stimulus CC_EVENT_B_FREE { + /* Received CCBSBFree */ + Action Pass_Up_B_Free; + } + Stimulus CC_EVENT_REMOTE_USER_FREE { + /* Received CCBSRemoteUserFree */ + Action Pass_Up_Remote_User_Free; + Next_State CC_STATE_WAIT_CALLBACK; + } + Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION { + Action Send_CC_Deactivate_Req; + Action Pass_Up_CC_Cancel; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_LINK_CANCEL { + /* Received CCBSErase */ + Action Pass_Up_CC_Cancel; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Send_CC_Deactivate_Req; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_WAIT_CALLBACK { + Stimulus CC_EVENT_STOP_ALERTING { + Action Pass_Up_Stop_Alerting; + Next_State CC_STATE_ACTIVATED; + } + Stimulus CC_EVENT_RECALL { + /* The original call parameters have already been set. */ + Action Queue_SETUP_Recall; + Next_State CC_STATE_CALLBACK; + } + Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION { + Action Send_CC_Deactivate_Req; + Action Pass_Up_CC_Cancel; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_LINK_CANCEL { + /* Received CCBSErase */ + Action Pass_Up_CC_Cancel; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Send_CC_Deactivate_Req; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_CALLBACK { + /* + * We are waiting for the CC records to be torn down because + * CC is complete. + * This state is mainly to block CC_EVENT_STOP_ALERTING since + * we are the one doing the CC recall so we do not need to stop + * alerting. + */ + Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION { + Action Send_CC_Deactivate_Req; + Action Pass_Up_CC_Cancel; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_LINK_CANCEL { + /* Received CCBSErase */ + Action Pass_Up_CC_Cancel; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Send_CC_Deactivate_Req; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } +} diff --git a/doc/cc_ptp_agent.fsm b/doc/cc_ptp_agent.fsm new file mode 100644 index 0000000..db02205 --- /dev/null +++ b/doc/cc_ptp_agent.fsm @@ -0,0 +1,144 @@ +/* + * FSM pseudo code used in the design/implementation of the CC PTP agent. + */ +FSM CC_PTP_Agent +{ + State CC_STATE_IDLE { + Init { + } + Prolog { + Action Set_Selfdestruct; + } + Stimulus CC_EVENT_AVAILABLE { + Next_State CC_STATE_PENDING_AVAILABLE; + } + Stimulus CC_EVENT_CANCEL { + Action Set_Selfdestruct; + } + } + State CC_STATE_PENDING_AVAILABLE { + Stimulus CC_EVENT_MSG_ALERTING { + Action Send_CC_Available(Q931_ALERTING); + Next_State CC_STATE_AVAILABLE; + } + Stimulus CC_EVENT_MSG_DISCONNECT { + Action Send_CC_Available(Q931_DISCONNECT); + Next_State CC_STATE_AVAILABLE; + } + Stimulus CC_EVENT_CANCEL { + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_AVAILABLE { + /* + * For PTP mode the T_RETENTION timer is not defined. However, + * we will use it anyway in this state to protect our resources + * from leaks caused by user A not requesting CC. This timer + * should be set much longer than the PTMP network link to + * allow for variations in user A's CC offer timer. + */ + Epilog { + Action Stop_T_RETENTION; + } + Stimulus CC_EVENT_MSG_RELEASE { + Action Stop_T_RETENTION; + Action Start_T_RETENTION; + } + Stimulus CC_EVENT_MSG_RELEASE_COMPLETE { + Action Stop_T_RETENTION; + Action Start_T_RETENTION; + } + Stimulus CC_EVENT_CC_REQUEST { + Action Pass_Up_CC_Request; + Action Stop_T_RETENTION; + Next_State CC_STATE_REQUESTED; + } + Stimulus CC_EVENT_TIMEOUT_T_RETENTION { + Action Pass_Up_CC_Cancel; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_REQUESTED { + Stimulus CC_EVENT_CC_REQUEST_ACCEPT { + Next_State CC_STATE_ACTIVATED; + } + Stimulus CC_EVENT_SIGNALING_GONE { + /* Signaling link cleared. */ + Action Pass_Up_CC_Cancel; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Hangup_Signaling_Link; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_ACTIVATED { + Prolog { + Action Reset_A_Status; + } + Stimulus CC_EVENT_REMOTE_USER_FREE { + Action Pass_Up_A_Status_Indirect; + Test = Get_A_Status; + Test == Busy { + Next_State CC_STATE_SUSPENDED; + } + Action Send_RemoteUserFree; + Next_State CC_STATE_WAIT_CALLBACK; + } + Stimulus CC_EVENT_SUSPEND { + /* Received CCBS_T_Suspend */ + Action Set_A_Status_Busy; + } + Stimulus CC_EVENT_RESUME { + /* Received CCBS_T_Resume */ + Action Reset_A_Status; + } + } + State CC_STATE_WAIT_CALLBACK { + Stimulus CC_EVENT_SUSPEND { + /* Received CCBS_T_Suspend */ + Action Set_A_Status_Busy; + Action Pass_Up_A_Status; + Next_State CC_STATE_SUSPENDED; + } + } + State CC_STATE_SUSPENDED { + Stimulus CC_EVENT_RESUME { + /* Received CCBS_T_Resume */ + Action Set_A_Status_Free; + Action Pass_Up_A_Status; + Next_State CC_STATE_ACTIVATED; + } + } + Superstate CC_ACTIVE(CC_STATE_ACTIVATED, CC_STATE_WAIT_CALLBACK, CC_STATE_SUSPENDED) { + Prolog { + /* Start T_CCBS5/T_CCNR5 depending upon CC mode. */ + Action Start_T_SUPERVISION; + } + Epilog { + Action Stop_T_SUPERVISION; + } + Stimulus CC_EVENT_RECALL { + /* Received CCBS_T_Call */ + Action Pass_Up_CC_Call; + Action Set_Original_Call_Parameters; + } + Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION { + Action Pass_Up_CC_Cancel; + Action Hangup_Signaling_Link; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_SIGNALING_GONE { + /* Signaling link cleared. */ + Action Pass_Up_CC_Cancel; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Hangup_Signaling_Link; + Next_State CC_STATE_IDLE; + } + } +} diff --git a/doc/cc_ptp_agent_flattened.fsm b/doc/cc_ptp_agent_flattened.fsm new file mode 100644 index 0000000..cc48835 --- /dev/null +++ b/doc/cc_ptp_agent_flattened.fsm @@ -0,0 +1,191 @@ +/* + * FSM pseudo code used in the design/implementation of the CC PTP agent. + */ +FSM CC_PTP_Agent +{ + State CC_STATE_IDLE { + Stimulus CC_EVENT_AVAILABLE { + Next_State CC_STATE_PENDING_AVAILABLE; + } + Stimulus CC_EVENT_CANCEL { + Action Set_Selfdestruct; + } + } + State CC_STATE_PENDING_AVAILABLE { + Stimulus CC_EVENT_MSG_ALERTING { + Action Send_CC_Available(Q931_ALERTING); + Next_State CC_STATE_AVAILABLE; + } + Stimulus CC_EVENT_MSG_DISCONNECT { + Action Send_CC_Available(Q931_DISCONNECT); + Next_State CC_STATE_AVAILABLE; + } + Stimulus CC_EVENT_CANCEL { + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_AVAILABLE { + /* + * For PTP mode the T_RETENTION timer is not defined. However, + * we will use it anyway in this state to protect our resources + * from leaks caused by user A not requesting CC. This timer + * should be set much longer than the PTMP network link to + * allow for variations in user A's CC offer timer. + */ + Stimulus CC_EVENT_MSG_RELEASE { + Action Stop_T_RETENTION; + Action Start_T_RETENTION; + } + Stimulus CC_EVENT_MSG_RELEASE_COMPLETE { + Action Stop_T_RETENTION; + Action Start_T_RETENTION; + } + Stimulus CC_EVENT_CC_REQUEST { + Action Pass_Up_CC_Request; + Action Stop_T_RETENTION; + Next_State CC_STATE_REQUESTED; + } + Stimulus CC_EVENT_TIMEOUT_T_RETENTION { + Action Pass_Up_CC_Cancel; + Action Stop_T_RETENTION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Stop_T_RETENTION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_REQUESTED { + Stimulus CC_EVENT_CC_REQUEST_ACCEPT { + /* Start T_CCBS5/T_CCNR5 depending upon CC mode. */ + Action Start_T_SUPERVISION; + Action Reset_A_Status; + Next_State CC_STATE_ACTIVATED; + } + Stimulus CC_EVENT_SIGNALING_GONE { + /* Signaling link cleared. */ + Action Pass_Up_CC_Cancel; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Hangup_Signaling_Link; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_ACTIVATED { + Stimulus CC_EVENT_REMOTE_USER_FREE { + Action Pass_Up_A_Status_Indirect; + Test = Get_A_Status; + Test == Busy { + Next_State CC_STATE_SUSPENDED; + } + Action Send_RemoteUserFree; + Next_State CC_STATE_WAIT_CALLBACK; + } + Stimulus CC_EVENT_SUSPEND { + /* Received CCBS_T_Suspend */ + Action Set_A_Status_Busy; + } + Stimulus CC_EVENT_RESUME { + /* Received CCBS_T_Resume */ + Action Reset_A_Status; + } + Stimulus CC_EVENT_RECALL { + /* Received CCBS_T_Call */ + Action Pass_Up_CC_Call; + Action Set_Original_Call_Parameters; + } + Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION { + Action Pass_Up_CC_Cancel; + Action Hangup_Signaling_Link; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_SIGNALING_GONE { + /* Signaling link cleared. */ + Action Pass_Up_CC_Cancel; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Hangup_Signaling_Link; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_WAIT_CALLBACK { + Stimulus CC_EVENT_SUSPEND { + /* Received CCBS_T_Suspend */ + Action Set_A_Status_Busy; + Action Pass_Up_A_Status; + Next_State CC_STATE_SUSPENDED; + } + Stimulus CC_EVENT_RECALL { + /* Received CCBS_T_Call */ + Action Pass_Up_CC_Call; + Action Set_Original_Call_Parameters; + } + Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION { + Action Pass_Up_CC_Cancel; + Action Hangup_Signaling_Link; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_SIGNALING_GONE { + /* Signaling link cleared. */ + Action Pass_Up_CC_Cancel; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Hangup_Signaling_Link; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_SUSPENDED { + Stimulus CC_EVENT_RESUME { + /* Received CCBS_T_Resume */ + Action Set_A_Status_Free; + Action Pass_Up_A_Status; + Action Reset_A_Status; + Next_State CC_STATE_ACTIVATED; + } + Stimulus CC_EVENT_RECALL { + /* Received CCBS_T_Call */ + Action Pass_Up_CC_Call; + Action Set_Original_Call_Parameters; + } + Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION { + Action Pass_Up_CC_Cancel; + Action Hangup_Signaling_Link; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_SIGNALING_GONE { + /* Signaling link cleared. */ + Action Pass_Up_CC_Cancel; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Hangup_Signaling_Link; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } +} diff --git a/doc/cc_ptp_monitor.fsm b/doc/cc_ptp_monitor.fsm new file mode 100644 index 0000000..8729a62 --- /dev/null +++ b/doc/cc_ptp_monitor.fsm @@ -0,0 +1,168 @@ +/* + * FSM pseudo code used in the design/implementation of the CC PTP monitor. + */ +FSM CC_PTP_Monitor +{ + State CC_STATE_IDLE { + Init { + } + Prolog { + Action Set_Selfdestruct; + } + Stimulus CC_EVENT_AVAILABLE { + /* Received CCBS-T-Aailable */ + Action Pass_Up_CC_Available; + Next_State CC_STATE_AVAILABLE; + } + Stimulus CC_EVENT_CANCEL { + Action Set_Selfdestruct; + } + } + State CC_STATE_AVAILABLE { + /* + * The upper layer is responsible for canceling the CC available + * offering. + */ + Stimulus CC_EVENT_CC_REQUEST { + /* + * Before event is posted: + * cc_record->is_ccnr is set. + * The signaling connection call record is created. + */ + Action Queue_CC_Request; + /* + * For PTP mode the T_ACTIVATE timer is not defined. However, + * we will use it to protect our resources from leaks caused + * by the network cable being disconnected. + * This timer should be set longer than normal so the + * CC records will normally be cleaned up by network activity. + */ + Action Start_T_ACTIVATE; + Next_State CC_STATE_REQUESTED; + } + Stimulus CC_EVENT_CANCEL { + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_REQUESTED { + Epilog { + Action Stop_T_ACTIVATE; + } + Stimulus CC_EVENT_CC_REQUEST_ACCEPT { + /* + * Received CCBS-T-Request/CCNR-T-Request response + * Before event is posted: + * Negotiated CC retention setting saved + */ + Action Pass_Up_CC_Req_Rsp_Success; + Next_State CC_STATE_ACTIVATED; + } + Stimulus CC_EVENT_CC_REQUEST_FAIL { + Action Pass_Up_CC_Req_Rsp_Fail(error/reject, code); + Action Pass_Up_CC_Cancel; + /* + * If this request fail comes in with the RELEASE_COMPLETE + * message then the post action will never get a chance to + * run. It will be aborted because the CC_EVENT_SIGNALING_GONE + * will be processed first. + */ + Action Post_HANGUP_SIGNALING; + Next_State CC_STATE_WAIT_DESTRUCTION; + } + Stimulus CC_EVENT_TIMEOUT_T_ACTIVATE { + Action Pass_Up_CC_Req_Rsp_Timeout; + Action Pass_Up_CC_Cancel; + Action Hangup_Signaling_Link; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_SIGNALING_GONE { + /* Claim it was a timeout */ + Action Pass_Up_CC_Req_Rsp_Timeout; + Action Pass_Up_CC_Cancel; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Hangup_Signaling_Link; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_WAIT_DESTRUCTION { + /* + * Delayed disconnect of the signaling link to allow subcmd events + * from the signaling link to be passed up. + */ + Stimulus CC_EVENT_SIGNALING_GONE { + /* Signaling link cleared. */ + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_HANGUP_SIGNALING { + Action Hangup_Signaling_Link; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Hangup_Signaling_Link; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_ACTIVATED { + Prolog { + Action Reset_A_Status; + } + Stimulus CC_EVENT_REMOTE_USER_FREE { + /* Received CCBS_T_RemoteUserFree */ + Action Pass_Up_Remote_User_Free; + Test = Get_A_Status; + Test == Busy { + Next_State CC_STATE_SUSPENDED; + } + Next_State CC_STATE_WAIT_CALLBACK; + } + Stimulus CC_EVENT_SUSPEND { + Action Set_A_Status_Busy; + } + Stimulus CC_EVENT_RESUME { + Action Reset_A_Status; + } + } + State CC_STATE_WAIT_CALLBACK { + Stimulus CC_EVENT_SUSPEND { + Next_State CC_STATE_SUSPENDED; + } + } + State CC_STATE_SUSPENDED { + Prolog { + Action Send_CC_Suspend; + } + Stimulus CC_EVENT_RESUME { + Action Send_CC_Resume; + Next_State CC_STATE_ACTIVATED; + } + } + Superstate CC_ACTIVE(CC_STATE_ACTIVATED, CC_STATE_WAIT_CALLBACK, CC_STATE_SUSPENDED) { + Prolog { + /* Start T_CCBS6/T_CCNR6 depending upon CC mode. */ + Action Start_T_SUPERVISION; + } + Epilog { + Action Stop_T_SUPERVISION; + } + Stimulus CC_EVENT_RECALL { + /* The original call parameters have already been set. */ + Action Queue_SETUP_Recall; + } + Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION { + Action Pass_Up_CC_Cancel; + Action Hangup_Signaling_Link; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_SIGNALING_GONE { + /* Signaling link cleared. */ + Action Pass_Up_CC_Cancel; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Hangup_Signaling_Link; + Next_State CC_STATE_IDLE; + } + } +} diff --git a/doc/cc_ptp_monitor_flattened.fsm b/doc/cc_ptp_monitor_flattened.fsm new file mode 100644 index 0000000..8c2afb5 --- /dev/null +++ b/doc/cc_ptp_monitor_flattened.fsm @@ -0,0 +1,217 @@ +/* + * FSM pseudo code used in the design/implementation of the CC PTP monitor. + */ +FSM CC_PTP_Monitor +{ + State CC_STATE_IDLE { + Stimulus CC_EVENT_AVAILABLE { + /* Received CCBS-T-Aailable */ + Action Pass_Up_CC_Available; + Next_State CC_STATE_AVAILABLE; + } + Stimulus CC_EVENT_CANCEL { + Action Set_Selfdestruct; + } + } + State CC_STATE_AVAILABLE { + /* + * The upper layer is responsible for canceling the CC available + * offering. + */ + Stimulus CC_EVENT_CC_REQUEST { + /* + * Before event is posted: + * cc_record->is_ccnr is set. + * The signaling connection call record is created. + */ + Action Queue_CC_Request; + /* + * For PTP mode the T_ACTIVATE timer is not defined. However, + * we will use it to protect our resources from leaks caused + * by the network cable being disconnected. + * This timer should be set longer than normal so the + * CC records will normally be cleaned up by network activity. + */ + Action Start_T_ACTIVATE; + Next_State CC_STATE_REQUESTED; + } + Stimulus CC_EVENT_CANCEL { + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_REQUESTED { + Stimulus CC_EVENT_CC_REQUEST_ACCEPT { + /* + * Received CCBS-T-Request/CCNR-T-Request response + * Before event is posted: + * Negotiated CC retention setting saved + */ + Action Pass_Up_CC_Req_Rsp_Success; + Action Stop_T_ACTIVATE; + /* Start T_CCBS6/T_CCNR6 depending upon CC mode. */ + Action Start_T_SUPERVISION; + Action Reset_A_Status; + Next_State CC_STATE_ACTIVATED; + } + Stimulus CC_EVENT_CC_REQUEST_FAIL { + Action Pass_Up_CC_Req_Rsp_Fail(error/reject, code); + Action Pass_Up_CC_Cancel; + /* + * If this request fail comes in with the RELEASE_COMPLETE + * message then the post action will never get a chance to + * run. It will be aborted because the CC_EVENT_SIGNALING_GONE + * will be processed first. + */ + Action Post_HANGUP_SIGNALING; + Action Stop_T_ACTIVATE; + Next_State CC_STATE_WAIT_DESTRUCTION; + } + Stimulus CC_EVENT_TIMEOUT_T_ACTIVATE { + Action Pass_Up_CC_Req_Rsp_Timeout; + Action Pass_Up_CC_Cancel; + Action Hangup_Signaling_Link; + Action Stop_T_ACTIVATE; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_SIGNALING_GONE { + /* Claim it was a timeout */ + Action Pass_Up_CC_Req_Rsp_Timeout; + Action Pass_Up_CC_Cancel; + Action Stop_T_ACTIVATE; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Hangup_Signaling_Link; + Action Stop_T_ACTIVATE; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_WAIT_DESTRUCTION { + /* + * Delayed disconnect of the signaling link to allow subcmd events + * from the signaling link to be passed up. + */ + Stimulus CC_EVENT_SIGNALING_GONE { + /* Signaling link cleared. */ + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_HANGUP_SIGNALING { + Action Hangup_Signaling_Link; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Hangup_Signaling_Link; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_ACTIVATED { + Stimulus CC_EVENT_REMOTE_USER_FREE { + /* Received CCBS_T_RemoteUserFree */ + Action Pass_Up_Remote_User_Free; + Test = Get_A_Status; + Test == Busy { + Action Send_CC_Suspend; + Next_State CC_STATE_SUSPENDED; + } + Next_State CC_STATE_WAIT_CALLBACK; + } + Stimulus CC_EVENT_SUSPEND { + Action Set_A_Status_Busy; + } + Stimulus CC_EVENT_RESUME { + Action Reset_A_Status; + } + Stimulus CC_EVENT_RECALL { + /* The original call parameters have already been set. */ + Action Queue_SETUP_Recall; + } + Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION { + Action Pass_Up_CC_Cancel; + Action Hangup_Signaling_Link; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_SIGNALING_GONE { + /* Signaling link cleared. */ + Action Pass_Up_CC_Cancel; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Hangup_Signaling_Link; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_WAIT_CALLBACK { + Stimulus CC_EVENT_SUSPEND { + Action Send_CC_Suspend; + Next_State CC_STATE_SUSPENDED; + } + Stimulus CC_EVENT_RECALL { + /* The original call parameters have already been set. */ + Action Queue_SETUP_Recall; + } + Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION { + Action Pass_Up_CC_Cancel; + Action Hangup_Signaling_Link; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_SIGNALING_GONE { + /* Signaling link cleared. */ + Action Pass_Up_CC_Cancel; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Hangup_Signaling_Link; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_SUSPENDED { + Stimulus CC_EVENT_RESUME { + Action Send_CC_Resume; + Action Reset_A_Status; + Next_State CC_STATE_ACTIVATED; + } + Stimulus CC_EVENT_RECALL { + /* The original call parameters have already been set. */ + Action Queue_SETUP_Recall; + } + Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION { + Action Pass_Up_CC_Cancel; + Action Hangup_Signaling_Link; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_SIGNALING_GONE { + /* Signaling link cleared. */ + Action Pass_Up_CC_Cancel; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Hangup_Signaling_Link; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } +} diff --git a/doc/cc_qsig_agent.fsm b/doc/cc_qsig_agent.fsm new file mode 100644 index 0000000..f93f8b6 --- /dev/null +++ b/doc/cc_qsig_agent.fsm @@ -0,0 +1,136 @@ +/* + * FSM pseudo code used in the design/implementation of the CC Q.SIG agent. + */ +FSM CC_QSIG_Agent +{ + State CC_STATE_IDLE { + Init { + } + Prolog { + Action Set_Selfdestruct; + } + Stimulus CC_EVENT_AVAILABLE { + Next_State CC_STATE_AVAILABLE; + } + Stimulus CC_EVENT_CANCEL { + Action Set_Selfdestruct; + } + } + State CC_STATE_AVAILABLE { + /* + * For Q.SIG mode the T_RETENTION timer is not defined. However, + * we will use it anyway in this state to protect our resources + * from leaks caused by user A not requesting CC. This timer + * should be set much longer than the PTMP network link to + * allow for variations in user A's CC offer timer. + */ + Epilog { + Action Stop_T_RETENTION; + } + Stimulus CC_EVENT_MSG_RELEASE { + Action Stop_T_RETENTION; + Action Start_T_RETENTION; + } + Stimulus CC_EVENT_MSG_RELEASE_COMPLETE { + Action Stop_T_RETENTION; + Action Start_T_RETENTION; + } + Stimulus CC_EVENT_CC_REQUEST { + Action Pass_Up_CC_Request; + /* Send Q931_CALL_PROCEEDING message on signaling link. */ + Action Send_Call_Proceeding; + Next_State CC_STATE_REQUESTED; + } + Stimulus CC_EVENT_TIMEOUT_T_RETENTION { + Action Pass_Up_CC_Cancel; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_REQUESTED { + Stimulus CC_EVENT_CC_REQUEST_ACCEPT { + Next_State CC_STATE_ACTIVATED; + } + Stimulus CC_EVENT_SIGNALING_GONE { + /* Signaling link cleared. */ + Action Pass_Up_CC_Cancel; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Hangup_Signaling_Link; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_WAIT_DESTRUCTION { + /* + * Delayed disconnect of the signaling link to allow subcmd events + * from the signaling link to be passed up. + */ + Stimulus CC_EVENT_SIGNALING_GONE { + /* Signaling link cleared. */ + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_HANGUP_SIGNALING { + Action Hangup_Signaling_Link; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_ACTIVATED { + Stimulus CC_EVENT_REMOTE_USER_FREE { + /* Send ccExecPossible in FACILITY or SETUP. */ + Action Send_RemoteUserFree; + Next_State CC_STATE_WAIT_CALLBACK; + } + } + State CC_STATE_WAIT_CALLBACK { + Stimulus CC_EVENT_SUSPEND { + /* Received ccSuspend */ + Action Set_A_Status_Busy; + Action Pass_Up_A_Status; + Next_State CC_STATE_SUSPENDED; + } + Stimulus CC_EVENT_RECALL { + /* Received ccRingout */ + Action Pass_Up_CC_Call; + Action Set_Original_Call_Parameters; + } + } + State CC_STATE_SUSPENDED { + Stimulus CC_EVENT_RESUME { + /* Received ccResume */ + Action Set_A_Status_Free; + Action Pass_Up_A_Status; + Next_State CC_STATE_ACTIVATED; + } + } + Superstate CC_ACTIVE(CC_STATE_ACTIVATED, CC_STATE_WAIT_CALLBACK, CC_STATE_SUSPENDED) { + Prolog { + /* Start QSIG_CCBS_T2/QSIG_CCNR_T2 depending upon CC mode. */ + Action Start_T_SUPERVISION; + } + Epilog { + Action Stop_T_SUPERVISION; + } + Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION { + Action Pass_Up_CC_Cancel; + Action Send_CC_Cancel; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_SIGNALING_GONE { + /* Signaling link cleared. */ + Action Disassociate_Signaling_Link; + } + Stimulus CC_EVENT_LINK_CANCEL { + /* Received ccCancel */ + Action Pass_Up_CC_Cancel; + Action Post_HANGUP_SIGNALING; + Next_State CC_STATE_WAIT_DESTRUCTION; + } + Stimulus CC_EVENT_CANCEL { + Action Send_CC_Cancel; + Next_State CC_STATE_IDLE; + } + } +} diff --git a/doc/cc_qsig_agent_flattened.fsm b/doc/cc_qsig_agent_flattened.fsm new file mode 100644 index 0000000..ceafcd7 --- /dev/null +++ b/doc/cc_qsig_agent_flattened.fsm @@ -0,0 +1,183 @@ +/* + * FSM pseudo code used in the design/implementation of the CC Q.SIG agent. + */ +FSM CC_QSIG_Agent +{ + State CC_STATE_IDLE { + Stimulus CC_EVENT_AVAILABLE { + Next_State CC_STATE_AVAILABLE; + } + Stimulus CC_EVENT_CANCEL { + Action Set_Selfdestruct; + } + } + State CC_STATE_AVAILABLE { + /* + * For Q.SIG mode the T_RETENTION timer is not defined. However, + * we will use it anyway in this state to protect our resources + * from leaks caused by user A not requesting CC. This timer + * should be set much longer than the PTMP network link to + * allow for variations in user A's CC offer timer. + */ + Stimulus CC_EVENT_MSG_RELEASE { + Action Stop_T_RETENTION; + Action Start_T_RETENTION; + } + Stimulus CC_EVENT_MSG_RELEASE_COMPLETE { + Action Stop_T_RETENTION; + Action Start_T_RETENTION; + } + Stimulus CC_EVENT_CC_REQUEST { + Action Pass_Up_CC_Request; + /* Send Q931_CALL_PROCEEDING message on signaling link. */ + Action Send_Call_Proceeding; + Action Stop_T_RETENTION; + Next_State CC_STATE_REQUESTED; + } + Stimulus CC_EVENT_TIMEOUT_T_RETENTION { + Action Pass_Up_CC_Cancel; + Action Stop_T_RETENTION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Stop_T_RETENTION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_REQUESTED { + Stimulus CC_EVENT_CC_REQUEST_ACCEPT { + /* Start QSIG_CCBS_T2/QSIG_CCNR_T2 depending upon CC mode. */ + Action Start_T_SUPERVISION; + Next_State CC_STATE_ACTIVATED; + } + Stimulus CC_EVENT_SIGNALING_GONE { + /* Signaling link cleared. */ + Action Pass_Up_CC_Cancel; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Action Hangup_Signaling_Link; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_WAIT_DESTRUCTION { + /* + * Delayed disconnect of the signaling link to allow subcmd events + * from the signaling link to be passed up. + */ + Stimulus CC_EVENT_SIGNALING_GONE { + /* Signaling link cleared. */ + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_HANGUP_SIGNALING { + Action Hangup_Signaling_Link; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_ACTIVATED { + Stimulus CC_EVENT_REMOTE_USER_FREE { + /* Send ccExecPossible in FACILITY or SETUP. */ + Action Send_RemoteUserFree; + Next_State CC_STATE_WAIT_CALLBACK; + } + Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION { + Action Pass_Up_CC_Cancel; + Action Send_CC_Cancel; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_SIGNALING_GONE { + /* Signaling link cleared. */ + Action Disassociate_Signaling_Link; + } + Stimulus CC_EVENT_LINK_CANCEL { + /* Received ccCancel */ + Action Pass_Up_CC_Cancel; + Action Post_HANGUP_SIGNALING; + Action Stop_T_SUPERVISION; + Next_State CC_STATE_WAIT_DESTRUCTION; + } + Stimulus CC_EVENT_CANCEL { + Action Send_CC_Cancel; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_WAIT_CALLBACK { + Stimulus CC_EVENT_SUSPEND { + /* Received ccSuspend */ + Action Set_A_Status_Busy; + Action Pass_Up_A_Status; + Next_State CC_STATE_SUSPENDED; + } + Stimulus CC_EVENT_RECALL { + /* Received ccRingout */ + Action Pass_Up_CC_Call; + Action Set_Original_Call_Parameters; + } + Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION { + Action Pass_Up_CC_Cancel; + Action Send_CC_Cancel; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_SIGNALING_GONE { + /* Signaling link cleared. */ + Action Disassociate_Signaling_Link; + } + Stimulus CC_EVENT_LINK_CANCEL { + /* Received ccCancel */ + Action Pass_Up_CC_Cancel; + Action Post_HANGUP_SIGNALING; + Action Stop_T_SUPERVISION; + Next_State CC_STATE_WAIT_DESTRUCTION; + } + Stimulus CC_EVENT_CANCEL { + Action Send_CC_Cancel; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_SUSPENDED { + Stimulus CC_EVENT_RESUME { + /* Received ccResume */ + Action Set_A_Status_Free; + Action Pass_Up_A_Status; + Next_State CC_STATE_ACTIVATED; + } + Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION { + Action Pass_Up_CC_Cancel; + Action Send_CC_Cancel; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_SIGNALING_GONE { + /* Signaling link cleared. */ + Action Disassociate_Signaling_Link; + } + Stimulus CC_EVENT_LINK_CANCEL { + /* Received ccCancel */ + Action Pass_Up_CC_Cancel; + Action Post_HANGUP_SIGNALING; + Action Stop_T_SUPERVISION; + Next_State CC_STATE_WAIT_DESTRUCTION; + } + Stimulus CC_EVENT_CANCEL { + Action Send_CC_Cancel; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } +} diff --git a/doc/cc_qsig_monitor.fsm b/doc/cc_qsig_monitor.fsm new file mode 100644 index 0000000..a6e631c --- /dev/null +++ b/doc/cc_qsig_monitor.fsm @@ -0,0 +1,249 @@ +/* + * FSM pseudo code used in the design/implementation of the CC Q.SIG monitor. + */ +FSM CC_QSIG_Monitor +{ + State CC_STATE_IDLE { + Init { + } + Prolog { + Action Set_Selfdestruct; + } + Stimulus CC_EVENT_AVAILABLE { + /* + * LibPRI will determine if CC will be offered based upon + * if it is even possible. + * Essentially: + * 1) The call must not have been redirected in this link's + * setup. + * 2) Received an ALERTING or received a + * DISCONNECT(busy/congestion). + */ + Action Pass_Up_CC_Available; + Next_State CC_STATE_AVAILABLE; + } + Stimulus CC_EVENT_CANCEL { + Action Set_Selfdestruct; + } + } + State CC_STATE_AVAILABLE { + /* + * The upper layer is responsible for canceling the CC available + * offering. + */ + Stimulus CC_EVENT_CC_REQUEST { + /* + * Before event is posted: + * cc_record->is_ccnr is set. + * The signaling connection call record is created. + */ + Action Queue_CC_Request; + /* Start QSIG_CC_T1. */ + Action Start_T_ACTIVATE; + Next_State CC_STATE_REQUESTED; + } + Stimulus CC_EVENT_CANCEL { + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_REQUESTED { + Stimulus CC_EVENT_CC_REQUEST_ACCEPT { + /* + * Received ccbsRequest/ccnrRequest response + * Before event is posted: + * Negotiated CC retention setting saved + * Negotiated signaling link retention setting saved + */ + Action Stop_T_ACTIVATE; + Test = Get_msgtype; + Test == Q931_RELEASE { + Action Disassociate_Signaling_Link; + Test = Get_Retain_Signaling_Link; + Test == TRUE { + /* + * The far end did not honor the + * signaling link retention requirement. + * ECMA-186 Section 6.5.2.2.1 + */ + Action Pass_Up_CC_Req_Rsp_Timeout; + Action Pass_Up_CC_Cancel; + Action Send_CC_Cancel; + Next_State CC_STATE_IDLE; + } + } + Action Pass_Up_CC_Req_Rsp_Success; + Next_State CC_STATE_ACTIVATED; + } + Stimulus CC_EVENT_CC_REQUEST_FAIL { + Action Stop_T_ACTIVATE; + Action Pass_Up_CC_Req_Rsp_Fail(error/reject, code); + Action Pass_Up_CC_Cancel; + /* + * If this request fail comes in with the RELEASE message + * then the post action will never get a chance to run. + * It will be aborted because the CC_EVENT_SIGNALING_GONE + * will be processed first. + */ + Action Post_HANGUP_SIGNALING; + Next_State CC_STATE_WAIT_DESTRUCTION; + } + Stimulus CC_EVENT_TIMEOUT_T_ACTIVATE { + Action Stop_T_ACTIVATE; + Action Pass_Up_CC_Req_Rsp_Timeout; + Action Pass_Up_CC_Cancel; + Action Hangup_Signaling_Link; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_SIGNALING_GONE { + Action Stop_T_ACTIVATE; + /* Claim it was a timeout */ + Action Pass_Up_CC_Req_Rsp_Timeout; + Action Pass_Up_CC_Cancel; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Next_State CC_STATE_WAIT_DESTRUCTION; + } + } + State CC_STATE_WAIT_DESTRUCTION { + /* + * Delayed disconnect of the signaling link to allow subcmd events + * from the signaling link to be passed up. + */ + Stimulus CC_EVENT_CC_REQUEST_ACCEPT { + /* + * Received ccbsRequest/ccnrRequest response + * Before event is posted: + * Negotiated CC retention setting saved + * Negotiated signaling link retention setting saved + */ + Action Stop_T_ACTIVATE; + Test = Get_msgtype; + Test == Q931_RELEASE { + Action Disassociate_Signaling_Link; + } + Action Send_CC_Cancel; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CC_REQUEST_FAIL { + Action Stop_T_ACTIVATE; + /* + * If this request fail comes in with the RELEASE message + * then the post action will never get a chance to run. + * It will be aborted because the CC_EVENT_SIGNALING_GONE + * will be processed first. + */ + Action Post_HANGUP_SIGNALING; + } + Stimulus CC_EVENT_TIMEOUT_T_ACTIVATE { + Action Stop_T_ACTIVATE; + Action Hangup_Signaling_Link; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_SIGNALING_GONE { + /* Signaling link cleared. */ + Action Stop_T_ACTIVATE; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_HANGUP_SIGNALING { + //Action Stop_T_ACTIVATE; + Action Hangup_Signaling_Link; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_ACTIVATED { + Prolog { + Action Reset_A_Status; + } + Stimulus CC_EVENT_REMOTE_USER_FREE { + /* Received ccExecPossible */ + Action Pass_Up_Remote_User_Free; + /* + * ECMA-186 Section 6.5.2.1.7 + * Implied switch to retain-signaling-link. + */ + Action Set_Retain_Signaling_Link; + Test = Get_msgtype; + Test == Q931_SETUP { + /* Send Q931_CALL_PROCEEDING message on signaling link. */ + Action Send_Call_Proceeding; + } + Test = Get_A_Status; + Test == Busy { + Next_State CC_STATE_SUSPENDED; + } + Next_State CC_STATE_WAIT_CALLBACK; + } + Stimulus CC_EVENT_SUSPEND { + Action Set_A_Status_Busy; + } + Stimulus CC_EVENT_RESUME { + Action Reset_A_Status; + } + } + State CC_STATE_WAIT_CALLBACK { + Prolog { + /* Start QSIG_CC_T3 */ + Action Start_T_RECALL; + } + Epilog { + Action Stop_T_RECALL; + } + Stimulus CC_EVENT_RECALL { + /* The original call parameters have already been set. */ + Action Queue_SETUP_Recall; + Next_State CC_STATE_CALLBACK; + } + Stimulus CC_EVENT_SUSPEND { + Next_State CC_STATE_SUSPENDED; + } + Stimulus CC_EVENT_TIMEOUT_T_RECALL { + Action Pass_Up_CC_Cancel; + Action Send_CC_Cancel; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_CALLBACK { + } + State CC_STATE_SUSPENDED { + Prolog { + /* + * The ccSuspend will be sent in a FACILITY or CONNECT + * message depending upon the CIS call state. + */ + Action Send_CC_Suspend; + } + Stimulus CC_EVENT_RESUME { + Action Send_CC_Resume; + Next_State CC_STATE_ACTIVATED; + } + } + Superstate CC_ACTIVE(CC_STATE_ACTIVATED, CC_STATE_WAIT_CALLBACK, CC_STATE_CALLBACK, CC_STATE_SUSPENDED) { + Prolog { + /* Start QSIG_CCBS_T2/QSIG_CCNR_T2 depending upon CC mode. */ + Action Start_T_SUPERVISION; + } + Epilog { + Action Stop_T_SUPERVISION; + } + Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION { + Action Pass_Up_CC_Cancel; + Action Send_CC_Cancel; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_SIGNALING_GONE { + /* Signaling link cleared. */ + Action Disassociate_Signaling_Link; + } + Stimulus CC_EVENT_LINK_CANCEL { + /* Received ccCancel */ + Action Pass_Up_CC_Cancel; + Action Post_HANGUP_SIGNALING; + Next_State CC_STATE_WAIT_DESTRUCTION; + } + Stimulus CC_EVENT_CANCEL { + Action Send_CC_Cancel; + Next_State CC_STATE_IDLE; + } + } +} diff --git a/doc/cc_qsig_monitor_flattened.fsm b/doc/cc_qsig_monitor_flattened.fsm new file mode 100644 index 0000000..5a7ed6f --- /dev/null +++ b/doc/cc_qsig_monitor_flattened.fsm @@ -0,0 +1,327 @@ +/* + * FSM pseudo code used in the design/implementation of the CC Q.SIG monitor. + */ +FSM CC_QSIG_Monitor +{ + State CC_STATE_IDLE { + Stimulus CC_EVENT_AVAILABLE { + /* + * LibPRI will determine if CC will be offered based upon + * if it is even possible. + * Essentially: + * 1) The call must not have been redirected in this link's + * setup. + * 2) Received an ALERTING or received a + * DISCONNECT(busy/congestion). + */ + Action Pass_Up_CC_Available; + Next_State CC_STATE_AVAILABLE; + } + Stimulus CC_EVENT_CANCEL { + Action Set_Selfdestruct; + } + } + State CC_STATE_AVAILABLE { + /* + * The upper layer is responsible for canceling the CC available + * offering. + */ + Stimulus CC_EVENT_CC_REQUEST { + /* + * Before event is posted: + * cc_record->is_ccnr is set. + * The signaling connection call record is created. + */ + Action Queue_CC_Request; + /* Start QSIG_CC_T1. */ + Action Start_T_ACTIVATE; + Next_State CC_STATE_REQUESTED; + } + Stimulus CC_EVENT_CANCEL { + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_REQUESTED { + Stimulus CC_EVENT_CC_REQUEST_ACCEPT { + /* + * Received ccbsRequest/ccnrRequest response + * Before event is posted: + * Negotiated CC retention setting saved + * Negotiated signaling link retention setting saved + */ + Action Stop_T_ACTIVATE; + Test = Get_msgtype; + Test == Q931_RELEASE { + Action Disassociate_Signaling_Link; + Test = Get_Retain_Signaling_Link; + Test == TRUE { + /* + * The far end did not honor the + * signaling link retention requirement. + * ECMA-186 Section 6.5.2.2.1 + */ + Action Pass_Up_CC_Req_Rsp_Timeout; + Action Pass_Up_CC_Cancel; + Action Send_CC_Cancel; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + Action Pass_Up_CC_Req_Rsp_Success; + /* Start QSIG_CCBS_T2/QSIG_CCNR_T2 depending upon CC mode. */ + Action Start_T_SUPERVISION; + Action Reset_A_Status; + Next_State CC_STATE_ACTIVATED; + } + Stimulus CC_EVENT_CC_REQUEST_FAIL { + Action Stop_T_ACTIVATE; + Action Pass_Up_CC_Req_Rsp_Fail(error/reject, code); + Action Pass_Up_CC_Cancel; + /* + * If this request fail comes in with the RELEASE message + * then the post action will never get a chance to run. + * It will be aborted because the CC_EVENT_SIGNALING_GONE + * will be processed first. + */ + Action Post_HANGUP_SIGNALING; + Next_State CC_STATE_WAIT_DESTRUCTION; + } + Stimulus CC_EVENT_TIMEOUT_T_ACTIVATE { + Action Stop_T_ACTIVATE; + Action Pass_Up_CC_Req_Rsp_Timeout; + Action Pass_Up_CC_Cancel; + Action Hangup_Signaling_Link; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_SIGNALING_GONE { + Action Stop_T_ACTIVATE; + /* Claim it was a timeout */ + Action Pass_Up_CC_Req_Rsp_Timeout; + Action Pass_Up_CC_Cancel; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CANCEL { + Next_State CC_STATE_WAIT_DESTRUCTION; + } + } + State CC_STATE_WAIT_DESTRUCTION { + /* + * Delayed disconnect of the signaling link to allow subcmd events + * from the signaling link to be passed up. + */ + Stimulus CC_EVENT_CC_REQUEST_ACCEPT { + /* + * Received ccbsRequest/ccnrRequest response + * Before event is posted: + * Negotiated CC retention setting saved + * Negotiated signaling link retention setting saved + */ + Action Stop_T_ACTIVATE; + Test = Get_msgtype; + Test == Q931_RELEASE { + Action Disassociate_Signaling_Link; + } + Action Send_CC_Cancel; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_CC_REQUEST_FAIL { + Action Stop_T_ACTIVATE; + /* + * If this request fail comes in with the RELEASE message + * then the post action will never get a chance to run. + * It will be aborted because the CC_EVENT_SIGNALING_GONE + * will be processed first. + */ + Action Post_HANGUP_SIGNALING; + } + Stimulus CC_EVENT_TIMEOUT_T_ACTIVATE { + Action Stop_T_ACTIVATE; + Action Hangup_Signaling_Link; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_SIGNALING_GONE { + /* Signaling link cleared. */ + Action Stop_T_ACTIVATE; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_HANGUP_SIGNALING { + //Action Stop_T_ACTIVATE; + Action Hangup_Signaling_Link; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_ACTIVATED { + Stimulus CC_EVENT_REMOTE_USER_FREE { + /* Received ccExecPossible */ + Action Pass_Up_Remote_User_Free; + /* + * ECMA-186 Section 6.5.2.1.7 + * Implied switch to retain-signaling-link. + */ + Action Set_Retain_Signaling_Link; + Test = Get_msgtype; + Test == Q931_SETUP { + /* Send Q931_CALL_PROCEEDING message on signaling link. */ + Action Send_Call_Proceeding; + } + Test = Get_A_Status; + Test == Busy { + /* + * The ccSuspend will be sent in a FACILITY or CONNECT + * message depending upon the CIS call state. + */ + Action Send_CC_Suspend; + Next_State CC_STATE_SUSPENDED; + } + /* Start QSIG_CC_T3 */ + Action Start_T_RECALL; + Next_State CC_STATE_WAIT_CALLBACK; + } + Stimulus CC_EVENT_SUSPEND { + Action Set_A_Status_Busy; + } + Stimulus CC_EVENT_RESUME { + Action Reset_A_Status; + } + Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION { + Action Pass_Up_CC_Cancel; + Action Send_CC_Cancel; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_SIGNALING_GONE { + /* Signaling link cleared. */ + Action Disassociate_Signaling_Link; + } + Stimulus CC_EVENT_LINK_CANCEL { + /* Received ccCancel */ + Action Pass_Up_CC_Cancel; + Action Post_HANGUP_SIGNALING; + Action Stop_T_SUPERVISION; + Next_State CC_STATE_WAIT_DESTRUCTION; + } + Stimulus CC_EVENT_CANCEL { + Action Send_CC_Cancel; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_WAIT_CALLBACK { + Stimulus CC_EVENT_RECALL { + /* The original call parameters have already been set. */ + Action Queue_SETUP_Recall; + Action Stop_T_RECALL; + Next_State CC_STATE_CALLBACK; + } + Stimulus CC_EVENT_SUSPEND { + Action Stop_T_RECALL; + /* + * The ccSuspend will be sent in a FACILITY or CONNECT + * message depending upon the CIS call state. + */ + Action Send_CC_Suspend; + Next_State CC_STATE_SUSPENDED; + } + Stimulus CC_EVENT_TIMEOUT_T_RECALL { + Action Pass_Up_CC_Cancel; + Action Send_CC_Cancel; + Action Stop_T_RECALL; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION { + Action Pass_Up_CC_Cancel; + Action Send_CC_Cancel; + Action Stop_T_RECALL; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_SIGNALING_GONE { + /* Signaling link cleared. */ + Action Disassociate_Signaling_Link; + } + Stimulus CC_EVENT_LINK_CANCEL { + /* Received ccCancel */ + Action Pass_Up_CC_Cancel; + Action Post_HANGUP_SIGNALING; + Action Stop_T_RECALL; + Action Stop_T_SUPERVISION; + Next_State CC_STATE_WAIT_DESTRUCTION; + } + Stimulus CC_EVENT_CANCEL { + Action Send_CC_Cancel; + Action Stop_T_RECALL; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_CALLBACK { + Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION { + Action Pass_Up_CC_Cancel; + Action Send_CC_Cancel; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_SIGNALING_GONE { + /* Signaling link cleared. */ + Action Disassociate_Signaling_Link; + } + Stimulus CC_EVENT_LINK_CANCEL { + /* Received ccCancel */ + Action Pass_Up_CC_Cancel; + Action Post_HANGUP_SIGNALING; + Action Stop_T_SUPERVISION; + Next_State CC_STATE_WAIT_DESTRUCTION; + } + Stimulus CC_EVENT_CANCEL { + Action Send_CC_Cancel; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } + State CC_STATE_SUSPENDED { + Stimulus CC_EVENT_RESUME { + Action Send_CC_Resume; + Action Reset_A_Status; + Next_State CC_STATE_ACTIVATED; + } + Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION { + Action Pass_Up_CC_Cancel; + Action Send_CC_Cancel; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + Stimulus CC_EVENT_SIGNALING_GONE { + /* Signaling link cleared. */ + Action Disassociate_Signaling_Link; + } + Stimulus CC_EVENT_LINK_CANCEL { + /* Received ccCancel */ + Action Pass_Up_CC_Cancel; + Action Post_HANGUP_SIGNALING; + Action Stop_T_SUPERVISION; + Next_State CC_STATE_WAIT_DESTRUCTION; + } + Stimulus CC_EVENT_CANCEL { + Action Send_CC_Cancel; + Action Stop_T_SUPERVISION; + Action Set_Selfdestruct; + Next_State CC_STATE_IDLE; + } + } +} diff --git a/libpri.h b/libpri.h index 89dbb2a..0e1dbb4 100644 --- a/libpri.h +++ b/libpri.h @@ -51,6 +51,7 @@ #define PRI_DEBUG_Q931_ANOMALY (1 << 7) /* Show unexpected events */ #define PRI_DEBUG_APDU (1 << 8) /* Debug of APDU components such as ROSE */ #define PRI_DEBUG_AOC (1 << 9) /* Debug of Advice of Charge ROSE Messages */ +#define PRI_DEBUG_CC (1 << 10) /* Debug call-completion. */ #define PRI_DEBUG_ALL (0xffff) /* Everything */ @@ -439,6 +440,14 @@ struct pri_party_subaddress { unsigned char data[32]; }; +/*! \brief Addressing information needed to identify an endpoint in a call. */ +struct pri_party_address { + /*! \brief Subscriber phone number */ + struct pri_party_number number; + /*! \brief Subscriber subaddress */ + struct pri_party_subaddress subaddress; +}; + /*! \brief Information needed to identify an endpoint in a call. */ struct pri_party_id { /*! \brief Subscriber name */ @@ -503,11 +512,127 @@ struct pri_rerouting_data { int invoke_id; }; -/* Subcommands derived from supplementary services. */ -#define PRI_SUBCMD_REDIRECTING 1 -#define PRI_SUBCMD_CONNECTED_LINE 2 -#define PRI_SUBCMD_REROUTING 3 +/* + * NOTE: + * The code surrounded by STATUS_REQUEST_PLACE_HOLDER is not implemented. + * The STATUS_REQUEST_PLACE_HOLDER code will be made unconditional if support + * for the messages is ever needed (and gets written). + */ +/* Subcommands derived from supplementary services. */ +#define PRI_SUBCMD_REDIRECTING 1 /*!< Redirecting information update */ +#define PRI_SUBCMD_CONNECTED_LINE 2 /*!< Connected line information update */ +#define PRI_SUBCMD_REROUTING 3 /*!< CallRerouting/CallDeflection received. */ +#if defined(STATUS_REQUEST_PLACE_HOLDER) +#define PRI_SUBCMD_STATUS_REQ 4 /*!< Determine the status of the given party. */ +#define PRI_SUBCMD_STATUS_REQ_RSP 5 /*!< Status request response */ +#endif /* defined(STATUS_REQUEST_PLACE_HOLDER) */ +#define PRI_SUBCMD_CC_AVAILABLE 6 /*!< Indicate that CC is available */ +#define PRI_SUBCMD_CC_REQ 7 /*!< CC activation request */ +#define PRI_SUBCMD_CC_REQ_RSP 8 /*!< CC activation request response */ +#define PRI_SUBCMD_CC_REMOTE_USER_FREE 9 /*!< Indicate that CC party B is available, party A is considered free. */ +#define PRI_SUBCMD_CC_B_FREE 10 /*!< Indicate that CC party B is available, party A is considered busy. */ +#define PRI_SUBCMD_CC_STATUS_REQ 11 /*!< Request/prod to receive updates of CC party A status */ +#define PRI_SUBCMD_CC_STATUS_REQ_RSP 12 /*!< Requested update of CC party A status */ +#define PRI_SUBCMD_CC_STATUS 13 /*!< Unsolicited update of CC party A status */ +#define PRI_SUBCMD_CC_CALL 14 /*!< Indicate that this call is a CC callback */ +#define PRI_SUBCMD_CC_CANCEL 15 /*!< Unsolicited indication that CC is canceled */ +#define PRI_SUBCMD_CC_STOP_ALERTING 16 /*!< Indicate that someone else has responed to remote user free */ + +#if defined(STATUS_REQUEST_PLACE_HOLDER) +struct pri_subcmd_status_request { + /*! + * \brief Invoke id in case there are multiple outstanding requests. + * \note Used to match any responses with the original invoke in case + * there are several requests active. + */ + int invoke_id; + /*! \brief Party address requesting status about. */ + struct pri_party_address party; +}; +#endif /* defined(STATUS_REQUEST_PLACE_HOLDER) */ + +#if defined(STATUS_REQUEST_PLACE_HOLDER) +struct pri_subcmd_status_request_rsp { + /*! + * \brief Request id in case there are multiple outstanding requests. + * \note Used to match any responses with the request in case there + * are several requests active. + */ + int request_id; + /*! + * \brief Response status to the status request. + * \details + * free(0), + * busy(1), + * incompatible(2) + * timeout(3), + */ + int status; +}; +#endif /* defined(STATUS_REQUEST_PLACE_HOLDER) */ + +struct pri_subcmd_cc_id { + /*! \brief Call-Completion record id */ + long cc_id; +}; + +struct pri_subcmd_cc_request { + /*! \brief Call-Completion record id */ + long cc_id; + /*! + * \brief Mode of call-completion requested. + * \details + * ccbs(0), + * ccnr(1) + */ + int mode; +}; + +struct pri_subcmd_cc_request_rsp { + /*! \brief Call-Completion record id */ + long cc_id; + /*! + * \brief Status of the requested call-completion activation. + * \details + * success(0), + * timeout(1), + * error(2), + * reject(3) + */ + int status; + /*! + * \brief Failure code that can be converted to a string to further + * explain the non-timeout failure. + * \note Valid when status is error or reject. + * \note Use pri_facility_error2str() to convert the error_code. + * \note Use pri_facility_reject2str() to convert the reject_code. + */ + int fail_code; +}; + +struct pri_subcmd_cc_status { + /*! \brief Call-Completion record id */ + long cc_id; + /*! + * \brief Party A status. + * \details + * free(0), + * busy(1) + */ + int status; +}; + +struct pri_subcmd_cc_cancel { + /*! \brief Call-Completion record id */ + long cc_id; + /*! + * \brief TRUE if the cc_id is for an agent. + * \note This is a convenience value so the upper layer can know which + * list it should search for the cc_id. + */ + int is_agent; +}; struct pri_subcommand { /*! PRI_SUBCMD_xxx defined values */ @@ -518,6 +643,21 @@ struct pri_subcommand { struct pri_party_connected_line connected_line; struct pri_party_redirecting redirecting; struct pri_rerouting_data rerouting; +#if defined(STATUS_REQUEST_PLACE_HOLDER) + struct pri_subcmd_status_request status_request; + struct pri_subcmd_status_request_rsp status_request_rsp; +#endif /* defined(STATUS_REQUEST_PLACE_HOLDER) */ + struct pri_subcmd_cc_id cc_available; + struct pri_subcmd_cc_request cc_request; + struct pri_subcmd_cc_request_rsp cc_request_rsp; + struct pri_subcmd_cc_id cc_remote_user_free; + struct pri_subcmd_cc_id cc_b_free; + struct pri_subcmd_cc_id cc_stop_alerting; + struct pri_subcmd_cc_id cc_status_req; + struct pri_subcmd_cc_status cc_status_req_rsp; + struct pri_subcmd_cc_status cc_status; + struct pri_subcmd_cc_id cc_call; + struct pri_subcmd_cc_cancel cc_cancel; } u; }; @@ -867,6 +1007,24 @@ char *pri_plan2str(int plan); /* Turn cause into a string */ char *pri_cause2str(int cause); +/*! + * \brief Convert the given facility error code to a descriptive string. + * + * \param facility_error_code Error code to convert to a string. + * + * \return Descriptive error string. + */ +const char *pri_facility_error2str(int facility_error_code); + +/*! + * \brief Convert the given facility reject code to a descriptive string. + * + * \param facility_reject_code Error code to convert to a string. + * + * \return Descriptive reject string. + */ +const char *pri_facility_reject2str(int facility_reject_code); + /* Acknowledge a call and place it on the given channel. Set info to non-zero if there is in-band data available on the channel */ int pri_acknowledge(struct pri *pri, q931_call *call, int channel, int info); @@ -1262,6 +1420,65 @@ int pri_retrieve_ack(struct pri *ctrl, q931_call *call, int channel); */ int pri_retrieve_rej(struct pri *ctrl, q931_call *call, int cause); +#if defined(STATUS_REQUEST_PLACE_HOLDER) +int pri_status_req(struct pri *ctrl, int request_id, const struct pri_sr *req); +void pri_status_req_rsp(struct pri *ctrl, int invoke_id, int status); +#endif /* defined(STATUS_REQUEST_PLACE_HOLDER) */ + +/*! + * \brief Set the call completion feature enable flag. + * + * \param ctrl D channel controller. + * \param enable TRUE to enable call completion feature. + * + * \return Nothing + */ +void pri_cc_enable(struct pri *ctrl, int enable); + +/*! + * \brief Set the PTMP NT call completion recall mode. + * + * \param ctrl D channel controller. + * \param mode globalRecall(0), specificRecall(1) + * + * \return Nothing + */ +void pri_cc_recall_mode(struct pri *ctrl, int mode); + +/*! + * \brief Set the Q.SIG call completion signaling link retention mode. + * (Requestor/Initiator/Originator/Party-A) + * + * \param ctrl D channel controller. + * \param signaling_retention release(0), retain(1), do-not-care(2). + * + * \return Nothing + */ +void pri_cc_retain_signaling_req(struct pri *ctrl, int signaling_retention); + +/*! + * \brief Set the Q.SIG call completion signaling link retention mode. + * (Responder/Answerer/Party-B) + * + * \param ctrl D channel controller. + * \param signaling_retention release(0), retain(1). + * + * \return Nothing + */ +void pri_cc_retain_signaling_rsp(struct pri *ctrl, int signaling_retention); + +long pri_cc_available(struct pri *ctrl, q931_call *call); +int pri_cc_req(struct pri *ctrl, long cc_id, int mode); +int pri_cc_req_rsp(struct pri *ctrl, long cc_id, int status); +void pri_cc_remote_user_free(struct pri *ctrl, long cc_id); +void pri_cc_b_free(struct pri *ctrl, long cc_id); +void pri_cc_stop_alerting(struct pri *ctrl, long cc_id); +void pri_cc_status_req(struct pri *ctrl, long cc_id); +void pri_cc_status_req_rsp(struct pri *ctrl, long cc_id, int status); +void pri_cc_status(struct pri *ctrl, long cc_id, int status); +int pri_cc_call(struct pri *ctrl, long cc_id, q931_call *call, struct pri_sr *req); +void pri_cc_cancel(struct pri *ctrl, long cc_id); + /* Get/Set PRI Timers */ #define PRI_GETSET_TIMERS int pri_set_timer(struct pri *pri, int timer, int value); @@ -1309,6 +1526,31 @@ enum PRI_TIMERS_AND_COUNTERS { PRI_TIMER_T_RESPONSE, /*!< Maximum time to wait for a typical APDU response. */ + PRI_TIMER_T_STATUS, /*!< Max time to wait for all replies to check for compatible terminals */ + + PRI_TIMER_T_ACTIVATE, /*!< Request supervision timeout. */ + PRI_TIMER_T_DEACTIVATE, /*!< Deactivate supervision timeout. */ + PRI_TIMER_T_INTERROGATE,/*!< Interrogation supervision timeout. */ + + /* ETSI call-completion timers */ + PRI_TIMER_T_RETENTION, /*!< Max time to wait for user A to activate call-completion. */ + PRI_TIMER_T_CCBS1, /*!< T-STATUS timer equivalent for CC user A status. */ + PRI_TIMER_T_CCBS2, /*!< Max time the CCBS service will be active */ + PRI_TIMER_T_CCBS3, /*!< Max time to wait for user A to respond to user B availability. */ + PRI_TIMER_T_CCBS4, /*!< CC user B guard time before sending CC recall indication. */ + PRI_TIMER_T_CCBS5, /*!< Network B CCBS supervision timeout. */ + PRI_TIMER_T_CCBS6, /*!< Network A CCBS supervision timeout. */ + PRI_TIMER_T_CCNR2, /*!< Max time the CCNR service will be active */ + PRI_TIMER_T_CCNR5, /*!< Network B CCNR supervision timeout. */ + PRI_TIMER_T_CCNR6, /*!< Network A CCNR supervision timeout. */ + + /* Q.SIG call-completion timers */ + PRI_TIMER_QSIG_CC_T1, /*!< CC request supervision timeout. */ + PRI_TIMER_QSIG_CCBS_T2, /*!< CCBS supervision timeout. */ + PRI_TIMER_QSIG_CCNR_T2, /*!< CCNR supervision timeout. */ + PRI_TIMER_QSIG_CC_T3, /*!< Max time to wait for user A to respond to user B availability. */ + PRI_TIMER_QSIG_CC_T4, /*!< Path reservation supervision timeout. */ + /* Must be last in the enum list */ PRI_MAX_TIMERS }; diff --git a/pri.c b/pri.c index 4aa9456..ea6d176 100644 --- a/pri.c +++ b/pri.c @@ -40,11 +40,10 @@ #include "libpri.h" #include "pri_internal.h" #include "pri_facility.h" -#include "pri_q921.h" -#include "pri_q931.h" #define PRI_BIT(a_bit) (1UL << (a_bit)) #define PRI_ALL_SWITCHES 0xFFFFFFFF +#define PRI_ETSI_SWITCHES (PRI_BIT(PRI_SWITCH_EUROISDN_E1) | PRI_BIT(PRI_SWITCH_EUROISDN_T1)) struct pri_timer_table { const char *name; @@ -89,6 +88,27 @@ static const struct pri_timer_table pri_timer[] = { { "T-HOLD", PRI_TIMER_T_HOLD, PRI_ALL_SWITCHES }, { "T-RETRIEVE", PRI_TIMER_T_RETRIEVE, PRI_ALL_SWITCHES }, { "T-RESPONSE", PRI_TIMER_T_RESPONSE, PRI_ALL_SWITCHES }, + { "T-STATUS", PRI_TIMER_T_STATUS, PRI_ETSI_SWITCHES }, + { "T-ACTIVATE", PRI_TIMER_T_ACTIVATE, PRI_ETSI_SWITCHES }, + { "T-DEACTIVATE", PRI_TIMER_T_DEACTIVATE, PRI_ETSI_SWITCHES }, + { "T-INTERROGATE", PRI_TIMER_T_INTERROGATE, PRI_ETSI_SWITCHES }, + { "T-RETENTION", PRI_TIMER_T_RETENTION, PRI_ETSI_SWITCHES | PRI_BIT(PRI_SWITCH_QSIG) }, + { "T-CCBS1", PRI_TIMER_T_CCBS1, PRI_ETSI_SWITCHES }, + { "T-CCBS2", PRI_TIMER_T_CCBS2, PRI_ETSI_SWITCHES }, + { "T-CCBS3", PRI_TIMER_T_CCBS3, PRI_ETSI_SWITCHES }, + { "T-CCBS4", PRI_TIMER_T_CCBS4, PRI_ETSI_SWITCHES }, + { "T-CCBS5", PRI_TIMER_T_CCBS5, PRI_ETSI_SWITCHES }, + { "T-CCBS6", PRI_TIMER_T_CCBS6, PRI_ETSI_SWITCHES }, + { "T-CCNR2", PRI_TIMER_T_CCNR2, PRI_ETSI_SWITCHES }, + { "T-CCNR5", PRI_TIMER_T_CCNR5, PRI_ETSI_SWITCHES }, + { "T-CCNR6", PRI_TIMER_T_CCNR6, PRI_ETSI_SWITCHES }, + { "CC-T1", PRI_TIMER_QSIG_CC_T1, PRI_BIT(PRI_SWITCH_QSIG) }, + { "CCBS-T2", PRI_TIMER_QSIG_CCBS_T2, PRI_BIT(PRI_SWITCH_QSIG) }, + { "CCNR-T2", PRI_TIMER_QSIG_CCNR_T2, PRI_BIT(PRI_SWITCH_QSIG) }, + { "CC-T3", PRI_TIMER_QSIG_CC_T3, PRI_BIT(PRI_SWITCH_QSIG) }, +#if defined(QSIG_PATH_RESERVATION_SUPPORT) + { "CC-T4", PRI_TIMER_QSIG_CC_T4, PRI_BIT(PRI_SWITCH_QSIG) }, +#endif /* defined(QSIG_PATH_RESERVATION_SUPPORT) */ /* *INDENT-ON* */ }; @@ -166,6 +186,33 @@ static void pri_default_timers(struct pri *ctrl, int switchtype) ctrl->timers[PRI_TIMER_T_RESPONSE] = 4 * 1000; /* Maximum time to wait for a typical APDU response. */ + /* ETSI timers */ + ctrl->timers[PRI_TIMER_T_STATUS] = 4 * 1000; /* Max time to wait for all replies to check for compatible terminals */ + ctrl->timers[PRI_TIMER_T_ACTIVATE] = 10 * 1000; /* Request supervision timeout. */ + ctrl->timers[PRI_TIMER_T_DEACTIVATE] = 4 * 1000;/* Deactivate supervision timeout. */ + ctrl->timers[PRI_TIMER_T_INTERROGATE] = 4 * 1000;/* Interrogation supervision timeout. */ + + /* ETSI call-completion timers */ + ctrl->timers[PRI_TIMER_T_RETENTION] = 30 * 1000;/* Max time to wait for user A to activate call-completion. */ + ctrl->timers[PRI_TIMER_T_CCBS1] = 4 * 1000; /* T-STATUS timer equivalent for CC user A status. */ + ctrl->timers[PRI_TIMER_T_CCBS2] = 45 * 60 * 1000;/* Max time the CCBS service will be active */ + ctrl->timers[PRI_TIMER_T_CCBS3] = 20 * 1000; /* Max time to wait for user A to respond to user B availability. */ + ctrl->timers[PRI_TIMER_T_CCBS4] = 5 * 1000; /* CC user B guard time before sending CC recall indication. */ + ctrl->timers[PRI_TIMER_T_CCBS5] = 60 * 60 * 1000;/* Network B CCBS supervision timeout. */ + ctrl->timers[PRI_TIMER_T_CCBS6] = 60 * 60 * 1000;/* Network A CCBS supervision timeout. */ + ctrl->timers[PRI_TIMER_T_CCNR2] = 180 * 60 * 1000;/* Max time the CCNR service will be active */ + ctrl->timers[PRI_TIMER_T_CCNR5] = 195 * 60 * 1000;/* Network B CCNR supervision timeout. */ + ctrl->timers[PRI_TIMER_T_CCNR6] = 195 * 60 * 1000;/* Network A CCNR supervision timeout. */ + + /* Q.SIG call-completion timers */ + ctrl->timers[PRI_TIMER_QSIG_CC_T1] = 30 * 1000;/* CC request supervision timeout. */ + ctrl->timers[PRI_TIMER_QSIG_CCBS_T2] = 60 * 60 * 1000;/* CCBS supervision timeout. */ + ctrl->timers[PRI_TIMER_QSIG_CCNR_T2] = 195 * 60 * 1000;/* CCNR supervision timeout. */ + ctrl->timers[PRI_TIMER_QSIG_CC_T3] = 30 * 1000;/* Max time to wait for user A to respond to user B availability. */ +#if defined(QSIG_PATH_RESERVATION_SUPPORT) + ctrl->timers[PRI_TIMER_QSIG_CC_T4] = 40 * 1000;/* Path reservation supervision timeout. */ +#endif /* defined(QSIG_PATH_RESERVATION_SUPPORT) */ + /* Set any switch specific override default values */ switch (switchtype) { default: @@ -246,6 +293,7 @@ void __pri_free_tei(struct pri * p) pri_call_apdu_queue_cleanup(call); } free(p->msg_line); + free(p->sched.timer); free(p); } } @@ -903,6 +951,52 @@ int pri_redirecting_update(struct pri *ctrl, q931_call *call, const struct pri_p return 0; } +#if defined(STATUS_REQUEST_PLACE_HOLDER) +/*! + * \brief Poll/ping for the status of any "called" party. + * + * \param ctrl D channel controller. + * \param request_id The upper layer's ID number to match with the response in case + * there are several requests at the same time. + * \param req Setup request for "called" party to determine the status. + * + * \note + * There could be one or more PRI_SUBCMD_STATUS_REQ_RSP to the status request + * depending upon how many endpoints respond to the request. + * (This includes the timeout termination response.) + * \note + * Could be used to poll for the status of call-completion party B. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int pri_status_req(struct pri *ctrl, int request_id, const struct pri_sr *req) +{ + return -1; +} +#endif /* defined(STATUS_REQUEST_PLACE_HOLDER) */ + +#if defined(STATUS_REQUEST_PLACE_HOLDER) +/*! + * \brief Response to a poll/ping request for status of any "called" party by libpri. + * + * \param ctrl D channel controller. + * \param invoke_id ID given by libpri when it requested the party status. + * \param status free(0)/busy(1)/incompatible(2) + * + * \note + * There could be zero, one, or more responses to the original + * status request depending upon how many endpoints respond to the request. + * \note + * Could be used to poll for the status of call-completion party B. + * + * \return Nothing + */ +void pri_status_req_rsp(struct pri *ctrl, int invoke_id, int status) +{ +} +#endif /* defined(STATUS_REQUEST_PLACE_HOLDER) */ + #if 0 /* deprecated routines, use pri_hangup */ int pri_release(struct pri *pri, q931_call *call, int cause) @@ -930,7 +1024,7 @@ int pri_channel_bridge(q931_call *call1, q931_call *call2) return -1; /* Check for bearer capability */ - if (call1->transcapability != call2->transcapability) + if (call1->bc.transcapability != call2->bc.transcapability) return -1; /* Check to see if we're on the same PRI */ @@ -1040,7 +1134,7 @@ void pri_dump_event(struct pri *pri, pri_event *e) } } -static void pri_sr_init(struct pri_sr *req) +void pri_sr_init(struct pri_sr *req) { memset(req, 0, sizeof(struct pri_sr)); q931_party_redirecting_init(&req->redirecting); @@ -1618,3 +1712,35 @@ int pri_reroute_call(struct pri *ctrl, q931_call *call, const struct pri_party_i return send_reroute_request(ctrl, call, caller_id, &reroute, subscription_option); } + +void pri_cc_enable(struct pri *ctrl, int enable) +{ + if (ctrl) { + ctrl = PRI_MASTER(ctrl); + ctrl->cc_support = enable ? 1 : 0; + } +} + +void pri_cc_recall_mode(struct pri *ctrl, int mode) +{ + if (ctrl) { + ctrl = PRI_MASTER(ctrl); + ctrl->cc.option.recall_mode = mode ? 1 : 0; + } +} + +void pri_cc_retain_signaling_req(struct pri *ctrl, int signaling_retention) +{ + if (ctrl && 0 <= signaling_retention && signaling_retention < 3) { + ctrl = PRI_MASTER(ctrl); + ctrl->cc.option.signaling_retention_req = signaling_retention; + } +} + +void pri_cc_retain_signaling_rsp(struct pri *ctrl, int signaling_retention) +{ + if (ctrl) { + ctrl = PRI_MASTER(ctrl); + ctrl->cc.option.signaling_retention_rsp = signaling_retention ? 1 : 0; + } +} diff --git a/pri_cc.c b/pri_cc.c new file mode 100644 index 0000000..93089b0 --- /dev/null +++ b/pri_cc.c @@ -0,0 +1,7872 @@ +/* + * libpri: An implementation of Primary Rate ISDN + * + * Copyright (C) 2009 Digium, Inc. + * + * Richard Mudgett + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2 as published by the + * Free Software Foundation. See the LICENSE file included with + * this program for more details. + * + * In addition, when this program is distributed with Asterisk in + * any form that would qualify as a 'combined work' or as a + * 'derivative work' (but not mere aggregation), you can redistribute + * and/or modify the combination under the terms of the license + * provided with that copy of Asterisk, instead of the license + * terms granted here. + */ + +/*! + * \file + * \brief Call Completion controller + * + * \author Richard Mudgett + */ + + +#include "compat.h" +#include "libpri.h" +#include "pri_internal.h" +#include "pri_facility.h" + +#include + +/* Define CC_SANITY_CHECKS to add some consistency sanity checking. */ +//#define CC_SANITY_CHECKS 1 +#define CC_SANITY_CHECKS 1// BUGBUG + +/*! Maximum times CCBSStatusRequest can have no response before canceling CC. */ +#define RAW_STATUS_COUNT_MAX 3 + +/* ------------------------------------------------------------------- */ + +/*! + * \brief Find a cc_record by the PTMP reference_id. + * + * \param ctrl D channel controller. + * \param reference_id CCBS reference ID to look for in cc_record pool. + * + * \retval cc_record on success. + * \retval NULL on error. + */ +struct pri_cc_record *pri_cc_find_by_reference(struct pri *ctrl, unsigned reference_id) +{ + struct pri_cc_record *cc_record; + + ctrl = PRI_MASTER(ctrl); + for (cc_record = ctrl->cc.pool; cc_record; cc_record = cc_record->next) { + if (cc_record->ccbs_reference_id == reference_id) { + /* Found the record */ + break; + } + } + + return cc_record; +} + +/*! + * \brief Find a cc_record by the PTMP linkage_id. + * + * \param ctrl D channel controller. + * \param linkage_id Call linkage ID to look for in cc_record pool. + * + * \retval cc_record on success. + * \retval NULL on error. + */ +struct pri_cc_record *pri_cc_find_by_linkage(struct pri *ctrl, unsigned linkage_id) +{ + struct pri_cc_record *cc_record; + + ctrl = PRI_MASTER(ctrl); + for (cc_record = ctrl->cc.pool; cc_record; cc_record = cc_record->next) { + if (cc_record->call_linkage_id == linkage_id) { + /* Found the record */ + break; + } + } + + return cc_record; +} + +/*! + * \internal + * \brief Find a cc_record by the cc_id. + * + * \param ctrl D channel controller. + * \param cc_id ID to look for in cc_record pool. + * + * \retval cc_record on success. + * \retval NULL on error. + */ +static struct pri_cc_record *pri_cc_find_by_id(struct pri *ctrl, long cc_id) +{ + struct pri_cc_record *cc_record; + + ctrl = PRI_MASTER(ctrl); + for (cc_record = ctrl->cc.pool; cc_record; cc_record = cc_record->next) { + if (cc_record->record_id == cc_id) { + /* Found the record */ + break; + } + } + + return cc_record; +} + +/*! + * \internal + * \brief Find the given ie_type in the string of q931 ies. + * + * \param ie_type Q.931 ie type to find in q931_ies. + * \param length Length of the given q931_ies + * \param q931_ies Given q931_ies + * + * \retval found-ie on success. + * \retval NULL on error. + */ +static const struct q931_ie *pri_cc_find_ie(unsigned ie_type, unsigned length, const unsigned char *q931_ies) +{ + const unsigned char *pos; + const unsigned char *end; + const unsigned char *next; + const struct q931_ie *cur; + + end = q931_ies + length; + for (pos = q931_ies; pos < end; pos = next) { + cur = (const struct q931_ie *) pos; + if (cur->ie & 0x80) { + /* Single octet ie. */ + next = pos + 1; + } else { + /* Variable length ie. */ + next = cur->data + cur->len; + } + if (cur->ie == ie_type && next <= end) { + /* Found the ie and it is within the given q931_ies body. */ + return cur; + } + } + return NULL; +} + +/*! + * \internal + * \brief Compare the specified ie type in the CC record q931_ies to the given q931_ies. + * + * \details + * The individual q931 ie is compared with memcmp(). + * + * \param ie_type Q.931 ie type to compare. + * \param record_ies CC record q931_ies + * \param length Length of the given q931_ies + * \param q931_ies Given q931_ies + * + * \retval == 0 when record_ies == q931_ies. + * \retval != 0 when record_ies != q931_ies. + */ +static int pri_cc_cmp_ie(unsigned ie_type, const struct q931_saved_ie_contents *record_ies, unsigned length, const unsigned char *q931_ies) +{ + const struct q931_ie *left; + const struct q931_ie *right; + + left = pri_cc_find_ie(ie_type, record_ies->length, record_ies->data); + right = pri_cc_find_ie(ie_type, length, q931_ies); + + if (!left && !right) { + /* Neither has the requested ie to compare so they match. */ + return 0; + } + if (!left || !right) { + /* One or the other does not have the requested ie to compare. */ + return 1; + } + if (left->len != right->len) { + /* They are not the same length. */ + return 1; + } + return memcmp(left->data, right->data, right->len); +} + +/*! + * \internal + * \brief Compare the CC record q931_ies to the given q931_ies. + * + * \note + * Only the first BC, HLC, and LLC ies in the given q931_ies are compared. + * + * \param record_ies CC record q931_ies + * \param length Length of the given q931_ies + * \param q931_ies Given q931_ies + * + * \retval == 0 when record_ies == q931_ies. + * \retval != 0 when record_ies != q931_ies. + */ +static int pri_cc_cmp_q931_ies(const struct q931_saved_ie_contents *record_ies, unsigned length, const unsigned char *q931_ies) +{ + return pri_cc_cmp_ie(Q931_BEARER_CAPABILITY, record_ies, length, q931_ies) + || pri_cc_cmp_ie(Q931_HIGH_LAYER_COMPAT, record_ies, length, q931_ies) + || pri_cc_cmp_ie(Q931_LOW_LAYER_COMPAT, record_ies, length, q931_ies); +} + +/*! + * \brief Find a cc_record by an incoming call addressing data. + * + * \param ctrl D channel controller. + * \param party_a Party A address. + * \param party_b Party B address. + * \param length Length of the given q931_ies. + * \param q931_ies BC, HLC, LLC ies to compare with CC records. + * + * \retval cc_record on success. + * \retval NULL on error. + */ +struct pri_cc_record *pri_cc_find_by_addressing(struct pri *ctrl, const struct q931_party_address *party_a, const struct q931_party_address *party_b, unsigned length, const unsigned char *q931_ies) +{ + struct pri_cc_record *cc_record; + struct q931_party_address addr_a; + struct q931_party_address addr_b; + + ctrl = PRI_MASTER(ctrl); + addr_a = *party_a; + addr_b = *party_b; + for (cc_record = ctrl->cc.pool; cc_record; cc_record = cc_record->next) { + /* Do not compare the number presentation. */ + addr_a.number.presentation = cc_record->party_a.number.presentation; + addr_b.number.presentation = cc_record->party_b.number.presentation; + if (!q931_cmp_party_id_to_address(&cc_record->party_a, &addr_a) + && !q931_party_address_cmp(&cc_record->party_b, &addr_b) + && !pri_cc_cmp_q931_ies(&cc_record->saved_ie_contents, length, q931_ies)) { + /* Found the record */ + break; + } + } + + return cc_record; +} + +/*! + * \internal + * \brief Allocate a new cc_record reference id. + * + * \param ctrl D channel controller. + * + * \retval reference_id on success. + * \retval CC_PTMP_INVALID_ID on error. + */ +static int pri_cc_new_reference_id(struct pri *ctrl) +{ + long reference_id; + long first_id; + + ctrl = PRI_MASTER(ctrl); + ctrl->cc.last_reference_id = (ctrl->cc.last_reference_id + 1) & 0x7F; + reference_id = ctrl->cc.last_reference_id; + first_id = reference_id; + while (pri_cc_find_by_reference(ctrl, reference_id)) { + ctrl->cc.last_reference_id = (ctrl->cc.last_reference_id + 1) & 0x7F; + reference_id = ctrl->cc.last_reference_id; + if (reference_id == first_id) { + /* We probably have a resource leak. */ + pri_error(ctrl, "PTMP call completion reference id exhaustion!\n"); + reference_id = CC_PTMP_INVALID_ID; + break; + } + } + + return reference_id; +} + +/*! + * \internal + * \brief Allocate a new cc_record linkage id. + * + * \param ctrl D channel controller. + * + * \retval linkage_id on success. + * \retval CC_PTMP_INVALID_ID on error. + */ +static int pri_cc_new_linkage_id(struct pri *ctrl) +{ + long linkage_id; + long first_id; + + ctrl = PRI_MASTER(ctrl); + ctrl->cc.last_linkage_id = (ctrl->cc.last_linkage_id + 1) & 0x7F; + linkage_id = ctrl->cc.last_linkage_id; + first_id = linkage_id; + while (pri_cc_find_by_linkage(ctrl, linkage_id)) { + ctrl->cc.last_linkage_id = (ctrl->cc.last_linkage_id + 1) & 0x7F; + linkage_id = ctrl->cc.last_linkage_id; + if (linkage_id == first_id) { + /* We probably have a resource leak. */ + pri_error(ctrl, "PTMP call completion linkage id exhaustion!\n"); + linkage_id = CC_PTMP_INVALID_ID; + break; + } + } + + return linkage_id; +} + +/*! + * \internal + * \brief Allocate a new cc_record id. + * + * \param ctrl D channel controller. + * + * \retval cc_id on success. + * \retval -1 on error. + */ +static long pri_cc_new_id(struct pri *ctrl) +{ + long record_id; + long first_id; + + ctrl = PRI_MASTER(ctrl); + record_id = ++ctrl->cc.last_record_id; + first_id = record_id; + while (pri_cc_find_by_id(ctrl, record_id)) { + record_id = ++ctrl->cc.last_record_id; + if (record_id == first_id) { + /* + * We have a resource leak. + * We should never need to allocate 64k records on a D channel. + */ + pri_error(ctrl, "Too many call completion records!\n"); + record_id = -1; + break; + } + } + + return record_id; +} + +/*! + * \internal + * \brief Disassociate the signaling link call from the cc_record. + * + * \param cc_record CC record to disassociate from the signaling link call. + * + * \return Nothing + */ +static void pri_cc_disassociate_signaling_link(struct pri_cc_record *cc_record) +{ + if (cc_record->signaling) { + cc_record->signaling->cc.record = NULL; + cc_record->signaling = NULL; + } +} + +/*! + * \internal + * \brief Delete the given call completion record + * + * \param ctrl D channel controller. + * \param doomed Call completion record to destroy + * + * \return Nothing + */ +static void pri_cc_delete_record(struct pri *ctrl, struct pri_cc_record *doomed) +{ + struct pri_cc_record **prev; + struct pri_cc_record *current; + + /* Unlink CC signaling link associations. */ + if (doomed->original_call) { + doomed->original_call->cc.record = NULL; + doomed->original_call = NULL; + } + pri_cc_disassociate_signaling_link(doomed); + + ctrl = PRI_MASTER(ctrl); + for (prev = &ctrl->cc.pool, current = ctrl->cc.pool; current; + prev = ¤t->next, current = current->next) { + if (current == doomed) { + *prev = current->next; + free(doomed); + return; + } + } + + /* The doomed node is not in the call completion database */ +} + +/*! + * \brief Allocate a new cc_record. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * + * \retval pointer to new call completion record + * \retval NULL if failed + */ +struct pri_cc_record *pri_cc_new_record(struct pri *ctrl, q931_call *call) +{ + struct pri_cc_record *cc_record; + long record_id; + + ctrl = PRI_MASTER(ctrl); + record_id = pri_cc_new_id(ctrl); + if (record_id < 0) { + return NULL; + } + cc_record = calloc(1, sizeof(*cc_record)); + if (!cc_record) { + return NULL; + } + + /* Initialize the new record */ + cc_record->master = ctrl; + cc_record->record_id = record_id; + cc_record->call_linkage_id = CC_PTMP_INVALID_ID;/* So it will never be found this way */ + cc_record->ccbs_reference_id = CC_PTMP_INVALID_ID;/* So it will never be found this way */ + cc_record->party_a = call->cc.party_a; + cc_record->party_b = call->called; + cc_record->saved_ie_contents = call->cc.saved_ie_contents; + cc_record->bc = call->bc; + cc_record->option.recall_mode = ctrl->cc.option.recall_mode; + + /* + * Append the new record to the end of the list so they are in + * cronological order for interrogations. + */ + if (ctrl->cc.pool) { + struct pri_cc_record *cur; + + for (cur = ctrl->cc.pool; cur->next; cur = cur->next) { + } + cur->next = cc_record; + } else { + ctrl->cc.pool = cc_record; + } + + return cc_record; +} + +/*! + * \internal + * \brief Encode ETSI PTP call completion event operation message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param operation PTP call completion event operation to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_ptp_cc_operation(struct pri *ctrl, unsigned char *pos, + unsigned char *end, enum rose_operation operation) +{ + struct rose_msg_invoke msg; + + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.invoke_id = get_invokeid(ctrl); + msg.operation = operation; + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode ETSI PTMP call completion available message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param cc_record Call completion record to process event. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_ptmp_cc_available(struct pri *ctrl, unsigned char *pos, + unsigned char *end, struct pri_cc_record *cc_record) +{ + struct rose_msg_invoke msg; + + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.invoke_id = get_invokeid(ctrl); + msg.operation = ROSE_ETSI_CallInfoRetain; + + msg.args.etsi.CallInfoRetain.call_linkage_id = cc_record->call_linkage_id; + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode and queue a cc-available message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode call completion available. + * \param cc_record Call completion record to process event. + * \param msgtype Q.931 message type to put facility ie in. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rose_cc_available_encode(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, int msgtype) +{ + unsigned char buffer[256]; + unsigned char *end; + + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + if (PTMP_MODE(ctrl)) { + end = + enc_etsi_ptmp_cc_available(ctrl, buffer, buffer + sizeof(buffer), + cc_record); + } else { + end = + enc_etsi_ptp_cc_operation(ctrl, buffer, buffer + sizeof(buffer), + ROSE_ETSI_CCBS_T_Available); + } + break; + case PRI_SWITCH_QSIG: + /* Q.SIG does not have a cc-available type message. */ + return 0; + default: + return -1; + } + if (!end) { + return -1; + } + + return pri_call_apdu_queue(call, msgtype, buffer, end - buffer, NULL); +} + +/*! + * \internal + * \brief Encode ETSI PTMP EraseCallLinkageID message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param cc_record Call completion record to process event. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_ptmp_erase_call_linkage(struct pri *ctrl, + unsigned char *pos, unsigned char *end, struct pri_cc_record *cc_record) +{ + struct rose_msg_invoke msg; + + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.invoke_id = get_invokeid(ctrl); + msg.operation = ROSE_ETSI_EraseCallLinkageID; + + msg.args.etsi.EraseCallLinkageID.call_linkage_id = cc_record->call_linkage_id; + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode and queue an EraseCallLinkageID message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode EraseCallLinkageID. + * \param cc_record Call completion record to process event. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rose_erase_call_linkage_encode(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record) +{ + unsigned char buffer[256]; + unsigned char *end; + + end = + enc_etsi_ptmp_erase_call_linkage(ctrl, buffer, buffer + sizeof(buffer), + cc_record); + if (!end) { + return -1; + } + + return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL); +} + +/*! + * \internal + * \brief Encode and send an EraseCallLinkageID message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode EraseCallLinkageID. + * \param cc_record Call completion record to process event. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int send_erase_call_linkage_id(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record) +{ + if (rose_erase_call_linkage_encode(ctrl, call, cc_record) + || q931_facility(ctrl, call)) { + pri_message(ctrl, + "Could not schedule facility message for EraseCallLinkageID.\n"); + return -1; + } + + return 0; +} + +/*! + * \internal + * \brief Encode ETSI PTMP CCBSErase message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param cc_record Call completion record to process event. + * \param reason CCBS Erase reason + * normal-unspecified(0), t-CCBS2-timeout(1), t-CCBS3-timeout(2), basic-call-failed(3) + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_ptmp_ccbs_erase(struct pri *ctrl, + unsigned char *pos, unsigned char *end, struct pri_cc_record *cc_record, int reason) +{ + struct rose_msg_invoke msg; + + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.invoke_id = get_invokeid(ctrl); + msg.operation = ROSE_ETSI_CCBSErase; + + if (cc_record->saved_ie_contents.length + <= sizeof(msg.args.etsi.CCBSErase.q931ie_contents)) { + /* Saved BC, HLC, and LLC from initial SETUP */ + msg.args.etsi.CCBSErase.q931ie.length = cc_record->saved_ie_contents.length; + memcpy(msg.args.etsi.CCBSErase.q931ie.contents, cc_record->saved_ie_contents.data, + cc_record->saved_ie_contents.length); + } else { + pri_error(ctrl, "CCBSErase q931 ie contents did not fit.\n"); + } + + q931_copy_address_to_rose(ctrl, &msg.args.etsi.CCBSErase.address_of_b, + &cc_record->party_b); + msg.args.etsi.CCBSErase.recall_mode = cc_record->option.recall_mode; + msg.args.etsi.CCBSErase.ccbs_reference = cc_record->ccbs_reference_id; + msg.args.etsi.CCBSErase.reason = reason; + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode and queue an CCBSErase message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode CCBSErase. + * \param cc_record Call completion record to process event. + * \param reason CCBS Erase reason + * normal-unspecified(0), t-CCBS2-timeout(1), t-CCBS3-timeout(2), basic-call-failed(3) + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rose_ccbs_erase_encode(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, int reason) +{ + unsigned char buffer[256]; + unsigned char *end; + + end = + enc_etsi_ptmp_ccbs_erase(ctrl, buffer, buffer + sizeof(buffer), cc_record, + reason); + if (!end) { + return -1; + } + + return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL); +} + +/*! + * \internal + * \brief Encode and send an CCBSErase message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode EraseCallLinkageID. + * \param cc_record Call completion record to process event. + * \param reason CCBS Erase reason + * normal-unspecified(0), t-CCBS2-timeout(1), t-CCBS3-timeout(2), basic-call-failed(3) + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int send_ccbs_erase(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, int reason) +{ +/* + * XXX May need to add called-party-ie with Party A number in FACILITY message. (CCBSErase) + * ETSI EN 300-195-1 Section 5.41 MSN interaction. + */ + if (rose_ccbs_erase_encode(ctrl, call, cc_record, reason) + || q931_facility(ctrl, call)) { + pri_message(ctrl, + "Could not schedule facility message for CCBSErase.\n"); + return -1; + } + + return 0; +} + +/*! + * \internal + * \brief Encode ETSI PTMP CCBSStatusRequest result message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param cc_record Call completion record to process event. + * \param is_free TRUE if the Party A status is available. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_ptmp_ccbs_status_request_rsp(struct pri *ctrl, + unsigned char *pos, unsigned char *end, struct pri_cc_record *cc_record, int is_free) +{ + struct rose_msg_result msg; + + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.invoke_id = cc_record->response.invoke_id; + msg.operation = ROSE_ETSI_CCBSStatusRequest; + + msg.args.etsi.CCBSStatusRequest.free = is_free; + + pos = rose_encode_result(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode ETSI PTMP CCBSStatusRequest message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param cc_record Call completion record to process event. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_ptmp_ccbs_status_request(struct pri *ctrl, + unsigned char *pos, unsigned char *end, struct pri_cc_record *cc_record) +{ + struct rose_msg_invoke msg; + + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.invoke_id = get_invokeid(ctrl); + msg.operation = ROSE_ETSI_CCBSStatusRequest; + + if (cc_record->saved_ie_contents.length + <= sizeof(msg.args.etsi.CCBSStatusRequest.q931ie_contents)) { + /* Saved BC, HLC, and LLC from initial SETUP */ + msg.args.etsi.CCBSStatusRequest.q931ie.length = + cc_record->saved_ie_contents.length; + memcpy(msg.args.etsi.CCBSStatusRequest.q931ie.contents, + cc_record->saved_ie_contents.data, cc_record->saved_ie_contents.length); + } else { + pri_error(ctrl, "CCBSStatusRequest q931 ie contents did not fit.\n"); + } + + msg.args.etsi.CCBSStatusRequest.recall_mode = cc_record->option.recall_mode; + msg.args.etsi.CCBSStatusRequest.ccbs_reference = cc_record->ccbs_reference_id; + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode ETSI PTMP CCBSRequest/CCNRRequest message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param cc_record Call completion record to process event. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_ptmp_cc_request(struct pri *ctrl, + unsigned char *pos, unsigned char *end, struct pri_cc_record *cc_record) +{ + struct rose_msg_invoke msg; + + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.invoke_id = get_invokeid(ctrl); + msg.operation = cc_record->is_ccnr ? ROSE_ETSI_CCNRRequest : ROSE_ETSI_CCBSRequest; + + msg.args.etsi.CCBSRequest.call_linkage_id = cc_record->call_linkage_id; + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode ETSI PTP CCBS_T_Request/CCNR_T_Request message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param cc_record Call completion record to process event. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_ptp_cc_request(struct pri *ctrl, + unsigned char *pos, unsigned char *end, struct pri_cc_record *cc_record) +{ + struct rose_msg_invoke msg; + + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.invoke_id = get_invokeid(ctrl); + msg.operation = cc_record->is_ccnr + ? ROSE_ETSI_CCNR_T_Request : ROSE_ETSI_CCBS_T_Request; + + if (cc_record->saved_ie_contents.length + <= sizeof(msg.args.etsi.CCBS_T_Request.q931ie_contents)) { + /* Saved BC, HLC, and LLC from initial SETUP */ + msg.args.etsi.CCBS_T_Request.q931ie.length = cc_record->saved_ie_contents.length; + memcpy(msg.args.etsi.CCBS_T_Request.q931ie.contents, + cc_record->saved_ie_contents.data, cc_record->saved_ie_contents.length); + } else { + pri_error(ctrl, "CCBS_T_Request q931 ie contents did not fit.\n"); + } + + q931_copy_address_to_rose(ctrl, &msg.args.etsi.CCBS_T_Request.destination, + &cc_record->party_b); + + if (cc_record->party_a.number.valid && cc_record->party_a.number.str[0]) { + q931_copy_id_address_to_rose(ctrl, &msg.args.etsi.CCBS_T_Request.originating, + &cc_record->party_a); + + msg.args.etsi.CCBS_T_Request.presentation_allowed_indicator_present = 1; + if ((cc_record->party_a.number.presentation & PRI_PRES_RESTRICTION) + == PRI_PRES_ALLOWED) { + msg.args.etsi.CCBS_T_Request.presentation_allowed_indicator = 1; + } + } + + //msg.args.etsi.CCBS_T_Request.retention_supported = 0; + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode Q.SIG ccbsRequest/ccnrRequest message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param cc_record Call completion record to process event. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_qsig_cc_request(struct pri *ctrl, + unsigned char *pos, unsigned char *end, struct pri_cc_record *cc_record) +{ + struct fac_extension_header header; + struct rose_msg_invoke msg; + + memset(&header, 0, sizeof(header)); + header.nfe_present = 1; + header.nfe.source_entity = 0; /* endPINX */ + header.nfe.destination_entity = 0; /* endPINX */ + header.interpretation_present = 1; + header.interpretation = 1; /* clearCallIfAnyInvokePduNotRecognised */ + pos = facility_encode_header(ctrl, pos, end, &header); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.invoke_id = get_invokeid(ctrl); + msg.operation = cc_record->is_ccnr + ? ROSE_QSIG_CcnrRequest : ROSE_QSIG_CcbsRequest; + + /* Fill in Party B address. */ + q931_copy_number_to_rose(ctrl, &msg.args.qsig.CcbsRequest.number_b, + &cc_record->party_b.number); + q931_copy_subaddress_to_rose(ctrl, &msg.args.qsig.CcbsRequest.subaddr_b, + &cc_record->party_b.subaddress); + + /* Fill in Party A address. */ + q931_copy_presented_number_unscreened_to_rose(ctrl, + &msg.args.qsig.CcbsRequest.number_a, &cc_record->party_a.number); + q931_copy_subaddress_to_rose(ctrl, &msg.args.qsig.CcbsRequest.subaddr_a, + &cc_record->party_a.subaddress); + + /* Fill in service Q.931 ie information. */ + if (cc_record->saved_ie_contents.length + <= sizeof(msg.args.qsig.CcbsRequest.q931ie_contents)) { + /* Saved BC, HLC, and LLC from initial SETUP */ + msg.args.qsig.CcbsRequest.q931ie.length = cc_record->saved_ie_contents.length; + memcpy(msg.args.qsig.CcbsRequest.q931ie.contents, + cc_record->saved_ie_contents.data, cc_record->saved_ie_contents.length); + } else { + pri_error(ctrl, "CcbsRequest q931 ie contents did not fit.\n"); + } + + //msg.args.qsig.CcbsRequest.can_retain_service = 0; + + switch (PRI_MASTER(ctrl)->cc.option.signaling_retention_req) { + case 0:/* Want release signaling link. */ + cc_record->option.retain_signaling_link = 0; + + msg.args.qsig.CcbsRequest.retain_sig_connection_present = 1; + msg.args.qsig.CcbsRequest.retain_sig_connection = 0; + break; + case 1:/* Demand retain signaling link. */ + cc_record->option.retain_signaling_link = 1; + + msg.args.qsig.CcbsRequest.retain_sig_connection_present = 1; + msg.args.qsig.CcbsRequest.retain_sig_connection = 1; + break; + case 2:/* Don't care about signaling link retention. */ + default: + cc_record->option.retain_signaling_link = 0; + break; + } + if (!cc_record->party_a.number.valid || cc_record->party_a.number.str[0] == '\0') { + /* + * Party A number is not available for the other end to initiate + * a signaling link to us. We must require that the signaling link + * be retained. + */ + cc_record->option.retain_signaling_link = 1; + + msg.args.qsig.CcbsRequest.retain_sig_connection_present = 1; + msg.args.qsig.CcbsRequest.retain_sig_connection = 1; + } + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode Q.SIG ccSuspend/ccResume/ccPathReserve/ccRingout message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param operation Q.SIG call completion event operation to encode. + * \param interpretation Component interpretation: + * discardAnyUnrecognisedInvokePdu(0), + * clearCallIfAnyInvokePduNotRecognised(1), + * rejectAnyUnrecognisedInvokePdu(2) + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_qsig_cc_extension_event(struct pri *ctrl, + unsigned char *pos, unsigned char *end, enum rose_operation operation, + int interpretation) +{ + struct fac_extension_header header; + struct rose_msg_invoke msg; + + memset(&header, 0, sizeof(header)); + header.nfe_present = 1; + header.nfe.source_entity = 0; /* endPINX */ + header.nfe.destination_entity = 0; /* endPINX */ + header.interpretation_present = 1; + header.interpretation = interpretation; + pos = facility_encode_header(ctrl, pos, end, &header); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.invoke_id = get_invokeid(ctrl); + msg.operation = operation; + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode ETSI PTMP CCBSDeactivate message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param cc_record Call completion record to process event. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_ptmp_cc_deactivate(struct pri *ctrl, + unsigned char *pos, unsigned char *end, struct pri_cc_record *cc_record) +{ + struct rose_msg_invoke msg; + + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.invoke_id = get_invokeid(ctrl); + msg.operation = ROSE_ETSI_CCBSDeactivate; + + msg.args.etsi.CCBSDeactivate.ccbs_reference = cc_record->ccbs_reference_id; + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode and queue an CCBSDeactivate message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode CCBSDeactivate. + * \param cc_record Call completion record to process event. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rose_cc_deactivate_encode(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record) +{ + unsigned char buffer[256]; + unsigned char *end; + + end = + enc_etsi_ptmp_cc_deactivate(ctrl, buffer, buffer + sizeof(buffer), cc_record); + if (!end) { + return -1; + } + + return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL); +} + +/*! + * \internal + * \brief Encode and send an CCBSDeactivate message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode CCBSDeactivate. + * \param cc_record Call completion record to process event. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int send_cc_deactivate_req(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record) +{ + if (rose_cc_deactivate_encode(ctrl, call, cc_record) + || q931_facility(ctrl, call)) { + pri_message(ctrl, + "Could not schedule facility message for CCBSDeactivate.\n"); + return -1; + } + + return 0; +} + +/*! + * \internal + * \brief Encode ETSI PTMP CCBSBFree message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param cc_record Call completion record to process event. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_ptmp_ccbs_b_free(struct pri *ctrl, + unsigned char *pos, unsigned char *end, struct pri_cc_record *cc_record) +{ + struct rose_msg_invoke msg; + + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.invoke_id = get_invokeid(ctrl); + msg.operation = ROSE_ETSI_CCBSBFree; + + if (cc_record->saved_ie_contents.length + <= sizeof(msg.args.etsi.CCBSBFree.q931ie_contents)) { + /* Saved BC, HLC, and LLC from initial SETUP */ + msg.args.etsi.CCBSBFree.q931ie.length = cc_record->saved_ie_contents.length; + memcpy(msg.args.etsi.CCBSBFree.q931ie.contents, cc_record->saved_ie_contents.data, + cc_record->saved_ie_contents.length); + } else { + pri_error(ctrl, "CCBSBFree q931 ie contents did not fit.\n"); + } + + q931_copy_address_to_rose(ctrl, &msg.args.etsi.CCBSBFree.address_of_b, + &cc_record->party_b); + msg.args.etsi.CCBSBFree.recall_mode = cc_record->option.recall_mode; + msg.args.etsi.CCBSBFree.ccbs_reference = cc_record->ccbs_reference_id; + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode and queue an CCBSBFree message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode CCBSBFree. + * \param cc_record Call completion record to process event. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rose_ccbs_b_free_encode(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record) +{ + unsigned char buffer[256]; + unsigned char *end; + + end = + enc_etsi_ptmp_ccbs_b_free(ctrl, buffer, buffer + sizeof(buffer), cc_record); + if (!end) { + return -1; + } + + return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL); +} + +/*! + * \internal + * \brief Encode and send an CCBSBFree message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode CCBSBFree. + * \param cc_record Call completion record to process event. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int send_ccbs_b_free(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record) +{ +/* + * XXX May need to add called-party-ie with Party A number in FACILITY message. (CCBSBFree) + * ETSI EN 300-195-1 Section 5.41 MSN interaction. + */ + if (rose_ccbs_b_free_encode(ctrl, call, cc_record) + || q931_facility(ctrl, call)) { + pri_message(ctrl, + "Could not schedule facility message for CCBSBFree.\n"); + return -1; + } + + return 0; +} + +/*! + * \internal + * \brief Encode ETSI PTMP CCBSRemoteUserFree message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param cc_record Call completion record to process event. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_ptmp_remote_user_free(struct pri *ctrl, + unsigned char *pos, unsigned char *end, struct pri_cc_record *cc_record) +{ + struct rose_msg_invoke msg; + + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.invoke_id = get_invokeid(ctrl); + msg.operation = ROSE_ETSI_CCBSRemoteUserFree; + + if (cc_record->saved_ie_contents.length + <= sizeof(msg.args.etsi.CCBSRemoteUserFree.q931ie_contents)) { + /* Saved BC, HLC, and LLC from initial SETUP */ + msg.args.etsi.CCBSRemoteUserFree.q931ie.length = + cc_record->saved_ie_contents.length; + memcpy(msg.args.etsi.CCBSRemoteUserFree.q931ie.contents, + cc_record->saved_ie_contents.data, cc_record->saved_ie_contents.length); + } else { + pri_error(ctrl, "CCBSRemoteUserFree q931 ie contents did not fit.\n"); + } + + q931_copy_address_to_rose(ctrl, &msg.args.etsi.CCBSRemoteUserFree.address_of_b, + &cc_record->party_b); + msg.args.etsi.CCBSRemoteUserFree.recall_mode = cc_record->option.recall_mode; + msg.args.etsi.CCBSRemoteUserFree.ccbs_reference = cc_record->ccbs_reference_id; + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode Q.SIG CcOptionalArg for ccCancel/ccExecPossible message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param cc_record Call completion record to process event. + * \param msgtype Q.931 message type to put facility ie in. + * \param operation library encoded operation-value + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_qsig_cc_optional_arg(struct pri *ctrl, + unsigned char *pos, unsigned char *end, struct pri_cc_record *cc_record, int msgtype, + enum rose_operation operation) +{ + struct fac_extension_header header; + struct rose_msg_invoke msg; + + memset(&header, 0, sizeof(header)); + header.nfe_present = 1; + header.nfe.source_entity = 0; /* endPINX */ + header.nfe.destination_entity = 0; /* endPINX */ + header.interpretation_present = 1; + header.interpretation = 1; /* clearCallIfAnyInvokePduNotRecognised */ + pos = facility_encode_header(ctrl, pos, end, &header); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.invoke_id = get_invokeid(ctrl); + msg.operation = operation; + + if (cc_record && msgtype == Q931_SETUP) { + msg.args.qsig.CcCancel.full_arg_present = 1; + + /* Fill in Party A address. */ + q931_copy_number_to_rose(ctrl, &msg.args.qsig.CcCancel.number_a, + &cc_record->party_a.number); + q931_copy_subaddress_to_rose(ctrl, &msg.args.qsig.CcCancel.subaddr_a, + &cc_record->party_a.subaddress); + + /* Fill in Party B address. */ + q931_copy_number_to_rose(ctrl, &msg.args.qsig.CcCancel.number_b, + &cc_record->party_b.number); + q931_copy_subaddress_to_rose(ctrl, &msg.args.qsig.CcCancel.subaddr_b, + &cc_record->party_b.subaddress); + + /* Fill in service Q.931 ie information. */ + if (cc_record->saved_ie_contents.length + <= sizeof(msg.args.qsig.CcCancel.q931ie_contents)) { + /* Saved BC, HLC, and LLC from initial SETUP */ + msg.args.qsig.CcCancel.q931ie.length = cc_record->saved_ie_contents.length; + memcpy(msg.args.qsig.CcCancel.q931ie.contents, + cc_record->saved_ie_contents.data, cc_record->saved_ie_contents.length); + } else { + pri_error(ctrl, "CcOptionalArg q931 ie contents did not fit.\n"); + } + } + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode and queue a remote user free message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode remote user free message. + * \param cc_record Call completion record to process event. + * \param msgtype Q.931 message type to put facility ie in. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rose_remote_user_free_encode(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, int msgtype) +{ + unsigned char buffer[256]; + unsigned char *end; + + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + if (PTMP_MODE(ctrl)) { + end = + enc_etsi_ptmp_remote_user_free(ctrl, buffer, buffer + sizeof(buffer), + cc_record); + } else { + end = + enc_etsi_ptp_cc_operation(ctrl, buffer, buffer + sizeof(buffer), + ROSE_ETSI_CCBS_T_RemoteUserFree); + } + break; + case PRI_SWITCH_QSIG: + end = enc_qsig_cc_optional_arg(ctrl, buffer, buffer + sizeof(buffer), cc_record, + msgtype, ROSE_QSIG_CcExecPossible); + break; + default: + return -1; + } + if (!end) { + return -1; + } + + return pri_call_apdu_queue(call, msgtype, buffer, end - buffer, NULL); +} + +/*! + * \internal + * \brief Encode and send a CC facility event in a SETUP message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param cc_record Call completion record to process event. + * \param encode Function to encode facility to send out in SETUP message. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int pri_cc_send_setup_encode(struct pri *ctrl, struct pri_cc_record *cc_record, + int (*encode)(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, + int msgtype)) +{ + struct pri_sr req; + q931_call *call; + + call = q931_new_call(ctrl); + if (!call) { + return -1; + } + + /* Link the new call as the signaling link. */ + cc_record->signaling = call; + call->cc.record = cc_record; + + if (encode(ctrl, call, cc_record, Q931_SETUP)) { + /* Should not happen. */ + q931_destroycall(ctrl, call); + return -1; + } + + pri_sr_init(&req); + if (cc_record->is_agent) { + q931_party_address_to_id(&req.caller, &cc_record->party_b); + q931_party_id_to_address(&req.called, &cc_record->party_a); + } else { + req.caller = cc_record->party_a; + req.called = cc_record->party_b; + } + //req.cis_auto_disconnect = 0; + req.cis_call = 1; + if (q931_setup(ctrl, call, &req)) { + /* Should not happen. */ + q931_destroycall(ctrl, call); + return -1; + } + return 0; +} + +/*! + * \internal + * \brief Encode and send an remote user free message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param cc_record Call completion record to process event. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int send_remote_user_free(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + int retval; + q931_call *call; + +/* + * XXX May need to add called-party-ie with Party A number in FACILITY message. (CCBSRemoteUserFree) + * ETSI EN 300-195-1 Section 5.41 MSN interaction. + */ + retval = -1; + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + call = cc_record->signaling; + retval = rose_remote_user_free_encode(ctrl, call, cc_record, Q931_FACILITY); + if (!retval) { + retval = q931_facility(ctrl, call); + } + break; + case PRI_SWITCH_QSIG: + /* ccExecPossible could be sent in FACILITY or SETUP. */ + call = cc_record->signaling; + if (call) { + retval = rose_remote_user_free_encode(ctrl, call, cc_record, Q931_FACILITY); + if (!retval) { + retval = q931_facility(ctrl, call); + } + } else { + retval = pri_cc_send_setup_encode(ctrl, cc_record, + rose_remote_user_free_encode); + } + break; + default: + break; + } + if (retval) { + pri_message(ctrl, "Could not schedule message for remote user free.\n"); + return -1; + } + return 0; +} + +/*! + * \internal + * \brief Encode and queue a Q.SIG ccCancel message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode remote user free message. + * \param cc_record Call completion record to process event. + * \param msgtype Q.931 message type to put facility ie in. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rose_cc_cancel(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, int msgtype) +{ + unsigned char buffer[256]; + unsigned char *end; + + end = enc_qsig_cc_optional_arg(ctrl, buffer, buffer + sizeof(buffer), cc_record, + msgtype, ROSE_QSIG_CcCancel); + if (!end) { + return -1; + } + + return pri_call_apdu_queue(call, msgtype, buffer, end - buffer, NULL); +} + +/*! + * \internal + * \brief Encode and send a Q.SIG ccCancel message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param cc_record Call completion record to process event. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int send_cc_cancel(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + int retval; + q931_call *call; + + /* + * ccCancel could be sent in SETUP or RELEASE. + * If ccPathReserve is supported it could also be sent in DISCONNECT. + */ + retval = -1; + call = cc_record->signaling; + if (call) { + retval = rose_cc_cancel(ctrl, call, cc_record, Q931_ANY_MESSAGE); + if (!retval) { + retval = pri_hangup(ctrl, call, -1); + } + } else { + retval = pri_cc_send_setup_encode(ctrl, cc_record, rose_cc_cancel); + } + if (retval) { + pri_message(ctrl, "Could not schedule message for ccCancel.\n"); + return -1; + } + return 0; +} + +/*! + * \internal + * \brief Encode and queue a CC suspend message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode CC suspend message. + * \param msgtype Q.931 message type to put facility ie in. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rose_cc_suspend_encode(struct pri *ctrl, q931_call *call, int msgtype) +{ + unsigned char buffer[256]; + unsigned char *end; + + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + end = + enc_etsi_ptp_cc_operation(ctrl, buffer, buffer + sizeof(buffer), + ROSE_ETSI_CCBS_T_Suspend); + break; + case PRI_SWITCH_QSIG: + end = + enc_qsig_cc_extension_event(ctrl, buffer, buffer + sizeof(buffer), + ROSE_QSIG_CcSuspend, 0/* discardAnyUnrecognisedInvokePdu */); + break; + default: + return -1; + } + if (!end) { + return -1; + } + + return pri_call_apdu_queue(call, msgtype, buffer, end - buffer, NULL); +} + +/*! + * \internal + * \brief Encode and send a CC suspend message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param cc_record Call completion record to process event. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int send_cc_suspend(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + int retval; + q931_call *call; + + retval = -1; + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + call = cc_record->signaling; + retval = rose_cc_suspend_encode(ctrl, call, Q931_FACILITY); + if (!retval) { + retval = q931_facility(ctrl, call); + } + break; + case PRI_SWITCH_QSIG: + /* + * Suspend is sent in a CONNECT or FACILITY message. + * If ccPathReserve is supported, it could also be sent in + * RELEASE or DISCONNECT. + */ + call = cc_record->signaling; + if (!call) { + break; + } + retval = rose_cc_suspend_encode(ctrl, call, Q931_ANY_MESSAGE); + if (!retval) { + if (call->ourcallstate == Q931_CALL_STATE_ACTIVE) { + retval = q931_facility(ctrl, call); + } else { + retval = q931_connect(ctrl, call, 0, 0); + } + } + break; + default: + break; + } + if (retval) { + pri_message(ctrl, "Could not schedule message for CC suspend.\n"); + return -1; + } + return 0; +} + +/*! + * \internal + * \brief Encode and queue a CC resume message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode CC resume message. + * \param msgtype Q.931 message type to put facility ie in. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rose_cc_resume_encode(struct pri *ctrl, q931_call *call, int msgtype) +{ + unsigned char buffer[256]; + unsigned char *end; + + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + end = + enc_etsi_ptp_cc_operation(ctrl, buffer, buffer + sizeof(buffer), + ROSE_ETSI_CCBS_T_Resume); + break; + case PRI_SWITCH_QSIG: + end = + enc_qsig_cc_extension_event(ctrl, buffer, buffer + sizeof(buffer), + ROSE_QSIG_CcResume, 0/* discardAnyUnrecognisedInvokePdu */); + break; + default: + return -1; + } + if (!end) { + return -1; + } + + return pri_call_apdu_queue(call, msgtype, buffer, end - buffer, NULL); +} + +/*! + * \internal + * \brief Encode and send a CC resume message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param cc_record Call completion record to process event. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int send_cc_resume(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + q931_call *call; + + call = cc_record->signaling; + if (!call + || rose_cc_resume_encode(ctrl, call, Q931_FACILITY) + || q931_facility(ctrl, call)) { + pri_message(ctrl, "Could not schedule message for CC resume.\n"); + return -1; + } + return 0; +} + +/*! + * \internal + * \brief Encode ETSI PTMP CCBSStopAlerting message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param cc_record Call completion record to process event. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_ptmp_ccbs_stop_alerting(struct pri *ctrl, + unsigned char *pos, unsigned char *end, struct pri_cc_record *cc_record) +{ + struct rose_msg_invoke msg; + + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.invoke_id = get_invokeid(ctrl); + msg.operation = ROSE_ETSI_CCBSStopAlerting; + + msg.args.etsi.CCBSStopAlerting.ccbs_reference = cc_record->ccbs_reference_id; + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode and queue an CCBSStopAlerting message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode CCBSStopAlerting. + * \param cc_record Call completion record to process event. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rose_ccbs_stop_alerting_encode(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record) +{ + unsigned char buffer[256]; + unsigned char *end; + + end = + enc_etsi_ptmp_ccbs_stop_alerting(ctrl, buffer, buffer + sizeof(buffer), cc_record); + if (!end) { + return -1; + } + + return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL); +} + +/*! + * \internal + * \brief Encode and send CCBSStopAlerting message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode remote user free. + * \param cc_record Call completion record to process event. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int send_ccbs_stop_alerting(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record) +{ + if (rose_ccbs_stop_alerting_encode(ctrl, call, cc_record) + || q931_facility(ctrl, call)) { + pri_message(ctrl, + "Could not schedule facility message for CCBSStopAlerting.\n"); + return -1; + } + + return 0; +} + +/*! + * \internal + * \brief Encode ETSI PTMP CCBSCall message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param cc_record Call completion record to process event. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_ptmp_cc_recall(struct pri *ctrl, unsigned char *pos, + unsigned char *end, struct pri_cc_record *cc_record) +{ + struct rose_msg_invoke msg; + + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.invoke_id = get_invokeid(ctrl); + msg.operation = ROSE_ETSI_CCBSCall; + + msg.args.etsi.CCBSCall.ccbs_reference = cc_record->ccbs_reference_id; + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode and queue a cc-recall message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode cc-recall. + * \param cc_record Call completion record to process event. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rose_cc_recall_encode(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record) +{ + unsigned char buffer[256]; + unsigned char *end; + + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + if (PTMP_MODE(ctrl)) { + end = + enc_etsi_ptmp_cc_recall(ctrl, buffer, buffer + sizeof(buffer), cc_record); + } else { + end = + enc_etsi_ptp_cc_operation(ctrl, buffer, buffer + sizeof(buffer), + ROSE_ETSI_CCBS_T_Call); + } + break; + case PRI_SWITCH_QSIG: + end = + enc_qsig_cc_extension_event(ctrl, buffer, buffer + sizeof(buffer), + ROSE_QSIG_CcRingout, 0/* discardAnyUnrecognisedInvokePdu */); + break; + default: + return -1; + } + if (!end) { + return -1; + } + + return pri_call_apdu_queue(call, Q931_SETUP, buffer, end - buffer, NULL); +} + +/*! + * \internal + * \brief Copy the cc information into the ETSI ROSE call-information record. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call_information ROSE call-information record to fill in. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void q931_copy_call_information_to_etsi_rose(struct pri *ctrl, struct roseEtsiCallInformation *call_information, const struct pri_cc_record *cc_record) +{ + q931_copy_address_to_rose(ctrl, &call_information->address_of_b, &cc_record->party_b); + + if (cc_record->saved_ie_contents.length + <= sizeof(call_information->q931ie_contents)) { + /* Saved BC, HLC, and LLC from initial SETUP */ + call_information->q931ie.length = cc_record->saved_ie_contents.length; + memcpy(call_information->q931ie.contents, cc_record->saved_ie_contents.data, + cc_record->saved_ie_contents.length); + } else { + pri_error(ctrl, "call-information q931 ie contents did not fit.\n"); + } + + call_information->ccbs_reference = cc_record->ccbs_reference_id; + + q931_copy_subaddress_to_rose(ctrl, &call_information->subaddress_of_a, + &cc_record->party_a.subaddress); +} + +/*! + * \internal + * \brief Encode ETSI PTMP specific CCBSInterrogate/CCNRInterrogate result message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param invoke Decoded ROSE invoke message contents. + * \param cc_record Call completion record to process event. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_ptmp_cc_interrogate_rsp_specific(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const struct rose_msg_invoke *invoke, + const struct pri_cc_record *cc_record) +{ + struct rose_msg_result msg; + + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.invoke_id = invoke->invoke_id; + msg.operation = invoke->operation; + + msg.args.etsi.CCBSInterrogate.recall_mode = cc_record->option.recall_mode; + msg.args.etsi.CCBSInterrogate.call_details.num_records = 1; + q931_copy_call_information_to_etsi_rose(ctrl, + &msg.args.etsi.CCBSInterrogate.call_details.list[0], cc_record); + + pos = rose_encode_result(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode ETSI PTMP general CCBSInterrogate/CCNRInterrogate result message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param invoke Decoded ROSE invoke message contents. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_ptmp_cc_interrogate_rsp_general(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const struct rose_msg_invoke *invoke) +{ + struct rose_msg_result msg; + struct q931_party_number party_a_number; + const struct pri_cc_record *cc_record; + unsigned char *new_pos; + struct pri *master; + unsigned idx; + + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.invoke_id = invoke->invoke_id; + msg.operation = invoke->operation; + + master = PRI_MASTER(ctrl); + msg.args.etsi.CCBSInterrogate.recall_mode = master->cc.option.recall_mode; + + /* Convert the given party A number. */ + q931_party_number_init(&party_a_number); + if (invoke->args.etsi.CCBSInterrogate.a_party_number.length) { + /* The party A number was given. */ + rose_copy_number_to_q931(ctrl, &party_a_number, + &invoke->args.etsi.CCBSInterrogate.a_party_number); + } + + /* Build the CallDetails list. */ + idx = 0; + for (cc_record = master->cc.pool; cc_record; cc_record = cc_record->next) { + if (cc_record->ccbs_reference_id == CC_PTMP_INVALID_ID + || (!cc_record->is_ccnr) != (invoke->operation == ROSE_ETSI_CCBSInterrogate)) { + /* + * Record does not have a reference id yet + * or is not for the requested CCBS/CCNR mode. + */ + continue; + } + if (party_a_number.valid) { + /* The party A number was given. */ + party_a_number.presentation = cc_record->party_a.number.presentation; + if (q931_party_number_cmp(&party_a_number, &cc_record->party_a.number)) { + /* Record party A does not match. */ + continue; + } + } + + /* Add call information to the CallDetails list. */ + q931_copy_call_information_to_etsi_rose(ctrl, + &msg.args.etsi.CCBSInterrogate.call_details.list[idx], cc_record); + + ++idx; + if (ARRAY_LEN(msg.args.etsi.CCBSInterrogate.call_details.list) <= idx) { + /* List is full. */ + break; + } + } + msg.args.etsi.CCBSInterrogate.call_details.num_records = idx; + + new_pos = rose_encode_result(ctrl, pos, end, &msg); + + /* Reduce the CallDetails list until it fits into the given buffer. */ + while (!new_pos && msg.args.etsi.CCBSInterrogate.call_details.num_records) { + --msg.args.etsi.CCBSInterrogate.call_details.num_records; + new_pos = rose_encode_result(ctrl, pos, end, &msg); + } + + return new_pos; +} + +/*! + * \internal + * \brief Encode and queue a specific CCBSInterrogate/CCNRInterrogate result message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode CCBSInterrogate/CCNRInterrogate response. + * \param invoke Decoded ROSE invoke message contents. + * \param cc_record Call completion record to process event. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rose_cc_interrogate_rsp_specific(struct pri *ctrl, q931_call *call, const struct rose_msg_invoke *invoke, const struct pri_cc_record *cc_record) +{ + unsigned char buffer[256]; + unsigned char *end; + + end = + enc_etsi_ptmp_cc_interrogate_rsp_specific(ctrl, buffer, buffer + sizeof(buffer), + invoke, cc_record); + if (!end) { + return -1; + } + + return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL); +} + +/*! + * \internal + * \brief Encode and queue a general CCBSInterrogate/CCNRInterrogate result message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode CCBSInterrogate/CCNRInterrogate response. + * \param invoke Decoded ROSE invoke message contents. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rose_cc_interrogate_rsp_general(struct pri *ctrl, q931_call *call, const struct rose_msg_invoke *invoke) +{ + unsigned char buffer[256]; + unsigned char *end; + + end = + enc_etsi_ptmp_cc_interrogate_rsp_general(ctrl, buffer, buffer + sizeof(buffer), + invoke); + if (!end) { + return -1; + } + + return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL); +} + +/*! + * \brief Respond to the received CCBSInterrogate/CCNRInterrogate invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which the message came. + * \param invoke Decoded ROSE invoke message contents. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int pri_cc_interrogate_rsp(struct pri *ctrl, q931_call *call, const struct rose_msg_invoke *invoke) +{ + int encode_result; + + if (!PRI_MASTER(ctrl)->cc_support) { + /* Call completion is disabled. */ + return send_facility_error(ctrl, call, invoke->invoke_id, + ROSE_ERROR_Gen_NotSubscribed); + } + + if (invoke->args.etsi.CCBSInterrogate.ccbs_reference_present) { + struct pri_cc_record *cc_record; + + /* Specific CC request interrogation. */ + cc_record = pri_cc_find_by_reference(ctrl, + invoke->args.etsi.CCBSInterrogate.ccbs_reference); + if (!cc_record + || ((!cc_record->is_ccnr) + == (invoke->operation == ROSE_ETSI_CCBSInterrogate))) { + /* Record does not exist or is not for the requested CCBS/CCNR mode. */ + return send_facility_error(ctrl, call, invoke->invoke_id, + ROSE_ERROR_CCBS_InvalidCCBSReference); + } + encode_result = rose_cc_interrogate_rsp_specific(ctrl, call, invoke, cc_record); + } else { + /* General CC request interrogation. */ + encode_result = rose_cc_interrogate_rsp_general(ctrl, call, invoke); + } + + if (encode_result || q931_facility(ctrl, call)) { + pri_message(ctrl, + "Could not schedule facility message for cc-interrogate.\n"); + return -1; + } + + return 0; +} + +/*! + * \brief Respond to the received PTMP CCBSRequest/CCNRRequest invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which the message came. + * \param invoke Decoded ROSE invoke message contents. + * + * \return Nothing + */ +void pri_cc_ptmp_request(struct pri *ctrl, q931_call *call, const struct rose_msg_invoke *invoke) +{ + struct pri_cc_record *cc_record; + + if (!PRI_MASTER(ctrl)->cc_support) { + /* Call completion is disabled. */ + send_facility_error(ctrl, call, invoke->invoke_id, + ROSE_ERROR_Gen_NotSubscribed); + return; + } + cc_record = pri_cc_find_by_linkage(ctrl, + invoke->args.etsi.CCBSRequest.call_linkage_id); + if (!cc_record) { + send_facility_error(ctrl, call, invoke->invoke_id, + ROSE_ERROR_CCBS_InvalidCallLinkageID); + return; + } + if (cc_record->state != CC_STATE_AVAILABLE) { + send_facility_error(ctrl, call, invoke->invoke_id, + ROSE_ERROR_CCBS_IsAlreadyActivated); + return; + } + cc_record->ccbs_reference_id = pri_cc_new_reference_id(ctrl); + if (cc_record->ccbs_reference_id == CC_PTMP_INVALID_ID) { + /* Could not allocate a call reference id. */ + send_facility_error(ctrl, call, invoke->invoke_id, + ROSE_ERROR_CCBS_OutgoingCCBSQueueFull); + return; + } + + /* Save off data to know how to send back any response. */ + cc_record->response.signaling = call; + cc_record->response.invoke_operation = invoke->operation; + cc_record->response.invoke_id = invoke->invoke_id; + + /* Set the requested CC mode. */ + cc_record->is_ccnr = (invoke->operation == ROSE_ETSI_CCNRRequest) ? 1 : 0; + + pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST); +} + +/*! + * \brief Respond to the received PTP CCBS_T_Request/CCNR_T_Request invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which the message came. + * \param msgtype Q.931 message type ie is in. + * \param invoke Decoded ROSE invoke message contents. + * + * \return Nothing + */ +void pri_cc_ptp_request(struct pri *ctrl, q931_call *call, int msgtype, const struct rose_msg_invoke *invoke) +{ + struct pri_cc_record *cc_record; + struct q931_party_address party_a; + struct q931_party_address party_b; + + if (msgtype != Q931_REGISTER) { + /* Ignore CC request message since it did not come in on the correct message. */ + return; + } + if (!PRI_MASTER(ctrl)->cc_support) { + /* Call completion is disabled. */ + rose_error_msg_encode(ctrl, call, Q931_ANY_MESSAGE, invoke->invoke_id, + ROSE_ERROR_Gen_NotSubscribed); + call->cc.hangup_call = 1; + return; + } + + q931_party_address_init(&party_a); + if (invoke->args.etsi.CCBS_T_Request.originating.number.length) { + /* The originating number is present. */ + rose_copy_address_to_q931(ctrl, &party_a, + &invoke->args.etsi.CCBS_T_Request.originating); + } + q931_party_address_init(&party_b); + rose_copy_address_to_q931(ctrl, &party_b, + &invoke->args.etsi.CCBS_T_Request.destination); + cc_record = pri_cc_find_by_addressing(ctrl, &party_a, &party_b, + invoke->args.etsi.CCBS_T_Request.q931ie.length, + invoke->args.etsi.CCBS_T_Request.q931ie.contents); + if (!cc_record || cc_record->state != CC_STATE_AVAILABLE) { + /* Could not find the record or already activated */ + rose_error_msg_encode(ctrl, call, Q931_ANY_MESSAGE, invoke->invoke_id, + ROSE_ERROR_CCBS_T_ShortTermDenial); + call->cc.hangup_call = 1; + return; + } + + /* + * We already have the presentationAllowedIndicator in the cc_record + * when we saved the original call information. + */ +#if 0 + if (invoke->args.etsi.CCBS_T_Request.presentation_allowed_indicator_present) { + if (invoke->args.etsi.CCBS_T_Request.presentation_allowed_indicator) { + if (party_a.number.str[0]) { + party_a.number.presentation = + PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; + } else { + party_a.number.presentation = + PRI_PRES_UNAVAILABLE | PRI_PRES_USER_NUMBER_UNSCREENED; + } + } else { + party_a.number.presentation = + PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; + } + } +#endif + + /* Link the signaling link to the cc_record. */ + call->cc.record = cc_record; + cc_record->signaling = call; + + /* Save off data to know how to send back any response. */ + //cc_record->response.signaling = call; + cc_record->response.invoke_operation = invoke->operation; + cc_record->response.invoke_id = invoke->invoke_id; + + /* Set the requested CC mode. */ + cc_record->is_ccnr = (invoke->operation == ROSE_ETSI_CCNR_T_Request) ? 1 : 0; + + /* Lets keep this signaling link around for awhile. */ + call->cis_recognized = 1; + + pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST); +} + +/*! + * \brief Respond to the received Q.SIG ccbsRequest/ccnrRequest invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which the message came. + * \param msgtype Q.931 message type ie is in. + * \param invoke Decoded ROSE invoke message contents. + * + * \return Nothing + */ +void pri_cc_qsig_request(struct pri *ctrl, q931_call *call, int msgtype, const struct rose_msg_invoke *invoke) +{ + struct pri *master; + struct pri_cc_record *cc_record; + struct q931_party_address party_a; + struct q931_party_address party_b; + + if (msgtype != Q931_SETUP) { + /* Ignore CC request message since it did not come in on the correct message. */ + return; + } + master = PRI_MASTER(ctrl); + if (!master->cc_support) { + /* Call completion is disabled. */ + rose_error_msg_encode(ctrl, call, Q931_ANY_MESSAGE, invoke->invoke_id, + ROSE_ERROR_QSIG_LongTermRejection); + call->cc.hangup_call = 1; + return; + } + + /* Extract Party A address. */ + rose_copy_presented_number_unscreened_to_q931(ctrl, &party_a.number, + &invoke->args.qsig.CcbsRequest.number_a); + q931_party_subaddress_init(&party_a.subaddress); + rose_copy_subaddress_to_q931(ctrl, &party_a.subaddress, + &invoke->args.qsig.CcbsRequest.subaddr_a); + + /* Extract Party B address. */ + q931_party_address_init(&party_b); + rose_copy_number_to_q931(ctrl, &party_b.number, + &invoke->args.qsig.CcbsRequest.number_b); + rose_copy_subaddress_to_q931(ctrl, &party_b.subaddress, + &invoke->args.qsig.CcbsRequest.subaddr_b); + + cc_record = pri_cc_find_by_addressing(master, &party_a, &party_b, + invoke->args.qsig.CcbsRequest.q931ie.length, + invoke->args.qsig.CcbsRequest.q931ie.contents); + if (!cc_record || cc_record->state != CC_STATE_AVAILABLE) { + /* Could not find the record or already activated */ + rose_error_msg_encode(ctrl, call, Q931_ANY_MESSAGE, invoke->invoke_id, + ROSE_ERROR_QSIG_ShortTermRejection); + call->cc.hangup_call = 1; + return; + } + + /* Determine negotiated signaling retention method. */ + if (invoke->args.qsig.CcbsRequest.retain_sig_connection_present) { + /* We will do what the originator desires. */ + cc_record->option.retain_signaling_link = + invoke->args.qsig.CcbsRequest.retain_sig_connection; + } else { + /* The originator does not care. Do how we are configured. */ + cc_record->option.retain_signaling_link = + master->cc.option.signaling_retention_rsp; + } + if (!cc_record->party_a.number.valid || cc_record->party_a.number.str[0] == '\0') { + /* + * Party A number is not available for us to initiate + * a signaling link. We must retain the signaling link. + */ + cc_record->option.retain_signaling_link = 1; + } + + /* Link the signaling link to the cc_record. */ + call->cc.record = cc_record; + cc_record->signaling = call; + + /* Save off data to know how to send back any response. */ + //cc_record->response.signaling = call; + cc_record->response.invoke_operation = invoke->operation; + cc_record->response.invoke_id = invoke->invoke_id; + + /* Set the requested CC mode. */ + cc_record->is_ccnr = (invoke->operation == ROSE_QSIG_CcnrRequest) ? 1 : 0; + + /* Lets keep this signaling link around for awhile. */ + call->cis_recognized = 1; + + pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST); +} + +/*! + * \brief Handle the received Q.SIG ccCancel invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which the message came. + * \param msgtype Q.931 message type ie is in. + * \param invoke Decoded ROSE invoke message contents. + * + * \return Nothing + */ +void pri_cc_qsig_cancel(struct pri *ctrl, q931_call *call, int msgtype, const struct rose_msg_invoke *invoke) +{ + struct pri_cc_record *cc_record; + struct q931_party_address party_a; + struct q931_party_address party_b; + + cc_record = call->cc.record; + if (!cc_record) { + /* The current call is not associated with the cc_record. */ + if (invoke->args.qsig.CcCancel.full_arg_present) { + /* Extract Party A address. */ + q931_party_address_init(&party_a); + rose_copy_number_to_q931(ctrl, &party_a.number, + &invoke->args.qsig.CcCancel.number_a); + rose_copy_subaddress_to_q931(ctrl, &party_a.subaddress, + &invoke->args.qsig.CcCancel.subaddr_a); + + /* Extract Party B address. */ + q931_party_address_init(&party_b); + rose_copy_number_to_q931(ctrl, &party_b.number, + &invoke->args.qsig.CcCancel.number_b); + rose_copy_subaddress_to_q931(ctrl, &party_b.subaddress, + &invoke->args.qsig.CcCancel.subaddr_b); + + cc_record = pri_cc_find_by_addressing(ctrl, &party_a, &party_b, + invoke->args.qsig.CcCancel.q931ie.length, + invoke->args.qsig.CcCancel.q931ie.contents); + } + if (!cc_record) { + /* + * Could not find the cc_record + * or not enough information to look up a cc_record. + */ + if (msgtype == Q931_SETUP) { + call->cc.hangup_call = 1; + } + return; + } + } + + if (msgtype == Q931_SETUP && call->cis_call) { + if (cc_record->signaling) { + /* + * We already have a signaling link. + * This could be a collision with our ccExecPossible. + * Could this be an alias match? + */ + switch (cc_record->state) { + case CC_STATE_WAIT_CALLBACK: + if (ctrl->debug & PRI_DEBUG_CC) { + pri_message(ctrl, + "-- Collision with our ccExecPossible event call. Canceling CC.\n"); + } + break; + default: + pri_message(ctrl, + "-- Warning: Possible Q.SIG CC alias match. Canceling CC.\n"); + break; + } + cc_record->fsm.qsig.msgtype = msgtype; + pri_cc_event(ctrl, call, cc_record, CC_EVENT_LINK_CANCEL); + + call->cc.hangup_call = 1; + return; + } + + /* Link the signaling link to the cc_record. */ + call->cc.record = cc_record; + cc_record->signaling = call; + + /* Lets keep this signaling link around for awhile. */ + call->cis_recognized = 1; + } + + cc_record->fsm.qsig.msgtype = msgtype; + pri_cc_event(ctrl, call, cc_record, CC_EVENT_LINK_CANCEL); +} + +/*! + * \brief Handle the received Q.SIG ccExecPossible invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which the message came. + * \param msgtype Q.931 message type ie is in. + * \param invoke Decoded ROSE invoke message contents. + * + * \return Nothing + */ +void pri_cc_qsig_exec_possible(struct pri *ctrl, q931_call *call, int msgtype, const struct rose_msg_invoke *invoke) +{ + struct pri_cc_record *cc_record; + struct q931_party_address party_a; + struct q931_party_address party_b; + + cc_record = call->cc.record; + if (!cc_record) { + /* The current call is not associated with the cc_record. */ + if (invoke->args.qsig.CcExecPossible.full_arg_present) { + /* Extract Party A address. */ + q931_party_address_init(&party_a); + rose_copy_number_to_q931(ctrl, &party_a.number, + &invoke->args.qsig.CcExecPossible.number_a); + rose_copy_subaddress_to_q931(ctrl, &party_a.subaddress, + &invoke->args.qsig.CcExecPossible.subaddr_a); + + /* Extract Party B address. */ + q931_party_address_init(&party_b); + rose_copy_number_to_q931(ctrl, &party_b.number, + &invoke->args.qsig.CcExecPossible.number_b); + rose_copy_subaddress_to_q931(ctrl, &party_b.subaddress, + &invoke->args.qsig.CcExecPossible.subaddr_b); + + cc_record = pri_cc_find_by_addressing(ctrl, &party_a, &party_b, + invoke->args.qsig.CcExecPossible.q931ie.length, + invoke->args.qsig.CcExecPossible.q931ie.contents); + } + if (!cc_record) { + /* + * Could not find the cc_record + * or not enough information to look up a cc_record. + */ + rose_cc_cancel(ctrl, call, NULL, Q931_ANY_MESSAGE); + if (msgtype == Q931_SETUP) { + call->cc.hangup_call = 1; + } else { + /* msgtype should be Q931_FACILITY. */ + pri_hangup(ctrl, call, -1); + } + return; + } + } + + if (msgtype == Q931_SETUP && call->cis_call) { + if (cc_record->signaling) { + /* + * We already have a signaling link. This should not happen. + * Could this be an alias match? + */ + pri_message(ctrl, + "-- Warning: Possible Q.SIG CC alias match. Sending ccCancel back.\n"); + rose_cc_cancel(ctrl, call, NULL, Q931_ANY_MESSAGE); + call->cc.hangup_call = 1; + return; + } + + /* Link the signaling link to the cc_record. */ + call->cc.record = cc_record; + cc_record->signaling = call; + + /* Lets keep this signaling link around for awhile. */ + call->cis_recognized = 1; + } + + cc_record->fsm.qsig.msgtype = msgtype; + pri_cc_event(ctrl, call, cc_record, CC_EVENT_REMOTE_USER_FREE); +} + +/*! + * \internal + * \brief Convert the given call completion state to a string. + * + * \param state CC state to convert to string. + * + * \return String version of call completion state. + */ +static const char *pri_cc_fsm_state_str(enum CC_STATES state) +{ + const char *str; + + str = "Unknown"; + switch (state) { + case CC_STATE_IDLE: + str = "CC_STATE_IDLE"; + break; + case CC_STATE_PENDING_AVAILABLE: + str = "CC_STATE_PENDING_AVAILABLE"; + break; + case CC_STATE_AVAILABLE: + str = "CC_STATE_AVAILABLE"; + break; + case CC_STATE_REQUESTED: + str = "CC_STATE_REQUESTED"; + break; + case CC_STATE_ACTIVATED: + str = "CC_STATE_ACTIVATED"; + break; + case CC_STATE_B_AVAILABLE: + str = "CC_STATE_B_AVAILABLE"; + break; + case CC_STATE_SUSPENDED: + str = "CC_STATE_SUSPENDED"; + break; + case CC_STATE_WAIT_CALLBACK: + str = "CC_STATE_WAIT_CALLBACK"; + break; + case CC_STATE_CALLBACK: + str = "CC_STATE_CALLBACK"; + break; + case CC_STATE_WAIT_DESTRUCTION: + str = "CC_STATE_WAIT_DESTRUCTION"; + break; + case CC_STATE_NUM: + /* Not a real state. */ + break; + } + return str; +} + +/*! + * \internal + * \brief Convert the given call completion event to a string. + * + * \param event CC event to convert to string. + * + * \return String version of call completion event. + */ +static const char *pri_cc_fsm_event_str(enum CC_EVENTS event) +{ + const char *str; + + str = "Unknown"; + switch (event) { + case CC_EVENT_AVAILABLE: + str = "CC_EVENT_AVAILABLE"; + break; + case CC_EVENT_CC_REQUEST: + str = "CC_EVENT_CC_REQUEST"; + break; + case CC_EVENT_CC_REQUEST_ACCEPT: + str = "CC_EVENT_CC_REQUEST_ACCEPT"; + break; + case CC_EVENT_CC_REQUEST_FAIL: + str = "CC_EVENT_CC_REQUEST_FAIL"; + break; + case CC_EVENT_REMOTE_USER_FREE: + str = "CC_EVENT_REMOTE_USER_FREE"; + break; + case CC_EVENT_B_FREE: + str = "CC_EVENT_B_FREE"; + break; + case CC_EVENT_STOP_ALERTING: + str = "CC_EVENT_STOP_ALERTING"; + break; + case CC_EVENT_A_STATUS: + str = "CC_EVENT_A_STATUS"; + break; + case CC_EVENT_A_FREE: + str = "CC_EVENT_A_FREE"; + break; + case CC_EVENT_A_BUSY: + str = "CC_EVENT_A_BUSY"; + break; + case CC_EVENT_SUSPEND: + str = "CC_EVENT_SUSPEND"; + break; + case CC_EVENT_RESUME: + str = "CC_EVENT_RESUME"; + break; + case CC_EVENT_RECALL: + str = "CC_EVENT_RECALL"; + break; + case CC_EVENT_LINK_CANCEL: + str = "CC_EVENT_LINK_CANCEL"; + break; + case CC_EVENT_CANCEL: + str = "CC_EVENT_CANCEL"; + break; + case CC_EVENT_SIGNALING_GONE: + str = "CC_EVENT_SIGNALING_GONE"; + break; + case CC_EVENT_HANGUP_SIGNALING: + str = "CC_EVENT_HANGUP_SIGNALING"; + break; + case CC_EVENT_MSG_ALERTING: + str = "CC_EVENT_MSG_ALERTING"; + break; + case CC_EVENT_MSG_DISCONNECT: + str = "CC_EVENT_MSG_DISCONNECT"; + break; + case CC_EVENT_MSG_RELEASE: + str = "CC_EVENT_MSG_RELEASE"; + break; + case CC_EVENT_MSG_RELEASE_COMPLETE: + str = "CC_EVENT_MSG_RELEASE_COMPLETE"; + break; + case CC_EVENT_TIMEOUT_T_ACTIVATE: + str = "CC_EVENT_TIMEOUT_T_ACTIVATE"; + break; +#if 0 + case CC_EVENT_TIMEOUT_T_DEACTIVATE: + str = "CC_EVENT_TIMEOUT_T_DEACTIVATE"; + break; + case CC_EVENT_TIMEOUT_T_INTERROGATE: + str = "CC_EVENT_TIMEOUT_T_INTERROGATE"; + break; +#endif + case CC_EVENT_TIMEOUT_T_RETENTION: + str = "CC_EVENT_TIMEOUT_T_RETENTION"; + break; + case CC_EVENT_TIMEOUT_T_CCBS1: + str = "CC_EVENT_TIMEOUT_T_CCBS1"; + break; + case CC_EVENT_TIMEOUT_EXTENDED_T_CCBS1: + str = "CC_EVENT_TIMEOUT_EXTENDED_T_CCBS1"; + break; + case CC_EVENT_TIMEOUT_T_SUPERVISION: + str = "CC_EVENT_TIMEOUT_T_SUPERVISION"; + break; + case CC_EVENT_TIMEOUT_T_RECALL: + str = "CC_EVENT_TIMEOUT_T_RECALL"; + break; + } + return str; +} + +static const char pri_cc_act_header[] = "%ld CC-Act: %s\n"; +#define PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_id) \ + if ((ctrl)->debug & PRI_DEBUG_CC) { \ + pri_message((ctrl), pri_cc_act_header, (cc_id), __FUNCTION__); \ + } + +/*! + * \internal + * \brief FSM action to mark FSM for destruction. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_set_self_destruct(struct pri *ctrl, struct pri_cc_record *cc_record) +{ +#if defined(CC_SANITY_CHECKS) + struct apdu_event *msg; +#endif /* defined(CC_SANITY_CHECKS) */ + + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + + /* Abort any pending indirect events. */ + pri_schedule_del(ctrl, cc_record->t_indirect); + cc_record->t_indirect = 0; + +#if defined(CC_SANITY_CHECKS) + if (cc_record->t_retention) { + pri_error(ctrl, "T_RETENTION still active"); + pri_schedule_del(ctrl, cc_record->t_retention); + cc_record->t_retention = 0; + } + if (cc_record->t_supervision) { + pri_error(ctrl, "T_SUPERVISION still active"); + pri_schedule_del(ctrl, cc_record->t_supervision); + cc_record->t_supervision = 0; + } + if (cc_record->t_recall) { + pri_error(ctrl, "T_RECALL still active"); + pri_schedule_del(ctrl, cc_record->t_recall); + cc_record->t_recall = 0; + } + if (PTMP_MODE(ctrl)) { + msg = pri_call_apdu_find(cc_record->signaling, + cc_record->fsm.ptmp.t_ccbs1_invoke_id); + if (msg) { + pri_error(ctrl, "T_CCBS1 still active"); + cc_record->fsm.ptmp.t_ccbs1_invoke_id = APDU_INVALID_INVOKE_ID; + pri_call_apdu_delete(cc_record->signaling, msg); + } + if (cc_record->fsm.ptmp.extended_t_ccbs1) { + pri_error(ctrl, "Extended T_CCBS1 still active"); + pri_schedule_del(ctrl, cc_record->fsm.ptmp.extended_t_ccbs1); + cc_record->fsm.ptmp.extended_t_ccbs1 = 0; + } + } + if (cc_record->signaling) { + msg = pri_call_apdu_find(cc_record->signaling, cc_record->t_activate_invoke_id); + if (msg) { + pri_error(ctrl, "T_ACTIVATE still active"); + cc_record->t_activate_invoke_id = APDU_INVALID_INVOKE_ID; + pri_call_apdu_delete(cc_record->signaling, msg); + } + } +#endif /* defined(CC_SANITY_CHECKS) */ + + cc_record->fsm_complete = 1; +} + +/*! + * \internal + * \brief FSM action to disassociate the signaling link from the cc_record. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_disassociate_signaling_link(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + pri_cc_disassociate_signaling_link(cc_record); +} + +/*! + * \internal + * \brief FSM action to send CC available message. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * \param call Q.931 call leg. + * \param msgtype Q.931 message type to put facility ie in. + * + * \return Nothing + */ +static void pri_cc_act_send_cc_available(struct pri *ctrl, struct pri_cc_record *cc_record, q931_call *call, int msgtype) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + rose_cc_available_encode(ctrl, call, cc_record, msgtype); +} + +/*! + * \internal + * \brief FSM action to stop the PTMP T_RETENTION timer. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_stop_t_retention(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + pri_schedule_del(ctrl, cc_record->t_retention); + cc_record->t_retention = 0; +} + +/*! + * \internal + * \brief T_RETENTION timeout callback. + * + * \param data CC record pointer. + * + * \return Nothing + */ +static void pri_cc_timeout_t_retention(void *data) +{ + struct pri_cc_record *cc_record = data; + + cc_record->t_retention = 0; + q931_cc_timeout(cc_record->master, cc_record, CC_EVENT_TIMEOUT_T_RETENTION); +} + +/*! + * \internal + * \brief FSM action to start the PTMP T_RETENTION timer. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_start_t_retention(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + if (cc_record->t_retention) { + pri_error(ctrl, "!! T_RETENTION is already running!"); + pri_schedule_del(ctrl, cc_record->t_retention); + } + cc_record->t_retention = pri_schedule_event(ctrl, + ctrl->timers[PRI_TIMER_T_RETENTION], pri_cc_timeout_t_retention, cc_record); +} + +/*! + * \internal + * \brief FSM action to stop the PTMP EXTENDED_T_CCBS1 timer. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_stop_extended_t_ccbs1(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + pri_schedule_del(ctrl, cc_record->fsm.ptmp.extended_t_ccbs1); + cc_record->fsm.ptmp.extended_t_ccbs1 = 0; +} + +/*! + * \internal + * \brief EXTENDED_T_CCBS1 timeout callback. + * + * \param data CC record pointer. + * + * \return Nothing + */ +static void pri_cc_timeout_extended_t_ccbs1(void *data) +{ + struct pri_cc_record *cc_record = data; + + cc_record->fsm.ptmp.extended_t_ccbs1 = 0; + q931_cc_timeout(cc_record->master, cc_record, CC_EVENT_TIMEOUT_EXTENDED_T_CCBS1); +} + +/*! + * \internal + * \brief FSM action to start the PTMP extended T_CCBS1 timer. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_start_extended_t_ccbs1(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + if (cc_record->fsm.ptmp.extended_t_ccbs1) { + pri_error(ctrl, "!! Extended T_CCBS1 is already running!"); + pri_schedule_del(ctrl, cc_record->fsm.ptmp.extended_t_ccbs1); + } + /* Timeout is T_CCBS1 + 2 seconds. */ + cc_record->fsm.ptmp.extended_t_ccbs1 = pri_schedule_event(ctrl, + ctrl->timers[PRI_TIMER_T_CCBS1] + 2000, pri_cc_timeout_extended_t_ccbs1, + cc_record); +} + +/*! + * \internal + * \brief FSM action to stop the T_SUPERVISION timer. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_stop_t_supervision(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + pri_schedule_del(ctrl, cc_record->t_supervision); + cc_record->t_supervision = 0; +} + +/*! + * \internal + * \brief T_SUPERVISION timeout callback. + * + * \param data CC record pointer. + * + * \return Nothing + */ +static void pri_cc_timeout_t_supervision(void *data) +{ + struct pri_cc_record *cc_record = data; + + cc_record->t_supervision = 0; + q931_cc_timeout(cc_record->master, cc_record, CC_EVENT_TIMEOUT_T_SUPERVISION); +} + +/*! + * \internal + * \brief FSM action to start the T_SUPERVISION timer. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_start_t_supervision(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + int timer_id; + int duration; + + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + if (cc_record->t_supervision) { + pri_error(ctrl, "!! A CC supervision timer is already running!"); + pri_schedule_del(ctrl, cc_record->t_supervision); + } + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + if (PTMP_MODE(ctrl)) { + /* ETSI PTMP mode. */ + timer_id = cc_record->is_ccnr ? PRI_TIMER_T_CCNR2 : PRI_TIMER_T_CCBS2; + } else if (cc_record->is_agent) { + /* ETSI PTP mode network B side. */ + timer_id = cc_record->is_ccnr ? PRI_TIMER_T_CCNR5 : PRI_TIMER_T_CCBS5; + } else { + /* ETSI PTP mode network A side. */ + timer_id = cc_record->is_ccnr ? PRI_TIMER_T_CCNR6 : PRI_TIMER_T_CCBS6; + } + duration = ctrl->timers[timer_id]; + break; + case PRI_SWITCH_QSIG: + timer_id = cc_record->is_ccnr ? PRI_TIMER_QSIG_CCNR_T2 : PRI_TIMER_QSIG_CCBS_T2; + duration = ctrl->timers[timer_id]; + break; + default: + /* Timer not defined for this switch type. Should never happen. */ + pri_error(ctrl, "!! A CC supervision timer is not defined!"); + duration = 0; + break; + } + cc_record->t_supervision = pri_schedule_event(ctrl, duration, + pri_cc_timeout_t_supervision, cc_record); +} + +/*! + * \internal + * \brief FSM action to stop the T_RECALL timer. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_stop_t_recall(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + pri_schedule_del(ctrl, cc_record->t_recall); + cc_record->t_recall = 0; +} + +/*! + * \internal + * \brief T_RECALL timeout callback. + * + * \param data CC record pointer. + * + * \return Nothing + */ +static void pri_cc_timeout_t_recall(void *data) +{ + struct pri_cc_record *cc_record = data; + + cc_record->t_recall = 0; + q931_cc_timeout(cc_record->master, cc_record, CC_EVENT_TIMEOUT_T_RECALL); +} + +/*! + * \internal + * \brief FSM action to start the T_RECALL timer. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_start_t_recall(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + int duration; + + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + if (cc_record->t_recall) { + pri_error(ctrl, "!! T_RECALL is already running!"); + pri_schedule_del(ctrl, cc_record->t_recall); + } + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + duration = ctrl->timers[PRI_TIMER_T_CCBS3]; + break; + case PRI_SWITCH_QSIG: + duration = ctrl->timers[PRI_TIMER_QSIG_CC_T3]; + break; + default: + /* Timer not defined for this switch type. Should never happen. */ + pri_error(ctrl, "!! A CC recall timer is not defined!"); + duration = 0; + break; + } + cc_record->t_recall = pri_schedule_event(ctrl, duration, pri_cc_timeout_t_recall, + cc_record); +} + +/*! + * \internal + * \brief FSM action to send the EraseCallLinkageID message. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_send_erase_call_linkage_id(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + send_erase_call_linkage_id(ctrl, cc_record->signaling, cc_record); +} + +/*! + * \internal + * \brief FSM action to send the CCBSErase message. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * \param reason CCBS Erase reason + * normal-unspecified(0), t-CCBS2-timeout(1), t-CCBS3-timeout(2), basic-call-failed(3) + * + * \return Nothing + */ +static void pri_cc_act_send_ccbs_erase(struct pri *ctrl, struct pri_cc_record *cc_record, int reason) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + send_ccbs_erase(ctrl, cc_record->signaling, cc_record, reason); +} + +/*! + * \internal + * \brief Find the T_CCBS1 timer/CCBSStatusRequest message. + * + * \param cc_record Call completion record to process event. + * + * \return Facility message pointer or NULL if not active. + */ +static struct apdu_event *pri_cc_get_t_ccbs1_status(struct pri_cc_record *cc_record) +{ + return pri_call_apdu_find(cc_record->signaling, + cc_record->fsm.ptmp.t_ccbs1_invoke_id); +} + +/*! + * \internal + * \brief FSM action to stop the PTMP T_CCBS1 timer. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_stop_t_ccbs1(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + struct apdu_event *msg; + + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + + msg = pri_call_apdu_find(cc_record->signaling, cc_record->fsm.ptmp.t_ccbs1_invoke_id); + if (msg) { + cc_record->fsm.ptmp.t_ccbs1_invoke_id = APDU_INVALID_INVOKE_ID; + pri_call_apdu_delete(cc_record->signaling, msg); + } +} + +/*! + * \internal + * \brief CCBSStatusRequest response callback function. + * + * \param reason Reason callback is called. + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param apdu APDU queued entry. Do not change! + * \param msg APDU response message data. (NULL if was not the reason called.) + * + * \return TRUE if no more responses are expected. + */ +static int pri_cc_ccbs_status_response(enum APDU_CALLBACK_REASON reason, struct pri *ctrl, struct q931_call *call, struct apdu_event *apdu, const struct apdu_msg_data *msg) +{ + struct pri_cc_record *cc_record; + + cc_record = apdu->response.user.ptr; + switch (reason) { + case APDU_CALLBACK_REASON_ERROR: + cc_record->fsm.ptmp.t_ccbs1_invoke_id = APDU_INVALID_INVOKE_ID; + break; + case APDU_CALLBACK_REASON_TIMEOUT: + cc_record->fsm.ptmp.t_ccbs1_invoke_id = APDU_INVALID_INVOKE_ID; + pri_cc_event(ctrl, call, cc_record, CC_EVENT_TIMEOUT_T_CCBS1); + break; + case APDU_CALLBACK_REASON_MSG_RESULT: + pri_cc_event(ctrl, call, cc_record, + msg->response.result->args.etsi.CCBSStatusRequest.free + ? CC_EVENT_A_FREE : CC_EVENT_A_BUSY); + break; + default: + break; + } + return 0; +} + +/*! + * \internal + * \brief Encode and queue an CCBSStatusRequest message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode CCBSStatusRequest. + * \param cc_record Call completion record to process event. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rose_ccbs_status_request(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record) +{ + unsigned char buffer[256]; + unsigned char *end; + struct apdu_callback_data response; + + end = + enc_etsi_ptmp_ccbs_status_request(ctrl, buffer, buffer + sizeof(buffer), + cc_record); + if (!end) { + return -1; + } + + memset(&response, 0, sizeof(response)); + cc_record->fsm.ptmp.t_ccbs1_invoke_id = ctrl->last_invoke; + response.invoke_id = ctrl->last_invoke; + response.timeout_time = ctrl->timers[PRI_TIMER_T_CCBS1]; + response.callback = pri_cc_ccbs_status_response; + response.user.ptr = cc_record; + return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, &response); +} + +/*! + * \internal + * \brief Encode and send an CCBSStatusRequest message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode CCBSStatusRequest. + * \param cc_record Call completion record to process event. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int send_ccbs_status_request(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record) +{ +/* + * XXX May need to add called-party-ie with Party A number in FACILITY message. (CCBSStatusRequest) + * ETSI EN 300-195-1 Section 5.41 MSN interaction. + */ + if (rose_ccbs_status_request(ctrl, call, cc_record) + || q931_facility(ctrl, call)) { + pri_message(ctrl, + "Could not schedule facility message for CCBSStatusRequest.\n"); + return -1; + } + + return 0; +} + +/*! + * \internal + * \brief FSM action to send the CCBSStatusRequest message. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_send_ccbs_status_request(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + send_ccbs_status_request(ctrl, cc_record->signaling, cc_record); +} + +/*! + * \internal + * \brief FSM action to stop the PTMP T_ACTIVATE timer. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_stop_t_activate(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + struct apdu_event *msg; + + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + + if (!cc_record->signaling) { + return; + } + msg = pri_call_apdu_find(cc_record->signaling, cc_record->t_activate_invoke_id); + if (msg) { + cc_record->t_activate_invoke_id = APDU_INVALID_INVOKE_ID; + pri_call_apdu_delete(cc_record->signaling, msg); + } +} + +/*! + * \internal + * \brief cc-request PTMP response callback function. + * + * \param reason Reason callback is called. + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param apdu APDU queued entry. Do not change! + * \param msg APDU response message data. (NULL if was not the reason called.) + * + * \return TRUE if no more responses are expected. + */ +static int pri_cc_req_response_ptmp(enum APDU_CALLBACK_REASON reason, struct pri *ctrl, struct q931_call *call, struct apdu_event *apdu, const struct apdu_msg_data *msg) +{ + struct pri_cc_record *cc_record; + + cc_record = apdu->response.user.ptr; + + switch (reason) { + case APDU_CALLBACK_REASON_ERROR: + cc_record->t_activate_invoke_id = APDU_INVALID_INVOKE_ID; + break; + case APDU_CALLBACK_REASON_TIMEOUT: + cc_record->t_activate_invoke_id = APDU_INVALID_INVOKE_ID; + pri_cc_event(ctrl, call, cc_record, CC_EVENT_TIMEOUT_T_ACTIVATE); + break; + case APDU_CALLBACK_REASON_MSG_RESULT: + /* + * Since we received this facility, we will not be allocating any + * reference and linkage id's. + */ + cc_record->ccbs_reference_id = + msg->response.result->args.etsi.CCBSRequest.ccbs_reference & 0x7F; + cc_record->option.recall_mode = + msg->response.result->args.etsi.CCBSRequest.recall_mode; + + pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST_ACCEPT); + break; + case APDU_CALLBACK_REASON_MSG_ERROR: + cc_record->msg.cc_req_rsp.reason = reason; + cc_record->msg.cc_req_rsp.code = msg->response.error->code; + + pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST_FAIL); + break; + case APDU_CALLBACK_REASON_MSG_REJECT: + cc_record->msg.cc_req_rsp.reason = reason; + cc_record->msg.cc_req_rsp.code = msg->response.reject->code; + + pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST_FAIL); + break; + default: + break; + } + + /* + * No more reponses are really expected. + * However, the FSM will be removing the apdu_event itself instead. + */ + return 0; +} + +/*! + * \internal + * \brief cc-request PTP response callback function. + * + * \param reason Reason callback is called. + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param apdu APDU queued entry. Do not change! + * \param msg APDU response message data. (NULL if was not the reason called.) + * + * \return TRUE if no more responses are expected. + */ +static int pri_cc_req_response_ptp(enum APDU_CALLBACK_REASON reason, struct pri *ctrl, struct q931_call *call, struct apdu_event *apdu, const struct apdu_msg_data *msg) +{ + struct pri_cc_record *cc_record; + + cc_record = apdu->response.user.ptr; + + switch (reason) { + case APDU_CALLBACK_REASON_ERROR: + cc_record->t_activate_invoke_id = APDU_INVALID_INVOKE_ID; + break; + case APDU_CALLBACK_REASON_TIMEOUT: + cc_record->t_activate_invoke_id = APDU_INVALID_INVOKE_ID; + pri_cc_event(ctrl, call, cc_record, CC_EVENT_TIMEOUT_T_ACTIVATE); + break; + case APDU_CALLBACK_REASON_MSG_RESULT: + pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST_ACCEPT); + break; + case APDU_CALLBACK_REASON_MSG_ERROR: + cc_record->msg.cc_req_rsp.reason = reason; + cc_record->msg.cc_req_rsp.code = msg->response.error->code; + + pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST_FAIL); + break; + case APDU_CALLBACK_REASON_MSG_REJECT: + cc_record->msg.cc_req_rsp.reason = reason; + cc_record->msg.cc_req_rsp.code = msg->response.reject->code; + + pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST_FAIL); + break; + default: + break; + } + + /* + * No more reponses are really expected. + * However, the FSM will be removing the apdu_event itself instead. + */ + return 0; +} + +/*! + * \internal + * \brief cc-request Q.SIG response callback function. + * + * \param reason Reason callback is called. + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param apdu APDU queued entry. Do not change! + * \param msg APDU response message data. (NULL if was not the reason called.) + * + * \return TRUE if no more responses are expected. + */ +static int pri_cc_req_response_qsig(enum APDU_CALLBACK_REASON reason, struct pri *ctrl, struct q931_call *call, struct apdu_event *apdu, const struct apdu_msg_data *msg) +{ + struct pri_cc_record *cc_record; + + cc_record = apdu->response.user.ptr; + + switch (reason) { + case APDU_CALLBACK_REASON_ERROR: + cc_record->t_activate_invoke_id = APDU_INVALID_INVOKE_ID; + break; + case APDU_CALLBACK_REASON_TIMEOUT: + cc_record->t_activate_invoke_id = APDU_INVALID_INVOKE_ID; + pri_cc_event(ctrl, call, cc_record, CC_EVENT_TIMEOUT_T_ACTIVATE); + break; + case APDU_CALLBACK_REASON_MSG_RESULT: + if (!cc_record->option.retain_signaling_link) { + /* We were ambivalent about the signaling link retention option. */ + if (msg->type == Q931_CONNECT) { + /* The far end elected to retain the signaling link. */ + cc_record->option.retain_signaling_link = 1; + } + } + + cc_record->fsm.qsig.msgtype = msg->type; + pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST_ACCEPT); + break; + case APDU_CALLBACK_REASON_MSG_ERROR: + cc_record->msg.cc_req_rsp.reason = reason; + cc_record->msg.cc_req_rsp.code = msg->response.error->code; + + cc_record->fsm.qsig.msgtype = msg->type; + pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST_FAIL); + break; + case APDU_CALLBACK_REASON_MSG_REJECT: + cc_record->msg.cc_req_rsp.reason = reason; + cc_record->msg.cc_req_rsp.code = msg->response.reject->code; + + cc_record->fsm.qsig.msgtype = msg->type; + pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST_FAIL); + break; + default: + break; + } + + /* + * No more reponses are really expected. + * However, the FSM will be removing the apdu_event itself instead. + */ + return 0; +} + +/*! + * \internal + * \brief Encode and queue a cc-request message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode cc-request. + * \param cc_record Call completion record to process event. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rose_cc_request(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record) +{ + unsigned char buffer[256]; + unsigned char *end; + struct apdu_callback_data response; + int msgtype; + + memset(&response, 0, sizeof(response)); + + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + if (PTMP_MODE(ctrl)) { + end = + enc_etsi_ptmp_cc_request(ctrl, buffer, buffer + sizeof(buffer), + cc_record); + msgtype = Q931_FACILITY; + response.callback = pri_cc_req_response_ptmp; + } else { + end = + enc_etsi_ptp_cc_request(ctrl, buffer, buffer + sizeof(buffer), cc_record); + msgtype = Q931_REGISTER; + response.callback = pri_cc_req_response_ptp; + } + response.timeout_time = ctrl->timers[PRI_TIMER_T_ACTIVATE]; + break; + case PRI_SWITCH_QSIG: + end = enc_qsig_cc_request(ctrl, buffer, buffer + sizeof(buffer), cc_record); + msgtype = Q931_SETUP; + response.callback = pri_cc_req_response_qsig; + response.timeout_time = ctrl->timers[PRI_TIMER_QSIG_CC_T1]; + break; + default: + return -1; + } + if (!end) { + return -1; + } + + response.user.ptr = cc_record; + response.invoke_id = ctrl->last_invoke; + cc_record->t_activate_invoke_id = ctrl->last_invoke; + return pri_call_apdu_queue(call, msgtype, buffer, end - buffer, &response); +} + +/*! + * \internal + * \brief FSM action to queue the cc-request message. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * \param call Call leg from which to encode cc-request. + * + * \return Nothing + */ +static void pri_cc_act_queue_cc_request(struct pri *ctrl, struct pri_cc_record *cc_record, q931_call *call) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + if (rose_cc_request(ctrl, call, cc_record)) { + pri_message(ctrl, "Could not queue message for cc-request.\n"); + } +} + +/*! + * \internal + * \brief FSM action to send the CCBSDeactivate message. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_send_cc_deactivate_req(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + send_cc_deactivate_req(ctrl, cc_record->signaling, cc_record); +} + +/*! + * \internal + * \brief FSM action to send the CCBSBFree message. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_send_ccbs_b_free(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + send_ccbs_b_free(ctrl, cc_record->signaling, cc_record); +} + +/*! + * \internal + * \brief FSM action to send the remote user free message. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_send_remote_user_free(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + send_remote_user_free(ctrl, cc_record); +} + +/*! + * \internal + * \brief FSM action to send the CALL_PROCEEDING message on the signaling link. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_send_call_proceeding(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + pri_proceeding(ctrl, cc_record->signaling, 0, 0); +} + +/*! + * \internal + * \brief FSM action to send the CC suspend message. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_send_cc_suspend(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + send_cc_suspend(ctrl, cc_record); +} + +/*! + * \internal + * \brief FSM action to send the CC resume message. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_send_cc_resume(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + send_cc_resume(ctrl, cc_record); +} + +/*! + * \internal + * \brief FSM action to send the ccCancel message. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_send_cc_cancel(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + send_cc_cancel(ctrl, cc_record); +} + +/*! + * \internal + * \brief FSM action to send the CCBSStopAlerting message. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_send_ccbs_stop_alerting(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + send_ccbs_stop_alerting(ctrl, cc_record->signaling, cc_record); +} + +/*! + * \internal + * \brief FSM action to release the call linkage id. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_release_link_id(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + cc_record->call_linkage_id = CC_PTMP_INVALID_ID; +} + +/*! + * \internal + * \brief FSM action to set the Q.SIG retain-signaling-link option. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_set_retain_signaling_link(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + cc_record->option.retain_signaling_link = 1; +} + +/*! + * \internal + * \brief FSM action to reset the raw A status request no response count. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_raw_status_count_reset(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + cc_record->fsm.ptmp.party_a_status_count = 0; +} + +/*! + * \internal + * \brief FSM action to increment the raw A status request no response count. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_raw_status_count_increment(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + ++cc_record->fsm.ptmp.party_a_status_count; +} + +/*! + * \internal + * \brief FSM action to reset raw A status. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_reset_raw_a_status(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + cc_record->fsm.ptmp.party_a_status_acc = CC_PARTY_A_AVAILABILITY_INVALID; +} + +/*! + * \internal + * \brief FSM action to add raw A status with busy. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_add_raw_a_status_busy(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + if (cc_record->fsm.ptmp.party_a_status_acc != CC_PARTY_A_AVAILABILITY_FREE) { + cc_record->fsm.ptmp.party_a_status_acc = CC_PARTY_A_AVAILABILITY_BUSY; + } +} + +/*! + * \internal + * \brief FSM action to set raw A status to free. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_set_raw_a_status_free(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + cc_record->fsm.ptmp.party_a_status_acc = CC_PARTY_A_AVAILABILITY_FREE; +} + +/*! + * \internal + * \brief Fill in the status response party A status update event. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_fill_status_rsp_a(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record) +{ + struct pri_subcommand *subcmd; + + if (cc_record->fsm.ptmp.party_a_status_acc == CC_PARTY_A_AVAILABILITY_INVALID) { + /* Accumulated party A status is invalid so don't pass it up. */ + return; + } + + subcmd = q931_alloc_subcommand(ctrl); + if (!subcmd) { + return; + } + + subcmd->cmd = PRI_SUBCMD_CC_STATUS_REQ_RSP; + subcmd->u.cc_status_req_rsp.cc_id = cc_record->record_id; + subcmd->u.cc_status_req_rsp.status = + (cc_record->fsm.ptmp.party_a_status_acc == CC_PARTY_A_AVAILABILITY_FREE) + ? 0 /* free */ : 1 /* busy */; +} + +/*! + * \internal + * \brief Pass up party A status response to upper layer (indirectly). + * + * \param data CC record pointer. + * + * \return Nothing + */ +static void pri_cc_indirect_status_rsp_a(void *data) +{ + struct pri_cc_record *cc_record = data; + + cc_record->t_indirect = 0; + q931_cc_indirect(cc_record->master, cc_record, pri_cc_fill_status_rsp_a); +} + +/*! + * \internal + * \brief FSM action to pass up party A status response to upper layer (indirectly). + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + * + * \note + * Warning: Must not use this action with pri_cc_act_set_self_destruct() in the + * same event. + */ +static void pri_cc_act_pass_up_status_rsp_a_indirect(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + if (cc_record->fsm.ptmp.party_a_status_acc != CC_PARTY_A_AVAILABILITY_INVALID) { + /* Accumulated party A status is not invalid so pass it up. */ + if (cc_record->t_indirect) { + pri_error(ctrl, "!! An indirect action is already active!"); + pri_schedule_del(ctrl, cc_record->t_indirect); + } + cc_record->t_indirect = pri_schedule_event(ctrl, 0, pri_cc_indirect_status_rsp_a, + cc_record); + } +} + +/*! + * \internal + * \brief FSM action to pass up party A status response to upper layer. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_pass_up_status_rsp_a(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + pri_cc_fill_status_rsp_a(ctrl, cc_record->signaling, cc_record); +} + +/*! + * \internal + * \brief FSM action to promote raw A status. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_promote_raw_a_status(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + cc_record->party_a_status = cc_record->fsm.ptmp.party_a_status_acc; +} + +/*! + * \internal + * \brief FSM action to reset A status. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_reset_a_status(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + cc_record->party_a_status = CC_PARTY_A_AVAILABILITY_INVALID; +} + +/*! + * \internal + * \brief FSM action to set A status as busy. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_set_a_status_busy(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + cc_record->party_a_status = CC_PARTY_A_AVAILABILITY_BUSY; +} + +/*! + * \internal + * \brief FSM action to set A status as free. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_set_a_status_free(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + cc_record->party_a_status = CC_PARTY_A_AVAILABILITY_FREE; +} + +/*! + * \internal + * \brief Fill in the party A status update event. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_fill_status_a(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record) +{ + struct pri_subcommand *subcmd; + + if (cc_record->party_a_status == CC_PARTY_A_AVAILABILITY_INVALID) { + /* Party A status is invalid so don't pass it up. */ + return; + } + + subcmd = q931_alloc_subcommand(ctrl); + if (!subcmd) { + return; + } + + subcmd->cmd = PRI_SUBCMD_CC_STATUS; + subcmd->u.cc_status.cc_id = cc_record->record_id; + subcmd->u.cc_status.status = + (cc_record->party_a_status == CC_PARTY_A_AVAILABILITY_FREE) + ? 0 /* free */ : 1 /* busy */; +} + +/*! + * \internal + * \brief Pass up party A status to upper layer (indirectly). + * + * \param data CC record pointer. + * + * \return Nothing + */ +static void pri_cc_indirect_status_a(void *data) +{ + struct pri_cc_record *cc_record = data; + + cc_record->t_indirect = 0; + q931_cc_indirect(cc_record->master, cc_record, pri_cc_fill_status_a); +} + +/*! + * \internal + * \brief FSM action to pass up party A status to upper layer (indirectly). + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + * + * \note + * Warning: Must not use this action with pri_cc_act_set_self_destruct() in the + * same event. + */ +static void pri_cc_act_pass_up_a_status_indirect(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + if (cc_record->party_a_status != CC_PARTY_A_AVAILABILITY_INVALID) { + /* Party A status is not invalid so pass it up. */ + if (cc_record->t_indirect) { + pri_error(ctrl, "!! An indirect action is already active!"); + pri_schedule_del(ctrl, cc_record->t_indirect); + } + cc_record->t_indirect = pri_schedule_event(ctrl, 0, pri_cc_indirect_status_a, + cc_record); + } +} + +/*! + * \internal + * \brief FSM action to pass up party A status to upper layer. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_pass_up_a_status(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + pri_cc_fill_status_a(ctrl, cc_record->signaling, cc_record); +} + +/*! + * \internal + * \brief FSM action to pass up CC request (CCBS/CCNR) to upper layer. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_pass_up_cc_request(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + struct pri_subcommand *subcmd; + + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + + subcmd = q931_alloc_subcommand(ctrl); + if (!subcmd) { + return; + } + + subcmd->cmd = PRI_SUBCMD_CC_REQ; + subcmd->u.cc_request.cc_id = cc_record->record_id; + subcmd->u.cc_request.mode = cc_record->is_ccnr ? 1 /* ccnr */ : 0 /* ccbs */; +} + +/*! + * \internal + * \brief FSM action to pass up CC cancel to upper layer. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_pass_up_cc_cancel(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + struct pri_subcommand *subcmd; + + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + + subcmd = q931_alloc_subcommand(ctrl); + if (!subcmd) { + return; + } + + subcmd->cmd = PRI_SUBCMD_CC_CANCEL; + subcmd->u.cc_cancel.cc_id = cc_record->record_id; + subcmd->u.cc_cancel.is_agent = cc_record->is_agent; +} + +/*! + * \internal + * \brief FSM action to pass up CC call to upper layer. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_pass_up_cc_call(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + struct pri_subcommand *subcmd; + + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + + subcmd = q931_alloc_subcommand(ctrl); + if (!subcmd) { + return; + } + + subcmd->cmd = PRI_SUBCMD_CC_CALL; + subcmd->u.cc_call.cc_id = cc_record->record_id; +} + +/*! + * \internal + * \brief FSM action to pass up CC available to upper layer. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_pass_up_cc_available(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + struct pri_subcommand *subcmd; + + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + + subcmd = q931_alloc_subcommand(ctrl); + if (!subcmd) { + return; + } + + subcmd->cmd = PRI_SUBCMD_CC_AVAILABLE; + subcmd->u.cc_available.cc_id = cc_record->record_id; +} + +/*! + * \internal + * \brief FSM action to pass up CC request response is success to upper layer. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_pass_up_cc_req_rsp_success(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + struct pri_subcommand *subcmd; + + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + + subcmd = q931_alloc_subcommand(ctrl); + if (!subcmd) { + return; + } + + subcmd->cmd = PRI_SUBCMD_CC_REQ_RSP; + subcmd->u.cc_request_rsp.cc_id = cc_record->record_id; + subcmd->u.cc_request_rsp.status = 0;/* success */ + subcmd->u.cc_request_rsp.fail_code = 0; +} + +/*! + * \internal + * \brief FSM action to pass up CC request response is failed to upper layer. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_pass_up_cc_req_rsp_fail(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + struct pri_subcommand *subcmd; + + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + + subcmd = q931_alloc_subcommand(ctrl); + if (!subcmd) { + return; + } + + subcmd->cmd = PRI_SUBCMD_CC_REQ_RSP; + subcmd->u.cc_request_rsp.cc_id = cc_record->record_id; + subcmd->u.cc_request_rsp.status = + (cc_record->msg.cc_req_rsp.reason == APDU_CALLBACK_REASON_MSG_ERROR) + ? 2 /* error */ : 3 /* reject */; + subcmd->u.cc_request_rsp.fail_code = cc_record->msg.cc_req_rsp.code; +} + +/*! + * \internal + * \brief FSM action to pass up CC request response is timeout to upper layer. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_pass_up_cc_req_rsp_timeout(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + struct pri_subcommand *subcmd; + + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + + subcmd = q931_alloc_subcommand(ctrl); + if (!subcmd) { + return; + } + + subcmd->cmd = PRI_SUBCMD_CC_REQ_RSP; + subcmd->u.cc_request_rsp.cc_id = cc_record->record_id; + subcmd->u.cc_request_rsp.status = 1;/* timeout */ + subcmd->u.cc_request_rsp.fail_code = 0; +} + +/*! + * \internal + * \brief FSM action to pass up CC B free to upper layer. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_pass_up_b_free(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + struct pri_subcommand *subcmd; + + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + + subcmd = q931_alloc_subcommand(ctrl); + if (!subcmd) { + return; + } + + subcmd->cmd = PRI_SUBCMD_CC_B_FREE; + subcmd->u.cc_b_free.cc_id = cc_record->record_id; +} + +/*! + * \internal + * \brief FSM action to pass up CC remote user free to upper layer. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_pass_up_remote_user_free(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + struct pri_subcommand *subcmd; + + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + + subcmd = q931_alloc_subcommand(ctrl); + if (!subcmd) { + return; + } + + subcmd->cmd = PRI_SUBCMD_CC_REMOTE_USER_FREE; + subcmd->u.cc_remote_user_free.cc_id = cc_record->record_id; +} + +/*! + * \internal + * \brief FSM action to pass up stop alerting to upper layer. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_pass_up_stop_alerting(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + struct pri_subcommand *subcmd; + + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + + subcmd = q931_alloc_subcommand(ctrl); + if (!subcmd) { + return; + } + + subcmd->cmd = PRI_SUBCMD_CC_STOP_ALERTING; + subcmd->u.cc_stop_alerting.cc_id = cc_record->record_id; +} + +/*! + * \internal + * \brief FSM action to send error response to recall attempt. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * \param code Error code to put in error message response. + * + * \return Nothing + */ +static void pri_cc_act_send_error_recall(struct pri *ctrl, struct pri_cc_record *cc_record, enum rose_error_code code) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + rose_error_msg_encode(ctrl, cc_record->response.signaling, Q931_ANY_MESSAGE, + cc_record->response.invoke_id, code); +} + +/*! + * \internal + * \brief FSM action to queue CC recall marker. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * \param call Q.931 call leg. + * + * \return Nothing + */ +static void pri_cc_act_queue_setup_recall(struct pri *ctrl, struct pri_cc_record *cc_record, q931_call *call) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + rose_cc_recall_encode(ctrl, call, cc_record); +} + +/*! + * \internal + * \brief FSM action to request the call be hung up. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * \param call Q.931 call leg. + * + * \return Nothing + */ +static void pri_cc_act_set_call_to_hangup(struct pri *ctrl, struct pri_cc_record *cc_record, q931_call *call) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + call->cc.hangup_call = 1; +} + +/*! + * \internal + * \brief Post the CC_EVENT_HANGUP_SIGNALING event (timeout action). + * + * \param data CC record pointer. + * + * \return Nothing + */ +static void pri_cc_post_hangup_signaling(void *data) +{ + struct pri_cc_record *cc_record = data; + + cc_record->t_indirect = 0; + q931_cc_timeout(cc_record->master, cc_record, CC_EVENT_HANGUP_SIGNALING); +} + +/*! + * \internal + * \brief FSM action to post the CC_EVENT_HANGUP_SIGNALING event indirectly. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_post_hangup_signaling(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + if (cc_record->t_indirect) { + pri_error(ctrl, "!! An indirect action is already active!"); + pri_schedule_del(ctrl, cc_record->t_indirect); + } + cc_record->t_indirect = pri_schedule_event(ctrl, 0, pri_cc_post_hangup_signaling, + cc_record); +} + +/*! + * \internal + * \brief FSM action to immediately hangup the signaling link. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_hangup_signaling_link(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id); + pri_hangup(ctrl, cc_record->signaling, -1); +} + +/*! + * \internal + * \brief FSM action to set original call data into recall call. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * + * \return Nothing + */ +static void pri_cc_act_set_original_call_parameters(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record) +{ + call->called = cc_record->party_b; + call->remote_id = cc_record->party_a; + call->cc.saved_ie_contents = cc_record->saved_ie_contents; + call->bc = cc_record->bc; +} + +/*! + * \internal + * \brief CC FSM PTMP agent CC_STATE_IDLE. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_ptmp_agent_idle(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_AVAILABLE: + cc_record->state = CC_STATE_PENDING_AVAILABLE; + break; + case CC_EVENT_CANCEL: + pri_cc_act_set_self_destruct(ctrl, cc_record); + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM PTMP agent CC_STATE_PENDING_AVAILABLE. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_ptmp_agent_pend_avail(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_MSG_ALERTING: + pri_cc_act_send_cc_available(ctrl, cc_record, call, Q931_ALERTING); + cc_record->state = CC_STATE_AVAILABLE; + break; + case CC_EVENT_MSG_DISCONNECT: + pri_cc_act_send_cc_available(ctrl, cc_record, call, Q931_DISCONNECT); + cc_record->state = CC_STATE_AVAILABLE; + break; + case CC_EVENT_CANCEL: + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM PTMP agent CC_STATE_AVAILABLE. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_ptmp_agent_avail(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_MSG_RELEASE: + case CC_EVENT_MSG_RELEASE_COMPLETE: + pri_cc_act_stop_t_retention(ctrl, cc_record); + pri_cc_act_start_t_retention(ctrl, cc_record); + break; + case CC_EVENT_CC_REQUEST: + pri_cc_act_pass_up_cc_request(ctrl, cc_record); + pri_cc_act_stop_t_retention(ctrl, cc_record); + cc_record->state = CC_STATE_REQUESTED; + break; + case CC_EVENT_TIMEOUT_T_RETENTION: + pri_cc_act_send_erase_call_linkage_id(ctrl, cc_record); + pri_cc_act_release_link_id(ctrl, cc_record); + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_retention(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_CANCEL: + pri_cc_act_send_erase_call_linkage_id(ctrl, cc_record); + pri_cc_act_release_link_id(ctrl, cc_record); + pri_cc_act_stop_t_retention(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM PTMP agent CC_STATE_REQUESTED. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_ptmp_agent_req(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_CC_REQUEST_ACCEPT: + pri_cc_act_send_erase_call_linkage_id(ctrl, cc_record); + pri_cc_act_release_link_id(ctrl, cc_record); + pri_cc_act_start_t_supervision(ctrl, cc_record); + pri_cc_act_reset_a_status(ctrl, cc_record); + pri_cc_act_raw_status_count_reset(ctrl, cc_record); + cc_record->state = CC_STATE_ACTIVATED; + break; + case CC_EVENT_CANCEL: + pri_cc_act_send_erase_call_linkage_id(ctrl, cc_record); + pri_cc_act_release_link_id(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM PTMP agent CC_STATE_ACTIVATED. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_ptmp_agent_activated(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_RECALL: + pri_cc_act_send_error_recall(ctrl, cc_record, ROSE_ERROR_CCBS_NotReadyForCall); + pri_cc_act_set_call_to_hangup(ctrl, cc_record, call); + break; + case CC_EVENT_B_FREE: + pri_cc_act_send_ccbs_b_free(ctrl, cc_record); + break; + case CC_EVENT_REMOTE_USER_FREE: + switch (cc_record->party_a_status) { + case CC_PARTY_A_AVAILABILITY_INVALID: + if (!pri_cc_get_t_ccbs1_status(cc_record)) { + pri_cc_act_reset_raw_a_status(ctrl, cc_record); + pri_cc_act_send_ccbs_status_request(ctrl, cc_record); + //pri_cc_act_start_t_ccbs1(ctrl, cc_record); + } + cc_record->state = CC_STATE_B_AVAILABLE; + break; + case CC_PARTY_A_AVAILABILITY_BUSY: + pri_cc_act_pass_up_a_status_indirect(ctrl, cc_record); + pri_cc_act_send_ccbs_b_free(ctrl, cc_record); + if (!pri_cc_get_t_ccbs1_status(cc_record)) { + pri_cc_act_reset_raw_a_status(ctrl, cc_record); + pri_cc_act_send_ccbs_status_request(ctrl, cc_record); + //pri_cc_act_start_t_ccbs1(ctrl, cc_record); + } + cc_record->state = CC_STATE_SUSPENDED; + break; + case CC_PARTY_A_AVAILABILITY_FREE: + //pri_cc_act_pass_up_a_status_indirect(ctrl, cc_record); + pri_cc_act_send_remote_user_free(ctrl, cc_record); + pri_cc_act_stop_t_ccbs1(ctrl, cc_record); + pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record); + pri_cc_act_start_t_recall(ctrl, cc_record); + cc_record->state = CC_STATE_WAIT_CALLBACK; + break; + default: + break; + } + break; + case CC_EVENT_A_STATUS: + if (pri_cc_get_t_ccbs1_status(cc_record)) { + pri_cc_act_pass_up_status_rsp_a_indirect(ctrl, cc_record); + } else { + pri_cc_act_reset_a_status(ctrl, cc_record); + pri_cc_act_reset_raw_a_status(ctrl, cc_record); + pri_cc_act_send_ccbs_status_request(ctrl, cc_record); + //pri_cc_act_start_t_ccbs1(ctrl, cc_record); + pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record); + pri_cc_act_start_extended_t_ccbs1(ctrl, cc_record); + } + break; + case CC_EVENT_A_FREE: + pri_cc_act_raw_status_count_reset(ctrl, cc_record); + pri_cc_act_set_raw_a_status_free(ctrl, cc_record); + pri_cc_act_promote_raw_a_status(ctrl, cc_record); + pri_cc_act_pass_up_a_status(ctrl, cc_record); + pri_cc_act_stop_t_ccbs1(ctrl, cc_record); + break; + case CC_EVENT_A_BUSY: + pri_cc_act_add_raw_a_status_busy(ctrl, cc_record); + pri_cc_act_pass_up_status_rsp_a(ctrl, cc_record); + break; + case CC_EVENT_TIMEOUT_T_CCBS1: + pri_cc_act_promote_raw_a_status(ctrl, cc_record); + if (cc_record->party_a_status != CC_PARTY_A_AVAILABILITY_INVALID) { + /* Only received User A busy. */ + pri_cc_act_raw_status_count_reset(ctrl, cc_record); + } else { + /* Did not get any responses. */ + pri_cc_act_raw_status_count_increment(ctrl, cc_record); + if (cc_record->fsm.ptmp.party_a_status_count >= RAW_STATUS_COUNT_MAX) { + /* User A no longer present. */ + pri_cc_act_send_ccbs_erase(ctrl, cc_record, 0 /* normal-unspecified */); + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_ccbs1(ctrl, cc_record); + pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + } + } + break; + case CC_EVENT_TIMEOUT_EXTENDED_T_CCBS1: + pri_cc_act_reset_a_status(ctrl, cc_record); + pri_cc_act_raw_status_count_reset(ctrl, cc_record); + break; + case CC_EVENT_TIMEOUT_T_SUPERVISION: + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_send_ccbs_erase(ctrl, cc_record, 1 /* t-CCBS2-timeout */); + pri_cc_act_stop_t_ccbs1(ctrl, cc_record); + pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_LINK_CANCEL: + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_send_ccbs_erase(ctrl, cc_record, 0 /* normal-unspecified */); + pri_cc_act_stop_t_ccbs1(ctrl, cc_record); + pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_CANCEL: + pri_cc_act_send_ccbs_erase(ctrl, cc_record, 0 /* normal-unspecified */); + pri_cc_act_stop_t_ccbs1(ctrl, cc_record); + pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM PTMP agent CC_STATE_B_AVAILABLE. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_ptmp_agent_b_avail(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_RECALL: + pri_cc_act_send_error_recall(ctrl, cc_record, ROSE_ERROR_CCBS_NotReadyForCall); + pri_cc_act_set_call_to_hangup(ctrl, cc_record, call); + break; + case CC_EVENT_A_STATUS: + pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record); + pri_cc_act_start_extended_t_ccbs1(ctrl, cc_record); + pri_cc_act_pass_up_status_rsp_a_indirect(ctrl, cc_record); + break; + case CC_EVENT_A_FREE: + pri_cc_act_send_remote_user_free(ctrl, cc_record); + pri_cc_act_set_raw_a_status_free(ctrl, cc_record); + //pri_cc_act_promote_raw_a_status(ctrl, cc_record); + //pri_cc_act_pass_up_a_status(ctrl, cc_record); + if (cc_record->fsm.ptmp.extended_t_ccbs1) { + pri_cc_act_pass_up_status_rsp_a(ctrl, cc_record); + } + pri_cc_act_stop_t_ccbs1(ctrl, cc_record); + pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record); + pri_cc_act_start_t_recall(ctrl, cc_record); + cc_record->state = CC_STATE_WAIT_CALLBACK; + break; + case CC_EVENT_A_BUSY: + pri_cc_act_add_raw_a_status_busy(ctrl, cc_record); + if (cc_record->fsm.ptmp.extended_t_ccbs1) { + pri_cc_act_pass_up_status_rsp_a(ctrl, cc_record); + } + break; + case CC_EVENT_TIMEOUT_T_CCBS1: + if (cc_record->fsm.ptmp.party_a_status_acc != CC_PARTY_A_AVAILABILITY_INVALID) { + /* Only received User A busy. */ + pri_cc_act_raw_status_count_reset(ctrl, cc_record); + pri_cc_act_send_ccbs_b_free(ctrl, cc_record); + pri_cc_act_promote_raw_a_status(ctrl, cc_record); + pri_cc_act_pass_up_a_status(ctrl, cc_record); + /* Optimization due to flattening. */ + //if (!pri_cc_get_t_ccbs1_status(cc_record)) + { + pri_cc_act_reset_raw_a_status(ctrl, cc_record); + pri_cc_act_send_ccbs_status_request(ctrl, cc_record); + //pri_cc_act_start_t_ccbs1(ctrl, cc_record); + } + cc_record->state = CC_STATE_SUSPENDED; + } else { + /* Did not get any responses. */ + pri_cc_act_raw_status_count_increment(ctrl, cc_record); + if (cc_record->fsm.ptmp.party_a_status_count >= RAW_STATUS_COUNT_MAX) { + /* User A no longer present. */ + pri_cc_act_send_ccbs_erase(ctrl, cc_record, 0 /* normal-unspecified */); + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_ccbs1(ctrl, cc_record); + pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + } + //pri_cc_act_reset_raw_a_status(ctrl, cc_record); + pri_cc_act_send_ccbs_status_request(ctrl, cc_record); + //pri_cc_act_start_t_ccbs1(ctrl, cc_record); + } + break; + case CC_EVENT_TIMEOUT_T_SUPERVISION: + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_send_ccbs_erase(ctrl, cc_record, 1 /* t-CCBS2-timeout */); + pri_cc_act_stop_t_ccbs1(ctrl, cc_record); + pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_LINK_CANCEL: + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_send_ccbs_erase(ctrl, cc_record, 0 /* normal-unspecified */); + pri_cc_act_stop_t_ccbs1(ctrl, cc_record); + pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_CANCEL: + pri_cc_act_send_ccbs_erase(ctrl, cc_record, 0 /* normal-unspecified */); + pri_cc_act_stop_t_ccbs1(ctrl, cc_record); + pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM PTMP agent CC_STATE_SUSPENDED. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_ptmp_agent_suspended(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_RECALL: + pri_cc_act_send_error_recall(ctrl, cc_record, ROSE_ERROR_CCBS_NotReadyForCall); + pri_cc_act_set_call_to_hangup(ctrl, cc_record, call); + break; + case CC_EVENT_A_STATUS: + pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record); + pri_cc_act_start_extended_t_ccbs1(ctrl, cc_record); + pri_cc_act_pass_up_status_rsp_a_indirect(ctrl, cc_record); + break; + case CC_EVENT_A_FREE: + pri_cc_act_set_raw_a_status_free(ctrl, cc_record); + pri_cc_act_promote_raw_a_status(ctrl, cc_record); + pri_cc_act_pass_up_a_status(ctrl, cc_record); + if (cc_record->fsm.ptmp.extended_t_ccbs1) { + pri_cc_act_pass_up_status_rsp_a(ctrl, cc_record); + } + pri_cc_act_stop_t_ccbs1(ctrl, cc_record); + pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record); + pri_cc_act_reset_a_status(ctrl, cc_record); + pri_cc_act_raw_status_count_reset(ctrl, cc_record); + cc_record->state = CC_STATE_ACTIVATED; + break; + case CC_EVENT_A_BUSY: + pri_cc_act_add_raw_a_status_busy(ctrl, cc_record); + if (cc_record->fsm.ptmp.extended_t_ccbs1) { + pri_cc_act_pass_up_status_rsp_a(ctrl, cc_record); + } + break; + case CC_EVENT_TIMEOUT_T_CCBS1: + if (cc_record->fsm.ptmp.party_a_status_acc != CC_PARTY_A_AVAILABILITY_INVALID) { + /* Only received User A busy. */ + pri_cc_act_raw_status_count_reset(ctrl, cc_record); + } else { + /* Did not get any responses. */ + pri_cc_act_raw_status_count_increment(ctrl, cc_record); + if (cc_record->fsm.ptmp.party_a_status_count >= RAW_STATUS_COUNT_MAX) { + /* User A no longer present. */ + pri_cc_act_send_ccbs_erase(ctrl, cc_record, 0 /* normal-unspecified */); + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_ccbs1(ctrl, cc_record); + pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + } + } + pri_cc_act_reset_raw_a_status(ctrl, cc_record); + pri_cc_act_send_ccbs_status_request(ctrl, cc_record); + //pri_cc_act_start_t_ccbs1(ctrl, cc_record); + break; + case CC_EVENT_TIMEOUT_T_SUPERVISION: + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_send_ccbs_erase(ctrl, cc_record, 1 /* t-CCBS2-timeout */); + pri_cc_act_stop_t_ccbs1(ctrl, cc_record); + pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_LINK_CANCEL: + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_send_ccbs_erase(ctrl, cc_record, 0 /* normal-unspecified */); + pri_cc_act_stop_t_ccbs1(ctrl, cc_record); + pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_CANCEL: + pri_cc_act_send_ccbs_erase(ctrl, cc_record, 0 /* normal-unspecified */); + pri_cc_act_stop_t_ccbs1(ctrl, cc_record); + pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM PTMP agent CC_STATE_WAIT_CALLBACK. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_ptmp_agent_wait_callback(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_TIMEOUT_T_RECALL: + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_send_ccbs_erase(ctrl, cc_record, 2 /* t-CCBS3-timeout */); + pri_cc_act_stop_t_recall(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_STOP_ALERTING: + /* + * If an earlier link can send us this event then we + * really should be configured for globalRecall like + * the earlier link. + */ + if (cc_record->option.recall_mode == 0 /* globalRecall */) { + pri_cc_act_send_ccbs_stop_alerting(ctrl, cc_record); + } + pri_cc_act_stop_t_recall(ctrl, cc_record); + pri_cc_act_reset_a_status(ctrl, cc_record); + pri_cc_act_raw_status_count_reset(ctrl, cc_record); + cc_record->state = CC_STATE_ACTIVATED; + break; + case CC_EVENT_RECALL: + pri_cc_act_pass_up_cc_call(ctrl, cc_record); + pri_cc_act_set_original_call_parameters(ctrl, call, cc_record); + if (cc_record->option.recall_mode == 0 /* globalRecall */) { + pri_cc_act_send_ccbs_stop_alerting(ctrl, cc_record); + } + pri_cc_act_stop_t_recall(ctrl, cc_record); + cc_record->state = CC_STATE_CALLBACK; + break; + case CC_EVENT_A_STATUS: + pri_cc_act_set_raw_a_status_free(ctrl, cc_record); + pri_cc_act_pass_up_status_rsp_a_indirect(ctrl, cc_record); + break; + case CC_EVENT_TIMEOUT_T_SUPERVISION: + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_send_ccbs_erase(ctrl, cc_record, 1 /* t-CCBS2-timeout */); + pri_cc_act_stop_t_recall(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_LINK_CANCEL: + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_send_ccbs_erase(ctrl, cc_record, 0 /* normal-unspecified */); + pri_cc_act_stop_t_recall(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_CANCEL: + pri_cc_act_send_ccbs_erase(ctrl, cc_record, 0 /* normal-unspecified */); + pri_cc_act_stop_t_recall(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM PTMP agent CC_STATE_CALLBACK. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_ptmp_agent_callback(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_RECALL: + pri_cc_act_send_error_recall(ctrl, cc_record, ROSE_ERROR_CCBS_AlreadyAccepted); + pri_cc_act_set_call_to_hangup(ctrl, cc_record, call); + break; + case CC_EVENT_A_STATUS: + pri_cc_act_set_raw_a_status_free(ctrl, cc_record); + pri_cc_act_pass_up_status_rsp_a_indirect(ctrl, cc_record); + break; + case CC_EVENT_TIMEOUT_T_SUPERVISION: + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_send_ccbs_erase(ctrl, cc_record, 1 /* t-CCBS2-timeout */); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_LINK_CANCEL: + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_send_ccbs_erase(ctrl, cc_record, 0 /* normal-unspecified */); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_CANCEL: + pri_cc_act_send_ccbs_erase(ctrl, cc_record, 0 /* normal-unspecified */); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM PTMP monitor CC_STATE_IDLE. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_ptmp_monitor_idle(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_AVAILABLE: + /* + * Before event is posted: + * Received CallInfoRetain + * Created cc_record + * Saved CallLinkageID + */ + pri_cc_act_pass_up_cc_available(ctrl, cc_record); + cc_record->state = CC_STATE_AVAILABLE; + break; + case CC_EVENT_CANCEL: + pri_cc_act_set_self_destruct(ctrl, cc_record); + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM PTMP monitor CC_STATE_AVAILABLE. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_ptmp_monitor_avail(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + /* + * The upper layer is responsible for canceling the CC available + * offering as a safeguard in case the network cable is disconnected. + * The timer should be set much longer than the network T_RETENTION + * timer so normally the CC records will be cleaned up by network + * activity. + */ + switch (event) { + case CC_EVENT_CC_REQUEST: + /* cc_record->is_ccnr is set before event posted. */ + pri_cc_act_queue_cc_request(ctrl, cc_record, call); + //pri_cc_act_start_t_activate(ctrl, cc_record); + cc_record->state = CC_STATE_REQUESTED; + break; + case CC_EVENT_TIMEOUT_T_RETENTION: + /* + * Received EraseCallLinkageID + * T_RETENTION expired on the network side so we will pretend + * that it expired on our side. + */ + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_CANCEL: + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM PTMP monitor CC_STATE_REQUESTED. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_ptmp_monitor_req(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_CC_REQUEST_ACCEPT: + /* + * Before event is posted: + * Received CCBSRequest/CCNRRequest response + * Saved CCBSReference + */ + pri_cc_act_release_link_id(ctrl, cc_record); + pri_cc_act_pass_up_cc_req_rsp_success(ctrl, cc_record); + pri_cc_act_stop_t_activate(ctrl, cc_record); + /* + * Start T_CCBS2 or T_CCNR2 depending upon CC mode. + * For PTMP TE mode these timers are not defined. However, + * we will use them anyway to protect our resources from leaks + * caused by the network cable being disconnected. These + * timers should be set much longer than the network + * so normally the CC records will be cleaned up by network + * activity. + */ + pri_cc_act_start_t_supervision(ctrl, cc_record); + cc_record->state = CC_STATE_ACTIVATED; + break; + case CC_EVENT_CC_REQUEST_FAIL: + pri_cc_act_pass_up_cc_req_rsp_fail(ctrl, cc_record); + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_activate(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_TIMEOUT_T_ACTIVATE: + pri_cc_act_pass_up_cc_req_rsp_timeout(ctrl, cc_record); + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_activate(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_LINK_CANCEL: + /* Received CCBSErase */ + /* Claim it was a timeout */ + pri_cc_act_pass_up_cc_req_rsp_timeout(ctrl, cc_record); + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_activate(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_CANCEL: + cc_record->state = CC_STATE_WAIT_DESTRUCTION; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM PTMP monitor CC_STATE_WAIT_DESTRUCTION. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_ptmp_monitor_wait_destruction(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + /* We were in the middle of a cc-request when we were asked to cancel. */ + switch (event) { + case CC_EVENT_CC_REQUEST_ACCEPT: + /* + * Before event is posted: + * Received CCBSRequest/CCNRRequest response + * Saved CCBSReference + */ + pri_cc_act_send_cc_deactivate_req(ctrl, cc_record); + pri_cc_act_stop_t_activate(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_CC_REQUEST_FAIL: + pri_cc_act_stop_t_activate(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_TIMEOUT_T_ACTIVATE: + pri_cc_act_stop_t_activate(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_LINK_CANCEL: + /* Received CCBSErase */ + pri_cc_act_stop_t_activate(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM PTMP monitor CC_STATE_ACTIVATED. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_ptmp_monitor_activated(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_B_FREE: + /* Received CCBSBFree */ + pri_cc_act_pass_up_b_free(ctrl, cc_record); + break; + case CC_EVENT_REMOTE_USER_FREE: + /* Received CCBSRemoteUserFree */ + pri_cc_act_pass_up_remote_user_free(ctrl, cc_record); + cc_record->state = CC_STATE_WAIT_CALLBACK; + break; + case CC_EVENT_TIMEOUT_T_SUPERVISION: + pri_cc_act_send_cc_deactivate_req(ctrl, cc_record); + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_LINK_CANCEL: + /* Received CCBSErase */ + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_CANCEL: + pri_cc_act_send_cc_deactivate_req(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM PTMP monitor CC_STATE_WAIT_CALLBACK. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_ptmp_monitor_wait_callback(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_STOP_ALERTING: + pri_cc_act_pass_up_stop_alerting(ctrl, cc_record); + cc_record->state = CC_STATE_ACTIVATED; + break; + case CC_EVENT_RECALL: + /* The original call parameters have already been set. */ + pri_cc_act_queue_setup_recall(ctrl, cc_record, call); + cc_record->state = CC_STATE_CALLBACK; + break; + case CC_EVENT_TIMEOUT_T_SUPERVISION: + pri_cc_act_send_cc_deactivate_req(ctrl, cc_record); + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_LINK_CANCEL: + /* Received CCBSErase */ + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_CANCEL: + pri_cc_act_send_cc_deactivate_req(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM PTMP monitor CC_STATE_CALLBACK. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_ptmp_monitor_callback(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + /* + * We are waiting for the CC records to be torn down because + * CC is complete. + * This state is mainly to block CC_EVENT_STOP_ALERTING since + * we are the one doing the CC recall so we do not need to stop + * alerting. + */ + switch (event) { + case CC_EVENT_TIMEOUT_T_SUPERVISION: + pri_cc_act_send_cc_deactivate_req(ctrl, cc_record); + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_LINK_CANCEL: + /* Received CCBSErase */ + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_CANCEL: + pri_cc_act_send_cc_deactivate_req(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM PTP agent CC_STATE_IDLE. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_ptp_agent_idle(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_AVAILABLE: + cc_record->state = CC_STATE_PENDING_AVAILABLE; + break; + case CC_EVENT_CANCEL: + pri_cc_act_set_self_destruct(ctrl, cc_record); + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM PTP agent CC_STATE_PENDING_AVAILABLE. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_ptp_agent_pend_avail(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_MSG_ALERTING: + pri_cc_act_send_cc_available(ctrl, cc_record, call, Q931_ALERTING); + cc_record->state = CC_STATE_AVAILABLE; + break; + case CC_EVENT_MSG_DISCONNECT: + pri_cc_act_send_cc_available(ctrl, cc_record, call, Q931_DISCONNECT); + cc_record->state = CC_STATE_AVAILABLE; + break; + case CC_EVENT_CANCEL: + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM PTP agent CC_STATE_AVAILABLE. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_ptp_agent_avail(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + /* + * For PTP mode the T_RETENTION timer is not defined. However, + * we will use it anyway in this state to protect our resources + * from leaks caused by user A not requesting CC. This timer + * should be set much longer than the PTMP network link to + * allow for variations in user A's CC offer timer. + */ + switch (event) { + case CC_EVENT_MSG_RELEASE: + case CC_EVENT_MSG_RELEASE_COMPLETE: + pri_cc_act_stop_t_retention(ctrl, cc_record); + pri_cc_act_start_t_retention(ctrl, cc_record); + break; + case CC_EVENT_CC_REQUEST: + pri_cc_act_pass_up_cc_request(ctrl, cc_record); + pri_cc_act_stop_t_retention(ctrl, cc_record); + cc_record->state = CC_STATE_REQUESTED; + break; + case CC_EVENT_TIMEOUT_T_RETENTION: + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_retention(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_CANCEL: + pri_cc_act_stop_t_retention(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM PTP agent CC_STATE_REQUESTED. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_ptp_agent_req(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_CC_REQUEST_ACCEPT: + /* Start T_CCBS5/T_CCNR5 depending upon CC mode. */ + pri_cc_act_start_t_supervision(ctrl, cc_record); + pri_cc_act_reset_a_status(ctrl, cc_record); + cc_record->state = CC_STATE_ACTIVATED; + break; + case CC_EVENT_SIGNALING_GONE: + /* Signaling link cleared. */ + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_CANCEL: + pri_cc_act_hangup_signaling_link(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM PTP agent CC_STATE_ACTIVATED. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_ptp_agent_activated(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_REMOTE_USER_FREE: + pri_cc_act_pass_up_a_status_indirect(ctrl, cc_record); + if (cc_record->party_a_status == CC_PARTY_A_AVAILABILITY_BUSY) { + cc_record->state = CC_STATE_SUSPENDED; + } else { + pri_cc_act_send_remote_user_free(ctrl, cc_record); + cc_record->state = CC_STATE_WAIT_CALLBACK; + } + break; + case CC_EVENT_SUSPEND: + /* Received CCBS_T_Suspend */ + pri_cc_act_set_a_status_busy(ctrl, cc_record); + break; + case CC_EVENT_RESUME: + /* Received CCBS_T_Resume */ + pri_cc_act_reset_a_status(ctrl, cc_record); + break; + case CC_EVENT_RECALL: + /* Received CCBS_T_Call */ + pri_cc_act_pass_up_cc_call(ctrl, cc_record); + pri_cc_act_set_original_call_parameters(ctrl, call, cc_record); + break; + case CC_EVENT_TIMEOUT_T_SUPERVISION: + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_hangup_signaling_link(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_SIGNALING_GONE: + /* Signaling link cleared. */ + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_CANCEL: + pri_cc_act_hangup_signaling_link(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM PTP agent CC_STATE_WAIT_CALLBACK. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_ptp_agent_wait_callback(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_SUSPEND: + /* Received CCBS_T_Suspend */ + pri_cc_act_set_a_status_busy(ctrl, cc_record); + pri_cc_act_pass_up_a_status(ctrl, cc_record); + cc_record->state = CC_STATE_SUSPENDED; + break; + case CC_EVENT_RECALL: + /* Received CCBS_T_Call */ + pri_cc_act_pass_up_cc_call(ctrl, cc_record); + pri_cc_act_set_original_call_parameters(ctrl, call, cc_record); + break; + case CC_EVENT_TIMEOUT_T_SUPERVISION: + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_hangup_signaling_link(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_SIGNALING_GONE: + /* Signaling link cleared. */ + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_CANCEL: + pri_cc_act_hangup_signaling_link(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM PTP agent CC_STATE_SUSPENDED. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_ptp_agent_suspended(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_RESUME: + /* Received CCBS_T_Resume */ + pri_cc_act_set_a_status_free(ctrl, cc_record); + pri_cc_act_pass_up_a_status(ctrl, cc_record); + pri_cc_act_reset_a_status(ctrl, cc_record); + cc_record->state = CC_STATE_ACTIVATED; + break; + case CC_EVENT_RECALL: + /* Received CCBS_T_Call */ + pri_cc_act_pass_up_cc_call(ctrl, cc_record); + pri_cc_act_set_original_call_parameters(ctrl, call, cc_record); + break; + case CC_EVENT_TIMEOUT_T_SUPERVISION: + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_hangup_signaling_link(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_SIGNALING_GONE: + /* Signaling link cleared. */ + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_CANCEL: + pri_cc_act_hangup_signaling_link(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM PTP monitor CC_STATE_IDLE. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_ptp_monitor_idle(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_AVAILABLE: + /* Received CCBS-T-Aailable */ + pri_cc_act_pass_up_cc_available(ctrl, cc_record); + cc_record->state = CC_STATE_AVAILABLE; + break; + case CC_EVENT_CANCEL: + pri_cc_act_set_self_destruct(ctrl, cc_record); + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM PTP monitor CC_STATE_AVAILABLE. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_ptp_monitor_avail(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + /* The upper layer is responsible for canceling the CC available offering. */ + switch (event) { + case CC_EVENT_CC_REQUEST: + /* + * Before event is posted: + * cc_record->is_ccnr is set. + * The signaling connection call record is created. + */ + pri_cc_act_queue_cc_request(ctrl, cc_record, call); + /* + * For PTP mode the T_ACTIVATE timer is not defined. However, + * we will use it to protect our resources from leaks caused + * by the network cable being disconnected. + * This timer should be set longer than normal so the + * CC records will normally be cleaned up by network activity. + */ + //pri_cc_act_start_t_activate(ctrl, cc_record); + cc_record->state = CC_STATE_REQUESTED; + break; + case CC_EVENT_CANCEL: + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM PTP monitor CC_STATE_REQUESTED. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_ptp_monitor_req(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_CC_REQUEST_ACCEPT: + /* + * Received CCBS-T-Request/CCNR-T-Request response + * Before event is posted: + * Negotiated CC retention setting saved + */ + pri_cc_act_pass_up_cc_req_rsp_success(ctrl, cc_record); + pri_cc_act_stop_t_activate(ctrl, cc_record); + /* Start T_CCBS6/T_CCNR6 depending upon CC mode. */ + pri_cc_act_start_t_supervision(ctrl, cc_record); + pri_cc_act_reset_a_status(ctrl, cc_record); + cc_record->state = CC_STATE_ACTIVATED; + break; + case CC_EVENT_CC_REQUEST_FAIL: + pri_cc_act_pass_up_cc_req_rsp_fail(ctrl, cc_record); + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + /* + * If this request fail comes in with the RELEASE_COMPLETE + * message then the post action will never get a chance to + * run. It will be aborted because the CC_EVENT_SIGNALING_GONE + * will be processed first. + */ + pri_cc_act_post_hangup_signaling(ctrl, cc_record); + pri_cc_act_stop_t_activate(ctrl, cc_record); + cc_record->state = CC_STATE_WAIT_DESTRUCTION; + break; + case CC_EVENT_TIMEOUT_T_ACTIVATE: + pri_cc_act_pass_up_cc_req_rsp_timeout(ctrl, cc_record); + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_hangup_signaling_link(ctrl, cc_record); + pri_cc_act_stop_t_activate(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_SIGNALING_GONE: + /* Claim it was a timeout */ + pri_cc_act_pass_up_cc_req_rsp_timeout(ctrl, cc_record); + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_activate(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_CANCEL: + pri_cc_act_hangup_signaling_link(ctrl, cc_record); + pri_cc_act_stop_t_activate(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM PTP monitor CC_STATE_WAIT_DESTRUCTION. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_ptp_monitor_wait_destruction(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + /* + * Delayed disconnect of the signaling link to allow subcmd events + * from the signaling link to be passed up. + */ + switch (event) { + case CC_EVENT_SIGNALING_GONE: + /* Signaling link cleared. */ + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_HANGUP_SIGNALING: + pri_cc_act_hangup_signaling_link(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_CANCEL: + pri_cc_act_hangup_signaling_link(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM PTP monitor CC_STATE_ACTIVATED. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_ptp_monitor_activated(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_REMOTE_USER_FREE: + /* Received CCBS_T_RemoteUserFree */ + pri_cc_act_pass_up_remote_user_free(ctrl, cc_record); + if (cc_record->party_a_status == CC_PARTY_A_AVAILABILITY_BUSY) { + pri_cc_act_send_cc_suspend(ctrl, cc_record); + cc_record->state = CC_STATE_SUSPENDED; + } else { + cc_record->state = CC_STATE_WAIT_CALLBACK; + } + break; + case CC_EVENT_SUSPEND: + pri_cc_act_set_a_status_busy(ctrl, cc_record); + break; + case CC_EVENT_RESUME: + pri_cc_act_reset_a_status(ctrl, cc_record); + break; + case CC_EVENT_RECALL: + /* The original call parameters have already been set. */ + pri_cc_act_queue_setup_recall(ctrl, cc_record, call); + break; + case CC_EVENT_TIMEOUT_T_SUPERVISION: + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_hangup_signaling_link(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_SIGNALING_GONE: + /* Signaling link cleared. */ + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_CANCEL: + pri_cc_act_hangup_signaling_link(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM PTP monitor CC_STATE_WAIT_CALLBACK. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_ptp_monitor_wait_callback(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_SUSPEND: + pri_cc_act_send_cc_suspend(ctrl, cc_record); + cc_record->state = CC_STATE_SUSPENDED; + break; + case CC_EVENT_RECALL: + /* The original call parameters have already been set. */ + pri_cc_act_queue_setup_recall(ctrl, cc_record, call); + break; + case CC_EVENT_TIMEOUT_T_SUPERVISION: + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_hangup_signaling_link(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_SIGNALING_GONE: + /* Signaling link cleared. */ + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_CANCEL: + pri_cc_act_hangup_signaling_link(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM PTP monitor CC_STATE_SUSPENDED. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_ptp_monitor_suspended(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_RESUME: + pri_cc_act_send_cc_resume(ctrl, cc_record); + pri_cc_act_reset_a_status(ctrl, cc_record); + cc_record->state = CC_STATE_ACTIVATED; + break; + case CC_EVENT_RECALL: + /* The original call parameters have already been set. */ + pri_cc_act_queue_setup_recall(ctrl, cc_record, call); + break; + case CC_EVENT_TIMEOUT_T_SUPERVISION: + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_hangup_signaling_link(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_SIGNALING_GONE: + /* Signaling link cleared. */ + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_CANCEL: + pri_cc_act_hangup_signaling_link(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM Q.SIG agent CC_STATE_IDLE. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_qsig_agent_idle(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_AVAILABLE: + cc_record->state = CC_STATE_AVAILABLE; + break; + case CC_EVENT_CANCEL: + pri_cc_act_set_self_destruct(ctrl, cc_record); + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM Q.SIG agent CC_STATE_AVAILABLE. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_qsig_agent_avail(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + /* + * For Q.SIG mode the T_RETENTION timer is not defined. However, + * we will use it anyway in this state to protect our resources + * from leaks caused by user A not requesting CC. This timer + * should be set much longer than the PTMP network link to + * allow for variations in user A's CC offer timer. + */ + switch (event) { + case CC_EVENT_MSG_RELEASE: + case CC_EVENT_MSG_RELEASE_COMPLETE: + pri_cc_act_stop_t_retention(ctrl, cc_record); + pri_cc_act_start_t_retention(ctrl, cc_record); + break; + case CC_EVENT_CC_REQUEST: + pri_cc_act_pass_up_cc_request(ctrl, cc_record); + /* Send Q931_CALL_PROCEEDING message on signaling link. */ + pri_cc_act_send_call_proceeding(ctrl, cc_record); + pri_cc_act_stop_t_retention(ctrl, cc_record); + cc_record->state = CC_STATE_REQUESTED; + break; + case CC_EVENT_TIMEOUT_T_RETENTION: + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_retention(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_CANCEL: + pri_cc_act_stop_t_retention(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM Q.SIG agent CC_STATE_REQUESTED. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_qsig_agent_req(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_CC_REQUEST_ACCEPT: + /* Start QSIG_CCBS_T2/QSIG_CCNR_T2 depending upon CC mode. */ + pri_cc_act_start_t_supervision(ctrl, cc_record); + cc_record->state = CC_STATE_ACTIVATED; + break; + case CC_EVENT_SIGNALING_GONE: + /* Signaling link cleared. */ + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_CANCEL: + pri_cc_act_hangup_signaling_link(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM Q.SIG agent CC_STATE_WAIT_DESTRUCTION. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_qsig_agent_wait_destruction(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + /* + * Delayed disconnect of the signaling link to allow subcmd events + * from the signaling link to be passed up. + */ + switch (event) { + case CC_EVENT_SIGNALING_GONE: + /* Signaling link cleared. */ + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_HANGUP_SIGNALING: + pri_cc_act_hangup_signaling_link(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM Q.SIG agent CC_STATE_ACTIVATED. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_qsig_agent_activated(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_REMOTE_USER_FREE: + /* Send ccExecPossible in FACILITY or SETUP. */ + pri_cc_act_send_remote_user_free(ctrl, cc_record); + cc_record->state = CC_STATE_WAIT_CALLBACK; + break; + case CC_EVENT_TIMEOUT_T_SUPERVISION: + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_send_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_SIGNALING_GONE: + /* Signaling link cleared. */ + pri_cc_act_disassociate_signaling_link(ctrl, cc_record); + break; + case CC_EVENT_LINK_CANCEL: + /* Received ccCancel */ + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_post_hangup_signaling(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + cc_record->state = CC_STATE_WAIT_DESTRUCTION; + break; + case CC_EVENT_CANCEL: + pri_cc_act_send_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM Q.SIG agent CC_STATE_WAIT_CALLBACK. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_qsig_agent_wait_callback(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_SUSPEND: + /* Received ccSuspend */ + pri_cc_act_set_a_status_busy(ctrl, cc_record); + pri_cc_act_pass_up_a_status(ctrl, cc_record); + cc_record->state = CC_STATE_SUSPENDED; + break; + case CC_EVENT_RECALL: + /* Received ccRingout */ + pri_cc_act_pass_up_cc_call(ctrl, cc_record); + pri_cc_act_set_original_call_parameters(ctrl, call, cc_record); + break; + case CC_EVENT_TIMEOUT_T_SUPERVISION: + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_send_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_SIGNALING_GONE: + /* Signaling link cleared. */ + pri_cc_act_disassociate_signaling_link(ctrl, cc_record); + break; + case CC_EVENT_LINK_CANCEL: + /* Received ccCancel */ + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_post_hangup_signaling(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + cc_record->state = CC_STATE_WAIT_DESTRUCTION; + break; + case CC_EVENT_CANCEL: + pri_cc_act_send_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM Q.SIG agent CC_STATE_SUSPENDED. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_qsig_agent_suspended(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_RESUME: + /* Received ccResume */ + pri_cc_act_set_a_status_free(ctrl, cc_record); + pri_cc_act_pass_up_a_status(ctrl, cc_record); + cc_record->state = CC_STATE_ACTIVATED; + break; + case CC_EVENT_TIMEOUT_T_SUPERVISION: + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_send_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_SIGNALING_GONE: + /* Signaling link cleared. */ + pri_cc_act_disassociate_signaling_link(ctrl, cc_record); + break; + case CC_EVENT_LINK_CANCEL: + /* Received ccCancel */ + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_post_hangup_signaling(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + cc_record->state = CC_STATE_WAIT_DESTRUCTION; + break; + case CC_EVENT_CANCEL: + pri_cc_act_send_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM Q.SIG monitor CC_STATE_IDLE. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_qsig_monitor_idle(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_AVAILABLE: + /* + * LibPRI will determine if CC will be offered based upon + * if it is even possible. + * Essentially: + * 1) The call must not have been redirected in this link's + * setup. + * 2) Received an ALERTING or received a + * DISCONNECT(busy/congestion). + */ + pri_cc_act_pass_up_cc_available(ctrl, cc_record); + cc_record->state = CC_STATE_AVAILABLE; + break; + case CC_EVENT_CANCEL: + pri_cc_act_set_self_destruct(ctrl, cc_record); + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM Q.SIG monitor CC_STATE_AVAILABLE. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_qsig_monitor_avail(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + /* The upper layer is responsible for canceling the CC available offering. */ + switch (event) { + case CC_EVENT_CC_REQUEST: + /* + * Before event is posted: + * cc_record->is_ccnr is set. + * The signaling connection call record is created. + */ + pri_cc_act_queue_cc_request(ctrl, cc_record, call); + /* Start QSIG_CC_T1. */ + //pri_cc_act_start_t_activate(ctrl, cc_record); + cc_record->state = CC_STATE_REQUESTED; + break; + case CC_EVENT_CANCEL: + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM Q.SIG monitor CC_STATE_REQUESTED. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_qsig_monitor_req(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_CC_REQUEST_ACCEPT: + /* + * Received ccbsRequest/ccnrRequest response + * Before event is posted: + * Negotiated CC retention setting saved + * Negotiated signaling link retention setting saved + */ + pri_cc_act_stop_t_activate(ctrl, cc_record); + if (cc_record->fsm.qsig.msgtype == Q931_RELEASE) { + pri_cc_act_disassociate_signaling_link(ctrl, cc_record); + if (cc_record->option.retain_signaling_link) { + /* + * The far end did not honor the + * signaling link retention requirement. + * ECMA-186 Section 6.5.2.2.1 + */ + pri_cc_act_pass_up_cc_req_rsp_timeout(ctrl, cc_record); + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_send_cc_cancel(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + } + } + pri_cc_act_pass_up_cc_req_rsp_success(ctrl, cc_record); + /* Start QSIG_CCBS_T2/QSIG_CCNR_T2 depending upon CC mode. */ + pri_cc_act_start_t_supervision(ctrl, cc_record); + pri_cc_act_reset_a_status(ctrl, cc_record); + cc_record->state = CC_STATE_ACTIVATED; + break; + case CC_EVENT_CC_REQUEST_FAIL: + pri_cc_act_stop_t_activate(ctrl, cc_record); + pri_cc_act_pass_up_cc_req_rsp_fail(ctrl, cc_record); + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + /* + * If this request fail comes in with the RELEASE message + * then the post action will never get a chance to run. + * It will be aborted because the CC_EVENT_SIGNALING_GONE + * will be processed first. + */ + pri_cc_act_post_hangup_signaling(ctrl, cc_record); + cc_record->state = CC_STATE_WAIT_DESTRUCTION; + break; + case CC_EVENT_TIMEOUT_T_ACTIVATE: + pri_cc_act_stop_t_activate(ctrl, cc_record); + pri_cc_act_pass_up_cc_req_rsp_timeout(ctrl, cc_record); + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_hangup_signaling_link(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_SIGNALING_GONE: + pri_cc_act_stop_t_activate(ctrl, cc_record); + /* Claim it was a timeout */ + pri_cc_act_pass_up_cc_req_rsp_timeout(ctrl, cc_record); + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_CANCEL: + cc_record->state = CC_STATE_WAIT_DESTRUCTION; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM Q.SIG monitor CC_STATE_WAIT_DESTRUCTION. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_qsig_monitor_wait_destruction(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + /* + * Delayed disconnect of the signaling link to allow subcmd events + * from the signaling link to be passed up. + */ + switch (event) { + case CC_EVENT_CC_REQUEST_ACCEPT: + /* + * Received ccbsRequest/ccnrRequest response + * Before event is posted: + * Negotiated CC retention setting saved + * Negotiated signaling link retention setting saved + */ + pri_cc_act_stop_t_activate(ctrl, cc_record); + if (cc_record->fsm.qsig.msgtype == Q931_RELEASE) { + pri_cc_act_disassociate_signaling_link(ctrl, cc_record); + } + pri_cc_act_send_cc_cancel(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_CC_REQUEST_FAIL: + pri_cc_act_stop_t_activate(ctrl, cc_record); + /* + * If this request fail comes in with the RELEASE message + * then the post action will never get a chance to run. + * It will be aborted because the CC_EVENT_SIGNALING_GONE + * will be processed first. + */ + pri_cc_act_post_hangup_signaling(ctrl, cc_record); + break; + case CC_EVENT_TIMEOUT_T_ACTIVATE: + pri_cc_act_stop_t_activate(ctrl, cc_record); + pri_cc_act_hangup_signaling_link(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_SIGNALING_GONE: + /* Signaling link cleared. */ + pri_cc_act_stop_t_activate(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_HANGUP_SIGNALING: + //pri_cc_act_stop_t_activate(ctrl, cc_record); + pri_cc_act_hangup_signaling_link(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM Q.SIG monitor CC_STATE_ACTIVATED. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_qsig_monitor_activated(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_REMOTE_USER_FREE: + /* Received ccExecPossible */ + pri_cc_act_pass_up_remote_user_free(ctrl, cc_record); + /* + * ECMA-186 Section 6.5.2.1.7 + * Implied switch to retain-signaling-link. + */ + pri_cc_act_set_retain_signaling_link(ctrl, cc_record); + if (cc_record->fsm.qsig.msgtype == Q931_SETUP) { + /* Send Q931_CALL_PROCEEDING message on signaling link. */ + pri_cc_act_send_call_proceeding(ctrl, cc_record); + } + if (cc_record->party_a_status == CC_PARTY_A_AVAILABILITY_BUSY) { + /* + * The ccSuspend will be sent in a FACILITY or CONNECT + * message depending upon the CIS call state. + */ + pri_cc_act_send_cc_suspend(ctrl, cc_record); + cc_record->state = CC_STATE_SUSPENDED; + } else { + /* Start QSIG_CC_T3 */ + pri_cc_act_start_t_recall(ctrl, cc_record); + cc_record->state = CC_STATE_WAIT_CALLBACK; + } + break; + case CC_EVENT_SUSPEND: + pri_cc_act_set_a_status_busy(ctrl, cc_record); + break; + case CC_EVENT_RESUME: + pri_cc_act_reset_a_status(ctrl, cc_record); + break; + case CC_EVENT_TIMEOUT_T_SUPERVISION: + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_send_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_SIGNALING_GONE: + /* Signaling link cleared. */ + pri_cc_act_disassociate_signaling_link(ctrl, cc_record); + break; + case CC_EVENT_LINK_CANCEL: + /* Received ccCancel */ + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_post_hangup_signaling(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + cc_record->state = CC_STATE_WAIT_DESTRUCTION; + break; + case CC_EVENT_CANCEL: + pri_cc_act_send_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM Q.SIG monitor CC_STATE_WAIT_CALLBACK. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_qsig_monitor_wait_callback(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_RECALL: + /* The original call parameters have already been set. */ + pri_cc_act_queue_setup_recall(ctrl, cc_record, call); + pri_cc_act_stop_t_recall(ctrl, cc_record); + cc_record->state = CC_STATE_CALLBACK; + break; + case CC_EVENT_SUSPEND: + pri_cc_act_stop_t_recall(ctrl, cc_record); + /* + * The ccSuspend will be sent in a FACILITY or CONNECT + * message depending upon the CIS call state. + */ + pri_cc_act_send_cc_suspend(ctrl, cc_record); + cc_record->state = CC_STATE_SUSPENDED; + break; + case CC_EVENT_TIMEOUT_T_RECALL: + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_send_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_recall(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_TIMEOUT_T_SUPERVISION: + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_send_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_recall(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_SIGNALING_GONE: + /* Signaling link cleared. */ + pri_cc_act_disassociate_signaling_link(ctrl, cc_record); + break; + case CC_EVENT_LINK_CANCEL: + /* Received ccCancel */ + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_post_hangup_signaling(ctrl, cc_record); + pri_cc_act_stop_t_recall(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + cc_record->state = CC_STATE_WAIT_DESTRUCTION; + break; + case CC_EVENT_CANCEL: + pri_cc_act_send_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_recall(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM Q.SIG monitor CC_STATE_CALLBACK. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_qsig_monitor_callback(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_TIMEOUT_T_SUPERVISION: + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_send_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_SIGNALING_GONE: + /* Signaling link cleared. */ + pri_cc_act_disassociate_signaling_link(ctrl, cc_record); + break; + case CC_EVENT_LINK_CANCEL: + /* Received ccCancel */ + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_post_hangup_signaling(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + cc_record->state = CC_STATE_WAIT_DESTRUCTION; + break; + case CC_EVENT_CANCEL: + pri_cc_act_send_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM Q.SIG monitor CC_STATE_SUSPENDED. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +static void pri_cc_fsm_qsig_monitor_suspended(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + switch (event) { + case CC_EVENT_RESUME: + pri_cc_act_send_cc_resume(ctrl, cc_record); + pri_cc_act_reset_a_status(ctrl, cc_record); + cc_record->state = CC_STATE_ACTIVATED; + break; + case CC_EVENT_TIMEOUT_T_SUPERVISION: + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_send_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + case CC_EVENT_SIGNALING_GONE: + /* Signaling link cleared. */ + pri_cc_act_disassociate_signaling_link(ctrl, cc_record); + break; + case CC_EVENT_LINK_CANCEL: + /* Received ccCancel */ + pri_cc_act_pass_up_cc_cancel(ctrl, cc_record); + pri_cc_act_post_hangup_signaling(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + cc_record->state = CC_STATE_WAIT_DESTRUCTION; + break; + case CC_EVENT_CANCEL: + pri_cc_act_send_cc_cancel(ctrl, cc_record); + pri_cc_act_stop_t_supervision(ctrl, cc_record); + pri_cc_act_set_self_destruct(ctrl, cc_record); + cc_record->state = CC_STATE_IDLE; + break; + default: + break; + } +} + +/*! + * \internal + * \brief CC FSM state function type. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \return Nothing + */ +typedef void (*pri_cc_fsm_state)(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event); + +/*! CC FSM PTMP agent state table. */ +static const pri_cc_fsm_state pri_cc_fsm_ptmp_agent[CC_STATE_NUM] = { +/* *INDENT-OFF* */ + [CC_STATE_IDLE] = pri_cc_fsm_ptmp_agent_idle, + [CC_STATE_PENDING_AVAILABLE] = pri_cc_fsm_ptmp_agent_pend_avail, + [CC_STATE_AVAILABLE] = pri_cc_fsm_ptmp_agent_avail, + [CC_STATE_REQUESTED] = pri_cc_fsm_ptmp_agent_req, + [CC_STATE_ACTIVATED] = pri_cc_fsm_ptmp_agent_activated, + [CC_STATE_B_AVAILABLE] = pri_cc_fsm_ptmp_agent_b_avail, + [CC_STATE_SUSPENDED] = pri_cc_fsm_ptmp_agent_suspended, + [CC_STATE_WAIT_CALLBACK] = pri_cc_fsm_ptmp_agent_wait_callback, + [CC_STATE_CALLBACK] = pri_cc_fsm_ptmp_agent_callback, +/* *INDENT-ON* */ +}; + +/*! CC FSM PTMP monitor state table. */ +static const pri_cc_fsm_state pri_cc_fsm_ptmp_monitor[CC_STATE_NUM] = { +/* *INDENT-OFF* */ + [CC_STATE_IDLE] = pri_cc_fsm_ptmp_monitor_idle, + [CC_STATE_AVAILABLE] = pri_cc_fsm_ptmp_monitor_avail, + [CC_STATE_REQUESTED] = pri_cc_fsm_ptmp_monitor_req, + [CC_STATE_WAIT_DESTRUCTION] = pri_cc_fsm_ptmp_monitor_wait_destruction, + [CC_STATE_ACTIVATED] = pri_cc_fsm_ptmp_monitor_activated, + [CC_STATE_WAIT_CALLBACK] = pri_cc_fsm_ptmp_monitor_wait_callback, + [CC_STATE_CALLBACK] = pri_cc_fsm_ptmp_monitor_callback, +/* *INDENT-ON* */ +}; + +/*! CC FSM PTP agent state table. */ +static const pri_cc_fsm_state pri_cc_fsm_ptp_agent[CC_STATE_NUM] = { +/* *INDENT-OFF* */ + [CC_STATE_IDLE] = pri_cc_fsm_ptp_agent_idle, + [CC_STATE_PENDING_AVAILABLE] = pri_cc_fsm_ptp_agent_pend_avail, + [CC_STATE_AVAILABLE] = pri_cc_fsm_ptp_agent_avail, + [CC_STATE_REQUESTED] = pri_cc_fsm_ptp_agent_req, + [CC_STATE_ACTIVATED] = pri_cc_fsm_ptp_agent_activated, + [CC_STATE_WAIT_CALLBACK] = pri_cc_fsm_ptp_agent_wait_callback, + [CC_STATE_SUSPENDED] = pri_cc_fsm_ptp_agent_suspended, +/* *INDENT-ON* */ +}; + +/*! CC FSM PTP monitor state table. */ +static const pri_cc_fsm_state pri_cc_fsm_ptp_monitor[CC_STATE_NUM] = { +/* *INDENT-OFF* */ + [CC_STATE_IDLE] = pri_cc_fsm_ptp_monitor_idle, + [CC_STATE_AVAILABLE] = pri_cc_fsm_ptp_monitor_avail, + [CC_STATE_REQUESTED] = pri_cc_fsm_ptp_monitor_req, + [CC_STATE_WAIT_DESTRUCTION] = pri_cc_fsm_ptp_monitor_wait_destruction, + [CC_STATE_ACTIVATED] = pri_cc_fsm_ptp_monitor_activated, + [CC_STATE_WAIT_CALLBACK] = pri_cc_fsm_ptp_monitor_wait_callback, + [CC_STATE_SUSPENDED] = pri_cc_fsm_ptp_monitor_suspended, +/* *INDENT-ON* */ +}; + +/*! CC FSM Q.SIG agent state table. */ +static const pri_cc_fsm_state pri_cc_fsm_qsig_agent[CC_STATE_NUM] = { +/* *INDENT-OFF* */ + [CC_STATE_IDLE] = pri_cc_fsm_qsig_agent_idle, + [CC_STATE_AVAILABLE] = pri_cc_fsm_qsig_agent_avail, + [CC_STATE_REQUESTED] = pri_cc_fsm_qsig_agent_req, + [CC_STATE_WAIT_DESTRUCTION] = pri_cc_fsm_qsig_agent_wait_destruction, + [CC_STATE_ACTIVATED] = pri_cc_fsm_qsig_agent_activated, + [CC_STATE_WAIT_CALLBACK] = pri_cc_fsm_qsig_agent_wait_callback, + [CC_STATE_SUSPENDED] = pri_cc_fsm_qsig_agent_suspended, +/* *INDENT-ON* */ +}; + +/*! CC FSM Q.SIG monitor state table. */ +static const pri_cc_fsm_state pri_cc_fsm_qsig_monitor[CC_STATE_NUM] = { +/* *INDENT-OFF* */ + [CC_STATE_IDLE] = pri_cc_fsm_qsig_monitor_idle, + [CC_STATE_AVAILABLE] = pri_cc_fsm_qsig_monitor_avail, + [CC_STATE_REQUESTED] = pri_cc_fsm_qsig_monitor_req, + [CC_STATE_WAIT_DESTRUCTION] = pri_cc_fsm_qsig_monitor_wait_destruction, + [CC_STATE_ACTIVATED] = pri_cc_fsm_qsig_monitor_activated, + [CC_STATE_WAIT_CALLBACK] = pri_cc_fsm_qsig_monitor_wait_callback, + [CC_STATE_CALLBACK] = pri_cc_fsm_qsig_monitor_callback, + [CC_STATE_SUSPENDED] = pri_cc_fsm_qsig_monitor_suspended, +/* *INDENT-ON* */ +}; + +/*! + * \brief Send an event to the cc state machine. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * (May be NULL if it is supposed to be the signaling connection + * for Q.SIG or PTP and it is not established yet.) + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \retval nonzero if cc record destroyed because FSM completed. + */ +int pri_cc_event(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + const pri_cc_fsm_state *cc_fsm; + enum CC_STATES orig_state; + + switch (ctrl->switchtype) { + case PRI_SWITCH_QSIG: + if (cc_record->is_agent) { + cc_fsm = pri_cc_fsm_qsig_agent; + } else { + cc_fsm = pri_cc_fsm_qsig_monitor; + } + break; + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + if (PTMP_MODE(ctrl)) { + if (cc_record->is_agent) { + cc_fsm = pri_cc_fsm_ptmp_agent; + } else { + cc_fsm = pri_cc_fsm_ptmp_monitor; + } + } else { + if (cc_record->is_agent) { + cc_fsm = pri_cc_fsm_ptp_agent; + } else { + cc_fsm = pri_cc_fsm_ptp_monitor; + } + } + break; + default: + /* CC not supported on this switch type. */ + cc_fsm = NULL; + break; + } + + if (!cc_fsm) { + /* No FSM available. */ + pri_cc_delete_record(ctrl, cc_record); + return 1; + } + orig_state = cc_record->state; + if (ctrl->debug & PRI_DEBUG_CC) { + pri_message(ctrl, "%ld CC-Event: %s in state %s\n", cc_record->record_id, + pri_cc_fsm_event_str(event), pri_cc_fsm_state_str(orig_state)); + } + if (orig_state < CC_STATE_IDLE || CC_STATE_NUM <= orig_state || !cc_fsm[orig_state]) { + /* Programming error: State not implemented. */ + pri_error(ctrl, "!! CC state not implemented: %s(%d)\n", + pri_cc_fsm_state_str(orig_state), orig_state); + return 0; + } + /* Execute the state. */ + cc_fsm[orig_state](ctrl, call, cc_record, event); + if (ctrl->debug & PRI_DEBUG_CC) { + pri_message(ctrl, "%ld CC-Next-State: %s\n", cc_record->record_id, + (orig_state == cc_record->state) + ? "$" : pri_cc_fsm_state_str(cc_record->state)); + } + if (cc_record->fsm_complete) { + pri_cc_delete_record(ctrl, cc_record); + return 1; + } else { + return 0; + } +} + +/*! + * \brief Indicate to the far end that CCBS/CCNR is available. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * + * \details + * The CC available indication will go out with the next + * DISCONNECT(busy/congested)/ALERTING message. + * + * \retval cc_id on success for subsequent reference. + * \retval -1 on error. + */ +long pri_cc_available(struct pri *ctrl, q931_call *call) +{ + struct pri_cc_record *cc_record; + long cc_id; + + if (!ctrl || !call) { + return -1; + } + if (call->cc.record) { + /* This call is already associated with call completion. */ + return -1; + } + + cc_record = NULL; + + switch (ctrl->switchtype) { + case PRI_SWITCH_QSIG: + cc_record = pri_cc_new_record(ctrl, call); + if (!cc_record) { + break; + } + + /* + * Q.SIG has no message to send when CC is available. + * Q.SIG assumes CC is always available and is denied when + * requested if CC is not possible or allowed. + */ + cc_record->original_call = call; + cc_record->is_agent = 1; + break; + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + if (PTMP_MODE(ctrl)) { + int linkage_id; + + if (!BRI_NT_PTMP(ctrl)) { + /* + * No CC agent protocol defined for this mode. + * i.e., A device acting like a phone cannot be a CC agent. + */ + break; + } + + linkage_id = pri_cc_new_linkage_id(ctrl); + if (linkage_id == CC_PTMP_INVALID_ID) { + break; + } + cc_record = pri_cc_new_record(ctrl, call); + if (!cc_record) { + break; + } + cc_record->call_linkage_id = linkage_id; + cc_record->signaling = PRI_MASTER(ctrl)->dummy_call; + } else { + cc_record = pri_cc_new_record(ctrl, call); + if (!cc_record) { + break; + } + } + cc_record->original_call = call; + cc_record->is_agent = 1; + break; + default: + break; + } + + call->cc.record = cc_record; + if (cc_record && !pri_cc_event(ctrl, call, cc_record, CC_EVENT_AVAILABLE)) { + cc_id = cc_record->record_id; + } else { + cc_id = -1; + } + return cc_id; +} + +/*! + * \brief Determine if CC is available for Q.SIG outgoing call. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * + * \return Nothing + */ +void pri_cc_qsig_determine_available(struct pri *ctrl, q931_call *call) +{ + struct pri_cc_record *cc_record; + + if (!call->cc.originated || call->cc.initially_redirected) { + /* + * The call is not suitable for us to consider CC: + * The call was not originated by us. + * The call was originally redirected. + */ + return; + } + + if (!PRI_MASTER(ctrl)->cc_support) { + /* + * Blocking the cc-available event effectively + * disables call completion for outgoing calls. + */ + return; + } + if (call->cc.record) { + /* Already made available. */ + return; + } + cc_record = pri_cc_new_record(ctrl, call); + if (!cc_record) { + return; + } + cc_record->original_call = call; + call->cc.record = cc_record; + pri_cc_event(ctrl, call, cc_record, CC_EVENT_AVAILABLE); +} + +/*! + * \brief Request to activate CC. + * + * \param ctrl D channel controller. + * \param cc_id CC record ID to activate. + * \param mode Which CC mode to use CCBS(0)/CCNR(1) + * + * \note + * Will always get a reply from libpri. libpri will start a timer to guarantee + * that a reply will be passed back to the upper layer. + * \note + * If you cancel with pri_cc_cancel() you are indicating that you do not need + * the request reply and the cc_id will no longer be valid anyway. + * \note + * Allow for the possibility that the reply may come in before this + * function returns. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int pri_cc_req(struct pri *ctrl, long cc_id, int mode) +{ + struct pri_sr req; + q931_call *call; + struct pri_cc_record *cc_record; + + if (!ctrl) { + return -1; + } + cc_record = pri_cc_find_by_id(ctrl, cc_id); + if (!cc_record) { + return -1; + } + if (cc_record->is_agent || cc_record->state != CC_STATE_AVAILABLE) { + /* CC is an agent or already requested. */ + return -1; + } + + /* Set the requested CC mode. */ + cc_record->is_ccnr = mode ? 1 : 0; + + switch (ctrl->switchtype) { + case PRI_SWITCH_QSIG: + if (cc_record->signaling) { + /* We should not have a signaling link at this point. */ + return -1; + } + call = q931_new_call(ctrl); + if (!call) { + return -1; + } + + /* Link the new call as the signaling link. */ + cc_record->signaling = call; + call->cc.record = cc_record; + + if (pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST)) { + /* Should not happen. */ + q931_destroycall(ctrl, call); + break; + } + + pri_sr_init(&req); + req.caller = cc_record->party_a; + req.called = cc_record->party_b; + //req.cis_auto_disconnect = 0; + req.cis_call = 1; + if (q931_setup(ctrl, call, &req)) { + /* Should not happen. */ + q931_destroycall(ctrl, call); + return -1; + } + break; + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + if (PTMP_MODE(ctrl)) { + /* ETSI PTMP */ + if (pri_cc_event(ctrl, cc_record->signaling, cc_record, CC_EVENT_CC_REQUEST)) { + /* Should not happen. */ + break; + } + q931_facility(ctrl, cc_record->signaling); + break; + } + + /* ETSI PTP */ + if (cc_record->signaling) { + /* We should not have a signaling link at this point. */ + return -1; + } + call = q931_new_call(ctrl); + if (!call) { + return -1; + } + + cc_record->signaling = call; + call->cc.record = cc_record; + if (pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST)) { + /* Should not happen. */ + q931_destroycall(ctrl, call); + break; + } + + if (q931_register(ctrl, call)) { + /* Should not happen. */ + q931_destroycall(ctrl, call); + return -1; + } + break; + default: + return -1; + } + + return 0; +} + +/*! + * \internal + * \brief Encode a PTMP cc-request reply message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param operation CCBS/CCNR operation code. + * \param invoke_id Invoke id to put in error message response. + * \param recall_mode Configured PTMP recall mode. + * \param reference_id Active CC reference id. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_cc_etsi_ptmp_req_rsp(struct pri *ctrl, unsigned char *pos, + unsigned char *end, enum rose_operation operation, int invoke_id, int recall_mode, + int reference_id) +{ + struct rose_msg_result msg; + + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.invoke_id = invoke_id; + msg.operation = operation; + + /* CCBS/CCNR reply */ + msg.args.etsi.CCBSRequest.recall_mode = recall_mode; + msg.args.etsi.CCBSRequest.ccbs_reference = reference_id; + + pos = rose_encode_result(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode and queue PTMP a cc-request reply message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param msgtype Q.931 message type to put facility ie in. + * \param operation CCBS/CCNR operation code. + * \param invoke_id Invoke id to put in error message response. + * \param recall_mode Configured PTMP recall mode. + * \param reference_id Active CC reference id. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rose_cc_etsi_ptmp_req_rsp_encode(struct pri *ctrl, q931_call *call, int msgtype, enum rose_operation operation, int invoke_id, int recall_mode, int reference_id) +{ + unsigned char buffer[256]; + unsigned char *end; + + end = enc_cc_etsi_ptmp_req_rsp(ctrl, buffer, buffer + sizeof(buffer), operation, + invoke_id, recall_mode, reference_id); + if (!end) { + return -1; + } + + return pri_call_apdu_queue(call, msgtype, buffer, end - buffer, NULL); +} + +/*! + * \internal + * \brief Send the CC activation request result PTMP. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param operation CCBS/CCNR operation code. + * \param invoke_id Invoke id to put in error message response. + * \param recall_mode Configured PTMP recall mode. + * \param reference_id Active CC reference id. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int send_cc_etsi_ptmp_req_rsp(struct pri *ctrl, q931_call *call, enum rose_operation operation, int invoke_id, int recall_mode, int reference_id) +{ + if (rose_cc_etsi_ptmp_req_rsp_encode(ctrl, call, Q931_FACILITY, operation, invoke_id, + recall_mode, reference_id) + || q931_facility(ctrl, call)) { + pri_message(ctrl, "Could not schedule CC request result message.\n"); + return -1; + } + + return 0; +} + +/*! + * \internal + * \brief Response to an incoming CC activation request PTMP. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * \param status success(0)/timeout(1)/ + * short_term_denial(2)/long_term_denial(3)/not_subscribed(4)/queue_full(5) + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int pri_cc_req_rsp_ptmp(struct pri *ctrl, struct pri_cc_record *cc_record, int status) +{ + int fail; + + switch (cc_record->response.invoke_operation) { + case ROSE_ETSI_CCBSRequest: + case ROSE_ETSI_CCNRRequest: + break; + default: + /* We no longer know how to send the response. Should not happen. */ + return -1; + } + + fail = 0; + if (status) { + enum rose_error_code code; + + switch (status) { + default: + case 1:/* timeout */ + case 2:/* short_term_denial */ + code = ROSE_ERROR_CCBS_ShortTermDenial; + break; + case 3:/* long_term_denial */ + code = ROSE_ERROR_CCBS_LongTermDenial; + break; + case 4:/* not_subscribed */ + code = ROSE_ERROR_Gen_NotSubscribed; + break; + case 5:/* queue_full */ + code = ROSE_ERROR_CCBS_OutgoingCCBSQueueFull; + break; + } + send_facility_error(ctrl, cc_record->response.signaling, + cc_record->response.invoke_id, code); + pri_cc_event(ctrl, cc_record->response.signaling, cc_record, + CC_EVENT_CANCEL); + } else { + /* Successful CC activation. */ + if (send_cc_etsi_ptmp_req_rsp(ctrl, cc_record->response.signaling, + cc_record->response.invoke_operation, cc_record->response.invoke_id, + cc_record->option.recall_mode, cc_record->ccbs_reference_id)) { + fail = -1; + } + pri_cc_event(ctrl, cc_record->response.signaling, cc_record, + CC_EVENT_CC_REQUEST_ACCEPT); + } + return fail; +} + +/*! + * \internal + * \brief Encode a PTP cc-request reply message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param cc_record Call completion record to process event. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_cc_etsi_ptp_req_rsp(struct pri *ctrl, unsigned char *pos, + unsigned char *end, struct pri_cc_record *cc_record) +{ + struct rose_msg_result msg; + + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.invoke_id = cc_record->response.invoke_id; + msg.operation = cc_record->response.invoke_operation; + + /* CCBS/CCNR reply */ + //msg.args.etsi.CCBS_T_Request.retention_supported = 0; + + pos = rose_encode_result(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode and queue PTP a cc-request reply message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rose_cc_etsi_ptp_req_rsp_encode(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record) +{ + unsigned char buffer[256]; + unsigned char *end; + + end = enc_cc_etsi_ptp_req_rsp(ctrl, buffer, buffer + sizeof(buffer), cc_record); + if (!end) { + return -1; + } + + return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL); +} + +/*! + * \internal + * \brief Send the CC activation request result PTP. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int send_cc_etsi_ptp_req_rsp(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + if (rose_cc_etsi_ptp_req_rsp_encode(ctrl, cc_record->signaling, cc_record) + || q931_facility(ctrl, cc_record->signaling)) { + pri_message(ctrl, "Could not schedule CC request result message.\n"); + return -1; + } + + return 0; +} + +/*! + * \internal + * \brief Response to an incoming CC activation request PTP. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * \param status success(0)/timeout(1)/ + * short_term_denial(2)/long_term_denial(3)/not_subscribed(4)/queue_full(5) + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int pri_cc_req_rsp_ptp(struct pri *ctrl, struct pri_cc_record *cc_record, int status) +{ + int fail; + + switch (cc_record->response.invoke_operation) { + case ROSE_ETSI_CCBS_T_Request: + case ROSE_ETSI_CCNR_T_Request: + break; + default: + /* We no longer know how to send the response. Should not happen. */ + return -1; + } + if (!cc_record->signaling) { + return -1; + } + + fail = 0; + if (status) { + enum rose_error_code code; + + switch (status) { + default: + case 1:/* timeout */ + case 5:/* queue_full */ + case 2:/* short_term_denial */ + code = ROSE_ERROR_CCBS_T_ShortTermDenial; + break; + case 3:/* long_term_denial */ + code = ROSE_ERROR_CCBS_T_LongTermDenial; + break; + case 4:/* not_subscribed */ + code = ROSE_ERROR_Gen_NotSubscribed; + break; + } + rose_error_msg_encode(ctrl, cc_record->signaling, Q931_ANY_MESSAGE, + cc_record->response.invoke_id, code); + pri_cc_event(ctrl, cc_record->signaling, cc_record, CC_EVENT_CANCEL); + } else { + /* Successful CC activation. */ + if (send_cc_etsi_ptp_req_rsp(ctrl, cc_record)) { + fail = -1; + } + pri_cc_event(ctrl, cc_record->signaling, cc_record, CC_EVENT_CC_REQUEST_ACCEPT); + } + return fail; +} + +/*! + * \internal + * \brief Encode a Q.SIG cc-request reply message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param cc_record Call completion record to process event. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_cc_qsig_req_rsp(struct pri *ctrl, unsigned char *pos, + unsigned char *end, struct pri_cc_record *cc_record) +{ + struct fac_extension_header header; + struct rose_msg_result msg; + + memset(&header, 0, sizeof(header)); + header.nfe_present = 1; + header.nfe.source_entity = 0; /* endPINX */ + header.nfe.destination_entity = 0; /* endPINX */ + header.interpretation_present = 1; + header.interpretation = 0; /* discardAnyUnrecognisedInvokePdu */ + pos = facility_encode_header(ctrl, pos, end, &header); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.invoke_id = cc_record->response.invoke_id; + msg.operation = cc_record->response.invoke_operation; + + /* CCBS/CCNR reply */ + + /* We do not support ccPathReserve */ + msg.args.qsig.CcbsRequest.no_path_reservation = 1; + //msg.args.qsig.CcbsRequest.retain_service = 0; + + pos = rose_encode_result(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode and queue Q.SIG a cc-request reply message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param cc_record Call completion record to process event. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rose_cc_qsig_req_rsp_encode(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record) +{ + unsigned char buffer[256]; + unsigned char *end; + + end = enc_cc_qsig_req_rsp(ctrl, buffer, buffer + sizeof(buffer), cc_record); + if (!end) { + return -1; + } + + return pri_call_apdu_queue(call, Q931_ANY_MESSAGE, buffer, end - buffer, NULL); +} + +/*! + * \internal + * \brief Send the CC activation request result Q.SIG. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int send_cc_qsig_req_rsp(struct pri *ctrl, struct pri_cc_record *cc_record) +{ + struct q931_call *call; + int retval; + + /* The cc-request response goes out on either a CONNECT or RELEASE message. */ + call = cc_record->signaling; + retval = rose_cc_qsig_req_rsp_encode(ctrl, call, cc_record); + if (!retval) { + if (cc_record->option.retain_signaling_link) { + retval = q931_connect(ctrl, call, 0, 0); + } else { + pri_cc_disassociate_signaling_link(cc_record); + retval = pri_hangup(ctrl, call, -1); + } + } + if (retval) { + pri_message(ctrl, "Could not schedule CC request result message.\n"); + return -1; + } + + return 0; +} + +/*! + * \internal + * \brief Response to an incoming CC activation request Q.SIG. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * \param status success(0)/timeout(1)/ + * short_term_denial(2)/long_term_denial(3)/not_subscribed(4)/queue_full(5) + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int pri_cc_req_rsp_qsig(struct pri *ctrl, struct pri_cc_record *cc_record, int status) +{ + int fail; + + switch (cc_record->response.invoke_operation) { + case ROSE_QSIG_CcbsRequest: + case ROSE_QSIG_CcnrRequest: + break; + default: + /* We no longer know how to send the response. Should not happen. */ + return -1; + } + if (!cc_record->signaling) { + return -1; + } + + fail = 0; + if (status) { + enum rose_error_code code; + + switch (status) { + default: + case 1:/* timeout */ + case 5:/* queue_full */ + case 2:/* short_term_denial */ + code = ROSE_ERROR_QSIG_ShortTermRejection; + break; + case 4:/* not_subscribed */ + case 3:/* long_term_denial */ + code = ROSE_ERROR_QSIG_LongTermRejection; + break; + } + rose_error_msg_encode(ctrl, cc_record->signaling, Q931_ANY_MESSAGE, + cc_record->response.invoke_id, code); + pri_cc_event(ctrl, cc_record->signaling, cc_record, CC_EVENT_CANCEL); + } else { + /* Successful CC activation. */ + if (send_cc_qsig_req_rsp(ctrl, cc_record)) { + fail = -1; + } + pri_cc_event(ctrl, cc_record->signaling, cc_record, CC_EVENT_CC_REQUEST_ACCEPT); + } + return fail; +} + +/*! + * \brief Response to an incoming CC activation request. + * + * \param ctrl D channel controller. + * \param cc_id CC record ID to activate. + * \param status success(0)/timeout(1)/ + * short_term_denial(2)/long_term_denial(3)/not_subscribed(4)/queue_full(5) + * + * \note + * If the given status was failure, then the cc_id is no longer valid. + * \note + * The caller should cancel CC if error is returned. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int pri_cc_req_rsp(struct pri *ctrl, long cc_id, int status) +{ + struct pri_cc_record *cc_record; + int fail; + + if (!ctrl) { + return -1; + } + cc_record = pri_cc_find_by_id(ctrl, cc_id); + if (!cc_record) { + return -1; + } + if (!cc_record->is_agent) { + /* CC is a monitor and does not send this response event. */ + return -1; + } + + fail = -1; + switch (ctrl->switchtype) { + case PRI_SWITCH_QSIG: + if (!pri_cc_req_rsp_qsig(ctrl, cc_record, status)) { + fail = 0; + } + break; + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + if (PTMP_MODE(ctrl)) { + if (!pri_cc_req_rsp_ptmp(ctrl, cc_record, status)) { + fail = 0; + } + } else { + if (!pri_cc_req_rsp_ptp(ctrl, cc_record, status)) { + fail = 0; + } + } + break; + default: + break; + } + return fail; +} + +/*! + * \brief Indicate that the remote user (Party B) is free to call. + * The upper layer considers Party A is free. + * + * \param ctrl D channel controller. + * \param cc_id CC record ID to activate. + * + * \return Nothing + */ +void pri_cc_remote_user_free(struct pri *ctrl, long cc_id) +{ + struct pri_cc_record *cc_record; + + if (!ctrl) { + return; + } + cc_record = pri_cc_find_by_id(ctrl, cc_id); + if (!cc_record) { + return; + } + if (!cc_record->is_agent) { + /* CC is a monitor and does not send this event. */ + return; + } + + pri_cc_event(ctrl, cc_record->signaling, cc_record, CC_EVENT_REMOTE_USER_FREE); +} + +/*! + * \brief Indicate that the remote user (Party B) is free to call. + * However, the upper layer considers Party A is busy. + * + * \details + * Party B is free, but Party A is considered busy for some reason. + * This is mainly due to the upper layer experiencing congestion. + * The upper layer will be monitoring Party A until it considers + * Party A free again. + * + * \param ctrl D channel controller. + * \param cc_id CC record ID to activate. + * + * \return Nothing + */ +void pri_cc_b_free(struct pri *ctrl, long cc_id) +{ + struct pri_cc_record *cc_record; + + if (!ctrl) { + return; + } + cc_record = pri_cc_find_by_id(ctrl, cc_id); + if (!cc_record) { + return; + } + if (!cc_record->is_agent) { + /* CC is a monitor and does not send this event. */ + return; + } + + pri_cc_event(ctrl, cc_record->signaling, cc_record, CC_EVENT_B_FREE); +} + +/*! + * \brief Indicate that some other Party A has responed to the CC recall. + * + * \param ctrl D channel controller. + * \param cc_id CC record ID to activate. + * + * \return Nothing + */ +void pri_cc_stop_alerting(struct pri *ctrl, long cc_id) +{ + struct pri_cc_record *cc_record; + + if (!ctrl) { + return; + } + cc_record = pri_cc_find_by_id(ctrl, cc_id); + if (!cc_record) { + return; + } + if (!cc_record->is_agent) { + /* CC is a monitor and does not send this event. */ + return; + } + + pri_cc_event(ctrl, cc_record->signaling, cc_record, CC_EVENT_STOP_ALERTING); +} + +/*! + * \brief Poll/Ping for the status of CC party A. + * + * \param ctrl D channel controller. + * \param cc_id CC record ID to activate. + * + * \note + * There could be zero, one, or more PRI_SUBCMD_CC_STATUS_REQ_RSP responses to + * the status request depending upon how many endpoints respond to the request. + * \note + * This is expected to be called only if there are two PTMP links between + * party A and the network. (e.g., A --> * --> PSTN) + * + * \return Nothing + */ +void pri_cc_status_req(struct pri *ctrl, long cc_id) +{ + struct pri_cc_record *cc_record; + + if (!ctrl) { + return; + } + cc_record = pri_cc_find_by_id(ctrl, cc_id); + if (!cc_record) { + return; + } + if (!cc_record->is_agent) { + /* CC is a monitor and does not send this event. */ + return; + } + + pri_cc_event(ctrl, cc_record->signaling, cc_record, CC_EVENT_A_STATUS); +} + +/*! + * \internal + * \brief Encode and queue an CCBSStatusRequest result message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode CCBSStatusRequest. + * \param cc_record Call completion record to process event. + * \param is_free TRUE if the Party A status is available. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rose_ccbs_status_request_rsp(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, int is_free) +{ + unsigned char buffer[256]; + unsigned char *end; + + end = + enc_etsi_ptmp_ccbs_status_request_rsp(ctrl, buffer, buffer + sizeof(buffer), + cc_record, is_free); + if (!end) { + return -1; + } + + return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL); +} + +/*! + * \internal + * \brief Encode and send an CCBSStatusRequest result message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode CCBSStatusRequest. + * \param cc_record Call completion record to process event. + * \param is_free TRUE if the Party A status is available. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int send_ccbs_status_request_rsp(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, int is_free) +{ + if (rose_ccbs_status_request_rsp(ctrl, call, cc_record, is_free) + || q931_facility(ctrl, call)) { + pri_message(ctrl, + "Could not schedule facility message for CCBSStatusRequest result.\n"); + return -1; + } + + return 0; +} + +/*! + * \brief Update the busy status of CC party A. + * + * \param ctrl D channel controller. + * \param cc_id CC record ID to activate. + * \param status Updated party A status free(0)/busy(1) + * + * \note + * This is expected to be called only if there are two PTMP links between + * party A and the network. (e.g., A --> * --> PSTN) + * + * \return Nothing + */ +void pri_cc_status_req_rsp(struct pri *ctrl, long cc_id, int status) +{ + struct pri_cc_record *cc_record; + + if (!ctrl) { + return; + } + cc_record = pri_cc_find_by_id(ctrl, cc_id); + if (!cc_record) { + return; + } + if (cc_record->is_agent) { + /* CC is an agent and does not send this response event. */ + return; + } + + switch (ctrl->switchtype) { + case PRI_SWITCH_QSIG: + /* Does not apply. */ + break; + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + if (PTMP_MODE(ctrl)) { + if (cc_record->response.invoke_operation != ROSE_ETSI_CCBSStatusRequest) { + /* We no longer know how to send the response. */ + break; + } + send_ccbs_status_request_rsp(ctrl, cc_record->signaling, cc_record, + status ? 0 /* busy */ : 1 /* free */); + } + break; + default: + break; + } +} + +/*! + * \brief Update the busy status of CC party A. + * + * \param ctrl D channel controller. + * \param cc_id CC record ID to activate. + * \param status Updated party A status free(0)/busy(1) + * + * \note + * Party A status is used to suspend/resume monitoring party B. + * + * \return Nothing + */ +void pri_cc_status(struct pri *ctrl, long cc_id, int status) +{ + struct pri_cc_record *cc_record; + + if (!ctrl) { + return; + } + cc_record = pri_cc_find_by_id(ctrl, cc_id); + if (!cc_record) { + return; + } + if (cc_record->is_agent) { + /* CC is an agent and does not send this event. */ + return; + } + + pri_cc_event(ctrl, cc_record->signaling, cc_record, + status ? CC_EVENT_SUSPEND : CC_EVENT_RESUME); +} + +/*! + * \brief Initiate the CC callback call. + * + * \param ctrl D channel controller. + * \param cc_id CC record ID to activate. + * \param call Q.931 call leg. + * \param req SETUP request parameters. Parameters saved by CC will override. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int pri_cc_call(struct pri *ctrl, long cc_id, q931_call *call, struct pri_sr *req) +{ + struct pri_cc_record *cc_record; + + if (!ctrl || !call || !req) { + return -1; + } + cc_record = pri_cc_find_by_id(ctrl, cc_id); + if (!cc_record) { + return -1; + } + if (cc_record->is_agent) { + /* CC is an agent and does not initiate callbacks. */ + return -1; + } + + /* Override parameters for sending recall. */ + req->caller = cc_record->party_a; + req->called = cc_record->party_b; + req->transmode = cc_record->bc.transcapability; + req->userl1 = cc_record->bc.userl1; + + /* + * The caller is allowed to send different user-user information. + * + * It makes no sense for the caller to supply redirecting information + * but we'll allow it to pass anyway. + */ + //q931_party_redirecting_init(&req->redirecting); + + /* Add switch specific recall APDU to call. */ + pri_cc_event(ctrl, call, cc_record, CC_EVENT_RECALL); + + if (q931_setup(ctrl, call, req)) { + return -1; + } + return 0; +} + +/*! + * \brief Unsolicited indication that CC is cancelled. + * + * \param ctrl D channel controller. + * \param cc_id CC record ID to deactivate. + * + * \return Nothing. The cc_id is no longer valid. + */ +void pri_cc_cancel(struct pri *ctrl, long cc_id) +{ + struct pri_cc_record *cc_record; + + if (!ctrl) { + return; + } + cc_record = pri_cc_find_by_id(ctrl, cc_id); + if (!cc_record) { + return; + } + pri_cc_event(ctrl, cc_record->signaling, cc_record, CC_EVENT_CANCEL); +} + +/* ------------------------------------------------------------------- */ +/* end pri_cc.c */ diff --git a/pri_facility.c b/pri_facility.c index 1539d19..9035848 100644 --- a/pri_facility.c +++ b/pri_facility.c @@ -30,20 +30,21 @@ #include "compat.h" #include "libpri.h" #include "pri_internal.h" -#include "pri_q921.h" -#include "pri_q931.h" #include "pri_facility.h" -#include "rose.h" #include #include #include #include -static short get_invokeid(struct pri *ctrl) +const char *pri_facility_error2str(int facility_error_code) { - ctrl = PRI_MASTER(ctrl); - return ++ctrl->last_invoke; + return rose_error2str(facility_error_code); +} + +const char *pri_facility_reject2str(int facility_reject_code) +{ + return rose_reject2str(facility_reject_code); } static int redirectingreason_from_q931(struct pri *ctrl, int redirectingreason) @@ -171,6 +172,7 @@ static int redirectingreason_for_q931(struct pri *ctrl, int redirectingreason) } /*! + * \internal * \brief Convert the Q.931 type-of-number field to facility. * * \param ctrl D channel controller for diagnostic messages or global options. @@ -497,7 +499,6 @@ static int presentation_to_subscription(struct pri *ctrl, int presentation) } /*! - * \internal * \brief Copy the given rose party number to the q931_party_number * * \param ctrl D channel controller for diagnostic messages or global options. @@ -508,8 +509,8 @@ static int presentation_to_subscription(struct pri *ctrl, int presentation) * * \return Nothing */ -static void rose_copy_number_to_q931(struct pri *ctrl, - struct q931_party_number *q931_number, const struct rosePartyNumber *rose_number) +void rose_copy_number_to_q931(struct pri *ctrl, struct q931_party_number *q931_number, + const struct rosePartyNumber *rose_number) { //q931_party_number_init(q931_number); libpri_copy_string(q931_number->str, (char *) rose_number->str, @@ -520,7 +521,6 @@ static void rose_copy_number_to_q931(struct pri *ctrl, } /*! - * \internal * \brief Copy the given rose subaddress to the q931_party_subaddress. * * \param ctrl D channel controller for diagnostic messages or global options. @@ -531,7 +531,7 @@ static void rose_copy_number_to_q931(struct pri *ctrl, * * \return Nothing */ -static void rose_copy_subaddress_to_q931(struct pri *ctrl, +void rose_copy_subaddress_to_q931(struct pri *ctrl, struct q931_party_subaddress *q931_subaddress, const struct rosePartySubaddress *rose_subaddress) { @@ -571,7 +571,25 @@ static void rose_copy_subaddress_to_q931(struct pri *ctrl, } /*! - * \internal + * \brief Copy the given rose address to the q931_party_address. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param q931_address Q.931 party address structure + * \param rose_address ROSE address structure + * + * \note It is assumed that the q931_address has been initialized before calling. + * + * \return Nothing + */ +void rose_copy_address_to_q931(struct pri *ctrl, struct q931_party_address *q931_address, + const struct roseAddress *rose_address) +{ + rose_copy_number_to_q931(ctrl, &q931_address->number, &rose_address->number); + rose_copy_subaddress_to_q931(ctrl, &q931_address->subaddress, + &rose_address->subaddress); +} + +/*! * \brief Copy the given rose address to the q931_party_id address. * * \param ctrl D channel controller for diagnostic messages or global options. @@ -582,8 +600,8 @@ static void rose_copy_subaddress_to_q931(struct pri *ctrl, * * \return Nothing */ -static void rose_copy_address_to_q931(struct pri *ctrl, - struct q931_party_id *q931_address, const struct roseAddress *rose_address) +void rose_copy_address_to_id_q931(struct pri *ctrl, struct q931_party_id *q931_address, + const struct roseAddress *rose_address) { rose_copy_number_to_q931(ctrl, &q931_address->number, &rose_address->number); rose_copy_subaddress_to_q931(ctrl, &q931_address->subaddress, @@ -591,7 +609,6 @@ static void rose_copy_address_to_q931(struct pri *ctrl, } /*! - * \internal * \brief Copy the given rose presented screened party number to the q931_party_number * * \param ctrl D channel controller for diagnostic messages or global options. @@ -600,7 +617,7 @@ static void rose_copy_address_to_q931(struct pri *ctrl, * * \return Nothing */ -static void rose_copy_presented_number_screened_to_q931(struct pri *ctrl, +void rose_copy_presented_number_screened_to_q931(struct pri *ctrl, struct q931_party_number *q931_number, const struct rosePresentedNumberScreened *rose_presented) { @@ -622,7 +639,6 @@ static void rose_copy_presented_number_screened_to_q931(struct pri *ctrl, } /*! - * \internal * \brief Copy the given rose presented unscreened party number to the q931_party_number * * \param ctrl D channel controller for diagnostic messages or global options. @@ -631,7 +647,7 @@ static void rose_copy_presented_number_screened_to_q931(struct pri *ctrl, * * \return Nothing */ -static void rose_copy_presented_number_unscreened_to_q931(struct pri *ctrl, +void rose_copy_presented_number_unscreened_to_q931(struct pri *ctrl, struct q931_party_number *q931_number, const struct rosePresentedNumberUnscreened *rose_presented) { @@ -650,7 +666,6 @@ static void rose_copy_presented_number_unscreened_to_q931(struct pri *ctrl, } /*! - * \internal * \brief Copy the given rose presented screened party address to the q931_party_number * * \param ctrl D channel controller for diagnostic messages or global options. @@ -659,7 +674,7 @@ static void rose_copy_presented_number_unscreened_to_q931(struct pri *ctrl, * * \return Nothing */ -static void rose_copy_presented_address_screened_to_q931(struct pri *ctrl, +void rose_copy_presented_address_screened_to_id_q931(struct pri *ctrl, struct q931_party_id *q931_address, const struct rosePresentedAddressScreened *rose_presented) { @@ -685,7 +700,6 @@ static void rose_copy_presented_address_screened_to_q931(struct pri *ctrl, } /*! - * \internal * \brief Copy the given rose party name to the q931_party_name * * \param ctrl D channel controller for diagnostic messages or global options. @@ -694,8 +708,8 @@ static void rose_copy_presented_address_screened_to_q931(struct pri *ctrl, * * \return Nothing */ -static void rose_copy_name_to_q931(struct pri *ctrl, - struct q931_party_name *qsig_name, const struct roseQsigName *rose_name) +void rose_copy_name_to_q931(struct pri *ctrl, struct q931_party_name *qsig_name, + const struct roseQsigName *rose_name) { //q931_party_name_init(qsig_name); qsig_name->valid = 1; @@ -706,7 +720,6 @@ static void rose_copy_name_to_q931(struct pri *ctrl, } /*! - * \internal * \brief Copy the given q931_party_number to the rose party number * * \param ctrl D channel controller for diagnostic messages or global options. @@ -715,8 +728,8 @@ static void rose_copy_name_to_q931(struct pri *ctrl, * * \return Nothing */ -static void q931_copy_number_to_rose(struct pri *ctrl, - struct rosePartyNumber *rose_number, const struct q931_party_number *q931_number) +void q931_copy_number_to_rose(struct pri *ctrl, struct rosePartyNumber *rose_number, + const struct q931_party_number *q931_number) { rose_number->plan = numbering_plan_from_q931(ctrl, q931_number->plan); rose_number->ton = typeofnumber_from_q931(ctrl, q931_number->plan); @@ -727,7 +740,6 @@ static void q931_copy_number_to_rose(struct pri *ctrl, } /*! - * \internal * \brief Copy the given q931_party_subaddress to the rose subaddress. * * \param ctrl D channel controller for diagnostic messages or global options. @@ -736,7 +748,7 @@ static void q931_copy_number_to_rose(struct pri *ctrl, * * \return Nothing */ -static void q931_copy_subaddress_to_rose(struct pri *ctrl, +void q931_copy_subaddress_to_rose(struct pri *ctrl, struct rosePartySubaddress *rose_subaddress, const struct q931_party_subaddress *q931_subaddress) { @@ -778,7 +790,23 @@ static void q931_copy_subaddress_to_rose(struct pri *ctrl, } /*! - * \internal + * \brief Copy the given q931_party_address to the rose address. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param rose_address ROSE address structure + * \param q931_address Q.931 party address structure + * + * \return Nothing + */ +void q931_copy_address_to_rose(struct pri *ctrl, struct roseAddress *rose_address, + const struct q931_party_address *q931_address) +{ + q931_copy_number_to_rose(ctrl, &rose_address->number, &q931_address->number); + q931_copy_subaddress_to_rose(ctrl, &rose_address->subaddress, + &q931_address->subaddress); +} + +/*! * \brief Copy the given q931_party_id address to the rose address. * * \param ctrl D channel controller for diagnostic messages or global options. @@ -787,7 +815,7 @@ static void q931_copy_subaddress_to_rose(struct pri *ctrl, * * \return Nothing */ -static void q931_copy_address_to_rose(struct pri *ctrl, struct roseAddress *rose_address, +void q931_copy_id_address_to_rose(struct pri *ctrl, struct roseAddress *rose_address, const struct q931_party_id *q931_address) { q931_copy_number_to_rose(ctrl, &rose_address->number, &q931_address->number); @@ -796,7 +824,6 @@ static void q931_copy_address_to_rose(struct pri *ctrl, struct roseAddress *rose } /*! - * \internal * \brief Copy the given q931_party_number to the rose presented screened party number * * \param ctrl D channel controller for diagnostic messages or global options. @@ -805,7 +832,7 @@ static void q931_copy_address_to_rose(struct pri *ctrl, struct roseAddress *rose * * \return Nothing */ -static void q931_copy_presented_number_screened_to_rose(struct pri *ctrl, +void q931_copy_presented_number_screened_to_rose(struct pri *ctrl, struct rosePresentedNumberScreened *rose_presented, const struct q931_party_number *q931_number) { @@ -821,7 +848,6 @@ static void q931_copy_presented_number_screened_to_rose(struct pri *ctrl, } /*! - * \internal * \brief Copy the given q931_party_number to the rose presented unscreened party number * * \param ctrl D channel controller for diagnostic messages or global options. @@ -830,7 +856,7 @@ static void q931_copy_presented_number_screened_to_rose(struct pri *ctrl, * * \return Nothing */ -static void q931_copy_presented_number_unscreened_to_rose(struct pri *ctrl, +void q931_copy_presented_number_unscreened_to_rose(struct pri *ctrl, struct rosePresentedNumberUnscreened *rose_presented, const struct q931_party_number *q931_number) { @@ -843,9 +869,7 @@ static void q931_copy_presented_number_unscreened_to_rose(struct pri *ctrl, } } -#if 0 /* In case it is needed in the future */ /*! - * \internal * \brief Copy the given q931_party_number to the rose presented screened party address * * \param ctrl D channel controller for diagnostic messages or global options. @@ -854,7 +878,7 @@ static void q931_copy_presented_number_unscreened_to_rose(struct pri *ctrl, * * \return Nothing */ -static void q931_copy_presented_address_screened_to_rose(struct pri *ctrl, +void q931_copy_presented_id_address_screened_to_rose(struct pri *ctrl, struct rosePresentedAddressScreened *rose_presented, const struct q931_party_id *q931_address) { @@ -872,10 +896,8 @@ static void q931_copy_presented_address_screened_to_rose(struct pri *ctrl, rose_presented->presentation = 2;/* numberNotAvailableDueToInterworking */ } } -#endif /* In case it is needed in the future */ /*! - * \internal * \brief Copy the given q931_party_name to the rose party name * * \param ctrl D channel controller for diagnostic messages or global options. @@ -884,8 +906,8 @@ static void q931_copy_presented_address_screened_to_rose(struct pri *ctrl, * * \return Nothing */ -static void q931_copy_name_to_rose(struct pri *ctrl, - struct roseQsigName *rose_name, const struct q931_party_name *qsig_name) +void q931_copy_name_to_rose(struct pri *ctrl, struct roseQsigName *rose_name, + const struct q931_party_name *qsig_name) { if (qsig_name->valid) { rose_name->presentation = qsig_name_presentation_from_q931(ctrl, @@ -1841,7 +1863,7 @@ static unsigned char *enc_qsig_call_rerouting(struct pri *ctrl, unsigned char *p redirectingreason_from_q931(ctrl, deflection->reason); /* calledAddress is the passed in deflection->to address */ - q931_copy_address_to_rose(ctrl, &msg.args.qsig.CallRerouting.called, &deflection->to); + q931_copy_id_address_to_rose(ctrl, &msg.args.qsig.CallRerouting.called, &deflection->to); msg.args.qsig.CallRerouting.diversion_counter = deflection->count; @@ -1849,7 +1871,7 @@ static unsigned char *enc_qsig_call_rerouting(struct pri *ctrl, unsigned char *p q931ie_pos = msg.args.qsig.CallRerouting.q931ie_contents; *q931ie_pos++ = 0x04; /* Bearer Capability IE */ *q931ie_pos++ = 0x03; /* len */ - *q931ie_pos++ = 0x80 | call->transcapability; /* Rxed transfer capability. */ + *q931ie_pos++ = 0x80 | call->bc.transcapability; /* Rxed transfer capability. */ *q931ie_pos++ = 0x90; /* circuit mode, 64kbit/s */ *q931ie_pos++ = 0xa3; /* level1 protocol, a-law */ *q931ie_pos++ = 0x95; /* locking shift to codeset 5 (national use) */ @@ -1953,7 +1975,7 @@ static unsigned char *enc_etsi_call_rerouting(struct pri *ctrl, unsigned char *p redirectingreason_from_q931(ctrl, deflection->reason); /* calledAddress is the passed in deflection->to address */ - q931_copy_address_to_rose(ctrl, &msg.args.etsi.CallRerouting.called_address, + q931_copy_id_address_to_rose(ctrl, &msg.args.etsi.CallRerouting.called_address, &deflection->to); msg.args.etsi.CallRerouting.rerouting_counter = deflection->count; @@ -1962,7 +1984,7 @@ static unsigned char *enc_etsi_call_rerouting(struct pri *ctrl, unsigned char *p q931ie_pos = msg.args.etsi.CallRerouting.q931ie_contents; *q931ie_pos++ = 0x04; /* Bearer Capability IE */ *q931ie_pos++ = 0x03; /* len */ - *q931ie_pos++ = 0x80 | call->transcapability; /* Rxed transfer capability. */ + *q931ie_pos++ = 0x80 | call->bc.transcapability; /* Rxed transfer capability. */ *q931ie_pos++ = 0x90; /* circuit mode, 64kbit/s */ *q931ie_pos++ = 0xa3; /* level1 protocol, a-law */ msg.args.etsi.CallRerouting.q931ie.length = q931ie_pos @@ -2014,7 +2036,7 @@ static unsigned char *enc_etsi_call_deflection(struct pri *ctrl, unsigned char * msg.invoke_id = get_invokeid(ctrl); /* deflectionAddress is the passed in deflection->to address */ - q931_copy_address_to_rose(ctrl, &msg.args.etsi.CallDeflection.deflection, + q931_copy_id_address_to_rose(ctrl, &msg.args.etsi.CallDeflection.deflection, deflection); msg.args.etsi.CallDeflection.presentation_allowed_to_diverted_to_user_present = 1; @@ -2766,13 +2788,15 @@ void pri_call_apdu_queue_cleanup(q931_call *call) call->apdus = NULL; while (cur_event) { if (cur_event->response.callback) { + /* Stop any response timeout. */ + pri_schedule_del(call->pri, cur_event->timer); + cur_event->timer = 0; + /* Indicate to callback that the APDU is being cleaned up. */ cur_event->response.callback(APDU_CALLBACK_REASON_CLEANUP, call->pri, call, cur_event, NULL); - - /* Stop any response timeout. */ - pri_schedule_del(call->pri, cur_event->timer); } + free_event = cur_event; cur_event = cur_event->next; free(free_event); @@ -2781,7 +2805,6 @@ void pri_call_apdu_queue_cleanup(q931_call *call) } /*! - * \internal * \brief Find an outstanding APDU with the given invoke id. * * \param call Call to find APDU. @@ -2790,10 +2813,14 @@ void pri_call_apdu_queue_cleanup(q931_call *call) * \retval apdu_event if found. * \retval NULL if not found. */ -static struct apdu_event *pri_call_apdu_find(struct q931_call *call, int invoke_id) +struct apdu_event *pri_call_apdu_find(struct q931_call *call, int invoke_id) { struct apdu_event *apdu; + if (invoke_id == APDU_INVALID_INVOKE_ID) { + /* No need to search the list since it cannot be in there. */ + return NULL; + } for (apdu = call->apdus; apdu; apdu = apdu->next) { /* * Note: The APDU cannot be sent and still in the queue without a @@ -2807,6 +2834,41 @@ static struct apdu_event *pri_call_apdu_find(struct q931_call *call, int invoke_ return apdu; } +/*! + * \brief Extract the given APDU event from the given call. + * + * \param call Call to remove the APDU. + * \param extract APDU event to extract. + * + * \retval TRUE on success. + * \retval FALSE on error. + */ +int pri_call_apdu_extract(struct q931_call *call, struct apdu_event *extract) +{ + struct apdu_event **prev; + struct apdu_event *cur; + + /* Find APDU in list. */ + for (prev = &call->apdus, cur = call->apdus; + cur; + prev = &cur->next, cur = cur->next) { + if (cur == extract) { + /* Stop any response timeout. */ + pri_schedule_del(call->pri, cur->timer); + cur->timer = 0; + + /* Remove APDU from list. */ + *prev = cur->next; + + /* Found and extracted APDU from list. */ + return 1; + } + } + + /* Did not find the APDU in the list. */ + return 0; +} + /*! * \brief Delete the given APDU event from the given call. * @@ -2817,22 +2879,8 @@ static struct apdu_event *pri_call_apdu_find(struct q931_call *call, int invoke_ */ void pri_call_apdu_delete(struct q931_call *call, struct apdu_event *doomed) { - struct apdu_event **prev; - struct apdu_event *cur; - - /* Find APDU in list. */ - for (prev = &call->apdus, cur = call->apdus; - cur; - prev = &cur->next, cur = cur->next) { - if (cur == doomed) { - /* Stop any response timeout. */ - pri_schedule_del(call->pri, cur->timer); - - /* Remove APDU from list. */ - *prev = cur->next; - free(cur); - break; - } + if (pri_call_apdu_extract(call, doomed)) { + free(doomed); } } @@ -2985,18 +3033,18 @@ static unsigned char *enc_qsig_error(struct pri *ctrl, unsigned char *pos, } /*! - * \internal * \brief Encode and queue a plain facility error code. * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Call leg from which to encode error message response. + * \param msgtype Q.931 message type to put facility ie in. * \param invoke_id Invoke id to put in error message response. * \param code Error code to put in error message response. * * \retval 0 on success. * \retval -1 on error. */ -static int rose_facility_error_encode(struct pri *ctrl, q931_call *call, int invoke_id, +int rose_error_msg_encode(struct pri *ctrl, q931_call *call, int msgtype, int invoke_id, enum rose_error_code code) { unsigned char buffer[256]; @@ -3019,7 +3067,7 @@ static int rose_facility_error_encode(struct pri *ctrl, q931_call *call, int inv return -1; } - return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL); + return pri_call_apdu_queue(call, msgtype, buffer, end - buffer, NULL); } /*! @@ -3033,10 +3081,10 @@ static int rose_facility_error_encode(struct pri *ctrl, q931_call *call, int inv * \retval 0 on success. * \retval -1 on error. */ -static int send_facility_error(struct pri *ctrl, q931_call *call, int invoke_id, +int send_facility_error(struct pri *ctrl, q931_call *call, int invoke_id, enum rose_error_code code) { - if (rose_facility_error_encode(ctrl, call, invoke_id, code) + if (rose_error_msg_encode(ctrl, call, Q931_FACILITY, invoke_id, code) || q931_facility(ctrl, call)) { pri_message(ctrl, "Could not schedule facility message for error message.\n"); @@ -3118,7 +3166,6 @@ static unsigned char *enc_qsig_result_ok(struct pri *ctrl, unsigned char *pos, } /*! - * \internal * \brief Encode and queue a plain ROSE result ok. * * \param ctrl D channel controller for diagnostic messages or global options. @@ -3129,7 +3176,7 @@ static unsigned char *enc_qsig_result_ok(struct pri *ctrl, unsigned char *pos, * \retval 0 on success. * \retval -1 on error. */ -static int rose_result_ok_encode(struct pri *ctrl, q931_call *call, int msgtype, int invoke_id) +int rose_result_ok_encode(struct pri *ctrl, q931_call *call, int msgtype, int invoke_id) { unsigned char buffer[256]; unsigned char *end; @@ -3164,7 +3211,7 @@ static int rose_result_ok_encode(struct pri *ctrl, q931_call *call, int msgtype, * \retval 0 on success. * \retval -1 on error. */ -static int send_facility_result_ok(struct pri *ctrl, q931_call *call, int invoke_id) +int send_facility_result_ok(struct pri *ctrl, q931_call *call, int invoke_id) { if (rose_result_ok_encode(ctrl, call, Q931_FACILITY, invoke_id) || q931_facility(ctrl, call)) { @@ -3234,15 +3281,18 @@ int pri_rerouting_rsp(struct pri *ctrl, q931_call *call, int invoke_id, enum PRI void rose_handle_reject(struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, const struct fac_extension_header *header, const struct rose_msg_reject *reject) { + q931_call *orig_call; struct apdu_event *apdu; - union apdu_msg_data msg; + struct apdu_msg_data msg; - /* Gripe to the user about getting rejected. */ - pri_error(ctrl, "ROSE REJECT:\n"); - if (reject->invoke_id_present) { - pri_error(ctrl, "\tINVOKE ID: %d\n", reject->invoke_id); + if (ctrl->debug & PRI_DEBUG_APDU) { + /* Gripe to the user about getting rejected. */ + pri_message(ctrl, "ROSE REJECT:\n"); + if (reject->invoke_id_present) { + pri_message(ctrl, "\tINVOKE ID: %d\n", reject->invoke_id); + } + pri_message(ctrl, "\tPROBLEM: %s\n", rose_reject2str(reject->code)); } - pri_error(ctrl, "\tPROBLEM: %s\n", rose_reject2str(reject->code)); switch (ctrl->switchtype) { case PRI_SWITCH_DMS100: @@ -3259,13 +3309,31 @@ void rose_handle_reject(struct pri *ctrl, q931_call *call, int msgtype, q931_ie */ return; } - apdu = pri_call_apdu_find(call, reject->invoke_id); - if (!apdu) { - return; + orig_call = NULL;/* To make some compilers happy. */ + apdu = NULL; + if (q931_is_dummy_call(call)) { + /* + * The message was likely sent on the broadcast dummy call reference call + * and the reject came in on a specific dummy call reference call. + * Look for the original invocation message on the + * broadcast dummy call reference call first. + */ + orig_call = PRI_MASTER(ctrl)->dummy_call; + if (orig_call) { + apdu = pri_call_apdu_find(orig_call, reject->invoke_id); + } } - msg.reject = reject; + if (!apdu) { + apdu = pri_call_apdu_find(call, reject->invoke_id); + if (!apdu) { + return; + } + orig_call = call; + } + msg.response.reject = reject; + msg.type = msgtype; if (apdu->response.callback(APDU_CALLBACK_REASON_MSG_REJECT, ctrl, call, apdu, &msg)) { - pri_call_apdu_delete(call, apdu); + pri_call_apdu_delete(orig_call, apdu); } } @@ -3285,34 +3353,37 @@ void rose_handle_error(struct pri *ctrl, q931_call *call, int msgtype, q931_ie * const struct fac_extension_header *header, const struct rose_msg_error *error) { const char *dms100_operation; + q931_call *orig_call; struct apdu_event *apdu; - union apdu_msg_data msg; + struct apdu_msg_data msg; - /* Gripe to the user about getting an error. */ - pri_error(ctrl, "ROSE RETURN ERROR:\n"); - switch (ctrl->switchtype) { - case PRI_SWITCH_DMS100: - switch (error->invoke_id) { - case ROSE_DMS100_RLT_OPERATION_IND: - dms100_operation = "RLT_OPERATION_IND"; - break; - case ROSE_DMS100_RLT_THIRD_PARTY: - dms100_operation = "RLT_THIRD_PARTY"; - break; + if (ctrl->debug & PRI_DEBUG_APDU) { + /* Gripe to the user about getting an error. */ + pri_message(ctrl, "ROSE RETURN ERROR:\n"); + switch (ctrl->switchtype) { + case PRI_SWITCH_DMS100: + switch (error->invoke_id) { + case ROSE_DMS100_RLT_OPERATION_IND: + dms100_operation = "RLT_OPERATION_IND"; + break; + case ROSE_DMS100_RLT_THIRD_PARTY: + dms100_operation = "RLT_THIRD_PARTY"; + break; + default: + dms100_operation = NULL; + break; + } + if (dms100_operation) { + pri_message(ctrl, "\tOPERATION: %s\n", dms100_operation); + break; + } + /* fall through */ default: - dms100_operation = NULL; + pri_message(ctrl, "\tINVOKE ID: %d\n", error->invoke_id); break; } - if (dms100_operation) { - pri_error(ctrl, "\tOPERATION: %s\n", dms100_operation); - break; - } - /* fall through */ - default: - pri_error(ctrl, "\tINVOKE ID: %d\n", error->invoke_id); - break; + pri_message(ctrl, "\tERROR: %s\n", rose_error2str(error->code)); } - pri_error(ctrl, "\tERROR: %s\n", rose_error2str(error->code)); switch (ctrl->switchtype) { case PRI_SWITCH_DMS100: @@ -3322,13 +3393,31 @@ void rose_handle_error(struct pri *ctrl, q931_call *call, int msgtype, q931_ie * break; } - apdu = pri_call_apdu_find(call, error->invoke_id); - if (!apdu) { - return; + orig_call = NULL;/* To make some compilers happy. */ + apdu = NULL; + if (q931_is_dummy_call(call)) { + /* + * The message was likely sent on the broadcast dummy call reference call + * and the error came in on a specific dummy call reference call. + * Look for the original invocation message on the + * broadcast dummy call reference call first. + */ + orig_call = PRI_MASTER(ctrl)->dummy_call; + if (orig_call) { + apdu = pri_call_apdu_find(orig_call, error->invoke_id); + } } - msg.error = error; + if (!apdu) { + apdu = pri_call_apdu_find(call, error->invoke_id); + if (!apdu) { + return; + } + orig_call = call; + } + msg.response.error = error; + msg.type = msgtype; if (apdu->response.callback(APDU_CALLBACK_REASON_MSG_ERROR, ctrl, call, apdu, &msg)) { - pri_call_apdu_delete(call, apdu); + pri_call_apdu_delete(orig_call, apdu); } } @@ -3347,8 +3436,9 @@ void rose_handle_error(struct pri *ctrl, q931_call *call, int msgtype, q931_ie * void rose_handle_result(struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, const struct fac_extension_header *header, const struct rose_msg_result *result) { + q931_call *orig_call; struct apdu_event *apdu; - union apdu_msg_data msg; + struct apdu_msg_data msg; switch (ctrl->switchtype) { case PRI_SWITCH_DMS100: @@ -3379,13 +3469,31 @@ void rose_handle_result(struct pri *ctrl, q931_call *call, int msgtype, q931_ie break; } - apdu = pri_call_apdu_find(call, result->invoke_id); - if (!apdu) { - return; + orig_call = NULL;/* To make some compilers happy. */ + apdu = NULL; + if (q931_is_dummy_call(call)) { + /* + * The message was likely sent on the broadcast dummy call reference call + * and the result came in on a specific dummy call reference call. + * Look for the original invocation message on the + * broadcast dummy call reference call first. + */ + orig_call = PRI_MASTER(ctrl)->dummy_call; + if (orig_call) { + apdu = pri_call_apdu_find(orig_call, result->invoke_id); + } } - msg.result = result; + if (!apdu) { + apdu = pri_call_apdu_find(call, result->invoke_id); + if (!apdu) { + return; + } + orig_call = call; + } + msg.response.result = result; + msg.type = msgtype; if (apdu->response.callback(APDU_CALLBACK_REASON_MSG_RESULT, ctrl, call, apdu, &msg)) { - pri_call_apdu_delete(call, apdu); + pri_call_apdu_delete(orig_call, apdu); } } @@ -3404,8 +3512,10 @@ void rose_handle_result(struct pri *ctrl, q931_call *call, int msgtype, q931_ie void rose_handle_invoke(struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, const struct fac_extension_header *header, const struct rose_msg_invoke *invoke) { + struct pri_cc_record *cc_record; struct pri_subcommand *subcmd; struct q931_party_id party_id; + struct q931_party_address party_address; struct q931_party_redirecting deflection; switch (invoke->operation) { @@ -3449,7 +3559,6 @@ void rose_handle_invoke(struct pri *ctrl, q931_call *call, int msgtype, q931_ie */ send_facility_error(ctrl, call, invoke->invoke_id, ROSE_ERROR_Gen_ResourceUnavailable); - pri_error(ctrl, "ERROR: Too many facility subcommands\n"); break; } @@ -3470,7 +3579,7 @@ void rose_handle_invoke(struct pri *ctrl, q931_call *call, int msgtype, q931_ie } /* Deflecting to the new address. */ - rose_copy_address_to_q931(ctrl, &deflection.to, + rose_copy_address_to_id_q931(ctrl, &deflection.to, &invoke->args.etsi.CallDeflection.deflection); deflection.to.number.presentation = deflection.from.number.presentation; @@ -3502,7 +3611,6 @@ void rose_handle_invoke(struct pri *ctrl, q931_call *call, int msgtype, q931_ie if (!subcmd) { send_facility_error(ctrl, call, invoke->invoke_id, ROSE_ERROR_Gen_ResourceUnavailable); - pri_error(ctrl, "ERROR: Too many facility subcommands\n"); break; } @@ -3513,7 +3621,7 @@ void rose_handle_invoke(struct pri *ctrl, q931_call *call, int msgtype, q931_ie &invoke->args.etsi.CallRerouting.last_rerouting); /* Rerouting to the new address. */ - rose_copy_address_to_q931(ctrl, &deflection.to, + rose_copy_address_to_id_q931(ctrl, &deflection.to, &invoke->args.etsi.CallRerouting.called_address); switch (invoke->args.etsi.CallRerouting.subscription_option) { default: @@ -3655,7 +3763,6 @@ void rose_handle_invoke(struct pri *ctrl, q931_call *call, int msgtype, q931_ie call->redirecting.state = Q931_REDIRECTING_STATE_IDLE; subcmd = q931_alloc_subcommand(ctrl); if (!subcmd) { - pri_error(ctrl, "ERROR: Too many facility subcommands\n"); break; } /* Setup redirecting subcommand */ @@ -3741,6 +3848,246 @@ void rose_handle_invoke(struct pri *ctrl, q931_call *call, int msgtype, q931_ie case ROSE_ETSI_EctLoopTest: break; #endif /* Not handled yet */ +#if defined(STATUS_REQUEST_PLACE_HOLDER) + case ROSE_ETSI_StatusRequest: + /* Not handled yet */ + break; +#endif /* defined(STATUS_REQUEST_PLACE_HOLDER) */ + case ROSE_ETSI_CallInfoRetain: + if (!PRI_MASTER(ctrl)->cc_support) { + /* + * Blocking the cc-available event effectively + * disables call completion for outgoing calls. + */ + break; + } + if (call->cc.record) { + /* Duplicate message! Should not happen. */ + break; + } + cc_record = pri_cc_new_record(ctrl, call); + if (!cc_record) { + break; + } + cc_record->signaling = PRI_MASTER(ctrl)->dummy_call; + /* + * Since we received this facility, we will not be allocating any + * reference and linkage id's. + */ + cc_record->call_linkage_id = + invoke->args.etsi.CallInfoRetain.call_linkage_id & 0x7F; + cc_record->original_call = call; + call->cc.record = cc_record; + pri_cc_event(ctrl, call, cc_record, CC_EVENT_AVAILABLE); + break; + case ROSE_ETSI_CCBSRequest: + pri_cc_ptmp_request(ctrl, call, invoke); + break; + case ROSE_ETSI_CCNRRequest: + pri_cc_ptmp_request(ctrl, call, invoke); + break; + case ROSE_ETSI_CCBSDeactivate: + cc_record = pri_cc_find_by_reference(ctrl, + invoke->args.etsi.CCBSDeactivate.ccbs_reference); + if (!cc_record) { + send_facility_error(ctrl, call, invoke->invoke_id, + ROSE_ERROR_CCBS_InvalidCCBSReference); + break; + } + send_facility_result_ok(ctrl, call, invoke->invoke_id); + pri_cc_event(ctrl, call, cc_record, CC_EVENT_LINK_CANCEL); + break; + case ROSE_ETSI_CCBSInterrogate: + pri_cc_interrogate_rsp(ctrl, call, invoke); + break; + case ROSE_ETSI_CCNRInterrogate: + pri_cc_interrogate_rsp(ctrl, call, invoke); + break; + case ROSE_ETSI_CCBSErase: + cc_record = pri_cc_find_by_reference(ctrl, + invoke->args.etsi.CCBSErase.ccbs_reference); + if (!cc_record) { + /* + * Ignore any status requests that we do not have a record. + * We will not participate in any CC requests that we did + * not initiate. + */ + break; + } + pri_cc_event(ctrl, call, cc_record, CC_EVENT_LINK_CANCEL); + break; + case ROSE_ETSI_CCBSRemoteUserFree: + cc_record = pri_cc_find_by_reference(ctrl, + invoke->args.etsi.CCBSRemoteUserFree.ccbs_reference); + if (!cc_record) { + /* + * Ignore any status requests that we do not have a record. + * We will not participate in any CC requests that we did + * not initiate. + */ + break; + } + pri_cc_event(ctrl, call, cc_record, CC_EVENT_REMOTE_USER_FREE); + break; + case ROSE_ETSI_CCBSCall: + cc_record = pri_cc_find_by_reference(ctrl, + invoke->args.etsi.CCBSCall.ccbs_reference); + if (!cc_record) { + rose_error_msg_encode(ctrl, call, Q931_ANY_MESSAGE, invoke->invoke_id, + ROSE_ERROR_CCBS_InvalidCCBSReference); + call->cc.hangup_call = 1; + break; + } + + /* Save off data to know how to send back any response. */ + cc_record->response.signaling = call; + cc_record->response.invoke_operation = invoke->operation; + cc_record->response.invoke_id = invoke->invoke_id; + + pri_cc_event(ctrl, call, cc_record, CC_EVENT_RECALL); + break; + case ROSE_ETSI_CCBSStatusRequest: + cc_record = pri_cc_find_by_reference(ctrl, + invoke->args.etsi.CCBSStatusRequest.ccbs_reference); + if (!cc_record) { + /* + * Ignore any status requests that we do not have a record. + * We will not participate in any CC requests that we did + * not initiate. + */ + break; + } + + /* Save off data to know how to send back any response. */ + //cc_record->response.signaling = call; + cc_record->response.invoke_operation = invoke->operation; + cc_record->response.invoke_id = invoke->invoke_id; + + subcmd = q931_alloc_subcommand(ctrl); + if (!subcmd) { + break; + } + + subcmd->cmd = PRI_SUBCMD_CC_STATUS_REQ; + subcmd->u.cc_status_req.cc_id = cc_record->record_id; + break; + case ROSE_ETSI_CCBSBFree: + cc_record = pri_cc_find_by_reference(ctrl, + invoke->args.etsi.CCBSBFree.ccbs_reference); + if (!cc_record) { + /* + * Ignore any status requests that we do not have a record. + * We will not participate in any CC requests that we did + * not initiate. + */ + break; + } + pri_cc_event(ctrl, call, cc_record, CC_EVENT_B_FREE); + break; + case ROSE_ETSI_EraseCallLinkageID: + cc_record = pri_cc_find_by_linkage(ctrl, + invoke->args.etsi.EraseCallLinkageID.call_linkage_id); + if (!cc_record) { + /* + * Ignore any status requests that we do not have a record. + * We will not participate in any CC requests that we did + * not initiate. + */ + break; + } + /* + * T_RETENTION expired on the network side so we will pretend + * that it expired on our side. + */ + pri_cc_event(ctrl, call, cc_record, CC_EVENT_TIMEOUT_T_RETENTION); + break; + case ROSE_ETSI_CCBSStopAlerting: + cc_record = pri_cc_find_by_reference(ctrl, + invoke->args.etsi.CCBSStopAlerting.ccbs_reference); + if (!cc_record) { + /* + * Ignore any status requests that we do not have a record. + * We will not participate in any CC requests that we did + * not initiate. + */ + break; + } + pri_cc_event(ctrl, call, cc_record, CC_EVENT_STOP_ALERTING); + break; + case ROSE_ETSI_CCBS_T_Request: + pri_cc_ptp_request(ctrl, call, msgtype, invoke); + break; + case ROSE_ETSI_CCNR_T_Request: + pri_cc_ptp_request(ctrl, call, msgtype, invoke); + break; + case ROSE_ETSI_CCBS_T_Call: + if (msgtype != Q931_SETUP) { + /* Ignore since it did not come in on the correct message. */ + break; + } + + /* + * If we cannot find the cc_record we should still pass up the + * CC call indication but with a -1 for the cc_id. + * The upper layer would then need to search its records for a + * matching CC. The call may have come in on a different interface. + */ + q931_party_id_to_address(&party_address, &call->remote_id); + cc_record = pri_cc_find_by_addressing(ctrl, &party_address, &call->called, + call->cc.saved_ie_contents.length, call->cc.saved_ie_contents.data); + if (cc_record) { + pri_cc_event(ctrl, call, cc_record, CC_EVENT_RECALL); + } else { + subcmd = q931_alloc_subcommand(ctrl); + if (!subcmd) { + break; + } + + subcmd->cmd = PRI_SUBCMD_CC_CALL; + subcmd->u.cc_call.cc_id = -1; + } + break; + case ROSE_ETSI_CCBS_T_Suspend: + cc_record = call->cc.record; + if (!cc_record) { + break; + } + pri_cc_event(ctrl, call, cc_record, CC_EVENT_SUSPEND); + break; + case ROSE_ETSI_CCBS_T_Resume: + cc_record = call->cc.record; + if (!cc_record) { + break; + } + pri_cc_event(ctrl, call, cc_record, CC_EVENT_RESUME); + break; + case ROSE_ETSI_CCBS_T_RemoteUserFree: + cc_record = call->cc.record; + if (!cc_record) { + break; + } + pri_cc_event(ctrl, call, cc_record, CC_EVENT_REMOTE_USER_FREE); + break; + case ROSE_ETSI_CCBS_T_Available: + if (!PRI_MASTER(ctrl)->cc_support) { + /* + * Blocking the cc-available event effectively + * disables call completion for outgoing calls. + */ + break; + } + if (call->cc.record) { + /* Duplicate message! Should not happen. */ + break; + } + cc_record = pri_cc_new_record(ctrl, call); + if (!cc_record) { + break; + } + cc_record->original_call = call; + call->cc.record = cc_record; + pri_cc_event(ctrl, call, cc_record, CC_EVENT_AVAILABLE); + break; case ROSE_QSIG_CallingName: /* CallingName is put in remote_id.name */ rose_copy_name_to_q931(ctrl, &call->remote_id.name, @@ -3754,7 +4101,6 @@ void rose_handle_invoke(struct pri *ctrl, q931_call *call, int msgtype, q931_ie /* Setup connected line subcommand */ subcmd = q931_alloc_subcommand(ctrl); if (!subcmd) { - pri_error(ctrl, "ERROR: Too many facility subcommands\n"); break; } subcmd->cmd = PRI_SUBCMD_CONNECTED_LINE; @@ -3799,7 +4145,7 @@ void rose_handle_invoke(struct pri *ctrl, q931_call *call, int msgtype, q931_ie call->incoming_ct_state = INCOMING_CT_STATE_POST_CONNECTED_LINE; /* connectedAddress is put in remote_id */ - rose_copy_presented_address_screened_to_q931(ctrl, &call->remote_id, + rose_copy_presented_address_screened_to_id_q931(ctrl, &call->remote_id, &invoke->args.qsig.CallTransferActive.connected); /* connectedName is put in remote_id.name */ @@ -3878,7 +4224,6 @@ void rose_handle_invoke(struct pri *ctrl, q931_call *call, int msgtype, q931_ie if (!subcmd) { send_facility_error(ctrl, call, invoke->invoke_id, ROSE_ERROR_Gen_ResourceUnavailable); - pri_error(ctrl, "ERROR: Too many facility subcommands\n"); break; } @@ -3893,7 +4238,7 @@ void rose_handle_invoke(struct pri *ctrl, q931_call *call, int msgtype, q931_ie } /* Rerouting to the new address. */ - rose_copy_address_to_q931(ctrl, &deflection.to, + rose_copy_address_to_id_q931(ctrl, &deflection.to, &invoke->args.qsig.CallRerouting.called); switch (invoke->args.qsig.CallRerouting.subscription_option) { default: @@ -4072,7 +4417,6 @@ void rose_handle_invoke(struct pri *ctrl, q931_call *call, int msgtype, q931_ie call->redirecting.state = Q931_REDIRECTING_STATE_IDLE; subcmd = q931_alloc_subcommand(ctrl); if (!subcmd) { - pri_error(ctrl, "ERROR: Too many facility subcommands\n"); break; } /* Setup redirecting subcommand */ @@ -4088,6 +4432,70 @@ void rose_handle_invoke(struct pri *ctrl, q931_call *call, int msgtype, q931_ie case ROSE_QSIG_CfnrDivertedLegFailed: break; #endif /* Not handled yet */ + case ROSE_QSIG_CcbsRequest: + pri_cc_qsig_request(ctrl, call, msgtype, invoke); + break; + case ROSE_QSIG_CcnrRequest: + pri_cc_qsig_request(ctrl, call, msgtype, invoke); + break; + case ROSE_QSIG_CcCancel: + pri_cc_qsig_cancel(ctrl, call, msgtype, invoke); + break; + case ROSE_QSIG_CcExecPossible: + pri_cc_qsig_exec_possible(ctrl, call, msgtype, invoke); + break; + case ROSE_QSIG_CcPathReserve: + /*! + * \todo It may be possible for us to accept the ccPathReserve call. + * We could certainly never initiate it. + */ + rose_error_msg_encode(ctrl, call, Q931_ANY_MESSAGE, invoke->invoke_id, + ROSE_ERROR_QSIG_FailedDueToInterworking); + call->cc.hangup_call = 1; + break; + case ROSE_QSIG_CcRingout: + if (msgtype != Q931_SETUP) { + /* + * Ignore since it did not come in on the correct message. + * + * It could come in on a FACILITY message if we supported + * incoming ccPathReserve calls. + */ + break; + } + + q931_party_id_to_address(&party_address, &call->remote_id); + cc_record = pri_cc_find_by_addressing(ctrl, &party_address, &call->called, + call->cc.saved_ie_contents.length, call->cc.saved_ie_contents.data); + if (cc_record) { + /* Save off data to know how to send back any response. */ + cc_record->response.signaling = call; + cc_record->response.invoke_operation = invoke->operation; + cc_record->response.invoke_id = invoke->invoke_id; + + pri_cc_event(ctrl, call, cc_record, CC_EVENT_RECALL); + } else { + rose_error_msg_encode(ctrl, call, Q931_ANY_MESSAGE, invoke->invoke_id, + ROSE_ERROR_QSIG_FailureToMatch); + call->cc.hangup_call = 1; + } + break; + case ROSE_QSIG_CcSuspend: + cc_record = call->cc.record; + if (!cc_record) { + break; + } + cc_record->fsm.qsig.msgtype = msgtype; + pri_cc_event(ctrl, call, cc_record, CC_EVENT_SUSPEND); + break; + case ROSE_QSIG_CcResume: + cc_record = call->cc.record; + if (!cc_record) { + break; + } + cc_record->fsm.qsig.msgtype = msgtype; + pri_cc_event(ctrl, call, cc_record, CC_EVENT_RESUME); + break; #if 0 /* Not handled yet */ case ROSE_QSIG_MWIActivate: break; diff --git a/pri_facility.h b/pri_facility.h index 018e7d2..fdd9403 100644 --- a/pri_facility.h +++ b/pri_facility.h @@ -30,13 +30,7 @@ #ifndef _PRI_FACILITY_H #define _PRI_FACILITY_H #include "pri_q931.h" - -/* Forward declare some structs */ -struct fac_extension_header; -struct rose_msg_invoke; -struct rose_msg_result; -struct rose_msg_error; -struct rose_msg_reject; +#include "rose.h" /* Protocol Profile field */ #define Q932_PROTOCOL_MASK 0x1F @@ -75,7 +69,7 @@ struct rose_msg_reject; /*! Reasons an APDU callback is called. */ enum APDU_CALLBACK_REASON { /*! - * \brief Send setup error. Abort and cleanup. + * \brief Transmit facility ie setup error. Abort and cleanup. * \note The message may or may not actually get sent. * \note The callback cannot generate an event subcmd. * \note The callback should not send messages. Out of order messages will result. @@ -114,10 +108,15 @@ enum APDU_CALLBACK_REASON { APDU_CALLBACK_REASON_MSG_REJECT, }; -union apdu_msg_data { - const struct rose_msg_result *result; - const struct rose_msg_error *error; - const struct rose_msg_reject *reject; +struct apdu_msg_data { + /*! Decoded response message contents. */ + union { + const struct rose_msg_result *result; + const struct rose_msg_error *error; + const struct rose_msg_reject *reject; + } response; + /*! Q.931 message type the response came in with. */ + int type; }; union apdu_callback_param { @@ -126,6 +125,9 @@ union apdu_callback_param { char pad[8]; }; +/* So calls to pri_call_apdu_find() will not find an aliased event. */ +#define APDU_INVALID_INVOKE_ID 0x10000 + struct apdu_callback_data { /*! APDU invoke id to match with any response messages. (Result/Error/Reject) */ int invoke_id; @@ -149,7 +151,7 @@ struct apdu_callback_data { * * \return TRUE if no more responses are expected. */ - int (*callback)(enum APDU_CALLBACK_REASON reason, struct pri *ctrl, struct q931_call *call, struct apdu_event *apdu, const union apdu_msg_data *msg); + int (*callback)(enum APDU_CALLBACK_REASON reason, struct pri *ctrl, struct q931_call *call, struct apdu_event *apdu, const struct apdu_msg_data *msg); /*! \brief Sender data for the callback function to identify the particular APDU. */ union apdu_callback_param user; }; @@ -173,6 +175,29 @@ struct apdu_event { unsigned char apdu[255]; }; +void rose_copy_number_to_q931(struct pri *ctrl, struct q931_party_number *q931_number, const struct rosePartyNumber *rose_number); +void rose_copy_subaddress_to_q931(struct pri *ctrl, struct q931_party_subaddress *q931_subaddress, const struct rosePartySubaddress *rose_subaddress); +void rose_copy_address_to_q931(struct pri *ctrl, struct q931_party_address *q931_address, const struct roseAddress *rose_address); +void rose_copy_address_to_id_q931(struct pri *ctrl, struct q931_party_id *q931_address, const struct roseAddress *rose_address); +void rose_copy_presented_number_screened_to_q931(struct pri *ctrl, struct q931_party_number *q931_number, const struct rosePresentedNumberScreened *rose_presented); +void rose_copy_presented_number_unscreened_to_q931(struct pri *ctrl, struct q931_party_number *q931_number, const struct rosePresentedNumberUnscreened *rose_presented); +void rose_copy_presented_address_screened_to_id_q931(struct pri *ctrl, struct q931_party_id *q931_address, const struct rosePresentedAddressScreened *rose_presented); +void rose_copy_name_to_q931(struct pri *ctrl, struct q931_party_name *qsig_name, const struct roseQsigName *rose_name); + +void q931_copy_number_to_rose(struct pri *ctrl, struct rosePartyNumber *rose_number, const struct q931_party_number *q931_number); +void q931_copy_subaddress_to_rose(struct pri *ctrl, struct rosePartySubaddress *rose_subaddress, const struct q931_party_subaddress *q931_subaddress); +void q931_copy_address_to_rose(struct pri *ctrl, struct roseAddress *rose_address, const struct q931_party_address *q931_address); +void q931_copy_id_address_to_rose(struct pri *ctrl, struct roseAddress *rose_address, const struct q931_party_id *q931_address); +void q931_copy_presented_number_screened_to_rose(struct pri *ctrl, struct rosePresentedNumberScreened *rose_presented, const struct q931_party_number *q931_number); +void q931_copy_presented_number_unscreened_to_rose(struct pri *ctrl, struct rosePresentedNumberUnscreened *rose_presented, const struct q931_party_number *q931_number); +void q931_copy_presented_id_address_screened_to_rose(struct pri *ctrl, struct rosePresentedAddressScreened *rose_presented, const struct q931_party_id *q931_address); +void q931_copy_name_to_rose(struct pri *ctrl, struct roseQsigName *rose_name, const struct q931_party_name *qsig_name); + +int rose_error_msg_encode(struct pri *ctrl, q931_call *call, int msgtype, int invoke_id, enum rose_error_code code); +int send_facility_error(struct pri *ctrl, q931_call *call, int invoke_id, enum rose_error_code code); +int rose_result_ok_encode(struct pri *ctrl, q931_call *call, int msgtype, int invoke_id); +int send_facility_result_ok(struct pri *ctrl, q931_call *call, int invoke_id); + /* Queues an MWI apdu on a the given call */ int mwi_message_send(struct pri *pri, q931_call *call, struct pri_sr *req, int activate); @@ -197,6 +222,8 @@ int rose_called_name_encode(struct pri *pri, q931_call *call, int messagetype); int pri_call_apdu_queue(q931_call *call, int messagetype, const unsigned char *apdu, int apdu_len, struct apdu_callback_data *response); void pri_call_apdu_queue_cleanup(q931_call *call); +struct apdu_event *pri_call_apdu_find(struct q931_call *call, int invoke_id); +int pri_call_apdu_extract(struct q931_call *call, struct apdu_event *extract); void pri_call_apdu_delete(struct q931_call *call, struct apdu_event *doomed); /* Adds the "standard" APDUs to a call */ @@ -209,4 +236,11 @@ void rose_handle_result(struct pri *ctrl, q931_call *call, int msgtype, q931_ie void rose_handle_error(struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, const struct fac_extension_header *header, const struct rose_msg_error *error); void rose_handle_reject(struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, const struct fac_extension_header *header, const struct rose_msg_reject *reject); +int pri_cc_interrogate_rsp(struct pri *ctrl, q931_call *call, const struct rose_msg_invoke *invoke); +void pri_cc_ptmp_request(struct pri *ctrl, q931_call *call, const struct rose_msg_invoke *invoke); +void pri_cc_ptp_request(struct pri *ctrl, q931_call *call, int msgtype, const struct rose_msg_invoke *invoke); +void pri_cc_qsig_request(struct pri *ctrl, q931_call *call, int msgtype, const struct rose_msg_invoke *invoke); +void pri_cc_qsig_cancel(struct pri *ctrl, q931_call *call, int msgtype, const struct rose_msg_invoke *invoke); +void pri_cc_qsig_exec_possible(struct pri *ctrl, q931_call *call, int msgtype, const struct rose_msg_invoke *invoke); + #endif /* _PRI_FACILITY_H */ diff --git a/pri_internal.h b/pri_internal.h index edb8bb6..62bac10 100644 --- a/pri_internal.h +++ b/pri_internal.h @@ -42,6 +42,7 @@ /* Forward declare some structs */ struct apdu_event; +struct pri_cc_record; struct pri_sched { struct timeval when; @@ -49,8 +50,12 @@ struct pri_sched { void *data; }; -/*! Maximum number of scheduled events active at the same time. */ -#define MAX_SCHED 128 +/* + * libpri needs to be able to allocate B channels to support Q.SIG path reservation. + * Until that happens, path reservation is not possible. Fortunately, + * path reservation is optional with a fallback to what we can implement. + */ +//#define QSIG_PATH_RESERVATION_SUPPORT 1 /*! Maximum number of facility ie's to handle per incoming message. */ #define MAX_FACILITY_IES 8 @@ -73,7 +78,14 @@ struct pri { struct pri_msg_line *msg_line; struct pri *subchannel; /* Sub-channel if appropriate */ struct pri *master; /* Master channel if appropriate */ - struct pri_sched pri_sched[MAX_SCHED]; /* Scheduled events */ + struct { + /*! Dynamically allocated array of timers that can grow as needed. */ + struct pri_sched *timer; + /*! Numer of timer slots in the allocated array of timers. */ + unsigned num_slots; + /*! Maximum timer slots currently needed. */ + unsigned max_used; + } sched; int debug; /* Debug stuff */ int state; /* State of D-channel */ int switchtype; /* Switch type */ @@ -93,6 +105,7 @@ struct pri { unsigned int hold_support:1;/* TRUE if upper layer supports call hold. */ unsigned int deflection_support:1;/* TRUE if upper layer supports call deflection/rerouting. */ unsigned int hangup_fix_enabled:1;/* TRUE if should follow Q.931 Section 5.3.2 instead of blindly sending RELEASE_COMPLETE for certain causes */ + unsigned int cc_support:1;/* TRUE if upper layer supports call completion. */ /* MDL variables */ int mdl_error; @@ -156,6 +169,31 @@ struct pri { short last_invoke; /* Last ROSE invoke ID (Valid in master record only) */ + /*! Call completion (Valid in master record only) */ + struct { + /*! Active CC records */ + struct pri_cc_record *pool; + /*! Last CC record id allocated. */ + unsigned short last_record_id; + /*! Last CC PTMP reference id allocated. (0-127) */ + unsigned char last_reference_id; + /*! Last CC PTMP linkage id allocated. (0-127) */ + unsigned char last_linkage_id; + /*! Configured CC options. */ + struct { + /*! PTMP recall mode: globalRecall(0), specificRecall(1) */ + unsigned char recall_mode; + /*! Q.SIG Request signaling link retention: release(0), retain(1), do-not-care(2) */ + unsigned char signaling_retention_req; + /*! Q.SIG Response request signaling link retention: release(0), retain(1) */ + unsigned char signaling_retention_rsp; +#if defined(QSIG_PATH_RESERVATION_SUPPORT) + /*! Q.SIG TRUE if response request can support path reservation. */ + unsigned char allow_path_reservation; +#endif /* defined(QSIG_PATH_RESERVATION_SUPPORT) */ + } option; + } cc; + /*! For delayed processing of facility ie's. */ struct { /*! Array of facility ie locations in the current received message. */ @@ -370,6 +408,38 @@ enum Q931_HOLD_STATE { Q931_HOLD_STATE_RETRIEVE_IND, }; +/* Only save the first of each BC, HLC, and LLC from the initial SETUP. */ +#define CC_SAVED_IE_BC (1 << 0) /*!< BC has already been saved. */ +#define CC_SAVED_IE_HLC (1 << 1) /*!< HLC has already been saved. */ +#define CC_SAVED_IE_LLC (1 << 2) /*!< LLC has already been saved. */ + +/*! Saved ie contents for BC, HLC, and LLC. (Only the first of each is saved.) */ +struct q931_saved_ie_contents { + /*! Length of saved ie contents. */ + unsigned char length; + /*! Saved ie contents data. */ + unsigned char data[ + /* Bearer Capability has a max length of 12. */ + 12 + /* High Layer Compatibility has a max length of 5. */ + + 5 + /* Low Layer Compatibility has a max length of 18. */ + + 18 + /* Room for null terminator just in case. */ + + 1]; +}; + +/*! Digested BC parameters. */ +struct decoded_bc { + int transcapability; + int transmoderate; + int transmultiple; + int userl1; + int userl2; + int userl3; + int rateadaption; +}; + /* q931_call datastructure */ struct q931_call { struct pri *pri; /* PRI */ @@ -394,20 +464,21 @@ struct q931_call { int ri; /* Restart Indicator (Restart Indicator IE) */ - /* Bearer Capability */ - int transcapability; - int transmoderate; - int transmultiple; - int userl1; - int userl2; - int userl3; - int rateadaption; + /*! Bearer Capability */ + struct decoded_bc bc; /*! * \brief TRUE if the call is a Call Independent Signalling connection. * \note The call has no B channel associated with it. (Just signalling) */ int cis_call; + /*! + * \brief TRUE if we have recognized a use for this CIS call. + * \note An incoming CIS call will be immediately disconnected if not set. + * This is a safeguard against unhandled incoming CIS calls to protect the + * call reference pool. + */ + int cis_recognized; /*! \brief TRUE if we will auto disconnect the cis_call we originated. */ int cis_auto_disconnect; @@ -530,6 +601,261 @@ struct q931_call { /* These valid in master call only */ struct q931_call *subcalls[Q931_MAX_TEI]; int pri_winner; + + /* Call completion */ + struct { + /*! + * \brief CC record associated with this call. + * \note + * CC signaling link or original call when cc-available indicated. + */ + struct pri_cc_record *record; + /*! Original calling party. */ + struct q931_party_id party_a; + /*! Saved BC, HLC, and LLC from initial SETUP */ + struct q931_saved_ie_contents saved_ie_contents; + /*! Only save the first of each BC, HLC, and LLC from the initial SETUP. */ + unsigned char saved_ie_flags; + /*! TRUE if call needs to be hung up. */ + unsigned char hangup_call; + /*! TRUE if we originated this call. */ + unsigned char originated; + /*! TRUE if outgoing call was already redirected. */ + unsigned char initially_redirected; + } cc; +}; + +enum CC_STATES { + /*! CC is not active. */ + CC_STATE_IDLE, + // /*! CC has recorded call information in anticipation of CC availability. */ + // CC_STATE_RECORD_RETENTION, + /*! CC is available and waiting on ALERTING or DISCONNECT to go out. */ + CC_STATE_PENDING_AVAILABLE, + /*! CC is available and waiting on possible CC request. */ + CC_STATE_AVAILABLE, + /*! CC is requested to be activated and waiting on party B to acknowledge. */ + CC_STATE_REQUESTED, + /*! CC is activated and waiting for party B to become available. */ + CC_STATE_ACTIVATED, + /*! CC party B is available and waiting for status of party A. */ + CC_STATE_B_AVAILABLE, + /*! CC is suspended because party A is not available. (Monitor party A.) */ + CC_STATE_SUSPENDED, + /*! CC is waiting for party A to initiate CC callback. */ + CC_STATE_WAIT_CALLBACK, + /*! CC callback in progress. */ + CC_STATE_CALLBACK, + /*! CC is waiting for signaling link to be cleared before destruction. */ + CC_STATE_WAIT_DESTRUCTION, + + /*! Number of CC states. Must be last in enum. */ + CC_STATE_NUM +}; + +enum CC_EVENTS { + /*! CC is available for the current call. */ + CC_EVENT_AVAILABLE, + /*! Requesting CC activation. */ + CC_EVENT_CC_REQUEST, + /*! Requesting CC activation accepted. */ + CC_EVENT_CC_REQUEST_ACCEPT, + /*! Requesting CC activation failed (error/reject received). */ + CC_EVENT_CC_REQUEST_FAIL, + /*! CC party B is available, party A is considered free. */ + CC_EVENT_REMOTE_USER_FREE, + /*! CC party B is available, party A is busy or CCBS busy. */ + CC_EVENT_B_FREE, + /*! Someone else responded to the CC recall. */ + CC_EVENT_STOP_ALERTING, + /*! CC poll/prompt for party A status. */ + CC_EVENT_A_STATUS, + /*! CC party A is free/available for recall. */ + CC_EVENT_A_FREE, + /*! CC party A is busy/not-available for recall. */ + CC_EVENT_A_BUSY, + /*! Suspend monitoring party B because party A is busy. */ + CC_EVENT_SUSPEND, + /*! Resume monitoring party B because party A is now available. */ + CC_EVENT_RESUME, + /*! This is the CC recall call attempt. */ + CC_EVENT_RECALL, + /*! Link request to cancel/deactivate CC received. */ + CC_EVENT_LINK_CANCEL, + /*! Tear down CC request from upper layer. */ + CC_EVENT_CANCEL, + /*! Received message indicating tear down of CC signaling link completed. */ + CC_EVENT_SIGNALING_GONE, + /*! Delayed hangup request for the signaling link to allow subcmd events to be passed up. */ + CC_EVENT_HANGUP_SIGNALING, + /*! Sent ALERTING message. */ + CC_EVENT_MSG_ALERTING, + /*! Sent DISCONNECT message. */ + CC_EVENT_MSG_DISCONNECT, + /*! Sent RELEASE message. */ + CC_EVENT_MSG_RELEASE, + /*! Sent RELEASE_COMPLETE message. */ + CC_EVENT_MSG_RELEASE_COMPLETE, + /*! T_ACTIVATE timer timed out. */ + CC_EVENT_TIMEOUT_T_ACTIVATE, +#if 0 + /*! T_DEACTIVATE timer timed out. */ + CC_EVENT_TIMEOUT_T_DEACTIVATE, + /*! T_INTERROGATE timer timed out. */ + CC_EVENT_TIMEOUT_T_INTERROGATE, +#endif + /*! T_RETENTION timer timed out. */ + CC_EVENT_TIMEOUT_T_RETENTION, + /*! T-STATUS timer equivalent for CC user A status timed out. */ + CC_EVENT_TIMEOUT_T_CCBS1, + /*! Timeout for valid party A status. */ + CC_EVENT_TIMEOUT_EXTENDED_T_CCBS1, + /*! Max time the CCBS/CCNR service will be active. */ + CC_EVENT_TIMEOUT_T_SUPERVISION, + /*! Max time to wait for user A to respond to user B availability. */ + CC_EVENT_TIMEOUT_T_RECALL, +}; + +enum CC_PARTY_A_AVAILABILITY { + CC_PARTY_A_AVAILABILITY_INVALID, + CC_PARTY_A_AVAILABILITY_BUSY, + CC_PARTY_A_AVAILABILITY_FREE, +}; + +/* Invalid PTMP call completion reference and linkage id value. */ +#define CC_PTMP_INVALID_ID 0xFF + +/*! \brief Call-completion record */ +struct pri_cc_record { + /*! Next call-completion record in the list */ + struct pri_cc_record *next; + /*! Master D channel control structure. */ + struct pri *master; + /*! Original call that is offered CC availability. (NULL if no longer exists.) */ + struct q931_call *original_call; + /*! + * \brief Associated signaling link. (NULL if not established.) + * \note + * PTMP - Broadcast dummy call reference call. + * (If needed, the TE side could use this pointer to locate its specific + * dummy call reference call.) + * \note + * PTP - REGISTER signaling link. + * \note + * Q.SIG - SETUP signaling link. + */ + struct q931_call *signaling; + /*! Call-completion record id (0 - 65535) */ + long record_id; + /*! Call-completion state */ + enum CC_STATES state; + /*! Original calling party. */ + struct q931_party_id party_a; + /*! Original called party. */ + struct q931_party_address party_b; + /*! Saved BC, HLC, and LLC from initial SETUP */ + struct q931_saved_ie_contents saved_ie_contents; + /*! Saved decoded BC */ + struct decoded_bc bc; + + /*! FSM parameters. */ + union { + /*! PTMP FSM parameters. */ + struct { + /*! Extended T_CCBS1 timer id for CCBSStatusRequest handling. */ + int extended_t_ccbs1; + /*! Invoke id for the CCBSStatusRequest message to find if T_CCBS1 still running. */ + int t_ccbs1_invoke_id; + /*! Number of times party A status request got no responses. */ + int party_a_status_count; + /*! Accumulating party A availability status */ + enum CC_PARTY_A_AVAILABILITY party_a_status_acc; + } ptmp; + /*! PTP FSM parameters. */ + struct { + } ptp; + struct { + /*! Q.931 message type the current message event came in on. */ + int msgtype; + } qsig; + } fsm; + /*! Received message parameters of interest. */ + union { + /*! cc-request error/reject response */ + struct { + /*! enum APDU_CALLBACK_REASON reason */ + int reason; + /*! MSG_ERROR/MSG_REJECT fail code. */ + int code; + } cc_req_rsp; + } msg; + /*! Party A availability status */ + enum CC_PARTY_A_AVAILABILITY party_a_status; + /*! Indirect timer id to abort indirect action events. */ + int t_indirect; + /*! + * \brief PTMP T_RETENTION timer id. + * \note + * This timer is used by all CC agents to implement + * the Asterisk CC core offer timer. + */ + int t_retention; + /*! + * \brief CC service supervision timer. + * + * \details + * This timer is one of the following timer id's depending upon + * switch type and CC mode: + * PTMP - T_CCBS2/T_CCNR2, + * PTP - T_CCBS5/T_CCNR5/T_CCBS6/T_CCNR6, + * Q.SIG - QSIG_CCBS_T2/QSIG_CCNR_T2 + */ + int t_supervision; + /*! + * \brief Party A response to B availability for recall timer. + * \details + * This timer is one of the following timer id's: + * PTMP - T_CCBS3 + * Q.SIG - QSIG_CC_T3 + */ + int t_recall; + /*! Invoke id for the cc-request message to find if T_ACTIVATE/QSIG_CC_T1 still running. */ + int t_activate_invoke_id; + /*! Pending response information. */ + struct { + /*! + * \brief Send response on this signaling link. + * \note Used by PTMP for CCBSRequest/CCNRRequest/CCBSCall responses. + * \note Used by Q.SIG for ccRingout responses. + */ + struct q931_call *signaling; + /*! Invoke operation code */ + int invoke_operation; + /*! Invoke id to use in the pending response. */ + short invoke_id; + } response; + + /*! TRUE if the call-completion FSM has completed and this record needs to be destroyed. */ + unsigned char fsm_complete; + /*! TRUE if we are a call completion agent. */ + unsigned char is_agent; + /*! TRUE if active cc mode is CCNR. */ + unsigned char is_ccnr; + /*! PTMP pre-activation reference id. (0-127) */ + unsigned char call_linkage_id; + /*! PTMP active CCBS reference id. (0-127) */ + unsigned char ccbs_reference_id; + /*! Negotiated options */ + struct { + /*! PTMP recall mode: globalRecall(0), specificRecall(1) */ + unsigned char recall_mode; + /*! TRUE if negotiated for Q.SIG signaling link to be retained. */ + unsigned char retain_signaling_link; +#if defined(QSIG_PATH_RESERVATION_SUPPORT) + /*! Q.SIG TRUE if can do path reservation. */ + unsigned char do_path_reservation; +#endif /* defined(QSIG_PATH_RESERVATION_SUPPORT) */ + } option; }; /*! D channel control structure with associated dummy call reference record. */ @@ -558,6 +884,8 @@ void __pri_free_tei(struct pri *p); void q931_init_call_record(struct pri *ctrl, struct q931_call *call, int cr); +void pri_sr_init(struct pri_sr *req); + void q931_party_name_init(struct q931_party_name *name); void q931_party_number_init(struct q931_party_number *number); void q931_party_subaddress_init(struct q931_party_subaddress *subaddr); @@ -565,20 +893,32 @@ void q931_party_address_init(struct q931_party_address *address); void q931_party_id_init(struct q931_party_id *id); void q931_party_redirecting_init(struct q931_party_redirecting *redirecting); -static inline void q931_party_address_to_id(struct q931_party_id *id, struct q931_party_address *address) +static inline void q931_party_address_to_id(struct q931_party_id *id, const struct q931_party_address *address) { id->number = address->number; id->subaddress = address->subaddress; } +static inline void q931_party_id_to_address(struct q931_party_address *address, const struct q931_party_id *id) +{ + address->number = id->number; + address->subaddress = id->subaddress; +} + int q931_party_name_cmp(const struct q931_party_name *left, const struct q931_party_name *right); int q931_party_number_cmp(const struct q931_party_number *left, const struct q931_party_number *right); int q931_party_subaddress_cmp(const struct q931_party_subaddress *left, const struct q931_party_subaddress *right); +int q931_party_address_cmp(const struct q931_party_address *left, const struct q931_party_address *right); int q931_party_id_cmp(const struct q931_party_id *left, const struct q931_party_id *right); +int q931_party_id_cmp_address(const struct q931_party_id *left, const struct q931_party_id *right); + +int q931_cmp_party_id_to_address(const struct q931_party_id *id, const struct q931_party_address *address); +void q931_party_id_copy_to_address(struct q931_party_address *address, const struct q931_party_id *id); void q931_party_name_copy_to_pri(struct pri_party_name *pri_name, const struct q931_party_name *q931_name); void q931_party_number_copy_to_pri(struct pri_party_number *pri_number, const struct q931_party_number *q931_number); void q931_party_subaddress_copy_to_pri(struct pri_party_subaddress *pri_subaddress, const struct q931_party_subaddress *q931_subaddress); +void q931_party_address_copy_to_pri(struct pri_party_address *pri_address, const struct q931_party_address *q931_address); void q931_party_id_copy_to_pri(struct pri_party_id *pri_id, const struct q931_party_id *q931_id); void q931_party_redirecting_copy_to_pri(struct pri_party_redirecting *pri_redirecting, const struct q931_party_redirecting *q931_redirecting); @@ -598,6 +938,15 @@ struct pri_subcommand *q931_alloc_subcommand(struct pri *ctrl); int q931_notify_redirection(struct pri *ctrl, q931_call *call, int notify, const struct q931_party_number *number); +struct pri_cc_record *pri_cc_find_by_reference(struct pri *ctrl, unsigned reference_id); +struct pri_cc_record *pri_cc_find_by_linkage(struct pri *ctrl, unsigned linkage_id); +struct pri_cc_record *pri_cc_find_by_addressing(struct pri *ctrl, const struct q931_party_address *party_a, const struct q931_party_address *party_b, unsigned length, const unsigned char *q931_ies); +struct pri_cc_record *pri_cc_new_record(struct pri *ctrl, q931_call *call); +void pri_cc_qsig_determine_available(struct pri *ctrl, q931_call *call); +int pri_cc_event(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event); +int q931_cc_timeout(struct pri *ctrl, struct pri_cc_record *cc_record, enum CC_EVENTS event); +void q931_cc_indirect(struct pri *ctrl, struct pri_cc_record *cc_record, void (*func)(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)); + /*! * \brief Get the master PRI control structure. * @@ -731,4 +1080,10 @@ static inline int q931_is_dummy_call(const q931_call *call) return (call->cr == Q931_DUMMY_CALL_REFERENCE) ? 1 : 0; } +static inline short get_invokeid(struct pri *ctrl) +{ + ctrl = PRI_MASTER(ctrl); + return ++ctrl->last_invoke; +} + #endif diff --git a/pri_q931.h b/pri_q931.h index 9ff4de6..5bfef65 100644 --- a/pri_q931.h +++ b/pri_q931.h @@ -82,6 +82,9 @@ typedef struct q931_ie { /* Q.931 / National ISDN Message Types */ +/*! Send this facility APDU on the next message to go out. */ +#define Q931_ANY_MESSAGE -1 + /* Call Establishment Messages */ #define Q931_ALERTING 0x01 #define Q931_CALL_PROCEEDING 0x02 @@ -106,6 +109,7 @@ typedef struct q931_ie { #define Q931_CONGESTION_CONTROL 0x79 #define Q931_INFORMATION 0x7b #define Q931_FACILITY 0x62 +#define Q931_REGISTER 0x64 /* Q.932 */ #define Q931_NOTIFY 0x6e /* Call Management Messages */ @@ -490,6 +494,9 @@ struct q931_call *q931_find_call(struct pri *ctrl, int cr); struct q931_call *q931_new_call(struct pri *pri); extern int q931_setup(struct pri *pri, q931_call *c, struct pri_sr *req); + +int q931_register(struct pri *ctrl, q931_call *call); + void q931_dump(struct pri *ctrl, int tei, q931_h *h, int len, int txrx); void q931_destroycall(struct pri *pri, q931_call *c); diff --git a/prisched.c b/prisched.c index 536b696..1093de7 100644 --- a/prisched.c +++ b/prisched.c @@ -28,16 +28,73 @@ */ #include +#include +#include #include "libpri.h" #include "pri_internal.h" +/*! Initial number of scheduled timer slots. */ +#define SCHED_EVENTS_INITIAL 128 +/*! + * Maximum number of scheduled timer slots. + * Should be a power of 2 multiple of SCHED_EVENTS_INITIAL. + */ +#define SCHED_EVENTS_MAX 8192 + /*! \brief The maximum number of timers that were active at once. */ -static int maxsched = 0; +static unsigned maxsched = 0; /* Scheduler routines */ +/*! + * \internal + * \brief Increase the number of scheduler timer slots available. + * + * \param ctrl D channel controller. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int pri_schedule_grow(struct pri *ctrl) +{ + unsigned num_slots; + struct pri_sched *timers; + + /* Determine how many slots in the new timer table. */ + if (ctrl->sched.num_slots) { + if (SCHED_EVENTS_MAX <= ctrl->sched.num_slots) { + /* Cannot grow the timer table any more. */ + return -1; + } + num_slots = ctrl->sched.num_slots * 2; + if (SCHED_EVENTS_MAX < num_slots) { + num_slots = SCHED_EVENTS_MAX; + } + } else { + num_slots = SCHED_EVENTS_INITIAL; + } + + /* Get and initialize the new timer table. */ + timers = calloc(num_slots, sizeof(struct pri_sched)); + if (!timers) { + /* Could not get a new timer table. */ + return -1; + } + if (ctrl->sched.timer) { + /* Copy over the old timer table. */ + memcpy(timers, ctrl->sched.timer, + ctrl->sched.num_slots * sizeof(struct pri_sched)); + free(ctrl->sched.timer); + } + + /* Put the new timer table in place. */ + ctrl->sched.timer = timers; + ctrl->sched.num_slots = num_slots; + return 0; +} + /*! * \brief Start a timer to schedule an event. * @@ -51,22 +108,26 @@ static int maxsched = 0; */ int pri_schedule_event(struct pri *ctrl, int ms, void (*function)(void *data), void *data) { - int x; + unsigned max_used; + unsigned x; struct timeval tv; /* Scheduling runs on master channels only */ - while (ctrl->master) { - ctrl = ctrl->master; - } - for (x = 0; x < MAX_SCHED; ++x) { - if (!ctrl->pri_sched[x].callback) { + ctrl = PRI_MASTER(ctrl); + + max_used = ctrl->sched.max_used; + for (x = 0; x < max_used; ++x) { + if (!ctrl->sched.timer[x].callback) { break; } } - if (x == MAX_SCHED) { + if (x == ctrl->sched.num_slots && pri_schedule_grow(ctrl)) { pri_error(ctrl, "No more room in scheduler\n"); return 0; } + if (ctrl->sched.max_used <= x) { + ctrl->sched.max_used = x + 1; + } if (x >= maxsched) { maxsched = x + 1; } @@ -77,9 +138,9 @@ int pri_schedule_event(struct pri *ctrl, int ms, void (*function)(void *data), v tv.tv_usec -= 1000000; tv.tv_sec += 1; } - ctrl->pri_sched[x].when = tv; - ctrl->pri_sched[x].callback = function; - ctrl->pri_sched[x].data = data; + ctrl->sched.timer[x].when = tv; + ctrl->sched.timer[x].callback = function; + ctrl->sched.timer[x].data = data; return x + 1; } @@ -93,20 +154,29 @@ int pri_schedule_event(struct pri *ctrl, int ms, void (*function)(void *data), v struct timeval *pri_schedule_next(struct pri *ctrl) { struct timeval *closest = NULL; - int x; + unsigned x; /* Scheduling runs on master channels only */ - while (ctrl->master) { - ctrl = ctrl->master; - } - for (x = 0; x < MAX_SCHED; ++x) { - if (ctrl->pri_sched[x].callback && (!closest - || (closest->tv_sec > ctrl->pri_sched[x].when.tv_sec) - || ((closest->tv_sec == ctrl->pri_sched[x].when.tv_sec) - && (closest->tv_usec > ctrl->pri_sched[x].when.tv_usec)))) { - closest = &ctrl->pri_sched[x].when; + ctrl = PRI_MASTER(ctrl); + + /* Scan the scheduled timer slots backwards so we can update the max_used value. */ + for (x = ctrl->sched.max_used; x--;) { + if (ctrl->sched.timer[x].callback) { + if (!closest) { + /* This is the highest sheduled timer slot in use. */ + closest = &ctrl->sched.timer[x].when; + ctrl->sched.max_used = x + 1; + } else if ((closest->tv_sec > ctrl->sched.timer[x].when.tv_sec) + || ((closest->tv_sec == ctrl->sched.timer[x].when.tv_sec) + && (closest->tv_usec > ctrl->sched.timer[x].when.tv_usec))) { + closest = &ctrl->sched.timer[x].when; + } } } + if (!closest) { + /* No scheduled timer slots are active. */ + ctrl->sched.max_used = 0; + } return closest; } @@ -121,23 +191,25 @@ struct timeval *pri_schedule_next(struct pri *ctrl) */ static pri_event *__pri_schedule_run(struct pri *ctrl, struct timeval *tv) { - int x; + unsigned x; + unsigned max_used; void (*callback)(void *); void *data; /* Scheduling runs on master channels only */ - while (ctrl->master) { - ctrl = ctrl->master; - } - for (x = 0; x < MAX_SCHED; ++x) { - if (ctrl->pri_sched[x].callback && ((ctrl->pri_sched[x].when.tv_sec < tv->tv_sec) - || ((ctrl->pri_sched[x].when.tv_sec == tv->tv_sec) - && (ctrl->pri_sched[x].when.tv_usec <= tv->tv_usec)))) { + ctrl = PRI_MASTER(ctrl); + + max_used = ctrl->sched.max_used; + for (x = 0; x < max_used; ++x) { + if (ctrl->sched.timer[x].callback + && ((ctrl->sched.timer[x].when.tv_sec < tv->tv_sec) + || ((ctrl->sched.timer[x].when.tv_sec == tv->tv_sec) + && (ctrl->sched.timer[x].when.tv_usec <= tv->tv_usec)))) { /* This timer has expired. */ ctrl->schedev = 0; - callback = ctrl->pri_sched[x].callback; - data = ctrl->pri_sched[x].data; - ctrl->pri_sched[x].callback = NULL; + callback = ctrl->sched.timer[x].callback; + data = ctrl->sched.timer[x].data; + ctrl->sched.timer[x].callback = NULL; callback(data); if (ctrl->schedev) { return &ctrl->ev; @@ -175,12 +247,12 @@ pri_event *pri_schedule_run(struct pri *ctrl) void pri_schedule_del(struct pri *ctrl, int id) { /* Scheduling runs on master channels only */ - while (ctrl->master) { - ctrl = ctrl->master; - } - if (0 < id && id <= MAX_SCHED) { - ctrl->pri_sched[id - 1].callback = NULL; + ctrl = PRI_MASTER(ctrl); + + if (0 < id && id <= ctrl->sched.num_slots) { + ctrl->sched.timer[id - 1].callback = NULL; } else if (id) { - pri_error(ctrl, "Asked to delete sched id %d???\n", id); + pri_error(ctrl, "Asked to delete sched id %d??? num_slots=%d\n", id, + ctrl->sched.num_slots); } } diff --git a/q931.c b/q931.c index 1b3f49f..5785e03 100644 --- a/q931.c +++ b/q931.c @@ -30,10 +30,7 @@ #include "compat.h" #include "libpri.h" #include "pri_internal.h" -#include "pri_q921.h" -#include "pri_q931.h" #include "pri_facility.h" -#include "rose.h" #include #include @@ -75,6 +72,7 @@ static struct msgtype msgs[] = { { Q931_CONGESTION_CONTROL, "CONGESTION CONTROL" }, { Q931_INFORMATION, "INFORMATION" }, { Q931_FACILITY, "FACILITY" }, + { Q931_REGISTER, "REGISTER" }, { Q931_NOTIFY, "NOTIFY", { Q931_IE_NOTIFY_IND } }, /* Call Management */ @@ -90,6 +88,8 @@ static struct msgtype msgs[] = { { Q931_SUSPEND, "SUSPEND" }, { Q931_SUSPEND_ACKNOWLEDGE, "SUSPEND ACKNOWLEDGE" }, { Q931_SUSPEND_REJECT, "SUSPEND REJECT" }, + + { Q931_ANY_MESSAGE, "ANY MESSAGE" }, }; static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct q931_call *c, int missingmand); @@ -305,16 +305,18 @@ static int q931_encode_channel(const q931_call *call) case Q931_HOLD_STATE_RETRIEVE_REQ: case Q931_HOLD_STATE_RETRIEVE_IND: held_call = 1 << 18; - - /* So a -1 does not wipe out the held_call flag. */ - channelno = call->channelno & 0xFF; - ds1no = call->ds1no & 0xFF; break; default: held_call = 0; + break; + } + if (held_call || call->cis_call) { + /* So a -1 does not wipe out the held_call or cis_call flags. */ + channelno = call->channelno & 0xFF; + ds1no = call->ds1no & 0xFF; + } else { channelno = call->channelno; ds1no = call->ds1no; - break; } return channelno | (ds1no << 8) | (call->ds1explicit << 16) | (call->cis_call << 17) | held_call; @@ -518,6 +520,28 @@ int q931_party_subaddress_cmp(const struct q931_party_subaddress *left, const st return cmp; } +/*! + * \brief Compare the left and right party address. + * + * \param left Left parameter party address. + * \param right Right parameter party address. + * + * \retval < 0 when left < right. + * \retval == 0 when left == right. + * \retval > 0 when left > right. + */ +int q931_party_address_cmp(const struct q931_party_address *left, const struct q931_party_address *right) +{ + int cmp; + + cmp = q931_party_number_cmp(&left->number, &right->number); + if (cmp) { + return cmp; + } + cmp = q931_party_subaddress_cmp(&left->subaddress, &right->subaddress); + return cmp; +} + /*! * \brief Compare the left and right party id. * @@ -544,6 +568,64 @@ int q931_party_id_cmp(const struct q931_party_id *left, const struct q931_party_ return cmp; } +/*! + * \brief Compare the left and right party id addresses. + * + * \param left Left parameter party id. + * \param right Right parameter party id. + * + * \retval < 0 when left < right. + * \retval == 0 when left == right. + * \retval > 0 when left > right. + */ +int q931_party_id_cmp_address(const struct q931_party_id *left, const struct q931_party_id *right) +{ + int cmp; + + cmp = q931_party_number_cmp(&left->number, &right->number); + if (cmp) { + return cmp; + } + cmp = q931_party_subaddress_cmp(&left->subaddress, &right->subaddress); + return cmp; +} + +/*! + * \brief Compare the party id to the party address. + * + * \param id Party id. + * \param address Party address. + * + * \retval < 0 when id < address. + * \retval == 0 when id == address. + * \retval > 0 when id > address. + */ +int q931_cmp_party_id_to_address(const struct q931_party_id *id, const struct q931_party_address *address) +{ + int cmp; + + cmp = q931_party_number_cmp(&id->number, &address->number); + if (cmp) { + return cmp; + } + cmp = q931_party_subaddress_cmp(&id->subaddress, &address->subaddress); + return cmp; +} + +/*! + * \brief Copy a party id into a party address. + * + * \param address Party address. + * \param id Party id. + * + * \return Nothing + */ +void q931_party_id_copy_to_address(struct q931_party_address *address, const struct q931_party_id *id) +{ + address->number = id->number; + address->subaddress = id->subaddress; +} + /*! * \brief Copy the Q.931 party name to the PRI party name structure. * @@ -625,6 +707,20 @@ void q931_party_subaddress_copy_to_pri(struct pri_party_subaddress *pri_subaddre pri_subaddress->data[length] = '\0'; } +/*! + * \brief Copy the Q.931 party address to the PRI party address structure. + * + * \param pri_address PRI party address structure + * \param q931_address Q.931 party address structure + * + * \return Nothing + */ +void q931_party_address_copy_to_pri(struct pri_party_address *pri_address, const struct q931_party_address *q931_address) +{ + q931_party_number_copy_to_pri(&pri_address->number, &q931_address->number); + q931_party_subaddress_copy_to_pri(&pri_address->subaddress, &q931_address->subaddress); +} + /*! * \brief Copy the Q.931 party id to the PRI party id structure. * @@ -755,6 +851,27 @@ int q931_party_id_presentation(const struct q931_party_id *id) return number_value | number_screening; } +/*! + * \internal + * \brief Append the given ie contents to the save ie location. + * + * \param save_ie Saved ie contents to append new ie. + * \param ie Contents to append. + * + * \return Nothing + */ +static void q931_append_ie_contents(struct q931_saved_ie_contents *save_ie, struct q931_ie *ie) +{ + int size; + + size = ie->len + 2; + if (size < sizeof(save_ie->data) - save_ie->length) { + /* Contents will fit so append it. */ + memcpy(&save_ie->data[save_ie->length], ie, size); + save_ie->length += size; + } +} + static void q931_clr_subcommands(struct pri *ctrl) { ctrl->subcmds.counter_subcmd = 0; @@ -766,6 +883,7 @@ struct pri_subcommand *q931_alloc_subcommand(struct pri *ctrl) return &ctrl->subcmds.subcmd[ctrl->subcmds.counter_subcmd++]; } + pri_error(ctrl, "ERROR: Too many facility subcommands\n"); return NULL; } @@ -1193,7 +1311,7 @@ static char *int_rate2str(int proto) static void dump_bearer_capability(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { int pos=2; - pri_message(ctrl, "%c Bearer Capability (len=%2d) [ Ext: %d Q.931 Std: %d Info transfer capability: %s (%d)\n", + pri_message(ctrl, "%c Bearer Capability (len=%2d) [ Ext: %d Coding-Std: %d Info transfer capability: %s (%d)\n", prefix, len, (ie->data[0] & 0x80 ) >> 7, (ie->data[0] & 0x60) >> 5, cap2str(ie->data[0] & 0x1f), (ie->data[0] & 0x1f)); pri_message(ctrl, "%c Ext: %d Trans mode/rate: %s (%d)\n", prefix, (ie->data[1] & 0x80) >> 7, mode2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f); @@ -1323,51 +1441,63 @@ static void dump_bearer_capability(int full_ie, struct pri *ctrl, q931_ie *ie, i static int receive_bearer_capability(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { - int pos=2; - if (ie->data[0] & 0x60) { - pri_error(ctrl, "!! non-standard Q.931 standard field\n"); - return -1; - } - call->transcapability = ie->data[0] & 0x1f; - call->transmoderate = ie->data[1] & 0x7f; - - /* octet 4.1 exists iff mode/rate is multirate */ - if (call->transmoderate == TRANS_MODE_MULTIRATE) { - call->transmultiple = ie->data[pos++] & 0x7f; - } + int pos = 2; - /* Look for octet 5; this is identified by bits 5,6 == 01 */ - if (pos < len && - (ie->data[pos] & 0x60) == 0x20 ) { - /* although the layer1 is only the bottom 5 bits of the byte, - previous versions of this library passed bits 5&6 through - too, so we have to do the same for binary compatability */ - call->userl1 = ie->data[pos] & 0x7f; - pos++; - - /* octet 5a? */ - if (pos < len && !(ie->data[pos-1] & 0x80)) { - call->rateadaption = ie->data[pos] & 0x7f; - pos++; - } - - /* octets 5b through 5d? */ - while (pos < len && !(ie->data[pos-1] & 0x80)) { - pos++; + switch (ie->data[0] & 0x60) { + case 0x00:/* ITU-T standardized coding */ + call->bc.transcapability = ie->data[0] & 0x1f; + call->bc.transmoderate = ie->data[1] & 0x7f; + + /* octet 4.1 exists iff mode/rate is multirate */ + if (call->bc.transmoderate == TRANS_MODE_MULTIRATE) { + call->bc.transmultiple = ie->data[pos++] & 0x7f; } - - } - /* Look for octet 6; this is identified by bits 5,6 == 10 */ - if (pos < len && - (ie->data[pos] & 0x60) == 0x40) { - call->userl2 = ie->data[pos++] & 0x1f; - } + /* Look for octet 5; this is identified by bits 5,6 == 01 */ + if (pos < len && (ie->data[pos] & 0x60) == 0x20) { + /* although the layer1 is only the bottom 5 bits of the byte, + previous versions of this library passed bits 5&6 through + too, so we have to do the same for binary compatability */ + call->bc.userl1 = ie->data[pos] & 0x7f; + pos++; - /* Look for octet 7; this is identified by bits 5,6 == 11 */ - if (pos < len && - (ie->data[pos] & 0x60) == 0x60) { - call->userl3 = ie->data[pos++] & 0x1f; + /* octet 5a? */ + if (pos < len && !(ie->data[pos-1] & 0x80)) { + call->bc.rateadaption = ie->data[pos] & 0x7f; + pos++; + } + + /* octets 5b through 5d? */ + while (pos < len && !(ie->data[pos-1] & 0x80)) { + pos++; + } + + } + + /* Look for octet 6; this is identified by bits 5,6 == 10 */ + if (pos < len && (ie->data[pos] & 0x60) == 0x40) { + call->bc.userl2 = ie->data[pos++] & 0x1f; + } + + /* Look for octet 7; this is identified by bits 5,6 == 11 */ + if (pos < len && (ie->data[pos] & 0x60) == 0x60) { + call->bc.userl3 = ie->data[pos++] & 0x1f; + } + break; + case 0x20:/* ISO/IEC standard */ + if (ie->data[0] == 0xa8 && ie->data[1] == 0x80) { + /* + * Q.SIG uses for CIS calls. ECMA-165 Section 11.3.1 + * This mandatory ie is more or less a place holder in this case. + */ + call->bc.transcapability = PRI_TRANS_CAP_DIGITAL; + call->bc.transmoderate = TRANS_MODE_64_CIRCUIT; + break; + } + /* Fall through */ + default: + pri_error(ctrl, "!! Coding-standard field is not Q.931.\n"); + return -1; } return 0; } @@ -1394,51 +1524,51 @@ static int transmit_bearer_capability(int full_ie, struct pri *ctrl, q931_call * return 4; } - tc = call->transcapability; + tc = call->bc.transcapability; ie->data[0] = 0x80 | tc; - ie->data[1] = call->transmoderate | 0x80; + ie->data[1] = call->bc.transmoderate | 0x80; pos = 2; /* octet 4.1 exists iff mode/rate is multirate */ - if (call->transmoderate == TRANS_MODE_MULTIRATE ) { - ie->data[pos++] = call->transmultiple | 0x80; + if (call->bc.transmoderate == TRANS_MODE_MULTIRATE ) { + ie->data[pos++] = call->bc.transmultiple | 0x80; } if ((tc & PRI_TRANS_CAP_DIGITAL) && (ctrl->switchtype == PRI_SWITCH_EUROISDN_E1) && - (call->transmoderate == TRANS_MODE_PACKET)) { + (call->bc.transmoderate == TRANS_MODE_PACKET)) { /* Apparently EuroISDN switches don't seem to like user layer 2/3 */ return 4; } - if ((tc & PRI_TRANS_CAP_DIGITAL) && (call->transmoderate == TRANS_MODE_64_CIRCUIT)) { + if ((tc & PRI_TRANS_CAP_DIGITAL) && (call->bc.transmoderate == TRANS_MODE_64_CIRCUIT)) { /* Unrestricted digital 64k data calls don't use user layer 2/3 */ return 4; } - if (call->transmoderate != TRANS_MODE_PACKET) { + if (call->bc.transmoderate != TRANS_MODE_PACKET) { /* If you have an AT&T 4ESS, you don't send any more info */ - if ((ctrl->switchtype != PRI_SWITCH_ATT4ESS) && (call->userl1 > -1)) { - ie->data[pos++] = call->userl1 | 0x80; /* XXX Ext bit? XXX */ - if (call->userl1 == PRI_LAYER_1_ITU_RATE_ADAPT) { - ie->data[pos++] = call->rateadaption | 0x80; + if ((ctrl->switchtype != PRI_SWITCH_ATT4ESS) && (call->bc.userl1 > -1)) { + ie->data[pos++] = call->bc.userl1 | 0x80; /* XXX Ext bit? XXX */ + if (call->bc.userl1 == PRI_LAYER_1_ITU_RATE_ADAPT) { + ie->data[pos++] = call->bc.rateadaption | 0x80; } return pos + 2; } - ie->data[pos++] = 0xa0 | (call->userl1 & 0x1f); + ie->data[pos++] = 0xa0 | (call->bc.userl1 & 0x1f); - if (call->userl1 == PRI_LAYER_1_ITU_RATE_ADAPT) { + if (call->bc.userl1 == PRI_LAYER_1_ITU_RATE_ADAPT) { ie->data[pos-1] &= ~0x80; /* clear EXT bit in octet 5 */ - ie->data[pos++] = call->rateadaption | 0x80; + ie->data[pos++] = call->bc.rateadaption | 0x80; } } - if (call->userl2 != -1) - ie->data[pos++] = 0xc0 | (call->userl2 & 0x1f); + if (call->bc.userl2 != -1) + ie->data[pos++] = 0xc0 | (call->bc.userl2 & 0x1f); - if (call->userl3 != -1) - ie->data[pos++] = 0xe0 | (call->userl3 & 0x1f); + if (call->bc.userl3 != -1) + ie->data[pos++] = 0xe0 | (call->bc.userl3 & 0x1f); return pos + 2; } @@ -2266,7 +2396,7 @@ static int transmit_facility(int full_ie, struct pri *ctrl, q931_call *call, int for (prev = &call->apdus, cur = call->apdus; cur; prev = &cur->next, cur = cur->next) { - if (!cur->sent && cur->message == msgtype) { + if (!cur->sent && (cur->message == msgtype || cur->message == Q931_ANY_MESSAGE)) { break; } } @@ -3490,13 +3620,13 @@ void q931_init_call_record(struct pri *ctrl, struct q931_call *call, int cr) call->peercallstate = Q931_CALL_STATE_NULL; call->sugcallstate = Q931_CALL_STATE_NOT_SET; call->ri = -1; - call->transcapability = -1; - call->transmoderate = -1; - call->transmultiple = -1; - call->userl1 = -1; - call->userl2 = -1; - call->userl3 = -1; - call->rateadaption = -1; + call->bc.transcapability = -1; + call->bc.transmoderate = -1; + call->bc.transmultiple = -1; + call->bc.userl1 = -1; + call->bc.userl2 = -1; + call->bc.userl3 = -1; + call->bc.rateadaption = -1; call->progress = -1; call->causecode = -1; call->causeloc = -1; @@ -3692,6 +3822,15 @@ static void cleanup_and_free_call(struct q931_call *cur) stop_t303(cur); pri_schedule_del(cur->pri, cur->retranstimer); pri_call_apdu_queue_cleanup(cur); + if (cur->cc.record) { + /* Unlink CC associations. */ + if (cur->cc.record->original_call == cur) { + cur->cc.record->original_call = NULL; + } + if (cur->cc.record->signaling == cur) { + cur->cc.record->signaling = NULL; + } + } free(cur); } @@ -3836,7 +3975,8 @@ static int add_ie(struct pri *ctrl, q931_call *call, int msgtype, int ie, q931_i int res, total_res; int have_shift; int ies_count, order; - for (x=0;x 0) { if ((iet->ie & 0x80) == 0) /* Multibyte IE */ iet->len = res - 2; + if (msgtype == Q931_SETUP && *codeset == 0) { + switch (iet->ie) { + case Q931_BEARER_CAPABILITY: + if (!(call->cc.saved_ie_flags & CC_SAVED_IE_BC)) { + /* Save first BC ie contents for possible CC. */ + call->cc.saved_ie_flags |= CC_SAVED_IE_BC; + q931_append_ie_contents(&call->cc.saved_ie_contents, + iet); + } + break; + case Q931_LOW_LAYER_COMPAT: + if (!(call->cc.saved_ie_flags & CC_SAVED_IE_LLC)) { + /* Save first LLC ie contents for possible CC. */ + call->cc.saved_ie_flags |= CC_SAVED_IE_LLC; + q931_append_ie_contents(&call->cc.saved_ie_contents, + iet); + } + break; + case Q931_HIGH_LAYER_COMPAT: + if (!(call->cc.saved_ie_flags & CC_SAVED_IE_HLC)) { + /* Save first HLC ie contents for possible CC. */ + call->cc.saved_ie_flags |= CC_SAVED_IE_HLC; + q931_append_ie_contents(&call->cc.saved_ie_contents, + iet); + } + break; + default: + break; + } + } total_res += res; maxlen -= res; iet = (q931_ie *)((char *)iet + res); @@ -3988,7 +4158,34 @@ static int q931_handle_ie(int codeset, struct pri *ctrl, q931_call *c, int msg, int full_ie = Q931_FULL_IE(codeset, ie->ie); if (ctrl->debug & PRI_DEBUG_Q931_STATE) pri_message(ctrl, "-- Processing IE %d (cs%d, %s)\n", ie->ie, codeset, ie2str(full_ie)); - for (x=0;xie) { + case Q931_BEARER_CAPABILITY: + if (!(c->cc.saved_ie_flags & CC_SAVED_IE_BC)) { + /* Save first BC ie contents for possible CC. */ + c->cc.saved_ie_flags |= CC_SAVED_IE_BC; + q931_append_ie_contents(&c->cc.saved_ie_contents, ie); + } + break; + case Q931_LOW_LAYER_COMPAT: + if (!(c->cc.saved_ie_flags & CC_SAVED_IE_LLC)) { + /* Save first LLC ie contents for possible CC. */ + c->cc.saved_ie_flags |= CC_SAVED_IE_LLC; + q931_append_ie_contents(&c->cc.saved_ie_contents, ie); + } + break; + case Q931_HIGH_LAYER_COMPAT: + if (!(c->cc.saved_ie_flags & CC_SAVED_IE_HLC)) { + /* Save first HLC ie contents for possible CC. */ + c->cc.saved_ie_flags |= CC_SAVED_IE_HLC; + q931_append_ie_contents(&c->cc.saved_ie_contents, ie); + } + break; + default: + break; + } + } + for (x = 0; x < ARRAY_LEN(ies); ++x) { if (full_ie == ies[x].ie) { if (ies[x].receive) return ies[x].receive(full_ie, ctrl, c, msg, ie, ielen(ie)); @@ -4354,6 +4551,10 @@ static int call_progress_ies[] = { Q931_PROGRESS_INDICATOR, -1 }; int q931_call_progress(struct pri *ctrl, q931_call *c, int channel, int info) { + if (c->ourcallstate == Q931_CALL_STATE_CALL_INDEPENDENT_SERVICE) { + /* Cannot send this message when in this state */ + return 0; + } if (channel) { c->ds1no = (channel & 0xff00) >> 8; c->ds1explicit = (channel & 0x10000) >> 16; @@ -4376,6 +4577,10 @@ int q931_call_progress(struct pri *ctrl, q931_call *c, int channel, int info) int q931_call_progress_with_cause(struct pri *ctrl, q931_call *c, int channel, int info, int cause) { + if (c->ourcallstate == Q931_CALL_STATE_CALL_INDEPENDENT_SERVICE) { + /* Cannot send this message when in this state */ + return 0; + } if (channel) { c->ds1no = (channel & 0xff00) >> 8; c->ds1explicit = (channel & 0x10000) >> 16; @@ -4412,7 +4617,10 @@ int q931_call_proceeding(struct pri *ctrl, q931_call *c, int channel, int info) /* We have already sent a PROCEEDING message. Don't send another one. */ return 0; } - + if (c->ourcallstate == Q931_CALL_STATE_CALL_INDEPENDENT_SERVICE) { + /* Cannot send this message when in this state */ + return 0; + } if (channel) { c->ds1no = (channel & 0xff00) >> 8; c->ds1explicit = (channel & 0x10000) >> 16; @@ -4440,6 +4648,10 @@ static int alerting_ies[] = { Q931_IE_FACILITY, -1 }; int q931_alerting(struct pri *ctrl, q931_call *c, int channel, int info) { + if (c->ourcallstate == Q931_CALL_STATE_CALL_INDEPENDENT_SERVICE) { + /* Cannot send this message when in this state */ + return 0; + } if (!c->proc) q931_call_proceeding(ctrl, c, channel, 0); if (info) { @@ -4463,6 +4675,10 @@ int q931_alerting(struct pri *ctrl, q931_call *c, int channel, int info) break; } + if (c->cc.record) { + pri_cc_event(ctrl, c, c->cc.record, CC_EVENT_MSG_ALERTING); + } + return send_message(ctrl, c, Q931_ALERTING, alerting_ies); } @@ -4470,6 +4686,10 @@ static int setup_ack_ies[] = { Q931_CHANNEL_IDENT, Q931_IE_FACILITY, Q931_PROGRE int q931_setup_ack(struct pri *ctrl, q931_call *c, int channel, int nonisdn) { + if (c->ourcallstate == Q931_CALL_STATE_CALL_INDEPENDENT_SERVICE) { + /* Cannot send this message when in this state */ + return 0; + } if (channel) { c->ds1no = (channel & 0xff00) >> 8; c->ds1explicit = (channel & 0x10000) >> 16; @@ -4563,6 +4783,10 @@ static int connect_ies[] = { int q931_connect(struct pri *ctrl, q931_call *c, int channel, int nonisdn) { + if (c->ourcallstate == Q931_CALL_STATE_CALL_INDEPENDENT_SERVICE) { + /* Cannot send this message when in this state */ + return 0; + } if (channel) { c->ds1no = (channel & 0xff00) >> 8; c->ds1explicit = (channel & 0x10000) >> 16; @@ -4613,7 +4837,7 @@ int q931_connect(struct pri *ctrl, q931_call *c, int channel, int nonisdn) return send_message(ctrl, c, Q931_CONNECT, connect_ies); } -static int release_ies[] = { Q931_CAUSE, Q931_IE_USER_USER, -1 }; +static int release_ies[] = { Q931_CAUSE, Q931_IE_FACILITY, Q931_IE_USER_USER, -1 }; int q931_release(struct pri *ctrl, q931_call *c, int cause) { @@ -4631,9 +4855,16 @@ int q931_release(struct pri *ctrl, q931_call *c, int cause) } else { c->retranstimer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T308], pri_release_finaltimeout, c); } + if (c->cc.record) { + pri_cc_event(ctrl, c, c->cc.record, CC_EVENT_MSG_RELEASE); + } return send_message(ctrl, c, Q931_RELEASE, release_ies); - } else + } else { + if (c->cc.record) { + pri_cc_event(ctrl, c, c->cc.record, CC_EVENT_MSG_RELEASE_COMPLETE); + } return send_message(ctrl, c, Q931_RELEASE_COMPLETE, release_ies); /* Yes, release_ies, not release_complete_ies */ + } } else return 0; } @@ -4672,6 +4903,11 @@ int q931_disconnect(struct pri *ctrl, q931_call *c, int cause) c->causecode = CODE_CCITT; c->causeloc = LOC_PRIV_NET_LOCAL_USER; c->sendhangupack = 1; + + if (c->cc.record) { + pri_cc_event(ctrl, c, c->cc.record, CC_EVENT_MSG_DISCONNECT); + } + pri_schedule_del(ctrl, c->retranstimer); c->retranstimer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T305], pri_disconnect_timeout, c); return send_message(ctrl, c, Q931_DISCONNECT, disconnect_ies); @@ -4751,17 +4987,24 @@ static void t303_expiry(void *data) c->t303_expirycnt++; c->t303_timer = 0; + /*! + * \todo XXX Resending the SETUP message loses all facility ies + * that the original may have had. Actually any message Q.931 + * retransmits will lose the facility ies. + */ + if (c->cause != -1) { /* We got a DISCONNECT, RELEASE, or RELEASE_COMPLETE and no other responses. */ pri_fake_clearing(c); } else if (c->t303_expirycnt < 2) { + c->cc.saved_ie_contents.length = 0; + c->cc.saved_ie_flags = 0; if (ctrl->subchannel && !ctrl->bri) res = send_message(ctrl, c, Q931_SETUP, gr303_setup_ies); else if (c->cis_call) res = send_message(ctrl, c, Q931_SETUP, cis_setup_ies); else res = send_message(ctrl, c, Q931_SETUP, setup_ies); - if (res) { pri_error(c->pri, "Error resending setup message!\n"); } @@ -4791,13 +5034,14 @@ int q931_setup(struct pri *ctrl, q931_call *c, struct pri_sr *req) c->keypad_digits[0] = '\0'; } - c->transcapability = req->transmode; - c->transmoderate = TRANS_MODE_64_CIRCUIT; + c->bc.transcapability = req->transmode; + c->bc.transmoderate = TRANS_MODE_64_CIRCUIT; if (!req->userl1) req->userl1 = PRI_LAYER_1_ULAW; - c->userl1 = req->userl1; - c->userl2 = -1; - c->userl3 = -1; + c->bc.userl1 = req->userl1; + c->bc.userl2 = -1; + c->bc.userl3 = -1; + c->ds1no = (req->channel & 0xff00) >> 8; c->ds1explicit = (req->channel & 0x10000) >> 16; if ((ctrl->localtype == PRI_CPE) && ctrl->subchannel && !ctrl->bri) { @@ -4816,6 +5060,7 @@ int q931_setup(struct pri *ctrl, q931_call *c, struct pri_sr *req) c->nonisdn = req->nonisdn; c->newcall = 0; c->cis_call = req->cis_call; + c->cis_recognized = req->cis_call; c->cis_auto_disconnect = req->cis_auto_disconnect; c->complete = req->numcomplete; @@ -4845,6 +5090,15 @@ int q931_setup(struct pri *ctrl, q931_call *c, struct pri_sr *req) pri_call_add_standard_apdus(ctrl, c); + /* Save the initial cc-parties. */ + c->cc.party_a = c->local_id; + c->cc.originated = 1; + if (c->redirecting.from.number.valid) { + c->cc.initially_redirected = 1; + } + + c->cc.saved_ie_contents.length = 0; + c->cc.saved_ie_flags = 0; if (ctrl->subchannel && !ctrl->bri) res = send_message(ctrl, c, Q931_SETUP, gr303_setup_ies); else if (c->cis_call) @@ -4867,13 +5121,49 @@ int q931_setup(struct pri *ctrl, q931_call *c, struct pri_sr *req) } -static int release_complete_ies[] = { Q931_IE_USER_USER, -1 }; +static int register_ies[] = { Q931_IE_FACILITY, -1 }; + +/*! + * \brief Build and send the REGISTER message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg + * + * \retval 0 on success. + * \retval -1 on error. + */ +int q931_register(struct pri *ctrl, q931_call *call) +{ + int res; + + call->newcall = 0; + + call->cis_call = 1; + call->cis_recognized = 1; + call->cis_auto_disconnect = 0; + call->chanflags = FLAG_EXCLUSIVE;/* For safety mark this channel as exclusive. */ + call->channelno = 0; + + res = send_message(ctrl, call, Q931_REGISTER, register_ies); + if (!res) { + call->alive = 1; + + UPDATE_OURCALLSTATE(ctrl, call, Q931_CALL_STATE_CALL_INDEPENDENT_SERVICE); + call->peercallstate = Q931_CALL_STATE_CALL_INDEPENDENT_SERVICE; + } + return res; +} + +static int release_complete_ies[] = { Q931_IE_FACILITY, Q931_IE_USER_USER, -1 }; static int q931_release_complete(struct pri *ctrl, q931_call *c, int cause) { int res = 0; UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL); c->peercallstate = Q931_CALL_STATE_NULL; + if (c->cc.record) { + pri_cc_event(ctrl, c, c->cc.record, CC_EVENT_MSG_RELEASE_COMPLETE); + } if (cause > -1) { c->cause = cause; c->causecode = CODE_CCITT; @@ -5528,6 +5818,10 @@ static int __q931_hangup(struct pri *ctrl, q931_call *c, int cause) /* sent RELEASE */ /* don't do anything, waiting for RELEASE_COMPLETE */ break; + case Q931_CALL_STATE_CALL_INDEPENDENT_SERVICE: + /* we sent or received REGISTER */ + q931_release_complete(ctrl, c, cause); + break; case Q931_CALL_STATE_RESTART: case Q931_CALL_STATE_RESTART_REQUEST: /* sent RESTART */ @@ -5654,18 +5948,23 @@ static int prepare_to_handle_q931_message(struct pri *ctrl, q931_mh *mh, q931_ca case Q931_SETUP: if (ctrl->debug & PRI_DEBUG_Q931_STATE) pri_message(ctrl, "-- Processing Q.931 Call Setup\n"); + c->cc.saved_ie_contents.length = 0; + c->cc.saved_ie_flags = 0; + /* Fall through */ + case Q931_REGISTER: c->channelno = -1; c->slotmap = -1; c->chanflags = 0; c->ds1no = 0; c->ri = -1; - c->transcapability = -1; - c->transmoderate = -1; - c->transmultiple = -1; - c->userl1 = -1; - c->userl2 = -1; - c->userl3 = -1; - c->rateadaption = -1; + + c->bc.transcapability = -1; + c->bc.transmoderate = -1; + c->bc.transmultiple = -1; + c->bc.userl1 = -1; + c->bc.userl2 = -1; + c->bc.userl3 = -1; + c->bc.rateadaption = -1; q931_party_address_init(&c->called); q931_party_id_init(&c->local_id); @@ -6078,8 +6377,10 @@ int q931_receive(struct pri *ctrl, int tei, q931_h *h, int len) } } - /* Now handle the facility ie's after all the other ie's were processed. */ - q931_handle_facilities(ctrl, c, mh->msg); + if (!missingmand) { + /* Now handle the facility ie's after all the other ie's were processed. */ + q931_handle_facilities(ctrl, c, mh->msg); + } /* Post handling */ switch (h->pd) { @@ -6540,9 +6841,9 @@ static void q931_fill_ring_event(struct pri *ctrl, struct q931_call *call) ctrl->ev.ring.flexible = !(call->chanflags & FLAG_EXCLUSIVE); ctrl->ev.ring.cref = call->cr; ctrl->ev.ring.call = call->master_call; - ctrl->ev.ring.layer1 = call->userl1; + ctrl->ev.ring.layer1 = call->bc.userl1; ctrl->ev.ring.complete = call->complete; - ctrl->ev.ring.ctype = call->transcapability; + ctrl->ev.ring.ctype = call->bc.transcapability; ctrl->ev.ring.progress = call->progress; ctrl->ev.ring.progressmask = call->progressmask; ctrl->ev.ring.reversecharge = call->reversecharge; @@ -6573,7 +6874,11 @@ static void q931_fill_facility_event(struct pri *ctrl, struct q931_call *call) ctrl->ev.facility.subcmds = &ctrl->subcmds; ctrl->ev.facility.channel = q931_encode_channel(call); ctrl->ev.facility.cref = call->cr; - ctrl->ev.facility.call = call->master_call; + if (q931_is_dummy_call(call)) { + ctrl->ev.facility.call = NULL; + } else { + ctrl->ev.facility.call = call->master_call; + } ctrl->ev.facility.subcall = call; /* Need to do this for backward compatibility with struct pri_event_facname */ @@ -6598,11 +6903,18 @@ static void q931_apdu_timeout(void *data) struct apdu_event *apdu; struct pri *ctrl; struct q931_call *call; + int free_it; apdu = data; call = apdu->call; ctrl = call->pri; + /* + * Extract the APDU from the list so it cannot be + * deleted from under us by the callback. + */ + free_it = pri_call_apdu_extract(call, apdu); + q931_clr_subcommands(ctrl); apdu->response.callback(APDU_CALLBACK_REASON_TIMEOUT, ctrl, call, apdu, NULL); if (ctrl->subcmds.counter_subcmd) { @@ -6610,7 +6922,67 @@ static void q931_apdu_timeout(void *data) ctrl->schedev = 1; } - pri_call_apdu_delete(call, apdu); + if (free_it) { + free(apdu); + } +} + +/*! + * \brief Generic call-completion timeout event handler. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * \param event Event to process. + * + * \retval nonzero if cc record destroyed because FSM completed. + */ +int q931_cc_timeout(struct pri *ctrl, struct pri_cc_record *cc_record, enum CC_EVENTS event) +{ + q931_call *call; + q931_call *dummy; + int fsm_complete; + + q931_clr_subcommands(ctrl); + dummy = cc_record->master->dummy_call; + call = cc_record->signaling; + if (!call) { + /* Substitute the broadcast dummy call reference call. */ + call = dummy; + } + fsm_complete = pri_cc_event(ctrl, call, cc_record, event); + if (ctrl->subcmds.counter_subcmd) { + q931_fill_facility_event(ctrl, dummy); + ctrl->schedev = 1; + } + return fsm_complete; +} + +/*! + * \brief Generic call-completion indirect event creation. + * + * \param ctrl D channel controller. + * \param cc_record Call completion record to process event. + * \param func Function to call that will generate a libpri event. + * + * \return Nothing + */ +void q931_cc_indirect(struct pri *ctrl, struct pri_cc_record *cc_record, void (*func)(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)) +{ + q931_call *call; + q931_call *dummy; + + q931_clr_subcommands(ctrl); + dummy = cc_record->master->dummy_call; + call = cc_record->signaling; + if (!call) { + /* Substitute the broadcast dummy call reference call. */ + call = dummy; + } + func(ctrl, call, cc_record); + if (ctrl->subcmds.counter_subcmd) { + q931_fill_facility_event(ctrl, dummy); + ctrl->schedev = 1; + } } /*! @@ -6756,6 +7128,35 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct ctrl->ev.e = PRI_EVENT_RESTART; ctrl->ev.restart.channel = q931_encode_channel(c); return Q931_RES_HAVEEVENT; + case Q931_REGISTER: + /* Must be new call */ + if (!c->newcall) { + q931_status(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE); + break; + } + c->newcall = 0; + c->alive = 1; + + c->cis_call = 1; + c->chanflags = FLAG_EXCLUSIVE;/* For safety mark this channel as exclusive. */ + c->channelno = 0; + + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_CALL_INDEPENDENT_SERVICE); + c->peercallstate = Q931_CALL_STATE_CALL_INDEPENDENT_SERVICE; + + if (c->cc.hangup_call) { + q931_release_complete(ctrl, c, PRI_CAUSE_NORMAL_CLEARING); + break; + } + if (/* c->cis_call && */ !c->cis_recognized) { + pri_message(ctrl, + "-- CIS connection not marked as handled. Disconnecting it.\n"); + q931_release_complete(ctrl, c, PRI_CAUSE_FACILITY_NOT_IMPLEMENTED); + break; + } + + q931_fill_ring_event(ctrl, c); + return Q931_RES_HAVEEVENT; case Q931_SETUP: if (missingmand) { q931_release_complete(ctrl, c, PRI_CAUSE_MANDATORY_IE_MISSING); @@ -6770,12 +7171,32 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct c->newcall = 0; UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_CALL_PRESENT); c->peercallstate = Q931_CALL_STATE_CALL_INITIATED; - /* it's not yet a call since higher level can respond with RELEASE or RELEASE_COMPLETE */ - c->alive = 0; - if (c->transmoderate != TRANS_MODE_64_CIRCUIT) { + if (c->cis_call) { + /* + * Make call alive so any message events clearing this + * signaling call can pass up any subcmds. + */ + c->alive = 1; + } else { + /* it's not yet a call since higher level can respond with RELEASE or RELEASE_COMPLETE */ + c->alive = 0; + } + if (c->bc.transmoderate != TRANS_MODE_64_CIRCUIT) { q931_release_complete(ctrl, c, PRI_CAUSE_BEARERCAPABILITY_NOTIMPL); break; } + if (c->cc.hangup_call) { + q931_release_complete(ctrl, c, PRI_CAUSE_NORMAL_CLEARING); + break; + } + if (c->cis_call && !c->cis_recognized) { + pri_message(ctrl, "-- CIS call not marked as handled. Disconnecting it.\n"); + q931_release_complete(ctrl, c, PRI_CAUSE_FACILITY_NOT_IMPLEMENTED); + break; + } + + /* Save the initial cc-parties. (Incoming SETUP can only be a master call.) */ + c->cc.party_a = c->remote_id; q931_fill_ring_event(ctrl, c); return Q931_RES_HAVEEVENT; @@ -6798,6 +7219,14 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct libpri_copy_string(ctrl->ev.ringing.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.ringing.useruserinfo)); c->useruserinfo[0] = '\0'; + switch (ctrl->switchtype) { + case PRI_SWITCH_QSIG: + pri_cc_qsig_determine_available(ctrl, c); + break; + default: + break; + } + for (cur = c->apdus; cur; cur = cur->next) { if (!cur->sent && cur->message == Q931_FACILITY) { q931_facility(ctrl, c); @@ -6982,6 +7411,7 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct c->hangupinitiated = 1; UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL); c->peercallstate = Q931_CALL_STATE_NULL; + ctrl->ev.hangup.subcmds = &ctrl->subcmds; ctrl->ev.hangup.channel = q931_encode_channel(c); ctrl->ev.hangup.cause = c->cause; @@ -6992,6 +7422,11 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct ctrl->ev.hangup.call_active = NULL; libpri_copy_string(ctrl->ev.hangup.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.hangup.useruserinfo)); c->useruserinfo[0] = '\0'; + + if (c->cc.record && c->cc.record->signaling == c) { + pri_cc_event(ctrl, c, c->cc.record, CC_EVENT_SIGNALING_GONE); + } + /* Free resources */ if (c->alive) { ctrl->ev.e = PRI_EVENT_HANGUP; @@ -7015,12 +7450,19 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct /* Force cause to be mandatory IE missing */ c->cause = PRI_CAUSE_MANDATORY_IE_MISSING; } + if (c->ourcallstate == Q931_CALL_STATE_RELEASE_REQUEST) c->peercallstate = Q931_CALL_STATE_NULL; else { c->peercallstate = Q931_CALL_STATE_RELEASE_REQUEST; } UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL); + + if (c->newcall) { + q931_release_complete(ctrl, c, PRI_CAUSE_INVALID_CALL_REFERENCE); + break; + } + ctrl->ev.e = PRI_EVENT_HANGUP; ctrl->ev.hangup.subcmds = &ctrl->subcmds; ctrl->ev.hangup.channel = q931_encode_channel(c); @@ -7032,15 +7474,17 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct ctrl->ev.hangup.call_active = NULL; libpri_copy_string(ctrl->ev.hangup.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.hangup.useruserinfo)); c->useruserinfo[0] = '\0'; + + if (c->cc.record && c->cc.record->signaling == c) { + pri_cc_event(ctrl, c, c->cc.record, CC_EVENT_SIGNALING_GONE); + } + /* Don't send release complete if they send us release while we sent it, assume a NULL state */ - if (c->newcall) - q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE); - else if (c->outboundbroadcast && (c != q931_get_subcall_winner(c->master_call))) + if (c->outboundbroadcast && (c != q931_get_subcall_winner(c->master_call))) { return pri_hangup(ctrl, c, -1); - else - return Q931_RES_HAVEEVENT; - break; + } + return Q931_RES_HAVEEVENT; case Q931_DISCONNECT: c->hangupinitiated = 1; if (missingmand) { @@ -7117,10 +7561,27 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct ctrl->ev.hangup.aoc_units = c->aoc_units; libpri_copy_string(ctrl->ev.hangup.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.hangup.useruserinfo)); c->useruserinfo[0] = '\0'; - if (c->alive) + + if (c->alive) { + switch (c->cause) { + case PRI_CAUSE_USER_BUSY: + case PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION: + switch (ctrl->switchtype) { + case PRI_SWITCH_QSIG: + pri_cc_qsig_determine_available(ctrl, c); + break; + default: + break; + } + break; + default: + break; + } + return Q931_RES_HAVEEVENT; - else + } else { pri_hangup(ctrl,c,c->cause); + } break; case Q931_RESTART_ACKNOWLEDGE: UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL); @@ -7541,6 +8002,11 @@ static int pri_internal_clear(void *data) if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, "clearing, alive %d, hangupack %d\n", c->alive, c->sendhangupack); } + + if (c->cc.record && c->cc.record->signaling == c) { + pri_cc_event(ctrl, c, c->cc.record, CC_EVENT_SIGNALING_GONE); + } + /* Free resources */ if (c->alive) { ctrl->ev.e = PRI_EVENT_HANGUP; diff --git a/rose.c b/rose.c index 778f9fd..04f2ad0 100644 --- a/rose.c +++ b/rose.c @@ -200,6 +200,46 @@ static const struct asn1_oid rose_etsi_ect = { /* *INDENT-ON* */ }; +/*! \brief ETSI Status Request OID prefix. */ +static const struct asn1_oid rose_etsi_status_request = { +/* *INDENT-OFF* */ + /* {itu-t(0) identified-organization(4) etsi(0) 196 status-request-procedure(9)} */ + 4, { 4, 0, 196, 9 } +/* *INDENT-ON* */ +}; + +/*! \brief ETSI Call Completion Busy Status OID prefix. */ +static const struct asn1_oid rose_etsi_ccbs = { +/* *INDENT-OFF* */ + /* {ccitt(0) identified-organization(4) etsi(0) 359 operations-and-errors(1)} */ + 4, { 4, 0, 359, 1 } +/* *INDENT-ON* */ +}; + +/*! \brief ETSI Call Completion Busy Status public-private interworking OID prefix. */ +static const struct asn1_oid rose_etsi_ccbs_t = { +/* *INDENT-OFF* */ + /* {ccitt(0) identified-organization(4) etsi(0) 359 private-networks-operations-and-errors(2)} */ + 4, { 4, 0, 359, 2 } +/* *INDENT-ON* */ +}; + +/*! \brief ETSI Call Completion No Reply OID prefix. */ +static const struct asn1_oid rose_etsi_ccnr = { +/* *INDENT-OFF* */ + /* {ccitt(0) identified-organization(4) etsi(0) 1065 operations-and-errors(1)} */ + 4, { 4, 0, 1065, 1 } +/* *INDENT-ON* */ +}; + +/*! \brief ETSI Call Completion No Reply public-private interworking OID prefix. */ +static const struct asn1_oid rose_etsi_ccnr_t = { +/* *INDENT-OFF* */ + /* {ccitt(0) identified-organization(4) etsi(0) 1065 private-networks-operations-and-errors(2)} */ + 4, { 4, 0, 1065, 2 } +/* *INDENT-ON* */ +}; + /*! \brief ETSI specific invoke/result encode/decode message table */ static const struct rose_convert_msg rose_etsi_msgs[] = { /* *INDENT-OFF* */ @@ -361,6 +401,136 @@ static const struct rose_convert_msg rose_etsi_msgs[] = { rose_enc_etsi_EctLoopTest_ARG, rose_enc_etsi_EctLoopTest_RES, rose_dec_etsi_EctLoopTest_ARG, rose_dec_etsi_EctLoopTest_RES }, + + /* + * globalValue's (OIDs) from Status-Request-Procedure + * {itu-t identified-organization etsi(0) 196 status-request-procedure(9)} + */ + { + ROSE_ETSI_StatusRequest, &rose_etsi_status_request, 1, + rose_enc_etsi_StatusRequest_ARG, rose_enc_etsi_StatusRequest_RES, + rose_dec_etsi_StatusRequest_ARG, rose_dec_etsi_StatusRequest_RES + }, + + /* + * globalValue's (OIDs) from CCBS-Operations-and-Errors + * {ccitt identified-organization etsi(0) 359 operations-and-errors(1)} + */ + { + ROSE_ETSI_CallInfoRetain, &rose_etsi_ccbs, 1, + rose_enc_etsi_CallInfoRetain_ARG, NULL, + rose_dec_etsi_CallInfoRetain_ARG, NULL + }, + { + ROSE_ETSI_CCBSRequest, &rose_etsi_ccbs, 2, + rose_enc_etsi_CCBSRequest_ARG, rose_enc_etsi_CCBSRequest_RES, + rose_dec_etsi_CCBSRequest_ARG, rose_dec_etsi_CCBSRequest_RES + }, + { + ROSE_ETSI_CCBSDeactivate, &rose_etsi_ccbs, 3, + rose_enc_etsi_CCBSDeactivate_ARG, NULL, + rose_dec_etsi_CCBSDeactivate_ARG, NULL + }, + { + ROSE_ETSI_CCBSInterrogate, &rose_etsi_ccbs, 4, + rose_enc_etsi_CCBSInterrogate_ARG, rose_enc_etsi_CCBSInterrogate_RES, + rose_dec_etsi_CCBSInterrogate_ARG, rose_dec_etsi_CCBSInterrogate_RES + }, + { + ROSE_ETSI_CCBSErase, &rose_etsi_ccbs, 5, + rose_enc_etsi_CCBSErase_ARG, NULL, + rose_dec_etsi_CCBSErase_ARG, NULL + }, + { + ROSE_ETSI_CCBSRemoteUserFree, &rose_etsi_ccbs, 6, + rose_enc_etsi_CCBSRemoteUserFree_ARG, NULL, + rose_dec_etsi_CCBSRemoteUserFree_ARG, NULL + }, + { + ROSE_ETSI_CCBSCall, &rose_etsi_ccbs, 7, + rose_enc_etsi_CCBSCall_ARG, NULL, + rose_dec_etsi_CCBSCall_ARG, NULL + }, + { + ROSE_ETSI_CCBSStatusRequest, &rose_etsi_ccbs, 8, + rose_enc_etsi_CCBSStatusRequest_ARG, rose_enc_etsi_CCBSStatusRequest_RES, + rose_dec_etsi_CCBSStatusRequest_ARG, rose_dec_etsi_CCBSStatusRequest_RES + }, + { + ROSE_ETSI_CCBSBFree, &rose_etsi_ccbs, 9, + rose_enc_etsi_CCBSBFree_ARG, NULL, + rose_dec_etsi_CCBSBFree_ARG, NULL + }, + { + ROSE_ETSI_EraseCallLinkageID, &rose_etsi_ccbs, 10, + rose_enc_etsi_EraseCallLinkageID_ARG, NULL, + rose_dec_etsi_EraseCallLinkageID_ARG, NULL + }, + { + ROSE_ETSI_CCBSStopAlerting, &rose_etsi_ccbs, 11, + rose_enc_etsi_CCBSStopAlerting_ARG, NULL, + rose_dec_etsi_CCBSStopAlerting_ARG, NULL + }, + + /* + * globalValue's (OIDs) from CCBS-private-networks-Operations-and-Errors + * {ccitt identified-organization etsi(0) 359 private-networks-operations-and-errors(2)} + */ + { + ROSE_ETSI_CCBS_T_Request, &rose_etsi_ccbs_t, 1, + rose_enc_etsi_CCBS_T_Request_ARG, rose_enc_etsi_CCBS_T_Request_RES, + rose_dec_etsi_CCBS_T_Request_ARG, rose_dec_etsi_CCBS_T_Request_RES + }, + { + ROSE_ETSI_CCBS_T_Call, &rose_etsi_ccbs_t, 2, + NULL, NULL, + NULL, NULL + }, + { + ROSE_ETSI_CCBS_T_Suspend, &rose_etsi_ccbs_t, 3, + NULL, NULL, + NULL, NULL + }, + { + ROSE_ETSI_CCBS_T_Resume, &rose_etsi_ccbs_t, 4, + NULL, NULL, + NULL, NULL + }, + { + ROSE_ETSI_CCBS_T_RemoteUserFree, &rose_etsi_ccbs_t, 5, + NULL, NULL, + NULL, NULL + }, + { + ROSE_ETSI_CCBS_T_Available, &rose_etsi_ccbs_t, 6, + NULL, NULL, + NULL, NULL + }, + + /* + * globalValue's (OIDs) from CCNR-Operations-and-Errors + * {ccitt identified-organization etsi(0) 1065 operations-and-errors(1)} + */ + { + ROSE_ETSI_CCNRRequest, &rose_etsi_ccnr, 1, + rose_enc_etsi_CCNRRequest_ARG, rose_enc_etsi_CCNRRequest_RES, + rose_dec_etsi_CCNRRequest_ARG, rose_dec_etsi_CCNRRequest_RES + }, + { + ROSE_ETSI_CCNRInterrogate, &rose_etsi_ccnr, 2, + rose_enc_etsi_CCNRInterrogate_ARG, rose_enc_etsi_CCNRInterrogate_RES, + rose_dec_etsi_CCNRInterrogate_ARG, rose_dec_etsi_CCNRInterrogate_RES + }, + + /* + * globalValue's (OIDs) from CCNR-private-networks-Operations-and-Errors + * {ccitt identified-organization etsi(0) 1065 private-networks-operations-and-errors(2)} + */ + { + ROSE_ETSI_CCNR_T_Request, &rose_etsi_ccnr_t, 1, + rose_enc_etsi_CCNR_T_Request_ARG, rose_enc_etsi_CCNR_T_Request_RES, + rose_dec_etsi_CCNR_T_Request_ARG, rose_dec_etsi_CCNR_T_Request_RES + }, /* *INDENT-ON* */ }; @@ -463,6 +633,60 @@ static const struct rose_convert_error rose_etsi_errors[] = { ROSE_ERROR_ECT_LinkIdNotAssignedByNetwork, &rose_etsi_ect, 21, NULL, NULL }, + + /* + * globalValue Errors (OIDs) from CCBS-Operations-and-Errors + * {ccitt identified-organization etsi(0) 359 operations-and-errors(1)} + */ + { + ROSE_ERROR_CCBS_InvalidCallLinkageID, &rose_etsi_ccbs, 20, + NULL, NULL + }, + { + ROSE_ERROR_CCBS_InvalidCCBSReference, &rose_etsi_ccbs, 21, + NULL, NULL + }, + { + ROSE_ERROR_CCBS_LongTermDenial, &rose_etsi_ccbs, 22, + NULL, NULL + }, + { + ROSE_ERROR_CCBS_ShortTermDenial, &rose_etsi_ccbs, 23, + NULL, NULL + }, + { + ROSE_ERROR_CCBS_IsAlreadyActivated, &rose_etsi_ccbs, 24, + NULL, NULL + }, + { + ROSE_ERROR_CCBS_AlreadyAccepted, &rose_etsi_ccbs, 25, + NULL, NULL + }, + { + ROSE_ERROR_CCBS_OutgoingCCBSQueueFull, &rose_etsi_ccbs, 26, + NULL, NULL + }, + { + ROSE_ERROR_CCBS_CallFailureReasonNotBusy, &rose_etsi_ccbs, 27, + NULL, NULL + }, + { + ROSE_ERROR_CCBS_NotReadyForCall, &rose_etsi_ccbs, 28, + NULL, NULL + }, + + /* + * globalValue Errors (OIDs) from CCBS-private-networks-Operations-and-Errors + * {ccitt identified-organization etsi(0) 359 private-networks-operations-and-errors(2)} + */ + { + ROSE_ERROR_CCBS_T_LongTermDenial, &rose_etsi_ccbs_t, 20, + NULL, NULL + }, + { + ROSE_ERROR_CCBS_T_ShortTermDenial, &rose_etsi_ccbs_t, 21, + NULL, NULL + }, /* *INDENT-ON* */ }; @@ -649,6 +873,51 @@ static const struct rose_convert_msg rose_qsig_msgs[] = { rose_dec_qsig_DummyArg_ARG, NULL }, + /* + * localValue's from Q.SIG SS-CC-Operations + * { iso(1) standard(0) pss1-call-completion(13870) operations(0) } + */ + { + ROSE_QSIG_CcbsRequest, NULL, 40, + rose_enc_qsig_CcbsRequest_ARG, rose_enc_qsig_CcbsRequest_RES, + rose_dec_qsig_CcbsRequest_ARG, rose_dec_qsig_CcbsRequest_RES + }, + { + ROSE_QSIG_CcnrRequest, NULL, 27, + rose_enc_qsig_CcnrRequest_ARG, rose_enc_qsig_CcnrRequest_RES, + rose_dec_qsig_CcnrRequest_ARG, rose_dec_qsig_CcnrRequest_RES + }, + { + ROSE_QSIG_CcCancel, NULL, 28, + rose_enq_qsig_CcCancel_ARG, NULL, + rose_dec_qsig_CcCancel_ARG, NULL + }, + { + ROSE_QSIG_CcExecPossible, NULL, 29, + rose_enq_qsig_CcExecPossible_ARG, NULL, + rose_dec_qsig_CcExecPossible_ARG, NULL + }, + { + ROSE_QSIG_CcPathReserve, NULL, 30, + rose_enc_qsig_CcPathReserve_ARG, rose_enc_qsig_CcPathReserve_RES, + rose_dec_qsig_CcPathReserve_ARG, rose_dec_qsig_CcPathReserve_RES + }, + { + ROSE_QSIG_CcRingout, NULL, 31, + rose_enc_qsig_CcRingout_ARG, NULL, + rose_dec_qsig_CcRingout_ARG, NULL + }, + { + ROSE_QSIG_CcSuspend, NULL, 32, + rose_enc_qsig_CcSuspend_ARG, NULL, + rose_dec_qsig_CcSuspend_ARG, NULL + }, + { + ROSE_QSIG_CcResume, NULL, 33, + rose_enc_qsig_CcResume_ARG, NULL, + rose_dec_qsig_CcResume_ARG, NULL + }, + /* * localValue's from Q.SIG SS-MWI-Operations * { iso(1) standard(0) pss1-message-waiting-indication(15506) message-waiting-operations(0) } @@ -800,6 +1069,31 @@ static const struct rose_convert_error rose_qsig_errors[] = { NULL, NULL }, + /* + * localValue's from Q.SIG SS-CC-Operations + * { iso(1) standard(0) pss1-call-completion(13870) operations(0) } + */ + { + ROSE_ERROR_QSIG_ShortTermRejection, NULL, 1010, + NULL, NULL + }, + { + ROSE_ERROR_QSIG_LongTermRejection, NULL, 1011, + NULL, NULL + }, + { + ROSE_ERROR_QSIG_RemoteUserBusyAgain, NULL, 1012, + NULL, NULL + }, + { + ROSE_ERROR_QSIG_FailureToMatch, NULL, 1013, + NULL, NULL + }, + { + ROSE_ERROR_QSIG_FailedDueToInterworking, NULL, 1014, + NULL, NULL + }, + /* * localValue's from Q.SIG SS-MWI-Operations * { iso(1) standard(0) pss1-message-waiting-indication(15506) message-waiting-operations(0) } @@ -1069,6 +1363,34 @@ const char *rose_operation2str(enum rose_operation operation) { ROSE_ETSI_AOCECurrency, "ROSE_ETSI_AOCECurrency" }, { ROSE_ETSI_AOCEChargingUnit, "ROSE_ETSI_AOCEChargingUnit" }, + { ROSE_ETSI_StatusRequest, "ROSE_ETSI_StatusRequest" }, + + { ROSE_ETSI_CallInfoRetain, "ROSE_ETSI_CallInfoRetain" }, + { ROSE_ETSI_EraseCallLinkageID, "ROSE_ETSI_EraseCallLinkageID" }, + { ROSE_ETSI_CCBSDeactivate, "ROSE_ETSI_CCBSDeactivate" }, + { ROSE_ETSI_CCBSErase, "ROSE_ETSI_CCBSErase" }, + { ROSE_ETSI_CCBSRemoteUserFree, "ROSE_ETSI_CCBSRemoteUserFree" }, + { ROSE_ETSI_CCBSCall, "ROSE_ETSI_CCBSCall" }, + { ROSE_ETSI_CCBSStatusRequest, "ROSE_ETSI_CCBSStatusRequest" }, + { ROSE_ETSI_CCBSBFree, "ROSE_ETSI_CCBSBFree" }, + { ROSE_ETSI_CCBSStopAlerting, "ROSE_ETSI_CCBSStopAlerting" }, + + { ROSE_ETSI_CCBSRequest, "ROSE_ETSI_CCBSRequest" }, + { ROSE_ETSI_CCBSInterrogate, "ROSE_ETSI_CCBSInterrogate" }, + + { ROSE_ETSI_CCNRRequest, "ROSE_ETSI_CCNRRequest" }, + { ROSE_ETSI_CCNRInterrogate, "ROSE_ETSI_CCNRInterrogate" }, + + { ROSE_ETSI_CCBS_T_Call, "ROSE_ETSI_CCBS_T_Call" }, + { ROSE_ETSI_CCBS_T_Suspend, "ROSE_ETSI_CCBS_T_Suspend" }, + { ROSE_ETSI_CCBS_T_Resume, "ROSE_ETSI_CCBS_T_Resume" }, + { ROSE_ETSI_CCBS_T_RemoteUserFree, "ROSE_ETSI_CCBS_T_RemoteUserFree" }, + { ROSE_ETSI_CCBS_T_Available, "ROSE_ETSI_CCBS_T_Available" }, + + { ROSE_ETSI_CCBS_T_Request, "ROSE_ETSI_CCBS_T_Request" }, + + { ROSE_ETSI_CCNR_T_Request, "ROSE_ETSI_CCNR_T_Request" }, + { ROSE_QSIG_CallingName, "ROSE_QSIG_CallingName" }, { ROSE_QSIG_CalledName, "ROSE_QSIG_CalledName" }, { ROSE_QSIG_ConnectedName, "ROSE_QSIG_ConnectedName" }, @@ -1103,6 +1425,15 @@ const char *rose_operation2str(enum rose_operation operation) { ROSE_QSIG_DivertingLegInformation3, "ROSE_QSIG_DivertingLegInformation3" }, { ROSE_QSIG_CfnrDivertedLegFailed, "ROSE_QSIG_CfnrDivertedLegFailed" }, + { ROSE_QSIG_CcbsRequest, "ROSE_QSIG_CcbsRequest" }, + { ROSE_QSIG_CcnrRequest, "ROSE_QSIG_CcnrRequest" }, + { ROSE_QSIG_CcCancel, "ROSE_QSIG_CcCancel" }, + { ROSE_QSIG_CcExecPossible, "ROSE_QSIG_CcExecPossible" }, + { ROSE_QSIG_CcPathReserve, "ROSE_QSIG_CcPathReserve" }, + { ROSE_QSIG_CcRingout, "ROSE_QSIG_CcRingout" }, + { ROSE_QSIG_CcSuspend, "ROSE_QSIG_CcSuspend" }, + { ROSE_QSIG_CcResume, "ROSE_QSIG_CcResume" }, + { ROSE_QSIG_MWIActivate, "ROSE_QSIG_MWIActivate" }, { ROSE_QSIG_MWIDeactivate, "ROSE_QSIG_MWIDeactivate" }, { ROSE_QSIG_MWIInterrogate, "ROSE_QSIG_MWIInterrogate" }, @@ -1161,6 +1492,19 @@ const char *rose_error2str(enum rose_error_code code) { ROSE_ERROR_ECT_LinkIdNotAssignedByNetwork, "ECT: Link ID Not Assigned By Network" }, + { ROSE_ERROR_CCBS_InvalidCallLinkageID, "CCBS: Invalid Call Linkage ID" }, + { ROSE_ERROR_CCBS_InvalidCCBSReference, "CCBS: Invalid CCBS Reference" }, + { ROSE_ERROR_CCBS_LongTermDenial, "CCBS: Long Term Denial" }, + { ROSE_ERROR_CCBS_ShortTermDenial, "CCBS: Short Term Denial" }, + { ROSE_ERROR_CCBS_IsAlreadyActivated, "CCBS: Is Already Activated" }, + { ROSE_ERROR_CCBS_AlreadyAccepted, "CCBS: Already Accepted" }, + { ROSE_ERROR_CCBS_OutgoingCCBSQueueFull, "CCBS: Outgoing CCBS Queue Full" }, + { ROSE_ERROR_CCBS_CallFailureReasonNotBusy, "CCBS: Call Failure Reason Not Busy" }, + { ROSE_ERROR_CCBS_NotReadyForCall, "CCBS: Not Ready For Call" }, + + { ROSE_ERROR_CCBS_T_LongTermDenial, "CCBS-T: Long Term Denial" }, + { ROSE_ERROR_CCBS_T_ShortTermDenial, "CCBS-T: Short Term Denial" }, + /* Q.SIG specific errors */ { ROSE_ERROR_QSIG_Unspecified, "Unspecified" }, @@ -1173,6 +1517,12 @@ const char *rose_error2str(enum rose_error_code code) { ROSE_ERROR_QSIG_Div_TemporarilyUnavailable, "Diversion: Temporarily Unavailable" }, { ROSE_ERROR_QSIG_Div_NotAuthorized, "Diversion: Not Authorized" }, + { ROSE_ERROR_QSIG_ShortTermRejection, "CC: Short Term Rejection" }, + { ROSE_ERROR_QSIG_LongTermRejection, "CC: Long Term Rejection" }, + { ROSE_ERROR_QSIG_RemoteUserBusyAgain, "CC: Remote User Busy Again" }, + { ROSE_ERROR_QSIG_FailureToMatch, "CC: Failure To Match" }, + { ROSE_ERROR_QSIG_FailedDueToInterworking, "CC: Failed Due To Interworking" }, + { ROSE_ERROR_QSIG_InvalidMsgCentreId, "MWI: Invalid Message Center ID" }, /* DMS-100 specific errors */ diff --git a/rose.h b/rose.h index 7b5b5a3..f9b4607 100644 --- a/rose.h +++ b/rose.h @@ -103,6 +103,37 @@ enum rose_operation { ROSE_ETSI_EctInform, /*!< Invoke only */ ROSE_ETSI_EctLoopTest, /*!< Invoke/Result */ + /* ETSI Status-Request-Procedure */ + ROSE_ETSI_StatusRequest, /*!< Invoke/Result */ + + /* ETSI CCBS-Operations-and-Errors */ + ROSE_ETSI_CallInfoRetain, /*!< Invoke only */ + ROSE_ETSI_CCBSRequest, /*!< Invoke/Result */ + ROSE_ETSI_CCBSDeactivate, /*!< Invoke/Result */ + ROSE_ETSI_CCBSInterrogate, /*!< Invoke/Result */ + ROSE_ETSI_CCBSErase, /*!< Invoke only */ + ROSE_ETSI_CCBSRemoteUserFree, /*!< Invoke only */ + ROSE_ETSI_CCBSCall, /*!< Invoke only */ + ROSE_ETSI_CCBSStatusRequest, /*!< Invoke/Result */ + ROSE_ETSI_CCBSBFree, /*!< Invoke only */ + ROSE_ETSI_EraseCallLinkageID, /*!< Invoke only */ + ROSE_ETSI_CCBSStopAlerting, /*!< Invoke only */ + + /* ETSI CCBS-private-networks-Operations-and-Errors */ + ROSE_ETSI_CCBS_T_Request, /*!< Invoke/Result */ + ROSE_ETSI_CCBS_T_Call, /*!< Invoke only */ + ROSE_ETSI_CCBS_T_Suspend, /*!< Invoke only */ + ROSE_ETSI_CCBS_T_Resume, /*!< Invoke only */ + ROSE_ETSI_CCBS_T_RemoteUserFree, /*!< Invoke only */ + ROSE_ETSI_CCBS_T_Available, /*!< Invoke only */ + + /* ETSI CCNR-Operations-and-Errors */ + ROSE_ETSI_CCNRRequest, /*!< Invoke/Result */ + ROSE_ETSI_CCNRInterrogate, /*!< Invoke/Result */ + + /* ETSI CCNR-private-networks-Operations-and-Errors */ + ROSE_ETSI_CCNR_T_Request, /*!< Invoke/Result */ + /* Q.SIG Name-Operations */ ROSE_QSIG_CallingName, /*!< Invoke only */ ROSE_QSIG_CalledName, /*!< Invoke only */ @@ -141,6 +172,16 @@ enum rose_operation { ROSE_QSIG_DivertingLegInformation3, /*!< Invoke only */ ROSE_QSIG_CfnrDivertedLegFailed, /*!< Invoke only */ + /* Q.SIG SS-CC-Operations */ + ROSE_QSIG_CcbsRequest, /*!< Invoke/Result */ + ROSE_QSIG_CcnrRequest, /*!< Invoke/Result */ + ROSE_QSIG_CcCancel, /*!< Invoke only */ + ROSE_QSIG_CcExecPossible, /*!< Invoke only */ + ROSE_QSIG_CcPathReserve, /*!< Invoke/Result */ + ROSE_QSIG_CcRingout, /*!< Invoke only */ + ROSE_QSIG_CcSuspend, /*!< Invoke only */ + ROSE_QSIG_CcResume, /*!< Invoke only */ + /* Q.SIG SS-MWI-Operations */ ROSE_QSIG_MWIActivate, /*!< Invoke/Result */ ROSE_QSIG_MWIDeactivate, /*!< Invoke/Result */ @@ -198,6 +239,21 @@ enum rose_error_code { /* ETSI Explicit-Call-Transfer-Operations-and-Errors */ ROSE_ERROR_ECT_LinkIdNotAssignedByNetwork, + /* ETSI CCBS-Operations-and-Errors */ + ROSE_ERROR_CCBS_InvalidCallLinkageID, + ROSE_ERROR_CCBS_InvalidCCBSReference, + ROSE_ERROR_CCBS_LongTermDenial, + ROSE_ERROR_CCBS_ShortTermDenial, + ROSE_ERROR_CCBS_IsAlreadyActivated, + ROSE_ERROR_CCBS_AlreadyAccepted, + ROSE_ERROR_CCBS_OutgoingCCBSQueueFull, + ROSE_ERROR_CCBS_CallFailureReasonNotBusy, + ROSE_ERROR_CCBS_NotReadyForCall, + + /* ETSI CCBS-private-networks-Operations-and-Errors */ + ROSE_ERROR_CCBS_T_LongTermDenial, + ROSE_ERROR_CCBS_T_ShortTermDenial, + /* Q.SIG from various specifications */ ROSE_ERROR_QSIG_Unspecified, @@ -213,6 +269,13 @@ enum rose_error_code { ROSE_ERROR_QSIG_Div_TemporarilyUnavailable, ROSE_ERROR_QSIG_Div_NotAuthorized, + /* Q.SIG SS-CC-Operations */ + ROSE_ERROR_QSIG_ShortTermRejection, + ROSE_ERROR_QSIG_LongTermRejection, + ROSE_ERROR_QSIG_RemoteUserBusyAgain, + ROSE_ERROR_QSIG_FailureToMatch, + ROSE_ERROR_QSIG_FailedDueToInterworking, + /* Q.SIG SS-MWI-Operations */ ROSE_ERROR_QSIG_InvalidMsgCentreId, @@ -1671,6 +1734,351 @@ struct roseEtsiEctLoopTest_RES { /* ------------------------------------------------------------------- */ +/* + * ARGUMENT SEQUENCE { + * compatibilityMode CompatibilityMode, + * + * -- The BC, HLC (optional) and LLC (optional) information + * -- elements shall be embedded in q931InfoElement + * q931InformationElement Q931InformationElement + * } + */ +struct roseEtsiStatusRequest_ARG { + /*! \brief The BC, HLC (optional) and LLC (optional) information */ + struct roseQ931ie q931ie; + /*! \brief q931ie.contents "allocated" after the stucture. */ + unsigned char q931ie_contents[ROSE_Q931_MAX_BC + ROSE_Q931_MAX_HLC + + ROSE_Q931_MAX_LLC + 1]; + + /*! \details allBasicServices(0), oneOrMoreBasicServices(1) */ + u_int8_t compatibility_mode; +}; + +/* + * RESULT StatusResult + */ +struct roseEtsiStatusRequest_RES { + /*! \details compatibleAndFree(0), compatibleAndBusy(1), incompatible(2) */ + u_int8_t status; +}; + + +/* ------------------------------------------------------------------- */ + + +/* + * CallLinkageID ::= INTEGER (0..127) + * CCBSReference ::= INTEGER (0..127) + */ + +/* + * ARGUMENT callLinkageID CallLinkageID + */ +struct roseEtsiCallInfoRetain_ARG { + /*! \brief Call Linkage Record ID */ + u_int8_t call_linkage_id; +}; + +/* + * ARGUMENT callLinkageID CallLinkageID + */ +struct roseEtsiEraseCallLinkageID_ARG { + /*! \brief Call Linkage Record ID */ + u_int8_t call_linkage_id; +}; + + +/* + * ARGUMENT callLinkageID CallLinkageID + */ +struct roseEtsiCCBSRequest_ARG { + /*! \brief Call Linkage Record ID */ + u_int8_t call_linkage_id; +}; + +/* + * RESULT SEQUENCE { + * recallMode RecallMode, + * cCBSReference CCBSReference + * } + */ +struct roseEtsiCCBSRequest_RES { + /*! \details globalRecall(0), specificRecall(1) */ + u_int8_t recall_mode; + + /*! \brief CCBS Record ID */ + u_int8_t ccbs_reference; +}; + + +/* + * ARGUMENT cCBSReference CCBSReference + */ +struct roseEtsiCCBSDeactivate_ARG { + /*! \brief CCBS Record ID */ + u_int8_t ccbs_reference; +}; + + +/* + * ARGUMENT SEQUENCE { + * cCBSReference CCBSReference OPTIONAL, + * partyNumberOfA PartyNumber OPTIONAL + * } + */ +struct roseEtsiCCBSInterrogate_ARG { + /*! \brief Party A number (Optional) */ + struct rosePartyNumber a_party_number; + + /*! \brief TRUE if CCBSReference present */ + u_int8_t ccbs_reference_present; + + /*! \brief CCBS Record ID (optional) */ + u_int8_t ccbs_reference; +}; + +/* + * -- The Bearer capability, High layer compatibility (optional) + * -- and Low layer compatibility (optional) information elements + * -- shall be embedded in q931InfoElement. + * CallInformation ::= SEQUENCE { + * addressOfB Address, + * q931InfoElement Q931InformationElement, + * cCBSReference CCBSReference, + * subAddressOfA PartySubaddress OPTIONAL + * } + */ +struct roseEtsiCallInformation { + /*! \brief The BC, HLC (optional) and LLC (optional) information */ + struct roseQ931ie q931ie; + /*! \brief q931ie.contents "allocated" after the stucture. */ + unsigned char q931ie_contents[ROSE_Q931_MAX_BC + ROSE_Q931_MAX_HLC + + ROSE_Q931_MAX_LLC + 1]; + + /*! \brief Address of B */ + struct roseAddress address_of_b; + + /*! \brief Subaddress of A (Optional) */ + struct rosePartySubaddress subaddress_of_a; + + /*! \brief CCBS Record ID */ + u_int8_t ccbs_reference; +}; + +/* + * CallDetails ::= SEQUENCE SIZE(1..5) OF CallInformation + */ +struct roseEtsiCallDetailsList { + struct roseEtsiCallInformation list[5]; + + /*! \brief Number of CallDetails records present */ + u_int8_t num_records; +}; + +/* + * RESULT SEQUENCE { + * recallMode RecallMode, + * callDetails CallDetails OPTIONAL + * } + */ +struct roseEtsiCCBSInterrogate_RES { + struct roseEtsiCallDetailsList call_details; + + /*! \details globalRecall(0), specificRecall(1) */ + u_int8_t recall_mode; +}; + + +/* + * ARGUMENT SEQUENCE { + * recallMode RecallMode, + * cCBSReference CCBSReference, + * addressOfB Address, + * q931InfoElement Q931InformationElement, + * eraseReason CCBSEraseReason + * } + */ +struct roseEtsiCCBSErase_ARG { + /*! \brief The BC, HLC (optional) and LLC (optional) information */ + struct roseQ931ie q931ie; + /*! \brief q931ie.contents "allocated" after the stucture. */ + unsigned char q931ie_contents[ROSE_Q931_MAX_BC + ROSE_Q931_MAX_HLC + + ROSE_Q931_MAX_LLC + 1]; + + /*! \brief Address of B */ + struct roseAddress address_of_b; + + /*! \details globalRecall(0), specificRecall(1) */ + u_int8_t recall_mode; + + /*! \brief CCBS Record ID */ + u_int8_t ccbs_reference; + + /*! + * \brief CCBS Erase reason + * \details + * normal-unspecified(0), + * t-CCBS2-timeout(1), + * t-CCBS3-timeout(2), + * basic-call-failed(3) + */ + u_int8_t reason; +}; + +/* + * ARGUMENT SEQUENCE { + * recallMode RecallMode, + * cCBSReference CCBSReference, + * addressOfB Address, + * q931InfoElement Q931InformationElement + * } + */ +struct roseEtsiCCBSRemoteUserFree_ARG { + /*! \brief The BC, HLC (optional) and LLC (optional) information */ + struct roseQ931ie q931ie; + /*! \brief q931ie.contents "allocated" after the stucture. */ + unsigned char q931ie_contents[ROSE_Q931_MAX_BC + ROSE_Q931_MAX_HLC + + ROSE_Q931_MAX_LLC + 1]; + + /*! \brief Address of B */ + struct roseAddress address_of_b; + + /*! \details globalRecall(0), specificRecall(1) */ + u_int8_t recall_mode; + + /*! \brief CCBS Record ID */ + u_int8_t ccbs_reference; +}; + +/* + * ARGUMENT cCBSReference CCBSReference + */ +struct roseEtsiCCBSCall_ARG { + /*! \brief CCBS Record ID */ + u_int8_t ccbs_reference; +}; + + +/* + * ARGUMENT SEQUENCE { + * recallMode RecallMode, + * cCBSReference CCBSReference, + * q931InfoElement Q931InformationElement + * } + */ +struct roseEtsiCCBSStatusRequest_ARG { + /*! \brief The BC, HLC (optional) and LLC (optional) information */ + struct roseQ931ie q931ie; + /*! \brief q931ie.contents "allocated" after the stucture. */ + unsigned char q931ie_contents[ROSE_Q931_MAX_BC + ROSE_Q931_MAX_HLC + + ROSE_Q931_MAX_LLC + 1]; + + /*! \details globalRecall(0), specificRecall(1) */ + u_int8_t recall_mode; + + /*! \brief CCBS Record ID */ + u_int8_t ccbs_reference; +}; + +/* + * RESULT BOOLEAN -- free=TRUE, busy=FALSE + */ +struct roseEtsiCCBSStatusRequest_RES { + /*! \brief TRUE if User A is free */ + u_int8_t free; +}; + + +/* + * ARGUMENT SEQUENCE { + * recallMode RecallMode, + * cCBSReference CCBSReference, + * addressOfB Address, + * q931InfoElement Q931InformationElement + * } + */ +struct roseEtsiCCBSBFree_ARG { + /*! \brief The BC, HLC (optional) and LLC (optional) information */ + struct roseQ931ie q931ie; + /*! \brief q931ie.contents "allocated" after the stucture. */ + unsigned char q931ie_contents[ROSE_Q931_MAX_BC + ROSE_Q931_MAX_HLC + + ROSE_Q931_MAX_LLC + 1]; + + /*! \brief Address of B */ + struct roseAddress address_of_b; + + /*! \details globalRecall(0), specificRecall(1) */ + u_int8_t recall_mode; + + /*! \brief CCBS Record ID */ + u_int8_t ccbs_reference; +}; + +/* + * ARGUMENT cCBSReference CCBSReference + */ +struct roseEtsiCCBSStopAlerting_ARG { + /*! \brief CCBS Record ID */ + u_int8_t ccbs_reference; +}; + + +/* ------------------------------------------------------------------- */ + + +/* + * ARGUMENT SEQUENCE { + * destinationAddress Address, + * + * -- contains HLC, LLC and BC information + * q931InfoElement Q931InformationElement, + * + * retentionSupported [1] IMPLICIT BOOLEAN DEFAULT FALSE, + * + * -- The use of this parameter is specified in + * -- EN 300 195-1 for interaction of CCBS with CLIP + * presentationAllowedIndicator [2] IMPLICIT BOOLEAN OPTIONAL, + * + * -- The use of this parameter is specified in + * -- EN 300 195-1 for interaction of CCBS with CLIP + * originatingAddress Address OPTIONAL + * } + */ +struct roseEtsiCCBS_T_Request_ARG { + /*! \brief The BC, HLC (optional) and LLC (optional) information */ + struct roseQ931ie q931ie; + /*! \brief q931ie.contents "allocated" after the stucture. */ + unsigned char q931ie_contents[ROSE_Q931_MAX_BC + ROSE_Q931_MAX_HLC + + ROSE_Q931_MAX_LLC + 1]; + + /*! \brief Address of B */ + struct roseAddress destination; + + /*! \brief Caller-ID Address (Present if Originating.Party.LengthOfNumber is nonzero) */ + struct roseAddress originating; + + /*! \brief TRUE if the PresentationAllowedIndicator is present */ + u_int8_t presentation_allowed_indicator_present; + + /*! \brief TRUE if presentation is allowed for the originating address (optional) */ + u_int8_t presentation_allowed_indicator; + + /*! \brief TRUE if User A's CCBS request is continued if user B is busy again. */ + u_int8_t retention_supported; +}; + +/* + * RESULT retentionSupported BOOLEAN -- Default False + */ +struct roseEtsiCCBS_T_Request_RES { + /*! \brief TRUE if User A's CCBS request is continued if user B is busy again. */ + u_int8_t retention_supported; +}; + + +/* ------------------------------------------------------------------- */ + + /* * Name ::= CHOICE { * -- iso8859-1 is implied in namePresentationAllowedSimple. @@ -2835,6 +3243,146 @@ struct roseQsigDivertingLegInformation3_ARG { /* ------------------------------------------------------------------- */ +/* + * CcExtension ::= CHOICE { + * none NULL, + * single [14] IMPLICIT Extension, + * multiple [15] IMPLICIT SEQUENCE OF Extension + * } + */ + +/* + * CcRequestArg ::= SEQUENCE { + * numberA PresentedNumberUnscreened, + * numberB PartyNumber, + * + * -- permitted information elements are: + * -- Bearer capability; + * -- Low layer compatibility; + * -- High layer compatibility + * service PSS1InformationElement, + * subaddrA [10] EXPLICIT PartySubaddress OPTIONAL, + * subaddrB [11] EXPLICIT PartySubaddress OPTIONAL, + * can-retain-service [12] IMPLICIT BOOLEAN DEFAULT FALSE, + * + * -- TRUE: signalling connection to be retained; + * -- FALSE: signalling connection to be released; + * -- omission: release or retain signalling connection + * retain-sig-connection [13] IMPLICIT BOOLEAN OPTIONAL, + * extension CcExtension OPTIONAL + * } + */ +struct roseQsigCcRequestArg { + struct rosePresentedNumberUnscreened number_a; + struct rosePartyNumber number_b; + + /*! + * \brief subaddrA (optional) + * The subaddress is present if the length is nonzero. + */ + struct rosePartySubaddress subaddr_a; + + /*! + * \brief subaddrB (optional) + * The subaddress is present if the length is nonzero. + */ + struct rosePartySubaddress subaddr_b; + + /*! + * \brief The BC, HLC (optional) and LLC (optional) information. + * \note The ASN.1 field name is service. + */ + struct roseQ931ie q931ie; + /*! \brief q931ie.contents "allocated" after the stucture. */ + unsigned char q931ie_contents[ROSE_Q931_MAX_BC + ROSE_Q931_MAX_HLC + + ROSE_Q931_MAX_LLC + 1]; + + /*! \brief TRUE if can retain service (DEFAULT FALSE) */ + u_int8_t can_retain_service; + + /*! + * \brief TRUE if retain_sig_connection present + * \note If not present then the signaling connection could be + * released or retained. + */ + u_int8_t retain_sig_connection_present; + + /*! + * \brief Determine if the signalling connection should be retained. + * \note TRUE if signalling connection to be retained. + * \note FALSE if signalling connection to be released. + */ + u_int8_t retain_sig_connection; +}; + +/* + * CcRequestRes ::= SEQUENCE { + * no-path-reservation [0] IMPLICIT BOOLEAN DEFAULT FALSE, + * retain-service [1] IMPLICIT BOOLEAN DEFAULT FALSE, + * extension CcExtension OPTIONAL + * } + */ +struct roseQsigCcRequestRes { + /*! \brief TRUE if no path reservation. (DEFAULT FALSE) */ + u_int8_t no_path_reservation; + + /*! \brief TRUE if agree to retain service (DEFAULT FALSE) */ + u_int8_t retain_service; +}; + +/* + * CcOptionalArg ::= CHOICE { + * fullArg [0] IMPLICIT SEQUENCE { + * numberA PartyNumber, + * numberB PartyNumber, + * + * -- permitted information elements are: + * -- Bearer capability; + * -- Low layer compatibility; + * -- High layer compatibility. + * service PSS1InformationElement, + * subaddrA [10] EXPLICIT PartySubaddress OPTIONAL, + * subaddrB [11] EXPLICIT PartySubaddress OPTIONAL, + * extension CcExtension OPTIONAL + * }, + * extArg CcExtension + * } + */ +struct roseQsigCcOptionalArg { +#if 1 /* The conditional is here to indicate fullArg values grouping. */ + struct rosePartyNumber number_a; + struct rosePartyNumber number_b; + + /*! + * \brief subaddrA (optional) + * The subaddress is present if the length is nonzero. + */ + struct rosePartySubaddress subaddr_a; + + /*! + * \brief subaddrB (optional) + * The subaddress is present if the length is nonzero. + */ + struct rosePartySubaddress subaddr_b; + + /*! + * \brief The BC, HLC (optional) and LLC (optional) information. + * \note The ASN.1 field name is service. + */ + struct roseQ931ie q931ie; + /*! \brief q931ie.contents "allocated" after the stucture. */ + unsigned char q931ie_contents[ROSE_Q931_MAX_BC + ROSE_Q931_MAX_HLC + + ROSE_Q931_MAX_LLC + 1]; +#endif /* end fullArg values */ + + /*! \brief TRUE if the fullArg values are present. */ + u_int8_t full_arg_present; +}; + + +/* ------------------------------------------------------------------- */ + + /* * MsgCentreId ::= CHOICE { * integer [0] IMPLICIT INTEGER (0..65535), @@ -3283,6 +3831,34 @@ union rose_msg_invoke_etsi_args { struct roseEtsiSubaddressTransfer_ARG SubaddressTransfer; struct roseEtsiEctInform_ARG EctInform; struct roseEtsiEctLoopTest_ARG EctLoopTest; + + /* ETSI Status Request (CCBS/CCNR support) */ + struct roseEtsiStatusRequest_ARG StatusRequest; + + /* ETSI CCBS/CCNR support */ + struct roseEtsiCallInfoRetain_ARG CallInfoRetain; + struct roseEtsiEraseCallLinkageID_ARG EraseCallLinkageID; + struct roseEtsiCCBSDeactivate_ARG CCBSDeactivate; + struct roseEtsiCCBSErase_ARG CCBSErase; + struct roseEtsiCCBSRemoteUserFree_ARG CCBSRemoteUserFree; + struct roseEtsiCCBSCall_ARG CCBSCall; + struct roseEtsiCCBSStatusRequest_ARG CCBSStatusRequest; + struct roseEtsiCCBSBFree_ARG CCBSBFree; + struct roseEtsiCCBSStopAlerting_ARG CCBSStopAlerting; + + /* ETSI CCBS */ + struct roseEtsiCCBSRequest_ARG CCBSRequest; + struct roseEtsiCCBSInterrogate_ARG CCBSInterrogate; + + /* ETSI CCNR */ + struct roseEtsiCCBSRequest_ARG CCNRRequest; + struct roseEtsiCCBSInterrogate_ARG CCNRInterrogate; + + /* ETSI CCBS-T */ + struct roseEtsiCCBS_T_Request_ARG CCBS_T_Request; + + /* ETSI CCNR-T */ + struct roseEtsiCCBS_T_Request_ARG CCNR_T_Request; }; /*! \brief Facility ie result etsi messages with arguments. */ @@ -3297,6 +3873,26 @@ union rose_msg_result_etsi_args { /* ETSI Explicit Call Transfer (ECT) */ struct roseEtsiEctLinkIdRequest_RES EctLinkIdRequest; struct roseEtsiEctLoopTest_RES EctLoopTest; + + /* ETSI Status Request (CCBS/CCNR support) */ + struct roseEtsiStatusRequest_RES StatusRequest; + + /* ETSI CCBS/CCNR support */ + struct roseEtsiCCBSStatusRequest_RES CCBSStatusRequest; + + /* ETSI CCBS */ + struct roseEtsiCCBSRequest_RES CCBSRequest; + struct roseEtsiCCBSInterrogate_RES CCBSInterrogate; + + /* ETSI CCNR */ + struct roseEtsiCCBSRequest_RES CCNRRequest; + struct roseEtsiCCBSInterrogate_RES CCNRInterrogate; + + /* ETSI CCBS-T */ + struct roseEtsiCCBS_T_Request_RES CCBS_T_Request; + + /* ETSI CCNR-T */ + struct roseEtsiCCBS_T_Request_RES CCNR_T_Request; }; /*! \brief Facility ie invoke qsig messages with arguments. */ @@ -3333,6 +3929,12 @@ union rose_msg_invoke_qsig_args { struct roseQsigDivertingLegInformation2_ARG DivertingLegInformation2; struct roseQsigDivertingLegInformation3_ARG DivertingLegInformation3; + /* Q.SIG SS-CC-Operations */ + struct roseQsigCcRequestArg CcbsRequest; + struct roseQsigCcRequestArg CcnrRequest; + struct roseQsigCcOptionalArg CcCancel; + struct roseQsigCcOptionalArg CcExecPossible; + /* Q.SIG SS-MWI-Operations */ struct roseQsigMWIActivateArg MWIActivate; struct roseQsigMWIDeactivateArg MWIDeactivate; @@ -3351,6 +3953,10 @@ union rose_msg_result_qsig_args { /* Q.SIG Call-Diversion-Operations */ struct roseQsigForwardingList InterrogateDiversionQ; + /* Q.SIG SS-CC-Operations */ + struct roseQsigCcRequestRes CcbsRequest; + struct roseQsigCcRequestRes CcnrRequest; + /* Q.SIG SS-MWI-Operations */ struct roseQsigMWIInterrogateRes MWIInterrogate; }; diff --git a/rose_etsi_cc.c b/rose_etsi_cc.c new file mode 100644 index 0000000..6cd430b --- /dev/null +++ b/rose_etsi_cc.c @@ -0,0 +1,1853 @@ +/* + * libpri: An implementation of Primary Rate ISDN + * + * Copyright (C) 2009 Digium, Inc. + * + * Richard Mudgett + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2 as published by the + * Free Software Foundation. See the LICENSE file included with + * this program for more details. + * + * In addition, when this program is distributed with Asterisk in + * any form that would qualify as a 'combined work' or as a + * 'derivative work' (but not mere aggregation), you can redistribute + * and/or modify the combination under the terms of the license + * provided with that copy of Asterisk, instead of the license + * terms granted here. + */ + +/*! + * \file + * \brief ROSE Status-Request/CCBS/CCBS-T/CCNR/CCNR-T operations + * + * Status-Request ETS 300 196-1 D.7 + * CCBS Supplementary Services ETS 300 359-1 + * CCNR Supplementary Services ETS 301 065-1 + * + * \author Richard Mudgett + */ + + +#include "compat.h" +#include "libpri.h" +#include "pri_internal.h" +#include "rose.h" +#include "rose_internal.h" +#include "asn1.h" + + +/* ------------------------------------------------------------------- */ + +/*! + * \internal + * \brief Encode the array of call information details type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param call_information Call information record to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_etsi_CallInformation(struct pri *ctrl, unsigned char *pos, + unsigned char *end, unsigned tag, + const struct roseEtsiCallInformation *call_information) +{ + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + ASN1_CALL(pos, rose_enc_Address(ctrl, pos, end, ASN1_TAG_SEQUENCE, + &call_information->address_of_b)); + ASN1_CALL(pos, rose_enc_Q931ie(ctrl, pos, end, ASN1_CLASS_APPLICATION | 0, + &call_information->q931ie)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, + call_information->ccbs_reference)); + if (call_information->subaddress_of_a.length) { + ASN1_CALL(pos, rose_enc_PartySubaddress(ctrl, pos, end, + &call_information->subaddress_of_a)); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \internal + * \brief Encode the array of call information details type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param call_details Call detail list information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_etsi_CallDetails(struct pri *ctrl, unsigned char *pos, + unsigned char *end, unsigned tag, const struct roseEtsiCallDetailsList *call_details) +{ + unsigned index; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + for (index = 0; index < call_details->num_records; ++index) { + ASN1_CALL(pos, rose_enc_etsi_CallInformation(ctrl, pos, end, ASN1_TAG_SEQUENCE, + &call_details->list[index])); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the StatusRequest invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_StatusRequest_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseEtsiStatusRequest_ARG *status_request; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + status_request = &args->etsi.StatusRequest; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + status_request->compatibility_mode)); + ASN1_CALL(pos, rose_enc_Q931ie(ctrl, pos, end, ASN1_CLASS_APPLICATION | 0, + &status_request->q931ie)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the StatusRequest result facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_StatusRequest_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args) +{ + return asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, args->etsi.StatusRequest.status); +} + +/*! + * \brief Encode the CallInfoRetain invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_CallInfoRetain_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, + args->etsi.CallInfoRetain.call_linkage_id); +} + +/*! + * \brief Encode the EraseCallLinkageID invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_EraseCallLinkageID_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, + args->etsi.EraseCallLinkageID.call_linkage_id); +} + +/*! + * \brief Encode the CCBSDeactivate invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_CCBSDeactivate_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, + args->etsi.CCBSDeactivate.ccbs_reference); +} + +/*! + * \brief Encode the CCBSErase invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_CCBSErase_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseEtsiCCBSErase_ARG *ccbs_erase; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + ccbs_erase = &args->etsi.CCBSErase; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + ccbs_erase->recall_mode)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, + ccbs_erase->ccbs_reference)); + ASN1_CALL(pos, rose_enc_Address(ctrl, pos, end, ASN1_TAG_SEQUENCE, + &ccbs_erase->address_of_b)); + ASN1_CALL(pos, rose_enc_Q931ie(ctrl, pos, end, ASN1_CLASS_APPLICATION | 0, + &ccbs_erase->q931ie)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, ccbs_erase->reason)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the CCBSRemoteUserFree invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_CCBSRemoteUserFree_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseEtsiCCBSRemoteUserFree_ARG *ccbs_remote_user_free; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + ccbs_remote_user_free = &args->etsi.CCBSRemoteUserFree; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + ccbs_remote_user_free->recall_mode)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, + ccbs_remote_user_free->ccbs_reference)); + ASN1_CALL(pos, rose_enc_Address(ctrl, pos, end, ASN1_TAG_SEQUENCE, + &ccbs_remote_user_free->address_of_b)); + ASN1_CALL(pos, rose_enc_Q931ie(ctrl, pos, end, ASN1_CLASS_APPLICATION | 0, + &ccbs_remote_user_free->q931ie)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the CCBSCall invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_CCBSCall_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, args->etsi.CCBSCall.ccbs_reference); +} + +/*! + * \brief Encode the CCBSBFree invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_CCBSBFree_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseEtsiCCBSBFree_ARG *ccbs_b_free; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + ccbs_b_free = &args->etsi.CCBSBFree; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + ccbs_b_free->recall_mode)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, + ccbs_b_free->ccbs_reference)); + ASN1_CALL(pos, rose_enc_Address(ctrl, pos, end, ASN1_TAG_SEQUENCE, + &ccbs_b_free->address_of_b)); + ASN1_CALL(pos, rose_enc_Q931ie(ctrl, pos, end, ASN1_CLASS_APPLICATION | 0, + &ccbs_b_free->q931ie)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the CCBSStopAlerting invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_CCBSStopAlerting_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, + args->etsi.CCBSStopAlerting.ccbs_reference); +} + +/*! + * \brief Encode the CCBSStatusRequest invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_CCBSStatusRequest_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseEtsiCCBSStatusRequest_ARG *ccbs_status_request; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + ccbs_status_request = &args->etsi.CCBSStatusRequest; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + ccbs_status_request->recall_mode)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, + ccbs_status_request->ccbs_reference)); + ASN1_CALL(pos, rose_enc_Q931ie(ctrl, pos, end, ASN1_CLASS_APPLICATION | 0, + &ccbs_status_request->q931ie)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the CCBSStatusRequest result facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_CCBSStatusRequest_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args) +{ + return asn1_enc_boolean(pos, end, ASN1_TYPE_BOOLEAN, + args->etsi.CCBSStatusRequest.free); +} + +/*! + * \internal + * \brief Encode the CCBS/CCNR-Request invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param ccbs_request Information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_etsi_CC_Request_ARG_Backend(struct pri *ctrl, + unsigned char *pos, unsigned char *end, + const struct roseEtsiCCBSRequest_ARG *ccbs_request) +{ + return asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, ccbs_request->call_linkage_id); +} + +/*! + * \brief Encode the CCBS-Request invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_CCBSRequest_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return rose_enc_etsi_CC_Request_ARG_Backend(ctrl, pos, end, &args->etsi.CCBSRequest); +} + +/*! + * \brief Encode the CCNR-Request invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_CCNRRequest_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return rose_enc_etsi_CC_Request_ARG_Backend(ctrl, pos, end, &args->etsi.CCNRRequest); +} + +/*! + * \internal + * \brief Encode the CCBS/CCNR-Request result facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param ccbs_request Information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_etsi_CC_Request_RES_Backend(struct pri *ctrl, + unsigned char *pos, unsigned char *end, + const struct roseEtsiCCBSRequest_RES *ccbs_request) +{ + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + ccbs_request->recall_mode)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, + ccbs_request->ccbs_reference)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the CCBS-Request result facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_CCBSRequest_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args) +{ + return rose_enc_etsi_CC_Request_RES_Backend(ctrl, pos, end, &args->etsi.CCBSRequest); +} + +/*! + * \brief Encode the CCNR-Request result facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_CCNRRequest_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args) +{ + return rose_enc_etsi_CC_Request_RES_Backend(ctrl, pos, end, &args->etsi.CCNRRequest); +} + +/*! + * \internal + * \brief Encode the CCBS/CCNR-Interrogate invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param ccbs_interrogate Information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_etsi_CC_Interrogate_ARG_Backend(struct pri *ctrl, + unsigned char *pos, unsigned char *end, + const struct roseEtsiCCBSInterrogate_ARG *ccbs_interrogate) +{ + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + if (ccbs_interrogate->ccbs_reference_present) { + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, + ccbs_interrogate->ccbs_reference)); + } + if (ccbs_interrogate->a_party_number.length) { + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, + &ccbs_interrogate->a_party_number)); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the CCBSInterrogate invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_CCBSInterrogate_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return rose_enc_etsi_CC_Interrogate_ARG_Backend(ctrl, pos, end, + &args->etsi.CCBSInterrogate); +} + +/*! + * \brief Encode the CCNRInterrogate invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_CCNRInterrogate_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return rose_enc_etsi_CC_Interrogate_ARG_Backend(ctrl, pos, end, + &args->etsi.CCNRInterrogate); +} + +/*! + * \internal + * \brief Encode the CCBS/CCNR-Interrogate result facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param ccbs_interrogate Information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_etsi_CC_Interrogate_RES_Backend(struct pri *ctrl, + unsigned char *pos, unsigned char *end, + const struct roseEtsiCCBSInterrogate_RES *ccbs_interrogate) +{ + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + ccbs_interrogate->recall_mode)); + if (ccbs_interrogate->call_details.num_records) { + ASN1_CALL(pos, rose_enc_etsi_CallDetails(ctrl, pos, end, ASN1_TAG_SEQUENCE, + &ccbs_interrogate->call_details)); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the CCBSInterrogate result facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_CCBSInterrogate_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args) +{ + return rose_enc_etsi_CC_Interrogate_RES_Backend(ctrl, pos, end, + &args->etsi.CCBSInterrogate); +} + +/*! + * \brief Encode the CCNRInterrogate result facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_CCNRInterrogate_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args) +{ + return rose_enc_etsi_CC_Interrogate_RES_Backend(ctrl, pos, end, + &args->etsi.CCNRInterrogate); +} + +/*! + * \internal + * \brief Encode the CCBS-T/CCNR-T-Request invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param ccbs_t_request Information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_etsi_CC_T_Request_ARG_Backend(struct pri *ctrl, + unsigned char *pos, unsigned char *end, + const struct roseEtsiCCBS_T_Request_ARG *ccbs_t_request) +{ + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + ASN1_CALL(pos, rose_enc_Address(ctrl, pos, end, ASN1_TAG_SEQUENCE, + &ccbs_t_request->destination)); + ASN1_CALL(pos, rose_enc_Q931ie(ctrl, pos, end, ASN1_CLASS_APPLICATION | 0, + &ccbs_t_request->q931ie)); + if (ccbs_t_request->retention_supported) { + /* Not the DEFAULT value */ + ASN1_CALL(pos, asn1_enc_boolean(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, + ccbs_t_request->retention_supported)); + } + if (ccbs_t_request->presentation_allowed_indicator_present) { + ASN1_CALL(pos, asn1_enc_boolean(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, + ccbs_t_request->presentation_allowed_indicator)); + } + if (ccbs_t_request->originating.number.length) { + ASN1_CALL(pos, rose_enc_Address(ctrl, pos, end, ASN1_TAG_SEQUENCE, + &ccbs_t_request->originating)); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the CCBS_T_Request invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_CCBS_T_Request_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return rose_enc_etsi_CC_T_Request_ARG_Backend(ctrl, pos, end, + &args->etsi.CCBS_T_Request); +} + +/*! + * \brief Encode the CCNR_T_Request invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_CCNR_T_Request_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return rose_enc_etsi_CC_T_Request_ARG_Backend(ctrl, pos, end, + &args->etsi.CCNR_T_Request); +} + +/*! + * \internal + * \brief Encode the CCBS-T/CCNR-T-Request result facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param ccbs_t_request Information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_etsi_CC_T_Request_RES_Backend(struct pri *ctrl, + unsigned char *pos, unsigned char *end, + const struct roseEtsiCCBS_T_Request_RES *ccbs_t_request) +{ + return asn1_enc_boolean(pos, end, ASN1_TYPE_BOOLEAN, + ccbs_t_request->retention_supported); +} + +/*! + * \brief Encode the CCBS_T_Request result facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_CCBS_T_Request_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args) +{ + return rose_enc_etsi_CC_T_Request_RES_Backend(ctrl, pos, end, + &args->etsi.CCBS_T_Request); +} + +/*! + * \brief Encode the CCNR_T_Request result facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_CCNR_T_Request_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args) +{ + return rose_enc_etsi_CC_T_Request_RES_Backend(ctrl, pos, end, + &args->etsi.CCNR_T_Request); +} + +/*! + * \internal + * \brief Decode the CallInformation argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param name Field name + * \param tag Component tag that identified this production. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param call_information Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_etsi_CallInformation(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseEtsiCallInformation *call_information) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s CallInformation %s\n", name, asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + ASN1_CALL(pos, rose_dec_Address(ctrl, "addressOfB", tag, pos, seq_end, + &call_information->address_of_b)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag & ~ASN1_PC_MASK, ASN1_CLASS_APPLICATION | 0); + ASN1_CALL(pos, rose_dec_Q931ie(ctrl, "q931ie", tag, pos, seq_end, + &call_information->q931ie, sizeof(call_information->q931ie_contents))); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER); + ASN1_CALL(pos, asn1_dec_int(ctrl, "ccbsReference", tag, pos, seq_end, &value)); + call_information->ccbs_reference = value; + + if (pos < seq_end && *pos != ASN1_INDEF_TERM) { + /* The optional subaddress must be present since there is something left. */ + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_PartySubaddress(ctrl, "subaddressOfA", tag, pos, seq_end, + &call_information->subaddress_of_a)); + } else { + /* Subaddress not present */ + call_information->subaddress_of_a.length = 0; + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the array of call information details argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param name Field name + * \param tag Component tag that identified this production. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param call_details Parameter storage array to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_etsi_CallDetails(struct pri *ctrl, const char *name, + unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseEtsiCallDetailsList *call_details) +{ + int length; + int seq_offset; + const unsigned char *seq_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s CallDetails %s\n", name, asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + call_details->num_records = 0; + while (pos < seq_end && *pos != ASN1_INDEF_TERM) { + if (ARRAY_LEN(call_details->list) <= call_details->num_records) { + /* Too many records */ + return NULL; + } + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + ASN1_CALL(pos, rose_dec_etsi_CallInformation(ctrl, "listEntry", tag, pos, + seq_end, &call_details->list[call_details->num_records])); + ++call_details->num_records; + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the StatusRequest invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_StatusRequest_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + struct roseEtsiStatusRequest_ARG *status_request; + int length; + int seq_offset; + const unsigned char *seq_end; + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " StatusRequest %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + status_request = &args->etsi.StatusRequest; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "compatibilityMode", tag, pos, seq_end, &value)); + status_request->compatibility_mode = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag & ~ASN1_PC_MASK, ASN1_CLASS_APPLICATION | 0); + ASN1_CALL(pos, rose_dec_Q931ie(ctrl, "q931ie", tag, pos, seq_end, + &status_request->q931ie, sizeof(status_request->q931ie_contents))); + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the StatusRequest result argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_StatusRequest_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args) +{ + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "status", tag, pos, end, &value)); + args->etsi.StatusRequest.status = value; + + return pos; +} + +/*! + * \brief Decode the CallInfoRetain invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_CallInfoRetain_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER); + ASN1_CALL(pos, asn1_dec_int(ctrl, "callLinkageId", tag, pos, end, &value)); + args->etsi.CallInfoRetain.call_linkage_id = value; + + return pos; +} + +/*! + * \brief Decode the EraseCallLinkageID invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_EraseCallLinkageID_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER); + ASN1_CALL(pos, asn1_dec_int(ctrl, "callLinkageId", tag, pos, end, &value)); + args->etsi.EraseCallLinkageID.call_linkage_id = value; + + return pos; +} + +/*! + * \brief Decode the CCBSDeactivate invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_CCBSDeactivate_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER); + ASN1_CALL(pos, asn1_dec_int(ctrl, "ccbsReference", tag, pos, end, &value)); + args->etsi.CCBSDeactivate.ccbs_reference = value; + + return pos; +} + +/*! + * \brief Decode the CCBSErase invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_CCBSErase_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + struct roseEtsiCCBSErase_ARG *ccbs_erase; + int length; + int seq_offset; + const unsigned char *seq_end; + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " CCBSErase %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + ccbs_erase = &args->etsi.CCBSErase; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "recallMode", tag, pos, seq_end, &value)); + ccbs_erase->recall_mode = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER); + ASN1_CALL(pos, asn1_dec_int(ctrl, "ccbsReference", tag, pos, seq_end, &value)); + ccbs_erase->ccbs_reference = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + ASN1_CALL(pos, rose_dec_Address(ctrl, "addressOfB", tag, pos, seq_end, + &ccbs_erase->address_of_b)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag & ~ASN1_PC_MASK, ASN1_CLASS_APPLICATION | 0); + ASN1_CALL(pos, rose_dec_Q931ie(ctrl, "q931ie", tag, pos, seq_end, + &ccbs_erase->q931ie, sizeof(ccbs_erase->q931ie_contents))); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "eraseReason", tag, pos, seq_end, &value)); + ccbs_erase->reason = value; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the CCBSRemoteUserFree invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_CCBSRemoteUserFree_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + struct roseEtsiCCBSRemoteUserFree_ARG *ccbs_remote_user_free; + int length; + int seq_offset; + const unsigned char *seq_end; + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " CCBSRemoteUserFree %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + ccbs_remote_user_free = &args->etsi.CCBSRemoteUserFree; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "recallMode", tag, pos, seq_end, &value)); + ccbs_remote_user_free->recall_mode = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER); + ASN1_CALL(pos, asn1_dec_int(ctrl, "ccbsReference", tag, pos, seq_end, &value)); + ccbs_remote_user_free->ccbs_reference = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + ASN1_CALL(pos, rose_dec_Address(ctrl, "addressOfB", tag, pos, seq_end, + &ccbs_remote_user_free->address_of_b)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag & ~ASN1_PC_MASK, ASN1_CLASS_APPLICATION | 0); + ASN1_CALL(pos, rose_dec_Q931ie(ctrl, "q931ie", tag, pos, seq_end, + &ccbs_remote_user_free->q931ie, sizeof(ccbs_remote_user_free->q931ie_contents))); + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the CCBSCall invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_CCBSCall_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER); + ASN1_CALL(pos, asn1_dec_int(ctrl, "ccbsReference", tag, pos, end, &value)); + args->etsi.CCBSCall.ccbs_reference = value; + + return pos; +} + +/*! + * \brief Decode the CCBSBFree invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_CCBSBFree_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + struct roseEtsiCCBSBFree_ARG *ccbs_b_free; + int length; + int seq_offset; + const unsigned char *seq_end; + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " CCBSBFree %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + ccbs_b_free = &args->etsi.CCBSBFree; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "recallMode", tag, pos, seq_end, &value)); + ccbs_b_free->recall_mode = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER); + ASN1_CALL(pos, asn1_dec_int(ctrl, "ccbsReference", tag, pos, seq_end, &value)); + ccbs_b_free->ccbs_reference = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + ASN1_CALL(pos, rose_dec_Address(ctrl, "addressOfB", tag, pos, seq_end, + &ccbs_b_free->address_of_b)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag & ~ASN1_PC_MASK, ASN1_CLASS_APPLICATION | 0); + ASN1_CALL(pos, rose_dec_Q931ie(ctrl, "q931ie", tag, pos, seq_end, + &ccbs_b_free->q931ie, sizeof(ccbs_b_free->q931ie_contents))); + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the CCBSStopAlerting invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_CCBSStopAlerting_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER); + ASN1_CALL(pos, asn1_dec_int(ctrl, "ccbsReference", tag, pos, end, &value)); + args->etsi.CCBSStopAlerting.ccbs_reference = value; + + return pos; +} + +/*! + * \brief Decode the CCBSStatusRequest invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_CCBSStatusRequest_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + struct roseEtsiCCBSStatusRequest_ARG *ccbs_status_request; + int length; + int seq_offset; + const unsigned char *seq_end; + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " CCBSStatusRequest %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + ccbs_status_request = &args->etsi.CCBSStatusRequest; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "recallMode", tag, pos, seq_end, &value)); + ccbs_status_request->recall_mode = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER); + ASN1_CALL(pos, asn1_dec_int(ctrl, "ccbsReference", tag, pos, seq_end, &value)); + ccbs_status_request->ccbs_reference = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag & ~ASN1_PC_MASK, ASN1_CLASS_APPLICATION | 0); + ASN1_CALL(pos, rose_dec_Q931ie(ctrl, "q931ie", tag, pos, seq_end, + &ccbs_status_request->q931ie, sizeof(ccbs_status_request->q931ie_contents))); + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the CCBSStatusRequest result argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_CCBSStatusRequest_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args) +{ + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_BOOLEAN); + ASN1_CALL(pos, asn1_dec_boolean(ctrl, "free", tag, pos, end, &value)); + args->etsi.CCBSStatusRequest.free = value; + + return pos; +} + +/*! + * \internal + * \brief Decode the CCBS/CCNR-Request invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param ccbs_request Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_etsi_CC_Request_ARG_Backend(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseEtsiCCBSRequest_ARG *ccbs_request) +{ + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER); + ASN1_CALL(pos, asn1_dec_int(ctrl, "callLinkageId", tag, pos, end, &value)); + ccbs_request->call_linkage_id = value; + + return pos; +} + +/*! + * \brief Decode the CCBSRequest invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_CCBSRequest_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + return rose_dec_etsi_CC_Request_ARG_Backend(ctrl, tag, pos, end, + &args->etsi.CCBSRequest); +} + +/*! + * \brief Decode the CCNRRequest invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_CCNRRequest_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + return rose_dec_etsi_CC_Request_ARG_Backend(ctrl, tag, pos, end, + &args->etsi.CCNRRequest); +} + +/*! + * \internal + * \brief Decode the CCBS/CCNR-Request result argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param name Field name + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param ccbs_request Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_etsi_CC_Request_RES_Backend(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseEtsiCCBSRequest_RES *ccbs_request) +{ + int length; + int seq_offset; + const unsigned char *seq_end; + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " CC%sRequest %s\n", name, asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "recallMode", tag, pos, seq_end, &value)); + ccbs_request->recall_mode = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER); + ASN1_CALL(pos, asn1_dec_int(ctrl, "ccbsReference", tag, pos, seq_end, &value)); + ccbs_request->ccbs_reference = value; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the CCBSRequest result argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_CCBSRequest_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args) +{ + return rose_dec_etsi_CC_Request_RES_Backend(ctrl, "BS", tag, pos, end, + &args->etsi.CCBSRequest); +} + +/*! + * \brief Decode the CCNRRequest result argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_CCNRRequest_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args) +{ + return rose_dec_etsi_CC_Request_RES_Backend(ctrl, "NR", tag, pos, end, + &args->etsi.CCNRRequest); +} + +/*! + * \internal + * \brief Decode the CCBS/CCNR-Interrogate invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param name Field name + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param ccbs_interrogate Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_etsi_CC_Interrogate_ARG_Backend(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseEtsiCCBSInterrogate_ARG *ccbs_interrogate) +{ + int length; + int seq_offset; + const unsigned char *seq_end; + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " CC%sInterrogate %s\n", name, asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + /* + * A sequence specifies an ordered list of component types. + * However, for simplicity we are not checking the order of + * the optional components. + */ + ccbs_interrogate->ccbs_reference = 0; + ccbs_interrogate->ccbs_reference_present = 0; + ccbs_interrogate->a_party_number.length = 0; /* Assume A party number not present */ + while (pos < seq_end && *pos != ASN1_INDEF_TERM) { + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + switch (tag) { + case ASN1_TYPE_INTEGER: + ASN1_CALL(pos, asn1_dec_int(ctrl, "ccbsReference", tag, pos, seq_end, + &value)); + ccbs_interrogate->ccbs_reference = value; + ccbs_interrogate->ccbs_reference_present = 1; + break; + default: + ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "partyNumberOfA", tag, pos, + seq_end, &ccbs_interrogate->a_party_number)); + break; + } + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the CCBSInterrogate invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_CCBSInterrogate_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + return rose_dec_etsi_CC_Interrogate_ARG_Backend(ctrl, "BS", tag, pos, end, + &args->etsi.CCBSInterrogate); +} + +/*! + * \brief Decode the CCNRInterrogate invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_CCNRInterrogate_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + return rose_dec_etsi_CC_Interrogate_ARG_Backend(ctrl, "NR", tag, pos, end, + &args->etsi.CCNRInterrogate); +} + +/*! + * \internal + * \brief Decode the CCBS/CCNR-Interrogate result argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param name Field name + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param ccbs_interrogate Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_etsi_CC_Interrogate_RES_Backend(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseEtsiCCBSInterrogate_RES *ccbs_interrogate) +{ + int length; + int seq_offset; + const unsigned char *seq_end; + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " CC%sInterrogate %s\n", name, asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "recallMode", tag, pos, seq_end, &value)); + ccbs_interrogate->recall_mode = value; + + ccbs_interrogate->call_details.num_records = 0; + if (pos < seq_end && *pos != ASN1_INDEF_TERM) { + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + ASN1_CALL(pos, rose_dec_etsi_CallDetails(ctrl, "callDetails", tag, pos, seq_end, + &ccbs_interrogate->call_details)); + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the CCBSInterrogate result argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_CCBSInterrogate_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args) +{ + return rose_dec_etsi_CC_Interrogate_RES_Backend(ctrl, "BS", tag, pos, end, + &args->etsi.CCBSInterrogate); +} + +/*! + * \brief Decode the CCNRInterrogate result argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_CCNRInterrogate_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args) +{ + return rose_dec_etsi_CC_Interrogate_RES_Backend(ctrl, "NR", tag, pos, end, + &args->etsi.CCNRInterrogate); +} + +/*! + * \internal + * \brief Decode the CCBS-T/CCNR-T-Request invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param name Field name + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param ccbs_t_request Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_etsi_CC_T_Request_ARG_Backend(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseEtsiCCBS_T_Request_ARG *ccbs_t_request) +{ + int length; + int seq_offset; + const unsigned char *seq_end; + const unsigned char *save_pos; + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " CC%s-T-Request %s\n", name, asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + ASN1_CALL(pos, rose_dec_Address(ctrl, "destinationAddress", tag, pos, seq_end, + &ccbs_t_request->destination)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag & ~ASN1_PC_MASK, ASN1_CLASS_APPLICATION | 0); + ASN1_CALL(pos, rose_dec_Q931ie(ctrl, "q931ie", tag, pos, seq_end, + &ccbs_t_request->q931ie, sizeof(ccbs_t_request->q931ie_contents))); + + /* + * A sequence specifies an ordered list of component types. + * However, for simplicity we are not checking the order of + * the remaining optional components. + */ + ccbs_t_request->retention_supported = 0; /* DEFAULT retention_supported value (FALSE) */ + ccbs_t_request->presentation_allowed_indicator = 0; + ccbs_t_request->presentation_allowed_indicator_present = 0; + ccbs_t_request->originating.number.length = 0; /* Assume originating party number not present */ + while (pos < seq_end && *pos != ASN1_INDEF_TERM) { + save_pos = pos; + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + switch (tag) { + case ASN1_CLASS_CONTEXT_SPECIFIC | 1: + ASN1_CALL(pos, asn1_dec_boolean(ctrl, "retentionSupported", tag, pos, + seq_end, &value)); + ccbs_t_request->retention_supported = value; + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 2: + ASN1_CALL(pos, asn1_dec_boolean(ctrl, "presentationAllowedIndicator", tag, + pos, seq_end, &value)); + ccbs_t_request->presentation_allowed_indicator = value; + ccbs_t_request->presentation_allowed_indicator_present = 1; + break; + case ASN1_TAG_SEQUENCE: + ASN1_CALL(pos, rose_dec_Address(ctrl, "originatingAddress", tag, pos, + seq_end, &ccbs_t_request->originating)); + break; + default: + pos = save_pos; + goto cancel_options; + } + } +cancel_options:; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the CCBS_T_Request invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_CCBS_T_Request_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + return rose_dec_etsi_CC_T_Request_ARG_Backend(ctrl, "BS", tag, pos, end, + &args->etsi.CCBS_T_Request); +} + +/*! + * \brief Decode the CCNR_T_Request invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_CCNR_T_Request_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + return rose_dec_etsi_CC_T_Request_ARG_Backend(ctrl, "NR", tag, pos, end, + &args->etsi.CCNR_T_Request); +} + +/*! + * \internal + * \brief Decode the CCBS-T/CCNR-T-Request result argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param name Field name + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param ccbs_t_request Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_etsi_CC_T_Request_RES_Backend(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseEtsiCCBS_T_Request_RES *ccbs_t_request) +{ + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_BOOLEAN); + ASN1_CALL(pos, asn1_dec_boolean(ctrl, "retentionSupported", tag, pos, end, &value)); + ccbs_t_request->retention_supported = value; + + return pos; +} + +/*! + * \brief Decode the CCBS_T_Request result argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_CCBS_T_Request_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args) +{ + return rose_dec_etsi_CC_T_Request_RES_Backend(ctrl, tag, pos, end, + &args->etsi.CCBS_T_Request); +} + +/*! + * \brief Decode the CCNR_T_Request result argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_CCNR_T_Request_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args) +{ + return rose_dec_etsi_CC_T_Request_RES_Backend(ctrl, tag, pos, end, + &args->etsi.CCNR_T_Request); +} + +/* ------------------------------------------------------------------- */ +/* end rose_etsi_cc.c */ diff --git a/rose_internal.h b/rose_internal.h index 83ec353..ed8e369 100644 --- a/rose_internal.h +++ b/rose_internal.h @@ -238,6 +238,135 @@ const unsigned char *rose_dec_etsi_EctLoopTest_RES(struct pri *ctrl, unsigned ta const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args); +/* ETSI Status Request */ +unsigned char *rose_enc_etsi_StatusRequest_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_StatusRequest_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args); + +const unsigned char *rose_dec_etsi_StatusRequest_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_StatusRequest_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args); + +/* ETSI Call-Completion Busy Status (CCBS) / Call-Completion No Reply (CCNR) */ +unsigned char *rose_enc_etsi_CallInfoRetain_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_EraseCallLinkageID_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_CCBSDeactivate_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_CCBSErase_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_CCBSRemoteUserFree_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_CCBSCall_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_CCBSBFree_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_CCBSStopAlerting_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_CCBSStatusRequest_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_CCBSStatusRequest_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args); +unsigned char *rose_enc_etsi_CCBSRequest_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_CCNRRequest_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_CCBSRequest_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args); +unsigned char *rose_enc_etsi_CCNRRequest_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args); +unsigned char *rose_enc_etsi_CCBSInterrogate_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_CCNRInterrogate_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_CCBSInterrogate_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args); +unsigned char *rose_enc_etsi_CCNRInterrogate_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args); + +const unsigned char *rose_dec_etsi_CallInfoRetain_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_EraseCallLinkageID_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_CCBSDeactivate_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_CCBSErase_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_CCBSRemoteUserFree_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_CCBSCall_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_CCBSBFree_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_CCBSStopAlerting_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_CCBSStatusRequest_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_CCBSStatusRequest_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args); +const unsigned char *rose_dec_etsi_CCBSRequest_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_CCNRRequest_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_CCBSRequest_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args); +const unsigned char *rose_dec_etsi_CCNRRequest_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args); +const unsigned char *rose_dec_etsi_CCBSInterrogate_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_CCNRInterrogate_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_CCBSInterrogate_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args); +const unsigned char *rose_dec_etsi_CCNRInterrogate_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args); + +/* ETSI CCBS-T/CCNR-T */ +unsigned char *rose_enc_etsi_CCBS_T_Request_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_CCNR_T_Request_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_CCBS_T_Request_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args); +unsigned char *rose_enc_etsi_CCNR_T_Request_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args); + +const unsigned char *rose_dec_etsi_CCBS_T_Request_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_CCNR_T_Request_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_CCBS_T_Request_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args); +const unsigned char *rose_dec_etsi_CCNR_T_Request_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args); + /* Q.SIG Name-Operations */ unsigned char *rose_enc_qsig_Name(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct roseQsigName *name); @@ -416,6 +545,64 @@ const unsigned char *rose_dec_qsig_SubaddressTransfer_ARG(struct pri *ctrl, unsi const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args); +/* Q.SIG SS-CC-Operations */ +unsigned char *rose_enc_qsig_CcbsRequest_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_qsig_CcnrRequest_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_qsig_CcbsRequest_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args); +unsigned char *rose_enc_qsig_CcnrRequest_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args); +unsigned char *rose_enq_qsig_CcCancel_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enq_qsig_CcExecPossible_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_qsig_CcPathReserve_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_qsig_CcPathReserve_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args); +unsigned char *rose_enc_qsig_CcRingout_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_qsig_CcSuspend_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_qsig_CcResume_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); + +const unsigned char *rose_dec_qsig_CcbsRequest_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_CcnrRequest_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_CcbsRequest_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args); +const unsigned char *rose_dec_qsig_CcnrRequest_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args); +const unsigned char *rose_dec_qsig_CcCancel_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_CcExecPossible_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_CcPathReserve_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_CcPathReserve_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args); +const unsigned char *rose_dec_qsig_CcRingout_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_CcSuspend_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_CcResume_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); + /* Q.SIG SS-MWI-Operations */ unsigned char *rose_enc_qsig_MWIActivate_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); diff --git a/rose_qsig_cc.c b/rose_qsig_cc.c new file mode 100644 index 0000000..f77aa70 --- /dev/null +++ b/rose_qsig_cc.c @@ -0,0 +1,984 @@ +/* + * libpri: An implementation of Primary Rate ISDN + * + * Copyright (C) 2009 Digium, Inc. + * + * Richard Mudgett + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2 as published by the + * Free Software Foundation. See the LICENSE file included with + * this program for more details. + * + * In addition, when this program is distributed with Asterisk in + * any form that would qualify as a 'combined work' or as a + * 'derivative work' (but not mere aggregation), you can redistribute + * and/or modify the combination under the terms of the license + * provided with that copy of Asterisk, instead of the license + * terms granted here. + */ + +/*! + * \file + * \brief Q.SIG ROSE SS-CC-Operations (CC) + * + * SS-CC-Operations ECMA-186 Annex F Table F.1 + * + * \author Richard Mudgett + */ + + +#include "compat.h" +#include "libpri.h" +#include "pri_internal.h" +#include "rose.h" +#include "rose_internal.h" +#include "asn1.h" + + +/* ------------------------------------------------------------------- */ + +/*! + * \internal + * \brief Encode the CcExtension type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + * + * \details + * CcExtension ::= CHOICE { + * none NULL, + * single [14] IMPLICIT Extension, + * multiple [15] IMPLICIT SEQUENCE OF Extension + * } + */ +static unsigned char *rose_enc_qsig_CcExtension(struct pri *ctrl, unsigned char *pos, + unsigned char *end) +{ + return asn1_enc_null(pos, end, ASN1_TYPE_NULL); +} + +/*! + * \internal + * \brief Encode the CcRequestArg type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param cc_request_arg Call-completion request arguments to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_qsig_CcRequestArg(struct pri *ctrl, unsigned char *pos, + unsigned char *end, unsigned tag, const struct roseQsigCcRequestArg *cc_request_arg) +{ + unsigned char *seq_len; + unsigned char *exp_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + ASN1_CALL(pos, rose_enc_PresentedNumberUnscreened(ctrl, pos, end, + &cc_request_arg->number_a)); + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &cc_request_arg->number_b)); + ASN1_CALL(pos, rose_enc_Q931ie(ctrl, pos, end, ASN1_CLASS_APPLICATION | 0, + &cc_request_arg->q931ie)); + + if (cc_request_arg->subaddr_a.length) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(exp_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 10); + ASN1_CALL(pos, rose_enc_PartySubaddress(ctrl, pos, end, + &cc_request_arg->subaddr_a)); + ASN1_CONSTRUCTED_END(exp_len, pos, end); + } + + if (cc_request_arg->subaddr_b.length) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(exp_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 11); + ASN1_CALL(pos, rose_enc_PartySubaddress(ctrl, pos, end, + &cc_request_arg->subaddr_b)); + ASN1_CONSTRUCTED_END(exp_len, pos, end); + } + + if (cc_request_arg->can_retain_service) { + /* Not the DEFAULT value */ + ASN1_CALL(pos, asn1_enc_boolean(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 12, + cc_request_arg->can_retain_service)); + } + + if (cc_request_arg->retain_sig_connection_present) { + ASN1_CALL(pos, asn1_enc_boolean(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 13, + cc_request_arg->retain_sig_connection)); + } + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the Q.SIG CcbsRequest invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_qsig_CcbsRequest_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return rose_enc_qsig_CcRequestArg(ctrl, pos, end, ASN1_TAG_SEQUENCE, + &args->qsig.CcbsRequest); +} + +/*! + * \brief Encode the Q.SIG CcnrRequest invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_qsig_CcnrRequest_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return rose_enc_qsig_CcRequestArg(ctrl, pos, end, ASN1_TAG_SEQUENCE, + &args->qsig.CcnrRequest); +} + +/*! + * \internal + * \brief Encode the CcRequestRes type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param cc_request_res Call-completion request result to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_qsig_CcRequestRes(struct pri *ctrl, unsigned char *pos, + unsigned char *end, unsigned tag, const struct roseQsigCcRequestRes *cc_request_res) +{ + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + if (cc_request_res->no_path_reservation) { + /* Not the DEFAULT value */ + ASN1_CALL(pos, asn1_enc_boolean(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0, + cc_request_res->no_path_reservation)); + } + + if (cc_request_res->retain_service) { + /* Not the DEFAULT value */ + ASN1_CALL(pos, asn1_enc_boolean(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, + cc_request_res->retain_service)); + } + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the Q.SIG CcbsRequest result facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_qsig_CcbsRequest_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args) +{ + return rose_enc_qsig_CcRequestRes(ctrl, pos, end, ASN1_TAG_SEQUENCE, + &args->qsig.CcbsRequest); +} + +/*! + * \brief Encode the Q.SIG CcnrRequest result facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_qsig_CcnrRequest_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args) +{ + return rose_enc_qsig_CcRequestRes(ctrl, pos, end, ASN1_TAG_SEQUENCE, + &args->qsig.CcnrRequest); +} + +/*! + * \internal + * \brief Encode the CcOptionalArg type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param cc_optional_arg Call-completion optional arguments to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_qsig_CcOptionalArg(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct roseQsigCcOptionalArg *cc_optional_arg) +{ + unsigned char *seq_len; + unsigned char *exp_len; + + if (!cc_optional_arg->full_arg_present) { + return rose_enc_qsig_CcExtension(ctrl, pos, end); + } + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0); + + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &cc_optional_arg->number_a)); + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &cc_optional_arg->number_b)); + ASN1_CALL(pos, rose_enc_Q931ie(ctrl, pos, end, ASN1_CLASS_APPLICATION | 0, + &cc_optional_arg->q931ie)); + + if (cc_optional_arg->subaddr_a.length) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(exp_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 10); + ASN1_CALL(pos, rose_enc_PartySubaddress(ctrl, pos, end, + &cc_optional_arg->subaddr_a)); + ASN1_CONSTRUCTED_END(exp_len, pos, end); + } + + if (cc_optional_arg->subaddr_b.length) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(exp_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 11); + ASN1_CALL(pos, rose_enc_PartySubaddress(ctrl, pos, end, + &cc_optional_arg->subaddr_b)); + ASN1_CONSTRUCTED_END(exp_len, pos, end); + } + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the Q.SIG CcCancel invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enq_qsig_CcCancel_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return rose_enc_qsig_CcOptionalArg(ctrl, pos, end, &args->qsig.CcCancel); +} + +/*! + * \brief Encode the Q.SIG CcExecPossible invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enq_qsig_CcExecPossible_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return rose_enc_qsig_CcOptionalArg(ctrl, pos, end, &args->qsig.CcExecPossible); +} + +/*! + * \brief Encode the Q.SIG CcPathReserve invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_qsig_CcPathReserve_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return rose_enc_qsig_CcExtension(ctrl, pos, end); +} + +/*! + * \brief Encode the Q.SIG CcPathReserve result facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_qsig_CcPathReserve_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args) +{ + return rose_enc_qsig_CcExtension(ctrl, pos, end); +} + +/*! + * \brief Encode the Q.SIG CcRingout invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_qsig_CcRingout_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return rose_enc_qsig_CcExtension(ctrl, pos, end); +} + +/*! + * \brief Encode the Q.SIG CcSuspend invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_qsig_CcSuspend_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return rose_enc_qsig_CcExtension(ctrl, pos, end); +} + +/*! + * \brief Encode the Q.SIG CcResume invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_qsig_CcResume_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return rose_enc_qsig_CcExtension(ctrl, pos, end); +} + +/*! + * \internal + * \brief Decode the CcExtension argument parameters. + * + * \param ctrl D channel controller for any diagnostic messages. + * \param name Field name + * \param tag Component tag that identified this production. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + * + * \details + * CcExtension ::= CHOICE { + * none NULL, + * single [14] IMPLICIT Extension, + * multiple [15] IMPLICIT SEQUENCE OF Extension + * } + */ +static const unsigned char *rose_dec_qsig_CcExtension(struct pri *ctrl, const char *name, + unsigned tag, const unsigned char *pos, const unsigned char *end) +{ + int length; + int ext_offset; + const unsigned char *ext_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s CcExtension\n", name); + } + switch (tag & ~ASN1_PC_MASK) { + case ASN1_TYPE_NULL: + /* Must not be constructed but we will not check for it for simplicity. */ + return asn1_dec_null(ctrl, "none", tag, pos, end); + case ASN1_CLASS_CONTEXT_SPECIFIC | 14: + name = "single"; + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 15: + name = "multiple"; + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s %s\n", name, asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(ext_end, ext_offset, length, pos, end); + + /* Fixup will skip over the manufacturer extension information */ + ASN1_END_FIXUP(ctrl, pos, ext_offset, ext_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the CcRequestArg argument parameters. + * + * \param ctrl D channel controller for any diagnostic messages. + * \param name Field name + * \param tag Component tag that identified this production. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param cc_request_arg Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_qsig_CcRequestArg(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseQsigCcRequestArg *cc_request_arg) +{ + int32_t value; + int length; + int seq_offset; + int explicit_offset; + const unsigned char *explicit_end; + const unsigned char *seq_end; + const unsigned char *save_pos; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s CcRequestArg %s\n", name, asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_PresentedNumberUnscreened(ctrl, "numberA", tag, pos, seq_end, + &cc_request_arg->number_a)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "numberB", tag, pos, seq_end, + &cc_request_arg->number_b)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag & ~ASN1_PC_MASK, ASN1_CLASS_APPLICATION | 0); + ASN1_CALL(pos, rose_dec_Q931ie(ctrl, "service", tag, pos, seq_end, + &cc_request_arg->q931ie, sizeof(cc_request_arg->q931ie_contents))); + + /* + * A sequence specifies an ordered list of component types. + * However, for simplicity we are not checking the order of + * the remaining optional components. + */ + cc_request_arg->subaddr_a.length = 0; + cc_request_arg->subaddr_b.length = 0; + cc_request_arg->can_retain_service = 0; /* DEFAULT FALSE */ + cc_request_arg->retain_sig_connection_present = 0; + while (pos < seq_end && *pos != ASN1_INDEF_TERM) { + save_pos = pos; + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + switch (tag) { + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 10: + /* Remove EXPLICIT tag */ + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " Explicit %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, seq_end, &length)); + ASN1_END_SETUP(explicit_end, explicit_offset, length, pos, seq_end); + + ASN1_CALL(pos, asn1_dec_tag(pos, explicit_end, &tag)); + ASN1_CALL(pos, rose_dec_PartySubaddress(ctrl, "subaddrA", tag, pos, + explicit_end, &cc_request_arg->subaddr_a)); + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 11: + /* Remove EXPLICIT tag */ + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " Explicit %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, seq_end, &length)); + ASN1_END_SETUP(explicit_end, explicit_offset, length, pos, seq_end); + + ASN1_CALL(pos, asn1_dec_tag(pos, explicit_end, &tag)); + ASN1_CALL(pos, rose_dec_PartySubaddress(ctrl, "subaddrB", tag, pos, + explicit_end, &cc_request_arg->subaddr_b)); + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 12: + ASN1_CALL(pos, asn1_dec_boolean(ctrl, "can-retain-service", tag, pos, + seq_end, &value)); + cc_request_arg->can_retain_service = value; + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 13: + ASN1_CALL(pos, asn1_dec_boolean(ctrl, "retain-sig-connection", tag, pos, + seq_end, &value)); + cc_request_arg->retain_sig_connection = value; + cc_request_arg->retain_sig_connection_present = 1; + break; + case ASN1_TYPE_NULL: + case ASN1_CLASS_CONTEXT_SPECIFIC | 14: + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 14: + case ASN1_CLASS_CONTEXT_SPECIFIC | 15: + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 15: + ASN1_CALL(pos, rose_dec_qsig_CcExtension(ctrl, "extension", tag, pos, + seq_end)); + break; + default: + pos = save_pos; + goto cancel_options; + } + } +cancel_options:; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the Q.SIG CcbsRequest invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_qsig_CcbsRequest_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + return rose_dec_qsig_CcRequestArg(ctrl, "CcbsRequest", tag, pos, end, + &args->qsig.CcbsRequest); +} + +/*! + * \brief Decode the Q.SIG CcnrRequest invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_qsig_CcnrRequest_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + return rose_dec_qsig_CcRequestArg(ctrl, "CcnrRequest", tag, pos, end, + &args->qsig.CcnrRequest); +} + +/*! + * \internal + * \brief Decode the CcRequestRes argument parameters. + * + * \param ctrl D channel controller for any diagnostic messages. + * \param name Field name + * \param tag Component tag that identified this production. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param cc_request_res Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_qsig_CcRequestRes(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseQsigCcRequestRes *cc_request_res) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + const unsigned char *save_pos; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s CcRequestRes %s\n", name, asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + /* + * A sequence specifies an ordered list of component types. + * However, for simplicity we are not checking the order of + * the remaining optional components. + */ + cc_request_res->no_path_reservation = 0; /* DEFAULT FALSE */ + cc_request_res->retain_service = 0; /* DEFAULT FALSE */ + while (pos < seq_end && *pos != ASN1_INDEF_TERM) { + save_pos = pos; + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + switch (tag) { + case ASN1_CLASS_CONTEXT_SPECIFIC | 0: + ASN1_CALL(pos, asn1_dec_boolean(ctrl, "no-path-reservation", tag, pos, + seq_end, &value)); + cc_request_res->no_path_reservation = value; + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 1: + ASN1_CALL(pos, asn1_dec_boolean(ctrl, "retain-service", tag, pos, seq_end, + &value)); + cc_request_res->retain_service = value; + break; + case ASN1_TYPE_NULL: + case ASN1_CLASS_CONTEXT_SPECIFIC | 14: + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 14: + case ASN1_CLASS_CONTEXT_SPECIFIC | 15: + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 15: + ASN1_CALL(pos, rose_dec_qsig_CcExtension(ctrl, "extension", tag, pos, + seq_end)); + break; + default: + pos = save_pos; + goto cancel_options; + } + } +cancel_options:; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the Q.SIG CcbsRequest result argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_qsig_CcbsRequest_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args) +{ + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + return rose_dec_qsig_CcRequestRes(ctrl, "CcbsRequest", tag, pos, end, + &args->qsig.CcbsRequest); +} + +/*! + * \brief Decode the Q.SIG CcnrRequest result argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_qsig_CcnrRequest_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args) +{ + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + return rose_dec_qsig_CcRequestRes(ctrl, "CcnrRequest", tag, pos, end, + &args->qsig.CcnrRequest); +} + +/*! + * \internal + * \brief Decode the CcOptionalArg argument parameters. + * + * \param ctrl D channel controller for any diagnostic messages. + * \param name Field name + * \param tag Component tag that identified this production. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param cc_optional_arg Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_qsig_CcOptionalArg(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseQsigCcOptionalArg *cc_optional_arg) +{ + int length; + int seq_offset; + int explicit_offset; + const unsigned char *explicit_end; + const unsigned char *seq_end; + const unsigned char *save_pos; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s CcOptionalArg\n", name); + } + if (tag != (ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 0)) { + cc_optional_arg->full_arg_present = 0; + return rose_dec_qsig_CcExtension(ctrl, "extArg", tag, pos, end); + } + cc_optional_arg->full_arg_present = 1; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " fullArg %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "numberA", tag, pos, seq_end, + &cc_optional_arg->number_a)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "numberB", tag, pos, seq_end, + &cc_optional_arg->number_b)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag & ~ASN1_PC_MASK, ASN1_CLASS_APPLICATION | 0); + ASN1_CALL(pos, rose_dec_Q931ie(ctrl, "service", tag, pos, seq_end, + &cc_optional_arg->q931ie, sizeof(cc_optional_arg->q931ie_contents))); + + /* + * A sequence specifies an ordered list of component types. + * However, for simplicity we are not checking the order of + * the remaining optional components. + */ + cc_optional_arg->subaddr_a.length = 0; + cc_optional_arg->subaddr_b.length = 0; + while (pos < seq_end && *pos != ASN1_INDEF_TERM) { + save_pos = pos; + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + switch (tag) { + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 10: + /* Remove EXPLICIT tag */ + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " Explicit %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, seq_end, &length)); + ASN1_END_SETUP(explicit_end, explicit_offset, length, pos, seq_end); + + ASN1_CALL(pos, asn1_dec_tag(pos, explicit_end, &tag)); + ASN1_CALL(pos, rose_dec_PartySubaddress(ctrl, "subaddrA", tag, pos, + explicit_end, &cc_optional_arg->subaddr_a)); + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 11: + /* Remove EXPLICIT tag */ + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " Explicit %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, seq_end, &length)); + ASN1_END_SETUP(explicit_end, explicit_offset, length, pos, seq_end); + + ASN1_CALL(pos, asn1_dec_tag(pos, explicit_end, &tag)); + ASN1_CALL(pos, rose_dec_PartySubaddress(ctrl, "subaddrB", tag, pos, + explicit_end, &cc_optional_arg->subaddr_b)); + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + case ASN1_TYPE_NULL: + case ASN1_CLASS_CONTEXT_SPECIFIC | 14: + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 14: + case ASN1_CLASS_CONTEXT_SPECIFIC | 15: + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 15: + ASN1_CALL(pos, rose_dec_qsig_CcExtension(ctrl, "extension", tag, pos, + seq_end)); + break; + default: + pos = save_pos; + goto cancel_options; + } + } +cancel_options:; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the Q.SIG CcCancel invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_qsig_CcCancel_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + return rose_dec_qsig_CcOptionalArg(ctrl, "CcCancel", tag, pos, end, + &args->qsig.CcCancel); +} + +/*! + * \brief Decode the Q.SIG CcExecPossible invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_qsig_CcExecPossible_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + return rose_dec_qsig_CcOptionalArg(ctrl, "CcExecPossible", tag, pos, end, + &args->qsig.CcCancel); +} + +/*! + * \brief Decode the Q.SIG CcPathReserve invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_qsig_CcPathReserve_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + return rose_dec_qsig_CcExtension(ctrl, "CcPathReserve", tag, pos, end); +} + +/*! + * \brief Decode the Q.SIG CcPathReserve result argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_qsig_CcPathReserve_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args) +{ + return rose_dec_qsig_CcExtension(ctrl, "CcPathReserve", tag, pos, end); +} + +/*! + * \brief Decode the Q.SIG CcRingout invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_qsig_CcRingout_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + return rose_dec_qsig_CcExtension(ctrl, "CcRingout", tag, pos, end); +} + +/*! + * \brief Decode the Q.SIG CcSuspend invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_qsig_CcSuspend_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + return rose_dec_qsig_CcExtension(ctrl, "CcSuspend", tag, pos, end); +} + +/*! + * \brief Decode the Q.SIG CcResume invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_qsig_CcResume_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + return rose_dec_qsig_CcExtension(ctrl, "CcResume", tag, pos, end); +} + +/* ------------------------------------------------------------------- */ +/* end rose_qsig_cc.c */ diff --git a/rosetest.c b/rosetest.c index fa8d065..b8f8acc 100644 --- a/rosetest.c +++ b/rosetest.c @@ -951,6 +951,444 @@ static const struct rose_message rose_etsi_msgs[] = { .component.result.invoke_id = 66, .component.result.args.etsi.EctLoopTest.loop_result = 2, }, + + /* Status Request */ + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_StatusRequest, + .component.invoke.invoke_id = 13, + .component.invoke.args.etsi.StatusRequest.q931ie.length = 5, + .component.invoke.args.etsi.StatusRequest.q931ie_contents = "CDEZY", + .component.invoke.args.etsi.StatusRequest.compatibility_mode = 1, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_StatusRequest, + .component.result.invoke_id = 14, + .component.result.args.etsi.StatusRequest.status = 2, + }, + + /* CCBS support */ + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CallInfoRetain, + .component.invoke.invoke_id = 15, + .component.invoke.args.etsi.CallInfoRetain.call_linkage_id = 115, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_EraseCallLinkageID, + .component.invoke.invoke_id = 16, + .component.invoke.args.etsi.EraseCallLinkageID.call_linkage_id = 105, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCBSDeactivate, + .component.invoke.invoke_id = 17, + .component.invoke.args.etsi.CCBSDeactivate.ccbs_reference = 2, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_CCBSDeactivate, + .component.result.invoke_id = 18, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCBSErase, + .component.invoke.invoke_id = 19, + .component.invoke.args.etsi.CCBSErase.q931ie.length = 2, + .component.invoke.args.etsi.CCBSErase.q931ie_contents = "JK", + .component.invoke.args.etsi.CCBSErase.address_of_b.number.plan = 0, + .component.invoke.args.etsi.CCBSErase.address_of_b.number.length = 5, + .component.invoke.args.etsi.CCBSErase.address_of_b.number.str = "33403", + .component.invoke.args.etsi.CCBSErase.address_of_b.subaddress.type = 0, + .component.invoke.args.etsi.CCBSErase.address_of_b.subaddress.length = 4, + .component.invoke.args.etsi.CCBSErase.address_of_b.subaddress.u.user_specified.information = "3748", + .component.invoke.args.etsi.CCBSErase.recall_mode = 1, + .component.invoke.args.etsi.CCBSErase.ccbs_reference = 102, + .component.invoke.args.etsi.CCBSErase.reason = 3, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCBSErase, + .component.invoke.invoke_id = 20, + .component.invoke.args.etsi.CCBSErase.q931ie.length = 2, + .component.invoke.args.etsi.CCBSErase.q931ie_contents = "JK", + .component.invoke.args.etsi.CCBSErase.address_of_b.number.plan = 1, + .component.invoke.args.etsi.CCBSErase.address_of_b.number.length = 11, + .component.invoke.args.etsi.CCBSErase.address_of_b.number.ton = 1, + .component.invoke.args.etsi.CCBSErase.address_of_b.number.str = "18003020102", + .component.invoke.args.etsi.CCBSErase.address_of_b.subaddress.type = 0, + .component.invoke.args.etsi.CCBSErase.address_of_b.subaddress.length = 4, + .component.invoke.args.etsi.CCBSErase.address_of_b.subaddress.u.user_specified.odd_count_present = 1, + .component.invoke.args.etsi.CCBSErase.address_of_b.subaddress.u.user_specified.odd_count = 1, + .component.invoke.args.etsi.CCBSErase.address_of_b.subaddress.u.user_specified.information = "3748", + .component.invoke.args.etsi.CCBSErase.recall_mode = 1, + .component.invoke.args.etsi.CCBSErase.ccbs_reference = 102, + .component.invoke.args.etsi.CCBSErase.reason = 3, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCBSErase, + .component.invoke.invoke_id = 21, + .component.invoke.args.etsi.CCBSErase.q931ie.length = 2, + .component.invoke.args.etsi.CCBSErase.q931ie_contents = "JK", + .component.invoke.args.etsi.CCBSErase.address_of_b.number.plan = 2, + .component.invoke.args.etsi.CCBSErase.address_of_b.number.length = 4, + .component.invoke.args.etsi.CCBSErase.address_of_b.number.str = "1803", + .component.invoke.args.etsi.CCBSErase.address_of_b.subaddress.type = 1, + .component.invoke.args.etsi.CCBSErase.address_of_b.subaddress.length = 4, + .component.invoke.args.etsi.CCBSErase.address_of_b.subaddress.u.nsap = "6492", + .component.invoke.args.etsi.CCBSErase.recall_mode = 1, + .component.invoke.args.etsi.CCBSErase.ccbs_reference = 102, + .component.invoke.args.etsi.CCBSErase.reason = 3, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCBSErase, + .component.invoke.invoke_id = 22, + .component.invoke.args.etsi.CCBSErase.q931ie.length = 2, + .component.invoke.args.etsi.CCBSErase.q931ie_contents = "JK", + .component.invoke.args.etsi.CCBSErase.address_of_b.number.plan = 3, + .component.invoke.args.etsi.CCBSErase.address_of_b.number.length = 4, + .component.invoke.args.etsi.CCBSErase.address_of_b.number.str = "1803", + .component.invoke.args.etsi.CCBSErase.recall_mode = 1, + .component.invoke.args.etsi.CCBSErase.ccbs_reference = 102, + .component.invoke.args.etsi.CCBSErase.reason = 3, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCBSErase, + .component.invoke.invoke_id = 23, + .component.invoke.args.etsi.CCBSErase.q931ie.length = 2, + .component.invoke.args.etsi.CCBSErase.q931ie_contents = "JK", + .component.invoke.args.etsi.CCBSErase.address_of_b.number.plan = 4, + .component.invoke.args.etsi.CCBSErase.address_of_b.number.length = 4, + .component.invoke.args.etsi.CCBSErase.address_of_b.number.str = "1803", + .component.invoke.args.etsi.CCBSErase.recall_mode = 1, + .component.invoke.args.etsi.CCBSErase.ccbs_reference = 102, + .component.invoke.args.etsi.CCBSErase.reason = 3, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCBSErase, + .component.invoke.invoke_id = 24, + .component.invoke.args.etsi.CCBSErase.q931ie.length = 2, + .component.invoke.args.etsi.CCBSErase.q931ie_contents = "JK", + .component.invoke.args.etsi.CCBSErase.address_of_b.number.plan = 5, + .component.invoke.args.etsi.CCBSErase.address_of_b.number.length = 11, + .component.invoke.args.etsi.CCBSErase.address_of_b.number.ton = 4, + .component.invoke.args.etsi.CCBSErase.address_of_b.number.str = "18003020102", + .component.invoke.args.etsi.CCBSErase.recall_mode = 1, + .component.invoke.args.etsi.CCBSErase.ccbs_reference = 102, + .component.invoke.args.etsi.CCBSErase.reason = 3, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCBSErase, + .component.invoke.invoke_id = 25, + .component.invoke.args.etsi.CCBSErase.q931ie.length = 2, + .component.invoke.args.etsi.CCBSErase.q931ie_contents = "JK", + .component.invoke.args.etsi.CCBSErase.address_of_b.number.plan = 8, + .component.invoke.args.etsi.CCBSErase.address_of_b.number.length = 4, + .component.invoke.args.etsi.CCBSErase.address_of_b.number.str = "1803", + .component.invoke.args.etsi.CCBSErase.recall_mode = 1, + .component.invoke.args.etsi.CCBSErase.ccbs_reference = 102, + .component.invoke.args.etsi.CCBSErase.reason = 3, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCBSRemoteUserFree, + .component.invoke.invoke_id = 26, + .component.invoke.args.etsi.CCBSRemoteUserFree.q931ie.length = 2, + .component.invoke.args.etsi.CCBSRemoteUserFree.q931ie_contents = "JK", + .component.invoke.args.etsi.CCBSRemoteUserFree.address_of_b.number.plan = 8, + .component.invoke.args.etsi.CCBSRemoteUserFree.address_of_b.number.length = 4, + .component.invoke.args.etsi.CCBSRemoteUserFree.address_of_b.number.str = "1803", + .component.invoke.args.etsi.CCBSRemoteUserFree.recall_mode = 1, + .component.invoke.args.etsi.CCBSRemoteUserFree.ccbs_reference = 102, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCBSCall, + .component.invoke.invoke_id = 27, + .component.invoke.args.etsi.CCBSCall.ccbs_reference = 115, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCBSStatusRequest, + .component.invoke.invoke_id = 28, + .component.invoke.args.etsi.CCBSStatusRequest.q931ie.length = 2, + .component.invoke.args.etsi.CCBSStatusRequest.q931ie_contents = "JK", + .component.invoke.args.etsi.CCBSStatusRequest.recall_mode = 1, + .component.invoke.args.etsi.CCBSStatusRequest.ccbs_reference = 102, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_CCBSStatusRequest, + .component.result.invoke_id = 29, + .component.result.args.etsi.CCBSStatusRequest.free = 1, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCBSBFree, + .component.invoke.invoke_id = 30, + .component.invoke.args.etsi.CCBSBFree.q931ie.length = 2, + .component.invoke.args.etsi.CCBSBFree.q931ie_contents = "JK", + .component.invoke.args.etsi.CCBSBFree.address_of_b.number.plan = 8, + .component.invoke.args.etsi.CCBSBFree.address_of_b.number.length = 4, + .component.invoke.args.etsi.CCBSBFree.address_of_b.number.str = "1803", + .component.invoke.args.etsi.CCBSBFree.recall_mode = 1, + .component.invoke.args.etsi.CCBSBFree.ccbs_reference = 14, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCBSStopAlerting, + .component.invoke.invoke_id = 31, + .component.invoke.args.etsi.CCBSStopAlerting.ccbs_reference = 37, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCBSRequest, + .component.invoke.invoke_id = 32, + .component.invoke.args.etsi.CCBSRequest.call_linkage_id = 57, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_CCBSRequest, + .component.result.invoke_id = 33, + .component.result.args.etsi.CCBSRequest.recall_mode = 1, + .component.result.args.etsi.CCBSRequest.ccbs_reference = 102, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCBSInterrogate, + .component.invoke.invoke_id = 34, + .component.invoke.args.etsi.CCBSInterrogate.a_party_number.plan = 8, + .component.invoke.args.etsi.CCBSInterrogate.a_party_number.length = 4, + .component.invoke.args.etsi.CCBSInterrogate.a_party_number.str = "1803", + .component.invoke.args.etsi.CCBSInterrogate.ccbs_reference_present = 1, + .component.invoke.args.etsi.CCBSInterrogate.ccbs_reference = 76, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCBSInterrogate, + .component.invoke.invoke_id = 35, + .component.invoke.args.etsi.CCBSInterrogate.a_party_number.plan = 8, + .component.invoke.args.etsi.CCBSInterrogate.a_party_number.length = 4, + .component.invoke.args.etsi.CCBSInterrogate.a_party_number.str = "1803", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCBSInterrogate, + .component.invoke.invoke_id = 36, + .component.invoke.args.etsi.CCBSInterrogate.ccbs_reference_present = 1, + .component.invoke.args.etsi.CCBSInterrogate.ccbs_reference = 76, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCBSInterrogate, + .component.invoke.invoke_id = 37, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_CCBSInterrogate, + .component.result.invoke_id = 38, + .component.result.args.etsi.CCBSInterrogate.recall_mode = 1, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_CCBSInterrogate, + .component.result.invoke_id = 39, + .component.result.args.etsi.CCBSInterrogate.recall_mode = 1, + .component.result.args.etsi.CCBSInterrogate.call_details.num_records = 1, + .component.result.args.etsi.CCBSInterrogate.call_details.list[0].ccbs_reference = 12, + .component.result.args.etsi.CCBSInterrogate.call_details.list[0].q931ie.length = 2, + .component.result.args.etsi.CCBSInterrogate.call_details.list[0].q931ie_contents = "JK", + .component.result.args.etsi.CCBSInterrogate.call_details.list[0].address_of_b.number.plan = 8, + .component.result.args.etsi.CCBSInterrogate.call_details.list[0].address_of_b.number.length = 4, + .component.result.args.etsi.CCBSInterrogate.call_details.list[0].address_of_b.number.str = "1803", + .component.result.args.etsi.CCBSInterrogate.call_details.list[0].subaddress_of_a.type = 1, + .component.result.args.etsi.CCBSInterrogate.call_details.list[0].subaddress_of_a.length = 4, + .component.result.args.etsi.CCBSInterrogate.call_details.list[0].subaddress_of_a.u.nsap = "6492", + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_CCBSInterrogate, + .component.result.invoke_id = 40, + .component.result.args.etsi.CCBSInterrogate.recall_mode = 1, + .component.result.args.etsi.CCBSInterrogate.call_details.num_records = 2, + .component.result.args.etsi.CCBSInterrogate.call_details.list[0].ccbs_reference = 12, + .component.result.args.etsi.CCBSInterrogate.call_details.list[0].q931ie.length = 2, + .component.result.args.etsi.CCBSInterrogate.call_details.list[0].q931ie_contents = "JK", + .component.result.args.etsi.CCBSInterrogate.call_details.list[0].address_of_b.number.plan = 8, + .component.result.args.etsi.CCBSInterrogate.call_details.list[0].address_of_b.number.length = 4, + .component.result.args.etsi.CCBSInterrogate.call_details.list[0].address_of_b.number.str = "1803", + .component.result.args.etsi.CCBSInterrogate.call_details.list[1].ccbs_reference = 102, + .component.result.args.etsi.CCBSInterrogate.call_details.list[1].q931ie.length = 2, + .component.result.args.etsi.CCBSInterrogate.call_details.list[1].q931ie_contents = "LM", + .component.result.args.etsi.CCBSInterrogate.call_details.list[1].address_of_b.number.plan = 8, + .component.result.args.etsi.CCBSInterrogate.call_details.list[1].address_of_b.number.length = 4, + .component.result.args.etsi.CCBSInterrogate.call_details.list[1].address_of_b.number.str = "6229", + .component.result.args.etsi.CCBSInterrogate.call_details.list[1].address_of_b.subaddress.type = 1, + .component.result.args.etsi.CCBSInterrogate.call_details.list[1].address_of_b.subaddress.length = 4, + .component.result.args.etsi.CCBSInterrogate.call_details.list[1].address_of_b.subaddress.u.nsap = "8592", + .component.result.args.etsi.CCBSInterrogate.call_details.list[1].subaddress_of_a.type = 1, + .component.result.args.etsi.CCBSInterrogate.call_details.list[1].subaddress_of_a.length = 4, + .component.result.args.etsi.CCBSInterrogate.call_details.list[1].subaddress_of_a.u.nsap = "6492", + }, + + /* CCNR support */ + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCNRRequest, + .component.invoke.invoke_id = 512, + .component.invoke.args.etsi.CCNRRequest.call_linkage_id = 57, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_CCNRRequest, + .component.result.invoke_id = 150, + .component.result.args.etsi.CCNRRequest.recall_mode = 1, + .component.result.args.etsi.CCNRRequest.ccbs_reference = 102, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCNRInterrogate, + .component.invoke.invoke_id = -129, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_CCNRInterrogate, + .component.result.invoke_id = -3, + .component.result.args.etsi.CCNRInterrogate.recall_mode = 1, + }, + + /* CCBS-T */ + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCBS_T_Call, + .component.invoke.invoke_id = 41, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCBS_T_Suspend, + .component.invoke.invoke_id = 42, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCBS_T_Resume, + .component.invoke.invoke_id = 43, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCBS_T_RemoteUserFree, + .component.invoke.invoke_id = 44, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCBS_T_Available, + .component.invoke.invoke_id = 45, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCBS_T_Request, + .component.invoke.invoke_id = 46, + .component.invoke.args.etsi.CCBS_T_Request.destination.number.plan = 8, + .component.invoke.args.etsi.CCBS_T_Request.destination.number.length = 4, + .component.invoke.args.etsi.CCBS_T_Request.destination.number.str = "6229", + .component.invoke.args.etsi.CCBS_T_Request.q931ie.length = 2, + .component.invoke.args.etsi.CCBS_T_Request.q931ie_contents = "LM", + .component.invoke.args.etsi.CCBS_T_Request.retention_supported = 1, + .component.invoke.args.etsi.CCBS_T_Request.presentation_allowed_indicator_present = 1, + .component.invoke.args.etsi.CCBS_T_Request.presentation_allowed_indicator = 1, + .component.invoke.args.etsi.CCBS_T_Request.originating.number.plan = 8, + .component.invoke.args.etsi.CCBS_T_Request.originating.number.length = 4, + .component.invoke.args.etsi.CCBS_T_Request.originating.number.str = "9864", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCBS_T_Request, + .component.invoke.invoke_id = 47, + .component.invoke.args.etsi.CCBS_T_Request.destination.number.plan = 8, + .component.invoke.args.etsi.CCBS_T_Request.destination.number.length = 4, + .component.invoke.args.etsi.CCBS_T_Request.destination.number.str = "6229", + .component.invoke.args.etsi.CCBS_T_Request.q931ie.length = 2, + .component.invoke.args.etsi.CCBS_T_Request.q931ie_contents = "LM", + .component.invoke.args.etsi.CCBS_T_Request.presentation_allowed_indicator_present = 1, + .component.invoke.args.etsi.CCBS_T_Request.presentation_allowed_indicator = 1, + .component.invoke.args.etsi.CCBS_T_Request.originating.number.plan = 8, + .component.invoke.args.etsi.CCBS_T_Request.originating.number.length = 4, + .component.invoke.args.etsi.CCBS_T_Request.originating.number.str = "9864", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCBS_T_Request, + .component.invoke.invoke_id = 48, + .component.invoke.args.etsi.CCBS_T_Request.destination.number.plan = 8, + .component.invoke.args.etsi.CCBS_T_Request.destination.number.length = 4, + .component.invoke.args.etsi.CCBS_T_Request.destination.number.str = "6229", + .component.invoke.args.etsi.CCBS_T_Request.q931ie.length = 2, + .component.invoke.args.etsi.CCBS_T_Request.q931ie_contents = "LM", + .component.invoke.args.etsi.CCBS_T_Request.originating.number.plan = 8, + .component.invoke.args.etsi.CCBS_T_Request.originating.number.length = 4, + .component.invoke.args.etsi.CCBS_T_Request.originating.number.str = "9864", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCBS_T_Request, + .component.invoke.invoke_id = 49, + .component.invoke.args.etsi.CCBS_T_Request.destination.number.plan = 8, + .component.invoke.args.etsi.CCBS_T_Request.destination.number.length = 4, + .component.invoke.args.etsi.CCBS_T_Request.destination.number.str = "6229", + .component.invoke.args.etsi.CCBS_T_Request.q931ie.length = 2, + .component.invoke.args.etsi.CCBS_T_Request.q931ie_contents = "LM", + .component.invoke.args.etsi.CCBS_T_Request.presentation_allowed_indicator_present = 1, + .component.invoke.args.etsi.CCBS_T_Request.presentation_allowed_indicator = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCBS_T_Request, + .component.invoke.invoke_id = 50, + .component.invoke.args.etsi.CCBS_T_Request.destination.number.plan = 8, + .component.invoke.args.etsi.CCBS_T_Request.destination.number.length = 4, + .component.invoke.args.etsi.CCBS_T_Request.destination.number.str = "6229", + .component.invoke.args.etsi.CCBS_T_Request.q931ie.length = 2, + .component.invoke.args.etsi.CCBS_T_Request.q931ie_contents = "LM", + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_CCBS_T_Request, + .component.result.invoke_id = 51, + .component.result.args.etsi.CCBS_T_Request.retention_supported = 1, + }, + + /* CCNR-T */ + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CCNR_T_Request, + .component.invoke.invoke_id = 52, + .component.invoke.args.etsi.CCNR_T_Request.destination.number.plan = 8, + .component.invoke.args.etsi.CCNR_T_Request.destination.number.length = 4, + .component.invoke.args.etsi.CCNR_T_Request.destination.number.str = "6229", + .component.invoke.args.etsi.CCNR_T_Request.q931ie.length = 2, + .component.invoke.args.etsi.CCNR_T_Request.q931ie_contents = "LM", + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_CCNR_T_Request, + .component.result.invoke_id = 53, + .component.result.args.etsi.CCNR_T_Request.retention_supported = 1, + }, /* *INDENT-ON* */ }; @@ -1975,6 +2413,143 @@ static const struct rose_message rose_qsig_msgs[] = { .component.invoke.invoke_id = 86, }, + /* Q.SIG SS-CC-Operations */ + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CcbsRequest, + .component.invoke.invoke_id = 87, + .component.invoke.args.qsig.CcbsRequest.number_a.presentation = 1, + .component.invoke.args.qsig.CcbsRequest.number_b.plan = 4, + .component.invoke.args.qsig.CcbsRequest.number_b.length = 4, + .component.invoke.args.qsig.CcbsRequest.number_b.str = "8347", + .component.invoke.args.qsig.CcbsRequest.q931ie.length = 2, + .component.invoke.args.qsig.CcbsRequest.q931ie_contents = "AB", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CcbsRequest, + .component.invoke.invoke_id = 88, + .component.invoke.args.qsig.CcbsRequest.number_a.presentation = 1, + .component.invoke.args.qsig.CcbsRequest.number_b.plan = 4, + .component.invoke.args.qsig.CcbsRequest.number_b.length = 4, + .component.invoke.args.qsig.CcbsRequest.number_b.str = "8347", + .component.invoke.args.qsig.CcbsRequest.q931ie.length = 2, + .component.invoke.args.qsig.CcbsRequest.q931ie_contents = "AB", + .component.invoke.args.qsig.CcbsRequest.subaddr_a.type = 1, + .component.invoke.args.qsig.CcbsRequest.subaddr_a.length = 4, + .component.invoke.args.qsig.CcbsRequest.subaddr_a.u.nsap = "8765", + .component.invoke.args.qsig.CcbsRequest.subaddr_b.type = 1, + .component.invoke.args.qsig.CcbsRequest.subaddr_b.length = 4, + .component.invoke.args.qsig.CcbsRequest.subaddr_b.u.nsap = "8765", + .component.invoke.args.qsig.CcbsRequest.can_retain_service = 1, + .component.invoke.args.qsig.CcbsRequest.retain_sig_connection_present = 1, + .component.invoke.args.qsig.CcbsRequest.retain_sig_connection = 1, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_QSIG_CcbsRequest, + .component.result.invoke_id = 89, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_QSIG_CcbsRequest, + .component.result.invoke_id = 90, + .component.result.args.qsig.CcbsRequest.no_path_reservation = 1, + .component.result.args.qsig.CcbsRequest.retain_service = 1, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CcnrRequest, + .component.invoke.invoke_id = 91, + .component.invoke.args.qsig.CcnrRequest.number_a.presentation = 1, + .component.invoke.args.qsig.CcnrRequest.number_b.plan = 4, + .component.invoke.args.qsig.CcnrRequest.number_b.length = 4, + .component.invoke.args.qsig.CcnrRequest.number_b.str = "8347", + .component.invoke.args.qsig.CcnrRequest.q931ie.length = 2, + .component.invoke.args.qsig.CcnrRequest.q931ie_contents = "AB", + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_QSIG_CcnrRequest, + .component.result.invoke_id = 92, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CcCancel, + .component.invoke.invoke_id = 93, + .component.invoke.args.qsig.CcCancel.full_arg_present = 0, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CcCancel, + .component.invoke.invoke_id = 94, + .component.invoke.args.qsig.CcCancel.full_arg_present = 1, + .component.invoke.args.qsig.CcCancel.number_a.plan = 4, + .component.invoke.args.qsig.CcCancel.number_a.length = 4, + .component.invoke.args.qsig.CcCancel.number_a.str = "8347", + .component.invoke.args.qsig.CcCancel.number_b.plan = 4, + .component.invoke.args.qsig.CcCancel.number_b.length = 4, + .component.invoke.args.qsig.CcCancel.number_b.str = "8347", + .component.invoke.args.qsig.CcCancel.q931ie.length = 2, + .component.invoke.args.qsig.CcCancel.q931ie_contents = "AB", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CcCancel, + .component.invoke.invoke_id = 95, + .component.invoke.args.qsig.CcCancel.full_arg_present = 1, + .component.invoke.args.qsig.CcCancel.number_a.plan = 4, + .component.invoke.args.qsig.CcCancel.number_a.length = 4, + .component.invoke.args.qsig.CcCancel.number_a.str = "8347", + .component.invoke.args.qsig.CcCancel.number_b.plan = 4, + .component.invoke.args.qsig.CcCancel.number_b.length = 4, + .component.invoke.args.qsig.CcCancel.number_b.str = "8347", + .component.invoke.args.qsig.CcCancel.q931ie.length = 2, + .component.invoke.args.qsig.CcCancel.q931ie_contents = "AB", + .component.invoke.args.qsig.CcCancel.subaddr_a.type = 1, + .component.invoke.args.qsig.CcCancel.subaddr_a.length = 4, + .component.invoke.args.qsig.CcCancel.subaddr_a.u.nsap = "8765", + .component.invoke.args.qsig.CcCancel.subaddr_b.type = 1, + .component.invoke.args.qsig.CcCancel.subaddr_b.length = 4, + .component.invoke.args.qsig.CcCancel.subaddr_b.u.nsap = "8765", + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CcExecPossible, + .component.invoke.invoke_id = 96, + .component.invoke.args.qsig.CcExecPossible.full_arg_present = 0, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CcPathReserve, + .component.invoke.invoke_id = 97, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_QSIG_CcPathReserve, + .component.result.invoke_id = 98, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CcRingout, + .component.invoke.invoke_id = 99, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CcSuspend, + .component.invoke.invoke_id = 100, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CcResume, + .component.invoke.invoke_id = 101, + }, + /* Q.SIG SS-MWI-Operations */ { .type = ROSE_COMP_TYPE_INVOKE, @@ -2455,6 +3030,8 @@ int main(int argc, char *argv[]) (unsigned) sizeof(struct roseEtsiForwardingList)); pri_message(&dummy_ctrl, "sizeof(struct roseEtsiServedUserNumberList) = %u\n", (unsigned) sizeof(struct roseEtsiServedUserNumberList)); + pri_message(&dummy_ctrl, "sizeof(struct roseEtsiCallDetailsList) = %u\n", + (unsigned) sizeof(struct roseEtsiCallDetailsList)); pri_message(&dummy_ctrl, "\n"); pri_message(&dummy_ctrl, "sizeof(struct roseEtsiCallRerouting_ARG) = %u\n",