Harald Welte has submitted this change and it was merged. Change subject: pcu_sock: add basic pcu interface support ......................................................................
pcu_sock: add basic pcu interface support Adds a basic version of a pcu socket interface, similar to the one that can be found in osmo-bts. Change-Id: Ib13cb4099d12fa71e9e0b8727e19ab29e11909b2 --- M openbsc/include/openbsc/Makefile.am M openbsc/include/openbsc/debug.h M openbsc/include/openbsc/gsm_data_shared.h A openbsc/include/openbsc/pcu_if.h A openbsc/include/openbsc/pcuif_proto.h M openbsc/src/libbsc/Makefile.am M openbsc/src/libbsc/abis_rsl.c M openbsc/src/libbsc/bsc_init.c A openbsc/src/libbsc/pcu_sock.c M openbsc/src/libcommon/debug.c 10 files changed, 929 insertions(+), 1 deletion(-) Approvals: Harald Welte: Looks good to me, approved Jenkins Builder: Verified diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am index eb09e18..2740a5d 100644 --- a/openbsc/include/openbsc/Makefile.am +++ b/openbsc/include/openbsc/Makefile.am @@ -65,6 +65,8 @@ bsc_msc_data.h \ osmux.h \ paging.h \ + pcu_if.h \ + pcuif_proto.h \ rest_octets.h \ rrlp.h \ rs232.h \ diff --git a/openbsc/include/openbsc/debug.h b/openbsc/include/openbsc/debug.h index 74db723..8a4247b 100644 --- a/openbsc/include/openbsc/debug.h +++ b/openbsc/include/openbsc/debug.h @@ -37,6 +37,7 @@ DRANAP, DSUA, DV42BIS, + DPCU, Debug_LastEntry, }; diff --git a/openbsc/include/openbsc/gsm_data_shared.h b/openbsc/include/openbsc/gsm_data_shared.h index e7c8467..3371fb9 100644 --- a/openbsc/include/openbsc/gsm_data_shared.h +++ b/openbsc/include/openbsc/gsm_data_shared.h @@ -869,6 +869,9 @@ struct amr_multirate_conf mr_full; struct amr_multirate_conf mr_half; + /* PCU socket state */ + struct pcu_sock_state *pcu_state; + #endif /* ROLE_BSC */ void *role; }; diff --git a/openbsc/include/openbsc/pcu_if.h b/openbsc/include/openbsc/pcu_if.h new file mode 100644 index 0000000..68d4174 --- /dev/null +++ b/openbsc/include/openbsc/pcu_if.h @@ -0,0 +1,33 @@ +#ifndef _PCU_IF_H +#define _PCU_IF_H + +#define PCU_SOCK_DEFAULT "/tmp/pcu_bts" + +#include <osmocom/gsm/l1sap.h> + +extern int pcu_direct; + +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 */ +}; + +/* PCU relevant information has changed; Inform PCU (if connected) */ +void pcu_info_update(struct gsm_bts *bts); + +/* Forward rach indication to PCU */ +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); + +/* Confirm the sending of an immediate assignment to the pcu */ +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); + +/* Close connection to PCU */ +void pcu_sock_exit(struct gsm_bts *bts); + +#endif /* _PCU_IF_H */ diff --git a/openbsc/include/openbsc/pcuif_proto.h b/openbsc/include/openbsc/pcuif_proto.h new file mode 100644 index 0000000..a52d896 --- /dev/null +++ b/openbsc/include/openbsc/pcuif_proto.h @@ -0,0 +1,158 @@ +#ifndef _PCUIF_PROTO_H +#define _PCUIF_PROTO_H + +#define PCU_IF_VERSION 0x07 + +/* msg_type */ +#define PCU_IF_MSG_DATA_REQ 0x00 /* send data to given channel */ +#define PCU_IF_MSG_DATA_CNF 0x01 /* confirm (e.g. transmission on PCH) */ +#define PCU_IF_MSG_DATA_IND 0x02 /* receive data from given channel */ +#define PCU_IF_MSG_RTS_REQ 0x10 /* ready to send request */ +#define PCU_IF_MSG_RACH_IND 0x22 /* receive RACH */ +#define PCU_IF_MSG_INFO_IND 0x32 /* retrieve BTS info */ +#define PCU_IF_MSG_ACT_REQ 0x40 /* activate/deactivate PDCH */ +#define PCU_IF_MSG_TIME_IND 0x52 /* GSM time indication */ +#define PCU_IF_MSG_PAG_REQ 0x60 /* paging request */ + +/* sapi */ +#define PCU_IF_SAPI_RACH 0x01 /* channel request on CCCH */ +#define PCU_IF_SAPI_AGCH 0x02 /* assignment on AGCH */ +#define PCU_IF_SAPI_PCH 0x03 /* paging/assignment on PCH */ +#define PCU_IF_SAPI_BCCH 0x04 /* SI on BCCH */ +#define PCU_IF_SAPI_PDTCH 0x05 /* packet data/control/ccch block */ +#define PCU_IF_SAPI_PRACH 0x06 /* packet random access channel */ +#define PCU_IF_SAPI_PTCCH 0x07 /* packet TA control channel */ + +/* flags */ +#define PCU_IF_FLAG_ACTIVE (1 << 0)/* BTS is active */ +#define PCU_IF_FLAG_SYSMO (1 << 1)/* access PDCH of sysmoBTS directly */ +#define PCU_IF_FLAG_CS1 (1 << 16) +#define PCU_IF_FLAG_CS2 (1 << 17) +#define PCU_IF_FLAG_CS3 (1 << 18) +#define PCU_IF_FLAG_CS4 (1 << 19) +#define PCU_IF_FLAG_MCS1 (1 << 20) +#define PCU_IF_FLAG_MCS2 (1 << 21) +#define PCU_IF_FLAG_MCS3 (1 << 22) +#define PCU_IF_FLAG_MCS4 (1 << 23) +#define PCU_IF_FLAG_MCS5 (1 << 24) +#define PCU_IF_FLAG_MCS6 (1 << 25) +#define PCU_IF_FLAG_MCS7 (1 << 26) +#define PCU_IF_FLAG_MCS8 (1 << 27) +#define PCU_IF_FLAG_MCS9 (1 << 28) + +struct gsm_pcu_if_data { + uint8_t sapi; + uint8_t len; + uint8_t data[162]; + uint32_t fn; + uint16_t arfcn; + uint8_t trx_nr; + uint8_t ts_nr; + uint8_t block_nr; + int8_t rssi; + uint16_t ber10k; /*!< \brief BER in units of 0.01% */ + int16_t ta_offs_qbits; /* !< \brief Burst TA Offset in quarter bits */ + int16_t lqual_cb; /* !< \brief Link quality in centiBel */ +} __attribute__ ((packed)); + +struct gsm_pcu_if_rts_req { + uint8_t sapi; + uint8_t spare[3]; + uint32_t fn; + uint16_t arfcn; + uint8_t trx_nr; + uint8_t ts_nr; + uint8_t block_nr; +} __attribute__ ((packed)); + +struct gsm_pcu_if_rach_ind { + uint8_t sapi; + uint16_t ra; + int16_t qta; + uint32_t fn; + uint16_t arfcn; + uint8_t is_11bit; + uint8_t burst_type; +} __attribute__ ((packed)); + +struct gsm_pcu_if_info_trx { + uint16_t arfcn; + uint8_t pdch_mask; /* PDCH channels per TS */ + uint8_t spare; + uint8_t tsc[8]; /* TSC per channel */ + uint32_t hlayer1; +} __attribute__ ((packed)); + +struct gsm_pcu_if_info_ind { + uint32_t version; + uint32_t flags; + struct gsm_pcu_if_info_trx trx[8]; /* TRX infos per BTS */ + uint8_t bsic; + /* RAI */ + uint16_t mcc, mnc, lac, rac; + /* NSE */ + uint16_t nsei; + uint8_t nse_timer[7]; + uint8_t cell_timer[11]; + /* cell */ + uint16_t cell_id; + uint16_t repeat_time; + uint8_t repeat_count; + uint16_t bvci; + uint8_t t3142; + uint8_t t3169; + uint8_t t3191; + uint8_t t3193_10ms; + uint8_t t3195; + uint8_t n3101; + uint8_t n3103; + uint8_t n3105; + uint8_t cv_countdown; + uint16_t dl_tbf_ext; + uint16_t ul_tbf_ext; + uint8_t initial_cs; + uint8_t initial_mcs; + /* NSVC */ + uint16_t nsvci[2]; + uint16_t local_port[2]; + uint16_t remote_port[2]; + uint32_t remote_ip[2]; +} __attribute__ ((packed)); + +struct gsm_pcu_if_act_req { + uint8_t activate; + uint8_t trx_nr; + uint8_t ts_nr; + uint8_t spare; +} __attribute__ ((packed)); + +struct gsm_pcu_if_time_ind { + uint32_t fn; +} __attribute__ ((packed)); + +struct gsm_pcu_if_pag_req { + uint8_t sapi; + uint8_t chan_needed; + uint8_t identity_lv[9]; +} __attribute__ ((packed)); + +struct gsm_pcu_if { + /* context based information */ + uint8_t msg_type; /* message type */ + uint8_t bts_nr; /* bts number */ + uint8_t spare[2]; + + union { + struct gsm_pcu_if_data data_req; + struct gsm_pcu_if_data data_cnf; + struct gsm_pcu_if_data data_ind; + struct gsm_pcu_if_rts_req rts_req; + struct gsm_pcu_if_rach_ind rach_ind; + struct gsm_pcu_if_info_ind info_ind; + struct gsm_pcu_if_act_req act_req; + struct gsm_pcu_if_time_ind time_ind; + struct gsm_pcu_if_pag_req pag_req; + } u; +} __attribute__ ((packed)); + +#endif /* _PCUIF_PROTO_H */ diff --git a/openbsc/src/libbsc/Makefile.am b/openbsc/src/libbsc/Makefile.am index fd8f37a..e78bde6 100644 --- a/openbsc/src/libbsc/Makefile.am +++ b/openbsc/src/libbsc/Makefile.am @@ -36,6 +36,7 @@ handover_decision.c \ handover_logic.c \ meas_rep.c \ + pcu_sock.c \ rest_octets.c \ system_information.c \ e1_config.c \ diff --git a/openbsc/src/libbsc/abis_rsl.c b/openbsc/src/libbsc/abis_rsl.c index 20dd2b5..62c12a8 100644 --- a/openbsc/src/libbsc/abis_rsl.c +++ b/openbsc/src/libbsc/abis_rsl.c @@ -45,6 +45,7 @@ #include <osmocom/abis/e1_input.h> #include <osmocom/gsm/rsl.h> #include <osmocom/core/talloc.h> +#include <openbsc/pcu_if.h> #define RSL_ALLOC_SIZE 1024 #define RSL_ALLOC_HEADROOM 128 @@ -1285,6 +1286,12 @@ send_lchan_signal(S_LCHAN_ACTIVATE_ACK, lchan, NULL); + /* Update bts attributes inside the PCU */ + if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH || + ts->pchan == GSM_PCHAN_TCH_F_PDCH || + ts->pchan == GSM_PCHAN_PDCH) + pcu_info_update(ts->trx->bts); + return 0; } @@ -1768,6 +1775,39 @@ return rsl_imm_assign_cmd(bts, sizeof(*iar), (uint8_t *) iar); } +/* Handle packet channel rach requests */ +static int rsl_rx_pchan_rqd(struct msgb *msg, struct gsm_bts *bts) +{ + struct gsm48_req_ref *rqd_ref; + struct abis_rsl_dchan_hdr *rqd_hdr = msgb_l2(msg); + rqd_ref = (struct gsm48_req_ref *) &rqd_hdr->data[1]; + uint8_t ra = rqd_ref->ra; + uint8_t t1, t2, t3; + uint32_t fn; + uint8_t rqd_ta; + uint8_t is_11bit; + + /* Process rach request and forward contained information to PCU */ + if (ra == 0x7F) { + is_11bit = 1; + + /* FIXME: Also handle 11 bit rach requests */ + LOGP(DRSL, LOGL_ERROR, "BTS %d eleven bit access burst not supported yet!\n",bts->nr); + return -EINVAL; + } else { + is_11bit = 0; + t1 = rqd_ref->t1; + t2 = rqd_ref->t2; + t3 = rqd_ref->t3_low | (rqd_ref->t3_high << 3); + fn = (51 * ((t3-t2) % 26) + t3 + 51 * 26 * t1); + + rqd_ta = rqd_hdr->data[sizeof(struct gsm48_req_ref)+2]; + } + + return pcu_tx_rach_ind(bts, rqd_ta, ra, fn, is_11bit, + GSM_L1_BURST_TYPE_ACCESS_0); +} + /* MS has requested a channel on the RACH */ static int rsl_rx_chan_rqd(struct msgb *msg) { @@ -1795,10 +1835,20 @@ return -EINVAL; rqd_ta = rqd_hdr->data[sizeof(struct gsm48_req_ref)+2]; + /* Determine channel request cause code */ + chreq_reason = get_reason_by_chreq(rqd_ref->ra, bts->network->neci); + LOGP(DRSL, LOGL_NOTICE, "BTS %d CHAN RQD: reason: %s (ra=0x%02x, neci=0x%02x, chreq_reason=0x%02x)\n", + msg->lchan->ts->trx->bts->nr, + get_value_string(gsm_chreq_descs, chreq_reason), + rqd_ref->ra, bts->network->neci, chreq_reason); + + /* Handle PDCH related rach requests (in case of BSC-co-located-PCU */ + if (chreq_reason == GSM_CHREQ_REASON_PDCH) + return rsl_rx_pchan_rqd(msg, bts); + /* determine channel type (SDCCH/TCH_F/TCH_H) based on * request reference RA */ lctype = get_ctype_by_chreq(bts->network, rqd_ref->ra); - chreq_reason = get_reason_by_chreq(rqd_ref->ra, bts->network->neci); rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_CHREQ_TOTAL]); diff --git a/openbsc/src/libbsc/bsc_init.c b/openbsc/src/libbsc/bsc_init.c index 2af846e..50c73db 100644 --- a/openbsc/src/libbsc/bsc_init.c +++ b/openbsc/src/libbsc/bsc_init.c @@ -37,6 +37,8 @@ #include <osmocom/gsm/sysinfo.h> #include <openbsc/e1_config.h> #include <openbsc/common_bsc.h> +#include <openbsc/pcu_if.h> +#include <limits.h> /* global pointer to the gsm network data structure */ extern struct gsm_network *bsc_gsmnet; @@ -190,6 +192,10 @@ if (rc < 0) return rc; } + + /* Make sure the PCU is aware (in case anything GPRS related has + * changed in SI */ + pcu_info_update(bts); return 0; err_out: @@ -486,6 +492,7 @@ int bsc_network_alloc(mncc_recv_cb_t mncc_recv) { + /* initialize our data structures */ bsc_gsmnet = bsc_network_init(tall_bsc_ctx, 1, 1, mncc_recv); if (!bsc_gsmnet) return -ENOMEM; @@ -500,6 +507,8 @@ { struct gsm_bts *bts; int rc; + char pcu_sock_path[PATH_MAX]; + char pcu_sock_path_ending[PATH_MAX]; rc = vty_read_config_file(config_file, NULL); if (rc < 0) { @@ -527,6 +536,19 @@ LOGP(DNM, LOGL_FATAL, "Error enabling E1 input driver\n"); return rc; } + + strcpy(pcu_sock_path, PCU_SOCK_DEFAULT); + sprintf(pcu_sock_path_ending,"_%i", bts->nr); + if (bts->nr > 0) + strcat(pcu_sock_path, pcu_sock_path_ending); + rc = pcu_sock_init(pcu_sock_path, bts); + + if (rc < 0) { + LOGP(DNM, LOGL_FATAL, + "PCU L1 socket failed for bts %i\n", bts->nr); + return rc; + } } + return 0; } diff --git a/openbsc/src/libbsc/pcu_sock.c b/openbsc/src/libbsc/pcu_sock.c new file mode 100644 index 0000000..b2bc425 --- /dev/null +++ b/openbsc/src/libbsc/pcu_sock.c @@ -0,0 +1,653 @@ +/* pcu_sock.c: Connect from PCU via unix domain socket */ + +/* (C) 2008-2010 by Harald Welte <lafo...@gnumonks.org> + * (C) 2009-2012 by Andreas Eversberg <jo...@eversberg.eu> + * (C) 2012 by Holger Hans Peter Freyther + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <sys/socket.h> +#include <sys/un.h> + +#include <osmocom/core/talloc.h> +#include <osmocom/core/select.h> +#include <osmocom/core/socket.h> +#include <osmocom/core/logging.h> +#include <osmocom/gsm/l1sap.h> +#include <osmocom/gsm/gsm0502.h> + +#include <openbsc/gsm_data.h> +#include <openbsc/pcu_if.h> +#include <openbsc/pcuif_proto.h> +#include <openbsc/signal.h> +#include <openbsc/debug.h> +#include <openbsc/abis_rsl.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 = 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", +}; + +static struct gsm_bts_trx *trx_by_nr(struct gsm_bts *bts, uint8_t trx_nr) +{ + struct gsm_bts_trx *trx; + + llist_for_each_entry(trx, &bts->trx_list, list) { + if (trx->nr == trx_nr) + return trx; + } + + return NULL; +} + +/* Check if BTS has a PCU connection */ +static bool pcu_connected(struct gsm_bts *bts) +{ + struct pcu_sock_state *state = bts->pcu_state; + + if (!state) + return false; + if (state->conn_bfd.fd <= 0) + return false; + return true; +} + +/* + * PCU messages + */ + +/* Set up an message buffer to package an pcu interface message */ +struct msgb *pcu_msgb_alloc(uint8_t msg_type, uint8_t bts_nr) +{ + struct msgb *msg; + struct gsm_pcu_if *pcu_prim; + + msg = msgb_alloc(sizeof(struct gsm_pcu_if), "pcu_sock_tx"); + if (!msg) + return NULL; + + msgb_put(msg, sizeof(struct gsm_pcu_if)); + pcu_prim = (struct gsm_pcu_if *) msg->data; + pcu_prim->msg_type = msg_type; + pcu_prim->bts_nr = bts_nr; + + return msg; +} + +/* Helper function exclusivly used by pcu_if_signal_cb() */ +static bool ts_should_be_pdch(struct gsm_bts_trx_ts *ts) { + if (ts->pchan == GSM_PCHAN_PDCH) + return true; + if (ts->pchan == 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. + * Similarly, when we're activating PDCH, we set the + * ACT_PENDING and wait for an activation response from the + * PCU, so ACT_PENDING means "is PDCH". */ + if (ts->flags & TS_F_PDCH_ACTIVE) + 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) { + /* + * 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. + */ + return ts->dyn.pchan_want == GSM_PCHAN_PDCH; + } + return false; +} + +/* Send BTS properties to the PCU */ +static int pcu_tx_info_ind(struct gsm_bts *bts) +{ + struct msgb *msg; + 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_trx *trx; + struct gsm_bts_trx_ts *ts; + int i, j; + + OSMO_ASSERT(bts); + OSMO_ASSERT(bts->network); + + LOGP(DPCU, LOGL_INFO, "Sending info for BTS %d\n",bts->nr); + + rlcc = &bts->gprs.cell.rlc_cfg; + + msg = pcu_msgb_alloc(PCU_IF_MSG_INFO_IND, bts->nr); + if (!msg) + return -ENOMEM; + + pcu_prim = (struct gsm_pcu_if *) msg->data; + 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; + + /* RAI */ + info_ind->mcc = bts->network->country_code; + info_ind->mnc = bts->network->network_code; + 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); + memcpy(info_ind->cell_timer, bts->gprs.cell.timer, 11); + + /* cell attributes */ + info_ind->cell_id = bts->cell_identity; + info_ind->repeat_time = rlcc->paging.repeat_time; + info_ind->repeat_count = rlcc->paging.repeat_count; + info_ind->bvci = bts->gprs.cell.bvci; + info_ind->t3142 = rlcc->parameter[RLC_T3142]; + info_ind->t3169 = rlcc->parameter[RLC_T3169]; + info_ind->t3191 = rlcc->parameter[RLC_T3191]; + info_ind->t3193_10ms = rlcc->parameter[RLC_T3193]; + info_ind->t3195 = rlcc->parameter[RLC_T3195]; + info_ind->n3101 = rlcc->parameter[RLC_N3101]; + info_ind->n3103 = rlcc->parameter[RLC_N3103]; + info_ind->n3105 = rlcc->parameter[RLC_N3105]; + info_ind->cv_countdown = rlcc->parameter[CV_COUNTDOWN]; + if (rlcc->cs_mask & (1 << GPRS_CS1)) + info_ind->flags |= PCU_IF_FLAG_CS1; + if (rlcc->cs_mask & (1 << GPRS_CS2)) + info_ind->flags |= PCU_IF_FLAG_CS2; + if (rlcc->cs_mask & (1 << GPRS_CS3)) + info_ind->flags |= PCU_IF_FLAG_CS3; + if (rlcc->cs_mask & (1 << GPRS_CS4)) + info_ind->flags |= PCU_IF_FLAG_CS4; + if (bts->gprs.mode == BTS_GPRS_EGPRS) { + if (rlcc->cs_mask & (1 << GPRS_MCS1)) + info_ind->flags |= PCU_IF_FLAG_MCS1; + if (rlcc->cs_mask & (1 << GPRS_MCS2)) + info_ind->flags |= PCU_IF_FLAG_MCS2; + if (rlcc->cs_mask & (1 << GPRS_MCS3)) + info_ind->flags |= PCU_IF_FLAG_MCS3; + if (rlcc->cs_mask & (1 << GPRS_MCS4)) + info_ind->flags |= PCU_IF_FLAG_MCS4; + if (rlcc->cs_mask & (1 << GPRS_MCS5)) + info_ind->flags |= PCU_IF_FLAG_MCS5; + if (rlcc->cs_mask & (1 << GPRS_MCS6)) + info_ind->flags |= PCU_IF_FLAG_MCS6; + if (rlcc->cs_mask & (1 << GPRS_MCS7)) + info_ind->flags |= PCU_IF_FLAG_MCS7; + if (rlcc->cs_mask & (1 << GPRS_MCS8)) + 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" + info_ind->dl_tbf_ext = rlcc->parameter[T_DL_TBF_EXT]; +#warning "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]; + 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; + } + + for (i = 0; i < 8; i++) { + trx = trx_by_nr(bts, i); + if (!trx) + break; + info_ind->trx[i].pdch_mask = 0; + info_ind->trx[i].arfcn = trx->arfcn; + 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] = + (ts->tsc >= 0) ? ts->tsc : bts->bsic & 7; + 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); + } + } + } + + return pcu_sock_send(bts, msg); +} + +void pcu_info_update(struct gsm_bts *bts) +{ + if (pcu_connected(bts)) + pcu_tx_info_ind(bts); +} + +/* Forward rach indication to PCU */ +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) +{ + struct msgb *msg; + struct gsm_pcu_if *pcu_prim; + struct gsm_pcu_if_rach_ind *rach_ind; + + /* Bail if no PCU is connected */ + if (!pcu_connected(bts)) { + LOGP(DRSL, LOGL_ERROR, "BTS %d CHAN RQD(GPRS) but PCU not " + "connected!\n", bts->nr); + return -ENODEV; + } + + 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); + 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->ra = ra; + rach_ind->qta = qta; + rach_ind->fn = fn; + rach_ind->is_11bit = is_11bit; + rach_ind->burst_type = burst_type; + + return pcu_sock_send(bts, msg); +} + +static int pcu_rx_data_req(struct gsm_bts *bts, uint8_t msg_type, + struct gsm_pcu_if_data *data_req) +{ + uint8_t is_ptcch; + struct gsm_bts_trx *trx; + struct gsm_bts_trx_ts *ts; + struct msgb *msg; + int rc = 0; + + LOGP(DPCU, LOGL_DEBUG, "Data request received: sapi=%s arfcn=%d " + "block=%d data=%s\n", sapi_string[data_req->sapi], + data_req->arfcn, data_req->block_nr, + osmo_hexdump(data_req->data, data_req->len)); + + switch (data_req->sapi) { + case PCU_IF_SAPI_PCH: + if (msg_type == PCU_IF_MSG_PAG_REQ) { + /* FIXME: Add function to schedule paging request. + * This might not be required, if PCU_IF_MSG_DATA_REQ + * is used instead. */ + } else { + struct gsm_bts_role_bts *btsb = bts->role; + + printf("paging_add_imm_ass(btsb->paging_state, data_req->data,data_req->len);\n"); + } + break; + case PCU_IF_SAPI_AGCH: + msg = msgb_alloc(data_req->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); + + if (rsl_imm_assign_cmd(bts, msg->len, msg->data)) { + msgb_free(msg); + rc = -EIO; + } + break; + default: + LOGP(DPCU, LOGL_ERROR, "Received PCU data request with " + "unsupported sapi %d\n", data_req->sapi); + rc = -EINVAL; + } + + return rc; +} + +static int pcu_rx(struct gsm_network *net, uint8_t msg_type, + struct gsm_pcu_if *pcu_prim) +{ + int rc = 0; + struct gsm_bts *bts; + + /* FIXME: allow multiple BTS */ + bts = llist_entry(net->bts_list.next, struct gsm_bts, list); + + switch (msg_type) { + case PCU_IF_MSG_DATA_REQ: + case PCU_IF_MSG_PAG_REQ: + rc = pcu_rx_data_req(bts, msg_type, &pcu_prim->u.data_req); + break; + default: + LOGP(DPCU, LOGL_ERROR, "Received unknwon PCU msg type %d\n", + msg_type); + rc = -EINVAL; + } + + return rc; +} + +/* + * PCU socket interface + */ + +static int pcu_sock_send(struct gsm_bts *bts, struct msgb *msg) +{ + struct pcu_sock_state *state = bts->pcu_state; + struct osmo_fd *conn_bfd; + struct gsm_pcu_if *pcu_prim = (struct gsm_pcu_if *) msg->data; + + if (!state) { + if (pcu_prim->msg_type != PCU_IF_MSG_TIME_IND) + LOGP(DPCU, LOGL_INFO, "PCU socket not created, " + "dropping message\n"); + msgb_free(msg); + return -EINVAL; + } + conn_bfd = &state->conn_bfd; + if (conn_bfd->fd <= 0) { + if (pcu_prim->msg_type != PCU_IF_MSG_TIME_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; + + return 0; +} + +static void pcu_sock_close(struct pcu_sock_state *state) +{ + struct osmo_fd *bfd = &state->conn_bfd; + struct gsm_bts *bts; + struct gsm_bts_trx *trx; + struct gsm_bts_trx_ts *ts; + int i, 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"); + + close(bfd->fd); + bfd->fd = -1; + osmo_fd_unregister(bfd); + + /* re-enable the generation of ACCEPT for new connections */ + state->listen_bfd.when |= BSC_FD_READ; + +#if 0 + /* remove si13, ... */ + bts->si_valid &= ~(1 << SYSINFO_TYPE_13); + osmo_signal_dispatch(SS_GLOBAL, S_NEW_SYSINFO, bts); +#endif + + /* release PDCH */ + for (i = 0; i < 8; i++) { + trx = trx_by_nr(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) { + printf("l1sap_chan_rel(trx,gsm_lchan2chan_nr(ts->lchan));\n"); + } + } + } + + /* flush the queue */ + while (!llist_empty(&state->upqueue)) { + struct msgb *msg = msgb_dequeue(&state->upqueue); + msgb_free(msg); + } +} + +static int pcu_sock_read(struct osmo_fd *bfd) +{ + struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data; + struct gsm_pcu_if *pcu_prim; + struct msgb *msg; + int rc; + + msg = msgb_alloc(sizeof(*pcu_prim), "pcu_sock_rx"); + if (!msg) + return -ENOMEM; + + pcu_prim = (struct gsm_pcu_if *) msg->tail; + + rc = recv(bfd->fd, msg->tail, msgb_tailroom(msg), 0); + if (rc == 0) + goto close; + + if (rc < 0) { + if (errno == EAGAIN) + return 0; + goto close; + } + + rc = pcu_rx(state->net, pcu_prim->msg_type, pcu_prim); + + /* as we always synchronously process the message in pcu_rx() and + * its callbacks, we can free the message here. */ + msgb_free(msg); + + return rc; + +close: + msgb_free(msg); + pcu_sock_close(state); + return -1; +} + +static int pcu_sock_write(struct osmo_fd *bfd) +{ + 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); + } + 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 */ +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 sockaddr_un un_addr; + socklen_t len; + int rc; + + len = sizeof(un_addr); + rc = accept(bfd->fd, (struct sockaddr *) &un_addr, &len); + if (rc < 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"); + /* We already have one PCU connected, this is all we support */ + state->listen_bfd.when &= ~BSC_FD_READ; + close(rc); + return 0; + } + + conn_bfd->fd = rc; + conn_bfd->when = BSC_FD_READ; + conn_bfd->cb = pcu_sock_cb; + conn_bfd->data = state; + + if (osmo_fd_register(conn_bfd) != 0) { + LOGP(DPCU, LOGL_ERROR, "Failed to register new connection " + "fd\n"); + close(conn_bfd->fd); + conn_bfd->fd = -1; + return -1; + } + + LOGP(DPCU, LOGL_NOTICE, "PCU socket connected to external PCU\n"); + + return 0; +} + +/* Open connection to PCU */ +int pcu_sock_init(const char *path, struct gsm_bts *bts) +{ + struct pcu_sock_state *state; + struct osmo_fd *bfd; + int rc; + + state = talloc_zero(NULL, struct pcu_sock_state); + if (!state) + return -ENOMEM; + + INIT_LLIST_HEAD(&state->upqueue); + state->net = bts->network; + state->conn_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) { + LOGP(DPCU, LOGL_ERROR, "Could not create unix socket: %s\n", + strerror(errno)); + talloc_free(state); + return -1; + } + + bfd->when = BSC_FD_READ; + bfd->cb = pcu_sock_accept; + bfd->data = state; + + rc = osmo_fd_register(bfd); + if (rc < 0) { + LOGP(DPCU, LOGL_ERROR, "Could not register listen fd: %d\n", + rc); + close(bfd->fd); + talloc_free(state); + return rc; + } + + bts->pcu_state = state; + return 0; +} + +/* Close connection to PCU */ +void pcu_sock_exit(struct gsm_bts *bts) +{ + struct pcu_sock_state *state = bts->pcu_state; + struct osmo_fd *bfd, *conn_bfd; + + if (!state) + return; + + conn_bfd = &state->conn_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->pcu_state = NULL; +} + diff --git a/openbsc/src/libcommon/debug.c b/openbsc/src/libcommon/debug.c index 0f82211..f29f168 100644 --- a/openbsc/src/libcommon/debug.c +++ b/openbsc/src/libcommon/debug.c @@ -175,6 +175,11 @@ .description = "SCCP User Adaptation Protocol", .enabled = 1, .loglevel = LOGL_DEBUG, }, + [DPCU] = { + .name = "DPCU", + .description = "PCU Interface", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, }; static int filter_fn(const struct log_context *ctx, struct log_target *tar) -- To view, visit https://gerrit.osmocom.org/2056 To unsubscribe, visit https://gerrit.osmocom.org/settings Gerrit-MessageType: merged Gerrit-Change-Id: Ib13cb4099d12fa71e9e0b8727e19ab29e11909b2 Gerrit-PatchSet: 2 Gerrit-Project: openbsc Gerrit-Branch: master Gerrit-Owner: lynxis lazus <lyn...@fe80.eu> Gerrit-Reviewer: Harald Welte <lafo...@gnumonks.org> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: Max <msur...@sysmocom.de> Gerrit-Reviewer: dexter <pma...@sysmocom.de>