aboutsummaryrefslogtreecommitdiffstats
path: root/include/osmocom
diff options
context:
space:
mode:
Diffstat (limited to 'include/osmocom')
-rw-r--r--include/osmocom/bsc/Makefile.am6
-rw-r--r--include/osmocom/bsc/abis_rsl.h31
-rw-r--r--include/osmocom/bsc/assignment_fsm.h42
-rw-r--r--include/osmocom/bsc/bsc_api.h6
-rw-r--r--include/osmocom/bsc/bsc_msc_data.h5
-rw-r--r--include/osmocom/bsc/bsc_subscr_conn_fsm.h73
-rw-r--r--include/osmocom/bsc/bsc_subscriber.h1
-rw-r--r--include/osmocom/bsc/chan_alloc.h9
-rw-r--r--include/osmocom/bsc/debug.h2
-rw-r--r--include/osmocom/bsc/gsm_data.h379
-rw-r--r--include/osmocom/bsc/gsm_timers.h9
-rw-r--r--include/osmocom/bsc/handover.h80
-rw-r--r--include/osmocom/bsc/handover_fsm.h81
-rw-r--r--include/osmocom/bsc/lchan_fsm.h88
-rw-r--r--include/osmocom/bsc/lchan_select.h6
-rw-r--r--include/osmocom/bsc/mgw_endpoint_fsm.h60
-rw-r--r--include/osmocom/bsc/neighbor_ident.h5
-rw-r--r--include/osmocom/bsc/osmo_bsc.h14
-rw-r--r--include/osmocom/bsc/osmo_bsc_lcls.h1
-rw-r--r--include/osmocom/bsc/timeslot_fsm.h50
20 files changed, 727 insertions, 221 deletions
diff --git a/include/osmocom/bsc/Makefile.am b/include/osmocom/bsc/Makefile.am
index 82a2e26cd..49d35e602 100644
--- a/include/osmocom/bsc/Makefile.am
+++ b/include/osmocom/bsc/Makefile.am
@@ -5,6 +5,7 @@ noinst_HEADERS = \
abis_rsl.h \
acc_ramp.h \
arfcn_range_encode.h \
+ assignment_fsm.h \
bsc_msg_filter.h \
bsc_rll.h \
bsc_subscriber.h \
@@ -23,11 +24,15 @@ noinst_HEADERS = \
handover_cfg.h \
handover_decision.h \
handover_decision_2.h \
+ handover_fsm.h \
handover_vty.h \
ipaccess.h \
+ lchan_fsm.h \
+ lchan_select.h \
meas_feed.h \
meas_rep.h \
misdn.h \
+ mgw_endpoint_fsm.h \
neighbor_ident.h \
network_listen.h \
openbscdefines.h \
@@ -44,6 +49,7 @@ noinst_HEADERS = \
rs232.h \
signal.h \
system_information.h \
+ timeslot_fsm.h \
ussd.h \
vty.h \
bsc_api.h \
diff --git a/include/osmocom/bsc/abis_rsl.h b/include/osmocom/bsc/abis_rsl.h
index c04c61a9f..1177b373c 100644
--- a/include/osmocom/bsc/abis_rsl.h
+++ b/include/osmocom/bsc/abis_rsl.h
@@ -35,19 +35,20 @@ struct gsm_bts_trx_ts;
#define GSM48_LEN2PLEN(a) (((a) << 2) | 1)
-#define rsl_lchan_set_state(lch_, st_) \
- rsl_lchan_set_state_with_log(lch_, st_, __BASE_FILE__, __LINE__)
+int gsm48_sendmsg(struct msgb *msg);
int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len);
int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type,
const uint8_t *data, int len);
-int rsl_chan_activate_lchan(struct gsm_lchan *lchan, uint8_t act_type,
- uint8_t ho_ref);
+int rsl_tx_chan_activ(struct gsm_lchan *lchan, uint8_t act_type, uint8_t ho_ref);
int rsl_chan_mode_modify_req(struct gsm_lchan *ts);
int rsl_encryption_cmd(struct msgb *msg);
int rsl_paging_cmd(struct gsm_bts *bts, uint8_t paging_group, uint8_t len,
uint8_t *ms_ident, uint8_t chan_needed, bool is_gprs);
int rsl_imm_assign_cmd(struct gsm_bts *bts, uint8_t len, uint8_t *val);
+int rsl_tx_imm_assignment(struct gsm_lchan *lchan);
+int rsl_tx_imm_ass_rej(struct gsm_bts *bts, struct gsm48_req_ref *rqd_ref);
+int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, uint8_t power_command);
int rsl_data_request(struct msgb *msg, uint8_t link_id);
int rsl_establish_request(struct gsm_lchan *lchan, uint8_t link_id);
@@ -60,9 +61,8 @@ int rsl_ericsson_imm_assign_cmd(struct gsm_bts *bts, uint32_t tlli, uint8_t len,
int rsl_siemens_mrpci(struct gsm_lchan *lchan, struct rsl_mrpci *mrpci);
/* ip.access specfic RSL extensions */
-int rsl_ipacc_crcx(struct gsm_lchan *lchan);
-int rsl_ipacc_mdcx(struct gsm_lchan *lchan, uint32_t ip,
- uint16_t port, uint8_t rtp_payload2);
+int rsl_tx_ipacc_crcx(struct gsm_lchan *lchan);
+int rsl_tx_ipacc_mdcx(struct gsm_lchan *lchan);
int rsl_ipacc_mdcx_to_rtpsock(struct gsm_lchan *lchan);
int rsl_ipacc_pdch_activate(struct gsm_bts_trx_ts *ts, int act);
@@ -71,7 +71,6 @@ int abis_rsl_rcvmsg(struct msgb *msg);
int rsl_release_request(struct gsm_lchan *lchan, uint8_t link_id,
enum rsl_rel_mode release_mode);
-int rsl_lchan_set_state_with_log(struct gsm_lchan *lchan, enum gsm_lchan_state state, const char *file, unsigned line);
int rsl_lchan_mark_broken(struct gsm_lchan *lchan, const char *broken);
/* to be provided by external code */
@@ -106,9 +105,15 @@ int rsl_start_t3109(struct gsm_lchan *lchan);
int rsl_direct_rf_release(struct gsm_lchan *lchan);
-void dyn_ts_init(struct gsm_bts_trx_ts *ts);
-int dyn_ts_switchover_start(struct gsm_bts_trx_ts *ts,
- enum gsm_phys_chan_config to_pchan);
+int rsl_tx_dyn_ts_pdch_act_deact(struct gsm_bts_trx_ts *ts, bool activate);
+
+int rsl_forward_layer3_info(struct gsm_lchan *lchan, const uint8_t *l3_info, uint8_t l3_info_len);
+
+int ipacc_speech_mode(enum gsm48_chan_mode tch_mode, enum gsm_chan_t type);
+void ipacc_speech_mode_set_direction(uint8_t *speech_mode, bool send);
+int ipacc_payload_type(enum gsm48_chan_mode tch_mode, enum gsm_chan_t type);
+
+int rsl_tx_rf_chan_release(struct gsm_lchan *lchan);
struct msgb *gsm48_create_rr_status(uint8_t cause);
int gsm48_tx_rr_status(struct gsm_subscriber_connection *conn, uint8_t cause);
@@ -120,9 +125,11 @@ int send_siemens_mrpci(struct gsm_lchan *lchan,
int gsm48_rx_rr_modif_ack(struct msgb *msg);
int gsm48_send_rr_release(struct gsm_lchan *lchan);
int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv);
+
+struct msgb *gsm48_make_ho_cmd(struct gsm_lchan *new_lchan, uint8_t power_command, uint8_t ho_ref);
int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan,
uint8_t power_command, uint8_t ho_ref);
-int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, uint8_t power_command);
+int gsm48_rx_rr_modif_ack(struct msgb *msg);
int gsm48_lchan_modify(struct gsm_lchan *lchan, uint8_t mode);
#endif /* RSL_MT_H */
diff --git a/include/osmocom/bsc/assignment_fsm.h b/include/osmocom/bsc/assignment_fsm.h
new file mode 100644
index 000000000..ffb698320
--- /dev/null
+++ b/include/osmocom/bsc/assignment_fsm.h
@@ -0,0 +1,42 @@
+/* osmo-bsc API to manage BSSMAP Assignment Command */
+#pragma once
+
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+
+#include <osmocom/bsc/debug.h>
+
+/* This macro automatically includes a final \n, if omitted. */
+#define LOG_ASSIGNMENT(conn, level, fmt, args...) do { \
+ if (conn->assignment.fi) \
+ LOGPFSML(conn->assignment.fi, level, "%s%s" fmt "%s", \
+ conn->assignment.new_lchan ? gsm_lchan_name(conn->assignment.new_lchan) : "", \
+ conn->assignment.new_lchan ? " " : "", \
+ ## args, LOG_ADD_NEWLINE(fmt)); \
+ else \
+ LOGP(DMSC, level, "Assignment%s%s: " fmt "%s", \
+ conn->assignment.new_lchan ? " of " : "", \
+ conn->assignment.new_lchan ? gsm_lchan_name(conn->assignment.new_lchan) : "", \
+ ## args, LOG_ADD_NEWLINE(fmt)); \
+ } while(0)
+
+enum assignment_fsm_state {
+ ASSIGNMENT_ST_WAIT_LCHAN_ACTIVE,
+ ASSIGNMENT_ST_WAIT_RR_ASS_COMPLETE,
+ ASSIGNMENT_ST_WAIT_LCHAN_ESTABLISHED,
+ ASSIGNMENT_ST_WAIT_MGW_ENDPOINT_TO_MSC,
+};
+
+enum assignment_fsm_event {
+ ASSIGNMENT_EV_LCHAN_ACTIVE,
+ ASSIGNMENT_EV_LCHAN_ESTABLISHED,
+ ASSIGNMENT_EV_LCHAN_ERROR,
+ ASSIGNMENT_EV_MSC_MGW_OK,
+ ASSIGNMENT_EV_MSC_MGW_FAIL,
+ ASSIGNMENT_EV_RR_ASSIGNMENT_COMPLETE,
+ ASSIGNMENT_EV_RR_ASSIGNMENT_FAIL,
+ ASSIGNMENT_EV_CONN_RELEASING,
+};
+
+void assignment_fsm_start(struct gsm_subscriber_connection *conn, struct gsm_bts *bts,
+ struct assignment_request *req);
+void assignment_reset(struct gsm_subscriber_connection *conn);
diff --git a/include/osmocom/bsc/bsc_api.h b/include/osmocom/bsc/bsc_api.h
index 9e2b44e2c..0d62506ab 100644
--- a/include/osmocom/bsc/bsc_api.h
+++ b/include/osmocom/bsc/bsc_api.h
@@ -12,15 +12,14 @@ void bsc_sapi_n_reject(struct gsm_subscriber_connection *conn, int dlci);
void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn, struct msgb *msg, uint8_t chosen_encr);
int bsc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg, uint16_t chosen_channel);
void bsc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg);
-void bsc_assign_compl(struct gsm_subscriber_connection *conn, uint8_t rr_cause);
-void bsc_assign_fail(struct gsm_subscriber_connection *conn, uint8_t cause, uint8_t *rr_cause);
int bsc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause);
void bsc_cm_update(struct gsm_subscriber_connection *conn,
const uint8_t *cm2, uint8_t cm2_len,
const uint8_t *cm3, uint8_t cm3_len);
void bsc_mr_config(struct gsm_subscriber_connection *conn, struct gsm_lchan *lchan, int full_rate);
-int gsm0808_submit_dtap(struct gsm_subscriber_connection *conn, struct msgb *msg, int link_id, int allow_sacch);
+int gsm0808_submit_dtap(struct gsm_subscriber_connection *conn, struct msgb *msg, uint8_t link_id,
+ bool allow_sacch);
int gsm0808_assign_req(struct gsm_subscriber_connection *conn, int chan_mode, int full_rate);
int gsm0808_cipher_mode(struct gsm_subscriber_connection *conn, int cipher,
const uint8_t *key, int len, int include_imeisv);
@@ -28,4 +27,5 @@ int gsm0808_page(struct gsm_bts *bts, unsigned int page_group,
unsigned int mi_len, uint8_t *mi, int chan_type);
int gsm0808_clear(struct gsm_subscriber_connection *conn);
+bool msc_connected(struct gsm_subscriber_connection *conn);
#endif
diff --git a/include/osmocom/bsc/bsc_msc_data.h b/include/osmocom/bsc/bsc_msc_data.h
index 7235fbacc..79d2eca48 100644
--- a/include/osmocom/bsc/bsc_msc_data.h
+++ b/include/osmocom/bsc/bsc_msc_data.h
@@ -48,11 +48,6 @@
struct osmo_bsc_rf;
struct gsm_network;
-struct gsm_audio_support {
- uint8_t hr : 1,
- ver : 7;
-};
-
enum {
MSC_CON_TYPE_NORMAL,
MSC_CON_TYPE_LOCAL,
diff --git a/include/osmocom/bsc/bsc_subscr_conn_fsm.h b/include/osmocom/bsc/bsc_subscr_conn_fsm.h
index 91cfccce2..437d64e56 100644
--- a/include/osmocom/bsc/bsc_subscr_conn_fsm.h
+++ b/include/osmocom/bsc/bsc_subscr_conn_fsm.h
@@ -8,28 +8,20 @@ enum gscon_fsm_event {
GSCON_EV_A_CONN_REQ,
/* MSC confirms the SCCP connection */
GSCON_EV_A_CONN_CFM,
- /* MSC requests assignment */
- GSCON_EV_A_ASSIGNMENT_CMD,
/* MSC has sent BSSMAP CLEAR CMD */
GSCON_EV_A_CLEAR_CMD,
/* MSC SCCP disconnect indication */
GSCON_EV_A_DISC_IND,
- /* MSC sends Handover Request (in CR) */
- GSCON_EV_A_HO_REQ,
- /* RR ASSIGNMENT COMPLETE received */
- GSCON_EV_RR_ASS_COMPL,
- /* RR ASSIGNMENT FAIL received */
- GSCON_EV_RR_ASS_FAIL,
+ GSCON_EV_ASSIGNMENT_START,
+ GSCON_EV_ASSIGNMENT_END,
+
+ GSCON_EV_HANDOVER_START,
+ GSCON_EV_HANDOVER_END,
- /* RSL RLL Release Indication */
- GSCON_EV_RLL_REL_IND,
/* RSL CONNection FAILure Indication */
GSCON_EV_RSL_CONN_FAIL,
- /* RSL/lchan tells us clearing is complete */
- GSCON_EV_RSL_CLEAR_COMPL,
-
/* Mobile-originated DTAP (from MS) */
GSCON_EV_MO_DTAP,
/* Mobile-terminated DTAP (from MSC) */
@@ -38,42 +30,51 @@ enum gscon_fsm_event {
/* Transmit custom SCCP message */
GSCON_EV_TX_SCCP,
- /* MGW is indicating failure (BTS) */
- GSCON_EV_MGW_FAIL_BTS,
- /* MGW is indicating failure (MSC) */
- GSCON_EV_MGW_FAIL_MSC,
- /* CRCX response received (BTS) */
- GSCON_EV_MGW_CRCX_RESP_BTS,
- /* MDCX response received (BTS) */
- GSCON_EV_MGW_MDCX_RESP_BTS,
- /* CRCX response received (MSC) */
- GSCON_EV_MGW_CRCX_RESP_MSC,
/* MDCX response received (MSC) - triggered by LCLS */
GSCON_EV_MGW_MDCX_RESP_MSC,
- /* Internal handover request (intra-BSC handover) */
- GSCON_EV_HO_START,
- /* Handover timed out (T3103 in handover_logic.c) */
- GSCON_EV_HO_TIMEOUT,
- /* Handover failed (handover_logic.c) */
- GSCON_EV_HO_FAIL,
- /* Handover completed successfully (handover_logic.c) */
- GSCON_EV_HO_COMPL,
-
/* LCLS child FSM has terminated due to hard failure */
GSCON_EV_LCLS_FAIL,
+
+ GSCON_EV_FORGET_LCHAN,
+ GSCON_EV_FORGET_MGW_ENDPOINT,
};
struct gsm_subscriber_connection;
struct gsm_network;
-struct mgcp_conn_peer;
struct msgb;
+struct mgwep_ci;
+struct assignment_request;
+struct gsm_lchan;
/* Allocate a subscriber connection and its associated FSM */
struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net);
-
-void bsc_subscr_pick_codec(struct mgcp_conn_peer *conn_peer, struct gsm_subscriber_connection *conn);
+void gscon_update_id(struct gsm_subscriber_connection *conn);
void gscon_submit_rsl_dtap(struct gsm_subscriber_connection *conn,
struct msgb *msg, int link_id, int allow_sacch);
-void gscon_dtap_cache_flush(struct gsm_subscriber_connection *conn, int send);
+int gscon_sigtran_send(struct gsm_subscriber_connection *conn, struct msgb *msg);
+
+struct mgw_endpoint *gscon_ensure_mgw_endpoint(struct gsm_subscriber_connection *conn,
+ uint16_t msc_assigned_cic);
+bool gscon_connect_mgw_to_msc(struct gsm_subscriber_connection *conn,
+ struct gsm_lchan *for_lchan,
+ const char *addr, uint16_t port,
+ struct osmo_fsm_inst *notify,
+ uint32_t event_success, uint32_t event_failure,
+ void *notify_data,
+ struct mgwep_ci **created_ci);
+
+void gscon_start_assignment(struct gsm_subscriber_connection *conn,
+ struct assignment_request *req);
+
+void gscon_change_primary_lchan(struct gsm_subscriber_connection *conn, struct gsm_lchan *new_lchan);
+void gscon_release_lchans(struct gsm_subscriber_connection *conn, bool do_sacch_deact);
+
+void gscon_lchan_releasing(struct gsm_subscriber_connection *conn, struct gsm_lchan *lchan);
+void gscon_forget_lchan(struct gsm_subscriber_connection *conn, struct gsm_lchan *lchan);
+
+void gscon_forget_mgw_endpoint_ci(struct gsm_subscriber_connection *conn, struct mgwep_ci *ci);
+
+bool gscon_is_aoip(struct gsm_subscriber_connection *conn);
+bool gscon_is_sccplite(struct gsm_subscriber_connection *conn);
diff --git a/include/osmocom/bsc/bsc_subscriber.h b/include/osmocom/bsc/bsc_subscriber.h
index 324734f9a..a7b3e53fc 100644
--- a/include/osmocom/bsc/bsc_subscriber.h
+++ b/include/osmocom/bsc/bsc_subscriber.h
@@ -19,6 +19,7 @@ struct bsc_subscr {
};
const char *bsc_subscr_name(struct bsc_subscr *bsub);
+const char *bsc_subscr_id(struct bsc_subscr *bsub);
struct bsc_subscr *bsc_subscr_find_or_create_by_imsi(struct llist_head *list,
const char *imsi);
diff --git a/include/osmocom/bsc/chan_alloc.h b/include/osmocom/bsc/chan_alloc.h
index f3aec9dd5..97f6cb2db 100644
--- a/include/osmocom/bsc/chan_alloc.h
+++ b/include/osmocom/bsc/chan_alloc.h
@@ -24,18 +24,11 @@
struct gsm_subscriber_connection;
-/* Count number of free TS of given pchan type */
-int bts_count_free_ts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan);
-
/* Allocate a logical channel (SDCCH, TCH, ...) */
struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type, int allow_bigger);
/* Free a logical channel (SDCCH, TCH, ...) */
void lchan_free(struct gsm_lchan *lchan);
-void lchan_reset(struct gsm_lchan *lchan);
-
-/* Release the given lchan */
-int lchan_release(struct gsm_lchan *lchan, int sacch_deact, enum rsl_rel_mode release_mode);
struct pchan_load {
struct load_counter pchan[_GSM_PCHAN_MAX];
@@ -45,6 +38,4 @@ void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts);
void network_chan_load(struct pchan_load *pl, struct gsm_network *net);
void bts_update_t3122_chan_load(struct gsm_bts *bts);
-bool ts_is_usable(const struct gsm_bts_trx_ts *ts);
-
#endif /* _CHAN_ALLOC_H */
diff --git a/include/osmocom/bsc/debug.h b/include/osmocom/bsc/debug.h
index 1133bf6f6..ed6cbc3cc 100644
--- a/include/osmocom/bsc/debug.h
+++ b/include/osmocom/bsc/debug.h
@@ -6,6 +6,8 @@
#define DEBUG
#include <osmocom/core/logging.h>
+#define LOG_ADD_NEWLINE(fmt) ((!fmt || !*fmt || fmt[strlen(fmt)-1] != '\n') ? "\n" : "")
+
/* Debug Areas of the code */
enum {
DRLL,
diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h
index ce7a8fee6..36daabbfa 100644
--- a/include/osmocom/bsc/gsm_data.h
+++ b/include/osmocom/bsc/gsm_data.h
@@ -14,13 +14,13 @@
#include <osmocom/core/stat_item.h>
#include <osmocom/gsm/bts_features.h>
#include <osmocom/gsm/protocol/gsm_08_08.h>
+#include <osmocom/gsm/gsm0808.h>
#include <osmocom/gsm/gsm48.h>
#include <osmocom/core/fsm.h>
#include <osmocom/crypt/auth.h>
#include <osmocom/bsc/rest_octets.h>
-#include <osmocom/bsc/handover.h>
#include <osmocom/core/bitvec.h>
#include <osmocom/gsm/gsm_utils.h>
@@ -31,6 +31,8 @@
#include <osmocom/bsc/meas_rep.h>
#include <osmocom/bsc/bsc_msg_filter.h>
#include <osmocom/bsc/acc_ramp.h>
+#include <osmocom/bsc/gsm_timers.h>
+#include <osmocom/bsc/neighbor_ident.h>
#define GSM_T3122_DEFAULT 10
@@ -38,6 +40,7 @@ struct mgcp_client_conf;
struct mgcp_client;
struct mgcp_ctx;
struct gsm0808_cell_id;
+struct mgw_endpoint;
/** annotations for msgb ownership */
#define __uses
@@ -46,6 +49,7 @@ struct gsm0808_cell_id;
struct bsc_subscr;
struct gprs_ra_id;
+struct handover;
#define OBSC_LINKID_CB(__msgb) (__msgb)->cb[3]
@@ -95,6 +99,98 @@ enum subscr_sccp_state {
SUBSCR_SCCP_ST_CONNECTED
};
+struct assignment_request {
+ bool aoip;
+
+ uint16_t msc_assigned_cic;
+
+ char msc_rtp_addr[INET_ADDRSTRLEN];
+ uint16_t msc_rtp_port;
+
+ enum gsm48_chan_mode chan_mode;
+ bool full_rate;
+};
+
+struct assignment_fsm_data {
+ struct assignment_request req;
+ bool requires_voice_stream;
+
+ struct osmo_fsm_inst *fi;
+ struct gsm_lchan *new_lchan;
+
+ /* Whether this assignment triggered creation of the MGW endpoint: if the assignment
+ * fails, we will release that again as soon as possible. (If false, the endpoint already
+ * existed before or isn't needed at all.)*/
+ struct mgwep_ci *created_ci_for_msc;
+
+ enum gsm0808_cause failure_cause;
+ enum gsm48_rr_cause rr_cause;
+
+ bool result_rate_ctr_done;
+};
+
+enum hodec_id {
+ HODEC_NONE,
+ HODEC1 = 1,
+ HODEC2 = 2,
+ HODEC_USER,
+ HODEC_REMOTE,
+};
+
+/* For example, to count specific kinds of ongoing handovers, it is useful to be able to OR-combine
+ * scopes. */
+enum handover_scope {
+ HO_NO_HANDOVER = 0,
+ HO_INTRA_CELL = 0x1,
+ HO_INTRA_BSC = 0x2,
+ HO_INTER_BSC_MO = 0x4,
+ HO_INTER_BSC_MT = 0x8,
+ HO_SCOPE_ALL = 0xffff,
+};
+
+extern const struct value_string handover_scope_names[];
+inline static const char *handover_scope_name(enum handover_scope val)
+{ return get_value_string(handover_scope_names, val); }
+
+struct handover_mo_req {
+ enum hodec_id from_hodec_id;
+ struct gsm_lchan *old_lchan;
+ struct neighbor_ident_key target_nik;
+ enum gsm_chan_t new_lchan_type; /*< leave GSM_LCHAN_NONE to use same as old_lchan */
+};
+
+struct handover_mt_req {
+ struct gsm0808_channel_type ct;
+ struct gsm0808_speech_codec_list scl;
+ struct gsm0808_encrypt_info ei;
+ struct gsm_classmark classmark;
+ struct gsm0808_cell_id cell_id_serving;
+ char cell_id_serving_name[64];
+ struct gsm0808_cell_id cell_id_target;
+ char cell_id_target_name[64];
+ uint16_t msc_assigned_cic;
+ char msc_assigned_rtp_addr[INET_ADDRSTRLEN];
+ uint16_t msc_assigned_rtp_port;
+};
+
+struct handover {
+ struct osmo_fsm_inst *fi;
+
+ enum hodec_id from_hodec_id;
+ enum handover_scope scope;
+ enum gsm_chan_t new_lchan_type;
+ struct neighbor_ident_key target_cell;
+
+ struct {
+ uint8_t ho_ref;
+ struct gsm_bts *new_bts;
+ struct gsm_lchan *new_lchan;
+ bool async;
+ struct handover_mt_req inter_bsc;
+ struct mgwep_ci *created_ci_for_msc;
+ } mt;
+};
+
/* active radio connection of a mobile subscriber */
struct gsm_subscriber_connection {
/* global linked list of subscriber_connections */
@@ -112,11 +208,10 @@ struct gsm_subscriber_connection {
/* the primary / currently active lchan to the BTS/subscriber */
struct gsm_lchan *lchan;
- /* handover information, if a handover is pending for this conn. */
- struct bsc_handover *ho;
+ struct assignment_fsm_data assignment;
- /* the future allocated but not yet used lchan during ASSIGNMENT */
- struct gsm_lchan *secondary_lchan;
+ /* handover information, if a handover is pending for this conn. */
+ struct handover ho;
/* buffer/cache for classmark of the ME of the subscriber */
struct gsm_classmark classmark;
@@ -137,7 +232,6 @@ struct gsm_subscriber_connection {
* i.e. by heeding the "Codec list (MSC Preferred)", we inherently heed the MS bearer
* capabilities, which the MSC is required to translate into the codec list. */
struct gsm0808_speech_codec_list codec_list;
- bool codec_list_present;
/* flag to prevent multiple simultaneous ciphering commands */
int ciphering_handled;
@@ -163,29 +257,24 @@ struct gsm_subscriber_connection {
/* for audio handling */
struct {
- uint16_t cic;
- uint32_t rtp_ip;
- int rtp_port;
- /* RTP address of the remote end (assigned by MSC through assignment request) */
- struct sockaddr_storage aoip_rtp_addr_remote;
-
- /* Local RTP address (reported back to the MSC by us with the
- * assignment complete message) */
- struct sockaddr_storage aoip_rtp_addr_local;
+ uint16_t msc_assigned_cic;
- /* FSM instance to control the BTS sided RTP connection */
- struct osmo_fsm_inst *fi_bts;
+ /* RTP address where the MSC expects us to send the RTP stream coming from the BTS. */
+ char msc_assigned_rtp_addr[INET_ADDRSTRLEN];
+ uint16_t msc_assigned_rtp_port;
- /* FSM instance to control the MSC sided RTP connection */
- struct osmo_fsm_inst *fi_msc;
+ /* The endpoint at the MGW used to join both BTS and MSC side connections, e.g.
+ * "rtpbridge/23@mgw". */
+ struct mgw_endpoint *mgw_endpoint;
- /* Endpoint identifier of the MGCP endpoint the connection uses */
- char *mgw_endpoint;
+ /* The connection identifier of the mgw_endpoint used to transceive RTP towards the MSC.
+ * (The BTS side CI is handled by struct gsm_lchan and the lchan_fsm.) */
+ struct mgwep_ci *mgw_endpoint_ci_msc;
- /* Channel rate flag, FR=1, HR=0, Invalid=-1 */
+ /* Channel rate flag requested by the MSC, FR=1, HR=0, Invalid=-1 */
int full_rate;
- /* Channel mode flag (signaling or voice channel) */
+ /* Channel mode requested by the MSC (signalling or voice channel) */
enum gsm48_chan_mode chan_mode;
} user_plane;
@@ -298,18 +387,6 @@ struct om2k_mo {
#define LCHAN_SAPI_UNUSED 0
#define LCHAN_SAPI_MS 1
#define LCHAN_SAPI_NET 2
-#define LCHAN_SAPI_REL 3
-
-/* state of a logical channel */
-enum gsm_lchan_state {
- LCHAN_S_NONE, /* channel is not active */
- LCHAN_S_ACT_REQ, /* channel activation requested */
- LCHAN_S_ACTIVE, /* channel is active and operational */
- LCHAN_S_REL_REQ, /* channel release has been requested */
- LCHAN_S_REL_ERR, /* channel is in an error state */
- LCHAN_S_BROKEN, /* channel is somehow unusable */
- LCHAN_S_INACTIVE, /* channel is set inactive */
-};
/* BTS ONLY */
#define MAX_NUM_UL_MEAS 104
@@ -386,11 +463,67 @@ struct gsm_encr {
bsc_subscr_name(lchan && lchan->conn ? lchan->conn->bsub : NULL), \
## args)
+/* usage:
+ * struct gsm_lchan *lchan;
+ * struct gsm_bts_trx_ts *ts = get_some_timeslot();
+ * ts_for_each_lchan(lchan, ts) {
+ * LOGPLCHAN(DMAIN, LOGL_DEBUG, "hello world\n");
+ * }
+ * Iterate only those lchans that have an FSM allocated. */
+#define ts_for_each_lchan(lchan, ts) ts_as_pchan_for_each_lchan(lchan, ts, (ts)->pchan_is)
+
+/* Same as ts_for_each_lchan() but with an explicit pchan kind (GSM_PCHAN_* constant).
+ * Iterate only those lchans that have an FSM allocated. */
+#define ts_as_pchan_for_each_lchan(lchan, ts, as_pchan) \
+ for (lchan = (ts)->lchan; \
+ ((lchan - (ts)->lchan) < ARRAY_SIZE((ts)->lchan)) \
+ && lchan->fi \
+ && lchan->nr < pchan_subslots(as_pchan); \
+ lchan++)
+
+enum lchan_activate_mode {
+ FOR_NONE,
+ FOR_MS_CHANNEL_REQUEST,
+ FOR_ASSIGNMENT,
+ FOR_HANDOVER,
+ FOR_VTY,
+};
+
+extern const struct value_string lchan_activate_mode_names[];
+static inline const char *lchan_activate_mode_name(enum lchan_activate_mode activ_for)
+{ return get_value_string(lchan_activate_mode_names, activ_for); }
+
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;
+
+ struct osmo_fsm_inst *fi;
+ struct mgwep_ci *mgw_endpoint_ci_bts;
+
+ struct {
+ enum lchan_activate_mode activ_for;
+ bool concluded; /*< true as soon as LCHAN_ST_ESTABLISHED is reached */
+ bool requires_voice_stream;
+ bool mgw_endpoint_available;
+ uint16_t msc_assigned_cic;
+ enum gsm0808_cause gsm0808_error_cause;
+ } activate;
+
+ /* 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 release_requested;
+ bool deact_sacch;
+
+ char *last_error;
+
+ /* There is an RSL error cause of value 0, so we need a separate flag. */
+ bool release_in_error;
+ /* RSL error code, RSL_ERR_* */
+ uint8_t rsl_error_cause;
+
/* The logical channel type */
enum gsm_chan_t type;
/* RSL channel mode */
@@ -398,9 +531,6 @@ struct gsm_lchan {
/* If TCH, traffic channel mode */
enum gsm48_chan_mode tch_mode;
enum lchan_csd_mode csd_mode;
- /* State */
- enum gsm_lchan_state state;
- const char *broken_reason;
/* Power levels for MS and BTS */
uint8_t bs_power;
uint8_t ms_power;
@@ -415,15 +545,14 @@ struct gsm_lchan {
uint8_t sapis[8];
struct {
- uint32_t bound_ip;
- uint32_t connect_ip;
+ 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;
- struct rtp_socket *rtp_socket;
/* info we need to postpone the AoIP
* assignment completed message */
@@ -435,16 +564,6 @@ struct gsm_lchan {
uint8_t rqd_ta;
- char *name;
-
- struct osmo_timer_list T3101;
- struct osmo_timer_list T3109;
- struct osmo_timer_list T3111;
- struct osmo_timer_list error_timer;
- struct osmo_timer_list act_timer;
- struct osmo_timer_list rel_work;
- uint8_t error_cause;
-
/* table of neighbor cell measurements */
struct neigh_meas_proc neigh_meas[MAX_NEIGH_MEAS];
@@ -455,43 +574,38 @@ struct gsm_lchan {
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;
-
- struct {
- /* channel activation type and handover ref */
- uint8_t act_type;
- uint8_t ho_ref;
- struct gsm48_req_ref *rqd_ref;
- uint8_t rqd_ta;
- } dyn;
};
-enum {
- TS_F_PDCH_ACTIVE = 0x1000,
- TS_F_PDCH_ACT_PENDING = 0x2000,
- TS_F_PDCH_DEACT_PENDING = 0x4000,
- TS_F_PDCH_PENDING_MASK = (TS_F_PDCH_ACT_PENDING | TS_F_PDCH_DEACT_PENDING),
-} gsm_bts_trx_ts_flags;
-
/* One Timeslot in a TRX */
struct gsm_bts_trx_ts {
struct gsm_bts_trx *trx;
- bool initialized;
-
/* number of this timeslot at the TRX */
uint8_t nr;
- enum gsm_phys_chan_config pchan;
-
- struct {
- enum gsm_phys_chan_config pchan_is;
- enum gsm_phys_chan_config pchan_want;
- struct msgb *pending_chan_activ;
- } dyn;
+ struct osmo_fsm_inst *fi;
+ char *last_errmsg;
+
+ /* vty phys_chan_config setting, not necessarily in effect in case it was changed in the telnet
+ * vty after OML activation. Gets written on vty 'write file'. */
+ enum gsm_phys_chan_config pchan_from_config;
+ /* When the timeslot OML is established, pchan_from_config is copied here. This is the pchan
+ * currently in effect; for dynamic ts, this is the dyn kind (GSM_PCHAN_TCH_F_TCH_H_PDCH or
+ * GSM_PCHAN_TCH_F_PDCH) and does not show the pchan type currently active. */
+ enum gsm_phys_chan_config pchan_on_init;
+ /* This is the *actual* pchan type currently active. For dynamic timeslots, this reflects either
+ * GSM_PCHAN_NONE or one of the standard GSM_PCHAN_TCH_F, GSM_PCHAN_TCH_H, GSM_PCHAN_PDCH.
+ * Callers can use this transparently without being aware of dyn ts. */
+ enum gsm_phys_chan_config pchan_is;
+
+ /* After a PDCH ACT NACK, we shall not infinitely loop to try and ACT again.
+ * Also marks a timeslot where PDCH was deactivated by VTY. This is cleared whenever a timeslot
+ * enters IN_USE state, i.e. after each TCH use we try to PDCH ACT once again. */
+ bool pdch_act_allowed;
- unsigned int flags;
struct gsm_abis_mo mo;
struct tlv_parsed nm_attr;
uint8_t nm_chan_comb;
@@ -1011,9 +1125,12 @@ struct gsm_bts {
struct gsm_network *gsm_network_init(void *ctx);
struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, uint8_t bts_num);
-struct gsm_bts *gsm_bts_num(struct gsm_network *net, int num);
-bool gsm_bts_matches_cell_id(struct gsm_bts *bts, const struct gsm0808_cell_id *ci);
-struct gsm_bts *gsm_bts_by_cell_id(struct gsm_network *net, const struct gsm0808_cell_id *ci);
+struct gsm_bts *gsm_bts_num(const struct gsm_network *net, int num);
+bool gsm_bts_matches_cell_id(const struct gsm_bts *bts, const struct gsm0808_cell_id *cell_id);
+bool gsm_bts_matches_lai(const struct gsm_bts *bts, const struct osmo_location_area_id *lai);
+struct gsm_bts *gsm_bts_by_cell_id(const struct gsm_network *net,
+ const struct gsm0808_cell_id *cell_id,
+ int match_idx);
int gsm_bts_local_neighbor_add(struct gsm_bts *bts, struct gsm_bts *neighbor);
int gsm_bts_local_neighbor_del(struct gsm_bts *bts, const struct gsm_bts *neighbor);
@@ -1030,9 +1147,12 @@ enum gsm_bts_type_variant str2btsvariant(const char *arg);
const char *btsvariant2str(enum gsm_bts_type_variant v);
extern const struct value_string gsm_chreq_descs[];
-const struct value_string gsm_pchant_names[13];
-const struct value_string gsm_pchant_descs[13];
+extern const struct value_string gsm_pchant_names[];
+extern const struct value_string gsm_pchant_descs[];
+extern const struct value_string gsm_pchan_ids[];
const char *gsm_pchan_name(enum gsm_phys_chan_config c);
+static inline const char *gsm_pchan_id(enum gsm_phys_chan_config c)
+{ return get_value_string(gsm_pchan_ids, c); }
enum gsm_phys_chan_config gsm_pchan_parse(const char *name);
const char *gsm_lchant_name(enum gsm_chan_t c);
const char *gsm_chreq_name(enum gsm_chreq_reason_t c);
@@ -1040,7 +1160,6 @@ char *gsm_trx_name(const struct gsm_bts_trx *trx);
char *gsm_ts_name(const struct gsm_bts_trx_ts *ts);
char *gsm_ts_and_pchan_name(const struct gsm_bts_trx_ts *ts);
char *gsm_lchan_name_compute(const struct gsm_lchan *lchan);
-const char *gsm_lchans_name(enum gsm_lchan_state s);
static inline char *gsm_lchan_name(const struct gsm_lchan *lchan)
{
@@ -1086,7 +1205,7 @@ struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr,
int *rc);
enum gsm_phys_chan_config ts_pchan(struct gsm_bts_trx_ts *ts);
-uint8_t ts_subslots(struct gsm_bts_trx_ts *ts);
+uint8_t pchan_subslots(enum gsm_phys_chan_config pchan);
bool ts_is_tch(struct gsm_bts_trx_ts *ts);
@@ -1156,11 +1275,32 @@ enum {
};
enum {
+ BSC_CTR_ASSIGNMENT_ATTEMPTED,
+ BSC_CTR_ASSIGNMENT_COMPLETED,
+ BSC_CTR_ASSIGNMENT_STOPPED,
+ BSC_CTR_ASSIGNMENT_NO_CHANNEL,
+ BSC_CTR_ASSIGNMENT_TIMEOUT,
+ BSC_CTR_ASSIGNMENT_FAILED,
+ BSC_CTR_ASSIGNMENT_ERROR,
BSC_CTR_HANDOVER_ATTEMPTED,
+ BSC_CTR_HANDOVER_COMPLETED,
+ BSC_CTR_HANDOVER_STOPPED,
BSC_CTR_HANDOVER_NO_CHANNEL,
BSC_CTR_HANDOVER_TIMEOUT,
- BSC_CTR_HANDOVER_COMPLETED,
BSC_CTR_HANDOVER_FAILED,
+ BSC_CTR_HANDOVER_ERROR,
+ BSC_CTR_INTER_BSC_HO_MO_ATTEMPTED,
+ BSC_CTR_INTER_BSC_HO_MO_COMPLETED,
+ BSC_CTR_INTER_BSC_HO_MO_STOPPED,
+ BSC_CTR_INTER_BSC_HO_MO_TIMEOUT,
+ BSC_CTR_INTER_BSC_HO_MO_ERROR,
+ BSC_CTR_INTER_BSC_HO_MT_ATTEMPTED,
+ BSC_CTR_INTER_BSC_HO_MT_COMPLETED,
+ BSC_CTR_INTER_BSC_HO_MT_STOPPED,
+ BSC_CTR_INTER_BSC_HO_MT_NO_CHANNEL,
+ BSC_CTR_INTER_BSC_HO_MT_FAILED,
+ BSC_CTR_INTER_BSC_HO_MT_TIMEOUT,
+ BSC_CTR_INTER_BSC_HO_MT_ERROR,
BSC_CTR_PAGING_ATTEMPTED,
BSC_CTR_PAGING_DETACHED,
BSC_CTR_PAGING_RESPONDED,
@@ -1168,11 +1308,42 @@ enum {
};
static const struct rate_ctr_desc bsc_ctr_description[] = {
- [BSC_CTR_HANDOVER_ATTEMPTED] = {"handover:attempted", "Received handover attempts."},
- [BSC_CTR_HANDOVER_NO_CHANNEL] = {"handover:no_channel", "Sent no channel available responses."},
- [BSC_CTR_HANDOVER_TIMEOUT] = {"handover:timeout", "Timeouts of timer T3103."},
- [BSC_CTR_HANDOVER_COMPLETED] = {"handover:completed", "Received handover completed."},
- [BSC_CTR_HANDOVER_FAILED] = {"handover:failed", "Received HO FAIL messages."},
+ [BSC_CTR_ASSIGNMENT_ATTEMPTED] = {"assignment:attempted", "Intra-cell re-assignment attempts."},
+ [BSC_CTR_ASSIGNMENT_COMPLETED] = {"assignment:completed", "Intra-cell re-assignment completed."},
+ [BSC_CTR_ASSIGNMENT_STOPPED] = {"assignment:stopped", "Connection ended during re-assignment."},
+ [BSC_CTR_ASSIGNMENT_NO_CHANNEL] = {"assignment:no_channel", "Failure to allocate lchan for re-assignment."},
+ [BSC_CTR_ASSIGNMENT_TIMEOUT] = {"assignment:timeout", "Re-assignment timed out."},
+ [BSC_CTR_ASSIGNMENT_FAILED] = {"assignment:failed", "Received FAIL message."},
+ [BSC_CTR_ASSIGNMENT_ERROR] = {"assignment:error", "Re-assigment failed for other reason."},
+
+ [BSC_CTR_HANDOVER_ATTEMPTED] = {"handover:attempted", "Intra-BSC handover attempts."},
+ [BSC_CTR_HANDOVER_COMPLETED] = {"handover:completed", "Intra-BSC handover completed."},
+ [BSC_CTR_HANDOVER_STOPPED] = {"handover:stopped", "Connection ended during HO."},
+ [BSC_CTR_HANDOVER_NO_CHANNEL] = {"handover:no_channel", "Failure to allocate lchan for HO."},
+ [BSC_CTR_HANDOVER_TIMEOUT] = {"handover:timeout", "Handover timed out."},
+ [BSC_CTR_HANDOVER_FAILED] = {"handover:failed", "Received Handover Fail messages."},
+ [BSC_CTR_HANDOVER_ERROR] = {"handover:error", "Re-assigment failed for other reason."},
+
+ [BSC_CTR_INTER_BSC_HO_MO_ATTEMPTED] = {"interbsc_ho_mo:attempted",
+ "Attempts to handover to remote BSS."},
+ [BSC_CTR_INTER_BSC_HO_MO_COMPLETED] = {"interbsc_ho_mo:completed",
+ "Handover to remote BSS completed."},
+ [BSC_CTR_INTER_BSC_HO_MO_STOPPED] = {"interbsc_ho_mo:stopped", "Connection ended during HO."},
+ [BSC_CTR_INTER_BSC_HO_MO_TIMEOUT] = {"interbsc_ho_mo:timeout", "Handover timed out."},
+ [BSC_CTR_INTER_BSC_HO_MO_ERROR] = {"interbsc_ho_mo:error",
+ "Handover to remote BSS failed for other reason."},
+
+ [BSC_CTR_INTER_BSC_HO_MT_ATTEMPTED] = {"interbsc_ho_mt:attempted",
+ "Attempts to handover from remote BSS."},
+ [BSC_CTR_INTER_BSC_HO_MT_COMPLETED] = {"interbsc_ho_mt:completed",
+ "Handover from remote BSS completed."},
+ [BSC_CTR_INTER_BSC_HO_MT_STOPPED] = {"interbsc_ho_mt:stopped", "Connection ended during HO."},
+ [BSC_CTR_INTER_BSC_HO_MT_NO_CHANNEL] = {"interbsc_ho_mt:no_channel",
+ "Failure to allocate lchan for HO."},
+ [BSC_CTR_INTER_BSC_HO_MT_TIMEOUT] = {"interbsc_ho_mt:timeout", "Handover from remote BSS timed out."},
+ [BSC_CTR_INTER_BSC_HO_MT_FAILED] = {"interbsc_ho_mt:failed", "Received Handover Fail message."},
+ [BSC_CTR_INTER_BSC_HO_MT_ERROR] = {"interbsc_ho_mt:error",
+ "Handover from remote BSS failed for other reason."},
[BSC_CTR_PAGING_ATTEMPTED] = {"paging:attempted", "Paging attempts for a subscriber."},
[BSC_CTR_PAGING_DETACHED] = {"paging:detached", "Paging request send failures because no responsible BTS was found."},
@@ -1269,6 +1440,11 @@ struct gsm_network {
struct neighbor_ident_list *neighbor_bss_cells;
};
+struct gsm_audio_support {
+ uint8_t hr : 1,
+ ver : 7;
+};
+
static inline const struct osmo_location_area_id *bts_lai(struct gsm_bts *bts)
{
static struct osmo_location_area_id lai;
@@ -1404,12 +1580,27 @@ void gsm_bts_set_radio_link_timeout(struct gsm_bts *bts, int value);
bool classmark_is_r99(struct gsm_classmark *cm);
-void gsm_ts_check_init(struct gsm_bts_trx_ts *ts);
-void gsm_trx_mark_all_ts_uninitialized(struct gsm_bts_trx *trx);
-void gsm_bts_mark_all_ts_uninitialized(struct gsm_bts *bts);
-
bool trx_is_usable(const struct gsm_bts_trx *trx);
+bool ts_is_usable(const struct gsm_bts_trx_ts *ts);
+
+int gsm_lchan_type_by_pchan(enum gsm_phys_chan_config pchan);
+enum gsm_phys_chan_config gsm_pchan_by_lchan_type(enum gsm_chan_t type);
+
+void gsm_bts_all_ts_dispatch(struct gsm_bts *bts, uint32_t ts_ev, void *data);
+void gsm_trx_all_ts_dispatch(struct gsm_bts_trx *trx, uint32_t ts_ev, void *data);
+
+int bts_count_free_ts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan);
+
+static inline const char *gsm48_chan_mode_name(enum gsm48_chan_mode val)
+{ return get_value_string(gsm48_chan_mode_names, val); }
-bool on_gsm_ts_init(struct gsm_bts_trx_ts *ts);
+int bsc_match_codec_pref(enum gsm48_chan_mode *chan_mode,
+ bool *full_rate,
+ const struct gsm0808_channel_type *ct,
+ const struct gsm0808_speech_codec_list *scl,
+ struct gsm_audio_support * const *audio_support,
+ int audio_length);
+bool sockaddr_to_str_and_uint(char *rtp_addr, size_t rtp_addr_len, uint16_t *rtp_port,
+ const struct sockaddr_storage *sa);
#endif /* _GSM_DATA_H */
diff --git a/include/osmocom/bsc/gsm_timers.h b/include/osmocom/bsc/gsm_timers.h
index fde8c93c0..78f04edd9 100644
--- a/include/osmocom/bsc/gsm_timers.h
+++ b/include/osmocom/bsc/gsm_timers.h
@@ -31,7 +31,7 @@ struct T_def {
#define for_each_T_def(d, T_defs) \
for (d = T_defs; d && (d->T || d->default_val || d->desc); d++)
-int T_def_get(struct T_def *T_defs, int T, enum T_unit as_unit, int val_if_not_present);
+int T_def_get(const struct T_def *T_defs, int T, enum T_unit as_unit, int val_if_not_present);
void T_defs_reset(struct T_def *T_defs);
struct T_def *T_def_get_entry(struct T_def *T_defs, int T);
@@ -44,12 +44,13 @@ struct state_timeout {
bool keep_timer;
};
-struct state_timeout *get_state_timeout(uint32_t state, struct state_timeout *timeouts_array);
+const struct state_timeout *get_state_timeout(uint32_t state,
+ const struct state_timeout *timeouts_array);
#define fsm_inst_state_chg_T(fi, state, timeouts_array, T_defs, default_timeout) \
_fsm_inst_state_chg_T(fi, state, timeouts_array, T_defs, default_timeout, \
__FILE__, __LINE__)
int _fsm_inst_state_chg_T(struct osmo_fsm_inst *fi, uint32_t state,
- struct state_timeout *timeouts_array,
- struct T_def *T_defs, int default_timeout,
+ const struct state_timeout *timeouts_array,
+ const struct T_def *T_defs, int default_timeout,
const char *file, int line);
diff --git a/include/osmocom/bsc/handover.h b/include/osmocom/bsc/handover.h
index 847d985fb..322913da4 100644
--- a/include/osmocom/bsc/handover.h
+++ b/include/osmocom/bsc/handover.h
@@ -5,71 +5,38 @@
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/timer.h>
#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/gsm0808.h>
#include <osmocom/bsc/neighbor_ident.h>
+#include <osmocom/bsc/gsm_data.h>
+struct gsm_network;
struct gsm_lchan;
struct gsm_bts;
struct gsm_subscriber_connection;
struct gsm_meas_rep mr;
-#define LOGPHOLCHANTOLCHAN(old_lchan, new_lchan, level, fmt, args...) \
- LOGP(DHODEC, level, "(BTS %u trx %u arfcn %u ts %u lchan %u %s)->(BTS %u trx %u arfcn %u ts %u lchan %u %s) (subscr %s) " fmt, \
- old_lchan->ts->trx->bts->nr, \
- old_lchan->ts->trx->nr, \
- old_lchan->ts->trx->arfcn, \
- old_lchan->ts->nr, \
- old_lchan->nr, \
- gsm_pchan_name(old_lchan->ts->pchan), \
- new_lchan->ts->trx->bts->nr, \
- new_lchan->ts->trx->nr, \
- new_lchan->ts->trx->arfcn, \
- new_lchan->ts->nr, \
- new_lchan->nr, \
- gsm_pchan_name(new_lchan->ts->pchan), \
- bsc_subscr_name(old_lchan->conn? old_lchan->conn->bsub : NULL), \
- ## args)
-
-#define LOGPHO(struct_bsc_handover, level, fmt, args ...) \
- LOGPHOLCHANTOLCHAN(struct_bsc_handover->old_lchan, struct_bsc_handover->new_lchan, level, fmt, ## args)
-
-enum hodec_id {
- HODEC_NONE,
- HODEC1 = 1,
- HODEC2 = 2,
+enum handover_result {
+ HO_RESULT_OK,
+ HO_RESULT_FAIL_NO_CHANNEL,
+ HO_RESULT_FAIL_RR_HO_FAIL,
+ HO_RESULT_FAIL_TIMEOUT,
+ HO_RESULT_CONN_RELEASE,
+ HO_RESULT_ERROR,
};
-struct bsc_handover {
- struct llist_head list;
-
- /* Initial details of what is requested */
- struct gsm_lchan *old_lchan;
- struct gsm_bts *new_bts;
- enum gsm_chan_t new_lchan_type;
- bool async;
-
- /* Derived and resulting state */
- bool inter_cell;
- uint8_t ho_ref;
- enum hodec_id from_hodec_id;
- struct gsm_lchan *new_lchan;
- struct osmo_timer_list T3103;
-};
+extern const struct value_string handover_result_names[];
+inline static const char *handover_result_name(enum handover_result val)
+{ return get_value_string(handover_result_names, val); }
-int bsc_handover_start(enum hodec_id from_hodec_id, struct gsm_lchan *old_lchan, struct gsm_bts *new_bts,
- enum gsm_chan_t new_lchan_type);
-int bsc_handover_start_gscon(struct gsm_subscriber_connection *conn);
-void bsc_clear_handover(struct gsm_subscriber_connection *conn, int free_lchan);
-struct gsm_lchan *bsc_handover_pending(struct gsm_lchan *new_lchan);
-
-int bsc_ho_count(struct gsm_bts *bts, bool inter_cell);
+int bts_handover_count(struct gsm_bts *bts, int ho_scopes);
/* Handover decision algorithms' actions to take on incoming handover-relevant events.
*
* All events that are interesting for handover decision are actually communicated by S_LCHAN_* signals,
* so theoretically, each handover algorithm could evaluate those. However, handover_logic.c cleans up
* handover operation state upon receiving some of these signals. To allow a handover decision algorithm
- * to take advantage of e.g. the struct bsc_handover before it is discarded, the handover decision event
+ * to take advantage of e.g. the struct handover before it is discarded, the handover decision event
* handler needs to be invoked before handover_logic.c discards the state. For example, if the handover
* decision wants to place a penalty timer upon a handover failure, it still needs to know which target
* cell the handover failed for; handover_logic.c erases that knowledge on handover failure, since it
@@ -89,13 +56,26 @@ struct handover_decision_callbacks {
int hodec_id;
void (*on_measurement_report)(struct gsm_meas_rep *mr);
- void (*on_ho_chan_activ_nack)(struct bsc_handover *ho);
- void (*on_ho_failure)(struct bsc_handover *ho);
+ void (*on_handover_end)(struct gsm_subscriber_connection *conn, enum handover_result result);
};
void handover_decision_callbacks_register(struct handover_decision_callbacks *hdc);
struct handover_decision_callbacks *handover_decision_callbacks_get(int hodec_id);
+int bsc_tx_bssmap_ho_required(struct gsm_lchan *lchan, const struct gsm0808_cell_id_list2 *target_cells);
+int bsc_tx_bssmap_ho_request_ack(struct gsm_subscriber_connection *conn,
+ struct msgb *rr_ho_command);
+int bsc_tx_bssmap_ho_detect(struct gsm_subscriber_connection *conn);
+enum handover_result bsc_tx_bssmap_ho_complete(struct gsm_subscriber_connection *conn,
+ struct gsm_lchan *lchan);
+void bsc_tx_bssmap_ho_failure(struct gsm_subscriber_connection *conn);
+
struct gsm_bts *bts_by_neighbor_ident(const struct gsm_network *net,
const struct neighbor_ident_key *search_for);
struct neighbor_ident_key *bts_ident_key(const struct gsm_bts *bts);
+
+void handover_parse_inter_bsc_mt(struct gsm_subscriber_connection *conn,
+ struct msgb *ho_request_msg);
+
+void handover_mt_allocate_lchan(struct handover *ho);
+int handover_mt_send_rr_ho_command(struct handover *ho);
diff --git a/include/osmocom/bsc/handover_fsm.h b/include/osmocom/bsc/handover_fsm.h
new file mode 100644
index 000000000..4b8b6ec00
--- /dev/null
+++ b/include/osmocom/bsc/handover_fsm.h
@@ -0,0 +1,81 @@
+/* Handover FSM API for intra-BSC and inter-BSC MT (to this BSC) Handover.
+ * (For inter-BSC MO, from this BSC, see handover_inter_mo_fsm.c) */
+#pragma once
+
+#include <osmocom/bsc/debug.h>
+#include <osmocom/bsc/handover.h>
+
+const char *handover_status(struct gsm_subscriber_connection *conn);
+
+/* This macro automatically includes a final \n, if omitted. */
+#define LOG_HO(conn, level, fmt, args...) do { \
+ if (conn->ho.fi) \
+ LOGPFSML(conn->ho.fi, level, "%s: " fmt "%s", \
+ handover_status(conn), \
+ ## args, LOG_ADD_NEWLINE(fmt)); \
+ else \
+ LOGP(DHODEC, level, "%s: " fmt "%s", \
+ handover_status(conn), \
+ ## args, LOG_ADD_NEWLINE(fmt)); \
+ } while(0)
+
+/* Terminology:
+ * Intra-Cell: stays within one BTS, this should actually be an Assignment.
+ * Intra-BSC: stays within one BSC, but moves between BTSes.
+ * Inter-BSC: moves between BSCs.
+ * Inter-BSC MO: Mobile Originated: move away from this BSC to another one.
+ * Inter-BSC MT: Mobile Terminated: move from another BSC to this one.
+ */
+
+enum handover_fsm_state {
+ HO_ST_NOT_STARTED,
+
+ HO_ST_WAIT_LCHAN_ACTIVE,
+ HO_ST_WAIT_RR_HO_DETECT,
+ HO_ST_WAIT_RR_HO_COMPLETE,
+ HO_ST_WAIT_LCHAN_ESTABLISHED,
+ HO_ST_WAIT_MGW_ENDPOINT_TO_MSC,
+
+ /* The inter-BSC MO Handover FSM has completely separate states, but since it makes sense for it
+ * to also live in conn->ho.fi, it should share the same event enum. From there it is merely
+ * cosmetic to just include the separate fairly trivial states in the same FSM definition.
+ * An inter-MO FSM is almost unnecessary. The sole reason is to wait whether the MSC indeed
+ * clears the conn, and if not to log and count a failed handover attempt. */
+ MOHO_ST_WAIT_HO_COMMAND,
+ MOHO_ST_WAIT_CLEAR,
+};
+
+enum handover_fsm_event {
+ HO_EV_LCHAN_ACTIVE,
+ HO_EV_LCHAN_ESTABLISHED,
+ HO_EV_LCHAN_ERROR,
+ HO_EV_RR_HO_DETECT,
+ HO_EV_RR_HO_COMPLETE,
+ HO_EV_RR_HO_FAIL,
+ HO_EV_MSC_MGW_OK,
+ HO_EV_MSC_MGW_FAIL,
+ HO_EV_CONN_RELEASING,
+
+ MOHO_EV_BSSMAP_HO_COMMAND,
+};
+
+struct moho_rx_bssmap_ho_command {
+ const uint8_t *l3_info;
+ const uint8_t l3_info_len;
+};
+
+/* To be sent along with the HO_EV_RR_HO_DETECT */
+struct handover_rr_detect_data {
+ struct msgb *msg;
+ const uint8_t *access_delay;
+};
+
+void handover_request(struct handover_mo_req *req);
+void handover_start(struct handover_mo_req *req);
+void handover_start_inter_bsc_mt(struct gsm_subscriber_connection *conn,
+ struct msgb *ho_request_msg);
+void handover_end(struct gsm_subscriber_connection *conn, enum handover_result result);
+
+const char *handover_status(struct gsm_subscriber_connection *conn);
+bool handover_is_sane(struct gsm_subscriber_connection *conn, struct gsm_lchan *old_lchan,
+ struct gsm_lchan *new_lchan);
diff --git a/include/osmocom/bsc/lchan_fsm.h b/include/osmocom/bsc/lchan_fsm.h
new file mode 100644
index 000000000..2be9e1ca1
--- /dev/null
+++ b/include/osmocom/bsc/lchan_fsm.h
@@ -0,0 +1,88 @@
+/* osmo-bsc API to manage lchans, logical channels in GSM cells. */
+#pragma once
+
+#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/debug.h>
+
+/* This macro automatically includes a final \n, if omitted. */
+#define LOG_LCHAN(lchan, level, fmt, args...) do { \
+ if (lchan->fi) \
+ LOGPFSML(lchan->fi, level, "(type=%s) " fmt "%s", gsm_lchant_name(lchan->type), \
+ ## args, LOG_ADD_NEWLINE(fmt)); \
+ else \
+ LOGP(DRSL, level, "%s (not initialized) " fmt "%s", gsm_lchan_name(lchan), \
+ ## args, LOG_ADD_NEWLINE(fmt)); \
+ } while(0)
+
+enum lchan_fsm_state {
+ LCHAN_ST_UNUSED,
+ LCHAN_ST_WAIT_TS_READY,
+ LCHAN_ST_WAIT_ACTIV_ACK, /*< After RSL Chan Act Ack, lchan is active but RTP not configured. */
+ LCHAN_ST_WAIT_RLL_ESTABLISH,
+ LCHAN_ST_WAIT_MGW_ENDPOINT_AVAILABLE,
+ LCHAN_ST_WAIT_IPACC_CRCX_ACK,
+ LCHAN_ST_WAIT_IPACC_MDCX_ACK,
+ LCHAN_ST_WAIT_MGW_ENDPOINT_CONFIGURED,
+ LCHAN_ST_ESTABLISHED, /*< Active and RTP is fully configured. */
+ LCHAN_ST_WAIT_SAPIS_RELEASED,
+ LCHAN_ST_WAIT_BEFORE_RF_RELEASE,
+ LCHAN_ST_WAIT_RF_RELEASE_ACK,
+ LCHAN_ST_WAIT_AFTER_ERROR,
+ LCHAN_ST_BORKEN,
+};
+
+enum lchan_fsm_event {
+ LCHAN_EV_ACTIVATE,
+ LCHAN_EV_TS_READY,
+ LCHAN_EV_TS_ERROR,
+ LCHAN_EV_RSL_CHAN_ACTIV_ACK,
+ LCHAN_EV_RSL_CHAN_ACTIV_NACK,
+ LCHAN_EV_RLL_ESTABLISH_IND,
+ LCHAN_EV_MGW_ENDPOINT_AVAILABLE,
+ LCHAN_EV_MGW_ENDPOINT_CONFIGURED,
+ LCHAN_EV_MGW_ENDPOINT_ERROR,
+ LCHAN_EV_IPACC_CRCX_ACK,
+ LCHAN_EV_IPACC_CRCX_NACK,
+ LCHAN_EV_IPACC_MDCX_ACK,
+ LCHAN_EV_IPACC_MDCX_NACK,
+ LCHAN_EV_RLL_REL_IND,
+ LCHAN_EV_RLL_REL_CONF,
+ LCHAN_EV_RSL_RF_CHAN_REL_ACK,
+ LCHAN_EV_RLL_ERR_IND,
+
+ /* FIXME: not yet implemented: Chan Mode Modify, see assignment_fsm_start(). */
+ LCHAN_EV_CHAN_MODE_MODIF_ACK,
+ LCHAN_EV_CHAN_MODE_MODIF_ERROR,
+};
+
+void lchan_fsm_alloc(struct gsm_lchan *lchan);
+void lchan_release(struct gsm_lchan *lchan, bool sacch_deact,
+ bool err, enum gsm48_rr_cause cause_rr);
+
+struct lchan_activate_info {
+ enum lchan_activate_mode activ_for;
+ struct gsm_subscriber_connection *for_conn;
+ /* This always is for a specific lchan, so its lchan->type indicates full or half rate.
+ * When a dyn TS was selected, the lchan->type has been set to the desired rate. */
+ enum gsm48_chan_mode chan_mode;
+ bool requires_voice_stream;
+ uint16_t msc_assigned_cic;
+ struct gsm_lchan *old_lchan;
+};
+
+void lchan_activate(struct gsm_lchan *lchan, struct lchan_activate_info *info);
+
+static inline const char *lchan_state_name(struct gsm_lchan *lchan)
+{
+ return lchan->fi ? osmo_fsm_inst_state_name(lchan->fi) : "NULL";
+}
+
+static inline bool lchan_state_is(struct gsm_lchan *lchan, uint32_t state)
+{
+ return lchan->fi && lchan->fi->state == state;
+}
+
+bool lchan_may_receive_data(struct gsm_lchan *lchan);
+
+void lchan_forget_conn(struct gsm_lchan *lchan);
+void lchan_forget_mgw_endpoint(struct gsm_lchan *lchan);
diff --git a/include/osmocom/bsc/lchan_select.h b/include/osmocom/bsc/lchan_select.h
new file mode 100644
index 000000000..4aecdf676
--- /dev/null
+++ b/include/osmocom/bsc/lchan_select.h
@@ -0,0 +1,6 @@
+/* Select a suitable lchan from a given cell. */
+#pragma once
+
+struct gsm_lchan *lchan_select_by_type(struct gsm_bts *bts, enum gsm_chan_t type);
+struct gsm_lchan *lchan_select_by_chan_mode(struct gsm_bts *bts,
+ enum gsm48_chan_mode chan_mode, bool full_rate);
diff --git a/include/osmocom/bsc/mgw_endpoint_fsm.h b/include/osmocom/bsc/mgw_endpoint_fsm.h
new file mode 100644
index 000000000..e68468b5c
--- /dev/null
+++ b/include/osmocom/bsc/mgw_endpoint_fsm.h
@@ -0,0 +1,60 @@
+/* osmo-bsc API to manage all sides of an MGW endpoint */
+#pragma once
+
+#include <osmocom/mgcp_client/mgcp_client_fsm.h>
+
+#include <osmocom/bsc/debug.h>
+
+/* This macro automatically includes a final \n, if omitted. */
+#define LOG_MGWEP(mgwep, level, fmt, args...) do { \
+ LOGPFSML(mgwep->fi, level, "(%s) " fmt "%s", \
+ mgw_endpoint_name(mgwep), \
+ ## args, LOG_ADD_NEWLINE(fmt)); \
+ } while(0)
+
+enum mgwep_fsm_state {
+ MGWEP_ST_UNUSED,
+ MGWEP_ST_WAIT_MGW_RESPONSE,
+ MGWEP_ST_IN_USE,
+};
+
+enum mgwep_fsm_event {
+ _MGWEP_EV_LAST,
+ /* and MGW response events are allocated dynamically */
+};
+
+struct mgw_endpoint;
+struct mgwep_ci;
+struct T_def;
+
+void mgw_endpoint_fsm_init(struct T_def *T_defs);
+
+struct mgw_endpoint *mgw_endpoint_alloc(struct osmo_fsm_inst *parent, uint32_t parent_term_event,
+ struct mgcp_client *mgcp_client,
+ const char *fsm_id,
+ const char *endpoint_str_fmt, ...);
+
+struct mgwep_ci *mgw_endpoint_ci_add(struct mgw_endpoint *mgwep,
+ const char *label_fmt, ...);
+const struct mgcp_conn_peer *mgwep_ci_get_rtp_info(const struct mgwep_ci *ci);
+bool mgwep_ci_get_crcx_info_to_sockaddr(const struct mgwep_ci *ci, struct sockaddr_storage *dest);
+
+void mgw_endpoint_ci_request(struct mgwep_ci *ci,
+ enum mgcp_verb verb, const struct mgcp_conn_peer *verb_info,
+ struct osmo_fsm_inst *notify,
+ uint32_t event_success, uint32_t event_failure,
+ void *notify_data);
+
+static inline void mgw_endpoint_ci_dlcx(struct mgwep_ci *ci)
+{
+ mgw_endpoint_ci_request(ci, MGCP_VERB_DLCX, NULL, NULL, 0, 0, NULL);
+}
+
+void mgw_endpoint_clear(struct mgw_endpoint *mgwep);
+
+const char *mgw_endpoint_name(const struct mgw_endpoint *mgwep);
+const char *mgwep_ci_name(const struct mgwep_ci *ci);
+const char *mgcp_conn_peer_name(const struct mgcp_conn_peer *info);
+
+enum mgcp_codecs chan_mode_to_mgcp_codec(enum gsm48_chan_mode chan_mode, bool full_rate);
+void mgcp_pick_codec(struct mgcp_conn_peer *verb_info, const struct gsm_lchan *lchan);
diff --git a/include/osmocom/bsc/neighbor_ident.h b/include/osmocom/bsc/neighbor_ident.h
index 86e062a67..bba18c3e3 100644
--- a/include/osmocom/bsc/neighbor_ident.h
+++ b/include/osmocom/bsc/neighbor_ident.h
@@ -57,4 +57,7 @@ void neighbor_ident_vty_write(struct vty *vty, const char *indent, struct gsm_bt
"ARFCN of neighbor cell\n" "ARFCN value\n" \
"BSIC of neighbor cell\n" "9-bit BSIC of neighbor cell\n" "BSIC value\n" \
"for all BSICs / use any BSIC in this ARFCN\n"
-bool neighbor_ident_vty_parse_key_params(struct vty *vty, const char **argv, struct neighbor_ident_key *key);
+bool neighbor_ident_vty_parse_key_params(struct vty *vty, const char **argv,
+ struct neighbor_ident_key *key);
+bool neighbor_ident_bts_parse_key_params(struct vty *vty, struct gsm_bts *bts, const char **argv,
+ struct neighbor_ident_key *key);
diff --git a/include/osmocom/bsc/osmo_bsc.h b/include/osmocom/bsc/osmo_bsc.h
index ed5698db4..0e19b0bbc 100644
--- a/include/osmocom/bsc/osmo_bsc.h
+++ b/include/osmocom/bsc/osmo_bsc.h
@@ -1,10 +1,9 @@
-/* OpenBSC BSC code */
+#pragma once
-#ifndef OSMO_BSC_H
-#define OSMO_BSC_H
+#include <osmocom/gsm/protocol/gsm_04_08.h>
-#include "bsc_api.h"
-#include "bsc_msg_filter.h"
+#include <osmocom/bsc/bsc_api.h>
+#include <osmocom/bsc/bsc_msg_filter.h>
#define BSS_SEND_USSD 1
@@ -16,6 +15,9 @@ enum bsc_con {
};
struct bsc_msc_data;
+struct gsm0808_channel_type;
+struct gsm0808_speech_codec_list;
+struct gsm_audio_support;
struct bsc_api *osmo_bsc_api();
@@ -38,5 +40,3 @@ int bsc_ctrl_cmds_install();
void bsc_gen_location_state_trap(struct gsm_bts *bts);
struct llist_head *bsc_access_lists(void);
-
-#endif
diff --git a/include/osmocom/bsc/osmo_bsc_lcls.h b/include/osmocom/bsc/osmo_bsc_lcls.h
index 2e6023404..3eaa5891a 100644
--- a/include/osmocom/bsc/osmo_bsc_lcls.h
+++ b/include/osmocom/bsc/osmo_bsc_lcls.h
@@ -38,3 +38,4 @@ void lcls_apply_config(struct gsm_subscriber_connection *conn);
extern struct osmo_fsm lcls_fsm;
+void bssmap_add_lcls_status_if_needed(struct gsm_subscriber_connection *conn, struct msgb *msg);
diff --git a/include/osmocom/bsc/timeslot_fsm.h b/include/osmocom/bsc/timeslot_fsm.h
new file mode 100644
index 000000000..7ca9009ae
--- /dev/null
+++ b/include/osmocom/bsc/timeslot_fsm.h
@@ -0,0 +1,50 @@
+/* osmo-bsc API to manage timeslot status: init and switch of dynamic PDCH. */
+#pragma once
+
+#include <osmocom/bsc/gsm_data.h>
+
+/* This macro automatically includes a final \n, if omitted. */
+#define LOG_TS(ts, level, fmt, args...) do { \
+ if (ts->fi) \
+ LOGPFSML(ts->fi, level, "%s%s%s" fmt "%s", \
+ ts->pchan_is != ts->pchan_from_config ? "(pchan_is=" : "", \
+ ts->pchan_is != ts->pchan_from_config ? gsm_pchan_name(ts->pchan_is) : "", \
+ ts->pchan_is != ts->pchan_from_config ? ") " : "", \
+## args, \
+ (!fmt || !*fmt || fmt[strlen(fmt)-1] != '\n') ? "\n" : ""); \
+ else \
+ LOGP(DRSL, level, "%s" fmt "%s", \
+ gsm_ts_name(ts), \
+ ## args, \
+ (!fmt || !*fmt || fmt[strlen(fmt)-1] != '\n') ? "\n" : ""); \
+ } while(0)
+
+enum ts_fsm_state {
+ TS_ST_NOT_INITIALIZED,
+ TS_ST_UNUSED,
+ TS_ST_WAIT_PDCH_ACT,
+ TS_ST_PDCH,
+ TS_ST_WAIT_PDCH_DEACT,
+ TS_ST_IN_USE,
+ TS_ST_BORKEN,
+};
+
+enum ts_fsm_event {
+ TS_EV_OML_READY,
+ TS_EV_OML_DOWN,
+ TS_EV_RSL_READY,
+ TS_EV_RSL_DOWN,
+ TS_EV_LCHAN_REQUESTED,
+ TS_EV_LCHAN_UNUSED,
+ TS_EV_PDCH_ACT_ACK,
+ TS_EV_PDCH_ACT_NACK,
+ TS_EV_PDCH_DEACT_ACK,
+ TS_EV_PDCH_DEACT_NACK,
+};
+
+void ts_fsm_alloc(struct gsm_bts_trx_ts *ts);
+
+bool ts_is_capable_of_pchan(struct gsm_bts_trx_ts *ts, enum gsm_phys_chan_config pchan);
+bool ts_is_lchan_waiting_for_pchan(struct gsm_bts_trx_ts *ts, enum gsm_phys_chan_config *target_pchan);
+bool ts_is_pchan_switching(struct gsm_bts_trx_ts *ts, enum gsm_phys_chan_config *target_pchan);
+bool ts_usable_as_pchan(struct gsm_bts_trx_ts *ts, enum gsm_phys_chan_config as_pchan);