2720 lines
78 KiB
C
2720 lines
78 KiB
C
#warning warum kommt kein ton, wenn ich bei watson ins timeout komme?:
|
|
/* layer3 handling
|
|
*
|
|
* (C) 2020 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 <unistd.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <mISDN/mbuffer.h>
|
|
#include "../libdebug/debug.h"
|
|
#include "../libg711/g711.h"
|
|
#include "isdn.h"
|
|
#include "dss1.h"
|
|
#include "bridge.h"
|
|
#include "ie.h"
|
|
#ifndef u_char
|
|
#define u_char unsigned char
|
|
#endif
|
|
#include <mISDN/mlayer3.h>
|
|
#include <mISDN/q931.h>
|
|
#include <mISDN/suppserv.h>
|
|
|
|
static struct osmo_cc_helper_audio_codecs codecs_offer_alaw_ulaw[] = {
|
|
{ "PCMA", 8000, 1, g711_transcode_flipped, g711_transcode_flipped },
|
|
{ "PCMU", 8000, 1, g711_transcode_alaw_flipped_to_ulaw, g711_transcode_ulaw_to_alaw_flipped },
|
|
{ NULL, 0, 0, NULL, NULL},
|
|
};
|
|
|
|
static struct osmo_cc_helper_audio_codecs codecs_offer_ulaw_alaw[] = {
|
|
{ "PCMU", 8000, 1, g711_transcode_flipped, g711_transcode_flipped },
|
|
{ "PCMA", 8000, 1, g711_transcode_ulaw_flipped_to_alaw, g711_transcode_alaw_to_ulaw_flipped },
|
|
{ NULL, 0, 0, NULL, NULL},
|
|
};
|
|
|
|
static struct osmo_cc_helper_audio_codecs codecs_offer_clearmode[] = {
|
|
{ "CLEARMODE", 8000, 1, NULL, NULL },
|
|
{ NULL, 0, 0, NULL, NULL},
|
|
};
|
|
|
|
static struct osmo_cc_helper_audio_codecs codecs_accept_alaw_ulaw_clearmode[] = {
|
|
{ "PCMA", 8000, 1, g711_transcode_flipped, g711_transcode_flipped },
|
|
{ "PCMU", 8000, 1, g711_transcode_alaw_flipped_to_ulaw, g711_transcode_ulaw_to_alaw_flipped },
|
|
{ "CLEARMODE", 8000, 1, NULL, NULL },
|
|
{ NULL, 0, 0, NULL, NULL},
|
|
};
|
|
|
|
static struct osmo_cc_helper_audio_codecs codecs_accept_ulaw_alaw_clearmode[] = {
|
|
{ "PCMA", 8000, 1, g711_transcode_flipped, g711_transcode_flipped },
|
|
{ "PCMU", 8000, 1, g711_transcode_alaw_flipped_to_ulaw, g711_transcode_ulaw_to_alaw_flipped },
|
|
{ "CLEARMODE", 8000, 1, NULL, NULL },
|
|
{ NULL, 0, 0, NULL, NULL},
|
|
};
|
|
|
|
static struct l3_msg *create_l3msg(void)
|
|
{
|
|
struct l3_msg *l3m;
|
|
|
|
l3m = alloc_l3_msg();
|
|
if (!l3m) {
|
|
PDEBUG(DDSS1, DEBUG_ERROR, "No MEM!\n");
|
|
abort();
|
|
}
|
|
return l3m;
|
|
}
|
|
|
|
static void free_l3msg(struct l3_msg *l3m)
|
|
{
|
|
free_l3_msg(l3m);
|
|
}
|
|
|
|
static const char *state_names[] = {
|
|
"IDLE",
|
|
"IN-SETUP",
|
|
"OUT-SETUP",
|
|
"IN-OVERLAP",
|
|
"OUT-OVERLAP",
|
|
"IN-PROCEEDING",
|
|
"OUT-PROCEEDING",
|
|
"IN-ALERTING",
|
|
"OUT-ALERTING",
|
|
"IN-CONNECTING",
|
|
"OUT-CONNECTING",
|
|
"CONNECT",
|
|
"IN-DISCONNECT",
|
|
"OUT-DISCONNECT",
|
|
"OUT-RELEASE",
|
|
"SUSPENDED",
|
|
};
|
|
|
|
static void new_state(call_t *call, enum isdn_state state)
|
|
{
|
|
if (call->state == state)
|
|
return;
|
|
PDEBUG(DDSS1, DEBUG_DEBUG, "Changing state %s -> %s\n", state_names[call->state], state_names[state]);
|
|
call->state = state;
|
|
}
|
|
|
|
static void release_and_destroy(call_t *call, uint8_t cc_isdn_cause, uint16_t cc_sip_cause, uint8_t isdn_cause)
|
|
{
|
|
osmo_cc_msg_t *msg;
|
|
struct l3_msg *l3m;
|
|
|
|
if (cc_isdn_cause || cc_sip_cause) {
|
|
/* create osmo-cc message */
|
|
if (call->state == ISDN_STATE_IDLE
|
|
|| call->state == ISDN_STATE_OUT_SETUP)
|
|
msg = osmo_cc_new_msg(OSMO_CC_MSG_REJ_IND);
|
|
else
|
|
msg = osmo_cc_new_msg(OSMO_CC_MSG_REL_IND);
|
|
|
|
/* cause */
|
|
osmo_cc_add_ie_cause(msg, call->isdn_ep->serving_location, cc_isdn_cause, cc_sip_cause, 0);
|
|
|
|
/* send message to osmo-cc */
|
|
osmo_cc_ll_msg(&call->isdn_ep->cc_ep, call->cc_callref, msg);
|
|
}
|
|
|
|
if (isdn_cause) {
|
|
PDEBUG(DDSS1, DEBUG_INFO, "REJECT REQUEST (pid = 0x%x callref = %d)\n", call->l3_pid, call->cc_callref);
|
|
|
|
/* creating release complete */
|
|
l3m = create_l3msg();
|
|
|
|
/* cause */
|
|
enc_ie_cause(l3m, call->isdn_ep->serving_location, isdn_cause);
|
|
|
|
/* send message to ISDN */
|
|
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_RELEASE_COMPLETE, call->l3_pid, l3m);
|
|
}
|
|
|
|
/* call terminated */
|
|
new_state(call, ISDN_STATE_IDLE);
|
|
call_destroy(call);
|
|
}
|
|
|
|
static void split_3pty(call_t *call)
|
|
{
|
|
call_t *other;
|
|
osmo_cc_msg_t *msg;
|
|
|
|
/* call must be a conference call */
|
|
if (!call->conference_3pty)
|
|
return;
|
|
|
|
/* search for other 3pty party on same terminal */
|
|
other = call->isdn_ep->call_list;
|
|
while (other) {
|
|
if (other != call
|
|
&& other->l3_ces == call->l3_ces
|
|
&& other->conference_3pty)
|
|
break;
|
|
other = other->next;
|
|
}
|
|
|
|
/* remove conference state */
|
|
if (other) {
|
|
other->conference_3pty = 0;
|
|
/* process local briding capability */
|
|
bridge_socket_client_update(other, 1);
|
|
/* create osmo-cc message */
|
|
msg = osmo_cc_new_msg(OSMO_CC_MSG_NOTIFY_IND);
|
|
/* notify the facility */
|
|
osmo_cc_add_ie_notify(msg, OSMO_CC_NOTIFY_CONFERENCE_DISCONNECTED);
|
|
/* send message to osmo-cc */
|
|
osmo_cc_ll_msg(&other->isdn_ep->cc_ep, call->cc_callref, msg);
|
|
}
|
|
}
|
|
|
|
/* Generate AOC-S facility IE from metering information */
|
|
#define AOCS_CURRENCY_AMOUNT_PER_UNIT 5
|
|
static void generate_aocs_ie(call_t *call, struct l3_msg *l3m)
|
|
{
|
|
uint8_t fac_ie[256];
|
|
struct asn1_parm fac;
|
|
|
|
if (call->isdn_ep->ntmode && call->isdn_ep->aocs && call->metering_info_received) {
|
|
memset(&fac, 0, sizeof(fac));
|
|
fac.Valid = 1;
|
|
fac.comp = CompInvoke;
|
|
fac.u.inv.invokeId = 2; /* doesn't matter since no response is expected */
|
|
fac.u.inv.operationValue = Fac_AOCSCurrency;
|
|
|
|
PDEBUG(DISDN, DEBUG_DEBUG, "Sending AOC-S information from metering data: connect_units=%d unit_period_decisecs=%d\n", call->metering_connect_units, call->metering_unit_period_decisecs);
|
|
|
|
if(call->metering_connect_units == 0) { // Free call
|
|
PDEBUG(DISDN, DEBUG_DEBUG, "AOC-S currencyInfoList: BasicComm FreeOfCharge\n");
|
|
fac.u.inv.o.AOCcuril.currencyInfoCount = 1;
|
|
fac.u.inv.o.AOCcuril.currencyInfo[0].chargedItem = 0x00; // basic comm
|
|
fac.u.inv.o.AOCcuril.currencyInfo[0].currencyType = 0x84; // free
|
|
}
|
|
else if(call->metering_unit_period_decisecs == 0) { // setup/connect FlatRate
|
|
/* note some payphones (e.g. BluePhone) only interpret 'basic comm' charged item, therefore we don't use 'call setup' charged item */
|
|
PDEBUG(DISDN, DEBUG_DEBUG, "AOC-S currencyInfoList: BasicComm FlatRate\n");
|
|
fac.u.inv.o.AOCcuril.currencyInfoCount = 1;
|
|
fac.u.inv.o.AOCcuril.currencyInfo[0].chargedItem = 0x00; // basic comm
|
|
fac.u.inv.o.AOCcuril.currencyInfo[0].currencyType = 0xA2; // FlatRate
|
|
strncpy(fac.u.inv.o.AOCcuril.currencyInfo[0].FlatRateCurrency.currency, "EUR", 10); // Currency value
|
|
fac.u.inv.o.AOCcuril.currencyInfo[0].FlatRateCurrency.currencyAmount = AOCS_CURRENCY_AMOUNT_PER_UNIT * call->metering_connect_units;
|
|
fac.u.inv.o.AOCcuril.currencyInfo[0].FlatRateCurrency.multiplier = 1; // 1/100 EUR
|
|
}
|
|
else if(call->metering_connect_units == 1) { // Normal call
|
|
PDEBUG(DISDN, DEBUG_DEBUG, "AOC-S currencyInfoList: BasicComm DurationCurrency\n");
|
|
fac.u.inv.o.AOCcuril.currencyInfoCount = 1;
|
|
fac.u.inv.o.AOCcuril.currencyInfo[0].chargedItem = 0x00; // basic comm
|
|
fac.u.inv.o.AOCcuril.currencyInfo[0].currencyType = 0xA1; // DurationCurrency
|
|
strncpy(fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.currency, "EUR", 10); // Currency value
|
|
fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.currencyAmount = AOCS_CURRENCY_AMOUNT_PER_UNIT;
|
|
fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.multiplier = 1; // 1/100 EUR
|
|
fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.typeOfCharging = 0x00; // Contin. Charging
|
|
fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.durLengthTimeUnit = call->metering_unit_period_decisecs * 10; // Unit duration
|
|
fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.durLengthTimeScale = 0x00; // 1/100 s
|
|
fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.granLengthTimeUnit = 1; // Granularity 1
|
|
fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.granLengthTimeScale = 2; // 1/1 s
|
|
}
|
|
else { // Special call with setup/connect FlatRate
|
|
/* note some payphones (e.g. BluePhone) only interpret 'basic comm' charged item */
|
|
PDEBUG(DISDN, DEBUG_DEBUG, "AOC-S currencyInfoList: BasicComm DurationCurrency / CallSetup FlatRate\n");
|
|
fac.u.inv.o.AOCcuril.currencyInfoCount = 2;
|
|
fac.u.inv.o.AOCcuril.currencyInfo[0].chargedItem = 0x00; // basic comm
|
|
fac.u.inv.o.AOCcuril.currencyInfo[0].currencyType = 0xA1; // DurationCurrency
|
|
strncpy(fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.currency, "EUR", 10); // Currency value
|
|
fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.currencyAmount = AOCS_CURRENCY_AMOUNT_PER_UNIT;
|
|
fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.multiplier = 1; // 1/100 EUR
|
|
fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.typeOfCharging = 0x00; // Contin. Charging
|
|
fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.durLengthTimeUnit = call->metering_unit_period_decisecs * 10; // Unit duration
|
|
fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.durLengthTimeScale = 0x00; // 1/100 s
|
|
fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.granLengthTimeUnit = 1; // Granularity 1
|
|
fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.granLengthTimeScale = 2; // 1/1 s
|
|
fac.u.inv.o.AOCcuril.currencyInfo[1].chargedItem = 0x02; // call setup
|
|
fac.u.inv.o.AOCcuril.currencyInfo[1].currencyType = 0xA2; // FlatRate
|
|
strncpy(fac.u.inv.o.AOCcuril.currencyInfo[1].FlatRateCurrency.currency, "EUR", 10); // Currency value
|
|
fac.u.inv.o.AOCcuril.currencyInfo[1].FlatRateCurrency.currencyAmount = AOCS_CURRENCY_AMOUNT_PER_UNIT * call->metering_connect_units;
|
|
fac.u.inv.o.AOCcuril.currencyInfo[1].FlatRateCurrency.multiplier = 1; // 1/100 EUR
|
|
}
|
|
|
|
encodeFac(fac_ie, &fac);
|
|
enc_ie_facility(l3m, fac_ie + 2, fac_ie[1]);
|
|
}
|
|
}
|
|
|
|
/* AOC-D timer callback */
|
|
static void aocd_timer_cb(void *data)
|
|
{
|
|
uint8_t fac_ie[256];
|
|
struct asn1_parm fac;
|
|
struct l3_msg *l3m;
|
|
call_t *call = data;
|
|
|
|
call->metering_total_units++;
|
|
|
|
PDEBUG(DISDN, DEBUG_DEBUG, "Sending next AOC-D unit information, total_units=%d\n", call->metering_total_units);
|
|
|
|
timer_start(&call->aocd_unit_timer, (double)call->metering_unit_period_decisecs / (double)10);
|
|
|
|
memset(&fac, 0, sizeof(fac));
|
|
fac.Valid = 1;
|
|
fac.comp = CompInvoke;
|
|
fac.u.inv.invokeId = 2; /* doesn't matter since no response is expected */
|
|
fac.u.inv.operationValue = Fac_AOCDChargingUnit;
|
|
fac.u.inv.o.AOCchu.recordedUnits = call->metering_total_units;
|
|
|
|
encodeFac(fac_ie, &fac);
|
|
|
|
// sending facility
|
|
l3m = create_l3msg();
|
|
enc_ie_facility(l3m, fac_ie + 2, fac_ie[1]);
|
|
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_FACILITY, call->l3_pid, l3m);
|
|
}
|
|
|
|
/*
|
|
* handles all indications from ISDN stack
|
|
*/
|
|
|
|
/* CC-SETUP INDICATION */
|
|
void setup_ind(call_t *call, uint32_t pid, struct l3_msg *l3m)
|
|
{
|
|
osmo_cc_msg_t *msg;
|
|
uint8_t type, plan, present, screen, reason;
|
|
int has_mode, has_multi, has_user, has_present, has_reason;
|
|
uint8_t coding, capability, mode, rate, multi, user;
|
|
int exclusive, channel;
|
|
int sending_complete;
|
|
char callerid[33];
|
|
char called[33];
|
|
char keypad[33];
|
|
char redir[33];
|
|
char display[128];
|
|
int rc;
|
|
struct osmo_cc_helper_audio_codecs *codecs;
|
|
int clearmode = 0;
|
|
|
|
PDEBUG(DDSS1, DEBUG_INFO, "SETUP INDICATION (pid = 0x%x)\n", pid);
|
|
|
|
/* assign pid */
|
|
PDEBUG(DDSS1, DEBUG_DEBUG, " -> new L3ID assigned (l3id = 0x%x)\n", pid);
|
|
call->l3_pid = pid;
|
|
call->l3_ces = pid >> 16;
|
|
|
|
msg = osmo_cc_new_msg(OSMO_CC_MSG_SETUP_IND);
|
|
|
|
/* newtwork + interface */
|
|
osmo_cc_add_ie_calling_network(msg, OSMO_CC_NETWORK_ISDN_NONE, "");
|
|
osmo_cc_add_ie_calling_interface(msg, call->isdn_ep->portname);
|
|
|
|
/* caller info */
|
|
rc = dec_ie_calling_pn(l3m, 0, &type, &plan, &has_present, &present, &screen, callerid, sizeof(callerid));
|
|
if (rc >= 0) {
|
|
if (!has_present)
|
|
present = screen = 0;
|
|
/* check MSN */
|
|
struct msn_list *m = call->isdn_ep->msn_list;
|
|
if (m) {
|
|
/* we have an MSN list */
|
|
while (m) {
|
|
if (!strcmp(callerid, m->msn))
|
|
break;
|
|
m = m->next;
|
|
}
|
|
/* not found, so we use first MSN */
|
|
if (!m) {
|
|
strncpy(callerid, call->isdn_ep->msn_list->msn, sizeof(callerid) - 1);
|
|
callerid[sizeof(callerid) - 1] = '\0';
|
|
}
|
|
}
|
|
osmo_cc_add_ie_calling(msg, type, plan, present, screen, callerid);
|
|
/* secondary caller info */
|
|
rc = dec_ie_calling_pn(l3m, 1, &type, &plan, &has_present, &present, &screen, callerid, sizeof(callerid));
|
|
if (rc >= 0) {
|
|
if (!has_present)
|
|
present = screen = 0;
|
|
osmo_cc_add_ie_calling(msg, type, plan, present, screen, callerid);
|
|
}
|
|
} else {
|
|
/* no caller ID, use MSN, if exists */
|
|
if (call->isdn_ep->msn_list) {
|
|
strncpy(callerid, call->isdn_ep->msn_list->msn, sizeof(callerid) - 1);
|
|
callerid[sizeof(callerid) - 1] = '\0';
|
|
type = OSMO_CC_TYPE_UNKNOWN;
|
|
plan = OSMO_CC_PLAN_TELEPHONY;
|
|
present = OSMO_CC_PRESENT_ALLOWED;
|
|
screen = OSMO_CC_SCREEN_NETWORK;
|
|
osmo_cc_add_ie_calling(msg, type, plan, present, screen, callerid);
|
|
}
|
|
}
|
|
|
|
/* dialing information */
|
|
rc = dec_ie_called_pn(l3m, &type, &plan, called, sizeof(called));
|
|
if (rc >= 0) {
|
|
osmo_cc_add_ie_called(msg, type, plan, called);
|
|
if (called[0])
|
|
call->any_dialing = 1;
|
|
}
|
|
|
|
/* keypad */
|
|
rc = dec_ie_keypad(l3m, keypad, sizeof(keypad));
|
|
if (rc >= 0)
|
|
osmo_cc_add_ie_keypad(msg, keypad);
|
|
|
|
/* redirecting number */
|
|
rc = dec_ie_redirecting(l3m, &type, &plan, &has_present, &present, &screen, &has_reason, &reason, redir, sizeof(redir));
|
|
if (rc >= 0) {
|
|
if (!has_present)
|
|
present = screen = 0;
|
|
if (!has_reason)
|
|
reason = 0;
|
|
osmo_cc_add_ie_redir(msg, type, plan, present, screen, reason, redir);
|
|
}
|
|
|
|
/* bearer capability */
|
|
rc = dec_ie_bearer(l3m, &coding, &capability, &has_mode, &mode, &rate, &has_multi, &multi, &has_user, &user);
|
|
if (rc >= 0) {
|
|
if (!has_mode)
|
|
mode = 0;
|
|
osmo_cc_add_ie_bearer(msg, coding, capability, mode);
|
|
/* use clear mode */
|
|
if (capability==OSMO_CC_CAPABILITY_DATA
|
|
|| capability==OSMO_CC_CAPABILITY_DATA_RESTRICTED
|
|
|| capability==OSMO_CC_CAPABILITY_VIDEO)
|
|
clearmode = 1;
|
|
}
|
|
|
|
/* select codec */
|
|
if (clearmode) {
|
|
codecs = codecs_offer_clearmode;
|
|
/* init jitter buffer */
|
|
call_create_jitter(call, 1);
|
|
} else {
|
|
if (call->isdn_ep->law == 'a')
|
|
codecs = codecs_offer_alaw_ulaw;
|
|
else
|
|
codecs = codecs_offer_ulaw_alaw;
|
|
/* init jitter buffer */
|
|
call_create_jitter(call, 0);
|
|
}
|
|
|
|
/* sdp offer */
|
|
call->cc_session = osmo_cc_helper_audio_offer(&call->isdn_ep->cc_ep.session_config, call, codecs, rtp_receive, msg, 1);
|
|
|
|
/* dialing complete */
|
|
dec_ie_complete(l3m, &sending_complete);
|
|
if (sending_complete) {
|
|
call->sending_complete = 1;
|
|
osmo_cc_add_ie_complete(msg);
|
|
}
|
|
|
|
/* display */
|
|
rc = dec_ie_display(l3m, display, sizeof(display));
|
|
if (rc >= 0)
|
|
osmo_cc_add_ie_display(msg, display);
|
|
|
|
/* hunt channel */
|
|
dec_ie_channel_id(l3m, call->isdn_ep->pri, &exclusive, &channel);
|
|
rc = channel = hunt_bchannel_in(call->isdn_ep, channel, exclusive);
|
|
if (rc < 0) {
|
|
no_channel:
|
|
osmo_cc_free_msg(msg);
|
|
/* send MT_RELEASE_COMPLETE to "REJECT" the channel */
|
|
PDEBUG(DDSS1, DEBUG_INFO, "RELEASE-COMPLETE REQUEST (pid = 0x%x)\n", pid);
|
|
l3m = create_l3msg();
|
|
enc_ie_cause(l3m, call->isdn_ep->serving_location, -rc);
|
|
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_RELEASE_COMPLETE, call->l3_pid, l3m);
|
|
new_state(call, ISDN_STATE_IDLE);
|
|
call_destroy(call);
|
|
return;
|
|
}
|
|
|
|
/* open channel */
|
|
rc = open_bchannel_in(call, channel, 1);
|
|
if (rc < 0)
|
|
goto no_channel;
|
|
|
|
/* process local briding capability */
|
|
bridge_socket_client_update(call, 1);
|
|
|
|
/* create endpoint */
|
|
osmo_cc_call_t *cc_call = osmo_cc_call_new(&call->isdn_ep->cc_ep);
|
|
call->cc_callref = cc_call->callref;
|
|
PDEBUG(DDSS1, DEBUG_DEBUG, " -> new callref assigned (callref = %d)\n", call->cc_callref);
|
|
|
|
/* reset AOC information */
|
|
call->metering_info_received = 0;
|
|
call->aocd_unit_timer_started = 0;
|
|
|
|
new_state(call, ISDN_STATE_IN_SETUP);
|
|
|
|
/* send message to osmo-cc */
|
|
osmo_cc_ll_msg(&call->isdn_ep->cc_ep, call->cc_callref, msg);
|
|
}
|
|
|
|
/* CC-SETUP-ACKNOWLEDGE INDICATION */
|
|
void setup_ack_ind(call_t *call, uint32_t cmd, uint32_t pid, struct l3_msg *l3m)
|
|
{
|
|
osmo_cc_msg_t *msg;
|
|
int exclusive, channel;
|
|
uint8_t coding, location, progress;
|
|
char display[128];
|
|
int rc;
|
|
|
|
PDEBUG(DDSS1, DEBUG_INFO, "SETUP-ACKNOWLEDGE INDICATION (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
|
|
/* create osmo-cc message */
|
|
msg = osmo_cc_new_msg(OSMO_CC_MSG_SETUP_ACK_IND);
|
|
|
|
/* progress indicator */
|
|
rc = dec_ie_progress(l3m, &coding, &location, &progress);
|
|
if (rc >= 0)
|
|
osmo_cc_add_ie_progress(msg, coding, location, progress);
|
|
else {
|
|
coding = OSMO_CC_CODING_ITU_T;
|
|
progress = 0;
|
|
}
|
|
|
|
/* display */
|
|
rc = dec_ie_display(l3m, display, sizeof(display));
|
|
if (rc >= 0)
|
|
osmo_cc_add_ie_display(msg, display);
|
|
|
|
/* complete channel negotiation */
|
|
rc = dec_ie_channel_id(l3m, call->isdn_ep->pri, &exclusive, &channel);
|
|
rc = open_bchannel_out(call, cmd, (rc < 0) ? -1 : channel, (rc < 0) ? -1 : exclusive);
|
|
if (rc < 0) {
|
|
PDEBUG(DDSS1, DEBUG_NOTICE, "Channel negotiation failed.\n");
|
|
osmo_cc_free_msg(msg);
|
|
release_and_destroy(call, OSMO_CC_ISDN_CAUSE_RESOURCE_UNAVAIL, 0, -rc);
|
|
return;
|
|
}
|
|
|
|
/* send SDP answer */
|
|
if (coding == OSMO_CC_CODING_ITU_T && (progress == 1 || progress == 8) && !call->codec_negotiated) {
|
|
call->codec_negotiated = 1;
|
|
if (call->sdp) {
|
|
/* send SDP */
|
|
osmo_cc_add_ie_sdp(msg, call->sdp);
|
|
/* process local briding capability */
|
|
bridge_socket_client_update(call, 1);
|
|
}
|
|
}
|
|
|
|
/* the audio path is throughconnected */
|
|
if (coding == OSMO_CC_CODING_ITU_T && (progress == 1 || progress == 8))
|
|
call->audio_path = 1;
|
|
|
|
new_state(call, ISDN_STATE_OUT_OVERLAP);
|
|
|
|
/* send message to osmo-cc */
|
|
osmo_cc_ll_msg(&call->isdn_ep->cc_ep, call->cc_callref, msg);
|
|
}
|
|
|
|
/* CC-PROCEEDING INDICATION */
|
|
void proc_ind(call_t *call, uint32_t cmd, uint32_t pid, struct l3_msg *l3m)
|
|
{
|
|
osmo_cc_msg_t *msg;
|
|
int exclusive, channel;
|
|
uint8_t coding, location, progress;
|
|
uint8_t notify, type, plan, present;
|
|
int has_present;
|
|
char redir[32];
|
|
char display[128];
|
|
int rc;
|
|
|
|
PDEBUG(DDSS1, DEBUG_INFO, "PREOCEEDING INDICATION (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
|
|
/* create osmo-cc message */
|
|
msg = osmo_cc_new_msg(OSMO_CC_MSG_PROC_IND);
|
|
|
|
/* progress indicator */
|
|
rc = dec_ie_progress(l3m, &coding, &location, &progress);
|
|
if (rc >= 0)
|
|
osmo_cc_add_ie_progress(msg, coding, location, progress);
|
|
else {
|
|
coding = OSMO_CC_CODING_ITU_T;
|
|
progress = 0;
|
|
}
|
|
|
|
/* display */
|
|
rc = dec_ie_display(l3m, display, sizeof(display));
|
|
if (rc >= 0)
|
|
osmo_cc_add_ie_display(msg, display);
|
|
|
|
/* notify */
|
|
rc = dec_ie_notify(l3m, ¬ify);
|
|
if (rc >= 0)
|
|
osmo_cc_add_ie_notify(msg, notify);
|
|
|
|
/* redirection info */
|
|
rc = dec_ie_redirection(l3m, &type, &plan, &has_present, &present, redir, sizeof(redir));
|
|
if (rc >= 0) {
|
|
if (!has_present)
|
|
present = 0;
|
|
osmo_cc_add_ie_redir(msg, type, plan, present, 0, 0, redir);
|
|
}
|
|
|
|
/* complete channel negotiation */
|
|
rc = dec_ie_channel_id(l3m, call->isdn_ep->pri, &exclusive, &channel);
|
|
rc = open_bchannel_out(call, cmd, (rc < 0) ? -1 : channel, (rc < 0) ? -1 : exclusive);
|
|
if (rc < 0) {
|
|
PDEBUG(DDSS1, DEBUG_NOTICE, "Channel negotiation failed.\n");
|
|
osmo_cc_free_msg(msg);
|
|
release_and_destroy(call, OSMO_CC_ISDN_CAUSE_RESOURCE_UNAVAIL, 0, -rc);
|
|
return;
|
|
}
|
|
|
|
/* send SDP answer */
|
|
if (coding == OSMO_CC_CODING_ITU_T && (progress == 1 || progress == 8) && !call->codec_negotiated) {
|
|
call->codec_negotiated = 1;
|
|
if (call->sdp) {
|
|
/* send SDP */
|
|
osmo_cc_add_ie_sdp(msg, call->sdp);
|
|
/* process local briding capability */
|
|
bridge_socket_client_update(call, 1);
|
|
}
|
|
}
|
|
|
|
/* the audio path is throughconnected */
|
|
if (coding == OSMO_CC_CODING_ITU_T && (progress == 1 || progress == 8))
|
|
call->audio_path = 1;
|
|
|
|
new_state(call, ISDN_STATE_OUT_PROCEEDING);
|
|
|
|
/* send message to osmo-cc */
|
|
osmo_cc_ll_msg(&call->isdn_ep->cc_ep, call->cc_callref, msg);
|
|
}
|
|
|
|
/* CC-ALERTING INDICATION */
|
|
void alert_ind(call_t *call, uint32_t cmd, uint32_t pid, struct l3_msg *l3m)
|
|
{
|
|
osmo_cc_msg_t *msg;
|
|
int exclusive, channel;
|
|
uint8_t coding, location, progress;
|
|
uint8_t notify, type, plan, present;
|
|
int has_present;
|
|
char redir[32];
|
|
char display[128];
|
|
int rc;
|
|
|
|
PDEBUG(DDSS1, DEBUG_INFO, "ALERTING INDICATION (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
|
|
/* create osmo-cc message */
|
|
msg = osmo_cc_new_msg(OSMO_CC_MSG_ALERT_IND);
|
|
|
|
/* progress indicator */
|
|
rc = dec_ie_progress(l3m, &coding, &location, &progress);
|
|
if (rc >= 0)
|
|
osmo_cc_add_ie_progress(msg, coding, location, progress);
|
|
else {
|
|
coding = OSMO_CC_CODING_ITU_T;
|
|
progress = 0;
|
|
}
|
|
|
|
/* display */
|
|
rc = dec_ie_display(l3m, display, sizeof(display));
|
|
if (rc >= 0)
|
|
osmo_cc_add_ie_display(msg, display);
|
|
|
|
/* notify */
|
|
rc = dec_ie_notify(l3m, ¬ify);
|
|
if (rc >= 0)
|
|
osmo_cc_add_ie_notify(msg, notify);
|
|
|
|
/* redir info */
|
|
rc = dec_ie_redirection(l3m, &type, &plan, &has_present, &present, redir, sizeof(redir));
|
|
if (rc >= 0) {
|
|
if (!has_present)
|
|
present = 0;
|
|
osmo_cc_add_ie_redir(msg, type, plan, present, 0, 0, redir);
|
|
}
|
|
|
|
/* complete channel negotiation */
|
|
rc = dec_ie_channel_id(l3m, call->isdn_ep->pri, &exclusive, &channel);
|
|
rc = open_bchannel_out(call, cmd, (rc < 0) ? -1 : channel, (rc < 0) ? -1 : exclusive);
|
|
if (rc < 0) {
|
|
PDEBUG(DDSS1, DEBUG_NOTICE, "Channel negotiation failed.\n");
|
|
osmo_cc_free_msg(msg);
|
|
release_and_destroy(call, OSMO_CC_ISDN_CAUSE_RESOURCE_UNAVAIL, 0, -rc);
|
|
return;
|
|
}
|
|
|
|
/* send SDP answer */
|
|
if (coding == OSMO_CC_CODING_ITU_T && (progress == 1 || progress == 8) && !call->codec_negotiated) {
|
|
call->codec_negotiated = 1;
|
|
if (call->sdp) {
|
|
/* send SDP */
|
|
osmo_cc_add_ie_sdp(msg, call->sdp);
|
|
/* process local briding capability */
|
|
bridge_socket_client_update(call, 1);
|
|
}
|
|
}
|
|
|
|
/* the audio path is throughconnected */
|
|
if (coding == OSMO_CC_CODING_ITU_T && (progress == 1 || progress == 8))
|
|
call->audio_path = 1;
|
|
|
|
new_state(call, ISDN_STATE_OUT_ALERTING);
|
|
|
|
/* send message to osmo-cc */
|
|
osmo_cc_ll_msg(&call->isdn_ep->cc_ep, call->cc_callref, msg);
|
|
}
|
|
|
|
/* CC-CONNECT INDICATION */
|
|
void setup_cnf(call_t *call, uint32_t cmd, uint32_t pid, struct l3_msg *l3m)
|
|
{
|
|
osmo_cc_msg_t *msg;
|
|
int exclusive, channel;
|
|
uint8_t coding, location, progress;
|
|
uint8_t type, plan, present, screen;
|
|
int has_present;
|
|
char connected[33];
|
|
char display[128];
|
|
int rc;
|
|
|
|
PDEBUG(DDSS1, DEBUG_INFO, "CONNECT INDICATION (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
|
|
/* create osmo-cc message */
|
|
msg = osmo_cc_new_msg(OSMO_CC_MSG_SETUP_CNF);
|
|
|
|
/* progress indicator */
|
|
rc = dec_ie_progress(l3m, &coding, &location, &progress);
|
|
if (rc >= 0)
|
|
osmo_cc_add_ie_progress(msg, coding, location, progress);
|
|
else {
|
|
coding = OSMO_CC_CODING_ITU_T;
|
|
progress = 0;
|
|
}
|
|
|
|
/* display */
|
|
rc = dec_ie_display(l3m, display, sizeof(display));
|
|
if (rc >= 0)
|
|
osmo_cc_add_ie_display(msg, display);
|
|
|
|
/* connected info */
|
|
rc = dec_ie_connected_pn(l3m, &type, &plan, &has_present, &present, &screen, connected, sizeof(connected));
|
|
if (rc >= 0) {
|
|
if (!has_present)
|
|
present = screen = 0;
|
|
osmo_cc_add_ie_calling(msg, type, plan, present, screen, connected);
|
|
/* secondary connected info */
|
|
rc = dec_ie_connected_pn(l3m, &type, &plan, &has_present, &present, &screen, connected, sizeof(connected));
|
|
if (rc >= 0) {
|
|
if (!has_present)
|
|
present = screen = 0;
|
|
osmo_cc_add_ie_calling(msg, type, plan, present, screen, connected);
|
|
}
|
|
}
|
|
|
|
/* if we have no channel (answer call with no channel) we use this flag to assign later */
|
|
rc = dec_ie_channel_id(l3m, call->isdn_ep->pri, &exclusive, &channel);
|
|
if (rc < 0) {
|
|
channel = -1;
|
|
exclusive = -1;
|
|
}
|
|
if (channel < 0)
|
|
call->setup_comp_req_channel_assignment = 1;
|
|
/* complete channel negotiation */
|
|
rc = open_bchannel_out(call, cmd, channel, exclusive); /* channel and exclusive may be -1 */
|
|
if (rc < 0) {
|
|
PDEBUG(DDSS1, DEBUG_NOTICE, "Channel negotiation failed.\n");
|
|
osmo_cc_free_msg(msg);
|
|
release_and_destroy(call, OSMO_CC_ISDN_CAUSE_RESOURCE_UNAVAIL, 0, -rc);
|
|
return;
|
|
}
|
|
|
|
/* send SDP answer */
|
|
if (!call->codec_negotiated) {
|
|
call->codec_negotiated = 1;
|
|
if (call->sdp) {
|
|
/* send SDP */
|
|
osmo_cc_add_ie_sdp(msg, call->sdp);
|
|
/* process local briding capability */
|
|
bridge_socket_client_update(call, 1);
|
|
}
|
|
}
|
|
|
|
/* the audio path is throughconnected */
|
|
call->audio_path = 1;
|
|
|
|
new_state(call, ISDN_STATE_OUT_CONNECTING);
|
|
|
|
/* send message to osmo-cc */
|
|
osmo_cc_ll_msg(&call->isdn_ep->cc_ep, call->cc_callref, msg);
|
|
}
|
|
|
|
/* CC-CONNECT-ACKNOWLEDGE INDICATION */
|
|
void setup_comp_ind(call_t *call, uint32_t pid, struct l3_msg *l3m)
|
|
{
|
|
osmo_cc_msg_t *msg;
|
|
char display[128];
|
|
int rc;
|
|
|
|
PDEBUG(DDSS1, DEBUG_INFO, "CONNECT-ACKNOWLEDGE INDICATION (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
|
|
/* only send in TE mode, because we automatically reply in NT mode */
|
|
if (call->isdn_ep->ntmode)
|
|
return;
|
|
|
|
/* create osmo-cc message */
|
|
msg = osmo_cc_new_msg(OSMO_CC_MSG_SETUP_COMP_IND);
|
|
|
|
/* display */
|
|
rc = dec_ie_display(l3m, display, sizeof(display));
|
|
if (rc >= 0)
|
|
osmo_cc_add_ie_display(msg, display);
|
|
|
|
new_state(call, ISDN_STATE_CONNECT);
|
|
|
|
/* send message to osmo-cc */
|
|
osmo_cc_ll_msg(&call->isdn_ep->cc_ep, call->cc_callref, msg);
|
|
}
|
|
|
|
/* CC-INFORMATION INDICATION */
|
|
void info_ind(call_t *call, uint32_t pid, struct l3_msg *l3m)
|
|
{
|
|
osmo_cc_msg_t *msg;
|
|
uint8_t type, plan;
|
|
int sending_complete;
|
|
char called[33];
|
|
char keypad[33];
|
|
char display[128];
|
|
int rc;
|
|
|
|
PDEBUG(DDSS1, DEBUG_INFO, "INFO INDICATION (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
|
|
/* create osmo-cc message */
|
|
msg = osmo_cc_new_msg(OSMO_CC_MSG_INFO_IND);
|
|
|
|
/* dialing information */
|
|
rc = dec_ie_called_pn(l3m, &type, &plan, called, sizeof(called));
|
|
if (rc >= 0) {
|
|
osmo_cc_add_ie_called(msg, type, plan, called);
|
|
if (called[0])
|
|
call->any_dialing = 1;
|
|
}
|
|
|
|
/* keypad */
|
|
rc = dec_ie_keypad(l3m, keypad, sizeof(keypad));
|
|
if (rc >= 0)
|
|
osmo_cc_add_ie_keypad(msg, keypad);
|
|
|
|
/* display */
|
|
rc = dec_ie_display(l3m, display, sizeof(display));
|
|
if (rc >= 0)
|
|
osmo_cc_add_ie_display(msg, display);
|
|
|
|
/* dialing complete */
|
|
dec_ie_complete(l3m, &sending_complete);
|
|
if (sending_complete) {
|
|
call->sending_complete = 1;
|
|
osmo_cc_add_ie_complete(msg);
|
|
}
|
|
|
|
/* reset overlap timeout */ // FIXME: is this still required?
|
|
new_state(call, call->state);
|
|
|
|
/* stop tone, if something has been dialled */
|
|
if (call->send_local_tones && call->any_dialing && call->state == ISDN_STATE_IN_OVERLAP) {
|
|
PDEBUG(DDSS1, DEBUG_DEBUG, "Stop sending locally generated dial tone. (if not already)\n");
|
|
bchannel_tone(call, 0);
|
|
}
|
|
|
|
/* send message to osmo-cc */
|
|
osmo_cc_ll_msg(&call->isdn_ep->cc_ep, call->cc_callref, msg);
|
|
}
|
|
|
|
/* CC-DISCONNECT INDICATION */
|
|
void disconnect_ind(call_t *call, uint32_t pid, struct l3_msg *l3m)
|
|
{
|
|
osmo_cc_msg_t *msg;
|
|
uint8_t location, cause;
|
|
char display[128];
|
|
int rc;
|
|
|
|
PDEBUG(DDSS1, DEBUG_INFO, "DISCONNECT INDICATION (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
|
|
/* create osmo-cc message */
|
|
msg = osmo_cc_new_msg(OSMO_CC_MSG_DISC_IND);
|
|
|
|
/* cause */
|
|
rc = dec_ie_cause(l3m, &location, &cause);
|
|
if (rc < 0) {
|
|
cause = 0;
|
|
location = call->isdn_ep->serving_location;
|
|
}
|
|
osmo_cc_add_ie_cause(msg, location, cause, 0, 0);
|
|
|
|
rc = dec_ie_display(l3m, display, sizeof(display));
|
|
if (rc >= 0)
|
|
osmo_cc_add_ie_display(msg, display);
|
|
|
|
/* note: disconnect does not assign channel ID */
|
|
|
|
/* note: disconnect does not have progress indicator */
|
|
|
|
new_state(call, ISDN_STATE_IN_DISCONNECT);
|
|
|
|
/* send message to osmo-cc */
|
|
osmo_cc_ll_msg(&call->isdn_ep->cc_ep, call->cc_callref, msg);
|
|
|
|
/* stop AOC-D timer */
|
|
if(call->aocd_unit_timer_started)
|
|
{
|
|
timer_stop(&call->aocd_unit_timer);
|
|
timer_exit(&call->aocd_unit_timer);
|
|
call->aocd_unit_timer_started = 0;
|
|
}
|
|
}
|
|
|
|
/* CC-DISCONNECT INDICATION of child instance */
|
|
void disconnect_ind_i(call_t *call, uint32_t pid, struct l3_msg *l3m)
|
|
{
|
|
uint8_t location, cause;
|
|
int rc;
|
|
|
|
PDEBUG(DDSS1, DEBUG_INFO, "DISCONNECT INDICATION of child (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
|
|
/* collect cause */
|
|
rc = dec_ie_cause(l3m, &location, &cause);
|
|
if (rc >= 0) {
|
|
call->collect_cause = osmo_cc_collect_cause(call->collect_cause, cause);
|
|
call->collect_location = location;
|
|
}
|
|
}
|
|
|
|
/* CC-RELEASE INDICATION */
|
|
void release_ind(call_t *call, uint32_t pid, struct l3_msg *l3m)
|
|
{
|
|
osmo_cc_msg_t *msg;
|
|
uint8_t location, cause;
|
|
char display[128];
|
|
int rc;
|
|
|
|
PDEBUG(DDSS1, DEBUG_INFO, "RELEASE INDICATION (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
|
|
/* create osmo-cc message */
|
|
msg = osmo_cc_new_msg(OSMO_CC_MSG_REL_IND);
|
|
|
|
/* cause */
|
|
rc = dec_ie_cause(l3m, &location, &cause);
|
|
if (rc < 0) {
|
|
cause = 0;
|
|
location = call->isdn_ep->serving_location;
|
|
}
|
|
osmo_cc_add_ie_cause(msg, location, cause, 0, 0);
|
|
|
|
rc = dec_ie_display(l3m, display, sizeof(display));
|
|
if (rc >= 0)
|
|
osmo_cc_add_ie_display(msg, display);
|
|
|
|
/* send message to osmo-cc */
|
|
osmo_cc_ll_msg(&call->isdn_ep->cc_ep, call->cc_callref, msg);
|
|
|
|
/* call terminated */
|
|
new_state(call, ISDN_STATE_IDLE);
|
|
split_3pty(call);
|
|
call_destroy(call);
|
|
}
|
|
|
|
/* CC-RELEASE-COMPLETE INDICATION (a reject) */
|
|
void release_complete_ind(call_t *call, uint32_t pid, struct l3_msg *l3m)
|
|
{
|
|
osmo_cc_msg_t *msg;
|
|
uint8_t location, cause;
|
|
int rc;
|
|
|
|
PDEBUG(DDSS1, DEBUG_INFO, "RELEASE-COMPLETE INDICATION (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
|
|
/* create osmo-cc message */
|
|
switch (call->state) {
|
|
case ISDN_STATE_OUT_SETUP:
|
|
msg = osmo_cc_new_msg(OSMO_CC_MSG_REJ_IND);
|
|
break;
|
|
case ISDN_STATE_OUT_RELEASE:
|
|
msg = osmo_cc_new_msg(OSMO_CC_MSG_REL_CNF);
|
|
break;
|
|
default:
|
|
msg = osmo_cc_new_msg(OSMO_CC_MSG_REL_IND);
|
|
}
|
|
|
|
/* cause */
|
|
/* in case layer 1 is down during setup, we send cause 27 */
|
|
if (call->state == ISDN_STATE_OUT_SETUP && call->isdn_ep->l1link == 0) {
|
|
cause = 27;
|
|
location = call->isdn_ep->serving_location;
|
|
} else {
|
|
rc = dec_ie_cause(l3m, &location, &cause);
|
|
if (rc < 0) {
|
|
cause = 0;
|
|
location = call->isdn_ep->serving_location;
|
|
}
|
|
}
|
|
osmo_cc_add_ie_cause(msg, location, cause, 0, 0);
|
|
|
|
/* send message to osmo-cc */
|
|
osmo_cc_ll_msg(&call->isdn_ep->cc_ep, call->cc_callref, msg);
|
|
|
|
/* call terminated */
|
|
new_state(call, ISDN_STATE_IDLE);
|
|
split_3pty(call);
|
|
call_destroy(call);
|
|
}
|
|
|
|
/* CC-RESTART INDICATION */
|
|
void restart_ind(uint32_t pid)
|
|
{
|
|
PDEBUG(DDSS1, DEBUG_INFO, "RESTART INDICATION (pid = 0x%x)\n", pid);
|
|
|
|
// L3 process is not touched. (not even by network stack)
|
|
}
|
|
|
|
/* T312 timeout */
|
|
void t312_timeout_ind(void)
|
|
{
|
|
// not required, release is performed with MT_FREE
|
|
}
|
|
|
|
/* CC-NOTIFY INDICATION */
|
|
void notify_ind(call_t *call, uint32_t pid, struct l3_msg *l3m)
|
|
{
|
|
osmo_cc_msg_t *msg;
|
|
uint8_t notify, type, plan, present;
|
|
int has_present;
|
|
char redir[33];
|
|
char display[128];
|
|
int rc;
|
|
|
|
PDEBUG(DDSS1, DEBUG_INFO, "NOTIFY INDICATION (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
|
|
/* create osmo-cc message */
|
|
msg = osmo_cc_new_msg(OSMO_CC_MSG_NOTIFY_IND);
|
|
|
|
/* notify */
|
|
rc = dec_ie_notify(l3m, ¬ify);
|
|
if (rc >= 0)
|
|
osmo_cc_add_ie_notify(msg, notify);
|
|
else {
|
|
osmo_cc_free_msg(msg);
|
|
return;
|
|
}
|
|
|
|
/* redirection number */
|
|
rc = dec_ie_redirection(l3m, &type, &plan, &has_present, &present, redir, sizeof(redir));
|
|
if (rc >= 0) {
|
|
if (!has_present)
|
|
present = 0;
|
|
osmo_cc_add_ie_redir(msg, type, plan, present, 0, 0, redir);
|
|
}
|
|
|
|
/* display */
|
|
rc = dec_ie_display(l3m, display, sizeof(display));
|
|
if (rc >= 0)
|
|
osmo_cc_add_ie_display(msg, display);
|
|
|
|
/* send message to osmo-cc */
|
|
osmo_cc_ll_msg(&call->isdn_ep->cc_ep, call->cc_callref, msg);
|
|
}
|
|
|
|
|
|
/* CC-HOLD INDICATION */
|
|
void hold_ind(call_t *call, uint32_t pid, struct l3_msg *l3m)
|
|
{
|
|
osmo_cc_msg_t *msg;
|
|
|
|
PDEBUG(DDSS1, DEBUG_INFO, "HOLD INDICATION (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
|
|
/* reject, if we are on hold */
|
|
if (call->hold) {
|
|
PDEBUG(DDSS1, DEBUG_INFO, "HOLD-REJECT REQUEST (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
l3m = create_l3msg();
|
|
enc_ie_cause(l3m, call->isdn_ep->serving_location, call->hold?101:31); /* normal unspecified / incompatible state */
|
|
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_HOLD_REJECT, call->l3_pid, l3m);
|
|
return;
|
|
}
|
|
|
|
/* process local briding capability */
|
|
bridge_socket_client_update(call, 0);
|
|
|
|
/* create osmo-cc message */
|
|
msg = osmo_cc_new_msg(OSMO_CC_MSG_NOTIFY_IND);
|
|
|
|
/* notify the hold of call */
|
|
osmo_cc_add_ie_notify(msg, OSMO_CC_NOTIFY_REMOTE_HOLD);
|
|
|
|
/* send message to osmo-cc */
|
|
osmo_cc_ll_msg(&call->isdn_ep->cc_ep, call->cc_callref, msg);
|
|
|
|
/* deactivate bchannel */
|
|
drop_bchannel(call);
|
|
|
|
/* set hold state */
|
|
call->hold = 1;
|
|
|
|
/* reset jitter buffer, to ignore packets coming from remote */
|
|
jitter_reset(&call->tx_dejitter);
|
|
|
|
/* generate hold tone, using local clock */
|
|
enable_hold_clock(call->isdn_ep);
|
|
|
|
/* acknowledge hold */
|
|
PDEBUG(DDSS1, DEBUG_INFO, "HOLD-ACKNOWLEDGE REQUEST (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
l3m = create_l3msg();
|
|
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_HOLD_ACKNOWLEDGE, call->l3_pid, l3m);
|
|
}
|
|
|
|
|
|
/* CC-RETRIEVE INDICATION */
|
|
void retrieve_ind(call_t *call, uint32_t pid, struct l3_msg *l3m)
|
|
{
|
|
osmo_cc_msg_t *msg;
|
|
int channel, exclusive;
|
|
int rc;
|
|
|
|
PDEBUG(DDSS1, DEBUG_INFO, "RETRIEVE INDICATION (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
|
|
if (!call->hold) {
|
|
rc = -101; /* incompatible state */
|
|
no_channel:
|
|
/* reject retrieve */
|
|
PDEBUG(DDSS1, DEBUG_INFO, "RETRIEVE-REJECT REQUEST (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
l3m = create_l3msg();
|
|
enc_ie_cause(l3m, call->isdn_ep->serving_location, rc);
|
|
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_RETRIEVE_REJECT, call->l3_pid, l3m);
|
|
return;
|
|
}
|
|
|
|
/* create osmo-cc message */
|
|
msg = osmo_cc_new_msg(OSMO_CC_MSG_NOTIFY_IND);
|
|
|
|
/* notify the retrieve of call */
|
|
osmo_cc_add_ie_notify(msg, OSMO_CC_NOTIFY_REMOTE_RETRIEVAL);
|
|
|
|
/* send message to osmo-cc */
|
|
osmo_cc_ll_msg(&call->isdn_ep->cc_ep, call->cc_callref, msg);
|
|
|
|
/* hunt channel */
|
|
dec_ie_channel_id(l3m, call->isdn_ep->pri, &exclusive, &channel);
|
|
rc = channel = hunt_bchannel_in(call->isdn_ep, channel, exclusive);
|
|
if (rc < 0)
|
|
goto no_channel;
|
|
|
|
/* open channel */
|
|
rc = open_bchannel_in(call, channel, 1);
|
|
if (rc < 0)
|
|
goto no_channel;
|
|
|
|
/* process local briding capability */
|
|
bridge_socket_client_update(call, 1);
|
|
|
|
/* set hold state */
|
|
call->hold = 0;
|
|
|
|
/* reset jitter buffer */
|
|
jitter_reset(&call->tx_dejitter);
|
|
|
|
/* acknowledge retrieve */
|
|
PDEBUG(DDSS1, DEBUG_INFO, "RETRIEVE-ACKNOWLEDGE REQUEST (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
l3m = create_l3msg();
|
|
enc_ie_channel_id(l3m, call->isdn_ep->pri, 1, call->b_channel);
|
|
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_RETRIEVE_ACKNOWLEDGE, call->l3_pid, l3m);
|
|
}
|
|
|
|
/* CC-SUSPEND INDICATION */
|
|
void suspend_ind(call_t *call, uint32_t pid, struct l3_msg *l3m)
|
|
{
|
|
osmo_cc_msg_t *msg;
|
|
uint8_t callid[8];
|
|
int len;
|
|
int rc = -31; /* normal, unspecified */
|
|
call_t *check;
|
|
|
|
PDEBUG(DDSS1, DEBUG_INFO, "SUSPEND INDICATION (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
|
|
if (0) {
|
|
reject:
|
|
PDEBUG(DDSS1, DEBUG_INFO, "SUSPEND-REJECT REQUEST (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
l3m = create_l3msg();
|
|
enc_ie_cause(l3m, call->isdn_ep->serving_location, -rc);
|
|
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_SUSPEND_REJECT, call->l3_pid, l3m);
|
|
return;
|
|
}
|
|
|
|
/* call id */
|
|
rc = dec_ie_call_id(l3m, callid, &len);
|
|
if (rc < 0)
|
|
len = 0;
|
|
|
|
/* check if call id is in use */
|
|
check = call->isdn_ep->call_list;
|
|
while (check) {
|
|
if (check->state == ISDN_STATE_SUSPENDED
|
|
&& check->park_len == len
|
|
&& !memcmp(check->park_callid, callid, len)) {
|
|
PDEBUG(DDSS1, DEBUG_INFO, "Given Park ID is already in use, rejecting!\n");
|
|
rc = -84; /* call id in use */
|
|
goto reject;
|
|
}
|
|
check = check->next;
|
|
}
|
|
|
|
/* process local briding capability */
|
|
bridge_socket_client_update(call, 0);
|
|
|
|
/* create osmo-cc message */
|
|
msg = osmo_cc_new_msg(OSMO_CC_MSG_NOTIFY_IND);
|
|
|
|
/* notify the suspension of call */
|
|
osmo_cc_add_ie_notify(msg, OSMO_CC_NOTIFY_USER_SUSPENDED);
|
|
|
|
new_state(call, ISDN_STATE_SUSPENDED);
|
|
memcpy(call->park_callid, callid, len);
|
|
call->park_len = len;
|
|
|
|
/* reset jitter buffer, to ignore packets coming from remote */
|
|
jitter_reset(&call->tx_dejitter);
|
|
|
|
/* send message to osmo-cc */
|
|
osmo_cc_ll_msg(&call->isdn_ep->cc_ep, call->cc_callref, msg);
|
|
|
|
/* deactivate bchannel */
|
|
drop_bchannel(call);
|
|
|
|
/* generate hold tone, using local clock */
|
|
enable_hold_clock(call->isdn_ep);
|
|
|
|
/* sending SUSPEND_ACKNOWLEDGE */
|
|
PDEBUG(DDSS1, DEBUG_INFO, "SUSPEND-ACKNOWLEDGE REQUEST (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
l3m = create_l3msg();
|
|
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_SUSPEND_ACKNOWLEDGE, call->l3_pid, l3m);
|
|
}
|
|
|
|
/* CC-RESUME INDICATION */
|
|
void resume_ind(isdn_t *isdn_ep, uint32_t pid, struct l3_msg *l3m)
|
|
{
|
|
call_t *call;
|
|
osmo_cc_msg_t *msg;
|
|
uint8_t callid[8];
|
|
int len;
|
|
int channel, exclusive;
|
|
int rc;
|
|
|
|
PDEBUG(DDSS1, DEBUG_INFO, "RESUME INDICATION (pid = 0x%x)\n", pid);
|
|
|
|
/* call id */
|
|
rc = dec_ie_call_id(l3m, callid, &len);
|
|
if (rc < 0)
|
|
len = 0;
|
|
|
|
/* check if call id is in use */
|
|
call = isdn_ep->call_list;
|
|
while (call) {
|
|
if (call->state == ISDN_STATE_SUSPENDED
|
|
&& call->park_len == len
|
|
&& !memcmp(call->park_callid, callid, len))
|
|
break;
|
|
call = call->next;
|
|
}
|
|
|
|
/* process given callref */
|
|
if (!call) {
|
|
PDEBUG(DDSS1, DEBUG_NOTICE, "No parked call found with given park ID, rejecting.\n");
|
|
rc = -85;
|
|
no_channel:
|
|
PDEBUG(DDSS1, DEBUG_INFO, "RESUME-REJECT REQUEST (pid = 0x%x)\n", pid);
|
|
l3m = create_l3msg();
|
|
enc_ie_cause(l3m, isdn_ep->serving_location, -rc);
|
|
isdn_ep->ml3->to_layer3(isdn_ep->ml3, MT_RESUME_REJECT, pid, l3m);
|
|
return;
|
|
}
|
|
|
|
/* assign pid */
|
|
PDEBUG(DDSS1, DEBUG_DEBUG, "new L3ID assigned (l3id = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
call->l3_pid = pid;
|
|
call->l3_ces = pid >> 16;
|
|
|
|
/* channel_id (no channel is possible in message) */
|
|
exclusive = 0;
|
|
channel = -1; /* any channel */
|
|
|
|
/* hunt channel */
|
|
rc = channel = hunt_bchannel_in(call->isdn_ep, channel, exclusive);
|
|
if (rc < 0)
|
|
goto no_channel;
|
|
|
|
/* open channel */
|
|
rc = open_bchannel_in(call, channel, 1);
|
|
if (rc < 0)
|
|
goto no_channel;
|
|
|
|
/* process local briding capability */
|
|
bridge_socket_client_update(call, 1);
|
|
|
|
/* create osmo-cc message */
|
|
msg = osmo_cc_new_msg(OSMO_CC_MSG_NOTIFY_IND);
|
|
|
|
/* notify the resume of call */
|
|
osmo_cc_add_ie_notify(msg, OSMO_CC_NOTIFY_USER_RESUMED);
|
|
|
|
new_state(call, ISDN_STATE_CONNECT);
|
|
|
|
/* reset jitter buffer */
|
|
jitter_reset(&call->tx_dejitter);
|
|
|
|
/* send message to osmo-cc */
|
|
osmo_cc_ll_msg(&call->isdn_ep->cc_ep, call->cc_callref, msg);
|
|
|
|
/* sending RESUME_ACKNOWLEDGE */
|
|
PDEBUG(DDSS1, DEBUG_INFO, "RESUME-ACKNOWLEDGE REQUEST (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
l3m = create_l3msg();
|
|
enc_ie_channel_id(l3m, call->isdn_ep->pri, 1, call->b_channel);
|
|
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_RESUME_ACKNOWLEDGE, call->l3_pid, l3m);
|
|
}
|
|
|
|
/* CC-FACILITY INDICATION */
|
|
void facility_ind(call_t *call, uint32_t pid, struct l3_msg *l3m)
|
|
{
|
|
osmo_cc_msg_t *msg;
|
|
uint8_t fac_ie[256];
|
|
struct asn1_parm fac;
|
|
int fac_len;
|
|
uint8_t invokeid = 0;
|
|
int set_3pty = -1;
|
|
uint8_t notify = 0;
|
|
call_t *other;
|
|
int rc;
|
|
|
|
PDEBUG(DDSS1, DEBUG_INFO, "FACILITY INDICATION (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
|
|
/* facility */
|
|
rc = dec_ie_facility(l3m, fac_ie + 1, &fac_len);
|
|
if (rc < 0)
|
|
return;
|
|
fac_ie[0] = fac_len;
|
|
if (fac_len <= 0)
|
|
return;
|
|
|
|
decodeFac(fac_ie, &fac);
|
|
switch (fac.comp) {
|
|
case CompInvoke:
|
|
switch(fac.u.inv.operationValue) {
|
|
case Fac_Begin3PTY:
|
|
notify = OSMO_CC_NOTIFY_CONFERENCE_ESTABLISHED;
|
|
invokeid = fac.u.inv.invokeId;
|
|
set_3pty = 1;
|
|
break;
|
|
|
|
case Fac_End3PTY:
|
|
notify = OSMO_CC_NOTIFY_CONFERENCE_DISCONNECTED;
|
|
invokeid = fac.u.inv.invokeId;
|
|
set_3pty = 0;
|
|
break;
|
|
default:
|
|
PDEBUG(DDSS1, DEBUG_DEBUG, "Facility Operation=%x of Component=%x not implemented\n", fac.u.inv.operationValue, fac.comp);
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
PDEBUG(DDSS1, DEBUG_DEBUG, "Facility Operation=%x of Component=%x not implemented\n", fac.u.inv.operationValue, fac.comp);
|
|
break;
|
|
}
|
|
|
|
if(set_3pty >= 0) {
|
|
/* find other terminal on hold */
|
|
other = call->isdn_ep->call_list;
|
|
while (other) {
|
|
// printf("check: call=%p other=%p call_ces=%x other_ces=%x other_hold=%d\n", call, other, call->l3_ces, other->l3_ces, other->hold);
|
|
if (other != call
|
|
&& other->l3_ces == call->l3_ces) {
|
|
/* if we got facility on active call */
|
|
if (other->hold && !call->hold)
|
|
break;
|
|
/* if we got facility on active call */
|
|
if (!other->hold && call->hold)
|
|
break;
|
|
}
|
|
other = other->next;
|
|
}
|
|
if (other) {
|
|
other->conference_3pty = call->conference_3pty = set_3pty;
|
|
/* process local briding capability */
|
|
bridge_socket_client_update(call, 0);
|
|
bridge_socket_client_update(other, 0);
|
|
jitter_reset(&call->conf_dejitter);
|
|
jitter_reset(&call->tx_dejitter);
|
|
jitter_reset(&other->conf_dejitter);
|
|
jitter_reset(&other->tx_dejitter);
|
|
} else {
|
|
PDEBUG(DDSS1, DEBUG_NOTICE, "Phone requests conference, but no call on hold!\n");
|
|
notify = 0;
|
|
}
|
|
|
|
/* encode 3PTY facility */
|
|
memset(&fac, 0, sizeof(fac));
|
|
fac.Valid = 1;
|
|
if (notify) {
|
|
fac.comp = CompReturnResult;
|
|
fac.u.retResult.invokeId = invokeid;
|
|
fac.u.retResult.operationValuePresent = 1;
|
|
if (notify == OSMO_CC_NOTIFY_CONFERENCE_ESTABLISHED)
|
|
fac.u.retResult.operationValue = Fac_Begin3PTY;
|
|
if (notify == OSMO_CC_NOTIFY_CONFERENCE_DISCONNECTED)
|
|
fac.u.retResult.operationValue = Fac_End3PTY;
|
|
} else {
|
|
fac.comp = CompReturnError;
|
|
fac.u.retError.invokeId = invokeid;
|
|
fac.u.retError.errorValue = FacError_Gen_InvalidCallState;
|
|
}
|
|
encodeFac(fac_ie, &fac);
|
|
|
|
/* sending facility */
|
|
l3m = create_l3msg();
|
|
enc_ie_facility(l3m, fac_ie + 2, fac_ie[1]);
|
|
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_FACILITY, call->l3_pid, l3m);
|
|
|
|
if (notify) {
|
|
/* create osmo-cc message */
|
|
msg = osmo_cc_new_msg(OSMO_CC_MSG_NOTIFY_IND);
|
|
/* notify the facility */
|
|
osmo_cc_add_ie_notify(msg, notify);
|
|
/* send message to osmo-cc */
|
|
osmo_cc_ll_msg(&call->isdn_ep->cc_ep, call->cc_callref, msg);
|
|
|
|
/* create osmo-cc message */
|
|
msg = osmo_cc_new_msg(OSMO_CC_MSG_NOTIFY_IND);
|
|
/* notify the facility */
|
|
osmo_cc_add_ie_notify(msg, notify);
|
|
/* send message to osmo-cc */
|
|
osmo_cc_ll_msg(&other->isdn_ep->cc_ep, call->cc_callref, msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* CC-PROGRESS INDICATION */
|
|
void progress_ind(call_t *call, uint32_t pid, struct l3_msg *l3m)
|
|
{
|
|
osmo_cc_msg_t *msg;
|
|
uint8_t coding, location, progress;
|
|
int rc;
|
|
|
|
PDEBUG(DDSS1, DEBUG_INFO, "PROGRESS INDICATION (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
|
|
/* create osmo-cc message */
|
|
msg = osmo_cc_new_msg(OSMO_CC_MSG_PROGRESS_IND);
|
|
|
|
/* progress indicator */
|
|
rc = dec_ie_progress(l3m, &coding, &location, &progress);
|
|
if (rc >= 0)
|
|
osmo_cc_add_ie_progress(msg, coding, location, progress);
|
|
else {
|
|
coding = OSMO_CC_CODING_ITU_T;
|
|
progress = 0;
|
|
}
|
|
|
|
/* send SDP answer */
|
|
if (coding == OSMO_CC_CODING_ITU_T && (progress == 1 || progress == 8) && !call->codec_negotiated) {
|
|
call->codec_negotiated = 1;
|
|
if (call->sdp) {
|
|
/* send SDP */
|
|
osmo_cc_add_ie_sdp(msg, call->sdp);
|
|
/* process local briding capability */
|
|
bridge_socket_client_update(call, 1);
|
|
}
|
|
}
|
|
|
|
/* the audio path is throughconnected */
|
|
if (coding == OSMO_CC_CODING_ITU_T && (progress == 1 || progress == 8))
|
|
call->audio_path = 1;
|
|
|
|
/* send message to osmo-cc */
|
|
osmo_cc_ll_msg(&call->isdn_ep->cc_ep, call->cc_callref, msg);
|
|
}
|
|
|
|
/* all calls from multipoint interface have released */
|
|
void mt_free(call_t *call)
|
|
{
|
|
osmo_cc_msg_t *msg;
|
|
|
|
PDEBUG(DDSS1, DEBUG_DEBUG, "Got MT_FREE (release from stack) (old pid 0x%x)\n", call->l3_pid);
|
|
|
|
/* create osmo-cc message */
|
|
switch (call->state) {
|
|
case ISDN_STATE_OUT_SETUP:
|
|
msg = osmo_cc_new_msg(OSMO_CC_MSG_REJ_IND);
|
|
break;
|
|
case ISDN_STATE_SUSPENDED:
|
|
return;
|
|
default:
|
|
msg = osmo_cc_new_msg(OSMO_CC_MSG_REL_IND);
|
|
}
|
|
|
|
/* cause */
|
|
osmo_cc_add_ie_cause(msg, call->collect_location, call->collect_cause, 0, 0);
|
|
|
|
/* send message to osmo-cc */
|
|
osmo_cc_ll_msg(&call->isdn_ep->cc_ep, call->cc_callref, msg);
|
|
|
|
/* terminate call */
|
|
new_state(call, ISDN_STATE_IDLE);
|
|
call_destroy(call);
|
|
}
|
|
|
|
/* take DSS1 message type and call sub routines (above) to handle each message */
|
|
void dss1_message(isdn_t *isdn_ep, call_t *call, uint32_t cmd, uint32_t pid, struct l3_msg *l3m)
|
|
{
|
|
int timer = 0;
|
|
|
|
switch (cmd) {
|
|
case MT_TIMEOUT:
|
|
if (!l3m->cause) {
|
|
PDEBUG(DDSS1, DEBUG_ERROR, "timeout without cause.\n");
|
|
break;
|
|
}
|
|
if (l3m->cause[0] != 5) {
|
|
PDEBUG(DDSS1, DEBUG_ERROR, "expecting timeout with timer diagnostic. (got len=%d)\n", l3m->cause[0]);
|
|
break;
|
|
}
|
|
timer = (l3m->cause[3]-'0')*100;
|
|
timer += (l3m->cause[4]-'0')*10;
|
|
timer += (l3m->cause[5]-'0');
|
|
PDEBUG(DDSS1, DEBUG_DEBUG, "isdn timer T%d timeout\n", timer);
|
|
if (timer == 312)
|
|
t312_timeout_ind();
|
|
break;
|
|
|
|
case MT_SETUP:
|
|
if (call->state != ISDN_STATE_IDLE)
|
|
break;
|
|
setup_ind(call, pid, l3m);
|
|
break;
|
|
|
|
case MT_SETUP_ACKNOWLEDGE:
|
|
setup_ack_ind(call, cmd, pid, l3m);
|
|
break;
|
|
|
|
case MT_CALL_PROCEEDING:
|
|
proc_ind(call, cmd, pid, l3m);
|
|
break;
|
|
|
|
case MT_ALERTING:
|
|
alert_ind(call, cmd, pid, l3m);
|
|
break;
|
|
|
|
case MT_CONNECT:
|
|
setup_cnf(call, cmd, pid, l3m);
|
|
break;
|
|
|
|
case MT_CONNECT_ACKNOWLEDGE:
|
|
setup_comp_ind(call, pid, l3m);
|
|
break;
|
|
|
|
case MT_INFORMATION:
|
|
info_ind(call, pid, l3m);
|
|
break;
|
|
|
|
case MT_DISCONNECT:
|
|
disconnect_ind(call, pid, l3m);
|
|
break;
|
|
|
|
case MT_RELEASE:
|
|
release_ind(call, pid, l3m);
|
|
break;
|
|
|
|
case MT_RELEASE_COMPLETE:
|
|
release_complete_ind(call, pid, l3m);
|
|
break;
|
|
|
|
case MT_RESTART:
|
|
restart_ind(pid);
|
|
break;
|
|
|
|
case MT_NOTIFY:
|
|
notify_ind(call, pid, l3m);
|
|
break;
|
|
|
|
case MT_HOLD:
|
|
hold_ind(call, pid, l3m);
|
|
break;
|
|
|
|
case MT_RETRIEVE:
|
|
retrieve_ind(call, pid, l3m);
|
|
break;
|
|
|
|
case MT_SUSPEND:
|
|
suspend_ind(call, pid, l3m);
|
|
break;
|
|
|
|
case MT_RESUME:
|
|
resume_ind(isdn_ep, pid, l3m);
|
|
break;
|
|
|
|
case MT_FACILITY:
|
|
facility_ind(call, pid, l3m);
|
|
break;
|
|
|
|
case MT_PROGRESS:
|
|
progress_ind(call, pid, l3m);
|
|
break;
|
|
|
|
case MT_FREE:
|
|
mt_free(call);
|
|
break;
|
|
|
|
default:
|
|
PDEBUG(DDSS1, DEBUG_ERROR, "unhandled message: cmd(0x%x) pid(0x%x)\n", cmd, pid);
|
|
}
|
|
}
|
|
|
|
/* receive message from L3 stack and associate with a call instance by searching or creating it */
|
|
int dss1_receive(isdn_t *isdn_ep, uint32_t cmd, uint32_t pid, struct l3_msg *l3m)
|
|
{
|
|
call_t *call;
|
|
|
|
PDEBUG(DDSS1, DEBUG_DEBUG, "message from L3 stack: cmd(0x%x) pid(0x%x)\n", cmd, pid);
|
|
|
|
/* find call that is associated with the pid */
|
|
call = isdn_ep->call_list;
|
|
while (call) {
|
|
if (call->l3_pid & MISDN_PID_CR_FLAG) {
|
|
/* local callref, so match value only */
|
|
if ((call->l3_pid & MISDN_PID_CRVAL_MASK) == (pid & MISDN_PID_CRVAL_MASK)) {
|
|
break;
|
|
}
|
|
} else {
|
|
/* remote callref, ref + channel id */
|
|
if (call->l3_pid == pid) {
|
|
break;
|
|
}
|
|
}
|
|
call = call->next;
|
|
}
|
|
|
|
/* messages for a call */
|
|
if (call) {
|
|
/* after answering the phone, the PID will be completed and CES will be assigned */
|
|
if (cmd == MT_ASSIGN) {
|
|
PDEBUG(DDSS1, DEBUG_DEBUG, "Got assignment (old pid 0x%x, new pid 0x%x)\n", call->l3_pid, pid);
|
|
if ((call->l3_pid & MISDN_PID_CRTYPE_MASK) != MISDN_PID_MASTER)
|
|
PDEBUG(DDSS1, DEBUG_ERROR, "strange setup-procid 0x%x\n", call->l3_pid);
|
|
call->l3_pid = pid;
|
|
if (call->state == ISDN_STATE_OUT_CONNECTING || call->state == ISDN_STATE_CONNECT)
|
|
call->l3_ces = pid >> 16;
|
|
}
|
|
/* if process id is master process, but a child disconnects */
|
|
if (call->isdn_ep->ntmode
|
|
&& (pid & MISDN_PID_CRTYPE_MASK) != MISDN_PID_MASTER
|
|
&& (call->l3_pid & MISDN_PID_CRTYPE_MASK) == MISDN_PID_MASTER) {
|
|
if (cmd == MT_DISCONNECT || cmd == MT_RELEASE) {
|
|
/* send special indication for child disconnect */
|
|
disconnect_ind_i(call, pid, l3m);
|
|
return 0;
|
|
}
|
|
if (cmd == MT_RELEASE_COMPLETE)
|
|
return 0;
|
|
}
|
|
/* if we have child pid and got different child pid message, ignore */
|
|
if (call->isdn_ep->ntmode
|
|
&& (pid & MISDN_PID_CRTYPE_MASK) != MISDN_PID_MASTER
|
|
&& (call->l3_pid & MISDN_PID_CRTYPE_MASK) != MISDN_PID_MASTER
|
|
&& pid != call->l3_pid)
|
|
return 0;
|
|
|
|
/* process message */
|
|
dss1_message(isdn_ep, call, cmd, pid, l3m);
|
|
return 0;
|
|
}
|
|
|
|
/* messages without call */
|
|
switch(cmd) {
|
|
case MT_SETUP:
|
|
/* creating call instance, transparent until setup with hdlc */
|
|
call = call_create(isdn_ep, DIRECTION_ORIGINATOR, 0, 0, B_MODE_TRANSPARENT);
|
|
if (!call) {
|
|
PDEBUG(DDSS1, DEBUG_ERROR, "Cannot create call instance.\n");
|
|
abort();
|
|
}
|
|
dss1_message(isdn_ep, call, cmd, pid, l3m);
|
|
break;
|
|
|
|
case MT_RESUME:
|
|
/* resume existing call instance */
|
|
dss1_message(isdn_ep, NULL, cmd, pid, l3m);
|
|
break;
|
|
|
|
case MT_FREE:
|
|
PDEBUG(DDSS1, DEBUG_DEBUG, "unused L3ID released (pid = 0x%x, call->cc_callref)\n", pid);
|
|
break;
|
|
|
|
case MT_RELEASE_COMPLETE:
|
|
PDEBUG(DDSS1, DEBUG_ERROR, "MT_RELEASE_COMPLETE must be ignored by stack, not sent to app\n");
|
|
break;
|
|
|
|
case MT_FACILITY:
|
|
// facility als broadcast
|
|
break;
|
|
|
|
case MT_L2IDLE:
|
|
// L2 became idle - we could sent a MT_L2RELEASE if we are the L2 master
|
|
PDEBUG(DDSS1, DEBUG_DEBUG, "Got L2 idle\n");
|
|
break;
|
|
|
|
default:
|
|
PDEBUG(DDSS1, DEBUG_ERROR, "unhandled message: cmd(0x%x) pid(0x%x)\n", cmd, pid);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* handles all requests from osmo-cc
|
|
*/
|
|
|
|
/* CC-SETUP REQUEST */
|
|
void setup_req(call_t *call, osmo_cc_msg_t *msg)
|
|
{
|
|
const char *sdp;
|
|
struct l3_msg *l3m;
|
|
uint8_t plan, type, screen, present, reason;
|
|
uint8_t capability, mode, rate, coding, user;
|
|
// uint8_t presentation, interpretation, hlc, exthlc;
|
|
int has_user;
|
|
char callerid[33];
|
|
char dialing[33];
|
|
char redir[33];
|
|
char keypad[33];
|
|
char display[128];
|
|
int channel, exclusive;
|
|
int rc;
|
|
struct osmo_cc_helper_audio_codecs *codecs;
|
|
|
|
PDEBUG(DDSS1, DEBUG_INFO, "SETUP REQUEST\n");
|
|
|
|
/* select codec */
|
|
if (call->isdn_ep->law == 'a')
|
|
codecs = codecs_accept_alaw_ulaw_clearmode;
|
|
else
|
|
codecs = codecs_accept_ulaw_alaw_clearmode;
|
|
|
|
/* sdp accept, force our codec, so we can use bchannel bridging if remote side supports it too */
|
|
sdp = osmo_cc_helper_audio_accept(&call->isdn_ep->cc_ep.session_config, call, codecs, rtp_receive, msg, &call->cc_session, &call->codec, 1/*force*/);
|
|
if (!sdp) {
|
|
release_and_destroy(call, 47, 415/* Unsupported Media*/, 0);
|
|
return;
|
|
}
|
|
call->sdp = strdup(sdp);
|
|
PDEBUG(DDSS1, DEBUG_INFO, "Codec %s selected for transmission.\n", call->codec->payload_name);
|
|
|
|
/* get channel */
|
|
rc = hunt_bchannel_out(call->isdn_ep, &channel, &exclusive);
|
|
if (rc < 0) {
|
|
PDEBUG(DDSS1, DEBUG_NOTICE, "There is no channel available on the interface.\n");
|
|
release_and_destroy(call, -rc, 0, 0);
|
|
return;
|
|
}
|
|
/* must seize it, if we gave a channel, so that requested channel is stored in call instance */
|
|
if (channel)
|
|
seize_bchannel(call, channel, exclusive);
|
|
|
|
/* creating pid */
|
|
call->l3_pid = request_new_pid(call->isdn_ep->ml3);
|
|
if (call->l3_pid == MISDN_PID_NONE) {
|
|
PDEBUG(DDSS1, DEBUG_NOTICE, "There is no free L3ID on the mISDN stack, please restart!\n");
|
|
release_and_destroy(call, OSMO_CC_ISDN_CAUSE_RESOURCE_UNAVAIL, 0, 0);
|
|
return;
|
|
}
|
|
PDEBUG(DDSS1, DEBUG_DEBUG, "new L3ID assigned (pid = 0x%x)\n", call->l3_pid);
|
|
|
|
/* creating setup */
|
|
l3m = create_l3msg();
|
|
|
|
/* bearer capability */
|
|
rc = osmo_cc_get_ie_bearer(msg, 0, &coding, &capability, &mode);
|
|
if (rc < 0) {
|
|
PDEBUG(DDSS1, DEBUG_DEBUG, "No bearer capability given, generating from selected codec.\n");
|
|
coding = OSMO_CC_CODING_ITU_T;
|
|
if (!strcmp(call->codec->payload_name, "CLEARMODE")) {
|
|
capability = OSMO_CC_CAPABILITY_DATA;
|
|
mode = OSMO_CC_MODE_CIRCUIT;
|
|
} else {
|
|
capability = OSMO_CC_CAPABILITY_AUDIO;
|
|
mode = OSMO_CC_MODE_CIRCUIT;
|
|
}
|
|
} else
|
|
PDEBUG(DDSS1, DEBUG_DEBUG, "Bearer capability given, override what is defined by selected codec.\n");
|
|
rate = (mode == OSMO_CC_MODE_PACKET) ? 0x00 : 0x10;
|
|
if (capability == OSMO_CC_CAPABILITY_AUDIO || capability == OSMO_CC_CAPABILITY_SPEECH) {
|
|
has_user = 1;
|
|
if (call->isdn_ep->law == 'u')
|
|
user = 2;
|
|
else
|
|
user = 3;
|
|
/* init jitter buffer */
|
|
call_create_jitter(call, 0);
|
|
} else {
|
|
has_user = 0;
|
|
user = 0;
|
|
/* init jitter buffer */
|
|
call_create_jitter(call, 1);
|
|
}
|
|
enc_ie_bearer(l3m, coding, capability, 1, mode, rate, 0, 0, has_user, user);
|
|
|
|
/* channel information */
|
|
if (call->isdn_ep->ntmode || channel != CHANNEL_ANY) /* only omit channel id in te-mode/any channel */
|
|
enc_ie_channel_id(l3m, call->isdn_ep->pri, exclusive, channel);
|
|
|
|
/* display */
|
|
rc = osmo_cc_get_ie_display(msg, 0, display, sizeof(display));
|
|
if (rc >= 0) {
|
|
/* sending display text only in ntmode */
|
|
if (call->isdn_ep->ntmode)
|
|
enc_ie_display(l3m, display);
|
|
}
|
|
|
|
/* keypad */
|
|
rc = osmo_cc_get_ie_keypad(msg, 0, keypad, sizeof(keypad));
|
|
if (rc >= 0)
|
|
enc_ie_keypad(l3m, keypad);
|
|
|
|
/* caller information */
|
|
rc = osmo_cc_get_ie_calling(msg, 0, &type, &plan, &present, &screen, callerid, sizeof(callerid));
|
|
if (rc >= 0) {
|
|
enc_ie_calling_pn(l3m, type, plan, 1, present, screen, callerid);
|
|
/* secondary caller info */
|
|
rc = osmo_cc_get_ie_calling(msg, 1, &type, &plan, &present, &screen, callerid, sizeof(callerid));
|
|
if (rc >= 0)
|
|
enc_ie_calling_pn(l3m, type, 1, plan, present, screen, callerid);
|
|
}
|
|
|
|
/* dialing information */
|
|
rc = osmo_cc_get_ie_called(msg, 0, &type, &plan, dialing, sizeof(dialing));
|
|
if (rc >= 0) {
|
|
/* check MSN */
|
|
struct msn_list *m = call->isdn_ep->msn_list;
|
|
if (m) {
|
|
/* we have an MSN list */
|
|
while (m) {
|
|
if (!strcmp(dialing, m->msn))
|
|
break;
|
|
m = m->next;
|
|
}
|
|
/* not found, so we use first MSN */
|
|
if (!m) {
|
|
strncpy(dialing, call->isdn_ep->msn_list->msn, sizeof(dialing) - 1);
|
|
dialing[sizeof(dialing) - 1] = '\0';
|
|
}
|
|
}
|
|
enc_ie_called_pn(l3m, type, plan, dialing);
|
|
}
|
|
|
|
/* redirecting number */
|
|
rc = osmo_cc_get_ie_redir(msg, 0, &type, &plan, &present, &screen, &reason, redir, sizeof(redir));
|
|
if (rc >= 0) {
|
|
/* sending redirecting number only in ntmode */
|
|
if (call->isdn_ep->ntmode)
|
|
enc_ie_redirecting(l3m, type, plan, 1, present, screen, 1, reason, redir);
|
|
}
|
|
|
|
rc = osmo_cc_get_ie_complete(msg, 0);
|
|
if (rc >= 0) {
|
|
call->sending_complete = 1;
|
|
enc_ie_complete(l3m, 1);
|
|
}
|
|
|
|
new_state(call, ISDN_STATE_OUT_SETUP);
|
|
|
|
/* send message to ISDN */
|
|
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_SETUP, call->l3_pid, l3m);
|
|
|
|
/* process local briding capability */
|
|
bridge_socket_client_update(call, 1);
|
|
}
|
|
|
|
void proc_req(call_t *call, uint32_t pid, osmo_cc_msg_t *msg, int with_ies);
|
|
|
|
/* process progress message and handle tones */
|
|
int process_progress(call_t *call, osmo_cc_msg_t *msg, struct l3_msg *l3m, int call_state, uint8_t cause)
|
|
{
|
|
uint8_t coding, location, progress;
|
|
int tone;
|
|
int rc;
|
|
|
|
/* read progress message from upper layer */
|
|
rc = osmo_cc_get_ie_progress(msg, 0, &coding, &location, &progress);
|
|
if (rc < 0) {
|
|
coding = OSMO_CC_CODING_ITU_T;
|
|
progress = 0;
|
|
}
|
|
|
|
/* the audio path is throughconnected */
|
|
if (coding == OSMO_CC_CODING_ITU_T && (progress == 1 || progress == 8))
|
|
call->audio_path = 1;
|
|
|
|
/* if local tones are not enabled, don't process them here */
|
|
if (!call->isdn_ep->local_tones)
|
|
goto finish;
|
|
|
|
/* if we have remote tones, we don't process local tones */
|
|
if (call->send_remote_tones)
|
|
goto finish;
|
|
|
|
/* if we are getting remote tones... */
|
|
if (coding == OSMO_CC_CODING_ITU_T && (progress == 1 || progress == 8)) {
|
|
/* if we are sending local tones, we stop them */
|
|
if (call->send_local_tones) {
|
|
PDEBUG(DDSS1, DEBUG_DEBUG, "Stop sending locally generated tones.\n");
|
|
call->send_local_tones = 0;
|
|
bchannel_tone(call, 0);
|
|
}
|
|
PDEBUG(DDSS1, DEBUG_DEBUG, "Using remote tones.\n");
|
|
call->send_remote_tones = 1;
|
|
goto finish;
|
|
}
|
|
|
|
/* if we have local tones the first time, send progress indicator */
|
|
if (!call->send_local_tones) {
|
|
PDEBUG(DDSS1, DEBUG_DEBUG, "Start sending locally generated tones.\n");
|
|
call->send_local_tones = 1;
|
|
coding = OSMO_CC_CODING_ITU_T;
|
|
location = call->isdn_ep->serving_location;
|
|
progress = OSMO_CC_PROGRESS_INBAND_INFO_AVAILABLE;
|
|
}
|
|
|
|
/* select local tones from call state */
|
|
switch (call_state) {
|
|
case ISDN_STATE_IN_OVERLAP:
|
|
if (call->any_dialing)
|
|
bchannel_tone(call, 0);
|
|
else {
|
|
switch (call->isdn_ep->local_tones) {
|
|
case TONES_TYPE_GERMAN:
|
|
tone = TONE_GERMAN_DIALTONE;
|
|
break;
|
|
case TONES_TYPE_OLDGERMAN:
|
|
tone = TONE_GERMAN_OLDDIALTONE;
|
|
break;
|
|
default:
|
|
tone = TONE_AMERICAN_DIALTONE;
|
|
}
|
|
bchannel_tone(call, tone);
|
|
}
|
|
break;
|
|
case ISDN_STATE_IN_PROCEEDING:
|
|
bchannel_tone(call, 0);
|
|
break;
|
|
case ISDN_STATE_IN_ALERTING:
|
|
switch (call->isdn_ep->local_tones) {
|
|
case TONES_TYPE_GERMAN:
|
|
tone = TONE_GERMAN_RINGING;
|
|
break;
|
|
case TONES_TYPE_OLDGERMAN:
|
|
tone = TONE_GERMAN_OLDRINGING;
|
|
break;
|
|
default:
|
|
tone = TONE_AMERICAN_RINGING;
|
|
}
|
|
bchannel_tone(call, tone);
|
|
break;
|
|
case ISDN_STATE_OUT_DISCONNECT:
|
|
switch (cause) {
|
|
case 17:
|
|
case 18:
|
|
switch (call->isdn_ep->local_tones) {
|
|
case TONES_TYPE_GERMAN:
|
|
tone = TONE_GERMAN_BUSY;
|
|
break;
|
|
case TONES_TYPE_OLDGERMAN:
|
|
tone = TONE_GERMAN_OLDBUSY;
|
|
break;
|
|
default:
|
|
tone = TONE_AMERICAN_BUSY;
|
|
}
|
|
break;
|
|
case 16:
|
|
case 19:
|
|
switch (call->isdn_ep->local_tones) {
|
|
case TONES_TYPE_GERMAN:
|
|
tone = TONE_GERMAN_HANGUP;
|
|
break;
|
|
case TONES_TYPE_OLDGERMAN:
|
|
tone = TONE_GERMAN_OLDHANGUP;
|
|
break;
|
|
default:
|
|
tone = TONE_AMERICAN_BUSY;
|
|
}
|
|
break;
|
|
case 34:
|
|
switch (call->isdn_ep->local_tones) {
|
|
case TONES_TYPE_GERMAN:
|
|
tone = TONE_GERMAN_GASSENBESETZT;
|
|
break;
|
|
case TONES_TYPE_OLDGERMAN:
|
|
tone = TONE_GERMAN_OLDHANGUP;
|
|
break;
|
|
default:
|
|
tone = TONE_SPECIAL_INFO;
|
|
}
|
|
break;
|
|
default:
|
|
switch (call->isdn_ep->local_tones) {
|
|
case TONES_TYPE_GERMAN:
|
|
tone = TONE_SPECIAL_INFO;
|
|
break;
|
|
case TONES_TYPE_OLDGERMAN:
|
|
tone = TONE_SPECIAL_INFO;
|
|
break;
|
|
default:
|
|
tone = TONE_SPECIAL_INFO;
|
|
}
|
|
}
|
|
bchannel_tone(call, tone);
|
|
break;
|
|
}
|
|
|
|
finish:
|
|
/* append progress message. return TRUE, if message was added */
|
|
if (progress) {
|
|
enc_ie_progress(l3m, coding, location, progress);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* CC-SETUP-ACKNOWLEDGE REQUEST */
|
|
void setup_ack_req(call_t *call, uint32_t pid, osmo_cc_msg_t *msg)
|
|
{
|
|
struct l3_msg *l3m;
|
|
char display[128];
|
|
int rc;
|
|
|
|
/* in case of sending complete, we proceed */
|
|
if (call->sending_complete) {
|
|
new_state(call, ISDN_STATE_IN_PROCEEDING);
|
|
proc_req(call, pid, msg, 1);
|
|
return;
|
|
}
|
|
|
|
/* in case of te-mode and multipoint, we proceed, because overlap dialing is not supported in that mode */
|
|
if (!call->isdn_ep->ntmode && !call->isdn_ep->ptp) {
|
|
new_state(call, ISDN_STATE_IN_PROCEEDING);
|
|
proc_req(call, pid, msg, 1);
|
|
return;
|
|
}
|
|
|
|
PDEBUG(DDSS1, DEBUG_INFO, "SETUP-ACKNOWLEDGE REQUEST (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
|
|
/* creating setup acknowledge */
|
|
l3m = create_l3msg();
|
|
|
|
/* channel information */
|
|
if (!call->channel_negotiated) {
|
|
call->channel_negotiated = 1;
|
|
enc_ie_channel_id(l3m, call->isdn_ep->pri, 1, call->b_channel);
|
|
}
|
|
|
|
// NOTE: codec negotiation is performed at cc_message()
|
|
|
|
/* progress information */
|
|
process_progress(call, msg, l3m, ISDN_STATE_IN_OVERLAP, 0);
|
|
|
|
/* display */
|
|
rc = osmo_cc_get_ie_display(msg, 0, display, sizeof(display));
|
|
if (rc >= 0) {
|
|
/* sending display text only in ntmode */
|
|
if (call->isdn_ep->ntmode)
|
|
enc_ie_display(l3m, display);
|
|
}
|
|
|
|
new_state(call, ISDN_STATE_IN_OVERLAP);
|
|
|
|
/* send message to ISDN */
|
|
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_SETUP_ACKNOWLEDGE, call->l3_pid, l3m);
|
|
}
|
|
|
|
/* CC-PROCEEDING REQUEST */
|
|
void proc_req(call_t *call, uint32_t pid, osmo_cc_msg_t *msg, int with_ies)
|
|
{
|
|
struct l3_msg *l3m;
|
|
uint8_t plan, type, screen, present, reason;
|
|
uint8_t notify;
|
|
char redir[33];
|
|
char display[128];
|
|
int rc;
|
|
|
|
if (call->proceeding_sent)
|
|
return;
|
|
call->proceeding_sent = 1;
|
|
|
|
PDEBUG(DDSS1, DEBUG_INFO, "PROCEEDING REQUEST (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
|
|
/* creating proceeding */
|
|
l3m = create_l3msg();
|
|
|
|
/* channel information */
|
|
if (!call->channel_negotiated) {
|
|
call->channel_negotiated = 1;
|
|
enc_ie_channel_id(l3m, call->isdn_ep->pri, 1, call->b_channel);
|
|
}
|
|
|
|
// NOTE: codec negotiation is performed at cc_message()
|
|
|
|
if (!with_ies)
|
|
goto skip_ies;
|
|
|
|
/* progress information */
|
|
process_progress(call, msg, l3m, ISDN_STATE_IN_PROCEEDING, 0);
|
|
|
|
/* notify */
|
|
rc = osmo_cc_get_ie_notify(msg, 0, ¬ify);
|
|
if (rc >= 0)
|
|
enc_ie_notify(l3m, notify);
|
|
|
|
/* display */
|
|
rc = osmo_cc_get_ie_display(msg, 0, display, sizeof(display));
|
|
if (rc >= 0) {
|
|
/* sending display text only in ntmode */
|
|
if (call->isdn_ep->ntmode)
|
|
enc_ie_display(l3m, display);
|
|
}
|
|
|
|
/* redirection number */
|
|
rc = osmo_cc_get_ie_redir(msg, 0, &type, &plan, &present, &screen, &reason, redir, sizeof(redir));
|
|
/* sending redirection number only in ntmode */
|
|
if (rc >= 0 && call->isdn_ep->ntmode)
|
|
enc_ie_redirection(l3m, type, plan, 1, present, redir);
|
|
|
|
/* Metering handling and AOC-S generation */
|
|
rc = osmo_cc_get_ie_metering(msg, 0, &(call->metering_connect_units), &(call->metering_unit_period_decisecs));
|
|
if(rc >= 0) {
|
|
call->metering_info_received = 1;
|
|
generate_aocs_ie(call, l3m);
|
|
}
|
|
|
|
skip_ies:
|
|
new_state(call, ISDN_STATE_IN_PROCEEDING);
|
|
|
|
/* send message to ISDN */
|
|
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_CALL_PROCEEDING, call->l3_pid, l3m);
|
|
}
|
|
|
|
/* CC-ALERTING REQUEST */
|
|
void alert_req(call_t *call, uint32_t pid, osmo_cc_msg_t *msg)
|
|
{
|
|
struct l3_msg *l3m;
|
|
uint8_t plan, type, screen, present, reason;
|
|
uint8_t notify;
|
|
char redir[33];
|
|
char display[128];
|
|
int rc;
|
|
|
|
/* NT-MODE in setup state we must send PROCEEDING first */
|
|
if (!call->proceeding_sent && call->isdn_ep->ntmode) {
|
|
proc_req(call, pid, msg, 0);
|
|
}
|
|
|
|
PDEBUG(DDSS1, DEBUG_INFO, "ALERTING REQUEST (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
|
|
/* creating alerting */
|
|
l3m = create_l3msg();
|
|
|
|
/* channel information */
|
|
if (!call->channel_negotiated) {
|
|
call->channel_negotiated = 1;
|
|
enc_ie_channel_id(l3m, call->isdn_ep->pri, 1, call->b_channel);
|
|
}
|
|
|
|
// NOTE: codec negotiation is performed at cc_message()
|
|
|
|
/* progress information */
|
|
process_progress(call, msg, l3m, ISDN_STATE_IN_ALERTING, 0);
|
|
|
|
/* notify */
|
|
rc = osmo_cc_get_ie_notify(msg, 0, ¬ify);
|
|
if (rc >= 0)
|
|
enc_ie_notify(l3m, notify);
|
|
|
|
/* display */
|
|
rc = osmo_cc_get_ie_display(msg, 0, display, sizeof(display));
|
|
if (rc >= 0) {
|
|
/* sending display text only in ntmode */
|
|
if (call->isdn_ep->ntmode)
|
|
enc_ie_display(l3m, display);
|
|
}
|
|
|
|
/* redirection number */
|
|
rc = osmo_cc_get_ie_redir(msg, 0, &type, &plan, &present, &screen, &reason, redir, sizeof(redir));
|
|
/* sending redirection number only in ntmode */
|
|
if (rc >= 0 && call->isdn_ep->ntmode)
|
|
enc_ie_redirection(l3m, type, plan, 1, present, redir);
|
|
|
|
/* Metering handling and AOC-S generation */
|
|
rc = osmo_cc_get_ie_metering(msg, 0, &(call->metering_connect_units), &(call->metering_unit_period_decisecs));
|
|
if(rc >= 0) {
|
|
call->metering_info_received = 1;
|
|
generate_aocs_ie(call, l3m);
|
|
}
|
|
|
|
new_state(call, ISDN_STATE_IN_ALERTING);
|
|
|
|
/* send message to ISDN */
|
|
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_ALERTING, call->l3_pid, l3m);
|
|
}
|
|
|
|
/* CC-CONNECT REQUEST */
|
|
void setup_rsp(call_t *call, uint32_t pid, osmo_cc_msg_t *msg)
|
|
{
|
|
struct l3_msg *l3m;
|
|
uint8_t type, plan, present, screen;
|
|
uint8_t coding, location, progress;
|
|
time_t current_time;
|
|
char connected[33];
|
|
char display[128];
|
|
int rc;
|
|
uint8_t fac_ie[256];
|
|
struct asn1_parm fac;
|
|
|
|
/* NT-MODE in setup state we must send PROCEEDING first */
|
|
if (!call->proceeding_sent && call->isdn_ep->ntmode) {
|
|
proc_req(call, pid, msg, 0);
|
|
}
|
|
|
|
PDEBUG(DDSS1, DEBUG_INFO, "CONNECT REQUEST (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
|
|
/* stop local tones, if sending until now */
|
|
if (call->send_local_tones) {
|
|
PDEBUG(DDSS1, DEBUG_INFO, "Stop sending locally generated tones.\n");
|
|
call->send_local_tones = 0;
|
|
bchannel_tone(call, 0);
|
|
}
|
|
|
|
/* creating connect */
|
|
l3m = create_l3msg();
|
|
|
|
/* channel information */
|
|
if (!call->channel_negotiated) {
|
|
call->channel_negotiated = 1;
|
|
enc_ie_channel_id(l3m, call->isdn_ep->pri, 1, call->b_channel);
|
|
}
|
|
|
|
// NOTE: codec negotiation is performed at cc_message()
|
|
|
|
/* progress information */
|
|
rc = osmo_cc_get_ie_progress(msg, 0, &coding, &location, &progress);
|
|
if (rc >= 0)
|
|
enc_ie_progress(l3m, coding, location, progress);
|
|
|
|
/* display */
|
|
rc = osmo_cc_get_ie_display(msg, 0, display, sizeof(display));
|
|
if (rc >= 0) {
|
|
/* sending display text only in ntmode */
|
|
if (call->isdn_ep->ntmode)
|
|
enc_ie_display(l3m, display);
|
|
}
|
|
|
|
/* date & time, in NT mode only */
|
|
if (call->isdn_ep->ntmode) {
|
|
time(¤t_time);
|
|
enc_ie_date(l3m, current_time, 0);
|
|
}
|
|
|
|
/* connected number */
|
|
rc = osmo_cc_get_ie_calling(msg, 0, &type, &plan, &present, &screen, connected, sizeof(connected));
|
|
if (rc >= 0) {
|
|
enc_ie_connected_pn(l3m, type, plan, 1, present, screen, connected);
|
|
rc = osmo_cc_get_ie_calling(msg, 1, &type, &plan, &present, &screen, connected, sizeof(connected));
|
|
/* secondary connected info */
|
|
if (rc >= 0)
|
|
enc_ie_connected_pn(l3m, type, plan, 1, present, screen, connected);
|
|
}
|
|
|
|
/* Metering handling and AOC-S generation */
|
|
rc = osmo_cc_get_ie_metering(msg, 0, &(call->metering_connect_units), &(call->metering_unit_period_decisecs));
|
|
if(rc >= 0) {
|
|
call->metering_info_received = 1;
|
|
generate_aocs_ie(call, l3m);
|
|
}
|
|
|
|
new_state(call, ISDN_STATE_IN_CONNECTING);
|
|
|
|
/* AOC-D handling on connect */
|
|
if(call->isdn_ep->ntmode && call->isdn_ep->aocd && call->metering_info_received && call->metering_connect_units > 0)
|
|
{
|
|
PDEBUG(DISDN, DEBUG_DEBUG, "Attaching AOC-D connect facility, units: %d\n", call->metering_connect_units);
|
|
call->metering_total_units = call->metering_connect_units;
|
|
|
|
memset(&fac, 0, sizeof(fac));
|
|
fac.Valid = 1;
|
|
fac.comp = CompInvoke;
|
|
fac.u.inv.invokeId = 2; /* doesn't matter since no response is expected */
|
|
fac.u.inv.operationValue = Fac_AOCDChargingUnit;
|
|
|
|
fac.u.inv.o.AOCchu.recordedUnits = call->metering_total_units;
|
|
encodeFac(fac_ie, &fac);
|
|
enc_ie_facility(l3m, fac_ie + 2, fac_ie[1]);
|
|
|
|
if(call->metering_unit_period_decisecs > 0) {
|
|
PDEBUG(DISDN, DEBUG_DEBUG, "Scheduling AOC-D unit information every %d deciseconds.\n", call->metering_unit_period_decisecs);
|
|
call->aocd_unit_timer_started = 1;
|
|
timer_init(&call->aocd_unit_timer, aocd_timer_cb, call);
|
|
timer_start(&call->aocd_unit_timer, (double)call->metering_unit_period_decisecs / (double)10);
|
|
}
|
|
}
|
|
|
|
/* send message to ISDN */
|
|
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_CONNECT, call->l3_pid, l3m);
|
|
|
|
/* in NT mode we might not receive CONNECT ACKNOWLEDGE */
|
|
if (call->isdn_ep->ntmode) {
|
|
new_state(call, ISDN_STATE_CONNECT);
|
|
|
|
/* create osmo-cc message */
|
|
msg = osmo_cc_new_msg(OSMO_CC_MSG_SETUP_COMP_IND);
|
|
|
|
/* send message to osmo-cc */
|
|
osmo_cc_ll_msg(&call->isdn_ep->cc_ep, call->cc_callref, msg);
|
|
}
|
|
|
|
/* the audio path is throughconnected */
|
|
call->audio_path = 1;
|
|
}
|
|
|
|
/* CC-CONNECT ACKNOWLEDGE REQUEST */
|
|
void setup_comp_req(call_t *call, uint32_t pid, osmo_cc_msg_t *msg)
|
|
{
|
|
struct l3_msg *l3m;
|
|
char display[128];
|
|
int rc;
|
|
|
|
PDEBUG(DDSS1, DEBUG_INFO, "CONNECT ACKNOWLEDGE REQUEST (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
|
|
new_state(call, ISDN_STATE_CONNECT);
|
|
|
|
/* only send in NT mode */
|
|
if (!call->isdn_ep->ntmode)
|
|
return;
|
|
|
|
/* creating connect acknowledge */
|
|
l3m = create_l3msg();
|
|
|
|
/* if we had no bchannel before, we send it now (answer call with NO_CHANNEL) */
|
|
if (call->setup_comp_req_channel_assignment && call->b_channel)
|
|
enc_ie_channel_id(l3m, call->isdn_ep->pri, 1, call->b_channel);
|
|
|
|
/* display */
|
|
rc = osmo_cc_get_ie_display(msg, 0, display, sizeof(display));
|
|
if (rc >= 0) {
|
|
/* sending display text only in ntmode */
|
|
if (call->isdn_ep->ntmode)
|
|
enc_ie_display(l3m, display);
|
|
}
|
|
|
|
/* send message to ISDN */
|
|
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_CONNECT_ACKNOWLEDGE, call->l3_pid, l3m);
|
|
}
|
|
|
|
/* CC-INFORMATION REQUEST */
|
|
void info_req(call_t *call, uint32_t pid, osmo_cc_msg_t *msg)
|
|
{
|
|
struct l3_msg *l3m;
|
|
uint8_t type, plan;
|
|
char keypad[33];
|
|
char dialing[33];
|
|
int rc_called, rc_kp;
|
|
|
|
PDEBUG(DDSS1, DEBUG_INFO, "INFORMATION REQUEST (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
|
|
/* keypad */
|
|
rc_kp = osmo_cc_get_ie_keypad(msg, 0, keypad, sizeof(keypad));
|
|
|
|
/* dialing information */
|
|
rc_called = osmo_cc_get_ie_called(msg, 0, &type, &plan, dialing, sizeof(dialing));
|
|
|
|
if (rc_called >= 0 || rc_kp >= 0) {
|
|
/* creating information */
|
|
l3m = create_l3msg();
|
|
if (rc_kp >= 0)
|
|
enc_ie_keypad(l3m, keypad);
|
|
if (rc_called >= 0)
|
|
enc_ie_called_pn(l3m, type, plan, dialing);
|
|
/* send message to ISDN */
|
|
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_INFORMATION, call->l3_pid, l3m);
|
|
}
|
|
|
|
/* reset overlap timeout */
|
|
new_state(call, call->state);
|
|
}
|
|
|
|
/* CC-PROGRESS REQUEST */
|
|
void progress_req(call_t *call, uint32_t pid, osmo_cc_msg_t *msg)
|
|
{
|
|
struct l3_msg *l3m;
|
|
int rc;
|
|
|
|
PDEBUG(DDSS1, DEBUG_INFO, "PROGRESS REQUEST (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
|
|
/* creating progress */
|
|
l3m = create_l3msg();
|
|
|
|
/* progress information */
|
|
rc = process_progress(call, msg, l3m, call->state, 0);
|
|
|
|
/* send message to ISDN */
|
|
if (rc)
|
|
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_PROGRESS, call->l3_pid, l3m);
|
|
else
|
|
free_l3msg(l3m);
|
|
}
|
|
|
|
/* CC-NOTIFY REQUEST */
|
|
void notify_req(call_t *call, uint32_t pid, osmo_cc_msg_t *msg)
|
|
{
|
|
struct l3_msg *l3m;
|
|
uint8_t notify;
|
|
uint8_t plan, type, screen, present, reason;
|
|
char redir[32];
|
|
char display[128];
|
|
int rc_notify, rc_redir, rc_display;
|
|
|
|
PDEBUG(DDSS1, DEBUG_INFO, "NOTIFY REQUEST (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
|
|
/* notify */
|
|
rc_notify = osmo_cc_get_ie_notify(msg, 0, ¬ify);
|
|
|
|
/* redirection number */
|
|
rc_redir = osmo_cc_get_ie_redir(msg, 0, &type, &plan, &present, &screen, &reason, redir, sizeof(redir));
|
|
|
|
/* display */
|
|
rc_display = osmo_cc_get_ie_display(msg, 0, display, sizeof(display));
|
|
|
|
if (rc_notify >= 0) {
|
|
/* creating notify */
|
|
l3m = create_l3msg();
|
|
enc_ie_notify(l3m, notify);
|
|
/* sending display only in ntmode */
|
|
if (rc_display >= 0 && call->isdn_ep->ntmode)
|
|
enc_ie_display(l3m, display);
|
|
/* sending redirection number only in ntmode */
|
|
if (rc_redir >= 0 && call->isdn_ep->ntmode)
|
|
enc_ie_redirection(l3m, type, plan, 1, present, redir);
|
|
/* send message to ISDN */
|
|
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_NOTIFY, call->l3_pid, l3m);
|
|
} else {
|
|
PDEBUG(DDSS1, DEBUG_ERROR, "NOTIFY REQUEST without notification indicator, ignoring.\n");
|
|
}
|
|
}
|
|
|
|
/* CC-REJECT REQUEST */
|
|
void rej_req(call_t *call, uint32_t pid, osmo_cc_msg_t *msg)
|
|
{
|
|
struct l3_msg *l3m;
|
|
uint8_t location, isdn_cause, socket_cause;
|
|
uint16_t sip_cause;
|
|
char display[128];
|
|
int rc;
|
|
|
|
PDEBUG(DDSS1, DEBUG_INFO, "REJECT REQUEST (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
|
|
/* creating release complete */
|
|
l3m = create_l3msg();
|
|
|
|
/* cause */
|
|
rc = osmo_cc_get_ie_cause(msg, 0, &location, &isdn_cause, &sip_cause, &socket_cause);
|
|
if (rc < 0) {
|
|
location = OSMO_CC_LOCATION_BEYOND_INTERWORKING;
|
|
isdn_cause = OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR;
|
|
}
|
|
enc_ie_cause(l3m, location, isdn_cause);
|
|
|
|
/* display */
|
|
rc = osmo_cc_get_ie_display(msg, 0, display, sizeof(display));
|
|
if (rc >= 0) {
|
|
/* sending display text only in ntmode */
|
|
if (call->isdn_ep->ntmode)
|
|
enc_ie_display(l3m, display);
|
|
}
|
|
/* send message to ISDN */
|
|
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_RELEASE_COMPLETE, call->l3_pid, l3m);
|
|
|
|
/* call terminated */
|
|
new_state(call, ISDN_STATE_IDLE);
|
|
call_destroy(call);
|
|
}
|
|
|
|
/* CC-DISCONNECT REQUEST */
|
|
void disc_req(call_t *call, uint32_t pid, osmo_cc_msg_t *msg)
|
|
{
|
|
struct l3_msg *l3m;
|
|
uint8_t location;
|
|
uint8_t isdn_cause, socket_cause;
|
|
uint16_t sip_cause;
|
|
char display[128];
|
|
int rc;
|
|
|
|
/* send a proceeding and open channel if we are still in setup state */
|
|
if (call->state == ISDN_STATE_IN_SETUP) {
|
|
proc_req(call, pid, msg, 0);
|
|
}
|
|
|
|
PDEBUG(DDSS1, DEBUG_INFO, "DISCONNECT REQUEST (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
|
|
/* creating disconnect */
|
|
l3m = create_l3msg();
|
|
|
|
/* cause */
|
|
rc = osmo_cc_get_ie_cause(msg, 0, &location, &isdn_cause, &sip_cause, &socket_cause);
|
|
if (rc < 0) {
|
|
location = OSMO_CC_LOCATION_BEYOND_INTERWORKING;
|
|
isdn_cause = OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR;
|
|
}
|
|
enc_ie_cause(l3m, location, isdn_cause);
|
|
|
|
/* reset states and handle progress information */
|
|
if (call->send_local_tones) {
|
|
PDEBUG(DDSS1, DEBUG_DEBUG, "Stop sending locally generated tones.\n");
|
|
call->send_local_tones = 0;
|
|
bchannel_tone(call, 0);
|
|
}
|
|
call->send_remote_tones = 0;
|
|
call->audio_path = 0;
|
|
process_progress(call, msg, l3m, ISDN_STATE_OUT_DISCONNECT, isdn_cause);
|
|
|
|
/* display */
|
|
rc = osmo_cc_get_ie_display(msg, 0, display, sizeof(display));
|
|
if (rc >= 0) {
|
|
/* sending display text only in ntmode */
|
|
if (call->isdn_ep->ntmode)
|
|
enc_ie_display(l3m, display);
|
|
}
|
|
|
|
/* stop AOC-D timer */
|
|
if(call->aocd_unit_timer_started)
|
|
{
|
|
timer_stop(&call->aocd_unit_timer);
|
|
timer_exit(&call->aocd_unit_timer);
|
|
call->aocd_unit_timer_started = 0;
|
|
}
|
|
|
|
new_state(call, ISDN_STATE_OUT_DISCONNECT);
|
|
|
|
/* send message to ISDN */
|
|
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_DISCONNECT, call->l3_pid, l3m);
|
|
}
|
|
|
|
/* CC-RELEASE REQUEST */
|
|
void rel_req(call_t *call, uint32_t pid, osmo_cc_msg_t *msg)
|
|
{
|
|
struct l3_msg *l3m;
|
|
uint8_t location, isdn_cause, socket_cause;
|
|
uint16_t sip_cause;
|
|
char display[128];
|
|
int rc;
|
|
|
|
PDEBUG(DDSS1, DEBUG_INFO, "RELEASE REQUEST (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
|
|
|
|
/* creating release */
|
|
l3m = create_l3msg();
|
|
|
|
/* cause */
|
|
rc = osmo_cc_get_ie_cause(msg, 0, &location, &isdn_cause, &sip_cause, &socket_cause);
|
|
if (rc < 0) {
|
|
location = OSMO_CC_LOCATION_BEYOND_INTERWORKING;
|
|
isdn_cause = OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR;
|
|
}
|
|
enc_ie_cause(l3m, location, isdn_cause);
|
|
|
|
/* display */
|
|
rc = osmo_cc_get_ie_display(msg, 0, display, sizeof(display));
|
|
if (rc >= 0) {
|
|
/* sending display text only in ntmode */
|
|
if (call->isdn_ep->ntmode)
|
|
enc_ie_display(l3m, display);
|
|
}
|
|
|
|
new_state(call, ISDN_STATE_OUT_RELEASE);
|
|
|
|
/* send message to ISDN */
|
|
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_RELEASE, call->l3_pid, l3m);
|
|
}
|
|
|
|
/* special case where call is suspended */
|
|
void disc_rel_suspended(call_t *call, osmo_cc_msg_t *msg)
|
|
{
|
|
uint8_t location, isdn_cause, socket_cause;
|
|
uint16_t sip_cause;
|
|
int rc;
|
|
|
|
rc = osmo_cc_get_ie_cause(msg, 0, &location, &isdn_cause, &sip_cause, &socket_cause);
|
|
if (rc < 0)
|
|
isdn_cause = OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR;
|
|
|
|
msg = osmo_cc_new_msg((msg->type == OSMO_CC_MSG_REL_REQ) ? OSMO_CC_MSG_REL_CNF : OSMO_CC_MSG_REL_IND);
|
|
|
|
/* cause */
|
|
osmo_cc_add_ie_cause(msg, call->isdn_ep->serving_location, isdn_cause, 0, 0);
|
|
|
|
/* send message to osmo-cc */
|
|
osmo_cc_ll_msg(&call->isdn_ep->cc_ep, call->cc_callref, msg);
|
|
|
|
/* terminate call */
|
|
new_state(call, ISDN_STATE_IDLE);
|
|
call_destroy(call);
|
|
}
|
|
|
|
void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg)
|
|
{
|
|
isdn_t *isdn_ep = ep->priv;
|
|
call_t *call;
|
|
|
|
/* hunt for callref */
|
|
call = isdn_ep->call_list;
|
|
while (call) {
|
|
if (call->cc_callref == callref)
|
|
break;
|
|
call = call->next;
|
|
}
|
|
|
|
/* process SETUP */
|
|
if (!call) {
|
|
if (msg->type != OSMO_CC_MSG_SETUP_REQ) {
|
|
PDEBUG(DDSS1, DEBUG_ERROR, "received message without call instance, please fix!\n");
|
|
osmo_cc_free_msg(msg);
|
|
return;
|
|
}
|
|
/* creating call instance, transparent until setup with hdlc */
|
|
call = call_create(isdn_ep, DIRECTION_TERMINATOR, 0, 0, B_MODE_TRANSPARENT);
|
|
if (!call) {
|
|
PDEBUG(DDSS1, DEBUG_ERROR, "Cannot create call instance.\n");
|
|
abort();
|
|
}
|
|
/* link with cc */
|
|
call->cc_callref = callref;
|
|
}
|
|
|
|
switch (msg->type) {
|
|
case OSMO_CC_MSG_SETUP_REQ: /* dial-out command received from epoint */
|
|
setup_req(call, msg);
|
|
break;
|
|
case OSMO_CC_MSG_SETUP_ACK_REQ: /* more information is needed */
|
|
osmo_cc_helper_audio_negotiate(msg, &call->cc_session, &call->codec);
|
|
setup_ack_req(call, call->l3_pid, msg);
|
|
break;
|
|
case OSMO_CC_MSG_PROC_REQ: /* call of endpoint is proceeding */
|
|
osmo_cc_helper_audio_negotiate(msg, &call->cc_session, &call->codec);
|
|
proc_req(call, call->l3_pid, msg, 1);
|
|
break;
|
|
case OSMO_CC_MSG_ALERT_REQ: /* call of endpoint is ringing */
|
|
osmo_cc_helper_audio_negotiate(msg, &call->cc_session, &call->codec);
|
|
alert_req(call, call->l3_pid, msg);
|
|
break;
|
|
case OSMO_CC_MSG_SETUP_RSP: /* call of endpoint is connected */
|
|
osmo_cc_helper_audio_negotiate(msg, &call->cc_session, &call->codec);
|
|
setup_rsp(call, call->l3_pid, msg);
|
|
break;
|
|
case OSMO_CC_MSG_SETUP_COMP_REQ: /* call of endpoint is connected */
|
|
setup_comp_req(call, call->l3_pid, msg);
|
|
break;
|
|
case OSMO_CC_MSG_INFO_REQ: /* overlap dialing */
|
|
if (isdn_ep->ntmode
|
|
&& call->state != ISDN_STATE_OUT_OVERLAP
|
|
&& call->state != ISDN_STATE_OUT_CONNECTING
|
|
&& call->state != ISDN_STATE_IN_OVERLAP
|
|
&& call->state != ISDN_STATE_IN_PROCEEDING
|
|
&& call->state != ISDN_STATE_IN_ALERTING
|
|
&& call->state != ISDN_STATE_IN_CONNECTING
|
|
&& call->state != ISDN_STATE_CONNECT
|
|
&& call->state != ISDN_STATE_OUT_DISCONNECT
|
|
&& call->state != ISDN_STATE_IN_DISCONNECT)
|
|
break;
|
|
if (!isdn_ep->ntmode
|
|
&& call->state != ISDN_STATE_OUT_OVERLAP
|
|
&& call->state != ISDN_STATE_OUT_PROCEEDING
|
|
&& call->state != ISDN_STATE_OUT_ALERTING
|
|
&& call->state != ISDN_STATE_OUT_CONNECTING
|
|
&& call->state != ISDN_STATE_IN_OVERLAP
|
|
&& call->state != ISDN_STATE_IN_PROCEEDING
|
|
&& call->state != ISDN_STATE_IN_ALERTING
|
|
&& call->state != ISDN_STATE_IN_CONNECTING
|
|
&& call->state != ISDN_STATE_CONNECT
|
|
&& call->state != ISDN_STATE_OUT_DISCONNECT
|
|
&& call->state != ISDN_STATE_IN_DISCONNECT)
|
|
break;
|
|
info_req(call, call->l3_pid, msg);
|
|
break;
|
|
case OSMO_CC_MSG_PROGRESS_REQ: /* progress */
|
|
osmo_cc_helper_audio_negotiate(msg, &call->cc_session, &call->codec);
|
|
if (isdn_ep->ntmode
|
|
&& call->state != ISDN_STATE_OUT_OVERLAP
|
|
&& call->state != ISDN_STATE_OUT_PROCEEDING
|
|
&& call->state != ISDN_STATE_IN_OVERLAP
|
|
&& call->state != ISDN_STATE_IN_PROCEEDING
|
|
&& call->state != ISDN_STATE_IN_ALERTING)
|
|
break;
|
|
if (!isdn_ep->ntmode
|
|
&& call->state != ISDN_STATE_OUT_OVERLAP
|
|
&& call->state != ISDN_STATE_OUT_PROCEEDING
|
|
&& call->state != ISDN_STATE_OUT_ALERTING
|
|
&& call->state != ISDN_STATE_IN_OVERLAP
|
|
&& call->state != ISDN_STATE_IN_PROCEEDING)
|
|
break;
|
|
progress_req(call, call->l3_pid, msg);
|
|
break;
|
|
case OSMO_CC_MSG_NOTIFY_REQ: /* display and notifications */
|
|
if (call->state != ISDN_STATE_IN_PROCEEDING
|
|
&& call->state != ISDN_STATE_IN_ALERTING
|
|
&& call->state != ISDN_STATE_IN_CONNECTING
|
|
&& call->state != ISDN_STATE_CONNECT)
|
|
break;
|
|
notify_req(call, call->l3_pid, msg);
|
|
break;
|
|
case OSMO_CC_MSG_REJ_REQ: /* call has been rejected */
|
|
rej_req(call, call->l3_pid, msg);
|
|
break;
|
|
case OSMO_CC_MSG_DISC_REQ: /* call has been disconnected */
|
|
osmo_cc_helper_audio_negotiate(msg, &call->cc_session, &call->codec);
|
|
if (call->state == ISDN_STATE_SUSPENDED) {
|
|
disc_rel_suspended(call, msg);
|
|
break;
|
|
}
|
|
if (call->state != ISDN_STATE_IN_SETUP
|
|
&& call->state != ISDN_STATE_IN_OVERLAP
|
|
&& call->state != ISDN_STATE_IN_PROCEEDING
|
|
&& call->state != ISDN_STATE_IN_ALERTING
|
|
&& call->state != ISDN_STATE_IN_CONNECTING
|
|
&& call->state != ISDN_STATE_OUT_OVERLAP
|
|
&& call->state != ISDN_STATE_OUT_PROCEEDING
|
|
&& call->state != ISDN_STATE_OUT_ALERTING
|
|
&& call->state != ISDN_STATE_OUT_CONNECTING
|
|
&& call->state != ISDN_STATE_CONNECT
|
|
&& call->state != ISDN_STATE_IN_DISCONNECT)
|
|
break;
|
|
disc_req(call, call->l3_pid, msg);
|
|
break;
|
|
case OSMO_CC_MSG_REL_REQ: /* release isdn port */
|
|
if (call->state == ISDN_STATE_SUSPENDED) {
|
|
disc_rel_suspended(call, msg);
|
|
break;
|
|
}
|
|
rel_req(call, call->l3_pid, msg);
|
|
break;
|
|
default:
|
|
PDEBUG(DDSS1, DEBUG_ERROR, "received an unsupported CC message: %d\n", msg->type);
|
|
}
|
|
|
|
osmo_cc_free_msg(msg);
|
|
}
|
|
|