diff --git a/pcu/GPRS_Components.ttcn b/pcu/GPRS_Components.ttcn new file mode 100644 index 000000000..c3ec440e4 --- /dev/null +++ b/pcu/GPRS_Components.ttcn @@ -0,0 +1,620 @@ +module GPRS_Components { +/* + * Osmocom PCU test suite in TTCN-3, components for GPRS handlng + * (C) 2018-2019 Harald Welte + * (C) 2019 Vadim Yanitskiy + * (C) 2020 by sysmocom s.f.m.c. GmbH + * All rights reserved. + * + * Released under the terms of GNU General Public License, Version 2 or + * (at your option) any later version. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +import from General_Types all; +import from Osmocom_Types all; +import from GSM_Types all; +import from GSM_RR_Types all; + +import from Osmocom_VTY_Functions all; +import from TELNETasp_PortType all; + +import from MobileL3_GMM_SM_Types all; +import from RLCMAC_CSN1_Types all; +import from RLCMAC_CSN1_Templates all; +import from RLCMAC_Types all; +import from RLCMAC_Templates all; + +import from MobileL3_CommonIE_Types all; +import from L3_Templates all; + +import from NS_Types all; +import from BSSGP_Types all; +import from Osmocom_Gb_Types all; + +import from BSSGP_Emulation all; /* BssgpConfig */ +import from NS_Emulation all; /* NSConfiguration */ + +import from UD_Types all; +import from PCUIF_Types all; +import from PCUIF_CodecPort all; +import from PCUIF_Components all; +import from IPL4asp_Types all; +import from Native_Functions all; +import from SGSN_Components all; + +type component MS_BTS_IFACE_CT { + /* Virtual BTS component */ + var RAW_PCU_BTS_CT vc_BTS; + /* Connection to the BTS component (one for now) */ + port RAW_PCU_MSG_PT BTS; + + /* Value at which Countdown Procedure starts. Announced by network (GPRS Cell Options as per TS 04.60 Chapter 12.24) */ + var uint4_t g_bs_cv_max := 4; +} + +function f_shutdown(charstring file, integer line, + boolean final := false) +runs on MS_BTS_IFACE_CT { + /* Determine if the test case was aborted in the middle */ + if (not final) { + log("Test case ", testcasename(), " aborted at ", file, ":", line); + } else { + /* Guard verdict to avoid 'none' */ + setverdict(pass); + } + + /* Properly shutdown virtual BTS and its clock generator */ + BTS.send(ts_RAW_PCU_CMD(GENERAL_CMD_SHUTDOWN)); + vc_BTS.done; /* wait untill it's done */ + + /* Shutdown the others and MTC */ + all component.stop; + mtc.stop; +} + +template AckNackDescription t_AckNackDescription_init := { + final_ack := '0'B, + starting_seq_nr := 0, + receive_block_bitmap := '0000000000000000000000000000000000000000000000000000000000000000'B +} + +function f_rlcmac_dl_block_get_tfi(RlcmacDlBlock dl_block) +runs on MS_BTS_IFACE_CT return uint5_t { + if (ischosen(dl_block.data)) { + return dl_block.data.mac_hdr.hdr_ext.tfi; + } else if (ischosen(dl_block.data_egprs)) { + return dl_block.data_egprs.mac_hdr.tfi; + } else { /* Ctrl block */ + if (match(dl_block, tr_RLCMAC_UL_PACKET_ASS_GPRS(?, tr_PktUlAssGprsDynamic(tr_DynamicAllocation(?))))) { + return dl_block.ctrl.payload.u.ul_assignment.gprs.dyn_block_alloc.ul_tfi_assignment; + } + if (match(dl_block, tr_RLCMAC_UL_PACKET_ASS_EGPRS(?, tr_PktUlAssEgprsDynamic(tr_DynamicAllocation(?))))) { + return dl_block.ctrl.payload.u.ul_assignment.egprs.dyn_block_alloc.ul_tfi_assignment; + } + } + setverdict(fail, "DlBlock doesn't contain a TFI:", dl_block); + f_shutdown(__BFILE__, __LINE__); + return 0; /* make compiler happy */ +} + +/* Get the Chan coding command from a dl block containing PACCH UL Assignment */ +function f_rlcmac_dl_block_get_assigned_ul_cs_mcs(RlcmacDlBlock dl_block) +runs on MS_BTS_IFACE_CT return CodingScheme { + if (match(dl_block, tr_RLCMAC_UL_PACKET_ASS_GPRS(?, tr_PktUlAssGprsDynamic(?)))) { + return f_rlcmac_block_ChCodingCommand2cs_mcs(dl_block.ctrl.payload.u.ul_assignment.gprs.ch_coding_cmd); + } + if (match(dl_block, tr_RLCMAC_UL_PACKET_ASS_EGPRS(?, tr_PktUlAssEgprsDynamic(?)))) { + return f_rlcmac_block_EgprsChCodingCommand2cs_mcs(dl_block.ctrl.payload.u.ul_assignment.egprs.chan_coding_cmd); + } + setverdict(fail, "DlBlock doesn't contain CS_MCS information:", dl_block); + f_shutdown(__BFILE__, __LINE__); + return CS_1; /* make compiler happy */ +} + +/* TS 44.060 sec 12.3 Ack/Nack Description */ +function f_acknackdesc_ack_block(inout AckNackDescription desc, RlcmacDlBlock dl_block, BIT1 final_ack := '0'B) +{ + var uint7_t bsn; + var integer i; + var integer inc; + + if (ischosen(dl_block.data)) { + bsn := dl_block.data.mac_hdr.hdr_ext.bsn; + } else { + bsn := dl_block.data_egprs.mac_hdr.bsn1; + } + + inc := bsn - desc.starting_seq_nr + 1; + /* Filling hole? */ + if (bsn < desc.starting_seq_nr) { + desc.receive_block_bitmap[lengthof(desc.receive_block_bitmap) - (desc.starting_seq_nr - bsn)] := int2bit(1, 1); + return; + } + + /* SSN is increased, and so RBB values need to be moved */ + for (i := 0; i < lengthof(desc.receive_block_bitmap) - inc; i := i+1) { + desc.receive_block_bitmap[i] := desc.receive_block_bitmap[i + inc]; + } + for (i := lengthof(desc.receive_block_bitmap) - inc; i < lengthof(desc.receive_block_bitmap) - 1; i := i+1) { + desc.receive_block_bitmap[i] := int2bit(0, 1); + } + /* Now we can set current bit and update SSN */ + desc.starting_seq_nr := bsn + 1; + desc.receive_block_bitmap[lengthof(desc.receive_block_bitmap) - 1] := int2bit(1, 1); + + /* Finally update the final_ack bit as requested: */ + desc.final_ack := final_ack; +} + +/* This function can be used to send DATA.cnf in response to the IUT originated DATA.req. + * NOTE: it's the responsibility of caller to make sure that pcu_msg contains u.data_req. */ +function f_pcuif_tx_data_cnf(in PCUIF_Message pcu_msg) +runs on MS_BTS_IFACE_CT { + var PCUIF_Message pcu_msg_cnf := { + msg_type := PCU_IF_MSG_DATA_CNF, + bts_nr := pcu_msg.bts_nr, + spare := pcu_msg.spare, + u := { data_cnf := pcu_msg.u.data_req } + }; + + /* PCU wants DATA.cnf containing basically everything that was in DATA.req, + * but PCU_IF_SAPI_PCH is a special case - paging group shall be excluded. */ + if (pcu_msg.u.data_req.sapi == PCU_IF_SAPI_PCH) { + pcu_msg_cnf.u.data_cnf.data := substr(pcu_msg.u.data_req.data, 3, + pcu_msg.u.data_req.len - 3); + } + + BTS.send(pcu_msg_cnf); +} + +function f_pcuif_rx_imm_ass(out GsmRrMessage rr_imm_ass, + template PCUIF_Sapi sapi := PCU_IF_SAPI_AGCH, + template GsmRrMessage t_imm_ass := ?, + uint8_t bts_nr := 0) +runs on MS_BTS_IFACE_CT return boolean { + var PCUIF_Message pcu_msg; + var octetstring data; + timer T; + + T.start(2.0); + alt { + [] BTS.receive(tr_PCUIF_DATA_REQ(bts_nr := bts_nr, trx_nr := 0, ts_nr := 0, + sapi := sapi, data := ?)) -> value pcu_msg { + /* On PCH the payload is prefixed with paging group (3 octets): skip it. + * TODO: add an additional template parameter, so we can match it. */ + if (pcu_msg.u.data_req.sapi == PCU_IF_SAPI_PCH) { + data := substr(pcu_msg.u.data_req.data, 3, pcu_msg.u.data_req.len - 3); + } else { + data := pcu_msg.u.data_req.data; + } + + rr_imm_ass := dec_GsmRrMessage(data); + if (not match(rr_imm_ass, t_imm_ass)) { + /* Not for us? Wait for more. */ + repeat; + } + + log("Rx Immediate Assignment: ", rr_imm_ass); + setverdict(pass); + return true; + } + [] BTS.receive { repeat; } + [] T.timeout { + setverdict(fail, "Timeout waiting for Immediate Assignment"); + } + } + + return false; +} + +/* One phase packet access (see 3GPP TS 44.018, table 9.1.8.1) */ +const BIT8 chan_req_def := '01111000'B; + +/* Establish an Uplink TBF by sending RACH.ind towards the PCU */ +function f_establish_tbf(out GsmRrMessage rr_imm_ass, uint8_t bts_nr := 0, + uint16_t ra := bit2int(chan_req_def), + uint8_t is_11bit := 0, + PCUIF_BurstType burst_type := BURST_TYPE_0, + TimingAdvance ta := 0) +runs on MS_BTS_IFACE_CT return boolean { + var uint32_t fn; + + /* FIXME: ask the BTS component to give us the current TDMA fn */ + fn := 1337 + ta; + + /* Send RACH.ind */ + log("Sending RACH.ind on fn=", fn, " with RA=", ra, ", TA=", ta); + BTS.send(ts_PCUIF_RACH_IND(bts_nr := bts_nr, trx_nr := 0, ts_nr := 0, + ra := ra, is_11bit := is_11bit, + burst_type := burst_type, + fn := fn, arfcn := 871, + qta := ta * 4)); + + /* 3GPP TS 44.018, table 9.1.8.1, note 2b: Request Reference shall be set to 127 + * when Immediate Assignment is triggered by EGPRS Packet Channel Request. Here + * we assume that 11 bit RA always contains EGPRS Packet Channel Request. */ + if (is_11bit != 0) { ra := 127; } + + /* Expect Immediate (TBF) Assignment on TS0/AGCH */ + return f_pcuif_rx_imm_ass(rr_imm_ass, PCU_IF_SAPI_AGCH, + tr_IMM_TBF_ASS(false, ra, fn), + bts_nr := bts_nr); +} + +function f_imm_ass_verify_ul_tbf_ass(in GsmRrMessage rr_imm_ass, out PacketUlAssign ul_tbf_ass, template PacketUlAssign ul_ass := tr_PacketUlDynAssign) +runs on MS_BTS_IFACE_CT { + + /* Make sure we received an UL TBF Assignment */ + if (match(rr_imm_ass, tr_IMM_TBF_ASS(dl := false, rest := tr_IaRestOctets_ULAss(ul_ass)))) { + ul_tbf_ass := rr_imm_ass.payload.imm_ass.rest_octets.hh.pa.uldl.ass.ul; + log("Rx Uplink TBF assignment: ", ul_tbf_ass); + setverdict(pass); + } else { + setverdict(fail, "Failed to match UL TBF Assignment"); + f_shutdown(__BFILE__, __LINE__); + } +} + +function f_imm_ass_verify_dl_tbf_ass(in GsmRrMessage rr_imm_ass, out PacketDlAssign dl_tbf_ass) +runs on MS_BTS_IFACE_CT { + + /* Make sure we received a DL TBF Assignment */ + if (match(rr_imm_ass, tr_IMM_TBF_ASS(dl := true, rest := tr_IaRestOctets_DLAss(?)))) { + dl_tbf_ass := rr_imm_ass.payload.imm_ass.rest_octets.hh.pa.uldl.ass.dl; + log("Rx Downlink TBF assignment: ", dl_tbf_ass); + setverdict(pass); + } else { + setverdict(fail, "Failed to match DL TBF Assignment"); + f_shutdown(__BFILE__, __LINE__); + } +} + +/* Enqueue DATA.ind (both TDMA frame and block numbers to be patched) */ +function f_pcuif_tx_data_ind(octetstring data, int16_t lqual_cb := 0, uint32_t fn := 0) +runs on MS_BTS_IFACE_CT { + var template RAW_PCU_EventParam ev_param := {tdma_fn := ? }; + BTS.send(ts_PCUIF_DATA_IND(bts_nr := 0, trx_nr := 0, ts_nr := 7, block_nr := 0, + sapi := PCU_IF_SAPI_PDTCH, data := data, + fn := fn, arfcn := 871, lqual_cb := lqual_cb)); + if (fn != 0) { + ev_param := {tdma_fn := fn }; + } + BTS.receive(tr_RAW_PCU_EV(TDMA_EV_PDTCH_BLOCK_SENT, ev_param)); +} + +/* Enqueue RTS.req, expect DATA.req with UL ACK from the PCU */ +function f_pcuif_rx_data_req(out PCUIF_Message pcu_msg) +runs on MS_BTS_IFACE_CT { + BTS.send(ts_PCUIF_RTS_REQ(bts_nr := 0, trx_nr := 0, ts_nr := 7, + sapi := PCU_IF_SAPI_PDTCH, fn := 0, + arfcn := 871, block_nr := 0)); + BTS.receive(tr_PCUIF_DATA_REQ(bts_nr := 0, trx_nr := 0, ts_nr := 7, + sapi := PCU_IF_SAPI_PDTCH)) -> value pcu_msg; +} + +/* Expect an Immediate Assignment (paging) from PCU on PCUIF on specified sapi. */ +function f_pcuif_rx_pch_imm_tbf_ass(out GsmRrMessage rr_imm_ass) +runs on MS_BTS_IFACE_CT { + var PCUIF_Message pcu_msg; + var octetstring macblock; + BTS.receive(tr_PCUIF_DATA_REQ(bts_nr := 0, trx_nr := 0, ts_nr := 0, + sapi := PCU_IF_SAPI_PCH)) -> value pcu_msg; + /* First 3 bytes contain paging group: */ + macblock := substr(pcu_msg.u.data_req.data, 3, pcu_msg.u.data_req.len - 3); + rr_imm_ass := dec_GsmRrMessage(macblock); + if (not match(rr_imm_ass, tr_IMM_TBF_ASS())) { + setverdict(fail, "Failed to match Immediate Assignment: ", rr_imm_ass); + f_shutdown(__BFILE__, __LINE__); + } + f_pcuif_tx_data_cnf(pcu_msg); +} + +/* Expect a Paging Request Type 1 from PCU on PCUIF on specified sapi. */ +function f_pcuif_rx_pch_pag_req1(template MobileIdentityV mi1 := ?, + template integer pag_group := ?) +runs on MS_BTS_IFACE_CT return GsmRrMessage { + var GsmRrMessage rr_pag_req1; + var PCUIF_Message pcu_msg; + var octetstring imsi_suff_octstr; + var integer pag_group_rx; + var octetstring macblock; + + BTS.receive(tr_PCUIF_DATA_REQ(bts_nr := 0, trx_nr := 0, ts_nr := 0, + sapi := PCU_IF_SAPI_PCH)) -> value pcu_msg; + + /* First 3 bytes contain IMSI suffix to calculate paging group: */ + imsi_suff_octstr := substr(pcu_msg.u.data_req.data, 0, 3); + pag_group_rx := str2int(oct2char(imsi_suff_octstr[0])) * 100 + + str2int(oct2char(imsi_suff_octstr[1])) * 10 + + str2int(oct2char(imsi_suff_octstr[2])); + + /* Make sure we've got RR Paging Request Type 1 for a given MI */ + macblock := substr(pcu_msg.u.data_req.data, 3, pcu_msg.u.data_req.len - 3); + rr_pag_req1 := dec_GsmRrMessage(macblock); + if (not match(rr_pag_req1, tr_PAG_REQ1(tr_MI_LV(mi1)))) { + setverdict(fail, "Failed to match Paging Request Type 1: ", rr_pag_req1); + f_shutdown(__BFILE__, __LINE__); + } + + /* Make sure that received paging froup matches the expected one */ + if (not match(pag_group_rx, pag_group)) { + setverdict(fail, "Paging group", pag_group_rx, " does not match expected ", pag_group); + f_shutdown(__BFILE__, __LINE__); + } + + f_pcuif_tx_data_cnf(pcu_msg); + return rr_pag_req1; +} + +/* Send one rlcmac UL block adding necessary extra padding at the end. + * returns length of extra padding added at the end, in octets. + * FIXME: Only supports CS-1 so far. + */ +function f_tx_rlcmac_ul_block(template (value) RlcmacUlBlock ul_data, int16_t lqual_cb := 0, uint32_t fn := 0) +runs on MS_BTS_IFACE_CT return integer { + var octetstring data; + var integer padding_len; + /* Encode the payload of DATA.ind */ + data := enc_RlcmacUlBlock(valueof(ul_data)); + padding_len := 23 - lengthof(data); + data := f_pad_oct(data, 23, '00'O); /* CS-1 */ + + /* Enqueue DATA.ind (both TDMA frame and block numbers to be patched) */ + f_pcuif_tx_data_ind(data, lqual_cb, fn); + return padding_len; +} + +function f_tx_rlcmac_ul_n_blocks(uint5_t tfi, inout uint14_t bsn, integer num_blocks := 1, template (omit) GprsTlli tlli := omit) +runs on MS_BTS_IFACE_CT return octetstring { + var octetstring total_payload := ''O; + var template (value) RlcmacUlBlock ul_data := t_RLCMAC_UL_DATA( + tfi := tfi, + cv := 15, /* num UL blocks to be sent (to be overridden in loop) */ + bsn := 0, /* To be generated in loop */ + blocks := { /* To be generated in loop */ }); + + if (not istemplatekind(tlli, "omit")) { + ul_data.data.mac_hdr.tlli_ind := true; + ul_data.data.tlli := tlli; + } + + for (var integer i := 0; i < num_blocks; i := i + 1) { + var integer padding_len; + var octetstring payload := f_rnd_octstring(10); + /* Prepare a new UL block (CV, random payload) */ + var integer cv := num_blocks - i - 1; + if (cv > g_bs_cv_max) { + cv := 15; + } + ul_data.data.mac_hdr.countdown := cv; + ul_data.data.mac_hdr.bsn := bsn + i; + ul_data.data.blocks := { valueof(t_RLCMAC_LLCBLOCK(payload)) }; + padding_len := f_tx_rlcmac_ul_block(ul_data); + total_payload := total_payload & payload & f_pad_oct(''O, padding_len, '00'O); + } + bsn := valueof(ul_data.data.mac_hdr.bsn) + 1; /* update bsn to point to next one */ + return total_payload; +} + +function f_rx_rlcmac_dl_block(out RlcmacDlBlock dl_block, out uint32_t dl_fn, template (present) CodingScheme exp_cs_mcs := ?) +runs on MS_BTS_IFACE_CT { + var PCUIF_Message pcu_msg; + f_pcuif_rx_data_req(pcu_msg); + dl_block := dec_RlcmacDlBlock(pcu_msg.u.data_req.data); + dl_fn := pcu_msg.u.data_req.fn; + + var integer len := lengthof(pcu_msg.u.data_req.data); + var CodingScheme cs_mcs := f_rlcmac_block_len2cs_mcs(len) + if (not match(f_rlcmac_block_len2cs_mcs(len), exp_cs_mcs)) { + setverdict(fail, "Failed to match Coding Scheme exp ", exp_cs_mcs, " vs ", cs_mcs, " (", len, ")"); + f_shutdown(__BFILE__, __LINE__); + } +} + +function f_rx_rlcmac_dl_block_exp_ack_nack(out RlcmacDlBlock dl_block, out uint32_t poll_fn) +runs on MS_BTS_IFACE_CT { + var uint32_t dl_fn; + + f_rx_rlcmac_dl_block(dl_block, dl_fn); + if (not match(dl_block, tr_RLCMAC_UL_ACK_NACK(ul_tfi := ?, tlli := ?))) { + setverdict(fail, "Failed to match Packet Uplink ACK / NACK"); + f_shutdown(__BFILE__, __LINE__); + } + + poll_fn := f_rrbp_ack_fn(dl_fn, dl_block.ctrl.mac_hdr.rrbp); +} + +function f_rx_rlcmac_dl_block_exp_dummy(out RlcmacDlBlock dl_block) +runs on MS_BTS_IFACE_CT { + var uint32_t dl_fn; + + f_rx_rlcmac_dl_block(dl_block, dl_fn); + if (not match(dl_block, tr_RLCMAC_DUMMY_CTRL())) { + setverdict(fail, "Failed to match Packet DUMMY DL"); + f_shutdown(__BFILE__, __LINE__); + } +} + +function f_rx_rlcmac_dl_block_exp_pkt_ass(out RlcmacDlBlock dl_block, out uint32_t poll_fn) +runs on MS_BTS_IFACE_CT { + var uint32_t dl_fn; + + f_rx_rlcmac_dl_block(dl_block, dl_fn); + if (not match(dl_block, tr_RLCMAC_DL_PACKET_ASS())) { + setverdict(fail, "Failed to match Packet Downlink Assignment"); + f_shutdown(__BFILE__, __LINE__); + } + + poll_fn := f_rrbp_ack_fn(dl_fn, dl_block.ctrl.mac_hdr.rrbp); +} + +function f_rx_rlcmac_dl_block_exp_pkt_ul_ass(out RlcmacDlBlock dl_block, out uint32_t poll_fn) +runs on MS_BTS_IFACE_CT { + var uint32_t dl_fn; + + f_rx_rlcmac_dl_block(dl_block, dl_fn); + if (not match(dl_block, tr_RLCMAC_UL_PACKET_ASS())) { + setverdict(fail, "Failed to match Packet Uplink Assignment"); + f_shutdown(__BFILE__, __LINE__); + } + + poll_fn := f_rrbp_ack_fn(dl_fn, dl_block.ctrl.mac_hdr.rrbp); +} + +function f_rx_rlcmac_dl_block_exp_pkt_dl_ass(out RlcmacDlBlock dl_block, out uint32_t poll_fn) +runs on MS_BTS_IFACE_CT { + var uint32_t dl_fn; + + f_rx_rlcmac_dl_block(dl_block, dl_fn); + if (not match(dl_block, tr_RLCMAC_DL_PACKET_ASS())) { + setverdict(fail, "Failed to match Packet Downlink Assignment"); + f_shutdown(__BFILE__, __LINE__); + } + + poll_fn := f_rrbp_ack_fn(dl_fn, dl_block.ctrl.mac_hdr.rrbp); +} + + +function f_rx_rlcmac_dl_block_exp_pkt_pag_req(out RlcmacDlBlock dl_block) +runs on MS_BTS_IFACE_CT { + var uint32_t dl_fn; + + f_rx_rlcmac_dl_block(dl_block, dl_fn); + if (not match(dl_block, tr_RLCMAC_PACKET_PAG_REQ())) { + setverdict(fail, "Failed to match Packet Paging Request: ", dl_block, " vs ", tr_RLCMAC_PACKET_PAG_REQ()); + f_shutdown(__BFILE__, __LINE__); + } +} + +/* This function does what could probably be done with templates */ +function f_rlcmac_dl_block_verify_data_gprs(in RlcmacDlDataBlock data_block, + template (present) octetstring data := ?, + template (present) uint7_t exp_bsn := ?, + template (present) CodingScheme exp_cs := ?) +runs on MS_BTS_IFACE_CT { + if (not match(data_block.mac_hdr.hdr_ext.bsn, exp_bsn)) { + setverdict(fail, "DL block BSN doesn't match: ", + data_block.mac_hdr.hdr_ext.bsn, " vs exp ", exp_bsn); + } + + if (lengthof(data_block.blocks) < 1) { + setverdict(fail, "DL block has no LLC payload: ", data_block); + f_shutdown(__BFILE__, __LINE__); + } + + if (not match(data_block.blocks[0].payload, data)) { + setverdict(fail, "Failed to match content of LLC payload in DL Block: ", + data_block.blocks[0].payload, " vs ", data); + f_shutdown(__BFILE__, __LINE__); + } + + /* Check next data blocks contain dummy frames */ + if (lengthof(data_block.blocks) > 1 and substr(data_block.blocks[1].payload, 0, 3) != '43C001'O) { + setverdict(fail, "Second data payload is not a dummy frame: ", + data_block.blocks[1].payload); + f_shutdown(__BFILE__, __LINE__); + } + + /* TODO: check exp_cs */ +} + +/* This function does what could probably be done with templates */ +function f_rlcmac_dl_block_verify_data_egprs(in RlcmacDlEgprsDataBlock data_block, + template (present) octetstring data := ?, + template (present) uint14_t exp_bsn := ?, + template (present) CodingScheme exp_cs := ?) +runs on MS_BTS_IFACE_CT { + if (not match(data_block.mac_hdr.bsn1, exp_bsn)) { + setverdict(fail, "DL block BSN doesn't match: ", + data_block.mac_hdr.bsn1, " vs exp ", exp_bsn); + } + + if (lengthof(data_block.blocks) < 1) { + setverdict(fail, "DL block has no LLC payload: ", data_block); + f_shutdown(__BFILE__, __LINE__); + } + + if (not match(data_block.blocks[0].payload, data)) { + setverdict(fail, "Failed to match content of LLC payload in DL Block: ", + data_block.blocks[0].payload, " vs ", data); + f_shutdown(__BFILE__, __LINE__); + } + + /* Check next data blocks contain dummy frames */ + if (lengthof(data_block.blocks) > 1 and substr(data_block.blocks[1].payload, 0, 3) != '43C001'O) { + setverdict(fail, "Second data payload is not a dummy frame: ", + data_block.blocks[1].payload); + f_shutdown(__BFILE__, __LINE__); + } + + /* TODO: Check exp_cs. In the case of EGPRS, first check mac_hdr.header_type and then decode CPS = exp_cs based on mac_hdr.header_type. + See wireshark's egprs_Header_type1_coding_puncturing_scheme_to_mcs. */ +} + +/* High level (task specific) helper for receiving and matching GPRS/EGPRS data blocks */ +function f_rx_rlcmac_dl_block_exp_data(out RlcmacDlBlock dl_block, out uint32_t dl_fn, + template (present) octetstring data := ?, + template (present) uint7_t exp_bsn := ?, + template (present) CodingScheme exp_cs := ?) +runs on MS_BTS_IFACE_CT { + /* FIXME: ideally we should use an alt statement with timeout here, rather than + * having +100500 layers of abstraction. This would facilitate developing the + * multi-TBF/-TRX/-BTS tests, where you cannot expect that the first received + * block is exactly what you need. */ + f_rx_rlcmac_dl_block(dl_block, dl_fn); + + /* Make sure it's either GPRS or EGPRS data block */ + if (not match(dl_block, tr_RLCMAC_DATA)) { + setverdict(fail, "Failed to match DL DATA: ", dl_block, " vs ", tr_RLCMAC_DATA); + f_shutdown(__BFILE__, __LINE__); + } + + if (ischosen(dl_block.data_egprs)) { + f_rlcmac_dl_block_verify_data_egprs(dl_block.data_egprs, data, exp_bsn, exp_cs); + } else if (ischosen(dl_block.data)) { + f_rlcmac_dl_block_verify_data_gprs(dl_block.data, data, exp_bsn, exp_cs); + } else { + /* Should not happen, but the caller may theoretically give us a template for CTRL */ + setverdict(fail, "DL block is neither GPRS nor EGPRS data block: ", dl_block); + f_shutdown(__BFILE__, __LINE__); + } +} + +function f_dl_block_ack_fn(in RlcmacDlBlock dl_block, uint32_t dl_fn) +runs on MS_BTS_IFACE_CT return uint32_t { + var boolean rrbp_valid; + var MacRrbp rrbp; + + /* The argument must be either a GPRS or EGPRS data block */ + if (ischosen(dl_block.data_egprs)) { + rrbp_valid := true; /* always valid */ + rrbp := dl_block.data_egprs.mac_hdr.rrbp; + } else if (ischosen(dl_block.data)) { + rrbp_valid := dl_block.data.mac_hdr.mac_hdr.rrbp_valid; + rrbp := dl_block.data.mac_hdr.mac_hdr.rrbp; + } else { + rrbp_valid := dl_block.ctrl.mac_hdr.rrbp_valid; + rrbp := dl_block.ctrl.mac_hdr.rrbp; + } + + /* Make sure that the given block really needs to be ACKnowledged */ + if (not rrbp_valid) { + setverdict(fail, "DL block shall not be ACKnowledged, field RRBP is not valid"); + f_shutdown(__BFILE__, __LINE__); + } + + return f_rrbp_ack_fn(dl_fn, rrbp); +} + +function f_pkt_paging_match_tmsi(in PacketPagingReq req, template GsmTmsi tmsi) +runs on MS_BTS_IFACE_CT { + if (not match(req.repeated_pageinfo.cs.tmsi, tmsi)) { + setverdict(fail, "Mobile Identity (TMSI/P-TMSI) mismatch: ", + "expected: ", tmsi, "got: ", req.repeated_pageinfo.cs.tmsi); + f_shutdown(__BFILE__, __LINE__); + } +} + +} diff --git a/pcu/PCU_Tests.ttcn b/pcu/PCU_Tests.ttcn index 43ed7c5d2..51933694b 100644 --- a/pcu/PCU_Tests.ttcn +++ b/pcu/PCU_Tests.ttcn @@ -49,6 +49,7 @@ import from PCUIF_Components all; import from IPL4asp_Types all; import from Native_Functions all; import from SGSN_Components all; +import from GPRS_Components all; modulepar { charstring mp_pcu_sock_path := PCU_SOCK_DEFAULT; @@ -100,14 +101,10 @@ type record lqual_range { uint8_t high } -type component RAW_PCU_Test_CT extends bssgp_CT { +type component RAW_PCU_Test_CT extends bssgp_CT, MS_BTS_IFACE_CT { /* PCU interface abstraction component */ var RAW_PCUIF_CT vc_PCUIF; - /* Virtual BTS component */ - var RAW_PCU_BTS_CT vc_BTS; - /* Connection to the BTS component (one for now) */ - port RAW_PCU_MSG_PT BTS; /* Connection to the PCUIF component */ port RAW_PCU_MSG_PT PCUIF; /* VTY connection to the PCU */ @@ -136,9 +133,6 @@ type component RAW_PCU_Test_CT extends bssgp_CT { var uint8_t g_mcs_max_dl := 9; var uint8_t g_mcs_max_ul := 9; - /* Value at which Countdown Procedure starts. Announced by network (GPRS Cell Options as per TS 04.60 Chapter 12.24) */ - var uint4_t g_bs_cv_max := 4; - var boolean g_egprs_only := false; var boolean g_force_two_phase_access := false; @@ -227,560 +221,6 @@ runs on RAW_PCU_Test_CT { BTS.receive(tr_RAW_PCU_EV(BTS_EV_SI13_NEGO)); } -private function f_shutdown(charstring file, integer line, - boolean final := false) -runs on RAW_PCU_Test_CT { - /* Determine if the test case was aborted in the middle */ - if (not final) { - log("Test case ", testcasename(), " aborted at ", file, ":", line); - } else { - /* Guard verdict to avoid 'none' */ - setverdict(pass); - } - - /* Properly shutdown virtual BTS and its clock generator */ - BTS.send(ts_RAW_PCU_CMD(GENERAL_CMD_SHUTDOWN)); - vc_BTS.done; /* wait untill it's done */ - - /* Shutdown the others and MTC */ - all component.stop; - mtc.stop; -} - -template AckNackDescription t_AckNackDescription_init := { - final_ack := '0'B, - starting_seq_nr := 0, - receive_block_bitmap := '0000000000000000000000000000000000000000000000000000000000000000'B -} - -private function f_rlcmac_dl_block_get_tfi(RlcmacDlBlock dl_block) -runs on RAW_PCU_Test_CT return uint5_t { - if (ischosen(dl_block.data)) { - return dl_block.data.mac_hdr.hdr_ext.tfi; - } else if (ischosen(dl_block.data_egprs)) { - return dl_block.data_egprs.mac_hdr.tfi; - } else { /* Ctrl block */ - if (match(dl_block, tr_RLCMAC_UL_PACKET_ASS_GPRS(?, tr_PktUlAssGprsDynamic(tr_DynamicAllocation(?))))) { - return dl_block.ctrl.payload.u.ul_assignment.gprs.dyn_block_alloc.ul_tfi_assignment; - } - if (match(dl_block, tr_RLCMAC_UL_PACKET_ASS_EGPRS(?, tr_PktUlAssEgprsDynamic(tr_DynamicAllocation(?))))) { - return dl_block.ctrl.payload.u.ul_assignment.egprs.dyn_block_alloc.ul_tfi_assignment; - } - } - setverdict(fail, "DlBlock doesn't contain a TFI:", dl_block); - f_shutdown(__BFILE__, __LINE__); - return 0; /* make compiler happy */ -} - -/* Get the Chan coding command from a dl block containing PACCH UL Assignment */ -private function f_rlcmac_dl_block_get_assigned_ul_cs_mcs(RlcmacDlBlock dl_block) -runs on RAW_PCU_Test_CT return CodingScheme { - if (match(dl_block, tr_RLCMAC_UL_PACKET_ASS_GPRS(?, tr_PktUlAssGprsDynamic(?)))) { - return f_rlcmac_block_ChCodingCommand2cs_mcs(dl_block.ctrl.payload.u.ul_assignment.gprs.ch_coding_cmd); - } - if (match(dl_block, tr_RLCMAC_UL_PACKET_ASS_EGPRS(?, tr_PktUlAssEgprsDynamic(?)))) { - return f_rlcmac_block_EgprsChCodingCommand2cs_mcs(dl_block.ctrl.payload.u.ul_assignment.egprs.chan_coding_cmd); - } - setverdict(fail, "DlBlock doesn't contain CS_MCS information:", dl_block); - f_shutdown(__BFILE__, __LINE__); - return CS_1; /* make compiler happy */ -} - -/* TS 44.060 sec 12.3 Ack/Nack Description */ -private function f_acknackdesc_ack_block(inout AckNackDescription desc, RlcmacDlBlock dl_block, BIT1 final_ack := '0'B) -{ - var uint7_t bsn; - var integer i; - var integer inc; - - if (ischosen(dl_block.data)) { - bsn := dl_block.data.mac_hdr.hdr_ext.bsn; - } else { - bsn := dl_block.data_egprs.mac_hdr.bsn1; - } - - inc := bsn - desc.starting_seq_nr + 1; - /* Filling hole? */ - if (bsn < desc.starting_seq_nr) { - desc.receive_block_bitmap[lengthof(desc.receive_block_bitmap) - (desc.starting_seq_nr - bsn)] := int2bit(1, 1); - return; - } - - /* SSN is increased, and so RBB values need to be moved */ - for (i := 0; i < lengthof(desc.receive_block_bitmap) - inc; i := i+1) { - desc.receive_block_bitmap[i] := desc.receive_block_bitmap[i + inc]; - } - for (i := lengthof(desc.receive_block_bitmap) - inc; i < lengthof(desc.receive_block_bitmap) - 1; i := i+1) { - desc.receive_block_bitmap[i] := int2bit(0, 1); - } - /* Now we can set current bit and update SSN */ - desc.starting_seq_nr := bsn + 1; - desc.receive_block_bitmap[lengthof(desc.receive_block_bitmap) - 1] := int2bit(1, 1); - - /* Finally update the final_ack bit as requested: */ - desc.final_ack := final_ack; -} - -/* This function can be used to send DATA.cnf in response to the IUT originated DATA.req. - * NOTE: it's the responsibility of caller to make sure that pcu_msg contains u.data_req. */ -private function f_pcuif_tx_data_cnf(in PCUIF_Message pcu_msg) -runs on RAW_PCU_Test_CT { - var PCUIF_Message pcu_msg_cnf := { - msg_type := PCU_IF_MSG_DATA_CNF, - bts_nr := pcu_msg.bts_nr, - spare := pcu_msg.spare, - u := { data_cnf := pcu_msg.u.data_req } - }; - - /* PCU wants DATA.cnf containing basically everything that was in DATA.req, - * but PCU_IF_SAPI_PCH is a special case - paging group shall be excluded. */ - if (pcu_msg.u.data_req.sapi == PCU_IF_SAPI_PCH) { - pcu_msg_cnf.u.data_cnf.data := substr(pcu_msg.u.data_req.data, 3, - pcu_msg.u.data_req.len - 3); - } - - BTS.send(pcu_msg_cnf); -} - -private function f_pcuif_rx_imm_ass(out GsmRrMessage rr_imm_ass, - template PCUIF_Sapi sapi := PCU_IF_SAPI_AGCH, - template GsmRrMessage t_imm_ass := ?, - uint8_t bts_nr := 0) -runs on RAW_PCU_Test_CT return boolean { - var PCUIF_Message pcu_msg; - var octetstring data; - timer T; - - T.start(2.0); - alt { - [] BTS.receive(tr_PCUIF_DATA_REQ(bts_nr := bts_nr, trx_nr := 0, ts_nr := 0, - sapi := sapi, data := ?)) -> value pcu_msg { - /* On PCH the payload is prefixed with paging group (3 octets): skip it. - * TODO: add an additional template parameter, so we can match it. */ - if (pcu_msg.u.data_req.sapi == PCU_IF_SAPI_PCH) { - data := substr(pcu_msg.u.data_req.data, 3, pcu_msg.u.data_req.len - 3); - } else { - data := pcu_msg.u.data_req.data; - } - - rr_imm_ass := dec_GsmRrMessage(data); - if (not match(rr_imm_ass, t_imm_ass)) { - /* Not for us? Wait for more. */ - repeat; - } - - log("Rx Immediate Assignment: ", rr_imm_ass); - setverdict(pass); - return true; - } - [] BTS.receive { repeat; } - [] T.timeout { - setverdict(fail, "Timeout waiting for Immediate Assignment"); - } - } - - return false; -} - -/* One phase packet access (see 3GPP TS 44.018, table 9.1.8.1) */ -private const BIT8 chan_req_def := '01111000'B; - -/* Establish an Uplink TBF by sending RACH.ind towards the PCU */ -private function f_establish_tbf(out GsmRrMessage rr_imm_ass, uint8_t bts_nr := 0, - uint16_t ra := bit2int(chan_req_def), - uint8_t is_11bit := 0, - PCUIF_BurstType burst_type := BURST_TYPE_0, - TimingAdvance ta := 0) -runs on RAW_PCU_Test_CT return boolean { - var uint32_t fn; - - /* FIXME: ask the BTS component to give us the current TDMA fn */ - fn := 1337 + ta; - - /* Send RACH.ind */ - log("Sending RACH.ind on fn=", fn, " with RA=", ra, ", TA=", ta); - BTS.send(ts_PCUIF_RACH_IND(bts_nr := bts_nr, trx_nr := 0, ts_nr := 0, - ra := ra, is_11bit := is_11bit, - burst_type := burst_type, - fn := fn, arfcn := 871, - qta := ta * 4)); - - /* 3GPP TS 44.018, table 9.1.8.1, note 2b: Request Reference shall be set to 127 - * when Immediate Assignment is triggered by EGPRS Packet Channel Request. Here - * we assume that 11 bit RA always contains EGPRS Packet Channel Request. */ - if (is_11bit != 0) { ra := 127; } - - /* Expect Immediate (TBF) Assignment on TS0/AGCH */ - return f_pcuif_rx_imm_ass(rr_imm_ass, PCU_IF_SAPI_AGCH, - tr_IMM_TBF_ASS(false, ra, fn), - bts_nr := bts_nr); -} - -private function f_imm_ass_verify_ul_tbf_ass(in GsmRrMessage rr_imm_ass, out PacketUlAssign ul_tbf_ass, template PacketUlAssign ul_ass := tr_PacketUlDynAssign) -runs on RAW_PCU_Test_CT { - - /* Make sure we received an UL TBF Assignment */ - if (match(rr_imm_ass, tr_IMM_TBF_ASS(dl := false, rest := tr_IaRestOctets_ULAss(ul_ass)))) { - ul_tbf_ass := rr_imm_ass.payload.imm_ass.rest_octets.hh.pa.uldl.ass.ul; - log("Rx Uplink TBF assignment: ", ul_tbf_ass); - setverdict(pass); - } else { - setverdict(fail, "Failed to match UL TBF Assignment"); - f_shutdown(__BFILE__, __LINE__); - } -} - -private function f_imm_ass_verify_dl_tbf_ass(in GsmRrMessage rr_imm_ass, out PacketDlAssign dl_tbf_ass) -runs on RAW_PCU_Test_CT { - - /* Make sure we received a DL TBF Assignment */ - if (match(rr_imm_ass, tr_IMM_TBF_ASS(dl := true, rest := tr_IaRestOctets_DLAss(?)))) { - dl_tbf_ass := rr_imm_ass.payload.imm_ass.rest_octets.hh.pa.uldl.ass.dl; - log("Rx Downlink TBF assignment: ", dl_tbf_ass); - setverdict(pass); - } else { - setverdict(fail, "Failed to match DL TBF Assignment"); - f_shutdown(__BFILE__, __LINE__); - } -} - -/* Enqueue DATA.ind (both TDMA frame and block numbers to be patched) */ -function f_pcuif_tx_data_ind(octetstring data, int16_t lqual_cb := 0, uint32_t fn := 0) -runs on RAW_PCU_Test_CT { - var template RAW_PCU_EventParam ev_param := {tdma_fn := ? }; - BTS.send(ts_PCUIF_DATA_IND(bts_nr := 0, trx_nr := 0, ts_nr := 7, block_nr := 0, - sapi := PCU_IF_SAPI_PDTCH, data := data, - fn := fn, arfcn := 871, lqual_cb := lqual_cb)); - if (fn != 0) { - ev_param := {tdma_fn := fn }; - } - BTS.receive(tr_RAW_PCU_EV(TDMA_EV_PDTCH_BLOCK_SENT, ev_param)); -} - -/* Enqueue RTS.req, expect DATA.req with UL ACK from the PCU */ -private function f_pcuif_rx_data_req(out PCUIF_Message pcu_msg) -runs on RAW_PCU_Test_CT { - BTS.send(ts_PCUIF_RTS_REQ(bts_nr := 0, trx_nr := 0, ts_nr := 7, - sapi := PCU_IF_SAPI_PDTCH, fn := 0, - arfcn := 871, block_nr := 0)); - BTS.receive(tr_PCUIF_DATA_REQ(bts_nr := 0, trx_nr := 0, ts_nr := 7, - sapi := PCU_IF_SAPI_PDTCH)) -> value pcu_msg; -} - -/* Expect an Immediate Assignment (paging) from PCU on PCUIF on specified sapi. */ -private function f_pcuif_rx_pch_imm_tbf_ass(out GsmRrMessage rr_imm_ass) -runs on RAW_PCU_Test_CT { - var PCUIF_Message pcu_msg; - var octetstring macblock; - BTS.receive(tr_PCUIF_DATA_REQ(bts_nr := 0, trx_nr := 0, ts_nr := 0, - sapi := PCU_IF_SAPI_PCH)) -> value pcu_msg; - /* First 3 bytes contain paging group: */ - macblock := substr(pcu_msg.u.data_req.data, 3, pcu_msg.u.data_req.len - 3); - rr_imm_ass := dec_GsmRrMessage(macblock); - if (not match(rr_imm_ass, tr_IMM_TBF_ASS())) { - setverdict(fail, "Failed to match Immediate Assignment: ", rr_imm_ass); - f_shutdown(__BFILE__, __LINE__); - } - f_pcuif_tx_data_cnf(pcu_msg); -} - -/* Expect a Paging Request Type 1 from PCU on PCUIF on specified sapi. */ -private function f_pcuif_rx_pch_pag_req1(template MobileIdentityV mi1 := ?, - template integer pag_group := ?) -runs on RAW_PCU_Test_CT return GsmRrMessage { - var GsmRrMessage rr_pag_req1; - var PCUIF_Message pcu_msg; - var octetstring imsi_suff_octstr; - var integer pag_group_rx; - var octetstring macblock; - - BTS.receive(tr_PCUIF_DATA_REQ(bts_nr := 0, trx_nr := 0, ts_nr := 0, - sapi := PCU_IF_SAPI_PCH)) -> value pcu_msg; - - /* First 3 bytes contain IMSI suffix to calculate paging group: */ - imsi_suff_octstr := substr(pcu_msg.u.data_req.data, 0, 3); - pag_group_rx := str2int(oct2char(imsi_suff_octstr[0])) * 100 + - str2int(oct2char(imsi_suff_octstr[1])) * 10 + - str2int(oct2char(imsi_suff_octstr[2])); - - /* Make sure we've got RR Paging Request Type 1 for a given MI */ - macblock := substr(pcu_msg.u.data_req.data, 3, pcu_msg.u.data_req.len - 3); - rr_pag_req1 := dec_GsmRrMessage(macblock); - if (not match(rr_pag_req1, tr_PAG_REQ1(tr_MI_LV(mi1)))) { - setverdict(fail, "Failed to match Paging Request Type 1: ", rr_pag_req1); - f_shutdown(__BFILE__, __LINE__); - } - - /* Make sure that received paging froup matches the expected one */ - if (not match(pag_group_rx, pag_group)) { - setverdict(fail, "Paging group", pag_group_rx, " does not match expected ", pag_group); - f_shutdown(__BFILE__, __LINE__); - } - - f_pcuif_tx_data_cnf(pcu_msg); - return rr_pag_req1; -} - -/* Send one rlcmac UL block adding necessary extra padding at the end. - * returns length of extra padding added at the end, in octets. - * FIXME: Only supports CS-1 so far. - */ -private function f_tx_rlcmac_ul_block(template (value) RlcmacUlBlock ul_data, int16_t lqual_cb := 0, uint32_t fn := 0) -runs on RAW_PCU_Test_CT return integer { - var octetstring data; - var integer padding_len; - /* Encode the payload of DATA.ind */ - data := enc_RlcmacUlBlock(valueof(ul_data)); - padding_len := 23 - lengthof(data); - data := f_pad_oct(data, 23, '00'O); /* CS-1 */ - - /* Enqueue DATA.ind (both TDMA frame and block numbers to be patched) */ - f_pcuif_tx_data_ind(data, lqual_cb, fn); - return padding_len; -} - -private function f_tx_rlcmac_ul_n_blocks(uint5_t tfi, inout uint14_t bsn, integer num_blocks := 1, template (omit) GprsTlli tlli := omit) -runs on RAW_PCU_Test_CT return octetstring { - var octetstring total_payload := ''O; - var template (value) RlcmacUlBlock ul_data := t_RLCMAC_UL_DATA( - tfi := tfi, - cv := 15, /* num UL blocks to be sent (to be overridden in loop) */ - bsn := 0, /* To be generated in loop */ - blocks := { /* To be generated in loop */ }); - - if (not istemplatekind(tlli, "omit")) { - ul_data.data.mac_hdr.tlli_ind := true; - ul_data.data.tlli := tlli; - } - - for (var integer i := 0; i < num_blocks; i := i + 1) { - var integer padding_len; - var octetstring payload := f_rnd_octstring(10); - /* Prepare a new UL block (CV, random payload) */ - var integer cv := num_blocks - i - 1; - if (cv > g_bs_cv_max) { - cv := 15; - } - ul_data.data.mac_hdr.countdown := cv; - ul_data.data.mac_hdr.bsn := bsn + i; - ul_data.data.blocks := { valueof(t_RLCMAC_LLCBLOCK(payload)) }; - padding_len := f_tx_rlcmac_ul_block(ul_data); - total_payload := total_payload & payload & f_pad_oct(''O, padding_len, '00'O); - } - bsn := valueof(ul_data.data.mac_hdr.bsn) + 1; /* update bsn to point to next one */ - return total_payload; -} - -private function f_rx_rlcmac_dl_block(out RlcmacDlBlock dl_block, out uint32_t dl_fn, template (present) CodingScheme exp_cs_mcs := ?) -runs on RAW_PCU_Test_CT { - var PCUIF_Message pcu_msg; - f_pcuif_rx_data_req(pcu_msg); - dl_block := dec_RlcmacDlBlock(pcu_msg.u.data_req.data); - dl_fn := pcu_msg.u.data_req.fn; - - var integer len := lengthof(pcu_msg.u.data_req.data); - var CodingScheme cs_mcs := f_rlcmac_block_len2cs_mcs(len) - if (not match(f_rlcmac_block_len2cs_mcs(len), exp_cs_mcs)) { - setverdict(fail, "Failed to match Coding Scheme exp ", exp_cs_mcs, " vs ", cs_mcs, " (", len, ")"); - f_shutdown(__BFILE__, __LINE__); - } -} - -private function f_rx_rlcmac_dl_block_exp_ack_nack(out RlcmacDlBlock dl_block, out uint32_t poll_fn) -runs on RAW_PCU_Test_CT { - var uint32_t dl_fn; - - f_rx_rlcmac_dl_block(dl_block, dl_fn); - if (not match(dl_block, tr_RLCMAC_UL_ACK_NACK(ul_tfi := ?, tlli := ?))) { - setverdict(fail, "Failed to match Packet Uplink ACK / NACK"); - f_shutdown(__BFILE__, __LINE__); - } - - poll_fn := f_rrbp_ack_fn(dl_fn, dl_block.ctrl.mac_hdr.rrbp); -} - -private function f_rx_rlcmac_dl_block_exp_dummy(out RlcmacDlBlock dl_block) -runs on RAW_PCU_Test_CT { - var uint32_t dl_fn; - - f_rx_rlcmac_dl_block(dl_block, dl_fn); - if (not match(dl_block, tr_RLCMAC_DUMMY_CTRL())) { - setverdict(fail, "Failed to match Packet DUMMY DL"); - f_shutdown(__BFILE__, __LINE__); - } -} - -private function f_rx_rlcmac_dl_block_exp_pkt_ass(out RlcmacDlBlock dl_block, out uint32_t poll_fn) -runs on RAW_PCU_Test_CT { - var uint32_t dl_fn; - - f_rx_rlcmac_dl_block(dl_block, dl_fn); - if (not match(dl_block, tr_RLCMAC_DL_PACKET_ASS())) { - setverdict(fail, "Failed to match Packet Downlink Assignment"); - f_shutdown(__BFILE__, __LINE__); - } - - poll_fn := f_rrbp_ack_fn(dl_fn, dl_block.ctrl.mac_hdr.rrbp); -} - -private function f_rx_rlcmac_dl_block_exp_pkt_ul_ass(out RlcmacDlBlock dl_block, out uint32_t poll_fn) -runs on RAW_PCU_Test_CT { - var uint32_t dl_fn; - - f_rx_rlcmac_dl_block(dl_block, dl_fn); - if (not match(dl_block, tr_RLCMAC_UL_PACKET_ASS())) { - setverdict(fail, "Failed to match Packet Uplink Assignment"); - f_shutdown(__BFILE__, __LINE__); - } - - poll_fn := f_rrbp_ack_fn(dl_fn, dl_block.ctrl.mac_hdr.rrbp); -} - -private function f_rx_rlcmac_dl_block_exp_pkt_dl_ass(out RlcmacDlBlock dl_block, out uint32_t poll_fn) -runs on RAW_PCU_Test_CT { - var uint32_t dl_fn; - - f_rx_rlcmac_dl_block(dl_block, dl_fn); - if (not match(dl_block, tr_RLCMAC_DL_PACKET_ASS())) { - setverdict(fail, "Failed to match Packet Downlink Assignment"); - f_shutdown(__BFILE__, __LINE__); - } - - poll_fn := f_rrbp_ack_fn(dl_fn, dl_block.ctrl.mac_hdr.rrbp); -} - - -private function f_rx_rlcmac_dl_block_exp_pkt_pag_req(out RlcmacDlBlock dl_block) -runs on RAW_PCU_Test_CT { - var uint32_t dl_fn; - - f_rx_rlcmac_dl_block(dl_block, dl_fn); - if (not match(dl_block, tr_RLCMAC_PACKET_PAG_REQ())) { - setverdict(fail, "Failed to match Packet Paging Request: ", dl_block, " vs ", tr_RLCMAC_PACKET_PAG_REQ()); - f_shutdown(__BFILE__, __LINE__); - } -} - -/* This function does what could probably be done with templates */ -private function f_rlcmac_dl_block_verify_data_gprs(in RlcmacDlDataBlock data_block, - template (present) octetstring data := ?, - template (present) uint7_t exp_bsn := ?, - template (present) CodingScheme exp_cs := ?) -runs on RAW_PCU_Test_CT { - if (not match(data_block.mac_hdr.hdr_ext.bsn, exp_bsn)) { - setverdict(fail, "DL block BSN doesn't match: ", - data_block.mac_hdr.hdr_ext.bsn, " vs exp ", exp_bsn); - } - - if (lengthof(data_block.blocks) < 1) { - setverdict(fail, "DL block has no LLC payload: ", data_block); - f_shutdown(__BFILE__, __LINE__); - } - - if (not match(data_block.blocks[0].payload, data)) { - setverdict(fail, "Failed to match content of LLC payload in DL Block: ", - data_block.blocks[0].payload, " vs ", data); - f_shutdown(__BFILE__, __LINE__); - } - - /* Check next data blocks contain dummy frames */ - if (lengthof(data_block.blocks) > 1 and substr(data_block.blocks[1].payload, 0, 3) != '43C001'O) { - setverdict(fail, "Second data payload is not a dummy frame: ", - data_block.blocks[1].payload); - f_shutdown(__BFILE__, __LINE__); - } - - /* TODO: check exp_cs */ -} - -/* This function does what could probably be done with templates */ -private function f_rlcmac_dl_block_verify_data_egprs(in RlcmacDlEgprsDataBlock data_block, - template (present) octetstring data := ?, - template (present) uint14_t exp_bsn := ?, - template (present) CodingScheme exp_cs := ?) -runs on RAW_PCU_Test_CT { - if (not match(data_block.mac_hdr.bsn1, exp_bsn)) { - setverdict(fail, "DL block BSN doesn't match: ", - data_block.mac_hdr.bsn1, " vs exp ", exp_bsn); - } - - if (lengthof(data_block.blocks) < 1) { - setverdict(fail, "DL block has no LLC payload: ", data_block); - f_shutdown(__BFILE__, __LINE__); - } - - if (not match(data_block.blocks[0].payload, data)) { - setverdict(fail, "Failed to match content of LLC payload in DL Block: ", - data_block.blocks[0].payload, " vs ", data); - f_shutdown(__BFILE__, __LINE__); - } - - /* Check next data blocks contain dummy frames */ - if (lengthof(data_block.blocks) > 1 and substr(data_block.blocks[1].payload, 0, 3) != '43C001'O) { - setverdict(fail, "Second data payload is not a dummy frame: ", - data_block.blocks[1].payload); - f_shutdown(__BFILE__, __LINE__); - } - - /* TODO: Check exp_cs. In the case of EGPRS, first check mac_hdr.header_type and then decode CPS = exp_cs based on mac_hdr.header_type. - See wireshark's egprs_Header_type1_coding_puncturing_scheme_to_mcs. */ -} - -/* High level (task specific) helper for receiving and matching GPRS/EGPRS data blocks */ -private function f_rx_rlcmac_dl_block_exp_data(out RlcmacDlBlock dl_block, out uint32_t dl_fn, - template (present) octetstring data := ?, - template (present) uint7_t exp_bsn := ?, - template (present) CodingScheme exp_cs := ?) -runs on RAW_PCU_Test_CT { - /* FIXME: ideally we should use an alt statement with timeout here, rather than - * having +100500 layers of abstraction. This would facilitate developing the - * multi-TBF/-TRX/-BTS tests, where you cannot expect that the first received - * block is exactly what you need. */ - f_rx_rlcmac_dl_block(dl_block, dl_fn); - - /* Make sure it's either GPRS or EGPRS data block */ - if (not match(dl_block, tr_RLCMAC_DATA)) { - setverdict(fail, "Failed to match DL DATA: ", dl_block, " vs ", tr_RLCMAC_DATA); - f_shutdown(__BFILE__, __LINE__); - } - - if (ischosen(dl_block.data_egprs)) { - f_rlcmac_dl_block_verify_data_egprs(dl_block.data_egprs, data, exp_bsn, exp_cs); - } else if (ischosen(dl_block.data)) { - f_rlcmac_dl_block_verify_data_gprs(dl_block.data, data, exp_bsn, exp_cs); - } else { - /* Should not happen, but the caller may theoretically give us a template for CTRL */ - setverdict(fail, "DL block is neither GPRS nor EGPRS data block: ", dl_block); - f_shutdown(__BFILE__, __LINE__); - } -} - -private function f_dl_block_ack_fn(in RlcmacDlBlock dl_block, uint32_t dl_fn) -runs on RAW_PCU_Test_CT return uint32_t { - var boolean rrbp_valid; - var MacRrbp rrbp; - - /* The argument must be either a GPRS or EGPRS data block */ - if (ischosen(dl_block.data_egprs)) { - rrbp_valid := true; /* always valid */ - rrbp := dl_block.data_egprs.mac_hdr.rrbp; - } else if (ischosen(dl_block.data)) { - rrbp_valid := dl_block.data.mac_hdr.mac_hdr.rrbp_valid; - rrbp := dl_block.data.mac_hdr.mac_hdr.rrbp; - } else { - rrbp_valid := dl_block.ctrl.mac_hdr.rrbp_valid; - rrbp := dl_block.ctrl.mac_hdr.rrbp; - } - - /* Make sure that the given block really needs to be ACKnowledged */ - if (not rrbp_valid) { - setverdict(fail, "DL block shall not be ACKnowledged, field RRBP is not valid"); - f_shutdown(__BFILE__, __LINE__); - } - - return f_rrbp_ack_fn(dl_fn, rrbp); -} - testcase TC_pcuif_suspend() runs on RAW_PCU_Test_CT { var octetstring ra_id := enc_RoutingAreaIdentification(mp_gb_cfg.cell_id.ra_id); var GprsTlli tlli := 'FFFFFFFF'O; @@ -2038,15 +1478,6 @@ runs on RAW_PCU_Test_CT { } } -private function f_pkt_paging_match_tmsi(in PacketPagingReq req, template GsmTmsi tmsi) -runs on RAW_PCU_Test_CT { - if (not match(req.repeated_pageinfo.cs.tmsi, tmsi)) { - setverdict(fail, "Mobile Identity (TMSI/P-TMSI) mismatch: ", - "expected: ", tmsi, "got: ", req.repeated_pageinfo.cs.tmsi); - f_shutdown(__BFILE__, __LINE__); - } -} - /* Test CS paging over the BTS<->PCU socket. * When a (class B or C, not A) MS has an active TBF (or is on the PDCH), the MS can not react on CS paging over CCCH. * Paging should be send on the PACCH. diff --git a/pcu/PCU_selftest.ttcn b/pcu/PCU_selftest.ttcn index 15cdd801b..70138bff5 100644 --- a/pcu/PCU_selftest.ttcn +++ b/pcu/PCU_selftest.ttcn @@ -25,6 +25,7 @@ import from GSM_RR_Types all; import from RLCMAC_CSN1_Types all; import from RLCMAC_Types all; import from RLCMAC_Templates all; +import from GPRS_Components all; import from PCU_Tests all; type component dummy_CT extends BSSGP_Client_CT {