split lchan specific defines and code to its own file

It is really difficult right now to find out where all the different
stuff relative to operation and lifecycle of an lchan is. Let's move
everything to its own file to have all the related defines and logic
together.

Change-Id: Idd855d126c43ac6576c5f3ba7e0b8014127a65e1
This commit is contained in:
Pau Espin 2022-08-08 15:03:41 +02:00 committed by pespin
parent 742bb99ed9
commit 3d0fbe387f
22 changed files with 541 additions and 476 deletions

View File

@ -32,6 +32,7 @@ noinst_HEADERS = \
handover_vty.h \
ipaccess.h \
lb.h \
lchan.h \
lchan_fsm.h \
lchan_rtp_fsm.h \
lchan_select.h \

View File

@ -4,6 +4,7 @@
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/bsc/debug.h>
#include <osmocom/bsc/lchan.h>
/* This macro automatically includes a final \n, if omitted. */
#define LOG_ASSIGNMENT(conn, level, fmt, args...) do { \

View File

@ -35,6 +35,7 @@
#include <osmocom/bsc/acc.h>
#include <osmocom/bsc/osmux.h>
#include <osmocom/bsc/chan_counts.h>
#include <osmocom/bsc/lchan.h>
#define GSM_T3122_DEFAULT 10
@ -91,22 +92,6 @@ typedef int gsm_cbfn(unsigned int hooknum,
struct msgb *msg,
void *data, void *param);
/* Maximum number of neighbor cells whose average we track */
#define MAX_NEIGH_MEAS 10
/* Maximum size of the averaging window for neighbor cells */
#define MAX_WIN_NEIGH_AVG 10
/* Maximum number of report history we store */
#define MAX_MEAS_REP 10
/* processed neighbor measurements for one cell */
struct neigh_meas_proc {
uint16_t arfcn;
uint8_t bsic;
uint8_t rxlev[MAX_WIN_NEIGH_AVG];
unsigned int rxlev_cnt;
uint8_t last_seen_nr;
};
struct gsm_classmark {
bool classmark1_set;
struct gsm48_classmark1 classmark1;
@ -122,34 +107,6 @@ enum subscr_sccp_state {
SUBSCR_SCCP_ST_CONNECTED
};
enum channel_rate {
CH_RATE_SDCCH,
CH_RATE_HALF,
CH_RATE_FULL,
};
enum channel_rate chan_t_to_chan_rate(enum gsm_chan_t chan_t);
enum lchan_csd_mode {
LCHAN_CSD_M_NT,
LCHAN_CSD_M_T_1200_75,
LCHAN_CSD_M_T_600,
LCHAN_CSD_M_T_1200,
LCHAN_CSD_M_T_2400,
LCHAN_CSD_M_T_9600,
LCHAN_CSD_M_T_14400,
LCHAN_CSD_M_T_29000,
LCHAN_CSD_M_T_32000,
};
struct channel_mode_and_rate {
enum gsm48_chan_mode chan_mode;
enum channel_rate chan_rate;
uint16_t s15_s0;
/* only used for GSM48_CMODE_DATA_* */
enum lchan_csd_mode csd_mode;
};
enum assign_for {
ASSIGN_FOR_NONE,
ASSIGN_FOR_BSSMAP_REQ,
@ -161,14 +118,6 @@ extern const struct value_string assign_for_names[];
static inline const char *assign_for_name(enum assign_for assign_for)
{ return get_value_string(assign_for_names, assign_for); }
/* If .present is false, use the default value defined elsewhere. If true, use .val below.
* (A practical benefit of this is that the default initialization sets .present to false, so that even if a .val == 0
* is a valid value, a struct containing this as member does not need to explicitly set .val = INVALID_VAL_CONSTANT.) */
struct optional_val {
bool present;
int val;
};
/* Information retrieved during an Assignment Request from the MSC. This is storage of the Assignment instructions
* parsed from the Assignment Request message, to pass on until the gscon and assignment FSMs have decided whether an
* Assignment is actually going to be carried out. Should remain unchanged after initial decoding. */
@ -444,21 +393,6 @@ struct osmo_bsc_data;
struct osmo_bsc_sccp_con;
/* Channel Request reason */
enum gsm_chreq_reason_t {
GSM_CHREQ_REASON_EMERG,
GSM_CHREQ_REASON_PAG,
GSM_CHREQ_REASON_CALL,
GSM_CHREQ_REASON_LOCATION_UPD,
GSM_CHREQ_REASON_OTHER,
GSM_CHREQ_REASON_PDCH,
};
static inline bool gsm_chreq_reason_is_voicecall(enum gsm_chreq_reason_t reason)
{
return reason == GSM_CHREQ_REASON_EMERG || reason == GSM_CHREQ_REASON_CALL;
}
/* lchans 0..3 are SDCCH in combined channel configuration,
use 4 as magic number for BCCH hack - see osmo-bts-../oml.c:opstart_compl() */
#define CCCH_LCHAN 4
@ -556,11 +490,6 @@ struct om2k_mo {
* rest octets elements, so to really fit 48 EARFCNs most other SI2quater elements need to be omitted. */
#define MAX_EARFCN_LIST (3*16)
/* is the data link established? who established it? */
#define LCHAN_SAPI_UNUSED 0
#define LCHAN_SAPI_MS 1
#define LCHAN_SAPI_NET 2
/* BTS ONLY */
#define MAX_NUM_UL_MEAS 104
#define LC_UL_M_F_L1_VALID (1 << 0)
@ -599,33 +528,6 @@ struct amr_multirate_conf {
};
/* /BTS ONLY */
/* State of the SAPIs in the lchan */
enum lchan_sapi_state {
LCHAN_SAPI_S_NONE,
LCHAN_SAPI_S_REQ,
LCHAN_SAPI_S_ASSIGNED,
LCHAN_SAPI_S_REL,
LCHAN_SAPI_S_ERROR,
};
#define MAX_A5_KEY_LEN (128/8)
struct gsm_encr {
uint8_t alg_a5_n; /* N: 0 (A5/0), 1 (A5/1), ... 7 (A5/7) */
uint8_t key_len;
uint8_t key[MAX_A5_KEY_LEN];
bool kc128_present;
uint8_t kc128[16];
};
#define LOGPLCHAN(lchan, ss, level, fmt, args...) \
LOGP(ss, level, "%s (ss=%d,%s) (%s) " fmt, \
lchan ? gsm_ts_and_pchan_name(lchan->ts) : "-", \
lchan ? lchan->nr : 0, \
lchan ? gsm_chan_t_name(lchan->type) : "-", \
bsc_subscr_name(lchan && lchan->conn ? lchan->conn->bsub : NULL), \
## args)
/* Iterate at most N lchans of the given timeslot.
* usage:
* struct gsm_lchan *lchan;
@ -641,260 +543,9 @@ struct gsm_encr {
&& ((lchan - (ts)->lchan) < (N)); \
lchan++)
enum lchan_activate_for {
ACTIVATE_FOR_NONE,
ACTIVATE_FOR_MS_CHANNEL_REQUEST,
ACTIVATE_FOR_ASSIGNMENT,
ACTIVATE_FOR_HANDOVER,
ACTIVATE_FOR_VTY,
ACTIVATE_FOR_MODE_MODIFY_RTP,
};
extern const struct value_string lchan_activate_mode_names[];
static inline const char *lchan_activate_mode_name(enum lchan_activate_for activ_for)
{ return get_value_string(lchan_activate_mode_names, activ_for); }
enum imm_ass_time {
IMM_ASS_TIME_POST_CHAN_ACK = 0,
IMM_ASS_TIME_PRE_CHAN_ACK,
IMM_ASS_TIME_PRE_TS_ACK,
};
struct lchan_activate_info {
enum lchan_activate_for activ_for;
/* If activ_for == ACTIVATE_FOR_MS_CHANNEL_REQUEST, the original CHREQ reason. */
enum gsm_chreq_reason_t chreq_reason;
struct gsm_subscriber_connection *for_conn;
struct channel_mode_and_rate ch_mode_rate;
struct gsm_encr encr;
bool requires_voice_stream;
bool wait_before_switching_rtp; /*< true = requires LCHAN_EV_READY_TO_SWITCH_RTP */
uint16_t msc_assigned_cic;
/* During intra-BSC handover, we keep the MGW endpoint intact and just re-route to the new lchan. This
* activate_info is for the new lchan, the re_use_mgw_endpoint_from_lchan points at the old lchan. */
struct gsm_lchan *re_use_mgw_endpoint_from_lchan;
bool ta_known;
uint8_t ta;
/* The TSC Set to use if 'use' is true, otherwise automatically determine the TSC Set value to use. Valid range
* is 1 to 4, as described in 3GPP TS 45.002. */
struct optional_val tsc_set;
/* The TSC to use if 'use' is true, otherwise automatically determine the TSC value to use. Valid range is 0 to
* 7, as described in 3GPP TS 45.002. */
struct optional_val tsc;
bool vamos;
/* A copy of bts->imm_ass_time at the time where Channel Activation was requested. A change in the VTY
* configuration has immediate effect on the value, so make sure we don't get mixed up when it gets changed
* while a channel activation is in progress. */
enum imm_ass_time imm_ass_time;
};
enum lchan_modify_for {
MODIFY_FOR_NONE,
MODIFY_FOR_ASSIGNMENT,
MODIFY_FOR_VTY,
};
extern const struct value_string lchan_modify_for_names[];
static inline const char *lchan_modify_for_name(enum lchan_modify_for modify_for)
{ return get_value_string(lchan_modify_for_names, modify_for); }
struct lchan_modify_info {
enum lchan_modify_for modify_for;
struct channel_mode_and_rate ch_mode_rate;
bool requires_voice_stream;
uint16_t msc_assigned_cic;
/* The TSC Set to use if 'use' is true, otherwise automatically determine the TSC Set value to use. Valid range
* is 1 to 4, as described in 3GPP TS 45.002. */
struct optional_val tsc_set;
/* The TSC to use if 'use' is true, otherwise automatically determine the TSC value to use. Valid range is 0 to
* 7, as described in 3GPP TS 45.002. */
struct optional_val tsc;
bool vamos;
};
#define INTERF_DBM_UNKNOWN 0
#define INTERF_BAND_UNKNOWN 0xff
/* Measurement pre-processing state */
struct gsm_power_ctrl_meas_proc_state {
/* Number of measurements processed */
unsigned int meas_num;
/* Algorithm specific data */
union {
struct {
/* Scaled up 100 times average value */
int Avg100;
} ewma;
};
};
struct lchan_power_ctrl_state {
/* Measurement pre-processing state (for dynamic mode) */
struct gsm_power_ctrl_meas_proc_state rxlev_meas_proc;
struct gsm_power_ctrl_meas_proc_state rxqual_meas_proc;
/* Number of SACCH blocks to skip (for dynamic mode) */
int skip_block_num;
};
struct gsm_lchan {
/* The TS that we're part of */
struct gsm_bts_trx_ts *ts;
/* The logical subslot number in the TS */
uint8_t nr;
char *name;
char *last_error;
struct osmo_fsm_inst *fi;
struct osmo_fsm_inst *fi_rtp;
struct osmo_mgcpc_ep_ci *mgw_endpoint_ci_bts;
struct {
/* The request as made by the caller, see lchan_activate().
* lchan->activate.info is treated immutable: remains unchanged throughout the Activation.
* The mutable versions are below: some values need automatic adjustments, in which case they are copied
* from immutable lchan->activate.info.* to lchan->activate.*, for example lchan->activate.ch_mode_rate
* is initially a copy of lchan->activate.info.ch_mode_rate, and is possibly adjusted afterwards. */
struct lchan_activate_info info;
struct channel_mode_and_rate ch_mode_rate;
struct gsm48_multi_rate_conf mr_conf_filtered;
bool activ_ack; /*< true as soon as RSL Chan Activ Ack is received */
bool immediate_assignment_sent;
/*! This flag ensures that when an lchan activation has succeeded, and we have already
* sent ACKs like Immediate Assignment or BSSMAP Assignment Complete, and if other errors
* occur later, e.g. during release, that we don't send a NACK out of context. */
bool concluded;
enum gsm0808_cause gsm0808_error_cause;
/* Actually used TSC Set. */
int tsc_set;
/* Actually used TSC. */
uint8_t tsc;
} activate;
struct {
/* The request as made by the caller, see lchan_mode_modify().
* lchan->modify.info is treated immutable: remains unchanged throughout the Mode Modify.
* The mutable versions are below: some values need automatic adjustments, in which case they are copied
* from immutable lchan->modify.info.* to lchan->modify.*, for example lchan->modify.ch_mode_rate
* is initially a copy of lchan->modify.info.ch_mode_rate, and is possibly adjusted afterwards. */
struct lchan_modify_info info;
struct channel_mode_and_rate ch_mode_rate;
struct gsm48_multi_rate_conf mr_conf_filtered;
/* Actually used TSC Set. */
int tsc_set;
/* Actually used TSC. */
uint8_t tsc;
bool concluded;
} modify;
struct {
/* If an event to release the lchan comes in while still waiting for responses, just mark this
* flag, so that the lchan will gracefully release at the next sensible junction. */
bool requested;
bool do_rr_release;
enum gsm48_rr_cause rr_cause;
bool last_eutran_plmn_valid;
struct osmo_plmn_id last_eutran_plmn;
/* There is an RSL error cause of value 0, so we need a separate flag. */
bool in_error;
/* RSL error code, RSL_ERR_* */
uint8_t rsl_error_cause;
/* If a release event is being handled, ignore other ricocheting release events until that
* release handling has concluded. */
bool in_release_handler;
} release;
/* The logical channel type */
enum gsm_chan_t type;
/* Power levels for MS and BTS */
uint8_t bs_power_db;
uint8_t ms_power;
/* Encryption information */
struct gsm_encr encr;
/* Established data link layer services */
uint8_t sapis[8];
struct {
uint32_t bound_ip; /*< where the BTS receives RTP */
uint16_t bound_port;
uint32_t connect_ip; /*< where the BTS sends RTP to (MGW) */
uint16_t connect_port;
uint16_t conn_id;
uint8_t rtp_payload;
uint8_t rtp_payload2;
uint8_t speech_mode;
/* info we need to postpone the AoIP
* assignment completed message */
struct {
uint8_t rr_cause;
bool valid;
} ass_compl;
} abis_ip;
/* At first, the Timing Advance from the initial Channel Request. Later, the Timing Advance value received from
* the most recent Measurement Report. */
uint8_t last_ta;
/* table of neighbor cell measurements */
struct neigh_meas_proc neigh_meas[MAX_NEIGH_MEAS];
/* cache of last measurement reports on this lchan */
struct gsm_meas_rep meas_rep[MAX_MEAS_REP];
int meas_rep_idx;
int meas_rep_count;
uint8_t meas_rep_last_seen_nr;
/* GSM Random Access data */
/* TODO: don't allocate this, rather keep an "is_present" flag */
struct gsm48_req_ref *rqd_ref;
struct gsm_subscriber_connection *conn;
/* After the Channel Activation ACK or RSL Mode Modify ACK is received, this reflects the actually used
* channel_mode_and_rate. */
struct channel_mode_and_rate current_ch_mode_rate;
struct gsm48_multi_rate_conf current_mr_conf;
/* Circuit-Switched TSC Set in use, or -1 if no specific TSC Set was requested. The valid range is 1-4 as
* described in the spec 3GPP TS 45.002. */
int tsc_set;
/* Training Sequence Code in use. The valid range is 0-7 as described in the spec 3GPP TS 45.002. */
uint8_t tsc;
struct {
/* Whether this lchan represents a secondary "shadow" lchan to multiplex a second MS onto a primary
* "normal" lchan */
bool is_secondary;
/* Whether this lchan is activated/modified into a mode that allows VAMOS multiplexing at this moment */
bool enabled;
} vamos;
/* dBm value of interference level as reported in the most recent Resource Indication, if any for this lchan. Or
* INTERF_DBM_UNKNOWN if this lchan was not included in the most recent Resource Indication.
* The range is typically -115 to -85 dBm, here stored 1:1 as a signed integer, to ease comparison. */
int16_t interf_dbm;
/* Actual reported interference band index, or INTERF_BAND_UNKNOWN if this lchan was not included in the most
* recent Resource Indication. */
uint8_t interf_band;
/* MS power control state */
struct lchan_power_ctrl_state ms_power_ctrl;
/* Timestamps and markers to track active state duration. */
struct timespec active_start;
struct timespec active_stored;
};
/* One Timeslot in a TRX */
struct gsm_bts_trx_ts {
struct gsm_bts_trx *trx;
@ -967,8 +618,6 @@ struct gsm_bts_trx_ts {
struct chan_counts chan_counts;
};
#define GSM_LCHAN_SI(lchan, i) (void *)((lchan)->si.buf[i][0])
struct gsm_envabtse {
struct gsm_abis_mo mo;
};
@ -1151,14 +800,6 @@ enum gsm_phys_chan_config gsm_pchan_parse(const char *name);
const char *gsm_chreq_name(enum gsm_chreq_reason_t c);
char *gsm_ts_name(const struct gsm_bts_trx_ts *ts);
char *gsm_ts_and_pchan_name(const struct gsm_bts_trx_ts *ts);
void lchan_update_name(struct gsm_lchan *lchan);
uint64_t gsm_lchan_active_duration_ms(const struct gsm_lchan *lchan);
static inline char *gsm_lchan_name(const struct gsm_lchan *lchan)
{
OSMO_ASSERT(lchan);
return lchan->name;
}
void gsm_abis_mo_reset(struct gsm_abis_mo *mo);
void gsm_mo_init(struct gsm_abis_mo *mo, struct gsm_bts *bts,
@ -1190,13 +831,9 @@ uint8_t pchan_subslots(enum gsm_phys_chan_config pchan);
uint8_t pchan_subslots_vamos(enum gsm_phys_chan_config pchan);
bool ts_is_tch(struct gsm_bts_trx_ts *ts);
struct gsm_lchan *gsm_lchan_vamos_to_primary(const struct gsm_lchan *lchan_vamos);
struct gsm_lchan *gsm_lchan_primary_to_vamos(const struct gsm_lchan *lchan_primary);
struct gsm_bts *conn_get_bts(struct gsm_subscriber_connection *conn);
void conn_update_ms_power_class(struct gsm_subscriber_connection *conn, uint8_t power_class);
void lchan_update_ms_power_ctrl_level(struct gsm_lchan *lchan, int ms_power_dbm);
struct gsm_tz {
int override; /* if 0, use system's time zone instead. */

379
include/osmocom/bsc/lchan.h Normal file
View File

@ -0,0 +1,379 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/protocol/gsm_08_08.h>
#include <osmocom/gsm/gsm23003.h>
#include <osmocom/bsc/meas_rep.h>
/* If .present is false, use the default value defined elsewhere. If true, use .val below.
* (A practical benefit of this is that the default initialization sets .present to false, so that even if a .val == 0
* is a valid value, a struct containing this as member does not need to explicitly set .val = INVALID_VAL_CONSTANT.) */
struct optional_val {
bool present;
int val;
};
/* Maximum number of neighbor cells whose average we track */
#define MAX_NEIGH_MEAS 10
/* Maximum size of the averaging window for neighbor cells */
#define MAX_WIN_NEIGH_AVG 10
/* Maximum number of report history we store */
#define MAX_MEAS_REP 10
/* processed neighbor measurements for one cell */
struct neigh_meas_proc {
uint16_t arfcn;
uint8_t bsic;
uint8_t rxlev[MAX_WIN_NEIGH_AVG];
unsigned int rxlev_cnt;
uint8_t last_seen_nr;
};
enum channel_rate {
CH_RATE_SDCCH,
CH_RATE_HALF,
CH_RATE_FULL,
};
enum channel_rate chan_t_to_chan_rate(enum gsm_chan_t chan_t);
enum lchan_csd_mode {
LCHAN_CSD_M_NT,
LCHAN_CSD_M_T_1200_75,
LCHAN_CSD_M_T_600,
LCHAN_CSD_M_T_1200,
LCHAN_CSD_M_T_2400,
LCHAN_CSD_M_T_9600,
LCHAN_CSD_M_T_14400,
LCHAN_CSD_M_T_29000,
LCHAN_CSD_M_T_32000,
};
struct channel_mode_and_rate {
enum gsm48_chan_mode chan_mode;
enum channel_rate chan_rate;
uint16_t s15_s0;
/* only used for GSM48_CMODE_DATA_* */
enum lchan_csd_mode csd_mode;
};
/* Channel Request reason */
enum gsm_chreq_reason_t {
GSM_CHREQ_REASON_EMERG,
GSM_CHREQ_REASON_PAG,
GSM_CHREQ_REASON_CALL,
GSM_CHREQ_REASON_LOCATION_UPD,
GSM_CHREQ_REASON_OTHER,
GSM_CHREQ_REASON_PDCH,
};
static inline bool gsm_chreq_reason_is_voicecall(enum gsm_chreq_reason_t reason)
{
return reason == GSM_CHREQ_REASON_EMERG || reason == GSM_CHREQ_REASON_CALL;
}
/* State of the SAPIs in the lchan */
enum lchan_sapi_state {
LCHAN_SAPI_S_NONE,
LCHAN_SAPI_S_REQ,
LCHAN_SAPI_S_ASSIGNED,
LCHAN_SAPI_S_REL,
LCHAN_SAPI_S_ERROR,
};
/* is the data link established? who established it? */
#define LCHAN_SAPI_UNUSED 0
#define LCHAN_SAPI_MS 1
#define LCHAN_SAPI_NET 2
#define MAX_A5_KEY_LEN (128/8)
struct gsm_encr {
uint8_t alg_a5_n; /* N: 0 (A5/0), 1 (A5/1), ... 7 (A5/7) */
uint8_t key_len;
uint8_t key[MAX_A5_KEY_LEN];
bool kc128_present;
uint8_t kc128[16];
};
enum lchan_activate_for {
ACTIVATE_FOR_NONE,
ACTIVATE_FOR_MS_CHANNEL_REQUEST,
ACTIVATE_FOR_ASSIGNMENT,
ACTIVATE_FOR_HANDOVER,
ACTIVATE_FOR_VTY,
ACTIVATE_FOR_MODE_MODIFY_RTP,
};
extern const struct value_string lchan_activate_mode_names[];
static inline const char *lchan_activate_mode_name(enum lchan_activate_for activ_for)
{ return get_value_string(lchan_activate_mode_names, activ_for); }
enum imm_ass_time {
IMM_ASS_TIME_POST_CHAN_ACK = 0,
IMM_ASS_TIME_PRE_CHAN_ACK,
IMM_ASS_TIME_PRE_TS_ACK,
};
struct lchan_activate_info {
enum lchan_activate_for activ_for;
/* If activ_for == ACTIVATE_FOR_MS_CHANNEL_REQUEST, the original CHREQ reason. */
enum gsm_chreq_reason_t chreq_reason;
struct gsm_subscriber_connection *for_conn;
struct channel_mode_and_rate ch_mode_rate;
struct gsm_encr encr;
bool requires_voice_stream;
bool wait_before_switching_rtp; /*< true = requires LCHAN_EV_READY_TO_SWITCH_RTP */
uint16_t msc_assigned_cic;
/* During intra-BSC handover, we keep the MGW endpoint intact and just re-route to the new lchan. This
* activate_info is for the new lchan, the re_use_mgw_endpoint_from_lchan points at the old lchan. */
struct gsm_lchan *re_use_mgw_endpoint_from_lchan;
bool ta_known;
uint8_t ta;
/* The TSC Set to use if 'use' is true, otherwise automatically determine the TSC Set value to use. Valid range
* is 1 to 4, as described in 3GPP TS 45.002. */
struct optional_val tsc_set;
/* The TSC to use if 'use' is true, otherwise automatically determine the TSC value to use. Valid range is 0 to
* 7, as described in 3GPP TS 45.002. */
struct optional_val tsc;
bool vamos;
/* A copy of bts->imm_ass_time at the time where Channel Activation was requested. A change in the VTY
* configuration has immediate effect on the value, so make sure we don't get mixed up when it gets changed
* while a channel activation is in progress. */
enum imm_ass_time imm_ass_time;
};
enum lchan_modify_for {
MODIFY_FOR_NONE,
MODIFY_FOR_ASSIGNMENT,
MODIFY_FOR_VTY,
};
extern const struct value_string lchan_modify_for_names[];
static inline const char *lchan_modify_for_name(enum lchan_modify_for modify_for)
{ return get_value_string(lchan_modify_for_names, modify_for); }
struct lchan_modify_info {
enum lchan_modify_for modify_for;
struct channel_mode_and_rate ch_mode_rate;
bool requires_voice_stream;
uint16_t msc_assigned_cic;
/* The TSC Set to use if 'use' is true, otherwise automatically determine the TSC Set value to use. Valid range
* is 1 to 4, as described in 3GPP TS 45.002. */
struct optional_val tsc_set;
/* The TSC to use if 'use' is true, otherwise automatically determine the TSC value to use. Valid range is 0 to
* 7, as described in 3GPP TS 45.002. */
struct optional_val tsc;
bool vamos;
};
/* Measurement pre-processing state */
struct gsm_power_ctrl_meas_proc_state {
/* Number of measurements processed */
unsigned int meas_num;
/* Algorithm specific data */
union {
struct {
/* Scaled up 100 times average value */
int Avg100;
} ewma;
};
};
struct lchan_power_ctrl_state {
/* Measurement pre-processing state (for dynamic mode) */
struct gsm_power_ctrl_meas_proc_state rxlev_meas_proc;
struct gsm_power_ctrl_meas_proc_state rxqual_meas_proc;
/* Number of SACCH blocks to skip (for dynamic mode) */
int skip_block_num;
};
struct gsm_lchan {
/* The TS that we're part of */
struct gsm_bts_trx_ts *ts;
/* The logical subslot number in the TS */
uint8_t nr;
char *name;
char *last_error;
struct osmo_fsm_inst *fi;
struct osmo_fsm_inst *fi_rtp;
struct osmo_mgcpc_ep_ci *mgw_endpoint_ci_bts;
struct {
/* The request as made by the caller, see lchan_activate().
* lchan->activate.info is treated immutable: remains unchanged throughout the Activation.
* The mutable versions are below: some values need automatic adjustments, in which case they are copied
* from immutable lchan->activate.info.* to lchan->activate.*, for example lchan->activate.ch_mode_rate
* is initially a copy of lchan->activate.info.ch_mode_rate, and is possibly adjusted afterwards. */
struct lchan_activate_info info;
struct channel_mode_and_rate ch_mode_rate;
struct gsm48_multi_rate_conf mr_conf_filtered;
bool activ_ack; /*< true as soon as RSL Chan Activ Ack is received */
bool immediate_assignment_sent;
/*! This flag ensures that when an lchan activation has succeeded, and we have already
* sent ACKs like Immediate Assignment or BSSMAP Assignment Complete, and if other errors
* occur later, e.g. during release, that we don't send a NACK out of context. */
bool concluded;
enum gsm0808_cause gsm0808_error_cause;
/* Actually used TSC Set. */
int tsc_set;
/* Actually used TSC. */
uint8_t tsc;
} activate;
struct {
/* The request as made by the caller, see lchan_mode_modify().
* lchan->modify.info is treated immutable: remains unchanged throughout the Mode Modify.
* The mutable versions are below: some values need automatic adjustments, in which case they are copied
* from immutable lchan->modify.info.* to lchan->modify.*, for example lchan->modify.ch_mode_rate
* is initially a copy of lchan->modify.info.ch_mode_rate, and is possibly adjusted afterwards. */
struct lchan_modify_info info;
struct channel_mode_and_rate ch_mode_rate;
struct gsm48_multi_rate_conf mr_conf_filtered;
/* Actually used TSC Set. */
int tsc_set;
/* Actually used TSC. */
uint8_t tsc;
bool concluded;
} modify;
struct {
/* If an event to release the lchan comes in while still waiting for responses, just mark this
* flag, so that the lchan will gracefully release at the next sensible junction. */
bool requested;
bool do_rr_release;
enum gsm48_rr_cause rr_cause;
bool last_eutran_plmn_valid;
struct osmo_plmn_id last_eutran_plmn;
/* There is an RSL error cause of value 0, so we need a separate flag. */
bool in_error;
/* RSL error code, RSL_ERR_* */
uint8_t rsl_error_cause;
/* If a release event is being handled, ignore other ricocheting release events until that
* release handling has concluded. */
bool in_release_handler;
} release;
/* The logical channel type */
enum gsm_chan_t type;
/* Power levels for MS and BTS */
uint8_t bs_power_db;
uint8_t ms_power;
/* Encryption information */
struct gsm_encr encr;
/* Established data link layer services */
uint8_t sapis[8];
struct {
uint32_t bound_ip; /*< where the BTS receives RTP */
uint16_t bound_port;
uint32_t connect_ip; /*< where the BTS sends RTP to (MGW) */
uint16_t connect_port;
uint16_t conn_id;
uint8_t rtp_payload;
uint8_t rtp_payload2;
uint8_t speech_mode;
/* info we need to postpone the AoIP
* assignment completed message */
struct {
uint8_t rr_cause;
bool valid;
} ass_compl;
} abis_ip;
/* At first, the Timing Advance from the initial Channel Request. Later, the Timing Advance value received from
* the most recent Measurement Report. */
uint8_t last_ta;
/* table of neighbor cell measurements */
struct neigh_meas_proc neigh_meas[MAX_NEIGH_MEAS];
/* cache of last measurement reports on this lchan */
struct gsm_meas_rep meas_rep[MAX_MEAS_REP];
int meas_rep_idx;
int meas_rep_count;
uint8_t meas_rep_last_seen_nr;
/* GSM Random Access data */
/* TODO: don't allocate this, rather keep an "is_present" flag */
struct gsm48_req_ref *rqd_ref;
struct gsm_subscriber_connection *conn;
/* After the Channel Activation ACK or RSL Mode Modify ACK is received, this reflects the actually used
* channel_mode_and_rate. */
struct channel_mode_and_rate current_ch_mode_rate;
struct gsm48_multi_rate_conf current_mr_conf;
/* Circuit-Switched TSC Set in use, or -1 if no specific TSC Set was requested. The valid range is 1-4 as
* described in the spec 3GPP TS 45.002. */
int tsc_set;
/* Training Sequence Code in use. The valid range is 0-7 as described in the spec 3GPP TS 45.002. */
uint8_t tsc;
struct {
/* Whether this lchan represents a secondary "shadow" lchan to multiplex a second MS onto a primary
* "normal" lchan */
bool is_secondary;
/* Whether this lchan is activated/modified into a mode that allows VAMOS multiplexing at this moment */
bool enabled;
} vamos;
/* dBm value of interference level as reported in the most recent Resource Indication, if any for this lchan. Or
* INTERF_DBM_UNKNOWN if this lchan was not included in the most recent Resource Indication.
* The range is typically -115 to -85 dBm, here stored 1:1 as a signed integer, to ease comparison. */
int16_t interf_dbm;
/* Actual reported interference band index, or INTERF_BAND_UNKNOWN if this lchan was not included in the most
* recent Resource Indication. */
uint8_t interf_band;
/* MS power control state */
struct lchan_power_ctrl_state ms_power_ctrl;
/* Timestamps and markers to track active state duration. */
struct timespec active_start;
struct timespec active_stored;
};
#define GSM_LCHAN_SI(lchan, i) (void *)((lchan)->si.buf[i][0])
void lchan_update_name(struct gsm_lchan *lchan);
uint64_t gsm_lchan_active_duration_ms(const struct gsm_lchan *lchan);
static inline char *gsm_lchan_name(const struct gsm_lchan *lchan)
{
OSMO_ASSERT(lchan);
return lchan->name;
}
struct gsm_lchan *gsm_lchan_vamos_to_primary(const struct gsm_lchan *lchan_vamos);
struct gsm_lchan *gsm_lchan_primary_to_vamos(const struct gsm_lchan *lchan_primary);
void lchan_update_ms_power_ctrl_level(struct gsm_lchan *lchan, int ms_power_dbm);
#define LOGPLCHAN(lchan, ss, level, fmt, args...) \
LOGP(ss, level, "%s (ss=%d,%s) (%s) " fmt, \
lchan ? gsm_ts_and_pchan_name(lchan->ts) : "-", \
lchan ? lchan->nr : 0, \
lchan ? gsm_chan_t_name(lchan->type) : "-", \
bsc_subscr_name(lchan && lchan->conn ? lchan->conn->bsub : NULL), \
## args)

View File

@ -1,6 +1,8 @@
/* osmo-bsc API to manage lchans, logical channels in GSM cells. */
#pragma once
#include <osmocom/bsc/lchan.h>
#define LOG_LCHAN_RTP(lchan, level, fmt, args...) do { \
if (lchan->fi_rtp) \
LOGPFSML(lchan->fi_rtp, level, fmt, ## args); \

View File

@ -70,6 +70,7 @@ libbsc_la_SOURCES = \
handover_logic.c \
handover_vty.c \
lb.c \
lchan.c \
lchan_fsm.c \
lchan_rtp_fsm.c \
lchan_select.c \

View File

@ -57,6 +57,7 @@
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/power_control.h>
#include <osmocom/bsc/chan_counts.h>
#include <osmocom/bsc/lchan.h>
static void send_lchan_signal(int sig_no, struct gsm_lchan *lchan,
struct gsm_meas_rep *resp)

View File

@ -37,7 +37,7 @@
#include <osmocom/bsc/abis_rsl.h>
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/bsc_stats.h>
#include <osmocom/bsc/lchan.h>
#include <osmocom/bsc/assignment_fsm.h>
static struct osmo_fsm assignment_fsm;

View File

@ -32,6 +32,7 @@
#include <osmocom/bsc/handover_fsm.h>
#include <osmocom/bsc/lchan_fsm.h>
#include <osmocom/bsc/lchan_rtp_fsm.h>
#include <osmocom/bsc/lchan.h>
#include <osmocom/bsc/bsc_subscriber.h>
#include <osmocom/bsc/osmo_bsc_sigtran.h>
#include <osmocom/bsc/osmo_bsc_lcls.h>

View File

@ -65,6 +65,7 @@
#include <osmocom/bsc/assignment_fsm.h>
#include <osmocom/bsc/bssmap_reset.h>
#include <osmocom/bsc/bsc_msc_data.h>
#include <osmocom/bsc/lchan.h>
#include <inttypes.h>

View File

@ -31,6 +31,7 @@
#include <osmocom/bsc/pcu_if.h>
#include <osmocom/bsc/debug.h>
#include <osmocom/bsc/nm_common_fsm.h>
#include <osmocom/bsc/lchan.h>
static int gsm_bts_trx_talloc_destructor(struct gsm_bts_trx *trx)
{

View File

@ -39,6 +39,7 @@
#include <osmocom/bsc/system_information.h>
#include <osmocom/bsc/debug.h>
#include <osmocom/bsc/timeslot_fsm.h>
#include <osmocom/bsc/lchan.h>
#include <osmocom/bsc/lchan_fsm.h>
#include <osmocom/bsc/lchan_select.h>
#include <osmocom/bsc/bts.h>

View File

@ -46,7 +46,7 @@
#include <osmocom/bsc/bsc_msc_data.h>
#include <osmocom/bsc/system_information.h>
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/lchan.h>
int gsm48_sendmsg(struct msgb *msg)
{

View File

@ -325,32 +325,6 @@ char *gsm_ts_and_pchan_name(const struct gsm_bts_trx_ts *ts)
return ts2str;
}
void lchan_update_name(struct gsm_lchan *lchan)
{
struct gsm_bts_trx_ts *ts = lchan->ts;
if (lchan->name)
talloc_free(lchan->name);
lchan->name = talloc_asprintf(ts->trx, "(bts=%d,trx=%d,ts=%d,ss=%s%d)",
ts->trx->bts->nr, ts->trx->nr, ts->nr,
lchan->vamos.is_secondary ? "shadow" : "",
lchan->nr - (lchan->vamos.is_secondary ? ts->max_primary_lchans : 0));
}
/* If the lchan is currently active, return the duration since activation in milliseconds.
* Otherwise return 0. */
uint64_t gsm_lchan_active_duration_ms(const struct gsm_lchan *lchan)
{
struct timespec now, elapsed;
if (lchan->active_start.tv_sec == 0 && lchan->active_start.tv_nsec == 0)
return 0;
osmo_clock_gettime(CLOCK_MONOTONIC, &now);
timespecsub(&now, &lchan->active_start, &elapsed);
return elapsed.tv_sec * 1000 + elapsed.tv_nsec / 1000000;
}
/* obtain the MO structure for a given object instance */
static inline struct gsm_abis_mo *
gsm_objclass2mo(struct gsm_bts *bts, uint8_t obj_class,
@ -668,36 +642,6 @@ bool ts_is_tch(struct gsm_bts_trx_ts *ts)
return pchan_is_tch(ts->pchan_is);
}
/* For a VAMOS secondary shadow lchan, return its primary lchan. If the lchan is not a secondary lchan, return NULL. */
struct gsm_lchan *gsm_lchan_vamos_to_primary(const struct gsm_lchan *lchan_vamos)
{
struct gsm_lchan *lchan_primary;
if (!lchan_vamos || !lchan_vamos->vamos.is_secondary)
return NULL;
/* OsmoBSC currently does not support mixed TCH/F + TCH/H VAMOS multiplexes. Hence the primary <-> secondary
* relation is a simple index shift in the lchan array. If mixed multiplexes were allowed, a TCH/F primary might
* have two TCH/H VAMOS secondary lchans, etc. Fortunately, we don't need to care about that. */
lchan_primary = (struct gsm_lchan*)lchan_vamos - lchan_vamos->ts->max_primary_lchans;
if (!lchan_primary->fi)
return NULL;
return lchan_primary;
}
/* For a primary lchan, return its VAMOS secondary shadow lchan. If the lchan is not a primary lchan, return NULL. */
struct gsm_lchan *gsm_lchan_primary_to_vamos(const struct gsm_lchan *lchan_primary)
{
struct gsm_lchan *lchan_vamos;
if (!lchan_primary || lchan_primary->vamos.is_secondary)
return NULL;
/* OsmoBSC currently does not support mixed TCH/F + TCH/H VAMOS multiplexes. Hence the primary <-> secondary
* relation is a simple index shift in the lchan array. If mixed multiplexes were allowed, a TCH/F primary might
* have two TCH/H VAMOS secondary lchans, etc. Fortunately, we don't need to care about that. */
lchan_vamos = (struct gsm_lchan*)lchan_primary + lchan_primary->ts->max_primary_lchans;
if (!lchan_vamos->fi)
return NULL;
return lchan_vamos;
}
struct gsm_bts *conn_get_bts(struct gsm_subscriber_connection *conn) {
if (!conn || !conn->lchan)
return NULL;
@ -990,59 +934,6 @@ void conn_update_ms_power_class(struct gsm_subscriber_connection *conn, uint8_t
lchan_update_ms_power_ctrl_level(conn->lchan, bts->ms_max_power);
}
void lchan_update_ms_power_ctrl_level(struct gsm_lchan *lchan, int ms_power_dbm)
{
struct gsm_bts *bts = lchan->ts->trx->bts;
struct gsm_subscriber_connection *conn = lchan->conn;
int max_pwr_dbm_pwclass, new_pwr;
bool send_pwr_ctrl_msg = false;
LOG_LCHAN(lchan, LOGL_DEBUG,
"MS Power level update requested: %d dBm\n", ms_power_dbm);
if (!conn)
goto ms_power_default;
if (conn->ms_power_class == 0)
goto ms_power_default;
if ((max_pwr_dbm_pwclass = (int)ms_class_gmsk_dbm(bts->band, conn->ms_power_class)) < 0) {
LOG_LCHAN(lchan, LOGL_INFO,
"Failed getting max ms power for power class %" PRIu8
" on band %s, providing default max ms power\n",
conn->ms_power_class, gsm_band_name(bts->band));
goto ms_power_default;
}
/* Current configured max pwr is above maximum one allowed on
current band + ms power class, so use that one. */
if (ms_power_dbm > max_pwr_dbm_pwclass)
ms_power_dbm = max_pwr_dbm_pwclass;
ms_power_default:
if ((new_pwr = ms_pwr_ctl_lvl(bts->band, ms_power_dbm)) < 0) {
LOG_LCHAN(lchan, LOGL_INFO,
"Failed getting max ms power level %d on band %s,"
" providing default max ms power\n",
ms_power_dbm, gsm_band_name(bts->band));
return;
}
LOG_LCHAN(lchan, LOGL_DEBUG,
"MS Power level update (power class %" PRIu8 "): %" PRIu8 " -> %d\n",
conn ? conn->ms_power_class : 0, lchan->ms_power, new_pwr);
/* If chan was already activated and max ms_power changes (due to power
classmark received), send an MS Power Control message */
if (lchan->activate.activ_ack && new_pwr != lchan->ms_power)
send_pwr_ctrl_msg = true;
lchan->ms_power = new_pwr;
if (send_pwr_ctrl_msg)
rsl_chan_ms_power_ctrl(lchan);
}
const struct value_string lchan_activate_mode_names[] = {
OSMO_VALUE_STRING(ACTIVATE_FOR_NONE),
OSMO_VALUE_STRING(ACTIVATE_FOR_MS_CHANNEL_REQUEST),

View File

@ -46,6 +46,7 @@
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/lcs_loc_req.h>
#include <osmocom/bsc/bsc_stats.h>
#include <osmocom/bsc/lchan.h>
#define LOG_FMT_BTS "bts %u lac-ci %u-%u arfcn-bsic %d-%d"
#define LOG_ARGS_BTS(bts) \

141
src/osmo-bsc/lchan.c Normal file
View File

@ -0,0 +1,141 @@
/* (C) 2022 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
* (C) 2008-2018 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdint.h>
#include <stdbool.h>
#include <inttypes.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/bsc/lchan.h>
#include <osmocom/bsc/lchan_fsm.h>
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/bts_trx.h>
#include <osmocom/bsc/abis_rsl.h>
void lchan_update_name(struct gsm_lchan *lchan)
{
struct gsm_bts_trx_ts *ts = lchan->ts;
if (lchan->name)
talloc_free(lchan->name);
lchan->name = talloc_asprintf(ts->trx, "(bts=%d,trx=%d,ts=%d,ss=%s%d)",
ts->trx->bts->nr, ts->trx->nr, ts->nr,
lchan->vamos.is_secondary ? "shadow" : "",
lchan->nr - (lchan->vamos.is_secondary ? ts->max_primary_lchans : 0));
}
/* If the lchan is currently active, return the duration since activation in milliseconds.
* Otherwise return 0. */
uint64_t gsm_lchan_active_duration_ms(const struct gsm_lchan *lchan)
{
struct timespec now, elapsed;
if (lchan->active_start.tv_sec == 0 && lchan->active_start.tv_nsec == 0)
return 0;
osmo_clock_gettime(CLOCK_MONOTONIC, &now);
timespecsub(&now, &lchan->active_start, &elapsed);
return elapsed.tv_sec * 1000 + elapsed.tv_nsec / 1000000;
}
/* For a VAMOS secondary shadow lchan, return its primary lchan. If the lchan is not a secondary lchan, return NULL. */
struct gsm_lchan *gsm_lchan_vamos_to_primary(const struct gsm_lchan *lchan_vamos)
{
struct gsm_lchan *lchan_primary;
if (!lchan_vamos || !lchan_vamos->vamos.is_secondary)
return NULL;
/* OsmoBSC currently does not support mixed TCH/F + TCH/H VAMOS multiplexes. Hence the primary <-> secondary
* relation is a simple index shift in the lchan array. If mixed multiplexes were allowed, a TCH/F primary might
* have two TCH/H VAMOS secondary lchans, etc. Fortunately, we don't need to care about that. */
lchan_primary = (struct gsm_lchan *)lchan_vamos - lchan_vamos->ts->max_primary_lchans;
if (!lchan_primary->fi)
return NULL;
return lchan_primary;
}
/* For a primary lchan, return its VAMOS secondary shadow lchan. If the lchan is not a primary lchan, return NULL. */
struct gsm_lchan *gsm_lchan_primary_to_vamos(const struct gsm_lchan *lchan_primary)
{
struct gsm_lchan *lchan_vamos;
if (!lchan_primary || lchan_primary->vamos.is_secondary)
return NULL;
/* OsmoBSC currently does not support mixed TCH/F + TCH/H VAMOS multiplexes. Hence the primary <-> secondary
* relation is a simple index shift in the lchan array. If mixed multiplexes were allowed, a TCH/F primary might
* have two TCH/H VAMOS secondary lchans, etc. Fortunately, we don't need to care about that. */
lchan_vamos = (struct gsm_lchan *)lchan_primary + lchan_primary->ts->max_primary_lchans;
if (!lchan_vamos->fi)
return NULL;
return lchan_vamos;
}
void lchan_update_ms_power_ctrl_level(struct gsm_lchan *lchan, int ms_power_dbm)
{
struct gsm_bts *bts = lchan->ts->trx->bts;
struct gsm_subscriber_connection *conn = lchan->conn;
int max_pwr_dbm_pwclass, new_pwr;
bool send_pwr_ctrl_msg = false;
LOG_LCHAN(lchan, LOGL_DEBUG,
"MS Power level update requested: %d dBm\n", ms_power_dbm);
if (!conn)
goto ms_power_default;
if (conn->ms_power_class == 0)
goto ms_power_default;
if ((max_pwr_dbm_pwclass = (int)ms_class_gmsk_dbm(bts->band, conn->ms_power_class)) < 0) {
LOG_LCHAN(lchan, LOGL_INFO,
"Failed getting max ms power for power class %" PRIu8
" on band %s, providing default max ms power\n",
conn->ms_power_class, gsm_band_name(bts->band));
goto ms_power_default;
}
/* Current configured max pwr is above maximum one allowed on
current band + ms power class, so use that one. */
if (ms_power_dbm > max_pwr_dbm_pwclass)
ms_power_dbm = max_pwr_dbm_pwclass;
ms_power_default:
if ((new_pwr = ms_pwr_ctl_lvl(bts->band, ms_power_dbm)) < 0) {
LOG_LCHAN(lchan, LOGL_INFO,
"Failed getting max ms power level %d on band %s,"
" providing default max ms power\n",
ms_power_dbm, gsm_band_name(bts->band));
return;
}
LOG_LCHAN(lchan, LOGL_DEBUG,
"MS Power level update (power class %" PRIu8 "): %" PRIu8 " -> %d\n",
conn ? conn->ms_power_class : 0, lchan->ms_power, new_pwr);
/* If chan was already activated and max ms_power changes (due to power
classmark received), send an MS Power Control message */
if (lchan->activate.activ_ack && new_pwr != lchan->ms_power)
send_pwr_ctrl_msg = true;
lchan->ms_power = new_pwr;
if (send_pwr_ctrl_msg)
rsl_chan_ms_power_ctrl(lchan);
}

View File

@ -41,6 +41,7 @@
#include <osmocom/bsc/codec_pref.h>
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/bsc_stats.h>
#include <osmocom/bsc/lchan.h>
static struct osmo_fsm lchan_fsm;

View File

@ -31,6 +31,7 @@
#include <osmocom/bsc/abis_rsl.h>
#include <osmocom/bsc/bsc_msc_data.h>
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/lchan.h>
static struct osmo_fsm lchan_rtp_fsm;

View File

@ -20,6 +20,7 @@
#include <osmocom/bsc/vty.h>
#include <osmocom/bsc/debug.h>
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/lchan.h>
struct meas_feed_state {
struct osmo_wqueue wqueue;

View File

@ -30,6 +30,7 @@
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/bsc/osmo_bsc_lcls.h>
#include <osmocom/bsc/lchan_rtp_fsm.h>
#include <osmocom/bsc/lchan.h>
#include <osmocom/mgcp_client/mgcp_client_endpoint_fsm.h>
struct value_string lcls_event_names[] = {
@ -561,7 +562,7 @@ static void lcls_req_lcls_not_supp_fn(struct osmo_fsm_inst *fi, uint32_t event,
case LCLS_EV_UPDATE_CFG_CSC:
if (lcls_handle_cfg_update(conn, data) != 0)
return;
//FIXME osmo_fsm_inst_state_chg(fi,
//FIXME osmo_fsm_inst_state_chg(fi,
return;
case LCLS_EV_APPLY_CFG_CSC:
if (lcls_perform_correlation(conn) != 0) {

View File

@ -29,6 +29,7 @@
#include <osmocom/bsc/abis_rsl.h>
#include <osmocom/bsc/pcu_if.h>
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/lchan.h>
static struct osmo_fsm ts_fsm;

View File

@ -52,6 +52,7 @@
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/paging.h>
#include <osmocom/bsc/vty.h>
#include <osmocom/bsc/lchan.h>
#include <osmocom/mgcp_client/mgcp_client_pool.h>
#include "../../bscconfig.h"