diff options
Diffstat (limited to 'src/pcu_l1_if.cpp')
-rw-r--r-- | src/pcu_l1_if.cpp | 563 |
1 files changed, 460 insertions, 103 deletions
diff --git a/src/pcu_l1_if.cpp b/src/pcu_l1_if.cpp index 0ccf642e..fb44bd83 100644 --- a/src/pcu_l1_if.cpp +++ b/src/pcu_l1_if.cpp @@ -11,10 +11,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include <stdio.h> @@ -44,6 +40,10 @@ extern "C" { #include <osmocom/gsm/protocol/gsm_04_08.h> #include <osmocom/gsm/gsm48_rest_octets.h> #include <osmocom/gsm/sysinfo.h> +#include <osmocom/gsm/gsm0502.h> + +#include <nacc_fsm.h> +#include <pcu_l1_if_phy.h> } #include <gprs_rlcmac.h> @@ -55,46 +55,32 @@ extern "C" { #include <pdch.h> #include <tbf_ul.h> #include <tbf_dl.h> -#include <gprs_ms_storage.h> - -// FIXME: move this, when changed from c++ to c. -extern "C" { -void *l1if_open_pdch(uint8_t trx_no, uint32_t hlayer1, - struct gsmtap_inst *gsmtap); -int l1if_connect_pdch(void *obj, uint8_t ts); -int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn, - uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len); -} +#include <gprs_ms.h> extern void *tall_pcu_ctx; -#define PAGING_GROUP_LEN 3 +struct e1_ccu_conn_pars { + struct llist_head entry; -/* returns [0,999] on success, > 999 on error */ -uint16_t imsi2paging_group(const char* imsi) -{ - uint16_t pgroup = 0; - size_t len; + /* Related air interface */ + uint8_t bts_nr; + uint8_t trx_nr; + uint8_t ts_nr; - len = (imsi != NULL) ? strlen(imsi) : 0; - if (len < PAGING_GROUP_LEN) - return 0xFFFF; - imsi += len - PAGING_GROUP_LEN; + /* E1 communication parameter */ + struct e1_conn_pars e1_conn_pars; +}; - while (*imsi != '\0') { - if (!isdigit(*imsi)) - return 0xFFFF; - pgroup *= 10; - pgroup += *imsi - '0'; - imsi++; - } - return pgroup; -} +/* List storage to collect E1 connection information that we receive through the pcu_sock. The collected data serves as + * a lookup table so that we can lookup the E1 connection information for each PDCH (trx number and timeslot number) + * when it is needed. */ +static LLIST_HEAD(e1_ccu_table); /* * PCU messages */ +/* Can be used to allocate message with non-variable size */ struct msgb *pcu_msgb_alloc(uint8_t msg_type, uint8_t bts_nr) { struct msgb *msg; @@ -111,6 +97,21 @@ struct msgb *pcu_msgb_alloc(uint8_t msg_type, uint8_t bts_nr) return msg; } +/* Allocate message with extra size, only reserve pcuif msg hdr */ +static struct msgb *pcu_msgb_alloc_ext_size(uint8_t msg_type, uint8_t bts_nr, size_t extra_size) +{ + struct msgb *msg; + struct gsm_pcu_if *pcu_prim; + msg = msgb_alloc(sizeof(struct gsm_pcu_if) + extra_size, "pcu_sock_tx"); + /* Only header is filled, caller is responible for reserving + filling + * message type specific contents: */ + msgb_put(msg, PCUIF_HDR_SIZE); + pcu_prim = (struct gsm_pcu_if *) msgb_data(msg); + pcu_prim->msg_type = msg_type; + pcu_prim->bts_nr = bts_nr; + return msg; +} + const struct value_string gsm_pcu_if_text_type_names[] = { OSMO_VALUE_STRING(PCU_VERSION), OSMO_VALUE_STRING(PCU_OML_ALERT), @@ -194,7 +195,8 @@ static int pcu_tx_data_req(struct gprs_rlcmac_bts *bts, uint8_t trx, uint8_t ts, data_req->trx_nr = trx; data_req->ts_nr = ts; data_req->block_nr = block_nr; - memcpy(data_req->data, data, len); + if (len) + memcpy(data_req->data, data, len); data_req->len = len; return pcu_sock_send(msg); @@ -205,12 +207,20 @@ void pcu_l1if_tx_pdtch(msgb *msg, struct gprs_rlcmac_bts *bts, uint8_t trx, uint { #ifdef ENABLE_DIRECT_PHY if (bts->trx[trx].fl1h) { + if (!msg) /* Simply skip sending idle frames to L1 */ + return; l1if_pdch_req(bts->trx[trx].fl1h, ts, 0, fn, arfcn, block_nr, msg->data, msg->len); msgb_free(msg); return; } #endif + if (!msg) { + pcu_tx_data_req(bts, trx, ts, PCU_IF_SAPI_PDTCH, arfcn, fn, block_nr, + NULL, 0); + return; + } + pcu_tx_data_req(bts, trx, ts, PCU_IF_SAPI_PDTCH, arfcn, fn, block_nr, msg->data, msg->len); msgb_free(msg); @@ -221,54 +231,94 @@ void pcu_l1if_tx_ptcch(struct gprs_rlcmac_bts *bts, uint32_t fn, uint8_t block_nr, uint8_t *data, size_t data_len) { - if (the_pcu->gsmtap_categ_mask & (1 << PCU_GSMTAP_C_DL_PTCCH)) + if (data_len && the_pcu->gsmtap_categ_mask & (1 << PCU_GSMTAP_C_DL_PTCCH)) gsmtap_send(the_pcu->gsmtap, arfcn, ts, GSMTAP_CHANNEL_PTCCH, 0, fn, 0, 0, data, data_len); #ifdef ENABLE_DIRECT_PHY if (bts->trx[trx].fl1h) { + if (!data_len) /* Simply skip sending idle frames to L1 */ + return; l1if_pdch_req(bts->trx[trx].fl1h, ts, 1, fn, arfcn, block_nr, data, data_len); return; } #endif + if (!data_len) { + pcu_tx_data_req(bts, trx, ts, PCU_IF_SAPI_PTCCH, arfcn, fn, block_nr, NULL, 0); + return; + } + pcu_tx_data_req(bts, trx, ts, PCU_IF_SAPI_PTCCH, arfcn, fn, block_nr, data, data_len); } -void pcu_l1if_tx_agch(struct gprs_rlcmac_bts *bts, bitvec * block, int plen) +/* Send a MAC block via the access grant channel. This will (obviously) only work for MAC blocks that contain + * an IMMEDIATE ASSIGNMENT. In case the confirm flag is set, the receiving end is required to send a confirmation + * back when the IMMEDIATE ASSIGNMENT has been sent. */ +void pcu_l1if_tx_agch2(struct gprs_rlcmac_bts *bts, bitvec *block, int plen, bool confirm, uint32_t msg_id) { - uint8_t data[GSM_MACBLOCK_LEN]; /* prefix PLEN */ + struct gsm_pcu_if_agch agch = { 0 }; - /* FIXME: why does OpenBTS has no PLEN and no fill in message? */ - bitvec_pack(block, data + 1); - data[0] = (plen << 2) | 0x01; + agch.confirm = confirm; + agch.msg_id = msg_id; + agch.data[0] = (plen << 2) | 0x01; + bitvec_pack(block, agch.data + 1); if (the_pcu->gsmtap_categ_mask & (1 << PCU_GSMTAP_C_DL_AGCH)) - gsmtap_send(the_pcu->gsmtap, 0, 0, GSMTAP_CHANNEL_AGCH, 0, 0, 0, 0, data, GSM_MACBLOCK_LEN); + gsmtap_send(the_pcu->gsmtap, 0, 0, GSMTAP_CHANNEL_AGCH, 0, 0, 0, 0, agch.data, GSM_MACBLOCK_LEN); - pcu_tx_data_req(bts, 0, 0, PCU_IF_SAPI_AGCH, 0, 0, 0, data, GSM_MACBLOCK_LEN); + pcu_tx_data_req(bts, 0, 0, PCU_IF_SAPI_AGCH_2, 0, 0, 0, (uint8_t*)&agch, sizeof(agch)); } -void pcu_l1if_tx_pch(struct gprs_rlcmac_bts *bts, bitvec * block, int plen, uint16_t pgroup) +/* Send a MAC block via the paging channel. This will (obviously) only work for MAC blocks that contain an + * IMMEDIATE ASSIGNMENT or a PAGING COMMAND message. In case the MAC block contains an IMMEDIATE ASSIGNMENT + * message, the receiving end is required to confirm when the IMMEDIATE ASSIGNMENT has been sent. */ +void pcu_l1if_tx_pch2(struct gprs_rlcmac_bts *bts, struct bitvec *block, int plen, bool confirm, + const char *imsi, uint32_t msg_id) { - uint8_t data[PAGING_GROUP_LEN + GSM_MACBLOCK_LEN]; - int i; - - /* prepend paging group */ - for (i = 0; i < PAGING_GROUP_LEN; i++) { - data[PAGING_GROUP_LEN - 1 - i] = '0' + (char)(pgroup % 10); - pgroup = pgroup / 10; - } - OSMO_ASSERT(pgroup == 0); - - /* block provided by upper layer comes without first byte (plen), - * prepend it manually: + struct gsm_pcu_if_pch pch = { 0 }; + + pch.msg_id = msg_id; + if (imsi) + OSMO_STRLCPY_ARRAY(pch.imsi, imsi); + /* OS#6097: if strlen(pch.imsi) == 0: We assume the MS is in non-DRX + * mode (TS 44.060 5.5.1.5) and hence it is listening on all CCCH blocks + * (TS 45.002 6.5.3, 6.5.6). */ - OSMO_ASSERT(sizeof(data) >= PAGING_GROUP_LEN + 1 + block->data_len); - data[3] = (plen << 2) | 0x01; - bitvec_pack(block, data + PAGING_GROUP_LEN + 1); + + pch.confirm = confirm; + pch.data[0] = (plen << 2) | 0x01; + bitvec_pack(block, pch.data + 1); if (the_pcu->gsmtap_categ_mask & (1 << PCU_GSMTAP_C_DL_PCH)) - gsmtap_send(the_pcu->gsmtap, 0, 0, GSMTAP_CHANNEL_PCH, 0, 0, 0, 0, data + 3, GSM_MACBLOCK_LEN); + gsmtap_send(the_pcu->gsmtap, 0, 0, GSMTAP_CHANNEL_PCH, 0, 0, 0, 0, + pch.data, GSM_MACBLOCK_LEN); - pcu_tx_data_req(bts, 0, 0, PCU_IF_SAPI_PCH, 0, 0, 0, data, PAGING_GROUP_LEN + GSM_MACBLOCK_LEN); + pcu_tx_data_req(bts, 0, 0, PCU_IF_SAPI_PCH_2, 0, 0, 0, (uint8_t*)&pch, sizeof(pch)); +} + +int pcu_tx_neigh_addr_res_req(struct gprs_rlcmac_bts *bts, const struct neigh_cache_entry_key *neigh_key) +{ + struct msgb *msg; + struct gsm_pcu_if *pcu_prim; + struct gsm_pcu_if_neigh_addr_req *naddr_req; + + LOGP(DL1IF, LOGL_DEBUG, "(bts=%u) Tx Neighbor Address Resolution Request: " NEIGH_CACHE_ENTRY_KEY_FMT "\n", + bts->nr, NEIGH_CACHE_ENTRY_KEY_ARGS(neigh_key)); + + msg = pcu_msgb_alloc_ext_size(PCU_IF_MSG_CONTAINER, bts->nr, sizeof(struct gsm_pcu_if_neigh_addr_req)); + if (!msg) + return -ENOMEM; + pcu_prim = (struct gsm_pcu_if *) msgb_data(msg); + naddr_req = (struct gsm_pcu_if_neigh_addr_req *)&pcu_prim->u.container.data[0]; + + msgb_put(msg, sizeof(pcu_prim->u.container) + sizeof(struct gsm_pcu_if_neigh_addr_req)); + pcu_prim->u.container.msg_type = PCU_IF_MSG_NEIGH_ADDR_REQ; + osmo_store16be(sizeof(struct gsm_pcu_if_neigh_addr_req), &pcu_prim->u.container.length); + + osmo_store16be(neigh_key->local_lac, &naddr_req->local_lac); + osmo_store16be(neigh_key->local_ci, &naddr_req->local_ci); + osmo_store16be(neigh_key->tgt_arfcn, &naddr_req->tgt_arfcn); + naddr_req->tgt_bsic = neigh_key->tgt_bsic; + + return pcu_sock_send(msg); } void pcu_rx_block_time(struct gprs_rlcmac_bts *bts, uint16_t arfcn, uint32_t fn, uint8_t ts_no) @@ -281,14 +331,41 @@ int pcu_rx_data_ind_pdtch(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_pdch * { int rc; + /* First of all, update TDMA clock: */ + bts_set_current_frame_number(bts, fn); + + if (!pdch->is_enabled()) { + LOGPDCH(pdch, DL1IF, LOGL_INFO, "Received DATA.ind (PDTCH) on disabled TS\n"); + return -EINVAL; + } + rc = pdch->rcv_block(data, len, fn, meas); pdch_ulc_expire_fn(pdch->ulc, fn); return rc; } +static int list_arfcn(const struct gprs_rlcmac_bts *bts, const struct gsm_sysinfo_freq *freq, const char *text) +{ + int n = 0, i; + for (i = 0; i < 1024; i++) { + if (freq[i].mask) { + if (!n) + LOGP(DL1IF, LOGL_INFO, "BTS%d: %s", bts->nr, text); + LOGPC(DL1IF, LOGL_INFO, " %d", i); + n++; + } + } + if (n) + LOGPC(DL1IF, LOGL_INFO, "\n"); + + return n; +} + static int pcu_rx_data_ind_bcch(struct gprs_rlcmac_bts *bts, uint8_t *data, uint8_t len) { + struct gsm48_system_information_type_2 *si2; const uint8_t *si_ro; + switch (len) { case 0: /* Due to historical reasons also accept a completely empty message as @@ -305,6 +382,9 @@ static int pcu_rx_data_ind_bcch(struct gprs_rlcmac_bts *bts, uint8_t *data, uint case SYSINFO_TYPE_1: bts->si1_is_set = false; break; + case SYSINFO_TYPE_2: + bts->si2_is_set = false; + break; case SYSINFO_TYPE_3: bts->si3_is_set = false; break; @@ -328,6 +408,14 @@ static int pcu_rx_data_ind_bcch(struct gprs_rlcmac_bts *bts, uint8_t *data, uint memcpy(bts->si1, data, GSM_MACBLOCK_LEN); bts->si1_is_set = true; break; + case GSM48_MT_RR_SYSINFO_2: + memcpy(bts->si2, data, GSM_MACBLOCK_LEN); + bts->si2_is_set = true; + si2 = (struct gsm48_system_information_type_2 *)bts->si2; + gsm48_decode_freq_list(bts->si2_bcch_cell_list, si2->bcch_frequency_list, + sizeof(si2->bcch_frequency_list), 0xce, 1); + list_arfcn(bts, bts->si2_bcch_cell_list, "SI2 Neighbour cells in same band:"); + break; case GSM48_MT_RR_SYSINFO_3: memcpy(bts->si3, data, GSM_MACBLOCK_LEN); bts->si3_is_set = true; @@ -336,8 +424,15 @@ static int pcu_rx_data_ind_bcch(struct gprs_rlcmac_bts *bts, uint8_t *data, uint memcpy(bts->si13, data, GSM_MACBLOCK_LEN); bts->si13_is_set = true; si_ro = ((struct gsm48_system_information_type_13*)data)->rest_octets; - if (osmo_gsm48_rest_octets_si13_decode(&bts->si31_ro_decoded, si_ro) < 0) + if (osmo_gsm48_rest_octets_si13_decode(&bts->si13_ro_decoded, si_ro) < 0) LOGP(DPCU, LOGL_ERROR, "Error decoding SI13\n"); + /* Update our cached timers from it: */ + osmo_tdef_set(bts->T_defs_bts, 3168, bts->si13_ro_decoded.cell_opts.t3168, OSMO_TDEF_MS); + osmo_tdef_set(bts->T_defs_bts, 3192, bts->si13_ro_decoded.cell_opts.t3192, OSMO_TDEF_MS); + /* Some sanity checks: */ + if (bts->si13_ro_decoded.cell_opts.t3192 >= + osmo_tdef_get(bts->T_defs_bts, 3193, OSMO_TDEF_MS, -1)) + LOGP(DL1IF, LOGL_ERROR, "Timers incorrectly configured! T3192 >= T3193\n"); break; default: LOGP(DL1IF, LOGL_ERROR, @@ -408,22 +503,19 @@ static int pcu_rx_data_ind(struct gprs_rlcmac_bts *bts, struct gsm_pcu_if_data * return rc; } -static int pcu_rx_data_cnf(struct gprs_rlcmac_bts *bts, struct gsm_pcu_if_data *data_cnf) +static int pcu_rx_data_cnf2(struct gprs_rlcmac_bts *bts, struct gsm_pcu_if_data_cnf *data_cnf) { int rc = 0; - int current_fn = bts_current_frame_number(bts); - LOGP(DL1IF, LOGL_DEBUG, "Data confirm received: sapi=%d fn=%d cur_fn=%d\n", - data_cnf->sapi, data_cnf->fn, current_fn); + LOGP(DL1IF, LOGL_DEBUG, "Data confirm received: sapi=%d\n", data_cnf->sapi); switch (data_cnf->sapi) { - case PCU_IF_SAPI_PCH: - if (data_cnf->data[2] == 0x3f) - bts_rcv_imm_ass_cnf(bts, data_cnf->data, data_cnf->fn); + case PCU_IF_SAPI_PCH_2: + case PCU_IF_SAPI_AGCH_2: + bts_rcv_imm_ass_cnf(bts, NULL, data_cnf->msg_id); break; default: - LOGP(DL1IF, LOGL_ERROR, "Received PCU data confirm with " - "unsupported sapi %d\n", data_cnf->sapi); + LOGP(DL1IF, LOGL_ERROR, "Received PCU data confirm with unsupported sapi %d\n", data_cnf->sapi); rc = -EINVAL; } @@ -448,9 +540,27 @@ int pcu_rx_rts_req_ptcch(struct gprs_rlcmac_bts *bts, uint8_t trx, uint8_t ts, /* Make sure PDCH time-slot is enabled */ pdch = &bts->trx[trx].pdch[ts]; - if (!pdch->m_is_enabled) + if (!pdch_is_enabled(pdch)) return -EAGAIN; + /* If there's no TBF attached to this PDCH, we can skip Tx of PTCCH + * since there's nothing worthy of being transmitted. This way BTS can + * identify idle blocks and send nothing or dumy blocks with reduced + * energy for the sake of energy saving. + */ + const unsigned num_tbfs = pdch->num_tbfs(GPRS_RLCMAC_DL_TBF) + + pdch->num_tbfs(GPRS_RLCMAC_UL_TBF); + bool skip_idle = (num_tbfs == 0); + + if (bts->gen_idle_blocks_C0) + skip_idle = skip_idle && trx != 0; + + if (skip_idle) { + pcu_l1if_tx_ptcch(bts, trx, ts, bts->trx[trx].arfcn, fn, block_nr, + NULL, 0); + return 0; + } + pcu_l1if_tx_ptcch(bts, trx, ts, bts->trx[trx].arfcn, fn, block_nr, pdch->ptcch_msg, GSM_MACBLOCK_LEN); return 0; @@ -486,7 +596,7 @@ static int pcu_rx_rts_req(struct gprs_rlcmac_bts *bts, struct gsm_pcu_if_rts_req return rc; } -/* C -> C++ adapter for direct DSP access code (e.g. osmo-bts-sysmo) */ +/* C -> C++ adapter for direct PHY access code (e.g. osmo-bts-sysmo) */ extern "C" int pcu_rx_rach_ind_ptcch(struct gprs_rlcmac_bts *bts, uint8_t trx_nr, uint8_t ts_nr, uint32_t fn, int16_t qta) { struct rach_ind_params rip = { @@ -496,7 +606,8 @@ extern "C" int pcu_rx_rach_ind_ptcch(struct gprs_rlcmac_bts *bts, uint8_t trx_nr .ra = 0x00, .trx_nr = trx_nr, .ts_nr = ts_nr, - .rfn = fn, + .rfn = fn2rfn(fn), + .fn = fn, .qta = qta, }; @@ -506,11 +617,28 @@ extern "C" int pcu_rx_rach_ind_ptcch(struct gprs_rlcmac_bts *bts, uint8_t trx_nr static int pcu_rx_rach_ind(struct gprs_rlcmac_bts *bts, const struct gsm_pcu_if_rach_ind *rach_ind) { int rc = 0; - int current_fn = bts_current_frame_number(bts); + uint32_t current_fn = bts_current_frame_number(bts); + uint16_t rfn; + + /* Note: If a BSC is sending a RACH req to us, it is actually forwarding it to + * us from BTS as a result of receiving an RFN (Fn % 42432) over RSL + * (see 3GPP TS 48.058, section 9.3.19). + * If a BTS is sending a RACH req to us, it may contain a full FN + * (current osmo-bts does that) instead of an RFN. + * For consistency, and taking into account the BSC case limitations, + * work always with RFNs here: + */ + rfn = fn2rfn(rach_ind->fn); + + LOGP(DL1IF, LOGL_INFO, + "RACH request received: sapi=%d qta=%d, ra=0x%02x, fn=%u (rfn=%u), cur_fn=%d, is_11bit=%d\n", + rach_ind->sapi, rach_ind->qta, rach_ind->ra, rach_ind->fn, rfn, current_fn, rach_ind->is_11bit); - LOGP(DL1IF, LOGL_INFO, "RACH request received: sapi=%d " - "qta=%d, ra=0x%02x, fn=%u, cur_fn=%d, is_11bit=%d\n", rach_ind->sapi, rach_ind->qta, - rach_ind->ra, rach_ind->fn, current_fn, rach_ind->is_11bit); + if (OSMO_UNLIKELY(rach_ind->fn > GSM_TDMA_HYPERFRAME - 1)) { + LOGP(DL1IF, LOGL_ERROR, "RACH request contains fn=%u that exceeds valid limits (0-%u) -- ignored!\n", + rach_ind->fn, GSM_TDMA_HYPERFRAME - 1); + return -EINVAL; + } struct rach_ind_params rip = { .burst_type = (enum ph_burst_type) rach_ind->burst_type, @@ -518,7 +646,8 @@ static int pcu_rx_rach_ind(struct gprs_rlcmac_bts *bts, const struct gsm_pcu_if_ .ra = rach_ind->ra, .trx_nr = rach_ind->trx_nr, .ts_nr = rach_ind->ts_nr, - .rfn = rach_ind->fn, + .rfn = rfn, + .fn = bts_rfn_to_fn(bts, rfn), .qta = rach_ind->qta, }; @@ -594,6 +723,38 @@ static int pcu_info_ind_ns(struct gprs_rlcmac_bts *bts, return gprs_ns_update_config(bts, info_ind->nsei, local, remote, nsvci, valid); } +const struct value_string gsm_pcuif_bts_model_names[] = { + { PCU_IF_BTS_MODEL_UNSPEC, "(unspecified)" }, + { PCU_IF_BTS_MODEL_LC15, "osmo-bts-lc15" }, + { PCU_IF_BTS_MODEL_OC2G, "osmo-bts-oc2g" }, + { PCU_IF_BTS_MODEL_OCTPHY, "osmo-bts-octphy" }, + { PCU_IF_BTS_MODEL_SYSMO, "osmo-bts-sysmo" }, + { PCU_IF_BTS_MODEL_TRX, "osmo-bts-trx" }, + { PCU_IF_BTS_MODEL_RBS, "ericsson-rbs" }, + { 0, NULL } +}; + +static bool decide_gen_idle_blocks(struct gprs_rlcmac_bts *bts) +{ + switch (bts->bts_model) { + case PCU_IF_BTS_MODEL_UNSPEC: + case PCU_IF_BTS_MODEL_LC15: + case PCU_IF_BTS_MODEL_OC2G: + case PCU_IF_BTS_MODEL_OCTPHY: + case PCU_IF_BTS_MODEL_SYSMO: + case PCU_IF_BTS_MODEL_RBS: + /* The BTS models above do not generate dummy blocks by themselves, so OsmoPCU must fill the idle gaps in the + * stream of generated PDCH blocks with dummy blocks. */ + return true; + case PCU_IF_BTS_MODEL_TRX: + /* The BTS models above generate dummy blocks by themselves, so OsmoBTS will only generate PDCH bloks that + * actually contain data. On idle, no blocks are generated. */ + return false; + default: + return false; + } +} + static int pcu_rx_info_ind(struct gprs_rlcmac_bts *bts, const struct gsm_pcu_if_info_ind *info_ind) { struct gprs_bssgp_pcu *pcu; @@ -601,14 +762,18 @@ static int pcu_rx_info_ind(struct gprs_rlcmac_bts *bts, const struct gsm_pcu_if_ unsigned int trx_nr, ts_nr; unsigned int i; + if (llist_count(&the_pcu->bts_list) > 1) + LOGP(DL1IF, LOGL_ERROR, "more than one BTS regsitered at this PCU. This PCU has only been tested with one BTS! OS#5930\n"); + + LOGP(DL1IF, LOGL_DEBUG, "Info indication received:\n"); + if (info_ind->version != PCU_IF_VERSION) { - fprintf(stderr, "PCU interface version number of BTS (%u) is " - "different (%u).\nPlease re-compile!\n", + fprintf(stderr, "PCU interface version number of BTS/BSC (%u) is different (%u).\nPlease use a BTS/BSC with a compatble interface!\n", info_ind->version, PCU_IF_VERSION); exit(-1); } - LOGP(DL1IF, LOGL_DEBUG, "Info indication received:\n"); + the_pcu->pcu_if_version = info_ind->version; if (!(info_ind->flags & PCU_IF_FLAG_ACTIVE)) { LOGP(DL1IF, LOGL_NOTICE, "BTS not available\n"); @@ -620,7 +785,8 @@ bssgp_failed: for (trx_nr = 0; trx_nr < ARRAY_SIZE(bts->trx); trx_nr++) { bts->trx[trx_nr].arfcn = info_ind->trx[trx_nr].arfcn; for (ts_nr = 0; ts_nr < ARRAY_SIZE(bts->trx[0].pdch); ts_nr++) - bts->trx[trx_nr].pdch[ts_nr].free_resources(); + if (bts->trx[trx_nr].pdch[ts_nr].is_enabled()) + bts->trx[trx_nr].pdch[ts_nr].disable(); } gprs_bssgp_destroy(bts); exit(0); @@ -726,23 +892,23 @@ bssgp_failed: for (trx_nr = 0; trx_nr < ARRAY_SIZE(bts->trx); trx_nr++) { bts->trx[trx_nr].arfcn = info_ind->trx[trx_nr].arfcn; - if ((info_ind->flags & PCU_IF_FLAG_SYSMO) + if ((info_ind->flags & PCU_IF_FLAG_DIRECT_PHY) && info_ind->trx[trx_nr].hlayer1) { #ifdef ENABLE_DIRECT_PHY LOGP(DL1IF, LOGL_DEBUG, " TRX %d hlayer1=%x\n", trx_nr, info_ind->trx[trx_nr].hlayer1); if (!bts->trx[trx_nr].fl1h) - bts->trx[trx_nr].fl1h = l1if_open_pdch( - trx_nr, + bts->trx[trx_nr].fl1h = l1if_open_trx( + bts->nr, trx_nr, info_ind->trx[trx_nr].hlayer1, the_pcu->gsmtap); if (!bts->trx[trx_nr].fl1h) { LOGP(DL1IF, LOGL_FATAL, "Failed to open direct " - "DSP access for PDCH.\n"); + "PHY access for PDCH.\n"); exit(0); } #else - LOGP(DL1IF, LOGL_FATAL, "Compiled without direct DSP " + LOGP(DL1IF, LOGL_FATAL, "Compiled without direct PHY " "access for PDCH, but enabled at " "BTS. Please deactivate it!\n"); exit(0); @@ -750,14 +916,14 @@ bssgp_failed: } for (ts_nr = 0; ts_nr < ARRAY_SIZE(bts->trx[0].pdch); ts_nr++) { - const struct gsm_pcu_if_info_ts *its = &info_ind->trx[trx_nr].ts[ts_nr]; + const struct gsm_pcu_if_info_trx_ts *its = &info_ind->trx[trx_nr].ts[ts_nr]; struct gprs_rlcmac_pdch *pdch = &bts->trx[trx_nr].pdch[ts_nr]; if ((info_ind->trx[trx_nr].pdch_mask & (1 << ts_nr))) { /* FIXME: activate dynamically at RLCMAC */ if (!pdch->is_enabled()) { #ifdef ENABLE_DIRECT_PHY if ((info_ind->flags & - PCU_IF_FLAG_SYSMO)) + PCU_IF_FLAG_DIRECT_PHY)) l1if_connect_pdch( bts->trx[trx_nr].fl1h, ts_nr); #endif @@ -768,7 +934,7 @@ bssgp_failed: pdch->tsc = its->tsc; /* (Optional) frequency hopping parameters */ - if (its->h) { + if (its->hopping) { pdch->fh.enabled = true; pdch->fh.maio = its->maio; pdch->fh.hsn = its->hsn; @@ -787,24 +953,96 @@ bssgp_failed: trx_nr, ts_nr, pdch->tsc, pdch->fh.enabled ? "yes" : "no"); } else { if (pdch->is_enabled()) { +#ifdef ENABLE_DIRECT_PHY + if ((info_ind->flags & PCU_IF_FLAG_DIRECT_PHY)) + l1if_disconnect_pdch(bts->trx[trx_nr].fl1h, ts_nr); +#endif pcu_tx_act_req(bts, pdch, 0); - pdch->free_resources(); pdch->disable(); } } } } + LOGP(DL1IF, LOGL_INFO, "BTS model: %s\n", get_value_string(gsm_pcuif_bts_model_names, info_ind->bts_model)); + bts->bts_model = info_ind->bts_model; + bts->gen_idle_blocks_C0 = decide_gen_idle_blocks(bts); + bts->active = true; return rc; } -static int pcu_rx_time_ind(struct gprs_rlcmac_bts *bts, struct gsm_pcu_if_time_ind *time_ind) +/* Query E1 CCU connection parameters by TS and TRX number */ +int pcu_l1if_get_e1_ccu_conn_pars(struct e1_conn_pars **e1_conn_pars, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr) +{ + struct e1_ccu_conn_pars *e1_ccu_conn_pars; + + llist_for_each_entry(e1_ccu_conn_pars, &e1_ccu_table, entry) { + if (e1_ccu_conn_pars->bts_nr == bts_nr && e1_ccu_conn_pars->trx_nr == trx_nr + && e1_ccu_conn_pars->ts_nr == ts_nr) { + *e1_conn_pars = &e1_ccu_conn_pars->e1_conn_pars; + return 0; + } + } + + return -EINVAL; +} + +/* Allocate a new connection parameter struct and store connection parameters */ +static void new_e1_ccu_conn_pars(const struct gsm_pcu_if_e1_ccu_ind *e1_ccu_ind, uint8_t bts_nr) { - uint8_t fn13 = time_ind->fn % 13; + struct e1_ccu_conn_pars *e1_ccu_conn_pars; + + e1_ccu_conn_pars = talloc_zero(tall_pcu_ctx, struct e1_ccu_conn_pars); + OSMO_ASSERT(e1_ccu_conn_pars); + e1_ccu_conn_pars->bts_nr = bts_nr; + e1_ccu_conn_pars->trx_nr = e1_ccu_ind->trx_nr; + e1_ccu_conn_pars->ts_nr = e1_ccu_ind->ts_nr; + e1_ccu_conn_pars->e1_conn_pars.e1_nr = e1_ccu_ind->e1_nr; + e1_ccu_conn_pars->e1_conn_pars.e1_ts = e1_ccu_ind->e1_ts; + e1_ccu_conn_pars->e1_conn_pars.e1_ts_ss = e1_ccu_ind->e1_ts_ss; + llist_add(&e1_ccu_conn_pars->entry, &e1_ccu_table); +} +static int pcu_rx_e1_ccu_ind(struct gprs_rlcmac_bts *bts, const struct gsm_pcu_if_e1_ccu_ind *e1_ccu_ind) +{ + struct e1_conn_pars *e1_conn_pars; + uint8_t rate; + uint8_t subslot_nr; + int rc; + + /* only used with log statement below, no technical relevance otherwise. */ + if (e1_ccu_ind->e1_ts_ss > 3) { + rate = 64; + subslot_nr = 0; + } else { + rate = 16; + subslot_nr = e1_ccu_ind->e1_ts_ss; + } + + LOGP(DL1IF, LOGL_NOTICE, + "(ts=%u,trx=%u) new E1 CCU communication parameters for CCU (E1-line:%u, E1-TS:%u, E1-SS:%u, rate:%ukbps)\n", + e1_ccu_ind->ts_nr, e1_ccu_ind->trx_nr, e1_ccu_ind->e1_nr, e1_ccu_ind->e1_ts, + subslot_nr, rate); + + /* Search for an existing entry, when found, update it. */ + rc = pcu_l1if_get_e1_ccu_conn_pars(&e1_conn_pars, bts->nr, e1_ccu_ind->trx_nr, e1_ccu_ind->ts_nr); + if (rc == 0) { + e1_conn_pars->e1_nr = e1_ccu_ind->e1_nr; + e1_conn_pars->e1_ts = e1_ccu_ind->e1_ts; + e1_conn_pars->e1_ts_ss = e1_ccu_ind->e1_ts_ss; + return 0; + } + + /* Create new connection parameter entry */ + new_e1_ccu_conn_pars(e1_ccu_ind, bts->nr); + return 0; +} + +static int pcu_rx_time_ind(struct gprs_rlcmac_bts *bts, struct gsm_pcu_if_time_ind *time_ind) +{ /* omit frame numbers not starting at a MAC block */ - if (fn13 != 0 && fn13 != 4 && fn13 != 8) + if (!fn_valid(time_ind->fn)) return 0; LOGP(DL1IF, LOGL_DEBUG, "Time indication received: %d\n", time_ind->fn % 52); @@ -817,6 +1055,9 @@ static int pcu_rx_time_ind(struct gprs_rlcmac_bts *bts, struct gsm_pcu_if_time_i static int pcu_rx_pag_req(struct gprs_rlcmac_bts *bts, struct gsm_pcu_if_pag_req *pag_req) { struct osmo_mobile_identity mi; + struct GprsMs *ms = NULL; + struct paging_req_cs req = { .chan_needed = pag_req->chan_needed, + .tlli = GSM_RESERVED_TMSI }; int rc; LOGP(DL1IF, LOGL_DEBUG, "Paging request received: chan_needed=%d " @@ -835,7 +1076,23 @@ static int pcu_rx_pag_req(struct gprs_rlcmac_bts *bts, struct gsm_pcu_if_pag_req return -EINVAL; } - return bts_add_paging(bts, pag_req->chan_needed, &mi); + switch (mi.type) { + case GSM_MI_TYPE_TMSI: + req.mi_tmsi = mi; + req.mi_tmsi_present = true; + /* TODO: look up MS by TMSI? Derive TLLI? */ + break; + case GSM_MI_TYPE_IMSI: + req.mi_imsi = mi; + req.mi_imsi_present = true; + ms = bts_get_ms_by_imsi(bts, req.mi_imsi.imsi); + break; + default: + LOGP(DL1IF, LOGL_ERROR, "Unexpected MI type %u\n", mi.type); + return -EINVAL; + } + + return bts_add_paging(bts, &req, ms); } static int pcu_rx_susp_req(struct gprs_rlcmac_bts *bts, struct gsm_pcu_if_susp_req *susp_req) @@ -851,7 +1108,7 @@ static int pcu_rx_susp_req(struct gprs_rlcmac_bts *bts, struct gsm_pcu_if_susp_r LOGP(DL1IF, LOGL_INFO, "GPRS Suspend request received: TLLI=0x%08x RAI=%s\n", susp_req->tlli, osmo_rai_name(&ra_id)); - if ((ms = bts_ms_store(bts)->get_ms(susp_req->tlli))) { + if ((ms = bts_get_ms_by_tlli(bts, susp_req->tlli, GSM_RESERVED_TMSI))) { /* We need to catch both pointers here since MS may become freed after first tbf_free(dl_tbf) if only DL TBF was available */ dl_tbf = ms_dl_tbf(ms); @@ -876,8 +1133,8 @@ static int pcu_rx_app_info_req(struct gprs_rlcmac_bts *bts, struct gsm_pcu_if_ap app_info_req->application_type, app_info_req->len); bts->app_info_pending = 0; - llist_for_each(tmp, bts_ms_store(bts)->ms_list()) { - GprsMs *ms = llist_entry(tmp, typeof(*ms), list); + llist_for_each(tmp, &bts->ms_list) { + struct GprsMs *ms = llist_entry(tmp, typeof(*ms), list); if (!ms_dl_tbf(ms)) continue; bts->app_info_pending++; @@ -902,9 +1159,81 @@ static int pcu_rx_app_info_req(struct gprs_rlcmac_bts *bts, struct gsm_pcu_if_ap return 0; } -int pcu_rx(uint8_t msg_type, struct gsm_pcu_if *pcu_prim) +static int pcu_rx_neigh_addr_cnf(struct gprs_rlcmac_bts *bts, struct gsm_pcu_if_neigh_addr_cnf *naddr_cnf) +{ + struct llist_head *tmp; + struct osmo_cell_global_id_ps cgi_ps; + struct osmo_cell_global_id_ps *cgi_ps_ptr = &cgi_ps; + + struct neigh_cache_entry_key neigh_key = { + .local_lac = osmo_load16be(&naddr_cnf->orig_req.local_lac), + .local_ci = osmo_load16be(&naddr_cnf->orig_req.local_ci), + .tgt_arfcn = osmo_load16be(&naddr_cnf->orig_req.tgt_arfcn), + .tgt_bsic = naddr_cnf->orig_req.tgt_bsic, + }; + + if (naddr_cnf->err_code == 0) { + cgi_ps.rai.lac.plmn.mcc = osmo_load16be(&naddr_cnf->cgi_ps.mcc); + cgi_ps.rai.lac.plmn.mnc = osmo_load16be(&naddr_cnf->cgi_ps.mnc); + cgi_ps.rai.lac.plmn.mnc_3_digits = naddr_cnf->cgi_ps.mnc_3_digits; + cgi_ps.rai.lac.lac = osmo_load16be(&naddr_cnf->cgi_ps.lac); + cgi_ps.rai.rac = naddr_cnf->cgi_ps.rac; + cgi_ps.cell_identity = osmo_load16be(&naddr_cnf->cgi_ps.cell_identity); + + LOGP(DL1IF, LOGL_INFO, "Rx Neighbor Address Resolution Confirmation for " NEIGH_CACHE_ENTRY_KEY_FMT ": %s\n", + NEIGH_CACHE_ENTRY_KEY_ARGS(&neigh_key), osmo_cgi_ps_name(&cgi_ps)); + + /* Cache the cgi_ps so we can avoid requesting again same resolution for a while */ + neigh_cache_add(bts->pcu->neigh_cache, &neigh_key, &cgi_ps); + } else { + cgi_ps_ptr = NULL; + LOGP(DL1IF, LOGL_INFO, "Rx Neighbor Address Resolution Confirmation for " NEIGH_CACHE_ENTRY_KEY_FMT ": failed with err_code=%u\n", + NEIGH_CACHE_ENTRY_KEY_ARGS(&neigh_key), naddr_cnf->err_code); + } + + llist_for_each(tmp, &bts->ms_list) { + struct GprsMs *ms = llist_entry(tmp, typeof(*ms), list); + if (ms->nacc && nacc_fsm_is_waiting_addr_resolution(ms->nacc, &neigh_key)) + osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_RAC_CI, cgi_ps_ptr); + } + return 0; +} + +static int pcu_rx_container(struct gprs_rlcmac_bts *bts, struct gsm_pcu_if_container *container) +{ + int rc; + uint16_t data_length = osmo_load16be(&container->length); + + switch (container->msg_type) { + case PCU_IF_MSG_NEIGH_ADDR_CNF: + if (data_length < sizeof(struct gsm_pcu_if_neigh_addr_cnf)) { + LOGP(DL1IF, LOGL_ERROR, "Rx container(NEIGH_ADDR_CNF) message too short: %u vs exp %zu\n", + data_length, sizeof(struct gsm_pcu_if_neigh_addr_cnf)); + return -EINVAL; + } + rc = pcu_rx_neigh_addr_cnf(bts, (struct gsm_pcu_if_neigh_addr_cnf*)&container->data); + break; + default: + LOGP(DL1IF, LOGL_NOTICE, "(bts=%d) Rx unexpected msg type (%u) inside container!\n", + bts->nr, container->msg_type); + rc = -1; + } + return rc; +} + +#define CHECK_IF_MSG_SIZE(prim_len, prim_msg) \ + do { \ + size_t _len = PCUIF_HDR_SIZE + sizeof(prim_msg); \ + if (prim_len < _len) { \ + LOGP(DL1IF, LOGL_ERROR, "Received %zu bytes on PCU Socket, but primitive %s " \ + "size is %zu, discarding\n", prim_len, #prim_msg, _len); \ + return -EINVAL; \ + } \ + } while(0); +int pcu_rx(struct gsm_pcu_if *pcu_prim, size_t pcu_prim_length) { int rc = 0; + size_t exp_len; struct gprs_rlcmac_bts *bts = gprs_pcu_get_bts_by_nr(the_pcu, pcu_prim->bts_nr); if (!bts) { LOGP(DL1IF, LOGL_NOTICE, "Received message for new BTS%d\n", pcu_prim->bts_nr); @@ -915,37 +1244,65 @@ int pcu_rx(uint8_t msg_type, struct gsm_pcu_if *pcu_prim) } } - switch (msg_type) { + switch (pcu_prim->msg_type) { case PCU_IF_MSG_DATA_IND: + CHECK_IF_MSG_SIZE(pcu_prim_length, pcu_prim->u.data_ind); rc = pcu_rx_data_ind(bts, &pcu_prim->u.data_ind); break; - case PCU_IF_MSG_DATA_CNF: - rc = pcu_rx_data_cnf(bts, &pcu_prim->u.data_cnf); + case PCU_IF_MSG_DATA_CNF_2: + CHECK_IF_MSG_SIZE(pcu_prim_length, pcu_prim->u.data_cnf2); + rc = pcu_rx_data_cnf2(bts, &pcu_prim->u.data_cnf2); break; case PCU_IF_MSG_RTS_REQ: + CHECK_IF_MSG_SIZE(pcu_prim_length, pcu_prim->u.rts_req); rc = pcu_rx_rts_req(bts, &pcu_prim->u.rts_req); break; case PCU_IF_MSG_RACH_IND: + CHECK_IF_MSG_SIZE(pcu_prim_length, pcu_prim->u.rach_ind); rc = pcu_rx_rach_ind(bts, &pcu_prim->u.rach_ind); break; case PCU_IF_MSG_INFO_IND: + CHECK_IF_MSG_SIZE(pcu_prim_length, pcu_prim->u.info_ind); rc = pcu_rx_info_ind(bts, &pcu_prim->u.info_ind); break; + case PCU_IF_MSG_E1_CCU_IND: + CHECK_IF_MSG_SIZE(pcu_prim_length, pcu_prim->u.e1_ccu_ind); + rc = pcu_rx_e1_ccu_ind(bts, &pcu_prim->u.e1_ccu_ind); + break; case PCU_IF_MSG_TIME_IND: + CHECK_IF_MSG_SIZE(pcu_prim_length, pcu_prim->u.time_ind); rc = pcu_rx_time_ind(bts, &pcu_prim->u.time_ind); break; case PCU_IF_MSG_PAG_REQ: + CHECK_IF_MSG_SIZE(pcu_prim_length, pcu_prim->u.pag_req); rc = pcu_rx_pag_req(bts, &pcu_prim->u.pag_req); break; case PCU_IF_MSG_SUSP_REQ: + CHECK_IF_MSG_SIZE(pcu_prim_length, pcu_prim->u.susp_req); rc = pcu_rx_susp_req(bts, &pcu_prim->u.susp_req); break; case PCU_IF_MSG_APP_INFO_REQ: + CHECK_IF_MSG_SIZE(pcu_prim_length, pcu_prim->u.app_info_req); rc = pcu_rx_app_info_req(bts, &pcu_prim->u.app_info_req); break; + case PCU_IF_MSG_INTERF_IND: + /* TODO: handle interference reports */ + break; + case PCU_IF_MSG_CONTAINER: + CHECK_IF_MSG_SIZE(pcu_prim_length, pcu_prim->u.container); + /* ^ check if we can access container fields, v check with container data length */ + exp_len = PCUIF_HDR_SIZE + sizeof(pcu_prim->u.container) + osmo_load16be(&pcu_prim->u.container.length); + if (pcu_prim_length < exp_len) { + LOGP(DL1IF, LOGL_ERROR, "Received %zu bytes on PCU Socket, but primitive container size" \ + "is %zu, discarding\n", pcu_prim_length, exp_len); + rc = -EINVAL; + break; + } + rc = pcu_rx_container(bts, &pcu_prim->u.container); + break; default: LOGP(DL1IF, LOGL_ERROR, "Received unknown PCU msg type %d\n", - msg_type); + pcu_prim->msg_type); rc = -EINVAL; } |