osmo-cc-misdn-endpoint/src/isdn/dss1.c

2417 lines
66 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 "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_alaw[] = {
{ "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_ulaw[] = {
{ "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 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_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;
/* 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);
}
}
/*
* 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;
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 FIXME: clearmode */
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);
/* set bchannel mode */
if (capability==OSMO_CC_CAPABILITY_DATA
|| capability==OSMO_CC_CAPABILITY_DATA_RESTRICTED
|| capability==OSMO_CC_CAPABILITY_VIDEO)
call->b_mode = B_MODE_HDLC;
}
/* sdp offer */
call->cc_session = osmo_cc_helper_audio_offer(&call->isdn_ep->cc_ep.session_config, call, (call->isdn_ep->law == 'a') ? codecs_alaw : codecs_ulaw, bchannel_send, 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;
/* 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);
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)
osmo_cc_add_ie_sdp(msg, call->sdp);
}
/* 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, &notify);
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)
osmo_cc_add_ie_sdp(msg, call->sdp);
}
/* 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, &notify);
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)
osmo_cc_add_ie_sdp(msg, call->sdp);
}
/* 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)
osmo_cc_add_ie_sdp(msg, call->sdp);
}
/* 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;
uint8_t coding, proglocation, progress;
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);
/* progress indicator */
rc = dec_ie_progress(l3m, &coding, &proglocation, &progress);
if (rc >= 0)
osmo_cc_add_ie_progress(msg, coding, proglocation, progress);
else {
coding = OSMO_CC_CODING_ITU_T;
progress = 0;
}
/* 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 SDP answer */
if (coding == OSMO_CC_CODING_ITU_T && (progress == 1 || progress == 8) && !call->codec_negotiated) {
call->codec_negotiated = 1;
if (call->sdp)
osmo_cc_add_ie_sdp(msg, call->sdp);
}
/* the audio path is throughconnected */
if (coding == OSMO_CC_CODING_ITU_T && (progress == 1 || progress == 8))
call->audio_path = 1;
else
call->audio_path = 0;
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);
}
/* 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 2 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, &notify);
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;
}
/* 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;
#warning use clock to send empty packets/holdmusic
/* 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;
/* set hold state */
call->hold = 0;
/* 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;
}
/* 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);
#warning use clock to send empty packets/holdmusic
/* send message to osmo-cc */
osmo_cc_ll_msg(&call->isdn_ep->cc_ep, call->cc_callref, msg);
/* deactivate bchannel */
drop_bchannel(call);
/* 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) {
rc = -85;
no_channel:
PDEBUG(DDSS1, DEBUG_INFO, "RESUME-REJECT REQUEST (pid = 0x%x)\n", pid);
l3m = create_l3msg();
enc_ie_cause(l3m, call->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;
/* 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);
/* 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;
jitter_clear(&call->dejitter);
break;
case Fac_End3PTY:
notify = OSMO_CC_NOTIFY_CONFERENCE_DISCONNECTED;
invokeid = fac.u.inv.invokeId;
set_3pty = 0;
break;
default:
;
}
break;
default:
;
}
/* 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 (set_3pty >= 0 && other) {
other->conference_3pty = call->conference_3pty = set_3pty;
jitter_clear(&other->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)
osmo_cc_add_ie_sdp(msg, call->sdp);
}
/* 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;
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, 0, 0, B_MODE_TRANSPARENT);
if (!call) {
PDEBUG(DDSS1, DEBUG_ERROR, "Cannot create calll 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;
PDEBUG(DDSS1, DEBUG_INFO, "SETUP REQUEST\n");
/* sdp accept, force our preferred codec */
sdp = osmo_cc_helper_audio_accept(&call->isdn_ep->cc_ep.session_config, call, (call->isdn_ep->law == 'a') ? codecs_alaw : codecs_ulaw, bchannel_send, msg, &call->cc_session, &call->codec, 1);
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 FIXME: clearmode */
rc = osmo_cc_get_ie_bearer(msg, 0, &coding, &capability, &mode);
if (rc < 0) {
coding = OSMO_CC_CODING_ITU_T;
capability = OSMO_CC_CAPABILITY_AUDIO;
mode = OSMO_CC_MODE_CIRCUIT;
}
rate = (mode == OSMO_CC_MODE_PACKET) ? 0x00 : 0x10;
if (mode == OSMO_CC_MODE_CIRCUIT && call->isdn_ep->law == 'u') {
has_user = 1;
user = 2;
}
if (mode == OSMO_CC_MODE_CIRCUIT && call->isdn_ep->law == 'a') {
has_user = 1;
user = 3;
}
/* set bchannel mode */
if (capability==OSMO_CC_CAPABILITY_DATA
|| capability==OSMO_CC_CAPABILITY_DATA_RESTRICTED
|| capability==OSMO_CC_CAPABILITY_VIDEO)
call->b_mode = B_MODE_HDLC;
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);
}
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, &notify);
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);
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, &notify);
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);
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;
/* 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(&current_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);
}
new_state(call, ISDN_STATE_IN_CONNECTING);
/* 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, &notify);
/* 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);
}
}
/* 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);
}
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);
}
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, 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_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 */
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);
}