aboutsummaryrefslogtreecommitdiffstats
path: root/src/sgsn/gprs_mm_state_gb_fsm.c
blob: 4e1ed48d5c77b3c8ad461b50c33a56f839bdb233 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#include <osmocom/core/tdef.h>

#include <osmocom/sgsn/gprs_mm_state_gb_fsm.h>
#include <osmocom/sgsn/gprs_llc.h>

#include <osmocom/sgsn/debug.h>
#include <osmocom/sgsn/sgsn.h>

#define X(s) (1 << (s))

static const struct osmo_tdef_state_timeout mm_state_gb_fsm_timeouts[32] = {
	[ST_MM_IDLE] = { },
	[ST_MM_READY] = { .T=3314 },
	[ST_MM_STANDBY] = { },
};

#define mm_state_gb_fsm_state_chg(fi, NEXT_STATE) \
	osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, mm_state_gb_fsm_timeouts, sgsn->cfg.T_defs, -1)

static void st_mm_idle_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state) {
	struct sgsn_mm_ctx *ctx = fi->priv;

	if (ctx->gb.llme) {
		gprs_llgmm_unassign(ctx->gb.llme);
		ctx->gb.llme = NULL;
	}
}

static void st_mm_idle(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
	switch(event) {
	case E_MM_GPRS_ATTACH:
		mm_state_gb_fsm_state_chg(fi, ST_MM_READY);
		break;
	case E_MM_PDU_RECEPTION:
		break;
	}
}

static void st_mm_ready(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
	unsigned long t_secs;

	switch(event) {
	case E_MM_READY_TIMER_EXPIRY:
	case E_MM_IMPLICIT_DETACH:
		mm_state_gb_fsm_state_chg(fi, ST_MM_STANDBY);
		break;
	case E_MM_PDU_RECEPTION:
		/* RE-arm the READY timer upon receival of Gb PDUs */
		t_secs = osmo_tdef_get(sgsn->cfg.T_defs, 3314, OSMO_TDEF_S, -1);
		osmo_timer_schedule(&fi->timer, t_secs, 0);
		break;
	case E_MM_RA_UPDATE:
		break;
	}
}

static void st_mm_standby(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
	switch(event) {
	case E_MM_PDU_RECEPTION:
		mm_state_gb_fsm_state_chg(fi, ST_MM_READY);
		break;
	}
}

static struct osmo_fsm_state mm_state_gb_fsm_states[] = {
	[ST_MM_IDLE] = {
		.in_event_mask = X(E_MM_GPRS_ATTACH) | X(E_MM_PDU_RECEPTION),
		.out_state_mask = X(ST_MM_READY),
		.onenter = st_mm_idle_on_enter,
		.name = "Idle",
		.action = st_mm_idle,
	},
	[ST_MM_READY] = {
		.in_event_mask = X(E_MM_READY_TIMER_EXPIRY) | X(E_MM_RA_UPDATE) | X(E_MM_IMPLICIT_DETACH) | X(E_MM_PDU_RECEPTION),
		.out_state_mask = X(ST_MM_IDLE) | X(ST_MM_STANDBY),
		.name = "Ready",
		.action = st_mm_ready,
	},
	[ST_MM_STANDBY] = {
		.in_event_mask = X(E_MM_PDU_RECEPTION),
		.out_state_mask = X(ST_MM_IDLE) | X(ST_MM_READY),
		.name = "Standby",
		.action = st_mm_standby,
	},
};

const struct value_string mm_state_gb_fsm_event_names[] = {
	OSMO_VALUE_STRING(E_MM_GPRS_ATTACH),
	OSMO_VALUE_STRING(E_MM_PDU_RECEPTION),
	OSMO_VALUE_STRING(E_MM_IMPLICIT_DETACH),
	OSMO_VALUE_STRING(E_MM_READY_TIMER_EXPIRY),
	OSMO_VALUE_STRING(E_MM_RA_UPDATE),
	{ 0, NULL }
};

int mm_state_gb_fsm_timer_cb(struct osmo_fsm_inst *fi)
{
	switch(fi->state) {
	case ST_MM_READY:
		/* timer for mm state. state=READY: T3314 (aka TS 23.060 "READY timer") */
		osmo_fsm_inst_dispatch(fi, E_MM_READY_TIMER_EXPIRY, NULL);
		break;
	}

	return 0;
}

struct osmo_fsm mm_state_gb_fsm = {
	.name = "MM_STATE_Gb",
	.states = mm_state_gb_fsm_states,
	.num_states = ARRAY_SIZE(mm_state_gb_fsm_states),
	.event_names = mm_state_gb_fsm_event_names,
	.log_subsys = DMM,
	.timer_cb = mm_state_gb_fsm_timer_cb,
};

static __attribute__((constructor)) void mm_state_gb_fsm_init(void)
{
	osmo_fsm_register(&mm_state_gb_fsm);
}