X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=src%2Fhost%2Flayer23%2Fsrc%2Fgsm322.c;h=9ffc769311e542ab014af9f0b2024be4c189c734;hb=38c6b4b35a4400a62b0e534c9326154bb4f232f9;hp=b6694408f205b2030bb3508a3f5677cb8c4c151d;hpb=516917e83645fad3b72623817c01190cc773e0e6;p=osmocom-bb.git diff --git a/src/host/layer23/src/gsm322.c b/src/host/layer23/src/gsm322.c old mode 100755 new mode 100644 index b669440..9ffc769 --- a/src/host/layer23/src/gsm322.c +++ b/src/host/layer23/src/gsm322.c @@ -36,13 +36,18 @@ #include #include #include +#include extern void *l23_ctx; static void gsm322_cs_timeout(void *arg); +static void gsm322_cs_loss(void *arg); static int gsm322_cs_select(struct osmocom_ms *ms, int any); static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg); +#warning HACKING!!! +int hack; + /* * notes */ @@ -63,6 +68,19 @@ static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg); * if the cell is 'suitable' and 'allowable' to 'camp' on. * * This list is also used to generate a list of available networks. + * + * The states are: + * + * - cs->list[0..1023].xxx for each cell, where + * - flags and rxlev_db are used to store outcome of cell scanning process + * - sysinfo pointing to sysinfo memory, allocated temporarily + * - cs->selected and cs->sel_* states of the current / last selected cell. + * + * + * There is a special state: GSM322_PLMN_SEARCH + * It is used to search for all cells, to find the HPLMN. This is triggered + * by a timer. Also it is used before selecting PLMN from list. + * */ /* PLMN selection process @@ -138,9 +156,8 @@ static const struct value_string gsm322_event_names[] = { { GSM322_EVENT_REG_SUCCESS, "EVENT_REG_SUCCESS" }, { GSM322_EVENT_NEW_PLMN, "EVENT_NEW_PLMN" }, { GSM322_EVENT_ON_PLMN, "EVENT_ON_PLMN" }, - { GSM322_EVENT_HPLMN_SEARCH, "EVENT_HPLMN_SEARCH" }, - { GSM322_EVENT_HPLMN_FOUND, "EVENT_HPLMN_FOUND" }, - { GSM322_EVENT_HPLMN_NOT_FOUND, "EVENT_HPLMN_NOT_FOUND" }, + { GSM322_EVENT_PLMN_SEARCH_START,"EVENT_PLMN_SEARCH_START" }, + { GSM322_EVENT_PLMN_SEARCH_END, "EVENT_PLMN_SEARCH_END" }, { GSM322_EVENT_USER_RESEL, "EVENT_USER_RESEL" }, { GSM322_EVENT_PLMN_AVAIL, "EVENT_PLMN_AVAIL" }, { GSM322_EVENT_CHOSE_PLMN, "EVENT_CHOSE_PLMN" }, @@ -152,6 +169,7 @@ static const struct value_string gsm322_event_names[] = { { GSM322_EVENT_RET_IDLE, "EVENT_RET_IDLE" }, { GSM322_EVENT_CELL_RESEL, "EVENT_CELL_RESEL" }, { GSM322_EVENT_SYSINFO, "EVENT_SYSINFO" }, + { GSM322_EVENT_HPLMN_SEARCH, "EVENT_HPLMN_SEARCH" }, { 0, NULL } }; @@ -201,69 +219,35 @@ int gsm322_cs_sendmsg(struct osmocom_ms *ms, struct msgb *msg) * support */ -/* del forbidden PLMN */ -int gsm322_del_forbidden_plmn(struct osmocom_ms *ms, uint16_t mcc, - uint16_t mnc) +static int gsm322_sync_to_cell(struct osmocom_ms *ms, struct gsm322_cellsel *cs) { - struct gsm_subscriber *subscr = &ms->subscr; - struct gsm_sub_plmn_na *na; - - llist_for_each_entry(na, &subscr->plmn_na, entry) { - if (na->mcc == mcc && na->mnc == mnc) { - LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden " - "PLMNs (mcc=%03d, mnc=%02d)\n", mcc, mnc); - llist_del(&na->entry); - talloc_free(na); -#ifdef TODO - update plmn not allowed list on sim -#endif - return 0; - } - } - - return -EINVAL; + return l1ctl_tx_fbsb_req(ms, cs->arfcn, + L1CTL_FBSB_F_FB01SB, 100, 0, + CCCH_MODE_COMBINED); } -/* add forbidden PLMN */ -int gsm322_add_forbidden_plmn(struct osmocom_ms *ms, uint16_t mcc, - uint16_t mnc, uint8_t cause) +static void gsm322_unselect_cell(struct gsm322_cellsel *cs) { - struct gsm_subscriber *subscr = &ms->subscr; - struct gsm_sub_plmn_na *na; - - /* don't add Home PLMN */ - if (subscr->sim_valid && mcc == subscr->mcc && mnc == subscr->mnc) - return -EINVAL; - - LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden PLMNs " - "(mcc=%03d, mnc=%02d)\n", mcc, mnc); - na = talloc_zero(l23_ctx, struct gsm_sub_plmn_na); - if (!na) - return -ENOMEM; - na->mcc = mcc; - na->mnc = mnc; - na->cause = cause; - llist_add_tail(&na->entry, &subscr->plmn_na); - -#ifdef TODO - update plmn not allowed list on sim -#endif - - return 0; + cs->selected = 0; + cs->si = NULL; + memset(&cs->sel_si, 0, sizeof(cs->sel_si)); + cs->sel_mcc = cs->sel_mnc = cs->sel_lac = cs->sel_id = 0; } -/* search forbidden PLMN */ -int gsm322_is_forbidden_plmn(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc) +/* print to DCS logging */ +static void print_dcs(void *priv, const char *fmt, ...) { - struct gsm_subscriber *subscr = &ms->subscr; - struct gsm_sub_plmn_na *na; + char buffer[1000]; + va_list args; - llist_for_each_entry(na, &subscr->plmn_na, entry) { - if (na->mcc == mcc && na->mnc == mnc) - return 1; - } + va_start(args, fmt); + vsnprintf(buffer, sizeof(buffer) - 1, fmt, args); + buffer[sizeof(buffer) - 1] = '\0'; + va_end(args); - return 0; + if (buffer[0]) +// LOGP(DCS, LOGL_INFO, "%s", buffer); + printf("%s", buffer); } /* del forbidden LA */ @@ -276,8 +260,8 @@ int gsm322_del_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, llist_for_each_entry(la, &plmn->forbidden_la, entry) { if (la->mcc == mcc && la->mnc == mnc && la->lac == lac) { LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden " - "LAs (mcc=%03d, mnc=%02d, lac=%04x)\n", - mcc, mnc, lac); + "LAs (mcc=%s, mnc=%s, lac=%04x)\n", + gsm_print_mcc(mcc), gsm_print_mnc(mnc), lac); llist_del(&la->entry); talloc_free(la); return 0; @@ -295,7 +279,8 @@ int gsm322_add_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, struct gsm322_la_list *la; LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden LAs " - "(mcc=%03d, mnc=%02d, lac=%04x)\n", mcc, mnc, lac); + "(mcc=%s, mnc=%s, lac=%04x)\n", gsm_print_mcc(mcc), + gsm_print_mnc(mnc), lac); la = talloc_zero(l23_ctx, struct gsm322_la_list); if (!la) return -ENOMEM; @@ -341,6 +326,37 @@ static struct gsm322_ba_list *gsm322_find_ba_list(struct gsm322_cellsel *cs, return ba_found; } +/* search available PLMN */ +int gsm322_is_plmn_avail(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc) +{ + int i; + + for (i = 0; i <= 1023; i++) { + if (cs->list[i].sysinfo + && cs->list[i].sysinfo->mcc == mcc + && cs->list[i].sysinfo->mnc == mnc) + return 1; + } + + return 0; +} + +/* search available HPLMN */ +int gsm322_is_hplmn_avail(struct gsm322_cellsel *cs, char *imsi) +{ + int i; + + for (i = 0; i <= 1023; i++) { + if (cs->list[i].sysinfo + && gsm_match_mnc(cs->list[i].sysinfo->mcc, + cs->list[i].sysinfo->mnc, imsi)) + return 1; + } + + return 0; +} + +/* del forbidden LA */ /* * timer */ @@ -380,7 +396,7 @@ static void stop_plmn_timer(struct gsm322_plmn *plmn) } /* start cell selection timer */ -static void start_cs_timer(struct gsm322_cellsel *cs, int sec, int micro) +void start_cs_timer(struct gsm322_cellsel *cs, int sec, int micro) { LOGP(DCS, LOGL_INFO, "Starting CS timer with %d seconds.\n", sec); cs->timer.cb = gsm322_cs_timeout; @@ -388,6 +404,30 @@ static void start_cs_timer(struct gsm322_cellsel *cs, int sec, int micro) bsc_schedule_timer(&cs->timer, sec, micro); } +/* start loss timer */ +void start_loss_timer(struct gsm322_cellsel *cs, int sec, int micro) +{ + /* update timer */ + cs->timer.cb = gsm322_cs_loss; + cs->timer.data = cs; + if (bsc_timer_pending(&cs->timer)) { + struct timeval current_time; + unsigned long long currentTime; + + gettimeofday(¤t_time, NULL); + currentTime = current_time.tv_sec * 1000000LL + + current_time.tv_usec; + currentTime += sec * 1000000LL + micro; + cs->timer.timeout.tv_sec = currentTime / 1000000LL; + cs->timer.timeout.tv_usec = currentTime % 1000000LL; + + return; + } + + LOGP(DCS, LOGL_INFO, "Starting loss CS timer with %d seconds.\n", sec); + bsc_schedule_timer(&cs->timer, sec, micro); +} + /* stop cell selection timer */ static void stop_cs_timer(struct gsm322_cellsel *cs) { @@ -401,44 +441,45 @@ static void stop_cs_timer(struct gsm322_cellsel *cs) * state change */ -static const char *plmn_a_state_names[] = { - "A0_NULL", - "A1_TRYING_RPLMN", - "A2_ON_PLMN", - "A3_TRYING_PLMN", - "A4_WAIT_FOR_PLMN", - "A5_HPLMN", - "A6_NO_SIM" +const char *plmn_a_state_names[] = { + "A0 null", + "A1 trying RPLMN", + "A2 on PLMN", + "A3 trying PLMN", + "A4 wait for PLMN to appear", + "A5 HPLMN search", + "A6 no SIM inserted" }; -static const char *plmn_m_state_names[] = { - "M1_NULL", - "M1_TRYING_RPLMN", - "M2_ON_PLMN", - "M3_NOT_ON_PLMN", - "M4_TRYING_PLMN", - "M5_NO_SIM" +const char *plmn_m_state_names[] = { + "M0 null", + "M1 trying RPLMN", + "M2 on PLMN", + "M3 not on PLMN", + "M4 trying PLMN", + "M5 no SIM inserted" }; -static const char *cs_state_names[] = { - "C0_NULL", - "C1_NORMAL_CELL_SEL", - "C2_STORED_CELL_SEL", - "C3_CAMPED_NORMALLY", - "C4_NORMAL_CELL_RESEL", - "C5_CHOOSE_CELL", - "C6_ANY_CELL_SEL", - "C7_CAMPED_ANY_CELL", - "C8_ANY_CELL_RESEL", - "C9_CHOOSE_ANY_CELL", - "HPLMN_SEARCH" +const char *cs_state_names[] = { + "C0 null", + "C1 normal cell selection", + "C2 stored cell selection", + "C3 camped normally", + "C4 normal cell re-selection", + "C5 choose cell", + "C6 any cell selection", + "C7 camped on any cell", + "C8 any cell re-selection", + "C9 choose any cell", + "PLMN search", + "HPLMN search" }; /* new automatic PLMN search state */ static void new_a_state(struct gsm322_plmn *plmn, int state) { - if (plmn->mode != PLMN_MODE_AUTO) { + if (plmn->ms->settings.plmn_mode != PLMN_MODE_AUTO) { LOGP(DPLMN, LOGL_FATAL, "not in auto mode, please fix!\n"); return; } @@ -448,7 +489,7 @@ static void new_a_state(struct gsm322_plmn *plmn, int state) if (state < 0 || state >= (sizeof(plmn_a_state_names) / sizeof(char *))) return; - LOGP(DPLMN, LOGL_INFO, "new state %s -> %s\n", + LOGP(DPLMN, LOGL_INFO, "new state '%s' -> '%s'\n", plmn_a_state_names[plmn->state], plmn_a_state_names[state]); plmn->state = state; @@ -457,7 +498,7 @@ static void new_a_state(struct gsm322_plmn *plmn, int state) /* new manual PLMN search state */ static void new_m_state(struct gsm322_plmn *plmn, int state) { - if (plmn->mode != PLMN_MODE_MANUAL) { + if (plmn->ms->settings.plmn_mode != PLMN_MODE_MANUAL) { LOGP(DPLMN, LOGL_FATAL, "not in manual mode, please fix!\n"); return; } @@ -465,7 +506,7 @@ static void new_m_state(struct gsm322_plmn *plmn, int state) if (state < 0 || state >= (sizeof(plmn_m_state_names) / sizeof(char *))) return; - LOGP(DPLMN, LOGL_INFO, "new state %s -> %s\n", + LOGP(DPLMN, LOGL_INFO, "new state '%s' -> '%s'\n", plmn_m_state_names[plmn->state], plmn_m_state_names[state]); plmn->state = state; @@ -477,7 +518,7 @@ static void new_c_state(struct gsm322_cellsel *cs, int state) if (state < 0 || state >= (sizeof(cs_state_names) / sizeof(char *))) return; - LOGP(DCS, LOGL_INFO, "new state %s -> %s\n", + LOGP(DCS, LOGL_INFO, "new state '%s' -> '%s'\n", cs_state_names[cs->state], cs_state_names[state]); /* stop cell selection timer, if running */ @@ -540,16 +581,18 @@ static int gsm322_sort_list(struct osmocom_ms *ms) /* Create a temporary list of all networks */ INIT_LLIST_HEAD(&temp_list); for (i = 0; i <= 1023; i++) { - if (!(cs->list[i].flags & GSM322_CS_FLAG_TEMP_AA)) + if (!(cs->list[i].flags & GSM322_CS_FLAG_TEMP_AA) + || !cs->list[i].sysinfo) continue; /* search if network has multiple cells */ found = NULL; llist_for_each_entry(temp, &temp_list, entry) { - if (temp->mcc == cs->list[i].mcc - && temp->mnc == cs->list[i].mnc) + if (temp->mcc == cs->list[i].sysinfo->mcc + && temp->mnc == cs->list[i].sysinfo->mnc) { found = temp; break; + } } /* update or create */ if (found) { @@ -559,24 +602,24 @@ static int gsm322_sort_list(struct osmocom_ms *ms) temp = talloc_zero(l23_ctx, struct gsm322_plmn_list); if (!temp) return -ENOMEM; - temp->mcc = cs->list[i].mcc; - temp->mnc = cs->list[i].mnc; + temp->mcc = cs->list[i].sysinfo->mcc; + temp->mnc = cs->list[i].sysinfo->mnc; temp->rxlev_db = cs->list[i].rxlev_db; llist_add_tail(&temp->entry, &temp_list); } } - /* move Home PLMN, if in list */ + /* move Home PLMN, if in list, else add it */ if (subscr->sim_valid) { found = NULL; llist_for_each_entry(temp, &temp_list, entry) { - if (temp->mcc == subscr->mcc - && temp->mnc == subscr->mnc) { + if (gsm_match_mnc(temp->mcc, temp->mnc, subscr->imsi)) { found = temp; break; } } if (found) { + /* move */ llist_del(&found->entry); llist_add_tail(&found->entry, &plmn->sorted_plmn); } @@ -647,13 +690,16 @@ static int gsm322_sort_list(struct osmocom_ms *ms) break; } } - LOGP(DPLMN, LOGL_INFO, "Crating Sorted PLMN list. " - "(%02d: mcc=%03d mnc=%02d allowed=%s rx-lev=%d)\n", - i, temp->mcc, temp->mnc, (temp->cause) ? "no ":"yes", + LOGP(DPLMN, LOGL_INFO, "Creating Sorted PLMN list. " + "(%02d: mcc=%s mnc=%s allowed=%s rx-lev=%d)\n", + i, gsm_print_mcc(temp->mcc), + gsm_print_mnc(temp->mnc), (temp->cause) ? "no ":"yes", temp->rxlev_db); i++; } + gsm322_dump_sorted_plmn(ms); + return 0; } @@ -667,20 +713,12 @@ static int gsm322_a_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg) struct gsm322_plmn *plmn = &ms->plmn; struct gsm_subscriber *subscr = &ms->subscr; - /* set last registered PLMN */ - subscr->plmn_valid = 1; - subscr->plmn_mcc = plmn->mcc; - subscr->plmn_mnc = plmn->mnc; -#ifdef TODO - store on sim -#endif - new_a_state(plmn, GSM322_A2_ON_PLMN); /* start timer, if on VPLMN of home country OR special case */ - if ((plmn->mcc == subscr->mcc && plmn->mcc != subscr->mnc) - || (subscr->always_search_hplmn && (plmn->mcc != subscr->mnc - || plmn->mcc != subscr->mnc))) { + if (!gsm_match_mnc(plmn->mcc, plmn->mnc, subscr->imsi) + && (subscr->always_search_hplmn + || gsm_match_mcc(plmn->mcc, subscr->imsi))) { if (subscr->sim_valid && subscr->t6m_hplmn) start_plmn_timer(plmn, subscr->t6m_hplmn * 360); else @@ -694,9 +732,11 @@ static int gsm322_a_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg) /* indicate selected PLMN */ static int gsm322_a_indicate_selected(struct osmocom_ms *ms, struct msgb *msg) { -#ifdef TODO - indicate selected plmn to user -#endif + struct gsm322_plmn *plmn = &ms->plmn; + + vty_notify(ms, NULL); + vty_notify(ms, "Selected Network: %s, %s\n", + gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc)); return gsm322_a_go_on_plmn(ms, msg); } @@ -706,24 +746,16 @@ static int gsm322_a_no_more_plmn(struct osmocom_ms *ms, struct msgb *msg) { struct gsm322_plmn *plmn = &ms->plmn; struct gsm322_cellsel *cs = &ms->cellsel; - struct gsm_subscriber *subscr = &ms->subscr; struct msgb *nmsg; int found; - /* any PLMN available */ + /* any allowable PLMN available? */ + plmn->mcc = plmn->mnc = 0; found = gsm322_cs_select(ms, 0); /* if no PLMN in list */ if (found < 0) { - if (subscr->plmn_valid) { - LOGP(DPLMN, LOGL_INFO, "Select RPLMN.\n"); - plmn->mcc = subscr->plmn_mcc; - plmn->mnc = subscr->plmn_mnc; - } else { - LOGP(DPLMN, LOGL_INFO, "Select HPLMN.\n"); - plmn->mcc = subscr->mcc; - plmn->mnc = subscr->mnc; - } + LOGP(DPLMN, LOGL_INFO, "Not any PLNs allowable.\n"); new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN); @@ -739,11 +771,11 @@ static int gsm322_a_no_more_plmn(struct osmocom_ms *ms, struct msgb *msg) } /* select first PLMN in list */ - plmn->mcc = cs->list[found].mcc; - plmn->mnc = cs->list[found].mnc; + plmn->mcc = cs->list[found].sysinfo->mcc; + plmn->mnc = cs->list[found].sysinfo->mnc; - LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%03d mnc=%02d %s, %s)\n", - plmn->mcc, plmn->mnc, + LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%s mnc=%s %s, %s)\n", + gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc)); /* indicate New PLMN */ @@ -760,6 +792,7 @@ static int gsm322_a_no_more_plmn(struct osmocom_ms *ms, struct msgb *msg) static int gsm322_a_sel_first_plmn(struct osmocom_ms *ms, struct msgb *msg) { struct gsm322_plmn *plmn = &ms->plmn; + struct gsm_subscriber *subscr = &ms->subscr; struct msgb *nmsg; struct gsm322_plmn_list *plmn_entry; struct gsm322_plmn_list *plmn_first = NULL; @@ -771,10 +804,13 @@ static int gsm322_a_sel_first_plmn(struct osmocom_ms *ms, struct msgb *msg) /* select first entry */ i = 0; llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) { - /* if RPLMN is HPLMN, we skip that */ - if (plmn->state == GSM322_A1_TRYING_RPLMN + /* if last selected PLMN was HPLMN, we skip that */ + if (gsm_match_mnc(plmn_entry->mcc, plmn_entry->mnc, + subscr->imsi) && plmn_entry->mcc == plmn->mcc && plmn_entry->mnc == plmn->mnc) { + LOGP(DPLMN, LOGL_INFO, "Skip HPLMN, because it was " + "previously selected.\n"); i++; continue; } @@ -783,6 +819,9 @@ static int gsm322_a_sel_first_plmn(struct osmocom_ms *ms, struct msgb *msg) plmn_first = plmn_entry; break; } + LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%03d), because it " + "not allowed (cause %d).\n", plmn_entry->mcc, + plmn_entry->mnc, plmn_entry->cause); i++; } plmn->plmn_curr = i; @@ -795,9 +834,10 @@ static int gsm322_a_sel_first_plmn(struct osmocom_ms *ms, struct msgb *msg) return 0; } - LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%03d " - "mnc=%02d %s, %s)\n", plmn->plmn_curr, plmn_first->mcc, - plmn_first->mnc, gsm_get_mcc(plmn_first->mcc), + LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%s " + "mnc=%s %s, %s)\n", plmn->plmn_curr, + gsm_print_mcc(plmn_first->mcc), gsm_print_mnc(plmn_first->mnc), + gsm_get_mcc(plmn_first->mcc), gsm_get_mnc(plmn_first->mcc, plmn_first->mnc)); /* set current network */ @@ -838,6 +878,9 @@ static int gsm322_a_sel_next_plmn(struct osmocom_ms *ms, struct msgb *msg) plmn_next = plmn_entry; break; } + LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%03d), because it " + "not allowed (cause %d).\n", plmn_entry->mcc, + plmn_entry->mnc, plmn_entry->cause); i++; } plmn->plmn_curr = i; @@ -850,9 +893,9 @@ static int gsm322_a_sel_next_plmn(struct osmocom_ms *ms, struct msgb *msg) } - LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%03d " - "mnc=%02d %s, %s)\n", plmn->plmn_curr, plmn_next->mcc, - plmn_next->mnc, + LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%s " + "mnc=%s %s, %s)\n", plmn->plmn_curr, + gsm_print_mcc(plmn_next->mcc), gsm_print_mnc(plmn_next->mnc), gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc)); /* set next network */ @@ -871,12 +914,25 @@ static int gsm322_a_sel_next_plmn(struct osmocom_ms *ms, struct msgb *msg) } /* User re-selection event */ -static int gsm322_a_user_reselection(struct osmocom_ms *ms, struct msgb *msg) +static int gsm322_a_user_resel(struct osmocom_ms *ms, struct msgb *msg) { struct gsm322_plmn *plmn = &ms->plmn; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm322_plmn_list *plmn_entry; struct gsm322_plmn_list *plmn_found = NULL; + if (!subscr->sim_valid) { + return 0; + } + + /* try again later, if not idle */ + if (rr->state != GSM48_RR_ST_IDLE) { + LOGP(DPLMN, LOGL_INFO, "Not idle, rejecting.\n"); + + return 0; + } + /* search current PLMN in list */ llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) { if (plmn_entry->mcc == plmn->mcc @@ -914,7 +970,7 @@ static int gsm322_a_plmn_avail(struct osmocom_ms *ms, struct msgb *msg) /* go On PLMN */ plmn->mcc = gm->mcc; plmn->mnc = gm->mnc; - LOGP(DPLMN, LOGL_INFO, "HPLMN became available.\n"); + LOGP(DPLMN, LOGL_INFO, "RPLMN became available.\n"); return gsm322_a_go_on_plmn(ms, msg); } else { /* select first PLMN in list */ @@ -930,16 +986,20 @@ static int gsm322_a_loss_of_radio(struct osmocom_ms *ms, struct msgb *msg) struct gsm322_plmn *plmn = &ms->plmn; struct gsm322_cellsel *cs = &ms->cellsel; int found; + struct msgb *nmsg; /* any PLMN available */ found = gsm322_cs_select(ms, 0); /* if PLMN in list */ if (found >= 0) { - LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%03d mnc=%02d " - "%s, %s)\n", cs->list[found].mcc, cs->list[found].mnc, - gsm_get_mcc(cs->list[found].mcc), - gsm_get_mnc(cs->list[found].mcc, cs->list[found].mnc)); + LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%s mnc=%s " + "%s, %s)\n", gsm_print_mcc( + cs->list[found].sysinfo->mcc), + gsm_print_mnc(cs->list[found].sysinfo->mnc), + gsm_get_mcc(cs->list[found].sysinfo->mcc), + gsm_get_mnc(cs->list[found].sysinfo->mcc, + cs->list[found].sysinfo->mnc)); return gsm322_a_sel_first_plmn(ms, msg); } @@ -949,6 +1009,12 @@ static int gsm322_a_loss_of_radio(struct osmocom_ms *ms, struct msgb *msg) new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN); + /* Tell cell selection process to handle "no cell found". */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + return 0; } @@ -960,6 +1026,7 @@ static int gsm322_a_switch_on(struct osmocom_ms *ms, struct msgb *msg) struct msgb *nmsg; if (!subscr->sim_valid) { + LOGP(DSUM, LOGL_INFO, "SIM is removed\n"); LOGP(DPLMN, LOGL_INFO, "Switch on without SIM.\n"); new_a_state(plmn, GSM322_A6_NO_SIM); @@ -972,9 +1039,13 @@ static int gsm322_a_switch_on(struct osmocom_ms *ms, struct msgb *msg) plmn->mcc = subscr->plmn_mcc; plmn->mnc = subscr->plmn_mnc; - LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%03d mnc=%02d " - "%s, %s)\n", plmn->mcc, plmn->mnc, - gsm_get_mcc(plmn->mcc), + LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN " + "(mcc=%s mnc=%s %s, %s)\n", gsm_print_mcc(plmn->mcc), + gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc), + gsm_get_mnc(plmn->mcc, plmn->mnc)); + LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%s mnc=%s " + "%s, %s)\n", gsm_print_mcc(plmn->mcc), + gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc)); new_a_state(plmn, GSM322_A1_TRYING_RPLMN); @@ -988,8 +1059,16 @@ static int gsm322_a_switch_on(struct osmocom_ms *ms, struct msgb *msg) return 0; } - /* select first PLMN in list */ - return gsm322_a_sel_first_plmn(ms, msg); + /* initiate search at cell selection */ + LOGP(DSUM, LOGL_INFO, "Search for network\n"); + LOGP(DPLMN, LOGL_INFO, "Switch on, start PLMN search first.\n"); + + nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return 0; } /* MS is switched off */ @@ -1002,6 +1081,12 @@ static int gsm322_a_switch_off(struct osmocom_ms *ms, struct msgb *msg) return 0; } +static int gsm322_a_sim_insert(struct osmocom_ms *ms, struct msgb *msg) +{ + LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n"); + return 0; +} + /* SIM is removed */ static int gsm322_a_sim_removed(struct osmocom_ms *ms, struct msgb *msg) { @@ -1025,15 +1110,17 @@ static int gsm322_a_roaming_na(struct osmocom_ms *ms, struct msgb *msg) } /* On VPLMN of home country and timeout occurs */ -static int gsm322_a_hplmn_search(struct osmocom_ms *ms, struct msgb *msg) +static int gsm322_a_hplmn_search_start(struct osmocom_ms *ms, struct msgb *msg) { + struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm322_plmn *plmn = &ms->plmn; struct gsm322_cellsel *cs = &ms->cellsel; struct msgb *nmsg; - /* try again later, if not idle */ - if (cs->state != GSM322_C3_CAMPED_NORMALLY) { - LOGP(DPLMN, LOGL_INFO, "Not camping normal, wait some more.\n"); + /* try again later, if not idle and not camping */ + if (rr->state != GSM48_RR_ST_IDLE + || cs->state != GSM322_C3_CAMPED_NORMALLY) { + LOGP(DPLMN, LOGL_INFO, "Not camping, wait some more.\n"); start_plmn_timer(plmn, 60); return 0; @@ -1042,7 +1129,7 @@ static int gsm322_a_hplmn_search(struct osmocom_ms *ms, struct msgb *msg) new_a_state(plmn, GSM322_A5_HPLMN_SEARCH); /* initiate search at cell selection */ - nmsg = gsm322_msgb_alloc(GSM322_EVENT_HPLMN_SEARCH); + nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START); if (!nmsg) return -ENOMEM; gsm322_cs_sendmsg(ms, nmsg); @@ -1053,13 +1140,18 @@ static int gsm322_a_hplmn_search(struct osmocom_ms *ms, struct msgb *msg) /* manual mode selected */ static int gsm322_a_sel_manual(struct osmocom_ms *ms, struct msgb *msg) { - struct gsm322_plmn *plmn = &ms->plmn; + struct msgb *nmsg; /* restart state machine */ gsm322_a_switch_off(ms, msg); - plmn->mode = PLMN_MODE_MANUAL; + ms->settings.plmn_mode = PLMN_MODE_MANUAL; gsm322_m_switch_on(ms, msg); + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL); + if (!nmsg) + return -ENOMEM; + gsm48_mmevent_msg(ms, nmsg); + return 0; } @@ -1067,28 +1159,89 @@ static int gsm322_a_sel_manual(struct osmocom_ms *ms, struct msgb *msg) * handler for manual search */ -/* go Not on PLMN state */ -static int gsm322_m_go_not_on_plmn(struct osmocom_ms *ms, struct msgb *msg) +/* display PLMNs and to Not on PLMN */ +static int gsm322_m_display_plmns(struct osmocom_ms *ms, struct msgb *msg) { + struct gsm322_msg *gm = (struct gsm322_msg *) msg->data; + int msg_type = gm->msg_type; struct gsm322_plmn *plmn = &ms->plmn; + struct gsm_sub_plmn_list *temp; + + /* generate list */ + gsm322_sort_list(ms); + + vty_notify(ms, NULL); + switch (msg_type) { + case GSM322_EVENT_REG_FAILED: + vty_notify(ms, "Failed to register to network %s, %s " + "(%s, %s)\n", + gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc), + gsm_get_mcc(plmn->mcc), + gsm_get_mnc(plmn->mcc, plmn->mnc)); + break; + case GSM322_EVENT_NO_CELL_FOUND: + vty_notify(ms, "No cell found for network %s, %s " + "(%s, %s)\n", + gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc), + gsm_get_mcc(plmn->mcc), + gsm_get_mnc(plmn->mcc, plmn->mnc)); + break; + case GSM322_EVENT_ROAMING_NA: + vty_notify(ms, "Roaming not allowed to network %s, %s " + "(%s, %s)\n", + gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc), + gsm_get_mcc(plmn->mcc), + gsm_get_mnc(plmn->mcc, plmn->mnc)); + break; + } + if (llist_empty(&plmn->sorted_plmn)) + vty_notify(ms, "Search network!\n"); + else { + vty_notify(ms, "Search or select from network:\n"); + llist_for_each_entry(temp, &plmn->sorted_plmn, entry) + vty_notify(ms, " Network %s, %s (%s, %s)\n", + gsm_print_mcc(temp->mcc), + gsm_print_mnc(temp->mnc), + gsm_get_mcc(temp->mcc), + gsm_get_mnc(temp->mcc, temp->mnc)); + } + + /* go Not on PLMN state */ new_m_state(plmn, GSM322_M3_NOT_ON_PLMN); return 0; } -/* display PLMNs and to Not on PLMN */ -static int gsm322_m_display_plmns(struct osmocom_ms *ms, struct msgb *msg) +/* user starts reselection */ +static int gsm322_m_user_resel(struct osmocom_ms *ms, struct msgb *msg) { - /* generate list */ - gsm322_sort_list(ms); + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *nmsg; -#ifdef TODO - display PLMNs to user -#endif - - /* go Not on PLMN state */ - return gsm322_m_go_not_on_plmn(ms, msg); + if (!subscr->sim_valid) { + return 0; + } + + /* try again later, if not idle */ + if (rr->state != GSM48_RR_ST_IDLE) { + LOGP(DPLMN, LOGL_INFO, "Not idle, rejecting.\n"); + + return 0; + } + + /* initiate search at cell selection */ + vty_notify(ms, NULL); + vty_notify(ms, "Searching Network, please wait...\n"); + LOGP(DPLMN, LOGL_INFO, "User re-select, start PLMN search first.\n"); + + nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return 0; } /* MS is switched on OR SIM is inserted OR removed */ @@ -1096,8 +1249,10 @@ static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg) { struct gsm_subscriber *subscr = &ms->subscr; struct gsm322_plmn *plmn = &ms->plmn; + struct msgb *nmsg; if (!subscr->sim_valid) { + LOGP(DSUM, LOGL_INFO, "SIM is removed\n"); LOGP(DPLMN, LOGL_INFO, "Switch on without SIM.\n"); new_m_state(plmn, GSM322_M5_NO_SIM); @@ -1106,22 +1261,44 @@ static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg) /* if there is a registered PLMN */ if (subscr->plmn_valid) { + struct msgb *nmsg; + /* select the registered PLMN */ plmn->mcc = subscr->plmn_mcc; plmn->mnc = subscr->plmn_mnc; - LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%03d mnc=%02d " - "%s, %s)\n", plmn->mcc, plmn->mnc, - gsm_get_mcc(plmn->mcc), + LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN " + "(mcc=%s mnc=%s %s, %s)\n", gsm_print_mcc(plmn->mcc), + gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc), + gsm_get_mnc(plmn->mcc, plmn->mnc)); + LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%s mnc=%s " + "%s, %s)\n", gsm_print_mcc(plmn->mcc), + gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc)); new_m_state(plmn, GSM322_M1_TRYING_RPLMN); + /* indicate New PLMN */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + return 0; } - /* display PLMNs */ - return gsm322_m_display_plmns(ms, msg); + /* initiate search at cell selection */ + LOGP(DSUM, LOGL_INFO, "Search for network\n"); + LOGP(DPLMN, LOGL_INFO, "Switch on, start PLMN search first.\n"); + vty_notify(ms, NULL); + vty_notify(ms, "Searching Network, please wait...\n"); + + nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return 0; } /* MS is switched off */ @@ -1136,6 +1313,12 @@ static int gsm322_m_switch_off(struct osmocom_ms *ms, struct msgb *msg) return 0; } +static int gsm322_m_sim_insert(struct osmocom_ms *ms, struct msgb *msg) +{ + LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n"); + return 0; +} + /* SIM is removed */ static int gsm322_m_sim_removed(struct osmocom_ms *ms, struct msgb *msg) { @@ -1160,7 +1343,7 @@ static int gsm322_m_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg) struct gsm_subscriber *subscr = &ms->subscr; /* if selected PLMN is in list of forbidden PLMNs */ - gsm322_del_forbidden_plmn(ms, plmn->mcc, plmn->mnc); + gsm_subscr_del_forbidden_plmn(subscr, plmn->mcc, plmn->mnc); /* set last registered PLMN */ subscr->plmn_valid = 1; @@ -1178,9 +1361,11 @@ static int gsm322_m_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg) /* indicate selected PLMN */ static int gsm322_m_indicate_selected(struct osmocom_ms *ms, struct msgb *msg) { -#ifdef TODO - indicate selected plmn to user -#endif + struct gsm322_plmn *plmn = &ms->plmn; + + vty_notify(ms, NULL); + vty_notify(ms, "Selected Network: %s, %s\n", + gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc)); return gsm322_m_go_on_plmn(ms, msg); } @@ -1189,9 +1374,23 @@ static int gsm322_m_indicate_selected(struct osmocom_ms *ms, struct msgb *msg) static int gsm322_m_plmn_avail(struct osmocom_ms *ms, struct msgb *msg) { struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_cellsel *cs = &ms->cellsel; new_m_state(plmn, GSM322_M1_TRYING_RPLMN); + if (cs->mcc != plmn->mcc || cs->mnc != plmn->mnc) { + struct msgb *nmsg; + + LOGP(DPLMN, LOGL_INFO, "PLMN available, but currently not " + "selected, so start selection.\n"); + + /* indicate New PLMN */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + } + return 0; } @@ -1200,30 +1399,45 @@ static int gsm322_m_choose_plmn(struct osmocom_ms *ms, struct msgb *msg) { struct gsm322_plmn *plmn = &ms->plmn; struct gsm322_msg *gm = (struct gsm322_msg *) msg->data; + struct msgb *nmsg; /* use user selection */ plmn->mcc = gm->mcc; plmn->mnc = gm->mnc; - LOGP(DPLMN, LOGL_INFO, "User selects PLMN. (mcc=%03d mnc=%02d " - "%s, %s)\n", plmn->mcc, plmn->mnc, + vty_notify(ms, NULL); + vty_notify(ms, "Selected Network: %s, %s\n", + gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc)); + LOGP(DPLMN, LOGL_INFO, "User selects PLMN. (mcc=%s mnc=%s " + "%s, %s)\n", gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc)); new_m_state(plmn, GSM322_M4_TRYING_PLMN); + /* indicate New PLMN */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + return 0; } /* auto mode selected */ static int gsm322_m_sel_auto(struct osmocom_ms *ms, struct msgb *msg) { - struct gsm322_plmn *plmn = &ms->plmn; + struct msgb *nmsg; /* restart state machine */ gsm322_m_switch_off(ms, msg); - plmn->mode = PLMN_MODE_AUTO; + ms->settings.plmn_mode = PLMN_MODE_AUTO; gsm322_a_switch_on(ms, msg); + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL); + if (!nmsg) + return -ENOMEM; + gsm48_mmevent_msg(ms, nmsg); + return 0; } @@ -1250,6 +1464,7 @@ static int gsm322_cs_select(struct osmocom_ms *ms, int any) { struct gsm322_cellsel *cs = &ms->cellsel; struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_sysinfo *s; int i, found = -1, power = 0; uint8_t flags, mask; uint16_t acc_class; @@ -1257,36 +1472,37 @@ static int gsm322_cs_select(struct osmocom_ms *ms, int any) /* set out access class depending on the cell selection type */ if (any) { acc_class = subscr->acc_class | 0x0400; /* add emergency */ - LOGP(DCS, LOGL_INFO, "Using access class with Emergency " + LOGP(DCS, LOGL_INFO, "Select using access class with Emergency " "class.\n"); } else { - acc_class = subscr->acc_class & 0xfbff; /* remove emergency */ - LOGP(DCS, LOGL_INFO, "Using access class without Emergency " - "class\n"); + acc_class = subscr->acc_class; + LOGP(DCS, LOGL_INFO, "Select using access class \n"); } /* flags to match */ mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER | GSM322_CS_FLAG_SIGNAL | GSM322_CS_FLAG_SYSINFO; - if (cs->state == GSM322_C2_STORED_CELL_SEL) + if (cs->state == GSM322_C2_STORED_CELL_SEL + || cs->state == GSM322_C5_CHOOSE_CELL) mask |= GSM322_CS_FLAG_BA; flags = mask; /* all masked flags are requied */ /* loop through all scanned frequencies and select cell */ for (i = 0; i <= 1023; i++) { cs->list[i].flags &= ~GSM322_CS_FLAG_TEMP_AA; + s = cs->list[i].sysinfo; /* channel has no informations for us */ - if ((cs->list[i].flags & mask) != flags) { + if (!s || (cs->list[i].flags & mask) != flags) { continue; } /* check C1 criteria not fullfilled */ // TODO: C1 is also dependant on power class and max power - if (cs->list[i].rxlev_db < cs->list[i].min_db) { + if (cs->list[i].rxlev_db < s->rxlev_acc_min_db) { LOGP(DCS, LOGL_INFO, "Skip frequency %d: C1 criteria " "not met. (rxlev=%d < min=%d)\n", i, - cs->list[i].rxlev_db, cs->list[i].min_db); + cs->list[i].rxlev_db, s->rxlev_acc_min_db); continue; } @@ -1302,19 +1518,18 @@ static int gsm322_cs_select(struct osmocom_ms *ms, int any) if (!subscr->acc_barr && (cs->list[i].flags & GSM322_CS_FLAG_FORBIDD)) { LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is in " - "list of forbidden LAs. (mcc=%03d mnc=%02d " - "lai=%04x)\n", i, cs->list[i].mcc, - cs->list[i].mnc, cs->list[i].lac); + "list of forbidden LAs. (mcc=%s mnc=%s " + "lai=%04x)\n", i, gsm_print_mcc(s->mcc), + gsm_print_mnc(s->mnc), s->lac); continue; } /* if we have no access to the cell and we don't override */ if (!subscr->acc_barr - && !(acc_class & (cs->list[i].class_barr ^ 0xffff))) { + && !(acc_class & (s->class_barr ^ 0xffff))) { LOGP(DCS, LOGL_INFO, "Skip frequency %d: Class is " "barred for out access. (access=%04x " - "barred=%04x)\n", i, acc_class, - cs->list[i].class_barr); + "barred=%04x)\n", i, acc_class, s->class_barr); continue; } @@ -1322,20 +1537,20 @@ static int gsm322_cs_select(struct osmocom_ms *ms, int any) cs->list[i].flags |= GSM322_CS_FLAG_TEMP_AA; /* if we search a specific PLMN, but it does not match */ - if (!any && (cs->mcc != cs->list[i].mcc - || cs->mnc != cs->list[i].mnc)) { + if (!any && cs->mcc && (cs->mcc != s->mcc + || cs->mnc != s->mnc)) { LOGP(DCS, LOGL_INFO, "Skip frequency %d: PLMN of cell " - "does not match target PLMN. (mcc=%03d " - "mnc=%02d)\n", i, cs->list[i].mcc, - cs->list[i].mnc); + "does not match target PLMN. (mcc=%s " + "mnc=%s)\n", i, gsm_print_mcc(s->mcc), + gsm_print_mnc(s->mnc)); continue; } LOGP(DCS, LOGL_INFO, "Cell frequency %d: Cell found, (rxlev=%d " - "mcc=%03d mnc=%02d lac=%04x %s, %s)\n", i, - cs->list[i].rxlev_db, cs->list[i].mcc, cs->list[i].mnc, - cs->list[i].lac, gsm_get_mcc(cs->list[i].mcc), - gsm_get_mnc(cs->list[i].mcc, cs->list[i].mnc)); + "mcc=%s mnc=%s lac=%04x %s, %s)\n", i, + cs->list[i].rxlev_db, gsm_print_mcc(s->mcc), + gsm_print_mnc(s->mnc), s->lac, gsm_get_mcc(s->mcc), + gsm_get_mnc(s->mcc, s->mnc)); /* find highest power cell */ if (found < 0 || cs->list[i].rxlev_db > power) { @@ -1344,8 +1559,6 @@ static int gsm322_cs_select(struct osmocom_ms *ms, int any) } } - gsm322_dump_sorted_plmn(ms); - if (found >= 0) LOGP(DCS, LOGL_INFO, "Cell frequency %d selected.\n", found); @@ -1356,37 +1569,15 @@ static int gsm322_cs_select(struct osmocom_ms *ms, int any) static int gsm322_cs_scan(struct osmocom_ms *ms) { struct gsm322_cellsel *cs = &ms->cellsel; - struct gsm_subscriber *subscr = &ms->subscr; - struct gsm48_sysinfo *s = &ms->sysinfo; - struct gsm48_rrlayer *rr = &ms->rrlayer; int i, j; uint8_t mask, flags; uint32_t weight = 0, test = cs->scan_state; - if (rr->state != GSM48_RR_ST_IDLE) { - LOGP(DCS, LOGL_FATAL, "This must only happen in IDLE mode, " - "please fix!\n"); - return -EINVAL; - } - - /* special prositive case for HPLMN search */ - if (cs->state == GSM322_HPLMN_SEARCH && s->mcc == subscr->mcc - && s->mnc == subscr->mnc) { - struct msgb *nmsg; - - nmsg = gsm322_msgb_alloc(GSM322_EVENT_HPLMN_FOUND); - LOGP(DCS, LOGL_INFO, "HPLMN cell available.\n"); - if (!nmsg) - return -ENOMEM; - gsm322_plmn_sendmsg(ms, nmsg); - - return 0; - } - /* search for strongest unscanned cell */ mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER | GSM322_CS_FLAG_SIGNAL; - if (cs->state == GSM322_C2_STORED_CELL_SEL) + if (cs->state == GSM322_C2_STORED_CELL_SEL + || cs->state == GSM322_C5_CHOOSE_CELL) mask |= GSM322_CS_FLAG_BA; flags = mask; /* all masked flags are requied */ for (i = 0; i <= 1023; i++) { @@ -1422,29 +1613,56 @@ static int gsm322_cs_scan(struct osmocom_ms *ms) cs->scan_state = weight; if (!weight) - gsm322_dump_cs_list(ms); + gsm322_dump_cs_list(cs, GSM322_CS_FLAG_SYSINFO, print_dcs, + NULL); - /* special negative case for HPLMN search */ - if (cs->state == GSM322_HPLMN_SEARCH && !weight) { + /* special case for PLMN search */ + if (cs->state == GSM322_PLMN_SEARCH && !weight) { struct msgb *nmsg; - nmsg = gsm322_msgb_alloc(GSM322_EVENT_HPLMN_NOT_FOUND); - LOGP(DCS, LOGL_INFO, "No HPLMN cell available.\n"); + /* create AA flag */ + cs->mcc = cs->mnc = 0; + gsm322_cs_select(ms, 0); + + new_c_state(cs, GSM322_C0_NULL); + + nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_END); + LOGP(DCS, LOGL_INFO, "PLMN search finished.\n"); if (!nmsg) return -ENOMEM; gsm322_plmn_sendmsg(ms, nmsg); - /* re-tune back to current VPLMN */ - l1ctl_tx_ccch_req(ms, cs->arfcn); - cs->ccch_active = 0; - return 0; } - /* if all frequencies have been searched */ - if (!weight) { + /* special case for HPLMN search */ + if (cs->state == GSM322_HPLMN_SEARCH && !weight) { struct msgb *nmsg; - int found, any; + + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND); + LOGP(DCS, LOGL_INFO, "HPLMN search finished, no cell.\n"); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + + new_c_state(cs, GSM322_C3_CAMPED_NORMALLY); + + cs->arfcn = cs->sel_arfcn; + LOGP(DCS, LOGL_INFO, "Tuning back to frequency %d (rxlev " + "%d).\n", cs->arfcn, cs->list[cs->arfcn].rxlev_db); + cs->ccch_state = GSM322_CCCH_ST_INIT; + hack = 5; + gsm322_sync_to_cell(ms, cs); +// start_cs_timer(cs, ms->support.sync_to, 0); + + return 0; + } + + /* if all frequencies have been searched */ + if (!weight) { + struct msgb *nmsg; +#if 0 + int found, any = 0; LOGP(DCS, LOGL_INFO, "All frequencies scanned.\n"); @@ -1458,29 +1676,67 @@ static int gsm322_cs_scan(struct osmocom_ms *ms) /* if found */ if (found >= 0) { + struct gsm322_plmn *plmn = &ms->plmn; + LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found); /* tune */ cs->arfcn = found; - l1ctl_tx_ccch_req(ms, cs->arfcn); - cs->ccch_active = 0; - - /* Clear system information. */ - gsm48_sysinfo_init(ms); - - /* tell PLMN process about available PLMNs */ - nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL); - if (!nmsg) - return -ENOMEM; - gsm322_plmn_sendmsg(ms, nmsg); + cs->si = cs->list[cs->arfcn].sysinfo; + cs->ccch_state = GSM322_CCCH_ST_INIT; + hack = 5; + gsm322_sync_to_cell(ms, cs); + + /* selected PLMN (manual) or any PLMN (auto) */ + switch (ms->settings.plmn_mode) { + case PLMN_MODE_AUTO: + if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) { + /* PLMN becomes available */ + nmsg = gsm322_msgb_alloc( + GSM322_EVENT_PLMN_AVAIL); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + } + break; + case PLMN_MODE_MANUAL: + if (plmn->state == GSM322_M3_NOT_ON_PLMN + && gsm322_is_plmn_avail(cs, plmn->mcc, + plmn->mnc)) { + /* PLMN becomes available */ + nmsg = gsm322_msgb_alloc( + GSM322_EVENT_PLMN_AVAIL); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + } + break; + } + /* set selected cell */ + cs->selected = 1; + cs->sel_arfcn = cs->arfcn; + memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si)); + cs->sel_mcc = cs->si->mcc; + cs->sel_mnc = cs->si->mnc; + cs->sel_lac = cs->si->lac; + cs->sel_id = cs->si->cell_id; + + /* tell CS process about available cell */ + LOGP(DCS, LOGL_INFO, "Cell available.\n"); nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND); } else { - nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND); +#endif + /* unset selected cell */ + gsm322_unselect_cell(cs); + + /* tell CS process about no cell available */ LOGP(DCS, LOGL_INFO, "No cell available.\n"); - } + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND); +// } if (!nmsg) return -ENOMEM; - gsm322_cs_sendmsg(ms, nmsg); + gsm322_c_event(ms, nmsg); + msgb_free(nmsg); return 0; } @@ -1491,16 +1747,33 @@ static int gsm322_cs_scan(struct osmocom_ms *ms) /* Tune to frequency for a while, to receive broadcasts. */ cs->arfcn = weight & 1023; - LOGP(DCS, LOGL_INFO, "Scanning frequency %d.\n", cs->arfcn); - l1ctl_tx_ccch_req(ms, cs->arfcn); - cs->ccch_active = 0; - - /* Clear system information. */ - gsm48_sysinfo_init(ms); + LOGP(DCS, LOGL_INFO, "Scanning frequency %d (rxlev %d).\n", cs->arfcn, + cs->list[cs->arfcn].rxlev_db); + cs->ccch_state = GSM322_CCCH_ST_INIT; + hack = 5; + gsm322_sync_to_cell(ms, cs); +// start_cs_timer(cs, ms->support.sync_to, 0); + + /* Allocate/clean system information. */ + cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO; + if (cs->list[cs->arfcn].sysinfo) + memset(cs->list[cs->arfcn].sysinfo, 0, + sizeof(struct gsm48_sysinfo)); + else + cs->list[cs->arfcn].sysinfo = talloc_zero(l23_ctx, + struct gsm48_sysinfo); + if (!cs->list[cs->arfcn].sysinfo) + exit(-ENOMEM); + cs->si = cs->list[cs->arfcn].sysinfo; /* increase scan counter for each maximum scan range */ - if (gsm_sup_smax[j].max) + if (gsm_sup_smax[j].max) { + LOGP(DCS, LOGL_INFO, "%d frequencies left in band %d..%d\n", + gsm_sup_smax[j].max - gsm_sup_smax[j].temp, + gsm_sup_smax[j].start, gsm_sup_smax[j].end); gsm_sup_smax[j].temp++; + } + return 0; } @@ -1508,8 +1781,10 @@ static int gsm322_cs_scan(struct osmocom_ms *ms) static int gsm322_cs_store(struct osmocom_ms *ms) { struct gsm322_cellsel *cs = &ms->cellsel; - struct gsm48_sysinfo *s = &ms->sysinfo; - int i = cs->scan_state & 1023; + struct gsm48_sysinfo *s = cs->si; + struct gsm322_plmn *plmn = &ms->plmn; + struct msgb *nmsg; + int found, any = 0; if (cs->state != GSM322_C2_STORED_CELL_SEL && cs->state != GSM322_C1_NORMAL_CELL_SEL @@ -1517,47 +1792,143 @@ static int gsm322_cs_store(struct osmocom_ms *ms) && cs->state != GSM322_C4_NORMAL_CELL_RESEL && cs->state != GSM322_C8_ANY_CELL_RESEL && cs->state != GSM322_C5_CHOOSE_CELL - && cs->state != GSM322_C9_CHOOSE_ANY_CELL) { + && cs->state != GSM322_C9_CHOOSE_ANY_CELL + && cs->state != GSM322_PLMN_SEARCH + && cs->state != GSM322_HPLMN_SEARCH) { LOGP(DCS, LOGL_FATAL, "This must only happen during cell " "(re-)selection, please fix!\n"); return -EINVAL; } /* store sysinfo */ - cs->list[i].flags |= GSM322_CS_FLAG_SYSINFO; - if (s->cell_barr) - cs->list[i].flags |= GSM322_CS_FLAG_BARRED; + cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_SYSINFO; + if (s->cell_barr + && !(cs->list[cs->arfcn].sysinfo && cs->list[cs->arfcn].sysinfo->sp && + cs->list[cs->arfcn].sysinfo->sp_cbq)) + cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_BARRED; else - cs->list[i].flags &= ~GSM322_CS_FLAG_BARRED; - cs->list[i].min_db = s->rxlev_acc_min_db; - cs->list[i].class_barr = s->class_barr; - cs->list[i].max_pwr = s->ms_txpwr_max_ccch; + cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_BARRED; + +#if 0 + cs->list[cs->arfcn].min_db = s->rxlev_acc_min_db; + cs->list[cs->arfcn].class_barr = s->class_barr; + cs->list[cs->arfcn].max_pwr = s->ms_txpwr_max_ccch; +#endif /* store selected network */ if (s->mcc) { - cs->list[i].mcc = s->mcc; - cs->list[i].mnc = s->mnc; - cs->list[i].lac = s->lac; +#if 0 + cs->list[cs->arfcn].mcc = s->mcc; + cs->list[cs->arfcn].mnc = s->mnc; + cs->list[cs->arfcn].lac = s->lac; +#endif if (gsm322_is_forbidden_la(ms, s->mcc, s->mnc, s->lac)) - cs->list[i].flags |= GSM322_CS_FLAG_FORBIDD; + cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_FORBIDD; else - cs->list[i].flags &= ~GSM322_CS_FLAG_FORBIDD; + cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_FORBIDD; } LOGP(DCS, LOGL_INFO, "Scan frequency %d: Cell found. (rxlev=%d " - "mcc=%03d mnc=%02d lac=%04x)\n", i, cs->list[i].rxlev_db, - cs->list[i].mcc, cs->list[i].mnc, cs->list[i].lac); + "mcc=%s mnc=%s lac=%04x)\n", cs->arfcn, + cs->list[cs->arfcn].rxlev_db, gsm_print_mcc(s->mcc), + gsm_print_mnc(s->mnc), s->lac); - /* tune to next cell */ - return gsm322_cs_scan(ms); + /* special case for PLMN search */ + if (cs->state == GSM322_PLMN_SEARCH) + /* tune to next cell */ + return gsm322_cs_scan(ms); + + /* special case for HPLMN search */ + if (cs->state == GSM322_HPLMN_SEARCH) { + struct gsm_subscriber *subscr = &ms->subscr; + struct msgb *nmsg; + + if (!gsm322_is_hplmn_avail(cs, subscr->imsi)) + /* tune to next cell */ + return gsm322_cs_scan(ms); + + nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND); + LOGP(DCS, LOGL_INFO, "HPLMN search finished, cell found.\n"); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + + return 0; + } + + /* just see, if we search for any cell */ + if (cs->state == GSM322_C6_ANY_CELL_SEL + || cs->state == GSM322_C8_ANY_CELL_RESEL + || cs->state == GSM322_C9_CHOOSE_ANY_CELL) + any = 1; + + found = gsm322_cs_select(ms, any); + + /* if not found */ + if (found < 0) { + LOGP(DCS, LOGL_INFO, "Cell not suitable and allowable.\n"); + /* tune to next cell */ + return gsm322_cs_scan(ms); + } + + LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found); + /* tune */ + cs->arfcn = found; + cs->si = cs->list[cs->arfcn].sysinfo; + cs->ccch_state = GSM322_CCCH_ST_INIT; + hack = 5; + gsm322_sync_to_cell(ms, cs); + + /* selected PLMN (manual) or any PLMN (auto) */ + switch (ms->settings.plmn_mode) { + case PLMN_MODE_AUTO: + if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) { + /* PLMN becomes available */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + } + break; + case PLMN_MODE_MANUAL: + if (plmn->state == GSM322_M3_NOT_ON_PLMN + && gsm322_is_plmn_avail(cs, plmn->mcc, + plmn->mnc)) { + /* PLMN becomes available */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + } + break; + } + + /* set selected cell */ + cs->selected = 1; + cs->sel_arfcn = cs->arfcn; + memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si)); + cs->sel_mcc = cs->si->mcc; + cs->sel_mnc = cs->si->mnc; + cs->sel_lac = cs->si->lac; + cs->sel_id = cs->si->cell_id; + + /* tell CS process about available cell */ + LOGP(DCS, LOGL_INFO, "Cell available.\n"); + nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND); + if (!nmsg) + return -ENOMEM; + gsm322_c_event(ms, nmsg); + msgb_free(nmsg); + + return 0; } /* process system information when returing to idle mode */ struct gsm322_ba_list *gsm322_cs_sysinfo_sacch(struct osmocom_ms *ms) { struct gsm322_cellsel *cs = &ms->cellsel; - struct gsm48_sysinfo *s = &ms->sysinfo; + struct gsm48_sysinfo *s = cs->si; struct gsm322_ba_list *ba = NULL; int i; uint8_t freq[128]; @@ -1585,9 +1956,9 @@ struct gsm322_ba_list *gsm322_cs_sysinfo_sacch(struct osmocom_ms *ms) freq[i >> 3] |= (1 << (i & 7)); } if (!!memcmp(freq, ba->freq, sizeof(freq))) { - LOGP(DCS, LOGL_INFO, "New BA list (mcc=%d mnc=%d " - "%s, %s).\n", ba->mcc, ba->mnc, - gsm_get_mcc(ba->mcc), + LOGP(DCS, LOGL_INFO, "New BA list (mcc=%s mnc=%s " + "%s, %s).\n", gsm_print_mcc(ba->mcc), + gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc), gsm_get_mnc(ba->mcc, ba->mnc)); memcpy(ba->freq, freq, sizeof(freq)); } @@ -1596,38 +1967,141 @@ struct gsm322_ba_list *gsm322_cs_sysinfo_sacch(struct osmocom_ms *ms) return ba; } +/* store BA whenever a system informations changes */ +static int gsm322_store_ba_list(struct gsm322_cellsel *cs, + struct gsm48_sysinfo *s) +{ + struct gsm322_ba_list *ba; + int i; + uint8_t freq[128]; + + /* find or create ba list */ + ba = gsm322_find_ba_list(cs, s->mcc, s->mnc); + if (!ba) { + ba = talloc_zero(l23_ctx, struct gsm322_ba_list); + if (!ba) + return -ENOMEM; + ba->mcc = s->mcc; + ba->mnc = s->mnc; + llist_add_tail(&ba->entry, &cs->ba_list); + } + /* update ba list */ + memset(freq, 0, sizeof(freq)); + freq[cs->arfcn >> 3] |= (1 << (cs->arfcn & 7)); + for (i = 0; i <= 1023; i++) { + if ((s->freq[i].mask & + (FREQ_TYPE_SERV | FREQ_TYPE_NCELL))) + freq[i >> 3] |= (1 << (i & 7)); + } + if (!!memcmp(freq, ba->freq, sizeof(freq))) { + LOGP(DCS, LOGL_INFO, "New BA list (mcc=%s mnc=%s " + "%s, %s).\n", gsm_print_mcc(ba->mcc), + gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc), + gsm_get_mnc(ba->mcc, ba->mnc)); + memcpy(ba->freq, freq, sizeof(freq)); + } + + return 0; +} + /* process system information during camping on a cell */ static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg) { + struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm322_cellsel *cs = &ms->cellsel; - struct gsm48_sysinfo *s = &ms->sysinfo; + struct gsm48_sysinfo *s = cs->si; struct gsm_subscriber *subscr = &ms->subscr; struct gsm322_msg *gm = (struct gsm322_msg *) msg->data; struct msgb *nmsg; + if (rr->state != GSM48_RR_ST_IDLE) { + LOGP(DCS, LOGL_INFO, "Ignoring in dedicated mode.\n"); + return -EBUSY; + } + + /* Store BA if we have full system info about cells and neigbor cells. + * Depending on the extended bit in the channel description, + * we require more or less system informations about neighbor cells + */ + if (s->mcc + && s->mnc + && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1 + || gm->sysinfo == GSM48_MT_RR_SYSINFO_2 + || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis + || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter) + && s->si1 + && s->si2 + && (!s->nb_ext_ind_si2 + || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis) + || (s->si2bis && s->si2ter && s->nb_ext_ind_si2 + && s->nb_ext_ind_si2bis))) + gsm322_store_ba_list(cs, s); + + /* update sel_si, if all relevant system informations received */ + if (s->si1 && s->si2 && s->si3 + && (!s->nb_ext_ind_si2 + || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis) + || (s->si2bis && s->si2ter && s->nb_ext_ind_si2 + && s->nb_ext_ind_si2bis))) { + if (cs->selected) { + LOGP(DCS, LOGL_INFO, "Sysinfo of selected cell is " + "updated.\n"); + memcpy(&cs->sel_si, s, sizeof(cs->sel_si)); + //gsm48_sysinfo_dump(s, print_dcs, NULL); + } + } + + /* check for barred cell */ if (gm->sysinfo == GSM48_MT_RR_SYSINFO_1) { /* check if cell becomes barred */ - if (!subscr->acc_barr && s->cell_barr) { + if (!subscr->acc_barr && s->cell_barr + && !(cs->list[cs->arfcn].sysinfo + && cs->list[cs->arfcn].sysinfo->sp + && cs->list[cs->arfcn].sysinfo->sp_cbq)) { LOGP(DCS, LOGL_INFO, "Cell becomes barred.\n"); trigger_resel: /* mark cell as unscanned */ cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO; - /* trigger reselection event */ + if (cs->list[cs->arfcn].sysinfo) { + LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", + cs->arfcn); + talloc_free(cs->list[cs->arfcn].sysinfo); + cs->list[cs->arfcn].sysinfo = NULL; + gsm322_unselect_cell(cs); + } + /* trigger reselection without queueing, + * because other sysinfo message may be queued + * before + */ nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL); if (!nmsg) return -ENOMEM; - gsm322_cs_sendmsg(ms, nmsg); + gsm322_c_event(ms, nmsg); + msgb_free(nmsg); return 0; } /* check if cell access becomes barred */ if (!((subscr->acc_class & 0xfbff) - & (cs->list[cs->arfcn].class_barr ^ 0xffff))) { + & (s->class_barr ^ 0xffff))) { LOGP(DCS, LOGL_INFO, "Cell access becomes barred.\n"); goto trigger_resel; } } + /* check if MCC, MNC, LAC, cell ID changes */ + if (cs->sel_mcc != s->mcc || cs->sel_mnc != s->mnc + || cs->sel_lac != s->lac) { + LOGP(DCS, LOGL_NOTICE, "Cell changes location area. " + "This is not good!\n"); + goto trigger_resel; + } + if (cs->sel_id != s->cell_id) { + LOGP(DCS, LOGL_NOTICE, "Cell changes cell ID. " + "This is not good!\n"); + goto trigger_resel; + } + return 0; } @@ -1635,18 +2109,8 @@ static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg) static int gsm322_c_scan_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg) { struct gsm322_cellsel *cs = &ms->cellsel; - struct gsm48_rrlayer *rr = &ms->rrlayer; - struct gsm48_sysinfo *s = &ms->sysinfo; + struct gsm48_sysinfo *s = cs->si; struct gsm322_msg *gm = (struct gsm322_msg *) msg->data; - struct gsm322_ba_list *ba; - int i; - uint8_t freq[128]; - - if (rr->state != GSM48_RR_ST_IDLE) { - LOGP(DCS, LOGL_FATAL, "This must only happen in IDLE mode, " - "please fix!\n"); - return -EINVAL; - } /* no sysinfo if we are not done with power scan */ if (cs->powerscan) { @@ -1669,46 +2133,20 @@ static int gsm322_c_scan_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg) && (!s->nb_ext_ind_si2 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis) || (s->si2bis && s->si2ter && s->nb_ext_ind_si2 - && s->nb_ext_ind_si2bis))) { - /* find or create ba list */ - ba = gsm322_find_ba_list(cs, s->mcc, s->mnc); - if (!ba) { - ba = talloc_zero(l23_ctx, struct gsm322_ba_list); - if (!ba) - return -ENOMEM; - ba->mcc = s->mcc; - ba->mnc = s->mnc; - llist_add_tail(&ba->entry, &cs->ba_list); - } - /* update ba list */ - memset(freq, 0, sizeof(freq)); - freq[cs->arfcn >> 3] |= (1 << (cs->arfcn & 7)); - for (i = 0; i <= 1023; i++) { - if ((s->freq[i].mask & - (FREQ_TYPE_SERV | FREQ_TYPE_NCELL))) - freq[i >> 3] |= (1 << (i & 7)); - } - if (!!memcmp(freq, ba->freq, sizeof(freq))) { - LOGP(DCS, LOGL_INFO, "New BA list (mcc=%d mnc=%d " - "%s, %s).\n", ba->mcc, ba->mnc, - gsm_get_mcc(ba->mcc), - gsm_get_mnc(ba->mcc, ba->mnc)); - memcpy(ba->freq, freq, sizeof(freq)); - } - } + && s->nb_ext_ind_si2bis))) + gsm322_store_ba_list(cs, s); -#warning testing -printf("%d %d %d\n", s->si1, s->si2, s->si3); /* all relevant system informations received */ if (s->si1 && s->si2 && s->si3 && (!s->nb_ext_ind_si2 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis) || (s->si2bis && s->si2ter && s->nb_ext_ind_si2 && s->nb_ext_ind_si2bis))) { + LOGP(DCS, LOGL_INFO, "Received relevant sysinfo.\n"); /* stop timer */ stop_cs_timer(cs); - gsm48_sysinfo_dump(ms); + //gsm48_sysinfo_dump(s, print_dcs, NULL); /* store sysinfo and continue scan */ return gsm322_cs_store(ms); @@ -1722,14 +2160,26 @@ static void gsm322_cs_timeout(void *arg) { struct gsm322_cellsel *cs = arg; struct osmocom_ms *ms = cs->ms; - int i = cs->scan_state & 1023; - LOGP(DCS, LOGL_INFO, "Cell selection timer has fired.\n"); + LOGP(DCS, LOGL_INFO, "Cell selection failed.\n"); + + /* if we have no lock, we retry */ + if (cs->ccch_state != GSM322_CCCH_ST_SYNC) + LOGP(DCS, LOGL_INFO, "Sync timeout.\n"); + else + LOGP(DCS, LOGL_INFO, "Read timeout.\n"); + LOGP(DCS, LOGL_INFO, "Scan frequency %d: Cell not found. (rxlev=%d)\n", - i, cs->list[i].rxlev_db); + cs->arfcn, cs->list[cs->arfcn].rxlev_db); /* remove system information */ - cs->list[i].flags &= ~GSM322_CS_FLAG_SYSINFO; + cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO; + if (cs->list[cs->arfcn].sysinfo) { + LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", cs->arfcn); + talloc_free(cs->list[cs->arfcn].sysinfo); + cs->list[cs->arfcn].sysinfo = NULL; + gsm322_unselect_cell(cs); + } /* tune to next cell */ gsm322_cs_scan(ms); @@ -1741,26 +2191,20 @@ static void gsm322_cs_timeout(void *arg) * power scan process */ -/* search for block of unscanned freqeuncies and start scanning */ +/* search for block of unscanned frequencies and start scanning */ static int gsm322_cs_powerscan(struct osmocom_ms *ms) { struct gsm322_cellsel *cs = &ms->cellsel; - struct gsm48_rrlayer *rr = &ms->rrlayer; int i, s = -1, e; uint8_t mask, flags; - if (rr->state != GSM48_RR_ST_IDLE) { - LOGP(DCS, LOGL_FATAL, "This must only happen in IDLE mode, " - "please fix!\n"); - return -EINVAL; - } - again: /* search for first frequency to scan */ mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER; flags = GSM322_CS_FLAG_SUPPORT; - if (cs->state == GSM322_C2_STORED_CELL_SEL) { + if (cs->state == GSM322_C2_STORED_CELL_SEL + || cs->state == GSM322_C5_CHOOSE_CELL) { LOGP(DCS, LOGL_FATAL, "Scanning power for stored BA list.\n"); mask |= GSM322_CS_FLAG_BA; flags |= GSM322_CS_FLAG_BA; @@ -1797,7 +2241,17 @@ static int gsm322_cs_powerscan(struct osmocom_ms *ms) ~(GSM322_CS_FLAG_POWER | GSM322_CS_FLAG_SIGNAL | GSM322_CS_FLAG_SYSINFO); + if (cs->list[i].sysinfo) { + LOGP(DCS, LOGL_INFO, "free " + "sysinfo arfcn=%d\n", + i); + talloc_free( + cs->list[i].sysinfo); + cs->list[i].sysinfo = NULL; + } } + /* no cell selected */ + gsm322_unselect_cell(cs); goto again; } /* on other cell selection, indicate "no cell found" */ @@ -1806,11 +2260,32 @@ static int gsm322_cs_powerscan(struct osmocom_ms *ms) * If we would continue to process CS, then we might get * our list of scanned cells disturbed. */ - nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND); + if (cs->state == GSM322_PLMN_SEARCH) + nmsg = gsm322_msgb_alloc( + GSM322_EVENT_PLMN_SEARCH_END); + else + nmsg = gsm322_msgb_alloc( + GSM322_EVENT_NO_CELL_FOUND); if (!nmsg) return -ENOMEM; gsm322_plmn_sendmsg(ms, nmsg); + /* if HPLMN search, select last frequency */ + if (cs->state == GSM322_HPLMN_SEARCH) { + new_c_state(cs, GSM322_C3_CAMPED_NORMALLY); + + cs->arfcn = cs->sel_arfcn; + LOGP(DCS, LOGL_INFO, "Tuning back to frequency " + "%d (rxlev %d).\n", cs->arfcn, + cs->list[cs->arfcn].rxlev_db); + cs->ccch_state = GSM322_CCCH_ST_INIT; + hack = 5; + gsm322_sync_to_cell(ms, cs); +// start_cs_timer(cs, ms->support.sync_to, 0); + + } else + new_c_state(cs, GSM322_C0_NULL); + return 0; } LOGP(DCS, LOGL_INFO, "Found %d frequencies.\n", found); @@ -1830,10 +2305,12 @@ static int gsm322_cs_powerscan(struct osmocom_ms *ms) break; } - LOGP(DCS, LOGL_INFO, "Scanning frequecies. (%d..%d)\n", s, e); + LOGP(DCS, LOGL_INFO, "Scanning frequencies. (%d..%d)\n", s, e); /* start scan on radio interface */ cs->powerscan = 1; +#warning TESTING!!!! +usleep(300000); return l1ctl_tx_pm_req_range(ms, s, e); } @@ -1874,12 +2351,20 @@ static int gsm322_l1_signal(unsigned int subsys, unsigned int signal, return -EINVAL; gsm322_cs_powerscan(ms); break; - case S_L1CTL_CCCH_RESP: + case S_L1CTL_FBSB_RESP: ms = signal_data; cs = &ms->cellsel; - if (!cs->ccch_active) { - LOGP(DCS, LOGL_INFO, "Channel activated.\n"); - cs->ccch_active = 1; + if (cs->ccch_state == GSM322_CCCH_ST_INIT) { + LOGP(DCS, LOGL_INFO, "Channel synched.\n"); + cs->ccch_state = GSM322_CCCH_ST_SYNC; +#if 0 + stop_cs_timer(cs); + + /* in dedicated mode */ + if (ms->rrlayer.state == GSM48_RR_ST_CONN_PEND) + return gsm48_rr_tx_rand_acc(ms, NULL); +#endif + /* set timer for reading BCCH */ if (cs->state == GSM322_C2_STORED_CELL_SEL || cs->state == GSM322_C1_NORMAL_CELL_SEL @@ -1887,19 +2372,101 @@ static int gsm322_l1_signal(unsigned int subsys, unsigned int signal, || cs->state == GSM322_C4_NORMAL_CELL_RESEL || cs->state == GSM322_C8_ANY_CELL_RESEL || cs->state == GSM322_C5_CHOOSE_CELL - || cs->state == GSM322_C9_CHOOSE_ANY_CELL) - start_cs_timer(cs, 4, 0); + || cs->state == GSM322_C9_CHOOSE_ANY_CELL + || cs->state == GSM322_PLMN_SEARCH + || cs->state == GSM322_HPLMN_SEARCH) + start_cs_timer(cs, ms->support.scan_to, 0); // TODO: timer depends on BCCH config } break; + case S_L1CTL_FBSB_ERR: + if (hack) { + ms = signal_data; + cs = &ms->cellsel; + gsm322_sync_to_cell(ms, cs); + hack--; + LOGP(DCS, LOGL_INFO, "Channel sync error, try again.\n"); + break; + } + LOGP(DCS, LOGL_INFO, "Channel sync error.\n"); + ms = signal_data; + cs = &ms->cellsel; + + stop_cs_timer(cs); + if (cs->selected) + gsm322_cs_loss(cs); + else + gsm322_cs_timeout(cs); + break; } + return 0; } +static void gsm322_cs_loss(void *arg) +{ + struct gsm322_cellsel *cs = arg; + struct osmocom_ms *ms = cs->ms; + struct gsm48_rrlayer *rr = &ms->rrlayer; + + LOGP(DCS, LOGL_INFO, "Loss of CCCH.\n"); + if (cs->state == GSM322_C3_CAMPED_NORMALLY + || cs->state == GSM322_C7_CAMPED_ANY_CELL) { + if (rr->state == GSM48_RR_ST_IDLE) { + struct msgb *nmsg; + + LOGP(DCS, LOGL_INFO, "Trigger re-selection.\n"); + + nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL); + if (!nmsg) + return; + gsm322_c_event(ms, nmsg); + msgb_free(nmsg); + } else { + LOGP(DCS, LOGL_INFO, "Trigger RR abort.\n"); + gsm48_rr_los(ms); + /* be shure that nothing else is done after here + * because the function call above may cause + * to return from idle state and trigger cell re-sel. + */ + } + } + + return; +} + /* * handler for cell selection process */ +/* start PLMN search */ +static int gsm322_c_plmn_search(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + int i; + + new_c_state(cs, GSM322_PLMN_SEARCH); + + /* mark all frequencies except our own BA to be scanned */ + for (i = 0; i <= 1023; i++) { + cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER + | GSM322_CS_FLAG_SIGNAL + | GSM322_CS_FLAG_SYSINFO); + if (cs->list[i].sysinfo) { + LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i); + talloc_free(cs->list[i].sysinfo); + cs->list[i].sysinfo = NULL; + gsm322_unselect_cell(cs); + } + } + + /* unset selected cell */ + gsm322_unselect_cell(cs); + + /* start power scan */ + return gsm322_cs_powerscan(ms); +} + /* start HPLMN search */ static int gsm322_c_hplmn_search(struct osmocom_ms *ms, struct msgb *msg) { @@ -1910,13 +2477,24 @@ static int gsm322_c_hplmn_search(struct osmocom_ms *ms, struct msgb *msg) /* mark all frequencies except our own BA to be scanned */ for (i = 0; i <= 1023; i++) { - if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO) - && !(cs->list[i].flags & GSM322_CS_FLAG_BA)) + if (i != cs->sel_arfcn + && (cs->list[i].flags & GSM322_CS_FLAG_SYSINFO) + && !(cs->list[i].flags & GSM322_CS_FLAG_BA)) { cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER | GSM322_CS_FLAG_SIGNAL | GSM322_CS_FLAG_SYSINFO); + if (cs->list[i].sysinfo) { + LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", + i); + talloc_free(cs->list[i].sysinfo); + cs->list[i].sysinfo = NULL; + } + } } + /* no cell selected */ + gsm322_unselect_cell(cs); + /* start power scan */ return gsm322_cs_powerscan(ms); } @@ -1937,6 +2515,9 @@ static int gsm322_c_stored_cell_sel(struct osmocom_ms *ms, struct gsm322_ba_list cs->list[i].flags &= ~GSM322_CS_FLAG_BA; } + /* unset selected cell */ + gsm322_unselect_cell(cs); + /* start power scan */ return gsm322_cs_powerscan(ms); } @@ -1949,14 +2530,24 @@ static int gsm322_c_normal_cell_sel(struct osmocom_ms *ms, struct msgb *msg) /* except for stored cell selection state, we weed to rescan ?? */ if (cs->state != GSM322_C2_STORED_CELL_SEL) { - for (i = 0; i <= 1023; i++) + for (i = 0; i <= 1023; i++) { cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER | GSM322_CS_FLAG_SIGNAL | GSM322_CS_FLAG_SYSINFO); + if (cs->list[i].sysinfo) { + LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", + i); + talloc_free(cs->list[i].sysinfo); + cs->list[i].sysinfo = NULL; + } + } } new_c_state(cs, GSM322_C1_NORMAL_CELL_SEL); + /* unset selected cell */ + gsm322_unselect_cell(cs); + /* start power scan */ return gsm322_cs_powerscan(ms); } @@ -1965,20 +2556,45 @@ static int gsm322_c_normal_cell_sel(struct osmocom_ms *ms, struct msgb *msg) static int gsm322_c_any_cell_sel(struct osmocom_ms *ms, struct msgb *msg) { struct gsm322_cellsel *cs = &ms->cellsel; - int i; - /* in case we already tried any cell selection, power scan again */ - if (cs->state == GSM322_C6_ANY_CELL_SEL) { - for (i = 0; i <= 1023; i++) + /* in case we already tried any cell (re-)selection, power scan again */ + if (cs->state == GSM322_C0_NULL + || cs->state == GSM322_C6_ANY_CELL_SEL + || cs->state == GSM322_C8_ANY_CELL_RESEL) { + int i; + + for (i = 0; i <= 1023; i++) { cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER | GSM322_CS_FLAG_SIGNAL | GSM322_CS_FLAG_SYSINFO); + if (cs->list[i].sysinfo) { + LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", + i); + talloc_free(cs->list[i].sysinfo); + cs->list[i].sysinfo = NULL; + } + } + } + /* after re-selection, indicate no cell found */ + if (cs->state == GSM322_C6_ANY_CELL_SEL + || cs->state == GSM322_C8_ANY_CELL_RESEL) { + struct msgb *nmsg; + + /* tell that we have no cell found */ + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_NO_CELL_FOUND); + if (!nmsg) + return -ENOMEM; + gsm48_mmevent_msg(ms, nmsg); + } else { new_c_state(cs, GSM322_C6_ANY_CELL_SEL); } cs->mcc = cs->mnc = 0; + /* unset selected cell */ + gsm322_unselect_cell(cs); + /* start power scan */ return gsm322_cs_powerscan(ms); } @@ -2041,53 +2657,63 @@ struct gsm322_ba_list *gsm322_cs_ba_range(struct osmocom_ms *ms, static int gsm322_cs_choose(struct osmocom_ms *ms) { struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm322_ba_list *ba = NULL; int i; -#ifdef TODO - if (message->ranges) - ba = gsm322_cs_ba_range(ms, message->range, message->ranges); + /* NOTE: The call to this function is synchron to RR layer, so + * we may access the BA range there. + */ + if (rr->ba_ranges) + ba = gsm322_cs_ba_range(ms, rr->ba_range, rr->ba_ranges); else { LOGP(DCS, LOGL_INFO, "No BA range(s), try sysinfo.\n"); -#endif /* get and update BA of last received sysinfo 5* */ ba = gsm322_cs_sysinfo_sacch(ms); -#ifdef TODO + if (!ba) { + LOGP(DCS, LOGL_INFO, "No BA on sysinfo, try stored " + "BA list.\n"); + ba = gsm322_find_ba_list(cs, cs->sel_si.mcc, + cs->sel_si.mnc); + } } -#endif if (!ba) { struct msgb *nmsg; - LOGP(DCS, LOGL_INFO, "No BA list.\n"); + LOGP(DCS, LOGL_INFO, "No BA list to use.\n"); /* tell CS to start over */ nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND); if (!nmsg) return -ENOMEM; - gsm322_cs_sendmsg(ms, nmsg); + gsm322_c_event(ms, nmsg); + msgb_free(nmsg); + + return 0; } /* flag all frequencies that are in current band allocation */ for (i = 0; i <= 1023; i++) { if (cs->state == GSM322_C5_CHOOSE_CELL) { - if ((ba->freq[i >> 3] & (1 << (i & 7)))) + if ((ba->freq[i >> 3] & (1 << (i & 7)))) { cs->list[i].flags |= GSM322_CS_FLAG_BA; - else + } else { cs->list[i].flags &= ~GSM322_CS_FLAG_BA; + } } cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER | GSM322_CS_FLAG_SIGNAL | GSM322_CS_FLAG_SYSINFO); + if (cs->list[i].sysinfo) { + LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i); + talloc_free(cs->list[i].sysinfo); + cs->list[i].sysinfo = NULL; + } } -#ifdef TODO - remove this: -#else - /* use hacked frequency */ - cs->list[ms->test_arfcn].flags |= GSM322_CS_FLAG_POWER; - cs->list[ms->test_arfcn].flags |= GSM322_CS_FLAG_SIGNAL; - cs->list[ms->test_arfcn].rxlev_db = -40; -#endif + + /* unset selected cell */ + gsm322_unselect_cell(cs); /* start power scan */ return gsm322_cs_powerscan(ms); @@ -2123,6 +2749,11 @@ static int gsm322_c_new_plmn(struct osmocom_ms *ms, struct msgb *msg) cs->mcc = plmn->mcc; cs->mnc = plmn->mnc; + LOGP(DSUM, LOGL_INFO, "Selecting network (mcc=%s " + "mnc=%s %s, %s)\n", gsm_print_mcc(cs->mcc), + gsm_print_mnc(cs->mnc), gsm_get_mcc(cs->mcc), + gsm_get_mnc(cs->mcc, cs->mnc)); + /* search for BA list */ ba = gsm322_find_ba_list(cs, plmn->mcc, plmn->mnc); @@ -2141,7 +2772,13 @@ static int gsm322_c_camp_normally(struct osmocom_ms *ms, struct msgb *msg) struct gsm322_cellsel *cs = &ms->cellsel; struct msgb *nmsg; - nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_NEW_LAI); + LOGP(DSUM, LOGL_INFO, "Camping normally on cell (arfcn=%d mcc=%s " + "mnc=%s %s, %s)\n", cs->sel_arfcn, gsm_print_mcc(cs->sel_mcc), + gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc), + gsm_get_mnc(cs->sel_mcc, cs->sel_mnc)); + + /* tell that we have selected a (new) cell */ + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED); if (!nmsg) return -ENOMEM; gsm48_mmevent_msg(ms, nmsg); @@ -2155,6 +2792,19 @@ static int gsm322_c_camp_normally(struct osmocom_ms *ms, struct msgb *msg) static int gsm322_c_camp_any_cell(struct osmocom_ms *ms, struct msgb *msg) { struct gsm322_cellsel *cs = &ms->cellsel; + struct msgb *nmsg; + + LOGP(DSUM, LOGL_INFO, "Camping on any cell (arfcn=%d mcc=%s " + "mnc=%s %s, %s)\n", cs->sel_arfcn, gsm_print_mcc(cs->sel_mcc), + gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc), + gsm_get_mnc(cs->sel_mcc, cs->sel_mnc)); + + + /* tell that we have selected a (new) cell */ + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED); + if (!nmsg) + return -ENOMEM; + gsm48_mmevent_msg(ms, nmsg); new_c_state(cs, GSM322_C7_CAMPED_ANY_CELL); @@ -2166,12 +2816,19 @@ static int gsm322_c_conn_mode_1(struct osmocom_ms *ms, struct msgb *msg) { struct gsm322_cellsel *cs = &ms->cellsel; - /* stop camping process */ + /* check for error */ + if (!cs->selected) + return -EINVAL; + cs->arfcn = cs->sel_arfcn; /* be sure to go to current camping frequency on return */ LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn); - l1ctl_tx_ccch_req(ms, cs->arfcn); - cs->ccch_active = 0; + cs->ccch_state = GSM322_CCCH_ST_INIT; + hack = 5; + gsm322_sync_to_cell(ms, cs); + cs->si = cs->list[cs->arfcn].sysinfo; +#warning TESTING: laforge must fix the sync error when sending fbsb request too close to each other. also we must get a response with arfcn or a confirm, so we know where the response belongs to. +usleep(300000); return 0; } @@ -2180,12 +2837,17 @@ static int gsm322_c_conn_mode_2(struct osmocom_ms *ms, struct msgb *msg) { struct gsm322_cellsel *cs = &ms->cellsel; - /* stop camping process */ + /* check for error */ + if (!cs->selected) + return -EINVAL; + cs->arfcn = cs->sel_arfcn; /* be sure to go to current camping frequency on return */ LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn); - l1ctl_tx_ccch_req(ms, cs->arfcn); - cs->ccch_active = 0; + cs->ccch_state = GSM322_CCCH_ST_INIT; + hack = 5; + gsm322_sync_to_cell(ms, cs); + cs->si = cs->list[cs->arfcn].sysinfo; return 0; } @@ -2219,40 +2881,70 @@ static struct plmnastatelist { } plmnastatelist[] = { {SBIT(GSM322_A0_NULL), GSM322_EVENT_SWITCH_ON, gsm322_a_switch_on}, + + {SBIT(GSM322_A0_NULL), + GSM322_EVENT_PLMN_SEARCH_END, gsm322_a_sel_first_plmn}, + {ALL_STATES, GSM322_EVENT_SWITCH_OFF, gsm322_a_switch_off}, + {SBIT(GSM322_A6_NO_SIM), GSM322_EVENT_SIM_INSERT, gsm322_a_switch_on}, + + {ALL_STATES, + GSM322_EVENT_SIM_INSERT, gsm322_a_sim_insert}, + {ALL_STATES, GSM322_EVENT_SIM_REMOVE, gsm322_a_sim_removed}, + {ALL_STATES, GSM322_EVENT_INVALID_SIM, gsm322_a_sim_removed}, + {SBIT(GSM322_A1_TRYING_RPLMN), GSM322_EVENT_REG_FAILED, gsm322_a_sel_first_plmn}, + + {SBIT(GSM322_A1_TRYING_RPLMN), + GSM322_EVENT_ROAMING_NA, gsm322_a_sel_first_plmn}, + {SBIT(GSM322_A1_TRYING_RPLMN), GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_first_plmn}, + {SBIT(GSM322_A1_TRYING_RPLMN) | SBIT(GSM322_A3_TRYING_PLMN), GSM322_EVENT_REG_SUCCESS, gsm322_a_indicate_selected}, + {SBIT(GSM322_A2_ON_PLMN), GSM322_EVENT_ROAMING_NA, gsm322_a_roaming_na}, + {SBIT(GSM322_A2_ON_PLMN), - GSM322_EVENT_HPLMN_SEARCH, gsm322_a_hplmn_search}, + GSM322_EVENT_HPLMN_SEARCH, gsm322_a_hplmn_search_start}, + {SBIT(GSM322_A2_ON_PLMN), GSM322_EVENT_NO_CELL_FOUND, gsm322_a_loss_of_radio}, + {SBIT(GSM322_A2_ON_PLMN), - GSM322_EVENT_USER_RESEL, gsm322_a_user_reselection}, + GSM322_EVENT_USER_RESEL, gsm322_a_user_resel}, + {SBIT(GSM322_A3_TRYING_PLMN), GSM322_EVENT_REG_FAILED, gsm322_a_sel_next_plmn}, + + {SBIT(GSM322_A3_TRYING_PLMN), + GSM322_EVENT_ROAMING_NA, gsm322_a_sel_next_plmn}, + {SBIT(GSM322_A3_TRYING_PLMN), GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_next_plmn}, + {SBIT(GSM322_A5_HPLMN_SEARCH), - GSM322_EVENT_HPLMN_FOUND, gsm322_a_sel_first_plmn}, + GSM322_EVENT_CELL_FOUND, gsm322_a_sel_first_plmn}, + {SBIT(GSM322_A5_HPLMN_SEARCH), - GSM322_EVENT_HPLMN_NOT_FOUND, gsm322_a_go_on_plmn}, + GSM322_EVENT_NO_CELL_FOUND, gsm322_a_go_on_plmn}, + {SBIT(GSM322_A4_WAIT_FOR_PLMN), GSM322_EVENT_PLMN_AVAIL, gsm322_a_plmn_avail}, + {ALL_STATES, GSM322_EVENT_SEL_MANUAL, gsm322_a_sel_manual}, + {ALL_STATES, GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found}, }; @@ -2269,7 +2961,7 @@ static int gsm322_a_event(struct osmocom_ms *ms, struct msgb *msg) int i; LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for automatic PLMN " - "selection in state %s\n", ms->name, get_event_name(msg_type), + "selection in state '%s'\n", ms->name, get_event_name(msg_type), plmn_a_state_names[plmn->state]); /* find function for current state and message */ for (i = 0; i < PLMNASLLEN; i++) @@ -2294,37 +2986,66 @@ static struct plmnmstatelist { } plmnmstatelist[] = { {SBIT(GSM322_M0_NULL), GSM322_EVENT_SWITCH_ON, gsm322_m_switch_on}, + + {SBIT(GSM322_M0_NULL) | SBIT(GSM322_M3_NOT_ON_PLMN) | + SBIT(GSM322_M2_ON_PLMN), + GSM322_EVENT_PLMN_SEARCH_END, gsm322_m_display_plmns}, + {ALL_STATES, GSM322_EVENT_SWITCH_OFF, gsm322_m_switch_off}, + {SBIT(GSM322_M5_NO_SIM), GSM322_EVENT_SIM_INSERT, gsm322_m_switch_on}, + + {ALL_STATES, + GSM322_EVENT_SIM_INSERT, gsm322_m_sim_insert}, + {ALL_STATES, GSM322_EVENT_SIM_REMOVE, gsm322_m_sim_removed}, + {SBIT(GSM322_M1_TRYING_RPLMN), GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns}, + + {SBIT(GSM322_M1_TRYING_RPLMN), + GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns}, + {SBIT(GSM322_M1_TRYING_RPLMN), GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns}, + {SBIT(GSM322_M1_TRYING_RPLMN), GSM322_EVENT_REG_SUCCESS, gsm322_m_indicate_selected}, + {SBIT(GSM322_M2_ON_PLMN), GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns}, + {SBIT(GSM322_M1_TRYING_RPLMN) | SBIT(GSM322_M2_ON_PLMN) | SBIT(GSM322_M4_TRYING_PLMN), GSM322_EVENT_INVALID_SIM, gsm322_m_sim_removed}, - {SBIT(GSM322_M2_ON_PLMN), - GSM322_EVENT_USER_RESEL, gsm322_m_display_plmns}, + + {SBIT(GSM322_M3_NOT_ON_PLMN) | SBIT(GSM322_M2_ON_PLMN), + GSM322_EVENT_USER_RESEL, gsm322_m_user_resel}, + {SBIT(GSM322_M3_NOT_ON_PLMN), GSM322_EVENT_PLMN_AVAIL, gsm322_m_plmn_avail}, + {SBIT(GSM322_M3_NOT_ON_PLMN), GSM322_EVENT_CHOSE_PLMN, gsm322_m_choose_plmn}, + {SBIT(GSM322_M4_TRYING_PLMN), GSM322_EVENT_REG_SUCCESS, gsm322_m_go_on_plmn}, + + {SBIT(GSM322_M4_TRYING_PLMN), + GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns}, + {SBIT(GSM322_M4_TRYING_PLMN), - GSM322_EVENT_REG_FAILED, gsm322_m_go_not_on_plmn}, + GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns}, + {SBIT(GSM322_M4_TRYING_PLMN), - GSM322_EVENT_NO_CELL_FOUND, gsm322_m_go_not_on_plmn}, + GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns}, + {ALL_STATES, GSM322_EVENT_SEL_AUTO, gsm322_m_sel_auto}, + {ALL_STATES, GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found}, }; @@ -2341,7 +3062,7 @@ static int gsm322_m_event(struct osmocom_ms *ms, struct msgb *msg) int i; LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for manual PLMN selection " - "in state %s\n", ms->name, get_event_name(msg_type), + "in state '%s'\n", ms->name, get_event_name(msg_type), plmn_m_state_names[plmn->state]); /* find function for current state and message */ for (i = 0; i < PLMNMSLLEN; i++) @@ -2367,7 +3088,7 @@ int gsm322_plmn_dequeue(struct osmocom_ms *ms) while ((msg = msgb_dequeue(&plmn->event_queue))) { /* send event to PLMN select process */ - if (plmn->mode == PLMN_MODE_AUTO) + if (ms->settings.plmn_mode == PLMN_MODE_AUTO) gsm322_a_event(ms, msg); else gsm322_m_event(ms, msg); @@ -2386,46 +3107,65 @@ static struct cellselstatelist { } cellselstatelist[] = { {ALL_STATES, GSM322_EVENT_SWITCH_ON, gsm322_c_switch_on}, + {ALL_STATES, GSM322_EVENT_SIM_REMOVE, gsm322_c_any_cell_sel}, + {ALL_STATES, GSM322_EVENT_NEW_PLMN, gsm322_c_new_plmn}, + + {ALL_STATES, + GSM322_EVENT_PLMN_SEARCH_START, gsm322_c_plmn_search}, + {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) | SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL), GSM322_EVENT_CELL_FOUND, gsm322_c_camp_normally}, + {SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C6_ANY_CELL_SEL) | - SBIT(GSM322_C4_NORMAL_CELL_RESEL), + SBIT(GSM322_C8_ANY_CELL_RESEL), GSM322_EVENT_CELL_FOUND, gsm322_c_camp_any_cell}, + {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C6_ANY_CELL_SEL) | SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) | SBIT(GSM322_C0_NULL), GSM322_EVENT_NO_CELL_FOUND, gsm322_c_any_cell_sel}, + {SBIT(GSM322_C2_STORED_CELL_SEL) | SBIT(GSM322_C5_CHOOSE_CELL) | SBIT(GSM322_C4_NORMAL_CELL_RESEL), GSM322_EVENT_NO_CELL_FOUND, gsm322_c_normal_cell_sel}, + {SBIT(GSM322_C3_CAMPED_NORMALLY), GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_1}, + {SBIT(GSM322_C7_CAMPED_ANY_CELL), GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_2}, + {SBIT(GSM322_C3_CAMPED_NORMALLY), GSM322_EVENT_RET_IDLE, gsm322_c_choose_cell}, + {SBIT(GSM322_C7_CAMPED_ANY_CELL), GSM322_EVENT_RET_IDLE, gsm322_c_choose_any_cell}, + {SBIT(GSM322_C3_CAMPED_NORMALLY), GSM322_EVENT_CELL_RESEL, gsm322_c_normal_cell_resel}, + {SBIT(GSM322_C7_CAMPED_ANY_CELL), GSM322_EVENT_CELL_RESEL, gsm322_c_any_cell_resel}, + {SBIT(GSM322_C7_CAMPED_ANY_CELL), GSM322_EVENT_CELL_FOUND, gsm322_c_normal_cell_sel}, + {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) | SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL) | SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) | - SBIT(GSM322_C6_ANY_CELL_SEL), + SBIT(GSM322_C6_ANY_CELL_SEL) | SBIT(GSM322_PLMN_SEARCH), GSM322_EVENT_SYSINFO, gsm322_c_scan_sysinfo_bcch}, + {SBIT(GSM322_C3_CAMPED_NORMALLY) | SBIT(GSM322_C7_CAMPED_ANY_CELL), GSM322_EVENT_SYSINFO, gsm322_c_camp_sysinfo_bcch}, + {SBIT(GSM322_C3_CAMPED_NORMALLY), - GSM322_EVENT_HPLMN_SEARCH, gsm322_c_hplmn_search} + GSM322_EVENT_HPLMN_SEARCH, gsm322_c_hplmn_search}, }; #define CELLSELSLLEN \ @@ -2440,7 +3180,7 @@ int gsm322_c_event(struct osmocom_ms *ms, struct msgb *msg) int i; LOGP(DCS, LOGL_INFO, "(ms %s) Event '%s' for Cell selection in state " - "%s\n", ms->name, get_event_name(msg_type), + "'%s'\n", ms->name, get_event_name(msg_type), cs_state_names[cs->state]); /* find function for current state and message */ for (i = 0; i < CELLSELSLLEN; i++) @@ -2486,90 +3226,93 @@ int gsm322_dump_sorted_plmn(struct osmocom_ms *ms) printf("MCC |MNC |allowed|rx-lev\n"); printf("-------+-------+-------+-------\n"); llist_for_each_entry(temp, &plmn->sorted_plmn, entry) { - printf("%03d |%02d |%s |%d\n", temp->mcc, temp->mnc, + printf("%s |%s%s |%s |%d\n", gsm_print_mcc(temp->mcc), + gsm_print_mnc(temp->mnc), + ((temp->mnc & 0x00f) == 0x00f) ? " ":"", (temp->cause) ? "no ":"yes", temp->rxlev_db); } return 0; } -int gsm322_dump_cs_list(struct osmocom_ms *ms) +int gsm322_dump_cs_list(struct gsm322_cellsel *cs, uint8_t flags, + void (*print)(void *, const char *, ...), void *priv) { - struct gsm322_cellsel *cs = &ms->cellsel; - int i, j; + int i; + struct gsm48_sysinfo *s; - printf("rx-lev |MCC |MNC |forb.LA|barred,0123456789abcdef|" - "min-db |max-pwr\n" - "-------+-------+-------+-------+-----------------------+" - "-------+-------\n"); + print(priv, "arfcn |rx-lev |MCC |MNC |LAC |cell ID|forb.LA|" + "prio |min-db |max-pwr\n"); + print(priv, "-------+-------+-------+-------+-------+-------+-------+" + "-------+-------+-------\n"); for (i = 0; i <= 1023; i++) { - if (!(cs->list[i].flags & GSM322_CS_FLAG_SIGNAL)) + s = cs->list[i].sysinfo; + if (!s || !(cs->list[i].flags & flags)) continue; - printf("%4d |", cs->list[i].rxlev_db); + print(priv, "%4d |%4d |", i, cs->list[i].rxlev_db); if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)) { - printf("%03d |%02d |", cs->list[i].mcc, - cs->list[i].mnc); + print(priv, "%s |%s%s |", gsm_print_mcc(s->mcc), + gsm_print_mnc(s->mnc), + ((s->mnc & 0x00f) == 0x00f) ? " ":""); + print(priv, "0x%04x |0x%04x |", s->lac, s->cell_id); if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD)) - printf("yes |"); + print(priv, "yes |"); else - printf("no |"); + print(priv, "no |"); if ((cs->list[i].flags & GSM322_CS_FLAG_BARRED)) - printf("yes "); - else - printf("no "); - for (j = 0; j < 16; j++) { - if ((cs->list[i].class_barr & (1 << j))) - printf("*"); + print(priv, "barred |"); + else { + if (cs->list[i].sysinfo->cell_barr) + print(priv, "low |"); else - printf(" "); + print(priv, "normal |"); } - printf("|%4d |%4d\n", cs->list[i].min_db, - cs->list[i].max_pwr); + print(priv, "%4d |%4d\n", s->rxlev_acc_min_db, + s->ms_txpwr_max_ccch); } else - printf("n/a |n/a | | " - "|n/a |n/a\n"); + print(priv, "n/a |n/a |n/a |n/a |n/a |" + "n/a |n/a |n/a\n"); } + print(priv, "\n"); return 0; } -int gsm322_dump_sim_plmn(struct osmocom_ms *ms) -{ - struct gsm_subscriber *subscr = &ms->subscr; - struct gsm_sub_plmn_list *temp; - - printf("MCC |MNC\n"); - printf("-------+-------\n"); - llist_for_each_entry(temp, &subscr->plmn_list, entry) - printf("%03d |%02d\n", temp->mcc, temp->mnc); - - return 0; -} - -int gsm322_dump_forbidden_plmn(struct osmocom_ms *ms) +int gsm322_dump_forbidden_la(struct osmocom_ms *ms, + void (*print)(void *, const char *, ...), void *priv) { - struct gsm_subscriber *subscr = &ms->subscr; - struct gsm_sub_plmn_na *temp; + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_la_list *temp; - printf("MCC |MNC |cause\n"); - printf("-------+-------+-------\n"); - llist_for_each_entry(temp, &subscr->plmn_na, entry) - printf("%03d |%02d |#%d\n", temp->mcc, temp->mnc, - temp->cause); + print(priv, "MCC |MNC |LAC |cause\n"); + print(priv, "-------+-------+-------+-------\n"); + llist_for_each_entry(temp, &plmn->forbidden_la, entry) + print(priv, "%s |%s%s |0x%04x |#%d\n", + gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc), + ((temp->mnc & 0x00f) == 0x00f) ? " ":"", + temp->lac, temp->cause); return 0; } -int gsm322_dump_forbidden_la(struct osmocom_ms *ms) +int gsm322_dump_ba_list(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc, + void (*print)(void *, const char *, ...), void *priv) { - struct gsm322_plmn *plmn = &ms->plmn; - struct gsm322_la_list *temp; + struct gsm322_ba_list *ba; + int i; - printf("MCC |MNC |LAC |cause\n"); - printf("-------+-------+-------+-------\n"); - llist_for_each_entry(temp, &plmn->forbidden_la, entry) - printf("%03d |%02d |0x%04x |#%d\n", temp->mcc, temp->mnc, - temp->lac, temp->cause); + llist_for_each_entry(ba, &cs->ba_list, entry) { + if (mcc && mnc && (mcc != ba->mcc || mnc != ba->mnc)) + continue; + print(priv, "Band Allocation of network: MCC %03d MNC %02d " + "(%s, %s)\n", ba->mcc, ba->mnc, gsm_get_mcc(ba->mcc), + gsm_get_mnc(ba->mcc, ba->mnc)); + for (i = 0; i <= 1023; i++) { + if ((ba->freq[i >> 3] & (1 << (i & 7)))) + print(priv, " %d", i); + } + print(priv, "\n"); + } return 0; } @@ -2600,7 +3343,7 @@ int gsm322_init(struct osmocom_ms *ms) /* set initial state */ plmn->state = 0; cs->state = 0; - plmn->mode = PLMN_MODE_AUTO; + ms->settings.plmn_mode = PLMN_MODE_AUTO; /* init lists */ INIT_LLIST_HEAD(&plmn->event_queue); @@ -2638,14 +3381,14 @@ int gsm322_init(struct osmocom_ms *ms) break; } llist_add_tail(&ba->entry, &cs->ba_list); - LOGP(DCS, LOGL_INFO, "Read stored BA list (mcc=%d " - "mnc=%d %s, %s)\n", ba->mcc, ba->mnc, - gsm_get_mcc(ba->mcc), + LOGP(DCS, LOGL_INFO, "Read stored BA list (mcc=%s " + "mnc=%s %s, %s)\n", gsm_print_mcc(ba->mcc), + gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc), gsm_get_mnc(ba->mcc, ba->mnc)); } osmocom_fclose(fp); } else - LOGP(DCS, LOGL_NOTICE, "No stored BA list\n"); + LOGP(DCS, LOGL_INFO, "No stored BA list\n"); register_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL); @@ -2663,6 +3406,7 @@ int gsm322_exit(struct osmocom_ms *ms) char filename[sizeof(ms->name) + strlen(suffix) + 1]; struct gsm322_ba_list *ba; uint8_t buf[4]; + int i; LOGP(DPLMN, LOGL_INFO, "exit PLMN process\n"); LOGP(DCS, LOGL_INFO, "exit Cell Selection process\n"); @@ -2676,6 +3420,16 @@ int gsm322_exit(struct osmocom_ms *ms) stop_cs_timer(cs); stop_plmn_timer(plmn); + /* flush sysinfo */ + for (i = 0; i <= 1023; i++) { + if (cs->list[i].sysinfo) { + LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i); + talloc_free(cs->list[i].sysinfo); + cs->list[i].sysinfo = NULL; + } + cs->list[i].flags = 0; + } + /* store BA list */ strcpy(filename, ms->name); strcat(filename, suffix); @@ -2690,9 +3444,9 @@ int gsm322_exit(struct osmocom_ms *ms) buf[3] = ba->mnc & 0xff; rc = osmocom_fwrite(buf, 4, 1, fp); rc = osmocom_fwrite(ba->freq, sizeof(ba->freq), 1, fp); - LOGP(DCS, LOGL_INFO, "Write stored BA list (mcc=%d " - "mnc=%d %s, %s)\n", ba->mcc, ba->mnc, - gsm_get_mcc(ba->mcc), + LOGP(DCS, LOGL_INFO, "Write stored BA list (mcc=%s " + "mnc=%s %s, %s)\n", gsm_print_mcc(ba->mcc), + gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc), gsm_get_mnc(ba->mcc, ba->mnc)); } osmocom_fclose(fp);