From c10a77af68c231a026b4f0aa2faf86c584e08eb6 Mon Sep 17 00:00:00 2001 From: Neels Hofmeyr Date: Mon, 11 Jun 2018 02:40:30 +0200 Subject: code bomb: refactor just about everything for inter-bsc HO This is way too large, yet the typical tangle of modification onto modifications needs to be squashed to make sensible bits of them later for code review... Change-Id: I82e3f918295daa83274a4cf803f046979f284366 --- include/osmocom/bsc/Makefile.am | 6 + include/osmocom/bsc/abis_rsl.h | 27 ++- include/osmocom/bsc/assignment_fsm.h | 42 ++++ include/osmocom/bsc/bsc_api.h | 7 +- include/osmocom/bsc/bsc_msc_data.h | 5 - include/osmocom/bsc/bsc_subscr_conn_fsm.h | 65 +++--- include/osmocom/bsc/bsc_subscriber.h | 1 + include/osmocom/bsc/chan_alloc.h | 9 - include/osmocom/bsc/debug.h | 2 + include/osmocom/bsc/gsm_04_08_utils.h | 6 +- include/osmocom/bsc/gsm_data.h | 334 +++++++++++++++++++----------- include/osmocom/bsc/gsm_timers.h | 55 +++++ include/osmocom/bsc/handover.h | 82 +++----- include/osmocom/bsc/handover_fsm.h | 80 +++++++ include/osmocom/bsc/lchan_fsm.h | 88 ++++++++ include/osmocom/bsc/lchan_select.h | 6 + include/osmocom/bsc/mgw_endpoint_fsm.h | 57 +++++ include/osmocom/bsc/neighbor_ident.h | 5 +- include/osmocom/bsc/osmo_bsc.h | 14 +- include/osmocom/bsc/osmo_bsc_lcls.h | 1 + include/osmocom/bsc/timeslot_fsm.h | 50 +++++ 21 files changed, 700 insertions(+), 242 deletions(-) create mode 100644 include/osmocom/bsc/assignment_fsm.h create mode 100644 include/osmocom/bsc/gsm_timers.h create mode 100644 include/osmocom/bsc/handover_fsm.h create mode 100644 include/osmocom/bsc/lchan_fsm.h create mode 100644 include/osmocom/bsc/lchan_select.h create mode 100644 include/osmocom/bsc/mgw_endpoint_fsm.h create mode 100644 include/osmocom/bsc/timeslot_fsm.h (limited to 'include/osmocom') diff --git a/include/osmocom/bsc/Makefile.am b/include/osmocom/bsc/Makefile.am index 18737a390..8f3e89f86 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 \ @@ -18,15 +19,19 @@ noinst_HEADERS = \ gsm_04_08_utils.h \ gsm_04_80.h \ gsm_data.h \ + gsm_timers.h \ handover.h \ handover_cfg.h \ handover_decision.h \ handover_decision_2.h \ + handover_fsm.h \ handover_vty.h \ ipaccess.h \ + lchan_fsm.h \ meas_feed.h \ meas_rep.h \ misdn.h \ + mgw_endpoint_fsm.h \ neighbor_ident.h \ network_listen.h \ openbscdefines.h \ @@ -43,6 +48,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..b7619d33c 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_lchan *lchan); +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); #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 + +#include + +/* 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 a90268bde..01b61d556 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); @@ -29,4 +28,6 @@ int gsm0808_page(struct gsm_bts *bts, unsigned int page_group, int gsm0808_clear(struct gsm_subscriber_connection *conn); int gsm0408_rcvmsg(struct msgb *msg, uint8_t link_id); + +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 e8226f443..2317c83ed 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,34 +30,43 @@ 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 msgb; +struct mgcp_conn_peer; +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 gscon_update_id(struct gsm_subscriber_connection *conn); + +int gscon_sigtran_send(struct gsm_subscriber_connection *conn, struct msgb *msg); + +struct mgw_endpoint *gscon_ensure_mgw_endpoint(struct gsm_subscriber_connection *conn); +bool gscon_connect_mgw_to_msc(struct gsm_subscriber_connection *conn, + 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_forgetx_lchan(struct gsm_subscriber_connection *conn, struct gsm_lchan *lchan); 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 +#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_04_08_utils.h b/include/osmocom/bsc/gsm_04_08_utils.h index 4349a37cc..2c812defa 100644 --- a/include/osmocom/bsc/gsm_04_08_utils.h +++ b/include/osmocom/bsc/gsm_04_08_utils.h @@ -4,17 +4,15 @@ void gsm_net_update_ctype(struct gsm_network *network); enum gsm_chan_t get_ctype_by_chreq(struct gsm_network *network, uint8_t ra); int get_reason_by_chreq(uint8_t ra, int neci); int gsm48_send_rr_release(struct gsm_lchan *lchan); -int send_siemens_mrpci(struct gsm_lchan *lchan, - uint8_t *classmark2_lv); int gsm48_handle_paging_resp(struct gsm_subscriber_connection *conn, struct msgb *msg, struct bsc_subscr *bsub); 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); int gsm48_lchan_modify(struct gsm_lchan *lchan, uint8_t mode); int gsm48_rx_rr_modif_ack(struct msgb *msg); int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg); @@ -31,6 +29,8 @@ struct msgb *gsm48_create_loc_upd_rej(uint8_t cause); struct msgb *gsm48_create_rr_status(uint8_t cause); int gsm48_tx_rr_status(struct gsm_subscriber_connection *conn, uint8_t cause); +void gsm48_cell_desc(struct gsm48_cell_desc *cd, const struct gsm_bts *bts); + #define GSM48_ALLOC_SIZE 2048 #define GSM48_ALLOC_HEADROOM 256 diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h index 1cba22e68..a591af97a 100644 --- a/include/osmocom/bsc/gsm_data.h +++ b/include/osmocom/bsc/gsm_data.h @@ -14,13 +14,13 @@ #include #include #include +#include #include #include #include #include -#include #include #include @@ -31,11 +31,16 @@ #include #include #include +#include +#include + +#define GSM_T3122_DEFAULT 10 struct mgcp_client_conf; struct mgcp_client; struct mgcp_ctx; struct gsm0808_cell_id; +struct mgw_endpoint; /** annotations for msgb ownership */ #define __uses @@ -44,6 +49,7 @@ struct gsm0808_cell_id; struct bsc_subscr; struct gprs_ra_id; +struct handover; #define OBSC_LINKID_CB(__msgb) (__msgb)->cb[3] @@ -93,6 +99,89 @@ enum subscr_sccp_state { SUBSCR_SCCP_ST_CONNECTED }; +struct assignment_request { + bool aoip; + + 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; +}; + +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; + struct gsm0808_cell_id cell_id_target; +}; + + +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; + } mt; +}; + /* active radio connection of a mobile subscriber */ struct gsm_subscriber_connection { /* global linked list of subscriber_connections */ @@ -110,18 +199,17 @@ 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; /* Cache DTAP messages during handover/assignment (msgb_enqueue()/msgb_dequeue())*/ - struct llist_head ho_dtap_cache; - unsigned int ho_dtap_cache_len; + struct llist_head gscon_dtap_cache; + unsigned int gscon_dtap_cache_len; struct { int failures; @@ -135,7 +223,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; @@ -164,21 +251,12 @@ struct gsm_subscriber_connection { 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; - - /* FSM instance to control the BTS sided RTP connection */ - struct osmo_fsm_inst *fi_bts; - /* FSM instance to control the MSC sided RTP connection */ - struct osmo_fsm_inst *fi_msc; + char msc_assigned_rtp_addr[INET_ADDRSTRLEN]; + uint16_t msc_assigned_rtp_port; - /* Endpoint identifier of the MGCP endpoint the connection uses */ - char *mgw_endpoint; + struct mgw_endpoint *mgw_endpoint; + struct mgwep_ci *mgw_endpoint_ci_msc; /* Channel rate flag, FR=1, HR=0, Invalid=-1 */ int full_rate; @@ -296,18 +374,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 @@ -384,11 +450,65 @@ 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_ACTIVE is reached */ + bool requires_voice_stream; + bool mgw_endpoint_available; + 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; + uint8_t error_cause; + /* The logical channel type */ enum gsm_chan_t type; /* RSL channel mode */ @@ -396,9 +516,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; @@ -413,15 +530,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 */ @@ -433,15 +549,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]; @@ -453,43 +560,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; @@ -1009,9 +1111,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); @@ -1028,9 +1133,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); @@ -1038,7 +1146,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) { @@ -1084,7 +1191,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); @@ -1241,23 +1348,6 @@ static const struct rate_ctr_group_desc bsc_ctrg_desc = { bsc_ctr_description, }; -#define GSM_T3101_DEFAULT 3 /* s */ -#define GSM_T3103_DEFAULT 5 /* s */ -#define GSM_T3105_DEFAULT 100 /* ms */ -#define GSM_T3107_DEFAULT 5 /* s */ -#define GSM_T3109_DEFAULT 5 /* s, must be 2s + radio_link_timeout*0.48 */ -#define GSM_T3111_DEFAULT 2 /* s */ -#define GSM_T3113_DEFAULT 10 /* s */ -#define GSM_T3115_DEFAULT 10 -#define GSM_T3117_DEFAULT 10 -#define GSM_T3119_DEFAULT 10 -#define GSM_T3122_DEFAULT 10 -#define GSM_T3141_DEFAULT 10 -#define GSM_T10_DEFAULT 6 /* RR Assignment timeout, in seconds */ -#define GSM_T7_DEFAULT 10 /* inter-BSC MO Handover first timeout, in seconds */ -#define GSM_T8_DEFAULT 10 /* inter-BSC MO Handover second timeout, in seconds */ -#define GSM_T101_DEFAULT 10 /* inter-BSC MT Handover timeout, in seconds */ - struct gsm_tz { int override; /* if 0, use system's time zone instead. */ int hr; /* hour */ @@ -1289,23 +1379,8 @@ struct gsm_network { unsigned int num_bts; struct llist_head bts_list; - /* timer values */ - int T3101; - int T3103; /*< Handover timeout */ - int T3105; - int T3107; - int T3109; - int T3111; - int T3113; - int T3115; - int T3117; - int T3119; - int T3122; - int T3141; - int T10; /*< RR Assignment timeout, in seconds */ - int T7; /*< inter-BSC handover MO timeout from Handover Required to Handover Command */ - int T8; /*< inter-BSC handover MO timeout from Handover Command to final Clear*/ - int T101; /*< inter-BSC handover MT timeout from Handover Request to Handover Accept */ + /* shall reference gsm_network_T[] */ + struct T_def *T_defs; enum gsm_chan_t ctype_by_chreq[_NUM_CHREQ_T]; @@ -1339,9 +1414,6 @@ struct gsm_network { * pointer is NULL to indicate absence of a bsc_subscribers list. */ struct llist_head *bsc_subscribers; - /* Periodic location update default value */ - uint8_t t3212; - /* Timer for periodic channel load measurements to maintain each BTS's T3122. */ struct osmo_timer_list t3122_chan_load_timer; @@ -1354,6 +1426,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; @@ -1489,12 +1566,25 @@ 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); #endif /* _GSM_DATA_H */ diff --git a/include/osmocom/bsc/gsm_timers.h b/include/osmocom/bsc/gsm_timers.h new file mode 100644 index 000000000..fde8c93c0 --- /dev/null +++ b/include/osmocom/bsc/gsm_timers.h @@ -0,0 +1,55 @@ +/* API to define Tnnn timers globally, configure in VTY and use for FSM state changes. */ +#pragma once + +#include +#include + +struct osmo_fsm_inst; +struct vty; + +enum T_unit { + T_S = 0, /*< most T are in seconds, keep 0 as default. */ + T_MS, /*< milliseconds */ + T_M, /*< minutes */ + T_CUSTOM, +}; + +extern const struct value_string T_unit_names[]; +static inline const char *T_unit_name(enum T_unit val) +{ return get_value_string(T_unit_names, val); } + +/* Define a GSM timer of the form Tnnn, with unit, default value and doc string. */ +struct T_def { + const int T; /*< T1234 number */ + const int default_val; /*< timeout duration (according to unit), default value. */ + const enum T_unit unit; + const char *desc; + int val; /*< currently active value, e.g. set by user config. */ +}; + +/* Iterate an array of struct T_def, the last item should be fully zero, i.e. "{}" */ +#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); +void T_defs_reset(struct T_def *T_defs); +struct T_def *T_def_get_entry(struct T_def *T_defs, int T); + +void T_defs_vty_init(struct T_def *T_defs, int cfg_parent_node); +void T_defs_vty_write(struct vty *vty, const char *indent); + + +struct state_timeout { + int T; + bool keep_timer; +}; + +struct state_timeout *get_state_timeout(uint32_t state, 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 char *file, int line); diff --git a/include/osmocom/bsc/handover.h b/include/osmocom/bsc/handover.h index eb03f6a7b..3e8da3bbc 100644 --- a/include/osmocom/bsc/handover.h +++ b/include/osmocom/bsc/handover.h @@ -5,69 +5,40 @@ #include #include #include +#include +#include +#include + +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, -}; -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; + +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, }; -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); +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_ho_count(struct gsm_bts *bts, bool inter_cell); +int 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 @@ -87,9 +58,22 @@ 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_send_handover_required(struct gsm_lchan *lchan, + const struct gsm0808_handover_required *params); +int bsc_send_handover_request_ack(struct gsm_subscriber_connection *conn, struct msgb *rr_ho_command); + +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..f2ce732e9 --- /dev/null +++ b/include/osmocom/bsc/handover_fsm.h @@ -0,0 +1,80 @@ +/* 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 +#include + +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_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..8734e57f9 --- /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 + +#define LOG_ADD_NEWLINE(fmt) ((!fmt || !*fmt || fmt[strlen(fmt)-1] != '\n') ? "\n" : "") + +/* 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, + 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_ACTIVE, + 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 */ + 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; + 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_forgetx_conn(struct gsm_lchan *lchan); +void lchan_forgetx_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..54ffe07d0 --- /dev/null +++ b/include/osmocom/bsc/mgw_endpoint_fsm.h @@ -0,0 +1,57 @@ +/* osmo-bsc API to manage all sides of an MGW endpoint */ +#pragma once + +#include + +#include + +/* 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); 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 81b43e2c7..8c73bb042 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 -#include "bsc_api.h" -#include "bsc_msg_filter.h" +#include +#include #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(); @@ -40,5 +42,3 @@ void bsc_gen_location_state_trap(struct gsm_bts *bts); struct llist_head *bsc_access_lists(void); struct bsc_subscr *extract_sub(struct gsm_subscriber_connection *conn, struct msgb *msg); - -#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 + +/* 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); -- cgit v1.2.3