trxcon: rework trxcon_fsm, move into a separate file

The original trxcon_fsm I wrote back in 2017 [1] was more like
a boolean flag, as there were only two states: IDLE and MANAGED.
Not surprising, given that until recently handling of multiple
L1CTL connections was not supported.  Now that we have this
implemented, lifetime of a trxcon_fsm instance is limited by
lifetime of a L1CTL connection, what renders the FSM useless.

This change removes the old 'boolean' trxcon_fsm and introduces
the new one, which will allows us to abstract the L1CTL interface
from the TRXC/TRXD interfaces, as well as the scheduler.  The new
FSM will also simplify development of the RLC/MAC layer for GPRS.

Change-Id: Ifaf63ead9dd180181358e771367b2a686ba159ca
Related: [1] I7ee6fc891abe5f775f5b7ebbf093181a97950dea
Related: OS#5599
This commit is contained in:
Vadim Yanitskiy 2022-07-18 05:03:45 +07:00
parent 40d66990e0
commit 3e023128dc
8 changed files with 787 additions and 411 deletions

View File

@ -4,6 +4,8 @@
#include <stdint.h>
#include <stdbool.h>
#include <arpa/inet.h>
#include <osmocom/core/bits.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>

View File

@ -9,8 +9,8 @@
/* Event handlers */
int l1ctl_rx_cb(struct l1ctl_client *l1c, struct msgb *msg);
int l1ctl_tx_fbsb_conf(struct l1ctl_client *l1c, uint8_t result,
const struct l1ctl_info_dl *dl_info, uint8_t bsic);
int l1ctl_tx_fbsb_conf(struct l1ctl_client *l1c, uint16_t band_arfcn, uint8_t bsic);
int l1ctl_tx_fbsb_fail(struct l1ctl_client *l1c, uint16_t band_arfcn);
int l1ctl_tx_ccch_mode_conf(struct l1ctl_client *l1c, uint8_t mode);
int l1ctl_tx_pm_conf(struct l1ctl_client *l1c, uint16_t band_arfcn,
int dbm, int last);

View File

@ -4,19 +4,127 @@ struct l1sched_state;
struct trx_instance;
struct l1ctl_client;
extern struct osmo_fsm trxcon_fsm_def;
enum trxcon_fsm_states {
TRXCON_STATE_IDLE = 0,
TRXCON_STATE_MANAGED,
TRXCON_ST_RESET,
TRXCON_ST_FULL_POWER_SCAN,
TRXCON_ST_FBSB_SEARCH,
TRXCON_ST_BCCH_CCCH,
TRXCON_ST_DEDICATED,
};
enum trxcon_fsm_events {
/* L1CTL specific events */
L1CTL_EVENT_CONNECT,
L1CTL_EVENT_DISCONNECT,
TRXCON_EV_L1IF_FAILURE,
TRXCON_EV_L2IF_FAILURE,
TRXCON_EV_RESET_FULL_REQ,
TRXCON_EV_RESET_SCHED_REQ,
TRXCON_EV_FULL_POWER_SCAN_REQ,
TRXCON_EV_FULL_POWER_SCAN_RES,
TRXCON_EV_FBSB_SEARCH_REQ,
TRXCON_EV_FBSB_SEARCH_RES,
TRXCON_EV_SET_CCCH_MODE_REQ,
TRXCON_EV_SET_TCH_MODE_REQ,
TRXCON_EV_SET_CONFIG_REQ,
TRXCON_EV_TX_ACCESS_BURST_REQ,
TRXCON_EV_DEDICATED_ESTABLISH_REQ,
TRXCON_EV_DEDICATED_RELEASE_REQ,
TRXCON_EV_TX_TRAFFIC_REQ,
TRXCON_EV_RX_TRAFFIC_IND,
TRXCON_EV_TX_DATA_REQ,
TRXCON_EV_RX_DATA_IND,
TRXCON_EV_CRYPTO_REQ,
};
/* TRX specific events */
TRX_EVENT_RSP_ERROR,
TRX_EVENT_OFFLINE,
/* param of TRXCON_EV_FULL_POWER_SCAN_REQ */
struct trxcon_param_full_power_scan_req {
uint16_t band_arfcn_start;
uint16_t band_arfcn_stop;
};
/* param of TRXCON_EV_FULL_POWER_SCAN_RES */
struct trxcon_param_full_power_scan_res {
bool last_result;
uint16_t band_arfcn;
int dbm;
};
/* param of TRXCON_EV_FBSB_SEARCH_REQ */
struct trxcon_param_fbsb_search_req {
uint16_t band_arfcn;
uint16_t timeout_ms;
uint8_t pchan_config;
};
/* param of TRXCON_EV_SET_{CCCH,TCH}_MODE_REQ */
struct trxcon_param_set_ccch_tch_mode_req {
uint8_t mode;
bool applied;
};
/* param of TRXCON_EV_SET_CONFIG_REQ */
struct trxcon_param_set_config_req {
uint8_t timing_advance;
uint8_t tx_power;
};
/* param of TRXCON_EV_TX_{TRAFFIC,DATA}_REQ */
struct trxcon_param_tx_traffic_data_req {
uint8_t chan_nr;
uint8_t link_id;
size_t data_len;
const uint8_t *data;
};
/* param of TRXCON_EV_RX_{TRAFFIC,DATA}_IND */
struct trxcon_param_rx_traffic_data_ind {
uint8_t chan_nr;
uint8_t link_id;
uint32_t frame_nr;
int16_t toa256;
int8_t rssi;
int n_errors;
int n_bits_total;
size_t data_len;
const uint8_t *data;
};
/* param of TRXCON_EV_TX_ACCESS_BURST_REQ */
struct trxcon_param_tx_access_burst_req {
uint8_t chan_nr;
uint8_t link_id;
uint8_t offset;
uint8_t synch_seq;
uint16_t ra;
bool is_11bit;
};
/* param of TRXCON_EV_DEDICATED_ESTABLISH_REQ */
struct trxcon_param_dedicated_establish_req {
uint8_t chan_nr;
uint8_t tch_mode;
uint8_t tsc;
bool hopping;
union {
struct { /* hopping=false */
uint16_t band_arfcn;
} h0;
struct { /* hopping=true */
uint8_t hsn;
uint8_t maio;
uint8_t n;
uint16_t ma[64];
} h1;
};
};
/* param of TRXCON_EV_CRYPTO_REQ */
struct trxcon_param_crypto_req {
uint8_t chan_nr;
uint8_t a5_algo; /* 0 is A5/0 */
uint8_t key_len;
const uint8_t *key;
};
struct trxcon_inst {
@ -31,10 +139,6 @@ struct trxcon_inst {
/* L1/L2 interfaces */
struct trx_instance *trx;
struct l1ctl_client *l1c;
/* TODO: implement this as an FSM state with timeout */
struct osmo_timer_list fbsb_timer;
bool fbsb_conf_sent;
};
struct trxcon_inst *trxcon_inst_alloc(void *ctx, unsigned int id);

View File

@ -42,6 +42,7 @@ trxcon_SOURCES = \
l1ctl.c \
trx_if.c \
logging.c \
trxcon_fsm.c \
trxcon.c \
$(NULL)

View File

@ -30,18 +30,18 @@
#include <arpa/inet.h>
#include <osmocom/core/fsm.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/gsm/gsm0502.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/protocol/gsm_08_58.h>
#include <osmocom/bb/trxcon/logging.h>
#include <osmocom/bb/trxcon/l1ctl_server.h>
#include <osmocom/bb/trxcon/l1ctl_proto.h>
#include <osmocom/bb/trxcon/trx_if.h>
#include <osmocom/bb/l1sched/l1sched.h>
#include <osmocom/bb/trxcon/trxcon.h>
static const char *arfcn2band_name(uint16_t arfcn)
@ -166,33 +166,52 @@ static struct l1ctl_fbsb_conf *fbsb_conf_make(struct msgb *msg, uint8_t result,
return conf;
}
int l1ctl_tx_fbsb_conf(struct l1ctl_client *l1c, uint8_t result,
const struct l1ctl_info_dl *dl_info, uint8_t bsic)
int l1ctl_tx_fbsb_fail(struct l1ctl_client *l1c, uint16_t band_arfcn)
{
struct trxcon_inst *trxcon = l1c->priv;
struct l1ctl_fbsb_conf *conf;
struct l1ctl_info_dl *dl;
struct msgb *msg;
msg = l1ctl_alloc_msg(L1CTL_FBSB_CONF);
if (msg == NULL)
return -ENOMEM;
put_dl_info_hdr(msg, dl_info);
dl = put_dl_info_hdr(msg, NULL);
conf = fbsb_conf_make(msg, result, bsic);
/* Fill in current ARFCN */
dl->band_arfcn = htons(band_arfcn);
fbsb_conf_make(msg, 255, 0);
LOGPFSMSL(trxcon->fi, DL1C, LOGL_DEBUG, "Send FBSB Conf (timeout)\n");
return l1ctl_client_send(l1c, msg);
}
int l1ctl_tx_fbsb_conf(struct l1ctl_client *l1c, uint16_t band_arfcn, uint8_t bsic)
{
struct trxcon_inst *trxcon = l1c->priv;
struct l1ctl_fbsb_conf *conf;
struct l1ctl_info_dl *dl;
struct msgb *msg;
msg = l1ctl_alloc_msg(L1CTL_FBSB_CONF);
if (msg == NULL)
return -ENOMEM;
dl = put_dl_info_hdr(msg, NULL);
/* Fill in current ARFCN */
dl->band_arfcn = htons(band_arfcn);
conf = fbsb_conf_make(msg, 0, bsic);
/* FIXME: set proper value */
conf->initial_freq_err = 0;
/* Ask SCH handler not to send L1CTL_FBSB_CONF anymore */
trxcon->fbsb_conf_sent = true;
/* Abort FBSB expire timer */
osmo_timer_del(&trxcon->fbsb_timer);
LOGPFSMSL(trxcon->fi, DL1C, LOGL_DEBUG,
"Send FBSB Conf (result=%u, bsic=%u)\n",
result, bsic);
conf->result, conf->bsic);
return l1ctl_client_send(l1c, msg);
}
@ -299,44 +318,10 @@ static enum gsm_phys_chan_config l1ctl_ccch_mode2pchan_config(enum ccch_mode mod
}
}
/* FBSB expire timer */
static void fbsb_timer_cb(void *data)
{
struct l1ctl_client *l1c = (struct l1ctl_client *) data;
struct trxcon_inst *trxcon = l1c->priv;
struct l1ctl_info_dl *dl;
struct msgb *msg;
msg = l1ctl_alloc_msg(L1CTL_FBSB_CONF);
if (msg == NULL)
return;
LOGPFSMSL(trxcon->fi, DL1C, LOGL_NOTICE,
"FBSB timer fired for ARFCN %u\n",
trxcon->trx->band_arfcn & ~ARFCN_FLAG_MASK);
dl = put_dl_info_hdr(msg, NULL);
/* Fill in current ARFCN */
dl->band_arfcn = htons(trxcon->trx->band_arfcn);
fbsb_conf_make(msg, 255, 0);
/* Ask SCH handler not to send L1CTL_FBSB_CONF anymore */
trxcon->fbsb_conf_sent = true;
LOGPFSMSL(trxcon->fi, DL1C, LOGL_DEBUG, "Send FBSB Conf (timeout)\n");
l1ctl_client_send(l1c, msg);
}
static int l1ctl_rx_fbsb_req(struct l1ctl_client *l1c, struct msgb *msg)
{
struct trxcon_inst *trxcon = l1c->priv;
enum gsm_phys_chan_config ch_config;
struct l1ctl_fbsb_req *fbsb;
uint16_t band_arfcn;
uint16_t timeout;
int rc = 0;
fbsb = (struct l1ctl_fbsb_req *) msg->l1h;
@ -348,44 +333,19 @@ static int l1ctl_rx_fbsb_req(struct l1ctl_client *l1c, struct msgb *msg)
goto exit;
}
ch_config = l1ctl_ccch_mode2pchan_config(fbsb->ccch_mode);
band_arfcn = ntohs(fbsb->band_arfcn);
timeout = ntohs(fbsb->timeout);
struct trxcon_param_fbsb_search_req req = {
.pchan_config = l1ctl_ccch_mode2pchan_config(fbsb->ccch_mode),
.timeout_ms = ntohs(fbsb->timeout) * GSM_TDMA_FN_DURATION_uS / 1000,
.band_arfcn = ntohs(fbsb->band_arfcn),
};
LOGPFSMSL(trxcon->fi, DL1C, LOGL_NOTICE, "Received FBSB request (%s %d)\n",
arfcn2band_name(band_arfcn), band_arfcn & ~ARFCN_FLAG_MASK);
LOGPFSMSL(trxcon->fi, DL1C, LOGL_NOTICE,
"Received FBSB request (%s %d, timeout %u ms)\n",
arfcn2band_name(req.band_arfcn),
req.band_arfcn & ~ARFCN_FLAG_MASK,
req.timeout_ms);
/* Reset scheduler and clock counter */
l1sched_reset(trxcon->sched, true);
/* Configure a single timeslot */
l1sched_configure_ts(trxcon->sched, 0, ch_config);
/* Ask SCH handler to send L1CTL_FBSB_CONF */
trxcon->fbsb_conf_sent = false;
/* Only if current ARFCN differs */
if (trxcon->trx->band_arfcn != band_arfcn) {
/* Update current ARFCN */
trxcon->trx->band_arfcn = band_arfcn;
/* Tune transceiver to required ARFCN */
trx_if_cmd_rxtune(trxcon->trx, band_arfcn);
trx_if_cmd_txtune(trxcon->trx, band_arfcn);
}
/* Transceiver might have been powered on before, e.g.
* in case of sending L1CTL_FBSB_REQ due to signal loss. */
if (!trxcon->trx->powered_up)
trx_if_cmd_poweron(trxcon->trx);
/* Start FBSB expire timer */
trxcon->fbsb_timer.data = l1c;
trxcon->fbsb_timer.cb = fbsb_timer_cb;
LOGPFSMSL(trxcon->fi, DL1C, LOGL_INFO, "Starting FBSB timer %u ms\n",
timeout * GSM_TDMA_FN_DURATION_uS / 1000);
osmo_timer_schedule(&trxcon->fbsb_timer, 0,
timeout * GSM_TDMA_FN_DURATION_uS);
osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_FBSB_SEARCH_REQ, &req);
exit:
msgb_free(msg);
@ -394,7 +354,6 @@ exit:
static int l1ctl_rx_pm_req(struct l1ctl_client *l1c, struct msgb *msg)
{
uint16_t band_arfcn_start, band_arfcn_stop;
struct trxcon_inst *trxcon = l1c->priv;
struct l1ctl_pm_req *pmr;
int rc = 0;
@ -408,17 +367,18 @@ static int l1ctl_rx_pm_req(struct l1ctl_client *l1c, struct msgb *msg)
goto exit;
}
band_arfcn_start = ntohs(pmr->range.band_arfcn_from);
band_arfcn_stop = ntohs(pmr->range.band_arfcn_to);
struct trxcon_param_full_power_scan_req req = {
.band_arfcn_start = ntohs(pmr->range.band_arfcn_from),
.band_arfcn_stop = ntohs(pmr->range.band_arfcn_to),
};
LOGPFSMSL(trxcon->fi, DL1C, LOGL_NOTICE,
"Received power measurement request (%s: %d -> %d)\n",
arfcn2band_name(band_arfcn_start),
band_arfcn_start & ~ARFCN_FLAG_MASK,
band_arfcn_stop & ~ARFCN_FLAG_MASK);
arfcn2band_name(req.band_arfcn_start),
req.band_arfcn_start & ~ARFCN_FLAG_MASK,
req.band_arfcn_stop & ~ARFCN_FLAG_MASK);
/* Send measurement request to transceiver */
rc = trx_if_cmd_measure(trxcon->trx, band_arfcn_start, band_arfcn_stop);
osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_FULL_POWER_SCAN_REQ, &req);
exit:
msgb_free(msg);
@ -445,13 +405,10 @@ static int l1ctl_rx_reset_req(struct l1ctl_client *l1c, struct msgb *msg)
switch (res->type) {
case L1CTL_RES_T_FULL:
/* TODO: implement trx_if_reset() */
trx_if_cmd_poweroff(trxcon->trx);
trx_if_cmd_echo(trxcon->trx);
/* Fall through */
osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_RESET_FULL_REQ, NULL);
break;
case L1CTL_RES_T_SCHED:
l1sched_reset(trxcon->sched, true);
osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_RESET_SCHED_REQ, NULL);
break;
default:
LOGPFSMSL(trxcon->fi, DL1C, LOGL_ERROR,
@ -486,13 +443,11 @@ static int l1ctl_rx_echo_req(struct l1ctl_client *l1c, struct msgb *msg)
static int l1ctl_rx_ccch_mode_req(struct l1ctl_client *l1c, struct msgb *msg)
{
struct trxcon_inst *trxcon = l1c->priv;
enum gsm_phys_chan_config ch_config;
struct l1ctl_ccch_mode_req *req;
struct l1sched_ts *ts;
int rc = 0;
struct l1ctl_ccch_mode_req *mode_req;
int rc;
req = (struct l1ctl_ccch_mode_req *) msg->l1h;
if (msgb_l1len(msg) < sizeof(*req)) {
mode_req = (struct l1ctl_ccch_mode_req *)msg->l1h;
if (msgb_l1len(msg) < sizeof(*mode_req)) {
LOGPFSMSL(trxcon->fi, DL1C, LOGL_ERROR,
"MSG too short Reset Req: %u\n",
msgb_l1len(msg));
@ -501,26 +456,16 @@ static int l1ctl_rx_ccch_mode_req(struct l1ctl_client *l1c, struct msgb *msg)
}
LOGPFSMSL(trxcon->fi, DL1C, LOGL_NOTICE, "Received CCCH mode request (%u)\n",
req->ccch_mode); /* TODO: add value-string for ccch_mode */
mode_req->ccch_mode); /* TODO: add value-string for ccch_mode */
/* Make sure that TS0 is allocated and configured */
ts = trxcon->sched->ts[0];
if (ts == NULL || ts->mf_layout == NULL) {
LOGPFSMSL(trxcon->fi, DL1C, LOGL_ERROR, "TS0 is not configured");
rc = -EINVAL;
goto exit;
}
struct trxcon_param_set_ccch_tch_mode_req req = {
/* Choose corresponding channel combination */
.mode = l1ctl_ccch_mode2pchan_config(mode_req->ccch_mode),
};
/* Choose corresponding channel combination */
ch_config = l1ctl_ccch_mode2pchan_config(req->ccch_mode);
/* Do nothing if the current mode matches required */
if (ts->mf_layout->chan_config != ch_config)
rc = l1sched_configure_ts(trxcon->sched, 0, ch_config);
/* Confirm reconfiguration */
if (!rc)
rc = l1ctl_tx_ccch_mode_conf(l1c, req->ccch_mode);
rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_SET_CCCH_MODE_REQ, &req);
if (rc == 0 && req.applied)
l1ctl_tx_ccch_mode_conf(l1c, mode_req->ccch_mode);
exit:
msgb_free(msg);
@ -529,40 +474,38 @@ exit:
static int l1ctl_rx_rach_req(struct l1ctl_client *l1c, struct msgb *msg, bool ext)
{
struct trxcon_param_tx_access_burst_req req;
struct trxcon_inst *trxcon = l1c->priv;
struct l1ctl_info_ul *ul;
struct l1sched_ts_prim *prim;
struct l1sched_ts_prim_rach rach;
enum l1sched_ts_prim_type prim_type;
int rc;
ul = (struct l1ctl_info_ul *) msg->l1h;
/* Is it extended (11-bit) RACH or not? */
if (ext) {
const struct l1ctl_ext_rach_req *req = (void *)ul->payload;
const struct l1ctl_ext_rach_req *rr = (void *)ul->payload;
rach = (struct l1sched_ts_prim_rach) {
.ra = ntohs(req->ra11),
.synch_seq = req->synch_seq,
.offset = ntohs(req->offset),
req = (struct trxcon_param_tx_access_burst_req) {
.offset = ntohs(rr->offset),
.synch_seq = rr->synch_seq,
.ra = ntohs(rr->ra11),
.is_11bit = true,
};
LOGPFSMSL(trxcon->fi, DL1C, LOGL_NOTICE,
"Received extended (11-bit) RACH request "
"(offset=%u, synch_seq=%u, ra11=0x%02hx)\n",
rach.offset, rach.synch_seq, rach.ra);
req.offset, req.synch_seq, req.ra);
} else {
const struct l1ctl_rach_req *req = (void *)ul->payload;
const struct l1ctl_rach_req *rr = (void *)ul->payload;
rach = (struct l1sched_ts_prim_rach) {
.ra = req->ra,
.offset = ntohs(req->offset),
req = (struct trxcon_param_tx_access_burst_req) {
.offset = ntohs(rr->offset),
.ra = rr->ra,
};
LOGPFSMSL(trxcon->fi, DL1C, LOGL_NOTICE,
"Received regular (8-bit) RACH request "
"(offset=%u, ra=0x%02x)\n", rach.offset, rach.ra);
"(offset=%u, ra=0x%02x)\n", req.offset, req.ra);
}
/* The controlling L1CTL side always does include the UL info header,
@ -570,55 +513,38 @@ static int l1ctl_rx_rach_req(struct l1ctl_client *l1c, struct msgb *msg, bool ex
if (ul->chan_nr == 0x00) {
LOGPFSMSL(trxcon->fi, DL1C, LOGL_NOTICE,
"The UL info header is empty, assuming RACH is on TS0\n");
ul->chan_nr = RSL_CHAN_RACH;
req.chan_nr = RSL_CHAN_RACH;
req.link_id = 0x00;
} else {
req.chan_nr = ul->chan_nr;
req.link_id = ul->link_id;
}
/**
* Push this primitive to the transmit queue.
* Indicated timeslot needs to be configured.
*/
prim_type = ext ? L1SCHED_PRIM_RACH11 : L1SCHED_PRIM_RACH8;
prim = l1sched_prim_push(trxcon->sched, prim_type, ul->chan_nr, ul->link_id,
(const uint8_t *)&rach, sizeof(rach));
if (prim == NULL)
rc = -ENOMEM;
osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_TX_ACCESS_BURST_REQ, &req);
msgb_free(msg);
return rc;
return 0;
}
static int l1ctl_proc_est_req_h0(struct trxcon_inst *trxcon, struct l1ctl_h0 *h)
static int l1ctl_proc_est_req_h0(struct trxcon_inst *trxcon,
struct trxcon_param_dedicated_establish_req *req,
const struct l1ctl_h0 *h)
{
struct trx_instance *trx = trxcon->trx;
uint16_t band_arfcn;
int rc = 0;
band_arfcn = ntohs(h->band_arfcn);
req->h0.band_arfcn = ntohs(h->band_arfcn);
LOGPFSMSL(trxcon->fi, DL1C, LOGL_NOTICE,
"L1CTL_DM_EST_REQ indicates a single ARFCN=%u\n",
band_arfcn & ~ARFCN_FLAG_MASK);
/* Do we need to retune? */
if (trx->band_arfcn == band_arfcn)
return 0;
/* Tune transceiver to required ARFCN */
rc |= trx_if_cmd_rxtune(trx, band_arfcn);
rc |= trx_if_cmd_txtune(trx, band_arfcn);
if (rc)
return rc;
/* Update current ARFCN */
trx->band_arfcn = band_arfcn;
"L1CTL_DM_EST_REQ indicates single ARFCN %s %u\n",
arfcn2band_name(req->h0.band_arfcn),
req->h0.band_arfcn & ~ARFCN_FLAG_MASK);
return 0;
}
static int l1ctl_proc_est_req_h1(struct trxcon_inst *trxcon, struct l1ctl_h1 *h)
static int l1ctl_proc_est_req_h1(struct trxcon_inst *trxcon,
struct trxcon_param_dedicated_establish_req *req,
const struct l1ctl_h1 *h)
{
uint16_t ma[64];
int i, rc;
unsigned int i;
LOGPFSMSL(trxcon->fi, DL1C, LOGL_NOTICE,
"L1CTL_DM_EST_REQ indicates a Frequency "
@ -630,7 +556,7 @@ static int l1ctl_proc_est_req_h1(struct trxcon_inst *trxcon, struct l1ctl_h1 *h)
LOGPFSMSL(trxcon->fi, DL1C, LOGL_ERROR,
"No channels in mobile allocation?!?\n");
return -EINVAL;
} else if (h->n > ARRAY_SIZE(ma)) {
} else if (h->n > ARRAY_SIZE(h->ma)) {
LOGPFSMSL(trxcon->fi, DL1C, LOGL_ERROR,
"More than 64 channels in mobile allocation?!?\n");
return -EINVAL;
@ -638,75 +564,45 @@ static int l1ctl_proc_est_req_h1(struct trxcon_inst *trxcon, struct l1ctl_h1 *h)
/* Convert from network to host byte order */
for (i = 0; i < h->n; i++)
ma[i] = ntohs(h->ma[i]);
req->h1.ma[i] = ntohs(h->ma[i]);
req->h1.n = h->n;
req->h1.hsn = h->hsn;
req->h1.maio = h->maio;
/* Forward hopping parameters to TRX */
rc = trx_if_cmd_setfh(trxcon->trx, h->hsn, h->maio, ma, h->n);
if (rc)
return rc;
/**
* TODO: update the state of trx_instance somehow
* in order to indicate that it is in hopping mode...
*/
return 0;
}
static int l1ctl_rx_dm_est_req(struct l1ctl_client *l1c, struct msgb *msg)
{
struct trxcon_inst *trxcon = l1c->priv;
enum gsm_phys_chan_config config;
struct l1ctl_dm_est_req *est_req;
struct l1ctl_info_ul *ul;
struct l1sched_ts *ts;
uint8_t chan_nr, tn;
int rc;
ul = (struct l1ctl_info_ul *) msg->l1h;
est_req = (struct l1ctl_dm_est_req *) ul->payload;
chan_nr = ul->chan_nr;
tn = chan_nr & 0x07;
struct trxcon_param_dedicated_establish_req req = {
.chan_nr = ul->chan_nr,
.tch_mode = est_req->tch_mode,
.tsc = est_req->tsc,
.hopping = est_req->h,
};
LOGPFSMSL(trxcon->fi, DL1C, LOGL_NOTICE,
"Received L1CTL_DM_EST_REQ "
"(tn=%u, chan_nr=0x%02x, tsc=%u, tch_mode=0x%02x)\n",
tn, chan_nr, est_req->tsc, est_req->tch_mode);
/* Determine channel config */
config = l1sched_chan_nr2pchan_config(chan_nr);
if (config == GSM_PCHAN_NONE) {
LOGPFSMSL(trxcon->fi, DL1C, LOGL_ERROR, "Couldn't determine channel config\n");
rc = -EINVAL;
goto exit;
}
req.chan_nr & 0x07, req.chan_nr, req.tsc, req.tch_mode);
/* Frequency hopping? */
if (est_req->h)
rc = l1ctl_proc_est_req_h1(trxcon, &est_req->h1);
rc = l1ctl_proc_est_req_h1(trxcon, &req, &est_req->h1);
else /* Single ARFCN */
rc = l1ctl_proc_est_req_h0(trxcon, &est_req->h0);
rc = l1ctl_proc_est_req_h0(trxcon, &req, &est_req->h0);
if (rc)
goto exit;
/* Configure requested TS */
rc = l1sched_configure_ts(trxcon->sched, tn, config);
ts = trxcon->sched->ts[tn];
if (rc) {
rc = -EINVAL;
goto exit;
}
/* Deactivate all lchans */
l1sched_deactivate_all_lchans(ts);
/* Activate only requested lchans */
rc = l1sched_set_lchans(ts, chan_nr, 1, est_req->tch_mode, est_req->tsc);
if (rc) {
LOGPFSMSL(trxcon->fi, DL1C, LOGL_ERROR, "Couldn't activate requested lchans\n");
rc = -EINVAL;
goto exit;
}
osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_DEDICATED_ESTABLISH_REQ, &req);
exit:
msgb_free(msg);
@ -717,11 +613,9 @@ static int l1ctl_rx_dm_rel_req(struct l1ctl_client *l1c, struct msgb *msg)
{
struct trxcon_inst *trxcon = l1c->priv;
LOGPFSMSL(trxcon->fi, DL1C, LOGL_NOTICE,
"Received L1CTL_DM_REL_REQ, resetting scheduler\n");
LOGPFSMSL(trxcon->fi, DL1C, LOGL_NOTICE, "Received L1CTL_DM_REL_REQ\n");
/* Reset scheduler */
l1sched_reset(trxcon->sched, false);
osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_DEDICATED_RELEASE_REQ, NULL);
msgb_free(msg);
return 0;
@ -735,34 +629,29 @@ static int l1ctl_rx_dt_req(struct l1ctl_client *l1c,
{
struct trxcon_inst *trxcon = l1c->priv;
struct l1ctl_info_ul *ul;
struct l1sched_ts_prim *prim;
uint8_t chan_nr, link_id;
size_t payload_len;
int rc;
/* Extract UL frame header */
ul = (struct l1ctl_info_ul *) msg->l1h;
/* Calculate the payload len */
msg->l2h = ul->payload;
payload_len = msgb_l2len(msg);
/* Obtain channel description */
chan_nr = ul->chan_nr;
link_id = ul->link_id & 0x40;
struct trxcon_param_tx_traffic_data_req req = {
.chan_nr = ul->chan_nr,
.link_id = ul->link_id & 0x40,
.data_len = msgb_l2len(msg),
.data = ul->payload,
};
LOGPFSMSL(trxcon->fi, DL1D, LOGL_DEBUG,
"Recv %s Req (chan_nr=0x%02x, link_id=0x%02x, len=%zu)\n",
traffic ? "TRAFFIC" : "DATA", chan_nr, link_id, payload_len);
traffic ? "TRAFFIC" : "DATA", req.chan_nr, req.link_id, req.data_len);
/* Push this primitive to transmit queue */
prim = l1sched_prim_push(trxcon->sched, L1SCHED_PRIM_DATA,
chan_nr, link_id, ul->payload, payload_len);
if (prim == NULL)
rc = -ENOMEM;
if (traffic)
osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_TX_TRAFFIC_REQ, &req);
else
osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_TX_DATA_REQ, &req);
msgb_free(msg);
return rc;
return 0;
}
static int l1ctl_rx_param_req(struct l1ctl_client *l1c, struct msgb *msg)
@ -778,13 +667,12 @@ static int l1ctl_rx_param_req(struct l1ctl_client *l1c, struct msgb *msg)
"Received L1CTL_PARAM_REQ (ta=%d, tx_power=%u)\n",
par_req->ta, par_req->tx_power);
/* Instruct TRX to use new TA value */
if (trxcon->trx->ta != par_req->ta) {
trx_if_cmd_setta(trxcon->trx, par_req->ta);
trxcon->trx->ta = par_req->ta;
}
struct trxcon_param_set_config_req req = {
.timing_advance = par_req->ta,
.tx_power = par_req->tx_power,
};
trxcon->trx->tx_power = par_req->tx_power;
osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_SET_CONFIG_REQ, &req);
msgb_free(msg);
return 0;
@ -793,41 +681,27 @@ static int l1ctl_rx_param_req(struct l1ctl_client *l1c, struct msgb *msg)
static int l1ctl_rx_tch_mode_req(struct l1ctl_client *l1c, struct msgb *msg)
{
struct trxcon_inst *trxcon = l1c->priv;
struct l1ctl_tch_mode_req *req;
struct l1sched_lchan_state *lchan;
struct l1sched_ts *ts;
unsigned int tn;
struct l1ctl_tch_mode_req *mode_req;
int rc;
req = (struct l1ctl_tch_mode_req *) msg->l1h;
mode_req = (struct l1ctl_tch_mode_req *)msg->l1h;
LOGPFSMSL(trxcon->fi, DL1C, LOGL_NOTICE,
"Received L1CTL_TCH_MODE_REQ (tch_mode=%u, audio_mode=%u)\n",
req->tch_mode, req->audio_mode);
/* Iterate over timeslot list */
for (tn = 0; tn < ARRAY_SIZE(trxcon->sched->ts); tn++) {
/* Timeslot is not allocated */
ts = trxcon->sched->ts[tn];
if (ts == NULL)
continue;
/* Timeslot is not configured */
if (ts->mf_layout == NULL)
continue;
/* Iterate over all allocated lchans */
llist_for_each_entry(lchan, &ts->lchans, list) {
/* Omit inactive channels */
if (!lchan->active)
continue;
/* Set TCH mode */
lchan->tch_mode = req->tch_mode;
}
}
mode_req->tch_mode, mode_req->audio_mode);
/* TODO: do we need to care about audio_mode? */
struct trxcon_param_set_ccch_tch_mode_req req = {
.mode = mode_req->tch_mode,
};
rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_SET_TCH_MODE_REQ, &req);
if (rc != 0 || !req.applied) {
talloc_free(msg);
return rc;
}
/* Re-use the original message as confirmation */
struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
l1h->msg_type = L1CTL_TCH_MODE_CONF;
@ -838,41 +712,27 @@ static int l1ctl_rx_tch_mode_req(struct l1ctl_client *l1c, struct msgb *msg)
static int l1ctl_rx_crypto_req(struct l1ctl_client *l1c, struct msgb *msg)
{
struct trxcon_inst *trxcon = l1c->priv;
struct l1ctl_crypto_req *req;
struct l1ctl_crypto_req *cr;
struct l1ctl_info_ul *ul;
struct l1sched_ts *ts;
uint8_t tn;
int rc = 0;
ul = (struct l1ctl_info_ul *) msg->l1h;
req = (struct l1ctl_crypto_req *) ul->payload;
cr = (struct l1ctl_crypto_req *) ul->payload;
struct trxcon_param_crypto_req req = {
.chan_nr = ul->chan_nr,
.a5_algo = cr->algo,
.key_len = cr->key_len,
.key = cr->key,
};
LOGPFSMSL(trxcon->fi, DL1C, LOGL_NOTICE,
"L1CTL_CRYPTO_REQ (algo=A5/%u, key_len=%u)\n",
req->algo, req->key_len);
req.a5_algo, req.key_len);
/* Determine TS index */
tn = ul->chan_nr & 0x7;
osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_CRYPTO_REQ, &req);
/* Make sure that required TS is allocated and configured */
ts = trxcon->sched->ts[tn];
if (ts == NULL || ts->mf_layout == NULL) {
LOGPFSMSL(trxcon->fi, DL1C, LOGL_ERROR, "TS %u is not configured\n", tn);
rc = -EINVAL;
goto exit;
}
/* Poke scheduler */
rc = l1sched_start_ciphering(ts, req->algo, req->key, req->key_len);
if (rc) {
LOGPFSMSL(trxcon->fi, DL1C, LOGL_ERROR, "Couldn't configure ciphering\n");
rc = -EINVAL;
goto exit;
}
exit:
msgb_free(msg);
return rc;
return 0;
}
int l1ctl_rx_cb(struct l1ctl_client *l1c, struct msgb *msg)

View File

@ -40,7 +40,6 @@
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/bb/l1sched/l1sched.h>
#include <osmocom/bb/trxcon/l1ctl.h>
#include <osmocom/bb/trxcon/trxcon.h>
#include <osmocom/bb/trxcon/trx_if.h>
#include <osmocom/bb/trxcon/logging.h>
@ -163,7 +162,6 @@ static void trx_ctrl_send(struct trx_instance *trx)
static void trx_ctrl_timer_cb(void *data)
{
struct trx_instance *trx = (struct trx_instance *) data;
struct trxcon_inst *trxcon = trx->trxcon;
struct trx_ctrl_msg *tcm;
/* Queue may be cleaned at this moment */
@ -176,7 +174,7 @@ static void trx_ctrl_timer_cb(void *data)
if (++tcm->retry_cnt > 3) {
LOGPFSML(trx->fi, LOGL_NOTICE, "Transceiver offline\n");
osmo_fsm_inst_state_chg(trx->fi, TRX_STATE_OFFLINE, 0, 0);
osmo_fsm_inst_dispatch(trxcon->fi, TRX_EVENT_OFFLINE, trx);
osmo_fsm_inst_term(trx->fi, OSMO_FSM_TERM_TIMEOUT, NULL);
return;
}
@ -388,9 +386,13 @@ static void trx_if_measure_rsp_cb(struct trx_instance *trx, char *resp)
return;
}
/* Send L1CTL_PM_CONF */
l1ctl_tx_pm_conf(trxcon->l1c, band_arfcn, dbm,
band_arfcn == trx->pm_band_arfcn_stop);
struct trxcon_param_full_power_scan_res res = {
.last_result = band_arfcn == trx->pm_band_arfcn_stop,
.band_arfcn = band_arfcn,
.dbm = dbm,
};
osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_FULL_POWER_SCAN_RES, &res);
/* Schedule a next measurement */
if (band_arfcn != trx->pm_band_arfcn_stop)
@ -479,7 +481,6 @@ int trx_if_cmd_setfh(struct trx_instance *trx, uint8_t hsn, uint8_t maio,
static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what)
{
struct trx_instance *trx = ofd->data;
struct trxcon_inst *trxcon = trx->trxcon;
struct trx_ctrl_msg *tcm;
int resp, rsp_len;
char buf[TRXC_BUF_SIZE], *p;
@ -560,8 +561,7 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what)
return 0;
rsp_error:
/* Notify higher layers about the problem */
osmo_fsm_inst_dispatch(trxcon->fi, TRX_EVENT_RSP_ERROR, trx);
osmo_fsm_inst_term(trx->fi, OSMO_FSM_TERM_ERROR, NULL);
return -EIO;
}
@ -712,7 +712,7 @@ struct trx_instance *trx_if_open(struct trxcon_inst *trxcon,
}
/* Allocate a new dedicated state machine */
trx->fi = osmo_fsm_inst_alloc_child(&trx_fsm, trxcon->fi, TRX_EVENT_OFFLINE);
trx->fi = osmo_fsm_inst_alloc_child(&trx_fsm, trxcon->fi, TRXCON_EV_L1IF_FAILURE);
if (trx->fi == NULL) {
LOGPFSML(trxcon->fi, LOGL_ERROR, "Failed to allocate an instance "
"of FSM '%s'\n", trx_fsm.name);

View File

@ -49,8 +49,6 @@
#include <osmocom/bb/trxcon/l1ctl_proto.h>
#include <osmocom/bb/l1sched/l1sched.h>
#define S(x) (1 << (x))
#define COPYRIGHT \
"Copyright (C) 2016-2022 by Vadim Yanitskiy <axilirator@gmail.com>\n" \
"Contributions by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>\n" \
@ -142,35 +140,35 @@ int l1sched_handle_data_ind(struct l1sched_lchan_state *lchan,
const struct l1sched_lchan_desc *lchan_desc;
struct l1sched_state *sched = lchan->ts->sched;
struct trxcon_inst *trxcon = sched->priv;
struct l1ctl_info_dl dl_hdr;
int rc;
lchan_desc = &l1sched_lchan_desc[lchan->type];
dl_hdr = (struct l1ctl_info_dl) {
struct trxcon_param_rx_traffic_data_ind ind = {
.chan_nr = lchan_desc->chan_nr | lchan->ts->index,
.link_id = lchan_desc->link_id,
.frame_nr = htonl(meas->fn),
.band_arfcn = htons(trxcon->trx->band_arfcn),
.fire_crc = data_len > 0 ? 0 : 2,
.rx_level = dbm2rxlev(meas->rssi),
.num_biterr = n_errors,
/* TODO: set proper .snr */
.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_TRAFFIC:
case L1SCHED_DT_PACKET_DATA:
rc = l1ctl_tx_dt_ind(trxcon->l1c, &dl_hdr, data, data_len, true);
rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_RX_TRAFFIC_IND, &ind);
break;
case L1SCHED_DT_SIGNALING:
rc = l1ctl_tx_dt_ind(trxcon->l1c, &dl_hdr, data, data_len, false);
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->fbsb_conf_sent)
if (trxcon->fi->state != TRXCON_ST_FBSB_SEARCH)
return 0;
rc = l1ctl_tx_fbsb_conf(trxcon->l1c, 0, &dl_hdr, sched->bsic);
rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_FBSB_SEARCH_RES, NULL);
break;
}
/* fall through */
@ -255,75 +253,6 @@ int l1sched_handle_data_cnf(struct l1sched_lchan_state *lchan,
return rc;
}
/* The trxcon state machine */
static void trxcon_fsm_idle_action(struct osmo_fsm_inst *fi,
uint32_t event, void *data)
{
if (event == L1CTL_EVENT_CONNECT)
osmo_fsm_inst_state_chg(fi, TRXCON_STATE_MANAGED, 0, 0);
}
static void trxcon_fsm_managed_action(struct osmo_fsm_inst *fi,
uint32_t event, void *data)
{
struct trxcon_inst *trxcon = fi->priv;
switch (event) {
case L1CTL_EVENT_DISCONNECT:
osmo_fsm_inst_state_chg(fi, TRXCON_STATE_IDLE, 0, 0);
if (trxcon->trx->fi->state != TRX_STATE_OFFLINE) {
/* Reset scheduler and clock counter */
l1sched_reset(trxcon->sched, true);
/* TODO: implement trx_if_reset() */
trx_if_cmd_poweroff(trxcon->trx);
trx_if_cmd_echo(trxcon->trx);
}
break;
case TRX_EVENT_RSP_ERROR:
case TRX_EVENT_OFFLINE:
/* TODO: notify L2 & L3 about that */
break;
default:
LOGPFSML(fi, LOGL_ERROR, "Unhandled event %u\n", event);
}
}
static struct osmo_fsm_state trxcon_fsm_states[] = {
[TRXCON_STATE_IDLE] = {
.in_event_mask = S(L1CTL_EVENT_CONNECT),
.out_state_mask = S(TRXCON_STATE_MANAGED),
.name = "IDLE",
.action = trxcon_fsm_idle_action,
},
[TRXCON_STATE_MANAGED] = {
.in_event_mask = (
S(L1CTL_EVENT_DISCONNECT) |
S(TRX_EVENT_RSP_ERROR) |
S(TRX_EVENT_OFFLINE)),
.out_state_mask = S(TRXCON_STATE_IDLE),
.name = "MANAGED",
.action = trxcon_fsm_managed_action,
},
};
static const struct value_string trxcon_fsm_event_names[] = {
OSMO_VALUE_STRING(L1CTL_EVENT_CONNECT),
OSMO_VALUE_STRING(L1CTL_EVENT_DISCONNECT),
OSMO_VALUE_STRING(TRX_EVENT_OFFLINE),
OSMO_VALUE_STRING(TRX_EVENT_RSP_ERROR),
{ 0, NULL }
};
static struct osmo_fsm trxcon_fsm_def = {
.name = "trxcon",
.states = trxcon_fsm_states,
.num_states = ARRAY_SIZE(trxcon_fsm_states),
.log_subsys = DAPP,
.event_names = trxcon_fsm_event_names,
};
struct trxcon_inst *trxcon_inst_alloc(void *ctx, unsigned int id)
{
struct trxcon_inst *trxcon;
@ -377,8 +306,6 @@ void trxcon_inst_free(struct trxcon_inst *trxcon)
if (trxcon->trx != NULL)
trx_if_close(trxcon->trx);
osmo_timer_del(&trxcon->fbsb_timer);
if (trxcon->fi != NULL)
osmo_fsm_inst_free(trxcon->fi);
talloc_free(trxcon);
@ -406,6 +333,8 @@ static void l1ctl_conn_close_cb(struct l1ctl_client *l1c)
if (trxcon == NULL)
return;
osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_L2IF_FAILURE, NULL);
/* l1c is free()ed by the caller */
trxcon->l1c = NULL;
trxcon_inst_free(trxcon);
@ -560,6 +489,8 @@ int main(int argc, char **argv)
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME);
log_set_print_filename_pos(osmo_stderr_target, LOG_FILENAME_POS_LINE_END);
osmo_fsm_log_timeouts(true);
/* Optional GSMTAP */
if (app_data.gsmtap_ip != NULL) {
app_data.gsmtap = gsmtap_source_init(app_data.gsmtap_ip, GSMTAP_UDP_PORT, 1);
@ -571,9 +502,6 @@ int main(int argc, char **argv)
gsmtap_source_add_sink(app_data.gsmtap);
}
/* Register the trxcon state machine */
OSMO_ASSERT(osmo_fsm_register(&trxcon_fsm_def) == 0);
/* Start the L1CTL server */
server_cfg = (struct l1ctl_server_cfg) {
.sock_path = app_data.bind_socket,

View File

@ -0,0 +1,481 @@
/*
* 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 <stdio.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <osmocom/core/fsm.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
#include <osmocom/bb/trxcon/trxcon.h>
#include <osmocom/bb/trxcon/trx_if.h>
#include <osmocom/bb/trxcon/logging.h>
#include <osmocom/bb/trxcon/l1ctl.h>
#include <osmocom/bb/trxcon/l1ctl_server.h>
#include <osmocom/bb/trxcon/l1ctl_proto.h>
#include <osmocom/bb/l1sched/l1sched.h>
#define S(x) (1 << (x))
static void trxcon_allstate_action(struct osmo_fsm_inst *fi,
uint32_t event, void *data)
{
struct trxcon_inst *trxcon = fi->priv;
switch (event) {
case TRXCON_EV_L1IF_FAILURE:
case TRXCON_EV_L2IF_FAILURE:
LOGPFSML(fi, LOGL_NOTICE, "Event %s is not handled\n",
osmo_fsm_event_name(&trxcon_fsm_def, event));
/* TODO: osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); */
break;
case TRXCON_EV_RESET_FULL_REQ:
if (fi->state != TRXCON_ST_RESET)
osmo_fsm_inst_state_chg(fi, TRXCON_ST_RESET, 0, 0);
l1sched_reset(trxcon->sched, true);
trx_if_cmd_poweroff(trxcon->trx);
trx_if_cmd_echo(trxcon->trx);
break;
case TRXCON_EV_RESET_SCHED_REQ:
l1sched_reset(trxcon->sched, false);
break;
case TRXCON_EV_SET_CONFIG_REQ:
{
const struct trxcon_param_set_config_req *req = data;
if (trxcon->trx->ta != req->timing_advance)
trx_if_cmd_setta(trxcon->trx, req->timing_advance);
trxcon->trx->tx_power = req->tx_power;
trxcon->trx->ta = req->timing_advance;
break;
}
default:
OSMO_ASSERT(0);
}
}
static int trxcon_timer_cb(struct osmo_fsm_inst *fi)
{
struct trxcon_inst *trxcon = fi->priv;
switch (fi->state) {
case TRXCON_ST_FBSB_SEARCH:
l1ctl_tx_fbsb_fail(trxcon->l1c, trxcon->trx->band_arfcn);
osmo_fsm_inst_state_chg(fi, TRXCON_ST_RESET, 0, 0);
return 0;
default:
OSMO_ASSERT(0);
}
}
static void trxcon_st_reset_action(struct osmo_fsm_inst *fi,
uint32_t event, void *data)
{
struct trxcon_inst *trxcon = fi->priv;
switch (event) {
case TRXCON_EV_FBSB_SEARCH_REQ:
{
const struct trxcon_param_fbsb_search_req *req = data;
osmo_fsm_inst_state_chg_ms(fi, TRXCON_ST_FBSB_SEARCH, req->timeout_ms, 0);
l1sched_configure_ts(trxcon->sched, 0, req->pchan_config);
/* Only if current ARFCN differs */
if (trxcon->trx->band_arfcn != req->band_arfcn) {
/* Update current ARFCN */
trxcon->trx->band_arfcn = req->band_arfcn;
/* Tune transceiver to required ARFCN */
trx_if_cmd_rxtune(trxcon->trx, req->band_arfcn);
trx_if_cmd_txtune(trxcon->trx, req->band_arfcn);
}
/* Transceiver might have been powered on before, e.g.
* in case of sending L1CTL_FBSB_REQ due to signal loss. */
if (!trxcon->trx->powered_up)
trx_if_cmd_poweron(trxcon->trx);
break;
}
case TRXCON_EV_FULL_POWER_SCAN_REQ:
{
const struct trxcon_param_full_power_scan_req *req = data;
osmo_fsm_inst_state_chg(fi, TRXCON_ST_FULL_POWER_SCAN, 0, 0); /* TODO: timeout */
trx_if_cmd_measure(trxcon->trx, req->band_arfcn_start, req->band_arfcn_stop);
break;
}
default:
OSMO_ASSERT(0);
}
}
static void trxcon_st_full_power_scan_action(struct osmo_fsm_inst *fi,
uint32_t event, void *data)
{
struct trxcon_inst *trxcon = fi->priv;
switch (event) {
case TRXCON_EV_FULL_POWER_SCAN_RES:
{
const struct trxcon_param_full_power_scan_res *res = data;
l1ctl_tx_pm_conf(trxcon->l1c, res->band_arfcn, res->dbm, res->last_result);
break;
}
default:
OSMO_ASSERT(0);
}
}
static void trxcon_st_fbsb_search_action(struct osmo_fsm_inst *fi,
uint32_t event, void *data)
{
struct trxcon_inst *trxcon = fi->priv;
switch (event) {
case TRXCON_EV_FBSB_SEARCH_RES:
osmo_fsm_inst_state_chg(fi, TRXCON_ST_BCCH_CCCH, 0, 0);
l1ctl_tx_fbsb_conf(trxcon->l1c,
trxcon->trx->band_arfcn,
trxcon->sched->bsic);
break;
default:
OSMO_ASSERT(0);
}
}
static void handle_tx_access_burst_req(struct osmo_fsm_inst *fi,
const struct trxcon_param_tx_access_burst_req *req)
{
struct trxcon_inst *trxcon = fi->priv;
enum l1sched_ts_prim_type prim_type;
const struct l1sched_ts_prim *prim;
const struct l1sched_ts_prim_rach rach = {
.synch_seq = req->synch_seq,
.offset = req->offset,
.ra = req->ra,
};
prim_type = req->is_11bit ? L1SCHED_PRIM_RACH11 : L1SCHED_PRIM_RACH8;
prim = l1sched_prim_push(trxcon->sched, prim_type,
req->chan_nr, req->link_id,
(const uint8_t *)&rach, sizeof(rach));
if (prim == NULL)
LOGPFSML(fi, LOGL_ERROR, "Failed to enqueue a prim\n");
}
static void trxcon_st_bcch_ccch_action(struct osmo_fsm_inst *fi,
uint32_t event, void *data)
{
struct trxcon_inst *trxcon = fi->priv;
struct l1sched_ts *ts;
int rc;
switch (event) {
case TRXCON_EV_TX_ACCESS_BURST_REQ:
handle_tx_access_burst_req(fi, data);
break;
case TRXCON_EV_SET_CCCH_MODE_REQ:
{
struct trxcon_param_set_ccch_tch_mode_req *req = data;
enum gsm_phys_chan_config chan_config = req->mode;
/* Make sure that TS0 is allocated and configured */
ts = trxcon->sched->ts[0];
if (ts == NULL || ts->mf_layout == NULL) {
LOGPFSML(fi, LOGL_ERROR, "TS0 is not configured\n");
return;
}
/* Do nothing if the current mode matches required */
if (ts->mf_layout->chan_config != chan_config)
l1sched_configure_ts(trxcon->sched, 0, chan_config);
req->applied = true;
break;
}
case TRXCON_EV_DEDICATED_ESTABLISH_REQ:
{
const struct trxcon_param_dedicated_establish_req *req = data;
enum gsm_phys_chan_config config;
config = l1sched_chan_nr2pchan_config(req->chan_nr);
if (config == GSM_PCHAN_NONE) {
LOGPFSML(fi, LOGL_ERROR, "Failed to determine channel config\n");
return;
}
if (req->hopping) {
/* Apply the freq. hopping parameters */
rc = trx_if_cmd_setfh(trxcon->trx,
req->h1.hsn, req->h1.maio,
&req->h1.ma[0], req->h1.n);
if (rc)
return;
/* Set current ARFCN to an invalid value */
trxcon->trx->band_arfcn = 0xffff;
} else {
/* Tune transceiver to required ARFCN */
if (trx_if_cmd_rxtune(trxcon->trx, req->h0.band_arfcn))
return;
if (trx_if_cmd_txtune(trxcon->trx, req->h0.band_arfcn))
return;
/* Update current ARFCN */
trxcon->trx->band_arfcn = req->h0.band_arfcn;
}
rc = l1sched_configure_ts(trxcon->sched, req->chan_nr & 0x07, config);
if (rc)
return;
ts = trxcon->sched->ts[req->chan_nr & 0x07];
OSMO_ASSERT(ts != NULL);
l1sched_deactivate_all_lchans(ts);
/* Activate only requested lchans */
rc = l1sched_set_lchans(ts, req->chan_nr, 1, req->tch_mode, req->tsc);
if (rc) {
LOGPFSML(fi, LOGL_ERROR, "Failed to activate requested lchans\n");
return;
}
osmo_fsm_inst_state_chg(fi, TRXCON_ST_DEDICATED, 0, 0);
break;
}
case TRXCON_EV_RX_DATA_IND:
{
const struct trxcon_param_rx_traffic_data_ind *ind = data;
const struct l1ctl_info_dl dl_hdr = {
.chan_nr = ind->chan_nr,
.link_id = ind->link_id,
.frame_nr = htonl(ind->frame_nr),
.band_arfcn = htons(trxcon->trx->band_arfcn),
.fire_crc = ind->data_len > 0 ? 0 : 2,
.rx_level = dbm2rxlev(ind->rssi),
.num_biterr = ind->n_errors,
/* TODO: set proper .snr */
};
l1ctl_tx_dt_ind(trxcon->l1c, &dl_hdr, ind->data, ind->data_len, false);
break;
}
default:
OSMO_ASSERT(0);
}
}
static void trxcon_st_dedicated_action(struct osmo_fsm_inst *fi,
uint32_t event, void *data)
{
struct trxcon_inst *trxcon = fi->priv;
switch (event) {
case TRXCON_EV_TX_ACCESS_BURST_REQ:
handle_tx_access_burst_req(fi, data);
break;
case TRXCON_EV_DEDICATED_RELEASE_REQ:
l1sched_reset(trxcon->sched, false);
osmo_fsm_inst_state_chg(fi, TRXCON_ST_RESET, 0, 0);
break;
case TRXCON_EV_SET_TCH_MODE_REQ:
{
struct trxcon_param_set_ccch_tch_mode_req *req = data;
unsigned int tn;
/* Iterate over timeslot list */
for (tn = 0; tn < ARRAY_SIZE(trxcon->sched->ts); tn++) {
struct l1sched_ts *ts = trxcon->sched->ts[tn];
struct l1sched_lchan_state *lchan;
/* Timeslot is not allocated */
if (ts == NULL || ts->mf_layout == NULL)
continue;
/* Iterate over all allocated lchans */
llist_for_each_entry(lchan, &ts->lchans, list) {
/* Omit inactive channels */
if (!lchan->active)
continue;
lchan->tch_mode = req->mode;
req->applied = true;
}
}
break;
}
case TRXCON_EV_CRYPTO_REQ:
{
const struct trxcon_param_crypto_req *req = data;
unsigned int tn = req->chan_nr & 0x07;
struct l1sched_ts *ts;
/* Make sure that required TS is allocated and configured */
ts = trxcon->sched->ts[tn];
if (ts == NULL || ts->mf_layout == NULL) {
LOGPFSML(fi, LOGL_ERROR, "TS%u is not configured\n", tn);
return;
}
if (l1sched_start_ciphering(ts, req->a5_algo, req->key, req->key_len) != 0) {
LOGPFSML(fi, LOGL_ERROR, "Failed to configure ciphering\n");
return;
}
break;
}
case TRXCON_EV_TX_TRAFFIC_REQ:
case TRXCON_EV_TX_DATA_REQ:
{
const struct trxcon_param_tx_traffic_data_req *req = data;
struct l1sched_ts_prim *prim;
prim = l1sched_prim_push(trxcon->sched, L1SCHED_PRIM_DATA,
req->chan_nr, req->link_id,
req->data, req->data_len);
if (prim == NULL) {
LOGPFSML(fi, LOGL_ERROR, "Failed to enqueue a prim\n");
return;
}
break;
}
case TRXCON_EV_RX_TRAFFIC_IND:
case TRXCON_EV_RX_DATA_IND:
{
const struct trxcon_param_rx_traffic_data_ind *ind = data;
const struct l1ctl_info_dl dl_hdr = {
.chan_nr = ind->chan_nr,
.link_id = ind->link_id,
.frame_nr = htonl(ind->frame_nr),
.band_arfcn = htons(trxcon->trx->band_arfcn),
.fire_crc = ind->data_len > 0 ? 0 : 2,
.rx_level = dbm2rxlev(ind->rssi),
.num_biterr = ind->n_errors,
/* TODO: set proper .snr */
};
l1ctl_tx_dt_ind(trxcon->l1c, &dl_hdr,
ind->data, ind->data_len,
event == TRXCON_EV_RX_TRAFFIC_IND);
break;
}
default:
OSMO_ASSERT(0);
}
}
static const struct osmo_fsm_state trxcon_fsm_states[] = {
[TRXCON_ST_RESET] = {
.name = "RESET",
.out_state_mask = S(TRXCON_ST_FBSB_SEARCH)
| S(TRXCON_ST_FULL_POWER_SCAN),
.in_event_mask = S(TRXCON_EV_FBSB_SEARCH_REQ)
| S(TRXCON_EV_FULL_POWER_SCAN_REQ),
.action = &trxcon_st_reset_action,
},
[TRXCON_ST_FULL_POWER_SCAN] = {
.name = "FULL_POWER_SCAN",
.out_state_mask = S(TRXCON_ST_RESET),
.in_event_mask = S(TRXCON_EV_FULL_POWER_SCAN_RES),
.action = &trxcon_st_full_power_scan_action,
},
[TRXCON_ST_FBSB_SEARCH] = {
.name = "FBSB_SEARCH",
.out_state_mask = S(TRXCON_ST_RESET)
| S(TRXCON_ST_BCCH_CCCH),
.in_event_mask = S(TRXCON_EV_FBSB_SEARCH_RES),
.action = &trxcon_st_fbsb_search_action,
},
[TRXCON_ST_BCCH_CCCH] = {
.name = "BCCH_CCCH",
.out_state_mask = S(TRXCON_ST_RESET)
| S(TRXCON_ST_FBSB_SEARCH)
| S(TRXCON_ST_DEDICATED),
.in_event_mask = S(TRXCON_EV_RX_DATA_IND)
| S(TRXCON_EV_SET_CCCH_MODE_REQ)
| S(TRXCON_EV_TX_ACCESS_BURST_REQ)
| S(TRXCON_EV_DEDICATED_ESTABLISH_REQ),
.action = &trxcon_st_bcch_ccch_action,
},
[TRXCON_ST_DEDICATED] = {
.name = "DEDICATED",
.out_state_mask = S(TRXCON_ST_RESET)
| S(TRXCON_ST_FBSB_SEARCH)
| S(TRXCON_ST_BCCH_CCCH),
.in_event_mask = S(TRXCON_EV_DEDICATED_RELEASE_REQ)
| S(TRXCON_EV_TX_ACCESS_BURST_REQ)
| S(TRXCON_EV_SET_TCH_MODE_REQ)
| S(TRXCON_EV_TX_TRAFFIC_REQ)
| S(TRXCON_EV_RX_TRAFFIC_IND)
| S(TRXCON_EV_TX_DATA_REQ)
| S(TRXCON_EV_RX_DATA_IND)
| S(TRXCON_EV_CRYPTO_REQ),
.action = &trxcon_st_dedicated_action,
},
};
static const struct value_string trxcon_fsm_event_names[] = {
OSMO_VALUE_STRING(TRXCON_EV_L1IF_FAILURE),
OSMO_VALUE_STRING(TRXCON_EV_L2IF_FAILURE),
OSMO_VALUE_STRING(TRXCON_EV_RESET_FULL_REQ),
OSMO_VALUE_STRING(TRXCON_EV_RESET_SCHED_REQ),
OSMO_VALUE_STRING(TRXCON_EV_FULL_POWER_SCAN_REQ),
OSMO_VALUE_STRING(TRXCON_EV_FULL_POWER_SCAN_RES),
OSMO_VALUE_STRING(TRXCON_EV_FBSB_SEARCH_REQ),
OSMO_VALUE_STRING(TRXCON_EV_FBSB_SEARCH_RES),
OSMO_VALUE_STRING(TRXCON_EV_SET_CCCH_MODE_REQ),
OSMO_VALUE_STRING(TRXCON_EV_SET_TCH_MODE_REQ),
OSMO_VALUE_STRING(TRXCON_EV_SET_CONFIG_REQ),
OSMO_VALUE_STRING(TRXCON_EV_TX_ACCESS_BURST_REQ),
OSMO_VALUE_STRING(TRXCON_EV_DEDICATED_ESTABLISH_REQ),
OSMO_VALUE_STRING(TRXCON_EV_DEDICATED_RELEASE_REQ),
OSMO_VALUE_STRING(TRXCON_EV_TX_TRAFFIC_REQ),
OSMO_VALUE_STRING(TRXCON_EV_RX_TRAFFIC_IND),
OSMO_VALUE_STRING(TRXCON_EV_TX_DATA_REQ),
OSMO_VALUE_STRING(TRXCON_EV_RX_DATA_IND),
OSMO_VALUE_STRING(TRXCON_EV_CRYPTO_REQ),
{ 0, NULL }
};
struct osmo_fsm trxcon_fsm_def = {
.name = "trxcon",
.states = trxcon_fsm_states,
.num_states = ARRAY_SIZE(trxcon_fsm_states),
.log_subsys = DAPP,
.event_names = trxcon_fsm_event_names,
.allstate_event_mask = S(TRXCON_EV_L1IF_FAILURE)
| S(TRXCON_EV_L2IF_FAILURE)
| S(TRXCON_EV_RESET_FULL_REQ)
| S(TRXCON_EV_RESET_SCHED_REQ)
| S(TRXCON_EV_SET_CONFIG_REQ),
.allstate_action = &trxcon_allstate_action,
.timer_cb = &trxcon_timer_cb,
};
static __attribute__((constructor)) void on_dso_load(void)
{
OSMO_ASSERT(osmo_fsm_register(&trxcon_fsm_def) == 0);
}