static void decode_d_mle_sysinfo(struct tetra_mle_si_decoded *msid, const uint8_t *bits) { const uint8_t *cur = bits; msid->la = bits_to_uint(cur, 14); cur += 14; msid->subscr_class = bits_to_uint(cur, 16); cur += 16; msid->bs_service_details = bits_to_uint(cur, 12); cur += 12; }
static int rx_tmv_unitdata_ind(struct tetra_tmvsap_prim *tmvp, struct tetra_mac_state *tms) { struct tmv_unitdata_param *tup = &tmvp->u.unitdata; struct msgb *msg = tmvp->oph.msg; uint8_t pdu_type = bits_to_uint(msg->l1h, 2); const char *pdu_name; struct msgb *gsmtap_msg; if (tup->lchan == TETRA_LC_BSCH) pdu_name = "SYNC"; else if (tup->lchan == TETRA_LC_AACH) pdu_name = "ACCESS-ASSIGN"; else { pdu_type = bits_to_uint(msg->l1h, 2); pdu_name = tetra_get_macpdu_name(pdu_type); } printf("TMV-UNITDATA.ind %s %s CRC=%u %s\n", tetra_tdma_time_dump(&tup->tdma_time), tetra_get_lchan_name(tup->lchan), tup->crc_ok, pdu_name); if (!tup->crc_ok) return 0; gsmtap_msg = tetra_gsmtap_makemsg(&tup->tdma_time, tup->lchan, tup->tdma_time.tn, /* FIXME: */ 0, 0, 0, msg->l1h, msgb_l1len(msg)); if (gsmtap_msg) tetra_gsmtap_sendmsg(gsmtap_msg); switch (tup->lchan) { case TETRA_LC_AACH: rx_aach(tmvp, tms); break; case TETRA_LC_BNCH: case TETRA_LC_UNKNOWN: case TETRA_LC_SCH_F: switch (pdu_type) { case TETRA_PDU_T_BROADCAST: rx_bcast(tmvp, tms); break; case TETRA_PDU_T_MAC_RESOURCE: rx_resrc(tmvp, tms); break; case TETRA_PDU_T_MAC_SUPPL: rx_suppl(tmvp, tms); break; case TETRA_PDU_T_MAC_FRAG_END: if (msg->l1h[3] == TETRA_MAC_FRAGE_FRAG) { printf("FRAG/END FRAG: "); msg->l2h = msg->l1h+4; rx_tm_sdu(tms, msg, 100 /*FIXME*/); printf("\n"); } else printf("FRAG/END END\n"); break; default: printf("STRANGE pdu=%u\n", pdu_type); break; } break; case TETRA_LC_BSCH: break; default: printf("STRANGE lchan=%u\n", tup->lchan); break; } return 0; }
/* Receive TL-SDU (LLC SDU == MLE PDU) */ static int rx_tl_sdu(struct tetra_mac_state *tms, struct msgb *msg, unsigned int len) { uint8_t *bits = msg->l3h; uint8_t mle_pdisc = bits_to_uint(bits, 3); printf("TL-SDU(%s): %s", tetra_get_mle_pdisc_name(mle_pdisc), osmo_ubit_dump(bits, len)); switch (mle_pdisc) { case TMLE_PDISC_MM: printf(" %s", tetra_get_mm_pdut_name(bits_to_uint(bits+3, 4), 0)); break; case TMLE_PDISC_CMCE: printf(" %s", tetra_get_cmce_pdut_name(bits_to_uint(bits+3, 5), 0)); break; case TMLE_PDISC_SNDCP: printf(" %s", tetra_get_sndcp_pdut_name(bits_to_uint(bits+3, 4), 0)); printf(" NSAPI=%u PCOMP=%u, DCOMP=%u", bits_to_uint(bits+3+4, 4), bits_to_uint(bits+3+4+4, 4), bits_to_uint(bits+3+4+4+4, 4)); printf(" V%u, IHL=%u", bits_to_uint(bits+3+4+4+4+4, 4), 4*bits_to_uint(bits+3+4+4+4+4+4, 4)); printf(" Proto=%u", bits_to_uint(bits+3+4+4+4+4+4+4+64, 8)); break; case TMLE_PDISC_MLE: printf(" %s", tetra_get_mle_pdut_name(bits_to_uint(bits+3, 3), 0)); break; default: break; } return len; }
/* 21.5.2 */ static int decode_chan_alloc(struct tetra_chan_alloc_decoded *cad, const uint8_t *bits) { const uint8_t *cur = bits; cad->type = bits_to_uint(cur, 2); cur += 2; cad->timeslot = bits_to_uint(cur, 4); cur += 4; cad->ul_dl = bits_to_uint(cur, 2); cur += 2; cad->clch_perm = *cur++; cad->cell_chg_f = *cur++; cad->carrier_nr = bits_to_uint(cur, 12); cur += 12; cad->ext_carr_pres = *cur++; if (cad->ext_carr_pres) { cad->ext_carr.freq_band = bits_to_uint(cur, 4); cur += 4; cad->ext_carr.freq_offset = bits_to_uint(cur, 2); cur += 2; cad->ext_carr.duplex_spc = bits_to_uint(cur, 3); cur += 3; cad->ext_carr.reverse_oper = bits_to_uint(cur, 1); cur += 1; } cad->monit_pattern = bits_to_uint(cur, 2); cur += 2; if (cad->monit_pattern == 0) { cad->monit_patt_f18 = bits_to_uint(cur, 2); cur += 2; } if (cad->ul_dl == 0) { cad->aug.ul_dl_ass = bits_to_uint(cur, 2); cur += 2; cad->aug.bandwidth = bits_to_uint(cur, 3); cur += 3; cad->aug.modulation = bits_to_uint(cur, 3); cur += 3; cad->aug.max_ul_qam = bits_to_uint(cur, 3); cur += 3; cur += 3; /* reserved */ cad->aug.conf_chan_stat=bits_to_uint(cur, 3); cur += 3; cad->aug.bs_imbalance = bits_to_uint(cur, 4); cur += 4; cad->aug.bs_tx_rel = bits_to_uint(cur, 5); cur += 5; cad->aug.napping_sts = bits_to_uint(cur, 2); cur += 2; if (cad->aug.napping_sts == 1) cur += 11; /* napping info 21.5.2c */ cur += 4; /* reserved */ if (*cur++) cur += 16; if (*cur++) cur += 16; cur++; } return cur - bits; }
void macpdu_decode_sysinfo(struct tetra_si_decoded *sid, const uint8_t *si_bits) { const uint8_t *cur = si_bits + 4; sid->main_carrier = bits_to_uint(cur, 12); cur += 12; sid->freq_band = bits_to_uint(cur, 4); cur += 4; sid->freq_offset = bits_to_uint(cur, 2); cur += 2; sid->duplex_spacing = bits_to_uint(cur, 3); cur += 3; sid->reverse_operation = *cur++; sid->num_of_csch = bits_to_uint(cur, 2); cur +=2; sid->ms_txpwr_max_cell = bits_to_uint(cur, 3); cur += 3; sid->rxlev_access_min = bits_to_uint(cur, 4); cur += 4; sid->access_parameter = bits_to_uint(cur, 4); cur += 4; sid->radio_dl_timeout = bits_to_uint(cur, 4); cur += 4; sid->cck_valid_no_hf = *cur++; if (sid->cck_valid_no_hf) sid->cck_id = bits_to_uint(cur, 16); else sid->hyperframe_number = bits_to_uint(cur, 16); cur += 16; /* FIXME: more */ decode_d_mle_sysinfo(&sid->mle_si, si_bits + 124-42); }
/* Section 21.4.7.2 ACCESS-ASSIGN PDU */ void macpdu_decode_access_assign(struct tetra_acc_ass_decoded *aad, const uint8_t *bits, int f18) { uint8_t field1, field2; aad->hdr = bits_to_uint(bits, 2); field1 = bits_to_uint(bits+2, 6); field2 = bits_to_uint(bits+8, 6); if (f18 == 0) { switch (aad->hdr) { case TETRA_ACC_ASS_DLCC_ULCO: /* Field 1 and Field2 are Access fields */ decode_access_field(&aad->access[0], field1); decode_access_field(&aad->access[1], field2); aad->pres |= TETRA_ACC_ASS_PRES_ACCESS1; aad->pres |= TETRA_ACC_ASS_PRES_ACCESS2; break; case TETRA_ACC_ASS_DLF1_ULCA: /* Field1: DL usage marker */ aad->dl_usage = field1; aad->pres |= TETRA_ACC_ASS_PRES_DL_USAGE; /* Field2: Access field */ decode_access_field(&aad->access[1], field2); aad->pres |= TETRA_ACC_ASS_PRES_ACCESS2; break; case TETRA_ACC_ASS_DLF1_ULAO: /* Field1: DL usage marker */ aad->dl_usage = field1; aad->pres |= TETRA_ACC_ASS_PRES_DL_USAGE; /* Field2: Access field */ decode_access_field(&aad->access[1], field2); aad->pres |= TETRA_ACC_ASS_PRES_ACCESS2; break; case TETRA_ACC_ASS_DLF1_ULF1: /* Field1: DL usage marker */ aad->dl_usage = field1; aad->pres |= TETRA_ACC_ASS_PRES_DL_USAGE; /* Field2: UL usage marker */ aad->ul_usage = field2; aad->pres |= TETRA_ACC_ASS_PRES_UL_USAGE; break; } } else { switch (aad->hdr) { case TETRA_ACC_ASS_ULCO: /* Field1 and Field2: Access field */ decode_access_field(&aad->access[0], field1); decode_access_field(&aad->access[1], field2); aad->pres |= TETRA_ACC_ASS_PRES_ACCESS1; aad->pres |= TETRA_ACC_ASS_PRES_ACCESS2; break; case TETRA_ACC_ASS_ULCA: /* Field1 and Field2: Access field */ decode_access_field(&aad->access[0], field1); decode_access_field(&aad->access[1], field2); aad->pres |= TETRA_ACC_ASS_PRES_ACCESS1; aad->pres |= TETRA_ACC_ASS_PRES_ACCESS2; break; case TETRA_ACC_ASS_ULAO: /* Field1 and Field2: Access field */ decode_access_field(&aad->access[0], field1); decode_access_field(&aad->access[1], field2); aad->pres |= TETRA_ACC_ASS_PRES_ACCESS1; aad->pres |= TETRA_ACC_ASS_PRES_ACCESS2; break; case TETRA_ACC_ASS_ULCA2: /* Field1: Traffic usage marker (UMt) */ /* FIXME */ /* Field2: Access field */ decode_access_field(&aad->access[1], field2); aad->pres |= TETRA_ACC_ASS_PRES_ACCESS2; break; } } }
/* Section 21.4.3.1 MAC-RESOURCE */ int macpdu_decode_resource(struct tetra_resrc_decoded *rsd, const uint8_t *bits) { const uint8_t *cur = bits + 4; rsd->encryption_mode = bits_to_uint(cur, 2); cur += 2; rsd->rand_acc_flag = *cur++; rsd->macpdu_length = decode_length(bits_to_uint(cur, 6)); cur += 6; rsd->addr.type = bits_to_uint(cur, 3); cur += 3; switch (rsd->addr.type) { case ADDR_TYPE_NULL: return 0; break; case ADDR_TYPE_SSI: case ADDR_TYPE_USSI: case ADDR_TYPE_SMI: rsd->addr.ssi = bits_to_uint(cur, 24); break; case ADDR_TYPE_EVENT_LABEL: rsd->addr.event_label = bits_to_uint(cur, 10); break; case ADDR_TYPE_SSI_EVENT: case ADDR_TYPE_SMI_EVENT: rsd->addr.ssi = bits_to_uint(cur, 24); rsd->addr.event_label = bits_to_uint(cur+24, 10); break; case ADDR_TYPE_SSI_USAGE: rsd->addr.ssi = bits_to_uint(cur, 24); rsd->addr.usage_marker = bits_to_uint(cur+24, 6); break; default: return -EINVAL; break; } cur += addr_len_by_type[rsd->addr.type]; /* no intermediate napping in pi/4 */ rsd->power_control_pres = *cur++; if (rsd->power_control_pres) cur += 4; rsd->slot_granting.pres = *cur++; if (rsd->slot_granting.pres) { #if 0 /* check for multiple slot granting flag (can only exist in QAM) */ if (*cur++) { cur += 0; //FIXME; } else { #endif rsd->slot_granting.nr_slots = decode_nr_slots(bits_to_uint(cur, 4)); cur += 4; rsd->slot_granting.delay = bits_to_uint(cur, 4); cur += 4; #if 0 } #endif } rsd->chan_alloc_pres = *cur++; /* FIXME: If encryption is enabled, Channel Allocation is encrypted !!! */ if (rsd->chan_alloc_pres) cur += decode_chan_alloc(&rsd->cad, cur); /* FIXME: TM-SDU */ return cur - bits; }