aboutsummaryrefslogtreecommitdiffstats
path: root/src/common/pcu_sock.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/pcu_sock.c')
-rw-r--r--src/common/pcu_sock.c457
1 files changed, 281 insertions, 176 deletions
diff --git a/src/common/pcu_sock.c b/src/common/pcu_sock.c
index 0466698d..048e7668 100644
--- a/src/common/pcu_sock.c
+++ b/src/common/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>
@@ -32,8 +28,10 @@
#include <inttypes.h>
#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
#include <osmocom/core/select.h>
#include <osmocom/core/socket.h>
+#include <osmocom/core/write_queue.h>
#include <osmocom/gsm/gsm23003.h>
#include <osmocom/gsm/abis_nm.h>
#include <osmo-bts/logging.h>
@@ -41,29 +39,28 @@
#include <osmo-bts/pcu_if.h>
#include <osmo-bts/pcuif_proto.h>
#include <osmo-bts/bts.h>
+#include <osmo-bts/bts_sm.h>
#include <osmo-bts/rsl.h>
#include <osmo-bts/signal.h>
#include <osmo-bts/l1sap.h>
#include <osmo-bts/oml.h>
+#include <osmo-bts/abis_osmo.h>
uint32_t trx_get_hlayer1(const struct gsm_bts_trx *trx);
-extern struct gsm_network bts_gsmnet;
int pcu_direct = 0;
static int avail_lai = 0, avail_nse = 0, avail_cell = 0, avail_nsvc[2] = {0, 0};
static const char *sapi_string[] = {
[PCU_IF_SAPI_RACH] = "RACH",
- [PCU_IF_SAPI_AGCH] = "AGCH",
- [PCU_IF_SAPI_PCH] = "PCH",
[PCU_IF_SAPI_BCCH] = "BCCH",
[PCU_IF_SAPI_PDTCH] = "PDTCH",
[PCU_IF_SAPI_PRACH] = "PRACH",
[PCU_IF_SAPI_PTCCH] = "PTCCH",
+ [PCU_IF_SAPI_PCH_2] = "PCH_2",
+ [PCU_IF_SAPI_AGCH_2] = "AGCH_2",
};
-static int pcu_sock_send(struct gsm_network *net, struct msgb *msg);
-
/*
* PCU messages
*/
@@ -100,7 +97,7 @@ static bool ts_should_be_pdch(const struct gsm_bts_trx_ts *ts)
return !(ts->flags & TS_F_PDCH_DEACT_PENDING);
else
return (ts->flags & TS_F_PDCH_ACT_PENDING);
- case GSM_PCHAN_TCH_F_TCH_H_PDCH:
+ case GSM_PCHAN_OSMO_DYN:
/*
* When we're busy de-/activating the PDCH, we first set
* ts->dyn.pchan_want, tell the PCU about it and wait for a
@@ -208,7 +205,7 @@ static void info_ind_fill_trx(struct gsm_pcu_if_info_trx *trx_info,
return;
}
- for (tn = 0; tn < 8; tn++) {
+ for (tn = 0; tn < ARRAY_SIZE(trx->ts); tn++) {
const struct gsm_bts_trx_ts *ts = &trx->ts[tn];
if (ts->mo.nm_state.operational != NM_OPSTATE_ENABLED)
@@ -217,7 +214,7 @@ static void info_ind_fill_trx(struct gsm_pcu_if_info_trx *trx_info,
continue;
trx_info->pdch_mask |= (1 << tn);
- trx_info->ts[tn].tsc = gsm_ts_tsc(ts);
+ trx_info->ts[tn].tsc = ts->tsc;
if (ts->hopping.enabled)
info_ind_fill_fhp(&trx_info->ts[tn], ts);
@@ -233,9 +230,27 @@ static void info_ind_fill_trx(struct gsm_pcu_if_info_trx *trx_info,
}
}
+static enum gsm_pcuif_bts_model bts_model_from_variant(enum gsm_bts_type_variant variant)
+{
+ switch (variant) {
+ case BTS_OSMO_LITECELL15:
+ return PCU_IF_BTS_MODEL_LC15;
+ case BTS_OSMO_OC2G:
+ return PCU_IF_BTS_MODEL_OC2G;
+ case BTS_OSMO_OCTPHY:
+ return PCU_IF_BTS_MODEL_OCTPHY;
+ case BTS_OSMO_SYSMO:
+ return PCU_IF_BTS_MODEL_SYSMO;
+ case BTS_OSMO_TRX:
+ case BTS_OSMO_VIRTUAL:
+ return PCU_IF_BTS_MODEL_TRX;
+ default:
+ return PCU_IF_BTS_MODEL_UNSPEC;
+ }
+}
+
int pcu_tx_info_ind(void)
{
- struct gsm_network *net = &bts_gsmnet;
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_info_ind *info_ind;
@@ -243,11 +258,13 @@ int pcu_tx_info_ind(void)
struct gprs_rlc_cfg *rlcc;
struct gsm_bts_trx *trx;
int i;
+ struct gsm_gprs_nse *nse;
LOGP(DPCU, LOGL_INFO, "Sending info\n");
+ nse = &g_bts_sm->gprs.nse;
/* FIXME: allow multiple BTS */
- bts = llist_entry(net->bts_list.next, struct gsm_bts, list);
+ bts = llist_entry(g_bts_sm->bts_list.next, struct gsm_bts, list);
rlcc = &bts->gprs.cell.rlc_cfg;
msg = pcu_msgb_alloc(PCU_IF_MSG_INFO_IND, bts->nr);
@@ -264,18 +281,19 @@ int pcu_tx_info_ind(void)
LOGP(DPCU, LOGL_INFO, "BTS is down\n");
if (pcu_direct)
- info_ind->flags |= PCU_IF_FLAG_SYSMO;
+ info_ind->flags |= PCU_IF_FLAG_DIRECT_PHY;
+ info_ind->bsic = bts->bsic;
/* RAI */
- info_ind->mcc = net->plmn.mcc;
- info_ind->mnc = net->plmn.mnc;
- info_ind->mnc_3_digits = net->plmn.mnc_3_digits;
+ info_ind->mcc = g_bts_sm->plmn.mcc;
+ info_ind->mnc = g_bts_sm->plmn.mnc;
+ info_ind->mnc_3_digits = g_bts_sm->plmn.mnc_3_digits;
info_ind->lac = bts->location_area_code;
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 = nse->nsei;
+ memcpy(info_ind->nse_timer, nse->timer, 7);
memcpy(info_ind->cell_timer, bts->gprs.cell.timer, 11);
/* cell attributes */
@@ -318,16 +336,16 @@ int pcu_tx_info_ind(void)
info_ind->flags |= PCU_IF_FLAG_MCS8;
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"
+ /* FIXME: 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"
+ /* FIXME: 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(bts->gprs.nsvc); i++) {
- const struct gsm_bts_gprs_nsvc *nsvc = &bts->gprs.nsvc[i];
+ for (i = 0; i < ARRAY_SIZE(nse->nsvc); i++) {
+ const struct gsm_gprs_nsvc *nsvc = &nse->nsvc[i];
info_ind->nsvci[i] = nsvc->nsvci;
/* PCUIF beauty: the NSVC addresses are sent in the network byte order,
* while the port numbers need to be send in the host order. Sigh. */
@@ -353,20 +371,21 @@ int pcu_tx_info_ind(void)
LOGPTRX(trx, DPCU, LOGL_NOTICE, "PCU interface (version %u) "
"cannot handle more than %zu transceivers => skipped\n",
PCU_IF_VERSION, ARRAY_SIZE(info_ind->trx));
- continue;
+ break;
}
info_ind_fill_trx(&info_ind->trx[trx->nr], trx);
}
- return pcu_sock_send(net, msg);
+ info_ind->bts_model = bts_model_from_variant(bts->variant);
+
+ return pcu_sock_send(msg);
}
static int pcu_if_signal_cb(unsigned int subsys, unsigned int signal,
void *hdlr_data, void *signal_data)
{
- struct gsm_network *net = &bts_gsmnet;
- struct gsm_bts_gprs_nsvc *nsvc;
+ struct gsm_gprs_nsvc *nsvc;
struct gsm_bts *bts;
struct gsm48_system_information_type_3 *si3;
int id;
@@ -381,7 +400,7 @@ static int pcu_if_signal_cb(unsigned int subsys, unsigned int signal,
break;
si3 = (struct gsm48_system_information_type_3 *)
bts->si_buf[SYSINFO_TYPE_3];
- osmo_plmn_from_bcd(si3->lai.digits, &net->plmn);
+ osmo_plmn_from_bcd(si3->lai.digits, &g_bts_sm->plmn);
bts->location_area_code = ntohs(si3->lai.lac);
bts->cell_identity = ntohs(si3->cell_identity);
avail_lai = 1;
@@ -437,7 +456,7 @@ int pcu_tx_app_info_req(struct gsm_bts *bts, uint8_t app_type, uint8_t len, cons
ai_req->len = len;
memcpy(ai_req->data, app_data, ai_req->len);
- return pcu_sock_send(&bts_gsmnet, msg);
+ return pcu_sock_send(msg);
}
int pcu_tx_rts_req(struct gsm_bts_trx_ts *ts, uint8_t is_ptcch, uint32_t fn,
@@ -464,7 +483,7 @@ int pcu_tx_rts_req(struct gsm_bts_trx_ts *ts, uint8_t is_ptcch, uint32_t fn,
rts_req->ts_nr = ts->nr;
rts_req->block_nr = block_nr;
- return pcu_sock_send(&bts_gsmnet, msg);
+ return pcu_sock_send(msg);
}
int pcu_tx_data_ind(struct gsm_bts_trx_ts *ts, uint8_t sapi, uint32_t fn,
@@ -479,12 +498,6 @@ int pcu_tx_data_ind(struct gsm_bts_trx_ts *ts, uint8_t sapi, uint32_t fn,
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));
- if (lqual < bts->min_qual_norm) {
- LOGP(DPCU, LOGL_DEBUG, "Link quality %"PRId16" is below threshold %d, dropping packet\n",
- lqual, bts->min_qual_norm);
- return 0;
- }
-
msg = pcu_msgb_alloc(PCU_IF_MSG_DATA_IND, bts->nr);
if (!msg)
return -ENOMEM;
@@ -501,10 +514,11 @@ int pcu_tx_data_ind(struct gsm_bts_trx_ts *ts, uint8_t sapi, uint32_t fn,
data_ind->ber10k = ber10k;
data_ind->ta_offs_qbits = bto;
data_ind->lqual_cb = lqual;
- memcpy(data_ind->data, data, len);
+ if (len)
+ memcpy(data_ind->data, data, len);
data_ind->len = len;
- return pcu_sock_send(&bts_gsmnet, msg);
+ return pcu_sock_send(msg);
}
int pcu_tx_rach_ind(uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr,
@@ -533,7 +547,7 @@ int pcu_tx_rach_ind(uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr,
rach_ind->trx_nr = trx_nr;
rach_ind->ts_nr = ts_nr;
- return pcu_sock_send(&bts_gsmnet, msg);
+ return pcu_sock_send(msg);
}
int pcu_tx_time_ind(uint32_t fn)
@@ -555,12 +569,45 @@ int pcu_tx_time_ind(uint32_t fn)
time_ind->fn = fn;
- return pcu_sock_send(&bts_gsmnet, msg);
+ return pcu_sock_send(msg);
+}
+
+int pcu_tx_interf_ind(const struct gsm_bts_trx *trx, uint32_t fn)
+{
+ struct gsm_pcu_if_interf_ind *interf_ind;
+ struct gsm_pcu_if *pcu_prim;
+ struct msgb *msg;
+ unsigned int tn;
+
+ msg = pcu_msgb_alloc(PCU_IF_MSG_INTERF_IND, trx->bts->nr);
+ if (!msg)
+ return -ENOMEM;
+ pcu_prim = (struct gsm_pcu_if *) msg->data;
+ interf_ind = &pcu_prim->u.interf_ind;
+
+ interf_ind->trx_nr = trx->nr;
+ interf_ind->fn = fn;
+
+ for (tn = 0; tn < ARRAY_SIZE(trx->ts); tn++) {
+ const struct gsm_bts_trx_ts *ts = &trx->ts[tn];
+ const struct gsm_lchan *lchan = &ts->lchan[0];
+
+ if (ts->mo.nm_state.operational != NM_OPSTATE_ENABLED)
+ continue;
+ if (ts->mo.nm_state.availability != NM_AVSTATE_OK)
+ continue;
+ if (ts_pchan(ts) != GSM_PCHAN_PDCH)
+ continue;
+
+ interf_ind->interf[tn] = -1 * lchan->meas.interf_meas_avg_dbm;
+ }
+
+ return pcu_sock_send(msg);
}
int pcu_tx_pag_req(const uint8_t *identity_lv, uint8_t chan_needed)
{
- struct pcu_sock_state *state = bts_gsmnet.pcu_state;
+ struct pcu_sock_state *state = g_bts_sm->gprs.pcu_state;
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_pag_req *pag_req;
@@ -588,34 +635,31 @@ int pcu_tx_pag_req(const uint8_t *identity_lv, uint8_t chan_needed)
pag_req->chan_needed = chan_needed;
memcpy(pag_req->identity_lv, identity_lv, identity_lv[0] + 1);
- return pcu_sock_send(&bts_gsmnet, msg);
+ return pcu_sock_send(msg);
}
-int pcu_tx_pch_data_cnf(uint32_t fn, uint8_t *data, uint8_t len)
+int pcu_tx_data_cnf(uint32_t msg_id, uint8_t sapi)
{
- struct gsm_network *net = &bts_gsmnet;
struct gsm_bts *bts;
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
- struct gsm_pcu_if_data *data_cnf;
/* FIXME: allow multiple BTS */
- bts = llist_entry(net->bts_list.next, struct gsm_bts, list);
+ bts = llist_entry(g_bts_sm->bts_list.next, struct gsm_bts, list);
- LOGP(DPCU, LOGL_DEBUG, "Sending PCH confirm\n");
+ LOGP(DPCU, LOGL_DEBUG, "Sending DATA.cnf: sapi=%s msg_id=%08x\n",
+ sapi_string[sapi], msg_id);
- msg = pcu_msgb_alloc(PCU_IF_MSG_DATA_CNF, bts->nr);
+ msg = pcu_msgb_alloc(PCU_IF_MSG_DATA_CNF_2, bts->nr);
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->data;
- data_cnf = &pcu_prim->u.data_cnf;
-
- data_cnf->sapi = PCU_IF_SAPI_PCH;
- data_cnf->fn = fn;
- memcpy(data_cnf->data, data, len);
- data_cnf->len = len;
+ pcu_prim->u.data_cnf2 = (struct gsm_pcu_if_data_cnf) {
+ .sapi = sapi,
+ .msg_id = msg_id,
+ };
- return pcu_sock_send(&bts_gsmnet, msg);
+ return pcu_sock_send(msg);
}
/* forward data from a RR GPRS SUSPEND REQ towards PCU */
@@ -632,7 +676,7 @@ int pcu_tx_susp_req(struct gsm_lchan *lchan, uint32_t tlli, const uint8_t *ra_id
memcpy(pcu_prim->u.susp_req.ra_id, ra_id, sizeof(pcu_prim->u.susp_req.ra_id));
pcu_prim->u.susp_req.cause = cause;
- return pcu_sock_send(&bts_gsmnet, msg);
+ return pcu_sock_send(msg);
}
static int pcu_rx_data_req(struct gsm_bts *bts, uint8_t msg_type,
@@ -650,22 +694,45 @@ static int pcu_rx_data_req(struct gsm_bts *bts, uint8_t msg_type,
osmo_hexdump(data_req->data, data_req->len));
switch (data_req->sapi) {
- case PCU_IF_SAPI_PCH:
- paging_add_imm_ass(bts->paging_state, data_req->data, data_req->len);
+ case PCU_IF_SAPI_PCH_2:
+ {
+ const struct gsm_pcu_if_pch *gsm_pcu_if_pch;
+
+ if (OSMO_UNLIKELY(data_req->len != sizeof(*gsm_pcu_if_pch))) {
+ LOGP(DPCU, LOGL_ERROR, "Rx malformed DATA.req for PCH\n");
+ rc = -EINVAL;
+ break;
+ }
+
+ gsm_pcu_if_pch = (struct gsm_pcu_if_pch *)data_req->data;
+ rc = paging_add_macblock(bts->paging_state, gsm_pcu_if_pch->msg_id,
+ gsm_pcu_if_pch->imsi, gsm_pcu_if_pch->confirm, gsm_pcu_if_pch->data);
break;
- case PCU_IF_SAPI_AGCH:
- msg = msgb_alloc(data_req->len, "pcu_agch");
+ }
+ case PCU_IF_SAPI_AGCH_2:
+ {
+ const struct gsm_pcu_if_agch *gsm_pcu_if_agch;
+ struct bts_agch_msg_cb *msg_cb;
+
+ gsm_pcu_if_agch = (struct gsm_pcu_if_agch *)data_req->data;
+
+ msg = msgb_alloc(GSM_MACBLOCK_LEN, "pcu_agch");
if (!msg) {
rc = -ENOMEM;
break;
}
- msg->l3h = msgb_put(msg, data_req->len);
- memcpy(msg->l3h, data_req->data, data_req->len);
+ msg->l3h = msgb_put(msg, GSM_MACBLOCK_LEN);
+ memcpy(msg->l3h, gsm_pcu_if_agch->data, GSM_MACBLOCK_LEN);
+
+ msg_cb = (struct bts_agch_msg_cb *) msg->cb;
+ msg_cb->confirm = gsm_pcu_if_agch->confirm;
+ msg_cb->msg_id = gsm_pcu_if_agch->msg_id;
if (bts_agch_enqueue(bts, msg) < 0) {
msgb_free(msg);
rc = -EIO;
}
break;
+ }
case PCU_IF_SAPI_PDTCH:
case PCU_IF_SAPI_PTCCH:
trx = gsm_bts_trx_num(bts, data_req->trx_nr);
@@ -725,24 +792,74 @@ static int pcu_rx_pag_req(struct gsm_bts *bts, uint8_t msg_type,
return rc;
}
-int pcu_tx_si13(const struct gsm_bts *bts, bool enable)
+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 = gsm_bts_trx_num(bts, 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 */
- int rc = pcu_tx_data_ind(&trx->ts[0], PCU_IF_SAPI_BCCH, 0, 0, 0, GSM_BTS_SI(bts, SYSINFO_TYPE_13),
- enable ? GSM_MACBLOCK_LEN : 0, 0, 0, 0, INT16_MAX);
+ 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 SI13 to PCU: %d\n", rc);
+ 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,
struct gsm_pcu_if_txt_ind *txt)
{
+ int rc;
+
switch (txt->type) {
case PCU_VERSION:
LOGP(DPCU, LOGL_INFO, "OsmoPCU version %s connected\n",
@@ -754,10 +871,10 @@ static int pcu_rx_txt_ind(struct gsm_bts *bts,
regenerate_si3_restoctets(bts);
regenerate_si4_restoctets(bts);
- if (GSM_BTS_HAS_SI(bts, SYSINFO_TYPE_13))
- return pcu_tx_si13(bts, true);
+ rc = pcu_tx_si_all(bts);
+ if (rc < 0)
+ return -EINVAL;
- LOGP(DPCU, LOGL_INFO, "SI13 is not available on PCU connection\n");
break;
case PCU_OML_ALERT:
oml_tx_failure_event_rep(&bts->gprs.cell.mo, NM_SEVER_INDETERMINATE, OSMO_EVT_EXT_ALARM,
@@ -795,7 +912,7 @@ static int pcu_rx_act_req(struct gsm_bts *bts,
gsm_lchant_name(lchan->type));
return -EINVAL;
}
- if (lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH &&
+ if (lchan->ts->pchan == GSM_PCHAN_OSMO_DYN &&
lchan->ts->dyn.pchan_is != GSM_PCHAN_PDCH) {
LOGP(DPCU, LOGL_ERROR,
"%s request, but lchan in dyn TS is not configured as PDCH in lower layers (is %s)\n",
@@ -804,37 +921,61 @@ static int pcu_rx_act_req(struct gsm_bts *bts,
return -EINVAL;
}
if (act_req->activate)
- l1sap_chan_act(trx, gsm_lchan2chan_nr(lchan), NULL);
+ l1sap_chan_act(trx, gsm_lchan2chan_nr(lchan));
else
l1sap_chan_rel(trx, gsm_lchan2chan_nr(lchan));
return 0;
}
-static int pcu_rx(struct gsm_network *net, uint8_t msg_type,
- struct gsm_pcu_if *pcu_prim)
+#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(uint8_t msg_type, struct gsm_pcu_if *pcu_prim, size_t prim_len)
{
int rc = 0;
struct gsm_bts *bts;
+ size_t exp_len;
- if ((bts = gsm_bts_num(net, pcu_prim->bts_nr)) == NULL) {
+ if ((bts = gsm_bts_num(g_bts_sm, pcu_prim->bts_nr)) == NULL) {
LOGP(DPCU, LOGL_ERROR, "Received PCU Prim for non-existent BTS %u\n", pcu_prim->bts_nr);
return -EINVAL;
}
switch (msg_type) {
case PCU_IF_MSG_DATA_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_PAG_REQ:
+ CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.pag_req);
rc = pcu_rx_pag_req(bts, msg_type, &pcu_prim->u.pag_req);
break;
case PCU_IF_MSG_ACT_REQ:
+ CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.act_req);
rc = pcu_rx_act_req(bts, &pcu_prim->u.act_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;
+ case PCU_IF_MSG_CONTAINER:
+ CHECK_IF_MSG_SIZE(prim_len, 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 (prim_len < exp_len) {
+ LOGP(DPCU, LOGL_ERROR, "Received %zu bytes on PCU Socket, but primitive "
+ "container size is %zu, discarding\n", prim_len, exp_len);
+ return -EINVAL;
+ }
+ rc = abis_osmo_pcu_tx_container(bts, &pcu_prim->u.container);
+ break;
default:
LOGP(DPCU, LOGL_ERROR, "Received unknown PCU msg type %d\n",
msg_type);
@@ -849,48 +990,58 @@ static int pcu_rx(struct gsm_network *net, uint8_t msg_type,
*/
struct pcu_sock_state {
- struct gsm_network *net;
struct osmo_fd listen_bfd; /* fd for listen socket */
- struct osmo_fd conn_bfd; /* fd for connection to lcr */
- struct llist_head upqueue; /* queue for sending messages */
+ struct osmo_wqueue upqueue; /* For sending messages; has fd for conn. to PCU */
};
-static int pcu_sock_send(struct gsm_network *net, struct msgb *msg)
+static void pcu_sock_close(struct pcu_sock_state *state);
+
+int pcu_sock_send(struct msgb *msg)
{
- struct pcu_sock_state *state = net->pcu_state;
+ struct pcu_sock_state *state = g_bts_sm->gprs.pcu_state;
struct osmo_fd *conn_bfd;
struct gsm_pcu_if *pcu_prim = (struct gsm_pcu_if *) msg->data;
+ int rc;
if (!state) {
- if (pcu_prim->msg_type != PCU_IF_MSG_TIME_IND)
+ if (pcu_prim->msg_type != PCU_IF_MSG_TIME_IND &&
+ pcu_prim->msg_type != PCU_IF_MSG_INTERF_IND)
LOGP(DPCU, LOGL_INFO, "PCU socket not created, "
"dropping message\n");
msgb_free(msg);
return -EINVAL;
}
- conn_bfd = &state->conn_bfd;
+ conn_bfd = &state->upqueue.bfd;
if (conn_bfd->fd <= 0) {
- if (pcu_prim->msg_type != PCU_IF_MSG_TIME_IND)
+ if (pcu_prim->msg_type != PCU_IF_MSG_TIME_IND &&
+ pcu_prim->msg_type != PCU_IF_MSG_INTERF_IND)
LOGP(DPCU, LOGL_NOTICE, "PCU socket not connected, "
"dropping message\n");
msgb_free(msg);
return -EIO;
}
- msgb_enqueue(&state->upqueue, msg);
- conn_bfd->when |= OSMO_FD_WRITE;
+ rc = osmo_wqueue_enqueue(&state->upqueue, msg);
+ if (rc < 0) {
+ if (rc == -ENOSPC)
+ LOGP(DPCU, LOGL_NOTICE, "PCU not reacting (more than %u messages waiting). Closing connection\n",
+ state->upqueue.max_length);
+ pcu_sock_close(state);
+ msgb_free(msg);
+ return rc;
+ }
return 0;
}
static void pcu_sock_close(struct pcu_sock_state *state)
{
- struct osmo_fd *bfd = &state->conn_bfd;
+ struct osmo_fd *bfd = &state->upqueue.bfd;
struct gsm_bts *bts;
struct gsm_bts_trx *trx;
unsigned int tn;
/* FIXME: allow multiple BTS */
- bts = llist_entry(state->net->bts_list.next, struct gsm_bts, list);
+ bts = llist_entry(g_bts_sm->bts_list.next, struct gsm_bts, list);
LOGP(DPCU, LOGL_NOTICE, "PCU socket has LOST connection\n");
oml_tx_failure_event_rep(&bts->gprs.cell.mo, NM_SEVER_MAJOR, OSMO_EVT_PCU_VERS,
@@ -898,16 +1049,16 @@ static void pcu_sock_close(struct pcu_sock_state *state)
bts->pcu_version[0] = '\0';
+ osmo_fd_unregister(bfd);
close(bfd->fd);
bfd->fd = -1;
- osmo_fd_unregister(bfd);
/* patch SI3 to remove GPRS indicator */
regenerate_si3_restoctets(bts);
regenerate_si4_restoctets(bts);
/* 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, ... */
@@ -930,11 +1081,7 @@ static void pcu_sock_close(struct pcu_sock_state *state)
}
}
- /* flush the queue */
- while (!llist_empty(&state->upqueue)) {
- struct msgb *msg = msgb_dequeue(&state->upqueue);
- msgb_free(msg);
- }
+ osmo_wqueue_clear(&state->upqueue);
}
static int pcu_sock_read(struct osmo_fd *bfd)
@@ -944,7 +1091,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;
@@ -962,14 +1109,14 @@ static int pcu_sock_read(struct osmo_fd *bfd)
goto close;
}
- if (rc < sizeof(*pcu_prim)) {
- LOGP(DPCU, LOGL_ERROR, "Received %d bytes on PCU Socket, but primitive size "
- "is %zu, discarding\n", rc, sizeof(*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 = pcu_rx(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. */
@@ -983,99 +1130,57 @@ close:
return -1;
}
-static int pcu_sock_write(struct osmo_fd *bfd)
+static int pcu_sock_write(struct osmo_fd *bfd, struct msgb *msg)
{
struct pcu_sock_state *state = bfd->data;
int rc;
- while (!llist_empty(&state->upqueue)) {
- struct msgb *msg, *msg2;
- struct gsm_pcu_if *pcu_prim;
-
- /* peek at the beginning of the queue */
- msg = llist_entry(state->upqueue.next, struct msgb, list);
- pcu_prim = (struct gsm_pcu_if *)msg->data;
-
- bfd->when &= ~OSMO_FD_WRITE;
-
- /* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
- if (!msgb_length(msg)) {
- LOGP(DPCU, LOGL_ERROR, "message type (%d) with ZERO "
- "bytes!\n", pcu_prim->msg_type);
- goto dontsend;
- }
-
- /* try to send it over the socket */
- rc = write(bfd->fd, msgb_data(msg), msgb_length(msg));
- if (rc == 0)
- goto close;
- if (rc < 0) {
- if (errno == EAGAIN) {
- bfd->when |= OSMO_FD_WRITE;
- break;
- }
- goto close;
- }
-
-dontsend:
- /* _after_ we send it, we can deueue */
- msg2 = msgb_dequeue(&state->upqueue);
- assert(msg == msg2);
- msgb_free(msg);
+ /* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
+ OSMO_ASSERT(msgb_length(msg) > 0);
+ /* try to send it over the socket */
+ rc = write(bfd->fd, msgb_data(msg), msgb_length(msg));
+ if (OSMO_UNLIKELY(rc == 0))
+ goto close;
+ if (OSMO_UNLIKELY(rc < 0)) {
+ if (errno == EAGAIN)
+ return -EAGAIN;
+ return -1;
}
return 0;
close:
pcu_sock_close(state);
-
return -1;
}
-static int pcu_sock_cb(struct osmo_fd *bfd, unsigned int flags)
-{
- int rc = 0;
-
- if (flags & OSMO_FD_READ)
- rc = pcu_sock_read(bfd);
- if (rc < 0)
- return rc;
-
- if (flags & OSMO_FD_WRITE)
- rc = pcu_sock_write(bfd);
-
- return rc;
-}
-
/* accept connection coming from PCU */
static int pcu_sock_accept(struct osmo_fd *bfd, unsigned int flags)
{
struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data;
- struct osmo_fd *conn_bfd = &state->conn_bfd;
+ struct osmo_fd *conn_bfd = &state->upqueue.bfd;
struct sockaddr_un un_addr;
socklen_t len;
- int rc;
+ int fd;
len = sizeof(un_addr);
- rc = accept(bfd->fd, (struct sockaddr *) &un_addr, &len);
- if (rc < 0) {
+ fd = accept(bfd->fd, (struct sockaddr *)&un_addr, &len);
+ if (fd < 0) {
LOGP(DPCU, LOGL_ERROR, "Failed to accept a new connection\n");
return -1;
}
if (conn_bfd->fd >= 0) {
- LOGP(DPCU, LOGL_NOTICE, "PCU connects but we already have "
- "another active connection ?!?\n");
+ 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;
- close(rc);
+ osmo_fd_read_disable(&state->listen_bfd);
+ close(fd);
return 0;
}
- osmo_fd_setup(conn_bfd, rc, OSMO_FD_READ, pcu_sock_cb, state, 0);
+ osmo_fd_setup(conn_bfd, fd, OSMO_FD_READ, osmo_wqueue_bfd_cb, state, 0);
if (osmo_fd_register(conn_bfd) != 0) {
- LOGP(DPCU, LOGL_ERROR, "Failed to register new connection "
- "fd\n");
+ LOGP(DPCU, LOGL_ERROR, "Failed to register new connection fd\n");
close(conn_bfd->fd);
conn_bfd->fd = -1;
return -1;
@@ -1089,19 +1194,20 @@ static int pcu_sock_accept(struct osmo_fd *bfd, unsigned int flags)
return 0;
}
-int pcu_sock_init(const char *path)
+int pcu_sock_init(const char *path, int qlength_max)
{
struct pcu_sock_state *state;
struct osmo_fd *bfd;
int rc;
- state = talloc_zero(NULL, struct pcu_sock_state);
+ state = talloc_zero(g_bts_sm, struct pcu_sock_state);
if (!state)
return -ENOMEM;
- INIT_LLIST_HEAD(&state->upqueue);
- state->net = &bts_gsmnet;
- state->conn_bfd.fd = -1;
+ osmo_wqueue_init(&state->upqueue, qlength_max);
+ state->upqueue.read_cb = pcu_sock_read;
+ state->upqueue.write_cb = pcu_sock_write;
+ state->upqueue.bfd.fd = -1;
bfd = &state->listen_bfd;
@@ -1126,39 +1232,38 @@ int pcu_sock_init(const char *path)
osmo_signal_register_handler(SS_GLOBAL, pcu_if_signal_cb, NULL);
- bts_gsmnet.pcu_state = state;
+ g_bts_sm->gprs.pcu_state = state;
- LOGP(DPCU, LOGL_INFO, "Started listening on PCU socket: %s\n", path);
+ LOGP(DPCU, LOGL_INFO, "Started listening on PCU socket (PCU IF v%u): %s\n", PCU_IF_VERSION, path);
return 0;
}
void pcu_sock_exit(void)
{
- struct pcu_sock_state *state = bts_gsmnet.pcu_state;
+ struct pcu_sock_state *state = g_bts_sm->gprs.pcu_state;
struct osmo_fd *bfd, *conn_bfd;
if (!state)
return;
osmo_signal_unregister_handler(SS_GLOBAL, pcu_if_signal_cb, NULL);
- conn_bfd = &state->conn_bfd;
+ conn_bfd = &state->upqueue.bfd;
if (conn_bfd->fd > 0)
pcu_sock_close(state);
bfd = &state->listen_bfd;
close(bfd->fd);
osmo_fd_unregister(bfd);
talloc_free(state);
- bts_gsmnet.pcu_state = NULL;
+ g_bts_sm->gprs.pcu_state = NULL;
}
bool pcu_connected(void) {
- struct gsm_network *net = &bts_gsmnet;
- struct pcu_sock_state *state = net->pcu_state;
+ struct pcu_sock_state *state = g_bts_sm->gprs.pcu_state;
if (!state)
return false;
- if (state->conn_bfd.fd <= 0)
+ if (state->upqueue.bfd.fd <= 0)
return false;
return true;
}