From c6b4c87e5d57b91b29894835e7ac8e42f6e67f32 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Mon, 27 Jun 2011 11:25:35 +0200 Subject: re-work original osmo-bts with support for sysmocom femtobts This code re-works osmo-bts to add support for the upcoming sysmocom BTS. It also tries to add some level of abstraction between the generic part of a BTS (A-bis, RSL, OML, data structures, paging scheduling, BCCH/AGCH scheduling, etc.) and the actual hardware-specific bits. The hardware-specific bits are currently only implemented for the sysmocom femtobts, but should be (re-)added for osmocom-bb, as well as a virtual BTS for simulation purpose later. The sysmocom bts specific parts require hardware-specific header files which are (at least currently) not publicly distributed. --- src/common/Makefile.am | 4 +- src/common/abis.c | 144 ++++--- src/common/bts.c | 145 +++---- src/common/gsm_data_shared.c | 1 + src/common/load_indication.c | 69 ++++ src/common/logging.c | 122 ++++++ src/common/oml.c | 900 ++++++++++++++++++++++++++++++++----------- src/common/paging.c | 440 +++++++++++++++++++++ src/common/rsl.c | 778 +++++++++++++++++++++++++------------ src/common/rtp.c | 18 +- src/common/support.c | 9 +- src/common/sysinfo.c | 66 ++++ src/common/vty.c | 61 +++ 13 files changed, 2132 insertions(+), 625 deletions(-) create mode 100644 src/common/gsm_data_shared.c create mode 100644 src/common/load_indication.c create mode 100644 src/common/logging.c create mode 100644 src/common/paging.c create mode 100644 src/common/sysinfo.c create mode 100644 src/common/vty.c (limited to 'src/common') diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 0c13b54a..70b94957 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -3,5 +3,5 @@ AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) LDADD = $(LIBOSMOCORE_LIBS) noinst_LIBRARIES = libbts.a -libbts_a_SOURCES = support.c bts.c abis.c rsl.c oml.c rtp.c -# ../common/l1l2_interface.c ../common/lapdm.c ../common/logging.c +libbts_a_SOURCES = gsm_data_shared.c sysinfo.c logging.c abis.c oml.c bts.c rsl.c vty.c paging.c +#support.c rtp.c diff --git a/src/common/abis.c b/src/common/abis.c index f70eb843..c60f5443 100644 --- a/src/common/abis.c +++ b/src/common/abis.c @@ -1,10 +1,14 @@ +/* Minimalistic Abis/IP interface routines, soon to be replaced by + * libosmo-abis (Pablo) */ + /* (C) 2011 by Andreas Eversberg + * (C) 2011 by Harald Welte * * 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 + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, @@ -12,9 +16,8 @@ * 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. + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . * */ @@ -27,16 +30,20 @@ #include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#if 0 #include -#include -#include -#include +#endif extern char *software_version; extern uint8_t abis_mac[6]; @@ -61,6 +68,23 @@ int abis_tx(struct ipabis_link *link, struct msgb *msg) return 0; } +int abis_oml_sendmsg(struct msgb *msg) +{ + struct gsm_bts *bts = msg->trx->bts; + + abis_push_ipa(msg, 0xff); + + return abis_tx((struct ipabis_link *) bts->oml_link, msg); +} + +int abis_rsl_sendmsg(struct msgb *msg) +{ + struct gsm_bts_trx *trx = msg->trx; + + abis_push_ipa(msg, 0); + + return abis_tx((struct ipabis_link *) trx->rsl_link, msg); +} struct msgb *abis_msgb_alloc(int headroom) { @@ -98,7 +122,7 @@ static int abis_tx_ipa_pingpong(struct ipabis_link *link, uint8_t pingpong) if (!nmsg) return -ENOMEM; *msgb_put(nmsg, 1) = pingpong; - abis_push_ipa(nmsg, IPA_PROTO_IPACCESS); + abis_push_ipa(nmsg, IPAC_PROTO_IPACCESS); return abis_tx(link, nmsg); } @@ -106,16 +130,20 @@ static int abis_tx_ipa_pingpong(struct ipabis_link *link, uint8_t pingpong) /* send ACK and ID RESP */ static int abis_rx_ipa_id_get(struct ipabis_link *link, uint8_t *data, int len) { + struct gsm_bts *bts = link->bts; struct msgb *nmsg, *nmsg2; char str[64]; uint8_t *tag; + if (!link->bts) + bts = link->trx->bts; + LOGP(DABIS, LOGL_INFO, "Reply to ID_GET\n"); nmsg = abis_msgb_alloc(0); if (!nmsg) return -ENOMEM; - *msgb_put(nmsg, 1) = IPA_MSGT_ID_RESP; + *msgb_put(nmsg, 1) = IPAC_MSGT_ID_RESP; while (len) { if (len < 2) { LOGP(DABIS, LOGL_NOTICE, @@ -124,32 +152,33 @@ static int abis_rx_ipa_id_get(struct ipabis_link *link, uint8_t *data, int len) return -EIO; } switch (data[1]) { - case IPA_IDTAG_UNIT: - sprintf(str, "%s/%d", (link->trx) ? link->trx->bts->id - : link->bts->id, - (link->trx) ? link->trx->trx_nr : 0); + case IPAC_IDTAG_UNIT: + sprintf(str, "%u/%u/%u", + bts->ip_access.site_id, + bts->ip_access.bts_id, + (link->trx) ? link->trx->nr : 0); break; - case IPA_IDTAG_MACADDR: + case IPAC_IDTAG_MACADDR: sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x", abis_mac[0], abis_mac[1], abis_mac[2], abis_mac[3], abis_mac[4], abis_mac[5]); break; - case IPA_IDTAG_LOCATION1: + case IPAC_IDTAG_LOCATION1: strcpy(str, "osmoBTS"); break; - case IPA_IDTAG_LOCATION2: + case IPAC_IDTAG_LOCATION2: strcpy(str, "osmoBTS"); break; - case IPA_IDTAG_EQUIPVERS: - case IPA_IDTAG_SWVERSION: + case IPAC_IDTAG_EQUIPVERS: + case IPAC_IDTAG_SWVERSION: strcpy(str, software_version); break; - case IPA_IDTAG_UNITNAME: + case IPAC_IDTAG_UNITNAME: sprintf(str, "osmoBTS-%02x-%02x-%02x-%02x-%02x-%02x", abis_mac[0], abis_mac[1], abis_mac[2], abis_mac[3], abis_mac[4], abis_mac[5]); break; - case IPA_IDTAG_SERNR: + case IPAC_IDTAG_SERNR: strcpy(str, ""); break; default: @@ -167,15 +196,15 @@ static int abis_rx_ipa_id_get(struct ipabis_link *link, uint8_t *data, int len) data += 2; len -= 2; } - abis_push_ipa(nmsg, IPA_PROTO_IPACCESS); + abis_push_ipa(nmsg, IPAC_PROTO_IPACCESS); nmsg2 = abis_msgb_alloc(0); if (!nmsg2) { msgb_free(nmsg); return -ENOMEM; } - *msgb_put(nmsg2, 1) = IPA_MSGT_ID_ACK; - abis_push_ipa(nmsg2, IPA_PROTO_IPACCESS); + *msgb_put(nmsg2, 1) = IPAC_MSGT_ID_ACK; + abis_push_ipa(nmsg2, IPAC_PROTO_IPACCESS); link->id_resp = 1; @@ -196,21 +225,23 @@ static int abis_rx_ipaccess(struct ipabis_link *link, struct msgb *msg) } switch (*data) { - case IPA_MSGT_PONG: + case IPAC_MSGT_PONG: +#if 0 #warning HACK rsl_tx_chan_rqd(link->bts->trx[0]); - LOGP(DABIS, LOGL_INFO, "PONG\n"); +#endif + LOGP(DABIS, LOGL_DEBUG, "PONG\n"); link->pong = 1; break; - case IPA_MSGT_PING: - LOGP(DABIS, LOGL_INFO, "reply to ping request\n"); - ret = abis_tx_ipa_pingpong(link, IPA_MSGT_PONG); + case IPAC_MSGT_PING: + LOGP(DABIS, LOGL_DEBUG, "reply to ping request\n"); + ret = abis_tx_ipa_pingpong(link, IPAC_MSGT_PONG); break; - case IPA_MSGT_ID_GET: + case IPAC_MSGT_ID_GET: ret = abis_rx_ipa_id_get(link, data + 1, len - 1); break; - case IPA_MSGT_ID_ACK: - LOGP(DABIS, LOGL_INFO, "ID ACK\n"); + case IPAC_MSGT_ID_ACK: + LOGP(DABIS, LOGL_DEBUG, "ID ACK\n"); if (link->id_resp && link->bts) ret = bts_link_estab(link->bts); if (link->id_resp && link->trx) @@ -240,7 +271,7 @@ static int abis_rx(struct ipabis_link *link, struct msgb *msg) int ret = 0; switch (hh->proto) { - case IPA_PROTO_RSL: + case IPAC_PROTO_RSL: if (!link->trx) { LOGP(DABIS, LOGL_NOTICE, "Received RSL message not on RSL link\n"); @@ -248,16 +279,17 @@ static int abis_rx(struct ipabis_link *link, struct msgb *msg) ret = EIO; break; } + msg->trx = link->trx; ret = down_rsl(link->trx, msg); break; - case IPA_PROTO_IPACCESS: + case IPAC_PROTO_IPACCESS: ret = abis_rx_ipaccess(link, msg); break; - case IPA_PROTO_SCCP: + case IPAC_PROTO_SCCP: LOGP(DABIS, LOGL_INFO, "Received SCCP message\n"); msgb_free(msg); break; - case IPA_PROTO_OML: + case IPAC_PROTO_OML: if (!link->bts) { LOGP(DABIS, LOGL_NOTICE, "Received OML message not on OML link\n"); @@ -265,6 +297,7 @@ static int abis_rx(struct ipabis_link *link, struct msgb *msg) ret = EIO; break; } + msg->trx = link->bts->c0; ret = down_oml(link->bts, msg); break; default: @@ -302,8 +335,8 @@ static void abis_timeout(void *arg) } link->ping = 1; link->pong = 0; - LOGP(DABIS, LOGL_INFO, "PING\n"); - abis_tx_ipa_pingpong(link, IPA_MSGT_PING); + LOGP(DABIS, LOGL_DEBUG, "PING\n"); + abis_tx_ipa_pingpong(link, IPAC_MSGT_PING); osmo_timer_schedule(&link->timer, OML_PING_TIMER, 0); break; } @@ -348,7 +381,7 @@ static int abis_sock_cb(struct osmo_fd *bfd, unsigned int what) return 0; msg->l2h = msg->data + sizeof(*hh); if (ntohs(hh->len) > msgb_tailroom(msg)) { - LOGP(DABIS, LOGL_NOTICE, "Received packet from " + LOGP(DABIS, LOGL_DEBUG, "Received packet from " "Abis socket too large.\n"); goto close; } @@ -363,13 +396,13 @@ static int abis_sock_cb(struct osmo_fd *bfd, unsigned int what) if (ntohs(hh->len) + sizeof(*hh) > msg->len) return 0; link->rx_msg = NULL; - LOGP(DABIS, LOGL_INFO, "Received messages from Abis socket.\n"); + LOGP(DABIS, LOGL_DEBUG, "Received messages from Abis socket.\n"); ret = abis_rx(link, msg); } if ((what & BSC_FD_WRITE)) { msg = msgb_dequeue(&link->tx_queue); if (msg) { - LOGP(DABIS, LOGL_INFO, "Sending messages to Abis socket.\n"); + LOGP(DABIS, LOGL_DEBUG, "Sending messages to Abis socket.\n"); ret = send(link->bfd.fd, msg->data, msg->len, 0); if (ret < 0) goto close; @@ -377,7 +410,7 @@ static int abis_sock_cb(struct osmo_fd *bfd, unsigned int what) link->bfd.when &= ~BSC_FD_WRITE; } if ((what & BSC_FD_EXCEPT)) { - LOGP(DABIS, LOGL_INFO, "Abis socket received exception\n"); + LOGP(DABIS, LOGL_NOTICE, "Abis socket received exception\n"); goto close; } @@ -388,11 +421,11 @@ close: /* RSL link will just close and BSC is notified */ if (link->trx) { - LOGP(DABIS, LOGL_INFO, "Connection to BSC failed\n"); + LOGP(DABIS, LOGL_NOTICE, "Connection to BSC failed\n"); return trx_link_estab(link->trx); } - LOGP(DABIS, LOGL_INFO, "Connection to BSC failed, retrying in %d " + LOGP(DABIS, LOGL_NOTICE, "Connection to BSC failed, retrying in %d " "seconds.\n", OML_RETRY_TIMER); osmo_timer_schedule(&link->timer, OML_RETRY_TIMER, 0); link->state = LINK_STATE_RETRYING; @@ -407,6 +440,8 @@ int abis_open(struct ipabis_link *link, uint32_t ip) int sock; int ret; + oml_init(); + if (link->bfd.fd > 0) return -EBUSY; @@ -455,18 +490,11 @@ int abis_open(struct ipabis_link *link, uint32_t ip) void abis_close(struct ipabis_link *link) { struct msgb *msg; - int i; if (link->bfd.fd <= 0) return; - LOGP(DABIS, LOGL_INFO, "Abis socket closed.\n"); - -#warning if any link fails, shutdown all links - if (link->trx) { - for (i = 0; i < 8; i++) - bts_setup_slot(&link->trx->slot[i], 0xff); - } + LOGP(DABIS, LOGL_NOTICE, "Abis socket closed.\n"); if (link->rx_msg) { msgb_free(link->rx_msg); diff --git a/src/common/bts.c b/src/common/bts.c index 1c912f03..02cb0fe1 100644 --- a/src/common/bts.c +++ b/src/common/bts.c @@ -1,10 +1,13 @@ +/* BTS support code common to all supported BTS models */ + /* (C) 2011 by Andreas Eversberg + * (C) 2011 by Harald Welte * * 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 + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, @@ -12,9 +15,8 @@ * 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. + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . * */ @@ -29,23 +31,30 @@ #include #include #include +#include + #include -//#include -//#include #include -#include #include #include #include -extern void *l23_ctx; -extern uint8_t cr_loc2rem_cmd; -extern uint8_t cr_loc2rem_resp; -extern uint8_t cr_rem2loc_cmd; -extern uint8_t cr_rem2loc_resp; +void *tall_bts_ctx; + +int bts_init(struct gsm_bts *bts) +{ + struct gsm_bts_role_bts *btsb; + + bts->role = btsb = talloc_zero(bts, struct gsm_bts_role_bts); + + INIT_LLIST_HEAD(&btsb->agch_queue); -BTS_SI_NAME; + /* FIXME: make those parameters configurable */ + btsb->paging_state = paging_init(btsb, 200, 0); + + return bts_model_init(bts); +} #if 0 struct osmobts_lchan *lchan_by_channelnr(struct osmobts_trx *trx, @@ -88,7 +97,6 @@ struct osmobts_lchan *lchan_by_channelnr(struct osmobts_trx *trx, return NULL; } -#endif struct osmocom_bts *create_bts(uint8_t num_trx, char *id) { @@ -267,45 +275,33 @@ void destroy_bts(struct osmocom_bts *bts) abis_close(&bts->link); talloc_free(bts); } +#endif /* main link is established, send status report */ -int bts_link_estab(struct osmocom_bts *bts) +int bts_link_estab(struct gsm_bts *bts) { int i, j; uint8_t radio_state; LOGP(DSUM, LOGL_INFO, "Main link established, sending Status'.\n"); - /* site-manager status */ - oml_tx_state_changed(&bts->link, NM_OPSTATE_ENABLED, NM_AVSTATE_OK, - NM_OC_SITE_MANAGER, 0xff, 0xff, 0xff); - /* bts status */ - oml_tx_state_changed(&bts->link, NM_OPSTATE_ENABLED, NM_AVSTATE_OK, - NM_OC_BTS, 0, 0xff, 0xff); - /* trx */ + oml_mo_state_chg(&bts->site_mgr.mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK); + oml_mo_state_chg(&bts->mo, NM_OPSTATE_ENABLED, NM_AVSTATE_DEPENDENCY); + for (i = 0; i < bts->num_trx; i++) { - radio_state = (bts->trx[i]->link.state == LINK_STATE_CONNECT) ? NM_OPSTATE_ENABLED : NM_OPSTATE_DISABLED; - oml_tx_state_changed(&bts->link, radio_state, - NM_AVSTATE_OK, NM_OC_RADIO_CARRIER, 0, - bts->trx[i]->trx_nr, 0xff); - oml_tx_sw_act_rep(&bts->link, NM_OC_RADIO_CARRIER, 0, bts->trx[i]->trx_nr, 0xff); - oml_tx_state_changed(&bts->link, NM_OPSTATE_ENABLED, - NM_AVSTATE_OK, NM_OC_BASEB_TRANSC, 0, - bts->trx[i]->trx_nr, 0xff); - oml_tx_sw_act_rep(&bts->link, NM_OC_BASEB_TRANSC, 0, bts->trx[i]->trx_nr, 0xff); - /* channel */ - for (j = 0; j < 8; j++) { - if (bts->trx[i]->slot[j].tx_ms) - oml_tx_state_changed(&bts->link, - NM_OPSTATE_DISABLED, NM_AVSTATE_DEPENDENCY, - NM_OC_CHANNEL, 0, bts->trx[i]->trx_nr, - bts->trx[i]->slot[j].slot_nr); - else - oml_tx_state_changed(&bts->link, - NM_OPSTATE_DISABLED, - NM_AVSTATE_NOT_INSTALLED, - NM_OC_CHANNEL, 0, bts->trx[i]->trx_nr, - bts->trx[i]->slot[j].slot_nr); + struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, i); + struct ipabis_link *link = (struct ipabis_link *) trx->rsl_link; + + radio_state = (link && link->state == LINK_STATE_CONNECT) ? NM_OPSTATE_ENABLED : NM_OPSTATE_DISABLED; + oml_mo_state_chg(&trx->mo, radio_state, NM_AVSTATE_OK); + oml_mo_tx_sw_act_rep(&trx->mo); + oml_mo_state_chg(&trx->bb_transc.mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK); + oml_mo_tx_sw_act_rep(&trx->bb_transc.mo); + + for (j = 0; j < ARRAY_SIZE(trx->ts); j++) { + struct gsm_bts_trx_ts *ts = &trx->ts[j]; + + oml_mo_state_chg(&ts->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_DEPENDENCY); } } @@ -313,15 +309,17 @@ int bts_link_estab(struct osmocom_bts *bts) } /* RSL link is established, send status report */ -int trx_link_estab(struct osmobts_trx *trx) +int trx_link_estab(struct gsm_bts_trx *trx) { - uint8_t radio_state = (trx->link.state == LINK_STATE_CONNECT) ? NM_OPSTATE_ENABLED : NM_OPSTATE_DISABLED; + struct ipabis_link *link = (struct ipabis_link *) trx->rsl_link; + uint8_t radio_state = (link->state == LINK_STATE_CONNECT) ? NM_OPSTATE_ENABLED : NM_OPSTATE_DISABLED; - LOGP(DSUM, LOGL_INFO, "RSL link (TRX %02x) state changed to %s, sending Status'.\n", trx->trx_nr, (trx->link.state == LINK_STATE_CONNECT) ? "up" : "down"); + LOGP(DSUM, LOGL_INFO, "RSL link (TRX %02x) state changed to %s, sending Status'.\n", + trx->nr, (link->state == LINK_STATE_CONNECT) ? "up" : "down"); - oml_tx_state_changed(&trx->bts->link, radio_state, NM_AVSTATE_OK, NM_OC_RADIO_CARRIER, 0, trx->trx_nr, 0xff); + oml_mo_state_chg(&trx->mo, radio_state, NM_AVSTATE_OK); - if (trx->link.state == LINK_STATE_CONNECT) + if (link->state == LINK_STATE_CONNECT) rsl_tx_rf_res(trx); return 0; @@ -332,6 +330,7 @@ void bts_new_si(void *arg) struct osmobts_trx *trx = arg; int i; +#if 0 if (osmo_timer_pending(&trx->si.timer)) return; @@ -354,25 +353,41 @@ void bts_new_si(void *arg) trx->si.timer.cb = bts_new_si; trx->si.timer.data = trx; osmo_timer_schedule(&trx->si.timer, 0, 200000); +#endif } -/* handle bts instance (including MS instances) */ -int work_bts(struct osmocom_bts *bts) +int lchan_init_lapdm(struct gsm_lchan *lchan) { - int work = 0, w; - - do { - w = 0; -// w |= xxx_dequeue(ms); - if (w) - work = 1; - } while (w); - return work; + struct lapdm_channel *lc = &lchan->lapdm_ch; + + lapdm_channel_init(lc, LAPDM_MODE_BTS); + lapdm_channel_set_flags(lc, LAPDM_ENT_F_POLLING_ONLY); + lapdm_channel_set_l1(lc, NULL, lchan); + lapdm_channel_set_l3(lc, lapdm_rll_tx_cb, lchan); + + return 0; } -#if 0 -int create_chan(struct osmocom_bts *slot, uint8_t type) + +int bts_agch_enqueue(struct gsm_bts *bts, struct msgb *msg) { - welcher type?: + struct gsm_bts_role_bts *btsb = bts_role_bts(bts);; + + /* FIXME: implement max queue length */ + llist_add_tail(&msg->list, &btsb->agch_queue); + + return 0; } -#endif +struct msgb *bts_agch_dequeue(struct gsm_bts *bts) +{ + struct gsm_bts_role_bts *btsb = bts_role_bts(bts);; + struct msgb *msg; + + if (llist_empty(&btsb->agch_queue)) + return NULL; + + msg = llist_entry(btsb->agch_queue.next, struct msgb, list); + llist_del(&msg->list); + + return msg; +} diff --git a/src/common/gsm_data_shared.c b/src/common/gsm_data_shared.c new file mode 100644 index 00000000..706892db --- /dev/null +++ b/src/common/gsm_data_shared.c @@ -0,0 +1 @@ +#include "../../../openbsc/openbsc/src/libcommon/gsm_data_shared.c" diff --git a/src/common/load_indication.c b/src/common/load_indication.c new file mode 100644 index 00000000..12e41e43 --- /dev/null +++ b/src/common/load_indication.c @@ -0,0 +1,69 @@ +/* Support for generating RSL Load Indication */ + +/* (C) 2011 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License + * along with this program. If not, see . + * + */ + +#include + +#include + +static void reset_load_counters(void) +{ + /* re-set the counters */ + btsb->load.ccch.pch_used = btsb->load.ccch.pch_total = 0; +} + +static void load_timer_cb(void *data) +{ + struct gsm_bts *bts = data; + struct gsm_bts_role_bts *btsb = FIXME; + unsigned int pch_percent; + + /* compute percentages */ + pch_percent = (btsb->load.ccch.pch_used * 100) / btsb->load.ccch.pch_total; + + if (pch_percent >= btsb->load.ccch.load_ind_thresh) { + /* send RSL load indication message to BSC */ + uint16_t paging_buffer_space = FIXME; + rsl_tx_ccch_load_ind_pch(bts, paging_buffer_space); + } + + reset_load_counters(); + + /* re-schedule the timer */ + osmo_timer_schedule(&btsb->load.ccch.timer, + btsb->load.ccch.load_ind_period, 0); +} + +static void load_timer_start(struct gsm_bts *bts) +{ + struct gsm_bts_role_bts *btsb = FIXME; + + btsb->load.ccch.timer.data = bts; + reset_load_counters(); + osmo_timer_schedule(&btsb->load.ccch.timer, + btsb->load.ccch.load_ind_period, 0); + + return 0 +} + +static void load_timer_stop(struct gsm_bts *bts) +{ + osmo_timer_del(&btsb->load.ccch.timer); +} diff --git a/src/common/logging.c b/src/common/logging.c new file mode 100644 index 00000000..362e30cd --- /dev/null +++ b/src/common/logging.c @@ -0,0 +1,122 @@ +/* libosmocore logging support */ + +/* (C) 2011 by Andreas Eversberg + * (C) 2011 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License + * along with this program. If not, see . + * + */ + + +#include + +#include +#include +#include + +#include +#include + +static const struct log_info_cat bts_log_info_cat[] = { + [DRSL] = { + .name = "DRSL", + .description = "A-bis Radio Siganlling Link (RSL)", + .color = "\033[1;35m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DOML] = { + .name = "DOML", + .description = "A-bis Network Management / O&M (NM/OML)", + .color = "\033[1;36m", + .enabled = 1, .loglevel = LOGL_INFO, + }, + [DRLL] = { + .name = "DRLL", + .description = "A-bis Radio Link Layer (RLL)", + .color = "\033[1;31m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DRR] = { + .name = "DRR", + .description = "Layer3 Radio Resource (RR)", + .color = "\033[1;34m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DMEAS] = { + .name = "DMEAS", + .description = "Radio Measurement Processing", + .enabled = 0, .loglevel = LOGL_NOTICE, + }, + [DPAG] = { + .name = "DPAG", + .description = "Paging Subsystem", + .color = "\033[1;38m", + .enabled = 1, .loglevel = LOGL_INFO, + }, + [DL1C] = { + .name = "DL1C", + .description = "Layer 1", + .loglevel = LOGL_DEBUG, + .enabled = 1, + }, + [DL1P] = { + .name = "DL1P", + .description = "Layer 1 Primitives", + .loglevel = LOGL_DEBUG, + .enabled = 0, + }, + [DABIS] = { + .name = "DABIS", + .description = "A-bis Intput Subsystem", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, +#if 0 + [DNS] = { + .name = "DNS", + .description = "GPRS Network Service (NS)", + .enabled = 1, .loglevel = LOGL_INFO, + }, + [DBSSGP] = { + .name = "DBSSGP", + .description = "GPRS BSS Gateway Protocol (BSSGP)", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DLLC] = { + .name = "DLLC", + .description = "GPRS Logical Link Control Protocol (LLC)", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, +#endif +}; + +const struct log_info bts_log_info = { + .cat = bts_log_info_cat, + .num_cat = ARRAY_SIZE(bts_log_info_cat), +}; + +#define DEFAULT_MASK "DL1C:DOML:DRSL:DPAG" + +int bts_log_init(const char *category_mask) +{ + if (!category_mask) + category_mask = DEFAULT_MASK; + + osmo_init_logging(&bts_log_info); + + log_parse_category_mask(osmo_stderr_target, category_mask); + + return 0; +} diff --git a/src/common/oml.c b/src/common/oml.c index 9af09395..92c7848d 100644 --- a/src/common/oml.c +++ b/src/common/oml.c @@ -1,12 +1,13 @@ -/* - * (C) 2011 by Andreas Eversberg +/* GSM TS 12.21 O&M / OML, BTS side */ + +/* (C) 2011 by Andreas Eversberg * (C) 2011 by Harald Welte * * 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 + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, @@ -14,9 +15,8 @@ * 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. + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . * */ @@ -28,95 +28,294 @@ #include #include +#include #include #include + #include -//#include -#include +#include #include -#include -#include -#include #include +#include + +/* FIXME: move this to libosmocore */ +static struct tlv_definition abis_nm_att_tlvdef_ipa = { + .def = { + /* ip.access specifics */ + [NM_ATT_IPACC_DST_IP] = { TLV_TYPE_FIXED, 4 }, + [NM_ATT_IPACC_DST_IP_PORT] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_IPACC_STREAM_ID] = { TLV_TYPE_TV, }, + [NM_ATT_IPACC_SEC_OML_CFG] = { TLV_TYPE_FIXED, 6 }, + [NM_ATT_IPACC_IP_IF_CFG] = { TLV_TYPE_FIXED, 8 }, + [NM_ATT_IPACC_IP_GW_CFG] = { TLV_TYPE_FIXED, 12 }, + [NM_ATT_IPACC_IN_SERV_TIME] = { TLV_TYPE_FIXED, 4 }, + [NM_ATT_IPACC_LOCATION] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_PAGING_CFG] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_IPACC_UNIT_ID] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_UNIT_NAME] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_SNMP_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_PRIM_OML_CFG_LIST] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_NV_FLAGS] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_FREQ_CTRL] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_IPACC_PRIM_OML_FB_TOUT] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_CUR_SW_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_TIMING_BUS] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_CGI] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_RAC] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_OBJ_VERSION] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_GPRS_PAGING_CFG]= { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_NSEI] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_BVCI] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_NSVCI] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_NS_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_BSSGP_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_NS_LINK_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_RLC_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_ALM_THRESH_LIST]= { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_MONIT_VAL_LIST] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_TIB_CONTROL] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_SUPP_FEATURES] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_CODING_SCHEMES] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_RLC_CFG_2] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_HEARTB_TOUT] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_UPTIME] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_RLC_CFG_3] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_SSL_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_SEC_POSSIBLE] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_IML_SSL_STATE] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_REVOC_DATE] = { TLV_TYPE_TL16V }, + }, +}; + +/* ip.access nanoBTS specific commands */ +static const char ipaccess_magic[] = "com.ipaccess"; /* * support */ -struct osmobts_trx *get_trx_by_nr(struct osmocom_bts *bts, uint8_t trx_nr) +struct tlv_parsed *tlvp_copy(const struct tlv_parsed *tp_orig, void *ctx) { - int max = sizeof(bts->trx) / sizeof(bts->trx[0]); - struct osmobts_trx *trx; + struct tlv_parsed *tp_out; + unsigned int i; - if (trx_nr >= max) { - LOGP(DOML, LOGL_NOTICE, "Indicated TRX #%d is out of range. (max #%d)\n", trx_nr, max - 1); + tp_out = talloc_zero(ctx, struct tlv_parsed); + if (!tp_out) return NULL; + + /* if the original is NULL, return empty tlvp */ + if (!tp_orig) + return tp_out; + + for (i = 0; i < ARRAY_SIZE(tp_orig->lv); i++) { + unsigned int len = tp_orig->lv[i].len; + tp_out->lv[i].len = len; + if (len && tp_out->lv[i].val) { + tp_out->lv[i].val = talloc_zero_size(tp_out, len); + if (!tp_out->lv[i].val) { + talloc_free(tp_out); + return NULL; + } + memcpy((uint8_t *)tp_out->lv[i].val, tp_orig->lv[i].val, len); + } } - trx = bts->trx[trx_nr]; - if (!trx) { - LOGP(DOML, LOGL_NOTICE, "Indicated TRX #%d does not exist.\n", trx_nr); - return NULL; + return tp_out; +} + +/* merge all attributes of 'new' into 'out' */ +int tlvp_merge(struct tlv_parsed *out, const struct tlv_parsed *new) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(out->lv); i++) { + unsigned int len = new->lv[i].len; + if (len == 0 || new->lv[i].val == NULL) + continue; + if (out->lv[i].val) { + talloc_free((uint8_t *) out->lv[i].val); + out->lv[i].len = 0; + } + out->lv[i].val = talloc_zero_size(out, len); + if (!out->lv[i].val) + return -ENOMEM; + memcpy((uint8_t *) out->lv[i].val, new->lv[i].val, len); } + return 0; +} - return trx; +static int oml_tlv_parse(struct tlv_parsed *tp, const uint8_t *buf, int len) +{ + return tlv_parse(tp, &abis_nm_att_tlvdef_ipa, buf, len, 0, 0); } -struct osmobts_slot *get_slot_by_nr(struct osmobts_trx *trx, uint8_t slot_nr) +struct msgb *oml_msgb_alloc(void) { - struct osmobts_slot *slot; + return msgb_alloc_headroom(1024, 128, "OML"); +} - if (slot_nr >= 8) { - LOGP(DOML, LOGL_NOTICE, "Indicated Slot #%d is out of range. (max #8)\n", slot_nr); - return NULL; +int oml_send_msg(struct msgb *msg, int is_manuf) +{ + struct abis_om_hdr *omh; + + if (is_manuf) { + /* length byte, string + 0 termination */ + uint8_t *manuf = msgb_push(msg, 1 + sizeof(ipaccess_magic)); + manuf[0] = strlen(ipaccess_magic)+1; + memcpy(manuf+1, ipaccess_magic, strlen(ipaccess_magic)); } - slot = &trx->slot[slot_nr]; + /* Push the main OML header and send it off */ + omh = (struct abis_om_hdr *) msgb_push(msg, sizeof(*omh)); + if (is_manuf) + omh->mdisc = ABIS_OM_MDISC_MANUF; + else + omh->mdisc = ABIS_OM_MDISC_FOM; + omh->placement = ABIS_OM_PLACEMENT_ONLY; + omh->sequence = 0; + omh->length = msgb_l3len(msg); + + msg->l2h = (uint8_t *)omh; + + return abis_oml_sendmsg(msg); +} + +int oml_mo_send_msg(struct gsm_abis_mo *mo, struct msgb *msg, uint8_t msg_type) +{ + struct abis_om_fom_hdr *foh; + + msg->l3h = msgb_push(msg, sizeof(*foh)); + foh = (struct abis_om_fom_hdr *) msg->l3h; + foh->msg_type = msg_type; + foh->obj_class = mo->obj_class; + memcpy(&foh->obj_inst, &mo->obj_inst, sizeof(foh->obj_inst)); + + /* FIXME: This assumption may not always be correct */ + msg->trx = mo->bts->c0; - return slot; + return oml_send_msg(msg, 0); } -static struct msgb *fom_msgb_alloc(void) +/* FIXME: move to gsm_data_shared */ +static char mo_buf[128]; +char *gsm_abis_mo_name(const struct gsm_abis_mo *mo) +{ + snprintf(mo_buf, sizeof(mo_buf), "OC=%s INST=(%02x,%02x,%02x)", + get_value_string(abis_nm_obj_class_names, mo->obj_class), + mo->obj_inst.bts_nr, mo->obj_inst.trx_nr, mo->obj_inst.ts_nr); + return mo_buf; +} + +/* 8.8.1 sending State Changed Event Report */ +int oml_tx_state_changed(struct gsm_abis_mo *mo, + uint8_t op_state, uint8_t avail_status) { struct msgb *nmsg; - nmsg = abis_msgb_alloc(sizeof(struct abis_om_hdr)); + LOGP(DOML, LOGL_INFO, "%s Tx STATE CHG REP\n", gsm_abis_mo_name(mo)); + + nmsg = oml_msgb_alloc(); if (!nmsg) - return NULL; - return nmsg; + return -ENOMEM; + + /* 9.4.38 Operational State */ + msgb_tv_put(nmsg, NM_ATT_OPER_STATE, op_state); + + /* 9.4.7 Availability Status */ + msgb_tl16v_put(nmsg, NM_ATT_AVAIL_STATUS, 1, &avail_status); + + return oml_mo_send_msg(mo, nmsg, NM_MT_STATECHG_EVENT_REP); } -static void fom_push_om(struct msgb *msg, uint8_t mdisc, uint8_t placement, uint8_t sequence) +int oml_mo_state_chg(struct gsm_abis_mo *mo, int op_state, int avail_state) { - struct abis_om_hdr *noh; - - msg->l3h = msg->data; - noh = (struct abis_om_hdr *) msgb_push(msg, sizeof(*noh)); - noh->mdisc = mdisc; - noh->placement = placement; - noh->sequence = sequence; - noh->length = msgb_l3len(msg); + int rc = 0; + + if ((op_state != -1 && mo->nm_state.operational != op_state) || + (avail_state != -1 && mo->nm_state.availability != avail_state)) { + if (avail_state != -1) { + LOGP(DOML, LOGL_INFO, "%s AVAIL STATE %s -> %s\n", + gsm_abis_mo_name(mo), + abis_nm_avail_name(mo->nm_state.availability), + abis_nm_avail_name(avail_state)); + mo->nm_state.availability = avail_state; + } + if (op_state != -1) { + LOGP(DOML, LOGL_INFO, "%s OPER STATE %s -> %s\n", + gsm_abis_mo_name(mo), + abis_nm_opstate_name(mo->nm_state.operational), + abis_nm_opstate_name(op_state)); + mo->nm_state.operational = op_state; + } + + /* send state change report */ + rc = oml_tx_state_changed(mo, op_state, avail_state); + } + return rc; } -static int fom_ack_nack(struct ipabis_link *link, struct msgb *msg, uint8_t cause) +int oml_mo_fom_ack_nack(struct gsm_abis_mo *mo, uint8_t orig_msg_type, + uint8_t cause) { - struct abis_om_fom_hdr *foh = msgb_l3(msg); - uint8_t *ie; + struct msgb *msg; + uint8_t new_msg_type; + + msg = oml_msgb_alloc(); + if (!msg) + return -ENOMEM; + + if (cause) { + new_msg_type = orig_msg_type + 2; + msgb_tv_put(msg, NM_ATT_NACK_CAUSES, cause); + } else { + new_msg_type = orig_msg_type + 1; + } + + return oml_mo_send_msg(mo, msg, new_msg_type); +} + +int oml_mo_opstart_ack(struct gsm_abis_mo *mo) +{ + return oml_mo_fom_ack_nack(mo, NM_MT_OPSTART, 0); +} + +int oml_mo_opstart_nack(struct gsm_abis_mo *mo, uint8_t nack_cause) +{ + return oml_mo_fom_ack_nack(mo, NM_MT_OPSTART, nack_cause); +} + +int oml_fom_ack_nack(struct msgb *old_msg, uint8_t cause) +{ + struct abis_om_hdr *old_oh = msgb_l2(old_msg); + struct abis_om_fom_hdr *old_foh = msgb_l3(old_msg); + struct msgb *msg = oml_msgb_alloc(); + struct abis_om_fom_hdr *foh; + int is_manuf = 0; + + /* make sure to respond with MANUF if request was MANUF */ + if (old_oh->mdisc == ABIS_OM_MDISC_MANUF) + is_manuf = 1; + + msg->trx = old_msg->trx; + + /* copy over old FOM-Header and later only change the msg_type */ + msg->l3h = msgb_push(msg, sizeof(*foh)); + foh = (struct abis_om_fom_hdr *) msg->l3h; + memcpy(foh, old_foh, sizeof(*foh)); /* alter message type */ if (cause) { - LOGP(DOML, LOGL_NOTICE, "Sending FOM NACK with cause %d.\n", cause); + LOGP(DOML, LOGL_NOTICE, "Sending FOM NACK with cause %s.\n", + abis_nm_nack_cause_name(cause)); foh->msg_type += 2; /* nack */ /* add cause */ - ie = msgb_put(msg, 2); - ie[0] = NM_ATT_NACK_CAUSES; - ie[1] = cause; + msgb_tv_put(msg, NM_ATT_NACK_CAUSES, cause); } else { LOGP(DOML, LOGL_NOTICE, "Sending FOM ACK.\n"); foh->msg_type++; /* ack */ } - return abis_tx(link, msg); + return oml_send_msg(msg, is_manuf); } /* @@ -124,246 +323,396 @@ static int fom_ack_nack(struct ipabis_link *link, struct msgb *msg, uint8_t caus */ /* 8.3.7 sending SW Activated Report */ -int oml_tx_sw_act_rep(struct ipabis_link *link, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr) +int oml_mo_tx_sw_act_rep(struct gsm_abis_mo *mo) { struct msgb *nmsg; struct abis_om_fom_hdr *nofh; - LOGP(DOML, LOGL_INFO, "Sending SW Activated Report (%02x,%02x,%02x).\n", bts_nr, trx_nr, ts_nr); + LOGP(DOML, LOGL_INFO, "%s Tx SW ACT REP\n", gsm_abis_mo_name(mo)); - nmsg = fom_msgb_alloc(); + nmsg = oml_msgb_alloc(); if (!nmsg) return -ENOMEM; - nofh = (struct abis_om_fom_hdr *) msgb_put(nmsg, sizeof(*nofh)); - nofh->msg_type = NM_MT_SW_ACTIVATED_REP; - nofh->obj_class = obj_class; - nofh->obj_inst.bts_nr = bts_nr; - nofh->obj_inst.trx_nr = trx_nr; - nofh->obj_inst.ts_nr = ts_nr; - fom_push_om(nmsg, ABIS_OM_MDISC_FOM, ABIS_OM_PLACEMENT_ONLY, 0); - abis_push_ipa(nmsg, IPA_PROTO_OML); - - return abis_tx(link, nmsg); -} -/* 8.8.1 sending State Changed Event Report */ -int oml_tx_state_changed(struct ipabis_link *link, uint8_t op_state, uint8_t avail_status, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr) -{ - struct msgb *nmsg; - struct abis_om_fom_hdr *nofh; - uint8_t *ie; - - LOGP(DOML, LOGL_INFO, "Sending state change (bts=%02x trx=%02x ts=%02x).\n", bts_nr, trx_nr, ts_nr); - - nmsg = fom_msgb_alloc(); - if (!nmsg) - return -ENOMEM; nofh = (struct abis_om_fom_hdr *) msgb_put(nmsg, sizeof(*nofh)); - nofh->msg_type = NM_MT_STATECHG_EVENT_REP; - nofh->obj_class = obj_class; - nofh->obj_inst.bts_nr = bts_nr; - nofh->obj_inst.trx_nr = trx_nr; - nofh->obj_inst.ts_nr = ts_nr; - /* 9.4.38 Operational State */ - ie = msgb_put(nmsg, 2); - ie[0] = NM_ATT_OPER_STATE; - ie[1] = op_state; - /* 9.4.7 Availability Status */ - ie = msgb_put(nmsg, 4); - ie[0] = NM_ATT_AVAIL_STATUS; - ie[1] = 0; - ie[2] = 1; - ie[3] = avail_status; - fom_push_om(nmsg, ABIS_OM_MDISC_FOM, ABIS_OM_PLACEMENT_ONLY, 0); - abis_push_ipa(nmsg, IPA_PROTO_OML); - - return abis_tx(link, nmsg); + + return oml_mo_send_msg(mo, nmsg, NM_MT_SW_ACTIVATED_REP); } -/* 8.6.1 Set BTS Attributes is received */ -int oml_rx_set_bts_attr(struct osmocom_bts *bts, struct msgb *msg) +/* TS 12.21 9.4.53 */ +enum abis_nm_t200_idx { + T200_SDCCH = 0, + T200_FACCH_F = 1, + T200_FACCH_H = 2, + T200_SACCH_TCH_SAPI0 = 3, + T200_SACCH_SDCCH = 4, + T200_SDCCH_SAPI3 = 5, + T200_SACCH_TCH_SAPI3 = 6 +}; + +/* TS 12.21 9.4.53 */ +static const uint8_t abis_nm_t200_mult[] = { + [T200_SDCCH] = 5, + [T200_FACCH_F] = 5, + [T200_FACCH_H] = 5, + [T200_SACCH_TCH_SAPI0] = 10, + [T200_SACCH_SDCCH] = 10, + [T200_SDCCH_SAPI3] = 5, + [T200_SACCH_TCH_SAPI3] = 10 +}; + +/* 8.6.1 Set BTS Attributes has been received */ +static int oml_rx_set_bts_attr(struct gsm_bts *bts, struct msgb *msg) { struct abis_om_fom_hdr *foh = msgb_l3(msg); - struct tlv_parsed tp; - struct bts_support *sup = &bts_support; + struct tlv_parsed tp, *tp_merged; + struct gsm_bts_role_bts *btsb = bts_role_bts(bts); + int rc, i; + const uint8_t *payload; - LOGP(DOML, LOGL_INFO, "BSC is setting BTS attributes:\n"); + abis_nm_debugp_foh(DOML, foh); + DEBUGPC(DOML, "Rx SET BTS ATTR\n"); - tlv_parse(&tp, &abis_nm_att_tlvdef, foh->data, msgb_l3len(msg) - sizeof(*foh), 0, 0); - /* 9.4.31 Maximum Timing Advance */ - if (TLVP_PRESENT(&tp, NM_ATT_MAX_TA)) { - uint16_t *fn = (uint16_t *) TLVP_VAL(&tp, NM_ATT_START_TIME); - bts->max_ta = ntohs(*fn); - LOGP(DOML, LOGL_INFO, " Maximum TA = %d\n", bts->max_ta); - } - /* 9.4.8 BCCH ARFCN */ + rc = oml_tlv_parse(&tp, foh->data, msgb_l3len(msg) - sizeof(*foh)); + if (rc < 0) + return oml_fom_ack_nack(msg, NM_NACK_INCORR_STRUCT); + + /* Test for globally unsupported stuff here */ if (TLVP_PRESENT(&tp, NM_ATT_BCCH_ARFCN)) { - uint16_t *value = (uint16_t *) TLVP_VAL(&tp, NM_ATT_BCCH_ARFCN); + const uint16_t *value = (uint16_t *) TLVP_VAL(&tp, NM_ATT_BCCH_ARFCN); uint16_t arfcn = ntohs(*value); - LOGP(DOML, LOGL_INFO, " ARFCN = %d", bts->bcch_arfcn); - if (arfcn > 1023 || !(sup->freq_map[arfcn >> 3] & (1 << (arfcn & 0x7)))) { + if (arfcn > 1024) { LOGP(DOML, LOGL_NOTICE, "Given ARFCN %d is not supported.\n", arfcn); - return fom_ack_nack(&bts->link, msg, NM_NACK_FREQ_NOTAVAIL); + return oml_fom_ack_nack(msg, NM_NACK_FREQ_NOTAVAIL); } - bts->bcch_arfcn = arfcn; - } - /* 9.4.9 BSIC */ - if (TLVP_PRESENT(&tp, NM_ATT_BSIC)) { - uint8_t *bsic = (uint8_t *) TLVP_VAL(&tp, NM_ATT_BSIC); - bts->bcc = *bsic & 0x7; - bts->ncc = (*bsic >> 3) & 0x7; - LOGP(DOML, LOGL_INFO, " BCC = %d\n", bts->bcc); - LOGP(DOML, LOGL_INFO, " NCC = %d\n", bts->ncc); } /* 9.4.52 Starting Time */ if (TLVP_PRESENT(&tp, NM_ATT_START_TIME)) { - uint16_t *fn = (uint16_t *) TLVP_VAL(&tp, NM_ATT_START_TIME); - bts->start_time = ntohs(*fn); - LOGP(DOML, LOGL_INFO, " Starting Time = %d\n", bts->start_time); + return oml_fom_ack_nack(msg, NM_NACK_SPEC_IMPL_NOTSUPP); } - return fom_ack_nack(&bts->link, msg, 0); + /* merge existing BTS attributes with new attributes */ + tp_merged = tlvp_copy(bts->mo.nm_attr, bts); + tlvp_merge(tp_merged, &tp); + + /* Ask BTS driver to validate new merged attributes */ + rc = bts_model_check_oml(bts, foh->msg_type, bts->mo.nm_attr, tp_merged, bts); + if (rc < 0) { + talloc_free(tp_merged); + /* FIXME: send nack? */ + return rc; + } + + /* Success: replace old BTS attributes with new */ + talloc_free(bts->mo.nm_attr); + bts->mo.nm_attr = tp_merged; + + /* ... and actually still parse them */ + + /* 9.4.25 Interference Level Boundaries */ + if (TLVP_PRESENT(&tp, NM_ATT_INTERF_BOUND)) { + payload = TLVP_VAL(&tp, NM_ATT_INTERF_BOUND); + for (i = 0; i < 6; i++) { + int16_t boundary = *payload; + btsb->interference.boundary[i] = -1 * boundary; + } + /* 9.4.24 Intave Parameter */ + if (TLVP_PRESENT(&tp, NM_ATT_INTAVE_PARAM)) + btsb->interference.intave = *TLVP_VAL(&tp, NM_ATT_INTAVE_PARAM); + + /* 9.4.14 Connection Failure Criterion */ + /* ... can be 'operator dependent' and needs to be parsed by bts driver */ + + /* 9.4.53 T200 */ + if (TLVP_PRESENT(&tp, NM_ATT_T200)) { + payload = TLVP_VAL(&tp, NM_ATT_T200); + for (i = 0; i < ARRAY_SIZE(btsb->t200_ms); i++) + btsb->t200_ms[i] = payload[i] * abis_nm_t200_mult[i]; + } + + /* 9.4.31 Maximum Timing Advance */ + if (TLVP_PRESENT(&tp, NM_ATT_MAX_TA)) { + uint16_t *fn = (uint16_t *) TLVP_VAL(&tp, NM_ATT_MAX_TA); + btsb->max_ta = ntohs(*fn); + } + + /* 9.4.39 Overload Period */ + if (TLVP_PRESENT(&tp, NM_ATT_OVERL_PERIOD)) + btsb->load.overload_period = *TLVP_VAL(&tp, NM_ATT_OVERL_PERIOD); + + /* 9.4.12 CCCH Load Threshold */ + if (TLVP_PRESENT(&tp, NM_ATT_CCCH_L_T)) + btsb->load.ccch.load_ind_thresh = *TLVP_VAL(&tp, NM_ATT_CCCH_L_T); + + /* 9.4.11 CCCH Load Indication Period */ + if (TLVP_PRESENT(&tp, NM_ATT_CCCH_L_I_P)) + btsb->load.ccch.load_ind_period = *TLVP_VAL(&tp, NM_ATT_CCCH_L_I_P); + + /* 9.4.44 RACH Busy Threshold */ + if (TLVP_PRESENT(&tp, NM_ATT_RACH_B_THRESH)) { + int16_t thresh = *TLVP_VAL(&tp, NM_ATT_RACH_B_THRESH); + btsb->load.rach.busy_thresh = -1 * thresh; + } + + /* 9.4.45 RACH Load Averaging Slots */ + if (TLVP_PRESENT(&tp, NM_ATT_LDAVG_SLOTS)) + payload = TLVP_VAL(&tp, NM_ATT_LDAVG_SLOTS); + btsb->load.rach.averaging_slots = ntohs(*(uint16_t *)payload); + } + + /* 9.4.10 BTS Air Timer */ + if (TLVP_PRESENT(&tp, NM_ATT_BTS_AIR_TIMER)) + btsb->t3105_ms = *TLVP_VAL(&tp, NM_ATT_BTS_AIR_TIMER) * 10; + + /* 9.4.37 NY1 */ + if (TLVP_PRESENT(&tp, NM_ATT_NY1)) + btsb->ny1 = *TLVP_VAL(&tp, NM_ATT_NY1); + + /* 9.4.8 BCCH ARFCN */ + if (TLVP_PRESENT(&tp, NM_ATT_BCCH_ARFCN)) { + const uint16_t *value = (uint16_t *) TLVP_VAL(&tp, NM_ATT_BCCH_ARFCN); + bts->c0->arfcn = ntohs(*value); + } + /* 9.4.9 BSIC */ + if (TLVP_PRESENT(&tp, NM_ATT_BSIC)) + bts->bsic = *TLVP_VAL(&tp, NM_ATT_BSIC); + + /* call into BTS driver to apply new attributes to hardware */ + return bts_model_apply_oml(bts, msg, tp_merged, bts); } -/* 8.6.2 Set Radio Attributes is received */ -int oml_rx_set_radio_attr(struct osmocom_bts *bts, struct msgb *msg) +/* 8.6.2 Set Radio Attributes has been received */ +static int oml_rx_set_radio_attr(struct gsm_bts_trx *trx, struct msgb *msg) { struct abis_om_fom_hdr *foh = msgb_l3(msg); - struct tlv_parsed tp; - struct osmobts_trx *trx; - struct bts_support *sup = &bts_support; + struct tlv_parsed tp, *tp_merged; + int rc; + + abis_nm_debugp_foh(DOML, foh); + DEBUGPC(DOML, "Rx SET RADIO CARRIER ATTR\n"); + + rc = oml_tlv_parse(&tp, foh->data, msgb_l3len(msg) - sizeof(*foh)); + if (rc < 0) + return oml_fom_ack_nack(msg, NM_NACK_INCORR_STRUCT); + + /* merge existing BTS attributes with new attributes */ + tp_merged = tlvp_copy(trx->mo.nm_attr, trx->bts); + tlvp_merge(tp_merged, &tp); + + /* Ask BTS driver to validate new merged attributes */ + rc = bts_model_check_oml(trx->bts, foh->msg_type, trx->mo.nm_attr, tp_merged, trx); + if (rc < 0) { + talloc_free(tp_merged); + /* FIXME: send NACK */ + return rc; + } - trx = get_trx_by_nr(bts, foh->obj_inst.trx_nr); - if (!trx) - return fom_ack_nack(&bts->link, msg, NM_NACK_TRXNR_UNKN); + /* Success: replace old BTS attributes with new */ + talloc_free(trx->mo.nm_attr); + trx->mo.nm_attr = tp_merged; - LOGP(DOML, LOGL_INFO, "BSC is setting radio attributes:\n"); + /* ... and actually still parse them */ - tlv_parse(&tp, &abis_nm_att_tlvdef, foh->data, msgb_l3len(msg) - sizeof(*foh), 0, 0); /* 9.4.47 RF Max Power Reduction */ if (TLVP_PRESENT(&tp, NM_ATT_RF_MAXPOWR_R)) { - trx->rf_red = *TLVP_VAL(&tp, NM_ATT_RF_MAXPOWR_R); - LOGP(DOML, LOGL_INFO, " RF Max Power Reduction = %d\n", trx->rf_red); - } else - trx->rf_red = 0; + trx->max_power_red = *TLVP_VAL(&tp, NM_ATT_RF_MAXPOWR_R); + LOGP(DOML, LOGL_INFO, " RF Max Power Reduction = %d\n", trx->max_power_red); + } /* 9.4.5 ARFCN List */ +#if 0 if (TLVP_PRESENT(&tp, NM_ATT_ARFCN_LIST)) { uint16_t *value = (uint16_t *) TLVP_VAL(&tp, NM_ATT_ARFCN_LIST); - uint16_t length = *(TLVP_VAL(&tp, NM_ATT_ARFCN_LIST) - 1); + uint16_t length = TLVP_LEN(&tp, NM_ATT_ARFCN_LIST); uint16_t arfcn; - int max = (sizeof(trx->arfcn_list) / sizeof(trx->arfcn_list[0])); int i; - if (length > max) { - LOGP(DOML, LOGL_NOTICE, "Too many ARFCN given. (max #%d)\n", max); - return fom_ack_nack(&bts->link, msg, NM_NACK_PARAM_RANGE); - } for (i = 0; i < length; i++) { arfcn = ntohs(*value++); - if (arfcn > 1023 || !(sup->freq_map[arfcn >> 3] & (1 << (arfcn & 0x7)))) - return fom_ack_nack(&bts->link, msg, NM_NACK_FREQ_NOTAVAIL); + if (arfcn > 1024) + return oml_fom_ack_nack(msg, NM_NACK_FREQ_NOTAVAIL); trx->arfcn_list[i] = arfcn; LOGP(DOML, LOGL_INFO, " ARFCN list = %d\n", trx->arfcn_list[i]); } trx->arfcn_num = length; } else trx->arfcn_num = 0; +#endif + /* call into BTS driver to apply new attributes to hardware */ + return bts_model_apply_oml(trx->bts, msg, tp_merged, trx); +} - return fom_ack_nack(&bts->link, msg, 0); +static int conf_lchans_for_pchan(struct gsm_bts_trx_ts *ts) +{ + struct gsm_lchan *lchan; + unsigned int i; + + switch (ts->pchan) { + case GSM_PCHAN_CCCH_SDCCH4: + for (i = 0; i < 4; i++) { + lchan = &ts->lchan[i+1]; + lchan->type = GSM_LCHAN_SDCCH; + } + /* fallthrough */ + case GSM_PCHAN_CCCH: + lchan = &ts->lchan[0]; + lchan->type = GSM_LCHAN_CCCH; + break; + case GSM_PCHAN_TCH_F: + lchan = &ts->lchan[0]; + lchan->type = GSM_LCHAN_TCH_F; + break; + case GSM_PCHAN_TCH_H: + for (i = 0; i < 2; i++) { + lchan = &ts->lchan[i]; + lchan->type = GSM_LCHAN_TCH_H; + } + break; + case GSM_PCHAN_SDCCH8_SACCH8C: + for (i = 0; i < 8; i++) { + lchan = &ts->lchan[i]; + lchan->type = GSM_LCHAN_SDCCH; + } + break; + default: + /* FIXME */ + break; + } + return 0; } -/* 8.6.3 Set Channel Attributes is received */ -int oml_rx_set_chan_attr(struct osmocom_bts *bts, struct msgb *msg) +/* 8.6.3 Set Channel Attributes has been received */ +static int oml_rx_set_chan_attr(struct gsm_bts_trx_ts *ts, struct msgb *msg) { struct abis_om_fom_hdr *foh = msgb_l3(msg); - struct tlv_parsed tp; - struct osmobts_trx *trx; - struct osmobts_slot *slot; - struct bts_support *sup = &bts_support; + struct gsm_bts *bts = ts->trx->bts; + struct tlv_parsed tp, *tp_merged; + int rc; + + abis_nm_debugp_foh(DOML, foh); + DEBUGPC(DOML, "Rx SET CHAN ATTR\n"); - trx = get_trx_by_nr(bts, foh->obj_inst.trx_nr); - if (!trx) - return fom_ack_nack(&bts->link, msg, NM_NACK_TRXNR_UNKN); - slot = get_slot_by_nr(trx, foh->obj_inst.ts_nr); - if (!slot) - return fom_ack_nack(&bts->link, msg, NM_NACK_OBJINST_UNKN); + rc = oml_tlv_parse(&tp, foh->data, msgb_l3len(msg) - sizeof(*foh)); + if (rc < 0) + return oml_fom_ack_nack(msg, NM_NACK_INCORR_STRUCT); - LOGP(DOML, LOGL_INFO, "BSC is setting channel attributes:\n"); + /* 9.4.21 HSN... */ + /* 9.4.27 MAIO */ + if (TLVP_PRESENT(&tp, NM_ATT_HSN) || TLVP_PRESENT(&tp, NM_ATT_MAIO)) { + LOGP(DOML, LOGL_NOTICE, "SET CHAN ATTR: Frequency hopping not supported.\n"); + return oml_fom_ack_nack(msg, NM_NACK_SPEC_IMPL_NOTSUPP); + } + + /* 9.4.52 Starting Time */ + if (TLVP_PRESENT(&tp, NM_ATT_START_TIME)) { + LOGP(DOML, LOGL_NOTICE, "SET CHAN ATTR: Starting time not supported.\n"); + return oml_fom_ack_nack(msg, NM_NACK_SPEC_IMPL_NOTSUPP); + } + + /* merge existing BTS attributes with new attributes */ + tp_merged = tlvp_copy(bts->mo.nm_attr, bts); + tlvp_merge(tp_merged, &tp); + + /* Call into BTS driver to check attribute values */ + rc = bts_model_check_oml(bts, foh->msg_type, ts->mo.nm_attr, tp_merged, ts); + if (rc < 0) { + talloc_free(&tp_merged); + /* FIXME: Send NACK */ + return rc; + } - tlv_parse(&tp, &abis_nm_att_tlvdef, foh->data, msgb_l3len(msg) - sizeof(*foh), 0, 0); /* 9.4.13 Channel Combination */ if (TLVP_PRESENT(&tp, NM_ATT_CHAN_COMB)) { uint8_t comb = *TLVP_VAL(&tp, NM_ATT_CHAN_COMB); - if (!sup->chan_comb[comb]) { - LOGP(DOML, LOGL_NOTICE, " channel combination %d (not supported).\n", comb); - return fom_ack_nack(&bts->link, msg, NM_NACK_SPEC_IMPL_NOTSUPP); - } - LOGP(DOML, LOGL_INFO, " channel combination = %s\n", bts_support_comb_name(comb)); - bts_setup_slot(slot, comb); - slot->chan_comb = comb; + ts->pchan = abis_nm_pchan4chcomb(comb); + conf_lchans_for_pchan(ts); } - /* 9.4.21 HSN... */ - if (TLVP_PRESENT(&tp, NM_ATT_HSN)) { - LOGP(DOML, LOGL_NOTICE, "Frequency hopping not supported.\n"); - return fom_ack_nack(&bts->link, msg, NM_NACK_SPEC_IMPL_NOTSUPP); + + /* 9.4.5 ARFCN List */ + + /* 9.4.60 TSC */ + if (TLVP_PRESENT(&tp, NM_ATT_TSC) && TLVP_LEN(&tp, NM_ATT_TSC) >= 1) { + ts->tsc = *TLVP_VAL(&tp, NM_ATT_TSC); + } else { + /* If there is no TSC specified, use the BCC */ + ts->tsc = bts->bsic & 0x3; } + DEBUGP(DOML, "TS %u, settig TSC = %u\n", ts->nr, ts->tsc); - return fom_ack_nack(&bts->link, msg, 0); + /* call into BTS driver to apply new attributes to hardware */ + return bts_model_apply_oml(bts, msg, tp_merged, ts); } -/* 8.9.2 Opstart is received */ -int oml_rx_opstart(struct osmocom_bts *bts, struct msgb *msg) +/* 8.9.2 Opstart has been received */ +static int oml_rx_opstart(struct gsm_bts *bts, struct msgb *msg) { struct abis_om_fom_hdr *foh = msgb_l3(msg); - struct osmobts_trx *trx; - struct osmobts_slot *slot; - - - /* site manager */ - if (foh->obj_inst.bts_nr == 0xff) { - LOGP(DOML, LOGL_INFO, "BSC is sending Opstart. (Site Manager)\n"); - oml_tx_state_changed(&bts->link, NM_OPSTATE_ENABLED, NM_AVSTATE_OK, NM_OC_SITE_MANAGER, foh->obj_inst.bts_nr, foh->obj_inst.trx_nr, foh->obj_inst.ts_nr); - return fom_ack_nack(&bts->link, msg, 0); - } - -#warning todo: change state - /* BTS */ - if (foh->obj_inst.trx_nr == 0xff) { - LOGP(DOML, LOGL_INFO, "BSC is sending Opstart. (BTS)\n"); - return fom_ack_nack(&bts->link, msg, 0); - } - - /* TRX */ - trx = get_trx_by_nr(bts, foh->obj_inst.trx_nr); - if (!trx) - return fom_ack_nack(&bts->link, msg, NM_NACK_TRXNR_UNKN); - if (foh->obj_inst.ts_nr == 0xff) { - LOGP(DOML, LOGL_INFO, "BSC is sending Opstart. (TRX %d)\n", trx->trx_nr); - if (trx->link.state == LINK_STATE_IDLE) { - int ret; - - /* connecting TRX */ - ret = abis_open(&trx->link, bts->link.ip); - if (ret <= 0) { - LOGP(DOML, LOGL_ERROR, "Failed to connect TRX.\n"); - return fom_ack_nack(&bts->link, msg, NM_NACK_CANT_PERFORM); - } - } - return fom_ack_nack(&bts->link, msg, 0); + struct gsm_abis_mo *mo; + void *obj; + + abis_nm_debugp_foh(DOML, foh); + DEBUGPC(DOML, "Rx OPSTART\n"); + + /* Step 1: Resolve MO by obj_class/obj_inst */ + mo = gsm_objclass2mo(bts, foh->obj_class, &foh->obj_inst); + obj = gsm_objclass2obj(bts, foh->obj_class, &foh->obj_inst); + if (!mo || !obj) + return oml_fom_ack_nack(msg, NM_NACK_OBJINST_UNKN); + + /* Step 2: Do some global dependency/consistency checking */ + if (mo->nm_state.operational == NM_OPSTATE_ENABLED) { + DEBUGP(DOML, "... automatic ACK, OP state already was Enabled\n"); + return oml_mo_opstart_ack(mo); + } + + /* Step 3: Ask BTS driver to apply the opstart */ + return bts_model_opstart(bts, mo, obj); +} + +static int oml_rx_chg_adm_state(struct gsm_bts *bts, struct msgb *msg) +{ + struct abis_om_fom_hdr *foh = msgb_l3(msg); + struct tlv_parsed tp; + struct gsm_abis_mo *mo; + uint8_t adm_state; + void *obj; + int rc; + + abis_nm_debugp_foh(DOML, foh); + DEBUGPC(DOML, "Rx CHG ADM STATE\n"); + + rc = oml_tlv_parse(&tp, foh->data, msgb_l3len(msg) - sizeof(*foh)); + if (rc < 0) { + LOGP(DOML, LOGL_ERROR, "Rx CHG ADM STATE: error during TLV parse\n"); + return oml_fom_ack_nack(msg, NM_NACK_INCORR_STRUCT); } - /* slot */ - slot = get_slot_by_nr(trx, foh->obj_inst.ts_nr); - if (!slot) - return fom_ack_nack(&bts->link, msg, NM_NACK_OBJINST_UNKN); + if (!TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) { + LOGP(DOML, LOGL_ERROR, "Rx CHG ADM STATE: no ADM state attribute\n"); + return oml_fom_ack_nack(msg, NM_NACK_INCORR_STRUCT); + } + + adm_state = *TLVP_VAL(&tp, NM_ATT_ADM_STATE); + + /* Step 1: Resolve MO by obj_class/obj_inst */ + mo = gsm_objclass2mo(bts, foh->obj_class, &foh->obj_inst); + obj = gsm_objclass2obj(bts, foh->obj_class, &foh->obj_inst); + if (!mo || !obj) + return oml_fom_ack_nack(msg, NM_NACK_OBJINST_UNKN); - LOGP(DOML, LOGL_INFO, "BSC is sending Opstart. (trx=%d ts=%d)\n", trx->trx_nr, slot->slot_nr); - oml_tx_state_changed(&bts->link, NM_OPSTATE_ENABLED, NM_AVSTATE_OK, NM_OC_CHANNEL, foh->obj_inst.bts_nr, foh->obj_inst.trx_nr, foh->obj_inst.ts_nr); - return fom_ack_nack(&bts->link, msg, 0); + /* Step 2: Do some global dependency/consistency checking */ + if (mo->nm_state.administrative == adm_state) { + DEBUGP(DOML, "... automatic ACK, ADM state already was %s\n", + get_value_string(abis_nm_adm_state_names, adm_state)); + return oml_fom_ack_nack(msg, 0); + } + + /* Step 3: Ask BTS driver to apply the state chg */ + return bts_model_chg_adm_state(bts, mo, obj, adm_state); } -static int down_fom(struct osmocom_bts *bts, struct msgb *msg) +static int down_fom(struct gsm_bts *bts, struct msgb *msg) { struct abis_om_fom_hdr *foh = msgb_l3(msg); + struct gsm_bts_trx *trx; int ret; if (msgb_l2len(msg) < sizeof(*foh)) { @@ -374,7 +723,7 @@ static int down_fom(struct osmocom_bts *bts, struct msgb *msg) if (foh->obj_inst.bts_nr != 0 && foh->obj_inst.bts_nr != 0xff) { LOGP(DOML, LOGL_INFO, "Formatted O&M with BTS %d out of range.\n", foh->obj_inst.bts_nr); - return fom_ack_nack(&bts->link, msg, NM_NACK_BTSNR_UNKN); + return oml_fom_ack_nack(msg, NM_NACK_BTSNR_UNKN); } switch (foh->msg_type) { @@ -382,22 +731,29 @@ static int down_fom(struct osmocom_bts *bts, struct msgb *msg) ret = oml_rx_set_bts_attr(bts, msg); break; case NM_MT_SET_RADIO_ATTR: - ret = oml_rx_set_radio_attr(bts, msg); + trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr); + if (!trx) + return oml_fom_ack_nack(msg, NM_NACK_TRXNR_UNKN); + ret = oml_rx_set_radio_attr(trx, msg); break; case NM_MT_SET_CHAN_ATTR: - ret = oml_rx_set_chan_attr(bts, msg); + trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr); + if (!trx) + return oml_fom_ack_nack(msg, NM_NACK_TRXNR_UNKN); + if (foh->obj_inst.ts_nr >= ARRAY_SIZE(trx->ts)) + return oml_fom_ack_nack(msg, NM_NACK_OBJINST_UNKN); + ret = oml_rx_set_chan_attr(&trx->ts[foh->obj_inst.ts_nr], msg); break; case NM_MT_OPSTART: ret = oml_rx_opstart(bts, msg); break; case NM_MT_CHG_ADM_STATE: - LOGP(DOML, LOGL_INFO, "BSC is changing ADM state.\n"); - ret = fom_ack_nack(&bts->link, msg, 0); + ret = oml_rx_chg_adm_state(bts, msg); break; default: LOGP(DOML, LOGL_INFO, "unknown Formatted O&M msg_type 0x%02x\n", foh->msg_type); - ret = fom_ack_nack(&bts->link, msg, NM_NACK_MSGTYPE_INVAL); + ret = oml_fom_ack_nack(msg, NM_NACK_MSGTYPE_INVAL); } return ret; @@ -407,37 +763,107 @@ static int down_fom(struct osmocom_bts *bts, struct msgb *msg) * manufacturer related messages */ -static int down_mom(struct osmocom_bts *bts, struct msgb *msg) + +static int rx_oml_ipa_rsl_connect(struct gsm_bts_trx *trx, struct msgb *msg, + struct tlv_parsed *tp) { - struct abis_om_fom_hdr *foh = msgb_l3(msg); + struct ipabis_link *oml_link = (struct ipabis_link *) trx->bts->oml_link; + uint16_t port = IPA_TCP_PORT_RSL; + uint32_t ip = oml_link->ip; + struct in_addr in; + int rc; + + uint8_t stream_id = 0; + + DEBUGP(DOML, "Rx IPA RSL CONNECT "); + + if (TLVP_PRESENT(tp, NM_ATT_IPACC_DST_IP)) { + const uint8_t *ptr = TLVP_VAL(tp, NM_ATT_IPACC_DST_IP); + ip = ntohl(*(uint32_t *)ptr); + } + if (TLVP_PRESENT(tp, NM_ATT_IPACC_DST_IP_PORT)) { + const uint8_t *ptr = TLVP_VAL(tp, NM_ATT_IPACC_DST_IP_PORT); + port = ntohs(*(uint16_t *)ptr); + } + if (TLVP_PRESENT(tp, NM_ATT_IPACC_STREAM_ID)) { + stream_id = *TLVP_VAL(tp, NM_ATT_IPACC_STREAM_ID); + } + + in.s_addr = htonl(ip); + DEBUGPC(DOML, "IP=%s PORT=%u STREAM=0x%02x\n", inet_ntoa(in), + port, stream_id); + + if (!trx->rsl_link) { + struct ipabis_link *rsl_link = talloc_zero(trx, struct ipabis_link); + rsl_link->trx = trx; + trx->rsl_link = rsl_link; + } + + /* FIXME: we cannot even use a non-standard port here */ + rc = abis_open(trx->rsl_link, ip); + if (rc < 0) { + LOGP(DOML, LOGL_ERROR, "Error in abis_open(RSL): %d\n", rc); + return oml_fom_ack_nack(msg, NM_NACK_CANT_PERFORM); + } + + return oml_fom_ack_nack(msg, 0); +} + +static int down_mom(struct gsm_bts *bts, struct msgb *msg) +{ + struct abis_om_hdr *oh = msgb_l2(msg); + struct abis_om_fom_hdr *foh; + struct gsm_bts_trx *trx; + uint8_t idstrlen = oh->data[0]; + struct tlv_parsed tp; int ret; + DEBUGP(DOML, "Manufacturer OML message\n"); + if (msgb_l2len(msg) < sizeof(*foh)) { LOGP(DOML, LOGL_NOTICE, "Manufacturer O&M message too short\n"); msgb_free(msg); return -EIO; } + if (strncmp((char *)&oh->data[1], ipaccess_magic, idstrlen)) { + LOGP(DOML, LOGL_ERROR, "Manufacturer OML message != ipaccess not supported\n"); + return -EINVAL; + } + + msg->l3h = oh->data + 1 + idstrlen; + foh = (struct abis_om_fom_hdr *) msg->l3h; + if (foh->obj_inst.bts_nr != 0 && foh->obj_inst.bts_nr != 0xff) { LOGP(DOML, LOGL_INFO, "Manufacturer O&M with BTS %d out of range.\n", foh->obj_inst.bts_nr); - return fom_ack_nack(&bts->link, msg, NM_NACK_BTSNR_UNKN); + return oml_fom_ack_nack(msg, NM_NACK_BTSNR_UNKN); + } + + ret = oml_tlv_parse(&tp, foh->data, oh->length - sizeof(*foh)); + if (ret < 0) { + LOGP(DOML, LOGL_ERROR, "TLV parse error %d\n", ret); + return oml_fom_ack_nack(msg, NM_NACK_BTSNR_UNKN); } + abis_nm_debugp_foh(DOML, foh); + DEBUGPC(DOML, "IPACCESS(0x%02x): ", foh->msg_type); + switch (foh->msg_type) { + case NM_MT_IPACC_RSL_CONNECT: + trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr); + ret = rx_oml_ipa_rsl_connect(trx, msg, &tp); + break; default: LOGP(DOML, LOGL_INFO, "Manufacturer Formatted O&M msg_type 0x%02x\n", foh->msg_type); - ret = fom_ack_nack(&bts->link, msg, NM_NACK_MSGTYPE_INVAL); + ret = oml_fom_ack_nack(msg, NM_NACK_MSGTYPE_INVAL); } return ret; } -/* - * selecting messages - */ - -int down_oml(struct osmocom_bts *bts, struct msgb *msg) +/* incoming OML message from BSC */ +int down_oml(struct gsm_bts *bts, struct msgb *msg) { struct abis_om_hdr *oh = msgb_l2(msg); int ret = 0; @@ -478,4 +904,10 @@ int down_oml(struct osmocom_bts *bts, struct msgb *msg) return ret; } +int oml_init(void) +{ + DEBUGP(DOML, "Initializing OML attribute definitions\n"); + tlv_def_patch(&abis_nm_att_tlvdef_ipa, &abis_nm_att_tlvdef); + return 0; +} diff --git a/src/common/paging.c b/src/common/paging.c new file mode 100644 index 00000000..0f51d753 --- /dev/null +++ b/src/common/paging.c @@ -0,0 +1,440 @@ +/* Paging message encoding + queue management */ + +/* (C) 2011 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License + * along with this program. If not, see . + * + */ + +/* TODO: + * eMLPP priprity + * add P1/P2/P3 rest octets + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#define MAX_PAGING_BLOCKS_CCCH 9 +#define MAX_BS_PA_MFRMS 9 + +struct paging_record { + struct llist_head list; + time_t expiration_time; + uint8_t chan_needed; + uint8_t identity_lv[9]; +}; + +struct paging_state { + /* parameters taken / interpreted from BCCH/CCCH configuration */ + struct gsm48_control_channel_descr chan_desc; + + /* configured otherwise */ + unsigned int paging_lifetime; /* in seconds */ + unsigned int num_paging_max; + + /* total number of currently active paging records in queue */ + unsigned int num_paging; + struct llist_head paging_queue[MAX_PAGING_BLOCKS_CCCH*MAX_BS_PA_MFRMS]; +}; + +static int tmsi_mi_to_uint(uint32_t *out, const uint8_t *tmsi_lv) +{ + if (tmsi_lv[0] < 5) + return -EINVAL; + if ((tmsi_lv[1] & 7) != GSM_MI_TYPE_TMSI) + return -EINVAL; + + *out = *((uint32_t *)(tmsi_lv+2)); + + return 0; +} + +/* paging block numbers in a simple non-combined CCCH */ +static const uint8_t block_by_tdma51[51] = { + 255, 255, /* FCCH, SCH */ + 255, 255, 255, 255, /* BCCH */ + 0, 0, 0, 0, /* B0(6..9) */ + 255, 255, /* FCCH, SCH */ + 1, 1, 1, 1, /* B1(12..15) */ + 2, 2, 2, 2, /* B2(16..19) */ + 255, 255, /* FCCH, SCH */ + 3, 3, 3, 3, /* B3(22..25) */ + 4, 4, 4, 4, /* B3(26..29) */ + 255, 255, /* FCCH, SCH */ + 5, 5, 5, 5, /* B3(32..35) */ + 6, 6, 6, 6, /* B3(36..39) */ + 255, 255, /* FCCH, SCH */ + 7, 7, 7, 7, /* B3(42..45) */ + 8, 8, 8, 8, /* B3(46..49) */ + 255, /* empty */ +}; + +/* get the paging block number _within_ current 51 multiframe */ +static int get_pag_idx_n(struct paging_state *ps, struct gsm_time *gt) +{ + int blk_n = block_by_tdma51[gt->t3]; + int blk_idx; + + if (blk_n == 255) + return -EINVAL; + + blk_idx = blk_n - ps->chan_desc.bs_ag_blks_res; + if (blk_idx < 0) + return -EINVAL; + + return blk_idx; +} + +/* get paging block index over multiple 51 multiframes */ +static int get_pag_subch_nr(struct paging_state *ps, struct gsm_time *gt) +{ + int pag_idx = get_pag_idx_n(ps, gt); + unsigned int n_pag_blks_51 = gsm0502_get_n_pag_blocks(&ps->chan_desc); + unsigned int mfrm_part; + + if (pag_idx < 0) + return pag_idx; + + mfrm_part = ((gt->fn / 51) % (ps->chan_desc.bs_pa_mfrms+2)) * n_pag_blks_51; + + return pag_idx + mfrm_part; +} + + +/* Add an identity to the paging queue */ +int paging_add_identity(struct paging_state *ps, uint8_t paging_group, + const uint8_t *identity_lv, uint8_t chan_needed) +{ + struct llist_head *group_q = &ps->paging_queue[paging_group]; + struct paging_record *pr; + + if (ps->num_paging >= ps->num_paging_max) { + LOGP(DPAG, LOGL_NOTICE, "Dropping paging, queue full (%u)\n", + ps->num_paging); + return -ENOSPC; + } + + /* Check if we already have this identity */ + llist_for_each_entry(pr, group_q, list) { + if (identity_lv[0] == pr->identity_lv[0] && + !memcmp(identity_lv+1, pr->identity_lv+1, identity_lv[0])) { + LOGP(DPAG, LOGL_INFO, "Ignoring duplicate paging\n"); + pr->expiration_time = time(NULL) + ps->paging_lifetime; + return -EEXIST; + } + } + + pr = talloc_zero(ps, struct paging_record); + if (!pr) + return -ENOMEM; + + if (*identity_lv + 1 > sizeof(pr->identity_lv)) { + talloc_free(pr); + return -E2BIG; + } + + LOGP(DPAG, LOGL_INFO, "Add paging to queue (group=%u, queue_len=%u)\n", + paging_group, ps->num_paging+1); + + pr->expiration_time = time(NULL) + ps->paging_lifetime; + pr->chan_needed = chan_needed; + memcpy(&pr->identity_lv, identity_lv, identity_lv[0]+1); + + /* enqueue the new identity to the HEAD of the queue, + * to ensure it will be paged quickly at least once. */ + llist_add(&pr->list, group_q); + ps->num_paging++; + + return 0; +} + +static int fill_paging_type_1(uint8_t *out_buf, const uint8_t *identity1_lv, + uint8_t chan1, const uint8_t *identity2_lv, + uint8_t chan2) +{ + struct gsm48_paging1 *pt1 = (struct gsm48_paging1 *) out_buf; + uint8_t *cur; + + memset(out_buf, 0, sizeof(*pt1)); + + pt1->proto_discr = GSM48_PDISC_RR; + pt1->msg_type = GSM48_MT_RR_PAG_REQ_1; + pt1->pag_mode = GSM48_PM_NORMAL; + pt1->cneed1 = chan1 & 3; + pt1->cneed2 = chan2 & 3; + cur = lv_put(pt1->data, identity1_lv[0], identity1_lv+1); + if (identity2_lv) + cur = lv_put(cur, identity2_lv[0], identity2_lv+1); + + pt1->l2_plen = cur - out_buf - 1; + + return cur - out_buf; +} + +static int fill_paging_type_2(uint8_t *out_buf, const uint8_t *tmsi1_lv, + uint8_t cneed1, const uint8_t *tmsi2_lv, + uint8_t cneed2, const uint8_t *identity3_lv) +{ + struct gsm48_paging2 *pt2 = (struct gsm48_paging2 *) out_buf; + uint8_t *cur; + + memset(out_buf, 0, sizeof(*pt2)); + + pt2->proto_discr = GSM48_PDISC_RR; + pt2->msg_type = GSM48_MT_RR_PAG_REQ_2; + pt2->pag_mode = GSM48_PM_NORMAL; + pt2->cneed1 = cneed1; + pt2->cneed2 = cneed2; + tmsi_mi_to_uint(&pt2->tmsi1, tmsi1_lv); + tmsi_mi_to_uint(&pt2->tmsi2, tmsi2_lv); + cur = out_buf + sizeof(*pt2); + + if (identity3_lv) + cur = lv_put(pt2->data, identity3_lv[0], identity3_lv+1); + + pt2->l2_plen = cur - out_buf -1; + + return cur - out_buf; +} + +static int fill_paging_type_3(uint8_t *out_buf, const uint8_t *tmsi1_lv, + uint8_t cneed1, const uint8_t *tmsi2_lv, + uint8_t cneed2, const uint8_t *tmsi3_lv, + const uint8_t *tmsi4_lv) +{ + struct gsm48_paging3 *pt3 = (struct gsm48_paging3 *) out_buf; + uint8_t *cur; + + memset(out_buf, 0, sizeof(*pt3)); + + pt3->proto_discr = GSM48_PDISC_RR; + pt3->msg_type = GSM48_MT_RR_PAG_REQ_3; + pt3->pag_mode = GSM48_PM_NORMAL; + pt3->cneed1 = cneed1; + pt3->cneed2 = cneed2; + tmsi_mi_to_uint(&pt3->tmsi1, tmsi1_lv); + tmsi_mi_to_uint(&pt3->tmsi2, tmsi2_lv); + tmsi_mi_to_uint(&pt3->tmsi3, tmsi3_lv); + tmsi_mi_to_uint(&pt3->tmsi4, tmsi4_lv); + + cur = out_buf + sizeof(*pt3); + + return cur - out_buf; +} + +static const uint8_t empty_id_lv[] = { 0x01, 0x00 }; + +static struct paging_record *dequeue_pr(struct llist_head *group_q) +{ + struct paging_record *pr; + + pr = llist_entry(group_q->next, struct paging_record, list); + llist_del(&pr->list); + + return pr; +} + +static int pr_is_imsi(struct paging_record *pr) +{ + if ((pr->identity_lv[1] & 7) == GSM_MI_TYPE_IMSI) + return 1; + else + return 0; +} + +static void sort_pr_tmsi_imsi(struct paging_record *pr[], unsigned int n) +{ + int i, j; + struct paging_record *t; + + if (n < 2) + return; + + /* simple bubble sort */ + for (i = n-2; i >= 0; i--) { + for (j=0; j<=i ; j++) { + if (pr_is_imsi(pr[j]) > pr_is_imsi(pr[j+1])) { + t = pr[j]; + pr[j] = pr[j+1]; + pr[j+1] = t; + } + } + } +} + +/* generate paging message for given gsm time */ +int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *gt) +{ + unsigned int group = get_pag_subch_nr(ps, gt); + struct llist_head *group_q = &ps->paging_queue[group]; + int len; + + /* There is nobody to be paged, send Type1 with two empty ID */ + if (llist_empty(group_q)) { + //DEBUGP(DPAG, "Tx PAGING TYPE 1 (empty)\n"); + len = fill_paging_type_1(out_buf, empty_id_lv, 0, + NULL, 0); + } else { + struct paging_record *pr[4]; + unsigned int num_pr = 0; + time_t now = time(NULL); + unsigned int i, num_imsi = 0; + + /* get (if we have) up to four paging records */ + for (i = 0; i < ARRAY_SIZE(pr); i++) { + if (llist_empty(group_q)) + break; + pr[i] = dequeue_pr(group_q); + num_pr++; + + /* count how many IMSIs are among them */ + if (pr_is_imsi(pr[i])) + num_imsi++; + } + + /* make sure the TMSIs are ahead of the IMSIs in the array */ + sort_pr_tmsi_imsi(pr, num_pr); + + if (num_pr == 4 && num_imsi == 0) { + /* No IMSI: easy case, can use TYPE 3 */ + DEBUGP(DPAG, "Tx PAGING TYPE 3 (4 TMSI)\n"); + len = fill_paging_type_3(out_buf, pr[0]->identity_lv, + pr[0]->chan_needed, + pr[1]->identity_lv, + pr[1]->chan_needed, + pr[2]->identity_lv, + pr[3]->identity_lv); + } else if (num_pr >= 3 && num_imsi <= 1) { + /* 3 or 4, of which only up to 1 is IMSI */ + DEBUGP(DPAG, "Tx PAGING TYPE 2 (2 TMSI,1 xMSI)\n"); + len = fill_paging_type_2(out_buf, + pr[0]->identity_lv, + pr[0]->chan_needed, + pr[1]->identity_lv, + pr[1]->chan_needed, + pr[2]->identity_lv); + if (num_pr == 4) { + /* re-add #4 for next time */ + llist_add(&pr[3]->list, group_q); + pr[3] = NULL; + } + } else if (num_pr == 1) { + DEBUGP(DPAG, "Tx PAGING TYPE 1 (1 xMSI,1 empty)\n"); + len = fill_paging_type_1(out_buf, pr[0]->identity_lv, + pr[0]->chan_needed, NULL, 0); + } else { + /* 2 (any type) or + * 3 or 4, of which only 2 will be sent */ + DEBUGP(DPAG, "Tx PAGING TYPE 1 (2 xMSI)\n"); + len = fill_paging_type_1(out_buf, pr[0]->identity_lv, + pr[0]->chan_needed, + pr[1]->identity_lv, + pr[1]->chan_needed); + if (num_pr >= 3) { + /* re-add #4 for next time */ + llist_add(&pr[2]->list, group_q); + pr[2] = NULL; + } + if (num_pr == 4) { + /* re-add #4 for next time */ + llist_add(&pr[3]->list, group_q); + pr[3] = NULL; + } + } + + for (i = 0; i < num_pr; i++) { + /* skip those that we might have re-added above */ + if (pr[i] == NULL) + continue; + /* check if we can expire the paging record, + * or if we need to re-queue it */ + if (pr[i]->expiration_time >= now) { + talloc_free(pr[i]); + ps->num_paging--; + LOGP(DPAG, LOGL_INFO, "Removed paging record, queue_len=%u\n", + ps->num_paging); + } else + llist_add_tail(&pr[i]->list, group_q); + } + } + memset(out_buf+len, 0x2B, GSM_MACBLOCK_LEN-len); + return len; +} + +int paging_si_update(struct paging_state *ps, struct gsm48_control_channel_descr *chan_desc) +{ + LOGP(DPAG, LOGL_INFO, "Paging SI update\n"); + + memcpy(&ps->chan_desc, chan_desc, sizeof(chan_desc)); + + /* FIXME: do we need to re-sort the old paging_records? */ + + return 0; +} + +static int paging_signal_cbfn(unsigned int subsys, unsigned int signal, void *hdlr_data, + void *signal_data) +{ + if (subsys == SS_GLOBAL && signal == S_NEW_SYSINFO) { + struct gsm_bts *bts = signal_data; + struct gsm_bts_role_bts *btsb = bts->role; + struct paging_state *ps = btsb->paging_state; + struct gsm48_system_information_type_3 *si3 = (void *) bts->si_buf[SYSINFO_TYPE_3]; + + paging_si_update(ps, &si3->control_channel_desc); + } + return 0; +} + +static int initialized = 0; + +struct paging_state *paging_init(void *ctx, unsigned int num_paging_max, + unsigned int paging_lifetime) +{ + struct paging_state *ps; + unsigned int i; + + ps = talloc_zero(ctx, struct paging_state); + if (!ps) + return NULL; + + ps->paging_lifetime = paging_lifetime; + ps->num_paging_max = num_paging_max; + + for (i = 0; i < ARRAY_SIZE(ps->paging_queue); i++) + INIT_LLIST_HEAD(&ps->paging_queue[i]); + + if (!initialized) { + osmo_signal_register_handler(SS_GLOBAL, paging_signal_cbfn, NULL); + initialized = 1; + } + return ps; +} diff --git a/src/common/rsl.c b/src/common/rsl.c index 2f6fbfdb..650db245 100644 --- a/src/common/rsl.c +++ b/src/common/rsl.c @@ -1,11 +1,13 @@ -/* - * (C) 2011 by Andreas Eversberg +/* GSM TS 08.58 RSL, BTS Side */ + +/* (C) 2011 by Andreas Eversberg + * (C) 2011 by Harald Welte * * 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 + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, @@ -13,105 +15,120 @@ * 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. + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . * */ -/* - * Radio Link Layer Messages - */ - #include #include #include #include -#include -#include -#include +#include +#include +#include +#include + #include -//#include +#include #include #include #include #include #include -#include +#include +#include + +static int rsl_tx_error_report(struct gsm_bts_trx *trx, uint8_t cause); + +/* list of RSL SI types that can occur on the SACCH */ +static const unsigned int rsl_sacch_sitypes[] = { + RSL_SYSTEM_INFO_5, + RSL_SYSTEM_INFO_6, + RSL_SYSTEM_INFO_5bis, + RSL_SYSTEM_INFO_5ter, + RSL_EXT_MEAS_ORDER, + RSL_MEAS_INFO, +}; + +/* FIXME: move this to libosmocore */ +int osmo_in_array(unsigned int search, const unsigned int *arr, unsigned int size) +{ + unsigned int i; + for (i = 0; i < size; i++) { + if (arr[i] == search) + return 1; + } + return 0; +} +#define OSMO_IN_ARRAY(search, arr) osmo_in_array(search, arr, ARRAY_SIZE(arr)) + +/* FIXME: move this to libosmocore */ +void gsm48_gen_starting_time(uint8_t *out, struct gsm_time *gtime) +{ + uint8_t t1p = gtime->t1 % 32; + out[0] = (t1p << 3) | (gtime->t3 >> 3); + out[1] = (gtime->t3 << 5) | gtime->t2; +} + -static int rsl_tx_error_report(struct osmobts_trx *trx, uint8_t cause); /* * support */ -static uint8_t bts_si_list[BTS_SI_NUM] = BTS_SI_LIST; -struct osmobts_lchan *rsl_get_chan(struct osmobts_trx *trx, uint8_t chan_nr) +#warning merge lchan_lookup with OpenBSC +/* determine logical channel based on TRX and channel number IE */ +struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr) { + struct gsm_lchan *lchan; uint8_t ts_nr = chan_nr & 0x07; uint8_t cbits = chan_nr >> 3; - struct bts_support *sup = &bts_support; - struct osmobts_slot *slot = &trx->slot[ts_nr]; - struct osmobts_lchan *lchan = NULL; - - if (!slot->tx_ms) { - LOGP(DRSL, LOGL_NOTICE, "Given slot not available: %d\n", ts_nr); - return NULL; - } + uint8_t lch_idx; + struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr]; if (cbits == 0x01) { - /* TCH/F */ - if (!sup->chan_comb[NM_CHANC_TCHFull]) { - LOGP(DRSL, LOGL_NOTICE, "TCH/F not not supported on slot: %d\n", ts_nr); - return NULL; - } - if (slot->chan_comb != NM_CHANC_TCHFull) { - LOGP(DRSL, LOGL_NOTICE, "Given channel type not TCH/F: %d\n", ts_nr); - return NULL; - } - lchan = slot->lchan[0]; + lch_idx = 0; /* TCH/F */ + if (ts->pchan != GSM_PCHAN_TCH_F && + ts->pchan != GSM_PCHAN_PDCH && + ts->pchan != GSM_PCHAN_TCH_F_PDCH) + LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", + chan_nr, ts->pchan); } else if ((cbits & 0x1e) == 0x02) { - /* TCH/H */ - if (!sup->chan_comb[NM_CHANC_TCHHalf]) { - LOGP(DRSL, LOGL_NOTICE, "TCH/H not not supported on slot: %d\n", ts_nr); - return NULL; - } - if (slot->chan_comb != NM_CHANC_TCHHalf) { - LOGP(DRSL, LOGL_NOTICE, "Given channel type not TCH/H: %d\n", ts_nr); - return NULL; - } - lchan = slot->lchan[cbits & 0x01];; + lch_idx = cbits & 0x1; /* TCH/H */ + if (ts->pchan != GSM_PCHAN_TCH_H) + LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", + chan_nr, ts->pchan); } else if ((cbits & 0x1c) == 0x04) { - /* BCCH+SDCCH4 */ - if (!sup->chan_comb[NM_CHANC_BCCHComb]) { - LOGP(DRSL, LOGL_NOTICE, "Combined BCCH+SDCCH/4 not not supported on slot: %d\n", ts_nr); - return NULL; - } - if (slot->chan_comb != NM_CHANC_BCCHComb) { - LOGP(DRSL, LOGL_NOTICE, "Given channel type not Combined BCCH+SDCCH/4: %d\n", ts_nr); - return NULL; - } - lchan = slot->lchan[cbits & 0x03];; + lch_idx = cbits & 0x3; /* SDCCH/4 */ + if (ts->pchan != GSM_PCHAN_CCCH_SDCCH4) + LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", + chan_nr, ts->pchan); } else if ((cbits & 0x18) == 0x08) { - /* SDCCH8 */ - if (!sup->chan_comb[NM_CHANC_SDCCH]) { - LOGP(DRSL, LOGL_NOTICE, "SDCCH/8 not not supported on slot: %d\n", ts_nr); - return NULL; - } - if (slot->chan_comb != NM_CHANC_SDCCH) { - LOGP(DRSL, LOGL_NOTICE, "Given channel type not SDCCH/8: %d\n", ts_nr); - return NULL; - } - lchan = slot->lchan[cbits & 0x07]; + lch_idx = cbits & 0x7; /* SDCCH/8 */ + if (ts->pchan != GSM_PCHAN_SDCCH8_SACCH8C) + LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", + chan_nr, ts->pchan); + } else if (cbits == 0x10 || cbits == 0x11 || cbits == 0x12) { + lch_idx = 0; + if (ts->pchan != GSM_PCHAN_CCCH && + ts->pchan != GSM_PCHAN_CCCH_SDCCH4) + LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", + chan_nr, ts->pchan); + /* FIXME: we should not return first sdcch4 !!! */ } else { - LOGP(DRSL, LOGL_NOTICE, "Given chan_nr unknown: %d\n", chan_nr); + LOGP(DRSL, LOGL_ERROR, "unknown chan_nr=0x%02x\n", chan_nr); return NULL; } - if (!lchan) - LOGP(DRSL, LOGL_ERROR, "Lchan not created.\n"); + lchan = &ts->lchan[lch_idx]; +#if 0 + log_set_context(BSC_CTX_LCHAN, lchan); + if (lchan->conn) + log_set_context(BSC_CTX_SUBSCR, lchan->conn->subscr); +#endif return lchan; } @@ -153,6 +170,7 @@ static void rsl_dch_push_hdr(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr dch = (struct abis_rsl_dchan_hdr *) msgb_push(msg, sizeof(*dch)); dch->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN; dch->c.msg_type = msg_type; + dch->ie_chan = RSL_IE_CHAN_NR; dch->chan_nr = chan_nr; } @@ -162,41 +180,37 @@ static void rsl_dch_push_hdr(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr */ /* 8.6.4 sending ERROR REPORT */ -static int rsl_tx_error_report(struct osmobts_trx *trx, uint8_t cause) +static int rsl_tx_error_report(struct gsm_bts_trx *trx, uint8_t cause) { struct msgb *nmsg; - uint8_t *ie; - LOGP(DRSL, LOGL_NOTICE, "Sending Error Report: cause = 0x%02x\n", cause); + LOGP(DRSL, LOGL_NOTICE, "Tx RSL Error Report: cause = 0x%02x\n", cause); nmsg = rsl_msgb_alloc(sizeof(struct abis_rsl_common_hdr)); if (!nmsg) return -ENOMEM; - ie = msgb_put(nmsg, 3); - ie[0] = RSL_IE_CAUSE; - ie[1] = 1; - ie[2] = cause; + msgb_tlv_put(nmsg, RSL_IE_CAUSE, 1, &cause); rsl_trx_push_hdr(nmsg, RSL_MT_ERROR_REPORT); - abis_push_ipa(nmsg, IPA_PROTO_RSL); + nmsg->trx = trx; - return abis_tx(&trx->link, nmsg); + return abis_rsl_sendmsg(nmsg); } /* 8.6.1 sending RF RESOURCE INDICATION */ -int rsl_tx_rf_res(struct osmobts_trx *trx) +int rsl_tx_rf_res(struct gsm_bts_trx *trx) { struct msgb *nmsg; - LOGP(DRSL, LOGL_INFO, "Sending RF RESource INDication\n"); + LOGP(DRSL, LOGL_INFO, "Tx RSL RF RESource INDication\n"); nmsg = rsl_msgb_alloc(sizeof(struct abis_rsl_common_hdr)); if (!nmsg) return -ENOMEM; // FIXME: add interference levels of TRX rsl_trx_push_hdr(nmsg, RSL_MT_RF_RES_IND); - abis_push_ipa(nmsg, IPA_PROTO_RSL); + nmsg->trx = trx; - return abis_tx(&trx->link, nmsg); + return abis_rsl_sendmsg(nmsg); } /* @@ -204,87 +218,191 @@ int rsl_tx_rf_res(struct osmobts_trx *trx) */ /* 8.5.1 BCCH INFOrmation is received */ -static int rsl_rx_bcch_info(struct osmobts_trx *trx, struct msgb *msg) +static int rsl_rx_bcch_info(struct gsm_bts_trx *trx, struct msgb *msg) { + struct gsm_bts *bts = trx->bts; struct tlv_parsed tp; - uint8_t si; - int i; - - LOGP(DRSL, LOGL_INFO, "RSL BCCH Information:\n"); + uint8_t rsl_si; + enum osmo_sysinfo_type osmo_si; rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)); /* 9.3.30 System Info Type */ - if (!TLVP_PRESENT(&tp, RSL_IE_SYSINFO_TYPE)) { + if (!TLVP_PRESENT(&tp, RSL_IE_SYSINFO_TYPE)) return rsl_tx_error_report(trx, RSL_ERR_MAND_IE_ERROR); - } - si = *TLVP_VAL(&tp, RSL_IE_SYSINFO_TYPE); - i = 0; - while(i < BTS_SI_NUM) { - if (bts_si_list[i] == si) - break; - i++; - } - if (i == BTS_SI_NUM) { - LOGP(DRSL, LOGL_NOTICE, " SI 0x%02x not supported.\n", si); + + rsl_si = *TLVP_VAL(&tp, RSL_IE_SYSINFO_TYPE); + if (OSMO_IN_ARRAY(rsl_si, rsl_sacch_sitypes)) + return rsl_tx_error_report(trx, RSL_ERR_IE_CONTENT); + + osmo_si = osmo_rsl2sitype(rsl_si); + if (osmo_si == SYSINFO_TYPE_NONE) { + LOGP(DRSL, LOGL_NOTICE, " Rx RSL SI 0x%02x not supported.\n", rsl_si); return rsl_tx_error_report(trx, RSL_ERR_IE_CONTENT); } /* 9.3.39 Full BCCH Information */ if (TLVP_PRESENT(&tp, RSL_IE_FULL_BCCH_INFO)) { - trx->si.flags[i] |= BTS_SI_USE; - memcpy(trx->si.si[i], TLVP_VAL(&tp, RSL_IE_FULL_BCCH_INFO), 23); - LOGP(DRSL, LOGL_INFO, " Got new SYSTEM INFORMATION 0x%02x.\n",si); + uint8_t len = TLVP_LEN(&tp, RSL_IE_FULL_BCCH_INFO); + if (len > sizeof(sysinfo_buf_t)) + len = sizeof(sysinfo_buf_t); + bts->si_valid |= (1 << osmo_si); + memcpy(bts->si_buf[osmo_si], + TLVP_VAL(&tp, RSL_IE_FULL_BCCH_INFO), len); + LOGP(DRSL, LOGL_INFO, " Rx RSL BCCH INFO (SI%s)\n", + get_value_string(osmo_sitype_strs, osmo_si)); } else if (TLVP_PRESENT(&tp, RSL_IE_L3_INFO)) { - trx->si.flags[i] |= BTS_SI_USE; - memcpy(trx->si.si[i], TLVP_VAL(&tp, RSL_IE_L3_INFO), 23); - LOGP(DRSL, LOGL_INFO, " Got new SYSTEM INFORMATION 0x%02x.\n",si); + uint8_t len = TLVP_LEN(&tp, RSL_IE_L3_INFO); + if (len > sizeof(sysinfo_buf_t)) + len = sizeof(sysinfo_buf_t); + bts->si_valid |= (1 << osmo_si); + memcpy(bts->si_buf[osmo_si], + TLVP_VAL(&tp, RSL_IE_L3_INFO), len); + LOGP(DRSL, LOGL_INFO, " Rx RSL BCCH INFO (SI%s)\n", + get_value_string(osmo_sitype_strs, osmo_si)); } else { - trx->si.flags[i] &= ~BTS_SI_USE; - LOGP(DRSL, LOGL_INFO, " Removing SYSTEM INFORMATION 0x%02x.\n",si); + bts->si_valid &= (1 << osmo_si); + LOGP(DRSL, LOGL_INFO, " RX RSL Disabling BCCH INFO (SI%s)\n", + get_value_string(osmo_sitype_strs, osmo_si)); } - trx->si.flags[i] |= BTS_SI_NEW; - bts_new_si(trx); + osmo_signal_dispatch(SS_GLOBAL, S_NEW_SYSINFO, bts); return 0; } -/* 8.5.6 IMMEDIATE ASSIGN COMMAND is received */ -static int rsl_rx_imm_ass(struct osmobts_trx *trx, struct msgb *msg) +/* 8.5.2 CCCH Load Indication (PCH) */ +int rsl_tx_ccch_load_ind_pch(struct gsm_bts *bts, uint16_t paging_avail) +{ + struct msgb *msg; + + msg = rsl_msgb_alloc(sizeof(struct abis_rsl_common_hdr)); + if (!msg) + return -ENOMEM; + rsl_trx_push_hdr(msg, RSL_MT_CCCH_LOAD_IND); + msgb_tv16_put(msg, RSL_IE_PAGING_LOAD, paging_avail); + msg->trx = bts->c0; + + return abis_rsl_sendmsg(msg); +} + +/* 8.5.5 PAGING COMMAND */ +static int rsl_rx_paging_cmd(struct gsm_bts_trx *trx, struct msgb *msg) { + struct gsm_bts_role_bts *btsb = trx->bts->role; struct tlv_parsed tp; - uint8_t *data; + uint8_t chan_needed = 0, paging_group; + const uint8_t *identity_lv; + int rc; - LOGP(DRSL, LOGL_INFO, "Immidiate Assignment Command:\n"); + rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)); + + if (!TLVP_PRESENT(&tp, RSL_IE_PAGING_GROUP) || + !TLVP_PRESENT(&tp, RSL_IE_MS_IDENTITY)) + return rsl_tx_error_report(trx, RSL_ERR_MAND_IE_ERROR); + + paging_group = *TLVP_VAL(&tp, RSL_IE_PAGING_GROUP); + identity_lv = TLVP_VAL(&tp, RSL_IE_MS_IDENTITY)-1; + + if (TLVP_PRESENT(&tp, RSL_IE_CHAN_NEEDED)) + chan_needed = *TLVP_VAL(&tp, RSL_IE_CHAN_NEEDED); + + rc = paging_add_identity(btsb->paging_state, paging_group, + identity_lv, chan_needed); + if (rc < 0) { + /* FIXME: notfiy the BSC somehow ?*/ + } + + return 0; +} + +int rsl_tx_ccch_load_ind_rach(struct gsm_bts *bts, uint16_t rach_slots, + uint16_t rach_busy, uint16_t rach_access) +{ + struct msgb *msg; + uint16_t payload[3]; + + payload[0] = htons(rach_slots); + payload[1] = htons(rach_busy); + payload[2] = htons(rach_access); + + msg = rsl_msgb_alloc(sizeof(struct abis_rsl_common_hdr)); + if (!msg) + return -ENOMEM; + + msgb_tlv_put(msg, RSL_IE_RACH_LOAD, 6, (uint8_t *)payload); + rsl_trx_push_hdr(msg, RSL_MT_CCCH_LOAD_IND); + msg->trx = bts->c0; + + return abis_rsl_sendmsg(msg); +} + +/* 8.6.2 SACCH FILLING */ +static int rsl_rx_sacch_fill(struct gsm_bts_trx *trx, struct msgb *msg) +{ + struct gsm_bts *bts = trx->bts; + struct tlv_parsed tp; + uint8_t rsl_si; + enum osmo_sysinfo_type osmo_si; rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)); /* 9.3.30 System Info Type */ - if (!TLVP_PRESENT(&tp, RSL_IE_FULL_IMM_ASS_INFO)) { + if (!TLVP_PRESENT(&tp, RSL_IE_SYSINFO_TYPE)) return rsl_tx_error_report(trx, RSL_ERR_MAND_IE_ERROR); + + rsl_si = *TLVP_VAL(&tp, RSL_IE_SYSINFO_TYPE); + if (!OSMO_IN_ARRAY(rsl_si, rsl_sacch_sitypes)) + return rsl_tx_error_report(trx, RSL_ERR_IE_CONTENT); + + osmo_si = osmo_rsl2sitype(rsl_si); + if (osmo_si == SYSINFO_TYPE_NONE) { + LOGP(DRSL, LOGL_NOTICE, " Rx SACCH SI 0x%02x not supported.\n", rsl_si); + return rsl_tx_error_report(trx, RSL_ERR_IE_CONTENT); + } + if (TLVP_PRESENT(&tp, RSL_IE_L3_INFO)) { + uint8_t len = TLVP_LEN(&tp, RSL_IE_L3_INFO); + /* We have to pre-fix with the two-byte LAPDM UI header */ + if (len > sizeof(sysinfo_buf_t)-2) + len = sizeof(sysinfo_buf_t)-2; + bts->si_valid |= (1 << osmo_si); + bts->si_buf[osmo_si][0] = 0x00; + bts->si_buf[osmo_si][1] = 0x03; + memcpy(bts->si_buf[osmo_si]+2, + TLVP_VAL(&tp, RSL_IE_L3_INFO), len); + LOGP(DRSL, LOGL_INFO, " Rx RSL SACCH FILLING (SI%s)\n", + get_value_string(osmo_sitype_strs, osmo_si)); + } else { + bts->si_valid &= (1 << osmo_si); + LOGP(DRSL, LOGL_INFO, " Rx RSL Disabling SACCH FILLING (SI%s)\n", + get_value_string(osmo_sitype_strs, osmo_si)); } - data = (uint8_t *) TLVP_VAL(&tp, RSL_IE_FULL_IMM_ASS_INFO); - LOGP(DRSL, LOGL_INFO, " length = %d\n", data[-1]); + osmo_signal_dispatch(SS_GLOBAL, S_NEW_SYSINFO, bts); + + return 0; + +} -#warning HACK - { - struct msgb *nmsg = rsl_msgb_alloc(64); - struct l1ctl_info_dl *dl; - uint8_t lupd[23] = {0x01,0x03f,0x3d, - 0x05,0x08,0x12,0x62,0xf2,0x10,0x31,0x04,0x33,0x05,0xf4,0x87,0x16,0xb3,0xf0, - 0x2b, 0x2b, 0x2b, 0x2b, 0x2b}; +/* 8.5.6 IMMEDIATE ASSIGN COMMAND is received */ +static int rsl_rx_imm_ass(struct gsm_bts_trx *trx, struct msgb *msg) +{ + struct tlv_parsed tp; + + rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)); - memcpy(msgb_put(nmsg, sizeof(lupd)), lupd, sizeof(lupd)); + if (!TLVP_PRESENT(&tp, RSL_IE_FULL_IMM_ASS_INFO)) + return rsl_tx_error_report(trx, RSL_ERR_MAND_IE_ERROR); + + /* cut down msg to the 04.08 RR part */ + msg->data = (uint8_t *) TLVP_VAL(&tp, RSL_IE_FULL_IMM_ASS_INFO); + msg->len = TLVP_LEN(&tp, RSL_IE_FULL_IMM_ASS_INFO); - nmsg->l3h = nmsg->data; - dl = (struct l1ctl_info_dl *)(nmsg->l1h = msgb_push(nmsg, sizeof(*dl))); - dl->chan_nr = trx->slot[2].lchan[0]->chan_nr; - dl->link_id = 0x00; - msgb_push(nmsg, sizeof(struct l1ctl_hdr)); - printf("%p '%s'\n", trx->slot[2].tx_ms, trx->slot[2].tx_ms->ms.name); - rx_ph_data_ind(&trx->slot[2].tx_ms->ms, nmsg); + /* put into the AGCH queue of the BTS */ + if (bts_agch_enqueue(trx->bts, msg) < 0) { + /* if there is no space in the queue: send DELETE IND */ + msgb_free(msg); } - return 0; + /* return 1 means: don't msgb_free() the msg */ + return 1; } /* @@ -292,49 +410,40 @@ static int rsl_rx_imm_ass(struct osmobts_trx *trx, struct msgb *msg) */ /* 8.4.19 sebdubg RF CHANnel RELease ACKnowledge */ -static int rsl_tx_rf_rel_ack(struct osmobts_trx *trx, struct msgb *msg, uint8_t t1, uint8_t t2, uint8_t t3) +int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan) { - struct abis_rsl_dchan_hdr *dch = msgb_l2(msg); - uint8_t chan_nr = dch->chan_nr; - - LOGP(DRSL, LOGL_NOTICE, "Sending Channel Release ACK\n"); + struct msgb *msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr)); + uint8_t chan_nr = gsm_lchan2chan_nr(lchan); - msg->len = 0; - msg->data = msg->tail = msg->l3h; + LOGP(DRSL, LOGL_NOTICE, "%s Tx RF CHAN REL ACK\n", gsm_lchan_name(lchan)); rsl_dch_push_hdr(msg, RSL_MT_RF_CHAN_REL_ACK, chan_nr); - abis_push_ipa(msg, IPA_PROTO_RSL); + msg->trx = lchan->ts->trx; - return abis_tx(&trx->link, msg); + return abis_rsl_sendmsg(msg); } /* 8.4.2 sending CHANnel ACTIVation ACKnowledge */ -static int rsl_tx_chan_ack(struct osmobts_trx *trx, struct msgb *msg, uint8_t t1, uint8_t t2, uint8_t t3) +int rsl_tx_chan_act_ack(struct gsm_lchan *lchan, struct gsm_time *gtime) { - struct abis_rsl_dchan_hdr *dch = msgb_l2(msg); - uint8_t *ie; - uint8_t chan_nr = dch->chan_nr; + struct msgb *msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr)); + uint8_t chan_nr = gsm_lchan2chan_nr(lchan); + uint8_t ie[2]; - LOGP(DRSL, LOGL_NOTICE, "Sending Channel Activated ACK\n"); + LOGP(DRSL, LOGL_NOTICE, "(%s) Tx CHAN ACT ACK\n", gsm_lchan_name(lchan)); - msg->len = 0; - msg->data = msg->tail = msg->l3h; - - ie = msgb_put(msg, 3); - ie[0] = RSL_IE_FRAME_NUMBER; - ie[1] = (t1 << 3) | (t3 >> 3); - ie[2] = (t3 & 0x07) | (t2 & 0x1f); + gsm48_gen_starting_time(ie, gtime); + msgb_tv_fixed_put(msg, RSL_IE_FRAME_NUMBER, 2, ie); rsl_dch_push_hdr(msg, RSL_MT_CHAN_ACTIV_ACK, chan_nr); - abis_push_ipa(msg, IPA_PROTO_RSL); + msg->trx = lchan->ts->trx; - return abis_tx(&trx->link, msg); + return abis_rsl_sendmsg(msg); } /* 8.4.3 sending CHANnel ACTIVation Negative ACK */ -static int rsl_tx_chan_nack(struct osmobts_trx *trx, struct msgb *msg, uint8_t cause) +static int rsl_tx_chan_nack(struct gsm_bts_trx *trx, struct msgb *msg, uint8_t cause) { struct abis_rsl_dchan_hdr *dch = msgb_l2(msg); - uint8_t *ie; uint8_t chan_nr = dch->chan_nr; LOGP(DRSL, LOGL_NOTICE, "Sending Channel Activated NACK: cause = 0x%02x\n", cause); @@ -342,57 +451,71 @@ static int rsl_tx_chan_nack(struct osmobts_trx *trx, struct msgb *msg, uint8_t c msg->len = 0; msg->data = msg->tail = msg->l3h; - ie = msgb_put(msg, 3); /* 9.3.26 Cause */ - ie[0] = RSL_IE_CAUSE; - ie[1] = 1; - ie[2] = cause; + msgb_tlv_put(msg, RSL_IE_CAUSE, 1, &cause); rsl_dch_push_hdr(msg, RSL_MT_CHAN_ACTIV_NACK, chan_nr); - abis_push_ipa(msg, IPA_PROTO_RSL); + msg->trx = trx; - return abis_tx(&trx->link, msg); + return abis_rsl_sendmsg(msg); } /* 8.5.3 sending CHANnel ReQuireD */ -int rsl_tx_chan_rqd(struct osmobts_trx *trx) +int rsl_tx_chan_rqd(struct gsm_bts_trx *trx, struct gsm_time *gtime, + uint8_t ra, uint8_t acc_delay) { struct msgb *nmsg; - uint8_t *ie; + uint8_t payload[3]; LOGP(DRSL, LOGL_NOTICE, "Sending Channel Required\n"); nmsg = rsl_msgb_alloc(sizeof(struct abis_rsl_cchan_hdr)); if (!nmsg) return -ENOMEM; - ie = msgb_put(nmsg, 4); + /* 9.3.19 Request Reference */ - ie[0] = RSL_IE_REQ_REFERENCE; - ie[1] = 0xe0; // FIXME - ie[2] = 0x00; - ie[3] = 0x00; + payload[0] = ra; + gsm48_gen_starting_time(payload+1, gtime); + msgb_tv_fixed_put(nmsg, RSL_IE_REQ_REFERENCE, 3, payload); + /* 9.3.17 Access Delay */ - ie = msgb_put(nmsg, 2); - ie[0] = RSL_IE_ACCESS_DELAY; - ie[1] = 0x00; // FIXME + msgb_tv_put(nmsg, RSL_IE_ACCESS_DELAY, acc_delay); + rsl_cch_push_hdr(nmsg, RSL_MT_CHAN_RQD, 0x88); // FIXME - abis_push_ipa(nmsg, IPA_PROTO_RSL); + nmsg->trx = trx; + + return abis_rsl_sendmsg(nmsg); +} - return abis_tx(&trx->link, nmsg); +/* copy the SACCH related sysinfo from BTS global buffer to lchan specific buffer */ +static void copy_sacch_si_to_lchan(struct gsm_lchan *lchan) +{ + struct gsm_bts *bts = lchan->ts->trx->bts; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(rsl_sacch_sitypes); i++) { + uint8_t rsl_si = rsl_sacch_sitypes[i]; + uint8_t osmo_si = osmo_rsl2sitype(rsl_si); + uint8_t osmo_si_shifted = (1 << osmo_si); + if (osmo_si == SYSINFO_TYPE_NONE) + continue; + if (!(bts->si_valid & osmo_si_shifted)) { + lchan->si.valid &= ~osmo_si_shifted; + continue; + } + lchan->si.valid |= osmo_si_shifted; + memcpy(lchan->si.buf[osmo_si], bts->si_buf[osmo_si], + sizeof(sysinfo_buf_t)); + } } + /* 8.4.1 CHANnel ACTIVation is received */ -static int rsl_rx_chan_activ(struct osmobts_trx *trx, struct msgb *msg) +static int rsl_rx_chan_activ(struct msgb *msg) { struct abis_rsl_dchan_hdr *dch = msgb_l2(msg); + struct gsm_lchan *lchan = msg->lchan; struct tlv_parsed tp; uint8_t type, mode; - struct osmobts_lchan *lchan; - - LOGP(DRSL, LOGL_INFO, "Channel Activation:\n"); - - lchan = rsl_get_chan(trx, dch->chan_nr); - if (!lchan) - return rsl_tx_chan_nack(trx, msg, RSL_ERR_MAND_IE_ERROR); rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)); @@ -400,48 +523,157 @@ static int rsl_rx_chan_activ(struct osmobts_trx *trx, struct msgb *msg) if (!TLVP_PRESENT(&tp, RSL_IE_ACT_TYPE)) { LOGP(DRSL, LOGL_NOTICE, "missing Activation Type\n"); msgb_free(msg); - return rsl_tx_chan_nack(trx, msg, RSL_ERR_MAND_IE_ERROR); + return rsl_tx_chan_nack(msg->trx, msg, RSL_ERR_MAND_IE_ERROR); } type = *TLVP_VAL(&tp, RSL_IE_ACT_TYPE); + /* 9.3.6 Channel Mode */ if (!TLVP_PRESENT(&tp, RSL_IE_CHAN_MODE)) { LOGP(DRSL, LOGL_NOTICE, "missing Channel Mode\n"); msgb_free(msg); - return rsl_tx_chan_nack(trx, msg, RSL_ERR_MAND_IE_ERROR); + return rsl_tx_chan_nack(msg->trx, msg, RSL_ERR_MAND_IE_ERROR); } mode = *TLVP_VAL(&tp, RSL_IE_CHAN_MODE); + /* 9.3.7 Encryption Information */ + if (TLVP_PRESENT(&tp, RSL_IE_ENCR_INFO)) { + uint8_t len = TLVP_LEN(&tp, RSL_IE_ENCR_INFO); + const uint8_t *val = TLVP_VAL(&tp, RSL_IE_ENCR_INFO); + lchan->encr.alg_id = *val++; + lchan->encr.key_len = len -1; + if (lchan->encr.key_len > sizeof(lchan->encr.key)) + lchan->encr.key_len = sizeof(lchan->encr.key); + memcpy(lchan->encr.key, val, lchan->encr.key_len); + } + + /* 9.3.9 Handover Reference */ + + /* 9.3.4 BS Power */ + if (TLVP_PRESENT(&tp, RSL_IE_BS_POWER)) + lchan->bs_power = *TLVP_VAL(&tp, RSL_IE_BS_POWER); + /* 9.3.13 MS Power */ + if (TLVP_PRESENT(&tp, RSL_IE_MS_POWER)) + lchan->bs_power = *TLVP_VAL(&tp, RSL_IE_MS_POWER); + /* 9.3.24 Timing Advance */ + if (TLVP_PRESENT(&tp, RSL_IE_TIMING_ADVANCE)) + lchan->rqd_ta = *TLVP_VAL(&tp, RSL_IE_TIMING_ADVANCE); + + /* 9.3.32 BS Power Parameters */ + /* 9.3.31 MS Power Parameters */ + /* 9.3.16 Physical Context */ + + /* 9.3.29 SACCH Information */ + if (TLVP_PRESENT(&tp, RSL_IE_SACCH_INFO)) { + uint8_t tot_len = TLVP_LEN(&tp, RSL_IE_SACCH_INFO); + const uint8_t *val = TLVP_VAL(&tp, RSL_IE_SACCH_INFO); + uint8_t num_msgs = *val++; + unsigned int i; + for (i = 0; i < num_msgs; i++) { + uint8_t rsl_si = *val++; + uint8_t si_len = *val++; + uint8_t osmo_si; + uint8_t copy_len; + + if (!OSMO_IN_ARRAY(rsl_si, rsl_sacch_sitypes)) + return rsl_tx_error_report(msg->trx, RSL_ERR_IE_CONTENT); + + osmo_si = osmo_rsl2sitype(rsl_si); + if (osmo_si == SYSINFO_TYPE_NONE) { + LOGP(DRSL, LOGL_NOTICE, " Rx SACCH SI 0x%02x not supported.\n", rsl_si); + return rsl_tx_error_report(msg->trx, RSL_ERR_IE_CONTENT); + } + + copy_len = si_len; + /* We have to pre-fix with the two-byte LAPDM UI header */ + if (copy_len > sizeof(sysinfo_buf_t)-2) + copy_len = sizeof(sysinfo_buf_t)-2; + lchan->si.valid |= (1 << osmo_si); + lchan->si.buf[osmo_si][0] = 0x00; + lchan->si.buf[osmo_si][1] = 0x03; + memcpy(lchan->si.buf[osmo_si]+2, val, copy_len); + + val += si_len; + } + } else { + /* use standard SACCH filling of the BTS */ + copy_sacch_si_to_lchan(lchan); + } + /* 9.3.52 MultiRate Configuration */ + /* 9.3.53 MultiRate Control */ + /* 9.3.54 Supported Codec Types */ + LOGP(DRSL, LOGL_INFO, " chan_nr=0x%02x type=0x%02x mode=0x%02x\n", dch->chan_nr, type, mode); - return rsl_tx_chan_ack(trx, msg, 0, 0, 0); + /* actually activate the channel in the BTS */ + return bts_model_rsl_chan_act(msg->lchan, &tp); } /* 8.4.14 RF CHANnel RELease is received */ -static int rsl_rx_rf_chan_rel(struct osmobts_trx *trx, struct msgb *msg) +static int rsl_rx_rf_chan_rel(struct msgb *msg) { - struct abis_rsl_dchan_hdr *dch = msgb_l2(msg); - struct osmobts_lchan *lchan; - int rc; +#if 0 + lapdm_reset(&lchan->l2_entity.lapdm_dcch); + lapdm_reset(&lchan->l2_entity.lapdm_acch); - LOGP(DRSL, LOGL_INFO, "Channel Release:\n"); + if (lchan->rtp.socket_created) + rsl_tx_ipac_dlcx_ind(lchan, RSL_ERR_NORMAL_UNSPEC); +#endif - lchan = rsl_get_chan(trx, dch->chan_nr); - if (!lchan) - return rsl_tx_chan_nack(trx, msg, RSL_ERR_MAND_IE_ERROR); + return bts_model_rsl_chan_rel(msg->lchan); +} - LOGP(DRSL, LOGL_INFO, " chan_nr=0x%02x\n", dch->chan_nr); +/* 8.4.20 SACCH INFO MODify */ +static int rsl_rx_sacch_inf_mod(struct msgb *msg) +{ + struct gsm_lchan *lchan = msg->lchan; + struct tlv_parsed tp; + uint8_t rsl_si, osmo_si; - lapdm_reset(&lchan->l2_entity.lapdm_dcch); - lapdm_reset(&lchan->l2_entity.lapdm_acch); + rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)); - rc = rsl_tx_rf_rel_ack(trx, msg, 0, 0, 0); + if (TLVP_PRESENT(&tp, RSL_IE_STARTNG_TIME)) { + LOGP(DRSL, LOGL_NOTICE, "Starting time not supported\n"); + return rsl_tx_error_report(msg->trx, RSL_ERR_SERV_OPT_UNIMPL); + } - if (lchan->rtp.socket_created) - rsl_tx_ipac_dlcx_ind(lchan, RSL_ERR_NORMAL_UNSPEC); + /* 9.3.30 System Info Type */ + if (!TLVP_PRESENT(&tp, RSL_IE_SYSINFO_TYPE)) + return rsl_tx_error_report(msg->trx, RSL_ERR_MAND_IE_ERROR); + + rsl_si = *TLVP_VAL(&tp, RSL_IE_SYSINFO_TYPE); + if (!OSMO_IN_ARRAY(rsl_si, rsl_sacch_sitypes)) + return rsl_tx_error_report(msg->trx, RSL_ERR_IE_CONTENT); + + osmo_si = osmo_rsl2sitype(rsl_si); + if (osmo_si == SYSINFO_TYPE_NONE) { + LOGP(DRSL, LOGL_NOTICE, "%s Rx SACCH SI 0x%02x not supported.\n", + gsm_lchan_name(lchan), rsl_si); + return rsl_tx_error_report(msg->trx, RSL_ERR_IE_CONTENT); + } + if (TLVP_PRESENT(&tp, RSL_IE_L3_INFO)) { + uint8_t len = TLVP_LEN(&tp, RSL_IE_L3_INFO); + /* We have to pre-fix with the two-byte LAPDM UI header */ + if (len > sizeof(sysinfo_buf_t)-2) + len = sizeof(sysinfo_buf_t)-2; + lchan->si.valid |= (1 << osmo_si); + lchan->si.buf[osmo_si][0] = 0x00; + lchan->si.buf[osmo_si][1] = 0x03; + memcpy(lchan->si.buf[osmo_si]+2, + TLVP_VAL(&tp, RSL_IE_L3_INFO), len); + LOGP(DRSL, LOGL_INFO, "%s Rx RSL SACCH FILLING (SI%s)\n", + gsm_lchan_name(lchan), + get_value_string(osmo_sitype_strs, osmo_si)); + } else { + lchan->si.valid &= (1 << osmo_si); + LOGP(DRSL, LOGL_INFO, "%s Rx RSL Disabling SACCH FILLING (SI%s)\n", + gsm_lchan_name(lchan), + get_value_string(osmo_sitype_strs, osmo_si)); + } - return rc; + return 0; } +#if 0 /* * ip.access related messages */ @@ -449,25 +681,21 @@ static int rsl_rx_rf_chan_rel(struct osmobts_trx *trx, struct msgb *msg) int rsl_tx_ipac_dlcx_ind(struct osmobts_lchan *lchan, uint8_t cause) { struct msgb *nmsg; - struct osmobts_trx *trx = lchan->slot->trx; - uint8_t *ie; + struct gsm_bts_trx *trx = lchan->slot->trx; LOGP(DRSL, LOGL_NOTICE, "Sending RTP delete indication: cause=%d\n", cause); nmsg = rsl_msgb_alloc(sizeof(struct abis_rsl_common_hdr)); if (!nmsg) return -ENOMEM; - ie = msgb_put(nmsg, 3); - ie[0] = RSL_IE_CAUSE; - ie[1] = 1; - ie[2] = cause; + msgb_tlv_put(nmsg, RSL_IE_CAUSE, 1, &cause); rsl_trx_push_hdr(nmsg, RSL_MT_IPAC_DLCX_IND); - abis_push_ipa(nmsg, IPA_PROTO_RSL); + msg->trx = trx; - return abis_tx(&trx->link, nmsg); + return abis_rsl_sendmsg(nmsg); } -static int rsl_tx_ipac_cx_ack(struct osmobts_trx *trx, struct msgb *msg, uint32_t ip, uint16_t port, uint16_t *conn_id) +static int rsl_tx_ipac_cx_ack(struct gsm_bts_trx *trx, struct msgb *msg, uint32_t ip, uint16_t port, uint16_t *conn_id) { struct abis_rsl_dchan_hdr *dch = msgb_l2(msg); uint8_t chan_nr = dch->chan_nr; @@ -487,35 +715,31 @@ static int rsl_tx_ipac_cx_ack(struct osmobts_trx *trx, struct msgb *msg, uint32_ msgb_tv16_put(msg, RSL_IE_IPAC_LOCAL_PORT, htons(port)); rsl_dch_push_hdr(msg, msg_type + 1, chan_nr); - abis_push_ipa(msg, IPA_PROTO_RSL); + msg->trx = trx; - return abis_tx(&trx->link, msg); + return abis_rsl_sendmsg(msg); } -static int rsl_tx_ipac_cx_nack(struct osmobts_trx *trx, struct msgb *msg, uint8_t cause) +static int rsl_tx_ipac_cx_nack(struct gsm_bts_trx *trx, struct msgb *msg, uint8_t cause) { struct abis_rsl_dchan_hdr *dch = msgb_l2(msg); uint8_t chan_nr = dch->chan_nr; uint8_t msg_type = dch->c.msg_type; - uint8_t *ie; LOGP(DRSL, LOGL_NOTICE, "Sending RTP connection request NACK: cause=%d\n", cause); msg->len = 0; msg->data = msg->tail = msg->l3h; - ie = msgb_put(msg, 3); /* 9.3.26 Cause */ - ie[0] = RSL_IE_CAUSE; - ie[1] = 1; - ie[2] = cause; + msgb_tlv_put(msg, RSL_IE_CAUSE, 1, &cause); rsl_dch_push_hdr(msg, msg_type + 2, chan_nr); - abis_push_ipa(msg, IPA_PROTO_RSL); + msg->trx = trx; - return abis_tx(&trx->link, msg); + return abis_rsl_sendmsg(msg); } -static int rsl_rx_ipac_crcx_mdcx(struct osmobts_trx *trx, struct msgb *msg) +static int rsl_rx_ipac_crcx_mdcx(struct gsm_bts_trx *trx, struct msgb *msg) { struct abis_rsl_dchan_hdr *dch = msgb_l2(msg); struct tlv_parsed tp; @@ -531,7 +755,7 @@ static int rsl_rx_ipac_crcx_mdcx(struct osmobts_trx *trx, struct msgb *msg) else LOGP(DRSL, LOGL_INFO, "Request of modding RTP connection:\n"); - lchan = rsl_get_chan(trx, dch->chan_nr); + lchan = rsl_lchan_lookup(trx, dch->chan_nr); if (!lchan) return rsl_tx_ipac_cx_nack(trx, msg, RSL_ERR_MAND_IE_ERROR); @@ -584,7 +808,7 @@ static int rsl_rx_ipac_crcx_mdcx(struct osmobts_trx *trx, struct msgb *msg) return rsl_tx_ipac_cx_ack(trx, msg, ntohl(lchan->rtp.rtp_udp.sin_local.sin_addr.s_addr), ntohs(lchan->rtp.rtp_udp.sin_local.sin_port), &conn_id); } -static int rsl_rx_ipac_dlcx(struct osmobts_trx *trx, struct msgb *msg) +static int rsl_rx_ipac_dlcx(struct gsm_bts_trx *trx, struct msgb *msg) { struct abis_rsl_dchan_hdr *dch = msgb_l2(msg); struct tlv_parsed tp; @@ -592,7 +816,7 @@ static int rsl_rx_ipac_dlcx(struct osmobts_trx *trx, struct msgb *msg) LOGP(DRSL, LOGL_INFO, "Request of deleting RTP connection:\n"); - lchan = rsl_get_chan(trx, dch->chan_nr); + lchan = rsl_lchan_lookup(trx, dch->chan_nr); if (!lchan) return rsl_tx_ipac_cx_nack(trx, msg, RSL_ERR_MAND_IE_ERROR); @@ -602,15 +826,16 @@ static int rsl_rx_ipac_dlcx(struct osmobts_trx *trx, struct msgb *msg) return rsl_tx_ipac_cx_ack(trx, msg, ntohl(lchan->rtp.rtp_udp.sin_local.sin_addr.s_addr), ntohs(lchan->rtp.rtp_udp.sin_local.sin_port), NULL); } +#endif /* * selecting message */ -static int rsl_rx_rll(struct osmobts_trx *trx, struct msgb *msg) +static int rsl_rx_rll(struct gsm_bts_trx *trx, struct msgb *msg) { struct abis_rsl_rll_hdr *rh = msgb_l2(msg); - struct osmobts_lchan *lchan; + struct gsm_lchan *lchan; if (msgb_l2len(msg) < sizeof(*rh)) { LOGP(DRSL, LOGL_NOTICE, "RSL Radio Link Layer message too short\n"); @@ -619,26 +844,28 @@ static int rsl_rx_rll(struct osmobts_trx *trx, struct msgb *msg) } msg->l3h = (unsigned char *)rh + sizeof(*rh); - lchan = rsl_get_chan(trx, rh->chan_nr); + lchan = rsl_lchan_lookup(trx, rh->chan_nr); if (!lchan) return rsl_tx_chan_nack(trx, msg, RSL_ERR_MAND_IE_ERROR); - return rslms_recvmsg(msg, &lchan->l2_entity); + return lapdm_rslms_recvmsg(msg, &lchan->lapdm_ch); } -int rsl_tx_rll(struct msgb *msg, struct osmol2_entity *l2_entity) +/* call-back for LAPDm code, called when it wants to send msgs UP */ +int lapdm_rll_tx_cb(struct msgb *msg, struct lapdm_entity *le, void *ctx) { - struct osmobts_lchan *lchan = container_of(l2_entity, struct osmobts_lchan, l2_entity); + struct gsm_lchan *lchan = ctx; struct abis_rsl_common_hdr *rh = msgb_l2(msg); - LOGP(DRSL, LOGL_INFO, "Got '%s' message from L2.\n", get_rsl_name(rh->msg_type)); + LOGP(DRSL, LOGL_INFO, "%s Fwd RLL msg %s from LAPDm to A-bis\n", + gsm_lchan_name(lchan), rsl_msg_name(rh->msg_type)); - abis_push_ipa(msg, IPA_PROTO_RSL); + msg->trx = lchan->ts->trx; - return abis_tx(&lchan->slot->trx->link, msg); + return abis_rsl_sendmsg(msg); } -static int rsl_rx_cchan(struct osmobts_trx *trx, struct msgb *msg) +static int rsl_rx_cchan(struct gsm_bts_trx *trx, struct msgb *msg) { struct abis_rsl_cchan_hdr *cch = msgb_l2(msg); int ret = 0; @@ -650,6 +877,16 @@ static int rsl_rx_cchan(struct osmobts_trx *trx, struct msgb *msg) } msg->l3h = (unsigned char *)cch + sizeof(*cch); + msg->lchan = rsl_lchan_lookup(trx, cch->chan_nr); + if (!msg->lchan) { + LOGP(DRSL, LOGL_ERROR, "Rx RSL %s for unknow lchan\n", + rsl_msg_name(cch->c.msg_type)); + //return rsl_tx_chan_nack(trx, msg, RSL_ERR_MAND_IE_ERROR); + } + + LOGP(DRSL, LOGL_INFO, "%s Rx RSL %s\n", gsm_lchan_name(msg->lchan), + rsl_msg_name(cch->c.msg_type)); + switch (cch->c.msg_type) { case RSL_MT_BCCH_INFO: ret = rsl_rx_bcch_info(trx, msg); @@ -657,17 +894,29 @@ static int rsl_rx_cchan(struct osmobts_trx *trx, struct msgb *msg) case RSL_MT_IMMEDIATE_ASSIGN_CMD: ret = rsl_rx_imm_ass(trx, msg); break; + case RSL_MT_PAGING_CMD: + ret = rsl_rx_paging_cmd(trx, msg); + break; + case RSL_MT_SMS_BC_REQ: + case RSL_MT_SMS_BC_CMD: + case RSL_MT_NOT_CMD: + LOGP(DRSL, LOGL_NOTICE, "unimplemented RSL cchan msg_type %s\n", + rsl_msg_name(cch->c.msg_type)); + break; default: - LOGP(DRSL, LOGL_NOTICE, "unsupported RSL cchan msg_type 0x%02x\n", + LOGP(DRSL, LOGL_NOTICE, "undefined RSL cchan msg_type 0x%02x\n", cch->c.msg_type); ret = -EINVAL; + break; } - msgb_free(msg); + if (ret != 1) + msgb_free(msg); + return ret; } -static int rsl_rx_dchan(struct osmobts_trx *trx, struct msgb *msg) +static int rsl_rx_dchan(struct gsm_bts_trx *trx, struct msgb *msg) { struct abis_rsl_dchan_hdr *dch = msgb_l2(msg); int ret = 0; @@ -679,22 +928,48 @@ static int rsl_rx_dchan(struct osmobts_trx *trx, struct msgb *msg) } msg->l3h = (unsigned char *)dch + sizeof(*dch); + msg->lchan = rsl_lchan_lookup(trx, dch->chan_nr); + if (!msg->lchan) { + LOGP(DRSL, LOGL_ERROR, "Rx RSL %s for unknow lchan\n", + rsl_msg_name(dch->c.msg_type)); + //return rsl_tx_chan_nack(trx, msg, RSL_ERR_MAND_IE_ERROR); + } + + LOGP(DRSL, LOGL_INFO, "%s Rx RSL %s\n", gsm_lchan_name(msg->lchan), + rsl_msg_name(dch->c.msg_type)); + switch (dch->c.msg_type) { case RSL_MT_CHAN_ACTIV: - return rsl_rx_chan_activ(trx, msg); + return rsl_rx_chan_activ(msg); case RSL_MT_RF_CHAN_REL: - return rsl_rx_rf_chan_rel(trx, msg); + return rsl_rx_rf_chan_rel(msg); + case RSL_MT_SACCH_INFO_MODIFY: + return rsl_rx_sacch_inf_mod(msg); + case RSL_MT_DEACTIVATE_SACCH: + case RSL_MT_ENCR_CMD: + case RSL_MT_MODE_MODIFY_REQ: + case RSL_MT_PHY_CONTEXT_REQ: + case RSL_MT_PREPROC_CONFIG: + case RSL_MT_RTD_REP: + case RSL_MT_PRE_HANDO_NOTIF: + case RSL_MT_MR_CODEC_MOD_REQ: + case RSL_MT_TFO_MOD_REQ: + LOGP(DRSL, LOGL_NOTICE, "unimplemented RSL dchan msg_type %s\n", + rsl_msg_name(dch->c.msg_type)); + break; default: - LOGP(DRSL, LOGL_NOTICE, "unsupported RSL dchan msg_type 0x%02x\n", + LOGP(DRSL, LOGL_NOTICE, "undefined RSL dchan msg_type 0x%02x\n", dch->c.msg_type); ret = -EINVAL; } - msgb_free(msg); + if (ret != 1) + msgb_free(msg); + return ret; } -static int rsl_rx_trx(struct osmobts_trx *trx, struct msgb *msg) +static int rsl_rx_trx(struct gsm_bts_trx *trx, struct msgb *msg) { struct abis_rsl_common_hdr *th = msgb_l2(msg); int ret = 0; @@ -708,19 +983,21 @@ static int rsl_rx_trx(struct osmobts_trx *trx, struct msgb *msg) switch (th->msg_type) { case RSL_MT_SACCH_FILL: - ret = rsl_rx_bcch_info(trx, msg); + ret = rsl_rx_sacch_fill(trx, msg); break; default: - LOGP(DRSL, LOGL_NOTICE, "unsupported RSL TRX msg_type 0x%02x\n", + LOGP(DRSL, LOGL_NOTICE, "undefined RSL TRX msg_type 0x%02x\n", th->msg_type); ret = -EINVAL; } - msgb_free(msg); + if (ret != 1) + msgb_free(msg); + return ret; } -static int rsl_rx_ipaccess(struct osmobts_trx *trx, struct msgb *msg) +static int rsl_rx_ipaccess(struct gsm_bts_trx *trx, struct msgb *msg) { struct abis_rsl_dchan_hdr *dch = msgb_l2(msg); int ret = 0; @@ -733,22 +1010,25 @@ static int rsl_rx_ipaccess(struct osmobts_trx *trx, struct msgb *msg) msg->l3h = (unsigned char *)dch + sizeof(*dch); switch (dch->c.msg_type) { +#if 0 case RSL_MT_IPAC_CRCX: case RSL_MT_IPAC_MDCX: return rsl_rx_ipac_crcx_mdcx(trx, msg); case RSL_MT_IPAC_DLCX: return rsl_rx_ipac_dlcx(trx, msg); +#endif default: LOGP(DRSL, LOGL_NOTICE, "unsupported RSL ip.access msg_type 0x%02x\n", dch->c.msg_type); ret = -EINVAL; } - msgb_free(msg); + if (ret != 1) + msgb_free(msg); return ret; } -int down_rsl(struct osmobts_trx *trx, struct msgb *msg) +int down_rsl(struct gsm_bts_trx *trx, struct msgb *msg) { struct abis_rsl_common_hdr *rslh = msgb_l2(msg); int ret = 0; @@ -784,5 +1064,3 @@ int down_rsl(struct osmobts_trx *trx, struct msgb *msg) return ret; } - - diff --git a/src/common/rtp.c b/src/common/rtp.c index e8a7e7fc..99be75b0 100644 --- a/src/common/rtp.c +++ b/src/common/rtp.c @@ -1,11 +1,12 @@ -/* - * (C) 2011 by Andreas Eversberg +/* RTP code for BTS side */ + +/* (C) 2011 by Andreas Eversberg * * 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 + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, @@ -13,16 +14,11 @@ * 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. + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . * */ -/* - * RTP peer - */ - #include #include #include diff --git a/src/common/support.c b/src/common/support.c index 36fc5ca3..e271b957 100644 --- a/src/common/support.c +++ b/src/common/support.c @@ -3,8 +3,8 @@ * 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 + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, @@ -12,9 +12,8 @@ * 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. + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . * */ diff --git a/src/common/sysinfo.c b/src/common/sysinfo.c new file mode 100644 index 00000000..90141d5b --- /dev/null +++ b/src/common/sysinfo.c @@ -0,0 +1,66 @@ +/* (C) 2011 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License + * along with this program. If not, see . + * + */ + +#include + +#include +#include + +#include + +uint8_t *bts_sysinfo_get(struct gsm_bts *bts, struct gsm_time *g_time) +{ + /* Apply the rules from 05.02 6.3.1.3 Mapping of BCCH Data */ + switch (g_time->tc) { + case 0: + return GSM_BTS_SI(bts, SYSINFO_TYPE_1); + case 1: + return GSM_BTS_SI(bts, SYSINFO_TYPE_2); + case 2: + return GSM_BTS_SI(bts, SYSINFO_TYPE_3); + case 3: + return GSM_BTS_SI(bts, SYSINFO_TYPE_4); + case 4: + /* 2ter, 2quater, 9, 13 */ + break; + case 5: + /* 2ter, 2quater */ + break; + case 6: + return GSM_BTS_SI(bts, SYSINFO_TYPE_3); + case 7: + return GSM_BTS_SI(bts, SYSINFO_TYPE_4); + } + + return NULL; +} + +uint8_t *lchan_sacch_get(struct gsm_lchan *lchan, struct gsm_time *g_time) +{ + uint32_t tmp; + + for (tmp = lchan->si.last + 1; tmp != lchan->si.last; tmp = (tmp + 1) % 32) { + if (lchan->si.valid & (1 << tmp)) { + lchan->si.last = tmp; + printf("returning SACCH SI type %u\n", tmp); + return lchan->si.buf[tmp]; + } + } + return NULL; +} diff --git a/src/common/vty.c b/src/common/vty.c new file mode 100644 index 00000000..d2cda2fb --- /dev/null +++ b/src/common/vty.c @@ -0,0 +1,61 @@ + +#include +#include + +#include + +#include + +enum node_type bts_vty_go_parent(struct vty *vty) +{ + switch (vty->node) { + default: + vty->node = CONFIG_NODE; + } + return vty->node; +} + +int bts_vty_is_config_node(struct vty *vty, int node) +{ + switch (node) { + default: + return 0; + } +} + +gDEFUN(ournode_exit, ournode_exit_cmd, "exit", + "Exit current node, go down to provious node") +{ + switch (vty->node) { + default: + break; + } + return CMD_SUCCESS; +} + +gDEFUN(ournode_end, ournode_end_cmd, "end", + "End current mode and change to enable mode") +{ + switch (vty->node) { + default: + vty_config_unlock(vty); + vty->node = ENABLE_NODE; + vty->index = NULL; + vty->index_sub = NULL; + break; + } + return CMD_SUCCESS; +} + +struct vty_app_info bts_vty_info = { + .name = "OsmoBTS", + .version = PACKAGE_VERSION, + .go_parent_cb = bts_vty_go_parent, + .is_config_node = bts_vty_is_config_node, +}; + +const char *osmobts_copyright = + "Copyright (C) 2010, 2011 by Harald Welte and Andreas Eversberg\r\n" + "License AGPLv3+: GNU AGPL version 3 or later \r\n" + "This is free software: you are free to change and redistribute it.\r\n" + "There is NO WARRANTY, to the extent permitted by law.\r\n"; -- cgit v1.2.3