aboutsummaryrefslogtreecommitdiffstats
path: root/src/pcu_l1_if.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/pcu_l1_if.cpp')
-rw-r--r--src/pcu_l1_if.cpp563
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;
}