/* * (C) 2010 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 * (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. * */ #include #include #include #include #include #include #include #include #include #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 INFORMATION ="); 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"); } else { 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; }