diff options
Diffstat (limited to 'src/host/layer23/src/common/sysinfo.c')
-rw-r--r-- | src/host/layer23/src/common/sysinfo.c | 870 |
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; +} + |