osmocom-bb/src/host/trxcon/src/trxcon_shim.c

264 lines
7.0 KiB
C

/*
* OsmocomBB <-> SDR connection bridge
*
* (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
*
* All Rights Reserved
*
* 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 <stdint.h>
#include <errno.h>
#include <osmocom/core/fsm.h>
#include <osmocom/core/gsmtap_util.h>
#include <osmocom/core/gsmtap.h>
#include <osmocom/bb/trxcon/trxcon.h>
#include <osmocom/bb/trxcon/trxcon_fsm.h>
#include <osmocom/bb/trxcon/phyif.h>
#include <osmocom/bb/l1sched/l1sched.h>
static void trxcon_gsmtap_send(struct gsmtap_inst *gi, uint8_t chan_type,
uint32_t fn, uint8_t tn, uint8_t ss,
uint16_t band_arfcn,
int8_t signal_dbm, uint8_t snr,
const uint8_t *data, size_t data_len)
{
/* Omit frames with unknown channel type */
if (chan_type == GSMTAP_CHANNEL_UNKNOWN)
return;
/* TODO: distinguish GSMTAP_CHANNEL_PCH and GSMTAP_CHANNEL_AGCH */
gsmtap_send(gi, band_arfcn, tn, chan_type, ss, fn, signal_dbm, snr, data, data_len);
}
/* External L1 API for the scheduler */
int l1sched_handle_config_req(struct l1sched_state *sched,
const struct l1sched_config_req *cr)
{
struct trxcon_inst *trxcon = sched->priv;
switch (cr->type) {
case L1SCHED_CFG_PCHAN_COMB:
{
struct trxcon_param_set_phy_config_req req = {
.type = TRXCON_PHY_CFGT_PCHAN_COMB,
.pchan_comb = {
.tn = cr->pchan_comb.tn,
.pchan = cr->pchan_comb.pchan,
},
};
return osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_SET_PHY_CONFIG_REQ, &req);
}
default:
LOGPFSML(trxcon->fi, LOGL_ERROR,
"Unhandled config request (type 0x%02x)\n", cr->type);
return -ENODEV;
}
}
int l1sched_handle_burst_req(struct l1sched_state *sched,
const struct l1sched_burst_req *br)
{
struct trxcon_inst *trxcon = sched->priv;
const struct phyif_burst_req phybr = {
.fn = br->fn,
.tn = br->tn,
.pwr = br->pwr,
.burst = &br->burst[0],
.burst_len = br->burst_len,
};
return phyif_handle_burst_req(trxcon->phyif, &phybr);
}
/* External L2 API for the scheduler */
int l1sched_handle_data_ind(struct l1sched_lchan_state *lchan,
const uint8_t *data, size_t data_len,
int n_errors, int n_bits_total,
enum l1sched_data_type dt)
{
const struct l1sched_meas_set *meas = &lchan->meas_avg;
const struct l1sched_lchan_desc *lchan_desc;
struct l1sched_state *sched = lchan->ts->sched;
struct trxcon_inst *trxcon = sched->priv;
int rc;
lchan_desc = &l1sched_lchan_desc[lchan->type];
struct trxcon_param_rx_data_ind ind = {
/* .traffic is set below */
.chan_nr = lchan_desc->chan_nr | lchan->ts->index,
.link_id = lchan_desc->link_id,
.band_arfcn = trxcon->l1p.band_arfcn,
.frame_nr = meas->fn,
.toa256 = meas->toa256,
.rssi = meas->rssi,
.n_errors = n_errors,
.n_bits_total = n_bits_total,
.data_len = data_len,
.data = data,
};
switch (dt) {
case L1SCHED_DT_PACKET_DATA:
case L1SCHED_DT_TRAFFIC:
ind.traffic = true;
/* fall-through */
case L1SCHED_DT_SIGNALING:
rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_RX_DATA_IND, &ind);
break;
case L1SCHED_DT_OTHER:
if (lchan->type == L1SCHED_SCH) {
if (trxcon->fi->state != TRXCON_ST_FBSB_SEARCH)
return 0;
rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_FBSB_SEARCH_RES, NULL);
break;
}
/* fall through */
default:
LOGPFSML(trxcon->fi, LOGL_ERROR,
"Unhandled L2 DATA.ind (type 0x%02x)\n", dt);
return -ENODEV;
}
if (trxcon->gsmtap != NULL && data != NULL && data_len > 0) {
trxcon_gsmtap_send(trxcon->gsmtap, lchan_desc->gsmtap_chan_type,
meas->fn, lchan->ts->index, lchan_desc->ss_nr,
trxcon->l1p.band_arfcn, meas->rssi, 0,
data, data_len);
}
return rc;
}
int l1sched_handle_data_cnf(struct l1sched_lchan_state *lchan,
uint32_t fn, enum l1sched_data_type dt)
{
const struct l1sched_lchan_desc *lchan_desc;
struct l1sched_state *sched = lchan->ts->sched;
struct trxcon_inst *trxcon = sched->priv;
bool is_traffic = false;
const uint8_t *data;
uint8_t ra_buf[2];
size_t data_len;
int rc;
lchan_desc = &l1sched_lchan_desc[lchan->type];
switch (dt) {
case L1SCHED_DT_TRAFFIC:
case L1SCHED_DT_PACKET_DATA:
is_traffic = true;
/* fall-through */
case L1SCHED_DT_SIGNALING:
{
struct trxcon_param_tx_data_cnf cnf = {
.traffic = is_traffic,
.chan_nr = lchan_desc->chan_nr | lchan->ts->index,
.link_id = lchan_desc->link_id,
.band_arfcn = trxcon->l1p.band_arfcn,
.frame_nr = fn,
};
rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_TX_DATA_CNF, &cnf);
data_len = lchan->prim->payload_len;
data = lchan->prim->payload;
break;
}
case L1SCHED_DT_OTHER:
if (L1SCHED_PRIM_IS_RACH(lchan->prim)) {
const struct l1sched_ts_prim_rach *rach;
struct trxcon_param_tx_access_burst_cnf cnf = {
.band_arfcn = trxcon->l1p.band_arfcn,
.frame_nr = fn,
};
rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_TX_ACCESS_BURST_CNF, &cnf);
rach = (struct l1sched_ts_prim_rach *)lchan->prim->payload;
if (lchan->prim->type == L1SCHED_PRIM_RACH11) {
ra_buf[0] = (uint8_t)(rach->ra >> 3);
ra_buf[1] = (uint8_t)(rach->ra & 0x07);
data = &ra_buf[0];
data_len = 2;
} else {
ra_buf[0] = (uint8_t)(rach->ra);
data = &ra_buf[0];
data_len = 1;
}
break;
}
/* fall through */
default:
LOGPFSML(trxcon->fi, LOGL_ERROR,
"Unhandled L2 DATA.cnf (type 0x%02x)\n", dt);
return -ENODEV;
}
if (trxcon->gsmtap != NULL) {
trxcon_gsmtap_send(trxcon->gsmtap, lchan_desc->gsmtap_chan_type,
fn, lchan->ts->index, lchan_desc->ss_nr,
trxcon->l1p.band_arfcn | ARFCN_UPLINK,
0, 0, data, data_len);
}
return rc;
}
/* External L1 API for the PHYIF */
int phyif_handle_burst_ind(void *priv, const struct phyif_burst_ind *bi)
{
struct trxcon_inst *trxcon = priv;
const struct l1sched_meas_set meas = {
.fn = bi->fn,
.toa256 = bi->toa256,
.rssi = bi->rssi,
};
/* Poke scheduler */
l1sched_handle_rx_burst(trxcon->sched, bi->tn, bi->fn,
bi->burst, bi->burst_len, &meas);
/* Correct local clock counter */
if (bi->fn % 51 == 0)
l1sched_clck_handle(trxcon->sched, bi->fn);
return 0;
}
int phyif_handle_rsp(void *priv, const struct phyif_rsp *rsp)
{
struct trxcon_inst *trxcon = priv;
switch (rsp->type) {
case PHYIF_CMDT_MEASURE:
{
const struct phyif_rspp_measure *meas = &rsp->param.measure;
struct trxcon_param_full_power_scan_res res = {
.band_arfcn = meas->band_arfcn,
.dbm = meas->dbm,
};
return osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_FULL_POWER_SCAN_RES, &res);
}
default:
LOGPFSML(trxcon->fi, LOGL_ERROR,
"Unhandled PHYIF response (type 0x%02x)\n", rsp->type);
return -ENODEV;
}
}