/* TETRA upper MAC layer main routine, above TMV-SAP */ /* (C) 2011 by Harald Welte * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include "tetra_common.h" #include "tetra_prim.h" #include "tetra_upper_mac.h" #include "tetra_mac_pdu.h" #include "tetra_mle_pdu.h" #include "tetra_mm_pdu.h" #include "tetra_gsmtap.h" static void rx_bcast(struct tetra_tmvsap_prim *tmvp) { struct tmv_unitdata_param *tup = &tmvp->u.unitdata; struct tetra_si_decoded sid; uint32_t dl_freq, ul_freq; int i; memset(&sid, 0, sizeof(sid)); macpdu_decode_sysinfo(&sid, tup->mac_block); dl_freq = tetra_dl_carrier_hz(sid.freq_band, sid.main_carrier, sid.freq_offset); ul_freq = tetra_ul_carrier_hz(sid.freq_band, sid.main_carrier, sid.freq_offset, sid.duplex_spacing, sid.reverse_operation); printf("BNCH SYSINFO (DL %u Hz, UL %u Hz), service_details 0x%04x ", dl_freq, ul_freq, sid.mle_si.bs_service_details); if (sid.cck_valid_no_hf) printf("CCK ID %u", sid.cck_id); else printf("Hyperframe %u", sid.hyperframe_number); printf("\n"); for (i = 0; i < 12; i++) printf("\t%s: %u\n", tetra_get_bs_serv_det_name(1 << i), sid.mle_si.bs_service_details & (1 << i) ? 1 : 0); } const char *tetra_alloc_dump(const struct tetra_chan_alloc_decoded *cad) { static char buf[64]; char *cur = buf; cur += sprintf(cur, "%s (TN%u/%s/%uHz)", tetra_get_alloc_t_name(cad->type), cad->timeslot, tetra_get_ul_dl_name(cad->ul_dl), tetra_dl_carrier_hz(cad->ext_carr.freq_band, cad->carrier_nr, cad->ext_carr.freq_offset)); return buf; } static int rx_tm_sdu(uint8_t *bits, unsigned int len) { uint8_t mle_pdisc = bits_to_uint(bits, 3); printf("TM-SDU(%s): %s", tetra_get_mle_pdisc_name(mle_pdisc), bitdump(bits, len)); switch (mle_pdisc) { case TMLE_PDISC_MM: printf(" %s", tetra_get_mm_pdut_name(bits_to_uint(bits+3, 4), 0)); break; default: break; } return len; } static void rx_resrc(struct tetra_tmvsap_prim *tmvp) { struct tmv_unitdata_param *tup = &tmvp->u.unitdata; struct tetra_resrc_decoded rsd; int tmpdu_offset; memset(&rsd, 0, sizeof(rsd)); tmpdu_offset = macpdu_decode_resource(&rsd, tup->mac_block); printf("RESOURCE Encr=%u, Length_ind=%u Addr=%s ", rsd.encryption_mode, rsd.length_ind, tetra_addr_dump(&rsd.addr)); if (rsd.chan_alloc_pres) printf("ChanAlloc=%s ", tetra_alloc_dump(&rsd.cad)); if (rsd.length_ind && rsd.encryption_mode == 0) { int len_bits = rsd.length_ind*8; if (tup->mac_block + tmpdu_offset + len_bits > tup->mac_block + tup->mac_block_len) len_bits = tup->mac_block_len - tmpdu_offset; rx_tm_sdu(tup->mac_block + tmpdu_offset, len_bits); } printf("\n"); } static void dump_access(struct tetra_access_field *acc, unsigned int num) { printf("ACCESS%u: %c/%u ", num, 'A'+acc->access_code, acc->base_frame_len); } static void rx_aach(struct tetra_tmvsap_prim *tmvp) { struct tmv_unitdata_param *tup = &tmvp->u.unitdata; struct tetra_acc_ass_decoded aad; printf("ACCESS-ASSIGN PDU: "); memset(&aad, 0, sizeof(aad)); macpdu_decode_access_assign(&aad, tup->mac_block, tup->tdma_time.fn == 18 ? 1 : 0); if (aad.pres & TETRA_ACC_ASS_PRES_ACCESS1) dump_access(&aad.access[0], 1); if (aad.pres & TETRA_ACC_ASS_PRES_ACCESS2) dump_access(&aad.access[1], 2); if (aad.pres & TETRA_ACC_ASS_PRES_DL_USAGE) printf("DL_USAGE: %s ", tetra_get_dl_usage_name(aad.dl_usage)); if (aad.pres & TETRA_ACC_ASS_PRES_UL_USAGE) printf("UL_USAGE: %s ", tetra_get_ul_usage_name(aad.ul_usage)); printf("\n"); } static int rx_tmv_unitdata_ind(struct tetra_tmvsap_prim *tmvp) { struct tmv_unitdata_param *tup = &tmvp->u.unitdata; uint8_t pdu_type = bits_to_uint(tup->mac_block, 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(tup->mac_block, 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, tup->mac_block, tup->mac_block_len); if (gsmtap_msg) tetra_gsmtap_sendmsg(gsmtap_msg); switch (tup->lchan) { case TETRA_LC_AACH: rx_aach(tmvp); break; case TETRA_LC_BNCH: case TETRA_LC_UNKNOWN: case TETRA_LC_SCH_F: if (pdu_type == TETRA_PDU_T_BROADCAST) rx_bcast(tmvp); if (pdu_type == TETRA_PDU_T_MAC_RESOURCE) rx_resrc(tmvp); break; } if (pdu_type == TETRA_PDU_T_MAC_FRAG_END) { if (tup->mac_block[3] == TETRA_MAC_FRAGE_FRAG) rx_tm_sdu(tup->mac_block+4, 100 /*FIXME*/); } return 0; } int upper_mac_prim_recv(struct osmo_prim_hdr *op, void *priv) { struct tetra_tmvsap_prim *tmvp; int rc; switch (op->sap) { case TETRA_SAP_TMV: tmvp = (struct tetra_tmvsap_prim *) op; rc = rx_tmv_unitdata_ind(tmvp); break; default: printf("primitive on unknown sap\n"); break; } free(op); return rc; }