/* TETRA lower MAC layer main routine, between TP-SAP and 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 #include #include #include #include #include #include #include #include #include #include #include #include #include "tetra_upper_mac.h" #include #include struct tetra_blk_param { const char *name; uint16_t type345_bits; uint16_t type2_bits; uint16_t type1_bits; uint16_t interleave_a; uint8_t have_crc16; }; /* try to aggregate all of the magic numbers somewhere central */ static const struct tetra_blk_param tetra_blk_param[] = { [TPSAP_T_SB1] = { .name = "SB1", .type345_bits = 120, .type2_bits = 80, .type1_bits = 60, .interleave_a = 11, .have_crc16 = 1, }, [TPSAP_T_SB2] = { .name = "SB2", .type345_bits = 216, .type2_bits = 144, .type1_bits = 124, .interleave_a = 101, .have_crc16 = 1, }, [TPSAP_T_NDB] = { .name = "NDB", .type345_bits = 216, .type2_bits = 144, .type1_bits = 124, .interleave_a = 101, .have_crc16 = 1, }, [TPSAP_T_SCH_HU] = { .name = "SCH/HU", .type345_bits = 168, .type2_bits = 112, .type1_bits = 92, .interleave_a = 13, .have_crc16 = 1, }, [TPSAP_T_SCH_F] = { .name = "SCH/F", .type345_bits = 432, .type2_bits = 288, .type1_bits = 268, .interleave_a = 103, .have_crc16 = 1, }, [TPSAP_T_BBK] = { .name = "BBK", .type345_bits = 30, .type2_bits = 30, .type1_bits = 14, }, }; struct tetra_cell_data { uint16_t mcc; uint16_t mnc; uint8_t colour_code; struct tetra_tdma_time time; uint32_t scramb_init; }; static struct tetra_cell_data _tcd, *tcd = &_tcd; int is_bsch(struct tetra_tdma_time *tm) { if (tm->fn == 18 && tm->tn == 4 - ((tm->mn+1)%4)) return 1; return 0; } int is_bnch(struct tetra_tdma_time *tm) { if (tm->fn == 18 && tm->tn == 4 - ((tm->mn+3)%4)) return 1; return 0; } struct tetra_tmvsap_prim *tmvsap_prim_alloc(uint16_t prim, uint8_t op) { struct tetra_tmvsap_prim *ttp; ttp = talloc_zero(NULL, struct tetra_tmvsap_prim); ttp->oph.msg = msgb_alloc(412, "tmvsap_prim"); ttp->oph.sap = TETRA_SAP_TMV; ttp->oph.primitive = prim; ttp->oph.operation = op; return ttp; } /* incoming TP-SAP UNITDATA.ind from PHY into lower MAC */ void tp_sap_udata_ind(enum tp_sap_data_type type, int blk_num, const uint8_t *bits, unsigned int len, void *priv) { /* various intermediary buffers */ uint8_t type4[512]; uint8_t type3dp[512*4]; uint8_t type3[512]; uint8_t type2[512]; const struct tetra_blk_param *tbp = &tetra_blk_param[type]; struct tetra_mac_state *tms = priv; struct tetra_crypto_state *tcs = tms->tcs; const char *time_str; /* TMV-SAP.UNITDATA.ind primitive which we will send to the upper MAC */ struct tetra_tmvsap_prim *ttp; struct tmv_unitdata_param *tup; struct msgb *msg; ttp = tmvsap_prim_alloc(PRIM_TMV_UNITDATA, PRIM_OP_INDICATION); tup = &ttp->u.unitdata; msg = ttp->oph.msg; /* update the cell time */ memcpy(&tcd->time, &t_phy_state.time, sizeof(tcd->time)); time_str = tetra_tdma_time_dump(&tcd->time); if (type == TPSAP_T_SB2 && is_bnch(&tcd->time)) { tup->lchan = TETRA_LC_BNCH; printf("BNCH FOLLOWS\n"); } DEBUGP("%s %s type5: %s\n", tbp->name, tetra_tdma_time_dump(&tcd->time), osmo_ubit_dump(bits, tbp->type345_bits)); /* De-scramble, pay special attention to SB1 pre-defined scrambling */ memcpy(type4, bits, tbp->type345_bits); if (type == TPSAP_T_SB1) { tetra_scramb_bits(SCRAMB_INIT, type4, tbp->type345_bits); tup->scrambling_code = SCRAMB_INIT; } else { tetra_scramb_bits(tcd->scramb_init, type4, tbp->type345_bits); tup->scrambling_code = tcd->scramb_init; } DEBUGP("%s %s type4: %s\n", tbp->name, time_str, osmo_ubit_dump(type4, tbp->type345_bits)); /* Handle block 1 slot stealing, see clause 19.4.4 */ /* Block 1 is stolen if AACH says slot is traffic and burst used training sequence 1 */ /* Block 2 is stolen if indicated in stolen block 1 resource len (-2) */ if (tms->cur_burst.is_traffic && type == TPSAP_T_NDB && blk_num == BLK_1) tms->cur_burst.blk1_stolen = true; /* If this is a traffic channel, dump. */ if (tms->cur_burst.is_traffic && (type == TPSAP_T_SCH_F || (blk_num == BLK_2 && !tms->cur_burst.blk2_stolen))) { char fname[PATH_MAX]; int16_t block[690]; FILE *f; int i; /* Open target file */ snprintf(fname, sizeof(fname), "%s/traffic_%d_%d.out", tms->dumpdir, tms->cur_burst.is_traffic, tms->tsn); f = fopen(fname, "ab"); if (!f) { fprintf(stderr, "Could not open dump file %s for writing: %s\n", fname, strerror(errno)); exit(1); } /* Generate a block */ memset(block, 0x00, sizeof(int16_t) * 690); for (i = 0; i < 6; i++) block[115*i] = 0x6b21 + i; for (i = 0; i < 114; i++) block[1+i] = type4[i] ? -127 : 127; for (i = 0; i < 114; i++) block[116+i] = type4[114+i] ? -127 : 127; for (i = 0; i < 114; i++) block[231+i] = type4[228+i] ? -127 : 127; for (i = 0; i < 90; i++) block[346+i] = type4[342+i] ? -127 : 127; /* Write it */ fwrite(block, sizeof(int16_t), 690, f); fclose(f); /* Write used ssi */ snprintf(fname, sizeof(fname), "%s/traffic_%d_%d.txt", tms->dumpdir, tms->cur_burst.is_traffic, tms->tsn); f = fopen(fname, "a"); fprintf(f, "%d\n", tms->ssi); fclose(f); goto out; } if (tbp->interleave_a) { /* Run block deinterleaving: type-3 bits */ block_deinterleave(tbp->type345_bits, tbp->interleave_a, type4, type3); DEBUGP("%s %s type3: %s\n", tbp->name, time_str, osmo_ubit_dump(type3, tbp->type345_bits)); /* De-puncture */ memset(type3dp, 0xff, sizeof(type3dp)); tetra_rcpc_depunct(TETRA_RCPC_PUNCT_2_3, type3, tbp->type345_bits, type3dp); DEBUGP("%s %s type3dp: %s\n", tbp->name, time_str, osmo_ubit_dump(type3dp, tbp->type2_bits*4)); viterbi_dec_sb1_wrapper(type3dp, type2, tbp->type2_bits); DEBUGP("%s %s type2: %s\n", tbp->name, time_str, osmo_ubit_dump(type2, tbp->type2_bits)); } if (tbp->have_crc16) { uint16_t crc = crc16_ccitt_bits(type2, tbp->type1_bits+16); printf("CRC COMP: 0x%04x ", crc); if (crc == TETRA_CRC_OK) { printf("OK\n"); tup->crc_ok = 1; printf("%s %s type1: %s\n", tbp->name, time_str, osmo_ubit_dump(type2, tbp->type1_bits)); } else printf("WRONG\n"); } else if (type == TPSAP_T_BBK) { /* FIXME: RM3014-decode */ tup->crc_ok = 1; memcpy(type2, type4, tbp->type2_bits); DEBUGP("%s %s type1: %s\n", tbp->name, time_str, osmo_ubit_dump(type2, tbp->type1_bits)); } /* Set whether BLK1 or BLK2 in downlink burst (or 0 if not applicable) */ tup->blk_num = blk_num; msg->l1h = msgb_put(msg, tbp->type1_bits); memcpy(msg->l1h, type2, tbp->type1_bits); switch (type) { case TPSAP_T_SB1: printf("TMB-SAP SYNC CC %s(0x%02x) ", osmo_ubit_dump(type2+4, 6), bits_to_uint(type2+4, 6)); printf("TN %s(%u) ", osmo_ubit_dump(type2+10, 2), bits_to_uint(type2+10, 2) + 1); printf("FN %s(%2u) ", osmo_ubit_dump(type2+12, 5), bits_to_uint(type2+12, 5)); printf("MN %s(%2u) ", osmo_ubit_dump(type2+17, 6), bits_to_uint(type2+17, 6)); printf("MCC %s(%u) ", osmo_ubit_dump(type2+31, 10), bits_to_uint(type2+31, 10)); printf("MNC %s(%u)\n", osmo_ubit_dump(type2+41, 14), bits_to_uint(type2+41, 14)); /* obtain information from SYNC PDU */ if (tup->crc_ok) { tcd->colour_code = bits_to_uint(type2+4, 6); tcd->time.tn = bits_to_uint(type2+10, 2) + 1; tcd->time.fn = bits_to_uint(type2+12, 5); tcd->time.mn = bits_to_uint(type2+17, 6); tcd->mcc = bits_to_uint(type2+31, 10); tcd->mnc = bits_to_uint(type2+41, 14); /* compute the scrambling code for the current cell */ tcd->scramb_init = tetra_scramb_get_init(tcd->mcc, tcd->mnc, tcd->colour_code); } /* update the PHY layer time */ memcpy(&t_phy_state.time, &tcd->time, sizeof(t_phy_state.time)); tup->lchan = TETRA_LC_BSCH; /* Update colour code and network info for crypto IV generation */ tcs->cc = tcd->colour_code; if (tcs->mcc != tcd->mcc || tcs->mnc != tcd->mnc) update_current_network(tcs, tcd->mcc, tcd->mnc); break; case TPSAP_T_SB2: case TPSAP_T_NDB: /* FIXME: do something */ break; case TPSAP_T_BBK: tup->lchan = TETRA_LC_AACH; break; case TPSAP_T_SCH_F: tup->lchan = TETRA_LC_SCH_F; break; default: /* FIXME: do something */ break; } int pdu_bits = 0; uint32_t offset = 0; uint8_t *orig_head = msg->head; /* The true start of the timeslot */ uint8_t *orig_tail = msg->tail; /* The true end of the timeslot */ while (offset < tbp->type1_bits - 16) { /* send Rx time along with the TMV-UNITDATA.ind primitive */ memcpy(&tup->tdma_time, &tcd->time, sizeof(tup->tdma_time)); /* Parse MAC element in timeslot (just one, possibly more later in the loop) */ pdu_bits = upper_mac_prim_recv(&ttp->oph, tms); /* Check if we are done (-1 returned) */ if (pdu_bits < 0) break; /* Not done */ /* Increment head and l1h ptrs */ /* Reset tail to end of msg (may be altered by removing FCS) */ offset += pdu_bits; msg->head = orig_head + offset; /* New head is old head plus parsed len from prev msg */ msg->tail = orig_tail; /* Restore original tail */ msg->len = msg->tail - msg->head; /* Fixup len */ msg->l1h = msg->head; msg->l2h = 0; msg->l3h = 0; msg->l4h = 0; } out: talloc_free(msg); talloc_free(ttp); }