diff options
Diffstat (limited to 'include')
-rw-r--r-- | include/osmocom/bsc/Makefile.am | 6 | ||||
-rw-r--r-- | include/osmocom/bsc/abis_rsl.h | 27 | ||||
-rw-r--r-- | include/osmocom/bsc/assignment_fsm.h | 44 | ||||
-rw-r--r-- | include/osmocom/bsc/bsc_api.h | 7 | ||||
-rw-r--r-- | include/osmocom/bsc/bsc_msc_data.h | 5 | ||||
-rw-r--r-- | include/osmocom/bsc/bsc_subscr_conn_fsm.h | 75 | ||||
-rw-r--r-- | include/osmocom/bsc/bsc_subscriber.h | 1 | ||||
-rw-r--r-- | include/osmocom/bsc/chan_alloc.h | 9 | ||||
-rw-r--r-- | include/osmocom/bsc/codec_pref.h | 17 | ||||
-rw-r--r-- | include/osmocom/bsc/gsm_04_08_rr.h | 2 | ||||
-rw-r--r-- | include/osmocom/bsc/gsm_data.h | 357 | ||||
-rw-r--r-- | include/osmocom/bsc/gsm_timers.h | 9 | ||||
-rw-r--r-- | include/osmocom/bsc/handover.h | 81 | ||||
-rw-r--r-- | include/osmocom/bsc/handover_fsm.h | 80 | ||||
-rw-r--r-- | include/osmocom/bsc/lchan_fsm.h | 89 | ||||
-rw-r--r-- | include/osmocom/bsc/lchan_select.h | 6 | ||||
-rw-r--r-- | include/osmocom/bsc/mgw_endpoint_fsm.h | 59 | ||||
-rw-r--r-- | include/osmocom/bsc/osmo_bsc.h | 14 | ||||
-rw-r--r-- | include/osmocom/bsc/osmo_bsc_lcls.h | 1 | ||||
-rw-r--r-- | include/osmocom/bsc/timeslot_fsm.h | 53 |
20 files changed, 720 insertions, 222 deletions
diff --git a/include/osmocom/bsc/Makefile.am b/include/osmocom/bsc/Makefile.am index 0f134c850..f73417eb7 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 \ @@ -24,11 +25,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 \ @@ -45,6 +50,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 2fe8c38e7..886e7d604 100644 --- a/include/osmocom/bsc/abis_rsl.h +++ b/include/osmocom/bsc/abis_rsl.h @@ -35,19 +35,18 @@ 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 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 +59,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 +69,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 +103,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); #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..156da42fa --- /dev/null +++ b/include/osmocom/bsc/assignment_fsm.h @@ -0,0 +1,44 @@ +/* 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, \ + conn->assignment.new_lchan ? gsm_lchan_name(conn->assignment.new_lchan) : "", \ + conn->assignment.new_lchan ? " " : "", \ + ## args); \ + else \ + LOGP(DMSC, level, "Assignment%s%s: " fmt, \ + conn->assignment.new_lchan ? " of " : "", \ + conn->assignment.new_lchan ? gsm_lchan_name(conn->assignment.new_lchan) : "", \ + ## args); \ + } 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_init(); + +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..bc6c67651 100644 --- a/include/osmocom/bsc/bsc_api.h +++ b/include/osmocom/bsc/bsc_api.h @@ -12,15 +12,13 @@ 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 +26,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 70fce32a4..d4de1c975 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,53 @@ 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; + +void bsc_subscr_conn_fsm_init(); /* 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_queue_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/codec_pref.h b/include/osmocom/bsc/codec_pref.h index bbda14e47..94fc987eb 100644 --- a/include/osmocom/bsc/codec_pref.h +++ b/include/osmocom/bsc/codec_pref.h @@ -1,6 +1,17 @@ #pragma once -struct gsm_bts; +#include <stdbool.h> +#include <osmocom/gsm/protocol/gsm_04_08.h> -int match_codec_pref(int *full_rate, enum gsm48_chan_mode *chan_mode, const struct gsm0808_channel_type *ct, - const struct gsm0808_speech_codec_list *scl, const struct bsc_msc_data *msc, struct gsm_bts *bts); +struct gsm0808_channel_type; +struct gsm0808_speech_codec_list; +struct gsm_audio_support; +struct bts_codec_conf; + +int 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, + const struct bts_codec_conf *bts_codec); diff --git a/include/osmocom/bsc/gsm_04_08_rr.h b/include/osmocom/bsc/gsm_04_08_rr.h index 69cb6eab5..63bec7af3 100644 --- a/include/osmocom/bsc/gsm_04_08_rr.h +++ b/include/osmocom/bsc/gsm_04_08_rr.h @@ -12,6 +12,7 @@ int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv); void gsm48_lchan2chan_desc(struct gsm48_chan_desc *cd, const struct gsm_lchan *lchan); int gsm48_multirate_config(uint8_t *lv, const struct amr_multirate_conf *mr, const struct amr_mode *modes); +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); @@ -42,4 +43,5 @@ static inline struct msgb *gsm48_msgb_alloc_name(const char *name) uint64_t str_to_imsi(const char *imsi_str); +int gsm48_sendmsg(struct msgb *msg); int gsm0408_rcvmsg(struct msgb *msg, uint8_t link_id); diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h index 272b192ce..ce563ef17 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,96 @@ 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_OUT = 0x4, + HO_INTER_BSC_IN = 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_out_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_in_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; + + uint8_t ho_ref; + struct gsm_bts *new_bts; + struct gsm_lchan *new_lchan; + bool async; + struct handover_in_req inter_bsc_in; + struct mgwep_ci *created_ci_for_msc; +}; + /* active radio connection of a mobile subscriber */ struct gsm_subscriber_connection { /* global linked list of subscriber_connections */ @@ -112,11 +206,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 +230,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 +255,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 +385,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 +461,68 @@ 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; + struct gsm_lchan *re_use_mgw_endpoint_from_lchan; + } 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 +530,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 +544,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 +563,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 +573,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; @@ -1033,9 +1146,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); @@ -1043,7 +1159,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) { @@ -1089,7 +1204,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); @@ -1159,11 +1274,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_OUT_ATTEMPTED, + BSC_CTR_INTER_BSC_HO_OUT_COMPLETED, + BSC_CTR_INTER_BSC_HO_OUT_STOPPED, + BSC_CTR_INTER_BSC_HO_OUT_TIMEOUT, + BSC_CTR_INTER_BSC_HO_OUT_ERROR, + BSC_CTR_INTER_BSC_HO_IN_ATTEMPTED, + BSC_CTR_INTER_BSC_HO_IN_COMPLETED, + BSC_CTR_INTER_BSC_HO_IN_STOPPED, + BSC_CTR_INTER_BSC_HO_IN_NO_CHANNEL, + BSC_CTR_INTER_BSC_HO_IN_FAILED, + BSC_CTR_INTER_BSC_HO_IN_TIMEOUT, + BSC_CTR_INTER_BSC_HO_IN_ERROR, BSC_CTR_PAGING_ATTEMPTED, BSC_CTR_PAGING_DETACHED, BSC_CTR_PAGING_RESPONDED, @@ -1171,11 +1307,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_OUT_ATTEMPTED] = {"interbsc_ho_out:attempted", + "Attempts to handover to remote BSS."}, + [BSC_CTR_INTER_BSC_HO_OUT_COMPLETED] = {"interbsc_ho_out:completed", + "Handover to remote BSS completed."}, + [BSC_CTR_INTER_BSC_HO_OUT_STOPPED] = {"interbsc_ho_out:stopped", "Connection ended during HO."}, + [BSC_CTR_INTER_BSC_HO_OUT_TIMEOUT] = {"interbsc_ho_out:timeout", "Handover timed out."}, + [BSC_CTR_INTER_BSC_HO_OUT_ERROR] = {"interbsc_ho_out:error", + "Handover to remote BSS failed for other reason."}, + + [BSC_CTR_INTER_BSC_HO_IN_ATTEMPTED] = {"interbsc_ho_in:attempted", + "Attempts to handover from remote BSS."}, + [BSC_CTR_INTER_BSC_HO_IN_COMPLETED] = {"interbsc_ho_in:completed", + "Handover from remote BSS completed."}, + [BSC_CTR_INTER_BSC_HO_IN_STOPPED] = {"interbsc_ho_in:stopped", "Connection ended during HO."}, + [BSC_CTR_INTER_BSC_HO_IN_NO_CHANNEL] = {"interbsc_ho_in:no_channel", + "Failure to allocate lchan for HO."}, + [BSC_CTR_INTER_BSC_HO_IN_TIMEOUT] = {"interbsc_ho_in:timeout", "Handover from remote BSS timed out."}, + [BSC_CTR_INTER_BSC_HO_IN_FAILED] = {"interbsc_ho_in:failed", "Received Handover Fail message."}, + [BSC_CTR_INTER_BSC_HO_IN_ERROR] = {"interbsc_ho_in: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."}, @@ -1272,6 +1439,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; @@ -1407,12 +1579,15 @@ 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); -bool on_gsm_ts_init(struct gsm_bts_trx_ts *ts); +int bts_count_free_ts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan); #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 aa117cc24..322913da4 100644 --- a/include/osmocom/bsc/handover.h +++ b/include/osmocom/bsc/handover.h @@ -5,72 +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 %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_lchant_name(old_lchan->type), \ - gsm48_chan_mode_name(old_lchan->tch_mode), \ - 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 @@ -90,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..4db08901d --- /dev/null +++ b/include/osmocom/bsc/handover_fsm.h @@ -0,0 +1,80 @@ +/* Handover FSM API for intra-BSC and inter-BSC Handover. */ +#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, \ + handover_status(conn), ## args); \ + else \ + LOGP(DHODEC, level, "%s: " fmt, \ + handover_status(conn), ## args); \ + } 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 Out: move away from this BSC to another one. + * Inter-BSC In: 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 Outgoing 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-BSC Outgoing 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. */ + HO_OUT_ST_WAIT_HO_COMMAND, + HO_OUT_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, + + HO_OUT_EV_BSSMAP_HO_COMMAND, +}; + +struct ho_out_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_fsm_init(); + +void handover_request(struct handover_out_req *req); +void handover_start(struct handover_out_req *req); +void handover_start_inter_bsc_in(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..49701c1d0 --- /dev/null +++ b/include/osmocom/bsc/lchan_fsm.h @@ -0,0 +1,89 @@ +/* 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, gsm_lchant_name(lchan->type), ## args); \ + else \ + LOGP(DRSL, level, "%s (not initialized) " fmt, gsm_lchan_name(lchan), ## args); \ + } 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_init(); + +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 && state == LCHAN_ST_UNUSED) + || (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..34fdcb76a --- /dev/null +++ b/include/osmocom/bsc/mgw_endpoint_fsm.h @@ -0,0 +1,59 @@ +/* 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, \ + mgw_endpoint_name(mgwep), ## args); \ + } 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/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..d02e156df --- /dev/null +++ b/include/osmocom/bsc/timeslot_fsm.h @@ -0,0 +1,53 @@ +/* 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_init(); + +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_capable_of_lchant(struct gsm_bts_trx_ts *ts, enum gsm_chan_t type); +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); |