/* rlcmac_prim_test.c * * (C) 2023 by sysmocom - s.f.m.c. GmbH * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void *tall_ctx = NULL; uint8_t last_rach_req_ra = 0; /** MS-SGSN LLC (Mobile Station - Serving GPRS Support Node Logical Link Control) SAPI: GPRS Mobility Management Address field SAPI: LLGMM 0... .... = Protocol Discriminator_bit: OK .0.. .... = Command/Response bit: DownLink/UpLink = Response/Command .... 0001 = SAPI: GPRS Mobility Management (1) Unconfirmed Information format - UI: UI format: 0x6, Spare bits: 0x0, N(U): 0, E bit: non encrypted frame, PM bit: FCS covers only the frame header and first N202 octets of the information field 110. .... .... .... = UI format: 0x6 ...0 0... .... .... = Spare bits: 0x0 .... .000 0000 00.. = N(U): 0 .... .... .... ..0. = E bit: non encrypted frame .... .... .... ...0 = PM bit: FCS covers only the frame header and first N202 octets of the information field FCS: 0xf218e2 (correct) GSM A-I/F DTAP - Attach Request Protocol Discriminator: GPRS mobility management messages (8) DTAP GPRS Mobility Management Message Type: Attach Request (0x01) MS Network Capability Attach Type Ciphering Key Sequence Number DRX Parameter Mobile Identity - IMSI (262420000000017) Routing Area Identification - Old routing area identification - RAI: 262-42-27780-68 MS Radio Access Capability */ static uint8_t pdu_llc_gmm_att_req[] = { 0x01, 0xc0, 0x00, 0x08, 0x01, 0x01, 0xd5, 0x71, 0x00, 0x00, 0x08, 0x29, 0x26, 0x24, 0x00, 0x00, 0x00, 0x00, 0x71, 0x62, 0xf2, 0x24, 0x6c, 0x84, 0x44, 0x04, 0x11, 0xe5, 0x10, 0x00, 0xe2, 0x18, 0xf2 }; /* GMM Attach Compl */ static uint8_t pdu_llc_gmm_att_compl[] = { 0x01, 0xc0, 0x0d, 0x08, 0x03, 0x55, 0x1c, 0xea }; /* SM Activate PDP Context Request */ static uint8_t pdu_llc_sm_act_pdp_ctx_req[] = { 0x01, 0xc0, 0x11, 0x8a, 0x41, 0x06, 0x03, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x21, 0x28, 0x09, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x9e, 0x49, 0x7a }; /** GSM CCCH - Immediate Assignment L2 Pseudo Length 0010 11.. = L2 Pseudo Length value: 11 .... 0110 = Protocol discriminator: Radio Resources Management messages (0x6) .... 0110 = Protocol discriminator: Radio Resources Management messages (0x6) 0000 .... = Skip Indicator: No indication of selected PLMN (0) Message Type: Immediate Assignment Page Mode .... 0000 = Page Mode: Normal paging (0) Dedicated mode or TBF 0001 .... = Dedicated mode or TBF: This message assigns an uplink TBF or is the second message of two in a two-message assignment of an uplink or downlink TBF (1) Packet Channel Description 0000 1... = Channel Type: 1 .... .111 = Timeslot: 7 111. .... = Training Sequence: 7 .... .0.. = Spare: 0x00 .... ..11 0110 0111 = Single channel ARFCN: 871 Request Reference Random Access Information (RA): 120 0000 1... = T1': 1 .... .001 011. .... = T3: 11 ...0 1011 = T2: 11 [RFN: 1337] Timing Advance Timing advance value: 0 Mobile Allocation Length: 0 IA Rest Octets H... .... = First Discriminator Bit: High .H.. .... = Second Discriminator Bit: High ..0. .... = Discriminator Bit: Packet Assignment ...0 .... = Discriminator Bit: Packet Uplink Assignment Packet Uplink Assignment .... 1... = Packet Uplink Assignment: Normal .... .000 00.. .... = TFI_Assignment: 0 ..0. .... = Polling: no action is required from MS ...0 .... = Allocation Type: Dynamic Allocation (mandatory after Rel-4) .... 000. = USF: 0 .... ...0 = USF_granularity: the mobile station shall transmit one RLC/MAC block 0... .... = P0: Not Present .01. .... = Channel_Coding_Command: CS-2 (1) ...1 .... = TLLI_Block_Channel_Coding: mobile station shall use coding scheme as specified by the corresponding CHANNEL CODING COMMAND or EGPRS CHANNEL CODING COMMAND field .... 0... = Alpha: Not Present .... .000 00.. .... = Gamma: 0 dB (0) ..0. .... = Timing Advance Index: Not Present ...0 .... = TBF Starting Time: Not Present .... L... = Additions in R99: Not Present .... .L.. = Additions in Rel-6: Not Present .... ..L. = Additions in Rel-10: Not Present .... ...L = Additions in Rel-13: Not Present Padding Bits: default padding */ static uint8_t ccch_imm_ass_pkt_ul_tbf_normal[] = { 0x2d, 0x06, 0x3f, 0x10, 0x0f, 0xe3, 0x67, 0x78, 0x09, 0x6b, 0x00, 0x00, 0xc8, 0x00, 0x30, 0x0b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b }; /* GSM CCCH - Immediate Assignment L2 Pseudo Length 0010 11.. = L2 Pseudo Length value: 11 .... 0110 = Protocol discriminator: Radio Resources Management messages (0x6) .... 0110 = Protocol discriminator: Radio Resources Management messages (0x6) 0000 .... = Skip Indicator: No indication of selected PLMN (0) Message Type: Immediate Assignment Page Mode .... 0000 = Page Mode: Normal paging (0) Dedicated mode or TBF 0011 .... = Dedicated mode or TBF: This message assigns a downlink TBF to the mobile station identified in the IA Rest Octets IE (3) Packet Channel Description 0000 1... = Channel Type: 1 .... .111 = Timeslot: 7 111. .... = Training Sequence: 7 .... .0.. = Spare: 0x00 .... ..11 0110 0111 = Single channel ARFCN: 871 Request Reference Random Access Information (RA): 125 1000 0... = T1': 16 .... .000 000. .... = T3: 0 ...0 0000 = T2: 0 [RFN: 21216] Timing Advance Timing advance value: 28 Mobile Allocation Length: 0 IA Rest Octets H... .... = First Discriminator Bit: High .H.. .... = Second Discriminator Bit: High ..0. .... = Discriminator Bit: Packet Assignment ...1 .... = Discriminator Bit: Packet Downlink Assignment Packet Downlink Assignment .... 0000 0000 0000 0000 0000 0000 0000 0001 .... = TLLI: 0x00000001 .... 1... = TFI Assignment (etc): Present .... .000 00.. .... = TFI_Assignment: 0 ..0. .... = RLC_Mode: RLC acknowledged mode ...0 .... = Alpha: Not Present .... 0000 0... .... = Gamma: 0 dB (0) .0.. .... = Polling: no action is required from MS ..0. .... = TA_Valid: the timing advance value is not valid ...0 .... = Timing Advance Index: Not Present .... 0... = TBF Starting Time: Not Present .... .0.. = P0: Not Present .... ..L. = Additions in R99: Not Present .... ...L = Additions in Rel-6: Not Present L... .... = Additions in Rel-7: Not Present .L.. .... = Additions in Rel-10: Not Present ..L. .... = Additions in Rel-13: Not Present Padding Bits: default padding */ static uint8_t ccch_imm_ass_pkt_dl_tbf[] = { 0x2d, 0x06, 0x3f, 0x30, 0x0f, 0xe3, 0x67, 0x7d, 0x80, 0x00, 0x1c, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x18, 0x00, 0x03, 0x2b, 0x2b, 0x2b, 0x2b }; /* GSM CCCH - Paging Request Type 1 L2 Pseudo Length 0011 00.. = L2 Pseudo Length value: 12 .... 0110 = Protocol discriminator: Radio Resources Management messages (0x6) .... 0110 = Protocol discriminator: Radio Resources Management messages (0x6) 0000 .... = Skip Indicator: No indication of selected PLMN (0) Message Type: Paging Request Type 1 Page Mode .... 0000 = Page Mode: Normal paging (0) Channel Needed ..00 .... = Channel 1: Any channel (0) 00.. .... = Channel 2: Any channel (0) Mobile Identity - Mobile Identity 1 - IMSI (262420000000423) Length: 8 0010 .... = Identity Digit 1: 2 .... 1... = Odd/even indication: Odd number of identity digits .... .001 = Mobile Identity Type: IMSI (1) IMSI: 262420000000423 [Association IMSI: 262420000000423] Mobile Country Code (MCC): Germany (262) Mobile Network Code (MNC): Vodafone GmbH (42) P1 Rest Octets L... .... = NLN(PCH): Not Present .L.. .... = Priority 1: Not Present ..L. .... = Priority 2: Not Present ...L .... = Group Call Information: Not Present .... H... = Packet Page Indication 1: For GPRS .... .H.. = Packet Page Indication 2: For GPRS Padding Bits: default padding */ static uint8_t ccch_pag_req_1[] = { 0x31, 0x06, 0x21, 0x00, 0x08, 0x29, 0x26, 0x24, 0x00, 0x00, 0x00, 0x40, 0x32, 0x27, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b }; #define clock_debug(fmt, args...) \ do { \ struct timespec ts; \ struct timeval tv; \ osmo_clock_gettime(CLOCK_MONOTONIC, &ts); \ osmo_gettimeofday(&tv, NULL); \ fprintf(stdout, "sys={%lu.%06lu}, mono={%lu.%06lu}: " fmt "\n", \ tv.tv_sec, tv.tv_usec, ts.tv_sec, ts.tv_nsec/1000, ##args); \ } while (0) static void clock_override_enable(bool enable) { osmo_gettimeofday_override = enable; osmo_clock_override_enable(CLOCK_MONOTONIC, enable); } static void clock_override_set(long sec, long usec) { struct timespec *mono; osmo_gettimeofday_override_time.tv_sec = sec; osmo_gettimeofday_override_time.tv_usec = usec; mono = osmo_clock_override_gettimespec(CLOCK_MONOTONIC); mono->tv_sec = sec; mono->tv_nsec = usec*1000; clock_debug("clock_override_set"); } static void clock_override_add_debug(long sec, long usec, bool dbg) { osmo_gettimeofday_override_add(sec, usec); osmo_clock_override_add(CLOCK_MONOTONIC, sec, usec*1000); if (dbg) clock_debug("clock_override_add"); } #define clock_override_add(sec, usec) clock_override_add_debug(sec, usec, true) static struct osmo_gprs_rlcmac_prim *create_dl_ctrl_block_buf(uint8_t *buf, int num_bytes, uint8_t tn, uint32_t fn) { struct osmo_gprs_rlcmac_prim *rlcmac_prim; rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_data_ind(tn, fn, 0, 0, 0, NULL, num_bytes); rlcmac_prim->l1ctl.pdch_data_ind.data = msgb_put(rlcmac_prim->oph.msg, num_bytes); memcpy(rlcmac_prim->l1ctl.pdch_data_ind.data, buf, num_bytes); return rlcmac_prim; } static struct osmo_gprs_rlcmac_prim *create_dl_ctrl_block(RlcMacDownlink_t *dl_block, uint8_t tn, uint32_t fn) { struct bitvec *rlc_block; uint8_t buf[64]; int num_bytes; rlc_block = bitvec_alloc(23, tall_ctx); OSMO_ASSERT(osmo_gprs_rlcmac_encode_downlink(rlc_block, dl_block) == 0); num_bytes = bitvec_pack(rlc_block, &buf[0]); OSMO_ASSERT((size_t)num_bytes < sizeof(buf)); bitvec_free(rlc_block); return create_dl_ctrl_block_buf(&buf[0], num_bytes, tn, fn); } static void ul_ack_nack_init(RlcMacDownlink_t *dl_block, uint8_t ul_tfi, enum gprs_rlcmac_coding_scheme cs) { Packet_Uplink_Ack_Nack_t *ack = &dl_block->u.Packet_Uplink_Ack_Nack; PU_AckNack_GPRS_t *gprs = &ack->u.PU_AckNack_GPRS_Struct; memset(dl_block, 0, sizeof(*dl_block)); dl_block->PAYLOAD_TYPE = GPRS_RLCMAC_PT_CONTROL_BLOCK; dl_block->RRBP = 0; dl_block->SP = 0; dl_block->USF = 0x00; dl_block->u.MESSAGE_TYPE = OSMO_GPRS_RLCMAC_DL_MSGT_PACKET_UPLINK_ACK_NACK; ack->MESSAGE_TYPE = OSMO_GPRS_RLCMAC_DL_MSGT_PACKET_UPLINK_ACK_NACK; ack->PAGE_MODE = GPRS_RLCMAC_PAGE_MODE_NORMAL; ack->UPLINK_TFI = ul_tfi; ack->UnionType = 0; /* GPRS */ gprs->CHANNEL_CODING_COMMAND = gprs_rlcmac_mcs_chan_code(cs); } static void ul_ack_nack_mark(Ack_Nack_Description_t *ack_desc, unsigned int idx, bool received) { //ack_desc->RECEIVED_BLOCK_BITMAP[sizeof(ack_desc->RECEIVED_BLOCK_BITMAP) - 1] = 0xff; //memset(ack_desc->RECEIVED_BLOCK_BITMAP, 0xff, sizeof(ack_desc->RECEIVED_BLOCK_BITMAP)); if (received) ack_desc->RECEIVED_BLOCK_BITMAP[sizeof(ack_desc->RECEIVED_BLOCK_BITMAP) - idx/8 - 1] |= (1 << (idx & 0x03)); else ack_desc->RECEIVED_BLOCK_BITMAP[sizeof(ack_desc->RECEIVED_BLOCK_BITMAP) - idx/8 - 1] &= ~(1 << (idx & 0x03)); } static void pkt_ul_ass_from_dl_tbf_init(RlcMacDownlink_t *block, uint8_t dl_tfi, uint8_t new_ul_tfi, uint16_t arfcn, uint8_t *usf_li) { Packet_Uplink_Assignment_t *pua = &block->u.Packet_Uplink_Assignment; PUA_GPRS_t *gprs = &pua->u.PUA_GPRS_Struct; Packet_Timing_Advance_t *pta = &gprs->Packet_Timing_Advance; Frequency_Parameters_t *fp = &gprs->Frequency_Parameters; Dynamic_Allocation_t *da = &gprs->u.Dynamic_Allocation; unsigned int tn; memset(block, 0, sizeof(*block)); block->PAYLOAD_TYPE = GPRS_RLCMAC_PT_CONTROL_BLOCK; block->RRBP = 0; block->SP = 0; block->USF = 0x00; block->u.MESSAGE_TYPE = OSMO_GPRS_RLCMAC_DL_MSGT_PACKET_UPLINK_ASSIGNMENT; /* See 3GPP TS 44.060, section 11.2.29 */ pua = &block->u.Packet_Uplink_Assignment; pua->MESSAGE_TYPE = OSMO_GPRS_RLCMAC_DL_MSGT_PACKET_UPLINK_ASSIGNMENT; pua->PAGE_MODE = 0x00; /* TLLI or Global DL TFI */ pua->ID.UnionType = 0x00; pua->ID.u.Global_TFI.UnionType = 0x01; pua->ID.u.Global_TFI.u.UPLINK_TFI = dl_tfi; /* GPRS specific parameters */ pua->UnionType = 0x00; /* Use the commanded CS/MCS value during the content resolution */ gprs->CHANNEL_CODING_COMMAND = gprs_rlcmac_mcs_chan_code(GPRS_RLCMAC_MCS_2); gprs->TLLI_BLOCK_CHANNEL_CODING = 0x01; // ^^^ /* Dynamic allocation */ gprs->UnionType = 0x01; /* Frequency Parameters IE is present */ gprs->Exist_Frequency_Parameters = 0x01; /* Packet Timing Advance (if known) */ pta->Exist_TIMING_ADVANCE_VALUE = 0x01; // Present pta->TIMING_ADVANCE_VALUE = 1; /* Continuous Timing Advance Control */ pta->Exist_IndexAndtimeSlot = 0x01; // Present pta->TIMING_ADVANCE_TIMESLOT_NUMBER = 0; // FIXME! pta->TIMING_ADVANCE_INDEX = 2; /* Frequency Parameters IE */ fp->TSC = 2; fp->UnionType = 0x00; fp->u.ARFCN = arfcn; /* Dynamic allocation parameters */ da->USF_GRANULARITY = 0x00; /* Assign an Uplink TFI */ da->Exist_UPLINK_TFI_ASSIGNMENT = 0x01; da->UPLINK_TFI_ASSIGNMENT = new_ul_tfi; /* Timeslot Allocation with or without Power Control */ da->UnionType = 0x00; for (tn = 0; tn < 8; tn++) { Timeslot_Allocation_t *slot = &da->u.Timeslot_Allocation[tn]; if (usf_li[tn] == 0xff) continue; slot->Exist = 0x01; // Enable this timeslot slot->USF_TN = usf_li[tn]; // USF_TN(i) } } static uint8_t *create_si13(uint8_t bs_cv_max /* 0..15 */) { static uint8_t si13_buf[GSM_MACBLOCK_LEN]; struct gsm48_system_information_type_13 *si13 = (struct gsm48_system_information_type_13 *)&si13_buf[0]; struct osmo_gsm48_si13_info si13_info; int ret; memset(si13, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); si13->header.rr_protocol_discriminator = GSM48_PDISC_RR; si13->header.skip_indicator = 0; si13->header.system_information = GSM48_MT_RR_SYSINFO_13; si13_info = (struct osmo_gsm48_si13_info){ .cell_opts = { .nmo = GPRS_NMO_II, .t3168 = 2000, .t3192 = 1500, .drx_timer_max = 3, .bs_cv_max = bs_cv_max, .ctrl_ack_type_use_block = 1, .ext_info_present = true, .ext_info = { .egprs_supported = 1, .use_egprs_p_ch_req = 1, .bep_period = 5, .pfc_supported = 0, .dtm_supported = 0, .bss_paging_coordination = 1, .ccn_active = true, }, }, .pwr_ctrl_pars = { .alpha = 0, /* a = 0.0 */ .t_avg_w = 16, .t_avg_t = 16, .pc_meas_chan = 0, /* downling measured on CCCH */ .n_avg_i = 8, }, .bcch_change_mark = 1, /* Information about the other SIs */ .si_change_field = 0, .rac = 33, .spgc_ccch_sup = 0, .net_ctrl_ord = 1 /* NC1 */, .prio_acc_thr = 6, }; ret = osmo_gsm48_rest_octets_si13_encode(si13->rest_octets, &si13_info); if (ret < 0) return NULL; /* length is coded in bit 2 an up */ si13->header.l2_plen = 0x01; return &si13_buf[0]; } static int test_rlcmac_prim_up_cb(struct osmo_gprs_rlcmac_prim *rlcmac_prim, void *user_data) { const char *pdu_name = osmo_gprs_rlcmac_prim_name(rlcmac_prim); switch (rlcmac_prim->oph.sap) { case OSMO_GPRS_RLCMAC_SAP_GMMRR: printf("%s(): Rx %s TLLI=0x%08x\n", __func__, pdu_name, rlcmac_prim->gmmrr.tlli); break; case OSMO_GPRS_RLCMAC_SAP_GRR: printf("%s(): Rx %s TLLI=0x%08x ll=[%s]\n", __func__, pdu_name, rlcmac_prim->grr.tlli, osmo_hexdump(rlcmac_prim->grr.ll_pdu, rlcmac_prim->grr.ll_pdu_len)); break; default: printf("%s(): Unexpected Rx %s\n", __func__, pdu_name); OSMO_ASSERT(0); } return 0; } static int test_rlcmac_prim_down_cb(struct osmo_gprs_rlcmac_prim *rlcmac_prim, void *user_data) { const char *pdu_name = osmo_gprs_rlcmac_prim_name(rlcmac_prim); switch (rlcmac_prim->oph.sap) { case OSMO_GPRS_RLCMAC_SAP_L1CTL: switch (OSMO_PRIM_HDR(&rlcmac_prim->oph)) { case OSMO_PRIM(OSMO_GPRS_RLCMAC_L1CTL_PDCH_DATA, PRIM_OP_REQUEST): printf("%s(): Rx %s fn=%u ts=%u data_len=%u data=[%s]\n", __func__, pdu_name, rlcmac_prim->l1ctl.pdch_data_req.fn, rlcmac_prim->l1ctl.pdch_data_req.ts_nr, rlcmac_prim->l1ctl.pdch_data_req.data_len, osmo_hexdump(rlcmac_prim->l1ctl.pdch_data_req.data, rlcmac_prim->l1ctl.pdch_data_req.data_len)); break; case OSMO_PRIM(OSMO_GPRS_RLCMAC_L1CTL_RACH, PRIM_OP_REQUEST): last_rach_req_ra = rlcmac_prim->l1ctl.rach_req.ra; printf("%s(): Rx %s ra=0x%02x\n", __func__, pdu_name, last_rach_req_ra); break; case OSMO_PRIM(OSMO_GPRS_RLCMAC_L1CTL_CFG_UL_TBF, PRIM_OP_REQUEST): printf("%s(): Rx %s ul_tbf_nr=%u ul_slotmask=0x%02x\n", __func__, pdu_name, rlcmac_prim->l1ctl.cfg_ul_tbf_req.ul_tbf_nr, rlcmac_prim->l1ctl.cfg_ul_tbf_req.ul_slotmask); break; case OSMO_PRIM(OSMO_GPRS_RLCMAC_L1CTL_CFG_DL_TBF, PRIM_OP_REQUEST): printf("%s(): Rx %s dl_tbf_nr=%u dl_slotmask=0x%02x dl_tfi=%u\n", __func__, pdu_name, rlcmac_prim->l1ctl.cfg_dl_tbf_req.dl_tbf_nr, rlcmac_prim->l1ctl.cfg_dl_tbf_req.dl_slotmask, rlcmac_prim->l1ctl.cfg_dl_tbf_req.dl_tfi); break; default: printf("%s(): Rx %s\n", __func__, pdu_name); } break; default: printf("%s(): Unexpected Rx %s\n", __func__, pdu_name); OSMO_ASSERT(0); } return 0; } static const uint8_t llc_dummy_command[] = { 0x43, 0xc0, 0x01, 0x2b, 0x2b, 0x2b }; static struct msgb *create_dl_data_block(uint8_t dl_tfi, uint8_t usf, enum gprs_rlcmac_coding_scheme cs, uint8_t bsn, bool fbi, bool s_p, uint8_t rrbp) { struct msgb *msg = msgb_alloc(128, __func__); struct gprs_rlcmac_rlc_dl_data_header *hdr; struct gprs_rlcmac_rlc_li_field *lime; hdr = (struct gprs_rlcmac_rlc_dl_data_header *)msgb_put(msg, gprs_rlcmac_mcs_size_dl(cs)); hdr->pt = 0; /* RLC/MAC block contains an RLC data block */ hdr->rrbp = rrbp; hdr->s_p = s_p ? 1 : 0; hdr->usf = usf; hdr->pr = 0; hdr->tfi = dl_tfi; hdr->fbi = fbi ? 1 : 0; hdr->tfi = dl_tfi; hdr->bsn = bsn; hdr->e = 0; lime = &hdr->lime[0]; lime->li = sizeof(llc_dummy_command); lime->m = 0; lime->e = 1; msg->l3h = &lime->ll_pdu[0]; memset(msg->l3h, 0x2b, msgb_l3len(msg)); memcpy(msg->l3h, llc_dummy_command, sizeof(llc_dummy_command)); return msg; } void prepare_test(void) { int rc; clock_override_set(0, 0); rc = osmo_gprs_rlcmac_init(OSMO_GPRS_RLCMAC_LOCATION_MS); OSMO_ASSERT(rc == 0); osmo_gprs_rlcmac_prim_set_up_cb(test_rlcmac_prim_up_cb, NULL); osmo_gprs_rlcmac_prim_set_down_cb(test_rlcmac_prim_down_cb, NULL); } void cleanup_test(void) { struct gprs_rlcmac_entity *gre; llist_for_each_entry(gre, &g_rlcmac_ctx->gre_list, entry) { if (osmo_timer_pending(&gre->defer_pkt_idle_timer)) { /* increase time DEFER_SCHED_PDCH_REL_REQ_uS, defer_pkt_idle_timer should trigger */ clock_override_add(0, DEFER_SCHED_PDCH_REL_REQ_uS); clock_debug("Expect defer_pkt_idle_timer timeout"); osmo_select_main(0); break; } } /* Reinit the RLCMAC layer so that data generated during the test is freed within the test context: */ osmo_gprs_rlcmac_init(OSMO_GPRS_RLCMAC_LOCATION_MS); } static void test_ul_tbf_attach(void) { struct osmo_gprs_rlcmac_prim *rlcmac_prim; int rc; printf("=== %s start ===\n", __func__); prepare_test(); RlcMacDownlink_t dl_block; Ack_Nack_Description_t *ack_desc = &dl_block.u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description; uint32_t tlli = 0x2342; uint8_t ul_tfi = 0; uint8_t ts_nr = 7; uint8_t usf = 0; uint32_t rts_fn = 4; rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_grr_unitdata_req(tlli, pdu_llc_gmm_att_req, sizeof(pdu_llc_gmm_att_req)); rlcmac_prim->grr.unitdata_req.sapi = OSMO_GPRS_RLCMAC_LLC_SAPI_GMM; rlcmac_prim->grr.unitdata_req.radio_prio = 1; rc = osmo_gprs_rlcmac_prim_upper_down(rlcmac_prim); OSMO_ASSERT(sizeof(ccch_imm_ass_pkt_ul_tbf_normal) == GSM_MACBLOCK_LEN); ccch_imm_ass_pkt_ul_tbf_normal[7] = last_rach_req_ra; /* Update RA to match */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_data_ind(0, ccch_imm_ass_pkt_ul_tbf_normal); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); /* Trigger transmission of LLC data (GMM Attach) (first part) */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ts_nr, rts_fn, usf); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); /* Trigger transmission of LLC data (GMM Attach) (second part) */ rts_fn = fn_next_block(rts_fn); rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ts_nr, rts_fn, usf); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); /* PCU acks it: */ ul_ack_nack_init(&dl_block, ul_tfi, GPRS_RLCMAC_CS_2); ack_desc->STARTING_SEQUENCE_NUMBER = 1; ack_desc->FINAL_ACK_INDICATION = 1; ul_ack_nack_mark(ack_desc, 0, true); ul_ack_nack_mark(ack_desc, 1, true); /* Final ACK has Poll set: */ dl_block.SP = 1; dl_block.RRBP = GPRS_RLCMAC_RRBP_N_plus_13; rlcmac_prim = create_dl_ctrl_block(&dl_block, ts_nr, rts_fn); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); /* Trigger transmission of PKT CTRL ACK */ rts_fn = rrbp2fn(rts_fn, dl_block.RRBP); rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ts_nr, rts_fn, usf); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); /* Trigger transmission confirmation of PKT CTRL ACK */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_data_cnf(ts_nr, rts_fn, NULL, 0); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); printf("=== %s end ===\n", __func__); cleanup_test(); } /* Test UL TBF requesting assignment of a new UL TBF through PACCH when * answering UL ACK/NACK w/ FinalACK=1 */ static void test_ul_tbf_request_another_ul_tbf(void) { struct osmo_gprs_rlcmac_prim *rlcmac_prim; int rc; printf("=== %s start ===\n", __func__); prepare_test(); RlcMacDownlink_t dl_block; PU_AckNack_GPRS_t *ack_gprs = &dl_block.u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct; Ack_Nack_Description_t *ack_desc = &ack_gprs->Ack_Nack_Description; uint32_t tlli = 0x2342; uint8_t ul_tfi = 0; uint8_t ts_nr = 7; uint8_t usf = 0; uint32_t rts_fn = 4; /* Send only 14 data to feed it in 1 UL block and speed up test length: */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_grr_unitdata_req(tlli, pdu_llc_gmm_att_req, 14); rlcmac_prim->grr.unitdata_req.sapi = OSMO_GPRS_RLCMAC_LLC_SAPI_GMM; rlcmac_prim->grr.unitdata_req.radio_prio = 1; rc = osmo_gprs_rlcmac_prim_upper_down(rlcmac_prim); OSMO_ASSERT(sizeof(ccch_imm_ass_pkt_ul_tbf_normal) == GSM_MACBLOCK_LEN); ccch_imm_ass_pkt_ul_tbf_normal[7] = last_rach_req_ra; /* Update RA to match */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_data_ind(0, ccch_imm_ass_pkt_ul_tbf_normal); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); /* Trigger transmission of LLC data (GMM Attach) */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ts_nr, rts_fn, usf); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); /* PCU acks it: */ ul_ack_nack_init(&dl_block, ul_tfi, GPRS_RLCMAC_CS_2); ack_desc->STARTING_SEQUENCE_NUMBER = 1; ack_desc->FINAL_ACK_INDICATION = 1; ul_ack_nack_mark(ack_desc, 0, true); ul_ack_nack_mark(ack_desc, 1, true); /* TBF Est is set: */ ack_gprs->Exist_AdditionsR99 = 1; ack_gprs->AdditionsR99.TBF_EST = 1; /* Final ACK has Poll set: */ dl_block.SP = 1; dl_block.RRBP = GPRS_RLCMAC_RRBP_N_plus_13; rlcmac_prim = create_dl_ctrl_block(&dl_block, ts_nr, rts_fn); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); /* New data from upper layers arrive which needs to be transmitted. This * will make UL_TBF request a new UL_TBF when triggered to answer the final * UL ACK/NACK, because there's no active DL TBF: */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_grr_unitdata_req(tlli, pdu_llc_gmm_att_req, 14); rlcmac_prim->grr.unitdata_req.sapi = OSMO_GPRS_RLCMAC_LLC_SAPI_GMM; rlcmac_prim->grr.unitdata_req.radio_prio = 1; rc = osmo_gprs_rlcmac_prim_upper_down(rlcmac_prim); /* Trigger transmission of PKT RES REQ: */ rts_fn = rrbp2fn(rts_fn, dl_block.RRBP); rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ts_nr, rts_fn, usf); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); printf("=== %s end ===\n", __func__); cleanup_test(); } static void test_ul_tbf_t3164_timeout(void) { struct osmo_gprs_rlcmac_prim *rlcmac_prim; int rc; unsigned int i; printf("=== %s start ===\n", __func__); prepare_test(); uint32_t tlli = 0x2342; rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_grr_unitdata_req(tlli, pdu_llc_gmm_att_req, sizeof(pdu_llc_gmm_att_req)); rlcmac_prim->grr.unitdata_req.sapi = OSMO_GPRS_RLCMAC_LLC_SAPI_GMM; rlcmac_prim->grr.unitdata_req.radio_prio = 1; rc = osmo_gprs_rlcmac_prim_upper_down(rlcmac_prim); OSMO_ASSERT(sizeof(ccch_imm_ass_pkt_ul_tbf_normal) == GSM_MACBLOCK_LEN); for (i = 0; i < 4; i++) { ccch_imm_ass_pkt_ul_tbf_normal[7] = last_rach_req_ra; /* Update RA to match */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_data_ind(0, ccch_imm_ass_pkt_ul_tbf_normal); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); /* increase time 5 seconds, timeout should trigger */ clock_override_add(5, 0); clock_debug("Expect T3164 timeout"); osmo_select_main(0); } printf("=== %s end ===\n", __func__); cleanup_test(); } static void test_ul_tbf_t3166_timeout(void) { struct osmo_gprs_rlcmac_prim *rlcmac_prim; int rc; printf("=== %s start ===\n", __func__); prepare_test(); uint32_t tlli = 0x2342; uint8_t ts_nr = 7; uint8_t usf = 0; uint32_t rts_fn = 4; unsigned int i; rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_grr_unitdata_req(tlli, pdu_llc_gmm_att_req, sizeof(pdu_llc_gmm_att_req)); rlcmac_prim->grr.unitdata_req.sapi = OSMO_GPRS_RLCMAC_LLC_SAPI_GMM; rlcmac_prim->grr.unitdata_req.radio_prio = 1; rc = osmo_gprs_rlcmac_prim_upper_down(rlcmac_prim); for (i = 0; i < 4; i++) { /* 4 packet access procedure attempts */ OSMO_ASSERT(sizeof(ccch_imm_ass_pkt_ul_tbf_normal) == GSM_MACBLOCK_LEN); ccch_imm_ass_pkt_ul_tbf_normal[7] = last_rach_req_ra; /* Update RA to match */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_data_ind(0, ccch_imm_ass_pkt_ul_tbf_normal); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); /* Trigger transmission of LLC data (GMM Attach) (first part) */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ts_nr, rts_fn, usf); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); /* In 5 seconds, T3166 will trigger. T3180 has also been armed * at 5s, so scenify PCU USF-polling the MS to avoid trigger T3180 * instead of T3166. First increase 2 of the 5 seconds: */ clock_override_add(2, 0); rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ts_nr, rts_fn, usf); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); /* Increase time (remaining 3 out of 5 seconds), T3166 timeout should trigger */ clock_override_add(3, 0); clock_debug("Expect T3166 timeout"); osmo_select_main(0); } printf("=== %s end ===\n", __func__); cleanup_test(); } static void test_ul_tbf_t3180_timeout(void) { struct osmo_gprs_rlcmac_prim *rlcmac_prim; int rc; printf("=== %s start ===\n", __func__); prepare_test(); uint32_t tlli = 0x2342; uint8_t ts_nr = 7; uint8_t usf = 0; uint32_t rts_fn = 4; rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_grr_unitdata_req(tlli, pdu_llc_gmm_att_req, sizeof(pdu_llc_gmm_att_req)); rlcmac_prim->grr.unitdata_req.sapi = OSMO_GPRS_RLCMAC_LLC_SAPI_GMM; rlcmac_prim->grr.unitdata_req.radio_prio = 1; rc = osmo_gprs_rlcmac_prim_upper_down(rlcmac_prim); OSMO_ASSERT(sizeof(ccch_imm_ass_pkt_ul_tbf_normal) == GSM_MACBLOCK_LEN); ccch_imm_ass_pkt_ul_tbf_normal[7] = last_rach_req_ra; /* Update RA to match */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_data_ind(0, ccch_imm_ass_pkt_ul_tbf_normal); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); /* Trigger transmission of LLC data (GMM Attach) (first part) */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ts_nr, rts_fn, usf); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); /* Transmit another block */ rts_fn = fn_next_block(rts_fn); rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ts_nr, rts_fn, usf); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); /* increase time 5 seconds, T3180 timeout should trigger */ clock_override_add(5, 0); clock_debug("Expect T3180 timeout"); osmo_select_main(0); printf("=== %s end ===\n", __func__); cleanup_test(); } static void test_ul_tbf_n3104_timeout(void) { struct osmo_gprs_rlcmac_prim *rlcmac_prim; int rc; printf("=== %s start ===\n", __func__); prepare_test(); uint32_t tlli = 0x2342; uint8_t ts_nr = 7; uint8_t usf = 0; uint32_t rts_fn = 4; unsigned int i; const unsigned int bs_cv_max = 1; /* 0 interpreted as 1 for N3104max */ const unsigned int num_ts = 1; const unsigned int n3104_max = 3 * (bs_cv_max + 3) * num_ts; /* Submit an SI13 with bs_cv_max=0: */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_data_ind(0, create_si13(0)); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); /* Fill in several LLC PDUs: */ for (i = 0; i < 2; i++) { rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_grr_unitdata_req(tlli, pdu_llc_gmm_att_req, sizeof(pdu_llc_gmm_att_req)); rlcmac_prim->grr.unitdata_req.sapi = OSMO_GPRS_RLCMAC_LLC_SAPI_GMM; rlcmac_prim->grr.unitdata_req.radio_prio = 1; rc = osmo_gprs_rlcmac_prim_upper_down(rlcmac_prim); OSMO_ASSERT(rc == 0); } ccch_imm_ass_pkt_ul_tbf_normal[7] = last_rach_req_ra; /* Update RA to match */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_data_ind(0, ccch_imm_ass_pkt_ul_tbf_normal); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); for (i = 0; i < n3104_max; i++) { rts_fn = fn_next_block(rts_fn); printf("RTS %u: FN=%u\n", i, rts_fn); /* Trigger transmission of LLC data (GMM Attach) (first part) */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ts_nr, rts_fn, usf); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); } /* After N3104 triggers, MS re-tries pkt access: */ ccch_imm_ass_pkt_ul_tbf_normal[7] = last_rach_req_ra; /* Update RA to match */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_data_ind(0, ccch_imm_ass_pkt_ul_tbf_normal); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); printf("=== %s end ===\n", __func__); cleanup_test(); } /* 9.3.2.4.2 (acked mode): When the mobile station has sent the RLC data block * with CV = 0 and there are no elements in the V(B) array set to the value * Nacked, it shall start timer T3182 for this TBF. The mobile station shall * continue to send RLC data blocks on each assigned uplink data block, * according to the algorithm defined in sub-clause 9.1.3. * 9.3.3.3.2 (unacked mode): Upon each retransmission of the last block with * CV=0, the mobile station shall restart timer T3182 for the TBF. The block * with CV=0 shall not be retransmitted more than four times. If the medium * access mode is dynamic allocation, the repetitions are transmitted when the * mobile station is scheduled USFs. */ static void test_ul_tbf_t3182_timeout(bool acked_mode) { struct osmo_gprs_rlcmac_prim *rlcmac_prim; int rc; unsigned int i; printf("=== %s start ===\n", __func__); prepare_test(); uint32_t tlli = 0x2342; uint8_t ul_tfi = 0; uint8_t ts_nr = 7; uint8_t usf = 0; uint32_t rts_fn = 4; RlcMacDownlink_t dl_block; Ack_Nack_Description_t *ack_desc = &dl_block.u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description; /* Submit 14 bytes to fit in 1 RLCMAC block to shorten test and end up in FINISHED state quickly: */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_grr_unitdata_req(tlli, pdu_llc_gmm_att_req, sizeof(pdu_llc_gmm_att_req)); rlcmac_prim->grr.unitdata_req.sapi = OSMO_GPRS_RLCMAC_LLC_SAPI_GMM; rlcmac_prim->grr.unitdata_req.radio_prio = 1; rc = osmo_gprs_rlcmac_prim_upper_down(rlcmac_prim); ccch_imm_ass_pkt_ul_tbf_normal[7] = last_rach_req_ra; /* Update RA to match */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_data_ind(0, ccch_imm_ass_pkt_ul_tbf_normal); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); /* Trigger transmission of LLC data (GMM Attach) (first part) */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ts_nr, rts_fn, usf); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); /* PCU acks it: */ ul_ack_nack_init(&dl_block, ul_tfi, GPRS_RLCMAC_CS_2); ack_desc->STARTING_SEQUENCE_NUMBER = 1; ack_desc->FINAL_ACK_INDICATION = 0; ul_ack_nack_mark(ack_desc, 0, true); rlcmac_prim = create_dl_ctrl_block(&dl_block, ts_nr, rts_fn); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); /* Trigger transmission of LLC data (GMM Attach) (second part, CV=0) */ rts_fn = fn_next_block(rts_fn); rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ts_nr, rts_fn, usf); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); /* Now T3182 is armed and will trigger in 5 seconds. */ if (acked_mode) { /* Keep sending some USF-polling from PCU to avoid T3180 triggering. * while at it, make sure we don't apply the max-4-cv0-tx limit as with unacked mode: */ for (i = 0; i < 5; i++) { /* increase time 1 seconds, timeout should trigger */ clock_override_add(1, 0); rts_fn = fn_next_block(rts_fn); printf("RTS %u: FN=%u\n", i, rts_fn); /* Trigger transmission of LLC data (GMM Attach) (second part) */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ts_nr, rts_fn, usf); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); } } else { for (i = 0; i < 2; i++) { /* Keep sending some USF-polling from PCU to avoid T3180 triggering: * increase time 2.500 seconds 2 times = 5s, */ clock_override_add(2, 500000); rts_fn = fn_next_block(rts_fn); printf("RTS %u: FN=%u\n", i, rts_fn); /* Trigger transmission of LLC data (GMM Attach) (second part) */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ts_nr, rts_fn, usf); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); } } /* Timeout should trigger now: */ clock_debug("Expect T3182 timeout"); osmo_select_main(0); printf("=== %s end ===\n", __func__); cleanup_test(); } #if 0 /* FIXME: enable this whenever unacknowledged mode is supported. */ /* 9.3.3.3.2: The block with CV=0 shall not be retransmitted more than four times. */ static void test_ul_tbf_unack_mode_last_data_cv0_retrans_max(void) { struct osmo_gprs_rlcmac_prim *rlcmac_prim; int rc; printf("=== %s start ===\n", __func__); prepare_test(); uint32_t tlli = 0x2342; uint8_t ul_tfi = 0; uint8_t ts_nr = 7; uint8_t usf = 0; uint32_t rts_fn = 4; unsigned int i; RlcMacDownlink_t dl_block; Ack_Nack_Description_t *ack_desc = &dl_block.u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description; /* Submit 14 bytes to fit in 1 RLCMAC block to shorten test and end up in FINISHED state quickly: */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_grr_unitdata_req(tlli, pdu_llc_gmm_att_req, sizeof(pdu_llc_gmm_att_req)); rlcmac_prim->grr.unitdata_req.sapi = OSMO_GPRS_RLCMAC_LLC_SAPI_GMM; rlcmac_prim->grr.unitdata_req.radio_prio = 1; rc = osmo_gprs_rlcmac_prim_upper_down(rlcmac_prim); ccch_imm_ass_pkt_ul_tbf_normal[7] = last_rach_req_ra; /* Update RA to match */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_data_ind(0, ccch_imm_ass_pkt_ul_tbf_normal); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); /* Trigger transmission of LLC data (GMM Attach) (first part) */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ts_nr, rts_fn, usf); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); /* PCU acks it: */ ul_ack_nack_init(&dl_block, ul_tfi, GPRS_RLCMAC_CS_2); ack_desc->STARTING_SEQUENCE_NUMBER = 1; ack_desc->FINAL_ACK_INDICATION = 0; ul_ack_nack_mark(ack_desc, 0, true); rlcmac_prim = create_dl_ctrl_block(&dl_block, ts_nr, rts_fn); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); /* Trigger transmission of LLC data (GMM Attach) (second part) */ rts_fn = fn_next_block(rts_fn); rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ts_nr, rts_fn, usf); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); for (i = 0; i < 4; i++) { rts_fn = fn_next_block(rts_fn); printf("RTS %u: FN=%u\n", i, rts_fn); /* Trigger transmission of LLC data (GMM Attach) (second part) */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ts_nr, rts_fn, usf); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); } printf("=== %s end ===\n", __func__); cleanup_test(); } #endif /* 9.3.1 Countdown procedure */ static void test_ul_tbf_countdown_procedure(void) { struct osmo_gprs_rlcmac_prim *rlcmac_prim; int rc; printf("=== %s start ===\n", __func__); prepare_test(); uint32_t tlli = 0x2342; uint8_t ul_tfi = 0; uint8_t ts_nr = 7; uint8_t usf = 0; uint32_t rts_fn = 4; uint8_t bs_cv_max = 4; unsigned int i; RlcMacDownlink_t dl_block; struct msgb *llc_msg = msgb_alloc(200, "llc_msg"); Ack_Nack_Description_t *ack_desc = &dl_block.u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description; /* Submit an SI13 with bs_cv_max: */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_data_ind(0, create_si13(bs_cv_max)); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); /* Submit LLC bytes */ msgb_put(llc_msg, msgb_tailroom(llc_msg)); memset(msgb_data(llc_msg), 0xab, msgb_length(llc_msg)); rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_grr_unitdata_req(tlli, msgb_data(llc_msg), msgb_length(llc_msg)); rlcmac_prim->grr.unitdata_req.sapi = OSMO_GPRS_RLCMAC_LLC_SAPI_SNDCP3; rlcmac_prim->grr.unitdata_req.radio_prio = 2; rc = osmo_gprs_rlcmac_prim_upper_down(rlcmac_prim); ccch_imm_ass_pkt_ul_tbf_normal[7] = last_rach_req_ra; /* Update RA to match */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_data_ind(0, ccch_imm_ass_pkt_ul_tbf_normal); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); /* Trigger transmission of LLC data (first part) */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ts_nr, rts_fn, usf); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); /* PCU acks it: */ ul_ack_nack_init(&dl_block, ul_tfi, GPRS_RLCMAC_CS_2); ack_desc->STARTING_SEQUENCE_NUMBER = 1; ack_desc->FINAL_ACK_INDICATION = 0; ul_ack_nack_mark(ack_desc, 0, true); rlcmac_prim = create_dl_ctrl_block(&dl_block, ts_nr, rts_fn); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); for (i = 0; i < 6; i++) { rts_fn = fn_next_block(rts_fn); printf("RTS %u: FN=%u\n", i, rts_fn); /* Trigger transmission of LLC data (GMM Attach) (second part) */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ts_nr, rts_fn, usf); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); } printf("=== %s end ===\n", __func__); msgb_free(llc_msg); cleanup_test(); } /* PCU allocates a DL TBF through PCH ImmAss for MS (when in packet-idle) */ static void test_dl_tbf_ccch_assign(void) { struct osmo_gprs_rlcmac_prim *rlcmac_prim; int rc; struct msgb *dl_data_msg; printf("=== %s start ===\n", __func__); prepare_test(); uint32_t ptmsi = 0x00001234; char *imsi = "1234567890"; uint32_t tlli = 0x0000001; uint8_t ts_nr = 7; uint8_t usf = 0; uint32_t rts_fn = 4; uint8_t dl_tfi = 0; uint8_t rrbp = GPRS_RLCMAC_RRBP_N_plus_17_18; /* Notify RLCMAC about our TLLI */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_gmmrr_assign_req(GPRS_RLCMAC_TLLI_UNASSIGNED, tlli); rlcmac_prim->gmmrr.assign_req.ptmsi = ptmsi; OSMO_STRLCPY_ARRAY(rlcmac_prim->gmmrr.assign_req.imsi, imsi); rc = osmo_gprs_rlcmac_prim_upper_down(rlcmac_prim); OSMO_ASSERT(sizeof(ccch_imm_ass_pkt_dl_tbf) == GSM_MACBLOCK_LEN); rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_data_ind(0, ccch_imm_ass_pkt_dl_tbf); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); /* Transmit some DL LLC data MS<-PCU */ dl_data_msg = create_dl_data_block(dl_tfi, usf, GPRS_RLCMAC_CS_1, 0, true, true, rrbp); rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_data_ind(ts_nr, rts_fn, 0, 0, 0, msgb_data(dl_data_msg), msgb_length(dl_data_msg)); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); msgb_free(dl_data_msg); /* Trigger transmission of DL ACK/NACK */ rts_fn = rrbp2fn(rts_fn, rrbp); rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ts_nr, rts_fn, usf); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); printf("=== %s end ===\n", __func__); cleanup_test(); } /* PCU allocates a DL TBF through PCH ImmAss for MS (when in packet-idle). Then * upper layers want to transmit more data so during DL ACK/NACK a new UL TBF is * requested. */ static void test_dl_tbf_ccch_assign_requests_ul_tbf_pacch(void) { struct osmo_gprs_rlcmac_prim *rlcmac_prim; int rc; struct msgb *dl_data_msg; printf("=== %s start ===\n", __func__); prepare_test(); RlcMacDownlink_t dl_block; uint32_t ptmsi = 0x00001234; char *imsi = "1234567890"; uint32_t tlli = 0x0000001; uint8_t ts_nr = 7; uint8_t usf = 0; uint32_t rts_fn = 4; uint8_t dl_tfi = 0; uint8_t ul_tfi = 3; uint8_t rrbp = GPRS_RLCMAC_RRBP_N_plus_17_18; uint16_t arfcn = 871; uint8_t usf_li[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 1, 2 }; /* Notify RLCMAC about our TLLI */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_gmmrr_assign_req(GPRS_RLCMAC_TLLI_UNASSIGNED, tlli); rlcmac_prim->gmmrr.assign_req.ptmsi = ptmsi; OSMO_STRLCPY_ARRAY(rlcmac_prim->gmmrr.assign_req.imsi, imsi); rc = osmo_gprs_rlcmac_prim_upper_down(rlcmac_prim); OSMO_ASSERT(sizeof(ccch_imm_ass_pkt_dl_tbf) == GSM_MACBLOCK_LEN); rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_data_ind(0, ccch_imm_ass_pkt_dl_tbf); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); /* Transmit some DL LLC data MS<-PCU */ dl_data_msg = create_dl_data_block(dl_tfi, usf, GPRS_RLCMAC_CS_1, 0, true, true, rrbp); rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_data_ind(ts_nr, rts_fn, 0, 0, 0, msgb_data(dl_data_msg), msgb_length(dl_data_msg)); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); msgb_free(dl_data_msg); /* Upper layers wants to transmit some payload, but no UL TBF exists yet: */ /* Submit 14 bytes to fit in 1 RLCMAC block to shorten test and end up in FINISHED state quickly: */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_grr_unitdata_req(tlli, pdu_llc_gmm_att_req, 14); rlcmac_prim->grr.unitdata_req.sapi = OSMO_GPRS_RLCMAC_LLC_SAPI_GMM; rlcmac_prim->grr.unitdata_req.radio_prio = 1; rc = osmo_gprs_rlcmac_prim_upper_down(rlcmac_prim); OSMO_ASSERT(rc == 0); /* Trigger transmission of DL ACK/NACK, which should request a UL TBF in "Channel Request Description" */ rts_fn = rrbp2fn(rts_fn, rrbp); rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ts_nr, rts_fn, usf); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); /* Network sends a Pkt Ul Ass to DL TBF's PACCH: */ rts_fn = fn_next_block(rts_fn); pkt_ul_ass_from_dl_tbf_init(&dl_block, dl_tfi, ul_tfi, arfcn, &usf_li[0]); /* has Poll set: */ dl_block.SP = 1; dl_block.RRBP = rrbp; rlcmac_prim = create_dl_ctrl_block(&dl_block, ts_nr, rts_fn); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); /* Trigger transmission of PKT CTRL ACK */ rts_fn = rrbp2fn(rts_fn, rrbp); rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ts_nr, rts_fn, usf_li[ts_nr]); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); /* from now on use one of the assigned TS in UL TBF.*/ rts_fn = fn_next_block(rts_fn); ts_nr = 6; /* FIXME: see extra fn_next_block() in libosmo-gprs-rlcmac st_wait_tbf_starting_time2() */ rts_fn = fn_next_block(rts_fn); /* Trigger transmission of LLC data (GMM Attach) (first part) */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ts_nr, rts_fn, usf_li[ts_nr]); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); printf("=== %s end ===\n", __func__); cleanup_test(); } /* SGSN->PCU->BTS --PCH--> MS containing "Paging Request Type 1" asking for PS services. * RLCMAC will send GMMRR-PAGE.ind to GMM layer, which is in charge of orchestrating the response. */ static void test_ccch_pag_req1(void) { struct osmo_gprs_rlcmac_prim *rlcmac_prim; int rc; printf("=== %s start ===\n", __func__); prepare_test(); uint32_t ptmsi = 0x00001234; char *imsi = "262420000000423"; uint32_t tlli = 0x0000001; /* Notify RLCMAC about our TLLI */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_gmmrr_assign_req(GPRS_RLCMAC_TLLI_UNASSIGNED, tlli); rlcmac_prim->gmmrr.assign_req.ptmsi = ptmsi; OSMO_STRLCPY_ARRAY(rlcmac_prim->gmmrr.assign_req.imsi, imsi); rc = osmo_gprs_rlcmac_prim_upper_down(rlcmac_prim); OSMO_ASSERT(sizeof(ccch_pag_req_1) == GSM_MACBLOCK_LEN); rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_data_ind(0, ccch_pag_req_1); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); /* Above prim is expected to trigger RLCMAC layer submitting GMMRR-Page.ind as here. */ /* Here GMM would start UL TBF through LLGM-TRIGGER.req(PAGE_RESPONSE), * and LLC in turn submits GRR-UNITDATA.req */ printf("=== %s end ===\n", __func__); cleanup_test(); } /* Validate 2 LLC frames (GMM Attach Compl + SM Act PDP Context Req) in the tx * queue are transmitted in 1 UL TBF in CS4 (OS#6351): */ static void test_ul_tbf_2_llc_blocks_in_1_cs4_rlc_block(void) { struct osmo_gprs_rlcmac_prim *rlcmac_prim; int rc; printf("=== %s start ===\n", __func__); prepare_test(); uint32_t tlli = 0x2342; uint8_t ts_nr = 7; uint8_t usf = 0; uint32_t rts_fn = 4; uint8_t bs_cv_max = 15; struct msgb *llc_msg = msgb_alloc(200, "llc_msg"); uint8_t imm_ass[sizeof(ccch_imm_ass_pkt_ul_tbf_normal)]; /* Submit an SI13 with bs_cv_max: */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_data_ind(0, create_si13(bs_cv_max)); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); /* Submit LLC bytes containing GMM ATTACH COMPL */ memcpy(msgb_put(llc_msg, sizeof(pdu_llc_gmm_att_compl)), pdu_llc_gmm_att_compl, sizeof(pdu_llc_gmm_att_compl)); rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_grr_unitdata_req(tlli, msgb_data(llc_msg), msgb_length(llc_msg)); rlcmac_prim->grr.unitdata_req.sapi = OSMO_GPRS_RLCMAC_LLC_SAPI_GMM; rlcmac_prim->grr.unitdata_req.radio_prio = 1; rc = osmo_gprs_rlcmac_prim_upper_down(rlcmac_prim); OSMO_ASSERT(rc == 0); /* Submit LLC bytes containing SM ACT PDP CTX REQ */ msgb_trim(llc_msg, 0); memcpy(msgb_put(llc_msg, sizeof(pdu_llc_sm_act_pdp_ctx_req)), pdu_llc_sm_act_pdp_ctx_req, sizeof(pdu_llc_sm_act_pdp_ctx_req)); /* -4: The scenario where this was seen is actually reproduced by having * a DL TBF sending GMM Attach Accept, and MS requesting a UL TBF through * DL AKC/NACK and getting it assigned over PACCH. As a result, no * contention resolution is needed and the UL TBF block doesn't contain * TLLI. Since it's far easy writing here a test by directlly allocating * the UL TBF, we simply account for the extra TLLI added in the RLC block * by discounting 4 bytes from the upper LLC layer payload to end up with * the same packet size as the one reproduced in the real case scenario. */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_grr_unitdata_req(tlli, msgb_data(llc_msg), msgb_length(llc_msg) - 4); rlcmac_prim->grr.unitdata_req.sapi = OSMO_GPRS_RLCMAC_LLC_SAPI_GMM; rlcmac_prim->grr.unitdata_req.radio_prio = 1; rc = osmo_gprs_rlcmac_prim_upper_down(rlcmac_prim); OSMO_ASSERT(rc == 0); memcpy(imm_ass, ccch_imm_ass_pkt_ul_tbf_normal, sizeof(imm_ass)); imm_ass[7] = last_rach_req_ra; /* Update RA to match */ imm_ass[14] = 0x70; /* Set CS4 */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_data_ind(0, imm_ass); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); /* Trigger transmission of LLC data (first part) */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ts_nr, rts_fn, usf); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); printf("=== %s end ===\n", __func__); msgb_free(llc_msg); cleanup_test(); } static const struct log_info_cat test_log_categories[] = { }; static const struct log_info test_log_info = { .cat = test_log_categories, .num_cat = ARRAY_SIZE(test_log_categories), }; int main(int argc, char *argv[]) { tall_ctx = talloc_named_const(NULL, 1, __FILE__); osmo_init_logging2(tall_ctx, &test_log_info); log_parse_category_mask(osmo_stderr_target, "DLGLOBAL,1:"); osmo_fsm_log_addr(false); log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE); log_set_print_category_hex(osmo_stderr_target, 0); log_set_print_category(osmo_stderr_target, 1); log_set_print_level(osmo_stderr_target, 1); log_set_use_color(osmo_stderr_target, 0); clock_override_enable(true); test_ul_tbf_attach(); test_ul_tbf_t3164_timeout(); test_ul_tbf_t3166_timeout(); test_ul_tbf_t3180_timeout(); test_ul_tbf_n3104_timeout(); test_ul_tbf_t3182_timeout(true); #if 0 test_ul_tbf_t3182_timeout(false); test_ul_tbf_unack_mode_last_data_cv0_retrans_max(); #endif test_ul_tbf_countdown_procedure(); test_ul_tbf_request_another_ul_tbf(); test_dl_tbf_ccch_assign(); test_dl_tbf_ccch_assign_requests_ul_tbf_pacch(); test_ccch_pag_req1(); test_ul_tbf_2_llc_blocks_in_1_cs4_rlc_block(); talloc_free(tall_ctx); }