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:
parent
40d66990e0
commit
3e023128dc
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -42,6 +42,7 @@ trxcon_SOURCES = \
|
|||
l1ctl.c \
|
||||
trx_if.c \
|
||||
logging.c \
|
||||
trxcon_fsm.c \
|
||||
trxcon.c \
|
||||
$(NULL)
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
||||
/* 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;
|
||||
}
|
||||
mode_req->ccch_mode); /* TODO: add value-string for ccch_mode */
|
||||
|
||||
struct trxcon_param_set_ccch_tch_mode_req req = {
|
||||
/* Choose corresponding channel combination */
|
||||
ch_config = l1ctl_ccch_mode2pchan_config(req->ccch_mode);
|
||||
.mode = l1ctl_ccch_mode2pchan_config(mode_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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue