fixeria has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmocom-bb/+/28809 )
Change subject: trxcon: rework trxcon_fsm, move into a separate file ......................................................................
trxcon: rework trxcon_fsm, move into a separate file
Change-Id: Ifaf63ead9dd180181358e771367b2a686ba159ca Related: OS#5599 --- M src/host/trxcon/include/osmocom/bb/trxcon/l1ctl.h M src/host/trxcon/include/osmocom/bb/trxcon/trxcon.h M src/host/trxcon/src/Makefile.am M src/host/trxcon/src/l1ctl.c M src/host/trxcon/src/logging.c M src/host/trxcon/src/trx_if.c M src/host/trxcon/src/trxcon.c A src/host/trxcon/src/trxcon_fsm.c 8 files changed, 670 insertions(+), 384 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmocom-bb refs/changes/09/28809/1
diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/l1ctl.h b/src/host/trxcon/include/osmocom/bb/trxcon/l1ctl.h index e15d5a2..3ad5406 100644 --- a/src/host/trxcon/include/osmocom/bb/trxcon/l1ctl.h +++ b/src/host/trxcon/include/osmocom/bb/trxcon/l1ctl.h @@ -9,6 +9,7 @@ /* Event handlers */ int l1ctl_rx_cb(struct l1ctl_client *l1c, struct msgb *msg);
+int l1ctl_tx_fbsb_fail(struct l1ctl_client *l1c, uint16_t band_arfcn); 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_ccch_mode_conf(struct l1ctl_client *l1c, uint8_t mode); diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/trxcon.h b/src/host/trxcon/include/osmocom/bb/trxcon/trxcon.h index da47f8b..df5ad64 100644 --- a/src/host/trxcon/include/osmocom/bb/trxcon/trxcon.h +++ b/src/host/trxcon/include/osmocom/bb/trxcon/trxcon.h @@ -4,19 +4,106 @@ 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_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_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_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 +118,6 @@ /* 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); diff --git a/src/host/trxcon/src/Makefile.am b/src/host/trxcon/src/Makefile.am index cf1357f..3af004d 100644 --- a/src/host/trxcon/src/Makefile.am +++ b/src/host/trxcon/src/Makefile.am @@ -42,6 +42,7 @@ l1ctl.c \ trx_if.c \ logging.c \ + trxcon_fsm.c \ trxcon.c \ $(NULL)
diff --git a/src/host/trxcon/src/l1ctl.c b/src/host/trxcon/src/l1ctl.c index 5e40af5..12f45e2 100644 --- a/src/host/trxcon/src/l1ctl.c +++ b/src/host/trxcon/src/l1ctl.c @@ -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,6 +166,28 @@ return conf; }
+int l1ctl_tx_fbsb_fail(struct l1ctl_client *l1c, uint16_t band_arfcn) +{ + 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 -ENOMEM; + + dl = put_dl_info_hdr(msg, NULL); + + /* 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, uint8_t result, const struct l1ctl_info_dl *dl_info, uint8_t bsic) { @@ -184,12 +206,6 @@ /* 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); @@ -299,44 +315,10 @@ } }
-/* 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 +330,19 @@ 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 +351,6 @@
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 +364,18 @@ 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 +402,10 @@
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, @@ -488,8 +442,7 @@ 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; + int rc;
req = (struct l1ctl_ccch_mode_req *) msg->l1h; if (msgb_l1len(msg) < sizeof(*req)) { @@ -503,24 +456,15 @@ LOGPFSMSL(trxcon->fi, DL1C, LOGL_NOTICE, "Received CCCH mode request (%u)\n", 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; - } - /* 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, &ch_config); + if (rc == 0) { + /* FIXME: we cannot know for sure if the given mode was actually applied. + * All we know is whether the event was dispatched. Blindly confirm. */ + l1ctl_tx_ccch_mode_conf(l1c, req->ccch_mode); + }
exit: msgb_free(msg); @@ -529,40 +473,38 @@
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 +512,38 @@ 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 +555,7 @@ 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 +563,45 @@
/* 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 +612,9 @@ { 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 +628,29 @@ { 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 +666,12 @@ "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; @@ -794,9 +681,7 @@ { struct trxcon_inst *trxcon = l1c->priv; struct l1ctl_tch_mode_req *req; - struct l1sched_lchan_state *lchan; - struct l1sched_ts *ts; - unsigned int tn; + int rc;
req = (struct l1ctl_tch_mode_req *) msg->l1h;
@@ -804,30 +689,14 @@ "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; - } - } - /* TODO: do we need to care about audio_mode? */
+ rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_SET_TCH_MODE_REQ, &req->tch_mode); + if (rc != 0) { + 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 +707,27 @@ 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) diff --git a/src/host/trxcon/src/logging.c b/src/host/trxcon/src/logging.c index 5c16ac5..e25af8b 100644 --- a/src/host/trxcon/src/logging.c +++ b/src/host/trxcon/src/logging.c @@ -28,7 +28,7 @@ .name = "DAPP", .description = "Application", .color = "\033[1;35m", - .enabled = 1, .loglevel = LOGL_NOTICE, + .enabled = 1, .loglevel = LOGL_DEBUG, }, [DL1C] = { .name = "DL1C", diff --git a/src/host/trxcon/src/trx_if.c b/src/host/trxcon/src/trx_if.c index a9a6c96..c960645 100644 --- a/src/host/trxcon/src/trx_if.c +++ b/src/host/trxcon/src/trx_if.c @@ -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_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 @@ 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 @@ 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 @@ 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 @@ 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 @@ }
/* 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, 0); // XXX if (trx->fi == NULL) { LOGPFSML(trxcon->fi, LOGL_ERROR, "Failed to allocate an instance " "of FSM '%s'\n", trx_fsm.name); diff --git a/src/host/trxcon/src/trxcon.c b/src/host/trxcon/src/trxcon.c index 14354d1..570a5a7 100644 --- a/src/host/trxcon/src/trxcon.c +++ b/src/host/trxcon/src/trxcon.c @@ -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" \ @@ -168,9 +166,10 @@ 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); + osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_FBSB_SEARCH_RES, NULL); + rc = l1ctl_tx_fbsb_conf(trxcon->l1c, 0, &dl_hdr, sched->bsic); // XXX break; } /* fall through */ @@ -255,75 +254,6 @@ 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 +307,6 @@ 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); @@ -560,6 +488,8 @@ 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 +501,6 @@ 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, diff --git a/src/host/trxcon/src/trxcon_fsm.c b/src/host/trxcon/src/trxcon_fsm.c new file mode 100644 index 0000000..9f56b41 --- /dev/null +++ b/src/host/trxcon/src/trxcon_fsm.c @@ -0,0 +1,419 @@ +/* + * 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 <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_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) +{ + switch (event) { + case TRXCON_EV_FBSB_SEARCH_RES: + osmo_fsm_inst_state_chg(fi, TRXCON_ST_BCCH_CCCH, 0, 0); + break; + default: + OSMO_ASSERT(0); + } +} + +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: + { + const struct trxcon_param_tx_access_burst_req *req = data; + enum l1sched_ts_prim_type prim_type; + 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"); + return; + } + break; + } + case TRXCON_EV_SET_CCCH_MODE_REQ: + { + const enum gsm_phys_chan_config *chan_config = data; + + /* 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"); + 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); + 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; + } + 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_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: + { + const uint8_t *tch_mode = 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 = *tch_mode; + } + } + 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: // XXX + case TRXCON_EV_RX_DATA_IND: // XXX + 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_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_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_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_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); +}