#warning warum kommt kein ton, wenn ich bei watson ins timeout komme?: /* layer3 handling * * (C) 2020 by Andreas Eversberg * 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 . */ #include #include #include #include #include #include #include #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 #include #include 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, ¬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) 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, ¬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) 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, ¬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; } /* 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, ¬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); 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); 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(¤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); } 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, ¬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); } } /* 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); }