[layer23/mobile] Improvement and fixes of idle mode process
authorAndreas Eversberg <jolly@eversberg.eu>
Sun, 17 Jul 2011 09:12:15 +0000 (11:12 +0200)
committerAndreas Eversberg <jolly@eversberg.eu>
Sun, 17 Jul 2011 09:12:15 +0000 (11:12 +0200)
This patch introduces cell re-relection. When camping on a cell, it
scanns neighbour cells. If a 'better' cell is found, the cell is selected.
If the cell is in a different location area, a location upating is
performed under certain conditions.

The 'better' cell depends on various informations that are broadcasted on
the BCCH of a neihbour cell and of course the RX level. Most operators
don't set these informations, so the 'better' cell depend on a better
RX level for the same location area, or a much better RX level (6 dBm)
at a different location area.

There were many issues at the idle mode process that has been fixed.
Expecially when moving, the state machines got stuck, so no more cell search
was possible, or no further calls / location updating was possible.

In order to see the process of cell selection, enter the VTY interface and
enable the network monitor:

enable
monitor network 1  (where '1' is the instance of the MS)

In order to see the current state of the processes, enter:

show ms

13 files changed:
src/host/layer23/include/osmocom/bb/common/osmocom_data.h
src/host/layer23/include/osmocom/bb/mobile/gsm322.h
src/host/layer23/include/osmocom/bb/mobile/gsm48_mm.h
src/host/layer23/include/osmocom/bb/mobile/settings.h
src/host/layer23/include/osmocom/bb/mobile/subscriber.h
src/host/layer23/src/common/l1ctl.c
src/host/layer23/src/common/sysinfo.c
src/host/layer23/src/mobile/gsm322.c
src/host/layer23/src/mobile/gsm48_mm.c
src/host/layer23/src/mobile/gsm48_rr.c
src/host/layer23/src/mobile/settings.c
src/host/layer23/src/mobile/subscriber.c
src/host/layer23/src/mobile/vty_interface.c

index 6af5ca4..d387c91 100644 (file)
@@ -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 {
index d94e368..f39e566 100644 (file)
 #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 */
index 1cfe401..afdcf02 100644 (file)
@@ -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 {
index 20a8692..cd1b800 100644 (file)
@@ -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;
index c6cf57a..cc0cfac 100644 (file)
@@ -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 */
 
index bd7c67e..5e1e3cc 100644 (file)
@@ -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;
index 92a0516..225f5ad 100644 (file)
@@ -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)
index 210215e..802adc9 100644 (file)
 #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);
-
-       /* 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);
+       LOGP(DPLMN, LOGL_INFO, "PLMN not available after loss of coverage.\n");
 
-       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;
+                       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",
+               /* 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)),
-                               gsm_print_mcc(s->mcc),
-                               gsm_print_mnc(s->mnc), s->lac);
-                       continue;
-               }
+                               acc_class, s->class_barr);
+                       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",
+               /* 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));
-                       continue;
+                               gsm_print_mcc(s->mcc),
+                               gsm_print_mnc(s->mnc), s->lac);
+                       cs->list[i].flags &= ~GSM322_CS_FLAG_TEMP_AA;
                }
 
-               /* 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 out access. (access=%04x "
-                               "barred=%04x)\n",
+               /* 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)),
-                               acc_class, s->class_barr);
-                       continue;
+                               gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc));
+                       cs->list[i].flags &= ~GSM322_CS_FLAG_TEMP_AA;
                }
 
-               /* store temporary available and allowable flag */
-               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;
 
-       /* 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_C8_ANY_CELL_RESEL);
+
+       /* 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,17 +4055,761 @@ 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;
 }
index a276a9e..e84d26a 100644 (file)
@@ -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. */
@@ -3988,12 +4084,6 @@ static struct eventstate {
        {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL),
         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 */
 
@@ -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},
 
index d199aac..9d014d6 100644 (file)
@@ -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);
index 15397fa..592f8a8 100644 (file)
@@ -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");
index 544d53f..6de742a 100644 (file)
 #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 */
index 8aff3ac..7860e31 100644 (file)
@@ -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);