2022-08-08 13:03:41 +00:00
|
|
|
#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;
|
2022-08-08 11:50:39 +00:00
|
|
|
|
|
|
|
struct {
|
|
|
|
bool use;
|
|
|
|
uint8_t local_cid;
|
|
|
|
uint8_t remote_cid;
|
|
|
|
} osmux;
|
2022-08-08 13:03:41 +00:00
|
|
|
} 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])
|
|
|
|
|
2022-08-08 13:32:33 +00:00
|
|
|
void lchan_init(struct gsm_lchan *lchan, struct gsm_bts_trx_ts *ts, unsigned int nr);
|
|
|
|
|
2022-08-08 13:03:41 +00:00
|
|
|
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)
|