mirror of https://gerrit.osmocom.org/osmo-tetra
223 lines
5.8 KiB
C
223 lines
5.8 KiB
C
/* TETRA upper MAC layer main routine, above TMV-SAP */
|
|
|
|
/* (C) 2011 by Harald Welte <laforge@gnumonks.org>
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include <osmocore/utils.h>
|
|
|
|
#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;
|
|
}
|