aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhilipp Maier <pmaier@sysmocom.de>2023-03-01 13:21:14 +0100
committerPhilipp Maier <pmaier@sysmocom.de>2023-03-20 10:44:22 +0100
commit0625172265d3cbd40effe64abf630c06a50f6679 (patch)
tree3c5dc5960d38f3b51a0e4ad9a299c76d80e3c9fc
parent10ae64a777e0b935be400bf2f6df4f5b5916f3af (diff)
pcu_sock: handle multiple BTSs with one BSC co-located PCU (in theory)
The current PCU implementation has never been tested with multiple BTS attached to it. This is due to the fact that it has been used exclusively in an BTS co-located setup where naturally only one BTS is present. The PCU sock protocol supports multiple BTSs in theory and we should handle this correctly. Related: OS#5198 Change-Id: I0b42c2c130106f6ffca2dd08d079e1a7bda41f0b
-rw-r--r--include/osmocom/bsc/bts.h4
-rw-r--r--include/osmocom/bsc/gsm_data.h4
-rw-r--r--include/osmocom/bsc/pcu_if.h8
-rw-r--r--src/osmo-bsc/bsc_vty.c45
-rw-r--r--src/osmo-bsc/bts_vty.c27
-rw-r--r--src/osmo-bsc/pcu_sock.c135
-rw-r--r--src/osmo-bsc/timeslot_fsm.c2
7 files changed, 127 insertions, 98 deletions
diff --git a/include/osmocom/bsc/bts.h b/include/osmocom/bsc/bts.h
index ccca1860f..20ed0893b 100644
--- a/include/osmocom/bsc/bts.h
+++ b/include/osmocom/bsc/bts.h
@@ -600,10 +600,6 @@ struct gsm_bts {
/* osmux config: */
enum osmux_usage use_osmux;
- /* PCU socket state */
- char *pcu_sock_path;
- struct pcu_sock_state *pcu_state;
-
struct rate_ctr_group *bts_ctrs;
struct osmo_stat_item_group *bts_statg;
diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h
index 25e634ab8..777944908 100644
--- a/include/osmocom/bsc/gsm_data.h
+++ b/include/osmocom/bsc/gsm_data.h
@@ -1003,6 +1003,10 @@ struct gsm_network {
struct chan_counts chan_counts;
struct all_allocated all_allocated;
+
+ /* PCU socket state */
+ char *pcu_sock_path;
+ struct pcu_sock_state *pcu_state;
};
struct gsm_audio_support {
diff --git a/include/osmocom/bsc/pcu_if.h b/include/osmocom/bsc/pcu_if.h
index 2a2afa869..34349dc22 100644
--- a/include/osmocom/bsc/pcu_if.h
+++ b/include/osmocom/bsc/pcu_if.h
@@ -8,14 +8,14 @@ extern int pcu_direct;
#define PCUIF_HDR_SIZE (sizeof(struct gsm_pcu_if) - sizeof(((struct gsm_pcu_if *)0)->u))
struct pcu_sock_state {
- struct gsm_network *net;
+ struct gsm_network *net; /* backpointer to GSM network */
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 */
};
/* Check if BTS has a PCU connection */
-bool pcu_connected(struct gsm_bts *bts);
+bool pcu_connected(struct gsm_network *net);
/* PCU relevant information has changed; Inform PCU (if connected) */
void pcu_info_update(struct gsm_bts *bts);
@@ -32,9 +32,9 @@ int pcu_tx_imm_ass_sent(struct gsm_bts *bts, uint32_t tlli);
int pcu_tx_imm_ass_sent(struct gsm_bts *bts, uint32_t tlli);
/* Open connection to PCU */
-int pcu_sock_init(const char *path, struct gsm_bts *bts);
+int pcu_sock_init(struct gsm_network *net);
/* Close connection to PCU */
-void pcu_sock_exit(struct gsm_bts *bts);
+void pcu_sock_exit(struct gsm_network *net);
#endif /* _PCU_IF_H */
diff --git a/src/osmo-bsc/bsc_vty.c b/src/osmo-bsc/bsc_vty.c
index 467301baf..f1bd7c485 100644
--- a/src/osmo-bsc/bsc_vty.c
+++ b/src/osmo-bsc/bsc_vty.c
@@ -67,6 +67,7 @@
#include <osmocom/bsc/bssmap_reset.h>
#include <osmocom/bsc/bsc_msc_data.h>
#include <osmocom/bsc/lchan.h>
+#include <osmocom/bsc/pcu_if.h>
#include <inttypes.h>
@@ -218,6 +219,9 @@ static void net_dump_vty(struct vty *vty, struct gsm_network *net)
vty_out(vty, " Last RF Lock Command: %s%s",
net->rf_ctrl->last_rf_lock_ctrl_command,
VTY_NEWLINE);
+
+ if (net->pcu_sock_path)
+ vty_out(vty, " PCU Socket Path: %s%s", net->pcu_sock_path, VTY_NEWLINE);
}
DEFUN(bsc_show_net, bsc_show_net_cmd, "show network",
@@ -406,6 +410,9 @@ static int config_write_net(struct vty *vty)
vty_out(vty, "%s", VTY_NEWLINE);
}
+ if (gsmnet->pcu_sock_path)
+ vty_out(vty, " pcu-socket %s%s", gsmnet->pcu_sock_path, VTY_NEWLINE);
+
neighbor_ident_vty_write_network(vty, " ");
mgcp_client_pool_config_write(vty, " ");
@@ -2460,6 +2467,42 @@ DEFUN(cfg_net_allow_unusable_timeslots, cfg_net_allow_unusable_timeslots_cmd,
return CMD_SUCCESS;
}
+DEFUN_ATTR(cfg_net_pcu_sock,
+ cfg_net_pcu_sock_cmd,
+ "pcu-socket PATH",
+ "PCU Socket Path for using OsmoPCU co-located with BSC\n"
+ "Path in the file system for the unix-domain PCU socket\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_network *net = gsmnet_from_vty(vty);
+ int rc;
+
+ osmo_talloc_replace_string(net, &net->pcu_sock_path, argv[0]);
+ pcu_sock_exit(net);
+ rc = pcu_sock_init(net);
+ if (rc < 0) {
+ vty_out(vty, "%% Error creating PCU socket `%s'%s",
+ net->pcu_sock_path, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_net_no_pcu_sock,
+ cfg_net_no_pcu_sock_cmd,
+ "no pcu-socket",
+ NO_STR "Disable BSC co-located PCU\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_network *net = gsmnet_from_vty(vty);
+
+ pcu_sock_exit(net);
+ talloc_free(net->pcu_sock_path);
+ net->pcu_sock_path = NULL;
+ return CMD_SUCCESS;
+}
+
static struct bsc_msc_data *bsc_msc_data(struct vty *vty)
{
return vty->index;
@@ -3534,6 +3577,8 @@ int bsc_vty_init(struct gsm_network *network)
install_element(GSMNET_NODE, &cfg_net_meas_feed_scenario_cmd);
install_element(GSMNET_NODE, &cfg_net_timer_cmd);
install_element(GSMNET_NODE, &cfg_net_allow_unusable_timeslots_cmd);
+ install_element(GSMNET_NODE, &cfg_net_pcu_sock_cmd);
+ install_element(GSMNET_NODE, &cfg_net_no_pcu_sock_cmd);
/* Timer configuration commands (generic osmo_tdef API) */
osmo_tdef_vty_groups_init(GSMNET_NODE, bsc_tdef_group);
diff --git a/src/osmo-bsc/bts_vty.c b/src/osmo-bsc/bts_vty.c
index 08166ca40..e02c2d5b0 100644
--- a/src/osmo-bsc/bts_vty.c
+++ b/src/osmo-bsc/bts_vty.c
@@ -2191,28 +2191,6 @@ DEFUN_USRATTR(cfg_bts_si5_neigh,
return CMD_SUCCESS;
}
-DEFUN_ATTR(cfg_bts_pcu_sock,
- cfg_bts_pcu_sock_cmd,
- "pcu-socket PATH",
- "PCU Socket Path for using OsmoPCU co-located with BSC (legacy BTS)\n"
- "Path in the file system for the unix-domain PCU socket\n",
- CMD_ATTR_IMMEDIATE)
-{
- struct gsm_bts *bts = vty->index;
- int rc;
-
- osmo_talloc_replace_string(bts, &bts->pcu_sock_path, argv[0]);
- pcu_sock_exit(bts);
- rc = pcu_sock_init(bts->pcu_sock_path, bts);
- if (rc < 0) {
- vty_out(vty, "%% Error creating PCU socket `%s' for BTS %u%s",
- bts->pcu_sock_path, bts->nr, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- return CMD_SUCCESS;
-}
-
DEFUN_ATTR(cfg_bts_acc_rotate,
cfg_bts_acc_rotate_cmd,
"access-control-class-rotate <0-10>",
@@ -3979,8 +3957,6 @@ void bts_dump_vty(struct vty *vty, struct gsm_bts *bts)
bts->early_classmark_allowed_3g && !bts->early_classmark_allowed ?
" (forbidden by 2G bit)" : "",
VTY_NEWLINE);
- if (bts->pcu_sock_path)
- vty_out(vty, " PCU Socket Path: %s%s", bts->pcu_sock_path, VTY_NEWLINE);
if (is_ipaccess_bts(bts))
vty_out(vty, " Unit ID: %u/%u/0, OML Stream ID 0x%02x%s",
bts->ip_access.site_id, bts->ip_access.bts_id,
@@ -4618,8 +4594,6 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
vty_out(vty, " depends-on-bts %d%s", bts_nr, VTY_NEWLINE);
}
}
- if (bts->pcu_sock_path)
- vty_out(vty, " pcu-socket %s%s", bts->pcu_sock_path, VTY_NEWLINE);
ho_vty_write_bts(vty, bts);
@@ -4857,7 +4831,6 @@ int bts_vty_init(void)
install_element(BTS_NODE, &cfg_bts_osmux_cmd);
install_element(BTS_NODE, &cfg_bts_mgw_pool_target_cmd);
install_element(BTS_NODE, &cfg_bts_no_mgw_pool_target_cmd);
- install_element(BTS_NODE, &cfg_bts_pcu_sock_cmd);
install_element(BTS_NODE, &cfg_bts_acc_rotate_cmd);
install_element(BTS_NODE, &cfg_bts_acc_rotate_quantum_cmd);
install_element(BTS_NODE, &cfg_bts_acc_ramping_cmd);
diff --git a/src/osmo-bsc/pcu_sock.c b/src/osmo-bsc/pcu_sock.c
index 165fca4cc..dba502f49 100644
--- a/src/osmo-bsc/pcu_sock.c
+++ b/src/osmo-bsc/pcu_sock.c
@@ -46,7 +46,7 @@
#include <osmocom/bsc/bts_sm.h>
#include <osmocom/bsc/timeslot_fsm.h>
-static int pcu_sock_send(struct gsm_bts *bts, struct msgb *msg);
+static int pcu_sock_send(struct gsm_network *net, struct msgb *msg);
static const char *sapi_string[] = {
[PCU_IF_SAPI_RACH] = "RACH",
@@ -59,13 +59,9 @@ static const char *sapi_string[] = {
[PCU_IF_SAPI_PCH_DT] = "PCH_DT",
};
-bool pcu_connected(struct gsm_bts *bts)
+bool pcu_connected(struct gsm_network *net)
{
- struct pcu_sock_state *state = bts->pcu_state;
-
- /* BSC co-located PCU is only supported for Ericsson RBS */
- if (!is_ericsson_bts(bts))
- return false;
+ struct pcu_sock_state *state = net->pcu_state;
if (!state)
return false;
@@ -305,7 +301,7 @@ static int pcu_tx_info_ind(struct gsm_bts *bts)
info_ind_fill_trx(&info_ind->trx[trx->nr], trx);
}
- return pcu_sock_send(bts, msg);
+ return pcu_sock_send(bts->network, msg);
}
static int pcu_tx_e1_ccu_ind(struct gsm_bts *bts)
@@ -348,7 +344,7 @@ static int pcu_tx_e1_ccu_ind(struct gsm_bts *bts)
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);
+ rc = pcu_sock_send(bts->network, msg);
if (rc < 0)
return -EINVAL;
}
@@ -360,7 +356,7 @@ static int pcu_tx_e1_ccu_ind(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->network)) {
if (bsc_co_located_pcu(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. */
@@ -403,7 +399,7 @@ static int pcu_tx_data_ind(struct gsm_bts_trx_ts *ts, uint8_t sapi, uint32_t fn,
memcpy(data_ind->data, data, len);
data_ind->len = len;
- return pcu_sock_send(bts, msg);
+ return pcu_sock_send(bts->network, msg);
}
/* Forward rach indication to PCU */
@@ -415,7 +411,7 @@ int pcu_tx_rach_ind(struct gsm_bts *bts, int16_t qta, uint16_t ra, uint32_t fn,
struct gsm_pcu_if_rach_ind *rach_ind;
/* Bail if no PCU is connected */
- if (!pcu_connected(bts)) {
+ if (!pcu_connected(bts->network)) {
LOG_BTS(bts, DRSL, LOGL_ERROR, "CHAN RQD(GPRS) but PCU not connected!\n");
return -ENODEV;
}
@@ -436,7 +432,7 @@ int pcu_tx_rach_ind(struct gsm_bts *bts, int16_t qta, uint16_t ra, uint32_t fn,
rach_ind->is_11bit = is_11bit;
rach_ind->burst_type = burst_type;
- return pcu_sock_send(bts, msg);
+ return pcu_sock_send(bts->network, msg);
}
/* Confirm the sending of an immediate assignment to the pcu */
@@ -457,7 +453,7 @@ int pcu_tx_imm_ass_sent(struct gsm_bts *bts, uint32_t tlli)
data_cnf_dt->sapi = PCU_IF_SAPI_PCH;
data_cnf_dt->tlli = tlli;
- return pcu_sock_send(bts, msg);
+ return pcu_sock_send(bts->network, msg);
}
/* we need to decode the raw RR paging message (see PCU code
@@ -693,8 +689,9 @@ static int pcu_rx(struct gsm_network *net, uint8_t msg_type,
int rc = 0;
struct gsm_bts *bts;
- /* FIXME: allow multiple BTS */
- bts = llist_entry(net->bts_list.next, struct gsm_bts, list);
+ bts = gsm_bts_num(net, pcu_prim->bts_nr);
+ if (!bts)
+ return -EINVAL;
switch (msg_type) {
case PCU_IF_MSG_DATA_REQ:
@@ -719,9 +716,9 @@ static int pcu_rx(struct gsm_network *net, uint8_t msg_type,
* PCU socket interface
*/
-static int pcu_sock_send(struct gsm_bts *bts, struct msgb *msg)
+static int pcu_sock_send(struct gsm_network *net, struct msgb *msg)
{
- struct pcu_sock_state *state = bts->pcu_state;
+ struct pcu_sock_state *state = net->pcu_state;
struct osmo_fd *conn_bfd;
struct gsm_pcu_if *pcu_prim = (struct gsm_pcu_if *) msg->data;
@@ -746,25 +743,11 @@ static int pcu_sock_send(struct gsm_bts *bts, struct msgb *msg)
return 0;
}
-static void pcu_sock_close(struct pcu_sock_state *state)
+static void pdch_deact_bts(struct gsm_bts *bts)
{
- struct osmo_fd *bfd = &state->conn_bfd;
- struct gsm_bts *bts;
struct gsm_bts_trx *trx;
int j;
- /* FIXME: allow multiple BTS */
- bts = llist_entry(state->net->bts_list.next, struct gsm_bts, list);
-
- LOGP(DPCU, LOGL_NOTICE, "PCU socket has LOST connection\n");
-
- osmo_fd_unregister(bfd);
- close(bfd->fd);
- bfd->fd = -1;
-
- /* re-enable the generation of ACCEPT for new connections */
- osmo_fd_read_enable(&state->listen_bfd);
-
#if 0
/* remove si13, ... */
bts->si_valid &= ~(1 << SYSINFO_TYPE_13);
@@ -783,6 +766,27 @@ static void pcu_sock_close(struct pcu_sock_state *state)
}
}
}
+}
+
+static void pcu_sock_close(struct pcu_sock_state *state)
+{
+ struct osmo_fd *bfd = &state->conn_bfd;
+ struct gsm_bts *bts;
+
+ LOGP(DPCU, LOGL_NOTICE, "PCU socket has LOST connection\n");
+
+ osmo_fd_unregister(bfd);
+ close(bfd->fd);
+ bfd->fd = -1;
+
+ /* re-enable the generation of ACCEPT for new connections */
+ osmo_fd_read_enable(&state->listen_bfd);
+
+ /* Disable all PDCHs on all BTSs that are served by the PCU */
+ llist_for_each_entry(bts, &state->net->bts_list, list) {
+ if (bsc_co_located_pcu(bts))
+ pdch_deact_bts(bts);
+ }
/* flush the queue */
while (!llist_empty(&state->upqueue)) {
@@ -900,6 +904,24 @@ static int pcu_sock_cb(struct osmo_fd *bfd, unsigned int flags)
return rc;
}
+static void pdch_act_bts(struct gsm_bts *bts)
+{
+ struct gsm_bts_trx *trx;
+ int j;
+
+ /* activate PDCH */
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ for (j = 0; j < ARRAY_SIZE(trx->ts); j++) {
+ struct gsm_bts_trx_ts *ts = &trx->ts[j];
+ /* (See comment in pdch_deact_bts above) */
+ if (ts->mo.nm_state.operational == NM_OPSTATE_ENABLED
+ && ts->pchan_on_init == GSM_PCHAN_OSMO_DYN) {
+ ts_pdch_act(ts);
+ }
+ }
+ }
+}
+
/* accept connection coming from PCU */
static int pcu_sock_accept(struct osmo_fd *bfd, unsigned int flags)
{
@@ -907,23 +929,18 @@ static int pcu_sock_accept(struct osmo_fd *bfd, unsigned int flags)
struct osmo_fd *conn_bfd = &state->conn_bfd;
struct sockaddr_un un_addr;
struct gsm_bts *bts;
- struct gsm_bts_trx *trx;
- int j;
socklen_t len;
int fd;
- /* FIXME: allow multiple BTS */
- bts = llist_entry(state->net->bts_list.next, struct gsm_bts, list);
-
len = sizeof(un_addr);
fd = accept(bfd->fd, (struct sockaddr *)&un_addr, &len);
if (fd < 0) {
- LOG_BTS(bts, DPCU, LOGL_ERROR, "Failed to accept a new connection\n");
+ LOGP(DPCU, LOGL_ERROR, "Failed to accept a new connection\n");
return -1;
}
if (conn_bfd->fd >= 0) {
- LOG_BTS(bts, 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 */
osmo_fd_read_disable(&state->listen_bfd);
close(fd);
@@ -933,31 +950,25 @@ static int pcu_sock_accept(struct osmo_fd *bfd, unsigned int flags)
osmo_fd_setup(conn_bfd, fd, OSMO_FD_READ, pcu_sock_cb, state, 0);
if (osmo_fd_register(conn_bfd) != 0) {
- LOG_BTS(bts, 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;
}
- LOG_BTS(bts, DPCU, LOGL_NOTICE, "PCU socket connected to external PCU\n");
+ LOGP(DPCU, LOGL_NOTICE, "PCU socket connected to external PCU\n");
- /* activate PDCH */
- llist_for_each_entry(trx, &bts->trx_list, list) {
- for (j = 0; j < ARRAY_SIZE(trx->ts); j++) {
- struct gsm_bts_trx_ts *ts = &trx->ts[j];
- /* (See comment in pcu_sock_close above) */
- if (ts->mo.nm_state.operational == NM_OPSTATE_ENABLED
- && ts->pchan_on_init == GSM_PCHAN_OSMO_DYN) {
- ts_pdch_act(ts);
- }
- }
+ /* Activate all PDCHs on all BTSs that are served by the PCU */
+ llist_for_each_entry(bts, &state->net->bts_list, list) {
+ if (bsc_co_located_pcu(bts))
+ pdch_act_bts(bts);
}
return 0;
}
/* Open connection to PCU */
-int pcu_sock_init(const char *path, struct gsm_bts *bts)
+int pcu_sock_init(struct gsm_network *net)
{
struct pcu_sock_state *state;
struct osmo_fd *bfd;
@@ -968,14 +979,14 @@ int pcu_sock_init(const char *path, struct gsm_bts *bts)
return -ENOMEM;
INIT_LLIST_HEAD(&state->upqueue);
- state->net = bts->network;
+ state->net = net;
state->conn_bfd.fd = -1;
bfd = &state->listen_bfd;
- rc = osmo_sock_unix_init(SOCK_SEQPACKET, 0, path, OSMO_SOCK_F_BIND);
+ rc = osmo_sock_unix_init(SOCK_SEQPACKET, 0, net->pcu_sock_path, OSMO_SOCK_F_BIND);
if (rc < 0) {
- LOG_BTS(bts, DPCU, LOGL_ERROR, "Could not create unix socket: %s\n",
+ LOGP(DPCU, LOGL_ERROR, "Could not create unix socket: %s\n",
strerror(errno));
talloc_free(state);
return -1;
@@ -985,23 +996,23 @@ int pcu_sock_init(const char *path, struct gsm_bts *bts)
rc = osmo_fd_register(bfd);
if (rc < 0) {
- LOG_BTS(bts, DPCU, LOGL_ERROR, "Could not register listen fd: %d\n",
+ LOGP(DPCU, LOGL_ERROR, "Could not register listen fd: %d\n",
rc);
close(bfd->fd);
talloc_free(state);
return rc;
}
- LOG_BTS(bts, DPCU, LOGL_INFO, "Started listening on PCU socket: %s\n", path);
+ LOGP(DPCU, LOGL_INFO, "Started listening on PCU socket: %s\n", net->pcu_sock_path);
- bts->pcu_state = state;
+ net->pcu_state = state;
return 0;
}
/* Close connection to PCU */
-void pcu_sock_exit(struct gsm_bts *bts)
+void pcu_sock_exit(struct gsm_network *net)
{
- struct pcu_sock_state *state = bts->pcu_state;
+ struct pcu_sock_state *state = net->pcu_state;
struct osmo_fd *bfd, *conn_bfd;
if (!state)
@@ -1014,5 +1025,5 @@ void pcu_sock_exit(struct gsm_bts *bts)
osmo_fd_unregister(bfd);
close(bfd->fd);
talloc_free(state);
- bts->pcu_state = NULL;
+ net->pcu_state = NULL;
}
diff --git a/src/osmo-bsc/timeslot_fsm.c b/src/osmo-bsc/timeslot_fsm.c
index 790ad7745..9618f9280 100644
--- a/src/osmo-bsc/timeslot_fsm.c
+++ b/src/osmo-bsc/timeslot_fsm.c
@@ -341,7 +341,7 @@ static void ts_fsm_unused_pdch_act(struct osmo_fsm_inst *fi)
return;
}
- if (bsc_co_located_pcu(bts) && !pcu_connected(bts)) {
+ if (bsc_co_located_pcu(bts) && !pcu_connected(bts->network)) {
LOG_TS(ts, LOGL_DEBUG, "PCU not connected: not activating PDCH.\n");
return;
}