diff options
Diffstat (limited to 'src/common/pcu_sock.c')
-rw-r--r-- | src/common/pcu_sock.c | 712 |
1 files changed, 474 insertions, 238 deletions
diff --git a/src/common/pcu_sock.c b/src/common/pcu_sock.c index ba9e1721..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,37 +28,39 @@ #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> #include <osmo-bts/gsm_data.h> #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(struct gsm_bts_trx *trx); +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 */ @@ -83,10 +81,12 @@ struct msgb *pcu_msgb_alloc(uint8_t msg_type, uint8_t bts_nr) return msg; } -static bool ts_should_be_pdch(struct gsm_bts_trx_ts *ts) { - if (ts->pchan == GSM_PCHAN_PDCH) +static bool ts_should_be_pdch(const struct gsm_bts_trx_ts *ts) +{ + switch (ts->pchan) { + case GSM_PCHAN_PDCH: return true; - if (ts->pchan == GSM_PCHAN_TCH_F_PDCH) { + case GSM_PCHAN_TCH_F_PDCH: /* When we're busy deactivating the PDCH, we first set * DEACT_PENDING, tell the PCU about it and wait for a * response. So DEACT_PENDING means "no PDCH" to the PCU. @@ -97,35 +97,174 @@ static bool ts_should_be_pdch(struct gsm_bts_trx_ts *ts) { return !(ts->flags & TS_F_PDCH_DEACT_PENDING); else return (ts->flags & TS_F_PDCH_ACT_PENDING); - } - if (ts->pchan == 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 - * response. So only care about dyn.pchan_want here. + * response. To make it available to PCU, we want to make sure + * it's already configured by phy (pchan_is==PDCH) and that we + * are not in progress of removing it (pchan_want=None). */ - return ts->dyn.pchan_want == GSM_PCHAN_PDCH; + + return ts->dyn.pchan_is == GSM_PCHAN_PDCH && ts->dyn.pchan_want == GSM_PCHAN_PDCH; + default: + return false; + } +} + +/* As a BTS, we do not (and neither need to) know the Mobile Allocation, because + * in CS domain it's responsibility of the BSC to encode RR messages containing + * this IE. However, a BTS co-located PCU needs to know all hopping parameters, + * including the Mobile Allocation, because it's responsible for encoding of the + * packet resource assignment messages. + * + * This function, similar to generate_ma_for_ts() in osmo-bsc, computes the + * Mobile Allocation bit-mask and populates the given part of INFO.ind with + * the hopping parameters for the given timeslot. */ +static void info_ind_fill_fhp(struct gsm_pcu_if_info_trx_ts *ts_info, + const struct gsm_bts_trx_ts *ts) +{ + const struct gsm_bts *bts = ts->trx->bts; + const struct gsm_bts_trx *trx; + uint8_t ca_buf[1024 / 8] = { 0 }; + uint8_t sa_buf[1024 / 8] = { 0 }; + struct bitvec ca, sa, ma; + unsigned int i; + + ts_info->maio = ts->hopping.maio; + ts_info->hsn = ts->hopping.hsn; + ts_info->hopping = 0x01; + + /* Cell Allocation bit-mask */ + ca = (struct bitvec) { + .data_len = sizeof(ca_buf), + .data = &ca_buf[0], + }; + + llist_for_each_entry(trx, &bts->trx_list, list) { + /* Skip non-provisioned transceivers */ + if (trx->mo.nm_attr == NULL) { + LOGPTRX(trx, DPCU, LOGL_NOTICE, "not (yet) provisioned\n"); + continue; + } + + bitvec_set_bit_pos(&ca, trx->arfcn, ONE); + ts_info->ma_bit_len++; + } + + /* Slot Allocation bit-mask */ + sa = (struct bitvec) { + .data_len = sizeof(sa_buf), + .data = &sa_buf[0], + }; + + for (i = 0; i < ts->hopping.arfcn_num; i++) { + bitvec_set_bit_pos(&sa, ts->hopping.arfcn_list[i], ONE); + if (bitvec_get_bit_pos(&ca, ts->hopping.arfcn_list[i]) != ONE) { + LOGP(DPCU, LOGL_NOTICE, "A transceiver with ARFCN %u " + "is not (yet) provisioned\n", ts->hopping.arfcn_list[i]); + bitvec_set_bit_pos(&ca, ts->hopping.arfcn_list[i], ONE); + ts_info->ma_bit_len++; + } + } + + /* Mobile Allocation bit-mask */ + ma = (struct bitvec) { + .cur_bit = sizeof(ts_info->ma) * 8 - 1, + .data_len = sizeof(ts_info->ma), + .data = &ts_info->ma[0], + }; + + /* Skip ARFCN 0, it goes to the end of MA bit-mask */ + for (i = 1; i < sizeof(ca_buf) * 8; i++) { + if (bitvec_get_bit_pos(&ca, i) != ONE) + continue; + if (bitvec_get_bit_pos(&sa, i) == ONE) + bitvec_set_bit_pos(&ma, ma.cur_bit, ONE); + ma.cur_bit--; + } + + if (bitvec_get_bit_pos(&sa, 0) == ONE) + bitvec_set_bit_pos(&ma, ma.cur_bit, ONE); +} + +static void info_ind_fill_trx(struct gsm_pcu_if_info_trx *trx_info, + const struct gsm_bts_trx *trx) +{ + unsigned int tn; + + trx_info->pdch_mask = 0; + trx_info->arfcn = trx->arfcn; + trx_info->hlayer1 = trx_get_hlayer1(trx); + + if (trx->mo.nm_state.operational != NM_OPSTATE_ENABLED || + trx->mo.nm_state.administrative != NM_STATE_UNLOCKED) { + LOGPTRX(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++) { + const struct gsm_bts_trx_ts *ts = &trx->ts[tn]; + + if (ts->mo.nm_state.operational != NM_OPSTATE_ENABLED) + continue; + if (!ts_should_be_pdch(ts)) + continue; + + trx_info->pdch_mask |= (1 << tn); + trx_info->ts[tn].tsc = ts->tsc; + + if (ts->hopping.enabled) + info_ind_fill_fhp(&trx_info->ts[tn], ts); + + LOGPTRX(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); + } + } +} + +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; } - return false; } 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; struct gsm_bts *bts; struct gprs_rlc_cfg *rlcc; - struct gsm_bts_gprs_nsvc *nsvc; struct gsm_bts_trx *trx; - struct gsm_bts_trx_ts *ts; - int i, j; + 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); @@ -142,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 */ @@ -196,53 +336,56 @@ 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 < 2; i++) { - 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; - info_ind->local_port[i] = nsvc->local_port; - info_ind->remote_port[i] = nsvc->remote_port; - info_ind->remote_ip[i] = nsvc->remote_ip; + /* 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. */ + info_ind->local_port[i] = ntohs(nsvc->local.u.sin.sin_port); + info_ind->remote_port[i] = ntohs(nsvc->remote.u.sin.sin_port); + switch (nsvc->remote.u.sas.ss_family) { + 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; + break; + case AF_INET6: + info_ind->address_type[i] = PCU_IF_ADDR_TYPE_IPV6; + info_ind->remote_ip[i].v6 = nsvc->remote.u.sin6.sin6_addr; + break; + default: + info_ind->address_type[i] = PCU_IF_ADDR_TYPE_UNSPEC; + break; + } } - for (i = 0; i < 8; i++) { - trx = gsm_bts_trx_num(bts, i); - if (!trx) + llist_for_each_entry(trx, &bts->trx_list, list) { + if (trx->nr >= ARRAY_SIZE(info_ind->trx)) { + 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)); break; - info_ind->trx[i].pdch_mask = 0; - info_ind->trx[i].arfcn = trx->arfcn; - info_ind->trx[i].hlayer1 = trx_get_hlayer1(trx); - for (j = 0; j < 8; j++) { - ts = &trx->ts[j]; - if (ts->mo.nm_state.operational == NM_OPSTATE_ENABLED - && ts_should_be_pdch(ts)) { - info_ind->trx[i].pdch_mask |= (1 << j); - info_ind->trx[i].tsc[j] = gsm_ts_tsc(ts); - - LOGP(DPCU, LOGL_INFO, "trx=%d ts=%d: " - "available (tsc=%d arfcn=%d)\n", - trx->nr, ts->nr, - info_ind->trx[i].tsc[j], - info_ind->trx[i].arfcn); - } } + + 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; @@ -257,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; @@ -283,6 +426,10 @@ static int pcu_if_signal_cb(unsigned int subsys, unsigned int signal, return -EINVAL; } + /* Do not send INFO.ind if PCU is not connected */ + if (!pcu_connected()) + return 0; + /* If all infos have been received, of if one info is updated after * all infos have been received, transmit info update. */ if (avail_lai && avail_nse && avail_cell && avail_nsvc[0]) @@ -309,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, @@ -336,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, @@ -351,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; @@ -373,14 +514,16 @@ 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(struct gsm_bts *bts, int16_t qta, uint16_t ra, uint32_t fn, - uint8_t is_11bit, enum ph_burst_type burst_type) +int pcu_tx_rach_ind(uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, + int16_t qta, uint16_t ra, uint32_t fn, uint8_t is_11bit, + enum ph_burst_type burst_type, uint8_t sapi) { struct msgb *msg; struct gsm_pcu_if *pcu_prim; @@ -389,20 +532,22 @@ int pcu_tx_rach_ind(struct gsm_bts *bts, int16_t qta, uint16_t ra, uint32_t fn, LOGP(DPCU, LOGL_INFO, "Sending RACH indication: qta=%d, ra=%d, " "fn=%d\n", qta, ra, fn); - msg = pcu_msgb_alloc(PCU_IF_MSG_RACH_IND, bts->nr); + msg = pcu_msgb_alloc(PCU_IF_MSG_RACH_IND, bts_nr); if (!msg) return -ENOMEM; pcu_prim = (struct gsm_pcu_if *) msg->data; rach_ind = &pcu_prim->u.rach_ind; - rach_ind->sapi = PCU_IF_SAPI_RACH; + rach_ind->sapi = sapi; rach_ind->ra = ra; rach_ind->qta = qta; rach_ind->fn = fn; rach_ind->is_11bit = is_11bit; rach_ind->burst_type = burst_type; + 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) @@ -424,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; @@ -457,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_INFO, "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; + pcu_prim->u.data_cnf2 = (struct gsm_pcu_if_data_cnf) { + .sapi = sapi, + .msg_id = msg_id, + }; - data_cnf->sapi = PCU_IF_SAPI_PCH; - data_cnf->fn = fn; - memcpy(data_cnf->data, data, len); - data_cnf->len = len; - - return pcu_sock_send(&bts_gsmnet, msg); + return pcu_sock_send(msg); } /* forward data from a RR GPRS SUSPEND REQ towards PCU */ @@ -501,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, @@ -519,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); @@ -594,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", @@ -619,13 +867,14 @@ static int pcu_rx_txt_ind(struct gsm_bts *bts, oml_tx_failure_event_rep(&bts->gprs.cell.mo, NM_SEVER_CEASED, OSMO_EVT_PCU_VERS, txt->text); osmo_strlcpy(bts->pcu_version, txt->text, MAX_VERSION_LENGTH); - /* patch SI3 to advertise GPRS, *if* the SI3 sent by BSC said so */ + /* patch SI to advertise GPRS, *if* the SI sent by BSC said so */ 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, @@ -646,7 +895,7 @@ static int pcu_rx_act_req(struct gsm_bts *bts, struct gsm_bts_trx *trx; struct gsm_lchan *lchan; - LOGP(DPCU, LOGL_INFO, "%s request received: TRX=%d TX=%d\n", + LOGP(DPCU, LOGL_INFO, "%s request received: TRX=%d TS=%d\n", (act_req->activate) ? "Activate" : "Deactivate", act_req->trx_nr, act_req->ts_nr); @@ -663,40 +912,70 @@ static int pcu_rx_act_req(struct gsm_bts *bts, gsm_lchant_name(lchan->type)); return -EINVAL; } + 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", + (act_req->activate) ? "Activate" : "Deactivate", + gsm_pchan_name(lchan->ts->dyn.pchan_is)); + 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; - /* FIXME: allow multiple BTS */ - if (pcu_prim->bts_nr != 0) { + 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; } - bts = llist_entry(net->bts_list.next, struct gsm_bts, list); 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); @@ -711,49 +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 |= BSC_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; - struct gsm_bts_trx_ts *ts; - int i, j; + 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, @@ -761,15 +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 |= BSC_FD_READ; + osmo_fd_read_enable(&state->listen_bfd); #if 0 /* remove si13, ... */ @@ -777,27 +1066,22 @@ static void pcu_sock_close(struct pcu_sock_state *state) osmo_signal_dispatch(SS_GLOBAL, S_NEW_SYSINFO, bts); #endif - /* release PDCH */ - for (i = 0; i < 8; i++) { - trx = gsm_bts_trx_num(bts, i); - if (!trx) - break; - for (j = 0; j < 8; j++) { - ts = &trx->ts[j]; - if (ts->mo.nm_state.operational == NM_OPSTATE_ENABLED - && ts->pchan == GSM_PCHAN_PDCH) { - ts->lchan[0].rel_act_kind = LCHAN_REL_ACT_PCU; - l1sap_chan_rel(trx, - gsm_lchan2chan_nr(&ts->lchan[0])); - } + /* Deactivate all active PDCH timeslots */ + llist_for_each_entry(trx, &bts->trx_list, list) { + for (tn = 0; tn < 8; tn++) { + struct gsm_bts_trx_ts *ts = &trx->ts[tn]; + + if (ts->mo.nm_state.operational != NM_OPSTATE_ENABLED) + continue; + if (!ts_should_be_pdch(ts)) + continue; + + ts->lchan[0].rel_act_kind = LCHAN_REL_ACT_PCU; + l1sap_chan_rel(trx, gsm_lchan2chan_nr(&ts->lchan[0])); } } - /* 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) @@ -807,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; @@ -825,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. */ @@ -846,102 +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 &= ~BSC_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 |= BSC_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 & BSC_FD_READ) - rc = pcu_sock_read(bfd); - if (rc < 0) - return rc; - - if (flags & BSC_FD_WRITE) - rc = pcu_sock_write(bfd); - - return rc; -} - -/* accept connection comming from PCU */ +/* 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 &= ~BSC_FD_READ; - close(rc); + osmo_fd_read_disable(&state->listen_bfd); + close(fd); return 0; } - conn_bfd->fd = rc; - conn_bfd->when = BSC_FD_READ; - conn_bfd->cb = pcu_sock_cb; - conn_bfd->data = state; + 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; @@ -955,34 +1194,32 @@ 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; - 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 %s unix socket: %s\n", path, strerror(errno)); talloc_free(state); return -1; } - bfd->when = BSC_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) { @@ -995,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; } |