summaryrefslogtreecommitdiffstats
path: root/src/host/layer23/src/common/sysinfo.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/host/layer23/src/common/sysinfo.c')
-rw-r--r--src/host/layer23/src/common/sysinfo.c870
1 files changed, 870 insertions, 0 deletions
diff --git a/src/host/layer23/src/common/sysinfo.c b/src/host/layer23/src/common/sysinfo.c
new file mode 100644
index 00000000..2816c266
--- /dev/null
+++ b/src/host/layer23/src/common/sysinfo.c
@@ -0,0 +1,870 @@
+/*
+ * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+#include <osmocom/core/bitvec.h>
+
+#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/networks.h>
+#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/common/sysinfo.h>
+
+#define MIN(a, b) ((a < b) ? a : b)
+
+/*
+ * dumping
+ */
+
+// FIXME: move to libosmocore
+char *gsm_print_arfcn(uint16_t arfcn)
+{
+ static char text[10];
+
+ sprintf(text, "%d", arfcn & 1023);
+ if ((arfcn & ARFCN_PCS))
+ strcat(text, "(PCS)");
+ else if (arfcn >= 512 && arfcn <= 885)
+ strcat(text, "(DCS)");
+
+ return text;
+}
+
+/* check if the cell 'talks' about DCS (0) or PCS (1) */
+uint8_t gsm_refer_pcs(uint16_t arfcn, struct gsm48_sysinfo *s)
+{
+ /* If ARFCN is PCS band, the cell refers to PCS */
+ if ((arfcn & ARFCN_PCS))
+ return 1;
+
+ /* If no SI1 is available, we assume DCS. Be sure to call this
+ * function only if SI 1 is available. */
+ if (!s->si1)
+ return 0;
+
+ /* If band indicator indicates PCS band, the cell refers to PCSThe */
+ return s->band_ind;
+}
+
+int gsm48_sysinfo_dump(struct gsm48_sysinfo *s, uint16_t arfcn,
+ void (*print)(void *, const char *, ...), void *priv, uint8_t *freq_map)
+{
+ char buffer[81];
+ int i, j, k, index;
+ int refer_pcs = gsm_refer_pcs(arfcn, s);
+
+ /* available sysinfos */
+ print(priv, "ARFCN = %s channels 512+ refer to %s\n",
+ gsm_print_arfcn(arfcn),
+ (refer_pcs) ? "PCS (1900)" : "DCS (1800)");
+ print(priv, "Available SYSTEM INFORMATIONS =");
+ if (s->si1)
+ print(priv, " 1");
+ if (s->si2)
+ print(priv, " 2");
+ if (s->si2bis)
+ print(priv, " 2bis");
+ if (s->si2ter)
+ print(priv, " 2ter");
+ if (s->si3)
+ print(priv, " 3");
+ if (s->si4)
+ print(priv, " 4");
+ if (s->si5)
+ print(priv, " 5");
+ if (s->si5bis)
+ print(priv, " 5bis");
+ if (s->si5ter)
+ print(priv, " 5ter");
+ if (s->si6)
+ print(priv, " 6");
+ print(priv, "\n");
+ print(priv, "\n");
+
+ /* frequency list */
+ j = 0; k = 0;
+ for (i = 0; i < 1024; i++) {
+ if ((s->freq[i].mask & FREQ_TYPE_SERV)) {
+ if (!k) {
+ sprintf(buffer, "serv. cell : ");
+ j = strlen(buffer);
+ }
+ if (j >= 75) {
+ buffer[j - 1] = '\0';
+ print(priv, "%s\n", buffer);
+ sprintf(buffer, " ");
+ j = strlen(buffer);
+ }
+ sprintf(buffer + j, "%d,", i);
+ j = strlen(buffer);
+ k++;
+ }
+ }
+ if (j) {
+ buffer[j - 1] = '\0';
+ print(priv, "%s\n", buffer);
+ }
+ j = 0; k = 0;
+ for (i = 0; i < 1024; i++) {
+ if ((s->freq[i].mask & FREQ_TYPE_NCELL)) {
+ if (!k) {
+ sprintf(buffer, "SI2 (neigh.) BA=%d: ",
+ s->nb_ba_ind_si2);
+ j = strlen(buffer);
+ }
+ if (j >= 70) {
+ buffer[j - 1] = '\0';
+ print(priv, "%s\n", buffer);
+ sprintf(buffer, " ");
+ j = strlen(buffer);
+ }
+ sprintf(buffer + j, "%d,", i);
+ j = strlen(buffer);
+ k++;
+ }
+ }
+ if (j) {
+ buffer[j - 1] = '\0';
+ print(priv, "%s\n", buffer);
+ }
+ j = 0; k = 0;
+ for (i = 0; i < 1024; i++) {
+ if ((s->freq[i].mask & FREQ_TYPE_REP)) {
+ if (!k) {
+ sprintf(buffer, "SI5 (report) BA=%d: ",
+ s->nb_ba_ind_si5);
+ j = strlen(buffer);
+ }
+ if (j >= 70) {
+ buffer[j - 1] = '\0';
+ print(priv, "%s\n", buffer);
+ sprintf(buffer, " ");
+ j = strlen(buffer);
+ }
+ sprintf(buffer + j, "%d,", i);
+ j = strlen(buffer);
+ k++;
+ }
+ }
+ if (j) {
+ buffer[j - 1] = '\0';
+ print(priv, "%s\n", buffer);
+ }
+ print(priv, "\n");
+
+ /* frequency map */
+ for (i = 0; i < 1024; i += 64) {
+ sprintf(buffer, " %3d ", i);
+ for (j = 0; j < 64; j++) {
+ index = i+j;
+ if (refer_pcs && index >= 512 && index <= 885)
+ index = index-512+1024;
+ if ((s->freq[i+j].mask & FREQ_TYPE_SERV))
+ buffer[j + 5] = 'S';
+ else if ((s->freq[i+j].mask & FREQ_TYPE_NCELL)
+ && (s->freq[i+j].mask & FREQ_TYPE_REP))
+ buffer[j + 5] = 'b';
+ else if ((s->freq[i+j].mask & FREQ_TYPE_NCELL))
+ buffer[j + 5] = 'n';
+ else if ((s->freq[i+j].mask & FREQ_TYPE_REP))
+ buffer[j + 5] = 'r';
+ else if (!freq_map || (freq_map[index >> 3]
+ & (1 << (index & 7))))
+ buffer[j + 5] = '.';
+ else
+ buffer[j + 5] = ' ';
+ }
+ for (; j < 64; j++)
+ buffer[j + 5] = ' ';
+ sprintf(buffer + 69, " %d", i + 63);
+ print(priv, "%s\n", buffer);
+ }
+ print(priv, " 'S' = serv. cell 'n' = SI2 (neigh.) 'r' = SI5 (rep.) "
+ "'b' = SI2+SI5\n\n");
+
+ /* serving cell */
+ print(priv, "Serving Cell:\n");
+ print(priv, " BSIC = %d,%d MCC = %s MNC = %s LAC = 0x%04x Cell ID "
+ "= 0x%04x\n", s->bsic >> 3, s->bsic & 0x7,
+ gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc), s->lac,
+ s->cell_id);
+ print(priv, " Country = %s Network Name = %s\n", gsm_get_mcc(s->mcc),
+ gsm_get_mnc(s->mcc, s->mnc));
+ print(priv, " MAX_RETRANS = %d TX_INTEGER = %d re-establish = %s\n",
+ s->max_retrans, s->tx_integer,
+ (s->reest_denied) ? "denied" : "allowed");
+ print(priv, " Cell barred = %s barred classes =",
+ (s->cell_barr ? "yes" : "no"));
+ for (i = 0; i < 16; i++) {
+ if ((s->class_barr & (1 << i)))
+ print(priv, " C%d", i);
+ }
+ print(priv, "\n");
+ if (s->sp)
+ print(priv, " CBQ = %d CRO = %d TEMP_OFFSET = %d "
+ "PENALTY_TIME = %d\n", s->sp_cbq, s->sp_cro, s->sp_to,
+ s->sp_pt);
+ if (s->nb_ncc_permitted_si2) {
+ print(priv, "NCC Permitted BCCH =");
+ for (i = 0; i < 8; i++)
+ if ((s->nb_ncc_permitted_si2 & (1 << i)))
+ print(priv, " %d", i);
+ print(priv, "\n");
+ }
+ if (s->nb_ncc_permitted_si6) {
+ print(priv, "NCC Permitted SACCH/TCH =");
+ for (i = 0; i < 8; i++)
+ if ((s->nb_ncc_permitted_si6 & (1 << i)))
+ print(priv, " %d", i);
+ print(priv, "\n");
+ }
+ print(priv, "\n");
+
+ /* neighbor cell */
+ print(priv, "Neighbor Cell:\n");
+ print(priv, " MAX_RETRANS = %d TX_INTEGER = %d re-establish = %s\n",
+ s->nb_max_retrans, s->nb_tx_integer,
+ (s->nb_reest_denied) ? "denied" : "allowed");
+ print(priv, " Cell barred = %s barred classes =",
+ (s->nb_cell_barr ? "yes" : "no"));
+ for (i = 0; i < 16; i++) {
+ if ((s->nb_class_barr & (1 << i)))
+ print(priv, " C%d", i);
+ }
+ print(priv, "\n");
+ print(priv, "\n");
+
+ /* cell selection */
+ print(priv, "MX_TXPWR_MAX_CCCH = %d CRH = %d RXLEV_MIN = %d "
+ "NECI = %d ACS = %d\n", s->ms_txpwr_max_cch,
+ s->cell_resel_hyst_db, s->rxlev_acc_min_db, s->neci, s->acs);
+
+ /* bcch options */
+ print(priv, "BCCH link timeout = %d DTX = %d PWRC = %d\n",
+ s->bcch_radio_link_timeout, s->bcch_dtx, s->bcch_pwrc);
+
+ /* sacch options */
+ print(priv, "SACCH link timeout = %d DTX = %d PWRC = %d\n",
+ s->sacch_radio_link_timeout, s->sacch_dtx, s->sacch_pwrc);
+
+ /* control channel */
+ switch(s->ccch_conf) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ print(priv, "CCCH Config = %d CCCH", (s->ccch_conf >> 1) + 1);
+ break;
+ case 1:
+ print(priv, "CCCH Config = 1 CCCH + SDCCH");
+ break;
+ default:
+ print(priv, "CCCH Config = reserved");
+ }
+ print(priv, " BS-PA-MFMS = %d Attachment = %s\n",
+ s->pag_mf_periods, (s->att_allowed) ? "allowed" : "denied");
+ print(priv, "BS-AG_BLKS_RES = %d ", s->bs_ag_blks_res);
+ if (s->t3212)
+ print(priv, "T3212 = %d sec.\n", s->t3212);
+ else
+ print(priv, "T3212 = disabled\n", s->t3212);
+
+ /* channel description */
+ if (s->h)
+ print(priv, "chan_nr = 0x%02x TSC = %d MAIO = %d HSN = %d\n",
+ s->chan_nr, s->tsc, s->maio, s->hsn);
+ else
+ print(priv, "chan_nr = 0x%02x TSC = %d ARFCN = %d\n",
+ s->chan_nr, s->tsc, s->arfcn);
+ print(priv, "\n");
+
+ return 0;
+}
+
+/*
+ * decoding
+ */
+
+int gsm48_decode_chan_h0(struct gsm48_chan_desc *cd, uint8_t *tsc,
+ uint16_t *arfcn)
+{
+ *tsc = cd->h0.tsc;
+ *arfcn = cd->h0.arfcn_low | (cd->h0.arfcn_high << 8);
+
+ return 0;
+}
+
+int gsm48_decode_chan_h1(struct gsm48_chan_desc *cd, uint8_t *tsc,
+ uint8_t *maio, uint8_t *hsn)
+{
+ *tsc = cd->h1.tsc;
+ *maio = cd->h1.maio_low | (cd->h1.maio_high << 2);
+ *hsn = cd->h1.hsn;
+
+ return 0;
+}
+
+/* decode "Cell Channel Description" (10.5.2.1b) and other frequency lists */
+static int decode_freq_list(struct gsm_sysinfo_freq *f, uint8_t *cd,
+ uint8_t len, uint8_t mask, uint8_t frqt)
+{
+#if 0
+ /* only Bit map 0 format for P-GSM */
+ if ((cd[0] & 0xc0 & mask) != 0x00 &&
+ (set->p_gsm && !set->e_gsm && !set->r_gsm && !set->dcs))
+ return 0;
+#endif
+
+ return gsm48_decode_freq_list(f, cd, len, mask, frqt);
+}
+
+/* decode "Cell Selection Parameters" (10.5.2.4) */
+static int gsm48_decode_cell_sel_param(struct gsm48_sysinfo *s,
+ struct gsm48_cell_sel_par *cs)
+{
+ s->ms_txpwr_max_cch = cs->ms_txpwr_max_ccch;
+ s->cell_resel_hyst_db = cs->cell_resel_hyst * 2;
+ s->rxlev_acc_min_db = cs->rxlev_acc_min - 110;
+ s->neci = cs->neci;
+ s->acs = cs->acs;
+
+ return 0;
+}
+
+/* decode "Cell Options (BCCH)" (10.5.2.3) */
+static int gsm48_decode_cellopt_bcch(struct gsm48_sysinfo *s,
+ struct gsm48_cell_options *co)
+{
+ s->bcch_radio_link_timeout = (co->radio_link_timeout + 1) * 4;
+ s->bcch_dtx = co->dtx;
+ s->bcch_pwrc = co->pwrc;
+
+ return 0;
+}
+
+/* decode "Cell Options (SACCH)" (10.5.2.3a) */
+static int gsm48_decode_cellopt_sacch(struct gsm48_sysinfo *s,
+ struct gsm48_cell_options *co)
+{
+ s->sacch_radio_link_timeout = (co->radio_link_timeout + 1) * 4;
+ s->sacch_dtx = co->dtx;
+ s->sacch_pwrc = co->pwrc;
+
+ return 0;
+}
+
+/* decode "Control Channel Description" (10.5.2.11) */
+static int gsm48_decode_ccd(struct gsm48_sysinfo *s,
+ struct gsm48_control_channel_descr *cc)
+{
+ s->ccch_conf = cc->ccch_conf;
+ s->bs_ag_blks_res = cc->bs_ag_blks_res;
+ s->att_allowed = cc->att;
+ s->pag_mf_periods = cc->bs_pa_mfrms + 2;
+ s->t3212 = cc->t3212 * 360; /* convert deci-hours to seconds */
+
+ return 0;
+}
+
+/* decode "Mobile Allocation" (10.5.2.21) */
+int gsm48_decode_mobile_alloc(struct gsm_sysinfo_freq *freq,
+ uint8_t *ma, uint8_t len, uint16_t *hopping, uint8_t *hopp_len, int si4)
+{
+ int i, j = 0;
+ uint16_t f[len << 3];
+
+ /* not more than 64 hopping indexes allowed in IE */
+ if (len > 8)
+ return -EINVAL;
+
+ /* tabula rasa */
+ *hopp_len = 0;
+ if (si4) {
+ for (i = 0; i < 1024; i++)
+ freq[i].mask &= ~FREQ_TYPE_HOPP;
+ }
+
+ /* generating list of all frequencies (1..1023,0) */
+ for (i = 1; i <= 1024; i++) {
+ if ((freq[i & 1023].mask & FREQ_TYPE_SERV)) {
+ LOGP(DRR, LOGL_INFO, "Serving cell ARFCN #%d: %d\n",
+ j, i & 1023);
+ f[j++] = i & 1023;
+ if (j == (len << 3))
+ break;
+ }
+ }
+
+ /* fill hopping table with frequency index given by IE
+ * and set hopping type bits
+ */
+ for (i = 0; i < (len << 3); i++) {
+ /* if bit is set, this frequency index is used for hopping */
+ if ((ma[len - 1 - (i >> 3)] & (1 << (i & 7)))) {
+ LOGP(DRR, LOGL_INFO, "Hopping ARFCN: %d (bit %d)\n",
+ i, f[i]);
+ /* index higher than entries in list ? */
+ if (i >= j) {
+ LOGP(DRR, LOGL_NOTICE, "Mobile Allocation "
+ "hopping index %d exceeds maximum "
+ "number of cell frequencies. (%d)\n",
+ i + 1, j);
+ break;
+ }
+ hopping[(*hopp_len)++] = f[i];
+ if (si4)
+ freq[f[i]].mask |= FREQ_TYPE_HOPP;
+ }
+ }
+
+ return 0;
+}
+
+/* Rach Control decode tables */
+static uint8_t gsm48_max_retrans[4] = {
+ 1, 2, 4, 7
+};
+static uint8_t gsm48_tx_integer[16] = {
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 20, 25, 32, 50
+};
+
+/* decode "RACH Control Parameter" (10.5.2.29) */
+static int gsm48_decode_rach_ctl_param(struct gsm48_sysinfo *s,
+ struct gsm48_rach_control *rc)
+{
+ s->reest_denied = rc->re;
+ s->cell_barr = rc->cell_bar;
+ s->tx_integer = gsm48_tx_integer[rc->tx_integer];
+ s->max_retrans = gsm48_max_retrans[rc->max_trans];
+ s->class_barr = (rc->t2 << 8) | rc->t3;
+
+ return 0;
+}
+static int gsm48_decode_rach_ctl_neigh(struct gsm48_sysinfo *s,
+ struct gsm48_rach_control *rc)
+{
+ s->nb_reest_denied = rc->re;
+ s->nb_cell_barr = rc->cell_bar;
+ s->nb_tx_integer = gsm48_tx_integer[rc->tx_integer];
+ s->nb_max_retrans = gsm48_max_retrans[rc->max_trans];
+ s->nb_class_barr = (rc->t2 << 8) | rc->t3;
+
+ return 0;
+}
+
+/* decode "SI 1 Rest Octets" (10.5.2.32) */
+static int gsm48_decode_si1_rest(struct gsm48_sysinfo *s, uint8_t *si,
+ uint8_t len)
+{
+ struct bitvec bv;
+
+ memset(&bv, 0, sizeof(bv));
+ bv.data_len = len;
+ bv.data = si;
+
+ /* Optional Selection Parameters */
+ if (bitvec_get_bit_high(&bv) == H) {
+ s->nch = 1;
+ s->nch_position = bitvec_get_uint(&bv, 5);
+ } else
+ s->nch = 0;
+ if (bitvec_get_bit_high(&bv) == H)
+ s->band_ind = 1;
+ else
+ s->band_ind = 0;
+
+ return 0;
+}
+
+/* decode "SI 3 Rest Octets" (10.5.2.34) */
+static int gsm48_decode_si3_rest(struct gsm48_sysinfo *s, uint8_t *si,
+ uint8_t len)
+{
+ struct bitvec bv;
+
+ memset(&bv, 0, sizeof(bv));
+ bv.data_len = len;
+ bv.data = si;
+
+ /* Optional Selection Parameters */
+ if (bitvec_get_bit_high(&bv) == H) {
+ s->sp = 1;
+ s->sp_cbq = bitvec_get_uint(&bv, 1);
+ s->sp_cro = bitvec_get_uint(&bv, 6);
+ s->sp_to = bitvec_get_uint(&bv, 3);
+ s->sp_pt = bitvec_get_uint(&bv, 5);
+ } else
+ s->sp = 0;
+ /* Optional Power Offset */
+ if (bitvec_get_bit_high(&bv) == H) {
+ s->po = 1;
+ s->po_value = bitvec_get_uint(&bv, 2);
+ } else
+ s->po = 0;
+ /* System Onformation 2ter Indicator */
+ if (bitvec_get_bit_high(&bv) == H)
+ s->si2ter_ind = 1;
+ else
+ s->si2ter_ind = 0;
+ /* Early Classark Sending Control */
+ if (bitvec_get_bit_high(&bv) == H)
+ s->ecsm = 1;
+ else
+ s->ecsm = 0;
+ /* Scheduling if and where */
+ if (bitvec_get_bit_high(&bv) == H) {
+ s->sched = 1;
+ s->sched_where = bitvec_get_uint(&bv, 3);
+ } else
+ s->sched = 0;
+ /* GPRS Indicator */
+ if (bitvec_get_bit_high(&bv) == H) {
+ s->gprs = 1;
+ s->gprs_ra_colour = bitvec_get_uint(&bv, 3);
+ s->gprs_si13_pos = bitvec_get_uint(&bv, 1);
+ } else
+ s->gprs = 0;
+
+ return 0;
+}
+
+/* decode "SI 4 Rest Octets" (10.5.2.35) */
+static int gsm48_decode_si4_rest(struct gsm48_sysinfo *s, uint8_t *si,
+ uint8_t len)
+{
+ struct bitvec bv;
+
+ memset(&bv, 0, sizeof(bv));
+ bv.data_len = len;
+ bv.data = si;
+
+ /* Optional Selection Parameters */
+ if (bitvec_get_bit_high(&bv) == H) {
+ s->sp = 1;
+ s->sp_cbq = bitvec_get_uint(&bv, 1);
+ s->sp_cro = bitvec_get_uint(&bv, 6);
+ s->sp_to = bitvec_get_uint(&bv, 3);
+ s->sp_pt = bitvec_get_uint(&bv, 5);
+ } else
+ s->sp = 0;
+ /* Optional Power Offset */
+ if (bitvec_get_bit_high(&bv) == H) {
+ s->po = 1;
+ s->po_value = bitvec_get_uint(&bv, 3);
+ } else
+ s->po = 0;
+ /* GPRS Indicator */
+ if (bitvec_get_bit_high(&bv) == H) {
+ s->gprs = 1;
+ s->gprs_ra_colour = bitvec_get_uint(&bv, 3);
+ s->gprs_si13_pos = bitvec_get_uint(&bv, 1);
+ } else
+ s->gprs = 0;
+ // todo: more rest octet bits
+
+ return 0;
+}
+
+/* decode "SI 6 Rest Octets" (10.5.2.35a) */
+static int gsm48_decode_si6_rest(struct gsm48_sysinfo *s, uint8_t *si,
+ uint8_t len)
+{
+ return 0;
+}
+
+int gsm48_decode_sysinfo1(struct gsm48_sysinfo *s,
+ struct gsm48_system_information_type_1 *si, int len)
+{
+ int payload_len = len - sizeof(*si);
+
+ memcpy(s->si1_msg, si, MIN(len, sizeof(s->si1_msg)));
+
+ /* Cell Channel Description */
+ decode_freq_list(s->freq, si->cell_channel_description,
+ sizeof(si->cell_channel_description), 0xce, FREQ_TYPE_SERV);
+ /* RACH Control Parameter */
+ gsm48_decode_rach_ctl_param(s, &si->rach_control);
+ /* SI 1 Rest Octets */
+ if (payload_len)
+ gsm48_decode_si1_rest(s, si->rest_octets, payload_len);
+
+ s->si1 = 1;
+
+ if (s->si4) {
+ LOGP(DRR, LOGL_NOTICE, "Now updating previously received "
+ "SYSTEM INFORMATION 4\n");
+ gsm48_decode_sysinfo4(s,
+ (struct gsm48_system_information_type_4 *) s->si4_msg,
+ sizeof(s->si4_msg));
+ }
+
+ return 0;
+}
+
+int gsm48_decode_sysinfo2(struct gsm48_sysinfo *s,
+ struct gsm48_system_information_type_2 *si, int len)
+{
+ memcpy(s->si2_msg, si, MIN(len, sizeof(s->si2_msg)));
+
+ /* Neighbor Cell Description */
+ s->nb_ext_ind_si2 = (si->bcch_frequency_list[0] >> 6) & 1;
+ s->nb_ba_ind_si2 = (si->bcch_frequency_list[0] >> 5) & 1;
+ decode_freq_list(s->freq, si->bcch_frequency_list,
+ sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_NCELL_2);
+ /* NCC Permitted */
+ s->nb_ncc_permitted_si2 = si->ncc_permitted;
+ /* RACH Control Parameter */
+ gsm48_decode_rach_ctl_neigh(s, &si->rach_control);
+
+ s->si2 = 1;
+
+ return 0;
+}
+
+int gsm48_decode_sysinfo2bis(struct gsm48_sysinfo *s,
+ struct gsm48_system_information_type_2bis *si, int len)
+{
+ memcpy(s->si2b_msg, si, MIN(len, sizeof(s->si2b_msg)));
+
+ /* Neighbor Cell Description */
+ s->nb_ext_ind_si2bis = (si->bcch_frequency_list[0] >> 6) & 1;
+ s->nb_ba_ind_si2bis = (si->bcch_frequency_list[0] >> 5) & 1;
+ decode_freq_list(s->freq, si->bcch_frequency_list,
+ sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_NCELL_2bis);
+ /* RACH Control Parameter */
+ gsm48_decode_rach_ctl_neigh(s, &si->rach_control);
+
+ s->si2bis = 1;
+
+ return 0;
+}
+
+int gsm48_decode_sysinfo2ter(struct gsm48_sysinfo *s,
+ struct gsm48_system_information_type_2ter *si, int len)
+{
+ memcpy(s->si2t_msg, si, MIN(len, sizeof(s->si2t_msg)));
+
+ /* Neighbor Cell Description 2 */
+ s->nb_multi_rep_si2ter = (si->ext_bcch_frequency_list[0] >> 6) & 3;
+ s->nb_ba_ind_si2ter = (si->ext_bcch_frequency_list[0] >> 5) & 1;
+ decode_freq_list(s->freq, si->ext_bcch_frequency_list,
+ sizeof(si->ext_bcch_frequency_list), 0x8e,
+ FREQ_TYPE_NCELL_2ter);
+
+ s->si2ter = 1;
+
+ return 0;
+}
+
+int gsm48_decode_sysinfo3(struct gsm48_sysinfo *s,
+ struct gsm48_system_information_type_3 *si, int len)
+{
+ int payload_len = len - sizeof(*si);
+
+ memcpy(s->si3_msg, si, MIN(len, sizeof(s->si3_msg)));
+
+ /* Cell Identity */
+ s->cell_id = ntohs(si->cell_identity);
+ /* LAI */
+ gsm48_decode_lai_hex(&si->lai, &s->mcc, &s->mnc, &s->lac);
+ /* Control Channel Description */
+ gsm48_decode_ccd(s, &si->control_channel_desc);
+ /* Cell Options (BCCH) */
+ gsm48_decode_cellopt_bcch(s, &si->cell_options);
+ /* Cell Selection Parameters */
+ gsm48_decode_cell_sel_param(s, &si->cell_sel_par);
+ /* RACH Control Parameter */
+ gsm48_decode_rach_ctl_param(s, &si->rach_control);
+ /* SI 3 Rest Octets */
+ if (payload_len >= 4)
+ gsm48_decode_si3_rest(s, si->rest_octets, payload_len);
+
+ LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 3 (mcc %s mnc %s "
+ "lac 0x%04x)\n", gsm_print_mcc(s->mcc),
+ gsm_print_mnc(s->mnc), s->lac);
+
+ s->si3 = 1;
+
+ return 0;
+}
+
+int gsm48_decode_sysinfo4(struct gsm48_sysinfo *s,
+ struct gsm48_system_information_type_4 *si, int len)
+{
+ int payload_len = len - sizeof(*si);
+
+ uint8_t *data = si->data;
+ struct gsm48_chan_desc *cd;
+
+ memcpy(s->si4_msg, si, MIN(len, sizeof(s->si4_msg)));
+
+ /* LAI */
+ gsm48_decode_lai_hex(&si->lai, &s->mcc, &s->mnc, &s->lac);
+ /* Cell Selection Parameters */
+ gsm48_decode_cell_sel_param(s, &si->cell_sel_par);
+ /* RACH Control Parameter */
+ gsm48_decode_rach_ctl_param(s, &si->rach_control);
+
+ /* CBCH Channel Description */
+ if (payload_len >= 1 && data[0] == GSM48_IE_CBCH_CHAN_DESC) {
+ if (payload_len < 4) {
+short_read:
+ LOGP(DRR, LOGL_NOTICE, "Short read!\n");
+ return -EIO;
+ }
+ cd = (struct gsm48_chan_desc *) (data + 1);
+ s->chan_nr = cd->chan_nr;
+ if (cd->h0.h) {
+ s->h = 1;
+ gsm48_decode_chan_h1(cd, &s->tsc, &s->maio, &s->hsn);
+ } else {
+ s->h = 0;
+ gsm48_decode_chan_h0(cd, &s->tsc, &s->arfcn);
+ }
+ payload_len -= 4;
+ data += 4;
+ }
+ /* CBCH Mobile Allocation */
+ if (payload_len >= 1 && data[0] == GSM48_IE_CBCH_MOB_AL) {
+ if (payload_len < 1 || payload_len < 2 + data[1])
+ goto short_read;
+ if (!s->si1) {
+ LOGP(DRR, LOGL_NOTICE, "Ignoring CBCH allocation of "
+ "SYSTEM INFORMATION 4 until SI 1 is "
+ "received.\n");
+ gsm48_decode_mobile_alloc(s->freq, data + 2, data[1],
+ s->hopping, &s->hopp_len, 1);
+ }
+ payload_len -= 2 + data[1];
+ data += 2 + data[1];
+ }
+ /* SI 4 Rest Octets */
+ if (payload_len > 0)
+ gsm48_decode_si4_rest(s, data, payload_len);
+
+ s->si4 = 1;
+
+ return 0;
+}
+
+int gsm48_decode_sysinfo5(struct gsm48_sysinfo *s,
+ struct gsm48_system_information_type_5 *si, int len)
+{
+ memcpy(s->si5_msg, si, MIN(len, sizeof(s->si5_msg)));
+
+ /* Neighbor Cell Description */
+ s->nb_ext_ind_si5 = (si->bcch_frequency_list[0] >> 6) & 1;
+ s->nb_ba_ind_si5 = (si->bcch_frequency_list[0] >> 5) & 1;
+ decode_freq_list(s->freq, si->bcch_frequency_list,
+ sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_REP_5);
+
+ s->si5 = 1;
+
+ return 0;
+}
+
+int gsm48_decode_sysinfo5bis(struct gsm48_sysinfo *s,
+ struct gsm48_system_information_type_5bis *si, int len)
+{
+ memcpy(s->si5b_msg, si, MIN(len, sizeof(s->si5b_msg)));
+
+ /* Neighbor Cell Description */
+ s->nb_ext_ind_si5bis = (si->bcch_frequency_list[0] >> 6) & 1;
+ s->nb_ba_ind_si5bis = (si->bcch_frequency_list[0] >> 5) & 1;
+ decode_freq_list(s->freq, si->bcch_frequency_list,
+ sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_REP_5bis);
+
+ s->si5bis = 1;
+
+ return 0;
+}
+
+int gsm48_decode_sysinfo5ter(struct gsm48_sysinfo *s,
+ struct gsm48_system_information_type_5ter *si, int len)
+{
+ memcpy(s->si5t_msg, si, MIN(len, sizeof(s->si5t_msg)));
+
+ /* Neighbor Cell Description */
+ s->nb_multi_rep_si5ter = (si->bcch_frequency_list[0] >> 6) & 3;
+ s->nb_ba_ind_si5ter = (si->bcch_frequency_list[0] >> 5) & 1;
+ decode_freq_list(s->freq, si->bcch_frequency_list,
+ sizeof(si->bcch_frequency_list), 0x8e, FREQ_TYPE_REP_5ter);
+
+ s->si5ter = 1;
+
+ return 0;
+}
+
+int gsm48_decode_sysinfo6(struct gsm48_sysinfo *s,
+ struct gsm48_system_information_type_6 *si, int len)
+{
+ int payload_len = len - sizeof(*si);
+
+ memcpy(s->si6_msg, si, MIN(len, sizeof(s->si6_msg)));
+
+ /* Cell Identity */
+ if (s->si6 && s->cell_id != ntohs(si->cell_identity))
+ LOGP(DRR, LOGL_INFO, "Cell ID on SI 6 differs from previous "
+ "read.\n");
+ s->cell_id = ntohs(si->cell_identity);
+ /* LAI */
+ gsm48_decode_lai_hex(&si->lai, &s->mcc, &s->mnc, &s->lac);
+ /* Cell Options (SACCH) */
+ gsm48_decode_cellopt_sacch(s, &si->cell_options);
+ /* NCC Permitted */
+ s->nb_ncc_permitted_si6 = si->ncc_permitted;
+ /* SI 6 Rest Octets */
+ if (payload_len >= 4)
+ gsm48_decode_si6_rest(s, si->rest_octets, payload_len);
+
+ s->si6 = 1;
+
+ return 0;
+}
+
+int gsm48_encode_lai_hex(struct gsm48_loc_area_id *lai, uint16_t mcc,
+ uint16_t mnc, uint16_t lac)
+{
+ lai->digits[0] = (mcc >> 8) | (mcc & 0xf0);
+ lai->digits[1] = (mcc & 0x0f) | (mnc << 4);
+ lai->digits[2] = (mnc >> 8) | (mnc & 0xf0);
+ lai->lac = htons(lac);
+
+ return 0;
+}
+
+ int gsm48_decode_lai_hex(struct gsm48_loc_area_id *lai, uint16_t *mcc,
+ uint16_t *mnc, uint16_t *lac)
+{
+ *mcc = ((lai->digits[0] & 0x0f) << 8)
+ | (lai->digits[0] & 0xf0)
+ | (lai->digits[1] & 0x0f);
+ *mnc = ((lai->digits[2] & 0x0f) << 8)
+ | (lai->digits[2] & 0xf0)
+ | ((lai->digits[1] & 0xf0) >> 4);
+ *lac = ntohs(lai->lac);
+
+ return 0;
+}
+