blob: 91ffe4069ad3c9720bf45ec0cd5cef4f6c881269 [file] [log] [blame]
Harald Weltea183a6e2016-06-17 00:06:42 +02001/* MSC subscriber connection implementation */
2
3/*
4 * (C) 2016 by sysmocom s.m.f.c. <info@sysmocom.de>
5 * All Rights Reserved
6 *
7 * Author: Neels Hofmeyr
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Affero General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Affero General Public License for more details.
18 *
19 * You should have received a copy of the GNU Affero General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 *
22 */
23
24#include <osmocom/core/logging.h>
25#include <osmocom/core/fsm.h>
26
27#include <openbsc/osmo_msc.h>
28#include <openbsc/vlr.h>
29#include <openbsc/debug.h>
30#include <openbsc/transaction.h>
31
32static const struct value_string subscr_conn_fsm_event_names[] = {
33 OSMO_VALUE_STRING(SUBSCR_CONN_E_INVALID),
34 OSMO_VALUE_STRING(SUBSCR_CONN_E_ACCEPTED),
35 OSMO_VALUE_STRING(SUBSCR_CONN_E_BUMP),
36 OSMO_VALUE_STRING(SUBSCR_CONN_E_MO_CLOSE),
37 OSMO_VALUE_STRING(SUBSCR_CONN_E_CN_CLOSE),
38 OSMO_VALUE_STRING(SUBSCR_CONN_E_CLOSE_CONF),
39 { 0, NULL }
40};
41
42const struct value_string subscr_conn_from_names[] = {
43 OSMO_VALUE_STRING(SUBSCR_CONN_FROM_INVALID),
44 OSMO_VALUE_STRING(SUBSCR_CONN_FROM_LU),
45 OSMO_VALUE_STRING(SUBSCR_CONN_FROM_CM_SERVICE_REQ),
46 OSMO_VALUE_STRING(SUBSCR_CONN_FROM_PAGING_RESP),
47 { 0, NULL }
48};
49
50static void paging_resp(struct gsm_subscriber_connection *conn,
51 enum gsm_paging_event pe)
52{
53 subscr_paging_dispatch(GSM_HOOK_RR_PAGING, pe, NULL, conn, conn->subscr);
54}
55
56void subscr_conn_fsm_new(struct osmo_fsm_inst *fi, uint32_t event, void *data)
57{
58 struct gsm_subscriber_connection *conn = fi->priv;
59 enum subscr_conn_from from = SUBSCR_CONN_FROM_INVALID;
60 enum gsm_paging_event pe;
61
62 if (data) {
63 from = *(enum subscr_conn_from*)data;
64 LOGPFSM(fi, "%s\n", subscr_conn_from_name(from));
65 }
66
67 /* If accepted, transition the state, all other cases mean failure. */
68 switch (event) {
69 case SUBSCR_CONN_E_ACCEPTED:
70 osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_ACCEPTED, 0, 0);
71 break;
72
73 case SUBSCR_CONN_E_MO_CLOSE:
74 case SUBSCR_CONN_E_CN_CLOSE:
75 case SUBSCR_CONN_E_CLOSE_CONF:
76 break;
77
78 default:
79 LOGPFSM(fi, "Unexpected event: %d %s\n",
80 event, osmo_fsm_event_name(fi->fsm, event));
81 break;
82 }
83
84 /* if appropriate, signal paging success or failure */
85 if (from == SUBSCR_CONN_FROM_PAGING_RESP) {
86 pe = (fi->state == SUBSCR_CONN_S_ACCEPTED)?
87 GSM_PAGING_SUCCEEDED : GSM_PAGING_EXPIRED;
88 paging_resp(conn, pe);
89 }
90
91 /* On failure, discard the conn */
92 if (fi->state != SUBSCR_CONN_S_ACCEPTED) {
93 /* TODO: on MO_CLOSE or CN_CLOSE, first go to RELEASING and
94 * await BSC confirmation? */
95 osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0);
96 return;
97 }
98
99 /* On success, handle pending requests and/or close conn */
100
101 if (from == SUBSCR_CONN_FROM_CM_SERVICE_REQ) {
102 conn->received_cm_service_request = true;
103 LOGPFSM(fi, "received_cm_service_request = true\n");
104 }
105
106 osmo_fsm_inst_dispatch(fi, SUBSCR_CONN_E_BUMP, data);
107}
108
109#if 0
110 case SUBSCR_CONN_E_PARQ_SUCCESS:
111 osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_ACCEPTED, 0, 0);
112 accept_conn = true;
113 /* fall through */
114 case SUBSCR_CONN_E_PARQ_FAILURE:
115 parq_type = data ? *(enum vlr_parq_type*)data : VLR_PR_ARQ_T_INVALID;
116 switch (parq_type) {
117
118 case VLR_PR_ARQ_T_CM_SERV_REQ:
119 accept_conn = handle_cm_serv_result(fi, accept_conn);
120 break;
121
122 case VLR_PR_ARQ_T_PAGING_RESP:
123 accept_conn = handle_paging_result(fi, accept_conn);
124 break;
125
126 default:
127 LOGPFSML(fi, LOGL_ERROR,
128 "Invalid VLR Process Access Request type"
129 " %d\n", parq_type);
130 accept_conn = false;
131 break;
132 }
133 break;
134#endif
135
136static void subscr_conn_fsm_bump(struct osmo_fsm_inst *fi, uint32_t event, void *data)
137{
138 struct gsm_subscriber_connection *conn = fi->priv;
139
140 if (conn->silent_call)
141 return;
142
143 if (conn->received_cm_service_request)
144 return;
145
146 /* is this needed? */
147 if (conn->subscr && !llist_empty(&conn->subscr->requests))
148 return;
149
150 if (trans_has_conn(conn))
151 return;
152
153 osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0);
154}
155
156static void subscr_conn_fsm_accepted(struct osmo_fsm_inst *fi, uint32_t event, void *data)
157{
158 switch (event) {
159 case SUBSCR_CONN_E_BUMP:
160 subscr_conn_fsm_bump(fi, event, data);
161 return;
162
163 default:
164 break;
165 }
166 /* Whatever unexpected happens in the accepted state, it means release.
167 * Even if an unexpected event is passed, the safest thing to do is
168 * discard the conn. We don't expect another SUBSCR_CONN_E_ACCEPTED. */
169 osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0);
170}
171
172static void subscr_conn_fsm_release(struct osmo_fsm_inst *fi, uint32_t prev_state)
173{
174 struct gsm_subscriber_connection *conn = fi->priv;
175 if (!conn)
176 return;
177
178 /* temporary hack, see owned_by_msc */
179 if (!conn->owned_by_msc) {
180 DEBUGP(DMM, "%s leaving bsc_subscr_con_free() to bsc_api.c, owned_by_msc = false\n",
181 subscr_name(conn->subscr));
182 return;
183 }
184
185 DEBUGP(DMM, "%s calling bsc_subscr_con_free(), owned_by_msc = true\n",
186 subscr_name(conn->subscr));
187 gsm0808_clear(conn);
188 bsc_subscr_con_free(conn);
189}
190
191#define S(x) (1 << (x))
192
193static const struct osmo_fsm_state subscr_conn_fsm_states[] = {
194 [SUBSCR_CONN_S_NEW] = {
195 .name = OSMO_STRINGIFY(SUBSCR_CONN_S_NEW),
196 .in_event_mask = S(SUBSCR_CONN_E_ACCEPTED) |
197 S(SUBSCR_CONN_E_MO_CLOSE) |
198 S(SUBSCR_CONN_E_CN_CLOSE) |
199 S(SUBSCR_CONN_E_CLOSE_CONF),
200 .out_state_mask = S(SUBSCR_CONN_S_ACCEPTED) |
201 S(SUBSCR_CONN_S_RELEASED),
202 .action = subscr_conn_fsm_new,
203 },
204 [SUBSCR_CONN_S_ACCEPTED] = {
205 .name = OSMO_STRINGIFY(SUBSCR_CONN_S_ACCEPTED),
206 /* allow everything to release for any odd behavior */
207 .in_event_mask = S(SUBSCR_CONN_E_ACCEPTED) |
208 S(SUBSCR_CONN_E_BUMP) |
209 S(SUBSCR_CONN_E_MO_CLOSE) |
210 S(SUBSCR_CONN_E_CN_CLOSE) |
211 S(SUBSCR_CONN_E_CLOSE_CONF),
212 .out_state_mask = S(SUBSCR_CONN_S_RELEASED),
213 .action = subscr_conn_fsm_accepted,
214 },
215 [SUBSCR_CONN_S_RELEASED] = {
216 .name = OSMO_STRINGIFY(SUBSCR_CONN_S_RELEASED),
217 .onenter = subscr_conn_fsm_release,
218 },
219};
220
221static struct osmo_fsm subscr_conn_fsm = {
222 .name = "Subscr_Conn",
223 .states = subscr_conn_fsm_states,
224 .num_states = ARRAY_SIZE(subscr_conn_fsm_states),
225 .allstate_event_mask = 0,
226 .allstate_action = NULL,
227 .log_subsys = DVLR,
228 .event_names = subscr_conn_fsm_event_names,
229};
230
231int msc_create_conn_fsm(struct gsm_subscriber_connection *conn, const char *id)
232{
233 struct osmo_fsm_inst *fi;
234 OSMO_ASSERT(conn);
235
236 if (conn->conn_fsm) {
237 LOGP(DMM, LOGL_ERROR,
238 "%s: Error: connection already in use\n", id);
239 return -EINVAL;
240 }
241
242 fi = osmo_fsm_inst_alloc(&subscr_conn_fsm, conn, conn, LOGL_DEBUG, id);
243
244 if (!fi) {
245 LOGP(DMM, LOGL_ERROR,
246 "%s: Failed to allocate subscr conn master FSM\n", id);
247 return -ENOMEM;
248 }
249 conn->conn_fsm = fi;
250 return 0;
251}
252
253bool msc_subscr_conn_is_accepted(struct gsm_subscriber_connection *conn)
254{
255 if (!conn)
256 return false;
257 if (!conn->subscr)
258 return false;
259 if (!conn->conn_fsm)
260 return false;
261 if (conn->conn_fsm->state != SUBSCR_CONN_S_ACCEPTED)
262 return false;
263 return true;
264}
265
266void msc_subscr_conn_init(void)
267{
268 osmo_fsm_register(&subscr_conn_fsm);
269}