| /* GMM mobility management states on the network side, 3GPP TS 24.008 ยง 4.1.3.3 */ |
| /* |
| * (C) 2019 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de> |
| * All Rights Reserved |
| * |
| * SPDX-License-Identifier: AGPL-3.0+ |
| * |
| * Author: Pau Espin Pedrol <pespin@sysmocom.de> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU Affero General Public License as published by |
| * the Free Software Foundation; either version 3 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU Affero General Public License for more details. |
| * |
| * You should have received a copy of the GNU Affero General Public License |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| #include <osmocom/core/tdef.h> |
| |
| #include <osmocom/sgsn/gprs_gmm_fsm.h> |
| #include <osmocom/sgsn/gprs_mm_state_gb_fsm.h> |
| #include <osmocom/sgsn/gprs_mm_state_iu_fsm.h> |
| |
| #include <osmocom/sgsn/debug.h> |
| #include <osmocom/sgsn/sgsn.h> |
| |
| #define X(s) (1 << (s)) |
| |
| static const struct osmo_tdef_state_timeout gmm_fsm_timeouts[32] = { |
| [ST_GMM_DEREGISTERED] = { }, |
| [ST_GMM_COMMON_PROC_INIT] = { }, |
| [ST_GMM_REGISTERED_NORMAL] = { }, |
| [ST_GMM_REGISTERED_SUSPENDED] = { }, |
| [ST_GMM_DEREGISTERED_INIT] = { }, |
| }; |
| |
| #define gmm_fsm_state_chg(fi, NEXT_STATE) \ |
| osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, gmm_fsm_timeouts, sgsn->cfg.T_defs, -1) |
| |
| static void st_gmm_deregistered(struct osmo_fsm_inst *fi, uint32_t event, void *data) |
| { |
| switch(event) { |
| case E_GMM_COMMON_PROC_INIT_REQ: |
| gmm_fsm_state_chg(fi, ST_GMM_COMMON_PROC_INIT); |
| break; |
| case E_GMM_ATTACH_SUCCESS: |
| gmm_fsm_state_chg(fi, ST_GMM_REGISTERED_NORMAL); |
| break; |
| } |
| } |
| |
| static void st_gmm_common_proc_init(struct osmo_fsm_inst *fi, uint32_t event, void *data) |
| { |
| switch(event) { |
| case E_GMM_COMMON_PROC_INIT_REQ: |
| /* MS may retransmit GPRS Attach Request if for some reason |
| * CommonProcedure didn't go forward correctly */ |
| break; |
| /* TODO: events not used |
| case E_GMM_LOWER_LAYER_FAILED: |
| case E_GMM_COMMON_PROC_FAILED: |
| gmm_fsm_state_chg(fi, ST_GMM_DEREGISTERED); |
| break; |
| */ |
| case E_GMM_COMMON_PROC_SUCCESS: |
| case E_GMM_ATTACH_SUCCESS: |
| gmm_fsm_state_chg(fi, ST_GMM_REGISTERED_NORMAL); |
| break; |
| } |
| } |
| |
| static void st_gmm_registered_normal(struct osmo_fsm_inst *fi, uint32_t event, void *data) |
| { |
| switch(event) { |
| case E_GMM_COMMON_PROC_INIT_REQ: |
| gmm_fsm_state_chg(fi, ST_GMM_COMMON_PROC_INIT); |
| break; |
| /* case E_GMM_NET_INIT_DETACH_REQ: |
| gmm_fsm_state_chg(fi, ST_GMM_DEREGISTERED_INIT); |
| break; */ |
| /* case E_GMM_MS_INIT_DETACH_REQ: |
| gmm_fsm_state_chg(fi, ST_GMM_DEREGISTERED); |
| break; */ |
| case E_GMM_SUSPEND: |
| gmm_fsm_state_chg(fi, ST_GMM_REGISTERED_SUSPENDED); |
| break; |
| } |
| } |
| |
| static void st_gmm_registered_suspended(struct osmo_fsm_inst *fi, uint32_t event, void *data) |
| { |
| switch(event) { |
| case E_GMM_RESUME: /* explicit BSSGP RESUME from BSS */ |
| gmm_fsm_state_chg(fi, ST_GMM_REGISTERED_NORMAL); |
| break; |
| case E_GMM_COMMON_PROC_INIT_REQ: /* implicit resume from MS */ |
| gmm_fsm_state_chg(fi, ST_GMM_COMMON_PROC_INIT); |
| break; |
| } |
| } |
| |
| static void st_gmm_deregistered_init(struct osmo_fsm_inst *fi, uint32_t event, void *data) |
| { |
| switch(event) { |
| /* TODO: events not used in osmo-sgsn code |
| case E_GMM_DETACH_ACCEPTED: |
| case E_GMM_LOWER_LAYER_FAILED: |
| gmm_fsm_state_chg(fi, ST_GMM_DEREGISTERED); |
| break; |
| */ |
| } |
| } |
| |
| static struct osmo_fsm_state gmm_fsm_states[] = { |
| [ST_GMM_DEREGISTERED] = { |
| .in_event_mask = |
| X(E_GMM_COMMON_PROC_INIT_REQ) | |
| X(E_GMM_ATTACH_SUCCESS), |
| .out_state_mask = X(ST_GMM_COMMON_PROC_INIT), |
| .name = "Deregistered", |
| .action = st_gmm_deregistered, |
| }, |
| [ST_GMM_COMMON_PROC_INIT] = { |
| .in_event_mask = |
| /* X(E_GMM_LOWER_LAYER_FAILED) | */ |
| /* X(E_GMM_COMMON_PROC_FAILED) | */ |
| X(E_GMM_COMMON_PROC_SUCCESS) | |
| X(E_GMM_ATTACH_SUCCESS) | |
| X(E_GMM_COMMON_PROC_INIT_REQ), |
| .out_state_mask = |
| X(ST_GMM_DEREGISTERED) | |
| X(ST_GMM_REGISTERED_NORMAL), |
| .name = "CommonProcedureInitiated", |
| .action = st_gmm_common_proc_init, |
| }, |
| [ST_GMM_REGISTERED_NORMAL] = { |
| .in_event_mask = |
| X(E_GMM_COMMON_PROC_INIT_REQ) | |
| /* X(E_GMM_NET_INIT_DETACH_REQ) | */ |
| /* X(E_GMM_MS_INIT_DETACH_REQ) | */ |
| X(E_GMM_SUSPEND), |
| .out_state_mask = |
| X(ST_GMM_DEREGISTERED) | |
| X(ST_GMM_COMMON_PROC_INIT) | |
| X(ST_GMM_DEREGISTERED_INIT) | |
| X(ST_GMM_REGISTERED_SUSPENDED), |
| .name = "Registered.NORMAL", |
| .action = st_gmm_registered_normal, |
| }, |
| [ST_GMM_REGISTERED_SUSPENDED] = { |
| .in_event_mask = X(E_GMM_RESUME) | |
| X(E_GMM_COMMON_PROC_INIT_REQ), |
| .out_state_mask = |
| X(ST_GMM_DEREGISTERED) | |
| X(ST_GMM_REGISTERED_NORMAL) | |
| X(ST_GMM_COMMON_PROC_INIT), |
| .name = "Registered.SUSPENDED", |
| .action = st_gmm_registered_suspended, |
| }, |
| [ST_GMM_DEREGISTERED_INIT] = { |
| .in_event_mask = 0 |
| /* X(E_GMM_DETACH_ACCEPTED) | */ |
| /* X(E_GMM_LOWER_LAYER_FAILED) */, |
| .out_state_mask = X(ST_GMM_DEREGISTERED), |
| .name = "DeregisteredInitiated", |
| .action = st_gmm_deregistered_init, |
| }, |
| }; |
| |
| const struct value_string gmm_fsm_event_names[] = { |
| OSMO_VALUE_STRING(E_GMM_COMMON_PROC_INIT_REQ), |
| /* OSMO_VALUE_STRING(E_GMM_COMMON_PROC_FAILED), */ |
| /* OSMO_VALUE_STRING(E_GMM_LOWER_LAYER_FAILED), */ |
| OSMO_VALUE_STRING(E_GMM_COMMON_PROC_SUCCESS), |
| OSMO_VALUE_STRING(E_GMM_ATTACH_SUCCESS), |
| /* OSMO_VALUE_STRING(E_GMM_NET_INIT_DETACH_REQ), */ |
| /* OSMO_VALUE_STRING(E_GMM_MS_INIT_DETACH_REQ), */ |
| /* OSMO_VALUE_STRING(E_GMM_DETACH_ACCEPTED), */ |
| OSMO_VALUE_STRING(E_GMM_SUSPEND), |
| OSMO_VALUE_STRING(E_GMM_CLEANUP), |
| OSMO_VALUE_STRING(E_GMM_RAT_CHANGE), |
| { 0, NULL } |
| }; |
| |
| void gmm_fsm_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) { |
| struct sgsn_mm_ctx *mmctx = fi->priv; |
| struct gmm_rat_change_data *rat_chg = (struct gmm_rat_change_data *)data; |
| |
| switch (event) { |
| case E_GMM_RAT_CHANGE: |
| |
| switch (fi->state) { |
| case ST_GMM_COMMON_PROC_INIT: |
| gmm_fsm_state_chg(fi, ST_GMM_DEREGISTERED); |
| default: |
| if (mmctx->ran_type == MM_CTX_T_GERAN_Gb) |
| osmo_fsm_inst_dispatch(mmctx->gb.mm_state_fsm, E_MM_GPRS_DETACH, NULL); |
| else if (mmctx->ran_type == MM_CTX_T_UTRAN_Iu) { |
| osmo_fsm_inst_dispatch(mmctx->iu.mm_state_fsm, E_PMM_PS_DETACH, NULL); |
| mmctx->gb.llme = rat_chg->llme; |
| } |
| |
| mmctx->ran_type = rat_chg->new_ran_type; |
| break; |
| } |
| |
| case E_GMM_CLEANUP: |
| switch (fi->state) { |
| case ST_GMM_DEREGISTERED: |
| break; |
| default: |
| gmm_fsm_state_chg(fi, ST_GMM_DEREGISTERED); |
| break; |
| } |
| } |
| } |
| |
| int gmm_fsm_timer_cb(struct osmo_fsm_inst *fi) |
| { |
| return 0; |
| } |
| |
| struct osmo_fsm gmm_fsm = { |
| .name = "GMM", |
| .states = gmm_fsm_states, |
| .num_states = ARRAY_SIZE(gmm_fsm_states), |
| .event_names = gmm_fsm_event_names, |
| .allstate_event_mask = X(E_GMM_CLEANUP) | X(E_GMM_RAT_CHANGE), |
| .allstate_action = gmm_fsm_allstate_action, |
| .log_subsys = DMM, |
| .timer_cb = gmm_fsm_timer_cb, |
| }; |
| |
| static __attribute__((constructor)) void gmm_fsm_init(void) |
| { |
| OSMO_ASSERT(osmo_fsm_register(&gmm_fsm) == 0); |
| } |