aboutsummaryrefslogtreecommitdiffstats
path: root/src/osmo-bsc/pcu_sock.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/osmo-bsc/pcu_sock.c')
-rw-r--r--src/osmo-bsc/pcu_sock.c447
1 files changed, 358 insertions, 89 deletions
diff --git a/src/osmo-bsc/pcu_sock.c b/src/osmo-bsc/pcu_sock.c
index a1a1cfc18..a0c9827dd 100644
--- a/src/osmo-bsc/pcu_sock.c
+++ b/src/osmo-bsc/pcu_sock.c
@@ -15,10 +15,6 @@
* 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.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
@@ -37,6 +33,7 @@
#include <osmocom/core/logging.h>
#include <osmocom/gsm/l1sap.h>
#include <osmocom/gsm/gsm0502.h>
+#include <osmocom/bsc/abis_nm.h>
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/bsc/pcu_if.h>
@@ -46,10 +43,9 @@
#include <osmocom/bsc/abis_rsl.h>
#include <osmocom/bsc/gsm_04_08_rr.h>
#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/bts_sm.h>
static int pcu_sock_send(struct gsm_bts *bts, struct msgb *msg);
-uint32_t trx_get_hlayer1(struct gsm_bts_trx *trx);
-int pcu_direct = 1;
static const char *sapi_string[] = {
[PCU_IF_SAPI_RACH] = "RACH",
@@ -58,8 +54,8 @@ static const char *sapi_string[] = {
[PCU_IF_SAPI_BCCH] = "BCCH",
[PCU_IF_SAPI_PDTCH] = "PDTCH",
[PCU_IF_SAPI_PRACH] = "PRACH",
- [PCU_IF_SAPI_PTCCH] = "PTCCH",
- [PCU_IF_SAPI_AGCH_DT] = "AGCH_DT",
+ [PCU_IF_SAPI_PTCCH] = "PTCCH",
+ [PCU_IF_SAPI_PCH_DT] = "PCH_DT",
};
/* Check if BTS has a PCU connection */
@@ -96,6 +92,33 @@ struct msgb *pcu_msgb_alloc(uint8_t msg_type, uint8_t bts_nr)
return msg;
}
+/* Check if the timeslot can be utilized as PDCH now
+ * (PDCH is currently active on BTS) */
+static bool ts_now_usable_as_pdch(const struct gsm_bts_trx_ts *ts)
+{
+ switch (ts->pchan_is) {
+ case GSM_PCHAN_PDCH:
+ /* NOTE: We currently only support Ericsson RBS as a BSC
+ * co-located BTS. This BTS only supports dynamic channels. */
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* Check if it is possible to use the TS as PDCH (not now, but maybe later) */
+static bool ts_usable_as_pdch(const struct gsm_bts_trx_ts *ts)
+{
+ switch (ts->pchan_from_config) {
+ case GSM_PCHAN_TCH_F_PDCH:
+ case GSM_PCHAN_OSMO_DYN:
+ case GSM_PCHAN_PDCH:
+ return true;
+ default:
+ return false;
+ }
+}
+
/* Fill the frequency hopping parameter */
static void info_ind_fill_fhp(struct gsm_pcu_if_info_trx_ts *ts_info,
const struct gsm_bts_trx_ts *ts)
@@ -108,6 +131,48 @@ static void info_ind_fill_fhp(struct gsm_pcu_if_info_trx_ts *ts_info,
ts_info->ma_bit_len = ts->hopping.ma_len * 8 - ts->hopping.ma.cur_bit;
}
+/* Fill the TRX parameter */
+static void info_ind_fill_trx(struct gsm_pcu_if_info_trx *trx_info, const struct gsm_bts_trx *trx)
+{
+ unsigned int tn;
+ const struct gsm_bts_trx_ts *ts;
+
+ trx_info->hlayer1 = 0x2342;
+ trx_info->pdch_mask = 0;
+ trx_info->arfcn = trx->arfcn;
+
+ if (trx->mo.nm_state.operational != NM_OPSTATE_ENABLED ||
+ trx->mo.nm_state.administrative != NM_STATE_UNLOCKED) {
+ LOG_TRX(trx, DPCU, LOGL_INFO, "unavailable for PCU (op=%s adm=%s)\n",
+ abis_nm_opstate_name(trx->mo.nm_state.operational),
+ abis_nm_admin_name(trx->mo.nm_state.administrative));
+ return;
+ }
+
+ for (tn = 0; tn < ARRAY_SIZE(trx->ts); tn++) {
+ ts = &trx->ts[tn];
+ if (ts->mo.nm_state.operational != NM_OPSTATE_ENABLED)
+ continue;
+ if (!ts_now_usable_as_pdch(ts))
+ continue;
+
+ trx_info->pdch_mask |= (1 << tn);
+ trx_info->ts[tn].tsc =
+ (ts->tsc >= 0) ? ts->tsc : trx->bts->bsic & 7;
+
+ if (ts->hopping.enabled)
+ info_ind_fill_fhp(&trx_info->ts[tn], ts);
+
+ LOG_TRX(trx, DPCU, LOGL_INFO, "PDCH on ts=%u is available (tsc=%u ", ts->nr,
+ trx_info->ts[tn].tsc);
+ if (ts->hopping.enabled)
+ LOGPC(DPCU, LOGL_INFO, "hopping=yes hsn=%u maio=%u ma_bit_len=%u)\n",
+ ts->hopping.hsn, ts->hopping.maio, trx_info->ts[tn].ma_bit_len);
+ else
+ LOGPC(DPCU, LOGL_INFO, "hopping=no arfcn=%u)\n", trx->arfcn);
+ }
+}
+
/* Send BTS properties to the PCU */
static int pcu_tx_info_ind(struct gsm_bts *bts)
{
@@ -115,15 +180,14 @@ static int pcu_tx_info_ind(struct gsm_bts *bts)
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_info_ind *info_ind;
struct gprs_rlc_cfg *rlcc;
- struct gsm_bts_gprs_nsvc *nsvc;
+ struct gsm_bts_sm *bts_sm;
+ struct gsm_gprs_nsvc *nsvc;
struct gsm_bts_trx *trx;
- struct gsm_bts_trx_ts *ts;
- int i, tn;
+ int i;
- OSMO_ASSERT(bts);
- OSMO_ASSERT(bts->network);
+ bts_sm = bts->site_mgr;
- LOGP(DPCU, LOGL_INFO, "Sending info for BTS %d\n",bts->nr);
+ LOGP(DPCU, LOGL_INFO, "Sending info for BTS %d\n", bts->nr);
rlcc = &bts->gprs.cell.rlc_cfg;
@@ -135,9 +199,8 @@ static int pcu_tx_info_ind(struct gsm_bts *bts)
info_ind = &pcu_prim->u.info_ind;
info_ind->version = PCU_IF_VERSION;
info_ind->flags |= PCU_IF_FLAG_ACTIVE;
-
- if (pcu_direct)
- info_ind->flags |= PCU_IF_FLAG_SYSMO;
+ info_ind->flags |= PCU_IF_FLAG_SYSMO;
+ info_ind->flags |= PCU_IF_FLAG_DT;
/* RAI */
info_ind->mcc = bts->network->plmn.mcc;
@@ -147,11 +210,12 @@ static int pcu_tx_info_ind(struct gsm_bts *bts)
info_ind->rac = bts->gprs.rac;
/* NSE */
- info_ind->nsei = bts->gprs.nse.nsei;
- memcpy(info_ind->nse_timer, bts->gprs.nse.timer, 7);
+ info_ind->nsei = bts_sm->gprs.nse.nsei;
+ memcpy(info_ind->nse_timer, bts_sm->gprs.nse.timer, 7);
memcpy(info_ind->cell_timer, bts->gprs.cell.timer, 11);
/* cell attributes */
+ info_ind->bsic = bts->bsic;
info_ind->cell_id = bts->cell_identity;
info_ind->repeat_time = rlcc->paging.repeat_time;
info_ind->repeat_count = rlcc->paging.repeat_count;
@@ -193,16 +257,16 @@ static int pcu_tx_info_ind(struct gsm_bts *bts)
if (rlcc->cs_mask & (1 << GPRS_MCS9))
info_ind->flags |= PCU_IF_FLAG_MCS9;
}
-#warning "isn't dl_tbf_ext wrong?: * 10 and no ntohs"
+ /* TODO: isn't dl_tbf_ext wrong?: * 10 and no ntohs */
info_ind->dl_tbf_ext = rlcc->parameter[T_DL_TBF_EXT];
-#warning "isn't ul_tbf_ext wrong?: * 10 and no ntohs"
+ /* TODO: isn't ul_tbf_ext wrong?: * 10 and no ntohs */
info_ind->ul_tbf_ext = rlcc->parameter[T_UL_TBF_EXT];
info_ind->initial_cs = rlcc->initial_cs;
info_ind->initial_mcs = rlcc->initial_mcs;
/* NSVC */
for (i = 0; i < ARRAY_SIZE(info_ind->nsvci); i++) {
- nsvc = &bts->gprs.nsvc[i];
+ nsvc = &bts->site_mgr->gprs.nsvc[i];
info_ind->nsvci[i] = nsvc->nsvci;
info_ind->local_port[i] = nsvc->local_port;
@@ -210,14 +274,14 @@ static int pcu_tx_info_ind(struct gsm_bts *bts)
case AF_INET:
info_ind->address_type[i] = PCU_IF_ADDR_TYPE_IPV4;
info_ind->remote_ip[i].v4 = nsvc->remote.u.sin.sin_addr;
- info_ind->remote_port[i] = nsvc->remote.u.sin.sin_port;
+ info_ind->remote_port[i] = ntohs(nsvc->remote.u.sin.sin_port);
break;
case AF_INET6:
info_ind->address_type[i] = PCU_IF_ADDR_TYPE_IPV6;
memcpy(&info_ind->remote_ip[i].v6,
&nsvc->remote.u.sin6.sin6_addr,
sizeof(struct in6_addr));
- info_ind->remote_port[i] = nsvc->remote.u.sin6.sin6_port;
+ info_ind->remote_port[i] = ntohs(nsvc->remote.u.sin6.sin6_port);
break;
default:
info_ind->address_type[i] = PCU_IF_ADDR_TYPE_UNSPEC;
@@ -229,39 +293,114 @@ static int pcu_tx_info_ind(struct gsm_bts *bts)
trx = gsm_bts_trx_num(bts, i);
if (!trx)
continue;
- info_ind->trx[i].hlayer1 = 0x2342;
- info_ind->trx[i].pdch_mask = 0;
- info_ind->trx[i].arfcn = trx->arfcn;
- for (tn = 0; tn < ARRAY_SIZE(trx->ts); tn++) {
- ts = &trx->ts[tn];
- if (ts->mo.nm_state.operational != NM_OPSTATE_ENABLED ||
- ts->pchan_is != GSM_PCHAN_PDCH)
- continue;
+ if (trx->nr >= ARRAY_SIZE(info_ind->trx)) {
+ LOG_TRX(trx, DPCU, LOGL_NOTICE, "PCU interface (version %u) "
+ "cannot handle more than %zu transceivers => skipped\n",
+ PCU_IF_VERSION, ARRAY_SIZE(info_ind->trx));
+ break;
+ }
+ info_ind_fill_trx(&info_ind->trx[trx->nr], trx);
+ }
+
+ return pcu_sock_send(bts, msg);
+}
- info_ind->trx[i].pdch_mask |= (1 << tn);
- info_ind->trx[i].ts[tn].tsc =
- (ts->tsc >= 0) ? ts->tsc : bts->bsic & 7;
+static int pcu_tx_e1_ccu_ind(struct gsm_bts *bts)
+{
+ struct gsm_bts_trx *trx;
- if (ts->hopping.enabled)
- info_ind_fill_fhp(&info_ind->trx[i].ts[tn], ts);
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ struct gsm_pcu_if_e1_ccu_ind *e1_ccu_ind;
+ int i;
- LOGP(DPCU, LOGL_INFO, "trx=%d ts=%d: PDCH is available "
- "(tsc=%u ", trx->nr, ts->nr, info_ind->trx[i].ts[tn].tsc);
- if (ts->hopping.enabled)
- LOGPC(DPCU, LOGL_INFO, "hopping=yes hsn=%u maio=%u ma_bit_len=%u)\n",
- ts->hopping.hsn, ts->hopping.maio, trx->ts[tn].hopping.ma.data_len - trx->ts[tn].hopping.ma.cur_bit);
- else
- LOGPC(DPCU, LOGL_INFO, "hopping=no arfcn=%u)\n", trx->arfcn);
+ if (trx->nr >= PCU_IF_NUM_TRX) {
+ LOG_TRX(trx, DPCU, LOGL_NOTICE, "PCU interface (version %u) "
+ "cannot handle more than %u transceivers => skipped\n",
+ PCU_IF_VERSION, PCU_IF_NUM_TRX);
+ continue;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
+ struct gsm_pcu_if *pcu_prim;
+ struct gsm_bts_trx_ts *ts;
+ struct msgb *msg;
+ int rc;
+
+ ts = &trx->ts[i];
+
+ if (ts->mo.nm_state.operational != NM_OPSTATE_ENABLED)
+ continue;
+ if (!ts_usable_as_pdch(ts))
+ continue;
+
+ msg = pcu_msgb_alloc(PCU_IF_MSG_E1_CCU_IND, bts->nr);
+ if (!msg)
+ return -ENOMEM;
+ pcu_prim = (struct gsm_pcu_if *)msg->data;
+ e1_ccu_ind = &pcu_prim->u.e1_ccu_ind;
+ e1_ccu_ind->ts_nr = ts->nr;
+ e1_ccu_ind->trx_nr = trx->nr;
+ e1_ccu_ind->e1_nr = ts->e1_link.e1_nr;
+ e1_ccu_ind->e1_ts = ts->e1_link.e1_ts;
+ e1_ccu_ind->e1_ts_ss = ts->e1_link.e1_ts_ss;
+
+ LOG_TRX(trx, DPCU, LOGL_INFO, "Sending E1 CCU info for TS %d\n", e1_ccu_ind->ts_nr);
+ rc = pcu_sock_send(bts, msg);
+ if (rc < 0)
+ return -EINVAL;
}
}
- return pcu_sock_send(bts, msg);
+ return 0;
}
-void pcu_info_update(struct gsm_bts *bts)
+/* Allow test to overwrite it */
+__attribute__((weak)) void pcu_info_update(struct gsm_bts *bts)
{
- if (pcu_connected(bts))
+ if (pcu_connected(bts)) {
+ /* In cases where the CCU is connected via an E1 line, we transmit the connection parameters for the
+ * PDCH before we announce the other BTS related parameters. At the moment Ericsson RBS is the only
+ * E1 BTS we support. */
+ if (is_ericsson_bts(bts))
+ pcu_tx_e1_ccu_ind(bts);
+
pcu_tx_info_ind(bts);
+ }
+}
+
+static int pcu_tx_data_ind(struct gsm_bts_trx_ts *ts, uint8_t sapi, uint32_t fn,
+ uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len,
+ int8_t rssi, uint16_t ber10k, int16_t bto, int16_t lqual)
+{
+ struct msgb *msg;
+ struct gsm_pcu_if *pcu_prim;
+ struct gsm_pcu_if_data *data_ind;
+ struct gsm_bts *bts = ts->trx->bts;
+
+ LOGP(DPCU, LOGL_DEBUG, "Sending data indication: sapi=%s arfcn=%d block=%d data=%s\n",
+ sapi_string[sapi], arfcn, block_nr, osmo_hexdump(data, len));
+
+ msg = pcu_msgb_alloc(PCU_IF_MSG_DATA_IND, bts->nr);
+ if (!msg)
+ return -ENOMEM;
+ pcu_prim = (struct gsm_pcu_if *) msg->data;
+ data_ind = &pcu_prim->u.data_ind;
+
+ data_ind->sapi = sapi;
+ data_ind->rssi = rssi;
+ data_ind->fn = fn;
+ data_ind->arfcn = arfcn;
+ data_ind->trx_nr = ts->trx->nr;
+ data_ind->ts_nr = ts->nr;
+ data_ind->block_nr = block_nr;
+ data_ind->ber10k = ber10k;
+ data_ind->ta_offs_qbits = bto;
+ data_ind->lqual_cb = lqual;
+ if (len)
+ memcpy(data_ind->data, data, len);
+ data_ind->len = len;
+
+ return pcu_sock_send(bts, msg);
}
/* Forward rach indication to PCU */
@@ -368,11 +507,31 @@ static int pcu_rx_rr_paging(struct gsm_bts *bts, uint8_t paging_group,
return rc;
}
+/* Helper function for pcu_rx_data_req() to extract paging group info (3 byte) */
+static uint8_t extract_paging_group(struct gsm_bts *bts, uint8_t *data)
+{
+ char imsi_digit_buf[4];
+ uint8_t pag_grp;
+
+ /* the first three bytes are the last three digits of the IMSI, which we need to compute the paging group */
+ imsi_digit_buf[0] = data[0];
+ imsi_digit_buf[1] = data[1];
+ imsi_digit_buf[2] = data[2];
+ imsi_digit_buf[3] = '\0';
+
+ pag_grp = gsm0502_calc_paging_group(&bts->si_common.chan_desc,
+ str_to_imsi(imsi_digit_buf));
+
+ LOGP(DPCU, LOGL_DEBUG, "Calculating paging group: imsi_digit_buf=%s ==> pag_grp=0x%02x\n",
+ imsi_digit_buf, pag_grp);
+
+ return pag_grp;
+}
+
static int pcu_rx_data_req(struct gsm_bts *bts, uint8_t msg_type,
struct gsm_pcu_if_data *data_req)
{
struct msgb *msg;
- char imsi_digit_buf[4];
uint32_t tlli = -1;
uint8_t pag_grp;
int rc = 0;
@@ -384,15 +543,8 @@ static int pcu_rx_data_req(struct gsm_bts *bts, uint8_t msg_type,
switch (data_req->sapi) {
case PCU_IF_SAPI_PCH:
- /* the first three bytes are the last three digits of
- * the IMSI, which we need to compute the paging group */
- imsi_digit_buf[0] = data_req->data[0];
- imsi_digit_buf[1] = data_req->data[1];
- imsi_digit_buf[2] = data_req->data[2];
- imsi_digit_buf[3] = '\0';
- LOGP(DPCU, LOGL_DEBUG, "SAPI PCH imsi %s\n", imsi_digit_buf);
- pag_grp = gsm0502_calc_paging_group(&bts->si_common.chan_desc,
- str_to_imsi(imsi_digit_buf));
+ /* Extract 3 byte paging group */
+ pag_grp = extract_paging_group(bts, data_req->data);
pcu_rx_rr_paging(bts, pag_grp, data_req->data+3);
break;
case PCU_IF_SAPI_AGCH:
@@ -409,28 +561,42 @@ static int pcu_rx_data_req(struct gsm_bts *bts, uint8_t msg_type,
rc = -EIO;
}
break;
- case PCU_IF_SAPI_AGCH_DT:
- /* DT = direct tlli. A tlli is prefixed */
+ case PCU_IF_SAPI_PCH_DT:
+ /* DT = direct TLLI. A tlli is prefixed so that the BSC/BTS can confirm the sending of the downlink
+ * IMMEDIATE ASSIGNMENT towards the PCU using this TLLI as a reference. */
- if (data_req->len < 5) {
- LOGP(DPCU, LOGL_ERROR, "Received PCU data request with "
- "invalid/small length %d\n", data_req->len);
+ if (data_req->len < 8) {
+ LOGP(DPCU, LOGL_ERROR, "Received PCU data request with invalid/small length %d\n",
+ data_req->len);
break;
}
+
+ /* Extract 4 byte TLLI */
memcpy(&tlli, data_req->data, 4);
- msg = msgb_alloc(data_req->len - 4, "pcu_agch");
+ /* Extract 3 byte paging group */
+ pag_grp = extract_paging_group(bts, data_req->data + 4);
+
+ LOGP(DPCU, LOGL_DEBUG, "PCU Sends immediate assignment via PCH (tlli=0x%08x, pag_grp=0x%02x)\n",
+ tlli, pag_grp);
+ msg = msgb_alloc(data_req->len - 7, "pcu_pch");
if (!msg) {
rc = -ENOMEM;
break;
}
- msg->l3h = msgb_put(msg, data_req->len - 4);
- memcpy(msg->l3h, data_req->data + 4, data_req->len - 4);
-
- if (bts->type == GSM_BTS_TYPE_RBS2000)
- rc = rsl_ericsson_imm_assign_cmd(bts, tlli, msg->len, msg->data);
- else
- rc = rsl_imm_assign_cmd(bts, msg->len, msg->data);
+ msg->l3h = msgb_put(msg, data_req->len - 7);
+ memcpy(msg->l3h, data_req->data + 7, data_req->len - 7);
+
+ /* NOTE: Sending an IMMEDIATE ASSIGNMENT via PCH became necessary with GPRS in order to be able to
+ * assign downlink TBFs directly through the paging channel. However, this method never became part
+ * of the RSL specs. This means that each BTS vendor has to come up with a proprietary method. At
+ * the moment we only support Ericsson RBS here. */
+ if (bts->type == GSM_BTS_TYPE_RBS2000) {
+ rc = rsl_ericsson_imm_assign_cmd(bts, tlli, msg->len, msg->data, pag_grp);
+ } else {
+ LOGP(DPCU, LOGL_ERROR, "BTS model does not support sending immediate assignment via PCH!\n");
+ rc = -ENOTSUP;
+ }
if (rc) {
msgb_free(msg);
@@ -446,8 +612,103 @@ static int pcu_rx_data_req(struct gsm_bts *bts, uint8_t msg_type,
return rc;
}
+static int pcu_tx_si(const struct gsm_bts *bts, enum osmo_sysinfo_type si_type, bool enable)
+{
+ /* the SI is per-BTS so it doesn't matter which TRX we use */
+ struct gsm_bts_trx *trx = bts->c0;
+
+ uint8_t si_buf[GSM_MACBLOCK_LEN];
+ uint8_t len;
+ int rc;
+
+ if (enable) {
+ memcpy(si_buf, GSM_BTS_SI(bts, si_type), GSM_MACBLOCK_LEN);
+ len = GSM_MACBLOCK_LEN;
+ LOGP(DPCU, LOGL_DEBUG, "Updating SI%s to PCU: %s\n",
+ get_value_string(osmo_sitype_strs, si_type),
+ osmo_hexdump_nospc(si_buf, GSM_MACBLOCK_LEN));
+ } else {
+ si_buf[0] = si_type;
+ len = 1;
+
+ /* Note: SI13 is the only system information type that is revked
+ * by just sending a completely empty message. This is due to
+ * historical reasons */
+ if (si_type != SYSINFO_TYPE_13)
+ len = 0;
+
+ LOGP(DPCU, LOGL_DEBUG, "Revoking SI%s from PCU\n",
+ get_value_string(osmo_sitype_strs, si_buf[0]));
+ }
+
+ /* The low-level data like FN, ARFCN etc will be ignored but we have to
+ * set lqual high enough to bypass the check at lower levels */
+ rc = pcu_tx_data_ind(&trx->ts[0], PCU_IF_SAPI_BCCH, 0, 0, 0, si_buf, len,
+ 0, 0, 0, INT16_MAX);
+ if (rc < 0)
+ LOGP(DPCU, LOGL_NOTICE, "Failed to send SI%s to PCU: rc=%d\n",
+ get_value_string(osmo_sitype_strs, si_type), rc);
+
+ return rc;
+}
+
+static int pcu_tx_si_all(struct gsm_bts *bts)
+{
+ const enum osmo_sysinfo_type si_types[] = { SYSINFO_TYPE_1, SYSINFO_TYPE_2, SYSINFO_TYPE_3, SYSINFO_TYPE_13 };
+ unsigned int i;
+ int rc = 0;
+
+ for (i = 0; i < ARRAY_SIZE(si_types); i++) {
+ if (GSM_BTS_HAS_SI(bts, si_types[i])) {
+ rc = pcu_tx_si(bts, si_types[i], true);
+ if (rc < 0)
+ return rc;
+ } else {
+ LOGP(DPCU, LOGL_INFO,
+ "SI%s is not available on PCU connection\n",
+ get_value_string(osmo_sitype_strs, si_types[i]));
+ }
+ }
+
+ return 0;
+}
+
+static int pcu_rx_txt_ind(struct gsm_bts *bts,
+ const struct gsm_pcu_if_txt_ind *txt)
+{
+ int rc;
+
+ switch (txt->type) {
+ case PCU_VERSION:
+ LOGP(DPCU, LOGL_INFO, "OsmoPCU version %s connected\n",
+ txt->text);
+ rc = pcu_tx_si_all(bts);
+ if (rc < 0)
+ return -EINVAL;
+ break;
+ case PCU_OML_ALERT:
+ LOG_BTS(bts, DPCU, LOGL_ERROR, "PCU external alarm: %s\n", txt->text);
+ break;
+ default:
+ LOGP(DPCU, LOGL_ERROR, "Unknown TXT_IND type %u received\n",
+ txt->type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#define CHECK_IF_MSG_SIZE(prim_len, prim_msg) \
+ do { \
+ size_t _len = PCUIF_HDR_SIZE + sizeof(prim_msg); \
+ if (prim_len < _len) { \
+ LOGP(DPCU, 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)
static int pcu_rx(struct gsm_network *net, uint8_t msg_type,
- struct gsm_pcu_if *pcu_prim)
+ struct gsm_pcu_if *pcu_prim, size_t prim_len)
{
int rc = 0;
struct gsm_bts *bts;
@@ -458,8 +719,13 @@ static int pcu_rx(struct gsm_network *net, uint8_t msg_type,
switch (msg_type) {
case PCU_IF_MSG_DATA_REQ:
case PCU_IF_MSG_PAG_REQ:
+ CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.data_req);
rc = pcu_rx_data_req(bts, msg_type, &pcu_prim->u.data_req);
break;
+ case PCU_IF_MSG_TXT_IND:
+ CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.txt_ind);
+ rc = pcu_rx_txt_ind(bts, &pcu_prim->u.txt_ind);
+ break;
default:
LOGP(DPCU, LOGL_ERROR, "Received unknown PCU msg type %d\n",
msg_type);
@@ -495,7 +761,7 @@ static int pcu_sock_send(struct gsm_bts *bts, struct msgb *msg)
return -EIO;
}
msgb_enqueue(&state->upqueue, msg);
- conn_bfd->when |= OSMO_FD_WRITE;
+ osmo_fd_write_enable(conn_bfd);
return 0;
}
@@ -518,7 +784,7 @@ static void pcu_sock_close(struct pcu_sock_state *state)
osmo_fd_unregister(bfd);
/* re-enable the generation of ACCEPT for new connections */
- state->listen_bfd.when |= OSMO_FD_READ;
+ osmo_fd_read_enable(&state->listen_bfd);
#if 0
/* remove si13, ... */
@@ -554,7 +820,7 @@ static int pcu_sock_read(struct osmo_fd *bfd)
struct msgb *msg;
int rc;
- msg = msgb_alloc(sizeof(*pcu_prim), "pcu_sock_rx");
+ msg = msgb_alloc(sizeof(*pcu_prim) + 1000, "pcu_sock_rx");
if (!msg)
return -ENOMEM;
@@ -565,12 +831,21 @@ static int pcu_sock_read(struct osmo_fd *bfd)
goto close;
if (rc < 0) {
- if (errno == EAGAIN)
+ if (errno == EAGAIN) {
+ msgb_free(msg);
return 0;
+ }
goto close;
}
- rc = pcu_rx(state->net, pcu_prim->msg_type, pcu_prim);
+ if (rc < PCUIF_HDR_SIZE) {
+ LOGP(DPCU, LOGL_ERROR, "Received %d bytes on PCU Socket, but primitive hdr size "
+ "is %zu, discarding\n", rc, PCUIF_HDR_SIZE);
+ msgb_free(msg);
+ return 0;
+ }
+
+ rc = pcu_rx(state->net, pcu_prim->msg_type, pcu_prim, rc);
/* as we always synchronously process the message in pcu_rx() and
* its callbacks, we can free the message here. */
@@ -597,7 +872,7 @@ static int pcu_sock_write(struct osmo_fd *bfd)
msg = llist_entry(state->upqueue.next, struct msgb, list);
pcu_prim = (struct gsm_pcu_if *)msg->data;
- bfd->when &= ~OSMO_FD_WRITE;
+ osmo_fd_write_disable(bfd);
/* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
if (!msgb_length(msg)) {
@@ -612,7 +887,7 @@ static int pcu_sock_write(struct osmo_fd *bfd)
goto close;
if (rc < 0) {
if (errno == EAGAIN) {
- bfd->when |= OSMO_FD_WRITE;
+ osmo_fd_write_enable(bfd);
break;
}
goto close;
@@ -667,15 +942,12 @@ static int pcu_sock_accept(struct osmo_fd *bfd, unsigned int flags)
LOGP(DPCU, LOGL_NOTICE, "PCU connects but we already have "
"another active connection ?!?\n");
/* We already have one PCU connected, this is all we support */
- state->listen_bfd.when &= ~OSMO_FD_READ;
+ osmo_fd_read_disable(&state->listen_bfd);
close(rc);
return 0;
}
- conn_bfd->fd = rc;
- conn_bfd->when = OSMO_FD_READ;
- conn_bfd->cb = pcu_sock_cb;
- conn_bfd->data = state;
+ osmo_fd_setup(conn_bfd, rc, OSMO_FD_READ, pcu_sock_cb, state, 0);
if (osmo_fd_register(conn_bfd) != 0) {
LOGP(DPCU, LOGL_ERROR, "Failed to register new connection "
@@ -707,18 +979,15 @@ int pcu_sock_init(const char *path, struct gsm_bts *bts)
bfd = &state->listen_bfd;
- bfd->fd = osmo_sock_unix_init(SOCK_SEQPACKET, 0, path,
- OSMO_SOCK_F_BIND);
- if (bfd->fd < 0) {
+ rc = osmo_sock_unix_init(SOCK_SEQPACKET, 0, path, OSMO_SOCK_F_BIND);
+ if (rc < 0) {
LOGP(DPCU, LOGL_ERROR, "Could not create unix socket: %s\n",
strerror(errno));
talloc_free(state);
return -1;
}
- bfd->when = OSMO_FD_READ;
- bfd->cb = pcu_sock_accept;
- bfd->data = state;
+ osmo_fd_setup(bfd, rc, OSMO_FD_READ, pcu_sock_accept, state, 0);
rc = osmo_fd_register(bfd);
if (rc < 0) {