diff options
Diffstat (limited to 'src/libmobile/call.c')
-rw-r--r-- | src/libmobile/call.c | 796 |
1 files changed, 796 insertions, 0 deletions
diff --git a/src/libmobile/call.c b/src/libmobile/call.c new file mode 100644 index 0000000..a3bce20 --- /dev/null +++ b/src/libmobile/call.c @@ -0,0 +1,796 @@ +/* interface between mobile network/phone implementation and MNCC + * + * (C) 2016 by Andreas Eversberg <jolly@eversberg.eu> + * All Rights Reserved + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <stdint.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/time.h> +#include "../libsample/sample.h" +#include "../common/debug.h" +#include "sender.h" +#include "call.h" +#include "../libtimer/timer.h" +#include "../libmncc/mncc.h" +#include "../libmncc/cause.h" + +#define DISC_TIMEOUT 30 + +//#define DEBUG_LEVEL + +#ifdef DEBUG_LEVEL +static double level_of(double *samples, int count) +{ + double level = 0; + int i; + + for (i = 0; i < count; i++) { + if (samples[i] > level) + level = samples[i]; + } + + return level; +} +#endif + +static int send_patterns; /* send patterns towards fixed network */ +static int release_on_disconnect; /* release towards mobile phone, if MNCC call disconnects, don't send disconnect tone */ + +/* stream patterns/announcements */ +int16_t *ringback_spl = NULL; +int ringback_size = 0; +int ringback_max = 0; +int16_t *hangup_spl = NULL; +int hangup_size = 0; +int hangup_max = 0; +int16_t *busy_spl = NULL; +int busy_size = 0; +int busy_max = 0; +int16_t *noanswer_spl = NULL; +int noanswer_size = 0; +int noanswer_max = 0; +int16_t *outoforder_spl = NULL; +int outoforder_size = 0; +int outoforder_max = 0; +int16_t *invalidnumber_spl = NULL; +int invalidnumber_size = 0; +int invalidnumber_max = 0; +int16_t *congestion_spl = NULL; +int congestion_size = 0; +int congestion_max = 0; +int16_t *recall_spl = NULL; +int recall_size = 0; +int recall_max = 0; + +enum audio_pattern { + PATTERN_NONE = 0, + PATTERN_TEST, + PATTERN_RINGBACK, + PATTERN_HANGUP, + PATTERN_BUSY, + PATTERN_NOANSWER, + PATTERN_OUTOFORDER, + PATTERN_INVALIDNUMBER, + PATTERN_CONGESTION, + PATTERN_RECALL, +}; + +static void get_pattern(const int16_t **spl, int *size, int *max, enum audio_pattern pattern) +{ + *spl = NULL; + *size = 0; + *max = 0; + + switch (pattern) { + case PATTERN_RINGBACK: +no_recall: + *spl = ringback_spl; + *size = ringback_size; + *max = ringback_max; + break; + case PATTERN_HANGUP: + if (!hangup_spl) + goto no_hangup; + *spl = hangup_spl; + *size = hangup_size; + *max = hangup_max; + break; + case PATTERN_BUSY: +no_hangup: +no_noanswer: + *spl = busy_spl; + *size = busy_size; + *max = busy_max; + break; + case PATTERN_NOANSWER: + if (!noanswer_spl) + goto no_noanswer; + *spl = noanswer_spl; + *size = noanswer_size; + *max = noanswer_max; + break; + case PATTERN_OUTOFORDER: + if (!outoforder_spl) + goto no_outoforder; + *spl = outoforder_spl; + *size = outoforder_size; + *max = outoforder_max; + break; + case PATTERN_INVALIDNUMBER: + if (!invalidnumber_spl) + goto no_invalidnumber; + *spl = invalidnumber_spl; + *size = invalidnumber_size; + *max = invalidnumber_max; + break; + case PATTERN_CONGESTION: +no_outoforder: +no_invalidnumber: + *spl = congestion_spl; + *size = congestion_size; + *max = congestion_max; + break; + case PATTERN_RECALL: + if (!recall_spl) + goto no_recall; + *spl = recall_spl; + *size = recall_size; + *max = recall_max; + break; + default: + ; + } +} + +static enum audio_pattern cause2pattern(int cause) +{ + int pattern; + + switch (cause) { + case CAUSE_NORMAL: + pattern = PATTERN_HANGUP; + break; + case CAUSE_BUSY: + pattern = PATTERN_BUSY; + break; + case CAUSE_NOANSWER: + pattern = PATTERN_NOANSWER; + break; + case CAUSE_OUTOFORDER: + pattern = PATTERN_OUTOFORDER; + break; + case CAUSE_INVALNUMBER: + pattern = PATTERN_INVALIDNUMBER; + break; + case CAUSE_NOCHANNEL: + pattern = PATTERN_CONGESTION; + break; + default: + pattern = PATTERN_HANGUP; + } + + return pattern; +} + +enum process_state { + PROCESS_IDLE = 0, /* IDLE */ + PROCESS_SETUP_RO, /* call from radio to MNCC */ + PROCESS_SETUP_RT, /* call from MNCC to radio */ + PROCESS_ALERTING_RO, /* call from radio to MNCC */ + PROCESS_ALERTING_RT, /* call from MNCC to radio */ + PROCESS_CONNECT, + PROCESS_DISCONNECT, +}; + +/* MNCC call instance */ +typedef struct process { + struct process *next; + int callref; + enum process_state state; + int audio_disconnected; /* if not associated with transceiver anymore */ + enum audio_pattern pattern; + int audio_pos; + uint8_t cause; + struct timer timer; +} process_t; + +static process_t *process_head = NULL; + +static void process_timeout(struct timer *timer); + +static process_t *create_process(int callref, enum process_state state) +{ + process_t *process; + + process = calloc(sizeof(*process), 1); + if (!process) { + PDEBUG(DCALL, DEBUG_ERROR, "No memory!\n"); + abort(); + } + timer_init(&process->timer, process_timeout, process); + process->next = process_head; + process_head = process; + + process->callref = callref; + process->state = state; + + return process; +} + +static void destroy_process(int callref) +{ + process_t *process = process_head; + process_t **process_p = &process_head; + + while (process) { + if (process->callref == callref) { + *process_p = process->next; + timer_exit(&process->timer); + free(process); + return; + } + process_p = &process->next; + process = process->next; + } + PDEBUG(DCALL, DEBUG_ERROR, "Process with callref 0x%x not found!\n", callref); +} + +static process_t *get_process(int callref) +{ + process_t *process = process_head; + + while (process) { + if (process->callref == callref) + return process; + process = process->next; + } + return NULL; +} + +static void new_state_process(int callref, enum process_state state) +{ + process_t *process = get_process(callref); + + if (!process) { + PDEBUG(DCALL, DEBUG_ERROR, "Process with callref 0x%x not found!\n", callref); + return; + } + process->state = state; +} + +static void set_pattern_process(int callref, enum audio_pattern pattern) +{ + process_t *process = get_process(callref); + + if (!process) { + PDEBUG(DCALL, DEBUG_ERROR, "Process with callref 0x%x not found!\n", callref); + return; + } + process->pattern = pattern; + process->audio_pos = 0; +} + +/* disconnect audio, now send audio directly from pattern/announcement, not from transceiver */ +static void disconnect_process(int callref, int cause) +{ + process_t *process = get_process(callref); + + if (!process) { + PDEBUG(DCALL, DEBUG_ERROR, "Process with callref 0x%x not found!\n", callref); + return; + } + process->pattern = cause2pattern(cause); + process->audio_disconnected = 1; + process->audio_pos = 0; + process->cause = cause; + timer_start(&process->timer, DISC_TIMEOUT); +} + +static void get_process_patterns(process_t *process, int16_t *samples, int length) +{ + const int16_t *spl; + int size, max, pos; + + get_pattern(&spl, &size, &max, process->pattern); + + /* stream sample */ + pos = process->audio_pos; + while(length--) { + if (pos >= size) + *samples++ = 0; + else + *samples++ = spl[pos] >> 1; + if (++pos == max) + pos = 0; + } + process->audio_pos = pos; +} + +static void process_timeout(struct timer *timer) +{ + process_t *process = (process_t *)timer->priv; + + { + /* announcement timeout */ + uint8_t buf[sizeof(struct gsm_mncc)]; + struct gsm_mncc *mncc = (struct gsm_mncc *)buf; + + if (process->state == PROCESS_DISCONNECT) { + PDEBUG(DCALL, DEBUG_INFO, "Call released toward mobile network (after timeout)\n"); + call_down_release(process->callref, process->cause); + } + + memset(buf, 0, sizeof(buf)); + mncc->msg_type = MNCC_REL_IND; + mncc->callref = process->callref; + mncc->fields |= MNCC_F_CAUSE; + mncc->cause.location = LOCATION_PRIVATE_LOCAL; + mncc->cause.value = process->cause; + + destroy_process(process->callref); + PDEBUG(DCALL, DEBUG_INFO, "Releasing MNCC call towards fixed network (after timeout)\n"); + mncc_up(buf, sizeof(struct gsm_mncc)); + } +} + +int call_init(int _send_patterns, int _release_on_disconnect) +{ + send_patterns = _send_patterns; + release_on_disconnect = _release_on_disconnect; + + return 0; +} + +/* Setup is received from transceiver. */ +static int _indicate_setup(int callref, const char *callerid, const char *dialing) +{ + uint8_t buf[sizeof(struct gsm_mncc)]; + struct gsm_mncc *mncc = (struct gsm_mncc *)buf; + int rc; + + memset(buf, 0, sizeof(buf)); + mncc->msg_type = MNCC_SETUP_IND; + mncc->callref = callref; + mncc->fields |= MNCC_F_CALLING; + if (callerid) { + strncpy(mncc->calling.number, callerid, sizeof(mncc->calling.number) - 1); + mncc->calling.type = 4; /* caller ID is of type 'subscriber' */ + } // otherwise unknown and no number + mncc->fields |= MNCC_F_CALLED; + strncpy(mncc->called.number, dialing, sizeof(mncc->called.number) - 1); + mncc->called.type = 0; /* dialing is of type 'unknown' */ + mncc->lchan_type = GSM_LCHAN_TCH_F; + mncc->fields |= MNCC_F_BEARER_CAP; + mncc->bearer_cap.speech_ver[0] = BCAP_ANALOG_8000HZ; + mncc->bearer_cap.speech_ver[1] = -1; + + PDEBUG(DCALL, DEBUG_INFO, "Indicate MNCC setup towards fixed network\n"); + rc = mncc_up(buf, sizeof(struct gsm_mncc)); + if (rc < 0) + destroy_process(callref); + return rc; +} +int call_up_setup(int callref, const char *callerid, const char *dialing) +{ + int rc; + + if (!callref) { + PDEBUG(DCALL, DEBUG_DEBUG, "Ignoring setup, because callref not set. (not for us)\n"); + return -CAUSE_INVALCALLREF; + } + + if (callref < 0x4000000) { + PDEBUG(DCALL, DEBUG_ERROR, "Invalid callref from mobile station, please fix!\n"); + abort(); + } + + PDEBUG(DCALL, DEBUG_INFO, "Incoming call from '%s' to '%s'\n", callerid ? : "unknown", dialing); + if (!strcmp(dialing, "010")) + PDEBUG(DCALL, DEBUG_INFO, " -> Call to Operator '%s'\n", dialing); + + + create_process(callref, PROCESS_SETUP_RO); + + rc = _indicate_setup(callref, callerid, dialing); + + return rc; +} + +/* Transceiver indicates alerting. */ +static void _indicate_alerting(int callref) +{ + uint8_t buf[sizeof(struct gsm_mncc)]; + struct gsm_mncc *mncc = (struct gsm_mncc *)buf; + int rc; + + memset(buf, 0, sizeof(buf)); + mncc->msg_type = MNCC_ALERT_IND; + mncc->callref = callref; + + PDEBUG(DCALL, DEBUG_INFO, "Indicate MNCC alerting towards fixed network\n"); + rc = mncc_up(buf, sizeof(struct gsm_mncc)); + if (rc < 0) + destroy_process(callref); +} +void call_up_alerting(int callref) +{ + if (!callref) { + PDEBUG(DCALL, DEBUG_DEBUG, "Ignoring alerting, because callref not set. (not for us)\n"); + return; + } + + PDEBUG(DCALL, DEBUG_INFO, "Call is alerting\n"); + + if (!send_patterns) + _indicate_alerting(callref); + set_pattern_process(callref, PATTERN_RINGBACK); + new_state_process(callref, PROCESS_ALERTING_RT); +} + +/* Transceiver indicates answer. */ +static void _indicate_answer(int callref, const char *connect_id) +{ + uint8_t buf[sizeof(struct gsm_mncc)]; + struct gsm_mncc *mncc = (struct gsm_mncc *)buf; + int rc; + + memset(buf, 0, sizeof(buf)); + mncc->msg_type = MNCC_SETUP_CNF; + mncc->callref = callref; + mncc->fields |= MNCC_F_CONNECTED; + /* copy connected number as subscriber number */ + strncpy(mncc->connected.number, connect_id, sizeof(mncc->connected.number)); + mncc->connected.type = 4; + mncc->connected.plan = 1; + mncc->connected.present = 0; + mncc->connected.screen = 3; + + PDEBUG(DCALL, DEBUG_INFO, "Indicate MNCC answer towards fixed network\n"); + rc = mncc_up(buf, sizeof(struct gsm_mncc)); + if (rc < 0) + destroy_process(callref); +} +void call_up_answer(int callref, const char *connect_id) +{ + if (!callref) { + PDEBUG(DCALL, DEBUG_DEBUG, "Ignoring answer, because callref not set. (not for us)\n"); + return; + } + + PDEBUG(DCALL, DEBUG_INFO, "Call has been answered by '%s'\n", connect_id); + + if (!send_patterns) + _indicate_answer(callref, connect_id); + set_pattern_process(callref, PATTERN_NONE); + new_state_process(callref, PROCESS_CONNECT); +} + +/* Transceiver indicates release. */ +static void _indicate_disconnect_release(int callref, int cause, int disc) +{ + uint8_t buf[sizeof(struct gsm_mncc)]; + struct gsm_mncc *mncc = (struct gsm_mncc *)buf; + int rc; + + memset(buf, 0, sizeof(buf)); + mncc->msg_type = (disc) ? MNCC_DISC_IND : MNCC_REL_IND; + mncc->callref = callref; + mncc->fields |= MNCC_F_CAUSE; + mncc->cause.location = LOCATION_PRIVATE_LOCAL; + mncc->cause.value = cause; + + PDEBUG(DCALL, DEBUG_INFO, "Indicate MNCC %s towards fixed network\n", (disc) ? "disconnect" : "release"); + rc = mncc_up(buf, sizeof(struct gsm_mncc)); + if (rc < 0) + destroy_process(callref); +} +void call_up_release(int callref, int cause) +{ + process_t *process; + + if (!callref) { + PDEBUG(DCALL, DEBUG_DEBUG, "Ignoring release, because callref not set. (not for us)\n"); + return; + } + + PDEBUG(DCALL, DEBUG_INFO, "Call has been released with cause=%d\n", cause); + + process = get_process(callref); + if (process) { + /* just keep MNCC connection if tones shall be sent. + * no tones while setting up / alerting the call. */ + if (send_patterns + && process->state != PROCESS_SETUP_RO + && process->state != PROCESS_ALERTING_RO) + disconnect_process(callref, cause); + else + /* if no tones shall be sent, release on disconnect + * or RO setup states */ + if (process->state == PROCESS_DISCONNECT + || process->state == PROCESS_SETUP_RO + || process->state == PROCESS_ALERTING_RO) { + destroy_process(callref); + _indicate_disconnect_release(callref, cause, 0); + /* if no tones shall be sent, disconnect on all other states */ + } else { + disconnect_process(callref, cause); + _indicate_disconnect_release(callref, cause, 1); + } + } else { + /* we don't know about the process, just send release to upper layer anyway */ + _indicate_disconnect_release(callref, cause, 0); + } +} + +/* turn recall tone on or off */ +void call_tone_recall(int callref, int on) +{ + set_pattern_process(callref, (on) ? PATTERN_RECALL : PATTERN_NONE); +} + +/* forward audio to MNCC or call instance */ +void call_up_audio(int callref, sample_t *samples, int count) +{ + if (count != 160) { + fprintf(stderr, "Samples must be 160, please fix!\n"); + abort(); + } + /* is MNCC us used, forward audio */ + uint8_t buf[sizeof(struct gsm_data_frame) + 160 * sizeof(int16_t)]; + struct gsm_data_frame *data = (struct gsm_data_frame *)buf; + process_t *process; + + if (!callref) + return; + + /* if we are disconnected, ignore audio */ + process = get_process(callref); + if (!process || process->pattern != PATTERN_NONE) + return; + + /* forward audio */ + data->msg_type = ANALOG_8000HZ; + data->callref = callref; +#ifdef DEBUG_LEVEL + double lev = level_of(samples, count); + printf(" mobil-level: %s%.4f\n", debug_db(lev), (20 * log10(lev))); +#endif + samples_to_int16((int16_t *)data->data, samples, count); + + mncc_up(buf, sizeof(buf)); + /* don't destroy process here in case of an error */ +} + +/* clock that is used to transmit patterns */ +void call_clock(void) +{ + process_t *process = process_head; + uint8_t buf[sizeof(struct gsm_data_frame) + 160 * sizeof(int16_t)]; + struct gsm_data_frame *data = (struct gsm_data_frame *)buf; + + while(process) { + if (process->pattern != PATTERN_NONE) { + data->msg_type = ANALOG_8000HZ; + data->callref = process->callref; + /* try to get patterns, else copy the samples we got */ + get_process_patterns(process, (int16_t *)data->data, 160); +#ifdef DEBUG_LEVEL + sample_t samples[160]; + int16_to_samples(samples, (int16_t *)data->data, 160); + double lev = level_of(samples, 160); + printf(" mobil-level: %s%.4f\n", debug_db(lev), (20 * log10(lev))); + samples_to_int16((int16_t *)data->data, samples, 160); +#endif + mncc_up(buf, sizeof(buf)); + /* don't destroy process here in case of an error */ + } + process = process->next; + } +} + +/* mncc messages received from fixed network */ +void mncc_down(uint8_t *buf, int length) +{ + struct gsm_mncc *mncc = (struct gsm_mncc *)buf; + char number[sizeof(mncc->called.number)]; + char caller_id[sizeof(mncc->calling.number)]; + enum number_type caller_type; + int callref; + int rc; + process_t *process; + + callref = mncc->callref; + process = get_process(callref); + if (!process) { + if (mncc->msg_type == MNCC_SETUP_REQ) + process = create_process(callref, PROCESS_SETUP_RT); + else { + if (mncc->msg_type != MNCC_REL_REQ) + PDEBUG(DCALL, DEBUG_ERROR, "No process!\n"); + return; + } + } + + if (mncc->msg_type == ANALOG_8000HZ) { + struct gsm_data_frame *data = (struct gsm_data_frame *)buf; + sample_t samples[160]; + + /* if we are disconnected, ignore audio */ + if (process->pattern != PATTERN_NONE) + return; + int16_to_samples(samples, (int16_t *)data->data, 160); +#ifdef DEBUG_LEVEL + double lev = level_of(samples, 160); + printf("festnetz-level: %s %.4f\n", debug_db(lev), (20 * log10(lev))); +#endif + call_down_audio(callref, samples, 160); + return; + } + + if (process->audio_disconnected) { + switch(mncc->msg_type) { + case MNCC_DISC_REQ: + PDEBUG(DCALL, DEBUG_INFO, "Received MNCC disconnect from fixed network with cause %d\n", mncc->cause.value); + + PDEBUG(DCALL, DEBUG_INFO, "Call disconnected, releasing!\n"); + + destroy_process(callref); + + PDEBUG(DCALL, DEBUG_INFO, "Indicate MNCC release towards fixed network\n"); + mncc->msg_type = MNCC_REL_IND; + rc = mncc_up(buf, sizeof(struct gsm_mncc)); + if (rc < 0) + destroy_process(callref); + break; + case MNCC_REL_REQ: + PDEBUG(DCALL, DEBUG_INFO, "Received MNCC release from fixed network with cause %d\n", mncc->cause.value); + + PDEBUG(DCALL, DEBUG_INFO, "Call released\n"); + + destroy_process(callref); + break; + } + return; + } + + switch(mncc->msg_type) { + case MNCC_SETUP_REQ: + strcpy(number, mncc->called.number); + + /* caller ID conversion */ + strcpy(caller_id, mncc->calling.number); + switch(mncc->calling.type) { + case 1: + caller_type = TYPE_INTERNATIONAL; + break; + case 2: + caller_type = TYPE_NATIONAL; + break; + case 4: + caller_type = TYPE_SUBSCRIBER; + break; + default: /* or 0 */ + caller_type = TYPE_UNKNOWN; + break; + } + if (!caller_id[0]) + caller_type = TYPE_NOTAVAIL; + if (mncc->calling.present == 1) + caller_type = TYPE_ANONYMOUS; + + PDEBUG(DCALL, DEBUG_INFO, "Received MNCC call from fixed network '%s' to mobile '%s'\n", caller_id, number); + + if (mncc->callref >= 0x4000000) { + fprintf(stderr, "Invalid callref from fixed network, please fix!\n"); + abort(); + } + + PDEBUG(DCALL, DEBUG_INFO, "Indicate MNCC call confirm towards fixed network\n"); + memset(buf, 0, length); + mncc->msg_type = MNCC_CALL_CONF_IND; + mncc->callref = callref; + mncc->lchan_type = GSM_LCHAN_TCH_F; + mncc->fields |= MNCC_F_BEARER_CAP; + mncc->bearer_cap.speech_ver[0] = BCAP_ANALOG_8000HZ; + mncc->bearer_cap.speech_ver[1] = -1; + + mncc_up(buf, sizeof(struct gsm_mncc)); + + PDEBUG(DCALL, DEBUG_INFO, "Outgoing call from '%s' to '%s'\n", caller_id, number); + + rc = call_down_setup(callref, caller_id, caller_type, number); + if (rc < 0) { + PDEBUG(DCALL, DEBUG_NOTICE, "Call rejected, cause %d\n", -rc); + if (send_patterns) { + PDEBUG(DCALL, DEBUG_DEBUG, "Early connecting after setup\n"); + _indicate_answer(callref, number); + } else { + PDEBUG(DCALL, DEBUG_INFO, "Disconnecting MNCC call towards fixed network (cause=%d)\n", -rc); + _indicate_disconnect_release(callref, -rc, 1); + } + disconnect_process(callref, -rc); + break; + } + + if (send_patterns) { + PDEBUG(DCALL, DEBUG_DEBUG, "Early connecting after setup\n"); + _indicate_answer(callref, number); + break; + } + break; + case MNCC_ALERT_REQ: + PDEBUG(DCALL, DEBUG_INFO, "Received MNCC alerting from fixed network\n"); + new_state_process(callref, PROCESS_ALERTING_RO); + break; + case MNCC_SETUP_RSP: + PDEBUG(DCALL, DEBUG_INFO, "Received MNCC answer from fixed network\n"); + new_state_process(callref, PROCESS_CONNECT); + PDEBUG(DCALL, DEBUG_INFO, "Call answered\n"); + call_down_answer(callref); + PDEBUG(DCALL, DEBUG_INFO, "Indicate MNCC setup complete towards fixed network\n"); + memset(buf, 0, length); + mncc->msg_type = MNCC_SETUP_COMPL_IND; + mncc->callref = callref; + rc = mncc_up(buf, sizeof(struct gsm_mncc)); + if (rc < 0) + destroy_process(callref); + break; + case MNCC_DISC_REQ: + PDEBUG(DCALL, DEBUG_INFO, "Received MNCC disconnect from fixed network with cause %d\n", mncc->cause.value); + + process = get_process(callref); + if (process && process->state == PROCESS_CONNECT && release_on_disconnect) { + PDEBUG(DCALL, DEBUG_INFO, "Releasing, because we don't send disconnect tones to mobile phone\n"); + + PDEBUG(DCALL, DEBUG_INFO, "Indicate MNCC release towards fixed network\n"); + mncc->msg_type = MNCC_REL_IND; + mncc_up(buf, sizeof(struct gsm_mncc)); + goto release; + } + new_state_process(callref, PROCESS_DISCONNECT); + PDEBUG(DCALL, DEBUG_INFO, "Call disconnected\n"); + call_down_disconnect(callref, mncc->cause.value); + timer_start(&process->timer, DISC_TIMEOUT); + break; + case MNCC_REL_REQ: + PDEBUG(DCALL, DEBUG_INFO, "Received MNCC release from fixed network with cause %d\n", mncc->cause.value); + +release: + destroy_process(callref); + PDEBUG(DCALL, DEBUG_INFO, "Call released toward mobile network\n"); + call_down_release(callref, mncc->cause.value); + break; + } +} + +int (*mncc_up)(uint8_t *buf, int length) = NULL; + +/* break down of MNCC socket */ +void mncc_flush(void) +{ + while(process_head) { + PDEBUG(DCALL, DEBUG_NOTICE, "MNCC socket closed, releasing call\n"); + call_down_release(process_head->callref, CAUSE_TEMPFAIL); + destroy_process(process_head->callref); + /* note: callref is released by sender's instance */ + } +} + |