[layer23] Correctly report to restart mobile instance after band change
[osmocom-bb.git] / src / host / layer23 / src / mobile / vty_interface.c
index 6b6e09e..f5ccac3 100644 (file)
 #include <unistd.h>
 #include <sys/types.h>
 
-#include <osmocore/utils.h>
-#include <osmocore/gsm48.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/gsm48.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/signal.h>
 
 #include <osmocom/bb/common/osmocom_data.h>
 #include <osmocom/bb/common/networks.h>
+#include <osmocom/bb/common/gps.h>
 #include <osmocom/bb/mobile/mncc.h>
 #include <osmocom/bb/mobile/transaction.h>
 #include <osmocom/bb/mobile/vty.h>
-#include <osmocom/bb/mobile/gps.h>
+#include <osmocom/bb/mobile/app_mobile.h>
 #include <osmocom/vty/telnet_interface.h>
 
+void *l23_ctx;
+
 int mncc_call(struct osmocom_ms *ms, char *number);
 int mncc_hangup(struct osmocom_ms *ms);
 int mncc_answer(struct osmocom_ms *ms);
 int mncc_hold(struct osmocom_ms *ms);
 int mncc_retrieve(struct osmocom_ms *ms, int number);
+int mncc_dtmf(struct osmocom_ms *ms, char *dtmf);
 
 extern struct llist_head ms_list;
 extern struct llist_head active_connections;
@@ -57,6 +63,12 @@ struct cmd_node testsim_node = {
        1
 };
 
+struct cmd_node support_node = {
+       SUPPORT_NODE,
+       "%s(support)#",
+       1
+};
+
 static void print_vty(void *priv, const char *fmt, ...)
 {
        char buffer[1000];
@@ -77,93 +89,186 @@ static void print_vty(void *priv, const char *fmt, ...)
        }
 }
 
-static struct osmocom_ms *get_ms(const char *name, struct vty *vty)
+int vty_check_number(struct vty *vty, const char *number)
 {
-       struct osmocom_ms *ms;
+       int i;
 
-       llist_for_each_entry(ms, &ms_list, entity) {
-               if (!strcmp(ms->name, name))
-                       return ms;
+       for (i = 0; i < strlen(number); i++) {
+               /* allow international notation with + */
+               if (i == 0 && number[i] == '+')
+                       continue;
+               if (!(number[i] >= '0' && number[i] <= '9')
+                && number[i] != '*'
+                && number[i] != '#'
+                && !(number[i] >= 'a' && number[i] <= 'c')) {
+                       vty_out(vty, "Invalid digit '%c' of number!%s",
+                               number[i], VTY_NEWLINE);
+                       return -EINVAL;
+               }
+       }
+       if (number[0] == '\0' || (number[0] == '+' && number[1] == '\0')) {
+               vty_out(vty, "Given number has no digits!%s", VTY_NEWLINE);
+               return -EINVAL;
        }
-       vty_out(vty, "MS name '%s' does not exits.%s", name, VTY_NEWLINE);
 
-       return NULL;
+       return 0;
 }
 
-DEFUN(show_ms, show_ms_cmd, "show ms",
-       SHOW_STR "Display available MS entities\n")
-{
-       struct osmocom_ms *ms;
+int vty_reading = 0;
+static int hide_default = 0;
 
-       llist_for_each_entry(ms, &ms_list, entity) {
-               struct gsm_settings *set = &ms->settings;
-
-               vty_out(vty, "MS NAME: %s%s", ms->name, VTY_NEWLINE);
-               vty_out(vty, " IMEI: %s%s", set->imei, VTY_NEWLINE);
-               vty_out(vty, " IMEISV: %s%s", set->imeisv, VTY_NEWLINE);
-               if (set->imei_random)
-                       vty_out(vty, " IMEI generation: random (%d trailing "
-                               "digits)%s", set->imei_random, VTY_NEWLINE);
-               else
-                       vty_out(vty, " IMEI generation: fixed%s", VTY_NEWLINE);
-               vty_out(vty, " network selection mode: %s%s",
-                       (set->plmn_mode == PLMN_MODE_AUTO)
-                               ? "automatic" : "manual", VTY_NEWLINE);
-       }
+static void vty_restart(struct vty *vty, struct osmocom_ms *ms)
+{
+       if (vty_reading)
+               return;
+       if (ms->shutdown != 0)
+               return;
+       vty_out(vty, "You must restart MS '%s' ('shutdown / no shutdown') for "
+               "change to take effect!%s", ms->name, VTY_NEWLINE);
+}
 
-       return CMD_SUCCESS;
+static void vty_restart_if_started(struct vty *vty, struct osmocom_ms *ms)
+{
+       if (!ms->started)
+               return;
+       vty_restart(vty, ms);
 }
 
-DEFUN(show_support, show_support_cmd, "show support [ms_name]",
-       SHOW_STR "Display information about MS support\n"
-       "Name of MS (see \"show ms\")")
+static struct osmocom_ms *get_ms(const char *name, struct vty *vty)
 {
        struct osmocom_ms *ms;
 
-       if (argc) {
-               ms = get_ms(argv[0], vty);
-               if (!ms)
-                       return CMD_WARNING;
-               gsm_support_dump(&ms->support, print_vty, vty);
-       } else {
-               llist_for_each_entry(ms, &ms_list, entity) {
-                       gsm_support_dump(&ms->support, print_vty, vty);
-                       vty_out(vty, "%s", VTY_NEWLINE);
+       llist_for_each_entry(ms, &ms_list, entity) {
+               if (!strcmp(ms->name, name)) {
+                       if (ms->shutdown) {
+                               vty_out(vty, "MS '%s' is admin down.%s", name,
+                                       VTY_NEWLINE);
+                               return NULL;
+                       }
+                       return ms;
                }
        }
+       vty_out(vty, "MS name '%s' does not exits.%s", name, VTY_NEWLINE);
 
-       return CMD_SUCCESS;
+       return NULL;
 }
 
-static void gsm_states_dump(struct osmocom_ms *ms, struct vty *vty)
+static void gsm_ms_dump(struct osmocom_ms *ms, struct vty *vty)
 {
+       struct gsm_settings *set = &ms->settings;
        struct gsm_trans *trans;
+       char *service = "";
+
+       if (!ms->started)
+               service = ", radio is not started";
+       else if (ms->mmlayer.state == GSM48_MM_ST_MM_IDLE) {
+               /* current MM idle state */
+               switch (ms->mmlayer.substate) {
+               case GSM48_MM_SST_NORMAL_SERVICE:
+               case GSM48_MM_SST_PLMN_SEARCH_NORMAL:
+                       service = ", service is normal";
+                       break;
+               case GSM48_MM_SST_LOC_UPD_NEEDED:
+               case GSM48_MM_SST_ATTEMPT_UPDATE:
+                       service = ", service is limited (pending)";
+                       break;
+               case GSM48_MM_SST_NO_CELL_AVAIL:
+                       service = ", service is unavailable";
+                       break;
+               default:
+                       if (ms->subscr.sim_valid)
+                               service = ", service is limited";
+                       else
+                               service = ", service is limited "
+                                       "(IMSI detached)";
+                       break;
+               }
+       } else
+               service = ", MM connection active";
+
+       vty_out(vty, "MS '%s' is %s%s%s%s", ms->name,
+               (ms->shutdown) ? "administratively " : "",
+               (ms->shutdown || !ms->started) ? "down" : "up",
+               (!ms->shutdown) ? service : "",
+               VTY_NEWLINE);
+       vty_out(vty, "  IMEI: %s%s", set->imei, VTY_NEWLINE);
+       vty_out(vty, "     IMEISV: %s%s", set->imeisv, VTY_NEWLINE);
+       if (set->imei_random)
+               vty_out(vty, "     IMEI generation: random (%d trailing "
+                       "digits)%s", set->imei_random, VTY_NEWLINE);
+       else
+               vty_out(vty, "     IMEI generation: fixed%s", VTY_NEWLINE);
 
-       vty_out(vty, "Current state of MS '%s'%s", ms->name, VTY_NEWLINE);
-       if (ms->settings.plmn_mode == PLMN_MODE_AUTO)
-               vty_out(vty, " automatic network selection: %s%s", 
-                       plmn_a_state_names[ms->plmn.state], VTY_NEWLINE);
+       if (ms->shutdown)
+               return;
+
+       if (set->plmn_mode == PLMN_MODE_AUTO)
+               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: %s%s", 
-                       plmn_m_state_names[ms->plmn.state], VTY_NEWLINE);
-       vty_out(vty, " cell selection: %s%s", 
-               cs_state_names[ms->cellsel.state], VTY_NEWLINE);
-       vty_out(vty, " radio ressource layer: %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: %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: %s%s", 
+               vty_out(vty, "  call control state: %s%s",
                        gsm48_cc_state_name(trans->cc.state), VTY_NEWLINE);
        }
 }
 
-DEFUN(show_states, show_states_cmd, "show states [ms_name]",
-       SHOW_STR "Display current states of given MS\n"
+
+DEFUN(show_ms, show_ms_cmd, "show ms [MS_NAME]",
+       SHOW_STR "Display available MS entities\n")
+{
+       struct osmocom_ms *ms;
+
+       if (argc) {
+               llist_for_each_entry(ms, &ms_list, entity) {
+                       if (!strcmp(ms->name, argv[0])) {
+                               gsm_ms_dump(ms, vty);
+                               return CMD_SUCCESS;
+                       }
+               }
+               vty_out(vty, "MS name '%s' does not exits.%s", argv[0],
+               VTY_NEWLINE);
+               return CMD_WARNING;
+       } else {
+               llist_for_each_entry(ms, &ms_list, entity) {
+                       gsm_ms_dump(ms, vty);
+                       vty_out(vty, "%s", VTY_NEWLINE);
+               }
+       }
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(show_support, show_support_cmd, "show support [MS_NAME]",
+       SHOW_STR "Display information about MS support\n"
        "Name of MS (see \"show ms\")")
 {
        struct osmocom_ms *ms;
@@ -172,10 +277,10 @@ DEFUN(show_states, show_states_cmd, "show states [ms_name]",
                ms = get_ms(argv[0], vty);
                if (!ms)
                        return CMD_WARNING;
-               gsm_states_dump(ms, vty);
+               gsm_support_dump(ms, print_vty, vty);
        } else {
                llist_for_each_entry(ms, &ms_list, entity) {
-                       gsm_states_dump(ms, vty);
+                       gsm_support_dump(ms, print_vty, vty);
                        vty_out(vty, "%s", VTY_NEWLINE);
                }
        }
@@ -183,7 +288,7 @@ DEFUN(show_states, show_states_cmd, "show states [ms_name]",
        return CMD_SUCCESS;
 }
 
-DEFUN(show_subscr, show_subscr_cmd, "show subscriber [ms_name]",
+DEFUN(show_subscr, show_subscr_cmd, "show subscriber [MS_NAME]",
        SHOW_STR "Display information about subscriber\n"
        "Name of MS (see \"show ms\")")
 {
@@ -196,8 +301,10 @@ DEFUN(show_subscr, show_subscr_cmd, "show subscriber [ms_name]",
                gsm_subscr_dump(&ms->subscr, print_vty, vty);
        } else {
                llist_for_each_entry(ms, &ms_list, entity) {
-                       gsm_subscr_dump(&ms->subscr, print_vty, vty);
-                       vty_out(vty, "%s", VTY_NEWLINE);
+                       if (!ms->shutdown) {
+                               gsm_subscr_dump(&ms->subscr, print_vty, vty);
+                               vty_out(vty, "%s", VTY_NEWLINE);
+                       }
                }
        }
 
@@ -214,43 +321,62 @@ 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;
 }
 
-DEFUN(show_cell_si, show_cell_si_cmd, "show cell MS_NAME <0-1023>",
+DEFUN(show_cell_si, show_cell_si_cmd, "show cell MS_NAME <0-1023> [pcs]",
        SHOW_STR "Display information about received cell\n"
-       "Name of MS (see \"show ms\")\nRadio frequency number")
+       "Name of MS (see \"show ms\")\nRadio frequency number\n"
+       "Given frequency is PCS band (1900) rather than DCS band.")
 {
        struct osmocom_ms *ms;
-       int i;
        struct gsm48_sysinfo *s;
+       uint16_t arfcn = atoi(argv[1]);
 
        ms = get_ms(argv[0], vty);
        if (!ms)
                return CMD_WARNING;
 
-       i = atoi(argv[1]);
-       if (i < 0 || i > 1023) {
-               vty_out(vty, "Given ARFCN '%s' not in range (0..1023)%s",
-                       argv[1], VTY_NEWLINE);
-               return CMD_WARNING;
+       if (argc > 2) {
+               if (arfcn < 512 || arfcn > 810) {
+                       vty_out(vty, "Given ARFCN not in PCS band%s",
+                               VTY_NEWLINE);
+                       return CMD_WARNING;
+               }
+               arfcn |= ARFCN_PCS;
        }
-       s = ms->cellsel.list[i].sysinfo;
+
+       s = ms->cellsel.list[arfcn2index(arfcn)].sysinfo;
        if (!s) {
                vty_out(vty, "Given ARFCN '%s' has no sysinfo available%s",
                        argv[1], VTY_NEWLINE);
                return CMD_SUCCESS;
        }
 
-       gsm48_sysinfo_dump(s, i, print_vty, vty);
+       gsm48_sysinfo_dump(s, arfcn, print_vty, vty, ms->settings.freq_map);
+
+       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]",
+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"
        "Mobile Network Code")
@@ -339,19 +465,21 @@ DEFUN(no_monitor_network, no_monitor_network_cmd, "no monitor network MS_NAME",
        return CMD_SUCCESS;
 }
 
-DEFUN(sim_test, sim_test_cmd, "sim testcard MS_NAME [mcc] [mnc]",
-       "SIM actions\nInsert test card\nName of MS (see \"show ms\")\n"
-       "Mobile Country Code of RPLMN\nMobile Network Code of RPLMN")
+DEFUN(sim_test, sim_test_cmd, "sim testcard MS_NAME [MCC] [MNC] [LAC] [TMSI]",
+       "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 location area code\nOptionally current assigned TMSI")
 {
        struct osmocom_ms *ms;
-       uint16_t mcc = 0x001, mnc = 0x01f;
+       uint16_t mcc = 0x001, mnc = 0x01f, lac = 0x0000;
+       uint32_t tmsi = 0xffffffff;
 
        ms = get_ms(argv[0], vty);
        if (!ms)
                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;
        }
@@ -369,13 +497,19 @@ DEFUN(sim_test, sim_test_cmd, "sim testcard MS_NAME [mcc] [mnc]",
                }
        }
 
-       gsm_subscr_testcard(ms, mcc, mnc);
+       if (argc >= 4)
+               lac = strtoul(argv[3], NULL, 16);
+
+       if (argc >= 5)
+               tmsi = strtoul(argv[4], NULL, 16);
+
+       gsm_subscr_testcard(ms, mcc, mnc, lac, tmsi);
 
        return CMD_SUCCESS;
 }
 
 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;
 
@@ -384,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;
        }
@@ -395,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;
 
@@ -404,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;
        }
 
@@ -526,9 +660,44 @@ DEFUN(sim_unblock_pin, sim_unblock_pin_cmd, "sim unblock-pin MS_NAME PUC NEW",
        return CMD_SUCCESS;
 }
 
-DEFUN(network_select, network_select_cmd, "network select MS_NAME MCC MNC",
+DEFUN(sim_lai, sim_lai_cmd, "sim lai MS_NAME MCC MNC LAC",
+       "SIM actions\nChange LAI of SIM card\nName of MS (see \"show ms\")\n"
+       "Mobile Country Code\nMobile Network Code\nLocation Area Code "
+       " (use 0000 to remove LAI)")
+{
+       struct osmocom_ms *ms;
+       uint16_t mcc = gsm_input_mcc((char *)argv[1]),
+                mnc = gsm_input_mnc((char *)argv[2]),
+                lac = strtoul(argv[3], NULL, 16);
+
+       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;
+       }
+
+       ms->subscr.mcc = mcc;
+       ms->subscr.mnc = mnc;
+       ms->subscr.lac = lac;
+       ms->subscr.tmsi = 0xffffffff;
+
+       gsm_subscr_write_loci(ms);
+
+       return CMD_SUCCESS;
+}
+
+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;
@@ -544,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;
@@ -553,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);
@@ -573,34 +752,54 @@ DEFUN(network_select, network_select_cmd, "network select MS_NAME MCC MNC",
 }
 
 DEFUN(call, call_cmd, "call MS_NAME (NUMBER|emergency|answer|hangup|hold)",
-       "Make a call\nName of MS (see \"show ms\")\nPhone number to call\n"
+       "Make a call\nName of MS (see \"show ms\")\nPhone number to call "
+       "(Use digits '0123456789*#abc', and '+' to dial international)\n"
        "Make an emergency call\nAnswer an incomming call\nHangup a call\n"
        "Hold current active call\n")
 {
        struct osmocom_ms *ms;
+       struct gsm_settings *set;
+       struct gsm_settings_abbrev *abbrev;
+       char *number;
 
        ms = get_ms(argv[0], vty);
        if (!ms)
                return CMD_WARNING;
+       set = &ms->settings;
 
-       switch (argv[1][0]) {
-       case 'a':
+       if (set->ch_cap == GSM_CAP_SDCCH) {
+               vty_out(vty, "Basic call is not supported for SDCCH only "
+                       "mobile%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       number = (char *)argv[1];
+       if (!strcmp(number, "emergency"))
+               mncc_call(ms, number);
+       else if (!strcmp(number, "answer"))
                mncc_answer(ms);
-               break;
-       case 'h':
-               if (argv[1][1] == 'a')
-                       mncc_hangup(ms);
-               else
-                       mncc_hold(ms);
-               break;
-       default:
-               mncc_call(ms, (char *)argv[1]);
+       else if (!strcmp(number, "hangup"))
+               mncc_hangup(ms);
+       else if (!strcmp(number, "hold"))
+               mncc_hold(ms);
+       else {
+               llist_for_each_entry(abbrev, &set->abbrev, list) {
+                       if (!strcmp(number, abbrev->abbrev)) {
+                               number = abbrev->number;
+                               vty_out(vty, "Dialing number '%s'%s", number,
+                                       VTY_NEWLINE);
+                               break;
+                       }
+               }
+               if (vty_check_number(vty, number))
+                       return CMD_WARNING;
+               mncc_call(ms, number);
        }
 
        return CMD_SUCCESS;
 }
 
-DEFUN(call_retr, call_retr_cmd, "call MS_NAME retrieve [number]",
+DEFUN(call_retr, call_retr_cmd, "call MS_NAME retrieve [NUMBER]",
        "Make a call\nName of MS (see \"show ms\")\n"
        "Retrieve call on hold\nNumber of call to retrieve")
 {
@@ -615,20 +814,99 @@ DEFUN(call_retr, call_retr_cmd, "call MS_NAME retrieve [number]",
        return CMD_SUCCESS;
 }
 
+DEFUN(call_dtmf, call_dtmf_cmd, "call MS_NAME dtmf DIGITS",
+       "Make a call\nName of MS (see \"show ms\")\n"
+       "One or more DTMF digits to transmit")
+{
+       struct osmocom_ms *ms;
+       struct gsm_settings *set;
+
+       ms = get_ms(argv[0], vty);
+       if (!ms)
+               return CMD_WARNING;
+       set = &ms->settings;
+
+       if (!set->cc_dtmf) {
+               vty_out(vty, "DTMF not supported, please enable!%s",
+                       VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       mncc_dtmf(ms, (char *)argv[1]);
+
+       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\")")
 {
        struct osmocom_ms *ms;
+       struct gsm_settings *set;
        struct gsm322_plmn *plmn;
        struct gsm322_plmn_list *temp;
 
        ms = get_ms(argv[0], vty);
        if (!ms)
                return CMD_WARNING;
+       set = &ms->settings;
        plmn = &ms->plmn;
 
-       if (ms->settings.plmn_mode != PLMN_MODE_AUTO
+       if (set->plmn_mode != PLMN_MODE_AUTO
         && plmn->state != GSM322_M3_NOT_ON_PLMN) {
                vty_out(vty, "Start network search first!%s", VTY_NEWLINE);
                return CMD_WARNING;
@@ -664,12 +942,12 @@ DEFUN(network_search, network_search_cmd, "network search MS_NAME",
 DEFUN(cfg_gps_enable, cfg_gps_enable_cmd, "gps enable",
        "GPS receiver")
 {
-       if (gps_open()) {
-               gps.enable = 1;
+       if (osmo_gps_open()) {
+               g.enable = 1;
                vty_out(vty, "Failed to open GPS device!%s", VTY_NEWLINE);
                return CMD_WARNING;
        }
-       gps.enable = 1;
+       g.enable = 1;
 
        return CMD_SUCCESS;
 }
@@ -677,22 +955,54 @@ DEFUN(cfg_gps_enable, cfg_gps_enable_cmd, "gps enable",
 DEFUN(cfg_no_gps_enable, cfg_no_gps_enable_cmd, "no gps enable",
        NO_STR "Disable GPS receiver")
 {
-       if (gps.enable)
-               gps_close();
-       gps.enable = 0;
+       if (g.enable)
+               osmo_gps_close();
+       g.enable = 0;
+
+       return CMD_SUCCESS;
+}
+
+#ifdef _HAVE_GPSD
+DEFUN(cfg_gps_host, cfg_gps_host_cmd, "gps host HOST:PORT",
+       "GPS receiver\nSelect gpsd host and port\n"
+       "IP and port (optional) of the host running gpsd")
+{
+       char* colon = strstr(argv[0], ":");
+       if (colon != NULL) {
+               memcpy(g.gpsd_host, argv[0], colon - argv[0] - 1);
+               g.gpsd_host[colon - argv[0]] = '\0';
+               memcpy(g.gpsd_port, colon, strlen(colon));
+               g.gpsd_port[strlen(colon)] = '\0';
+       } else {
+               snprintf(g.gpsd_host, ARRAY_SIZE(g.gpsd_host), "%s", argv[0]);
+               g.gpsd_host[ARRAY_SIZE(g.gpsd_host) - 1] = '\0';
+               snprintf(g.gpsd_port, ARRAY_SIZE(g.gpsd_port), "2947");
+               g.gpsd_port[ARRAY_SIZE(g.gpsd_port) - 1] = '\0';
+       }
+       g.gps_type = GPS_TYPE_GPSD;
+       if (g.enable) {
+               osmo_gps_close();
+               if (osmo_gps_open()) {
+                       vty_out(vty, "Failed to connect to gpsd host!%s",
+                               VTY_NEWLINE);
+                       return CMD_WARNING;
+               }
+       }
 
        return CMD_SUCCESS;
 }
+#endif
 
 DEFUN(cfg_gps_device, cfg_gps_device_cmd, "gps device DEVICE",
        "GPS receiver\nSelect serial device\n"
        "Full path of serial device including /dev/")
 {
-       strncpy(gps.device, argv[0], sizeof(gps.device));
-       gps.device[sizeof(gps.device) - 1] = '\0';
-       if (gps.enable) {
-               gps_close();
-               if (gps_open()) {
+       strncpy(g.device, argv[0], sizeof(g.device));
+       g.device[sizeof(g.device) - 1] = '\0';
+       g.gps_type = GPS_TYPE_SERIAL;
+       if (g.enable) {
+               osmo_gps_close();
+               if (osmo_gps_open()) {
                        vty_out(vty, "Failed to open GPS device!%s",
                                VTY_NEWLINE);
                        return CMD_WARNING;
@@ -707,13 +1017,13 @@ DEFUN(cfg_gps_baud, cfg_gps_baud_cmd, "gps baudrate "
        "GPS receiver\nSelect baud rate\nDefault, don't modify\n\n\n\n\n\n")
 {
        if (argv[0][0] == 'd')
-               gps.baud = 0;
+               g.baud = 0;
        else
-               gps.baud = atoi(argv[0]);
-       if (gps.enable) {
-               gps_close();
-               if (gps_open()) {
-                       gps.enable = 0;
+               g.baud = atoi(argv[0]);
+       if (g.enable) {
+               osmo_gps_close();
+               if (osmo_gps_open()) {
+                       g.enable = 0;
                        vty_out(vty, "Failed to open GPS device!%s",
                                VTY_NEWLINE);
                        return CMD_WARNING;
@@ -723,15 +1033,50 @@ DEFUN(cfg_gps_baud, cfg_gps_baud_cmd, "gps baudrate "
        return CMD_SUCCESS;
 }
 
+DEFUN(cfg_hide_default, cfg_hide_default_cmd, "hide-default",
+       "Hide most default values in config to make it more compact")
+{
+       hide_default = 1;
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_hide_default, cfg_no_hide_default_cmd, "no hide-default",
+       NO_STR "Show default values in config")
+{
+       hide_default = 0;
+
+       return CMD_SUCCESS;
+}
+
 /* per MS config */
 DEFUN(cfg_ms, cfg_ms_cmd, "ms MS_NAME",
        "Select a mobile station to configure\nName of MS (see \"show ms\")")
 {
        struct osmocom_ms *ms;
+       int found = 0;
 
-       ms = get_ms(argv[0], vty);
-       if (!ms)
-               return CMD_WARNING;
+       llist_for_each_entry(ms, &ms_list, entity) {
+               if (!strcmp(ms->name, argv[0])) {
+                       found = 1;
+                       break;
+               }
+       }
+
+       if (!found) {
+               if (!vty_reading) {
+                       vty_out(vty, "MS name '%s' does not exits, try "
+                               "'ms %s create'%s", argv[0], argv[0],
+                               VTY_NEWLINE);
+                       return CMD_WARNING;
+               }
+               ms = mobile_new((char *)argv[0]);
+               if (!ms) {
+                       vty_out(vty, "Failed to add MS name '%s'%s", argv[0],
+                               VTY_NEWLINE);
+                       return CMD_WARNING;
+               }
+       }
 
        vty->index = ms;
        vty->node = MS_NODE;
@@ -739,63 +1084,142 @@ DEFUN(cfg_ms, cfg_ms_cmd, "ms MS_NAME",
        return CMD_SUCCESS;
 }
 
-static void config_write_ms_single(struct vty *vty, struct osmocom_ms *ms)
+DEFUN(cfg_ms_create, cfg_ms_create_cmd, "ms MS_NAME create",
+       "Select a mobile station to configure\nName of MS (see \"show ms\")\n"
+       "Create if MS does not exists")
 {
-       struct gsm_settings *set = &ms->settings;
+       struct osmocom_ms *ms;
+       int found = 0;
 
-       vty_out(vty, "ms %s%s", ms->name, VTY_NEWLINE);
-       switch(set->sim_type) {
-               case GSM_SIM_TYPE_NONE:
-               vty_out(vty, " sim none%s", VTY_NEWLINE);
-               break;
-               case GSM_SIM_TYPE_READER:
-               vty_out(vty, " sim reader%s", VTY_NEWLINE);
-               break;
-               case GSM_SIM_TYPE_TEST:
-               vty_out(vty, " sim test%s", VTY_NEWLINE);
-               break;
+       llist_for_each_entry(ms, &ms_list, entity) {
+               if (!strcmp(ms->name, argv[0])) {
+                       found = 1;
+                       break;
+               }
        }
-       vty_out(vty, " network-selection-mode %s%s", (set->plmn_mode
-                       == PLMN_MODE_AUTO) ? "auto" : "manual", VTY_NEWLINE);
-       vty_out(vty, " imei %s %s%s", set->imei,
-               set->imeisv + strlen(set->imei), VTY_NEWLINE);
-       if (set->imei_random)
-               vty_out(vty, " imei-random %d%s", set->imei_random,
-                       VTY_NEWLINE);
-       else
-               vty_out(vty, " imei-fixed%s", VTY_NEWLINE);
-       if (set->emergency_imsi[0])
-               vty_out(vty, " emergency-imsi %s%s", set->emergency_imsi,
+
+       if (!found) {
+               ms = mobile_new((char *)argv[0]);
+               if (!ms) {
+                       vty_out(vty, "Failed to add MS name '%s'%s", argv[0],
+                               VTY_NEWLINE);
+                       return CMD_WARNING;
+               }
+       }
+
+       vty->index = ms;
+       vty->node = MS_NODE;
+
+       vty_out(vty, "MS '%s' created, after configuration, do 'no shutdown'%s",
+               argv[0], VTY_NEWLINE);
+       return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ms_rename, cfg_ms_rename_cmd, "ms MS_NAME rename MS_NAME",
+       "Select a mobile station to configure\nName of MS (see \"show ms\")\n"
+       "Rename MS\nNew name of MS")
+{
+       struct osmocom_ms *ms;
+       int found = 0;
+
+       llist_for_each_entry(ms, &ms_list, entity) {
+               if (!strcmp(ms->name, argv[0])) {
+                       found = 1;
+                       break;
+               }
+       }
+
+       if (!found) {
+               vty_out(vty, "MS name '%s' does not exist%s", argv[0],
                        VTY_NEWLINE);
-       else
-               vty_out(vty, " no emergency-imsi%s", VTY_NEWLINE);
-       vty_out(vty, " %scall-waiting%s", (set->cw) ? "" : "no ", VTY_NEWLINE);
-       vty_out(vty, " %sclip%s", (set->clip) ? "" : "no ", VTY_NEWLINE);
-       vty_out(vty, " %sclir%s", (set->clir) ? "" : "no ", VTY_NEWLINE);
-       vty_out(vty, " test-sim%s", VTY_NEWLINE);
-       vty_out(vty, "  imsi %s%s", set->test_imsi, VTY_NEWLINE);
-       switch (set->test_ki_type) {
-       case GSM_SIM_KEY_XOR:
-               vty_out(vty, "  ki xor %s%s", hexdump(set->test_ki, 12),
+               return CMD_WARNING;
+       }
+
+       strncpy(ms->name, argv[1], sizeof(ms->name) - 1);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_ms, cfg_no_ms_cmd, "no ms MS_NAME",
+       NO_STR "Select a mobile station to remove\n"
+       "Name of MS (see \"show ms\")")
+{
+       struct osmocom_ms *ms;
+       int found = 0;
+
+       llist_for_each_entry(ms, &ms_list, entity) {
+               if (!strcmp(ms->name, argv[0])) {
+                       found = 1;
+                       break;
+               }
+       }
+
+       if (!found) {
+               vty_out(vty, "MS name '%s' does not exist%s", argv[0],
                        VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       mobile_delete(ms, 1);
+
+       return CMD_SUCCESS;
+}
+
+#define SUP_WRITE(item, cmd) \
+       if (sup->item) \
+               if (!hide_default || !set->item) \
+                       vty_out(vty, "  %s%s%s", (set->item) ? "" : "no ", \
+                       cmd, VTY_NEWLINE);
+
+static void config_write_ms(struct vty *vty, struct osmocom_ms *ms)
+{
+       struct gsm_settings *set = &ms->settings;
+       struct gsm_support *sup = &ms->support;
+       struct gsm_settings_abbrev *abbrev;
+
+       vty_out(vty, "ms %s%s", ms->name, VTY_NEWLINE);
+       vty_out(vty, " layer2-socket %s%s", set->layer2_socket_path,
+               VTY_NEWLINE);
+       vty_out(vty, " sap-socket %s%s", set->sap_socket_path, VTY_NEWLINE);
+       switch(set->sim_type) {
+               case GSM_SIM_TYPE_NONE:
+               vty_out(vty, " sim none%s", VTY_NEWLINE);
                break;
-       case GSM_SIM_KEY_COMP128:
-               vty_out(vty, "  ki comp128 %s%s", hexdump(set->test_ki, 16),
-                       VTY_NEWLINE);
+               case GSM_SIM_TYPE_READER:
+               vty_out(vty, " sim reader%s", VTY_NEWLINE);
+               break;
+               case GSM_SIM_TYPE_TEST:
+               vty_out(vty, " sim test%s", VTY_NEWLINE);
                break;
        }
-       vty_out(vty, "  %sbarred-access%s", (set->test_barr) ? "" : "no ",
-               VTY_NEWLINE);
-       if (set->test_rplmn_valid)
-               vty_out(vty, "  rplmn %s %s%s",
-                       gsm_print_mcc(set->test_rplmn_mcc),
-                       gsm_print_mnc(set->test_rplmn_mnc),
+       vty_out(vty, " network-selection-mode %s%s", (set->plmn_mode
+                       == PLMN_MODE_AUTO) ? "auto" : "manual", VTY_NEWLINE);
+       vty_out(vty, " imei %s %s%s", set->imei,
+               set->imeisv + strlen(set->imei), VTY_NEWLINE);
+       if (set->imei_random)
+               vty_out(vty, " imei-random %d%s", set->imei_random,
                        VTY_NEWLINE);
        else
-               vty_out(vty, "  no rplmn%s", VTY_NEWLINE);
-       vty_out(vty, "  hplmn-search %s%s", (set->test_always) ? "everywhere"
-                       : "foreign-country", VTY_NEWLINE);
-       vty_out(vty, " exit%s", VTY_NEWLINE);
+               if (!hide_default)
+                       vty_out(vty, " imei-fixed%s", VTY_NEWLINE);
+       if (set->emergency_imsi[0])
+               vty_out(vty, " emergency-imsi %s%s", set->emergency_imsi,
+                       VTY_NEWLINE);
+       else
+               if (!hide_default)
+                       vty_out(vty, " no emergency-imsi%s", VTY_NEWLINE);
+       if (!hide_default || set->cw)
+               vty_out(vty, " %scall-waiting%s", (set->cw) ? "" : "no ",
+                       VTY_NEWLINE);
+       if (!hide_default || set->auto_answer)
+               vty_out(vty, " %sauto-answer%s",
+                       (set->auto_answer) ? "" : "no ", VTY_NEWLINE);
+       if (!hide_default || set->clip)
+               vty_out(vty, " %sclip%s", (set->clip) ? "" : "no ",
+                       VTY_NEWLINE);
+       if (!hide_default || set->clir)
+               vty_out(vty, " %sclir%s", (set->clir) ? "" : "no ",
+                       VTY_NEWLINE);
        if (set->alter_tx_power)
                if (set->alter_tx_power_value)
                        vty_out(vty, " tx-power %d%s",
@@ -803,64 +1227,244 @@ static void config_write_ms_single(struct vty *vty, struct osmocom_ms *ms)
                else
                        vty_out(vty, " tx-power full%s", VTY_NEWLINE);
        else
-               vty_out(vty, " tx-power auto%s", VTY_NEWLINE);
+               if (!hide_default)
+                       vty_out(vty, " tx-power auto%s", VTY_NEWLINE);
        if (set->alter_delay)
                vty_out(vty, " simulated-delay %d%s", set->alter_delay,
                        VTY_NEWLINE);
        else
-               vty_out(vty, " no simulated-delay%s", VTY_NEWLINE);
+               if (!hide_default)
+                       vty_out(vty, " no simulated-delay%s", VTY_NEWLINE);
        if (set->stick)
-               vty_out(vty, " stick %d%s", set->stick_arfcn,
+               vty_out(vty, " stick %d%s%s", set->stick_arfcn & 1023,
+                       (set->stick_arfcn & ARFCN_PCS) ? " pcs" : "",
                        VTY_NEWLINE);
        else
-               vty_out(vty, " no stick%s", VTY_NEWLINE);
-       if (set->no_lupd)
-               vty_out(vty, " no location-updating%s", VTY_NEWLINE);
-       else
-               vty_out(vty, " location-updating%s", VTY_NEWLINE);
+               if (!hide_default)
+                       vty_out(vty, " no stick%s", VTY_NEWLINE);
+       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",
+                       (!set->half_prefer) ? " prefer" : "",
+                       VTY_NEWLINE);
+       }
+       if (set->half_v1 || set->half_v3) {
+               if (set->half)
+                       vty_out(vty, " codec half-speed%s%s",
+                               (set->half_prefer) ? " prefer" : "",
+                               VTY_NEWLINE);
+               else
+                       vty_out(vty, " no codec half-speed%s", VTY_NEWLINE);
+       }
+       if (llist_empty(&set->abbrev)) {
+               if (!hide_default)
+                       vty_out(vty, " no abbrev%s", VTY_NEWLINE);
+       } else {
+               llist_for_each_entry(abbrev, &set->abbrev, list)
+                       vty_out(vty, " abbrev %s %s%s%s%s", abbrev->abbrev,
+                               abbrev->number, (abbrev->name[0]) ? " " : "",
+                               abbrev->name, VTY_NEWLINE);
+       }
+       vty_out(vty, " support%s", VTY_NEWLINE);
+       SUP_WRITE(sms_ptp, "sms");
+       SUP_WRITE(a5_1, "a5/1");
+       SUP_WRITE(a5_2, "a5/2");
+       SUP_WRITE(a5_3, "a5/3");
+       SUP_WRITE(a5_4, "a5/4");
+       SUP_WRITE(a5_5, "a5/5");
+       SUP_WRITE(a5_6, "a5/6");
+       SUP_WRITE(a5_7, "a5/7");
+       SUP_WRITE(p_gsm, "p-gsm");
+       SUP_WRITE(e_gsm, "e-gsm");
+       SUP_WRITE(r_gsm, "r-gsm");
+       SUP_WRITE(pcs, "gsm-850");
+       SUP_WRITE(gsm_480, "gsm-480");
+       SUP_WRITE(gsm_450, "gsm-450");
+       SUP_WRITE(dcs, "dcs");
+       SUP_WRITE(pcs, "pcs");
+       if (sup->r_gsm || sup->e_gsm || sup->p_gsm)
+               if (!hide_default || sup->class_900 != set->class_900)
+                       vty_out(vty, "  class-900 %d%s", set->class_900,
+                               VTY_NEWLINE);
+       if (sup->gsm_850)
+               if (!hide_default || sup->class_850 != set->class_850)
+                       vty_out(vty, "  class-850 %d%s", set->class_850,
+                               VTY_NEWLINE);
+       if (sup->gsm_480 || sup->gsm_450)
+               if (!hide_default || sup->class_400 != set->class_400)
+                       vty_out(vty, "  class-400 %d%s", set->class_400,
+                               VTY_NEWLINE);
+       if (sup->dcs)
+               if (!hide_default || sup->class_dcs != set->class_dcs)
+                       vty_out(vty, "  class-dcs %d%s", set->class_dcs,
+                               VTY_NEWLINE);
+       if (sup->pcs)
+               if (!hide_default || sup->class_pcs != set->class_pcs)
+                       vty_out(vty, "  class-pcs %d%s", set->class_pcs,
+                               VTY_NEWLINE);
+       if (!hide_default || sup->ch_cap != set->ch_cap) {
+               switch (set->ch_cap) {
+               case GSM_CAP_SDCCH:
+                       vty_out(vty, "  channel-capability sdcch%s",
+                               VTY_NEWLINE);
+                       break;
+               case GSM_CAP_SDCCH_TCHF:
+                       vty_out(vty, "  channel-capability sdcch+tchf%s",
+                               VTY_NEWLINE);
+                       break;
+               case GSM_CAP_SDCCH_TCHF_TCHH:
+                       vty_out(vty, "  channel-capability sdcch+tchf+tchh%s",
+                               VTY_NEWLINE);
+                       break;
+               }
+       }
+       SUP_WRITE(full_v1, "full-speech-v1");
+       SUP_WRITE(full_v2, "full-speech-v2");
+       SUP_WRITE(full_v3, "full-speech-v3");
+       SUP_WRITE(half_v1, "half-speech-v1");
+       SUP_WRITE(half_v3, "half-speech-v3");
+       if (!hide_default || sup->min_rxlev_db != set->min_rxlev_db)
+               vty_out(vty, "  min-rxlev %d%s", set->min_rxlev_db,
+                       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);
+       switch (set->test_ki_type) {
+       case GSM_SIM_KEY_XOR:
+               vty_out(vty, "  ki xor %s%s",
+                       osmo_hexdump(set->test_ki, 12), VTY_NEWLINE);
+               break;
+       case GSM_SIM_KEY_COMP128:
+               vty_out(vty, "  ki comp128 %s%s",
+                       osmo_hexdump(set->test_ki, 16), VTY_NEWLINE);
+               break;
+       }
+       if (!hide_default || set->test_barr)
+               vty_out(vty, "  %sbarred-access%s",
+                       (set->test_barr) ? "" : "no ", VTY_NEWLINE);
+       if (set->test_rplmn_valid) {
+               vty_out(vty, "  rplmn %s %s",
+                       gsm_print_mcc(set->test_rplmn_mcc),
+                       gsm_print_mnc(set->test_rplmn_mnc));
+               if (set->test_lac > 0x0000 && set->test_lac < 0xfffe)
+                       vty_out(vty, " 0x%04x", set->test_lac);
+               if (set->test_tmsi != 0xffffffff)
+                       vty_out(vty, " 0x%08x", set->test_tmsi);
+               vty_out(vty, "%s", VTY_NEWLINE);
+       } else
+               if (!hide_default)
+                       vty_out(vty, "  no rplmn%s", VTY_NEWLINE);
+       if (!hide_default || set->test_always)
+               vty_out(vty, "  hplmn-search %s%s",
+                       (set->test_always) ? "everywhere" : "foreign-country",
+                       VTY_NEWLINE);
+       vty_out(vty, " exit%s", VTY_NEWLINE);
+       /* no shutdown must be written to config, because shutdown is default */
+       vty_out(vty, " %sshutdown%s", (ms->shutdown) ? "" : "no ",
+               VTY_NEWLINE);
        vty_out(vty, "exit%s", VTY_NEWLINE);
        vty_out(vty, "!%s", VTY_NEWLINE);
 }
 
-static int config_write_ms(struct vty *vty)
+static int config_write(struct vty *vty)
 {
        struct osmocom_ms *ms;
 
-       vty_out(vty, "gps device %s%s", gps.device, VTY_NEWLINE);
-       if (gps.baud)
-               vty_out(vty, "gps baudrate %d%s", gps.baud, VTY_NEWLINE);
+#ifdef _HAVE_GPSD
+       vty_out(vty, "gpsd host %s%s", g.gpsd_host, VTY_NEWLINE);
+       vty_out(vty, "gpsd port %s%s", g.gpsd_port, VTY_NEWLINE);
+#endif
+       vty_out(vty, "gps device %s%s", g.device, VTY_NEWLINE);
+       if (g.baud)
+               vty_out(vty, "gps baudrate %d%s", g.baud, VTY_NEWLINE);
        else
                vty_out(vty, "gps baudrate default%s", VTY_NEWLINE);
-       vty_out(vty, "%sgps enable%s", (gps.enable) ? "" : "no ", VTY_NEWLINE);
+       vty_out(vty, "%sgps enable%s", (g.enable) ? "" : "no ", VTY_NEWLINE);
+       vty_out(vty, "!%s", VTY_NEWLINE);
+
+       vty_out(vty, "%shide-default%s", (hide_default) ? "": "no ",
+               VTY_NEWLINE);
        vty_out(vty, "!%s", VTY_NEWLINE);
 
        llist_for_each_entry(ms, &ms_list, entity)
-               config_write_ms_single(vty, ms);
+               config_write_ms(vty, ms);
 
        return CMD_SUCCESS;
 }
 
+DEFUN(cfg_ms_show_this, cfg_ms_show_this_cmd, "show this",
+       SHOW_STR "Show config of this MS")
+{
+       struct osmocom_ms *ms = vty->index;
+
+       config_write_ms(vty, ms);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ms_layer2, cfg_ms_layer2_cmd, "layer2-socket PATH",
+       "Define socket path to connect between layer 2 and layer 1\n"
+       "Unix socket, default '/tmp/osmocom_l2'")
+{
+       struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
+
+       strncpy(set->layer2_socket_path, argv[0],
+               sizeof(set->layer2_socket_path) - 1);
+
+       vty_restart(vty, ms);
+       return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ms_sap, cfg_ms_sap_cmd, "sap-socket PATH",
+       "Define socket path to connect to SIM reader\n"
+       "Unix socket, default '/tmp/osmocom_sap'")
+{
+       struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
+
+       strncpy(set->sap_socket_path, argv[0],
+               sizeof(set->sap_socket_path) - 1);
+
+       vty_restart(vty, ms);
+       return CMD_SUCCESS;
+}
+
 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;
 
        switch (argv[0][0]) {
        case 'n':
-               ms->settings.sim_type = GSM_SIM_TYPE_NONE;
+               set->sim_type = GSM_SIM_TYPE_NONE;
                break;
        case 'r':
-               ms->settings.sim_type = GSM_SIM_TYPE_READER;
+               set->sim_type = GSM_SIM_TYPE_READER;
                break;
        case 't':
-               ms->settings.sim_type = GSM_SIM_TYPE_TEST;
+               set->sim_type = GSM_SIM_TYPE_TEST;
                break;
        default:
                vty_out(vty, "unknown SIM type%s", VTY_NEWLINE);
                return CMD_WARNING;
        }
 
+       vty_restart_if_started(vty, ms);
+
        return CMD_SUCCESS;
 }
 
@@ -869,23 +1473,23 @@ DEFUN(cfg_ms_mode, cfg_ms_mode_cmd, "network-selection-mode (auto|manual)",
        "Manual network selection")
 {
        struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
        struct msgb *nmsg;
 
-       if (!ms->plmn.state) {
+       if (!ms->started) {
                if (argv[0][0] == 'a')
-                       ms->settings.plmn_mode = PLMN_MODE_AUTO;
+                       set->plmn_mode = PLMN_MODE_AUTO;
                else
-                       ms->settings.plmn_mode = PLMN_MODE_MANUAL;
-
-               return CMD_SUCCESS;
+                       set->plmn_mode = PLMN_MODE_MANUAL;
+       } 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;
 }
@@ -895,6 +1499,7 @@ DEFUN(cfg_ms_imei, cfg_ms_imei_cmd, "imei IMEI [SV]",
        "Software version digit")
 {
        struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
        char *error, *sv = "0";
 
        if (argc >= 2)
@@ -906,9 +1511,9 @@ DEFUN(cfg_ms_imei, cfg_ms_imei_cmd, "imei IMEI [SV]",
                return CMD_WARNING;
        }
 
-       strcpy(ms->settings.imei, argv[0]);
-       strcpy(ms->settings.imeisv, argv[0]);
-       strcpy(ms->settings.imeisv + 15, sv);
+       strcpy(set->imei, argv[0]);
+       strcpy(set->imeisv, argv[0]);
+       strcpy(set->imeisv + 15, sv);
 
        return CMD_SUCCESS;
 }
@@ -917,8 +1522,9 @@ DEFUN(cfg_ms_imei_fixed, cfg_ms_imei_fixed_cmd, "imei-fixed",
        "Use fixed IMEI on every power on")
 {
        struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
 
-       ms->settings.imei_random = 0;
+       set->imei_random = 0;
 
        return CMD_SUCCESS;
 }
@@ -928,8 +1534,9 @@ DEFUN(cfg_ms_imei_random, cfg_ms_imei_random_cmd, "imei-random <0-15>",
        "Number of trailing digits to randomize")
 {
        struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
 
-       ms->settings.imei_random = atoi(argv[0]);
+       set->imei_random = atoi(argv[0]);
 
        return CMD_SUCCESS;
 }
@@ -938,6 +1545,7 @@ DEFUN(cfg_ms_emerg_imsi, cfg_ms_emerg_imsi_cmd, "emergency-imsi IMSI",
        "Use special IMSI for emergency calls\n15 digits IMSI")
 {
        struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
        char *error;
 
        error = gsm_check_imsi(argv[0]);
@@ -945,7 +1553,7 @@ DEFUN(cfg_ms_emerg_imsi, cfg_ms_emerg_imsi_cmd, "emergency-imsi IMSI",
                vty_out(vty, "%s%s", error, VTY_NEWLINE);
                return CMD_WARNING;
        }
-       strcpy(ms->settings.emergency_imsi, argv[0]);
+       strcpy(set->emergency_imsi, argv[0]);
 
        return CMD_SUCCESS;
 }
@@ -954,8 +1562,9 @@ DEFUN(cfg_ms_no_emerg_imsi, cfg_ms_no_emerg_imsi_cmd, "no emergency-imsi",
        NO_STR "Use IMSI of SIM or IMEI for emergency calls")
 {
        struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
 
-       ms->settings.emergency_imsi[0] = '\0';
+       set->emergency_imsi[0] = '\0';
 
        return CMD_SUCCESS;
 }
@@ -964,8 +1573,9 @@ DEFUN(cfg_no_cw, cfg_ms_no_cw_cmd, "no call-waiting",
        NO_STR "Disallow waiting calls")
 {
        struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
 
-       ms->settings.cw = 0;
+       set->cw = 0;
 
        return CMD_SUCCESS;
 }
@@ -974,8 +1584,31 @@ DEFUN(cfg_cw, cfg_ms_cw_cmd, "call-waiting",
        "Allow waiting calls")
 {
        struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
+
+       set->cw = 1;
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_auto_answer, cfg_ms_no_auto_answer_cmd, "no auto-answer",
+       NO_STR "Disable auto-answering calls")
+{
+       struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
+
+       set->auto_answer = 0;
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(cfg_auto_answer, cfg_ms_auto_answer_cmd, "auto-answer",
+       "Enable auto-answering calls")
+{
+       struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
 
-       ms->settings.cw = 1;
+       set->auto_answer = 1;
 
        return CMD_SUCCESS;
 }
@@ -984,9 +1617,10 @@ DEFUN(cfg_clip, cfg_ms_clip_cmd, "clip",
        "Force caller ID presentation")
 {
        struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
 
-       ms->settings.clip = 1;
-       ms->settings.clir = 0;
+       set->clip = 1;
+       set->clir = 0;
 
        return CMD_SUCCESS;
 }
@@ -995,9 +1629,10 @@ DEFUN(cfg_clir, cfg_ms_clir_cmd, "clir",
        "Force caller ID restriction")
 {
        struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
 
-       ms->settings.clip = 0;
-       ms->settings.clir = 1;
+       set->clip = 0;
+       set->clir = 1;
 
        return CMD_SUCCESS;
 }
@@ -1006,8 +1641,9 @@ DEFUN(cfg_no_clip, cfg_ms_no_clip_cmd, "no clip",
        NO_STR "Disable forcing of caller ID presentation")
 {
        struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
 
-       ms->settings.clip = 0;
+       set->clip = 0;
 
        return CMD_SUCCESS;
 }
@@ -1016,8 +1652,9 @@ DEFUN(cfg_no_clir, cfg_ms_no_clir_cmd, "no clir",
        NO_STR "Disable forcing of caller ID restriction")
 {
        struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
 
-       ms->settings.clir = 0;
+       set->clir = 0;
 
        return CMD_SUCCESS;
 }
@@ -1027,14 +1664,15 @@ DEFUN(cfg_ms_tx_power, cfg_ms_tx_power_cmd, "tx-power (auto|full)",
        "Always full power\nFixed GSM power value if supported")
 {
        struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
 
        switch (argv[0][0]) {
        case 'a':
-               ms->settings.alter_tx_power = 0;
+               set->alter_tx_power = 0;
                break;
        case 'f':
-               ms->settings.alter_tx_power = 1;
-               ms->settings.alter_tx_power_value = 0;
+               set->alter_tx_power = 1;
+               set->alter_tx_power_value = 0;
                break;
        }
 
@@ -1046,9 +1684,10 @@ DEFUN(cfg_ms_tx_power_val, cfg_ms_tx_power_val_cmd, "tx-power <0-31>",
        "Fixed GSM power value if supported")
 {
        struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
 
-       ms->settings.alter_tx_power = 1;
-       ms->settings.alter_tx_power_value = atoi(argv[0]);
+       set->alter_tx_power = 1;
+       set->alter_tx_power_value = atoi(argv[0]);
 
        return CMD_SUCCESS;
 }
@@ -1058,8 +1697,10 @@ DEFUN(cfg_ms_sim_delay, cfg_ms_sim_delay_cmd, "simulated-delay <-128-127>",
        "Delay in half bits (distance in 553.85 meter steps)")
 {
        struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
 
-       ms->settings.alter_delay = atoi(argv[0]);
+       set->alter_delay = atoi(argv[0]);
+       gsm48_rr_alter_delay(ms);
 
        return CMD_SUCCESS;
 }
@@ -1068,19 +1709,32 @@ DEFUN(cfg_ms_no_sim_delay, cfg_ms_no_sim_delay_cmd, "no simulated-delay",
        NO_STR "Do not simulate a lower or higher distance from the BTS")
 {
        struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
 
-       ms->settings.alter_delay = 0;
+       set->alter_delay = 0;
+       gsm48_rr_alter_delay(ms);
 
        return CMD_SUCCESS;
 }
 
-DEFUN(cfg_ms_stick, cfg_ms_stick_cmd, "stick <0-1023>",
-       "Stick to the given cell\nARFCN of the cell to stick to")
+DEFUN(cfg_ms_stick, cfg_ms_stick_cmd, "stick <0-1023> [pcs]",
+       "Stick to the given cell\nARFCN of the cell to stick to\n"
+       "Given frequency is PCS band (1900) rather than DCS band.")
 {
        struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
+       uint16_t arfcn = atoi(argv[0]);
 
-       ms->settings.stick = 1;
-       ms->settings.stick_arfcn = atoi(argv[0]);
+       if (argc > 1) {
+               if (arfcn < 512 || arfcn > 810) {
+                       vty_out(vty, "Given ARFCN not in PCS band%s",
+                               VTY_NEWLINE);
+                       return CMD_WARNING;
+               }
+               arfcn |= ARFCN_PCS;
+       }
+       set->stick = 1;
+       set->stick_arfcn = arfcn;
 
        return CMD_SUCCESS;
 }
@@ -1089,8 +1743,9 @@ DEFUN(cfg_ms_no_stick, cfg_ms_no_stick_cmd, "no stick",
        NO_STR "Do not stick to any cell")
 {
        struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
 
-       ms->settings.stick = 0;
+       set->stick = 0;
 
        return CMD_SUCCESS;
 }
@@ -1099,8 +1754,9 @@ DEFUN(cfg_ms_lupd, cfg_ms_lupd_cmd, "location-updating",
        "Allow location updating")
 {
        struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
 
-       ms->settings.no_lupd = 0;
+       set->no_lupd = 0;
 
        return CMD_SUCCESS;
 }
@@ -1109,17 +1765,192 @@ DEFUN(cfg_ms_no_lupd, cfg_ms_no_lupd_cmd, "no location-updating",
        NO_STR "Do not allow location updating")
 {
        struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
 
-       ms->settings.no_lupd = 1;
+       set->no_lupd = 1;
 
        return CMD_SUCCESS;
 }
 
-/* per testsim config */
-DEFUN(cfg_ms_testsim, cfg_ms_testsim_cmd, "test-sim",
-       "Configure test SIM emulation")
+DEFUN(cfg_codec_full, cfg_ms_codec_full_cmd, "codec full-speed",
+       "Enable codec\nFull speed speech codec")
 {
-       vty->node = TESTSIM_NODE;
+       struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
+
+       if (!set->full_v1 && !set->full_v2 && !set->full_v3) {
+               vty_out(vty, "Full-rate codec not supported%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(cfg_codec_full_pref, cfg_ms_codec_full_pref_cmd, "codec full-speed "
+       "prefer",
+       "Enable codec\nFull speed speech codec\nPrefer this codec")
+{
+       struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
+
+       if (!set->full_v1 && !set->full_v2 && !set->full_v3) {
+               vty_out(vty, "Full-rate codec not supported%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       set->half_prefer = 0;
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(cfg_codec_half, cfg_ms_codec_half_cmd, "codec half-speed",
+       "Enable codec\nHalf speed speech codec")
+{
+       struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
+
+       if (!set->half_v1 && !set->half_v3) {
+               vty_out(vty, "Half-rate codec not supported%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       set->half = 1;
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(cfg_codec_half_pref, cfg_ms_codec_half_pref_cmd, "codec half-speed "
+       "prefer",
+       "Enable codec\nHalf speed speech codec\nPrefer this codec")
+{
+       struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
+
+       if (!set->half_v1 && !set->half_v3) {
+               vty_out(vty, "Half-rate codec not supported%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       set->half = 1;
+       set->half_prefer = 1;
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_codec_half, cfg_ms_no_codec_half_cmd, "no codec half-speed",
+       NO_STR "Disable codec\nHalf speed speech codec")
+{
+       struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
+
+       if (!set->half_v1 && !set->half_v3) {
+               vty_out(vty, "Half-rate codec not supported%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       set->half = 0;
+       set->half_prefer = 0;
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(cfg_abbrev, cfg_ms_abbrev_cmd, "abbrev ABBREVIATION NUMBER [NAME]",
+       "Store given abbreviation number\n1-3 digits abbreviation\n"
+       "Number to store for the abbreviation "
+       "(Use digits '0123456789*#abc', and '+' to dial international)\n"
+       "Name of the abbreviation")
+{
+       struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
+       struct gsm_settings_abbrev *abbrev;
+       int i;
+
+       llist_for_each_entry(abbrev, &set->abbrev, list) {
+               if (!strcmp(argv[0], abbrev->abbrev)) {
+                       vty_out(vty, "Given abbreviation '%s' already stored, "
+                               "delete first!%s", argv[0], VTY_NEWLINE);
+                       return CMD_WARNING;
+               }
+       }
+
+       if (strlen(argv[0]) >= sizeof(abbrev->abbrev)) {
+               vty_out(vty, "Given abbreviation too long%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       for (i = 0; i < strlen(argv[0]); i++) {
+               if (argv[0][i] < '0' || argv[0][i] > '9') {
+                       vty_out(vty, "Given abbreviation must have digits "
+                               "0..9 only!%s", VTY_NEWLINE);
+                       return CMD_WARNING;
+               }
+       }
+
+       if (vty_check_number(vty, argv[1]))
+               return CMD_WARNING;
+
+       abbrev = talloc_zero(l23_ctx, struct gsm_settings_abbrev);
+       if (!abbrev) {
+               vty_out(vty, "No Memory!%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+       llist_add_tail(&abbrev->list, &set->abbrev);
+       strncpy(abbrev->abbrev, argv[0], sizeof(abbrev->abbrev) - 1);
+       strncpy(abbrev->number, argv[1], sizeof(abbrev->number) - 1);
+       if (argc >= 3)
+               strncpy(abbrev->name, argv[2], sizeof(abbrev->name) - 1);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_abbrev, cfg_ms_no_abbrev_cmd, "no abbrev [ABBREVIATION]",
+       NO_STR "Remove given abbreviation number or all numbers\n"
+       "Abbreviation number to remove")
+{
+       struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
+       struct gsm_settings_abbrev *abbrev, *abbrev2;
+       uint8_t deleted = 0;
+
+       llist_for_each_entry_safe(abbrev, abbrev2, &set->abbrev, list) {
+               if (argc < 1 || !strcmp(argv[0], abbrev->abbrev)) {
+                       llist_del(&abbrev->list);
+                       deleted = 1;
+               }
+       }
+
+       if (argc >= 1 && !deleted) {
+               vty_out(vty, "Given abbreviation '%s' not found!%s",
+                       argv[0], VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       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;
 }
@@ -1129,10 +1960,339 @@ static int config_write_dummy(struct vty *vty)
        return CMD_SUCCESS;
 }
 
+/* per support config */
+DEFUN(cfg_ms_support, cfg_ms_support_cmd, "support",
+       "Define supported features")
+{
+       vty->node = SUPPORT_NODE;
+
+       return CMD_SUCCESS;
+}
+
+#define SUP_EN(cfg, cfg_cmd, item, cmd, desc, restart) \
+DEFUN(cfg, cfg_cmd, cmd, "Enable " desc "support") \
+{ \
+       struct osmocom_ms *ms = vty->index; \
+       struct gsm_settings *set = &ms->settings; \
+       struct gsm_support *sup = &ms->support; \
+       if (!sup->item) { \
+               vty_out(vty, desc " not supported%s", VTY_NEWLINE); \
+               if (vty_reading) \
+                       return CMD_SUCCESS; \
+               return CMD_WARNING; \
+       } \
+       if (restart) \
+               vty_restart(vty, ms); \
+       set->item = 1; \
+       return CMD_SUCCESS; \
+}
+
+#define SUP_DI(cfg, cfg_cmd, item, cmd, desc, restart) \
+DEFUN(cfg, cfg_cmd, "no " cmd, NO_STR "Disable " desc " support") \
+{ \
+       struct osmocom_ms *ms = vty->index; \
+       struct gsm_settings *set = &ms->settings; \
+       struct gsm_support *sup = &ms->support; \
+       if (!sup->item) { \
+               vty_out(vty, desc " not supported%s", VTY_NEWLINE); \
+               if (vty_reading) \
+                       return CMD_SUCCESS; \
+               return CMD_WARNING; \
+       } \
+       if (restart) \
+               vty_restart(vty, ms); \
+       set->item = 0; \
+       return CMD_SUCCESS; \
+}
+
+#define SET_EN(cfg, cfg_cmd, item, cmd, desc, restart) \
+DEFUN(cfg, cfg_cmd, cmd, "Enable " desc "support") \
+{ \
+       struct osmocom_ms *ms = vty->index; \
+       struct gsm_settings *set = &ms->settings; \
+       if (restart) \
+               vty_restart(vty, ms); \
+       set->item = 1; \
+       return CMD_SUCCESS; \
+}
+
+#define SET_DI(cfg, cfg_cmd, item, cmd, desc, restart) \
+DEFUN(cfg, cfg_cmd, "no " cmd, NO_STR "Disable " desc " support") \
+{ \
+       struct osmocom_ms *ms = vty->index; \
+       struct gsm_settings *set = &ms->settings; \
+       if (restart) \
+               vty_restart(vty, ms); \
+       set->item = 0; \
+       return CMD_SUCCESS; \
+}
+
+SET_EN(cfg_ms_sup_dtmf, cfg_ms_sup_dtmf_cmd, cc_dtmf, "dtmf", "DTMF", 0);
+SET_DI(cfg_ms_sup_no_dtmf, cfg_ms_sup_no_dtmf_cmd, cc_dtmf, "dtmf", "DTMF", 0);
+SUP_EN(cfg_ms_sup_sms, cfg_ms_sup_sms_cmd, sms_ptp, "sms", "SMS", 0);
+SUP_DI(cfg_ms_sup_no_sms, cfg_ms_sup_no_sms_cmd, sms_ptp, "sms", "SMS", 0);
+SUP_EN(cfg_ms_sup_a5_1, cfg_ms_sup_a5_1_cmd, a5_1, "a5/1", "A5/1", 0);
+SUP_DI(cfg_ms_sup_no_a5_1, cfg_ms_sup_no_a5_1_cmd, a5_1, "a5/1", "A5/1", 0);
+SUP_EN(cfg_ms_sup_a5_2, cfg_ms_sup_a5_2_cmd, a5_2, "a5/2", "A5/2", 0);
+SUP_DI(cfg_ms_sup_no_a5_2, cfg_ms_sup_no_a5_2_cmd, a5_2, "a5/2", "A5/2", 0);
+SUP_EN(cfg_ms_sup_a5_3, cfg_ms_sup_a5_3_cmd, a5_3, "a5/3", "A5/3", 0);
+SUP_DI(cfg_ms_sup_no_a5_3, cfg_ms_sup_no_a5_3_cmd, a5_3, "a5/3", "A5/3", 0);
+SUP_EN(cfg_ms_sup_a5_4, cfg_ms_sup_a5_4_cmd, a5_4, "a5/4", "A5/4", 0);
+SUP_DI(cfg_ms_sup_no_a5_4, cfg_ms_sup_no_a5_4_cmd, a5_4, "a5/4", "A5/4", 0);
+SUP_EN(cfg_ms_sup_a5_5, cfg_ms_sup_a5_5_cmd, a5_5, "a5/5", "A5/5", 0);
+SUP_DI(cfg_ms_sup_no_a5_5, cfg_ms_sup_no_a5_5_cmd, a5_5, "a5/5", "A5/5", 0);
+SUP_EN(cfg_ms_sup_a5_6, cfg_ms_sup_a5_6_cmd, a5_6, "a5/6", "A5/6", 0);
+SUP_DI(cfg_ms_sup_no_a5_6, cfg_ms_sup_no_a5_6_cmd, a5_6, "a5/6", "A5/6", 0);
+SUP_EN(cfg_ms_sup_a5_7, cfg_ms_sup_a5_7_cmd, a5_7, "a5/7", "A5/7", 0);
+SUP_DI(cfg_ms_sup_no_a5_7, cfg_ms_sup_no_a5_7_cmd, a5_7, "a5/7", "A5/7", 0);
+SUP_EN(cfg_ms_sup_p_gsm, cfg_ms_sup_p_gsm_cmd, p_gsm, "p-gsm", "P-GSM (900)",
+       1);
+SUP_DI(cfg_ms_sup_no_p_gsm, cfg_ms_sup_no_p_gsm_cmd, p_gsm, "p-gsm",
+       "P-GSM (900)", 1);
+SUP_EN(cfg_ms_sup_e_gsm, cfg_ms_sup_e_gsm_cmd, e_gsm, "e-gsm", "E-GSM (850)",
+       1);
+SUP_DI(cfg_ms_sup_no_e_gsm, cfg_ms_sup_no_e_gsm_cmd, e_gsm, "e-gsm",
+       "E-GSM (850)", 1);
+SUP_EN(cfg_ms_sup_r_gsm, cfg_ms_sup_r_gsm_cmd, r_gsm, "r-gsm", "R-GSM (850)",
+       1);
+SUP_DI(cfg_ms_sup_no_r_gsm, cfg_ms_sup_no_r_gsm_cmd, r_gsm, "r-gsm",
+       "R-GSM (850)", 1);
+SUP_EN(cfg_ms_sup_dcs, cfg_ms_sup_dcs_cmd, dcs, "dcs", "DCS (1800)", 1);
+SUP_DI(cfg_ms_sup_no_dcs, cfg_ms_sup_no_dcs_cmd, dcs, "dcs", "DCS (1800)", 1);
+SUP_EN(cfg_ms_sup_gsm_850, cfg_ms_sup_gsm_850_cmd, gsm_850, "gsm-850",
+       "GSM 850", 1);
+SUP_DI(cfg_ms_sup_no_gsm_850, cfg_ms_sup_no_gsm_850_cmd, gsm_850, "gsm-850",
+       "GSM 850", 1);
+SUP_EN(cfg_ms_sup_pcs, cfg_ms_sup_pcs_cmd, pcs, "pcs", "PCS (1900)", 1);
+SUP_DI(cfg_ms_sup_no_pcs, cfg_ms_sup_no_pcs_cmd, pcs, "pcs", "PCS (1900)", 1);
+SUP_EN(cfg_ms_sup_gsm_480, cfg_ms_sup_gsm_480_cmd, gsm_480, "gsm-480",
+       "GSM 480", 1);
+SUP_DI(cfg_ms_sup_no_gsm_480, cfg_ms_sup_no_gsm_480_cmd, gsm_480, "gsm-480",
+       "GSM 480", 1);
+SUP_EN(cfg_ms_sup_gsm_450, cfg_ms_sup_gsm_450_cmd, gsm_450, "gsm-450",
+       "GSM 450", 1);
+SUP_DI(cfg_ms_sup_no_gsm_450, cfg_ms_sup_no_gsm_450_cmd, gsm_450, "gsm-450",
+       "GSM 450", 1);
+
+DEFUN(cfg_ms_sup_class_900, cfg_ms_sup_class_900_cmd, "class-900 (1|2|3|4|5)",
+       "Select power class for GSM 900\n"
+       "20 Watts\n"
+       "8 Watts\n"
+       "5 Watts\n"
+       "2 Watts\n"
+       "0.8 Watts")
+{
+       struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
+       struct gsm_support *sup = &ms->support;
+
+       set->class_900 = atoi(argv[0]);
+
+       if (set->class_900 < sup->class_900 && !vty_reading)
+               vty_out(vty, "Note: You selected a higher class than supported "
+                       " by hardware!%s", VTY_NEWLINE);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ms_sup_class_850, cfg_ms_sup_class_850_cmd, "class-850 (1|2|3|4|5)",
+       "Select power class for GSM 850\n"
+       "20 Watts\n"
+       "8 Watts\n"
+       "5 Watts\n"
+       "2 Watts\n"
+       "0.8 Watts")
+{
+       struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
+       struct gsm_support *sup = &ms->support;
+
+       set->class_850 = atoi(argv[0]);
+
+       if (set->class_850 < sup->class_850 && !vty_reading)
+               vty_out(vty, "Note: You selected a higher class than supported "
+                       " by hardware!%s", VTY_NEWLINE);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ms_sup_class_400, cfg_ms_sup_class_400_cmd, "class-400 (1|2|3|4|5)",
+       "Select power class for GSM 400 (480 and 450)\n"
+       "20 Watts\n"
+       "8 Watts\n"
+       "5 Watts\n"
+       "2 Watts\n"
+       "0.8 Watts")
+{
+       struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
+       struct gsm_support *sup = &ms->support;
+
+       set->class_400 = atoi(argv[0]);
+
+       if (set->class_400 < sup->class_400 && !vty_reading)
+               vty_out(vty, "Note: You selected a higher class than supported "
+                       " by hardware!%s", VTY_NEWLINE);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ms_sup_class_dcs, cfg_ms_sup_class_dcs_cmd, "class-dcs (1|2|3)",
+       "Select power class for DCS 1800\n"
+       "1 Watt\n"
+       "0.25 Watts\n"
+       "4 Watts")
+{
+       struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
+       struct gsm_support *sup = &ms->support;
+
+       set->class_dcs = atoi(argv[0]);
+
+       if (((set->class_dcs + 1) & 3) < ((sup->class_dcs + 1) & 3)
+        && !vty_reading)
+               vty_out(vty, "Note: You selected a higher class than supported "
+                       " by hardware!%s", VTY_NEWLINE);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ms_sup_class_pcs, cfg_ms_sup_class_pcs_cmd, "class-pcs (1|2|3)",
+       "Select power class for PCS 1900\n"
+       "1 Watt\n"
+       "0.25 Watts\n"
+       "2 Watts")
+{
+       struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
+       struct gsm_support *sup = &ms->support;
+
+       set->class_pcs = atoi(argv[0]);
+
+       if (((set->class_pcs + 1) & 3) < ((sup->class_pcs + 1) & 3)
+        && !vty_reading)
+               vty_out(vty, "Note: You selected a higher class than supported "
+                       " by hardware!%s", VTY_NEWLINE);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ms_sup_ch_cap, cfg_ms_sup_ch_cap_cmd, "channel-capability "
+       "(sdcch|sdcch+tchf|sdcch+tchf+tchh)",
+       "Select channel capability\nSDCCH only\nSDCCH + TCH/F\nSDCCH + TCH/H")
+{
+       struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
+       struct gsm_support *sup = &ms->support;
+       uint8_t ch_cap;
+
+       if (!strcmp(argv[0], "sdcch+tchf+tchh"))
+               ch_cap = GSM_CAP_SDCCH_TCHF_TCHH;
+       else if (!strcmp(argv[0], "sdcch+tchf"))
+               ch_cap = GSM_CAP_SDCCH_TCHF;
+       else
+               ch_cap = GSM_CAP_SDCCH;
+
+       if (ch_cap > sup->ch_cap && !vty_reading) {
+               vty_out(vty, "You selected an higher capability than supported "
+                       " by hardware!%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       if (ms->started && ch_cap != set->ch_cap
+        && (ch_cap == GSM_CAP_SDCCH || set->ch_cap == GSM_CAP_SDCCH))
+               vty_restart_if_started(vty, ms);
+
+       set->ch_cap = ch_cap;
+
+       return CMD_SUCCESS;
+}
+
+SUP_EN(cfg_ms_sup_full_v1, cfg_ms_sup_full_v1_cmd, full_v1, "full-speech-v1",
+       "Full rate speech V1", 0);
+SUP_DI(cfg_ms_sup_no_full_v1, cfg_ms_sup_no_full_v1_cmd, full_v1,
+       "full-speech-v1", "Full rate speech V1", 0);
+SUP_EN(cfg_ms_sup_full_v2, cfg_ms_sup_full_v2_cmd, full_v2, "full-speech-v2",
+       "Full rate speech V2 (EFR)", 0);
+SUP_DI(cfg_ms_sup_no_full_v2, cfg_ms_sup_no_full_v2_cmd, full_v2,
+       "full-speech-v2", "Full rate speech V2 (EFR)", 0);
+SUP_EN(cfg_ms_sup_full_v3, cfg_ms_sup_full_v3_cmd, full_v3, "full-speech-v3",
+       "Full rate speech V3 (AMR)", 0);
+SUP_DI(cfg_ms_sup_no_full_v3, cfg_ms_sup_no_full_v3_cmd, full_v3,
+       "full-speech-v3", "Full rate speech V3 (AMR)", 0);
+SUP_EN(cfg_ms_sup_half_v1, cfg_ms_sup_half_v1_cmd, half_v1, "half-speech-v1",
+       "Half rate speech V1", 0);
+SUP_DI(cfg_ms_sup_no_half_v1, cfg_ms_sup_no_half_v1_cmd, half_v1,
+       "half-speech-v1", "Half rate speech V1", 0);
+SUP_EN(cfg_ms_sup_half_v3, cfg_ms_sup_half_v3_cmd, half_v3, "half-speech-v3",
+       "Half rate speech V3 (AMR)", 0);
+SUP_DI(cfg_ms_sup_no_half_v3, cfg_ms_sup_no_half_v3_cmd, half_v3,
+       "half-speech-v3", "Half rate speech V3 (AMR)", 0);
+
+DEFUN(cfg_ms_sup_min_rxlev, cfg_ms_sup_min_rxlev_cmd, "min-rxlev <-110--47>",
+       "Set the minimum receive level to select a cell\n"
+       "Minimum receive level from -110 dBm to -47 dBm")
+{
+       struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
+
+       set->min_rxlev_db = atoi(argv[0]);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ms_sup_dsc_max, cfg_ms_sup_dsc_max_cmd, "dsc-max <90-500>",
+       "Set the maximum DSC value. Standard is 90. Increase to make mobile "
+       "more reliable against bad RX signal. This increase the propability "
+       "of missing a paging requests\n"
+       "DSC initial and maximum value (standard is 90)")
+{
+       struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
+
+       set->dsc_max = atoi(argv[0]);
+
+       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")
+{
+       vty->node = TESTSIM_NODE;
+
+       return CMD_SUCCESS;
+}
+
 DEFUN(cfg_test_imsi, cfg_test_imsi_cmd, "imsi IMSI",
        "Set IMSI on test card\n15 digits IMSI")
 {
        struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
        char *error = gsm_check_imsi(argv[0]);
 
        if (error) {
@@ -1140,7 +2300,9 @@ DEFUN(cfg_test_imsi, cfg_test_imsi_cmd, "imsi IMSI",
                return CMD_WARNING;
        }
 
-       strcpy(ms->settings.test_imsi, argv[0]);
+       strcpy(set->test_imsi, argv[0]);
+
+       vty_restart_if_started(vty, ms);
 
        return CMD_SUCCESS;
 }
@@ -1207,8 +2369,9 @@ DEFUN(cfg_test_barr, cfg_test_barr_cmd, "barred-access",
        "Allow access to barred cells")
 {
        struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
 
-       ms->settings.test_barr = 1;
+       set->test_barr = 1;
 
        return CMD_SUCCESS;
 }
@@ -1217,8 +2380,9 @@ DEFUN(cfg_test_no_barr, cfg_test_no_barr_cmd, "no barred-access",
        NO_STR "Deny access to barred cells")
 {
        struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
 
-       ms->settings.test_barr = 0;
+       set->test_barr = 0;
 
        return CMD_SUCCESS;
 }
@@ -1227,16 +2391,22 @@ DEFUN(cfg_test_no_rplmn, cfg_test_no_rplmn_cmd, "no rplmn",
        NO_STR "Unset Registered PLMN")
 {
        struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
+
+       set->test_rplmn_valid = 0;
 
-       ms->settings.test_rplmn_valid = 0;
+       vty_restart_if_started(vty, ms);
 
        return CMD_SUCCESS;
 }
 
-DEFUN(cfg_test_rplmn, cfg_test_rplmn_cmd, "rplmn MCC MNC",
-       "Set Registered PLMN\nMobile Country Code\nMobile Network Code")
+DEFUN(cfg_test_rplmn, cfg_test_rplmn_cmd, "rplmn MCC MNC [LAC] [TMSI]",
+       "Set Registered PLMN\nMobile Country Code\nMobile Network Code\n"
+       "Optionally set locatio area code\n"
+       "Optionally set current assigned TMSI")
 {
        struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
        uint16_t mcc = gsm_input_mcc((char *)argv[0]),
                 mnc = gsm_input_mnc((char *)argv[1]);
 
@@ -1248,9 +2418,21 @@ DEFUN(cfg_test_rplmn, cfg_test_rplmn_cmd, "rplmn MCC MNC",
                vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE);
                return CMD_WARNING;
        }
-       ms->settings.test_rplmn_valid = 1;
-       ms->settings.test_rplmn_mcc = mcc;
-       ms->settings.test_rplmn_mnc = mnc;
+       set->test_rplmn_valid = 1;
+       set->test_rplmn_mcc = mcc;
+       set->test_rplmn_mnc = mnc;
+
+       if (argc >= 3)
+               set->test_lac = strtoul(argv[2], NULL, 16);
+       else
+               set->test_lac = 0xfffe;
+
+       if (argc >= 4)
+               set->test_tmsi = strtoul(argv[3], NULL, 16);
+       else
+               set->test_tmsi = 0xffffffff;
+
+       vty_restart_if_started(vty, ms);
 
        return CMD_SUCCESS;
 }
@@ -1261,16 +2443,81 @@ DEFUN(cfg_test_hplmn, cfg_test_hplmn_cmd, "hplmn-search (everywhere|foreign-coun
        "Search for HPLMN when in a different country")
 {
        struct osmocom_ms *ms = vty->index;
+       struct gsm_settings *set = &ms->settings;
 
        switch (argv[0][0]) {
        case 'e':
-               ms->settings.test_always = 1;
+               set->test_always = 1;
                break;
        case 'f':
-               ms->settings.test_always = 0;
+               set->test_always = 0;
                break;
        }
 
+       vty_restart_if_started(vty, ms);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_shutdown, cfg_ms_no_shutdown_cmd, "no shutdown",
+       NO_STR "Activate and run MS")
+{
+       struct osmocom_ms *ms = vty->index, *tmp;
+       int rc;
+
+       if (ms->shutdown != 2)
+               return CMD_SUCCESS;
+
+       llist_for_each_entry(tmp, &ms_list, entity) {
+               if (tmp->shutdown == 2)
+                       continue;
+               if (!strcmp(ms->settings.layer2_socket_path,
+                               tmp->settings.layer2_socket_path)) {
+                       vty_out(vty, "Cannot start MS '%s', because MS '%s' "
+                               "use the same layer2-socket.%sPlease shutdown "
+                               "MS '%s' first.%s", ms->name, tmp->name,
+                               VTY_NEWLINE, tmp->name, VTY_NEWLINE);
+                       return CMD_WARNING;
+               }
+               if (!strcmp(ms->settings.sap_socket_path,
+                               tmp->settings.sap_socket_path)) {
+                       vty_out(vty, "Cannot start MS '%s', because MS '%s' "
+                               "use the same sap-socket.%sPlease shutdown "
+                               "MS '%s' first.%s", ms->name, tmp->name,
+                               VTY_NEWLINE, tmp->name, VTY_NEWLINE);
+                       return CMD_WARNING;
+               }
+       }
+
+       rc = mobile_init(ms);
+       if (rc < 0) {
+               vty_out(vty, "Connection to layer 1 failed!%s",
+                       VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(cfg_shutdown, cfg_ms_shutdown_cmd, "shutdown",
+       "Shut down and deactivate MS")
+{
+       struct osmocom_ms *ms = vty->index;
+
+       if (ms->shutdown == 0)
+               mobile_exit(ms, 0);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(cfg_shutdown_force, cfg_ms_shutdown_force_cmd, "shutdown force",
+       "Shut down and deactivate MS\nDo not perform IMSI detach")
+{
+       struct osmocom_ms *ms = vty->index;
+
+       if (ms->shutdown <= 1)
+               mobile_exit(ms, 1);
+
        return CMD_SUCCESS;
 }
 
@@ -1282,6 +2529,7 @@ enum node_type ms_vty_go_parent(struct vty *vty)
                vty->index = NULL;
                break;
        case TESTSIM_NODE:
+       case SUPPORT_NODE:
                vty->node = MS_NODE;
                break;
        default:
@@ -1301,6 +2549,7 @@ gDEFUN(ournode_exit,
                vty->index = NULL;
                break;
        case TESTSIM_NODE:
+       case SUPPORT_NODE:
                vty->node = MS_NODE;
                break;
        default:
@@ -1322,6 +2571,7 @@ gDEFUN(ournode_end,
        case VTY_NODE:
        case MS_NODE:
        case TESTSIM_NODE:
+       case SUPPORT_NODE:
                vty_config_unlock(vty);
                vty->node = ENABLE_NODE;
                vty->index = NULL;
@@ -1333,19 +2583,31 @@ gDEFUN(ournode_end,
        return CMD_SUCCESS;
 }
 
+DEFUN(off, off_cmd, "off",
+       "Turn mobiles off (shutdown) and exit")
+{
+       osmo_signal_dispatch(SS_GLOBAL, S_GLOBAL_SHUTDOWN, NULL);
+
+       return CMD_SUCCESS;
+}
+
+#define SUP_NODE(item) \
+       install_element(SUPPORT_NODE, &cfg_ms_sup_item_cmd);
+
 int ms_vty_init(void)
 {
        install_element_ve(&show_ms_cmd);
        install_element_ve(&show_subscr_cmd);
        install_element_ve(&show_support_cmd);
-       install_element_ve(&show_states_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);
        install_element_ve(&monitor_network_cmd);
        install_element_ve(&no_monitor_network_cmd);
+       install_element(ENABLE_NODE, &off_cmd);
 
        install_element(ENABLE_NODE, &sim_test_cmd);
        install_element(ENABLE_NODE, &sim_reader_cmd);
@@ -1355,23 +2617,39 @@ int ms_vty_init(void)
        install_element(ENABLE_NODE, &sim_enable_pin_cmd);
        install_element(ENABLE_NODE, &sim_change_pin_cmd);
        install_element(ENABLE_NODE, &sim_unblock_pin_cmd);
+       install_element(ENABLE_NODE, &sim_lai_cmd);
        install_element(ENABLE_NODE, &network_search_cmd);
        install_element(ENABLE_NODE, &network_show_cmd);
        install_element(ENABLE_NODE, &network_select_cmd);
        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);
+#endif
        install_element(CONFIG_NODE, &cfg_gps_device_cmd);
        install_element(CONFIG_NODE, &cfg_gps_baud_cmd);
        install_element(CONFIG_NODE, &cfg_gps_enable_cmd);
        install_element(CONFIG_NODE, &cfg_no_gps_enable_cmd);
 
+       install_element(CONFIG_NODE, &cfg_hide_default_cmd);
+       install_element(CONFIG_NODE, &cfg_no_hide_default_cmd);
+
        install_element(CONFIG_NODE, &cfg_ms_cmd);
+       install_element(CONFIG_NODE, &cfg_ms_create_cmd);
+       install_element(CONFIG_NODE, &cfg_ms_rename_cmd);
+       install_element(CONFIG_NODE, &cfg_no_ms_cmd);
        install_element(CONFIG_NODE, &ournode_end_cmd);
-       install_node(&ms_node, config_write_ms);
+       install_node(&ms_node, config_write);
        install_default(MS_NODE);
        install_element(MS_NODE, &ournode_exit_cmd);
        install_element(MS_NODE, &ournode_end_cmd);
+       install_element(MS_NODE, &cfg_ms_show_this_cmd);
+       install_element(MS_NODE, &cfg_ms_layer2_cmd);
+       install_element(MS_NODE, &cfg_ms_sap_cmd);
        install_element(MS_NODE, &cfg_ms_sim_cmd);
        install_element(MS_NODE, &cfg_ms_mode_cmd);
        install_element(MS_NODE, &cfg_ms_imei_cmd);
@@ -1381,11 +2659,12 @@ int ms_vty_init(void)
        install_element(MS_NODE, &cfg_ms_emerg_imsi_cmd);
        install_element(MS_NODE, &cfg_ms_cw_cmd);
        install_element(MS_NODE, &cfg_ms_no_cw_cmd);
+       install_element(MS_NODE, &cfg_ms_auto_answer_cmd);
+       install_element(MS_NODE, &cfg_ms_no_auto_answer_cmd);
        install_element(MS_NODE, &cfg_ms_clip_cmd);
        install_element(MS_NODE, &cfg_ms_clir_cmd);
        install_element(MS_NODE, &cfg_ms_no_clip_cmd);
        install_element(MS_NODE, &cfg_ms_no_clir_cmd);
-       install_element(MS_NODE, &cfg_ms_testsim_cmd);
        install_element(MS_NODE, &cfg_ms_tx_power_cmd);
        install_element(MS_NODE, &cfg_ms_tx_power_val_cmd);
        install_element(MS_NODE, &cfg_ms_sim_delay_cmd);
@@ -1394,7 +2673,75 @@ int ms_vty_init(void)
        install_element(MS_NODE, &cfg_ms_no_stick_cmd);
        install_element(MS_NODE, &cfg_ms_lupd_cmd);
        install_element(MS_NODE, &cfg_ms_no_lupd_cmd);
-
+       install_element(MS_NODE, &cfg_ms_codec_full_cmd);
+       install_element(MS_NODE, &cfg_ms_codec_full_pref_cmd);
+       install_element(MS_NODE, &cfg_ms_codec_half_cmd);
+       install_element(MS_NODE, &cfg_ms_codec_half_pref_cmd);
+       install_element(MS_NODE, &cfg_ms_no_codec_half_cmd);
+       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);
+       install_element(SUPPORT_NODE, &ournode_exit_cmd);
+       install_element(SUPPORT_NODE, &ournode_end_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_dtmf_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_no_dtmf_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_sms_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_no_sms_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_a5_1_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_no_a5_1_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_a5_2_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_no_a5_2_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_a5_3_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_no_a5_3_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_a5_4_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_no_a5_4_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_a5_5_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_no_a5_5_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_a5_6_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_no_a5_6_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_a5_7_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_no_a5_7_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_p_gsm_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_no_p_gsm_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_e_gsm_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_no_e_gsm_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_r_gsm_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_no_r_gsm_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_dcs_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_no_dcs_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_gsm_850_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_no_gsm_850_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_pcs_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_no_pcs_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_gsm_480_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_no_gsm_480_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_gsm_450_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_no_gsm_450_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_class_900_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_class_dcs_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_class_850_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_class_pcs_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_class_400_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_ch_cap_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_full_v1_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_no_full_v1_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_full_v2_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_no_full_v2_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_full_v3_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_no_full_v3_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_half_v1_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_no_half_v1_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_half_v3_cmd);
+       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);
@@ -1407,6 +2754,9 @@ int ms_vty_init(void)
        install_element(TESTSIM_NODE, &cfg_test_no_rplmn_cmd);
        install_element(TESTSIM_NODE, &cfg_test_rplmn_cmd);
        install_element(TESTSIM_NODE, &cfg_test_hplmn_cmd);
+       install_element(MS_NODE, &cfg_ms_shutdown_cmd);
+       install_element(MS_NODE, &cfg_ms_shutdown_force_cmd);
+       install_element(MS_NODE, &cfg_ms_no_shutdown_cmd);
 
        return 0;
 }