/* * OsmocomBB <-> SDR connection bridge * * (C) 2022-2023 by sysmocom - s.f.m.c. GmbH * Author: Vadim Yanitskiy * * 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 #include #include #include #include #include #include #include #include #include static void trxcon_gsmtap_send(struct trxcon_inst *trxcon, const struct l1sched_prim_chdr *chdr, const uint8_t *data, size_t data_len, int8_t signal_dbm, uint8_t snr, bool uplink) { uint16_t band_arfcn = trxcon->l1p.band_arfcn; uint8_t chan_type, ss, tn; if (uplink) band_arfcn |= ARFCN_UPLINK; if (rsl_dec_chan_nr(chdr->chan_nr, &chan_type, &ss, &tn) != 0) return; chan_type = chantype_rsl2gsmtap2(chan_type, chdr->link_id, chdr->traffic); gsmtap_send(trxcon->gsmtap, band_arfcn, tn, chan_type, ss, chdr->frame_nr, signal_dbm, snr, data, data_len); } /* External L1 API for the scheduler */ int l1sched_handle_burst_req(struct l1sched_state *sched, const struct l1sched_burst_req *br) { struct trxcon_inst *trxcon = sched->priv; const struct trxcon_phyif_burst_req phybr = { .fn = br->fn, .tn = br->tn, .pwr = br->pwr, .burst = &br->burst[0], .burst_len = br->burst_len, }; return trxcon_phyif_handle_burst_req(trxcon->phyif, &phybr); } /* External L2 API for the scheduler */ static int handle_prim_data_ind(struct trxcon_inst *trxcon, struct msgb *msg) { const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg); struct trxcon_param_rx_data_ind ind = { .traffic = prim->data_ind.chdr.traffic, .chan_nr = prim->data_ind.chdr.chan_nr, .link_id = prim->data_ind.chdr.link_id, .band_arfcn = trxcon->l1p.band_arfcn, .frame_nr = prim->data_ind.chdr.frame_nr, .toa256 = prim->data_ind.toa256, .rssi = prim->data_ind.rssi, .n_errors = prim->data_ind.n_errors, .n_bits_total = prim->data_ind.n_bits_total, .data_len = msgb_l2len(msg), .data = msgb_l2(msg), }; if (trxcon->gsmtap != NULL && ind.data_len > 0) { trxcon_gsmtap_send(trxcon, &prim->data_ind.chdr, ind.data, ind.data_len, ind.rssi, 0, false); } return osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_RX_DATA_IND, &ind); } static int handle_prim_data_cnf(struct trxcon_inst *trxcon, struct msgb *msg) { const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg); struct trxcon_param_tx_data_cnf cnf = { .traffic = prim->data_cnf.traffic, .chan_nr = prim->data_cnf.chan_nr, .link_id = prim->data_cnf.link_id, .band_arfcn = trxcon->l1p.band_arfcn, .frame_nr = prim->data_cnf.frame_nr, .data_len = msgb_l2len(msg), .data = msgb_l2(msg), }; if (trxcon->gsmtap != NULL) { trxcon_gsmtap_send(trxcon, &prim->data_cnf, msgb_l2(msg), msgb_l2len(msg), 0, 0, true); } return osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_TX_DATA_CNF, &cnf); } static int handle_prim_rach_cnf(struct trxcon_inst *trxcon, struct msgb *msg) { const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg); struct trxcon_param_tx_access_burst_cnf cnf = { .band_arfcn = trxcon->l1p.band_arfcn, .frame_nr = prim->rach_cnf.chdr.frame_nr, }; if (trxcon->gsmtap != NULL) { if (prim->rach_cnf.is_11bit) { msgb_put_u8(msg, (uint8_t)(prim->rach_cnf.ra >> 3)); msgb_put_u8(msg, (uint8_t)(prim->rach_cnf.ra & 0x07)); } else { msgb_put_u8(msg, (uint8_t)(prim->rach_cnf.ra)); } trxcon_gsmtap_send(trxcon, &prim->rach_cnf.chdr, msgb_l2(msg), msgb_l2len(msg), 0, 0, true); } return osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_TX_ACCESS_BURST_CNF, &cnf); } int l1sched_prim_to_user(struct l1sched_state *sched, struct msgb *msg) { const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg); struct trxcon_inst *trxcon = sched->priv; int rc = 0; LOGPFSML(trxcon->fi, LOGL_DEBUG, "%s(): Rx " L1SCHED_PRIM_STR_FMT "\n", __func__, L1SCHED_PRIM_STR_ARGS(prim)); switch (OSMO_PRIM_HDR(&prim->oph)) { case OSMO_PRIM(L1SCHED_PRIM_T_DATA, PRIM_OP_INDICATION): rc = handle_prim_data_ind(trxcon, msg); break; case OSMO_PRIM(L1SCHED_PRIM_T_DATA, PRIM_OP_CONFIRM): rc = handle_prim_data_cnf(trxcon, msg); break; case OSMO_PRIM(L1SCHED_PRIM_T_RACH, PRIM_OP_CONFIRM): rc = handle_prim_rach_cnf(trxcon, msg); break; case OSMO_PRIM(L1SCHED_PRIM_T_SCH, PRIM_OP_INDICATION): if (trxcon->fi->state == TRXCON_ST_FBSB_SEARCH) rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_FBSB_SEARCH_RES, NULL); break; case OSMO_PRIM(L1SCHED_PRIM_T_PCHAN_COMB, PRIM_OP_INDICATION): { struct trxcon_param_set_phy_config_req req = { .type = TRXCON_PHY_CFGT_PCHAN_COMB, .pchan_comb = { .tn = prim->pchan_comb_ind.tn, .pchan = prim->pchan_comb_ind.pchan, }, }; rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_SET_PHY_CONFIG_REQ, &req); break; } default: LOGPFSML(trxcon->fi, LOGL_ERROR, "%s(): Unhandled primitive " L1SCHED_PRIM_STR_FMT "\n", __func__, L1SCHED_PRIM_STR_ARGS(prim)); rc = -ENOTSUP; } msgb_free(msg); return rc; } /* External L1 API for the PHYIF */ int trxcon_phyif_handle_rts_ind(void *priv, const struct trxcon_phyif_rts_ind *rts) { struct trxcon_inst *trxcon = priv; struct l1sched_burst_req br = { .fn = rts->fn, .tn = rts->tn, .burst_len = 0, /* NOPE.ind */ }; l1sched_pull_burst(trxcon->sched, &br); return l1sched_handle_burst_req(trxcon->sched, &br); } int trxcon_phyif_handle_rtr_ind(void *priv, const struct trxcon_phyif_rtr_ind *ind, struct trxcon_phyif_rtr_rsp *rsp) { struct trxcon_inst *trxcon = priv; struct l1sched_probe probe = { .fn = ind->fn, .tn = ind->tn, }; l1sched_handle_rx_probe(trxcon->sched, &probe); memset(rsp, 0x00, sizeof(*rsp)); if (probe.flags & L1SCHED_PROBE_F_ACTIVE) rsp->flags |= TRXCON_PHYIF_RTR_F_ACTIVE; return 0; } int trxcon_phyif_handle_burst_ind(void *priv, const struct trxcon_phyif_burst_ind *phybi) { struct trxcon_inst *trxcon = priv; struct l1sched_burst_ind bi = { .fn = phybi->fn, .tn = phybi->tn, .toa256 = phybi->toa256, .rssi = phybi->rssi, /* .burst[] is populated below */ .burst_len = phybi->burst_len, }; OSMO_ASSERT(phybi->burst_len <= sizeof(bi.burst)); memcpy(&bi.burst[0], phybi->burst, phybi->burst_len); /* Poke scheduler */ return l1sched_handle_rx_burst(trxcon->sched, &bi); } int trxcon_phyif_handle_rsp(void *priv, const struct trxcon_phyif_rsp *rsp) { struct trxcon_inst *trxcon = priv; switch (rsp->type) { case TRXCON_PHYIF_CMDT_MEASURE: { const struct trxcon_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; } }