module BSSGP_Emulation { import from NS_Types all; import from NS_Emulation all; import from BSSGP_Types all; import from BSSGP_Helper_Functions all; import from IPL4asp_Types all; type record BssgpStatusIndication { Nsei nsei, BssgpBvci bvci, BvcState state } template BssgpStatusIndication t_BssgpStsInd(template Nsei nsei, template BssgpBvci bvci, template BvcState state) := { nsei := nsei, bvci := bvci, state := state } type enumerated BvcState { BVC_S_BLOCKED, BVC_S_UNBLOCKED }; /* port from our (internal) point of view */ type port BSSGP_SP_PT message { in BssgpPdu; out BssgpPdu, NsStatusIndication, BssgpStatusIndication, ASP_Event; } with { extension "internal" }; /* port from the user point of view */ type port BSSGP_PT message { in ASP_Event, NsStatusIndication, BssgpStatusIndication, BssgpPdu; out BssgpPdu; } with { extension "internal" }; function BssgpStart() runs on BSSGP_CT { f_init(); f_ScanEvents(); } private function f_init() runs on BSSGP_CT { /* Connect the UDP socket */ f_change_state(BVC_S_BLOCKED); } type component BSSGP_CT { /* UDP port towards the bottom (IUT) */ port NS_PT BSCP; /* NS-User SAP towards the user */ port BSSGP_SP_PT BSSGP_SP; var boolean g_sgsn_role := true; var BvcState g_ptp_bvc_state := BVC_S_BLOCKED; timer g_T1 := 15.0; timer g_T2 := 60.0; } modulepar { Nsvci mp_nsei := 96; Nsvci mp_bvci := 196; BssgpCellId mp_cellid := { ra_id := { lai := { mcc_mnc := '262F42'H, lac := 13135}, rac := 0 }, cell_id := 20960 }; }; function f_BnsUdReq(template BssgpPdu pdu, BssgpBvci bvci := mp_bvci) return NsUnitdataRequest { var NsUnitdataRequest udr := { bvci := bvci, nsei := mp_nsei, /* for some weird reason we get "Dynamic test case error: Text encoder: Encoding an * unbound integer value." when trying to send the reocrd rather than the octetstring */ //sdu := omit, //bssgp := valueof(pdu) sdu := f_BSSGP_compact_len(enc_BssgpPdu(valueof(pdu))), bssgp := omit } return udr; } function f_BnsUdInd(template BssgpPdu pdu, template BssgpBvci bvci := mp_bvci) return template NsUnitdataIndication { var template NsUnitdataIndication udi := { bvci := bvci, nsei := mp_nsei, sdu := *, bssgp := pdu } return udi; } private function f_change_state(BvcState new_state) runs on BSSGP_CT { log("BSSGP State Transition: ", g_ptp_bvc_state, " -> ", new_state); g_ptp_bvc_state := new_state; BSSGP_SP.send(t_BssgpStsInd(mp_nsei, mp_bvci, g_ptp_bvc_state)); } private function f_sendReset() runs on BSSGP_CT { var BssgpPdu pdu := valueof(t_BVC_RESET(BSSGP_CAUSE_OM_INTERVENTION, mp_bvci, mp_cellid)); log("PDU: ", pdu); log("ENC: ", enc_BssgpPdu(pdu)); /* BVC-RESET is always sent via the SIGNALLING BVCI, see Table 5.4.1 */ BSCP.send(f_BnsUdReq(pdu, 0)); g_T2.start; //f_change_state(BVC_S_WAIT_RESET); } private function f_sendUnblock() runs on BSSGP_CT { BSCP.send(f_BnsUdReq(t_BVC_UNBLOCK(mp_bvci), 0)); g_T1.start; } private function f_sendBlock(BssgpCause cause) runs on BSSGP_CT { BSCP.send(f_BnsUdReq(t_BVC_BLOCK(mp_bvci, cause), 0)); g_T1.start; } private function f_sendStatus(BssgpCause cause, BssgpPdu pdu) runs on BSSGP_CT { /* FIXME: Make sure correct Signaling or PTP BVCI is used! */ BSCP.send(f_BnsUdReq(t_BSSGP_STATUS({ t_BSSGP_IE_Cause(cause), t_BSSGP_IE_Bvci(mp_bvci), t_BSSGP_IE_PDU(pdu)}))); } altstep as_allstate() runs on BSSGP_CT { var NsUnitdataIndication udi; var NsStatusIndication nsi; var ASP_Event evt; /* Respond to BLOCK for wrong NSVCI */ [] BSCP.receive(f_BnsUdInd(t_BVC_BLOCK(?, ?), 0)) -> value udi { log("Rx BVC-BLOCK for unknown BVCI"); f_sendStatus(BSSGP_CAUSE_BVCI_UNKNOWN, udi.bssgp); } /* Respond to RESET with correct BVCI/CellID */ [] BSCP.receive(f_BnsUdInd(t_BVC_RESET(?, mp_bvci, mp_cellid), 0)) -> value udi { log("Rx BVC-RESET for Our BVCI=", mp_bvci); BSCP.send(f_BnsUdReq(t_BVC_RESET_ACK(mp_bvci, mp_cellid), 0)); f_change_state(BVC_S_UNBLOCKED); } /* Respond to RESET for signalling BVCI 0 */ [] BSCP.receive(f_BnsUdInd(t_BVC_RESET(?, 0, mp_cellid), 0)) -> value udi { log("Rx BVC-RESET for Signaling BVCI=0"); BSCP.send(f_BnsUdReq(t_BVC_RESET_ACK(0, mp_cellid), 0)); } /* Respond to RESET with wrong NSEI/NSVCI */ [] BSCP.receive(f_BnsUdInd(t_BVC_RESET(?, ?, ?), 0)) -> value udi { log("Rx BVC-RESET for unknown BVCI"); f_sendStatus(BSSGP_CAUSE_BVCI_UNKNOWN, udi.bssgp); } /* default case of handling unknown PDUs */ [] BSCP.receive(f_BnsUdInd(?, ?)) -> value udi { log("Rx Unexpected BSSGP PDU ", udi.bssgp," in state ", g_ptp_bvc_state); f_sendStatus(BSSGP_CAUSE_PDU_NOT_COMPATIBLE_WITH_PROTOCOL_STATE, udi.bssgp); } /* Forwarding of ASP_Event and NsStatusIndication to user */ [] BSCP.receive(ASP_Event:?) -> value evt { BSSGP_SP.send(evt); } [] BSCP.receive(NsStatusIndication:?) -> value nsi { /* if we just became NS-unblocked, send a BCC-RESET */ if (nsi.old_state != NSE_S_ALIVE_UNBLOCKED and nsi.new_state == NSE_S_ALIVE_UNBLOCKED) { if (g_sgsn_role == false) { f_sendReset(); } /* Idea: We coudl send BVC-UNBLOCK here like some SGSN do */ } BSSGP_SP.send(nsi); } } private function f_ScanEvents() runs on BSSGP_CT { var NsUnitdataIndication udi; var BssgpPdu bs_pdu; var default d; log("matching against ", t_BVC_RESET(?, mp_bvci, mp_cellid)); d := activate(as_allstate()); while (true) { if (g_ptp_bvc_state == BVC_S_BLOCKED) { alt { [] g_T1.timeout { f_sendUnblock(); } [] BSCP.receive(f_BnsUdInd(t_BVC_UNBLOCK_ACK(mp_bvci))) { g_T1.stop; f_change_state(BVC_S_UNBLOCKED); } } } else if (g_ptp_bvc_state == BVC_S_UNBLOCKED) { alt { /* bogus unblock, just respond with ACK */ [] BSCP.receive(f_BnsUdInd(t_BVC_UNBLOCK(mp_bvci), 0)) -> value udi { BSCP.send(f_BnsUdReq(t_BVC_UNBLOCK_ACK(mp_bvci), 0)); } /* Respond to BLOCK with BLOCK-ACK + change state */ [] BSCP.receive(f_BnsUdInd(t_BVC_BLOCK(mp_bvci, ?), 0)) -> value udi { BSCP.send(f_BnsUdReq(t_BVC_BLOCK_ACK(mp_bvci), 0)); g_T1.stop; f_change_state(BVC_S_BLOCKED); } [] g_T1.timeout { f_sendBlock(BSSGP_CAUSE_OM_INTERVENTION); } [] BSCP.receive(f_BnsUdInd(t_BVC_BLOCK_ACK(mp_bvci), 0)) -> value udi { g_T1.stop; f_change_state(BVC_S_BLOCKED); } [] BSCP.receive(f_BnsUdInd(t_BVC_RESET_ACK(mp_bvci, mp_cellid), 0)) -> value udi { g_T2.stop; f_change_state(BVC_S_UNBLOCKED); } /* simply acknowledge all Flow Control Messages */ [g_sgsn_role] BSCP.receive(f_BnsUdInd(t_BVC_FC_BVC)) { BSCP.send(f_BnsUdReq(t_BVC_FC_BVC_ACK)); } [g_sgsn_role] BSCP.receive(f_BnsUdInd(t_BVC_FC_MS)) { BSCP.send(f_BnsUdReq(t_BVC_FC_MS_ACK)); } /* BSSGP-UNITDATA PDUs from network to NS-UNITDATA.ind to user */ [] BSCP.receive(f_BnsUdInd(tr_BSSGP_type(DL_UNITDATA))) -> value udi { BSSGP_SP.send(udi.bssgp); } [] BSCP.receive(f_BnsUdInd(tr_BSSGP_type(UL_UNITDATA))) -> value udi { BSSGP_SP.send(udi.bssgp); } /* pass virtually any PDU from user to NS-UNITDATA PDU on network */ [] BSSGP_SP.receive(BssgpPdu:?) -> value bs_pdu { BSCP.send(f_BnsUdReq(bs_pdu)); } } } } /* while */ //deactivate(d); } }