diff options
-rw-r--r-- | src/host/layer23/include/osmocom/bb/common/osmocom_data.h | 1 | ||||
-rw-r--r-- | src/host/layer23/include/osmocom/bb/mobile/gsm322.h | 87 | ||||
-rw-r--r-- | src/host/layer23/include/osmocom/bb/mobile/gsm48_mm.h | 1 | ||||
-rw-r--r-- | src/host/layer23/include/osmocom/bb/mobile/settings.h | 2 | ||||
-rw-r--r-- | src/host/layer23/include/osmocom/bb/mobile/subscriber.h | 2 | ||||
-rw-r--r-- | src/host/layer23/src/common/l1ctl.c | 5 | ||||
-rw-r--r-- | src/host/layer23/src/common/sysinfo.c | 6 | ||||
-rw-r--r-- | src/host/layer23/src/mobile/gsm322.c | 2714 | ||||
-rw-r--r-- | src/host/layer23/src/mobile/gsm48_mm.c | 212 | ||||
-rw-r--r-- | src/host/layer23/src/mobile/gsm48_rr.c | 58 | ||||
-rw-r--r-- | src/host/layer23/src/mobile/settings.c | 3 | ||||
-rw-r--r-- | src/host/layer23/src/mobile/subscriber.c | 39 | ||||
-rw-r--r-- | src/host/layer23/src/mobile/vty_interface.c | 269 |
13 files changed, 2626 insertions, 773 deletions
diff --git a/src/host/layer23/include/osmocom/bb/common/osmocom_data.h b/src/host/layer23/include/osmocom/bb/common/osmocom_data.h index 6af5ca49..d387c917 100644 --- a/src/host/layer23/include/osmocom/bb/common/osmocom_data.h +++ b/src/host/layer23/include/osmocom/bb/common/osmocom_data.h @@ -87,6 +87,7 @@ struct osmobb_fbsb_res { struct osmocom_ms *ms; int8_t snr; uint8_t bsic; + uint16_t band_arfcn; }; struct osmobb_meas_res { diff --git a/src/host/layer23/include/osmocom/bb/mobile/gsm322.h b/src/host/layer23/include/osmocom/bb/mobile/gsm322.h index d94e368d..f39e5668 100644 --- a/src/host/layer23/include/osmocom/bb/mobile/gsm322.h +++ b/src/host/layer23/include/osmocom/bb/mobile/gsm322.h @@ -29,8 +29,11 @@ #define GSM322_C7_CAMPED_ANY_CELL 7 #define GSM322_C8_ANY_CELL_RESEL 8 #define GSM322_C9_CHOOSE_ANY_CELL 9 -#define GSM322_PLMN_SEARCH 10 -#define GSM322_HPLMN_SEARCH 11 +#define GSM322_CONNECTED_MODE_1 10 +#define GSM322_CONNECTED_MODE_2 11 +#define GSM322_PLMN_SEARCH 12 +#define GSM322_HPLMN_SEARCH 13 +#define GSM322_ANY_SEARCH 14 /* GSM 03.22 events */ #define GSM322_EVENT_SWITCH_ON 1 @@ -102,12 +105,6 @@ struct gsm322_cs_list { uint8_t flags; /* see GSM322_CS_FLAG_* */ int8_t rxlev; /* rx level range format */ struct gsm48_sysinfo *sysinfo; -#if 0 - int8_t min_dbm; /* minimum level to enter cell */ - int8_t max_pwr; /* maximum power to access cell */ - uint16_t class_barr; /* barred classes */ - uint16_t mcc, mnc, lac; /* received mcc, mnc, lac */ -#endif }; /* PLMN search process */ @@ -131,6 +128,32 @@ struct gsm322_plmn { #define GSM322_CCCH_ST_SYNC 2 /* got sync */ #define GSM322_CCCH_ST_DATA 3 /* receiveing data */ +/* neighbour cell info list entry */ +struct gsm322_neighbour { + struct llist_head entry; + struct gsm322_cellsel *cs; + uint16_t arfcn; /* ARFCN identity of that neighbour */ + + uint8_t state; /* GSM322_NB_* */ + time_t created; /* when was this neighbour created */ + time_t when; /* when did we sync / read */ + int16_t rxlev_dbm; /* sum of received levels */ + uint8_t rxlev_count; /* number of received levels */ + int8_t rla_c_dbm; /* average of the reveive level */ + uint8_t c12_valid; /* both C1 and C2 are calculated */ + int16_t c1, c2, crh; + uint8_t checked_for_resel; + uint8_t suitable_allowable; + uint8_t prio_low; +}; + +#define GSM322_NB_NEW 0 /* new NB instance */ +#define GSM322_NB_NOT_SUP 1 /* ARFCN not supported */ +#define GSM322_NB_RLA_C 2 /* valid measurement available */ +#define GSM322_NB_NO_SYNC 3 /* cannot sync to neighbour */ +#define GSM322_NB_NO_BCCH 4 /* sync */ +#define GSM322_NB_SYSINFO 5 /* sysinfo */ + struct gsm48_sysinfo; /* Cell selection process */ struct gsm322_cellsel { @@ -139,25 +162,43 @@ struct gsm322_cellsel { struct llist_head event_queue; /* event messages */ struct llist_head ba_list; /* BCCH Allocation per PLMN */ - - struct osmo_timer_list timer; - - uint16_t mcc, mnc; /* current network to search for */ struct gsm322_cs_list list[1024+299]; /* cell selection list per frequency. */ - + /* scan and tune state */ + struct osmo_timer_list timer; /* cell selection timer */ + uint16_t mcc, mnc; /* current network to search for */ uint8_t powerscan; /* currently scanning for power */ - uint32_t scan_state; /* special state of current scan */ uint8_t ccch_state; /* special state of current ccch */ + uint32_t scan_state; /* special state of current scan */ uint16_t arfcn; /* current tuned idle mode arfcn */ int arfci; /* list index of frequency above */ uint8_t ccch_mode; /* curren CCCH_MODE_* */ - struct gsm48_sysinfo *si; /* current sysinfo */ + uint8_t sync_retries; /* number retries to sync */ + uint8_t sync_pending; /* to prevent double sync req. */ + struct gsm48_sysinfo *si; /* current sysinfo of tuned cell */ + uint8_t tuned; /* if a cell is selected */ + struct osmo_timer_list any_timer; /* restart search 'any cell' */ + /* serving cell */ uint8_t selected; /* if a cell is selected */ - uint16_t sel_arfcn; + uint16_t sel_arfcn; /* current selected serving cell! */ struct gsm48_sysinfo sel_si; /* copy of selected cell, will update */ uint16_t sel_mcc, sel_mnc, sel_lac, sel_id; + + /* cell re-selection */ + struct llist_head nb_list; /* list of neighbour cells */ + uint16_t last_serving_arfcn; /* the ARFCN of last cell */ + uint8_t last_serving_valid; /* there is a last cell */ + struct gsm322_neighbour *neighbour; /* when selecting neighbour cell */ + time_t resel_when; /* timestamp of last re-selection */ + int8_t nb_meas_set; + int16_t rxlev_dbm; /* sum of received levels */ + uint8_t rxlev_count; /* number of received levels */ + int8_t rla_c_dbm; /* average of received level */ + uint8_t c12_valid; /* both C1 and C2 values are + calculated */ + int16_t c1, c2; + uint8_t prio_low; }; /* GSM 03.22 message */ @@ -167,6 +208,7 @@ struct gsm322_msg { uint8_t sysinfo; /* system information type */ uint8_t same_cell; /* select same cell when RET_IDLE */ uint8_t reject; /* location update reject cause */ + uint8_t limited; /* trigger search for limited serv. */ }; #define GSM322_ALLOC_SIZE sizeof(struct gsm322_msg) @@ -195,14 +237,19 @@ int gsm322_dump_forbidden_la(struct osmocom_ms *ms, void (*print)(void *, const char *, ...), void *priv); int gsm322_dump_ba_list(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc, void (*print)(void *, const char *, ...), void *priv); +int gsm322_dump_nb_list(struct gsm322_cellsel *cs, + void (*print)(void *, const char *, ...), void *priv); void start_cs_timer(struct gsm322_cellsel *cs, int sec, int micro); void start_loss_timer(struct gsm322_cellsel *cs, int sec, int micro); -extern const char *plmn_a_state_names[]; -extern const char *plmn_m_state_names[]; -extern const char *cs_state_names[]; +const char *get_a_state_name(int value); +const char *get_m_state_name(int value); +const char *get_cs_state_name(int value); int gsm322_l1_signal(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data); -char *gsm_print_rxlev(uint8_t rxlev); +int gsm322_meas(struct osmocom_ms *ms, uint8_t rx_lev); + +char *gsm_print_rxlev(uint8_t rxlev); + #endif /* _GSM322_H */ diff --git a/src/host/layer23/include/osmocom/bb/mobile/gsm48_mm.h b/src/host/layer23/include/osmocom/bb/mobile/gsm48_mm.h index 1cfe401f..afdcf020 100644 --- a/src/host/layer23/include/osmocom/bb/mobile/gsm48_mm.h +++ b/src/host/layer23/include/osmocom/bb/mobile/gsm48_mm.h @@ -132,6 +132,7 @@ struct gsm48_mmr { #define GSM48_MM_EVENT_AUTH_RESPONSE 13 #define GSM48_MM_EVENT_SYSINFO 14 #define GSM48_MM_EVENT_USER_PLMN_SEL 15 +#define GSM48_MM_EVENT_LOST_COVERAGE 16 /* message for MM events */ struct gsm48_mm_event { diff --git a/src/host/layer23/include/osmocom/bb/mobile/settings.h b/src/host/layer23/include/osmocom/bb/mobile/settings.h index 20a8692e..cd1b8001 100644 --- a/src/host/layer23/include/osmocom/bb/mobile/settings.h +++ b/src/host/layer23/include/osmocom/bb/mobile/settings.h @@ -46,7 +46,9 @@ struct gsm_settings { int8_t alter_delay; uint8_t stick; uint16_t stick_arfcn; + uint8_t skip_max_per_band; uint8_t no_lupd; + uint8_t no_neighbour; /* supported by configuration */ uint8_t cc_dtmf; diff --git a/src/host/layer23/include/osmocom/bb/mobile/subscriber.h b/src/host/layer23/include/osmocom/bb/mobile/subscriber.h index c6cf57a0..cc0cfac6 100644 --- a/src/host/layer23/include/osmocom/bb/mobile/subscriber.h +++ b/src/host/layer23/include/osmocom/bb/mobile/subscriber.h @@ -57,6 +57,8 @@ struct gsm_subscriber { /* special things */ uint8_t always_search_hplmn; /* search hplmn in other countries also (for test cards) */ + uint8_t any_timeout; + /* timer to restart 'any cell selection' */ char sim_name[31]; /* name to load/save sim */ char sim_spn[17]; /* name of service privider */ diff --git a/src/host/layer23/src/common/l1ctl.c b/src/host/layer23/src/common/l1ctl.c index bd7c67ed..5e1e3ccf 100644 --- a/src/host/layer23/src/common/l1ctl.c +++ b/src/host/layer23/src/common/l1ctl.c @@ -94,7 +94,9 @@ static int rx_l1_fbsb_conf(struct osmocom_ms *ms, struct msgb *msg) if (sb->result != 0) { LOGP(DL1C, LOGL_ERROR, "FBSB RESP: result=%u\n", sb->result); - osmo_signal_dispatch(SS_L1CTL, S_L1CTL_FBSB_ERR, ms); + fr.ms = ms; + fr.band_arfcn = ntohs(dl->band_arfcn); + osmo_signal_dispatch(SS_L1CTL, S_L1CTL_FBSB_ERR, &fr); return 0; } @@ -104,6 +106,7 @@ static int rx_l1_fbsb_conf(struct osmocom_ms *ms, struct msgb *msg) fr.ms = ms; fr.snr = dl->snr; fr.bsic = sb->bsic; + fr.band_arfcn = ntohs(dl->band_arfcn); osmo_signal_dispatch(SS_L1CTL, S_L1CTL_FBSB_RESP, &fr); return 0; diff --git a/src/host/layer23/src/common/sysinfo.c b/src/host/layer23/src/common/sysinfo.c index 92a0516d..225f5ad6 100644 --- a/src/host/layer23/src/common/sysinfo.c +++ b/src/host/layer23/src/common/sysinfo.c @@ -290,7 +290,11 @@ int gsm48_sysinfo_dump(struct gsm48_sysinfo *s, uint16_t arfcn, } 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\n", s->bs_ag_blks_res); + 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) diff --git a/src/host/layer23/src/mobile/gsm322.c b/src/host/layer23/src/mobile/gsm322.c index 210215e0..802adc93 100644 --- a/src/host/layer23/src/mobile/gsm322.c +++ b/src/host/layer23/src/mobile/gsm322.c @@ -25,11 +25,13 @@ #include <string.h> #include <stdlib.h> #include <limits.h> +#include <time.h> #include <osmocom/core/msgb.h> #include <osmocom/core/talloc.h> #include <osmocom/core/utils.h> #include <osmocom/gsm/gsm48.h> +#include <osmocom/gsm/gsm_utils.h> #include <osmocom/core/signal.h> #include <osmocom/bb/common/logging.h> @@ -46,14 +48,42 @@ const char *ba_version = "osmocom BA V1\n"; extern void *l23_ctx; static void gsm322_cs_timeout(void *arg); -static void gsm322_cs_loss(void *arg); -static int gsm322_cs_select(struct osmocom_ms *ms, int any, int plmn_allowed); +static int gsm322_cs_select(struct osmocom_ms *ms, int index, uint16_t mcc, + uint16_t mnc, int any); static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg); +static void gsm322_any_timeout(void *arg); +static int gsm322_nb_scan(struct osmocom_ms *ms); +static int gsm322_nb_synced(struct gsm322_cellsel *cs, int yes); +static int gsm322_nb_read(struct gsm322_cellsel *cs, int yes); +static int gsm322_c_camp_any_cell(struct osmocom_ms *ms, struct msgb *msg); +static int gsm322_nb_start(struct osmocom_ms *ms, int synced); +static void gsm322_cs_loss(void *arg); +static int gsm322_nb_meas_ind(struct osmocom_ms *ms, uint16_t arfcn, + uint8_t rx_lev); + +#define SYNC_RETRIES 1 +#define SYNC_RETRIES_SERVING 2 + +/* time for trying to sync and read BCCH of neighbour cell again + * NOTE: This value is not defined by TS, i think. */ +#define GSM58_TRY_AGAIN 30 + +/* time for reading BCCH of neighbour cell again */ +#define GSM58_READ_AGAIN 300 + +/* number of neighbour cells to monitor */ +#define GSM58_NB_NUMBER 6 + +/* Timeout for reading BCCH of neighbour cells */ +#define GSM322_NB_TIMEOUT 2 + +/* number of neighbour cells to measure for average */ +#define RLA_C_NUM 4 -#define SKIP_MAX_PER_BAND +/* wait before doing neighbour cell reselecton due to a better cell again */ +#define GSM58_RESEL_THRESHOLD 15 -#warning HACKING!!! -int hack; +//#define TEST_INCLUDE_SERV /* * notes @@ -84,9 +114,18 @@ int hack; * - cs->selected and cs->sel_* states of the current / last selected cell. * * - * There is a special state: GSM322_PLMN_SEARCH - * It is used to search for all cells, to find the HPLMN. This is triggered - * by a timer. Also it is used before selecting PLMN from list. + * There are special states: GSM322_HPLMN_SEARCH, GSM322_PLMN_SEARCH + * and GSM322_ANY_SEARCH: + * + * GSM322_HPLMN_SEARCH is used to find a HPLMN. This is triggered + * by automatic cell selection. + * + * GSM322_PLMN_SEARCH is triggered when network search process is started. + * It will do a complete search. Also it is used before selecting PLMN from list. + * + * GSM322_ANY_SEARCH is similar to GSM322_PLMN_SEARCH, but it is done while + * camping on any cell. If there is a suitable and allowable cell found, + * it is indicated to the PLMN search process. * */ @@ -108,8 +147,8 @@ int hack; * uint16_t mcc * uint16_t mcc * uint8_t freq[128+38]; - * where frequency 0 is bit 0 of first byte - * + * where frequency 0 is bit 0 of first byte + * * If not end-of-file, the next BA list is stored. */ @@ -146,6 +185,32 @@ int hack; * This list is generated whenever a PLMN search is started and a list of PLMNs * is required. It consists of home PLMN, PLMN Selector list, and PLMNs found * during scan process. + * + * + * Cell re-selection process + * + * The cell re-selection process takes place when a "serving cell" is selected. + * The neighbour cells to be monitored for re-selection are given via SI2* of + * the serving cell. + * + * Therefore a list of neighbour cells is created or updated, when the cell + * allocation is received or changed by the network. + * + * All neighbour cells are monitored, but only up to 6 of the strongest cells + * are synced to, in order to read the BCCH data. A timer is used to re-read + * the BCCH data after 5 minutes. This timer is also used if sync or read + * fails. + * + * The C1 and C2 criterion is calculated for the currently monitored neigbour + * cells. During this process, a better neighbour cell will trigger cell + * re-selection. + * + * The cell re-selection is similar to the cell selection process, except that + * only neighbour cells are searched in order of their quality criterion C2. + * + * During camping, and monitoring neighbour cells, it is possible to enter + * dedicated mode at any time. + * */ /* @@ -235,28 +300,154 @@ uint16_t index2arfcn(int index) int arfcn2index(uint16_t arfcn) { - if ((arfcn & ARFCN_PCS)) + if ((arfcn & ARFCN_PCS) && arfcn >= 512 && arfcn <= 810) return (arfcn & 1023)-512+1024; return arfcn & 1023; } +static char *bargraph(int value, int min, int max) +{ + static char bar[128]; + + /* shift value to the range of min..max */ + if (value < min) + value = 0; + else if (value > max) + value = max - min; + else + value -= min; + + memset(bar, '=', value); + bar[value] = '\0'; + + return bar; +} + +static int class_of_band(struct osmocom_ms *ms, int band) +{ + struct gsm_settings *set = &ms->settings; + + switch (band) { + case GSM_BAND_450: + case GSM_BAND_480: + return set->class_400; + break; + case GSM_BAND_850: + return set->class_850; + break; + case GSM_BAND_1800: + return set->class_dcs; + break; + case GSM_BAND_1900: + return set->class_pcs; + break; + } + + return set->class_900; +} + char *gsm_print_rxlev(uint8_t rxlev) { static char string[5]; if (rxlev == 0) return "<=-110"; if (rxlev >= 63) - return ">=-48"; + return ">=-47"; sprintf(string, "-%d", 110 - rxlev); return string; } -static int gsm322_sync_to_cell(struct gsm322_cellsel *cs) +/* GSM 05.08 6.4 (special class 3 DCS 1800 MS case is omitted ) */ +static int16_t calculate_c1(int log, int8_t rla_c, int8_t rxlev_acc_min, + int8_t ms_txpwr_max_cch, int8_t p) +{ + int16_t a, b, c1, max_b_0; + + a = rla_c - rxlev_acc_min; + b = ms_txpwr_max_cch - p; + + max_b_0 = (b > 0) ? b : 0; + + c1 = a - max_b_0; + + LOGP(log, LOGL_INFO, "A (RLA_C (%d) - RXLEV_ACC_MIN (%d)) = %d\n", + rla_c, rxlev_acc_min, a); + LOGP(log, LOGL_INFO, "B (MS_TXPWR_MAX_CCH (%d) - p (%d)) = %d\n", + ms_txpwr_max_cch, p, b); + LOGP(log, LOGL_INFO, "C1 (A - MAX(B,0)) = %d\n", c1); + + return c1; +} + +static int16_t calculate_c2(int16_t c1, int serving, int last_serving, + int cell_resel_param_ind, uint8_t cell_resel_off, int t, + uint8_t penalty_time, uint8_t temp_offset) { + int16_t c2; + + c2 = c1; + + /* no reselect parameters. same process for serving and neighbour cells */ + if (!cell_resel_param_ind) { + LOGP(DNB, LOGL_INFO, "C2 = C1 = %d (because no extended " + "re-selection parameters available)\n", c2); + return c2; + } + + /* special case, if PENALTY_TIME is '11111' */ + if (penalty_time == 31) { + c2 -= (cell_resel_off << 1); + LOGP(DNB, LOGL_INFO, "C2 = C1 - CELL_RESELECT_OFFSET (%d) = %d " + "(special case)\n", cell_resel_off, c2); + return c2; + } + + c2 += (cell_resel_off << 1); + + /* parameters for serving cell */ + if (serving) { + LOGP(DNB, LOGL_INFO, "C2 = C1 + CELL_RESELECT_OFFSET (%d) = %d " + "(serving cell)\n", cell_resel_off, c2); + return c2; + } + + /* the cell is the last serving cell */ + if (last_serving) { + LOGP(DNB, LOGL_INFO, "C2 = C1 + CELL_RESELECT_OFFSET (%d) = %d " + "(last serving cell)\n", cell_resel_off, c2); + return c2; + } + + /* penatly time reached */ + if (t >= (penalty_time + 1) * 20) { + LOGP(DNB, LOGL_INFO, "C2 = C1 + CELL_RESELECT_OFFSET (%d) = %d " + "(PENALTY_TIME reached)\n", cell_resel_off, c2); + return c2; + } + + /* penalty time not reached, substract temporary offset */ + if (temp_offset < 7) + c2 -= temp_offset * 10; + else + c2 = -1000; /* infinite */ + LOGP(DNB, LOGL_INFO, "C2 = C1 + CELL_RESELECT_OFFSET (%d) = %d " + "(PENALTY_TIME not reached, %d seconds left)\n", cell_resel_off, + c2, (penalty_time + 1) * 20 - t); + return c2; +} + +static int gsm322_sync_to_cell(struct gsm322_cellsel *cs, + struct gsm322_neighbour * neighbour, int camping) { struct osmocom_ms *ms = cs->ms; struct gsm48_sysinfo *s = cs->si; struct rx_meas_stat *meas = &ms->meas; + if (cs->sync_pending) { + LOGP(DCS, LOGL_INFO, "Sync to ARFCN=%s, but there is a sync " + "already pending\n",gsm_print_arfcn(cs->arfcn)); + return 0; + } + cs->ccch_state = GSM322_CCCH_ST_INIT; if (s && s->si3) { if (s->ccch_conf == 1) { @@ -280,15 +471,32 @@ static int gsm322_sync_to_cell(struct gsm322_cellsel *cs) } meas->frames = meas->snr = meas->berr = meas->rxlev = 0; + cs->rxlev_dbm = cs->rxlev_count = 0; + + cs->neighbour = neighbour; + + if (camping) { + cs->rla_c_dbm = -128; + cs->c12_valid = 0; + /* keep neighbour cells! if they are old, they are re-read + * anyway, because re-read timer has expired. */ + } + cs->sync_pending = 1; l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL); return l1ctl_tx_fbsb_req(ms, cs->arfcn, - L1CTL_FBSB_F_FB01SB, 100, 0, - cs->ccch_mode); + L1CTL_FBSB_F_FB01SB, 100, 0, + cs->ccch_mode); } +/* this is called whenever the serving cell is unselectied */ static void gsm322_unselect_cell(struct gsm322_cellsel *cs) { + if (!cs->selected) + return; + + LOGP(DCS, LOGL_INFO, "Unselecting serving cell.\n"); + cs->selected = 0; if (cs->si) cs->si->si5 = 0; /* unset SI5* */ @@ -300,17 +508,19 @@ static void gsm322_unselect_cell(struct gsm322_cellsel *cs) /* print to DCS logging */ static void print_dcs(void *priv, const char *fmt, ...) { - char buffer[1000]; + static char buffer[256] = ""; + int in = strlen(buffer); va_list args; va_start(args, fmt); - vsnprintf(buffer, sizeof(buffer) - 1, fmt, args); - buffer[sizeof(buffer) - 1] = '\0'; + vsnprintf(buffer + in, sizeof(buffer) - in - 1, fmt, args); + buffer[sizeof(buffer) - in - 1] = '\0'; va_end(args); - if (buffer[0]) + if (buffer[0] && buffer[strlen(buffer) - 1] == '\n') { LOGP(DCS, LOGL_INFO, "%s", buffer); -// printf("%s", buffer); + buffer[0] = '\0'; + } } /* del forbidden LA */ @@ -381,7 +591,7 @@ static struct gsm322_ba_list *gsm322_find_ba_list(struct gsm322_cellsel *cs, llist_for_each_entry(ba, &cs->ba_list, entry) { if (ba->mcc == mcc && ba->mnc == mnc) { - ba_found = ba; + ba_found = ba; break; } } @@ -390,12 +600,14 @@ static struct gsm322_ba_list *gsm322_find_ba_list(struct gsm322_cellsel *cs, } /* search available PLMN */ -int gsm322_is_plmn_avail(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc) +int gsm322_is_plmn_avail_and_allow(struct gsm322_cellsel *cs, uint16_t mcc, + uint16_t mnc) { int i; for (i = 0; i <= 1023+299; i++) { - if (cs->list[i].sysinfo + if ((cs->list[i].flags & GSM322_CS_FLAG_TEMP_AA) + && cs->list[i].sysinfo && cs->list[i].sysinfo->mcc == mcc && cs->list[i].sysinfo->mnc == mnc) return 1; @@ -410,16 +622,32 @@ int gsm322_is_hplmn_avail(struct gsm322_cellsel *cs, char *imsi) int i; for (i = 0; i <= 1023+299; i++) { - if (cs->list[i].sysinfo + if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO) + && cs->list[i].sysinfo && gsm_match_mnc(cs->list[i].sysinfo->mcc, - cs->list[i].sysinfo->mnc, imsi)) + cs->list[i].sysinfo->mnc, imsi)) return 1; } return 0; } -/* del forbidden LA */ +static const struct value_string gsm322_nb_state_names[] = { + { GSM322_NB_NEW, "new" }, + { GSM322_NB_NOT_SUP, "not sup" }, + { GSM322_NB_RLA_C, "RLA_C" }, + { GSM322_NB_NO_SYNC, "no sync" }, + { GSM322_NB_NO_BCCH, "no BCCH" }, + { GSM322_NB_SYSINFO, "SYSINFO" }, + { 0, NULL } +}; + +const char *get_nb_state_name(int value) +{ + return get_value_string(gsm322_nb_state_names, value); +} + + /* * timer */ @@ -476,44 +704,87 @@ static void stop_cs_timer(struct gsm322_cellsel *cs) } } +/* the following timer is used to search again for allowable cell, after + * loss of coverage. (loss of any allowed PLMN) */ + +/* start any cell selection timer */ +void start_any_timer(struct gsm322_cellsel *cs, int sec, int micro) +{ + LOGP(DCS, LOGL_DEBUG, "Starting 'any cell selection' timer with %d " + "seconds.\n", sec); + cs->any_timer.cb = gsm322_any_timeout; + cs->any_timer.data = cs; + osmo_timer_schedule(&cs->any_timer, sec, micro); +} + +/* stop cell selection timer */ +static void stop_any_timer(struct gsm322_cellsel *cs) +{ + if (osmo_timer_pending(&cs->any_timer)) { + LOGP(DCS, LOGL_DEBUG, "stopping pending 'any cell selection' " + "timer.\n"); + osmo_timer_del(&cs->any_timer); + } +} + /* * state change */ -const char *plmn_a_state_names[] = { - "A0 null", - "A1 trying RPLMN", - "A2 on PLMN", - "A3 trying PLMN", - "A4 wait for PLMN to appear", - "A5 HPLMN search", - "A6 no SIM inserted" +static const struct value_string gsm322_a_state_names[] = { + { GSM322_A0_NULL, "A0 null"}, + { GSM322_A1_TRYING_RPLMN, "A1 trying RPLMN"}, + { GSM322_A2_ON_PLMN, "A2 on PLMN"}, + { GSM322_A3_TRYING_PLMN, "A3 trying PLMN"}, + { GSM322_A4_WAIT_FOR_PLMN, "A4 wait for PLMN to appear"}, + { GSM322_A5_HPLMN_SEARCH, "A5 HPLMN search"}, + { GSM322_A6_NO_SIM, "A6 no SIM inserted"}, + { 0, NULL } }; -const char *plmn_m_state_names[] = { - "M0 null", - "M1 trying RPLMN", - "M2 on PLMN", - "M3 not on PLMN", - "M4 trying PLMN", - "M5 no SIM inserted" +const char *get_a_state_name(int value) +{ + return get_value_string(gsm322_a_state_names, value); +} + +static const struct value_string gsm322_m_state_names[] = { + { GSM322_M0_NULL, "M0 null"}, + { GSM322_M1_TRYING_RPLMN, "M1 trying RPLMN"}, + { GSM322_M2_ON_PLMN, "M2 on PLMN"}, + { GSM322_M3_NOT_ON_PLMN, "M3 not on PLMN"}, + { GSM322_M4_TRYING_PLMN, "M4 trying PLMN"}, + { GSM322_M5_NO_SIM, "M5 no SIM inserted"}, + { 0, NULL } }; -const char *cs_state_names[] = { - "C0 null", - "C1 normal cell selection", - "C2 stored cell selection", - "C3 camped normally", - "C4 normal cell re-selection", - "C5 choose cell", - "C6 any cell selection", - "C7 camped on any cell", - "C8 any cell re-selection", - "C9 choose any cell", - "PLMN search", - "HPLMN search" +const char *get_m_state_name(int value) +{ + return get_value_string(gsm322_m_state_names, value); +} + +static const struct value_string gsm322_cs_state_names[] = { + { GSM322_C0_NULL, "C0 null"}, + { GSM322_C1_NORMAL_CELL_SEL, "C1 normal cell selection"}, + { GSM322_C2_STORED_CELL_SEL, "C2 stored cell selection"}, + { GSM322_C3_CAMPED_NORMALLY, "C3 camped normally"}, + { GSM322_C4_NORMAL_CELL_RESEL, "C4 normal cell re-selection"}, + { GSM322_C5_CHOOSE_CELL, "C5 choose cell"}, + { GSM322_C6_ANY_CELL_SEL, "C6 any cell selection"}, + { GSM322_C7_CAMPED_ANY_CELL, "C7 camped on any cell"}, + { GSM322_C8_ANY_CELL_RESEL, "C8 any cell re-selection"}, + { GSM322_C9_CHOOSE_ANY_CELL, "C9 choose any cell"}, + { GSM322_CONNECTED_MODE_1, "connected mode 1"}, + { GSM322_CONNECTED_MODE_2, "connected mode 2"}, + { GSM322_PLMN_SEARCH, "PLMN search"}, + { GSM322_HPLMN_SEARCH, "HPLMN search"}, + { GSM322_ANY_SEARCH, "ANY search"}, + { 0, NULL } }; +const char *get_cs_state_name(int value) +{ + return get_value_string(gsm322_cs_state_names, value); +} /* new automatic PLMN search state */ static void new_a_state(struct gsm322_plmn *plmn, int state) @@ -525,11 +796,8 @@ static void new_a_state(struct gsm322_plmn *plmn, int state) stop_plmn_timer(plmn); - if (state < 0 || state >= (sizeof(plmn_a_state_names) / sizeof(char *))) - return; - LOGP(DPLMN, LOGL_INFO, "new state '%s' -> '%s'\n", - plmn_a_state_names[plmn->state], plmn_a_state_names[state]); + get_a_state_name(plmn->state), get_a_state_name(state)); plmn->state = state; } @@ -542,11 +810,8 @@ static void new_m_state(struct gsm322_plmn *plmn, int state) return; } - if (state < 0 || state >= (sizeof(plmn_m_state_names) / sizeof(char *))) - return; - LOGP(DPLMN, LOGL_INFO, "new state '%s' -> '%s'\n", - plmn_m_state_names[plmn->state], plmn_m_state_names[state]); + get_m_state_name(plmn->state), get_m_state_name(state)); plmn->state = state; } @@ -554,11 +819,8 @@ static void new_m_state(struct gsm322_plmn *plmn, int state) /* new Cell selection state */ static void new_c_state(struct gsm322_cellsel *cs, int state) { - if (state < 0 || state >= (sizeof(cs_state_names) / sizeof(char *))) - return; - LOGP(DCS, LOGL_INFO, "new state '%s' -> '%s'\n", - cs_state_names[cs->state], cs_state_names[state]); + get_cs_state_name(cs->state), get_cs_state_name(state)); /* stop cell selection timer, if running */ stop_cs_timer(cs); @@ -580,7 +842,7 @@ static void new_c_state(struct gsm322_cellsel *cs, int state) /* 4.4.3 create sorted list of PLMN * * the source of entries are - * + * * - HPLMN * - entries found in the SIM's PLMN Selector list * - scanned PLMNs above -85 dB (random order) @@ -628,8 +890,8 @@ static int gsm322_sort_list(struct osmocom_ms *ms) llist_for_each_entry(temp, &temp_list, entry) { if (temp->mcc == cs->list[i].sysinfo->mcc && temp->mnc == cs->list[i].sysinfo->mnc) { - found = temp; - break; + found = temp; + break; } } /* update or create */ @@ -652,7 +914,7 @@ static int gsm322_sort_list(struct osmocom_ms *ms) found = NULL; llist_for_each_entry(temp, &temp_list, entry) { if (gsm_match_mnc(temp->mcc, temp->mnc, subscr->imsi)) { - found = temp; + found = temp; break; } } @@ -669,7 +931,7 @@ static int gsm322_sort_list(struct osmocom_ms *ms) llist_for_each_entry(temp, &temp_list, entry) { if (temp->mcc == sim_entry->mcc && temp->mnc == sim_entry->mnc) { - found = temp; + found = temp; break; } } @@ -708,7 +970,7 @@ static int gsm322_sort_list(struct osmocom_ms *ms) llist_for_each_entry(temp, &temp_list, entry) { if (!found || temp->rxlev > search) { - search = temp->rxlev; + search = temp->rxlev; found = temp; } } @@ -758,67 +1020,78 @@ static int gsm322_a_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg) && (subscr->always_search_hplmn || gsm_match_mcc(plmn->mcc, subscr->imsi)) && subscr->sim_valid && subscr->t6m_hplmn) - start_plmn_timer(plmn, subscr->t6m_hplmn * 360); + start_plmn_timer(plmn, subscr->t6m_hplmn * 360); else stop_plmn_timer(plmn); return 0; } -/* indicate selected PLMN */ -static int gsm322_a_indicate_selected(struct osmocom_ms *ms, struct msgb *msg) +/* go to Wait for PLMNs to appear state */ +static int gsm322_a_go_wait_for_plmns(struct osmocom_ms *ms, struct msgb *msg) { struct gsm322_plmn *plmn = &ms->plmn; + struct msgb *nmsg; + struct gsm322_msg *ngm; - vty_notify(ms, NULL); - vty_notify(ms, "Selected Network: %s, %s\n", - gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc)); + new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN); - return gsm322_a_go_on_plmn(ms, msg); + /* we must forward this, otherwhise "Any cell selection" + * will not start automatically. + */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN); + if (!nmsg) + return -ENOMEM; + ngm = (struct gsm322_msg *) nmsg->data; + ngm->limited = 1; + gsm322_cs_sendmsg(ms, nmsg); + + return 0; } /* no (more) PLMN in list */ static int gsm322_a_no_more_plmn(struct osmocom_ms *ms, struct msgb *msg) { struct gsm322_plmn *plmn = &ms->plmn; + struct gsm_subscriber *subscr = &ms->subscr; struct gsm322_cellsel *cs = &ms->cellsel; struct msgb *nmsg; int found; /* any allowable PLMN available? */ - plmn->mcc = plmn->mnc = 0; - found = gsm322_cs_select(ms, 0, 1); + found = gsm322_cs_select(ms, -1, 0, 0, 0); - /* if no PLMN in list */ + /* if no PLMN in list: + * this means that we are at a point where we camp on any cell or + * no cell ist available. */ if (found < 0) { - LOGP(DPLMN, LOGL_INFO, "Not any PLMN allowable.\n"); - - new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN); - -#if 0 - /* we must forward this, otherwhise "Any cell selection" - * will not start automatically. - */ - nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND); - if (!nmsg) - return -ENOMEM; - gsm322_cs_sendmsg(ms, nmsg); -#endif - LOGP(DPLMN, LOGL_INFO, "Trigger full PLMN search.\n"); - - nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START); - if (!nmsg) - return -ENOMEM; - gsm322_cs_sendmsg(ms, nmsg); + if (subscr->plmn_valid) { + LOGP(DPLMN, LOGL_INFO, "Not any PLMN allowable. " + "Do limited search with RPLMN.\n"); + plmn->mcc = subscr->plmn_mcc; + plmn->mnc = subscr->plmn_mnc; + } else + if (subscr->sim_valid) { + LOGP(DPLMN, LOGL_INFO, "Not any PLMN allowable. " + "Do limited search with HPLMN.\n"); + plmn->mcc = subscr->mcc; + plmn->mnc = subscr->mnc; + } else { + LOGP(DPLMN, LOGL_INFO, "Not any PLMN allowable. " + "Do limited search with no PLMN.\n"); + plmn->mcc = 0; + plmn->mnc = 0; + } - return 0; + return gsm322_a_go_wait_for_plmns(ms, msg); } /* select first PLMN in list */ plmn->mcc = cs->list[found].sysinfo->mcc; plmn->mnc = cs->list[found].sysinfo->mnc; - LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%s mnc=%s %s, %s)\n", + LOGP(DPLMN, LOGL_INFO, "PLMN available after searching PLMN list " + "(mcc=%s mnc=%s %s, %s)\n", gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc)); @@ -829,7 +1102,7 @@ static int gsm322_a_no_more_plmn(struct osmocom_ms *ms, struct msgb *msg) gsm322_cs_sendmsg(ms, nmsg); /* go On PLMN */ - return gsm322_a_indicate_selected(ms, msg); + return gsm322_a_go_on_plmn(ms, msg); } /* select first PLMN in list */ @@ -963,18 +1236,20 @@ static int gsm322_a_sel_next_plmn(struct osmocom_ms *ms, struct msgb *msg) /* User re-selection event */ static int gsm322_a_user_resel(struct osmocom_ms *ms, struct msgb *msg) { + struct gsm322_cellsel *cs = &ms->cellsel; struct gsm322_plmn *plmn = &ms->plmn; struct gsm_subscriber *subscr = &ms->subscr; - struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm322_plmn_list *plmn_entry; struct gsm322_plmn_list *plmn_found = NULL; + struct msgb *nmsg; if (!subscr->sim_valid) { return 0; } /* try again later, if not idle */ - if (rr->state != GSM48_RR_ST_IDLE) { + if (cs->state == GSM322_CONNECTED_MODE_1 + || cs->state == GSM322_CONNECTED_MODE_2) { LOGP(DPLMN, LOGL_INFO, "Not idle, rejecting.\n"); return 0; @@ -1001,6 +1276,12 @@ static int gsm322_a_user_resel(struct osmocom_ms *ms, struct msgb *msg) llist_del(&plmn_found->entry); llist_add_tail(&plmn_found->entry, &plmn->sorted_plmn); + /* tell MM that we selected a PLMN */ + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL); + if (!nmsg) + return -ENOMEM; + gsm48_mmevent_msg(ms, nmsg); + /* select first PLMN in list */ return gsm322_a_sel_first_plmn(ms, msg); } @@ -1012,31 +1293,39 @@ static int gsm322_a_plmn_avail(struct osmocom_ms *ms, struct msgb *msg) struct gsm_subscriber *subscr = &ms->subscr; struct gsm322_msg *gm = (struct gsm322_msg *) msg->data; - if (subscr->plmn_valid && subscr->plmn_mcc == gm->mcc - && subscr->plmn_mnc == gm->mnc) { - /* go On PLMN */ - plmn->mcc = gm->mcc; - plmn->mnc = gm->mnc; - LOGP(DPLMN, LOGL_INFO, "RPLMN became available.\n"); - return gsm322_a_go_on_plmn(ms, msg); + if (subscr->plmn_valid && plmn->mcc == gm->mcc + && plmn->mnc == gm->mnc) { + struct msgb *nmsg; + + new_m_state(plmn, GSM322_A1_TRYING_RPLMN); + + LOGP(DPLMN, LOGL_INFO, "Last selected PLMN becomes available " + "again.\n"); + + /* indicate New PLMN */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return 0; + } else { /* select first PLMN in list */ - LOGP(DPLMN, LOGL_INFO, "PLMN became available, start PLMN " + LOGP(DPLMN, LOGL_INFO, "Some PLMN became available, start PLMN " "search process.\n"); return gsm322_a_sel_first_plmn(ms, msg); } } - + /* loss of radio coverage */ static int gsm322_a_loss_of_radio(struct osmocom_ms *ms, struct msgb *msg) { - struct gsm322_plmn *plmn = &ms->plmn; struct gsm322_cellsel *cs = &ms->cellsel; int found; - struct msgb *nmsg; - /* any PLMN available */ - found = gsm322_cs_select(ms, 0, 1); + /* any allowable PLMN available */ + found = gsm322_cs_select(ms, -1, 0, 0, 0); /* if PLMN in list */ if (found >= 0) { @@ -1050,19 +1339,9 @@ static int gsm322_a_loss_of_radio(struct osmocom_ms *ms, struct msgb *msg) return gsm322_a_sel_first_plmn(ms, msg); } - LOGP(DPLMN, LOGL_INFO, "PLMN not available.\n"); - - plmn->mcc = plmn->mnc = 0; - - new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN); + LOGP(DPLMN, LOGL_INFO, "PLMN not available after loss of coverage.\n"); - /* Tell cell selection process to handle "no cell found". */ - nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND); - if (!nmsg) - return -ENOMEM; - gsm322_cs_sendmsg(ms, nmsg); - - return 0; + return gsm322_a_go_wait_for_plmns(ms, msg); } /* MS is switched on OR SIM is inserted OR removed */ @@ -1106,9 +1385,12 @@ static int gsm322_a_switch_on(struct osmocom_ms *ms, struct msgb *msg) return 0; } + plmn->mcc = plmn->mnc = 0; + /* initiate search at cell selection */ LOGP(DSUM, LOGL_INFO, "Search for network\n"); - LOGP(DPLMN, LOGL_INFO, "Switch on, start PLMN search first.\n"); + LOGP(DPLMN, LOGL_INFO, "Switch on, no RPLMN, start PLMN search " + "first.\n"); nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START); if (!nmsg) @@ -1137,20 +1419,17 @@ static int gsm322_a_sim_insert(struct osmocom_ms *ms, struct msgb *msg) /* SIM is removed */ static int gsm322_a_sim_removed(struct osmocom_ms *ms, struct msgb *msg) { - struct gsm322_msg *gm = (struct gsm322_msg *) msg->data; - int msg_type = gm->msg_type; struct msgb *nmsg; - if (msg_type == GSM322_EVENT_INVALID_SIM) { - vty_notify(ms, NULL); - vty_notify(ms, "SIM not valid\n"); - } /* indicate SIM remove to cell selection process */ nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE); if (!nmsg) return -ENOMEM; gsm322_cs_sendmsg(ms, nmsg); + /* flush list of PLMNs */ + gsm_subscr_del_forbidden_plmn(&ms->subscr, 0, 0); + return gsm322_a_switch_on(ms, msg); } @@ -1165,15 +1444,14 @@ static int gsm322_a_roaming_na(struct osmocom_ms *ms, struct msgb *msg) /* On VPLMN of home country and timeout occurs */ static int gsm322_a_hplmn_search_start(struct osmocom_ms *ms, struct msgb *msg) { - struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm322_plmn *plmn = &ms->plmn; struct gsm322_cellsel *cs = &ms->cellsel; struct msgb *nmsg; /* try again later, if not idle and not camping */ - if (rr->state != GSM48_RR_ST_IDLE - || cs->state != GSM322_C3_CAMPED_NORMALLY) { - LOGP(DPLMN, LOGL_INFO, "Not camping, wait some more.\n"); + if (cs->state != GSM322_C3_CAMPED_NORMALLY) { + LOGP(DPLMN, LOGL_INFO, "Not camping normally, wait some more." + "\n"); start_plmn_timer(plmn, 60); return 0; @@ -1182,7 +1460,7 @@ static int gsm322_a_hplmn_search_start(struct osmocom_ms *ms, struct msgb *msg) new_a_state(plmn, GSM322_A5_HPLMN_SEARCH); /* initiate search at cell selection */ - nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START); + nmsg = gsm322_msgb_alloc(GSM322_EVENT_HPLMN_SEARCH); if (!nmsg) return -ENOMEM; gsm322_cs_sendmsg(ms, nmsg); @@ -1219,6 +1497,8 @@ static int gsm322_m_display_plmns(struct osmocom_ms *ms, struct msgb *msg) int msg_type = gm->msg_type; struct gsm322_plmn *plmn = &ms->plmn; struct gsm_sub_plmn_list *temp; + struct msgb *nmsg; + struct gsm322_msg *ngm; /* generate list */ gsm322_sort_list(ms); @@ -1259,36 +1539,61 @@ static int gsm322_m_display_plmns(struct osmocom_ms *ms, struct msgb *msg) gsm_get_mcc(temp->mcc), gsm_get_mnc(temp->mcc, temp->mnc)); } - + /* go Not on PLMN state */ new_m_state(plmn, GSM322_M3_NOT_ON_PLMN); + /* we must forward this, otherwhise "Any cell selection" + * will not start automatically. + * this way we get back to the last PLMN, in case we gained + * our coverage back. + */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN); + if (!nmsg) + return -ENOMEM; + ngm = (struct gsm322_msg *) nmsg->data; + ngm->limited = 1; + gsm322_cs_sendmsg(ms, nmsg); + return 0; } /* user starts reselection */ static int gsm322_m_user_resel(struct osmocom_ms *ms, struct msgb *msg) { + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_cellsel *cs = &ms->cellsel; struct gsm_subscriber *subscr = &ms->subscr; - struct gsm48_rrlayer *rr = &ms->rrlayer; struct msgb *nmsg; + /* unselect PLMN. after search, the process will wait until a PLMN is + * selected by the user. this prevents from switching back to the + * last selected PLMN and destroying the list of scanned networks. + */ + plmn->mcc = plmn->mnc = 0; + if (!subscr->sim_valid) { return 0; } /* try again later, if not idle */ - if (rr->state != GSM48_RR_ST_IDLE) { + if (cs->state == GSM322_CONNECTED_MODE_1 + || cs->state == GSM322_CONNECTED_MODE_2) { LOGP(DPLMN, LOGL_INFO, "Not idle, rejecting.\n"); return 0; } /* initiate search at cell selection */ - vty_notify(ms, NULL); - vty_notify(ms, "Searching Network, please wait...\n"); LOGP(DPLMN, LOGL_INFO, "User re-select, start PLMN search first.\n"); + /* tell MM that we selected a PLMN */ + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL); + if (!nmsg) + return -ENOMEM; + gsm48_mmevent_msg(ms, nmsg); + + /* triffer PLMN search */ nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START); if (!nmsg) return -ENOMEM; @@ -1340,11 +1645,11 @@ static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg) return 0; } + plmn->mcc = plmn->mnc = 0; + /* initiate search at cell selection */ LOGP(DSUM, LOGL_INFO, "Search for network\n"); LOGP(DPLMN, LOGL_INFO, "Switch on, start PLMN search first.\n"); - vty_notify(ms, NULL); - vty_notify(ms, "Searching Network, please wait...\n"); nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START); if (!nmsg) @@ -1386,6 +1691,9 @@ static int gsm322_m_sim_removed(struct osmocom_ms *ms, struct msgb *msg) return -ENOMEM; gsm322_cs_sendmsg(ms, nmsg); + /* flush list of PLMNs */ + gsm_subscr_del_forbidden_plmn(&ms->subscr, 0, 0); + return gsm322_m_switch_on(ms, msg); } @@ -1399,48 +1707,28 @@ static int gsm322_m_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg) subscr->plmn_valid = 1; subscr->plmn_mcc = plmn->mcc; subscr->plmn_mnc = plmn->mnc; -#ifdef TODO - store on sim -#endif new_m_state(plmn, GSM322_M2_ON_PLMN); return 0; } -/* indicate selected PLMN */ -static int gsm322_m_indicate_selected(struct osmocom_ms *ms, struct msgb *msg) -{ - struct gsm322_plmn *plmn = &ms->plmn; - - vty_notify(ms, NULL); - vty_notify(ms, "Selected Network: %s, %s\n", - gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc)); - - return gsm322_m_go_on_plmn(ms, msg); -} - /* previously selected PLMN becomes available again */ static int gsm322_m_plmn_avail(struct osmocom_ms *ms, struct msgb *msg) { struct gsm322_plmn *plmn = &ms->plmn; - struct gsm322_cellsel *cs = &ms->cellsel; + struct msgb *nmsg; new_m_state(plmn, GSM322_M1_TRYING_RPLMN); - if (cs->mcc != plmn->mcc || cs->mnc != plmn->mnc) { - struct msgb *nmsg; + LOGP(DPLMN, LOGL_INFO, "Last selected PLMN becomes available again.\n"); - LOGP(DPLMN, LOGL_INFO, "PLMN available, but currently not " - "selected, so start selection.\n"); + /* indicate New PLMN */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); - /* indicate New PLMN */ - nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN); - if (!nmsg) - return -ENOMEM; - gsm322_cs_sendmsg(ms, nmsg); - } - return 0; } @@ -1456,9 +1744,6 @@ static int gsm322_m_choose_plmn(struct osmocom_ms *ms, struct msgb *msg) plmn->mcc = gm->mcc; plmn->mnc = gm->mnc; - vty_notify(ms, NULL); - vty_notify(ms, "Selected Network: %s, %s\n", - gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc)); LOGP(DPLMN, LOGL_INFO, "User selects PLMN. (mcc=%s mnc=%s " "%s, %s)\n", gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc)); @@ -1468,6 +1753,12 @@ static int gsm322_m_choose_plmn(struct osmocom_ms *ms, struct msgb *msg) new_m_state(plmn, GSM322_M4_TRYING_PLMN); + /* tell MM that we selected a PLMN */ + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL); + if (!nmsg) + return -ENOMEM; + gsm48_mmevent_msg(ms, nmsg); + /* indicate New PLMN */ nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN); if (!nmsg) @@ -1514,17 +1805,21 @@ static int gsm322_am_no_cell_found(struct osmocom_ms *ms, struct msgb *msg) */ /* select a suitable and allowable cell */ -static int gsm322_cs_select(struct osmocom_ms *ms, int any, int plmn_allowed) +static int gsm322_cs_select(struct osmocom_ms *ms, int index, uint16_t mcc, + uint16_t mnc, int any) { struct gsm322_cellsel *cs = &ms->cellsel; struct gsm_settings *set = &ms->settings; struct gsm_subscriber *subscr = &ms->subscr; struct gsm48_sysinfo *s; - int i, found = -1, power = 0; + int start, end, i, found = -1, power = 0; uint8_t flags, mask; uint16_t acc_class; + int16_t c1; + enum gsm_band band; + int class; - /* set out access class depending on the cell selection type */ + /* set our access class depending on the cell selection type */ if (any) { acc_class = subscr->acc_class | 0x0400; /* add emergency */ LOGP(DCS, LOGL_DEBUG, "Select using access class with " @@ -1542,8 +1837,13 @@ static int gsm322_cs_select(struct osmocom_ms *ms, int any, int plmn_allowed) mask |= GSM322_CS_FLAG_BA; flags = mask; /* all masked flags are requied */ - /* loop through all scanned frequencies and select cell */ - for (i = 0; i <= 1023+299; i++) { + /* loop through all scanned frequencies and select cell. + * if an index is given (arfci), we just check this cell only */ + if (index >= 0) + start = end = index; + else + start = 0; end = 1023+299; + for (i = start; i <= end; i++) { cs->list[i].flags &= ~GSM322_CS_FLAG_TEMP_AA; s = cs->list[i].sysinfo; @@ -1553,63 +1853,84 @@ static int gsm322_cs_select(struct osmocom_ms *ms, int any, int plmn_allowed) } /* check C1 criteria not fullfilled */ - // TODO: C1 is also dependant on power class and max power - if (rxlev2dbm(cs->list[i].rxlev) < s->rxlev_acc_min_db - && !set->stick) { - LOGP(DCS, LOGL_INFO, "Skip ARFCN %s: C1 criteria " - "not met. (rxlev %s < min %d)\n", - gsm_print_arfcn(index2arfcn(i)), - gsm_print_rxlev(cs->list[i].rxlev), - s->rxlev_acc_min_db); + // TODO: class 3 DCS mobile + band = gsm_arfcn2band(index2arfcn(i)); + class = class_of_band(ms, band); + c1 = calculate_c1(DCS, rxlev2dbm(cs->list[i].rxlev), + s->rxlev_acc_min_db, + ms_pwr_dbm(band, s->ms_txpwr_max_cch), + ms_class_gmsk_dbm(band, class)); + if (!set->stick && c1 < 0) { + LOGP(DCS, LOGL_INFO, "Skip ARFCN %s: C1 criterion " + "not met. (C1 = %d)\n", + gsm_print_arfcn(index2arfcn(i)), c1); continue; } /* if cell is barred and we don't override */ - if (!subscr->acc_barr + if (!subscr->acc_barr && (cs->list[i].flags & GSM322_CS_FLAG_BARRED)) { LOGP(DCS, LOGL_INFO, "Skip ARFCN %s: Cell is " "barred.\n", gsm_print_arfcn(index2arfcn(i))); - continue; - } - - /* if cell is in list of forbidden LAs */ - if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD)) { - LOGP(DCS, LOGL_INFO, "Skip ARFCN %s: Cell is in " - "list of forbidden LAs. (mcc=%s mnc=%s " - "lai=%04x)\n", - gsm_print_arfcn(index2arfcn(i)), - gsm_print_mcc(s->mcc), - gsm_print_mnc(s->mnc), s->lac); - continue; - } - - /* if cell is in list of forbidden PLMNs */ - if (plmn_allowed && gsm_subscr_is_forbidden_plmn(subscr, - s->mcc, s->mnc)) { - LOGP(DCS, LOGL_INFO, "Skip ARFCN %s: Cell is in " - "list of forbidden PLMNs. (mcc=%s mnc=%s)\n", - gsm_print_arfcn(index2arfcn(i)), - gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc)); - continue; + continue; } /* if we have no access to the cell and we don't override */ - if (!subscr->acc_barr + if (!subscr->acc_barr && !(acc_class & (s->class_barr ^ 0xffff))) { LOGP(DCS, LOGL_INFO, "Skip ARFCN %s: Class is " - "barred for out access. (access=%04x " + "barred for our access. (access=%04x " "barred=%04x)\n", gsm_print_arfcn(index2arfcn(i)), acc_class, s->class_barr); - continue; + continue; } /* store temporary available and allowable flag */ cs->list[i].flags |= GSM322_CS_FLAG_TEMP_AA; + /* if cell is in list of forbidden LAs */ + if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD)) { + if (!any) { + LOGP(DCS, LOGL_INFO, "Skip ARFCN %s: Cell is " + "in list of forbidden LAs. (mcc=%s " + "mnc=%s lai=%04x)\n", + gsm_print_arfcn(index2arfcn(i)), + gsm_print_mcc(s->mcc), + gsm_print_mnc(s->mnc), s->lac); + continue; + } + LOGP(DCS, LOGL_INFO, "Accept ARFCN %s: Cell is in " + "list of forbidden LAs, but we search for any " + "cell. (mcc=%s mnc=%s lai=%04x)\n", + gsm_print_arfcn(index2arfcn(i)), + gsm_print_mcc(s->mcc), + gsm_print_mnc(s->mnc), s->lac); + cs->list[i].flags &= ~GSM322_CS_FLAG_TEMP_AA; + } + + /* if cell is in list of forbidden PLMNs */ + if (gsm_subscr_is_forbidden_plmn(subscr, s->mcc, s->mnc)) { + if (!any) { + LOGP(DCS, LOGL_INFO, "Skip ARFCN %s: Cell is " + "in list of forbidden PLMNs. (mcc=%s " + "mnc=%s)\n", + gsm_print_arfcn(index2arfcn(i)), + gsm_print_mcc(s->mcc), + gsm_print_mnc(s->mnc)); + continue; + } + LOGP(DCS, LOGL_INFO, "Accept ARFCN %s: Cell is in list " + "of forbidden PLMNs, but we search for any " + "cell. (mcc=%s mnc=%s)\n", + gsm_print_arfcn(index2arfcn(i)), + gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc)); + cs->list[i].flags &= ~GSM322_CS_FLAG_TEMP_AA; + } + /* if we search a specific PLMN, but it does not match */ - if (!any && cs->mcc && (cs->mcc != s->mcc - || cs->mnc != s->mnc)) { + if (!any && mcc && (mcc != s->mcc + || mnc != s->mnc)) { LOGP(DCS, LOGL_INFO, "Skip ARFCN %s: PLMN of cell " "does not match target PLMN. (mcc=%s " "mnc=%s)\n", gsm_print_arfcn(index2arfcn(i)), @@ -1639,14 +1960,219 @@ static int gsm322_cs_select(struct osmocom_ms *ms, int any, int plmn_allowed) return found; } +/* re-select a suitable and allowable cell */ +static int gsm322_cs_reselect(struct osmocom_ms *ms, uint16_t mcc, + uint16_t mnc, int any) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_sysinfo *s = cs->si; + int i = cs->arfci; + uint16_t acc_class; + + /* set our access class depending on the cell selection type */ + if (any) { + acc_class = subscr->acc_class | 0x0400; /* add emergency */ + LOGP(DCS, LOGL_DEBUG, "Select using access class with " + "Emergency class.\n"); + } else { + acc_class = subscr->acc_class; + LOGP(DCS, LOGL_DEBUG, "Select using access class \n"); + } + + /* if cell is barred and we don't override */ + if (!subscr->acc_barr + && (cs->list[i].flags & GSM322_CS_FLAG_BARRED)) { + LOGP(DCS, LOGL_INFO, "Skip ARFCN %s: Cell is barred.\n", + gsm_print_arfcn(index2arfcn(i))); + return -1; + } + + /* if cell is in list of forbidden LAs */ + if (!any && (cs->list[i].flags & GSM322_CS_FLAG_FORBIDD)) { + LOGP(DCS, LOGL_INFO, "Skip ARFCN %s: Cell is in list of " + "forbidden LAs. (mcc=%s mnc=%s lai=%04x)\n", + gsm_print_arfcn(index2arfcn(i)), gsm_print_mcc(s->mcc), + gsm_print_mnc(s->mnc), s->lac); + return -1; + } + + /* if cell is in list of forbidden PLMNs */ + if (!any && gsm_subscr_is_forbidden_plmn(subscr, s->mcc, s->mnc)) { + LOGP(DCS, LOGL_INFO, "Skip ARFCN %s: Cell is in " + "list of forbidden PLMNs. (mcc=%s mnc=%s)\n", + gsm_print_arfcn(index2arfcn(i)), + gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc)); + return -1; + } + + /* if we have no access to the cell and we don't override */ + if (!subscr->acc_barr + && !(acc_class & (s->class_barr ^ 0xffff))) { + LOGP(DCS, LOGL_INFO, "Skip ARFCN %s: Class is barred for our " + "access. (access=%04x barred=%04x)\n", + gsm_print_arfcn(index2arfcn(i)), acc_class, + s->class_barr); + return -1; + } + + /* if we search a specific PLMN, but it does not match */ + if (!any && mcc && (mcc != s->mcc + || mnc != s->mnc)) { + LOGP(DCS, LOGL_INFO, "Skip ARFCN %s: PLMN of cell " + "does not match target PLMN. (mcc=%s mnc=%s)\n", + gsm_print_arfcn(index2arfcn(i)), gsm_print_mcc(s->mcc), + gsm_print_mnc(s->mnc)); + return -1; + } + + LOGP(DCS, LOGL_INFO, "Cell ARFCN %s: Neighbour cell accepted, " + "(rxlev=%s mcc=%s mnc=%s lac=%04x %s, %s)\n", + gsm_print_arfcn(index2arfcn(i)), + gsm_print_rxlev(cs->list[i].rxlev), + gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc), s->lac, + gsm_get_mcc(s->mcc), gsm_get_mnc(s->mcc, s->mnc)); + + return i; +} + +/* this processes the end of frequency scanning or cell searches */ +static int gsm322_search_end(struct osmocom_ms *ms) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm322_plmn *plmn = &ms->plmn; + struct msgb *nmsg; + struct gsm322_msg *ngm; + int msg_type = -1; /* no message to be sent */ + int tune_back = 0, mcc = 0, mnc = 0; + int found; + + switch (cs->state) { + case GSM322_ANY_SEARCH: + /* special case for 'any cell' search */ + LOGP(DCS, LOGL_INFO, "Any cell search finished.\n"); + + /* create AA flag */ + found = gsm322_cs_select(ms, -1, 0, 0, 0); + + /* if no cell is found, or if we don't wait for any available + * and allowable PLMN to appear, we just continue to camp */ + if (ms->settings.plmn_mode != PLMN_MODE_AUTO + || plmn->state != GSM322_A4_WAIT_FOR_PLMN + || found < 0) { + tune_back = 1; + gsm322_c_camp_any_cell(ms, NULL); + break; + } + + /* indicate available PLMN, include selected PLMN, if found */ + msg_type = GSM322_EVENT_PLMN_AVAIL; + if (gsm322_is_plmn_avail_and_allow(cs, plmn->mcc, plmn->mnc)) { + /* set what PLMN becomes available */ + mcc = plmn->mcc; + mnc = plmn->mnc; + } + + new_c_state(cs, GSM322_C0_NULL); + + break; + + case GSM322_PLMN_SEARCH: + /* special case for PLMN search */ + msg_type = GSM322_EVENT_PLMN_SEARCH_END; + LOGP(DCS, LOGL_INFO, "PLMN search finished.\n"); + + /* create AA flag */ + gsm322_cs_select(ms, -1, 0, 0, 0); + + new_c_state(cs, GSM322_C0_NULL); + + break; + + case GSM322_HPLMN_SEARCH: + /* special case for HPLMN search */ + msg_type = GSM322_EVENT_NO_CELL_FOUND; + LOGP(DCS, LOGL_INFO, "HPLMN search finished, no cell.\n"); + + new_c_state(cs, GSM322_C3_CAMPED_NORMALLY); + + tune_back = 1; + + break; + + default: + /* we start normal cell selection if this fails */ + if (cs->state == GSM322_C2_STORED_CELL_SEL + || cs->state == GSM322_C5_CHOOSE_CELL) { + /* tell CS to start over */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND); + if (!nmsg) + return -ENOMEM; + gsm322_c_event(ms, nmsg); + msgb_free(nmsg); + + break; + } + + /* on other cell selection, indicate "no cell found" */ + /* NOTE: PLMN search process handles it. + * If not handled there, CS process gets indicated. + * If we would continue to process CS, then we might get + * our list of scanned cells disturbed. + */ + LOGP(DCS, LOGL_INFO, "Cell search finished without result.\n"); + msg_type = GSM322_EVENT_NO_CELL_FOUND; + + /* stay in null-state until any cell selectio is triggered or + * new plmn is indicated. + */ + new_c_state(cs, GSM322_C0_NULL); + } + + if (msg_type > -1) { + /* send result to PLMN process, to trigger next CS event */ + nmsg = gsm322_msgb_alloc(msg_type); + if (!nmsg) + return -ENOMEM; + ngm = (struct gsm322_msg *) nmsg->data; + ngm->mcc = mcc; + ngm->mnc = mcc; + gsm322_plmn_sendmsg(ms, nmsg); + } + + if (cs->selected && tune_back) { + /* tuning back */ + cs->arfcn = cs->sel_arfcn; + cs->arfci = arfcn2index(cs->arfcn); + if (!cs->list[cs->arfci].sysinfo) + cs->list[cs->arfci].sysinfo = talloc_zero(l23_ctx, + struct gsm48_sysinfo); + if (!cs->list[cs->arfci].sysinfo) + exit(-ENOMEM); + cs->list[cs->arfci].flags |= GSM322_CS_FLAG_SYSINFO; + memcpy(cs->list[cs->arfci].sysinfo, &cs->sel_si, + sizeof(struct gsm48_sysinfo)); + cs->si = cs->list[cs->arfci].sysinfo; + cs->sel_mcc = cs->si->mcc; + cs->sel_mnc = cs->si->mnc; + cs->sel_lac = cs->si->lac; + cs->sel_id = cs->si->cell_id; + LOGP(DCS, LOGL_INFO, "Tuning back to frequency %s after full " + "search.\n", gsm_print_arfcn(cs->arfcn)); + cs->sync_retries = SYNC_RETRIES; + gsm322_sync_to_cell(cs, NULL, 0); + } + + return 0; +} + + /* tune to first/next unscanned frequency and search for PLMN */ static int gsm322_cs_scan(struct osmocom_ms *ms) { struct gsm322_cellsel *cs = &ms->cellsel; int i; -#ifndef SKIP_MAX_PER_BAND - int j; -#endif + int j, band = 0; uint8_t mask, flags; uint32_t weight = 0, test = cs->scan_state; @@ -1658,24 +2184,30 @@ static int gsm322_cs_scan(struct osmocom_ms *ms) mask |= GSM322_CS_FLAG_BA; flags = mask; /* all masked flags are requied */ for (i = 0; i <= 1023+299; i++) { -#ifndef SKIP_MAX_PER_BAND - /* skip if band has enough frequencies scanned (3.2.1) */ - for (j = 0; gsm_sup_smax[j].max; j++) { - if (gsm_sup_smax[j].end > gsm_sup_smax[j].start) { - if (gsm_sup_smax[j].start >= i - && gsm_sup_smax[j].end <= i) - break; - } else { - if (gsm_sup_smax[j].end <= i - || gsm_sup_smax[j].start >= i) - break; + j = 0; /* make gcc happy */ + if (!ms->settings.skip_max_per_band) { + /* skip if band has enough freqs. scanned (3.2.1) */ + for (j = 0; gsm_sup_smax[j].max; j++) { + if (gsm_sup_smax[j].end > + gsm_sup_smax[j].start) { + if (gsm_sup_smax[j].start <= i + && gsm_sup_smax[j].end >= i) + break; + } else { + if (gsm_sup_smax[j].start <= i + && 1023 >= i) + break; + if (0 <= i + && gsm_sup_smax[j].end >= i) + break; + } + } + if (gsm_sup_smax[j].max) { + if (gsm_sup_smax[j].temp == gsm_sup_smax[j].max) + continue; } } - if (gsm_sup_smax[j].max) { - if (gsm_sup_smax[j].temp == gsm_sup_smax[j].max) - continue; - } -#endif + /* search for unscanned frequency */ if ((cs->list[i].flags & mask) == flags) { /* weight depends on the power level @@ -1685,140 +2217,22 @@ static int gsm322_cs_scan(struct osmocom_ms *ms) test = (test << 16) | i; if (test >= cs->scan_state) continue; - if (test > weight) + if (test > weight) { weight = test; + band = j; + } + } } cs->scan_state = weight; - if (!weight) - gsm322_dump_cs_list(cs, GSM322_CS_FLAG_SYSINFO, print_dcs, - NULL); - - /* special case for PLMN search */ - if (cs->state == GSM322_PLMN_SEARCH && !weight) { - struct msgb *nmsg; - - /* create AA flag */ - cs->mcc = cs->mnc = 0; - gsm322_cs_select(ms, 0, 0); - - new_c_state(cs, GSM322_C0_NULL); - - nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_END); - LOGP(DCS, LOGL_INFO, "PLMN search finished.\n"); - if (!nmsg) - return -ENOMEM; - gsm322_plmn_sendmsg(ms, nmsg); - - return 0; - } - - /* special case for HPLMN search */ - if (cs->state == GSM322_HPLMN_SEARCH && !weight) { - struct msgb *nmsg; - - nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND); - LOGP(DCS, LOGL_INFO, "HPLMN search finished, no cell.\n"); - if (!nmsg) - return -ENOMEM; - gsm322_plmn_sendmsg(ms, nmsg); - - new_c_state(cs, GSM322_C3_CAMPED_NORMALLY); - - cs->arfcn = cs->sel_arfcn; - cs->arfci = arfcn2index(cs->arfcn); - LOGP(DCS, LOGL_INFO, "Tuning back to frequency %s (rxlev " - "%s).\n", gsm_print_arfcn(cs->arfcn), - gsm_print_rxlev(cs->list[cs->arfci].rxlev)); - hack = 1; - gsm322_sync_to_cell(cs); - - return 0; - } - /* if all frequencies have been searched */ if (!weight) { - struct msgb *nmsg; -#if 0 - int found, any = 0; - - LOGP(DCS, LOGL_INFO, "All frequencies scanned.\n"); - - /* just see, if we search for any cell */ - if (cs->state == GSM322_C6_ANY_CELL_SEL - || cs->state == GSM322_C8_ANY_CELL_RESEL - || cs->state == GSM322_C9_CHOOSE_ANY_CELL) - any = 1; - - found = gsm322_cs_select(ms, any, 0); - - /* if found */ - if (found >= 0) { - struct gsm322_plmn *plmn = &ms->plmn; - - LOGP(DCS, LOGL_INFO, "Tune to frequency index %d.\n", - found); - /* tune */ - cs->arfci = found; - cs->arfcn = index2arfcn(cs->arfci); - cs->si = cs->list[cs->arfci].sysinfo; - hack = 1; - gsm322_sync_to_cell(cs); - - /* selected PLMN (manual) or any PLMN (auto) */ - switch (ms->settings.plmn_mode) { - case PLMN_MODE_AUTO: - if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) { - /* PLMN becomes available */ - nmsg = gsm322_msgb_alloc( - GSM322_EVENT_PLMN_AVAIL); - if (!nmsg) - return -ENOMEM; - gsm322_plmn_sendmsg(ms, nmsg); - } - break; - case PLMN_MODE_MANUAL: - if (plmn->state == GSM322_M3_NOT_ON_PLMN - && gsm322_is_plmn_avail(cs, plmn->mcc, - plmn->mnc)) { - /* PLMN becomes available */ - nmsg = gsm322_msgb_alloc( - GSM322_EVENT_PLMN_AVAIL); - if (!nmsg) - return -ENOMEM; - gsm322_plmn_sendmsg(ms, nmsg); - } - break; - } - - /* set selected cell */ - cs->selected = 1; - cs->sel_arfcn = cs->arfcn; - memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si)); - cs->sel_mcc = cs->si->mcc; - cs->sel_mnc = cs->si->mnc; - cs->sel_lac = cs->si->lac; - cs->sel_id = cs->si->cell_id; - - /* tell CS process about available cell */ - LOGP(DCS, LOGL_INFO, "Cell available.\n"); - nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND); - } else { -#endif - /* unset selected cell */ - gsm322_unselect_cell(cs); - - /* tell CS process about no cell available */ - LOGP(DCS, LOGL_INFO, "No cell available.\n"); - nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND); -// } - if (!nmsg) - return -ENOMEM; - gsm322_c_event(ms, nmsg); - msgb_free(nmsg); + gsm322_dump_cs_list(cs, GSM322_CS_FLAG_SYSINFO, print_dcs, + NULL); - return 0; + /* selection process done, process (negative) result */ + return gsm322_search_end(ms); } /* NOTE: We might already have system information from previous @@ -1831,8 +2245,6 @@ static int gsm322_cs_scan(struct osmocom_ms *ms) LOGP(DCS, LOGL_DEBUG, "Scanning frequency %s (rxlev %s).\n", gsm_print_arfcn(cs->arfcn), gsm_print_rxlev(cs->list[cs->arfci].rxlev)); - hack = 1; - gsm322_sync_to_cell(cs); /* Allocate/clean system information. */ cs->list[cs->arfci].flags &= ~GSM322_CS_FLAG_SYSINFO; @@ -1845,16 +2257,16 @@ static int gsm322_cs_scan(struct osmocom_ms *ms) if (!cs->list[cs->arfci].sysinfo) exit(-ENOMEM); cs->si = cs->list[cs->arfci].sysinfo; + cs->sync_retries = 0; + gsm322_sync_to_cell(cs, NULL, 0); /* increase scan counter for each maximum scan range */ -#ifndef SKIP_MAX_PER_BAND - if (gsm_sup_smax[j].max) { + if (!ms->settings.skip_max_per_band && gsm_sup_smax[band].max) { LOGP(DCS, LOGL_DEBUG, "%d frequencies left in band %d..%d\n", - gsm_sup_smax[j].max - gsm_sup_smax[j].temp, - gsm_sup_smax[j].start, gsm_sup_smax[j].end); - gsm_sup_smax[j].temp++; + gsm_sup_smax[band].max - gsm_sup_smax[band].temp, + gsm_sup_smax[band].start, gsm_sup_smax[band].end); + gsm_sup_smax[band].temp++; } -#endif return 0; } @@ -1866,6 +2278,7 @@ static int gsm322_cs_store(struct osmocom_ms *ms) struct gsm48_sysinfo *s = cs->si; struct gsm322_plmn *plmn = &ms->plmn; struct msgb *nmsg; + struct gsm322_msg *ngm; int found, any = 0; if (cs->state != GSM322_C2_STORED_CELL_SEL @@ -1875,6 +2288,7 @@ static int gsm322_cs_store(struct osmocom_ms *ms) && cs->state != GSM322_C8_ANY_CELL_RESEL && cs->state != GSM322_C5_CHOOSE_CELL && cs->state != GSM322_C9_CHOOSE_ANY_CELL + && cs->state != GSM322_ANY_SEARCH && cs->state != GSM322_PLMN_SEARCH && cs->state != GSM322_HPLMN_SEARCH) { LOGP(DCS, LOGL_FATAL, "This must only happen during cell " @@ -1884,27 +2298,13 @@ static int gsm322_cs_store(struct osmocom_ms *ms) /* store sysinfo */ cs->list[cs->arfci].flags |= GSM322_CS_FLAG_SYSINFO; - if (s->cell_barr - && !(cs->list[cs->arfci].sysinfo && cs->list[cs->arfci].sysinfo->sp && - cs->list[cs->arfci].sysinfo->sp_cbq)) + if (s->cell_barr && !(s->sp && s->sp_cbq)) cs->list[cs->arfci].flags |= GSM322_CS_FLAG_BARRED; else cs->list[cs->arfci].flags &= ~GSM322_CS_FLAG_BARRED; -#if 0 - cs->list[cs->arfci].min_db = s->rxlev_acc_min_db; - cs->list[cs->arfci].class_barr = s->class_barr; - cs->list[cs->arfci].max_pwr = s->ms_txpwr_max_ccch; -#endif - /* store selected network */ if (s->mcc) { -#if 0 - cs->list[cs->arfci].mcc = s->mcc; - cs->list[cs->arfci].mnc = s->mnc; - cs->list[cs->arfci].lac = s->lac; -#endif - if (gsm322_is_forbidden_la(ms, s->mcc, s->mnc, s->lac)) cs->list[cs->arfci].flags |= GSM322_CS_FLAG_FORBIDD; else @@ -1916,8 +2316,58 @@ static int gsm322_cs_store(struct osmocom_ms *ms) gsm_print_rxlev(cs->list[cs->arfci].rxlev), gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc), s->lac); + /* selected PLMN (auto) becomes available during "any search" */ + if (ms->settings.plmn_mode == PLMN_MODE_AUTO + && (cs->state == GSM322_ANY_SEARCH + || cs->state == GSM322_C6_ANY_CELL_SEL + || cs->state == GSM322_C8_ANY_CELL_RESEL + || cs->state == GSM322_C9_CHOOSE_ANY_CELL) + && plmn->state == GSM322_A4_WAIT_FOR_PLMN + && s->mcc == plmn->mcc && s->mnc == plmn->mnc) { + LOGP(DCS, LOGL_INFO, "Candidate network to become available " + "again\n"); + found = gsm322_cs_select(ms, cs->arfci, s->mcc, s->mnc, 0); + if (found >= 0) { + LOGP(DCS, LOGL_INFO, "Selected PLMN in \"A4_WAIT_F" + "OR_PLMN\" state becomes available.\n"); +indicate_plmn_avail: + /* PLMN becomes available */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL); + if (!nmsg) + return -ENOMEM; + /* set what PLMN becomes available */ + ngm = (struct gsm322_msg *) nmsg->data; + ngm->mcc = plmn->mcc; + ngm->mnc = plmn->mcc; + gsm322_plmn_sendmsg(ms, nmsg); + + new_c_state(cs, GSM322_C0_NULL); + + return 0; + } + } + + /* selected PLMN (manual) becomes available during "any search" */ + if (ms->settings.plmn_mode == PLMN_MODE_MANUAL + && (cs->state == GSM322_ANY_SEARCH + || cs->state == GSM322_C6_ANY_CELL_SEL + || cs->state == GSM322_C8_ANY_CELL_RESEL + || cs->state == GSM322_C9_CHOOSE_ANY_CELL) + && plmn->state == GSM322_M3_NOT_ON_PLMN + && s->mcc == plmn->mcc && s->mnc == plmn->mnc) { + LOGP(DCS, LOGL_INFO, "Candidate network to become available " + "again\n"); + found = gsm322_cs_select(ms, cs->arfci, s->mcc, s->mnc, 0); + if (found >= 0) { + LOGP(DCS, LOGL_INFO, "Current selected PLMN in \"M3_N" + "OT_ON_PLMN\" state becomes available.\n"); + goto indicate_plmn_avail; + } + } + /* special case for PLMN search */ - if (cs->state == GSM322_PLMN_SEARCH) + if (cs->state == GSM322_PLMN_SEARCH + || cs->state == GSM322_ANY_SEARCH) /* tune to next cell */ return gsm322_cs_scan(ms); @@ -1943,15 +2393,23 @@ static int gsm322_cs_store(struct osmocom_ms *ms) if (cs->state == GSM322_C6_ANY_CELL_SEL || cs->state == GSM322_C8_ANY_CELL_RESEL || cs->state == GSM322_C9_CHOOSE_ANY_CELL) - any = 1; + any = 1; - found = gsm322_cs_select(ms, any, 0); + if (cs->state == GSM322_C4_NORMAL_CELL_RESEL + || cs->state == GSM322_C8_ANY_CELL_RESEL) + found = gsm322_cs_reselect(ms, cs->mcc, cs->mnc, any); + else + found = gsm322_cs_select(ms, -1, cs->mcc, cs->mnc, any); /* if not found */ if (found < 0) { LOGP(DCS, LOGL_INFO, "Cell not suitable and allowable.\n"); /* tune to next cell */ - return gsm322_cs_scan(ms); + if (cs->state == GSM322_C4_NORMAL_CELL_RESEL + || cs->state == GSM322_C8_ANY_CELL_RESEL) + return gsm322_nb_scan(ms); + else + return gsm322_cs_scan(ms); } LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found); @@ -1959,32 +2417,8 @@ static int gsm322_cs_store(struct osmocom_ms *ms) cs->arfci = found; cs->arfcn = index2arfcn(cs->arfci); cs->si = cs->list[cs->arfci].sysinfo; - hack = 1; - gsm322_sync_to_cell(cs); - - /* selected PLMN (manual) or any PLMN (auto) */ - switch (ms->settings.plmn_mode) { - case PLMN_MODE_AUTO: - if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) { - /* PLMN becomes available */ - nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL); - if (!nmsg) - return -ENOMEM; - gsm322_plmn_sendmsg(ms, nmsg); - } - break; - case PLMN_MODE_MANUAL: - if (plmn->state == GSM322_M3_NOT_ON_PLMN - && gsm322_is_plmn_avail(cs, plmn->mcc, - plmn->mnc)) { - /* PLMN becomes available */ - nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL); - if (!nmsg) - return -ENOMEM; - gsm322_plmn_sendmsg(ms, nmsg); - } - break; - } + cs->sync_retries = SYNC_RETRIES; + gsm322_sync_to_cell(cs, NULL, 0); /* set selected cell */ cs->selected = 1; @@ -1994,6 +2428,15 @@ static int gsm322_cs_store(struct osmocom_ms *ms) cs->sel_mnc = cs->si->mnc; cs->sel_lac = cs->si->lac; cs->sel_id = cs->si->cell_id; + if (ms->rrlayer.monitor) { + vty_notify(ms, "MON: %scell selected ARFCN=%s MCC=%s MNC=%s " + "LAC=0x%04x cellid=0x%04x (%s %s)\n", + (any) ? "any " : "", gsm_print_arfcn(cs->sel_arfcn), + gsm_print_mcc(cs->sel_mcc), gsm_print_mnc(cs->sel_mnc), + cs->sel_lac, cs->sel_id, + gsm_get_mcc(cs->sel_mcc), + gsm_get_mnc(cs->sel_mcc, cs->sel_mnc)); + } /* tell CS process about available cell */ LOGP(DCS, LOGL_INFO, "Cell available.\n"); @@ -2113,12 +2556,18 @@ static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg) struct gsm322_msg *gm = (struct gsm322_msg *) msg->data; struct msgb *nmsg; -#if 0 - if (rr->state != GSM48_RR_ST_IDLE) { - LOGP(DCS, LOGL_INFO, "Ignoring in dedicated mode.\n"); - return -EBUSY; + /* start in case we are camping on neighbour cell */ + if ((cs->state == GSM322_C3_CAMPED_NORMALLY + || cs->state == GSM322_C7_CAMPED_ANY_CELL) + && (cs->neighbour)) { + if (s->si3 || s->si4) { + stop_cs_timer(cs); + LOGP(DCS, LOGL_INFO, "Relevant sysinfo of neighbour " + "cell is now received or updated.\n"); + return gsm322_nb_read(cs, 1); + } + return 0; } -#endif /* Store BA if we have full system info about cells and neigbor cells. * Depending on the extended bit in the channel description, @@ -2132,10 +2581,10 @@ static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg) || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter) && s->si1 && s->si2 - && (!s->nb_ext_ind_si2 + && (!s->nb_ext_ind_si2 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis) || (s->si2bis && s->si2ter && s->nb_ext_ind_si2 - && s->nb_ext_ind_si2bis))) + && s->nb_ext_ind_si2bis))) gsm322_store_ba_list(cs, s); /* update sel_si, if all relevant system informations received */ @@ -2143,12 +2592,16 @@ static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg) && (!s->nb_ext_ind_si2 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis) || (s->si2bis && s->si2ter && s->nb_ext_ind_si2 - && s->nb_ext_ind_si2bis))) { + && s->nb_ext_ind_si2bis))) { if (cs->selected) { LOGP(DCS, LOGL_INFO, "Sysinfo of selected cell is " - "updated.\n"); + "now received or updated.\n"); memcpy(&cs->sel_si, s, sizeof(cs->sel_si)); - //gsm48_sysinfo_dump(s, print_dcs, NULL); + + /* start in case we are camping on serving cell */ + if (cs->state == GSM322_C3_CAMPED_NORMALLY + || cs->state == GSM322_C7_CAMPED_ANY_CELL) + gsm322_nb_start(ms, 0); } } @@ -2160,15 +2613,17 @@ static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg) && cs->list[cs->arfci].sysinfo->sp && cs->list[cs->arfci].sysinfo->sp_cbq)) { LOGP(DCS, LOGL_INFO, "Cell becomes barred.\n"); + if (ms->rrlayer.monitor) + vty_notify(ms, "MON: trigger cell re-selection" + ": cell becomes barred\n"); trigger_resel: /* mark cell as unscanned */ cs->list[cs->arfci].flags &= ~GSM322_CS_FLAG_SYSINFO; if (cs->list[cs->arfci].sysinfo) { - LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%s\n", + LOGP(DCS, LOGL_DEBUG, "free sysinfo arfcn=%s\n", gsm_print_arfcn(cs->arfcn)); talloc_free(cs->list[cs->arfci].sysinfo); cs->list[cs->arfci].sysinfo = NULL; - gsm322_unselect_cell(cs); } /* trigger reselection without queueing, * because other sysinfo message may be queued @@ -2186,6 +2641,9 @@ static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg) if (!((subscr->acc_class & 0xfbff) & (s->class_barr ^ 0xffff))) { LOGP(DCS, LOGL_INFO, "Cell access becomes barred.\n"); + if (ms->rrlayer.monitor) + vty_notify(ms, "MON: trigger cell re-selection" + ": access to cell becomes barred\n"); goto trigger_resel; } } @@ -2195,11 +2653,17 @@ static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg) || cs->sel_lac != s->lac) { LOGP(DCS, LOGL_NOTICE, "Cell changes location area. " "This is not good!\n"); + if (ms->rrlayer.monitor) + vty_notify(ms, "MON: trigger cell re-selection: " + "cell changes LAI\n"); goto trigger_resel; } if (cs->sel_id != s->cell_id) { LOGP(DCS, LOGL_NOTICE, "Cell changes cell ID. " "This is not good!\n"); + if (ms->rrlayer.monitor) + vty_notify(ms, "MON: trigger cell re-selection: " + "cell changes cell ID\n"); goto trigger_resel; } @@ -2258,6 +2722,12 @@ static void gsm322_cs_timeout(void *arg) struct gsm322_cellsel *cs = arg; struct osmocom_ms *ms = cs->ms; + if (cs->neighbour) { + LOGP(DCS, LOGL_INFO, "Neighbour cell read failed.\n"); + gsm322_nb_read(cs, 0); + return; + } + /* if we have no lock, we retry */ if (cs->ccch_state != GSM322_CCCH_ST_SYNC) LOGP(DCS, LOGL_INFO, "Cell selection failed, sync timeout.\n"); @@ -2271,11 +2741,14 @@ static void gsm322_cs_timeout(void *arg) gsm_print_arfcn(cs->arfcn)); talloc_free(cs->list[cs->arfci].sysinfo); cs->list[cs->arfci].sysinfo = NULL; - gsm322_unselect_cell(cs); } /* tune to next cell */ - gsm322_cs_scan(ms); + if (cs->state == GSM322_C4_NORMAL_CELL_RESEL + || cs->state == GSM322_C8_ANY_CELL_RESEL) + gsm322_nb_scan(ms); + else + gsm322_cs_scan(ms); return; } @@ -2335,8 +2808,6 @@ static int gsm322_cs_powerscan(struct osmocom_ms *ms) found++; } if (!found) { - struct msgb *nmsg; - LOGP(DCS, LOGL_INFO, "Found no frequency.\n"); /* on normal cell selection, start over */ if (cs->state == GSM322_C1_NORMAL_CELL_SEL) { @@ -2346,54 +2817,12 @@ static int gsm322_cs_powerscan(struct osmocom_ms *ms) ~(GSM322_CS_FLAG_POWER | GSM322_CS_FLAG_SIGNAL | GSM322_CS_FLAG_SYSINFO); - if (cs->list[i].sysinfo) { - LOGP(DCS, LOGL_INFO, "free " - "sysinfo arfcn=%s\n", - gsm_print_arfcn( - index2arfcn(i))); - talloc_free( - cs->list[i].sysinfo); - cs->list[i].sysinfo = NULL; - } } - /* no cell selected */ - gsm322_unselect_cell(cs); goto again; } - /* on other cell selection, indicate "no cell found" */ - /* NOTE: PLMN search process handles it. - * If not handled there, CS process gets indicated. - * If we would continue to process CS, then we might get - * our list of scanned cells disturbed. - */ - if (cs->state == GSM322_PLMN_SEARCH) - nmsg = gsm322_msgb_alloc( - GSM322_EVENT_PLMN_SEARCH_END); - else - nmsg = gsm322_msgb_alloc( - GSM322_EVENT_NO_CELL_FOUND); - if (!nmsg) - return -ENOMEM; - gsm322_plmn_sendmsg(ms, nmsg); - - /* if HPLMN search, select last frequency */ - if (cs->state == GSM322_HPLMN_SEARCH) { - new_c_state(cs, GSM322_C3_CAMPED_NORMALLY); - - cs->arfcn = cs->sel_arfcn; - cs->arfci = arfcn2index(cs->arfcn); - LOGP(DCS, LOGL_INFO, "Tuning back to frequency " - "%s (rxlev %s).\n", - gsm_print_arfcn(cs->arfcn), - gsm_print_rxlev( - cs->list[cs->arfci].rxlev)); - hack = 1; - gsm322_sync_to_cell(cs); - - } else - new_c_state(cs, GSM322_C0_NULL); - return 0; + /* freq. scan process done, process (negative) result */ + return gsm322_search_end(ms); } LOGP(DCS, LOGL_INFO, "Found %d frequencies.\n", found); cs->scan_state = 0xffffffff; /* higher than high */ @@ -2425,8 +2854,7 @@ static int gsm322_cs_powerscan(struct osmocom_ms *ms) l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL); cs->powerscan = 1; } -//#warning TESTING!!!! -//usleep(300000); + cs->sync_pending = 0; return l1ctl_tx_pm_req_range(ms, index2arfcn(s), index2arfcn(e)); } @@ -2437,6 +2865,7 @@ int gsm322_l1_signal(unsigned int subsys, unsigned int signal, struct gsm322_cellsel *cs; struct osmobb_meas_res *mr; struct osmobb_fbsb_res *fr; + struct osmobb_neigh_pm_ind *ni; int i; int8_t rxlev; @@ -2468,6 +2897,14 @@ int gsm322_l1_signal(unsigned int subsys, unsigned int signal, "rxlev %s (%d))\n", gsm_print_arfcn(index2arfcn(i)), gsm_print_rxlev(rxlev), rxlev); + } else + /* no signal found, free sysinfo, if allocated */ + if (cs->list[i].sysinfo) { + cs->list[i].flags &= ~GSM322_CS_FLAG_SYSINFO; + LOGP(DCS, LOGL_DEBUG, "free sysinfo ARFCN=%s\n", + gsm_print_arfcn(index2arfcn(i))); + talloc_free(cs->list[i].sysinfo); + cs->list[i].sysinfo = NULL; } break; case S_L1CTL_PM_DONE: @@ -2482,6 +2919,17 @@ int gsm322_l1_signal(unsigned int subsys, unsigned int signal, fr = signal_data; ms = fr->ms; cs = &ms->cellsel; + if (cs->powerscan) + return -EINVAL; + cs->sync_pending = 0; + if (cs->arfcn != fr->band_arfcn) { + LOGP(DCS, LOGL_NOTICE, "Channel synched on " + "wrong ARFCN=%d, syncing on right ARFCN again" + "...\n", fr->band_arfcn); + cs->sync_retries = SYNC_RETRIES; + gsm322_sync_to_cell(cs, cs->neighbour, 0); + break; + } if (cs->ccch_state == GSM322_CCCH_ST_INIT) { LOGP(DCS, LOGL_INFO, "Channel synched. (ARFCN=%s, " "snr=%u, BSIC=%u)\n", @@ -2489,13 +2937,6 @@ int gsm322_l1_signal(unsigned int subsys, unsigned int signal, cs->ccch_state = GSM322_CCCH_ST_SYNC; if (cs->si) cs->si->bsic = fr->bsic; -#if 0 - stop_cs_timer(cs); - - /* in dedicated mode */ - if (ms->rrlayer.state == GSM48_RR_ST_CONN_PEND) - return gsm48_rr_tx_rand_acc(ms, NULL); -#endif /* set timer for reading BCCH */ if (cs->state == GSM322_C2_STORED_CELL_SEL @@ -2505,6 +2946,7 @@ int gsm322_l1_signal(unsigned int subsys, unsigned int signal, || cs->state == GSM322_C8_ANY_CELL_RESEL || cs->state == GSM322_C5_CHOOSE_CELL || cs->state == GSM322_C9_CHOOSE_ANY_CELL + || cs->state == GSM322_ANY_SEARCH || cs->state == GSM322_PLMN_SEARCH || cs->state == GSM322_HPLMN_SEARCH) start_cs_timer(cs, ms->support.scan_to, 0); @@ -2513,32 +2955,79 @@ int gsm322_l1_signal(unsigned int subsys, unsigned int signal, /* set downlink signalling failure criterion */ ms->meas.ds_fail = ms->meas.dsc = ms->settings.dsc_max; LOGP(DRR, LOGL_INFO, "using DSC of %d\n", ms->meas.dsc); + + /* start in case we are camping on serving/neighbour + * cell */ + if (cs->state == GSM322_C3_CAMPED_NORMALLY + || cs->state == GSM322_C7_CAMPED_ANY_CELL) { + if (cs->neighbour) + gsm322_nb_synced(cs, 1); + else + gsm322_nb_start(ms, 1); + } } break; case S_L1CTL_FBSB_ERR: -#if 0 - if (hack) { - ms = signal_data; - cs = &ms->cellsel; - gsm322_sync_to_cell(cs); - hack--; + fr = signal_data; + ms = fr->ms; + cs = &ms->cellsel; + if (cs->powerscan) + return -EINVAL; + cs->sync_pending = 0; + /* retry */ + if (cs->sync_retries) { LOGP(DCS, LOGL_INFO, "Channel sync error, try again\n"); + cs->sync_retries--; + gsm322_sync_to_cell(cs, cs->neighbour, 0); + break; + } + if (cs->arfcn != fr->band_arfcn) { + LOGP(DCS, LOGL_NOTICE, "Channel synched failed on " + "wrong ARFCN=%d, syncing on right ARFCN again" + "...\n", fr->band_arfcn); + cs->sync_retries = SYNC_RETRIES; + gsm322_sync_to_cell(cs, cs->neighbour, 0); break; } -#endif LOGP(DCS, LOGL_INFO, "Channel sync error.\n"); - ms = signal_data; - cs = &ms->cellsel; + /* no sync, free sysinfo, if allocated */ + if (cs->list[cs->arfci].sysinfo) { + cs->list[cs->arfci].flags &= ~GSM322_CS_FLAG_SYSINFO; + LOGP(DCS, LOGL_DEBUG, "free sysinfo ARFCN=%s\n", + gsm_print_arfcn(index2arfcn(cs->arfci))); + talloc_free(cs->list[cs->arfci].sysinfo); + cs->list[cs->arfci].sysinfo = NULL; + } + if (cs->selected && cs->sel_arfcn == cs->arfcn) { + LOGP(DCS, LOGL_INFO, "Unselect cell due to sync " + "error!\n"); + /* unset selected cell */ + gsm322_unselect_cell(cs); + } stop_cs_timer(cs); - if (cs->selected) - gsm322_cs_loss(cs); - else - gsm322_cs_timeout(cs); + + /* start in case we are camping on neighbour * cell */ + if (cs->state == GSM322_C3_CAMPED_NORMALLY + || cs->state == GSM322_C7_CAMPED_ANY_CELL) { + if (cs->neighbour) { + gsm322_nb_synced(cs, 0); + break; + } + } + + gsm322_cs_loss(cs); break; case S_L1CTL_LOSS_IND: ms = signal_data; cs = &ms->cellsel; + LOGP(DCS, LOGL_INFO, "Loss of CCCH.\n"); + if (cs->selected && cs->sel_arfcn == cs->arfcn) { + LOGP(DCS, LOGL_INFO, "Unselect cell due to loss\n"); + /* unset selected cell */ + gsm322_unselect_cell(cs); + } + stop_cs_timer(cs); gsm322_cs_loss(cs); break; case S_L1CTL_RESET: @@ -2548,6 +3037,21 @@ int gsm322_l1_signal(unsigned int subsys, unsigned int signal, return 0; } break; + case S_L1CTL_NEIGH_PM_IND: + ni = signal_data; + ms = ni->ms; +#ifdef COMMING_LATE_R + /* in dedicated mode */ + if (ms->rrlayer.dm_est) + gsm48_rr_meas_ind(ms, ni->band_arfcn, ni->rx_lev); + else +#endif + /* in camping mode */ + if ((ms->cellsel.state == GSM322_C3_CAMPED_NORMALLY + || ms->cellsel.state == GSM322_C7_CAMPED_ANY_CELL) + && !ms->cellsel.neighbour) + gsm322_nb_meas_ind(ms, ni->band_arfcn, ni->rx_lev); + break; } return 0; @@ -2557,27 +3061,28 @@ static void gsm322_cs_loss(void *arg) { struct gsm322_cellsel *cs = arg; struct osmocom_ms *ms = cs->ms; - struct gsm48_rrlayer *rr = &ms->rrlayer; - LOGP(DCS, LOGL_INFO, "Loss of CCCH.\n"); - - if (cs->state == GSM322_C3_CAMPED_NORMALLY - || cs->state == GSM322_C7_CAMPED_ANY_CELL) { - if (rr->state == GSM48_RR_ST_IDLE) { + if ((cs->state == GSM322_C3_CAMPED_NORMALLY + || cs->state == GSM322_C7_CAMPED_ANY_CELL) + && !cs->neighbour) { struct msgb *nmsg; LOGP(DCS, LOGL_INFO, "Loss of CCCH, Trigger " "re-selection.\n"); - - /* unset selected cell */ - gsm322_unselect_cell(cs); + if (ms->rrlayer.monitor) + vty_notify(ms, "MON: trigger cell " + "re-selection: loss of signal\n"); nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL); if (!nmsg) return; gsm322_c_event(ms, nmsg); msgb_free(nmsg); - } else { + + return; + } else + if (cs->state == GSM322_CONNECTED_MODE_1 + || cs->state == GSM322_CONNECTED_MODE_2) { LOGP(DCS, LOGL_INFO, "Loss of SACCH, Trigger RR " "abort.\n"); @@ -2588,9 +3093,12 @@ static void gsm322_cs_loss(void *arg) * because the function call above may cause * to return from idle state and trigger cell re-sel. */ - } + + return; } + gsm322_cs_timeout(cs); + return; } @@ -2598,6 +3106,25 @@ static void gsm322_cs_loss(void *arg) * handler for cell selection process */ +/* start any cell search */ +static int gsm322_c_any_search(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + int i; + + new_c_state(cs, GSM322_ANY_SEARCH); + + /* mark all frequencies as scanned */ + for (i = 0; i <= 1023+299; i++) { + cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER + | GSM322_CS_FLAG_SIGNAL + | GSM322_CS_FLAG_SYSINFO); + } + + /* start power scan */ + return gsm322_cs_powerscan(ms); +} + /* start PLMN search */ static int gsm322_c_plmn_search(struct osmocom_ms *ms, struct msgb *msg) { @@ -2606,18 +3133,11 @@ static int gsm322_c_plmn_search(struct osmocom_ms *ms, struct msgb *msg) new_c_state(cs, GSM322_PLMN_SEARCH); - /* mark all frequencies except our own BA to be scanned */ + /* mark all frequencies as scanned */ for (i = 0; i <= 1023+299; i++) { cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER | GSM322_CS_FLAG_SIGNAL | GSM322_CS_FLAG_SYSINFO); - if (cs->list[i].sysinfo) { - LOGP(DCS, LOGL_INFO, "free sysinfo ARFCN=%s\n", - gsm_print_arfcn(index2arfcn(i))); - talloc_free(cs->list[i].sysinfo); - cs->list[i].sysinfo = NULL; - gsm322_unselect_cell(cs); - } } /* unset selected cell */ @@ -2635,7 +3155,7 @@ static int gsm322_c_hplmn_search(struct osmocom_ms *ms, struct msgb *msg) new_c_state(cs, GSM322_HPLMN_SEARCH); - /* mark all frequencies except our own BA to be scanned */ + /* mark all frequencies except our own BA as unscanned */ for (i = 0; i <= 1023+299; i++) { if (i != sel_i && (cs->list[i].flags & GSM322_CS_FLAG_SYSINFO) @@ -2643,28 +3163,27 @@ static int gsm322_c_hplmn_search(struct osmocom_ms *ms, struct msgb *msg) cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER | GSM322_CS_FLAG_SIGNAL | GSM322_CS_FLAG_SYSINFO); - if (cs->list[i].sysinfo) { - LOGP(DCS, LOGL_INFO, "free sysinfo ARFCN=%s\n", - gsm_print_arfcn(index2arfcn(i))); - talloc_free(cs->list[i].sysinfo); - cs->list[i].sysinfo = NULL; - } } } - /* no cell selected */ - gsm322_unselect_cell(cs); - /* start power scan */ return gsm322_cs_powerscan(ms); } /* start stored cell selection */ -static int gsm322_c_stored_cell_sel(struct osmocom_ms *ms, struct gsm322_ba_list *ba) +static int gsm322_c_stored_cell_sel(struct osmocom_ms *ms, + struct gsm322_ba_list *ba) { struct gsm322_cellsel *cs = &ms->cellsel; int i; + /* we weed to rescan */ + for (i = 0; i <= 1023+299; i++) { + cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER + | GSM322_CS_FLAG_SIGNAL + | GSM322_CS_FLAG_SYSINFO); + } + new_c_state(cs, GSM322_C2_STORED_CELL_SEL); /* flag all frequencies that are in current band allocation */ @@ -2688,18 +3207,12 @@ static int gsm322_c_normal_cell_sel(struct osmocom_ms *ms, struct msgb *msg) struct gsm322_cellsel *cs = &ms->cellsel; int i; - /* except for stored cell selection state, we weed to rescan ?? */ + /* except for stored cell selection state, we weed to rescan */ if (cs->state != GSM322_C2_STORED_CELL_SEL) { for (i = 0; i <= 1023+299; i++) { cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER | GSM322_CS_FLAG_SIGNAL | GSM322_CS_FLAG_SYSINFO); - if (cs->list[i].sysinfo) { - LOGP(DCS, LOGL_INFO, "free sysinfo ARFCN=%s\n", - gsm_print_arfcn(index2arfcn(i))); - talloc_free(cs->list[i].sysinfo); - cs->list[i].sysinfo = NULL; - } } } @@ -2716,6 +3229,8 @@ static int gsm322_c_normal_cell_sel(struct osmocom_ms *ms, struct msgb *msg) static int gsm322_c_any_cell_sel(struct osmocom_ms *ms, struct msgb *msg) { struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm322_msg *gm = (struct gsm322_msg *) msg->data; + int msg_type = gm->msg_type; /* in case we already tried any cell (re-)selection, power scan again */ if (cs->state == GSM322_C0_NULL @@ -2727,29 +3242,26 @@ static int gsm322_c_any_cell_sel(struct osmocom_ms *ms, struct msgb *msg) cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER | GSM322_CS_FLAG_SIGNAL | GSM322_CS_FLAG_SYSINFO); - if (cs->list[i].sysinfo) { - LOGP(DCS, LOGL_INFO, "free sysinfo ARFCN=%s\n", - gsm_print_arfcn(index2arfcn(i))); - talloc_free(cs->list[i].sysinfo); - cs->list[i].sysinfo = NULL; - } } - } - /* after re-selection, indicate no cell found */ - if (cs->state == GSM322_C6_ANY_CELL_SEL - || cs->state == GSM322_C8_ANY_CELL_RESEL) { - struct msgb *nmsg; - /* tell that we have no cell found */ - nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_NO_CELL_FOUND); - if (!nmsg) - return -ENOMEM; - gsm48_mmevent_msg(ms, nmsg); + /* indicate to MM that we lost coverage. + * this is the only case where we really have no coverage. + * we tell MM, so it will enter the "No Cell Avaiable" state. */ + if (msg_type == GSM322_EVENT_NO_CELL_FOUND) { + struct msgb *nmsg; - } else { - new_c_state(cs, GSM322_C6_ANY_CELL_SEL); + /* tell that we have no cell found + * (not any cell at all) */ + nmsg = gsm48_mmevent_msgb_alloc( + GSM48_MM_EVENT_NO_CELL_FOUND); + if (!nmsg) + return -ENOMEM; + gsm48_mmevent_msg(ms, nmsg); + } } + new_c_state(cs, GSM322_C6_ANY_CELL_SEL); + cs->mcc = cs->mnc = 0; /* unset selected cell */ @@ -2759,32 +3271,91 @@ static int gsm322_c_any_cell_sel(struct osmocom_ms *ms, struct msgb *msg) return gsm322_cs_powerscan(ms); } +static void gsm322_any_timeout(void *arg) +{ + struct gsm322_cellsel *cs = arg; + struct osmocom_ms *ms = cs->ms; + + /* the timer may still run when not camping, so we ignore it. + * it will be restarted whenever the 'camped on any cell' state + * is reached. */ + if (cs->state != GSM322_C7_CAMPED_ANY_CELL) + return; + + /* in case the time has been started before SIM was removed */ + if (!ms->subscr.sim_valid) + return; + + LOGP(DCS, LOGL_INFO, "'Any cell selection timer' timed out. " + "Starting special search to find allowed PLMNs.\n"); + + gsm322_c_any_search(ms, NULL); +} + +/* sim is removed, proceed with any cell selection */ +static int gsm322_c_sim_remove(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct llist_head *lh, *lh2; + + /* flush list of forbidden LAs */ + llist_for_each_safe(lh, lh2, &plmn->forbidden_la) { + llist_del(lh); + talloc_free(lh); + } + return gsm322_c_any_cell_sel(ms, msg); +} + /* start noraml cell re-selection */ static int gsm322_c_normal_cell_resel(struct osmocom_ms *ms, struct msgb *msg) { struct gsm322_cellsel *cs = &ms->cellsel; + struct msgb *nmsg; - new_c_state(cs, GSM322_C4_NORMAL_CELL_RESEL); + /* store last camped cell. this is required for next cell + * monitoring reselection criterion */ + cs->last_serving_arfcn = cs->sel_arfcn; + cs->last_serving_valid = 1; - /* NOTE: We keep our scan info we have so far. - * This may cause a skip in power scan. */ + /* unset selected cell */ + gsm322_unselect_cell(cs); - /* start power scan */ - return gsm322_cs_powerscan(ms); + /* tell MM that we lost coverage */ + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_LOST_COVERAGE); + if (!nmsg) + return -ENOMEM; + gsm48_mmevent_msg(ms, nmsg); + + new_c_state(cs, GSM322_C4_NORMAL_CELL_RESEL); + + /* start scanning neighbour cells for reselection */ + return gsm322_nb_scan(ms); } /* start any cell re-selection */ static int gsm322_c_any_cell_resel(struct osmocom_ms *ms, struct msgb *msg) { struct gsm322_cellsel *cs = &ms->cellsel; + struct msgb *nmsg; - new_c_state(cs, GSM322_C8_ANY_CELL_RESEL); + /* store last camped cell. this is required for next cell + * monitoring reselection criterion */ + cs->last_serving_arfcn = cs->sel_arfcn; + cs->last_serving_valid = 1; + + /* unset selected cell */ + gsm322_unselect_cell(cs); + + /* tell MM that we lost coverage */ + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_LOST_COVERAGE); + if (!nmsg) + return -ENOMEM; + gsm48_mmevent_msg(ms, nmsg); - /* NOTE: We keep our scan info we have so far. - * This may cause a skip in power scan. */ + new_c_state(cs, GSM322_C8_ANY_CELL_RESEL); - /* start power scan */ - return gsm322_cs_powerscan(ms); + /* start scanning neighbour cells for reselection */ + return gsm322_nb_scan(ms); } /* a suitable cell was found, so we camp normally */ @@ -2799,6 +3370,10 @@ static int gsm322_c_camp_normally(struct osmocom_ms *ms, struct msgb *msg) gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc), gsm_get_mnc(cs->sel_mcc, cs->sel_mnc)); + /* if we did cell reselection, we have a valid last serving cell */ + if (cs->state != GSM322_C4_NORMAL_CELL_RESEL) + cs->last_serving_valid = 0; + /* tell that we have selected a (new) cell */ nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED); if (!nmsg) @@ -2810,7 +3385,7 @@ static int gsm322_c_camp_normally(struct osmocom_ms *ms, struct msgb *msg) return 0; } -/* a not suitable cell was found, so we camp on any cell */ +/* any cell was found, so we camp on any cell */ static int gsm322_c_camp_any_cell(struct osmocom_ms *ms, struct msgb *msg) { struct gsm322_cellsel *cs = &ms->cellsel; @@ -2822,12 +3397,39 @@ static int gsm322_c_camp_any_cell(struct osmocom_ms *ms, struct msgb *msg) gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc), gsm_get_mnc(cs->sel_mcc, cs->sel_mnc)); + /* (re-)starting 'any cell selection' timer to look for coverage of + * allowed PLMNs. + * start timer, if not running. + * restart timer, if we just entered the 'camped any cell' state */ + if (ms->subscr.sim_valid + && (cs->state != GSM322_C8_ANY_CELL_RESEL + || !osmo_timer_pending(&cs->any_timer))) { + struct gsm322_plmn *plmn = &ms->plmn; + + stop_any_timer(cs); + if (ms->settings.plmn_mode == PLMN_MODE_MANUAL + && (!plmn->mcc + || gsm_subscr_is_forbidden_plmn(&ms->subscr, plmn->mcc, + plmn->mnc))) { + LOGP(DCS, LOGL_INFO, "Not starting 'any search' timer, " + "because no selected PLMN or forbidden\n"); + } else + start_any_timer(cs, ms->subscr.any_timeout, 0); + } - /* tell that we have selected a (new) cell */ - nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED); - if (!nmsg) - return -ENOMEM; - gsm48_mmevent_msg(ms, nmsg); + /* if we did cell reselection, we have a valid last serving cell */ + if (cs->state != GSM322_C8_ANY_CELL_RESEL) + cs->last_serving_valid = 0; + + /* tell that we have selected a (new) cell. + * this cell iss not allowable, so the MM state will enter limited + * service */ + if (cs->state != GSM322_C7_CAMPED_ANY_CELL) { + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED); + if (!nmsg) + return -ENOMEM; + gsm48_mmevent_msg(ms, nmsg); + } new_c_state(cs, GSM322_C7_CAMPED_ANY_CELL); @@ -2928,12 +3530,6 @@ static int gsm322_cs_choose(struct osmocom_ms *ms) cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER | GSM322_CS_FLAG_SIGNAL | GSM322_CS_FLAG_SYSINFO); - if (cs->list[i].sysinfo) { - LOGP(DCS, LOGL_INFO, "free sysinfo ARFCN=%s\n", - gsm_print_arfcn(index2arfcn(i))); - talloc_free(cs->list[i].sysinfo); - cs->list[i].sysinfo = NULL; - } } /* unset selected cell */ @@ -2954,8 +3550,9 @@ static int gsm322_c_choose_cell(struct osmocom_ms *ms, struct msgb *msg) struct msgb *nmsg; if (!cs->selected) { - printf("No cell selected when ret.idle, please fix!\n"); - exit(0L); + LOGP(DCS, LOGL_INFO, "Cell not selected anymore, " + "choose cell!\n"); + goto choose; } cs->arfcn = cs->sel_arfcn; cs->arfci = arfcn2index(cs->arfcn); @@ -2963,9 +3560,13 @@ static int gsm322_c_choose_cell(struct osmocom_ms *ms, struct msgb *msg) /* be sure to go to current camping frequency on return */ LOGP(DCS, LOGL_INFO, "Selecting ARFCN %s. after LOC.UPD.\n", gsm_print_arfcn(cs->arfcn)); - hack = 1; - gsm322_sync_to_cell(cs); + cs->sync_retries = SYNC_RETRIES; + gsm322_sync_to_cell(cs, NULL, 0); cs->si = cs->list[cs->arfci].sysinfo; + if (!cs->si) { + printf("No SI when ret.idle, please fix!\n"); + exit(0L); + } new_c_state(cs, GSM322_C3_CAMPED_NORMALLY); @@ -2978,6 +3579,7 @@ static int gsm322_c_choose_cell(struct osmocom_ms *ms, struct msgb *msg) return 0; } +choose: new_c_state(cs, GSM322_C5_CHOOSE_CELL); return gsm322_cs_choose(ms); @@ -2996,6 +3598,7 @@ static int gsm322_c_choose_any_cell(struct osmocom_ms *ms, struct msgb *msg) /* a new PLMN is selected by PLMN search process */ static int gsm322_c_new_plmn(struct osmocom_ms *ms, struct msgb *msg) { + struct gsm322_msg *gm = (struct gsm322_msg *) msg->data; struct gsm322_cellsel *cs = &ms->cellsel; struct gsm322_plmn *plmn = &ms->plmn; struct gsm322_ba_list *ba; @@ -3003,10 +3606,14 @@ static int gsm322_c_new_plmn(struct osmocom_ms *ms, struct msgb *msg) cs->mcc = plmn->mcc; cs->mnc = plmn->mnc; - LOGP(DSUM, LOGL_INFO, "Selecting network (mcc=%s " - "mnc=%s %s, %s)\n", gsm_print_mcc(cs->mcc), - gsm_print_mnc(cs->mnc), gsm_get_mcc(cs->mcc), - gsm_get_mnc(cs->mcc, cs->mnc)); + if (gm->limited) { + LOGP(DCS, LOGL_INFO, "Selected PLMN with limited service.\n"); + return gsm322_c_any_cell_sel(ms, msg); + } + + LOGP(DSUM, LOGL_INFO, "Selecting PLMN (mcc=%s mnc=%s %s, %s)\n", + gsm_print_mcc(cs->mcc), gsm_print_mnc(cs->mnc), + gsm_get_mcc(cs->mcc), gsm_get_mnc(cs->mcc, cs->mnc)); /* search for BA list */ ba = gsm322_find_ba_list(cs, plmn->mcc, plmn->mnc); @@ -3026,17 +3633,28 @@ static int gsm322_c_conn_mode_1(struct osmocom_ms *ms, struct msgb *msg) struct gsm322_cellsel *cs = &ms->cellsel; /* check for error */ - if (!cs->selected) - return -EINVAL; + if (!cs->selected) { + LOGP(DCS, LOGL_INFO, "No cell selected, please fix!\n"); + exit(0L); + } cs->arfcn = cs->sel_arfcn; cs->arfci = arfcn2index(cs->arfcn); + /* maybe we are currently syncing to neighbours */ + stop_cs_timer(cs); + + new_c_state(cs, GSM322_CONNECTED_MODE_1); + /* be sure to go to current camping frequency on return */ LOGP(DCS, LOGL_INFO, "Going to camping (normal) ARFCN %s.\n", gsm_print_arfcn(cs->arfcn)); - hack = 1; - gsm322_sync_to_cell(cs); cs->si = cs->list[cs->arfci].sysinfo; + if (!cs->si) { + printf("No SI when leaving idle, please fix!\n"); + exit(0L); + } + cs->sync_retries = SYNC_RETRIES; + gsm322_sync_to_cell(cs, NULL, 1); return 0; } @@ -3046,17 +3664,27 @@ static int gsm322_c_conn_mode_2(struct osmocom_ms *ms, struct msgb *msg) struct gsm322_cellsel *cs = &ms->cellsel; /* check for error */ - if (!cs->selected) - return -EINVAL; + if (!cs->selected) { + LOGP(DCS, LOGL_INFO, "No cell selected, please fix!\n"); + exit(0L); + } cs->arfcn = cs->sel_arfcn; cs->arfci = arfcn2index(cs->arfcn); + stop_cs_timer(cs); + + new_c_state(cs, GSM322_CONNECTED_MODE_2); + /* be sure to go to current camping frequency on return */ LOGP(DCS, LOGL_INFO, "Going to camping (any cell) ARFCN %s.\n", gsm_print_arfcn(cs->arfcn)); - hack = 1; - gsm322_sync_to_cell(cs); cs->si = cs->list[cs->arfci].sysinfo; + if (!cs->si) { + printf("No SI when leaving idle, please fix!\n"); + exit(0L); + } + cs->sync_retries = SYNC_RETRIES; + gsm322_sync_to_cell(cs, NULL, 1); return 0; } @@ -3120,7 +3748,7 @@ static struct plmnastatelist { GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_first_plmn}, {SBIT(GSM322_A1_TRYING_RPLMN) | SBIT(GSM322_A3_TRYING_PLMN), - GSM322_EVENT_REG_SUCCESS, gsm322_a_indicate_selected}, + GSM322_EVENT_REG_SUCCESS, gsm322_a_go_on_plmn}, {SBIT(GSM322_A2_ON_PLMN), GSM322_EVENT_ROAMING_NA, gsm322_a_roaming_na}, @@ -3152,9 +3780,6 @@ static struct plmnastatelist { {SBIT(GSM322_A4_WAIT_FOR_PLMN), GSM322_EVENT_PLMN_AVAIL, gsm322_a_plmn_avail}, - {SBIT(GSM322_A4_WAIT_FOR_PLMN), - GSM322_EVENT_PLMN_SEARCH_END, gsm322_a_plmn_avail}, - {ALL_STATES, GSM322_EVENT_SEL_MANUAL, gsm322_a_sel_manual}, @@ -3175,7 +3800,7 @@ static int gsm322_a_event(struct osmocom_ms *ms, struct msgb *msg) LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for automatic PLMN " "selection in state '%s'\n", ms->name, get_event_name(msg_type), - plmn_a_state_names[plmn->state]); + get_a_state_name(plmn->state)); /* find function for current state and message */ for (i = 0; i < PLMNASLLEN; i++) if ((msg_type == plmnastatelist[i].type) @@ -3226,11 +3851,15 @@ static struct plmnmstatelist { GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns}, {SBIT(GSM322_M1_TRYING_RPLMN), - GSM322_EVENT_REG_SUCCESS, gsm322_m_indicate_selected}, + GSM322_EVENT_REG_SUCCESS, gsm322_m_go_on_plmn}, {SBIT(GSM322_M2_ON_PLMN), GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns}, + /* undocumented case, where we loose coverage */ + {SBIT(GSM322_M2_ON_PLMN), + GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns}, + {SBIT(GSM322_M1_TRYING_RPLMN) | SBIT(GSM322_M2_ON_PLMN) | SBIT(GSM322_M4_TRYING_PLMN), GSM322_EVENT_INVALID_SIM, gsm322_m_sim_removed}, @@ -3241,12 +3870,18 @@ static struct plmnmstatelist { {SBIT(GSM322_M3_NOT_ON_PLMN), GSM322_EVENT_PLMN_AVAIL, gsm322_m_plmn_avail}, - {SBIT(GSM322_M3_NOT_ON_PLMN), + /* choose plmn is only specified when 'not on PLMN', but it makes + * sense to select cell from other states too. */ + {SBIT(GSM322_M3_NOT_ON_PLMN) | SBIT(GSM322_M2_ON_PLMN) | + SBIT(GSM322_M1_TRYING_RPLMN) | SBIT(GSM322_M4_TRYING_PLMN), GSM322_EVENT_CHOOSE_PLMN, gsm322_m_choose_plmn}, {SBIT(GSM322_M4_TRYING_PLMN), GSM322_EVENT_REG_SUCCESS, gsm322_m_go_on_plmn}, + /* we also display available PLMNs after trying to register. + * this is not standard. we need that so the user knows + * that registration failed, and the user can select a new network. */ {SBIT(GSM322_M4_TRYING_PLMN), GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns}, @@ -3276,7 +3911,7 @@ static int gsm322_m_event(struct osmocom_ms *ms, struct msgb *msg) LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for manual PLMN selection " "in state '%s'\n", ms->name, get_event_name(msg_type), - plmn_m_state_names[plmn->state]); + get_m_state_name(plmn->state)); /* find function for current state and message */ for (i = 0; i < PLMNMSLLEN; i++) if ((msg_type == plmnmstatelist[i].type) @@ -3298,7 +3933,7 @@ int gsm322_plmn_dequeue(struct osmocom_ms *ms) struct gsm322_plmn *plmn = &ms->plmn; struct msgb *msg; int work = 0; - + while ((msg = msgb_dequeue(&plmn->event_queue))) { /* send event to PLMN select process */ if (ms->settings.plmn_mode == PLMN_MODE_AUTO) @@ -3308,7 +3943,7 @@ int gsm322_plmn_dequeue(struct osmocom_ms *ms) msgb_free(msg); work = 1; /* work done */ } - + return work; } @@ -3322,7 +3957,7 @@ static struct cellselstatelist { GSM322_EVENT_SWITCH_ON, gsm322_c_switch_on}, {ALL_STATES, - GSM322_EVENT_SIM_REMOVE, gsm322_c_any_cell_sel}, + GSM322_EVENT_SIM_REMOVE, gsm322_c_sim_remove}, {ALL_STATES, GSM322_EVENT_NEW_PLMN, gsm322_c_new_plmn}, @@ -3340,7 +3975,7 @@ static struct cellselstatelist { {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C6_ANY_CELL_SEL) | SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) | - SBIT(GSM322_C0_NULL), + SBIT(GSM322_C0_NULL) /* after search */, GSM322_EVENT_NO_CELL_FOUND, gsm322_c_any_cell_sel}, {SBIT(GSM322_C2_STORED_CELL_SEL) | SBIT(GSM322_C5_CHOOSE_CELL) | @@ -3353,10 +3988,10 @@ static struct cellselstatelist { {SBIT(GSM322_C7_CAMPED_ANY_CELL), GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_2}, - {SBIT(GSM322_C3_CAMPED_NORMALLY), + {SBIT(GSM322_CONNECTED_MODE_1), GSM322_EVENT_RET_IDLE, gsm322_c_choose_cell}, - {SBIT(GSM322_C7_CAMPED_ANY_CELL), + {SBIT(GSM322_CONNECTED_MODE_2), GSM322_EVENT_RET_IDLE, gsm322_c_choose_any_cell}, {SBIT(GSM322_C3_CAMPED_NORMALLY), @@ -3371,7 +4006,8 @@ static struct cellselstatelist { {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) | SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL) | SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) | - SBIT(GSM322_C6_ANY_CELL_SEL) | SBIT(GSM322_PLMN_SEARCH), + SBIT(GSM322_C6_ANY_CELL_SEL) | SBIT(GSM322_ANY_SEARCH) | + SBIT(GSM322_PLMN_SEARCH) | SBIT(GSM322_HPLMN_SEARCH) , GSM322_EVENT_SYSINFO, gsm322_c_scan_sysinfo_bcch}, {SBIT(GSM322_C3_CAMPED_NORMALLY) | SBIT(GSM322_C7_CAMPED_ANY_CELL), @@ -3395,14 +4031,16 @@ int gsm322_c_event(struct osmocom_ms *ms, struct msgb *msg) if (msg_type != GSM322_EVENT_SYSINFO) LOGP(DCS, LOGL_INFO, "(ms %s) Event '%s' for Cell selection " "in state '%s'\n", ms->name, get_event_name(msg_type), - cs_state_names[cs->state]); + get_cs_state_name(cs->state)); /* find function for current state and message */ for (i = 0; i < CELLSELSLLEN; i++) if ((msg_type == cellselstatelist[i].type) && ((1 << cs->state) & cellselstatelist[i].states)) break; if (i == CELLSELSLLEN) { - LOGP(DCS, LOGL_NOTICE, "Event unhandled at this state.\n"); + if (msg_type != GSM322_EVENT_SYSINFO) + LOGP(DCS, LOGL_NOTICE, "Event unhandled at this state." + "\n"); return 0; } @@ -3417,18 +4055,762 @@ int gsm322_cs_dequeue(struct osmocom_ms *ms) struct gsm322_cellsel *cs = &ms->cellsel; struct msgb *msg; int work = 0; - + while ((msg = msgb_dequeue(&cs->event_queue))) { /* send event to cell selection process */ gsm322_c_event(ms, msg); msgb_free(msg); work = 1; /* work done */ } - + return work; } /* + * neighbour cell measurement process in idle mode + */ + +static struct gsm322_neighbour *gsm322_nb_alloc(struct gsm322_cellsel *cs, + uint16_t arfcn) +{ + struct gsm322_neighbour *nb; + time_t now; + + time(&now); + + nb = talloc_zero(l23_ctx, struct gsm322_neighbour); + if (!nb) + return 0; + + nb->cs = cs; + nb->arfcn = arfcn; + nb->rla_c_dbm = -128; + nb->created = now; + llist_add_tail(&nb->entry, &cs->nb_list); + + return nb; +} + +static void gsm322_nb_free(struct gsm322_neighbour *nb) +{ + llist_del(&nb->entry); + talloc_free(nb); +} + +/* check and calculate reselection criterion for all 6 neighbour cells and + * return, if cell reselection has to be triggered */ +static int gsm322_nb_check(struct osmocom_ms *ms, int any) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm_settings *set = &ms->settings; + struct gsm48_sysinfo *s; + int i = 0, reselect = 0; + uint16_t acc_class; + int band, class; + struct gsm322_neighbour *nb; + time_t now; + char arfcn_text[10]; + + time(&now); + + /* set out access class depending on the cell selection type */ + if (any) { + acc_class = (subscr->acc_class | 0x0400); /* add emergency */ + LOGP(DNB, LOGL_DEBUG, "Re-select using access class with " + "Emergency class.\n"); + } else { + acc_class = subscr->acc_class; + LOGP(DNB, LOGL_DEBUG, "Re-select using access class.\n"); + } + + if (ms->rrlayer.monitor) { + vty_notify(ms, "MON: cell ARFCN LAC C1 C2 CRH RLA_C " + "bargraph\n"); + snprintf(arfcn_text, 10, "%s ", + gsm_print_arfcn(cs->sel_arfcn)); + arfcn_text[9] = '\0'; + vty_notify(ms, "MON: serving %s 0x%04x %3d %3d %4d " + "%s\n", arfcn_text, cs->sel_lac, cs->c1, cs->c2, + cs->rla_c_dbm, bargraph(cs->rla_c_dbm / 2, -55, -24)); + } + + /* loop through all neighbour cells and select best cell */ + llist_for_each_entry(nb, &cs->nb_list, entry) { + LOGP(DNB, LOGL_INFO, "Checking cell of ARFCN %s for cell " + "re-selection.\n", gsm_print_arfcn(nb->arfcn)); + s = cs->list[arfcn2index(nb->arfcn)].sysinfo; + nb->checked_for_resel = 0; + nb->suitable_allowable = 0; + nb->c12_valid = 1; + nb->prio_low = 0; + + if (nb->state == GSM322_NB_NOT_SUP) { + LOGP(DNB, LOGL_INFO, "Skip cell: ARFCN not supported." + "\n"); + if (ms->rrlayer.monitor) { + snprintf(arfcn_text, 10, "%s ", + gsm_print_arfcn(nb->arfcn)); + arfcn_text[9] = '\0'; + vty_notify(ms, "MON: nb %2d %s ARFCN not " + "supported\n", i + 1, arfcn_text); + } + goto cont; + } + /* check if we have successfully read BCCH */ + if (!s || nb->state != GSM322_NB_SYSINFO) { + LOGP(DNB, LOGL_INFO, "Skip cell: There are no system " + "informations available.\n"); + if (ms->rrlayer.monitor) { + snprintf(arfcn_text, 10, "%s ", + gsm_print_arfcn(nb->arfcn)); + arfcn_text[9] = '\0'; + vty_notify(ms, "MON: nb %2d %s " + " %4d %s\n", + i + 1, arfcn_text, nb->rla_c_dbm, + bargraph(nb->rla_c_dbm / 2, -55, -24)); + } + goto cont; + } + + /* get prio */ + if (s->sp && s->sp_cbq) + nb->prio_low = 1; + + /* get C1 & C2 */ + band = gsm_arfcn2band(nb->arfcn); + class = class_of_band(ms, band); + nb->c1 = calculate_c1(DNB, nb->rla_c_dbm, s->rxlev_acc_min_db, + ms_pwr_dbm(band, s->ms_txpwr_max_cch), + ms_class_gmsk_dbm(band, class)); + nb->c2 = calculate_c2(nb->c1, 0, + (cs->last_serving_valid + && cs->last_serving_arfcn == nb->arfcn), + s->sp, s->sp_cro, now - nb->created, s->sp_pt, + s->sp_to); + nb->c12_valid = 1; + + /* calculate CRH depending on LAI */ + if (cs->sel_mcc == s->mcc && cs->sel_mnc == s->mnc + && cs->sel_lac == s->lac) { + LOGP(DNB, LOGL_INFO, "-> Cell of is in the same LA, " + "so CRH = 0\n"); + nb->crh = 0; + } else if (any) { + LOGP(DNB, LOGL_INFO, "-> Cell of is in a different LA, " + "but service is limited, so CRH = 0\n"); + nb->crh = 0; + } else { + nb->crh = s->cell_resel_hyst_db; + LOGP(DNB, LOGL_INFO, "-> Cell of is in a different LA, " + "and service is normal, so CRH = %d\n", + nb->crh); + } + + if (ms->rrlayer.monitor) { + snprintf(arfcn_text, 10, "%s ", + gsm_print_arfcn(nb->arfcn)); + arfcn_text[9] = '\0'; + vty_notify(ms, "MON: nb %2d %s 0x%04x %3d %3d %2d" + " %4d %s\n", i + 1, arfcn_text, s->lac, + nb->c1, nb->c2, nb->crh, nb->rla_c_dbm, + bargraph(nb->rla_c_dbm / 2, -55, -24)); + } + + /* if cell is barred and we don't override */ + if (s->cell_barr && !(s->sp && s->sp_cbq)) { + LOGP(DNB, LOGL_INFO, "Skip cell: Cell is barred.\n"); + goto cont; + } + + /* if we have no access to the cell and we don't override */ + if (!subscr->acc_barr + && !(acc_class & (s->class_barr ^ 0xffff))) { + LOGP(DNB, LOGL_INFO, "Skip cell: Class is " + "barred for our access. (access=%04x " + "barred=%04x)\n", acc_class, s->class_barr); + goto cont; + } + + /* check if LA is forbidden */ + if (any && gsm322_is_forbidden_la(ms, s->mcc, s->mnc, s->lac)) { + LOGP(DNB, LOGL_INFO, "Skip cell: Cell has " + "forbidden LA.\n"); + goto cont; + } + + /* check if we have same PLMN */ + if (!any && (cs->sel_mcc != s->mcc || cs->sel_mnc != s->mnc)) { + LOGP(DNB, LOGL_INFO, "Skip cell: PLMN of cell " + "does not match target PLMN. (cell: mcc=%s " + "mnc=%s)\n", gsm_print_mcc(s->mcc), + gsm_print_mnc(s->mnc)); + goto cont; + } + + /* check criterion C1 */ + if (nb->c1 < 0) { + LOGP(DNB, LOGL_INFO, "Skip cell: C1 criterion " + " (>0) not met. (C1 = %d)\n", nb->c1); + goto cont; + } + + /* we can use this cell, if it is better */ + nb->suitable_allowable = 1; + + /* check priority */ + if (!cs->prio_low && nb->prio_low) { + LOGP(DNB, LOGL_INFO, "Skip cell: cell has low " + "priority, but serving cell has normal " + "prio.\n"); + goto cont; + } + if (cs->prio_low && !nb->prio_low) { + LOGP(DNB, LOGL_INFO, "Found cell: cell has normal " + "priority, but serving cell has low prio.\n"); + reselect = 1; + goto cont; + } + + /* find better cell */ + if (nb->c2 - nb->crh > cs->c2) { + LOGP(DNB, LOGL_INFO, "Found cell: cell is better " + "than serving cell.\n"); + reselect = 1; + goto cont; + } + +cont: + if (++i == GSM58_NB_NUMBER) + break; + } + + if (!i) { + if (ms->rrlayer.monitor) + vty_notify(ms, "MON: no neighbour cells\n"); + } + + if (cs->resel_when + GSM58_RESEL_THRESHOLD >= now) { + LOGP(DNB, LOGL_INFO, "Found better neighbour cell, but " + "reselection threshold not reached.\n"); + reselect = 0; + } + + if (reselect && set->stick) { + LOGP(DNB, LOGL_INFO, "Don't trigger cell re-selection, because " + "we stick to serving cell.\n"); + reselect = 0; + } + + return reselect; +} + +/* select a suitable and allowable cell */ +static int gsm322_nb_scan(struct osmocom_ms *ms) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm_settings *set = &ms->settings; + int i = 0; + struct gsm322_neighbour *nb, *best_nb_low = NULL, *best_nb_normal = 0; + int16_t best_low = -32768, best_normal = -32768; + + if (set->stick) { + LOGP(DCS, LOGL_DEBUG, "Do not re-select cell, because we stick " + " to a cell.\n"); + goto no_cell_found; + } + + if (!cs->c12_valid) { + LOGP(DCS, LOGL_DEBUG, "Do not re-select cell, because there " + " are no valid C1 and C2.\n"); + goto no_cell_found; + } + + /* loop through all neighbour cells and select best cell */ + llist_for_each_entry(nb, &cs->nb_list, entry) { + LOGP(DCS, LOGL_INFO, "Checking cell with ARFCN %s for cell " + "re-selection. (C2 = %d)\n", gsm_print_arfcn(nb->arfcn), + nb->c2); + /* track which cells have been checked do far */ + if (nb->checked_for_resel) { + LOGP(DCS, LOGL_INFO, "Skip cell: alredy tried to " + "select.\n"); + goto cont; + } + + /* check if we can use this cell */ + if (!nb->suitable_allowable) { + LOGP(DCS, LOGL_INFO, "Skip cell: not suitable and/or " + "allowable.\n"); + goto cont; + } + + /* check if cell is "better" */ + if (nb->prio_low) { + if (nb->c2 - nb->crh > best_low) { + best_low = nb->c2 - nb->crh; + best_nb_low = nb; + } + } else { + if (nb->c2 - nb->crh > best_normal) { + best_normal = nb->c2 - nb->crh; + best_nb_normal = nb; + } + } + +cont: + if (++i == GSM58_NB_NUMBER) + break; + } + + nb = NULL; + if (best_nb_normal) { + nb = best_nb_normal; + LOGP(DCS, LOGL_INFO, "Best neighbour cell with ARFCN %s " + "selected. (normal priority)\n", + gsm_print_arfcn(nb->arfcn)); + } + if (best_nb_low) { + nb = best_nb_low; + LOGP(DCS, LOGL_INFO, "Best neighbour cell with ARFCN %s " + "selected. (low priority)\n", + gsm_print_arfcn(nb->arfcn)); + } + if (!nb) { + struct msgb *nmsg; + + LOGP(DCS, LOGL_INFO, "No (more) acceptable neighbour cell " + "available\n"); + +no_cell_found: + /* Tell cell selection process to handle "no cell found". */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return 0; + } + nb->checked_for_resel = 1; + + /* NOTE: We might already have system information from previous + * scan. But we need recent informations, so we scan again! + */ + + /* Tune to frequency for a while, to receive broadcasts. */ + cs->arfcn = nb->arfcn; + cs->arfci = arfcn2index(cs->arfcn); + LOGP(DCS, LOGL_DEBUG, "Scanning ARFCN %s of neighbour " + "cell during cell reselection.\n", gsm_print_arfcn(cs->arfcn)); + /* Allocate/clean system information. */ + cs->list[cs->arfci].flags &= ~GSM322_CS_FLAG_SYSINFO; + if (cs->list[cs->arfci].sysinfo) + memset(cs->list[cs->arfci].sysinfo, 0, + sizeof(struct gsm48_sysinfo)); + else + cs->list[cs->arfci].sysinfo = talloc_zero(l23_ctx, + struct gsm48_sysinfo); + if (!cs->list[cs->arfci].sysinfo) + exit(-ENOMEM); + cs->si = cs->list[cs->arfci].sysinfo; + cs->sync_retries = SYNC_RETRIES; + return gsm322_sync_to_cell(cs, NULL, 0); +} + +/* start/modify measurement process with the current list of neighbour cells. + * only do that if: 1. we are camping 2. we are on serving cell */ +static int gsm322_nb_start(struct osmocom_ms *ms, int synced) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_sysinfo *s = &cs->sel_si; + struct gsm322_neighbour *nb, *nb2; + int i, num; + uint8_t map[128]; + uint16_t nc[32]; + uint8_t changed = 0; + int refer_pcs, index; + uint16_t arfcn; + + if (cs->ms->settings.no_neighbour) + return 0; + + if (synced) + cs->nb_meas_set = 0; + + refer_pcs = gsm_refer_pcs(cs->sel_arfcn, s); + + /* remove all neighbours that are not in list anymore */ + memset(map, 0, sizeof(map)); + llist_for_each_entry_safe(nb, nb2, &cs->nb_list, entry) { + i = nb->arfcn & 1023; + map[i >> 3] |= (1 << (i & 7)); +#ifndef TEST_INCLUDE_SERV + if (!(s->freq[i].mask & FREQ_TYPE_NCELL)) { +#else + if (!(s->freq[i].mask & (FREQ_TYPE_NCELL | FREQ_TYPE_SERV))) { +#endif + LOGP(DNB, LOGL_INFO, "Removing neighbour cell %s from " + "list.\n", gsm_print_arfcn(nb->arfcn)); + gsm322_nb_free(nb); + changed = 1; + continue; + } +#ifndef TEST_INCLUDE_SERV + if (nb->arfcn == cs->sel_arfcn) { + LOGP(DNB, LOGL_INFO, "Removing serving cell %s (former " + "neighbour cell).\n", + gsm_print_arfcn(nb->arfcn)); + gsm322_nb_free(nb); + changed = 1; + continue; + } +#endif + } + + /* add missing entries to list */ + for (i = 0; i <= 1023; i++) { +#ifndef TEST_INCLUDE_SERV + if ((s->freq[i].mask & FREQ_TYPE_NCELL) && + !(map[i >> 3] & (1 << (i & 7)))) { +#else + if ((s->freq[i].mask & (FREQ_TYPE_NCELL | FREQ_TYPE_SERV)) && + !(map[i >> 3] & (1 << (i & 7)))) { +#endif + index = i; + if (refer_pcs && i >= 512 && i <= 810) + index = i-512+1024; + arfcn = index2arfcn(index); +#ifndef TEST_INCLUDE_SERV + if (arfcn == cs->sel_arfcn) { + LOGP(DNB, LOGL_INFO, "Omitting serving cell %s." + "\n", gsm_print_arfcn(cs->arfcn)); + continue; + } +#endif + nb = gsm322_nb_alloc(cs, arfcn); + LOGP(DNB, LOGL_INFO, "Adding neighbour cell %s to " + "list.\n", gsm_print_arfcn(nb->arfcn)); + if (!(cs->list[index].flags & GSM322_CS_FLAG_SUPPORT)) + nb->state = GSM322_NB_NOT_SUP; + changed = 1; + } + } + + /* if nothing has changed, we are done */ + if (!changed && cs->nb_meas_set) + return 0; + + /* start neigbour cell measurement task */ + num = 0; + llist_for_each_entry(nb, &cs->nb_list, entry) { + if (nb->state == GSM322_NB_NOT_SUP) + continue; + /* it should not happen that there are more than 32 nb-cells */ + if (num == 32) + break; + nc[num] = nb->arfcn; + num++; + } + LOGP(DNB, LOGL_INFO, "Sending list of neighbour cells to layer1.\n"); + l1ctl_tx_neigh_pm_req(ms, num, nc); + cs->nb_meas_set = 1; + + return 1; +} + + +/* a complete set of measurements are received, calculate the RLA_C, sort */ +static int gsm322_nb_trigger_event(struct gsm322_cellsel *cs) +{ + struct osmocom_ms *ms = cs->ms; + struct gsm322_neighbour *nb, *nb_sync = NULL, *nb_again = NULL; + int i = 0; + time_t now; + + time(&now); + + /* check the list for reading neighbour cell's BCCH */ + llist_for_each_entry(nb, &cs->nb_list, entry) { + if (nb->rla_c_dbm >= cs->ms->settings.min_rxlev_db) { + /* select the strongest unsynced cell */ + if (nb->state == GSM322_NB_RLA_C) { + nb_sync = nb; + break; + } +#if 0 +if (nb->state == GSM322_NB_SYSINFO) { +printf("%d time to sync again: %u\n", nb->arfcn, now + GSM58_READ_AGAIN - nb->when); +} +#endif + /* select the strongest cell to be read/try again */ + if (!nb_again) { + if ((nb->state == GSM322_NB_NO_SYNC + || nb->state == GSM322_NB_NO_BCCH) + && nb->when + GSM58_TRY_AGAIN <= now) + nb_again = nb; + else + if (nb->state == GSM322_NB_SYSINFO + && nb->when + GSM58_READ_AGAIN <= now) + nb_again = nb; + } + } + if (++i == GSM58_NB_NUMBER) + break; + } + + /* trigger sync to neighbour cell, priorize the untested cell */ + if (nb_sync || nb_again) { + if (nb_sync) { + nb = nb_sync; + cs->arfcn = nb->arfcn; + cs->arfci = arfcn2index(cs->arfcn); + LOGP(DNB, LOGL_INFO, "Syncing to new neighbour cell " + "%s.\n", gsm_print_arfcn(cs->arfcn)); + } else { + nb = nb_again; + cs->arfcn = nb->arfcn; + cs->arfci = arfcn2index(cs->arfcn); + LOGP(DNB, LOGL_INFO, "Syncing again to neighbour cell " + "%s after timerout.\n", + gsm_print_arfcn(cs->arfcn)); + } + /* Allocate/clean system information. */ + cs->list[cs->arfci].flags &= ~GSM322_CS_FLAG_SYSINFO; + if (cs->list[cs->arfci].sysinfo) + memset(cs->list[cs->arfci].sysinfo, 0, + sizeof(struct gsm48_sysinfo)); + else + cs->list[cs->arfci].sysinfo = talloc_zero(l23_ctx, + struct gsm48_sysinfo); + if (!cs->list[cs->arfci].sysinfo) + exit(-ENOMEM); + cs->si = cs->list[cs->arfci].sysinfo; + cs->sync_retries = SYNC_RETRIES; + return gsm322_sync_to_cell(cs, nb, 0); + } + + if (gsm322_nb_check(ms, (cs->state == GSM322_C7_CAMPED_ANY_CELL)) > 0) { + struct msgb *nmsg; + + LOGP(DNB, LOGL_INFO, "Better neighbour cell triggers cell " + "reselection.\n"); + + if (ms->rrlayer.monitor) + vty_notify(ms, "MON: trigger cell re-selection: " + "better cell\n"); + + cs->resel_when = now; + + /* unset selected cell */ + gsm322_unselect_cell(cs); + + nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL); + if (!nmsg) + return -ENOMEM; + gsm322_c_event(ms, nmsg); + msgb_free(nmsg); + return 0; + } + + if (cs->neighbour) { + cs->arfcn = cs->sel_arfcn; + cs->arfci = arfcn2index(cs->arfcn); + cs->si = cs->list[cs->arfci].sysinfo; + if (!cs->si) { + printf("No SI after neighbour scan, please fix!\n"); + exit(0L); + } + LOGP(DNB, LOGL_INFO, "Syncing back to serving cell\n"); + cs->sync_retries = SYNC_RETRIES_SERVING; + return gsm322_sync_to_cell(cs, NULL, 0); + } + + /* do nothing */ + return 0; +} + + +/* we (successfully) synced to a neighbour */ +static int gsm322_nb_synced(struct gsm322_cellsel *cs, int yes) +{ + time_t now; + + LOGP(DNB, LOGL_INFO, "%s to neighbour cell %d.\n", + (yes) ? "Synced" : "Failed to sync", cs->arfcn); + + if (yes) { + start_cs_timer(cs, GSM322_NB_TIMEOUT, 0); + return 0; + } + + cs->neighbour->state = GSM322_NB_NO_SYNC; + time(&now); + cs->neighbour->when = now; + + return gsm322_nb_trigger_event(cs); +} + +/* we (successfully) read the neighbour */ +static int gsm322_nb_read(struct gsm322_cellsel *cs, int yes) +{ + time_t now; + + LOGP(DNB, LOGL_INFO, "%s from neighbour cell %d (rxlev %s).\n", + (yes) ? "Read" : "Failed to read", + cs->arfcn, gsm_print_rxlev(cs->list[cs->arfci].rxlev)); + + cs->neighbour->state = (yes) ? GSM322_NB_SYSINFO : GSM322_NB_NO_BCCH; + time(&now); + cs->neighbour->when = now; + + return gsm322_nb_trigger_event(cs); +} + +/* a complete set of measurements are received, calculate the RLA_C, sort */ +static int gsm322_nb_new_rxlev(struct gsm322_cellsel *cs) +{ + struct gsm322_neighbour *nb, *strongest_nb; + int i = 0; + int8_t strongest; + struct llist_head sorted; + struct llist_head *lh, *lh2; + struct gsm48_sysinfo *s = &cs->sel_si; + int band = gsm_arfcn2band(cs->arfcn); + int class = class_of_band(cs->ms, band); + + + /* calculate the RAL_C of serving cell */ + if (cs->rxlev_count) { + cs->rla_c_dbm = (cs->rxlev_dbm + (cs->rxlev_count / 2)) + / cs->rxlev_count; + cs->rxlev_dbm = 0; + cs->rxlev_count = 0; + } + + LOGP(DNB, LOGL_INFO, "RLA_C of serving cell: %d\n", cs->rla_c_dbm); + + /* calculate C1 criterion, SI 3 carries complete neighbour cell info */ + cs->prio_low = 0; + if (s && (s->si3 || s->si4)) { + cs->c1 = calculate_c1(DNB, cs->rla_c_dbm, s->rxlev_acc_min_db, + ms_pwr_dbm(band, s->ms_txpwr_max_cch), + ms_class_gmsk_dbm(band, class)); + cs->c2 = calculate_c2(cs->c1, 1, 0, s->sp, s->sp_cro, 0, s->sp_pt, s->sp_to); + cs->c12_valid = 1; + + if (s->sp && s->sp_cbq) + cs->prio_low = 1; + } + + /* calculate the RAL_C of neighbours */ + llist_for_each_entry(nb, &cs->nb_list, entry) { + if (nb->state == GSM322_NB_NOT_SUP) + continue; + /* if sysinfo is gone due to scanning, mark neighbour as + * unscanned. */ + if (nb->state == GSM322_NB_SYSINFO) { + if (!cs->list[arfcn2index(nb->arfcn)].sysinfo) { + nb->state = GSM322_NB_NO_BCCH; + nb->when = 0; + } + } + nb->rla_c_dbm = + (nb->rxlev_dbm + (nb->rxlev_count / 2)) + / nb->rxlev_count; + nb->rxlev_count = 0; + nb->rxlev_dbm = 0; + if (nb->state == GSM322_NB_NEW) + nb->state = GSM322_NB_RLA_C; + } + + /* sort the 6 strongest */ + INIT_LLIST_HEAD(&sorted); + + /* detach up to 6 of the strongest neighbour cells from list and put + * them in the "sorted" list */ + while (!llist_empty(&cs->nb_list)) { + strongest = -128; + strongest_nb = NULL; + llist_for_each_entry(nb, &cs->nb_list, entry) { + if (nb->state == GSM322_NB_NOT_SUP) + continue; + if (nb->rla_c_dbm > strongest) { + strongest = nb->rla_c_dbm; + strongest_nb = nb; + } + } + if (strongest_nb == NULL) /* this should not happen */ + break; + LOGP(DNB, LOGL_INFO, "#%d ARFCN=%d RLA_C=%d\n", + i+1, strongest_nb->arfcn, strongest_nb->rla_c_dbm); + llist_del(&strongest_nb->entry); + llist_add(&strongest_nb->entry, &sorted); + if (++i == GSM58_NB_NUMBER) + break; + } + + /* take the sorted list and attat it to the head of the neighbour cell + * list */ + llist_for_each_safe(lh, lh2, &sorted) { + llist_del(lh); + llist_add(lh, &cs->nb_list); + } + + return gsm322_nb_trigger_event(cs); +} + +/* accumulate the measurement results and check if there is a complete set for + * all neighbour cells received. */ +static int gsm322_nb_meas_ind(struct osmocom_ms *ms, uint16_t arfcn, + uint8_t rx_lev) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm322_neighbour *nb; + int enough_results = 1, result = 0; + + llist_for_each_entry(nb, &cs->nb_list, entry) { + if (nb->state == GSM322_NB_NOT_SUP) + continue; + if (arfcn != nb->arfcn) { + if (nb->rxlev_count < RLA_C_NUM) + enough_results = 0; + continue; + } + nb->rxlev_dbm += rx_lev - 110; + nb->rxlev_count++; + LOGP(DNB, LOGL_INFO, "Measurement result for ARFCN %s: %d\n", + gsm_print_arfcn(arfcn), rx_lev - 110); + + if (nb->rxlev_count < RLA_C_NUM) + enough_results = 0; + + result = 1; + } + + if (!result) + LOGP(DNB, LOGL_INFO, "Measurement result for ARFCN %s not " + "requested. (not a bug)\n", gsm_print_arfcn(arfcn)); + + if (enough_results) + return gsm322_nb_new_rxlev(cs); + + return 0; +} + +int gsm322_meas(struct osmocom_ms *ms, uint8_t rx_lev) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + + if (cs->neighbour) + return -EINVAL; + + cs->rxlev_dbm += rx_lev - 110; + cs->rxlev_count++; + + return 0; +} + +/* * dump lists */ @@ -3437,11 +4819,11 @@ int gsm322_dump_sorted_plmn(struct osmocom_ms *ms) struct gsm322_plmn *plmn = &ms->plmn; struct gsm322_plmn_list *temp; - printf("MCC |MNC |allowed|rx-lev\n"); - printf("-------+-------+-------+-------\n"); + LOGP(DPLMN, LOGL_INFO, "MCC |MNC |allowed|rx-lev\n"); + LOGP(DPLMN, LOGL_INFO, "-------+-------+-------+-------\n"); llist_for_each_entry(temp, &plmn->sorted_plmn, entry) { - printf("%s |%s%s |%s |%s\n", gsm_print_mcc(temp->mcc), - gsm_print_mnc(temp->mnc), + LOGP(DPLMN, LOGL_INFO, "%s |%s%s |%s |%s\n", + gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc), ((temp->mnc & 0x00f) == 0x00f) ? " ":"", (temp->cause) ? "no ":"yes", gsm_print_rxlev(temp->rxlev)); @@ -3470,11 +4852,14 @@ int gsm322_dump_cs_list(struct gsm322_cellsel *cs, uint8_t flags, print(priv, "%4dDCS|", i); else print(priv, "%4d |", i); - if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)) { + if (s->mcc) { print(priv, "%s |%s%s |", gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc), ((s->mnc & 0x00f) == 0x00f) ? " ":""); print(priv, "0x%04x |0x%04x |", s->lac, s->cell_id); + } else + print(priv, "n/a |n/a |n/a |n/a |"); + if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)) { if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD)) print(priv, "yes |"); else @@ -3487,12 +4872,14 @@ int gsm322_dump_cs_list(struct gsm322_cellsel *cs, uint8_t flags, else print(priv, "normal |"); } + } else + print(priv, "n/a |n/a |"); + if (s->si3 || s->si4) print(priv, "%4d |%4d |%s\n", s->rxlev_acc_min_db, s->ms_txpwr_max_cch, gsm_print_rxlev(cs->list[i].rxlev)); - } else - print(priv, "n/a |n/a |n/a |n/a |n/a |" - "n/a |n/a |n/a\n"); + else + print(priv, "n/a |n/a |n/a\n"); } print(priv, "\n"); @@ -3540,6 +4927,83 @@ int gsm322_dump_ba_list(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc, return 0; } +int gsm322_dump_nb_list(struct gsm322_cellsel *cs, + void (*print)(void *, const char *, ...), void *priv) +{ + struct gsm48_sysinfo *s; + struct gsm322_neighbour *nb; + int i = 0; + + if (!cs->selected) { + print(priv, "No serving cell selected (yet).\n"); + return 0; + } + print(priv, "Serving cell:\n\n"); + print(priv, "ARFCN=%s ", gsm_print_arfcn(cs->sel_arfcn)); + print(priv, "RLA_C=%s ", gsm_print_rxlev(cs->rla_c_dbm + 110)); + if (cs->c12_valid) + print(priv, "C1=%d C2=%d ", cs->c1, cs->c1); + else + print(priv, "C1 - C2 - "); + print(priv, "LAC=0x%04x\n\n", (cs->selected) ? cs->sel_si.lac : 0); + + print(priv, "Neighbour cells:\n\n"); + llist_for_each_entry(nb, &cs->nb_list, entry) { + if (i == 0) { + print(priv, "# |ARFCN |RLA_C |C1 |C2 |" + "CRH |prio |LAC |cell ID|usable |" + "state\n"); + print(priv, "----------------------------------------" + "----------------------------------------" + "-------\n"); + } else + if (i == GSM58_NB_NUMBER) + print(priv, "--- unmonitored cells: ---\n"); + i++; + if (cs->last_serving_valid + && cs->last_serving_arfcn == nb->arfcn) + print(priv, "%2d last|", i); + else + print(priv, "%2d |", i); + if ((nb->arfcn & ARFCN_PCS)) + print(priv, "%4dPCS|", nb->arfcn & 1023); + else if (i >= 512 && i <= 885) + print(priv, "%4dDCS|", nb->arfcn & 1023); + else + print(priv, "%4d |", nb->arfcn); + if (nb->state == GSM322_NB_NOT_SUP) { + print(priv, " ARFCN not supported\n"); + continue; + } + if (nb->rla_c_dbm > -128) + print(priv, "%6s |", + gsm_print_rxlev(nb->rla_c_dbm + 110)); + else + print(priv, "- |"); + if (nb->state == GSM322_NB_SYSINFO && nb->c12_valid) + print(priv, "%4d |%4d |%4d |", nb->c1, nb->c1, + nb->crh); + else + print(priv, "- |- |- |"); + s = cs->list[arfcn2index(nb->arfcn)].sysinfo; + if (nb->state == GSM322_NB_SYSINFO && s) { + print(priv, "%s |0x%04x |0x%04x |", + (nb->prio_low) ? "low ":"normal", s->lac, + s->cell_id); + } else + print(priv, "- |- |- |"); + + print(priv, "%s |", + (nb->suitable_allowable) ? "yes" : "no "); + print(priv, "%s\n", get_nb_state_name(nb->state)); + } + + if (i == 0) + print(priv, "No neighbour cells available (yet).\n"); + + return 0; +} + /* * initialization */ @@ -3566,7 +5030,6 @@ int gsm322_init(struct osmocom_ms *ms) /* set initial state */ plmn->state = 0; cs->state = 0; - ms->settings.plmn_mode = PLMN_MODE_AUTO; /* init lists */ INIT_LLIST_HEAD(&plmn->event_queue); @@ -3574,6 +5037,7 @@ int gsm322_init(struct osmocom_ms *ms) INIT_LLIST_HEAD(&plmn->sorted_plmn); INIT_LLIST_HEAD(&plmn->forbidden_la); INIT_LLIST_HEAD(&cs->ba_list); + INIT_LLIST_HEAD(&cs->nb_list); /* set supported frequencies in cell selection list */ for (i = 0; i <= 1023+299; i++) @@ -3642,12 +5106,13 @@ int gsm322_exit(struct osmocom_ms *ms) /* stop timers */ stop_cs_timer(cs); + stop_any_timer(cs); stop_plmn_timer(plmn); /* flush sysinfo */ for (i = 0; i <= 1023+299; i++) { if (cs->list[i].sysinfo) { - LOGP(DCS, LOGL_INFO, "free sysinfo ARFCN=%s\n", + LOGP(DCS, LOGL_DEBUG, "free sysinfo ARFCN=%s\n", gsm_print_arfcn(index2arfcn(i))); talloc_free(cs->list[i].sysinfo); cs->list[i].sysinfo = NULL; @@ -3695,5 +5160,8 @@ int gsm322_exit(struct osmocom_ms *ms) llist_del(lh); talloc_free(lh); } + llist_for_each_safe(lh, lh2, &cs->nb_list) + gsm322_nb_free(container_of(lh, struct gsm322_neighbour, + entry)); return 0; } diff --git a/src/host/layer23/src/mobile/gsm48_mm.c b/src/host/layer23/src/mobile/gsm48_mm.c index a276a9e4..e84d26ab 100644 --- a/src/host/layer23/src/mobile/gsm48_mm.c +++ b/src/host/layer23/src/mobile/gsm48_mm.c @@ -37,6 +37,7 @@ #include <osmocom/bb/common/l1ctl.h> #include <osmocom/bb/mobile/gsm48_cc.h> #include <osmocom/bb/mobile/app_mobile.h> +#include <osmocom/bb/mobile/vty.h> extern void *l23_ctx; @@ -107,8 +108,6 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg); * Otherwhise PLMN SEARCH is entered. * * During PLMN SEARCH NORMAL state: (4.2.2.5) - * - on expirery of T3211 or T3213: Perform location update, when back - * to NORMAL SERVICE state. * - on expirery of T3212: Perform periodic location update, when back * to NORMAL SERVICE state. * - perform IMSI detach @@ -125,7 +124,6 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg); * During NO CELL AVAILABLE state: * - reject any MM connection * - * * The NO IMSI state is entered if: * - SIM is invalid * - and cell is selected during PLMN SEARCH states @@ -133,7 +131,6 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg); * During NO IMSO state: (4.2.2.4) * - reject MM connection except for emergency calls * - * * The LIMITED SERVICE state is entered if: * - SIM is valid * - and SIM state is U3 @@ -151,6 +148,10 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg); * During LOCATION UPDATE NEEDED state: * - reject MM connection except for emergency calls * + * In all IDLE states: + * - on expirery of T3211 or T3213: Perform location update, when back + * to NORMAL SERVICE state. + * * This state is left if location update is possible and directly enter * state ATTEMPTING TO UPDATE and trigger location update. * The function gsm48_mm_loc_upd_possible() is used to check this on state @@ -546,10 +547,12 @@ static const struct value_string gsm48_mmevent_names[] = { { GSM48_MM_EVENT_TIMEOUT_T3230, "MM_EVENT_TIMEOUT_T3230" }, { GSM48_MM_EVENT_TIMEOUT_T3240, "MM_EVENT_TIMEOUT_T3240" }, { GSM48_MM_EVENT_IMSI_DETACH, "MM_EVENT_IMSI_DETACH" }, + { GSM48_MM_EVENT_POWER_OFF, "MM_EVENT_POWER_OFF" }, { GSM48_MM_EVENT_PAGING, "MM_EVENT_PAGING" }, { GSM48_MM_EVENT_AUTH_RESPONSE, "MM_EVENT_AUTH_RESPONSE" }, { GSM48_MM_EVENT_SYSINFO, "MM_EVENT_SYSINFO" }, { GSM48_MM_EVENT_USER_PLMN_SEL, "MM_EVENT_USER_PLMN_SEL" }, + { GSM48_MM_EVENT_LOST_COVERAGE, "MM_EVENT_LOST_COVERAGE" }, { 0, NULL } }; @@ -894,6 +897,9 @@ static int gsm48_mm_loc_upd_possible(struct gsm48_mmlayer *mm) /* Set new MM state, also new substate in case of MM IDLE state. */ static void new_mm_state(struct gsm48_mmlayer *mm, int state, int substate) { + struct osmocom_ms *ms = mm->ms; + struct gsm322_plmn *plmn = &ms->plmn; + /* IDLE -> IDLE */ if (mm->state == GSM48_MM_ST_MM_IDLE && state == mm->state) LOGP(DMM, LOGL_INFO, "new MM IDLE state %s -> %s\n", @@ -915,6 +921,43 @@ static void new_mm_state(struct gsm48_mmlayer *mm, int state, int substate) gsm48_mm_state_names[mm->state], gsm48_mm_state_names[state]); + /* display service on new IDLE state */ + if (state == GSM48_MM_ST_MM_IDLE + && (mm->state != GSM48_MM_ST_MM_IDLE || mm->substate != substate)) { + switch (substate) { + case GSM48_MM_SST_NORMAL_SERVICE: + vty_notify(ms, NULL); + vty_notify(ms, "On Network, normal service: %s, %s\n", + gsm_get_mcc(plmn->mcc), + gsm_get_mnc(plmn->mcc, plmn->mnc)); + break; + case GSM48_MM_SST_LIMITED_SERVICE: + vty_notify(ms, NULL); + vty_notify(ms, "Limited service, emergency calls are " + "possible.\n"); + break; + case GSM48_MM_SST_PLMN_SEARCH_NORMAL: + case GSM48_MM_SST_PLMN_SEARCH: + vty_notify(ms, NULL); + vty_notify(ms, "Searching network...\n"); + break; + case GSM48_MM_SST_NO_IMSI: + vty_notify(ms, NULL); + vty_notify(ms, "No SIM, emergency calls are " + "possible.\n"); + break; + case GSM48_MM_SST_NO_CELL_AVAIL: + vty_notify(ms, NULL); + vty_notify(ms, "No service.\n"); + break; + case GSM48_MM_SST_ATTEMPT_UPDATE: + vty_notify(ms, NULL); + vty_notify(ms, "Trying to registering with " + "network...\n"); + break; + } + } + /* remember most recent substate */ if (mm->state == GSM48_MM_ST_MM_IDLE) mm->mr_substate = mm->substate; @@ -931,35 +974,37 @@ static void new_mm_state(struct gsm48_mmlayer *mm, int state, int substate) nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_IMSI_DETACH); if (!nmsg) return; - gsm48_mmevent_msg(mm->ms, nmsg); + gsm48_mmevent_msg(ms, nmsg); } /* 4.4.2 start T3212 in MM IDLE mode if not started or has expired */ if (state == GSM48_MM_ST_MM_IDLE && (substate == GSM48_MM_SST_NORMAL_SERVICE || substate == GSM48_MM_SST_ATTEMPT_UPDATE)) { + struct gsm48_sysinfo *s = &mm->ms->cellsel.sel_si; + /* start periodic location update timer */ - if (!osmo_timer_pending(&mm->t3212)) + if (s->t3212 && !osmo_timer_pending(&mm->t3212)) { + mm->t3212_value = s->t3212; start_mm_t3212(mm, mm->t3212_value); + } /* perform pending location update */ if (mm->lupd_retry) { LOGP(DMM, LOGL_INFO, "Loc. upd. pending (type %d)\n", mm->lupd_type); mm->lupd_retry = 0; - gsm48_mm_loc_upd(mm->ms, NULL); + gsm48_mm_loc_upd(ms, NULL); /* must exit, because this function can be called * recursively */ return; } if (mm->lupd_periodic) { - struct gsm48_sysinfo *s = &mm->ms->cellsel.sel_si; - LOGP(DMM, LOGL_INFO, "Periodic loc. upd. pending " "(type %d)\n", mm->lupd_type); mm->lupd_periodic = 0; - if (s->t3212) - gsm48_mm_loc_upd_periodic(mm->ms, NULL); + if (s->t3212) /* still required? */ + gsm48_mm_loc_upd_periodic(ms, NULL); else LOGP(DMM, LOGL_INFO, "but not requred\n"); /* must exit, because this function can be called @@ -1035,6 +1080,13 @@ static int gsm48_mm_return_idle(struct osmocom_ms *ms, struct msgb *msg) struct gsm48_mmlayer *mm = &ms->mmlayer; struct gsm322_cellsel *cs = &ms->cellsel; + if (cs->state != GSM322_C3_CAMPED_NORMALLY + && cs->state != GSM322_C7_CAMPED_ANY_CELL) { + LOGP(DMM, LOGL_INFO, "Not camping, wait for CS process to " + "camp, it sends us CELL_SELECTED then.\n"); + return 0; + } + /* 4.4.4.9 start T3211 when RR is released */ if (mm->start_t3211) { LOGP(DMM, LOGL_INFO, "Starting T3211 after RR release.\n"); @@ -1083,13 +1135,30 @@ static int gsm48_mm_return_idle(struct osmocom_ms *ms, struct msgb *msg) return 0; } - /* location update allowed */ if (cs->state == GSM322_C3_CAMPED_NORMALLY) { LOGP(DMM, LOGL_INFO, "We are camping normally as returning to " "MM IDLE\n"); - new_mm_state(mm, GSM48_MM_ST_MM_IDLE, - GSM48_MM_SST_LOC_UPD_NEEDED); - } else { /* location update not allowed */ + if (gsm_subscr_is_forbidden_plmn(subscr, cs->sel_mcc, + cs->sel_mnc)) { + /* location update not allowed */ + LOGP(DMM, LOGL_INFO, "Loc. upd. not allowed PLMN.\n"); + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, + GSM48_MM_SST_LIMITED_SERVICE); + } else + if (gsm322_is_forbidden_la(ms, cs->sel_mcc, cs->sel_mnc, + cs->sel_lac)) { + /* location update not allowed */ + LOGP(DMM, LOGL_INFO, "Loc. upd. not allowed LA.\n"); + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, + GSM48_MM_SST_LIMITED_SERVICE); + } else { + /* location update allowed */ + LOGP(DMM, LOGL_INFO, "Loc. upd. allowed.\n"); + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, + GSM48_MM_SST_LOC_UPD_NEEDED); + } + } else { + /* location update not allowed */ LOGP(DMM, LOGL_INFO, "We are camping on any cell as returning " "to MM IDLE\n"); new_mm_state(mm, GSM48_MM_ST_MM_IDLE, @@ -1171,7 +1240,8 @@ static int gsm48_mm_cell_selected(struct osmocom_ms *ms, struct msgb *msg) /* PLMN mode auto and selected cell is forbidden */ if (set->plmn_mode == PLMN_MODE_AUTO - && (gsm_subscr_is_forbidden_plmn(subscr, cs->sel_mcc, cs->sel_mnc) + && (!cs->selected + || gsm_subscr_is_forbidden_plmn(subscr, cs->sel_mcc, cs->sel_mnc) || gsm322_is_forbidden_la(ms, cs->sel_mcc, cs->sel_mnc, cs->sel_lac))) { struct msgb *nmsg; @@ -1181,7 +1251,7 @@ static int gsm48_mm_cell_selected(struct osmocom_ms *ms, struct msgb *msg) GSM48_MM_SST_LIMITED_SERVICE); /* send message to PLMN search process */ - nmsg = gsm322_msgb_alloc(GSM322_EVENT_ROAMING_NA); + nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_FAILED); if (!nmsg) return -ENOMEM; gsm322_plmn_sendmsg(ms, nmsg); @@ -1189,10 +1259,13 @@ static int gsm48_mm_cell_selected(struct osmocom_ms *ms, struct msgb *msg) return 0; } - /* PLMN mode manual and selected cell not selected PLMN */ + /* PLMN mode manual and selected cell not selected PLMN. + * in M3 state the PLMN is not selected for registration. */ if (set->plmn_mode == PLMN_MODE_MANUAL - && (plmn->mcc != cs->sel_mcc - || plmn->mnc != cs->sel_mnc)) { + && (!cs->selected + || plmn->mcc != cs->sel_mcc + || plmn->mnc != cs->sel_mnc + || plmn->state == GSM322_M3_NOT_ON_PLMN)) { struct msgb *nmsg; LOGP(DMM, LOGL_INFO, "Selected cell not found.\n"); @@ -1630,8 +1703,8 @@ static int gsm48_mm_rx_auth_rej(struct osmocom_ms *ms, struct msgb *msg) nrrh->cause = GSM48_RR_CAUSE_NORMAL; gsm48_rr_downmsg(ms, nmsg); - /* CS process will trigger: return to MM IDLE / No SIM */ - return 0; + /* return to MM IDLE */ + return gsm48_mm_return_idle(ms, NULL); } return 0; @@ -1765,7 +1838,8 @@ static int gsm48_mm_imsi_detach_end(struct osmocom_ms *ms, struct msgb *msg) /* power off when MM idle */ mm->power_off_idle = 1; - return 0; + /* return to MM IDLE */ + return gsm48_mm_return_idle(ms, NULL); } /* send SIM remove event to gsm322 */ @@ -1774,8 +1848,8 @@ static int gsm48_mm_imsi_detach_end(struct osmocom_ms *ms, struct msgb *msg) return -ENOMEM; gsm322_plmn_sendmsg(ms, nmsg); - /* CS process will trigger return to MM IDLE / No SIM */ - return 0; + /* return to MM IDLE */ + return gsm48_mm_return_idle(ms, NULL); } /* start an IMSI detach in MM IDLE */ @@ -1914,8 +1988,8 @@ static int gsm48_mm_rx_abort(struct osmocom_ms *ms, struct msgb *msg) /* store LOCI on sim */ gsm_subscr_write_loci(ms); - /* CS process will trigger: return to MM IDLE / No SIM */ - return 0; + /* return to MM IDLE */ + return gsm48_mm_return_idle(ms, NULL); } return 0; @@ -2021,7 +2095,8 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg) /* (re)start only if we still require location update */ if (!mm->lupd_pending) { LOGP(DMM, LOGL_INFO, "No loc. upd. pending.\n"); - return 0; + /* use MM IDLE to selecte the idle state */ + return gsm48_mm_return_idle(ms, NULL); } /* must camp normally */ @@ -2032,12 +2107,20 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg) LOGP(DSUM, LOGL_INFO, "Location updating not possible\n"); _stop: mm->lupd_pending = 0; + +#if 0 + /* don't send message, if we got not triggered by PLMN search */ + if (!msg) + return 0; +#endif + /* send message to PLMN search process */ nmsg = gsm322_msgb_alloc(msg_type); if (!nmsg) return -ENOMEM; gsm322_plmn_sendmsg(ms, nmsg); - return 0; + /* use MM IDLE to selecte the idle state */ + return gsm48_mm_return_idle(ms, NULL); } /* deny network, if disabled */ @@ -2048,20 +2131,20 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg) "configuration\n"); gsm_subscr_add_forbidden_plmn(subscr, cs->sel_mcc, cs->sel_mnc, GSM48_REJECT_PLMN_NOT_ALLOWED); - msg_type = GSM322_EVENT_ROAMING_NA; + msg_type = GSM322_EVENT_REG_FAILED; goto _stop; } /* if LAI is forbidden, don't start */ if (gsm_subscr_is_forbidden_plmn(subscr, cs->sel_mcc, cs->sel_mnc)) { LOGP(DMM, LOGL_INFO, "Loc. upd. not allowed PLMN.\n"); - msg_type = GSM322_EVENT_ROAMING_NA; + msg_type = GSM322_EVENT_REG_FAILED; goto stop; } if (gsm322_is_forbidden_la(ms, cs->sel_mcc, cs->sel_mnc, cs->sel_lac)) { LOGP(DMM, LOGL_INFO, "Loc. upd. not allowed LA.\n"); - msg_type = GSM322_EVENT_ROAMING_NA; + msg_type = GSM322_EVENT_REG_FAILED; goto stop; } @@ -2070,7 +2153,7 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg) || (!subscr->acc_barr && !((subscr->acc_class & 0xfbff) & (s->class_barr ^ 0xffff)))) { LOGP(DMM, LOGL_INFO, "Loc. upd. no access.\n"); - msg_type = GSM322_EVENT_ROAMING_NA; + msg_type = GSM322_EVENT_REG_FAILED; goto stop; } @@ -2105,6 +2188,12 @@ static int gsm48_mm_loc_upd_normal(struct osmocom_ms *ms, struct msgb *msg) if (cs->state != GSM322_C3_CAMPED_NORMALLY) { LOGP(DMM, LOGL_INFO, "Loc. upd. not allowed.\n"); +#if 0 + /* don't send message, if we got not triggered by PLMN search */ + if (!msg) + return 0; +#endif + /* send message to PLMN search process */ nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_FAILED); if (!nmsg) @@ -2129,6 +2218,12 @@ static int gsm48_mm_loc_upd_normal(struct osmocom_ms *ms, struct msgb *msg) new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_NORMAL_SERVICE); +#if 0 + /* don't send message, if we got not triggered by PLMN search */ + if (!msg) + return 0; +#endif + /* send message to PLMN search process */ nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_SUCCESS); if (!nmsg) @@ -2465,6 +2560,8 @@ static int gsm48_mm_rel_loc_upd_rej(struct osmocom_ms *ms, struct msgb *msg) /* send event to PLMN search process */ switch(mm->lupd_rej_cause) { case GSM48_REJECT_ROAMING_NOT_ALLOWED: + case GSM48_REJECT_PLMN_NOT_ALLOWED: + case GSM48_REJECT_LOC_NOT_ALLOWED: nmsg = gsm322_msgb_alloc(GSM322_EVENT_ROAMING_NA); break; case GSM48_REJECT_IMSI_UNKNOWN_IN_HLR: @@ -2511,8 +2608,8 @@ static int gsm48_mm_rel_loc_upd_rej(struct osmocom_ms *ms, struct msgb *msg) return gsm48_mm_loc_upd_failed(ms, NULL); } - /* CS proc triggers: return to IDLE, case 13 is also handled there */ - return 0; + /* return to MM IDLE */ + return gsm48_mm_return_idle(ms, NULL); } /* 4.2.2 delay a location update */ @@ -2560,8 +2657,8 @@ static int gsm48_mm_loc_upd_failed(struct osmocom_ms *ms, struct msgb *msg) /* start update retry timer */ start_mm_t3211(mm); - /* CS process will trigger: return to MM IDLE */ - return 0; + /* return to MM IDLE */ + return gsm48_mm_return_idle(ms, NULL); } else LOGP(DMM, LOGL_INFO, "Loc. upd. failed too often.\n"); } @@ -2585,8 +2682,8 @@ static int gsm48_mm_loc_upd_failed(struct osmocom_ms *ms, struct msgb *msg) LOGP(DSUM, LOGL_INFO, "Try location update later\n"); } - /* CS process will trigger: return to MM IDLE */ - return 0; + /* return to MM IDLE */ + return gsm48_mm_return_idle(ms, NULL); } /* abort a location update due to radio failure or release */ @@ -3228,12 +3325,8 @@ static int gsm48_mm_abort_mm_con(struct osmocom_ms *ms, struct msgb *msg) /* release all connections */ gsm48_mm_release_mm_conn(ms, 1, cause, 1); - /* no RR connection, so we return to MM IDLE */ - if (mm->state == GSM48_MM_ST_WAIT_RR_CONN_MM_CON) - return gsm48_mm_return_idle(ms, NULL); - - /* CS process will trigger: return to MM IDLE */ - return 0; + /* return to MM IDLE */ + return gsm48_mm_return_idle(ms, NULL); } /* 4.5.1.2 timeout is received during MM connection establishment */ @@ -3404,8 +3497,8 @@ static int gsm48_mm_abort_rr(struct osmocom_ms *ms, struct msgb *msg) nrrh->cause = GSM48_RR_CAUSE_ABNORMAL_TIMER; gsm48_rr_downmsg(ms, nmsg); - /* CS process will trigger: return to MM IDLE / No SIM */ - return 0; + /* return to MM IDLE */ + return gsm48_mm_return_idle(ms, NULL); } /* @@ -3420,8 +3513,8 @@ static int gsm48_mm_rel_other(struct osmocom_ms *ms, struct msgb *msg) /* stop RR release timer (if running) */ stop_mm_t3240(mm); - /* CS process will trigger: return to MM IDLE */ - return 0; + /* return to MM IDLE */ + return gsm48_mm_return_idle(ms, NULL); } /* @@ -3928,7 +4021,10 @@ static struct eventstate { /* 4.2.2.1 Normal service */ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), - GSM48_MM_EVENT_NO_CELL_FOUND, gsm48_mm_plmn_search}, /* 4.2.1.2 */ + GSM48_MM_EVENT_USER_PLMN_SEL, gsm48_mm_plmn_search}, /* 4.2.1.2 */ + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), + GSM48_MM_EVENT_LOST_COVERAGE, gsm48_mm_plmn_search}, /* 4.2.1.2 */ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), GSM48_MM_EVENT_CELL_SELECTED, gsm48_mm_loc_upd_normal}, /* change */ @@ -3952,7 +4048,7 @@ static struct eventstate { {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE) | SBIT(GSM48_MM_SST_LOC_UPD_NEEDED), - GSM48_MM_EVENT_NO_CELL_FOUND, gsm48_mm_plmn_search}, /* 4.2.1.2 */ + GSM48_MM_EVENT_LOST_COVERAGE, gsm48_mm_plmn_search}, /* 4.2.1.2 */ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE) | SBIT(GSM48_MM_SST_LOC_UPD_NEEDED), @@ -3972,7 +4068,7 @@ static struct eventstate { GSM48_MM_EVENT_USER_PLMN_SEL, gsm48_mm_plmn_search}, /* 4.2.1.2 */ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_LIMITED_SERVICE), - GSM48_MM_EVENT_NO_CELL_FOUND, gsm48_mm_plmn_search}, /* 4.2.1.2 */ + GSM48_MM_EVENT_LOST_COVERAGE, gsm48_mm_plmn_search}, /* 4.2.1.2 */ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_LIMITED_SERVICE), GSM48_MM_EVENT_CELL_SELECTED, gsm48_mm_loc_upd_normal}, /* if allow. */ @@ -3989,12 +4085,6 @@ static struct eventstate { GSM48_MM_EVENT_CELL_SELECTED, gsm48_mm_cell_selected}, /* 4.2.1.1 */ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL), - GSM48_MM_EVENT_TIMEOUT_T3211, gsm48_mm_loc_upd_delay_retry}, - - {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL), - GSM48_MM_EVENT_TIMEOUT_T3213, gsm48_mm_loc_upd_delay_retry}, - - {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL), GSM48_MM_EVENT_TIMEOUT_T3212, gsm48_mm_loc_upd_delay_per}, /* 4.4.2 */ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL), @@ -4042,6 +4132,12 @@ static struct eventstate { GSM48_MM_EVENT_TIMEOUT_T3220, gsm48_mm_imsi_detach_end}, /* location update in other cases */ + {SBIT(GSM48_MM_ST_MM_IDLE), ALL_STATES, + GSM48_MM_EVENT_TIMEOUT_T3211, gsm48_mm_loc_upd_delay_retry}, + + {SBIT(GSM48_MM_ST_MM_IDLE), ALL_STATES, + GSM48_MM_EVENT_TIMEOUT_T3213, gsm48_mm_loc_upd_delay_retry}, + {ALL_STATES, ALL_STATES, GSM48_MM_EVENT_TIMEOUT_T3212, gsm48_mm_loc_upd_ignore}, diff --git a/src/host/layer23/src/mobile/gsm48_rr.c b/src/host/layer23/src/mobile/gsm48_rr.c index d199aacb..9d014d63 100644 --- a/src/host/layer23/src/mobile/gsm48_rr.c +++ b/src/host/layer23/src/mobile/gsm48_rr.c @@ -585,14 +585,16 @@ static void timeout_rr_meas(void *arg) uint8_t ch_type, ch_subch, ch_ts; char text[256]; - if (!cs->selected) { + /* don't monitor if no cell is selcted or if we scan neighbour cells */ + if (!cs->selected || cs->neighbour) { + sprintf(text, "MON: not camping on serving cell"); goto restart; } else if (!meas->frames) { sprintf(text, "MON: no cell info"); } else { - rxlev = meas->rxlev / meas->frames; - berr = meas->berr / meas->frames; - snr = meas->snr / meas->frames; + rxlev = (meas->rxlev + meas->frames / 2) / meas->frames; + berr = (meas->berr + meas->frames / 2) / meas->frames; + snr = (meas->snr + meas->frames / 2) / meas->frames; sprintf(text, "MON: f=%d lev=%s snr=%2d ber=%3d " "LAI=%s %s %04x ID=%04x", cs->sel_arfcn, gsm_print_rxlev(rxlev), berr, snr, @@ -608,7 +610,8 @@ static void timeout_rr_meas(void *arg) if (ch_type == RSL_CHAN_SDCCH8_ACCH || ch_type == RSL_CHAN_SDCCH4_ACCH) sprintf(text + strlen(text), "/%d", ch_subch); - } + } else + gsm322_meas(rr->ms, rxlev); } LOGP(DRR, LOGL_INFO, "%s\n", text); if (rr->monitor) @@ -1256,6 +1259,14 @@ static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging) return -EINVAL; } + /* ignore channel request while not camping on a cell */ + if (!cs->selected) { + LOGP(DRR, LOGL_INFO, "Channel request rejected, we did not " + "properly select the serving cell.\n"); + + goto rel_ind; + } + /* tell cell selection process to leave idle mode * NOTE: this must be sent unbuffered, because the state may not * change until idle mode is left @@ -1415,6 +1426,7 @@ static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging) undefined: LOGP(DSUM, LOGL_INFO, "Requesting channel failed\n"); +rel_ind: nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND); if (!nmsg) return -ENOMEM; @@ -1683,6 +1695,12 @@ static int gsm48_new_sysinfo(struct osmocom_ms *ms, uint8_t type) em->sysinfo = type; gsm322_cs_sendmsg(ms, nmsg); + /* if not camping, we don't care about SI */ + if (ms->cellsel.neighbour + || (ms->cellsel.state != GSM322_C3_CAMPED_NORMALLY + && ms->cellsel.state != GSM322_C7_CAMPED_ANY_CELL)) + return 0; + /* send timer info to location update process */ nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_SYSINFO); if (!nmsg) @@ -2078,7 +2096,8 @@ static int gsm48_rr_rx_pag_req_1(struct osmocom_ms *ms, struct msgb *msg) /* 3.3.1.1.2: ignore paging while not camping on a cell */ if (rr->state != GSM48_RR_ST_IDLE || !cs->selected || (cs->state != GSM322_C3_CAMPED_NORMALLY - && cs->state != GSM322_C7_CAMPED_ANY_CELL)) { + && cs->state != GSM322_C7_CAMPED_ANY_CELL) + || cs->neighbour) { LOGP(DRR, LOGL_INFO, "PAGING ignored, we are not camping.\n"); return 0; @@ -2130,7 +2149,8 @@ static int gsm48_rr_rx_pag_req_2(struct osmocom_ms *ms, struct msgb *msg) /* 3.3.1.1.2: ignore paging while not camping on a cell */ if (rr->state != GSM48_RR_ST_IDLE || !cs->selected || (cs->state != GSM322_C3_CAMPED_NORMALLY - && cs->state != GSM322_C7_CAMPED_ANY_CELL)) { + && cs->state != GSM322_C7_CAMPED_ANY_CELL) + || cs->neighbour) { LOGP(DRR, LOGL_INFO, "PAGING ignored, we are not camping.\n"); return 0; @@ -2195,7 +2215,8 @@ static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg) /* 3.3.1.1.2: ignore paging while not camping on a cell */ if (rr->state != GSM48_RR_ST_IDLE || !cs->selected || (cs->state != GSM322_C3_CAMPED_NORMALLY - && cs->state != GSM322_C7_CAMPED_ANY_CELL)) { + && cs->state != GSM322_C7_CAMPED_ANY_CELL) + || cs->neighbour) { LOGP(DRR, LOGL_INFO, "PAGING ignored, we are not camping.\n"); return 0; @@ -2312,6 +2333,14 @@ static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg) uint8_t *st, st_len; #endif + /* ignore imm.ass. while not camping on a cell */ + if (!cs->selected || cs->neighbour || !s) { + LOGP(DRR, LOGL_INFO, "IMMEDIATED ASSGINMENT ignored, we are " + "have not proper selected the serving cell.\n"); + + return 0; + } + memset(&cd, 0, sizeof(cd)); cd.ind_tx_power = rr->cd_now.ind_tx_power; @@ -2412,6 +2441,14 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg) uint8_t *st, st_len; #endif + /* ignore imm.ass.ext while not camping on a cell */ + if (!cs->selected || cs->neighbour || !s) { + LOGP(DRR, LOGL_INFO, "IMMEDIATED ASSGINMENT ignored, we are " + "have not proper selected the serving cell.\n"); + + return 0; + } + memset(&cd1, 0, sizeof(cd1)); cd1.ind_tx_power = rr->cd_now.ind_tx_power; memset(&cd2, 0, sizeof(cd2)); @@ -2651,7 +2688,8 @@ static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms) /* check for valid measurements, any frame must exist */ if (meas->frames) { meas_valid = 1; - serv_rxlev_full = serv_rxlev_sub = meas->rxlev / meas->frames; + serv_rxlev_full = serv_rxlev_sub = + (meas->rxlev + (meas->frames / 2)) / meas->frames; serv_rxqual_full = serv_rxqual_sub = 0; // FIXME } @@ -4335,7 +4373,7 @@ static int gsm48_rr_est_req(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm322_cellsel *cs = &ms->cellsel; - struct gsm48_sysinfo *s = cs->si; + struct gsm48_sysinfo *s = &cs->sel_si; struct gsm_subscriber *subscr = &ms->subscr; struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *) msg->data; struct gsm48_hdr *gh = msgb_l3(msg); diff --git a/src/host/layer23/src/mobile/settings.c b/src/host/layer23/src/mobile/settings.c index 15397fa6..592f8a8e 100644 --- a/src/host/layer23/src/mobile/settings.c +++ b/src/host/layer23/src/mobile/settings.c @@ -39,6 +39,9 @@ int gsm_settings_init(struct osmocom_ms *ms) strcpy(set->layer2_socket_path, layer2_socket_path); strcpy(set->sap_socket_path, sap_socket_path); + /* network search */ + set->plmn_mode = PLMN_MODE_AUTO; + /* IMEI */ sprintf(set->imei, "000000000000000"); sprintf(set->imeisv, "0000000000000000"); diff --git a/src/host/layer23/src/mobile/subscriber.c b/src/host/layer23/src/mobile/subscriber.c index 544d53fb..6de742aa 100644 --- a/src/host/layer23/src/mobile/subscriber.c +++ b/src/host/layer23/src/mobile/subscriber.c @@ -31,6 +31,10 @@ #include <osmocom/bb/common/networks.h> #include <osmocom/bb/mobile/vty.h> +/* enable to get an empty list of forbidden PLMNs, even if stored on SIM. + * if list is changed, the result is not written back to SIM */ +//#define TEST_EMPTY_FPLMN + void *l23_ctx; static void subscr_sim_query_cb(struct osmocom_ms *ms, struct msgb *msg); @@ -105,6 +109,9 @@ int gsm_subscr_init(struct osmocom_ms *ms) /* set key invalid */ subscr->key_seq = 7; + /* any cell selection timer timeout */ + subscr->any_timeout = 30; + /* init lists */ INIT_LLIST_HEAD(&subscr->plmn_list); INIT_LLIST_HEAD(&subscr->plmn_na); @@ -453,6 +460,10 @@ static int subscr_sim_fplmn(struct osmocom_ms *ms, uint8_t *data, uint8_t lai[5]; uint16_t dummy_lac; +#ifdef TEST_EMPTY_FPLMN + return 0; +#endif + /* flush list */ llist_for_each_safe(lh, lh2, &subscr->plmn_na) { llist_del(lh); @@ -473,7 +484,7 @@ static int subscr_sim_fplmn(struct osmocom_ms *ms, uint8_t *data, lai[2] = data[2]; gsm48_decode_lai((struct gsm48_loc_area_id *)lai, &na->mcc, &na->mnc, &dummy_lac); - na->cause = 0; + na->cause = -1; /* must have a value, but SIM stores no cause */ llist_add_tail(&na->entry, &subscr->plmn_na); data += 3; @@ -742,6 +753,10 @@ static int subscr_write_plmn_na(struct osmocom_ms *ms) uint8_t *data; uint8_t lai[5]; +#ifdef TEST_EMPTY_FPLMN + return 0; +#endif + /* skip, if no real valid SIM */ if (subscr->sim_type != GSM_SIM_TYPE_READER || !subscr->sim_valid) return 0; @@ -1024,25 +1039,31 @@ void new_sim_ustate(struct gsm_subscriber *subscr, int state) subscr->ustate = state; } -/* del forbidden PLMN */ +/* del forbidden PLMN. if MCC==0, flush complete list */ int gsm_subscr_del_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, uint16_t mnc) { - struct gsm_sub_plmn_na *na; + struct gsm_sub_plmn_na *na, *na2; + int deleted = 0; - llist_for_each_entry(na, &subscr->plmn_na, entry) { - if (na->mcc == mcc && na->mnc == mnc) { + llist_for_each_entry_safe(na, na2, &subscr->plmn_na, entry) { + if (!mcc || (na->mcc == mcc && na->mnc == mnc)) { LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden " "PLMNs (mcc=%s, mnc=%s)\n", gsm_print_mcc(mcc), gsm_print_mnc(mnc)); llist_del(&na->entry); talloc_free(na); - /* update plmn not allowed list on SIM */ - subscr_write_plmn_na(subscr->ms); - return 0; + deleted = 1; + if (mcc) + break; } } + if (deleted) { + /* update plmn not allowed list on SIM */ + subscr_write_plmn_na(subscr->ms); + } + return -EINVAL; } @@ -1062,7 +1083,7 @@ int gsm_subscr_add_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, return -ENOMEM; na->mcc = mcc; na->mnc = mnc; - na->cause = cause; + na->cause = cause ? : -1; /* cause 0 is not allowed */ llist_add_tail(&na->entry, &subscr->plmn_na); /* don't add Home PLMN to SIM */ diff --git a/src/host/layer23/src/mobile/vty_interface.c b/src/host/layer23/src/mobile/vty_interface.c index 8aff3ac6..7860e318 100644 --- a/src/host/layer23/src/mobile/vty_interface.c +++ b/src/host/layer23/src/mobile/vty_interface.c @@ -127,6 +127,13 @@ static void vty_restart(struct vty *vty, struct osmocom_ms *ms) "change to take effect!%s", ms->name, VTY_NEWLINE); } +static void vty_restart_if_started(struct vty *vty, struct osmocom_ms *ms) +{ + if (!ms->started) + return; + vty_restart(vty, ms); +} + static struct osmocom_ms *get_ms(const char *name, struct vty *vty) { struct osmocom_ms *ms; @@ -196,27 +203,40 @@ static void gsm_ms_dump(struct osmocom_ms *ms, struct vty *vty) return; if (set->plmn_mode == PLMN_MODE_AUTO) - vty_out(vty, " automatic network selection state: %s%s", - plmn_a_state_names[ms->plmn.state], VTY_NEWLINE); + vty_out(vty, " automatic network selection state: %s%s", + get_a_state_name(ms->plmn.state), VTY_NEWLINE); else - vty_out(vty, " manual network selection state: %s%s", - plmn_m_state_names[ms->plmn.state], VTY_NEWLINE); - vty_out(vty, " cell selection state: %s", - cs_state_names[ms->cellsel.state]); - if (ms->rrlayer.state == GSM48_RR_ST_IDLE && ms->cellsel.selected) - vty_out(vty, " (ARFCN %s)", - gsm_print_arfcn(ms->cellsel.sel_arfcn)); - vty_out(vty, "%s", VTY_NEWLINE); - vty_out(vty, " radio ressource layer state: %s%s", + vty_out(vty, " manual network selection state : %s%s", + get_m_state_name(ms->plmn.state), VTY_NEWLINE); + if (ms->plmn.mcc) + vty_out(vty, " MCC=%s " + "MNC=%s (%s, %s)%s", gsm_print_mcc(ms->plmn.mcc), + gsm_print_mnc(ms->plmn.mnc), gsm_get_mcc(ms->plmn.mcc), + gsm_get_mnc(ms->plmn.mcc, ms->plmn.mnc), VTY_NEWLINE); + vty_out(vty, " cell selection state: %s%s", + get_cs_state_name(ms->cellsel.state), VTY_NEWLINE); + if (ms->cellsel.sel_mcc) { + vty_out(vty, " ARFCN=%s MCC=%s MNC=%s " + "LAC=0x%04x CELLID=0x%04x%s", + gsm_print_arfcn(ms->cellsel.sel_arfcn), + gsm_print_mcc(ms->cellsel.sel_mcc), + gsm_print_mnc(ms->cellsel.sel_mnc), + ms->cellsel.sel_lac, ms->cellsel.sel_id, VTY_NEWLINE); + vty_out(vty, " (%s, %s)%s", + gsm_get_mcc(ms->cellsel.sel_mcc), + gsm_get_mnc(ms->cellsel.sel_mcc, ms->cellsel.sel_mnc), + VTY_NEWLINE); + } + vty_out(vty, " radio ressource layer state: %s%s", gsm48_rr_state_names[ms->rrlayer.state], VTY_NEWLINE); - vty_out(vty, " mobility management layer state: %s", + vty_out(vty, " mobility management layer state: %s", gsm48_mm_state_names[ms->mmlayer.state]); if (ms->mmlayer.state == GSM48_MM_ST_MM_IDLE) - vty_out(vty, ", %s", + vty_out(vty, ", %s", gsm48_mm_substate_names[ms->mmlayer.substate]); vty_out(vty, "%s", VTY_NEWLINE); llist_for_each_entry(trans, &ms->trans_list, entry) { - vty_out(vty, " call control state: %s%s", + vty_out(vty, " call control state: %s%s", gsm48_cc_state_name(trans->cc.state), VTY_NEWLINE); } } @@ -301,7 +321,7 @@ DEFUN(show_cell, show_cell_cmd, "show cell MS_NAME", if (!ms) return CMD_WARNING; - gsm322_dump_cs_list(&ms->cellsel, GSM322_CS_FLAG_SYSINFO, print_vty, + gsm322_dump_cs_list(&ms->cellsel, GSM322_CS_FLAG_SUPPORT, print_vty, vty); return CMD_SUCCESS; @@ -341,6 +361,21 @@ DEFUN(show_cell_si, show_cell_si_cmd, "show cell MS_NAME <0-1023> [pcs]", return CMD_SUCCESS; } +DEFUN(show_nbcells, show_nbcells_cmd, "show neighbour-cells MS_NAME", + SHOW_STR "Display information about current neighbour cells\n" + "Name of MS (see \"show ms\")") +{ + struct osmocom_ms *ms; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + gsm322_dump_nb_list(&ms->cellsel, print_vty, vty); + + return CMD_SUCCESS; +} + DEFUN(show_ba, show_ba_cmd, "show ba MS_NAME [MCC] [MNC]", SHOW_STR "Display information about band allocations\n" "Name of MS (see \"show ms\")\nMobile Country Code\n" @@ -431,9 +466,9 @@ DEFUN(no_monitor_network, no_monitor_network_cmd, "no monitor network MS_NAME", } DEFUN(sim_test, sim_test_cmd, "sim testcard MS_NAME [MCC] [MNC] [LAC] [TMSI]", - "SIM actions\nInsert test card\nName of MS (see \"show ms\")\n" + "SIM actions\nAttach bulit in test SIM\nName of MS (see \"show ms\")\n" "Mobile Country Code of RPLMN\nMobile Network Code of RPLMN\n" - "Optionally locatio area code\nOptionally current assigned TMSI") + "Optionally location area code\nOptionally current assigned TMSI") { struct osmocom_ms *ms; uint16_t mcc = 0x001, mnc = 0x01f, lac = 0x0000; @@ -444,7 +479,7 @@ DEFUN(sim_test, sim_test_cmd, "sim testcard MS_NAME [MCC] [MNC] [LAC] [TMSI]", return CMD_WARNING; if (ms->subscr.sim_valid) { - vty_out(vty, "SIM already presend, remove first!%s", + vty_out(vty, "SIM already attached, remove first!%s", VTY_NEWLINE); return CMD_WARNING; } @@ -474,7 +509,7 @@ DEFUN(sim_test, sim_test_cmd, "sim testcard MS_NAME [MCC] [MNC] [LAC] [TMSI]", } DEFUN(sim_reader, sim_reader_cmd, "sim reader MS_NAME", - "SIM actions\nSelect SIM from reader\nName of MS (see \"show ms\")") + "SIM actions\nAttach SIM from reader\nName of MS (see \"show ms\")") { struct osmocom_ms *ms; @@ -483,7 +518,7 @@ DEFUN(sim_reader, sim_reader_cmd, "sim reader MS_NAME", return CMD_WARNING; if (ms->subscr.sim_valid) { - vty_out(vty, "SIM already presend, remove first!%s", + vty_out(vty, "SIM already attached, remove first!%s", VTY_NEWLINE); return CMD_WARNING; } @@ -494,7 +529,7 @@ DEFUN(sim_reader, sim_reader_cmd, "sim reader MS_NAME", } DEFUN(sim_remove, sim_remove_cmd, "sim remove MS_NAME", - "SIM actions\nRemove SIM card\nName of MS (see \"show ms\")") + "SIM actions\nDetach SIM card\nName of MS (see \"show ms\")") { struct osmocom_ms *ms; @@ -503,7 +538,7 @@ DEFUN(sim_remove, sim_remove_cmd, "sim remove MS_NAME", return CMD_WARNING; if (!ms->subscr.sim_valid) { - vty_out(vty, "No Sim inserted!%s", VTY_NEWLINE); + vty_out(vty, "No SIM attached!%s", VTY_NEWLINE); return CMD_WARNING; } @@ -658,9 +693,11 @@ DEFUN(sim_lai, sim_lai_cmd, "sim lai MS_NAME MCC MNC LAC", return CMD_SUCCESS; } -DEFUN(network_select, network_select_cmd, "network select MS_NAME MCC MNC", +DEFUN(network_select, network_select_cmd, + "network select MS_NAME MCC MNC [force]", "Select ...\nSelect Network\nName of MS (see \"show ms\")\n" - "Mobile Country Code\nMobile Network Code") + "Mobile Country Code\nMobile Network Code\n" + "Force selecting a network that is not in the list") { struct osmocom_ms *ms; struct gsm322_plmn *plmn; @@ -676,6 +713,12 @@ DEFUN(network_select, network_select_cmd, "network select MS_NAME MCC MNC", return CMD_WARNING; plmn = &ms->plmn; + if (ms->settings.plmn_mode != PLMN_MODE_MANUAL) { + vty_out(vty, "Not in manual network selection mode%s", + VTY_NEWLINE); + return CMD_WARNING; + } + if (!mcc) { vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE); return CMD_WARNING; @@ -685,12 +728,16 @@ DEFUN(network_select, network_select_cmd, "network select MS_NAME MCC MNC", return CMD_WARNING; } - llist_for_each_entry(temp, &plmn->sorted_plmn, entry) - if (temp->mcc == mcc && temp->mnc == mnc) - found = 1; - if (!found) { - vty_out(vty, "Network not in list!%s", VTY_NEWLINE); - return CMD_WARNING; + if (argc < 4) { + llist_for_each_entry(temp, &plmn->sorted_plmn, entry) + if (temp->mcc == mcc && temp->mnc == mnc) + found = 1; + if (!found) { + vty_out(vty, "Network not in list!%s", VTY_NEWLINE); + vty_out(vty, "To force selecting this network, use " + "'force' keyword%s", VTY_NEWLINE); + return CMD_WARNING; + } } nmsg = gsm322_msgb_alloc(GSM322_EVENT_CHOOSE_PLMN); @@ -790,6 +837,60 @@ DEFUN(call_dtmf, call_dtmf_cmd, "call MS_NAME dtmf DIGITS", return CMD_SUCCESS; } +DEFUN(test_reselection, test_reselection_cmd, "test re-selection NAME", + "Manually trigger cell re-selection\nName of MS (see \"show ms\")") +{ + struct osmocom_ms *ms; + struct gsm_settings *set; + struct msgb *nmsg; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + set = &ms->settings; + + if (set->stick) { + vty_out(vty, "Cannot trigger cell re-selection, because we " + "stick to a cell!%s", VTY_NEWLINE); + return CMD_WARNING; + } + + nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL); + if (!nmsg) + return CMD_WARNING; + gsm322_c_event(ms, nmsg); + + + return CMD_SUCCESS; +} + +DEFUN(delete_forbidden_plmn, delete_forbidden_plmn_cmd, + "delete forbidden plmn NAME MCC MNC", + "Delete\nForbidden\nplmn\nName of MS (see \"show ms\")\n" + "Mobile Country Code\nMobile Network Code") +{ + struct osmocom_ms *ms; + uint16_t mcc = gsm_input_mcc((char *)argv[1]), + mnc = gsm_input_mnc((char *)argv[2]); + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + if (!mcc) { + vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (!mnc) { + vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + + gsm_subscr_del_forbidden_plmn(&ms->subscr, mcc, mnc); + + return CMD_SUCCESS; +} + DEFUN(network_show, network_show_cmd, "network show MS_NAME", "Network ...\nShow results of network search (again)\n" "Name of MS (see \"show ms\")") @@ -1144,6 +1245,9 @@ static void config_write_ms(struct vty *vty, struct osmocom_ms *ms) if (!hide_default || set->no_lupd) vty_out(vty, " %slocation-updating%s", (set->no_lupd) ? "no " : "", VTY_NEWLINE); + if (!hide_default || set->no_neighbour) + vty_out(vty, " %sneighbour-measurement%s", + (set->no_neighbour) ? "no " : "", VTY_NEWLINE); if (set->full_v1 || set->full_v2 || set->full_v3) { /* mandatory anyway */ vty_out(vty, " codec full-speed%s%s", @@ -1230,6 +1334,9 @@ static void config_write_ms(struct vty *vty, struct osmocom_ms *ms) VTY_NEWLINE); if (!hide_default || sup->dsc_max != set->dsc_max) vty_out(vty, " dsc-max %d%s", set->dsc_max, VTY_NEWLINE); + if (!hide_default || set->skip_max_per_band) + vty_out(vty, " %skip-max-per-band%s", + (set->skip_max_per_band) ? "" : "no ", VTY_NEWLINE); vty_out(vty, " exit%s", VTY_NEWLINE); vty_out(vty, " test-sim%s", VTY_NEWLINE); vty_out(vty, " imsi %s%s", set->test_imsi, VTY_NEWLINE); @@ -1335,8 +1442,8 @@ DEFUN(cfg_ms_sap, cfg_ms_sap_cmd, "sap-socket PATH", } DEFUN(cfg_ms_sim, cfg_ms_sim_cmd, "sim (none|reader|test)", - "Set SIM card type when powering on\nNo SIM interted\n" - "Use SIM from reader\nTest SIM inserted") + "Set SIM card to attach when powering on\nAttach no SIM\n" + "Attach SIM from reader\nAttach bulit in test SIM") { struct osmocom_ms *ms = vty->index; struct gsm_settings *set = &ms->settings; @@ -1356,7 +1463,8 @@ DEFUN(cfg_ms_sim, cfg_ms_sim_cmd, "sim (none|reader|test)", return CMD_WARNING; } - vty_restart(vty, ms); + vty_restart_if_started(vty, ms); + return CMD_SUCCESS; } @@ -1368,21 +1476,20 @@ DEFUN(cfg_ms_mode, cfg_ms_mode_cmd, "network-selection-mode (auto|manual)", struct gsm_settings *set = &ms->settings; struct msgb *nmsg; - if (!ms->plmn.state) { + if (!ms->started) { if (argv[0][0] == 'a') set->plmn_mode = PLMN_MODE_AUTO; else set->plmn_mode = PLMN_MODE_MANUAL; - - return CMD_SUCCESS; + } else { + if (argv[0][0] == 'a') + nmsg = gsm322_msgb_alloc(GSM322_EVENT_SEL_AUTO); + else + nmsg = gsm322_msgb_alloc(GSM322_EVENT_SEL_MANUAL); + if (!nmsg) + return CMD_WARNING; + gsm322_plmn_sendmsg(ms, nmsg); } - if (argv[0][0] == 'a') - nmsg = gsm322_msgb_alloc(GSM322_EVENT_SEL_AUTO); - else - nmsg = gsm322_msgb_alloc(GSM322_EVENT_SEL_MANUAL); - if (!nmsg) - return CMD_WARNING; - gsm322_plmn_sendmsg(ms, nmsg); return CMD_SUCCESS; } @@ -1419,7 +1526,6 @@ DEFUN(cfg_ms_imei_fixed, cfg_ms_imei_fixed_cmd, "imei-fixed", set->imei_random = 0; - vty_restart(vty, ms); return CMD_SUCCESS; } @@ -1432,7 +1538,6 @@ DEFUN(cfg_ms_imei_random, cfg_ms_imei_random_cmd, "imei-random <0-15>", set->imei_random = atoi(argv[0]); - vty_restart(vty, ms); return CMD_SUCCESS; } @@ -1823,6 +1928,33 @@ DEFUN(cfg_no_abbrev, cfg_ms_no_abbrev_cmd, "no abbrev [ABBREVIATION]", return CMD_SUCCESS; } +DEFUN(cfg_ms_neighbour, cfg_ms_neighbour_cmd, "neighbour-measurement", + "Allow neighbour cell measurement in idle mode") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->no_neighbour = 0; + + vty_restart_if_started(vty, ms); + + + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_no_neighbour, cfg_ms_no_neighbour_cmd, "no neighbour-measurement", + NO_STR "Do not allow neighbour cell measurement in idle mode") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->no_neighbour = 1; + + vty_restart_if_started(vty, ms); + + return CMD_SUCCESS; +} + static int config_write_dummy(struct vty *vty) { return CMD_SUCCESS; @@ -2067,9 +2199,9 @@ DEFUN(cfg_ms_sup_ch_cap, cfg_ms_sup_ch_cap_cmd, "channel-capability " return CMD_WARNING; } - if (ch_cap != set->ch_cap + if (ms->started && ch_cap != set->ch_cap && (ch_cap == GSM_CAP_SDCCH || set->ch_cap == GSM_CAP_SDCCH)) - vty_restart(vty, ms); + vty_restart_if_started(vty, ms); set->ch_cap = ch_cap; @@ -2123,6 +2255,30 @@ DEFUN(cfg_ms_sup_dsc_max, cfg_ms_sup_dsc_max_cmd, "dsc-max <90-500>", return CMD_SUCCESS; } +DEFUN(cfg_ms_sup_skip_max_per_band, cfg_ms_sup_skip_max_per_band_cmd, + "skip-max-per-band", + "Scan all frequencies per band, not only a maximum number") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->skip_max_per_band = 1; + + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_sup_no_skip_max_per_band, cfg_ms_sup_no_skip_max_per_band_cmd, + "no skip-max-per-band", + NO_STR "Scan only a maximum number of frequencies per band") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->skip_max_per_band = 0; + + return CMD_SUCCESS; +} + /* per testsim config */ DEFUN(cfg_ms_testsim, cfg_ms_testsim_cmd, "test-sim", "Configure test SIM emulation") @@ -2146,7 +2302,8 @@ DEFUN(cfg_test_imsi, cfg_test_imsi_cmd, "imsi IMSI", strcpy(set->test_imsi, argv[0]); - vty_restart(vty, ms); + vty_restart_if_started(vty, ms); + return CMD_SUCCESS; } @@ -2238,7 +2395,8 @@ DEFUN(cfg_test_no_rplmn, cfg_test_no_rplmn_cmd, "no rplmn", set->test_rplmn_valid = 0; - vty_restart(vty, ms); + vty_restart_if_started(vty, ms); + return CMD_SUCCESS; } @@ -2274,7 +2432,8 @@ DEFUN(cfg_test_rplmn, cfg_test_rplmn_cmd, "rplmn MCC MNC [LAC] [TMSI]", else set->test_tmsi = 0xffffffff; - vty_restart(vty, ms); + vty_restart_if_started(vty, ms); + return CMD_SUCCESS; } @@ -2295,7 +2454,8 @@ DEFUN(cfg_test_hplmn, cfg_test_hplmn_cmd, "hplmn-search (everywhere|foreign-coun break; } - vty_restart(vty, ms); + vty_restart_if_started(vty, ms); + return CMD_SUCCESS; } @@ -2441,6 +2601,7 @@ int ms_vty_init(void) install_element_ve(&show_support_cmd); install_element_ve(&show_cell_cmd); install_element_ve(&show_cell_si_cmd); + install_element_ve(&show_nbcells_cmd); install_element_ve(&show_ba_cmd); install_element_ve(&show_forb_la_cmd); install_element_ve(&show_forb_plmn_cmd); @@ -2463,6 +2624,8 @@ int ms_vty_init(void) install_element(ENABLE_NODE, &call_cmd); install_element(ENABLE_NODE, &call_retr_cmd); install_element(ENABLE_NODE, &call_dtmf_cmd); + install_element(ENABLE_NODE, &test_reselection_cmd); + install_element(ENABLE_NODE, &delete_forbidden_plmn_cmd); #ifdef _HAVE_GPSD install_element(CONFIG_NODE, &cfg_gps_host_cmd); @@ -2518,6 +2681,8 @@ int ms_vty_init(void) install_element(MS_NODE, &cfg_ms_abbrev_cmd); install_element(MS_NODE, &cfg_ms_no_abbrev_cmd); install_element(MS_NODE, &cfg_ms_testsim_cmd); + install_element(MS_NODE, &cfg_ms_neighbour_cmd); + install_element(MS_NODE, &cfg_ms_no_neighbour_cmd); install_element(MS_NODE, &cfg_ms_support_cmd); install_node(&support_node, config_write_dummy); install_default(SUPPORT_NODE); @@ -2575,6 +2740,8 @@ int ms_vty_init(void) install_element(SUPPORT_NODE, &cfg_ms_sup_no_half_v3_cmd); install_element(SUPPORT_NODE, &cfg_ms_sup_min_rxlev_cmd); install_element(SUPPORT_NODE, &cfg_ms_sup_dsc_max_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_skip_max_per_band_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_no_skip_max_per_band_cmd); install_node(&testsim_node, config_write_dummy); install_default(TESTSIM_NODE); install_element(TESTSIM_NODE, &ournode_exit_cmd); |