| 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); |
| } |
| } |