blob: bd708ed0ab6db37c80786c2b70e26cbdcca76081 [file] [log] [blame]
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +08001/* Use the UniPorte library to allocate endpoints */
2/*
3 * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
4 * (C) 2010 by On-Waves
5 * All Rights Reserved
6 *
Holger Hans Peter Freytherde56c222011-01-16 17:45:14 +01007 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080010 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Holger Hans Peter Freytherde56c222011-01-16 17:45:14 +010015 * GNU Affero General Public License for more details.
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080016 *
Holger Hans Peter Freytherde56c222011-01-16 17:45:14 +010017 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080019 *
20 */
21
22#include <mgcp_ss7.h>
23#include <mgcp/mgcp.h>
24#include <mgcp/mgcp_internal.h>
25
Holger Hans Peter Freythercbf7d182010-07-31 05:25:35 +080026#include <cellmgr_debug.h>
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080027
Holger Hans Peter Freythercbf7d182010-07-31 05:25:35 +080028#include <osmocore/select.h>
29#include <osmocore/talloc.h>
30#include <osmocore/timer.h>
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080031
Holger Hans Peter Freythercbf7d182010-07-31 05:25:35 +080032#include <osmocom/vty/command.h>
33#include <osmocom/vty/vty.h>
Holger Hans Peter Freythercc1a9382010-08-04 06:24:04 +080034#include <osmocom/vty/telnet_interface.h>
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080035
36/* uniporte includes */
37#ifndef NO_UNIPORTE
38#include <UniPorte.h>
39#include <BusMastHostApi.h>
40#include <MtnSa.h>
41#include <SystemLayer.h>
42#include <PredefMobs.h>
43#endif
44
45#include <errno.h>
46#include <limits.h>
47#include <string.h>
48#include <unistd.h>
49#include <signal.h>
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080050
51#ifndef _GNU_SOURCE
52#define _GNU_SOURCE
53#endif
54#include <getopt.h>
55
Holger Hans Peter Freythercbf7d182010-07-31 05:25:35 +080056static struct log_target *stderr_target;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080057static char *config_file = "mgcp_mgw.cfg";
58static int exit_on_failure = 0;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080059
60#define TO_MGW_PORT(no) (no-1)
61#define FROM_MGW_PORT(no) (no+1)
62
63static struct mgcp_ss7 *s_ss7;
Holger Hans Peter Freyther757f1742010-09-17 22:03:08 +080064static struct mgcp_config *g_cfg;
Holger Hans Peter Freytherd5918ff2010-08-06 16:03:27 +000065static int s_vad_enabled = 1;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080066
Holger Hans Peter Freyther8fa8e582010-12-01 23:14:57 +010067/* gain settings */
68int s_digital_inp_gain = 31;
69int s_digital_out_gain = 31;
70int s_upstr_agc_enbl = 0;
71int s_upstr_adp_rate = 100;
72int s_upstr_max_gain = 46;
73int s_upstr_target_lvl = 20;
74int s_dwnstr_agc_enbl = 0;
75int s_dwnstr_adp_rate = 100;
76int s_dwnstr_max_gain = 46;
77int s_dwnstr_target_lvl = 20;
78
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080079struct mgcp_ss7_endpoint {
80 unsigned int port;
81 int block;
82};
83
84static void mgcp_ss7_endp_free(struct mgcp_ss7* ss7, int endp);
Holger Hans Peter Freyther9ed3e1b2010-07-31 05:22:56 +080085static void mgcp_ss7_do_exec(struct mgcp_ss7 *mgcp, uint8_t type, uint32_t port, uint32_t param);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080086static void mgcp_mgw_vty_init();
87
88static void check_exit(int status)
89{
90 if (exit_on_failure && status == 21) {
91 LOGP(DMGCP, LOGL_ERROR, "Failure detected with the MGW. Exiting.\n");
92 exit(-1);
93 }
94}
95
96#ifndef NO_UNIPORTE
97static void Force_Poll( int milliseconds )
98{
99 int timeout = 0;
100 unsigned long startTime;
101
102 startTime = SysLyrGetTime();
103
104 /* Loop until the specified number of milliseconds
105 * have elapsed.
106 */
107 do {
108 MtnSaPoll();
109 SysLyrSleep( 20 );
110 } while ((SysLyrGetTime()-startTime)<(unsigned long)milliseconds);
111 return;
112}
113
114static char eventName[Event_TELEMETRY_DATA + 1][128] = {
115 { "Event_NOT_READY" },
116 { "Event_READY" },
117 { "Event_ANSWER" },
118 { "Event_OUTGOING_CALL" },
119 { "Event_ABORT" },
120 { "Event_CONNECT" },
121 { "Event_DISCONNECT" },
122 { "Event_MANAGED_OBJECT_GET_COMPLETE" },
123 { "Event_MANAGED_OBJECT_GET_AND_CLEAR_COMPLETE" },
124 { "Event_MANAGED_OBJECT_SET_COMPLETE" },
125 { "Event_MANAGED_OBJECT_TRAP" },
126 { "Event_PREDEF_MOB_SET_COMPLETE" },
127 { "Event_PREDEF_MOB_GET_COMPLETE" },
128 { "Event_USER_MOB_DEFINE_COMPLETE" },
129 { "Event_USER_MOB_SET_COMPLETE" },
130 { "Event_USER_MOB_GET_COMPLETE" },
131 { "Event_RECEIVE_DATA" },
132 { "Event_SEND_COMPLETE" },
133 { "Event_TDM_CONNECT_COMPLETE" },
134 { "Event_LOG" },
135 { "Event_DEVICE_IN_CONTACT" },
136 { "Event_DEVICE_MANAGED" },
137 { "Event_DEVICE_OUT_OF_CONTACT" },
138 { "Event_TELEMETRY_DATA" } };
139
140static char stateName[PortState_END_OF_ENUM][128] = {
141 { "PortState_IDLE" },
142 { "PortState_SIGNALING" },
143 { "PortState_INITIATING" },
144 { "PortState_LINK" },
145 { "PortState_TRAINING" },
146 { "PortState_EC_NEGOTIATING" },
147 { "PortState_DATA" },
148 { "PortState_RESYNCING" },
149 { "PortState_FAX" },
150 { "PortState_COMMAND_ESCAPE" },
151 { "PortState_TERMINATING" },
152 { "PortState_VOICE" },
153 { "PortState_PORT_RESET" },
154 { "PortState_DSP_RESET" },
155 { "PortState_ALLOCATED" },
156 { "PortState_OUT_OF_SERVICE" },
157 { "PortState_RECONFIGURE" },
158 { "PortState_ON_HOLD" } };
159static int uniporte_events(unsigned long port, EventTypeT event,
160 void *event_data, unsigned long event_data_length ) {
161 char text[128];
162 ManObjectInfoPtr info;
163 DataReceiveInfoPtr dataInfo;
164 int i;
165 ToneDetectionPtr tones;
166
167
168 /* Don't print output when we receive data or complete
169 * sending data. That would be too verbose.
170 */
171 if (event==Event_DEVICE_MANAGED) {
172 MtnSaSetManObject(0, ChannelType_ETHERNET, ManObj_C_MOE_COMM_LOSS_RESET_DELAY ,
173 10, 0);
174 }
175 else if (event==Event_MANAGED_OBJECT_TRAP ) {
176 info = (ManObjectInfoPtr)event_data;
177 if (info->trapId == Trap_PORT_STATE_CHANGE) {
178 sprintf(text, "Port #%ld, Change to state %s", port, stateName[info->value]);
179 puts(text);
180
181 /* update the mgcp state */
182 int mgcp_endp = FROM_MGW_PORT(port);
183 if (s_ss7->mgw_end[mgcp_endp].block != 1)
184 fprintf(stderr, "State change on a non blocked port. ERROR.\n");
185 s_ss7->mgw_end[mgcp_endp].block = 0;
186 }
187 }
188 else if ( event == Event_MANAGED_OBJECT_SET_COMPLETE ) {
189 info = (ManObjectInfoPtr)event_data;
190
191 sprintf(text, "Object %d value %d status %d", info->object, info->value,
192 info->status );
193 puts(text);
194 check_exit(info->status);
195 }
196 else if ( ( event == Event_USER_MOB_SET_COMPLETE ) ||
197 ( event == Event_USER_MOB_DEFINE_COMPLETE ) )
198 {
199 info = (ManObjectInfoPtr)event_data;
200
201 sprintf( text, "Mob ID %d status %d", info->MOBId, info->status );
202 puts(text);
203 check_exit(info->status);
204 }
205 else if ( event == Event_USER_MOB_GET_COMPLETE )
206 {
207 info = (ManObjectInfoPtr)event_data;
208
209 sprintf( text, "Mob ID %d status %d", info->MOBId, info->status );
210 puts(text);
211 check_exit(info->status);
212 }
213 else if (event == Event_CONNECT)
214 {
215 sprintf(text, "Port %d connected",port );
216 }
217 else if (event == Event_PREDEF_MOB_GET_COMPLETE)
218 {
219 info = (ManObjectInfoPtr)event_data;
220
221 sprintf(text, "Mob ID %d status %d", info->MOBId, info->status );
222 puts(text);
223 check_exit(info->status);
224 }
225
226 return( 0 );
227}
228
229static int initialize_uniporte(struct mgcp_ss7 *mgcp)
230{
231 ProfileT profile;
232 unsigned long mgw_address;
233 int rc;
234
235 LOGP(DMGCP, LOGL_NOTICE, "Initializing MGW on %s\n", mgcp->cfg->bts_ip);
236
237 MtnSaSetEthernetOnly();
238 rc = MtnSaStartup(uniporte_events);
239 if (rc != 0)
240 LOGP(DMGCP, LOGL_ERROR, "Failed to startup the MGW.\n");
241 SysEthGetHostAddress(mgcp->cfg->bts_ip, &mgw_address);
242 rc = MtnSaRegisterEthernetDevice(mgw_address, 0);
243 if (rc != 0)
244 LOGP(DMGCP, LOGL_ERROR, "Failed to register ethernet.\n");
245 Force_Poll(2000);
246 MtnSaTakeOverDevice(0);
247 Force_Poll(2000);
248 MtnSaSetReceiveTraps(1);
249 MtnSaSetTransparent();
250
251 /* change the voice profile to AMR */
252 MtnSaGetProfile(ProfileType_VOICE, 0, &profile);
253 profile.countryCode = CountryCode_INTERNAT_ALAW;
254 MtnSaSetProfile(ProfileType_VOICE, 0, &profile);
255
256 if (MtnSaGetPortCount() == 0)
257 return -1;
258
259 return 0;
260}
261
262
263static void* start_uniporte(void *_ss7) {
264 struct llist_head blocked;
265 struct mgcp_ss7_cmd *cmd, *tmp;
266 struct mgcp_ss7 *ss7 = _ss7;
267
268 s_ss7 = ss7;
269
270 if (initialize_uniporte(ss7) != 0) {
271 fprintf(stderr, "Failed to create Uniporte.\n");
272 exit(-1);
273 return 0;
274 }
275
276 fprintf(stderr, "Created the MGCP processing thread.\n");
277 INIT_LLIST_HEAD(&blocked);
278 for (;;) {
279 thread_swap(ss7->cmd_queue);
280start_over:
281 /* handle items that are currently blocked */
282 llist_for_each_entry_safe(cmd, tmp, &blocked, entry) {
283 if (ss7->mgw_end[cmd->port].block)
284 continue;
285
286 mgcp_ss7_do_exec(ss7, cmd->type, cmd->port, cmd->param);
287 llist_del(&cmd->entry);
288 free(cmd);
289
290 /* We might have unblocked something, make sure we operate in order */
291 MtnSaPoll();
292 goto start_over;
293 }
294
295 llist_for_each_entry_safe(cmd, tmp, ss7->cmd_queue->main_head, entry) {
296 if (ss7->mgw_end[cmd->port].block) {
297 llist_del(&cmd->entry);
298 llist_add_tail(&cmd->entry, &blocked);
299 continue;
300 }
301
302 mgcp_ss7_do_exec(ss7, cmd->type, cmd->port, cmd->param);
303 llist_del(&cmd->entry);
304 free(cmd);
305
306 /* We might have unblocked something, make sure we operate in order */
307 MtnSaPoll();
308 goto start_over;
309 }
310
311 Force_Poll(20);
312 }
313
314 return 0;
315}
316#endif
317
318static void update_mute_status(int mgw_port, int conn_mode)
319{
320#ifndef NO_UNIPORTE
321 if (conn_mode == MGCP_CONN_NONE) {
322 MtnSaSetManObject(mgw_port, ChannelType_PORT, ManObj_C_VOICE_UPSTREAM_MUTE, 1, 0);
323 MtnSaSetManObject(mgw_port, ChannelType_PORT, ManObj_C_VOICE_DOWNSTREAM_MUTE, 1, 0);
324 } else if (conn_mode == MGCP_CONN_RECV_ONLY) {
325 MtnSaSetManObject(mgw_port, ChannelType_PORT, ManObj_C_VOICE_UPSTREAM_MUTE, 1, 0);
326 MtnSaSetManObject(mgw_port, ChannelType_PORT, ManObj_C_VOICE_DOWNSTREAM_MUTE, 0, 0);
327 } else if (conn_mode == MGCP_CONN_SEND_ONLY) {
328 MtnSaSetManObject(mgw_port, ChannelType_PORT, ManObj_C_VOICE_UPSTREAM_MUTE, 0, 0);
329 MtnSaSetManObject(mgw_port, ChannelType_PORT, ManObj_C_VOICE_DOWNSTREAM_MUTE, 1, 0);
330 } else if (conn_mode == MGCP_CONN_RECV_SEND) {
331 MtnSaSetManObject(mgw_port, ChannelType_PORT, ManObj_C_VOICE_UPSTREAM_MUTE, 0, 0);
332 MtnSaSetManObject(mgw_port, ChannelType_PORT, ManObj_C_VOICE_DOWNSTREAM_MUTE, 0, 0);
333 } else {
334 LOGP(DMGCP, LOGL_ERROR, "Unhandled conn mode: %d\n", conn_mode);
335 }
336#endif
337}
338
339#ifndef NO_UNIPORTE
340static void allocate_endp(struct mgcp_ss7 *ss7, int endp_no)
341{
342 int mgw_port;
343 unsigned long mgw_address, loc_address;
344 struct mgcp_ss7_endpoint *mgw_endp = &ss7->mgw_end[endp_no];
345 struct mgcp_endpoint *mg_endp = &ss7->cfg->endpoints[endp_no];
346
347 mgw_port = TO_MGW_PORT(endp_no);
348 mgw_endp->port = MtnSaAllocate(mgw_port);
349 if (mgw_endp->port == UINT_MAX) {
350 fprintf(stderr, "Failed to allocate the port: %d\n", endp_no);
351 return;
352 }
353
Holger Hans Peter Freyther8fa8e582010-12-01 23:14:57 +0100354 /* Gain settings, apply before switching the port to voice */
355 MtnSaSetManObject(mgw_port, ChannelType_PORT,
356 ManObj_C_VOICE_INPUT_DIGITAL_GAIN, s_digital_inp_gain, 0);
357 MtnSaSetManObject(mgw_port, ChannelType_PORT,
358 ManObj_C_VOICE_OUTPUT_DIGITAL_GAIN, s_digital_out_gain, 0);
359 MtnSaSetManObject(mgw_port, ChannelType_PORT,
Holger Hans Peter Freyther2c472a32010-12-17 17:11:35 +0100360 ManObj_G_US_AGC_ENABLE, s_upstr_agc_enbl, 0);
Holger Hans Peter Freyther8fa8e582010-12-01 23:14:57 +0100361 MtnSaSetManObject(mgw_port, ChannelType_PORT,
Holger Hans Peter Freyther2c472a32010-12-17 17:11:35 +0100362 ManObj_G_DS_AGC_ENABLE, s_dwnstr_agc_enbl, 0);
Holger Hans Peter Freyther8fa8e582010-12-01 23:14:57 +0100363 MtnSaSetManObject(mgw_port, ChannelType_PORT,
Holger Hans Peter Freyther2c472a32010-12-17 17:11:35 +0100364 ManObj_G_US_ADAPTATION_RATE, s_upstr_adp_rate, 0);
Holger Hans Peter Freyther8fa8e582010-12-01 23:14:57 +0100365 MtnSaSetManObject(mgw_port, ChannelType_PORT,
Holger Hans Peter Freyther2c472a32010-12-17 17:11:35 +0100366 ManObj_G_DS_ADAPTATION_RATE, s_dwnstr_adp_rate, 0);
Holger Hans Peter Freyther8fa8e582010-12-01 23:14:57 +0100367 MtnSaSetManObject(mgw_port, ChannelType_PORT,
368 ManObj_G_US_MAX_APPLIED_GAIN, s_upstr_max_gain, 0);
369 MtnSaSetManObject(mgw_port, ChannelType_PORT,
370 ManObj_G_DS_MAX_APPLIED_GAIN, s_dwnstr_max_gain, 0);
371 MtnSaSetManObject(mgw_port, ChannelType_PORT,
372 ManObj_C_US_TARGET_LEVEL, s_upstr_target_lvl, 0);
373 MtnSaSetManObject(mgw_port, ChannelType_PORT,
374 ManObj_C_US_TARGET_LEVEL, s_dwnstr_target_lvl, 0);
375
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800376 /* Select AMR 5.9, Payload 98, no CRC, hardcoded */
377 MtnSaApplyProfile(mgw_port, ProfileType_VOICE, 0);
378 MtnSaSetManObject(mgw_port, ChannelType_PORT,
379 ManObj_G_DATA_PATH, DataPathT_ETHERNET, 0 );
380 MtnSaSetManObject(mgw_port, ChannelType_PORT,
381 ManObj_C_VOICE_RTP_TELEPHONE_EVENT_PT_TX, ss7->cfg->audio_payload, 0);
382 MtnSaSetManObject(mgw_port, ChannelType_PORT,
383 ManObj_G_RTP_AMR_PAYLOAD_TYPE, ss7->cfg->audio_payload, 0);
384 MtnSaSetManObject(mgw_port, ChannelType_PORT,
385 ManObj_G_RTP_AMR_PAYLOAD_FORMAT, RtpAmrPayloadFormat_OCTET_ALIGNED, 0);
386 MtnSaSetManObject(mgw_port, ChannelType_PORT,
387 ManObj_G_VOICE_ENCODING, Voice_Encoding_AMR_5_90, 0);
Holger Hans Peter Freytherd5918ff2010-08-06 16:03:27 +0000388 MtnSaSetManObject(mgw_port, ChannelType_PORT,
389 ManObj_C_VOICE_VAD_CNG, s_vad_enabled, 0);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800390
391 update_mute_status(mgw_port, mg_endp->conn_mode);
392
393 /* set the addresses */
394 SysEthGetHostAddress(ss7->cfg->bts_ip, &mgw_address);
395 SysEthGetHostAddress(ss7->cfg->local_ip, &loc_address);
396 MtnSaSetVoIpAddresses(mgw_port,
397 mgw_address, mg_endp->rtp_port,
398 loc_address, mg_endp->rtp_port);
399 MtnSaConnect(mgw_port, mgw_port);
400 mgw_endp->block = 1;
401}
402#endif
403
Holger Hans Peter Freyther9ed3e1b2010-07-31 05:22:56 +0800404static void mgcp_ss7_do_exec(struct mgcp_ss7 *mgcp, uint8_t type, uint32_t port, uint32_t param)
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800405{
406#ifndef NO_UNIPORTE
407 struct mgcp_ss7_endpoint *mgw_endp = &mgcp->mgw_end[port];
408 int rc;
409
410 switch (type) {
411 case MGCP_SS7_MUTE_STATUS:
412 if (mgw_endp->port != UINT_MAX)
413 update_mute_status(TO_MGW_PORT(port), param);
414 break;
415 case MGCP_SS7_DELETE:
416 if (mgw_endp->port != UINT_MAX) {
417 rc = MtnSaDisconnect(mgw_endp->port);
418 if (rc != 0)
419 fprintf(stderr, "Failed to disconnect port: %u\n", mgw_endp->port);
420 rc = MtnSaDeallocate(mgw_endp->port);
421 if (rc != 0)
422 fprintf(stderr, "Failed to deallocate port: %u\n", mgw_endp->port);
423 mgw_endp->port = UINT_MAX;
424 mgw_endp->block = 1;
425 }
426 break;
427 case MGCP_SS7_ALLOCATE:
428 allocate_endp(mgcp, port);
429 break;
430 case MGCP_SS7_SHUTDOWN:
431 MtnSaShutdown();
432 break;
433 }
434#endif
435}
436
Holger Hans Peter Freyther9ed3e1b2010-07-31 05:22:56 +0800437void mgcp_ss7_exec(struct mgcp_ss7 *mgcp, uint8_t type, uint32_t port, uint32_t param)
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800438{
439 struct mgcp_ss7_cmd *cmd = malloc(sizeof(*cmd));
440 memset(cmd, 0, sizeof(*cmd));
441 cmd->type = type;
442 cmd->port = port;
443 cmd->param = param;
444
445 thread_safe_add(mgcp->cmd_queue, &cmd->entry);
446}
447
448static int ss7_allocate_endpoint(struct mgcp_ss7 *ss7, int endp_no, struct mgcp_ss7_endpoint *endp)
449{
450 struct mgcp_endpoint *mg_endp;
451
452 mg_endp = &ss7->cfg->endpoints[endp_no];
453 mg_endp->bts_rtp = htons(mg_endp->rtp_port);
454 mg_endp->bts_rtcp = htons(mg_endp->rtp_port + 1);
455 mg_endp->bts = ss7->cfg->bts_in;
456
457 mgcp_ss7_exec(ss7, MGCP_SS7_ALLOCATE, endp_no, 0);
458 return MGCP_POLICY_CONT;
459}
460
461static int ss7_modify_endpoint(struct mgcp_ss7 *ss7, int endp_no, struct mgcp_ss7_endpoint *endp)
462{
463 struct mgcp_endpoint *mg_endp;
464
465 mg_endp = &ss7->cfg->endpoints[endp_no];
466 mgcp_ss7_exec(ss7, MGCP_SS7_MUTE_STATUS, endp_no, mg_endp->conn_mode);
467
468 /*
Holger Hans Peter Freythere3abeb12010-10-18 19:36:50 +0200469 * Just assume that we have the data now.
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800470 */
Holger Hans Peter Freythere3abeb12010-10-18 19:36:50 +0200471 mgcp_send_dummy(mg_endp);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800472
473 /* update the remote end */
474 return MGCP_POLICY_CONT;
475}
476
477static int ss7_delete_endpoint(struct mgcp_ss7 *ss7, int endp_no, struct mgcp_ss7_endpoint *endp)
478{
479 mgcp_ss7_endp_free(ss7, endp_no);
480 return MGCP_POLICY_CONT;
481}
482
483static int mgcp_ss7_policy(struct mgcp_config *cfg, int endp_no, int state, const char *trans)
484{
485 int rc;
486 struct mgcp_ss7 *ss7;
487 struct mgcp_ss7_endpoint *endp;
488
489 ss7 = (struct mgcp_ss7 *) cfg->data;
490 endp = &ss7->mgw_end[endp_no];
491
492 /* TODO: Make it async and wait for the port to be connected */
493 rc = MGCP_POLICY_REJECT;
494 switch (state) {
495 case MGCP_ENDP_CRCX:
496 rc = ss7_allocate_endpoint(ss7, endp_no, endp);
497 break;
498 case MGCP_ENDP_MDCX:
499 rc = ss7_modify_endpoint(ss7, endp_no, endp);
500 break;
501 case MGCP_ENDP_DLCX:
502 rc = ss7_delete_endpoint(ss7, endp_no, endp);
503 break;
504 }
505
506 return rc;
507}
508
509static void enqueue_msg(struct write_queue *queue, struct sockaddr_in *addr, struct msgb *msg)
510{
511 struct sockaddr_in *data;
512
513 data = (struct sockaddr_in *) msgb_push(msg, sizeof(*data));
514 *data = *addr;
515 if (write_queue_enqueue(queue, msg) != 0) {
516 LOGP(DMGCP, LOGL_ERROR, "Failed to queue the message.\n");
517 msgb_free(msg);
518 }
519}
520
521static int write_call_agent(struct bsc_fd *bfd, struct msgb *msg)
522{
523 int rc;
524 struct sockaddr_in *addr;
525
526 addr = (struct sockaddr_in *) msg->data;
527 rc = sendto(bfd->fd, msg->l2h, msgb_l2len(msg), 0,
528 (struct sockaddr *) addr, sizeof(*addr));
529
530 if (rc != msgb_l2len(msg))
531 LOGP(DMGCP, LOGL_ERROR, "Failed to write MGCP message: rc: %d errno: %d\n", rc, errno);
532
533 return rc;
534}
535
536
537static int read_call_agent(struct bsc_fd *fd)
538{
539 struct sockaddr_in addr;
540 socklen_t slen = sizeof(addr);
541 struct msgb *resp;
542 struct mgcp_ss7 *cfg;
543 struct write_queue *queue;
544
545 cfg = (struct mgcp_ss7 *) fd->data;
546 queue = container_of(fd, struct write_queue, bfd);
547
548 /* read one less so we can use it as a \0 */
549 int rc = recvfrom(fd->fd, cfg->mgcp_msg->data, cfg->mgcp_msg->data_len - 1, 0,
550 (struct sockaddr *) &addr, &slen);
551
552 if (rc < 0) {
553 perror("Gateway failed to read");
554 return -1;
555 } else if (slen > sizeof(addr)) {
556 fprintf(stderr, "Gateway received message from outerspace: %d %d\n",
557 slen, sizeof(addr));
558 return -1;
559 }
560
561 /* handle message now */
562 cfg->mgcp_msg->l2h = msgb_put(cfg->mgcp_msg, rc);
563 resp = mgcp_handle_message(cfg->cfg, cfg->mgcp_msg);
564 msgb_reset(cfg->mgcp_msg);
565
566 if (resp)
567 enqueue_msg(queue, &addr, resp);
568 return 0;
569}
570
571static int create_socket(struct mgcp_ss7 *cfg)
572{
573 int on;
574 struct sockaddr_in addr;
575 struct bsc_fd *bfd;
576
577 bfd = &cfg->mgcp_fd.bfd;
578
579 cfg->mgcp_fd.read_cb = read_call_agent;
580 cfg->mgcp_fd.write_cb = write_call_agent;
581 bfd->when = BSC_FD_READ;
582 bfd->fd = socket(AF_INET, SOCK_DGRAM, 0);
583 if (bfd->fd < 0) {
584 perror("Gateway failed to listen");
585 return -1;
586 }
587
588 on = 1;
589 setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
590
591 memset(&addr, 0, sizeof(addr));
592 addr.sin_family = AF_INET;
593 addr.sin_port = htons(cfg->cfg->source_port);
594 addr.sin_addr.s_addr = INADDR_ANY;
595
596 if (bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
597 perror("Gateway failed to bind");
598 close(bfd->fd);
599 return -1;
600 }
601
602 bfd->data = cfg;
603 cfg->mgcp_msg = msgb_alloc(4096, "mgcp-msg");
604 if (!cfg->mgcp_msg) {
605 fprintf(stderr, "Gateway memory error.\n");
606 close(bfd->fd);
607 return -1;
608 }
609 talloc_steal(cfg, cfg->mgcp_msg);
610
611
612 if (bsc_register_fd(bfd) != 0) {
613 DEBUGP(DMGCP, "Failed to register the fd\n");
614 close(bfd->fd);
615 return -1;
616 }
617
618 return 0;
619}
620
621static void mgcp_ss7_endp_free(struct mgcp_ss7 *ss7, int endp)
622{
623 mgcp_ss7_exec(ss7, MGCP_SS7_DELETE, endp, 0);
624}
625
626static int reset_cb(struct mgcp_config *cfg)
627{
628 mgcp_ss7_reset((struct mgcp_ss7 *) cfg->data);
629 return 0;
630}
631
Holger Hans Peter Freyther95cac742010-09-18 03:16:52 +0800632static int realloc_cb(struct mgcp_config *cfg, int endp)
633{
634 mgcp_ss7_endp_free((struct mgcp_ss7 *) cfg->data, endp);
635 return 0;
636}
637
Holger Hans Peter Freytherf9e99772010-08-04 07:10:55 +0800638static void mgcp_ss7_set_default(struct mgcp_config *cfg)
639{
640 /* do not attempt to allocate call ids */
641 cfg->early_bind = 1;
642
643 talloc_free(cfg->audio_name);
644 cfg->audio_payload = 126;
645 cfg->audio_name = talloc_strdup(cfg, "AMR/8000");
646}
647
648static struct mgcp_ss7 *mgcp_ss7_init(struct mgcp_config *cfg)
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800649{
650 int i;
651 struct mgcp_ss7 *conf = talloc_zero(NULL, struct mgcp_ss7);
652 if (!conf)
653 return NULL;
654
655 write_queue_init(&conf->mgcp_fd, 30);
Holger Hans Peter Freytherf9e99772010-08-04 07:10:55 +0800656 conf->cfg = cfg;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800657
658 /* take over the ownership */
659 talloc_steal(conf, conf->cfg);
Holger Hans Peter Freytherf9e99772010-08-04 07:10:55 +0800660
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800661 conf->cfg->policy_cb = mgcp_ss7_policy;
662 conf->cfg->reset_cb = reset_cb;
Holger Hans Peter Freyther95cac742010-09-18 03:16:52 +0800663 conf->cfg->realloc_cb = realloc_cb;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800664 conf->cfg->data = conf;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800665
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800666
667 if (mgcp_endpoints_allocate(conf->cfg) != 0) {
Holger Hans Peter Freytherf9e99772010-08-04 07:10:55 +0800668 LOGP(DMGCP, LOGL_ERROR, "Failed to allocate endpoints: %d\n",
Holger Hans Peter Freythere8073762010-08-04 07:34:21 +0800669 cfg->number_endpoints);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800670 talloc_free(conf);
671 return NULL;
672 }
673
674 if (create_socket(conf) != 0) {
675 LOGP(DMGCP, LOGL_ERROR, "Failed to create socket.\n");
676 talloc_free(conf);
677 return NULL;
678 }
679
680 conf->mgw_end = _talloc_zero_array(conf, sizeof(struct mgcp_ss7_endpoint),
681 conf->cfg->number_endpoints, "mgw endpoints");
682 if (!conf->mgw_end) {
683 LOGP(DMGCP, LOGL_ERROR, "Failed to allocate MGW endpoint array.\n");
684 talloc_free(conf);
685 return NULL;
686 }
687
688 for (i = 0; i < conf->cfg->number_endpoints; ++i) {
689 struct mgcp_endpoint *endp;
690 int rtp_port;
691
692 /* initialize the MGW part */
693 conf->mgw_end[i].port = UINT_MAX;
694
695 /* allocate the ports */
696 endp = &conf->cfg->endpoints[i];
697 rtp_port = rtp_calculate_port(ENDPOINT_NUMBER(endp), conf->cfg->rtp_base_port);
698 if (mgcp_bind_rtp_port(endp, rtp_port) != 0) {
699 LOGP(DMGCP, LOGL_ERROR, "Failed to bind: %d\n", rtp_port);
700 mgcp_ss7_free(conf);
701 return NULL;
702 }
703 }
704
705 conf->cmd_queue = thread_notifier_alloc();
706 if (!conf->cmd_queue) {
707 LOGP(DMGCP, LOGL_ERROR, "Failed to allocate the command queue.\n");
708 talloc_free(conf);
709 return NULL;
710 }
711
712#ifndef NO_UNIPORTE
713 conf->cmd_queue->no_write = 1;
714 pthread_create(&conf->thread, NULL, start_uniporte, conf);
715#endif
716
717 return conf;
718}
719
720void mgcp_ss7_free(struct mgcp_ss7 *mgcp)
721{
722 /* close everything */
723 mgcp_ss7_reset(mgcp);
724
725 mgcp_ss7_exec(mgcp, MGCP_SS7_SHUTDOWN, 0, 0);
726
727 close(mgcp->mgcp_fd.bfd.fd);
728 bsc_unregister_fd(&mgcp->mgcp_fd.bfd);
729 bsc_del_timer(&mgcp->poll_timer);
730 talloc_free(mgcp);
731}
732
733void mgcp_ss7_reset(struct mgcp_ss7 *mgcp)
734{
735 int i;
736
737 if (!mgcp)
738 return;
739
740 LOGP(DMGCP, LOGL_INFO, "Resetting all endpoints.\n");
741
742 /* free UniPorted and MGCP data */
743 for (i = 0; i < mgcp->cfg->number_endpoints; ++i) {
744 mgcp_ss7_endp_free(mgcp, i);
745 mgcp_free_endp(&mgcp->cfg->endpoints[i]);
746 }
747}
748
749static void print_help()
750{
751 printf(" Some useful help...\n");
752 printf(" -h This help text.\n");
753 printf(" -c --config=CFG. The configuration file.\n");
754 printf(" -e --exit-on-failure. Exit the app on MGW failure.\n");
755}
756
757static void print_usage()
758{
759 printf("Usage: mgcp_mgw\n");
760}
761
762
763static void handle_options(int argc, char **argv)
764{
765 while (1) {
766 int option_index = 0, c;
767 static struct option long_options[] = {
768 {"help", 0, 0, 'h'},
769 {"config", 1, 0, 'c'},
770 {"exit", 0, 0, 'e'},
771 {0, 0, 0, 0},
772 };
773
774 c = getopt_long(argc, argv, "hc:e",
775 long_options, &option_index);
776 if (c == -1)
777 break;
778
779 switch (c) {
780 case 'h':
781 print_usage();
782 print_help();
783 exit(0);
784 case 'c':
785 config_file = optarg;
786 break;
787 case 'e':
788 exit_on_failure = 1;
789 break;
790 default:
791 fprintf(stderr, "Unknown option.\n");
792 break;
793 }
794 }
795}
796
797
798int main(int argc, char **argv)
799{
800 struct mgcp_ss7 *mgcp;
Holger Hans Peter Freythercc1a9382010-08-04 06:24:04 +0800801 int rc;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800802
Holger Hans Peter Freythercbf7d182010-07-31 05:25:35 +0800803 log_init(&log_info);
804 stderr_target = log_target_create_stderr();
805 log_add_target(stderr_target);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800806
807 /* enable filters */
Holger Hans Peter Freythercbf7d182010-07-31 05:25:35 +0800808 log_set_all_filter(stderr_target, 1);
809 log_set_category_filter(stderr_target, DINP, 1, LOGL_INFO);
810 log_set_category_filter(stderr_target, DSCCP, 1, LOGL_INFO);
811 log_set_category_filter(stderr_target, DMSC, 1, LOGL_INFO);
812 log_set_category_filter(stderr_target, DMGCP, 1, LOGL_INFO);
813 log_set_print_timestamp(stderr_target, 1);
814 log_set_use_color(stderr_target, 0);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800815
816 handle_options(argc, argv);
817
818 signal(SIGPIPE, SIG_IGN);
819
820 mgcp_mgw_vty_init();
Holger Hans Peter Freytherf41617b2010-08-04 07:08:33 +0800821
Holger Hans Peter Freyther757f1742010-09-17 22:03:08 +0800822 g_cfg = mgcp_config_alloc();
823 if (!g_cfg) {
Holger Hans Peter Freytherf41617b2010-08-04 07:08:33 +0800824 LOGP(DMGCP, LOGL_ERROR, "Failed to allocate mgcp config.\n");
825 return -1;
826 }
827
Holger Hans Peter Freyther757f1742010-09-17 22:03:08 +0800828 mgcp_ss7_set_default(g_cfg);
829 mgcp_vty_set_config(g_cfg);
Holger Hans Peter Freythercbf7d182010-07-31 05:25:35 +0800830 if (vty_read_config_file(config_file, NULL) < 0) {
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800831 fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
832 return -1;
833 }
834
Holger Hans Peter Freythercc1a9382010-08-04 06:24:04 +0800835 rc = telnet_init(NULL, NULL, 4243);
836 if (rc < 0)
837 return rc;
838
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800839 printf("Creating MGCP MGW with endpoints: %d ip: %s mgw: %s rtp-base: %d payload: %d\n",
Holger Hans Peter Freyther757f1742010-09-17 22:03:08 +0800840 g_cfg->number_endpoints, g_cfg->local_ip, g_cfg->bts_ip,
841 g_cfg->rtp_base_port, g_cfg->audio_payload);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800842
Holger Hans Peter Freyther757f1742010-09-17 22:03:08 +0800843 mgcp = mgcp_ss7_init(g_cfg);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800844 if (!mgcp) {
845 fprintf(stderr, "Failed to create MGCP\n");
846 exit(-1);
847 }
848 while (1) {
849 bsc_select_main(0);
850 }
851 return 0;
852}
853
Holger Hans Peter Freythercbf7d182010-07-31 05:25:35 +0800854static struct vty_app_info vty_info = {
855 .name = "mgcp_ss7",
856 .version = "0.0.1",
857 .go_parent_cb = NULL,
858};
859
Holger Hans Peter Freytherd2c46d42010-08-04 06:18:32 +0800860void logging_vty_add_cmds(void);
Holger Hans Peter Freythercbf7d182010-07-31 05:25:35 +0800861
Holger Hans Peter Freytherd5918ff2010-08-06 16:03:27 +0000862DEFUN(cfg_mgcp_vad, cfg_mgcp_vad_cmd,
863 "vad (enabled|disabled)",
864 "Enable the Voice Activity Detection\n"
865 "Enable\n" "Disable\n")
866{
867 if (argv[0][0] == 'e')
868 s_vad_enabled = 1;
869 else
870 s_vad_enabled = 0;
871 return CMD_SUCCESS;
872}
873
Holger Hans Peter Freyther33cdb7e2010-09-17 22:03:47 +0800874DEFUN(cfg_mgcp_realloc, cfg_mgcp_realloc_cmd,
875 "force-realloc (0|1)",
876 "Force the reallocation of an endpoint\n"
877 "Disable\n" "Enable\n")
878{
879 g_cfg->force_realloc = atoi(argv[0]);
880 return CMD_SUCCESS;
881}
882
Holger Hans Peter Freyther8fa8e582010-12-01 23:14:57 +0100883DEFUN(cfg_mgcp_inp_dig_gain, cfg_mgcp_inp_dig_gain_cmd,
884 "input-digital-gain <0-62>",
885 "Static Digital Input Gain\n"
886 "Gain value")
887{
888 s_digital_inp_gain = atoi(argv[0]);
889 return CMD_SUCCESS;
890}
891
892DEFUN(cfg_mgcp_out_dig_gain, cfg_mgcp_out_dig_gain_cmd,
893 "outut-digital-gain <0-62>",
894 "Static Digital Output Gain\n"
895 "Gain value")
896{
897 s_digital_out_gain = atoi(argv[0]);
898 return CMD_SUCCESS;
899}
900
901DEFUN(cfg_mgcp_upstr_agc, cfg_mgcp_upstr_agc_cmd,
902 "upstream-automatic-gain (0|1)",
903 "Enable automatic gain control on upstream\n"
904 "Disable\n" "Enabled\n")
905{
906 s_upstr_agc_enbl = argv[0][0] == '1';
907 return CMD_SUCCESS;
908}
909
910DEFUN(cfg_mgc_upstr_adp, cfg_mgcp_upstr_adp_cmd,
Holger Hans Peter Freyther882082c2010-12-17 17:15:44 +0100911 "upstream-adaptiton-rate <1-128>",
Holger Hans Peter Freyther8fa8e582010-12-01 23:14:57 +0100912 "Set the adaption rate in (dB/sec) * 10\n"
913 "Range\n")
914{
915 s_upstr_adp_rate = atoi(argv[0]);
916 return CMD_SUCCESS;
917}
918
919DEFUN(cfg_mgcp_upstr_max_gain, cfg_mgcp_upstr_max_gain_cmd,
920 "upstream-max-applied-gain <0-49>",
921 "Maximum applied gain from -31db to 18db\n"
922 "Gain level\n")
923{
924 s_upstr_max_gain = atoi(argv[0]);
925 return CMD_SUCCESS;
926}
927
928DEFUN(cfg_mgcp_upstr_target, cfg_mgcp_upstr_target_cmd,
929 "upstream-target-level <6-37>",
930 "Set the desired level in db\n"
931 "Desired lievel\n")
932{
933 s_upstr_target_lvl = atoi(argv[0]);
934 return CMD_SUCCESS;
935}
936
937DEFUN(cfg_mgcp_dwnstr_agc, cfg_mgcp_dwnstr_agc_cmd,
938 "downstream-automatic-gain (0|1)",
939 "Enable automatic gain control on downstream\n"
940 "Disable\n" "Enabled\n")
941{
942 s_dwnstr_agc_enbl = argv[0][0] == '1';
943 return CMD_SUCCESS;
944}
945
946DEFUN(cfg_mgc_dwnstr_adp, cfg_mgcp_dwnstr_adp_cmd,
Holger Hans Peter Freyther882082c2010-12-17 17:15:44 +0100947 "downstream-adaptation-rate <1-128>",
Holger Hans Peter Freyther8fa8e582010-12-01 23:14:57 +0100948 "Set the adaption rate in (dB/sec) * 10\n"
949 "Range\n")
950{
951 s_dwnstr_adp_rate = atoi(argv[0]);
952 return CMD_SUCCESS;
953}
954
955DEFUN(cfg_mgcp_dwnstr_max_gain, cfg_mgcp_dwnstr_max_gain_cmd,
956 "downstream-max-applied-gain <0-49>",
957 "Maximum applied gain from -31db to 18db\n"
958 "Gain level\n")
959{
960 s_dwnstr_max_gain = atoi(argv[0]);
961 return CMD_SUCCESS;
962}
963
964DEFUN(cfg_mgcp_dwnstr_target, cfg_mgcp_dwnstr_target_cmd,
965 "downstream-target-level <6-37>",
966 "Set the desired level in db\n"
967 "Desired lievel\n")
968{
969 s_dwnstr_target_lvl = atoi(argv[0]);
970 return CMD_SUCCESS;
971}
972
Holger Hans Peter Freythered304632010-10-27 19:32:53 +0200973void mgcp_write_extra(struct vty *vty)
974{
975 vty_out(vty, " force-realloc %d%s", g_cfg->force_realloc, VTY_NEWLINE);
976 vty_out(vty, " vad %s%s", s_vad_enabled ? "enabled" : "disabled", VTY_NEWLINE);
Holger Hans Peter Freyther8fa8e582010-12-01 23:14:57 +0100977 vty_out(vty, " input-digital-gain %d%s", s_digital_inp_gain, VTY_NEWLINE);
978 vty_out(vty, " output-digital-gain %d%s", s_digital_out_gain, VTY_NEWLINE);
979 vty_out(vty, " upstream-automatic-gain %d%s", s_upstr_agc_enbl, VTY_NEWLINE);
Holger Hans Peter Freyther882082c2010-12-17 17:15:44 +0100980 vty_out(vty, " upstream-adaptation-rate %d%s", s_upstr_adp_rate, VTY_NEWLINE);
Holger Hans Peter Freyther8fa8e582010-12-01 23:14:57 +0100981 vty_out(vty, " upstream-max-applied-gain %d%s", s_upstr_max_gain, VTY_NEWLINE);
982 vty_out(vty, " upstream-target-level %d%s", s_upstr_target_lvl, VTY_NEWLINE);
983 vty_out(vty, " downstream-automatic-gain %d%s", s_dwnstr_agc_enbl, VTY_NEWLINE);
Holger Hans Peter Freyther882082c2010-12-17 17:15:44 +0100984 vty_out(vty, " downstream-adaptation-rate %d%s", s_dwnstr_adp_rate, VTY_NEWLINE);
Holger Hans Peter Freyther8fa8e582010-12-01 23:14:57 +0100985 vty_out(vty, " downstream-max-applied-gain %d%s", s_dwnstr_max_gain, VTY_NEWLINE);
986 vty_out(vty, " downstream-target-level %d%s", s_dwnstr_target_lvl, VTY_NEWLINE);
Holger Hans Peter Freythered304632010-10-27 19:32:53 +0200987}
988
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800989static void mgcp_mgw_vty_init(void)
990{
991 cmd_init(1);
Holger Hans Peter Freythercbf7d182010-07-31 05:25:35 +0800992 vty_init(&vty_info);
Holger Hans Peter Freytherd2c46d42010-08-04 06:18:32 +0800993 logging_vty_add_cmds();
Holger Hans Peter Freythere8073762010-08-04 07:34:21 +0800994 mgcp_vty_init();
Holger Hans Peter Freytherd5918ff2010-08-06 16:03:27 +0000995
996 install_element(MGCP_NODE, &cfg_mgcp_vad_cmd);
Holger Hans Peter Freyther33cdb7e2010-09-17 22:03:47 +0800997 install_element(MGCP_NODE, &cfg_mgcp_realloc_cmd);
Holger Hans Peter Freyther8fa8e582010-12-01 23:14:57 +0100998 install_element(MGCP_NODE, &cfg_mgcp_inp_dig_gain_cmd);
999 install_element(MGCP_NODE, &cfg_mgcp_out_dig_gain_cmd);
1000 install_element(MGCP_NODE, &cfg_mgcp_upstr_agc_cmd);
1001 install_element(MGCP_NODE, &cfg_mgcp_upstr_adp_cmd);
1002 install_element(MGCP_NODE, &cfg_mgcp_upstr_max_gain_cmd);
1003 install_element(MGCP_NODE, &cfg_mgcp_upstr_target_cmd);
1004 install_element(MGCP_NODE, &cfg_mgcp_dwnstr_agc_cmd);
1005 install_element(MGCP_NODE, &cfg_mgcp_dwnstr_adp_cmd);
1006 install_element(MGCP_NODE, &cfg_mgcp_dwnstr_max_gain_cmd);
1007 install_element(MGCP_NODE, &cfg_mgcp_dwnstr_target_cmd);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +08001008}
1009
Holger Hans Peter Freythercbf7d182010-07-31 05:25:35 +08001010
1011const char *openbsc_copyright = "";