libpri-1.4.14/0000755000000000000000000000000012064641001011552 5ustar rootrootlibpri-1.4.14/doc/0000755000000000000000000000000012064641001012317 5ustar rootrootlibpri-1.4.14/doc/cc_qsig_monitor.fsm0000644000000000000000000001440711377242706016232 0ustar rootroot/* * 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; } } } libpri-1.4.14/doc/cc_ptmp_monitor_flattened.fsm0000644000000000000000000001416011377242706020271 0ustar rootroot/* * 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; } } } libpri-1.4.14/doc/cc_qsig_agent.fsm0000644000000000000000000000670511460077643015642 0ustar rootroot/* * 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_INTERNAL_CLEARING { Action Stop_T_RETENTION; Action Start_T_RETENTION; } 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; } } } libpri-1.4.14/doc/cc_ptp_monitor.fsm0000644000000000000000000001022711377242706016066 0ustar rootroot/* * 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; } } } libpri-1.4.14/doc/cc_qsig_monitor_flattened.fsm0000644000000000000000000002113311377242706020252 0ustar rootroot/* * 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; } } } libpri-1.4.14/doc/cc_ptmp_agent_flattened.fsm0000644000000000000000000003207411460077643017703 0ustar rootroot/* * 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_INTERNAL_CLEARING { Action Release_LinkID; Action Pass_Up_CC_Cancel; Action Set_Selfdestruct; Next_State CC_STATE_IDLE; } Stimulus CC_EVENT_CANCEL { Action Release_LinkID; 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_INTERNAL_CLEARING { Action Stop_T_RETENTION; Action Start_T_RETENTION; } Stimulus CC_EVENT_TIMEOUT_T_RETENTION { Action Send_EraseCallLinkageID; Action Release_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 Release_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 Release_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 Release_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; } } } libpri-1.4.14/doc/cc_ptmp_monitor.fsm0000644000000000000000000001144611377242706016247 0ustar rootroot/* * 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; } } } libpri-1.4.14/doc/cc_ptmp_agent.fsm0000644000000000000000000002257511460077643015662 0ustar rootroot/* * 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_INTERNAL_CLEARING { Action Release_LinkID; Action Pass_Up_CC_Cancel; Next_State CC_STATE_IDLE; } Stimulus CC_EVENT_CANCEL { Action Release_LinkID; 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_INTERNAL_CLEARING { Action Stop_T_RETENTION; Action Start_T_RETENTION; } Stimulus CC_EVENT_TIMEOUT_T_RETENTION { Action Send_EraseCallLinkageID; Action Release_LinkID; Action Pass_Up_CC_Cancel; Next_State CC_STATE_IDLE; } Stimulus CC_EVENT_CANCEL { Action Send_EraseCallLinkageID; Action Release_LinkID; Next_State CC_STATE_IDLE; } } State CC_STATE_REQUESTED { Epilog { Action Send_EraseCallLinkageID; Action Release_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; } } } libpri-1.4.14/doc/cc_qsig_agent_flattened.fsm0000644000000000000000000001154711460077643017670 0ustar rootroot/* * 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_INTERNAL_CLEARING { Action Stop_T_RETENTION; Action Start_T_RETENTION; } 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; } } } libpri-1.4.14/doc/cc_ptp_agent.fsm0000644000000000000000000000715611460077643015503 0ustar rootroot/* * 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_INTERNAL_CLEARING { Action Pass_Up_CC_Cancel; Next_State CC_STATE_IDLE; } 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_INTERNAL_CLEARING { Action Stop_T_RETENTION; Action Start_T_RETENTION; } 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; } } } libpri-1.4.14/doc/cc_ptp_monitor_flattened.fsm0000644000000000000000000001333711377242706020121 0ustar rootroot/* * 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; } } } libpri-1.4.14/doc/cc_ptp_agent_flattened.fsm0000644000000000000000000001211111460077643017514 0ustar rootroot/* * 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_INTERNAL_CLEARING { 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_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_INTERNAL_CLEARING { Action Stop_T_RETENTION; Action Start_T_RETENTION; } 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; } } } libpri-1.4.14/rose.h0000644000000000000000000036023611400035226012704 0ustar rootroot/* * 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 definitions and prototypes * * \details * This file contains all of the data structures and definitions needed * for ROSE component encoding and decoding. * * ROSE - Remote Operations Service Element * ASN.1 - Abstract Syntax Notation 1 * APDU - Application Protocol Data Unit * * \author Richard Mudgett */ #ifndef _LIBPRI_ROSE_H #define _LIBPRI_ROSE_H #include #include #ifdef __cplusplus extern "C" { #endif /* ------------------------------------------------------------------- */ /* Northern Telecom DMS-100 RLT related operations */ #define ROSE_DMS100_RLT_SERVICE_ID 0x3e #define ROSE_DMS100_RLT_OPERATION_IND 0x01 #define ROSE_DMS100_RLT_THIRD_PARTY 0x02 /*! \brief ROSE operation-value function code */ enum rose_operation { /*! \brief No ROSE operation */ ROSE_None, /*! \brief Unknown OID/localValue operation-value code */ ROSE_Unknown, /* *INDENT-OFF* */ /* ETSI Diversion-Operations */ ROSE_ETSI_ActivationDiversion, /*!< Invoke/Result */ ROSE_ETSI_DeactivationDiversion, /*!< Invoke/Result */ ROSE_ETSI_ActivationStatusNotificationDiv,/*!< Invoke only */ ROSE_ETSI_DeactivationStatusNotificationDiv,/*!< Invoke only */ ROSE_ETSI_InterrogationDiversion, /*!< Invoke/Result */ ROSE_ETSI_DiversionInformation, /*!< Invoke only */ ROSE_ETSI_CallDeflection, /*!< Invoke/Result */ ROSE_ETSI_CallRerouting, /*!< Invoke/Result */ ROSE_ETSI_InterrogateServedUserNumbers, /*!< Invoke/Result */ ROSE_ETSI_DivertingLegInformation1, /*!< Invoke only */ ROSE_ETSI_DivertingLegInformation2, /*!< Invoke only */ ROSE_ETSI_DivertingLegInformation3, /*!< Invoke only */ /* * ETSI Advice-of-Charge-Operations * * Advice-Of-Charge-at-call-Setup(AOCS) * Advice-Of-Charge-During-the-call(AOCD) * Advice-Of-Charge-at-the-End-of-the-call(AOCE) */ ROSE_ETSI_ChargingRequest, /*!< Invoke/Result */ ROSE_ETSI_AOCSCurrency, /*!< Invoke only */ ROSE_ETSI_AOCSSpecialArr, /*!< Invoke only */ ROSE_ETSI_AOCDCurrency, /*!< Invoke only */ ROSE_ETSI_AOCDChargingUnit, /*!< Invoke only */ ROSE_ETSI_AOCECurrency, /*!< Invoke only */ ROSE_ETSI_AOCEChargingUnit, /*!< Invoke only */ /* ETSI Explicit-Call-Transfer-Operations-and-Errors */ ROSE_ETSI_EctExecute, /*!< Invoke/Result */ ROSE_ETSI_ExplicitEctExecute, /*!< Invoke/Result */ ROSE_ETSI_RequestSubaddress, /*!< Invoke only */ ROSE_ETSI_SubaddressTransfer, /*!< Invoke only */ ROSE_ETSI_EctLinkIdRequest, /*!< Invoke/Result */ 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 */ /* ETSI MCID-Operations */ ROSE_ETSI_MCIDRequest, /*!< Invoke/Result */ /* ETSI MWI-Operations-and-Errors */ ROSE_ETSI_MWIActivate, /*!< Invoke/Result */ ROSE_ETSI_MWIDeactivate, /*!< Invoke/Result */ ROSE_ETSI_MWIIndicate, /*!< Invoke only */ /* Q.SIG Name-Operations */ ROSE_QSIG_CallingName, /*!< Invoke only */ ROSE_QSIG_CalledName, /*!< Invoke only */ ROSE_QSIG_ConnectedName, /*!< Invoke only */ ROSE_QSIG_BusyName, /*!< Invoke only */ /* Q.SIG SS-AOC-Operations */ ROSE_QSIG_ChargeRequest, /*!< Invoke/Result */ ROSE_QSIG_GetFinalCharge, /*!< Invoke only */ ROSE_QSIG_AocFinal, /*!< Invoke only */ ROSE_QSIG_AocInterim, /*!< Invoke only */ ROSE_QSIG_AocRate, /*!< Invoke only */ ROSE_QSIG_AocComplete, /*!< Invoke/Result */ ROSE_QSIG_AocDivChargeReq, /*!< Invoke only */ /* Q.SIG Call-Transfer-Operations (CT) */ ROSE_QSIG_CallTransferIdentify, /*!< Invoke/Result */ ROSE_QSIG_CallTransferAbandon, /*!< Invoke only */ ROSE_QSIG_CallTransferInitiate, /*!< Invoke/Result */ ROSE_QSIG_CallTransferSetup, /*!< Invoke/Result */ ROSE_QSIG_CallTransferActive, /*!< Invoke only */ ROSE_QSIG_CallTransferComplete, /*!< Invoke only */ ROSE_QSIG_CallTransferUpdate, /*!< Invoke only */ ROSE_QSIG_SubaddressTransfer, /*!< Invoke only */ ROSE_QSIG_PathReplacement, /*!< Invoke only */ /* Q.SIG Call-Diversion-Operations */ ROSE_QSIG_ActivateDiversionQ, /*!< Invoke/Result */ ROSE_QSIG_DeactivateDiversionQ, /*!< Invoke/Result */ ROSE_QSIG_InterrogateDiversionQ, /*!< Invoke/Result */ ROSE_QSIG_CheckRestriction, /*!< Invoke/Result */ ROSE_QSIG_CallRerouting, /*!< Invoke/Result */ ROSE_QSIG_DivertingLegInformation1, /*!< Invoke only */ ROSE_QSIG_DivertingLegInformation2, /*!< Invoke only */ 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 */ ROSE_QSIG_MWIInterrogate, /*!< Invoke/Result */ /* Northern Telecom DMS-100 RLT related operations */ /*! Invoke/Result: Must set invokeId to ROSE_DMS100_RLT_OPERATION_IND */ ROSE_DMS100_RLT_OperationInd, /*! Invoke/Result: Must set invokeId to ROSE_DMS100_RLT_THIRD_PARTY */ ROSE_DMS100_RLT_ThirdParty, ROSE_NI2_InformationFollowing, /*!< Invoke only? */ ROSE_NI2_InitiateTransfer, /*!< Invoke only? Is this correct operation name? */ ROSE_Num_Operation_Codes /*!< Must be last in the enumeration */ /* *INDENT-ON* */ }; enum rose_error_code { /*! \brief No error occurred */ ROSE_ERROR_None, /*! \brief Unknown OID/localValue error-value code */ ROSE_ERROR_Unknown, /* General-Errors (ETS 300 196) and General-Error-List(Q.950) */ ROSE_ERROR_Gen_NotSubscribed, /*!< also: UserNotSubscribed */ ROSE_ERROR_Gen_NotAvailable, ROSE_ERROR_Gen_NotImplemented, /*!< Not in Q.950 */ ROSE_ERROR_Gen_InvalidServedUserNr, ROSE_ERROR_Gen_InvalidCallState, ROSE_ERROR_Gen_BasicServiceNotProvided, ROSE_ERROR_Gen_NotIncomingCall, ROSE_ERROR_Gen_SupplementaryServiceInteractionNotAllowed, ROSE_ERROR_Gen_ResourceUnavailable, /* Additional General-Error-List(Q.950) */ ROSE_ERROR_Gen_RejectedByNetwork, ROSE_ERROR_Gen_RejectedByUser, ROSE_ERROR_Gen_InsufficientInformation, ROSE_ERROR_Gen_CallFailure, ROSE_ERROR_Gen_ProceduralError, /* ETSI Diversion-Operations */ ROSE_ERROR_Div_InvalidDivertedToNr, ROSE_ERROR_Div_SpecialServiceNr, ROSE_ERROR_Div_DiversionToServedUserNr, ROSE_ERROR_Div_IncomingCallAccepted, ROSE_ERROR_Div_NumberOfDiversionsExceeded, ROSE_ERROR_Div_NotActivated, ROSE_ERROR_Div_RequestAlreadyAccepted, /* ETSI Advice-of-Charge-Operations */ ROSE_ERROR_AOC_NoChargingInfoAvailable, /* 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, /* ETSI MWI-Operations-and-Errors */ ROSE_ERROR_MWI_InvalidReceivingUserNr, ROSE_ERROR_MWI_ReceivingUserNotSubscribed, ROSE_ERROR_MWI_ControllingUserNotRegistered, ROSE_ERROR_MWI_IndicationNotDelivered, ROSE_ERROR_MWI_MaxNumOfControllingUsersReached, ROSE_ERROR_MWI_MaxNumOfActiveInstancesReached, /* Q.SIG from various specifications */ ROSE_ERROR_QSIG_Unspecified, /* Q.SIG SS-AOC-Operations */ ROSE_ERROR_QSIG_AOC_FreeOfCharge, /* Q.SIG Call-Transfer-Operations (CT) */ ROSE_ERROR_QSIG_CT_InvalidReroutingNumber, ROSE_ERROR_QSIG_CT_UnrecognizedCallIdentity, ROSE_ERROR_QSIG_CT_EstablishmentFailure, /* Q.SIG Call-Diversion-Operations (Additional Q.SIG specific errors) */ 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, /* Northern Telecom DMS-100 RLT related operations */ ROSE_ERROR_DMS100_RLT_BridgeFail, ROSE_ERROR_DMS100_RLT_CallIDNotFound, ROSE_ERROR_DMS100_RLT_NotAllowed, ROSE_ERROR_DMS100_RLT_SwitchEquipCongs, ROSE_ERROR_Num_Codes /*!< Must be last in the enumeration */ }; #define ROSE_REJECT_BASE(base) ((base) * 0x100) enum rose_reject_base { ROSE_REJECT_BASE_General, ROSE_REJECT_BASE_Invoke, ROSE_REJECT_BASE_Result, ROSE_REJECT_BASE_Error, /*! \brief Must be last in the list */ ROSE_REJECT_BASE_Last }; /*! * \brief From Facility-Information-Element-Components * {itu-t identified-organization etsi(0) 196 facility-information-element-component(3)} */ enum rose_reject_code { /*! \brief Not rejected */ ROSE_REJECT_None = -1, /*! \brief Unknown reject code */ ROSE_REJECT_Unknown = -2, /* *INDENT-OFF* */ ROSE_REJECT_Gen_UnrecognizedComponent = ROSE_REJECT_BASE(ROSE_REJECT_BASE_General) + 0, ROSE_REJECT_Gen_MistypedComponent = ROSE_REJECT_BASE(ROSE_REJECT_BASE_General) + 1, ROSE_REJECT_Gen_BadlyStructuredComponent = ROSE_REJECT_BASE(ROSE_REJECT_BASE_General) + 2, ROSE_REJECT_Inv_DuplicateInvocation = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Invoke) + 0, ROSE_REJECT_Inv_UnrecognizedOperation = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Invoke) + 1, ROSE_REJECT_Inv_MistypedArgument = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Invoke) + 2, ROSE_REJECT_Inv_ResourceLimitation = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Invoke) + 3, ROSE_REJECT_Inv_InitiatorReleasing = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Invoke) + 4, ROSE_REJECT_Inv_UnrecognizedLinkedID = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Invoke) + 5, ROSE_REJECT_Inv_LinkedResponseUnexpected = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Invoke) + 6, ROSE_REJECT_Inv_UnexpectedChildOperation = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Invoke) + 7, ROSE_REJECT_Res_UnrecognizedInvocation = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Result) + 0, ROSE_REJECT_Res_ResultResponseUnexpected = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Result) + 1, ROSE_REJECT_Res_MistypedResult = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Result) + 2, ROSE_REJECT_Err_UnrecognizedInvocation = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Error) + 0, ROSE_REJECT_Err_ErrorResponseUnexpected = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Error) + 1, ROSE_REJECT_Err_UnrecognizedError = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Error) + 2, ROSE_REJECT_Err_UnexpectedError = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Error) + 3, ROSE_REJECT_Err_MistypedParameter = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Error) + 4, /* *INDENT-ON* */ }; /* ------------------------------------------------------------------- */ /* * Q931InformationElement ::= [APPLICATION 0] IMPLICIT OCTET STRING */ struct roseQ931ie { /*! * \brief The Q.931 ie is present if length is nonzero. * (If this field is optional in the message.) */ u_int8_t length; /*! * \brief We mostly just need to store the contents so we will defer * decoding/encoding. * * \note To reduce the size of the structure, the memory for the * ie contents is "allocated" after the structure. * \note Remember the "allocated" memory needs to have room for a * null terminator. */ unsigned char contents[0]; }; enum { /*! Bearer Capability has a max length of 12. */ ROSE_Q931_MAX_BC = 12, /*! High Layer Compatibility has a max length of 5. */ ROSE_Q931_MAX_HLC = 5, /*! Low Layer Compatibility has a max length of 18. */ ROSE_Q931_MAX_LLC = 18, /*! * User-User Information has a network dependent maximum. * The network dependent maximum is either 35 or 131 octets * in non-USER-INFORMATION messages. */ ROSE_Q931_MAX_USER = 131, /*! * Progress Indicator has a max length of 4. * There can be multiple progress indicator ies. * Q.SIG allows up to 3. * ITU-T allows up to 2. */ ROSE_Q931_MAX_PROGRESS = 3 * 4, }; /* ------------------------------------------------------------------- */ /* * Comment obtained from ECMA-242 ASN.1 source. * a VisibleString containing: * - the (local) date in 8 digits (YYYYMMDD), * - followed by (local) time of day in 4 or 6 digits (HHMM[SS]), * - optionally followed by the letter "Z" or * by a local time differential in 5 digits ("+"HHMM or "-"HHMM); * this date and time representation follows ISO 8601 * Examples: * 1) 19970621194530, meaning 21 June 1997, 19:45:30; * 2) 19970621194530Z, meaning the same as 1); * 3) 19970621194530-0500, meaning the same as 1), * 5 hours retarded in relation to UTC time * * GeneralizedTime ::= [UNIVERSAL 24] IMPLICIT VisibleString */ struct roseGeneralizedTime { /*! GeneralizedTime (SIZE (12..19)) */ unsigned char str[19 + 1]; }; /* ------------------------------------------------------------------- */ /* * PartyNumber ::= CHOICE { * -- the numbering plan is the default numbering plan of * -- the network. It is recommended that this value is * -- used. * unknownPartyNumber [0] IMPLICIT NumberDigits, * * -- the numbering plan is according to * -- ITU-T Recommendation E.164. * publicPartyNumber [1] IMPLICIT PublicPartyNumber, * * -- ATM endsystem address encoded as an NSAP address. * nsapEncodedNumber [2] IMPLICIT NsapEncodedNumber, * * -- not used, value reserved. * dataPartyNumber [3] IMPLICIT NumberDigits, * * -- not used, value reserved. * telexPartyNumber [4] IMPLICIT NumberDigits, * privatePartyNumber [5] IMPLICIT PrivatePartyNumber, * * -- not used, value reserved. * nationalStandardPartyNumber [8] IMPLICIT NumberDigits * } */ struct rosePartyNumber { /*! * \brief Party numbering plan * \details * unknown(0), * public(1) - The numbering plan is according to ITU-T E.164, * nsapEncoded(2), * data(3) - Reserved, * telex(4) - Reserved, * private(5), * nationalStandard(8) - Reserved */ u_int8_t plan; /*! * \brief Type-Of-Number valid for public and private party number plans * \details * public: * unknown(0), * internationalNumber(1), * nationalNumber(2), * networkSpecificNumber(3) - Reserved, * subscriberNumber(4) - Reserved, * abbreviatedNumber(6) * \details * private: * unknown(0), * level2RegionalNumber(1), * level1RegionalNumber(2), * pTNSpecificNumber/pISNSpecificNumber(3), * localNumber(4), * abbreviatedNumber(6) */ u_int8_t ton; /*! \brief Number present if length is nonzero. */ u_int8_t length; /*! \brief Number string data. */ unsigned char str[20 + 1]; }; /* * NumberScreened ::= SEQUENCE { * partyNumber PartyNumber, * screeningIndicator ScreeningIndicator * } */ struct roseNumberScreened { struct rosePartyNumber number; /*! * \details * userProvidedNotScreened(0), * userProvidedVerifiedAndPassed(1), * userProvidedVerifiedAndFailed(2) (Not used, value reserved), * networkProvided(3) */ u_int8_t screening_indicator; }; /* * PartySubaddress ::= CHOICE { * -- not recommended * UserSpecifiedSubaddress, * * -- according to ITU-T Recommendation X.213 * NSAPSubaddress * } * * UserSpecifiedSubaddress ::= SEQUENCE { * SubaddressInformation, * * -- used when the coding of subaddress is BCD * oddCountIndicator BOOLEAN OPTIONAL * } * * -- specified according to ITU-T Recommendation X.213. Some * -- networks may limit the subaddress value to some other * -- length, e.g. 4 octets * NSAPSubaddress ::= OCTET STRING (SIZE(1..20)) * * -- coded according to user requirements. Some networks may * -- limit the subaddress value to some other length, * -- e.g. 4 octets * SubaddressInformation ::= OCTET STRING (SIZE(1..20)) */ struct rosePartySubaddress { /*! \brief Subaddress type UserSpecified(0), NSAP(1) */ u_int8_t type; /*! \brief Subaddress present if length is nonzero */ u_int8_t length; union { /*! \brief Specified according to ITU-T Recommendation X.213 */ unsigned char nsap[20 + 1]; /*! \brief Use of this formatting is not recommended */ struct { /*! \brief TRUE if OddCount present */ u_int8_t odd_count_present; /*! * \brief TRUE if odd number of BCD digits (optional) * \note Used when the coding of subaddress is BCD. */ u_int8_t odd_count; unsigned char information[20 + 1]; } user_specified; } u; }; /* * Address ::= SEQUENCE { * PartyNumber, * PartySubaddress OPTIONAL * } */ struct roseAddress { struct rosePartyNumber number; /*! \brief Subaddress (Optional) */ struct rosePartySubaddress subaddress; }; /* * AddressScreened ::= SEQUENCE { * PartyNumber, * ScreeningIndicator, * PartySubaddress OPTIONAL * } */ struct roseAddressScreened { struct rosePartyNumber number; /*! \brief Subaddress (Optional) */ struct rosePartySubaddress subaddress; /*! * \details * userProvidedNotScreened(0), * userProvidedVerifiedAndPassed(1), * userProvidedVerifiedAndFailed(2) (Not used, value reserved), * networkProvided(3) */ u_int8_t screening_indicator; }; /* * PresentedNumberUnscreened ::= CHOICE { * presentationAllowedNumber [0] EXPLICIT PartyNumber, * presentationRestricted [1] IMPLICIT NULL, * numberNotAvailableDueToInterworking [2] IMPLICIT NULL, * presentationRestrictedNumber [3] EXPLICIT PartyNumber * } */ struct rosePresentedNumberUnscreened { struct rosePartyNumber number; /*! * \brief Number presentation type * \details * presentationAllowedNumber(0), * presentationRestricted(1), * numberNotAvailableDueToInterworking(2), * presentationRestrictedNumber(3) */ u_int8_t presentation; }; /* * PresentedNumberScreened ::= CHOICE { * presentationAllowedNumber [0] IMPLICIT NumberScreened, * presentationRestricted [1] IMPLICIT NULL, * numberNotAvailableDueToInterworking [2] IMPLICIT NULL, * presentationRestrictedNumber [3] IMPLICIT NumberScreened * } */ struct rosePresentedNumberScreened { /*! \brief Screened number */ struct roseNumberScreened screened; /*! * \brief Number presentation type * \details * presentationAllowedNumber(0), * presentationRestricted(1), * numberNotAvailableDueToInterworking(2), * presentationRestrictedNumber(3) */ u_int8_t presentation; }; /* * PresentedAddressScreened ::= CHOICE { * presentationAllowedAddress [0] IMPLICIT AddressScreened, * presentationRestricted [1] IMPLICIT NULL, * numberNotAvailableDueToInterworking [2] IMPLICIT NULL, * presentationRestrictedAddress [3] IMPLICIT AddressScreened * } */ struct rosePresentedAddressScreened { /*! \breif Screened address */ struct roseAddressScreened screened; /*! * \brief Address presentation type * \details * presentationAllowedAddress(0), * presentationRestricted(1), * numberNotAvailableDueToInterworking(2), * presentationRestrictedAddress(3) */ u_int8_t presentation; }; /* ------------------------------------------------------------------- */ /* * Time ::= SEQUENCE { * lengthOfTimeUnit [1] IMPLICIT LengthOfTimeUnit, * scale [2] IMPLICIT Scale * } */ struct roseEtsiAOCTime { /*! LengthOfTimeUnit ::= INTEGER (0..16777215) -- 24 bit number */ u_int32_t length; /*! * \details * oneHundredthSecond(0), * oneTenthSecond(1), * oneSecond(2), * tenSeconds(3), * oneMinute(4), * oneHour(5), * twentyFourHours(6) */ u_int8_t scale; }; /* * Amount ::= SEQUENCE { * currencyAmount [1] IMPLICIT CurrencyAmount, * multiplier [2] IMPLICIT Multiplier * } */ struct roseEtsiAOCAmount { /*! CurrencyAmount ::= INTEGER (0..16777215) -- 24 bit number */ u_int32_t currency; /*! * \details * oneThousandth(0), * oneHundredth(1), * oneTenth(2), * one(3), * ten(4), * hundred(5), * thousand(6) */ u_int8_t multiplier; }; /* * DurationCurrency ::= SEQUENCE { * dCurrency [1] IMPLICIT Currency, * dAmount [2] IMPLICIT Amount, * dChargingType [3] IMPLICIT ChargingType, * dTime [4] IMPLICIT Time, * dGranularity [5] IMPLICIT Time OPTIONAL * } */ struct roseEtsiAOCDurationCurrency { struct roseEtsiAOCAmount amount; struct roseEtsiAOCTime time; /*! \breif dGranularity (optional) */ struct roseEtsiAOCTime granularity; /*! Currency ::= IA5String (SIZE (1..10)) -- Name of currency. */ unsigned char currency[10 + 1]; /*! * \details * continuousCharging(0), * stepFunction(1) */ u_int8_t charging_type; /*! TRUE if granularity time is present */ u_int8_t granularity_present; }; /* * FlatRateCurrency ::= SEQUENCE { * fRCurrency [1] IMPLICIT Currency, * fRAmount [2] IMPLICIT Amount * } */ struct roseEtsiAOCFlatRateCurrency { struct roseEtsiAOCAmount amount; /*! Currency ::= IA5String (SIZE (1..10)) -- Name of currency. */ unsigned char currency[10 + 1]; }; /* * VolumeRateCurrency ::= SEQUENCE { * vRCurrency [1] IMPLICIT Currency, * vRAmount [2] IMPLICIT Amount, * vRVolumeUnit [3] IMPLICIT VolumeUnit * } */ struct roseEtsiAOCVolumeRateCurrency { struct roseEtsiAOCAmount amount; /*! Currency ::= IA5String (SIZE (1..10)) -- Name of currency. */ unsigned char currency[10 + 1]; /*! * \brief Volume rate volume unit * \details * octet(0), * segment(1), * message(2) */ u_int8_t unit; }; /* * AOCSCurrencyInfo ::= SEQUENCE { * chargedItem ChargedItem, * CHOICE { * specialChargingCode SpecialChargingCode, * durationCurrency [1] IMPLICIT DurationCurrency, * flatRateCurrency [2] IMPLICIT FlatRateCurrency, * volumeRateCurrency [3] IMPLICIT VolumeRateCurrency * freeOfCharge [4] IMPLICIT NULL, * currencyInfoNotAvailable [5] IMPLICIT NULL * } * } */ struct roseEtsiAOCSCurrencyInfo { union { struct roseEtsiAOCDurationCurrency duration; struct roseEtsiAOCFlatRateCurrency flat_rate; struct roseEtsiAOCVolumeRateCurrency volume_rate; /*! SpecialChargingCode ::= INTEGER (1..10) */ u_int8_t special_charging_code; } u; /*! * \brief Determine what is stored in the union. * \details * specialChargingCode(0), * durationCurrency(1), * flatRateCurrency(2), * volumeRateCurrency(3), * freeOfCharge(4), * currencyInfoNotAvailable(5), */ u_int8_t currency_type; /*! * \brief What service is being billed. * \details * basicCommunication(0), * callAttempt(1), * callSetup(2), * userToUserInfo(3), * operationOfSupplementaryServ(4) */ u_int8_t charged_item; }; /* * AOCSCurrencyInfoList ::= SEQUENCE SIZE (1..10) OF AOCSCurrencyInfo */ struct roseEtsiAOCSCurrencyInfoList { /*! \brief SEQUENCE SIZE (1..10) OF AOCSCurrencyInfo */ struct roseEtsiAOCSCurrencyInfo list[10]; /*! \brief Number of AOCSCurrencyInfo records present */ u_int8_t num_records; }; /* * RecordedCurrency ::= SEQUENCE { * rCurrency [1] IMPLICIT Currency, * rAmount [2] IMPLICIT Amount * } */ struct roseEtsiAOCRecordedCurrency { /*! Amount of currency involved. */ struct roseEtsiAOCAmount amount; /*! Currency ::= IA5String (SIZE (1..10)) -- Name of currency. */ unsigned char currency[10 + 1]; }; /* * RecordedUnits ::= SEQUENCE { * CHOICE { * recordedNumberOfUnits NumberOfUnits, * notAvailable NULL * }, * recordedTypeOfUnits TypeOfUnit OPTIONAL * } */ struct roseEtsiAOCRecordedUnits { /*! \brief recordedNumberOfUnits INTEGER (0..16777215) -- 24 bit number */ u_int32_t number_of_units; /*! \brief TRUE if number_of_units is not available (not present) */ u_int8_t not_available; /*! \brief recordedTypeOfUnits INTEGER (1..16) (optional) */ u_int8_t type_of_unit; /*! \brief TRUE if type_of_unit is present */ u_int8_t type_of_unit_present; }; /* * RecordedUnitsList ::= SEQUENCE SIZE (1..32) OF RecordedUnits */ struct roseEtsiAOCRecordedUnitsList { /*! \brief SEQUENCE SIZE (1..32) OF RecordedUnits */ struct roseEtsiAOCRecordedUnits list[32]; /*! \brief Number of RecordedUnits records present */ u_int8_t num_records; }; /* * ChargingAssociation ::= CHOICE { * chargedNumber [0] EXPLICIT PartyNumber, * chargeIdentifier ChargeIdentifier * } */ struct roseEtsiAOCChargingAssociation { /*! chargeIdentifier: INTEGER (-32768..32767) -- 16 bit number */ int16_t id; /*! chargedNumber */ struct rosePartyNumber number; /*! * \details * charge_identifier(0), * charged_number(1) */ u_int8_t type; }; /* * AOCECurrencyInfo ::= SEQUENCE { * CHOICE { * freeOfCharge [1] IMPLICIT NULL, * specificCurrency SEQUENCE { * recordedCurrency [1] IMPLICIT RecordedCurrency, * aOCEBillingId [2] IMPLICIT AOCEBillingId OPTIONAL * } * }, * chargingAssociation ChargingAssociation OPTIONAL * } */ struct roseEtsiAOCECurrencyInfo { struct { /*! \brief recorded currency */ struct roseEtsiAOCRecordedCurrency recorded; /*! * \brief AOCEBillingId (optional) * \details * normalCharging(0), * reverseCharging(1), * creditCardCharging(2), * callForwardingUnconditional(3), * callForwardingBusy(4), * callForwardingNoReply(5), * callDeflection(6), * callTransfer(7) */ u_int8_t billing_id; /*! \brief TRUE if billing id is present */ u_int8_t billing_id_present; } specific; /*! \brief chargingAssociation (optional) */ struct roseEtsiAOCChargingAssociation charging_association; /*! \brief TRUE if charging_association is present */ u_int8_t charging_association_present; /*! * \brief TRUE if this is free of charge. * \note When TRUE, the contents of specific are not valid. */ u_int8_t free_of_charge; }; /* * AOCEChargingUnitInfo ::= SEQUENCE { * CHOICE { * freeOfCharge [1] IMPLICIT NULL, * specificChargingUnits SEQUENCE * { * recordedUnitsList [1] IMPLICIT RecordedUnitsList, * aOCEBillingId [2] IMPLICIT AOCEBillingId OPTIONAL * } * }, * chargingAssociation ChargingAssociation OPTIONAL * } */ struct roseEtsiAOCEChargingUnitInfo { /*! \brief Not valid if free_of_charge is TRUE */ struct { /*! \brief RecordedUnitsList */ struct roseEtsiAOCRecordedUnitsList recorded; /*! * \brief AOCEBillingId (optional) * \details * normalCharging(0), * reverseCharging(1), * creditCardCharging(2), * callForwardingUnconditional(3), * callForwardingBusy(4), * callForwardingNoReply(5), * callDeflection(6), * callTransfer(7) */ u_int8_t billing_id; /*! \brief TRUE if billing id is present */ u_int8_t billing_id_present; } specific; /*! \brief chargingAssociation (optional) */ struct roseEtsiAOCChargingAssociation charging_association; /*! \brief TRUE if charging_association is present */ u_int8_t charging_association_present; /*! * \brief TRUE if this is free of charge. * \note When TRUE, the contents of specific are not valid. */ u_int8_t free_of_charge; }; /* * ARGUMENT ChargingCase */ struct roseEtsiChargingRequest_ARG { /*! * \details * chargingInformationAtCallSetup(0), * chargingDuringACall(1), * chargingAtTheEndOfACall(2) */ u_int8_t charging_case; }; /* * RESULT CHOICE { * AOCSCurrencyInfoList, * AOCSSpecialArrInfo, * chargingInfoFollows NULL * } */ struct roseEtsiChargingRequest_RES { union { struct roseEtsiAOCSCurrencyInfoList currency_info; /*! AOCSSpecialArrInfo ::= INTEGER (1..10) */ u_int8_t special_arrangement; } u; /*! * \details * currency_info_list(0), * special_arrangement_info(1), * charging_info_follows(2) */ u_int8_t type; }; /* * ARGUMENT CHOICE { * chargeNotAvailable NULL, * AOCSCurrencyInfoList * } */ struct roseEtsiAOCSCurrency_ARG { struct roseEtsiAOCSCurrencyInfoList currency_info; /*! * \details * charge_not_available(0), * currency_info_list(1) */ u_int8_t type; }; /* * ARGUMENT CHOICE { * chargeNotAvailable NULL, * AOCSSpecialArrInfo * } */ struct roseEtsiAOCSSpecialArr_ARG { /*! * \details * charge_not_available(0), * special_arrangement_info(1) */ u_int8_t type; /*! AOCSSpecialArrInfo ::= INTEGER (1..10) */ u_int8_t special_arrangement; }; /* * ARGUMENT CHOICE { * chargeNotAvailable NULL, * aOCDCurrencyInfo CHOICE { * freeOfCharge [1] IMPLICIT NULL, * specificCurrency SEQUENCE { * recordedCurrency [1] IMPLICIT RecordedCurrency, * typeOfChargingInfo [2] IMPLICIT TypeOfChargingInfo, * aOCDBillingId [3] IMPLICIT AOCDBillingId OPTIONAL * } * } * } */ struct roseEtsiAOCDCurrency_ARG { struct { /*! \brief recorded currency */ struct roseEtsiAOCRecordedCurrency recorded; /*! * \brief Type of recorded charging information. * \details * subTotal(0), * total(1) */ u_int8_t type_of_charging_info; /*! * \brief AOCDBillingId (optional) * \details * normalCharging(0), * reverseCharging(1), * creditCardCharging(2) */ u_int8_t billing_id; /*! \brief TRUE if billing id is present */ u_int8_t billing_id_present; } specific; /*! * \details * charge_not_available(0), * free_of_charge(1), * specific_currency(2) */ u_int8_t type; }; /* * ARGUMENT CHOICE { * chargeNotAvailable NULL, * aOCDChargingUnitInfo CHOICE { * freeOfCharge [1] IMPLICIT NULL, * specificChargingUnits SEQUENCE { * recordedUnitsList [1] IMPLICIT RecordedUnitsList, * typeOfChargingInfo [2] IMPLICIT TypeOfChargingInfo, * aOCDBillingId [3] IMPLICIT AOCDBillingId OPTIONAL * } * } * } */ struct roseEtsiAOCDChargingUnit_ARG { struct { /*! \brief RecordedUnitsList */ struct roseEtsiAOCRecordedUnitsList recorded; /*! * \brief Type of recorded charging information. * \details * subTotal(0), * total(1) */ u_int8_t type_of_charging_info; /*! * \brief AOCDBillingId (optional) * \details * normalCharging(0), * reverseCharging(1), * creditCardCharging(2) */ u_int8_t billing_id; /*! \brief TRUE if billing id is present */ u_int8_t billing_id_present; } specific; /*! * \details * charge_not_available(0), * free_of_charge(1), * specific_charging_units(2) */ u_int8_t type; }; /* * ARGUMENT CHOICE { * chargeNotAvailable NULL, * AOCECurrencyInfo * } */ struct roseEtsiAOCECurrency_ARG { struct roseEtsiAOCECurrencyInfo currency_info; /*! * \details * charge_not_available(0), * currency_info(1) */ u_int8_t type; }; /* * ARGUMENT CHOICE { * chargeNotAvailable NULL, * AOCEChargingUnitInfo * } */ struct roseEtsiAOCEChargingUnit_ARG { struct roseEtsiAOCEChargingUnitInfo charging_unit; /*! * \details * charge_not_available(0), * charging_unit(1) */ u_int8_t type; }; /* ------------------------------------------------------------------- */ /* * ARGUMENT SEQUENCE { * procedure Procedure, * basicService BasicService, * forwardedToAddress Address, * servedUserNr ServedUserNr * } */ struct roseEtsiActivationDiversion_ARG { /*! \brief Forwarded to address */ struct roseAddress forwarded_to; /*! \brief Forward all numbers if not present (Number length is zero). */ struct rosePartyNumber served_user_number; /*! \details cfu(0), cfb(1), cfnr(2) */ u_int8_t procedure; /*! * \details * allServices(0), * speech(1), * unrestrictedDigitalInformation(2), * audio3k1Hz(3), * unrestrictedDigitalInformationWithTonesAndAnnouncements(4), * multirate(5), * telephony3k1Hz(32), * teletex(33), * telefaxGroup4Class1(34), * videotexSyntaxBased(35), * videotelephony(36), * telefaxGroup2-3(37), * telephony7kHz(38), * euroFileTransfer(39), * fileTransferAndAccessManagement(40), * videoconference(41), * audioGraphicConference(42) */ u_int8_t basic_service; }; /* * ARGUMENT SEQUENCE { * procedure Procedure, * basicService BasicService, * servedUserNr ServedUserNr * } */ struct roseEtsiDeactivationDiversion_ARG { /*! \brief Forward all numbers if not present (Number length is zero). */ struct rosePartyNumber served_user_number; /*! \details cfu(0), cfb(1), cfnr(2) */ u_int8_t procedure; /*! * \details * allServices(0), * speech(1), * unrestrictedDigitalInformation(2), * audio3k1Hz(3), * unrestrictedDigitalInformationWithTonesAndAnnouncements(4), * multirate(5), * telephony3k1Hz(32), * teletex(33), * telefaxGroup4Class1(34), * videotexSyntaxBased(35), * videotelephony(36), * telefaxGroup2-3(37), * telephony7kHz(38), * euroFileTransfer(39), * fileTransferAndAccessManagement(40), * videoconference(41), * audioGraphicConference(42) */ u_int8_t basic_service; }; /* * ARGUMENT SEQUENCE { * procedure Procedure, * basicService BasicService, * forwardedToAddresss Address, * servedUserNr ServedUserNr * } */ struct roseEtsiActivationStatusNotificationDiv_ARG { /*! \brief Forwarded to address */ struct roseAddress forwarded_to; /*! \brief Forward all numbers if not present (Number length is zero). */ struct rosePartyNumber served_user_number; /*! \details cfu(0), cfb(1), cfnr(2) */ u_int8_t procedure; /*! * \details * allServices(0), * speech(1), * unrestrictedDigitalInformation(2), * audio3k1Hz(3), * unrestrictedDigitalInformationWithTonesAndAnnouncements(4), * multirate(5), * telephony3k1Hz(32), * teletex(33), * telefaxGroup4Class1(34), * videotexSyntaxBased(35), * videotelephony(36), * telefaxGroup2-3(37), * telephony7kHz(38), * euroFileTransfer(39), * fileTransferAndAccessManagement(40), * videoconference(41), * audioGraphicConference(42) */ u_int8_t basic_service; }; /* * ARGUMENT SEQUENCE { * procedure Procedure, * basicService BasicService, * servedUserNr ServedUserNr * } */ struct roseEtsiDeactivationStatusNotificationDiv_ARG { /*! \brief Forward all numbers if not present (Number length is zero). */ struct rosePartyNumber served_user_number; /*! \details cfu(0), cfb(1), cfnr(2) */ u_int8_t procedure; /*! * \details * allServices(0), * speech(1), * unrestrictedDigitalInformation(2), * audio3k1Hz(3), * unrestrictedDigitalInformationWithTonesAndAnnouncements(4), * multirate(5), * telephony3k1Hz(32), * teletex(33), * telefaxGroup4Class1(34), * videotexSyntaxBased(35), * videotelephony(36), * telefaxGroup2-3(37), * telephony7kHz(38), * euroFileTransfer(39), * fileTransferAndAccessManagement(40), * videoconference(41), * audioGraphicConference(42) */ u_int8_t basic_service; }; /* * ARGUMENT SEQUENCE { * procedure Procedure, * basicService BasicService DEFAULT allServices, * servedUserNr ServedUserNr * } */ struct roseEtsiInterrogationDiversion_ARG { /*! \brief Forward all numbers if not present (Number length is zero). */ struct rosePartyNumber served_user_number; /*! \details cfu(0), cfb(1), cfnr(2) */ u_int8_t procedure; /*! * \details * DEFAULT allServices * * \details * allServices(0), * speech(1), * unrestrictedDigitalInformation(2), * audio3k1Hz(3), * unrestrictedDigitalInformationWithTonesAndAnnouncements(4), * multirate(5), * telephony3k1Hz(32), * teletex(33), * telefaxGroup4Class1(34), * videotexSyntaxBased(35), * videotelephony(36), * telefaxGroup2-3(37), * telephony7kHz(38), * euroFileTransfer(39), * fileTransferAndAccessManagement(40), * videoconference(41), * audioGraphicConference(42) */ u_int8_t basic_service; }; /* * IntResult ::= SEQUENCE { * servedUserNr ServedUserNr, * basicService BasicService, * procedure Procedure, * forwardedToAddress Address * } */ struct roseEtsiForwardingRecord { /*! \brief Forwarded to address */ struct roseAddress forwarded_to; /*! \brief Forward all numbers if not present (Number length is zero). */ struct rosePartyNumber served_user_number; /*! \details cfu(0), cfb(1), cfnr(2) */ u_int8_t procedure; /*! * \details * allServices(0), * speech(1), * unrestrictedDigitalInformation(2), * audio3k1Hz(3), * unrestrictedDigitalInformationWithTonesAndAnnouncements(4), * multirate(5), * telephony3k1Hz(32), * teletex(33), * telefaxGroup4Class1(34), * videotexSyntaxBased(35), * videotelephony(36), * telefaxGroup2-3(37), * telephony7kHz(38), * euroFileTransfer(39), * fileTransferAndAccessManagement(40), * videoconference(41), * audioGraphicConference(42) */ u_int8_t basic_service; }; /* * roseInterrogationDiversion_RES * IntResultList ::= SET SIZE (0..29) OF IntResult */ struct roseEtsiForwardingList { /*! * \brief SET SIZE (0..29) OF Forwarding Records * \note Reduced the size of the array to conserve * potential stack usage. */ struct roseEtsiForwardingRecord list[10]; /*! \brief Number of Forwarding records present */ u_int8_t num_records; }; /* * ARGUMENT SEQUENCE { * diversionReason DiversionReason, * basicService BasicService, * servedUserSubaddress PartySubaddress OPTIONAL, * callingAddress [0] EXPLICIT PresentedAddressScreened OPTIONAL, * originalCalledNr [1] EXPLICIT PresentedNumberUnscreened OPTIONAL, * lastDivertingNr [2] EXPLICIT PresentedNumberUnscreened OPTIONAL, * lastDivertingReason [3] EXPLICIT DiversionReason OPTIONAL, * * -- The User-user information element, as specified * -- in ETS 300 102-1 [11], subclause 4.5.29, shall * -- be embedded in the userInfo parameter. * userInfo Q931InformationElement OPTIONAL * } */ struct roseEtsiDiversionInformation_ARG { /*! \brief Served user subaddress (Optional) */ struct rosePartySubaddress served_user_subaddress; /*! \brief Calling address (Optional) */ struct rosePresentedAddressScreened calling; /*! \brief Original called number (Optional) */ struct rosePresentedNumberUnscreened original_called; /*! \brief Last diverting number (Optional) */ struct rosePresentedNumberUnscreened last_diverting; /*! \brief User-User information embedded in Q.931 IE (Optional) */ struct roseQ931ie q931ie; /*! \brief q931ie.contents "allocated" after the stucture. */ unsigned char q931ie_contents[ROSE_Q931_MAX_USER + 1]; /*! * \brief Last diverting reason (Optional) * * \details * unknown(0), * cfu(1), * cfb(2), * cfnr(3), * cdAlerting(4), * cdImmediate(5) */ u_int8_t last_diverting_reason; /*! \brief TRUE if CallingAddress is present */ u_int8_t calling_present; /*! \brief TRUE if OriginalCalled is present */ u_int8_t original_called_present; /*! \brief TRUE if LastDiverting is present */ u_int8_t last_diverting_present; /*! \brief TRUE if LastDivertingReason is present */ u_int8_t last_diverting_reason_present; /*! * \details * unknown(0), * cfu(1), * cfb(2), * cfnr(3), * cdAlerting(4), * cdImmediate(5) */ u_int8_t diversion_reason; /*! * \details * allServices(0), * speech(1), * unrestrictedDigitalInformation(2), * audio3k1Hz(3), * unrestrictedDigitalInformationWithTonesAndAnnouncements(4), * multirate(5), * telephony3k1Hz(32), * teletex(33), * telefaxGroup4Class1(34), * videotexSyntaxBased(35), * videotelephony(36), * telefaxGroup2-3(37), * telephony7kHz(38), * euroFileTransfer(39), * fileTransferAndAccessManagement(40), * videoconference(41), * audioGraphicConference(42) */ u_int8_t basic_service; }; /* * ARGUMENT SEQUENCE { * deflectionAddress Address, * presentationAllowedDivertedToUser PresentationAllowedIndicator OPTIONAL * } */ struct roseEtsiCallDeflection_ARG { /*! \brief Deflection address (Deflected-To address) */ struct roseAddress deflection; /*! \brief TRUE if PresentationAllowedToDivertedToUser is present */ u_int8_t presentation_allowed_to_diverted_to_user_present; /*! \brief TRUE if presentation is allowed (Optional) */ u_int8_t presentation_allowed_to_diverted_to_user; }; /* * ARGUMENT SEQUENCE { * reroutingReason DiversionReason, * calledAddress Address, * reroutingCounter DiversionCounter, * * -- The User-user information element (optional), * -- High layer compatibility information element (optional), * -- Bearer capability information element * -- and Low layer compatibility information element (optional) * -- as specified in ETS 300 102-1 [11] subclause 4.5 shall be * -- embedded in the q931InfoElement. * q931InfoElement Q931InformationElement, * lastReroutingNr [1] EXPLICIT PresentedNumberUnscreened, * subscriptionOption [2] EXPLICIT SubscriptionOption DEFAULT noNotification, * callingPartySubaddress [3] EXPLICIT PartySubaddress OPTIONAL * } */ struct roseEtsiCallRerouting_ARG { struct roseAddress called_address; /*! \brief The BC, HLC (optional), LLC (optional), and User-user (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 + ROSE_Q931_MAX_USER + 1]; /*! \brief Last rerouting number */ struct rosePresentedNumberUnscreened last_rerouting; /*! \brief Calling party subaddress (Optional) */ struct rosePartySubaddress calling_subaddress; /*! * \details * unknown(0), * cfu(1), * cfb(2), * cfnr(3), * cdAlerting(4), * cdImmediate(5) */ u_int8_t rerouting_reason; /*! \brief Range 1-5 */ u_int8_t rerouting_counter; /*! * \details * DEFAULT noNotification * * \details * noNotification(0), * notificationWithoutDivertedToNr(1), * notificationWithDivertedToNr(2) */ u_int8_t subscription_option; }; /* * roseInterrogateServedUserNumbers_RES * ServedUserNumberList ::= SET SIZE (0..99) OF PartyNumber */ struct roseEtsiServedUserNumberList { /*! * \brief SET SIZE (0..99) OF Served user numbers * \note Reduced the size of the array to conserve * potential stack usage. */ struct rosePartyNumber number[20]; /*! \brief Number of Served user numbers present */ u_int8_t num_records; }; /* * ARGUMENT SEQUENCE { * diversionReason DiversionReason, * subscriptionOption SubscriptionOption, * divertedToNumber PresentedNumberUnscreened OPTIONAL * } */ struct roseEtsiDivertingLegInformation1_ARG { /*! \brief Diverted to number (Optional) */ struct rosePresentedNumberUnscreened diverted_to; /*! \brief TRUE if DivertedTo is present */ u_int8_t diverted_to_present; /*! * \details * unknown(0), * cfu(1), * cfb(2), * cfnr(3), * cdAlerting(4), * cdImmediate(5) */ u_int8_t diversion_reason; /*! * \details * noNotification(0), * notificationWithoutDivertedToNr(1), * notificationWithDivertedToNr(2) */ u_int8_t subscription_option; }; /* * ARGUMENT SEQUENCE { * diversionCounter DiversionCounter, * diversionReason DiversionReason, * divertingNr [1] EXPLICIT PresentedNumberUnscreened OPTIONAL, * originalCalledNr [2] EXPLICIT PresentedNumberUnscreened OPTIONAL * } */ struct roseEtsiDivertingLegInformation2_ARG { /*! \brief Diverting number (Optional) */ struct rosePresentedNumberUnscreened diverting; /*! \brief Original called number (Optional) */ struct rosePresentedNumberUnscreened original_called; /*! \brief TRUE if Diverting number is present */ u_int8_t diverting_present; /*! \brief TRUE if OriginalCalled is present */ u_int8_t original_called_present; /*! * \details * unknown(0), * cfu(1), * cfb(2), * cfnr(3), * cdAlerting(4), * cdImmediate(5) */ u_int8_t diversion_reason; /*! \brief Range 1-5 */ u_int8_t diversion_counter; }; /* * ARGUMENT presentationAllowedIndicator PresentationAllowedIndicator */ struct roseEtsiDivertingLegInformation3_ARG { /*! \brief TRUE if presentation is allowed */ u_int8_t presentation_allowed_indicator; }; /* ------------------------------------------------------------------- */ /* * ARGUMENT LinkId */ struct roseEtsiExplicitEctExecute_ARG { int16_t link_id; }; /* * ARGUMENT transferredToSubaddress PartySubaddress */ struct roseEtsiSubaddressTransfer_ARG { /*! \brief Transferred to subaddress */ struct rosePartySubaddress subaddress; }; /* * RESULT LinkId */ struct roseEtsiEctLinkIdRequest_RES { int16_t link_id; }; /* * ARGUMENT SEQUENCE { * ENUMERATED { * alerting (0), * active (1) * }, * redirectionNumber PresentedNumberUnscreened OPTIONAL * } */ struct roseEtsiEctInform_ARG { /*! \brief Redirection Number (Optional) */ struct rosePresentedNumberUnscreened redirection; /*! \brief TRUE if the Redirection Number is present */ u_int8_t redirection_present; /*! \details alerting(0), active(1) */ u_int8_t status; }; /* * ARGUMENT CallTransferIdentity */ struct roseEtsiEctLoopTest_ARG { int8_t call_transfer_id; }; /* * RESULT LoopResult */ struct roseEtsiEctLoopTest_RES { /*! * \details * insufficientInformation(0), * noLoopExists(1), * simultaneousTransfer(2) */ u_int8_t loop_result; }; /* ------------------------------------------------------------------- */ /* * 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; }; /* ------------------------------------------------------------------- */ /* * MessageID ::= SEQUENCE { * messageRef MessageRef, * status MessageStatus * } */ struct roseEtsiMessageID { /*! \brief Message reference number. (INTEGER (0..65535)) */ u_int16_t reference_number; /*! * \brief Message status * \details * added_message(0), * removed_message(1) */ u_int8_t status; }; /* * ARGUMENT SEQUENCE { * receivingUserNr PartyNumber, * basicService BasicService, * controllingUserNr [1] EXPLICIT PartyNumber OPTIONAL, * numberOfMessages [2] EXPLICIT MessageCounter OPTIONAL, * controllingUserProvidedNr [3] EXPLICIT PartyNumber OPTIONAL, * time [4] EXPLICIT GeneralizedTime OPTIONAL, * messageId [5] EXPLICIT MessageID OPTIONAL, * mode [6] EXPLICIT InvocationMode OPTIONAL * } */ struct roseEtsiMWIActivate_ARG { /*! \brief Number of messages in mailbox. (INTEGER (0..65535)) (Optional) */ u_int16_t number_of_messages; /*! \brief Message ID (Status of this message) (Optional)*/ struct roseEtsiMessageID message_id; /*! \brief Receiving user number (Who the message is for.) */ struct rosePartyNumber receiving_user_number; /*! \brief Controlling user number (Mailbox number) (Optional) */ struct rosePartyNumber controlling_user_number; /*! \brief Controlling user provided number (Caller-ID of party leaving message) (Optional) */ struct rosePartyNumber controlling_user_provided_number; /*! \brief When message left. (optional) */ struct roseGeneralizedTime time; /*! * \brief Type of call leaving message. * \details * allServices(0), * speech(1), * unrestrictedDigitalInformation(2), * audio3k1Hz(3), * unrestrictedDigitalInformationWithTonesAndAnnouncements(4), * multirate(5), * telephony3k1Hz(32), * teletex(33), * telefaxGroup4Class1(34), * videotexSyntaxBased(35), * videotelephony(36), * telefaxGroup2-3(37), * telephony7kHz(38), * euroFileTransfer(39), * fileTransferAndAccessManagement(40), * videoconference(41), * audioGraphicConference(42) */ u_int8_t basic_service; /*! * \brief Invocation mode (When it should be delivered.) (Optional) * \details * deferred(0), * immediate(1), * combined(2) */ u_int8_t mode; /*! \brief TRUE if NumberOfMessages present */ u_int8_t number_of_messages_present; /*! \brief TRUE if time present */ u_int8_t time_present; /*! \brief TRUE if MessageId present */ u_int8_t message_id_present; /*! \brief TRUE if invocation mode present */ u_int8_t mode_present; }; /* * ARGUMENT SEQUENCE { * receivingUserNr PartyNumber, * basicService BasicService, * controllingUserNr PartyNumber OPTIONAL, * mode InvocationMode OPTIONAL * } */ struct roseEtsiMWIDeactivate_ARG { /*! \brief Receiving user number (Who the message is for.) */ struct rosePartyNumber receiving_user_number; /*! \brief Controlling user number (Mailbox number) (Optional) */ struct rosePartyNumber controlling_user_number; /*! * \brief Type of call leaving message. * \details * allServices(0), * speech(1), * unrestrictedDigitalInformation(2), * audio3k1Hz(3), * unrestrictedDigitalInformationWithTonesAndAnnouncements(4), * multirate(5), * telephony3k1Hz(32), * teletex(33), * telefaxGroup4Class1(34), * videotexSyntaxBased(35), * videotelephony(36), * telefaxGroup2-3(37), * telephony7kHz(38), * euroFileTransfer(39), * fileTransferAndAccessManagement(40), * videoconference(41), * audioGraphicConference(42) */ u_int8_t basic_service; /*! * \brief Invocation mode (When it should be delivered.) (Optional) * \details * deferred(0), * immediate(1), * combined(2) */ u_int8_t mode; /*! \brief TRUE if invocation mode present */ u_int8_t mode_present; }; /* * ARGUMENT SEQUENCE { * controllingUserNr [1] EXPLICIT PartyNumber OPTIONAL, * basicService [2] EXPLICIT BasicService OPTIONAL, * numberOfMessages [3] EXPLICIT MessageCounter OPTIONAL, * controllingUserProvidedNr [4] EXPLICIT PartyNumber OPTIONAL, * time [5] EXPLICIT GeneralizedTime OPTIONAL, * messageId [6] EXPLICIT MessageID OPTIONAL * } */ struct roseEtsiMWIIndicate_ARG { /*! \brief Number of messages in mailbox. (INTEGER (0..65535)) (Optional) */ u_int16_t number_of_messages; /*! \brief Message ID (Status of this message) (Optional)*/ struct roseEtsiMessageID message_id; /*! \brief Controlling user number (Mailbox number) (Optional) */ struct rosePartyNumber controlling_user_number; /*! \brief Controlling user provided number (Caller-ID of party leaving message) (Optional) */ struct rosePartyNumber controlling_user_provided_number; /*! \brief When message left. (optional) */ struct roseGeneralizedTime time; /*! * \brief Type of call leaving message. * \details * allServices(0), * speech(1), * unrestrictedDigitalInformation(2), * audio3k1Hz(3), * unrestrictedDigitalInformationWithTonesAndAnnouncements(4), * multirate(5), * telephony3k1Hz(32), * teletex(33), * telefaxGroup4Class1(34), * videotexSyntaxBased(35), * videotelephony(36), * telefaxGroup2-3(37), * telephony7kHz(38), * euroFileTransfer(39), * fileTransferAndAccessManagement(40), * videoconference(41), * audioGraphicConference(42) */ u_int8_t basic_service; /*! \brief TRUE if basic_service present */ u_int8_t basic_service_present; /*! \brief TRUE if NumberOfMessages present */ u_int8_t number_of_messages_present; /*! \brief TRUE if time present */ u_int8_t time_present; /*! \brief TRUE if MessageId present */ u_int8_t message_id_present; }; /* ------------------------------------------------------------------- */ /* * Name ::= CHOICE { * -- iso8859-1 is implied in namePresentationAllowedSimple. * namePresentationAllowedSimple [0] IMPLICIT NameData, * namePresentationAllowedExtended [1] IMPLICIT NameSet, * * -- iso8859-1 is implied in namePresentationRestrictedSimple. * namePresentationRestrictedSimple [2] IMPLICIT NameData, * namePresentationRestrictedExtended [3] IMPLICIT NameSet, * * -- namePresentationRestrictedNull shall only be used in the * -- case of interworking where the other network provides an * -- indication that the name is restricted without the name itself. * namePresentationRestrictedNull [7] IMPLICIT NULL, * * nameNotAvailable [4] IMPLICIT NULL * } * * NameSet ::= SEQUENCE { * nameData NameData, * * -- If characterSet is not included, iso8859-1 is implied. * characterSet CharacterSet OPTIONAL -- DEFAULT iso8859-1 * } * * -- The maximum allowed size of the name field is 50 octets. * -- The minimum required size of the name field is 1 octet. * NameData ::= OCTET STRING (SIZE (1..50)) */ struct roseQsigName { /*! * \details * optional_name_not_present(0), * presentation_allowed(1), * presentation_restricted(2), * presentation_restricted_null(3), * name_not_available(4) */ u_int8_t presentation; /*! * \details * Set to iso8859-1 if not present in the encoding. * * \details * unknown(0), * iso8859-1(1), * enum-value-withdrawn-by-ITU-T(2) * iso8859-2(3), * iso8859-3(4), * iso8859-4(5), * iso8859-5(6), * iso8859-7(7), * iso10646-BmpString(8), * iso10646-utf-8String(9) */ u_int8_t char_set; /*! \brief Length of name data */ u_int8_t length; /*! \brief Name string data */ unsigned char data[50 + 1]; }; /* * NOTE: We are not going to record the Extension information * since it is manufacturer specific. * * ARGUMENT CHOICE { * Name, * SEQUENCE { * Name, * CHOICE { * [5] IMPLICIT Extension, * [6] IMPLICIT SEQUENCE OF Extension * } OPTIONAL * } * } */ struct roseQsigPartyName_ARG { struct roseQsigName name; }; /* ------------------------------------------------------------------- */ /* * Time ::= SEQUENCE { * lengthOfTimeUnit [1] IMPLICIT LengthOfTimeUnit, * scale [2] IMPLICIT Scale * } */ struct roseQsigAOCTime { /*! LengthOfTimeUnit ::= INTEGER (0..16777215) -- 24 bit number */ u_int32_t length; /*! * \details * oneHundredthSecond(0), * oneTenthSecond(1), * oneSecond(2), * tenSeconds(3), * oneMinute(4), * oneHour(5), * twentyFourHours(6) */ u_int8_t scale; }; /* * Amount ::= SEQUENCE { * currencyAmount [1] IMPLICIT CurrencyAmount, * multiplier [2] IMPLICIT Multiplier * } */ struct roseQsigAOCAmount { /*! CurrencyAmount ::= INTEGER (0..16777215) -- 24 bit number */ u_int32_t currency; /*! * \details * oneThousandth(0), * oneHundredth(1), * oneTenth(2), * one(3), * ten(4), * hundred(5), * thousand(6) */ u_int8_t multiplier; }; /* * DurationCurrency ::= SEQUENCE { * dCurrency [1] IMPLICIT Currency, * dAmount [2] IMPLICIT Amount, * dChargingType [3] IMPLICIT ChargingType, * dTime [4] IMPLICIT Time, * dGranularity [5] IMPLICIT Time OPTIONAL * } */ struct roseQsigAOCDurationCurrency { struct roseQsigAOCAmount amount; struct roseQsigAOCTime time; /*! \brief dGranularity (optional) */ struct roseQsigAOCTime granularity; /*! * \brief Name of currency * \details * Currency ::= IA5String (SIZE (0..10)) * \note The empty string is the default currency for the network. */ unsigned char currency[10 + 1]; /*! * \details * continuousCharging(0), * stepFunction(1) */ u_int8_t charging_type; /*! TRUE if granularity time is present */ u_int8_t granularity_present; }; /* * FlatRateCurrency ::= SEQUENCE { * fRCurrency [1] IMPLICIT Currency, * fRAmount [2] IMPLICIT Amount * } */ struct roseQsigAOCFlatRateCurrency { struct roseQsigAOCAmount amount; /*! * \brief Name of currency * \details * Currency ::= IA5String (SIZE (0..10)) * \note The empty string is the default currency for the network. */ unsigned char currency[10 + 1]; }; /* * VolumeRateCurrency ::= SEQUENCE { * vRCurrency [1] IMPLICIT Currency, * vRAmount [2] IMPLICIT Amount, * vRVolumeUnit [3] IMPLICIT VolumeUnit * } */ struct roseQsigAOCVolumeRateCurrency { struct roseQsigAOCAmount amount; /*! * \brief Name of currency * \details * Currency ::= IA5String (SIZE (0..10)) * \note The empty string is the default currency for the network. */ unsigned char currency[10 + 1]; /*! * \brief Volume rate volume unit * \details * octet(0), * segment(1), * message(2) */ u_int8_t unit; }; /* * AOCSCurrencyInfo ::= SEQUENCE { * chargedItem ChargedItem, * rateType CHOICE { * durationCurrency [1] IMPLICIT DurationCurrency, * flatRateCurrency [2] IMPLICIT FlatRateCurrency, * volumeRateCurrency [3] IMPLICIT VolumeRateCurrency, * specialChargingCode SpecialChargingCode, * freeOfCharge [4] IMPLICIT NULL, * currencyInfoNotAvailable [5] IMPLICIT NULL, * freeOfChargefromBeginning [6] IMPLICIT NULL * } * } */ struct roseQsigAOCSCurrencyInfo { union { struct roseQsigAOCDurationCurrency duration; struct roseQsigAOCFlatRateCurrency flat_rate; struct roseQsigAOCVolumeRateCurrency volume_rate; /*! SpecialChargingCode ::= INTEGER (1..10) */ u_int8_t special_charging_code; } u; /*! * \brief Determine what is stored in the union. * \details * specialChargingCode(0), * durationCurrency(1), * flatRateCurrency(2), * volumeRateCurrency(3), * freeOfCharge(4), * currencyInfoNotAvailable(5), * freeOfChargeFromBeginning(6) */ u_int8_t currency_type; /*! * \brief What service is being billed. * \details * basicCommunication(0), * callAttempt(1), * callSetup(2), * userToUserInfo(3), * operationOfSupplementaryServ(4) */ u_int8_t charged_item; }; /* * AOCSCurrencyInfoList ::= SEQUENCE SIZE (1..10) OF AOCSCurrencyInfo */ struct roseQsigAOCSCurrencyInfoList { /*! \brief SEQUENCE SIZE (1..10) OF AOCSCurrencyInfo */ struct roseQsigAOCSCurrencyInfo list[10]; /*! \brief Number of AOCSCurrencyInfo records present */ u_int8_t num_records; }; /* * RecordedCurrency ::= SEQUENCE { * rCurrency [1] IMPLICIT Currency, * rAmount [2] IMPLICIT Amount * } */ struct roseQsigAOCRecordedCurrency { /*! Amount of currency involved. */ struct roseQsigAOCAmount amount; /*! * \brief Name of currency * \details * Currency ::= IA5String (SIZE (0..10)) * \note The empty string is the default currency for the network. */ unsigned char currency[10 + 1]; }; /* * ChargingAssociation ::= CHOICE { * chargedNumber [0] EXPLICIT PartyNumber, * chargeIdentifier ChargeIdentifier * } */ struct roseQsigAOCChargingAssociation { /*! chargeIdentifier: INTEGER (-32768..32767) -- 16 bit number */ int16_t id; /*! chargedNumber */ struct rosePartyNumber number; /*! * \details * charge_identifier(0) * charged_number(1), */ u_int8_t type; }; /* * AocRateArg ::= SEQUENCE { * aocRate CHOICE * { * chargeNotAvailable NULL, * aocSCurrencyInfoList AOCSCurrencyInfoList * }, * rateArgExtension CHOICE { * extension [1] IMPLICIT Extension, * multipleExtension [2] IMPLICIT SEQUENCE OF Extension * } OPTIONAL * } */ struct roseQsigAocRateArg_ARG { struct roseQsigAOCSCurrencyInfoList currency_info; /*! * \details * charge_not_available(0), * currency_info_list(1) */ u_int8_t type; }; /* * AocInterimArg ::= SEQUENCE { * interimCharge CHOICE { * chargeNotAvailable [0] IMPLICIT NULL, * freeOfCharge [1] IMPLICIT NULL, * specificCurrency SEQUENCE { * recordedCurrency [1] IMPLICIT RecordedCurrency, * interimBillingId [2] IMPLICIT InterimBillingId OPTIONAL * } * }, * interimArgExtension CHOICE { * extension [1] IMPLICIT Extension, * multipleExtension [2] IMPLICIT SEQUENCE OF Extension * } OPTIONAL * } */ struct roseQsigAocInterimArg_ARG { struct { /*! \brief recorded currency */ struct roseQsigAOCRecordedCurrency recorded; /*! * \brief InterimBillingId (optional) * \details * normalCharging(0), * creditCardCharging(2) */ u_int8_t billing_id; /*! \brief TRUE if billing id is present */ u_int8_t billing_id_present; } specific; /*! * \details * charge_not_available(0), * free_of_charge(1), * specific_currency(2) */ u_int8_t type; }; /* * AocFinalArg ::= SEQUENCE { * finalCharge CHOICE { * chargeNotAvailable [0] IMPLICIT NULL, * freeOfCharge [1] IMPLICIT NULL, * specificCurrency SEQUENCE { * recordedCurrency [1] IMPLICIT RecordedCurrency, * finalBillingId [2] IMPLICIT FinalBillingId OPTIONAL * } * }, * chargingAssociation ChargingAssociation OPTIONAL, * finalArgExtension CHOICE { * extension [1] IMPLICIT Extension, * multipleExtension [2] IMPLICIT SEQUENCE OF Extension * } OPTIONAL * } */ struct roseQsigAocFinalArg_ARG { struct { /*! \brief recorded currency */ struct roseQsigAOCRecordedCurrency recorded; /*! * \brief FinalBillingId (optional) * \details * normalCharging(0), * creditCardCharging(2), * callForwardingUnconditional(3), * callForwardingBusy(4), * callForwardingNoReply(5), * callDeflection(6), * callTransfer(7) */ u_int8_t billing_id; /*! \brief TRUE if billing id is present */ u_int8_t billing_id_present; } specific; /*! \brief chargingAssociation (optional) */ struct roseQsigAOCChargingAssociation charging_association; /*! \brief TRUE if charging_association is present */ u_int8_t charging_association_present; /*! * \details * charge_not_available(0), * free_of_charge(1), * specific_currency(2) */ u_int8_t type; }; /* * ChargeRequestArg ::= SEQUENCE { * adviceModeCombinations SEQUENCE SIZE(0..7) OF AdviceModeCombination, * chargeReqArgExtension CHOICE { * extension [1] IMPLICIT Extension, * multipleExtension [2] IMPLICIT SEQUENCE OF Extension * } OPTIONAL * } */ struct roseQsigChargeRequestArg_ARG { /*! * \brief SEQUENCE SIZE(0..7) OF AdviceModeCombination * \details * rate(0) , * rateInterim(1) , * rateFinal(2) , * interim(3) , * final(4) , * interimFinal(5) , * rateInterimFinal(6) */ u_int8_t advice_mode_combinations[7]; /*! \brief Number of AdviceModeCombination values present */ u_int8_t num_records; }; /* * ChargeRequestRes ::= SEQUENCE { * adviceModeCombination AdviceModeCombination, * chargeReqResExtension CHOICE { * extension [1] IMPLICIT Extension, * multipleExtension [2] IMPLICIT SEQUENCE OF Extension * } OPTIONAL * } */ struct roseQsigChargeRequestRes_RES { /*! * \details * rate(0) , * rateInterim(1) , * rateFinal(2) , * interim(3) , * final(4) , * interimFinal(5) , * rateInterimFinal(6) */ u_int8_t advice_mode_combination; }; /* * AocCompleteArg ::= SEQUENCE { * chargedUser PartyNumber, * chargingAssociation ChargingAssociation OPTIONAL, * completeArgExtension CHOICE { * extension [1] IMPLICIT Extension, * multipleExtension [2] IMPLICIT SEQUENCE OF Extension * } OPTIONAL * } */ struct roseQsigAocCompleteArg_ARG { /*! \brief chargingAssociation (optional) */ struct roseQsigAOCChargingAssociation charging_association; struct rosePartyNumber charged_user_number; /*! \brief TRUE if charging_association is present */ u_int8_t charging_association_present; }; /* * AocCompleteRes ::= SEQUENCE { * chargingOption ChargingOption, * completeResExtension CHOICE { * extension [1] IMPLICIT Extension, * multipleExtension [2] IMPLICIT SEQUENCE OF Extension * } OPTIONAL * } */ struct roseQsigAocCompleteRes_RES { /*! * \details * aocFreeOfCharge(0), * aocContinueCharging(1), * aocStopCharging(2) */ u_int8_t charging_option; }; /* * AocDivChargeReqArg ::= SEQUENCE { * divertingUser PartyNumber, * chargingAssociation ChargingAssociation OPTIONAL, * diversionType DiversionType, * aocDivChargeReqArgExt CHOICE { * extension [1] IMPLICIT Extension, * multipleExtension [2] IMPLICIT SEQUENCE OF Extension * } OPTIONAL * } */ struct roseQsigAocDivChargeReqArg_ARG { /*! \brief chargingAssociation (optional) */ struct roseQsigAOCChargingAssociation charging_association; struct rosePartyNumber diverting_user_number; /*! \brief TRUE if charging_association is present */ u_int8_t charging_association_present; /*! * \details * callForwardingUnconditional(0), * callForwardingBusy(1), * callForwardingNoReply(2), * callDeflection(3) */ u_int8_t diversion_type; }; /* ------------------------------------------------------------------- */ /* * CTIdentifyRes ::= SEQUENCE { * callIdentity CallIdentity, * reroutingNumber PartyNumber, * resultExtension CHOICE { * [6] IMPLICIT Extension, * [7] IMPLICIT SEQUENCE OF Extension * } OPTIONAL * } */ struct roseQsigCTIdentifyRes_RES { struct rosePartyNumber rerouting_number; /*! \brief CallIdentity ::= NumericString (SIZE (1..4)) */ unsigned char call_id[4 + 1]; }; /* * CTInitiateArg ::= SEQUENCE { * callIdentity CallIdentity, * reroutingNumber PartyNumber, * argumentExtension CHOICE { * [6] IMPLICIT Extension, * [7] IMPLICIT SEQUENCE OF Extension * } OPTIONAL * } */ struct roseQsigCTInitiateArg_ARG { struct rosePartyNumber rerouting_number; /*! \brief CallIdentity ::= NumericString (SIZE (1..4)) */ unsigned char call_id[4 + 1]; }; /* * CTSetupArg ::= SEQUENCE { * callIdentity CallIdentity, * argumentExtension CHOICE { * [0] IMPLICIT Extension, * [1] IMPLICIT SEQUENCE OF Extension * } OPTIONAL * } */ struct roseQsigCTSetupArg_ARG { /*! \brief CallIdentity ::= NumericString (SIZE (1..4)) */ unsigned char call_id[4 + 1]; }; /* * CTActiveArg ::= SEQUENCE { * connectedAddress PresentedAddressScreened, * * -- ISO/IEC 11572 information elements Party * -- category and Progress indicator are conveyed * basicCallInfoElements PSS1InformationElement OPTIONAL, * connectedName Name OPTIONAL, * argumentExtension CHOICE { * [9] IMPLICIT Extension, * [10] IMPLICIT SEQUENCE OF Extension * } OPTIONAL * } */ struct roseQsigCTActiveArg_ARG { /*! \brief connectedAddress */ struct rosePresentedAddressScreened connected; /*! \brief basicCallInfoElements (optional) */ struct roseQ931ie q931ie; /*! \brief q931ie.contents "allocated" after the stucture. */ unsigned char q931ie_contents[ROSE_Q931_MAX_PROGRESS + 1]; /*! \brief connectedName (optional) */ struct roseQsigName connected_name; /*! \brief TRUE if connected_name is present */ u_int8_t connected_name_present; }; /* * CTCompleteArg ::= SEQUENCE { * endDesignation EndDesignation, * redirectionNumber PresentedNumberScreened, * * -- ISO/IEC 11572 information elements Party * -- category and Progress indicator are conveyed * basicCallInfoElements PSS1InformationElement OPTIONAL, * redirectionName Name OPTIONAL, * callStatus CallStatus DEFAULT answered, * argumentExtension CHOICE { * [9] IMPLICIT Extension, * [10] IMPLICIT SEQUENCE OF Extension * } OPTIONAL * } */ struct roseQsigCTCompleteArg_ARG { /*! \brief redirectionNumber */ struct rosePresentedNumberScreened redirection; /*! \brief basicCallInfoElements (optional) */ struct roseQ931ie q931ie; /*! \brief q931ie.contents "allocated" after the stucture. */ unsigned char q931ie_contents[ROSE_Q931_MAX_PROGRESS + 1]; /*! \brief redirectionName (optional) */ struct roseQsigName redirection_name; /*! \brief TRUE if redirection_name is present */ u_int8_t redirection_name_present; /*! * \brief endDesignation * \details * primaryEnd(0), * secondaryEnd(1) */ u_int8_t end_designation; /*! * \brief callStatus * \details * DEFAULT answered * * \details * answered(0), * alerting(1) */ u_int8_t call_status; }; /* * CTUpdateArg ::= SEQUENCE { * redirectionNumber PresentedNumberScreened, * redirectionName Name OPTIONAL, * * -- ISO/IEC 11572 information elements Party * -- category and Progress indicator are conveyed * basicCallInfoElements PSS1InformationElement OPTIONAL, * argumentExtension CHOICE { * [9] IMPLICIT Extension, * [10] IMPLICIT SEQUENCE OF Extension * } OPTIONAL * } */ struct roseQsigCTUpdateArg_ARG { /*! \brief redirectionNumber */ struct rosePresentedNumberScreened redirection; /*! \brief basicCallInfoElements (optional) */ struct roseQ931ie q931ie; /*! \brief q931ie.contents "allocated" after the stucture. */ unsigned char q931ie_contents[ROSE_Q931_MAX_PROGRESS + 1]; /*! \brief redirectionName (optional) */ struct roseQsigName redirection_name; /*! \brief TRUE if redirection_name is present */ u_int8_t redirection_name_present; }; /* * SubaddressTransferArg ::= SEQUENCE { * redirectionSubaddress PartySubaddress, * argumentExtension CHOICE { * [0] IMPLICIT Extension, * [1] IMPLICIT SEQUENCE OF Extension * } OPTIONAL * } */ struct roseQsigSubaddressTransferArg_ARG { struct rosePartySubaddress redirection_subaddress; }; /* ------------------------------------------------------------------- */ /* * IntResult ::= SEQUENCE { * servedUserNr PartyNumber, * basicService BasicService, * procedure Procedure, * divertedToAddress Address, * remoteEnabled BOOLEAN DEFAULT FALSE, * extension CHOICE { * [1] IMPLICIT Extension, * [2] IMPLICIT SEQUENCE OF Extension * } OPTIONAL * } */ struct roseQsigForwardingRecord { /*! \brief Diverted to address */ struct roseAddress diverted_to; struct rosePartyNumber served_user_number; /*! * \details * allServices(0), * speech(1), * unrestrictedDigitalInformation(2), * audio3100Hz(3), * telephony(32), * teletex(33), * telefaxGroup4Class1(34), * videotexSyntaxBased(35), * videotelephony(36) */ u_int8_t basic_service; /*! \details cfu(0), cfb(1), cfnr(2) */ u_int8_t procedure; /*! \brief remoteEnabled BOOLEAN DEFAULT FALSE */ u_int8_t remote_enabled; }; /* * roseQsigInterrogateDiversionQ_REQ * IntResultList ::= SET SIZE (0..29) OF IntResult */ struct roseQsigForwardingList { /*! * \brief SET SIZE (0..29) OF Forwarding Records * \note Reduced the size of the array to conserve * potential stack usage. */ struct roseQsigForwardingRecord list[10]; /*! \brief Number of Forwarding records present */ u_int8_t num_records; }; /* * ARGUMENT SEQUENCE { * procedure Procedure, * basicService BasicService, * divertedToAddress Address, * servedUserNr PartyNumber, * activatingUserNr PartyNumber, * extension CHOICE { * [1] IMPLICIT Extension, * [2] IMPLICIT SEQUENCE OF Extension * } OPTIONAL * } */ struct roseQsigActivateDiversionQ_ARG { /*! \brief divertedToAddress */ struct roseAddress diverted_to; struct rosePartyNumber served_user_number; struct rosePartyNumber activating_user_number; /*! \details cfu(0), cfb(1), cfnr(2) */ u_int8_t procedure; /*! * \details * allServices(0), * speech(1), * unrestrictedDigitalInformation(2), * audio3100Hz(3), * telephony(32), * teletex(33), * telefaxGroup4Class1(34), * videotexSyntaxBased(35), * videotelephony(36) */ u_int8_t basic_service; }; /* * ARGUMENT SEQUENCE { * procedure Procedure, * basicService BasicService, * servedUserNr PartyNumber, * deactivatingUserNr PartyNumber, * extension CHOICE { * [1] IMPLICIT Extension, * [2] IMPLICIT SEQUENCE OF Extension * } OPTIONAL * } */ struct roseQsigDeactivateDiversionQ_ARG { struct rosePartyNumber served_user_number; struct rosePartyNumber deactivating_user_number; /*! \details cfu(0), cfb(1), cfnr(2) */ u_int8_t procedure; /*! * \details * allServices(0), * speech(1), * unrestrictedDigitalInformation(2), * audio3100Hz(3), * telephony(32), * teletex(33), * telefaxGroup4Class1(34), * videotexSyntaxBased(35), * videotelephony(36) */ u_int8_t basic_service; }; /* * ARGUMENT SEQUENCE { * procedure Procedure, * basicService BasicService DEFAULT allServices, * servedUserNr PartyNumber, * interrogatingUserNr PartyNumber, * extension CHOICE { * [1] IMPLICIT Extension, * [2] IMPLICIT SEQUENCE OF Extension * } OPTIONAL * } */ struct roseQsigInterrogateDiversionQ_ARG { struct rosePartyNumber served_user_number; struct rosePartyNumber interrogating_user_number; /*! \details cfu(0), cfb(1), cfnr(2) */ u_int8_t procedure; /*! * \details * DEFAULT allServices * * \details * allServices(0), * speech(1), * unrestrictedDigitalInformation(2), * audio3100Hz(3), * telephony(32), * teletex(33), * telefaxGroup4Class1(34), * videotexSyntaxBased(35), * videotelephony(36) */ u_int8_t basic_service; }; /* * ARGUMENT SEQUENCE { * servedUserNr PartyNumber, * basicService BasicService, * divertedToNr PartyNumber, * extension CHOICE { * [1] IMPLICIT Extension, * [2] IMPLICIT SEQUENCE OF Extension * } OPTIONAL * } */ struct roseQsigCheckRestriction_ARG { struct rosePartyNumber served_user_number; struct rosePartyNumber diverted_to_number; /*! * \details * allServices(0), * speech(1), * unrestrictedDigitalInformation(2), * audio3100Hz(3), * telephony(32), * teletex(33), * telefaxGroup4Class1(34), * videotexSyntaxBased(35), * videotelephony(36) */ u_int8_t basic_service; }; /* * ARGUMENT SEQUENCE { * reroutingReason DiversionReason, * originalReroutingReason [0] IMPLICIT DiversionReason OPTIONAL, * calledAddress Address, * diversionCounter INTEGER (1..15), * * -- The basic call information elements Bearer capability, * -- High layer compatibility, Low layer compatibity * -- and Progress indicator can be embedded in the * -- pSS1InfoElement in accordance with 6.5.3.1.5. * pSS1InfoElement PSS1InformationElement, * lastReroutingNr [1] EXPLICIT PresentedNumberUnscreened, * subscriptionOption [2] IMPLICIT SubscriptionOption, * callingPartySubaddress [3] EXPLICIT PartySubaddress OPTIONAL, * callingNumber [4] EXPLICIT PresentedNumberScreened, * callingName [5] EXPLICIT Name OPTIONAL, * originalCalledNr [6] EXPLICIT PresentedNumberUnscreened OPTIONAL, * redirectingName [7] EXPLICIT Name OPTIONAL, * originalCalledName [8] EXPLICIT Name OPTIONAL, * extension CHOICE { * [9] IMPLICIT Extension, * [10] IMPLICIT SEQUENCE OF Extension * } OPTIONAL * } */ struct roseQsigCallRerouting_ARG { /*! \brief calledAddress */ struct roseAddress called; /*! \brief lastReroutingNr */ struct rosePresentedNumberUnscreened last_rerouting; /*! \brief originalCalledNr (optional) */ struct rosePresentedNumberUnscreened original_called; /*! * \brief callingPartySubaddress (optional) * The subaddress is present if the length is nonzero. */ struct rosePartySubaddress calling_subaddress; /*! \brief callingNumber */ struct rosePresentedNumberScreened calling; /*! \brief callingName (optional) */ struct roseQsigName calling_name; /*! \brief redirectingName (optional) */ struct roseQsigName redirecting_name; /*! \brief originalCalledName (optional) */ struct roseQsigName original_called_name; /*! * \brief The BC, HLC (optional), LLC (optional), * and progress indicator(s) 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 + ROSE_Q931_MAX_PROGRESS + 1]; /*! \brief TRUE if calling_name is present */ u_int8_t calling_name_present; /*! \brief TRUE if redirecting_name is present */ u_int8_t redirecting_name_present; /*! \brief TRUE if original_called_name is present */ u_int8_t original_called_name_present; /*! \brief TRUE if original_called number is present */ u_int8_t original_called_present; /*! * \details * unknown(0), * cfu(1), * cfb(2), * cfnr(3) * * \note The value unknown is only used if received from * another network when interworking. */ u_int8_t rerouting_reason; /*! * \brief originalReroutingReason (optional) * * \details * unknown(0), * cfu(1), * cfb(2), * cfnr(3) * * \note The value unknown is only used if received from * another network when interworking. */ u_int8_t original_rerouting_reason; /*! \brief TRUE if original_rerouting_reason is present */ u_int8_t original_rerouting_reason_present; /*! \brief diversionCounter INTEGER (1..15) */ u_int8_t diversion_counter; /*! * \brief subscriptionOption * * \details * noNotification(0), * notificationWithoutDivertedToNr(1), * notificationWithDivertedToNr(2) */ u_int8_t subscription_option; }; /* * ARGUMENT SEQUENCE { * diversionReason DiversionReason, * subscriptionOption SubscriptionOption, * nominatedNr PartyNumber, * extension CHOICE { * [9] IMPLICIT Extension, * [10] IMPLICIT SEQUENCE OF Extension * } OPTIONAL * } */ struct roseQsigDivertingLegInformation1_ARG { struct rosePartyNumber nominated_number; /*! * \details * unknown(0), * cfu(1), * cfb(2), * cfnr(3) * * \note The value unknown is only used if received from * another network when interworking. */ u_int8_t diversion_reason; /*! * \brief subscriptionOption * * \details * noNotification(0), * notificationWithoutDivertedToNr(1), * notificationWithDivertedToNr(2) */ u_int8_t subscription_option; }; /* * ARGUMENT SEQUENCE { * diversionCounter INTEGER (1..15), * diversionReason DiversionReason, * originalDiversionReason [0] IMPLICIT DiversionReason OPTIONAL, * * -- The divertingNr element is mandatory except in the case * -- of interworking. * divertingNr [1] EXPLICIT PresentedNumberUnscreened OPTIONAL, * originalCalledNr [2] EXPLICIT PresentedNumberUnscreened OPTIONAL, * redirectingName [3] EXPLICIT Name OPTIONAL, * originalCalledName [4] EXPLICIT Name OPTIONAL, * extension CHOICE { * [5] IMPLICIT Extension, * [6] IMPLICIT SEQUENCE OF Extension * } OPTIONAL * } */ struct roseQsigDivertingLegInformation2_ARG { /*! \brief divertingNr (optional) */ struct rosePresentedNumberUnscreened diverting; /*! \brief originalCalledNr (optional) */ struct rosePresentedNumberUnscreened original_called; /*! \brief redirectingName (optional) */ struct roseQsigName redirecting_name; /*! \brief originalCalledName (optional) */ struct roseQsigName original_called_name; /*! \brief diversionCounter INTEGER (1..15) */ u_int8_t diversion_counter; /*! * \details * unknown(0), * cfu(1), * cfb(2), * cfnr(3) * * \note The value unknown is only used if received from * another network when interworking. */ u_int8_t diversion_reason; /*! * \brief originalDiversionReason (optional) * * \details * unknown(0), * cfu(1), * cfb(2), * cfnr(3) * * \note The value unknown is only used if received from * another network when interworking. */ u_int8_t original_diversion_reason; /*! \brief TRUE if original_diversion_reason is present */ u_int8_t original_diversion_reason_present; /*! \brief TRUE if diverting number is present */ u_int8_t diverting_present; /*! \brief TRUE if original_called number is present */ u_int8_t original_called_present; /*! \brief TRUE if redirecting_name is present */ u_int8_t redirecting_name_present; /*! \brief TRUE if original_called_name is present */ u_int8_t original_called_name_present; }; /* * ARGUMENT SEQUENCE { * presentationAllowedIndicator PresentationAllowedIndicator, -- BOOLEAN * redirectionName [0] EXPLICIT Name OPTIONAL, * extension CHOICE { * [1] IMPLICIT Extension, * [2] IMPLICIT SEQUENCE OF Extension * } OPTIONAL * } */ struct roseQsigDivertingLegInformation3_ARG { /*! \brief redirectionName (optional) */ struct roseQsigName redirection_name; /*! \brief TRUE if redirection_name is present */ u_int8_t redirection_name_present; /*! \brief TRUE if presentation is allowed */ u_int8_t presentation_allowed_indicator; }; /* ------------------------------------------------------------------- */ /* * 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), * * -- The party number must be a complete number as required * -- for routing purposes. * partyNumber [1] EXPLICIT PartyNumber, * numericString [2] IMPLICIT NumericString (SIZE (1..10)) * } */ struct roseQsigMsgCentreId { union { /*! \brief INTEGER (0..65535) */ u_int16_t integer; /*! * \note The party number must be a complete number as required * for routing purposes. */ struct rosePartyNumber number; /*! \brief NumericString (SIZE (1..10)) */ unsigned char str[10 + 1]; } u; /*! * \details * integer(0), * partyNumber(1), * numericString(2) */ u_int8_t type; }; /* * MWIActivateArg ::= SEQUENCE { * servedUserNr PartyNumber, * basicService BasicService, * msgCentreId MsgCentreId OPTIONAL, * nbOfMessages [3] IMPLICIT NbOfMessages OPTIONAL, * originatingNr [4] EXPLICIT PartyNumber OPTIONAL, * timestamp TimeStamp OPTIONAL, * * -- The value 0 means the highest priority and 9 the lowest * priority [5] IMPLICIT INTEGER (0..9) OPTIONAL, * argumentExt CHOICE { * extension [6] IMPLICIT Extension, * multipleExtension [7] IMPLICIT SEQUENCE OF Extension * } OPTIONAL * } */ struct roseQsigMWIActivateArg { /*! \brief NbOfMessages ::= INTEGER (0..65535) (optional) */ u_int16_t number_of_messages; /*! \brief msgCentreId (optional) */ struct roseQsigMsgCentreId msg_centre_id; struct rosePartyNumber served_user_number; /*! \brief originatingNr (optional) (Number present if length is nonzero) */ struct rosePartyNumber originating_number; /*! \brief GeneralizedTime (SIZE (12..19)) (optional) */ struct roseGeneralizedTime timestamp; /*! * \details * allServices(0), * speech(1), * unrestrictedDigitalInformation(2), * audio3100Hz(3), * telephony(32), * teletex(33), * telefaxGroup4Class1(34), * videotextSyntaxBased(35), * videotelephony(36), * telefaxGroup2-3(37), * reservedNotUsed1(38), * reservedNotUsed2(39), * reservedNotUsed3(40), * reservedNotUsed4(41), * reservedNotUsed5(42), * email(51), * video(52), * fileTransfer(53), * shortMessageService(54), * speechAndVideo(55), * speechAndFax(56), * speechAndEmail(57), * videoAndFax(58), * videoAndEmail(59), * faxAndEmail(60), * speechVideoAndFax(61), * speechVideoAndEmail(62), * speechFaxAndEmail(63), * videoFaxAndEmail(64), * speechVideoFaxAndEmail(65), * multimediaUnknown(66), * serviceUnknown(67), * futureReserve1(68), * futureReserve2(69), * futureReserve3(70), * futureReserve4(71), * futureReserve5(72), * futureReserve6(73), * futureReserve7(74), * futureReserve8(75) */ u_int8_t basic_service; /*! * \brief INTEGER (0..9) (optional) * \note The value 0 means the highest priority and 9 the lowest. */ u_int8_t priority; /*! \brief TRUE if msg_centre_id is present */ u_int8_t msg_centre_id_present; /*! \brief TRUE if number_of_messages is present */ u_int8_t number_of_messages_present; /*! \brief TRUE if timestamp is present */ u_int8_t timestamp_present; /*! \brief TRUE if priority is present */ u_int8_t priority_present; }; /* * MWIDeactivateArg ::= SEQUENCE { * servedUserNr PartyNumber, * basicService BasicService, * msgCentreId MsgCentreId OPTIONAL, * argumentExt CHOICE { * extension [3] IMPLICIT Extension, * multipleExtension [4] IMPLICIT SEQUENCE OF Extension * } OPTIONAL * } */ struct roseQsigMWIDeactivateArg { /*! \brief msgCentreId (optional) */ struct roseQsigMsgCentreId msg_centre_id; struct rosePartyNumber served_user_number; /*! * \details * allServices(0), * speech(1), * unrestrictedDigitalInformation(2), * audio3100Hz(3), * telephony(32), * teletex(33), * telefaxGroup4Class1(34), * videotextSyntaxBased(35), * videotelephony(36), * telefaxGroup2-3(37), * reservedNotUsed1(38), * reservedNotUsed2(39), * reservedNotUsed3(40), * reservedNotUsed4(41), * reservedNotUsed5(42), * email(51), * video(52), * fileTransfer(53), * shortMessageService(54), * speechAndVideo(55), * speechAndFax(56), * speechAndEmail(57), * videoAndFax(58), * videoAndEmail(59), * faxAndEmail(60), * speechVideoAndFax(61), * speechVideoAndEmail(62), * speechFaxAndEmail(63), * videoFaxAndEmail(64), * speechVideoFaxAndEmail(65), * multimediaUnknown(66), * serviceUnknown(67), * futureReserve1(68), * futureReserve2(69), * futureReserve3(70), * futureReserve4(71), * futureReserve5(72), * futureReserve6(73), * futureReserve7(74), * futureReserve8(75) */ u_int8_t basic_service; /*! \brief TRUE if msg_centre_id is present */ u_int8_t msg_centre_id_present; }; /* * MWIInterrogateArg ::= SEQUENCE { * servedUserNr PartyNumber, * basicService BasicService, * msgCentreId MsgCentreId OPTIONAL, * argumentExt CHOICE { * extension [3] IMPLICIT Extension, * multipleExtension [4] IMPLICIT SEQUENCE OF Extension * } OPTIONAL * } */ struct roseQsigMWIInterrogateArg { /*! \brief msgCentreId (optional) */ struct roseQsigMsgCentreId msg_centre_id; struct rosePartyNumber served_user_number; /*! * \details * allServices(0), * speech(1), * unrestrictedDigitalInformation(2), * audio3100Hz(3), * telephony(32), * teletex(33), * telefaxGroup4Class1(34), * videotextSyntaxBased(35), * videotelephony(36), * telefaxGroup2-3(37), * reservedNotUsed1(38), * reservedNotUsed2(39), * reservedNotUsed3(40), * reservedNotUsed4(41), * reservedNotUsed5(42), * email(51), * video(52), * fileTransfer(53), * shortMessageService(54), * speechAndVideo(55), * speechAndFax(56), * speechAndEmail(57), * videoAndFax(58), * videoAndEmail(59), * faxAndEmail(60), * speechVideoAndFax(61), * speechVideoAndEmail(62), * speechFaxAndEmail(63), * videoFaxAndEmail(64), * speechVideoFaxAndEmail(65), * multimediaUnknown(66), * serviceUnknown(67), * futureReserve1(68), * futureReserve2(69), * futureReserve3(70), * futureReserve4(71), * futureReserve5(72), * futureReserve6(73), * futureReserve7(74), * futureReserve8(75) */ u_int8_t basic_service; /*! \brief TRUE if msg_centre_id is present */ u_int8_t msg_centre_id_present; }; /* * MWIInterrogateResElt ::= SEQUENCE { * basicService BasicService, * msgCentreId MsgCentreId OPTIONAL, * nbOfMessages [3] IMPLICIT NbOfMessages OPTIONAL, * originatingNr [4] EXPLICIT PartyNumber OPTIONAL, * timestamp TimeStamp OPTIONAL, * * -- The value 0 means the highest priority and 9 the lowest * priority [5] IMPLICIT INTEGER (0..9) OPTIONAL, * argumentExt CHOICE { * extension [6] IMPLICIT Extension, * multipleExtension [7] IMPLICIT SEQUENCE OF Extension * } OPTIONAL * } */ struct roseQsigMWIInterrogateResElt { /*! \brief NbOfMessages ::= INTEGER (0..65535) (optional) */ u_int16_t number_of_messages; /*! \brief msgCentreId (optional) */ struct roseQsigMsgCentreId msg_centre_id; /*! \brief originatingNr (optional) (Number present if length is nonzero) */ struct rosePartyNumber originating_number; /*! \brief GeneralizedTime (SIZE (12..19)) (optional) */ struct roseGeneralizedTime timestamp; /*! * \details * allServices(0), * speech(1), * unrestrictedDigitalInformation(2), * audio3100Hz(3), * telephony(32), * teletex(33), * telefaxGroup4Class1(34), * videotextSyntaxBased(35), * videotelephony(36), * telefaxGroup2-3(37), * reservedNotUsed1(38), * reservedNotUsed2(39), * reservedNotUsed3(40), * reservedNotUsed4(41), * reservedNotUsed5(42), * email(51), * video(52), * fileTransfer(53), * shortMessageService(54), * speechAndVideo(55), * speechAndFax(56), * speechAndEmail(57), * videoAndFax(58), * videoAndEmail(59), * faxAndEmail(60), * speechVideoAndFax(61), * speechVideoAndEmail(62), * speechFaxAndEmail(63), * videoFaxAndEmail(64), * speechVideoFaxAndEmail(65), * multimediaUnknown(66), * serviceUnknown(67), * futureReserve1(68), * futureReserve2(69), * futureReserve3(70), * futureReserve4(71), * futureReserve5(72), * futureReserve6(73), * futureReserve7(74), * futureReserve8(75) */ u_int8_t basic_service; /*! * \brief INTEGER (0..9) (optional) * \note The value 0 means the highest priority and 9 the lowest. */ u_int8_t priority; /*! \brief TRUE if msg_centre_id is present */ u_int8_t msg_centre_id_present; /*! \brief TRUE if number_of_messages is present */ u_int8_t number_of_messages_present; /*! \brief TRUE if timestamp is present */ u_int8_t timestamp_present; /*! \brief TRUE if priority is present */ u_int8_t priority_present; }; /* * MWIInterrogateRes ::= SEQUENCE SIZE(1..10) OF MWIInterrogateResElt */ struct roseQsigMWIInterrogateRes { /*! \brief SEQUENCE SIZE(1..10) OF MWIInterrogateResElt */ struct roseQsigMWIInterrogateResElt list[10]; /*! \brief Number of MWIInterrogateResElt records present */ u_int8_t num_records; }; /* ------------------------------------------------------------------- */ /* * Northern Telecom DMS-100 transfer ability result * * callId [0] IMPLICIT INTEGER (0..16777215) -- 24 bit number */ struct roseDms100RLTOperationInd_RES { /*! INTEGER (0..16777215) -- 24 bit number */ u_int32_t call_id; }; /* * Northern Telecom DMS-100 transfer invoke * * ARGUMENT SEQUENCE { * callId [0] IMPLICIT INTEGER (0..16777215), -- 24 bit number * reason [1] IMPLICIT INTEGER * } */ struct roseDms100RLTThirdParty_ARG { /*! INTEGER (0..16777215) -- 24 bit number */ u_int32_t call_id; /*! Reason for redirect */ u_int8_t reason; }; /* ------------------------------------------------------------------- */ /* ARGUMENT ENUMERATED */ struct roseNi2InformationFollowing_ARG { u_int8_t value; /*!< Unknown enumerated value */ }; /* * ARGUMENT SEQUENCE { * callReference INTEGER -- 16 bit number * } */ struct roseNi2InitiateTransfer_ARG { u_int16_t call_reference; }; /* ------------------------------------------------------------------- */ /*! \brief Facility ie invoke etsi messages with arguments. */ union rose_msg_invoke_etsi_args { /* ETSI Advice Of Charge (AOC) */ struct roseEtsiChargingRequest_ARG ChargingRequest; struct roseEtsiAOCSCurrency_ARG AOCSCurrency; struct roseEtsiAOCSSpecialArr_ARG AOCSSpecialArr; struct roseEtsiAOCDCurrency_ARG AOCDCurrency; struct roseEtsiAOCDChargingUnit_ARG AOCDChargingUnit; struct roseEtsiAOCECurrency_ARG AOCECurrency; struct roseEtsiAOCEChargingUnit_ARG AOCEChargingUnit; /* ETSI Call Diversion */ struct roseEtsiActivationDiversion_ARG ActivationDiversion; struct roseEtsiDeactivationDiversion_ARG DeactivationDiversion; struct roseEtsiActivationStatusNotificationDiv_ARG ActivationStatusNotificationDiv; struct roseEtsiDeactivationStatusNotificationDiv_ARG DeactivationStatusNotificationDiv; struct roseEtsiInterrogationDiversion_ARG InterrogationDiversion; struct roseEtsiDiversionInformation_ARG DiversionInformation; struct roseEtsiCallDeflection_ARG CallDeflection; struct roseEtsiCallRerouting_ARG CallRerouting; struct roseEtsiDivertingLegInformation1_ARG DivertingLegInformation1; struct roseEtsiDivertingLegInformation2_ARG DivertingLegInformation2; struct roseEtsiDivertingLegInformation3_ARG DivertingLegInformation3; /* ETSI Explicit Call Transfer (ECT) */ struct roseEtsiExplicitEctExecute_ARG ExplicitEctExecute; 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; /* ETSI Message Waiting Indication (MWI) */ struct roseEtsiMWIActivate_ARG MWIActivate; struct roseEtsiMWIDeactivate_ARG MWIDeactivate; struct roseEtsiMWIIndicate_ARG MWIIndicate; }; /*! \brief Facility ie result etsi messages with arguments. */ union rose_msg_result_etsi_args { /* ETSI Advice Of Charge (AOC) */ struct roseEtsiChargingRequest_RES ChargingRequest; /* ETSI Call Diversion */ struct roseEtsiForwardingList InterrogationDiversion; struct roseEtsiServedUserNumberList InterrogateServedUserNumbers; /* 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. */ union rose_msg_invoke_qsig_args { /* Q.SIG Name-Operations */ struct roseQsigPartyName_ARG CallingName; struct roseQsigPartyName_ARG CalledName; struct roseQsigPartyName_ARG ConnectedName; struct roseQsigPartyName_ARG BusyName; /* Q.SIG SS-AOC-Operations */ struct roseQsigChargeRequestArg_ARG ChargeRequest; struct roseQsigAocFinalArg_ARG AocFinal; struct roseQsigAocInterimArg_ARG AocInterim; struct roseQsigAocRateArg_ARG AocRate; struct roseQsigAocCompleteArg_ARG AocComplete; struct roseQsigAocDivChargeReqArg_ARG AocDivChargeReq; /* Q.SIG Call-Transfer-Operations */ struct roseQsigCTInitiateArg_ARG CallTransferInitiate; struct roseQsigCTSetupArg_ARG CallTransferSetup; struct roseQsigCTActiveArg_ARG CallTransferActive; struct roseQsigCTCompleteArg_ARG CallTransferComplete; struct roseQsigCTUpdateArg_ARG CallTransferUpdate; struct roseQsigSubaddressTransferArg_ARG SubaddressTransfer; /* Q.SIG Call-Diversion-Operations */ struct roseQsigActivateDiversionQ_ARG ActivateDiversionQ; struct roseQsigDeactivateDiversionQ_ARG DeactivateDiversionQ; struct roseQsigInterrogateDiversionQ_ARG InterrogateDiversionQ; struct roseQsigCheckRestriction_ARG CheckRestriction; struct roseQsigCallRerouting_ARG CallRerouting; struct roseQsigDivertingLegInformation1_ARG DivertingLegInformation1; 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; struct roseQsigMWIInterrogateArg MWIInterrogate; }; /*! \brief Facility ie result qsig messages with arguments. */ union rose_msg_result_qsig_args { /* Q.SIG SS-AOC-Operations */ struct roseQsigChargeRequestRes_RES ChargeRequest; struct roseQsigAocCompleteRes_RES AocComplete; /* Q.SIG Call-Transfer-Operations */ struct roseQsigCTIdentifyRes_RES CallTransferIdentify; /* 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; }; /*! \brief Facility ie invoke DMS-100 messages with arguments. */ union rose_msg_invoke_dms100_args { struct roseDms100RLTThirdParty_ARG RLT_ThirdParty; }; /*! \brief Facility ie result DMS-100 messages with arguments. */ union rose_msg_result_dms100_args { struct roseDms100RLTOperationInd_RES RLT_OperationInd; }; /*! \brief Facility ie invoke NI2 messages with arguments. */ union rose_msg_invoke_ni2_args { struct roseNi2InformationFollowing_ARG InformationFollowing; struct roseNi2InitiateTransfer_ARG InitiateTransfer; }; /*! \brief Facility ie result NI2 messages with arguments. */ union rose_msg_result_ni2_args { int dummy; /*!< place holder until there are results with parameters */ }; /*! \brief Facility ie invoke messages with arguments. */ union rose_msg_invoke_args { union rose_msg_invoke_etsi_args etsi; union rose_msg_invoke_qsig_args qsig; union rose_msg_invoke_dms100_args dms100; union rose_msg_invoke_ni2_args ni2; }; /*! \brief Facility ie result messages with arguments. */ union rose_msg_result_args { union rose_msg_result_etsi_args etsi; union rose_msg_result_qsig_args qsig; union rose_msg_result_dms100_args dms100; union rose_msg_result_ni2_args ni2; }; /*! \brief Facility ie error messages with parameters. */ union rose_msg_error_args { int dummy; /*!< place holder until there are errors with parameters */ }; struct rose_msg_invoke { /*! \brief Invoke ID (-32768..32767) */ int16_t invoke_id; /*! \brief Linked ID (-32768..32767) (optional) */ int16_t linked_id; /*! \brief library encoded operation-value */ enum rose_operation operation; /*! \brief TRUE if the Linked ID is present */ u_int8_t linked_id_present; union rose_msg_invoke_args args; }; struct rose_msg_result { /*! \brief Invoke ID (-32768..32767) */ int16_t invoke_id; /*! * \brief library encoded operation-value * \note Set to ROSE_None if the operation sequence is not present. * \note ETSI and Q.SIG imply that if a return result does not have * any arguments then you must rely upon the invokeId value to * distinguish between return results because the operation-value is * not present. */ enum rose_operation operation; union rose_msg_result_args args; }; struct rose_msg_error { /*! \brief Invoke ID (-32768..32767) */ int16_t invoke_id; /*! \brief library encoded error-value */ enum rose_error_code code; union rose_msg_error_args args; }; struct rose_msg_reject { /*! \brief Invoke ID (-32768..32767) (optional) */ int16_t invoke_id; /*! \brief TRUE if the Invoke ID is present */ u_int8_t invoke_id_present; /*! \brief library encoded problem-value */ enum rose_reject_code code; }; enum rose_component_type { ROSE_COMP_TYPE_INVALID, ROSE_COMP_TYPE_INVOKE, ROSE_COMP_TYPE_RESULT, ROSE_COMP_TYPE_ERROR, ROSE_COMP_TYPE_REJECT }; struct rose_message { /*! \brief invoke, result, error, reject */ enum rose_component_type type; union { struct rose_msg_invoke invoke; struct rose_msg_result result; struct rose_msg_error error; struct rose_msg_reject reject; } component; }; /* * NetworkFacilityExtension ::= [10] IMPLICIT SEQUENCE { * sourceEntity [0] IMPLICIT EntityType, * sourceEntityAddress [1] EXPLICIT AddressInformation OPTIONAL, * destinationEntity [2] IMPLICIT EntityType, * destinationEntityAddress [3] EXPLICIT AddressInformation OPTIONAL * } * * AddressInformation ::= PartyNumber */ struct facNetworkFacilityExtension { /*! \brief sourceEntityAddress (optional) (Number present if length is nonzero) */ struct rosePartyNumber source_number; /*! \brief destinationEntityAddress (optional) (Number present if length is nonzero) */ struct rosePartyNumber destination_number; /*! * \details * endPINX(0), * anyTypeOfPINX(1) */ u_int8_t source_entity; /*! * \details * endPINX(0), * anyTypeOfPINX(1) */ u_int8_t destination_entity; }; /* * The network extensions header is a sequence of the following components: * * nfe NetworkFacilityExtension OPTIONAL, * npp NetworkProtocolProfile OPTIONAL, * interpretation InterpretationApdu OPTIONAL * * NetworkProtocolProfile ::= [18] IMPLICIT INTEGER (0..254) * * InterpretationApdu ::= [11] IMPLICIT ENUMERATED { * discardAnyUnrecognisedInvokePdu(0), * * -- this value also applies to Call independent signalling connections * -- see clause 8.1.2 (ECMA-165) * clearCallIfAnyInvokePduNotRecognised(1), * * -- this coding is implied by the absence of an * -- interpretation APDU. * rejectAnyUnrecognisedInvokePdu(2) * } */ struct fac_extension_header { /*! \brief Network Facility Extension component */ struct facNetworkFacilityExtension nfe; /*! \brief Network Protocol Profile component */ u_int8_t npp; /*! * \brief interpretation component * * \details * discardAnyUnrecognisedInvokePdu(0), * clearCallIfAnyInvokePduNotRecognised(1), * rejectAnyUnrecognisedInvokePdu(2) */ u_int8_t interpretation; /*! \brief TRUE if nfe is present */ u_int8_t nfe_present; /*! \brief TRUE if npp is present */ u_int8_t npp_present; /*! \brief TRUE if interpretation is present */ u_int8_t interpretation_present; }; const char *rose_operation2str(enum rose_operation operation); const char *rose_error2str(enum rose_error_code code); const char *rose_reject2str(enum rose_reject_code code); unsigned char *rose_encode_invoke(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct rose_msg_invoke *msg); unsigned char *rose_encode_result(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct rose_msg_result *msg); unsigned char *rose_encode_error(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct rose_msg_error *msg); unsigned char *rose_encode_reject(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct rose_msg_reject *msg); unsigned char *rose_encode(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct rose_message *msg); const unsigned char *rose_decode(struct pri *ctrl, const unsigned char *pos, const unsigned char *end, struct rose_message *msg); unsigned char *fac_enc_extension_header(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct fac_extension_header *header); unsigned char *facility_encode_header(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct fac_extension_header *header); const unsigned char *fac_dec_extension_header(struct pri *ctrl, const unsigned char *pos, const unsigned char *end, struct fac_extension_header *header); const unsigned char *facility_decode_header(struct pri *ctrl, const unsigned char *pos, const unsigned char *end, struct fac_extension_header *header); void facility_decode_dump(struct pri *ctrl, const unsigned char *buf, size_t length); /* ------------------------------------------------------------------- */ #ifdef __cplusplus } #endif #endif /* _LIBPRI_ROSE_H */ /* ------------------------------------------------------------------- */ /* end rose.h */ libpri-1.4.14/pri_aoc.c0000644000000000000000000015343511460074301013347 0ustar rootroot/* * libpri: An implementation of Primary Rate ISDN * * Copyright (C) 2010 Digium, Inc. * * Richard Mudgett * David Vossel * * 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 Advice Of Charge (AOC) facility support. * * \author Richard Mudgett */ #include "compat.h" #include "libpri.h" #include "pri_internal.h" #include "pri_facility.h" /* ------------------------------------------------------------------- */ /*! * \internal * \brief Fill in the AOC subcmd amount from the ETSI amount. * * \param subcmd_amount AOC subcmd amount. * \param etsi_amount AOC ETSI amount. * * \return Nothing */ static void aoc_etsi_subcmd_amount(struct pri_aoc_amount *subcmd_amount, const struct roseEtsiAOCAmount *etsi_amount) { subcmd_amount->cost = etsi_amount->currency; subcmd_amount->multiplier = etsi_amount->multiplier; } /*! * \internal * \brief Fill in the ETSI amount from the AOC subcmd amount. * * \param subcmd_amount AOC subcmd amount. * \param etsi_amount AOC ETSI amount. * * \return Nothing */ static void aoc_enc_etsi_subcmd_amount(const struct pri_aoc_amount *subcmd_amount, struct roseEtsiAOCAmount *etsi_amount) { etsi_amount->currency = subcmd_amount->cost; etsi_amount->multiplier = subcmd_amount->multiplier; } /*! * \internal * \brief Fill in the AOC subcmd time from the ETSI time. * * \param subcmd_time AOC subcmd time. * \param etsi_time AOC ETSI time. * * \return Nothing */ static void aoc_etsi_subcmd_time(struct pri_aoc_time *subcmd_time, const struct roseEtsiAOCTime *etsi_time) { subcmd_time->length = etsi_time->length; subcmd_time->scale = etsi_time->scale; } /*! * \internal * \brief Fill in the ETSI Time from the AOC subcmd time. * * \param subcmd_time AOC subcmd time. * \param etsi_time AOC ETSI time. * * \return Nothing */ static void aoc_enc_etsi_subcmd_time(const struct pri_aoc_time *subcmd_time, struct roseEtsiAOCTime *etsi_time) { etsi_time->length = subcmd_time->length; etsi_time->scale = subcmd_time->scale; } /*! * \internal * \brief Fill in the AOC subcmd recorded currency from the ETSI recorded currency. * * \param subcmd_recorded AOC subcmd recorded currency. * \param etsi_recorded AOC ETSI recorded currency. * * \return Nothing */ static void aoc_etsi_subcmd_recorded_currency(struct pri_aoc_recorded_currency *subcmd_recorded, const struct roseEtsiAOCRecordedCurrency *etsi_recorded) { aoc_etsi_subcmd_amount(&subcmd_recorded->amount, &etsi_recorded->amount); libpri_copy_string(subcmd_recorded->currency, (char *) etsi_recorded->currency, sizeof(subcmd_recorded->currency)); } /*! * \internal * \brief Fill in the the ETSI recorded currency from the subcmd currency info * * \param subcmd_recorded AOC subcmd recorded currency. * \param etsi_recorded AOC ETSI recorded currency. * * \return Nothing */ static void aoc_enc_etsi_subcmd_recorded_currency(const struct pri_aoc_recorded_currency *subcmd_recorded, struct roseEtsiAOCRecordedCurrency *etsi_recorded) { aoc_enc_etsi_subcmd_amount(&subcmd_recorded->amount, &etsi_recorded->amount); libpri_copy_string((char *) etsi_recorded->currency, subcmd_recorded->currency, sizeof(etsi_recorded->currency)); } /*! * \internal * \brief Fill in the AOC subcmd recorded units from the ETSI recorded units. * * \param subcmd_recorded AOC subcmd recorded units list. * \param etsi_recorded AOC ETSI recorded units list. * * \return Nothing */ static void aoc_etsi_subcmd_recorded_units(struct pri_aoc_recorded_units *subcmd_recorded, const struct roseEtsiAOCRecordedUnitsList *etsi_recorded) { int idx; /* Fill in the itemized list of recorded units. */ for (idx = 0; idx < etsi_recorded->num_records && idx < ARRAY_LEN(subcmd_recorded->item); ++idx) { if (etsi_recorded->list[idx].not_available) { subcmd_recorded->item[idx].number = -1; } else { subcmd_recorded->item[idx].number = etsi_recorded->list[idx].number_of_units; } if (etsi_recorded->list[idx].type_of_unit_present) { subcmd_recorded->item[idx].type = etsi_recorded->list[idx].type_of_unit; } else { subcmd_recorded->item[idx].type = -1; } } subcmd_recorded->num_items = idx; } /*! * \internal * \brief Fill in the ETSI recorded units from the AOC subcmd recorded units. * * \param subcmd_recorded AOC subcmd recorded units list. * \param etsi_recorded AOC ETSI recorded units list. * * \return Nothing */ static void aoc_enc_etsi_subcmd_recorded_units(const struct pri_aoc_recorded_units *subcmd_recorded, struct roseEtsiAOCRecordedUnitsList *etsi_recorded) { int i; /* Fill in the itemized list of recorded units. */ for (i = 0; i < subcmd_recorded->num_items; i++) { if (subcmd_recorded->item[i].number >= 0) { etsi_recorded->list[i].number_of_units = subcmd_recorded->item[i].number; } else { etsi_recorded->list[i].not_available = 1; } if (subcmd_recorded->item[i].type > 0) { etsi_recorded->list[i].type_of_unit = subcmd_recorded->item[i].type; etsi_recorded->list[i].type_of_unit_present = 1; } } etsi_recorded->num_records = i; if (!etsi_recorded->num_records) { etsi_recorded->list[0].not_available = 1; etsi_recorded->list[0].type_of_unit_present = 0; etsi_recorded->num_records = 1; } } /*! * \brief Handle the ETSI ChargingRequest. * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Q.931 call leg. * \param invoke Decoded ROSE invoke message contents. * * \return Nothing */ void aoc_etsi_aoc_request(struct pri *ctrl, q931_call *call, const struct rose_msg_invoke *invoke) { struct pri_subcommand *subcmd; int request; if (!ctrl->aoc_support) { send_facility_error(ctrl, call, invoke->invoke_id, ROSE_ERROR_Gen_NotSubscribed); return; } switch (invoke->args.etsi.ChargingRequest.charging_case) { case 0:/* chargingInformationAtCallSetup */ request = PRI_AOC_REQUEST_S; break; case 1:/* chargingDuringACall */ request = PRI_AOC_REQUEST_D; break; case 2:/* chargingAtTheEndOfACall */ request = PRI_AOC_REQUEST_E; break; default: send_facility_error(ctrl, call, invoke->invoke_id, ROSE_ERROR_Gen_NotImplemented); return; } subcmd = q931_alloc_subcommand(ctrl); if (!subcmd) { send_facility_error(ctrl, call, invoke->invoke_id, ROSE_ERROR_Gen_NotAvailable); return; } subcmd->cmd = PRI_SUBCMD_AOC_CHARGING_REQ; subcmd->u.aoc_request.invoke_id = invoke->invoke_id; subcmd->u.aoc_request.charging_request = request; } /*! * \internal * \brief Fill in the AOC-S subcmd currency info list of chargeable items. * * \param aoc_s AOC-S info list of chargeable items. * \param info ETSI info list of chargeable items. * * \return Nothing */ static void aoc_etsi_subcmd_aoc_s_currency_info(struct pri_subcmd_aoc_s *aoc_s, const struct roseEtsiAOCSCurrencyInfoList *info) { int idx; /* Fill in the itemized list of chargeable items. */ for (idx = 0; idx < info->num_records && idx < ARRAY_LEN(aoc_s->item); ++idx) { /* What is being charged. */ switch (info->list[idx].charged_item) { case 0:/* basicCommunication */ aoc_s->item[idx].chargeable = PRI_AOC_CHARGED_ITEM_BASIC_COMMUNICATION; break; case 1:/* callAttempt */ aoc_s->item[idx].chargeable = PRI_AOC_CHARGED_ITEM_CALL_ATTEMPT; break; case 2:/* callSetup */ aoc_s->item[idx].chargeable = PRI_AOC_CHARGED_ITEM_CALL_SETUP; break; case 3:/* userToUserInfo */ aoc_s->item[idx].chargeable = PRI_AOC_CHARGED_ITEM_USER_USER_INFO; break; case 4:/* operationOfSupplementaryServ */ aoc_s->item[idx].chargeable = PRI_AOC_CHARGED_ITEM_SUPPLEMENTARY_SERVICE; break; default: aoc_s->item[idx].chargeable = PRI_AOC_CHARGED_ITEM_NOT_AVAILABLE; break; } /* Rate method being used. */ switch (info->list[idx].currency_type) { case 0:/* specialChargingCode */ aoc_s->item[idx].rate_type = PRI_AOC_RATE_TYPE_SPECIAL_CODE; aoc_s->item[idx].rate.special = info->list[idx].u.special_charging_code; break; case 1:/* durationCurrency */ aoc_s->item[idx].rate_type = PRI_AOC_RATE_TYPE_DURATION; aoc_etsi_subcmd_amount(&aoc_s->item[idx].rate.duration.amount, &info->list[idx].u.duration.amount); aoc_etsi_subcmd_time(&aoc_s->item[idx].rate.duration.time, &info->list[idx].u.duration.time); if (info->list[idx].u.duration.granularity_present) { aoc_etsi_subcmd_time(&aoc_s->item[idx].rate.duration.granularity, &info->list[idx].u.duration.granularity); } else { aoc_s->item[idx].rate.duration.granularity.length = 0; aoc_s->item[idx].rate.duration.granularity.scale = PRI_AOC_TIME_SCALE_HUNDREDTH_SECOND; } aoc_s->item[idx].rate.duration.charging_type = info->list[idx].u.duration.charging_type; libpri_copy_string(aoc_s->item[idx].rate.duration.currency, (char *) info->list[idx].u.duration.currency, sizeof(aoc_s->item[idx].rate.duration.currency)); break; case 2:/* flatRateCurrency */ aoc_s->item[idx].rate_type = PRI_AOC_RATE_TYPE_FLAT; aoc_etsi_subcmd_amount(&aoc_s->item[idx].rate.flat.amount, &info->list[idx].u.flat_rate.amount); libpri_copy_string(aoc_s->item[idx].rate.flat.currency, (char *) info->list[idx].u.flat_rate.currency, sizeof(aoc_s->item[idx].rate.flat.currency)); break; case 3:/* volumeRateCurrency */ aoc_s->item[idx].rate_type = PRI_AOC_RATE_TYPE_VOLUME; aoc_etsi_subcmd_amount(&aoc_s->item[idx].rate.volume.amount, &info->list[idx].u.volume_rate.amount); aoc_s->item[idx].rate.volume.unit = info->list[idx].u.volume_rate.unit; libpri_copy_string(aoc_s->item[idx].rate.volume.currency, (char *) info->list[idx].u.volume_rate.currency, sizeof(aoc_s->item[idx].rate.volume.currency)); break; case 4:/* freeOfCharge */ aoc_s->item[idx].rate_type = PRI_AOC_RATE_TYPE_FREE; break; default: case 5:/* currencyInfoNotAvailable */ aoc_s->item[idx].rate_type = PRI_AOC_RATE_TYPE_NOT_AVAILABLE; break; } } aoc_s->num_items = idx; } /*! * \internal * \brief Fill in the currency info list of chargeable items from a aoc_s subcmd * * \param aoc_s AOC-S info list of chargeable items. * \param info ETSI info list of chargeable items. * * \return Nothing */ static void enc_etsi_subcmd_aoc_s_currency_info(const struct pri_subcmd_aoc_s *aoc_s, struct roseEtsiAOCSCurrencyInfoList *info) { int idx; for (idx = 0; idx < aoc_s->num_items && idx < ARRAY_LEN(info->list); ++idx) { /* What is being charged. */ switch (aoc_s->item[idx].chargeable) { default: case PRI_AOC_CHARGED_ITEM_BASIC_COMMUNICATION: info->list[idx].charged_item = 0;/* basicCommunication */ break; case PRI_AOC_CHARGED_ITEM_CALL_ATTEMPT: info->list[idx].charged_item = 1;/* callAttempt */ break; case PRI_AOC_CHARGED_ITEM_CALL_SETUP: info->list[idx].charged_item = 2;/* callSetup */ break; case PRI_AOC_CHARGED_ITEM_USER_USER_INFO: info->list[idx].charged_item = 3;/* userToUserInfo */ break; case PRI_AOC_CHARGED_ITEM_SUPPLEMENTARY_SERVICE: info->list[idx].charged_item = 4;/* operationOfSupplementaryServ */ break; } /* Rate method being used. */ switch (aoc_s->item[idx].rate_type) { case PRI_AOC_RATE_TYPE_SPECIAL_CODE: info->list[idx].currency_type = 0;/* specialChargingCode */ info->list[idx].u.special_charging_code = aoc_s->item[idx].rate.special; break; case PRI_AOC_RATE_TYPE_DURATION: info->list[idx].currency_type = 1;/* durationCurrency */ aoc_enc_etsi_subcmd_amount(&aoc_s->item[idx].rate.duration.amount, &info->list[idx].u.duration.amount); aoc_enc_etsi_subcmd_time(&aoc_s->item[idx].rate.duration.time, &info->list[idx].u.duration.time); if (aoc_s->item[idx].rate.duration.granularity.length) { info->list[idx].u.duration.granularity_present = 1; aoc_enc_etsi_subcmd_time(&aoc_s->item[idx].rate.duration.granularity, &info->list[idx].u.duration.granularity); } else { info->list[idx].u.duration.granularity_present = 0; } info->list[idx].u.duration.charging_type = aoc_s->item[idx].rate.duration.charging_type; libpri_copy_string((char *) info->list[idx].u.duration.currency, aoc_s->item[idx].rate.duration.currency, sizeof((char *) info->list[idx].u.duration.currency)); break; case PRI_AOC_RATE_TYPE_FLAT: info->list[idx].currency_type = 2;/* flatRateCurrency */ aoc_enc_etsi_subcmd_amount(&aoc_s->item[idx].rate.flat.amount, &info->list[idx].u.flat_rate.amount); libpri_copy_string((char *) info->list[idx].u.flat_rate.currency, aoc_s->item[idx].rate.flat.currency, sizeof((char *) info->list[idx].u.flat_rate.currency)); break; case PRI_AOC_RATE_TYPE_VOLUME: info->list[idx].currency_type = 3;/* volumeRateCurrency */ aoc_enc_etsi_subcmd_amount(&aoc_s->item[idx].rate.volume.amount, &info->list[idx].u.volume_rate.amount); info->list[idx].u.volume_rate.unit = aoc_s->item[idx].rate.volume.unit; libpri_copy_string((char *) info->list[idx].u.volume_rate.currency, aoc_s->item[idx].rate.volume.currency, sizeof((char *) info->list[idx].u.volume_rate.currency)); break; case PRI_AOC_RATE_TYPE_FREE: info->list[idx].currency_type = 4;/* freeOfCharge */ break; default: case PRI_AOC_RATE_TYPE_NOT_AVAILABLE: info->list[idx].currency_type = 5;/* currencyInfoNotAvailable */ break; } } if (!idx) { /* We cannot send an empty list so create a dummy list element. */ info->list[idx].charged_item = 0;/* basicCommunication */ info->list[idx].currency_type = 5;/* currencyInfoNotAvailable */ ++idx; } info->num_records = idx; } /*! * \brief Handle the ETSI AOCSCurrency message. * * \param ctrl D channel controller for diagnostic messages or global options. * \param invoke Decoded ROSE invoke message contents. * * \return Nothing */ void aoc_etsi_aoc_s_currency(struct pri *ctrl, const struct rose_msg_invoke *invoke) { struct pri_subcommand *subcmd; if (!ctrl->aoc_support) { return; } subcmd = q931_alloc_subcommand(ctrl); if (!subcmd) { return; } subcmd->cmd = PRI_SUBCMD_AOC_S; if (!invoke->args.etsi.AOCSCurrency.type) { subcmd->u.aoc_s.num_items = 0; return; } /* Fill in the itemized list of chargeable items. */ aoc_etsi_subcmd_aoc_s_currency_info(&subcmd->u.aoc_s, &invoke->args.etsi.AOCSCurrency.currency_info); } /*! * \brief Handle the ETSI AOCSSpecialArr message. * * \param ctrl D channel controller for diagnostic messages or global options. * \param invoke Decoded ROSE invoke message contents. * * \return Nothing */ void aoc_etsi_aoc_s_special_arrangement(struct pri *ctrl, const struct rose_msg_invoke *invoke) { struct pri_subcommand *subcmd; if (!ctrl->aoc_support) { return; } subcmd = q931_alloc_subcommand(ctrl); if (!subcmd) { return; } subcmd->cmd = PRI_SUBCMD_AOC_S; if (!invoke->args.etsi.AOCSSpecialArr.type) { subcmd->u.aoc_s.num_items = 0; return; } subcmd->u.aoc_s.num_items = 1; subcmd->u.aoc_s.item[0].chargeable = PRI_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT; subcmd->u.aoc_s.item[0].rate_type = PRI_AOC_RATE_TYPE_SPECIAL_CODE; subcmd->u.aoc_s.item[0].rate.special = invoke->args.etsi.AOCSSpecialArr.special_arrangement; } /*! * \internal * \brief Determine the AOC-D subcmd billing_id value. * * \param billing_id_present TRUE if billing_id valid. * \param billing_id ETSI billing id from ROSE. * * \return enum PRI_AOC_D_BILLING_ID value */ static enum PRI_AOC_D_BILLING_ID aoc_etsi_subcmd_aoc_d_billing_id(int billing_id_present, int billing_id) { enum PRI_AOC_D_BILLING_ID value; if (billing_id_present) { switch (billing_id) { case 0:/* normalCharging */ value = PRI_AOC_D_BILLING_ID_NORMAL; break; case 1:/* reverseCharging */ value = PRI_AOC_D_BILLING_ID_REVERSE; break; case 2:/* creditCardCharging */ value = PRI_AOC_D_BILLING_ID_CREDIT_CARD; break; default: value = PRI_AOC_D_BILLING_ID_NOT_AVAILABLE; break; } } else { value = PRI_AOC_D_BILLING_ID_NOT_AVAILABLE; } return value; } /*! * \brief Handle the ETSI AOCDCurrency message. * * \param ctrl D channel controller for diagnostic messages or global options. * \param invoke Decoded ROSE invoke message contents. * * \return Nothing */ void aoc_etsi_aoc_d_currency(struct pri *ctrl, const struct rose_msg_invoke *invoke) { struct pri_subcommand *subcmd; if (!ctrl->aoc_support) { return; } subcmd = q931_alloc_subcommand(ctrl); if (!subcmd) { return; } subcmd->cmd = PRI_SUBCMD_AOC_D; switch (invoke->args.etsi.AOCDCurrency.type) { default: case 0:/* charge_not_available */ subcmd->u.aoc_d.charge = PRI_AOC_DE_CHARGE_NOT_AVAILABLE; break; case 1:/* free_of_charge */ subcmd->u.aoc_d.charge = PRI_AOC_DE_CHARGE_FREE; break; case 2:/* specific_currency */ subcmd->u.aoc_d.charge = PRI_AOC_DE_CHARGE_CURRENCY; aoc_etsi_subcmd_recorded_currency(&subcmd->u.aoc_d.recorded.money, &invoke->args.etsi.AOCDCurrency.specific.recorded); subcmd->u.aoc_d.billing_accumulation = invoke->args.etsi.AOCDCurrency.specific.type_of_charging_info; subcmd->u.aoc_d.billing_id = aoc_etsi_subcmd_aoc_d_billing_id( invoke->args.etsi.AOCDCurrency.specific.billing_id_present, invoke->args.etsi.AOCDCurrency.specific.billing_id); break; } } /*! * \brief Handle the ETSI AOCDChargingUnit message. * * \param ctrl D channel controller for diagnostic messages or global options. * \param invoke Decoded ROSE invoke message contents. * * \return Nothing */ void aoc_etsi_aoc_d_charging_unit(struct pri *ctrl, const struct rose_msg_invoke *invoke) { struct pri_subcommand *subcmd; if (!ctrl->aoc_support) { return; } subcmd = q931_alloc_subcommand(ctrl); if (!subcmd) { return; } subcmd->cmd = PRI_SUBCMD_AOC_D; switch (invoke->args.etsi.AOCDChargingUnit.type) { default: case 0:/* charge_not_available */ subcmd->u.aoc_d.charge = PRI_AOC_DE_CHARGE_NOT_AVAILABLE; break; case 1:/* free_of_charge */ subcmd->u.aoc_d.charge = PRI_AOC_DE_CHARGE_FREE; break; case 2:/* specific_charging_units */ subcmd->u.aoc_d.charge = PRI_AOC_DE_CHARGE_UNITS; aoc_etsi_subcmd_recorded_units(&subcmd->u.aoc_d.recorded.unit, &invoke->args.etsi.AOCDChargingUnit.specific.recorded); subcmd->u.aoc_d.billing_accumulation = invoke->args.etsi.AOCDChargingUnit.specific.type_of_charging_info; subcmd->u.aoc_d.billing_id = aoc_etsi_subcmd_aoc_d_billing_id( invoke->args.etsi.AOCDChargingUnit.specific.billing_id_present, invoke->args.etsi.AOCDChargingUnit.specific.billing_id); break; } } /*! * \internal * \brief Fill in the AOC-E subcmd charging association from the ETSI charging association. * * \param ctrl D channel controller for diagnostic messages or global options. * \param subcmd_association AOC-E subcmd charging association. * \param etsi_association AOC-E ETSI charging association. * * \return Nothing */ static void aoc_etsi_subcmd_aoc_e_charging_association(struct pri *ctrl, struct pri_aoc_e_charging_association *subcmd_association, const struct roseEtsiAOCChargingAssociation *etsi_association) { struct q931_party_number q931_number; switch (etsi_association->type) { case 0:/* charge_identifier */ subcmd_association->charging_type = PRI_AOC_E_CHARGING_ASSOCIATION_ID; subcmd_association->charge.id = etsi_association->id; break; case 1:/* charged_number */ subcmd_association->charging_type = PRI_AOC_E_CHARGING_ASSOCIATION_NUMBER; q931_party_number_init(&q931_number); rose_copy_number_to_q931(ctrl, &q931_number, &etsi_association->number); q931_party_number_copy_to_pri(&subcmd_association->charge.number, &q931_number); break; default: subcmd_association->charging_type = PRI_AOC_E_CHARGING_ASSOCIATION_NOT_AVAILABLE; break; } } /*! * \internal * \brief Determine the AOC-E subcmd billing_id value. * * \param billing_id_present TRUE if billing_id valid. * \param billing_id ETSI billing id from ROSE. * * \return enum PRI_AOC_E_BILLING_ID value */ static enum PRI_AOC_E_BILLING_ID aoc_etsi_subcmd_aoc_e_billing_id(int billing_id_present, int billing_id) { enum PRI_AOC_E_BILLING_ID value; if (billing_id_present) { switch (billing_id) { case 0:/* normalCharging */ value = PRI_AOC_E_BILLING_ID_NORMAL; break; case 1:/* reverseCharging */ value = PRI_AOC_E_BILLING_ID_REVERSE; break; case 2:/* creditCardCharging */ value = PRI_AOC_E_BILLING_ID_CREDIT_CARD; break; case 3:/* callForwardingUnconditional */ value = PRI_AOC_E_BILLING_ID_CALL_FORWARDING_UNCONDITIONAL; break; case 4:/* callForwardingBusy */ value = PRI_AOC_E_BILLING_ID_CALL_FORWARDING_BUSY; break; case 5:/* callForwardingNoReply */ value = PRI_AOC_E_BILLING_ID_CALL_FORWARDING_NO_REPLY; break; case 6:/* callDeflection */ value = PRI_AOC_E_BILLING_ID_CALL_DEFLECTION; break; case 7:/* callTransfer */ value = PRI_AOC_E_BILLING_ID_CALL_TRANSFER; break; default: value = PRI_AOC_E_BILLING_ID_NOT_AVAILABLE; break; } } else { value = PRI_AOC_E_BILLING_ID_NOT_AVAILABLE; } return value; } /*! * \internal * \brief Determine the ETSI AOC-E billing_id value from the subcmd. * * \param billing_id from upper layer. * * \retval -1 failure * \retval etsi billing id */ static int aoc_subcmd_aoc_e_etsi_billing_id(enum PRI_AOC_E_BILLING_ID billing_id) { switch (billing_id) { case PRI_AOC_E_BILLING_ID_NORMAL: return 0;/* normalCharging */ case PRI_AOC_E_BILLING_ID_REVERSE: return 1;/* reverseCharging */ case PRI_AOC_E_BILLING_ID_CREDIT_CARD: return 2;/* creditCardCharging */ case PRI_AOC_E_BILLING_ID_CALL_FORWARDING_UNCONDITIONAL: return 3;/* callForwardingUnconditional */ case PRI_AOC_E_BILLING_ID_CALL_FORWARDING_BUSY: return 4;/* callForwardingBusy */ case PRI_AOC_E_BILLING_ID_CALL_FORWARDING_NO_REPLY: return 5;/* callForwardingNoReply */ case PRI_AOC_E_BILLING_ID_CALL_DEFLECTION: return 6;/* callDeflection */ case PRI_AOC_E_BILLING_ID_CALL_TRANSFER: return 7;/* callTransfer */ case PRI_AOC_E_BILLING_ID_NOT_AVAILABLE: break; } return -1; } /*! * \internal * \brief Determine the ETSI AOC-D billing_id value from the subcmd. * * \param billing_id from upper layer. * * \retval -1 failure * \retval etsi billing id */ static int aoc_subcmd_aoc_d_etsi_billing_id(enum PRI_AOC_D_BILLING_ID billing_id) { switch (billing_id) { case PRI_AOC_D_BILLING_ID_NORMAL: return 0;/* normalCharging */ case PRI_AOC_D_BILLING_ID_REVERSE: return 1;/* reverseCharging */ case PRI_AOC_D_BILLING_ID_CREDIT_CARD: return 2;/* creditCardCharging */ case PRI_AOC_D_BILLING_ID_NOT_AVAILABLE: break; } return -1; } /*! * \brief Handle the ETSI AOCECurrency message. * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Q.931 call leg. * \param invoke Decoded ROSE invoke message contents. * * \return Nothing */ void aoc_etsi_aoc_e_currency(struct pri *ctrl, q931_call *call, const struct rose_msg_invoke *invoke) { struct pri_subcommand *subcmd; if (!ctrl->aoc_support) { return; } subcmd = q931_alloc_subcommand(ctrl); if (!subcmd) { return; } subcmd->cmd = PRI_SUBCMD_AOC_E; subcmd->u.aoc_e.associated.charging_type = PRI_AOC_E_CHARGING_ASSOCIATION_NOT_AVAILABLE; if (!invoke->args.etsi.AOCECurrency.type) { subcmd->u.aoc_e.charge = PRI_AOC_DE_CHARGE_NOT_AVAILABLE; return; } /* Fill in charging association if present. */ if (invoke->args.etsi.AOCECurrency.currency_info.charging_association_present) { aoc_etsi_subcmd_aoc_e_charging_association(ctrl, &subcmd->u.aoc_e.associated, &invoke->args.etsi.AOCECurrency.currency_info.charging_association); } /* Call was free of charge. */ if (invoke->args.etsi.AOCECurrency.currency_info.free_of_charge) { subcmd->u.aoc_e.charge = PRI_AOC_DE_CHARGE_FREE; return; } /* Fill in currency cost of call. */ subcmd->u.aoc_e.charge = PRI_AOC_DE_CHARGE_CURRENCY; aoc_etsi_subcmd_recorded_currency(&subcmd->u.aoc_e.recorded.money, &invoke->args.etsi.AOCECurrency.currency_info.specific.recorded); subcmd->u.aoc_e.billing_id = aoc_etsi_subcmd_aoc_e_billing_id( invoke->args.etsi.AOCECurrency.currency_info.specific.billing_id_present, invoke->args.etsi.AOCECurrency.currency_info.specific.billing_id); } /*! * \brief Handle the ETSI AOCEChargingUnit message. * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Q.931 call leg. * \param invoke Decoded ROSE invoke message contents. * * \return Nothing */ void aoc_etsi_aoc_e_charging_unit(struct pri *ctrl, q931_call *call, const struct rose_msg_invoke *invoke) { struct pri_subcommand *subcmd; unsigned idx; /* Fill in legacy stuff. */ call->aoc_units = 0; if (invoke->args.etsi.AOCEChargingUnit.type == 1 && !invoke->args.etsi.AOCEChargingUnit.charging_unit.free_of_charge) { for (idx = invoke->args.etsi.AOCEChargingUnit.charging_unit.specific.recorded.num_records; idx--;) { if (!invoke->args.etsi.AOCEChargingUnit.charging_unit.specific.recorded. list[idx].not_available) { call->aoc_units += invoke->args.etsi.AOCEChargingUnit.charging_unit.specific. recorded.list[idx].number_of_units; } } } if (!ctrl->aoc_support) { return; } subcmd = q931_alloc_subcommand(ctrl); if (!subcmd) { return; } subcmd->cmd = PRI_SUBCMD_AOC_E; subcmd->u.aoc_e.associated.charging_type = PRI_AOC_E_CHARGING_ASSOCIATION_NOT_AVAILABLE; if (!invoke->args.etsi.AOCEChargingUnit.type) { subcmd->u.aoc_e.charge = PRI_AOC_DE_CHARGE_NOT_AVAILABLE; return; } /* Fill in charging association if present. */ if (invoke->args.etsi.AOCEChargingUnit.charging_unit.charging_association_present) { aoc_etsi_subcmd_aoc_e_charging_association(ctrl, &subcmd->u.aoc_e.associated, &invoke->args.etsi.AOCEChargingUnit.charging_unit.charging_association); } /* Call was free of charge. */ if (invoke->args.etsi.AOCEChargingUnit.charging_unit.free_of_charge) { subcmd->u.aoc_e.charge = PRI_AOC_DE_CHARGE_FREE; return; } /* Fill in unit cost of call. */ subcmd->u.aoc_e.charge = PRI_AOC_DE_CHARGE_UNITS; aoc_etsi_subcmd_recorded_units(&subcmd->u.aoc_e.recorded.unit, &invoke->args.etsi.AOCEChargingUnit.charging_unit.specific.recorded); subcmd->u.aoc_e.billing_id = aoc_etsi_subcmd_aoc_e_billing_id( invoke->args.etsi.AOCEChargingUnit.charging_unit.specific.billing_id_present, invoke->args.etsi.AOCEChargingUnit.charging_unit.specific.billing_id); } void pri_aoc_events_enable(struct pri *ctrl, int enable) { if (ctrl) { ctrl->aoc_support = enable ? 1 : 0; } } /*! * \internal * \brief Encode the ETSI AOCECurrency invoke 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 aoc_e the AOC-E data to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_etsi_aoce_currency(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct pri_subcmd_aoc_e *aoc_e) { struct rose_msg_invoke msg; struct q931_party_number q931_number; pos = facility_encode_header(ctrl, pos, end, NULL); if (!pos) { return NULL; } memset(&msg, 0, sizeof(msg)); msg.operation = ROSE_ETSI_AOCECurrency; msg.invoke_id = get_invokeid(ctrl); if (aoc_e->charge == PRI_AOC_DE_CHARGE_FREE) { msg.args.etsi.AOCECurrency.type = 1; /* currency_info */ msg.args.etsi.AOCECurrency.currency_info.free_of_charge = 1; } else if ((aoc_e->charge == PRI_AOC_DE_CHARGE_CURRENCY) && (aoc_e->recorded.money.amount.cost >= 0)) { msg.args.etsi.AOCECurrency.type = 1; /* currency_info */ aoc_enc_etsi_subcmd_recorded_currency(&aoc_e->recorded.money, &msg.args.etsi.AOCECurrency.currency_info.specific.recorded); } else { msg.args.etsi.AOCECurrency.type = 0; /* charge_not_available */ } if (aoc_subcmd_aoc_e_etsi_billing_id(aoc_e->billing_id) != -1) { msg.args.etsi.AOCECurrency.currency_info.specific.billing_id_present = 1; msg.args.etsi.AOCECurrency.currency_info.specific.billing_id = aoc_subcmd_aoc_e_etsi_billing_id(aoc_e->billing_id); } switch (aoc_e->associated.charging_type) { case PRI_AOC_E_CHARGING_ASSOCIATION_NUMBER: msg.args.etsi.AOCECurrency.currency_info.charging_association_present = 1; msg.args.etsi.AOCECurrency.currency_info.charging_association.type = 1; /* charged_number */ pri_copy_party_number_to_q931(&q931_number, &aoc_e->associated.charge.number); q931_copy_number_to_rose(ctrl, &msg.args.etsi.AOCECurrency.currency_info.charging_association.number, &q931_number); break; case PRI_AOC_E_CHARGING_ASSOCIATION_ID: msg.args.etsi.AOCECurrency.currency_info.charging_association_present = 1; msg.args.etsi.AOCECurrency.currency_info.charging_association.type = 0; /* charge_identifier */ msg.args.etsi.AOCECurrency.currency_info.charging_association.id = aoc_e->associated.charge.id; break; default: /* do nothing */ break; } pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \internal * \brief Encode the ETSI AOCEChargingUnit invoke 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 aoc_e the AOC-E data to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_etsi_aoce_charging_unit(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct pri_subcmd_aoc_e *aoc_e) { struct rose_msg_invoke msg; struct q931_party_number q931_number; pos = facility_encode_header(ctrl, pos, end, NULL); if (!pos) { return NULL; } memset(&msg, 0, sizeof(msg)); msg.operation = ROSE_ETSI_AOCEChargingUnit; msg.invoke_id = get_invokeid(ctrl); if (aoc_e->charge == PRI_AOC_DE_CHARGE_FREE) { msg.args.etsi.AOCEChargingUnit.type = 1; /* charging_unit */ msg.args.etsi.AOCEChargingUnit.charging_unit.free_of_charge = 1; } else if ((aoc_e->charge == PRI_AOC_DE_CHARGE_UNITS) && (aoc_e->recorded.unit.num_items > 0)) { msg.args.etsi.AOCEChargingUnit.type = 1; /* charging_unit */ aoc_enc_etsi_subcmd_recorded_units(&aoc_e->recorded.unit, &msg.args.etsi.AOCEChargingUnit.charging_unit.specific.recorded); } else { msg.args.etsi.AOCEChargingUnit.type = 0; /* charge_not_available */ } if (aoc_subcmd_aoc_e_etsi_billing_id(aoc_e->billing_id) != -1) { msg.args.etsi.AOCEChargingUnit.charging_unit.specific.billing_id_present = 1; msg.args.etsi.AOCEChargingUnit.charging_unit.specific.billing_id = aoc_subcmd_aoc_e_etsi_billing_id(aoc_e->billing_id); } switch (aoc_e->associated.charging_type) { case PRI_AOC_E_CHARGING_ASSOCIATION_NUMBER: msg.args.etsi.AOCEChargingUnit.charging_unit.charging_association_present = 1; msg.args.etsi.AOCEChargingUnit.charging_unit.charging_association.type = 1; /* charged_number */ pri_copy_party_number_to_q931(&q931_number, &aoc_e->associated.charge.number); q931_copy_number_to_rose(ctrl, &msg.args.etsi.AOCEChargingUnit.charging_unit.charging_association.number, &q931_number); break; case PRI_AOC_E_CHARGING_ASSOCIATION_ID: msg.args.etsi.AOCEChargingUnit.charging_unit.charging_association_present = 1; msg.args.etsi.AOCEChargingUnit.charging_unit.charging_association.type = 0; /* charge_identifier */ msg.args.etsi.AOCEChargingUnit.charging_unit.charging_association.id = aoc_e->associated.charge.id; break; default: /* do nothing */ break; } pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \internal * \brief Encode the ETSI AOCDChargingUnit invoke 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 aoc_d the AOC-D data to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_etsi_aocd_charging_unit(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct pri_subcmd_aoc_d *aoc_d) { struct rose_msg_invoke msg; pos = facility_encode_header(ctrl, pos, end, NULL); if (!pos) { return NULL; } memset(&msg, 0, sizeof(msg)); msg.operation = ROSE_ETSI_AOCDChargingUnit; msg.invoke_id = get_invokeid(ctrl); if (aoc_d->charge == PRI_AOC_DE_CHARGE_FREE) { msg.args.etsi.AOCDChargingUnit.type = 1; /* free_of_charge */ } else if ((aoc_d->charge == PRI_AOC_DE_CHARGE_UNITS) && (aoc_d->recorded.unit.num_items > 0)) { msg.args.etsi.AOCDChargingUnit.type = 2; /* specific_charging_units */ aoc_enc_etsi_subcmd_recorded_units(&aoc_d->recorded.unit, &msg.args.etsi.AOCDChargingUnit.specific.recorded); } else { msg.args.etsi.AOCDChargingUnit.type = 0; /* charge_not_available */ } if (aoc_subcmd_aoc_d_etsi_billing_id(aoc_d->billing_id) != -1) { msg.args.etsi.AOCDChargingUnit.specific.billing_id_present = 1; msg.args.etsi.AOCDChargingUnit.specific.billing_id = aoc_subcmd_aoc_d_etsi_billing_id(aoc_d->billing_id); } pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \internal * \brief Encode the ETSI AOCDCurrency invoke 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 aoc_d the AOC-D data to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_etsi_aocd_currency(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct pri_subcmd_aoc_d *aoc_d) { struct rose_msg_invoke msg; pos = facility_encode_header(ctrl, pos, end, NULL); if (!pos) { return NULL; } memset(&msg, 0, sizeof(msg)); msg.operation = ROSE_ETSI_AOCDCurrency; msg.invoke_id = get_invokeid(ctrl); if (aoc_d->charge == PRI_AOC_DE_CHARGE_FREE) { msg.args.etsi.AOCDCurrency.type = 1; /* free_of_charge */ } else if ((aoc_d->charge == PRI_AOC_DE_CHARGE_CURRENCY) && (aoc_d->recorded.money.amount.cost >= 0)) { msg.args.etsi.AOCDCurrency.type = 2; /* specific_currency */ aoc_enc_etsi_subcmd_recorded_currency(&aoc_d->recorded.money, &msg.args.etsi.AOCDCurrency.specific.recorded); } else { msg.args.etsi.AOCDCurrency.type = 0; /* charge_not_available */ } if (aoc_subcmd_aoc_d_etsi_billing_id(aoc_d->billing_id) != -1) { msg.args.etsi.AOCDCurrency.specific.billing_id_present = 1; msg.args.etsi.AOCDCurrency.specific.billing_id = aoc_subcmd_aoc_d_etsi_billing_id(aoc_d->billing_id); } pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \internal * \brief Encode the ETSI AOCSSpecialArr invoke 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 aoc_s the AOC-S data to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_etsi_aocs_special_arrangement(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct pri_subcmd_aoc_s *aoc_s) { struct rose_msg_invoke msg; pos = facility_encode_header(ctrl, pos, end, NULL); if (!pos) { return NULL; } memset(&msg, 0, sizeof(msg)); msg.operation = ROSE_ETSI_AOCSSpecialArr; msg.invoke_id = get_invokeid(ctrl); if (!aoc_s->num_items || (aoc_s->item[0].rate_type != PRI_AOC_RATE_TYPE_SPECIAL_CODE)) { msg.args.etsi.AOCSSpecialArr.type = 0;/* charge_not_available */ } else { msg.args.etsi.AOCSSpecialArr.type = 1;/* special_arrangement_info */ msg.args.etsi.AOCSSpecialArr.special_arrangement = aoc_s->item[0].rate.special; } pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \internal * \brief Encode the ETSI AOCSCurrency invoke 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 aoc_s the AOC-S data to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_etsi_aocs_currency(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct pri_subcmd_aoc_s *aoc_s) { struct rose_msg_invoke msg; pos = facility_encode_header(ctrl, pos, end, NULL); if (!pos) { return NULL; } memset(&msg, 0, sizeof(msg)); msg.operation = ROSE_ETSI_AOCSCurrency; msg.invoke_id = get_invokeid(ctrl); if (aoc_s->num_items) { msg.args.etsi.AOCSCurrency.type = 1; /* currency_info_list */ enc_etsi_subcmd_aoc_s_currency_info(aoc_s, &msg.args.etsi.AOCSCurrency.currency_info); } else { msg.args.etsi.AOCSCurrency.type = 0; /* charge_not_available */ } pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \internal * \brief Encode the ETSI ChargingRequest Response 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 response the response to the request * \param invoke_id the request's invoke id * \param aoc_s the rate list associated with a response to AOC-S request * Could be NULL. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_etsi_aoc_request_response(struct pri *ctrl, unsigned char *pos, unsigned char *end, enum PRI_AOC_REQ_RSP response, int invoke_id, const struct pri_subcmd_aoc_s *aoc_s) { struct rose_msg_result msg_result = { 0, }; struct rose_msg_error msg_error = { 0, }; int is_error = 0; pos = facility_encode_header(ctrl, pos, end, NULL); if (!pos) { return NULL; } switch (response) { case PRI_AOC_REQ_RSP_CURRENCY_INFO_LIST: if (!aoc_s) { return NULL; } enc_etsi_subcmd_aoc_s_currency_info(aoc_s, &msg_result.args.etsi.ChargingRequest.u.currency_info); msg_result.args.etsi.ChargingRequest.type = 0;/* currency_info_list */ break; case PRI_AOC_REQ_RSP_SPECIAL_ARR: if (!aoc_s) { return NULL; } msg_result.args.etsi.ChargingRequest.type = 1;/* special_arrangement_info */ msg_result.args.etsi.ChargingRequest.u.special_arrangement = aoc_s->item[0].rate.special; break; case PRI_AOC_REQ_RSP_CHARGING_INFO_FOLLOWS: msg_result.args.etsi.ChargingRequest.type = 2;/* charging_info_follows */ break; case PRI_AOC_REQ_RSP_ERROR_NOT_IMPLEMENTED: msg_error.code = ROSE_ERROR_Gen_NotImplemented; is_error = 1; break; default: case PRI_AOC_REQ_RSP_ERROR_NOT_AVAILABLE: is_error = 1; msg_error.code = ROSE_ERROR_Gen_NotAvailable; break; } if (is_error) { msg_error.invoke_id = invoke_id; pos = rose_encode_error(ctrl, pos, end, &msg_error); } else { msg_result.operation = ROSE_ETSI_ChargingRequest; msg_result.invoke_id = invoke_id; pos = rose_encode_result(ctrl, pos, end, &msg_result); } return pos; } /*! * \internal * \brief Encode the ETSI ChargingRequest invoke 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 aoc_request the aoc charging request data to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_etsi_aoc_request(struct pri *ctrl, unsigned char *pos, unsigned char *end, enum PRI_AOC_REQUEST request) { struct rose_msg_invoke msg; pos = facility_encode_header(ctrl, pos, end, NULL); if (!pos) { return NULL; } memset(&msg, 0, sizeof(msg)); msg.operation = ROSE_ETSI_ChargingRequest; msg.invoke_id = get_invokeid(ctrl); switch (request) { case PRI_AOC_REQUEST_S: msg.args.etsi.ChargingRequest.charging_case = 0;/* chargingInformationAtCallSetup */ break; case PRI_AOC_REQUEST_D: msg.args.etsi.ChargingRequest.charging_case = 1;/* chargingDuringACall */ break; case PRI_AOC_REQUEST_E: msg.args.etsi.ChargingRequest.charging_case = 2;/* chargingAtTheEndOfACall */ break; default: /* no valid request parameters are present */ return NULL; } pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \internal * \brief Send the ETSI AOC Request Response message for an AOC-S request * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Call leg from which to encode AOC. * \param invoke_id the request's invoke id * \param aoc_s Optional AOC-S rate list for response * * \note if aoc_s is NULL, then a response will be sent back as AOC-S not available. * * \retval 0 on success. * \retval -1 on error. */ static int aoc_s_request_response_encode(struct pri *ctrl, q931_call *call, int invoke_id, const struct pri_subcmd_aoc_s *aoc_s) { unsigned char buffer[255]; unsigned char *end = NULL; int response; if (!aoc_s) { response = PRI_AOC_REQ_RSP_ERROR_NOT_AVAILABLE; } else if (aoc_s->num_items && aoc_s->item[0].chargeable == PRI_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT) { response = PRI_AOC_REQ_RSP_SPECIAL_ARR; } else { response = PRI_AOC_REQ_RSP_CURRENCY_INFO_LIST; } end = enc_etsi_aoc_request_response(ctrl, buffer, buffer + sizeof(buffer), response, invoke_id, aoc_s); if (!end) { return -1; } /* Remember that if we queue a facility IE for a facility message we * have to explicitly send the facility message ourselves */ if (pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL) || q931_facility(call->pri, call)) { pri_message(ctrl, "Could not schedule aoc request response facility message for call %d\n", call->cr); return -1; } return 0; } /*! * \internal * \brief Send the ETSI AOC Request Response message for AOC-D and AOC-E requests * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Call leg from which to encode AOC. * \param response the response to the request * \param invoke_id the request's invoke id * * \retval 0 on success. * \retval -1 on error. */ static int aoc_de_request_response_encode(struct pri *ctrl, q931_call *call, enum PRI_AOC_REQ_RSP response, int invoke_id) { unsigned char buffer[255]; unsigned char *end = NULL; end = enc_etsi_aoc_request_response(ctrl, buffer, buffer + sizeof(buffer), response, invoke_id, NULL); if (!end) { return -1; } /* Remember that if we queue a facility IE for a facility message we * have to explicitly send the facility message ourselves */ if (pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL) || q931_facility(call->pri, call)) { pri_message(ctrl, "Could not schedule aoc request response facility message for call %d\n", call->cr); return -1; } return 0; } /*! * \internal * \brief AOC-Request 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_aoc_request_get_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_subcommand *subcmd; if ((reason == APDU_CALLBACK_REASON_ERROR) || (reason == APDU_CALLBACK_REASON_CLEANUP)) { return 1; } subcmd = q931_alloc_subcommand(ctrl); if (!subcmd) { return 1; } memset(&subcmd->u.aoc_request_response, 0, sizeof(subcmd->u.aoc_request_response)); subcmd->u.aoc_request_response.charging_request = apdu->response.user.value; subcmd->cmd = PRI_SUBCMD_AOC_CHARGING_REQ_RSP; switch (reason) { case APDU_CALLBACK_REASON_MSG_ERROR: switch (msg->response.error->code) { case ROSE_ERROR_Gen_NotImplemented: subcmd->u.aoc_request_response.charging_response = PRI_AOC_REQ_RSP_ERROR_NOT_IMPLEMENTED; break; case ROSE_ERROR_Gen_NotAvailable: subcmd->u.aoc_request_response.charging_response = PRI_AOC_REQ_RSP_ERROR_NOT_AVAILABLE; break; default: subcmd->u.aoc_request_response.charging_response = PRI_AOC_REQ_RSP_ERROR; break; } break; case APDU_CALLBACK_REASON_MSG_REJECT: subcmd->u.aoc_request_response.charging_response = PRI_AOC_REQ_RSP_ERROR_REJECT; break; case APDU_CALLBACK_REASON_TIMEOUT: subcmd->u.aoc_request_response.charging_response = PRI_AOC_REQ_RSP_ERROR_TIMEOUT; break; case APDU_CALLBACK_REASON_MSG_RESULT: switch (msg->response.result->args.etsi.ChargingRequest.type) { case 0:/* currency_info_list */ subcmd->u.aoc_request_response.valid_aoc_s = 1; subcmd->u.aoc_request_response.charging_response = PRI_AOC_REQ_RSP_CURRENCY_INFO_LIST; aoc_etsi_subcmd_aoc_s_currency_info(&subcmd->u.aoc_request_response.aoc_s, &msg->response.result->args.etsi.ChargingRequest.u.currency_info); break; case 1:/* special_arrangement_info */ subcmd->u.aoc_request_response.valid_aoc_s = 1; subcmd->u.aoc_request_response.charging_response = PRI_AOC_REQ_RSP_SPECIAL_ARR; subcmd->u.aoc_request_response.aoc_s.num_items = 1; subcmd->u.aoc_request_response.aoc_s.item[0].chargeable = PRI_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT; subcmd->u.aoc_request_response.aoc_s.item[0].rate_type = PRI_AOC_RATE_TYPE_SPECIAL_CODE; subcmd->u.aoc_request_response.aoc_s.item[0].rate.special = msg->response.result->args.etsi.ChargingRequest.u.special_arrangement; break; case 2:/* charging_info_follows */ subcmd->u.aoc_request_response.charging_response = PRI_AOC_REQ_RSP_CHARGING_INFO_FOLLOWS; break; default: subcmd->u.aoc_request_response.charging_response = PRI_AOC_REQ_RSP_ERROR; break; } break; default: subcmd->u.aoc_request_response.charging_response = PRI_AOC_REQ_RSP_ERROR; break; } return 1; } /*! * \internal * \brief Send the ETSI AOC Request invoke message. * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Call leg from which to encode AOC. * \param aoc_request the aoc charging request payload data to encode. * * \retval 0 on success. * \retval -1 on error. */ static int aoc_charging_request_encode(struct pri *ctrl, q931_call *call, enum PRI_AOC_REQUEST request) { unsigned char buffer[255]; unsigned char *end = NULL; struct apdu_callback_data response; end = enc_etsi_aoc_request(ctrl, buffer, buffer + sizeof(buffer), request); if (!end) { return -1; } memset(&response, 0, sizeof(response)); response.invoke_id = ctrl->last_invoke; response.timeout_time = APDU_TIMEOUT_MSGS_ONLY; response.num_messages = 1; response.message_type[0] = Q931_CONNECT; response.callback = pri_aoc_request_get_response; response.user.value = request; /* in the case of an AOC request message, we queue this on a SETUP message and * do not have to send it ourselves in this function */ return pri_call_apdu_queue(call, Q931_SETUP, buffer, end - buffer, &response); } /*! * \internal * \brief Send the ETSI AOCS invoke message. * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Call leg from which to encode AOC. * \param aoc_s the AOC-S payload data to encode. * * \retval 0 on success. * \retval -1 on error. */ static int aoc_s_encode(struct pri *ctrl, q931_call *call, const struct pri_subcmd_aoc_s *aoc_s) { unsigned char buffer[255]; unsigned char *end = NULL; if (aoc_s->item[0].chargeable == PRI_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT) { end = enc_etsi_aocs_special_arrangement(ctrl, buffer, buffer + sizeof(buffer), aoc_s); } else { end = enc_etsi_aocs_currency(ctrl, buffer, buffer + sizeof(buffer), aoc_s); } if (!end) { return -1; } /* Remember that if we queue a facility IE for a facility message we * have to explicitly send the facility message ourselves */ if (pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL) || q931_facility(call->pri, call)) { pri_message(ctrl, "Could not schedule aoc-s facility message for call %d\n", call->cr); return -1; } return 0; } /*! * \internal * \brief Send the ETSI AOCD invoke message. * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Call leg from which to encode AOC. * \param aoc_d the AOC-D payload data to encode. * * \retval 0 on success. * \retval -1 on error. */ static int aoc_d_encode(struct pri *ctrl, q931_call *call, const struct pri_subcmd_aoc_d *aoc_d) { unsigned char buffer[255]; unsigned char *end = NULL; switch (aoc_d->charge) { case PRI_AOC_DE_CHARGE_NOT_AVAILABLE: case PRI_AOC_DE_CHARGE_FREE: case PRI_AOC_DE_CHARGE_CURRENCY: end = enc_etsi_aocd_currency(ctrl, buffer, buffer + sizeof(buffer), aoc_d); break; case PRI_AOC_DE_CHARGE_UNITS: end = enc_etsi_aocd_charging_unit(ctrl, buffer, buffer + sizeof(buffer), aoc_d); break; } if (!end) { return -1; } /* Remember that if we queue a facility IE for a facility message we * have to explicitly send the facility message ourselves */ if (pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL) || q931_facility(call->pri, call)) { pri_message(ctrl, "Could not schedule aoc-d facility message for call %d\n", call->cr); return -1; } return 0; } /*! * \internal * \brief Send the ETSI AOCE invoke message. * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Call leg from which to encode AOC. * \param aoc_e the AOC-E payload data to encode. * * \retval 0 on success. * \retval -1 on error. */ static int aoc_e_encode(struct pri *ctrl, q931_call *call, const struct pri_subcmd_aoc_e *aoc_e) { unsigned char buffer[255]; unsigned char *end = NULL; switch (aoc_e->charge) { case PRI_AOC_DE_CHARGE_NOT_AVAILABLE: case PRI_AOC_DE_CHARGE_FREE: case PRI_AOC_DE_CHARGE_CURRENCY: end = enc_etsi_aoce_currency(ctrl, buffer, buffer + sizeof(buffer), aoc_e); break; case PRI_AOC_DE_CHARGE_UNITS: end = enc_etsi_aoce_charging_unit(ctrl, buffer, buffer + sizeof(buffer), aoc_e); break; } if (!end) { return -1; } if (pri_call_apdu_queue(call, Q931_ANY_MESSAGE, buffer, end - buffer, NULL)) { pri_message(ctrl, "Could not schedule aoc-e facility message for call %d\n", call->cr); return -1; } return 0; } int pri_aoc_de_request_response_send(struct pri *ctrl, q931_call *call, int response, int invoke_id) { if (!ctrl || !pri_is_call_valid(ctrl, call)) { return -1; } switch (ctrl->switchtype) { case PRI_SWITCH_EUROISDN_E1: case PRI_SWITCH_EUROISDN_T1: return aoc_de_request_response_encode(ctrl, call, response, invoke_id); case PRI_SWITCH_QSIG: break; default: return -1; } return 0; } int pri_aoc_s_request_response_send(struct pri *ctrl, q931_call *call, int invoke_id, const struct pri_subcmd_aoc_s *aoc_s) { if (!ctrl || !pri_is_call_valid(ctrl, call)) { return -1; } switch (ctrl->switchtype) { case PRI_SWITCH_EUROISDN_E1: case PRI_SWITCH_EUROISDN_T1: return aoc_s_request_response_encode(ctrl, call, invoke_id, aoc_s); case PRI_SWITCH_QSIG: break; default: return -1; } return 0; } /*! * \brief Send AOC request message. * * \param ctrl D channel controller. * \param call Q.931 call leg. * \param aoc types to request * * \retval 0 on success * \retval -1 on failure */ int aoc_charging_request_send(struct pri *ctrl, q931_call *call, enum PRI_AOC_REQUEST aoc_request_flag) { int res; switch (ctrl->switchtype) { case PRI_SWITCH_EUROISDN_E1: case PRI_SWITCH_EUROISDN_T1: if (BRI_NT_PTMP(ctrl)) { /* * We are not setup to handle responses from multiple phones. * Besides, it is silly to ask for AOC from a phone. */ return -1; } res = 0; if (aoc_request_flag & PRI_AOC_REQUEST_S) { res |= aoc_charging_request_encode(ctrl, call, PRI_AOC_REQUEST_S); } if (aoc_request_flag & PRI_AOC_REQUEST_D) { res |= aoc_charging_request_encode(ctrl, call, PRI_AOC_REQUEST_D); } if (aoc_request_flag & PRI_AOC_REQUEST_E) { res |= aoc_charging_request_encode(ctrl, call, PRI_AOC_REQUEST_E); } return res; case PRI_SWITCH_QSIG: break; default: return -1; } return 0; } int pri_aoc_s_send(struct pri *ctrl, q931_call *call, const struct pri_subcmd_aoc_s *aoc_s) { if (!ctrl || !pri_is_call_valid(ctrl, call)) { return -1; } switch (ctrl->switchtype) { case PRI_SWITCH_EUROISDN_E1: case PRI_SWITCH_EUROISDN_T1: return aoc_s_encode(ctrl, call, aoc_s); case PRI_SWITCH_QSIG: break; default: return -1; } return 0; } int pri_aoc_d_send(struct pri *ctrl, q931_call *call, const struct pri_subcmd_aoc_d *aoc_d) { if (!ctrl || !pri_is_call_valid(ctrl, call)) { return -1; } switch (ctrl->switchtype) { case PRI_SWITCH_EUROISDN_E1: case PRI_SWITCH_EUROISDN_T1: return aoc_d_encode(ctrl, call, aoc_d); case PRI_SWITCH_QSIG: break; default: return -1; } return 0; } int pri_aoc_e_send(struct pri *ctrl, q931_call *call, const struct pri_subcmd_aoc_e *aoc_e) { if (!ctrl || !pri_is_call_valid(ctrl, call)) { return -1; } switch (ctrl->switchtype) { case PRI_SWITCH_EUROISDN_E1: case PRI_SWITCH_EUROISDN_T1: return aoc_e_encode(ctrl, call, aoc_e); case PRI_SWITCH_QSIG: break; default: return -1; } return 0; } int pri_sr_set_aoc_charging_request(struct pri_sr *sr, int charging_request) { if (charging_request & PRI_AOC_REQUEST_S) { sr->aoc_charging_request |= PRI_AOC_REQUEST_S; } if (charging_request & PRI_AOC_REQUEST_D) { sr->aoc_charging_request |= PRI_AOC_REQUEST_D; } if (charging_request & PRI_AOC_REQUEST_E) { sr->aoc_charging_request |= PRI_AOC_REQUEST_E; } return 0; } /* ------------------------------------------------------------------- */ /* end pri_aoc.c */ libpri-1.4.14/testprilib.c0000644000000000000000000001520312011325134014076 0ustar rootroot/* * libpri: An implementation of Primary Rate ISDN * * Written by Mark Spencer * * Copyright (C) 2001-2005, Digium, Inc. * All Rights Reserved. */ /* * 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. */ /* * This program tests libpri call reception using a zaptel interface. * Its state machines are setup for RECEIVING CALLS ONLY, so if you * are trying to both place and receive calls you have to a bit more. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libpri.h" #include "pri_q921.h" #include "pri_q931.h" #ifndef AF_LOCAL #define AF_LOCAL AF_UNIX #endif #define DEBUG_LEVEL PRI_DEBUG_ALL #define PRI_DEF_NODETYPE PRI_CPE #define PRI_DEF_SWITCHTYPE PRI_SWITCH_NI2 static struct pri *first; static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; #define TEST_CALLS 1 static void event1(struct pri *pri, pri_event *e) { /* Network */ int x; static q931_call *calls[TEST_CALLS]; char name[256], num[256], dest[256]; switch(e->gen.e) { case PRI_EVENT_DCHAN_UP: printf("Network is up. Sending blast of calls!\n"); for (x=0;xgen.e), e->gen.e); q931_facility(pri, e->ringing.call); pri_answer(pri, e->ringing.call, e->ringing.channel, 0); break; case PRI_EVENT_HANGUP_REQ: printf("PRI 1: %s (%d)\n", pri_event2str(e->gen.e), e->gen.e); pri_hangup(pri, e->hangup.call, e->hangup.cause); break; default: printf("PRI 1: %s (%d)\n", pri_event2str(e->gen.e), e->gen.e); } } static void event2(struct pri *pri, pri_event *e) { /* CPE */ switch(e->gen.e) { case PRI_EVENT_RING: printf("PRI 2: %s (%d)\n", pri_event2str(e->gen.e), e->gen.e); pri_proceeding(pri, e->ring.call, e->ring.channel, 0); pri_acknowledge(pri, e->ring.call, e->ring.channel, 0); break; case PRI_EVENT_ANSWER: printf("PRI 2: %s (%d)\n", pri_event2str(e->gen.e), e->gen.e); pri_hangup(pri, e->answer.call, PRI_CAUSE_NORMAL_UNSPECIFIED); break; case PRI_EVENT_HANGUP: printf("PRI 2: %s (%d)\n", pri_event2str(e->gen.e), e->gen.e); pri_hangup(pri, e->hangup.call, e->hangup.cause); break; case PRI_EVENT_DCHAN_UP: default: printf("PRI 2: %s (%d)\n", pri_event2str(e->gen.e), e->gen.e); } } static void testmsg(struct pri *pri, char *s) { char *c; static int keeplast = 0; do { c = strchr(s, '\n'); if (c) { *c = '\0'; c++; } if (keeplast || !pri) printf("%s", s); else if (pri == first) printf("-1 %s", s); else printf("-2 %s", s); if (c) printf("\n"); s = c; } while(c && *c); if (!c) keeplast = 1; else keeplast = 0; } static void testerr(struct pri *pri, char *s) { char *c; static int keeplast = 0; do { c = strchr(s, '\n'); if (c) { *c = '\0'; c++; } if (keeplast || !pri) printf("%s", s); else if (pri == first) printf("=1 %s", s); else printf("=2 %s", s); if (c) printf("\n"); s = c; } while(c && *c); if (!c) keeplast = 1; else keeplast = 0; } static void *dchan(void *data) { /* Joint D-channel */ struct pri *pri = data; struct timeval *next, tv; pri_event *e = NULL; fd_set fds; int res; for(;;) { if ((next = pri_schedule_next(pri))) { gettimeofday(&tv, NULL); tv.tv_sec = next->tv_sec - tv.tv_sec; tv.tv_usec = next->tv_usec - tv.tv_usec; if (tv.tv_usec < 0) { tv.tv_usec += 1000000; tv.tv_sec -= 1; } if (tv.tv_sec < 0) { tv.tv_sec = 0; tv.tv_usec = 0; } } FD_ZERO(&fds); FD_SET(pri_fd(pri), &fds); res = select(pri_fd(pri) + 1, &fds, NULL, NULL, next ? &tv : NULL); pthread_mutex_lock(&lock); if (res < 0) { perror("select"); } else if (!res) { e = pri_schedule_run(pri); } else { e = pri_check_event(pri); } if (e) { if (first == pri) { event1(pri, e); } else { event2(pri, e); } } pthread_mutex_unlock(&lock); } return NULL; } int main(int argc, char *argv[]) { int pair[2]; pthread_t tmp; struct pri *pri; pri_set_message(testmsg); pri_set_error(testerr); if (socketpair(AF_LOCAL, SOCK_DGRAM, 0, pair)) { perror("socketpair"); exit(1); } if (!(pri = pri_new_bri(pair[0], 0, PRI_NETWORK, PRI_DEF_SWITCHTYPE))) { perror("pri(0)"); exit(1); } first = pri; pri_set_debug(pri, DEBUG_LEVEL); pri_facility_enable(pri); if (pthread_create(&tmp, NULL, dchan, pri)) { perror("thread(0)"); exit(1); } if (!(pri = pri_new_bri(pair[1], 0, PRI_CPE, PRI_DEF_SWITCHTYPE))) { perror("pri(1)"); exit(1); } pri_set_debug(pri, DEBUG_LEVEL); pri_facility_enable(pri); if (pthread_create(&tmp, NULL, dchan, pri)) { perror("thread(1)"); exit(1); } /* Wait for things to run */ sleep(5); exit(0); } libpri-1.4.14/rose_qsig_diversion.c0000644000000000000000000014466211237046760016024 0ustar rootroot/* * 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 Call-Diversion-Operations * * Call-Diversion-Operations ECMA-174 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 IntResult 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 int_result Forwarding record information to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *rose_enc_qsig_IntResult(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseQsigForwardingRecord *int_result) { unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &int_result->served_user_number)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, int_result->basic_service)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, int_result->procedure)); ASN1_CALL(pos, rose_enc_Address(ctrl, pos, end, ASN1_TAG_SEQUENCE, &int_result->diverted_to)); if (int_result->remote_enabled) { /* Not the DEFAULT value */ ASN1_CALL(pos, asn1_enc_boolean(pos, end, ASN1_TYPE_BOOLEAN, int_result->remote_enabled)); } /* No extension to encode */ ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \internal * \brief Encode the IntResultList 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_SET unless the caller implicitly * tags it otherwise. * \param int_result_list Forwarding record 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_qsig_IntResultList(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseQsigForwardingList *int_result_list) { unsigned index; unsigned char *set_len; ASN1_CONSTRUCTED_BEGIN(set_len, pos, end, tag); for (index = 0; index < int_result_list->num_records; ++index) { ASN1_CALL(pos, rose_enc_qsig_IntResult(ctrl, pos, end, ASN1_TAG_SEQUENCE, &int_result_list->list[index])); } ASN1_CONSTRUCTED_END(set_len, pos, end); return pos; } /*! * \brief Encode the ActivateDiversionQ 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_ActivateDiversionQ_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { const struct roseQsigActivateDiversionQ_ARG *activate_diversion_q; unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); activate_diversion_q = &args->qsig.ActivateDiversionQ; ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, activate_diversion_q->procedure)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, activate_diversion_q->basic_service)); ASN1_CALL(pos, rose_enc_Address(ctrl, pos, end, ASN1_TAG_SEQUENCE, &activate_diversion_q->diverted_to)); ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &activate_diversion_q->served_user_number)); ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &activate_diversion_q->activating_user_number)); /* No extension to encode */ ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the DeactivateDiversionQ 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_DeactivateDiversionQ_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { const struct roseQsigDeactivateDiversionQ_ARG *deactivate_diversion_q; unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); deactivate_diversion_q = &args->qsig.DeactivateDiversionQ; ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, deactivate_diversion_q->procedure)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, deactivate_diversion_q->basic_service)); ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &deactivate_diversion_q->served_user_number)); ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &deactivate_diversion_q->deactivating_user_number)); /* No extension to encode */ ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the InterrogateDiversionQ 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_InterrogateDiversionQ_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { const struct roseQsigInterrogateDiversionQ_ARG *interrogate_diversion_q; unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); interrogate_diversion_q = &args->qsig.InterrogateDiversionQ; ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, interrogate_diversion_q->procedure)); if (interrogate_diversion_q->basic_service) { /* Not the DEFAULT value */ ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, interrogate_diversion_q->basic_service)); } ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &interrogate_diversion_q->served_user_number)); ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &interrogate_diversion_q->interrogating_user_number)); /* No extension to encode */ ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the InterrogateDiversionQ 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_InterrogateDiversionQ_RES(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args) { return rose_enc_qsig_IntResultList(ctrl, pos, end, ASN1_TAG_SET, &args->qsig.InterrogateDiversionQ); } /*! * \brief Encode the CheckRestriction 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_CheckRestriction_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { const struct roseQsigCheckRestriction_ARG *check_restriction; unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); check_restriction = &args->qsig.CheckRestriction; ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &check_restriction->served_user_number)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, check_restriction->basic_service)); ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &check_restriction->diverted_to_number)); /* No extension to encode */ ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the CallRerouting 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_CallRerouting_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { const struct roseQsigCallRerouting_ARG *call_rerouting; unsigned char *seq_len; unsigned char *exp_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); call_rerouting = &args->qsig.CallRerouting; ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, call_rerouting->rerouting_reason)); if (call_rerouting->original_rerouting_reason_present) { ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0, call_rerouting->original_rerouting_reason)); } ASN1_CALL(pos, rose_enc_Address(ctrl, pos, end, ASN1_TAG_SEQUENCE, &call_rerouting->called)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, call_rerouting->diversion_counter)); ASN1_CALL(pos, rose_enc_Q931ie(ctrl, pos, end, ASN1_CLASS_APPLICATION | 0, &call_rerouting->q931ie)); /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(exp_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1); ASN1_CALL(pos, rose_enc_PresentedNumberUnscreened(ctrl, pos, end, &call_rerouting->last_rerouting)); ASN1_CONSTRUCTED_END(exp_len, pos, end); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, call_rerouting->subscription_option)); if (call_rerouting->calling_subaddress.length) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(exp_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3); ASN1_CALL(pos, rose_enc_PartySubaddress(ctrl, pos, end, &call_rerouting->calling_subaddress)); ASN1_CONSTRUCTED_END(exp_len, pos, end); } /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(exp_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 4); ASN1_CALL(pos, rose_enc_PresentedNumberScreened(ctrl, pos, end, &call_rerouting->calling)); ASN1_CONSTRUCTED_END(exp_len, pos, end); if (call_rerouting->calling_name_present) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(exp_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 5); ASN1_CALL(pos, rose_enc_qsig_Name(ctrl, pos, end, &call_rerouting->calling_name)); ASN1_CONSTRUCTED_END(exp_len, pos, end); } if (call_rerouting->original_called_present) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(exp_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 6); ASN1_CALL(pos, rose_enc_PresentedNumberUnscreened(ctrl, pos, end, &call_rerouting->original_called)); ASN1_CONSTRUCTED_END(exp_len, pos, end); } if (call_rerouting->redirecting_name_present) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(exp_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 7); ASN1_CALL(pos, rose_enc_qsig_Name(ctrl, pos, end, &call_rerouting->redirecting_name)); ASN1_CONSTRUCTED_END(exp_len, pos, end); } if (call_rerouting->original_called_name_present) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(exp_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 8); ASN1_CALL(pos, rose_enc_qsig_Name(ctrl, pos, end, &call_rerouting->original_called_name)); ASN1_CONSTRUCTED_END(exp_len, pos, end); } /* No extension to encode */ ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the DivertingLegInformation1 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_DivertingLegInformation1_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { const struct roseQsigDivertingLegInformation1_ARG *diverting_leg_information_1; unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); diverting_leg_information_1 = &args->qsig.DivertingLegInformation1; ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, diverting_leg_information_1->diversion_reason)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, diverting_leg_information_1->subscription_option)); ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &diverting_leg_information_1->nominated_number)); /* No extension to encode */ ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the DivertingLegInformation2 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_DivertingLegInformation2_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { const struct roseQsigDivertingLegInformation2_ARG *diverting_leg_information_2; unsigned char *seq_len; unsigned char *exp_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); diverting_leg_information_2 = &args->qsig.DivertingLegInformation2; ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, diverting_leg_information_2->diversion_counter)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, diverting_leg_information_2->diversion_reason)); if (diverting_leg_information_2->original_diversion_reason_present) { ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0, diverting_leg_information_2->original_diversion_reason)); } if (diverting_leg_information_2->diverting_present) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(exp_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1); ASN1_CALL(pos, rose_enc_PresentedNumberUnscreened(ctrl, pos, end, &diverting_leg_information_2->diverting)); ASN1_CONSTRUCTED_END(exp_len, pos, end); } if (diverting_leg_information_2->original_called_present) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(exp_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2); ASN1_CALL(pos, rose_enc_PresentedNumberUnscreened(ctrl, pos, end, &diverting_leg_information_2->original_called)); ASN1_CONSTRUCTED_END(exp_len, pos, end); } if (diverting_leg_information_2->redirecting_name_present) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(exp_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3); ASN1_CALL(pos, rose_enc_qsig_Name(ctrl, pos, end, &diverting_leg_information_2->redirecting_name)); ASN1_CONSTRUCTED_END(exp_len, pos, end); } if (diverting_leg_information_2->original_called_name_present) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(exp_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 4); ASN1_CALL(pos, rose_enc_qsig_Name(ctrl, pos, end, &diverting_leg_information_2->original_called_name)); ASN1_CONSTRUCTED_END(exp_len, pos, end); } /* No extension to encode */ ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the DivertingLegInformation3 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_DivertingLegInformation3_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { const struct roseQsigDivertingLegInformation3_ARG *diverting_leg_information_3; unsigned char *seq_len; unsigned char *exp_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); diverting_leg_information_3 = &args->qsig.DivertingLegInformation3; ASN1_CALL(pos, asn1_enc_boolean(pos, end, ASN1_TYPE_BOOLEAN, diverting_leg_information_3->presentation_allowed_indicator)); if (diverting_leg_information_3->redirection_name_present) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(exp_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0); ASN1_CALL(pos, rose_enc_qsig_Name(ctrl, pos, end, &diverting_leg_information_3->redirection_name)); ASN1_CONSTRUCTED_END(exp_len, pos, end); } /* No extension to encode */ ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \internal * \brief Decode the IntResult 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 int_result 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_IntResult(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseQsigForwardingRecord *int_result) { 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 IntResult %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_PartyNumber(ctrl, "servedUserNr", tag, pos, seq_end, &int_result->served_user_number)); 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, "basicService", tag, pos, seq_end, &value)); int_result->basic_service = value; 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, "procedure", tag, pos, seq_end, &value)); int_result->procedure = 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, "divertedToAddress", tag, pos, seq_end, &int_result->diverted_to)); /* * A sequence specifies an ordered list of component types. * However, for simplicity we are not checking the order of * the remaining optional components. */ int_result->remote_enabled = 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 & ~ASN1_PC_MASK) { case ASN1_TYPE_BOOLEAN: /* Must not be constructed but we will not check for it for simplicity. */ ASN1_CALL(pos, asn1_dec_boolean(ctrl, "remoteEnabled", tag, pos, seq_end, &value)); int_result->remote_enabled = value; break; case ASN1_CLASS_CONTEXT_SPECIFIC | 1: case ASN1_CLASS_CONTEXT_SPECIFIC | 2: if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " extension %s\n", asn1_tag2str(tag)); } /* Fixup will skip over the manufacturer extension information */ default: pos = save_pos; goto cancel_options; } } cancel_options:; ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \internal * \brief Decode the IntResultList 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 int_result_list 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_IntResultList(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseQsigForwardingList *int_result_list) { int length; int set_offset; const unsigned char *set_end; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s IntResultList %s\n", name, asn1_tag2str(tag)); } ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); ASN1_END_SETUP(set_end, set_offset, length, pos, end); int_result_list->num_records = 0; while (pos < set_end && *pos != ASN1_INDEF_TERM) { if (int_result_list->num_records < ARRAY_LEN(int_result_list->list)) { ASN1_CALL(pos, asn1_dec_tag(pos, set_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); ASN1_CALL(pos, rose_dec_qsig_IntResult(ctrl, "listEntry", tag, pos, set_end, &int_result_list->list[int_result_list->num_records])); ++int_result_list->num_records; } else { /* Too many records */ return NULL; } } ASN1_END_FIXUP(ctrl, pos, set_offset, set_end, end); return pos; } /*! * \brief Decode the Q.SIG ActivateDiversionQ 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_ActivateDiversionQ_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; struct roseQsigActivateDiversionQ_ARG *activate_diversion_q; activate_diversion_q = &args->qsig.ActivateDiversionQ; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " ActivateDiversionQ %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_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); ASN1_CALL(pos, asn1_dec_int(ctrl, "procedure", tag, pos, seq_end, &value)); activate_diversion_q->procedure = value; 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, "basicService", tag, pos, seq_end, &value)); activate_diversion_q->basic_service = 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, "divertedToAddress", tag, pos, seq_end, &activate_diversion_q->diverted_to)); ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "servedUserNr", tag, pos, seq_end, &activate_diversion_q->served_user_number)); ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "activatingUserNr", tag, pos, seq_end, &activate_diversion_q->activating_user_number)); /* Fixup will skip over any OPTIONAL manufacturer extension information */ ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the Q.SIG DeactivateDiversionQ 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_DeactivateDiversionQ_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; struct roseQsigDeactivateDiversionQ_ARG *deactivate_diversion_q; deactivate_diversion_q = &args->qsig.DeactivateDiversionQ; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " DeactivateDiversionQ %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_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); ASN1_CALL(pos, asn1_dec_int(ctrl, "procedure", tag, pos, seq_end, &value)); deactivate_diversion_q->procedure = value; 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, "basicService", tag, pos, seq_end, &value)); deactivate_diversion_q->basic_service = value; ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "servedUserNr", tag, pos, seq_end, &deactivate_diversion_q->served_user_number)); ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "deactivatingUserNr", tag, pos, seq_end, &deactivate_diversion_q->deactivating_user_number)); /* Fixup will skip over any OPTIONAL manufacturer extension information */ ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the Q.SIG InterrogateDiversionQ 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_InterrogateDiversionQ_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; struct roseQsigInterrogateDiversionQ_ARG *interrogate_diversion_q; interrogate_diversion_q = &args->qsig.InterrogateDiversionQ; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " InterrogateDiversionQ %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_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); ASN1_CALL(pos, asn1_dec_int(ctrl, "procedure", tag, pos, seq_end, &value)); interrogate_diversion_q->procedure = value; ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); if (tag == ASN1_TYPE_ENUMERATED) { ASN1_CALL(pos, asn1_dec_int(ctrl, "basicService", tag, pos, seq_end, &value)); interrogate_diversion_q->basic_service = value; ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); } else { interrogate_diversion_q->basic_service = 0; /* allServices */ } ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "servedUserNr", tag, pos, seq_end, &interrogate_diversion_q->served_user_number)); ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "interrogatingUserNr", tag, pos, seq_end, &interrogate_diversion_q->interrogating_user_number)); /* Fixup will skip over any OPTIONAL manufacturer extension information */ ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the Q.SIG InterrogateDiversionQ 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_InterrogateDiversionQ_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_SET); return rose_dec_qsig_IntResultList(ctrl, "InterrogateDiversionQ", tag, pos, end, &args->qsig.InterrogateDiversionQ); } /*! * \brief Decode the Q.SIG CheckRestriction 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_CheckRestriction_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; struct roseQsigCheckRestriction_ARG *check_restriction; check_restriction = &args->qsig.CheckRestriction; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " CheckRestriction %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, "servedUserNr", tag, pos, seq_end, &check_restriction->served_user_number)); 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, "basicService", tag, pos, seq_end, &value)); check_restriction->basic_service = value; ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "divertedToNr", tag, pos, seq_end, &check_restriction->diverted_to_number)); /* Fixup will skip over any OPTIONAL manufacturer extension information */ ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the Q.SIG CallRerouting 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_CallRerouting_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { 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; struct roseQsigCallRerouting_ARG *call_rerouting; call_rerouting = &args->qsig.CallRerouting; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " CallRerouting %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_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); ASN1_CALL(pos, asn1_dec_int(ctrl, "reroutingReason", tag, pos, seq_end, &value)); call_rerouting->rerouting_reason = value; ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); if (tag == (ASN1_CLASS_CONTEXT_SPECIFIC | 0)) { ASN1_CALL(pos, asn1_dec_int(ctrl, "originalReroutingReason", tag, pos, seq_end, &value)); call_rerouting->original_rerouting_reason = value; call_rerouting->original_rerouting_reason_present = 1; ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); } else { call_rerouting->original_rerouting_reason_present = 0; } ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); ASN1_CALL(pos, rose_dec_Address(ctrl, "calledAddress", tag, pos, seq_end, &call_rerouting->called)); 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, "diversionCounter", tag, pos, seq_end, &value)); call_rerouting->diversion_counter = 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, "pSS1InfoElement", tag, pos, seq_end, &call_rerouting->q931ie, sizeof(call_rerouting->q931ie_contents))); /* Remove EXPLICIT tag */ ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1); 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_PresentedNumberUnscreened(ctrl, "lastReroutingNr", tag, pos, explicit_end, &call_rerouting->last_rerouting)); ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 2); ASN1_CALL(pos, asn1_dec_int(ctrl, "subscriptionOption", tag, pos, seq_end, &value)); call_rerouting->subscription_option = value; ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); if (tag == (ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 3)) { /* 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, "callingPartySubaddress", tag, pos, explicit_end, &call_rerouting->calling_subaddress)); ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); } else { call_rerouting->calling_subaddress.length = 0; } /* Remove EXPLICIT tag */ ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 4); 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_PresentedNumberScreened(ctrl, "callingNumber", tag, pos, explicit_end, &call_rerouting->calling)); ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); /* * A sequence specifies an ordered list of component types. * However, for simplicity we are not checking the order of * the remaining optional components. */ call_rerouting->calling_name_present = 0; call_rerouting->redirecting_name_present = 0; call_rerouting->original_called_name_present = 0; call_rerouting->original_called_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 | 5: /* 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_qsig_Name(ctrl, "callingName", tag, pos, explicit_end, &call_rerouting->calling_name)); call_rerouting->calling_name_present = 1; ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 6: /* 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_PresentedNumberUnscreened(ctrl, "originalCalledNr", tag, pos, explicit_end, &call_rerouting->original_called)); call_rerouting->original_called_present = 1; ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 7: /* 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_qsig_Name(ctrl, "redirectingName", tag, pos, explicit_end, &call_rerouting->redirecting_name)); call_rerouting->redirecting_name_present = 1; ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 8: /* 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_qsig_Name(ctrl, "originalCalledName", tag, pos, explicit_end, &call_rerouting->original_called_name)); call_rerouting->original_called_name_present = 1; ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 9: case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 9: case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 10: if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " extension %s\n", asn1_tag2str(tag)); } /* Fixup will skip over the manufacturer extension information */ 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 DivertingLegInformation1 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_DivertingLegInformation1_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; struct roseQsigDivertingLegInformation1_ARG *diverting_leg_information_1; diverting_leg_information_1 = &args->qsig.DivertingLegInformation1; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " DivertingLegInformation1 %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_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); ASN1_CALL(pos, asn1_dec_int(ctrl, "diversionReason", tag, pos, seq_end, &value)); diverting_leg_information_1->diversion_reason = value; 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, "subscriptionOption", tag, pos, seq_end, &value)); diverting_leg_information_1->subscription_option = value; ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "nominatedNr", tag, pos, seq_end, &diverting_leg_information_1->nominated_number)); /* Fixup will skip over any OPTIONAL manufacturer extension information */ ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the Q.SIG DivertingLegInformation2 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_DivertingLegInformation2_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { 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; struct roseQsigDivertingLegInformation2_ARG *diverting_leg_information_2; diverting_leg_information_2 = &args->qsig.DivertingLegInformation2; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " DivertingLegInformation2 %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_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER); ASN1_CALL(pos, asn1_dec_int(ctrl, "diversionCounter", tag, pos, seq_end, &value)); diverting_leg_information_2->diversion_counter = value; 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, "diversionReason", tag, pos, seq_end, &value)); diverting_leg_information_2->diversion_reason = value; /* * A sequence specifies an ordered list of component types. * However, for simplicity we are not checking the order of * the remaining optional components. */ diverting_leg_information_2->original_diversion_reason_present = 0; diverting_leg_information_2->diverting_present = 0; diverting_leg_information_2->original_called_present = 0; diverting_leg_information_2->redirecting_name_present = 0; diverting_leg_information_2->original_called_name_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 | 0: ASN1_CALL(pos, asn1_dec_int(ctrl, "originalDiversionReason", tag, pos, seq_end, &value)); diverting_leg_information_2->original_diversion_reason = value; diverting_leg_information_2->original_diversion_reason_present = 1; break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1: /* 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_PresentedNumberUnscreened(ctrl, "divertingNr", tag, pos, explicit_end, &diverting_leg_information_2->diverting)); diverting_leg_information_2->diverting_present = 1; ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2: /* 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_PresentedNumberUnscreened(ctrl, "originalCalledNr", tag, pos, explicit_end, &diverting_leg_information_2->original_called)); diverting_leg_information_2->original_called_present = 1; ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 3: /* 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_qsig_Name(ctrl, "redirectingName", tag, pos, explicit_end, &diverting_leg_information_2->redirecting_name)); diverting_leg_information_2->redirecting_name_present = 1; ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 4: /* 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_qsig_Name(ctrl, "originalCalledName", tag, pos, explicit_end, &diverting_leg_information_2->original_called_name)); diverting_leg_information_2->original_called_name_present = 1; ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 5: case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 5: case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 6: if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " extension %s\n", asn1_tag2str(tag)); } /* Fixup will skip over the manufacturer extension information */ 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 DivertingLegInformation3 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_DivertingLegInformation3_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { 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; struct roseQsigDivertingLegInformation3_ARG *diverting_leg_information_3; diverting_leg_information_3 = &args->qsig.DivertingLegInformation3; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " DivertingLegInformation3 %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_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_BOOLEAN); ASN1_CALL(pos, asn1_dec_boolean(ctrl, "presentationAllowedIndicator", tag, pos, seq_end, &value)); diverting_leg_information_3->presentation_allowed_indicator = value; /* * A sequence specifies an ordered list of component types. * However, for simplicity we are not checking the order of * the remaining optional components. */ diverting_leg_information_3->redirection_name_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 | 0: /* 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_qsig_Name(ctrl, "redirectionName", tag, pos, explicit_end, &diverting_leg_information_3->redirection_name)); diverting_leg_information_3->redirection_name_present = 1; ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 1: case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1: case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2: if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " extension %s\n", asn1_tag2str(tag)); } /* Fixup will skip over the manufacturer extension information */ default: pos = save_pos; goto cancel_options; } } cancel_options:; ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /* ------------------------------------------------------------------- */ /* end rose_qsig_diversion.c */ libpri-1.4.14/pridump.c0000644000000000000000000000715012023406277013412 0ustar rootroot/* * libpri: An implementation of Primary Rate ISDN * * Written by Mark Spencer * * Copyright (C) 2001-2005, Digium, Inc. * All Rights Reserved. */ /* * 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. */ /* * This program tests libpri call reception using a zaptel interface. * Its state machines are setup for RECEIVING CALLS ONLY, so if you * are trying to both place and receive calls you have to a bit more. */ #include #include #include #include #include #include #include #include #include #include #include "libpri.h" #include "pri_q921.h" #include "pri_q931.h" static int pri_open(char *dev) { int dfd; struct dahdi_params p; dfd = open(dev, O_RDWR); if (dfd < 0) { fprintf(stderr, "Failed to open dchannel '%s': %s\n", dev, strerror(errno)); return -1; } if (ioctl(dfd, DAHDI_GET_PARAMS, &p)) { fprintf(stderr, "Unable to get parameters on '%s': %s\n", dev, strerror(errno)); return -1; } if ((p.sigtype != DAHDI_SIG_HDLCRAW) && (p.sigtype != DAHDI_SIG_HDLCFCS)) { fprintf(stderr, "%s is in %d signalling, not FCS HDLC or RAW HDLC mode\n", dev, p.sigtype); return -1; } return dfd; } static void dump_packet(struct pri *pri, char *buf, int len, int txrx) { q921_h *h = (q921_h *)buf; q921_dump(pri, h, len, PRI_DEBUG_ALL, txrx); if (!((h->h.data[0] & Q921_FRAMETYPE_MASK) & 0x3)) { q931_dump(pri, h->h.tei, (q931_h *)(h->i.data), len - 4 - 2 /* FCS */, txrx); } fflush(stdout); fflush(stderr); } static void pri_bridge(int d1, int d2) { char buf[1024]; fd_set fds; int max; int e; int res; for(;;) { FD_ZERO(&fds); FD_SET(d1, &fds); FD_SET(d2, &fds); max = d1; if (max < d2) max = d2; ioctl(d1, DAHDI_GETEVENT, &e); ioctl(d2, DAHDI_GETEVENT, &e); res = select(max + 1, &fds, NULL, NULL, NULL); if (res < 0) { fprintf(stderr, "Select returned %d: %s\n", res, strerror(errno)); continue; }; if (FD_ISSET(d1, &fds)) { /* Copy from d1 to d2 */ res = read(d1, buf, sizeof(buf)); dump_packet((struct pri *)NULL, buf, res, 1); res = write(d2, buf, res); } if (FD_ISSET(d2, &fds)) { /* Copy from d2 to d1 */ res = read(d2, buf, sizeof(buf)); dump_packet((struct pri *)NULL, buf, res, 0); res = write(d1, buf, res); } } } static void my_pri_message(struct pri *pri, char *stuff) { fprintf(stdout, "%s", stuff); } static void my_pri_error(struct pri *pri, char *stuff) { fprintf(stderr, "%s", stuff); } int main(int argc, char *argv[]) { int d1, d2; if (argc < 3) { fprintf(stderr, "Usage: pridump \n"); exit(1); } pri_set_message(my_pri_message); pri_set_error(my_pri_error); d1 = pri_open(argv[1]); if (d1 < 0) exit(1); d2 = pri_open(argv[2]); if (d2 < 0) exit(1); pri_bridge(d1, d2); return 0; } libpri-1.4.14/rose_etsi_ect.c0000644000000000000000000002650411242637374014573 0ustar rootroot/* * 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 Explicit Call Transfer operations. * * Explicit Call Transfer (ECT) Supplementary Services ETS 300 369-1 * * \author Richard Mudgett */ #include "compat.h" #include "libpri.h" #include "pri_internal.h" #include "rose.h" #include "rose_internal.h" #include "asn1.h" /* ------------------------------------------------------------------- */ /*! * \brief Encode the ExplicitEctExecute 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_ExplicitEctExecute_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.ExplicitEctExecute.link_id); } /*! * \brief Encode the SubaddressTransfer 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_SubaddressTransfer_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { return rose_enc_PartySubaddress(ctrl, pos, end, &args->etsi.SubaddressTransfer.subaddress); } /*! * \brief Encode the EctLinkIdRequest 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_EctLinkIdRequest_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_INTEGER, args->etsi.EctLinkIdRequest.link_id); } /*! * \brief Encode the EctInform 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_EctInform_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { const struct roseEtsiEctInform_ARG *ect_inform; unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); ect_inform = &args->etsi.EctInform; ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, ect_inform->status)); if (ect_inform->redirection_present) { ASN1_CALL(pos, rose_enc_PresentedNumberUnscreened(ctrl, pos, end, &ect_inform->redirection)); } ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the EctLoopTest 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_EctLoopTest_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.EctLoopTest.call_transfer_id); } /*! * \brief Encode the EctLoopTest 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_EctLoopTest_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.EctLoopTest.loop_result); } /*! * \brief Decode the ExplicitEctExecute 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_ExplicitEctExecute_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, "linkId", tag, pos, end, &value)); args->etsi.ExplicitEctExecute.link_id = value; return pos; } /*! * \brief Decode the SubaddressTransfer 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_SubaddressTransfer_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { return rose_dec_PartySubaddress(ctrl, "transferredToSubaddress", tag, pos, end, &args->etsi.SubaddressTransfer.subaddress); } /*! * \brief Decode the EctLinkIdRequest 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_EctLinkIdRequest_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_INTEGER); ASN1_CALL(pos, asn1_dec_int(ctrl, "linkId", tag, pos, end, &value)); args->etsi.EctLinkIdRequest.link_id = value; return pos; } /*! * \brief Decode the EctInform 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_EctInform_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { struct roseEtsiEctInform_ARG *ect_inform; int length; int seq_offset; const unsigned char *seq_end; int32_t value; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " EctInform %s\n", asn1_tag2str(tag)); } ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); ect_inform = &args->etsi.EctInform; 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, "callStatus", tag, pos, seq_end, &value)); ect_inform->status = value; if (pos < seq_end && *pos != ASN1_INDEF_TERM) { ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CALL(pos, rose_dec_PresentedNumberUnscreened(ctrl, "redirectionNumber", tag, pos, seq_end, &ect_inform->redirection)); ect_inform->redirection_present = 1; } else { ect_inform->redirection_present = 0; } ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the EctLoopTest 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_EctLoopTest_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, "callTransferId", tag, pos, end, &value)); args->etsi.EctLoopTest.call_transfer_id = value; return pos; } /*! * \brief Decode the EctLoopTest 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_EctLoopTest_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, "loopResult", tag, pos, end, &value)); args->etsi.EctLoopTest.loop_result = value; return pos; } /* ------------------------------------------------------------------- */ /* end rose_etsi_ect.c */ libpri-1.4.14/prisched.c0000644000000000000000000002151211527022026013523 0ustar rootroot/* * libpri: An implementation of Primary Rate ISDN * * Written by Mark Spencer * * Copyright (C) 2001-2005, Digium, Inc. * All Rights Reserved. */ /* * 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. */ #include #include #include #include "libpri.h" #include "pri_internal.h" /*! Initial number of scheduled timer slots. */ #define SCHED_EVENTS_INITIAL 128 /*! * \brief Maximum number of scheduled timer slots. * \note Should be a power of 2 and at least SCHED_EVENTS_INITIAL. */ #define SCHED_EVENTS_MAX 8192 /*! \brief The maximum number of timers that were active at once. */ static unsigned maxsched = 0; /*! Last pool id */ static unsigned pool_id = 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); } else { /* Creating the timer pool. */ pool_id += SCHED_EVENTS_MAX; if (pool_id < SCHED_EVENTS_MAX || pool_id + (SCHED_EVENTS_MAX - 1) < SCHED_EVENTS_MAX) { /* * Not likely to happen. * * Timer id's may be aliased if this D channel is used in an * NFAS group with redundant D channels. Another D channel in * the group may have the same pool_id. */ pri_error(ctrl, "Pool_id wrapped. Please ignore if you are not using NFAS with backup D channels.\n"); pool_id = SCHED_EVENTS_MAX; } ctrl->sched.first_id = pool_id; } /* 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. * * \param ctrl D channel controller. * \param ms Number of milliseconds to scheduled event. * \param function Callback function to call when timeout. * \param data Value to give callback function when timeout. * * \retval 0 if scheduler table is full and could not schedule the event. * \retval id Scheduled event id. */ unsigned pri_schedule_event(struct pri *ctrl, int ms, void (*function)(void *data), void *data) { unsigned max_used; unsigned x; struct timeval tv; max_used = ctrl->sched.max_used; for (x = 0; x < max_used; ++x) { if (!ctrl->sched.timer[x].callback) { break; } } 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; } gettimeofday(&tv, NULL); tv.tv_sec += ms / 1000; tv.tv_usec += (ms % 1000) * 1000; if (tv.tv_usec > 1000000) { tv.tv_usec -= 1000000; tv.tv_sec += 1; } ctrl->sched.timer[x].when = tv; ctrl->sched.timer[x].callback = function; ctrl->sched.timer[x].data = data; return ctrl->sched.first_id + x; } /*! * \brief Determine the time of the next scheduled event to expire. * * \param ctrl D channel controller. * * \return Time of the next scheduled event to expire or NULL if no timers active. */ struct timeval *pri_schedule_next(struct pri *ctrl) { struct timeval *closest = NULL; unsigned x; /* 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; } /*! * \internal * \brief Run all expired timers or return an event generated by an expired timer. * * \param ctrl D channel controller. * \param tv Current time. * * \return Event for upper layer to process or NULL if all expired timers run. */ static pri_event *__pri_schedule_run(struct pri *ctrl, struct timeval *tv) { unsigned x; unsigned max_used; void (*callback)(void *); void *data; 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->sched.timer[x].callback; data = ctrl->sched.timer[x].data; ctrl->sched.timer[x].callback = NULL; callback(data); if (ctrl->schedev) { return &ctrl->ev; } } } return NULL; } /*! * \brief Run all expired timers or return an event generated by an expired timer. * * \param ctrl D channel controller. * * \return Event for upper layer to process or NULL if all expired timers run. */ pri_event *pri_schedule_run(struct pri *ctrl) { struct timeval tv; gettimeofday(&tv, NULL); return __pri_schedule_run(ctrl, &tv); } /*! * \brief Delete a scheduled event. * * \param ctrl D channel controller. * \param id Scheduled event id to delete. * 0 is a disabled/unscheduled event id that is ignored. * * \return Nothing */ void pri_schedule_del(struct pri *ctrl, unsigned id) { struct pri *nfas; if (!id) { /* Disabled/unscheduled event id. */ return; } if (ctrl->sched.first_id <= id && id <= ctrl->sched.first_id + (SCHED_EVENTS_MAX - 1)) { ctrl->sched.timer[id - ctrl->sched.first_id].callback = NULL; return; } if (ctrl->nfas) { /* Try to find the timer on another D channel. */ for (nfas = PRI_NFAS_MASTER(ctrl); nfas; nfas = nfas->slave) { if (nfas->sched.first_id <= id && id <= nfas->sched.first_id + (SCHED_EVENTS_MAX - 1)) { nfas->sched.timer[id - nfas->sched.first_id].callback = NULL; return; } } } pri_error(ctrl, "Asked to delete sched id 0x%08x??? first_id=0x%08x, num_slots=0x%08x\n", id, ctrl->sched.first_id, ctrl->sched.num_slots); } /*! * \brief Is the scheduled event this callback. * * \param ctrl D channel controller. * \param id Scheduled event id to check. * 0 is a disabled/unscheduled event id. * \param function Callback function to call when timeout. * \param data Value to give callback function when timeout. * * \return TRUE if scheduled event has the callback. */ int pri_schedule_check(struct pri *ctrl, unsigned id, void (*function)(void *data), void *data) { struct pri *nfas; if (!id) { /* Disabled/unscheduled event id. */ return 0; } if (ctrl->sched.first_id <= id && id <= ctrl->sched.first_id + (SCHED_EVENTS_MAX - 1)) { return ctrl->sched.timer[id - ctrl->sched.first_id].callback == function && ctrl->sched.timer[id - ctrl->sched.first_id].data == data; } if (ctrl->nfas) { /* Try to find the timer on another D channel. */ for (nfas = PRI_NFAS_MASTER(ctrl); nfas; nfas = nfas->slave) { if (nfas->sched.first_id <= id && id <= nfas->sched.first_id + (SCHED_EVENTS_MAX - 1)) { return nfas->sched.timer[id - nfas->sched.first_id].callback == function && nfas->sched.timer[id - nfas->sched.first_id].data == data; } } } pri_error(ctrl, "Asked to check sched id 0x%08x??? first_id=0x%08x, num_slots=0x%08x\n", id, ctrl->sched.first_id, ctrl->sched.num_slots); return 0; } libpri-1.4.14/rose_etsi_diversion.c0000644000000000000000000016532311242637374016025 0ustar rootroot/* * 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 Call diversion operations * * Diversion Supplementary Services ETS 300 207-1 Table 3 * * \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 ServedUserNr 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 served_user_number Served user number 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_ServedUserNumber(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct rosePartyNumber *served_user_number) { if (served_user_number->length) { /* Forward this number */ pos = rose_enc_PartyNumber(ctrl, pos, end, served_user_number); } else { /* Forward all numbers */ pos = asn1_enc_null(pos, end, ASN1_TYPE_NULL); } return pos; } /*! * \internal * \brief Encode the IntResult 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 int_result Forwarding record 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_IntResult(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseEtsiForwardingRecord *int_result) { unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); ASN1_CALL(pos, rose_enc_etsi_ServedUserNumber(ctrl, pos, end, &int_result->served_user_number)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, int_result->basic_service)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, int_result->procedure)); ASN1_CALL(pos, rose_enc_Address(ctrl, pos, end, ASN1_TAG_SEQUENCE, &int_result->forwarded_to)); ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \internal * \brief Encode the IntResultList 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_SET unless the caller implicitly * tags it otherwise. * \param int_result_list Forwarding record 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_IntResultList(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseEtsiForwardingList *int_result_list) { unsigned index; unsigned char *set_len; ASN1_CONSTRUCTED_BEGIN(set_len, pos, end, tag); for (index = 0; index < int_result_list->num_records; ++index) { ASN1_CALL(pos, rose_enc_etsi_IntResult(ctrl, pos, end, ASN1_TAG_SEQUENCE, &int_result_list->list[index])); } ASN1_CONSTRUCTED_END(set_len, pos, end); return pos; } /*! * \internal * \brief Encode the ServedUserNumberList 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_SET unless the caller implicitly * tags it otherwise. * \param served_user_number_list Served user record 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_ServedUserNumberList(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseEtsiServedUserNumberList *served_user_number_list) { unsigned index; unsigned char *set_len; ASN1_CONSTRUCTED_BEGIN(set_len, pos, end, tag); for (index = 0; index < served_user_number_list->num_records; ++index) { ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &served_user_number_list->number[index])); } ASN1_CONSTRUCTED_END(set_len, pos, end); return pos; } /*! * \brief Encode the ActivationDiversion 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_ActivationDiversion_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { const struct roseEtsiActivationDiversion_ARG *activation_diversion; unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); activation_diversion = &args->etsi.ActivationDiversion; ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, activation_diversion->procedure)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, activation_diversion->basic_service)); ASN1_CALL(pos, rose_enc_Address(ctrl, pos, end, ASN1_TAG_SEQUENCE, &activation_diversion->forwarded_to)); ASN1_CALL(pos, rose_enc_etsi_ServedUserNumber(ctrl, pos, end, &activation_diversion->served_user_number)); ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the DeactivationDiversion 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_DeactivationDiversion_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { const struct roseEtsiDeactivationDiversion_ARG *deactivation_diversion; unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); deactivation_diversion = &args->etsi.DeactivationDiversion; ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, deactivation_diversion->procedure)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, deactivation_diversion->basic_service)); ASN1_CALL(pos, rose_enc_etsi_ServedUserNumber(ctrl, pos, end, &deactivation_diversion->served_user_number)); ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the ActivationStatusNotificationDiv 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_ActivationStatusNotificationDiv_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { const struct roseEtsiActivationStatusNotificationDiv_ARG *activation_status_notification_div; unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); activation_status_notification_div = &args->etsi.ActivationStatusNotificationDiv; ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, activation_status_notification_div->procedure)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, activation_status_notification_div->basic_service)); ASN1_CALL(pos, rose_enc_Address(ctrl, pos, end, ASN1_TAG_SEQUENCE, &activation_status_notification_div->forwarded_to)); ASN1_CALL(pos, rose_enc_etsi_ServedUserNumber(ctrl, pos, end, &activation_status_notification_div->served_user_number)); ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the DeactivationStatusNotificationDiv 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_DeactivationStatusNotificationDiv_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { const struct roseEtsiDeactivationStatusNotificationDiv_ARG *deactivation_status_notification_div; unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); deactivation_status_notification_div = &args->etsi.DeactivationStatusNotificationDiv; ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, deactivation_status_notification_div->procedure)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, deactivation_status_notification_div->basic_service)); ASN1_CALL(pos, rose_enc_etsi_ServedUserNumber(ctrl, pos, end, &deactivation_status_notification_div->served_user_number)); ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the InterrogationDiversion 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_InterrogationDiversion_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { const struct roseEtsiInterrogationDiversion_ARG *interrogation_diversion; unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); interrogation_diversion = &args->etsi.InterrogationDiversion; ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, interrogation_diversion->procedure)); if (interrogation_diversion->basic_service) { /* Not the DEFAULT value */ ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, interrogation_diversion->basic_service)); } ASN1_CALL(pos, rose_enc_etsi_ServedUserNumber(ctrl, pos, end, &interrogation_diversion->served_user_number)); ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the InterrogationDiversion 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_InterrogationDiversion_RES(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args) { return rose_enc_etsi_IntResultList(ctrl, pos, end, ASN1_TAG_SET, &args->etsi.InterrogationDiversion); } /*! * \brief Encode the DiversionInformation 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_DiversionInformation_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { const struct roseEtsiDiversionInformation_ARG *diversion_information; unsigned char *seq_len; unsigned char *explicit_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); diversion_information = &args->etsi.DiversionInformation; ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, diversion_information->diversion_reason)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, diversion_information->basic_service)); if (diversion_information->served_user_subaddress.length) { ASN1_CALL(pos, rose_enc_PartySubaddress(ctrl, pos, end, &diversion_information->served_user_subaddress)); } if (diversion_information->calling_present) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0); ASN1_CALL(pos, rose_enc_PresentedAddressScreened(ctrl, pos, end, &diversion_information->calling)); ASN1_CONSTRUCTED_END(explicit_len, pos, end); } if (diversion_information->original_called_present) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1); ASN1_CALL(pos, rose_enc_PresentedNumberUnscreened(ctrl, pos, end, &diversion_information->original_called)); ASN1_CONSTRUCTED_END(explicit_len, pos, end); } if (diversion_information->last_diverting_present) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2); ASN1_CALL(pos, rose_enc_PresentedNumberUnscreened(ctrl, pos, end, &diversion_information->last_diverting)); ASN1_CONSTRUCTED_END(explicit_len, pos, end); } if (diversion_information->last_diverting_reason_present) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, diversion_information->last_diverting_reason)); ASN1_CONSTRUCTED_END(explicit_len, pos, end); } if (diversion_information->q931ie.length) { ASN1_CALL(pos, rose_enc_Q931ie(ctrl, pos, end, ASN1_CLASS_APPLICATION | 0, &diversion_information->q931ie)); } ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the CallDeflection 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_CallDeflection_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { const struct roseEtsiCallDeflection_ARG *call_deflection; unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); call_deflection = &args->etsi.CallDeflection; ASN1_CALL(pos, rose_enc_Address(ctrl, pos, end, ASN1_TAG_SEQUENCE, &call_deflection->deflection)); if (call_deflection->presentation_allowed_to_diverted_to_user_present) { ASN1_CALL(pos, asn1_enc_boolean(pos, end, ASN1_TYPE_BOOLEAN, call_deflection->presentation_allowed_to_diverted_to_user)); } ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the CallRerouting 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_CallRerouting_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { const struct roseEtsiCallRerouting_ARG *call_rerouting; unsigned char *seq_len; unsigned char *explicit_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); call_rerouting = &args->etsi.CallRerouting; ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, call_rerouting->rerouting_reason)); ASN1_CALL(pos, rose_enc_Address(ctrl, pos, end, ASN1_TAG_SEQUENCE, &call_rerouting->called_address)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, call_rerouting->rerouting_counter)); ASN1_CALL(pos, rose_enc_Q931ie(ctrl, pos, end, ASN1_CLASS_APPLICATION | 0, &call_rerouting->q931ie)); /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1); ASN1_CALL(pos, rose_enc_PresentedNumberUnscreened(ctrl, pos, end, &call_rerouting->last_rerouting)); ASN1_CONSTRUCTED_END(explicit_len, pos, end); if (call_rerouting->subscription_option) { /* Not the DEFAULT value */ /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, call_rerouting->subscription_option)); ASN1_CONSTRUCTED_END(explicit_len, pos, end); } if (call_rerouting->calling_subaddress.length) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3); ASN1_CALL(pos, rose_enc_PartySubaddress(ctrl, pos, end, &call_rerouting->calling_subaddress)); ASN1_CONSTRUCTED_END(explicit_len, pos, end); } ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the InterrogateServedUserNumbers 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_InterrogateServedUserNumbers_RES(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args) { return rose_enc_etsi_ServedUserNumberList(ctrl, pos, end, ASN1_TAG_SET, &args->etsi.InterrogateServedUserNumbers); } /*! * \brief Encode the DivertingLegInformation1 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_DivertingLegInformation1_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { const struct roseEtsiDivertingLegInformation1_ARG *diverting_leg_information_1; unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); diverting_leg_information_1 = &args->etsi.DivertingLegInformation1; ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, diverting_leg_information_1->diversion_reason)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, diverting_leg_information_1->subscription_option)); if (diverting_leg_information_1->diverted_to_present) { ASN1_CALL(pos, rose_enc_PresentedNumberUnscreened(ctrl, pos, end, &diverting_leg_information_1->diverted_to)); } ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the DivertingLegInformation2 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_DivertingLegInformation2_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { const struct roseEtsiDivertingLegInformation2_ARG *diverting_leg_information_2; unsigned char *seq_len; unsigned char *explicit_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); diverting_leg_information_2 = &args->etsi.DivertingLegInformation2; ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, diverting_leg_information_2->diversion_counter)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, diverting_leg_information_2->diversion_reason)); if (diverting_leg_information_2->diverting_present) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1); ASN1_CALL(pos, rose_enc_PresentedNumberUnscreened(ctrl, pos, end, &diverting_leg_information_2->diverting)); ASN1_CONSTRUCTED_END(explicit_len, pos, end); } if (diverting_leg_information_2->original_called_present) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2); ASN1_CALL(pos, rose_enc_PresentedNumberUnscreened(ctrl, pos, end, &diverting_leg_information_2->original_called)); ASN1_CONSTRUCTED_END(explicit_len, pos, end); } ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the DivertingLegInformation3 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_DivertingLegInformation3_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { return asn1_enc_boolean(pos, end, ASN1_TYPE_BOOLEAN, args->etsi.DivertingLegInformation3.presentation_allowed_indicator); } /*! * \internal * \brief Decode the ServedUserNr 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 served_user_number 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_ServedUserNumber(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct rosePartyNumber *served_user_number) { if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s ServedUserNumber\n", name); } if (tag == ASN1_TYPE_NULL) { served_user_number->length = 0; pos = asn1_dec_null(ctrl, "allNumbers", tag, pos, end); } else { /* Must be a PartyNumber (Which is itself a CHOICE) */ pos = rose_dec_PartyNumber(ctrl, "individualNumber", tag, pos, end, served_user_number); } return pos; } /*! * \internal * \brief Decode the IntResult 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 int_result 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_IntResult(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseEtsiForwardingRecord *int_result) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s IntResult %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_etsi_ServedUserNumber(ctrl, "servedUserNr", tag, pos, seq_end, &int_result->served_user_number)); 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, "basicService", tag, pos, seq_end, &value)); int_result->basic_service = value; 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, "procedure", tag, pos, seq_end, &value)); int_result->procedure = 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, "forwardedToAddress", tag, pos, seq_end, &int_result->forwarded_to)); ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \internal * \brief Decode the IntResultList 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 int_result_list 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_IntResultList(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseEtsiForwardingList *int_result_list) { int length; int set_offset; const unsigned char *set_end; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s IntResultList %s\n", name, asn1_tag2str(tag)); } ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); ASN1_END_SETUP(set_end, set_offset, length, pos, end); int_result_list->num_records = 0; while (pos < set_end && *pos != ASN1_INDEF_TERM) { if (int_result_list->num_records < ARRAY_LEN(int_result_list->list)) { ASN1_CALL(pos, asn1_dec_tag(pos, set_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); ASN1_CALL(pos, rose_dec_etsi_IntResult(ctrl, "listEntry", tag, pos, set_end, &int_result_list->list[int_result_list->num_records])); ++int_result_list->num_records; } else { /* Too many records */ return NULL; } } ASN1_END_FIXUP(ctrl, pos, set_offset, set_end, end); return pos; } /*! * \internal * \brief Decode the ServedUserNumberList 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 served_user_number_list 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_ServedUserNumberList(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseEtsiServedUserNumberList *served_user_number_list) { int length; int set_offset; const unsigned char *set_end; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s ServedUserNumberList %s\n", name, asn1_tag2str(tag)); } ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); ASN1_END_SETUP(set_end, set_offset, length, pos, end); served_user_number_list->num_records = 0; while (pos < set_end && *pos != ASN1_INDEF_TERM) { if (served_user_number_list->num_records < ARRAY_LEN(served_user_number_list->number)) { ASN1_CALL(pos, asn1_dec_tag(pos, set_end, &tag)); ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "listEntry", tag, pos, set_end, &served_user_number_list->number[served_user_number_list->num_records])); ++served_user_number_list->num_records; } else { /* Too many records */ return NULL; } } ASN1_END_FIXUP(ctrl, pos, set_offset, set_end, end); return pos; } /*! * \brief Decode the ActivationDiversion 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_ActivationDiversion_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { struct roseEtsiActivationDiversion_ARG *activation_diversion; 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, " ActivationDiversion %s\n", asn1_tag2str(tag)); } ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); activation_diversion = &args->etsi.ActivationDiversion; 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, "procedure", tag, pos, seq_end, &value)); activation_diversion->procedure = value; 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, "basicService", tag, pos, seq_end, &value)); activation_diversion->basic_service = 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, "forwardedToAddress", tag, pos, seq_end, &activation_diversion->forwarded_to)); ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CALL(pos, rose_dec_etsi_ServedUserNumber(ctrl, "servedUserNr", tag, pos, seq_end, &activation_diversion->served_user_number)); ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the DeactivationDiversion 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_DeactivationDiversion_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { struct roseEtsiDeactivationDiversion_ARG *deactivation_diversion; 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, " DeactivationDiversion %s\n", asn1_tag2str(tag)); } ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); deactivation_diversion = &args->etsi.DeactivationDiversion; 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, "procedure", tag, pos, seq_end, &value)); deactivation_diversion->procedure = value; 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, "basicService", tag, pos, seq_end, &value)); deactivation_diversion->basic_service = value; ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CALL(pos, rose_dec_etsi_ServedUserNumber(ctrl, "servedUserNr", tag, pos, seq_end, &deactivation_diversion->served_user_number)); ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the ActivationStatusNotificationDiv 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_ActivationStatusNotificationDiv_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { struct roseEtsiActivationStatusNotificationDiv_ARG *activation_status_notification_div; 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, " ActivationStatusNotificationDiv %s\n", asn1_tag2str(tag)); } ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); activation_status_notification_div = &args->etsi.ActivationStatusNotificationDiv; 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, "procedure", tag, pos, seq_end, &value)); activation_status_notification_div->procedure = value; 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, "basicService", tag, pos, seq_end, &value)); activation_status_notification_div->basic_service = 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, "forwardedToAddress", tag, pos, seq_end, &activation_status_notification_div->forwarded_to)); ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CALL(pos, rose_dec_etsi_ServedUserNumber(ctrl, "servedUserNr", tag, pos, seq_end, &activation_status_notification_div->served_user_number)); ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the DeactivationStatusNotificationDiv 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_DeactivationStatusNotificationDiv_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { struct roseEtsiDeactivationStatusNotificationDiv_ARG *deactivation_status_notification_div; 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, " DeactivationStatusNotificationDiv %s\n", asn1_tag2str(tag)); } ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); deactivation_status_notification_div = &args->etsi.DeactivationStatusNotificationDiv; 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, "procedure", tag, pos, seq_end, &value)); deactivation_status_notification_div->procedure = value; 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, "basicService", tag, pos, seq_end, &value)); deactivation_status_notification_div->basic_service = value; ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CALL(pos, rose_dec_etsi_ServedUserNumber(ctrl, "forwardedToAddress", tag, pos, seq_end, &deactivation_status_notification_div->served_user_number)); ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the InterrogationDiversion 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_InterrogationDiversion_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { struct roseEtsiInterrogationDiversion_ARG *interrogation_diversion; 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, " InterrogationDiversion %s\n", asn1_tag2str(tag)); } ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); interrogation_diversion = &args->etsi.InterrogationDiversion; 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, "procedure", tag, pos, seq_end, &value)); interrogation_diversion->procedure = value; ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); if (tag == ASN1_TYPE_ENUMERATED) { ASN1_CALL(pos, asn1_dec_int(ctrl, "basicService", tag, pos, seq_end, &value)); ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); } else { value = 0; /* DEFAULT BasicService value (allServices) */ } interrogation_diversion->basic_service = value; ASN1_CALL(pos, rose_dec_etsi_ServedUserNumber(ctrl, "servedUserNr", tag, pos, seq_end, &interrogation_diversion->served_user_number)); ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the InterrogationDiversion result 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_InterrogationDiversion_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_SET); return rose_dec_etsi_IntResultList(ctrl, "diversionList", tag, pos, end, &args->etsi.InterrogationDiversion); } /*! * \brief Decode the DiversionInformation 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_DiversionInformation_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { struct roseEtsiDiversionInformation_ARG *diversion_information; int length; int seq_offset; int explicit_offset; const unsigned char *seq_end; const unsigned char *explicit_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, " DiversionInformation %s\n", asn1_tag2str(tag)); } ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); diversion_information = &args->etsi.DiversionInformation; 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, "diversionReason", tag, pos, seq_end, &value)); diversion_information->diversion_reason = value; 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, "basicService", tag, pos, seq_end, &value)); diversion_information->basic_service = value; /* * A sequence specifies an ordered list of component types. * However, for simplicity we are not checking the order of * the remaining optional components. */ diversion_information->served_user_subaddress.length = 0; diversion_information->calling_present = 0; diversion_information->original_called_present = 0; diversion_information->last_diverting_present = 0; diversion_information->last_diverting_reason_present = 0; diversion_information->q931ie.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_TAG_SEQUENCE: case ASN1_TYPE_OCTET_STRING: case ASN1_TYPE_OCTET_STRING | ASN1_PC_CONSTRUCTED: ASN1_CALL(pos, rose_dec_PartySubaddress(ctrl, "servedUserSubaddress", tag, pos, seq_end, &diversion_information->served_user_subaddress)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 0: /* 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_PresentedAddressScreened(ctrl, "callingAddress", tag, pos, explicit_end, &diversion_information->calling)); diversion_information->calling_present = 1; ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1: /* 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_PresentedNumberUnscreened(ctrl, "originalCalledNr", tag, pos, explicit_end, &diversion_information->original_called)); diversion_information->original_called_present = 1; ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2: /* 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_PresentedNumberUnscreened(ctrl, "lastDivertingNr", tag, pos, explicit_end, &diversion_information->last_diverting)); diversion_information->last_diverting_present = 1; ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 3: /* 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_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); ASN1_CALL(pos, asn1_dec_int(ctrl, "lastDivertingReason", tag, pos, explicit_end, &value)); diversion_information->last_diverting_reason = value; diversion_information->last_diverting_reason_present = 1; ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); break; case ASN1_CLASS_APPLICATION | 0: case ASN1_CLASS_APPLICATION | ASN1_PC_CONSTRUCTED | 0: ASN1_CALL(pos, rose_dec_Q931ie(ctrl, "userInfo", tag, pos, seq_end, &diversion_information->q931ie, sizeof(diversion_information->q931ie_contents))); 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 CallDeflection 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_CallDeflection_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { struct roseEtsiCallDeflection_ARG *call_deflection; 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, " CallDeflection %s\n", asn1_tag2str(tag)); } ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); call_deflection = &args->etsi.CallDeflection; 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, "deflectionAddress", tag, pos, seq_end, &call_deflection->deflection)); 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_TYPE_BOOLEAN); ASN1_CALL(pos, asn1_dec_boolean(ctrl, "presentationAllowedDivertedToUser", tag, pos, seq_end, &value)); call_deflection->presentation_allowed_to_diverted_to_user = value; call_deflection->presentation_allowed_to_diverted_to_user_present = 1; } else { call_deflection->presentation_allowed_to_diverted_to_user_present = 0; } ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the CallRerouting 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_CallRerouting_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { struct roseEtsiCallRerouting_ARG *call_rerouting; int length; int seq_offset; int explicit_offset; const unsigned char *seq_end; const unsigned char *explicit_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, " CallRerouting %s\n", asn1_tag2str(tag)); } ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); call_rerouting = &args->etsi.CallRerouting; 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, "reroutingReason", tag, pos, seq_end, &value)); call_rerouting->rerouting_reason = 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, "calledAddress", tag, pos, seq_end, &call_rerouting->called_address)); 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, "reroutingCounter", tag, pos, seq_end, &value)); call_rerouting->rerouting_counter = 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, &call_rerouting->q931ie, sizeof(call_rerouting->q931ie_contents))); /* Remove EXPLICIT tag */ ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1); 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_PresentedNumberUnscreened(ctrl, "lastReroutingNr", tag, pos, explicit_end, &call_rerouting->last_rerouting)); ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); /* * A sequence specifies an ordered list of component types. * However, for simplicity we are not checking the order of * the remaining optional components. */ call_rerouting->subscription_option = 0; /* DEFAULT value noNotification */ call_rerouting->calling_subaddress.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 | 2: /* 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_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); ASN1_CALL(pos, asn1_dec_int(ctrl, "subscriptionOption", tag, pos, explicit_end, &value)); call_rerouting->subscription_option = value; ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 3: /* 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, "callingPartySubaddress", tag, pos, explicit_end, &call_rerouting->calling_subaddress)); ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, 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 InterrogateServedUserNumbers result 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_InterrogateServedUserNumbers_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_SET); return rose_dec_etsi_ServedUserNumberList(ctrl, "interrogateServedUserNumbers", tag, pos, end, &args->etsi.InterrogateServedUserNumbers); } /*! * \brief Decode the DivertingLegInformation1 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_DivertingLegInformation1_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { struct roseEtsiDivertingLegInformation1_ARG *diverting_leg_informtion_1; 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, " DivertingLegInformation1 %s\n", asn1_tag2str(tag)); } ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); diverting_leg_informtion_1 = &args->etsi.DivertingLegInformation1; 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, "diversionReason", tag, pos, seq_end, &value)); diverting_leg_informtion_1->diversion_reason = value; 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, "subscriptionOption", tag, pos, seq_end, &value)); diverting_leg_informtion_1->subscription_option = value; if (pos < seq_end && *pos != ASN1_INDEF_TERM) { ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CALL(pos, rose_dec_PresentedNumberUnscreened(ctrl, "divertedToNumber", tag, pos, seq_end, &diverting_leg_informtion_1->diverted_to)); diverting_leg_informtion_1->diverted_to_present = 1; } else { diverting_leg_informtion_1->diverted_to_present = 0; } ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the DivertingLegInformation2 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_DivertingLegInformation2_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { struct roseEtsiDivertingLegInformation2_ARG *diverting_leg_information_2; int length; int seq_offset; int explicit_offset; const unsigned char *seq_end; const unsigned char *explicit_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, " DivertingLegInformation2 %s\n", asn1_tag2str(tag)); } ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); diverting_leg_information_2 = &args->etsi.DivertingLegInformation2; 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, "diversionCounter", tag, pos, seq_end, &value)); diverting_leg_information_2->diversion_counter = value; 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, "diversionReason", tag, pos, seq_end, &value)); diverting_leg_information_2->diversion_reason = value; /* * A sequence specifies an ordered list of component types. * However, for simplicity we are not checking the order of * the remaining optional components. */ diverting_leg_information_2->diverting_present = 0; diverting_leg_information_2->original_called_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 | 1: /* 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_PresentedNumberUnscreened(ctrl, "divertingNr", tag, pos, explicit_end, &diverting_leg_information_2->diverting)); diverting_leg_information_2->diverting_present = 1; ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2: /* 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_PresentedNumberUnscreened(ctrl, "originalCalledNr", tag, pos, explicit_end, &diverting_leg_information_2->original_called)); diverting_leg_information_2->original_called_present = 1; ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, 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 DivertingLegInformation3 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_DivertingLegInformation3_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_BOOLEAN); ASN1_CALL(pos, asn1_dec_boolean(ctrl, "presentationAllowedIndicator", tag, pos, end, &value)); args->etsi.DivertingLegInformation3.presentation_allowed_indicator = value; return pos; } /* ------------------------------------------------------------------- */ /* end rose_etsi_diversion.c */ libpri-1.4.14/rosetest.c0000644000000000000000000043024411435757651013621 0ustar rootroot/* * 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 encode/decode test program * * \author Richard Mudgett */ #include "compat.h" #include "libpri.h" #include "pri_internal.h" #include "rose.h" #include #include /* ------------------------------------------------------------------- */ static const struct fac_extension_header fac_headers[] = { /* *INDENT-OFF* */ { .nfe_present = 0, }, { .nfe_present = 1, .nfe.source_entity = 1, .nfe.destination_entity = 1, }, { .nfe_present = 1, .nfe.source_entity = 1, .nfe.source_number.plan = 4, .nfe.source_number.length = 4, .nfe.source_number.str = "9834", .nfe.destination_entity = 1, .nfe.destination_number.plan = 4, .nfe.destination_number.length = 4, .nfe.destination_number.str = "9834", }, { .nfe_present = 1, .nfe.source_entity = 1, .nfe.destination_entity = 1, .npp_present = 1, .npp = 19, .interpretation_present = 1, .interpretation = 2, }, /* *INDENT-ON* */ }; static const struct rose_message rose_etsi_msgs[] = { /* *INDENT-OFF* */ /* Error messages */ { .type = ROSE_COMP_TYPE_ERROR, .component.error.invoke_id = 82, .component.error.code = ROSE_ERROR_Div_SpecialServiceNr, }, { .type = ROSE_COMP_TYPE_ERROR, .component.error.invoke_id = 8, .component.error.code = ROSE_ERROR_ECT_LinkIdNotAssignedByNetwork, }, /* Reject messages */ { .type = ROSE_COMP_TYPE_REJECT, .component.reject.code = ROSE_REJECT_Gen_BadlyStructuredComponent, }, { .type = ROSE_COMP_TYPE_REJECT, .component.reject.invoke_id_present = 1, .component.reject.invoke_id = 10, .component.reject.code = ROSE_REJECT_Inv_InitiatorReleasing, }, { .type = ROSE_COMP_TYPE_REJECT, .component.reject.invoke_id_present = 1, .component.reject.invoke_id = 11, .component.reject.code = ROSE_REJECT_Res_MistypedResult, }, { .type = ROSE_COMP_TYPE_REJECT, .component.reject.invoke_id_present = 1, .component.reject.invoke_id = 12, .component.reject.code = ROSE_REJECT_Err_ErrorResponseUnexpected, }, /* Anonymous result or result without any arguments. */ { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_None, .component.result.invoke_id = 9, }, /* Advice Of Charge (AOC) */ { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_ChargingRequest, .component.invoke.invoke_id = 98, .component.invoke.args.etsi.ChargingRequest.charging_case = 2, }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_ETSI_ChargingRequest, .component.result.invoke_id = 99, .component.result.args.etsi.ChargingRequest.type = 0, .component.result.args.etsi.ChargingRequest.u.currency_info.num_records = 1, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].charged_item = 4, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].currency_type = 0, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.special_charging_code = 3, }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_ETSI_ChargingRequest, .component.result.invoke_id = 100, .component.result.args.etsi.ChargingRequest.type = 0, .component.result.args.etsi.ChargingRequest.u.currency_info.num_records = 1, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].charged_item = 4, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].currency_type = 1, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.duration.currency = "Dollars", .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.duration.amount.currency = 7, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.duration.amount.multiplier = 1, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.duration.charging_type = 1, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.duration.time.length = 8, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.duration.time.scale = 4, }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_ETSI_ChargingRequest, .component.result.invoke_id = 101, .component.result.args.etsi.ChargingRequest.type = 0, .component.result.args.etsi.ChargingRequest.u.currency_info.num_records = 1, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].charged_item = 4, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].currency_type = 1, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.duration.currency = "Dollars", .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.duration.amount.currency = 7, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.duration.amount.multiplier = 1, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.duration.charging_type = 1, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.duration.time.length = 8, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.duration.time.scale = 4, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.duration.granularity_present = 1, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.duration.granularity.length = 20, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.duration.granularity.scale = 3, }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_ETSI_ChargingRequest, .component.result.invoke_id = 102, .component.result.args.etsi.ChargingRequest.type = 0, .component.result.args.etsi.ChargingRequest.u.currency_info.num_records = 1, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].charged_item = 4, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].currency_type = 2, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.flat_rate.currency = "Euros", .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.flat_rate.amount.currency = 4, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.flat_rate.amount.multiplier = 1, }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_ETSI_ChargingRequest, .component.result.invoke_id = 103, .component.result.args.etsi.ChargingRequest.type = 0, .component.result.args.etsi.ChargingRequest.u.currency_info.num_records = 1, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].charged_item = 4, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].currency_type = 3, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.volume_rate.currency = "Yen", .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.volume_rate.amount.currency = 300, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.volume_rate.amount.multiplier = 5, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.volume_rate.unit = 2, }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_ETSI_ChargingRequest, .component.result.invoke_id = 104, .component.result.args.etsi.ChargingRequest.type = 0, .component.result.args.etsi.ChargingRequest.u.currency_info.num_records = 2, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].charged_item = 4, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].currency_type = 2, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.flat_rate.currency = "Euros", .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.flat_rate.amount.currency = 4, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.flat_rate.amount.multiplier = 1, .component.result.args.etsi.ChargingRequest.u.currency_info.list[1].charged_item = 4, .component.result.args.etsi.ChargingRequest.u.currency_info.list[1].currency_type = 3, .component.result.args.etsi.ChargingRequest.u.currency_info.list[1].u.volume_rate.currency = "Yen", .component.result.args.etsi.ChargingRequest.u.currency_info.list[1].u.volume_rate.amount.currency = 300, .component.result.args.etsi.ChargingRequest.u.currency_info.list[1].u.volume_rate.amount.multiplier = 5, .component.result.args.etsi.ChargingRequest.u.currency_info.list[1].u.volume_rate.unit = 2, }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_ETSI_ChargingRequest, .component.result.invoke_id = 105, .component.result.args.etsi.ChargingRequest.type = 0, .component.result.args.etsi.ChargingRequest.u.currency_info.num_records = 1, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].charged_item = 4, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].currency_type = 4, }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_ETSI_ChargingRequest, .component.result.invoke_id = 106, .component.result.args.etsi.ChargingRequest.type = 0, .component.result.args.etsi.ChargingRequest.u.currency_info.num_records = 1, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].charged_item = 4, .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].currency_type = 5, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_AOCSCurrency, .component.invoke.invoke_id = 107, .component.invoke.args.etsi.AOCSCurrency.type = 0, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_AOCSCurrency, .component.invoke.invoke_id = 108, .component.invoke.args.etsi.AOCSCurrency.type = 1, .component.invoke.args.etsi.AOCSCurrency.currency_info.num_records = 1, .component.invoke.args.etsi.AOCSCurrency.currency_info.list[0].charged_item = 3, .component.invoke.args.etsi.AOCSCurrency.currency_info.list[0].currency_type = 4, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_AOCSSpecialArr, .component.invoke.invoke_id = 109, .component.invoke.args.etsi.AOCSSpecialArr.type = 0, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_AOCSSpecialArr, .component.invoke.invoke_id = 110, .component.invoke.args.etsi.AOCSSpecialArr.type = 1, .component.invoke.args.etsi.AOCSSpecialArr.special_arrangement = 9, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_AOCDCurrency, .component.invoke.invoke_id = 111, .component.invoke.args.etsi.AOCDCurrency.type = 0, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_AOCDCurrency, .component.invoke.invoke_id = 112, .component.invoke.args.etsi.AOCDCurrency.type = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_AOCDCurrency, .component.invoke.invoke_id = 113, .component.invoke.args.etsi.AOCDCurrency.type = 2, .component.invoke.args.etsi.AOCDCurrency.specific.recorded.currency = "Francs", .component.invoke.args.etsi.AOCDCurrency.specific.recorded.amount.currency = 674, .component.invoke.args.etsi.AOCDCurrency.specific.recorded.amount.multiplier = 3, .component.invoke.args.etsi.AOCDCurrency.specific.type_of_charging_info = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_AOCDCurrency, .component.invoke.invoke_id = 114, .component.invoke.args.etsi.AOCDCurrency.type = 2, .component.invoke.args.etsi.AOCDCurrency.specific.recorded.currency = "Francs", .component.invoke.args.etsi.AOCDCurrency.specific.recorded.amount.currency = 674, .component.invoke.args.etsi.AOCDCurrency.specific.recorded.amount.multiplier = 3, .component.invoke.args.etsi.AOCDCurrency.specific.type_of_charging_info = 1, .component.invoke.args.etsi.AOCDCurrency.specific.billing_id_present = 1, .component.invoke.args.etsi.AOCDCurrency.specific.billing_id = 2, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_AOCDChargingUnit, .component.invoke.invoke_id = 115, .component.invoke.args.etsi.AOCDChargingUnit.type = 0, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_AOCDChargingUnit, .component.invoke.invoke_id = 116, .component.invoke.args.etsi.AOCDChargingUnit.type = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_AOCDChargingUnit, .component.invoke.invoke_id = 117, .component.invoke.args.etsi.AOCDChargingUnit.type = 2, .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.num_records = 1, .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.list[0].not_available = 1, .component.invoke.args.etsi.AOCDChargingUnit.specific.type_of_charging_info = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_AOCDChargingUnit, .component.invoke.invoke_id = 118, .component.invoke.args.etsi.AOCDChargingUnit.type = 2, .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.num_records = 1, .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.list[0].not_available = 0, .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.list[0].number_of_units = 8523, .component.invoke.args.etsi.AOCDChargingUnit.specific.type_of_charging_info = 1, .component.invoke.args.etsi.AOCDChargingUnit.specific.billing_id_present = 1, .component.invoke.args.etsi.AOCDChargingUnit.specific.billing_id = 2, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_AOCDChargingUnit, .component.invoke.invoke_id = 119, .component.invoke.args.etsi.AOCDChargingUnit.type = 2, .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.num_records = 1, .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.list[0].not_available = 1, .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.list[0].type_of_unit_present = 1, .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.list[0].type_of_unit = 13, .component.invoke.args.etsi.AOCDChargingUnit.specific.type_of_charging_info = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_AOCDChargingUnit, .component.invoke.invoke_id = 120, .component.invoke.args.etsi.AOCDChargingUnit.type = 2, .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.num_records = 1, .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.list[0].not_available = 0, .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.list[0].number_of_units = 8523, .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.list[0].type_of_unit_present = 1, .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.list[0].type_of_unit = 13, .component.invoke.args.etsi.AOCDChargingUnit.specific.type_of_charging_info = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_AOCDChargingUnit, .component.invoke.invoke_id = 121, .component.invoke.args.etsi.AOCDChargingUnit.type = 2, .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.num_records = 2, .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.list[0].not_available = 1, .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.list[1].not_available = 0, .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.list[1].number_of_units = 8523, .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.list[1].type_of_unit_present = 1, .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.list[1].type_of_unit = 13, .component.invoke.args.etsi.AOCDChargingUnit.specific.type_of_charging_info = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_AOCECurrency, .component.invoke.invoke_id = 122, .component.invoke.args.etsi.AOCECurrency.type = 0, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_AOCECurrency, .component.invoke.invoke_id = 123, .component.invoke.args.etsi.AOCECurrency.type = 1, .component.invoke.args.etsi.AOCECurrency.currency_info.free_of_charge = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_AOCECurrency, .component.invoke.invoke_id = 124, .component.invoke.args.etsi.AOCECurrency.type = 1, .component.invoke.args.etsi.AOCECurrency.currency_info.free_of_charge = 1, .component.invoke.args.etsi.AOCECurrency.currency_info.charging_association_present = 1, .component.invoke.args.etsi.AOCECurrency.currency_info.charging_association.type = 0, .component.invoke.args.etsi.AOCECurrency.currency_info.charging_association.id = -37, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_AOCECurrency, .component.invoke.invoke_id = 125, .component.invoke.args.etsi.AOCECurrency.type = 1, .component.invoke.args.etsi.AOCECurrency.currency_info.free_of_charge = 1, .component.invoke.args.etsi.AOCECurrency.currency_info.charging_association_present = 1, .component.invoke.args.etsi.AOCECurrency.currency_info.charging_association.type = 1, .component.invoke.args.etsi.AOCECurrency.currency_info.charging_association.number.plan = 0, .component.invoke.args.etsi.AOCECurrency.currency_info.charging_association.number.length = 7, .component.invoke.args.etsi.AOCECurrency.currency_info.charging_association.number.str = "5551212", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_AOCECurrency, .component.invoke.invoke_id = 126, .component.invoke.args.etsi.AOCECurrency.type = 1, .component.invoke.args.etsi.AOCECurrency.currency_info.free_of_charge = 0, .component.invoke.args.etsi.AOCECurrency.currency_info.specific.recorded.currency = "Francs", .component.invoke.args.etsi.AOCECurrency.currency_info.specific.recorded.amount.currency = 674, .component.invoke.args.etsi.AOCECurrency.currency_info.specific.recorded.amount.multiplier = 3, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_AOCECurrency, .component.invoke.invoke_id = 127, .component.invoke.args.etsi.AOCECurrency.type = 1, .component.invoke.args.etsi.AOCECurrency.currency_info.free_of_charge = 0, .component.invoke.args.etsi.AOCECurrency.currency_info.specific.recorded.currency = "Francs", .component.invoke.args.etsi.AOCECurrency.currency_info.specific.recorded.amount.currency = 674, .component.invoke.args.etsi.AOCECurrency.currency_info.specific.recorded.amount.multiplier = 3, .component.invoke.args.etsi.AOCECurrency.currency_info.charging_association_present = 1, .component.invoke.args.etsi.AOCECurrency.currency_info.charging_association.type = 0, .component.invoke.args.etsi.AOCECurrency.currency_info.charging_association.id = -37, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_AOCECurrency, .component.invoke.invoke_id = 128, .component.invoke.args.etsi.AOCECurrency.type = 1, .component.invoke.args.etsi.AOCECurrency.currency_info.free_of_charge = 0, .component.invoke.args.etsi.AOCECurrency.currency_info.specific.recorded.currency = "Francs", .component.invoke.args.etsi.AOCECurrency.currency_info.specific.recorded.amount.currency = 674, .component.invoke.args.etsi.AOCECurrency.currency_info.specific.recorded.amount.multiplier = 3, .component.invoke.args.etsi.AOCECurrency.currency_info.specific.billing_id_present = 1, .component.invoke.args.etsi.AOCECurrency.currency_info.specific.billing_id = 2, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_AOCECurrency, .component.invoke.invoke_id = 129, .component.invoke.args.etsi.AOCECurrency.type = 1, .component.invoke.args.etsi.AOCECurrency.currency_info.free_of_charge = 0, .component.invoke.args.etsi.AOCECurrency.currency_info.specific.recorded.currency = "Francs", .component.invoke.args.etsi.AOCECurrency.currency_info.specific.recorded.amount.currency = 674, .component.invoke.args.etsi.AOCECurrency.currency_info.specific.recorded.amount.multiplier = 3, .component.invoke.args.etsi.AOCECurrency.currency_info.specific.billing_id_present = 1, .component.invoke.args.etsi.AOCECurrency.currency_info.specific.billing_id = 2, .component.invoke.args.etsi.AOCECurrency.currency_info.charging_association_present = 1, .component.invoke.args.etsi.AOCECurrency.currency_info.charging_association.type = 0, .component.invoke.args.etsi.AOCECurrency.currency_info.charging_association.id = -37, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_AOCEChargingUnit, .component.invoke.invoke_id = 130, .component.invoke.args.etsi.AOCEChargingUnit.type = 0, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_AOCEChargingUnit, .component.invoke.invoke_id = 131, .component.invoke.args.etsi.AOCEChargingUnit.type = 1, .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.free_of_charge = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_AOCEChargingUnit, .component.invoke.invoke_id = 132, .component.invoke.args.etsi.AOCEChargingUnit.type = 1, .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.free_of_charge = 1, .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.charging_association_present = 1, .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.charging_association.type = 0, .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.charging_association.id = -37, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_AOCEChargingUnit, .component.invoke.invoke_id = 133, .component.invoke.args.etsi.AOCEChargingUnit.type = 1, .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.free_of_charge = 0, .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.specific.recorded.num_records = 1, .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.specific.recorded.list[0].not_available = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_AOCEChargingUnit, .component.invoke.invoke_id = 134, .component.invoke.args.etsi.AOCEChargingUnit.type = 1, .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.free_of_charge = 0, .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.specific.recorded.num_records = 1, .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.specific.recorded.list[0].not_available = 1, .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.charging_association_present = 1, .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.charging_association.type = 0, .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.charging_association.id = -37, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_AOCEChargingUnit, .component.invoke.invoke_id = 135, .component.invoke.args.etsi.AOCEChargingUnit.type = 1, .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.free_of_charge = 0, .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.specific.recorded.num_records = 1, .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.specific.recorded.list[0].not_available = 1, .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.specific.billing_id_present = 1, .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.specific.billing_id = 2, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_AOCEChargingUnit, .component.invoke.invoke_id = 136, .component.invoke.args.etsi.AOCEChargingUnit.type = 1, .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.free_of_charge = 0, .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.specific.recorded.num_records = 1, .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.specific.recorded.list[0].not_available = 1, .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.specific.billing_id_present = 1, .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.specific.billing_id = 2, .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.charging_association_present = 1, .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.charging_association.type = 0, .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.charging_association.id = -37, }, /* Call diversion */ { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_ActivationDiversion, .component.invoke.invoke_id = 67, .component.invoke.linked_id_present = 1, .component.invoke.linked_id = 27, .component.invoke.args.etsi.ActivationDiversion.procedure = 2, .component.invoke.args.etsi.ActivationDiversion.basic_service = 3, .component.invoke.args.etsi.ActivationDiversion.forwarded_to.number.plan = 4, .component.invoke.args.etsi.ActivationDiversion.forwarded_to.number.length = 4, .component.invoke.args.etsi.ActivationDiversion.forwarded_to.number.str = "1803", .component.invoke.args.etsi.ActivationDiversion.served_user_number.plan = 4, .component.invoke.args.etsi.ActivationDiversion.served_user_number.length = 4, .component.invoke.args.etsi.ActivationDiversion.served_user_number.str = "5398", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_ActivationDiversion, .component.invoke.invoke_id = 68, .component.invoke.args.etsi.ActivationDiversion.procedure = 1, .component.invoke.args.etsi.ActivationDiversion.basic_service = 5, .component.invoke.args.etsi.ActivationDiversion.forwarded_to.number.plan = 4, .component.invoke.args.etsi.ActivationDiversion.forwarded_to.number.length = 4, .component.invoke.args.etsi.ActivationDiversion.forwarded_to.number.str = "1803", }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_ETSI_ActivationDiversion, .component.result.invoke_id = 69, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_DeactivationDiversion, .component.invoke.invoke_id = 70, .component.invoke.args.etsi.DeactivationDiversion.procedure = 1, .component.invoke.args.etsi.DeactivationDiversion.basic_service = 5, }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_ETSI_DeactivationDiversion, .component.result.invoke_id = 71, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_ActivationStatusNotificationDiv, .component.invoke.invoke_id = 72, .component.invoke.args.etsi.ActivationStatusNotificationDiv.procedure = 1, .component.invoke.args.etsi.ActivationStatusNotificationDiv.basic_service = 5, .component.invoke.args.etsi.ActivationStatusNotificationDiv.forwarded_to.number.plan = 4, .component.invoke.args.etsi.ActivationStatusNotificationDiv.forwarded_to.number.length = 4, .component.invoke.args.etsi.ActivationStatusNotificationDiv.forwarded_to.number.str = "1803", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_DeactivationStatusNotificationDiv, .component.invoke.invoke_id = 73, .component.invoke.args.etsi.DeactivationStatusNotificationDiv.procedure = 1, .component.invoke.args.etsi.DeactivationStatusNotificationDiv.basic_service = 5, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_InterrogationDiversion, .component.invoke.invoke_id = 74, .component.invoke.args.etsi.InterrogationDiversion.procedure = 1, .component.invoke.args.etsi.InterrogationDiversion.basic_service = 5, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_InterrogationDiversion, .component.invoke.invoke_id = 75, .component.invoke.args.etsi.InterrogationDiversion.procedure = 1, }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_ETSI_InterrogationDiversion, .component.result.invoke_id = 76, .component.result.args.etsi.InterrogationDiversion.num_records = 2, .component.result.args.etsi.InterrogationDiversion.list[0].procedure = 2, .component.result.args.etsi.InterrogationDiversion.list[0].basic_service = 5, .component.result.args.etsi.InterrogationDiversion.list[0].forwarded_to.number.plan = 4, .component.result.args.etsi.InterrogationDiversion.list[0].forwarded_to.number.length = 4, .component.result.args.etsi.InterrogationDiversion.list[0].forwarded_to.number.str = "1803", .component.result.args.etsi.InterrogationDiversion.list[1].procedure = 1, .component.result.args.etsi.InterrogationDiversion.list[1].basic_service = 3, .component.result.args.etsi.InterrogationDiversion.list[1].forwarded_to.number.plan = 4, .component.result.args.etsi.InterrogationDiversion.list[1].forwarded_to.number.length = 4, .component.result.args.etsi.InterrogationDiversion.list[1].forwarded_to.number.str = "1903", .component.result.args.etsi.InterrogationDiversion.list[1].served_user_number.plan = 4, .component.result.args.etsi.InterrogationDiversion.list[1].served_user_number.length = 4, .component.result.args.etsi.InterrogationDiversion.list[1].served_user_number.str = "5398", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_DiversionInformation, .component.invoke.invoke_id = 77, .component.invoke.args.etsi.DiversionInformation.diversion_reason = 3, .component.invoke.args.etsi.DiversionInformation.basic_service = 5, .component.invoke.args.etsi.DiversionInformation.served_user_subaddress.type = 1, .component.invoke.args.etsi.DiversionInformation.served_user_subaddress.length = 4, .component.invoke.args.etsi.DiversionInformation.served_user_subaddress.u.nsap = "6492", .component.invoke.args.etsi.DiversionInformation.calling_present = 1, .component.invoke.args.etsi.DiversionInformation.calling.presentation = 0, .component.invoke.args.etsi.DiversionInformation.calling.screened.screening_indicator = 3, .component.invoke.args.etsi.DiversionInformation.calling.screened.number.plan = 4, .component.invoke.args.etsi.DiversionInformation.calling.screened.number.length = 4, .component.invoke.args.etsi.DiversionInformation.calling.screened.number.str = "1803", .component.invoke.args.etsi.DiversionInformation.original_called_present = 1, .component.invoke.args.etsi.DiversionInformation.original_called.presentation = 1, .component.invoke.args.etsi.DiversionInformation.last_diverting_present = 1, .component.invoke.args.etsi.DiversionInformation.last_diverting.presentation = 2, .component.invoke.args.etsi.DiversionInformation.last_diverting_reason_present = 1, .component.invoke.args.etsi.DiversionInformation.last_diverting_reason = 3, .component.invoke.args.etsi.DiversionInformation.q931ie.length = 5, .component.invoke.args.etsi.DiversionInformation.q931ie_contents = "79828", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_DiversionInformation, .component.invoke.invoke_id = 78, .component.invoke.args.etsi.DiversionInformation.diversion_reason = 3, .component.invoke.args.etsi.DiversionInformation.basic_service = 5, .component.invoke.args.etsi.DiversionInformation.calling_present = 1, .component.invoke.args.etsi.DiversionInformation.calling.presentation = 1, .component.invoke.args.etsi.DiversionInformation.original_called_present = 1, .component.invoke.args.etsi.DiversionInformation.original_called.presentation = 2, .component.invoke.args.etsi.DiversionInformation.last_diverting_present = 1, .component.invoke.args.etsi.DiversionInformation.last_diverting.presentation = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_DiversionInformation, .component.invoke.invoke_id = 79, .component.invoke.args.etsi.DiversionInformation.diversion_reason = 2, .component.invoke.args.etsi.DiversionInformation.basic_service = 3, .component.invoke.args.etsi.DiversionInformation.calling_present = 1, .component.invoke.args.etsi.DiversionInformation.calling.presentation = 2, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_DiversionInformation, .component.invoke.invoke_id = 80, .component.invoke.args.etsi.DiversionInformation.diversion_reason = 3, .component.invoke.args.etsi.DiversionInformation.basic_service = 5, .component.invoke.args.etsi.DiversionInformation.calling_present = 1, .component.invoke.args.etsi.DiversionInformation.calling.presentation = 3, .component.invoke.args.etsi.DiversionInformation.calling.screened.screening_indicator = 2, .component.invoke.args.etsi.DiversionInformation.calling.screened.number.plan = 4, .component.invoke.args.etsi.DiversionInformation.calling.screened.number.length = 4, .component.invoke.args.etsi.DiversionInformation.calling.screened.number.str = "1803", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_DiversionInformation, .component.invoke.invoke_id = 81, .component.invoke.args.etsi.DiversionInformation.diversion_reason = 2, .component.invoke.args.etsi.DiversionInformation.basic_service = 4, .component.invoke.args.etsi.DiversionInformation.q931ie.length = 5, .component.invoke.args.etsi.DiversionInformation.q931ie_contents = "79828", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_DiversionInformation, .component.invoke.invoke_id = 82, .component.invoke.args.etsi.DiversionInformation.diversion_reason = 2, .component.invoke.args.etsi.DiversionInformation.basic_service = 4, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_CallDeflection, .component.invoke.invoke_id = 83, .component.invoke.args.etsi.CallDeflection.deflection.number.plan = 4, .component.invoke.args.etsi.CallDeflection.deflection.number.length = 4, .component.invoke.args.etsi.CallDeflection.deflection.number.str = "1803", .component.invoke.args.etsi.CallDeflection.presentation_allowed_to_diverted_to_user_present = 1, .component.invoke.args.etsi.CallDeflection.presentation_allowed_to_diverted_to_user = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_CallDeflection, .component.invoke.invoke_id = 84, .component.invoke.args.etsi.CallDeflection.deflection.number.plan = 4, .component.invoke.args.etsi.CallDeflection.deflection.number.length = 4, .component.invoke.args.etsi.CallDeflection.deflection.number.str = "1803", .component.invoke.args.etsi.CallDeflection.presentation_allowed_to_diverted_to_user_present = 1, .component.invoke.args.etsi.CallDeflection.presentation_allowed_to_diverted_to_user = 0, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_CallDeflection, .component.invoke.invoke_id = 85, .component.invoke.args.etsi.CallDeflection.deflection.number.plan = 4, .component.invoke.args.etsi.CallDeflection.deflection.number.length = 4, .component.invoke.args.etsi.CallDeflection.deflection.number.str = "1803", }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_ETSI_CallDeflection, .component.result.invoke_id = 86, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_CallRerouting, .component.invoke.invoke_id = 87, .component.invoke.args.etsi.CallRerouting.rerouting_reason = 3, .component.invoke.args.etsi.CallRerouting.rerouting_counter = 2, .component.invoke.args.etsi.CallRerouting.called_address.number.plan = 4, .component.invoke.args.etsi.CallRerouting.called_address.number.length = 4, .component.invoke.args.etsi.CallRerouting.called_address.number.str = "1803", .component.invoke.args.etsi.CallRerouting.q931ie.length = 129, .component.invoke.args.etsi.CallRerouting.q931ie_contents = "YEHAW." " The quick brown fox jumped over the lazy dog test." " Now is the time for all good men to come to the aid of their country.", .component.invoke.args.etsi.CallRerouting.last_rerouting.presentation = 1, .component.invoke.args.etsi.CallRerouting.subscription_option = 2, .component.invoke.args.etsi.CallRerouting.calling_subaddress.type = 1, .component.invoke.args.etsi.CallRerouting.calling_subaddress.length = 4, .component.invoke.args.etsi.CallRerouting.calling_subaddress.u.nsap = "6492", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_CallRerouting, .component.invoke.invoke_id = 88, .component.invoke.args.etsi.CallRerouting.rerouting_reason = 3, .component.invoke.args.etsi.CallRerouting.rerouting_counter = 2, .component.invoke.args.etsi.CallRerouting.called_address.number.plan = 4, .component.invoke.args.etsi.CallRerouting.called_address.number.length = 4, .component.invoke.args.etsi.CallRerouting.called_address.number.str = "1803", .component.invoke.args.etsi.CallRerouting.q931ie.length = 2, .component.invoke.args.etsi.CallRerouting.q931ie_contents = "RT", .component.invoke.args.etsi.CallRerouting.last_rerouting.presentation = 1, .component.invoke.args.etsi.CallRerouting.subscription_option = 2, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_CallRerouting, .component.invoke.invoke_id = 89, .component.invoke.args.etsi.CallRerouting.rerouting_reason = 3, .component.invoke.args.etsi.CallRerouting.rerouting_counter = 2, .component.invoke.args.etsi.CallRerouting.called_address.number.plan = 4, .component.invoke.args.etsi.CallRerouting.called_address.number.length = 4, .component.invoke.args.etsi.CallRerouting.called_address.number.str = "1803", .component.invoke.args.etsi.CallRerouting.q931ie.length = 2, .component.invoke.args.etsi.CallRerouting.q931ie_contents = "RT", .component.invoke.args.etsi.CallRerouting.last_rerouting.presentation = 2, }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_ETSI_CallRerouting, .component.result.invoke_id = 90, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_InterrogateServedUserNumbers, .component.invoke.invoke_id = 91, }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_ETSI_InterrogateServedUserNumbers, .component.result.invoke_id = 92, .component.result.args.etsi.InterrogateServedUserNumbers.num_records = 2, .component.result.args.etsi.InterrogateServedUserNumbers.number[0].plan = 4, .component.result.args.etsi.InterrogateServedUserNumbers.number[0].length = 4, .component.result.args.etsi.InterrogateServedUserNumbers.number[0].str = "1803", .component.result.args.etsi.InterrogateServedUserNumbers.number[1].plan = 4, .component.result.args.etsi.InterrogateServedUserNumbers.number[1].length = 4, .component.result.args.etsi.InterrogateServedUserNumbers.number[1].str = "5786", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_DivertingLegInformation1, .component.invoke.invoke_id = 93, .component.invoke.args.etsi.DivertingLegInformation1.diversion_reason = 4, .component.invoke.args.etsi.DivertingLegInformation1.subscription_option = 1, .component.invoke.args.etsi.DivertingLegInformation1.diverted_to_present = 1, .component.invoke.args.etsi.DivertingLegInformation1.diverted_to.presentation = 2, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_DivertingLegInformation1, .component.invoke.invoke_id = 94, .component.invoke.args.etsi.DivertingLegInformation1.diversion_reason = 4, .component.invoke.args.etsi.DivertingLegInformation1.subscription_option = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_DivertingLegInformation2, .component.invoke.invoke_id = 95, .component.invoke.args.etsi.DivertingLegInformation2.diversion_counter = 3, .component.invoke.args.etsi.DivertingLegInformation2.diversion_reason = 2, .component.invoke.args.etsi.DivertingLegInformation2.diverting_present = 1, .component.invoke.args.etsi.DivertingLegInformation2.diverting.presentation = 2, .component.invoke.args.etsi.DivertingLegInformation2.original_called_present = 1, .component.invoke.args.etsi.DivertingLegInformation2.original_called.presentation = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_DivertingLegInformation2, .component.invoke.invoke_id = 96, .component.invoke.args.etsi.DivertingLegInformation2.diversion_counter = 3, .component.invoke.args.etsi.DivertingLegInformation2.diversion_reason = 2, .component.invoke.args.etsi.DivertingLegInformation2.original_called_present = 1, .component.invoke.args.etsi.DivertingLegInformation2.original_called.presentation = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_DivertingLegInformation2, .component.invoke.invoke_id = 97, .component.invoke.args.etsi.DivertingLegInformation2.diversion_counter = 1, .component.invoke.args.etsi.DivertingLegInformation2.diversion_reason = 2, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_DivertingLegInformation3, .component.invoke.invoke_id = 98, .component.invoke.args.etsi.DivertingLegInformation3.presentation_allowed_indicator = 1, }, /* Explicit Call Transfer (ECT) */ { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_EctExecute, .component.invoke.invoke_id = 54, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_ExplicitEctExecute, .component.invoke.invoke_id = 55, .component.invoke.args.etsi.ExplicitEctExecute.link_id = 23, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_RequestSubaddress, .component.invoke.invoke_id = 56, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_SubaddressTransfer, .component.invoke.invoke_id = 57, .component.invoke.args.etsi.SubaddressTransfer.subaddress.type = 1, .component.invoke.args.etsi.SubaddressTransfer.subaddress.length = 4, .component.invoke.args.etsi.SubaddressTransfer.subaddress.u.nsap = "6492", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_EctLinkIdRequest, .component.invoke.invoke_id = 58, }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_ETSI_EctLinkIdRequest, .component.result.invoke_id = 59, .component.result.args.etsi.EctLinkIdRequest.link_id = 76, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_EctInform, .component.invoke.invoke_id = 60, .component.invoke.args.etsi.EctInform.status = 1, .component.invoke.args.etsi.EctInform.redirection_present = 1, .component.invoke.args.etsi.EctInform.redirection.presentation = 0, .component.invoke.args.etsi.EctInform.redirection.number.plan = 8, .component.invoke.args.etsi.EctInform.redirection.number.length = 4, .component.invoke.args.etsi.EctInform.redirection.number.str = "6229", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_EctInform, .component.invoke.invoke_id = 61, .component.invoke.args.etsi.EctInform.status = 1, .component.invoke.args.etsi.EctInform.redirection_present = 1, .component.invoke.args.etsi.EctInform.redirection.presentation = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_EctInform, .component.invoke.invoke_id = 62, .component.invoke.args.etsi.EctInform.status = 1, .component.invoke.args.etsi.EctInform.redirection_present = 1, .component.invoke.args.etsi.EctInform.redirection.presentation = 2, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_EctInform, .component.invoke.invoke_id = 63, .component.invoke.args.etsi.EctInform.status = 1, .component.invoke.args.etsi.EctInform.redirection_present = 1, .component.invoke.args.etsi.EctInform.redirection.presentation = 3, .component.invoke.args.etsi.EctInform.redirection.number.plan = 8, .component.invoke.args.etsi.EctInform.redirection.number.length = 4, .component.invoke.args.etsi.EctInform.redirection.number.str = "3340", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_EctInform, .component.invoke.invoke_id = 64, .component.invoke.args.etsi.EctInform.status = 1, .component.invoke.args.etsi.EctInform.redirection_present = 0, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_EctLoopTest, .component.invoke.invoke_id = 65, .component.invoke.args.etsi.EctLoopTest.call_transfer_id = 7, }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_ETSI_EctLoopTest, .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, }, /* MCID */ { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_MCIDRequest, .component.invoke.invoke_id = 54, }, /* MWI */ { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_MWIActivate, .component.invoke.invoke_id = 55, .component.invoke.args.etsi.MWIActivate.receiving_user_number.plan = 8, .component.invoke.args.etsi.MWIActivate.receiving_user_number.length = 4, .component.invoke.args.etsi.MWIActivate.receiving_user_number.str = "6229", .component.invoke.args.etsi.MWIActivate.basic_service = 3, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_MWIActivate, .component.invoke.invoke_id = 56, .component.invoke.args.etsi.MWIActivate.receiving_user_number.plan = 8, .component.invoke.args.etsi.MWIActivate.receiving_user_number.length = 4, .component.invoke.args.etsi.MWIActivate.receiving_user_number.str = "6229", .component.invoke.args.etsi.MWIActivate.basic_service = 3, .component.invoke.args.etsi.MWIActivate.controlling_user_number.plan = 8, .component.invoke.args.etsi.MWIActivate.controlling_user_number.length = 4, .component.invoke.args.etsi.MWIActivate.controlling_user_number.str = "6229", .component.invoke.args.etsi.MWIActivate.number_of_messages_present = 1, .component.invoke.args.etsi.MWIActivate.number_of_messages = 7, .component.invoke.args.etsi.MWIActivate.controlling_user_provided_number.plan = 8, .component.invoke.args.etsi.MWIActivate.controlling_user_provided_number.length = 4, .component.invoke.args.etsi.MWIActivate.controlling_user_provided_number.str = "6229", .component.invoke.args.etsi.MWIActivate.time_present = 1, .component.invoke.args.etsi.MWIActivate.time.str = "19970621194530", .component.invoke.args.etsi.MWIActivate.message_id_present = 1, .component.invoke.args.etsi.MWIActivate.message_id.reference_number = 98, .component.invoke.args.etsi.MWIActivate.message_id.status = 1, .component.invoke.args.etsi.MWIActivate.mode_present = 1, .component.invoke.args.etsi.MWIActivate.mode = 2, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_MWIDeactivate, .component.invoke.invoke_id = 57, .component.invoke.args.etsi.MWIDeactivate.receiving_user_number.plan = 8, .component.invoke.args.etsi.MWIDeactivate.receiving_user_number.length = 4, .component.invoke.args.etsi.MWIDeactivate.receiving_user_number.str = "6229", .component.invoke.args.etsi.MWIDeactivate.basic_service = 3, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_MWIDeactivate, .component.invoke.invoke_id = 58, .component.invoke.args.etsi.MWIDeactivate.receiving_user_number.plan = 8, .component.invoke.args.etsi.MWIDeactivate.receiving_user_number.length = 4, .component.invoke.args.etsi.MWIDeactivate.receiving_user_number.str = "6229", .component.invoke.args.etsi.MWIDeactivate.basic_service = 3, .component.invoke.args.etsi.MWIDeactivate.controlling_user_number.plan = 8, .component.invoke.args.etsi.MWIDeactivate.controlling_user_number.length = 4, .component.invoke.args.etsi.MWIDeactivate.controlling_user_number.str = "6229", .component.invoke.args.etsi.MWIDeactivate.mode_present = 1, .component.invoke.args.etsi.MWIDeactivate.mode = 2, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_MWIIndicate, .component.invoke.invoke_id = 59, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_ETSI_MWIIndicate, .component.invoke.invoke_id = 60, .component.invoke.args.etsi.MWIIndicate.controlling_user_number.plan = 8, .component.invoke.args.etsi.MWIIndicate.controlling_user_number.length = 4, .component.invoke.args.etsi.MWIIndicate.controlling_user_number.str = "6229", .component.invoke.args.etsi.MWIIndicate.basic_service_present = 1, .component.invoke.args.etsi.MWIIndicate.basic_service = 3, .component.invoke.args.etsi.MWIIndicate.number_of_messages_present = 1, .component.invoke.args.etsi.MWIIndicate.number_of_messages = 7, .component.invoke.args.etsi.MWIIndicate.controlling_user_provided_number.plan = 8, .component.invoke.args.etsi.MWIIndicate.controlling_user_provided_number.length = 4, .component.invoke.args.etsi.MWIIndicate.controlling_user_provided_number.str = "6229", .component.invoke.args.etsi.MWIIndicate.time_present = 1, .component.invoke.args.etsi.MWIIndicate.time.str = "19970621194530", .component.invoke.args.etsi.MWIIndicate.message_id_present = 1, .component.invoke.args.etsi.MWIIndicate.message_id.reference_number = 98, .component.invoke.args.etsi.MWIIndicate.message_id.status = 1, }, /* *INDENT-ON* */ }; static unsigned char rose_etsi_indefinite_len[] = { /* *INDENT-OFF* */ /* * Context Specific/C [1 0x01] Len:24 <80> * Integer(2 0x02) <02> Len:1 <01> * <44> * Integer(2 0x02) <02> Len:1 <01> * <07> * Sequence/C(48 0x30) <30> Len:16 <80> * Enumerated(10 0x0A) <0A> Len:1 <01> * <01> * Enumerated(10 0x0A) <0A> Len:1 <01> * <05> * Sequence/C(48 0x30) <30> Len:6 <80> * Context Specific [4 0x04] <84> Len:4 <80> * <31 38 30 33> * 0x00, 0x00, * 0x00, 0x00, * NULL(5 0x05) <05> Len:0 <00> * 0x00, 0x00, * 0x00, 0x00 */ 0x91, 0xA1, 0x80, 0x02, 0x01, 0x44, 0x02, 0x01, 0x07, 0x30, 0x80, 0x0A, 0x01, 0x01, 0x0A, 0x01, 0x05, 0x30, 0x80, 0x84, 0x80, 0x31, 0x38, 0x30, 0x33, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00 /* *INDENT-ON* */ }; static unsigned char rose_etsi_unused_indefinite_len[] = { /* *INDENT-OFF* */ /* * Context Specific/C [1 0x01] Len:24 <80> * Integer(2 0x02) <02> Len:1 <01> * <44> * Integer(2 0x02) <02> Len:1 <01> * <06> -- EctExecute * Sequence/C(48 0x30) <30> Len:16 <80> * Enumerated(10 0x0A) <0A> Len:1 <01> * <01> * Enumerated(10 0x0A) <0A> Len:1 <01> * <05> * Sequence/C(48 0x30) <30> Len:6 <80> * Context Specific [4 0x04] <84> Len:4 <80> * <31 38 30 33> * 0x00, 0x00, * 0x00, 0x00, * NULL(5 0x05) <05> Len:0 <00> * 0x00, 0x00, * 0x00, 0x00 */ 0x91, 0xA1, 0x80, 0x02, 0x01, 0x44, 0x02, 0x01, 0x06, 0x30, 0x80, 0x0A, 0x01, 0x01, 0x0A, 0x01, 0x05, 0x30, 0x80, 0x84, 0x80, 0x31, 0x38, 0x30, 0x33, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00 /* *INDENT-ON* */ }; static unsigned char rose_etsi_unused[] = { /* *INDENT-OFF* */ /* * Context Specific/C [1 0x01] Len:24 <18> * Integer(2 0x02) <02> Len:1 <01> * <44> * Integer(2 0x02) <02> Len:1 <01> * <06> -- EctExecute * Sequence/C(48 0x30) <30> Len:16 <10> * Enumerated(10 0x0A) <0A> Len:1 <01> * <01> * Enumerated(10 0x0A) <0A> Len:1 <01> * <05> * Sequence/C(48 0x30) <30> Len:6 <06> * Context Specific [4 0x04] <84> Len:4 <04> * <31 38 30 33> * NULL(5 0x05) <05> Len:0 <00> */ 0x91, 0xA1, 0x18, 0x02, 0x01, 0x44, 0x02, 0x01, 0x06, 0x30, 0x10, 0x0A, 0x01, 0x01, 0x0A, 0x01, 0x05, 0x30, 0x06, 0x84, 0x04, 0x31, 0x38, 0x30, 0x33, 0x05, 0x00 /* *INDENT-ON* */ }; static const struct rose_message rose_qsig_msgs[] = { /* *INDENT-OFF* */ /* Q.SIG Name-Operations */ { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CallingName, .component.invoke.invoke_id = 2, .component.invoke.args.qsig.CallingName.name.presentation = 1, .component.invoke.args.qsig.CallingName.name.char_set = 1, .component.invoke.args.qsig.CallingName.name.length = 7, .component.invoke.args.qsig.CallingName.name.data = "Alphred", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CallingName, .component.invoke.invoke_id = 3, .component.invoke.args.qsig.CallingName.name.presentation = 1, .component.invoke.args.qsig.CallingName.name.char_set = 3, .component.invoke.args.qsig.CallingName.name.length = 7, .component.invoke.args.qsig.CallingName.name.data = "Alphred", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CallingName, .component.invoke.invoke_id = 4, .component.invoke.args.qsig.CallingName.name.presentation = 2, .component.invoke.args.qsig.CallingName.name.char_set = 1, .component.invoke.args.qsig.CallingName.name.length = 7, .component.invoke.args.qsig.CallingName.name.data = "Alphred", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CallingName, .component.invoke.invoke_id = 5, .component.invoke.args.qsig.CallingName.name.presentation = 2, .component.invoke.args.qsig.CallingName.name.char_set = 3, .component.invoke.args.qsig.CallingName.name.length = 7, .component.invoke.args.qsig.CallingName.name.data = "Alphred", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CallingName, .component.invoke.invoke_id = 6, .component.invoke.args.qsig.CallingName.name.presentation = 3, .component.invoke.args.qsig.CallingName.name.char_set = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CallingName, .component.invoke.invoke_id = 7, .component.invoke.args.qsig.CallingName.name.presentation = 4, .component.invoke.args.qsig.CallingName.name.char_set = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CalledName, .component.invoke.invoke_id = 8, .component.invoke.args.qsig.CallingName.name.presentation = 4, .component.invoke.args.qsig.CallingName.name.char_set = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_ConnectedName, .component.invoke.invoke_id = 9, .component.invoke.args.qsig.CallingName.name.presentation = 4, .component.invoke.args.qsig.CallingName.name.char_set = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_BusyName, .component.invoke.invoke_id = 10, .component.invoke.args.qsig.CallingName.name.presentation = 4, .component.invoke.args.qsig.CallingName.name.char_set = 1, }, /* Q.SIG SS-AOC-Operations */ { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_ChargeRequest, .component.invoke.invoke_id = 11, .component.invoke.args.qsig.ChargeRequest.num_records = 0, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_ChargeRequest, .component.invoke.invoke_id = 12, .component.invoke.args.qsig.ChargeRequest.num_records = 1, .component.invoke.args.qsig.ChargeRequest.advice_mode_combinations[0] = 3, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_ChargeRequest, .component.invoke.invoke_id = 13, .component.invoke.args.qsig.ChargeRequest.num_records = 2, .component.invoke.args.qsig.ChargeRequest.advice_mode_combinations[0] = 4, .component.invoke.args.qsig.ChargeRequest.advice_mode_combinations[1] = 3, }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_QSIG_ChargeRequest, .component.result.invoke_id = 14, .component.result.args.qsig.ChargeRequest.advice_mode_combination = 3, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_GetFinalCharge, .component.invoke.invoke_id = 15, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_AocFinal, .component.invoke.invoke_id = 16, .component.invoke.args.qsig.AocFinal.type = 0, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_AocFinal, .component.invoke.invoke_id = 17, .component.invoke.args.qsig.AocFinal.type = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_AocFinal, .component.invoke.invoke_id = 18, .component.invoke.args.qsig.AocFinal.type = 2, .component.invoke.args.qsig.AocFinal.specific.recorded.amount.currency = 800, .component.invoke.args.qsig.AocFinal.specific.recorded.amount.multiplier = 2, .component.invoke.args.qsig.AocFinal.specific.recorded.currency = "Rupies", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_AocFinal, .component.invoke.invoke_id = 19, .component.invoke.args.qsig.AocFinal.type = 2, .component.invoke.args.qsig.AocFinal.specific.recorded.amount.currency = 800, .component.invoke.args.qsig.AocFinal.specific.recorded.amount.multiplier = 2, .component.invoke.args.qsig.AocFinal.specific.recorded.currency = "Rupies", .component.invoke.args.qsig.AocFinal.specific.billing_id_present = 1, .component.invoke.args.qsig.AocFinal.specific.billing_id = 2, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_AocFinal, .component.invoke.invoke_id = 20, .component.invoke.args.qsig.AocFinal.type = 2, .component.invoke.args.qsig.AocFinal.specific.recorded.amount.currency = 800, .component.invoke.args.qsig.AocFinal.specific.recorded.amount.multiplier = 2, .component.invoke.args.qsig.AocFinal.specific.recorded.currency = "Rupies", .component.invoke.args.qsig.AocFinal.charging_association_present = 1, .component.invoke.args.qsig.AocFinal.charging_association.type = 0, .component.invoke.args.qsig.AocFinal.charging_association.id = 200, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_AocFinal, .component.invoke.invoke_id = 21, .component.invoke.args.qsig.AocFinal.type = 2, .component.invoke.args.qsig.AocFinal.specific.recorded.amount.currency = 800, .component.invoke.args.qsig.AocFinal.specific.recorded.amount.multiplier = 2, .component.invoke.args.qsig.AocFinal.specific.recorded.currency = "Rupies", .component.invoke.args.qsig.AocFinal.specific.billing_id_present = 1, .component.invoke.args.qsig.AocFinal.specific.billing_id = 2, .component.invoke.args.qsig.AocFinal.charging_association_present = 1, .component.invoke.args.qsig.AocFinal.charging_association.type = 0, .component.invoke.args.qsig.AocFinal.charging_association.id = 200, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_AocFinal, .component.invoke.invoke_id = 22, .component.invoke.args.qsig.AocFinal.type = 2, .component.invoke.args.qsig.AocFinal.specific.recorded.amount.currency = 800, .component.invoke.args.qsig.AocFinal.specific.recorded.amount.multiplier = 2, .component.invoke.args.qsig.AocFinal.specific.recorded.currency = "Rupies", .component.invoke.args.qsig.AocFinal.charging_association_present = 1, .component.invoke.args.qsig.AocFinal.charging_association.type = 1, .component.invoke.args.qsig.AocFinal.charging_association.number.plan = 4, .component.invoke.args.qsig.AocFinal.charging_association.number.length = 4, .component.invoke.args.qsig.AocFinal.charging_association.number.str = "1802", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_AocInterim, .component.invoke.invoke_id = 23, .component.invoke.args.qsig.AocInterim.type = 0, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_AocInterim, .component.invoke.invoke_id = 24, .component.invoke.args.qsig.AocInterim.type = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_AocInterim, .component.invoke.invoke_id = 25, .component.invoke.args.qsig.AocInterim.type = 2, .component.invoke.args.qsig.AocInterim.specific.recorded.amount.currency = 800, .component.invoke.args.qsig.AocInterim.specific.recorded.amount.multiplier = 2, .component.invoke.args.qsig.AocInterim.specific.recorded.currency = "Rupies", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_AocInterim, .component.invoke.invoke_id = 26, .component.invoke.args.qsig.AocInterim.type = 2, .component.invoke.args.qsig.AocInterim.specific.recorded.amount.currency = 800, .component.invoke.args.qsig.AocInterim.specific.recorded.amount.multiplier = 2, .component.invoke.args.qsig.AocInterim.specific.recorded.currency = "Rupies", .component.invoke.args.qsig.AocInterim.specific.billing_id_present = 1, .component.invoke.args.qsig.AocInterim.specific.billing_id = 2, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_AocRate, .component.invoke.invoke_id = 27, .component.invoke.args.qsig.AocRate.type = 0, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_AocRate, .component.invoke.invoke_id = 28, .component.invoke.args.qsig.AocRate.type = 1, .component.invoke.args.qsig.AocRate.currency_info.num_records = 1, .component.invoke.args.qsig.AocRate.currency_info.list[0].charged_item = 4, .component.invoke.args.qsig.AocRate.currency_info.list[0].currency_type = 0, .component.invoke.args.qsig.AocRate.currency_info.list[0].u.special_charging_code = 3, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_AocRate, .component.invoke.invoke_id = 29, .component.invoke.args.qsig.AocRate.type = 1, .component.invoke.args.qsig.AocRate.currency_info.num_records = 1, .component.invoke.args.qsig.AocRate.currency_info.list[0].charged_item = 4, .component.invoke.args.qsig.AocRate.currency_info.list[0].currency_type = 1, .component.invoke.args.qsig.AocRate.currency_info.list[0].u.duration.currency = "Dollars", .component.invoke.args.qsig.AocRate.currency_info.list[0].u.duration.amount.currency = 7, .component.invoke.args.qsig.AocRate.currency_info.list[0].u.duration.amount.multiplier = 1, .component.invoke.args.qsig.AocRate.currency_info.list[0].u.duration.charging_type = 1, .component.invoke.args.qsig.AocRate.currency_info.list[0].u.duration.time.length = 8, .component.invoke.args.qsig.AocRate.currency_info.list[0].u.duration.time.scale = 4, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_AocRate, .component.invoke.invoke_id = 30, .component.invoke.args.qsig.AocRate.type = 1, .component.invoke.args.qsig.AocRate.currency_info.num_records = 1, .component.invoke.args.qsig.AocRate.currency_info.list[0].charged_item = 4, .component.invoke.args.qsig.AocRate.currency_info.list[0].currency_type = 1, .component.invoke.args.qsig.AocRate.currency_info.list[0].u.duration.currency = "Dollars", .component.invoke.args.qsig.AocRate.currency_info.list[0].u.duration.amount.currency = 7, .component.invoke.args.qsig.AocRate.currency_info.list[0].u.duration.amount.multiplier = 1, .component.invoke.args.qsig.AocRate.currency_info.list[0].u.duration.charging_type = 1, .component.invoke.args.qsig.AocRate.currency_info.list[0].u.duration.time.length = 8, .component.invoke.args.qsig.AocRate.currency_info.list[0].u.duration.time.scale = 4, .component.invoke.args.qsig.AocRate.currency_info.list[0].u.duration.granularity_present = 1, .component.invoke.args.qsig.AocRate.currency_info.list[0].u.duration.granularity.length = 20, .component.invoke.args.qsig.AocRate.currency_info.list[0].u.duration.granularity.scale = 3, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_AocRate, .component.invoke.invoke_id = 31, .component.invoke.args.qsig.AocRate.type = 1, .component.invoke.args.qsig.AocRate.currency_info.num_records = 1, .component.invoke.args.qsig.AocRate.currency_info.list[0].charged_item = 4, .component.invoke.args.qsig.AocRate.currency_info.list[0].currency_type = 2, .component.invoke.args.qsig.AocRate.currency_info.list[0].u.flat_rate.currency = "Euros", .component.invoke.args.qsig.AocRate.currency_info.list[0].u.flat_rate.amount.currency = 4, .component.invoke.args.qsig.AocRate.currency_info.list[0].u.flat_rate.amount.multiplier = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_AocRate, .component.invoke.invoke_id = 32, .component.invoke.args.qsig.AocRate.type = 1, .component.invoke.args.qsig.AocRate.currency_info.num_records = 1, .component.invoke.args.qsig.AocRate.currency_info.list[0].charged_item = 4, .component.invoke.args.qsig.AocRate.currency_info.list[0].currency_type = 3, .component.invoke.args.qsig.AocRate.currency_info.list[0].u.volume_rate.currency = "Yen", .component.invoke.args.qsig.AocRate.currency_info.list[0].u.volume_rate.amount.currency = 300, .component.invoke.args.qsig.AocRate.currency_info.list[0].u.volume_rate.amount.multiplier = 5, .component.invoke.args.qsig.AocRate.currency_info.list[0].u.volume_rate.unit = 2, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_AocRate, .component.invoke.invoke_id = 33, .component.invoke.args.qsig.AocRate.type = 1, .component.invoke.args.qsig.AocRate.currency_info.num_records = 2, .component.invoke.args.qsig.AocRate.currency_info.list[0].charged_item = 4, .component.invoke.args.qsig.AocRate.currency_info.list[0].currency_type = 2, .component.invoke.args.qsig.AocRate.currency_info.list[0].u.flat_rate.currency = "Euros", .component.invoke.args.qsig.AocRate.currency_info.list[0].u.flat_rate.amount.currency = 4, .component.invoke.args.qsig.AocRate.currency_info.list[0].u.flat_rate.amount.multiplier = 1, .component.invoke.args.qsig.AocRate.currency_info.list[1].charged_item = 4, .component.invoke.args.qsig.AocRate.currency_info.list[1].currency_type = 3, .component.invoke.args.qsig.AocRate.currency_info.list[1].u.volume_rate.currency = "Yen", .component.invoke.args.qsig.AocRate.currency_info.list[1].u.volume_rate.amount.currency = 300, .component.invoke.args.qsig.AocRate.currency_info.list[1].u.volume_rate.amount.multiplier = 5, .component.invoke.args.qsig.AocRate.currency_info.list[1].u.volume_rate.unit = 2, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_AocRate, .component.invoke.invoke_id = 34, .component.invoke.args.qsig.AocRate.type = 1, .component.invoke.args.qsig.AocRate.currency_info.num_records = 1, .component.invoke.args.qsig.AocRate.currency_info.list[0].charged_item = 4, .component.invoke.args.qsig.AocRate.currency_info.list[0].currency_type = 4, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_AocRate, .component.invoke.invoke_id = 35, .component.invoke.args.qsig.AocRate.type = 1, .component.invoke.args.qsig.AocRate.currency_info.num_records = 1, .component.invoke.args.qsig.AocRate.currency_info.list[0].charged_item = 4, .component.invoke.args.qsig.AocRate.currency_info.list[0].currency_type = 5, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_AocRate, .component.invoke.invoke_id = 36, .component.invoke.args.qsig.AocRate.type = 1, .component.invoke.args.qsig.AocRate.currency_info.num_records = 1, .component.invoke.args.qsig.AocRate.currency_info.list[0].charged_item = 4, .component.invoke.args.qsig.AocRate.currency_info.list[0].currency_type = 6, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_AocComplete, .component.invoke.invoke_id = 37, .component.invoke.args.qsig.AocComplete.charged_user_number.plan = 4, .component.invoke.args.qsig.AocComplete.charged_user_number.length = 4, .component.invoke.args.qsig.AocComplete.charged_user_number.str = "8340", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_AocComplete, .component.invoke.invoke_id = 38, .component.invoke.args.qsig.AocComplete.charged_user_number.plan = 4, .component.invoke.args.qsig.AocComplete.charged_user_number.length = 4, .component.invoke.args.qsig.AocComplete.charged_user_number.str = "8340", .component.invoke.args.qsig.AocComplete.charging_association_present = 1, .component.invoke.args.qsig.AocComplete.charging_association.type = 0, .component.invoke.args.qsig.AocComplete.charging_association.id = 8298, }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_QSIG_AocComplete, .component.result.invoke_id = 39, .component.result.args.qsig.AocComplete.charging_option = 2, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_AocDivChargeReq, .component.invoke.invoke_id = 40, .component.invoke.args.qsig.AocDivChargeReq.diverting_user_number.plan = 4, .component.invoke.args.qsig.AocDivChargeReq.diverting_user_number.length = 4, .component.invoke.args.qsig.AocDivChargeReq.diverting_user_number.str = "8340", .component.invoke.args.qsig.AocDivChargeReq.diversion_type = 3, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_AocDivChargeReq, .component.invoke.invoke_id = 41, .component.invoke.args.qsig.AocDivChargeReq.diverting_user_number.plan = 4, .component.invoke.args.qsig.AocDivChargeReq.diverting_user_number.length = 4, .component.invoke.args.qsig.AocDivChargeReq.diverting_user_number.str = "8340", .component.invoke.args.qsig.AocDivChargeReq.charging_association_present = 1, .component.invoke.args.qsig.AocDivChargeReq.charging_association.type = 0, .component.invoke.args.qsig.AocDivChargeReq.charging_association.id = 8298, .component.invoke.args.qsig.AocDivChargeReq.diversion_type = 3, }, /* Q.SIG Call-Transfer-Operations (CT) */ { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CallTransferIdentify, .component.invoke.invoke_id = 42, }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_QSIG_CallTransferIdentify, .component.result.invoke_id = 43, .component.result.args.qsig.CallTransferIdentify.call_id = "2345", .component.result.args.qsig.CallTransferIdentify.rerouting_number.plan = 4, .component.result.args.qsig.CallTransferIdentify.rerouting_number.length = 4, .component.result.args.qsig.CallTransferIdentify.rerouting_number.str = "8340", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CallTransferAbandon, .component.invoke.invoke_id = 44, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CallTransferInitiate, .component.invoke.invoke_id = 45, .component.invoke.args.qsig.CallTransferInitiate.call_id = "2345", .component.invoke.args.qsig.CallTransferInitiate.rerouting_number.plan = 4, .component.invoke.args.qsig.CallTransferInitiate.rerouting_number.length = 4, .component.invoke.args.qsig.CallTransferInitiate.rerouting_number.str = "8340", }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_QSIG_CallTransferInitiate, .component.result.invoke_id = 46, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CallTransferSetup, .component.invoke.invoke_id = 47, .component.invoke.args.qsig.CallTransferSetup.call_id = "23", }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_QSIG_CallTransferSetup, .component.result.invoke_id = 48, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CallTransferActive, .component.invoke.invoke_id = 49, .component.invoke.args.qsig.CallTransferActive.connected.presentation = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CallTransferActive, .component.invoke.invoke_id = 50, .component.invoke.args.qsig.CallTransferActive.connected.presentation = 1, .component.invoke.args.qsig.CallTransferActive.q931ie.length = 2, .component.invoke.args.qsig.CallTransferActive.q931ie_contents = "RT", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CallTransferActive, .component.invoke.invoke_id = 51, .component.invoke.args.qsig.CallTransferActive.connected.presentation = 1, .component.invoke.args.qsig.CallTransferActive.connected_name_present = 1, .component.invoke.args.qsig.CallTransferActive.connected_name.presentation = 1, .component.invoke.args.qsig.CallTransferActive.connected_name.char_set = 1, .component.invoke.args.qsig.CallTransferActive.connected_name.length = 7, .component.invoke.args.qsig.CallTransferActive.connected_name.data = "Alphred", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CallTransferActive, .component.invoke.invoke_id = 52, .component.invoke.args.qsig.CallTransferActive.connected.presentation = 1, .component.invoke.args.qsig.CallTransferActive.q931ie.length = 2, .component.invoke.args.qsig.CallTransferActive.q931ie_contents = "RT", .component.invoke.args.qsig.CallTransferActive.connected_name_present = 1, .component.invoke.args.qsig.CallTransferActive.connected_name.presentation = 1, .component.invoke.args.qsig.CallTransferActive.connected_name.char_set = 1, .component.invoke.args.qsig.CallTransferActive.connected_name.length = 7, .component.invoke.args.qsig.CallTransferActive.connected_name.data = "Alphred", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CallTransferComplete, .component.invoke.invoke_id = 53, .component.invoke.args.qsig.CallTransferComplete.end_designation = 1, .component.invoke.args.qsig.CallTransferComplete.redirection.presentation = 0, .component.invoke.args.qsig.CallTransferComplete.redirection.screened.screening_indicator = 3, .component.invoke.args.qsig.CallTransferComplete.redirection.screened.number.plan = 4, .component.invoke.args.qsig.CallTransferComplete.redirection.screened.number.length = 4, .component.invoke.args.qsig.CallTransferComplete.redirection.screened.number.str = "8340", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CallTransferComplete, .component.invoke.invoke_id = 54, .component.invoke.args.qsig.CallTransferComplete.end_designation = 1, .component.invoke.args.qsig.CallTransferComplete.redirection.presentation = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CallTransferComplete, .component.invoke.invoke_id = 55, .component.invoke.args.qsig.CallTransferComplete.end_designation = 1, .component.invoke.args.qsig.CallTransferComplete.redirection.presentation = 2, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CallTransferComplete, .component.invoke.invoke_id = 56, .component.invoke.args.qsig.CallTransferComplete.end_designation = 1, .component.invoke.args.qsig.CallTransferComplete.redirection.presentation = 3, .component.invoke.args.qsig.CallTransferComplete.redirection.screened.screening_indicator = 3, .component.invoke.args.qsig.CallTransferComplete.redirection.screened.number.plan = 4, .component.invoke.args.qsig.CallTransferComplete.redirection.screened.number.length = 4, .component.invoke.args.qsig.CallTransferComplete.redirection.screened.number.str = "8340", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CallTransferComplete, .component.invoke.invoke_id = 57, .component.invoke.args.qsig.CallTransferComplete.end_designation = 1, .component.invoke.args.qsig.CallTransferComplete.redirection.presentation = 2, .component.invoke.args.qsig.CallTransferComplete.q931ie.length = 2, .component.invoke.args.qsig.CallTransferComplete.q931ie_contents = "RT", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CallTransferComplete, .component.invoke.invoke_id = 58, .component.invoke.args.qsig.CallTransferComplete.end_designation = 1, .component.invoke.args.qsig.CallTransferComplete.redirection.presentation = 2, .component.invoke.args.qsig.CallTransferComplete.redirection_name_present = 1, .component.invoke.args.qsig.CallTransferComplete.redirection_name.presentation = 1, .component.invoke.args.qsig.CallTransferComplete.redirection_name.char_set = 1, .component.invoke.args.qsig.CallTransferComplete.redirection_name.length = 7, .component.invoke.args.qsig.CallTransferComplete.redirection_name.data = "Alphred", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CallTransferComplete, .component.invoke.invoke_id = 59, .component.invoke.args.qsig.CallTransferComplete.end_designation = 1, .component.invoke.args.qsig.CallTransferComplete.redirection.presentation = 2, .component.invoke.args.qsig.CallTransferComplete.call_status = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CallTransferComplete, .component.invoke.invoke_id = 60, .component.invoke.args.qsig.CallTransferComplete.end_designation = 1, .component.invoke.args.qsig.CallTransferComplete.redirection.presentation = 2, .component.invoke.args.qsig.CallTransferComplete.q931ie.length = 2, .component.invoke.args.qsig.CallTransferComplete.q931ie_contents = "RT", .component.invoke.args.qsig.CallTransferComplete.call_status = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CallTransferUpdate, .component.invoke.invoke_id = 61, .component.invoke.args.qsig.CallTransferUpdate.redirection.presentation = 2, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CallTransferUpdate, .component.invoke.invoke_id = 62, .component.invoke.args.qsig.CallTransferUpdate.redirection.presentation = 2, .component.invoke.args.qsig.CallTransferUpdate.redirection_name_present = 1, .component.invoke.args.qsig.CallTransferUpdate.redirection_name.presentation = 1, .component.invoke.args.qsig.CallTransferUpdate.redirection_name.char_set = 1, .component.invoke.args.qsig.CallTransferUpdate.redirection_name.length = 7, .component.invoke.args.qsig.CallTransferUpdate.redirection_name.data = "Alphred", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CallTransferUpdate, .component.invoke.invoke_id = 63, .component.invoke.args.qsig.CallTransferUpdate.redirection.presentation = 2, .component.invoke.args.qsig.CallTransferUpdate.q931ie.length = 2, .component.invoke.args.qsig.CallTransferUpdate.q931ie_contents = "RT", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CallTransferUpdate, .component.invoke.invoke_id = 64, .component.invoke.args.qsig.CallTransferUpdate.redirection.presentation = 2, .component.invoke.args.qsig.CallTransferUpdate.redirection_name_present = 1, .component.invoke.args.qsig.CallTransferUpdate.redirection_name.presentation = 1, .component.invoke.args.qsig.CallTransferUpdate.redirection_name.char_set = 1, .component.invoke.args.qsig.CallTransferUpdate.redirection_name.length = 7, .component.invoke.args.qsig.CallTransferUpdate.redirection_name.data = "Alphred", .component.invoke.args.qsig.CallTransferUpdate.q931ie.length = 2, .component.invoke.args.qsig.CallTransferUpdate.q931ie_contents = "RT", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_SubaddressTransfer, .component.invoke.invoke_id = 65, .component.invoke.args.qsig.SubaddressTransfer.redirection_subaddress.type = 1, .component.invoke.args.qsig.SubaddressTransfer.redirection_subaddress.length = 4, .component.invoke.args.qsig.SubaddressTransfer.redirection_subaddress.u.nsap = "4356", }, /* Q.SIG Call-Diversion-Operations */ { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_ActivateDiversionQ, .component.invoke.invoke_id = 66, .component.invoke.args.qsig.ActivateDiversionQ.procedure = 1, .component.invoke.args.qsig.ActivateDiversionQ.basic_service = 3, .component.invoke.args.qsig.ActivateDiversionQ.diverted_to.number.plan = 4, .component.invoke.args.qsig.ActivateDiversionQ.diverted_to.number.length = 4, .component.invoke.args.qsig.ActivateDiversionQ.diverted_to.number.str = "8340", .component.invoke.args.qsig.ActivateDiversionQ.served_user_number.plan = 4, .component.invoke.args.qsig.ActivateDiversionQ.served_user_number.length = 4, .component.invoke.args.qsig.ActivateDiversionQ.served_user_number.str = "8340", .component.invoke.args.qsig.ActivateDiversionQ.activating_user_number.plan = 4, .component.invoke.args.qsig.ActivateDiversionQ.activating_user_number.length = 4, .component.invoke.args.qsig.ActivateDiversionQ.activating_user_number.str = "8340", }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_QSIG_ActivateDiversionQ, .component.result.invoke_id = 67, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_DeactivateDiversionQ, .component.invoke.invoke_id = 68, .component.invoke.args.qsig.DeactivateDiversionQ.procedure = 1, .component.invoke.args.qsig.DeactivateDiversionQ.basic_service = 3, .component.invoke.args.qsig.DeactivateDiversionQ.served_user_number.plan = 4, .component.invoke.args.qsig.DeactivateDiversionQ.served_user_number.length = 4, .component.invoke.args.qsig.DeactivateDiversionQ.served_user_number.str = "8340", .component.invoke.args.qsig.DeactivateDiversionQ.deactivating_user_number.plan = 4, .component.invoke.args.qsig.DeactivateDiversionQ.deactivating_user_number.length = 4, .component.invoke.args.qsig.DeactivateDiversionQ.deactivating_user_number.str = "8340", }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_QSIG_DeactivateDiversionQ, .component.result.invoke_id = 69, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_InterrogateDiversionQ, .component.invoke.invoke_id = 70, .component.invoke.args.qsig.InterrogateDiversionQ.procedure = 1, .component.invoke.args.qsig.InterrogateDiversionQ.basic_service = 3, .component.invoke.args.qsig.InterrogateDiversionQ.served_user_number.plan = 4, .component.invoke.args.qsig.InterrogateDiversionQ.served_user_number.length = 4, .component.invoke.args.qsig.InterrogateDiversionQ.served_user_number.str = "8340", .component.invoke.args.qsig.InterrogateDiversionQ.interrogating_user_number.plan = 4, .component.invoke.args.qsig.InterrogateDiversionQ.interrogating_user_number.length = 4, .component.invoke.args.qsig.InterrogateDiversionQ.interrogating_user_number.str = "8340", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_InterrogateDiversionQ, .component.invoke.invoke_id = 71, .component.invoke.args.qsig.InterrogateDiversionQ.procedure = 1, .component.invoke.args.qsig.InterrogateDiversionQ.basic_service = 0,/* default */ .component.invoke.args.qsig.InterrogateDiversionQ.served_user_number.plan = 4, .component.invoke.args.qsig.InterrogateDiversionQ.served_user_number.length = 4, .component.invoke.args.qsig.InterrogateDiversionQ.served_user_number.str = "8340", .component.invoke.args.qsig.InterrogateDiversionQ.interrogating_user_number.plan = 4, .component.invoke.args.qsig.InterrogateDiversionQ.interrogating_user_number.length = 4, .component.invoke.args.qsig.InterrogateDiversionQ.interrogating_user_number.str = "8340", }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_QSIG_InterrogateDiversionQ, .component.result.invoke_id = 72, .component.result.args.qsig.InterrogateDiversionQ.num_records = 0, }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_QSIG_InterrogateDiversionQ, .component.result.invoke_id = 73, .component.result.args.qsig.InterrogateDiversionQ.num_records = 1, .component.result.args.qsig.InterrogateDiversionQ.list[0].served_user_number.plan = 4, .component.result.args.qsig.InterrogateDiversionQ.list[0].served_user_number.length = 4, .component.result.args.qsig.InterrogateDiversionQ.list[0].served_user_number.str = "8340", .component.result.args.qsig.InterrogateDiversionQ.list[0].basic_service = 3, .component.result.args.qsig.InterrogateDiversionQ.list[0].procedure = 2, .component.result.args.qsig.InterrogateDiversionQ.list[0].diverted_to.number.plan = 4, .component.result.args.qsig.InterrogateDiversionQ.list[0].diverted_to.number.length = 4, .component.result.args.qsig.InterrogateDiversionQ.list[0].diverted_to.number.str = "8340", .component.result.args.qsig.InterrogateDiversionQ.list[0].remote_enabled = 0, }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_QSIG_InterrogateDiversionQ, .component.result.invoke_id = 74, .component.result.args.qsig.InterrogateDiversionQ.num_records = 1, .component.result.args.qsig.InterrogateDiversionQ.list[0].served_user_number.plan = 4, .component.result.args.qsig.InterrogateDiversionQ.list[0].served_user_number.length = 4, .component.result.args.qsig.InterrogateDiversionQ.list[0].served_user_number.str = "8340", .component.result.args.qsig.InterrogateDiversionQ.list[0].basic_service = 3, .component.result.args.qsig.InterrogateDiversionQ.list[0].procedure = 2, .component.result.args.qsig.InterrogateDiversionQ.list[0].diverted_to.number.plan = 4, .component.result.args.qsig.InterrogateDiversionQ.list[0].diverted_to.number.length = 4, .component.result.args.qsig.InterrogateDiversionQ.list[0].diverted_to.number.str = "8340", .component.result.args.qsig.InterrogateDiversionQ.list[0].remote_enabled = 1, }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_QSIG_InterrogateDiversionQ, .component.result.invoke_id = 75, .component.result.args.qsig.InterrogateDiversionQ.num_records = 2, .component.result.args.qsig.InterrogateDiversionQ.list[0].served_user_number.plan = 4, .component.result.args.qsig.InterrogateDiversionQ.list[0].served_user_number.length = 4, .component.result.args.qsig.InterrogateDiversionQ.list[0].served_user_number.str = "8340", .component.result.args.qsig.InterrogateDiversionQ.list[0].basic_service = 3, .component.result.args.qsig.InterrogateDiversionQ.list[0].procedure = 2, .component.result.args.qsig.InterrogateDiversionQ.list[0].diverted_to.number.plan = 4, .component.result.args.qsig.InterrogateDiversionQ.list[0].diverted_to.number.length = 4, .component.result.args.qsig.InterrogateDiversionQ.list[0].diverted_to.number.str = "8340", .component.result.args.qsig.InterrogateDiversionQ.list[1].served_user_number.plan = 4, .component.result.args.qsig.InterrogateDiversionQ.list[1].served_user_number.length = 4, .component.result.args.qsig.InterrogateDiversionQ.list[1].served_user_number.str = "8340", .component.result.args.qsig.InterrogateDiversionQ.list[1].basic_service = 3, .component.result.args.qsig.InterrogateDiversionQ.list[1].procedure = 2, .component.result.args.qsig.InterrogateDiversionQ.list[1].diverted_to.number.plan = 4, .component.result.args.qsig.InterrogateDiversionQ.list[1].diverted_to.number.length = 4, .component.result.args.qsig.InterrogateDiversionQ.list[1].diverted_to.number.str = "8340", .component.result.args.qsig.InterrogateDiversionQ.list[1].remote_enabled = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CheckRestriction, .component.invoke.invoke_id = 76, .component.invoke.args.qsig.CheckRestriction.served_user_number.plan = 4, .component.invoke.args.qsig.CheckRestriction.served_user_number.length = 4, .component.invoke.args.qsig.CheckRestriction.served_user_number.str = "8340", .component.invoke.args.qsig.CheckRestriction.basic_service = 3, .component.invoke.args.qsig.CheckRestriction.diverted_to_number.plan = 4, .component.invoke.args.qsig.CheckRestriction.diverted_to_number.length = 4, .component.invoke.args.qsig.CheckRestriction.diverted_to_number.str = "8340", }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_QSIG_CheckRestriction, .component.result.invoke_id = 77, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CallRerouting, .component.invoke.invoke_id = 78, .component.invoke.args.qsig.CallRerouting.rerouting_reason = 3, .component.invoke.args.qsig.CallRerouting.called.number.plan = 4, .component.invoke.args.qsig.CallRerouting.called.number.length = 4, .component.invoke.args.qsig.CallRerouting.called.number.str = "8340", .component.invoke.args.qsig.CallRerouting.diversion_counter = 5, .component.invoke.args.qsig.CallRerouting.q931ie.length = 2, .component.invoke.args.qsig.CallRerouting.q931ie_contents = "RT", .component.invoke.args.qsig.CallRerouting.last_rerouting.presentation = 1, .component.invoke.args.qsig.CallRerouting.subscription_option = 2, .component.invoke.args.qsig.CallRerouting.calling.presentation = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CallRerouting, .component.invoke.invoke_id = 79, .component.invoke.args.qsig.CallRerouting.rerouting_reason = 3, .component.invoke.args.qsig.CallRerouting.original_rerouting_reason_present = 1, .component.invoke.args.qsig.CallRerouting.original_rerouting_reason = 2, .component.invoke.args.qsig.CallRerouting.called.number.plan = 4, .component.invoke.args.qsig.CallRerouting.called.number.length = 4, .component.invoke.args.qsig.CallRerouting.called.number.str = "8340", .component.invoke.args.qsig.CallRerouting.diversion_counter = 5, .component.invoke.args.qsig.CallRerouting.q931ie.length = 2, .component.invoke.args.qsig.CallRerouting.q931ie_contents = "RT", .component.invoke.args.qsig.CallRerouting.last_rerouting.presentation = 1, .component.invoke.args.qsig.CallRerouting.subscription_option = 2, .component.invoke.args.qsig.CallRerouting.calling_subaddress.type = 1, .component.invoke.args.qsig.CallRerouting.calling_subaddress.length = 4, .component.invoke.args.qsig.CallRerouting.calling_subaddress.u.nsap = "3253", .component.invoke.args.qsig.CallRerouting.calling.presentation = 1, .component.invoke.args.qsig.CallRerouting.calling_name_present = 1, .component.invoke.args.qsig.CallRerouting.calling_name.presentation = 4, .component.invoke.args.qsig.CallRerouting.calling_name.char_set = 1, .component.invoke.args.qsig.CallRerouting.original_called_present = 1, .component.invoke.args.qsig.CallRerouting.original_called.presentation = 2, .component.invoke.args.qsig.CallRerouting.redirecting_name_present = 1, .component.invoke.args.qsig.CallRerouting.redirecting_name.presentation = 4, .component.invoke.args.qsig.CallRerouting.redirecting_name.char_set = 1, .component.invoke.args.qsig.CallRerouting.original_called_name_present = 1, .component.invoke.args.qsig.CallRerouting.original_called_name.presentation = 4, .component.invoke.args.qsig.CallRerouting.original_called_name.char_set = 1, }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_QSIG_CallRerouting, .component.result.invoke_id = 80, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_DivertingLegInformation1, .component.invoke.invoke_id = 81, .component.invoke.args.qsig.DivertingLegInformation1.diversion_reason = 3, .component.invoke.args.qsig.DivertingLegInformation1.subscription_option = 1, .component.invoke.args.qsig.DivertingLegInformation1.nominated_number.plan = 4, .component.invoke.args.qsig.DivertingLegInformation1.nominated_number.length = 4, .component.invoke.args.qsig.DivertingLegInformation1.nominated_number.str = "8340", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_DivertingLegInformation2, .component.invoke.invoke_id = 82, .component.invoke.args.qsig.DivertingLegInformation2.diversion_counter = 6, .component.invoke.args.qsig.DivertingLegInformation2.diversion_reason = 3, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_DivertingLegInformation2, .component.invoke.invoke_id = 83, .component.invoke.args.qsig.DivertingLegInformation2.diversion_counter = 6, .component.invoke.args.qsig.DivertingLegInformation2.diversion_reason = 3, .component.invoke.args.qsig.DivertingLegInformation2.original_diversion_reason_present = 1, .component.invoke.args.qsig.DivertingLegInformation2.original_diversion_reason = 2, .component.invoke.args.qsig.DivertingLegInformation2.diverting_present = 1, .component.invoke.args.qsig.DivertingLegInformation2.diverting.presentation = 2, .component.invoke.args.qsig.DivertingLegInformation2.original_called_present = 1, .component.invoke.args.qsig.DivertingLegInformation2.original_called.presentation = 2, .component.invoke.args.qsig.DivertingLegInformation2.redirecting_name_present = 1, .component.invoke.args.qsig.DivertingLegInformation2.redirecting_name.presentation = 4, .component.invoke.args.qsig.DivertingLegInformation2.redirecting_name.char_set = 1, .component.invoke.args.qsig.DivertingLegInformation2.original_called_name_present = 1, .component.invoke.args.qsig.DivertingLegInformation2.original_called_name.presentation = 4, .component.invoke.args.qsig.DivertingLegInformation2.original_called_name.char_set = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_DivertingLegInformation3, .component.invoke.invoke_id = 84, .component.invoke.args.qsig.DivertingLegInformation3.presentation_allowed_indicator = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_DivertingLegInformation3, .component.invoke.invoke_id = 85, .component.invoke.args.qsig.DivertingLegInformation3.presentation_allowed_indicator = 1, .component.invoke.args.qsig.DivertingLegInformation3.redirection_name_present = 1, .component.invoke.args.qsig.DivertingLegInformation3.redirection_name.presentation = 4, .component.invoke.args.qsig.DivertingLegInformation3.redirection_name.char_set = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_CfnrDivertedLegFailed, .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, .component.invoke.operation = ROSE_QSIG_MWIActivate, .component.invoke.invoke_id = 102, .component.invoke.args.qsig.MWIActivate.served_user_number.plan = 4, .component.invoke.args.qsig.MWIActivate.served_user_number.length = 4, .component.invoke.args.qsig.MWIActivate.served_user_number.str = "9838", .component.invoke.args.qsig.MWIActivate.basic_service = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_MWIActivate, .component.invoke.invoke_id = 103, .component.invoke.args.qsig.MWIActivate.served_user_number.plan = 4, .component.invoke.args.qsig.MWIActivate.served_user_number.length = 4, .component.invoke.args.qsig.MWIActivate.served_user_number.str = "9838", .component.invoke.args.qsig.MWIActivate.basic_service = 1, .component.invoke.args.qsig.MWIActivate.msg_centre_id_present = 1, .component.invoke.args.qsig.MWIActivate.msg_centre_id.type = 0, .component.invoke.args.qsig.MWIActivate.msg_centre_id.u.integer = 532, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_MWIActivate, .component.invoke.invoke_id = 104, .component.invoke.args.qsig.MWIActivate.served_user_number.plan = 4, .component.invoke.args.qsig.MWIActivate.served_user_number.length = 4, .component.invoke.args.qsig.MWIActivate.served_user_number.str = "9838", .component.invoke.args.qsig.MWIActivate.basic_service = 1, .component.invoke.args.qsig.MWIActivate.msg_centre_id_present = 1, .component.invoke.args.qsig.MWIActivate.msg_centre_id.type = 1, .component.invoke.args.qsig.MWIActivate.msg_centre_id.u.number.plan = 4, .component.invoke.args.qsig.MWIActivate.msg_centre_id.u.number.length = 4, .component.invoke.args.qsig.MWIActivate.msg_centre_id.u.number.str = "9838", }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_MWIActivate, .component.invoke.invoke_id = 105, .component.invoke.args.qsig.MWIActivate.served_user_number.plan = 4, .component.invoke.args.qsig.MWIActivate.served_user_number.length = 4, .component.invoke.args.qsig.MWIActivate.served_user_number.str = "9838", .component.invoke.args.qsig.MWIActivate.basic_service = 1, .component.invoke.args.qsig.MWIActivate.msg_centre_id_present = 1, .component.invoke.args.qsig.MWIActivate.msg_centre_id.type = 2, .component.invoke.args.qsig.MWIActivate.msg_centre_id.u.str = "123456", .component.invoke.args.qsig.MWIActivate.number_of_messages_present = 1, .component.invoke.args.qsig.MWIActivate.number_of_messages = 6548, .component.invoke.args.qsig.MWIActivate.originating_number.plan = 4, .component.invoke.args.qsig.MWIActivate.originating_number.length = 4, .component.invoke.args.qsig.MWIActivate.originating_number.str = "9838", .component.invoke.args.qsig.MWIActivate.timestamp_present = 1, .component.invoke.args.qsig.MWIActivate.timestamp.str = "19970621194530", .component.invoke.args.qsig.MWIActivate.priority_present = 1, .component.invoke.args.qsig.MWIActivate.priority = 7, }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_QSIG_MWIActivate, .component.result.invoke_id = 106, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_MWIDeactivate, .component.invoke.invoke_id = 107, .component.invoke.args.qsig.MWIDeactivate.served_user_number.plan = 4, .component.invoke.args.qsig.MWIDeactivate.served_user_number.length = 4, .component.invoke.args.qsig.MWIDeactivate.served_user_number.str = "9838", .component.invoke.args.qsig.MWIDeactivate.basic_service = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_MWIDeactivate, .component.invoke.invoke_id = 108, .component.invoke.args.qsig.MWIDeactivate.served_user_number.plan = 4, .component.invoke.args.qsig.MWIDeactivate.served_user_number.length = 4, .component.invoke.args.qsig.MWIDeactivate.served_user_number.str = "9838", .component.invoke.args.qsig.MWIDeactivate.basic_service = 1, .component.invoke.args.qsig.MWIDeactivate.msg_centre_id_present = 1, .component.invoke.args.qsig.MWIDeactivate.msg_centre_id.type = 0, .component.invoke.args.qsig.MWIDeactivate.msg_centre_id.u.integer = 532, }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_QSIG_MWIDeactivate, .component.result.invoke_id = 109, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_MWIInterrogate, .component.invoke.invoke_id = 110, .component.invoke.args.qsig.MWIInterrogate.served_user_number.plan = 4, .component.invoke.args.qsig.MWIInterrogate.served_user_number.length = 4, .component.invoke.args.qsig.MWIInterrogate.served_user_number.str = "9838", .component.invoke.args.qsig.MWIInterrogate.basic_service = 1, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_QSIG_MWIInterrogate, .component.invoke.invoke_id = 111, .component.invoke.args.qsig.MWIInterrogate.served_user_number.plan = 4, .component.invoke.args.qsig.MWIInterrogate.served_user_number.length = 4, .component.invoke.args.qsig.MWIInterrogate.served_user_number.str = "9838", .component.invoke.args.qsig.MWIInterrogate.basic_service = 1, .component.invoke.args.qsig.MWIInterrogate.msg_centre_id_present = 1, .component.invoke.args.qsig.MWIInterrogate.msg_centre_id.type = 0, .component.invoke.args.qsig.MWIInterrogate.msg_centre_id.u.integer = 532, }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_QSIG_MWIInterrogate, .component.result.invoke_id = 112, .component.result.args.qsig.MWIInterrogate.num_records = 1, .component.result.args.qsig.MWIInterrogate.list[0].basic_service = 1, }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_QSIG_MWIInterrogate, .component.result.invoke_id = 113, .component.result.args.qsig.MWIInterrogate.num_records = 2, .component.result.args.qsig.MWIInterrogate.list[0].basic_service = 1, .component.result.args.qsig.MWIInterrogate.list[0].msg_centre_id_present = 1, .component.result.args.qsig.MWIInterrogate.list[0].msg_centre_id.type = 0, .component.result.args.qsig.MWIInterrogate.list[0].msg_centre_id.u.integer = 987, .component.result.args.qsig.MWIInterrogate.list[0].number_of_messages_present = 1, .component.result.args.qsig.MWIInterrogate.list[0].number_of_messages = 6548, .component.result.args.qsig.MWIInterrogate.list[0].originating_number.plan = 4, .component.result.args.qsig.MWIInterrogate.list[0].originating_number.length = 4, .component.result.args.qsig.MWIInterrogate.list[0].originating_number.str = "9838", .component.result.args.qsig.MWIInterrogate.list[0].timestamp_present = 1, .component.result.args.qsig.MWIInterrogate.list[0].timestamp.str = "19970621194530", .component.result.args.qsig.MWIInterrogate.list[0].priority_present = 1, .component.result.args.qsig.MWIInterrogate.list[0].priority = 7, .component.result.args.qsig.MWIInterrogate.list[1].basic_service = 1, }, /* *INDENT-ON* */ }; static unsigned char rose_qsig_multiple_msg[] = { /* *INDENT-OFF* */ /* * Context Specific/C [10 0x0A] Len:6 <06> * Context Specific [0 0x00] <80> Len:1 <01> * <00> - "~" * Context Specific [2 0x02] <82> Len:1 <01> * <00> - "~" * Context Specific [11 0x0B] <8B> Len:1 <01> * <00> - "~" * Context Specific/C [1 0x01] Len:16 <10> * Integer(2 0x02) <02> Len:1 <01> * <01> - "~" * Integer(2 0x02) <02> Len:1 <01> * <55> - "U" * Sequence/C(48 0x30) <30> Len:8 <08> * Context Specific [2 0x02] <82> Len:3 <03> * <01 30 40> - "~0@" * Context Specific [6 0x06] <86> Len:1 <01> * <01> - "~" * Context Specific/C [1 0x01] Len:19 <13> * Integer(2 0x02) <02> Len:1 <01> * <02> - "~" * Integer(2 0x02) <02> Len:1 <01> * <00> - "~" * Context Specific [0 0x00] <80> Len:11 <0B> * <4D 6F 64 65 6D 20 44 69-73 63 6F> - "Modem Disco" */ 0x9f, 0xaa, 0x06, 0x80, 0x01, 0x00, 0x82, 0x01, 0x00, 0x8b, 0x01, 0x00, 0xa1, 0x10, 0x02, 0x01, 0x01, 0x02, 0x01, 0x55, 0x30, 0x08, 0x82, 0x03, 0x01, 0x30, 0x40, 0x86, 0x01, 0x01, 0xa1, 0x13, 0x02, 0x01, 0x02, 0x02, 0x01, 0x00, 0x80, 0x0b, 0x4d, 0x6f, 0x64, 0x65, 0x6d, 0x20, 0x44, 0x69, 0x73, 0x63, 0x6f /* *INDENT-ON* */ }; static unsigned char rose_qsig_name_alt_encode_msg[] = { /* *INDENT-OFF* */ /* * Context Specific/C [10 0x0A] Len:6 <06> * Context Specific [0 0x00] <80> Len:1 <01> * <00> - "~" * Context Specific [2 0x02] <82> Len:1 <01> * <00> - "~" * Context Specific [11 0x0B] <8B> Len:1 <01> * <00> - "~" * Context Specific/C [1 0x01] Len:21 <15> * Integer(2 0x02) <02> Len:1 <01> * <1D> - "~" * Integer(2 0x02) <02> Len:1 <01> * <00> - "~" * Sequence/C(48 0x30) <30> Len:13 <0D> * Context Specific [0 0x00] <80> Len:11 <0B> * <55 54 49 4C 49 54 59 20-54 45 4C> - "UTILITY TEL" */ 0x9F, 0xAA, 0x06, 0x80, 0x01, 0x00, 0x82, 0x01, 0x00, 0x8B, 0x01, 0x00, 0xA1, 0x15, 0x02, 0x01, 0x1D, 0x02, 0x01, 0x00, 0x30, 0x0D, 0x80, 0x0B, 0x55, 0x54, 0x49, 0x4C, 0x49, 0x54, 0x59, 0x20, 0x54, 0x45, 0x4C /* *INDENT-ON* */ }; static unsigned char rose_qsig_name_2nd_encode_msg[] = { /* *INDENT-OFF* */ /* * Context Specific/C [10 0x0A] Len:6 <06> * Context Specific [0 0x00] <80> Len:1 <01> * <00> - "~" * Context Specific [2 0x02] <82> Len:1 <01> * <00> - "~" * Context Specific [11 0x0B] <8B> Len:1 <01> * <00> - "~" * Context Specific/C [1 0x01] Len:26 <1A> * Integer(2 0x02) <02> Len:1 <01> * <40> - "@" * OID(6 0x06) <06> Len:4 <04> * <2B 0C 09 00> - "+~~~" * Context Specific [0 0x00] <80> Len:15 <0F> * <4D 6F 64 65 6D 20 44 69-73 63 6F 42 61 6C 6C> - "Modem DiscoBall" */ 0x91, 0xaa, 0x06, 0x80, 0x01, 0x00, 0x82, 0x01, 0x00, 0x8b, 0x01, 0x00, 0xa1, 0x1a, 0x02, 0x01, 0x40, 0x06, 0x04, 0x2b, 0x0c, 0x09, 0x00, 0x80, 0x0f, 0x4d, 0x6f, 0x64, 0x65, 0x6d, 0x20, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x42, 0x61, 0x6c, 0x6c /* *INDENT-ON* */ }; static const struct rose_message rose_dms100_msgs[] = { /* *INDENT-OFF* */ { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_DMS100_RLT_OperationInd, .component.invoke.invoke_id = ROSE_DMS100_RLT_OPERATION_IND, }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_DMS100_RLT_OperationInd, .component.result.invoke_id = ROSE_DMS100_RLT_OPERATION_IND, .component.result.args.dms100.RLT_OperationInd.call_id = 130363, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_DMS100_RLT_ThirdParty, .component.invoke.invoke_id = ROSE_DMS100_RLT_THIRD_PARTY, .component.invoke.args.dms100.RLT_ThirdParty.call_id = 120047, .component.invoke.args.dms100.RLT_ThirdParty.reason = 1, }, { .type = ROSE_COMP_TYPE_RESULT, .component.result.operation = ROSE_DMS100_RLT_ThirdParty, .component.result.invoke_id = ROSE_DMS100_RLT_THIRD_PARTY, }, /* *INDENT-ON* */ }; static const struct rose_message rose_ni2_msgs[] = { /* *INDENT-OFF* */ { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_NI2_InformationFollowing, .component.invoke.invoke_id = 1, .component.invoke.args.ni2.InformationFollowing.value = 7, }, { .type = ROSE_COMP_TYPE_INVOKE, .component.invoke.operation = ROSE_NI2_InitiateTransfer, .component.invoke.invoke_id = 2, .component.invoke.args.ni2.InitiateTransfer.call_reference = 5, }, /* *INDENT-ON* */ }; /* ------------------------------------------------------------------- */ static void rose_pri_message(struct pri *ctrl, char *stuff) { fprintf(stdout, "%s", stuff); } static void rose_pri_error(struct pri *ctrl, char *stuff) { fprintf(stdout, "%s", stuff); fprintf(stderr, "%s", stuff); } /*! * \internal * \brief Test ROSE encoding and decoding the given message. * * \param ctrl D channel controller for diagnostic messages or global options. * \param index Message number to report. * \param header Facility message header data to encode. * \param encode_msg Message data to encode. * * \return Nothing */ static void rose_test_msg(struct pri *ctrl, unsigned index, const struct fac_extension_header *header, const struct rose_message *encode_msg) { struct fac_extension_header decoded_header; struct rose_message decoded_msg; unsigned char *enc_pos; unsigned char *enc_end; const unsigned char *dec_pos; const unsigned char *dec_end; static unsigned char buf[1024]; pri_message(ctrl, "\n\n"); enc_end = buf + sizeof(buf); enc_pos = facility_encode_header(ctrl, buf, enc_end, header); if (!enc_pos) { pri_error(ctrl, "Error: Message:%u failed to encode header\n", index); } else { enc_pos = rose_encode(ctrl, enc_pos, enc_end, encode_msg); if (!enc_pos) { pri_error(ctrl, "Error: Message:%u failed to encode ROSE\n", index); } else { pri_message(ctrl, "Message %u encoded length is %u\n", index, (unsigned) (enc_pos - buf)); /* Clear the decoded message contents for comparison. */ memset(&decoded_header, 0, sizeof(decoded_header)); memset(&decoded_msg, 0, sizeof(decoded_msg)); dec_end = enc_pos; dec_pos = facility_decode_header(ctrl, buf, dec_end, &decoded_header); if (!dec_pos) { pri_error(ctrl, "Error: Message:%u failed to decode header\n", index); } else { while (dec_pos < dec_end) { dec_pos = rose_decode(ctrl, dec_pos, dec_end, &decoded_msg); if (!dec_pos) { pri_error(ctrl, "Error: Message:%u failed to decode ROSE\n", index); break; } else { if (header && memcmp(header, &decoded_header, sizeof(decoded_header))) { pri_error(ctrl, "Error: Message:%u Header did not match\n", index); } if (memcmp(encode_msg, &decoded_msg, sizeof(decoded_msg))) { pri_error(ctrl, "Error: Message:%u ROSE did not match\n", index); } } } } } } pri_message(ctrl, "\n\n" "************************************************************\n"); } /*! * \internal * \brief Test ROSE decoding messages of unusual encodings. * * \param ctrl D channel controller for diagnostic messages or global options. * \param name Test name for the encoded message. * \param msg_buf Encoded message to decode. * \param msg_len Length of encoded message buffer. * * \return Nothing */ static void rose_test_exception(struct pri *ctrl, const char *name, const unsigned char *msg, size_t msg_len) { const unsigned char *pos; const unsigned char *end; struct fac_extension_header header; struct rose_message decoded_msg; pri_message(ctrl, "\n\n" "%s test: Message encoded length is %u\n", name, (unsigned) msg_len); pos = msg; end = msg + msg_len; pos = facility_decode_header(ctrl, pos, end, &header); if (!pos) { pri_error(ctrl, "Error: %s test: Message failed to decode header\n", name); } else { while (pos < end) { pos = rose_decode(ctrl, pos, end, &decoded_msg); if (!pos) { pri_error(ctrl, "Error: %s test: Message failed to decode ROSE\n", name); break; } } } pri_message(ctrl, "\n\n" "************************************************************\n"); } /*! * \brief ROSE encode/decode test program. * * \param argc Program argument count. * \param argv Program argument string array. * * \retval 0 on success. * \retval Nonzero on error. */ int main(int argc, char *argv[]) { unsigned index; unsigned offset; const char *str; static struct pri dummy_ctrl; pri_set_message(rose_pri_message); pri_set_error(rose_pri_error); memset(&dummy_ctrl, 0, sizeof(dummy_ctrl)); dummy_ctrl.debug = PRI_DEBUG_APDU; /* For sanity specify what version of libpri we are testing. */ pri_error(&dummy_ctrl, "libpri version tested: %s\n", pri_get_version()); offset = 0; pri_message(&dummy_ctrl, "Encode/decode message(s)\n"); if (argc <= 1) { dummy_ctrl.switchtype = PRI_SWITCH_EUROISDN_E1; for (index = 0; index < ARRAY_LEN(rose_etsi_msgs); ++index) { rose_test_msg(&dummy_ctrl, index + offset, &fac_headers[0], &rose_etsi_msgs[index]); } offset += ARRAY_LEN(rose_etsi_msgs); dummy_ctrl.switchtype = PRI_SWITCH_QSIG; for (index = 0; index < ARRAY_LEN(rose_qsig_msgs); ++index) { rose_test_msg(&dummy_ctrl, index + offset, &fac_headers[index % ARRAY_LEN(fac_headers)], &rose_qsig_msgs[index]); } offset += ARRAY_LEN(rose_qsig_msgs); dummy_ctrl.switchtype = PRI_SWITCH_DMS100; for (index = 0; index < ARRAY_LEN(rose_dms100_msgs); ++index) { rose_test_msg(&dummy_ctrl, index + offset, &fac_headers[0], &rose_dms100_msgs[index]); } offset += ARRAY_LEN(rose_dms100_msgs); dummy_ctrl.switchtype = PRI_SWITCH_NI2; for (index = 0; index < ARRAY_LEN(rose_ni2_msgs); ++index) { rose_test_msg(&dummy_ctrl, index + offset, &fac_headers[0], &rose_ni2_msgs[index]); } //offset += ARRAY_LEN(rose_ni2_msgs); } else { index = atoi(argv[1]); if (index < ARRAY_LEN(rose_etsi_msgs)) { dummy_ctrl.switchtype = PRI_SWITCH_EUROISDN_E1; rose_test_msg(&dummy_ctrl, index + offset, &fac_headers[0], &rose_etsi_msgs[index]); return 0; } offset += ARRAY_LEN(rose_etsi_msgs); index -= ARRAY_LEN(rose_etsi_msgs); if (index < ARRAY_LEN(rose_qsig_msgs)) { dummy_ctrl.switchtype = PRI_SWITCH_QSIG; rose_test_msg(&dummy_ctrl, index + offset, &fac_headers[index % ARRAY_LEN(fac_headers)], &rose_qsig_msgs[index]); return 0; } offset += ARRAY_LEN(rose_qsig_msgs); index -= ARRAY_LEN(rose_qsig_msgs); if (index < ARRAY_LEN(rose_dms100_msgs)) { dummy_ctrl.switchtype = PRI_SWITCH_DMS100; rose_test_msg(&dummy_ctrl, index + offset, &fac_headers[0], &rose_dms100_msgs[index]); return 0; } offset += ARRAY_LEN(rose_dms100_msgs); index -= ARRAY_LEN(rose_dms100_msgs); if (index < ARRAY_LEN(rose_ni2_msgs)) { dummy_ctrl.switchtype = PRI_SWITCH_NI2; rose_test_msg(&dummy_ctrl, index + offset, &fac_headers[0], &rose_ni2_msgs[index]); return 0; } //offset += ARRAY_LEN(rose_ni2_msgs); //index -= ARRAY_LEN(rose_ni2_msgs); fprintf(stderr, "Invalid option\n"); return 0; } /* ------------------------------------------------------------------- */ pri_message(&dummy_ctrl, "\n\n" "Decode unusually encoded messages\n"); dummy_ctrl.switchtype = PRI_SWITCH_EUROISDN_E1; rose_test_exception(&dummy_ctrl, "Indefinite length", rose_etsi_indefinite_len, sizeof(rose_etsi_indefinite_len)); rose_test_exception(&dummy_ctrl, "Unused components (indefinite length)", rose_etsi_unused_indefinite_len, sizeof(rose_etsi_unused_indefinite_len)); rose_test_exception(&dummy_ctrl, "Unused components", rose_etsi_unused, sizeof(rose_etsi_unused)); dummy_ctrl.switchtype = PRI_SWITCH_QSIG; rose_test_exception(&dummy_ctrl, "Multiple component messages", rose_qsig_multiple_msg, sizeof(rose_qsig_multiple_msg)); rose_test_exception(&dummy_ctrl, "Alternate name encoded messages", rose_qsig_name_alt_encode_msg, sizeof(rose_qsig_name_alt_encode_msg)); rose_test_exception(&dummy_ctrl, "2nd edition name encoded messages", rose_qsig_name_2nd_encode_msg, sizeof(rose_qsig_name_2nd_encode_msg)); /* ------------------------------------------------------------------- */ pri_message(&dummy_ctrl, "\n\n" "List of operation codes:\n"); for (index = 0; index < ROSE_Num_Operation_Codes; ++index) { str = rose_operation2str(index); if (!strncmp(str, "Invalid code:", 13)) { pri_error(&dummy_ctrl, "%d: %s\n", index, str); } else { pri_message(&dummy_ctrl, "%d: %s\n", index, str); } } pri_message(&dummy_ctrl, "\n\n" "************************************************************\n"); /* ------------------------------------------------------------------- */ pri_message(&dummy_ctrl, "\n\n" "List of error codes:\n"); for (index = 0; index < ROSE_ERROR_Num_Codes; ++index) { str = rose_error2str(index); if (!strncmp(str, "Invalid code:", 13)) { pri_error(&dummy_ctrl, "%d: %s\n", index, str); } else { pri_message(&dummy_ctrl, "%d: %s\n", index, str); } } pri_message(&dummy_ctrl, "\n\n" "************************************************************\n"); /* ------------------------------------------------------------------- */ pri_message(&dummy_ctrl, "\n\n"); pri_message(&dummy_ctrl, "sizeof(struct rose_message) = %u\n", (unsigned) sizeof(struct rose_message)); pri_message(&dummy_ctrl, "sizeof(struct rose_msg_invoke) = %u\n", (unsigned) sizeof(struct rose_msg_invoke)); pri_message(&dummy_ctrl, "sizeof(struct rose_msg_result) = %u\n", (unsigned) sizeof(struct rose_msg_result)); pri_message(&dummy_ctrl, "sizeof(struct rose_msg_error) = %u\n", (unsigned) sizeof(struct rose_msg_error)); pri_message(&dummy_ctrl, "sizeof(struct rose_msg_reject) = %u\n", (unsigned) sizeof(struct rose_msg_reject)); pri_message(&dummy_ctrl, "sizeof(union rose_msg_invoke_args) = %u\n", (unsigned) sizeof(union rose_msg_invoke_args)); pri_message(&dummy_ctrl, "sizeof(union rose_msg_result_args) = %u\n", (unsigned) sizeof(union rose_msg_result_args)); pri_message(&dummy_ctrl, "\n"); pri_message(&dummy_ctrl, "sizeof(struct roseQsigForwardingList) = %u\n", (unsigned) sizeof(struct roseQsigForwardingList)); pri_message(&dummy_ctrl, "\n"); pri_message(&dummy_ctrl, "sizeof(struct roseQsigCallRerouting_ARG) = %u\n", (unsigned) sizeof(struct roseQsigCallRerouting_ARG)); pri_message(&dummy_ctrl, "sizeof(struct roseQsigAocRateArg_ARG) = %u\n", (unsigned) sizeof(struct roseQsigAocRateArg_ARG)); pri_message(&dummy_ctrl, "sizeof(struct roseQsigMWIInterrogateRes) = %u\n", (unsigned) sizeof(struct roseQsigMWIInterrogateRes)); pri_message(&dummy_ctrl, "\n"); pri_message(&dummy_ctrl, "sizeof(struct roseEtsiForwardingList) = %u\n", (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", (unsigned) sizeof(struct roseEtsiCallRerouting_ARG)); pri_message(&dummy_ctrl, "sizeof(struct roseEtsiDiversionInformation_ARG) = %u\n", (unsigned) sizeof(struct roseEtsiDiversionInformation_ARG)); pri_message(&dummy_ctrl, "sizeof(struct roseEtsiAOCSCurrencyInfoList) = %u\n", (unsigned) sizeof(struct roseEtsiAOCSCurrencyInfoList)); /* ------------------------------------------------------------------- */ return 0; } /* ------------------------------------------------------------------- */ /* end rosetest.c */ libpri-1.4.14/rose_qsig_ct.c0000644000000000000000000007321411173441755014424 0ustar rootroot/* * 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 Call-Transfer-Operations (CT) * * Call-Transfer-Operations ECMA-178 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" /* ------------------------------------------------------------------- */ /*! * \brief Encode the Q.SIG CallTransferIdentify 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_CallTransferIdentify_RES(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args) { unsigned char *seq_len; const struct roseQsigCTIdentifyRes_RES *call_transfer_identify; call_transfer_identify = &args->qsig.CallTransferIdentify; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); ASN1_CALL(pos, asn1_enc_string_max(pos, end, ASN1_TYPE_NUMERIC_STRING, call_transfer_identify->call_id, sizeof(call_transfer_identify->call_id) - 1)); ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &call_transfer_identify->rerouting_number)); /* No extension to encode */ ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the Q.SIG CallTransferInitiate 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_CallTransferInitiate_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { unsigned char *seq_len; const struct roseQsigCTInitiateArg_ARG *call_transfer_initiate; call_transfer_initiate = &args->qsig.CallTransferInitiate; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); ASN1_CALL(pos, asn1_enc_string_max(pos, end, ASN1_TYPE_NUMERIC_STRING, call_transfer_initiate->call_id, sizeof(call_transfer_initiate->call_id) - 1)); ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &call_transfer_initiate->rerouting_number)); /* No extension to encode */ ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the Q.SIG CallTransferSetup 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_CallTransferSetup_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { unsigned char *seq_len; const struct roseQsigCTSetupArg_ARG *call_transfer_setup; call_transfer_setup = &args->qsig.CallTransferSetup; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); ASN1_CALL(pos, asn1_enc_string_max(pos, end, ASN1_TYPE_NUMERIC_STRING, call_transfer_setup->call_id, sizeof(call_transfer_setup->call_id) - 1)); /* No extension to encode */ ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the Q.SIG CallTransferActive 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_CallTransferActive_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { unsigned char *seq_len; const struct roseQsigCTActiveArg_ARG *call_transfer_active; call_transfer_active = &args->qsig.CallTransferActive; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); ASN1_CALL(pos, rose_enc_PresentedAddressScreened(ctrl, pos, end, &call_transfer_active->connected)); if (call_transfer_active->q931ie.length) { ASN1_CALL(pos, rose_enc_Q931ie(ctrl, pos, end, ASN1_CLASS_APPLICATION | 0, &call_transfer_active->q931ie)); } if (call_transfer_active->connected_name_present) { ASN1_CALL(pos, rose_enc_qsig_Name(ctrl, pos, end, &call_transfer_active->connected_name)); } /* No extension to encode */ ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the Q.SIG CallTransferComplete 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_CallTransferComplete_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { unsigned char *seq_len; const struct roseQsigCTCompleteArg_ARG *call_transfer_complete; call_transfer_complete = &args->qsig.CallTransferComplete; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, call_transfer_complete->end_designation)); ASN1_CALL(pos, rose_enc_PresentedNumberScreened(ctrl, pos, end, &call_transfer_complete->redirection)); if (call_transfer_complete->q931ie.length) { ASN1_CALL(pos, rose_enc_Q931ie(ctrl, pos, end, ASN1_CLASS_APPLICATION | 0, &call_transfer_complete->q931ie)); } if (call_transfer_complete->redirection_name_present) { ASN1_CALL(pos, rose_enc_qsig_Name(ctrl, pos, end, &call_transfer_complete->redirection_name)); } if (call_transfer_complete->call_status) { /* Not the DEFAULT value */ ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, call_transfer_complete->call_status)); } /* No extension to encode */ ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the Q.SIG CallTransferUpdate 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_CallTransferUpdate_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { unsigned char *seq_len; const struct roseQsigCTUpdateArg_ARG *call_transfer_update; call_transfer_update = &args->qsig.CallTransferUpdate; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); ASN1_CALL(pos, rose_enc_PresentedNumberScreened(ctrl, pos, end, &call_transfer_update->redirection)); if (call_transfer_update->redirection_name_present) { ASN1_CALL(pos, rose_enc_qsig_Name(ctrl, pos, end, &call_transfer_update->redirection_name)); } if (call_transfer_update->q931ie.length) { ASN1_CALL(pos, rose_enc_Q931ie(ctrl, pos, end, ASN1_CLASS_APPLICATION | 0, &call_transfer_update->q931ie)); } /* No extension to encode */ ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the Q.SIG SubaddressTransfer 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_SubaddressTransfer_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { unsigned char *seq_len; const struct roseQsigSubaddressTransferArg_ARG *subaddress_transfer; subaddress_transfer = &args->qsig.SubaddressTransfer; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); ASN1_CALL(pos, rose_enc_PartySubaddress(ctrl, pos, end, &subaddress_transfer->redirection_subaddress)); /* No extension to encode */ ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the Q.SIG DummyArg 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. * * \details * DummyArg ::= CHOICE { * none NULL, * extension [1] IMPLICIT Extension, * multipleExtension [2] IMPLICIT SEQUENCE OF Extension * } */ unsigned char *rose_enc_qsig_DummyArg_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { return asn1_enc_null(pos, end, ASN1_TYPE_NULL); } /*! * \brief Encode the Q.SIG DummyRes 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. * * \details * DummyRes ::= CHOICE { * none NULL, * extension [1] IMPLICIT Extension, * multipleExtension [2] IMPLICIT SEQUENCE OF Extension * } */ unsigned char *rose_enc_qsig_DummyRes_RES(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args) { return asn1_enc_null(pos, end, ASN1_TYPE_NULL); } /*! * \brief Decode the Q.SIG CallTransferIdentify 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_CallTransferIdentify_RES(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args) { size_t str_len; int length; int seq_offset; const unsigned char *seq_end; struct roseQsigCTIdentifyRes_RES *call_transfer_identify; call_transfer_identify = &args->qsig.CallTransferIdentify; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " CallTransferIdentify %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_CHECK_TAG(ctrl, tag & ~ASN1_PC_MASK, tag, ASN1_TYPE_NUMERIC_STRING); ASN1_CALL(pos, asn1_dec_string_max(ctrl, "callIdentity", tag, pos, seq_end, sizeof(call_transfer_identify->call_id), call_transfer_identify->call_id, &str_len)); ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "reroutingNumber", tag, pos, seq_end, &call_transfer_identify->rerouting_number)); /* Fixup will skip over any OPTIONAL manufacturer extension information */ ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the Q.SIG CallTransferInitiate 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_CallTransferInitiate_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { size_t str_len; int length; int seq_offset; const unsigned char *seq_end; struct roseQsigCTInitiateArg_ARG *call_transfer_initiate; call_transfer_initiate = &args->qsig.CallTransferInitiate; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " CallTransferInitiate %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_CHECK_TAG(ctrl, tag & ~ASN1_PC_MASK, tag, ASN1_TYPE_NUMERIC_STRING); ASN1_CALL(pos, asn1_dec_string_max(ctrl, "callIdentity", tag, pos, seq_end, sizeof(call_transfer_initiate->call_id), call_transfer_initiate->call_id, &str_len)); ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "reroutingNumber", tag, pos, seq_end, &call_transfer_initiate->rerouting_number)); /* Fixup will skip over any OPTIONAL manufacturer extension information */ ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the Q.SIG CallTransferSetup 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_CallTransferSetup_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { size_t str_len; int length; int seq_offset; const unsigned char *seq_end; struct roseQsigCTSetupArg_ARG *call_transfer_setup; call_transfer_setup = &args->qsig.CallTransferSetup; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " CallTransferSetup %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_CHECK_TAG(ctrl, tag & ~ASN1_PC_MASK, tag, ASN1_TYPE_NUMERIC_STRING); ASN1_CALL(pos, asn1_dec_string_max(ctrl, "callIdentity", tag, pos, seq_end, sizeof(call_transfer_setup->call_id), call_transfer_setup->call_id, &str_len)); /* Fixup will skip over any OPTIONAL manufacturer extension information */ ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the Q.SIG CallTransferActive 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_CallTransferActive_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { int length; int seq_offset; const unsigned char *seq_end; const unsigned char *save_pos; struct roseQsigCTActiveArg_ARG *call_transfer_active; call_transfer_active = &args->qsig.CallTransferActive; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " CallTransferActive %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_PresentedAddressScreened(ctrl, "connectedAddress", tag, pos, seq_end, &call_transfer_active->connected)); /* * A sequence specifies an ordered list of component types. * However, for simplicity we are not checking the order of * the remaining optional components. */ call_transfer_active->q931ie.length = 0; call_transfer_active->connected_name_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 & ~ASN1_PC_MASK) { case ASN1_CLASS_APPLICATION | 0: ASN1_CALL(pos, rose_dec_Q931ie(ctrl, "basicCallInfoElements", tag, pos, seq_end, &call_transfer_active->q931ie, sizeof(call_transfer_active->q931ie_contents))); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 0: case ASN1_CLASS_CONTEXT_SPECIFIC | 1: case ASN1_CLASS_CONTEXT_SPECIFIC | 2: case ASN1_CLASS_CONTEXT_SPECIFIC | 3: case ASN1_CLASS_CONTEXT_SPECIFIC | 4: case ASN1_CLASS_CONTEXT_SPECIFIC | 7: ASN1_CALL(pos, rose_dec_qsig_Name(ctrl, "connectedName", tag, pos, seq_end, &call_transfer_active->connected_name)); call_transfer_active->connected_name_present = 1; break; case ASN1_CLASS_CONTEXT_SPECIFIC | 9: case ASN1_CLASS_CONTEXT_SPECIFIC | 10: if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " argumentExtension %s\n", asn1_tag2str(tag)); } /* Fixup will skip over the manufacturer extension information */ 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 CallTransferComplete 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_CallTransferComplete_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; const unsigned char *save_pos; struct roseQsigCTCompleteArg_ARG *call_transfer_complete; call_transfer_complete = &args->qsig.CallTransferComplete; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " CallTransferComplete %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_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); ASN1_CALL(pos, asn1_dec_int(ctrl, "endDesignation", tag, pos, seq_end, &value)); call_transfer_complete->end_designation = value; ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CALL(pos, rose_dec_PresentedNumberScreened(ctrl, "redirectionNumber", tag, pos, seq_end, &call_transfer_complete->redirection)); /* * A sequence specifies an ordered list of component types. * However, for simplicity we are not checking the order of * the remaining optional components. */ call_transfer_complete->q931ie.length = 0; call_transfer_complete->redirection_name_present = 0; call_transfer_complete->call_status = 0; /* DEFAULT answered */ while (pos < seq_end && *pos != ASN1_INDEF_TERM) { save_pos = pos; ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); switch (tag & ~ASN1_PC_MASK) { case ASN1_CLASS_APPLICATION | 0: ASN1_CALL(pos, rose_dec_Q931ie(ctrl, "basicCallInfoElements", tag, pos, seq_end, &call_transfer_complete->q931ie, sizeof(call_transfer_complete->q931ie_contents))); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 0: case ASN1_CLASS_CONTEXT_SPECIFIC | 1: case ASN1_CLASS_CONTEXT_SPECIFIC | 2: case ASN1_CLASS_CONTEXT_SPECIFIC | 3: case ASN1_CLASS_CONTEXT_SPECIFIC | 4: case ASN1_CLASS_CONTEXT_SPECIFIC | 7: ASN1_CALL(pos, rose_dec_qsig_Name(ctrl, "redirectionName", tag, pos, seq_end, &call_transfer_complete->redirection_name)); call_transfer_complete->redirection_name_present = 1; break; case ASN1_TYPE_ENUMERATED: /* Must not be constructed but we will not check for it for simplicity. */ ASN1_CALL(pos, asn1_dec_int(ctrl, "callStatus", tag, pos, seq_end, &value)); call_transfer_complete->call_status = value; break; case ASN1_CLASS_CONTEXT_SPECIFIC | 9: case ASN1_CLASS_CONTEXT_SPECIFIC | 10: if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " argumentExtension %s\n", asn1_tag2str(tag)); } /* Fixup will skip over the manufacturer extension information */ 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 CallTransferUpdate 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_CallTransferUpdate_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { int length; int seq_offset; const unsigned char *seq_end; const unsigned char *save_pos; struct roseQsigCTUpdateArg_ARG *call_transfer_update; call_transfer_update = &args->qsig.CallTransferUpdate; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " CallTransferUpdate %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_PresentedNumberScreened(ctrl, "redirectionNumber", tag, pos, seq_end, &call_transfer_update->redirection)); /* * A sequence specifies an ordered list of component types. * However, for simplicity we are not checking the order of * the remaining optional components. */ call_transfer_update->redirection_name_present = 0; call_transfer_update->q931ie.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 & ~ASN1_PC_MASK) { case ASN1_CLASS_CONTEXT_SPECIFIC | 0: case ASN1_CLASS_CONTEXT_SPECIFIC | 1: case ASN1_CLASS_CONTEXT_SPECIFIC | 2: case ASN1_CLASS_CONTEXT_SPECIFIC | 3: case ASN1_CLASS_CONTEXT_SPECIFIC | 4: case ASN1_CLASS_CONTEXT_SPECIFIC | 7: ASN1_CALL(pos, rose_dec_qsig_Name(ctrl, "redirectionName", tag, pos, seq_end, &call_transfer_update->redirection_name)); call_transfer_update->redirection_name_present = 1; break; case ASN1_CLASS_APPLICATION | 0: ASN1_CALL(pos, rose_dec_Q931ie(ctrl, "basicCallInfoElements", tag, pos, seq_end, &call_transfer_update->q931ie, sizeof(call_transfer_update->q931ie_contents))); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 9: case ASN1_CLASS_CONTEXT_SPECIFIC | 10: if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " argumentExtension %s\n", asn1_tag2str(tag)); } /* Fixup will skip over the manufacturer extension information */ 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 SubaddressTransfer 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_SubaddressTransfer_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { int length; int seq_offset; const unsigned char *seq_end; struct roseQsigSubaddressTransferArg_ARG *subaddress_transfer; subaddress_transfer = &args->qsig.SubaddressTransfer; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " SubaddressTransfer %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_PartySubaddress(ctrl, "redirectionSubaddress", tag, pos, seq_end, &subaddress_transfer->redirection_subaddress)); /* Fixup will skip over any OPTIONAL manufacturer extension information */ ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the Q.SIG DummyArg 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. * * \details * DummyArg ::= CHOICE { * none NULL, * extension [1] IMPLICIT Extension, * multipleExtension [2] IMPLICIT SEQUENCE OF Extension * } */ const unsigned char *rose_dec_qsig_DummyArg_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { const char *name; int length; int seq_offset; const unsigned char *seq_end; switch (tag) { case ASN1_TYPE_NULL: return asn1_dec_null(ctrl, "none", tag, pos, end); case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1: name = "extension Extension"; break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2: name = "multipleExtension SEQUENCE OF Extension"; 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(seq_end, seq_offset, length, pos, end); /* Fixup will skip over the manufacturer extension information */ ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the Q.SIG DummyRes 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. * * \details * DummyRes ::= CHOICE { * none NULL, * extension [1] IMPLICIT Extension, * multipleExtension [2] IMPLICIT SEQUENCE OF Extension * } */ const unsigned char *rose_dec_qsig_DummyRes_RES(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args) { const char *name; int length; int seq_offset; const unsigned char *seq_end; switch (tag) { case ASN1_TYPE_NULL: return asn1_dec_null(ctrl, "none", tag, pos, end); case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1: name = "extension Extension"; break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2: name = "multipleExtension SEQUENCE OF Extension"; 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(seq_end, seq_offset, length, pos, end); /* Fixup will skip over the manufacturer extension information */ ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /* ------------------------------------------------------------------- */ /* end rose_qsig_ct.c */ libpri-1.4.14/libpri.h0000644000000000000000000021621611772721141013225 0ustar rootroot/* * libpri: An implementation of Primary Rate ISDN * * Written by Mark Spencer * * Copyright (C) 2001, Digium, Inc. * All Rights Reserved. */ /* * 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. */ /* * NOTE: * All new global identifiers that are added to this file MUST be * prefixed with PRI_ or pri_ to indicate that they are part of this * library and to reduce potential naming conflicts. */ #ifndef _LIBPRI_H #define _LIBPRI_H /* Node types */ #define PRI_NETWORK 1 #define PRI_CPE 2 /* Debugging */ #define PRI_DEBUG_Q921_RAW (1 << 0) /* Show raw HDLC frames */ #define PRI_DEBUG_Q921_DUMP (1 << 1) /* Show each interpreted Q.921 frame */ #define PRI_DEBUG_Q921_STATE (1 << 2) /* Debug state machine changes */ #define PRI_DEBUG_CONFIG (1 << 3) /* Display error events on stdout */ #define PRI_DEBUG_Q931_DUMP (1 << 5) /* Show interpreted Q.931 frames */ #define PRI_DEBUG_Q931_STATE (1 << 6) /* Debug Q.931 state machine changes */ #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 */ /* Switch types */ #define PRI_SWITCH_UNKNOWN 0 #define PRI_SWITCH_NI2 1 /* National ISDN 2 */ #define PRI_SWITCH_DMS100 2 /* DMS 100 */ #define PRI_SWITCH_LUCENT5E 3 /* Lucent 5E */ #define PRI_SWITCH_ATT4ESS 4 /* AT&T 4ESS */ #define PRI_SWITCH_EUROISDN_E1 5 /* Standard EuroISDN (CTR4, ETSI 300-102) */ #define PRI_SWITCH_EUROISDN_T1 6 /* T1 EuroISDN variant (ETSI 300-102) */ #define PRI_SWITCH_NI1 7 /* National ISDN 1 */ #define PRI_SWITCH_GR303_EOC 8 /* GR-303 Embedded Operations Channel */ #define PRI_SWITCH_GR303_TMC 9 /* GR-303 Timeslot Management Channel */ #define PRI_SWITCH_QSIG 10 /* QSIG Switch */ /* Switchtypes 11 - 20 are reserved for internal use */ /* PRI D-Channel Events */ #define PRI_EVENT_DCHAN_UP 1 /* D-channel is up */ #define PRI_EVENT_DCHAN_DOWN 2 /* D-channel is down */ #define PRI_EVENT_RESTART 3 /* B-channel is restarted */ #define PRI_EVENT_CONFIG_ERR 4 /* Configuration Error Detected */ #define PRI_EVENT_RING 5 /* Incoming call (SETUP) */ #define PRI_EVENT_HANGUP 6 /* Call got hung up (RELEASE/RELEASE_COMPLETE/other) */ #define PRI_EVENT_RINGING 7 /* Call is ringing (ALERTING) */ #define PRI_EVENT_ANSWER 8 /* Call has been answered (CONNECT) */ #define PRI_EVENT_HANGUP_ACK 9 /* Call hangup has been acknowledged */ #define PRI_EVENT_RESTART_ACK 10 /* Restart complete on a given channel (RESTART_ACKNOWLEDGE) */ #define PRI_EVENT_FACNAME 11 /* Caller*ID Name received on Facility (DEPRECATED) */ #define PRI_EVENT_FACILITY 11 /* Facility received (FACILITY) */ #define PRI_EVENT_INFO_RECEIVED 12 /* Additional info (digits) received (INFORMATION) */ #define PRI_EVENT_PROCEEDING 13 /* When we get CALL_PROCEEDING */ #define PRI_EVENT_SETUP_ACK 14 /* When we get SETUP_ACKNOWLEDGE */ #define PRI_EVENT_HANGUP_REQ 15 /* Requesting the higher layer to hangup (DISCONNECT) */ #define PRI_EVENT_NOTIFY 16 /* Notification received (NOTIFY) */ #define PRI_EVENT_PROGRESS 17 /* When we get PROGRESS */ #define PRI_EVENT_KEYPAD_DIGIT 18 /* When we receive during ACTIVE state (INFORMATION) */ #define PRI_EVENT_SERVICE 19 /* SERVICE maintenance message */ #define PRI_EVENT_SERVICE_ACK 20 /* SERVICE maintenance acknowledgement message */ #define PRI_EVENT_HOLD 21 /* HOLD request received */ #define PRI_EVENT_HOLD_ACK 22 /* HOLD_ACKNOWLEDGE received */ #define PRI_EVENT_HOLD_REJ 23 /* HOLD_REJECT received */ #define PRI_EVENT_RETRIEVE 24 /* RETRIEVE request received */ #define PRI_EVENT_RETRIEVE_ACK 25 /* RETRIEVE_ACKNOWLEDGE received */ #define PRI_EVENT_RETRIEVE_REJ 26 /* RETRIEVE_REJECT received */ #define PRI_EVENT_CONNECT_ACK 27 /* CONNECT_ACKNOWLEDGE received */ /* Simple states */ #define PRI_STATE_DOWN 0 #define PRI_STATE_UP 1 #define PRI_PROGRESS_MASK /* Progress indicator values */ #define PRI_PROG_CALL_NOT_E2E_ISDN (1 << 0) #define PRI_PROG_CALLED_NOT_ISDN (1 << 1) #define PRI_PROG_CALLER_NOT_ISDN (1 << 2) #define PRI_PROG_INBAND_AVAILABLE (1 << 3) #define PRI_PROG_DELAY_AT_INTERF (1 << 4) #define PRI_PROG_INTERWORKING_WITH_PUBLIC (1 << 5) #define PRI_PROG_INTERWORKING_NO_RELEASE (1 << 6) #define PRI_PROG_INTERWORKING_NO_RELEASE_PRE_ANSWER (1 << 7) #define PRI_PROG_INTERWORKING_NO_RELEASE_POST_ANSWER (1 << 8) #define PRI_PROG_CALLER_RETURNED_TO_ISDN (1 << 9) /* Numbering plan identifier */ #define PRI_NPI_UNKNOWN 0x0 /*!< Unknown numbering plan */ #define PRI_NPI_E163_E164 0x1 /*!< ISDN/telephony numbering plan (public) */ #define PRI_NPI_X121 0x3 /*!< Data numbering plan */ #define PRI_NPI_F69 0x4 /*!< Telex numbering plan */ #define PRI_NPI_NATIONAL 0x8 /*!< National standard numbering plan */ #define PRI_NPI_PRIVATE 0x9 /*!< Private numbering plan */ #define PRI_NPI_RESERVED 0xF /*!< Reserved for extension */ /* Type of number */ #define PRI_TON_UNKNOWN 0x0 #define PRI_TON_INTERNATIONAL 0x1 #define PRI_TON_NATIONAL 0x2 #define PRI_TON_NET_SPECIFIC 0x3 #define PRI_TON_SUBSCRIBER 0x4 #define PRI_TON_ABBREVIATED 0x6 #define PRI_TON_RESERVED 0x7 /* Redirection reasons */ #define PRI_REDIR_UNKNOWN 0x0 #define PRI_REDIR_FORWARD_ON_BUSY 0x1 #define PRI_REDIR_FORWARD_ON_NO_REPLY 0x2 #define PRI_REDIR_DEFLECTION 0x3 #define PRI_REDIR_DTE_OUT_OF_ORDER 0x9 #define PRI_REDIR_FORWARDED_BY_DTE 0xA #define PRI_REDIR_UNCONDITIONAL 0xF /* Dialing plan */ #define PRI_INTERNATIONAL_ISDN 0x11 #define PRI_NATIONAL_ISDN 0x21 #define PRI_LOCAL_ISDN 0x41 #define PRI_PRIVATE 0x49 #define PRI_UNKNOWN 0x0 /* Presentation */ #define PRI_PRES_NUMBER_TYPE 0x03 #define PRI_PRES_USER_NUMBER_UNSCREENED 0x00 #define PRI_PRES_USER_NUMBER_PASSED_SCREEN 0x01 #define PRI_PRES_USER_NUMBER_FAILED_SCREEN 0x02 #define PRI_PRES_NETWORK_NUMBER 0x03 #define PRI_PRES_RESTRICTION 0x60 #define PRI_PRES_ALLOWED 0x00 #define PRI_PRES_RESTRICTED 0x20 #define PRI_PRES_UNAVAILABLE 0x40 #define PRI_PRES_RESERVED 0x60 #define PRES_ALLOWED_USER_NUMBER_NOT_SCREENED \ (PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED) #define PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN \ (PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_PASSED_SCREEN) #define PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN \ (PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_FAILED_SCREEN) #define PRES_ALLOWED_NETWORK_NUMBER \ (PRI_PRES_ALLOWED | PRI_PRES_NETWORK_NUMBER) #define PRES_PROHIB_USER_NUMBER_NOT_SCREENED \ (PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED) #define PRES_PROHIB_USER_NUMBER_PASSED_SCREEN \ (PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_PASSED_SCREEN) #define PRES_PROHIB_USER_NUMBER_FAILED_SCREEN \ (PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_FAILED_SCREEN) #define PRES_PROHIB_NETWORK_NUMBER \ (PRI_PRES_RESTRICTED | PRI_PRES_NETWORK_NUMBER) #define PRES_NUMBER_NOT_AVAILABLE \ (PRI_PRES_UNAVAILABLE | PRI_PRES_NETWORK_NUMBER) /* Reverse Charging Indication */ #define PRI_REVERSECHARGE_NONE -1 #define PRI_REVERSECHARGE_REQUESTED 1 /* Causes for disconnection (See Q.850) */ #define PRI_CAUSE_UNALLOCATED 1 /*!< Called number unassigned. */ #define PRI_CAUSE_NO_ROUTE_TRANSIT_NET 2 /* !Q.SIG */ #define PRI_CAUSE_NO_ROUTE_DESTINATION 3 #define PRI_CAUSE_CHANNEL_UNACCEPTABLE 6 #define PRI_CAUSE_CALL_AWARDED_DELIVERED 7 /* !Q.SIG */ #define PRI_CAUSE_NORMAL_CLEARING 16 #define PRI_CAUSE_USER_BUSY 17 #define PRI_CAUSE_NO_USER_RESPONSE 18 #define PRI_CAUSE_NO_ANSWER 19 #define PRI_CAUSE_CALL_REJECTED 21 #define PRI_CAUSE_NUMBER_CHANGED 22 #define PRI_CAUSE_NONSELECTED_USER_CLEARING 26 #define PRI_CAUSE_DESTINATION_OUT_OF_ORDER 27 #define PRI_CAUSE_INVALID_NUMBER_FORMAT 28 #define PRI_CAUSE_FACILITY_REJECTED 29 /* !Q.SIG */ #define PRI_CAUSE_RESPONSE_TO_STATUS_ENQUIRY 30 #define PRI_CAUSE_NORMAL_UNSPECIFIED 31 #define PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION 34 #define PRI_CAUSE_NETWORK_OUT_OF_ORDER 38 /* !Q.SIG */ #define PRI_CAUSE_NORMAL_TEMPORARY_FAILURE 41 #define PRI_CAUSE_SWITCH_CONGESTION 42 /* !Q.SIG */ #define PRI_CAUSE_ACCESS_INFO_DISCARDED 43 /* !Q.SIG */ #define PRI_CAUSE_REQUESTED_CHAN_UNAVAIL 44 #define PRI_CAUSE_PRE_EMPTED 45 /* !Q.SIG */ #define PRI_CAUSE_RESOURCE_UNAVAIL_UNSPECIFIED 47 #define PRI_CAUSE_FACILITY_NOT_SUBSCRIBED 50 /* !Q.SIG */ #define PRI_CAUSE_OUTGOING_CALL_BARRED 52 /* !Q.SIG */ #define PRI_CAUSE_INCOMING_CALL_BARRED 54 /* !Q.SIG */ #define PRI_CAUSE_BEARERCAPABILITY_NOTAUTH 57 #define PRI_CAUSE_BEARERCAPABILITY_NOTAVAIL 58 #define PRI_CAUSE_SERVICEOROPTION_NOTAVAIL 63 /* Q.SIG */ #define PRI_CAUSE_BEARERCAPABILITY_NOTIMPL 65 #define PRI_CAUSE_CHAN_NOT_IMPLEMENTED 66 /* !Q.SIG */ #define PRI_CAUSE_FACILITY_NOT_IMPLEMENTED 69 /* !Q.SIG */ #define PRI_CAUSE_INVALID_CALL_REFERENCE 81 #define PRI_CAUSE_IDENTIFIED_CHANNEL_NOTEXIST 82 /* Q.SIG */ #define PRI_CAUSE_INCOMPATIBLE_DESTINATION 88 #define PRI_CAUSE_INVALID_MSG_UNSPECIFIED 95 /* !Q.SIG */ #define PRI_CAUSE_MANDATORY_IE_MISSING 96 #define PRI_CAUSE_MESSAGE_TYPE_NONEXIST 97 #define PRI_CAUSE_WRONG_MESSAGE 98 #define PRI_CAUSE_IE_NONEXIST 99 #define PRI_CAUSE_INVALID_IE_CONTENTS 100 #define PRI_CAUSE_WRONG_CALL_STATE 101 #define PRI_CAUSE_RECOVERY_ON_TIMER_EXPIRE 102 #define PRI_CAUSE_MANDATORY_IE_LENGTH_ERROR 103 /* !Q.SIG */ #define PRI_CAUSE_PROTOCOL_ERROR 111 #define PRI_CAUSE_INTERWORKING 127 /* !Q.SIG */ /* Transmit capabilities */ #define PRI_TRANS_CAP_SPEECH 0x0 #define PRI_TRANS_CAP_DIGITAL 0x08 #define PRI_TRANS_CAP_RESTRICTED_DIGITAL 0x09 #define PRI_TRANS_CAP_3_1K_AUDIO 0x10 #define PRI_TRANS_CAP_7K_AUDIO 0x11 /* Depriciated ITU Q.931 (05/1998)*/ #define PRI_TRANS_CAP_DIGITAL_W_TONES 0x11 #define PRI_TRANS_CAP_VIDEO 0x18 #define PRI_LAYER_1_ITU_RATE_ADAPT 0x21 #define PRI_LAYER_1_ULAW 0x22 #define PRI_LAYER_1_ALAW 0x23 #define PRI_LAYER_1_G721 0x24 #define PRI_LAYER_1_G722_G725 0x25 #define PRI_LAYER_1_H223_H245 0x26 #define PRI_LAYER_1_NON_ITU_ADAPT 0x27 #define PRI_LAYER_1_V120_RATE_ADAPT 0x28 #define PRI_LAYER_1_X31_RATE_ADAPT 0x29 /* Intermediate rates for V.110 */ #define PRI_INT_RATE_8K 1 #define PRI_INT_RATE_16K 2 #define PRI_INT_RATE_32K 3 /* Rate adaption for bottom 5 bits of rateadaption */ #define PRI_RATE_USER_RATE_MASK 0x1F #define PRI_RATE_ADAPT_UNSPEC 0x00 #define PRI_RATE_ADAPT_0K6 0x01 #define PRI_RATE_ADAPT_1K2 0x02 #define PRI_RATE_ADAPT_2K4 0x03 #define PRI_RATE_ADAPT_3K6 0x04 #define PRI_RATE_ADAPT_4K8 0x05 #define PRI_RATE_ADAPT_7K2 0x06 #define PRI_RATE_ADAPT_8K 0x07 #define PRI_RATE_ADAPT_9K6 0x08 #define PRI_RATE_ADAPT_14K4 0x09 #define PRI_RATE_ADAPT_16K 0x0A #define PRI_RATE_ADAPT_19K2 0x0B #define PRI_RATE_ADAPT_32K 0x0C #define PRI_RATE_ADAPT_38K4 0x0D #define PRI_RATE_ADAPT_48K 0x0E #define PRI_RATE_ADAPT_56K 0x0F #define PRI_RATE_ADAPT_57K6 0x12 #define PRI_RATE_ADAPT_28K8 0x13 #define PRI_RATE_ADAPT_24K 0x14 #define PRI_RATE_ADAPT_0K1345 0x15 #define PRI_RATE_ADAPT_0K1 0x16 #define PRI_RATE_ADAPT_0K075_1K2 0x17 #define PRI_RATE_ADAPT_1K2_0K075 0x18 #define PRI_RATE_ADAPT_0K05 0x19 #define PRI_RATE_ADAPT_0K075 0x1A #define PRI_RATE_ADAPT_0K110 0x1B #define PRI_RATE_ADAPT_0K150 0x1C #define PRI_RATE_ADAPT_0K200 0x1D #define PRI_RATE_ADAPT_0K300 0x1E #define PRI_RATE_ADAPT_12K 0x1F /* in-band negotiation flag for rateadaption bit 5 */ #define PRI_RATE_ADAPT_NEGOTIATION_POSS 0x20 /* async flag for rateadaption bit 6 */ #define PRI_RATE_ADAPT_ASYNC 0x40 /* Notifications */ #define PRI_NOTIFY_USER_SUSPENDED 0x00 /* User suspended (Q.931) (Call is placed on hold) */ #define PRI_NOTIFY_USER_RESUMED 0x01 /* User resumed (Q.931) (Call is taken off hold) */ #define PRI_NOTIFY_BEARER_CHANGE 0x02 /* Bearer service change (DSS1) */ #define PRI_NOTIFY_ASN1_COMPONENT 0x03 /* ASN.1 encoded component (DSS1) */ #define PRI_NOTIFY_COMPLETION_DELAY 0x04 /* Call completion delay */ #define PRI_NOTIFY_CONF_ESTABLISHED 0x42 /* Conference established */ #define PRI_NOTIFY_CONF_DISCONNECTED 0x43 /* Conference disconnected */ #define PRI_NOTIFY_CONF_PARTY_ADDED 0x44 /* Other party added */ #define PRI_NOTIFY_CONF_ISOLATED 0x45 /* Isolated */ #define PRI_NOTIFY_CONF_REATTACHED 0x46 /* Reattached */ #define PRI_NOTIFY_CONF_OTHER_ISOLATED 0x47 /* Other party isolated */ #define PRI_NOTIFY_CONF_OTHER_REATTACHED 0x48 /* Other party reattached */ #define PRI_NOTIFY_CONF_OTHER_SPLIT 0x49 /* Other party split */ #define PRI_NOTIFY_CONF_OTHER_DISCONNECTED 0x4a /* Other party disconnected */ #define PRI_NOTIFY_CONF_FLOATING 0x4b /* Conference floating */ #define PRI_NOTIFY_WAITING_CALL 0x60 /* Call is waiting call */ #define PRI_NOTIFY_DIVERSION_ACTIVATED 0x68 /* Diversion activated (DSS1) (cfu, cfb, cfnr) (EN 300 207-1 Section 7.2.1) */ #define PRI_NOTIFY_TRANSFER_ALERTING 0x69 /* Call transfer, alerting (EN 300 369-1 Section 7.2) */ #define PRI_NOTIFY_TRANSFER_ACTIVE 0x6a /* Call transfer, active(answered) (EN 300 369-1 Section 7.2) */ #define PRI_NOTIFY_REMOTE_HOLD 0x79 /* Remote hold */ #define PRI_NOTIFY_REMOTE_RETRIEVAL 0x7a /* Remote retrieval */ #define PRI_NOTIFY_CALL_DIVERTING 0x7b /* Call is diverting (EN 300 207-1 Section 7.2.1) */ #define PRI_COPY_DIGITS_CALLED_NUMBER /* Network Specific Facilities (AT&T) */ #define PRI_NSF_NONE -1 #define PRI_NSF_SID_PREFERRED 0xB1 #define PRI_NSF_ANI_PREFERRED 0xB2 #define PRI_NSF_SID_ONLY 0xB3 #define PRI_NSF_ANI_ONLY 0xB4 #define PRI_NSF_CALL_ASSOC_TSC 0xB9 #define PRI_NSF_NOTIF_CATSC_CLEARING 0xBA #define PRI_NSF_OPERATOR 0xB5 #define PRI_NSF_PCCO 0xB6 #define PRI_NSF_SDN 0xE1 #define PRI_NSF_TOLL_FREE_MEGACOM 0xE2 #define PRI_NSF_MEGACOM 0xE3 #define PRI_NSF_ACCUNET 0xE6 #define PRI_NSF_LONG_DISTANCE_SERVICE 0xE7 #define PRI_NSF_INTERNATIONAL_TOLL_FREE 0xE8 #define PRI_NSF_ATT_MULTIQUEST 0xF0 #define PRI_NSF_CALL_REDIRECTION_SERVICE 0xF7 typedef struct q931_call q931_call; /* Name character set enumeration values */ #define PRI_CHAR_SET_UNKNOWN 0 #define PRI_CHAR_SET_ISO8859_1 1 #define PRI_CHAR_SET_WITHDRAWN 2 #define PRI_CHAR_SET_ISO8859_2 3 #define PRI_CHAR_SET_ISO8859_3 4 #define PRI_CHAR_SET_ISO8859_4 5 #define PRI_CHAR_SET_ISO8859_5 6 #define PRI_CHAR_SET_ISO8859_7 7 #define PRI_CHAR_SET_ISO10646_BMPSTRING 8 #define PRI_CHAR_SET_ISO10646_UTF_8STRING 9 /*! \brief Q.SIG name information. */ struct pri_party_name { /*! \brief TRUE if the name information is valid/present */ int valid; /*! * \brief Q.931 presentation-indicator encoded field * \note Must tollerate the Q.931 screening-indicator field values being present. */ int presentation; /*! * \brief Character set the name is using. * \details * unknown(0), * iso8859-1(1), * enum-value-withdrawn-by-ITU-T(2) * iso8859-2(3), * iso8859-3(4), * iso8859-4(5), * iso8859-5(6), * iso8859-7(7), * iso10646-BmpString(8), * iso10646-utf-8String(9) * \details * Set to iso8859-1(1) if unsure what to use. */ int char_set; /*! \brief Name data with null terminator. */ char str[64]; }; struct pri_party_number { /*! \brief TRUE if the number information is valid/present */ int valid; /*! \brief Q.931 presentation-indicator and screening-indicator encoded fields */ int presentation; /*! \brief Q.931 Type-Of-Number and numbering-plan encoded fields */ int plan; /*! \brief Number data with null terminator. */ char str[64]; }; /*! * \note This structure is a place holder for possible future subaddress support * to maintain ABI compatibility. */ struct pri_party_subaddress { /*! \brief TRUE if the subaddress information is valid/present */ int valid; /*! * \brief Subaddress type. * \details * nsap(0), * user_specified(2) */ int type; /*! * \brief TRUE if odd number of address signals * \note The odd/even indicator is used when the type of subaddress is * user_specified and the coding is BCD. */ int odd_even_indicator; /*! \brief Length of the subaddress data */ int length; /*! * \brief Subaddress data with null terminator. * \note The null terminator is a convenience only since the data could be * BCD/binary and thus have a null byte as part of the contents. */ 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 */ struct pri_party_name name; /*! \brief Subscriber phone number */ struct pri_party_number number; /*! \brief Subscriber subaddress */ struct pri_party_subaddress subaddress; }; /*! \brief Connected Line/Party information */ struct pri_party_connected_line { /*! Connected party ID */ struct pri_party_id id; }; /*! * \brief Redirecting Line information. * \details * RDNIS (Redirecting Directory Number Information Service) * Where a call diversion or transfer was invoked. */ struct pri_party_redirecting { /*! Who is redirecting the call (Sent to the party the call is redirected toward) */ struct pri_party_id from; /*! Call is redirecting to a new party (Sent to the caller) */ struct pri_party_id to; /*! Originally called party (in cases of multiple redirects) */ struct pri_party_id orig_called; /*! Number of times the call was redirected */ int count; /*! Original reason for redirect (in cases of multiple redirects) */ int orig_reason; /*! Redirection reason */ int reason; }; /*! * \brief Information for rerouting/deflecting the call. */ struct pri_rerouting_data { /*! * \brief Updated caller-id information. * \note The information may have been altered by procedure in the private network. */ struct pri_party_id caller; /*! * \note * deflection.to is the new called number and must always be present. */ struct pri_party_redirecting deflection; /*! * \brief Diverting user subscription option to specify if caller is notified. * \details * noNotification(0), * notificationWithoutDivertedToNr(1), * notificationWithDivertedToNr(2), * notApplicable(3) (Status only.) */ int subscription_option; /*! Invocation ID to use when sending a reply to the call rerouting/deflection request. */ int invoke_id; }; /* * 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 */ #define PRI_SUBCMD_TRANSFER_CALL 17 /*!< Request to transfer the specified calls together. */ #define PRI_SUBCMD_AOC_S 18 /*!< Advice Of Charge Start information (Rate list) */ #define PRI_SUBCMD_AOC_D 19 /*!< Advice Of Charge During information */ #define PRI_SUBCMD_AOC_E 20 /*!< Advice Of Charge End information */ #define PRI_SUBCMD_AOC_CHARGING_REQ 21 /*!< Advice Of Charge Request information */ #define PRI_SUBCMD_AOC_CHARGING_REQ_RSP 22 /*!< Advice Of Charge Request Response information */ #define PRI_SUBCMD_MCID_REQ 23 /*!< Malicious Call ID Request */ #define PRI_SUBCMD_MCID_RSP 24 /*!< Malicious Call ID Request response */ #define PRI_SUBCMD_DISPLAY_TEXT 25 /*!< Received display ie text */ #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_subcmd_transfer { /*! \brief Opaque call pointer for transfer with other call. */ q931_call *call_1; /*! \brief Opaque call pointer for transfer with other call. */ q931_call *call_2; /*! TRUE if call_1 is on hold. */ int is_call_1_held; /*! TRUE if call_2 is on hold. */ int is_call_2_held; /*! Invocation ID to use when sending a reply to the transfer request. */ int invoke_id; }; /*! \brief What is being charged. */ enum PRI_AOC_CHARGED_ITEM { PRI_AOC_CHARGED_ITEM_NOT_AVAILABLE, PRI_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT, PRI_AOC_CHARGED_ITEM_BASIC_COMMUNICATION, PRI_AOC_CHARGED_ITEM_CALL_ATTEMPT, PRI_AOC_CHARGED_ITEM_CALL_SETUP, PRI_AOC_CHARGED_ITEM_USER_USER_INFO, PRI_AOC_CHARGED_ITEM_SUPPLEMENTARY_SERVICE, }; /*! \brief Rate method being used. */ enum PRI_AOC_RATE_TYPE { PRI_AOC_RATE_TYPE_NOT_AVAILABLE, PRI_AOC_RATE_TYPE_FREE, PRI_AOC_RATE_TYPE_FREE_FROM_BEGINNING, PRI_AOC_RATE_TYPE_DURATION, PRI_AOC_RATE_TYPE_FLAT, PRI_AOC_RATE_TYPE_VOLUME, PRI_AOC_RATE_TYPE_SPECIAL_CODE, }; enum PRI_AOC_TIME_SCALE { PRI_AOC_TIME_SCALE_HUNDREDTH_SECOND, PRI_AOC_TIME_SCALE_TENTH_SECOND, PRI_AOC_TIME_SCALE_SECOND, PRI_AOC_TIME_SCALE_TEN_SECOND, PRI_AOC_TIME_SCALE_MINUTE, PRI_AOC_TIME_SCALE_HOUR, PRI_AOC_TIME_SCALE_DAY, }; struct pri_aoc_time { /*! LengthOfTimeUnit (Not valid if length is zero.) */ long length; /*! \see enum PRI_AOC_TIME_SCALE */ int scale; }; enum PRI_AOC_MULTIPLIER { PRI_AOC_MULTIPLIER_THOUSANDTH, PRI_AOC_MULTIPLIER_HUNDREDTH, PRI_AOC_MULTIPLIER_TENTH, PRI_AOC_MULTIPLIER_ONE, PRI_AOC_MULTIPLIER_TEN, PRI_AOC_MULTIPLIER_HUNDRED, PRI_AOC_MULTIPLIER_THOUSAND, }; struct pri_aoc_amount { long cost; /*! \see enum PRI_AOC_MULTIPLIER */ int multiplier; }; struct pri_aoc_duration { struct pri_aoc_amount amount; struct pri_aoc_time time; /*! Not present if the granularity time is zero. */ struct pri_aoc_time granularity; /*! * \brief Charging interval type * \details * continuousCharging(0), * stepFunction(1) */ int charging_type; /*! Name of currency involved. Null terminated. */ char currency[10 + 1]; }; struct pri_aoc_flat { struct pri_aoc_amount amount; /*! Name of currency involved. Null terminated. */ char currency[10 + 1]; }; enum PRI_AOC_VOLUME_UNIT { PRI_AOC_VOLUME_UNIT_OCTET, PRI_AOC_VOLUME_UNIT_SEGMENT, PRI_AOC_VOLUME_UNIT_MESSAGE, }; struct pri_aoc_volume { struct pri_aoc_amount amount; /*! \see enum PRI_AOC_VOLUME_UNIT */ int unit; /*! Name of currency involved. Null terminated. */ char currency[10 + 1]; }; struct pri_aoc_s_element { /*! * \brief What is being charged. * \see enum PRI_AOC_CHARGED_ITEM */ int chargeable; /*! * \brief Rate method being used. * \see enum PRI_AOC_RATE_TYPE */ int rate_type; /*! \brief Charge rate being applied. */ union { struct pri_aoc_duration duration; struct pri_aoc_flat flat; struct pri_aoc_volume volume; int special; } rate; }; struct pri_subcmd_aoc_s { /*! * \brief Number of items in the rate list. * \note If the list is empty then the charging information is not available. */ int num_items; struct pri_aoc_s_element item[10]; }; enum PRI_AOC_DE_CHARGE { PRI_AOC_DE_CHARGE_NOT_AVAILABLE, PRI_AOC_DE_CHARGE_FREE, PRI_AOC_DE_CHARGE_CURRENCY, PRI_AOC_DE_CHARGE_UNITS, }; struct pri_aoc_recorded_currency { struct pri_aoc_amount amount; /*! Name of currency involved. Null terminated. */ char currency[10 + 1]; }; struct pri_aoc_units_element { /*! Number of units recorded. -1 if not available. */ long number; /*! Type of unit recorded. -1 if not available. */ int type; }; struct pri_aoc_recorded_units { int num_items; struct pri_aoc_units_element item[32]; }; enum PRI_AOC_D_BILLING_ID { PRI_AOC_D_BILLING_ID_NOT_AVAILABLE, PRI_AOC_D_BILLING_ID_NORMAL, PRI_AOC_D_BILLING_ID_REVERSE, PRI_AOC_D_BILLING_ID_CREDIT_CARD, }; struct pri_subcmd_aoc_d { /*! * \brief What is being charged. * \see enum PRI_AOC_DE_CHARGE */ int charge; /*! * \brief Billing accumulation * \details * subTotal(0), * total(1) */ int billing_accumulation; /*! \see enum PRI_AOC_D_BILLING_ID */ int billing_id; union { /*! Recorded currency */ struct pri_aoc_recorded_currency money; /*! Recorded units list */ struct pri_aoc_recorded_units unit; } recorded; }; enum PRI_AOC_E_BILLING_ID { PRI_AOC_E_BILLING_ID_NOT_AVAILABLE, PRI_AOC_E_BILLING_ID_NORMAL, PRI_AOC_E_BILLING_ID_REVERSE, PRI_AOC_E_BILLING_ID_CREDIT_CARD, PRI_AOC_E_BILLING_ID_CALL_FORWARDING_UNCONDITIONAL, PRI_AOC_E_BILLING_ID_CALL_FORWARDING_BUSY, PRI_AOC_E_BILLING_ID_CALL_FORWARDING_NO_REPLY, PRI_AOC_E_BILLING_ID_CALL_DEFLECTION, PRI_AOC_E_BILLING_ID_CALL_TRANSFER, }; enum PRI_AOC_E_CHARGING_ASSOCIATION { PRI_AOC_E_CHARGING_ASSOCIATION_NOT_AVAILABLE, PRI_AOC_E_CHARGING_ASSOCIATION_NUMBER, PRI_AOC_E_CHARGING_ASSOCIATION_ID, }; struct pri_aoc_e_charging_association { union { /*! Charged number */ struct pri_party_number number; /*! Charge identifier */ int id; } charge; /*! \see enum PRI_AOC_E_CHARGING_ASSOCIATION */ int charging_type; }; struct pri_subcmd_aoc_e { /*! * \brief What is being charged. * \see enum PRI_AOC_DE_CHARGE */ int charge; /*! \see enum PRI_AOC_E_BILLING_ID */ int billing_id; union { /*! Recorded currency */ struct pri_aoc_recorded_currency money; /*! Recorded units list */ struct pri_aoc_recorded_units unit; } recorded; /*! Charging association. */ struct pri_aoc_e_charging_association associated; }; enum PRI_AOC_REQ_RSP { /* Error Results */ PRI_AOC_REQ_RSP_ERROR_NOT_IMPLEMENTED, PRI_AOC_REQ_RSP_ERROR_NOT_AVAILABLE, PRI_AOC_REQ_RSP_ERROR_TIMEOUT, PRI_AOC_REQ_RSP_ERROR_REJECT, /* generic error result all other errors are lumped into */ PRI_AOC_REQ_RSP_ERROR, /* AOC Results */ PRI_AOC_REQ_RSP_CHARGING_INFO_FOLLOWS, PRI_AOC_REQ_RSP_CURRENCY_INFO_LIST, PRI_AOC_REQ_RSP_SPECIAL_ARR, }; enum PRI_AOC_REQUEST { PRI_AOC_REQUEST_S = (1 << 0), PRI_AOC_REQUEST_D = (1 << 1), PRI_AOC_REQUEST_E = (1 << 2), }; struct pri_subcmd_aoc_request_response { /*! * \brief aoc_s data from response */ struct pri_subcmd_aoc_s aoc_s; /*! * \brief if the aoc_s msg is present, this will be set */ int valid_aoc_s; /*! * \brief What type of aoc was requested. * \see enum PRI_AOC_REQUEST */ int charging_request; /*! * \brief response to the charging_request * \see enum PRI_AOC_REQ_RSP */ int charging_response; }; struct pri_subcmd_aoc_request { /*! * \brief What types of aoc are being requested. * \see enum PRI_AOC_REQUEST */ int charging_request; /*! * \brief Value given by the initiating request. */ int invoke_id; }; struct pri_subcmd_mcid_req { /*! * \brief Information libpri knows about the malicious caller. * \note For the convenience of the upper layer. This information * may be incomplete if the upper layer redacted some caller * information because it was restricted. */ struct pri_party_id originator; /*! \brief Information libpri knows about the callee. */ struct pri_party_id answerer; }; struct pri_subcmd_mcid_rsp { /*! * \brief MCID request response status. * \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_display_txt { /*! * \brief Character set the text is using. * \details * unknown(0), * iso8859-1(1), * enum-value-withdrawn-by-ITU-T(2) * iso8859-2(3), * iso8859-3(4), * iso8859-4(5), * iso8859-5(6), * iso8859-7(7), * iso10646-BmpString(8), * iso10646-utf-8String(9) */ int char_set; /*! * \brief Number of octets in the display message. * \note Not including any added null terminator. */ int length; /*! * \brief Display text data. * \note Null terminated on receive. * \note Does not need to be null terminated on send. */ char text[128]; }; struct pri_subcommand { /*! PRI_SUBCMD_xxx defined values */ int cmd; union { /*! Reserve room for possible expansion to maintain ABI compatibility. */ char reserve_space[512]; 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; struct pri_subcmd_transfer transfer; struct pri_subcmd_aoc_request aoc_request; struct pri_subcmd_aoc_request_response aoc_request_response; struct pri_subcmd_aoc_s aoc_s; struct pri_subcmd_aoc_d aoc_d; struct pri_subcmd_aoc_e aoc_e; struct pri_subcmd_mcid_req mcid_req; struct pri_subcmd_mcid_rsp mcid_rsp; struct pri_subcmd_display_txt display; } u; }; /* Max number of subcommands per event message */ #define PRI_MAX_SUBCOMMANDS 8 struct pri_subcommands { int counter_subcmd; struct pri_subcommand subcmd[PRI_MAX_SUBCOMMANDS]; }; /* * Event channel parameter encoding: * 3322 2222 2222 1111 1111 1100 0000 0000 * 1098 7654 3210 9876 5432 1098 7654 3210 * xxxx xxxx xxxx xEDC BBBBBBBBB AAAAAAAAA * * Bit field * A - B channel * B - Span (DS1) (0 - 127) * C - DS1 Explicit bit * D - D channel (cis_call) bit (status only) * E - Call is held bit (status only) * * B channel values: * 0 - No channel (ISDN uses for call waiting feature) * 1-127 - B channel # * 0xFF - Any channel (Also if whole channel value is -1 in event) */ typedef struct pri_event_generic { /* Events with no additional information fall in this category */ int e; } pri_event_generic; typedef struct pri_event_error { int e; char err[256]; } pri_event_error; typedef struct pri_event_restart { int e; int channel; } pri_event_restart; typedef struct pri_event_ringing { int e; int channel; int cref; int progress; int progressmask; q931_call *call; char useruserinfo[260]; /* User->User info */ struct pri_subcommands *subcmds; } pri_event_ringing; typedef struct pri_event_answer { int e; int channel; int cref; int progress; int progressmask; q931_call *call; char useruserinfo[260]; /* User->User info */ struct pri_subcommands *subcmds; } pri_event_answer; /*! Deprecated replaced by struct pri_event_facility. */ typedef struct pri_event_facname { int e; char callingname[256]; char callingnum[256]; int channel; int cref; q931_call *call; int callingpres; /* Presentation of Calling CallerID */ int callingplan; /* Dialing plan of Calling entity */ } pri_event_facname; struct pri_event_facility { int e; char callingname[256]; /*!< Deprecated, preserved for struct pri_event_facname compatibility */ char callingnum[256]; /*!< Deprecated, preserved for struct pri_event_facname compatibility */ int channel; int cref; /*! * \brief Master call or normal call. * \note Call pointer known about by upper layer. * \note NULL if dummy call reference. */ q931_call *call; int callingpres; /*!< Presentation of Calling CallerID (Deprecated, preserved for struct pri_event_facname compatibility) */ int callingplan; /*!< Dialing plan of Calling entity (Deprecated, preserved for struct pri_event_facname compatibility) */ struct pri_subcommands *subcmds; q931_call *subcall; /*!< Subcall to send any reply toward. */ }; #define PRI_CALLINGPLANANI #define PRI_CALLINGPLANRDNIS typedef struct pri_event_ring { int e; int channel; /* Channel requested */ int callingpres; /* Presentation of Calling CallerID */ int callingplanani; /* Dialing plan of Calling entity ANI */ int callingplan; /* Dialing plan of Calling entity */ char callingani[256]; /* Calling ANI */ char callingnum[256]; /* Calling number */ char callingname[256]; /* Calling name (if provided) */ int calledplan; /* Dialing plan of Called number */ int ani2; /* ANI II */ char callednum[256]; /* Called number */ char redirectingnum[256]; /* Redirecting number */ char redirectingname[256]; /* Redirecting name */ int redirectingreason; /* Reason for redirect */ int callingplanrdnis; /* Dialing plan of Redirecting Number */ char useruserinfo[260]; /* User->User info */ int flexible; /* Are we flexible with our channel selection? */ int cref; /* Call Reference Number */ int ctype; /* Call type (see PRI_TRANS_CAP_* */ int layer1; /* User layer 1 */ int complete; /* Have we seen "Complete" i.e. no more number? */ q931_call *call; /* Opaque call pointer */ char callingsubaddr[256]; /* Calling parties subaddress, backwards compatibility */ int progress; int progressmask; char origcalledname[256]; char origcallednum[256]; int callingplanorigcalled; /* Dialing plan of Originally Called Number */ int origredirectingreason; int reversecharge; struct pri_subcommands *subcmds; struct pri_party_id calling; /* Calling Party's info, initially subaddress' */ struct pri_party_subaddress called_subaddress; /* Called party's subaddress */ char keypad_digits[64]; /* Keypad digits in the SETUP message. */ } pri_event_ring; typedef struct pri_event_hangup { int e; int channel; /* Channel requested */ int cause; int cref; q931_call *call; /* Opaque call pointer of call hanging up. */ long aoc_units; /* Advice of Charge number of charged units */ char useruserinfo[260]; /* User->User info */ struct pri_subcommands *subcmds; /*! * \brief Opaque held call pointer for possible transfer to active call. * \note The call_held and call_active pointers must not be NULL if * transfer held call on disconnect is available. */ q931_call *call_held; /*! * \brief Opaque active call pointer for possible transfer with held call. * \note The call_held and call_active pointers must not be NULL if * transfer held call on disconnect is available. */ q931_call *call_active; } pri_event_hangup; typedef struct pri_event_restart_ack { int e; int channel; } pri_event_restart_ack; #define PRI_PROGRESS_CAUSE typedef struct pri_event_proceeding { int e; int channel; int cref; int progress; int progressmask; int cause; q931_call *call; struct pri_subcommands *subcmds; } pri_event_proceeding; typedef struct pri_event_setup_ack { int e; int channel; q931_call *call; struct pri_subcommands *subcmds; } pri_event_setup_ack; typedef struct pri_event_notify { int e; int channel; int info; struct pri_subcommands *subcmds; q931_call *call; } pri_event_notify; typedef struct pri_event_keypad_digit { int e; int channel; q931_call *call; char digits[64]; struct pri_subcommands *subcmds; } pri_event_keypad_digit; typedef struct pri_event_service { int e; int channel; int changestatus; } pri_event_service; typedef struct pri_event_service_ack { int e; int channel; int changestatus; } pri_event_service_ack; struct pri_event_hold { int e; int channel; q931_call *call; struct pri_subcommands *subcmds; }; struct pri_event_hold_ack { int e; int channel; q931_call *call; struct pri_subcommands *subcmds; }; struct pri_event_hold_rej { int e; int channel; q931_call *call; int cause; struct pri_subcommands *subcmds; }; struct pri_event_retrieve { int e; int channel; q931_call *call; int flexible; /* Are we flexible with our channel selection? */ struct pri_subcommands *subcmds; }; struct pri_event_retrieve_ack { int e; int channel; q931_call *call; struct pri_subcommands *subcmds; }; struct pri_event_retrieve_rej { int e; int channel; q931_call *call; int cause; struct pri_subcommands *subcmds; }; struct pri_event_connect_ack { int e; int channel; q931_call *call; struct pri_subcommands *subcmds; }; typedef union { int e; pri_event_generic gen; /* Generic view */ pri_event_restart restart; /* Restart view */ pri_event_error err; /* Error view */ pri_event_facname facname; /* Caller*ID Name on Facility (Deprecated, use pri_event.facility) */ pri_event_ring ring; /* Ring */ pri_event_hangup hangup; /* Hang up */ pri_event_ringing ringing; /* Ringing */ pri_event_answer answer; /* Answer */ pri_event_restart_ack restartack; /* Restart Acknowledge */ pri_event_proceeding proceeding; /* Call proceeding & Progress */ pri_event_setup_ack setup_ack; /* SETUP_ACKNOWLEDGE structure */ pri_event_notify notify; /* Notification */ pri_event_keypad_digit digit; /* Digits that come during a call */ pri_event_service service; /* service message */ pri_event_service_ack service_ack; /* service acknowledgement message */ struct pri_event_facility facility; struct pri_event_hold hold; struct pri_event_hold_ack hold_ack; struct pri_event_hold_rej hold_rej; struct pri_event_retrieve retrieve; struct pri_event_retrieve_ack retrieve_ack; struct pri_event_retrieve_rej retrieve_rej; struct pri_event_connect_ack connect_ack; } pri_event; struct pri; struct pri_sr; #define PRI_IO_FUNCS /* Type declaration for callbacks to read or write a HDLC frame as below */ typedef int (*pri_io_cb)(struct pri *pri, void *buf, int buflen); /* Create a D-channel on a given file descriptor. The file descriptor must be a channel operating in HDLC mode with FCS computed by the fd's driver. Also it must be NON-BLOCKING! Frames received on the fd should include FCS. Nodetype must be one of PRI_NETWORK or PRI_CPE. switchtype should be PRI_SWITCH_* */ struct pri *pri_new(int fd, int nodetype, int switchtype); struct pri *pri_new_bri(int fd, int ptpmode, int nodetype, int switchtype); /* Create D-channel just as above with user defined I/O callbacks and data */ struct pri *pri_new_cb(int fd, int nodetype, int switchtype, pri_io_cb io_read, pri_io_cb io_write, void *userdata); /* Create BRI D-channel just as above with user defined I/O callbacks and data */ struct pri *pri_new_bri_cb(int fd, int ptpmode, int nodetype, int switchtype, pri_io_cb io_read, pri_io_cb io_write, void *userdata); /* Retrieve the user data associated with the D channel */ void *pri_get_userdata(struct pri *pri); /* Set the user data associated with the D channel */ void pri_set_userdata(struct pri *pri, void *userdata); /* Set Network Specific Facility for PRI */ void pri_set_nsf(struct pri *pri, int nsf); /* Set debug parameters on PRI -- see above debug definitions */ void pri_set_debug(struct pri *pri, int debug); /* Get debug parameters on PRI -- see above debug definitions */ int pri_get_debug(struct pri *pri); #define PRI_FACILITY_ENABLE /* Enable transmission support of Facility IEs on the pri */ void pri_facility_enable(struct pri *pri); /* Run PRI on the given D-channel, taking care of any events that need to be handled. If block is set, it will block until an event occurs which needs to be handled */ pri_event *pri_dchannel_run(struct pri *pri, int block); /* Check for an outstanding event on the PRI */ pri_event *pri_check_event(struct pri *pri); /* Give a name to a given event ID */ char *pri_event2str(int id); /* Give a name to a node type */ char *pri_node2str(int id); /* Give a name to a switch type */ char *pri_switch2str(int id); /* Print an event */ void pri_dump_event(struct pri *pri, pri_event *e); /* Turn presentation into a string */ char *pri_pres2str(int pres); /* Turn numbering plan into a string */ 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); /* Send a digit in overlap mode */ int pri_information(struct pri *pri, q931_call *call, char digit); #define PRI_KEYPAD_FACILITY_TX /* Send a keypad facility string of digits */ int pri_keypad_facility(struct pri *pri, q931_call *call, const char *digits); /* Answer the incomplete(call without called number) call on the given channel. Set non-isdn to non-zero if you are not connecting to ISDN equipment */ int pri_need_more_info(struct pri *pri, q931_call *call, int channel, int nonisdn); /* Answer(CONNECT) the call on the given channel. Set non-isdn to non-zero if you are not connecting to ISDN equipment */ int pri_answer(struct pri *pri, q931_call *call, int channel, int nonisdn); /*! * \brief Send the manual CONNECT_ACKNOWLEDGE message. * * \param ctrl D channel controller. * \param call Q.931 call leg. * \param channel Selected channel to assign to the call waiting call. * Zero if do not include the channel id ie in the CONNECT_ACKNOWLEDGE message. * * \retval 0 on success. * \retval -1 on error. */ int pri_connect_ack(struct pri *ctrl, q931_call *call, int channel); /*! * \brief Set the manual CONNECT_ACKNOWLEDGE message enable flag. * * \param ctrl D channel controller. * \param enable TRUE to enable manual CONNECT_ACKNOWLEDGE message feature. * * \return Nothing */ void pri_connect_ack_enable(struct pri *ctrl, int enable); /*! * \brief Give connected line information to a call * \note Could be used instead of pri_sr_set_caller_party() before calling pri_setup(). */ int pri_connected_line_update(struct pri *pri, q931_call *call, const struct pri_party_connected_line *connected); /*! * \brief Give redirection information to a call * \note Could be used instead of pri_sr_set_redirecting_parties() before calling pri_setup(). */ int pri_redirecting_update(struct pri *pri, q931_call *call, const struct pri_party_redirecting *redirecting); /* Set CRV reference for GR-303 calls */ #undef pri_release #undef pri_disconnect /* backwards compatibility for those who don't use asterisk with libpri */ #define pri_release(a,b,c) \ pri_hangup(a,b,c) #define pri_disconnect(a,b,c) \ pri_hangup(a,b,c) /* Hangup a call */ #define PRI_HANGUP int pri_hangup(struct pri *pri, q931_call *call, int cause); /*! * \brief Set the call hangup fix enable flag. * * \param ctrl D channel controller. * \param enable TRUE to follow Q.931 Section 5.3.2 call hangup better. * FALSE for legacy behaviour. (Default FALSE if not called.) * * \return Nothing */ void pri_hangup_fix_enable(struct pri *ctrl, int enable); #define PRI_DESTROYCALL void pri_destroycall(struct pri *pri, q931_call *call); #define PRI_RESTART int pri_restart(struct pri *pri); int pri_reset(struct pri *pri, int channel); /* handle b-channel maintenance messages */ extern int pri_maintenance_service(struct pri *pri, int span, int channel, int changestatus); /* Create a new call */ q931_call *pri_new_call(struct pri *pri); /*! * \brief Deterimine if the given call control pointer is a dummy call. * * \retval TRUE if given call is a dummy call. * \retval FALSE otherwise. */ int pri_is_dummy_call(q931_call *call); /* Retrieve CRV reference for GR-303 calls. Returns >0 on success. */ int pri_get_crv(struct pri *pri, q931_call *call, int *callmode); /* Retrieve CRV reference for GR-303 calls. CRV must be >0, call mode should be 0 */ int pri_set_crv(struct pri *pri, q931_call *call, int crv, int callmode); /* How long until you need to poll for a new event */ struct timeval *pri_schedule_next(struct pri *pri); /* Run any pending schedule events */ extern pri_event *pri_schedule_run(struct pri *pri); extern pri_event *pri_schedule_run_tv(struct pri *pri, const struct timeval *now); int pri_call(struct pri *pri, q931_call *c, int transmode, int channel, int exclusive, int nonisdn, char *caller, int callerplan, char *callername, int callerpres, char *called, int calledplan, int ulayer1); struct pri_sr *pri_sr_new(void); void pri_sr_free(struct pri_sr *sr); int pri_sr_set_channel(struct pri_sr *sr, int channel, int exclusive, int nonisdn); int pri_sr_set_bearer(struct pri_sr *sr, int transmode, int userl1); int pri_sr_set_called(struct pri_sr *sr, char *called, int calledplan, int complete); /*! * \brief Set the caller party ID information in the call SETUP record. * * \param sr New call SETUP record. * \param caller Caller party ID information to set. * * \return Nothing */ void pri_sr_set_caller_party(struct pri_sr *sr, const struct pri_party_id *caller); /*! \note Use pri_sr_set_caller_party() instead to pass more precise caller information. */ int pri_sr_set_caller(struct pri_sr *sr, char *caller, char *callername, int callerplan, int callerpres); /*! * \brief Set the calling subaddress information in the call SETUP record. * * \param sr New call SETUP record. * \param subaddress information to set. * * \return Nothing */ void pri_sr_set_caller_subaddress(struct pri_sr *sr, const struct pri_party_subaddress *subaddress); /*! * \brief Set the called subaddress information in the call SETUP record. * * \param sr New call SETUP record. * \param subaddress information to set. * * \return Nothing */ void pri_sr_set_called_subaddress(struct pri_sr *sr, const struct pri_party_subaddress *subaddress); /*! * \brief Set the redirecting information in the call SETUP record. * * \param sr New call SETUP record. * \param caller Redirecting information to set. * * \return Nothing */ void pri_sr_set_redirecting_parties(struct pri_sr *sr, const struct pri_party_redirecting *redirecting); /*! \note Use pri_sr_set_redirecting_parties() instead to pass more precise redirecting information. */ int pri_sr_set_redirecting(struct pri_sr *sr, char *num, int plan, int pres, int reason); /*! * \brief Set the keypad digits in the call SETUP record. * * \param sr New call SETUP record. * \param keypad_digits Keypad digits to send. * * \return Nothing */ void pri_sr_set_keypad_digits(struct pri_sr *sr, const char *keypad_digits); #define PRI_USER_USER_TX /* Set the user user field. Warning! don't send binary data accross this field */ void pri_sr_set_useruser(struct pri_sr *sr, const char *userchars); void pri_sr_set_reversecharge(struct pri_sr *sr, int requested); void pri_call_set_useruser(q931_call *sr, const char *userchars); int pri_setup(struct pri *pri, q931_call *call, struct pri_sr *req); /*! * \brief Set a call as a call indpendent signalling connection (i.e. no bchan) * \note Call will automaticlly disconnect after signalling sent. */ int pri_sr_set_connection_call_independent(struct pri_sr *req); /*! * \brief Set a call as a call indpendent signalling connection (i.e. no bchan) * \note Call will stay connected until explicitly disconnected. */ int pri_sr_set_no_channel_call(struct pri_sr *req); /* Send an MWI indication to a remote location. If activate is non zero, activates, if zero, deactivates */ int pri_mwi_activate(struct pri *pri, q931_call *c, char *caller, int callerplan, char *callername, int callerpres, char *called, int calledplan); /* Send an MWI deactivate request to a remote location */ int pri_mwi_deactivate(struct pri *pri, q931_call *c, char *caller, int callerplan, char *callername, int callerpres, char *called, int calledplan); /*! * \brief Send a MWI indication on the specified D channel. * * \param ctrl D channel controller. * \param mailbox Party receiving notification. * \param basic_service Basic service enum (-1 if not present). * \param num_messages NumberOfMessages (-1 if not present). * \param caller_id Party leaving message (NULL if not present). * \param timestamp When message left. (Generalized Time format, NULL if not present) * \param message_reference Message reference number (-1 if not present). * \param message_status Message status: added(0), removed(1). * * \retval 0 on success. * \retval -1 on error. */ int pri_mwi_indicate(struct pri *ctrl, const struct pri_party_id *mailbox, int basic_service, int num_messages, const struct pri_party_id *caller_id, const char *timestamp, int message_reference, int message_status); /*! * \brief Send a MWI indication on the specified D channel. (Take two) * * \param ctrl D channel controller. * \param mailbox Party receiving notification. * \param vm_id Voicemail system number (NULL if not present). * \param basic_service Basic service enum (-1 if not present). * \param num_messages NumberOfMessages (-1 if not present). * \param caller_id Party leaving message (NULL if not present). * \param timestamp When message left. (Generalized Time format, NULL if not present) * \param message_reference Message reference number (-1 if not present). * \param message_status Message status: added(0), removed(1). * * \retval 0 on success. * \retval -1 on error. */ int pri_mwi_indicate_v2(struct pri *ctrl, const struct pri_party_id *mailbox, const struct pri_party_id *vm_id, int basic_service, int num_messages, const struct pri_party_id *caller_id, const char *timestamp, int message_reference, int message_status); /* Set service message support flag */ int pri_set_service_message_support(struct pri *pri, int supportflag); #define PRI_2BCT /* Attempt to pass the channels back to the NET side if compatable and * subscribed. Sometimes called 2 bchannel transfer (2BCT) */ int pri_channel_bridge(q931_call *call1, q931_call *call2); /* Override message and error stuff */ #define PRI_NEW_SET_API void pri_set_message(void (*__pri_error)(struct pri *pri, char *)); void pri_set_error(void (*__pri_error)(struct pri *pri, char *)); /* Set overlap mode */ #define PRI_SET_OVERLAPDIAL void pri_set_overlapdial(struct pri *pri,int state); /* QSIG logical channel mapping option, do not skip channel 16 */ #define PRI_SET_CHAN_MAPPING_LOGICAL void pri_set_chan_mapping_logical(struct pri *pri, int state); #define PRI_DUMP_INFO_STR char *pri_dump_info_str(struct pri *pri); /* Get file descriptor */ int pri_fd(struct pri *pri); #define PRI_PROGRESS /* Send progress */ int pri_progress(struct pri *pri, q931_call *c, int channel, int info); /* Send progress with cause IE */ int pri_progress_with_cause(struct pri *pri, q931_call *c, int channel, int info, int cause); #define PRI_PROCEEDING_FULL /* Send call proceeding */ int pri_proceeding(struct pri *pri, q931_call *c, int channel, int info); /* Enable inband progress when a DISCONNECT is received */ void pri_set_inbanddisconnect(struct pri *pri, unsigned int enable); /* Enslave a PRI to another, so they share the same call list (and maybe some timers) */ void pri_enslave(struct pri *master, struct pri *slave); /*! * \brief Request AOC on call setup * * \param call setup struct to set charging request info on * \param charging request to set on setup struct * * \retval 0 on success * \retval -1 on failure */ int pri_sr_set_aoc_charging_request(struct pri_sr *sr, int charging_request); /*! * \brief Send AOC Request Response to a request for AOC-S * * \param ctrl D channel controller. * \param call Q.931 call leg. * \param invoke_id for response message * \param aoc_s message for response * * \retval 0 on success * \retval -1 on failure */ int pri_aoc_s_request_response_send(struct pri *ctrl, q931_call *call, int invoke_id, const struct pri_subcmd_aoc_s *aoc_s); /*! * \brief Send AOC Request Response to a request for AOC-D or AOC-E * * \param ctrl D channel controller. * \param call Q.931 call leg. * \param response in form of enum PRI_AOC_REQ_RSP * \param invoke_id for response message * * \retval 0 on success * \retval -1 on failure */ int pri_aoc_de_request_response_send(struct pri *ctrl, q931_call *call, int response, int invoke_id); /*! * \brief Send AOC-S message. * * \param ctrl D channel controller. * \param call Q.931 call leg. * \param aoc_s message to send * * \retval 0 on success * \retval -1 on failure */ int pri_aoc_s_send(struct pri *ctrl, q931_call *c, const struct pri_subcmd_aoc_s *aoc_s); /*! * \brief Send AOC-D message. * * \param ctrl D channel controller. * \param call Q.931 call leg. * \param aoc_d message to send * * \retval 0 on success * \retval -1 on failure */ int pri_aoc_d_send(struct pri *ctrl, q931_call *c, const struct pri_subcmd_aoc_d *aoc_d); /*! * \brief Send AOC-E message. * * \param ctrl D channel controller. * \param call Q.931 call leg. * \param aoc_e message to send * * \retval 0 on success * \retval -1 on failure */ int pri_aoc_e_send(struct pri *ctrl, q931_call *c, const struct pri_subcmd_aoc_e *aoc_e); #define PRI_GR303_SUPPORT #define PRI_ENSLAVE_SUPPORT #define PRI_SETUP_CALL #define PRI_RECEIVE_SUBADDR #define PRI_REDIRECTING_REASON #define PRI_AOC_UNITS #define PRI_ANI /* Send notification */ int pri_notify(struct pri *pri, q931_call *c, int channel, int info); int pri_callrerouting_facility(struct pri *pri, q931_call *call, const char *dest, const char* original, const char* reason); /*! * \brief Set the call deflection/rerouting feature enable flag. * * \param ctrl D channel controller. * \param enable TRUE to enable call deflection/rerouting feature. * * \return Nothing */ void pri_reroute_enable(struct pri *ctrl, int enable); /*! * \brief Send the CallRerouting/CallDeflection message. * * \param ctrl D channel controller. * \param call Q.931 call leg. * \param caller Call rerouting/deflecting updated caller data. (NULL if data not updated.) * \param deflection Call rerouting/deflecting redirection data. * \param subscription_option Diverting user subscription option to specify if caller is notified. * * \note * deflection->to is the new called number and must always be present. * \note * subscription option: * noNotification(0), * notificationWithoutDivertedToNr(1), * notificationWithDivertedToNr(2) * * \retval 0 on success. * \retval -1 on error. */ int pri_reroute_call(struct pri *ctrl, q931_call *call, const struct pri_party_id *caller, const struct pri_party_redirecting *deflection, int subscription_option); enum PRI_REROUTING_RSP_CODE { /*! * Rerouting invocation accepted and the network provider option * "served user call retention on invocation of diversion" * is "clear call on invocation". */ PRI_REROUTING_RSP_OK_CLEAR, /*! * Rerouting invocation accepted and the network provider option * "served user call retention on invocation of diversion" * is "retain call until alerting begins at the deflected-to user". */ PRI_REROUTING_RSP_OK_RETAIN, PRI_REROUTING_RSP_NOT_SUBSCRIBED, PRI_REROUTING_RSP_NOT_AVAILABLE, /*! Supplementary service interaction not allowed. */ PRI_REROUTING_RSP_NOT_ALLOWED, PRI_REROUTING_RSP_INVALID_NUMBER, /*! Deflection to prohibited number (e.g., operator, police, emergency). */ PRI_REROUTING_RSP_SPECIAL_SERVICE_NUMBER, /*! Deflection to served user number. */ PRI_REROUTING_RSP_DIVERSION_TO_SELF, PRI_REROUTING_RSP_MAX_DIVERSIONS_EXCEEDED, PRI_REROUTING_RSP_RESOURCE_UNAVAILABLE, }; /*! * \brief Send the CallRerouteing/CallDeflection response message. * * \param ctrl D channel controller. * \param call Q.931 call leg. * \param invoke_id Value given by the initiating request. * \param code The result to send. * * \retval 0 on success. * \retval -1 on error. */ int pri_rerouting_rsp(struct pri *ctrl, q931_call *call, int invoke_id, enum PRI_REROUTING_RSP_CODE code); /*! * \brief Set the call transfer feature enable flag. * * \param ctrl D channel controller. * \param enable TRUE to enable call transfer feature. * * \return Nothing */ void pri_transfer_enable(struct pri *ctrl, int enable); /*! * \brief Send the call transfer response message. * * \param ctrl D channel controller. * \param call Q.931 call leg. * \param invoke_id Value given by the initiating request. * \param is_successful TRUE if the transfer was successful. * * \retval 0 on success. * \retval -1 on error. */ int pri_transfer_rsp(struct pri *ctrl, q931_call *call, int invoke_id, int is_successful); /*! * \brief Set the advice of charge events feature enable flag. * * \param ctrl D channel controller. * \param enable TRUE to enable AOC events feature. * * \return Nothing */ void pri_aoc_events_enable(struct pri *ctrl, int enable); enum pri_layer2_persistence { PRI_L2_PERSISTENCE_DEFAULT, /*! Immediately bring layer 2 back up if the peer brings layer 2 down. */ PRI_L2_PERSISTENCE_KEEP_UP, /*! Leave layer 2 down if the peer brings layer 2 down. */ PRI_L2_PERSISTENCE_LEAVE_DOWN, #if 0 /* Possible future option. Would need to define how long to idle before dropping. */ /*! Drop layer 2 on D channel idle. */ PRI_L2_PERSISTENCE_IDLE_DROP, #endif }; /*! * \brief Set the layer2 persistence option. * * \param ctrl D channel controller. * \param option Layer 2 persistence to apply. * * \note * Not all values are supported by all modes. * * \return Nothing */ void pri_persistent_layer2_option(struct pri *ctrl, enum pri_layer2_persistence option); #define PRI_DISPLAY_OPTION_BLOCK (1 << 0) /*!< Do not pass display text. */ #define PRI_DISPLAY_OPTION_NAME_INITIAL (1 << 1) /*!< Use display in SETUP/CONNECT for name. */ #define PRI_DISPLAY_OPTION_NAME_UPDATE (1 << 2) /*!< Use display in FACILITY/NOTIFY for COLP name if appropriate. */ #define PRI_DISPLAY_OPTION_TEXT (1 << 3) /*!< Pass arbitrary display text in INFORMATION messages during call. */ /*! * \brief Set the display ie send policy options. * * \param ctrl D channel controller. * \param flags Option flags to apply. * * \note * If no flags set then legacy default behaviour. * * \note * Not all options are supported by all switches. * * \return Nothing */ void pri_display_options_send(struct pri *ctrl, unsigned long flags); /*! * \brief Set the display ie receive policy options. * * \param ctrl D channel controller. * \param flags Option flags to apply. * * \note * If no flags set then legacy default behaviour. * * \note * Not all options are supported by all switches. * * \return Nothing */ void pri_display_options_receive(struct pri *ctrl, unsigned long flags); /*! * \brief Send display text during a call. * * \param ctrl D channel controller. * \param call Q.931 call leg * \param display Display text to send. * * \retval 0 on success. * \retval -1 on error. */ int pri_display_text(struct pri *ctrl, q931_call *call, const struct pri_subcmd_display_txt *display); /*! * \brief Set the call hold feature enable flag. * * \param ctrl D channel controller. * \param enable TRUE to enable call hold feature. * * \return Nothing */ void pri_hold_enable(struct pri *ctrl, int enable); /*! * \brief Send the HOLD message. * * \param ctrl D channel controller. * \param call Q.931 call leg * * \retval 0 on success. * \retval -1 on error. */ int pri_hold(struct pri *ctrl, q931_call *call); /*! * \brief Send the HOLD ACKNOWLEDGE message. * * \param ctrl D channel controller. * \param call Q.931 call leg * * \retval 0 on success. * \retval -1 on error. */ int pri_hold_ack(struct pri *ctrl, q931_call *call); /*! * \brief Send the HOLD REJECT message. * * \param ctrl D channel controller. * \param call Q.931 call leg * \param cause Q.931 cause code for rejecting the hold request. * * \retval 0 on success. * \retval -1 on error. */ int pri_hold_rej(struct pri *ctrl, q931_call *call, int cause); /*! * \brief Send the RETRIEVE message. * * \param ctrl D channel controller. * \param call Q.931 call leg * \param channel Encoded channel id to use. If zero do not send channel id. * * \retval 0 on success. * \retval -1 on error. */ int pri_retrieve(struct pri *ctrl, q931_call *call, int channel); /*! * \brief Send the RETRIEVE ACKNOWLEDGE message. * * \param ctrl D channel controller. * \param call Q.931 call leg * \param channel Encoded channel id to use. * * \retval 0 on success. * \retval -1 on error. */ int pri_retrieve_ack(struct pri *ctrl, q931_call *call, int channel); /*! * \brief Send the RETRIEVE REJECT message. * * \param ctrl D channel controller. * \param call Q.931 call leg * \param cause Q.931 cause code for rejecting the retrieve request. * * \retval 0 on success. * \retval -1 on error. */ 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 Malicious Call ID feature enable flag. * * \param ctrl D channel controller. * \param enable TRUE to enable MCID feature. * * \return Nothing */ void pri_mcid_enable(struct pri *ctrl, int enable); /*! * \brief Send the MCID request message. * * \param ctrl D channel controller. * \param call Q.931 call leg * * \retval 0 on success. You should wait for a PRI_SUBCMD_MCID_RSP * to continue clearing the call if it was in progress. * \retval -1 on error. */ int pri_mcid_req_send(struct pri *ctrl, q931_call *call); /*! * \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); /* Date/time ie send policy option values. */ #define PRI_DATE_TIME_SEND_DEFAULT 0 /*!< Send date/time ie default. */ #define PRI_DATE_TIME_SEND_NO 1 /*!< Send date/time ie never. */ #define PRI_DATE_TIME_SEND_DATE 2 /*!< Send date/time ie date only. */ #define PRI_DATE_TIME_SEND_DATE_HH 3 /*!< Send date/time ie date and hour. */ #define PRI_DATE_TIME_SEND_DATE_HHMM 4 /*!< Send date/time ie date, hour, and minute. */ #define PRI_DATE_TIME_SEND_DATE_HHMMSS 5 /*!< Send date/time ie date, hour, minute, and second. */ /*! * \brief Set the date/time ie send policy option. * * \param ctrl D channel controller. * \param option Policy option to set. * * \note * Only valid in NT mode. * * \return Nothing */ void pri_date_time_send_option(struct pri *ctrl, int option); /* Get/Set PRI Timers */ #define PRI_GETSET_TIMERS int pri_set_timer(struct pri *pri, int timer, int value); int pri_get_timer(struct pri *pri, int timer); int pri_timer2idx(const char *timer_name); /*! New configurable timers and counters must be added to the end of the list */ enum PRI_TIMERS_AND_COUNTERS { PRI_TIMER_N200, /*!< Maximum numer of Q.921 retransmissions */ PRI_TIMER_N201, /*!< Maximum numer of octets in an information field */ PRI_TIMER_N202, /*!< Maximum numer of transmissions of the TEI identity request message */ PRI_TIMER_K, /*!< Maximum number of outstanding I-frames */ PRI_TIMER_T200, /*!< Time between SABME's */ PRI_TIMER_T201, /*!< Minimum time between retransmissions of the TEI Identity check messages */ PRI_TIMER_T202, /*!< Minimum time between transmission of TEI Identity request messages */ PRI_TIMER_T203, /*!< Maximum time without exchanging packets */ PRI_TIMER_T300, PRI_TIMER_T301, /*!< Maximum time to respond to an ALERT */ PRI_TIMER_T302, PRI_TIMER_T303, /*!< Maximum time to wait after sending a SETUP without a response */ PRI_TIMER_T304, PRI_TIMER_T305, /*!< Wait for DISCONNECT acknowledge */ PRI_TIMER_T306, PRI_TIMER_T307, PRI_TIMER_T308, /*!< Wait for RELEASE acknowledge */ PRI_TIMER_T309, /*!< Time active calls can tollerate data link layer being down before clearing. */ PRI_TIMER_T310, /*!< Maximum time between receiving a CALL_PROCEEDING and receiving a ALERT/CONNECT/DISCONNECT/PROGRESS */ PRI_TIMER_T313, /*!< Wait for CONNECT acknowledge, CPE side only */ PRI_TIMER_T314, PRI_TIMER_T316, /*!< Time to wait for a RESTART ACK before retransmitting RESTART. (Timer enabled if greater than zero.) */ PRI_TIMER_T317, PRI_TIMER_T318, PRI_TIMER_T319, PRI_TIMER_T320, PRI_TIMER_T321, PRI_TIMER_T322, PRI_TIMER_TM20, /*!< Maximum time awaiting XID response */ PRI_TIMER_NM20, /*!< Number of XID retransmits */ PRI_TIMER_T_HOLD, /*!< Maximum time to wait for HOLD request response. */ PRI_TIMER_T_RETRIEVE, /*!< Maximum time to wait for RETRIEVE request response. */ 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. */ PRI_TIMER_T312, /*!< Supervise broadcast SETUP message call reference retention. */ PRI_TIMER_N316, /*!< Number of times to transmit RESTART before giving up if T316 enabled. */ /* Must be last in the enum list */ PRI_MAX_TIMERS }; /* Get PRI version */ const char *pri_get_version(void); #endif libpri-1.4.14/rose_etsi_aoc.c0000644000000000000000000017762311400002572014551 0ustar rootroot/* * 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 Advice Of Charge (AOC) operations * * Advice of Charge (AOC) supplementary service EN 300 182-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 Time 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 time Time 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_AOC_Time(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseEtsiAOCTime *time) { unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, time->length)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, time->scale)); ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \internal * \brief Encode the Amount 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 amount Amount 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_AOC_Amount(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseEtsiAOCAmount *amount) { unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, amount->currency)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, amount->multiplier)); ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \internal * \brief Encode the RecordedCurrency 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 recorded Recorded currency 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_AOC_RecordedCurrency(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseEtsiAOCRecordedCurrency *recorded) { unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); ASN1_CALL(pos, asn1_enc_string_max(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, recorded->currency, sizeof(recorded->currency) - 1)); ASN1_CALL(pos, rose_enc_etsi_AOC_Amount(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, &recorded->amount)); ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \internal * \brief Encode the DurationCurrency 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 duration Duration currency 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_AOC_DurationCurrency(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseEtsiAOCDurationCurrency *duration) { unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); ASN1_CALL(pos, asn1_enc_string_max(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, duration->currency, sizeof(duration->currency) - 1)); ASN1_CALL(pos, rose_enc_etsi_AOC_Amount(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, &duration->amount)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3, duration->charging_type)); ASN1_CALL(pos, rose_enc_etsi_AOC_Time(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 4, &duration->time)); if (duration->granularity_present) { ASN1_CALL(pos, rose_enc_etsi_AOC_Time(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 5, &duration->granularity)); } ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \internal * \brief Encode the FlatRateCurrency 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 flat_rate Flat rate currency 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_AOC_FlatRateCurrency(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseEtsiAOCFlatRateCurrency *flat_rate) { unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); ASN1_CALL(pos, asn1_enc_string_max(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, flat_rate->currency, sizeof(flat_rate->currency) - 1)); ASN1_CALL(pos, rose_enc_etsi_AOC_Amount(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, &flat_rate->amount)); ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \internal * \brief Encode the VolumeRateCurrency 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 volume_rate Volume rate currency 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_AOC_VolumeRateCurrency(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseEtsiAOCVolumeRateCurrency *volume_rate) { unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); ASN1_CALL(pos, asn1_enc_string_max(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, volume_rate->currency, sizeof(volume_rate->currency) - 1)); ASN1_CALL(pos, rose_enc_etsi_AOC_Amount(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, &volume_rate->amount)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3, volume_rate->unit)); ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \internal * \brief Encode the AOCSCurrencyInfo 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 currency_info Currency 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_AOCSCurrencyInfo(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseEtsiAOCSCurrencyInfo *currency_info) { unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, currency_info->charged_item)); switch (currency_info->currency_type) { case 0: /* specialChargingCode */ ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, currency_info->u.special_charging_code)); break; case 1: /* durationCurrency */ ASN1_CALL(pos, rose_enc_etsi_AOC_DurationCurrency(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, ¤cy_info->u.duration)); break; case 2: /* flatRateCurrency */ ASN1_CALL(pos, rose_enc_etsi_AOC_FlatRateCurrency(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, ¤cy_info->u.flat_rate)); break; case 3: /* volumeRateCurrency */ ASN1_CALL(pos, rose_enc_etsi_AOC_VolumeRateCurrency(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3, ¤cy_info->u.volume_rate)); break; case 4: /* freeOfCharge */ ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 4)); break; case 5: /* currencyInfoNotAvailable */ ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 5)); break; default: ASN1_ENC_ERROR(ctrl, "Unknown currency type"); return NULL; } ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \internal * \brief Encode the AOCSCurrencyInfoList 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 currency_info Currency information list to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *rose_enc_etsi_AOCSCurrencyInfoList(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseEtsiAOCSCurrencyInfoList *currency_info) { unsigned index; unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); for (index = 0; index < currency_info->num_records; ++index) { ASN1_CALL(pos, rose_enc_etsi_AOCSCurrencyInfo(ctrl, pos, end, ASN1_TAG_SEQUENCE, ¤cy_info->list[index])); } ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \internal * \brief Encode the RecordedUnits 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 recorded Recorded units 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_AOC_RecordedUnits(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseEtsiAOCRecordedUnits *recorded) { unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); if (recorded->not_available) { ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_TYPE_NULL)); } else { ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, recorded->number_of_units)); } if (recorded->type_of_unit_present) { ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, recorded->type_of_unit)); } ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \internal * \brief Encode the RecordedUnitsList 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 recorded_info Recorded units information list to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *rose_enc_etsi_AOC_RecordedUnitsList(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseEtsiAOCRecordedUnitsList *recorded_info) { unsigned index; unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); for (index = 0; index < recorded_info->num_records; ++index) { ASN1_CALL(pos, rose_enc_etsi_AOC_RecordedUnits(ctrl, pos, end, ASN1_TAG_SEQUENCE, &recorded_info->list[index])); } ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \internal * \brief Encode the ChargingAssociation 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 charging Charging association 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_AOC_ChargingAssociation(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct roseEtsiAOCChargingAssociation *charging) { unsigned char *explicit_len; switch (charging->type) { case 0: /* charge_identifier */ ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, charging->id)); break; case 1: /* charged_number */ /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0); ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &charging->number)); ASN1_CONSTRUCTED_END(explicit_len, pos, end); break; default: ASN1_ENC_ERROR(ctrl, "Unknown ChargingAssociation type"); return NULL; } return pos; } /*! * \internal * \brief Encode the AOCECurrencyInfo 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 currency_info Currency 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_AOCECurrencyInfo(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseEtsiAOCECurrencyInfo *currency_info) { unsigned char *seq_len; unsigned char *specific_seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); if (currency_info->free_of_charge) { ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1)); } else { ASN1_CONSTRUCTED_BEGIN(specific_seq_len, pos, end, ASN1_TAG_SEQUENCE); ASN1_CALL(pos, rose_enc_etsi_AOC_RecordedCurrency(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, ¤cy_info->specific.recorded)); if (currency_info->specific.billing_id_present) { ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, currency_info->specific.billing_id)); } ASN1_CONSTRUCTED_END(specific_seq_len, pos, end); } if (currency_info->charging_association_present) { ASN1_CALL(pos, rose_enc_etsi_AOC_ChargingAssociation(ctrl, pos, end, ¤cy_info->charging_association)); } ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \internal * \brief Encode the AOCEChargingUnitInfo 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 charging_unit Charging unit 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_AOCEChargingUnitInfo(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseEtsiAOCEChargingUnitInfo *charging_unit) { unsigned char *seq_len; unsigned char *specific_seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); if (charging_unit->free_of_charge) { ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1)); } else { ASN1_CONSTRUCTED_BEGIN(specific_seq_len, pos, end, ASN1_TAG_SEQUENCE); ASN1_CALL(pos, rose_enc_etsi_AOC_RecordedUnitsList(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, &charging_unit->specific.recorded)); if (charging_unit->specific.billing_id_present) { ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, charging_unit->specific.billing_id)); } ASN1_CONSTRUCTED_END(specific_seq_len, pos, end); } if (charging_unit->charging_association_present) { ASN1_CALL(pos, rose_enc_etsi_AOC_ChargingAssociation(ctrl, pos, end, &charging_unit->charging_association)); } ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the ChargingRequest 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_ChargingRequest_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_ENUMERATED, args->etsi.ChargingRequest.charging_case); } /*! * \brief Encode the ChargingRequest 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_ChargingRequest_RES(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args) { switch (args->etsi.ChargingRequest.type) { case 0: /* currency_info_list */ ASN1_CALL(pos, rose_enc_etsi_AOCSCurrencyInfoList(ctrl, pos, end, ASN1_TAG_SEQUENCE, &args->etsi.ChargingRequest.u.currency_info)); break; case 1: /* special_arrangement_info */ ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, args->etsi.ChargingRequest.u.special_arrangement)); break; case 2: /* charging_info_follows */ ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_TYPE_NULL)); break; default: ASN1_ENC_ERROR(ctrl, "Unknown ChargingRequst type"); return NULL; } return pos; } /*! * \brief Encode the AOCSCurrency 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_AOCSCurrency_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { switch (args->etsi.AOCSCurrency.type) { case 0: /* charge_not_available */ ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_TYPE_NULL)); break; case 1: /* currency_info_list */ if (args->etsi.AOCSCurrency.currency_info.num_records) { ASN1_CALL(pos, rose_enc_etsi_AOCSCurrencyInfoList(ctrl, pos, end, ASN1_TAG_SEQUENCE, &args->etsi.AOCSCurrency.currency_info)); } else { /* There were no records so encode as charge_not_available */ ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_TYPE_NULL)); } break; default: ASN1_ENC_ERROR(ctrl, "Unknown AOCSCurrency type"); return NULL; } return pos; } /*! * \brief Encode the AOCSSpecialArr 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_AOCSSpecialArr_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { switch (args->etsi.AOCSSpecialArr.type) { case 0: /* charge_not_available */ ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_TYPE_NULL)); break; case 1: /* special_arrangement_info */ ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, args->etsi.AOCSSpecialArr.special_arrangement)); break; default: ASN1_ENC_ERROR(ctrl, "Unknown AOCSSpecialArr type"); return NULL; } return pos; } /*! * \brief Encode the AOCDCurrency 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_AOCDCurrency_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { const struct roseEtsiAOCDCurrency_ARG *aoc_d; unsigned char *seq_len; aoc_d = &args->etsi.AOCDCurrency; switch (aoc_d->type) { case 0: /* charge_not_available */ ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_TYPE_NULL)); break; case 1: /* free_of_charge */ ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1)); break; case 2: /* specific_currency */ ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); ASN1_CALL(pos, rose_enc_etsi_AOC_RecordedCurrency(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, &aoc_d->specific.recorded)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, aoc_d->specific.type_of_charging_info)); if (aoc_d->specific.billing_id_present) { ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3, aoc_d->specific.billing_id)); } ASN1_CONSTRUCTED_END(seq_len, pos, end); break; default: ASN1_ENC_ERROR(ctrl, "Unknown AOCDCurrency type"); return NULL; } return pos; } /*! * \brief Encode the AOCDChargingUnit 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_AOCDChargingUnit_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { const struct roseEtsiAOCDChargingUnit_ARG *aoc_d; unsigned char *seq_len; aoc_d = &args->etsi.AOCDChargingUnit; switch (aoc_d->type) { case 0: /* charge_not_available */ ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_TYPE_NULL)); break; case 1: /* free_of_charge */ ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1)); break; case 2: /* specific_charging_units */ ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); ASN1_CALL(pos, rose_enc_etsi_AOC_RecordedUnitsList(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, &aoc_d->specific.recorded)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, aoc_d->specific.type_of_charging_info)); if (aoc_d->specific.billing_id_present) { ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3, aoc_d->specific.billing_id)); } ASN1_CONSTRUCTED_END(seq_len, pos, end); break; default: ASN1_ENC_ERROR(ctrl, "Unknown AOCDChargingUnit type"); return NULL; } return pos; } /*! * \brief Encode the AOCECurrency 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_AOCECurrency_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { switch (args->etsi.AOCECurrency.type) { case 0: /* charge_not_available */ ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_TYPE_NULL)); break; case 1: /* currency_info */ ASN1_CALL(pos, rose_enc_etsi_AOCECurrencyInfo(ctrl, pos, end, ASN1_TAG_SEQUENCE, &args->etsi.AOCECurrency.currency_info)); break; default: ASN1_ENC_ERROR(ctrl, "Unknown AOCECurrency type"); return NULL; } return pos; } /*! * \brief Encode the AOCEChargingUnit 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_AOCEChargingUnit_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { switch (args->etsi.AOCEChargingUnit.type) { case 0: /* charge_not_available */ ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_TYPE_NULL)); break; case 1: /* charging_unit */ ASN1_CALL(pos, rose_enc_etsi_AOCEChargingUnitInfo(ctrl, pos, end, ASN1_TAG_SEQUENCE, &args->etsi.AOCEChargingUnit.charging_unit)); break; default: ASN1_ENC_ERROR(ctrl, "Unknown AOCEChargingUnit type"); return NULL; } return pos; } /*! * \internal * \brief Decode the Time type. * * \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 time 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_AOC_Time(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseEtsiAOCTime *time) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s Time %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_CLASS_CONTEXT_SPECIFIC | 1); ASN1_CALL(pos, asn1_dec_int(ctrl, "lengthOfTimeUnit", tag, pos, seq_end, &value)); time->length = value; ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 2); ASN1_CALL(pos, asn1_dec_int(ctrl, "scale", tag, pos, seq_end, &value)); time->scale = value; ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \internal * \brief Decode the Amount type. * * \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 amount 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_AOC_Amount(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseEtsiAOCAmount *amount) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s Amount %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_CLASS_CONTEXT_SPECIFIC | 1); ASN1_CALL(pos, asn1_dec_int(ctrl, "currencyAmount", tag, pos, seq_end, &value)); amount->currency = value; ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 2); ASN1_CALL(pos, asn1_dec_int(ctrl, "multiplier", tag, pos, seq_end, &value)); amount->multiplier = value; ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \internal * \brief Decode the RecordedCurrency type. * * \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 recorded 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_AOC_RecordedCurrency(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseEtsiAOCRecordedCurrency *recorded) { int length; int seq_offset; const unsigned char *seq_end; size_t str_len; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s RecordedCurrency %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 & ~ASN1_PC_MASK, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 1); ASN1_CALL(pos, asn1_dec_string_max(ctrl, "rCurrency", tag, pos, seq_end, sizeof(recorded->currency), recorded->currency, &str_len)); ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2); ASN1_CALL(pos, rose_dec_etsi_AOC_Amount(ctrl, "rAmount", tag, pos, seq_end, &recorded->amount)); ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \internal * \brief Decode the DurationCurrency type. * * \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 duration 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_AOC_DurationCurrency(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseEtsiAOCDurationCurrency *duration) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; size_t str_len; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s DurationCurrency %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 & ~ASN1_PC_MASK, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 1); ASN1_CALL(pos, asn1_dec_string_max(ctrl, "dCurrency", tag, pos, seq_end, sizeof(duration->currency), duration->currency, &str_len)); ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2); ASN1_CALL(pos, rose_dec_etsi_AOC_Amount(ctrl, "dAmount", tag, pos, seq_end, &duration->amount)); ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 3); ASN1_CALL(pos, asn1_dec_int(ctrl, "dChargingType", tag, pos, seq_end, &value)); duration->charging_type = value; ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 4); ASN1_CALL(pos, rose_dec_etsi_AOC_Time(ctrl, "dTime", tag, pos, seq_end, &duration->time)); 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_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 5); ASN1_CALL(pos, rose_dec_etsi_AOC_Time(ctrl, "dGranularity", tag, pos, seq_end, &duration->granularity)); duration->granularity_present = 1; } else { duration->granularity_present = 0; } ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \internal * \brief Decode the FlatRateCurrency type. * * \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 flat_rate 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_AOC_FlatRateCurrency(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseEtsiAOCFlatRateCurrency *flat_rate) { int length; int seq_offset; const unsigned char *seq_end; size_t str_len; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s FlatRateCurrency %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 & ~ASN1_PC_MASK, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 1); ASN1_CALL(pos, asn1_dec_string_max(ctrl, "fRCurrency", tag, pos, seq_end, sizeof(flat_rate->currency), flat_rate->currency, &str_len)); ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2); ASN1_CALL(pos, rose_dec_etsi_AOC_Amount(ctrl, "fRAmount", tag, pos, seq_end, &flat_rate->amount)); ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \internal * \brief Decode the VolumeRateCurrency type. * * \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 volume_rate 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_AOC_VolumeRateCurrency(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseEtsiAOCVolumeRateCurrency *volume_rate) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; size_t str_len; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s VolumeRateCurrency %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 & ~ASN1_PC_MASK, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 1); ASN1_CALL(pos, asn1_dec_string_max(ctrl, "vRCurrency", tag, pos, seq_end, sizeof(volume_rate->currency), volume_rate->currency, &str_len)); ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2); ASN1_CALL(pos, rose_dec_etsi_AOC_Amount(ctrl, "vRAmount", tag, pos, seq_end, &volume_rate->amount)); ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 3); ASN1_CALL(pos, asn1_dec_int(ctrl, "vRVolumeUnit", tag, pos, seq_end, &value)); volume_rate->unit = value; ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \internal * \brief Decode the AOCSCurrencyInfo type. * * \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 currency_info 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_AOCSCurrencyInfo(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseEtsiAOCSCurrencyInfo *currency_info) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s AOCSCurrencyInfo %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, "chargedItem", tag, pos, seq_end, &value)); currency_info->charged_item = value; ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); switch (tag) { case ASN1_TYPE_INTEGER: currency_info->currency_type = 0; /* specialChargingCode */ ASN1_CALL(pos, asn1_dec_int(ctrl, "specialChargingCode", tag, pos, seq_end, &value)); currency_info->u.special_charging_code = value; break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1: currency_info->currency_type = 1; /* durationCurrency */ ASN1_CALL(pos, rose_dec_etsi_AOC_DurationCurrency(ctrl, "durationCurrency", tag, pos, seq_end, ¤cy_info->u.duration)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2: currency_info->currency_type = 2; /* flatRateCurrency */ ASN1_CALL(pos, rose_dec_etsi_AOC_FlatRateCurrency(ctrl, "flatRateCurrency", tag, pos, seq_end, ¤cy_info->u.flat_rate)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 3: currency_info->currency_type = 3; /* volumeRateCurrency */ ASN1_CALL(pos, rose_dec_etsi_AOC_VolumeRateCurrency(ctrl, "volumeRateCurrency", tag, pos, seq_end, ¤cy_info->u.volume_rate)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 4: currency_info->currency_type = 4; /* freeOfCharge */ ASN1_CALL(pos, asn1_dec_null(ctrl, "freeOfCharge", tag, pos, seq_end)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 5: currency_info->currency_type = 5; /* currencyInfoNotAvailable */ ASN1_CALL(pos, asn1_dec_null(ctrl, "currencyInfoNotAvailable", tag, pos, seq_end)); break; default: ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); return NULL; } ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \internal * \brief Decode the AOCSCurrencyInfoList type. * * \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 currency_info 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_AOCSCurrencyInfoList(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseEtsiAOCSCurrencyInfoList *currency_info) { int length; int seq_offset; const unsigned char *seq_end; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s AOCSCurrencyInfoList %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); currency_info->num_records = 0; while (pos < seq_end && *pos != ASN1_INDEF_TERM) { if (currency_info->num_records < ARRAY_LEN(currency_info->list)) { 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_AOCSCurrencyInfo(ctrl, "listEntry", tag, pos, seq_end, ¤cy_info->list[currency_info->num_records])); ++currency_info->num_records; } else { /* Too many records */ return NULL; } } ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \internal * \brief Decode the RecordedUnits type. * * \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 recorded 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_AOC_RecordedUnits(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseEtsiAOCRecordedUnits *recorded) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s RecordedUnits %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)); switch (tag) { case ASN1_TYPE_INTEGER: recorded->not_available = 0; ASN1_CALL(pos, asn1_dec_int(ctrl, "recordedNumberOfUnits", tag, pos, seq_end, &value)); recorded->number_of_units = value; break; case ASN1_TYPE_NULL: recorded->not_available = 1; recorded->number_of_units = 0; ASN1_CALL(pos, asn1_dec_null(ctrl, "notAvailable", tag, pos, seq_end)); break; default: ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); return NULL; } 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_TYPE_INTEGER); ASN1_CALL(pos, asn1_dec_int(ctrl, "recordedTypeOfUnits", tag, pos, seq_end, &value)); recorded->type_of_unit = value; recorded->type_of_unit_present = 1; } else { recorded->type_of_unit_present = 0; } ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \internal * \brief Decode the RecordedUnitsList type. * * \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 recorded_info 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_AOC_RecordedUnitsList(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseEtsiAOCRecordedUnitsList *recorded_info) { int length; int seq_offset; const unsigned char *seq_end; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s RecordedUnitsList %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); recorded_info->num_records = 0; while (pos < seq_end && *pos != ASN1_INDEF_TERM) { if (recorded_info->num_records < ARRAY_LEN(recorded_info->list)) { 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_AOC_RecordedUnits(ctrl, "listEntry", tag, pos, seq_end, &recorded_info->list[recorded_info->num_records])); ++recorded_info->num_records; } else { /* Too many records */ return NULL; } } ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \internal * \brief Decode the ChargingAssociation type. * * \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 charging 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_AOC_ChargingAssociation(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseEtsiAOCChargingAssociation *charging) { int32_t value; int length; int explicit_offset; const unsigned char *explicit_end; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s ChargingAssociation\n", name); } switch (tag) { case ASN1_TYPE_INTEGER: charging->type = 0; /* charge_identifier */ ASN1_CALL(pos, asn1_dec_int(ctrl, "chargeIdentifier", tag, pos, end, &value)); charging->id = value; break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 0: charging->type = 1; /* charged_number */ /* 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, end, &length)); ASN1_END_SETUP(explicit_end, explicit_offset, length, pos, end); ASN1_CALL(pos, asn1_dec_tag(pos, explicit_end, &tag)); ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "chargedNumber", tag, pos, explicit_end, &charging->number)); ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, end); break; default: ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); return NULL; } return pos; } /*! * \internal * \brief Decode the AOCECurrencyInfo type. * * \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 currency_info 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_AOCECurrencyInfo(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseEtsiAOCECurrencyInfo *currency_info) { int32_t value; int length; int seq_offset; int specific_offset; const unsigned char *seq_end; const unsigned char *specific_end; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s AOCECurrencyInfo %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)); switch (tag) { case ASN1_CLASS_CONTEXT_SPECIFIC | 1: currency_info->free_of_charge = 1; ASN1_CALL(pos, asn1_dec_null(ctrl, "freeOfCharge", tag, pos, seq_end)); break; case ASN1_TAG_SEQUENCE: currency_info->free_of_charge = 0; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " specificCurrency %s\n", asn1_tag2str(tag)); } ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); ASN1_END_SETUP(specific_end, specific_offset, length, pos, seq_end); ASN1_CALL(pos, asn1_dec_tag(pos, specific_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1); ASN1_CALL(pos, rose_dec_etsi_AOC_RecordedCurrency(ctrl, "recordedCurrency", tag, pos, specific_end, ¤cy_info->specific.recorded)); if (pos < specific_end && *pos != ASN1_INDEF_TERM) { ASN1_CALL(pos, asn1_dec_tag(pos, specific_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 2); ASN1_CALL(pos, asn1_dec_int(ctrl, "billingId", tag, pos, specific_end, &value)); currency_info->specific.billing_id = value; currency_info->specific.billing_id_present = 1; } else { currency_info->specific.billing_id_present = 0; } ASN1_END_FIXUP(ctrl, pos, specific_offset, specific_end, seq_end); break; default: ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); return NULL; } if (pos < seq_end && *pos != ASN1_INDEF_TERM) { ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CALL(pos, rose_dec_etsi_AOC_ChargingAssociation(ctrl, "chargingAssociation", tag, pos, seq_end, ¤cy_info->charging_association)); currency_info->charging_association_present = 1; } else { currency_info->charging_association_present = 0; } ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \internal * \brief Decode the AOCEChargingUnitInfo type. * * \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 charging_unit 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_AOCEChargingUnitInfo(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseEtsiAOCEChargingUnitInfo *charging_unit) { int32_t value; int length; int seq_offset; int specific_offset; const unsigned char *seq_end; const unsigned char *specific_end; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s AOCEChargingUnitInfo %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)); switch (tag) { case ASN1_CLASS_CONTEXT_SPECIFIC | 1: charging_unit->free_of_charge = 1; ASN1_CALL(pos, asn1_dec_null(ctrl, "freeOfCharge", tag, pos, seq_end)); break; case ASN1_TAG_SEQUENCE: charging_unit->free_of_charge = 0; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " specificChargingUnits %s\n", asn1_tag2str(tag)); } ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); ASN1_END_SETUP(specific_end, specific_offset, length, pos, seq_end); ASN1_CALL(pos, asn1_dec_tag(pos, specific_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1); ASN1_CALL(pos, rose_dec_etsi_AOC_RecordedUnitsList(ctrl, "recordedUnitsList", tag, pos, specific_end, &charging_unit->specific.recorded)); if (pos < specific_end && *pos != ASN1_INDEF_TERM) { ASN1_CALL(pos, asn1_dec_tag(pos, specific_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 2); ASN1_CALL(pos, asn1_dec_int(ctrl, "billingId", tag, pos, specific_end, &value)); charging_unit->specific.billing_id = value; charging_unit->specific.billing_id_present = 1; } else { charging_unit->specific.billing_id_present = 0; } ASN1_END_FIXUP(ctrl, pos, specific_offset, specific_end, seq_end); break; default: ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); return NULL; } if (pos < seq_end && *pos != ASN1_INDEF_TERM) { ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CALL(pos, rose_dec_etsi_AOC_ChargingAssociation(ctrl, "chargingAssociation", tag, pos, seq_end, &charging_unit->charging_association)); charging_unit->charging_association_present = 1; } else { charging_unit->charging_association_present = 0; } ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the ChargingRequest 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_ChargingRequest_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_ENUMERATED); ASN1_CALL(pos, asn1_dec_int(ctrl, "chargingCase", tag, pos, end, &value)); args->etsi.ChargingRequest.charging_case = value; return pos; } /*! * \brief Decode the ChargingRequest result 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_ChargingRequest_RES(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args) { struct roseEtsiChargingRequest_RES *charging_request; int32_t value; charging_request = &args->etsi.ChargingRequest; switch (tag) { case ASN1_TAG_SEQUENCE: charging_request->type = 0; /* currency_info_list */ ASN1_CALL(pos, rose_dec_etsi_AOCSCurrencyInfoList(ctrl, "currencyList", tag, pos, end, &charging_request->u.currency_info)); break; case ASN1_TYPE_INTEGER: charging_request->type = 1; /* special_arrangement_info */ ASN1_CALL(pos, asn1_dec_int(ctrl, "specialArrangement", tag, pos, end, &value)); charging_request->u.special_arrangement = value; break; case ASN1_TYPE_NULL: charging_request->type = 2; /* charging_info_follows */ ASN1_CALL(pos, asn1_dec_null(ctrl, "chargingInfoFollows", tag, pos, end)); break; default: ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); return NULL; } return pos; } /*! * \brief Decode the AOCSCurrency 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_AOCSCurrency_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { struct roseEtsiAOCSCurrency_ARG *aoc_s; aoc_s = &args->etsi.AOCSCurrency; switch (tag) { case ASN1_TYPE_NULL: aoc_s->type = 0; /* charge_not_available */ ASN1_CALL(pos, asn1_dec_null(ctrl, "chargeNotAvailable", tag, pos, end)); break; case ASN1_TAG_SEQUENCE: aoc_s->type = 1; /* currency_info_list */ ASN1_CALL(pos, rose_dec_etsi_AOCSCurrencyInfoList(ctrl, "currencyInfo", tag, pos, end, &aoc_s->currency_info)); break; default: ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); return NULL; } return pos; } /*! * \brief Decode the AOCSSpecialArr 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_AOCSSpecialArr_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { struct roseEtsiAOCSSpecialArr_ARG *aoc_s; int32_t value; aoc_s = &args->etsi.AOCSSpecialArr; switch (tag) { case ASN1_TYPE_NULL: aoc_s->type = 0; /* charge_not_available */ ASN1_CALL(pos, asn1_dec_null(ctrl, "chargeNotAvailable", tag, pos, end)); break; case ASN1_TYPE_INTEGER: aoc_s->type = 1; /* special_arrangement_info */ ASN1_CALL(pos, asn1_dec_int(ctrl, "specialArrangement", tag, pos, end, &value)); aoc_s->special_arrangement = value; break; default: ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); return NULL; } return pos; } /*! * \brief Decode the AOCDCurrency 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_AOCDCurrency_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { struct roseEtsiAOCDCurrency_ARG *aoc_d; int length; int seq_offset; const unsigned char *seq_end; int32_t value; aoc_d = &args->etsi.AOCDCurrency; switch (tag) { case ASN1_TYPE_NULL: aoc_d->type = 0; /* charge_not_available */ ASN1_CALL(pos, asn1_dec_null(ctrl, "chargeNotAvailable", tag, pos, end)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 1: aoc_d->type = 1; /* free_of_charge */ ASN1_CALL(pos, asn1_dec_null(ctrl, "freeOfCharge", tag, pos, end)); break; case ASN1_TAG_SEQUENCE: aoc_d->type = 2; /* specific_currency */ if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " specificCurrency %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_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1); ASN1_CALL(pos, rose_dec_etsi_AOC_RecordedCurrency(ctrl, "recordedCurrency", tag, pos, seq_end, &aoc_d->specific.recorded)); ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 2); ASN1_CALL(pos, asn1_dec_int(ctrl, "typeOfChargingInfo", tag, pos, seq_end, &value)); aoc_d->specific.type_of_charging_info = value; 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_CLASS_CONTEXT_SPECIFIC | 3); ASN1_CALL(pos, asn1_dec_int(ctrl, "billingId", tag, pos, seq_end, &value)); aoc_d->specific.billing_id = value; aoc_d->specific.billing_id_present = 1; } else { aoc_d->specific.billing_id_present = 0; } ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); break; default: ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); return NULL; } return pos; } /*! * \brief Decode the AOCDChargingUnit 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_AOCDChargingUnit_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { struct roseEtsiAOCDChargingUnit_ARG *aoc_d; int length; int seq_offset; const unsigned char *seq_end; int32_t value; aoc_d = &args->etsi.AOCDChargingUnit; switch (tag) { case ASN1_TYPE_NULL: aoc_d->type = 0; /* charge_not_available */ ASN1_CALL(pos, asn1_dec_null(ctrl, "chargeNotAvailable", tag, pos, end)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 1: aoc_d->type = 1; /* free_of_charge */ ASN1_CALL(pos, asn1_dec_null(ctrl, "freeOfCharge", tag, pos, end)); break; case ASN1_TAG_SEQUENCE: aoc_d->type = 2; /* specific_charging_units */ if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " specificChargingUnits %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_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1); ASN1_CALL(pos, rose_dec_etsi_AOC_RecordedUnitsList(ctrl, "recordedUnitsList", tag, pos, seq_end, &aoc_d->specific.recorded)); ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 2); ASN1_CALL(pos, asn1_dec_int(ctrl, "typeOfChargingInfo", tag, pos, seq_end, &value)); aoc_d->specific.type_of_charging_info = value; 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_CLASS_CONTEXT_SPECIFIC | 3); ASN1_CALL(pos, asn1_dec_int(ctrl, "billingId", tag, pos, seq_end, &value)); aoc_d->specific.billing_id = value; aoc_d->specific.billing_id_present = 1; } else { aoc_d->specific.billing_id_present = 0; } ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); break; default: ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); return NULL; } return pos; } /*! * \brief Decode the AOCECurrency 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_AOCECurrency_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { struct roseEtsiAOCECurrency_ARG *aoc_e; aoc_e = &args->etsi.AOCECurrency; switch (tag) { case ASN1_TYPE_NULL: aoc_e->type = 0; /* charge_not_available */ ASN1_CALL(pos, asn1_dec_null(ctrl, "chargeNotAvailable", tag, pos, end)); break; case ASN1_TAG_SEQUENCE: aoc_e->type = 1; /* currency_info */ ASN1_CALL(pos, rose_dec_etsi_AOCECurrencyInfo(ctrl, "currencyInfo", tag, pos, end, &aoc_e->currency_info)); break; default: ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); return NULL; } return pos; } /*! * \brief Decode the AOCEChargingUnit 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_AOCEChargingUnit_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { struct roseEtsiAOCEChargingUnit_ARG *aoc_e; aoc_e = &args->etsi.AOCEChargingUnit; switch (tag) { case ASN1_TYPE_NULL: aoc_e->type = 0; /* charge_not_available */ ASN1_CALL(pos, asn1_dec_null(ctrl, "chargeNotAvailable", tag, pos, end)); break; case ASN1_TAG_SEQUENCE: aoc_e->type = 1; /* charging_unit */ ASN1_CALL(pos, rose_dec_etsi_AOCEChargingUnitInfo(ctrl, "chargingUnitInfo", tag, pos, end, &aoc_e->charging_unit)); break; default: ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); return NULL; } return pos; } /* ------------------------------------------------------------------- */ /* end rose_etsi_aoc.c */ libpri-1.4.14/rose.c0000644000000000000000000026777311527303165012725 0ustar rootroot/* * 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 Remote Operations Service Element (ROSE) main controlling functions * * \author Richard Mudgett */ #include #include "compat.h" #include "libpri.h" #include "pri_internal.h" #include "rose.h" #include "rose_internal.h" #include "asn1.h" #include "pri_facility.h" #define ROSE_TAG_COMPONENT_INVOKE (ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1) #define ROSE_TAG_COMPONENT_RESULT (ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2) #define ROSE_TAG_COMPONENT_ERROR (ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 3) #define ROSE_TAG_COMPONENT_REJECT (ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 4) /*! \brief Structure to convert a code value to a string */ struct rose_code_strings { /*! \brief Code value to convert to a string */ int code; /*! \brief String equivalent of the associated code value */ const char *name; }; /*! \brief ROSE invoke/result message conversion table entry. */ struct rose_convert_msg { /*! \brief library encoded operation-value */ enum rose_operation operation; /*! * \brief OID prefix values to use when encoding/decoding the operation-value OID * \note NULL if operation-value is a localValue. */ const struct asn1_oid *oid_prefix; /*! \brief Last OID value or localValue for the encoded operation-value */ u_int16_t value; /*! * \brief Encode the ROSE invoke operation-value 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. * * \note The function pointer is NULL if there are no arguments to encode. */ unsigned char *(*encode_invoke_args)(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); /*! * \brief Encode the ROSE result operation-value 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. * * \note The function pointer is NULL if there are no arguments to encode. */ unsigned char *(*encode_result_args)(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args); /*! * \brief Decode the ROSE invoke operation-value arguments. * * \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. * * \note The function pointer is NULL if there are no arguments to decode. */ const unsigned char *(*decode_invoke_args)(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args); /*! * \brief Decode the ROSE result operation-value arguments. * * \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. * * \note The function pointer is NULL if there are no arguments to decode. */ const unsigned char *(*decode_result_args)(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args); }; /*! \brief ROSE error code conversion table entry. */ struct rose_convert_error { /*! \brief library encoded error-value */ enum rose_error_code code; /*! * \brief OID prefix values to use when encoding/decoding the error-value OID * \note NULL if error-value is a localValue. */ const struct asn1_oid *oid_prefix; /*! \brief Last OID value or localValue for the encoded error-value */ u_int16_t value; /*! * \brief Encode the ROSE error parameters. * * \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. * * \note The function pointer is NULL if there are no arguments to encode. */ unsigned char *(*encode_error_args)(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_error_args *args); /*! * \brief Decode the ROSE error 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. * * \note The function pointer is NULL if there are no arguments to decode. */ const unsigned char *(*decode_error_args)(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_error_args *args); }; /* ------------------------------------------------------------------- */ /* * Note the first value in oid.values[] is really the first two * OID subidentifiers. They are compressed using this formula: * First_Value = (First_Subidentifier * 40) + Second_Subidentifier */ /*! \brief ETSI Explicit Call Transfer OID prefix. */ static const struct asn1_oid rose_etsi_ect = { /* *INDENT-OFF* */ /* {ccitt(0) identified-organization(4) etsi(0) 369 operations-and-errors(1)} */ 4, { 4, 0, 369, 1 } /* *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 Message Waiting Indication OID prefix. */ static const struct asn1_oid rose_etsi_mwi = { /* *INDENT-OFF* */ /* {ccitt(0) identified-organization(4) etsi(0) 745 operations-and-errors(1)} */ 4, { 4, 0, 745, 1 } /* *INDENT-ON* */ }; /*! \brief ETSI specific invoke/result encode/decode message table */ static const struct rose_convert_msg rose_etsi_msgs[] = { /* *INDENT-OFF* */ /* * operation, oid_prefix, value, * encode_invoke_args, encode_result_args, * decode_invoke_args, decode_result_args */ /* * localValue's from Diversion-Operations * {ccitt identified-organization etsi(0) 207 operations-and-errors(1)} */ { ROSE_ETSI_ActivationDiversion, NULL, 7, rose_enc_etsi_ActivationDiversion_ARG, NULL, rose_dec_etsi_ActivationDiversion_ARG, NULL }, { ROSE_ETSI_DeactivationDiversion, NULL, 8, rose_enc_etsi_DeactivationDiversion_ARG,NULL, rose_dec_etsi_DeactivationDiversion_ARG,NULL }, { ROSE_ETSI_ActivationStatusNotificationDiv, NULL, 9, rose_enc_etsi_ActivationStatusNotificationDiv_ARG,NULL, rose_dec_etsi_ActivationStatusNotificationDiv_ARG,NULL }, { ROSE_ETSI_DeactivationStatusNotificationDiv,NULL, 10, rose_enc_etsi_DeactivationStatusNotificationDiv_ARG,NULL, rose_dec_etsi_DeactivationStatusNotificationDiv_ARG,NULL }, { ROSE_ETSI_InterrogationDiversion, NULL, 11, rose_enc_etsi_InterrogationDiversion_ARG,rose_enc_etsi_InterrogationDiversion_RES, rose_dec_etsi_InterrogationDiversion_ARG,rose_dec_etsi_InterrogationDiversion_RES }, { ROSE_ETSI_DiversionInformation, NULL, 12, rose_enc_etsi_DiversionInformation_ARG, NULL, rose_dec_etsi_DiversionInformation_ARG, NULL }, { ROSE_ETSI_CallDeflection, NULL, 13, rose_enc_etsi_CallDeflection_ARG, NULL, rose_dec_etsi_CallDeflection_ARG, NULL }, { ROSE_ETSI_CallRerouting, NULL, 14, rose_enc_etsi_CallRerouting_ARG, NULL, rose_dec_etsi_CallRerouting_ARG, NULL }, { ROSE_ETSI_DivertingLegInformation2, NULL, 15, rose_enc_etsi_DivertingLegInformation2_ARG,NULL, rose_dec_etsi_DivertingLegInformation2_ARG,NULL }, { ROSE_ETSI_InterrogateServedUserNumbers, NULL, 17, NULL, rose_enc_etsi_InterrogateServedUserNumbers_RES, NULL, rose_dec_etsi_InterrogateServedUserNumbers_RES }, { ROSE_ETSI_DivertingLegInformation1, NULL, 18, rose_enc_etsi_DivertingLegInformation1_ARG,NULL, rose_dec_etsi_DivertingLegInformation1_ARG,NULL }, { ROSE_ETSI_DivertingLegInformation3, NULL, 19, rose_enc_etsi_DivertingLegInformation3_ARG,NULL, rose_dec_etsi_DivertingLegInformation3_ARG,NULL }, /* * localValue's from Advice-of-Charge-Operations * {ccitt identified-organization etsi (0) 182 operations-and-errors (1)} * * Advice-Of-Charge-at-call-Setup(AOCS) * Advice-Of-Charge-During-the-call(AOCD) * Advice-Of-Charge-at-the-End-of-the-call(AOCE) */ { ROSE_ETSI_ChargingRequest, NULL, 30, rose_enc_etsi_ChargingRequest_ARG, rose_enc_etsi_ChargingRequest_RES, rose_dec_etsi_ChargingRequest_ARG, rose_dec_etsi_ChargingRequest_RES }, { ROSE_ETSI_AOCSCurrency, NULL, 31, rose_enc_etsi_AOCSCurrency_ARG, NULL, rose_dec_etsi_AOCSCurrency_ARG, NULL }, { ROSE_ETSI_AOCSSpecialArr, NULL, 32, rose_enc_etsi_AOCSSpecialArr_ARG, NULL, rose_dec_etsi_AOCSSpecialArr_ARG, NULL }, { ROSE_ETSI_AOCDCurrency, NULL, 33, rose_enc_etsi_AOCDCurrency_ARG, NULL, rose_dec_etsi_AOCDCurrency_ARG, NULL }, { ROSE_ETSI_AOCDChargingUnit, NULL, 34, rose_enc_etsi_AOCDChargingUnit_ARG, NULL, rose_dec_etsi_AOCDChargingUnit_ARG, NULL }, { ROSE_ETSI_AOCECurrency, NULL, 35, rose_enc_etsi_AOCECurrency_ARG, NULL, rose_dec_etsi_AOCECurrency_ARG, NULL }, { ROSE_ETSI_AOCEChargingUnit, NULL, 36, rose_enc_etsi_AOCEChargingUnit_ARG, NULL, rose_dec_etsi_AOCEChargingUnit_ARG, NULL }, /* * localValue's from Explicit-Call-Transfer-Operations-and-Errors * {ccitt identified-organization etsi(0) 369 operations-and-errors(1)} */ { ROSE_ETSI_EctExecute, NULL, 6, NULL, NULL, NULL, NULL }, /* * globalValue's (OIDs) from Explicit-Call-Transfer-Operations-and-Errors * {ccitt identified-organization etsi(0) 369 operations-and-errors(1)} */ { ROSE_ETSI_ExplicitEctExecute, &rose_etsi_ect, 1, rose_enc_etsi_ExplicitEctExecute_ARG, NULL, rose_dec_etsi_ExplicitEctExecute_ARG, NULL }, { ROSE_ETSI_RequestSubaddress, &rose_etsi_ect, 2, NULL, NULL, NULL, NULL }, { ROSE_ETSI_SubaddressTransfer, &rose_etsi_ect, 3, rose_enc_etsi_SubaddressTransfer_ARG, NULL, rose_dec_etsi_SubaddressTransfer_ARG, NULL }, { ROSE_ETSI_EctLinkIdRequest, &rose_etsi_ect, 4, NULL, rose_enc_etsi_EctLinkIdRequest_RES, NULL, rose_dec_etsi_EctLinkIdRequest_RES }, { ROSE_ETSI_EctInform, &rose_etsi_ect, 5, rose_enc_etsi_EctInform_ARG, NULL, rose_dec_etsi_EctInform_ARG, NULL }, { ROSE_ETSI_EctLoopTest, &rose_etsi_ect, 6, 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 }, /* * localValue's from MCID-Operations * {ccitt identified-organization etsi(0) 130 operations-and-errors(1)} */ { ROSE_ETSI_MCIDRequest, NULL, 3, NULL, NULL, NULL, NULL }, /* * globalValue's (OIDs) from MWI-Operations-and-Errors * {ccitt identified-organization etsi(0) 745 operations-and-errors(1)} */ { ROSE_ETSI_MWIActivate, &rose_etsi_mwi, 1, rose_enc_etsi_MWIActivate_ARG, NULL, rose_dec_etsi_MWIActivate_ARG, NULL }, { ROSE_ETSI_MWIDeactivate, &rose_etsi_mwi, 2, rose_enc_etsi_MWIDeactivate_ARG, NULL, rose_dec_etsi_MWIDeactivate_ARG, NULL }, { ROSE_ETSI_MWIIndicate, &rose_etsi_mwi, 3, rose_enc_etsi_MWIIndicate_ARG, NULL, rose_dec_etsi_MWIIndicate_ARG, NULL }, /* *INDENT-ON* */ }; /*! \brief ETSI specific error-value converion table */ static const struct rose_convert_error rose_etsi_errors[] = { /* *INDENT-OFF* */ /* * error-code, oid_prefix, value * encode_error_args, decode_error_args */ /* * localValue Errors from General-Errors * {ccitt identified-organization etsi(0) 196 general-errors(2)} */ { ROSE_ERROR_Gen_NotSubscribed, NULL, 0, NULL, NULL }, { ROSE_ERROR_Gen_NotAvailable, NULL, 3, NULL, NULL }, { ROSE_ERROR_Gen_NotImplemented, NULL, 4, NULL, NULL }, { ROSE_ERROR_Gen_InvalidServedUserNr, NULL, 6, NULL, NULL }, { ROSE_ERROR_Gen_InvalidCallState, NULL, 7, NULL, NULL }, { ROSE_ERROR_Gen_BasicServiceNotProvided, NULL, 8, NULL, NULL }, { ROSE_ERROR_Gen_NotIncomingCall, NULL, 9, NULL, NULL }, { ROSE_ERROR_Gen_SupplementaryServiceInteractionNotAllowed,NULL, 10, NULL, NULL }, { ROSE_ERROR_Gen_ResourceUnavailable, NULL, 11, NULL, NULL }, /* * localValue Errors from Diversion-Operations * {ccitt identified-organization etsi(0) 207 operations-and-errors(1)} */ { ROSE_ERROR_Div_InvalidDivertedToNr, NULL, 12, NULL, NULL }, { ROSE_ERROR_Div_SpecialServiceNr, NULL, 14, NULL, NULL }, { ROSE_ERROR_Div_DiversionToServedUserNr, NULL, 15, NULL, NULL }, { ROSE_ERROR_Div_IncomingCallAccepted, NULL, 23, NULL, NULL }, { ROSE_ERROR_Div_NumberOfDiversionsExceeded, NULL, 24, NULL, NULL }, { ROSE_ERROR_Div_NotActivated, NULL, 46, NULL, NULL }, { ROSE_ERROR_Div_RequestAlreadyAccepted, NULL, 48, NULL, NULL }, /* * localValue Errors from Advice-of-Charge-Operations * {ccitt identified-organization etsi (0) 182 operations-and-errors (1)} */ { ROSE_ERROR_AOC_NoChargingInfoAvailable, NULL, 26, NULL, NULL }, /* * globalValue Errors (OIDs) from Explicit-Call-Transfer-Operations-and-Errors * {ccitt identified-organization etsi(0) 369 operations-and-errors(1)} */ { 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 }, /* * globalValue's (OIDs) from MWI-Operations-and-Errors * {ccitt identified-organization etsi(0) 745 operations-and-errors(1)} */ { ROSE_ERROR_MWI_InvalidReceivingUserNr, &rose_etsi_mwi, 10, NULL, NULL }, { ROSE_ERROR_MWI_ReceivingUserNotSubscribed, &rose_etsi_mwi, 11, NULL, NULL }, { ROSE_ERROR_MWI_ControllingUserNotRegistered,&rose_etsi_mwi, 12, NULL, NULL }, { ROSE_ERROR_MWI_IndicationNotDelivered, &rose_etsi_mwi, 13, NULL, NULL }, { ROSE_ERROR_MWI_MaxNumOfControllingUsersReached,&rose_etsi_mwi, 14, NULL, NULL }, { ROSE_ERROR_MWI_MaxNumOfActiveInstancesReached,&rose_etsi_mwi, 15, NULL, NULL }, /* *INDENT-ON* */ }; /* ------------------------------------------------------------------- */ /* * Note the first value in oid.values[] is really the first two * OID subidentifiers. They are compressed using this formula: * First_Value = (First_Subidentifier * 40) + Second_Subidentifier */ /*! \brief ECMA private-isdn-signalling-domain prefix. */ static const struct asn1_oid rose_qsig_isdn_domain = { /* *INDENT-OFF* */ /* {iso(1) identified-organization(3) icd-ecma(12) private-isdn-signalling-domain(9)} */ 3, { 43, 12, 9 } /* *INDENT-ON* */ }; /*! \brief Q.SIG specific invoke/result encode/decode message table */ static const struct rose_convert_msg rose_qsig_msgs[] = { /* *INDENT-OFF* */ /* * operation, oid_prefix, value, * encode_invoke_args, encode_result_args, * decode_invoke_args, decode_result_args */ /* * localValue's from Q.SIG Name-Operations 4th edition * { iso(1) standard(0) pss1-name(13868) name-operations(0) } */ { ROSE_QSIG_CallingName, NULL, 0, rose_enc_qsig_CallingName_ARG, NULL, rose_dec_qsig_CallingName_ARG, NULL }, { ROSE_QSIG_CalledName, NULL, 1, rose_enc_qsig_CalledName_ARG, NULL, rose_dec_qsig_CalledName_ARG, NULL }, { ROSE_QSIG_ConnectedName, NULL, 2, rose_enc_qsig_ConnectedName_ARG, NULL, rose_dec_qsig_ConnectedName_ARG, NULL }, { ROSE_QSIG_BusyName, NULL, 3, rose_enc_qsig_BusyName_ARG, NULL, rose_dec_qsig_BusyName_ARG, NULL }, /* * globalValue's (OIDs) from Q.SIG Name-Operations 2nd edition * { iso(1) identified-organization(3) icd-ecma(12) standard(0) qsig-name(164) name-operations(0) } * * This older version of the Q.SIG switch is not supported. * However, we will accept receiving these messages anyway. */ { ROSE_QSIG_CallingName, &rose_qsig_isdn_domain, 0, rose_enc_qsig_CallingName_ARG, NULL, rose_dec_qsig_CallingName_ARG, NULL }, { ROSE_QSIG_CalledName, &rose_qsig_isdn_domain, 1, rose_enc_qsig_CalledName_ARG, NULL, rose_dec_qsig_CalledName_ARG, NULL }, { ROSE_QSIG_ConnectedName, &rose_qsig_isdn_domain, 2, rose_enc_qsig_ConnectedName_ARG, NULL, rose_dec_qsig_ConnectedName_ARG, NULL }, { ROSE_QSIG_BusyName, &rose_qsig_isdn_domain, 3, rose_enc_qsig_BusyName_ARG, NULL, rose_dec_qsig_BusyName_ARG, NULL }, /* * localValue's from Q.SIG SS-AOC-Operations * { iso(1) standard(0) pss1-advice-of-charge(15050) advice-of-charge-operations(0) } */ { ROSE_QSIG_ChargeRequest, NULL, 59, rose_enc_qsig_ChargeRequest_ARG, rose_enc_qsig_ChargeRequest_RES, rose_dec_qsig_ChargeRequest_ARG, rose_dec_qsig_ChargeRequest_RES }, { ROSE_QSIG_GetFinalCharge, NULL, 60, rose_enc_qsig_DummyArg_ARG, NULL, rose_dec_qsig_DummyArg_ARG, NULL }, { ROSE_QSIG_AocFinal, NULL, 61, rose_enc_qsig_AocFinal_ARG, NULL, rose_dec_qsig_AocFinal_ARG, NULL }, { ROSE_QSIG_AocInterim, NULL, 62, rose_enc_qsig_AocInterim_ARG, NULL, rose_dec_qsig_AocInterim_ARG, NULL }, { ROSE_QSIG_AocRate, NULL, 63, rose_enc_qsig_AocRate_ARG, NULL, rose_dec_qsig_AocRate_ARG, NULL }, { ROSE_QSIG_AocComplete, NULL, 64, rose_enc_qsig_AocComplete_ARG, rose_enc_qsig_AocComplete_RES, rose_dec_qsig_AocComplete_ARG, rose_dec_qsig_AocComplete_RES }, { ROSE_QSIG_AocDivChargeReq, NULL, 65, rose_enc_qsig_AocDivChargeReq_ARG, NULL, rose_dec_qsig_AocDivChargeReq_ARG, NULL }, /* * localValue's from Q.SIG Call-Transfer-Operations * { iso(1) standard(0) pss1-call-transfer(13869) call-transfer-operations(0) } */ { ROSE_QSIG_CallTransferIdentify, NULL, 7, rose_enc_qsig_DummyArg_ARG, rose_enc_qsig_CallTransferIdentify_RES, rose_dec_qsig_DummyArg_ARG, rose_dec_qsig_CallTransferIdentify_RES }, { ROSE_QSIG_CallTransferAbandon, NULL, 8, rose_enc_qsig_DummyArg_ARG, NULL, rose_dec_qsig_DummyArg_ARG, NULL }, { ROSE_QSIG_CallTransferInitiate, NULL, 9, rose_enc_qsig_CallTransferInitiate_ARG, rose_enc_qsig_DummyRes_RES, rose_dec_qsig_CallTransferInitiate_ARG, rose_dec_qsig_DummyRes_RES }, { ROSE_QSIG_CallTransferSetup, NULL, 10, rose_enc_qsig_CallTransferSetup_ARG, rose_enc_qsig_DummyRes_RES, rose_dec_qsig_CallTransferSetup_ARG, rose_dec_qsig_DummyRes_RES }, { ROSE_QSIG_CallTransferActive, NULL, 11, rose_enc_qsig_CallTransferActive_ARG, NULL, rose_dec_qsig_CallTransferActive_ARG, NULL }, { ROSE_QSIG_CallTransferComplete, NULL, 12, rose_enc_qsig_CallTransferComplete_ARG, NULL, rose_dec_qsig_CallTransferComplete_ARG, NULL }, { ROSE_QSIG_CallTransferUpdate, NULL, 13, rose_enc_qsig_CallTransferUpdate_ARG, NULL, rose_dec_qsig_CallTransferUpdate_ARG, NULL }, { ROSE_QSIG_SubaddressTransfer, NULL, 14, rose_enc_qsig_SubaddressTransfer_ARG, NULL, rose_dec_qsig_SubaddressTransfer_ARG, NULL }, /* * NOTE: I do not have the specification needed to fully support this * message. Fortunately, all I have to do for this message is to switch * it to the bridged call leg for 2BCT support. */ { ROSE_QSIG_PathReplacement, NULL, 4, NULL, NULL, NULL, NULL }, /* * localValue's from Q.SIG Call-Diversion-Operations * { iso(1) standard(0) pss1-call-diversion(13873) call-diversion-operations(0) } */ { ROSE_QSIG_ActivateDiversionQ, NULL, 15, rose_enc_qsig_ActivateDiversionQ_ARG, rose_enc_qsig_DummyRes_RES, rose_dec_qsig_ActivateDiversionQ_ARG, rose_dec_qsig_DummyRes_RES }, { ROSE_QSIG_DeactivateDiversionQ, NULL, 16, rose_enc_qsig_DeactivateDiversionQ_ARG, rose_enc_qsig_DummyRes_RES, rose_dec_qsig_DeactivateDiversionQ_ARG, rose_dec_qsig_DummyRes_RES }, { ROSE_QSIG_InterrogateDiversionQ, NULL, 17, rose_enc_qsig_InterrogateDiversionQ_ARG,rose_enc_qsig_InterrogateDiversionQ_RES, rose_dec_qsig_InterrogateDiversionQ_ARG,rose_dec_qsig_InterrogateDiversionQ_RES }, { ROSE_QSIG_CheckRestriction, NULL, 18, rose_enc_qsig_CheckRestriction_ARG, rose_enc_qsig_DummyRes_RES, rose_dec_qsig_CheckRestriction_ARG, rose_dec_qsig_DummyRes_RES }, { ROSE_QSIG_CallRerouting, NULL, 19, rose_enc_qsig_CallRerouting_ARG, rose_enc_qsig_DummyRes_RES, rose_dec_qsig_CallRerouting_ARG, rose_dec_qsig_DummyRes_RES }, { ROSE_QSIG_DivertingLegInformation1, NULL, 20, rose_enc_qsig_DivertingLegInformation1_ARG,NULL, rose_dec_qsig_DivertingLegInformation1_ARG,NULL }, { ROSE_QSIG_DivertingLegInformation2, NULL, 21, rose_enc_qsig_DivertingLegInformation2_ARG,NULL, rose_dec_qsig_DivertingLegInformation2_ARG,NULL }, { ROSE_QSIG_DivertingLegInformation3, NULL, 22, rose_enc_qsig_DivertingLegInformation3_ARG,NULL, rose_dec_qsig_DivertingLegInformation3_ARG,NULL }, { ROSE_QSIG_CfnrDivertedLegFailed, NULL, 23, rose_enc_qsig_DummyArg_ARG, NULL, 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) } */ { ROSE_QSIG_MWIActivate, NULL, 80, rose_enc_qsig_MWIActivate_ARG, rose_enc_qsig_DummyRes_RES, rose_dec_qsig_MWIActivate_ARG, rose_dec_qsig_DummyRes_RES }, { ROSE_QSIG_MWIDeactivate, NULL, 81, rose_enc_qsig_MWIDeactivate_ARG, rose_enc_qsig_DummyRes_RES, rose_dec_qsig_MWIDeactivate_ARG, rose_dec_qsig_DummyRes_RES }, { ROSE_QSIG_MWIInterrogate, NULL, 82, rose_enc_qsig_MWIInterrogate_ARG, rose_enc_qsig_MWIInterrogate_RES, rose_dec_qsig_MWIInterrogate_ARG, rose_dec_qsig_MWIInterrogate_RES }, /* *INDENT-ON* */ }; /*! \brief Q.SIG specific error-value converion table */ static const struct rose_convert_error rose_qsig_errors[] = { /* *INDENT-OFF* */ /* * error-code, oid_prefix, value * encode_error_args, decode_error_args */ /* * localValue Errors from General-Error-List * {ccitt identified-organization q 950 general-error-list(1)} */ { ROSE_ERROR_Gen_NotSubscribed, NULL, 0, NULL, NULL }, { ROSE_ERROR_Gen_RejectedByNetwork, NULL, 1, NULL, NULL }, { ROSE_ERROR_Gen_RejectedByUser, NULL, 2, NULL, NULL }, { ROSE_ERROR_Gen_NotAvailable, NULL, 3, NULL, NULL }, { ROSE_ERROR_Gen_InsufficientInformation, NULL, 5, NULL, NULL }, { ROSE_ERROR_Gen_InvalidServedUserNr, NULL, 6, NULL, NULL }, { ROSE_ERROR_Gen_InvalidCallState, NULL, 7, NULL, NULL }, { ROSE_ERROR_Gen_BasicServiceNotProvided, NULL, 8, NULL, NULL }, { ROSE_ERROR_Gen_NotIncomingCall, NULL, 9, NULL, NULL }, { ROSE_ERROR_Gen_SupplementaryServiceInteractionNotAllowed,NULL, 10, NULL, NULL }, { ROSE_ERROR_Gen_ResourceUnavailable, NULL, 11, NULL, NULL }, { ROSE_ERROR_Gen_CallFailure, NULL, 25, NULL, NULL }, { ROSE_ERROR_Gen_ProceduralError, NULL, 43, NULL, NULL }, /* * From various Q.SIG specifications. * We will ignore the manufacturer specific extension information. */ { ROSE_ERROR_QSIG_Unspecified, NULL, 1008, NULL, NULL }, /* * localValue Errors from Q.SIG SS-AOC-Operations * { iso(1) standard(0) pss1-advice-of-charge(15050) advice-of-charge-operations(0) } */ { ROSE_ERROR_QSIG_AOC_FreeOfCharge, NULL, 1016, NULL, NULL }, /* * localValue's from Q.SIG Call-Transfer-Operations * { iso(1) standard(0) pss1-call-transfer(13869) call-transfer-operations(0) } */ { ROSE_ERROR_QSIG_CT_InvalidReroutingNumber, NULL, 1004, NULL, NULL }, { ROSE_ERROR_QSIG_CT_UnrecognizedCallIdentity,NULL, 1005, NULL, NULL }, { ROSE_ERROR_QSIG_CT_EstablishmentFailure, NULL, 1006, NULL, NULL }, /* * localValue's from Q.SIG Call-Diversion-Operations * { iso(1) standard(0) pss1-call-diversion(13873) call-diversion-operations(0) } */ { ROSE_ERROR_Div_InvalidDivertedToNr, NULL, 12, NULL, NULL }, { ROSE_ERROR_Div_SpecialServiceNr, NULL, 14, NULL, NULL }, { ROSE_ERROR_Div_DiversionToServedUserNr, NULL, 15, NULL, NULL }, { ROSE_ERROR_Div_NumberOfDiversionsExceeded, NULL, 24, NULL, NULL }, { ROSE_ERROR_QSIG_Div_TemporarilyUnavailable, NULL, 1000, NULL, NULL }, { ROSE_ERROR_QSIG_Div_NotAuthorized, NULL, 1007, 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) } */ { ROSE_ERROR_QSIG_InvalidMsgCentreId, NULL, 1018, NULL, NULL }, /* *INDENT-ON* */ }; /* ------------------------------------------------------------------- */ /*! \brief DMS-100 specific invoke/result encode/decode message table */ static const struct rose_convert_msg rose_dms100_msgs[] = { /* *INDENT-OFF* */ /* * operation, oid_prefix, value, * encode_invoke_args, encode_result_args, * decode_invoke_args, decode_result_args */ { ROSE_DMS100_RLT_OperationInd, NULL, ROSE_DMS100_RLT_OPERATION_IND, NULL, rose_enc_dms100_RLT_OperationInd_RES, NULL, rose_dec_dms100_RLT_OperationInd_RES }, { ROSE_DMS100_RLT_ThirdParty, NULL, ROSE_DMS100_RLT_THIRD_PARTY, rose_enc_dms100_RLT_ThirdParty_ARG, NULL, rose_dec_dms100_RLT_ThirdParty_ARG, NULL }, /* DMS-100 seems to have pirated some Q.SIG messages */ /* * localValue's from Q.SIG Name-Operations * { iso(1) standard(0) pss1-name(13868) name-operations(0) } */ { ROSE_QSIG_CallingName, NULL, 0, rose_enc_qsig_CallingName_ARG, NULL, rose_dec_qsig_CallingName_ARG, NULL }, #if 0 /* ROSE_DMS100_RLT_OPERATION_IND, and ROSE_DMS100_RLT_THIRD_PARTY conflict! */ { ROSE_QSIG_CalledName, NULL, 1, rose_enc_qsig_CalledName_ARG, NULL, rose_dec_qsig_CalledName_ARG, NULL }, { ROSE_QSIG_ConnectedName, NULL, 2, rose_enc_qsig_ConnectedName_ARG, NULL, rose_dec_qsig_ConnectedName_ARG, NULL }, { ROSE_QSIG_BusyName, NULL, 3, rose_enc_qsig_BusyName_ARG, NULL, rose_dec_qsig_BusyName_ARG, NULL }, #endif /* *INDENT-ON* */ }; /*! \brief DMS-100 specific error-value converion table */ static const struct rose_convert_error rose_dms100_errors[] = { /* *INDENT-OFF* */ /* * error-code, oid_prefix, value * encode_error_args, decode_error_args */ { ROSE_ERROR_DMS100_RLT_BridgeFail, NULL, 0x10, NULL, NULL }, { ROSE_ERROR_DMS100_RLT_CallIDNotFound, NULL, 0x11, NULL, NULL }, { ROSE_ERROR_DMS100_RLT_NotAllowed, NULL, 0x12, NULL, NULL }, { ROSE_ERROR_DMS100_RLT_SwitchEquipCongs, NULL, 0x13, NULL, NULL }, /* *INDENT-ON* */ }; /* ------------------------------------------------------------------- */ /* * Note the first value in oid.values[] is really the first two * OID subidentifiers. They are compressed using this formula: * First_Value = (First_Subidentifier * 40) + Second_Subidentifier */ static const struct asn1_oid rose_ni2_oid = { /* *INDENT-OFF* */ /* { iso(1) member-body(2) usa(840) ansi-t1(10005) operations(0) } */ 4, { 42, 840, 10005, 0 } /* *INDENT-ON* */ }; /*! \brief NI2 specific invoke/result encode/decode message table */ static const struct rose_convert_msg rose_ni2_msgs[] = { /* *INDENT-OFF* */ /* * operation, oid_prefix, value, * encode_invoke_args, encode_result_args, * decode_invoke_args, decode_result_args */ { ROSE_NI2_InformationFollowing, &rose_ni2_oid, 4, rose_enc_ni2_InformationFollowing_ARG, NULL, rose_dec_ni2_InformationFollowing_ARG, NULL }, /* Also used by PRI_SWITCH_ATT4ESS and PRI_SWITCH_LUCENT5E */ { ROSE_NI2_InitiateTransfer, &rose_ni2_oid, 8, rose_enc_ni2_InitiateTransfer_ARG, NULL, rose_dec_ni2_InitiateTransfer_ARG, NULL }, /* NI2 seems to have pirated several Q.SIG messages */ /* * localValue's from Q.SIG Name-Operations * { iso(1) standard(0) pss1-name(13868) name-operations(0) } */ { ROSE_QSIG_CallingName, NULL, 0, rose_enc_qsig_CallingName_ARG, NULL, rose_dec_qsig_CallingName_ARG, NULL }, { ROSE_QSIG_CalledName, NULL, 1, rose_enc_qsig_CalledName_ARG, NULL, rose_dec_qsig_CalledName_ARG, NULL }, { ROSE_QSIG_ConnectedName, NULL, 2, rose_enc_qsig_ConnectedName_ARG, NULL, rose_dec_qsig_ConnectedName_ARG, NULL }, { ROSE_QSIG_BusyName, NULL, 3, rose_enc_qsig_BusyName_ARG, NULL, rose_dec_qsig_BusyName_ARG, NULL }, /* *INDENT-ON* */ }; /*! \brief NI2 specific error-value converion table */ static const struct rose_convert_error rose_ni2_errors[] = { /* *INDENT-OFF* */ /* * error-code, oid_prefix, value * encode_error_args, decode_error_args */ /* * localValue Errors from General-Error-List * {ccitt identified-organization q 950 general-error-list(1)} */ { ROSE_ERROR_Gen_NotSubscribed, NULL, 0, NULL, NULL }, { ROSE_ERROR_Gen_RejectedByNetwork, NULL, 1, NULL, NULL }, { ROSE_ERROR_Gen_RejectedByUser, NULL, 2, NULL, NULL }, { ROSE_ERROR_Gen_NotAvailable, NULL, 3, NULL, NULL }, { ROSE_ERROR_Gen_InsufficientInformation, NULL, 5, NULL, NULL }, { ROSE_ERROR_Gen_InvalidServedUserNr, NULL, 6, NULL, NULL }, { ROSE_ERROR_Gen_InvalidCallState, NULL, 7, NULL, NULL }, { ROSE_ERROR_Gen_BasicServiceNotProvided, NULL, 8, NULL, NULL }, { ROSE_ERROR_Gen_NotIncomingCall, NULL, 9, NULL, NULL }, { ROSE_ERROR_Gen_SupplementaryServiceInteractionNotAllowed,NULL, 10, NULL, NULL }, { ROSE_ERROR_Gen_ResourceUnavailable, NULL, 11, NULL, NULL }, { ROSE_ERROR_Gen_CallFailure, NULL, 25, NULL, NULL }, { ROSE_ERROR_Gen_ProceduralError, NULL, 43, NULL, NULL }, /* *INDENT-ON* */ }; /* ------------------------------------------------------------------- */ /*! * \internal * \brief Convert the given code value to a string. * * \param code Code value to convert to a string. * \param arr Array to convert the code to a string. * \param num_elements Number of elements in the conversion array. * * \retval String version of the given code value. */ static const char *rose_code2str(int code, const struct rose_code_strings *arr, unsigned num_elements) { static char invalid_code[40]; unsigned index; for (index = 0; index < num_elements; ++index) { if (arr[index].code == code) { return arr[index].name; } } snprintf(invalid_code, sizeof(invalid_code), "Invalid code:%d 0x%X", code, code); return invalid_code; } /*! * \brief Convert the given operation-value to a string. * * \param operation Operation-value to convert to a string. * * \retval String version of the given operation-value. */ const char *rose_operation2str(enum rose_operation operation) { static const struct rose_code_strings arr[] = { /* *INDENT-OFF* */ { ROSE_None, "ROSE_None" }, { ROSE_Unknown, "ROSE_Unknown" }, { ROSE_ETSI_ActivationDiversion, "ROSE_ETSI_ActivationDiversion" }, { ROSE_ETSI_DeactivationDiversion, "ROSE_ETSI_DeactivationDiversion" }, { ROSE_ETSI_ActivationStatusNotificationDiv,"ROSE_ETSI_ActivationStatusNotificationDiv" }, { ROSE_ETSI_DeactivationStatusNotificationDiv,"ROSE_ETSI_DeactivationStatusNotificationDiv" }, { ROSE_ETSI_InterrogationDiversion, "ROSE_ETSI_InterrogationDiversion" }, { ROSE_ETSI_DiversionInformation, "ROSE_ETSI_DiversionInformation" }, { ROSE_ETSI_CallDeflection, "ROSE_ETSI_CallDeflection" }, { ROSE_ETSI_CallRerouting, "ROSE_ETSI_CallRerouting" }, { ROSE_ETSI_DivertingLegInformation2, "ROSE_ETSI_DivertingLegInformation2" }, { ROSE_ETSI_InterrogateServedUserNumbers, "ROSE_ETSI_InterrogateServedUserNumbers" }, { ROSE_ETSI_DivertingLegInformation1, "ROSE_ETSI_DivertingLegInformation1" }, { ROSE_ETSI_DivertingLegInformation3, "ROSE_ETSI_DivertingLegInformation3" }, { ROSE_ETSI_EctExecute, "ROSE_ETSI_EctExecute" }, { ROSE_ETSI_ExplicitEctExecute, "ROSE_ETSI_ExplicitEctExecute" }, { ROSE_ETSI_RequestSubaddress, "ROSE_ETSI_RequestSubaddress" }, { ROSE_ETSI_SubaddressTransfer, "ROSE_ETSI_SubaddressTransfer" }, { ROSE_ETSI_EctLinkIdRequest, "ROSE_ETSI_EctLinkIdRequest" }, { ROSE_ETSI_EctInform, "ROSE_ETSI_EctInform" }, { ROSE_ETSI_EctLoopTest, "ROSE_ETSI_EctLoopTest" }, { ROSE_ETSI_ChargingRequest, "ROSE_ETSI_ChargingRequest" }, { ROSE_ETSI_AOCSCurrency, "ROSE_ETSI_AOCSCurrency" }, { ROSE_ETSI_AOCSSpecialArr, "ROSE_ETSI_AOCSSpecialArr" }, { ROSE_ETSI_AOCDCurrency, "ROSE_ETSI_AOCDCurrency" }, { ROSE_ETSI_AOCDChargingUnit, "ROSE_ETSI_AOCDChargingUnit" }, { 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_ETSI_MCIDRequest, "ROSE_ETSI_MCIDRequest" }, { ROSE_ETSI_MWIActivate, "ROSE_ETSI_MWIActivate" }, { ROSE_ETSI_MWIDeactivate, "ROSE_ETSI_MWIDeactivate" }, { ROSE_ETSI_MWIIndicate, "ROSE_ETSI_MWIIndicate" }, { ROSE_QSIG_CallingName, "ROSE_QSIG_CallingName" }, { ROSE_QSIG_CalledName, "ROSE_QSIG_CalledName" }, { ROSE_QSIG_ConnectedName, "ROSE_QSIG_ConnectedName" }, { ROSE_QSIG_BusyName, "ROSE_QSIG_BusyName" }, { ROSE_QSIG_ChargeRequest, "ROSE_QSIG_ChargeRequest" }, { ROSE_QSIG_GetFinalCharge, "ROSE_QSIG_GetFinalCharge" }, { ROSE_QSIG_AocFinal, "ROSE_QSIG_AocFinal" }, { ROSE_QSIG_AocInterim, "ROSE_QSIG_AocInterim" }, { ROSE_QSIG_AocRate, "ROSE_QSIG_AocRate" }, { ROSE_QSIG_AocComplete, "ROSE_QSIG_AocComplete" }, { ROSE_QSIG_AocDivChargeReq, "ROSE_QSIG_AocDivChargeReq" }, { ROSE_QSIG_CallTransferIdentify, "ROSE_QSIG_CallTransferIdentify" }, { ROSE_QSIG_CallTransferAbandon, "ROSE_QSIG_CallTransferAbandon" }, { ROSE_QSIG_CallTransferInitiate, "ROSE_QSIG_CallTransferInitiate" }, { ROSE_QSIG_CallTransferSetup, "ROSE_QSIG_CallTransferSetup" }, { ROSE_QSIG_CallTransferActive, "ROSE_QSIG_CallTransferActive" }, { ROSE_QSIG_CallTransferComplete, "ROSE_QSIG_CallTransferComplete" }, { ROSE_QSIG_CallTransferUpdate, "ROSE_QSIG_CallTransferUpdate" }, { ROSE_QSIG_SubaddressTransfer, "ROSE_QSIG_SubaddressTransfer" }, { ROSE_QSIG_PathReplacement, "ROSE_QSIG_PathReplacement" }, { ROSE_QSIG_ActivateDiversionQ, "ROSE_QSIG_ActivateDiversionQ" }, { ROSE_QSIG_DeactivateDiversionQ, "ROSE_QSIG_DeactivateDiversionQ" }, { ROSE_QSIG_InterrogateDiversionQ, "ROSE_QSIG_InterrogateDiversionQ" }, { ROSE_QSIG_CheckRestriction, "ROSE_QSIG_CheckRestriction" }, { ROSE_QSIG_CallRerouting, "ROSE_QSIG_CallRerouting" }, { ROSE_QSIG_DivertingLegInformation1, "ROSE_QSIG_DivertingLegInformation1" }, { ROSE_QSIG_DivertingLegInformation2, "ROSE_QSIG_DivertingLegInformation2" }, { 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" }, { ROSE_DMS100_RLT_OperationInd, "ROSE_DMS100_RLT_OperationInd" }, { ROSE_DMS100_RLT_ThirdParty, "ROSE_DMS100_RLT_ThirdParty" }, { ROSE_NI2_InformationFollowing, "ROSE_NI2_InformationFollowing" }, { ROSE_NI2_InitiateTransfer, "ROSE_NI2_InitiateTransfer" }, /* *INDENT-ON* */ }; return rose_code2str(operation, arr, ARRAY_LEN(arr)); } /*! * \brief Convert the given error-value to a string. * * \param code Error-value to convert to a string. * * \retval String version of the given error-value. */ const char *rose_error2str(enum rose_error_code code) { static const struct rose_code_strings arr[] = { /* *INDENT-OFF* */ { ROSE_ERROR_None, "No error occurred" }, { ROSE_ERROR_Unknown, "Unknown error-value code" }, { ROSE_ERROR_Gen_NotSubscribed, "General: Not Subscribed" }, { ROSE_ERROR_Gen_NotAvailable, "General: Not Available" }, { ROSE_ERROR_Gen_NotImplemented, "General: Not Implemented" }, { ROSE_ERROR_Gen_InvalidServedUserNr, "General: Invalid Served User Number" }, { ROSE_ERROR_Gen_InvalidCallState, "General: Invalid Call State" }, { ROSE_ERROR_Gen_BasicServiceNotProvided, "General: Basic Service Not Provided" }, { ROSE_ERROR_Gen_NotIncomingCall, "General: Not Incoming Call" }, { ROSE_ERROR_Gen_SupplementaryServiceInteractionNotAllowed,"General: Supplementary Service Interaction Not Allowed" }, { ROSE_ERROR_Gen_ResourceUnavailable, "General: Resource Unavailable" }, /* Additional Q.950 General-Errors for Q.SIG */ { ROSE_ERROR_Gen_RejectedByNetwork, "General: Rejected By Network" }, { ROSE_ERROR_Gen_RejectedByUser, "General: Rejected By User" }, { ROSE_ERROR_Gen_InsufficientInformation, "General: Insufficient Information" }, { ROSE_ERROR_Gen_CallFailure, "General: Call Failure" }, { ROSE_ERROR_Gen_ProceduralError, "General: Procedural Error" }, { ROSE_ERROR_Div_InvalidDivertedToNr, "Diversion: Invalid Diverted To Number" }, { ROSE_ERROR_Div_SpecialServiceNr, "Diversion: Special Service Number" }, { ROSE_ERROR_Div_DiversionToServedUserNr, "Diversion: Diversion To Served User Number" }, { ROSE_ERROR_Div_IncomingCallAccepted, "Diversion: Incoming Call Accepted" }, { ROSE_ERROR_Div_NumberOfDiversionsExceeded, "Diversion: Number Of Diversions Exceeded" }, { ROSE_ERROR_Div_NotActivated, "Diversion: Not Activated" }, { ROSE_ERROR_Div_RequestAlreadyAccepted, "Diversion: Request Already Accepted" }, { ROSE_ERROR_AOC_NoChargingInfoAvailable, "AOC: No Charging Info Available" }, { 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" }, { ROSE_ERROR_MWI_InvalidReceivingUserNr, "MWI: Invalid Receiving User Number" }, { ROSE_ERROR_MWI_ReceivingUserNotSubscribed, "MWI: Receiving User Not Subscribed" }, { ROSE_ERROR_MWI_ControllingUserNotRegistered,"MWI: Controlling User Not Registered" }, { ROSE_ERROR_MWI_IndicationNotDelivered, "MWI: Indication Not Delivered" }, { ROSE_ERROR_MWI_MaxNumOfControllingUsersReached,"MWI: Max Num Of Controlling Users Reached" }, { ROSE_ERROR_MWI_MaxNumOfActiveInstancesReached,"MWI: Max Num Of Active Instances Reached" }, /* Q.SIG specific errors */ { ROSE_ERROR_QSIG_Unspecified, "Unspecified" }, { ROSE_ERROR_QSIG_AOC_FreeOfCharge, "AOC: FreeOfCharge" }, { ROSE_ERROR_QSIG_CT_InvalidReroutingNumber, "CT: Invalid Rerouting Number" }, { ROSE_ERROR_QSIG_CT_UnrecognizedCallIdentity,"CT: Unrecognized Call Identity" }, { ROSE_ERROR_QSIG_CT_EstablishmentFailure, "CT: Establishment Failure" }, { 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 */ { ROSE_ERROR_DMS100_RLT_BridgeFail, "RLT: Bridge Fail" }, { ROSE_ERROR_DMS100_RLT_CallIDNotFound, "RLT: Call ID Not Found" }, { ROSE_ERROR_DMS100_RLT_NotAllowed, "RLT: Not Allowed" }, { ROSE_ERROR_DMS100_RLT_SwitchEquipCongs, "RLT: Switch Equip Congs" }, /* *INDENT-ON* */ }; return rose_code2str(code, arr, ARRAY_LEN(arr)); } /*! * \brief Convert the given reject problem-value to a string. * * \param code Reject problem-value to convert to a string. * * \retval String version of the given reject problem-value. */ const char *rose_reject2str(enum rose_reject_code code) { static const struct rose_code_strings arr[] = { /* *INDENT-OFF* */ { ROSE_REJECT_None, "No reject occurred" }, { ROSE_REJECT_Unknown, "Unknown reject code" }, { ROSE_REJECT_Gen_UnrecognizedComponent, "General: Unrecognized Component" }, { ROSE_REJECT_Gen_MistypedComponent, "General: Mistyped Component" }, { ROSE_REJECT_Gen_BadlyStructuredComponent, "General: Badly Structured Component" }, { ROSE_REJECT_Inv_DuplicateInvocation, "Invoke: Duplicate Invocation" }, { ROSE_REJECT_Inv_UnrecognizedOperation, "Invoke: Unrecognized Operation" }, { ROSE_REJECT_Inv_MistypedArgument, "Invoke: Mistyped Argument" }, { ROSE_REJECT_Inv_ResourceLimitation, "Invoke: Resource Limitation" }, { ROSE_REJECT_Inv_InitiatorReleasing, "Invoke: Initiator Releasing" }, { ROSE_REJECT_Inv_UnrecognizedLinkedID, "Invoke: Unrecognized Linked ID" }, { ROSE_REJECT_Inv_LinkedResponseUnexpected, "Invoke: Linked Response Unexpected" }, { ROSE_REJECT_Inv_UnexpectedChildOperation, "Invoke: Unexpected Child Operation" }, { ROSE_REJECT_Res_UnrecognizedInvocation, "Result: Unrecognized Invocation" }, { ROSE_REJECT_Res_ResultResponseUnexpected, "Result: Result Response Unexpected" }, { ROSE_REJECT_Res_MistypedResult, "Result: Mistyped Result" }, { ROSE_REJECT_Err_UnrecognizedInvocation, "Error: Unrecognized Invocation" }, { ROSE_REJECT_Err_ErrorResponseUnexpected, "Error: Error Response Unexpected" }, { ROSE_REJECT_Err_UnrecognizedError, "Error: Unrecognized Error" }, { ROSE_REJECT_Err_UnexpectedError, "Error: Unexpected Error" }, { ROSE_REJECT_Err_MistypedParameter, "Error: Mistyped Parameter" }, /* *INDENT-ON* */ }; return rose_code2str(code, arr, ARRAY_LEN(arr)); } /*! * \internal * \brief Find an operation message conversion entry using the operation code. * * \param ctrl D channel controller for diagnostic messages or global options. * \param operation Library operation-value code. * * \retval Message conversion entry on success. * \retval NULL on error. */ static const struct rose_convert_msg *rose_find_msg_by_op_code(struct pri *ctrl, enum rose_operation operation) { const struct rose_convert_msg *found; const struct rose_convert_msg *table; size_t num_entries; size_t index; /* Determine which message conversion table to use */ switch (ctrl->switchtype) { case PRI_SWITCH_EUROISDN_T1: case PRI_SWITCH_EUROISDN_E1: table = rose_etsi_msgs; num_entries = ARRAY_LEN(rose_etsi_msgs); break; case PRI_SWITCH_QSIG: table = rose_qsig_msgs; num_entries = ARRAY_LEN(rose_qsig_msgs); break; case PRI_SWITCH_DMS100: table = rose_dms100_msgs; num_entries = ARRAY_LEN(rose_dms100_msgs); break; case PRI_SWITCH_ATT4ESS: case PRI_SWITCH_LUCENT5E: case PRI_SWITCH_NI2: table = rose_ni2_msgs; num_entries = ARRAY_LEN(rose_ni2_msgs); break; default: return NULL; } /* Search for the table entry */ found = NULL; for (index = 0; index < num_entries; ++index) { if (table[index].operation == operation) { found = &table[index]; break; } } return found; } /*! * \internal * \brief Find an operation message conversion entry using the * operation-value OID value or localValue. * * \param ctrl D channel controller for diagnostic messages or global options. * \param oid Search for the full OID if not NULL. * \param local Search for the localValue if OID is NULL. * * \retval Message conversion entry on success. * \retval NULL on error. */ static const struct rose_convert_msg *rose_find_msg_by_op_val(struct pri *ctrl, const struct asn1_oid *oid, unsigned local) { const struct rose_convert_msg *found; const struct rose_convert_msg *table; size_t num_entries; size_t index; int sub_index; /* Determine which message conversion table to use */ switch (ctrl->switchtype) { case PRI_SWITCH_EUROISDN_T1: case PRI_SWITCH_EUROISDN_E1: table = rose_etsi_msgs; num_entries = ARRAY_LEN(rose_etsi_msgs); break; case PRI_SWITCH_QSIG: table = rose_qsig_msgs; num_entries = ARRAY_LEN(rose_qsig_msgs); break; case PRI_SWITCH_DMS100: table = rose_dms100_msgs; num_entries = ARRAY_LEN(rose_dms100_msgs); break; case PRI_SWITCH_ATT4ESS: case PRI_SWITCH_LUCENT5E: case PRI_SWITCH_NI2: table = rose_ni2_msgs; num_entries = ARRAY_LEN(rose_ni2_msgs); break; default: return NULL; } /* Search for the table entry */ found = NULL; if (oid) { /* Search for an OID entry */ local = oid->value[oid->num_values - 1]; for (index = 0; index < num_entries; ++index) { if (table[index].value == local && table[index].oid_prefix && table[index].oid_prefix->num_values == oid->num_values - 1) { /* Now lets match the OID prefix subidentifiers */ for (sub_index = oid->num_values - 2; 0 <= sub_index; --sub_index) { if (oid->value[sub_index] != table[index].oid_prefix->value[sub_index]) { break; } } if (sub_index == -1) { /* All of the OID subidentifiers matched */ found = &table[index]; break; } } } } else { /* Search for a localValue entry */ for (index = 0; index < num_entries; ++index) { if (table[index].value == local && !table[index].oid_prefix) { found = &table[index]; break; } } } return found; } /*! * \internal * \brief Find an error conversion entry using the error code. * * \param ctrl D channel controller for diagnostic messages or global options. * \param code Library error-value code. * * \retval Error conversion entry on success. * \retval NULL on error. */ static const struct rose_convert_error *rose_find_error_by_op_code(struct pri *ctrl, enum rose_error_code code) { const struct rose_convert_error *found; const struct rose_convert_error *table; size_t num_entries; size_t index; /* Determine which error conversion table to use */ switch (ctrl->switchtype) { case PRI_SWITCH_EUROISDN_T1: case PRI_SWITCH_EUROISDN_E1: table = rose_etsi_errors; num_entries = ARRAY_LEN(rose_etsi_errors); break; case PRI_SWITCH_QSIG: table = rose_qsig_errors; num_entries = ARRAY_LEN(rose_qsig_errors); break; case PRI_SWITCH_DMS100: table = rose_dms100_errors; num_entries = ARRAY_LEN(rose_dms100_errors); break; case PRI_SWITCH_ATT4ESS: case PRI_SWITCH_LUCENT5E: case PRI_SWITCH_NI2: table = rose_ni2_errors; num_entries = ARRAY_LEN(rose_ni2_errors); break; default: return NULL; } /* Search for the table entry */ found = NULL; for (index = 0; index < num_entries; ++index) { if (table[index].code == code) { found = &table[index]; break; } } return found; } /*! * \internal * \brief Find an error conversion entry using the * error-value OID value or localValue. * * \param ctrl D channel controller for diagnostic messages or global options. * \param oid Search for the full OID if not NULL. * \param local Search for the localValue if OID is NULL. * * \retval Error conversion entry on success. * \retval NULL on error. */ static const struct rose_convert_error *rose_find_error_by_op_val(struct pri *ctrl, const struct asn1_oid *oid, unsigned local) { const struct rose_convert_error *found; const struct rose_convert_error *table; size_t num_entries; size_t index; int sub_index; /* Determine which error conversion table to use */ switch (ctrl->switchtype) { case PRI_SWITCH_EUROISDN_T1: case PRI_SWITCH_EUROISDN_E1: table = rose_etsi_errors; num_entries = ARRAY_LEN(rose_etsi_errors); break; case PRI_SWITCH_QSIG: table = rose_qsig_errors; num_entries = ARRAY_LEN(rose_qsig_errors); break; case PRI_SWITCH_DMS100: table = rose_dms100_errors; num_entries = ARRAY_LEN(rose_dms100_errors); break; case PRI_SWITCH_ATT4ESS: case PRI_SWITCH_LUCENT5E: case PRI_SWITCH_NI2: table = rose_ni2_errors; num_entries = ARRAY_LEN(rose_ni2_errors); break; default: return NULL; } /* Search for the table entry */ found = NULL; if (oid) { /* Search for an OID entry */ local = oid->value[oid->num_values - 1]; for (index = 0; index < num_entries; ++index) { if (table[index].value == local && table[index].oid_prefix && table[index].oid_prefix->num_values == oid->num_values - 1) { /* Now lets match the OID prefix subidentifiers */ for (sub_index = oid->num_values - 2; 0 <= sub_index; --sub_index) { if (oid->value[sub_index] != table[index].oid_prefix->value[sub_index]) { break; } } if (sub_index == -1) { /* All of the OID subidentifiers matched */ found = &table[index]; break; } } } } else { /* Search for a localValue entry */ for (index = 0; index < num_entries; ++index) { if (table[index].value == local && !table[index].oid_prefix) { found = &table[index]; break; } } } return found; } /*! * \internal * \brief Encode the Facility ie component operation-value. * * \param pos Starting position to encode the operation-value. * \param end End of ASN.1 encoding data buffer. * \param oid_prefix Encode as an OID if not NULL. * \param local Encode as a localValue if oid_prefix is NULL * else it is the last OID subidentifier. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *rose_enc_operation_value(unsigned char *pos, unsigned char *end, const struct asn1_oid *oid_prefix, unsigned local) { struct asn1_oid oid; if (oid_prefix) { if (ARRAY_LEN(oid_prefix->value) <= oid_prefix->num_values) { return NULL; } oid = *oid_prefix; oid.value[oid.num_values++] = local; return asn1_enc_oid(pos, end, ASN1_TYPE_OBJECT_IDENTIFIER, &oid); } else { return asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, local); } } /*! \brief Mapped to rose_enc_operation_value() */ #define rose_enc_error_value(pos, end, oid_prefix, local) \ rose_enc_operation_value(pos, end, oid_prefix, local) /*! * \brief Encode the invoke component for a ROSE message. * * \param ctrl D channel controller for diagnostic messages or global options. * \param pos Starting position to encode ASN.1 message. * \param end End of ASN.1 encoding data buffer. * \param msg ROSE invoke message to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ unsigned char *rose_encode_invoke(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct rose_msg_invoke *msg) { const struct rose_convert_msg *convert; unsigned char *seq_len; convert = rose_find_msg_by_op_code(ctrl, msg->operation); if (!convert) { return NULL; } ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ROSE_TAG_COMPONENT_INVOKE); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, msg->invoke_id)); if (msg->linked_id_present) { ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0, msg->linked_id)); } ASN1_CALL(pos, rose_enc_operation_value(pos, end, convert->oid_prefix, convert->value)); if (convert->encode_invoke_args) { ASN1_CALL(pos, convert->encode_invoke_args(ctrl, pos, end, &msg->args)); } ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the result component for a ROSE message. * * \param ctrl D channel controller for diagnostic messages or global options. * \param pos Starting position to encode ASN.1 message. * \param end End of ASN.1 encoding data buffer. * \param msg ROSE result message to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ unsigned char *rose_encode_result(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct rose_msg_result *msg) { const struct rose_convert_msg *convert; unsigned char *seq_len; unsigned char *op_seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ROSE_TAG_COMPONENT_RESULT); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, msg->invoke_id)); if (msg->operation != ROSE_None) { convert = rose_find_msg_by_op_code(ctrl, msg->operation); if (!convert) { return NULL; } ASN1_CONSTRUCTED_BEGIN(op_seq_len, pos, end, ASN1_TYPE_SEQUENCE); ASN1_CALL(pos, rose_enc_operation_value(pos, end, convert->oid_prefix, convert->value)); if (convert->encode_result_args) { ASN1_CALL(pos, convert->encode_result_args(ctrl, pos, end, &msg->args)); } ASN1_CONSTRUCTED_END(op_seq_len, pos, end); } ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the error component for a ROSE message. * * \param ctrl D channel controller for diagnostic messages or global options. * \param pos Starting position to encode ASN.1 message. * \param end End of ASN.1 encoding data buffer. * \param msg ROSE error message to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ unsigned char *rose_encode_error(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct rose_msg_error *msg) { const struct rose_convert_error *convert; unsigned char *seq_len; convert = rose_find_error_by_op_code(ctrl, msg->code); if (!convert) { return NULL; } ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ROSE_TAG_COMPONENT_ERROR); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, msg->invoke_id)); ASN1_CALL(pos, rose_enc_error_value(pos, end, convert->oid_prefix, convert->value)); if (convert->encode_error_args) { ASN1_CALL(pos, convert->encode_error_args(ctrl, pos, end, &msg->args)); } ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the reject component for a ROSE message. * * \param ctrl D channel controller for diagnostic messages or global options. * \param pos Starting position to encode ASN.1 message. * \param end End of ASN.1 encoding data buffer. * \param msg ROSE reject message to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ unsigned char *rose_encode_reject(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct rose_msg_reject *msg) { unsigned char *seq_len; unsigned tag; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ROSE_TAG_COMPONENT_REJECT); /* Encode Invoke ID */ if (msg->invoke_id_present) { ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, msg->invoke_id)); } else { ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_TYPE_NULL)); } /* Encode the reject problem */ switch (msg->code & ~0xFF) { case ROSE_REJECT_BASE(ROSE_REJECT_BASE_General): tag = ASN1_CLASS_CONTEXT_SPECIFIC | 0; break; case ROSE_REJECT_BASE(ROSE_REJECT_BASE_Invoke): tag = ASN1_CLASS_CONTEXT_SPECIFIC | 1; break; case ROSE_REJECT_BASE(ROSE_REJECT_BASE_Result): tag = ASN1_CLASS_CONTEXT_SPECIFIC | 2; break; case ROSE_REJECT_BASE(ROSE_REJECT_BASE_Error): tag = ASN1_CLASS_CONTEXT_SPECIFIC | 3; break; default: return NULL; } ASN1_CALL(pos, asn1_enc_int(pos, end, tag, msg->code & 0xFF)); ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the ROSE message. * * \param ctrl D channel controller for diagnostic messages or global options. * \param pos Starting position to encode ASN.1 message. * \param end End of ASN.1 encoding data buffer. * \param msg ROSE message to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. * * \note This function only encodes the ROSE contents. It does not include * the protocol profile, NFE, NPP, and interpretation octets defined in * a facility ie that may precede the ROSE contents. These header octets * may already be stored in the encompassing buffer before the starting * position given here. */ unsigned char *rose_encode(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct rose_message *msg) { switch (msg->type) { case ROSE_COMP_TYPE_INVOKE: pos = rose_encode_invoke(ctrl, pos, end, &msg->component.invoke); break; case ROSE_COMP_TYPE_RESULT: pos = rose_encode_result(ctrl, pos, end, &msg->component.result); break; case ROSE_COMP_TYPE_ERROR: pos = rose_encode_error(ctrl, pos, end, &msg->component.error); break; case ROSE_COMP_TYPE_REJECT: pos = rose_encode_reject(ctrl, pos, end, &msg->component.reject); break; default: pos = NULL; break; } return pos; } /*! * \internal * \brief Encode the NetworkFacilityExtension 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 nfe Network Facility Extension information to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *fac_enc_nfe(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct facNetworkFacilityExtension *nfe) { unsigned char *seq_len; unsigned char *explicit_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 10); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0, nfe->source_entity)); if (nfe->source_number.length) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1); ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &nfe->source_number)); ASN1_CONSTRUCTED_END(explicit_len, pos, end); } ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, nfe->destination_entity)); if (nfe->destination_number.length) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3); ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &nfe->destination_number)); ASN1_CONSTRUCTED_END(explicit_len, pos, end); } ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the facility extension header. * * \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 header Facility extension information to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ unsigned char *fac_enc_extension_header(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct fac_extension_header *header) { if (header->nfe_present) { ASN1_CALL(pos, fac_enc_nfe(ctrl, pos, end, &header->nfe)); } if (header->npp_present) { ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 18, header->npp)); } if (header->interpretation_present) { ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 11, header->interpretation)); } return pos; } /*! * \brief Encode the facility ie contents header. * * \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 header Facility extension header data to encode (NULL if none). * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ unsigned char *facility_encode_header(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct fac_extension_header *header) { /* Make sure we have some room. */ if (end < pos + 2) { return NULL; } switch (ctrl->switchtype) { case PRI_SWITCH_EUROISDN_T1: case PRI_SWITCH_EUROISDN_E1: *pos++ = 0x80 | Q932_PROTOCOL_ROSE; header = NULL; break; case PRI_SWITCH_QSIG: *pos++ = 0x80 | Q932_PROTOCOL_EXTENSIONS; break; case PRI_SWITCH_DMS100: *pos++ = Q932_PROTOCOL_ROSE; /* DON'T set the EXT bit yet. */ *pos++ = 0x80 | ROSE_DMS100_RLT_SERVICE_ID; header = NULL; break; case PRI_SWITCH_ATT4ESS: case PRI_SWITCH_LUCENT5E: case PRI_SWITCH_NI2: if (header) { *pos++ = 0x80 | Q932_PROTOCOL_EXTENSIONS; } else { *pos++ = 0x80 | Q932_PROTOCOL_ROSE; } break; default: return NULL; } if (header) { ASN1_CALL(pos, fac_enc_extension_header(ctrl, pos, end, header)); } return pos; } /*! * \internal * \brief Decode the ROSE invoke message. * * \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 msg ROSE invoke message data to fill. * * \retval Start of the next ASN.1 component on success. * \retval NULL on error. */ static const unsigned char *rose_decode_invoke(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, struct rose_msg_invoke *msg) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; const struct rose_convert_msg *convert; struct asn1_oid oid; unsigned local; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, "INVOKE Component %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_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER); ASN1_CALL(pos, asn1_dec_int(ctrl, "invokeId", tag, pos, seq_end, &value)); msg->invoke_id = value; ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); if (tag == (ASN1_CLASS_CONTEXT_SPECIFIC | 0)) { ASN1_CALL(pos, asn1_dec_int(ctrl, "linkedId", tag, pos, seq_end, &value)); msg->linked_id = value; msg->linked_id_present = 1; ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); } else { msg->linked_id_present = 0; } /* Decode operation-value */ switch (tag) { case ASN1_TYPE_INTEGER: ASN1_CALL(pos, asn1_dec_int(ctrl, "operationValue", tag, pos, seq_end, &value)); local = value; oid.num_values = 0; break; case ASN1_TYPE_OBJECT_IDENTIFIER: ASN1_CALL(pos, asn1_dec_oid(ctrl, "operationValue", tag, pos, seq_end, &oid)); local = 0; break; default: ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); return NULL; } convert = rose_find_msg_by_op_val(ctrl, (oid.num_values == 0) ? NULL : &oid, local); if (convert) { msg->operation = convert->operation; } else { msg->operation = ROSE_Unknown; } if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " operationValue = %s\n", rose_operation2str(msg->operation)); } /* Decode any expected invoke arguments */ if (convert && convert->decode_invoke_args) { ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CALL(pos, convert->decode_invoke_args(ctrl, tag, pos, seq_end, &msg->args)); } ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \internal * \brief Decode the ROSE result message. * * \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 msg ROSE result message data to fill. * * \retval Start of the next ASN.1 component on success. * \retval NULL on error. */ static const unsigned char *rose_decode_result(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, struct rose_msg_result *msg) { int32_t value; int length; int seq_offset; int op_seq_offset; const unsigned char *seq_end; const unsigned char *op_seq_end; const struct rose_convert_msg *convert; struct asn1_oid oid; unsigned local; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, "RESULT Component %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_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER); ASN1_CALL(pos, asn1_dec_int(ctrl, "invokeId", tag, pos, seq_end, &value)); msg->invoke_id = value; /* Decode optional operation sequence */ 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); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " operation %s\n", asn1_tag2str(tag)); } ASN1_CALL(pos, asn1_dec_length(pos, seq_end, &length)); ASN1_END_SETUP(op_seq_end, op_seq_offset, length, pos, seq_end); /* Decode operation-value */ ASN1_CALL(pos, asn1_dec_tag(pos, op_seq_end, &tag)); switch (tag) { case ASN1_TYPE_INTEGER: ASN1_CALL(pos, asn1_dec_int(ctrl, "operationValue", tag, pos, op_seq_end, &value)); local = value; oid.num_values = 0; break; case ASN1_TYPE_OBJECT_IDENTIFIER: ASN1_CALL(pos, asn1_dec_oid(ctrl, "operationValue", tag, pos, op_seq_end, &oid)); local = 0; break; default: ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); return NULL; } convert = rose_find_msg_by_op_val(ctrl, (oid.num_values == 0) ? NULL : &oid, local); if (convert) { msg->operation = convert->operation; } else { msg->operation = ROSE_Unknown; } if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " operationValue = %s\n", rose_operation2str(msg->operation)); } /* Decode any expected result arguments */ if (convert && convert->decode_result_args) { ASN1_CALL(pos, asn1_dec_tag(pos, op_seq_end, &tag)); ASN1_CALL(pos, convert->decode_result_args(ctrl, tag, pos, op_seq_end, &msg->args)); } ASN1_END_FIXUP(ctrl, pos, op_seq_offset, op_seq_end, seq_end); } else { msg->operation = ROSE_None; } ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \internal * \brief Decode the ROSE error message. * * \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 msg ROSE error message data to fill. * * \retval Start of the next ASN.1 component on success. * \retval NULL on error. */ static const unsigned char *rose_decode_error(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, struct rose_msg_error *msg) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; const struct rose_convert_error *convert; struct asn1_oid oid; unsigned local; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, "ERROR Component %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_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER); ASN1_CALL(pos, asn1_dec_int(ctrl, "invokeId", tag, pos, seq_end, &value)); msg->invoke_id = value; /* Decode error-value */ ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); switch (tag) { case ASN1_TYPE_INTEGER: ASN1_CALL(pos, asn1_dec_int(ctrl, "errorValue", tag, pos, seq_end, &value)); local = value; oid.num_values = 0; break; case ASN1_TYPE_OBJECT_IDENTIFIER: ASN1_CALL(pos, asn1_dec_oid(ctrl, "errorValue", tag, pos, seq_end, &oid)); local = 0; break; default: ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); return NULL; } convert = rose_find_error_by_op_val(ctrl, (oid.num_values == 0) ? NULL : &oid, local); if (convert) { msg->code = convert->code; } else { msg->code = ROSE_ERROR_Unknown; } if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " errorValue = %s\n", rose_error2str(msg->code)); } /* Decode any expected error parameters */ if (convert && convert->decode_error_args) { ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CALL(pos, convert->decode_error_args(ctrl, tag, pos, seq_end, &msg->args)); } ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \internal * \brief Decode the ROSE reject message. * * \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 msg ROSE reject message data to fill. * * \retval Start of the next ASN.1 component on success. * \retval NULL on error. */ static const unsigned char *rose_decode_reject(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, struct rose_msg_reject *msg) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, "REJECT Component %s\n", asn1_tag2str(tag)); } ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); /* Invoke ID choice */ ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); switch (tag) { case ASN1_TYPE_INTEGER: ASN1_CALL(pos, asn1_dec_int(ctrl, "invokeId", tag, pos, seq_end, &value)); msg->invoke_id = value; msg->invoke_id_present = 1; break; case ASN1_TYPE_NULL: ASN1_CALL(pos, asn1_dec_null(ctrl, "invokeId", tag, pos, seq_end)); msg->invoke_id_present = 0; break; default: ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); return NULL; } /* Problem choice */ ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); switch (tag) { case ASN1_CLASS_CONTEXT_SPECIFIC | 0: ASN1_CALL(pos, asn1_dec_int(ctrl, "problemGeneral", tag, pos, seq_end, &value)); msg->code = ROSE_REJECT_BASE(ROSE_REJECT_BASE_General) | (value & 0xFF); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 1: ASN1_CALL(pos, asn1_dec_int(ctrl, "problemInvoke", tag, pos, seq_end, &value)); msg->code = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Invoke) | (value & 0xFF); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 2: ASN1_CALL(pos, asn1_dec_int(ctrl, "problemResult", tag, pos, seq_end, &value)); msg->code = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Result) | (value & 0xFF); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 3: ASN1_CALL(pos, asn1_dec_int(ctrl, "problemError", tag, pos, seq_end, &value)); msg->code = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Error) | (value & 0xFF); break; default: ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); return NULL; } if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " problem = %s\n", rose_reject2str(msg->code)); } ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the ROSE message into the given buffer. * * \param ctrl D channel controller for diagnostic messages or global options. * \param pos Starting position of the ASN.1 component. * \param end End of ASN.1 decoding data buffer. * \param msg Decoded ROSE message contents. * * \retval Start of the next ASN.1 component on success. * \retval NULL on error. * * \note This function only decodes the ROSE contents. It does not check * for the protocol profile, NFE, NPP, and interpretation octets defined in * a facility ie that may preceed the ROSE contents. These header octets * may already have been consumed from the encompasing buffer before the * buffer given here. */ const unsigned char *rose_decode(struct pri *ctrl, const unsigned char *pos, const unsigned char *end, struct rose_message *msg) { unsigned tag; ASN1_CALL(pos, asn1_dec_tag(pos, end, &tag)); switch (tag) { case ROSE_TAG_COMPONENT_INVOKE: msg->type = ROSE_COMP_TYPE_INVOKE; ASN1_CALL(pos, rose_decode_invoke(ctrl, tag, pos, end, &msg->component.invoke)); break; case ROSE_TAG_COMPONENT_RESULT: msg->type = ROSE_COMP_TYPE_RESULT; ASN1_CALL(pos, rose_decode_result(ctrl, tag, pos, end, &msg->component.result)); break; case ROSE_TAG_COMPONENT_ERROR: msg->type = ROSE_COMP_TYPE_ERROR; ASN1_CALL(pos, rose_decode_error(ctrl, tag, pos, end, &msg->component.error)); break; case ROSE_TAG_COMPONENT_REJECT: msg->type = ROSE_COMP_TYPE_REJECT; ASN1_CALL(pos, rose_decode_reject(ctrl, tag, pos, end, &msg->component.reject)); break; default: msg->type = ROSE_COMP_TYPE_INVALID; ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); return NULL; } return pos; } /*! * \internal * \brief Decode the NetworkFacilityExtension 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 nfe Parameter storage to fill. * * \retval Start of the next ASN.1 component on success. * \retval NULL on error. */ static const unsigned char *fac_dec_nfe(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct facNetworkFacilityExtension *nfe) { int length; int seq_offset; int explicit_offset; const unsigned char *seq_end; const unsigned char *explicit_end; const unsigned char *save_pos; int32_t value; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s NetworkFacilityExtension %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_CLASS_CONTEXT_SPECIFIC | 0); ASN1_CALL(pos, asn1_dec_int(ctrl, "sourceEntity", tag, pos, seq_end, &value)); nfe->source_entity = value; ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); if (tag == (ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1)) { /* 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_PartyNumber(ctrl, "sourceEntityAddress", tag, pos, seq_end, &nfe->source_number)); ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); } else { nfe->source_number.length = 0; } ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 2); ASN1_CALL(pos, asn1_dec_int(ctrl, "destinationEntity", tag, pos, seq_end, &value)); nfe->destination_entity = value; nfe->destination_number.length = 0; if (pos < seq_end && *pos != ASN1_INDEF_TERM) { save_pos = pos; ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); if (tag == (ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 3)) { /* 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_PartyNumber(ctrl, "destinationEntityAddress", tag, pos, seq_end, &nfe->destination_number)); ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); } else { pos = save_pos; } } ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the extension header argument parameters. * * \param ctrl D channel controller for any diagnostic messages. * \param pos Starting position of the ASN.1 component. * \param end End of ASN.1 decoding data buffer. * \param header Parameter storage to fill. * * \retval Start of the next ASN.1 component on success. * \retval NULL on error. */ const unsigned char *fac_dec_extension_header(struct pri *ctrl, const unsigned char *pos, const unsigned char *end, struct fac_extension_header *header) { int32_t value; unsigned tag; const unsigned char *save_pos; /* * For simplicity we are not checking the order of * the optional header components. */ header->nfe_present = 0; header->npp_present = 0; header->interpretation_present = 0; while (pos < end) { save_pos = pos; ASN1_CALL(pos, asn1_dec_tag(pos, end, &tag)); switch (tag) { case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 10: ASN1_CALL(pos, fac_dec_nfe(ctrl, "nfe", tag, pos, end, &header->nfe)); header->nfe_present = 1; break; case ASN1_CLASS_CONTEXT_SPECIFIC | 18: ASN1_CALL(pos, asn1_dec_int(ctrl, "networkProtocolProfile", tag, pos, end, &value)); header->npp = value; header->npp_present = 1; break; case ASN1_CLASS_CONTEXT_SPECIFIC | 11: ASN1_CALL(pos, asn1_dec_int(ctrl, "interpretation", tag, pos, end, &value)); header->interpretation = value; header->interpretation_present = 1; break; default: pos = save_pos; goto cancel_options; } } cancel_options:; return pos; } /*! * \brief Decode the facility ie contents header. * * \param ctrl D channel controller for any diagnostic messages. * \param pos Starting position of the facility ie contents. * \param end End of facility ie contents. * \param header Parameter storage to fill. * * \retval Start of the next ASN.1 component on success (ROSE message). * \retval NULL on error. */ const unsigned char *facility_decode_header(struct pri *ctrl, const unsigned char *pos, const unsigned char *end, struct fac_extension_header *header) { /* Make sure we have enough room for the protocol profile ie octet(s) */ if (end < pos + 2) { return NULL; } switch (*pos & Q932_PROTOCOL_MASK) { case Q932_PROTOCOL_ROSE: case Q932_PROTOCOL_EXTENSIONS: break; default: return NULL; } if (!(*pos & 0x80)) { /* DMS-100 Service indicator octet - Just ignore for now */ ++pos; } ++pos; if (ctrl->debug & PRI_DEBUG_APDU) { asn1_dump(ctrl, pos, end); } pos = fac_dec_extension_header(ctrl, pos, end, header); return pos; } /*! * \brief Decode the facility ie contents for debug purposes. * * \param ctrl D channel controller for any diagnostic messages. * \param buf Buffer containing the facility ie contents. * \param length Length of facility ie contents. * * \return Nothing * * \note Should only be called if PRI_DEBUG_APDU is enabled. Otherwise, * it it does nothing useful. */ void facility_decode_dump(struct pri *ctrl, const unsigned char *buf, size_t length) { const unsigned char *pos; const unsigned char *end; union { struct fac_extension_header header; struct rose_message rose; } discard; end = buf + length; pos = facility_decode_header(ctrl, buf, end, &discard.header); while (pos && pos < end) { pos = rose_decode(ctrl, pos, end, &discard.rose); } } /* ------------------------------------------------------------------- */ /* end rose.c */ libpri-1.4.14/pri_cc.c0000644000000000000000000070541611733150173013201 0ustar rootroot/* * 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; 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; 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; 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; 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->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->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; 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); 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; 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->ctrl = 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 (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; 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; msg.args.etsi.CCBSInterrogate.recall_mode = ctrl->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 = ctrl->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 (!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 (!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 (!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 = PRES_NUMBER_NOT_AVAILABLE; } } 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_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; } if (!ctrl->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(ctrl, &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 = ctrl->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); } /*! * \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. */ 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; } /*! * \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. */ 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_INTERNAL_CLEARING: str = "CC_EVENT_INTERNAL_CLEARING"; 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->ctrl, 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->ctrl, 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->ctrl, 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->ctrl, 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->ctrl, 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->ctrl, 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->ctrl, 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_INTERNAL_CLEARING: pri_cc_act_release_link_id(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: 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_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_INTERNAL_CLEARING: pri_cc_act_stop_t_retention(ctrl, cc_record); pri_cc_act_start_t_retention(ctrl, cc_record); 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_INTERNAL_CLEARING: 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 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_INTERNAL_CLEARING: pri_cc_act_stop_t_retention(ctrl, cc_record); pri_cc_act_start_t_retention(ctrl, cc_record); 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_INTERNAL_CLEARING: pri_cc_act_stop_t_retention(ctrl, cc_record); pri_cc_act_start_t_retention(ctrl, cc_record); 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 || !pri_is_call_valid(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 = ctrl->link.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 (!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 || !pri_is_call_valid(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 */ libpri-1.4.14/asn1_primitive.c0000644000000000000000000010631411211577176014673 0ustar rootroot/* * 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 ASN.1 BER encode/decode primitives * * \author Richard Mudgett */ #include #include #include "compat.h" #include "libpri.h" #include "pri_internal.h" #include "asn1.h" /* ------------------------------------------------------------------- */ /*! * \internal * \brief Dump the memory contents indicated in printable characters. (Helper function.) * * \param ctrl D channel controller for any diagnostic messages. * \param start Dump memory starting position. * \param end Dump memory ending position. (Not included in dump.) * * \return Nothing */ static void asn1_dump_mem_helper(struct pri *ctrl, const unsigned char *start, const unsigned char *end) { pri_message(ctrl, " - \""); for (; start < end; ++start) { pri_message(ctrl, "%c", (isprint(*start)) ? *start : '~'); } pri_message(ctrl, "\"\n"); } /*! * \internal * \brief Dump the memory contents indicated. * * \param ctrl D channel controller for any diagnostic messages. * \param indent Number of spaces to indent for each new memory dump line. * \param pos Dump memory starting position. * \param length Number of bytes to dump. * * \return Nothing */ static void asn1_dump_mem(struct pri *ctrl, unsigned indent, const unsigned char *pos, unsigned length) { const unsigned char *seg_start; const unsigned char *end; unsigned delimiter; unsigned count; seg_start = pos; end = pos + length; if (pos < end) { delimiter = '<'; for (;;) { pri_message(ctrl, "%*s", indent, ""); for (count = 0; count++ < 16 && pos < end;) { pri_message(ctrl, "%c%02X", delimiter, *pos++); delimiter = (count == 8) ? '-' : ' '; } if (end <= pos) { break; } asn1_dump_mem_helper(ctrl, seg_start, pos); seg_start = pos; } } else { pri_message(ctrl, "%*s<", indent, ""); } pri_message(ctrl, ">"); asn1_dump_mem_helper(ctrl, seg_start, end); } /*! * \brief Convert the given tag value to a descriptive string. * * \param tag Component tag value to convert to a string. * * \return Converted tag string. */ const char *asn1_tag2str(unsigned tag) { static const char *primitives[32] = { [ASN1_TYPE_INDEF_TERM] = "Indefinite length terminator", [ASN1_TYPE_BOOLEAN] = "Boolean", [ASN1_TYPE_INTEGER] = "Integer", [ASN1_TYPE_BIT_STRING] = "Bit String", [ASN1_TYPE_OCTET_STRING] = "Octet String", [ASN1_TYPE_NULL] = "NULL", [ASN1_TYPE_OBJECT_IDENTIFIER] = "OID", [ASN1_TYPE_OBJECT_DESCRIPTOR] = "Object Descriptor", [ASN1_TYPE_EXTERN] = "External", [ASN1_TYPE_REAL] = "Real", [ASN1_TYPE_ENUMERATED] = "Enumerated", [ASN1_TYPE_EMBEDDED_PDV] = "Embedded PDV", [ASN1_TYPE_UTF8_STRING] = "UTF8 String", [ASN1_TYPE_RELATIVE_OID] = "Relative OID", [ASN1_TYPE_SEQUENCE] = "Sequence", [ASN1_TYPE_SET] = "Set", [ASN1_TYPE_NUMERIC_STRING] = "Numeric String", [ASN1_TYPE_PRINTABLE_STRING] = "Printable String", [ASN1_TYPE_TELETEX_STRING] = "Teletex String", [ASN1_TYPE_VIDEOTEX_STRING] = "Videotex String", [ASN1_TYPE_IA5_STRING] = "IA5 String", [ASN1_TYPE_UTC_TIME] = "UTC Time", [ASN1_TYPE_GENERALIZED_TIME] = "Generalized Time", [ASN1_TYPE_GRAPHIC_STRING] = "Graphic String", [ASN1_TYPE_VISIBLE_STRING] = "Visible/ISO646 String", [ASN1_TYPE_GENERAL_STRING] = "General String", [ASN1_TYPE_UNIVERSAL_STRING] = "Universal String", [ASN1_TYPE_CHAR_STRING] = "Character String", [ASN1_TYPE_BMP_STRING] = "BMP String", [ASN1_TYPE_EXTENSION] = "Type Extension", }; static char buf[64]; const char *description; unsigned asn1_constructed; /*! TRUE if the tag is constructed. */ unsigned asn1_type; asn1_constructed = ((tag & ASN1_PC_MASK) == ASN1_PC_CONSTRUCTED); asn1_type = tag & ASN1_TYPE_MASK; switch (tag & ASN1_CLASS_MASK) { case ASN1_CLASS_UNIVERSAL: if (tag == (ASN1_CLASS_UNIVERSAL | ASN1_PC_CONSTRUCTED | ASN1_TYPE_INDEF_TERM)) { description = NULL; } else { description = primitives[asn1_type]; } if (!description) { description = "Reserved"; } snprintf(buf, sizeof(buf), "%s%s(%u 0x%02X)", description, asn1_constructed ? "/C" : "", tag, tag); return buf; case ASN1_CLASS_APPLICATION: description = "Application"; break; case ASN1_CLASS_CONTEXT_SPECIFIC: description = "Context Specific"; break; case ASN1_CLASS_PRIVATE: description = "Private"; break; default: snprintf(buf, sizeof(buf), "Unknown tag (%u 0x%02X)", tag, tag); return buf; } snprintf(buf, sizeof(buf), "%s%s [%u 0x%02X]", description, asn1_constructed ? "/C" : "", asn1_type, asn1_type); return buf; } /*! * \brief Decode the ASN.1 tag value. * * \param tag_pos ASN.1 tag starting position. * \param end End of ASN.1 encoded data buffer. * \param tag Decoded tag value returned on success. * * \retval Next octet after the tag on success. * \retval NULL on error. */ const unsigned char *asn1_dec_tag(const unsigned char *tag_pos, const unsigned char *end, unsigned *tag) { unsigned extended_tag; if (end <= tag_pos) { return NULL; } *tag = *tag_pos++; if ((*tag & ASN1_TYPE_MASK) == ASN1_TYPE_EXTENSION) { /* Extract the extended tag value */ extended_tag = 0; do { if (end <= tag_pos) { return NULL; } extended_tag <<= 7; extended_tag |= *tag_pos & ~0x80; } while (*tag_pos++ & 0x80); if (extended_tag && extended_tag < ASN1_TYPE_EXTENSION) { /* * The sender did not need to use the extended format. * This is an encoding error on their part, but we will * accept it anyway. * * Note we cannot return a null tag value from this path. * We would misinterpret the indefinite length * terminator. */ *tag &= ~ASN1_TYPE_MASK; *tag |= extended_tag; } } return tag_pos; } /*! * \brief Decode the length of an ASN.1 component length. * * \param len_pos Starting position of the ASN.1 component length. * \param end End of ASN.1 decoding data buffer. * \param length Decoded length value returned on success. (-1 if indefinite) * * \retval Next octet after the length on success. * \retval NULL on error. * * \note The decoded length is checked to see if there is enough buffer * left for the component body. */ const unsigned char *asn1_dec_length(const unsigned char *len_pos, const unsigned char *end, int *length) { unsigned length_size; if (end <= len_pos) { /* Not enough buffer to determine how the length is encoded */ return NULL; } if (*len_pos < 0x80) { /* Short length encoding */ *length = *len_pos++; } else if (*len_pos == 0x80) { /* Indefinite length encoding */ *length = -1; ++len_pos; if (end < len_pos + ASN1_INDEF_TERM_LEN) { /* Not enough buffer for the indefinite length terminator */ return NULL; } return len_pos; } else { /* Long length encoding */ length_size = *len_pos++ & 0x7f; if (length_size == 0x7f) { /* Reserved extension encoding that has not been defined. */ return NULL; } if (end < len_pos + length_size) { /* Not enough buffer for the length value */ return NULL; } *length = 0; while (length_size--) { *length = (*length << 8) | *len_pos++; } } if (end < len_pos + *length) { /* Not enough buffer for the component body. */ return NULL; } return len_pos; } /*! * \internal * \brief Skip to the end of an indefinite length constructed component helper. * * \param pos ASN.1 tag starting position. * \param end End of ASN.1 decoding data buffer. * * \retval Start of the next ASN.1 component on success. * \retval NULL on error. */ static const unsigned char *asn1_dec_indef_end_fixup_helper(const unsigned char *pos, const unsigned char *end) { unsigned tag; int length; while (pos < end && *pos != ASN1_INDEF_TERM) { ASN1_CALL(pos, asn1_dec_tag(pos, end, &tag)); ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); if (length < 0) { /* Skip over indefinite length sub-component */ if ((tag & ASN1_PC_MASK) == ASN1_PC_CONSTRUCTED || tag == (ASN1_CLASS_UNIVERSAL | ASN1_PC_PRIMITIVE | ASN1_TYPE_SET) || tag == (ASN1_CLASS_UNIVERSAL | ASN1_PC_PRIMITIVE | ASN1_TYPE_SEQUENCE)) { /* This is an ITU encoded indefinite length component. */ ASN1_CALL(pos, asn1_dec_indef_end_fixup_helper(pos, end)); } else { /* This is a non-ITU encoded indefinite length component. */ while (pos < end && *pos != ASN1_INDEF_TERM) { ++pos; } pos += ASN1_INDEF_TERM_LEN; } } else { /* Skip over defininte length sub-component */ pos += length; } } if (end < pos + ASN1_INDEF_TERM_LEN) { return NULL; } return pos + ASN1_INDEF_TERM_LEN; } /*! * \brief Skip to the end of an indefinite length constructed component. * * \param ctrl D channel controller for any diagnostic messages. * \param pos ASN.1 tag starting position. * \param end End of ASN.1 decoding data buffer. * * \retval Start of the next ASN.1 component on success. * \retval NULL on error. */ const unsigned char *asn1_dec_indef_end_fixup(struct pri *ctrl, const unsigned char *pos, const unsigned char *end) { if (pos < end && *pos != ASN1_INDEF_TERM && (ctrl->debug & PRI_DEBUG_APDU)) { pri_message(ctrl, " Skipping unused indefinite length constructed component octets!\n"); } return asn1_dec_indef_end_fixup_helper(pos, end); } /*! * \brief Decode the boolean primitive. * * \param ctrl D channel controller for any diagnostic messages. * \param name Field name * \param tag Component tag that identified this primitive. * \param pos Starting position of the ASN.1 component length. * \param end End of ASN.1 decoding data buffer. * \param value Decoded boolean value. * * \retval Start of the next ASN.1 component on success. * \retval NULL on error. */ const unsigned char *asn1_dec_boolean(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, int32_t *value) { int length; ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); if (length != 1) { /* * The encoding rules say the length can only be one. * It is rediculus to get anything else anyway. */ return NULL; } *value = *pos++ ? 1 : 0; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s %s = %d\n", name, asn1_tag2str(tag), *value); } return pos; } /*! * \brief Decode the integer type primitive. * * \param ctrl D channel controller for any diagnostic messages. * \param name Field name * \param tag Component tag that identified this primitive. * \param pos Starting position of the ASN.1 component length. * \param end End of ASN.1 decoding data buffer. * \param value Decoded integer type value. * * \retval Start of the next ASN.1 component on success. * \retval NULL on error. */ const unsigned char *asn1_dec_int(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, int32_t *value) { int length; ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); if (length <= 0) { /* * The encoding rules say the length can not be indefinite. * It cannot be empty for that matter either. */ return NULL; } #if 1 /* Read value as signed */ if (*pos & 0x80) { /* The value is negative */ *value = -1; } else { *value = 0; } #else /* Read value as unsigned */ *value = 0; #endif while (length--) { *value = (*value << 8) | *pos; pos++; } if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s %s = %d 0x%04X\n", name, asn1_tag2str(tag), *value, *value); } return pos; } /*! * \brief Decode the null primitive. * * \param ctrl D channel controller for any diagnostic messages. * \param name Field name * \param tag Component tag that identified this primitive. * \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. */ const unsigned char *asn1_dec_null(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end) { int length; ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); if (length != 0) { /* * The encoding rules say the length can only be zero. * It is rediculus to get anything else anyway. */ return NULL; } if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s %s\n", name, asn1_tag2str(tag)); } return pos; } /*! * \brief Decode the object identifier primitive. * * \param ctrl D channel controller for any diagnostic messages. * \param name Field name * \param tag Component tag that identified this primitive. * \param pos Starting position of the ASN.1 component length. * \param end End of ASN.1 decoding data buffer. * \param oid Decoded OID type value. * * \retval Start of the next ASN.1 component on success. * \retval NULL on error. */ const unsigned char *asn1_dec_oid(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct asn1_oid *oid) { int length; unsigned num_values; unsigned value; unsigned delimiter; ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); if (length < 0) { /* * The encoding rules say the length can not be indefinite. */ return NULL; } if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s %s =", name, asn1_tag2str(tag)); } delimiter = ' '; num_values = 0; while (length) { value = 0; for (;;) { --length; value = (value << 7) | (*pos & 0x7F); if (!(*pos++ & 0x80)) { /* Last octet in the OID subidentifier value */ if (num_values < ARRAY_LEN(oid->value)) { oid->value[num_values] = value; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, "%c%u", delimiter, value); } delimiter = '.'; } else { /* Too many OID subidentifier values */ delimiter = '~'; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, "%c%u", delimiter, value); } } ++num_values; break; } if (!length) { oid->num_values = 0; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, "\n" " Last OID subidentifier value not terminated!\n"); } return NULL; } } } if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, "\n"); } if (num_values <= ARRAY_LEN(oid->value)) { oid->num_values = num_values; return pos; } else { /* Need to increase the size of the OID subidentifier list. */ oid->num_values = 0; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " Too many OID values!\n"); } return NULL; } } /*! * \brief Decode a binary string primitive. * * \param ctrl D channel controller for any diagnostic messages. * \param name Field name * \param tag Component tag that identified this primitive. * \param pos Starting position of the ASN.1 component length. * \param end End of ASN.1 decoding data buffer. * \param buf_size Size of the supplied string buffer. (Must be nonzero) * \param str Where to put the decoded string. * \param str_len Length of the decoded string. * * \note The string will be null terminated just in case. * The buffer needs to have enough room for a null terminator. * \note The parse will fail if the parsed string is too large for * the supplied buffer. * * \retval Start of the next ASN.1 component on success. * \retval NULL on error. */ const unsigned char *asn1_dec_string_bin(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, size_t buf_size, unsigned char *str, size_t *str_len) { int length; size_t sub_buf_size; size_t sub_str_len; unsigned char *sub_str; ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); if (length < 0) { /* This is an indefinite length string */ if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s %s = Indefinite length string\n", name, asn1_tag2str(tag)); } if ((tag & ASN1_PC_MASK) == ASN1_PC_CONSTRUCTED) { /* * This is an ITU encoded indefinite length string * and could contain null. */ /* Ensure that an empty string is null terminated. */ *str = 0; /* Collect all substrings into the original string buffer. */ *str_len = 0; sub_str = str; sub_buf_size = buf_size; for (;;) { ASN1_CALL(pos, asn1_dec_tag(pos, end, &tag)); if (tag == ASN1_INDEF_TERM) { /* End-of-contents octets */ break; } /* Append the substring to the accumulated indefinite string. */ ASN1_CALL(pos, asn1_dec_string_bin(ctrl, name, tag, pos, end, sub_buf_size, sub_str, &sub_str_len)); sub_buf_size -= sub_str_len; sub_str += sub_str_len; *str_len += sub_str_len; } } else { /* * This is a non-ITU encoded indefinite length string * and must not contain null's. */ for (length = 0;; ++length) { if (end <= pos + length) { /* Not enough buffer left */ return NULL; } if (pos[length] == 0) { /* Found End-of-contents octets */ break; } } if (buf_size - 1 < length) { /* The destination buffer is not large enough for the data */ if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " String buffer not large enough!\n"); } return NULL; } /* Extract the string and null terminate it. */ memcpy(str, pos, length); str[length] = 0; *str_len = length; pos += length + 1; } if (end <= pos) { /* Not enough buffer left for End-of-contents octets */ return NULL; } if (*pos++ != 0) { /* We actually did not find the End-of-contents octets. */ return NULL; } if (ctrl->debug & PRI_DEBUG_APDU) { /* Dump the collected string buffer contents. */ pri_message(ctrl, " Completed string =\n"); asn1_dump_mem(ctrl, 6, str, *str_len); } } else { /* This is a definite length string */ if (buf_size - 1 < length) { /* The destination buffer is not large enough for the data */ if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s %s = Buffer not large enough!\n", name, asn1_tag2str(tag)); } return NULL; } /* Extract the string and null terminate it. */ memcpy(str, pos, length); str[length] = 0; *str_len = length; pos += length; if (ctrl->debug & PRI_DEBUG_APDU) { /* Dump the collected string buffer contents. */ pri_message(ctrl, " %s %s =\n", name, asn1_tag2str(tag)); asn1_dump_mem(ctrl, 4, str, *str_len); } } return pos; } /*! * \brief Decode a string that can be truncated to a maximum length primitive. * * \param ctrl D channel controller for any diagnostic messages. * \param name Field name * \param tag Component tag that identified this primitive. * \param pos Starting position of the ASN.1 component length. * \param end End of ASN.1 decoding data buffer. * \param buf_size Size of the supplied string buffer. (Must be nonzero) * \param str Where to put the decoded null terminated string. * \param str_len Length of the decoded string. * (computed for convenience since you could just do a strlen()) * * \note The parsed string will be truncated if the string buffer * cannot contain it. * * \retval Start of the next ASN.1 component on success. * \retval NULL on error. */ const unsigned char *asn1_dec_string_max(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, size_t buf_size, unsigned char *str, size_t *str_len) { int length; size_t str_length; size_t sub_buf_size; size_t sub_str_len; unsigned char *sub_str; ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); if (length < 0) { /* This is an indefinite length string */ if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s %s = Indefinite length string\n", name, asn1_tag2str(tag)); } if ((tag & ASN1_PC_MASK) == ASN1_PC_CONSTRUCTED) { /* This is an ITU encoded indefinite length string. */ /* Ensure that an empty string is null terminated. */ *str = 0; /* Collect all substrings into the original string buffer. */ *str_len = 0; sub_str = str; sub_buf_size = buf_size; for (;;) { ASN1_CALL(pos, asn1_dec_tag(pos, end, &tag)); if (tag == ASN1_INDEF_TERM) { /* End-of-contents octets */ break; } /* Append the substring to the accumulated indefinite string. */ ASN1_CALL(pos, asn1_dec_string_max(ctrl, name, tag, pos, end, sub_buf_size, sub_str, &sub_str_len)); sub_buf_size -= sub_str_len; sub_str += sub_str_len; *str_len += sub_str_len; } } else { /* This is a non-ITU encoded indefinite length string. */ for (length = 0;; ++length) { if (end <= pos + length) { /* Not enough buffer left */ return NULL; } if (pos[length] == 0) { /* Found End-of-contents octets */ break; } } /* Extract the string, truncate if necessary, and terminate it. */ str_length = (buf_size - 1 < length) ? buf_size - 1 : length; memcpy(str, pos, str_length); str[str_length] = 0; *str_len = str_length; pos += length + 1; } if (end <= pos) { /* Not enough buffer left for End-of-contents octets */ return NULL; } if (*pos++ != 0) { /* We actually did not find the End-of-contents octets. */ return NULL; } if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " Completed string = \"%s\"\n", str); } } else { /* * This is a definite length string * * Extract the string, truncate if necessary, and terminate it. */ str_length = (buf_size - 1 < length) ? buf_size - 1 : length; memcpy(str, pos, str_length); str[str_length] = 0; *str_len = str_length; pos += length; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s %s = \"%s\"\n", name, asn1_tag2str(tag), str); } } return pos; } /*! * \internal * \brief Recursive ASN.1 buffer decoding dump helper. * * \param ctrl D channel controller for any diagnostic messages. * \param pos ASN.1 tag starting position. * \param end End of ASN.1 decoding data buffer. * \param level Indentation level to use. * \param indefinite_term TRUE if to stop on an indefinite length terminator. * * \retval Start of the next ASN.1 component on success. * \retval NULL on error. */ static const unsigned char *asn1_dump_helper(struct pri *ctrl, const unsigned char *pos, const unsigned char *end, unsigned level, unsigned indefinite_term) { unsigned delimiter; unsigned tag; int length; const unsigned char *len_pos; while (pos < end && (!indefinite_term || *pos != ASN1_INDEF_TERM)) { /* Decode the tag */ pri_message(ctrl, "%*s", 2 * level, ""); len_pos = asn1_dec_tag(pos, end, &tag); if (!len_pos) { pri_message(ctrl, "Invalid tag encoding!\n"); return NULL; } /* Dump the tag contents. */ pri_message(ctrl, "%s ", asn1_tag2str(tag)); delimiter = '<'; while (pos < len_pos) { pri_message(ctrl, "%c%02X", delimiter, *pos); delimiter = ' '; ++pos; } pri_message(ctrl, "> "); /* Decode the length */ pos = asn1_dec_length(len_pos, end, &length); if (!pos) { pri_message(ctrl, "Invalid length encoding!\n"); return NULL; } /* Dump the length contents. */ if (length < 0) { pri_message(ctrl, "Indefinite length "); } else { pri_message(ctrl, "Len:%d ", length); } delimiter = '<'; while (len_pos < pos) { pri_message(ctrl, "%c%02X", delimiter, *len_pos); delimiter = ' '; ++len_pos; } pri_message(ctrl, ">\n"); /* Dump the body contents */ ++level; if (length < 0) { /* Indefinite length */ if ((tag & ASN1_PC_MASK) == ASN1_PC_CONSTRUCTED) { /* This is an ITU encoded indefinite length component. */ ASN1_CALL(pos, asn1_dump_helper(ctrl, pos, end, level, 1)); } else if (tag == (ASN1_CLASS_UNIVERSAL | ASN1_PC_PRIMITIVE | ASN1_TYPE_SET) || tag == (ASN1_CLASS_UNIVERSAL | ASN1_PC_PRIMITIVE | ASN1_TYPE_SEQUENCE)) { pri_message(ctrl, "%*sThis tag must always be constructed!\n", 2 * level, ""); /* Assume tag was constructed to keep going */ ASN1_CALL(pos, asn1_dump_helper(ctrl, pos, end, level, 1)); } else { /* This is a non-ITU encoded indefinite length component. */ pri_message(ctrl, "%*sNon-ITU indefininte length component.\n", 2 * level, ""); length = 0; while (pos + length < end && pos[length] != ASN1_INDEF_TERM) { ++length; } if (length) { asn1_dump_mem(ctrl, 2 * level, pos, length); pos += length; } } --level; if (end < pos + ASN1_INDEF_TERM_LEN) { pri_message(ctrl, "%*sNot enough room for the End-of-contents octets!\n", 2 * level, ""); pos = end; } else { pri_message(ctrl, "%*sEnd-of-contents <%02X %02X>%s\n", 2 * level, "", pos[0], pos[1], (pos[1] != 0) ? " Invalid!" : ""); pos += ASN1_INDEF_TERM_LEN; } } else { /* Defininte length */ if ((tag & ASN1_PC_MASK) == ASN1_PC_CONSTRUCTED) { /* Dump constructed contents */ ASN1_CALL(pos, asn1_dump_helper(ctrl, pos, pos + length, level, 0)); } else if (tag == (ASN1_CLASS_UNIVERSAL | ASN1_PC_PRIMITIVE | ASN1_TYPE_SET) || tag == (ASN1_CLASS_UNIVERSAL | ASN1_PC_PRIMITIVE | ASN1_TYPE_SEQUENCE)) { /* Assume tag was constructed to keep going */ pri_message(ctrl, "%*sThis tag must always be constructed!\n", 2 * level, ""); ASN1_CALL(pos, asn1_dump_helper(ctrl, pos, pos + length, level, 0)); } else if (0 < length) { /* Dump primitive contents. */ asn1_dump_mem(ctrl, 2 * level, pos, length); pos += length; } --level; } #if 0 pri_message(ctrl, "%*sEnd\n", 2 * level, ""); #endif } return pos; } /*! * \brief Dump the given ASN.1 buffer contents. * * \param ctrl D channel controller for any diagnostic messages. * \param start_asn1 First octet in the ASN.1 buffer. (ASN.1 tag starting position) * \param end One beyond the last octet in the ASN.1 buffer. * * \return Nothing */ void asn1_dump(struct pri *ctrl, const unsigned char *start_asn1, const unsigned char *end) { pri_message(ctrl, "ASN.1 dump\n"); if (start_asn1) { asn1_dump_helper(ctrl, start_asn1, end, 1, 0); } pri_message(ctrl, "ASN.1 end\n"); } /*! * \brief Encode the length of an ASN.1 component body of predetermined size. * * \param len_pos Starting position of the ASN.1 component length. * \param end End of ASN.1 encoding data buffer. * \param length Predetermined component body length. * * \note The encoding buffer does not need to be checked after calling. * It is already checked to have the requested room. * * \retval Next octet after the length on success. * \retval NULL on error. */ unsigned char *asn1_enc_length(unsigned char *len_pos, unsigned char *end, size_t length) { u_int32_t body_length; /* Length of component contents */ u_int32_t value; u_int32_t test_mask; unsigned length_size; /* Length of the length encoding */ body_length = length; /* Determine length encoding length */ if (body_length < 128) { length_size = 1; } else { /* Find most significant octet of 32 bit integer that carries meaning. */ test_mask = 0xFF000000; for (length_size = 4; --length_size;) { if (body_length & test_mask) { /* * Found the first 8 bits of a multiple octet length that * is not all zeroes. */ break; } test_mask >>= 8; } length_size += 1 + 1; } if (end < len_pos + length_size + body_length) { /* No room for the length and component body in the buffer */ return NULL; } /* Encode the component body length */ if (length_size == 1) { *len_pos++ = body_length; } else { *len_pos++ = 0x80 | --length_size; while (length_size--) { value = body_length; value >>= (8 * length_size); *len_pos++ = value & 0xFF; } } return len_pos; } /*! * \brief Encode the length of an already encoded ASN.1 component. * * \param len_pos Starting position of the ASN.1 component length. * \param component_end Next octet after the component body. * \param end End of ASN.1 encoding data buffer. * * \note The total component size could increase or decrease. * \note The component length field must have been initialized with * ASN1_LEN_INIT() or ASN1_CONSTRUCTED_BEGIN(). * * \retval Next octet after the component body on success. * \retval NULL on error. */ unsigned char *asn1_enc_length_fixup(unsigned char *len_pos, unsigned char *component_end, unsigned char *end) { u_int32_t body_length; /* Length of component contents */ u_int32_t value; u_int32_t test_mask; unsigned length_size; /* Length of the length encoding */ if (component_end < len_pos + *len_pos) { /* Sanity check */ return NULL; } body_length = component_end - len_pos - *len_pos; /* Determine length encoding length */ if (body_length < 128) { length_size = 1; } else { /* Find most significant octet of 32 bit integer that carries meaning. */ test_mask = 0xFF000000; for (length_size = 4; --length_size;) { if (body_length & test_mask) { /* * Found the first 8 bits of a multiple octet length that * is not all zeroes. */ break; } test_mask >>= 8; } length_size += 1 + 1; } component_end = len_pos + length_size + body_length; if (end < component_end) { /* No room for the component in the buffer */ return NULL; } if (length_size != *len_pos) { /* Must shift the component body */ memmove(len_pos + length_size, len_pos + *len_pos, body_length); } /* Encode the component body length */ if (length_size == 1) { *len_pos = body_length; } else { *len_pos++ = 0x80 | --length_size; while (length_size--) { value = body_length; value >>= (8 * length_size); *len_pos++ = value & 0xFF; } } return component_end; } /*! * \brief Encode the boolean primitive. * * \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. * \param value Component value to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ unsigned char *asn1_enc_boolean(unsigned char *pos, unsigned char *end, unsigned tag, int32_t value) { if (end < pos + 3) { /* No room for the component in the buffer */ return NULL; } /* Encode component */ *pos++ = tag; *pos++ = 1; *pos++ = value ? 1 : 0; return pos; } /*! * \brief Encode the integer type primitive. * * \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. * \param value Component value to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ unsigned char *asn1_enc_int(unsigned char *pos, unsigned char *end, unsigned tag, int32_t value) { unsigned count; u_int32_t test_mask; u_int32_t val; /* Find most significant octet of 32 bit integer that carries meaning. */ test_mask = 0xFF800000; val = (u_int32_t) value; for (count = 4; --count;) { if ((val & test_mask) != test_mask && (val & test_mask) != 0) { /* * The first 9 bits of a multiple octet integer is not * all ones or zeroes. */ break; } test_mask >>= 8; } if (end < pos + 3 + count) { /* No room for the component in the buffer */ return NULL; } /* Encode component */ *pos++ = tag; *pos++ = count + 1; do { val = (u_int32_t) value; val >>= (8 * count); *pos++ = val & 0xFF; } while (count--); return pos; } /*! * \brief Encode the null type primitive. * * \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. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ unsigned char *asn1_enc_null(unsigned char *pos, unsigned char *end, unsigned tag) { if (end < pos + 2) { /* No room for the component in the buffer */ return NULL; } /* Encode component */ *pos++ = tag; *pos++ = 0; return pos; } /*! * \brief Encode the object identifier (OID) primitive. * * \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. * \param oid Component value to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ unsigned char *asn1_enc_oid(unsigned char *pos, unsigned char *end, unsigned tag, const struct asn1_oid *oid) { unsigned char *len_pos; unsigned num_values; unsigned count; u_int32_t value; if (end < pos + 2) { /* No room for the component tag and length in the buffer */ return NULL; } *pos++ = tag; len_pos = pos++; /* For all OID subidentifer values */ for (num_values = 0; num_values < oid->num_values; ++num_values) { /* * Count the number of 7 bit chunks that are needed * to encode the integer. */ value = oid->value[num_values] >> 7; for (count = 0; value; ++count) { /* There are bits still set */ value >>= 7; } if (end < pos + count + 1) { /* No room for the component body in the buffer */ return NULL; } /* Store OID subidentifier value */ do { value = oid->value[num_values]; value >>= (7 * count); *pos++ = (value & 0x7F) | (count ? 0x80 : 0); } while (count--); } /* length */ *len_pos = pos - len_pos - 1; return pos; } /*! * \brief Encode the binary string type primitive. * * \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. * \param str Binary string to encode. * \param str_len Length of binary string to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ unsigned char *asn1_enc_string_bin(unsigned char *pos, unsigned char *end, unsigned tag, const unsigned char *str, size_t str_len) { if (end < pos + 1) { /* No room for the component tag in the buffer */ return NULL; } /* Encode component */ *pos++ = tag; ASN1_CALL(pos, asn1_enc_length(pos, end, str_len)); memcpy(pos, str, str_len); return pos + str_len; } /*! * \brief Encode a string that can be truncated to a maximum length primitive. * * \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. * \param str Null terminated string to encode. * \param max_len Maximum length of string to encode. * * \note The string will be truncated if it is too long. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ unsigned char *asn1_enc_string_max(unsigned char *pos, unsigned char *end, unsigned tag, const unsigned char *str, size_t max_len) { size_t str_len; str_len = strlen((char *) str); if (max_len < str_len) { str_len = max_len; } return asn1_enc_string_bin(pos, end, tag, str, str_len); } /* ------------------------------------------------------------------- */ /* end asn1_primitive.c */ libpri-1.4.14/README0000644000000000000000000000323610374666137012461 0ustar rootrootlibpri: An implementation of Primary Rate ISDN Written by Mark Spencer What is libpri? =============== libpri is a C implementation of the Primary Rate ISDN specification. It was based on the Bellcore specification SR-NWT-002343 for National ISDN. As of May 12, 2001, it has been tested work with NI-2, Nortel DMS-100, and Lucent 5E Custom protocols on switches from Nortel and Lucent. What is the license for libpri? =============================== libpri is distributed under the terms of the GNU General Public License, which permit its use and linking with other GPL'd software only. The GNU GPL is included in the file LICENSE in this directory. As a special exception, libpri may also be linked to the OpenH323 library, so long as the entirity of the derivative work (as defined within the GPL) is licensed either under the MPL of the OpenH323 license or the GPL of libpri. If you wish to use libpri in an application for which the GPL is not appropriate (e.g. a proprietary embedded system), licenses for libpri under more flexible terms can be readily obtained through Digium, Inc. at reasonable cost. How do I report bugs or contribute? =================================== For now, contact the author directly. In the future if there is sufficient interest, we will setup a mailing list. Does anything use this library so far? ====================================== Yes, the Asterisk Open Source PBX does. http://www.asterisk.org Also, the Zapata library has hooks for it. http://www.zapatatelephony.org Special thanks ============== Special thanks to Jim Dixon for his help in testing and fixing the implementation. libpri-1.4.14/rose_qsig_mwi.c0000644000000000000000000006476611400035226014607 0ustar rootroot/* * 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-MWI-Operations * * SS-MWI-Operations ECMA-242 Annex E Table E.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 MsgCentreId 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 msg_centre_id * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *rose_enc_qsig_MsgCentreId(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct roseQsigMsgCentreId *msg_centre_id) { unsigned char *seq_len; switch (msg_centre_id->type) { case 0: /* integer */ ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0, msg_centre_id->u.integer)); break; case 1: /* partyNumber */ /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1); ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &msg_centre_id->u.number)); ASN1_CONSTRUCTED_END(seq_len, pos, end); break; case 2: /* numericString */ ASN1_CALL(pos, asn1_enc_string_max(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, msg_centre_id->u.str, sizeof(msg_centre_id->u.str) - 1)); break; default: ASN1_ENC_ERROR(ctrl, "Unknown MsgCentreId type"); return NULL; } return pos; } /*! * \brief Encode the MWIActivate 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_MWIActivate_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { const struct roseQsigMWIActivateArg *mwi_activate; unsigned char *seq_len; unsigned char *explicit_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); mwi_activate = &args->qsig.MWIActivate; ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &mwi_activate->served_user_number)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, mwi_activate->basic_service)); if (mwi_activate->msg_centre_id_present) { ASN1_CALL(pos, rose_enc_qsig_MsgCentreId(ctrl, pos, end, &mwi_activate->msg_centre_id)); } if (mwi_activate->number_of_messages_present) { ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3, mwi_activate->number_of_messages)); } if (mwi_activate->originating_number.length) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 4); ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &mwi_activate->originating_number)); ASN1_CONSTRUCTED_END(explicit_len, pos, end); } if (mwi_activate->timestamp_present) { ASN1_CALL(pos, asn1_enc_string_max(pos, end, ASN1_TYPE_GENERALIZED_TIME, mwi_activate->timestamp.str, sizeof(mwi_activate->timestamp.str) - 1)); } if (mwi_activate->priority_present) { ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 5, mwi_activate->priority)); } /* No extension to encode */ ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the MWIDeactivate 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_MWIDeactivate_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { const struct roseQsigMWIDeactivateArg *mwi_deactivate; unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); mwi_deactivate = &args->qsig.MWIDeactivate; ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &mwi_deactivate->served_user_number)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, mwi_deactivate->basic_service)); if (mwi_deactivate->msg_centre_id_present) { ASN1_CALL(pos, rose_enc_qsig_MsgCentreId(ctrl, pos, end, &mwi_deactivate->msg_centre_id)); } /* No extension to encode */ ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the MWIInterrogate 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_MWIInterrogate_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { const struct roseQsigMWIInterrogateArg *mwi_interrogate; unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); mwi_interrogate = &args->qsig.MWIInterrogate; ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &mwi_interrogate->served_user_number)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, mwi_interrogate->basic_service)); if (mwi_interrogate->msg_centre_id_present) { ASN1_CALL(pos, rose_enc_qsig_MsgCentreId(ctrl, pos, end, &mwi_interrogate->msg_centre_id)); } /* No extension to encode */ ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \internal * \brief Encode the MWIInterrogateResElt 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 record * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *rose_enc_qsig_MWIInterrogateResElt(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseQsigMWIInterrogateResElt *record) { unsigned char *seq_len; unsigned char *explicit_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, record->basic_service)); if (record->msg_centre_id_present) { ASN1_CALL(pos, rose_enc_qsig_MsgCentreId(ctrl, pos, end, &record->msg_centre_id)); } if (record->number_of_messages_present) { ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3, record->number_of_messages)); } if (record->originating_number.length) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 4); ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &record->originating_number)); ASN1_CONSTRUCTED_END(explicit_len, pos, end); } if (record->timestamp_present) { ASN1_CALL(pos, asn1_enc_string_max(pos, end, ASN1_TYPE_GENERALIZED_TIME, record->timestamp.str, sizeof(record->timestamp.str) - 1)); } if (record->priority_present) { ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 5, record->priority)); } /* No extension to encode */ ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the MWIInterrogate 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_MWIInterrogate_RES(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args) { unsigned index; unsigned char *seq_len; const struct roseQsigMWIInterrogateRes *mwi_interrogate; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); mwi_interrogate = &args->qsig.MWIInterrogate; for (index = 0; index < mwi_interrogate->num_records; ++index) { ASN1_CALL(pos, rose_enc_qsig_MWIInterrogateResElt(ctrl, pos, end, ASN1_TAG_SEQUENCE, &mwi_interrogate->list[index])); } ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \internal * \brief Decode the MsgCentreId 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 msg_centre_id 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_MsgCentreId(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseQsigMsgCentreId *msg_centre_id) { int32_t value; size_t str_len; int length; int explicit_offset; const unsigned char *explicit_end; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s MsgCentreId\n", name); } switch (tag) { case ASN1_CLASS_CONTEXT_SPECIFIC | 0: msg_centre_id->type = 0; /* integer */ ASN1_CALL(pos, asn1_dec_int(ctrl, "integer", tag, pos, end, &value)); msg_centre_id->u.integer = value; break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1: msg_centre_id->type = 1; /* partyNumber */ /* 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, end, &length)); ASN1_END_SETUP(explicit_end, explicit_offset, length, pos, end); ASN1_CALL(pos, asn1_dec_tag(pos, explicit_end, &tag)); ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "partyNumber", tag, pos, explicit_end, &msg_centre_id->u.number)); ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, end); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 2: case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2: msg_centre_id->type = 2; /* numericString */ ASN1_CALL(pos, asn1_dec_string_max(ctrl, "numericString", tag, pos, end, sizeof(msg_centre_id->u.str), msg_centre_id->u.str, &str_len)); break; default: ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); return NULL; } return pos; } /*! * \brief Decode the Q.SIG MWIActivate 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_MWIActivate_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { int32_t value; size_t str_len; int length; int seq_offset; int explicit_offset; const unsigned char *explicit_end; const unsigned char *seq_end; const unsigned char *save_pos; struct roseQsigMWIActivateArg *mwi_activate; mwi_activate = &args->qsig.MWIActivate; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " MWIActivateArg %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, "servedUserNr", tag, pos, seq_end, &mwi_activate->served_user_number)); 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, "basicService", tag, pos, seq_end, &value)); mwi_activate->basic_service = value; /* * A sequence specifies an ordered list of component types. * However, for simplicity we are not checking the order of * the remaining optional components. */ mwi_activate->msg_centre_id_present = 0; mwi_activate->number_of_messages_present = 0; mwi_activate->originating_number.length = 0; mwi_activate->timestamp_present = 0; mwi_activate->priority_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 & ~ASN1_PC_MASK) { case ASN1_CLASS_CONTEXT_SPECIFIC | 0: case ASN1_CLASS_CONTEXT_SPECIFIC | 1: case ASN1_CLASS_CONTEXT_SPECIFIC | 2: ASN1_CALL(pos, rose_dec_qsig_MsgCentreId(ctrl, "msgCentreId", tag, pos, seq_end, &mwi_activate->msg_centre_id)); mwi_activate->msg_centre_id_present = 1; break; case ASN1_CLASS_CONTEXT_SPECIFIC | 3: /* Must not be constructed but we will not check for it for simplicity. */ ASN1_CALL(pos, asn1_dec_int(ctrl, "nbOfMessages", tag, pos, seq_end, &value)); mwi_activate->number_of_messages = value; mwi_activate->number_of_messages_present = 1; break; case ASN1_CLASS_CONTEXT_SPECIFIC | 4: /* Must be constructed but we will not check for it for simplicity. */ /* 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_PartyNumber(ctrl, "originatingNr", tag, pos, explicit_end, &mwi_activate->originating_number)); ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); break; case ASN1_TYPE_GENERALIZED_TIME: ASN1_CALL(pos, asn1_dec_string_max(ctrl, "timestamp", tag, pos, end, sizeof(mwi_activate->timestamp.str), mwi_activate->timestamp.str, &str_len)); mwi_activate->timestamp_present = 1; break; case ASN1_CLASS_CONTEXT_SPECIFIC | 5: /* Must not be constructed but we will not check for it for simplicity. */ ASN1_CALL(pos, asn1_dec_int(ctrl, "priority", tag, pos, seq_end, &value)); mwi_activate->priority = value; mwi_activate->priority_present = 1; break; case ASN1_CLASS_CONTEXT_SPECIFIC | 6: case ASN1_CLASS_CONTEXT_SPECIFIC | 7: if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " argumentExt %s\n", asn1_tag2str(tag)); } /* Fixup will skip over the manufacturer extension information */ 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 MWIDeactivate 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_MWIDeactivate_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; const unsigned char *save_pos; struct roseQsigMWIDeactivateArg *mwi_deactivate; mwi_deactivate = &args->qsig.MWIDeactivate; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " MWIDeactivateArg %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, "servedUserNr", tag, pos, seq_end, &mwi_deactivate->served_user_number)); 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, "basicService", tag, pos, seq_end, &value)); mwi_deactivate->basic_service = value; /* * A sequence specifies an ordered list of component types. * However, for simplicity we are not checking the order of * the remaining optional components. */ mwi_deactivate->msg_centre_id_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 & ~ASN1_PC_MASK) { case ASN1_CLASS_CONTEXT_SPECIFIC | 0: case ASN1_CLASS_CONTEXT_SPECIFIC | 1: case ASN1_CLASS_CONTEXT_SPECIFIC | 2: ASN1_CALL(pos, rose_dec_qsig_MsgCentreId(ctrl, "msgCentreId", tag, pos, seq_end, &mwi_deactivate->msg_centre_id)); mwi_deactivate->msg_centre_id_present = 1; break; case ASN1_CLASS_CONTEXT_SPECIFIC | 3: case ASN1_CLASS_CONTEXT_SPECIFIC | 4: if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " argumentExt %s\n", asn1_tag2str(tag)); } /* Fixup will skip over the manufacturer extension information */ 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 MWIInterrogate 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_MWIInterrogate_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; const unsigned char *save_pos; struct roseQsigMWIInterrogateArg *mwi_interrogate; mwi_interrogate = &args->qsig.MWIInterrogate; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " MWIInterrogateArg %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, "servedUserNr", tag, pos, seq_end, &mwi_interrogate->served_user_number)); 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, "basicService", tag, pos, seq_end, &value)); mwi_interrogate->basic_service = value; /* * A sequence specifies an ordered list of component types. * However, for simplicity we are not checking the order of * the remaining optional components. */ mwi_interrogate->msg_centre_id_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 & ~ASN1_PC_MASK) { case ASN1_CLASS_CONTEXT_SPECIFIC | 0: case ASN1_CLASS_CONTEXT_SPECIFIC | 1: case ASN1_CLASS_CONTEXT_SPECIFIC | 2: ASN1_CALL(pos, rose_dec_qsig_MsgCentreId(ctrl, "msgCentreId", tag, pos, seq_end, &mwi_interrogate->msg_centre_id)); mwi_interrogate->msg_centre_id_present = 1; break; case ASN1_CLASS_CONTEXT_SPECIFIC | 3: case ASN1_CLASS_CONTEXT_SPECIFIC | 4: if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " argumentExt %s\n", asn1_tag2str(tag)); } /* Fixup will skip over the manufacturer extension information */ default: pos = save_pos; goto cancel_options; } } cancel_options:; ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \internal * \brief Decode the MWIInterrogateResElt 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 record 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_MWIInterrogateResElt(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseQsigMWIInterrogateResElt *record) { int32_t value; size_t str_len; 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, " MWIInterrogateResElt %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_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); ASN1_CALL(pos, asn1_dec_int(ctrl, "basicService", tag, pos, seq_end, &value)); record->basic_service = value; /* * A sequence specifies an ordered list of component types. * However, for simplicity we are not checking the order of * the remaining optional components. */ record->msg_centre_id_present = 0; record->number_of_messages_present = 0; record->originating_number.length = 0; record->timestamp_present = 0; record->priority_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 & ~ASN1_PC_MASK) { case ASN1_CLASS_CONTEXT_SPECIFIC | 0: case ASN1_CLASS_CONTEXT_SPECIFIC | 1: case ASN1_CLASS_CONTEXT_SPECIFIC | 2: ASN1_CALL(pos, rose_dec_qsig_MsgCentreId(ctrl, "msgCentreId", tag, pos, seq_end, &record->msg_centre_id)); record->msg_centre_id_present = 1; break; case ASN1_CLASS_CONTEXT_SPECIFIC | 3: /* Must not be constructed but we will not check for it for simplicity. */ ASN1_CALL(pos, asn1_dec_int(ctrl, "nbOfMessages", tag, pos, seq_end, &value)); record->number_of_messages = value; record->number_of_messages_present = 1; break; case ASN1_CLASS_CONTEXT_SPECIFIC | 4: /* Must be constructed but we will not check for it for simplicity. */ /* 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_PartyNumber(ctrl, "originatingNr", tag, pos, explicit_end, &record->originating_number)); ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); break; case ASN1_TYPE_GENERALIZED_TIME: ASN1_CALL(pos, asn1_dec_string_max(ctrl, "timestamp", tag, pos, end, sizeof(record->timestamp.str), record->timestamp.str, &str_len)); record->timestamp_present = 1; break; case ASN1_CLASS_CONTEXT_SPECIFIC | 5: /* Must not be constructed but we will not check for it for simplicity. */ ASN1_CALL(pos, asn1_dec_int(ctrl, "priority", tag, pos, seq_end, &value)); record->priority = value; record->priority_present = 1; break; case ASN1_CLASS_CONTEXT_SPECIFIC | 6: case ASN1_CLASS_CONTEXT_SPECIFIC | 7: if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " argumentExt %s\n", asn1_tag2str(tag)); } /* Fixup will skip over the manufacturer extension information */ 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 MWIInterrogate 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_MWIInterrogate_RES(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args) { int length; int seq_offset; const unsigned char *seq_end; struct roseQsigMWIInterrogateRes *mwi_interrogate; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " MWIInterrogateRes %s\n", asn1_tag2str(tag)); } ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); mwi_interrogate = &args->qsig.MWIInterrogate; mwi_interrogate->num_records = 0; while (pos < seq_end && *pos != ASN1_INDEF_TERM) { if (mwi_interrogate->num_records < ARRAY_LEN(mwi_interrogate->list)) { ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); ASN1_CALL(pos, rose_dec_qsig_MWIInterrogateResElt(ctrl, "listEntry", tag, pos, seq_end, &mwi_interrogate->list[mwi_interrogate->num_records])); ++mwi_interrogate->num_records; } else { /* Too many records */ return NULL; } } ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /* ------------------------------------------------------------------- */ /* end rose_qsig_mwi.c */ libpri-1.4.14/libpri-1.4.14-summary.html0000644000000000000000000001053312064640402016145 0ustar rootroot Release Summary - libpri-1.4.14

Release Summary

libpri-1.4.14

Date: 2012-12-20

<asteriskteam@digium.com>


Table of Contents

  1. Summary
  2. Contributors
  3. Closed Issues
  4. Other Changes
  5. Diffstat

Summary

[Back to Top]

This release includes only bug fixes. The changes included were made only to address problems that have been identified in this release series. Users should be able to safely upgrade to this version if this release series is already in use. Users considering upgrading from a previous release series are strongly encouraged to review the UPGRADE.txt document as well as the CHANGES document for information about upgrading to this release series.

The data in this summary reflects changes that have been made since the previous release, libpri-1.4.13.


Contributors

[Back to Top]

This table lists the people who have submitted code, those that have tested patches, as well as those that reported issues on the issue tracker that were resolved in this release. For coders, the number is how many of their patches (of any size) were committed into this release. For testers, the number is the number of times their name was listed as assisting with testing a patch. Finally, for reporters, the number is the number of issues that they reported that were closed by commits that went into this release.

Coders

Testers

Reporters

2 rmudgett
2 Tzafrir Cohen
2 tzafrir
1 nmerrett

Closed Issues

[Back to Top]

This is a list of all issues from the issue tracker that were closed by changes that went into this release.

Category: General


PRI-145: Unchecked write in pritest
Revision: 2305
Reporter: tzafrir
Coders: Tzafrir Cohen

PRI-147: Normal Clearing in Progress Message is ignored
Revision: 2308
Reporter: nmerrett
Coders: rmudgett


Commits Not Associated with an Issue

[Back to Top]

This is a list of all changes that went into this release that did not directly close an issue from the issue tracker. The commits may have been marked as being related to an issue. If that is the case, the issue numbers are listed here, as well.

RevisionAuthorSummaryIssues Referenced
2307rmudgettFix compiler error with ALERTING_NO_PROGRESS define.

Diffstat Results

[Back to Top]

This is a summary of the changes to the source code that went into this release that was generated using the diffstat utility.

pritest.c |    5 +++
q931.c    |   79 ++++++++++++++++++++++++++++++++++++++++++++++++++++----------
2 files changed, 71 insertions(+), 13 deletions(-)


libpri-1.4.14/rose_etsi_mwi.c0000644000000000000000000006076011400035226014576 0ustar rootroot/* * libpri: An implementation of Primary Rate ISDN * * Copyright (C) 2010 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 Message Waiting Indication (MWI) operations * * Message Waiting Indication (MWI) supplementary service EN 300 745-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 MessageID 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 msg_id * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *rose_enc_etsi_message_id(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct roseEtsiMessageID *msg_id) { unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, msg_id->reference_number)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, msg_id->status)); ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the MWIActivate 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_MWIActivate_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { const struct roseEtsiMWIActivate_ARG *mwi_activate; unsigned char *seq_len; unsigned char *explicit_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); mwi_activate = &args->etsi.MWIActivate; ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &mwi_activate->receiving_user_number)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, mwi_activate->basic_service)); if (mwi_activate->controlling_user_number.length) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1); ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &mwi_activate->controlling_user_number)); ASN1_CONSTRUCTED_END(explicit_len, pos, end); } if (mwi_activate->number_of_messages_present) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, mwi_activate->number_of_messages)); ASN1_CONSTRUCTED_END(explicit_len, pos, end); } if (mwi_activate->controlling_user_provided_number.length) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3); ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &mwi_activate->controlling_user_provided_number)); ASN1_CONSTRUCTED_END(explicit_len, pos, end); } if (mwi_activate->time_present) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 4); ASN1_CALL(pos, asn1_enc_string_max(pos, end, ASN1_TYPE_GENERALIZED_TIME, mwi_activate->time.str, sizeof(mwi_activate->time.str) - 1)); ASN1_CONSTRUCTED_END(explicit_len, pos, end); } if (mwi_activate->message_id_present) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 5); ASN1_CALL(pos, rose_enc_etsi_message_id(ctrl, pos, end, &mwi_activate->message_id)); ASN1_CONSTRUCTED_END(explicit_len, pos, end); } if (mwi_activate->mode_present) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 6); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, mwi_activate->mode)); ASN1_CONSTRUCTED_END(explicit_len, pos, end); } ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the MWIDeactivate 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_MWIDeactivate_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { const struct roseEtsiMWIDeactivate_ARG *mwi_deactivate; unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); mwi_deactivate = &args->etsi.MWIDeactivate; ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &mwi_deactivate->receiving_user_number)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, mwi_deactivate->basic_service)); if (mwi_deactivate->controlling_user_number.length) { ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &mwi_deactivate->controlling_user_number)); } if (mwi_deactivate->mode_present) { ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, mwi_deactivate->mode)); } ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the MWIIndicate 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_MWIIndicate_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { const struct roseEtsiMWIIndicate_ARG *mwi_indicate; unsigned char *seq_len; unsigned char *explicit_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); mwi_indicate = &args->etsi.MWIIndicate; if (mwi_indicate->controlling_user_number.length) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1); ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &mwi_indicate->controlling_user_number)); ASN1_CONSTRUCTED_END(explicit_len, pos, end); } if (mwi_indicate->basic_service_present) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, mwi_indicate->basic_service)); ASN1_CONSTRUCTED_END(explicit_len, pos, end); } if (mwi_indicate->number_of_messages_present) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, mwi_indicate->number_of_messages)); ASN1_CONSTRUCTED_END(explicit_len, pos, end); } if (mwi_indicate->controlling_user_provided_number.length) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 4); ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &mwi_indicate->controlling_user_provided_number)); ASN1_CONSTRUCTED_END(explicit_len, pos, end); } if (mwi_indicate->time_present) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 5); ASN1_CALL(pos, asn1_enc_string_max(pos, end, ASN1_TYPE_GENERALIZED_TIME, mwi_indicate->time.str, sizeof(mwi_indicate->time.str) - 1)); ASN1_CONSTRUCTED_END(explicit_len, pos, end); } if (mwi_indicate->message_id_present) { /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 6); ASN1_CALL(pos, rose_enc_etsi_message_id(ctrl, pos, end, &mwi_indicate->message_id)); ASN1_CONSTRUCTED_END(explicit_len, pos, end); } ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \internal * \brief Decode the MessageID 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 msg_id 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_message_id(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseEtsiMessageID *msg_id) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s MessageID %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_INTEGER); ASN1_CALL(pos, asn1_dec_int(ctrl, "messageRef", tag, pos, seq_end, &value)); msg_id->reference_number = value; 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, "status", tag, pos, seq_end, &value)); msg_id->status = value; ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the MWIActivate 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_MWIActivate_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { int32_t value; size_t str_len; int length; int seq_offset; int explicit_offset; const unsigned char *explicit_end; const unsigned char *seq_end; const unsigned char *save_pos; struct roseEtsiMWIActivate_ARG *mwi_activate; mwi_activate = &args->etsi.MWIActivate; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " MWIActivate %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, "receivingUserNr", tag, pos, seq_end, &mwi_activate->receiving_user_number)); 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, "basicService", tag, pos, seq_end, &value)); mwi_activate->basic_service = value; /* * A sequence specifies an ordered list of component types. * However, for simplicity we are not checking the order of * the remaining optional components. */ mwi_activate->controlling_user_number.length = 0; mwi_activate->number_of_messages_present = 0; mwi_activate->controlling_user_provided_number.length = 0; mwi_activate->time_present = 0; mwi_activate->message_id_present = 0; mwi_activate->mode_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 | 1: /* 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_PartyNumber(ctrl, "controllingUserNr", tag, pos, explicit_end, &mwi_activate->controlling_user_number)); ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2: /* 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_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER); ASN1_CALL(pos, asn1_dec_int(ctrl, "numberOfMessages", tag, pos, explicit_end, &value)); mwi_activate->number_of_messages = value; mwi_activate->number_of_messages_present = 1; ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 3: /* 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_PartyNumber(ctrl, "controllingUserProvidedNr", tag, pos, explicit_end, &mwi_activate->controlling_user_provided_number)); ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 4: /* 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_CHECK_TAG(ctrl, tag & ~ASN1_PC_MASK, tag, ASN1_TYPE_GENERALIZED_TIME); ASN1_CALL(pos, asn1_dec_string_max(ctrl, "time", tag, pos, explicit_end, sizeof(mwi_activate->time.str), mwi_activate->time.str, &str_len)); mwi_activate->time_present = 1; ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 5: /* 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_etsi_message_id(ctrl, "messageId", tag, pos, explicit_end, &mwi_activate->message_id)); mwi_activate->message_id_present = 1; ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 6: /* 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_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); ASN1_CALL(pos, asn1_dec_int(ctrl, "mode", tag, pos, explicit_end, &value)); mwi_activate->mode = value; mwi_activate->mode_present = 1; ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, 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 MWIDeactivate 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_MWIDeactivate_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; struct roseEtsiMWIDeactivate_ARG *mwi_deactivate; mwi_deactivate = &args->etsi.MWIDeactivate; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " MWIDeactivate %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, "receivingUserNr", tag, pos, seq_end, &mwi_deactivate->receiving_user_number)); 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, "basicService", tag, pos, seq_end, &value)); mwi_deactivate->basic_service = value; /* * A sequence specifies an ordered list of component types. * However, for simplicity we are not checking the order of * the remaining optional components. */ mwi_deactivate->controlling_user_number.length = 0; mwi_deactivate->mode_present = 0; while (pos < seq_end && *pos != ASN1_INDEF_TERM) { ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); switch (tag) { default: ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "controllingUserNr", tag, pos, seq_end, &mwi_deactivate->controlling_user_number)); break; case ASN1_TYPE_ENUMERATED: ASN1_CALL(pos, asn1_dec_int(ctrl, "mode", tag, pos, seq_end, &value)); mwi_deactivate->mode = value; mwi_deactivate->mode_present = 1; break; } } ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the MWIIndicate 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_MWIIndicate_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { int32_t value; size_t str_len; int length; int seq_offset; int explicit_offset; const unsigned char *explicit_end; const unsigned char *seq_end; const unsigned char *save_pos; struct roseEtsiMWIIndicate_ARG *mwi_indicate; mwi_indicate = &args->etsi.MWIIndicate; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " MWIIndicate %s\n", 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. */ mwi_indicate->controlling_user_number.length = 0; mwi_indicate->basic_service_present = 0; mwi_indicate->number_of_messages_present = 0; mwi_indicate->controlling_user_provided_number.length = 0; mwi_indicate->time_present = 0; mwi_indicate->message_id_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 | 1: /* 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_PartyNumber(ctrl, "controllingUserNr", tag, pos, explicit_end, &mwi_indicate->controlling_user_number)); ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2: /* 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_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); ASN1_CALL(pos, asn1_dec_int(ctrl, "basicService", tag, pos, explicit_end, &value)); mwi_indicate->basic_service = value; mwi_indicate->basic_service_present = 1; ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 3: /* 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_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER); ASN1_CALL(pos, asn1_dec_int(ctrl, "numberOfMessages", tag, pos, explicit_end, &value)); mwi_indicate->number_of_messages = value; mwi_indicate->number_of_messages_present = 1; ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 4: /* 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_PartyNumber(ctrl, "controllingUserProvidedNr", tag, pos, explicit_end, &mwi_indicate->controlling_user_provided_number)); ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 5: /* 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_CHECK_TAG(ctrl, tag & ~ASN1_PC_MASK, tag, ASN1_TYPE_GENERALIZED_TIME); ASN1_CALL(pos, asn1_dec_string_max(ctrl, "time", tag, pos, explicit_end, sizeof(mwi_indicate->time.str), mwi_indicate->time.str, &str_len)); mwi_indicate->time_present = 1; ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 6: /* 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_etsi_message_id(ctrl, "messageId", tag, pos, explicit_end, &mwi_indicate->message_id)); mwi_indicate->message_id_present = 1; ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); break; default: pos = save_pos; goto cancel_options; } } cancel_options:; ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /* ------------------------------------------------------------------- */ /* end rose_etsi_mwi.c */ libpri-1.4.14/ChangeLog0000644000000000000000000026400612064640374013350 0ustar rootroot2012-12-20 Asterisk Development Team * libpri 1.4.14 Released. 2012-12-10 18:28 +0000 [r2310] Richard Mudgett * q931.c: Handle optional Recommendation octet 3a in Cause IE. If the MSB of octet 3 is 0 then the optional Recommendation octet 3a is present. References: ITU-T Q.850 Section 2.1 and ETSI ETS 300 102-1 Section 4.5.12. (closes issue PRI-151) Reported by: Tzafrir Cohen Patches: 0001-handle-optional-Recommendation-in-Cause-IE.patch (license #5035) patch uploaded by Tzafrir Cohen Modified 2012-11-13 19:59 +0000 [r2307-2308] Richard Mudgett * q931.c: Q.SIG: Allow PROGRESS when in the Active state. ECMA-143 Section 10.1.7.2 indicates that PROGRESS is allowed when in the Active state. * Made Q.SIG ignore the PROGRESS message when in the Active call state. (closes issue PRI-147) Reported by: Nick Merrett * q931.c: Fix compiler error with ALERTING_NO_PROGRESS define. 2012-10-29 21:19 +0000 [r2305] Richard Mudgett * pritest.c: Fix compiler warning in pritest.c. * Made do_channel() exit on a failed write(). (closes issue PRI-145) Reported by: Tzafrir Cohen Patches: fix_unused_write.patch (license #5035) patch uploaded by Tzafrir Cohen Modified 2012-10-09 Asterisk Development Team * libpri 1.4.13 Released. 2012-09-26 15:46 +0000 [r2300] Richard Mudgett * Makefile: Allow passing compiler flags (CFLAGS, LDFLAGS) (closes issue PRI-144) Reported by: Tzafrir Cohen Patches: flags.diff (license #5035) patch uploaded by Tzafrir Cohen 2012-09-10 15:52 +0000 [r2298] Richard Mudgett * pridump.c: Fix compile error in pridump.c. With gcc 4.6.3 it's possible to get the following error: $ make gcc -Wall -Werror -Wstrict-prototypes -Wmissing-prototypes -g -fPIC -O2 -MD -MT pridump.o -MF .pridump.o.d -MP -c -o pridump.o pridump.c pridump.c: In function \u2018pri_bridge\u2019: pridump.c:117:1: error: no return statement in function returning non-void [-Werror=return-type] cc1: all warnings being treated as errors make: *** [pridump.o] Error 1 Changing the function return value to void fixes the issue since there were no places in the code that used the return value. (closes issue PRI-143) Reported by: Birger "WIMPy" Harzenetter Patches: 0001-Fix-no-return-statement-in-function-returning-non-vo.patch (license #5417) patch uploaded by Shaun Ruffell 2012-08-11 01:38 +0000 [r2292-2295] Richard Mudgett * /: SVN ignore built utilities. * Makefile: * Made no longer compile *.lo files they are identical to *.o files. * Made compile the pritest, rosetest, and testprilib utilities using the static libpri library. No more forgetting to install the library after a change and wondering why it still did not work. The pridump utility is still dynamically linked. * Made compile the utilities by default. * pridump.c, testprilib.c: Make pridump and testprilib compile again. 2012-07-30 16:20 +0000 [r2290] Richard Mudgett * q921.c: Removed MDL/TEI management configuration warning message. Some telco switches send out MDL messages even though they are configured for PTP. Usually they are checking for assigned TEI's. Since these switches periodically poll for assigned TEI's, the message needlessly fills up log files. * Changed message warning level to a normal debug message level and reworded. (closes issue PRI-137) Reported by: Bart Coninckx 2012-06-28 00:16 +0000 [r2288] Richard Mudgett * pri.c, pri_internal.h, q931.c, libpri.h: Implement T316 to allow RESTART messages to be automatically retransmitted. Q.931 defines the T316 timer to retransmit RESTART messages if a RESTART ACKNOWLEDGE message is not received before the timer expires. Q.931 defaults the time of T316 to 2 minutes with the default number of consecutive RESTART failures as two. * To support legacy behavior, the T316 timer is disabled by default. It is also disabled because the user cannot configure it to disabled if it is enabled. * The N316 count is created to allow the number of RESTART attempts to be configurable. Note you will need to recompile Asterisk to be able to configure N316. (issue ASTERISK-19608) (issue AST-815) (closes issue PRI-133) Reported by: Mike Boylan Tested by: rmudgett 2012-05-14 14:54 +0000 [r2286] Richard Mudgett * q931.c: Make pri_pres2str() return correct string. * Fix pri_pres2str() mask creation. (closes issue PRI-139) Reported by: Pavel Troller Patches: q931.c.diff (license #6302) patch uploaded by Pavel Troller 2012-03-23 19:35 +0000 [r2284] Richard Mudgett * pri.c, q931.c, pri_facility.c, pri_cc.c: Make number not available presentation also set screening to network provided. Q.951 indicates that when the presentation indicator is "Number not available due to interworking" for a number then the screening indicator field should be "Network provided". Released versions of Asterisk starting with v1.8 relesed before this patch only recognized the PRES_NUMBER_NOT_AVAILABLE value as an unavailable number. This patch improves compatibility as a result. * Made mask the presentation value for names and numbers from the upper layer. * Made pri_mwi_indicate_v2() also call q931_party_id_fixup() for completeness even though it is a noop in this case. * Made pri_pres2str() deceoode better. 2012-02-03 23:12 +0000 [r2282] Kinsey Moore * pri_q921.h, q921.c, pridump.c: Make PRI_DEBUG_Q921_RAW work independantly of PRI_DEBUG_Q921_DUMP Ensure that the DUMP and RAW flags work independently in q921_dump(). (closes issue PRI-119) Patch-by: wimpy 2012-01-26 20:19 +0000 [r2280] Richard Mudgett * q931.c: Use ie2str(full_ie) where possible in q931.c. Initial patch by Alec Davis. Review: https://reviewboard.asterisk.org/r/1633/ 2011-12-16 20:01 +0000 [r2277-2278] Richard Mudgett * pri_internal.h, q931.c: Remove nul octets from IE data that is normally treated as strings. Sometimes ie values received from carriers contain nul octets in values normally treated by libpri as nul terminated strings. A discussion on the asterisk-users list determined that the best thing to do in the situation is to delete the nul octets and unconditionally report/log when that happens. * Remove nul octets from the following ie's and generate an unconditional log message to the upper layer when they are removed: Connected Number Connected Address Redirecting Number Original Called Number Redirection Number Called Party Number Calling Party Number Display Keypad Facility (closes issue PRI-128) Reported by: phsultan Patches: jira_pri_128.patch (license #5621) patch uploaded by rmudgett (modified) Tested by: rmudgett * pri_internal.h, q931.c: Implement handling a multi-channel RESTART request. The channel id ie can supply a slotmap or list of channels. For a RESTART message, this can be handy to indicate multiple channels that need to be restarted at the same time. An incoming RESTART request will now generate a PRI_EVENT_RESTART to the upper layer for each channel indicated in the request. If the event is successfully generated for all indicated channels then a RESTART_ACKNOWLEDGE is sent back to the peer indicating all channels restarted. * Add the ability to process a channel id ie channel list with a RESTART request. * Add the ability to process slotmaps with a RESTART request. (closes issue PRI-93) Reported by: Marcin Kowalczyk Patches: jira_pri_93.patch (license #5621) patch uploaded by rmudgett Tested by: zvision, rmudgett (closes issue PRI-71) Reported by: Torrey Searle Tested by: rmudgett 2011-09-26 22:54 +0000 [r2275] Richard Mudgett * q931.c: Fix message typo: Weird (closes issue PRI-126) Reported by: Tzafrir Cohen 2011-08-17 15:48 +0000 [r2273] Richard Mudgett * pri.c, pri_internal.h, pri_q921.h, q921.c, libpri.h: Outgoing BRI calls fail when using Asterisk 1.8 with HA8, HB8, and B410P cards. France Telecom brings layer 2 and layer 1 down on BRI lines when the line is idle. When layer 1 goes down Asterisk cannot make outgoing calls and the HA8 and HB8 cards also get IRQ misses. The inability to make outgoing calls is because the line is in red alarm and Asterisk will not make calls over a line it considers unavailable. The IRQ misses for the HA8 and HB8 card are because the hardware is switching clock sources from the line which just brought layer 1 down to internal timing. There is a DAHDI option for the B410P card to not tell Asterisk that layer 1 went down so Asterisk will allow outgoing calls: "modprobe wcb4xxp teignored=1". There is a similar DAHDI option for the HA8 and HB8 cards: "modprobe wctdm24xxp bri_teignored=1". Unfortunately that will not clear up the IRQ misses when the telco brings layer 1 down. * Add layer 2 persistence option to customize the layer 2 behavior on BRI PTMP lines. The new option has three settings: 1) Use libpri default layer 2 setting. 2) Keep layer 2 up. Bring layer 2 back up when the peer brings it down. 3) Leave layer 2 down when the peer brings it down. Layer 2 will be brought up as needed for outgoing calls. JIRA AST-598 2011-07-05 Leif Madsen * Release libpri 1.4.12 2011-05-17 20:13 +0000 [r2266] Richard Mudgett * pri.c, pri_internal.h, q931.c, libpri.h: Option needed for Q931_IE_TIME_DATE to be optional in CONNECT message. The NEC SV8300 rejects the Q931_IE_TIME_DATE for Q.SIG. Add option to specify if and how much of the current time is put in Q931_IE_TIME_DATE. * Send date/time ie never. * Send date/time ie date only. * Send date/time ie date and hour. * Send date/time ie date, hour, and minute. * Send date/time ie date, hour, minute, and second. * Send date/time ie default: Libpri will send date and hhmm only when in NT PTMP mode to support ISDN phones. (closes issue #19221) Reported by: kenner JIRA SWP-3396 2011-04-18 19:43 +0000 [r2262] Richard Mudgett * pri_internal.h, q931.c, pri_facility.c, libpri.h: Problems with ISDN MWI to phones. 1) The "controlling user number" is always the number of the voice mail box which is identical with the subscriber number itself. This number which is listed in the ISDN phone MWI menu cannot be called back to contact the voice mail box. The controlling user number should be made configurable. 2) The MWI indication is not restricted to a user (broadcast facility with dummy call reference). A called party IE should be added to address only the relevant MSN. (ETSI 300-196 Section 8.3.2.4) JIRA ABE-2738 JIRA SWP-2846 2011-03-18 01:59 +0000 [r2258] Richard Mudgett * pri_facility.c: CallRerouting response not sent if peer hangs up first. Send the CallRerouting response on the next message instead of only on the DISCONNECT message. The next message is either going to be a DISCONNECT or RELEASE depending on who initiates disconnection first. 2011-03-01 00:50 +0000 [r2227-2238] Richard Mudgett * pri.c, pri_internal.h, pri_q931.h, q931.c, libpri.h: Implement the mandatory T312 timer for NT PTMP broadcast SETUP calls. * Fixed stopping T303 too early on a NT PTMP broadcast SETUP call if a subcall just receives a RELEASE_COMPLETE(busy). * Fixed a valgrind reported invalid read/write when hanging up a NT PTMP broadcast SETUP call. JIRA LIBPRI-32 JIRA SWP-2548 * q931.c, pri_facility.c: Miscellaneous cleanup before T312 branch merge. * q931.c: Fix valgrind reported invalid read/write for display text feature. * pri.c, pri_internal.h, q931.c, pri_cc.c: Improve the usefulness of pri_dump_info_str() output. * Add BRI and PTMP strings to node type config when configured that way. * Move Q.921 statistics to after configuration settings. * Add call and cc_record debug statistics to pri_dump_info_str(). * pri.c: Add determined remote node type to pri_dump_info_str(). 2011-02-17 21:12 +0000 [r2202-2207] Richard Mudgett * q931.c: B channel lost by incoming call in BRI NT PTMP mode. A phone's RELEASE_COMPLETE as a response to an initial broadcast SETUP blocks one B channel permantly when the call is cancelled. Scenario: A call to the ISDN Bus is acknowledged (ALERTING) by one phone/endpoint and rejected by another phone/endpoint with a RELEASE_COMPLETE. The call is then cancelled by the caller. If the whole procedure is repeated once again then any further call attempt is rejected (WARNING[5666]: app_dial.c:1546 dial_exec_full: Unable to create channel of type 'DAHDI' (cause 34 - Circuit/channel congestion)). It seems that receiving a RELEASE_COMPLETE in that state blocks one B channel permanently when the call is cancelled by the caller. Background: The ISDN phones (Siemens Gigaset 3035 or CX253) we use for testing additionally contain a DECT base station, which operates as a different endpoint on the ISDN Bus (TEI). If the DECT base station is not in use then there are no DECT phones registered to the base station. The DECT base station responds to an incoming call not directed toward it with (RELEASE_COMPLETE, cause: no user responding). * Made initiate_hangup_if_needed() also hangup the subcall if it is in the NULL state. * Simplified q931_set_subcall_winner(). JIRA ABE-2745 JIRA SWP-2954 * rose.c: DMS-100 not receiving caller name anymore. Looks like DMS-100 is using the same message as Q.SIG to receive the caller name. Add the ability to decode the ROSE calling name message defined for the Q.SIG switch on the DMS-100 switch. (closes issue #18822) Reported by: cmorford Patches: issue18822_v1.4.patch uploaded by rmudgett (license 664) Tested by: cmorford * rose.c, pri_facility.c: * Added switchtype to ROSE invoke operation not handled message. * Reordered NI2 ROSE message table so any conflicts with the pirated Q.SIG messages will be in favor of the NI2 specific messages. This is precautionary only. * pri_internal.h, prisched.c: Crash if NFAS swaps D channels on a call with an active timer. If a Q.931 call record related timer is started on one NFAS D channel expires after NFAS swaps to another D channel, then libpri could crash. For example: 1) Hangup a call. 1a) Send a DISCONNECT. 1b) Start the T305 retransmit timer on the current D channel. 2) The RELEASE comes in on another D channel. 2a) The found call record switches its assignment to the new D channel. 2b) Attempt to stop T305. Unfortunately, the timer was started on another D channel so the attempt does not find the timer to stop. 3) The hangup sequence continues normally and the call record is freed since there is only one call record pool. 4) T305 expires on the original D channel and crashes the system when it uses the stale call record pointer it has saved. Made each D channel timer pool have a unique range of valid timer identifiers. If a given timer identifier is not in the range for the current NFAS D channel, then search the D channel group for the original D channel. JIRA LIBPRI-58 JIRA SWP-2721 2011-02-14 23:33 +0000 [r2200] Richard Mudgett * pri_q921.h, q921.c: Fix I-frame retransmission quirks. Revamped the I-frame retransmission queue to better comply with Q.921: Figure B.7/Q.921 (sheet 1 of 10) and Figure B.9/Q.921 (Sheet 5 of 5). The changes prevent retransmitting I-frames when the peer is busy (RNR) (Q.921 Section 5.6.5) and eliminate an unnecessary delay sending new I-frames after an I-frame retransmission. Related to JIRA LIBPRI-60 2011-02-08 16:44 +0000 [r2192] Richard Mudgett * pri.c: Swap of master/slave in pri_enslave() incorrect. Thank you. All I can say is oops. (closes issue #18769) Reported by: jcollie Patches: libpri-1.4.12-beta3-swap.patch uploaded by jcollie (license 412) 2011-02-04 19:59 +0000 [r2187-2190] Richard Mudgett * pri.c, pri_internal.h, q931.c, pri_facility.c, libpri.h: Add display ie text handling options. The display ie handling can be controlled independently in the send and receive directions with the following options: * Block display text data. * Use display text in SETUP/CONNECT messages for name. * Use display text for COLP name updates (FACILITY/NOTIFY as appropriate). * Pass arbitrary display text during a call. Sent in INFORMATION messages. Received from any message that the display text was not used as a name. If the display options are not set then the options default to legacy behavior. * q931.c: Add Q931_IE_TIME_DATE to CONNECT message when in network mode. Add the Q931_IE_TIME_DATE with the current date/time of the system to the Q.931 CONNECT message when in network mode. The date/time IE allows attached equipment to synchronize their clock with the network. Most notably, ISDN phones can display the current date/time. See issue #18047 about a concern with non-conforming Siemens terminals. (closes issue #18047) Reported by: wuwu Patches: timedate.patch uploaded by rmudgett (license 664) Tested by: rmudgett JIRA SWP-2955 JIRA ABE-2747 2011-02-01 00:37 +0000 [r2183] Richard Mudgett * pri.c, pri_internal.h, Makefile, q921.c, q931.c: Enable LIBPRI_COUNTERS code by default. Removed the conditional LIBPRI_COUNTERS to include the code unconditionally. Patches: enable_LIBPRI_COUNTERS_LIBPRI-61.diff uploaded by jbigelow JIRA LIBPRI-61 2010-12-21 19:46 +0000 [r2169-2175] Richard Mudgett * pri.c, q931.c: Invalid PTMP redirecting signaling as TE towards NT. * The PTMP redirection signaling (NOTIFY redirection number and notification code, SETUP redirecting number) is also sent in PTMP/TE mode. It should only apply in PTMP/NT mode. The call setup proceeds but the network (Deutsche Telekom) reacts with ugly ISDN STATUS messages. * Don't send the redirecting number ie when PTP is also sending the DivertingLegInformation2 facility. The redirecting number ie is redundant and the network (Deutsche Telekom) complains about it. JIRA LIBPRI-53 JIRA SWP-2543 * q931.c: Fix Q.931 retried SETUP not sending facility ies. Resend standard facility ies when the SETUP is retried by Q.931. However, one time facility ies are no longer available to add to a retried SETUP message. * pri.c, pri_internal.h, q931.c, pri_facility.c, pri_facility.h: Add call transfer exchange of subaddresses support and fix PTMP call transfer signaling. * Add the ability to exchange subaddresses for ETSI PTMP, ETSI PTP, and Q.SIG for call transfer. * Fix ETSI PTMP to send the correct messages depending on the call state for call transfer. NOTE: Some ISDN phones only handle the NOTIFY message that the EN 300-369 spec says should be sent only if the call has not connected yet. JIRA LIBPRI-47 JIRA SWP-2363 Review: https://reviewboard.asterisk.org/r/1051/ * pri_internal.h, q931.c: Better HOLD/RETRIEVE collision handling. The upper layer is now initiating HOLD/RETRIEVE signaling. These changes are needed to help preserve the correct channel id after a collision. * q931.c: Fix regression when reorganized for struct pri and struct q921_link. 2010-12-14 01:09 +0000 [r2166-2167] Richard Mudgett * q931.c: Return error if q931_notify() cannot send NOTIFY. * q931.c: Fix bizarre logic to work as originally intended in q931_notify(). In revision 238: Don't allow notification codes outside of the Q.931 spec for switches other than EuroISDN. 2010-11-12 02:31 +0000 [r2113] Richard Mudgett * pri_internal.h, pri_q921.h, q921.c: Asterisk is getting a "No D-channels available!" warning message every 4 seconds. For PTP links, libpri generated the PRI_EVENT_DCHAN_DOWN event every time it failed to bring layer 2 up because the physical layer is down. For PTP links, made generate the PRI_EVENT_DCHAN_UP/PRI_EVENT_DCHAN_DOWN only when it enters/exits the Q.921 superstate consisting of states 7(Q921_MULTI_FRAME_ESTABLISHED) and 8(Q921_TIMER_RECOVERY). Also changed the PTP link restart delay to be link specific instead of D channel specific because the GR-303 PTP switch types have more than one Q.921 link. (closes issue #17270) Reported by: jmls 2010-11-11 00:30 +0000 [r2109-2111] Richard Mudgett * pri_internal.h, q921.c: SABME flood on backup D-channel in NFAS configuration. Made delay restarting the PTP layer 2 link by the T200 time instead of immediately. Q.921 does not specify any particular time to restart the layer 2 link. Q.921 leaves it up to the upper layers to decide when or if another attempt to bring layer 2 up is made. Earlier versions of libpri used the T200 time to restart the link. This is a reimplementaion of -r1878. (closes issue #18255) Reported by: bklang JIRA SWP-2508 * q921.c: Minor MDL handler changes. * Clear mdl_error in case we could not schedule the handler callback. * Change MDL handlers to not return the handled state since the caller did not care. 2010-11-05 20:05 +0000 [r2098-2105] Richard Mudgett * pri.c, pri_internal.h, pri_q921.h, q921.c: Added TEI identity check feature to reclaim dead TEIs. This is the new feature portion of JIRA LIBPRI-51/SWP-2453. * q921.c: Q.921 TEI assignment procedure corrections. * We should send the TEI identity denied message with TEI=127 when the TEI pool is exhausted. * We should remove our TEI if we see a TEI identity assign message assigning our TEI to someone else. * pri_q921.h, q921.c: Convert TEI identity defines to enum and create 2str() function. * pri_q921.h, q921.c: Remove unneeded struct q921_link.mdl_error_state member. * q921.c: Remove all TEIs when NT PTMP starts. Remove all TEIs when a NT PTMP link is started and there are no other links to make sure there are no devices that think they have a TEI. A device may think it has a TEI if the upper layer program is restarted or the system reboots. This fixes the bug portion of JIRA LIBPRI-51/SWP-2453. * q921.c: Mainly put space after switch and while keywords. 2010-11-02 19:11 +0000 [r2088] Richard Mudgett * q921.c: B410P gets incoming call packets on ISDN but Asterisk doesn't see the call. The Cisco 1751 with VIC 2-BRI ports sends out SETUP messages on the broadcast TEI as if the BRI were PTMP even though it is configured for PTP mode. Make PTP mode also accept frames on SAPI=0, TEI=127 (Broadcast). (closes issue #18232) Reported by: lelio Patches: issue18232_v1.4.patch uploaded by rmudgett (license 664) Tested by: lelio 2010-10-21 18:00 +0000 [r2063-2079] Richard Mudgett * doc/cc_ptmp_agent.fsm, pri_internal.h, q931.c, doc/cc_ptp_agent.fsm, doc/cc_qsig_agent.fsm, pri_cc.c, doc/cc_ptmp_agent_flattened.fsm, doc/cc_ptp_agent_flattened.fsm, doc/cc_qsig_agent_flattened.fsm: Fixes CC agents not automatically clearing if T309 clears the original call. Incoming calls with CC enabled will not automatically clear the CC offer record when the call is aborted by T309 processing. All CC agent FSM's have this problem (PTMP, PTP, and Q.SIG). To reproduce: 1) Place incoming call to Asterisk/libpri 2) Either before or after the call is answered, bring the ISDN link down. 3) T309 processing, T309 timeout, or TEI removal will leave the CC agent FSM in the CC available state. The problem is indicated by the "cc report status" CLI command showing a status of CC offered to caller but it will never timeout. The FSM's can be manually cleared by using the "cc cancel all" or "cc cancel core" CLI commands. JIRA LIBPRI-46 JIRA SWP-2241 * pri.c, pri_internal.h: Partial support for dynamic interfaces with NFAS. To have some support for dynamic interfaces, the master NFAS D channel control structure will always exist even if it is abandoned/deleted by the upper layer. The master/slave pointers ensure that the correct master will be used. * pri.c, pri_internal.h, pri_q921.h, pri_aoc.c, pri_q931.h, q921.c, q931.c, pri_facility.c, pri_cc.c, prisched.c: Extract the layer 2 link structure out of struct pri. This completes the layer 2 link and Q.931 call control restructuring. Some code is now simplified since there is only one D channel control structure and the amount of allocated memory is reduced. * pri.c, pri_internal.h, pri_q921.h, pri_q931.h, q921.c, q931.c, pri_facility.c: Restructure the Q.931 call record to layer 2 link association. This is in anticipation of extracting a layer 2 link structure out of struct pri. Also completes fixing timer value access for the rest of libpri. The timer access must always be on the D channel control structure (Master). May have fixed some events from timeouts not being passed to the upper layer. The timeout events must always be on the D channel control structure (Master). * pri_q921.h, q921.c: Logically separate Q.921 TEI link processing from D channel control. This is in anticipation of extracting a layer 2 link structure out of struct pri. Also fixes Q.921 timer value access. The timer access must always be on the D channel control structure (Master). 2010-10-16 04:34 +0000 [r2015-2042] Richard Mudgett * q921.c: Dump Q.931 message using the TEI value the message came in with. * pri_internal.h, q931.c: Create two versions of call ptr verify. One gripes and one does not. * q931.c: Crash when receiving an unknown/unsupported message type. Fix double free of a call record and the subsequent continued use of the freed call record when receiving an unsupported/unknown message type. (closes issue #17968) Reported by: gelo Patches: issue_17968_v1.4.patch uploaded by rmudgett (license 664) * pri.c, pri_internal.h, pri_aoc.c, q931.c, pri_facility.c, pri_cc.c: Segfault in pri_schedule_del() - ctrl value is invalid. Validate the given call pointer in libpri API calls. If the call pointer is not an active call record then a complaint message is issued and the API call aborts. The call pointer is likely stale. This patch is defensive. More information is needed to figure out why Asterisk still has a call pointer during its hangup sequence. (closes issue #17522) (closes issue #18032) Reported by: schmoozecom Patches: issue_18032_v1.4.patch uploaded by rmudgett (license 664) Tested by: rmudgett 2010-10-07 04:00 +0000 [r2009] Tilghman Lesher * Makefile: Minor changes to make libpri build on Mac OS X 2010-09-16 21:24 +0000 [r2001] Russell Bryant * Makefile: Makefile tweaks to allow building for code coverage analysis. 2010-09-13 21:21 +0000 [r1982-1991] Richard Mudgett * pri_q931.h, q921.c, q931.c: PRI links do not retain active calls if the link comes back before T309 expires. The DL-ESTABLISH confirm event was not passed from Q.921 to Q.931 so Q.931 never cancelled the T309 timer. Refactored q931_dl_tei_removal() and q931_dl_indication() into q931_dl_event() to allow the DL-ESTABLISH confirm/indication and DL-RELEASE confirm/indication events to be passed to Q.931. * pri.c, pri_internal.h, pri_q931.h, q921.c, q931.c, prisched.c: BRI PTMP: Active channels not cleared when the interface goes down. If the connection to the terminal is lost while there are open channels on the interface, red alarm is reported, but the open channels are never cleared. Additionally, if you manually try to channel request hangup, Asterisk crashes. For PTMP, the T309 processing was not searching the call pool on the master control record. Additionally, for NT PTMP, the timeout events were not passed to the upper layer because the events were not put on the master control record where timer processing expects them. (closes issue #17865) Reported by: wimpy Patches: issue17865_v1.4.patch uploaded by rmudgett (license 664) Tested by: rmudgett, wimpy 2010-09-10 23:15 +0000 [r1976] Richard Mudgett * doc/cc_ptmp_agent.fsm, doc/cc_ptmp_agent_flattened.fsm: Fix spelling error in PTMP agent FSM files. 2010-09-08 21:43 +0000 [r1958-1965] Richard Mudgett * pri.c: Added more parameter checks to pri_set_timer() and pri_get_timer(). Made pri_dump_info_str() output a line for each Q.921 TEI Tx queue when LIBPRI_COUNTERS is defined. * q921.c, q931.c: Made Q.921 delay events to Q.931 if the event could immediately generate response frames. Q.921 was passing a q931_dl_indication(up) event to Q.931 before it was finished processing the frame. The q931_dl_indication(up) event could immediately send STATUS messages in the Q.921 intermediate state that would then get stuck in the tx queue with an invalid N(S). Q.921 was passing i-frames to Q.931 before it was finished processing the frame. The i-frames could cause Q.931 to immediately generate a response message that may cause the peer to see the P/F bit as incorrect. Delayed passing q931_dl_indication(up) events and i-frames to Q.931 until Q.921 has completed processing the frame event. (The Q.921 SDL diagrams were designed with this assumption.) (closes issue #17360) Reported by: shawkris Patches: issue17360_v1.4.patch uploaded by rmudgett (license 664) Tested by: shawkris, rmudgett * q931.c: Prevent a CONNECT message from sending a CONNECT ACKNOWLEDGE in the wrong state. Filter the processing of the CONNECT message to prevent libpri from sending a CONNECT ACKNOWLEDGE when the call is in an inappropriate state. This can happen when we hang up an outgoing call after the other end has sent a CONNECT but before we have processed the CONNECT. (issue #17360) Reported by: shawkris Patches: issue17360_con_ack_v1.4.patch uploaded by rmudgett (license 664) * q931.c: Balance curly braces in post_handle_q931_message(). 2010-09-02 17:33 +0000 [r1955] Richard Mudgett * pri.c: Don't crash in __pri_new_tei() if a GR303 subchannel creation fails. 2010-08-30 20:49 +0000 [r1918-1929] Richard Mudgett * q921.c: Reduced fake MDL-ERROR (Z) message severity to be visible only when debugging enabled. * q921.c, q931.c: Q.921/Q.931 message debug output improvements. The Q.931 message decode debug output now will follow the correct Q.921 header decode if Q.921 message dumping is enabled. Also the Q.931 message decode will happen when the message actually goes out on the line instead of when Q.931 passes the message to Q.921. Q.921 may have to request a TEI, bring the connection up, or retransmit previous frames before it can actually send the new message. * q921.c: Q.921 improvements from comparing Q.921 SDL diagrams with implementation. * Handle sending and receiving DM response frames as needed. * Added handling of received FRMR frames. * Completed implementation of Q921_AWAITING_RELEASE state. (State is currently unreachable since we have no API to initiate sending the DISC message.) * Better NT PTMP TEI allocation. * Reduced more ERROR level severity messages so users will stop panicking when they see ERROR. This is especially true for the Q.921 MDL-ERROR messages. * Added better Q.921 visibility when normal debug message level is enabled. * q921.c: ISDN BRI does not recover from line faults Q.921 was getting stuck in state 2 (Q921_ASSIGN_AWAITING_TEI). For some reason the network was removing the TEI. Libpri then immediately tried to get a new TEI assigned. The network did not reply to the N202(3) attempts to get a new TEI. Libpri then just gave up trying but did not leave the state. Some paths in Q.921 Figure B.3 were not implemented. Q.921 now transitions to the Q921_TEI_UNASSIGNED state when the N202 count is exceeded. Q.921 will wait there until an incoming or outgoing call is attempted. * Fixed initializing the n202_counter. Not initializing the n202_counter would cause the Q921_TEI_IDENTITY_REQUEST to unexpectedly not go out and due to how state transitions were done, Q.921 would get stuck in the Q921_ASSIGN_AWAITING_TEI state. * Fixed start T202 timer fail causing Q.921 to get stuck in the Q921_ASSIGN_AWAITING_TEI state if the network did not respond to the request. * Fixed handling of Q921_TEI_IDENTITY_REMOVE to do the MDL-REMOVE primitive (q921_mdl_remove()) instead of transitioning directly to the Q921_TEI_UNASSIGNED state. Necessary state clean-up was not getting done. * Minor tweaks to q921_mdl_remove(). The worst problem was erroneously generating an error message. * Fixed potential for sending I-frames with an invalid TEI. The I-frame could have been queued when Q.921 did not have an assigned TEI. * Fixed testing of the q931_receive() return value when a UI-frame is received. (closes issue #17570) Reported by: jcovert Patches: issue17570_v1.4.11.3_v3.patch uploaded by rmudgett (license 664) issue17570_v1.4_v3.patch uploaded by rmudgett (license 664) Tested by: jcovert, rmudgett 2010-08-27 23:37 +0000 [r1894-1912] Richard Mudgett * pri.c, q931.c: Convert most references of Q931_MAX_TEI to use ARRAY_LEN(). * Minor comment correction in q931_destroycall(). * Redundant logic removal q931_destroycall(). "W && X && (Y || W && Z)" is the same as "W && X && (Y || Z)" * rose.c, rosetest.c: Add support to receive ECMA-164 2nd edition OID name ROSE messages. NOTE: To add support to send the old style name messages will require implementing them as new ROSE operation message types. NOTE: To actually use them would likely require implementing another version of the Q.SIG switch type. Like (NI1 & NI2) and (4ess & 5ess) for example. Patches: libpri37.patch uploaded by rmudgett (license 664) JIRA SWP-2100 JIRA LIBPRI-37 * pri.c: Make pri_dump_event() only print the event name. 2010-08-25 17:17 +0000 [r1883-1884] Richard Mudgett * rosetest.c: Added gripe check to rosetest for invalid operation and error codes. * q921.c: Don't silently fail to post our fake MDL-ERROR(Z). 2010-08-23 22:13 +0000 [r1878] Matthew Fredrickson * q921.c: Add silly fake MDL error Z for kicking L2 to reactivate if a DISC or DM is received in proper states on PTP links. 2010-08-06 18:35 +0000 [r1852-1853] Richard Mudgett * q931.c: No audio on inbound J1 calls. Incoming calls specifying the channel using a slot map could not negotiate a B channel correctly. Libpri historically has handled this as an any channel request. However, when chan_dahdi picked a new channel, libpri sent out the recorded slot map and not the new channel selected. Thus the two endpoints would be attached to different B channels and the parties would not hear anything or would hear the wrong parties. This patch restores the historical preference of sending out the channel id using the channel number method if a channel number is available. JIRA LIBPRI-35 Patches: libpri-35_v1.4.11.3.patch uploaded by rmudgett (license 664) libpri-35_v1.4.patch uploaded by rmudgett (license 664) Tested by: rmudgett * q921.c: Learn (AGAIN!) why state 7 and state 8 transitions were suppressed. 2010-08-03 23:04 +0000 [r1842-1848] Richard Mudgett * q921.c, q931.c: Improved Q.921/Q.931 debug output. * Debug output for a sent Q.931 message in TE PTMP now uses the best available TEI number instead of always using 127. It could still be wrong if layer 2 does not have a TEI assigned. * Q.921 debug output is grouped better so a decoded message is not split by a blank line. * The Q.921 state is also decoded to a name. * q921.c: Q921_TIMER_RECOVERY SDL issue in q921_rnr_rx() Added missing code specified by Q.921 (Figure B.8 Page 85) when receive RNR in "Timer Recovery" state. (closes issue #16791) Reported by: alecdavis Patches: rnr_timer_recovery.diff.txt uploaded by alecdavis (license 585) * q921.c: Renamed local struct pri *pri variables to struct pri *ctrl in q921.c. The context tagging for my editor is much happier now that the struct and the variable do not have the same name. (At least for this file.) 2010-07-22 17:59 +0000 [r1836] Richard Mudgett * pri.c, libpri.h: Add pri_new_bri_cb() API - Create BRI D-channel with user defined I/O callbacks and data There currently exists a pri_new_cb() API call that allows you to create a PRI with user-defined I/O read and write callbacks, and option userdata. Add the same capability for BRI interfaces by adding a pri_new_bri_cb() API function. (closes issue #16477) Reported by: nic_bellamy Patches: pri_new_bri_cb_api.patch uploaded by nic bellamy (license 299) (with minor cosmetic changes) 2010-07-16 22:55 +0000 [r1828-1833] Richard Mudgett * pritest.c, Makefile: pritest hadn't been ported to DAHDI pritest hadn't been ported to DAHDI, made the trivial changes to make it work. (I haven't found the equivalent to zap_playf, so changed the behaviour to an echo box) (closes issue #17274) Reported by: horape Patches: pritest.diff uploaded by horape (license 1035) (with minor cosmetic changes) * rosetest.c: Only need to output one version of the version message. * rosetest.c: Added a libpri version output line as a sanity check for rosetest. * rosetest.c, rose_qsig_name.c: Calling name not successfully processed on inbound QSIG PRI calls from Mitel PBX The alternate form of the Q.SIG Name sequence that allows manufacturer extensions for CallingName, CalledName, ConnectedName, and BusyName was not consuming the next ASN.1 tag. The code that processed the ASN.1 Name structure was then using a stale tag value. The stale tag value was then rejected with the "Did not expect" message. I have added a test case using the supplied PRI debug output to the rosetest utility to verify that this alternate encoding is tested in the future. (closes issue #17619) Reported by: jims8650 Patches: issue17619_v1.4.11.3.patch uploaded by rmudgett (license 664) Tested by: rmudgett 2010-06-29 16:15 +0000 [r1823] Richard Mudgett * pri_internal.h, q931.c: [regression] Calling Number assignment logic change in libpri 1.4.11. Restored the old behaviour if there is more than one calling number in the incoming SETUP message. A network provided number is reported as ANI. (closes issue #17495) Reported by: ibercom Patches: issue_17495_v1.4.11.2.patch uploaded by rmudgett (license 664) issue_17495_v1.4.patch uploaded by rmudgett (license 664) Tested by: ibercom 2010-06-25 20:22 +0000 [r1818] Richard Mudgett * q931.c: PRI: XXX Missing handling for mandatory IE 12 The switch is sending the Connected Address ie (12) and there was no handler for that ie. That is why the reporter was getting the "Missing mandatory ie" message. The simple fix is to just add the missing receive handler for that ie. Since connected line (COLP) functionality has been added and this is just an alternate for the Connected Number ie (0x4C), the handler was already written. I also changed the ie name to what Q.931 calls it: Connected Address. Also some minor code clean up in q931_handle_ie() and ie2str(). JIRA SWP-1678 (closes issue #16915) Reported by: kobaz 2010-06-04 22:45 +0000 [r1810] Richard Mudgett * pri_facility.c: Q.SIG calling name in FACILITY message not reported to the upper layer. Q.SIG can send the CallingName, CalledName, and ConnectedName in stand alone FACILITY messages. If the CallingName was not sent in the SETUP message, the caller id name was not reported to the upper layer. (closes issue #17458) Reported by: jsmith Patches: issue17458_post_qsig_name.patch uploaded by rmudgett (license 664) issue17458_post_qsig_name_v1.4.11.1.patch uploaded by rmudgett (license 664) Tested by: rmudgett, jsmith 2010-06-01 22:59 +0000 [r1785-1794] Richard Mudgett * pri.c: Inband disconnect setting does nothing on BRI spans. The acceptinbanddisconnect flag is not inherited when creating a new TEI and thus rendering the setting (and its respective equivalent in Asterisk) a no-op on BRI setups. (closes issue #15265) Reported by: paravoid Patches: inband-bri.diff uploaded by paravoid (license 200) Tested by: paravoid * rose.c, rosetest.c, q931.c: Multi component FACILITY messages only process the first component. The code was only processing the first ROSE component in the facility message. I also updated rosetest.c to have a multiple component ROSE message test. (closes issue #17428) Reported by: patrol-cz Patches: issue17428.patch uploaded by rmudgett (license 664) Tested by: rmudgett, patrol-cz 2010-05-28 22:34 +0000 [r1776] David Vossel * pri_internal.h, pri_aoc.c, q931.c, pri_facility.c, pri_facility.h, libpri.h: support for sending ETSI advice of charge Review: https://reviewboard.asterisk.org/r/619/ 2010-05-28 21:50 +0000 [r1723-1767] Richard Mudgett * rose_etsi_mwi.c (added), rose.c, rosetest.c, rose_internal.h, Makefile, rose.h, pri_facility.c, libpri.h, rose_qsig_mwi.c: ETSI Message Waiting Indication (MWI) support. Add the ability to report waiting messages to ISDN endpoints (phones). Relevant specification: EN 300 650 and EN 300 745 Review: https://reviewboard.asterisk.org/r/600/ * rose.c, rosetest.c, pri_internal.h, rose.h, pri_facility.c, libpri.h: ETSI Malicious Call ID support. Add the ability to report malicious callers. Relevant specification: EN 300 180 Review: https://reviewboard.asterisk.org/r/575/ * pri.c, pri_internal.h, pri_q931.h, q931.c, libpri.h: ETSI Call Waiting support. Add the ability to announce a call to an endpoint when there are no B channels available. A call waiting call is a SETUP message with no B channel selected. Relevant specification: EN 300 056, EN 300 057, EN 300 058 Review: https://reviewboard.asterisk.org/r/569/ * pri_internal.h, pri_aoc.c (added), Makefile, pri_facility.c, rose_etsi_aoc.c, pri_facility.h, libpri.h: ETSI Advice Of Charge (AOC) event reporting. This feature passes ETSI AOC-S, AOC-D, and AOC-E message information to the upper layer (i.e. Asterisk) for processing. Relevant specification: EN 300 182 Consideration was made for the possible future addition of Q.SIG AOC support (ECMA-212) with the events passed to the upper layer. Review: https://reviewboard.asterisk.org/r/538/ * pri.c, pri_internal.h, q931.c, pri_facility.c, pri_facility.h, libpri.h: Added ETSI Explicit Call Transfer (ECT) support. Added ability to send and receive ETSI ECT messages to eliminate tromboned calls. Added ETSI support to an existing API call to send ECT messages when the upper level indicates to transfer specified calls. The libpri API was extended to indicate to the upper layer that the far end requests the transfer of the indicated calls. Review: https://reviewboard.asterisk.org/r/521/ 2010-05-26 16:01 +0000 [r1714] Richard Mudgett * pri.c, doc/cc_ptmp_monitor_flattened.fsm, pri_internal.h, Makefile, q931.c, doc/cc_ptp_agent.fsm, doc/cc_qsig_agent.fsm, pri_cc.c (added), rose_qsig_cc.c (added), prisched.c, doc/cc_ptmp_agent_flattened.fsm, rose_etsi_cc.c (added), doc/cc_ptmp_monitor.fsm, rosetest.c, rose.c, rose_internal.h, doc/cc_ptmp_agent.fsm, pri_q931.h, doc/cc_qsig_monitor_flattened.fsm, doc/cc_ptp_monitor_flattened.fsm, rose.h, pri_facility.c, pri_facility.h, doc/cc_qsig_agent_flattened.fsm, doc/cc_ptp_agent_flattened.fsm, doc (added), libpri.h, doc/cc_qsig_monitor.fsm, doc/cc_ptp_monitor.fsm: 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/ 2010-05-20 Russell Bryant * libpri 1.4.11 released. 2010-05-19 21:50 +0000 [r1703] Richard Mudgett * q931.c: T309 should not do anything with the global call reference call record. 2010-05-19 21:30 +0000 [r1702] Matthew Fredrickson * q921.c: It's amazing what a tiny bug in the Q.921 SDL diagram can do to cause trouble.... Fix issue where V_R was not reset and N_R was consequentially transmitted incorrectly. Particularly in layer 2 initiated re-establishments. 2010-05-11 22:14 +0000 [r1688] Richard Mudgett * pri.c, pri_internal.h, q931.c, libpri.h: Dialing an invalid extension causes incomplete hangup sequence. Revision -r1489 corrected a deviation from Q.931 Section 5.3.2. However, this resulted in an unexpected behaviour change to the upper layer (Asterisk). This change restores the legacy hangup behaviour if the new API call is not used. Use pri_hangup_fix_enable() to follow Q.931 Section 5.3.2 call hangup better. (closes issue #17104) Reported by: shawkris Tested by: rmudgett 2010-04-26 19:54 +0000 [r1664-1675] Richard Mudgett * q931.c: Simplified some protocol discriminator handling code. * q931.c: Garbage on the end of Q.931 messages causing calls to fail to connect. The DAHDI driver had a bug where an extra byte appeared on the end of Q.931 messages. This garbage byte caused the message to be discarded with the diagnostic "XXX Message longer than it should be?? XXX". The Q.931 message will no longer be discarded if there were earlier ie's in the message. This patch also addresses the potential problem of reading beyond the buffer when trying to parse the garbage data. Thanks to roeften for the base patch. (closes issue #14378) Reported by: timking * q921.c: Avoid using a cast. * q931.c: Cleanup some pri debug output line presentation. 2010-04-19 22:40 +0000 [r1625-1630] Richard Mudgett * pri_internal.h, pri_q931.h, q931.c, pridump.c: PTMP NT mode call reference value ambiguity. Since the TE side can pick CR values independently, the TE CR needs to be qualified by TEI to distinguish CR values from other devices. Without doing this, multiple phones on the BRI line will have intermittent call failures. JIRA LIBPRI-30 Also eliminated some wierdness in q931_status() and several places where it is called. * q921.c: Fix potential crash when pridump.c calls q921_dump() with NULL pri ptr. 2010-04-15 18:43 +0000 [r1596] Richard Mudgett * pri.c, pri_internal.h: Make some internal routines available to other libpri components. 2010-04-09 21:43 +0000 [r1577] Richard Mudgett * q931.c: Only one PROCEEDING message per call please. 2010-03-18 15:50 +0000 [r1534-1547] Richard Mudgett * q931.c: Move a comment closer to where it has relevance. * pri.c, pri_internal.h, q921.c, q931.c, pri_facility.c: Miscellaneous simple reorganization. 1) Make PRI_MASTER() no longer check for a NULL parameter. It is the caller's responsibility. Not many callers could have passed a NULL without crashing before or after anyway. 2) Replace calls to q931_is_ptmp() with PTMP_MODE(). They were equivalent. 3) Made the following boolean config options bit fields: sendfacility, overlapdial, chan_mapping_logical, and service_message_support. 2010-03-02 23:47 +0000 [r1511] Richard Mudgett * pri.c, pri_internal.h, q921.c: Restore ability to change the Q.921 K value. The Q.921 rewrite only used value of PRI_TIMER_K right after it was set to the default. The Q.921 window size was thus no longer alterable by the user. (closes issue #16909) Reported by: alecdavis Patches: pritimer.libpri.diff.txt uploaded by alecdavis (license 585) Tested by: alecdavis 2010-02-11 21:47 +0000 [r1488-1489] Richard Mudgett * q931.c: Must send DISCONNECT if we have sent a response to a SETUP message. Q.931 Section 5.3.2 a) says we send a RELEASE_COMPLETE to reject a call SETUP if we have not already sent a message in response to the SETUP message. * pri.c, libpri.h: Minor comment changes. 2010-02-11 17:35 +0000 [r1482] Wendell Thompson * Makefile: Added CPUARCH option for selecting a 32-bit build from the command line. 2010-02-08 23:29 +0000 [r1470-1476] Matthew Fredrickson * q921.c: Revert useless check of pri->t200_timer value, since scheduler routines check the value anyways. * q921.c: Make sure we set the l3initiated flag when PTP links are attempted to be re-established 2010-02-05 23:34 +0000 [r1464] Richard Mudgett * pri.c: pri_restart() is no longer needed since the Q.921 rewrite. Don't output error message for a deprecated function. 2010-01-29 21:55 +0000 [r1457] Matthew Fredrickson * q921.c: Sense of statement was inverted from what it should have been. Might have caused false T200 operation on reception of I-frames. 2010-01-29 19:32 +0000 [r1451] Richard Mudgett * q921.c: Only set eres if there actually is an event to pass up. (issue 16713) 2010-01-29 17:27 +0000 [r1445] Matthew Fredrickson * q921.c: Fix bug in which an event was lost if an I-frame was received during a timer recovery state (related to #16713) 2010-01-26 21:04 +0000 [r1439] Richard Mudgett * q921.c: Don't be so noisy when D channel is down. 2010-01-19 21:53 +0000 [r1426] Richard Mudgett * q921.c, q931.c: Fix TE PTMP side sending FACILITY messages on the dummy call reference. Only the NT PTMP side can send Q.931 broadcast messages. Also removed an inaccurate comment in Q.921 and made q921_mdl_handle_error_callback() call the correct struct pri free function. 2010-01-15 18:28 +0000 [r1414] Richard Mudgett * q921.c, q931.c: Make some debugging messages conditional and some minor reformating changes. 2010-01-13 19:37 +0000 [r1406] Matthew Fredrickson * pri.c, pri_internal.h, pri_q921.h, Makefile, pri_q931.h, q921.c, q931.c: Merge of Q.921 rewrite branch for wider testing. 2009-12-09 20:59 +0000 [r1374] Richard Mudgett * q931.c: Extracted q931_fill_ring_event() from post_handle_q931_message(). Done so it is easier to see what was done in ccbs branch. 2009-11-21 02:40 +0000 [r1345-1351] Richard Mudgett * pri.c, pri_internal.h: Fix debug output so built up output lines are readable again. A recent change to Asterisk put the span number at the begining of each line. This is a good thing if you need to debug multiple spans or forget which span you are debugging. Unfortunately, any pri_message() output that is not a complete line is messed up. The pri_message() function now will accumulate line output until a '\n' is seen on the end. * pri_internal.h, q931.c, pri_facility.c: Delay processing of facility ie's after all other ie's are processed. * Some ROSE message processing depends on the presence of other ies. The DivertingLegInformation1, and 3 messages will be used as the default connected line number if the connected number ie is not present. The redirecting number ie is used as a default to the redirecting number in the DivertingLegInformation2 message if the ROSE message does not contain it and the redirecting number ie is present. * Some ROSE message processing depends upon other ie values. The StatusRequest, CCBS-T-Call, and CcRingout messages collectively need the BC, HLC, LLC, called number, called subaddress, calling number, and calling subaddress ie information to be available. 2009-11-18 00:36 +0000 [r1331] Richard Mudgett * q931.c: Merged revision 1328 from https://origsvn.digium.com/svn/libpri/team/mattf/libpri-1.4-q921-rewrite .......... r1328 | mattf | 2009-11-17 15:16:11 -0600 (Tue, 17 Nov 2009) | 1 line outboundbroadcast isn't set at this time, since it is set after the message is transmited, so we must use other criteria to determine the need for broadcast on a setup .......... 2009-11-14 00:20 +0000 [r1310-1322] Richard Mudgett * pri.c, pri_internal.h, q931.c, pri_facility.c, pri_facility.h, libpri.h: Reimplement callback mechanism to handle APDU response messages that we care about. 1) No sent messages will remain in the APDU queue unless they have an active timer to remove them. The dummy call reference call and global call reference call structures will not act like a memory leak to sent messages. 2) The new T-RESPONSE timer will be the generic response guard if the standards do not otherwise specify a timer for a message response. 3) The callback will be called. If it is called because of a response message, then the callback has an opportunity to indicate if more responses are expected. * libpri.h: We now have 32 timers. No need to reserve minimum space anymore. * pri_internal.h, pri_facility.c: There must be only one source for the invoke id values per D channel group. If there are potentially multiple sources for the invoke id sequence then we could get confused if there are multiple outstanding messages with the same invoke id that get responses. 2009-11-11 00:22 +0000 [r1291] Matthew Fredrickson * pri_internal.h, q921.c: Make sure we also revive links for PRIs, not just PTMP TE BRIs when we get a disconnect message 2009-11-10 21:51 +0000 [r1283] Richard Mudgett * pri_internal.h, q931.c, pri_facility.c, pri_facility.h: The facility ie queue needs to remove facilities that have been sent. The facility ie queue needs to remove facilities that have been sent. Otherwise, the queue just grows until the call is terminated. AOC messages can clog the queue during a long call and the dummy call reference may never be deleted. Also removed unneeded elements of struct apdu_event. The callback function was not a good idea since many facility messages do not have responses and the callback would prevents removal of events from the list. 2009-11-10 20:25 +0000 [r1276] Matthew Fredrickson * q921.c: Re-add back in support for TE initiated layer 2 activation 2009-11-10 19:27 +0000 [r1268-1275] Richard Mudgett * pri.c, pri_internal.h, q931.c, libpri.h: Add dummy call reference support. Fixes problem where PTMP NT mode responds erroneously to a FACILITY message from a phone on the dummy call reference. LibPRI behaved as if the dummy call reference were an invalid call reference and proceeded to respond on the global call reference. * pri_internal.h: Remove unused callingsubaddr[]. 2009-11-03 17:19 +0000 [r1255-1261] Richard Mudgett * pri_q931.h, q931.c: Unknown IE 50 (cs5, Unknown Information Element) Add code to recognize the code set 5 ie 50 (calling party category) to suppress the unknown IE message. (closes issue #13828) Reported by: fdecher Patches: libpri_ie50_cs5-trunk.diff3.txt uploaded by alecdavis (license 585) Tested by: alecdavis * q931.c: NT PTMP did not report busy when calling a busy phone. The caller would not get a busy indication when calling a busy phone. Timer T303 is not supposed to be stopped when RELEASE_COMPLETE received. When T303 expires we will now report the last clearing cause code to the caller if we received one. 2009-10-23 23:47 +0000 [r1242-1249] Richard Mudgett * pri_internal.h, pri_facility.c: Add subaddress handling to existing messages and functions. Connected line updates when transfering calls does not completely support subaddresses yet. * pri.c, pri_internal.h, pri_q921.h, pri_q931.h, q921.c, q931.c, pri_facility.c, pri_facility.h, libpri.h: Add BRI PTMP NT mode, HOLD/RETRIEVE, Call rerouting/deflection, and keypad facility support. * Added support for BRI PTMP NT mode. (Overlap dialing NT -> TE not supported.) * Added handling of received HOLD/RETRIEVE messages and the optional ability to transfer a held call on disconnect similar to an analog phone. * Added CallRerouting/CallDeflection support for Q.SIG, ETSI PTP, ETSI PTMP. Will reroute/deflect an outgoing call when receive the message. Can use the DAHDISendCallreroutingFacility to send the message for the supported switches. * Added ability to send/receive keypad digits in the SETUP message. Send keypad digits in SETUP message: Dial(DAHDI/g1[/K][/extension]) Access any received keypad digits in SETUP message by: ${CHANNEL(keypad_digits)} (closes issue #15048) Tested by: rmudgett, mattf 2009-10-22 16:16 +0000 [r1230] Richard Mudgett * pri.c, pri_internal.h, pri_q931.h, q931.c, libpri.h: Add support for calling and called subaddress. Partial support for COLP subaddress. The Telecom Specs in NZ suggests that SUB ADDRESS is always on, so doing "desk to desk" between offices each with an asterisk box over the ISDN should then be possible, without a whole load of DDI numbers required. (closes issue #15604) Reported by: alecdavis Patches: libpri_subaddr_trunk.diff11.txt uploaded by alecdavis (license 585) Some minor modificatons were made. Tested by: alecdavis, rmudgett Review: https://reviewboard.asterisk.org/r/406/ 2009-10-19 22:49 +0000 [r1219-1220] Richard Mudgett * q931.c: Fix call state ie transmission. Sending a STATUS message failed to include the call state ie for some reason. We will now always send a call state ie when a message ie list includes one. * q931.c: Fix comparision of invalid party name and number structs in comparison functions. 2009-10-15 22:34 +0000 [r1212] Richard Mudgett * pri_facility.c: Take diverted-to-number from DivLegInfo1 and use it as connected number. If no connected number is signaled in the CONNECT message we will use the last diverted to number. (issue #14292) Reported by: tomaso Patches: divleginfo1_to_connectednum.patch uploaded by tomaso (license 564) (Used as a guide since it no longer will apply.) (This patch is unrelated to the issue.) 2009-10-14 19:03 +0000 [r1191-1205] Richard Mudgett * q931.c: In q931_getcall(): Simplify test and add related switch types. * q931.c: Reduce future conflicts when adding ie's to the SETUP message. 2009-10-12 17:17 +0000 [r1177] Richard Mudgett * pri.c, pri_internal.h, pri_q931.h, q931.c, pri_facility.c: Miscellaneous changes: * Removed unnecessary Q931_IE_CONNECTED_NUM ie from setup_ack_ies[]. * Added internal state Q931_CALL_STATE_NOT_SET to Q.931 state enum. * Made q931_is_ptmp() take a const pointer. * pri_facility.c: Some preparations for subaddressing. * pri.c: Eliminate use of a magic number. 2009-10-09 23:20 +0000 [r1169] Richard Mudgett * q931.c: Conditional out noisy and redundant ASN.1 parse dump of facility ie contents. 1) Outgoing messages have the facility ie ASN.1 decoded and dumped when the ie is added to the message. The whole message is then dumped. 2) Incoming messages have the facility ie ASN.1 decoded and dumped when the ie is processed. The whole message has already been dumped. 2009-10-07 18:34 +0000 [r1151-1152] Richard Mudgett * pri_facility.c: Sent Q.SIG call rerouting message fixes. a) Account for pSS1InfoElement where the bearer capability for the new rerouted call is described. The call could be a fax or data call, let's use the appropriate signaling to avoid call rejects on the other end. b) Handle calling number appropriately, number can be prohibited or non-existent. c) Add calling name if available. d) Use the diversion counter from DivLegInfo2 (if was present in the incoming Setup) and increment. (issue #14292) Reported by: tomaso Patches: libpri-1.4-2009-01-29-rerouting-0.1.9.patch uploaded by tomaso (license 564) (Used as a guide since it no longer will apply.) (This patch is unrelated to the issue.) * pri.c, pri_q921.h, q921.c: Merged revisions 1143, 1144 from https://origsvn.digium.com/svn/libpri/tags/1.4.10.2 .......... r1144 | mattf | 2009-09-29 10:32:23 -0500 (Tue, 29 Sep 2009) | 1 line This fix is more like the fix that was used to resolve the issue for the PRI case .......... r1143 | mattf | 2009-09-28 14:07:01 -0500 (Mon, 28 Sep 2009) | 1 line Changes for 1.4.10.2 .......... 2009-09-22 17:12 +0000 [r1120] Jeff Peeler * q931.c: Fix call reference to be associated with the D channel message was received The problem is that once a call reference was associated with a particular D channel, it always was. This created an issue with NFAS when the secondary D channel became active as the messages were still being sent on the non-active D channel. (closes issue #14959) Reported by: remiq Patches: bug14959.patch uploaded by jpeeler (license 325) Tested by: remiq 2009-09-22 02:23 +0000 [r1107] Richard Mudgett * pri.c: Update pri_event2str() to current defined events. 2009-09-18 00:31 +0000 [r1097] Richard Mudgett * q931.c: Another place where timeout events with subcmds should clear any old subcmds. 2009-09-15 22:24 +0000 [r1084] Richard Mudgett * q931.c: Timeout events with subcmds should clear any old subcmds. 2009-09-14 22:32 +0000 [r1077] Matthew Fredrickson * q921.c: Output multiline output with multiple calls to pri_message, so that logs look normal in Asterisk 2009-09-13 22:54 +0000 [r1072] Dwayne M. Hubbard * pri_q931.h, q931.c: Add SERVICE message support for the 'national' switchtype This set of changes integrates SERVICE message support for the 'national' switchtype. The 'national' switchtype uses the 0x43 protocol discriminator. The 'national' SERVICE/SERVICE ACKNOWLEDGE and AT&T SERVICE/SERVICE ACKNOWLEDGE message values are opposite of each other. This is handled by first determining which protocol discriminator is in use, then responding with the appropriate SERVICE ACKNOWLEDGE value. AT&T SERVICE messages use the 0x3 protocol discriminator. (closes issue #15803) Reported by: dhubbard Review: https://reviewboard.asterisk.org/r/347/ 2009-09-02 20:19 +0000 [r1059-1061] Richard Mudgett * pri_internal.h, pri_q931.h, q931.c: Converted Q931_CALL_STATE_xxx defines to an enum. * q921.c, q931.c: Optimized calls to pri_schedule_del(). There is no need to test if a scheduled event is running before calling pri_schedule_del(). * prisched.c: Cleaned up scheduled events handling code. * Fixed pri_schedule_event() to return 0 on error instead of -1. Zero is a safer value to return. Users would not think that a timer was scheduled. * Fixed potential for pri_schedule_del() to write out of bounds of pri_sched[]. The out of bounds access could occur when pri_schedule_event() returned -1. * Made use all pri_sched[] entries. pri_sched[0] was previously unused. * Removed some unneeded code and recursion since scheduling only runs on master D channel structures. * Added doxygen comments. * Renamed struct pri *pri variables to struct pri *ctrl in this file. 2009-08-31 22:57 +0000 [r1042-1051] Richard Mudgett * q931.c, libpri.h: Make event channel parameter encoding consistent. Also make sure that service maintenance messages have the channel id parameters reinitialized for each message since they are sent over the global call reference. * pri.c, pri_internal.h, q931.c, libpri.h: Split justsignalling into cis_call and cis_auto_disconnect functionality. 2009-08-26 15:24 +0000 [r1006-1028] Richard Mudgett * q931.c: Fix BRI PTP broken by -r790. * q931.c: Put connected name in display ie for CONNECT message. * q931.c: Fix ie ordering in some ie lists for send_message(). * q931.c: Make dump_channel_id() handle variable length fields. Also did some other minor miscellaneous changes. * q931.c: Make sure reversecharge is initialized. 2009-08-21 19:51 +0000 [r1000] Jason Parker * Makefile: Add -n to ldconfig on HURD too. (closes issue #15130) Reported by: tzafrir Patches: osarch_hurd.diff uploaded by tzafrir (license 46) 2009-08-20 15:52 +0000 [r994] Richard Mudgett * q931.c: Made the call state to string table use the state defines. 2009-08-18 23:53 +0000 [r982] Richard Mudgett * rose.c, rosetest.c, rose_internal.h, pri.c, pri_internal.h, Makefile, q931.c, rose.h, rose_etsi_diversion.c (added), pri_facility.c, pri_facility.h, libpri.h, rose_etsi_ect.c (added): Add COLP support to libpri for ETSI PTP, ETSI PTMP, and Q.SIG. Add Connected Line Presentation (COLP) support to chan_dahdi/libpri as an addition to issue 8824. This is the libpri portion. COLP support is now available for ETSI PTP, ETSI PTMP, and Q.SIG with this patch. (closes issue #14068) Tested by: rmudgett Review: https://reviewboard.asterisk.org/r/339/ 2009-08-18 20:59 +0000 [r976] Jeff Peeler * Makefile: Allow custom CPU optimization flags Added make variable LIBPRI_OPT to set optimization level. By default the optimization level is now set to -O2. (closes issue #12676) Reported by: tzafrir Patches: libpri_opt.diff uploaded by tzafrir (license 46) 2009-08-07 15:53 +0000 [r968] Richard Mudgett * rose_qsig_diversion.c: Corrected standard document reference. 2009-07-23 20:53 +0000 [r952] Richard Mudgett * pri_facility.c: Fixed potential NULL pointer dereference. 2009-06-26 19:50 +0000 [r914-921] Richard Mudgett * pri.c: Fix potential buffer overflow in pri_dump_info_str(). * Created pri_snprintf() to prevent buffer overflow in pri_dump_info_str(). * Extracted timer name to timer number table from pri_timer2idx() so pri_dump_info_str() can use it. * pri_internal.h: Eliminate local version of PRI_MAX_TIMERS. * libpri.h: Doxygenify the timer comments. * pri_internal.h, q931.c: Added printf format attribute to pri_message() and pri_error() and fixed some detected errors. 2009-06-25 18:53 +0000 [r907] Sean Bright * pri.c, pri_internal.h, q931.c, libpri.h: Add support for sending Reverse Charging Indication IE on ISDN PRI. Add the ability to transmit a Reverse Charging Indication IE during a SETUP message. In passing, re-work some of the receive logic to be forwards compatible with new RCI values that may be added in the future. Also removed the PRI_REVERSECHARGE_SUPPORT define that I added on the last commit since we can just check for PRI_REVERSECHARGE_NONE or _REQUESTED on the Asterisk side to determine support for this. Special thanks to rmudgett who could have written this in half the time he spent reviewing it, but instead talked me through it. Much appreciated! (issue #13760) Reported by: mrgabu Review: https://reviewboard.asterisk.org/r/292/ 2009-06-25 17:35 +0000 [r894-901] Richard Mudgett * libpri.h: Convert PRI_TIMER_xxx to an enum so PRI_MAX_TIMERS can be automatically adjusted. * pri.c, pri_timers.h (removed), libpri.h: Make it easier to add more timers/counters and vary the defaults based upon switchtype. 2009-06-24 18:19 +0000 [r878-885] Sean Bright * pri_internal.h, q931.c, libpri.h: Capture and expose the Reverse Charging Indication IE on ISDN PRI. (issue #13760) Reported by: mrgabu Patches: 20090619_libpri_1.4.patch uploaded by seanbright (license 71) Tested by: seanbright, pruonckk Review: https://reviewboard.asterisk.org/r/291/ * pri_internal.h, q931.c, libpri.h: Revert unintentional changes * pri_internal.h, q931.c, /, libpri.h: Set reviewboard property. 2009-06-12 14:29 +0000 [r865] Richard Mudgett * pri_facility.c: Miscellaneous minor changes. 2009-06-09 19:47 +0000 [r859] Matthew Fredrickson * q921.c: There are two changes in this commit that are bug fixes for various Q.921 issues found in internal testing. Both were exposed/introduced by the TBR4 compliance patch for bug #12861, in changing how retransmissions and in how the transmission queue was maintained. TX-RX message flow and acknowledgement was severely restricted, since the patch changed the behavior so that pending untransmitted frames would not actually be send until the next RR was received in normal circumstances, or REJ when a reject frame was received. On busy links, this can severly limit the amount of useful traffic that is sent, and can slow down message transmission. Until someone can point out where in Q.921 it is mandated for us to wait for RR frames to start sending untransmitted messages, the first change is to allow us to send untransmitted frames when we receive new I frames as well, with updated N(R). The other bug fixed is a situation caused by the restricted traffic flow, if an outside process tries to send an I-frame asynchronous to an RR frame, when the transmit window was previously closed and then opened up but an RR has not been received yet. A bug was found with the integration of the old transmit code with the new reject handling code which caused the new frame to be sent immediately, regardless if there were any pending untransmitted I-frames in the queue to be sent and causing an out of order I-frame to be sent to the other side. This bug is also fixed in this patch. 2009-06-03 22:51 +0000 [r836-848] Richard Mudgett * asn1_primitive.c: Made ASN.1 memory dump also display printable characters. * q931.c: Renamed callstate2str() to q931_call_state_str(). * q931.c: Made transmit_facility() debug message indiate to which message the facility ie is being added. 2009-05-29 15:39 +0000 [r824] Richard Mudgett * q931.c: Renamed local struct pri *pri variables to struct pri *ctrl in q931.c. The context tagging for my editor is much happier now that the struct and the variable do not have the same name. (At least for this file.) 2009-05-20 15:03 +0000 [r804] Richard Mudgett * q931.c: Removed usage of FUNC_DUMP(), FUNC_RECV(), and FUNC_SEND() defines. They did not really help and hindered easy lookup of parameter types. 2009-05-13 15:17 +0000 [r798] Kevin P. Fleming * pri.c, pri_q931.h, q931.c, libpri.h: Add 'const' qualifier to character string argument to keypad facility API calls These API calls do not modify the string supplied, and should not be allowed to modify it, so this patch adds a 'const' qualifier to that argument to allow the compiler to enforce this restriction (and allow callers of the API that already have a 'const' pointer to be able to pass it to this API). 2009-05-07 16:21 +0000 [r790-794] Richard Mudgett * pri_internal.h, pri_q931.h, q931.c: Minor code clean up. * q931.c: Avoid a stale pointer crash if the TE BRI TEI is removed when active calls exist. Made the q931_call record point to the master D channel control structure instead of the BRI TEI subchannel control structure. When a layer 3 message is sent, the current TEI subchannel control structure is used. 2009-05-05 22:25 +0000 [r786] Richard Mudgett * pri_q931.h: Added Q.931 call state description comments. 2009-05-01 22:47 +0000 [r782] Richard Mudgett * pri_q931.h, q921.c: Comment changes. 2009-04-21 23:32 +0000 [r766-772] Richard Mudgett * rose.c, rosetest.c, rose_internal.h, Makefile, rose.h, rose_qsig_aoc.c (added), pri_facility.c: Added Q.SIG Advice-Of-Charge encode/decode routines. * asn1_primitive.c (added), pri_internal.h, rose_qsig_name.c (added), Makefile, q931.c, asn1.h (added), rose_qsig_mwi.c (added), rose.c (added), rosetest.c (added), rose_internal.h (added), rose_other.c (added), rose.h (added), rose_address.c (added), rose_qsig_diversion.c (added), rose_etsi_aoc.c (added), pri_facility.c, rose_qsig_ct.c (added), pri_facility.h, rose_q931.c (added), libpri.h: ROSE ASN.1 facility encode and decode rewrite of existing messages. Several components are now parsed correctly. Most notably: PartyNumber and Q.SIG Name. 2009-04-14 15:05 +0000 [r732] Jeff Peeler * pri.c, pri_internal.h, pri_q921.h, pri_q931.h, q921.c, q931.c, libpri.h: Add service maintenance message support This adds support for two new message types: Service and Service Acknowledge. When a channel receives a service message it will either take the channel in or out of service and then send a service acknowledgment. Although not enforced here (enforced in chan_dahdi), the service messages are only supported with switch types 4ess/5ess. The required Asterisk changes will be coming next. (issue #3450) Reported by: cmaj 2009-04-02 19:33 +0000 [r726] Richard Mudgett * pri_facility.c: Comment changes and some string content corrections. 2009-04-18 Matthew Fredrickson * libpri 1.4.10 released. 2009-04-02 19:33 +0000 [r726] Richard Mudgett * pri_facility.c: Comment changes and some string content corrections. 2009-03-26 16:01 +0000 [r715] Richard Mudgett * libpri.h: Comment changes to note what ISDN message type causes the event. 2009-03-19 01:39 +0000 [r711] Richard Mudgett * pri_facility.c: Added pSS1InfoElement comments in qsig_cf_callrerouting(). 2009-03-13 01:05 +0000 [r705] Richard Mudgett * build_tools/make_version: Use the correct branch integrated property when generating the version string. Update the make_version file to the latest Asterisk version with the appropriate libpri required chage. 2009-03-04 20:31 +0000 [r701] Joshua Colp * q931.c: Make sure we only have 4 octects on unrestricted 64k data calls. (closes issue #14507) Reported by: jsmith Patches: 64k-data.patch uploaded by jsmith (license 15) Tested by: jsmith (closes issue #13118) Reported by: radpeter 2009-02-02 20:18 +0000 [r687] Leif Madsen * libpri.h: Fix a small spelling error. (closes issue #14375) Reported by: jeremy1 2009-01-27 23:22 +0000 [r680] Matthew Fredrickson * q921.c: A couple of last BRI fixes in libpri... don't discard pending iframes when we call the q921_dchannel_up routine, since we need to be able to send the ones that were queued up while the D-channel went down and is being reactivated. Also fix some buggy logic in the frame transmission decision code. 2009-01-22 21:48 +0000 [r675] Matthew Fredrickson * q921.c: Change behavior so that we do not send I-frames when link is down, but instead queue them up until the link comes up and send them out then. 2009-01-09 Matthew Fredrickson * libpri 1.4.9 released. 2009-01-09 17:58 +0000 [r656] Matthew Fredrickson * q921.c: Add additional case where T200 expires greater than N200 times and we need to release and reacquire the TEI. 2008-12-23 21:38 +0000 [r653] Matthew Fredrickson * q921.c, q931.c: Add some additional debug for Q.921 MDL messages as well as fix a bug in RESTART messages on BRI 2008-11-22 Matthew Fredrickson * libpri 1.4.8 released. 2008-11-22 00:34 +0000 [r645] Matthew Fredrickson * q921.c: Fix a number of Q.921 bugs, found doing TBR4 compliance testing, thanks to Tzafrir, Xorcom, and co. (#12861). Thanks! 2008-10-17 16:13 +0000 [r636] Matthew Fredrickson * pri.c, pri_internal.h, pri_q931.h, q931.c, pri_facility.c, pri_facility.h, libpri.h: Merging in additional Q.SIG features in #13454. Includes Q.SIG physical/logical channel mapping support, extended coding of Q.SIG name operations (calling name), and call rerouting support via added dialplan application. 2008-08-06 18:20 +0000 [r616] Jason Parker * Makefile: Make install paths use libdir, like libss7 2008-08-05 Kevin P. Fleming * libpri 1.4.7 released. 2008-08-05 22:18 +0000 [r611] Kevin P. Fleming * pritest.c, pri.c, pri_internal.h, q921.c, q931.c, copy_string.c, prisched.c, pri_q921.h, pri_q931.h, pri_facility.c, /, compiler.h, pridump.c, testprilib.c, pri_timers.h, pri_facility.h, libpri.h: Merged revisions 610 via svnmerge from https://origsvn.digium.com/svn/libpri/branches/1.2 ........ r610 | kpfleming | 2008-08-05 17:16:58 -0500 (Tue, 05 Aug 2008) | 2 lines clean up license headers, and explicitly grant additional permissions when used with Asterisk ........ 2008-07-30 15:43 +0000 [r606-607] Kevin P. Fleming * /: update ignore list * Makefile, mkdep (removed): use better version.c creation process, and better dependency tracking process too 2008-07-29 22:47 +0000 [r598-602] Jason Parker * build_tools/make_version: I believe this is what was meant. (it matches how Asterisk does it) * build_tools/make_version: libpri is not libss7 :D 2008-07-22 22:36 +0000 [r594] Kevin P. Fleming * pri_internal.h, libpri.h: correct comments to match code 2008-07-22 Kevin P. Fleming * Libpri 1.4.6 released. 2008-07-22 19:43 +0000 [r589] Kevin P. Fleming * pri.c, pri_internal.h, q931.c, libpri.h: rename API call and option (nothing has been released to use it yet so this should be safe) to be more accurate in describing what it does (closes issue #13042) 2008-07-11 Jason Parker * Libpri 1.4.5 released. 2008-07-11 16:44 +0000 [r574-579] Kevin P. Fleming * /: ignore generated files * pri.c, pri_internal.h, q931.c, libpri.h: modify work done for issue #10552, making the support of inband audio after RELEASE a configurable option, since it is causing problems for a number of users (closes issue #13042) 2008-06-04 17:02 +0000 [r562] Dwayne M. Hubbard * q931.c: While working on issue 3450 I noticed that the information channel selection field in the channel identification IE was displayed incorrectly when using 'pri intense debug'. I wanted another pair of eyes to look at the code because everything looked correct until Shaun Ruffell noticed the missing comma in the msg_chan_sel array. 2008-05-07 19:51 +0000 [r557] Matthew Fredrickson * pri.c, pritest.c, pri_internal.h, pri_q921.h, Makefile, q921.c, q931.c, pri_facility.c, prisched.c, testprilib.c, pri_timers.h, pri_facility.h, libpri.h: Moving trunk changes back into 1.4 2008-05-07 Matthew Fredrickson * Libpri 1.4.4 released. 2008-05-06 16:43 +0000 [r553-555] Matthew Fredrickson * q921.c, pri.c: Remove some unnecessary debug messages * q931.c: We passed TBR3 layer 2 and layer 3 testing with this commit in for BRI-TE PTMP. * q931.c: Final patch to pass certification 2008-04-22 15:30 +0000 [r546] Russell Bryant * Makefile: All versions of libpri have been installed as libpri.so.1.0, even though the API and ABI have changed over time. This patch changes the Makefile to install the library as libpri.so.1.4 to indicate that this is not compatible with previous versions. In the future, this should be changed as we make ABI changes. (closes issue #10376) Reported by: tzafrir Patches: libpri-soname.diff uploaded by tzafrir (license 46) -- with very minor changes by me 2008-03-15 19:57 +0000 [r541-543] Matthew Fredrickson * libpri.h, pri.c: Remove some useless event items * q921.c, pri_internal.h: Make sure if we're a bri and in PTMP mode that we release the TEI and try to get a new one with the other end when we lose activity in multiframe mode * q921.c, pri.c, pri_q921.h: When we recieve a UA in any TEI established state other than awaiting establishement make sure we drop the TEI and get a new one. For passing tests PC37.2 - PC41.2 in Q.921 2008-03-06 23:43 +0000 [r538-539] Matthew Fredrickson * q921.c: Make sure we are sending as command not as response * q921.c: Make sure C/R bit is correct 2008-03-01 23:30 +0000 [r531-535] Matthew Fredrickson * q921.c: Remove some (currently) useless code * q921.c, pri.c, pri_internal.h: Some more updates to add code for passing PC 27.1 in Q.921 * q921.c, pri.c: Various coding style cleanups as well as a bug fix for TEI removal * q921.c, q931.c, pri.c, pri_internal.h: Initial checkin of code to pass Q.921 PC25 and PC26 * q921.c: Add support for responding to TEI requests (Q.921 PC23.x) 2008-02-18 20:31 +0000 [r525] Matthew Fredrickson * q931.c, libpri.h: Improve transmission, receiving, and dumping of bearer capability IE (#11593) 2008-01-16 18:06 +0000 [r518] Jason Parker * Makefile, /: Add logging for 'make update' command (also fixes updates in some places). Issue #11766, initial patch by jmls. 2007-12-26 17:35 +0000 [r505] Russell Bryant * libpri.h: Remove unnecessary usage of the extern keyword. 2007-12-06 22:16 +0000 [r494-496] Russell Bryant * q921.c, q931.c, pri_facility.c, pri.c: Change malloc+memset to calloc. Also, handle allocation failures early to reduce indentation. (closes issue #11469) Reported by: eliel Patches: pri.c.patch uploaded by eliel (license 64) q931.c.patch uploaded by eliel (license 64) q921.c.patch uploaded by eliel (license 64) pri_facility.c.patch uploaded by eliel (license 64) * pri.c: Change a use of malloc+memset to calloc. Also, reduce indentation of a function by doing the check for memory allocation failure at the beginning. * pri.c: Change the table used to hold the default timers to be global and const, instead of allocating it on the stack each time the function is called. 2007-12-05 00:20 +0000 [r491-493] Matthew Fredrickson * q931.c: Improve the parameter name to better reflect its use * q921.c, q931.c: More PTMP fixes. Stand by, more changes to come * q921.c, q931.c, testprilib.c: More magic to make BRI CPE PTMP work 2007-11-21 22:30 +0000 [r486-487] Matthew Fredrickson * q931.c: Make sure we loop BRI call references for one byte CREFs instead of two * q921.c, q931.c, libpri.h, pri.c, pritest.c, pri_internal.h: Add BRI support to libpri 2007-10-13 16:00 +0000 [r473] Matthew Fredrickson * q921.c: Make sure that we only output the message handling debug when we're actually debugging 2007-09-25 21:56 +0000 [r469] Matthew Fredrickson * q931.c: Fix user-user IE order in setup message (#10705) 2007-06-20 15:18 +0000 [r428-430] Matthew Fredrickson * Makefile: Revert that back to how it was * Makefile, q921.c, q931.c, prisched.c, testprilib.c, pri_timers.h, libpri.h, pri.c, pri_internal.h, pri_q921.h: Patch to add PTMP mode (BRI support) 2007-06-06 21:59 +0000 [r417-425] Matthew Fredrickson * q931.c, pri_facility.c, pri_facility.h, pri.c, pri_internal.h: Preliminary patch plus mods for Q.SIG Path Replacement (Q.SIG version of 2BCT) #7778 2007-05-25 17:36 +0000 [r413] Matthew Fredrickson * q931.c, pri_facility.c, pri_facility.h, libpri.h: Commit path for ROSE-12 and ROSE-13 support (#9076) 2006-11-03 18:16 +0000 [r382] Matthew Fredrickson * q921.c: Remove pointless recursion. (#8281) 2008-05-07 19:51 +0000 [r557] Matthew Fredrickson * pri.c, pritest.c, pri_internal.h, pri_q921.h, Makefile, q921.c, q931.c, pri_facility.c, prisched.c, testprilib.c, pri_timers.h, pri_facility.h, libpri.h: Moving trunk changes back into 1.4 2008-05-02 20:21 +0000 [r552] Brett Bryant * Makefile, build_tools (added), build_tools/make_version (added), build_tools/make_version_c (added), libpri.h: Add new API call (pri_get_version) to show the current version of the branch for support purposes. 2008-04-16 00:21 +0000 [r544] Kevin P. Fleming * q921.c: fix the indentation in this function so that it is actually possible to read it and understand the logic and flow 2008-02-21 16:38 +0000 [r528] Joshua Colp * q931.c: If inband audio is being provided with a disconnect message let the audio be heard before releasing the channel. (closes issue #10552) Reported by: paravoid Patches: disconnect-audio.diff uploaded by paravoid (license 200) 2008-02-18 20:31 +0000 [r524] Matthew Fredrickson * q931.c, libpri.h: Improve transmission, receiving, and dumping of bearer capability IE (#11593) 2008-01-11 16:34 +0000 [r514] Matthew Fredrickson * q931.c, /: Merged revisions 513 via svnmerge from https://origsvn.digium.com/svn/libpri/branches/1.2 ........ r513 | mattf | 2008-01-11 10:33:52 -0600 (Fri, 11 Jan 2008) | 1 line We should not be parsing further into the bearer capability IE if the length does not confirm that there actually is data present ........ 2007-12-13 Russell Bryant * libpri 1.4.3 released. 2007-10-22 15:10 +0000 [r479] Kevin P. Fleming * pri_internal.h, /: Merged revisions 478 via svnmerge from https://origsvn.digium.com/svn/libpri/branches/1.2 ........ r478 | kpfleming | 2007-10-22 10:09:27 -0500 (Mon, 22 Oct 2007) | 3 lines we need to include stddef.h for 'size_t' ........ 2007-10-16 Matthew Fredrickson * libpri 1.4.2 released. 2007-10-05 16:45 +0000 [r470] Jason Parker * libpri.h: Fix an incorrect pri_event structure definition. Issue 10832, patch by flefoll 2007-09-25 21:37 +0000 [r468] Matthew Fredrickson * q931.c: Fix user-user IE order in setup message (#10705) 2007-09-14 21:32 +0000 [r465] Matthew Fredrickson * q931.c, libpri.h: Fix for #10189. Make sure we properly report the user layer 1 for H.223 and H.245 2007-09-06 15:11 +0000 [r462] Matthew Fredrickson * pri.c, pri_facility.c, /: Merged revisions 460 via svnmerge from https://origsvn.digium.com/svn/libpri/branches/1.2 ........ r460 | mattf | 2007-09-06 10:06:42 -0500 (Thu, 06 Sep 2007) | 1 line TBCT now works. It should work for NI2, 4E, and 5E. This code was tested on NI2. ........ 2007-08-27 19:21 +0000 [r446] Jason Parker * Makefile, /: Merged revisions 445 via svnmerge from https://origsvn.digium.com/svn/libpri/branches/1.2 ........ r445 | qwell | 2007-08-27 14:20:23 -0500 (Mon, 27 Aug 2007) | 2 lines Make sure we build both the static and shared modules with -fPIC. ........ 2007-08-06 19:58 +0000 [r441] Jason Parker * Makefile: Allow setting CC to something with a space, such as `make CC="gcc -m32"` Issue 10253. 2007-07-09 Russell Bryant * libpri 1.4.1 released. 2007-06-19 18:23 +0000 [r427] Matthew Fredrickson * q931.c, /: Merged revisions 426 via svnmerge from https://origsvn.digium.com/svn/libpri/branches/1.2 ........ r426 | mattf | 2007-06-19 13:22:33 -0500 (Tue, 19 Jun 2007) | 1 line Try to send pending facility messages if we receive alerting (for when we don't get proceeding) (#9651) ........ 2007-06-06 21:58 +0000 [r416-424] Matthew Fredrickson * pri_facility.c, /: Merged revisions 423 via svnmerge from https://origsvn.digium.com/svn/libpri/branches/1.2 ........ r423 | mattf | 2007-06-06 16:57:15 -0500 (Wed, 06 Jun 2007) | 1 line Oops, that should not be on one line ........ * pri_facility.c, /: Merged revisions 415 via svnmerge from https://origsvn.digium.com/svn/libpri/branches/1.2 ........ r415 | mattf | 2007-06-06 08:20:05 -0500 (Wed, 06 Jun 2007) | 2 lines Make sure we only send the NFE when we are talking QSIG ........ 2007-01-22 22:29 +0000 [r390] Matthew Fredrickson * q931.c, /: Merged revisions 389 via svnmerge from https://origsvn.digium.com/svn/libpri/branches/1.2 ........ r389 | mattf | 2007-01-22 16:20:59 -0600 (Mon, 22 Jan 2007) | 2 lines Make sure we send DISCONNECT if we reached the active state and a call is disconnected, regardless of cause code. ........ 2006-12-30 19:17 +0000 [r386] Joshua Colp * Makefile, /: Merged revisions 385 via svnmerge from https://origsvn.digium.com/svn/libpri/branches/1.2 ........ r385 | file | 2006-12-30 14:16:17 -0500 (Sat, 30 Dec 2006) | 2 lines Specify full path to restorecon instead of assuming it will be available from what is in $PATH. (issue #8670 reported by djflux) ........ 2006-12-23 Kevin P. Fleming * libpri 1.4.0 released. 2006-09-20 Kevin P. Fleming * libpri 1.4.0-beta1 released. libpri-1.4.14/build_tools/0000755000000000000000000000000012064641001014071 5ustar rootrootlibpri-1.4.14/build_tools/make_version_c0000755000000000000000000000055611006673660017024 0ustar rootroot#!/bin/sh if [ ! -f ../.flavor ]; then EXTRA="" else aadkver=`cat ../.version` aadkflavor=`cat ../.flavor` EXTRA=" (${aadkflavor} ${aadkver})" fi cat << END /* * version.c * Automatically generated */ #include "libpri.h" static const char pri_version[] = "${PRIVERSION}${EXTRA}"; const char *pri_get_version(void) { return pri_version; } END libpri-1.4.14/build_tools/make_version0000755000000000000000000000343211156330562016513 0ustar rootroot#!/bin/sh if [ -f ${1}/.version ]; then cat ${1}/.version elif [ -d .svn ]; then PARTS=`LANG=C svn info ${1} | ${GREP} URL | ${AWK} '{print $2;}' | sed -e 's:^.*/svn/libpri/::' | sed -e 's:/: :g'` BRANCH=0 TEAM=0 TAG=0 REV=`svnversion -c ${1} | cut -d: -f2` INTEGRATED=`LANG=C svn pg automerge-propname ${1}` if [ -z "${INTEGRATED}" ] ; then INTEGRATED=svnmerge-integrated fi BASE=`LANG=C svn pg ${INTEGRATED} ${1} | cut -d: -f1` if [ "${PARTS}" = "trunk" ] ; then echo SVN-trunk-r${REV} exit 0 fi for PART in $PARTS ; do if [ ${TAG} != 0 ] ; then if [ "${PART}" = "autotag_for_be" ] ; then continue fi if [ "${PART}" = "autotag_for_sx00i" ] ; then continue fi RESULT="${PART}" break fi if [ ${BRANCH} != 0 ] ; then if [ -z "${RESULT}" ] ; then RESULT="${PART}" else RESULT="${RESULT}-${PART}" fi break fi if [ ${TEAM} != 0 ] ; then if [ -z "${RESULT}" ] ; then RESULT="${PART}" else RESULT="${RESULT}-${PART}" fi continue fi if [ "${PART}" = "branches" ] ; then BRANCH=1 RESULT="branch" continue fi if [ "${PART}" = "tags" ] ; then TAG=1 continue fi if [ "${PART}" = "team" ] ; then TEAM=1 continue fi done if [ ${TAG} != 0 ] ; then echo ${RESULT} else echo SVN-${RESULT}-r${REV}${BASE:+-${BASE}} fi else echo "UNKNOWN__and_probably_unsupported" fi libpri-1.4.14/pri_q921.h0000644000000000000000000002045011713064771013307 0ustar rootroot/* * libpri: An implementation of Primary Rate ISDN * * Written by Mark Spencer * * Copyright (C) 2001, Digium, Inc. * All Rights Reserved. */ /* * 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. */ #ifndef _PRI_Q921_H #define _PRI_Q921_H #include #if defined(__linux__) #include #elif defined(__FreeBSD__) #include #define __BYTE_ORDER _BYTE_ORDER #define __BIG_ENDIAN _BIG_ENDIAN #define __LITTLE_ENDIAN _LITTLE_ENDIAN #endif /* Timer values */ #define T_WAIT_MIN 2000 #define T_WAIT_MAX 10000 #define Q921_FRAMETYPE_MASK 0x3 #define Q921_FRAMETYPE_U 0x3 #define Q921_FRAMETYPE_I 0x0 #define Q921_FRAMETYPE_S 0x1 #define Q921_TEI_GROUP 127 #define Q921_TEI_PRI 0 #define Q921_TEI_GR303_EOC_PATH 0 #define Q921_TEI_GR303_EOC_OPS 4 #define Q921_TEI_GR303_TMC_SWITCHING 0 #define Q921_TEI_GR303_TMC_CALLPROC 0 #define Q921_TEI_AUTO_FIRST 64 #define Q921_TEI_AUTO_LAST 126 #define Q921_SAPI_CALL_CTRL 0 #define Q921_SAPI_GR303_EOC 1 #define Q921_SAPI_GR303_TMC_SWITCHING 1 #define Q921_SAPI_GR303_TMC_CALLPROC 0 #define Q921_SAPI_PACKET_MODE 1 #define Q921_SAPI_X25_LAYER3 16 #define Q921_SAPI_LAYER2_MANAGEMENT 63 /*! Q.921 TEI management message type */ enum q921_tei_identity { Q921_TEI_IDENTITY_REQUEST = 1, Q921_TEI_IDENTITY_ASSIGNED = 2, Q921_TEI_IDENTITY_DENIED = 3, Q921_TEI_IDENTITY_CHECK_REQUEST = 4, Q921_TEI_IDENTITY_CHECK_RESPONSE = 5, Q921_TEI_IDENTITY_REMOVE = 6, Q921_TEI_IDENTITY_VERIFY = 7, }; typedef struct q921_header { #if __BYTE_ORDER == __BIG_ENDIAN u_int8_t sapi:6; /* Service Access Point Indentifier (always 0 for PRI) (0) */ u_int8_t c_r:1; /* Command/Response (0 if CPE, 1 if network) */ u_int8_t ea1:1; /* Extended Address (0) */ u_int8_t tei:7; /* Terminal Endpoint Identifier (0) */ u_int8_t ea2:1; /* Extended Address Bit (1) */ #else u_int8_t ea1:1; /* Extended Address (0) */ u_int8_t c_r:1; /* Command/Response (0 if CPE, 1 if network) */ u_int8_t sapi:6; /* Service Access Point Indentifier (always 0 for PRI) (0) */ u_int8_t ea2:1; /* Extended Address Bit (1) */ u_int8_t tei:7; /* Terminal Endpoint Identifier (0) */ #endif u_int8_t data[0]; /* Further data */ } __attribute__ ((packed)) q921_header; /* A Supervisory Format frame */ typedef struct q921_s { struct q921_header h; /* Header */ #if __BYTE_ORDER == __BIG_ENDIAN u_int8_t x0:4; /* Unused */ u_int8_t ss:2; /* Supervisory frame bits */ u_int8_t ft:2; /* Frame type bits (01) */ u_int8_t n_r:7; /* Number Received */ u_int8_t p_f:1; /* Poll/Final bit */ #else u_int8_t ft:2; /* Frame type bits (01) */ u_int8_t ss:2; /* Supervisory frame bits */ u_int8_t x0:4; /* Unused */ u_int8_t p_f:1; /* Poll/Final bit */ u_int8_t n_r:7; /* Number Received */ #endif u_int8_t data[0]; /* Any further data */ u_int8_t fcs[2]; /* At least an FCS */ } __attribute__ ((packed)) q921_s; /* An Unnumbered Format frame */ typedef struct q921_u { struct q921_header h; /* Header */ #if __BYTE_ORDER == __BIG_ENDIAN u_int8_t m3:3; /* Top 3 modifier bits */ u_int8_t p_f:1; /* Poll/Final bit */ u_int8_t m2:2; /* Two more modifier bits */ u_int8_t ft:2; /* Frame type bits (11) */ #else u_int8_t ft:2; /* Frame type bits (11) */ u_int8_t m2:2; /* Two more modifier bits */ u_int8_t p_f:1; /* Poll/Final bit */ u_int8_t m3:3; /* Top 3 modifier bits */ #endif u_int8_t data[0]; /* Any further data */ u_int8_t fcs[2]; /* At least an FCS */ } __attribute__ ((packed)) q921_u; /* An Information frame */ typedef struct q921_i { struct q921_header h; /* Header */ #if __BYTE_ORDER == __BIG_ENDIAN u_int8_t n_s:7; /* Number sent */ u_int8_t ft:1; /* Frame type (0) */ u_int8_t n_r:7; /* Number received */ u_int8_t p_f:1; /* Poll/Final bit */ #else u_int8_t ft:1; /* Frame type (0) */ u_int8_t n_s:7; /* Number sent */ u_int8_t p_f:1; /* Poll/Final bit */ u_int8_t n_r:7; /* Number received */ #endif u_int8_t data[0]; /* Any further data */ u_int8_t fcs[2]; /* At least an FCS */ } q921_i; typedef union { u_int8_t raw[0]; q921_u u; q921_s s; q921_i i; struct q921_header h; } q921_h; enum q921_tx_frame_status { Q921_TX_FRAME_NEVER_SENT, Q921_TX_FRAME_PUSHED_BACK, Q921_TX_FRAME_SENT, }; typedef struct q921_frame { struct q921_frame *next; /*!< Next in list */ int len; /*!< Length of header + body */ enum q921_tx_frame_status status; /*!< Tx frame status */ q921_i h; /*!< Actual frame contents. */ } q921_frame; #define Q921_INC(j) (j) = (((j) + 1) % 128) #define Q921_DEC(j) (j) = (((j) - 1) % 128) typedef enum q921_state { /* All states except Q921_DOWN are defined in Q.921 SDL diagrams */ Q921_TEI_UNASSIGNED = 1, Q921_ASSIGN_AWAITING_TEI = 2, Q921_ESTABLISH_AWAITING_TEI = 3, Q921_TEI_ASSIGNED = 4, Q921_AWAITING_ESTABLISHMENT = 5, Q921_AWAITING_RELEASE = 6, Q921_MULTI_FRAME_ESTABLISHED = 7, Q921_TIMER_RECOVERY = 8, } q921_state; /*! TEI identity check procedure states. */ enum q921_tei_check_state { /*! Not participating in the TEI check procedure. */ Q921_TEI_CHECK_NONE, /*! No reply to TEI check received. */ Q921_TEI_CHECK_DEAD, /*! Reply to TEI check received in current poll. */ Q921_TEI_CHECK_REPLY, /*! No reply to current TEI check poll received. A previous poll got a reply. */ Q921_TEI_CHECK_DEAD_REPLY, }; /*! \brief Q.921 link controller structure */ struct q921_link { /*! Next Q.921 link in the chain. */ struct q921_link *next; /*! D channel controller associated with this link. */ struct pri *ctrl; /*! * \brief Q.931 Dummy call reference call associated with this TEI. * * \note If present then this call is allocated with the D * channel control structure or the link control structure * unless this is the TE PTMP broadcast TEI or a GR303 link. */ struct q931_call *dummy_call; /*! Q.921 Re-transmission queue */ struct q921_frame *tx_queue; /*! Q.921 State */ enum q921_state state; /*! TEI identity check procedure state. */ enum q921_tei_check_state tei_check; /*! Service Access Profile Identifier (SAPI) of this link */ int sapi; /*! Terminal Endpoint Identifier (TEI) of this link */ int tei; /*! TEI assignment random indicator. */ int ri; /*! V(A) - Next I-frame sequence number needing ack */ int v_a; /*! V(S) - Next I-frame sequence number to send */ int v_s; /*! V(R) - Next I-frame sequence number expected to receive */ int v_r; /* Various timers */ /*! T-200 retransmission timer */ int t200_timer; /*! Retry Count (T200) */ int RC; int t202_timer; int n202_counter; /*! Max idle time */ int t203_timer; /*! Layer 2 persistence restart delay timer */ int restart_timer; /* MDL variables */ int mdl_timer; int mdl_error; unsigned int mdl_free_me:1; unsigned int peer_rx_busy:1; unsigned int own_rx_busy:1; unsigned int acknowledge_pending:1; unsigned int reject_exception:1; unsigned int l3_initiated:1; }; static inline int Q921_ADD(int a, int b) { return (a + b) % 128; } /* Dumps a *known good* Q.921 packet */ extern void q921_dump(struct pri *pri, q921_h *h, int len, int debugflags, int txrx); /* Bring up the D-channel */ void q921_start(struct q921_link *link); void q921_bring_layer2_up(struct pri *ctrl); //extern void q921_reset(struct pri *pri, int reset_iqueue); extern pri_event *q921_receive(struct pri *pri, q921_h *h, int len); int q921_transmit_iframe(struct q921_link *link, void *buf, int len, int cr); int q921_transmit_uiframe(struct q921_link *link, void *buf, int len); extern pri_event *q921_dchannel_up(struct pri *pri); //extern pri_event *q921_dchannel_down(struct pri *pri); #endif libpri-1.4.14/pri_internal.h0000644000000000000000000011522111772721141014424 0ustar rootroot/* * libpri: An implementation of Primary Rate ISDN * * Written by Mark Spencer * * Copyright (C) 2001, Digium, Inc. * All Rights Reserved. */ /* * 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. */ #ifndef _PRI_INTERNAL_H #define _PRI_INTERNAL_H #include #include #include "pri_q921.h" #include "pri_q931.h" #define ARRAY_LEN(arr) (sizeof(arr) / sizeof((arr)[0])) #define DBGHEAD __FILE__ ":%d %s: " #define DBGINFO __LINE__,__PRETTY_FUNCTION__ /* Forward declare some structs */ struct apdu_event; struct pri_cc_record; struct pri_sched { struct timeval when; void (*callback)(void *data); void *data; }; /* * 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 /*! Maximum length of sent display text string. (No null terminator.) */ #define MAX_DISPLAY_TEXT 80 /*! Accumulated pri_message() line until a '\n' is seen on the end. */ struct pri_msg_line { /*! Accumulated buffer used. */ unsigned length; /*! Accumulated pri_message() contents. */ char str[2048]; }; /*! \brief D channel controller structure */ struct pri { int fd; /* File descriptor for D-Channel */ pri_io_cb read_func; /* Read data callback */ pri_io_cb write_func; /* Write data callback */ void *userdata; /*! Accumulated pri_message() line. (Valid in master record only) */ struct pri_msg_line *msg_line; /*! NFAS master/primary channel if appropriate */ struct pri *master; /*! Next NFAS slaved D channel if appropriate */ struct pri *slave; 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; /*! First timer id in this timer pool. */ unsigned first_id; } sched; int debug; /* Debug stuff */ int state; /* State of D-channel */ int switchtype; /* Switch type */ int nsf; /* Network-Specific Facility (if any) */ int localtype; /* Local network type (unknown, network, cpe) */ int remotetype; /* Remote network type (unknown, network, cpe) */ int protodisc; /* Layer 3 protocol discriminator */ unsigned int nfas:1;/* TRUE if this D channel is involved with an NFAS group */ unsigned int bri:1; unsigned int acceptinbanddisconnect:1; /* Should we allow inband progress after DISCONNECT? */ unsigned int sendfacility:1; unsigned int overlapdial:1;/* TRUE if we do overlap dialing */ unsigned int chan_mapping_logical:1;/* TRUE if do not skip channel 16 (Q.SIG) */ unsigned int service_message_support:1;/* TRUE if upper layer supports SERVICE messages */ 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. */ unsigned int transfer_support:1;/* TRUE if the upper layer supports ECT */ unsigned int aoc_support:1;/* TRUE if can send AOC events to the upper layer. */ unsigned int manual_connect_ack:1;/* TRUE if the CONNECT_ACKNOWLEDGE is sent with API call */ unsigned int mcid_support:1;/* TRUE if the upper layer supports MCID */ /*! Layer 2 link control for D channel. */ struct q921_link link; /*! Layer 2 persistence option. */ enum pri_layer2_persistence l2_persistence; /*! T201 TEI Identity Check timer. */ int t201_timer; /*! Number of times T201 has expired. */ int t201_expirycnt; int cref; /* Next call reference value */ /* All ISDN Timer values */ int timers[PRI_MAX_TIMERS]; /* Used by scheduler */ int schedev; pri_event ev; /* Static event thingy */ /*! Subcommands for static event thingy. */ struct pri_subcommands subcmds; /* Q.931 calls */ struct q931_call **callpool; struct q931_call *localpool; /* q921/q931 packet counters */ unsigned int q921_txcount; unsigned int q921_rxcount; unsigned int q931_txcount; unsigned int q931_rxcount; 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. */ q931_ie *ie[MAX_FACILITY_IES]; /*! Codeset facility ie found within. */ unsigned char codeset[MAX_FACILITY_IES]; /*! Number of facility ie's in the array from the current received message. */ unsigned char count; } facility; /*! Display text policy handling options. */ struct { /*! Send display text policy option flags. */ unsigned long send; /*! Receive display text policy option flags. */ unsigned long receive; } display_flags; /*! Configured date/time ie send policy option. */ int date_time_send; }; /*! \brief Maximum name length plus null terminator (From ECMA-164) */ #define PRI_MAX_NAME_LEN (50 + 1) /*! \brief Q.SIG name information. */ struct q931_party_name { /*! \brief TRUE if name data is valid */ unsigned char valid; /*! * \brief Q.931 presentation-indicator encoded field * \note Must tollerate the Q.931 screening-indicator field values being present. */ unsigned char presentation; /*! * \brief Character set the name is using. * \details * unknown(0), * iso8859-1(1), * enum-value-withdrawn-by-ITU-T(2) * iso8859-2(3), * iso8859-3(4), * iso8859-4(5), * iso8859-5(6), * iso8859-7(7), * iso10646-BmpString(8), * iso10646-utf-8String(9) */ unsigned char char_set; /*! \brief Name data with null terminator. */ char str[PRI_MAX_NAME_LEN]; }; /*! \brief Maximum phone number (address) length plus null terminator */ #define PRI_MAX_NUMBER_LEN (31 + 1) struct q931_party_number { /*! \brief TRUE if number data is valid */ unsigned char valid; /*! \brief Q.931 presentation-indicator and screening-indicator encoded fields */ unsigned char presentation; /*! \brief Q.931 Type-Of-Number and numbering-plan encoded fields */ unsigned char plan; /*! \brief Number data with terminator. */ char str[PRI_MAX_NUMBER_LEN]; }; /*! \brief Maximum subaddress length plus null terminator */ #define PRI_MAX_SUBADDRESS_LEN (20 + 1) struct q931_party_subaddress { /*! \brief TRUE if the subaddress information is valid/present */ unsigned char valid; /*! * \brief Subaddress type. * \details * nsap(0), * user_specified(2) */ unsigned char type; /*! * \brief TRUE if odd number of address signals * \note The odd/even indicator is used when the type of subaddress is * user_specified and the coding is BCD. */ unsigned char odd_even_indicator; /*! \brief Length of the subaddress data */ unsigned char length; /*! * \brief Subaddress data with null terminator. * \note The null terminator is a convenience only since the data could be * BCD/binary and thus have a null byte as part of the contents. */ unsigned char data[PRI_MAX_SUBADDRESS_LEN]; }; struct q931_party_address { /*! \brief Subscriber phone number */ struct q931_party_number number; /*! \brief Subscriber subaddress */ struct q931_party_subaddress subaddress; }; /*! \brief Information needed to identify an endpoint in a call. */ struct q931_party_id { /*! \brief Subscriber name */ struct q931_party_name name; /*! \brief Subscriber phone number */ struct q931_party_number number; /*! \brief Subscriber subaddress */ struct q931_party_subaddress subaddress; }; enum Q931_REDIRECTING_STATE { /*! * \details * CDO-Idle/CDF-Inv-Idle */ Q931_REDIRECTING_STATE_IDLE, /*! * \details * CDF-Inv-Wait - A DivLeg2 has been received and * we are waiting for valid presentation restriction information to send. */ Q931_REDIRECTING_STATE_PENDING_TX_DIV_LEG_3, /*! * \details * CDO-Divert - A DivLeg1 has been received and * we are waiting for the presentation restriction information to come in. */ Q931_REDIRECTING_STATE_EXPECTING_RX_DIV_LEG_3, }; /*! * \brief Do not increment above this count. * \details * It is not our responsibility to enforce the maximum number of redirects. * However, we cannot allow an increment past this number without breaking things. * Besides, more than 255 redirects is probably not a good thing. */ #define PRI_MAX_REDIRECTS 0xFF /*! \brief Redirecting information struct */ struct q931_party_redirecting { enum Q931_REDIRECTING_STATE state; /*! \brief Who is redirecting the call (Sent to the party the call is redirected toward) */ struct q931_party_id from; /*! \brief Call is redirecting to a new party (Sent to the caller) */ struct q931_party_id to; /*! Originally called party (in cases of multiple redirects) */ struct q931_party_id orig_called; /*! * \brief Number of times the call was redirected * \note The call is being redirected if the count is non-zero. */ unsigned char count; /*! Original reason for redirect (in cases of multiple redirects) */ unsigned char orig_reason; /*! \brief Redirection reasons */ unsigned char reason; }; /*! \brief New call setup parameter structure */ struct pri_sr { int transmode; int channel; int exclusive; int nonisdn; struct q931_party_redirecting redirecting; struct q931_party_id caller; struct q931_party_address called; int userl1; int numcomplete; int cis_call; int cis_auto_disconnect; const char *useruserinfo; const char *keypad_digits; int transferable; int reversecharge; int aoc_charging_request; }; #define Q931_MAX_TEI 8 /*! \brief Incoming call transfer states. */ enum INCOMING_CT_STATE { /*! * \details * Incoming call transfer is not active. */ INCOMING_CT_STATE_IDLE, /*! * \details * We have seen an incoming CallTransferComplete(alerting) * so we are waiting for the expected CallTransferActive * before updating the connected line about the remote party id. */ INCOMING_CT_STATE_EXPECT_CT_ACTIVE, /*! * \details * A call transfer message came in that updated the remote party id * that we need to post a connected line update. */ INCOMING_CT_STATE_POST_CONNECTED_LINE }; /*! Call hold supplementary states. */ enum Q931_HOLD_STATE { /*! \brief No call hold activity. */ Q931_HOLD_STATE_IDLE, /*! \brief Request made to hold call. */ Q931_HOLD_STATE_HOLD_REQ, /*! \brief Request received to hold call. */ Q931_HOLD_STATE_HOLD_IND, /*! \brief Call is held. */ Q931_HOLD_STATE_CALL_HELD, /*! \brief Request made to retrieve call. */ Q931_HOLD_STATE_RETRIEVE_REQ, /*! \brief Request received to retrieve call. */ 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; /* D channel controller (master) */ struct q921_link *link; /* Q.921 link associated with this call. */ struct q931_call *next; int cr; /* Call Reference */ /* Slotmap specified (bitmap of channels 31/24-1) (Channel Identifier IE) (-1 means not specified) */ int slotmap; /* An explicit channel (Channel Identifier IE) (-1 means not specified) */ int channelno; /* An explicit DS1 (-1 means not specified) */ int ds1no; /* Whether or not the ds1 is explicitly identified or implicit. If implicit the bchan is on the same span as the current active dchan (NFAS) */ int ds1explicit; /* Channel flags (0 means none retrieved) */ int chanflags; int alive; /* Whether or not the call is alive */ int acked; /* Whether setup has been acked or not */ int sendhangupack; /* Whether or not to send a hangup ack */ int proc; /* Whether we've sent a call proceeding / alerting */ int ri; /* Restart Indicator (Restart Indicator IE) */ /*! 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; int progcode; /* Progress coding */ int progloc; /* Progress Location */ int progress; /* Progress indicator */ int progressmask; /* Progress Indicator bitmask */ int notify; /* Notification indicator. */ int causecode; /* Cause Coding */ int causeloc; /* Cause Location */ int cause; /* Cause of clearing */ enum Q931_CALL_STATE peercallstate; /* Call state of peer as reported */ enum Q931_CALL_STATE ourcallstate; /* Our call state */ enum Q931_CALL_STATE sugcallstate; /* Status call state */ int ani2; /* ANI II */ /*! Buffer for digits that come in KEYPAD_FACILITY */ char keypad_digits[32 + 1]; /*! Current dialed digits to be sent or just received. */ char overlap_digits[PRI_MAX_NUMBER_LEN]; /*! * \brief Local party ID * \details * The Caller-ID and connected-line ID are just roles the local and remote party * play while a call is being established. Which roll depends upon the direction * of the call. * Outgoing party info is to identify the local party to the other end. * (Caller-ID for originated or connected-line for answered calls.) * Incoming party info is to identify the remote party to us. * (Caller-ID for answered or connected-line for originated calls.) */ struct q931_party_id local_id; /*! * \brief Remote party ID * \details * The Caller-ID and connected-line ID are just roles the local and remote party * play while a call is being established. Which roll depends upon the direction * of the call. * Outgoing party info is to identify the local party to the other end. * (Caller-ID for originated or connected-line for answered calls.) * Incoming party info is to identify the remote party to us. * (Caller-ID for answered or connected-line for originated calls.) */ struct q931_party_id remote_id; /*! \brief Automatic Number Identification (ANI) */ struct q931_party_number ani; /*! * \brief Staging place for the Q.931 redirection number ie. * \note * The number could be the remote_id.number or redirecting.to.number * depending upon the notification indicator. */ struct q931_party_number redirection_number; /*! * \brief Called party address. * \note The called.number.str is the accumulated overlap dial digits * and enbloc digits. * \note The called.number.presentation value is not used. */ struct q931_party_address called; int nonisdn; int complete; /* no more digits coming */ int newcall; /* if the received message has a new call reference value */ int retranstimer; /* Timer for retransmitting DISC */ int t308_timedout; /* Whether t308 timed out once */ struct q931_party_redirecting redirecting; /*! \brief Incoming call transfer state. */ enum INCOMING_CT_STATE incoming_ct_state; /*! Call hold supplementary state. Valid on master call record only. */ enum Q931_HOLD_STATE hold_state; /*! Call hold event timer. Valid on master call record only. */ int hold_timer; int deflection_in_progress; /*!< CallDeflection for NT PTMP in progress. */ /*! TRUE if the connected number ie was in the current received message. */ int connected_number_in_message; /*! TRUE if the redirecting number ie was in the current received message. */ int redirecting_number_in_message; int useruserprotocoldisc; char useruserinfo[256]; long aoc_units; /* Advice of Charge Units */ struct apdu_event *apdus; /* APDU queue for call */ int transferable; /* RLT call is transferable */ unsigned int rlt_call_id; /* RLT call id */ /*! ETSI Explicit Call Transfer link id. */ int link_id; /*! TRUE if link_id is valid. */ int is_link_id_valid; /* Bridged call info */ struct q931_call *bridged_call; /* Pointer to other leg of bridged call (Used by Q.SIG when eliminating tromboned calls) */ int changestatus; /* SERVICE message changestatus */ int reversecharge; /* Reverse charging indication: -1 - No reverse charging 1 - Reverse charging 0,2-7 - Reserved for future use */ int t303_timer; int t303_expirycnt; int t312_timer; int fake_clearing_timer; int hangupinitiated; /*! \brief TRUE if we broadcast this call's SETUP message. */ int outboundbroadcast; /*! TRUE if the master call is processing a hangup. Don't destroy it now. */ int master_hanging_up; /*! * \brief Master call controlling this call. * \note Always valid. Master and normal calls point to self. */ struct q931_call *master_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; /*! Display text ie contents. */ struct { /*! Display ie text. NULL if not present or consumed as remote name. */ const unsigned char *text; /*! Full IE code of received display text */ int full_ie; /*! Length of display text. */ unsigned char length; /*! * \brief Character set the text is using. * \details * unknown(0), * iso8859-1(1), * enum-value-withdrawn-by-ITU-T(2) * iso8859-2(3), * iso8859-3(4), * iso8859-4(5), * iso8859-5(6), * iso8859-7(7), * iso10646-BmpString(8), * iso10646-utf-8String(9) */ unsigned char char_set; } display; /* AOC charge requesting on Setup */ int aoc_charging_request; unsigned int slotmap_size:1;/* TRUE if the slotmap is E1 (32 bits). */ /*! Control the RESTART reception to the upper layer. */ struct { /*! Timer ID of RESTART notification events to upper layer. */ int timer; /*! Current RESTART notification index. */ int idx; /*! Number of channels in the channel ID list. */ int count; /*! Channel ID list */ char chan_no[32]; } restart; /*! Control the RESTART retransmissions. */ struct { /*! T316 RESTART retransmit timer. */ int t316_timer; /*! Number of times remaining that RESTART can be transmitted. */ int remain; /*! Encoded RESTART channel id. */ int channel; } restart_tx; }; 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, /*! Abnormal clearing of original call. (T309 processing/T309 timeout/TEI removal) */ CC_EVENT_INTERNAL_CLEARING, /*! 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; /*! D channel control structure. */ struct pri *ctrl; /*! 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. */ struct d_ctrl_dummy { /*! D channel control structure. Must be first in the structure. */ struct pri ctrl; /*! Dummy call reference call record. */ struct q931_call dummy_call; }; /*! Layer 2 link control structure with associated dummy call reference record. */ struct link_dummy { /*! Layer 2 control structure. Must be first in the structure. */ struct q921_link link; /*! Dummy call reference call record. */ struct q931_call dummy_call; }; /*! * \brief Check if the given call ptr is valid and gripe if not. * * \param ctrl D channel controller. * \param call Q.931 call leg. * * \retval TRUE if call ptr is valid. * \retval FALSE if call ptr is invalid. */ #define pri_is_call_valid(ctrl, call) \ q931_is_call_valid_gripe(ctrl, call, __PRETTY_FUNCTION__, __LINE__) int q931_is_call_valid(struct pri *ctrl, struct q931_call *call); int q931_is_call_valid_gripe(struct pri *ctrl, struct q931_call *call, const char *func_name, unsigned long func_line); unsigned pri_schedule_event(struct pri *ctrl, int ms, void (*function)(void *data), void *data); extern pri_event *pri_schedule_run(struct pri *pri); void pri_schedule_del(struct pri *ctrl, unsigned id); int pri_schedule_check(struct pri *ctrl, unsigned id, void (*function)(void *data), void *data); extern pri_event *pri_mkerror(struct pri *pri, char *errstr); void pri_message(struct pri *ctrl, const char *fmt, ...) __attribute__((format(printf, 2, 3))); void pri_error(struct pri *ctrl, const char *fmt, ...) __attribute__((format(printf, 2, 3))); void libpri_copy_string(char *dst, const char *src, size_t size); void pri_link_destroy(struct q921_link *link); struct q921_link *pri_link_new(struct pri *ctrl, int sapi, int tei); void q931_init_call_record(struct q921_link *link, 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); 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, 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); void pri_copy_party_name_to_q931(struct q931_party_name *q931_name, const struct pri_party_name *pri_name); void pri_copy_party_number_to_q931(struct q931_party_number *q931_number, const struct pri_party_number *pri_number); void pri_copy_party_subaddress_to_q931(struct q931_party_subaddress *q931_subaddress, const struct pri_party_subaddress *pri_subaddress); void pri_copy_party_id_to_q931(struct q931_party_id *q931_id, const struct pri_party_id *pri_id); void q931_party_id_fixup(const struct pri *ctrl, struct q931_party_id *id); int q931_party_id_presentation(const struct q931_party_id *id); int q931_display_name_get(struct q931_call *call, struct q931_party_name *name); int q931_display_text(struct pri *ctrl, struct q931_call *call, const struct pri_subcmd_display_txt *display); int q931_facility_display_name(struct pri *ctrl, struct q931_call *call, const struct q931_party_name *name); int q931_facility_called(struct pri *ctrl, struct q931_call *call, const struct q931_party_id *called); const char *q931_call_state_str(enum Q931_CALL_STATE callstate); const char *msg2str(int msg); int q931_get_subcall_count(struct q931_call *master); struct q931_call *q931_find_winning_call(struct q931_call *call); int q931_master_pass_event(struct pri *ctrl, struct q931_call *subcall, int msg_type); struct pri_subcommand *q931_alloc_subcommand(struct pri *ctrl); struct q931_call *q931_find_link_id_call(struct pri *ctrl, int link_id); struct q931_call *q931_find_held_active_call(struct pri *ctrl, struct q931_call *held_call); int q931_request_subaddress(struct pri *ctrl, struct q931_call *call, int notify, const struct q931_party_name *name, const struct q931_party_number *number); int q931_subaddress_transfer(struct pri *ctrl, struct q931_call *call); int q931_notify_redirection(struct pri *ctrl, struct q931_call *call, int notify, const struct q931_party_name *name, 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); const char *pri_cc_fsm_state_str(enum CC_STATES state); const char *pri_cc_fsm_event_str(enum CC_EVENTS event); 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 NFAS master PRI control structure. * * \param ctrl D channel controller. * * \return NFAS master PRI control structure. */ static inline struct pri *PRI_NFAS_MASTER(struct pri *ctrl) { if (ctrl->master) { ctrl = ctrl->master; } return ctrl; } /*! * \brief Determine if layer 2 is in BRI NT PTMP mode. * * \param ctrl D channel controller. * * \retval TRUE if in BRI NT PTMP mode. * \retval FALSE otherwise. */ static inline int BRI_NT_PTMP(const struct pri *ctrl) { struct pri *my_ctrl = (struct pri *) ctrl; return my_ctrl->bri && my_ctrl->localtype == PRI_NETWORK && my_ctrl->link.tei == Q921_TEI_GROUP; } /*! * \brief Determine if layer 2 is in BRI TE PTMP mode. * * \param ctrl D channel controller. * * \retval TRUE if in BRI TE PTMP mode. * \retval FALSE otherwise. */ static inline int BRI_TE_PTMP(const struct pri *ctrl) { struct pri *my_ctrl = (struct pri *) ctrl; return my_ctrl->bri && my_ctrl->localtype == PRI_CPE && my_ctrl->link.tei == Q921_TEI_GROUP; } /*! * \brief Determine if layer 2 is in NT mode. * * \param ctrl D channel controller. * * \retval TRUE if in NT mode. * \retval FALSE otherwise. */ static inline int NT_MODE(const struct pri *ctrl) { struct pri *my_ctrl = (struct pri *) ctrl; return my_ctrl->localtype == PRI_NETWORK; } /*! * \brief Determine if layer 2 is in TE mode. * * \param ctrl D channel controller. * * \retval TRUE if in TE mode. * \retval FALSE otherwise. */ static inline int TE_MODE(const struct pri *ctrl) { struct pri *my_ctrl = (struct pri *) ctrl; return my_ctrl->localtype == PRI_CPE; } /*! * \brief Determine if layer 2 is in PTP mode. * * \param ctrl D channel controller. * * \retval TRUE if in PTP mode. * \retval FALSE otherwise. */ static inline int PTP_MODE(const struct pri *ctrl) { struct pri *my_ctrl = (struct pri *) ctrl; return my_ctrl->link.tei == Q921_TEI_PRI; } /*! * \brief Determine if layer 2 is in PTMP mode. * * \param ctrl D channel controller. * * \retval TRUE if in PTMP mode. * \retval FALSE otherwise. */ static inline int PTMP_MODE(const struct pri *ctrl) { struct pri *my_ctrl = (struct pri *) ctrl; return my_ctrl->link.tei == Q921_TEI_GROUP; } #define Q931_DUMMY_CALL_REFERENCE -1 #define Q931_CALL_REFERENCE_FLAG 0x8000 /* Identify which end allocted the CR. */ /*! * \brief Deterimine if the given call control pointer is a dummy call. * * \retval TRUE if given call is a dummy call. * \retval FALSE otherwise. */ static inline int q931_is_dummy_call(const struct q931_call *call) { return (call->cr == Q931_DUMMY_CALL_REFERENCE) ? 1 : 0; } static inline short get_invokeid(struct pri *ctrl) { return ++ctrl->last_invoke; } #endif libpri-1.4.14/pri_facility.c0000644000000000000000000051212511733150173014411 0ustar rootroot/* * libpri: An implementation of Primary Rate ISDN * * Written by Matthew Fredrickson * * Copyright (C) 2004-2005, Digium, Inc. * All Rights Reserved. */ /* * 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. */ #include "compat.h" #include "libpri.h" #include "pri_internal.h" #include "pri_facility.h" #include #include #include #include const char *pri_facility_error2str(int facility_error_code) { 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) { int value; switch (ctrl->switchtype) { case PRI_SWITCH_QSIG: switch (redirectingreason) { case PRI_REDIR_UNKNOWN: value = QSIG_DIVERT_REASON_UNKNOWN; break; case PRI_REDIR_FORWARD_ON_BUSY: value = QSIG_DIVERT_REASON_CFB; break; case PRI_REDIR_FORWARD_ON_NO_REPLY: value = QSIG_DIVERT_REASON_CFNR; break; case PRI_REDIR_UNCONDITIONAL: value = QSIG_DIVERT_REASON_CFU; break; case PRI_REDIR_DEFLECTION: case PRI_REDIR_DTE_OUT_OF_ORDER: case PRI_REDIR_FORWARDED_BY_DTE: pri_message(ctrl, "!! Don't know how to convert Q.931 redirection reason %d to Q.SIG\n", redirectingreason); /* Fall through */ default: value = QSIG_DIVERT_REASON_UNKNOWN; break; } break; default: switch (redirectingreason) { case PRI_REDIR_UNKNOWN: value = Q952_DIVERT_REASON_UNKNOWN; break; case PRI_REDIR_FORWARD_ON_BUSY: value = Q952_DIVERT_REASON_CFB; break; case PRI_REDIR_FORWARD_ON_NO_REPLY: value = Q952_DIVERT_REASON_CFNR; break; case PRI_REDIR_DEFLECTION: value = Q952_DIVERT_REASON_CD; break; case PRI_REDIR_UNCONDITIONAL: value = Q952_DIVERT_REASON_CFU; break; case PRI_REDIR_DTE_OUT_OF_ORDER: case PRI_REDIR_FORWARDED_BY_DTE: pri_message(ctrl, "!! Don't know how to convert Q.931 redirection reason %d to Q.952\n", redirectingreason); /* Fall through */ default: value = Q952_DIVERT_REASON_UNKNOWN; break; } break; } return value; } static int redirectingreason_for_q931(struct pri *ctrl, int redirectingreason) { int value; switch (ctrl->switchtype) { case PRI_SWITCH_QSIG: switch (redirectingreason) { case QSIG_DIVERT_REASON_UNKNOWN: value = PRI_REDIR_UNKNOWN; break; case QSIG_DIVERT_REASON_CFU: value = PRI_REDIR_UNCONDITIONAL; break; case QSIG_DIVERT_REASON_CFB: value = PRI_REDIR_FORWARD_ON_BUSY; break; case QSIG_DIVERT_REASON_CFNR: value = PRI_REDIR_FORWARD_ON_NO_REPLY; break; default: pri_message(ctrl, "!! Unknown Q.SIG diversion reason %d\n", redirectingreason); value = PRI_REDIR_UNKNOWN; break; } break; default: switch (redirectingreason) { case Q952_DIVERT_REASON_UNKNOWN: value = PRI_REDIR_UNKNOWN; break; case Q952_DIVERT_REASON_CFU: value = PRI_REDIR_UNCONDITIONAL; break; case Q952_DIVERT_REASON_CFB: value = PRI_REDIR_FORWARD_ON_BUSY; break; case Q952_DIVERT_REASON_CFNR: value = PRI_REDIR_FORWARD_ON_NO_REPLY; break; case Q952_DIVERT_REASON_CD: value = PRI_REDIR_DEFLECTION; break; case Q952_DIVERT_REASON_IMMEDIATE: pri_message(ctrl, "!! Dont' know how to convert Q.952 diversion reason IMMEDIATE to PRI analog\n"); value = PRI_REDIR_UNKNOWN; /* ??? */ break; default: pri_message(ctrl, "!! Unknown Q.952 diversion reason %d\n", redirectingreason); value = PRI_REDIR_UNKNOWN; break; } break; } return value; } /*! * \internal * \brief Convert the Q.931 type-of-number field to facility. * * \param ctrl D channel controller for diagnostic messages or global options. * \param ton Q.931 ton/plan octet. * * \return PartyNumber enumeration value. */ static int typeofnumber_from_q931(struct pri *ctrl, int ton) { int value; switch ((ton >> 4) & 0x03) { default: pri_message(ctrl, "!! Unsupported Q.931 TypeOfNumber value (%d)\n", ton); /* fall through */ case PRI_TON_UNKNOWN: value = Q932_TON_UNKNOWN; break; case PRI_TON_INTERNATIONAL: value = Q932_TON_INTERNATIONAL; break; case PRI_TON_NATIONAL: value = Q932_TON_NATIONAL; break; case PRI_TON_NET_SPECIFIC: value = Q932_TON_NET_SPECIFIC; break; case PRI_TON_SUBSCRIBER: value = Q932_TON_SUBSCRIBER; break; case PRI_TON_ABBREVIATED: value = Q932_TON_ABBREVIATED; break; } return value; } static int typeofnumber_for_q931(struct pri *ctrl, int ton) { int value; switch (ton) { default: pri_message(ctrl, "!! Invalid TypeOfNumber %d\n", ton); /* fall through */ case Q932_TON_UNKNOWN: value = PRI_TON_UNKNOWN; break; case Q932_TON_INTERNATIONAL: value = PRI_TON_INTERNATIONAL; break; case Q932_TON_NATIONAL: value = PRI_TON_NATIONAL; break; case Q932_TON_NET_SPECIFIC: value = PRI_TON_NET_SPECIFIC; break; case Q932_TON_SUBSCRIBER: value = PRI_TON_SUBSCRIBER; break; case Q932_TON_ABBREVIATED: value = PRI_TON_ABBREVIATED; break; } return value << 4; } /*! * \internal * \brief Convert the Q.931 numbering plan field to facility. * * \param ctrl D channel controller for diagnostic messages or global options. * \param plan Q.931 ton/plan octet. * * \return PartyNumber enumeration value. */ static int numbering_plan_from_q931(struct pri *ctrl, int plan) { int value; switch (plan & 0x0F) { default: pri_message(ctrl, "!! Unsupported Q.931 numbering plan value (%d)\n", plan); /* fall through */ case PRI_NPI_UNKNOWN: value = 0; /* unknown */ break; case PRI_NPI_E163_E164: value = 1; /* public */ break; case PRI_NPI_X121: value = 3; /* data */ break; case PRI_NPI_F69: value = 4; /* telex */ break; case PRI_NPI_NATIONAL: value = 8; /* nationalStandard */ break; case PRI_NPI_PRIVATE: value = 5; /* private */ break; } return value; } /*! * \internal * \brief Convert the PartyNumber numbering plan to Q.931 plan field value. * * \param ctrl D channel controller for diagnostic messages or global options. * \param plan PartyNumber enumeration value. * * \return Q.931 plan field value. */ static int numbering_plan_for_q931(struct pri *ctrl, int plan) { int value; switch (plan) { default: pri_message(ctrl, "!! Unsupported PartyNumber to Q.931 numbering plan value (%d)\n", plan); /* fall through */ case 0: /* unknown */ value = PRI_NPI_UNKNOWN; break; case 1: /* public */ value = PRI_NPI_E163_E164; break; case 3: /* data */ value = PRI_NPI_X121; break; case 4: /* telex */ value = PRI_NPI_F69; break; case 5: /* private */ value = PRI_NPI_PRIVATE; break; case 8: /* nationalStandard */ value = PRI_NPI_NATIONAL; break; } return value; } /*! * \internal * \brief Convert the Q.931 number presentation field to facility. * * \param ctrl D channel controller for diagnostic messages or global options. * \param presentation Q.931 presentation/screening octet. * \param number_present Non-zero if the number is available. * * \return Presented enumeration value. */ static int presentation_from_q931(struct pri *ctrl, int presentation, int number_present) { int value; switch (presentation & PRI_PRES_RESTRICTION) { case PRI_PRES_ALLOWED: value = 0; /* presentationAllowed */ break; default: pri_message(ctrl, "!! Unsupported Q.931 number presentation value (%d)\n", presentation); /* fall through */ case PRI_PRES_RESTRICTED: if (number_present) { value = 3; /* presentationRestricted */ } else { value = 1; /* presentationRestricted */ } break; case PRI_PRES_UNAVAILABLE: value = 2; /* numberNotAvailableDueToInterworking */ break; } return value; } /*! * \internal * \brief Convert the Presented presentation * to Q.931 presentation field value. * * \param ctrl D channel controller for diagnostic messages or global options. * \param presentation Presented value. * * \return Q.931 presentation field value. */ static int presentation_for_q931(struct pri *ctrl, int presentation) { int value; switch (presentation) { case 0: /* presentationAllowed */ value = PRI_PRES_ALLOWED; break; default: pri_message(ctrl, "!! Unsupported Presented to Q.931 value (%d)\n", presentation); /* fall through */ case 1: /* presentationRestricted */ case 3: /* presentationRestricted */ value = PRI_PRES_RESTRICTED; break; case 2: /* numberNotAvailableDueToInterworking */ value = PRI_PRES_UNAVAILABLE; break; } return value; } /*! * \internal * \brief Convert the Q.931 number presentation field to Q.SIG name presentation. * * \param ctrl D channel controller for diagnostic messages or global options. * \param presentation Q.931 presentation/screening octet. * \param name_present Non-zero if the name is available. * * \return Name presentation enumeration value. */ static int qsig_name_presentation_from_q931(struct pri *ctrl, int presentation, int name_present) { int value; switch (presentation & PRI_PRES_RESTRICTION) { case PRI_PRES_ALLOWED: if (name_present) { value = 1; /* presentation_allowed */ } else { value = 4; /* name_not_available */ } break; default: pri_message(ctrl, "!! Unsupported Q.931 number presentation value (%d)\n", presentation); /* fall through */ case PRI_PRES_RESTRICTED: if (name_present) { value = 2; /* presentation_restricted */ } else { value = 3; /* presentation_restricted_null */ } break; case PRI_PRES_UNAVAILABLE: value = 4; /* name_not_available */ break; } return value; } /*! * \internal * \brief Convert the Q.SIG name presentation to Q.931 presentation field value. * * \param ctrl D channel controller for diagnostic messages or global options. * \param presentation Q.SIG name presentation value. * * \return Q.931 presentation field value. */ static int qsig_name_presentation_for_q931(struct pri *ctrl, int presentation) { int value; switch (presentation) { case 1: /* presentation_allowed */ value = PRI_PRES_ALLOWED; break; default: pri_message(ctrl, "!! Unsupported Q.SIG name presentation to Q.931 value (%d)\n", presentation); /* fall through */ case 2: /* presentation_restricted */ case 3: /* presentation_restricted_null */ value = PRI_PRES_RESTRICTED; break; case 0: /* optional_name_not_present */ case 4: /* name_not_available */ value = PRI_PRES_UNAVAILABLE; break; } return value; } /*! * \internal * \brief Convert number presentation to Q.SIG diversion subscription notification. * * \param ctrl D channel controller for diagnostic messages or global options. * \param presentation Number presentation value. * * \return Q.SIG diversion subscription notification value. */ static int presentation_to_subscription(struct pri *ctrl, int presentation) { /* derive subscription value from presentation value */ switch (presentation & PRI_PRES_RESTRICTION) { case PRI_PRES_ALLOWED: return QSIG_NOTIFICATION_WITH_DIVERTED_TO_NR; case PRI_PRES_RESTRICTED: return QSIG_NOTIFICATION_WITHOUT_DIVERTED_TO_NR; case PRI_PRES_UNAVAILABLE: /* Number not available due to interworking */ return QSIG_NOTIFICATION_WITHOUT_DIVERTED_TO_NR; /* ?? QSIG_NO_NOTIFICATION */ default: pri_message(ctrl, "!! Unknown Q.SIG presentationIndicator 0x%02x\n", presentation); return QSIG_NOTIFICATION_WITHOUT_DIVERTED_TO_NR; } } /*! * \brief Copy the given rose party number to the q931_party_number * * \param ctrl D channel controller for diagnostic messages or global options. * \param q931_number Q.931 party number structure * \param rose_number ROSE party number structure * * \note It is assumed that the q931_number has been initialized before calling. * * \return Nothing */ 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, sizeof(q931_number->str)); q931_number->plan = numbering_plan_for_q931(ctrl, rose_number->plan) | typeofnumber_for_q931(ctrl, rose_number->ton); q931_number->valid = 1; } /*! * \brief Copy the given rose subaddress to the q931_party_subaddress. * * \param ctrl D channel controller for diagnostic messages or global options. * \param q931_subaddress Q.931 party subaddress structure * \param rose_subaddress ROSE subaddress structure * * \note It is assumed that the q931_subaddress has been initialized before calling. * * \return Nothing */ void rose_copy_subaddress_to_q931(struct pri *ctrl, struct q931_party_subaddress *q931_subaddress, const struct rosePartySubaddress *rose_subaddress) { //q931_party_subaddress_init(q931_subaddress); if (!rose_subaddress->length) { /* Subaddress is not present. */ return; } switch (rose_subaddress->type) { case 0:/* UserSpecified */ q931_subaddress->type = 2;/* user_specified */ q931_subaddress->valid = 1; q931_subaddress->length = rose_subaddress->length; if (sizeof(q931_subaddress->data) <= q931_subaddress->length) { q931_subaddress->length = sizeof(q931_subaddress->data) - 1; } memcpy(q931_subaddress->data, rose_subaddress->u.user_specified.information, q931_subaddress->length); q931_subaddress->data[q931_subaddress->length] = '\0'; if (rose_subaddress->u.user_specified.odd_count_present) { q931_subaddress->odd_even_indicator = rose_subaddress->u.user_specified.odd_count; } break; case 1:/* NSAP */ q931_subaddress->type = 0;/* nsap */ q931_subaddress->valid = 1; libpri_copy_string((char *) q931_subaddress->data, (char *) rose_subaddress->u.nsap, sizeof(q931_subaddress->data)); q931_subaddress->length = strlen((char *) q931_subaddress->data); break; default: /* Don't know how to encode so assume it is not present. */ break; } } /*! * \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. * \param q931_address Q.931 party id structure to fill address * \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_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, &rose_address->subaddress); } /*! * \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. * \param q931_number Q.931 party number structure * \param rose_presented ROSE presented screened party number structure * * \return Nothing */ void rose_copy_presented_number_screened_to_q931(struct pri *ctrl, struct q931_party_number *q931_number, const struct rosePresentedNumberScreened *rose_presented) { q931_party_number_init(q931_number); q931_number->valid = 1; q931_number->presentation = presentation_for_q931(ctrl, rose_presented->presentation); switch (rose_presented->presentation) { case 0: /* presentationAllowedNumber */ case 3: /* presentationRestrictedNumber */ q931_number->presentation |= (rose_presented->screened.screening_indicator & PRI_PRES_NUMBER_TYPE); rose_copy_number_to_q931(ctrl, q931_number, &rose_presented->screened.number); break; case 2: /* numberNotAvailableDueToInterworking */ q931_number->presentation = PRES_NUMBER_NOT_AVAILABLE; break; default: q931_number->presentation |= PRI_PRES_USER_NUMBER_UNSCREENED; break; } } /*! * \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. * \param q931_number Q.931 party number structure * \param rose_presented ROSE presented unscreened party number structure * * \return Nothing */ void rose_copy_presented_number_unscreened_to_q931(struct pri *ctrl, struct q931_party_number *q931_number, const struct rosePresentedNumberUnscreened *rose_presented) { q931_party_number_init(q931_number); q931_number->valid = 1; q931_number->presentation = presentation_for_q931(ctrl, rose_presented->presentation) | PRI_PRES_USER_NUMBER_UNSCREENED; switch (rose_presented->presentation) { case 0: /* presentationAllowedNumber */ case 3: /* presentationRestrictedNumber */ rose_copy_number_to_q931(ctrl, q931_number, &rose_presented->number); break; case 2: /* numberNotAvailableDueToInterworking */ q931_number->presentation = PRES_NUMBER_NOT_AVAILABLE; break; default: break; } } /*! * \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. * \param q931_address Q.931 party id structure to fill the address * \param rose_presented ROSE presented screened party address structure * * \return Nothing */ void rose_copy_presented_address_screened_to_id_q931(struct pri *ctrl, struct q931_party_id *q931_address, const struct rosePresentedAddressScreened *rose_presented) { q931_party_number_init(&q931_address->number); q931_party_subaddress_init(&q931_address->subaddress); q931_address->number.valid = 1; q931_address->number.presentation = presentation_for_q931(ctrl, rose_presented->presentation); switch (rose_presented->presentation) { case 0: /* presentationAllowedAddress */ case 3: /* presentationRestrictedAddress */ q931_address->number.presentation |= (rose_presented->screened.screening_indicator & PRI_PRES_NUMBER_TYPE); rose_copy_number_to_q931(ctrl, &q931_address->number, &rose_presented->screened.number); rose_copy_subaddress_to_q931(ctrl, &q931_address->subaddress, &rose_presented->screened.subaddress); break; case 2: /* numberNotAvailableDueToInterworking */ q931_address->number.presentation = PRES_NUMBER_NOT_AVAILABLE; break; default: q931_address->number.presentation |= PRI_PRES_USER_NUMBER_UNSCREENED; break; } } /*! * \brief Copy the given rose party name to the q931_party_name * * \param ctrl D channel controller for diagnostic messages or global options. * \param qsig_name Q.SIG party name structure * \param rose_name Q.SIG ROSE party name structure * * \return Nothing */ 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; qsig_name->presentation = qsig_name_presentation_for_q931(ctrl, rose_name->presentation); qsig_name->char_set = rose_name->char_set; libpri_copy_string(qsig_name->str, (char *) rose_name->data, sizeof(qsig_name->str)); } /*! * \brief Copy the given q931_party_number to the rose party number * * \param ctrl D channel controller for diagnostic messages or global options. * \param rose_number ROSE party number structure * \param q931_number Q.931 party number structure * * \return Nothing */ 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); /* Truncate the q931_number->str if necessary. */ libpri_copy_string((char *) rose_number->str, q931_number->str, sizeof(rose_number->str)); rose_number->length = strlen((char *) rose_number->str); } /*! * \brief Copy the given q931_party_subaddress to the rose subaddress. * * \param ctrl D channel controller for diagnostic messages or global options. * \param rose_subaddress ROSE subaddress structure * \param q931_subaddress Q.931 party subaddress structure * * \return Nothing */ void q931_copy_subaddress_to_rose(struct pri *ctrl, struct rosePartySubaddress *rose_subaddress, const struct q931_party_subaddress *q931_subaddress) { if (!q931_subaddress->valid) { /* Subaddress is not present. */ rose_subaddress->length = 0; return; } switch (q931_subaddress->type) { case 0: /* NSAP */ rose_subaddress->type = 1;/* NSAP */ libpri_copy_string((char *) rose_subaddress->u.nsap, (char *) q931_subaddress->data, sizeof(rose_subaddress->u.nsap)); rose_subaddress->length = strlen((char *) rose_subaddress->u.nsap); break; case 2: /* user_specified */ rose_subaddress->type = 0;/* UserSpecified */ rose_subaddress->length = q931_subaddress->length; if (sizeof(rose_subaddress->u.user_specified.information) <= rose_subaddress->length) { rose_subaddress->length = sizeof(rose_subaddress->u.user_specified.information) - 1; } else { if (q931_subaddress->odd_even_indicator) { rose_subaddress->u.user_specified.odd_count_present = 1; rose_subaddress->u.user_specified.odd_count = 1; } } memcpy(rose_subaddress->u.user_specified.information, q931_subaddress->data, rose_subaddress->length); rose_subaddress->u.user_specified.information[rose_subaddress->length] = '\0'; break; default: /* Don't know how to encode so assume it is not present. */ rose_subaddress->length = 0; break; } } /*! * \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. * \param rose_address ROSE address structure * \param q931_address Q.931 party id structure to give address * * \return Nothing */ 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); q931_copy_subaddress_to_rose(ctrl, &rose_address->subaddress, &q931_address->subaddress); } /*! * \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. * \param rose_presented ROSE presented screened party number structure * \param q931_number Q.931 party number structure * * \return Nothing */ void q931_copy_presented_number_screened_to_rose(struct pri *ctrl, struct rosePresentedNumberScreened *rose_presented, const struct q931_party_number *q931_number) { if (q931_number->valid) { rose_presented->presentation = presentation_from_q931(ctrl, q931_number->presentation, q931_number->str[0]); rose_presented->screened.screening_indicator = q931_number->presentation & PRI_PRES_NUMBER_TYPE; q931_copy_number_to_rose(ctrl, &rose_presented->screened.number, q931_number); } else { rose_presented->presentation = 2;/* numberNotAvailableDueToInterworking */ } } /*! * \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. * \param rose_presented ROSE presented unscreened party number structure * \param q931_number Q.931 party number structure * * \return Nothing */ void q931_copy_presented_number_unscreened_to_rose(struct pri *ctrl, struct rosePresentedNumberUnscreened *rose_presented, const struct q931_party_number *q931_number) { if (q931_number->valid) { rose_presented->presentation = presentation_from_q931(ctrl, q931_number->presentation, q931_number->str[0]); q931_copy_number_to_rose(ctrl, &rose_presented->number, q931_number); } else { rose_presented->presentation = 2;/* numberNotAvailableDueToInterworking */ } } /*! * \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. * \param rose_presented ROSE presented screened party address structure * \param q931_address Q.931 party id structure to get the address * * \return Nothing */ void q931_copy_presented_id_address_screened_to_rose(struct pri *ctrl, struct rosePresentedAddressScreened *rose_presented, const struct q931_party_id *q931_address) { if (q931_address->number.valid) { rose_presented->presentation = presentation_from_q931(ctrl, q931_address->number.presentation, q931_address->number.str[0]); rose_presented->screened.screening_indicator = q931_address->number.presentation & PRI_PRES_NUMBER_TYPE; q931_copy_number_to_rose(ctrl, &rose_presented->screened.number, &q931_address->number); q931_copy_subaddress_to_rose(ctrl, &rose_presented->screened.subaddress, &q931_address->subaddress); } else { rose_presented->presentation = 2;/* numberNotAvailableDueToInterworking */ } } /*! * \brief Copy the given q931_party_name to the rose party name * * \param ctrl D channel controller for diagnostic messages or global options. * \param rose_name Q.SIG ROSE party name structure * \param qsig_name Q.SIG party name structure * * \return Nothing */ 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, qsig_name->presentation, qsig_name->str[0]); rose_name->char_set = qsig_name->char_set; /* Truncate the qsig_name->str if necessary. */ libpri_copy_string((char *) rose_name->data, qsig_name->str, sizeof(rose_name->data)); rose_name->length = strlen((char *) rose_name->data); } else { rose_name->presentation = 4;/* name_not_available */ } } /*! * \internal * \brief Encode the Q.SIG DivertingLegInformation1 invoke 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 call Call leg from which to encode diversion leg 1. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_qsig_diverting_leg_information1(struct pri *ctrl, unsigned char *pos, unsigned char *end, q931_call *call) { 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 = 0; /* discardAnyUnrecognisedInvokePdu */ pos = facility_encode_header(ctrl, pos, end, &header); if (!pos) { return NULL; } memset(&msg, 0, sizeof(msg)); msg.operation = ROSE_QSIG_DivertingLegInformation1; msg.invoke_id = get_invokeid(ctrl); msg.args.qsig.DivertingLegInformation1.diversion_reason = redirectingreason_from_q931(ctrl, call->redirecting.reason); /* subscriptionOption is the redirecting.to.number.presentation */ msg.args.qsig.DivertingLegInformation1.subscription_option = presentation_to_subscription(ctrl, call->redirecting.to.number.presentation); /* nominatedNr is the redirecting.to.number */ q931_copy_number_to_rose(ctrl, &msg.args.qsig.DivertingLegInformation1.nominated_number, &call->redirecting.to.number); pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \internal * \brief Encode the ETSI DivertingLegInformation1 invoke 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 call Call leg from which to encode diversion leg 1. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_etsi_diverting_leg_information1(struct pri *ctrl, unsigned char *pos, unsigned char *end, q931_call *call) { struct rose_msg_invoke msg; pos = facility_encode_header(ctrl, pos, end, NULL); if (!pos) { return NULL; } memset(&msg, 0, sizeof(msg)); msg.operation = ROSE_ETSI_DivertingLegInformation1; msg.invoke_id = get_invokeid(ctrl); msg.args.etsi.DivertingLegInformation1.diversion_reason = redirectingreason_from_q931(ctrl, call->redirecting.reason); if (call->redirecting.to.number.valid) { msg.args.etsi.DivertingLegInformation1.subscription_option = 2; /* divertedToNumber is the redirecting.to.number */ msg.args.etsi.DivertingLegInformation1.diverted_to_present = 1; q931_copy_presented_number_unscreened_to_rose(ctrl, &msg.args.etsi.DivertingLegInformation1.diverted_to, &call->redirecting.to.number); } else { msg.args.etsi.DivertingLegInformation1.subscription_option = 1; } pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \brief Encode and queue the DivertingLegInformation1 invoke message. * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Call leg from which to encode diversion leg 1. * * \retval 0 on success. * \retval -1 on error. */ int rose_diverting_leg_information1_encode(struct pri *ctrl, q931_call *call) { unsigned char buffer[256]; unsigned char *end; switch (ctrl->switchtype) { case PRI_SWITCH_EUROISDN_E1: case PRI_SWITCH_EUROISDN_T1: end = enc_etsi_diverting_leg_information1(ctrl, buffer, buffer + sizeof(buffer), call); break; case PRI_SWITCH_QSIG: end = enc_qsig_diverting_leg_information1(ctrl, buffer, buffer + sizeof(buffer), call); break; default: return -1; } if (!end) { return -1; } return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL); } /*! * \internal * \brief Encode the Q.SIG DivertingLegInformation2 invoke 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 call Call leg from which to encode diversion leg 2. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_qsig_diverting_leg_information2(struct pri *ctrl, unsigned char *pos, unsigned char *end, q931_call *call) { 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 = 0; /* discardAnyUnrecognisedInvokePdu */ pos = facility_encode_header(ctrl, pos, end, &header); if (!pos) { return NULL; } memset(&msg, 0, sizeof(msg)); msg.operation = ROSE_QSIG_DivertingLegInformation2; msg.invoke_id = get_invokeid(ctrl); /* diversionCounter is the redirecting.count */ msg.args.qsig.DivertingLegInformation2.diversion_counter = call->redirecting.count; msg.args.qsig.DivertingLegInformation2.diversion_reason = redirectingreason_from_q931(ctrl, call->redirecting.reason); /* divertingNr is the redirecting.from.number */ msg.args.qsig.DivertingLegInformation2.diverting_present = 1; q931_copy_presented_number_unscreened_to_rose(ctrl, &msg.args.qsig.DivertingLegInformation2.diverting, &call->redirecting.from.number); /* redirectingName is the redirecting.from.name */ if (call->redirecting.from.name.valid) { msg.args.qsig.DivertingLegInformation2.redirecting_name_present = 1; q931_copy_name_to_rose(ctrl, &msg.args.qsig.DivertingLegInformation2.redirecting_name, &call->redirecting.from.name); } if (1 < call->redirecting.count) { /* originalCalledNr is the redirecting.orig_called.number */ msg.args.qsig.DivertingLegInformation2.original_called_present = 1; q931_copy_presented_number_unscreened_to_rose(ctrl, &msg.args.qsig.DivertingLegInformation2.original_called, &call->redirecting.orig_called.number); msg.args.qsig.DivertingLegInformation2.original_diversion_reason_present = 1; if (call->redirecting.orig_called.number.valid) { msg.args.qsig.DivertingLegInformation2.original_diversion_reason = redirectingreason_from_q931(ctrl, call->redirecting.orig_reason); } else { msg.args.qsig.DivertingLegInformation2.original_diversion_reason = QSIG_DIVERT_REASON_UNKNOWN; } /* originalCalledName is the redirecting.orig_called.name */ if (call->redirecting.orig_called.name.valid) { msg.args.qsig.DivertingLegInformation2.original_called_name_present = 1; q931_copy_name_to_rose(ctrl, &msg.args.qsig.DivertingLegInformation2.original_called_name, &call->redirecting.orig_called.name); } } pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \internal * \brief Encode the ETSI DivertingLegInformation2 invoke 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 call Call leg from which to encode diversion leg 2. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_etsi_diverting_leg_information2(struct pri *ctrl, unsigned char *pos, unsigned char *end, q931_call *call) { struct rose_msg_invoke msg; pos = facility_encode_header(ctrl, pos, end, NULL); if (!pos) { return NULL; } memset(&msg, 0, sizeof(msg)); msg.operation = ROSE_ETSI_DivertingLegInformation2; msg.invoke_id = get_invokeid(ctrl); /* diversionCounter is the redirecting.count */ msg.args.etsi.DivertingLegInformation2.diversion_counter = call->redirecting.count; msg.args.etsi.DivertingLegInformation2.diversion_reason = redirectingreason_from_q931(ctrl, call->redirecting.reason); /* divertingNr is the redirecting.from.number */ msg.args.etsi.DivertingLegInformation2.diverting_present = 1; q931_copy_presented_number_unscreened_to_rose(ctrl, &msg.args.etsi.DivertingLegInformation2.diverting, &call->redirecting.from.number); if (1 < call->redirecting.count) { /* originalCalledNr is the redirecting.orig_called.number */ msg.args.etsi.DivertingLegInformation2.original_called_present = 1; q931_copy_presented_number_unscreened_to_rose(ctrl, &msg.args.etsi.DivertingLegInformation2.original_called, &call->redirecting.orig_called.number); } pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \internal * \brief Encode and queue the DivertingLegInformation2 invoke message. * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Call leg from which to encode diversion leg 2. * * \retval 0 on success. * \retval -1 on error. */ static int rose_diverting_leg_information2_encode(struct pri *ctrl, q931_call *call) { unsigned char buffer[256]; unsigned char *end; switch (ctrl->switchtype) { case PRI_SWITCH_EUROISDN_E1: case PRI_SWITCH_EUROISDN_T1: end = enc_etsi_diverting_leg_information2(ctrl, buffer, buffer + sizeof(buffer), call); break; case PRI_SWITCH_QSIG: end = enc_qsig_diverting_leg_information2(ctrl, buffer, buffer + sizeof(buffer), call); break; default: return -1; } if (!end) { return -1; } return pri_call_apdu_queue(call, Q931_SETUP, buffer, end - buffer, NULL); } /*! * \internal * \brief Encode the Q.SIG DivertingLegInformation3 invoke 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 call Call leg from which to encode diversion leg 3. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_qsig_diverting_leg_information3(struct pri *ctrl, unsigned char *pos, unsigned char *end, q931_call *call) { 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 = 0; /* discardAnyUnrecognisedInvokePdu */ pos = facility_encode_header(ctrl, pos, end, &header); if (!pos) { return NULL; } memset(&msg, 0, sizeof(msg)); msg.operation = ROSE_QSIG_DivertingLegInformation3; msg.invoke_id = get_invokeid(ctrl); /* redirecting.to.number.presentation also indicates if name presentation is allowed */ if ((call->redirecting.to.number.presentation & PRI_PRES_RESTRICTION) == PRI_PRES_ALLOWED) { msg.args.qsig.DivertingLegInformation3.presentation_allowed_indicator = 1; /* TRUE */ /* redirectionName is the redirecting.to.name */ if (call->redirecting.to.name.valid) { msg.args.qsig.DivertingLegInformation3.redirection_name_present = 1; q931_copy_name_to_rose(ctrl, &msg.args.qsig.DivertingLegInformation3.redirection_name, &call->redirecting.to.name); } } pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \internal * \brief Encode the ETSI DivertingLegInformation3 invoke 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 call Call leg from which to encode diversion leg 3. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_etsi_diverting_leg_information3(struct pri *ctrl, unsigned char *pos, unsigned char *end, q931_call *call) { struct rose_msg_invoke msg; pos = facility_encode_header(ctrl, pos, end, NULL); if (!pos) { return NULL; } memset(&msg, 0, sizeof(msg)); msg.operation = ROSE_ETSI_DivertingLegInformation3; msg.invoke_id = get_invokeid(ctrl); if ((call->redirecting.to.number.presentation & PRI_PRES_RESTRICTION) == PRI_PRES_ALLOWED) { msg.args.etsi.DivertingLegInformation3.presentation_allowed_indicator = 1; /* TRUE */ } pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \brief Encode and queue the DivertingLegInformation3 invoke message. * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Call leg from which to encode diversion leg 3. * \param messagetype Q.931 message type to add facility ie to. * * \retval 0 on success. * \retval -1 on error. */ int rose_diverting_leg_information3_encode(struct pri *ctrl, q931_call *call, int messagetype) { unsigned char buffer[256]; unsigned char *end; switch (ctrl->switchtype) { case PRI_SWITCH_EUROISDN_E1: case PRI_SWITCH_EUROISDN_T1: end = enc_etsi_diverting_leg_information3(ctrl, buffer, buffer + sizeof(buffer), call); break; case PRI_SWITCH_QSIG: end = enc_qsig_diverting_leg_information3(ctrl, buffer, buffer + sizeof(buffer), call); break; default: return -1; } if (!end) { return -1; } return pri_call_apdu_queue(call, messagetype, buffer, end - buffer, NULL); } /*! * \internal * \brief Encode the rltThirdParty invoke 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 callwithid Call-ID information to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_dms100_rlt_initiate_transfer(struct pri *ctrl, unsigned char *pos, unsigned char *end, const q931_call *callwithid) { struct rose_msg_invoke msg; pos = facility_encode_header(ctrl, pos, end, NULL); if (!pos) { return NULL; } memset(&msg, 0, sizeof(msg)); msg.operation = ROSE_DMS100_RLT_ThirdParty; msg.invoke_id = ROSE_DMS100_RLT_THIRD_PARTY; msg.args.dms100.RLT_ThirdParty.call_id = callwithid->rlt_call_id & 0xFFFFFF; msg.args.dms100.RLT_ThirdParty.reason = 0; /* unused, set to 129 */ pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \brief Send the rltThirdParty: Invoke. * * \note For PRI_SWITCH_DMS100 only. * * \param ctrl D channel controller for diagnostic messages or global options. * \param c1 Q.931 call leg 1 * \param c2 Q.931 call leg 2 * * \retval 0 on success. * \retval -1 on error. */ int rlt_initiate_transfer(struct pri *ctrl, q931_call *c1, q931_call *c2) { unsigned char buffer[256]; unsigned char *end; q931_call *apdubearer; q931_call *callwithid; if (c2->transferable) { apdubearer = c1; callwithid = c2; } else if (c1->transferable) { apdubearer = c2; callwithid = c1; } else { return -1; } end = enc_dms100_rlt_initiate_transfer(ctrl, buffer, buffer + sizeof(buffer), callwithid); if (!end) { return -1; } if (pri_call_apdu_queue(apdubearer, Q931_FACILITY, buffer, end - buffer, NULL)) { return -1; } if (q931_facility(apdubearer->pri, apdubearer)) { pri_message(ctrl, "Could not schedule facility message for call %d\n", apdubearer->cr); return -1; } return 0; } /*! * \internal * \brief Encode the rltOperationInd invoke 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. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_dms100_rlt_transfer_ability(struct pri *ctrl, unsigned char *pos, unsigned char *end) { struct rose_msg_invoke msg; pos = facility_encode_header(ctrl, pos, end, NULL); if (!pos) { return NULL; } memset(&msg, 0, sizeof(msg)); msg.operation = ROSE_DMS100_RLT_OperationInd; msg.invoke_id = ROSE_DMS100_RLT_OPERATION_IND; pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \internal * \brief Send the rltOperationInd: Invoke. * * \note For PRI_SWITCH_DMS100 only. * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Q.931 call leg * * \retval 0 on success. * \retval -1 on error. */ static int add_dms100_transfer_ability_apdu(struct pri *ctrl, q931_call *call) { unsigned char buffer[256]; unsigned char *end; end = enc_dms100_rlt_transfer_ability(ctrl, buffer, buffer + sizeof(buffer)); if (!end) { return -1; } return pri_call_apdu_queue(call, Q931_SETUP, buffer, end - buffer, NULL); } /*! * \internal * \brief Encode the NI2 InformationFollowing invoke 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. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_ni2_information_following(struct pri *ctrl, unsigned char *pos, unsigned char *end) { struct fac_extension_header header; struct rose_msg_invoke msg; memset(&header, 0, sizeof(header)); 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.operation = ROSE_NI2_InformationFollowing; msg.invoke_id = get_invokeid(ctrl); msg.args.ni2.InformationFollowing.value = 0; pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \internal * \brief Encode the Q.SIG CallingName invoke 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 name Name data which to encode name. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_qsig_calling_name(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct q931_party_name *name) { struct fac_extension_header header; struct rose_msg_invoke msg; memset(&header, 0, sizeof(header)); if (ctrl->switchtype == PRI_SWITCH_QSIG) { 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.operation = ROSE_QSIG_CallingName; msg.invoke_id = get_invokeid(ctrl); /* CallingName */ q931_copy_name_to_rose(ctrl, &msg.args.qsig.CallingName.name, name); pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \internal * \brief Send caller name information. * * \note For PRI_SWITCH_NI2 and PRI_SWITCH_QSIG. * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Call leg from which to encode name. * \param cpe TRUE if we are the CPE side. * * \retval 0 on success. * \retval -1 on error. */ static int add_callername_facility_ies(struct pri *ctrl, q931_call *call, int cpe) { unsigned char buffer[256]; unsigned char *end; int mymessage; if (!call->local_id.name.valid) { return 0; } if (ctrl->switchtype == PRI_SWITCH_NI2 && !cpe) { end = enc_ni2_information_following(ctrl, buffer, buffer + sizeof(buffer)); if (!end) { return -1; } if (pri_call_apdu_queue(call, Q931_SETUP, buffer, end - buffer, NULL)) { return -1; } /* * We can reuse the buffer since the queue function doesn't * need it. */ } /* CallingName is the local_id.name */ end = enc_qsig_calling_name(ctrl, buffer, buffer + sizeof(buffer), &call->local_id.name); if (!end) { return -1; } if (cpe) { mymessage = Q931_SETUP; } else { mymessage = Q931_FACILITY; } return pri_call_apdu_queue(call, mymessage, buffer, end - buffer, NULL); } /* End Callername */ /* MWI related encode and decode functions */ /*! * \internal * \brief Encode the Q.SIG MWIActivate invoke 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 req Served user setup request information. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_qsig_mwi_activate_message(struct pri *ctrl, unsigned char *pos, unsigned char *end, struct pri_sr *req) { 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 = 0; /* discardAnyUnrecognisedInvokePdu */ pos = facility_encode_header(ctrl, pos, end, &header); if (!pos) { return NULL; } memset(&msg, 0, sizeof(msg)); msg.operation = ROSE_QSIG_MWIActivate; msg.invoke_id = get_invokeid(ctrl); /* The called.number is the served user */ q931_copy_number_to_rose(ctrl, &msg.args.qsig.MWIActivate.served_user_number, &req->called.number); /* * For now, we will just force the numbering plan to unknown to preserve * the original behaviour. */ msg.args.qsig.MWIActivate.served_user_number.plan = 0; /* unknown */ msg.args.qsig.MWIActivate.basic_service = 1; /* speech */ pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \internal * \brief Encode the Q.SIG MWIDeactivate invoke 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 req Served user setup request information. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_qsig_mwi_deactivate_message(struct pri *ctrl, unsigned char *pos, unsigned char *end, struct pri_sr *req) { 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 = 0; /* discardAnyUnrecognisedInvokePdu */ pos = facility_encode_header(ctrl, pos, end, &header); if (!pos) { return NULL; } memset(&msg, 0, sizeof(msg)); msg.operation = ROSE_QSIG_MWIDeactivate; msg.invoke_id = get_invokeid(ctrl); /* The called.number is the served user */ q931_copy_number_to_rose(ctrl, &msg.args.qsig.MWIDeactivate.served_user_number, &req->called.number); /* * For now, we will just force the numbering plan to unknown to preserve * the original behaviour. */ msg.args.qsig.MWIDeactivate.served_user_number.plan = 0; /* unknown */ msg.args.qsig.MWIDeactivate.basic_service = 1; /* speech */ pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \brief Encode and queue the Q.SIG MWIActivate/MWIDeactivate invoke message. * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Call leg to queue message. * \param req Served user setup request information. * \param activate Nonzero to do the activate message. * * \retval 0 on success. * \retval -1 on error. */ int mwi_message_send(struct pri *ctrl, q931_call *call, struct pri_sr *req, int activate) { unsigned char buffer[255]; unsigned char *end; if (!req->called.number.valid || !req->called.number.str[0]) { return -1; } if (activate) { end = enc_qsig_mwi_activate_message(ctrl, buffer, buffer + sizeof(buffer), req); } else { end = enc_qsig_mwi_deactivate_message(ctrl, buffer, buffer + sizeof(buffer), req); } if (!end) { return -1; } return pri_call_apdu_queue(call, Q931_SETUP, buffer, end - buffer, NULL); } /*! * \internal * \brief Encode a MWI indication. * * \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 vm_id Controlling party number (NULL if not present). * \param basic_service Basic service enum (-1 if not present). * \param num_messages NumberOfMessages (-1 if not present). * \param caller_id Controlling party privided number (NULL if not present). * \param timestamp When message left. (Generalized Time format, NULL if not present) * \param message_reference Message reference number (-1 if not present). * \param message_status Message status: added(0), removed(1). * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_etsi_mwi_indicate_message(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct pri_party_id *vm_id, int basic_service, int num_messages, const struct pri_party_id *caller_id, const char *timestamp, int message_reference, int message_status) { struct rose_msg_invoke msg; struct q931_party_number number; pos = facility_encode_header(ctrl, pos, end, NULL); if (!pos) { return NULL; } memset(&msg, 0, sizeof(msg)); msg.operation = ROSE_ETSI_MWIIndicate; msg.invoke_id = get_invokeid(ctrl); if (vm_id && vm_id->number.valid) { pri_copy_party_number_to_q931(&number, &vm_id->number); q931_copy_number_to_rose(ctrl, &msg.args.etsi.MWIIndicate.controlling_user_number, &number); } if (-1 < basic_service) { msg.args.etsi.MWIIndicate.basic_service_present = 1; msg.args.etsi.MWIIndicate.basic_service = basic_service; } if (-1 < num_messages) { msg.args.etsi.MWIIndicate.number_of_messages_present = 1; msg.args.etsi.MWIIndicate.number_of_messages = num_messages; } if (caller_id && caller_id->number.valid) { pri_copy_party_number_to_q931(&number, &caller_id->number); q931_copy_number_to_rose(ctrl, &msg.args.etsi.MWIIndicate.controlling_user_provided_number, &number); } if (timestamp && timestamp[0]) { msg.args.etsi.MWIIndicate.time_present = 1; libpri_copy_string((char *) msg.args.etsi.MWIIndicate.time.str, timestamp, sizeof(msg.args.etsi.MWIIndicate.time.str)); } if (-1 < message_reference) { msg.args.etsi.MWIIndicate.message_id_present = 1; msg.args.etsi.MWIIndicate.message_id.reference_number = message_reference; msg.args.etsi.MWIIndicate.message_id.status = message_status; } pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \internal * \brief Encode and queue a MWI indication. * * \param ctrl D channel controller. * \param call Call leg to queue message. * \param vm_id Voicemail system number (NULL if not present). * \param basic_service Basic service enum (-1 if not present). * \param num_messages NumberOfMessages (-1 if not present). * \param caller_id Party leaving message (NULL if not present). * \param timestamp When message left. (Generalized Time format, NULL if not present) * \param message_reference Message reference number (-1 if not present). * \param message_status Message status: added(0), removed(1). * * \retval 0 on success. * \retval -1 on error. */ static int rose_mwi_indicate_encode(struct pri *ctrl, struct q931_call *call, const struct pri_party_id *vm_id, int basic_service, int num_messages, const struct pri_party_id *caller_id, const char *timestamp, int message_reference, int message_status) { unsigned char buffer[255]; unsigned char *end; end = enc_etsi_mwi_indicate_message(ctrl, buffer, buffer + sizeof(buffer), vm_id, basic_service, num_messages, caller_id, timestamp, message_reference, message_status); if (!end) { return -1; } return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL); } int pri_mwi_indicate_v2(struct pri *ctrl, const struct pri_party_id *mailbox, const struct pri_party_id *vm_id, int basic_service, int num_messages, const struct pri_party_id *caller_id, const char *timestamp, int message_reference, int message_status) { struct q931_call *call; struct q931_party_id called; if (!ctrl) { return -1; } switch (ctrl->switchtype) { case PRI_SWITCH_EUROISDN_E1: case PRI_SWITCH_EUROISDN_T1: if (!BRI_NT_PTMP(ctrl)) { return -1; } call = ctrl->link.dummy_call; if (!call) { return -1; } break; default: return -1; } pri_copy_party_id_to_q931(&called, mailbox); q931_party_id_fixup(ctrl, &called); if (rose_mwi_indicate_encode(ctrl, call, vm_id, basic_service, num_messages, caller_id, timestamp, message_reference, message_status) || q931_facility_called(ctrl, call, &called)) { pri_message(ctrl, "Could not schedule facility message for MWI indicate message.\n"); return -1; } return 0; } int pri_mwi_indicate(struct pri *ctrl, const struct pri_party_id *mailbox, int basic_service, int num_messages, const struct pri_party_id *caller_id, const char *timestamp, int message_reference, int message_status) { return pri_mwi_indicate_v2(ctrl, mailbox, mailbox, basic_service, num_messages, caller_id, timestamp, message_reference, message_status); } /* End MWI */ /* EECT functions */ /*! * \internal * \brief Encode the NI2 InitiateTransfer invoke 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 call Call leg from which to encode transfer information. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_ni2_initiate_transfer(struct pri *ctrl, unsigned char *pos, unsigned char *end, q931_call *call) { struct rose_msg_invoke msg; pos = facility_encode_header(ctrl, pos, end, NULL); if (!pos) { return NULL; } memset(&msg, 0, sizeof(msg)); msg.operation = ROSE_NI2_InitiateTransfer; msg.invoke_id = get_invokeid(ctrl); /* Let's do the trickery to make sure the flag is correct */ msg.args.ni2.InitiateTransfer.call_reference = call->cr ^ Q931_CALL_REFERENCE_FLAG; pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \brief Start a 2BCT * * \note Called for PRI_SWITCH_NI2, PRI_SWITCH_LUCENT5E, and PRI_SWITCH_ATT4ESS * * \param ctrl D channel controller for diagnostic messages or global options. * \param c1 Q.931 call leg 1 * \param c2 Q.931 call leg 2 * * \retval 0 on success. * \retval -1 on error. */ int eect_initiate_transfer(struct pri *ctrl, q931_call *c1, q931_call *c2) { unsigned char buffer[255]; unsigned char *end; end = enc_ni2_initiate_transfer(ctrl, buffer, buffer + sizeof(buffer), c2); if (!end) { return -1; } if (pri_call_apdu_queue(c1, Q931_FACILITY, buffer, end - buffer, NULL)) { pri_message(ctrl, "Could not queue APDU in facility message\n"); return -1; } /* Remember that if we queue a facility IE for a facility message we * have to explicitly send the facility message ourselves */ if (q931_facility(c1->pri, c1)) { pri_message(ctrl, "Could not schedule facility message for call %d\n", c1->cr); return -1; } return 0; } /* End EECT */ /* QSIG CF CallRerouting */ /*! * \internal * \brief Encode the Q.SIG CallRerouting invoke 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 call Q.931 call leg. * \param calling Call rerouting/deflecting updated caller data. * \param deflection Call rerouting/deflecting redirection data. * \param subscription_option Diverting user subscription option to specify if caller is notified. * * \note * deflection->to is the new called number and must always be present. * \note * subscription option: * noNotification(0), * notificationWithoutDivertedToNr(1), * notificationWithDivertedToNr(2) * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_qsig_call_rerouting(struct pri *ctrl, unsigned char *pos, unsigned char *end, q931_call *call, const struct q931_party_id *calling, const struct q931_party_redirecting *deflection, int subscription_option) { struct fac_extension_header header; struct rose_msg_invoke msg; unsigned char *q931ie_pos; 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 = 2; /* rejectAnyUnrecognisedInvokePdu */ pos = facility_encode_header(ctrl, pos, end, &header); if (!pos) { return NULL; } memset(&msg, 0, sizeof(msg)); msg.operation = ROSE_QSIG_CallRerouting; msg.invoke_id = get_invokeid(ctrl); msg.args.qsig.CallRerouting.rerouting_reason = redirectingreason_from_q931(ctrl, deflection->reason); /* calledAddress is the passed in deflection->to address */ q931_copy_id_address_to_rose(ctrl, &msg.args.qsig.CallRerouting.called, &deflection->to); msg.args.qsig.CallRerouting.diversion_counter = deflection->count; /* pSS1InfoElement */ q931ie_pos = msg.args.qsig.CallRerouting.q931ie_contents; *q931ie_pos++ = 0x04; /* Bearer Capability IE */ *q931ie_pos++ = 0x03; /* len */ *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) */ *q931ie_pos++ = 0x32; /* Unknown ie */ *q931ie_pos++ = 0x01; /* Unknown ie len */ *q931ie_pos++ = 0x81; /* Unknown ie body */ msg.args.qsig.CallRerouting.q931ie.length = q931ie_pos - msg.args.qsig.CallRerouting.q931ie_contents; /* lastReroutingNr is the passed in deflection->from.number */ q931_copy_presented_number_unscreened_to_rose(ctrl, &msg.args.qsig.CallRerouting.last_rerouting, &deflection->from.number); msg.args.qsig.CallRerouting.subscription_option = subscription_option; /* callingNumber is the passed in calling->number */ q931_copy_presented_number_screened_to_rose(ctrl, &msg.args.qsig.CallRerouting.calling, &calling->number); /* callingPartySubaddress is the passed in calling->subaddress if valid */ q931_copy_subaddress_to_rose(ctrl, &msg.args.qsig.CallRerouting.calling_subaddress, &calling->subaddress); /* callingName is the passed in calling->name if valid */ if (calling->name.valid) { msg.args.qsig.CallRerouting.calling_name_present = 1; q931_copy_name_to_rose(ctrl, &msg.args.qsig.CallRerouting.calling_name, &calling->name); } if (1 < deflection->count) { /* originalCalledNr is the deflection->orig_called.number */ msg.args.qsig.CallRerouting.original_called_present = 1; q931_copy_presented_number_unscreened_to_rose(ctrl, &msg.args.qsig.CallRerouting.original_called, &deflection->orig_called.number); msg.args.qsig.CallRerouting.original_rerouting_reason_present = 1; if (deflection->orig_called.number.valid) { msg.args.qsig.CallRerouting.original_rerouting_reason = redirectingreason_from_q931(ctrl, deflection->orig_reason); } else { msg.args.qsig.CallRerouting.original_rerouting_reason = QSIG_DIVERT_REASON_UNKNOWN; } /* originalCalledName is the deflection->orig_called.name */ if (deflection->orig_called.name.valid) { msg.args.qsig.CallRerouting.original_called_name_present = 1; q931_copy_name_to_rose(ctrl, &msg.args.qsig.CallRerouting.original_called_name, &deflection->orig_called.name); } } pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \internal * \brief Encode the ETSI CallRerouting invoke 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 call Q.931 call leg. * \param calling Call rerouting/deflecting updated caller data. * \param deflection Call rerouting/deflecting redirection data. * \param subscription_option Diverting user subscription option to specify if caller is notified. * * \note * deflection->to is the new called number and must always be present. * \note * subscription option: * noNotification(0), * notificationWithoutDivertedToNr(1), * notificationWithDivertedToNr(2) * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_etsi_call_rerouting(struct pri *ctrl, unsigned char *pos, unsigned char *end, q931_call *call, const struct q931_party_id *calling, const struct q931_party_redirecting *deflection, int subscription_option) { struct rose_msg_invoke msg; unsigned char *q931ie_pos; pos = facility_encode_header(ctrl, pos, end, NULL); if (!pos) { return NULL; } memset(&msg, 0, sizeof(msg)); msg.operation = ROSE_ETSI_CallRerouting; msg.invoke_id = get_invokeid(ctrl); msg.args.etsi.CallRerouting.rerouting_reason = redirectingreason_from_q931(ctrl, deflection->reason); /* calledAddress is the passed in deflection->to address */ q931_copy_id_address_to_rose(ctrl, &msg.args.etsi.CallRerouting.called_address, &deflection->to); msg.args.etsi.CallRerouting.rerouting_counter = deflection->count; /* q931InfoElement */ q931ie_pos = msg.args.etsi.CallRerouting.q931ie_contents; *q931ie_pos++ = 0x04; /* Bearer Capability IE */ *q931ie_pos++ = 0x03; /* len */ *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 - msg.args.etsi.CallRerouting.q931ie_contents; /* lastReroutingNr is the passed in deflection->from.number */ q931_copy_presented_number_unscreened_to_rose(ctrl, &msg.args.etsi.CallRerouting.last_rerouting, &deflection->from.number); msg.args.etsi.CallRerouting.subscription_option = subscription_option; /* callingPartySubaddress is the passed in calling->subaddress if valid */ q931_copy_subaddress_to_rose(ctrl, &msg.args.etsi.CallRerouting.calling_subaddress, &calling->subaddress); pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \internal * \brief Encode the ETSI CallDeflection invoke 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 call Q.931 call leg. * \param deflection Call deflection address. * * \note * deflection is the new called number and must always be present. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_etsi_call_deflection(struct pri *ctrl, unsigned char *pos, unsigned char *end, q931_call *call, const struct q931_party_id *deflection) { struct rose_msg_invoke msg; pos = facility_encode_header(ctrl, pos, end, NULL); if (!pos) { return NULL; } memset(&msg, 0, sizeof(msg)); msg.operation = ROSE_ETSI_CallDeflection; msg.invoke_id = get_invokeid(ctrl); /* deflectionAddress is the passed in deflection->to address */ 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; switch (deflection->number.presentation & PRI_PRES_RESTRICTION) { case PRI_PRES_ALLOWED: msg.args.etsi.CallDeflection.presentation_allowed_to_diverted_to_user = 1; break; default: case PRI_PRES_UNAVAILABLE: case PRI_PRES_RESTRICTED: break; } pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \internal * \brief Encode and queue the CallRerouting/CallDeflection message. * * \param ctrl D channel controller. * \param call Q.931 call leg. * \param caller Call rerouting/deflecting updated caller data. (NULL if data not updated.) * \param deflection Call rerouting/deflecting redirection data. * \param subscription_option Diverting user subscription option to specify if caller is notified. * * \note * deflection->to is the new called number and must always be present. * \note * subscription option: * noNotification(0), * notificationWithoutDivertedToNr(1), * notificationWithDivertedToNr(2) * * \retval 0 on success. * \retval -1 on error. */ static int rose_reroute_request_encode(struct pri *ctrl, q931_call *call, const struct q931_party_id *caller, const struct q931_party_redirecting *deflection, int subscription_option) { unsigned char buffer[256]; unsigned char *end; if (!caller) { /* * We are deflecting an incoming call back to the network. * Therefore, the Caller-ID is the remote party. */ caller = &call->remote_id; } switch (ctrl->switchtype) { case PRI_SWITCH_EUROISDN_E1: case PRI_SWITCH_EUROISDN_T1: if (PTMP_MODE(ctrl)) { end = enc_etsi_call_deflection(ctrl, buffer, buffer + sizeof(buffer), call, &deflection->to); } else { end = enc_etsi_call_rerouting(ctrl, buffer, buffer + sizeof(buffer), call, caller, deflection, subscription_option); } break; case PRI_SWITCH_QSIG: end = enc_qsig_call_rerouting(ctrl, buffer, buffer + sizeof(buffer), call, caller, deflection, subscription_option); break; default: return -1; } if (!end) { return -1; } return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL); } /*! * \brief Send the CallRerouting/CallDeflection message. * * \param ctrl D channel controller. * \param call Q.931 call leg. * \param caller Call rerouting/deflecting updated caller data. (NULL if data not updated.) * \param deflection Call rerouting/deflecting redirection data. * \param subscription_option Diverting user subscription option to specify if caller is notified. * * \note * deflection->to is the new called number and must always be present. * \note * subscription option: * noNotification(0), * notificationWithoutDivertedToNr(1), * notificationWithDivertedToNr(2) * * \retval 0 on success. * \retval -1 on error. */ int send_reroute_request(struct pri *ctrl, q931_call *call, const struct q931_party_id *caller, const struct q931_party_redirecting *deflection, int subscription_option) { if (!deflection->to.number.str[0]) { /* Must have a deflect to number. That is the point of deflection. */ return -1; } if (rose_reroute_request_encode(ctrl, call, caller, deflection, subscription_option) || q931_facility(ctrl, call)) { pri_message(ctrl, "Could not schedule facility message for CallRerouting/CallDeflection message.\n"); return -1; } return 0; } /*! * \brief Send the Q.SIG CallRerouting invoke message. * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Q.931 call leg. * \param dest Destination number. * \param original Original called number. * \param reason Rerouting reason: cfu, cfb, cfnr * * \retval 0 on success. * \retval -1 on error. */ int qsig_cf_callrerouting(struct pri *ctrl, q931_call *call, const char *dest, const char *original, const char *reason) { struct q931_party_redirecting reroute; q931_party_redirecting_init(&reroute); /* Rerouting to the dest number. */ reroute.to.number.valid = 1; reroute.to.number.plan = (PRI_TON_UNKNOWN << 4) | PRI_NPI_E163_E164; reroute.to.number.presentation = PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; libpri_copy_string(reroute.to.number.str, dest, sizeof(reroute.to.number.str)); /* Rerouting from the original number. */ if (original) { reroute.from.number.valid = 1; reroute.from.number.plan = (PRI_TON_UNKNOWN << 4) | PRI_NPI_E163_E164; libpri_copy_string(reroute.from.number.str, original, sizeof(reroute.from.number.str)); } else { q931_party_address_to_id(&reroute.from, &call->called); } reroute.from.number.presentation = PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; /* Decode the rerouting reason. */ reroute.reason = PRI_REDIR_UNKNOWN; if (!reason) { /* No reason for rerouting given. */ } else if (!strcasecmp(reason, "cfu")) { reroute.reason = PRI_REDIR_UNCONDITIONAL; } else if (!strcasecmp(reason, "cfb")) { reroute.reason = PRI_REDIR_FORWARD_ON_BUSY; } else if (!strcasecmp(reason, "cfnr")) { reroute.reason = PRI_REDIR_FORWARD_ON_NO_REPLY; } reroute.count = (call->redirecting.count < PRI_MAX_REDIRECTS) ? call->redirecting.count + 1 : PRI_MAX_REDIRECTS; if (!call->redirecting.orig_called.number.valid) { /* * Since we do not already have an originally called party, we * must either be the first redirected to party or this call * has not been redirected before. * * Preserve who redirected to us as the originally called party. */ reroute.orig_called = call->redirecting.from; reroute.orig_reason = call->redirecting.reason; } else { reroute.orig_called = call->redirecting.orig_called; reroute.orig_reason = call->redirecting.orig_reason; } return send_reroute_request(ctrl, call, NULL, &reroute, 0 /* noNotification */); } /* End QSIG CC-CallRerouting */ /* * From Mantis issue 7778 description: (ETS 300 258, ISO 13863) * After both legs of the call are setup and Asterisk has a successful "tromboned" or bridged call ... * Asterisk sees both 'B' channels (from trombone) are on same PRI/technology and initiates "Path Replacement" events * a. Asterisk sends "Transfer Complete" messages to both call legs * b. QSIG Switch sends "PathReplacement" message on one of the legs (random 1-10sec timer expires - 1st leg to send is it!) * c. Asterisk rebroadcasts "PathReplacement" message to other call leg * d. QSIG Switch sends "Disconnect" message on one of the legs (same random timer sequence as above) * e. Asterisk rebroadcasts "Disconnect" message to other call leg * f. QSIG Switch disconnects Asterisk call legs - callers are now within QSIG switch * * Just need to resend the message to the other tromboned leg of the call. */ static int anfpr_pathreplacement_respond(struct pri *ctrl, q931_call *call, q931_ie *ie) { int res; pri_call_apdu_queue_cleanup(call->bridged_call); /* Send message */ res = pri_call_apdu_queue(call->bridged_call, Q931_FACILITY, ie->data, ie->len, NULL); if (res) { pri_message(ctrl, "Could not queue ADPU in facility message\n"); return -1; } /* Remember that if we queue a facility IE for a facility message we * have to explicitly send the facility message ourselves */ res = q931_facility(call->bridged_call->pri, call->bridged_call); if (res) { pri_message(ctrl, "Could not schedule facility message for call %d\n", call->bridged_call->cr); return -1; } return 0; } /* AFN-PR */ /*! * \brief Start a Q.SIG path replacement. * * \note Called for PRI_SWITCH_QSIG * * \note Did all the tests to see if we're on the same PRI and * are on a compatible switchtype. * * \param ctrl D channel controller for diagnostic messages or global options. * \param c1 Q.931 call leg 1 * \param c2 Q.931 call leg 2 * * \retval 0 on success. * \retval -1 on error. */ int anfpr_initiate_transfer(struct pri *ctrl, q931_call *c1, q931_call *c2) { unsigned char buffer[255]; unsigned char *pos; unsigned char *end; int res; struct fac_extension_header header; struct rose_msg_invoke msg; end = buffer + sizeof(buffer); 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 = 2; /* rejectAnyUnrecognisedInvokePdu */ pos = facility_encode_header(ctrl, buffer, end, &header); if (!pos) { return -1; } memset(&msg, 0, sizeof(msg)); msg.operation = ROSE_QSIG_CallTransferComplete; msg.invoke_id = get_invokeid(ctrl); msg.args.qsig.CallTransferComplete.end_designation = 0; /* primaryEnd */ msg.args.qsig.CallTransferComplete.redirection.presentation = 1; /* presentationRestricted */ msg.args.qsig.CallTransferComplete.call_status = 1; /* alerting */ pos = rose_encode_invoke(ctrl, pos, end, &msg); if (!pos) { return -1; } res = pri_call_apdu_queue(c1, Q931_FACILITY, buffer, pos - buffer, NULL); if (res) { pri_message(ctrl, "Could not queue ADPU in facility message\n"); return -1; } /* Remember that if we queue a facility IE for a facility message we * have to explicitly send the facility message ourselves */ res = q931_facility(c1->pri, c1); if (res) { pri_message(ctrl, "Could not schedule facility message for call %d\n", c1->cr); return -1; } /* Reuse the previous message header */ pos = facility_encode_header(ctrl, buffer, end, &header); if (!pos) { return -1; } /* Update the previous message */ msg.invoke_id = get_invokeid(ctrl); msg.args.qsig.CallTransferComplete.end_designation = 1; /* secondaryEnd */ pos = rose_encode_invoke(ctrl, pos, end, &msg); if (!pos) { return -1; } res = pri_call_apdu_queue(c2, Q931_FACILITY, buffer, pos - buffer, NULL); if (res) { pri_message(ctrl, "Could not queue ADPU in facility message\n"); return -1; } /* Remember that if we queue a facility IE for a facility message we * have to explicitly send the facility message ourselves */ res = q931_facility(c2->pri, c2); if (res) { pri_message(ctrl, "Could not schedule facility message for call %d\n", c2->cr); return -1; } return 0; } /* End AFN-PR */ /*! * \internal * \brief Encode ETSI ExplicitEctExecute 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 link_id Identifier of other call involved in transfer. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_etsi_ect_explicit_execute(struct pri *ctrl, unsigned char *pos, unsigned char *end, int link_id) { 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_ExplicitEctExecute; msg.args.etsi.ExplicitEctExecute.link_id = link_id; pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \internal * \brief ECT LinkId 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 etsi_ect_link_id_rsp(enum APDU_CALLBACK_REASON reason, struct pri *ctrl, struct q931_call *call, struct apdu_event *apdu, const struct apdu_msg_data *msg) { unsigned char buffer[256]; unsigned char *end; q931_call *call_2; switch (reason) { case APDU_CALLBACK_REASON_MSG_RESULT: call_2 = apdu->response.user.ptr; if (!q931_is_call_valid(ctrl, call_2)) { /* Call is no longer present. */ break; } end = enc_etsi_ect_explicit_execute(ctrl, buffer, buffer + sizeof(buffer), msg->response.result->args.etsi.EctLinkIdRequest.link_id); if (!end) { break; } /* Remember that if we queue a facility IE for a facility message we * have to explicitly send the facility message ourselves */ if (pri_call_apdu_queue(call_2, Q931_FACILITY, buffer, end - buffer, NULL) || q931_facility(call_2->pri, call_2)) { pri_message(ctrl, "Could not schedule facility message for call %d\n", call_2->cr); } break; default: break; } return 1; } /*! * \internal * \brief Encode ETSI ECT LinkId 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. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_etsi_ect_link_id_req(struct pri *ctrl, unsigned char *pos, unsigned char *end) { 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_EctLinkIdRequest; pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \brief Start an Explicit Call Transfer (ECT) sequence between the two calls. * * \param ctrl D channel controller for diagnostic messages or global options. * \param call_1 Q.931 call leg 1 * \param call_2 Q.931 call leg 2 * * \retval 0 on success. * \retval -1 on error. */ int etsi_initiate_transfer(struct pri *ctrl, q931_call *call_1, q931_call *call_2) { unsigned char buffer[256]; unsigned char *end; struct apdu_callback_data response; end = enc_etsi_ect_link_id_req(ctrl, buffer, buffer + sizeof(buffer)); if (!end) { return -1; } memset(&response, 0, sizeof(response)); response.invoke_id = ctrl->last_invoke; response.timeout_time = ctrl->timers[PRI_TIMER_T_RESPONSE]; response.callback = etsi_ect_link_id_rsp; response.user.ptr = call_2; /* Remember that if we queue a facility IE for a facility message we * have to explicitly send the facility message ourselves */ if (pri_call_apdu_queue(call_1, Q931_FACILITY, buffer, end - buffer, &response) || q931_facility(call_1->pri, call_1)) { pri_message(ctrl, "Could not schedule facility message for call %d\n", call_1->cr); return -1; } return 0; } /*! * \internal * \brief Encode ETSI ECT LinkId result respnose 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_id Invoke id to put in result message. * \param link_id Requested link id to put in result message. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_etsi_ect_link_id_rsp(struct pri *ctrl, unsigned char *pos, unsigned char *end, int invoke_id, int link_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 = ROSE_ETSI_EctLinkIdRequest; msg.args.etsi.EctLinkIdRequest.link_id = link_id; pos = rose_encode_result(ctrl, pos, end, &msg); return pos; } /*! * \internal * \brief Send EctLinkIdRequest result response message. * * \param ctrl D channel controller. * \param call Q.931 call leg. * \param invoke_id Invoke id to put in result message. * * \retval 0 on success. * \retval -1 on error. */ static int send_ect_link_id_rsp(struct pri *ctrl, q931_call *call, int invoke_id) { unsigned char buffer[256]; unsigned char *end; end = enc_etsi_ect_link_id_rsp(ctrl, buffer, buffer + sizeof(buffer), invoke_id, call->link_id); if (!end) { return -1; } /* Remember that if we queue a facility IE for a facility message we * have to explicitly send the facility message ourselves */ if (pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL) || q931_facility(call->pri, call)) { pri_message(ctrl, "Could not schedule facility message for call %d\n", call->cr); return -1; } return 0; } /*! * \internal * \brief Process the received ETSI EctExecute message. * * \param ctrl D channel controller. * \param call Q.931 call leg. * \param invoke_id Invoke id to put in response message. * * \details * 1) Find the active call implied by the transfer request. * 2) Create the PRI_SUBCMD_TRANSFER_CALL event. * * \retval ROSE_ERROR_None on success. * \retval error_code on error. */ static enum rose_error_code etsi_ect_execute_transfer(struct pri *ctrl, q931_call *call, int invoke_id) { enum rose_error_code error_code; struct pri_subcommand *subcmd; q931_call *call_active; switch (call->ourcallstate) { case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: case Q931_CALL_STATE_CALL_DELIVERED: case Q931_CALL_STATE_CALL_RECEIVED: case Q931_CALL_STATE_CONNECT_REQUEST: case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: case Q931_CALL_STATE_ACTIVE: if (call->master_call->hold_state != Q931_HOLD_STATE_CALL_HELD) { /* EctExecute must be sent on the held call. */ error_code = ROSE_ERROR_Gen_InvalidCallState; break; } /* Held call is being transferred. */ call_active = q931_find_held_active_call(ctrl, call); if (!call_active) { error_code = ROSE_ERROR_Gen_NotAvailable; break; } /* Setup transfer subcommand */ subcmd = q931_alloc_subcommand(ctrl); if (!subcmd) { error_code = ROSE_ERROR_Gen_NotAvailable; break; } subcmd->cmd = PRI_SUBCMD_TRANSFER_CALL; subcmd->u.transfer.call_1 = call->master_call; subcmd->u.transfer.call_2 = call_active; subcmd->u.transfer.is_call_1_held = 1; subcmd->u.transfer.is_call_2_held = 0; subcmd->u.transfer.invoke_id = invoke_id; error_code = ROSE_ERROR_None; break; default: error_code = ROSE_ERROR_Gen_InvalidCallState; break; } return error_code; } /*! * \internal * \brief Process the received ETSI ExplicitEctExecute message. * * \param ctrl D channel controller. * \param call Q.931 call leg. * \param invoke_id Invoke id to put in response message. * \param link_id Link id of the other call involved in the transfer. * * \details * 1) Find the other call specified by the link_id in transfer request. * 2) Create the PRI_SUBCMD_TRANSFER_CALL event. * * \retval ROSE_ERROR_None on success. * \retval error_code on error. */ static enum rose_error_code etsi_explicit_ect_execute_transfer(struct pri *ctrl, q931_call *call, int invoke_id, int link_id) { enum rose_error_code error_code; struct pri_subcommand *subcmd; q931_call *call_2; switch (call->ourcallstate) { case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: case Q931_CALL_STATE_CALL_DELIVERED: case Q931_CALL_STATE_CALL_RECEIVED: case Q931_CALL_STATE_CONNECT_REQUEST: case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: case Q931_CALL_STATE_ACTIVE: call_2 = q931_find_link_id_call(ctrl, link_id); if (!call_2 || call_2 == call->master_call) { error_code = ROSE_ERROR_Gen_NotAvailable; break; } /* Setup transfer subcommand */ subcmd = q931_alloc_subcommand(ctrl); if (!subcmd) { error_code = ROSE_ERROR_Gen_NotAvailable; break; } subcmd->cmd = PRI_SUBCMD_TRANSFER_CALL; subcmd->u.transfer.call_1 = call->master_call; subcmd->u.transfer.call_2 = call_2; subcmd->u.transfer.is_call_1_held = (call->master_call->hold_state == Q931_HOLD_STATE_CALL_HELD) ? 1 : 0; subcmd->u.transfer.is_call_2_held = (call_2->hold_state == Q931_HOLD_STATE_CALL_HELD) ? 1 : 0; subcmd->u.transfer.invoke_id = invoke_id; error_code = ROSE_ERROR_None; break; default: error_code = ROSE_ERROR_Gen_InvalidCallState; break; } return error_code; } /* ===== Call Transfer Supplementary Service (ECMA-178) ===== */ /*! * \internal * \brief Encode the Q.SIG CallTransferComplete invoke 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 call Call leg from which to encode call transfer. * \param call_status TRUE if call is alerting. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_qsig_call_transfer_complete(struct pri *ctrl, unsigned char *pos, unsigned char *end, q931_call *call, int call_status) { 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 = 0; /* discardAnyUnrecognisedInvokePdu */ pos = facility_encode_header(ctrl, pos, end, &header); if (!pos) { return NULL; } memset(&msg, 0, sizeof(msg)); msg.operation = ROSE_QSIG_CallTransferComplete; msg.invoke_id = get_invokeid(ctrl); msg.args.qsig.CallTransferComplete.end_designation = 0; /* primaryEnd */ /* redirectionNumber is the local_id.number */ q931_copy_presented_number_screened_to_rose(ctrl, &msg.args.qsig.CallTransferComplete.redirection, &call->local_id.number); /* redirectionName is the local_id.name */ if (call->local_id.name.valid) { msg.args.qsig.CallTransferComplete.redirection_name_present = 1; q931_copy_name_to_rose(ctrl, &msg.args.qsig.CallTransferComplete.redirection_name, &call->local_id.name); } if (call_status) { msg.args.qsig.CallTransferComplete.call_status = 1; /* alerting */ } pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \internal * \brief Encode the ETSI EctInform invoke 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 call Call leg from which to encode inform message. * \param call_status TRUE if call is alerting. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_etsi_ect_inform(struct pri *ctrl, unsigned char *pos, unsigned char *end, q931_call *call, int call_status) { struct rose_msg_invoke msg; pos = facility_encode_header(ctrl, pos, end, NULL); if (!pos) { return NULL; } memset(&msg, 0, sizeof(msg)); msg.operation = ROSE_ETSI_EctInform; msg.invoke_id = get_invokeid(ctrl); if (!call_status) { msg.args.etsi.EctInform.status = 1;/* active */ /* * EctInform(active) contains the redirectionNumber * redirectionNumber is the local_id.number */ msg.args.etsi.EctInform.redirection_present = 1; q931_copy_presented_number_unscreened_to_rose(ctrl, &msg.args.etsi.EctInform.redirection, &call->local_id.number); } pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \internal * \brief Encode and queue the CallTransferComplete/EctInform invoke message. * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Call leg from which to encode call transfer. * \param call_status TRUE if call is alerting. * * \retval 0 on success. * \retval -1 on error. */ static int rose_call_transfer_complete_encode(struct pri *ctrl, q931_call *call, int call_status) { unsigned char buffer[256]; unsigned char *end; switch (ctrl->switchtype) { case PRI_SWITCH_EUROISDN_E1: case PRI_SWITCH_EUROISDN_T1: end = enc_etsi_ect_inform(ctrl, buffer, buffer + sizeof(buffer), call, call_status); break; case PRI_SWITCH_QSIG: end = enc_qsig_call_transfer_complete(ctrl, buffer, buffer + sizeof(buffer), call, call_status); break; default: return -1; } if (!end) { return -1; } return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL); } /* ===== End Call Transfer Supplementary Service (ECMA-178) ===== */ /*! * \internal * \brief Encode the Q.SIG CalledName invoke 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 name Name data which to encode name. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_qsig_called_name(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct q931_party_name *name) { 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 = 0; /* discardAnyUnrecognisedInvokePdu */ pos = facility_encode_header(ctrl, pos, end, &header); if (!pos) { return NULL; } memset(&msg, 0, sizeof(msg)); msg.operation = ROSE_QSIG_CalledName; msg.invoke_id = get_invokeid(ctrl); /* CalledName */ q931_copy_name_to_rose(ctrl, &msg.args.qsig.CalledName.name, name); pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \internal * \brief Encode and queue the Q.SIG CalledName invoke message. * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Call leg from which to encode name. * * \retval 0 on success. * \retval -1 on error. */ int rose_called_name_encode(struct pri *ctrl, q931_call *call, int messagetype) { unsigned char buffer[256]; unsigned char *end; /* CalledName is the local_id.name */ end = enc_qsig_called_name(ctrl, buffer, buffer + sizeof(buffer), &call->local_id.name); if (!end) { return -1; } return pri_call_apdu_queue(call, messagetype, buffer, end - buffer, NULL); } /*! * \internal * \brief Encode the Q.SIG ConnectedName invoke 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 name Name data which to encode name. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_qsig_connected_name(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct q931_party_name *name) { 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 = 0; /* discardAnyUnrecognisedInvokePdu */ pos = facility_encode_header(ctrl, pos, end, &header); if (!pos) { return NULL; } memset(&msg, 0, sizeof(msg)); msg.operation = ROSE_QSIG_ConnectedName; msg.invoke_id = get_invokeid(ctrl); /* ConnectedName */ q931_copy_name_to_rose(ctrl, &msg.args.qsig.ConnectedName.name, name); pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \internal * \brief Encode and queue the Q.SIG ConnectedName invoke message. * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Call leg from which to encode name. * * \retval 0 on success. * \retval -1 on error. */ int rose_connected_name_encode(struct pri *ctrl, q931_call *call, int messagetype) { unsigned char buffer[256]; unsigned char *end; /* ConnectedName is the local_id.name */ end = enc_qsig_connected_name(ctrl, buffer, buffer + sizeof(buffer), &call->local_id.name); if (!end) { return -1; } return pri_call_apdu_queue(call, messagetype, buffer, end - buffer, NULL); } /*! * \brief Put the APDU on the call queue. * * \param call Call to enqueue message. * \param messagetype Q.931 message type. * \param apdu Facility ie contents buffer. * \param apdu_len Length of the contents buffer. * \param response Sender supplied information to handle APDU response messages. * NULL if don't care about responses. * * \note * Only APDU messages with an invoke component can supply a response pointer. * If any other APDU messages supply a response pointer then aliasing of the * invoke_id can occur. * * \retval 0 on success. * \retval -1 on error. */ int pri_call_apdu_queue(q931_call *call, int messagetype, const unsigned char *apdu, int apdu_len, struct apdu_callback_data *response) { struct apdu_event *cur = NULL; struct apdu_event *new_event = NULL; if (!call || !messagetype || !apdu || apdu_len < 1 || sizeof(new_event->apdu) < apdu_len) { return -1; } switch (messagetype) { case Q931_FACILITY: break; default: if (q931_is_dummy_call(call)) { pri_error(call->pri, "!! Cannot send %s message on dummy call reference.\n", msg2str(messagetype)); return -1; } break; } new_event = calloc(1, sizeof(*new_event)); if (!new_event) { pri_error(call->pri, "!! Malloc failed!\n"); return -1; } /* Fill in the APDU event */ new_event->message = messagetype; if (response) { new_event->response = *response; } new_event->call = call; new_event->apdu_len = apdu_len; memcpy(new_event->apdu, apdu, apdu_len); /* Append APDU event to the end of the list. */ if (call->apdus) { for (cur = call->apdus; cur->next; cur = cur->next) { } cur->next = new_event; } else { call->apdus = new_event; } return 0; } /* Used by q931.c to cleanup the apdu queue upon destruction of a call */ void pri_call_apdu_queue_cleanup(q931_call *call) { struct apdu_event *cur_event; struct apdu_event *free_event; if (call) { cur_event = call->apdus; 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); } free_event = cur_event; cur_event = cur_event->next; free(free_event); } } } /*! * \brief Find an outstanding APDU with the given invoke id. * * \param call Call to find APDU. * \param invoke_id Invoke id to match outstanding APDUs in queue. * * \retval apdu_event if found. * \retval NULL if not found. */ 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 * callback and timeout timer active. Therefore, an invoke_id of * zero is valid and not just the result of a memset(). */ if (apdu->response.invoke_id == invoke_id && apdu->sent) { break; } } 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. * * \param call Call to remove the APDU. * \param doomed APDU event to delete. * * \return Nothing */ void pri_call_apdu_delete(struct q931_call *call, struct apdu_event *doomed) { if (pri_call_apdu_extract(call, doomed)) { free(doomed); } } /*! \note Only called when sending the SETUP message. */ int pri_call_add_standard_apdus(struct pri *ctrl, q931_call *call) { if (!ctrl->sendfacility) { return 0; } switch (ctrl->switchtype) { case PRI_SWITCH_EUROISDN_E1: case PRI_SWITCH_EUROISDN_T1: if (call->aoc_charging_request) { aoc_charging_request_send(ctrl, call, call->aoc_charging_request); } if (PTMP_MODE(ctrl)) { /* PTMP mode */ break; } /* PTP mode */ if (call->redirecting.count) { rose_diverting_leg_information2_encode(ctrl, call); /* * Expect a DivertingLegInformation3 to update the COLR of the * redirecting-to party we are attempting to call now. */ call->redirecting.state = Q931_REDIRECTING_STATE_EXPECTING_RX_DIV_LEG_3; } break; case PRI_SWITCH_QSIG: /* For Q.SIG it does network and cpe operations */ if (call->redirecting.count) { rose_diverting_leg_information2_encode(ctrl, call); /* * Expect a DivertingLegInformation3 to update the COLR of the * redirecting-to party we are attempting to call now. */ call->redirecting.state = Q931_REDIRECTING_STATE_EXPECTING_RX_DIV_LEG_3; } add_callername_facility_ies(ctrl, call, 1); break; case PRI_SWITCH_NI2: add_callername_facility_ies(ctrl, call, (ctrl->localtype == PRI_CPE)); break; case PRI_SWITCH_DMS100: if (ctrl->localtype == PRI_CPE) { add_dms100_transfer_ability_apdu(ctrl, call); } break; default: break; } return 0; } /*! * \brief Send the CallTransferComplete/EctInform invoke message. * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Call leg from which to encode call transfer. * \param call_status TRUE if call is alerting. * * \retval 0 on success. * \retval -1 on error. */ int send_call_transfer_complete(struct pri *ctrl, q931_call *call, int call_status) { int status; status = rose_call_transfer_complete_encode(ctrl, call, call_status); if (!status) { if (!call_status && call->local_id.number.valid && (ctrl->display_flags.send & PRI_DISPLAY_OPTION_NAME_UPDATE)) { status = q931_facility_display_name(ctrl, call, &call->local_id.name); } else { status = q931_facility(ctrl, call); } } if (status) { pri_message(ctrl, "Could not schedule facility message for call transfer completed.\n"); return -1; } return 0; } /*! * \internal * \brief Encode the ETSI RequestSubaddress invoke 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. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_etsi_request_subaddress(struct pri *ctrl, unsigned char *pos, unsigned char *end) { struct rose_msg_invoke msg; pos = facility_encode_header(ctrl, pos, end, NULL); if (!pos) { return NULL; } memset(&msg, 0, sizeof(msg)); msg.operation = ROSE_ETSI_RequestSubaddress; msg.invoke_id = get_invokeid(ctrl); pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \brief Encode and queue the RequestSubaddress invoke message. * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Call leg from which to encode message. * * \retval 0 on success. * \retval -1 on error. */ int rose_request_subaddress_encode(struct pri *ctrl, struct q931_call *call) { unsigned char buffer[256]; unsigned char *end; switch (ctrl->switchtype) { case PRI_SWITCH_EUROISDN_E1: case PRI_SWITCH_EUROISDN_T1: end = enc_etsi_request_subaddress(ctrl, buffer, buffer + sizeof(buffer)); break; case PRI_SWITCH_QSIG: default: return -1; } if (!end) { return -1; } return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL); } /*! * \internal * \brief Encode the ETSI SubaddressTransfer invoke 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 call Call leg from which to encode message. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_etsi_subaddress_transfer(struct pri *ctrl, unsigned char *pos, unsigned char *end, struct q931_call *call) { struct rose_msg_invoke msg; pos = facility_encode_header(ctrl, pos, end, NULL); if (!pos) { return NULL; } memset(&msg, 0, sizeof(msg)); msg.operation = ROSE_ETSI_SubaddressTransfer; msg.invoke_id = get_invokeid(ctrl); if (!call->local_id.subaddress.valid) { return NULL; } q931_copy_subaddress_to_rose(ctrl, &msg.args.etsi.SubaddressTransfer.subaddress, &call->local_id.subaddress); pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \internal * \brief Encode the Q.SIG SubaddressTransfer invoke 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 call Call leg from which to encode message. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_qsig_subaddress_transfer(struct pri *ctrl, unsigned char *pos, unsigned char *end, struct q931_call *call) { 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 = 0; /* discardAnyUnrecognisedInvokePdu */ pos = facility_encode_header(ctrl, pos, end, &header); if (!pos) { return NULL; } memset(&msg, 0, sizeof(msg)); msg.operation = ROSE_QSIG_SubaddressTransfer; msg.invoke_id = get_invokeid(ctrl); if (!call->local_id.subaddress.valid) { return NULL; } q931_copy_subaddress_to_rose(ctrl, &msg.args.qsig.SubaddressTransfer.redirection_subaddress, &call->local_id.subaddress); pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \internal * \brief Encode and queue the SubaddressTransfer invoke message. * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Call leg from which to encode message. * * \retval 0 on success. * \retval -1 on error. */ static int rose_subaddress_transfer_encode(struct pri *ctrl, struct q931_call *call) { unsigned char buffer[256]; unsigned char *end; switch (ctrl->switchtype) { case PRI_SWITCH_EUROISDN_E1: case PRI_SWITCH_EUROISDN_T1: end = enc_etsi_subaddress_transfer(ctrl, buffer, buffer + sizeof(buffer), call); break; case PRI_SWITCH_QSIG: end = enc_qsig_subaddress_transfer(ctrl, buffer, buffer + sizeof(buffer), call); break; default: return -1; } if (!end) { return -1; } return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL); } /*! * \brief Send a FACILITY SubaddressTransfer. * * \param ctrl D channel controller. * \param call Q.931 call leg * * \retval 0 on success. * \retval -1 on error. */ int send_subaddress_transfer(struct pri *ctrl, struct q931_call *call) { if (rose_subaddress_transfer_encode(ctrl, call) || q931_facility(ctrl, call)) { pri_message(ctrl, "Could not schedule facility message for subaddress transfer.\n"); return -1; } return 0; } /*! * \internal * \brief Handle the received RequestSubaddress facility. * * \param ctrl D channel controller. * \param call Q.931 call leg * * \return Nothing */ static void etsi_request_subaddress(struct pri *ctrl, struct q931_call *call) { struct q931_party_name name; int changed = 0; switch (call->notify) { case PRI_NOTIFY_TRANSFER_ACTIVE: if (q931_party_number_cmp(&call->remote_id.number, &call->redirection_number)) { /* The remote party number information changed. */ call->remote_id.number = call->redirection_number; changed = 1; } /* Fall through */ case PRI_NOTIFY_TRANSFER_ALERTING: if (ctrl->display_flags.receive & PRI_DISPLAY_OPTION_NAME_UPDATE) { if (q931_display_name_get(call, &name)) { if (q931_party_name_cmp(&call->remote_id.name, &name)) { /* The remote party name information changed. */ call->remote_id.name = name; changed = 1; } } } if (call->redirection_number.valid && q931_party_number_cmp(&call->remote_id.number, &call->redirection_number)) { /* The remote party number information changed. */ call->remote_id.number = call->redirection_number; changed = 1; } if (call->remote_id.subaddress.valid) { /* * Clear the subaddress as the remote party has been changed. * Any new subaddress will arrive later. */ q931_party_subaddress_init(&call->remote_id.subaddress); changed = 1; } if (changed) { call->incoming_ct_state = INCOMING_CT_STATE_POST_CONNECTED_LINE; } break; default: break; } /* Send our subaddress back if we have one. */ if (call->local_id.subaddress.valid) { send_subaddress_transfer(ctrl, call); } } /*! * \internal * \brief Handle the received SubaddressTransfer facility subaddress. * * \param ctrl D channel controller. * \param call Q.931 call leg * \param subaddr Received subaddress of remote party. * * \return Nothing */ static void handle_subaddress_transfer(struct pri *ctrl, struct q931_call *call, const struct rosePartySubaddress *subaddr) { int changed = 0; struct q931_party_name name; struct q931_party_subaddress q931_subaddress; q931_party_subaddress_init(&q931_subaddress); rose_copy_subaddress_to_q931(ctrl, &q931_subaddress, subaddr); if (q931_party_subaddress_cmp(&call->remote_id.subaddress, &q931_subaddress)) { call->remote_id.subaddress = q931_subaddress; changed = 1; } if (call->redirection_number.valid && q931_party_number_cmp(&call->remote_id.number, &call->redirection_number)) { /* The remote party number information changed. */ call->remote_id.number = call->redirection_number; changed = 1; } if (ctrl->display_flags.receive & PRI_DISPLAY_OPTION_NAME_UPDATE) { if (q931_display_name_get(call, &name)) { if (q931_party_name_cmp(&call->remote_id.name, &name)) { /* The remote party name information changed. */ call->remote_id.name = name; changed = 1; } } } if (changed) { call->incoming_ct_state = INCOMING_CT_STATE_POST_CONNECTED_LINE; } } /*! * \internal * \brief Encode a plain facility ETSI error code. * * \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 call Call leg from which to encode error message response. * \param invoke_id Invoke id to put in error message response. * \param code Error code to put in error message response. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_etsi_error(struct pri *ctrl, unsigned char *pos, unsigned char *end, q931_call *call, int invoke_id, enum rose_error_code code) { struct rose_msg_error msg; pos = facility_encode_header(ctrl, pos, end, NULL); if (!pos) { return NULL; } memset(&msg, 0, sizeof(msg)); msg.invoke_id = invoke_id; msg.code = code; pos = rose_encode_error(ctrl, pos, end, &msg); return pos; } /*! * \internal * \brief Encode a plain facility Q.SIG error code. * * \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 call Call leg from which to encode error message response. * \param invoke_id Invoke id to put in error message response. * \param code Error code to put in error message response. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_qsig_error(struct pri *ctrl, unsigned char *pos, unsigned char *end, q931_call *call, int invoke_id, enum rose_error_code code) { struct fac_extension_header header; struct rose_msg_error 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 = invoke_id; msg.code = code; pos = rose_encode_error(ctrl, pos, end, &msg); return pos; } /*! * \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. */ 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]; unsigned char *end; switch (ctrl->switchtype) { case PRI_SWITCH_EUROISDN_E1: case PRI_SWITCH_EUROISDN_T1: end = enc_etsi_error(ctrl, buffer, buffer + sizeof(buffer), call, invoke_id, code); break; case PRI_SWITCH_QSIG: end = enc_qsig_error(ctrl, buffer, buffer + sizeof(buffer), call, invoke_id, code); break; default: return -1; } if (!end) { return -1; } return pri_call_apdu_queue(call, msgtype, buffer, end - buffer, NULL); } /*! * \brief Encode and send 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 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. */ int send_facility_error(struct pri *ctrl, q931_call *call, int invoke_id, enum rose_error_code 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"); return -1; } return 0; } /*! * \internal * \brief Encode a plain facility ETSI result ok. * * \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 call Call leg from which to encode result ok message response. * \param invoke_id Invoke id to put in result ok message response. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_etsi_result_ok(struct pri *ctrl, unsigned char *pos, unsigned char *end, q931_call *call, int invoke_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 = ROSE_None; pos = rose_encode_result(ctrl, pos, end, &msg); return pos; } /*! * \internal * \brief Encode a plain facility Q.SIG result ok. * * \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 call Call leg from which to encode result ok message response. * \param invoke_id Invoke id to put in result ok message response. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_qsig_result_ok(struct pri *ctrl, unsigned char *pos, unsigned char *end, q931_call *call, int invoke_id) { 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 = invoke_id; msg.operation = ROSE_None; pos = rose_encode_result(ctrl, pos, end, &msg); return pos; } /*! * \brief Encode and queue a plain ROSE result ok. * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Call leg from which to encode result ok message response. * \param msgtype Q.931 message type to put facility ie in. * \param invoke_id Invoke id to put in result ok message response. * * \retval 0 on success. * \retval -1 on error. */ int rose_result_ok_encode(struct pri *ctrl, q931_call *call, int msgtype, int invoke_id) { unsigned char buffer[256]; unsigned char *end; switch (ctrl->switchtype) { case PRI_SWITCH_EUROISDN_E1: case PRI_SWITCH_EUROISDN_T1: end = enc_etsi_result_ok(ctrl, buffer, buffer + sizeof(buffer), call, invoke_id); break; case PRI_SWITCH_QSIG: end = enc_qsig_result_ok(ctrl, buffer, buffer + sizeof(buffer), call, invoke_id); break; default: return -1; } if (!end) { return -1; } return pri_call_apdu_queue(call, msgtype, buffer, end - buffer, NULL); } /*! * \brief Encode and send a FACILITY message with a plain ROSE result ok. * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Call leg from which to encode result ok message response. * \param invoke_id Invoke id to put in result ok message response. * * \retval 0 on success. * \retval -1 on error. */ 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)) { pri_message(ctrl, "Could not schedule facility message for result OK message.\n"); return -1; } return 0; } int pri_rerouting_rsp(struct pri *ctrl, q931_call *call, int invoke_id, enum PRI_REROUTING_RSP_CODE code) { enum rose_error_code rose_err; if (!ctrl || !pri_is_call_valid(ctrl, call)) { return -1; } /* Convert the public rerouting response code to an error code or result ok. */ rose_err = ROSE_ERROR_Gen_ResourceUnavailable; switch (code) { case PRI_REROUTING_RSP_OK_CLEAR: /* * Send the response out on the next message which should be * either Q931_DISCONNECT or Q931_RELEASE depending upon who * initiates the disconnect first. */ return rose_result_ok_encode(ctrl, call, Q931_ANY_MESSAGE, invoke_id); case PRI_REROUTING_RSP_OK_RETAIN: return send_facility_result_ok(ctrl, call, invoke_id); case PRI_REROUTING_RSP_NOT_SUBSCRIBED: rose_err = ROSE_ERROR_Gen_NotSubscribed; break; case PRI_REROUTING_RSP_NOT_AVAILABLE: rose_err = ROSE_ERROR_Gen_NotAvailable; break; case PRI_REROUTING_RSP_NOT_ALLOWED: rose_err = ROSE_ERROR_Gen_SupplementaryServiceInteractionNotAllowed; break; case PRI_REROUTING_RSP_INVALID_NUMBER: rose_err = ROSE_ERROR_Div_InvalidDivertedToNr; break; case PRI_REROUTING_RSP_SPECIAL_SERVICE_NUMBER: rose_err = ROSE_ERROR_Div_SpecialServiceNr; break; case PRI_REROUTING_RSP_DIVERSION_TO_SELF: rose_err = ROSE_ERROR_Div_DiversionToServedUserNr; break; case PRI_REROUTING_RSP_MAX_DIVERSIONS_EXCEEDED: rose_err = ROSE_ERROR_Div_NumberOfDiversionsExceeded; break; case PRI_REROUTING_RSP_RESOURCE_UNAVAILABLE: rose_err = ROSE_ERROR_Gen_ResourceUnavailable; break; } return send_facility_error(ctrl, call, invoke_id, rose_err); } int pri_transfer_rsp(struct pri *ctrl, q931_call *call, int invoke_id, int is_successful) { if (!ctrl || !pri_is_call_valid(ctrl, call)) { return -1; } if (is_successful) { return rose_result_ok_encode(ctrl, call, Q931_DISCONNECT, invoke_id); } else { return send_facility_error(ctrl, call, invoke_id, ROSE_ERROR_Gen_NotAvailable); } } /*! * \internal * \brief MCIDRequest 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 mcid_req_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_subcommand *subcmd; int status; int fail_code; switch (reason) { case APDU_CALLBACK_REASON_TIMEOUT: status = 1;/* timeout */ fail_code = 0; break; case APDU_CALLBACK_REASON_MSG_RESULT: status = 0;/* success */ fail_code = 0; break; case APDU_CALLBACK_REASON_MSG_ERROR: status = 2;/* error */ fail_code = msg->response.error->code; break; case APDU_CALLBACK_REASON_MSG_REJECT: status = 3;/* reject */ fail_code = 0; fail_code = msg->response.reject->code; break; default: return 1; } subcmd = q931_alloc_subcommand(ctrl); if (subcmd) { /* Indicate that our MCID request has completed. */ subcmd->cmd = PRI_SUBCMD_MCID_RSP; subcmd->u.mcid_rsp.status = status; subcmd->u.mcid_rsp.fail_code = fail_code; } else { /* Oh, well. */ } return 1; } /*! * \internal * \brief Encode a MCIDRequest 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 call Call leg from which to encode message. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_etsi_mcid_req(struct pri *ctrl, unsigned char *pos, unsigned char *end, q931_call *call) { 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_MCIDRequest; pos = rose_encode_invoke(ctrl, pos, end, &msg); return pos; } /*! * \internal * \brief Encode and queue a MCID request message. * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Call leg from which to encode message. * * \retval 0 on success. * \retval -1 on error. */ static int rose_mcid_req_encode(struct pri *ctrl, q931_call *call) { unsigned char buffer[256]; unsigned char *end; struct apdu_callback_data response; switch (ctrl->switchtype) { case PRI_SWITCH_EUROISDN_E1: case PRI_SWITCH_EUROISDN_T1: end = enc_etsi_mcid_req(ctrl, buffer, buffer + sizeof(buffer), call); break; default: return -1; } if (!end) { return -1; } memset(&response, 0, sizeof(response)); response.invoke_id = ctrl->last_invoke; response.timeout_time = ctrl->timers[PRI_TIMER_T_RESPONSE]; response.callback = mcid_req_response; return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, &response); } int pri_mcid_req_send(struct pri *ctrl, q931_call *call) { if (!ctrl || !pri_is_call_valid(ctrl, call)) { return -1; } if (call->cc.originated) { /* We can only send MCID if we answered the call. */ return -1; } if (rose_mcid_req_encode(ctrl, call) || q931_facility(ctrl, call)) { pri_message(ctrl, "Could not schedule facility message for MCID request message.\n"); return -1; } return 0; } void pri_mcid_enable(struct pri *ctrl, int enable) { if (ctrl) { ctrl->mcid_support = enable ? 1 : 0; } } /*! * \brief Handle the ROSE reject 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 ie Raw ie contents. * \param header Decoded facility header before ROSE. * \param reject Decoded ROSE reject message contents. * * \return Nothing */ 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; struct apdu_msg_data msg; 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)); } switch (ctrl->switchtype) { case PRI_SWITCH_DMS100: /* The DMS-100 switch apparently handles invoke_id as an invoke operation. */ return; default: break; } if (!reject->invoke_id_present) { /* * No invoke id to look up so we cannot match it to any outstanding APDUs. * This REJECT is apparently meant for someone monitoring the link. */ 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 = ctrl->link.dummy_call; if (orig_call) { apdu = pri_call_apdu_find(orig_call, reject->invoke_id); } } 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(orig_call, apdu); } } /*! * \brief Handle the ROSE error 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 ie Raw ie contents. * \param header Decoded facility header before ROSE. * \param error Decoded ROSE error message contents. * * \return Nothing */ 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) { const char *dms100_operation; q931_call *orig_call; struct apdu_event *apdu; struct apdu_msg_data msg; 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: pri_message(ctrl, "\tINVOKE ID: %d\n", error->invoke_id); break; } pri_message(ctrl, "\tERROR: %s\n", rose_error2str(error->code)); } switch (ctrl->switchtype) { case PRI_SWITCH_DMS100: /* The DMS-100 switch apparently handles invoke_id as an invoke operation. */ return; default: break; } 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 = ctrl->link.dummy_call; if (orig_call) { apdu = pri_call_apdu_find(orig_call, error->invoke_id); } } 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(orig_call, apdu); } } /*! * \brief Handle the ROSE result 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 ie Raw ie contents. * \param header Decoded facility header before ROSE. * \param result Decoded ROSE result message contents. * * \return Nothing */ 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; struct apdu_msg_data msg; switch (ctrl->switchtype) { case PRI_SWITCH_DMS100: /* The DMS-100 switch apparently handles invoke_id as an invoke operation. */ switch (result->invoke_id) { case ROSE_DMS100_RLT_OPERATION_IND: if (result->operation != ROSE_DMS100_RLT_OperationInd) { pri_message(ctrl, "Invalid Operation value in return result! %s\n", rose_operation2str(result->operation)); break; } /* We have enough data to transfer the call */ call->rlt_call_id = result->args.dms100.RLT_OperationInd.call_id; call->transferable = 1; break; case ROSE_DMS100_RLT_THIRD_PARTY: if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, "Successfully completed RLT transfer!\n"); } break; default: pri_message(ctrl, "Could not parse invoke of type %d!\n", result->invoke_id); break; } return; default: break; } 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 = ctrl->link.dummy_call; if (orig_call) { apdu = pri_call_apdu_find(orig_call, result->invoke_id); } } 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(orig_call, apdu); } } /*! * \brief Handle the ROSE 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 ie Raw ie contents. * \param header Decoded facility header before ROSE. * \param invoke Decoded ROSE invoke message contents. * * \return Nothing */ 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; enum rose_error_code error_code; switch (invoke->operation) { #if 0 /* Not handled yet */ case ROSE_ETSI_ActivationDiversion: break; case ROSE_ETSI_DeactivationDiversion: break; case ROSE_ETSI_ActivationStatusNotificationDiv: break; case ROSE_ETSI_DeactivationStatusNotificationDiv: break; case ROSE_ETSI_InterrogationDiversion: break; case ROSE_ETSI_DiversionInformation: break; #endif /* Not handled yet */ case ROSE_ETSI_CallDeflection: if (!ctrl->deflection_support) { send_facility_error(ctrl, call, invoke->invoke_id, ROSE_ERROR_Gen_NotSubscribed); break; } if (!q931_master_pass_event(ctrl, call, msgtype)) { /* Some other user is further along to connecting than this call. */ send_facility_error(ctrl, call, invoke->invoke_id, ROSE_ERROR_Div_IncomingCallAccepted); break; } if (call->master_call->deflection_in_progress) { /* Someone else is already doing a call deflection. */ send_facility_error(ctrl, call, invoke->invoke_id, ROSE_ERROR_Div_RequestAlreadyAccepted); break; } subcmd = q931_alloc_subcommand(ctrl); if (!subcmd) { /* * ROSE_ERROR_Gen_ResourceUnavailable was not in the list of allowed codes, * but we will send it anyway. */ send_facility_error(ctrl, call, invoke->invoke_id, ROSE_ERROR_Gen_ResourceUnavailable); break; } call->master_call->deflection_in_progress = 1; q931_party_redirecting_init(&deflection); /* Deflecting from the called address. */ q931_party_address_to_id(&deflection.from, &call->called); if (invoke->args.etsi.CallDeflection.presentation_allowed_to_diverted_to_user_present) { deflection.from.number.presentation = invoke->args.etsi.CallDeflection.presentation_allowed_to_diverted_to_user ? PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED : PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; } else { deflection.from.number.presentation = PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; } /* Deflecting to the new address. */ rose_copy_address_to_id_q931(ctrl, &deflection.to, &invoke->args.etsi.CallDeflection.deflection); deflection.to.number.presentation = deflection.from.number.presentation; deflection.count = (call->redirecting.count < PRI_MAX_REDIRECTS) ? call->redirecting.count + 1 : PRI_MAX_REDIRECTS; deflection.reason = PRI_REDIR_DEFLECTION; if (deflection.count == 1) { deflection.orig_called = deflection.from; deflection.orig_reason = deflection.reason; } else { deflection.orig_called = call->redirecting.orig_called; deflection.orig_reason = call->redirecting.orig_reason; } subcmd->cmd = PRI_SUBCMD_REROUTING; subcmd->u.rerouting.invoke_id = invoke->invoke_id; subcmd->u.rerouting.subscription_option = 3;/* notApplicable */ q931_party_id_copy_to_pri(&subcmd->u.rerouting.caller, &call->local_id); q931_party_redirecting_copy_to_pri(&subcmd->u.rerouting.deflection, &deflection); break; case ROSE_ETSI_CallRerouting: if (!ctrl->deflection_support) { send_facility_error(ctrl, call, invoke->invoke_id, ROSE_ERROR_Gen_NotSubscribed); break; } subcmd = q931_alloc_subcommand(ctrl); if (!subcmd) { send_facility_error(ctrl, call, invoke->invoke_id, ROSE_ERROR_Gen_ResourceUnavailable); break; } q931_party_redirecting_init(&deflection); /* Rerouting from the last address. */ rose_copy_presented_number_unscreened_to_q931(ctrl, &deflection.from.number, &invoke->args.etsi.CallRerouting.last_rerouting); /* Rerouting to the new address. */ rose_copy_address_to_id_q931(ctrl, &deflection.to, &invoke->args.etsi.CallRerouting.called_address); switch (invoke->args.etsi.CallRerouting.subscription_option) { default: case 0: /* noNotification */ case 1: /* notificationWithoutDivertedToNr */ deflection.to.number.presentation = PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; break; case 2: /* notificationWithDivertedToNr */ deflection.to.number.presentation = PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; break; } /* Calling party subaddress update. */ party_id = call->local_id; deflection.count = invoke->args.etsi.CallRerouting.rerouting_counter; deflection.reason = redirectingreason_for_q931(ctrl, invoke->args.etsi.CallRerouting.rerouting_reason); if (deflection.count == 1) { deflection.orig_called = deflection.from; deflection.orig_reason = deflection.reason; } else { deflection.orig_called = call->redirecting.orig_called; deflection.orig_reason = call->redirecting.orig_reason; } subcmd->cmd = PRI_SUBCMD_REROUTING; subcmd->u.rerouting.invoke_id = invoke->invoke_id; subcmd->u.rerouting.subscription_option = invoke->args.etsi.CallRerouting.subscription_option; q931_party_id_copy_to_pri(&subcmd->u.rerouting.caller, &party_id); q931_party_redirecting_copy_to_pri(&subcmd->u.rerouting.deflection, &deflection); break; #if 0 /* Not handled yet */ case ROSE_ETSI_InterrogateServedUserNumbers: break; #endif /* Not handled yet */ case ROSE_ETSI_DivertingLegInformation1: if (invoke->args.etsi.DivertingLegInformation1.diverted_to_present) { rose_copy_presented_number_unscreened_to_q931(ctrl, &party_id.number, &invoke->args.etsi.DivertingLegInformation1.diverted_to); /* * We set the presentation value since the sender cannot know the * presentation value preference of the destination party. */ if (party_id.number.str[0]) { party_id.number.presentation = PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; } else { party_id.number.presentation = PRES_NUMBER_NOT_AVAILABLE; } } else { q931_party_number_init(&party_id.number); party_id.number.valid = 1; } /* * Unless otherwise indicated by CONNECT, the divertedToNumber will be * the remote_id.number. */ if (!call->connected_number_in_message) { call->remote_id.number = party_id.number; } /* divertedToNumber is put in redirecting.to.number */ switch (invoke->args.etsi.DivertingLegInformation1.subscription_option) { default: case 0: /* noNotification */ case 1: /* notificationWithoutDivertedToNr */ q931_party_number_init(&call->redirecting.to.number); call->redirecting.to.number.valid = 1; call->redirecting.to.number.presentation = PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; break; case 2: /* notificationWithDivertedToNr */ call->redirecting.to.number = party_id.number; break; } call->redirecting.reason = redirectingreason_for_q931(ctrl, invoke->args.etsi.DivertingLegInformation1.diversion_reason); if (call->redirecting.count < PRI_MAX_REDIRECTS) { ++call->redirecting.count; } call->redirecting.state = Q931_REDIRECTING_STATE_EXPECTING_RX_DIV_LEG_3; break; case ROSE_ETSI_DivertingLegInformation2: call->redirecting.state = Q931_REDIRECTING_STATE_PENDING_TX_DIV_LEG_3; call->redirecting.count = invoke->args.etsi.DivertingLegInformation2.diversion_counter; if (!call->redirecting.count) { /* To be safe, make sure that the count is non-zero. */ call->redirecting.count = 1; } call->redirecting.reason = redirectingreason_for_q931(ctrl, invoke->args.etsi.DivertingLegInformation2.diversion_reason); /* divertingNr is put in redirecting.from.number */ if (invoke->args.etsi.DivertingLegInformation2.diverting_present) { rose_copy_presented_number_unscreened_to_q931(ctrl, &call->redirecting.from.number, &invoke->args.etsi.DivertingLegInformation2.diverting); } else if (!call->redirecting_number_in_message) { q931_party_number_init(&call->redirecting.from.number); call->redirecting.from.number.valid = 1; } call->redirecting.orig_reason = PRI_REDIR_UNKNOWN; /* originalCalledNr is put in redirecting.orig_called.number */ if (invoke->args.etsi.DivertingLegInformation2.original_called_present) { rose_copy_presented_number_unscreened_to_q931(ctrl, &call->redirecting.orig_called.number, &invoke->args.etsi.DivertingLegInformation2.original_called); } else { q931_party_number_init(&call->redirecting.orig_called.number); } break; case ROSE_ETSI_DivertingLegInformation3: /* * Unless otherwise indicated by CONNECT, this will be the * remote_id.number.presentation. */ if (!invoke->args.etsi.DivertingLegInformation3.presentation_allowed_indicator) { call->redirecting.to.number.presentation = PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; if (!call->connected_number_in_message) { call->remote_id.number.presentation = PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; } } switch (call->redirecting.state) { case Q931_REDIRECTING_STATE_EXPECTING_RX_DIV_LEG_3: call->redirecting.state = Q931_REDIRECTING_STATE_IDLE; subcmd = q931_alloc_subcommand(ctrl); if (!subcmd) { break; } /* Setup redirecting subcommand */ subcmd->cmd = PRI_SUBCMD_REDIRECTING; q931_party_redirecting_copy_to_pri(&subcmd->u.redirecting, &call->redirecting); break; default: break; } break; case ROSE_ETSI_ChargingRequest: aoc_etsi_aoc_request(ctrl, call, invoke); break; case ROSE_ETSI_AOCSCurrency: aoc_etsi_aoc_s_currency(ctrl, invoke); break; case ROSE_ETSI_AOCSSpecialArr: aoc_etsi_aoc_s_special_arrangement(ctrl, invoke); break; case ROSE_ETSI_AOCDCurrency: aoc_etsi_aoc_d_currency(ctrl, invoke); break; case ROSE_ETSI_AOCDChargingUnit: aoc_etsi_aoc_d_charging_unit(ctrl, invoke); break; case ROSE_ETSI_AOCECurrency: aoc_etsi_aoc_e_currency(ctrl, call, invoke); break; case ROSE_ETSI_AOCEChargingUnit: aoc_etsi_aoc_e_charging_unit(ctrl, call, invoke); break; #if 0 /* Not handled yet */ case ROSE_ITU_IdentificationOfCharge: break; #endif /* Not handled yet */ case ROSE_ETSI_EctExecute: if (!ctrl->transfer_support) { send_facility_error(ctrl, call, invoke->invoke_id, ROSE_ERROR_Gen_NotSubscribed); break; } error_code = etsi_ect_execute_transfer(ctrl, call, invoke->invoke_id); if (error_code != ROSE_ERROR_None) { send_facility_error(ctrl, call, invoke->invoke_id, error_code); } break; case ROSE_ETSI_ExplicitEctExecute: error_code = etsi_explicit_ect_execute_transfer(ctrl, call, invoke->invoke_id, invoke->args.etsi.ExplicitEctExecute.link_id); if (error_code != ROSE_ERROR_None) { send_facility_error(ctrl, call, invoke->invoke_id, error_code); } break; case ROSE_ETSI_RequestSubaddress: etsi_request_subaddress(ctrl, call); break; case ROSE_ETSI_SubaddressTransfer: handle_subaddress_transfer(ctrl, call, &invoke->args.etsi.SubaddressTransfer.subaddress); break; case ROSE_ETSI_EctLinkIdRequest: if (!ctrl->transfer_support) { send_facility_error(ctrl, call, invoke->invoke_id, ROSE_ERROR_Gen_ResourceUnavailable); break; } /* * Use the invoke_id sequence number as a link_id. * It should be safe enough to do this. If not then we will have to search * the call pool to ensure that the link_id is not already in use. */ call->master_call->link_id = get_invokeid(ctrl); call->master_call->is_link_id_valid = 1; send_ect_link_id_rsp(ctrl, call, invoke->invoke_id); break; case ROSE_ETSI_EctInform: if (ctrl->display_flags.receive & PRI_DISPLAY_OPTION_NAME_UPDATE) { q931_display_name_get(call, &call->remote_id.name); } /* redirectionNumber is put in remote_id.number */ if (invoke->args.etsi.EctInform.redirection_present) { rose_copy_presented_number_unscreened_to_q931(ctrl, &call->remote_id.number, &invoke->args.etsi.EctInform.redirection); } /* * Clear the subaddress as the remote party has been changed. * Any new subaddress will arrive later. */ q931_party_subaddress_init(&call->remote_id.subaddress); if (!invoke->args.etsi.EctInform.status) { /* The remote party for the transfer has not answered yet. */ call->incoming_ct_state = INCOMING_CT_STATE_EXPECT_CT_ACTIVE; } else { call->incoming_ct_state = INCOMING_CT_STATE_POST_CONNECTED_LINE; } /* Send our subaddress back if we have one. */ if (call->local_id.subaddress.valid) { send_subaddress_transfer(ctrl, call); } break; case ROSE_ETSI_EctLoopTest: /* * The ETS 300 369 specification does a very poor job describing * how this message is used to detect loops. */ send_facility_error(ctrl, call, invoke->invoke_id, ROSE_ERROR_Gen_NotAvailable); break; #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 (!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 = ctrl->link.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 (!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_ETSI_MCIDRequest: if (q931_is_dummy_call(call)) { /* Don't even dignify this with a response. */ break; } if (!ctrl->mcid_support) { send_facility_error(ctrl, call, invoke->invoke_id, ROSE_ERROR_Gen_NotSubscribed); break; } if (!call->cc.originated) { send_facility_error(ctrl, call, invoke->invoke_id, ROSE_ERROR_Gen_NotIncomingCall); break; } switch (call->ourcallstate) { case Q931_CALL_STATE_ACTIVE: case Q931_CALL_STATE_DISCONNECT_INDICATION: case Q931_CALL_STATE_DISCONNECT_REQUEST:/* XXX We are really in the wrong state for this mode. */ subcmd = q931_alloc_subcommand(ctrl); if (!subcmd) { send_facility_error(ctrl, call, invoke->invoke_id, ROSE_ERROR_Gen_NotAvailable); break; } subcmd->cmd = PRI_SUBCMD_MCID_REQ; q931_party_id_copy_to_pri(&subcmd->u.mcid_req.originator, &call->local_id); q931_party_id_copy_to_pri(&subcmd->u.mcid_req.answerer, &call->remote_id); send_facility_result_ok(ctrl, call, invoke->invoke_id); break; default: send_facility_error(ctrl, call, invoke->invoke_id, ROSE_ERROR_Gen_InvalidCallState); break; } break; #if 0 /* Not handled yet */ case ROSE_ETSI_MWIActivate: break; case ROSE_ETSI_MWIDeactivate: break; case ROSE_ETSI_MWIIndicate: break; #endif /* Not handled yet */ case ROSE_QSIG_CallingName: if (ctrl->display_flags.receive & PRI_DISPLAY_OPTION_NAME_UPDATE) { q931_display_name_get(call, &call->remote_id.name); } /* CallingName is put in remote_id.name */ rose_copy_name_to_q931(ctrl, &call->remote_id.name, &invoke->args.qsig.CallingName.name); switch (msgtype) { case Q931_SETUP: case Q931_CONNECT: /* The caller name will automatically be reported. */ break; default: /* Setup connected line subcommand */ subcmd = q931_alloc_subcommand(ctrl); if (!subcmd) { break; } subcmd->cmd = PRI_SUBCMD_CONNECTED_LINE; q931_party_id_copy_to_pri(&subcmd->u.connected_line.id, &call->remote_id); break; } break; case ROSE_QSIG_CalledName: if (ctrl->display_flags.receive & PRI_DISPLAY_OPTION_NAME_UPDATE) { q931_display_name_get(call, &call->remote_id.name); } /* CalledName is put in remote_id.name */ rose_copy_name_to_q931(ctrl, &call->remote_id.name, &invoke->args.qsig.CalledName.name); switch (msgtype) { case Q931_SETUP: case Q931_CONNECT: /* The called name will automatically be reported. */ break; default: /* Setup connected line subcommand */ subcmd = q931_alloc_subcommand(ctrl); if (!subcmd) { break; } subcmd->cmd = PRI_SUBCMD_CONNECTED_LINE; q931_party_id_copy_to_pri(&subcmd->u.connected_line.id, &call->remote_id); break; } break; case ROSE_QSIG_ConnectedName: if (ctrl->display_flags.receive & PRI_DISPLAY_OPTION_NAME_UPDATE) { q931_display_name_get(call, &call->remote_id.name); } /* ConnectedName is put in remote_id.name */ rose_copy_name_to_q931(ctrl, &call->remote_id.name, &invoke->args.qsig.ConnectedName.name); switch (msgtype) { case Q931_SETUP: case Q931_CONNECT: /* The connected line name will automatically be reported. */ break; default: /* Setup connected line subcommand */ subcmd = q931_alloc_subcommand(ctrl); if (!subcmd) { break; } subcmd->cmd = PRI_SUBCMD_CONNECTED_LINE; q931_party_id_copy_to_pri(&subcmd->u.connected_line.id, &call->remote_id); break; } break; #if 0 /* Not handled yet */ case ROSE_QSIG_BusyName: break; #endif /* Not handled yet */ #if 0 /* Not handled yet */ case ROSE_QSIG_ChargeRequest: break; case ROSE_QSIG_GetFinalCharge: break; case ROSE_QSIG_AocFinal: break; case ROSE_QSIG_AocInterim: break; case ROSE_QSIG_AocRate: break; case ROSE_QSIG_AocComplete: break; case ROSE_QSIG_AocDivChargeReq: break; #endif /* Not handled yet */ #if 0 /* Not handled yet */ case ROSE_QSIG_CallTransferIdentify: break; case ROSE_QSIG_CallTransferAbandon: break; case ROSE_QSIG_CallTransferInitiate: break; case ROSE_QSIG_CallTransferSetup: break; #endif /* Not handled yet */ case ROSE_QSIG_CallTransferActive: if (ctrl->display_flags.receive & PRI_DISPLAY_OPTION_NAME_UPDATE) { q931_display_name_get(call, &call->remote_id.name); } call->incoming_ct_state = INCOMING_CT_STATE_POST_CONNECTED_LINE; /* connectedAddress is put in 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 */ if (invoke->args.qsig.CallTransferActive.connected_name_present) { rose_copy_name_to_q931(ctrl, &call->remote_id.name, &invoke->args.qsig.CallTransferActive.connected_name); } break; case ROSE_QSIG_CallTransferComplete: if (ctrl->display_flags.receive & PRI_DISPLAY_OPTION_NAME_UPDATE) { q931_display_name_get(call, &call->remote_id.name); } /* redirectionNumber is put in remote_id.number */ rose_copy_presented_number_screened_to_q931(ctrl, &call->remote_id.number, &invoke->args.qsig.CallTransferComplete.redirection); /* redirectionName is put in remote_id.name */ if (invoke->args.qsig.CallTransferComplete.redirection_name_present) { rose_copy_name_to_q931(ctrl, &call->remote_id.name, &invoke->args.qsig.CallTransferComplete.redirection_name); } /* * Clear the subaddress as the remote party has been changed. * Any new subaddress will arrive later. */ q931_party_subaddress_init(&call->remote_id.subaddress); if (invoke->args.qsig.CallTransferComplete.call_status == 1) { /* The remote party for the transfer has not answered yet. */ call->incoming_ct_state = INCOMING_CT_STATE_EXPECT_CT_ACTIVE; } else { call->incoming_ct_state = INCOMING_CT_STATE_POST_CONNECTED_LINE; } /* Send our subaddress back if we have one. */ if (call->local_id.subaddress.valid) { send_subaddress_transfer(ctrl, call); } break; case ROSE_QSIG_CallTransferUpdate: party_id = call->remote_id; if (ctrl->display_flags.receive & PRI_DISPLAY_OPTION_NAME_UPDATE) { q931_display_name_get(call, &party_id.name); } /* redirectionNumber is put in party_id.number */ rose_copy_presented_number_screened_to_q931(ctrl, &party_id.number, &invoke->args.qsig.CallTransferUpdate.redirection); /* redirectionName is put in party_id.name */ if (invoke->args.qsig.CallTransferUpdate.redirection_name_present) { rose_copy_name_to_q931(ctrl, &party_id.name, &invoke->args.qsig.CallTransferUpdate.redirection_name); } if (q931_party_id_cmp(&party_id, &call->remote_id)) { /* The remote_id data has changed. */ call->remote_id = party_id; switch (call->incoming_ct_state) { case INCOMING_CT_STATE_IDLE: call->incoming_ct_state = INCOMING_CT_STATE_POST_CONNECTED_LINE; break; default: break; } } break; case ROSE_QSIG_SubaddressTransfer: handle_subaddress_transfer(ctrl, call, &invoke->args.qsig.SubaddressTransfer.redirection_subaddress); break; case ROSE_QSIG_PathReplacement: anfpr_pathreplacement_respond(ctrl, call, ie); break; #if 0 /* Not handled yet */ case ROSE_QSIG_ActivateDiversionQ: break; case ROSE_QSIG_DeactivateDiversionQ: break; case ROSE_QSIG_InterrogateDiversionQ: break; case ROSE_QSIG_CheckRestriction: break; #endif /* Not handled yet */ case ROSE_QSIG_CallRerouting: if (!ctrl->deflection_support) { send_facility_error(ctrl, call, invoke->invoke_id, ROSE_ERROR_Gen_NotSubscribed); break; } subcmd = q931_alloc_subcommand(ctrl); if (!subcmd) { send_facility_error(ctrl, call, invoke->invoke_id, ROSE_ERROR_Gen_ResourceUnavailable); break; } q931_party_redirecting_init(&deflection); /* Rerouting from the last address. */ rose_copy_presented_number_unscreened_to_q931(ctrl, &deflection.from.number, &invoke->args.qsig.CallRerouting.last_rerouting); if (invoke->args.qsig.CallRerouting.redirecting_name_present) { rose_copy_name_to_q931(ctrl, &deflection.from.name, &invoke->args.qsig.CallRerouting.redirecting_name); } /* Rerouting to the new address. */ rose_copy_address_to_id_q931(ctrl, &deflection.to, &invoke->args.qsig.CallRerouting.called); switch (invoke->args.qsig.CallRerouting.subscription_option) { default: case 0: /* noNotification */ case 1: /* notificationWithoutDivertedToNr */ deflection.to.number.presentation = PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; break; case 2: /* notificationWithDivertedToNr */ deflection.to.number.presentation = PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; break; } /* Calling party update. */ party_id = call->local_id; rose_copy_presented_number_screened_to_q931(ctrl, &party_id.number, &invoke->args.qsig.CallRerouting.calling); if (invoke->args.qsig.CallRerouting.calling_name_present) { rose_copy_name_to_q931(ctrl, &party_id.name, &invoke->args.qsig.CallRerouting.calling_name); } deflection.count = invoke->args.qsig.CallRerouting.diversion_counter; deflection.reason = redirectingreason_for_q931(ctrl, invoke->args.qsig.CallRerouting.rerouting_reason); /* Original called party update. */ if (deflection.count == 1) { deflection.orig_called = deflection.from; deflection.orig_reason = deflection.reason; } else { deflection.orig_called = call->redirecting.orig_called; deflection.orig_reason = call->redirecting.orig_reason; } if (invoke->args.qsig.CallRerouting.original_called_present) { rose_copy_presented_number_unscreened_to_q931(ctrl, &deflection.orig_called.number, &invoke->args.qsig.CallRerouting.original_called); } if (invoke->args.qsig.CallRerouting.original_called_name_present) { rose_copy_name_to_q931(ctrl, &deflection.orig_called.name, &invoke->args.qsig.CallRerouting.original_called_name); } if (invoke->args.qsig.CallRerouting.original_rerouting_reason_present) { deflection.orig_reason = redirectingreason_for_q931(ctrl, invoke->args.qsig.CallRerouting.original_rerouting_reason); } subcmd->cmd = PRI_SUBCMD_REROUTING; subcmd->u.rerouting.invoke_id = invoke->invoke_id; subcmd->u.rerouting.subscription_option = invoke->args.qsig.CallRerouting.subscription_option; q931_party_id_copy_to_pri(&subcmd->u.rerouting.caller, &party_id); q931_party_redirecting_copy_to_pri(&subcmd->u.rerouting.deflection, &deflection); break; case ROSE_QSIG_DivertingLegInformation1: q931_party_number_init(&party_id.number); rose_copy_number_to_q931(ctrl, &party_id.number, &invoke->args.qsig.DivertingLegInformation1.nominated_number); if (party_id.number.str[0]) { party_id.number.presentation = PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; } /* * Unless otherwise indicated by CONNECT, the nominatedNr will be * the remote_id.number. */ if (!call->connected_number_in_message) { call->remote_id.number = party_id.number; } /* nominatedNr is put in redirecting.to.number */ switch (invoke->args.qsig.DivertingLegInformation1.subscription_option) { default: case QSIG_NO_NOTIFICATION: case QSIG_NOTIFICATION_WITHOUT_DIVERTED_TO_NR: q931_party_number_init(&call->redirecting.to.number); call->redirecting.to.number.valid = 1; call->redirecting.to.number.presentation = PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; break; case QSIG_NOTIFICATION_WITH_DIVERTED_TO_NR: call->redirecting.to.number = party_id.number; break; } call->redirecting.reason = redirectingreason_for_q931(ctrl, invoke->args.qsig.DivertingLegInformation1.diversion_reason); if (call->redirecting.count < PRI_MAX_REDIRECTS) { ++call->redirecting.count; } call->redirecting.state = Q931_REDIRECTING_STATE_EXPECTING_RX_DIV_LEG_3; break; case ROSE_QSIG_DivertingLegInformation2: call->redirecting.state = Q931_REDIRECTING_STATE_PENDING_TX_DIV_LEG_3; call->redirecting.count = invoke->args.qsig.DivertingLegInformation2.diversion_counter; if (!call->redirecting.count) { /* To be safe, make sure that the count is non-zero. */ call->redirecting.count = 1; } call->redirecting.reason = redirectingreason_for_q931(ctrl, invoke->args.qsig.DivertingLegInformation2.diversion_reason); /* divertingNr is put in redirecting.from.number */ if (invoke->args.qsig.DivertingLegInformation2.diverting_present) { rose_copy_presented_number_unscreened_to_q931(ctrl, &call->redirecting.from.number, &invoke->args.qsig.DivertingLegInformation2.diverting); } else if (!call->redirecting_number_in_message) { q931_party_number_init(&call->redirecting.from.number); call->redirecting.from.number.valid = 1; } /* redirectingName is put in redirecting.from.name */ if (invoke->args.qsig.DivertingLegInformation2.redirecting_name_present) { rose_copy_name_to_q931(ctrl, &call->redirecting.from.name, &invoke->args.qsig.DivertingLegInformation2.redirecting_name); } else { q931_party_name_init(&call->redirecting.from.name); } call->redirecting.orig_reason = PRI_REDIR_UNKNOWN; if (invoke->args.qsig.DivertingLegInformation2.original_diversion_reason_present) { call->redirecting.orig_reason = redirectingreason_for_q931(ctrl, invoke->args.qsig.DivertingLegInformation2.original_diversion_reason); } /* originalCalledNr is put in redirecting.orig_called.number */ if (invoke->args.qsig.DivertingLegInformation2.original_called_present) { rose_copy_presented_number_unscreened_to_q931(ctrl, &call->redirecting.orig_called.number, &invoke->args.qsig.DivertingLegInformation2.original_called); } else { q931_party_number_init(&call->redirecting.orig_called.number); } /* originalCalledName is put in redirecting.orig_called.name */ if (invoke->args.qsig.DivertingLegInformation2.original_called_name_present) { rose_copy_name_to_q931(ctrl, &call->redirecting.orig_called.name, &invoke->args.qsig.DivertingLegInformation2.original_called_name); } else { q931_party_name_init(&call->redirecting.orig_called.name); } break; case ROSE_QSIG_DivertingLegInformation3: /* * Unless otherwise indicated by CONNECT, this will be the * remote_id.number.presentation. */ if (!invoke->args.qsig.DivertingLegInformation3.presentation_allowed_indicator) { call->redirecting.to.number.presentation = PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; if (!call->connected_number_in_message) { call->remote_id.number.presentation = PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; } } /* redirectionName is put in redirecting.to.name */ if (invoke->args.qsig.DivertingLegInformation3.redirection_name_present) { rose_copy_name_to_q931(ctrl, &call->redirecting.to.name, &invoke->args.qsig.DivertingLegInformation3.redirection_name); if (!invoke->args.qsig.DivertingLegInformation3.presentation_allowed_indicator) { call->redirecting.to.name.presentation = PRI_PRES_RESTRICTED; } } else { q931_party_name_init(&call->redirecting.to.name); } switch (call->redirecting.state) { case Q931_REDIRECTING_STATE_EXPECTING_RX_DIV_LEG_3: call->redirecting.state = Q931_REDIRECTING_STATE_IDLE; subcmd = q931_alloc_subcommand(ctrl); if (!subcmd) { break; } /* Setup redirecting subcommand */ subcmd->cmd = PRI_SUBCMD_REDIRECTING; q931_party_redirecting_copy_to_pri(&subcmd->u.redirecting, &call->redirecting); break; default: break; } break; #if 0 /* Not handled yet */ 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; case ROSE_QSIG_MWIDeactivate: break; case ROSE_QSIG_MWIInterrogate: break; #endif /* Not handled yet */ default: if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, "!! ROSE invoke operation not handled on switchtype:%s! %s\n", pri_switch2str(ctrl->switchtype), rose_operation2str(invoke->operation)); } break; } } libpri-1.4.14/.version0000644000000000000000000000000712064640374013251 0ustar rootroot1.4.14 libpri-1.4.14/q921.c0000644000000000000000000022453212005532137012426 0ustar rootroot/* * libpri: An implementation of Primary Rate ISDN * * Written by Mark Spencer * * Copyright (C) 2001-2005, Digium, Inc. * All Rights Reserved. */ /* * 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. */ #include #include #include #include #include #include #include "compat.h" #include "libpri.h" #include "pri_internal.h" #include "pri_q921.h" #include "pri_q931.h" /* * Define RANDOM_DROPS To randomly drop packets in order to simulate loss for testing * retransmission functionality */ //#define RANDOM_DROPS 1 #define Q921_INIT(link, hf) do { \ memset(&(hf),0,sizeof(hf)); \ (hf).h.sapi = (link)->sapi; \ (hf).h.ea1 = 0; \ (hf).h.ea2 = 1; \ (hf).h.tei = (link)->tei; \ } while (0) static void q921_dump_pri(struct q921_link *link, char direction_tag); static void q921_establish_data_link(struct q921_link *link); static void q921_mdl_error(struct q921_link *link, char error); static void q921_mdl_remove(struct q921_link *link); static void q921_mdl_destroy(struct q921_link *link); /*! * \internal * \brief Convert Q.921 TEI management message type to a string. * * \param message Q.921 TEI management message type to convert. * * \return TEI management message type name string */ static const char *q921_tei_mgmt2str(enum q921_tei_identity message) { switch (message) { case Q921_TEI_IDENTITY_REQUEST: return "TEI Identity Request"; case Q921_TEI_IDENTITY_ASSIGNED: return "TEI Identity Assigned"; case Q921_TEI_IDENTITY_CHECK_REQUEST: return "TEI Identity Check Request"; case Q921_TEI_IDENTITY_REMOVE: return "TEI Identity Remove"; case Q921_TEI_IDENTITY_DENIED: return "TEI Identity Denied"; case Q921_TEI_IDENTITY_CHECK_RESPONSE: return "TEI Identity Check Response"; case Q921_TEI_IDENTITY_VERIFY: return "TEI Identity Verify"; } return "Unknown"; } /*! * \internal * \brief Convert Q.921 state to a string. * * \param state Q.921 state to convert. * * \return State name string */ static const char *q921_state2str(enum q921_state state) { switch (state) { case Q921_TEI_UNASSIGNED: return "TEI unassigned"; case Q921_ASSIGN_AWAITING_TEI: return "Assign awaiting TEI"; case Q921_ESTABLISH_AWAITING_TEI: return "Establish awaiting TEI"; case Q921_TEI_ASSIGNED: return "TEI assigned"; case Q921_AWAITING_ESTABLISHMENT: return "Awaiting establishment"; case Q921_AWAITING_RELEASE: return "Awaiting release"; case Q921_MULTI_FRAME_ESTABLISHED: return "Multi-frame established"; case Q921_TIMER_RECOVERY: return "Timer recovery"; } return "Unknown state"; } static void q921_setstate(struct q921_link *link, int newstate) { struct pri *ctrl; ctrl = link->ctrl; if (ctrl->debug & PRI_DEBUG_Q921_STATE) { /* * Suppress displaying these state transitions: * Q921_MULTI_FRAME_ESTABLISHED <--> Q921_TIMER_RECOVERY * * Q921 keeps flipping back and forth between these two states * when it has nothing better to do. */ switch (link->state) { case Q921_MULTI_FRAME_ESTABLISHED: case Q921_TIMER_RECOVERY: switch (newstate) { case Q921_MULTI_FRAME_ESTABLISHED: case Q921_TIMER_RECOVERY: /* Suppress displaying this state transition. */ link->state = newstate; return; default: break; } break; default: break; } if (link->state != newstate) { pri_message(ctrl, "Changing from state %d(%s) to %d(%s)\n", link->state, q921_state2str(link->state), newstate, q921_state2str(newstate)); } } link->state = newstate; } static void q921_discard_iqueue(struct q921_link *link) { struct q921_frame *f, *p; f = link->tx_queue; while (f) { p = f; f = f->next; /* Free frame */ free(p); } link->tx_queue = NULL; } static int q921_transmit(struct pri *ctrl, q921_h *h, int len) { int res; #ifdef RANDOM_DROPS if (!(random() % 3)) { pri_message(ctrl, " === Dropping Packet ===\n"); return 0; } #endif ctrl->q921_txcount++; /* Just send it raw */ if (ctrl->debug & (PRI_DEBUG_Q921_DUMP | PRI_DEBUG_Q921_RAW)) q921_dump(ctrl, h, len, ctrl->debug, 1); /* Write an extra two bytes for the FCS */ res = ctrl->write_func ? ctrl->write_func(ctrl, h, len + 2) : 0; if (res != (len + 2)) { pri_error(ctrl, "Short write: %d/%d (%s)\n", res, len + 2, strerror(errno)); return -1; } return 0; } static void q921_send_tei(struct pri *ctrl, enum q921_tei_identity message, int ri, int ai, int iscommand) { q921_u *f; struct q921_link *link; link = &ctrl->link; if (!(f = calloc(1, sizeof(*f) + 5))) return; Q921_INIT(link, *f); f->h.c_r = (ctrl->localtype == PRI_NETWORK) ? iscommand : !iscommand; f->ft = Q921_FRAMETYPE_U; f->data[0] = 0x0f; /* Management entity */ f->data[1] = (ri >> 8) & 0xff; f->data[2] = ri & 0xff; f->data[3] = message; f->data[4] = (ai << 1) | 1; if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "Sending TEI management message %d(%s), TEI=%d\n", message, q921_tei_mgmt2str(message), ai); } q921_transmit(ctrl, (q921_h *)f, 8); free(f); } static void t202_expire(void *vlink) { struct q921_link *link = vlink; struct pri *ctrl; ctrl = link->ctrl; /* Start the TEI request timer. */ pri_schedule_del(ctrl, link->t202_timer); link->t202_timer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T202], t202_expire, link); if (ctrl->l2_persistence != PRI_L2_PERSISTENCE_KEEP_UP) { /* Only try to get a TEI for N202 times if layer 2 is not persistent. */ ++link->n202_counter; } if (!link->t202_timer || link->n202_counter > ctrl->timers[PRI_TIMER_N202]) { if (!link->t202_timer) { pri_error(ctrl, "Could not start T202 timer."); } else { pri_schedule_del(ctrl, link->t202_timer); link->t202_timer = 0; } pri_error(ctrl, "Unable to receive TEI from network in state %d(%s)!\n", link->state, q921_state2str(link->state)); switch (link->state) { case Q921_ASSIGN_AWAITING_TEI: break; case Q921_ESTABLISH_AWAITING_TEI: q921_discard_iqueue(link); /* DL-RELEASE indication */ q931_dl_event(link, Q931_DL_EVENT_DL_RELEASE_IND); break; default: break; } q921_setstate(link, Q921_TEI_UNASSIGNED); return; } /* Send TEI request */ link->ri = random() % 65535; q921_send_tei(ctrl, Q921_TEI_IDENTITY_REQUEST, link->ri, Q921_TEI_GROUP, 1); } static void q921_tei_request(struct q921_link *link) { link->n202_counter = 0; t202_expire(link); } static void q921_tei_remove(struct pri *ctrl, int tei) { /* * Q.921 Section 5.3.2 says we should send the remove message * twice, in case of message loss. */ q921_send_tei(ctrl, Q921_TEI_IDENTITY_REMOVE, 0, tei, 1); q921_send_tei(ctrl, Q921_TEI_IDENTITY_REMOVE, 0, tei, 1); } static void q921_send_dm(struct q921_link *link, int fbit) { q921_h h; struct pri *ctrl; ctrl = link->ctrl; Q921_INIT(link, h); h.u.m3 = 0; /* M3 = 0 */ h.u.m2 = 3; /* M2 = 3 */ h.u.p_f = fbit; /* Final set appropriately */ h.u.ft = Q921_FRAMETYPE_U; switch (ctrl->localtype) { case PRI_NETWORK: h.h.c_r = 0; break; case PRI_CPE: h.h.c_r = 1; break; default: pri_error(ctrl, "Don't know how to DM on a type %d node\n", ctrl->localtype); return; } if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "TEI=%d Sending DM\n", link->tei); } q921_transmit(ctrl, &h, 4); } static void q921_send_disc(struct q921_link *link, int pbit) { q921_h h; struct pri *ctrl; ctrl = link->ctrl; Q921_INIT(link, h); h.u.m3 = 2; /* M3 = 2 */ h.u.m2 = 0; /* M2 = 0 */ h.u.p_f = pbit; /* Poll set appropriately */ h.u.ft = Q921_FRAMETYPE_U; switch (ctrl->localtype) { case PRI_NETWORK: h.h.c_r = 0; break; case PRI_CPE: h.h.c_r = 1; break; default: pri_error(ctrl, "Don't know how to DISC on a type %d node\n", ctrl->localtype); return; } if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "TEI=%d Sending DISC\n", link->tei); } q921_transmit(ctrl, &h, 4); } static void q921_send_ua(struct q921_link *link, int fbit) { q921_h h; struct pri *ctrl; ctrl = link->ctrl; Q921_INIT(link, h); h.u.m3 = 3; /* M3 = 3 */ h.u.m2 = 0; /* M2 = 0 */ h.u.p_f = fbit; /* Final set appropriately */ h.u.ft = Q921_FRAMETYPE_U; switch (ctrl->localtype) { case PRI_NETWORK: h.h.c_r = 0; break; case PRI_CPE: h.h.c_r = 1; break; default: pri_error(ctrl, "Don't know how to UA on a type %d node\n", ctrl->localtype); return; } if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "TEI=%d Sending UA\n", link->tei); } q921_transmit(ctrl, &h, 3); } static void q921_send_sabme(struct q921_link *link) { q921_h h; struct pri *ctrl; ctrl = link->ctrl; Q921_INIT(link, h); h.u.m3 = 3; /* M3 = 3 */ h.u.m2 = 3; /* M2 = 3 */ h.u.p_f = 1; /* Poll bit set */ h.u.ft = Q921_FRAMETYPE_U; switch (ctrl->localtype) { case PRI_NETWORK: h.h.c_r = 1; break; case PRI_CPE: h.h.c_r = 0; break; default: pri_error(ctrl, "Don't know how to SABME on a type %d node\n", ctrl->localtype); return; } if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "TEI=%d Sending SABME\n", link->tei); } q921_transmit(ctrl, &h, 3); } static int q921_ack_packet(struct q921_link *link, int num) { struct q921_frame *f; struct q921_frame *prev; struct pri *ctrl; ctrl = link->ctrl; for (prev = NULL, f = link->tx_queue; f; prev = f, f = f->next) { if (f->status != Q921_TX_FRAME_SENT) { break; } if (f->h.n_s == num) { /* Cancel each packet as necessary */ /* That's our packet */ if (prev) prev->next = f->next; else link->tx_queue = f->next; if (ctrl->debug & PRI_DEBUG_Q921_DUMP) { pri_message(ctrl, "-- ACKing N(S)=%d, tx_queue head is N(S)=%d (-1 is empty, -2 is not transmitted)\n", f->h.n_s, link->tx_queue ? link->tx_queue->status == Q921_TX_FRAME_SENT ? link->tx_queue->h.n_s : -2 : -1); } /* Update v_a */ free(f); return 1; } } return 0; } static void t203_expire(void *vlink); static void t200_expire(void *vlink); #define restart_t200(link) reschedule_t200(link) static void reschedule_t200(struct q921_link *link) { struct pri *ctrl; ctrl = link->ctrl; if (ctrl->debug & PRI_DEBUG_Q921_DUMP) pri_message(ctrl, "-- Restarting T200 timer\n"); pri_schedule_del(ctrl, link->t200_timer); link->t200_timer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T200], t200_expire, link); } #if 0 static void reschedule_t203(struct q921_link *link) { struct pri *ctrl; ctrl = link->ctrl; if (ctrl->debug & PRI_DEBUG_Q921_DUMP) pri_message(ctrl, "-- Restarting T203 timer\n"); pri_schedule_del(ctrl, link->t203_timer); link->t203_timer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T203], t203_expire, link); } #endif static void start_t203(struct q921_link *link) { struct pri *ctrl; ctrl = link->ctrl; if (link->t203_timer) { if (ctrl->debug & PRI_DEBUG_Q921_DUMP) pri_message(ctrl, "T203 requested to start without stopping first\n"); pri_schedule_del(ctrl, link->t203_timer); } if (ctrl->debug & PRI_DEBUG_Q921_DUMP) pri_message(ctrl, "-- Starting T203 timer\n"); link->t203_timer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T203], t203_expire, link); } static void stop_t203(struct q921_link *link) { struct pri *ctrl; ctrl = link->ctrl; if (link->t203_timer) { if (ctrl->debug & PRI_DEBUG_Q921_DUMP) pri_message(ctrl, "-- Stopping T203 timer\n"); pri_schedule_del(ctrl, link->t203_timer); link->t203_timer = 0; } else { if (ctrl->debug & PRI_DEBUG_Q921_DUMP) pri_message(ctrl, "-- T203 requested to stop when not started\n"); } } static void start_t200(struct q921_link *link) { struct pri *ctrl; ctrl = link->ctrl; if (link->t200_timer) { if (ctrl->debug & PRI_DEBUG_Q921_DUMP) pri_message(ctrl, "T200 requested to start without stopping first\n"); pri_schedule_del(ctrl, link->t200_timer); } if (ctrl->debug & PRI_DEBUG_Q921_DUMP) pri_message(ctrl, "-- Starting T200 timer\n"); link->t200_timer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T200], t200_expire, link); } static void stop_t200(struct q921_link *link) { struct pri *ctrl; ctrl = link->ctrl; if (link->t200_timer) { if (ctrl->debug & PRI_DEBUG_Q921_DUMP) pri_message(ctrl, "-- Stopping T200 timer\n"); pri_schedule_del(ctrl, link->t200_timer); link->t200_timer = 0; } else { if (ctrl->debug & PRI_DEBUG_Q921_DUMP) pri_message(ctrl, "-- T200 requested to stop when not started\n"); } } /*! * \internal * \brief Initiate bringing up layer 2 link. * * \param link Layer 2 link to bring up. * * \return Nothing */ static void kick_start_link(struct q921_link *link) { struct pri *ctrl; ctrl = link->ctrl; switch (link->state) { case Q921_TEI_UNASSIGNED: if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "Kick starting link from no TEI.\n"); } q921_setstate(link, Q921_ESTABLISH_AWAITING_TEI); q921_tei_request(link); break; case Q921_ASSIGN_AWAITING_TEI: if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "Kick starting link when get TEI.\n"); } q921_setstate(link, Q921_ESTABLISH_AWAITING_TEI); break; case Q921_TEI_ASSIGNED: if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "SAPI/TEI=%d/%d Kick starting link\n", link->sapi, link->tei); } q921_discard_iqueue(link); q921_establish_data_link(link); link->l3_initiated = 1; q921_setstate(link, Q921_AWAITING_ESTABLISHMENT); break; default: break; } } static void restart_timer_expire(void *vlink) { struct q921_link *link = vlink; struct pri *ctrl; ctrl = link->ctrl; link->restart_timer = 0; switch (link->state) { case Q921_TEI_ASSIGNED: /* Try to bring layer 2 up. */ kick_start_link(link); break; default: /* Looks like someone forgot to stop the restart timer. */ pri_error(ctrl, "SAPI/TEI=%d/%d Link restart delay timer expired in state %d(%s)\n", link->sapi, link->tei, link->state, q921_state2str(link->state)); break; } } static void restart_timer_stop(struct q921_link *link) { struct pri *ctrl; ctrl = link->ctrl; pri_schedule_del(ctrl, link->restart_timer); link->restart_timer = 0; } /*! \note Only call on the transition to state Q921_TEI_ASSIGNED or already there. */ static void restart_timer_start(struct q921_link *link) { struct pri *ctrl; ctrl = link->ctrl; if (ctrl->debug & PRI_DEBUG_Q921_DUMP) { pri_message(ctrl, "SAPI/TEI=%d/%d Starting link restart delay timer\n", link->sapi, link->tei); } pri_schedule_del(ctrl, link->restart_timer); link->restart_timer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T200], restart_timer_expire, link); } /*! \note Only call on the transition to state Q921_TEI_ASSIGNED or already there. */ static pri_event *q921_check_delay_restart(struct q921_link *link) { pri_event *ev; struct pri *ctrl; ctrl = link->ctrl; if (ctrl->l2_persistence == PRI_L2_PERSISTENCE_KEEP_UP) { /* * For PTP links: * This is where we act a bit like L3 instead of L2, since we've * got an L3 that depends on us keeping L2 automatically alive * and happy. * * For PTMP links: * We can optionally keep L2 automatically alive and happy. */ restart_timer_start(link); } if (PTP_MODE(ctrl)) { switch (link->state) { case Q921_MULTI_FRAME_ESTABLISHED: case Q921_TIMER_RECOVERY: /* Notify the upper layer that layer 2 went down. */ ctrl->schedev = 1; ctrl->ev.gen.e = PRI_EVENT_DCHAN_DOWN; ev = &ctrl->ev; break; default: ev = NULL; break; } } else { ev = NULL; } return ev; } /*! * \brief Bring all layer 2 links up. * * \param ctrl D channel controller. * * \return Nothing */ void q921_bring_layer2_up(struct pri *ctrl) { struct q921_link *link; if (PTMP_MODE(ctrl)) { /* Don't start with the broadcast link. */ link = ctrl->link.next; } else { link = &ctrl->link; } for (; link; link = link->next) { if (!link->restart_timer) { /* A restart on the link is not already in the works. */ kick_start_link(link); } } } /* This is the equivalent of the I-Frame queued up path in Figure B.7 in MULTI_FRAME_ESTABLISHED */ static int q921_send_queued_iframes(struct q921_link *link) { struct pri *ctrl; struct q921_frame *f; int frames_txd = 0; ctrl = link->ctrl; for (f = link->tx_queue; f; f = f->next) { if (f->status != Q921_TX_FRAME_SENT) { /* This frame needs to be sent. */ break; } } if (!f) { /* The Tx queue has no pending frames. */ return 0; } if (link->peer_rx_busy) { /* Don't flood debug trace if not really looking at Q.921 layer. */ if (ctrl->debug & (/* PRI_DEBUG_Q921_STATE | */ PRI_DEBUG_Q921_DUMP)) { pri_message(ctrl, "TEI=%d Couldn't transmit I-frame at this time due to peer busy condition\n", link->tei); } return 0; } if (link->v_s == Q921_ADD(link->v_a, ctrl->timers[PRI_TIMER_K])) { /* Don't flood debug trace if not really looking at Q.921 layer. */ if (ctrl->debug & (/* PRI_DEBUG_Q921_STATE | */ PRI_DEBUG_Q921_DUMP)) { pri_message(ctrl, "TEI=%d Couldn't transmit I-frame at this time due to window shut\n", link->tei); } return 0; } /* Send all pending frames that fit in the window. */ for (; f; f = f->next) { if (link->v_s == Q921_ADD(link->v_a, ctrl->timers[PRI_TIMER_K])) { /* The window is no longer open. */ break; } /* Send it now... */ switch (f->status) { case Q921_TX_FRAME_NEVER_SENT: if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "TEI=%d Transmitting N(S)=%d, window is open V(A)=%d K=%d\n", link->tei, link->v_s, link->v_a, ctrl->timers[PRI_TIMER_K]); } break; case Q921_TX_FRAME_PUSHED_BACK: if (f->h.n_s != link->v_s) { /* Should never happen. */ pri_error(ctrl, "TEI=%d Retransmitting frame with old N(S)=%d as N(S)=%d!\n", link->tei, f->h.n_s, link->v_s); } else if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "TEI=%d Retransmitting frame N(S)=%d now!\n", link->tei, link->v_s); } break; default: /* Should never happen. */ pri_error(ctrl, "Unexpected Tx Q frame status: %d", f->status); break; } /* * Send the frame out on the assigned TEI. * Done now because the frame may have been queued before we * had an assigned TEI. */ f->h.h.tei = link->tei; f->h.n_s = link->v_s; f->h.n_r = link->v_r; f->h.ft = 0; f->h.p_f = 0; q921_transmit(ctrl, (q921_h *) (&f->h), f->len); Q921_INC(link->v_s); ++frames_txd; if ((ctrl->debug & PRI_DEBUG_Q931_DUMP) && f->status == Q921_TX_FRAME_NEVER_SENT) { /* * The transmit operation might dump the Q.921 header, so logging * the Q.931 message body after the transmit puts the sections of * the message in the right order in the log. * * Also dump the Q.931 part only once instead of for every * retransmission. */ q931_dump(ctrl, link->tei, (q931_h *) f->h.data, f->len - 4, 1); } f->status = Q921_TX_FRAME_SENT; } if (frames_txd) { link->acknowledge_pending = 0; if (!link->t200_timer) { stop_t203(link); start_t200(link); } } return frames_txd; } static void q921_reject(struct q921_link *link, int pf) { q921_h h; struct pri *ctrl; ctrl = link->ctrl; Q921_INIT(link, h); h.s.x0 = 0; /* Always 0 */ h.s.ss = 2; /* Reject */ h.s.ft = 1; /* Frametype (01) */ h.s.n_r = link->v_r; /* Where to start retransmission N(R) */ h.s.p_f = pf; switch (ctrl->localtype) { case PRI_NETWORK: h.h.c_r = 0; break; case PRI_CPE: h.h.c_r = 1; break; default: pri_error(ctrl, "Don't know how to REJ on a type %d node\n", ctrl->localtype); return; } if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "TEI=%d Sending REJ N(R)=%d\n", link->tei, link->v_r); } q921_transmit(ctrl, &h, 4); } static void q921_rr(struct q921_link *link, int pbit, int cmd) { q921_h h; struct pri *ctrl; ctrl = link->ctrl; Q921_INIT(link, h); h.s.x0 = 0; /* Always 0 */ h.s.ss = 0; /* Receive Ready */ h.s.ft = 1; /* Frametype (01) */ h.s.n_r = link->v_r; /* N(R) */ h.s.p_f = pbit; /* Poll/Final set appropriately */ switch (ctrl->localtype) { case PRI_NETWORK: if (cmd) h.h.c_r = 1; else h.h.c_r = 0; break; case PRI_CPE: if (cmd) h.h.c_r = 0; else h.h.c_r = 1; break; default: pri_error(ctrl, "Don't know how to RR on a type %d node\n", ctrl->localtype); return; } #if 0 /* Don't flood debug trace with RR if not really looking at Q.921 layer. */ if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "TEI=%d Sending RR N(R)=%d\n", link->tei, link->v_r); } #endif q921_transmit(ctrl, &h, 4); } static void transmit_enquiry(struct q921_link *link) { if (!link->own_rx_busy) { q921_rr(link, 1, 1); link->acknowledge_pending = 0; start_t200(link); } else { /* XXX: Implement me... */ } } static void t200_expire(void *vlink) { struct q921_link *link = vlink; struct pri *ctrl; ctrl = link->ctrl; if (ctrl->debug & PRI_DEBUG_Q921_DUMP) { pri_message(ctrl, "%s\n", __FUNCTION__); q921_dump_pri(link, ' '); } link->t200_timer = 0; switch (link->state) { case Q921_MULTI_FRAME_ESTABLISHED: link->RC = 0; transmit_enquiry(link); link->RC++; q921_setstate(link, Q921_TIMER_RECOVERY); break; case Q921_TIMER_RECOVERY: /* SDL Flow Figure B.8/Q.921 Page 81 */ if (link->RC != ctrl->timers[PRI_TIMER_N200]) { #if 0 if (link->v_s == link->v_a) { transmit_enquiry(link); } #else /* We are chosing to enquiry by default (to reduce risk of T200 timer errors at the other * side, instead of retransmission of the last I-frame we sent */ transmit_enquiry(link); #endif link->RC++; } else { q921_mdl_error(link, 'I'); q921_establish_data_link(link); link->l3_initiated = 0; q921_setstate(link, Q921_AWAITING_ESTABLISHMENT); if (PTP_MODE(ctrl)) { ctrl->schedev = 1; ctrl->ev.gen.e = PRI_EVENT_DCHAN_DOWN; } } break; case Q921_AWAITING_ESTABLISHMENT: if (link->RC != ctrl->timers[PRI_TIMER_N200]) { link->RC++; q921_send_sabme(link); start_t200(link); } else { q921_check_delay_restart(link); q921_discard_iqueue(link); q921_mdl_error(link, 'G'); q921_setstate(link, Q921_TEI_ASSIGNED); /* DL-RELEASE indication */ q931_dl_event(link, Q931_DL_EVENT_DL_RELEASE_IND); } break; case Q921_AWAITING_RELEASE: if (link->RC != ctrl->timers[PRI_TIMER_N200]) { ++link->RC; q921_send_disc(link, 1); start_t200(link); } else { q921_check_delay_restart(link); q921_mdl_error(link, 'H'); /* DL-RELEASE confirm */ q931_dl_event(link, Q931_DL_EVENT_DL_RELEASE_CONFIRM); q921_setstate(link, Q921_TEI_ASSIGNED); } break; default: /* Looks like someone forgot to stop the T200 timer. */ pri_error(ctrl, "T200 expired in state %d(%s)\n", link->state, q921_state2str(link->state)); break; } } /* This is sending a DL-UNIT-DATA request */ int q921_transmit_uiframe(struct q921_link *link, void *buf, int len) { uint8_t ubuf[512]; q921_h *h = (void *)&ubuf[0]; struct pri *ctrl; ctrl = link->ctrl; if (len >= 512) { pri_error(ctrl, "Requested to send UI-frame larger than 512 bytes!\n"); return -1; } memset(ubuf, 0, sizeof(ubuf)); h->h.sapi = 0; h->h.ea1 = 0; h->h.ea2 = 1; h->h.tei = link->tei; h->u.m3 = 0; h->u.m2 = 0; h->u.p_f = 0; /* Poll bit set */ h->u.ft = Q921_FRAMETYPE_U; switch (ctrl->localtype) { case PRI_NETWORK: h->h.c_r = 1; break; case PRI_CPE: h->h.c_r = 0; break; default: pri_error(ctrl, "Don't know how to UI-frame on a type %d node\n", ctrl->localtype); return -1; } memcpy(h->u.data, buf, len); q921_transmit(ctrl, h, len + 3); return 0; } static struct q921_link *pri_find_tei(struct pri *ctrl, int sapi, int tei) { struct q921_link *link; for (link = &ctrl->link; link; link = link->next) { if (link->tei == tei && link->sapi == sapi) return link; } return NULL; } /* This is the equivalent of a DL-DATA request, as well as the I-frame queued up outcome */ int q921_transmit_iframe(struct q921_link *link, void *buf, int len, int cr) { struct q921_frame *f, *prev=NULL; struct pri *ctrl; ctrl = link->ctrl; if (PTMP_MODE(ctrl)) { if (link->tei == Q921_TEI_GROUP) { pri_error(ctrl, "Huh?! For PTMP, we shouldn't be sending I-frames out the group TEI\n"); return 0; } if (BRI_TE_PTMP(ctrl)) { switch (link->state) { case Q921_TEI_UNASSIGNED: q921_setstate(link, Q921_ESTABLISH_AWAITING_TEI); q921_tei_request(link); break; case Q921_ASSIGN_AWAITING_TEI: q921_setstate(link, Q921_ESTABLISH_AWAITING_TEI); break; default: break; } } } else { /* PTP modes, which shouldn't have subs */ } /* Figure B.7/Q.921 Page 70 */ switch (link->state) { case Q921_TEI_ASSIGNED: /* If we aren't in a state compatiable with DL-DATA requests, start getting us there here */ restart_timer_stop(link); q921_establish_data_link(link); link->l3_initiated = 1; q921_setstate(link, Q921_AWAITING_ESTABLISHMENT); /* For all rest, we've done the work to get us up prior to this and fall through */ case Q921_ESTABLISH_AWAITING_TEI: case Q921_TIMER_RECOVERY: case Q921_AWAITING_ESTABLISHMENT: case Q921_MULTI_FRAME_ESTABLISHED: /* Find queue tail. */ for (f = link->tx_queue; f; f = f->next) { prev = f; } f = calloc(1, sizeof(struct q921_frame) + len + 2); if (f) { Q921_INIT(link, f->h); switch (ctrl->localtype) { case PRI_NETWORK: if (cr) f->h.h.c_r = 1; else f->h.h.c_r = 0; break; case PRI_CPE: if (cr) f->h.h.c_r = 0; else f->h.h.c_r = 1; break; } /* Put new frame on queue tail. */ f->next = NULL; f->status = Q921_TX_FRAME_NEVER_SENT; f->len = len + 4; memcpy(f->h.data, buf, len); if (prev) prev->next = f; else link->tx_queue = f; if (link->state != Q921_MULTI_FRAME_ESTABLISHED) { if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "TEI=%d Just queued I-frame since in state %d(%s)\n", link->tei, link->state, q921_state2str(link->state)); } break; } if (link->peer_rx_busy) { if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "TEI=%d Just queued I-frame due to peer busy condition\n", link->tei); } break; } if (!q921_send_queued_iframes(link)) { /* * No frames sent even though we just put a frame on the queue. * * Special debug message/test here because we want to say what * happened to the Q.931 message just queued but we don't want * to flood the debug trace if we are not really looking at the * Q.921 layer. */ if ((ctrl->debug & (PRI_DEBUG_Q921_STATE | PRI_DEBUG_Q921_DUMP)) == PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "TEI=%d Just queued I-frame due to window shut\n", link->tei); } } } else { pri_error(ctrl, "!! Out of memory for Q.921 transmit\n"); return -1; } break; case Q921_TEI_UNASSIGNED: case Q921_ASSIGN_AWAITING_TEI: case Q921_AWAITING_RELEASE: default: pri_error(ctrl, "Cannot transmit frames in state %d(%s)\n", link->state, q921_state2str(link->state)); break; } return 0; } static void t203_expire(void *vlink) { struct q921_link *link = vlink; struct pri *ctrl; ctrl = link->ctrl; if (ctrl->debug & PRI_DEBUG_Q921_DUMP) pri_message(ctrl, "%s\n", __FUNCTION__); link->t203_timer = 0; switch (link->state) { case Q921_MULTI_FRAME_ESTABLISHED: transmit_enquiry(link); link->RC = 0; q921_setstate(link, Q921_TIMER_RECOVERY); break; default: /* Looks like someone forgot to stop the T203 timer. */ pri_error(ctrl, "T203 expired in state %d(%s)\n", link->state, q921_state2str(link->state)); break; } } static void q921_dump_iqueue_info(struct q921_link *link) { struct pri *ctrl; struct q921_frame *f; int pending = 0; int unacked = 0; ctrl = link->ctrl; for (f = link->tx_queue; f; f = f->next) { if (f->status == Q921_TX_FRAME_SENT) { unacked++; } else { pending++; } } pri_error(ctrl, "Number of pending packets %d, sent but unacked %d\n", pending, unacked); } static void q921_dump_pri_by_h(struct pri *ctrl, char direction_tag, q921_h *h); void q921_dump(struct pri *ctrl, q921_h *h, int len, int debugflags, int txrx) { int x; const char *type; char direction_tag; direction_tag = txrx ? '>' : '<'; pri_message(ctrl, "\n"); if (debugflags & PRI_DEBUG_Q921_DUMP) { q921_dump_pri_by_h(ctrl, direction_tag, h); } if (debugflags & PRI_DEBUG_Q921_RAW) { char *buf = malloc(len * 3 + 1); int buflen = 0; if (buf) { for (x=0;xraw[x]); pri_message(ctrl, "%c [ %s]\n", direction_tag, buf); free(buf); } } if (debugflags & PRI_DEBUG_Q921_DUMP) { switch (h->h.data[0] & Q921_FRAMETYPE_MASK) { case 0: case 2: pri_message(ctrl, "%c Informational frame:\n", direction_tag); break; case 1: pri_message(ctrl, "%c Supervisory frame:\n", direction_tag); break; case 3: pri_message(ctrl, "%c Unnumbered frame:\n", direction_tag); break; } pri_message(ctrl, "%c SAPI: %02d C/R: %d EA: %d\n", direction_tag, h->h.sapi, h->h.c_r, h->h.ea1); pri_message(ctrl, "%c TEI: %03d EA: %d\n", direction_tag, h->h.tei, h->h.ea2); switch (h->h.data[0] & Q921_FRAMETYPE_MASK) { case 0: case 2: /* Informational frame */ pri_message(ctrl, "%c N(S): %03d 0: %d\n", direction_tag, h->i.n_s, h->i.ft); pri_message(ctrl, "%c N(R): %03d P: %d\n", direction_tag, h->i.n_r, h->i.p_f); pri_message(ctrl, "%c %d bytes of data\n", direction_tag, len - 4); break; case 1: /* Supervisory frame */ type = "???"; switch (h->s.ss) { case 0: type = "RR (receive ready)"; break; case 1: type = "RNR (receive not ready)"; break; case 2: type = "REJ (reject)"; break; } pri_message(ctrl, "%c Zero: %d S: %d 01: %d [ %s ]\n", direction_tag, h->s.x0, h->s.ss, h->s.ft, type); pri_message(ctrl, "%c N(R): %03d P/F: %d\n", direction_tag, h->s.n_r, h->s.p_f); pri_message(ctrl, "%c %d bytes of data\n", direction_tag, len - 4); break; case 3: /* Unnumbered frame */ type = "???"; if (h->u.ft == 3) { switch (h->u.m3) { case 0: if (h->u.m2 == 3) type = "DM (disconnect mode)"; else if (h->u.m2 == 0) type = "UI (unnumbered information)"; break; case 2: if (h->u.m2 == 0) type = "DISC (disconnect)"; break; case 3: if (h->u.m2 == 3) type = "SABME (set asynchronous balanced mode extended)"; else if (h->u.m2 == 0) type = "UA (unnumbered acknowledgement)"; break; case 4: if (h->u.m2 == 1) type = "FRMR (frame reject)"; break; case 5: if (h->u.m2 == 3) type = "XID (exchange identification note)"; break; default: break; } } pri_message(ctrl, "%c M3: %d P/F: %d M2: %d 11: %d [ %s ]\n", direction_tag, h->u.m3, h->u.p_f, h->u.m2, h->u.ft, type); pri_message(ctrl, "%c %d bytes of data\n", direction_tag, len - 3); break; } if ((h->u.ft == 3) && (h->u.m3 == 0) && (h->u.m2 == 0) && (h->u.data[0] == 0x0f)) { int ri; u_int8_t *action; /* TEI management related */ type = q921_tei_mgmt2str(h->u.data[3]); pri_message(ctrl, "%c MDL Message: %d(%s)\n", direction_tag, h->u.data[3], type); ri = (h->u.data[1] << 8) | h->u.data[2]; pri_message(ctrl, "%c Ri: %d\n", direction_tag, ri); action = &h->u.data[4]; for (x = len - (action - (u_int8_t *) h); 0 < x; --x, ++action) { pri_message(ctrl, "%c Ai: %d E:%d\n", direction_tag, (*action >> 1) & 0x7f, *action & 0x01); } } } } static void q921_dump_pri(struct q921_link *link, char direction_tag) { struct pri *ctrl; ctrl = link->ctrl; pri_message(ctrl, "%c TEI: %d State %d(%s)\n", direction_tag, link->tei, link->state, q921_state2str(link->state)); pri_message(ctrl, "%c V(A)=%d, V(S)=%d, V(R)=%d\n", direction_tag, link->v_a, link->v_s, link->v_r); pri_message(ctrl, "%c K=%d, RC=%d, l3_initiated=%d, reject_except=%d, ack_pend=%d\n", direction_tag, ctrl->timers[PRI_TIMER_K], link->RC, link->l3_initiated, link->reject_exception, link->acknowledge_pending); pri_message(ctrl, "%c T200_id=%d, N200=%d, T203_id=%d\n", direction_tag, link->t200_timer, ctrl->timers[PRI_TIMER_N200], link->t203_timer); } static void q921_dump_pri_by_h(struct pri *ctrl, char direction_tag, q921_h *h) { struct q921_link *link; if (!ctrl) { return; } if (BRI_NT_PTMP(ctrl)) { link = pri_find_tei(ctrl, h->h.sapi, h->h.tei); } else if (BRI_TE_PTMP(ctrl)) { /* We're operating on the specific TEI link */ link = ctrl->link.next; } else { link = &ctrl->link; } if (link) { q921_dump_pri(link, direction_tag); } else { pri_message(ctrl, "%c Link not found for this frame.\n", direction_tag); } } #define Q921_TEI_CHECK_MAX_POLLS 2 static void t201_expire(void *vctrl) { struct pri *ctrl; struct q921_link *link; struct q921_link *link_next; ctrl = vctrl; if (!ctrl->link.next) { /* No TEI links remain. */ ctrl->t201_timer = 0; return; } /* Start the TEI check timer. */ ctrl->t201_timer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T201], t201_expire, ctrl); ++ctrl->t201_expirycnt; if (Q921_TEI_CHECK_MAX_POLLS < ctrl->t201_expirycnt) { pri_schedule_del(ctrl, ctrl->t201_timer); ctrl->t201_timer = 0; /* Reclaim any dead TEI links. */ for (link = ctrl->link.next; link; link = link_next) { link_next = link->next; switch (link->tei_check) { case Q921_TEI_CHECK_DEAD: link->tei_check = Q921_TEI_CHECK_NONE; q921_tei_remove(ctrl, link->tei); q921_mdl_destroy(link); break; default: link->tei_check = Q921_TEI_CHECK_NONE; break; } } return; } if (!ctrl->t201_timer) { pri_error(ctrl, "Could not start T201 timer."); /* Abort the remaining TEI check. */ for (link = ctrl->link.next; link; link = link->next) { link->tei_check = Q921_TEI_CHECK_NONE; } return; } if (ctrl->t201_expirycnt == 1) { /* First poll. Setup TEI check state. */ for (link = ctrl->link.next; link; link = link->next) { if (link->state < Q921_TEI_ASSIGNED) { /* We do not have a TEI. */ link->tei_check = Q921_TEI_CHECK_NONE; } else { /* Mark TEI as dead until proved otherwise. */ link->tei_check = Q921_TEI_CHECK_DEAD; } } } else { /* Subsequent polls. Setup for new TEI check poll. */ for (link = ctrl->link.next; link; link = link->next) { switch (link->tei_check) { case Q921_TEI_CHECK_REPLY: link->tei_check = Q921_TEI_CHECK_DEAD_REPLY; break; default: break; } } } q921_send_tei(ctrl, Q921_TEI_IDENTITY_CHECK_REQUEST, 0, Q921_TEI_GROUP, 1); } static void q921_tei_check(struct pri *ctrl) { if (ctrl->t201_timer) { /* TEI check procedure already in progress. Do not disturb it. */ return; } ctrl->t201_expirycnt = 0; t201_expire(ctrl); } static pri_event *q921_receive_MDL(struct pri *ctrl, q921_u *h, int len) { int ri; struct q921_link *sub; struct q921_link *link; pri_event *res = NULL; u_int8_t *action; int count; int tei; if (!BRI_NT_PTMP(ctrl) && !BRI_TE_PTMP(ctrl)) { /* * Some telco switches send out MDL messages even though they * are configured for PTP. Usually they are checking for * assigned TEI's. */ if (ctrl->debug & PRI_DEBUG_Q921_STATE) { /* * Send out this message in debug modes since it is possible the * user has misconfigured their link for the wrong mode. */ pri_message(ctrl, "Not configured for PTMP. Ignoring MDL message: %d(%s)\n", h->data[3], q921_tei_mgmt2str(h->data[3])); } return NULL; } if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "Received MDL message\n"); } if (len <= &h->data[0] - (u_int8_t *) h) { pri_error(ctrl, "Received short frame\n"); return NULL; } if (h->data[0] != 0x0f) { pri_error(ctrl, "Received MDL with unsupported management entity %02x\n", h->data[0]); return NULL; } if (len <= &h->data[4] - (u_int8_t *) h) { pri_error(ctrl, "Received short MDL message\n"); return NULL; } if (h->data[3] != Q921_TEI_IDENTITY_CHECK_RESPONSE && !(h->data[4] & 0x01)) { pri_error(ctrl, "Received %d(%s) with Ai E bit not set.\n", h->data[3], q921_tei_mgmt2str(h->data[3])); return NULL; } ri = (h->data[1] << 8) | h->data[2]; tei = (h->data[4] >> 1); switch (h->data[3]) { case Q921_TEI_IDENTITY_REQUEST: if (!BRI_NT_PTMP(ctrl)) { return NULL; } if (tei != Q921_TEI_GROUP) { pri_error(ctrl, "Received %s with invalid TEI %d\n", q921_tei_mgmt2str(Q921_TEI_IDENTITY_REQUEST), tei); q921_send_tei(ctrl, Q921_TEI_IDENTITY_DENIED, ri, tei, 1); return NULL; } /* Find a TEI that is not allocated. */ tei = Q921_TEI_AUTO_FIRST; do { for (sub = &ctrl->link; sub->next; sub = sub->next) { if (sub->next->tei == tei) { /* This TEI is already assigned, try next one. */ ++tei; if (tei <= Q921_TEI_AUTO_LAST) { break; } pri_error(ctrl, "TEI pool exhausted. Reclaiming dead TEIs.\n"); q921_send_tei(ctrl, Q921_TEI_IDENTITY_DENIED, ri, Q921_TEI_GROUP, 1); q921_tei_check(ctrl); return NULL; } } } while (sub->next); if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "Allocating new TEI %d\n", tei); } link = pri_link_new(ctrl, Q921_SAPI_CALL_CTRL, tei); if (!link) { pri_error(ctrl, "Unable to allocate layer 2 link for new TEI %d\n", tei); return NULL; } sub->next = link; q921_setstate(link, Q921_TEI_ASSIGNED); q921_send_tei(ctrl, Q921_TEI_IDENTITY_ASSIGNED, ri, tei, 1); count = 0; for (sub = ctrl->link.next; sub; sub = sub->next) { ++count; } if (Q921_TEI_AUTO_LAST - Q921_TEI_AUTO_FIRST + 1 <= count) { /* * We just allocated the last TEI. Try to reclaim dead TEIs * before another is requested. */ if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "Allocated last TEI. Reclaiming dead TEIs.\n"); } q921_tei_check(ctrl); } if (ctrl->l2_persistence == PRI_L2_PERSISTENCE_KEEP_UP) { /* * Layer 2 is persistent so give the peer some time to setup * it's new TEI and bring the link up itself before we bring the * link up. */ restart_timer_start(link); } break; case Q921_TEI_IDENTITY_CHECK_RESPONSE: if (!BRI_NT_PTMP(ctrl)) { return NULL; } /* For each TEI listed in the message */ action = &h->data[4]; len -= (action - (u_int8_t *) h); for (; len; --len, ++action) { if (*action & 0x01) { /* This is the last TEI in the list because the Ai E bit is set. */ len = 1; } tei = (*action >> 1); if (tei == Q921_TEI_GROUP) { pri_error(ctrl, "Received %s with invalid TEI %d\n", q921_tei_mgmt2str(Q921_TEI_IDENTITY_CHECK_RESPONSE), tei); continue; } for (sub = ctrl->link.next; sub; sub = sub->next) { if (sub->tei == tei) { /* Found the TEI. */ switch (sub->tei_check) { case Q921_TEI_CHECK_NONE: break; case Q921_TEI_CHECK_DEAD: case Q921_TEI_CHECK_DEAD_REPLY: sub->tei_check = Q921_TEI_CHECK_REPLY; break; case Q921_TEI_CHECK_REPLY: /* Duplicate TEI detected. */ sub->tei_check = Q921_TEI_CHECK_NONE; q921_tei_remove(ctrl, tei); q921_mdl_destroy(sub); break; } break; } } if (!sub) { /* TEI not found. */ q921_tei_remove(ctrl, tei); } } break; case Q921_TEI_IDENTITY_VERIFY: if (!BRI_NT_PTMP(ctrl)) { return NULL; } if (tei == Q921_TEI_GROUP) { pri_error(ctrl, "Received %s with invalid TEI %d\n", q921_tei_mgmt2str(Q921_TEI_IDENTITY_VERIFY), tei); return NULL; } q921_tei_check(ctrl); break; case Q921_TEI_IDENTITY_ASSIGNED: if (!BRI_TE_PTMP(ctrl)) { return NULL; } /* Assuming we're operating on the specific TEI link here */ link = ctrl->link.next; switch (link->state) { case Q921_TEI_UNASSIGNED: /* * We do not have a TEI and we are not currently asking for one. * Start asking for one. */ q921_setstate(link, Q921_ASSIGN_AWAITING_TEI); q921_tei_request(link); return NULL; case Q921_ASSIGN_AWAITING_TEI: case Q921_ESTABLISH_AWAITING_TEI: /* We do not have a TEI and we want one. */ break; default: /* We already have a TEI. */ if (tei == link->tei) { /* * The TEI assignment conflicts with ours. Our TEI is the * duplicate so we should remove it. Q.921 Section 5.3.4.2 * condition c. */ pri_error(ctrl, "TEI=%d Conflicting TEI assignment. Removing our TEI.\n", tei); q921_mdl_remove(link); q921_start(link); } return NULL; } if (ri != link->ri) { if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "TEI assignment received for another Ri %02x (ours is %02x)\n", ri, link->ri); } return NULL; } pri_schedule_del(ctrl, link->t202_timer); link->t202_timer = 0; link->tei = tei; if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "Got assigned TEI %d\n", tei); } switch (link->state) { case Q921_ASSIGN_AWAITING_TEI: q921_setstate(link, Q921_TEI_ASSIGNED); if (ctrl->l2_persistence != PRI_L2_PERSISTENCE_KEEP_UP) { ctrl->ev.gen.e = PRI_EVENT_DCHAN_UP; res = &ctrl->ev; break; } /* Fall through: Layer 2 is persistent so bring it up. */ case Q921_ESTABLISH_AWAITING_TEI: q921_establish_data_link(link); link->l3_initiated = 1; q921_setstate(link, Q921_AWAITING_ESTABLISHMENT); ctrl->ev.gen.e = PRI_EVENT_DCHAN_UP; res = &ctrl->ev; break; default: break; } break; case Q921_TEI_IDENTITY_CHECK_REQUEST: if (!BRI_TE_PTMP(ctrl)) { return NULL; } /* Assuming we're operating on the specific TEI link here */ link = ctrl->link.next; if (link->state < Q921_TEI_ASSIGNED) { /* We do not have a TEI. */ return NULL; } /* If it's addressed to the group TEI or to our TEI specifically, we respond */ if (tei == Q921_TEI_GROUP || tei == link->tei) { q921_send_tei(ctrl, Q921_TEI_IDENTITY_CHECK_RESPONSE, random() % 65535, link->tei, 1); } break; case Q921_TEI_IDENTITY_REMOVE: if (!BRI_TE_PTMP(ctrl)) { return NULL; } /* Assuming we're operating on the specific TEI link here */ link = ctrl->link.next; if (link->state < Q921_TEI_ASSIGNED) { /* We do not have a TEI. */ return NULL; } /* If it's addressed to the group TEI or to our TEI specifically, we respond */ if (tei == Q921_TEI_GROUP || tei == link->tei) { q921_mdl_remove(link); q921_start(link); } break; } return res; /* Do we need to return something??? */ } static int is_command(struct pri *ctrl, q921_h *h) { int command = 0; int c_r = h->s.h.c_r; if ((ctrl->localtype == PRI_NETWORK && c_r == 0) || (ctrl->localtype == PRI_CPE && c_r == 1)) command = 1; return command; } static void q921_clear_exception_conditions(struct q921_link *link) { link->own_rx_busy = 0; link->peer_rx_busy = 0; link->reject_exception = 0; link->acknowledge_pending = 0; } static pri_event *q921_sabme_rx(struct q921_link *link, q921_h *h) { pri_event *res = NULL; struct pri *ctrl; enum Q931_DL_EVENT delay_q931_dl_event; ctrl = link->ctrl; switch (link->state) { case Q921_TIMER_RECOVERY: /* Timer recovery state handling is same as multiframe established */ case Q921_MULTI_FRAME_ESTABLISHED: /* Send Unnumbered Acknowledgement */ q921_send_ua(link, h->u.p_f); q921_clear_exception_conditions(link); q921_mdl_error(link, 'F'); if (link->v_s != link->v_a) { q921_discard_iqueue(link); /* DL-ESTABLISH indication */ delay_q931_dl_event = Q931_DL_EVENT_DL_ESTABLISH_IND; } else { delay_q931_dl_event = Q931_DL_EVENT_NONE; } stop_t200(link); start_t203(link); link->v_s = link->v_a = link->v_r = 0; q921_setstate(link, Q921_MULTI_FRAME_ESTABLISHED); if (delay_q931_dl_event != Q931_DL_EVENT_NONE) { /* Delayed because Q.931 could send STATUS messages. */ q931_dl_event(link, delay_q931_dl_event); } break; case Q921_TEI_ASSIGNED: restart_timer_stop(link); q921_send_ua(link, h->u.p_f); q921_clear_exception_conditions(link); link->v_s = link->v_a = link->v_r = 0; /* DL-ESTABLISH indication */ delay_q931_dl_event = Q931_DL_EVENT_DL_ESTABLISH_IND; if (PTP_MODE(ctrl)) { ctrl->ev.gen.e = PRI_EVENT_DCHAN_UP; res = &ctrl->ev; } start_t203(link); q921_setstate(link, Q921_MULTI_FRAME_ESTABLISHED); if (delay_q931_dl_event != Q931_DL_EVENT_NONE) { /* Delayed because Q.931 could send STATUS messages. */ q931_dl_event(link, delay_q931_dl_event); } break; case Q921_AWAITING_ESTABLISHMENT: q921_send_ua(link, h->u.p_f); break; case Q921_AWAITING_RELEASE: q921_send_dm(link, h->u.p_f); break; default: pri_error(ctrl, "Cannot handle SABME in state %d(%s)\n", link->state, q921_state2str(link->state)); break; } return res; } static pri_event *q921_disc_rx(struct q921_link *link, q921_h *h) { pri_event *res = NULL; struct pri *ctrl; ctrl = link->ctrl; if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "TEI=%d Got DISC\n", link->tei); } switch (link->state) { case Q921_TEI_ASSIGNED: case Q921_AWAITING_ESTABLISHMENT: q921_send_dm(link, h->u.p_f); break; case Q921_AWAITING_RELEASE: q921_send_ua(link, h->u.p_f); break; case Q921_MULTI_FRAME_ESTABLISHED: case Q921_TIMER_RECOVERY: res = q921_check_delay_restart(link); q921_discard_iqueue(link); q921_send_ua(link, h->u.p_f); /* DL-RELEASE indication */ q931_dl_event(link, Q931_DL_EVENT_DL_RELEASE_IND); stop_t200(link); if (link->state == Q921_MULTI_FRAME_ESTABLISHED) stop_t203(link); q921_setstate(link, Q921_TEI_ASSIGNED); break; default: pri_error(ctrl, "Don't know what to do with DISC in state %d(%s)\n", link->state, q921_state2str(link->state)); break; } return res; } static void q921_mdl_remove(struct q921_link *link) { int mdl_free_me; struct pri *ctrl; ctrl = link->ctrl; if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "MDL-REMOVE: Removing TEI %d\n", link->tei); } if (BRI_NT_PTMP(ctrl)) { if (link == &ctrl->link) { pri_error(ctrl, "Bad bad bad! Cannot MDL-REMOVE master\n"); return; } mdl_free_me = 1; } else { mdl_free_me = 0; } switch (link->state) { case Q921_TEI_ASSIGNED: restart_timer_stop(link); /* XXX: deviation! Since we don't have a UI queue, we just discard our I-queue */ q921_discard_iqueue(link); q921_setstate(link, Q921_TEI_UNASSIGNED); break; case Q921_AWAITING_ESTABLISHMENT: q921_discard_iqueue(link); /* DL-RELEASE indication */ q931_dl_event(link, Q931_DL_EVENT_DL_RELEASE_IND); stop_t200(link); q921_setstate(link, Q921_TEI_UNASSIGNED); break; case Q921_AWAITING_RELEASE: q921_discard_iqueue(link); /* DL-RELEASE confirm */ q931_dl_event(link, Q931_DL_EVENT_DL_RELEASE_CONFIRM); stop_t200(link); q921_setstate(link, Q921_TEI_UNASSIGNED); break; case Q921_MULTI_FRAME_ESTABLISHED: q921_discard_iqueue(link); /* DL-RELEASE indication */ q931_dl_event(link, Q931_DL_EVENT_DL_RELEASE_IND); stop_t200(link); stop_t203(link); q921_setstate(link, Q921_TEI_UNASSIGNED); break; case Q921_TIMER_RECOVERY: q921_discard_iqueue(link); /* DL-RELEASE indication */ q931_dl_event(link, Q931_DL_EVENT_DL_RELEASE_IND); stop_t200(link); q921_setstate(link, Q921_TEI_UNASSIGNED); break; default: pri_error(ctrl, "MDL-REMOVE when in state %d(%s)\n", link->state, q921_state2str(link->state)); return; } q931_dl_event(link, Q931_DL_EVENT_TEI_REMOVAL); /* * Negate the TEI value so debug messages will display a * negated TEI when it is actually unassigned. */ link->tei = -link->tei; link->mdl_free_me = mdl_free_me; } static void q921_mdl_link_destroy(struct q921_link *link) { struct pri *ctrl; struct q921_link *freep; struct q921_link *prev; ctrl = link->ctrl; freep = NULL; for (prev = &ctrl->link; prev->next; prev = prev->next) { if (prev->next == link) { prev->next = link->next; freep = link; break; } } if (freep == NULL) { pri_error(ctrl, "Huh!? no match found in list for TEI %d\n", -link->tei); return; } if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "Freeing TEI of %d\n", -freep->tei); } pri_link_destroy(freep); } static void q921_mdl_destroy(struct q921_link *link) { q921_mdl_remove(link); if (link->mdl_free_me) { q921_mdl_link_destroy(link); } } static void q921_mdl_handle_network_error(struct q921_link *link, char error) { struct pri *ctrl; switch (error) { case 'C': case 'D': case 'G': case 'H': q921_mdl_remove(link); break; case 'A': case 'B': case 'E': case 'F': case 'I': case 'J': case 'K': break; default: ctrl = link->ctrl; pri_error(ctrl, "Network MDL can't handle error of type %c\n", error); break; } } static void q921_mdl_handle_cpe_error(struct q921_link *link, char error) { struct pri *ctrl; switch (error) { case 'C': case 'D': case 'G': case 'H': q921_mdl_remove(link); break; case 'A': case 'B': case 'E': case 'F': case 'I': case 'J': case 'K': break; default: ctrl = link->ctrl; pri_error(ctrl, "CPE MDL can't handle error of type %c\n", error); break; } } static void q921_mdl_handle_ptp_error(struct q921_link *link, char error) { struct pri *ctrl; ctrl = link->ctrl; switch (error) { case 'J': /* * This is for the transition to Q921_AWAITING_ESTABLISHMENT. * The event is genereated here rather than where the MDL_ERROR * 'J' is posted because of the potential event conflict with * incoming I-frame information passed to Q.931. */ ctrl->schedev = 1; ctrl->ev.gen.e = PRI_EVENT_DCHAN_DOWN; break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'K': break; default: pri_error(ctrl, "PTP MDL can't handle error of type %c\n", error); break; } } static void q921_mdl_handle_error(struct q921_link *link, char error) { struct pri *ctrl; ctrl = link->ctrl; if (PTP_MODE(ctrl)) { q921_mdl_handle_ptp_error(link, error); } else { if (ctrl->localtype == PRI_NETWORK) { q921_mdl_handle_network_error(link, error); } else { q921_mdl_handle_cpe_error(link, error); } } } static void q921_mdl_handle_error_callback(void *vlink) { struct q921_link *link = vlink; q921_mdl_handle_error(link, link->mdl_error); link->mdl_error = 0; link->mdl_timer = 0; if (link->mdl_free_me) { q921_mdl_link_destroy(link); } } static void q921_mdl_error(struct q921_link *link, char error) { int is_debug_q921_state; struct pri *ctrl; ctrl = link->ctrl; /* Log the MDL-ERROR event when detected. */ is_debug_q921_state = (ctrl->debug & PRI_DEBUG_Q921_STATE); switch (error) { case 'A': pri_message(ctrl, "TEI=%d MDL-ERROR (A): Got supervisory frame with F=1 in state %d(%s)\n", link->tei, link->state, q921_state2str(link->state)); break; case 'B': case 'E': pri_message(ctrl, "TEI=%d MDL-ERROR (%c): DM (F=%c) in state %d(%s)\n", link->tei, error, (error == 'B') ? '1' : '0', link->state, q921_state2str(link->state)); break; case 'C': case 'D': if (is_debug_q921_state || PTP_MODE(ctrl)) { pri_message(ctrl, "TEI=%d MDL-ERROR (%c): UA (F=%c) in state %d(%s)\n", link->tei, error, (error == 'C') ? '1' : '0', link->state, q921_state2str(link->state)); } break; case 'F': /* * The peer is restarting the link. * Some reasons this might happen: * 1) Our link establishment requests collided. * 2) They got reset. * 3) They could not talk to us for some reason because * their T200 timer expired N200 times. * 4) They got an MDL-ERROR (J). */ if (is_debug_q921_state) { /* * This message is rather annoying and is normal for * reasons 1-3 above. */ pri_message(ctrl, "TEI=%d MDL-ERROR (F): SABME in state %d(%s)\n", link->tei, link->state, q921_state2str(link->state)); } break; case 'G': /* We could not get a response from the peer. */ if (is_debug_q921_state) { pri_message(ctrl, "TEI=%d MDL-ERROR (G): T200 expired N200 times sending SABME in state %d(%s)\n", link->tei, link->state, q921_state2str(link->state)); } break; case 'H': /* We could not get a response from the peer. */ if (is_debug_q921_state) { pri_message(ctrl, "TEI=%d MDL-ERROR (H): T200 expired N200 times sending DISC in state %d(%s)\n", link->tei, link->state, q921_state2str(link->state)); } break; case 'I': /* We could not get a response from the peer. */ if (is_debug_q921_state) { pri_message(ctrl, "TEI=%d MDL-ERROR (I): T200 expired N200 times sending RR/RNR in state %d(%s)\n", link->tei, link->state, q921_state2str(link->state)); } break; case 'J': /* N(R) not within ack window. */ pri_error(ctrl, "TEI=%d MDL-ERROR (J): N(R) error in state %d(%s)\n", link->tei, link->state, q921_state2str(link->state)); break; case 'K': /* * Received a frame reject frame. * The other end does not like what we are doing at all for some reason. */ pri_error(ctrl, "TEI=%d MDL-ERROR (K): FRMR in state %d(%s)\n", link->tei, link->state, q921_state2str(link->state)); break; default: pri_message(ctrl, "TEI=%d MDL-ERROR (%c): in state %d(%s)\n", link->tei, error, link->state, q921_state2str(link->state)); break; } if (link->mdl_error) { /* This should not happen. */ pri_error(ctrl, "Trying to queue MDL-ERROR (%c) when MDL-ERROR (%c) is already scheduled\n", error, link->mdl_error); return; } link->mdl_error = error; link->mdl_timer = pri_schedule_event(ctrl, 0, q921_mdl_handle_error_callback, link); if (!link->mdl_timer) { /* Timer allocation failed */ link->mdl_error = 0; } } static pri_event *q921_ua_rx(struct q921_link *link, q921_h *h) { struct pri *ctrl; pri_event *res = NULL; enum Q931_DL_EVENT delay_q931_dl_event; ctrl = link->ctrl; if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "TEI=%d Got UA\n", link->tei); } switch (link->state) { case Q921_TEI_ASSIGNED: case Q921_MULTI_FRAME_ESTABLISHED: case Q921_TIMER_RECOVERY: if (h->u.p_f) { q921_mdl_error(link, 'C'); } else { q921_mdl_error(link, 'D'); } break; case Q921_AWAITING_ESTABLISHMENT: if (!h->u.p_f) { q921_mdl_error(link, 'D'); break; } delay_q931_dl_event = Q931_DL_EVENT_NONE; if (!link->l3_initiated) { if (link->v_s != link->v_a) { q921_discard_iqueue(link); /* DL-ESTABLISH indication */ delay_q931_dl_event = Q931_DL_EVENT_DL_ESTABLISH_IND; } } else { link->l3_initiated = 0; /* DL-ESTABLISH confirm */ delay_q931_dl_event = Q931_DL_EVENT_DL_ESTABLISH_CONFIRM; } if (PTP_MODE(ctrl)) { ctrl->ev.gen.e = PRI_EVENT_DCHAN_UP; res = &ctrl->ev; } stop_t200(link); start_t203(link); link->v_r = link->v_s = link->v_a = 0; q921_setstate(link, Q921_MULTI_FRAME_ESTABLISHED); if (delay_q931_dl_event != Q931_DL_EVENT_NONE) { /* Delayed because Q.931 could send STATUS messages. */ q931_dl_event(link, delay_q931_dl_event); } break; case Q921_AWAITING_RELEASE: if (!h->u.p_f) { q921_mdl_error(link, 'D'); } else { res = q921_check_delay_restart(link); /* DL-RELEASE confirm */ q931_dl_event(link, Q931_DL_EVENT_DL_RELEASE_CONFIRM); stop_t200(link); q921_setstate(link, Q921_TEI_ASSIGNED); } break; default: pri_error(ctrl, "Don't know what to do with UA in state %d(%s)\n", link->state, q921_state2str(link->state)); break; } return res; } static void q921_enquiry_response(struct q921_link *link) { struct pri *ctrl; if (link->own_rx_busy) { /* XXX : TODO later sometime */ ctrl = link->ctrl; pri_error(ctrl, "Implement me %s: own_rx_busy\n", __FUNCTION__); //q921_rnr(link); } else { q921_rr(link, 1, 0); } link->acknowledge_pending = 0; } static void n_r_error_recovery(struct q921_link *link) { q921_mdl_error(link, 'J'); q921_establish_data_link(link); link->l3_initiated = 0; } static void update_v_a(struct q921_link *link, int n_r) { int idealcnt = 0, realcnt = 0; int x; struct pri *ctrl; ctrl = link->ctrl; /* Cancel each packet as necessary */ if (ctrl->debug & PRI_DEBUG_Q921_DUMP) pri_message(ctrl, "-- Got ACK for N(S)=%d to (but not including) N(S)=%d\n", link->v_a, n_r); for (x = link->v_a; x != n_r; Q921_INC(x)) { idealcnt++; realcnt += q921_ack_packet(link, x); } if (idealcnt != realcnt) { pri_error(ctrl, "Ideally should have ack'd %d frames, but actually ack'd %d. This is not good.\n", idealcnt, realcnt); q921_dump_iqueue_info(link); } link->v_a = n_r; } /*! \brief Is V(A) <= N(R) <= V(S) ? */ static int n_r_is_valid(struct q921_link *link, int n_r) { int x; for (x = link->v_a; x != n_r && x != link->v_s; Q921_INC(x)) { } if (x != n_r) { return 0; } else { return 1; } } static int q921_invoke_retransmission(struct q921_link *link, int n_r); static pri_event *timer_recovery_rr_rej_rx(struct q921_link *link, q921_h *h) { struct pri *ctrl; ctrl = link->ctrl; /* Figure B.7/Q.921 Page 74 */ link->peer_rx_busy = 0; if (is_command(ctrl, h)) { if (h->s.p_f) { /* Enquiry response */ q921_enquiry_response(link); } if (n_r_is_valid(link, h->s.n_r)) { update_v_a(link, h->s.n_r); } else { goto n_r_error_out; } } else { if (!h->s.p_f) { if (n_r_is_valid(link, h->s.n_r)) { update_v_a(link, h->s.n_r); } else { goto n_r_error_out; } } else { if (n_r_is_valid(link, h->s.n_r)) { update_v_a(link, h->s.n_r); stop_t200(link); start_t203(link); q921_invoke_retransmission(link, h->s.n_r); q921_setstate(link, Q921_MULTI_FRAME_ESTABLISHED); } else { goto n_r_error_out; } } } return NULL; n_r_error_out: n_r_error_recovery(link); q921_setstate(link, Q921_AWAITING_ESTABLISHMENT); return NULL; } static pri_event *q921_rr_rx(struct q921_link *link, q921_h *h) { pri_event *res = NULL; struct pri *ctrl; ctrl = link->ctrl; #if 0 /* Don't flood debug trace with RR if not really looking at Q.921 layer. */ if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "TEI=%d Got RR N(R)=%d\n", link->tei, h->s.n_r); } #endif switch (link->state) { case Q921_TIMER_RECOVERY: res = timer_recovery_rr_rej_rx(link, h); break; case Q921_MULTI_FRAME_ESTABLISHED: /* Figure B.7/Q.921 Page 74 */ link->peer_rx_busy = 0; if (is_command(ctrl, h)) { if (h->s.p_f) { /* Enquiry response */ q921_enquiry_response(link); } } else { if (h->s.p_f) { q921_mdl_error(link, 'A'); } } if (!n_r_is_valid(link, h->s.n_r)) { n_r_error_recovery(link); q921_setstate(link, Q921_AWAITING_ESTABLISHMENT); } else { if (h->s.n_r == link->v_s) { update_v_a(link, h->s.n_r); stop_t200(link); start_t203(link); } else { if (h->s.n_r != link->v_a) { /* Need to check the validity of n_r as well.. */ update_v_a(link, h->s.n_r); restart_t200(link); } } } break; case Q921_TEI_ASSIGNED: case Q921_AWAITING_ESTABLISHMENT: case Q921_AWAITING_RELEASE: /* * Ignore this frame. * We likely got reset and the other end has not realized it yet. */ break; default: pri_error(ctrl, "Don't know what to do with RR in state %d(%s)\n", link->state, q921_state2str(link->state)); break; } return res; } static int q921_invoke_retransmission(struct q921_link *link, int n_r) { struct q921_frame *f; struct pri *ctrl; ctrl = link->ctrl; /* * All acked frames should already have been removed from the queue. * Push back all sent frames. */ for (f = link->tx_queue; f && f->status == Q921_TX_FRAME_SENT; f = f->next) { f->status = Q921_TX_FRAME_PUSHED_BACK; /* Sanity check: Is V(A) <= N(S) <= V(S)? */ if (!n_r_is_valid(link, f->h.n_s)) { pri_error(ctrl, "Tx Q frame with invalid N(S)=%d. Must be (V(A)=%d) <= N(S) <= (V(S)=%d)\n", f->h.n_s, link->v_a, link->v_s); } } link->v_s = n_r; return q921_send_queued_iframes(link); } static pri_event *q921_rej_rx(struct q921_link *link, q921_h *h) { pri_event *res = NULL; struct pri *ctrl; ctrl = link->ctrl; if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "TEI=%d Got REJ N(R)=%d\n", link->tei, h->s.n_r); } switch (link->state) { case Q921_TIMER_RECOVERY: res = timer_recovery_rr_rej_rx(link, h); break; case Q921_MULTI_FRAME_ESTABLISHED: /* Figure B.7/Q.921 Page 74 */ link->peer_rx_busy = 0; if (is_command(ctrl, h)) { if (h->s.p_f) { /* Enquiry response */ q921_enquiry_response(link); } } else { if (h->s.p_f) { q921_mdl_error(link, 'A'); } } if (!n_r_is_valid(link, h->s.n_r)) { n_r_error_recovery(link); q921_setstate(link, Q921_AWAITING_ESTABLISHMENT); } else { update_v_a(link, h->s.n_r); stop_t200(link); start_t203(link); q921_invoke_retransmission(link, h->s.n_r); } break; case Q921_TEI_ASSIGNED: case Q921_AWAITING_ESTABLISHMENT: case Q921_AWAITING_RELEASE: /* * Ignore this frame. * We likely got reset and the other end has not realized it yet. */ break; default: pri_error(ctrl, "Don't know what to do with REJ in state %d(%s)\n", link->state, q921_state2str(link->state)); break; } return res; } static pri_event *q921_frmr_rx(struct q921_link *link, q921_h *h) { pri_event *res = NULL; struct pri *ctrl; ctrl = link->ctrl; if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "TEI=%d Got FRMR\n", link->tei); } switch (link->state) { case Q921_TIMER_RECOVERY: case Q921_MULTI_FRAME_ESTABLISHED: q921_mdl_error(link, 'K'); q921_establish_data_link(link); link->l3_initiated = 0; q921_setstate(link, Q921_AWAITING_ESTABLISHMENT); if (PTP_MODE(ctrl)) { ctrl->ev.gen.e = PRI_EVENT_DCHAN_DOWN; res = &ctrl->ev; } break; case Q921_TEI_ASSIGNED: case Q921_AWAITING_ESTABLISHMENT: case Q921_AWAITING_RELEASE: /* * Ignore this frame. * We likely got reset and the other end has not realized it yet. */ if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "TEI=%d Ignoring FRMR.\n", link->tei); } break; default: pri_error(ctrl, "Don't know what to do with FRMR in state %d(%s)\n", link->state, q921_state2str(link->state)); break; } return res; } static pri_event *q921_iframe_rx(struct q921_link *link, q921_h *h, int len) { struct pri *ctrl; pri_event *eres = NULL; int res = 0; int delay_q931_receive; ctrl = link->ctrl; switch (link->state) { case Q921_TIMER_RECOVERY: case Q921_MULTI_FRAME_ESTABLISHED: delay_q931_receive = 0; /* FIXME: Verify that it's a command ... */ if (link->own_rx_busy) { /* DEVIATION: Handle own rx busy */ } else if (h->i.n_s == link->v_r) { Q921_INC(link->v_r); link->reject_exception = 0; /* * Dump Q.931 message where Q.921 says to queue it to Q.931 so if * Q.921 is dumping its frames they will be in the correct order. */ if (ctrl->debug & PRI_DEBUG_Q931_DUMP) { q931_dump(ctrl, h->h.tei, (q931_h *) h->i.data, len - 4, 0); } delay_q931_receive = 1; if (h->i.p_f) { q921_rr(link, 1, 0); link->acknowledge_pending = 0; } else { link->acknowledge_pending = 1; } } else { if (link->reject_exception) { if (h->i.p_f) { q921_rr(link, 1, 0); link->acknowledge_pending = 0; } } else { link->reject_exception = 1; q921_reject(link, h->i.p_f); link->acknowledge_pending = 0; } } if (!n_r_is_valid(link, h->i.n_r)) { n_r_error_recovery(link); q921_setstate(link, Q921_AWAITING_ESTABLISHMENT); } else { if (link->state == Q921_TIMER_RECOVERY) { update_v_a(link, h->i.n_r); } else { if (link->peer_rx_busy) { update_v_a(link, h->i.n_r); } else { if (h->i.n_r == link->v_s) { update_v_a(link, h->i.n_r); stop_t200(link); start_t203(link); } else { if (h->i.n_r != link->v_a) { update_v_a(link, h->i.n_r); reschedule_t200(link); } } } } } if (delay_q931_receive) { /* Q.921 has finished processing the frame so we can give it to Q.931 now. */ res = q931_receive(link, (q931_h *) h->i.data, len - 4); if (res != -1 && (res & Q931_RES_HAVEEVENT)) { eres = &ctrl->ev; } } break; case Q921_TEI_ASSIGNED: case Q921_AWAITING_ESTABLISHMENT: case Q921_AWAITING_RELEASE: /* * Ignore this frame. * We likely got reset and the other end has not realized it yet. */ break; default: pri_error(ctrl, "Don't know what to do with an I-frame in state %d(%s)\n", link->state, q921_state2str(link->state)); break; } return eres; } static pri_event *q921_dm_rx(struct q921_link *link, q921_h *h) { pri_event *res = NULL; struct pri *ctrl; ctrl = link->ctrl; if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "TEI=%d Got DM\n", link->tei); } switch (link->state) { case Q921_TEI_ASSIGNED: if (h->u.p_f) break; /* else */ restart_timer_stop(link); q921_establish_data_link(link); link->l3_initiated = 1; q921_setstate(link, Q921_AWAITING_ESTABLISHMENT); break; case Q921_AWAITING_ESTABLISHMENT: if (!h->u.p_f) break; res = q921_check_delay_restart(link); q921_discard_iqueue(link); /* DL-RELEASE indication */ q931_dl_event(link, Q931_DL_EVENT_DL_RELEASE_IND); stop_t200(link); q921_setstate(link, Q921_TEI_ASSIGNED); break; case Q921_AWAITING_RELEASE: if (!h->u.p_f) break; res = q921_check_delay_restart(link); /* DL-RELEASE confirm */ q931_dl_event(link, Q931_DL_EVENT_DL_RELEASE_CONFIRM); stop_t200(link); q921_setstate(link, Q921_TEI_ASSIGNED); break; case Q921_MULTI_FRAME_ESTABLISHED: if (h->u.p_f) { q921_mdl_error(link, 'B'); break; } q921_mdl_error(link, 'E'); q921_establish_data_link(link); link->l3_initiated = 0; q921_setstate(link, Q921_AWAITING_ESTABLISHMENT); if (PTP_MODE(ctrl)) { ctrl->ev.gen.e = PRI_EVENT_DCHAN_DOWN; res = &ctrl->ev; } break; case Q921_TIMER_RECOVERY: if (h->u.p_f) { q921_mdl_error(link, 'B'); } else { q921_mdl_error(link, 'E'); } q921_establish_data_link(link); link->l3_initiated = 0; q921_setstate(link, Q921_AWAITING_ESTABLISHMENT); if (PTP_MODE(ctrl)) { ctrl->ev.gen.e = PRI_EVENT_DCHAN_DOWN; res = &ctrl->ev; } break; default: pri_error(ctrl, "Don't know what to do with DM frame in state %d(%s)\n", link->state, q921_state2str(link->state)); break; } return res; } static pri_event *q921_rnr_rx(struct q921_link *link, q921_h *h) { pri_event *res = NULL; struct pri *ctrl; ctrl = link->ctrl; if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "TEI=%d Got RNR N(R)=%d\n", link->tei, h->s.n_r); } switch (link->state) { case Q921_MULTI_FRAME_ESTABLISHED: link->peer_rx_busy = 1; if (!is_command(ctrl, h)) { if (h->s.p_f) { q921_mdl_error(link, 'A'); } } else { if (h->s.p_f) { q921_enquiry_response(link); } } if (!n_r_is_valid(link, h->s.n_r)) { n_r_error_recovery(link); q921_setstate(link, Q921_AWAITING_ESTABLISHMENT); } else { update_v_a(link, h->s.n_r); stop_t203(link); restart_t200(link); } break; case Q921_TIMER_RECOVERY: /* Q.921 Figure B.8 Q921 (Sheet 6 of 9) Page 85 */ link->peer_rx_busy = 1; if (is_command(ctrl, h)) { if (h->s.p_f) { q921_enquiry_response(link); } if (n_r_is_valid(link, h->s.n_r)) { update_v_a(link, h->s.n_r); break; } else { n_r_error_recovery(link); q921_setstate(link, Q921_AWAITING_ESTABLISHMENT); break; } } else { if (h->s.p_f) { if (n_r_is_valid(link, h->s.n_r)) { update_v_a(link, h->s.n_r); restart_t200(link); q921_invoke_retransmission(link, h->s.n_r); q921_setstate(link, Q921_MULTI_FRAME_ESTABLISHED); break; } else { n_r_error_recovery(link); q921_setstate(link, Q921_AWAITING_ESTABLISHMENT); break; } } else { if (n_r_is_valid(link, h->s.n_r)) { update_v_a(link, h->s.n_r); break; } else { n_r_error_recovery(link); q921_setstate(link, Q921_AWAITING_ESTABLISHMENT); break; } } } break; case Q921_TEI_ASSIGNED: case Q921_AWAITING_ESTABLISHMENT: case Q921_AWAITING_RELEASE: /* * Ignore this frame. * We likely got reset and the other end has not realized it yet. */ break; default: pri_error(ctrl, "Don't know what to do with RNR in state %d(%s)\n", link->state, q921_state2str(link->state)); break; } return res; } static void q921_acknowledge_pending_check(struct q921_link *link) { if (link->acknowledge_pending) { link->acknowledge_pending = 0; q921_rr(link, 0, 0); } } static void q921_statemachine_check(struct q921_link *link) { switch (link->state) { case Q921_MULTI_FRAME_ESTABLISHED: q921_send_queued_iframes(link); q921_acknowledge_pending_check(link); break; case Q921_TIMER_RECOVERY: q921_acknowledge_pending_check(link); break; default: break; } } static pri_event *__q921_receive_qualified(struct q921_link *link, q921_h *h, int len) { int res; pri_event *ev = NULL; struct pri *ctrl; ctrl = link->ctrl; switch (h->h.data[0] & Q921_FRAMETYPE_MASK) { case 0: case 2: ev = q921_iframe_rx(link, h, len); break; case 1: switch ((h->s.x0 << 2) | h->s.ss) { case 0x00: ev = q921_rr_rx(link, h); break; case 0x01: ev = q921_rnr_rx(link, h); break; case 0x02: ev = q921_rej_rx(link, h); break; default: pri_error(ctrl, "!! XXX Unknown Supervisory frame x0=%d ss=%d, pf=%d, N(R)=%d, V(A)=%d, V(S)=%d XXX\n", h->s.x0, h->s.ss, h->s.p_f, h->s.n_r, link->v_a, link->v_s); break; } break; case 3: if (len < 3) { pri_error(ctrl, "!! Received short unnumbered frame\n"); break; } switch ((h->u.m3 << 2) | h->u.m2) { case 0x03: ev = q921_dm_rx(link, h); break; case 0x00: /* UI-frame */ if (ctrl->debug & PRI_DEBUG_Q931_DUMP) { q931_dump(ctrl, h->h.tei, (q931_h *) h->u.data, len - 3, 0); } res = q931_receive(link, (q931_h *) h->u.data, len - 3); if (res != -1 && (res & Q931_RES_HAVEEVENT)) { ev = &ctrl->ev; } break; case 0x08: ev = q921_disc_rx(link, h); break; case 0x0F: /* SABME */ if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "TEI=%d Got SABME from %s peer.\n", link->tei, h->h.c_r ? "network" : "cpe"); } if (h->h.c_r) { ctrl->remotetype = PRI_NETWORK; if (ctrl->localtype == PRI_NETWORK) { /* We can't both be networks */ ev = pri_mkerror(ctrl, "We think we're the network, but they think they're the network, too."); break; } } else { ctrl->remotetype = PRI_CPE; if (ctrl->localtype == PRI_CPE) { /* We can't both be CPE */ ev = pri_mkerror(ctrl, "We think we're the CPE, but they think they're the CPE too.\n"); break; } } ev = q921_sabme_rx(link, h); break; case 0x0C: ev = q921_ua_rx(link, h); break; case 0x11: ev = q921_frmr_rx(link, h); break; case 0x17: pri_error(ctrl, "!! XID frames not supported\n"); break; default: pri_error(ctrl, "!! Don't know what to do with u-frame (m3=%d, m2=%d)\n", h->u.m3, h->u.m2); break; } break; } q921_statemachine_check(link); return ev; } static pri_event *q921_handle_unmatched_frame(struct pri *ctrl, q921_h *h, int len) { if (h->h.tei < 64) { pri_error(ctrl, "Do not support manual TEI range. Discarding\n"); return NULL; } if (h->h.sapi != Q921_SAPI_CALL_CTRL) { pri_error(ctrl, "Message with SAPI other than CALL CTRL is discarded\n"); return NULL; } /* If we're NT-PTMP, this means an unrecognized TEI that we'll kill */ if (BRI_NT_PTMP(ctrl)) { if (ctrl->debug & PRI_DEBUG_Q921_STATE) { pri_message(ctrl, "Could not find a layer 2 link for received frame with SAPI/TEI of %d/%d.\n", h->h.sapi, h->h.tei); pri_message(ctrl, "Sending TEI release, in order to re-establish TEI state\n"); } q921_tei_remove(ctrl, h->h.tei); } return NULL; } /* This code assumes that the pri structure is the master pri */ static pri_event *__q921_receive(struct pri *ctrl, q921_h *h, int len) { pri_event *ev = NULL; struct q921_link *link; /* Discard FCS */ len -= 2; if (ctrl->debug & (PRI_DEBUG_Q921_DUMP | PRI_DEBUG_Q921_RAW)) { q921_dump(ctrl, h, len, ctrl->debug, 0); } /* Check some reject conditions -- Start by rejecting improper ea's */ if (h->h.ea1 || !h->h.ea2) { return NULL; } if (h->h.sapi == Q921_SAPI_LAYER2_MANAGEMENT) { return q921_receive_MDL(ctrl, &h->u, len); } if (h->h.tei == Q921_TEI_GROUP && h->h.sapi != Q921_SAPI_CALL_CTRL) { pri_error(ctrl, "Do not handle group messages to services other than MDL or CALL CTRL\n"); return NULL; } if (BRI_TE_PTMP(ctrl)) { /* We're operating on the specific TEI link */ link = ctrl->link.next; if (h->h.sapi == link->sapi && ((link->state >= Q921_TEI_ASSIGNED && h->h.tei == link->tei) || h->h.tei == Q921_TEI_GROUP)) { ev = __q921_receive_qualified(link, h, len); } /* Only support reception on our specific TEI link */ } else if (BRI_NT_PTMP(ctrl)) { link = pri_find_tei(ctrl, h->h.sapi, h->h.tei); if (link) { ev = __q921_receive_qualified(link, h, len); } else { ev = q921_handle_unmatched_frame(ctrl, h, len); } } else if (PTP_MODE(ctrl) && h->h.sapi == ctrl->link.sapi && (h->h.tei == ctrl->link.tei || h->h.tei == Q921_TEI_GROUP)) { ev = __q921_receive_qualified(&ctrl->link, h, len); } else { ev = NULL; } if (ctrl->debug & PRI_DEBUG_Q921_DUMP) { pri_message(ctrl, "Done handling message for SAPI/TEI=%d/%d\n", h->h.sapi, h->h.tei); } return ev; } pri_event *q921_receive(struct pri *ctrl, q921_h *h, int len) { pri_event *e; e = __q921_receive(ctrl, h, len); ctrl->q921_rxcount++; return e; } static void q921_establish_data_link(struct q921_link *link) { q921_clear_exception_conditions(link); link->RC = 0; stop_t203(link); reschedule_t200(link); q921_send_sabme(link); } static void nt_ptmp_dchannel_up(void *vpri) { struct pri *ctrl = vpri; ctrl->schedev = 1; ctrl->ev.gen.e = PRI_EVENT_DCHAN_UP; } void q921_start(struct q921_link *link) { struct pri *ctrl; ctrl = link->ctrl; if (PTMP_MODE(ctrl)) { if (TE_MODE(ctrl)) { q921_setstate(link, Q921_ASSIGN_AWAITING_TEI); q921_tei_request(link); } else { q921_setstate(link, Q921_TEI_UNASSIGNED); pri_schedule_event(ctrl, 0, nt_ptmp_dchannel_up, ctrl); if (!ctrl->link.next) { /* * We do not have any TEI's so make sure there are no devices * that think they have a TEI. A device may think it has a TEI * if the upper layer program is restarted or the system * reboots. */ q921_tei_remove(ctrl, Q921_TEI_GROUP); } } } else { /* PTP mode, no need for TEI management junk */ q921_establish_data_link(link); link->l3_initiated = 1; q921_setstate(link, Q921_AWAITING_ESTABLISHMENT); } } libpri-1.4.14/rose_qsig_name.c0000644000000000000000000004014011420105263014710 0ustar rootroot/* * 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 Name operations and elements * * Name-Operations ECMA-164 Annex C * * \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 Q.SIG NameSet 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 name * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *rose_enc_qsig_NameSet(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseQsigName *name) { unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); ASN1_CALL(pos, asn1_enc_string_bin(pos, end, ASN1_TYPE_OCTET_STRING, name->data, name->length)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, name->char_set)); ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the Q.SIG Name 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 name * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ unsigned char *rose_enc_qsig_Name(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct roseQsigName *name) { switch (name->presentation) { case 0: /* optional_name_not_present */ /* Do not encode anything */ break; case 1: /* presentation_allowed */ if (name->char_set == 1) { ASN1_CALL(pos, asn1_enc_string_bin(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0, name->data, name->length)); } else { ASN1_CALL(pos, rose_enc_qsig_NameSet(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, name)); } break; case 2: /* presentation_restricted */ if (name->char_set == 1) { ASN1_CALL(pos, asn1_enc_string_bin(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, name->data, name->length)); } else { ASN1_CALL(pos, rose_enc_qsig_NameSet(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3, name)); } break; case 3: /* presentation_restricted_null */ ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 7)); break; case 4: /* name_not_available */ ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 4)); break; default: ASN1_ENC_ERROR(ctrl, "Unknown name presentation"); return NULL; } return pos; } /*! * \internal * \brief Encode the Q.SIG party-Name 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 party Information to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *rose_enc_qsig_PartyName_ARG_Backend(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct roseQsigPartyName_ARG *party) { return rose_enc_qsig_Name(ctrl, pos, end, &party->name); } /*! * \brief Encode the Q.SIG CallingName 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_CallingName_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { return rose_enc_qsig_PartyName_ARG_Backend(ctrl, pos, end, &args->qsig.CallingName); } /*! * \brief Encode the Q.SIG CalledName 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_CalledName_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { return rose_enc_qsig_PartyName_ARG_Backend(ctrl, pos, end, &args->qsig.CalledName); } /*! * \brief Encode the Q.SIG ConnectedName 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_ConnectedName_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { return rose_enc_qsig_PartyName_ARG_Backend(ctrl, pos, end, &args->qsig.ConnectedName); } /*! * \brief Encode the Q.SIG BusyName 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_BusyName_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { return rose_enc_qsig_PartyName_ARG_Backend(ctrl, pos, end, &args->qsig.BusyName); } /*! * \internal * \brief Decode the Q.SIG NameData Name argument parameters. * * \param ctrl D channel controller for any diagnostic messages. * \param fname 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 name 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_NameData(struct pri *ctrl, const char *fname, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseQsigName *name) { size_t str_len; ASN1_CALL(pos, asn1_dec_string_bin(ctrl, fname, tag, pos, end, sizeof(name->data), name->data, &str_len)); name->length = str_len; return pos; } /*! * \internal * \brief Decode the Q.SIG NameSet Name argument parameters. * * \param ctrl D channel controller for any diagnostic messages. * \param fname 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 name 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_NameSet(struct pri *ctrl, const char *fname, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseQsigName *name) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s NameSet %s\n", fname, 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_PC_MASK, ASN1_TYPE_OCTET_STRING); ASN1_CALL(pos, rose_dec_qsig_NameData(ctrl, "nameData", tag, pos, seq_end, name)); if (pos < end && *pos != ASN1_INDEF_TERM) { 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, "characterSet", tag, pos, seq_end, &value)); name->char_set = value; } else { name->char_set = 1; /* default to iso8859-1 */ } ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the Q.SIG Name argument parameters. * * \param ctrl D channel controller for any diagnostic messages. * \param fname 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 name Parameter storage to fill. * * \retval Start of the next ASN.1 component on success. * \retval NULL on error. */ const unsigned char *rose_dec_qsig_Name(struct pri *ctrl, const char *fname, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseQsigName *name) { if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s Name\n", fname); } name->char_set = 1; /* default to iso8859-1 */ switch (tag & ~ASN1_PC_MASK) { case ASN1_CLASS_CONTEXT_SPECIFIC | 0: name->presentation = 1; /* presentation_allowed */ ASN1_CALL(pos, rose_dec_qsig_NameData(ctrl, "namePresentationAllowedSimple", tag, pos, end, name)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 1: /* Must be constructed but we will not check for it for simplicity. */ name->presentation = 1; /* presentation_allowed */ ASN1_CALL(pos, rose_dec_qsig_NameSet(ctrl, "namePresentationAllowedExtended", tag, pos, end, name)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 2: name->presentation = 2; /* presentation_restricted */ ASN1_CALL(pos, rose_dec_qsig_NameData(ctrl, "namePresentationRestrictedSimple", tag, pos, end, name)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 3: /* Must be constructed but we will not check for it for simplicity. */ name->presentation = 2; /* presentation_restricted */ ASN1_CALL(pos, rose_dec_qsig_NameSet(ctrl, "namePresentationRestrictedExtended", tag, pos, end, name)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 4: /* Must not be constructed but we will not check for it for simplicity. */ name->presentation = 4; /* name_not_available */ name->length = 0; name->data[0] = 0; ASN1_CALL(pos, asn1_dec_null(ctrl, "nameNotAvailable", tag, pos, end)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 7: /* Must not be constructed but we will not check for it for simplicity. */ name->presentation = 3; /* presentation_restricted_null */ name->length = 0; name->data[0] = 0; ASN1_CALL(pos, asn1_dec_null(ctrl, "namePresentationRestrictedNull", tag, pos, end)); break; default: ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); return NULL; } return pos; } /*! * \internal * \brief Decode the Q.SIG party-Name 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 party 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_PartyName_ARG_Backend(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseQsigPartyName_ARG *party) { int length; int seq_offset; const unsigned char *seq_end; if (tag == ASN1_TAG_SEQUENCE) { 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(seq_end, seq_offset, length, pos, end); ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CALL(pos, rose_dec_qsig_Name(ctrl, "name", tag, pos, seq_end, &party->name)); /* Fixup will skip over any OPTIONAL manufacturer extension information */ ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); } else { ASN1_CALL(pos, rose_dec_qsig_Name(ctrl, name, tag, pos, end, &party->name)); } return pos; } /*! * \brief Decode the Q.SIG CallingName 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_CallingName_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { return rose_dec_qsig_PartyName_ARG_Backend(ctrl, "callingName", tag, pos, end, &args->qsig.CallingName); } /*! * \brief Decode the Q.SIG CalledName 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_CalledName_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { return rose_dec_qsig_PartyName_ARG_Backend(ctrl, "calledName", tag, pos, end, &args->qsig.CalledName); } /*! * \brief Decode the Q.SIG ConnectedName 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_ConnectedName_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { return rose_dec_qsig_PartyName_ARG_Backend(ctrl, "connectedName", tag, pos, end, &args->qsig.ConnectedName); } /*! * \brief Decode the Q.SIG BusyName 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_BusyName_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { return rose_dec_qsig_PartyName_ARG_Backend(ctrl, "busyName", tag, pos, end, &args->qsig.BusyName); } /* ------------------------------------------------------------------- */ /* end rose_qsig_name.c */ libpri-1.4.14/TODO0000644000000000000000000000023010375150272012245 0ustar rootrootGeneral: Q.921: -- Support unnumbered information frames -- Get TEI codes working for BRI interfaces Q.931: -- Implement the 11 missing Q.931 timers libpri-1.4.14/rose_other.c0000644000000000000000000002267711173441755014123 0ustar rootroot/* * 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 Switch type operations for: NI2, 4ESS, 5ESS, DMS-100 * * \author Richard Mudgett */ #include "compat.h" #include "libpri.h" #include "pri_internal.h" #include "rose.h" #include "rose_internal.h" #include "asn1.h" /* ------------------------------------------------------------------- */ /*! * \brief Encode the DMS-100 RLT_OperationInd 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_dms100_RLT_OperationInd_RES(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args) { return asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0, args->dms100.RLT_OperationInd.call_id); } /*! * \brief Encode the DMS-100 RLT_ThirdParty 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_dms100_RLT_ThirdParty_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { const struct roseDms100RLTThirdParty_ARG *rlt_thirdparty; unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); rlt_thirdparty = &args->dms100.RLT_ThirdParty; ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0, rlt_thirdparty->call_id)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, rlt_thirdparty->reason)); ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Decode the DMS-100 RLT_OperationInd 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_dms100_RLT_OperationInd_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_CLASS_CONTEXT_SPECIFIC | 0); ASN1_CALL(pos, asn1_dec_int(ctrl, "callId", tag, pos, end, &value)); args->dms100.RLT_OperationInd.call_id = value; return pos; } /*! * \brief Decode the DMS-100 RLT_ThirdParty 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_dms100_RLT_ThirdParty_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; struct roseDms100RLTThirdParty_ARG *rlt_third_party; rlt_third_party = &args->dms100.RLT_ThirdParty; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " RLT_ThirdParty %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_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 0); ASN1_CALL(pos, asn1_dec_int(ctrl, "callId", tag, pos, seq_end, &value)); rlt_third_party->call_id = value; ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 1); ASN1_CALL(pos, asn1_dec_int(ctrl, "reason", tag, pos, seq_end, &value)); rlt_third_party->reason = value; /* Fixup will skip over any OPTIONAL information */ ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Encode the NI2 InformationFollowing 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_ni2_InformationFollowing_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { /* Encode the unknown enumeration value. */ return asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, args->ni2.InformationFollowing.value); } /*! * \brief Encode the NI2 InitiateTransfer 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_ni2_InitiateTransfer_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { const struct roseNi2InitiateTransfer_ARG *initiate_transfer; unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); initiate_transfer = &args->ni2.InitiateTransfer; ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, initiate_transfer->call_reference)); ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Decode the NI2 InformationFollowing 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_ni2_InformationFollowing_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_ENUMERATED); ASN1_CALL(pos, asn1_dec_int(ctrl, "unknown", tag, pos, end, &value)); args->ni2.InformationFollowing.value = value; return pos; } /*! * \brief Decode the NI2 InitiateTransfer 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_ni2_InitiateTransfer_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; struct roseNi2InitiateTransfer_ARG *initiate_transfer; initiate_transfer = &args->ni2.InitiateTransfer; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " InitiateTransfer %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_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER); ASN1_CALL(pos, asn1_dec_int(ctrl, "callReference", tag, pos, seq_end, &value)); initiate_transfer->call_reference = value; /* Fixup will skip over any OPTIONAL information */ ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /* ------------------------------------------------------------------- */ /* end rose_other.c */ libpri-1.4.14/pritest.c0000644000000000000000000002500012043571370013415 0ustar rootroot/* * libpri: An implementation of Primary Rate ISDN * * Written by Mark Spencer * * Copyright (C) 2001-2005, Digium, Inc. * All Rights Reserved. */ /* * 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. */ /* * This program tests libpri call reception using a dahdi interface. * Its state machines are setup for RECEIVING CALLS ONLY, so if you * are trying to both place and receive calls you have to a bit more. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libpri.h" #define PRI_DEF_NODETYPE PRI_CPE #define PRI_DEF_SWITCHTYPE PRI_SWITCH_NI2 #define MAX_CHAN 32 #define DCHANNEL_TIMESLOT 16 #define READ_SIZE 160 static int offset = 0; static void do_channel(int fd) { /* This is the part that runs on a given channel */ char buf[READ_SIZE]; int res; int i=0; while ((res = read(fd, buf, READ_SIZE)) > 0 && (i++ < 1000)) { if (write(fd, buf, res) == -1) { fprintf(stderr, "--!! Failed write: %d\n", errno); break; } } } struct pri_chan { pid_t pid; int needhangup; int alreadyhungup; q931_call *call; } chans[MAX_CHAN]; static int str2node(char *node) { if (!strcasecmp(node, "cpe")) return PRI_CPE; if (!strcasecmp(node, "network")) return PRI_NETWORK; return -1; } static void chan_ended(int sig) { int status; int x; struct rusage rusage; pid_t pid; pid = wait4(-1, &status, WNOHANG, &rusage); for (x=0;x -1) { fprintf(stderr, "--!! Unknown PID %d exited\n", pid); return; } } static int str2switch(char *swtype) { if (!strcasecmp(swtype, "ni2")) return PRI_SWITCH_NI2; if (!strcasecmp(swtype, "dms100")) return PRI_SWITCH_DMS100; if (!strcasecmp(swtype, "lucent5e")) return PRI_SWITCH_LUCENT5E; if (!strcasecmp(swtype, "att4ess")) return PRI_SWITCH_ATT4ESS; if (!strcasecmp(swtype, "euroisdn")) return PRI_SWITCH_EUROISDN_E1; if (!strcasecmp(swtype, "gr303eoc")) return PRI_SWITCH_GR303_EOC; if (!strcasecmp(swtype, "gr303tmc")) return PRI_SWITCH_GR303_TMC; return -1; } static void hangup_channel(int channo) { if (chans[channo].pid) { #if 0 printf("Killing channel %d (pid = %d)\n", channo, chans[channo].pid); #endif chans[channo].alreadyhungup = 1; kill(chans[channo].pid, SIGTERM); } else if (chans[channo].needhangup) chans[channo].needhangup = 0; } static int dahdi_open(char *fn) { int fd; int isnum; int chan = 0; int bs; int x; fprintf(stderr, "dahdi open %s\n", fn); isnum = 1; for (x = 0; x < strlen(fn); x++) { if (!isdigit(fn[x])) { isnum = 0; break; } } if (isnum) { chan = atoi(fn); if (chan < 1) { printf("Invalid channel number '%s'\n", fn); exit(1); } fn = "/dev/dahdi/channel"; } fd = open(fn, O_RDWR /* | O_NONBLOCK */); if (fd < 0) { printf("Unable to open '%s': %s\n", fn, strerror(errno)); exit(1); } if (chan) { if (ioctl(fd, DAHDI_SPECIFY, &chan)) { x = errno; close(fd); errno = x; printf("Unable to specify channel %d: %s\n", chan, strerror(errno)); exit(1); } } bs = READ_SIZE; if (ioctl(fd, DAHDI_SET_BLOCKSIZE, &bs) == -1) { printf("Unable to set blocksize '%d': %s\n", bs, strerror(errno)); exit(1); } return fd; } static void launch_channel(int channo) { pid_t pid; int z; char ch[80]; /* Make sure hangup state is reset */ chans[channo].needhangup = 0; chans[channo].alreadyhungup = 0; pid = fork(); if (pid < 0) { fprintf(stderr, "--!! Unable to fork\n"); chans[channo].needhangup = 1; } if (pid) { printf("-- Launching process %d to handle channel %d\n", pid, channo); chans[channo].pid = pid; } else { sprintf(ch, "%d", channo + offset); z = dahdi_open(ch); if (z) { do_channel(z); exit(0); } else { fprintf(stderr, "--!! Unable to open channel %d\n", channo); exit(1); } } } static int get_free_channel(int channo) { channo--; if((channo>MAX_CHAN)||(channo<0)) { fprintf(stderr, "Invalid Bchannel RANGE <%d", channo); return 0; }; while(chans[channo].pid) { channo--; } return channo; } /* place here criteria for completion of destination number */ static int number_incommplete(char *number) { return strlen(number) < 3; } static void start_channel(struct pri *pri, pri_event *e) { int channo = e->ring.channel; int flag = 1; pri_event_ring *ring = &e->ring; if(channo == -1) { channo = e->ring.channel = get_free_channel(MAX_CHAN); if(channo == DCHANNEL_TIMESLOT) channo = e->ring.channel = get_free_channel(MAX_CHAN); fprintf(stdout, "Any channel selected: %d\n", channo); if(!channo) { pri_release(pri, ring->call, PRI_CAUSE_REQUESTED_CHAN_UNAVAIL); fprintf(stdout, "Abort call due to no avl B channels\n"); return; } flag = 0; } /* Make sure it's a valid number */ if ((channo >= MAX_CHAN) || (channo < 0)) { fprintf(stderr, "--!! Channel %d is out of range\n", channo); return; } /* Make sure nothing is there */ if (chans[channo].pid) { fprintf(stderr, "--!! Channel %d still has a call on it, ending it...\n", channo); hangup_channel(channo); /* Wait for it to die */ while(chans[channo].pid) usleep(100); } /* Record call number */ chans[channo].call = e->ring.call; /* Answer the line */ if(flag) { pri_answer(pri, chans[channo].call, channo, 1); } else { pri_need_more_info(pri, chans[channo].call, channo, 1); } /* Launch a process to handle it */ launch_channel(channo); } static void handle_pri_event(struct pri *pri, pri_event *e) { switch(e->e) { case PRI_EVENT_DCHAN_UP: printf("-- D-Channel is now up! :-)\n"); break; case PRI_EVENT_DCHAN_DOWN: printf("-- D-Channel is now down! :-(\n"); break; case PRI_EVENT_RESTART: printf("-- Restarting channel %d\n", e->restart.channel); hangup_channel(e->restart.channel); break; case PRI_EVENT_CONFIG_ERR: printf("-- Configuration error detected: %s\n", e->err.err); break; case PRI_EVENT_RING: printf("-- Ring on channel %d (from %s to %s), answering...\n", e->ring.channel, e->ring.callingnum, e->ring.callednum); start_channel(pri, e); break; case PRI_EVENT_HANGUP: printf("-- Hanging up channel %d\n", e->hangup.channel); hangup_channel(e->hangup.channel); break; case PRI_EVENT_RINGING: case PRI_EVENT_ANSWER: fprintf(stderr, "--!! What? We shouldn't be making any calls...\n"); break; case PRI_EVENT_HANGUP_ACK: /* Ignore */ break; case PRI_EVENT_INFO_RECEIVED: fprintf(stdout, "number is: %s\n", e->ring.callednum); if(!number_incommplete(e->ring.callednum)) { fprintf(stdout, "final number is: %s\n", e->ring.callednum); pri_answer(pri, e->ring.call, 0, 1); } break; default: fprintf(stderr, "--!! Unknown PRI event %d\n", e->e); } } static int run_pri(int dfd, int swtype, int node) { struct pri *pri; pri_event *e; struct timeval tv = {0,0}, *next; fd_set rfds, efds; int res,x; pri = pri_new(dfd, node, swtype); if (!pri) { fprintf(stderr, "Unable to create PRI\n"); return -1; } pri_set_debug(pri, -1); for (;;) { /* Run the D-Channel */ FD_ZERO(&rfds); FD_ZERO(&efds); FD_SET(dfd, &rfds); FD_SET(dfd, &efds); if ((next = pri_schedule_next(pri))) { gettimeofday(&tv, NULL); tv.tv_sec = next->tv_sec - tv.tv_sec; tv.tv_usec = next->tv_usec - tv.tv_usec; if (tv.tv_usec < 0) { tv.tv_usec += 1000000; tv.tv_sec -= 1; } if (tv.tv_sec < 0) { tv.tv_sec = 0; tv.tv_usec = 0; } } res = select(dfd + 1, &rfds, NULL, &efds, next ? &tv : NULL); e = NULL; if (!res) { e = pri_schedule_run(pri); } else if (res > 0) { e = pri_check_event(pri); } else if (errno == ELAST) { res = ioctl(dfd, DAHDI_GETEVENT, &x); printf("Got DAHDI event: %d\n", x); } else if (errno != EINTR) fprintf(stderr, "Error (%d) on select: %s\n", ELAST, strerror(errno)); if (e) { handle_pri_event(pri, e); } res = ioctl(dfd, DAHDI_GETEVENT, &x); if (!res && x) { fprintf(stderr, "Got event on PRI interface: %d\n", x); } /* Check for lines that need hangups */ for (x=0;x [swtypetype] [nodetype]\n"); exit(1); } dfd = open(argv[1], O_RDWR); if (dfd < 0) { fprintf(stderr, "Failed to open dchannel '%s': %s\n", argv[1], strerror(errno)); exit(1); } if (ioctl(dfd, DAHDI_GET_PARAMS, &p)) { fprintf(stderr, "Unable to get parameters on '%s': %s\n", argv[1], strerror(errno)); exit(1); } if ((p.sigtype != DAHDI_SIG_HDLCRAW) && (p.sigtype != DAHDI_SIG_HDLCFCS)) { fprintf(stderr, "%s is in %d signalling, not FCS HDLC or RAW HDLC mode\n", argv[1], p.sigtype); exit(1); } if (argc > 2) { swtype = str2switch(argv[2]); if (swtype < 0) { fprintf(stderr, "Valid switchtypes are: ni2, dms100, lucent5e, att4ess, and euroisdn\n"); exit(1); } } if (argc > 3) { node = str2node(argv[3]); if (node < 0) { fprintf(stderr, "Valid node types are: network and cpe\n"); exit(1); } } signal(SIGCHLD, chan_ended); if (run_pri(dfd, swtype, node)) exit(1); exit(0); return 0; } libpri-1.4.14/rose_qsig_aoc.c0000644000000000000000000016137011173453575014564 0ustar rootroot/* * 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 Advice-Of-Charge (AOC) operations * * SS-AOC-Operations ECMA-212 Annex E Table E.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 Time 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 time Time information to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *rose_enc_qsig_AOC_Time(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseQsigAOCTime *time) { unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, time->length)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, time->scale)); ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \internal * \brief Encode the Amount 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 amount Amount information to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *rose_enc_qsig_AOC_Amount(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseQsigAOCAmount *amount) { unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, amount->currency)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, amount->multiplier)); ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \internal * \brief Encode the RecordedCurrency 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 recorded Recorded currency information to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *rose_enc_qsig_AOC_RecordedCurrency(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseQsigAOCRecordedCurrency *recorded) { unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); ASN1_CALL(pos, asn1_enc_string_max(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, recorded->currency, sizeof(recorded->currency) - 1)); ASN1_CALL(pos, rose_enc_qsig_AOC_Amount(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, &recorded->amount)); ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \internal * \brief Encode the DurationCurrency 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 duration Duration currency information to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *rose_enc_qsig_AOC_DurationCurrency(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseQsigAOCDurationCurrency *duration) { unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); ASN1_CALL(pos, asn1_enc_string_max(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, duration->currency, sizeof(duration->currency) - 1)); ASN1_CALL(pos, rose_enc_qsig_AOC_Amount(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, &duration->amount)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3, duration->charging_type)); ASN1_CALL(pos, rose_enc_qsig_AOC_Time(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 4, &duration->time)); if (duration->granularity_present) { ASN1_CALL(pos, rose_enc_qsig_AOC_Time(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 5, &duration->granularity)); } ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \internal * \brief Encode the FlatRateCurrency 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 flat_rate Flat rate currency information to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *rose_enc_qsig_AOC_FlatRateCurrency(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseQsigAOCFlatRateCurrency *flat_rate) { unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); ASN1_CALL(pos, asn1_enc_string_max(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, flat_rate->currency, sizeof(flat_rate->currency) - 1)); ASN1_CALL(pos, rose_enc_qsig_AOC_Amount(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, &flat_rate->amount)); ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \internal * \brief Encode the VolumeRateCurrency 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 volume_rate Volume rate currency information to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *rose_enc_qsig_AOC_VolumeRateCurrency(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseQsigAOCVolumeRateCurrency *volume_rate) { unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); ASN1_CALL(pos, asn1_enc_string_max(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, volume_rate->currency, sizeof(volume_rate->currency) - 1)); ASN1_CALL(pos, rose_enc_qsig_AOC_Amount(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, &volume_rate->amount)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3, volume_rate->unit)); ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \internal * \brief Encode the AOCSCurrencyInfo 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 currency_info Currency 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_qsig_AOCSCurrencyInfo(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseQsigAOCSCurrencyInfo *currency_info) { unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, currency_info->charged_item)); switch (currency_info->currency_type) { case 0: /* specialChargingCode */ ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, currency_info->u.special_charging_code)); break; case 1: /* durationCurrency */ ASN1_CALL(pos, rose_enc_qsig_AOC_DurationCurrency(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, ¤cy_info->u.duration)); break; case 2: /* flatRateCurrency */ ASN1_CALL(pos, rose_enc_qsig_AOC_FlatRateCurrency(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, ¤cy_info->u.flat_rate)); break; case 3: /* volumeRateCurrency */ ASN1_CALL(pos, rose_enc_qsig_AOC_VolumeRateCurrency(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3, ¤cy_info->u.volume_rate)); break; case 4: /* freeOfCharge */ ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 4)); break; case 5: /* currencyInfoNotAvailable */ ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 5)); break; case 6: /* freeOfChargeFromBeginning */ ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 6)); break; default: ASN1_ENC_ERROR(ctrl, "Unknown currency type"); return NULL; } ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \internal * \brief Encode the AOCSCurrencyInfoList 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 currency_info Currency information list to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *rose_enc_qsig_AOCSCurrencyInfoList(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseQsigAOCSCurrencyInfoList *currency_info) { unsigned index; unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); for (index = 0; index < currency_info->num_records; ++index) { ASN1_CALL(pos, rose_enc_qsig_AOCSCurrencyInfo(ctrl, pos, end, ASN1_TAG_SEQUENCE, ¤cy_info->list[index])); } ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \internal * \brief Encode the ChargingAssociation 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 charging Charging association information to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *rose_enc_qsig_AOC_ChargingAssociation(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct roseQsigAOCChargingAssociation *charging) { unsigned char *explicit_len; switch (charging->type) { case 0: /* charge_identifier */ ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, charging->id)); break; case 1: /* charged_number */ /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0); ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &charging->number)); ASN1_CONSTRUCTED_END(explicit_len, pos, end); break; default: ASN1_ENC_ERROR(ctrl, "Unknown ChargingAssociation type"); return NULL; } return pos; } /*! * \brief Encode the Q.SIG ChargeRequest 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_ChargeRequest_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { unsigned index; unsigned char *seq_len; unsigned char *advice_len; const struct roseQsigChargeRequestArg_ARG *charge_request; charge_request = &args->qsig.ChargeRequest; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); /* SEQUENCE SIZE(0..7) OF AdviceModeCombination */ ASN1_CONSTRUCTED_BEGIN(advice_len, pos, end, ASN1_TAG_SEQUENCE); for (index = 0; index < charge_request->num_records; ++index) { ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, charge_request->advice_mode_combinations[index])); } ASN1_CONSTRUCTED_END(advice_len, pos, end); /* No extension to encode */ ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the Q.SIG ChargeRequest 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_ChargeRequest_RES(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args) { unsigned char *seq_len; const struct roseQsigChargeRequestRes_RES *charge_request; charge_request = &args->qsig.ChargeRequest; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, charge_request->advice_mode_combination)); /* No extension to encode */ ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the Q.SIG AocFinal 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_AocFinal_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { unsigned char *seq_len; unsigned char *specific_len; const struct roseQsigAocFinalArg_ARG *aoc_final; aoc_final = &args->qsig.AocFinal; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); switch (aoc_final->type) { case 0: /* charge_not_available */ ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0)); break; case 1: /* free_of_charge */ ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1)); break; case 2: /* specific_currency */ ASN1_CONSTRUCTED_BEGIN(specific_len, pos, end, ASN1_TAG_SEQUENCE); ASN1_CALL(pos, rose_enc_qsig_AOC_RecordedCurrency(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, &aoc_final->specific.recorded)); if (aoc_final->specific.billing_id_present) { ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, aoc_final->specific.billing_id)); } ASN1_CONSTRUCTED_END(specific_len, pos, end); break; default: ASN1_ENC_ERROR(ctrl, "Unknown AocFinal type"); return NULL; } if (aoc_final->charging_association_present) { ASN1_CALL(pos, rose_enc_qsig_AOC_ChargingAssociation(ctrl, pos, end, &aoc_final->charging_association)); } /* No extension to encode */ ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the Q.SIG AocInterim 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_AocInterim_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { unsigned char *seq_len; unsigned char *specific_len; const struct roseQsigAocInterimArg_ARG *aoc_interim; aoc_interim = &args->qsig.AocInterim; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); switch (aoc_interim->type) { case 0: /* charge_not_available */ ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0)); break; case 1: /* free_of_charge */ ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1)); break; case 2: /* specific_currency */ ASN1_CONSTRUCTED_BEGIN(specific_len, pos, end, ASN1_TAG_SEQUENCE); ASN1_CALL(pos, rose_enc_qsig_AOC_RecordedCurrency(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, &aoc_interim->specific.recorded)); if (aoc_interim->specific.billing_id_present) { ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, aoc_interim->specific.billing_id)); } ASN1_CONSTRUCTED_END(specific_len, pos, end); break; default: ASN1_ENC_ERROR(ctrl, "Unknown AocInterim type"); return NULL; } /* No extension to encode */ ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the Q.SIG AocRate 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_AocRate_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { unsigned char *seq_len; const struct roseQsigAocRateArg_ARG *aoc_rate; aoc_rate = &args->qsig.AocRate; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); switch (aoc_rate->type) { case 0: /* charge_not_available */ ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_TYPE_NULL)); break; case 1: /* currency_info_list */ ASN1_CALL(pos, rose_enc_qsig_AOCSCurrencyInfoList(ctrl, pos, end, ASN1_TAG_SEQUENCE, &aoc_rate->currency_info)); break; default: ASN1_ENC_ERROR(ctrl, "Unknown AocRate type"); return NULL; } /* No extension to encode */ ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the Q.SIG AocComplete 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_AocComplete_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { unsigned char *seq_len; const struct roseQsigAocCompleteArg_ARG *aoc_complete; aoc_complete = &args->qsig.AocComplete; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &aoc_complete->charged_user_number)); if (aoc_complete->charging_association_present) { ASN1_CALL(pos, rose_enc_qsig_AOC_ChargingAssociation(ctrl, pos, end, &aoc_complete->charging_association)); } /* No extension to encode */ ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the Q.SIG AocComplete 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_AocComplete_RES(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args) { unsigned char *seq_len; const struct roseQsigAocCompleteRes_RES *aoc_complete; aoc_complete = &args->qsig.AocComplete; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, aoc_complete->charging_option)); /* No extension to encode */ ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the Q.SIG AocDivChargeReq 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_AocDivChargeReq_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) { unsigned char *seq_len; const struct roseQsigAocDivChargeReqArg_ARG *aoc_div_charge_req; aoc_div_charge_req = &args->qsig.AocDivChargeReq; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &aoc_div_charge_req->diverting_user_number)); if (aoc_div_charge_req->charging_association_present) { ASN1_CALL(pos, rose_enc_qsig_AOC_ChargingAssociation(ctrl, pos, end, &aoc_div_charge_req->charging_association)); } ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, aoc_div_charge_req->diversion_type)); /* No extension to encode */ ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \internal * \brief Decode the Time type. * * \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 time 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_AOC_Time(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseQsigAOCTime *time) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s Time %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_CLASS_CONTEXT_SPECIFIC | 1); ASN1_CALL(pos, asn1_dec_int(ctrl, "lengthOfTimeUnit", tag, pos, seq_end, &value)); time->length = value; ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 2); ASN1_CALL(pos, asn1_dec_int(ctrl, "scale", tag, pos, seq_end, &value)); time->scale = value; ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \internal * \brief Decode the Amount type. * * \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 amount 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_AOC_Amount(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseQsigAOCAmount *amount) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s Amount %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_CLASS_CONTEXT_SPECIFIC | 1); ASN1_CALL(pos, asn1_dec_int(ctrl, "currencyAmount", tag, pos, seq_end, &value)); amount->currency = value; ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 2); ASN1_CALL(pos, asn1_dec_int(ctrl, "multiplier", tag, pos, seq_end, &value)); amount->multiplier = value; ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \internal * \brief Decode the RecordedCurrency type. * * \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 recorded 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_AOC_RecordedCurrency(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseQsigAOCRecordedCurrency *recorded) { int length; int seq_offset; const unsigned char *seq_end; size_t str_len; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s RecordedCurrency %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 & ~ASN1_PC_MASK, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 1); ASN1_CALL(pos, asn1_dec_string_max(ctrl, "rCurrency", tag, pos, seq_end, sizeof(recorded->currency), recorded->currency, &str_len)); ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2); ASN1_CALL(pos, rose_dec_qsig_AOC_Amount(ctrl, "rAmount", tag, pos, seq_end, &recorded->amount)); ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \internal * \brief Decode the DurationCurrency type. * * \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 duration 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_AOC_DurationCurrency(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseQsigAOCDurationCurrency *duration) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; size_t str_len; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s DurationCurrency %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 & ~ASN1_PC_MASK, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 1); ASN1_CALL(pos, asn1_dec_string_max(ctrl, "dCurrency", tag, pos, seq_end, sizeof(duration->currency), duration->currency, &str_len)); ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2); ASN1_CALL(pos, rose_dec_qsig_AOC_Amount(ctrl, "dAmount", tag, pos, seq_end, &duration->amount)); ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 3); ASN1_CALL(pos, asn1_dec_int(ctrl, "dChargingType", tag, pos, seq_end, &value)); duration->charging_type = value; ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 4); ASN1_CALL(pos, rose_dec_qsig_AOC_Time(ctrl, "dTime", tag, pos, seq_end, &duration->time)); 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_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 5); ASN1_CALL(pos, rose_dec_qsig_AOC_Time(ctrl, "dGranularity", tag, pos, seq_end, &duration->granularity)); duration->granularity_present = 1; } else { duration->granularity_present = 0; } ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \internal * \brief Decode the FlatRateCurrency type. * * \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 flat_rate 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_AOC_FlatRateCurrency(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseQsigAOCFlatRateCurrency *flat_rate) { int length; int seq_offset; const unsigned char *seq_end; size_t str_len; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s FlatRateCurrency %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 & ~ASN1_PC_MASK, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 1); ASN1_CALL(pos, asn1_dec_string_max(ctrl, "fRCurrency", tag, pos, seq_end, sizeof(flat_rate->currency), flat_rate->currency, &str_len)); ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2); ASN1_CALL(pos, rose_dec_qsig_AOC_Amount(ctrl, "fRAmount", tag, pos, seq_end, &flat_rate->amount)); ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \internal * \brief Decode the VolumeRateCurrency type. * * \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 volume_rate 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_AOC_VolumeRateCurrency(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseQsigAOCVolumeRateCurrency *volume_rate) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; size_t str_len; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s VolumeRateCurrency %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 & ~ASN1_PC_MASK, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 1); ASN1_CALL(pos, asn1_dec_string_max(ctrl, "vRCurrency", tag, pos, seq_end, sizeof(volume_rate->currency), volume_rate->currency, &str_len)); ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2); ASN1_CALL(pos, rose_dec_qsig_AOC_Amount(ctrl, "vRAmount", tag, pos, seq_end, &volume_rate->amount)); ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 3); ASN1_CALL(pos, asn1_dec_int(ctrl, "vRVolumeUnit", tag, pos, seq_end, &value)); volume_rate->unit = value; ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \internal * \brief Decode the AOCSCurrencyInfo type. * * \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 currency_info 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_AOCSCurrencyInfo(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseQsigAOCSCurrencyInfo *currency_info) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s AOCSCurrencyInfo %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, "chargedItem", tag, pos, seq_end, &value)); currency_info->charged_item = value; ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); switch (tag) { case ASN1_TYPE_INTEGER: currency_info->currency_type = 0; /* specialChargingCode */ ASN1_CALL(pos, asn1_dec_int(ctrl, "specialChargingCode", tag, pos, seq_end, &value)); currency_info->u.special_charging_code = value; break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1: currency_info->currency_type = 1; /* durationCurrency */ ASN1_CALL(pos, rose_dec_qsig_AOC_DurationCurrency(ctrl, "durationCurrency", tag, pos, seq_end, ¤cy_info->u.duration)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2: currency_info->currency_type = 2; /* flatRateCurrency */ ASN1_CALL(pos, rose_dec_qsig_AOC_FlatRateCurrency(ctrl, "flatRateCurrency", tag, pos, seq_end, ¤cy_info->u.flat_rate)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 3: currency_info->currency_type = 3; /* volumeRateCurrency */ ASN1_CALL(pos, rose_dec_qsig_AOC_VolumeRateCurrency(ctrl, "volumeRateCurrency", tag, pos, seq_end, ¤cy_info->u.volume_rate)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 4: currency_info->currency_type = 4; /* freeOfCharge */ ASN1_CALL(pos, asn1_dec_null(ctrl, "freeOfCharge", tag, pos, seq_end)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 5: currency_info->currency_type = 5; /* currencyInfoNotAvailable */ ASN1_CALL(pos, asn1_dec_null(ctrl, "currencyInfoNotAvailable", tag, pos, seq_end)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 6: currency_info->currency_type = 6; /* freeOfChargeFromBeginning */ ASN1_CALL(pos, asn1_dec_null(ctrl, "freeOfChargeFromBeginning", tag, pos, seq_end)); break; default: ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); return NULL; } ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \internal * \brief Decode the AOCSCurrencyInfoList type. * * \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 currency_info 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_AOCSCurrencyInfoList(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseQsigAOCSCurrencyInfoList *currency_info) { int length; int seq_offset; const unsigned char *seq_end; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s AOCSCurrencyInfoList %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); currency_info->num_records = 0; while (pos < seq_end && *pos != ASN1_INDEF_TERM) { if (currency_info->num_records < ARRAY_LEN(currency_info->list)) { ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); ASN1_CALL(pos, rose_dec_qsig_AOCSCurrencyInfo(ctrl, "listEntry", tag, pos, seq_end, ¤cy_info->list[currency_info->num_records])); ++currency_info->num_records; } else { /* Too many records */ return NULL; } } ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \internal * \brief Decode the ChargingAssociation type. * * \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 charging 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_AOC_ChargingAssociation(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseQsigAOCChargingAssociation *charging) { int32_t value; int length; int explicit_offset; const unsigned char *explicit_end; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s ChargingAssociation\n", name); } switch (tag) { case ASN1_TYPE_INTEGER: charging->type = 0; /* charge_identifier */ ASN1_CALL(pos, asn1_dec_int(ctrl, "chargeIdentifier", tag, pos, end, &value)); charging->id = value; break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 0: charging->type = 1; /* charged_number */ /* 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, end, &length)); ASN1_END_SETUP(explicit_end, explicit_offset, length, pos, end); ASN1_CALL(pos, asn1_dec_tag(pos, explicit_end, &tag)); ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "chargedNumber", tag, pos, explicit_end, &charging->number)); ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, end); break; default: ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); return NULL; } return pos; } /*! * \brief Decode the Q.SIG ChargeRequest 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_ChargeRequest_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { int32_t value; int length; int seq_offset; int advice_offset; const unsigned char *seq_end; const unsigned char *advice_end; struct roseQsigChargeRequestArg_ARG *charge_request; charge_request = &args->qsig.ChargeRequest; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " ChargeRequest %s\n", asn1_tag2str(tag)); } ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); /* SEQUENCE SIZE(0..7) OF AdviceModeCombination */ ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " adviceModeCombinations %s\n", asn1_tag2str(tag)); } ASN1_CALL(pos, asn1_dec_length(pos, seq_end, &length)); ASN1_END_SETUP(advice_end, advice_offset, length, pos, seq_end); /* Decode SIZE(0..7) OF AdviceModeCombination */ charge_request->num_records = 0; while (pos < advice_end && *pos != ASN1_INDEF_TERM) { if (charge_request->num_records < ARRAY_LEN(charge_request->advice_mode_combinations)) { ASN1_CALL(pos, asn1_dec_tag(pos, advice_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); ASN1_CALL(pos, asn1_dec_int(ctrl, "adviceModeCombination", tag, pos, advice_end, &value)); charge_request->advice_mode_combinations[charge_request->num_records] = value; ++charge_request->num_records; } else { /* Too many records */ return NULL; } } ASN1_END_FIXUP(ctrl, pos, advice_offset, advice_end, seq_end); /* Fixup will skip over any OPTIONAL manufacturer extension information */ ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the Q.SIG ChargeRequest 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_ChargeRequest_RES(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; struct roseQsigChargeRequestRes_RES *charge_request; charge_request = &args->qsig.ChargeRequest; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " ChargeRequest %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_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); ASN1_CALL(pos, asn1_dec_int(ctrl, "adviceModeCombination", tag, pos, seq_end, &value)); charge_request->advice_mode_combination = value; /* Fixup will skip over any OPTIONAL manufacturer extension information */ ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the Q.SIG AocFinal 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_AocFinal_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { int32_t value; int length; int seq_offset; int specific_offset; const unsigned char *seq_end; const unsigned char *specific_end; const unsigned char *save_pos; struct roseQsigAocFinalArg_ARG *aoc_final; aoc_final = &args->qsig.AocFinal; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " AocFinal %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)); switch (tag) { case ASN1_CLASS_CONTEXT_SPECIFIC | 0: aoc_final->type = 0; /* charge_not_available */ ASN1_CALL(pos, asn1_dec_null(ctrl, "chargeNotAvailable", tag, pos, seq_end)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 1: aoc_final->type = 1; /* free_of_charge */ ASN1_CALL(pos, asn1_dec_null(ctrl, "freeOfCharge", tag, pos, seq_end)); break; case ASN1_TAG_SEQUENCE: aoc_final->type = 2; /* specific_currency */ if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " specificCurrency %s\n", asn1_tag2str(tag)); } ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); ASN1_END_SETUP(specific_end, specific_offset, length, pos, seq_end); ASN1_CALL(pos, asn1_dec_tag(pos, specific_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1); ASN1_CALL(pos, rose_dec_qsig_AOC_RecordedCurrency(ctrl, "recordedCurrency", tag, pos, specific_end, &aoc_final->specific.recorded)); if (pos < specific_end && *pos != ASN1_INDEF_TERM) { ASN1_CALL(pos, asn1_dec_tag(pos, specific_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 2); ASN1_CALL(pos, asn1_dec_int(ctrl, "finalBillingId", tag, pos, specific_end, &value)); aoc_final->specific.billing_id = value; aoc_final->specific.billing_id_present = 1; } else { aoc_final->specific.billing_id_present = 0; } ASN1_END_FIXUP(ctrl, pos, specific_offset, specific_end, seq_end); break; default: ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); return NULL; } /* * A sequence specifies an ordered list of component types. * However, for simplicity we are not checking the order of * the remaining optional components. */ aoc_final->charging_association_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 | 0: case ASN1_TYPE_INTEGER: ASN1_CALL(pos, rose_dec_qsig_AOC_ChargingAssociation(ctrl, "chargingAssociation", tag, pos, seq_end, &aoc_final->charging_association)); aoc_final->charging_association_present = 1; break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1: case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2: if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " finalArgExtension %s\n", asn1_tag2str(tag)); } /* Fixup will skip over the manufacturer extension information */ 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 AocInterim 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_AocInterim_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { int32_t value; int length; int seq_offset; int specific_offset; const unsigned char *seq_end; const unsigned char *specific_end; struct roseQsigAocInterimArg_ARG *aoc_interim; aoc_interim = &args->qsig.AocInterim; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " AocInterim %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)); switch (tag) { case ASN1_CLASS_CONTEXT_SPECIFIC | 0: aoc_interim->type = 0; /* charge_not_available */ ASN1_CALL(pos, asn1_dec_null(ctrl, "chargeNotAvailable", tag, pos, seq_end)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 1: aoc_interim->type = 1; /* free_of_charge */ ASN1_CALL(pos, asn1_dec_null(ctrl, "freeOfCharge", tag, pos, seq_end)); break; case ASN1_TAG_SEQUENCE: aoc_interim->type = 2; /* specific_currency */ if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " specificCurrency %s\n", asn1_tag2str(tag)); } ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); ASN1_END_SETUP(specific_end, specific_offset, length, pos, seq_end); ASN1_CALL(pos, asn1_dec_tag(pos, specific_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1); ASN1_CALL(pos, rose_dec_qsig_AOC_RecordedCurrency(ctrl, "recordedCurrency", tag, pos, specific_end, &aoc_interim->specific.recorded)); if (pos < specific_end && *pos != ASN1_INDEF_TERM) { ASN1_CALL(pos, asn1_dec_tag(pos, specific_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 2); ASN1_CALL(pos, asn1_dec_int(ctrl, "interimBillingId", tag, pos, specific_end, &value)); aoc_interim->specific.billing_id = value; aoc_interim->specific.billing_id_present = 1; } else { aoc_interim->specific.billing_id_present = 0; } ASN1_END_FIXUP(ctrl, pos, specific_offset, specific_end, seq_end); break; default: ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); return NULL; } /* Fixup will skip over any OPTIONAL manufacturer extension information */ ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the Q.SIG AocRate 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_AocRate_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { int length; int seq_offset; const unsigned char *seq_end; struct roseQsigAocRateArg_ARG *aoc_rate; aoc_rate = &args->qsig.AocRate; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " AocRate %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)); switch (tag) { case ASN1_TYPE_NULL: aoc_rate->type = 0; /* charge_not_available */ ASN1_CALL(pos, asn1_dec_null(ctrl, "chargeNotAvailable", tag, pos, seq_end)); break; case ASN1_TAG_SEQUENCE: aoc_rate->type = 1; /* currency_info_list */ ASN1_CALL(pos, rose_dec_qsig_AOCSCurrencyInfoList(ctrl, "aocSCurrencyInfoList", tag, pos, seq_end, &aoc_rate->currency_info)); break; default: ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); return NULL; } /* Fixup will skip over any OPTIONAL manufacturer extension information */ ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the Q.SIG AocComplete 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_AocComplete_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { int length; int seq_offset; const unsigned char *seq_end; const unsigned char *save_pos; struct roseQsigAocCompleteArg_ARG *aoc_complete; aoc_complete = &args->qsig.AocComplete; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " AocComplete %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, "chargedUser", tag, pos, seq_end, &aoc_complete->charged_user_number)); /* * A sequence specifies an ordered list of component types. * However, for simplicity we are not checking the order of * the remaining optional components. */ aoc_complete->charging_association_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 | 0: case ASN1_TYPE_INTEGER: ASN1_CALL(pos, rose_dec_qsig_AOC_ChargingAssociation(ctrl, "chargingAssociation", tag, pos, seq_end, &aoc_complete->charging_association)); aoc_complete->charging_association_present = 1; break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1: case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2: if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " completeArgExtension %s\n", asn1_tag2str(tag)); } /* Fixup will skip over the manufacturer extension information */ 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 AocComplete 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_AocComplete_RES(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; struct roseQsigAocCompleteRes_RES *aoc_complete; aoc_complete = &args->qsig.AocComplete; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " AocComplete %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_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); ASN1_CALL(pos, asn1_dec_int(ctrl, "chargingOption", tag, pos, seq_end, &value)); aoc_complete->charging_option = value; /* Fixup will skip over any OPTIONAL manufacturer extension information */ ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the Q.SIG AocDivChargeReq 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_AocDivChargeReq_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; struct roseQsigAocDivChargeReqArg_ARG *aoc_div_charge_req; aoc_div_charge_req = &args->qsig.AocDivChargeReq; ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " AocDivChargeReq %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, "divertingUser", tag, pos, seq_end, &aoc_div_charge_req->diverting_user_number)); ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); switch (tag) { case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 0: case ASN1_TYPE_INTEGER: ASN1_CALL(pos, rose_dec_qsig_AOC_ChargingAssociation(ctrl, "chargingAssociation", tag, pos, seq_end, &aoc_div_charge_req->charging_association)); aoc_div_charge_req->charging_association_present = 1; ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); break; default: aoc_div_charge_req->charging_association_present = 0; break; } ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); ASN1_CALL(pos, asn1_dec_int(ctrl, "diversionType", tag, pos, seq_end, &value)); aoc_div_charge_req->diversion_type = value; /* Fixup will skip over any OPTIONAL manufacturer extension information */ ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /* ------------------------------------------------------------------- */ /* end rose_qsig_aoc.c */ libpri-1.4.14/libpri-1.4.14-summary.txt0000644000000000000000000001014312064640402016015 0ustar rootroot Release Summary libpri-1.4.14 Date: 2012-12-20 ---------------------------------------------------------------------- Table of Contents 1. Summary 2. Contributors 3. Closed Issues 4. Other Changes 5. Diffstat ---------------------------------------------------------------------- Summary [Back to Top] This release includes only bug fixes. The changes included were made only to address problems that have been identified in this release series. Users should be able to safely upgrade to this version if this release series is already in use. Users considering upgrading from a previous release series are strongly encouraged to review the UPGRADE.txt document as well as the CHANGES document for information about upgrading to this release series. The data in this summary reflects changes that have been made since the previous release, libpri-1.4.13. ---------------------------------------------------------------------- Contributors [Back to Top] This table lists the people who have submitted code, those that have tested patches, as well as those that reported issues on the issue tracker that were resolved in this release. For coders, the number is how many of their patches (of any size) were committed into this release. For testers, the number is the number of times their name was listed as assisting with testing a patch. Finally, for reporters, the number is the number of issues that they reported that were closed by commits that went into this release. Coders Testers Reporters 2 rmudgett 2 tzafrir 2 Tzafrir Cohen 1 nmerrett ---------------------------------------------------------------------- Closed Issues [Back to Top] This is a list of all issues from the issue tracker that were closed by changes that went into this release. Category: General PRI-145: Unchecked write in pritest Revision: 2305 Reporter: tzafrir Coders: Tzafrir Cohen PRI-147: Normal Clearing in Progress Message is ignored Revision: 2308 Reporter: nmerrett Coders: rmudgett ---------------------------------------------------------------------- Commits Not Associated with an Issue [Back to Top] This is a list of all changes that went into this release that did not directly close an issue from the issue tracker. The commits may have been marked as being related to an issue. If that is the case, the issue numbers are listed here, as well. +------------------------------------------------------------------------+ | Revision | Author | Summary | Issues | | | | | Referenced | |----------+----------+-------------------------------------+------------| | 2307 | rmudgett | Fix compiler error with | | | | | ALERTING_NO_PROGRESS define. | | +------------------------------------------------------------------------+ ---------------------------------------------------------------------- Diffstat Results [Back to Top] This is a summary of the changes to the source code that went into this release that was generated using the diffstat utility. pritest.c | 5 +++ q931.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 71 insertions(+), 13 deletions(-) ---------------------------------------------------------------------- libpri-1.4.14/Makefile0000644000000000000000000001463512030621517013226 0ustar rootroot# # libpri: An implementation of Primary Rate ISDN # # Written by Mark Spencer # # Copyright (C) 2001, Linux Support Services, Inc. # All Rights Reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # # Uncomment if you want libpri not send PROGRESS_INDICATOR w/ALERTING #ALERTING=-DALERTING_NO_PROGRESS CC=gcc GREP=grep AWK=awk OSARCH=$(shell uname -s) PROC?=$(shell uname -m) # SONAME version; should be changed on every ABI change # please don't change it needlessly; it's perfectly fine to have a SONAME # of 1.2 and a version of 1.4.x SONAME:=1.4 STATIC_LIBRARY=libpri.a DYNAMIC_LIBRARY:=libpri.so.$(SONAME) STATIC_OBJS= \ copy_string.o \ pri.o \ q921.o \ prisched.o \ q931.o \ pri_aoc.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_etsi_mwi.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 \ rose_qsig_name.o \ version.o DYNAMIC_OBJS= \ $(STATIC_OBJS) CFLAGS ?= -g CFLAGS += -Wall -Werror -Wstrict-prototypes -Wmissing-prototypes CFLAGS += -fPIC $(ALERTING) $(LIBPRI_OPT) $(COVERAGE_CFLAGS) INSTALL_PREFIX=$(DESTDIR) INSTALL_BASE=/usr libdir?=$(INSTALL_BASE)/lib ifneq ($(findstring Darwin,$(OSARCH)),) SOFLAGS=$(LDFLAGS) -dynamic -bundle -Xlinker -macosx_version_min -Xlinker 10.4 -Xlinker -undefined -Xlinker dynamic_lookup -force_flat_namespace ifeq ($(shell /usr/bin/sw_vers -productVersion | cut -c1-4),10.6) SOFLAGS+=/usr/lib/bundle1.o endif LDCONFIG=/usr/bin/true else SOFLAGS=$(LDFLAGS) -shared -Wl,-h$(DYNAMIC_LIBRARY) $(COVERAGE_LDFLAGS) LDCONFIG = /sbin/ldconfig endif ifneq (,$(findstring X$(OSARCH)X, XLinuxX XGNU/kFreeBSDX XGNUX)) LDCONFIG_FLAGS=-n else ifeq (${OSARCH},FreeBSD) LDCONFIG_FLAGS=-m CFLAGS += -I../zaptel -I../zapata INSTALL_BASE=/usr/local endif endif ifeq (${OSARCH},SunOS) CFLAGS += -DSOLARIS -I../zaptel-solaris LDCONFIG = LDCONFIG_FLAGS = \# # Trick to comment out the period in the command below #INSTALL_PREFIX = /opt/asterisk # Uncomment out to install in standard Solaris location for 3rd party code endif UTILITIES= pridump pritest rosetest testprilib export PRIVERSION PRIVERSION:=$(shell GREP=$(GREP) AWK=$(AWK) build_tools/make_version .) #The problem with sparc is the best stuff is in newer versions of gcc (post 3.0) only. #This works for even old (2.96) versions of gcc and provides a small boost either way. #A ultrasparc cpu is really v9 but the stock debian stable 3.0 gcc doesnt support it. ifeq ($(PROC),sparc64) PROC=ultrasparc LIBPRI_OPT = -mtune=$(PROC) -O3 -pipe -fomit-frame-pointer -mcpu=v8 else ifneq ($(CODE_COVERAGE),) LIBPRI_OPT= COVERAGE_CFLAGS=-ftest-coverage -fprofile-arcs COVERAGE_LDFLAGS=-ftest-coverage -fprofile-arcs else LIBPRI_OPT=-O2 endif endif ifeq ($(CPUARCH),i686) CFLAGS += -m32 SOFLAGS += -m32 endif all: $(STATIC_LIBRARY) $(DYNAMIC_LIBRARY) $(UTILITIES) update: @if [ -d .svn ]; then \ echo "Updating from Subversion..." ; \ fromrev="`svn info | $(AWK) '/Revision: / {print $$2}'`"; \ svn update | tee update.out; \ torev="`svn info | $(AWK) '/Revision: / {print $$2}'`"; \ echo "`date` Updated from revision $${fromrev} to $${torev}." >> update.log; \ rm -f .version; \ if [ `grep -c ^C update.out` -gt 0 ]; then \ echo ; echo "The following files have conflicts:" ; \ grep ^C update.out | cut -b4- ; \ fi ; \ rm -f update.out; \ else \ echo "Not under version control"; \ fi install: $(STATIC_LIBRARY) $(DYNAMIC_LIBRARY) mkdir -p $(INSTALL_PREFIX)$(libdir) mkdir -p $(INSTALL_PREFIX)$(INSTALL_BASE)/include ifneq (${OSARCH},SunOS) install -m 644 libpri.h $(INSTALL_PREFIX)$(INSTALL_BASE)/include install -m 755 $(DYNAMIC_LIBRARY) $(INSTALL_PREFIX)$(libdir) #if [ -x /usr/sbin/sestatus ] && ( /usr/sbin/sestatus | grep "SELinux status:" | grep -q "enabled"); then /sbin/restorecon -v $(INSTALL_PREFIX)$(libdir)/$(DYNAMIC_LIBRARY); fi ( cd $(INSTALL_PREFIX)$(libdir) ; ln -sf $(DYNAMIC_LIBRARY) libpri.so) install -m 644 $(STATIC_LIBRARY) $(INSTALL_PREFIX)$(libdir) if test $$(id -u) = 0; then $(LDCONFIG) $(LDCONFIG_FLAGS) $(INSTALL_PREFIX)$(libdir); fi else install -f $(INSTALL_PREFIX)$(INSTALL_BASE)/include -m 644 libpri.h install -f $(INSTALL_PREFIX)$(libdir) -m 755 $(DYNAMIC_LIBRARY) ( cd $(INSTALL_PREFIX)$(libdir) ; ln -sf $(DYNAMIC_LIBRARY) libpri.so) install -f $(INSTALL_PREFIX)$(libdir) -m 644 $(STATIC_LIBRARY) endif uninstall: @echo "Removing Libpri" rm -f $(INSTALL_PREFIX)$(libdir)/$(STATIC_LIBRARY) rm -f $(INSTALL_PREFIX)$(libdir)/libpri.so rm -f $(INSTALL_PREFIX)$(libdir)/$(DYNAMIC_LIBRARY) rm -f $(INSTALL_PREFIX)$(INSTALL_BASE)/include/libpri.h pritest: pritest.o $(STATIC_LIBRARY) $(CC) -o $@ $< $(STATIC_LIBRARY) $(CFLAGS) testprilib.o: testprilib.c $(CC) $(CFLAGS) -D_REENTRANT -D_GNU_SOURCE $(MAKE_DEPS) -c -o $@ $< testprilib: testprilib.o $(STATIC_LIBRARY) $(CC) -o $@ $< $(STATIC_LIBRARY) -lpthread $(CFLAGS) pridump: pridump.o $(DYNAMIC_LIBRARY) $(CC) -o $@ $< -L. -lpri $(CFLAGS) rosetest: rosetest.o $(STATIC_LIBRARY) $(CC) -o $@ $< $(STATIC_LIBRARY) $(CFLAGS) MAKE_DEPS= -MD -MT $@ -MF .$(subst /,_,$@).d -MP %.o: %.c $(CC) $(CFLAGS) $(MAKE_DEPS) -c -o $@ $< %.lo: %.c $(CC) $(CFLAGS) $(MAKE_DEPS) -c -o $@ $< $(STATIC_LIBRARY): $(STATIC_OBJS) ar rcs $(STATIC_LIBRARY) $(STATIC_OBJS) ranlib $(STATIC_LIBRARY) $(DYNAMIC_LIBRARY): $(DYNAMIC_OBJS) $(CC) $(SOFLAGS) -o $@ $(DYNAMIC_OBJS) $(LDCONFIG) $(LDCONFIG_FLAGS) . ln -sf $(DYNAMIC_LIBRARY) libpri.so version.c: FORCE @build_tools/make_version_c > $@.tmp @cmp -s $@.tmp $@ || mv $@.tmp $@ @rm -f $@.tmp clean: rm -f *.o *.so *.lo rm -f $(STATIC_LIBRARY) $(DYNAMIC_LIBRARY) rm -f $(UTILITIES) rm -f .*.d .PHONY: FORCE: ifneq ($(wildcard .*.d),) include .*.d endif libpri-1.4.14/copy_string.c0000644000000000000000000000246011046150644014267 0ustar rootroot/* * libpri: An implementation of Primary Rate ISDN * * Written by Mark Spencer * * Copyright (C) 2005, Digium, Inc. * All Rights Reserved. */ /* * 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. */ #include #include #include #include "compiler.h" #include "libpri.h" #include "pri_internal.h" void libpri_copy_string(char *dst, const char *src, size_t size) { while (*src && size) { *dst++ = *src++; size--; } if (__builtin_expect(!size, 0)) dst--; *dst = '\0'; } libpri-1.4.14/pri_facility.h0000644000000000000000000003121211504155232014404 0ustar rootroot/* This file contains all data structures and definitions associated with facility message usage and the ROSE components included within those messages. by Matthew Fredrickson Copyright (C) Digium, Inc. 2004-2005 */ /* * 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. */ #ifndef _PRI_FACILITY_H #define _PRI_FACILITY_H #include "pri_q931.h" #include "rose.h" /* Protocol Profile field */ #define Q932_PROTOCOL_MASK 0x1F #define Q932_PROTOCOL_ROSE 0x11 /* X.219 & X.229 */ #define Q932_PROTOCOL_CMIP 0x12 /* Q.941 */ #define Q932_PROTOCOL_ACSE 0x13 /* X.217 & X.227 */ #define Q932_PROTOCOL_GAT 0x16 #define Q932_PROTOCOL_EXTENSIONS 0x1F /* Q.952 Divert cause */ #define Q952_DIVERT_REASON_UNKNOWN 0x00 #define Q952_DIVERT_REASON_CFU 0x01 #define Q952_DIVERT_REASON_CFB 0x02 #define Q952_DIVERT_REASON_CFNR 0x03 #define Q952_DIVERT_REASON_CD 0x04 #define Q952_DIVERT_REASON_IMMEDIATE 0x05 /* Q.SIG Divert cause. Listed in ECMA-174 */ #define QSIG_DIVERT_REASON_UNKNOWN 0x00 /* Call forward unknown reason */ #define QSIG_DIVERT_REASON_CFU 0x01 /* Call Forward Unconditional (other reason) */ #define QSIG_DIVERT_REASON_CFB 0x02 /* Call Forward Busy */ #define QSIG_DIVERT_REASON_CFNR 0x03 /* Call Forward No Reply */ /* Q.932 Type of number */ #define Q932_TON_UNKNOWN 0x00 #define Q932_TON_INTERNATIONAL 0x01 #define Q932_TON_NATIONAL 0x02 #define Q932_TON_NET_SPECIFIC 0x03 #define Q932_TON_SUBSCRIBER 0x04 #define Q932_TON_ABBREVIATED 0x06 /* Q.SIG Subscription Option. Listed in ECMA-174 */ #define QSIG_NO_NOTIFICATION 0x00 #define QSIG_NOTIFICATION_WITHOUT_DIVERTED_TO_NR 0x01 #define QSIG_NOTIFICATION_WITH_DIVERTED_TO_NR 0x02 /*! Reasons an APDU callback is called. */ enum APDU_CALLBACK_REASON { /*! * \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. */ APDU_CALLBACK_REASON_ERROR, /*! * \brief Abort and cleanup. * \note The APDU queue is being destroyed. * \note The callback cannot generate an event subcmd. * \note The callback cannot send messages as the call is likely being destroyed. */ APDU_CALLBACK_REASON_CLEANUP, /*! * \brief Timeout waiting for responses to the message. * \note The callback can generate an event subcmd. * \note The callback can send messages. */ APDU_CALLBACK_REASON_TIMEOUT, /*! * \brief Received a facility response message. * \note The callback can generate an event subcmd. * \note The callback can send messages. */ APDU_CALLBACK_REASON_MSG_RESULT, /*! * \brief Received a facility error message. * \note The callback can generate an event subcmd. * \note The callback can send messages. */ APDU_CALLBACK_REASON_MSG_ERROR, /*! * \brief Received a facility reject message. * \note The callback can generate an event subcmd. * \note The callback can send messages. */ APDU_CALLBACK_REASON_MSG_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 { void *ptr; long value; char pad[8]; }; /* So calls to pri_call_apdu_find() will not find an aliased event. */ #define APDU_INVALID_INVOKE_ID 0x10000 #define APDU_TIMEOUT_MSGS_ONLY -1 struct apdu_callback_data { /*! APDU invoke id to match with any response messages. (Result/Error/Reject) */ int invoke_id; /*! * \brief Time to wait for responses to APDU in ms. * \note Set to 0 if send the message only. * \note Set to APDU_TIMEOUT_MSGS_ONLY to "timeout" with the message_type list only. */ int timeout_time; /*! Number of Q.931 messages the APDU can "timeout" on. */ unsigned num_messages; /*! Q.931 message list to "timeout" on. */ int message_type[5]; /*! * \brief APDU 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.) * * \note * A callback must be supplied if the sender cares about any APDU_CALLBACK_REASON. * * \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 struct apdu_msg_data *msg); /*! \brief Sender data for the callback function to identify the particular APDU. */ union apdu_callback_param user; }; struct apdu_event { /*! Linked list pointer */ struct apdu_event *next; /*! TRUE if this APDU has been sent. */ int sent; /*! What message to send the ADPU in */ int message; /*! Sender supplied information to handle APDU response messages. */ struct apdu_callback_data response; /*! Q.931 call leg. (Needed for the APDU timeout.) */ struct q931_call *call; /*! Response timeout timer. */ int timer; /*! Length of ADPU */ int apdu_len; /*! ADPU to send */ 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); /* starts a 2BCT */ int eect_initiate_transfer(struct pri *pri, q931_call *c1, q931_call *c2); int rlt_initiate_transfer(struct pri *pri, q931_call *c1, q931_call *c2); /* starts a QSIG Path Replacement */ int anfpr_initiate_transfer(struct pri *pri, q931_call *c1, q931_call *c2); int etsi_initiate_transfer(struct pri *ctrl, q931_call *call_1, q931_call *call_2); int qsig_cf_callrerouting(struct pri *pri, q931_call *c, const char* dest, const char* original, const char* reason); int send_reroute_request(struct pri *ctrl, q931_call *call, const struct q931_party_id *caller, const struct q931_party_redirecting *deflection, int subscription_option); int send_call_transfer_complete(struct pri *pri, q931_call *call, int call_status); int rose_request_subaddress_encode(struct pri *ctrl, struct q931_call *call); int send_subaddress_transfer(struct pri *ctrl, struct q931_call *call); int rose_diverting_leg_information1_encode(struct pri *pri, q931_call *call); int rose_diverting_leg_information3_encode(struct pri *pri, q931_call *call, int messagetype); int rose_connected_name_encode(struct pri *pri, q931_call *call, int messagetype); 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 */ int pri_call_add_standard_apdus(struct pri *pri, q931_call *call); void asn1_dump(struct pri *ctrl, const unsigned char *start_asn1, const unsigned char *end); 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); 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); 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); int aoc_charging_request_send(struct pri *ctrl, q931_call *c, enum PRI_AOC_REQUEST aoc_request_flag); void aoc_etsi_aoc_request(struct pri *ctrl, q931_call *call, const struct rose_msg_invoke *invoke); void aoc_etsi_aoc_s_currency(struct pri *ctrl, const struct rose_msg_invoke *invoke); void aoc_etsi_aoc_s_special_arrangement(struct pri *ctrl, const struct rose_msg_invoke *invoke); void aoc_etsi_aoc_d_currency(struct pri *ctrl, const struct rose_msg_invoke *invoke); void aoc_etsi_aoc_d_charging_unit(struct pri *ctrl, const struct rose_msg_invoke *invoke); void aoc_etsi_aoc_e_currency(struct pri *ctrl, q931_call *call, const struct rose_msg_invoke *invoke); void aoc_etsi_aoc_e_charging_unit(struct pri *ctrl, q931_call *call, const struct rose_msg_invoke *invoke); #endif /* _PRI_FACILITY_H */ libpri-1.4.14/compiler.h0000644000000000000000000000223611046150644013547 0ustar rootroot/* * Asterisk -- A telephony toolkit for Linux. * * Compiler-specific macros and other items * * Copyright (C) 2005, Digium, Inc. */ /* * 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. */ #ifndef _ASTERISK_COMPILER_H #define _ASTERISK_COMPILER_H #if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 96) #define __builtin_expect(exp, c) (exp) #endif #endif /* _ASTERISK_COMPILER_H */ libpri-1.4.14/rose_qsig_cc.c0000644000000000000000000010400311377242706014373 0ustar rootroot/* * 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 */ libpri-1.4.14/compat.h0000644000000000000000000000024710374426762013232 0ustar rootroot#ifndef __COMPAT_H #define __COMPAT_H #ifdef SOLARIS typedef unsigned char u_int8_t; typedef unsigned short u_int16_t; typedef unsigned int u_int32_t; #endif #endif libpri-1.4.14/asn1.h0000644000000000000000000002566011173441755012614 0ustar rootroot/* * 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 ASN.1 definitions and prototypes * * \details * This file contains all ASN.1 primitive data structures and * definitions needed for ROSE component encoding and decoding. * * ROSE - Remote Operations Service Element * ASN.1 - Abstract Syntax Notation 1 * APDU - Application Protocol Data Unit * * \author Richard Mudgett */ #ifndef _LIBPRI_ASN1_H #define _LIBPRI_ASN1_H #include #include #ifdef __cplusplus extern "C" { #endif /* ------------------------------------------------------------------- */ /*! ASN.1 Identifier Octet - Tag class bits */ #define ASN1_CLASS_MASK 0xc0 #define ASN1_CLASS_UNIVERSAL 0x00 /*!< Universal primitive data types */ #define ASN1_CLASS_APPLICATION 0x40 /*!< Application wide data tag */ #define ASN1_CLASS_CONTEXT_SPECIFIC 0x80 /*!< Context specifc data tag */ #define ASN1_CLASS_PRIVATE 0xc0 /*!< Private organization data tag */ /*! ASN.1 Identifier Octet - Primitive/Constructor bit */ #define ASN1_PC_MASK 0x20 #define ASN1_PC_PRIMITIVE 0x00 #define ASN1_PC_CONSTRUCTED 0x20 /*! ASN.1 Identifier Octet - Universal data types */ #define ASN1_TYPE_MASK 0x1f #define ASN1_TYPE_INDEF_TERM 0x00 /* 0 */ #define ASN1_TYPE_BOOLEAN 0x01 /* 1 */ #define ASN1_TYPE_INTEGER 0x02 /* 2 */ #define ASN1_TYPE_BIT_STRING 0x03 /* 3 */ #define ASN1_TYPE_OCTET_STRING 0x04 /* 4 */ #define ASN1_TYPE_NULL 0x05 /* 5 */ #define ASN1_TYPE_OBJECT_IDENTIFIER 0x06 /* 6 */ #define ASN1_TYPE_OBJECT_DESCRIPTOR 0x07 /* 7 */ #define ASN1_TYPE_EXTERN 0x08 /* 8 */ #define ASN1_TYPE_REAL 0x09 /* 9 */ #define ASN1_TYPE_ENUMERATED 0x0a /* 10 */ #define ASN1_TYPE_EMBEDDED_PDV 0x0b /* 11 */ #define ASN1_TYPE_UTF8_STRING 0x0c /* 12 */ #define ASN1_TYPE_RELATIVE_OID 0x0d /* 13 */ /* 0x0e & 0x0f are reserved for future ASN.1 editions */ #define ASN1_TYPE_SEQUENCE 0x10 /* 16 */ #define ASN1_TYPE_SET 0x11 /* 17 */ #define ASN1_TYPE_NUMERIC_STRING 0x12 /* 18 */ #define ASN1_TYPE_PRINTABLE_STRING 0x13 /* 19 */ #define ASN1_TYPE_TELETEX_STRING 0x14 /* 20 */ #define ASN1_TYPE_VIDEOTEX_STRING 0x15 /* 21 */ #define ASN1_TYPE_IA5_STRING 0x16 /* 22 */ #define ASN1_TYPE_UTC_TIME 0x17 /* 23 */ #define ASN1_TYPE_GENERALIZED_TIME 0x18 /* 24 */ #define ASN1_TYPE_GRAPHIC_STRING 0x19 /* 25 */ #define ASN1_TYPE_VISIBLE_STRING 0x1a /* 26 */ #define ASN1_TYPE_ISO646_STRING 0x1a /* 26 */ #define ASN1_TYPE_GENERAL_STRING 0x1b /* 27 */ #define ASN1_TYPE_UNIVERSAL_STRING 0x1c /* 28 */ #define ASN1_TYPE_CHAR_STRING 0x1d /* 29 */ #define ASN1_TYPE_BMP_STRING 0x1e /* 30 */ #define ASN1_TYPE_EXTENSION 0x1f /* 31 */ #define ASN1_TAG_SEQUENCE (ASN1_CLASS_UNIVERSAL | ASN1_PC_CONSTRUCTED | ASN1_TYPE_SEQUENCE) #define ASN1_TAG_SET (ASN1_CLASS_UNIVERSAL | ASN1_PC_CONSTRUCTED | ASN1_TYPE_SET) #define ASN1_INDEF_TERM (ASN1_CLASS_UNIVERSAL | ASN1_PC_PRIMITIVE | ASN1_TYPE_INDEF_TERM) #define ASN1_INDEF_TERM_LEN 2 struct asn1_oid { /*! \brief Number of subidentifier values in OID list */ u_int16_t num_values; /*! * \brief OID subidentifier value list * \note The first value is really the first two OID subidentifiers. * They are compressed using this formula: * First_Value = (First_Subidentifier * 40) + Second_Subidentifier */ u_int16_t value[10]; }; #define ASN1_CALL(new_pos, do_it) \ do \ { \ (new_pos) = (do_it); \ if (!(new_pos)) { \ return NULL; \ } \ } while (0) /*! \brief Determine the ending position of the set or sequence to verify the length. */ #define ASN1_END_SETUP(component_end, offset, length, pos, end) \ do { \ if ((length) < 0) { \ (offset) = ASN1_INDEF_TERM_LEN; \ (component_end) = (end); \ } else { \ (offset) = 0; \ (component_end) = (pos) + (length); \ } \ } while (0) /*! \brief Account for the indefinite length terminator of the set or sequence. */ #define ASN1_END_FIXUP(ctrl, pos, offset, component_end, end) \ do { \ if (offset) { \ ASN1_CALL((pos), asn1_dec_indef_end_fixup((ctrl), (pos), (end))); \ } else if ((pos) != (component_end)) { \ if ((ctrl)->debug & PRI_DEBUG_APDU) { \ pri_message((ctrl), \ " Skipping unused constructed component octets!\n"); \ } \ (pos) = (component_end); \ } \ } while (0) #define ASN1_DID_NOT_EXPECT_TAG(ctrl, tag) \ do { \ if ((ctrl)->debug & PRI_DEBUG_APDU) { \ pri_message((ctrl), " Did not expect: %s\n", asn1_tag2str(tag)); \ } \ } while (0) #define ASN1_CHECK_TAG(ctrl, actual_tag, match_tag, expected_tag) \ do { \ if ((match_tag) != (expected_tag)) { \ ASN1_DID_NOT_EXPECT_TAG((ctrl), (actual_tag)); \ return NULL; \ } \ } while (0) const unsigned char *asn1_dec_tag(const unsigned char *tag_pos, const unsigned char *end, unsigned *tag); const unsigned char *asn1_dec_length(const unsigned char *len_pos, const unsigned char *end, int *length); const unsigned char *asn1_dec_indef_end_fixup(struct pri *ctrl, const unsigned char *pos, const unsigned char *end); const unsigned char *asn1_dec_boolean(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, int32_t *value); const unsigned char *asn1_dec_int(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, int32_t *value); const unsigned char *asn1_dec_null(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end); const unsigned char *asn1_dec_oid(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct asn1_oid *oid); const unsigned char *asn1_dec_string_bin(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, size_t buf_size, unsigned char *str, size_t *str_len); const unsigned char *asn1_dec_string_max(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, size_t buf_size, unsigned char *str, size_t *str_len); const char *asn1_tag2str(unsigned tag); void asn1_dump(struct pri *ctrl, const unsigned char *start_asn1, const unsigned char *end); #define ASN1_LEN_FORM_SHORT 1 /*!< Hint that the final length will be less than 128 octets */ #define ASN1_LEN_FORM_LONG_U8 2 /*!< Hint that the final length will be less than 256 octets */ #define ASN1_LEN_FORM_LONG_U16 3 /*!< Hint that the final length will be less than 65536 octets */ #define ASN1_LEN_INIT(len_pos, end, form_hint) \ do { \ if ((end) < (len_pos) + (form_hint)) { \ return NULL; \ } \ *(len_pos) = (form_hint); \ (len_pos) += (form_hint); \ } while (0) #define ASN1_LEN_FIXUP(len_pos, component_end, end) \ ASN1_CALL((component_end), asn1_enc_length_fixup((len_pos), (component_end), (end))) /*! \brief Use to begin encoding explicit tags, SET, and SEQUENCE constructed groupings. */ #define ASN1_CONSTRUCTED_BEGIN(len_pos_save, pos, end, tag) \ do { \ if ((end) < (pos) + (1 + ASN1_LEN_FORM_SHORT)) { \ return NULL; \ } \ *(pos)++ = (tag) | ASN1_PC_CONSTRUCTED; \ (len_pos_save) = (pos); \ *(pos) = ASN1_LEN_FORM_SHORT; \ (pos) += ASN1_LEN_FORM_SHORT; \ } while (0) /*! \brief Use to end encoding explicit tags, SET, and SEQUENCE constructed groupings. */ #define ASN1_CONSTRUCTED_END(len_pos, component_end, end) \ ASN1_CALL((component_end), asn1_enc_length_fixup((len_pos), (component_end), (end))) #define ASN1_ENC_ERROR(ctrl, msg) \ pri_error((ctrl), "%s error: %s\n", __FUNCTION__, (msg)) unsigned char *asn1_enc_length(unsigned char *len_pos, unsigned char *end, size_t str_len); unsigned char *asn1_enc_length_fixup(unsigned char *len_pos, unsigned char *component_end, unsigned char *end); unsigned char *asn1_enc_boolean(unsigned char *pos, unsigned char *end, unsigned tag, int32_t value); unsigned char *asn1_enc_int(unsigned char *pos, unsigned char *end, unsigned tag, int32_t value); unsigned char *asn1_enc_null(unsigned char *pos, unsigned char *end, unsigned tag); unsigned char *asn1_enc_oid(unsigned char *pos, unsigned char *end, unsigned tag, const struct asn1_oid *oid); unsigned char *asn1_enc_string_bin(unsigned char *pos, unsigned char *end, unsigned tag, const unsigned char *str, size_t str_len); unsigned char *asn1_enc_string_max(unsigned char *pos, unsigned char *end, unsigned tag, const unsigned char *str, size_t max_len); /* ------------------------------------------------------------------- */ #ifdef __cplusplus } #endif #endif /* _LIBPRI_ASN1_H */ /* ------------------------------------------------------------------- */ /* end asn1.h */ libpri-1.4.14/LICENSE0000644000000000000000000004313010374426762012601 0ustar rootroot GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. libpri-1.4.14/pri_q931.h0000644000000000000000000004317511533041274013312 0ustar rootroot/* * libpri: An implementation of Primary Rate ISDN * * Written by Mark Spencer * * Copyright (C) 2001, Digium, Inc. * All Rights Reserved. */ /* * 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. */ #ifndef _PRI_Q931_H #define _PRI_Q931_H typedef enum q931_mode { UNKNOWN_MODE, CIRCUIT_MODE, PACKET_MODE } q931_mode; typedef struct q931_h { unsigned char raw[0]; u_int8_t pd; /* Protocol Discriminator */ #if __BYTE_ORDER == __BIG_ENDIAN u_int8_t x0:4; u_int8_t crlen:4;/*!< Call reference length */ #else u_int8_t crlen:4;/*!< Call reference length */ u_int8_t x0:4; #endif u_int8_t contents[0]; u_int8_t crv[3];/*!< Call reference value */ } __attribute__ ((packed)) q931_h; /* Message type header */ typedef struct q931_mh { #if __BYTE_ORDER == __BIG_ENDIAN u_int8_t f:1; u_int8_t msg:7; #else u_int8_t msg:7; u_int8_t f:1; #endif u_int8_t data[0]; } __attribute__ ((packed)) q931_mh; /* Information element format */ typedef struct q931_ie { u_int8_t ie; u_int8_t len; u_int8_t data[0]; } __attribute__ ((packed)) q931_ie; #define Q931_RES_HAVEEVENT (1 << 0) #define Q931_RES_INERRROR (1 << 1) #define Q931_PROTOCOL_DISCRIMINATOR 0x08 #define GR303_PROTOCOL_DISCRIMINATOR 0x4f /* AT&T Maintenance Protocol Discriminator */ #define MAINTENANCE_PROTOCOL_DISCRIMINATOR_1 0x03 /* National Maintenance Protocol Discriminator */ #define MAINTENANCE_PROTOCOL_DISCRIMINATOR_2 0x43 /* 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 #define Q931_CONNECT 0x07 #define Q931_CONNECT_ACKNOWLEDGE 0x0f #define Q931_PROGRESS 0x03 #define Q931_SETUP 0x05 #define Q931_SETUP_ACKNOWLEDGE 0x0d /* Call Disestablishment Messages */ #define Q931_DISCONNECT 0x45 #define Q931_RELEASE 0x4d #define Q931_RELEASE_COMPLETE 0x5a #define Q931_RESTART 0x46 #define Q931_RESTART_ACKNOWLEDGE 0x4e /* Miscellaneous Messages */ #define Q931_STATUS 0x7d #define Q931_STATUS_ENQUIRY 0x75 #define Q931_USER_INFORMATION 0x20 #define Q931_SEGMENT 0x60 #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 */ #define Q931_HOLD 0x24 #define Q931_HOLD_ACKNOWLEDGE 0x28 #define Q931_HOLD_REJECT 0x30 #define Q931_RETRIEVE 0x31 #define Q931_RETRIEVE_ACKNOWLEDGE 0x33 #define Q931_RETRIEVE_REJECT 0x37 #define Q931_RESUME 0x26 #define Q931_RESUME_ACKNOWLEDGE 0x2e #define Q931_RESUME_REJECT 0x22 #define Q931_SUSPEND 0x25 #define Q931_SUSPEND_ACKNOWLEDGE 0x2d #define Q931_SUSPEND_REJECT 0x21 /* Maintenance messages (codeset 0 only) */ #define ATT_SERVICE 0x0f #define ATT_SERVICE_ACKNOWLEDGE 0x07 #define NATIONAL_SERVICE 0x07 #define NATIONAL_SERVICE_ACKNOWLEDGE 0x0f #define SERVICE_CHANGE_STATUS_INSERVICE 0 #define SERVICE_CHANGE_STATUS_LOOPBACK 1 /* not supported */ #define SERVICE_CHANGE_STATUS_OUTOFSERVICE 2 #define SERVICE_CHANGE_STATUS_REQCONTINUITYCHECK 3 /* not supported */ #define SERVICE_CHANGE_STATUS_SHUTDOWN 4 /* not supported */ /* Special codeset 0 IE */ #define NATIONAL_CHANGE_STATUS 0x1 /* Q.931 / National ISDN Information Elements */ #define Q931_LOCKING_SHIFT 0x90 #define Q931_NON_LOCKING_SHIFT 0x98 #define Q931_BEARER_CAPABILITY 0x04 #define Q931_CAUSE 0x08 #define Q931_IE_CALL_STATE 0x14 #define Q931_CHANNEL_IDENT 0x18 #define Q931_PROGRESS_INDICATOR 0x1e #define Q931_NETWORK_SPEC_FAC 0x20 #define Q931_CALLING_PARTY_CATEGORY (0x32 | Q931_CODESET(5)) #define Q931_INFORMATION_RATE 0x40 #define Q931_TRANSIT_DELAY 0x42 #define Q931_TRANS_DELAY_SELECT 0x43 #define Q931_BINARY_PARAMETERS 0x44 #define Q931_WINDOW_SIZE 0x45 #define Q931_PACKET_SIZE 0x46 #define Q931_CLOSED_USER_GROUP 0x47 #define Q931_REVERSE_CHARGE_INDIC 0x4a #define Q931_CALLING_PARTY_NUMBER 0x6c #define Q931_CALLING_PARTY_SUBADDR 0x6d #define Q931_CALLED_PARTY_NUMBER 0x70 #define Q931_CALLED_PARTY_SUBADDR 0x71 #define Q931_REDIRECTING_NUMBER 0x74 #define Q931_REDIRECTING_SUBADDR 0x75 #define Q931_TRANSIT_NET_SELECT 0x78 #define Q931_RESTART_INDICATOR 0x79 #define Q931_LOW_LAYER_COMPAT 0x7c #define Q931_HIGH_LAYER_COMPAT 0x7d #define Q931_CODESET(x) ((x) << 8) #define Q931_IE_CODESET(x) ((x) >> 8) #define Q931_IE_IE(x) ((x) & 0xff) #define Q931_FULL_IE(codeset, ie) (((codeset) << 8) | ((ie) & 0xff)) #define Q931_DISPLAY 0x28 #define Q931_IE_SEGMENTED_MSG 0x00 #define Q931_IE_CHANGE_STATUS 0x01 #define Q931_IE_ORIGINATING_LINE_INFO (0x01 | Q931_CODESET(6)) #define Q931_IE_CONNECTED_ADDR 0x0c #define Q931_IE_CONNECTED_NUM 0x4c #define Q931_IE_CONNECTED_SUBADDR 0x4d #define Q931_IE_CALL_IDENTITY 0x10 #define Q931_IE_FACILITY 0x1c #define Q931_IE_ENDPOINT_ID 0x26 #define Q931_IE_NOTIFY_IND 0x27 #define Q931_IE_TIME_DATE 0x29 #define Q931_IE_KEYPAD_FACILITY 0x2c #define Q931_IE_CALL_STATUS 0x2d #define Q931_IE_UPDATE 0x31 #define Q931_IE_INFO_REQUEST 0x32 #define Q931_IE_SIGNAL 0x34 #define Q931_IE_SWITCHHOOK 0x36 #define Q931_IE_GENERIC_DIGITS (0x37 | Q931_CODESET(6)) #define Q931_IE_FEATURE_ACTIVATE 0x38 #define Q931_IE_FEATURE_IND 0x39 #define Q931_IE_ORIGINAL_CALLED_NUMBER 0x73 #define Q931_IE_REDIRECTION_NUMBER 0x76 #define Q931_IE_REDIRECTION_SUBADDR 0x77 #define Q931_IE_USER_USER_FACILITY 0x7A #define Q931_IE_USER_USER 0x7E #define Q931_IE_ESCAPE_FOR_EXT 0x7F /*! Q.931 call states */ enum Q931_CALL_STATE { /*! * \details * null state (U0): * No call exists. * \details * null state (N0): * No call exists. */ Q931_CALL_STATE_NULL = 0, /*! * \details * call initiated (U1): * This state exists for an outgoing call, when the user requests * call establishment from the network. * \details * call initiated (N1): * This state exists for an outgoing call when the network has received * a call establishment request but has not yet responded. */ Q931_CALL_STATE_CALL_INITIATED = 1, /*! * \details * overlap sending (U2): * This state exists for an outgoing call when the user has * received acknowledgement of the call establishment request which * permits the user to send additional call information to the network * in overlap mode. * \details * overlap sending (N2): * This state exists for an outgoing call when the network has acknowledged * the call establishment request and is prepared to receive additional * call information (if any) in overlap mode. */ Q931_CALL_STATE_OVERLAP_SENDING = 2, /*! * \details * outgoing call proceeding (U3): * This state exists for an outgoing call when the user has * received acknowledgement that the network has received all * call information necessary to effect call establishment. * \details * outgoing call proceeding (N3): * This state exists for an outgoing call when the network has sent * acknowledgement that the network has received all call information * necessary to effect call establishment. */ Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING = 3, /*! * \details * call delivered (U4): * This state exists for an outgoing call when the calling user has * received an indication that remote user alerting has been initiated. * \details * call delivered (N4): * This state exists for an outgoing call when the network has indicated * that remote user alerting has been initiated. */ Q931_CALL_STATE_CALL_DELIVERED = 4, /*! * \details * call present (U6): * This state exists for an incoming call when the user has received a * call establishment request but has not yet responded. * \details * call present (N6): * This state exists for an incoming call when the network has sent a * call establishment request but has not yet received a satisfactory * response. */ Q931_CALL_STATE_CALL_PRESENT = 6, /*! * \details * call received (U7): * This state exists for an incoming call when the user has indicated * alerting but has not yet answered. * \details * call received (N7): * This state exists for an incoming call when the network has received * an indication that the user is alerting but has not yet received an * answer. */ Q931_CALL_STATE_CALL_RECEIVED = 7, /*! * \details * connect request (U8): * This state exists for an incoming call when the user has answered * the call and is waiting to be awarded the call. * \details * connect request (N8): * This state exists for an incoming call when the network has received * an answer but the network has not yet awarded the call. */ Q931_CALL_STATE_CONNECT_REQUEST = 8, /*! * \details * incoming call proceeding (U9): * This state exists for an incoming call when the user has sent * acknowledgement that the user has received all call information * necessary to effect call establishment. * \details * incoming call proceeding (N9): * This state exists for an incoming call when the network has received * acknowledgement that the user has received all call information * necessary to effect call establishment. */ Q931_CALL_STATE_INCOMING_CALL_PROCEEDING = 9, /*! * \details * active (U10): * This state exists for an incoming call when the user has received * an acknowledgement from the network that the user has been awarded * the call. This state exists for an outgoing call when the user has * received an indication that the remote user has answered the call. * \details * active (N10): * This state exists for an incoming call when the network has awarded * the call to the called user. This state exists for an outgoing call * when the network has indicated that the remote user has answered * the call. */ Q931_CALL_STATE_ACTIVE = 10, /*! * \details * disconnect request (U11): * This state exists when the user has requested the network to clear * the end-to-end connection (if any) and is waiting for a response. * \details * disconnect request (N11): * This state exists when the network has received a request from the * user to clear the end-to-end connection (if any). */ Q931_CALL_STATE_DISCONNECT_REQUEST = 11, /*! * \details * disconnect indication (U12): * This state exists when the user has received an invitation to * disconnect because the network has disconnected the end-to-end * connection (if any). * \details * disconnect indication (N12): * This state exists when the network has disconnected the end-to-end * connection (if any) and has sent an invitation to disconnect the * user-network connection. */ Q931_CALL_STATE_DISCONNECT_INDICATION = 12, /*! * \details * suspend request (U15): * This state exists when the user has requested the network to suspend * the call and is waiting for a response. * \details * suspend request (N15): * This state exists when the network has received a request to suspend * the call but has not yet responded. */ Q931_CALL_STATE_SUSPEND_REQUEST = 15, /*! * \details * resume request (U17): * This state exists when the user has requested the network to resume * a previously suspended call and is waiting for a response. * \details * resume request (N17): * This state exists when the network has received a request to resume * a previously suspended call but has not yet responded. */ Q931_CALL_STATE_RESUME_REQUEST = 17, /*! * \details * release request (U19): * This state exists when the user has requested the network to release * and is waiting for a response. * \details * release request (N19): * This state exists when the network has requested the user to release * and is waiting for a response. */ Q931_CALL_STATE_RELEASE_REQUEST = 19, /*! * \details * call abort (N22): * This state exists for an incoming call for the point-to-multipoint * configuration when the call is being cleared before any user has been * awarded the call. */ Q931_CALL_STATE_CALL_ABORT = 22, /*! * \details * overlap receiving (U25): * This state exists for an incoming call when the user has acknowledged * the call establishment request from the network and is prepared to * receive additional call information (if any) in overlap mode. * \details * overlap receiving (N25): * This state exists for an incoming call when the network has received * acknowledgement of the call establishment request which permits the * network to send additional call information (if any) in the overlap * mode. */ Q931_CALL_STATE_OVERLAP_RECEIVING = 25, /*! * \details * call independent service (U31): (From Q.932) * This state exists when a call independent supplementary service * signalling connection is established. * \details * call independent service (N31): (From Q.932) * This state exists when a call independent supplementary service * signalling connection is established. */ Q931_CALL_STATE_CALL_INDEPENDENT_SERVICE = 31, Q931_CALL_STATE_RESTART_REQUEST = 61, Q931_CALL_STATE_RESTART = 62, /*! * \details * Call state has not been set. * Call state does not exist. * Call state not initialized. * Call state internal use only. */ Q931_CALL_STATE_NOT_SET = 0xFF, }; /*! Q.931 call establishment state ranking for competing calls in PTMP NT mode. */ enum Q931_RANKED_CALL_STATE { /*! Call is present but has no response yet. */ Q931_RANKED_CALL_STATE_PRESENT, /*! Call is collecting digits. */ Q931_RANKED_CALL_STATE_OVERLAP, /*! Call routing is happening. */ Q931_RANKED_CALL_STATE_PROCEEDING, /*! Called party is being alerted of the call. */ Q931_RANKED_CALL_STATE_ALERTING, /*! Call is connected. A winner has been declared. */ Q931_RANKED_CALL_STATE_CONNECT, /*! Call is in some non-call establishment state (likely disconnecting). */ Q931_RANKED_CALL_STATE_OTHER, /*! Master call is aborting. */ Q931_RANKED_CALL_STATE_ABORT, }; /* EuroISDN */ #define Q931_SENDING_COMPLETE 0xa1 extern int maintenance_service(struct pri *pri, int span, int channel, int changestatus); /* Q.SIG specific */ #define QSIG_IE_TRANSIT_COUNT 0x31 int q931_receive(struct q921_link *link, q931_h *h, int len); extern int q931_alerting(struct pri *pri, q931_call *call, int channel, int info); extern int q931_call_progress_with_cause(struct pri *pri, q931_call *call, int channel, int info, int cause); extern int q931_call_progress(struct pri *pri, q931_call *call, int channel, int info); extern int q931_notify(struct pri *pri, q931_call *call, int channel, int info); extern int q931_call_proceeding(struct pri *pri, q931_call *call, int channel, int info); extern int q931_setup_ack(struct pri *pri, q931_call *call, int channel, int nonisdn); extern int q931_information(struct pri *pri, q931_call *call, char digit); extern int q931_keypad_facility(struct pri *pri, q931_call *call, const char *digits); extern int q931_connect(struct pri *pri, q931_call *call, int channel, int nonisdn); int q931_connect_acknowledge(struct pri *ctrl, q931_call *call, int channel); extern int q931_release(struct pri *pri, q931_call *call, int cause); extern int q931_disconnect(struct pri *pri, q931_call *call, int cause); extern int q931_hangup(struct pri *pri, q931_call *call, int cause); extern int q931_restart(struct pri *pri, int channel); extern int q931_facility(struct pri *pri, q931_call *call); extern int q931_call_getcrv(struct pri *pri, q931_call *call, int *callmode); extern int q931_call_setcrv(struct pri *pri, q931_call *call, int crv, int callmode); struct q931_call *q931_new_call(struct pri *ctrl); 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); enum Q931_DL_EVENT { Q931_DL_EVENT_NONE, Q931_DL_EVENT_DL_ESTABLISH_IND, Q931_DL_EVENT_DL_ESTABLISH_CONFIRM, Q931_DL_EVENT_DL_RELEASE_IND, Q931_DL_EVENT_DL_RELEASE_CONFIRM, Q931_DL_EVENT_TEI_REMOVAL, }; void q931_dl_event(struct q921_link *link, enum Q931_DL_EVENT event); int q931_send_hold(struct pri *ctrl, struct q931_call *call); int q931_send_hold_ack(struct pri *ctrl, struct q931_call *call); int q931_send_hold_rej(struct pri *ctrl, struct q931_call *call, int cause); int q931_send_retrieve(struct pri *ctrl, struct q931_call *call, int channel); int q931_send_retrieve_ack(struct pri *ctrl, struct q931_call *call, int channel); int q931_send_retrieve_rej(struct pri *ctrl, struct q931_call *call, int cause); #endif libpri-1.4.14/pri.c0000644000000000000000000017523311772721141012534 0ustar rootroot/* * libpri: An implementation of Primary Rate ISDN * * Written by Mark Spencer * * Copyright (C) 2001-2005, Digium, Inc. * All Rights Reserved. */ /* * 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. */ #include #include #include #include #include #include #include #include #include #include "compat.h" #include "libpri.h" #include "pri_internal.h" #include "pri_facility.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; enum PRI_TIMERS_AND_COUNTERS number; unsigned long used_by; }; /*! * \note Sort the timer table entries in the order of the timer name so * pri_dump_info_str() can display them in a consistent order. */ static const struct pri_timer_table pri_timer[] = { /* *INDENT-OFF* */ /* timer name timer number used by switches */ { "N200", PRI_TIMER_N200, PRI_ALL_SWITCHES }, { "N201", PRI_TIMER_N201, PRI_ALL_SWITCHES }, { "N202", PRI_TIMER_N202, PRI_ALL_SWITCHES }, { "K", PRI_TIMER_K, PRI_ALL_SWITCHES }, { "T200", PRI_TIMER_T200, PRI_ALL_SWITCHES }, { "T201", PRI_TIMER_T201, PRI_ALL_SWITCHES }, { "T202", PRI_TIMER_T202, PRI_ALL_SWITCHES }, { "T203", PRI_TIMER_T203, PRI_ALL_SWITCHES }, { "T300", PRI_TIMER_T300, PRI_ALL_SWITCHES }, { "T301", PRI_TIMER_T301, PRI_ALL_SWITCHES }, { "T302", PRI_TIMER_T302, PRI_ALL_SWITCHES }, { "T303", PRI_TIMER_T303, PRI_ALL_SWITCHES }, { "T304", PRI_TIMER_T304, PRI_ALL_SWITCHES }, { "T305", PRI_TIMER_T305, PRI_ALL_SWITCHES }, { "T306", PRI_TIMER_T306, PRI_ALL_SWITCHES }, { "T307", PRI_TIMER_T307, PRI_ALL_SWITCHES }, { "T308", PRI_TIMER_T308, PRI_ALL_SWITCHES }, { "T309", PRI_TIMER_T309, PRI_ALL_SWITCHES }, { "T310", PRI_TIMER_T310, PRI_ALL_SWITCHES }, { "T312", PRI_TIMER_T312, PRI_ALL_SWITCHES }, { "T313", PRI_TIMER_T313, PRI_ALL_SWITCHES }, { "T314", PRI_TIMER_T314, PRI_ALL_SWITCHES }, { "T316", PRI_TIMER_T316, PRI_ALL_SWITCHES }, { "N316", PRI_TIMER_N316, PRI_ALL_SWITCHES }, { "T317", PRI_TIMER_T317, PRI_ALL_SWITCHES }, { "T318", PRI_TIMER_T318, PRI_ALL_SWITCHES }, { "T319", PRI_TIMER_T319, PRI_ALL_SWITCHES }, { "T320", PRI_TIMER_T320, PRI_ALL_SWITCHES }, { "T321", PRI_TIMER_T321, PRI_ALL_SWITCHES }, { "T322", PRI_TIMER_T322, PRI_ALL_SWITCHES }, { "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* */ }; char *pri_node2str(int node) { switch(node) { case PRI_UNKNOWN: return "Unknown node type"; case PRI_NETWORK: return "Network"; case PRI_CPE: return "CPE"; default: return "Invalid value"; } } char *pri_switch2str(int sw) { switch(sw) { case PRI_SWITCH_NI2: return "National ISDN"; case PRI_SWITCH_DMS100: return "Nortel DMS100"; case PRI_SWITCH_LUCENT5E: return "Lucent 5E"; case PRI_SWITCH_ATT4ESS: return "AT&T 4ESS"; case PRI_SWITCH_NI1: return "National ISDN 1"; case PRI_SWITCH_EUROISDN_E1: return "EuroISDN"; case PRI_SWITCH_GR303_EOC: return "GR303 EOC"; case PRI_SWITCH_GR303_TMC: return "GR303 TMC"; case PRI_SWITCH_QSIG: return "Q.SIG switch"; default: return "Unknown switchtype"; } } static void pri_default_timers(struct pri *ctrl, int switchtype) { unsigned idx; /* Initialize all timers/counters to unsupported/disabled. */ for (idx = 0; idx < PRI_MAX_TIMERS; ++idx) { ctrl->timers[idx] = -1; } /* Set timer values to standard defaults. Time is in ms. */ ctrl->timers[PRI_TIMER_N200] = 3; /* Max numer of Q.921 retransmissions */ ctrl->timers[PRI_TIMER_N202] = 3; /* Max numer of transmissions of the TEI identity request message */ if (ctrl->bri) { ctrl->timers[PRI_TIMER_K] = 1; /* Max number of outstanding I-frames */ } else { ctrl->timers[PRI_TIMER_K] = 7; /* Max number of outstanding I-frames */ } ctrl->timers[PRI_TIMER_T200] = 1000; /* Time between SABME's */ ctrl->timers[PRI_TIMER_T201] = ctrl->timers[PRI_TIMER_T200];/* Time between TEI Identity Checks (Default same as T200) */ ctrl->timers[PRI_TIMER_T202] = 10 * 1000; /* Min time between transmission of TEI Identity request messages */ ctrl->timers[PRI_TIMER_T203] = 10 * 1000; /* Max time without exchanging packets */ ctrl->timers[PRI_TIMER_T303] = 4 * 1000; /* Length between SETUP retransmissions and timeout */ ctrl->timers[PRI_TIMER_T305] = 30 * 1000; /* Wait for DISCONNECT acknowledge */ ctrl->timers[PRI_TIMER_T308] = 4 * 1000; /* Wait for RELEASE acknowledge */ ctrl->timers[PRI_TIMER_T309] = 6 * 1000; /* Time to wait before clearing calls in case of D-channel transient event. Q.931 specifies 6-90 seconds */ ctrl->timers[PRI_TIMER_T312] = (4 + 2) * 1000;/* Supervise broadcast SETUP message call reference retention. T303 + 2 seconds */ ctrl->timers[PRI_TIMER_T313] = 4 * 1000; /* Wait for CONNECT acknowledge, CPE side only */ #if 0 /* Default disable the T316 timer otherwise the user cannot disable it. */ ctrl->timers[PRI_TIMER_T316] = 2 * 60 * 1000; /* RESTART retransmit timer */ #endif ctrl->timers[PRI_TIMER_N316] = 2; /* Send RESTART this many times before giving up. */ ctrl->timers[PRI_TIMER_TM20] = 2500; /* Max time awaiting XID response - Q.921 Appendix IV */ ctrl->timers[PRI_TIMER_NM20] = 3; /* Number of XID retransmits - Q.921 Appendix IV */ ctrl->timers[PRI_TIMER_T_HOLD] = 4 * 1000; /* Wait for HOLD request response. */ ctrl->timers[PRI_TIMER_T_RETRIEVE] = 4 * 1000;/* Wait for RETRIEVE request response. */ 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: break; } } int pri_set_timer(struct pri *ctrl, int timer, int value) { if (!ctrl || timer < 0 || PRI_MAX_TIMERS <= timer || value < 0) { return -1; } ctrl->timers[timer] = value; return 0; } int pri_get_timer(struct pri *ctrl, int timer) { if (!ctrl || timer < 0 || PRI_MAX_TIMERS <= timer) { return -1; } return ctrl->timers[timer]; } int pri_set_service_message_support(struct pri *pri, int supportflag) { if (!pri) { return -1; } pri->service_message_support = supportflag ? 1 : 0; return 0; } int pri_timer2idx(const char *timer_name) { unsigned idx; enum PRI_TIMERS_AND_COUNTERS timer_number; timer_number = -1; for (idx = 0; idx < ARRAY_LEN(pri_timer); ++idx) { if (!strcasecmp(timer_name, pri_timer[idx].name)) { timer_number = pri_timer[idx].number; break; } } return timer_number; } static int __pri_read(struct pri *pri, void *buf, int buflen) { int res = read(pri->fd, buf, buflen); if (res < 0) { if (errno != EAGAIN) pri_error(pri, "Read on %d failed: %s\n", pri->fd, strerror(errno)); return 0; } return res; } static int __pri_write(struct pri *pri, void *buf, int buflen) { int res = write(pri->fd, buf, buflen); if (res < 0) { if (errno != EAGAIN) pri_error(pri, "Write to %d failed: %s\n", pri->fd, strerror(errno)); return 0; } return res; } /*! * \internal * \brief Determine the default layer 2 persistence option. * * \param ctrl D channel controller. * * \return Default layer 2 persistence option. (legacy behaviour default) */ static enum pri_layer2_persistence pri_l2_persistence_option_default(struct pri *ctrl) { enum pri_layer2_persistence persistence; if (PTMP_MODE(ctrl)) { persistence = PRI_L2_PERSISTENCE_LEAVE_DOWN; } else { persistence = PRI_L2_PERSISTENCE_KEEP_UP; } return persistence; } /*! * \internal * \brief Determine the default display text send options. * * \param ctrl D channel controller. * * \return Default display text send options. (legacy behaviour defaults) */ static unsigned long pri_display_options_send_default(struct pri *ctrl) { unsigned long flags; switch (ctrl->switchtype) { case PRI_SWITCH_QSIG: flags = PRI_DISPLAY_OPTION_BLOCK; break; case PRI_SWITCH_EUROISDN_E1: case PRI_SWITCH_EUROISDN_T1: if (ctrl->localtype == PRI_CPE) { flags = PRI_DISPLAY_OPTION_BLOCK; break; } flags = PRI_DISPLAY_OPTION_NAME_INITIAL; break; default: flags = PRI_DISPLAY_OPTION_NAME_INITIAL; break; } return flags; } /*! * \internal * \brief Determine the default display text receive options. * * \param ctrl D channel controller. * * \return Default display text receive options. (legacy behaviour defaults) */ static unsigned long pri_display_options_receive_default(struct pri *ctrl) { unsigned long flags; switch (ctrl->switchtype) { case PRI_SWITCH_QSIG: flags = PRI_DISPLAY_OPTION_BLOCK; break; default: flags = PRI_DISPLAY_OPTION_NAME_INITIAL; break; } return flags; } /*! * \internal * \brief Determine the default date/time send option default. * * \param ctrl D channel controller. * * \return Default date/time send option. */ static int pri_date_time_send_default(struct pri *ctrl) { int date_time_send; if (BRI_NT_PTMP(ctrl)) { date_time_send = PRI_DATE_TIME_SEND_DATE_HHMM; } else { date_time_send = PRI_DATE_TIME_SEND_NO; } return date_time_send; } /*! * \brief Destroy the given link. * * \param link Q.921 link to destroy. * * \return Nothing */ void pri_link_destroy(struct q921_link *link) { if (link) { struct q931_call *call; call = link->dummy_call; if (call) { pri_schedule_del(call->pri, call->retranstimer); call->retranstimer = 0; pri_call_apdu_queue_cleanup(call); } free(link); } } /*! * \internal * \brief Initialize the layer 2 link structure. * * \param ctrl D channel controller. * \param link Q.921 link to initialize. * \param sapi SAPI new link is to use. * \param tei TEI new link is to use. * * \note It is assumed that the link has already been memset to zero. * * \return Nothing */ static void pri_link_init(struct pri *ctrl, struct q921_link *link, int sapi, int tei) { link->ctrl = ctrl; link->sapi = sapi; link->tei = tei; } /*! * \brief Create a new layer 2 link. * * \param ctrl D channel controller. * \param sapi SAPI new link is to use. * \param tei TEI new link is to use. * * \retval link on success. * \retval NULL on error. */ struct q921_link *pri_link_new(struct pri *ctrl, int sapi, int tei) { struct link_dummy *dummy_link; struct q921_link *link; switch (ctrl->switchtype) { case PRI_SWITCH_GR303_EOC: case PRI_SWITCH_GR303_TMC: link = calloc(1, sizeof(*link)); if (!link) { return NULL; } dummy_link = NULL; break; default: dummy_link = calloc(1, sizeof(*dummy_link)); if (!dummy_link) { return NULL; } link = &dummy_link->link; break; } pri_link_init(ctrl, link, sapi, tei); if (dummy_link) { /* Initialize the dummy call reference call record. */ link->dummy_call = &dummy_link->dummy_call; q931_init_call_record(link, link->dummy_call, Q931_DUMMY_CALL_REFERENCE); } q921_start(link); return link; } /*! * \internal * \brief Destroy the given D channel controller. * * \param ctrl D channel control to destroy. * * \return Nothing */ static void pri_ctrl_destroy(struct pri *ctrl) { if (ctrl) { struct q931_call *call; if (ctrl->link.tei == Q921_TEI_GROUP && ctrl->link.sapi == Q921_SAPI_LAYER2_MANAGEMENT && ctrl->localtype == PRI_CPE) { /* This dummy call was borrowed from the specific TEI link. */ call = NULL; } else { call = ctrl->link.dummy_call; } if (call) { pri_schedule_del(call->pri, call->retranstimer); call->retranstimer = 0; pri_call_apdu_queue_cleanup(call); } free(ctrl->msg_line); free(ctrl->sched.timer); free(ctrl); } } /*! * \internal * \brief Create a new D channel control structure. * * \param fd D channel file descriptor if no callback functions supplied. * \param node Switch NET/CPE type * \param switchtype ISDN switch type * \param rd D channel read callback function * \param wr D channel write callback function * \param userdata Callback function parameter * \param tei TEI new link is to use. * \param bri TRUE if interface is BRI * * \retval ctrl on success. * \retval NULL on error. */ static struct pri *pri_ctrl_new(int fd, int node, int switchtype, pri_io_cb rd, pri_io_cb wr, void *userdata, int tei, int bri) { int create_dummy_call; struct d_ctrl_dummy *dummy_ctrl; struct pri *ctrl; switch (switchtype) { case PRI_SWITCH_GR303_EOC: case PRI_SWITCH_GR303_TMC: create_dummy_call = 0; break; default: if (bri && node == PRI_CPE && tei == Q921_TEI_GROUP) { /* * BRI TE PTMP will not use its own group dummy call record. It * will use the specific TEI dummy call instead. */ create_dummy_call = 0; } else { create_dummy_call = 1; } break; } if (create_dummy_call) { dummy_ctrl = calloc(1, sizeof(*dummy_ctrl)); if (!dummy_ctrl) { return NULL; } ctrl = &dummy_ctrl->ctrl; } else { ctrl = calloc(1, sizeof(*ctrl)); if (!ctrl) { return NULL; } dummy_ctrl = NULL; } ctrl->msg_line = calloc(1, sizeof(*ctrl->msg_line)); if (!ctrl->msg_line) { free(ctrl); return NULL; } ctrl->bri = bri; ctrl->fd = fd; ctrl->read_func = rd; ctrl->write_func = wr; ctrl->userdata = userdata; ctrl->localtype = node; ctrl->switchtype = switchtype; ctrl->cref = 1; ctrl->nsf = PRI_NSF_NONE; ctrl->callpool = &ctrl->localpool; pri_default_timers(ctrl, switchtype); ctrl->q921_rxcount = 0; ctrl->q921_txcount = 0; ctrl->q931_rxcount = 0; ctrl->q931_txcount = 0; ctrl->l2_persistence = pri_l2_persistence_option_default(ctrl); ctrl->display_flags.send = pri_display_options_send_default(ctrl); ctrl->display_flags.receive = pri_display_options_receive_default(ctrl); switch (switchtype) { case PRI_SWITCH_GR303_EOC: ctrl->protodisc = GR303_PROTOCOL_DISCRIMINATOR; pri_link_init(ctrl, &ctrl->link, Q921_SAPI_GR303_EOC, Q921_TEI_GR303_EOC_OPS); ctrl->link.next = pri_link_new(ctrl, Q921_SAPI_GR303_EOC, Q921_TEI_GR303_EOC_PATH); if (!ctrl->link.next) { pri_ctrl_destroy(ctrl); return NULL; } break; case PRI_SWITCH_GR303_TMC: ctrl->protodisc = GR303_PROTOCOL_DISCRIMINATOR; pri_link_init(ctrl, &ctrl->link, Q921_SAPI_GR303_TMC_CALLPROC, Q921_TEI_GR303_TMC_CALLPROC); ctrl->link.next = pri_link_new(ctrl, Q921_SAPI_GR303_TMC_SWITCHING, Q921_TEI_GR303_TMC_SWITCHING); if (!ctrl->link.next) { pri_ctrl_destroy(ctrl); return NULL; } break; default: ctrl->protodisc = Q931_PROTOCOL_DISCRIMINATOR; pri_link_init(ctrl, &ctrl->link, (tei == Q921_TEI_GROUP) ? Q921_SAPI_LAYER2_MANAGEMENT : Q921_SAPI_CALL_CTRL, tei); break; } ctrl->date_time_send = pri_date_time_send_default(ctrl); if (dummy_ctrl) { /* Initialize the dummy call reference call record. */ ctrl->link.dummy_call = &dummy_ctrl->dummy_call; q931_init_call_record(&ctrl->link, ctrl->link.dummy_call, Q931_DUMMY_CALL_REFERENCE); } if (ctrl->link.tei == Q921_TEI_GROUP && ctrl->link.sapi == Q921_SAPI_LAYER2_MANAGEMENT && ctrl->localtype == PRI_CPE) { ctrl->link.next = pri_link_new(ctrl, Q921_SAPI_CALL_CTRL, Q921_TEI_PRI); if (!ctrl->link.next) { pri_ctrl_destroy(ctrl); return NULL; } /* * Make the group link use the just created specific TEI link * dummy call instead. It makes no sense for TE PTMP interfaces * to broadcast messages on the dummy call or to broadcast any * messages for that matter. */ ctrl->link.dummy_call = ctrl->link.next->dummy_call; } else { q921_start(&ctrl->link); } return ctrl; } void pri_call_set_useruser(q931_call *c, const char *userchars) { /* * There is a slight risk here if c is actually stale. However, * if it is stale then it is better to catch it here than to * write with it. */ if (!userchars || !pri_is_call_valid(NULL, c)) { return; } libpri_copy_string(c->useruserinfo, userchars, sizeof(c->useruserinfo)); } void pri_sr_set_useruser(struct pri_sr *sr, const char *userchars) { sr->useruserinfo = userchars; } int pri_restart(struct pri *pri) { /* pri_restart() is no longer needed since the Q.921 rewrite. */ #if 0 /* Restart Q.921 layer */ if (pri) { q921_reset(pri, 1); q921_start(pri, pri->localtype == PRI_CPE); } #endif return 0; } struct pri *pri_new(int fd, int nodetype, int switchtype) { return pri_ctrl_new(fd, nodetype, switchtype, __pri_read, __pri_write, NULL, Q921_TEI_PRI, 0); } struct pri *pri_new_bri(int fd, int ptpmode, int nodetype, int switchtype) { if (ptpmode) return pri_ctrl_new(fd, nodetype, switchtype, __pri_read, __pri_write, NULL, Q921_TEI_PRI, 1); else return pri_ctrl_new(fd, nodetype, switchtype, __pri_read, __pri_write, NULL, Q921_TEI_GROUP, 1); } struct pri *pri_new_cb(int fd, int nodetype, int switchtype, pri_io_cb io_read, pri_io_cb io_write, void *userdata) { if (!io_read) io_read = __pri_read; if (!io_write) io_write = __pri_write; return pri_ctrl_new(fd, nodetype, switchtype, io_read, io_write, userdata, Q921_TEI_PRI, 0); } struct pri *pri_new_bri_cb(int fd, int ptpmode, int nodetype, int switchtype, pri_io_cb io_read, pri_io_cb io_write, void *userdata) { if (!io_read) { io_read = __pri_read; } if (!io_write) { io_write = __pri_write; } if (ptpmode) { return pri_ctrl_new(fd, nodetype, switchtype, io_read, io_write, userdata, Q921_TEI_PRI, 1); } else { return pri_ctrl_new(fd, nodetype, switchtype, io_read, io_write, userdata, Q921_TEI_GROUP, 1); } } void *pri_get_userdata(struct pri *pri) { return pri ? pri->userdata : NULL; } void pri_set_userdata(struct pri *pri, void *userdata) { if (pri) pri->userdata = userdata; } void pri_set_nsf(struct pri *pri, int nsf) { if (pri) pri->nsf = nsf; } char *pri_event2str(int id) { unsigned idx; struct { int id; char *name; } events[] = { /* *INDENT-OFF* */ { PRI_EVENT_DCHAN_UP, "PRI_EVENT_DCHAN_UP" }, { PRI_EVENT_DCHAN_DOWN, "PRI_EVENT_DCHAN_DOWN" }, { PRI_EVENT_RESTART, "PRI_EVENT_RESTART" }, { PRI_EVENT_CONFIG_ERR, "PRI_EVENT_CONFIG_ERR" }, { PRI_EVENT_RING, "PRI_EVENT_RING" }, { PRI_EVENT_HANGUP, "PRI_EVENT_HANGUP" }, { PRI_EVENT_RINGING, "PRI_EVENT_RINGING" }, { PRI_EVENT_ANSWER, "PRI_EVENT_ANSWER" }, { PRI_EVENT_HANGUP_ACK, "PRI_EVENT_HANGUP_ACK" }, { PRI_EVENT_RESTART_ACK, "PRI_EVENT_RESTART_ACK" }, { PRI_EVENT_FACILITY, "PRI_EVENT_FACILITY" }, { PRI_EVENT_INFO_RECEIVED, "PRI_EVENT_INFO_RECEIVED" }, { PRI_EVENT_PROCEEDING, "PRI_EVENT_PROCEEDING" }, { PRI_EVENT_SETUP_ACK, "PRI_EVENT_SETUP_ACK" }, { PRI_EVENT_HANGUP_REQ, "PRI_EVENT_HANGUP_REQ" }, { PRI_EVENT_NOTIFY, "PRI_EVENT_NOTIFY" }, { PRI_EVENT_PROGRESS, "PRI_EVENT_PROGRESS" }, { PRI_EVENT_KEYPAD_DIGIT, "PRI_EVENT_KEYPAD_DIGIT" }, { PRI_EVENT_SERVICE, "PRI_EVENT_SERVICE" }, { PRI_EVENT_SERVICE_ACK, "PRI_EVENT_SERVICE_ACK" }, { PRI_EVENT_HOLD, "PRI_EVENT_HOLD" }, { PRI_EVENT_HOLD_ACK, "PRI_EVENT_HOLD_ACK" }, { PRI_EVENT_HOLD_REJ, "PRI_EVENT_HOLD_REJ" }, { PRI_EVENT_RETRIEVE, "PRI_EVENT_RETRIEVE" }, { PRI_EVENT_RETRIEVE_ACK, "PRI_EVENT_RETRIEVE_ACK" }, { PRI_EVENT_RETRIEVE_REJ, "PRI_EVENT_RETRIEVE_REJ" }, { PRI_EVENT_CONNECT_ACK, "PRI_EVENT_CONNECT_ACK" }, /* *INDENT-ON* */ }; for (idx = 0; idx < ARRAY_LEN(events); ++idx) { if (events[idx].id == id) { return events[idx].name; } } return "Unknown Event"; } pri_event *pri_check_event(struct pri *pri) { char buf[1024]; int res; pri_event *e; res = pri->read_func ? pri->read_func(pri, buf, sizeof(buf)) : 0; if (!res) return NULL; /* Receive the q921 packet */ e = q921_receive(pri, (q921_h *)buf, res); return e; } static int wait_pri(struct pri *pri) { struct timeval *tv, real; fd_set fds; int res; FD_ZERO(&fds); FD_SET(pri->fd, &fds); tv = pri_schedule_next(pri); if (tv) { gettimeofday(&real, NULL); real.tv_sec = tv->tv_sec - real.tv_sec; real.tv_usec = tv->tv_usec - real.tv_usec; if (real.tv_usec < 0) { real.tv_usec += 1000000; real.tv_sec -= 1; } if (real.tv_sec < 0) { real.tv_sec = 0; real.tv_usec = 0; } } res = select(pri->fd + 1, &fds, NULL, NULL, tv ? &real : tv); if (res < 0) return -1; return res; } pri_event *pri_mkerror(struct pri *pri, char *errstr) { /* Return a configuration error */ pri->ev.err.e = PRI_EVENT_CONFIG_ERR; libpri_copy_string(pri->ev.err.err, errstr, sizeof(pri->ev.err.err)); return &pri->ev; } pri_event *pri_dchannel_run(struct pri *pri, int block) { pri_event *e; int res; if (!pri) return NULL; if (block) { do { e = NULL; res = wait_pri(pri); /* Check for error / interruption */ if (res < 0) return NULL; if (!res) e = pri_schedule_run(pri); else e = pri_check_event(pri); } while(!e); } else { e = pri_check_event(pri); return e; } return e; } void pri_set_debug(struct pri *pri, int debug) { if (!pri) return; pri->debug = debug; } int pri_get_debug(struct pri *pri) { if (!pri) return -1; return pri->debug; } void pri_facility_enable(struct pri *pri) { if (!pri) return; pri->sendfacility = 1; } int pri_acknowledge(struct pri *pri, q931_call *call, int channel, int info) { if (!pri || !pri_is_call_valid(pri, call)) { return -1; } return q931_alerting(pri, call, channel, info); } int pri_proceeding(struct pri *pri, q931_call *call, int channel, int info) { if (!pri || !pri_is_call_valid(pri, call)) { return -1; } return q931_call_proceeding(pri, call, channel, info); } int pri_progress_with_cause(struct pri *pri, q931_call *call, int channel, int info, int cause) { if (!pri || !pri_is_call_valid(pri, call)) { return -1; } return q931_call_progress_with_cause(pri, call, channel, info, cause); } int pri_progress(struct pri *pri, q931_call *call, int channel, int info) { if (!pri || !pri_is_call_valid(pri, call)) { return -1; } return q931_call_progress(pri, call, channel, info); } int pri_information(struct pri *pri, q931_call *call, char digit) { if (!pri || !pri_is_call_valid(pri, call)) { return -1; } return q931_information(pri, call, digit); } int pri_keypad_facility(struct pri *pri, q931_call *call, const char *digits) { if (!pri || !pri_is_call_valid(pri, call) || !digits || !digits[0]) { return -1; } return q931_keypad_facility(pri, call, digits); } int pri_notify(struct pri *pri, q931_call *call, int channel, int info) { if (!pri || !pri_is_call_valid(pri, call)) { return -1; } return q931_notify(pri, call, channel, info); } void pri_destroycall(struct pri *pri, q931_call *call) { if (pri && pri_is_call_valid(pri, call)) { q931_destroycall(pri, call); } } int pri_need_more_info(struct pri *pri, q931_call *call, int channel, int nonisdn) { if (!pri || !pri_is_call_valid(pri, call)) { return -1; } return q931_setup_ack(pri, call, channel, nonisdn); } int pri_answer(struct pri *pri, q931_call *call, int channel, int nonisdn) { if (!pri || !pri_is_call_valid(pri, call)) { return -1; } return q931_connect(pri, call, channel, nonisdn); } int pri_connect_ack(struct pri *ctrl, q931_call *call, int channel) { if (!ctrl || !pri_is_call_valid(ctrl, call)) { return -1; } return q931_connect_acknowledge(ctrl, call, channel); } void pri_connect_ack_enable(struct pri *ctrl, int enable) { if (ctrl) { ctrl->manual_connect_ack = enable ? 1 : 0; } } /*! * \brief Copy the PRI party name to the Q.931 party name structure. * * \param q931_name Q.931 party name structure * \param pri_name PRI party name structure * * \return Nothing */ void pri_copy_party_name_to_q931(struct q931_party_name *q931_name, const struct pri_party_name *pri_name) { q931_party_name_init(q931_name); if (pri_name->valid) { q931_name->valid = 1; q931_name->presentation = pri_name->presentation & PRI_PRES_RESTRICTION; q931_name->char_set = pri_name->char_set; libpri_copy_string(q931_name->str, pri_name->str, sizeof(q931_name->str)); } } /*! * \brief Copy the PRI party number to the Q.931 party number structure. * * \param q931_number Q.931 party number structure * \param pri_number PRI party number structure * * \return Nothing */ void pri_copy_party_number_to_q931(struct q931_party_number *q931_number, const struct pri_party_number *pri_number) { q931_party_number_init(q931_number); if (pri_number->valid) { q931_number->valid = 1; q931_number->presentation = pri_number->presentation & (PRI_PRES_RESTRICTION | PRI_PRES_NUMBER_TYPE); q931_number->plan = pri_number->plan; libpri_copy_string(q931_number->str, pri_number->str, sizeof(q931_number->str)); } } /*! * \brief Copy the PRI party subaddress to the Q.931 party subaddress structure. * * \param q931_subaddress Q.931 party subaddress structure * \param pri_subaddress PRI party subaddress structure * * \return Nothing */ void pri_copy_party_subaddress_to_q931(struct q931_party_subaddress *q931_subaddress, const struct pri_party_subaddress *pri_subaddress) { int length; int maxlen = sizeof(q931_subaddress->data) - 1; q931_party_subaddress_init(q931_subaddress); if (!pri_subaddress->valid) { return; } q931_subaddress->valid = 1; q931_subaddress->type = pri_subaddress->type; length = pri_subaddress->length; if (length > maxlen){ length = maxlen; } else { q931_subaddress->odd_even_indicator = pri_subaddress->odd_even_indicator; } q931_subaddress->length = length; memcpy(q931_subaddress->data, pri_subaddress->data, length); q931_subaddress->data[length] = '\0'; } /*! * \brief Copy the PRI party id to the Q.931 party id structure. * * \param q931_id Q.931 party id structure * \param pri_id PRI party id structure * * \return Nothing */ void pri_copy_party_id_to_q931(struct q931_party_id *q931_id, const struct pri_party_id *pri_id) { pri_copy_party_name_to_q931(&q931_id->name, &pri_id->name); pri_copy_party_number_to_q931(&q931_id->number, &pri_id->number); pri_copy_party_subaddress_to_q931(&q931_id->subaddress, &pri_id->subaddress); } int pri_connected_line_update(struct pri *ctrl, q931_call *call, const struct pri_party_connected_line *connected) { struct q931_party_id party_id; unsigned idx; unsigned new_name; unsigned new_number; unsigned new_subaddress; struct q931_call *subcall; if (!ctrl || !pri_is_call_valid(ctrl, call)) { return -1; } pri_copy_party_id_to_q931(&party_id, &connected->id); q931_party_id_fixup(ctrl, &party_id); new_name = q931_party_name_cmp(&party_id.name, &call->local_id.name); new_number = q931_party_number_cmp(&party_id.number, &call->local_id.number); new_subaddress = party_id.subaddress.valid && q931_party_subaddress_cmp(&party_id.subaddress, &call->local_id.subaddress); /* Update the call and all subcalls with new local_id. */ call->local_id = party_id; if (call->outboundbroadcast && call->master_call == call) { for (idx = 0; idx < ARRAY_LEN(call->subcalls); ++idx) { subcall = call->subcalls[idx]; if (subcall) { subcall->local_id = party_id; } } } switch (call->ourcallstate) { case Q931_CALL_STATE_CALL_INITIATED: case Q931_CALL_STATE_OVERLAP_SENDING: case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: case Q931_CALL_STATE_CALL_DELIVERED: /* * The local party transferred to someone else before * the remote end answered. */ switch (ctrl->switchtype) { case PRI_SWITCH_EUROISDN_E1: case PRI_SWITCH_EUROISDN_T1: if (BRI_NT_PTMP(ctrl)) { /* * NT PTMP mode * * We should not send these messages to the network if we are * the CPE side since phones do not transfer calls within * themselves. Well... If you consider handing the handset to * someone else a transfer then how is the network to know? */ if (new_number) { q931_notify_redirection(ctrl, call, PRI_NOTIFY_TRANSFER_ACTIVE, &party_id.name, &party_id.number); } if (new_subaddress || (party_id.subaddress.valid && new_number)) { q931_subaddress_transfer(ctrl, call); } } else if (PTP_MODE(ctrl)) { /* PTP mode */ if (new_number) { /* Immediately send EctInform APDU, callStatus=answered(0) */ send_call_transfer_complete(ctrl, call, 0); } if (new_subaddress || (party_id.subaddress.valid && new_number)) { q931_subaddress_transfer(ctrl, call); } } break; case PRI_SWITCH_QSIG: if (new_name || new_number) { /* Immediately send CallTransferComplete APDU, callStatus=answered(0) */ send_call_transfer_complete(ctrl, call, 0); } if (new_subaddress || (party_id.subaddress.valid && (new_name || new_number))) { q931_subaddress_transfer(ctrl, call); } break; default: break; } break; case Q931_CALL_STATE_ACTIVE: switch (ctrl->switchtype) { case PRI_SWITCH_EUROISDN_E1: case PRI_SWITCH_EUROISDN_T1: if (BRI_NT_PTMP(ctrl)) { /* * NT PTMP mode * * We should not send these messages to the network if we are * the CPE side since phones do not transfer calls within * themselves. Well... If you consider handing the handset to * someone else a transfer then how is the network to know? */ if (new_number) { #if defined(USE_NOTIFY_FOR_ECT) /* * Some ISDN phones only handle the NOTIFY message that the * EN 300-369 spec says should be sent only if the call has not * connected yet. */ q931_notify_redirection(ctrl, call, PRI_NOTIFY_TRANSFER_ACTIVE, &party_id.name, &party_id.number); #else q931_request_subaddress(ctrl, call, PRI_NOTIFY_TRANSFER_ACTIVE, &party_id.name, &party_id.number); #endif /* defined(USE_NOTIFY_FOR_ECT) */ } if (new_subaddress || (party_id.subaddress.valid && new_number)) { q931_subaddress_transfer(ctrl, call); } } else if (PTP_MODE(ctrl)) { /* PTP mode */ if (new_number) { /* Immediately send EctInform APDU, callStatus=answered(0) */ send_call_transfer_complete(ctrl, call, 0); } if (new_subaddress || (party_id.subaddress.valid && new_number)) { q931_subaddress_transfer(ctrl, call); } } break; case PRI_SWITCH_QSIG: if (new_name || new_number) { /* Immediately send CallTransferComplete APDU, callStatus=answered(0) */ send_call_transfer_complete(ctrl, call, 0); } if (new_subaddress || (party_id.subaddress.valid && (new_name || new_number))) { q931_subaddress_transfer(ctrl, call); } break; default: break; } break; default: /* Just save the data for further developments. */ break; } return 0; } int pri_redirecting_update(struct pri *ctrl, q931_call *call, const struct pri_party_redirecting *redirecting) { unsigned idx; struct q931_call *subcall; if (!ctrl || !pri_is_call_valid(ctrl, call)) { return -1; } /* Save redirecting.to information and reason. */ pri_copy_party_id_to_q931(&call->redirecting.to, &redirecting->to); q931_party_id_fixup(ctrl, &call->redirecting.to); call->redirecting.reason = redirecting->reason; /* * Update all subcalls with new redirecting.to information and reason. * I do not think we will ever have any subcalls when this data is relevant, * but update it just in case. */ if (call->outboundbroadcast && call->master_call == call) { for (idx = 0; idx < ARRAY_LEN(call->subcalls); ++idx) { subcall = call->subcalls[idx]; if (subcall) { subcall->redirecting.to = call->redirecting.to; subcall->redirecting.reason = redirecting->reason; } } } switch (call->ourcallstate) { case Q931_CALL_STATE_NULL: /* Save the remaining redirecting information before we place a call. */ pri_copy_party_id_to_q931(&call->redirecting.from, &redirecting->from); q931_party_id_fixup(ctrl, &call->redirecting.from); pri_copy_party_id_to_q931(&call->redirecting.orig_called, &redirecting->orig_called); q931_party_id_fixup(ctrl, &call->redirecting.orig_called); call->redirecting.orig_reason = redirecting->orig_reason; if (redirecting->count <= 0) { if (call->redirecting.from.number.valid) { /* * We are redirecting with an unknown count * so assume the count is one. */ call->redirecting.count = 1; } else { call->redirecting.count = 0; } } else if (redirecting->count < PRI_MAX_REDIRECTS) { call->redirecting.count = redirecting->count; } else { call->redirecting.count = PRI_MAX_REDIRECTS; } break; case Q931_CALL_STATE_OVERLAP_RECEIVING: case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: case Q931_CALL_STATE_CALL_RECEIVED: /* This is an incoming call that has not connected yet. */ if (!call->redirecting.to.number.valid) { /* Not being redirected toward valid number data. Ignore. */ break; } switch (ctrl->switchtype) { case PRI_SWITCH_EUROISDN_E1: case PRI_SWITCH_EUROISDN_T1: if (PTMP_MODE(ctrl)) { if (NT_MODE(ctrl)) { /* * NT PTMP mode * * We should not send these messages to the network if we are * the CPE side since phones do not redirect calls within * themselves. Well... If you consider someone else picking up * the handset a redirection then how is the network to know? */ q931_notify_redirection(ctrl, call, PRI_NOTIFY_CALL_DIVERTING, NULL, &call->redirecting.to.number); } break; } /* PTP mode - same behaviour as Q.SIG */ /* fall through */ case PRI_SWITCH_QSIG: if (call->redirecting.state != Q931_REDIRECTING_STATE_PENDING_TX_DIV_LEG_3 || strcmp(call->redirecting.to.number.str, call->called.number.str) != 0) { /* immediately send divertingLegInformation1 APDU */ if (rose_diverting_leg_information1_encode(ctrl, call) || q931_facility(ctrl, call)) { pri_message(ctrl, "Could not schedule facility message for divertingLegInfo1\n"); } } call->redirecting.state = Q931_REDIRECTING_STATE_IDLE; /* immediately send divertingLegInformation3 APDU */ if (rose_diverting_leg_information3_encode(ctrl, call, Q931_FACILITY) || q931_facility(ctrl, call)) { pri_message(ctrl, "Could not schedule facility message for divertingLegInfo3\n"); } break; default: break; } break; default: pri_message(ctrl, "Ignored redirecting update because call in state %s(%d).\n", q931_call_state_str(call->ourcallstate), call->ourcallstate); break; } 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) { if (!pri || !pri_is_call_valid(pri, call)) { return -1; } return q931_release(pri, call, cause); } int pri_disconnect(struct pri *pri, q931_call *call, int cause) { if (!pri || !pri_is_call_valid(pri, call)) { return -1; } return q931_disconnect(pri, call, cause); } #endif int pri_channel_bridge(q931_call *call1, q931_call *call2) { struct q931_call *winner; /* * There is a slight risk here if call1 or call2 is actually * stale. However, if they are stale then it is better to catch * it here than to write with these pointers. */ if (!pri_is_call_valid(NULL, call1) || !pri_is_call_valid(NULL, call2)) { return -1; } winner = q931_find_winning_call(call1); if (!winner) { /* Cannot transfer: Call 1 does not have a winner yet. */ return -1; } call1 = winner; winner = q931_find_winning_call(call2); if (!winner) { /* Cannot transfer: Call 2 does not have a winner yet. */ return -1; } call2 = winner; /* Check to see if we're on the same PRI */ if (call1->pri != call2->pri) { return -1; } /* Check for bearer capability */ if (call1->bc.transcapability != call2->bc.transcapability) return -1; switch (call1->pri->switchtype) { case PRI_SWITCH_NI2: case PRI_SWITCH_LUCENT5E: case PRI_SWITCH_ATT4ESS: if (eect_initiate_transfer(call1->pri, call1, call2)) { return -1; } break; case PRI_SWITCH_DMS100: if (rlt_initiate_transfer(call1->pri, call1, call2)) { return -1; } break; case PRI_SWITCH_QSIG: call1->bridged_call = call2; call2->bridged_call = call1; if (anfpr_initiate_transfer(call1->pri, call1, call2)) { return -1; } break; case PRI_SWITCH_EUROISDN_E1: case PRI_SWITCH_EUROISDN_T1: if (etsi_initiate_transfer(call1->pri, call1, call2)) { return -1; } break; default: return -1; } return 0; } void pri_hangup_fix_enable(struct pri *ctrl, int enable) { if (ctrl) { ctrl->hangup_fix_enabled = enable ? 1 : 0; } } int pri_hangup(struct pri *pri, q931_call *call, int cause) { if (!pri || !pri_is_call_valid(pri, call)) { return -1; } if (cause == -1) /* normal clear cause */ cause = PRI_CAUSE_NORMAL_CLEARING; return q931_hangup(pri, call, cause); } int pri_reset(struct pri *pri, int channel) { if (!pri) return -1; return q931_restart(pri, channel); } int pri_maintenance_service(struct pri *pri, int span, int channel, int changestatus) { if (!pri) { return -1; } return maintenance_service(pri, span, channel, changestatus); } q931_call *pri_new_call(struct pri *pri) { if (!pri) return NULL; return q931_new_call(pri); } int pri_is_dummy_call(q931_call *call) { if (!call) { return 0; } return q931_is_dummy_call(call); } void pri_dump_event(struct pri *pri, pri_event *e) { if (!pri || !e) return; pri_message(pri, "Event type: %s (%d)\n", pri_event2str(e->gen.e), e->gen.e); } void pri_sr_init(struct pri_sr *req) { memset(req, 0, sizeof(struct pri_sr)); q931_party_redirecting_init(&req->redirecting); q931_party_id_init(&req->caller); q931_party_address_init(&req->called); req->reversecharge = PRI_REVERSECHARGE_NONE; } int pri_sr_set_connection_call_independent(struct pri_sr *req) { if (!req) return -1; req->cis_call = 1; /* have to set cis_call for all those pesky IEs we need to setup */ req->cis_auto_disconnect = 1; return 0; } int pri_sr_set_no_channel_call(struct pri_sr *req) { if (!req) { return -1; } req->cis_call = 1; return 0; } /* Don't call any other pri functions on this */ int pri_mwi_activate(struct pri *pri, q931_call *c, char *caller, int callerplan, char *callername, int callerpres, char *called, int calledplan) { struct pri_sr req; if (!pri || !pri_is_call_valid(pri, c)) { return -1; } pri_sr_init(&req); pri_sr_set_connection_call_independent(&req); pri_sr_set_caller(&req, caller, callername, callerplan, callerpres); pri_sr_set_called(&req, called, calledplan, 0); if (mwi_message_send(pri, c, &req, 1) < 0) { pri_message(pri, "Unable to send MWI activate message\n"); return -1; } /* Do more stuff when we figure out that the CISC stuff works */ return q931_setup(pri, c, &req); } int pri_mwi_deactivate(struct pri *pri, q931_call *c, char *caller, int callerplan, char *callername, int callerpres, char *called, int calledplan) { struct pri_sr req; if (!pri || !pri_is_call_valid(pri, c)) { return -1; } pri_sr_init(&req); pri_sr_set_connection_call_independent(&req); pri_sr_set_caller(&req, caller, callername, callerplan, callerpres); pri_sr_set_called(&req, called, calledplan, 0); if(mwi_message_send(pri, c, &req, 0) < 0) { pri_message(pri, "Unable to send MWI deactivate message\n"); return -1; } return q931_setup(pri, c, &req); } int pri_setup(struct pri *pri, q931_call *c, struct pri_sr *req) { if (!pri || !pri_is_call_valid(pri, c)) { return -1; } return q931_setup(pri, c, req); } int pri_call(struct pri *pri, q931_call *c, int transmode, int channel, int exclusive, int nonisdn, char *caller, int callerplan, char *callername, int callerpres, char *called, int calledplan, int ulayer1) { struct pri_sr req; if (!pri || !pri_is_call_valid(pri, c)) { return -1; } pri_sr_init(&req); pri_sr_set_caller(&req, caller, callername, callerplan, callerpres); pri_sr_set_called(&req, called, calledplan, 0); req.transmode = transmode; req.channel = channel; req.exclusive = exclusive; req.nonisdn = nonisdn; req.userl1 = ulayer1; return q931_setup(pri, c, &req); } static void (*__pri_error)(struct pri *pri, char *stuff); static void (*__pri_message)(struct pri *pri, char *stuff); void pri_set_message(void (*func)(struct pri *pri, char *stuff)) { __pri_message = func; } void pri_set_error(void (*func)(struct pri *pri, char *stuff)) { __pri_error = func; } static void pri_old_message(struct pri *ctrl, const char *fmt, va_list *ap) { char tmp[1024]; vsnprintf(tmp, sizeof(tmp), fmt, *ap); if (__pri_message) __pri_message(ctrl, tmp); else fputs(tmp, stdout); } void pri_message(struct pri *ctrl, const char *fmt, ...) { int added_length; va_list ap; if (!ctrl || !ctrl->msg_line) { /* Just have to do it the old way. */ va_start(ap, fmt); pri_old_message(ctrl, fmt, &ap); va_end(ap); return; } va_start(ap, fmt); added_length = vsnprintf(ctrl->msg_line->str + ctrl->msg_line->length, sizeof(ctrl->msg_line->str) - ctrl->msg_line->length, fmt, ap); va_end(ap); if (added_length < 0 || sizeof(ctrl->msg_line->str) <= ctrl->msg_line->length + added_length) { static char truncated_output[] = "v-- Error building output or output was truncated. (Next line) --v\n"; /* * This clause should never need to run because the * output line accumulation buffer is quite large. */ /* vsnprintf() error or output string was truncated. */ if (__pri_message) { __pri_message(ctrl, truncated_output); } else { fputs(truncated_output, stdout); } /* Add a terminating '\n' to force a flush of the line. */ ctrl->msg_line->length = strlen(ctrl->msg_line->str); if (ctrl->msg_line->length) { ctrl->msg_line->str[ctrl->msg_line->length - 1] = '\n'; } else { ctrl->msg_line->str[0] = '\n'; ctrl->msg_line->str[1] = '\0'; } } else { ctrl->msg_line->length += added_length; } if (ctrl->msg_line->length && ctrl->msg_line->str[ctrl->msg_line->length - 1] == '\n') { /* The accumulated output line was terminated so send it out. */ ctrl->msg_line->length = 0; if (__pri_message) { __pri_message(ctrl, ctrl->msg_line->str); } else { fputs(ctrl->msg_line->str, stdout); } } } void pri_error(struct pri *pri, const char *fmt, ...) { char tmp[1024]; va_list ap; va_start(ap, fmt); vsnprintf(tmp, sizeof(tmp), fmt, ap); va_end(ap); if (__pri_error) __pri_error(pri, tmp); else fputs(tmp, stderr); } /* Set overlap mode */ void pri_set_overlapdial(struct pri *pri,int state) { if (pri) { pri->overlapdial = state ? 1 : 0; } } void pri_set_chan_mapping_logical(struct pri *pri, int state) { if (pri && pri->switchtype == PRI_SWITCH_QSIG) { pri->chan_mapping_logical = state ? 1 : 0; } } void pri_set_inbanddisconnect(struct pri *pri, unsigned int enable) { if (pri) { pri->acceptinbanddisconnect = (enable != 0); } } int pri_fd(struct pri *pri) { return pri->fd; } /*! * \internal * \brief Append snprintf output to the given buffer. * * \param buf Buffer currently filling. * \param buf_used Offset into buffer where to put new stuff. * \param buf_size Actual buffer size of buf. * \param format printf format string. * * \return Total buffer space used. */ static size_t pri_snprintf(char *buf, size_t buf_used, size_t buf_size, const char *format, ...) __attribute__((format(printf, 4, 5))); static size_t pri_snprintf(char *buf, size_t buf_used, size_t buf_size, const char *format, ...) { va_list args; if (buf_used < buf_size) { va_start(args, format); buf_used += vsnprintf(buf + buf_used, buf_size - buf_used, format, args); va_end(args); } if (buf_size < buf_used) { buf_used = buf_size + 1; } return buf_used; } char *pri_dump_info_str(struct pri *ctrl) { char *buf; size_t buf_size; size_t used; struct q921_frame *f; struct q921_link *link; struct pri_cc_record *cc_record; struct q931_call *call; unsigned num_calls; unsigned num_globals; unsigned q921outstanding; unsigned idx; unsigned long switch_bit; if (!ctrl) { return NULL; } buf_size = 4096; /* This should be bigger than we will ever need. */ buf = malloc(buf_size); if (!buf) { return NULL; } /* Might be nice to format these a little better */ used = 0; used = pri_snprintf(buf, used, buf_size, "Switchtype: %s\n", pri_switch2str(ctrl->switchtype)); used = pri_snprintf(buf, used, buf_size, "Type: %s%s%s\n", ctrl->bri ? "BRI " : "", pri_node2str(ctrl->localtype), PTMP_MODE(ctrl) ? " PTMP" : ""); used = pri_snprintf(buf, used, buf_size, "Remote type: %s\n", pri_node2str(ctrl->remotetype)); used = pri_snprintf(buf, used, buf_size, "Overlap Dial: %d\n", ctrl->overlapdial); used = pri_snprintf(buf, used, buf_size, "Logical Channel Mapping: %d\n", ctrl->chan_mapping_logical); used = pri_snprintf(buf, used, buf_size, "Timer and counter settings:\n"); switch_bit = PRI_BIT(ctrl->switchtype); for (idx = 0; idx < ARRAY_LEN(pri_timer); ++idx) { if (pri_timer[idx].used_by & switch_bit) { enum PRI_TIMERS_AND_COUNTERS tmr; tmr = pri_timer[idx].number; if (0 <= ctrl->timers[tmr] || tmr == PRI_TIMER_T316) { used = pri_snprintf(buf, used, buf_size, " %s: %d\n", pri_timer[idx].name, ctrl->timers[tmr]); } } } /* Remember that Q921 Counters include Q931 packets (and any retransmissions) */ used = pri_snprintf(buf, used, buf_size, "Q931 RX: %d\n", ctrl->q931_rxcount); used = pri_snprintf(buf, used, buf_size, "Q931 TX: %d\n", ctrl->q931_txcount); used = pri_snprintf(buf, used, buf_size, "Q921 RX: %d\n", ctrl->q921_rxcount); used = pri_snprintf(buf, used, buf_size, "Q921 TX: %d\n", ctrl->q921_txcount); for (link = &ctrl->link; link; link = link->next) { q921outstanding = 0; for (f = link->tx_queue; f; f = f->next) { ++q921outstanding; } used = pri_snprintf(buf, used, buf_size, "Q921 Outstanding: %u (TEI=%d)\n", q921outstanding, link->tei); } /* Count the call records in existance. Useful to check for unreleased calls. */ num_calls = 0; num_globals = 0; for (call = *ctrl->callpool; call; call = call->next) { if (!(call->cr & ~Q931_CALL_REFERENCE_FLAG)) { ++num_globals; continue; } ++num_calls; if (call->outboundbroadcast) { used = pri_snprintf(buf, used, buf_size, "Master call subcall count: %d\n", q931_get_subcall_count(call)); } } used = pri_snprintf(buf, used, buf_size, "Total active-calls:%u global:%u\n", num_calls, num_globals); /* * List simplified call completion records. * * This should be last in the output because it could overflow * the buffer. */ used = pri_snprintf(buf, used, buf_size, "CC records:\n"); for (cc_record = ctrl->cc.pool; cc_record; cc_record = cc_record->next) { used = pri_snprintf(buf, used, buf_size, " %ld A:%s B:%s state:%s\n", cc_record->record_id, cc_record->party_a.number.valid ? cc_record->party_a.number.str : "", cc_record->party_b.number.valid ? cc_record->party_b.number.str : "", pri_cc_fsm_state_str(cc_record->state)); } if (buf_size < used) { pri_message(ctrl, "pri_dump_info_str(): Produced output exceeded buffer capacity. (Truncated)\n"); } return buf; } int pri_get_crv(struct pri *pri, q931_call *call, int *callmode) { if (!pri || !pri_is_call_valid(pri, call)) { return -1; } return q931_call_getcrv(pri, call, callmode); } int pri_set_crv(struct pri *pri, q931_call *call, int crv, int callmode) { if (!pri || !pri_is_call_valid(pri, call)) { return -1; } return q931_call_setcrv(pri, call, crv, callmode); } void pri_enslave(struct pri *master, struct pri *slave) { if (!master || !slave) { return; } if (slave->master) { struct pri *swp; /* The slave already has a master */ if (master->master || master->slave) { /* The new master has a master or it already has slaves. */ return; } /* Swap master and slave. */ swp = master; master = slave; slave = swp; } /* * To have some support for dynamic interfaces, the master NFAS * D channel control structure will always exist even if it is * abandoned/deleted by the upper layer. The master/slave * pointers ensure that the correct master will be used. */ master = PRI_NFAS_MASTER(master); master->nfas = 1; slave->nfas = 1; slave->callpool = &master->localpool; /* Link the slave to the master on the end of the master's list. */ slave->master = master; slave->slave = NULL; for (; master->slave; master = master->slave) { } master->slave = slave; } struct pri_sr *pri_sr_new(void) { struct pri_sr *req; req = malloc(sizeof(*req)); if (req) pri_sr_init(req); return req; } void pri_sr_free(struct pri_sr *sr) { free(sr); } int pri_sr_set_channel(struct pri_sr *sr, int channel, int exclusive, int nonisdn) { sr->channel = channel; sr->exclusive = exclusive; sr->nonisdn = nonisdn; return 0; } int pri_sr_set_bearer(struct pri_sr *sr, int transmode, int userl1) { sr->transmode = transmode; sr->userl1 = userl1; return 0; } int pri_sr_set_called(struct pri_sr *sr, char *called, int calledplan, int numcomplete) { q931_party_address_init(&sr->called); if (called) { sr->called.number.valid = 1; sr->called.number.plan = calledplan; libpri_copy_string(sr->called.number.str, called, sizeof(sr->called.number.str)); } sr->numcomplete = numcomplete; return 0; } void pri_sr_set_called_subaddress(struct pri_sr *sr, const struct pri_party_subaddress *subaddress) { pri_copy_party_subaddress_to_q931(&sr->called.subaddress, subaddress); } int pri_sr_set_caller(struct pri_sr *sr, char *caller, char *callername, int callerplan, int callerpres) { q931_party_id_init(&sr->caller); if (caller) { sr->caller.number.valid = 1; sr->caller.number.presentation = callerpres & (PRI_PRES_RESTRICTION | PRI_PRES_NUMBER_TYPE); sr->caller.number.plan = callerplan; libpri_copy_string(sr->caller.number.str, caller, sizeof(sr->caller.number.str)); if (callername) { sr->caller.name.valid = 1; sr->caller.name.presentation = callerpres & PRI_PRES_RESTRICTION; sr->caller.name.char_set = PRI_CHAR_SET_ISO8859_1; libpri_copy_string(sr->caller.name.str, callername, sizeof(sr->caller.name.str)); } } return 0; } void pri_sr_set_caller_subaddress(struct pri_sr *sr, const struct pri_party_subaddress *subaddress) { pri_copy_party_subaddress_to_q931(&sr->caller.subaddress, subaddress); } void pri_sr_set_caller_party(struct pri_sr *sr, const struct pri_party_id *caller) { pri_copy_party_id_to_q931(&sr->caller, caller); } int pri_sr_set_redirecting(struct pri_sr *sr, char *num, int plan, int pres, int reason) { q931_party_redirecting_init(&sr->redirecting); if (num && num[0]) { sr->redirecting.from.number.valid = 1; sr->redirecting.from.number.presentation = pres & (PRI_PRES_RESTRICTION | PRI_PRES_NUMBER_TYPE); sr->redirecting.from.number.plan = plan; libpri_copy_string(sr->redirecting.from.number.str, num, sizeof(sr->redirecting.from.number.str)); sr->redirecting.count = 1; sr->redirecting.reason = reason; } return 0; } void pri_sr_set_redirecting_parties(struct pri_sr *sr, const struct pri_party_redirecting *redirecting) { pri_copy_party_id_to_q931(&sr->redirecting.from, &redirecting->from); pri_copy_party_id_to_q931(&sr->redirecting.to, &redirecting->to); pri_copy_party_id_to_q931(&sr->redirecting.orig_called, &redirecting->orig_called); sr->redirecting.orig_reason = redirecting->orig_reason; sr->redirecting.reason = redirecting->reason; if (redirecting->count <= 0) { if (sr->redirecting.from.number.valid) { /* * We are redirecting with an unknown count * so assume the count is one. */ sr->redirecting.count = 1; } else { sr->redirecting.count = 0; } } else if (redirecting->count < PRI_MAX_REDIRECTS) { sr->redirecting.count = redirecting->count; } else { sr->redirecting.count = PRI_MAX_REDIRECTS; } } void pri_sr_set_reversecharge(struct pri_sr *sr, int requested) { sr->reversecharge = requested; } void pri_sr_set_keypad_digits(struct pri_sr *sr, const char *keypad_digits) { sr->keypad_digits = keypad_digits; } void pri_transfer_enable(struct pri *ctrl, int enable) { if (ctrl) { ctrl->transfer_support = enable ? 1 : 0; } } void pri_hold_enable(struct pri *ctrl, int enable) { if (ctrl) { ctrl->hold_support = enable ? 1 : 0; } } int pri_hold(struct pri *ctrl, q931_call *call) { if (!ctrl || !pri_is_call_valid(ctrl, call)) { return -1; } return q931_send_hold(ctrl, call); } int pri_hold_ack(struct pri *ctrl, q931_call *call) { if (!ctrl || !pri_is_call_valid(ctrl, call)) { return -1; } return q931_send_hold_ack(ctrl, call); } int pri_hold_rej(struct pri *ctrl, q931_call *call, int cause) { if (!ctrl || !pri_is_call_valid(ctrl, call)) { return -1; } return q931_send_hold_rej(ctrl, call, cause); } int pri_retrieve(struct pri *ctrl, q931_call *call, int channel) { if (!ctrl || !pri_is_call_valid(ctrl, call)) { return -1; } return q931_send_retrieve(ctrl, call, channel); } int pri_retrieve_ack(struct pri *ctrl, q931_call *call, int channel) { if (!ctrl || !pri_is_call_valid(ctrl, call)) { return -1; } return q931_send_retrieve_ack(ctrl, call, channel); } int pri_retrieve_rej(struct pri *ctrl, q931_call *call, int cause) { if (!ctrl || !pri_is_call_valid(ctrl, call)) { return -1; } return q931_send_retrieve_rej(ctrl, call, cause); } int pri_callrerouting_facility(struct pri *pri, q931_call *call, const char *dest, const char* original, const char* reason) { if (!pri || !pri_is_call_valid(pri, call) || !dest) { return -1; } return qsig_cf_callrerouting(pri, call, dest, original, reason); } void pri_reroute_enable(struct pri *ctrl, int enable) { if (ctrl) { ctrl->deflection_support = enable ? 1 : 0; } } int pri_reroute_call(struct pri *ctrl, q931_call *call, const struct pri_party_id *caller, const struct pri_party_redirecting *deflection, int subscription_option) { const struct q931_party_id *caller_id; struct q931_party_id local_caller; struct q931_party_redirecting reroute; if (!ctrl || !pri_is_call_valid(ctrl, call) || !deflection) { return -1; } if (caller) { /* Convert the caller update information. */ pri_copy_party_id_to_q931(&local_caller, caller); q931_party_id_fixup(ctrl, &local_caller); caller_id = &local_caller; } else { caller_id = NULL; } /* Convert the deflection information. */ q931_party_redirecting_init(&reroute); pri_copy_party_id_to_q931(&reroute.from, &deflection->from); q931_party_id_fixup(ctrl, &reroute.from); pri_copy_party_id_to_q931(&reroute.to, &deflection->to); q931_party_id_fixup(ctrl, &reroute.to); pri_copy_party_id_to_q931(&reroute.orig_called, &deflection->orig_called); q931_party_id_fixup(ctrl, &reroute.orig_called); reroute.reason = deflection->reason; reroute.orig_reason = deflection->orig_reason; if (deflection->count <= 0) { /* * We are deflecting with an unknown count * so assume the count is one. */ reroute.count = 1; } else if (deflection->count < PRI_MAX_REDIRECTS) { reroute.count = deflection->count; } else { reroute.count = PRI_MAX_REDIRECTS; } return send_reroute_request(ctrl, call, caller_id, &reroute, subscription_option); } void pri_cc_enable(struct pri *ctrl, int enable) { if (ctrl) { ctrl->cc_support = enable ? 1 : 0; } } void pri_cc_recall_mode(struct pri *ctrl, int mode) { if (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->cc.option.signaling_retention_req = signaling_retention; } } void pri_cc_retain_signaling_rsp(struct pri *ctrl, int signaling_retention) { if (ctrl) { ctrl->cc.option.signaling_retention_rsp = signaling_retention ? 1 : 0; } } void pri_persistent_layer2_option(struct pri *ctrl, enum pri_layer2_persistence option) { if (!ctrl) { return; } if (PTMP_MODE(ctrl)) { switch (option) { case PRI_L2_PERSISTENCE_DEFAULT: ctrl->l2_persistence = pri_l2_persistence_option_default(ctrl); break; case PRI_L2_PERSISTENCE_KEEP_UP: case PRI_L2_PERSISTENCE_LEAVE_DOWN: ctrl->l2_persistence = option; break; } if (ctrl->l2_persistence == PRI_L2_PERSISTENCE_KEEP_UP) { q921_bring_layer2_up(ctrl); } } } void pri_display_options_send(struct pri *ctrl, unsigned long flags) { if (!ctrl) { return; } if (!flags) { flags = pri_display_options_send_default(ctrl); } ctrl->display_flags.send = flags; } void pri_display_options_receive(struct pri *ctrl, unsigned long flags) { if (!ctrl) { return; } if (!flags) { flags = pri_display_options_receive_default(ctrl); } ctrl->display_flags.receive = flags; } int pri_display_text(struct pri *ctrl, q931_call *call, const struct pri_subcmd_display_txt *display) { if (!ctrl || !display || display->length <= 0 || sizeof(display->text) < display->length || !pri_is_call_valid(ctrl, call)) { /* Parameter sanity checks failed. */ return -1; } return q931_display_text(ctrl, call, display); } void pri_date_time_send_option(struct pri *ctrl, int option) { if (!ctrl) { return; } switch (option) { case PRI_DATE_TIME_SEND_DEFAULT: ctrl->date_time_send = pri_date_time_send_default(ctrl); break; default: case PRI_DATE_TIME_SEND_NO: ctrl->date_time_send = PRI_DATE_TIME_SEND_NO; break; case PRI_DATE_TIME_SEND_DATE: case PRI_DATE_TIME_SEND_DATE_HH: case PRI_DATE_TIME_SEND_DATE_HHMM: case PRI_DATE_TIME_SEND_DATE_HHMMSS: if (NT_MODE(ctrl)) { /* Only networks may send date/time ie. */ ctrl->date_time_send = option; } else { ctrl->date_time_send = PRI_DATE_TIME_SEND_NO; } break; } } libpri-1.4.14/q931.c0000644000000000000000000107061112061424747012436 0ustar rootroot/* * libpri: An implementation of Primary Rate ISDN * * Written by Mark Spencer * * Copyright (C) 2001-2005, Digium, Inc. * All Rights Reserved. */ /* * 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. */ #include "compat.h" #include "libpri.h" #include "pri_internal.h" #include "pri_facility.h" #include #include #include #include #include #include #include #define MAX_MAND_IES 10 struct msgtype { int msgnum; char *name; int mandies[MAX_MAND_IES]; }; static struct msgtype msgs[] = { /* Call establishment messages */ { Q931_ALERTING, "ALERTING" }, { Q931_CALL_PROCEEDING, "CALL PROCEEDING" }, { Q931_CONNECT, "CONNECT" }, { Q931_CONNECT_ACKNOWLEDGE, "CONNECT ACKNOWLEDGE" }, { Q931_PROGRESS, "PROGRESS", { Q931_PROGRESS_INDICATOR } }, { Q931_SETUP, "SETUP", { Q931_BEARER_CAPABILITY, Q931_CHANNEL_IDENT } }, { Q931_SETUP_ACKNOWLEDGE, "SETUP ACKNOWLEDGE" }, /* Call disestablishment messages */ { Q931_DISCONNECT, "DISCONNECT", { Q931_CAUSE } }, { Q931_RELEASE, "RELEASE" }, { Q931_RELEASE_COMPLETE, "RELEASE COMPLETE" }, { Q931_RESTART, "RESTART", { Q931_RESTART_INDICATOR } }, { Q931_RESTART_ACKNOWLEDGE, "RESTART ACKNOWLEDGE", { Q931_RESTART_INDICATOR } }, /* Miscellaneous */ { Q931_STATUS, "STATUS", { Q931_CAUSE, Q931_IE_CALL_STATE } }, { Q931_STATUS_ENQUIRY, "STATUS ENQUIRY" }, { Q931_USER_INFORMATION, "USER_INFORMATION" }, { Q931_SEGMENT, "SEGMENT" }, { Q931_CONGESTION_CONTROL, "CONGESTION CONTROL" }, { Q931_INFORMATION, "INFORMATION" }, { Q931_FACILITY, "FACILITY" }, { Q931_REGISTER, "REGISTER" }, { Q931_NOTIFY, "NOTIFY", { Q931_IE_NOTIFY_IND } }, /* Call Management */ { Q931_HOLD, "HOLD" }, { Q931_HOLD_ACKNOWLEDGE, "HOLD ACKNOWLEDGE" }, { Q931_HOLD_REJECT, "HOLD REJECT", { Q931_CAUSE } }, { Q931_RETRIEVE, "RETRIEVE" }, { Q931_RETRIEVE_ACKNOWLEDGE, "RETRIEVE ACKNOWLEDGE" }, { Q931_RETRIEVE_REJECT, "RETRIEVE REJECT", { Q931_CAUSE } }, { Q931_RESUME, "RESUME" }, { Q931_RESUME_ACKNOWLEDGE, "RESUME ACKNOWLEDGE", { Q931_CHANNEL_IDENT } }, { Q931_RESUME_REJECT, "RESUME REJECT", { Q931_CAUSE } }, { 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); static void nt_ptmp_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct q931_call *c, int *allow_event, int *allow_posthandle); struct msgtype att_maintenance_msgs[] = { { ATT_SERVICE, "SERVICE", { Q931_CHANNEL_IDENT } }, { ATT_SERVICE_ACKNOWLEDGE, "SERVICE ACKNOWLEDGE", { Q931_CHANNEL_IDENT } }, }; struct msgtype national_maintenance_msgs[] = { { NATIONAL_SERVICE, "SERVICE", { Q931_CHANNEL_IDENT } }, { NATIONAL_SERVICE_ACKNOWLEDGE, "SERVICE ACKNOWLEDGE", { Q931_CHANNEL_IDENT } }, }; static int post_handle_maintenance_message(struct pri *ctrl, int protodisc, struct q931_mh *mh, struct q931_call *c); static struct msgtype causes[] = { { PRI_CAUSE_UNALLOCATED, "Unallocated (unassigned) number" }, { PRI_CAUSE_NO_ROUTE_TRANSIT_NET, "No route to specified transmit network" }, { PRI_CAUSE_NO_ROUTE_DESTINATION, "No route to destination" }, { PRI_CAUSE_CHANNEL_UNACCEPTABLE, "Channel unacceptable" }, { PRI_CAUSE_CALL_AWARDED_DELIVERED, "Call awarded and being delivered in an established channel" }, { PRI_CAUSE_NORMAL_CLEARING, "Normal Clearing" }, { PRI_CAUSE_USER_BUSY, "User busy" }, { PRI_CAUSE_NO_USER_RESPONSE, "No user responding" }, { PRI_CAUSE_NO_ANSWER, "User alerting, no answer" }, { PRI_CAUSE_CALL_REJECTED, "Call Rejected" }, { PRI_CAUSE_NUMBER_CHANGED, "Number changed" }, { PRI_CAUSE_NONSELECTED_USER_CLEARING, "Non-selected user clearing" }, { PRI_CAUSE_DESTINATION_OUT_OF_ORDER, "Destination out of order" }, { PRI_CAUSE_INVALID_NUMBER_FORMAT, "Invalid number format" }, { PRI_CAUSE_FACILITY_REJECTED, "Facility rejected" }, { PRI_CAUSE_RESPONSE_TO_STATUS_ENQUIRY, "Response to STATus ENQuiry" }, { PRI_CAUSE_NORMAL_UNSPECIFIED, "Normal, unspecified" }, { PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION, "Circuit/channel congestion" }, { PRI_CAUSE_NETWORK_OUT_OF_ORDER, "Network out of order" }, { PRI_CAUSE_NORMAL_TEMPORARY_FAILURE, "Temporary failure" }, { PRI_CAUSE_SWITCH_CONGESTION, "Switching equipment congestion" }, { PRI_CAUSE_ACCESS_INFO_DISCARDED, "Access information discarded" }, { PRI_CAUSE_REQUESTED_CHAN_UNAVAIL, "Requested channel not available" }, { PRI_CAUSE_PRE_EMPTED, "Pre-empted" }, { PRI_CAUSE_RESOURCE_UNAVAIL_UNSPECIFIED, "Resource unavailable, unspecified" }, { PRI_CAUSE_FACILITY_NOT_SUBSCRIBED, "Facility not subscribed" }, { PRI_CAUSE_OUTGOING_CALL_BARRED, "Outgoing call barred" }, { PRI_CAUSE_INCOMING_CALL_BARRED, "Incoming call barred" }, { PRI_CAUSE_BEARERCAPABILITY_NOTAUTH, "Bearer capability not authorized" }, { PRI_CAUSE_BEARERCAPABILITY_NOTAVAIL, "Bearer capability not available" }, { PRI_CAUSE_SERVICEOROPTION_NOTAVAIL, "Service or option not available, unspecified" }, { PRI_CAUSE_BEARERCAPABILITY_NOTIMPL, "Bearer capability not implemented" }, { PRI_CAUSE_CHAN_NOT_IMPLEMENTED, "Channel not implemented" }, { PRI_CAUSE_FACILITY_NOT_IMPLEMENTED, "Facility not implemented" }, { PRI_CAUSE_INVALID_CALL_REFERENCE, "Invalid call reference value" }, { PRI_CAUSE_IDENTIFIED_CHANNEL_NOTEXIST, "Identified channel does not exist" }, { PRI_CAUSE_INCOMPATIBLE_DESTINATION, "Incompatible destination" }, { PRI_CAUSE_INVALID_MSG_UNSPECIFIED, "Invalid message unspecified" }, { PRI_CAUSE_MANDATORY_IE_MISSING, "Mandatory information element is missing" }, { PRI_CAUSE_MESSAGE_TYPE_NONEXIST, "Message type nonexist." }, { PRI_CAUSE_WRONG_MESSAGE, "Wrong message" }, { PRI_CAUSE_IE_NONEXIST, "Info. element nonexist or not implemented" }, { PRI_CAUSE_INVALID_IE_CONTENTS, "Invalid information element contents" }, { PRI_CAUSE_WRONG_CALL_STATE, "Message not compatible with call state" }, { PRI_CAUSE_RECOVERY_ON_TIMER_EXPIRE, "Recover on timer expiry" }, { PRI_CAUSE_MANDATORY_IE_LENGTH_ERROR, "Mandatory IE length error" }, { PRI_CAUSE_PROTOCOL_ERROR, "Protocol error, unspecified" }, { PRI_CAUSE_INTERWORKING, "Interworking, unspecified" }, }; static struct msgtype facilities[] = { { PRI_NSF_SID_PREFERRED, "CPN (SID) preferred" }, { PRI_NSF_ANI_PREFERRED, "BN (ANI) preferred" }, { PRI_NSF_SID_ONLY, "CPN (SID) only" }, { PRI_NSF_ANI_ONLY, "BN (ANI) only" }, { PRI_NSF_CALL_ASSOC_TSC, "Call Associated TSC" }, { PRI_NSF_NOTIF_CATSC_CLEARING, "Notification of CATSC Clearing or Resource Unavailable" }, { PRI_NSF_OPERATOR, "Operator" }, { PRI_NSF_PCCO, "Pre-subscribed Common Carrier Operator (PCCO)" }, { PRI_NSF_SDN, "SDN (including GSDN)" }, { PRI_NSF_TOLL_FREE_MEGACOM, "Toll Free MEGACOM" }, { PRI_NSF_MEGACOM, "MEGACOM" }, { PRI_NSF_ACCUNET, "ACCUNET Switched Digital Service" }, { PRI_NSF_LONG_DISTANCE_SERVICE, "Long Distance Service" }, { PRI_NSF_INTERNATIONAL_TOLL_FREE, "International Toll Free Service" }, { PRI_NSF_ATT_MULTIQUEST, "AT&T MultiQuest" }, { PRI_NSF_CALL_REDIRECTION_SERVICE, "Call Redirection Service" } }; #define FLAG_WHOLE_INTERFACE 0x01 #define FLAG_PREFERRED 0x02 #define FLAG_EXCLUSIVE 0x04 #define RESET_INDICATOR_CHANNEL 0 #define RESET_INDICATOR_DS1 6 #define RESET_INDICATOR_PRI 7 #define TRANS_MODE_64_CIRCUIT 0x10 #define TRANS_MODE_2x64_CIRCUIT 0x11 #define TRANS_MODE_384_CIRCUIT 0x13 #define TRANS_MODE_1536_CIRCUIT 0x15 #define TRANS_MODE_1920_CIRCUIT 0x17 #define TRANS_MODE_MULTIRATE 0x18 #define TRANS_MODE_PACKET 0x40 #define RATE_ADAPT_56K 0x0f #define LAYER_2_LAPB 0x46 #define LAYER_3_X25 0x66 /* The 4ESS uses a different audio field */ #define PRI_TRANS_CAP_AUDIO_4ESS 0x08 /* Don't forget to update PRI_PROG_xxx at libpri.h */ #define Q931_PROG_CALL_NOT_E2E_ISDN 0x01 #define Q931_PROG_CALLED_NOT_ISDN 0x02 #define Q931_PROG_CALLER_NOT_ISDN 0x03 #define Q931_PROG_CALLER_RETURNED_TO_ISDN 0x04 #define Q931_PROG_INBAND_AVAILABLE 0x08 #define Q931_PROG_DELAY_AT_INTERF 0x0a #define Q931_PROG_INTERWORKING_WITH_PUBLIC 0x10 #define Q931_PROG_INTERWORKING_NO_RELEASE 0x11 #define Q931_PROG_INTERWORKING_NO_RELEASE_PRE_ANSWER 0x12 #define Q931_PROG_INTERWORKING_NO_RELEASE_POST_ANSWER 0x13 #define CODE_CCITT 0x0 #define CODE_INTERNATIONAL 0x1 #define CODE_NATIONAL 0x2 #define CODE_NETWORK_SPECIFIC 0x3 #define LOC_USER 0x0 #define LOC_PRIV_NET_LOCAL_USER 0x1 #define LOC_PUB_NET_LOCAL_USER 0x2 #define LOC_TRANSIT_NET 0x3 #define LOC_PUB_NET_REMOTE_USER 0x4 #define LOC_PRIV_NET_REMOTE_USER 0x5 #define LOC_INTERNATIONAL_NETWORK 0x7 #define LOC_NETWORK_BEYOND_INTERWORKING 0xa static char *ie2str(int ie); #define FUNC_DUMP(name) void (name)(int full_ie, struct pri *pri, q931_ie *ie, int len, char prefix) #define FUNC_RECV(name) int (name)(int full_ie, struct pri *pri, q931_call *call, int msgtype, q931_ie *ie, int len) #define FUNC_SEND(name) int (name)(int full_ie, struct pri *pri, q931_call *call, int msgtype, q931_ie *ie, int len, int order) #if 1 /* Update call state with transition trace. */ #define UPDATE_OURCALLSTATE(ctrl, call, newstate) \ do { \ if (((ctrl)->debug & PRI_DEBUG_Q931_STATE) && (call)->ourcallstate != (newstate)) { \ pri_message((ctrl), \ DBGHEAD "%s %d enters state %d (%s). Hold state: %s\n", \ DBGINFO, ((call) == (call)->master_call) ? "Call" : "Subcall", \ (call)->cr, (newstate), q931_call_state_str(newstate), \ q931_hold_state_str((call)->master_call->hold_state)); \ } \ (call)->ourcallstate = (newstate); \ } while (0) #else /* Update call state with no trace. */ #define UPDATE_OURCALLSTATE(ctrl, call, newstate) (call)->ourcallstate = (newstate) #endif #if 1 /* Update hold state with transition trace. */ #define UPDATE_HOLD_STATE(ctrl, master_call, newstate) \ do { \ if (((ctrl)->debug & PRI_DEBUG_Q931_STATE) \ && (master_call)->hold_state != (newstate)) { \ pri_message((ctrl), \ DBGHEAD "Call %d in state %d (%s) enters Hold state: %s\n", \ DBGINFO, (master_call)->cr, (master_call)->ourcallstate, \ q931_call_state_str((master_call)->ourcallstate), \ q931_hold_state_str(newstate)); \ } \ (master_call)->hold_state = (newstate); \ } while (0) #else /* Update hold state with no trace. */ #define UPDATE_HOLD_STATE(ctrl, master_call, newstate) (master_call)->hold_state = (newstate) #endif struct ie { /* Maximal count of same IEs at the message (0 - any, 1..n - limited) */ int max_count; /* IE code */ int ie; /* IE friendly name */ char *name; /* Dump an IE for debugging (preceed all lines by prefix) */ FUNC_DUMP(*dump); /* Handle IE returns 0 on success, -1 on failure */ FUNC_RECV(*receive); /* Add IE to a message, return the # of bytes added or -1 on failure */ FUNC_SEND(*transmit); }; /*! * \internal * \brief Encode the channel id information to pass to upper level. * * \param call Q.931 call leg * * \return Encoded channel value. */ static int q931_encode_channel(const q931_call *call) { int held_call; int channelno; int ds1no; switch (call->master_call->hold_state) { case Q931_HOLD_STATE_CALL_HELD: case Q931_HOLD_STATE_RETRIEVE_REQ: case Q931_HOLD_STATE_RETRIEVE_IND: held_call = 1 << 18; 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; } return channelno | (ds1no << 8) | (call->ds1explicit << 16) | (call->cis_call << 17) | held_call; } /*! * \brief Check if the given call ptr is valid. * * \param ctrl D channel controller. * \param call Q.931 call leg. * * \retval TRUE if call ptr is valid. * \retval FALSE if call ptr is invalid. */ int q931_is_call_valid(struct pri *ctrl, struct q931_call *call) { struct q931_call *cur; struct q921_link *link; int idx; if (!call) { return 0; } if (!ctrl) { /* Must use suspect ctrl from call ptr. */ if (!call->pri) { /* Definitely a bad call pointer. */ return 0; } ctrl = call->pri; } /* Check real call records. */ for (cur = *ctrl->callpool; cur; cur = cur->next) { if (call == cur) { /* Found it. */ return 1; } if (cur->outboundbroadcast) { /* Check subcalls for call ptr. */ for (idx = 0; idx < ARRAY_LEN(cur->subcalls); ++idx) { if (call == cur->subcalls[idx]) { /* Found it. */ return 1; } } } } /* Check dummy call records. */ for (link = &ctrl->link; link; link = link->next) { if (link->dummy_call == call) { /* Found it. */ return 1; } } /* Well it looks like this is a stale call ptr. */ return 0; } /*! * \brief Check if the given call ptr is valid and gripe if not. * * \param ctrl D channel controller. * \param call Q.931 call leg. * \param func_name Calling function name for debug tracing. (__PRETTY_FUNCTION__) * \param func_line Calling function line number for debug tracing. (__LINE__) * * \retval TRUE if call ptr is valid. * \retval FALSE if call ptr is invalid. */ int q931_is_call_valid_gripe(struct pri *ctrl, struct q931_call *call, const char *func_name, unsigned long func_line) { int res; if (!call) { /* Let's not gripe about this invalid call pointer. */ return 0; } res = q931_is_call_valid(ctrl, call); if (!res) { pri_message(ctrl, "!! %s() line:%lu Called with invalid call ptr (%p)\n", func_name, func_line, call); } return res; } /*! * \brief Initialize the given struct q931_party_name * * \param name Structure to initialize * * \return Nothing */ void q931_party_name_init(struct q931_party_name *name) { name->valid = 0; name->presentation = PRI_PRES_UNAVAILABLE; name->char_set = PRI_CHAR_SET_ISO8859_1; name->str[0] = '\0'; } /*! * \brief Initialize the given struct q931_party_number * * \param number Structure to initialize * * \return Nothing */ void q931_party_number_init(struct q931_party_number *number) { number->valid = 0; number->presentation = PRES_NUMBER_NOT_AVAILABLE; number->plan = (PRI_TON_UNKNOWN << 4) | PRI_NPI_E163_E164; number->str[0] = '\0'; } /*! * \brief Initialize the given struct q931_party_subaddress * * \param subaddress Structure to initialize * * \return Nothing */ void q931_party_subaddress_init(struct q931_party_subaddress *subaddress) { subaddress->valid = 0; subaddress->type = 0; subaddress->odd_even_indicator = 0; subaddress->length = 0; subaddress->data[0] = '\0'; } /*! * \brief Initialize the given struct q931_party_address * * \param address Structure to initialize * * \return Nothing */ void q931_party_address_init(struct q931_party_address *address) { q931_party_number_init(&address->number); q931_party_subaddress_init(&address->subaddress); } /*! * \brief Initialize the given struct q931_party_id * * \param id Structure to initialize * * \return Nothing */ void q931_party_id_init(struct q931_party_id *id) { q931_party_name_init(&id->name); q931_party_number_init(&id->number); q931_party_subaddress_init(&id->subaddress); } /*! * \brief Initialize the given struct q931_party_redirecting * * \param redirecting Structure to initialize * * \return Nothing */ void q931_party_redirecting_init(struct q931_party_redirecting *redirecting) { q931_party_id_init(&redirecting->from); q931_party_id_init(&redirecting->to); q931_party_id_init(&redirecting->orig_called); redirecting->state = Q931_REDIRECTING_STATE_IDLE; redirecting->count = 0; redirecting->orig_reason = PRI_REDIR_UNKNOWN; redirecting->reason = PRI_REDIR_UNKNOWN; } /*! * \brief Compare the left and right party name. * * \param left Left parameter party name. * \param right Right parameter party name. * * \retval < 0 when left < right. * \retval == 0 when left == right. * \retval > 0 when left > right. */ int q931_party_name_cmp(const struct q931_party_name *left, const struct q931_party_name *right) { int cmp; if (!left->valid) { if (!right->valid) { return 0; } return -1; } else if (!right->valid) { return 1; } cmp = left->char_set - right->char_set; if (cmp) { return cmp; } cmp = strcmp(left->str, right->str); if (cmp) { return cmp; } cmp = left->presentation - right->presentation; return cmp; } /*! * \brief Compare the left and right party number. * * \param left Left parameter party number. * \param right Right parameter party number. * * \retval < 0 when left < right. * \retval == 0 when left == right. * \retval > 0 when left > right. */ int q931_party_number_cmp(const struct q931_party_number *left, const struct q931_party_number *right) { int cmp; if (!left->valid) { if (!right->valid) { return 0; } return -1; } else if (!right->valid) { return 1; } cmp = left->plan - right->plan; if (cmp) { return cmp; } cmp = strcmp(left->str, right->str); if (cmp) { return cmp; } cmp = left->presentation - right->presentation; return cmp; } /*! * \brief Compare the left and right party subaddress. * * \param left Left parameter party subaddress. * \param right Right parameter party subaddress. * * \retval < 0 when left < right. * \retval == 0 when left == right. * \retval > 0 when left > right. */ int q931_party_subaddress_cmp(const struct q931_party_subaddress *left, const struct q931_party_subaddress *right) { int cmp; if (!left->valid) { if (!right->valid) { return 0; } return -1; } else if (!right->valid) { return 1; } cmp = left->type - right->type; if (cmp) { return cmp; } cmp = memcmp(left->data, right->data, (left->length < right->length) ? left->length : right->length); if (cmp) { return cmp; } cmp = left->length - right->length; if (cmp) { return cmp; } cmp = left->odd_even_indicator - right->odd_even_indicator; 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. * * \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(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); if (cmp) { return cmp; } cmp = q931_party_name_cmp(&left->name, &right->name); 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. * * \param pri_name PRI party name structure * \param q931_name Q.931 party name structure * * \return Nothing */ void q931_party_name_copy_to_pri(struct pri_party_name *pri_name, const struct q931_party_name *q931_name) { if (q931_name->valid) { pri_name->valid = 1; pri_name->presentation = q931_name->presentation; pri_name->char_set = q931_name->char_set; libpri_copy_string(pri_name->str, q931_name->str, sizeof(pri_name->str)); } else { pri_name->valid = 0; pri_name->presentation = PRI_PRES_UNAVAILABLE; pri_name->char_set = PRI_CHAR_SET_ISO8859_1; pri_name->str[0] = '\0'; } } /*! * \brief Copy the Q.931 party number to the PRI party number structure. * * \param pri_number PRI party number structure * \param q931_number Q.931 party number structure * * \return Nothing */ void q931_party_number_copy_to_pri(struct pri_party_number *pri_number, const struct q931_party_number *q931_number) { if (q931_number->valid) { pri_number->valid = 1; pri_number->presentation = q931_number->presentation; pri_number->plan = q931_number->plan; libpri_copy_string(pri_number->str, q931_number->str, sizeof(pri_number->str)); } else { pri_number->valid = 0; pri_number->presentation = PRES_NUMBER_NOT_AVAILABLE; pri_number->plan = (PRI_TON_UNKNOWN << 4) | PRI_NPI_E163_E164; pri_number->str[0] = '\0'; } } /*! * \brief Copy the Q.931 party subaddress to the PRI party subaddress structure. * * \param pri_subaddress PRI party subaddress structure * \param q931_subaddress Q.931 party subaddress structure * * \return Nothing */ void q931_party_subaddress_copy_to_pri(struct pri_party_subaddress *pri_subaddress, const struct q931_party_subaddress *q931_subaddress) { int length; if (!q931_subaddress->valid) { pri_subaddress->valid = 0; pri_subaddress->type = 0; pri_subaddress->odd_even_indicator = 0; pri_subaddress->length = 0; pri_subaddress->data[0] = '\0'; return; } pri_subaddress->valid = 1; pri_subaddress->type = q931_subaddress->type; pri_subaddress->odd_even_indicator = q931_subaddress->odd_even_indicator; /* * The size of pri_subaddress->data[] is not the same as the size of * q931_subaddress->data[]. */ length = q931_subaddress->length; pri_subaddress->length = length; memcpy(pri_subaddress->data, q931_subaddress->data, length); 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. * * \param pri_id PRI party id structure * \param q931_id Q.931 party id structure * * \return Nothing */ void q931_party_id_copy_to_pri(struct pri_party_id *pri_id, const struct q931_party_id *q931_id) { q931_party_name_copy_to_pri(&pri_id->name, &q931_id->name); q931_party_number_copy_to_pri(&pri_id->number, &q931_id->number); q931_party_subaddress_copy_to_pri(&pri_id->subaddress, &q931_id->subaddress); } /*! * \brief Copy the Q.931 redirecting data to the PRI redirecting structure. * * \param pri_redirecting PRI redirecting structure * \param q931_redirecting Q.931 redirecting structure * * \return Nothing */ void q931_party_redirecting_copy_to_pri(struct pri_party_redirecting *pri_redirecting, const struct q931_party_redirecting *q931_redirecting) { q931_party_id_copy_to_pri(&pri_redirecting->from, &q931_redirecting->from); q931_party_id_copy_to_pri(&pri_redirecting->to, &q931_redirecting->to); q931_party_id_copy_to_pri(&pri_redirecting->orig_called, &q931_redirecting->orig_called); pri_redirecting->count = q931_redirecting->count; pri_redirecting->orig_reason = q931_redirecting->orig_reason; pri_redirecting->reason = q931_redirecting->reason; } /*! * \brief Fixup some values in the q931_party_id that may be objectionable by switches. * * \param ctrl D channel controller. * \param id Party ID to tweak. * * \return Nothing */ void q931_party_id_fixup(const struct pri *ctrl, struct q931_party_id *id) { switch (ctrl->switchtype) { case PRI_SWITCH_DMS100: case PRI_SWITCH_ATT4ESS: /* Doesn't like certain presentation types */ if (id->number.valid && (id->number.presentation & PRI_PRES_RESTRICTION) == PRI_PRES_ALLOWED) { /* i.e., If presentation is allowed it must be a network number */ id->number.presentation = PRES_ALLOWED_NETWORK_NUMBER; } break; default: break; } } /*! * \brief Determine the overall presentation value for the given party. * * \param id Party to determine the overall presentation value. * * \return Overall presentation value for the given party. */ int q931_party_id_presentation(const struct q931_party_id *id) { int number_priority; int number_value; int number_screening; int name_priority; int name_value; /* Determine name presentation priority. */ if (!id->name.valid) { name_value = PRI_PRES_UNAVAILABLE; name_priority = 3; } else { name_value = id->name.presentation & PRI_PRES_RESTRICTION; switch (name_value) { case PRI_PRES_RESTRICTED: name_priority = 0; break; case PRI_PRES_ALLOWED: name_priority = 1; break; case PRI_PRES_UNAVAILABLE: name_priority = 2; break; default: name_value = PRI_PRES_UNAVAILABLE; name_priority = 3; break; } } /* Determine number presentation priority. */ if (!id->number.valid) { number_screening = PRI_PRES_USER_NUMBER_UNSCREENED; number_value = PRI_PRES_UNAVAILABLE; number_priority = 3; } else { number_screening = id->number.presentation & PRI_PRES_NUMBER_TYPE; number_value = id->number.presentation & PRI_PRES_RESTRICTION; switch (number_value) { case PRI_PRES_RESTRICTED: number_priority = 0; break; case PRI_PRES_ALLOWED: number_priority = 1; break; case PRI_PRES_UNAVAILABLE: number_priority = 2; break; default: number_screening = PRI_PRES_USER_NUMBER_UNSCREENED; number_value = PRI_PRES_UNAVAILABLE; number_priority = 3; break; } } /* Select the wining presentation value. */ if (name_priority < number_priority) { number_value = name_value; } if (number_value == PRI_PRES_UNAVAILABLE) { return PRES_NUMBER_NOT_AVAILABLE; } return number_value | number_screening; } /*! * \internal * \brief Get binary buffer contents into the destination buffer. * * \param dst Destination buffer. * \param dst_size Destination buffer sizeof() * \param src Source buffer. * \param src_len Source buffer length to copy. * * \note The destination buffer is nul terminated just in case * the contents are used as a string anyway. * * \retval 0 on success. * \retval -1 on error. The copy did not happen. */ static int q931_memget(unsigned char *dst, size_t dst_size, const unsigned char *src, int src_len) { if (src_len < 0 || src_len > dst_size - 1) { dst[0] = 0; return -1; } memcpy(dst, src, src_len); dst[src_len] = 0; return 0; } /*! * \internal * \brief Get source buffer contents into the destination buffer for a string. * * \param dst Destination buffer. * \param dst_size Destination buffer sizeof() * \param src Source buffer. * \param src_len Source buffer length to copy. * * \note The destination buffer is nul terminated. * \note Nul bytes from the source buffer are not copied. * * \retval 0 on success. * \retval -1 if nul bytes were found in the source data. */ static int q931_strget(unsigned char *dst, size_t dst_size, const unsigned char *src, int src_len) { int saw_nul; if (src_len < 1) { dst[0] = '\0'; return 0; } saw_nul = 0; --dst_size; while (dst_size && src_len) { if (*src) { *dst++ = *src; --dst_size; } else { /* Skip nul bytes in the source buffer. */ saw_nul = -1; } ++src; --src_len; } *dst = '\0'; return saw_nul; } /*! * \internal * \brief Get source buffer contents into the destination buffer for a string. * * \param ctrl D channel controller. * \param ie_name IE name to report nul bytes found in. * \param dst Destination buffer. * \param dst_size Destination buffer sizeof() * \param src Source buffer. * \param src_len Source buffer length to copy. * * \note The destination buffer is nul terminated. * \note Nul bytes from the source buffer are not copied. * * \retval 0 on success. * \retval -1 if nul bytes were found in the source data. */ static int q931_strget_gripe(struct pri *ctrl, const char *ie_name, unsigned char *dst, size_t dst_size, const unsigned char *src, int src_len) { int saw_nul; /* To quietly remove nul octets just comment out the following line. */ #define UNCONDITIONALLY_REPORT_REMOVED_NUL_OCTETS 1 saw_nul = q931_strget(dst, dst_size, src, src_len); if (saw_nul #if !defined(UNCONDITIONALLY_REPORT_REMOVED_NUL_OCTETS) && (ctrl->debug & PRI_DEBUG_Q931_STATE) #endif ) { pri_message(ctrl, "!! Removed nul octets from IE '%s' and returning '%s'.\n", ie_name, dst); } return saw_nul; } /*! * \internal * \brief Clear the display text. * * \param call Q.931 call to clear display text. * * \return Nothing */ static void q931_display_clear(struct q931_call *call) { call->display.text = NULL; } /*! * \internal * \brief Set the display text for the party name. * * \param call Q.931 call to set display text to the party name. * * \return Nothing */ static void q931_display_name_send(struct q931_call *call, const struct q931_party_name *name) { if (name->valid) { switch (name->presentation & PRI_PRES_RESTRICTION) { case PRI_PRES_ALLOWED: call->display.text = (unsigned char *) name->str; call->display.full_ie = 0; call->display.length = strlen(name->str); call->display.char_set = name->char_set; break; default: call->display.text = NULL; break; } } else { call->display.text = NULL; } } /*! * \brief Get the display text into the party name. * * \param call Q.931 call to get the display text into the party name. * \param name Party name to fill if there is display text. * * \note * The party name is not touched if there is no display text. * * \note * The display text is consumed. * * \return TRUE if party name filled. */ int q931_display_name_get(struct q931_call *call, struct q931_party_name *name) { if (!call->display.text) { return 0; } name->valid = 1; name->char_set = call->display.char_set; q931_strget_gripe(call->pri, ie2str(call->display.full_ie), (unsigned char *) name->str, sizeof(name->str), call->display.text, call->display.length); if (name->str[0]) { name->presentation = PRI_PRES_ALLOWED; } else { name->presentation = PRI_PRES_RESTRICTED; } /* Mark the display text as consumed. */ call->display.text = NULL; return 1; } /*! * \internal * \brief Fill a subcmd with any display text. * * \param ctrl D channel controller. * \param call Q.931 call leg. * * \note * The display text is consumed. * * \return Nothing */ static void q931_display_subcmd(struct pri *ctrl, struct q931_call *call) { struct pri_subcommand *subcmd; if (call->display.text && call->display.length && (ctrl->display_flags.receive & PRI_DISPLAY_OPTION_TEXT)) { subcmd = q931_alloc_subcommand(ctrl); if (subcmd) { /* Setup display text subcommand */ subcmd->cmd = PRI_SUBCMD_DISPLAY_TEXT; subcmd->u.display.char_set = call->display.char_set; q931_strget_gripe(ctrl, ie2str(call->display.full_ie), (unsigned char *) subcmd->u.display.text, sizeof(subcmd->u.display.text), call->display.text, call->display.length); subcmd->u.display.length = strlen(subcmd->u.display.text); } } /* Mark the display text as consumed. */ call->display.text = NULL; } /*! * \brief Find the winning subcall if it exists or current call if not outboundbroadcast. * * \param call Starting Q.931 call record of search. * * \retval winning-call or given call if not outboundbroadcast. * \retval NULL if no winning call yet. */ struct q931_call *q931_find_winning_call(struct q931_call *call) { struct q931_call *master; master = call->master_call; if (master->outboundbroadcast) { /* We have potential subcalls. Now get the winning call if declared yet. */ if (master->pri_winner < 0) { /* Winner not declared yet.*/ call = NULL; } else { call = master->subcalls[master->pri_winner]; } } return call; } /*! * \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; } struct pri_subcommand *q931_alloc_subcommand(struct pri *ctrl) { if (ctrl->subcmds.counter_subcmd < PRI_MAX_SUBCOMMANDS) { return &ctrl->subcmds.subcmd[ctrl->subcmds.counter_subcmd++]; } pri_error(ctrl, "ERROR: Too many facility subcommands\n"); return NULL; } static char *code2str(int code, struct msgtype *codes, int max) { int x; for (x=0;x 32) len = 32; for (x=1;x<=len;x++) res[x-1] = b & (1 << (len - x)) ? '1' : '0'; return res; } static int receive_channel_id(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { int x; int pos = 0; int need_extended_channel_octets;/*!< TRUE if octets 3.2 and 3.3 need to be present. */ call->restart.count = 0; if (ie->data[0] & 0x08) { call->chanflags = FLAG_EXCLUSIVE; } else { call->chanflags = FLAG_PREFERRED; } need_extended_channel_octets = 0; if (ie->data[0] & 0x20) { /* PRI encoded interface type */ switch (ie->data[0] & 0x03) { case 0x00: /* No channel */ call->channelno = 0; call->chanflags = FLAG_PREFERRED; break; case 0x01: /* As indicated in following octets */ need_extended_channel_octets = 1; break; case 0x03: /* Any channel */ call->chanflags = FLAG_PREFERRED; break; default: pri_error(ctrl, "!! Unexpected Channel selection %d\n", ie->data[0] & 0x03); return -1; } } else { /* BRI encoded interface type */ switch (ie->data[0] & 0x03) { case 0x00: /* No channel */ call->channelno = 0; call->chanflags = FLAG_PREFERRED; break; case 0x03: /* Any channel */ call->chanflags = FLAG_PREFERRED; break; default: /* Specified B channel (B1 or B2) */ call->channelno = ie->data[0] & 0x03; break; } } pos++; if (ie->data[0] & 0x40) { /* DS1 specified -- stop here */ call->ds1no = ie->data[1] & 0x7f; call->ds1explicit = 1; pos++; } else { call->ds1explicit = 0; } if (ie->data[0] & 0x04) { /* D channel call. Signaling only. */ call->cis_call = 1; call->chanflags = FLAG_EXCLUSIVE;/* For safety mark this channel as exclusive. */ call->channelno = 0; return 0; } if (need_extended_channel_octets && pos + 2 < len) { /* More coming */ if ((ie->data[pos] & 0x0f) != 3) { /* Channel type/mapping is not for B channel units. */ pri_error(ctrl, "!! Unexpected Channel Type %d\n", ie->data[pos] & 0x0f); return -1; } if ((ie->data[pos] & 0x60) != 0) { pri_error(ctrl, "!! Invalid CCITT coding %d\n", (ie->data[pos] & 0x60) >> 5); return -1; } if (ie->data[pos] & 0x10) { /* Expect Slot Map */ call->slotmap = 0; pos++; call->slotmap_size = (ie->len - pos > 3) ? 1 : 0; for (x = 0; x < (call->slotmap_size ? 4 : 3); ++x) { call->slotmap <<= 8; call->slotmap |= ie->data[x + pos]; } if (msgtype == Q931_RESTART) { int bit; /* Convert the slotmap to a channel list for RESTART support. */ for (bit = 0; bit < ARRAY_LEN(call->restart.chan_no); ++bit) { if (call->slotmap & (1UL << bit)) { call->restart.chan_no[call->restart.count++] = bit + (call->slotmap_size ? 0 : 1); } } } } else { pos++; /* Only expect a particular channel */ call->channelno = ie->data[pos] & 0x7f; if (ctrl->chan_mapping_logical && call->channelno > 15) { call->channelno++; } if (msgtype == Q931_RESTART) { /* Read in channel list for RESTART support. */ while (call->restart.count < ARRAY_LEN(call->restart.chan_no)) { int chan_no; chan_no = ie->data[pos] & 0x7f; if (ctrl->chan_mapping_logical && chan_no > 15) { ++chan_no; } call->restart.chan_no[call->restart.count++] = chan_no; if (ie->data[pos++] & 0x80) { /* Channel list finished. */ break; } if (ie->len <= pos) { /* No more ie contents. */ break; } } } } } return 0; } static int transmit_channel_id(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { int pos = 0; /* We are ready to transmit single IE only */ if (order > 1) return 0; if (call->cis_call) { /* * Read the standards docs to figure this out. * Q.SIG ECMA-165 section 7.3 * ITU Q.931 section 4.5.13 */ ie->data[pos++] = ctrl->bri ? 0x8c : 0xac; return pos + 2; } /* Start with standard stuff */ if (ctrl->switchtype == PRI_SWITCH_GR303_TMC) ie->data[pos] = 0x69; else if (ctrl->bri) { ie->data[pos] = 0x80; ie->data[pos] |= (call->channelno & 0x3); } else { /* PRI */ if (call->slotmap != -1 || (call->chanflags & FLAG_WHOLE_INTERFACE)) { /* Specified channel */ ie->data[pos] = 0xa1; } else if (call->channelno < 0 || call->channelno == 0xff) { /* Any channel */ ie->data[pos] = 0xa3; } else if (!call->channelno) { /* No channel */ ie->data[pos] = 0xa0; } else { /* Specified channel */ ie->data[pos] = 0xa1; } } if (call->chanflags & FLAG_EXCLUSIVE) { /* Channel is exclusive */ if (!(ie->data[pos] & 0x03)) { /* An exclusive no channel id ie is to be discarded. */ return 0; } ie->data[pos] |= 0x08; } else if (!call->chanflags) { /* Don't need this IE */ return 0; } if (!ctrl->bri && (((ctrl->switchtype != PRI_SWITCH_QSIG) && (call->ds1no > 0)) || call->ds1explicit)) { /* We are specifying the interface. Octet 3.1 */ ie->data[pos++] |= 0x40; ie->data[pos++] = 0x80 | call->ds1no; } else { ++pos; } if (!ctrl->bri && (ie->data[0] & 0x03) == 0x01 /* Specified channel */ && !(call->chanflags & FLAG_WHOLE_INTERFACE)) { /* The 3.2 and 3.3 octets need to be present */ ie->data[pos] = 0x83; if (0 < call->channelno && call->channelno != 0xff) { /* Channel number specified and preferred over slot map if we have one. */ ++pos; if (msgtype == Q931_RESTART_ACKNOWLEDGE && call->restart.count) { int chan_no; int idx; /* Build RESTART_ACKNOWLEDGE channel list */ for (idx = 0; idx < call->restart.count; ++idx) { chan_no = call->restart.chan_no[idx]; if (ctrl->chan_mapping_logical && chan_no > 16) { --chan_no; } if (call->restart.count <= idx + 1) { /* Last channel list channel. */ chan_no |= 0x80; } ie->data[pos++] = chan_no; } } else { if (ctrl->chan_mapping_logical && call->channelno > 16) { ie->data[pos++] = 0x80 | (call->channelno - 1); } else { ie->data[pos++] = 0x80 | call->channelno; } } } else if (call->slotmap != -1) { int octet; /* We have to send a slot map */ ie->data[pos++] |= 0x10; for (octet = call->slotmap_size ? 4 : 3; octet--;) { ie->data[pos++] = (call->slotmap >> (8 * octet)) & 0xff; } } else { pri_error(ctrl, "XXX We need either a channelno or slotmap but have neither!\n"); /* Discard this malformed ie. */ return 0; } } return pos + 2; } static void dump_channel_id(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { int pos; int x; int res; static const char *msg_chan_sel[] = { "No channel selected", "B1 channel", "B2 channel", "Any channel selected", "No channel selected", "As indicated in following octets", "Reserved", "Any channel selected" }; pri_message(ctrl, "%c %s (len=%2d) [ Ext: %d IntID: %s %s Spare: %d %s Dchan: %d\n", prefix, ie2str(full_ie), len, (ie->data[0] & 0x80) ? 1 : 0, (ie->data[0] & 0x40) ? "Explicit" : "Implicit", (ie->data[0] & 0x20) ? "Other(PRI)" : "BRI", (ie->data[0] & 0x10) ? 1 : 0, (ie->data[0] & 0x08) ? "Exclusive" : "Preferred", (ie->data[0] & 0x04) ? 1 : 0); pri_message(ctrl, "%c ChanSel: %s\n", prefix, msg_chan_sel[(ie->data[0] & 0x03) | ((ie->data[0] >> 3) & 0x04)]); pos = 1; len -= 2; if (ie->data[0] & 0x40) { /* Explicitly defined DS1 */ do { pri_message(ctrl, "%c Ext: %d DS1 Identifier: %d \n", prefix, (ie->data[pos] & 0x80) >> 7, ie->data[pos] & 0x7f); ++pos; } while (!(ie->data[pos - 1] & 0x80) && pos < len); } else { /* Implicitly defined DS1 */ } if (pos < len) { /* Still more information here */ pri_message(ctrl, "%c Ext: %d Coding: %d %s Specified Channel Type: %d\n", prefix, (ie->data[pos] & 0x80) >> 7, (ie->data[pos] & 60) >> 5, (ie->data[pos] & 0x10) ? "Slot Map" : "Number", ie->data[pos] & 0x0f); ++pos; } if (pos < len) { if (!(ie->data[pos - 1] & 0x10)) { /* Number specified */ do { pri_message(ctrl, "%c Ext: %d Channel: %d Type: %s%c\n", prefix, (ie->data[pos] & 0x80) >> 7, (ie->data[pos]) & 0x7f, pritype(ctrl->localtype), (pos + 1 < len) ? ' ' : ']'); ++pos; } while (pos < len); } else { /* Map specified */ res = 0; x = 0; do { res <<= 8; res |= ie->data[pos++]; ++x; } while (pos < len); pri_message(ctrl, "%c Map len: %d Map: %s ]\n", prefix, x, binary(res, x << 3)); } } else { pri_message(ctrl, "%c ]\n", prefix); } } static char *ri2str(int ri) { static struct msgtype ris[] = { { 0, "Indicated Channel" }, { 6, "Single DS1 Facility" }, { 7, "All DS1 Facilities" }, }; return code2str(ri, ris, sizeof(ris) / sizeof(ris[0])); } static void dump_restart_indicator(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { pri_message(ctrl, "%c %s (len=%2d) [ Ext: %d Spare: %d Resetting %s (%d) ]\n", prefix, ie2str(full_ie), len, (ie->data[0] & 0x80) >> 7, (ie->data[0] & 0x78) >> 3, ri2str(ie->data[0] & 0x7), ie->data[0] & 0x7); } static int receive_restart_indicator(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { /* Pretty simple */ call->ri = ie->data[0] & 0x7; return 0; } static int transmit_restart_indicator(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { /* Pretty simple */ switch(call->ri) { case 0: case 6: case 7: ie->data[0] = 0x80 | (call->ri & 0x7); break; case 5: /* Switch compatibility */ ie->data[0] = 0xA0 | (call->ri & 0x7); break; default: pri_error(ctrl, "!! Invalid restart indicator value %d\n", call->ri); return-1; } return 3; } static char *redirection_reason2str(int mode) { static struct msgtype modes[] = { { PRI_REDIR_UNKNOWN, "Unknown" }, { PRI_REDIR_FORWARD_ON_BUSY, "Forwarded on busy" }, { PRI_REDIR_FORWARD_ON_NO_REPLY, "Forwarded on no reply" }, { PRI_REDIR_DEFLECTION, "Call deflected" }, { PRI_REDIR_DTE_OUT_OF_ORDER, "Called DTE out of order" }, { PRI_REDIR_FORWARDED_BY_DTE, "Forwarded by called DTE" }, { PRI_REDIR_UNCONDITIONAL, "Forwarded unconditionally" }, }; return code2str(mode, modes, sizeof(modes) / sizeof(modes[0])); } static char *cap2str(int mode) { static struct msgtype modes[] = { { PRI_TRANS_CAP_SPEECH, "Speech" }, { PRI_TRANS_CAP_DIGITAL, "Unrestricted digital information" }, { PRI_TRANS_CAP_RESTRICTED_DIGITAL, "Restricted digital information" }, { PRI_TRANS_CAP_3_1K_AUDIO, "3.1kHz audio" }, { PRI_TRANS_CAP_DIGITAL_W_TONES, "Unrestricted digital information with tones/announcements" }, { PRI_TRANS_CAP_VIDEO, "Video" }, { PRI_TRANS_CAP_AUDIO_4ESS, "3.1khz audio (4ESS)" }, }; return code2str(mode, modes, sizeof(modes) / sizeof(modes[0])); } static char *mode2str(int mode) { static struct msgtype modes[] = { { TRANS_MODE_64_CIRCUIT, "64kbps, circuit-mode" }, { TRANS_MODE_2x64_CIRCUIT, "2x64kbps, circuit-mode" }, { TRANS_MODE_384_CIRCUIT, "384kbps, circuit-mode" }, { TRANS_MODE_1536_CIRCUIT, "1536kbps, circuit-mode" }, { TRANS_MODE_1920_CIRCUIT, "1920kbps, circuit-mode" }, { TRANS_MODE_MULTIRATE, "Multirate (Nx64kbps)" }, { TRANS_MODE_PACKET, "Packet Mode" }, }; return code2str(mode, modes, sizeof(modes) / sizeof(modes[0])); } static char *l12str(int proto) { static struct msgtype protos[] = { { PRI_LAYER_1_ITU_RATE_ADAPT, "V.110 Rate Adaption" }, { PRI_LAYER_1_ULAW, "u-Law" }, { PRI_LAYER_1_ALAW, "A-Law" }, { PRI_LAYER_1_G721, "G.721 ADPCM" }, { PRI_LAYER_1_G722_G725, "G.722/G.725 7kHz Audio" }, { PRI_LAYER_1_H223_H245, "H.223/H.245 Multimedia" }, { PRI_LAYER_1_NON_ITU_ADAPT, "Non-ITU Rate Adaption" }, { PRI_LAYER_1_V120_RATE_ADAPT, "V.120 Rate Adaption" }, { PRI_LAYER_1_X31_RATE_ADAPT, "X.31 Rate Adaption" }, }; return code2str(proto, protos, sizeof(protos) / sizeof(protos[0])); } static char *ra2str(int proto) { static struct msgtype protos[] = { { PRI_RATE_ADAPT_9K6, "9.6 kbit/s" }, }; return code2str(proto, protos, sizeof(protos) / sizeof(protos[0])); } static char *l22str(int proto) { static struct msgtype protos[] = { { LAYER_2_LAPB, "LAPB" }, }; return code2str(proto, protos, sizeof(protos) / sizeof(protos[0])); } static char *l32str(int proto) { static struct msgtype protos[] = { { LAYER_3_X25, "X.25" }, }; return code2str(proto, protos, sizeof(protos) / sizeof(protos[0])); } static char *int_rate2str(int proto) { static struct msgtype protos[] = { { PRI_INT_RATE_8K, "8 kbit/s" }, { PRI_INT_RATE_16K, "16 kbit/s" }, { PRI_INT_RATE_32K, "32 kbit/s" }, }; return code2str(proto, protos, sizeof(protos) / sizeof(protos[0])); } 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 %s (len=%2d) [ Ext: %d Coding-Std: %d Info transfer capability: %s (%d)\n", prefix, ie2str(full_ie), 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); /* octet 4.1 exists if mode/rate is multirate */ if ((ie->data[1] & 0x7f) == 0x18) { pri_message(ctrl, "%c Ext: %d Transfer rate multiplier: %d x 64\n", prefix, (ie->data[2] & 0x80) >> 7, ie->data[2] & 0x7f); pos++; } /* don't count the IE num and length as part of the data */ len -= 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 */ u_int8_t layer1 = ie->data[pos] & 0x7f; pri_message(ctrl, "%c User information layer 1: %s (%d)\n", prefix, l12str(layer1), layer1); pos++; /* octet 5a? */ if (pos < len && !(ie->data[pos-1] & 0x80)) { int ra = ie->data[pos] & 0x7f; pri_message(ctrl, "%c Async: %d, Negotiation: %d, " "User rate: %s (%#x)\n", prefix, ra & PRI_RATE_ADAPT_ASYNC ? 1 : 0, ra & PRI_RATE_ADAPT_NEGOTIATION_POSS ? 1 : 0, ra2str(ra & PRI_RATE_USER_RATE_MASK), ra & PRI_RATE_USER_RATE_MASK); pos++; } /* octet 5b? */ if (pos < len && !(ie->data[pos-1] & 0x80)) { u_int8_t data = ie->data[pos]; if (layer1 == PRI_LAYER_1_ITU_RATE_ADAPT) { pri_message(ctrl, "%c Intermediate rate: %s (%d), " "NIC on Tx: %d, NIC on Rx: %d, " "Flow control on Tx: %d, " "Flow control on Rx: %d\n", prefix, int_rate2str((data & 0x60)>>5), (data & 0x60)>>5, (data & 0x10)?1:0, (data & 0x08)?1:0, (data & 0x04)?1:0, (data & 0x02)?1:0); } else if (layer1 == PRI_LAYER_1_V120_RATE_ADAPT) { pri_message(ctrl, "%c Hdr: %d, Multiframe: %d, Mode: %d, " "LLI negot: %d, Assignor: %d, " "In-band neg: %d\n", prefix, (data & 0x40)?1:0, (data & 0x20)?1:0, (data & 0x10)?1:0, (data & 0x08)?1:0, (data & 0x04)?1:0, (data & 0x02)?1:0); } else { pri_message(ctrl, "%c Unknown octet 5b: 0x%x\n", prefix, data); } pos++; } /* octet 5c? */ if (pos < len && !(ie->data[pos-1] & 0x80)) { u_int8_t data = ie->data[pos]; const char *stop_bits[] = {"?","1","1.5","2"}; const char *data_bits[] = {"?","5","7","8"}; const char *parity[] = {"Odd","?","Even","None", "zero","one","?","?"}; pri_message(ctrl, "%c Stop bits: %s, data bits: %s, " "parity: %s\n", prefix, stop_bits[(data & 0x60) >> 5], data_bits[(data & 0x18) >> 3], parity[(data & 0x7)]); pos++; } /* octet 5d? */ if (pos < len && !(ie->data[pos-1] & 0x80)) { u_int8_t data = ie->data[pos]; pri_message(ctrl, "%c Duplex mode: %d, modem type: %d\n", prefix, (data & 0x40) ? 1 : 0,data & 0x3F); pos++; } } /* Look for octet 6; this is identified by bits 5,6 == 10 */ if (pos < len && (ie->data[pos] & 0x60) == 0x40) { pri_message(ctrl, "%c User information layer 2: %s (%d)\n", prefix, l22str(ie->data[pos] & 0x1f), ie->data[pos] & 0x1f); pos++; } /* Look for octet 7; this is identified by bits 5,6 == 11 */ if (pos < len && (ie->data[pos] & 0x60) == 0x60) { pri_message(ctrl, "%c User information layer 3: %s (%d)\n", prefix, l32str(ie->data[pos] & 0x1f), ie->data[pos] & 0x1f); pos++; /* octets 7a and 7b? */ if (pos + 1 < len && !(ie->data[pos-1] & 0x80) && !(ie->data[pos] & 0x80)) { unsigned int proto; proto = ((ie->data[pos] & 0xF) << 4 ) | (ie->data[pos+1] & 0xF); pri_message(ctrl, "%c Network layer: 0x%x\n", prefix, proto ); pos += 2; } } } static int receive_bearer_capability(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { int pos = 2; 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 if mode/rate is multirate */ if (call->bc.transmoderate == TRANS_MODE_MULTIRATE) { call->bc.transmultiple = ie->data[pos++] & 0x7f; } /* 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++; /* 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; } static int transmit_bearer_capability(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { int tc; int pos; /* We are ready to transmit single IE only */ if(order > 1) return 0; if (ctrl->link.next && !ctrl->bri) { /* Bearer capability is *hard coded* in GR-303 */ ie->data[0] = 0x88; ie->data[1] = 0x90; return 4; } if (call->cis_call) { ie->data[0] = 0xa8; ie->data[1] = 0x80; return 4; } tc = call->bc.transcapability; ie->data[0] = 0x80 | tc; ie->data[1] = call->bc.transmoderate | 0x80; pos = 2; /* octet 4.1 exists if mode/rate is multirate */ 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->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->bc.transmoderate == TRANS_MODE_64_CIRCUIT)) { /* Unrestricted digital 64k data calls don't use user layer 2/3 */ return 4; } 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->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->bc.userl1 & 0x1f); 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->bc.rateadaption | 0x80; } } if (call->bc.userl2 != -1) ie->data[pos++] = 0xc0 | (call->bc.userl2 & 0x1f); if (call->bc.userl3 != -1) ie->data[pos++] = 0xe0 | (call->bc.userl3 & 0x1f); return pos + 2; } char *pri_plan2str(int plan) { static struct msgtype plans[] = { { PRI_INTERNATIONAL_ISDN, "International number in ISDN" }, { PRI_NATIONAL_ISDN, "National number in ISDN" }, { PRI_LOCAL_ISDN, "Local number in ISDN" }, { PRI_PRIVATE, "Private numbering plan" }, { PRI_UNKNOWN, "Unknown numbering plan" }, }; return code2str(plan, plans, sizeof(plans) / sizeof(plans[0])); } static char *npi2str(int plan) { static struct msgtype plans[] = { { PRI_NPI_UNKNOWN, "Unknown Number Plan" }, { PRI_NPI_E163_E164, "ISDN/Telephony Numbering Plan (E.164/E.163)" }, { PRI_NPI_X121, "Data Numbering Plan (X.121)" }, { PRI_NPI_F69, "Telex Numbering Plan (F.69)" }, { PRI_NPI_NATIONAL, "National Standard Numbering Plan" }, { PRI_NPI_PRIVATE, "Private Numbering Plan" }, { PRI_NPI_RESERVED, "Reserved Number Plan" }, }; return code2str(plan, plans, sizeof(plans) / sizeof(plans[0])); } static char *ton2str(int plan) { static struct msgtype plans[] = { { PRI_TON_UNKNOWN, "Unknown Number Type" }, { PRI_TON_INTERNATIONAL, "International Number" }, { PRI_TON_NATIONAL, "National Number" }, { PRI_TON_NET_SPECIFIC, "Network Specific Number" }, { PRI_TON_SUBSCRIBER, "Subscriber Number" }, { PRI_TON_ABBREVIATED, "Abbreviated number" }, { PRI_TON_RESERVED, "Reserved Number" }, }; return code2str(plan, plans, sizeof(plans) / sizeof(plans[0])); } static char *subaddrtype2str(int plan) { static struct msgtype plans[] = { { 0, "NSAP (X.213/ISO 8348 AD2)" }, { 2, "User Specified" }, }; return code2str(plan, plans, sizeof(plans) / sizeof(plans[0])); } /* Calling Party Category (Definitions from Q.763) */ static char *cpc2str(int plan) { static struct msgtype plans[] = { { 0, "Unknown Source" }, { 1, "Operator French" }, { 2, "Operator English" }, { 3, "Operator German" }, { 4, "Operator Russian" }, { 5, "Operator Spanish" }, { 6, "Mut Agree Chinese" }, { 7, "Mut Agreement" }, { 8, "Mut Agree Japanese" }, { 9, "National Operator" }, { 10, "Ordinary Toll Caller" }, { 11, "Priority Toll Caller" }, { 12, "Data Call" }, { 13, "Test Call" }, { 14, "Spare" }, { 15, "Pay Phone" }, }; return code2str(plan, plans, ARRAY_LEN(plans)); } char *pri_pres2str(int pres) { static struct msgtype press[] = { { PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED, "Presentation allowed, User-provided, not screened" }, { PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_PASSED_SCREEN, "Presentation allowed, User-provided, verified and passed" }, { PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_FAILED_SCREEN, "Presentation allowed, User-provided, verified and failed" }, { PRI_PRES_ALLOWED | PRI_PRES_NETWORK_NUMBER, "Presentation allowed, Network provided" }, { PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED, "Presentation restricted, User-provided, not screened" }, { PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_PASSED_SCREEN, "Presentation restricted, User-provided, verified and passed" }, { PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_FAILED_SCREEN, "Presentation restricted, User-provided, verified and failed" }, { PRI_PRES_RESTRICTED | PRI_PRES_NETWORK_NUMBER, "Presentation restricted, Network provided" }, { PRI_PRES_UNAVAILABLE | PRI_PRES_USER_NUMBER_UNSCREENED, "Number not available, User-provided, not screened" }, { PRI_PRES_UNAVAILABLE | PRI_PRES_USER_NUMBER_PASSED_SCREEN, "Number not available, User-provided, verified and passed" }, { PRI_PRES_UNAVAILABLE | PRI_PRES_USER_NUMBER_FAILED_SCREEN, "Number not available, User-provided, verified and failed" }, { PRI_PRES_UNAVAILABLE | PRI_PRES_NETWORK_NUMBER, "Number not available, Network provided" }, { PRI_PRES_RESERVED | PRI_PRES_USER_NUMBER_UNSCREENED, "Reserved, User-provided, not screened" }, { PRI_PRES_RESERVED | PRI_PRES_USER_NUMBER_PASSED_SCREEN, "Reserved, User-provided, verified and passed" }, { PRI_PRES_RESERVED | PRI_PRES_USER_NUMBER_FAILED_SCREEN, "Reserved, User-provided, verified and failed" }, { PRI_PRES_RESERVED | PRI_PRES_NETWORK_NUMBER, "Reserved, Network provided" }, }; pres &= (PRI_PRES_RESTRICTION | PRI_PRES_NUMBER_TYPE); return code2str(pres, press, sizeof(press) / sizeof(press[0])); } static void q931_get_subaddr_specific(unsigned char *num, int maxlen, unsigned char *src, int len, char oddflag) { /* User Specified */ int x; char *ptr = (char *) num; if (len <= 0) { num[0] = '\0'; return; } if (((len * 2) + 1) > maxlen) { len = (maxlen / 2) - 1; } for (x = 0; x < (len - 1); ++x) { ptr += sprintf(ptr, "%02x", src[x]); } if (oddflag) { /* ODD */ sprintf(ptr, "%01x", (src[len - 1]) >> 4); } else { /* EVEN */ sprintf(ptr, "%02x", src[len - 1]); } } static int transmit_subaddr_helper(int full_ie, struct pri *ctrl, struct q931_party_subaddress *q931_subaddress, int msgtype, q931_ie *ie, int offset, int len, int order) { size_t datalen; if (!q931_subaddress->valid) { return 0; } datalen = q931_subaddress->length; if (!q931_subaddress->type) { /* 0 = NSAP */ /* 0 = Odd/Even indicator */ ie->data[0] = 0x80; } else { /* 2 = User Specified */ ie->data[0] = q931_subaddress->odd_even_indicator ? 0xA8 : 0xA0; } memcpy(ie->data + offset, q931_subaddress->data, datalen); return datalen + (offset + 2); } static int receive_subaddr_helper(int full_ie, struct pri *ctrl, struct q931_party_subaddress *q931_subaddress, int msgtype, q931_ie *ie, int offset, int len) { if (len <= 0) { return -1; } q931_subaddress->valid = 1; q931_subaddress->length = len; /* type: 0 = NSAP, 2 = User Specified */ q931_subaddress->type = ((ie->data[0] & 0x70) >> 4); q931_subaddress->odd_even_indicator = (ie->data[0] & 0x08) ? 1 : 0; q931_memget(q931_subaddress->data, sizeof(q931_subaddress->data), ie->data + offset, len); return 0; } static void dump_subaddr_helper(int full_ie, struct pri *ctrl, q931_ie *ie, int offset, int len, int datalen, char prefix) { unsigned char cnum[256]; if (!(ie->data[0] & 0x70)) { /* NSAP Get it as a string for dump display purposes only. */ q931_strget(cnum, sizeof(cnum), ie->data + offset, datalen); } else { /* User Specified */ q931_get_subaddr_specific(cnum, sizeof(cnum), ie->data + offset, datalen, ie->data[0] & 0x08); } pri_message(ctrl, "%c %s (len=%2d) [ Ext: %d Type: %s (%d) O: %d '%s' ]\n", prefix, ie2str(full_ie), len, ie->data[0] >> 7, subaddrtype2str((ie->data[0] & 0x70) >> 4), (ie->data[0] & 0x70) >> 4, (ie->data[0] & 0x08) >> 3, cnum); } static void dump_called_party_number(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { unsigned char cnum[256]; q931_strget(cnum, sizeof(cnum), ie->data + 1, len - 3); pri_message(ctrl, "%c %s (len=%2d) [ Ext: %d TON: %s (%d) NPI: %s (%d) '%s' ]\n", prefix, ie2str(full_ie), len, ie->data[0] >> 7, ton2str((ie->data[0] >> 4) & 0x07), (ie->data[0] >> 4) & 0x07, npi2str(ie->data[0] & 0x0f), ie->data[0] & 0x0f, cnum); } static void dump_called_party_subaddr(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { dump_subaddr_helper(full_ie, ctrl, ie, 1, len, len - 3, prefix); } static void dump_calling_party_number(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { unsigned char cnum[256]; if (ie->data[0] & 0x80) { q931_strget(cnum, sizeof(cnum), ie->data + 1, len - 3); } else { q931_strget(cnum, sizeof(cnum), ie->data + 2, len - 4); } pri_message(ctrl, "%c %s (len=%2d) [ Ext: %d TON: %s (%d) NPI: %s (%d)\n", prefix, ie2str(full_ie), len, ie->data[0] >> 7, ton2str((ie->data[0] >> 4) & 0x07), (ie->data[0] >> 4) & 0x07, npi2str(ie->data[0] & 0x0f), ie->data[0] & 0x0f); if (ie->data[0] & 0x80) { pri_message(ctrl, "%c Presentation: %s (%d) '%s' ]\n", prefix, pri_pres2str(0), 0, cnum); } else { pri_message(ctrl, "%c Presentation: %s (%d) '%s' ]\n", prefix, pri_pres2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f, cnum); } } static void dump_calling_party_subaddr(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { dump_subaddr_helper(full_ie, ctrl, ie, 1, len, len - 3, prefix); } static void dump_calling_party_category(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { pri_message(ctrl, "%c %s (len=%2d) [ Ext: %d Cat: %s (%d) ]\n", prefix, ie2str(full_ie), len, ie->data[0] >> 7, cpc2str(ie->data[0] & 0x0F), ie->data[0] & 0x0F); } static void dump_redirecting_number(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { unsigned char cnum[256]; int i = 0; /* To follow Q.931 (4.5.1), we must search for start of octet 4 by walking through all bytes until one with ext bit (8) set to 1 */ do { switch(i) { case 0: /* Octet 3 */ pri_message(ctrl, "%c %s (len=%2d) [ Ext: %d TON: %s (%d) NPI: %s (%d)", prefix, ie2str(full_ie), len, ie->data[0] >> 7, ton2str((ie->data[0] >> 4) & 0x07), (ie->data[0] >> 4) & 0x07, npi2str(ie->data[0] & 0x0f), ie->data[0] & 0x0f); break; case 1: /* Octet 3a */ pri_message(ctrl, "\n"); pri_message(ctrl, "%c Ext: %d Presentation: %s (%d)", prefix, ie->data[1] >> 7, pri_pres2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f); break; case 2: /* Octet 3b */ pri_message(ctrl, "\n"); pri_message(ctrl, "%c Ext: %d Reason: %s (%d)", prefix, ie->data[2] >> 7, redirection_reason2str(ie->data[2] & 0x7f), ie->data[2] & 0x7f); break; } } while (!(ie->data[i++] & 0x80)); q931_strget(cnum, sizeof(cnum), ie->data + i, ie->len - i); pri_message(ctrl, " '%s' ]\n", cnum); } static void dump_redirection_number(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { unsigned char cnum[256]; int i = 0; /* To follow Q.931 (4.5.1), we must search for start of octet 4 by walking through all bytes until one with ext bit (8) set to 1 */ do { switch (i) { case 0: /* Octet 3 */ pri_message(ctrl, "%c %s (len=%2d) [ Ext: %d TON: %s (%d) NPI: %s (%d)", prefix, ie2str(full_ie), len, ie->data[0] >> 7, ton2str((ie->data[0] >> 4) & 0x07), (ie->data[0] >> 4) & 0x07, npi2str(ie->data[0] & 0x0f), ie->data[0] & 0x0f); break; case 1: /* Octet 3a */ pri_message(ctrl, "\n"); pri_message(ctrl, "%c Ext: %d Presentation: %s (%d)", prefix, ie->data[1] >> 7, pri_pres2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f); break; } } while (!(ie->data[i++] & 0x80)); q931_strget(cnum, sizeof(cnum), ie->data + i, ie->len - i); pri_message(ctrl, " '%s' ]\n", cnum); } static int receive_connected_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { int i = 0; call->connected_number_in_message = 1; call->remote_id.number.valid = 1; call->remote_id.number.presentation = PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; /* To follow Q.931 (4.5.1), we must search for start of octet 4 by walking through all bytes until one with ext bit (8) set to 1 */ do { switch (i) { case 0: call->remote_id.number.plan = ie->data[i] & 0x7f; break; case 1: /* Keep only the presentation and screening fields */ call->remote_id.number.presentation = ie->data[i] & (PRI_PRES_RESTRICTION | PRI_PRES_NUMBER_TYPE); break; } } while (!(ie->data[i++] & 0x80)); q931_strget_gripe(ctrl, ie2str(full_ie), (unsigned char *) call->remote_id.number.str, sizeof(call->remote_id.number.str), ie->data + i, ie->len - i); return 0; } static int transmit_connected_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { size_t datalen; if (!call->local_id.number.valid) { return 0; } datalen = strlen(call->local_id.number.str); ie->data[0] = call->local_id.number.plan; ie->data[1] = 0x80 | call->local_id.number.presentation; memcpy(ie->data + 2, call->local_id.number.str, datalen); return datalen + (2 + 2); } static void dump_connected_number(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { unsigned char cnum[256]; int i = 0; /* To follow Q.931 (4.5.1), we must search for start of octet 4 by walking through all bytes until one with ext bit (8) set to 1 */ do { switch(i) { case 0: /* Octet 3 */ pri_message(ctrl, "%c %s (len=%2d) [ Ext: %d TON: %s (%d) NPI: %s (%d)", prefix, ie2str(full_ie), len, ie->data[0] >> 7, ton2str((ie->data[0] >> 4) & 0x07), (ie->data[0] >> 4) & 0x07, npi2str(ie->data[0] & 0x0f), ie->data[0] & 0x0f); break; case 1: /* Octet 3a */ pri_message(ctrl, "\n"); pri_message(ctrl, "%c Ext: %d Presentation: %s (%d)", prefix, ie->data[1] >> 7, pri_pres2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f); break; } } while(!(ie->data[i++]& 0x80)); q931_strget(cnum, sizeof(cnum), ie->data + i, ie->len - i); pri_message(ctrl, " '%s' ]\n", cnum); } static int receive_connected_subaddr(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { if (len < 3) { return -1; } return receive_subaddr_helper(full_ie, ctrl, &call->remote_id.subaddress, msgtype, ie, 1, len - 3); } static int transmit_connected_subaddr(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { return transmit_subaddr_helper(full_ie, ctrl, &call->local_id.subaddress, msgtype, ie, 1, len, order); } static void dump_connected_subaddr(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { dump_subaddr_helper(full_ie, ctrl, ie, 1, len, len - 3, prefix); } static int receive_redirecting_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { int i = 0; call->redirecting_number_in_message = 1; call->redirecting.from.number.valid = 1; call->redirecting.from.number.presentation = PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; call->redirecting.reason = PRI_REDIR_UNKNOWN; /* To follow Q.931 (4.5.1), we must search for start of octet 4 by walking through all bytes until one with ext bit (8) set to 1 */ do { switch (i) { case 0: call->redirecting.from.number.plan = ie->data[i] & 0x7f; break; case 1: /* Keep only the presentation and screening fields */ call->redirecting.from.number.presentation = ie->data[i] & (PRI_PRES_RESTRICTION | PRI_PRES_NUMBER_TYPE); break; case 2: call->redirecting.reason = ie->data[i] & 0x0f; break; } } while (!(ie->data[i++] & 0x80)); q931_strget_gripe(ctrl, ie2str(full_ie), (unsigned char *) call->redirecting.from.number.str, sizeof(call->redirecting.from.number.str), ie->data + i, ie->len - i); return 0; } static int transmit_redirecting_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { size_t datalen; if (order > 1) return 0; if (!call->redirecting.from.number.valid) { return 0; } if (BRI_TE_PTMP(ctrl)) { /* * We should not send this ie to the network if we are the TE * PTMP side since phones do not redirect calls within * themselves. Well... If you consider someone else dialing the * handset a redirection then how is the network to know? */ return 0; } if (call->redirecting.state != Q931_REDIRECTING_STATE_IDLE) { /* * There was a DivertingLegInformation2 in the message so the * Q931_REDIRECTING_NUMBER ie is redundant. Some networks * (Deutsche Telekom) complain about it. */ return 0; } datalen = strlen(call->redirecting.from.number.str); ie->data[0] = call->redirecting.from.number.plan; #if 1 /* ETSI and Q.952 do not define the screening field */ ie->data[1] = call->redirecting.from.number.presentation & PRI_PRES_RESTRICTION; #else /* Q.931 defines the screening field */ ie->data[1] = call->redirecting.from.number.presentation; #endif ie->data[2] = (call->redirecting.reason & 0x0f) | 0x80; memcpy(ie->data + 3, call->redirecting.from.number.str, datalen); return datalen + (3 + 2); } static void dump_redirecting_subaddr(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { dump_subaddr_helper(full_ie, ctrl, ie, 2, len, len - 4, prefix); } static int receive_redirection_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { int i = 0; call->redirection_number.valid = 1; call->redirection_number.presentation = PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; /* To follow Q.931 (4.5.1), we must search for start of octet 4 by walking through all bytes until one with ext bit (8) set to 1 */ do { switch (i) { case 0: call->redirection_number.plan = ie->data[i] & 0x7f; break; case 1: /* Keep only the presentation and screening fields */ call->redirection_number.presentation = ie->data[i] & (PRI_PRES_RESTRICTION | PRI_PRES_NUMBER_TYPE); break; } } while (!(ie->data[i++] & 0x80)); q931_strget_gripe(ctrl, ie2str(full_ie), (unsigned char *) call->redirection_number.str, sizeof(call->redirection_number.str), ie->data + i, ie->len - i); return 0; } static int transmit_redirection_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { size_t datalen; if (order > 1) { return 0; } if (!call->redirection_number.valid) { return 0; } datalen = strlen(call->redirection_number.str); ie->data[0] = call->redirection_number.plan; ie->data[1] = (call->redirection_number.presentation & PRI_PRES_RESTRICTION) | 0x80; memcpy(ie->data + 2, call->redirection_number.str, datalen); return datalen + (2 + 2); } static int receive_calling_party_subaddr(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { if (len < 3) { return -1; } return receive_subaddr_helper(full_ie, ctrl, &call->remote_id.subaddress, msgtype, ie, 1, len - 3); } static int transmit_calling_party_subaddr(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { return transmit_subaddr_helper(full_ie, ctrl, &call->local_id.subaddress, msgtype, ie, 1, len, order); } static int receive_called_party_subaddr(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { if (len < 3) { return -1; } return receive_subaddr_helper(full_ie, ctrl, &call->called.subaddress, msgtype, ie, 1, len - 3); } static int transmit_called_party_subaddr(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { return transmit_subaddr_helper(full_ie, ctrl, &call->called.subaddress, msgtype, ie, 1, len, order); } static int receive_called_party_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { size_t called_len; size_t max_len; char *called_end; if (len < 3) { return -1; } switch (msgtype) { case Q931_FACILITY: if (!q931_is_dummy_call(call)) { /* Discard the number. */ return 0; } /* Fall through */ case Q931_REGISTER: /* Accept the number for REGISTER only because it is so similar to SETUP. */ case Q931_SETUP: q931_strget((unsigned char *) call->called.number.str, sizeof(call->called.number.str), ie->data + 1, len - 3); break; case Q931_INFORMATION: if (call->ourcallstate == Q931_CALL_STATE_OVERLAP_RECEIVING) { /* * Since we are receiving overlap digits now, we need to append * them to any previously received digits in call->called.number.str. */ called_len = strlen(call->called.number.str); called_end = call->called.number.str + called_len; max_len = (sizeof(call->called.number.str) - 1) - called_len; if (max_len < len - 3) { called_len = max_len; } else { called_len = len - 3; } strncat(called_end, (char *) ie->data + 1, called_len); } break; default: /* Discard the number. */ return 0; } call->called.number.valid = 1; call->called.number.plan = ie->data[0] & 0x7f; q931_strget_gripe(ctrl, ie2str(full_ie), (unsigned char *) call->overlap_digits, sizeof(call->overlap_digits), ie->data + 1, len - 3); return 0; } static int transmit_called_party_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { size_t datalen; if (!call->called.number.valid) { return 0; } datalen = strlen(call->overlap_digits); ie->data[0] = 0x80 | call->called.number.plan; memcpy(ie->data + 1, call->overlap_digits, datalen); return datalen + (1 + 2); } static int receive_calling_party_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { int i = 0; struct q931_party_number number; q931_party_number_init(&number); number.valid = 1; number.presentation = PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; /* To follow Q.931 (4.5.1), we must search for start of octet 4 by walking through all bytes until one with ext bit (8) set to 1 */ do { switch (i) { case 0: number.plan = ie->data[i] & 0x7f; break; case 1: /* Keep only the presentation and screening fields */ number.presentation = ie->data[i] & (PRI_PRES_RESTRICTION | PRI_PRES_NUMBER_TYPE); break; } } while (!(ie->data[i++] & 0x80)); q931_strget_gripe(ctrl, ie2str(full_ie), (unsigned char *) number.str, sizeof(number.str), ie->data + i, ie->len - i); /* There can be more than one calling party number ie in the SETUP message. */ if (number.presentation == (PRI_PRES_ALLOWED | PRI_PRES_NETWORK_NUMBER) || number.presentation == (PRI_PRES_RESTRICTED | PRI_PRES_NETWORK_NUMBER)) { /* The number is network provided so it is an ANI number. */ call->ani = number; if (!call->remote_id.number.valid) { /* Copy ANI to CallerID if CallerID is not already set. */ call->remote_id.number = number; } } else { call->remote_id.number = number; } return 0; } static int transmit_calling_party_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { size_t datalen; if (!call->local_id.number.valid) { return 0; } datalen = strlen(call->local_id.number.str); ie->data[0] = call->local_id.number.plan; ie->data[1] = 0x80 | call->local_id.number.presentation; memcpy(ie->data + 2, call->local_id.number.str, datalen); return datalen + (2 + 2); } static void dump_user_user(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { int x; pri_message(ctrl, "%c %s (len=%2d) [", prefix, ie2str(full_ie), len); for (x = 0; x < ie->len; ++x) { pri_message(ctrl, " %02x", ie->data[x] & 0x7f); } pri_message(ctrl, " ]\n"); } static int receive_user_user(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { call->useruserprotocoldisc = ie->data[0] & 0xff; if (call->useruserprotocoldisc == 4) { /* IA5 */ q931_memget((unsigned char *) call->useruserinfo, sizeof(call->useruserinfo), ie->data + 1, len - 3); } return 0; } static int transmit_user_user(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { int datalen = strlen(call->useruserinfo); if (datalen > 0) { /* Restricted to 35 characters */ if (msgtype == Q931_USER_INFORMATION) { if (datalen > 260) datalen = 260; } else { if (datalen > 35) datalen = 35; } ie->data[0] = 4; /* IA5 characters */ memcpy(&ie->data[1], call->useruserinfo, datalen); call->useruserinfo[0] = '\0'; return datalen + 3; } return 0; } static void dump_change_status(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { int x; pri_message(ctrl, "%c %s (len=%2d) [", prefix, ie2str(full_ie), len); for (x = 0; x < ie->len; ++x) { pri_message(ctrl, " %02x", ie->data[x] & 0x7f); } pri_message(ctrl, " ]\n"); } static int receive_change_status(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { call->changestatus = ie->data[0] & 0x0f; return 0; } static int transmit_change_status(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { ie->data[0] = 0xc0 | call->changestatus; return 3; } static char *prog2str(int prog) { static struct msgtype progs[] = { { Q931_PROG_CALL_NOT_E2E_ISDN, "Call is not end-to-end ISDN; further call progress information may be available inband." }, { Q931_PROG_CALLED_NOT_ISDN, "Called equipment is non-ISDN." }, { Q931_PROG_CALLER_NOT_ISDN, "Calling equipment is non-ISDN." }, { Q931_PROG_INBAND_AVAILABLE, "Inband information or appropriate pattern now available." }, { Q931_PROG_DELAY_AT_INTERF, "Delay in response at called Interface." }, { Q931_PROG_INTERWORKING_WITH_PUBLIC, "Interworking with a public network." }, { Q931_PROG_INTERWORKING_NO_RELEASE, "Interworking with a network unable to supply a release signal." }, { Q931_PROG_INTERWORKING_NO_RELEASE_PRE_ANSWER, "Interworking with a network unable to supply a release signal before answer." }, { Q931_PROG_INTERWORKING_NO_RELEASE_POST_ANSWER, "Interworking with a network unable to supply a release signal after answer." }, }; return code2str(prog, progs, sizeof(progs) / sizeof(progs[0])); } static char *coding2str(int cod) { static struct msgtype cods[] = { { CODE_CCITT, "CCITT (ITU) standard" }, { CODE_INTERNATIONAL, "Non-ITU international standard" }, { CODE_NATIONAL, "National standard" }, { CODE_NETWORK_SPECIFIC, "Network specific standard" }, }; return code2str(cod, cods, sizeof(cods) / sizeof(cods[0])); } static char *loc2str(int loc) { static struct msgtype locs[] = { { LOC_USER, "User" }, { LOC_PRIV_NET_LOCAL_USER, "Private network serving the local user" }, { LOC_PUB_NET_LOCAL_USER, "Public network serving the local user" }, { LOC_TRANSIT_NET, "Transit network" }, { LOC_PUB_NET_REMOTE_USER, "Public network serving the remote user" }, { LOC_PRIV_NET_REMOTE_USER, "Private network serving the remote user" }, { LOC_INTERNATIONAL_NETWORK, "International network" }, { LOC_NETWORK_BEYOND_INTERWORKING, "Network beyond the interworking point" }, }; return code2str(loc, locs, sizeof(locs) / sizeof(locs[0])); } static void dump_progress_indicator(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { pri_message(ctrl, "%c %s (len=%2d) [ Ext: %d Coding: %s (%d) 0: %d Location: %s (%d)\n", prefix, ie2str(full_ie), len, ie->data[0] >> 7, coding2str((ie->data[0] & 0x60) >> 5), (ie->data[0] & 0x60) >> 5, (ie->data[0] & 0x10) >> 4, loc2str(ie->data[0] & 0xf), ie->data[0] & 0xf); pri_message(ctrl, "%c Ext: %d Progress Description: %s (%d) ]\n", prefix, ie->data[1] >> 7, prog2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f); } static int receive_display(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { u_int8_t *data; if (ctrl->display_flags.receive & PRI_DISPLAY_OPTION_BLOCK) { return 0; } data = ie->data; if (data[0] & 0x80) { /* Skip over character set */ data++; len--; } call->display.text = data; call->display.full_ie = full_ie; call->display.length = len - 2; call->display.char_set = PRI_CHAR_SET_ISO8859_1; return 0; } static int transmit_display(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { size_t datalen; int i; if (!call->display.text || !call->display.length) { return 0; } if (ctrl->display_flags.send & PRI_DISPLAY_OPTION_BLOCK) { return 0; } i = 0; switch (ctrl->switchtype) { case PRI_SWITCH_QSIG: case PRI_SWITCH_EUROISDN_E1: case PRI_SWITCH_EUROISDN_T1: break; default: /* Prefix text with character set indicator. */ ie->data[0] = 0xb1; ++i; break; } datalen = call->display.length; if (MAX_DISPLAY_TEXT < datalen + i) { datalen = MAX_DISPLAY_TEXT - i; } memcpy(ie->data + i, call->display.text, datalen); return 2 + i + datalen; } static int receive_progress_indicator(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { call->progloc = ie->data[0] & 0xf; call->progcode = (ie->data[0] & 0x60) >> 5; switch (call->progress = (ie->data[1] & 0x7f)) { case Q931_PROG_CALL_NOT_E2E_ISDN: call->progressmask |= PRI_PROG_CALL_NOT_E2E_ISDN; break; case Q931_PROG_CALLED_NOT_ISDN: call->progressmask |= PRI_PROG_CALLED_NOT_ISDN; break; case Q931_PROG_CALLER_NOT_ISDN: call->progressmask |= PRI_PROG_CALLER_NOT_ISDN; break; case Q931_PROG_CALLER_RETURNED_TO_ISDN: call->progressmask |= PRI_PROG_CALLER_RETURNED_TO_ISDN; break; case Q931_PROG_INBAND_AVAILABLE: call->progressmask |= PRI_PROG_INBAND_AVAILABLE; break; case Q931_PROG_DELAY_AT_INTERF: call->progressmask |= PRI_PROG_DELAY_AT_INTERF; break; case Q931_PROG_INTERWORKING_WITH_PUBLIC: call->progressmask |= PRI_PROG_INTERWORKING_WITH_PUBLIC; break; case Q931_PROG_INTERWORKING_NO_RELEASE: call->progressmask |= PRI_PROG_INTERWORKING_NO_RELEASE; break; case Q931_PROG_INTERWORKING_NO_RELEASE_PRE_ANSWER: call->progressmask |= PRI_PROG_INTERWORKING_NO_RELEASE_PRE_ANSWER; break; case Q931_PROG_INTERWORKING_NO_RELEASE_POST_ANSWER: call->progressmask |= PRI_PROG_INTERWORKING_NO_RELEASE_POST_ANSWER; break; default: pri_error(ctrl, "XXX Invalid Progress indicator value received: %02x\n",(ie->data[1] & 0x7f)); break; } return 0; } static void q931_apdu_timeout(void *data); static int transmit_facility(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { struct apdu_event **prev; struct apdu_event *cur; int apdu_len; for (prev = &call->apdus, cur = call->apdus; cur; prev = &cur->next, cur = cur->next) { if (!cur->sent && (cur->message == msgtype || cur->message == Q931_ANY_MESSAGE)) { break; } } if (!cur) { /* No APDU found */ return 0; } if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, "Adding facility ie contents to send in %s message:\n", msg2str(msgtype)); facility_decode_dump(ctrl, cur->apdu, cur->apdu_len); } if (len < cur->apdu_len) { pri_error(ctrl, "Could not fit facility ie in message. Size needed:%d Available space:%d\n", cur->apdu_len + 2, len); /* Remove APDU from list. */ *prev = cur->next; if (cur->response.callback) { /* Indicate to callback that the APDU had a problem getting sent. */ cur->response.callback(APDU_CALLBACK_REASON_ERROR, ctrl, call, cur, NULL); } free(cur); return 0; } memcpy(ie->data, cur->apdu, cur->apdu_len); apdu_len = cur->apdu_len; cur->sent = 1; if (cur->response.callback && cur->response.timeout_time) { int failed; if (0 < cur->response.timeout_time) { /* Sender specified a timeout duration. */ cur->timer = pri_schedule_event(ctrl, cur->response.timeout_time, q931_apdu_timeout, cur); failed = !cur->timer; } else { /* Sender wants to "timeout" only when specified messages are received. */ failed = !cur->response.num_messages; } if (failed) { /* Remove APDU from list. */ *prev = cur->next; /* Indicate to callback that the APDU had a problem getting sent. */ cur->response.callback(APDU_CALLBACK_REASON_ERROR, ctrl, call, cur, NULL); free(cur); } } else { /* Remove APDU from list. */ *prev = cur->next; free(cur); } return apdu_len + 2; } static int receive_facility(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { /* Delay processing facility ie's till after all other ie's are processed. */ if (MAX_FACILITY_IES <= ctrl->facility.count) { pri_message(ctrl, "!! Too many facility ie's to delay.\n"); return -1; } /* Make sure we have enough room for the protocol profile ie octet(s) */ if (ie->data + ie->len < ie->data + 2) { return -1; } /* Save the facility ie location for delayed decode. */ ctrl->facility.ie[ctrl->facility.count] = ie; ctrl->facility.codeset[ctrl->facility.count] = Q931_IE_CODESET((unsigned) full_ie); ++ctrl->facility.count; return 0; } static int process_facility(struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie) { struct fac_extension_header header; struct rose_message rose; const unsigned char *pos; const unsigned char *end; pos = ie->data; end = ie->data + ie->len; /* Make sure we have enough room for the protocol profile ie octet(s) */ if (end < pos + 2) { return -1; } switch (*pos & Q932_PROTOCOL_MASK) { case Q932_PROTOCOL_ROSE: case Q932_PROTOCOL_EXTENSIONS: break; default: case Q932_PROTOCOL_CMIP: case Q932_PROTOCOL_ACSE: if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, "!! Don't know how to handle Q.932 Protocol Profile type 0x%X\n", *pos & Q932_PROTOCOL_MASK); } return -1; } if (!(*pos & 0x80)) { /* DMS-100 Service indicator octet - Just ignore for now */ ++pos; } ++pos; if (ctrl->debug & PRI_DEBUG_APDU) { asn1_dump(ctrl, pos, end); } pos = fac_dec_extension_header(ctrl, pos, end, &header); if (!pos) { return -1; } if (header.npp_present) { if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, "!! Don't know how to handle Network Protocol Profile type 0x%X\n", header.npp); } return -1; } /* Process all components in the facility. */ while (pos < end) { pos = rose_decode(ctrl, pos, end, &rose); if (!pos) { return -1; } switch (rose.type) { case ROSE_COMP_TYPE_INVOKE: rose_handle_invoke(ctrl, call, msgtype, ie, &header, &rose.component.invoke); break; case ROSE_COMP_TYPE_RESULT: rose_handle_result(ctrl, call, msgtype, ie, &header, &rose.component.result); break; case ROSE_COMP_TYPE_ERROR: rose_handle_error(ctrl, call, msgtype, ie, &header, &rose.component.error); break; case ROSE_COMP_TYPE_REJECT: rose_handle_reject(ctrl, call, msgtype, ie, &header, &rose.component.reject); break; default: return -1; } } return 0; } static void q931_handle_facilities(struct pri *ctrl, q931_call *call, int msgtype) { unsigned idx; unsigned codeset; unsigned full_ie; q931_ie *ie; for (idx = 0; idx < ctrl->facility.count; ++idx) { ie = ctrl->facility.ie[idx]; if (ctrl->debug & PRI_DEBUG_Q931_STATE) { codeset = ctrl->facility.codeset[idx]; full_ie = Q931_FULL_IE(codeset, ie->ie); pri_message(ctrl, "-- Delayed processing IE %d (cs%d, %s)\n", ie->ie, codeset, ie2str(full_ie)); } process_facility(ctrl, call, msgtype, ie); } } /*! * \internal * \brief Check if any APDU responses "timeout" with the current Q.931 message. * * \param ctrl D channel controller. * \param call Q.931 call leg. * \param msgtype Q.931 message type received. * * \return Nothing */ static void q931_apdu_msg_expire(struct pri *ctrl, struct q931_call *call, int msgtype) { struct apdu_event **prev; struct apdu_event **prev_next; struct apdu_event *cur; unsigned idx; for (prev = &call->apdus; *prev; prev = prev_next) { cur = *prev; prev_next = &cur->next; if (cur->sent) { for (idx = 0; idx < cur->response.num_messages; ++idx) { if (cur->response.message_type[idx] == msgtype) { /* * APDU response message "timeout". * * Extract the APDU from the list so it cannot be * deleted from under us by the callback. */ prev_next = prev; *prev = cur->next; /* Stop any response timeout. */ pri_schedule_del(ctrl, cur->timer); cur->timer = 0; cur->response.callback(APDU_CALLBACK_REASON_TIMEOUT, ctrl, call, cur, NULL); free(cur); break; } } } } } static int transmit_progress_indicator(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { int code, mask; /* Can't send progress indicator on GR-303 -- EVER! */ if (ctrl->link.next && !ctrl->bri) return 0; if (call->progressmask > 0) { if (call->progressmask & (mask = PRI_PROG_CALL_NOT_E2E_ISDN)) code = Q931_PROG_CALL_NOT_E2E_ISDN; else if (call->progressmask & (mask = PRI_PROG_CALLED_NOT_ISDN)) code = Q931_PROG_CALLED_NOT_ISDN; else if (call->progressmask & (mask = PRI_PROG_CALLER_NOT_ISDN)) code = Q931_PROG_CALLER_NOT_ISDN; else if (call->progressmask & (mask = PRI_PROG_INBAND_AVAILABLE)) code = Q931_PROG_INBAND_AVAILABLE; else if (call->progressmask & (mask = PRI_PROG_DELAY_AT_INTERF)) code = Q931_PROG_DELAY_AT_INTERF; else if (call->progressmask & (mask = PRI_PROG_INTERWORKING_WITH_PUBLIC)) code = Q931_PROG_INTERWORKING_WITH_PUBLIC; else if (call->progressmask & (mask = PRI_PROG_INTERWORKING_NO_RELEASE)) code = Q931_PROG_INTERWORKING_NO_RELEASE; else if (call->progressmask & (mask = PRI_PROG_INTERWORKING_NO_RELEASE_PRE_ANSWER)) code = Q931_PROG_INTERWORKING_NO_RELEASE_PRE_ANSWER; else if (call->progressmask & (mask = PRI_PROG_INTERWORKING_NO_RELEASE_POST_ANSWER)) code = Q931_PROG_INTERWORKING_NO_RELEASE_POST_ANSWER; else { code = 0; pri_error(ctrl, "XXX Undefined progress bit: %x\n", call->progressmask); } if (code) { ie->data[0] = 0x80 | (call->progcode << 5) | (call->progloc); ie->data[1] = 0x80 | code; call->progressmask &= ~mask; return 4; } } /* Leave off */ return 0; } static int transmit_call_state(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { ie->data[0] = Q931_CALL_STATE_NULL; switch (call->ourcallstate) { case Q931_CALL_STATE_NULL: case Q931_CALL_STATE_CALL_INITIATED: case Q931_CALL_STATE_OVERLAP_SENDING: case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: case Q931_CALL_STATE_CALL_DELIVERED: case Q931_CALL_STATE_CALL_PRESENT: case Q931_CALL_STATE_CALL_RECEIVED: case Q931_CALL_STATE_CONNECT_REQUEST: case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: case Q931_CALL_STATE_ACTIVE: case Q931_CALL_STATE_DISCONNECT_REQUEST: case Q931_CALL_STATE_DISCONNECT_INDICATION: case Q931_CALL_STATE_SUSPEND_REQUEST: case Q931_CALL_STATE_RESUME_REQUEST: case Q931_CALL_STATE_RELEASE_REQUEST: case Q931_CALL_STATE_CALL_ABORT: case Q931_CALL_STATE_OVERLAP_RECEIVING: case Q931_CALL_STATE_CALL_INDEPENDENT_SERVICE: case Q931_CALL_STATE_RESTART_REQUEST: case Q931_CALL_STATE_RESTART: ie->data[0] = call->ourcallstate; break; case Q931_CALL_STATE_NOT_SET: break; } return 3; } static int receive_call_state(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { call->sugcallstate = ie->data[0] & 0x3f; return 0; } /*! * \brief Convert the internal Q.931 call state to a string. * * \param callstate Internal Q.931 call state. * * \return String equivalent of the given Q.931 call state. */ const char *q931_call_state_str(enum Q931_CALL_STATE callstate) { static struct msgtype callstates[] = { /* *INDENT-OFF* */ { Q931_CALL_STATE_NULL, "Null" }, { Q931_CALL_STATE_CALL_INITIATED, "Call Initiated" }, { Q931_CALL_STATE_OVERLAP_SENDING, "Overlap Sending" }, { Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING, "Outgoing Call Proceeding" }, { Q931_CALL_STATE_CALL_DELIVERED, "Call Delivered" }, { Q931_CALL_STATE_CALL_PRESENT, "Call Present" }, { Q931_CALL_STATE_CALL_RECEIVED, "Call Received" }, { Q931_CALL_STATE_CONNECT_REQUEST, "Connect Request" }, { Q931_CALL_STATE_INCOMING_CALL_PROCEEDING, "Incoming Call Proceeding" }, { Q931_CALL_STATE_ACTIVE, "Active" }, { Q931_CALL_STATE_DISCONNECT_REQUEST, "Disconnect Request" }, { Q931_CALL_STATE_DISCONNECT_INDICATION, "Disconnect Indication" }, { Q931_CALL_STATE_SUSPEND_REQUEST, "Suspend Request" }, { Q931_CALL_STATE_RESUME_REQUEST, "Resume Request" }, { Q931_CALL_STATE_RELEASE_REQUEST, "Release Request" }, { Q931_CALL_STATE_CALL_ABORT, "Call Abort" }, { Q931_CALL_STATE_OVERLAP_RECEIVING, "Overlap Receiving" }, { Q931_CALL_STATE_CALL_INDEPENDENT_SERVICE, "Call Independent Service" }, { Q931_CALL_STATE_RESTART_REQUEST, "Restart Request" }, { Q931_CALL_STATE_RESTART, "Restart" }, { Q931_CALL_STATE_NOT_SET, "Not set. Internal use only." }, /* *INDENT-ON* */ }; return code2str(callstate, callstates, ARRAY_LEN(callstates)); } /*! * \internal * \brief Convert the Q.932 supplementary hold state to a string. * * \param state Q.932 supplementary hold state. * * \return String equivalent of the given hold state. */ static const char *q931_hold_state_str(enum Q931_HOLD_STATE state) { static struct msgtype hold_states[] = { /* *INDENT-OFF* */ { Q931_HOLD_STATE_IDLE, "Idle" }, { Q931_HOLD_STATE_HOLD_REQ, "Hold Request" }, { Q931_HOLD_STATE_HOLD_IND, "Hold Indication" }, { Q931_HOLD_STATE_CALL_HELD, "Call Held" }, { Q931_HOLD_STATE_RETRIEVE_REQ, "Retrieve Request" }, { Q931_HOLD_STATE_RETRIEVE_IND, "Retrieve Indication" }, /* *INDENT-ON* */ }; return code2str(state, hold_states, ARRAY_LEN(hold_states)); } static void dump_call_state(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { pri_message(ctrl, "%c %s (len=%2d) [ Ext: %d Coding: %s (%d) Call state: %s (%d)\n", prefix, ie2str(full_ie), len, ie->data[0] >> 7, coding2str((ie->data[0] & 0xC0) >> 6), (ie->data[0] & 0xC0) >> 6, q931_call_state_str(ie->data[0] & 0x3f), ie->data[0] & 0x3f); } static void dump_call_identity(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { int x; pri_message(ctrl, "%c %s (len=%2d) [ ", prefix, ie2str(full_ie), len); for (x = 0; x < ie->len; ++x) { pri_message(ctrl, "0x%02X ", ie->data[x]); } pri_message(ctrl, " ]\n"); } static void dump_time_date(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { pri_message(ctrl, "%c %s (len=%2d) [ ", prefix, ie2str(full_ie), len); if (ie->len > 0) pri_message(ctrl, "%02d", ie->data[0]); if (ie->len > 1) pri_message(ctrl, "-%02d", ie->data[1]); if (ie->len > 2) pri_message(ctrl, "-%02d", ie->data[2]); if (ie->len > 3) pri_message(ctrl, " %02d", ie->data[3]); if (ie->len > 4) pri_message(ctrl, ":%02d", ie->data[4]); if (ie->len > 5) pri_message(ctrl, ":%02d", ie->data[5]); pri_message(ctrl, " ]\n"); } static int receive_time_date(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { /* Ignore incoming Date/Time since we have no use for it at this time. */ return 0; } static int transmit_time_date(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { time_t now; struct tm timedate; int ie_len; do { if (ctrl->date_time_send < PRI_DATE_TIME_SEND_DATE) { ie_len = 0; break; } /* Send the current date/time. */ time(&now); localtime_r(&now, &timedate); ie->data[0] = timedate.tm_year - 100; /* 1900+ */ ie->data[1] = timedate.tm_mon + 1; ie->data[2] = timedate.tm_mday; ie_len = 2 + 3; if (ctrl->date_time_send < PRI_DATE_TIME_SEND_DATE_HH) { break; } /* Add optional hour. */ ie->data[3] = timedate.tm_hour; ++ie_len; if (ctrl->date_time_send < PRI_DATE_TIME_SEND_DATE_HHMM) { break; } /* Add optional minutes. */ ie->data[4] = timedate.tm_min; ++ie_len; if (ctrl->date_time_send < PRI_DATE_TIME_SEND_DATE_HHMMSS) { break; } /* Add optional seconds. */ ie->data[5] = timedate.tm_sec; ++ie_len; } while (0); return ie_len; } static void dump_keypad_facility(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { unsigned char tmp[64]; q931_strget(tmp, sizeof(tmp), ie->data, ie->len); pri_message(ctrl, "%c %s (len=%2d) [ %s ]\n", prefix, ie2str(full_ie), ie->len, tmp); } static int receive_keypad_facility(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { if (ie->len == 0) return -1; q931_strget_gripe(ctrl, ie2str(full_ie), (unsigned char *) call->keypad_digits, sizeof(call->keypad_digits), ie->data, ie->len); return 0; } static int transmit_keypad_facility(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { int sublen; sublen = strlen(call->keypad_digits); if (sublen) { libpri_copy_string((char *) ie->data, call->keypad_digits, sizeof(call->keypad_digits)); return sublen + 2; } else return 0; } static void dump_display(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { int x; unsigned char buf[2*80 + 1]; char tmp[20 + 1]; x = 0; if (ie->len && (ie->data[x] & 0x80)) { snprintf(tmp, sizeof(tmp), "Charset: %02x ", ie->data[x] & 0x7f); ++x; } else { tmp[0] = '\0'; } q931_strget(buf, sizeof(buf), &ie->data[x], ie->len - x); pri_message(ctrl, "%c %s (len=%2d) %s[ %s ]\n", prefix, ie2str(full_ie), ie->len, tmp, buf); } #define CHECK_OVERFLOW(limit) \ if (tmpptr - tmp + limit >= sizeof(tmp)) { \ *tmpptr = '\0'; \ pri_message(ctrl, "%s", tmpptr = tmp); \ } static void dump_ie_data(struct pri *ctrl, unsigned char *c, int len) { static char hexs[16] = "0123456789ABCDEF"; char tmp[1024], *tmpptr; int lastascii = 0; tmpptr = tmp; for (; len; --len, ++c) { CHECK_OVERFLOW(7); if (isprint(*c)) { if (!lastascii) { if (tmpptr != tmp) { *tmpptr++ = ','; *tmpptr++ = ' '; } *tmpptr++ = '\''; lastascii = 1; } *tmpptr++ = *c; } else { if (lastascii) { *tmpptr++ = '\''; lastascii = 0; } if (tmpptr != tmp) { *tmpptr++ = ','; *tmpptr++ = ' '; } *tmpptr++ = '0'; *tmpptr++ = 'x'; *tmpptr++ = hexs[(*c >> 4) & 0x0f]; *tmpptr++ = hexs[(*c) & 0x0f]; } } if (lastascii) *tmpptr++ = '\''; *tmpptr = '\0'; pri_message(ctrl, "%s", tmp); } static void dump_facility(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { pri_message(ctrl, "%c %s (len=%2d, codeset=%d) [ ", prefix, ie2str(full_ie), len, Q931_IE_CODESET(full_ie)); dump_ie_data(ctrl, ie->data, ie->len); pri_message(ctrl, " ]\n"); #if 0 /* Lets not dump parse of facility contents here anymore. */ /* * The ASN.1 decode dump has already been done when the facility ie was added to the outgoing * message or the ASN.1 decode dump will be done when the facility ie is processed on incoming * messages. This dump is redundant and very noisy. */ if (ie->len > 1) { int dataat = (ie->data[0] & 0x80) ? 1 : 2; pri_message(ctrl, "PROTOCOL %02X\n", ie->data[0] & Q932_PROTOCOL_MASK); asn1_dump(ctrl, ie->data + dataat, ie->data + ie->len); } #endif /* Lets not dump parse of facility contents here anymore. */ } static void dump_network_spec_fac(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { pri_message(ctrl, "%c %s (len=%2d) [ ", prefix, ie2str(full_ie), ie->len); if (ie->data[0] == 0x00) { pri_message(ctrl, "%s", code2str(ie->data[1], facilities, ARRAY_LEN(facilities))); } else { dump_ie_data(ctrl, ie->data, ie->len); } pri_message(ctrl, " ]\n"); } static int receive_network_spec_fac(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { return 0; } static int transmit_network_spec_fac(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { /* We are ready to transmit single IE only */ if (order > 1) return 0; if (ctrl->nsf != PRI_NSF_NONE) { ie->data[0] = 0x00; ie->data[1] = ctrl->nsf; return 4; } /* Leave off */ return 0; } char *pri_cause2str(int cause) { return code2str(cause, causes, sizeof(causes) / sizeof(causes[0])); } static char *pri_causeclass2str(int cause) { static struct msgtype causeclasses[] = { { 0, "Normal Event" }, { 1, "Normal Event" }, { 2, "Network Congestion (resource unavailable)" }, { 3, "Service or Option not Available" }, { 4, "Service or Option not Implemented" }, { 5, "Invalid message (e.g. parameter out of range)" }, { 6, "Protocol Error (e.g. unknown message)" }, { 7, "Interworking" }, }; return code2str(cause, causeclasses, sizeof(causeclasses) / sizeof(causeclasses[0])); } static void dump_cause(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { int x; int cause_ind = 1; int data_ind = 2; pri_message(ctrl, "%c %s (len=%2d) [ Ext: %d Coding: %s (%d) Spare: %d Location: %s (%d)\n", prefix, ie2str(full_ie), len, ie->data[0] >> 7, coding2str((ie->data[0] & 0x60) >> 5), (ie->data[0] & 0x60) >> 5, (ie->data[0] & 0x10) >> 4, loc2str(ie->data[0] & 0xf), ie->data[0] & 0xf); if ((ie->data[0] & 0x80) == 0) { const char *recommendation; switch (ie->data[1] & 0x7f) { case 0x00: recommendation = "Q.931"; break; case 0x03: recommendation = "X.21"; break; case 0x04: recommendation = "X.25"; break; case 0x05: recommendation = "Q.1031/Q.1051"; break; default: recommendation = "Reserved"; break; } pri_message(ctrl, "%c Ext: %d Recommendation: %s (%d)\n", prefix, (ie->data[1] >> 7), recommendation, ie->data[1] & 0x7f); cause_ind = 2; data_ind = 3; } pri_message(ctrl, "%c Ext: %d Cause: %s (%d), class = %s (%d) ]\n", prefix, (ie->data[cause_ind] >> 7), pri_cause2str(ie->data[cause_ind] & 0x7f), ie->data[cause_ind] & 0x7f, pri_causeclass2str((ie->data[cause_ind] & 0x7f) >> 4), (ie->data[cause_ind] & 0x7f) >> 4); if (ie->len <= data_ind) { return; } /* Dump cause data in readable form */ switch(ie->data[cause_ind] & 0x7f) { case PRI_CAUSE_IE_NONEXIST: for (x = data_ind; x < ie->len; x++) { pri_message(ctrl, "%c Cause data %d: %02x (%d, %s IE)\n", prefix, x-1, ie->data[x], ie->data[x], ie2str(ie->data[x])); } break; case PRI_CAUSE_WRONG_CALL_STATE: for (x = data_ind; x < ie->len; x++) { pri_message(ctrl, "%c Cause data %d: %02x (%d, %s message)\n", prefix, x-1, ie->data[x], ie->data[x], msg2str(ie->data[x])); } break; case PRI_CAUSE_RECOVERY_ON_TIMER_EXPIRE: pri_message(ctrl, "%c Cause data:", prefix); for (x = data_ind; x < ie->len; x++) { pri_message(ctrl, " %02x", ie->data[x]); } pri_message(ctrl, " (Timer T"); for (x = data_ind; x < ie->len; x++) { pri_message(ctrl, "%c", ((ie->data[x] >= ' ') && (ie->data[x] < 0x7f)) ? ie->data[x] : '.'); } pri_message(ctrl, ")\n"); break; default: for (x = data_ind; x < ie->len; x++) { pri_message(ctrl, "%c Cause data %d: %02x (%d)\n", prefix, x-1, ie->data[x], ie->data[x]); } break; } } static int receive_cause(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { call->causeloc = ie->data[0] & 0xf; call->causecode = (ie->data[0] & 0x60) >> 5; if (ie->data[0] & 0x80) { call->cause = (ie->data[1] & 0x7f); } else { call->cause = (ie->data[2] & 0x7f); } return 0; } static int transmit_cause(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { /* We are ready to transmit single IE only */ if (order > 1) return 0; if (call->cause > 0) { ie->data[0] = 0x80 | (call->causecode << 5) | (call->causeloc); ie->data[1] = 0x80 | (call->cause); return 4; } else { /* Leave off */ return 0; } } static void dump_sending_complete(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { pri_message(ctrl, "%c %s (len=%2d)\n", prefix, ie2str(full_ie), len); } static int receive_sending_complete(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { /* We've got a "Complete" message: Exect no further digits. */ call->complete = 1; return 0; } static int transmit_sending_complete(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { if ((ctrl->overlapdial && call->complete) || /* Explicit */ (!ctrl->overlapdial && ((ctrl->switchtype == PRI_SWITCH_EUROISDN_E1) || /* Implicit */ (ctrl->switchtype == PRI_SWITCH_EUROISDN_T1)))) { /* Include this single-byte IE */ return 1; } return 0; } static char *notify2str(int info) { /* ITU-T Q.763 */ static struct msgtype notifies[] = { { PRI_NOTIFY_USER_SUSPENDED, "User suspended" }, { PRI_NOTIFY_USER_RESUMED, "User resumed" }, { PRI_NOTIFY_BEARER_CHANGE, "Bearer service change (DSS1)" }, { PRI_NOTIFY_ASN1_COMPONENT, "ASN.1 encoded component (DSS1)" }, { PRI_NOTIFY_COMPLETION_DELAY, "Call completion delay" }, { PRI_NOTIFY_CONF_ESTABLISHED, "Conference established" }, { PRI_NOTIFY_CONF_DISCONNECTED, "Conference disconnected" }, { PRI_NOTIFY_CONF_PARTY_ADDED, "Other party added" }, { PRI_NOTIFY_CONF_ISOLATED, "Isolated" }, { PRI_NOTIFY_CONF_REATTACHED, "Reattached" }, { PRI_NOTIFY_CONF_OTHER_ISOLATED, "Other party isolated" }, { PRI_NOTIFY_CONF_OTHER_REATTACHED, "Other party reattached" }, { PRI_NOTIFY_CONF_OTHER_SPLIT, "Other party split" }, { PRI_NOTIFY_CONF_OTHER_DISCONNECTED, "Other party disconnected" }, { PRI_NOTIFY_CONF_FLOATING, "Conference floating" }, { PRI_NOTIFY_WAITING_CALL, "Call is waiting call" }, { PRI_NOTIFY_DIVERSION_ACTIVATED, "Diversion activated (DSS1)" }, { PRI_NOTIFY_TRANSFER_ALERTING, "Call transfer, alerting" }, { PRI_NOTIFY_TRANSFER_ACTIVE, "Call transfer, active" }, { PRI_NOTIFY_REMOTE_HOLD, "Remote hold" }, { PRI_NOTIFY_REMOTE_RETRIEVAL, "Remote retrieval" }, { PRI_NOTIFY_CALL_DIVERTING, "Call is diverting" }, }; return code2str(info, notifies, sizeof(notifies) / sizeof(notifies[0])); } static void dump_notify(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { pri_message(ctrl, "%c %s (len=%2d): Ext: %d %s (%d)\n", prefix, ie2str(full_ie), len, ie->data[0] >> 7, notify2str(ie->data[0] & 0x7f), ie->data[0] & 0x7f); } static int receive_notify(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { call->notify = ie->data[0] & 0x7F; return 0; } static int transmit_notify(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { if (call->notify >= 0) { ie->data[0] = 0x80 | call->notify; return 3; } return 0; } static void dump_shift(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { pri_message(ctrl, "%c %sLocking Shift (len=%02d): Requested codeset %d\n", prefix, (full_ie & 8) ? "Non-" : "", len, full_ie & 7); } static char *lineinfo2str(int info) { /* NAPNA ANI II digits */ static struct msgtype lineinfo[] = { { 0, "Plain Old Telephone Service (POTS)" }, { 1, "Multiparty line (more than 2)" }, { 2, "ANI failure" }, { 6, "Station Level Rating" }, { 7, "Special Operator Handling Required" }, { 20, "Automatic Identified Outward Dialing (AIOD)" }, { 23, "Coing or Non-Coin" }, { 24, "Toll free translated to POTS originated for non-pay station" }, { 25, "Toll free translated to POTS originated from pay station" }, { 27, "Pay station with coin control signalling" }, { 29, "Prison/Inmate Service" }, { 30, "Intercept (blank)" }, { 31, "Intercept (trouble)" }, { 32, "Intercept (regular)" }, { 34, "Telco Operator Handled Call" }, { 52, "Outward Wide Area Telecommunications Service (OUTWATS)" }, { 60, "TRS call from unrestricted line" }, { 61, "Cellular/Wireless PCS (Type 1)" }, { 62, "Cellular/Wireless PCS (Type 2)" }, { 63, "Cellular/Wireless PCS (Roaming)" }, { 66, "TRS call from hotel/motel" }, { 67, "TRS call from restricted line" }, { 70, "Line connected to pay station" }, { 93, "Private virtual network call" }, }; return code2str(info, lineinfo, sizeof(lineinfo) / sizeof(lineinfo[0])); } static void dump_line_information(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { pri_message(ctrl, "%c %s (len=%02d): %s (%d)\n", prefix, ie2str(full_ie), len, lineinfo2str(ie->data[0]), ie->data[0]); } static int receive_line_information(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { call->ani2 = ie->data[0]; return 0; } static int transmit_line_information(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { #if 0 /* XXX Is this IE possible for 4ESS only? XXX */ if(ctrl->switchtype == PRI_SWITCH_ATT4ESS) { ie->data[0] = 0; return 3; } #endif return 0; } static char *gdencoding2str(int encoding) { static struct msgtype gdencoding[] = { { 0, "BCD even" }, { 1, "BCD odd" }, { 2, "IA5" }, { 3, "Binary" }, }; return code2str(encoding, gdencoding, sizeof(gdencoding) / sizeof(gdencoding[0])); } static char *gdtype2str(int type) { static struct msgtype gdtype[] = { { 0, "Account Code" }, { 1, "Auth Code" }, { 2, "Customer ID" }, { 3, "Universal Access" }, { 4, "Info Digits" }, { 5, "Callid" }, { 6, "Opart" }, { 7, "TCN" }, { 9, "Adin" }, }; return code2str(type, gdtype, sizeof(gdtype) / sizeof(gdtype[0])); } static void dump_generic_digits(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { int encoding; int type; int idx; int value; if (len < 3) { pri_message(ctrl, "%c %s (len=%02d): Invalid length\n", prefix, ie2str(full_ie), len); return; } encoding = (ie->data[0] >> 5) & 7; type = ie->data[0] & 0x1F; pri_message(ctrl, "%c %s (len=%02d): Encoding %s Type %s\n", prefix, ie2str(full_ie), len, gdencoding2str(encoding), gdtype2str(type)); if (encoding == 3) { /* Binary */ pri_message(ctrl, "%c Don't know how to handle binary encoding\n", prefix); return; } if (len == 3) /* No number information */ return; pri_message(ctrl, "%c Digits: ", prefix); value = 0; for(idx = 3; idx < len; ++idx) { switch(encoding) { case 0: /* BCD even */ case 1: /* BCD odd */ pri_message(ctrl, "%d", ie->data[idx-2] & 0x0f); value = value * 10 + (ie->data[idx-2] & 0x0f); if(!encoding || (idx+1 < len)) { /* Special handling for BCD odd */ pri_message(ctrl, "%d", (ie->data[idx-2] >> 4) & 0x0f); value = value * 10 + ((ie->data[idx-2] >> 4) & 0x0f); } break; case 2: /* IA5 */ pri_message(ctrl, "%c", ie->data[idx-2]); value = value * 10 + ie->data[idx-2] - '0'; break; } } switch(type) { case 4: /* Info Digits */ pri_message(ctrl, " - %s", lineinfo2str(value)); break; } pri_message(ctrl, "\n"); } static int receive_generic_digits(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { int encoding; int type; int idx; int value; int num_idx; char number[260]; if (len < 3) { pri_error(ctrl, "Invalid length of Generic Digits IE\n"); return -1; } encoding = (ie->data[0] >> 5) & 7; type = ie->data[0] & 0x1F; if (encoding == 3) { /* Binary */ pri_message(ctrl, "!! Unable to handle binary encoded Generic Digits IE\n"); return 0; } if (len == 3) /* No number information */ return 0; value = 0; switch(type) { /* Integer value handling */ case 4: /* Info Digits */ for(idx = 3; idx < len; ++idx) { switch(encoding) { case 0: /* BCD even */ case 1: /* BCD odd */ value = value * 10 + (ie->data[idx-2] & 0x0f); if(!encoding || (idx+1 < len)) /* Special handling for BCD odd */ value = value * 10 + ((ie->data[idx-2] >> 4) & 0x0f); break; case 2: /* IA5 */ value = value * 10 + (ie->data[idx-2] - '0'); break; } } break; /* String value handling */ case 5: /* Callid */ num_idx = 0; for(idx = 3; (idx < len) && (num_idx < sizeof(number) - 4); ++idx) { switch(encoding) { case 0: /* BCD even */ case 1: /* BCD odd */ number[num_idx++] = '0' + (ie->data[idx-2] & 0x0f); if(!encoding || (idx+1 < len)) /* Special handling for BCD odd */ number[num_idx++] = '0' + ((ie->data[idx-2] >> 4) & 0x0f); break; case 2: number[num_idx++] = ie->data[idx-2]; break; } } number[num_idx] = '\0'; break; } switch(type) { case 4: /* Info Digits */ call->ani2 = value; break; #if 0 case 5: /* Callid */ if (!call->remote_id.number.valid) { call->remote_id.number.valid = 1; call->remote_id.number.presentation = PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; call->remote_id.number.plan = PRI_UNKNOWN; libpri_copy_string(call->remote_id.number.str, number, sizeof(call->remote_id.number.str)); } break; #endif } return 0; } static int transmit_generic_digits(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { #if 0 /* XXX Is this IE possible for other switches? XXX */ if (order > 1) return 0; if(ctrl->switchtype == PRI_SWITCH_NI1) { ie->data[0] = 0x04; /* BCD even, Info Digits */ ie->data[1] = 0x00; /* POTS */ return 4; } #endif return 0; } static char *signal2str(int signal) { /* From Q.931 4.5.8 Table 4-24 */ static struct msgtype mtsignal[] = { { 0, "Dial tone" }, { 1, "Ring back tone" }, { 2, "Intercept tone" }, { 3, "Network congestion tone" }, { 4, "Busy tone" }, { 5, "Confirm tone" }, { 6, "Answer tone" }, { 7, "Call waiting tone" }, { 8, "Off-hook warning tone" }, { 9, "Pre-emption tone" }, { 63, "Tones off" }, { 64, "Alerting on - pattern 0" }, { 65, "Alerting on - pattern 1" }, { 66, "Alerting on - pattern 2" }, { 67, "Alerting on - pattern 3" }, { 68, "Alerting on - pattern 4" }, { 69, "Alerting on - pattern 5" }, { 70, "Alerting on - pattern 6" }, { 71, "Alerting on - pattern 7" }, { 79, "Alerting off" }, }; return code2str(signal, mtsignal, sizeof(mtsignal) / sizeof(mtsignal[0])); } static void dump_signal(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { pri_message(ctrl, "%c %s (len=%02d): ", prefix, ie2str(full_ie), len); if (len < 3) { pri_message(ctrl, "Invalid length\n"); return; } pri_message(ctrl, "Signal %s (%d)\n", signal2str(ie->data[0]), ie->data[0]); } static void dump_transit_count(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { /* Defined in ECMA-225 */ pri_message(ctrl, "%c %s (len=%02d): ", prefix, ie2str(full_ie), len); if (len < 3) { pri_message(ctrl, "Invalid length\n"); return; } pri_message(ctrl, "Count=%d (0x%02x)\n", ie->data[0] & 0x1f, ie->data[0] & 0x1f); } static void dump_reverse_charging_indication(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { pri_message(ctrl, "%c %s (len=%02d): %d\n", prefix, ie2str(full_ie), len, ie->data[0] & 0x7); } static int receive_reverse_charging_indication(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { call->reversecharge = ie->data[0] & 0x7; return 0; } static int transmit_reverse_charging_indication(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { if (call->reversecharge != PRI_REVERSECHARGE_NONE) { ie->data[0] = 0x80 | (call->reversecharge & 0x7); return 3; } return 0; } static struct ie ies[] = { /* Codeset 0 - Common */ { 1, NATIONAL_CHANGE_STATUS, "Change Status Information", dump_change_status, receive_change_status, transmit_change_status }, { 0, Q931_LOCKING_SHIFT, "Locking Shift", dump_shift }, { 0, Q931_BEARER_CAPABILITY, "Bearer Capability", dump_bearer_capability, receive_bearer_capability, transmit_bearer_capability }, { 0, Q931_CAUSE, "Cause", dump_cause, receive_cause, transmit_cause }, { 1, Q931_IE_CALL_STATE, "Call State", dump_call_state, receive_call_state, transmit_call_state }, { 0, Q931_CHANNEL_IDENT, "Channel ID", dump_channel_id, receive_channel_id, transmit_channel_id }, { 0, Q931_PROGRESS_INDICATOR, "Progress Indicator", dump_progress_indicator, receive_progress_indicator, transmit_progress_indicator }, { 0, Q931_NETWORK_SPEC_FAC, "Network-Specific Facilities", dump_network_spec_fac, receive_network_spec_fac, transmit_network_spec_fac }, { 1, Q931_INFORMATION_RATE, "Information Rate" }, { 1, Q931_TRANSIT_DELAY, "End-to-End Transit Delay" }, { 1, Q931_TRANS_DELAY_SELECT, "Transmit Delay Selection and Indication" }, { 1, Q931_BINARY_PARAMETERS, "Packet-layer Binary Parameters" }, { 1, Q931_WINDOW_SIZE, "Packet-layer Window Size" }, { 1, Q931_CLOSED_USER_GROUP, "Closed User Group" }, { 1, Q931_REVERSE_CHARGE_INDIC, "Reverse Charging Indication", dump_reverse_charging_indication, receive_reverse_charging_indication, transmit_reverse_charging_indication }, { 1, Q931_CALLING_PARTY_NUMBER, "Calling Party Number", dump_calling_party_number, receive_calling_party_number, transmit_calling_party_number }, { 1, Q931_CALLING_PARTY_SUBADDR, "Calling Party Subaddress", dump_calling_party_subaddr, receive_calling_party_subaddr, transmit_calling_party_subaddr }, { 1, Q931_CALLED_PARTY_NUMBER, "Called Party Number", dump_called_party_number, receive_called_party_number, transmit_called_party_number }, { 1, Q931_CALLED_PARTY_SUBADDR, "Called Party Subaddress", dump_called_party_subaddr, receive_called_party_subaddr, transmit_called_party_subaddr }, { 0, Q931_REDIRECTING_NUMBER, "Redirecting Number", dump_redirecting_number, receive_redirecting_number, transmit_redirecting_number }, { 1, Q931_REDIRECTING_SUBADDR, "Redirecting Subaddress", dump_redirecting_subaddr }, { 0, Q931_TRANSIT_NET_SELECT, "Transit Network Selection" }, { 1, Q931_RESTART_INDICATOR, "Restart Indicator", dump_restart_indicator, receive_restart_indicator, transmit_restart_indicator }, { 0, Q931_LOW_LAYER_COMPAT, "Low-layer Compatibility" }, { 0, Q931_HIGH_LAYER_COMPAT, "High-layer Compatibility" }, { 1, Q931_PACKET_SIZE, "Packet Size" }, { 0, Q931_IE_FACILITY, "Facility" , dump_facility, receive_facility, transmit_facility }, { 1, Q931_IE_REDIRECTION_NUMBER, "Redirection Number", dump_redirection_number, receive_redirection_number, transmit_redirection_number }, { 1, Q931_IE_REDIRECTION_SUBADDR, "Redirection Subaddress" }, { 1, Q931_IE_FEATURE_ACTIVATE, "Feature Activation" }, { 1, Q931_IE_INFO_REQUEST, "Feature Request" }, { 1, Q931_IE_FEATURE_IND, "Feature Indication" }, { 1, Q931_IE_SEGMENTED_MSG, "Segmented Message" }, { 1, Q931_IE_CALL_IDENTITY, "Call Identity", dump_call_identity }, { 1, Q931_IE_ENDPOINT_ID, "Endpoint Identification" }, { 1, Q931_IE_NOTIFY_IND, "Notification Indicator", dump_notify, receive_notify, transmit_notify }, { 1, Q931_DISPLAY, "Display", dump_display, receive_display, transmit_display }, { 1, Q931_IE_TIME_DATE, "Date/Time", dump_time_date, receive_time_date, transmit_time_date }, { 1, Q931_IE_KEYPAD_FACILITY, "Keypad Facility", dump_keypad_facility, receive_keypad_facility, transmit_keypad_facility }, { 0, Q931_IE_SIGNAL, "Signal", dump_signal }, { 1, Q931_IE_SWITCHHOOK, "Switch-hook" }, { 1, Q931_IE_USER_USER, "User-User Information", dump_user_user, receive_user_user, transmit_user_user }, { 1, Q931_IE_ESCAPE_FOR_EXT, "Escape for Extension" }, { 1, Q931_IE_CALL_STATUS, "Call Status" }, { 1, Q931_IE_CHANGE_STATUS, "Change Status Information", dump_change_status, receive_change_status, transmit_change_status }, { 1, Q931_IE_CONNECTED_ADDR, "Connected Address", dump_connected_number, receive_connected_number, transmit_connected_number }, { 1, Q931_IE_CONNECTED_NUM, "Connected Number", dump_connected_number, receive_connected_number, transmit_connected_number }, { 1, Q931_IE_CONNECTED_SUBADDR, "Connected Subaddress", dump_connected_subaddr, receive_connected_subaddr, transmit_connected_subaddr }, { 1, Q931_IE_ORIGINAL_CALLED_NUMBER, "Original Called Number", dump_redirecting_number, receive_redirecting_number, transmit_redirecting_number }, { 1, Q931_IE_USER_USER_FACILITY, "User-User Facility" }, { 1, Q931_IE_UPDATE, "Update" }, { 1, Q931_SENDING_COMPLETE, "Sending Complete", dump_sending_complete, receive_sending_complete, transmit_sending_complete }, /* Codeset 4 - Q.SIG specific */ { 1, QSIG_IE_TRANSIT_COUNT | Q931_CODESET(4), "Transit Count", dump_transit_count }, /* Codeset 5 - National specific (ETSI PISN specific) */ { 1, Q931_CALLING_PARTY_CATEGORY, "Calling Party Category", dump_calling_party_category }, /* Codeset 6 - Network specific */ { 1, Q931_IE_ORIGINATING_LINE_INFO, "Originating Line Information", dump_line_information, receive_line_information, transmit_line_information }, { 1, Q931_IE_FACILITY | Q931_CODESET(6), "Facility", dump_facility, receive_facility, transmit_facility }, { 1, Q931_DISPLAY | Q931_CODESET(6), "Display (CS6)", dump_display, receive_display, transmit_display }, { 0, Q931_IE_GENERIC_DIGITS, "Generic Digits", dump_generic_digits, receive_generic_digits, transmit_generic_digits }, /* Codeset 7 */ }; static char *ie2str(int ie) { unsigned int x; /* Special handling for Locking/Non-Locking Shifts */ switch (ie & 0xf8) { case Q931_LOCKING_SHIFT: switch (ie & 7) { case 0: return "!! INVALID Locking Shift To Codeset 0"; case 1: return "Locking Shift To Codeset 1"; case 2: return "Locking Shift To Codeset 2"; case 3: return "Locking Shift To Codeset 3"; case 4: return "Locking Shift To Codeset 4"; case 5: return "Locking Shift To Codeset 5"; case 6: return "Locking Shift To Codeset 6"; case 7: return "Locking Shift To Codeset 7"; default: break; } break; case Q931_NON_LOCKING_SHIFT: switch (ie & 7) { case 0: return "Non-Locking Shift To Codeset 0"; case 1: return "Non-Locking Shift To Codeset 1"; case 2: return "Non-Locking Shift To Codeset 2"; case 3: return "Non-Locking Shift To Codeset 3"; case 4: return "Non-Locking Shift To Codeset 4"; case 5: return "Non-Locking Shift To Codeset 5"; case 6: return "Non-Locking Shift To Codeset 6"; case 7: return "Non-Locking Shift To Codeset 7"; default: break; } break; default: break; } for (x = 0; x < ARRAY_LEN(ies); ++x) { if (ie == ies[x].ie) { return ies[x].name; } } return "Unknown Information Element"; } static inline unsigned int ielen(q931_ie *ie) { if ((ie->ie & 0x80) != 0) return 1; else return 2 + ie->len; } static inline int ielen_checked(q931_ie *ie, int len_remaining) { int len; if (ie->ie & 0x80) { len = 1; } else if (len_remaining < 2) { /* There is no length octet when there should be. */ len = -1; } else { len = 2 + ie->len; if (len_remaining < len) { /* There is not enough room left in the packet for this ie. */ len = -1; } } return len; } const char *msg2str(int msg) { unsigned int x; for (x=0;xcrlen > 3) { pri_error(NULL, "Call Reference Length Too long: %d\n", h->crlen); return Q931_DUMMY_CALL_REFERENCE; } switch (h->crlen) { case 2: cr = 0; for (x = 0; x < h->crlen; ++x) { cr <<= 8; cr |= h->crv[x]; } break; case 1: cr = h->crv[0]; if (cr & 0x80) { cr &= ~0x80; cr |= Q931_CALL_REFERENCE_FLAG; } break; case 0: cr = Q931_DUMMY_CALL_REFERENCE; break; default: pri_error(NULL, "Call Reference Length not supported: %d\n", h->crlen); cr = Q931_DUMMY_CALL_REFERENCE; break; } return cr; } static inline void q931_dumpie(struct pri *ctrl, int codeset, q931_ie *ie, char prefix) { unsigned int x; int full_ie = Q931_FULL_IE(codeset, ie->ie); int base_ie; char *buf = malloc(ielen(ie) * 3 + 1); int buflen = 0; buf[0] = '\0'; if (!(ie->ie & 0x80)) { buflen += sprintf(buf, " %02x", ielen(ie)-2); for (x = 0; x + 2 < ielen(ie); ++x) buflen += sprintf(buf + buflen, " %02x", ie->data[x]); } pri_message(ctrl, "%c [%02x%s]\n", prefix, ie->ie, buf); free(buf); /* Special treatment for shifts */ if((full_ie & 0xf0) == Q931_LOCKING_SHIFT) full_ie &= 0xff; base_ie = (((full_ie & ~0x7f) == Q931_FULL_IE(0, 0x80)) && ((full_ie & 0x70) != 0x20)) ? full_ie & ~0x0f : full_ie; for (x = 0; x < ARRAY_LEN(ies); ++x) if (ies[x].ie == base_ie) { if (ies[x].dump) ies[x].dump(full_ie, ctrl, ie, ielen(ie), prefix); else pri_message(ctrl, "%c IE: %s (len = %d)\n", prefix, ies[x].name, ielen(ie)); return; } pri_error(ctrl, "!! %c Unknown IE %d (cs%d, len = %d)\n", prefix, Q931_IE_IE(base_ie), Q931_IE_CODESET(base_ie), ielen(ie)); } /*! * \brief Initialize the call record. * * \param link Q.921 link associated with the call. * \param call Q.931 call leg. * \param cr Call Reference identifier. * * \note The call record is assumed to already be memset() to zero. * * \return Nothing */ void q931_init_call_record(struct q921_link *link, struct q931_call *call, int cr) { struct pri *ctrl; call->cr = cr; call->slotmap = -1; call->channelno = -1; if (cr != Q931_DUMMY_CALL_REFERENCE) { call->newcall = 1; } call->ourcallstate = Q931_CALL_STATE_NULL; call->peercallstate = Q931_CALL_STATE_NULL; call->sugcallstate = Q931_CALL_STATE_NOT_SET; call->ri = -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; call->cause = -1; call->useruserprotocoldisc = -1; call->aoc_units = -1; call->changestatus = -1; call->reversecharge = -1; call->pri_winner = -1; call->master_call = call; q931_party_number_init(&call->redirection_number); q931_party_address_init(&call->called); q931_party_id_init(&call->local_id); q931_party_id_init(&call->remote_id); q931_party_number_init(&call->ani); q931_party_redirecting_init(&call->redirecting); /* The call is now attached to whoever called us */ ctrl = link->ctrl; call->pri = ctrl; if (cr == Q931_DUMMY_CALL_REFERENCE) { /* Dummy calls are always for the given link. */ call->link = link; } else if (BRI_TE_PTMP(ctrl)) { /* Always uses the specific TEI link. */ call->link = ctrl->link.next; } else { call->link = link; } } /*! * \internal * \brief Create a new call record. * * \param link Q.921 link associated with the call. * \param cr Call Reference identifier. * * \retval record on success. * \retval NULL on error. */ static struct q931_call *q931_create_call_record(struct q921_link *link, int cr) { struct q931_call *call; struct q931_call *prev; struct pri *ctrl; ctrl = link->ctrl; if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, "-- Making new call for cref %d\n", cr); } call = calloc(1, sizeof(*call)); if (!call) { return NULL; } /* Initialize call structure. */ q931_init_call_record(link, call, cr); /* Append to the list end */ if (*ctrl->callpool) { /* Find the list end. */ for (prev = *ctrl->callpool; prev->next; prev = prev->next) { } prev->next = call; } else { /* List was empty. */ *ctrl->callpool = call; } return call; } /*! * \internal * \brief Find a call in the active call pool. * * \param link Q.921 link associated with the call. * \param cr Call Reference identifier. * * \retval call if found. * \retval NULL if not found. */ static struct q931_call *q931_find_call(struct q921_link *link, int cr) { struct q931_call *cur; struct pri *ctrl; if (cr == Q931_DUMMY_CALL_REFERENCE) { return link->dummy_call; } ctrl = link->ctrl; if (BRI_NT_PTMP(ctrl) && !(cr & Q931_CALL_REFERENCE_FLAG)) { if (link->tei == Q921_TEI_GROUP) { /* Broadcast TEI. This is bad. We are using the wrong link structure. */ pri_error(ctrl, "Looking for cref %d when using broadcast TEI.\n", cr); return NULL; } /* We are looking for a call reference value that the other side allocated. */ for (cur = *ctrl->callpool; cur; cur = cur->next) { if (cur->cr == cr && cur->link == link) { /* Found existing call. The call reference and link matched. */ break; } } } else { for (cur = *ctrl->callpool; cur; cur = cur->next) { if (cur->cr == cr) { /* Found existing call. */ switch (ctrl->switchtype) { case PRI_SWITCH_GR303_EOC: case PRI_SWITCH_GR303_TMC: break; default: if (!ctrl->bri) { /* The call is now attached to whoever called us */ cur->pri = ctrl; cur->link = link; } break; } break; } } } return cur; } static struct q931_call *q931_getcall(struct q921_link *link, int cr) { struct q931_call *cur; struct pri *ctrl; cur = q931_find_call(link, cr); if (cur) { return cur; } if (cr == Q931_DUMMY_CALL_REFERENCE) { /* Do not create new dummy call records. */ return NULL; } ctrl = link->ctrl; if (link->tei == Q921_TEI_GROUP && BRI_NT_PTMP(ctrl)) { /* Do not create NT PTMP broadcast call records here. */ pri_error(ctrl, "NT PTMP cannot create call record for cref %d on the broadcast TEI.\n", cr); return NULL; } /* No call record exists, make a new one */ return q931_create_call_record(link, cr); } /*! * \brief Create a new call record for an outgoing call. * * \param ctrl D channel controller. * * \retval call on success. * \retval NULL on error. */ struct q931_call *q931_new_call(struct pri *ctrl) { struct q931_call *cur; struct q921_link *link; int first_cref; int cref; /* Find a new call reference value. */ first_cref = ctrl->cref; do { cref = Q931_CALL_REFERENCE_FLAG | ctrl->cref; /* Next call reference. */ ++ctrl->cref; if (!ctrl->bri) { if (ctrl->cref > 32767) { ctrl->cref = 1; } } else { if (ctrl->cref > 127) { ctrl->cref = 1; } } /* Is the call reference value in use? */ for (cur = *ctrl->callpool; cur; cur = cur->next) { if (cur->cr == cref) { /* Yes it is in use. */ if (first_cref == ctrl->cref) { /* All call reference values are in use! */ return NULL; } break; } } } while (cur); link = &ctrl->link; return q931_create_call_record(link, cref); } static void stop_t303(struct q931_call *call); static void stop_t312(struct q931_call *call) { /* T312 should only be running on the master call */ pri_schedule_del(call->pri, call->t312_timer); call->t312_timer = 0; } static void cleanup_and_free_call(struct q931_call *cur) { struct pri *ctrl; ctrl = cur->pri; pri_schedule_del(ctrl, cur->restart.timer); pri_schedule_del(ctrl, cur->restart_tx.t316_timer); pri_schedule_del(ctrl, cur->retranstimer); pri_schedule_del(ctrl, cur->hold_timer); pri_schedule_del(ctrl, cur->fake_clearing_timer); stop_t303(cur); stop_t312(cur); 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); } int q931_get_subcall_count(struct q931_call *master) { int count = 0; int idx; for (idx = 0; idx < ARRAY_LEN(master->subcalls); ++idx) { if (master->subcalls[idx]) { ++count; } } return count; } static int pri_internal_clear(struct q931_call *call); /*! * \brief Fake RELEASE for NT-PTMP initiated SETUPs w/o response * * \param param call Call is not a subcall call record. */ static void pri_fake_clearing(struct q931_call *call) { struct pri *ctrl; ctrl = call->pri; if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, "Fake clearing. cref:%d\n", call->cr); } /* * This does not need to be running since this is what we are * doing right now anyway. */ pri_schedule_del(ctrl, call->fake_clearing_timer); call->fake_clearing_timer = 0; if (call->cause == -1) { /* Ensure that there is a resonable cause code. */ call->cause = PRI_CAUSE_NO_USER_RESPONSE; } if (pri_internal_clear(call) == Q931_RES_HAVEEVENT) { ctrl->schedev = 1; } } static void pri_fake_clearing_expiry(void *data) { struct q931_call *master = data; master->fake_clearing_timer = 0; pri_fake_clearing(master); } static void pri_create_fake_clearing(struct pri *ctrl, struct q931_call *master) { if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, "Fake clearing requested. cref:%d\n", master->cr); } pri_schedule_del(ctrl, master->fake_clearing_timer); master->fake_clearing_timer = pri_schedule_event(ctrl, 0, pri_fake_clearing_expiry, master); } static void t312_expiry(void *data) { struct q931_call *master = data; struct pri *ctrl; ctrl = master->pri; if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, "T312 timed out. cref:%d\n", master->cr); } master->t312_timer = 0; if (!q931_get_subcall_count(master)) { /* No subcalls remain. */ switch (master->ourcallstate) { case Q931_CALL_STATE_CALL_ABORT: /* We can destroy the master. */ q931_destroycall(ctrl, master); break; default: /* Let the upper layer know about the lack of call prospects. */ UPDATE_OURCALLSTATE(ctrl, master, Q931_CALL_STATE_CALL_ABORT); pri_fake_clearing(master); break; } } } /*! \param master Master call record for PTMP NT call. */ static void start_t312(struct q931_call *master) { struct pri *ctrl; ctrl = master->pri; pri_schedule_del(ctrl, master->t312_timer); master->t312_timer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T312], t312_expiry, master); } /*! * \internal * \brief Helper function to destroy a subcall. * * \param master Q.931 master call of subcall to destroy. * \param idx Master subcall index to destroy. * * \return Nothing */ static void q931_destroy_subcall(struct q931_call *master, int idx) { struct pri *ctrl = master->pri; if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, "Destroying subcall %p of call %p at %d\n", master->subcalls[idx], master, idx); } cleanup_and_free_call(master->subcalls[idx]); if (master->pri_winner == idx) { /* This was the winning subcall. */ master->pri_winner = -1; } master->subcalls[idx] = NULL; } void q931_destroycall(struct pri *ctrl, q931_call *c) { struct q931_call *cur; struct q931_call *prev; struct q931_call *slave; int i; int slavesleft; if (q931_is_dummy_call(c)) { /* Cannot destroy the dummy call. */ return; } if (c->master_call != c) { slave = c; c = slave->master_call; } else { slave = NULL; } prev = NULL; cur = *ctrl->callpool; while (cur) { if (cur == c) { if (slave) { /* Destroying a slave. */ for (i = 0; i < ARRAY_LEN(cur->subcalls); ++i) { if (cur->subcalls[i] == slave) { q931_destroy_subcall(cur, i); break; } } /* How many slaves are left? */ slavesleft = 0; for (i = 0; i < ARRAY_LEN(cur->subcalls); ++i) { if (cur->subcalls[i]) { if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, "Subcall still present at %d\n", i); } ++slavesleft; } } if (slavesleft || cur->t312_timer || cur->master_hanging_up) { return; } /* No slaves left. */ switch (cur->ourcallstate) { case Q931_CALL_STATE_CALL_ABORT: break; default: /* Let the upper layer know about the call clearing. */ UPDATE_OURCALLSTATE(ctrl, cur, Q931_CALL_STATE_CALL_ABORT); pri_create_fake_clearing(ctrl, cur); return; } /* We can try to destroy the master now. */ } else { /* Destroy any slaves that may be present as well. */ slavesleft = 0; for (i = 0; i < ARRAY_LEN(cur->subcalls); ++i) { if (cur->subcalls[i]) { ++slavesleft; q931_destroy_subcall(cur, i); } } } if (cur->fake_clearing_timer) { /* * Must wait for the fake clearing to complete before destroying * the master call record. */ return; } if (slavesleft) { /* This is likely not good. */ pri_error(ctrl, "Destroyed %d subcalls unconditionally with the master. cref:%d\n", slavesleft, cur->cr); } /* Master call or normal call destruction. */ if (prev) prev->next = cur->next; else *ctrl->callpool = cur->next; if (ctrl->debug & PRI_DEBUG_Q931_STATE) pri_message(ctrl, "Destroying call %p, ourstate %s, peerstate %s, hold-state %s\n", cur, q931_call_state_str(cur->ourcallstate), q931_call_state_str(cur->peercallstate), q931_hold_state_str(cur->hold_state)); cleanup_and_free_call(cur); return; } prev = cur; cur = cur->next; } pri_error(ctrl, "Can't destroy call %p cref:%d!\n", c, c->cr); } static int add_ie(struct pri *ctrl, q931_call *call, int msgtype, int ie, q931_ie *iet, int maxlen, int *codeset) { unsigned int x; int res, total_res; int have_shift; int ies_count, order; for (x = 0; x < ARRAY_LEN(ies); ++x) { if (ies[x].ie == ie) { /* This is our baby */ if (ies[x].transmit) { /* Prepend with CODE SHIFT IE if required */ if (*codeset != Q931_IE_CODESET(ies[x].ie)) { /* Locking shift to codeset 0 isn't possible */ iet->ie = Q931_IE_CODESET(ies[x].ie) | (Q931_IE_CODESET(ies[x].ie) ? Q931_LOCKING_SHIFT : Q931_NON_LOCKING_SHIFT); have_shift = 1; iet = (q931_ie *)((char *)iet + 1); maxlen--; } else have_shift = 0; ies_count = ies[x].max_count; if (ies_count == 0) ies_count = INT_MAX; order = 0; total_res = 0; do { iet->ie = ie; res = ies[x].transmit(ie, ctrl, call, msgtype, iet, maxlen, ++order); /* Error if res < 0 or ignored if res == 0 */ if (res < 0) return res; if (res > 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); } } while (res > 0 && order < ies_count); if (have_shift && total_res) { if (Q931_IE_CODESET(ies[x].ie)) *codeset = Q931_IE_CODESET(ies[x].ie); return total_res + 1; /* Shift is single-byte IE */ } return total_res; } else { pri_error(ctrl, "!! Don't know how to add an IE %s (%d)\n", ie2str(ie), ie); return -1; } } } pri_error(ctrl, "!! Unknown IE %d (%s)\n", ie, ie2str(ie)); return -1; } static char *disc2str(int disc) { static struct msgtype discs[] = { { Q931_PROTOCOL_DISCRIMINATOR, "Q.931" }, { GR303_PROTOCOL_DISCRIMINATOR, "GR-303" }, { MAINTENANCE_PROTOCOL_DISCRIMINATOR_1, "AT&T Maintenance" }, { MAINTENANCE_PROTOCOL_DISCRIMINATOR_2, "New AT&T Maintenance" }, }; return code2str(disc, discs, sizeof(discs) / sizeof(discs[0])); } /*! * \internal * \brief Dump the Q.931 message header. * * \param ctrl D channel controller. * \param tei TEI the packet is associated with. * \param h Q.931 packet contents/header. * \param len Received length of the Q.931 packet. * \param c Message direction prefix character. * * \retval 0 on success. * \retval -1 on error. */ static int q931_dump_header(struct pri *ctrl, int tei, q931_h *h, int len, char c) { q931_mh *mh; int cref; pri_message(ctrl, "%c Protocol Discriminator: %s (%d) len=%d\n", c, disc2str(h->pd), h->pd, len); if (len < 2 || len < 2 + h->crlen) { pri_message(ctrl, "%c Message too short for call reference. len=%d\n", c, len); return -1; } cref = q931_cr(h); pri_message(ctrl, "%c TEI=%d Call Ref: len=%2d (reference %d/0x%X) (%s)\n", c, tei, h->crlen, cref & ~Q931_CALL_REFERENCE_FLAG, cref & ~Q931_CALL_REFERENCE_FLAG, (cref == Q931_DUMMY_CALL_REFERENCE) ? "Dummy" : (cref & Q931_CALL_REFERENCE_FLAG) ? "Sent to originator" : "Sent from originator"); if (len < 3 + h->crlen) { pri_message(ctrl, "%c Message too short for supported protocols. len=%d\n", c, len); return -1; } /* Message header begins at the end of the call reference number */ mh = (q931_mh *)(h->contents + h->crlen); switch (h->pd) { case MAINTENANCE_PROTOCOL_DISCRIMINATOR_1: case MAINTENANCE_PROTOCOL_DISCRIMINATOR_2: pri_message(ctrl, "%c Message Type: %s (%d)\n", c, maintenance_msg2str(mh->msg, h->pd), mh->msg); break; default: /* Unknown protocol discriminator but we will treat it as Q.931 anyway. */ case GR303_PROTOCOL_DISCRIMINATOR: case Q931_PROTOCOL_DISCRIMINATOR: pri_message(ctrl, "%c Message Type: %s (%d)\n", c, msg2str(mh->msg), mh->msg); break; } return 0; } /*! * \internal * \brief Q.931 is passing this message to Q.921 debug indication. * * \param ctrl D channel controller. * \param tei TEI the packet is associated with. * \param h Q.931 packet contents/header. * \param len Received length of the Q.931 packet * * \return Nothing */ static void q931_to_q921_passing_dump(struct pri *ctrl, int tei, q931_h *h, int len) { char c; c = '>'; pri_message(ctrl, "\n"); pri_message(ctrl, "%c DL-DATA request\n", c); q931_dump_header(ctrl, tei, h, len, c); } /*! * \brief Debug dump the given Q.931 packet. * * \param ctrl D channel controller. * \param tei TEI the packet is associated with. * \param h Q.931 packet contents/header. * \param len Received length of the Q.931 packet * \param txrx TRUE if packet is transmitted/outgoing * * \return Nothing */ void q931_dump(struct pri *ctrl, int tei, q931_h *h, int len, int txrx) { q931_mh *mh; char c; int x; int r; int cur_codeset; int codeset; c = txrx ? '>' : '<'; if (!(ctrl->debug & (PRI_DEBUG_Q921_DUMP | PRI_DEBUG_Q921_RAW))) { /* Put out a blank line if Q.921 is not dumping. */ pri_message(ctrl, "\n"); } if (q931_dump_header(ctrl, tei, h, len, c)) { return; } /* Drop length of header, including call reference */ mh = (q931_mh *)(h->contents + h->crlen); len -= (h->crlen + 3); codeset = cur_codeset = 0; for (x = 0; x < len; x += r) { r = ielen_checked((q931_ie *) (mh->data + x), len - x); if (r < 0) { /* We have garbage on the end of the packet. */ pri_message(ctrl, "Not enough room for codeset:%d ie:%d(%02x)\n", cur_codeset, mh->data[x], mh->data[x]); break; } q931_dumpie(ctrl, cur_codeset, (q931_ie *)(mh->data + x), c); switch (mh->data[x] & 0xf8) { case Q931_LOCKING_SHIFT: if ((mh->data[x] & 7) > 0) codeset = cur_codeset = mh->data[x] & 7; break; case Q931_NON_LOCKING_SHIFT: cur_codeset = mh->data[x] & 7; break; default: /* Reset temporary codeset change */ cur_codeset = codeset; break; } } } static int q931_handle_ie(int codeset, struct pri *ctrl, q931_call *c, int msg, q931_ie *ie) { unsigned int x; 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)); if (msg == Q931_SETUP && codeset == 0) { switch (ie->ie) { 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)); else { if (ctrl->debug & PRI_DEBUG_Q931_ANOMALY) pri_message(ctrl, "!! No handler for IE %d (cs%d, %s)\n", ie->ie, codeset, ie2str(full_ie)); return -1; } } } pri_message(ctrl, "!! Unknown IE %d (cs%d)\n", ie->ie, codeset); return -1; } /* Returns header and message header and modifies length in place */ static void init_header(struct pri *ctrl, q931_call *call, unsigned char *buf, q931_h **hb, q931_mh **mhb, int *len, int protodisc) { q931_h *h = (q931_h *) buf; q931_mh *mh; unsigned crv; if (protodisc) { h->pd = protodisc; } else { h->pd = ctrl->protodisc; } h->x0 = 0; /* Reserved 0 */ if (q931_is_dummy_call(call)) { h->crlen = 0; } else if (!ctrl->bri) { /* Two bytes of Call Reference. */ h->crlen = 2; if (ctrl->link.next) { /* On GR-303, Q931_CALL_REFERENCE_FLAG is always 0 */ crv = ((unsigned) call->cr) & ~Q931_CALL_REFERENCE_FLAG; } else { /* Invert the Q931_CALL_REFERENCE_FLAG to make it from our sense */ crv = ((unsigned) call->cr) ^ Q931_CALL_REFERENCE_FLAG; } h->crv[0] = (crv >> 8) & 0xff; h->crv[1] = crv & 0xff; } else { h->crlen = 1; /* Invert the Q931_CALL_REFERENCE_FLAG to make it from our sense */ crv = ((unsigned) call->cr) ^ Q931_CALL_REFERENCE_FLAG; h->crv[0] = ((crv >> 8) & 0x80) | (crv & 0x7f); } *hb = h; *len -= 3;/* Protocol discriminator, call reference length, message type id */ *len -= h->crlen; mh = (q931_mh *) (h->contents + h->crlen); mh->f = 0; *mhb = mh; } static void q931_xmit(struct q921_link *link, q931_h *h, int len, int cr, int uiframe) { struct pri *ctrl; ctrl = link->ctrl; ctrl->q931_txcount++; if (uiframe) { if (link->tei != Q921_TEI_GROUP) { pri_error(ctrl, "Huh?! Attempting to send UI-frame on TEI %d\n", link->tei); return; } q921_transmit_uiframe(link, h, len); if (ctrl->debug & PRI_DEBUG_Q931_DUMP) { /* * The transmit operation might dump the Q.921 header, so logging * the Q.931 message body after the transmit puts the sections of * the message in the right order in the log, */ q931_dump(ctrl, link->tei, h, len, 1); } } else { /* * Indicate passing the Q.931 message to Q.921 first. Q.921 may * have to request a TEI or bring the connection up before it can * actually send the message. Therefore, the Q.931 message may * actually get sent a few seconds later. Q.921 will dump the * Q.931 message as appropriate at that time. */ if (ctrl->debug & PRI_DEBUG_Q931_DUMP) { q931_to_q921_passing_dump(ctrl, link->tei, h, len); } q921_transmit_iframe(link, h, len, cr); } } /*! * \internal * \brief Build and send the requested message. * * \param ctrl D channel controller. * \param call Q.931 call leg * \param msgtype Q.931 message type to build. * \param ies List of ie's to put in the message. * * \note The ie's in the ie list must be in numerical order. * See Q.931 section 4.5.1 coding rules. * * \retval 0 on success. * \retval -1 on error. */ static int send_message(struct pri *ctrl, q931_call *call, int msgtype, int ies[]) { unsigned char buf[1024]; q931_h *h; q931_mh *mh; int len; int res; int offset=0; int x; int codeset; int uiframe; if (call->outboundbroadcast && call->master_call == call && msgtype != Q931_SETUP) { pri_error(ctrl, "Attempting to use master call record to send %s on BRI PTMP NT %p\n", msg2str(msgtype), ctrl); return -1; } if (!call->link) { pri_error(ctrl, "Call w/ cref:%d is not associated with a link. TEI removed due to error conditions?\n", call->cr); return -1; } memset(buf, 0, sizeof(buf)); len = sizeof(buf); init_header(ctrl, call, buf, &h, &mh, &len, (msgtype >> 8)); mh->msg = msgtype & 0x00ff; x=0; codeset = 0; while(ies[x] > -1) { res = add_ie(ctrl, call, mh->msg, ies[x], (q931_ie *)(mh->data + offset), len, &codeset); if (res < 0) { pri_error(ctrl, "!! Unable to add IE '%s'\n", ie2str(ies[x])); return -1; } offset += res; len -= res; x++; } /* Invert the logic */ len = sizeof(buf) - len; uiframe = 0; if (BRI_NT_PTMP(ctrl)) { /* NT PTMP is the only mode that can broadcast Q.931 messages. */ switch (msgtype) { case Q931_SETUP: /* * For NT-PTMP mode, we need to check the following: * MODE = NT-PTMP * MESSAGE = SETUP * * If those are true, we need to send the SETUP in a UI frame * instead of an I-frame. */ uiframe = 1; break; case Q931_FACILITY: if (call->link->tei == Q921_TEI_GROUP) { /* Broadcast TEI. */ if (q931_is_dummy_call(call)) { /* * This is a FACILITY message on the dummy call reference * for the broadcast TEI. */ uiframe = 1; } else { pri_error(ctrl, "Attempting to broadcast %s on cref %d\n", msg2str(msgtype), call->cr); return -1; } } break; default: break; } if (ctrl->debug & PRI_DEBUG_Q931_STATE) { /* This message is only interesting for NT PTMP mode. */ pri_message(ctrl, "Sending message for call %p on call->link: %p with TEI/SAPI %d/%d\n", call, call->link, call->link->tei, call->link->sapi); } } q931_xmit(call->link, h, len, 1, uiframe); call->acked = 1; return 0; } static int maintenance_service_ies[] = { Q931_IE_CHANGE_STATUS, Q931_CHANNEL_IDENT, -1 }; static int maintenance_service_ack(struct pri *ctrl, q931_call *c) { int pd = MAINTENANCE_PROTOCOL_DISCRIMINATOR_1; int mt = ATT_SERVICE_ACKNOWLEDGE; if (ctrl->switchtype == PRI_SWITCH_NI2) { pd = MAINTENANCE_PROTOCOL_DISCRIMINATOR_2; mt = NATIONAL_SERVICE_ACKNOWLEDGE; } return send_message(ctrl, c, (pd << 8) | mt, maintenance_service_ies); } /*! * \note Maintenance service messages only supported in PRI mode. */ int maintenance_service(struct pri *ctrl, int span, int channel, int changestatus) { struct q931_call *c; int pd = MAINTENANCE_PROTOCOL_DISCRIMINATOR_1; int mt = ATT_SERVICE; c = q931_getcall(&ctrl->link, 0 | Q931_CALL_REFERENCE_FLAG); if (!c) { return -1; } if (channel > -1) { c->channelno = channel & 0xff; c->chanflags = FLAG_EXCLUSIVE; } else { c->channelno = channel; c->chanflags = FLAG_EXCLUSIVE | FLAG_WHOLE_INTERFACE; } c->ds1no = span; c->ds1explicit = 0; c->changestatus = changestatus; if (ctrl->switchtype == PRI_SWITCH_NI2) { pd = MAINTENANCE_PROTOCOL_DISCRIMINATOR_2; mt = NATIONAL_SERVICE; } return send_message(ctrl, c, (pd << 8) | mt, maintenance_service_ies); } static int q931_status(struct pri *ctrl, q931_call *call, int cause) { static int status_ies[] = { Q931_CAUSE, Q931_IE_CALL_STATE, -1 }; call->cause = cause; call->causecode = CODE_CCITT; call->causeloc = LOC_USER; return send_message(ctrl, call, Q931_STATUS, status_ies); } int q931_information(struct pri *ctrl, q931_call *c, char digit) { static int information_ies[] = { Q931_CALLED_PARTY_NUMBER, -1 }; c->overlap_digits[0] = digit; c->overlap_digits[1] = '\0'; /* * Since we are doing overlap dialing now, we need to accumulate * the digits into call->called.number.str. */ c->called.number.valid = 1; if (strlen(c->called.number.str) < sizeof(c->called.number.str) - 1) { /* There is enough room for the new digit. */ strcat(c->called.number.str, c->overlap_digits); } return send_message(ctrl, c, Q931_INFORMATION, information_ies); } /*! * \internal * \brief Actually send display text if in the right call state. * * \param ctrl D channel controller. * \param call Q.931 call leg * \param display Display text to send. * * \retval 0 on success. * \retval -1 on error. */ static int q931_display_text_helper(struct pri *ctrl, struct q931_call *call, const struct pri_subcmd_display_txt *display) { int status; static int information_display_ies[] = { Q931_DISPLAY, -1 }; switch (call->ourcallstate) { case Q931_CALL_STATE_OVERLAP_SENDING: case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: case Q931_CALL_STATE_CALL_DELIVERED: case Q931_CALL_STATE_CALL_RECEIVED: case Q931_CALL_STATE_CONNECT_REQUEST: case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: case Q931_CALL_STATE_ACTIVE: case Q931_CALL_STATE_OVERLAP_RECEIVING: call->display.text = (unsigned char *) display->text; call->display.full_ie = 0; call->display.length = display->length; call->display.char_set = display->char_set; status = send_message(ctrl, call, Q931_INFORMATION, information_display_ies); q931_display_clear(call); break; default: status = 0; break; } return status; } /*! * \brief Send display text during a call. * * \param ctrl D channel controller. * \param call Q.931 call leg * \param display Display text to send. * * \retval 0 on success. * \retval -1 on error. */ int q931_display_text(struct pri *ctrl, struct q931_call *call, const struct pri_subcmd_display_txt *display) { int status; unsigned idx; struct q931_call *subcall; if ((ctrl->display_flags.send & (PRI_DISPLAY_OPTION_BLOCK | PRI_DISPLAY_OPTION_TEXT)) != PRI_DISPLAY_OPTION_TEXT) { /* Not enabled */ return 0; } if (call->outboundbroadcast && call->master_call == call) { status = 0; for (idx = 0; idx < ARRAY_LEN(call->subcalls); ++idx) { subcall = call->subcalls[idx]; if (subcall && q931_display_text_helper(ctrl, subcall, display)) { status = -1; } } } else { status = q931_display_text_helper(ctrl, call, display); } return status; } static int keypad_facility_ies[] = { Q931_IE_KEYPAD_FACILITY, -1 }; int q931_keypad_facility(struct pri *ctrl, q931_call *call, const char *digits) { libpri_copy_string(call->keypad_digits, digits, sizeof(call->keypad_digits)); return send_message(ctrl, call, Q931_INFORMATION, keypad_facility_ies); } static int restart_ack_ies[] = { Q931_CHANNEL_IDENT, Q931_RESTART_INDICATOR, -1 }; static int restart_ack(struct pri *ctrl, q931_call *c) { UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL); c->peercallstate = Q931_CALL_STATE_NULL; return send_message(ctrl, c, Q931_RESTART_ACKNOWLEDGE, restart_ack_ies); } int q931_facility(struct pri *ctrl, struct q931_call *call) { static int facility_ies[] = { Q931_IE_FACILITY, -1 }; return send_message(ctrl, call, Q931_FACILITY, facility_ies); } /*! * \brief Send a FACILITY message with the called party number and subaddress ies. * * \param ctrl D channel controller. * \param call Call leg to send message over. * \param called Called party information to send. * * \note * This function can only be used by the dummy call because the call's called * structure is used by normal calls to contain persistent information. * * \retval 0 on success. * \retval -1 on error. */ int q931_facility_called(struct pri *ctrl, struct q931_call *call, const struct q931_party_id *called) { static int facility_called_ies[] = { Q931_IE_FACILITY, Q931_CALLED_PARTY_NUMBER, Q931_CALLED_PARTY_SUBADDR, -1 }; q931_party_id_copy_to_address(&call->called, called); libpri_copy_string(call->overlap_digits, call->called.number.str, sizeof(call->overlap_digits)); return send_message(ctrl, call, Q931_FACILITY, facility_called_ies); } int q931_facility_display_name(struct pri *ctrl, struct q931_call *call, const struct q931_party_name *name) { int status; static int facility_display_ies[] = { Q931_IE_FACILITY, Q931_DISPLAY, -1 }; q931_display_name_send(call, name); status = send_message(ctrl, call, Q931_FACILITY, facility_display_ies); q931_display_clear(call); return status; } /*! * \brief Send a FACILITY RequestSubaddress with optional redirection name and number. * * \param ctrl D channel controller. * \param call Q.931 call leg * \param notify Notification indicator * \param name Redirection name to send if not NULL. * \param number Redirection number to send if not NULL. * * \retval 0 on success. * \retval -1 on error. */ int q931_request_subaddress(struct pri *ctrl, struct q931_call *call, int notify, const struct q931_party_name *name, const struct q931_party_number *number) { int status; struct q931_call *winner; static int facility_notify_ies[] = { Q931_IE_FACILITY, Q931_IE_NOTIFY_IND, Q931_DISPLAY, Q931_IE_REDIRECTION_NUMBER, -1 }; winner = q931_find_winning_call(call); if (!winner) { return -1; } q931_display_clear(winner); if (number) { winner->redirection_number = *number; if (number->valid && name && (ctrl->display_flags.send & PRI_DISPLAY_OPTION_NAME_UPDATE)) { q931_display_name_send(winner, name); } } else { q931_party_number_init(&winner->redirection_number); } winner->notify = notify; if (rose_request_subaddress_encode(ctrl, winner) || send_message(ctrl, winner, Q931_FACILITY, facility_notify_ies)) { pri_message(ctrl, "Could not schedule facility message for request subaddress.\n"); status = -1; } else { status = 0; } q931_display_clear(winner); return status; } /*! * \brief Send a FACILITY SubaddressTransfer to all parties. * * \param ctrl D channel controller. * \param call Q.931 call leg * * \retval 0 on success. * \retval -1 on error. */ int q931_subaddress_transfer(struct pri *ctrl, struct q931_call *call) { int status; unsigned idx; struct q931_call *subcall; if (call->outboundbroadcast && call->master_call == call) { status = 0; for (idx = 0; idx < ARRAY_LEN(call->subcalls); ++idx) { subcall = call->subcalls[idx]; if (subcall) { /* Send to all subcalls that have given a positive response. */ switch (subcall->ourcallstate) { case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: case Q931_CALL_STATE_CALL_DELIVERED: case Q931_CALL_STATE_ACTIVE: if (send_subaddress_transfer(ctrl, subcall)) { status = -1; } break; default: break; } } } } else { status = send_subaddress_transfer(ctrl, call); } return status; } /*! * \internal * \brief Actually send a NOTIFY message with optional redirection name and number. * * \param ctrl D channel controller. * \param call Q.931 call leg * \param notify Notification indicator * \param name Redirection display name to send if not NULL. * \param number Redirection number to send if not NULL. * * \retval 0 on success. * \retval -1 on error. */ static int q931_notify_redirection_helper(struct pri *ctrl, struct q931_call *call, int notify, const struct q931_party_name *name, const struct q931_party_number *number) { int status; static int notify_ies[] = { Q931_IE_NOTIFY_IND, Q931_DISPLAY, Q931_IE_REDIRECTION_NUMBER, -1 }; q931_display_clear(call); if (number) { call->redirection_number = *number; if (number->valid && name && (ctrl->display_flags.send & PRI_DISPLAY_OPTION_NAME_UPDATE)) { q931_display_name_send(call, name); } } else { q931_party_number_init(&call->redirection_number); } call->notify = notify; status = send_message(ctrl, call, Q931_NOTIFY, notify_ies); q931_display_clear(call); return status; } /*! * \brief Send a NOTIFY message with optional redirection name and number. * * \param ctrl D channel controller. * \param call Q.931 call leg * \param notify Notification indicator * \param name Redirection display name to send if not NULL. * \param number Redirection number to send if not NULL. * * \retval 0 on success. * \retval -1 on error. */ int q931_notify_redirection(struct pri *ctrl, struct q931_call *call, int notify, const struct q931_party_name *name, const struct q931_party_number *number) { int status; unsigned idx; struct q931_call *subcall; if (call->outboundbroadcast && call->master_call == call) { status = 0; for (idx = 0; idx < ARRAY_LEN(call->subcalls); ++idx) { subcall = call->subcalls[idx]; if (subcall) { /* Send to all subcalls that have given a positive response. */ switch (subcall->ourcallstate) { case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: case Q931_CALL_STATE_CALL_DELIVERED: case Q931_CALL_STATE_ACTIVE: if (q931_notify_redirection_helper(ctrl, subcall, notify, name, number)) { status = -1; } break; default: break; } } } } else { status = q931_notify_redirection_helper(ctrl, call, notify, name, number); } return status; } int q931_notify(struct pri *ctrl, q931_call *c, int channel, int info) { switch (ctrl->switchtype) { case PRI_SWITCH_EUROISDN_T1: case PRI_SWITCH_EUROISDN_E1: break; default: if ((info > 0x2) || (info < 0x00)) { return -1; } break; } if (info >= 0) { info &= 0x7F; } else { /* Cannot send NOTIFY message if the mandatory ie is not going to be present. */ return -1; } return q931_notify_redirection(ctrl, c, info, NULL, NULL); } #ifdef ALERTING_NO_PROGRESS static int call_progress_ies[] = { -1 }; #else static int call_progress_ies[] = { Q931_PROGRESS_INDICATOR, -1 }; #endif 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; c->channelno = channel & 0xff; } if (info) { c->progloc = LOC_PRIV_NET_LOCAL_USER; c->progcode = CODE_CCITT; c->progressmask = PRI_PROG_INBAND_AVAILABLE; } else { /* PI is mandatory IE for PROGRESS message - Q.931 3.1.8 */ pri_error(ctrl, "XXX Progress message requested but no information is provided\n"); c->progressmask = 0; } c->alive = 1; return send_message(ctrl, c, Q931_PROGRESS, call_progress_ies); } #ifdef ALERTING_NO_PROGRESS static int call_progress_with_cause_ies[] = { Q931_CAUSE, -1 }; #else static int call_progress_with_cause_ies[] = { Q931_CAUSE, Q931_PROGRESS_INDICATOR, -1 }; #endif 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; c->channelno = channel & 0xff; } if (info) { c->progloc = LOC_PRIV_NET_LOCAL_USER; c->progcode = CODE_CCITT; c->progressmask = PRI_PROG_INBAND_AVAILABLE; } else { /* PI is mandatory IE for PROGRESS message - Q.931 3.1.8 */ pri_error(ctrl, "XXX Progress message requested but no information is provided\n"); c->progressmask = 0; } c->cause = cause; c->causecode = CODE_CCITT; c->causeloc = LOC_PRIV_NET_LOCAL_USER; c->alive = 1; return send_message(ctrl, c, Q931_PROGRESS, call_progress_with_cause_ies); } #ifdef ALERTING_NO_PROGRESS static int call_proceeding_ies[] = { Q931_CHANNEL_IDENT, -1 }; #else static int call_proceeding_ies[] = { Q931_CHANNEL_IDENT, Q931_PROGRESS_INDICATOR, -1 }; #endif int q931_call_proceeding(struct pri *ctrl, q931_call *c, int channel, int info) { if (c->proc) { /* 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; c->channelno = channel & 0xff; } c->chanflags &= ~FLAG_PREFERRED; c->chanflags |= FLAG_EXCLUSIVE; UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_INCOMING_CALL_PROCEEDING); c->peercallstate = Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING; if (info) { c->progloc = LOC_PRIV_NET_LOCAL_USER; c->progcode = CODE_CCITT; c->progressmask = PRI_PROG_INBAND_AVAILABLE; } else c->progressmask = 0; c->proc = 1; c->alive = 1; return send_message(ctrl, c, Q931_CALL_PROCEEDING, call_proceeding_ies); } #ifndef ALERTING_NO_PROGRESS static int alerting_ies[] = { Q931_IE_FACILITY, Q931_PROGRESS_INDICATOR, Q931_IE_USER_USER, -1 }; #else static int alerting_ies[] = { Q931_IE_FACILITY, -1 }; #endif 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) { c->progloc = LOC_PRIV_NET_LOCAL_USER; c->progcode = CODE_CCITT; c->progressmask = PRI_PROG_INBAND_AVAILABLE; } else c->progressmask = 0; UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_CALL_RECEIVED); c->peercallstate = Q931_CALL_STATE_CALL_DELIVERED; c->alive = 1; switch (ctrl->switchtype) { case PRI_SWITCH_QSIG: if (c->local_id.name.valid) { /* Send calledName with ALERTING */ rose_called_name_encode(ctrl, c, Q931_ALERTING); } break; default: 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); } static int setup_ack_ies[] = { Q931_CHANNEL_IDENT, Q931_IE_FACILITY, Q931_PROGRESS_INDICATOR, -1 }; 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; c->channelno = channel & 0xff; } c->chanflags &= ~FLAG_PREFERRED; c->chanflags |= FLAG_EXCLUSIVE; if (nonisdn && (ctrl->switchtype != PRI_SWITCH_DMS100)) { c->progloc = LOC_PRIV_NET_LOCAL_USER; c->progcode = CODE_CCITT; c->progressmask = PRI_PROG_CALLED_NOT_ISDN; } else c->progressmask = 0; UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_OVERLAP_RECEIVING); c->peercallstate = Q931_CALL_STATE_OVERLAP_SENDING; c->alive = 1; return send_message(ctrl, c, Q931_SETUP_ACKNOWLEDGE, setup_ack_ies); } /* T313 expiry, first time */ static void pri_connect_timeout(void *data) { struct q931_call *c = data; struct pri *ctrl = c->pri; if (ctrl->debug & PRI_DEBUG_Q931_STATE) pri_message(ctrl, "Timed out looking for connect acknowledge\n"); c->retranstimer = 0; q931_disconnect(ctrl, c, PRI_CAUSE_NORMAL_CLEARING); } /* T308 expiry, first time */ static void pri_release_timeout(void *data) { struct q931_call *c = data; struct pri *ctrl = c->pri; if (ctrl->debug & PRI_DEBUG_Q931_STATE) pri_message(ctrl, "Timed out looking for release complete\n"); c->t308_timedout++; c->retranstimer = 0; c->alive = 1; /* The call to q931_release will re-schedule T308 */ q931_release(ctrl, c, c->cause); } /* T308 expiry, second time */ static void pri_release_finaltimeout(void *data) { struct q931_call *c = data; struct pri *ctrl = c->pri; c->retranstimer = 0; c->alive = 1; if (ctrl->debug & PRI_DEBUG_Q931_STATE) pri_message(ctrl, "Final time-out looking for release complete\n"); c->t308_timedout++; c->ourcallstate = Q931_CALL_STATE_NULL; c->peercallstate = Q931_CALL_STATE_NULL; q931_clr_subcommands(ctrl); ctrl->schedev = 1; ctrl->ev.e = PRI_EVENT_HANGUP_ACK; ctrl->ev.hangup.subcmds = &ctrl->subcmds; ctrl->ev.hangup.channel = q931_encode_channel(c); ctrl->ev.hangup.cause = c->cause; ctrl->ev.hangup.cref = c->cr; ctrl->ev.hangup.call = c->master_call; ctrl->ev.hangup.aoc_units = c->aoc_units; ctrl->ev.hangup.call_held = NULL; ctrl->ev.hangup.call_active = NULL; libpri_copy_string(ctrl->ev.hangup.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.hangup.useruserinfo)); pri_hangup(ctrl, c, c->cause); } /* T305 expiry, first time */ static void pri_disconnect_timeout(void *data) { struct q931_call *c = data; struct pri *ctrl = c->pri; if (ctrl->debug & PRI_DEBUG_Q931_STATE) pri_message(ctrl, "Timed out looking for release\n"); c->retranstimer = 0; c->alive = 1; q931_release(ctrl, c, PRI_CAUSE_NORMAL_CLEARING); } static int connect_ies[] = { Q931_CHANNEL_IDENT, Q931_IE_FACILITY, Q931_PROGRESS_INDICATOR, Q931_DISPLAY, Q931_IE_TIME_DATE, Q931_IE_CONNECTED_NUM, Q931_IE_CONNECTED_SUBADDR, -1 }; 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; c->channelno = channel & 0xff; } c->chanflags &= ~FLAG_PREFERRED; c->chanflags |= FLAG_EXCLUSIVE; if (nonisdn && (ctrl->switchtype != PRI_SWITCH_DMS100)) { c->progloc = LOC_PRIV_NET_LOCAL_USER; c->progcode = CODE_CCITT; c->progressmask = PRI_PROG_CALLED_NOT_ISDN; } else c->progressmask = 0; if(ctrl->localtype == PRI_NETWORK || ctrl->switchtype == PRI_SWITCH_QSIG) UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_ACTIVE); else UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_CONNECT_REQUEST); c->peercallstate = Q931_CALL_STATE_ACTIVE; c->alive = 1; /* Connect request timer */ pri_schedule_del(ctrl, c->retranstimer); c->retranstimer = 0; if ((c->ourcallstate == Q931_CALL_STATE_CONNECT_REQUEST) && (ctrl->bri || (!ctrl->link.next))) c->retranstimer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T313], pri_connect_timeout, c); if (c->redirecting.state == Q931_REDIRECTING_STATE_PENDING_TX_DIV_LEG_3) { c->redirecting.state = Q931_REDIRECTING_STATE_IDLE; /* Send DivertingLegInformation3 with CONNECT. */ c->redirecting.to = c->local_id; if (!c->redirecting.to.number.valid) { q931_party_number_init(&c->redirecting.to.number); c->redirecting.to.number.valid = 1; c->redirecting.to.number.presentation = PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; } rose_diverting_leg_information3_encode(ctrl, c, Q931_CONNECT); } switch (ctrl->switchtype) { case PRI_SWITCH_QSIG: if (c->local_id.name.valid) { /* Send connectedName with CONNECT */ rose_connected_name_encode(ctrl, c, Q931_CONNECT); } break; default: break; } if (ctrl->display_flags.send & PRI_DISPLAY_OPTION_NAME_INITIAL) { q931_display_name_send(c, &c->local_id.name); } else { q931_display_clear(c); } return send_message(ctrl, c, Q931_CONNECT, connect_ies); } 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) { UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_RELEASE_REQUEST); /* c->peercallstate stays the same */ if (c->alive) { c->alive = 0; c->cause = cause; c->causecode = CODE_CCITT; c->causeloc = LOC_PRIV_NET_LOCAL_USER; if (c->acked) { pri_schedule_del(ctrl, c->retranstimer); if (!c->t308_timedout) { c->retranstimer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T308], pri_release_timeout, c); } 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 { 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; } static int restart_ies[] = { Q931_CHANNEL_IDENT, Q931_RESTART_INDICATOR, -1 }; static void t316_expire(void *vcall); /*! * \internal * \brief Send the RESTART message to the peer. * * \param call Q.931 call leg * \param channel Encoded channel id to use. * * \retval 0 on success. * \retval -1 on error. */ static int q931_send_restart(struct q931_call *call) { struct pri *ctrl = call->pri; int channel = call->restart_tx.channel; /* Start timer T316 if enabled. */ if (0 < ctrl->timers[PRI_TIMER_T316]) { call->restart_tx.t316_timer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T316], t316_expire, call); --call->restart_tx.remain; } call->ri = 0; call->ds1no = (channel >> 8) & 0xFF; call->ds1explicit = (channel >> 16) & 0x1; call->channelno = channel & 0xFF; call->chanflags &= ~FLAG_PREFERRED; call->chanflags |= FLAG_EXCLUSIVE; UPDATE_OURCALLSTATE(ctrl, call, Q931_CALL_STATE_RESTART); call->peercallstate = Q931_CALL_STATE_RESTART_REQUEST; return send_message(ctrl, call, Q931_RESTART, restart_ies); } /*! * \internal * \brief T316 expired. * * \param vcall Q.931 call leg * * \return Nothing */ static void t316_expire(void *vcall) { struct q931_call *call = vcall; call->restart_tx.t316_timer = 0; if (call->restart_tx.remain) { /* Retransmit the RESTART */ q931_send_restart(call); } else { int channel = call->restart_tx.channel; pri_message(call->pri, "!! Peer failed to ack our RESTART request for ds1/channel:%d/%d.\n", (channel >> 8) & 0xFF, channel & 0xFF); } } /*! * \internal * \brief Stop timer T316. * * \param call Q.931 call leg * * \return Nothing */ static void stop_t316(struct q931_call *call) { pri_schedule_del(call->pri, call->restart_tx.t316_timer); call->restart_tx.t316_timer = 0; } /*! * \brief Send the RESTART message to the peer. * * \param ctrl D channel controller. * \param channel Encoded channel id to use. * * \note * Sending RESTART in NT PTMP mode is not supported at the * present time. * * \note * NT PTMP should broadcast the RESTART if there is a TEI * allocated. Otherwise it should immediately ACK the RESTART * itself to avoid the T316 timeout delay (2 minutes) since * there might not be anything connected. The broadcast could * be handled in a similar manner to the broadcast SETUP. * * \todo NT PTMP mode should implement some protection from * receiving a RESTART on channels in use by another TEI. * * \retval 0 on success. * \retval -1 on error. */ int q931_restart(struct pri *ctrl, int channel) { struct q931_call *call; if (!channel) { return -1; } call = q931_getcall(&ctrl->link, 0 | Q931_CALL_REFERENCE_FLAG); if (!call) { return -1; } stop_t316(call); call->restart_tx.remain = (0 < ctrl->timers[PRI_TIMER_N316]) ? ctrl->timers[PRI_TIMER_N316] : 1; call->restart_tx.channel = channel; return q931_send_restart(call); } static int disconnect_ies[] = { Q931_CAUSE, Q931_IE_FACILITY, Q931_IE_USER_USER, -1 }; int q931_disconnect(struct pri *ctrl, q931_call *c, int cause) { UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_DISCONNECT_REQUEST); c->peercallstate = Q931_CALL_STATE_DISCONNECT_INDICATION; if (c->alive) { c->alive = 0; c->cause = 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); } else return 0; } static int setup_ies[] = { Q931_BEARER_CAPABILITY, Q931_CHANNEL_IDENT, Q931_IE_FACILITY, Q931_PROGRESS_INDICATOR, Q931_NETWORK_SPEC_FAC, Q931_DISPLAY, Q931_IE_KEYPAD_FACILITY, Q931_REVERSE_CHARGE_INDIC, Q931_CALLING_PARTY_NUMBER, Q931_CALLING_PARTY_SUBADDR, Q931_CALLED_PARTY_NUMBER, Q931_CALLED_PARTY_SUBADDR, Q931_REDIRECTING_NUMBER, Q931_IE_USER_USER, Q931_SENDING_COMPLETE, Q931_IE_ORIGINATING_LINE_INFO, Q931_IE_GENERIC_DIGITS, -1 }; static int gr303_setup_ies[] = { Q931_BEARER_CAPABILITY, Q931_CHANNEL_IDENT, -1 }; /*! Call Independent Signalling SETUP ie's */ static int cis_setup_ies[] = { Q931_BEARER_CAPABILITY, Q931_CHANNEL_IDENT, Q931_IE_FACILITY, Q931_IE_KEYPAD_FACILITY, Q931_CALLING_PARTY_NUMBER, Q931_CALLING_PARTY_SUBADDR, Q931_CALLED_PARTY_NUMBER, Q931_CALLED_PARTY_SUBADDR, Q931_SENDING_COMPLETE, -1 }; static void stop_t303(struct q931_call *call) { /* T303 should only be running on the master call */ pri_schedule_del(call->pri, call->t303_timer); call->t303_timer = 0; } static void t303_expiry(void *data); /*! \param call Call is not a subcall call record. */ static void start_t303(struct q931_call *call) { struct pri *ctrl; ctrl = call->pri; pri_schedule_del(ctrl, call->t303_timer); call->t303_timer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T303], t303_expiry, call); } static void t303_expiry(void *data) { struct q931_call *c = data;/* Call is not a subcall call record. */ struct pri *ctrl = c->pri; int res; if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, "T303 timed out. cref:%d\n", c->cr); } c->t303_expirycnt++; c->t303_timer = 0; if (c->cause != -1) { /* We got a DISCONNECT, RELEASE, or RELEASE_COMPLETE and no other responses. */ if (c->outboundbroadcast) { UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_CALL_ABORT); } else { /* This should never happen. T303 should not be running in this case. */ UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL); c->peercallstate = Q931_CALL_STATE_NULL; } pri_fake_clearing(c); } else if (c->t303_expirycnt < 2) { /*! * \todo XXX Resending the SETUP message loses any facility ies * that the original may have had that were not added by * pri_call_add_standard_apdus(). Actually any message Q.931 * retransmits will lose the facility ies. */ pri_call_add_standard_apdus(ctrl, c); if (ctrl->display_flags.send & PRI_DISPLAY_OPTION_NAME_INITIAL) { q931_display_name_send(c, &c->local_id.name); } else { q931_display_clear(c); } c->cc.saved_ie_contents.length = 0; c->cc.saved_ie_flags = 0; if (ctrl->link.next && !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(ctrl, "Error resending setup message!\n"); } start_t303(c); if (c->outboundbroadcast) { start_t312(c); } } else { /* * We never got any response for a normal call or an * establishment response from any TEI for a master/subcall * call. */ c->cause = PRI_CAUSE_NO_USER_RESPONSE; if (c->outboundbroadcast) { UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_CALL_ABORT); } else { UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL); c->peercallstate = Q931_CALL_STATE_NULL; } pri_fake_clearing(c); } } int q931_setup(struct pri *ctrl, q931_call *c, struct pri_sr *req) { int res; if (!req->called.number.valid && (!req->keypad_digits || !req->keypad_digits[0])) { /* No called number or keypad digits to send. */ return -1; } c->called = req->called; libpri_copy_string(c->overlap_digits, req->called.number.str, sizeof(c->overlap_digits)); if (req->keypad_digits) { libpri_copy_string(c->keypad_digits, req->keypad_digits, sizeof(c->keypad_digits)); } else { c->keypad_digits[0] = '\0'; } c->bc.transcapability = req->transmode; c->bc.transmoderate = TRANS_MODE_64_CIRCUIT; if (!req->userl1) req->userl1 = PRI_LAYER_1_ULAW; 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->link.next && !ctrl->bri) { c->channelno = 0; c->chanflags = 0; } else { c->channelno = req->channel & 0xff; if (req->exclusive) { c->chanflags = FLAG_EXCLUSIVE; } else { c->chanflags = FLAG_PREFERRED; } } c->slotmap = -1; 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; if (req->caller.number.valid) { c->local_id = req->caller; q931_party_id_fixup(ctrl, &c->local_id); } if (req->redirecting.from.number.valid) { c->redirecting = req->redirecting; q931_party_id_fixup(ctrl, &c->redirecting.from); q931_party_id_fixup(ctrl, &c->redirecting.to); q931_party_id_fixup(ctrl, &c->redirecting.orig_called); } if (req->useruserinfo) libpri_copy_string(c->useruserinfo, req->useruserinfo, sizeof(c->useruserinfo)); else c->useruserinfo[0] = '\0'; if (req->nonisdn && (ctrl->switchtype == PRI_SWITCH_NI2)) c->progressmask = PRI_PROG_CALLER_NOT_ISDN; else c->progressmask = 0; c->reversecharge = req->reversecharge; c->aoc_charging_request = req->aoc_charging_request; pri_call_add_standard_apdus(ctrl, c); if (ctrl->display_flags.send & PRI_DISPLAY_OPTION_NAME_INITIAL) { q931_display_name_send(c, &c->local_id.name); } else { q931_display_clear(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 (BRI_NT_PTMP(ctrl)) { c->outboundbroadcast = 1; } if (ctrl->link.next && !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) { c->alive = 1; /* make sure we call PRI_EVENT_HANGUP_ACK once we send/receive RELEASE_COMPLETE */ c->sendhangupack = 1; UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_CALL_INITIATED); c->peercallstate = Q931_CALL_STATE_CALL_PRESENT; c->t303_expirycnt = 0; start_t303(c); if (c->outboundbroadcast) { start_t312(c); } } return res; } 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; c->causeloc = LOC_PRIV_NET_LOCAL_USER; /* release_ies has CAUSE in it */ res = send_message(ctrl, c, Q931_RELEASE_COMPLETE, release_ies); } else res = send_message(ctrl, c, Q931_RELEASE_COMPLETE, release_complete_ies); c->alive = 0; /* release the structure */ res += pri_hangup(ctrl, c, cause); return res; } static int connect_ack_ies[] = { -1 }; static int connect_ack_w_chan_id_ies[] = { Q931_CHANNEL_IDENT, -1 }; static int gr303_connect_ack_ies[] = { Q931_CHANNEL_IDENT, -1 }; int q931_connect_acknowledge(struct pri *ctrl, q931_call *call, int channel) { int *use_ies; struct q931_call *winner; winner = q931_find_winning_call(call); if (!winner) { return -1; } if (winner != call) { UPDATE_OURCALLSTATE(ctrl, call, Q931_CALL_STATE_ACTIVE); call->peercallstate = Q931_CALL_STATE_ACTIVE; } UPDATE_OURCALLSTATE(ctrl, winner, Q931_CALL_STATE_ACTIVE); winner->peercallstate = Q931_CALL_STATE_ACTIVE; if (channel) { winner->ds1no = (channel & 0xff00) >> 8; winner->ds1explicit = (channel & 0x10000) >> 16; winner->channelno = channel & 0xff; winner->chanflags &= ~FLAG_PREFERRED; winner->chanflags |= FLAG_EXCLUSIVE; } use_ies = NULL; if (ctrl->link.next && !ctrl->bri) { if (ctrl->localtype == PRI_CPE) { use_ies = gr303_connect_ack_ies; } } else if (channel) { use_ies = connect_ack_w_chan_id_ies; } else { use_ies = connect_ack_ies; } if (use_ies) { return send_message(ctrl, winner, Q931_CONNECT_ACKNOWLEDGE, use_ies); } return 0; } /*! * \internal * \brief Send HOLD message response wait timeout. * * \param data Q.931 call leg. (Master Q.931 subcall structure) * * \return Nothing */ static void q931_hold_timeout(void *data) { struct q931_call *call = data; struct q931_call *master = call->master_call; struct pri *ctrl = call->pri; if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, "Time-out waiting for HOLD response\n"); } /* Ensure that the timer is deleted. */ pri_schedule_del(ctrl, master->hold_timer); master->hold_timer = 0; /* Don't change the hold state if there was HOLD a collision. */ switch (master->hold_state) { case Q931_HOLD_STATE_HOLD_REQ: UPDATE_HOLD_STATE(ctrl, master, Q931_HOLD_STATE_IDLE); break; default: break; } q931_clr_subcommands(ctrl); ctrl->schedev = 1; ctrl->ev.e = PRI_EVENT_HOLD_REJ; ctrl->ev.hold_rej.channel = q931_encode_channel(call); ctrl->ev.hold_rej.call = master; ctrl->ev.hold_rej.cause = PRI_CAUSE_MESSAGE_TYPE_NONEXIST; ctrl->ev.hold_rej.subcmds = &ctrl->subcmds; } /*! * \internal * \brief Determine if a hold request is allowed now. * * \param ctrl D channel controller. * \param call Q.931 call leg. (Master Q.931 subcall structure) * * \retval TRUE if we can send a HOLD request. * \retval FALSE if not allowed. */ static int q931_is_hold_allowed(const struct pri *ctrl, const struct q931_call *call) { int allowed; allowed = 0; switch (call->ourcallstate) { case Q931_CALL_STATE_CALL_RECEIVED: case Q931_CALL_STATE_CONNECT_REQUEST: case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: if (PTMP_MODE(ctrl)) { /* HOLD request only allowed in these states if point-to-point mode. */ break; } /* Fall through */ case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: case Q931_CALL_STATE_CALL_DELIVERED: case Q931_CALL_STATE_ACTIVE: switch (call->hold_state) { case Q931_HOLD_STATE_IDLE: allowed = 1; break; default: break; } break; case Q931_CALL_STATE_DISCONNECT_INDICATION: case Q931_CALL_STATE_RELEASE_REQUEST: /* Ignore HOLD request in these states. */ break; default: break; } return allowed; } static int hold_ies[] = { -1 }; /*! * \brief Send the HOLD message. * * \param ctrl D channel controller. * \param call Q.931 call leg. (Master Q.931 subcall structure) * * \retval 0 on success. * \retval -1 on error. */ int q931_send_hold(struct pri *ctrl, struct q931_call *call) { struct q931_call *winner; winner = q931_find_winning_call(call); if (!winner || !q931_is_hold_allowed(ctrl, call)) { return -1; } pri_schedule_del(ctrl, call->hold_timer); call->hold_timer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T_HOLD], q931_hold_timeout, winner); if (!call->hold_timer || send_message(ctrl, winner, Q931_HOLD, hold_ies)) { pri_schedule_del(ctrl, call->hold_timer); call->hold_timer = 0; return -1; } UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_HOLD_REQ); return 0; } static int hold_ack_ies[] = { -1 }; /*! * \brief Send the HOLD ACKNOWLEDGE message. * * \param ctrl D channel controller. * \param call Q.931 call leg. (Master Q.931 subcall structure) * * \retval 0 on success. * \retval -1 on error. */ int q931_send_hold_ack(struct pri *ctrl, struct q931_call *call) { struct q931_call *winner; UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_CALL_HELD); winner = q931_find_winning_call(call); if (!winner) { return -1; } /* Call is now on hold so forget the channel. */ winner->channelno = 0;/* No channel */ winner->ds1no = 0; winner->ds1explicit = 0; winner->chanflags = 0; return send_message(ctrl, winner, Q931_HOLD_ACKNOWLEDGE, hold_ack_ies); } static int hold_reject_ies[] = { Q931_CAUSE, -1 }; /*! * \internal * \brief Send the HOLD REJECT message only. * * \param ctrl D channel controller. * \param call Q.931 call leg. (subcall) * \param cause Q.931 cause code for rejecting the hold request. * * \retval 0 on success. * \retval -1 on error. */ static int q931_send_hold_rej_msg(struct pri *ctrl, struct q931_call *call, int cause) { call->cause = cause; call->causecode = CODE_CCITT; call->causeloc = LOC_PRIV_NET_LOCAL_USER; return send_message(ctrl, call, Q931_HOLD_REJECT, hold_reject_ies); } /*! * \brief Send the HOLD REJECT message. * * \param ctrl D channel controller. * \param call Q.931 call leg. (Master Q.931 subcall structure) * \param cause Q.931 cause code for rejecting the hold request. * * \retval 0 on success. * \retval -1 on error. */ int q931_send_hold_rej(struct pri *ctrl, struct q931_call *call, int cause) { struct q931_call *winner; UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_IDLE); winner = q931_find_winning_call(call); if (!winner) { return -1; } return q931_send_hold_rej_msg(ctrl, winner, cause); } /*! * \internal * \brief Send RETRIEVE message response wait timeout. * * \param data Q.931 call leg. (Master Q.931 subcall structure) * * \return Nothing */ static void q931_retrieve_timeout(void *data) { struct q931_call *call = data; struct q931_call *master = call->master_call; struct pri *ctrl = call->pri; if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, "Time-out waiting for RETRIEVE response\n"); } /* Ensure that the timer is deleted. */ pri_schedule_del(ctrl, master->hold_timer); master->hold_timer = 0; /* Don't change the hold state if there was RETRIEVE a collision. */ switch (master->hold_state) { case Q931_HOLD_STATE_CALL_HELD: case Q931_HOLD_STATE_RETRIEVE_REQ: UPDATE_HOLD_STATE(ctrl, master, Q931_HOLD_STATE_CALL_HELD); /* Call is still on hold so forget the channel. */ call->channelno = 0;/* No channel */ call->ds1no = 0; call->ds1explicit = 0; call->chanflags = 0; break; default: break; } q931_clr_subcommands(ctrl); ctrl->schedev = 1; ctrl->ev.e = PRI_EVENT_RETRIEVE_REJ; ctrl->ev.retrieve_rej.channel = q931_encode_channel(call); ctrl->ev.retrieve_rej.call = master; ctrl->ev.retrieve_rej.cause = PRI_CAUSE_MESSAGE_TYPE_NONEXIST; ctrl->ev.retrieve_rej.subcmds = &ctrl->subcmds; } /*! * \internal * \brief Determine if a retrieve request is allowed now. * * \param ctrl D channel controller. * \param call Q.931 call leg. (Master Q.931 subcall structure) * * \retval TRUE if we can send a RETRIEVE request. * \retval FALSE if not allowed. */ static int q931_is_retrieve_allowed(const struct pri *ctrl, const struct q931_call *call) { int allowed; allowed = 0; switch (call->ourcallstate) { case Q931_CALL_STATE_CALL_RECEIVED: case Q931_CALL_STATE_CONNECT_REQUEST: case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: if (PTMP_MODE(ctrl)) { /* RETRIEVE request only allowed in these states if point-to-point mode. */ break; } /* Fall through */ case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: case Q931_CALL_STATE_CALL_DELIVERED: case Q931_CALL_STATE_ACTIVE: switch (call->hold_state) { case Q931_HOLD_STATE_CALL_HELD: allowed = 1; break; default: break; } break; case Q931_CALL_STATE_DISCONNECT_INDICATION: case Q931_CALL_STATE_RELEASE_REQUEST: /* Ignore RETRIEVE request in these states. */ break; default: break; } return allowed; } static int retrieve_ies[] = { Q931_CHANNEL_IDENT, -1 }; /*! * \brief Send the RETRIEVE message. * * \param ctrl D channel controller. * \param call Q.931 call leg. (Master Q.931 subcall structure) * \param channel Encoded channel id to use. If zero do not send channel id. * * \retval 0 on success. * \retval -1 on error. */ int q931_send_retrieve(struct pri *ctrl, struct q931_call *call, int channel) { struct q931_call *winner; winner = q931_find_winning_call(call); if (!winner || !q931_is_retrieve_allowed(ctrl, call)) { return -1; } if (channel) { winner->ds1no = (channel & 0xff00) >> 8; winner->ds1explicit = (channel & 0x10000) >> 16; winner->channelno = channel & 0xff; if (ctrl->localtype == PRI_NETWORK && winner->channelno != 0xFF) { winner->chanflags = FLAG_EXCLUSIVE; } else { winner->chanflags = FLAG_PREFERRED; } } else { /* Do not send Q931_CHANNEL_IDENT */ winner->chanflags = 0; } pri_schedule_del(ctrl, call->hold_timer); call->hold_timer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T_RETRIEVE], q931_retrieve_timeout, winner); if (!call->hold_timer || send_message(ctrl, winner, Q931_RETRIEVE, retrieve_ies)) { pri_schedule_del(ctrl, call->hold_timer); call->hold_timer = 0; /* Call is still on hold so forget the channel. */ winner->channelno = 0;/* No channel */ winner->ds1no = 0; winner->ds1explicit = 0; winner->chanflags = 0; return -1; } UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_RETRIEVE_REQ); return 0; } static int retrieve_ack_ies[] = { Q931_CHANNEL_IDENT, -1 }; /*! * \brief Send the RETRIEVE ACKNOWLEDGE message. * * \param ctrl D channel controller. * \param call Q.931 call leg. (Master Q.931 subcall structure) * \param channel Encoded channel id to use. * * \retval 0 on success. * \retval -1 on error. */ int q931_send_retrieve_ack(struct pri *ctrl, struct q931_call *call, int channel) { struct q931_call *winner; winner = q931_find_winning_call(call); if (!winner) { return -1; } winner->ds1no = (channel & 0xff00) >> 8; winner->ds1explicit = (channel & 0x10000) >> 16; winner->channelno = channel & 0xff; winner->chanflags = FLAG_EXCLUSIVE; UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_IDLE); return send_message(ctrl, winner, Q931_RETRIEVE_ACKNOWLEDGE, retrieve_ack_ies); } static int retrieve_reject_ies[] = { Q931_CAUSE, -1 }; /*! * \internal * \brief Send the RETRIEVE REJECT message only. * * \param ctrl D channel controller. * \param call Q.931 call leg. (subcall) * \param cause Q.931 cause code for rejecting the retrieve request. * * \retval 0 on success. * \retval -1 on error. */ static int q931_send_retrieve_rej_msg(struct pri *ctrl, struct q931_call *call, int cause) { call->cause = cause; call->causecode = CODE_CCITT; call->causeloc = LOC_PRIV_NET_LOCAL_USER; return send_message(ctrl, call, Q931_RETRIEVE_REJECT, retrieve_reject_ies); } /*! * \brief Send the RETRIEVE REJECT message. * * \param ctrl D channel controller. * \param call Q.931 call leg. (Master Q.931 subcall structure) * \param cause Q.931 cause code for rejecting the retrieve request. * * \retval 0 on success. * \retval -1 on error. */ int q931_send_retrieve_rej(struct pri *ctrl, struct q931_call *call, int cause) { struct q931_call *winner; UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_CALL_HELD); winner = q931_find_winning_call(call); if (!winner) { return -1; } /* Call is still on hold so forget the channel. */ winner->channelno = 0;/* No channel */ winner->ds1no = 0; winner->ds1explicit = 0; winner->chanflags = 0; return q931_send_retrieve_rej_msg(ctrl, winner, cause); } static int __q931_hangup(struct pri *ctrl, q931_call *c, int cause) { int disconnect = 1; int release_compl = 0; if (!ctrl || !c) { return -1; } if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, DBGHEAD "ourstate %s, peerstate %s, hold-state %s\n", DBGINFO, q931_call_state_str(c->ourcallstate), q931_call_state_str(c->peercallstate), q931_hold_state_str(c->master_call->hold_state)); } /* If mandatory IE was missing, insist upon that cause code */ if (c->cause == PRI_CAUSE_MANDATORY_IE_MISSING) cause = c->cause; switch (cause) { case PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION: case PRI_CAUSE_REQUESTED_CHAN_UNAVAIL: case PRI_CAUSE_IDENTIFIED_CHANNEL_NOTEXIST: case PRI_CAUSE_UNALLOCATED: if (!ctrl->hangup_fix_enabled) { /* Legacy: We'll send RELEASE_COMPLETE with these causes */ disconnect = 0; release_compl = 1; break; } /* Fall through */ case PRI_CAUSE_INCOMPATIBLE_DESTINATION: /* See Q.931 Section 5.3.2 a) */ switch (c->ourcallstate) { case Q931_CALL_STATE_NULL: case Q931_CALL_STATE_CALL_INITIATED: case Q931_CALL_STATE_CALL_PRESENT: /* * Send RELEASE_COMPLETE because some other message * has not been sent previously. */ disconnect = 0; release_compl = 1; break; case Q931_CALL_STATE_CONNECT_REQUEST: /* * Send RELEASE because the B channel negotiation failed * for call waiting. */ disconnect = 0; break; default: /* * Send DISCONNECT because some other message * has been sent previously. */ break; } break; case PRI_CAUSE_INVALID_CALL_REFERENCE: /* We'll send RELEASE_COMPLETE with these causes */ disconnect = 0; release_compl = 1; break; case PRI_CAUSE_CHANNEL_UNACCEPTABLE: case PRI_CAUSE_CALL_AWARDED_DELIVERED: case PRI_CAUSE_NONSELECTED_USER_CLEARING: /* We'll send RELEASE with these causes */ disconnect = 0; break; default: break; } if (c->cis_call) { disconnect = 0; } c->hangupinitiated = 1; stop_t303(c); /* All other causes we send with DISCONNECT */ switch(c->ourcallstate) { case Q931_CALL_STATE_NULL: if (c->peercallstate == Q931_CALL_STATE_NULL) /* free the resources if we receive or send REL_COMPL */ pri_destroycall(ctrl, c); else if (c->peercallstate == Q931_CALL_STATE_RELEASE_REQUEST) q931_release_complete(ctrl,c,cause); break; case Q931_CALL_STATE_CALL_INITIATED: /* we sent SETUP */ case Q931_CALL_STATE_OVERLAP_SENDING: /* received SETUP_ACKNOWLEDGE */ case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: /* received CALL_PROCEEDING */ case Q931_CALL_STATE_CALL_DELIVERED: /* received ALERTING */ case Q931_CALL_STATE_CALL_PRESENT: /* received SETUP */ case Q931_CALL_STATE_CALL_RECEIVED: /* sent ALERTING */ case Q931_CALL_STATE_CONNECT_REQUEST: /* sent CONNECT */ case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: /* we sent CALL_PROCEEDING */ case Q931_CALL_STATE_OVERLAP_RECEIVING: /* received SETUP_ACKNOWLEDGE */ /* send DISCONNECT in general */ switch (c->peercallstate) { default: if (disconnect) q931_disconnect(ctrl,c,cause); else if (release_compl) q931_release_complete(ctrl,c,cause); else q931_release(ctrl,c,cause); break; case Q931_CALL_STATE_NULL: case Q931_CALL_STATE_DISCONNECT_REQUEST: case Q931_CALL_STATE_DISCONNECT_INDICATION: case Q931_CALL_STATE_RELEASE_REQUEST: case Q931_CALL_STATE_RESTART_REQUEST: case Q931_CALL_STATE_RESTART: pri_error(ctrl, "Weird, doing nothing but this shouldn't happen, ourstate %s, peerstate %s\n", q931_call_state_str(c->ourcallstate), q931_call_state_str(c->peercallstate)); break; } break; case Q931_CALL_STATE_ACTIVE: /* received CONNECT */ if (c->cis_call) { q931_release(ctrl, c, cause); break; } q931_disconnect(ctrl,c,cause); break; case Q931_CALL_STATE_DISCONNECT_REQUEST: /* sent DISCONNECT */ q931_release(ctrl,c,cause); break; case Q931_CALL_STATE_CALL_ABORT: /* Don't do anything, waiting for T312 to expire. */ break; case Q931_CALL_STATE_DISCONNECT_INDICATION: /* received DISCONNECT */ if (c->peercallstate == Q931_CALL_STATE_DISCONNECT_REQUEST) { c->alive = 1; q931_release(ctrl,c,cause); } break; case Q931_CALL_STATE_RELEASE_REQUEST: /* 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 */ pri_error(ctrl, "q931_hangup shouldn't be called in this state, ourstate %s, peerstate %s\n", q931_call_state_str(c->ourcallstate), q931_call_state_str(c->peercallstate)); break; default: pri_error(ctrl, "We're not yet handling hanging up when our state is %d, contact support@digium.com, ourstate %s, peerstate %s\n", c->ourcallstate, q931_call_state_str(c->ourcallstate), q931_call_state_str(c->peercallstate)); return -1; } /* we did handle hangup properly at this point */ return 0; } static void initiate_hangup_if_needed(struct q931_call *master, int idx, int cause); int q931_hangup(struct pri *ctrl, q931_call *call, int cause) { int i; if (call->master_call->outboundbroadcast) { if (call->master_call == call) { int slaves; if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, DBGHEAD "Hangup master cref:%d\n", DBGINFO, call->cr); } UPDATE_OURCALLSTATE(ctrl, call, Q931_CALL_STATE_CALL_ABORT); if (call->pri_winner < 0 && call->alive) { /* * Fake clearing if we have no winner to get rid of the upper * layer. */ pri_create_fake_clearing(ctrl, call); } else if (call->fake_clearing_timer) { /* * No need for fake clearing to be running anymore. * Will this actually happen? */ if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, "Fake clearing request cancelled. cref:%d\n", call->cr); } pri_schedule_del(ctrl, call->fake_clearing_timer); call->fake_clearing_timer = 0; } /* Initiate hangup of slaves */ call->master_hanging_up = 1; for (i = 0; i < ARRAY_LEN(call->subcalls); ++i) { if (call->subcalls[i]) { if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, DBGHEAD "Hanging up %d, winner:%d subcall:%p\n", DBGINFO, i, call->pri_winner, call->subcalls[i]); } if (i == call->pri_winner) { q931_hangup(ctrl, call->subcalls[i], cause); } else { initiate_hangup_if_needed(call, i, cause); } } } call->master_hanging_up = 0; slaves = q931_get_subcall_count(call); if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, DBGHEAD "Remaining slaves %d\n", DBGINFO, slaves); } stop_t303(call); if (!call->t312_timer && !slaves) { /* * T312 has expired and no slaves are left so we can safely * destroy the master. */ q931_destroycall(ctrl, call); } return 0; } else { if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, DBGHEAD "Hangup slave cref:%d\n", DBGINFO, call->cr); } return __q931_hangup(ctrl, call, cause); } } else { if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, DBGHEAD "Hangup other cref:%d\n", DBGINFO, call->cr); } return __q931_hangup(ctrl, call, cause); } return 0; } static int prepare_to_handle_maintenance_message(struct pri *ctrl, q931_mh *mh, q931_call *c) { if ((!ctrl) || (!mh) || (!c)) { return -1; } /* SERVICE messages are a superset of messages that can take b-channels * or entire d-channels in and out of service */ switch(mh->msg) { /* the ATT_SERVICE/ATT_SERVICE_ACKNOWLEDGE and NATIONAL_SERVICE/NATIONAL_SERVICE_ACKNOWLEDGE * are mirrors of each other. We only have to check for one type because they are pre-handled * the same way as each other */ case ATT_SERVICE: case ATT_SERVICE_ACKNOWLEDGE: c->channelno = -1; c->slotmap = -1; c->chanflags = 0; c->ds1explicit = 0; c->ds1no = 0; c->cis_call = 0; c->ri = -1; c->changestatus = -1; break; default: pri_error(ctrl, "!! Don't know how to pre-handle maintenance message type '0x%X'\n", mh->msg); return -1; } return 0; } static int prepare_to_handle_q931_message(struct pri *ctrl, q931_mh *mh, q931_call *c) { if ((!ctrl) || (!mh) || (!c)) { return -1; } switch(mh->msg) { case Q931_RESTART: if (ctrl->debug & PRI_DEBUG_Q931_STATE) pri_message(ctrl, "-- Processing Q.931 Restart\n"); /* Reset information */ c->channelno = -1; c->slotmap = -1; c->chanflags = 0; c->ds1no = 0; c->ds1explicit = 0; c->cis_call = 0; c->ri = -1; break; case Q931_FACILITY: c->notify = -1; q931_party_number_init(&c->redirection_number); if (q931_is_dummy_call(c)) { q931_party_address_init(&c->called); } break; 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->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); q931_party_id_init(&c->remote_id); q931_party_number_init(&c->ani); q931_party_redirecting_init(&c->redirecting); /* * Make sure that keypad and overlap digit buffers are empty in * case they are not in the message. */ c->keypad_digits[0] = '\0'; c->overlap_digits[0] = '\0'; c->useruserprotocoldisc = -1; c->useruserinfo[0] = '\0'; c->complete = 0; c->nonisdn = 0; c->aoc_units = -1; c->reversecharge = -1; /* Fall through */ case Q931_CONNECT: case Q931_ALERTING: case Q931_PROGRESS: c->useruserinfo[0] = '\0'; c->cause = -1; /* Fall through */ case Q931_CALL_PROCEEDING: c->progress = -1; c->progressmask = 0; break; case Q931_CONNECT_ACKNOWLEDGE: pri_schedule_del(ctrl, c->retranstimer); c->retranstimer = 0; break; case Q931_RELEASE: case Q931_DISCONNECT: c->cause = -1; c->causecode = -1; c->causeloc = -1; c->aoc_units = -1; pri_schedule_del(ctrl, c->retranstimer); c->retranstimer = 0; c->useruserinfo[0] = '\0'; break; case Q931_RELEASE_COMPLETE: pri_schedule_del(ctrl, c->retranstimer); c->retranstimer = 0; c->useruserinfo[0] = '\0'; /* Fall through */ case Q931_STATUS: c->cause = -1; c->causecode = -1; c->causeloc = -1; c->sugcallstate = Q931_CALL_STATE_NOT_SET; c->aoc_units = -1; break; case Q931_RESTART_ACKNOWLEDGE: c->channelno = -1; c->ds1no = 0; c->ds1explicit = 0; c->cis_call = 0; break; case Q931_INFORMATION: /* * Make sure that keypad and overlap digit buffers are empty in * case they are not in the message. */ c->keypad_digits[0] = '\0'; c->overlap_digits[0] = '\0'; break; case Q931_STATUS_ENQUIRY: break; case Q931_SETUP_ACKNOWLEDGE: break; case Q931_NOTIFY: c->notify = -1; q931_party_number_init(&c->redirection_number); break; case Q931_HOLD: break; case Q931_HOLD_ACKNOWLEDGE: break; case Q931_HOLD_REJECT: c->cause = -1; break; case Q931_RETRIEVE: c->channelno = 0xFF; c->ds1no = 0; c->ds1explicit = 0; break; case Q931_RETRIEVE_ACKNOWLEDGE: break; case Q931_RETRIEVE_REJECT: c->cause = -1; break; case Q931_USER_INFORMATION: case Q931_SEGMENT: case Q931_CONGESTION_CONTROL: case Q931_RESUME: case Q931_RESUME_ACKNOWLEDGE: case Q931_RESUME_REJECT: case Q931_SUSPEND: case Q931_SUSPEND_ACKNOWLEDGE: case Q931_SUSPEND_REJECT: pri_error(ctrl, "!! Not yet handling pre-handle message type %s (0x%X)\n", msg2str(mh->msg), mh->msg); /* Fall through */ default: pri_error(ctrl, "!! Don't know how to pre-handle message type %s (0x%X)\n", msg2str(mh->msg), mh->msg); q931_status(ctrl,c, PRI_CAUSE_MESSAGE_TYPE_NONEXIST); return -1; } return 0; } static struct q931_call *q931_get_subcall_winner(struct q931_call *master) { if (master->pri_winner < 0) { return NULL; } else { return master->subcalls[master->pri_winner]; } } static void initiate_hangup_if_needed(struct q931_call *master, int idx, int cause) { struct pri *ctrl; struct q931_call *subcall; ctrl = master->pri; subcall = master->subcalls[idx]; if (!subcall->hangupinitiated) { q931_hangup(ctrl, subcall, cause); if (master->subcalls[idx] == subcall) { /* The subcall was not destroyed. */ subcall->alive = 0; } } } static void q931_set_subcall_winner(struct q931_call *subcall) { struct q931_call *master = subcall->master_call; int i; /* Set the winner first */ for (i = 0; ; ++i) { if (ARRAY_LEN(master->subcalls) <= i) { pri_error(subcall->pri, "We should always find the winner in the list!\n"); return; } if (master->subcalls[i] == subcall) { master->pri_winner = i; break; } } /* Start tear down of calls that were not chosen */ for (i = 0; i < ARRAY_LEN(master->subcalls); ++i) { if (master->subcalls[i] && master->subcalls[i] != subcall) { initiate_hangup_if_needed(master, i, PRI_CAUSE_NONSELECTED_USER_CLEARING); } } } static struct q931_call *q931_get_subcall(struct q921_link *link, struct q931_call *master_call) { int i; struct q931_call *cur; struct pri *ctrl; int firstfree = -1; ctrl = link->ctrl; /* First try to locate our subcall */ for (i = 0; i < ARRAY_LEN(master_call->subcalls); ++i) { if (master_call->subcalls[i]) { if (master_call->subcalls[i]->link == link) { return master_call->subcalls[i]; } } else if (firstfree == -1) { firstfree = i; } } if (firstfree < 0) { pri_error(ctrl, "Tried to add more than %d TEIs to call and failed\n", (int) ARRAY_LEN(master_call->subcalls)); return NULL; } /* Create new subcall. */ cur = malloc(sizeof(*cur)); if (!cur) { pri_error(ctrl, "Unable to allocate call\n"); return NULL; } *cur = *master_call; //cur->pri = ctrl;/* We get this assignment for free. */ cur->link = link; cur->next = NULL; cur->apdus = NULL; cur->bridged_call = NULL; //cur->master_call = master_call; /* We get this assignment for free. */ for (i = 0; i < ARRAY_LEN(cur->subcalls); ++i) { cur->subcalls[i] = NULL; } cur->t303_timer = 0;/* T303 should only be on on the master call */ cur->t312_timer = 0;/* T312 should only be on on the master call */ cur->fake_clearing_timer = 0;/* Fake clearing should only be on on the master call */ cur->hold_timer = 0; cur->retranstimer = 0; /* * Mark this subcall as a newcall until it is determined if the * subcall can compete for the call. */ cur->newcall = 1; /* Assume we sent a SETUP and this is the first response to it from this peer. */ cur->ourcallstate = Q931_CALL_STATE_CALL_INITIATED; cur->peercallstate = Q931_CALL_STATE_CALL_PRESENT; master_call->subcalls[firstfree] = cur; if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, "Adding subcall %p for TEI %d to call %p at position %d\n", cur, link->tei, master_call, firstfree); } /* Should only get here if the TEI is not found */ return cur; } int q931_receive(struct q921_link *link, q931_h *h, int len) { q931_mh *mh; struct q931_call *c; struct pri *ctrl; q931_ie *ie; unsigned int x; int y; int res; int r; int mandies[MAX_MAND_IES]; int missingmand; int codeset, cur_codeset; int last_ie[8]; int cref; int allow_event; int allow_posthandle; ctrl = link->ctrl; memset(last_ie, 0, sizeof(last_ie)); ctrl->q931_rxcount++; if (len < 3 || len < 3 + h->crlen) { /* Message too short for supported protocols. */ return -1; } switch (h->pd) { case MAINTENANCE_PROTOCOL_DISCRIMINATOR_1: case MAINTENANCE_PROTOCOL_DISCRIMINATOR_2: if (!ctrl->service_message_support) { /* Real service message support has not been enabled (and is OFF in libpri by default), * so we have to revert to the 'traditional' KLUDGE of changing byte 4 from a 0xf (SERVICE) * to a 0x7 (SERVICE ACKNOWLEDGE) */ /* This is the weird maintenance stuff. We majorly KLUDGE this by changing byte 4 from a 0xf (SERVICE) to a 0x7 (SERVICE ACKNOWLEDGE) */ h->raw[h->crlen + 2] -= 0x8; q931_xmit(link, h, len, 1, 0); return 0; } break; default: if (h->pd != ctrl->protodisc) { pri_error(ctrl, "Warning: unknown/inappropriate protocol discriminator received (%02x/%d)\n", h->pd, h->pd); return 0; } break; } cref = q931_cr(h); c = q931_getcall(link, cref); if (!c) { pri_error(ctrl, "Unable to locate call %d\n", cref); return -1; } if (c->master_call->outboundbroadcast && link != &ctrl->link) { c = q931_get_subcall(link, c->master_call); if (!c) { pri_error(ctrl, "Unable to locate subcall for %d\n", cref); return -1; } } if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, "Received message for call %p on link %p TEI/SAPI %d/%d\n", c, link, link->tei, link->sapi); } /* Preliminary handling */ ctrl->facility.count = 0; c->connected_number_in_message = 0; c->redirecting_number_in_message = 0; mh = (q931_mh *)(h->contents + h->crlen); switch (h->pd) { case MAINTENANCE_PROTOCOL_DISCRIMINATOR_1: case MAINTENANCE_PROTOCOL_DISCRIMINATOR_2: prepare_to_handle_maintenance_message(ctrl, mh, c); break; default: /* Unknown protocol discriminator but we will treat it as Q.931 anyway. */ case GR303_PROTOCOL_DISCRIMINATOR: case Q931_PROTOCOL_DISCRIMINATOR: if (prepare_to_handle_q931_message(ctrl, mh, c)) { /* Discard message. We don't know how to handle it. */ if (c->newcall) { pri_destroycall(ctrl, c); } return 0; } break; } q931_clr_subcommands(ctrl); q931_display_clear(c); /* Handle IEs */ memset(mandies, 0, sizeof(mandies)); for (x = 0; x < ARRAY_LEN(msgs); ++x) { if (msgs[x].msgnum == mh->msg) { memcpy(mandies, msgs[x].mandies, sizeof(mandies)); break; } } /* Do real IE processing */ len -= (h->crlen + 3); codeset = cur_codeset = 0; for (x = 0; x < len; x += r) { ie = (q931_ie *)(mh->data + x); r = ielen_checked(ie, len - x); if (r < 0) { /* We have garbage on the end of the packet. */ pri_error(ctrl, "XXX Message longer than it should be?? XXX\n"); if (x) { /* Allow the message anyway. We have already processed an ie. */ break; } q931_display_clear(c); return -1; } for (y = 0; y < ARRAY_LEN(mandies); ++y) { if (mandies[y] == Q931_FULL_IE(cur_codeset, ie->ie)) mandies[y] = 0; } /* Special processing for codeset shifts */ switch (ie->ie & 0xf8) { case Q931_LOCKING_SHIFT: y = ie->ie & 7; /* Requested codeset */ /* Locking shifts couldn't go to lower codeset, and couldn't follows non-locking shifts - verify this */ if ((cur_codeset != codeset) && (ctrl->debug & PRI_DEBUG_Q931_ANOMALY)) pri_message(ctrl, "XXX Locking shift immediately follows non-locking shift (from %d through %d to %d) XXX\n", codeset, cur_codeset, y); if (y > 0) { if ((y < codeset) && (ctrl->debug & PRI_DEBUG_Q931_ANOMALY)) pri_error(ctrl, "!! Trying to locked downshift codeset from %d to %d !!\n", codeset, y); codeset = cur_codeset = y; } else { /* Locking shift to codeset 0 is forbidden by all specifications */ pri_error(ctrl, "!! Invalid locking shift to codeset 0 !!\n"); } break; case Q931_NON_LOCKING_SHIFT: cur_codeset = ie->ie & 7; break; default: /* Sanity check for IE code order */ if (!(ie->ie & 0x80)) { if (last_ie[cur_codeset] > ie->ie) { if ((ctrl->debug & PRI_DEBUG_Q931_ANOMALY)) pri_message(ctrl, "XXX Out-of-order IE %d at codeset %d (last was %d)\n", ie->ie, cur_codeset, last_ie[cur_codeset]); } else last_ie[cur_codeset] = ie->ie; } /* Ignore non-locking shifts for TR41459-based signalling */ switch (ctrl->switchtype) { case PRI_SWITCH_LUCENT5E: case PRI_SWITCH_ATT4ESS: if (cur_codeset != codeset) { if ((ctrl->debug & PRI_DEBUG_Q931_DUMP)) pri_message(ctrl, "XXX Ignoring IE %d for temporary codeset %d XXX\n", ie->ie, cur_codeset); break; } /* Fall through */ default: y = q931_handle_ie(cur_codeset, ctrl, c, mh->msg, ie); /* XXX Applicable to codeset 0 only? XXX */ if (!cur_codeset && !(ie->ie & 0xf0) && (y < 0)) { /* * Q.931 Section 5.8.7.1 * Unhandled ies in codeset 0 with the * upper nybble zero are mandatory. */ mandies[MAX_MAND_IES - 1] = Q931_FULL_IE(cur_codeset, ie->ie); } break; } /* Reset current codeset */ cur_codeset = codeset; break; } } missingmand = 0; for (x = 0; x < ARRAY_LEN(mandies); ++x) { if (mandies[x]) { /* check if there is no channel identification when we're configured as network -> that's not an error */ if (((ctrl->localtype != PRI_NETWORK) || (mh->msg != Q931_SETUP) || (mandies[x] != Q931_CHANNEL_IDENT)) && ((mh->msg != Q931_PROGRESS) || (mandies[x] != Q931_PROGRESS_INDICATOR))) { pri_error(ctrl, "XXX Missing handling for mandatory IE %d (cs%d, %s) XXX\n", Q931_IE_IE(mandies[x]), Q931_IE_CODESET(mandies[x]), ie2str(mandies[x])); missingmand++; } } } if (!missingmand) { switch (mh->msg) { case Q931_SETUP: case Q931_CONNECT: if (ctrl->display_flags.receive & PRI_DISPLAY_OPTION_NAME_INITIAL) { q931_display_name_get(c, &c->remote_id.name); } break; default: break; } /* Now handle the facility ie's after all the other ie's were processed. */ q931_handle_facilities(ctrl, c, mh->msg); } q931_apdu_msg_expire(ctrl, c, mh->msg); /* Post handling */ switch (h->pd) { case MAINTENANCE_PROTOCOL_DISCRIMINATOR_1: case MAINTENANCE_PROTOCOL_DISCRIMINATOR_2: res = post_handle_maintenance_message(ctrl, h->pd, mh, c); q931_display_clear(c); break; default: allow_event = 1; allow_posthandle = 1; if (c->master_call->outboundbroadcast) { nt_ptmp_handle_q931_message(ctrl, mh, c, &allow_event, &allow_posthandle); if (allow_event) { q931_apdu_msg_expire(ctrl, c->master_call, mh->msg); } } if (allow_posthandle) { res = post_handle_q931_message(ctrl, mh, c, missingmand); if (res == Q931_RES_HAVEEVENT && !allow_event) { res = 0; } } else { q931_display_clear(c); res = 0; } break; } return res; } static int post_handle_maintenance_message(struct pri *ctrl, int protodisc, struct q931_mh *mh, struct q931_call *c) { /* Do some maintenance stuff */ if (((protodisc == MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) && (mh->msg == ATT_SERVICE)) || ((protodisc == MAINTENANCE_PROTOCOL_DISCRIMINATOR_2) && (mh->msg == NATIONAL_SERVICE))) { if (c->channelno > 0) { ctrl->ev.e = PRI_EVENT_SERVICE; ctrl->ev.service.channel = q931_encode_channel(c); ctrl->ev.service.changestatus = 0x0f & c->changestatus; } else { switch (0x0f & c->changestatus) { case SERVICE_CHANGE_STATUS_INSERVICE: ctrl->ev.e = PRI_EVENT_DCHAN_UP; //q921_dchannel_up(ctrl); break; case SERVICE_CHANGE_STATUS_OUTOFSERVICE: ctrl->ev.e = PRI_EVENT_DCHAN_DOWN; //q921_dchannel_down(ctrl); break; default: pri_error(ctrl, "!! Don't know how to handle span service change status '%d'\n", (0x0f & c->changestatus)); return -1; } } maintenance_service_ack(ctrl, c); return Q931_RES_HAVEEVENT; } if (((protodisc == MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) && (mh->msg == ATT_SERVICE_ACKNOWLEDGE)) || ((protodisc == MAINTENANCE_PROTOCOL_DISCRIMINATOR_2) && (mh->msg == NATIONAL_SERVICE_ACKNOWLEDGE))) { if (c->channelno > 0) { ctrl->ev.e = PRI_EVENT_SERVICE_ACK; ctrl->ev.service_ack.channel = q931_encode_channel(c); ctrl->ev.service_ack.changestatus = 0x0f & c->changestatus; } else { switch (0x0f & c->changestatus) { case SERVICE_CHANGE_STATUS_INSERVICE: ctrl->ev.e = PRI_EVENT_DCHAN_UP; //q921_dchannel_up(ctrl); break; case SERVICE_CHANGE_STATUS_OUTOFSERVICE: ctrl->ev.e = PRI_EVENT_DCHAN_DOWN; //q921_dchannel_down(ctrl); break; default: pri_error(ctrl, "!! Don't know how to handle span service change status '%d'\n", (0x0f & c->changestatus)); return -1; } } return Q931_RES_HAVEEVENT; } pri_error(ctrl, "!! Don't know how to post-handle maintenance message type 0x%X\n", mh->msg); return -1; } /*! * \internal * \brief Rank the given Q.931 call state for call etablishment. * * \param state Q.931 call state to rank for competing PTMP NT calls. * * \return Call establishment state ranking. */ static enum Q931_RANKED_CALL_STATE q931_rank_state(enum Q931_CALL_STATE state) { enum Q931_RANKED_CALL_STATE rank; switch (state) { case Q931_CALL_STATE_CALL_INITIATED: case Q931_CALL_STATE_CALL_PRESENT: rank = Q931_RANKED_CALL_STATE_PRESENT; break; case Q931_CALL_STATE_OVERLAP_SENDING: case Q931_CALL_STATE_OVERLAP_RECEIVING: rank = Q931_RANKED_CALL_STATE_OVERLAP; break; case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: rank = Q931_RANKED_CALL_STATE_PROCEEDING; break; case Q931_CALL_STATE_CALL_DELIVERED: case Q931_CALL_STATE_CALL_RECEIVED: case Q931_CALL_STATE_CONNECT_REQUEST: rank = Q931_RANKED_CALL_STATE_ALERTING; break; case Q931_CALL_STATE_ACTIVE: case Q931_CALL_STATE_CALL_INDEPENDENT_SERVICE: rank = Q931_RANKED_CALL_STATE_CONNECT; break; case Q931_CALL_STATE_CALL_ABORT: rank = Q931_RANKED_CALL_STATE_ABORT; break; default: rank = Q931_RANKED_CALL_STATE_OTHER; break; } return rank; } /*! * \brief Determine if the master will pass an event to the upper layer. * * \param ctrl D channel controller. * \param subcall Q.931 call leg. * \param msg_type Current message type being processed. * * \note This function must parallel nt_ptmp_handle_q931_message(). * * \retval TRUE if the master will pass an event to the upper layer. * \retval FALSE if the event will be blocked. */ int q931_master_pass_event(struct pri *ctrl, struct q931_call *subcall, int msg_type) { struct q931_call *winner; struct q931_call *master; enum Q931_RANKED_CALL_STATE master_rank; enum Q931_RANKED_CALL_STATE subcall_rank; int will_pass; /*!< TRUE if the master will pass an event to the upper layer. */ master = subcall->master_call; if (subcall == master) { /* We are the master call so of course the master will pass an event. */ return 1; } winner = q931_get_subcall_winner(master); if (winner && subcall == winner) { /* We are the winner so of course the master will pass an event. */ return 1; } master_rank = q931_rank_state(master->ourcallstate); will_pass = 0; switch (msg_type) { case Q931_SETUP_ACKNOWLEDGE: #if 0 /* Overlap dialing in PTMP NT mode not supported at the present time. */ if (master_rank < Q931_RANKED_CALL_STATE_OVERLAP) { will_pass = 1; } #endif /* Overlap dialing in PTMP NT mode not supported at the present time. */ break; case Q931_CALL_PROCEEDING: if (master_rank < Q931_RANKED_CALL_STATE_PROCEEDING) { will_pass = 1; } break; case Q931_PROGRESS: /* * We will just ignore this message since there could be multiple devices * competing for this call. Who has access to the B channel at this time * to give in-band signals anyway? */ break; case Q931_ALERTING: if (master_rank < Q931_RANKED_CALL_STATE_ALERTING) { will_pass = 1; } break; case Q931_CONNECT: if (master_rank < Q931_RANKED_CALL_STATE_CONNECT) { /* We are expected to be the winner for the next message. */ will_pass = 1; } break; case Q931_DISCONNECT: case Q931_RELEASE: case Q931_RELEASE_COMPLETE: /* Only deal with the winner. */ break; case Q931_FACILITY: case Q931_NOTIFY: if (!winner) { /* The overlap rank does not count here. */ if (master_rank == Q931_RANKED_CALL_STATE_OVERLAP) { master_rank = Q931_RANKED_CALL_STATE_PRESENT; } subcall_rank = q931_rank_state(subcall->ourcallstate); if (subcall_rank == Q931_RANKED_CALL_STATE_OVERLAP) { subcall_rank = Q931_RANKED_CALL_STATE_PRESENT; } if (master_rank == subcall_rank) { /* * No winner yet but the subcall is as advanced as the master. * Allow the supplementary service event to pass. */ will_pass = 1; } } break; default: /* Only deal with the winner. */ break; } return will_pass; } /*! * \internal * \brief Handle outboundbroadcast incoming messages for the master_call's state. * * \param ctrl D channel controller. * \param mh Q.931 message type header. * \param subcall Q.931 call leg. * \param allow_event Where to set the allow event to upper layer flag. * \param allow_posthandle Where to set the allow post handle event flag. * * \details * This is where we interact the subcalls state with the master_call's state. * * \note This function must parallel q931_master_pass_event(). * * \return Nothing */ static void nt_ptmp_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct q931_call *subcall, int *allow_event, int *allow_posthandle) { struct q931_call *master = subcall->master_call; struct q931_call *winner = q931_get_subcall_winner(master); enum Q931_RANKED_CALL_STATE master_rank; enum Q931_RANKED_CALL_STATE subcall_rank; /* For broadcast calls, we default to not allowing events to keep events received to a minimum * and to allow post processing, since that is where hangup and subcall state handling and other processing is done */ *allow_event = 0; *allow_posthandle = 1; master_rank = q931_rank_state(master->ourcallstate); if (master_rank < Q931_RANKED_CALL_STATE_CONNECT) { /* This subcall can compete for the call. */ subcall->newcall = 0; } switch (mh->msg) { case Q931_SETUP_ACKNOWLEDGE: #if 0 /* Overlap dialing in PTMP NT mode not supported at the present time. */ if (master_rank < Q931_RANKED_CALL_STATE_OVERLAP) { *allow_event = 1; UPDATE_OURCALLSTATE(ctrl, master, Q931_CALL_STATE_OVERLAP_SENDING); } #endif /* Overlap dialing in PTMP NT mode not supported at the present time. */ break; case Q931_CALL_PROCEEDING: if (master_rank < Q931_RANKED_CALL_STATE_PROCEEDING) { *allow_event = 1; UPDATE_OURCALLSTATE(ctrl, master, Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING); } break; case Q931_PROGRESS: /* * We will just ignore this message since there could be multiple devices * competing for this call. Who has access to the B channel at this time * to give in-band signals anyway? */ break; case Q931_ALERTING: if (master_rank < Q931_RANKED_CALL_STATE_ALERTING) { *allow_event = 1; UPDATE_OURCALLSTATE(ctrl, master, Q931_CALL_STATE_CALL_DELIVERED); } break; case Q931_CONNECT: if (master_rank < Q931_RANKED_CALL_STATE_CONNECT) { UPDATE_OURCALLSTATE(ctrl, master, Q931_CALL_STATE_ACTIVE); q931_set_subcall_winner(subcall); *allow_event = 1; } else { /* Call clearing of non selected calls occurs in * q931_set_subcall_winner() - All we need to do is make sure * that this connect is not acknowledged */ *allow_posthandle = 0; } break; case Q931_DISCONNECT: case Q931_RELEASE: case Q931_RELEASE_COMPLETE: if (!winner) { int master_priority; int slave_priority; /* Pass up the cause on a priority basis. */ switch (master->cause) { case PRI_CAUSE_USER_BUSY: master_priority = 2; break; case PRI_CAUSE_CALL_REJECTED: master_priority = 1; break; default: master_priority = 0; break; case -1: /* First time priority. */ master_priority = -2; break; } switch (subcall->cause) { case PRI_CAUSE_USER_BUSY: slave_priority = 2; break; case PRI_CAUSE_CALL_REJECTED: slave_priority = 1; break; default: slave_priority = 0; break; case PRI_CAUSE_INCOMPATIBLE_DESTINATION: /* Cause explicitly ignored */ slave_priority = -1; break; } if (master_priority < slave_priority) { /* Pass up the cause to the master. */ master->cause = subcall->cause; } } else { /* There *is* a winner */ if (subcall == winner) { /* .. and we're it: */ *allow_event = 1; UPDATE_OURCALLSTATE(ctrl, master, Q931_CALL_STATE_CALL_ABORT); } } break; case Q931_FACILITY: case Q931_NOTIFY: if (winner) { if (subcall == winner) { /* Only deal with the winner. */ *allow_event = 1; } } else { /* The overlap rank does not count here. */ if (master_rank == Q931_RANKED_CALL_STATE_OVERLAP) { master_rank = Q931_RANKED_CALL_STATE_PRESENT; } subcall_rank = q931_rank_state(subcall->ourcallstate); if (subcall_rank == Q931_RANKED_CALL_STATE_OVERLAP) { subcall_rank = Q931_RANKED_CALL_STATE_PRESENT; } if (master_rank == subcall_rank) { /* * No winner yet but the subcall is as advanced as the master. * Allow the supplementary service event to pass. */ *allow_event = 1; } } break; default: if (winner && subcall == winner) { /* Only deal with the winner. */ *allow_event = 1; } break; } } /*! * \internal * \brief Fill in the RING event fields. * * \param ctrl D channel controller. * \param call Q.931 call leg * * \return Nothing */ static void q931_fill_ring_event(struct pri *ctrl, struct q931_call *call) { struct pri_subcommand *subcmd; if (call->redirecting.from.number.valid && !call->redirecting.count) { /* * This is most likely because the redirecting number came in * with the redirecting ie only and not a DivertingLegInformation2. */ call->redirecting.count = 1; } if (call->redirecting.state == Q931_REDIRECTING_STATE_PENDING_TX_DIV_LEG_3) { /* * Valid for Q.SIG and ETSI PRI/BRI-PTP modes: * Setup the redirecting.to informtion so we can identify * if the user wants to manually supply the COLR for this * redirected to number if further redirects could happen. * * All the user needs to do is set the REDIRECTING(to-pres) * to the COLR and REDIRECTING(to-num) = complete-dialed-number * (i.e. CALLERID(dnid)) to be safe after determining that the * incoming call was redirected by checking if the * REDIRECTING(count) is nonzero. */ call->redirecting.to.number = call->called.number; call->redirecting.to.number.presentation = PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; } ctrl->ev.e = PRI_EVENT_RING; ctrl->ev.ring.subcmds = &ctrl->subcmds; ctrl->ev.ring.channel = q931_encode_channel(call); /* Calling party information */ ctrl->ev.ring.callingpres = q931_party_id_presentation(&call->remote_id); ctrl->ev.ring.callingplan = call->remote_id.number.plan; if (call->ani.valid) { ctrl->ev.ring.callingplanani = call->ani.plan; libpri_copy_string(ctrl->ev.ring.callingani, call->ani.str, sizeof(ctrl->ev.ring.callingani)); } else { ctrl->ev.ring.callingplanani = -1; ctrl->ev.ring.callingani[0] = '\0'; } libpri_copy_string(ctrl->ev.ring.callingnum, call->remote_id.number.str, sizeof(ctrl->ev.ring.callingnum)); libpri_copy_string(ctrl->ev.ring.callingname, call->remote_id.name.str, sizeof(ctrl->ev.ring.callingname)); q931_party_id_copy_to_pri(&ctrl->ev.ring.calling, &call->remote_id); /* for backwards compatibility, still need ctrl->ev.ring.callingsubaddr */ if (!call->remote_id.subaddress.type) { /* NSAP: Type = 0 */ libpri_copy_string(ctrl->ev.ring.callingsubaddr, (char *) call->remote_id.subaddress.data, sizeof(ctrl->ev.ring.callingsubaddr)); } else { ctrl->ev.ring.callingsubaddr[0] = '\0'; } ctrl->ev.ring.ani2 = call->ani2; /* Called party information */ ctrl->ev.ring.calledplan = call->called.number.plan; libpri_copy_string(ctrl->ev.ring.callednum, call->called.number.str, sizeof(ctrl->ev.ring.callednum)); q931_party_subaddress_copy_to_pri(&ctrl->ev.ring.called_subaddress, &call->called.subaddress); /* Original called party information (For backward compatibility) */ libpri_copy_string(ctrl->ev.ring.origcalledname, call->redirecting.orig_called.name.str, sizeof(ctrl->ev.ring.origcalledname)); libpri_copy_string(ctrl->ev.ring.origcallednum, call->redirecting.orig_called.number.str, sizeof(ctrl->ev.ring.origcallednum)); ctrl->ev.ring.callingplanorigcalled = call->redirecting.orig_called.number.plan; if (call->redirecting.orig_called.number.valid || call->redirecting.orig_called.name.valid) { ctrl->ev.ring.origredirectingreason = call->redirecting.orig_reason; } else { ctrl->ev.ring.origredirectingreason = -1; } /* Redirecting from party information (For backward compatibility) */ ctrl->ev.ring.callingplanrdnis = call->redirecting.from.number.plan; libpri_copy_string(ctrl->ev.ring.redirectingnum, call->redirecting.from.number.str, sizeof(ctrl->ev.ring.redirectingnum)); libpri_copy_string(ctrl->ev.ring.redirectingname, call->redirecting.from.name.str, sizeof(ctrl->ev.ring.redirectingname)); ctrl->ev.ring.redirectingreason = call->redirecting.reason; libpri_copy_string(ctrl->ev.ring.useruserinfo, call->useruserinfo, sizeof(ctrl->ev.ring.useruserinfo)); call->useruserinfo[0] = '\0'; libpri_copy_string(ctrl->ev.ring.keypad_digits, call->keypad_digits, sizeof(ctrl->ev.ring.keypad_digits)); 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->bc.userl1; ctrl->ev.ring.complete = call->complete; 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; if (call->redirecting.count) { subcmd = q931_alloc_subcommand(ctrl); if (subcmd) { /* Setup redirecting subcommand */ subcmd->cmd = PRI_SUBCMD_REDIRECTING; q931_party_redirecting_copy_to_pri(&subcmd->u.redirecting, &call->redirecting); } } } /*! * \internal * \brief Fill in the FACILITY event fields. * * \param ctrl D channel controller. * \param call Q.931 call leg. * * \return Nothing */ static void q931_fill_facility_event(struct pri *ctrl, struct q931_call *call) { ctrl->ev.e = PRI_EVENT_FACILITY; ctrl->ev.facility.subcmds = &ctrl->subcmds; ctrl->ev.facility.channel = q931_encode_channel(call); ctrl->ev.facility.cref = call->cr; 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 */ libpri_copy_string(ctrl->ev.facility.callingname, call->remote_id.name.str, sizeof(ctrl->ev.facility.callingname)); libpri_copy_string(ctrl->ev.facility.callingnum, call->remote_id.number.str, sizeof(ctrl->ev.facility.callingnum)); ctrl->ev.facility.callingpres = q931_party_id_presentation(&call->remote_id); ctrl->ev.facility.callingplan = call->remote_id.number.plan; } /*! * \internal * \brief APDU wait for response message timeout. * * \param data Callback data pointer. * * \return Nothing */ 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) { q931_fill_facility_event(ctrl, call); ctrl->schedev = 1; } 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 = ctrl->link.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 = ctrl->link.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; } } /*! * \brief Find the transfer call indicated by the given link_id. * * \param ctrl D channel controller. * \param link_id Link id of the other call involved in the transfer. * * \retval found-master-call on success. * \retval NULL on error. */ struct q931_call *q931_find_link_id_call(struct pri *ctrl, int link_id) { struct q931_call *cur; struct q931_call *winner; struct q931_call *match; match = NULL; for (cur = *ctrl->callpool; cur; cur = cur->next) { if (cur->is_link_id_valid && cur->link_id == link_id) { /* Found the link_id call. */ winner = q931_find_winning_call(cur); if (!winner) { /* There is no winner. */ break; } switch (winner->ourcallstate) { case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: case Q931_CALL_STATE_CALL_DELIVERED: case Q931_CALL_STATE_CALL_RECEIVED: case Q931_CALL_STATE_CONNECT_REQUEST: case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: case Q931_CALL_STATE_ACTIVE: /* The link_id call is in a state suitable for transfer. */ match = cur; break; default: /* The link_id call is not in a good state to transfer. */ break; } break; } } return match; } /*! * \brief Find the active call given the held call. * * \param ctrl D channel controller. * \param held_call Held call to help locate a compatible active call. * * \retval master-active-call on success. * \retval NULL on error. */ struct q931_call *q931_find_held_active_call(struct pri *ctrl, struct q931_call *held_call) { struct q931_call *cur; struct q931_call *winner; struct q931_call *match; if (!held_call->link) { /* Held call does not have an active link. */ return NULL; } match = NULL; for (cur = *ctrl->callpool; cur; cur = cur->next) { if (cur->hold_state == Q931_HOLD_STATE_IDLE) { /* Found an active call. */ winner = q931_find_winning_call(cur); if (!winner || (BRI_NT_PTMP(ctrl) && winner->link != held_call->link)) { /* There is no winner or the active call does not go to the same TEI. */ continue; } switch (winner->ourcallstate) { case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: case Q931_CALL_STATE_CALL_DELIVERED: case Q931_CALL_STATE_CALL_RECEIVED: case Q931_CALL_STATE_CONNECT_REQUEST: case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: case Q931_CALL_STATE_ACTIVE: break; default: /* Active call not in a good state to transfer. */ continue; } if (q931_party_number_cmp(&winner->remote_id.number, &held_call->remote_id.number)) { /* The remote party number does not match. This is a weak match. */ match = cur; continue; } /* Found an exact match. */ match = cur; break; } } return match; } /*! * \internal * \brief Find the held call given the active call. * * \param ctrl D channel controller. * \param active_call Active call to help locate a compatible held call. * * \retval master-held-call on success. * \retval NULL on error. */ static struct q931_call *q931_find_held_call(struct pri *ctrl, struct q931_call *active_call) { struct q931_call *cur; struct q931_call *winner; struct q931_call *match; if (!active_call->link) { /* Active call does not have an active link. */ return NULL; } match = NULL; for (cur = *ctrl->callpool; cur; cur = cur->next) { if (cur->hold_state == Q931_HOLD_STATE_CALL_HELD) { /* Found a held call. */ winner = q931_find_winning_call(cur); if (!winner || (BRI_NT_PTMP(ctrl) && winner->link != active_call->link)) { /* There is no winner or the held call does not go to the same TEI. */ continue; } switch (winner->ourcallstate) { case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: case Q931_CALL_STATE_CALL_DELIVERED: case Q931_CALL_STATE_CALL_RECEIVED: case Q931_CALL_STATE_CONNECT_REQUEST: case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: case Q931_CALL_STATE_ACTIVE: break; default: /* Held call not in a good state to transfer. */ continue; } if (q931_party_number_cmp(&winner->remote_id.number, &active_call->remote_id.number)) { /* The remote party number does not match. This is a weak match. */ match = cur; continue; } /* Found an exact match. */ match = cur; break; } } return match; } /*! * \internal * \brief Determine RELEASE_COMPLETE cause code for newcall rejection. * * \param call Q.931 call leg. * * \return Cause code for RELEASE_COMPLETE. */ static int newcall_rel_comp_cause(struct q931_call *call) { struct q931_call *master; int cause; cause = PRI_CAUSE_INVALID_CALL_REFERENCE; master = call->master_call; if (master != call && master->t312_timer) { switch (master->ourcallstate) { case Q931_CALL_STATE_CALL_ABORT: cause = PRI_CAUSE_RECOVERY_ON_TIMER_EXPIRE; break; default: break; } } return cause; } /*! * \internal * \brief Restart channel notify event for upper layer notify chain timeout. * * \param data Callback data pointer. * * \return Nothing */ static void q931_restart_notify_timeout(void *data) { struct q931_call *call = data; struct pri *ctrl = call->pri; /* Create channel restart event to upper layer. */ call->channelno = call->restart.chan_no[call->restart.idx++]; ctrl->ev.e = PRI_EVENT_RESTART; ctrl->ev.restart.channel = q931_encode_channel(call); ctrl->schedev = 1; /* Reschedule for next channel restart event needed. */ if (call->restart.idx < call->restart.count) { call->restart.timer = pri_schedule_event(ctrl, 0, q931_restart_notify_timeout, call); } else { /* No more restart events needed. */ call->restart.timer = 0; /* Send back the Restart Acknowledge. All channels are now restarted. */ if (call->slotmap != -1) { /* Send slotmap format. */ call->channelno = -1; } restart_ack(ctrl, call); } } /*! * \internal * \brief Setup restart channel notify events for upper layer. * * \param call Q.931 call leg. * * \return Nothing */ static void q931_restart_notify(struct q931_call *call) { struct pri *ctrl = call->pri; /* Start notify chain. */ pri_schedule_del(ctrl, call->restart.timer); call->restart.idx = 0; q931_restart_notify_timeout(call); } /*! * \internal * \brief Process the decoded information in the Q.931 message. * * \param ctrl D channel controller. * \param mh Q.931 message header. * \param c Q.931 call leg. * \param missingmand Number of missing mandatory ie's. * * \note * When this function returns c may be destroyed so you can no * longer dereference it. * * \retval 0 if no error or event. * \retval Q931_RES_HAVEEVENT if have an event. * \retval -1 on error. */ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct q931_call *c, int missingmand) { int res; int changed; struct apdu_event *cur = NULL; struct pri_subcommand *subcmd; struct q931_call *master_call; switch(mh->msg) { case Q931_RESTART: q931_display_subcmd(ctrl, c); if (missingmand) { q931_status(ctrl, c, PRI_CAUSE_MANDATORY_IE_MISSING); pri_destroycall(ctrl, c); break; } UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_RESTART); c->peercallstate = Q931_CALL_STATE_RESTART_REQUEST; /* Notify upper layer of restart event */ if ((c->channelno == -1 && c->slotmap == -1) || !c->restart.count) { /* * Whole link restart or channel not identified by Channel ID ie * 3.3 octets. * * Send back the Restart Acknowledge */ restart_ack(ctrl, c); /* Create channel restart event to upper layer. */ ctrl->ev.e = PRI_EVENT_RESTART; ctrl->ev.restart.channel = q931_encode_channel(c); } else { /* Start notify chain. */ q931_restart_notify(c); } return Q931_RES_HAVEEVENT; case Q931_REGISTER: q931_display_subcmd(ctrl, c); /* 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: q931_display_subcmd(ctrl, c); if (missingmand) { q931_release_complete(ctrl, c, PRI_CAUSE_MANDATORY_IE_MISSING); break; } /* Must be new call */ if (!c->newcall) { break; } if (c->progressmask & PRI_PROG_CALLER_NOT_ISDN) c->nonisdn = 1; c->newcall = 0; UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_CALL_PRESENT); c->peercallstate = Q931_CALL_STATE_CALL_INITIATED; 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; case Q931_ALERTING: q931_display_subcmd(ctrl, c); stop_t303(c->master_call); if (c->newcall) { q931_release_complete(ctrl, c, newcall_rel_comp_cause(c)); break; } UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_CALL_DELIVERED); c->peercallstate = Q931_CALL_STATE_CALL_RECEIVED; ctrl->ev.e = PRI_EVENT_RINGING; ctrl->ev.ringing.subcmds = &ctrl->subcmds; ctrl->ev.ringing.channel = q931_encode_channel(c); ctrl->ev.ringing.cref = c->cr; ctrl->ev.ringing.call = c->master_call; ctrl->ev.ringing.progress = c->progress; ctrl->ev.ringing.progressmask = c->progressmask; 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); break; } } return Q931_RES_HAVEEVENT; case Q931_CONNECT: q931_display_subcmd(ctrl, c); stop_t303(c->master_call); if (c->newcall) { q931_release_complete(ctrl, c, newcall_rel_comp_cause(c)); break; } switch (c->ourcallstate) { case Q931_CALL_STATE_CALL_INITIATED: case Q931_CALL_STATE_OVERLAP_SENDING: case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: case Q931_CALL_STATE_CALL_DELIVERED: case Q931_CALL_STATE_CALL_PRESENT: case Q931_CALL_STATE_CALL_RECEIVED: case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: case Q931_CALL_STATE_OVERLAP_RECEIVING: /* Accept CONNECT in these states. */ break; default: q931_status(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE); return 0; } ctrl->ev.e = PRI_EVENT_ANSWER; ctrl->ev.answer.subcmds = &ctrl->subcmds; ctrl->ev.answer.channel = q931_encode_channel(c); ctrl->ev.answer.cref = c->cr; ctrl->ev.answer.call = c->master_call; ctrl->ev.answer.progress = c->progress; ctrl->ev.answer.progressmask = c->progressmask; libpri_copy_string(ctrl->ev.answer.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.answer.useruserinfo)); c->useruserinfo[0] = '\0'; if (!ctrl->manual_connect_ack) { q931_connect_acknowledge(ctrl, c, 0); } else { UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_CONNECT_REQUEST); c->peercallstate = Q931_CALL_STATE_CONNECT_REQUEST; } if (c->cis_auto_disconnect && c->cis_call) { /* Make sure WE release when we initiate a signalling only connection */ q931_hangup(ctrl, c, PRI_CAUSE_NORMAL_CLEARING); } else { c->incoming_ct_state = INCOMING_CT_STATE_IDLE; /* Setup connected line subcommand */ subcmd = q931_alloc_subcommand(ctrl); if (subcmd) { subcmd->cmd = PRI_SUBCMD_CONNECTED_LINE; q931_party_id_copy_to_pri(&subcmd->u.connected_line.id, &c->remote_id); } return Q931_RES_HAVEEVENT; } break; case Q931_FACILITY: q931_display_subcmd(ctrl, c); if (c->newcall) { q931_release_complete(ctrl, c, newcall_rel_comp_cause(c)); break; } switch (c->incoming_ct_state) { case INCOMING_CT_STATE_POST_CONNECTED_LINE: c->incoming_ct_state = INCOMING_CT_STATE_IDLE; subcmd = q931_alloc_subcommand(ctrl); if (subcmd) { subcmd->cmd = PRI_SUBCMD_CONNECTED_LINE; q931_party_id_copy_to_pri(&subcmd->u.connected_line.id, &c->remote_id); } break; default: break; } if (ctrl->subcmds.counter_subcmd) { q931_fill_facility_event(ctrl, c); return Q931_RES_HAVEEVENT; } break; case Q931_PROGRESS: if (missingmand) { q931_status(ctrl, c, PRI_CAUSE_MANDATORY_IE_MISSING); pri_destroycall(ctrl, c); break; } ctrl->ev.e = PRI_EVENT_PROGRESS; ctrl->ev.proceeding.cause = c->cause; /* Fall through */ case Q931_CALL_PROCEEDING: q931_display_subcmd(ctrl, c); stop_t303(c->master_call); ctrl->ev.proceeding.subcmds = &ctrl->subcmds; if (c->newcall) { q931_release_complete(ctrl, c, newcall_rel_comp_cause(c)); break; } if ((c->ourcallstate != Q931_CALL_STATE_CALL_INITIATED) && (c->ourcallstate != Q931_CALL_STATE_OVERLAP_SENDING) && (c->ourcallstate != Q931_CALL_STATE_CALL_DELIVERED) && (c->ourcallstate != Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING)) { if (mh->msg == Q931_PROGRESS && c->ourcallstate == Q931_CALL_STATE_ACTIVE && ctrl->switchtype == PRI_SWITCH_QSIG) { /* * Q.SIG is odd to allow PROGRESS when in the Active state since * the media path is already open. Ignore it since it doesn't * convey anything very useful. Maybe they will stop doing it. * * See ECMA-143 Section 10.1.7.2. */ break; } q931_status(ctrl,c,PRI_CAUSE_WRONG_MESSAGE); break; } ctrl->ev.proceeding.channel = q931_encode_channel(c); if (mh->msg == Q931_CALL_PROCEEDING) { ctrl->ev.e = PRI_EVENT_PROCEEDING; UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING); c->peercallstate = Q931_CALL_STATE_INCOMING_CALL_PROCEEDING; } ctrl->ev.proceeding.progress = c->progress; ctrl->ev.proceeding.progressmask = c->progressmask; ctrl->ev.proceeding.cref = c->cr; ctrl->ev.proceeding.call = c->master_call; for (cur = c->apdus; cur; cur = cur->next) { if (!cur->sent && cur->message == Q931_FACILITY) { q931_facility(ctrl, c); break; } } return Q931_RES_HAVEEVENT; case Q931_CONNECT_ACKNOWLEDGE: q931_display_subcmd(ctrl, c); if (c->newcall) { q931_release_complete(ctrl, c, newcall_rel_comp_cause(c)); break; } switch (c->ourcallstate) { default: if (ctrl->localtype == PRI_NETWORK || ctrl->switchtype == PRI_SWITCH_QSIG) { q931_status(ctrl, c, PRI_CAUSE_WRONG_MESSAGE); break; } /* Fall through */ case Q931_CALL_STATE_CONNECT_REQUEST: case Q931_CALL_STATE_ACTIVE: UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_ACTIVE); c->peercallstate = Q931_CALL_STATE_ACTIVE; if (ctrl->manual_connect_ack) { ctrl->ev.e = PRI_EVENT_CONNECT_ACK; ctrl->ev.connect_ack.subcmds = &ctrl->subcmds; ctrl->ev.connect_ack.channel = q931_encode_channel(c); ctrl->ev.connect_ack.call = c->master_call; return Q931_RES_HAVEEVENT; } break; } break; case Q931_STATUS: q931_display_subcmd(ctrl, c); if (missingmand) { q931_status(ctrl, c, PRI_CAUSE_MANDATORY_IE_MISSING); pri_destroycall(ctrl, c); break; } if (c->newcall) { if (c->cr & 0x7fff) q931_release_complete(ctrl,c,PRI_CAUSE_WRONG_CALL_STATE); break; } /* Do nothing */ /* Also when the STATUS asks for the call of an unexisting reference send RELEASE_COMPLETE */ if ((ctrl->debug & PRI_DEBUG_Q931_ANOMALY) && (c->cause != PRI_CAUSE_INTERWORKING)) pri_error(ctrl, "Received unsolicited status: %s\n", pri_cause2str(c->cause)); if ( #if 0 /* * Workaround for S-12 ver 7.3 - it responds to * invalid/non-implemented IEs in SETUP with NULL call state. */ !c->sugcallstate && (c->ourcallstate != Q931_CALL_STATE_CALL_INITIATED) #else /* * Remove "workaround" since it breaks certification testing. If * we receive a STATUS message of call state NULL and we are not * in the call state NULL we must clear resources and return to * the call state to pass testing. See section 5.8.11 of Q.931. */ !c->sugcallstate #endif ) { ctrl->ev.hangup.subcmds = &ctrl->subcmds; ctrl->ev.hangup.channel = q931_encode_channel(c); ctrl->ev.hangup.cause = c->cause; ctrl->ev.hangup.cref = c->cr; ctrl->ev.hangup.call = c->master_call; ctrl->ev.hangup.aoc_units = c->aoc_units; ctrl->ev.hangup.call_held = NULL; ctrl->ev.hangup.call_active = NULL; libpri_copy_string(ctrl->ev.hangup.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.hangup.useruserinfo)); /* Free resources */ UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL); c->peercallstate = Q931_CALL_STATE_NULL; if (c->outboundbroadcast && (c != q931_get_subcall_winner(c->master_call))) { /* Complete clearing the disconnecting non-winning subcall. */ pri_hangup(ctrl, c, -1); return 0; } /* Free resources */ if (c->alive) { ctrl->ev.e = PRI_EVENT_HANGUP; c->alive = 0; } else if (c->sendhangupack) { ctrl->ev.e = PRI_EVENT_HANGUP_ACK; pri_hangup(ctrl, c, c->cause); } else { pri_hangup(ctrl, c, c->cause); return 0; } return Q931_RES_HAVEEVENT; } break; case Q931_RELEASE_COMPLETE: q931_display_subcmd(ctrl, c); 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; ctrl->ev.hangup.cref = c->cr; ctrl->ev.hangup.call = c->master_call; ctrl->ev.hangup.aoc_units = c->aoc_units; ctrl->ev.hangup.call_held = NULL; 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); } if (c->outboundbroadcast && (c != q931_get_subcall_winner(c->master_call))) { /* Complete clearing the disconnecting non-winning subcall. */ pri_hangup(ctrl, c, -1); return 0; } /* Free resources */ if (c->alive) { ctrl->ev.e = PRI_EVENT_HANGUP; c->alive = 0; } else if (c->sendhangupack) { ctrl->ev.e = PRI_EVENT_HANGUP_ACK; pri_hangup(ctrl, c, c->cause); } else { pri_hangup(ctrl, c, c->cause); return 0; } return Q931_RES_HAVEEVENT; case Q931_RELEASE: q931_display_subcmd(ctrl, c); c->hangupinitiated = 1; if (missingmand) { /* Force cause to be mandatory IE missing */ c->cause = PRI_CAUSE_MANDATORY_IE_MISSING; } /* * Don't send RELEASE_COMPLETE if they sent us RELEASE while we * were waiting for RELEASE_COMPLETE from them, assume a NULL state. */ 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, newcall_rel_comp_cause(c)); break; } ctrl->ev.e = PRI_EVENT_HANGUP; ctrl->ev.hangup.subcmds = &ctrl->subcmds; ctrl->ev.hangup.channel = q931_encode_channel(c); ctrl->ev.hangup.cause = c->cause; ctrl->ev.hangup.cref = c->cr; ctrl->ev.hangup.call = c->master_call; ctrl->ev.hangup.aoc_units = c->aoc_units; ctrl->ev.hangup.call_held = NULL; 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); } if (c->outboundbroadcast && (c != q931_get_subcall_winner(c->master_call))) { /* Complete clearing the disconnecting non-winning subcall. */ pri_hangup(ctrl, c, -1); return 0; } return Q931_RES_HAVEEVENT; case Q931_DISCONNECT: q931_display_subcmd(ctrl, c); c->hangupinitiated = 1; if (missingmand) { /* Still let user call release */ c->cause = PRI_CAUSE_MANDATORY_IE_MISSING; } if (c->newcall) { q931_release_complete(ctrl, c, newcall_rel_comp_cause(c)); break; } /* * Determine if there are any calls that can be proposed for * a transfer of held call on disconnect. */ ctrl->ev.hangup.call_held = NULL; ctrl->ev.hangup.call_active = NULL; switch (c->ourcallstate) { case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: case Q931_CALL_STATE_CALL_DELIVERED: case Q931_CALL_STATE_CALL_RECEIVED: case Q931_CALL_STATE_CONNECT_REQUEST: case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: case Q931_CALL_STATE_ACTIVE: if (c->master_call->hold_state == Q931_HOLD_STATE_CALL_HELD) { /* Held call is being disconnected first. */ ctrl->ev.hangup.call_held = c->master_call; ctrl->ev.hangup.call_active = q931_find_held_active_call(ctrl, c); } else { /* Active call is being disconnected first. */ if (q931_find_winning_call(c) == c) { /* * Only a normal call or the winning call of a broadcast SETUP * can participate in a transfer of held call on disconnet. */ ctrl->ev.hangup.call_active = c->master_call; ctrl->ev.hangup.call_held = q931_find_held_call(ctrl, c); } } break; default: break; } if (ctrl->debug & PRI_DEBUG_Q931_STATE) { if (ctrl->ev.hangup.call_held) { pri_message(ctrl, "-- Found held call: %p cref:%d\n", ctrl->ev.hangup.call_held, ctrl->ev.hangup.call_held->cr); } if (ctrl->ev.hangup.call_active) { pri_message(ctrl, "-- Found active call: %p cref:%d\n", ctrl->ev.hangup.call_active, ctrl->ev.hangup.call_active->cr); } if (ctrl->ev.hangup.call_held && ctrl->ev.hangup.call_active) { pri_message(ctrl, "-- Transfer held call on disconnect possible.\n"); } } UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_DISCONNECT_INDICATION); c->peercallstate = Q931_CALL_STATE_DISCONNECT_REQUEST; c->sendhangupack = 1; /* wait for a RELEASE so that sufficient time has passed for the inband audio to be heard */ if (ctrl->acceptinbanddisconnect && (c->progressmask & PRI_PROG_INBAND_AVAILABLE)) break; /* Return such an event */ ctrl->ev.e = PRI_EVENT_HANGUP_REQ; ctrl->ev.hangup.subcmds = &ctrl->subcmds; ctrl->ev.hangup.channel = q931_encode_channel(c); ctrl->ev.hangup.cause = c->cause; ctrl->ev.hangup.cref = c->cr; ctrl->ev.hangup.call = c->master_call; 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->outboundbroadcast && (c != q931_get_subcall_winner(c->master_call))) { /* Complete clearing the disconnecting non-winning subcall. */ pri_hangup(ctrl, c, -1); return 0; } 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 { pri_hangup(ctrl,c,c->cause); } break; case Q931_RESTART_ACKNOWLEDGE: q931_display_subcmd(ctrl, c); UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL); c->peercallstate = Q931_CALL_STATE_NULL; ctrl->ev.e = PRI_EVENT_RESTART_ACK; ctrl->ev.restartack.channel = q931_encode_channel(c); if (c->restart_tx.t316_timer /* * Since the DS1 value can vary, only check the channel number. * We're only supposed to have one RESTART request outstanding * at a time anyway. */ && (c->restart_tx.channel & 0xFF) == (ctrl->ev.restartack.channel & 0xFF)) { /* This is the RESTART ACKNOWLEDGE we are expecting. */ stop_t316(c); } return Q931_RES_HAVEEVENT; case Q931_INFORMATION: /* XXX We're handling only INFORMATION messages that contain overlap dialing received digit + the "Complete" msg which is basically an EOF on further digits XXX */ q931_display_subcmd(ctrl, c); if (c->newcall) { q931_release_complete(ctrl, c, newcall_rel_comp_cause(c)); break; } if (c->ourcallstate != Q931_CALL_STATE_OVERLAP_RECEIVING) { ctrl->ev.e = PRI_EVENT_KEYPAD_DIGIT; ctrl->ev.digit.subcmds = &ctrl->subcmds; ctrl->ev.digit.call = c->master_call; ctrl->ev.digit.channel = q931_encode_channel(c); libpri_copy_string(ctrl->ev.digit.digits, c->keypad_digits, sizeof(ctrl->ev.digit.digits)); return Q931_RES_HAVEEVENT; } ctrl->ev.e = PRI_EVENT_INFO_RECEIVED; ctrl->ev.ring.subcmds = &ctrl->subcmds; ctrl->ev.ring.call = c->master_call; ctrl->ev.ring.channel = q931_encode_channel(c); libpri_copy_string(ctrl->ev.ring.callednum, c->overlap_digits, sizeof(ctrl->ev.ring.callednum)); q931_party_id_copy_to_pri(&ctrl->ev.ring.calling, &c->remote_id); /* for backwards compatibility, still need ctrl->ev.ring.callingsubaddr */ if (!c->remote_id.subaddress.type) { /* NSAP: Type = 0 */ libpri_copy_string(ctrl->ev.ring.callingsubaddr, (char *) c->remote_id.subaddress.data, sizeof(ctrl->ev.ring.callingsubaddr)); } else { ctrl->ev.ring.callingsubaddr[0] = '\0'; } ctrl->ev.ring.complete = c->complete; /* this covers IE 33 (Sending Complete) */ return Q931_RES_HAVEEVENT; case Q931_STATUS_ENQUIRY: q931_display_clear(c); if (c->newcall) { q931_release_complete(ctrl, c, newcall_rel_comp_cause(c)); } else q931_status(ctrl,c, PRI_CAUSE_RESPONSE_TO_STATUS_ENQUIRY); break; case Q931_SETUP_ACKNOWLEDGE: q931_display_subcmd(ctrl, c); stop_t303(c->master_call); if (c->newcall) { q931_release_complete(ctrl, c, newcall_rel_comp_cause(c)); break; } UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_OVERLAP_SENDING); c->peercallstate = Q931_CALL_STATE_OVERLAP_RECEIVING; ctrl->ev.e = PRI_EVENT_SETUP_ACK; ctrl->ev.setup_ack.subcmds = &ctrl->subcmds; ctrl->ev.setup_ack.channel = q931_encode_channel(c); ctrl->ev.setup_ack.call = c->master_call; for (cur = c->apdus; cur; cur = cur->next) { if (!cur->sent && cur->message == Q931_FACILITY) { q931_facility(ctrl, c); break; } } return Q931_RES_HAVEEVENT; case Q931_NOTIFY: res = 0; changed = 0; switch (c->notify) { case PRI_NOTIFY_CALL_DIVERTING: if (c->redirection_number.valid) { c->redirecting.to.number = c->redirection_number; if (c->redirecting.count < PRI_MAX_REDIRECTS) { ++c->redirecting.count; } switch (c->ourcallstate) { case Q931_CALL_STATE_CALL_DELIVERED: /* Call is deflecting after we have seen an ALERTING message */ c->redirecting.reason = PRI_REDIR_FORWARD_ON_NO_REPLY; break; default: /* Call is deflecting for call forwarding unconditional or busy reason. */ c->redirecting.reason = PRI_REDIR_UNKNOWN; break; } /* Setup redirecting subcommand */ subcmd = q931_alloc_subcommand(ctrl); if (subcmd) { subcmd->cmd = PRI_SUBCMD_REDIRECTING; q931_party_redirecting_copy_to_pri(&subcmd->u.redirecting, &c->redirecting); } } q931_display_subcmd(ctrl, c); if (ctrl->subcmds.counter_subcmd) { q931_fill_facility_event(ctrl, c); res = Q931_RES_HAVEEVENT; } break; case PRI_NOTIFY_TRANSFER_ACTIVE: if (q931_party_number_cmp(&c->remote_id.number, &c->redirection_number)) { /* The remote party number information changed. */ c->remote_id.number = c->redirection_number; changed = 1; } /* Fall through */ case PRI_NOTIFY_TRANSFER_ALERTING: if (ctrl->display_flags.receive & PRI_DISPLAY_OPTION_NAME_UPDATE) { struct q931_party_name name; if (q931_display_name_get(c, &name)) { if (q931_party_name_cmp(&c->remote_id.name, &name)) { /* The remote party name information changed. */ c->remote_id.name = name; changed = 1; } } } if (c->redirection_number.valid && q931_party_number_cmp(&c->remote_id.number, &c->redirection_number)) { /* The remote party number information changed. */ c->remote_id.number = c->redirection_number; changed = 1; } if (c->remote_id.subaddress.valid) { /* * Clear the subaddress as the remote party has been changed. * Any new subaddress will arrive later. */ q931_party_subaddress_init(&c->remote_id.subaddress); changed = 1; } if (changed) { /* Setup connected line subcommand */ subcmd = q931_alloc_subcommand(ctrl); if (subcmd) { subcmd->cmd = PRI_SUBCMD_CONNECTED_LINE; q931_party_id_copy_to_pri(&subcmd->u.connected_line.id, &c->remote_id); } } q931_display_subcmd(ctrl, c); if (ctrl->subcmds.counter_subcmd) { q931_fill_facility_event(ctrl, c); res = Q931_RES_HAVEEVENT; } break; default: ctrl->ev.e = PRI_EVENT_NOTIFY; ctrl->ev.notify.subcmds = &ctrl->subcmds; ctrl->ev.notify.channel = q931_encode_channel(c); ctrl->ev.notify.info = c->notify; ctrl->ev.notify.call = c->master_call; res = Q931_RES_HAVEEVENT; break; } q931_display_subcmd(ctrl, c); return res; case Q931_HOLD: q931_display_subcmd(ctrl, c); res = 0; if (!ctrl->hold_support) { /* * Blocking any calls from getting on HOLD effectively * disables HOLD/RETRIEVE. */ q931_send_hold_rej_msg(ctrl, c, PRI_CAUSE_FACILITY_NOT_IMPLEMENTED); break; } switch (c->ourcallstate) { case Q931_CALL_STATE_CALL_RECEIVED: case Q931_CALL_STATE_CONNECT_REQUEST: case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: if (PTMP_MODE(ctrl)) { /* HOLD request only allowed in these states if point-to-point mode. */ q931_send_hold_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE); break; } /* Fall through */ case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: case Q931_CALL_STATE_CALL_DELIVERED: case Q931_CALL_STATE_ACTIVE: if (!q931_find_winning_call(c)) { /* * Only the winning call of a broadcast SETUP can do hold since the * call must be answered first. */ q931_send_hold_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE); break; } master_call = c->master_call; switch (master_call->hold_state) { case Q931_HOLD_STATE_HOLD_REQ: if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, "HOLD collision\n"); } if (ctrl->localtype == PRI_NETWORK) { /* The network ignores HOLD request on a hold collision. */ break; } /* Fall through */ case Q931_HOLD_STATE_IDLE: ctrl->ev.e = PRI_EVENT_HOLD; ctrl->ev.hold.channel = q931_encode_channel(c); ctrl->ev.hold.call = master_call; ctrl->ev.hold.subcmds = &ctrl->subcmds; res = Q931_RES_HAVEEVENT; UPDATE_HOLD_STATE(ctrl, master_call, Q931_HOLD_STATE_HOLD_IND); break; default: q931_send_hold_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE); break; } break; case Q931_CALL_STATE_DISCONNECT_INDICATION: case Q931_CALL_STATE_RELEASE_REQUEST: /* Ignore HOLD request in these states. */ break; default: q931_send_hold_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE); break; } return res; case Q931_HOLD_ACKNOWLEDGE: q931_display_subcmd(ctrl, c); res = 0; master_call = c->master_call; switch (master_call->hold_state) { case Q931_HOLD_STATE_HOLD_REQ: ctrl->ev.e = PRI_EVENT_HOLD_ACK; ctrl->ev.hold_ack.channel = q931_encode_channel(c); ctrl->ev.hold_ack.call = master_call; ctrl->ev.hold_ack.subcmds = &ctrl->subcmds; res = Q931_RES_HAVEEVENT; UPDATE_HOLD_STATE(ctrl, master_call, Q931_HOLD_STATE_CALL_HELD); /* Call is now on hold so forget the channel. */ c->channelno = 0;/* No channel */ c->ds1no = 0; c->ds1explicit = 0; c->chanflags = 0; /* Stop T-HOLD timer */ pri_schedule_del(ctrl, master_call->hold_timer); master_call->hold_timer = 0; break; default: /* Ignore response. Response is late or spurrious. */ break; } return res; case Q931_HOLD_REJECT: q931_display_subcmd(ctrl, c); res = 0; master_call = c->master_call; switch (master_call->hold_state) { case Q931_HOLD_STATE_HOLD_REQ: if (missingmand) { /* Still, let hold rejection continue. */ c->cause = PRI_CAUSE_MANDATORY_IE_MISSING; } ctrl->ev.e = PRI_EVENT_HOLD_REJ; ctrl->ev.hold_rej.channel = q931_encode_channel(c); ctrl->ev.hold_rej.call = master_call; ctrl->ev.hold_rej.cause = c->cause; ctrl->ev.hold_rej.subcmds = &ctrl->subcmds; res = Q931_RES_HAVEEVENT; UPDATE_HOLD_STATE(ctrl, master_call, Q931_HOLD_STATE_IDLE); /* Stop T-HOLD timer */ pri_schedule_del(ctrl, master_call->hold_timer); master_call->hold_timer = 0; break; default: /* Ignore response. Response is late or spurrious. */ break; } return res; case Q931_RETRIEVE: q931_display_subcmd(ctrl, c); res = 0; switch (c->ourcallstate) { case Q931_CALL_STATE_CALL_RECEIVED: case Q931_CALL_STATE_CONNECT_REQUEST: case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: if (PTMP_MODE(ctrl)) { /* RETRIEVE request only allowed in these states if point-to-point mode. */ q931_send_retrieve_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE); break; } /* Fall through */ case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: case Q931_CALL_STATE_CALL_DELIVERED: case Q931_CALL_STATE_ACTIVE: if (!q931_find_winning_call(c)) { /* * Only the winning call of a broadcast SETUP can do hold since the * call must be answered first. */ q931_send_retrieve_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE); break; } master_call = c->master_call; switch (master_call->hold_state) { case Q931_HOLD_STATE_RETRIEVE_REQ: if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, "RETRIEVE collision\n"); } if (ctrl->localtype == PRI_NETWORK) { /* The network ignores RETRIEVE request on a retrieve collision. */ break; } /* Fall through */ case Q931_HOLD_STATE_CALL_HELD: ctrl->ev.e = PRI_EVENT_RETRIEVE; ctrl->ev.retrieve.channel = q931_encode_channel(c); ctrl->ev.retrieve.call = master_call; ctrl->ev.retrieve.flexible = !(c->chanflags & FLAG_EXCLUSIVE); ctrl->ev.retrieve.subcmds = &ctrl->subcmds; res = Q931_RES_HAVEEVENT; UPDATE_HOLD_STATE(ctrl, master_call, Q931_HOLD_STATE_RETRIEVE_IND); break; default: q931_send_retrieve_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE); break; } break; case Q931_CALL_STATE_DISCONNECT_INDICATION: case Q931_CALL_STATE_RELEASE_REQUEST: /* Ignore RETRIEVE request in these states. */ break; default: q931_send_retrieve_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE); break; } return res; case Q931_RETRIEVE_ACKNOWLEDGE: q931_display_subcmd(ctrl, c); res = 0; master_call = c->master_call; switch (master_call->hold_state) { case Q931_HOLD_STATE_RETRIEVE_REQ: UPDATE_HOLD_STATE(ctrl, master_call, Q931_HOLD_STATE_IDLE); /* Stop T-RETRIEVE timer */ pri_schedule_del(ctrl, master_call->hold_timer); master_call->hold_timer = 0; ctrl->ev.e = PRI_EVENT_RETRIEVE_ACK; ctrl->ev.retrieve_ack.channel = q931_encode_channel(c); ctrl->ev.retrieve_ack.call = master_call; ctrl->ev.retrieve_ack.subcmds = &ctrl->subcmds; res = Q931_RES_HAVEEVENT; break; default: /* Ignore response. Response is late or spurrious. */ break; } return res; case Q931_RETRIEVE_REJECT: q931_display_subcmd(ctrl, c); res = 0; master_call = c->master_call; switch (master_call->hold_state) { case Q931_HOLD_STATE_CALL_HELD: /* In this state likely because of a RETRIEVE collision. */ case Q931_HOLD_STATE_RETRIEVE_REQ: UPDATE_HOLD_STATE(ctrl, master_call, Q931_HOLD_STATE_CALL_HELD); /* Call is still on hold so forget the channel. */ c->channelno = 0;/* No channel */ c->ds1no = 0; c->ds1explicit = 0; c->chanflags = 0; /* Stop T-RETRIEVE timer */ pri_schedule_del(ctrl, master_call->hold_timer); master_call->hold_timer = 0; if (missingmand) { /* Still, let retrive rejection continue. */ c->cause = PRI_CAUSE_MANDATORY_IE_MISSING; } ctrl->ev.e = PRI_EVENT_RETRIEVE_REJ; ctrl->ev.retrieve_rej.channel = q931_encode_channel(c); ctrl->ev.retrieve_rej.call = master_call; ctrl->ev.retrieve_rej.cause = c->cause; ctrl->ev.retrieve_rej.subcmds = &ctrl->subcmds; res = Q931_RES_HAVEEVENT; break; default: /* Ignore response. Response is late or spurrious. */ break; } return res; case Q931_USER_INFORMATION: case Q931_SEGMENT: case Q931_CONGESTION_CONTROL: case Q931_RESUME: case Q931_RESUME_ACKNOWLEDGE: case Q931_RESUME_REJECT: case Q931_SUSPEND: case Q931_SUSPEND_ACKNOWLEDGE: case Q931_SUSPEND_REJECT: pri_error(ctrl, "!! Not yet handling post-handle message type %s (0x%X)\n", msg2str(mh->msg), mh->msg); /* Fall through */ default: pri_error(ctrl, "!! Don't know how to post-handle message type %s (0x%X)\n", msg2str(mh->msg), mh->msg); q931_display_clear(c); q931_status(ctrl,c, PRI_CAUSE_MESSAGE_TYPE_NONEXIST); if (c->newcall) { pri_destroycall(ctrl, c); } return -1; } return 0; } /* Clear a call, although we did not receive any hangup notification. */ static int pri_internal_clear(struct q931_call *c) { struct pri *ctrl = c->pri; int res; pri_schedule_del(ctrl, c->retranstimer); c->retranstimer = 0; c->useruserinfo[0] = '\0'; //c->cause = -1; c->causecode = -1; c->causeloc = -1; c->sugcallstate = Q931_CALL_STATE_NOT_SET; c->aoc_units = -1; if (c->master_call->outboundbroadcast && c == q931_find_winning_call(c)) { /* Pass the hangup cause to the master_call. */ c->master_call->cause = c->cause; } q931_clr_subcommands(ctrl); ctrl->ev.hangup.subcmds = &ctrl->subcmds; ctrl->ev.hangup.channel = q931_encode_channel(c); ctrl->ev.hangup.cause = c->cause; ctrl->ev.hangup.cref = c->cr; ctrl->ev.hangup.call = c->master_call; ctrl->ev.hangup.aoc_units = c->aoc_units; ctrl->ev.hangup.call_held = NULL; ctrl->ev.hangup.call_active = NULL; libpri_copy_string(ctrl->ev.hangup.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.hangup.useruserinfo)); if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, DBGHEAD "alive %d, hangupack %d\n", DBGINFO, c->alive, c->sendhangupack); } if (c->cc.record) { if (c->cc.record->signaling == c) { pri_cc_event(ctrl, c, c->cc.record, CC_EVENT_SIGNALING_GONE); } else if (c->cc.record->original_call == c) { pri_cc_event(ctrl, c, c->cc.record, CC_EVENT_INTERNAL_CLEARING); } } /* Free resources */ if (c->alive) { c->alive = 0; ctrl->ev.e = PRI_EVENT_HANGUP; res = Q931_RES_HAVEEVENT; } else if (c->sendhangupack) { pri_hangup(ctrl, c, c->cause); ctrl->ev.e = PRI_EVENT_HANGUP_ACK; res = Q931_RES_HAVEEVENT; } else { pri_hangup(ctrl, c, c->cause); if (ctrl->subcmds.counter_subcmd) { q931_fill_facility_event(ctrl, ctrl->link.dummy_call); res = Q931_RES_HAVEEVENT; } else { res = 0; } } return res; } /* Handle T309 timeout for an active call. */ static void pri_dl_down_timeout(void *data) { struct q931_call *c = data; struct pri *ctrl = c->pri; if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, "T309 timed out waiting for data link re-establishment\n"); } c->retranstimer = 0; c->cause = PRI_CAUSE_DESTINATION_OUT_OF_ORDER; UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL); c->peercallstate = Q931_CALL_STATE_NULL; if (pri_internal_clear(c) == Q931_RES_HAVEEVENT) { ctrl->schedev = 1; } } /* Handle Layer 2 down event for a non active call. */ static void pri_dl_down_cancelcall(void *data) { struct q931_call *c = data; struct pri *ctrl = c->pri; if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, "Cancel call after data link failure\n"); } c->retranstimer = 0; c->cause = PRI_CAUSE_DESTINATION_OUT_OF_ORDER; UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL); c->peercallstate = Q931_CALL_STATE_NULL; if (pri_internal_clear(c) == Q931_RES_HAVEEVENT) { ctrl->schedev = 1; } } /*! * \internal * \brief Convert the DL event to a string. * * \param event Data-link event to convert to a string. * * \return DL event string */ static const char *q931_dl_event2str(enum Q931_DL_EVENT event) { const char *str; str = "Unknown"; switch (event) { case Q931_DL_EVENT_NONE: str = "Q931_DL_EVENT_NONE"; break; case Q931_DL_EVENT_DL_ESTABLISH_IND: str = "Q931_DL_EVENT_DL_ESTABLISH_IND"; break; case Q931_DL_EVENT_DL_ESTABLISH_CONFIRM: str = "Q931_DL_EVENT_DL_ESTABLISH_CONFIRM"; break; case Q931_DL_EVENT_DL_RELEASE_IND: str = "Q931_DL_EVENT_DL_RELEASE_IND"; break; case Q931_DL_EVENT_DL_RELEASE_CONFIRM: str = "Q931_DL_EVENT_DL_RELEASE_CONFIRM"; break; case Q931_DL_EVENT_TEI_REMOVAL: str = "Q931_DL_EVENT_TEI_REMOVAL"; break; } return str; } /*! * \brief Receive a DL event from layer 2. * * \param link Q.921 link event occurred on. * \param event Data-link event reporting. * * \return Nothing */ void q931_dl_event(struct q921_link *link, enum Q931_DL_EVENT event) { struct q931_call *cur; struct q931_call *cur_next; struct q931_call *call; struct pri *ctrl; int idx; if (!link) { return; } ctrl = link->ctrl; if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, "TEI=%d DL event: %s(%d)\n", link->tei, q931_dl_event2str(event), event); } switch (event) { case Q931_DL_EVENT_TEI_REMOVAL: if (!BRI_NT_PTMP(ctrl)) { /* Only NT PTMP has anything to worry about when the TEI is removed. */ break; } /* * For NT PTMP, this deviation from the specifications is needed * because we have no way to re-associate any T309 calls on the * removed TEI. */ for (cur = *ctrl->callpool; cur; cur = cur->next) { if (cur->outboundbroadcast) { /* Does this master call have a subcall on the link that went down? */ call = NULL; for (idx = 0; idx < ARRAY_LEN(cur->subcalls); ++idx) { if (cur->subcalls[idx] && cur->subcalls[idx]->link == link) { /* This subcall is on the link that went down. */ call = cur->subcalls[idx]; break; } } if (!call) { /* No subcall is on the link that went down. */ continue; } } else if (cur->link != link) { /* This call is not on the link that went down. */ continue; } else { call = cur; } if (!(cur->cr & ~Q931_CALL_REFERENCE_FLAG)) { /* Simply destroy the global call reference call record. */ if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, "TEI=%d Destroying global call record\n", link->tei); } q931_destroycall(ctrl, call); continue; } /* * NOTE: We are gambling that no T309 timer's have had a chance * to expire. They should not expire since we are either called * immediately after the Q931_DL_EVENT_DL_RELEASE_xxx or after a * timeout of 0. */ if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, "Cancel call cref=%d on channel %d in state %d (%s)\n", call->cr, call->channelno, call->ourcallstate, q931_call_state_str(call->ourcallstate)); } call->link = NULL; pri_schedule_del(ctrl, call->retranstimer); call->retranstimer = pri_schedule_event(ctrl, 0, pri_dl_down_cancelcall, call); } break; case Q931_DL_EVENT_DL_RELEASE_IND: case Q931_DL_EVENT_DL_RELEASE_CONFIRM: for (cur = *ctrl->callpool; cur; cur = cur_next) { /* The master call could get destroyed if the last subcall dies. */ cur_next = cur->next; if (!(cur->cr & ~Q931_CALL_REFERENCE_FLAG)) { /* Don't do anything on the global call reference call record. */ continue; } if (cur->outboundbroadcast) { /* Does this master call have a subcall on the link that went down? */ call = NULL; for (idx = 0; idx < ARRAY_LEN(cur->subcalls); ++idx) { if (cur->subcalls[idx] && cur->subcalls[idx]->link == link) { /* This subcall is on the link that went down. */ call = cur->subcalls[idx]; break; } } if (!call) { /* No subcall is on the link that went down. */ continue; } } else if (cur->link != link) { /* This call is not on the link that went down. */ continue; } else { call = cur; } switch (call->ourcallstate) { case Q931_CALL_STATE_ACTIVE: /* NOTE: Only a winning subcall can get to the active state. */ if (ctrl->nfas) { /* * The upper layer should handle T309 for NFAS since the calls * could be maintained by a backup D channel if the B channel * for the call did not go into alarm. */ break; } /* * For a call in Active state, activate T309 only if there is * no timer already running. * * NOTE: cur != call when we have a winning subcall. */ if (!cur->retranstimer || !call->retranstimer) { if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, "Start T309 for call cref=%d on channel %d\n", call->cr, call->channelno); } call->retranstimer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T309], pri_dl_down_timeout, call); } break; case Q931_CALL_STATE_NULL: break; default: /* * For a call that is not in Active state, schedule internal * clearing of the call 'ASAP' (delay 0). * * NOTE: We are killing NFAS calls that are not connected yet * because there are likely messages in flight when this link * went down that could leave the call in an unknown/stuck state. */ if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, "Cancel call cref=%d on channel %d in state %d (%s)\n", call->cr, call->channelno, call->ourcallstate, q931_call_state_str(call->ourcallstate)); } if (cur->outboundbroadcast) { /* Simply destroy non-winning subcalls. */ q931_destroycall(ctrl, call); continue; } pri_schedule_del(ctrl, call->retranstimer); call->retranstimer = pri_schedule_event(ctrl, 0, pri_dl_down_cancelcall, call); break; } } break; case Q931_DL_EVENT_DL_ESTABLISH_IND: case Q931_DL_EVENT_DL_ESTABLISH_CONFIRM: for (cur = *ctrl->callpool; cur; cur = cur->next) { if (!(cur->cr & ~Q931_CALL_REFERENCE_FLAG)) { /* Don't do anything on the global call reference call record. */ continue; } if (cur->outboundbroadcast) { /* Does this master call have a subcall on the link that came up? */ call = NULL; for (idx = 0; idx < ARRAY_LEN(cur->subcalls); ++idx) { if (cur->subcalls[idx] && cur->subcalls[idx]->link == link) { /* This subcall is on the link that came up. */ call = cur->subcalls[idx]; break; } } if (!call) { /* No subcall is on the link that came up. */ continue; } } else if (cur->link != link) { /* This call is not on the link that came up. */ continue; } else { call = cur; } switch (call->ourcallstate) { case Q931_CALL_STATE_ACTIVE: /* NOTE: Only a winning subcall can get to the active state. */ if (pri_schedule_check(ctrl, call->retranstimer, pri_dl_down_timeout, call)) { if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, "Stop T309 for call cref=%d on channel %d\n", call->cr, call->channelno); } pri_schedule_del(ctrl, call->retranstimer); call->retranstimer = 0; } q931_status(ctrl, call, PRI_CAUSE_NORMAL_UNSPECIFIED); break; case Q931_CALL_STATE_NULL: case Q931_CALL_STATE_DISCONNECT_REQUEST: case Q931_CALL_STATE_DISCONNECT_INDICATION: case Q931_CALL_STATE_RELEASE_REQUEST: break; default: if (event == Q931_DL_EVENT_DL_ESTABLISH_CONFIRM) { /* * Lets not send a STATUS message for this call as we * requested the link to be established as a likely * result of this call. */ break; } /* * The STATUS message sent here is not required by Q.931, * but it may help anyway. * This looks like a new call started while the link was down. */ q931_status(ctrl, call, PRI_CAUSE_NORMAL_UNSPECIFIED); break; } } break; default: pri_message(ctrl, DBGHEAD "unexpected event %d.\n", DBGINFO, event); break; } } int q931_call_getcrv(struct pri *ctrl, q931_call *call, int *callmode) { if (callmode) *callmode = call->cr & 0x7; return ((call->cr & 0x7fff) >> 3); } int q931_call_setcrv(struct pri *ctrl, q931_call *call, int crv, int callmode) { /* Do not allow changing the dummy call reference */ if (!q931_is_dummy_call(call)) { call->cr = (crv << 3) & 0x7fff; call->cr |= (callmode & 0x7); } return 0; } libpri-1.4.14/rose_etsi_cc.c0000644000000000000000000020307011377242706014400 0ustar rootroot/* * 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 */ libpri-1.4.14/rose_address.c0000644000000000000000000010134211173441755014412 0ustar rootroot/* * 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 Addressing-Data-Elements * * Addressing-Data-Elements ETS 300 196-1 D.3 * * \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 public or private network PartyNumber 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 number * \param length_of_number * \param type_of_number * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *rose_enc_NetworkPartyNumber(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const unsigned char *number, size_t length_of_number, u_int8_t type_of_number) { unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, type_of_number)); ASN1_CALL(pos, asn1_enc_string_bin(pos, end, ASN1_TYPE_NUMERIC_STRING, number, length_of_number)); ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the PartyNumber 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 party_number * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ unsigned char *rose_enc_PartyNumber(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct rosePartyNumber *party_number) { switch (party_number->plan) { case 0: /* Unknown PartyNumber */ ASN1_CALL(pos, asn1_enc_string_bin(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0, party_number->str, party_number->length)); break; case 1: /* Public PartyNumber */ ASN1_CALL(pos, rose_enc_NetworkPartyNumber(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, party_number->str, party_number->length, party_number->ton)); break; case 2: /* NSAP encoded PartyNumber */ ASN1_CALL(pos, asn1_enc_string_bin(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, party_number->str, party_number->length)); break; case 3: /* Data PartyNumber (Not used) */ ASN1_CALL(pos, asn1_enc_string_bin(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3, party_number->str, party_number->length)); break; case 4: /* Telex PartyNumber (Not used) */ ASN1_CALL(pos, asn1_enc_string_bin(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 4, party_number->str, party_number->length)); break; case 5: /* Private PartyNumber */ ASN1_CALL(pos, rose_enc_NetworkPartyNumber(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 5, party_number->str, party_number->length, party_number->ton)); break; case 8: /* National Standard PartyNumber (Not used) */ ASN1_CALL(pos, asn1_enc_string_bin(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 8, party_number->str, party_number->length)); break; default: ASN1_ENC_ERROR(ctrl, "Unknown numbering plan"); return NULL; } return pos; } /*! * \brief Encode the PartySubaddress 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 party_subaddress * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ unsigned char *rose_enc_PartySubaddress(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct rosePartySubaddress *party_subaddress) { unsigned char *seq_len; switch (party_subaddress->type) { case 0: /* UserSpecified */ ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); ASN1_CALL(pos, asn1_enc_string_bin(pos, end, ASN1_TYPE_OCTET_STRING, party_subaddress->u.user_specified.information, party_subaddress->length)); if (party_subaddress->u.user_specified.odd_count_present) { ASN1_CALL(pos, asn1_enc_boolean(pos, end, ASN1_TYPE_BOOLEAN, party_subaddress->u.user_specified.odd_count)); } ASN1_CONSTRUCTED_END(seq_len, pos, end); break; case 1: /* NSAP */ ASN1_CALL(pos, asn1_enc_string_bin(pos, end, ASN1_TYPE_OCTET_STRING, party_subaddress->u.nsap, party_subaddress->length)); break; default: ASN1_ENC_ERROR(ctrl, "Unknown subaddress type"); return NULL; } return pos; } /*! * \brief Encode the Address 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 address * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ unsigned char *rose_enc_Address(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseAddress *address) { unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &address->number)); if (address->subaddress.length) { ASN1_CALL(pos, rose_enc_PartySubaddress(ctrl, pos, end, &address->subaddress)); } ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the PresentedNumberUnscreened 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 party * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ unsigned char *rose_enc_PresentedNumberUnscreened(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct rosePresentedNumberUnscreened *party) { unsigned char *seq_len; switch (party->presentation) { case 0: /* presentationAllowedNumber */ /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0); ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &party->number)); ASN1_CONSTRUCTED_END(seq_len, pos, end); break; case 1: /* presentationRestricted */ ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1)); break; case 2: /* numberNotAvailableDueToInterworking */ ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2)); break; case 3: /* presentationRestrictedNumber */ /* EXPLICIT tag */ ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3); ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &party->number)); ASN1_CONSTRUCTED_END(seq_len, pos, end); break; default: ASN1_ENC_ERROR(ctrl, "Unknown presentation type"); return NULL; } return pos; } /*! * \brief Encode the NumberScreened 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 screened * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ unsigned char *rose_enc_NumberScreened(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseNumberScreened *screened) { unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &screened->number)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, screened->screening_indicator)); ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the PresentedNumberScreened 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 party * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ unsigned char *rose_enc_PresentedNumberScreened(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct rosePresentedNumberScreened *party) { switch (party->presentation) { case 0: /* presentationAllowedNumber */ ASN1_CALL(pos, rose_enc_NumberScreened(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0, &party->screened)); break; case 1: /* presentationRestricted */ ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1)); break; case 2: /* numberNotAvailableDueToInterworking */ ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2)); break; case 3: /* presentationRestrictedNumber */ ASN1_CALL(pos, rose_enc_NumberScreened(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3, &party->screened)); break; default: ASN1_ENC_ERROR(ctrl, "Unknown presentation type"); return NULL; } return pos; } /*! * \brief Encode the AddressScreened 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 screened * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ unsigned char *rose_enc_AddressScreened(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseAddressScreened *screened) { unsigned char *seq_len; ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &screened->number)); ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, screened->screening_indicator)); if (screened->subaddress.length) { ASN1_CALL(pos, rose_enc_PartySubaddress(ctrl, pos, end, &screened->subaddress)); } ASN1_CONSTRUCTED_END(seq_len, pos, end); return pos; } /*! * \brief Encode the PresentedAddressScreened 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 party * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ unsigned char *rose_enc_PresentedAddressScreened(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct rosePresentedAddressScreened *party) { switch (party->presentation) { case 0: /* presentationAllowedAddress */ ASN1_CALL(pos, rose_enc_AddressScreened(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0, &party->screened)); break; case 1: /* presentationRestricted */ ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1)); break; case 2: /* numberNotAvailableDueToInterworking */ ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2)); break; case 3: /* presentationRestrictedAddress */ ASN1_CALL(pos, rose_enc_AddressScreened(ctrl, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3, &party->screened)); break; default: ASN1_ENC_ERROR(ctrl, "Unknown presentation type"); return NULL; } return pos; } /*! * \internal * \brief Decode the NumberDigits PartyNumber 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 party_number Parameter storage to fill. * * \retval Start of the next ASN.1 component on success. * \retval NULL on error. */ static const unsigned char *rose_dec_NumberDigits(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct rosePartyNumber *party_number) { size_t str_len; ASN1_CALL(pos, asn1_dec_string_max(ctrl, name, tag, pos, end, sizeof(party_number->str), party_number->str, &str_len)); party_number->length = str_len; return pos; } /*! * \internal * \brief Decode the NSAP PartyNumber 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 party_number Parameter storage to fill. * * \retval Start of the next ASN.1 component on success. * \retval NULL on error. */ static const unsigned char *rose_dec_NSAPPartyNumber(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct rosePartyNumber *party_number) { size_t str_len; ASN1_CALL(pos, asn1_dec_string_bin(ctrl, name, tag, pos, end, sizeof(party_number->str), party_number->str, &str_len)); party_number->length = str_len; return pos; } /*! * \internal * \brief Decode the public or private network PartyNumber 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 party_number Parameter storage to fill. * * \retval Start of the next ASN.1 component on success. * \retval NULL on error. */ static const unsigned char *rose_dec_NetworkPartyNumber(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct rosePartyNumber *party_number) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; 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(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, "typeOfNumber", tag, pos, seq_end, &value)); party_number->ton = value; ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag & ~ASN1_PC_MASK, ASN1_TYPE_NUMERIC_STRING); ASN1_CALL(pos, rose_dec_NumberDigits(ctrl, "numberDigits", tag, pos, seq_end, party_number)); ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the PartyNumber 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 party_number Parameter storage to fill. * * \retval Start of the next ASN.1 component on success. * \retval NULL on error. */ const unsigned char *rose_dec_PartyNumber(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct rosePartyNumber *party_number) { if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s PartyNumber\n", name); } party_number->ton = 0; /* unknown */ switch (tag & ~ASN1_PC_MASK) { case ASN1_CLASS_CONTEXT_SPECIFIC | 0: party_number->plan = 0; /* Unknown PartyNumber */ ASN1_CALL(pos, rose_dec_NumberDigits(ctrl, "unknownPartyNumber", tag, pos, end, party_number)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 1: /* Must be constructed but we will not check for it for simplicity. */ party_number->plan = 1; /* Public PartyNumber */ ASN1_CALL(pos, rose_dec_NetworkPartyNumber(ctrl, "publicPartyNumber", tag, pos, end, party_number)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 2: party_number->plan = 2; /* NSAP encoded PartyNumber */ ASN1_CALL(pos, rose_dec_NSAPPartyNumber(ctrl, "nsapEncodedPartyNumber", tag, pos, end, party_number)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 3: party_number->plan = 3; /* Data PartyNumber (Not used) */ ASN1_CALL(pos, rose_dec_NumberDigits(ctrl, "dataPartyNumber", tag, pos, end, party_number)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 4: party_number->plan = 4; /* Telex PartyNumber (Not used) */ ASN1_CALL(pos, rose_dec_NumberDigits(ctrl, "telexPartyNumber", tag, pos, end, party_number)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 5: /* Must be constructed but we will not check for it for simplicity. */ party_number->plan = 5; /* Private PartyNumber */ ASN1_CALL(pos, rose_dec_NetworkPartyNumber(ctrl, "privatePartyNumber", tag, pos, end, party_number)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 8: party_number->plan = 8; /* National Standard PartyNumber (Not used) */ ASN1_CALL(pos, rose_dec_NumberDigits(ctrl, "nationalStandardPartyNumber", tag, pos, end, party_number)); break; default: ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); return NULL; } return pos; } /*! * \internal * \brief Decode the User PartySubaddress 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 party_subaddress Parameter storage to fill. * * \retval Start of the next ASN.1 component on success. * \retval NULL on error. */ static const unsigned char *rose_dec_UserSubaddress(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct rosePartySubaddress *party_subaddress) { size_t str_len; int32_t odd_count; int length; int seq_offset; const unsigned char *seq_end; party_subaddress->type = 0; /* UserSpecified */ if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s UserSpecified %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); /* SubaddressInformation */ ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag & ~ASN1_PC_MASK, ASN1_TYPE_OCTET_STRING); ASN1_CALL(pos, asn1_dec_string_bin(ctrl, "subaddressInformation", tag, pos, seq_end, sizeof(party_subaddress->u.user_specified.information), party_subaddress->u.user_specified.information, &str_len)); party_subaddress->length = str_len; if (pos < seq_end && *pos != ASN1_INDEF_TERM) { /* * The optional odd count indicator must be present since there * is something left. */ ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_BOOLEAN); ASN1_CALL(pos, asn1_dec_boolean(ctrl, "oddCount", tag, pos, seq_end, &odd_count)); party_subaddress->u.user_specified.odd_count = odd_count; party_subaddress->u.user_specified.odd_count_present = 1; } else { party_subaddress->u.user_specified.odd_count = 0; party_subaddress->u.user_specified.odd_count_present = 0; } ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \internal * \brief Decode the NSAP PartySubaddress 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 party_subaddress Parameter storage to fill. * * \retval Start of the next ASN.1 component on success. * \retval NULL on error. */ static const unsigned char *rose_dec_NSAPSubaddress(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct rosePartySubaddress *party_subaddress) { size_t str_len; party_subaddress->type = 1; /* NSAP */ ASN1_CALL(pos, asn1_dec_string_bin(ctrl, name, tag, pos, end, sizeof(party_subaddress->u.nsap), party_subaddress->u.nsap, &str_len)); party_subaddress->length = str_len; return pos; } /*! * \brief Decode the PartySubaddress 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 party_subaddress Parameter storage to fill. * * \retval Start of the next ASN.1 component on success. * \retval NULL on error. */ const unsigned char *rose_dec_PartySubaddress(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct rosePartySubaddress *party_subaddress) { if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s PartySubaddress\n", name); } switch (tag) { case ASN1_TAG_SEQUENCE: ASN1_CALL(pos, rose_dec_UserSubaddress(ctrl, "user", tag, pos, end, party_subaddress)); break; case ASN1_TYPE_OCTET_STRING: case ASN1_TYPE_OCTET_STRING | ASN1_PC_CONSTRUCTED: ASN1_CALL(pos, rose_dec_NSAPSubaddress(ctrl, "nsap", tag, pos, end, party_subaddress)); break; default: ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); return NULL; } return pos; } /*! * \brief Decode the Address 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 address Parameter storage to fill. * * \retval Start of the next ASN.1 component on success. * \retval NULL on error. */ const unsigned char *rose_dec_Address(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseAddress *address) { int length; int seq_offset; const unsigned char *seq_end; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s Address %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_PartyNumber(ctrl, "partyNumber", tag, pos, seq_end, &address->number)); 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, "partySubaddress", tag, pos, seq_end, &address->subaddress)); } else { address->subaddress.length = 0; /* Subaddress not present */ } ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the PresentedNumberUnscreened 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 party Parameter storage to fill. * * \retval Start of the next ASN.1 component on success. * \retval NULL on error. */ const unsigned char *rose_dec_PresentedNumberUnscreened(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct rosePresentedNumberUnscreened *party) { int length; int seq_offset; const unsigned char *seq_end; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s PresentedNumberUnscreened\n", name); } switch (tag) { case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 0: if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " Explicit %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)); party->presentation = 0; /* presentationAllowedNumber */ ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "presentationAllowedNumber", tag, pos, seq_end, &party->number)); ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 1: party->presentation = 1; /* presentationRestricted */ ASN1_CALL(pos, asn1_dec_null(ctrl, "presentationRestricted", tag, pos, end)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 2: party->presentation = 2; /* numberNotAvailableDueToInterworking */ ASN1_CALL(pos, asn1_dec_null(ctrl, "numberNotAvailableDueToInterworking", tag, pos, end)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 3: if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " Explicit %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)); party->presentation = 3; /* presentationRestrictedNumber */ ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "presentationRestrictedNumber", tag, pos, seq_end, &party->number)); ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); break; default: ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); return NULL; } return pos; } /*! * \brief Decode the NumberScreened 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 screened Parameter storage to fill. * * \retval Start of the next ASN.1 component on success. * \retval NULL on error. */ const unsigned char *rose_dec_NumberScreened(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseNumberScreened *screened) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s NumberScreened %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_PartyNumber(ctrl, "partyNumber", tag, pos, seq_end, &screened->number)); 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, "screeningIndicator", tag, pos, seq_end, &value)); screened->screening_indicator = value; ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the PresentedNumberScreened 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 party Parameter storage to fill. * * \retval Start of the next ASN.1 component on success. * \retval NULL on error. */ const unsigned char *rose_dec_PresentedNumberScreened(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct rosePresentedNumberScreened *party) { if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s PresentedNumberScreened\n", name); } switch (tag) { case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 0: party->presentation = 0; /* presentationAllowedNumber */ ASN1_CALL(pos, rose_dec_NumberScreened(ctrl, "presentationAllowedNumber", tag, pos, end, &party->screened)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 1: party->presentation = 1; /* presentationRestricted */ ASN1_CALL(pos, asn1_dec_null(ctrl, "presentationRestricted", tag, pos, end)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 2: party->presentation = 2; /* numberNotAvailableDueToInterworking */ ASN1_CALL(pos, asn1_dec_null(ctrl, "numberNotAvailableDueToInterworking", tag, pos, end)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 3: party->presentation = 3; /* presentationRestrictedNumber */ ASN1_CALL(pos, rose_dec_NumberScreened(ctrl, "presentationRestrictedNumber", tag, pos, end, &party->screened)); break; default: ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); return NULL; } return pos; } /*! * \brief Decode the AddressScreened 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 screened Parameter storage to fill. * * \retval Start of the next ASN.1 component on success. * \retval NULL on error. */ const unsigned char *rose_dec_AddressScreened(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseAddressScreened *screened) { int32_t value; int length; int seq_offset; const unsigned char *seq_end; if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s AddressScreened %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_PartyNumber(ctrl, "partyNumber", tag, pos, seq_end, &screened->number)); 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, "screeningIndicator", tag, pos, seq_end, &value)); screened->screening_indicator = 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, "partySubaddress", tag, pos, seq_end, &screened->subaddress)); } else { screened->subaddress.length = 0; /* Subaddress not present */ } ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); return pos; } /*! * \brief Decode the PresentedAddressScreened 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 party Parameter storage to fill. * * \retval Start of the next ASN.1 component on success. * \retval NULL on error. */ const unsigned char *rose_dec_PresentedAddressScreened(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct rosePresentedAddressScreened *party) { if (ctrl->debug & PRI_DEBUG_APDU) { pri_message(ctrl, " %s PresentedAddressScreened\n", name); } switch (tag) { case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 0: party->presentation = 0; /* presentationAllowedAddress */ ASN1_CALL(pos, rose_dec_AddressScreened(ctrl, "presentationAllowedAddress", tag, pos, end, &party->screened)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 1: party->presentation = 1; /* presentationRestricted */ ASN1_CALL(pos, asn1_dec_null(ctrl, "presentationRestricted", tag, pos, end)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | 2: party->presentation = 2; /* numberNotAvailableDueToInterworking */ ASN1_CALL(pos, asn1_dec_null(ctrl, "numberNotAvailableDueToInterworking", tag, pos, end)); break; case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 3: party->presentation = 3; /* presentationRestrictedAddress */ ASN1_CALL(pos, rose_dec_AddressScreened(ctrl, "presentationRestrictedAddress", tag, pos, end, &party->screened)); break; default: ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); return NULL; } return pos; } /* ------------------------------------------------------------------- */ /* end rose_address.c */ libpri-1.4.14/rose_q931.c0000644000000000000000000000634011173441755013464 0ustar rootroot/* * 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 Q.931 ie encode/decode functions * * \author Richard Mudgett */ #include "compat.h" #include "libpri.h" #include "pri_internal.h" #include "rose.h" #include "rose_internal.h" #include "asn1.h" /* ------------------------------------------------------------------- */ /*! * \brief Encode the Q.931 ie value. * * \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_CLASS_APPLICATION | 0 unless the caller * implicitly tags it otherwise. * \param q931ie Q931 ie information to encode. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ unsigned char *rose_enc_Q931ie(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseQ931ie *q931ie) { return asn1_enc_string_bin(pos, end, tag, q931ie->contents, q931ie->length); } /*! * \brief Decode the Q.931 ie value. * * \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 q931ie Parameter storage to fill. * \param contents_size Amount of space "allocated" for the q931ie->contents * element. Must have enough room for a null terminator. * * \retval Start of the next ASN.1 component on success. * \retval NULL on error. */ const unsigned char *rose_dec_Q931ie(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseQ931ie *q931ie, size_t contents_size) { size_t str_len; /* NOTE: The q931ie->contents memory is "allocated" after the struct. */ ASN1_CALL(pos, asn1_dec_string_bin(ctrl, name, tag, pos, end, contents_size, q931ie->contents, &str_len)); q931ie->length = str_len; /* * NOTE: We may want to do some basic decoding of the Q.931 ie list * for debug purposes. */ return pos; } /* ------------------------------------------------------------------- */ /* end rose_q931.c */ libpri-1.4.14/rose_internal.h0000644000000000000000000011447511400035226014602 0ustar rootroot/* * 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 Internal definitions and prototypes for ROSE. * * \author Richard Mudgett */ #ifndef _LIBPRI_ROSE_INTERNAL_H #define _LIBPRI_ROSE_INTERNAL_H #include "rose.h" #ifdef __cplusplus extern "C" { #endif /* ------------------------------------------------------------------- */ /* Embedded-Q931-Types */ unsigned char *rose_enc_Q931ie(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseQ931ie *q931ie); const unsigned char *rose_dec_Q931ie(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseQ931ie *q931ie, size_t contents_size); /* Addressing-Data-Elements */ unsigned char *rose_enc_PartyNumber(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct rosePartyNumber *party_number); unsigned char *rose_enc_PartySubaddress(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct rosePartySubaddress *party_subaddress); unsigned char *rose_enc_Address(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseAddress *address); unsigned char *rose_enc_PresentedNumberUnscreened(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct rosePresentedNumberUnscreened *party); unsigned char *rose_enc_NumberScreened(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseNumberScreened *screened); unsigned char *rose_enc_PresentedNumberScreened(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct rosePresentedNumberScreened *party); unsigned char *rose_enc_AddressScreened(struct pri *ctrl, unsigned char *pos, unsigned char *end, unsigned tag, const struct roseAddressScreened *screened); unsigned char *rose_enc_PresentedAddressScreened(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct rosePresentedAddressScreened *party); const unsigned char *rose_dec_PartyNumber(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct rosePartyNumber *party_number); const unsigned char *rose_dec_PartySubaddress(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct rosePartySubaddress *party_subaddress); const unsigned char *rose_dec_Address(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseAddress *address); const unsigned char *rose_dec_PresentedNumberUnscreened(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct rosePresentedNumberUnscreened *party); const unsigned char *rose_dec_NumberScreened(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseNumberScreened *screened); const unsigned char *rose_dec_PresentedNumberScreened(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct rosePresentedNumberScreened *party); const unsigned char *rose_dec_AddressScreened(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseAddressScreened *screened); const unsigned char *rose_dec_PresentedAddressScreened(struct pri *ctrl, const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, struct rosePresentedAddressScreened *party); /* ETSI Advice-of-Charge (AOC) */ unsigned char *rose_enc_etsi_ChargingRequest_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_etsi_ChargingRequest_RES(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args); unsigned char *rose_enc_etsi_AOCSCurrency_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_etsi_AOCSSpecialArr_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_etsi_AOCDCurrency_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_etsi_AOCDChargingUnit_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_etsi_AOCECurrency_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_etsi_AOCEChargingUnit_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); const unsigned char *rose_dec_etsi_ChargingRequest_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_ChargingRequest_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_AOCSCurrency_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_AOCSSpecialArr_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_AOCDCurrency_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_AOCDChargingUnit_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_AOCECurrency_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_AOCEChargingUnit_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args); /* ETSI Call Diversion */ unsigned char *rose_enc_etsi_ActivationDiversion_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_etsi_DeactivationDiversion_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_etsi_ActivationStatusNotificationDiv_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_etsi_DeactivationStatusNotificationDiv_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_etsi_InterrogationDiversion_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_etsi_InterrogationDiversion_RES(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args); unsigned char *rose_enc_etsi_DiversionInformation_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_etsi_CallDeflection_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_etsi_CallRerouting_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_etsi_InterrogateServedUserNumbers_RES(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args); unsigned char *rose_enc_etsi_DivertingLegInformation1_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_etsi_DivertingLegInformation2_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_etsi_DivertingLegInformation3_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); const unsigned char *rose_dec_etsi_ActivationDiversion_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_DeactivationDiversion_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_ActivationStatusNotificationDiv_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_DeactivationStatusNotificationDiv_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_InterrogationDiversion_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_InterrogationDiversion_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_DiversionInformation_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_CallDeflection_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_CallRerouting_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_InterrogateServedUserNumbers_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_DivertingLegInformation1_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_DivertingLegInformation2_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_DivertingLegInformation3_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args); /* ETSI Explicit Call Transfer (ECT) */ unsigned char *rose_enc_etsi_ExplicitEctExecute_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_etsi_SubaddressTransfer_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_etsi_EctLinkIdRequest_RES(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args); unsigned char *rose_enc_etsi_EctInform_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_etsi_EctLoopTest_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_etsi_EctLoopTest_RES(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args); const unsigned char *rose_dec_etsi_ExplicitEctExecute_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_SubaddressTransfer_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_EctLinkIdRequest_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_EctInform_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_EctLoopTest_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_EctLoopTest_RES(struct pri *ctrl, unsigned tag, 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); /* ETSI Message Waiting Indication (MWI) */ unsigned char *rose_enc_etsi_MWIActivate_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_etsi_MWIDeactivate_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_etsi_MWIIndicate_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); const unsigned char *rose_dec_etsi_MWIActivate_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_MWIDeactivate_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_MWIIndicate_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_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); const unsigned char *rose_dec_qsig_Name(struct pri *ctrl, const char *fname, unsigned tag, const unsigned char *pos, const unsigned char *end, struct roseQsigName *name); unsigned char *rose_enc_qsig_CallingName_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_qsig_CalledName_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_qsig_ConnectedName_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_qsig_BusyName_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); const unsigned char *rose_dec_qsig_CallingName_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_CalledName_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_ConnectedName_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_BusyName_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args); /* * Q.SIG Dummy invoke/result argument used by: * SS-AOC-Operations, * Call-Transfer-Operations, * Call-Diversion-Operations, * and SS-MWI-Operations. */ unsigned char *rose_enc_qsig_DummyArg_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_qsig_DummyRes_RES(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args); const unsigned char *rose_dec_qsig_DummyArg_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_DummyRes_RES(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args); /* Q.SIG SS-AOC-Operations */ unsigned char *rose_enc_qsig_ChargeRequest_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_qsig_ChargeRequest_RES(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args); unsigned char *rose_enc_qsig_AocFinal_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_qsig_AocInterim_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_qsig_AocRate_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_qsig_AocComplete_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_qsig_AocComplete_RES(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args); unsigned char *rose_enc_qsig_AocDivChargeReq_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); const unsigned char *rose_dec_qsig_ChargeRequest_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_ChargeRequest_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_AocFinal_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_AocInterim_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_AocRate_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_AocComplete_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_AocComplete_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_AocDivChargeReq_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args); /* Q.SIG Call-Diversion-Operations */ unsigned char *rose_enc_qsig_ActivateDiversionQ_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_qsig_DeactivateDiversionQ_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_qsig_InterrogateDiversionQ_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_qsig_InterrogateDiversionQ_RES(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args); unsigned char *rose_enc_qsig_CheckRestriction_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_qsig_CallRerouting_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_qsig_DivertingLegInformation1_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_qsig_DivertingLegInformation2_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_qsig_DivertingLegInformation3_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); const unsigned char *rose_dec_qsig_ActivateDiversionQ_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_DeactivateDiversionQ_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_InterrogateDiversionQ_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_InterrogateDiversionQ_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_CheckRestriction_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_CallRerouting_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_DivertingLegInformation1_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_DivertingLegInformation2_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_DivertingLegInformation3_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args); /* Q.SIG Call-Transfer-Operations (CT) */ unsigned char *rose_enc_qsig_CallTransferIdentify_RES(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args); unsigned char *rose_enc_qsig_CallTransferInitiate_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_qsig_CallTransferSetup_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_qsig_CallTransferActive_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_qsig_CallTransferComplete_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_qsig_CallTransferUpdate_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_qsig_SubaddressTransfer_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); const unsigned char *rose_dec_qsig_CallTransferIdentify_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_CallTransferInitiate_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_CallTransferSetup_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_CallTransferActive_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_CallTransferComplete_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_CallTransferUpdate_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_SubaddressTransfer_ARG(struct pri *ctrl, unsigned tag, 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); unsigned char *rose_enc_qsig_MWIDeactivate_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_qsig_MWIInterrogate_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_qsig_MWIInterrogate_RES(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args); const unsigned char *rose_dec_qsig_MWIActivate_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_MWIDeactivate_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_MWIInterrogate_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_MWIInterrogate_RES(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args); /* Northern Telecom DMS-100 operations */ unsigned char *rose_enc_dms100_RLT_OperationInd_RES(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args); unsigned char *rose_enc_dms100_RLT_ThirdParty_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); const unsigned char *rose_dec_dms100_RLT_OperationInd_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_dms100_RLT_ThirdParty_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args); /* National ISDN 2 (NI2) operations */ unsigned char *rose_enc_ni2_InformationFollowing_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); unsigned char *rose_enc_ni2_InitiateTransfer_ARG(struct pri *ctrl, unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); const unsigned char *rose_dec_ni2_InformationFollowing_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_ni2_InitiateTransfer_ARG(struct pri *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args); /* ------------------------------------------------------------------- */ #ifdef __cplusplus } #endif #endif /* _LIBPRI_ROSE_INTERNAL_H */ /* ------------------------------------------------------------------- */ /* end rose_internal.h */