blob: 4282aaf3c2b1c3f13b93a99421deeecc05f5531a [file] [log] [blame]
Harald Welte4bfdfe72009-06-10 23:11:52 +08001/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
2 * (C) 2009 by Andreas Eversberg <Andreas.Eversberg@versatel.de>
3 * All Rights Reserved
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 */
20
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <sys/types.h>
26
27#include <openbsc/gsm_04_08.h>
28#include <openbsc/debug.h>
29#include <openbsc/mncc.h>
30
31static struct mncc_names {
32 char *name;
33 int value;
34} mncc_names[] = {
35 {"MNCC_SETUP_REQ", 0x0101},
36 {"MNCC_SETUP_IND", 0x0102},
37 {"MNCC_SETUP_RSP", 0x0103},
38 {"MNCC_SETUP_CNF", 0x0104},
39 {"MNCC_SETUP_COMPL_REQ",0x0105},
40 {"MNCC_SETUP_COMPL_IND",0x0106},
41 {"MNCC_CALL_CONF_IND", 0x0107},
42 {"MNCC_CALL_PROC_REQ", 0x0108},
43 {"MNCC_PROGRESS_REQ", 0x0109},
44 {"MNCC_ALERT_REQ", 0x010a},
45 {"MNCC_ALERT_IND", 0x010b},
46 {"MNCC_NOTIFY_REQ", 0x010c},
47 {"MNCC_NOTIFY_IND", 0x010d},
48 {"MNCC_DISC_REQ", 0x010e},
49 {"MNCC_DISC_IND", 0x010f},
50 {"MNCC_REL_REQ", 0x0110},
51 {"MNCC_REL_IND", 0x0111},
52 {"MNCC_REL_CNF", 0x0112},
53 {"MNCC_FACILITY_REQ", 0x0113},
54 {"MNCC_FACILITY_IND", 0x0114},
55 {"MNCC_START_DTMF_IND", 0x0115},
56 {"MNCC_START_DTMF_RSP", 0x0116},
57 {"MNCC_START_DTMF_REJ", 0x0117},
58 {"MNCC_STOP_DTMF_IND", 0x0118},
59 {"MNCC_STOP_DTMF_RSP", 0x0119},
60 {"MNCC_MODIFY_REQ", 0x011a},
61 {"MNCC_MODIFY_IND", 0x011b},
62 {"MNCC_MODIFY_RSP", 0x011c},
63 {"MNCC_MODIFY_CNF", 0x011d},
64 {"MNCC_MODIFY_REJ", 0x011e},
65 {"MNCC_HOLD_IND", 0x011f},
66 {"MNCC_HOLD_CNF", 0x0120},
67 {"MNCC_HOLD_REJ", 0x0121},
68 {"MNCC_RETRIEVE_IND", 0x0122},
69 {"MNCC_RETRIEVE_CNF", 0x0123},
70 {"MNCC_RETRIEVE_REJ", 0x0124},
71 {"MNCC_USERINFO_REQ", 0x0125},
72 {"MNCC_USERINFO_IND", 0x0126},
73 {"MNCC_REJ_REQ", 0x0127},
74 {"MNCC_REJ_IND", 0x0128},
75
76 {"MNCC_BRIDGE", 0x0200},
77 {"MNCC_FRAME_RECV", 0x0201},
78 {"MNCC_FRAME_DROP", 0x0202},
79 {"MNCC_LCHAN_MODIFY", 0x0203},
80
81 {"GSM_TRAU_FRAME", 0x0300},
82
83 {NULL, 0}
84};
85
86static LLIST_HEAD(call_list);
87
88static u_int32_t new_callref = 0x00000001;
89
90char *get_mncc_name(int value)
91{
92 int i;
93
94 for (i = 0; mncc_names[i].name; i++) {
95 if (mncc_names[i].value == value)
96 return mncc_names[i].name;
97 }
98
99 return "MNCC_Unknown";
100}
101
102static void free_call(struct gsm_call *call)
103{
104 llist_del(&call->entry);
105 DEBUGP(DMNCC, "(call %x) Call removed.\n", call->callref);
106 free(call);
107}
108
109
110struct gsm_call *get_call_ref(u_int32_t callref)
111{
112 struct gsm_call *callt;
113
114 llist_for_each_entry(callt, &call_list, entry) {
115 if (callt->callref == callref)
116 return callt;
117 }
118 return NULL;
119}
120
121void mncc_set_cause(struct gsm_mncc *data, int loc, int val)
122{
123 data->fields |= MNCC_F_CAUSE;
124 data->cause.location = loc;
125 data->cause.value = val;
126}
127
128/* on incoming call, look up database and send setup to remote subscr. */
129static int mncc_setup_ind(struct gsm_call *call, int msg_type,
130 struct gsm_mncc *setup)
131{
132 struct gsm_mncc mncc;
133 struct gsm_call *remote;
134
135 /* already have remote call */
136 if (call->remote_ref)
137 return 0;
138
139 /* create remote call */
140 if (!(remote = calloc(1, sizeof(struct gsm_call)))) {
141 memset(&mncc, 0, sizeof(struct gsm_mncc));
142 mncc.callref = call->callref;
Harald Weltec66b71c2009-06-11 14:23:20 +0800143 mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
144 GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
Harald Welte4bfdfe72009-06-10 23:11:52 +0800145 mncc_send(call->net, MNCC_REJ_REQ, &mncc);
146 free_call(call);
147 return 0;
148 }
149 llist_add_tail(&remote->entry, &call_list);
150 remote->net = call->net;
151 remote->callref = new_callref++;
152 DEBUGP(DMNCC, "(call %x) Creating new remote instance %x.\n",
153 call->callref, remote->callref);
154
155 /* link remote call */
156 call->remote_ref = remote->callref;
157 remote->remote_ref = call->callref;
158
159 /* modify mode */
160 memset(&mncc, 0, sizeof(struct gsm_mncc));
161 mncc.callref = call->callref;
162 mncc.lchan_mode = GSM48_CMODE_SPEECH_EFR;
163 DEBUGP(DMNCC, "(call %x) Modify channel mode.\n", call->callref);
164 mncc_send(call->net, MNCC_LCHAN_MODIFY, &mncc);
165
166 /* send call proceeding */
167 memset(&mncc, 0, sizeof(struct gsm_mncc));
168 mncc.callref = call->callref;
169 DEBUGP(DMNCC, "(call %x) Accepting call.\n", call->callref);
170 mncc_send(call->net, MNCC_CALL_PROC_REQ, &mncc);
171
172 /* send setup to remote */
173// setup->fields |= MNCC_F_SIGNAL;
174// setup->signal = GSM48_SIGNAL_DIALTONE;
175 setup->callref = remote->callref;
176 DEBUGP(DMNCC, "(call %x) Forwarding SETUP to remote.\n", call->callref);
177 return mncc_send(remote->net, MNCC_SETUP_REQ, setup);
178}
179
180static int mncc_alert_ind(struct gsm_call *call, int msg_type,
181 struct gsm_mncc *alert)
182{
183 struct gsm_call *remote;
184
185 /* send alerting to remote */
186 if (!(remote = get_call_ref(call->remote_ref)))
187 return 0;
188 alert->callref = remote->callref;
189 DEBUGP(DMNCC, "(call %x) Forwarding ALERT to remote.\n", call->callref);
190 return mncc_send(remote->net, MNCC_ALERT_REQ, alert);
191}
192
193static int mncc_notify_ind(struct gsm_call *call, int msg_type,
194 struct gsm_mncc *notify)
195{
196 struct gsm_call *remote;
197
198 /* send notify to remote */
199 if (!(remote = get_call_ref(call->remote_ref)))
200 return 0;
201 notify->callref = remote->callref;
202 DEBUGP(DMNCC, "(call %x) Forwarding NOTIF to remote.\n", call->callref);
203 return mncc_send(remote->net, MNCC_NOTIFY_REQ, notify);
204}
205
206static int mncc_setup_cnf(struct gsm_call *call, int msg_type,
207 struct gsm_mncc *connect)
208{
209 struct gsm_mncc connect_ack;
210 struct gsm_call *remote;
211 u_int32_t refs[2];
212
213 /* acknowledge connect */
214 memset(&connect_ack, 0, sizeof(struct gsm_mncc));
215 connect_ack.callref = call->callref;
216 DEBUGP(DMNCC, "(call %x) Acknowledge SETUP.\n", call->callref);
217 mncc_send(call->net, MNCC_SETUP_COMPL_REQ, &connect_ack);
218
219 /* send connect message to remote */
220 if (!(remote = get_call_ref(call->remote_ref)))
221 return 0;
222 connect->callref = remote->callref;
223 DEBUGP(DMNCC, "(call %x) Sending CONNECT to remote.\n", call->callref);
224 mncc_send(remote->net, MNCC_SETUP_RSP, connect);
225
226 /* bridge tch */
227 refs[0] = call->callref;
228 refs[1] = call->remote_ref;
229 DEBUGP(DMNCC, "(call %x) Bridging with remote.\n", call->callref);
230 return mncc_send(call->net, MNCC_BRIDGE, refs);
231}
232
233static int mncc_disc_ind(struct gsm_call *call, int msg_type,
234 struct gsm_mncc *disc)
235{
236 struct gsm_call *remote;
237
238 /* send release */
239 DEBUGP(DMNCC, "(call %x) Releasing call with cause %d\n",
240 call->callref, disc->cause.value);
241 mncc_send(call->net, MNCC_REL_REQ, disc);
242
243 /* send disc to remote */
244 if (!(remote = get_call_ref(call->remote_ref))) {
245 return 0;
246 }
247 disc->callref = remote->callref;
248 DEBUGP(DMNCC, "(call %x) Disconnecting remote with cause %d\n",
249 remote->callref, disc->cause.value);
250 return mncc_send(remote->net, MNCC_DISC_REQ, disc);
251}
252
253static int mncc_rel_ind(struct gsm_call *call, int msg_type, struct gsm_mncc *rel)
254{
255 struct gsm_call *remote;
256
257 /* send release to remote */
258 if (!(remote = get_call_ref(call->remote_ref))) {
259 free_call(call);
260 return 0;
261 }
262 rel->callref = remote->callref;
263 DEBUGP(DMNCC, "(call %x) Releasing remote with cause %d\n",
264 call->callref, rel->cause.value);
265 mncc_send(remote->net, MNCC_REL_REQ, rel);
266
267 free_call(call);
268
269 return 0;
270}
271
272static int mncc_rel_cnf(struct gsm_call *call, int msg_type, struct gsm_mncc *rel)
273{
274 free_call(call);
275 return 0;
276}
277
278int mncc_recv(struct gsm_network *net, int msg_type, void *arg)
279{
280 struct gsm_mncc *data = arg;
281 int callref;
282 struct gsm_call *call = NULL, *callt;
283 int rc = 0;
284
285 /* Special messages */
286 switch(msg_type) {
287 }
288
289 /* find callref */
290 callref = data->callref;
291 llist_for_each_entry(callt, &call_list, entry) {
292 if (callt->callref == callref) {
293 call = callt;
294 break;
295 }
296 }
297
298 /* create callref, if setup is received */
299 if (!call) {
300 if (msg_type != MNCC_SETUP_IND)
301 return 0; /* drop */
302 /* create call */
303 if (!(call = calloc(1, sizeof(struct gsm_call)))) {
304 struct gsm_mncc rel;
305
306 memset(&rel, 0, sizeof(struct gsm_mncc));
307 rel.callref = callref;
Harald Weltec66b71c2009-06-11 14:23:20 +0800308 mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU,
309 GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
Harald Welte4bfdfe72009-06-10 23:11:52 +0800310 mncc_send(net, MNCC_REL_REQ, &rel);
311 return 0;
312 }
313 llist_add_tail(&call->entry, &call_list);
314 call->net = net;
315 call->callref = callref;
316 DEBUGP(DMNCC, "(call %x) Call created.\n", call->callref);
317 }
318
319 DEBUGP(DMNCC, "(call %x) Received message %s\n", call->callref,
320 get_mncc_name(msg_type));
321
322 switch(msg_type) {
323 case MNCC_SETUP_IND:
324 rc = mncc_setup_ind(call, msg_type, arg);
325 break;
326 case MNCC_SETUP_CNF:
327 rc = mncc_setup_cnf(call, msg_type, arg);
328 break;
329 case MNCC_SETUP_COMPL_IND:
330 break;
331 case MNCC_CALL_CONF_IND:
332 /* we now need to MODIFY the channel */
333 data->lchan_mode = GSM48_CMODE_SPEECH_EFR;
334 mncc_send(call->net, MNCC_LCHAN_MODIFY, data);
335 break;
336 case MNCC_ALERT_IND:
337 rc = mncc_alert_ind(call, msg_type, arg);
338 break;
339 case MNCC_NOTIFY_IND:
340 rc = mncc_notify_ind(call, msg_type, arg);
341 break;
342 case MNCC_DISC_IND:
343 rc = mncc_disc_ind(call, msg_type, arg);
344 break;
345 case MNCC_REL_IND:
346 case MNCC_REJ_IND:
347 rc = mncc_rel_ind(call, msg_type, arg);
348 break;
349 case MNCC_REL_CNF:
350 rc = mncc_rel_cnf(call, msg_type, arg);
351 break;
352 case MNCC_FACILITY_IND:
353 break;
354 case MNCC_START_DTMF_IND:
355 break;
356 case MNCC_STOP_DTMF_IND:
357 break;
358 case MNCC_MODIFY_IND:
Harald Weltec66b71c2009-06-11 14:23:20 +0800359 mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU,
360 GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
Harald Welte4bfdfe72009-06-10 23:11:52 +0800361 DEBUGP(DMNCC, "(call %x) Rejecting MODIFY with cause %d\n",
362 call->callref, data->cause.value);
363 rc = mncc_send(net, MNCC_MODIFY_REJ, data);
364 break;
365 case MNCC_MODIFY_CNF:
366 break;
367 case MNCC_HOLD_IND:
Harald Weltec66b71c2009-06-11 14:23:20 +0800368 mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU,
369 GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
Harald Welte4bfdfe72009-06-10 23:11:52 +0800370 DEBUGP(DMNCC, "(call %x) Rejecting HOLD with cause %d\n",
371 call->callref, data->cause.value);
372 rc = mncc_send(net, MNCC_HOLD_REJ, data);
373 break;
374 case MNCC_RETRIEVE_IND:
Harald Weltec66b71c2009-06-11 14:23:20 +0800375 mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU,
376 GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
Harald Welte4bfdfe72009-06-10 23:11:52 +0800377 DEBUGP(DMNCC, "(call %x) Rejecting RETRIEVE with cause %d\n",
378 call->callref, data->cause.value);
379 rc = mncc_send(net, MNCC_RETRIEVE_REJ, data);
380 break;
381 default:
382 DEBUGP(DMNCC, "(call %x) Message unhandled\n", callref);
383 break;
384 }
385
386 return rc;
387}