From c2a14e09be1ad02e26223fd5b984b91d00822b7b Mon Sep 17 00:00:00 2001 From: Wenpeng Song <wenpeng.song@iopsys.eu> Date: Fri, 17 Feb 2023 09:18:43 +0000 Subject: [PATCH] Move chan_brcm out of Asterisk A separate package asterisk-chan-voicemngr has been added to replace chan_brcm. --- channels/Makefile | 1 - channels/chan_brcm.c | 5169 ------------------------------------ channels/chan_brcm.h | 286 -- configs/brcm.conf.sample | 50 - include/asterisk/cdr.h | 2 +- include/asterisk/channel.h | 2 +- 6 files changed, 2 insertions(+), 5508 deletions(-) delete mode 100644 channels/chan_brcm.c delete mode 100644 channels/chan_brcm.h delete mode 100644 configs/brcm.conf.sample diff --git a/channels/Makefile b/channels/Makefile index 5558ee546a..b641173fe8 100644 --- a/channels/Makefile +++ b/channels/Makefile @@ -36,7 +36,6 @@ chan_mgcp.o: _ASTCFLAGS+=$(AST_NO_FORMAT_TRUNCATION) chan_unistim.o: _ASTCFLAGS+=$(AST_NO_FORMAT_TRUNCATION) chan_phone.o: _ASTCFLAGS+=$(AST_NO_FORMAT_TRUNCATION) chan_sip.o: _ASTCFLAGS+=$(AST_NO_FORMAT_TRUNCATION) -chan_brcm.so: LIBS+=-lubus -lubox -lpicoevent chan_misdn.o: _ASTCFLAGS+=-Imisdn misdn_config.o: _ASTCFLAGS+=-Imisdn diff --git a/channels/chan_brcm.c b/channels/chan_brcm.c deleted file mode 100644 index eeaf7ee6c0..0000000000 --- a/channels/chan_brcm.c +++ /dev/null @@ -1,5169 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * Mark Spencer <markster@digium.com> - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*! \file - * - * \author Benjamin Larsson <benjamin@southpole.se> - * \author Jonas Höglund <jonash@southpole.se> - * \author Yalu Zhang <yalu.zhang@iopsys.eu> - * - * \ingroup channel_drivers - */ - -// #define BRCM_LOCK_DEBUG /* If defined we will log lock events to the asterisk debug channel */ - -#include "asterisk.h" - -#include <math.h> -#include <ctype.h> -#include <sys/socket.h> -#include <sys/time.h> -#include <arpa/inet.h> -#include <fcntl.h> -#include <sys/ioctl.h> -#include <signal.h> -#include <semaphore.h> -#include <sys/types.h> -#include <sys/stat.h> // mkfifo() -#include "asterisk/lock.h" -#include "asterisk/channel.h" -#include "asterisk/options.h" -#include "asterisk/cli.h" -#include "asterisk/config.h" -#include "asterisk/module.h" -#include "asterisk/pbx.h" -#include "asterisk/utils.h" -#include "asterisk/callerid.h" -#include "asterisk/causes.h" -#include "asterisk/stringfields.h" -#include "asterisk/musiconhold.h" -#include "asterisk/indications.h" -#include "asterisk/sched.h" -#include "asterisk/app.h" -#include "asterisk/format_cap.h" -#include "asterisk/format_compatibility.h" -#include "asterisk/format_cache.h" -#include "asterisk/logger.h" -#include "asterisk/bridge.h" -#include "asterisk/stasis_system.h" -#include "asterisk/stasis_channels.h" - -#include <libubus.h> -#include <libpicoevent.h> - -#include "chan_brcm.h" -#ifndef AST_MODULE -#define AST_MODULE "chan_brcm" -#endif - -static void brcm_dialtone_set(struct brcm_pvt *p, dialtone_state state); -static int brcm_signal_dialtone(struct brcm_pvt *p); -static int brcm_in_conference(const struct brcm_pvt *p); -static int cwtimeout_cb(const void *data); -static int cwbeep_cb(const void *data); -static int r4hanguptimeout_cb(const void *data); -static void brcm_generate_rtp_packet(struct brcm_subchannel *p, uint8_t *packet_buf, int type, int marker, int dtmf_timestamp, int seqno); -static void brcm_interpret_rtcp_packet(struct brcm_subchannel *p, uint8_t *rtcp_frame, uint32_t rtcp_size); -static int brcm_mute_connection(struct brcm_subchannel *p); -static int brcm_unmute_connection(struct brcm_subchannel *p); -static int brcm_close_connection(struct brcm_subchannel *p); -static int brcm_create_conference(struct brcm_pvt *p); -static int brcm_stop_conference(struct brcm_subchannel *p); -static void brcm_attended_call_transfer(struct brcm_subchannel *sub, struct brcm_subchannel *sub_peer, - struct ast_channel *owner, struct ast_channel *peer_owner); -static void brcm_unattended_call_transfer(struct brcm_subchannel *sub, struct brcm_subchannel *sub_peer, - struct ast_channel *owner, struct ast_channel *peer_owner); -static int brcm_finish_transfer(struct ast_channel *owner, struct brcm_subchannel *p, int result); -static int brcm_call(struct ast_channel *ast, const char *dest, int timeout); -static int brcm_devicestate(const char *device_number); -static int brcm_hangup(struct ast_channel *ast); -static int brcm_getRtpStats(struct ast_channel *ast); -static int brcm_answer(struct ast_channel *ast); -static struct ast_frame *brcm_read(struct ast_channel *ast); -static int brcm_write(struct ast_channel *ast, struct ast_frame *frame); -static int brcm_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen); -static int brcm_transfer(struct ast_channel *ast, const char *newdest); -static int brcm_senddigit_begin(struct ast_channel *ast, char digit); -static int brcm_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration); -static int brcm_in_call(const struct brcm_pvt *p); -static int brcm_in_callwaiting(const struct brcm_pvt *p); -static int brcm_in_onhold(const struct brcm_pvt *p); -static int brcm_subchannel_is_idle(const struct brcm_subchannel *sub); -static struct ast_channel *brcm_request(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *dest, int *cause); -static void brcm_lock_pvts(void); -static void brcm_unlock_pvts(void); -static struct brcm_pvt* brcm_get_next_pvt(struct brcm_pvt *p); -static void *pe_base_run(void *unused); -static int brcm_create_connection(struct brcm_subchannel *p); -static int brcm_stop_dialtone(struct brcm_pvt *p); -static void brcm_signal_howler(struct brcm_pvt *p); -static int brcm_signal_ringing(struct brcm_pvt *p); -static int brcm_stop_ringing(struct brcm_pvt *p); -static int brcm_signal_ringing_callerid_pending(struct brcm_pvt *p); -static int brcm_stop_ringing_callerid_pending(struct brcm_pvt *p); -static int brcm_signal_callwaiting(const struct brcm_pvt *p); -static int brcm_stop_callwaiting(const struct brcm_pvt *p); -static int brcm_signal_callerid(struct ast_channel *chan, struct brcm_subchannel *sub); -static struct brcm_subchannel *brcm_get_idle_subchannel(const struct brcm_pvt *p); -static struct brcm_subchannel* brcm_get_active_subchannel(const struct brcm_pvt *p); -static struct brcm_subchannel *brcm_subchannel_get_peer(const struct brcm_subchannel *sub); -static struct brcm_pvt* brcm_get_pvt_from_lineid(struct brcm_pvt *p, int line_id); -static void handle_dtmf_calling(struct brcm_subchannel *sub); -static void brcm_cancel_dialing_timeouts(struct brcm_pvt *p); -static int brcm_should_relay_dtmf(const struct brcm_subchannel *sub); -static struct ast_channel *brcm_new(struct brcm_subchannel *subchan, int state, const char *ext, const char *context, - const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, - struct ast_format_cap *format); -static int handle_dialtone_timeout(const void *data); -static int endpt_get_rtp_stats(int line); -static int is_call_waiting_enabled(const char *sip_account); -static int has_call_in_sip_client(const char *sip_account); - -/* Global brcm channel parameters */ -static const char tdesc[] = "Broadcom SLIC Driver"; -static const char config[] = "chan_telephony.conf"; -static const char broadcast_path[] = "voice.line"; -static channel_settings channel_config[MAX_NUM_LINEID]; -static int current_connection_id = 0; -static int num_fxs_endpoints = -1; -static int num_fxo_endpoints = -1; -static int num_dect_endpoints = -1; -static int num_endpoints = -1; - -/* driver scheduler */ -static struct ast_sched_context *sched = NULL; - -/* Call waiting */ -static int cwtimeout = DEFAULT_CALL_WAITING_TIMEOUT; -static int cwBeep = DEFAULT_CALL_WAITING_BEEP_FREQ; - -/* R4 transfer */ -static int r4hanguptimeout = DEFAULT_R4_HANGUP_TIMEOUT; - -/* Automatic call on hold hangup */ -static int onholdhanguptimeout = DEFAULT_ONHOLD_HANGUP_TIMEOUT; - -/* Max call count per extension */ -#define DEFAULT_MAX_SESSION_PER_EXTENSION 2 -static int max_sessions_per_extension = DEFAULT_MAX_SESSION_PER_EXTENSION; - -/* Boolean, controls whether the transferor puts the transfer target on-hold before sending - * REFER to the transferee */ -static int hold_target_before_refer = 1; - -/* Global jitter buffer configuration */ -static struct ast_jb_conf global_jbconf; - -//TODO change AST_MAX_EXTENSION to something shorter -/* Structure for feature access codes */ -struct feature_access_code { - AST_LIST_ENTRY(feature_access_code) list; - char code[AST_MAX_EXTENSION]; -}; - -/* List of configured feature access codes */ -static AST_LIST_HEAD_NOLOCK_STATIC(feature_access_codes, feature_access_code); - -/* Format a string of feature access codes */ -static const char *feature_access_code_string(char *buffer, unsigned int buffer_length); - -/* Add FAC to list */ -static int feature_access_code_add(const char *code); - -/* Clear list of FAC */ -static int feature_access_code_clear(void); - -/* Match dialed digits against feature access codes */ -static int feature_access_code_match(char *sequence); - -/* Send dialed DTMF codes during call */ -static void send_outgoing_dtmf(struct ast_channel *owner, char dtmf_button, int frametype); - -static pthread_t ubus_thread = AST_PTHREADT_NULL; - -static struct ubus_context *ctx; -static struct ubus_event_handler ObjAddListener; // Event handler for new ubus object events -static struct ubus_event_handler ObjRmListener; // Event handler for removed ubus object events - -static int base; -static volatile uint32_t endpt_id = 0; -static const char endpt_ubus_path[] = "endpt"; -static const char audio_rx_str[] = "/tmp/endpt_audio_tx"; -static const char audio_tx_str[] = "/tmp/endpt_audio_rx"; -static const char ubusStrObjAdd[] = "ubus.object.add"; // UBUS objects added to global context -static const char ubusStrObjRm[] = "ubus.object.remove"; // UBUS objects added removed from global context -static int audio_rx_fd = 0, audio_tx_fd = 0; -static pe_bus_t *audio_rx_bus, *audio_tx_bus; -static pe_stream_t *audio_rx_stream; -static int mwi_dialtone_state = DIALTONE_ON; -static char lineCallStatus[16]; -int dtmf_wait_timer = 2000; /*Time to wait for dtmf(for R4 call setup) before setting up a conference call*/ -#define RTP_HEADER_SIZE 12 -#define RTP_DTMF_SIZE 4 - -enum { - EVENT_TYPE, - EVENT_LINE_ID, - __EVENT_MAX, -}; - -enum { - CALL_STATUS_EXTENSION_ID, - CALL_STATUS_LINE_ID, - __CALL_STATUS_MAX, -}; - -enum { // New ubus objects in global context - OBJ_ID, // UBUS ID of object - OBJ_PATH, // String path of object -}; - -enum { - STATE_OFF = 0, - STATE_ON, - STATE_LAST, -}; - -struct endpt_event { - const char *name; - enum LINE_EVENT event; - int line; -}; - -typedef struct __attribute__((__packed__)) { - int32_t line; - int32_t cnx_id; - uint32_t rtp_size; - uint8_t rtp[1]; -} audio_packet_t; - -static struct endpt_event event_map[] = { - { .name = "DTMF0", .event = EVENT_DTMF0 }, - { .name = "DTMF1", .event = EVENT_DTMF1 }, - { .name = "DTMF2", .event = EVENT_DTMF2 }, - { .name = "DTMF3", .event = EVENT_DTMF3 }, - { .name = "DTMF4", .event = EVENT_DTMF4 }, - { .name = "DTMF5", .event = EVENT_DTMF5 }, - { .name = "DTMF6", .event = EVENT_DTMF6 }, - { .name = "DTMF7", .event = EVENT_DTMF7 }, - { .name = "DTMF8", .event = EVENT_DTMF8 }, - { .name = "DTMF9", .event = EVENT_DTMF9 }, - { .name = "DTMFA", .event = EVENT_DTMFA }, - { .name = "DTMFB", .event = EVENT_DTMFB }, - { .name = "DTMFC", .event = EVENT_DTMFC }, - { .name = "DTMFD", .event = EVENT_DTMFD }, - { .name = "DTMFS", .event = EVENT_DTMFS }, - { .name = "DTMFH", .event = EVENT_DTMFH }, - { .name = "OFFHOOK", .event = EVENT_OFFHOOK }, - { .name = "ONHOOK", .event = EVENT_ONHOOK }, - { .name = "FLASH", .event = EVENT_FLASH }, - { .name = "CALL_REJECT", .event = EVENT_CALL_REJECT }, - { .name = "DECT_UNAVAILABLE", .event = EVENT_DECT_UNAVAILABLE }, - { .name = "SWITCH", .event = EVENT_SWITCH }, - { .name = "JOIN", .event = EVENT_JOIN }, - { .name = "RELEASE", .event = EVENT_RELEASE }, - { .name = "", .event = EVENT_LAST }, -}; - -static const struct blobmsg_policy asterisk_event_policy[] = { - [EVENT_TYPE] = { .name = "event", .type = BLOBMSG_TYPE_STRING }, - [EVENT_LINE_ID] = { .name = "line", .type = BLOBMSG_TYPE_INT32 }, -}; - -static const struct blobmsg_policy asterisk_call_status_policy[] = { - [CALL_STATUS_EXTENSION_ID] = { .name = "extension", .type = BLOBMSG_TYPE_INT32 }, - [CALL_STATUS_LINE_ID] = { .name = "line", .type = BLOBMSG_TYPE_INT32 }, -}; - -static const struct blobmsg_policy new_obj_policy[] = { - [OBJ_ID] = { .name = "id", .type = BLOBMSG_TYPE_INT32 }, // UBUS ID of object - [OBJ_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING }, // String path of object -}; - -static struct ast_channel_tech *cur_tech; - -static struct stasis_subscription **registration_change_sub = NULL; /*!< subscription id for SIP registration change events for every line*/ - -static const DTMF_CHARNAME_MAP dtmf_to_charname[] = -{ - {EVENT_DTMF0, "DTMF0", '0', 0}, - {EVENT_DTMF1, "DTMF1", '1', 1}, - {EVENT_DTMF2, "DTMF2", '2', 2}, - {EVENT_DTMF3, "DTMF3", '3', 3}, - {EVENT_DTMF4, "DTMF4", '4', 4}, - {EVENT_DTMF5, "DTMF5", '5', 5}, - {EVENT_DTMF6, "DTMF6", '6', 6}, - {EVENT_DTMF7, "DTMF7", '7', 7}, - {EVENT_DTMF8, "DTMF8", '8', 8}, - {EVENT_DTMF9, "DTMF9", '9', 9}, - {EVENT_DTMFA, "DTMFA", 'A', 12}, - {EVENT_DTMFB, "DTMFB", 'B', 13}, - {EVENT_DTMFC, "DTMFC", 'C', 14}, - {EVENT_DTMFD, "DTMFD", 'D', 15}, - {EVENT_DTMFH, "DTMFH", 0x23, 11}, //# - {EVENT_DTMFS, "DTMFS", 0x2A, 10}, //* - {EVENT_LAST, "", '-', -1} -}; - -/* Linked list of pvt:s */ -static struct brcm_pvt *iflist; - -/* Protect the interface list (of brcm_pvt's) */ -AST_MUTEX_DEFINE_STATIC(iflock); - -/* Protect the monitoring thread, so only one process can kill or start it, and not - when it's doing something critical. */ -AST_MUTEX_DEFINE_STATIC(monlock); - -static struct ast_format_cap *default_cap; - -static int load_common_settings(struct ast_config **cfg); -static void load_settings(struct ast_config *cfg); -static char *state2str(enum brcm_channel_state state); - -/* exported capabilities */ -static struct ast_channel_tech brcm_tech = { - .type = "TELCHAN", - .description = tdesc, - .requester = brcm_request, //No lock held (no channel yet) - .call = brcm_call, //Channel is locked - .hangup = brcm_hangup, //Channel is locked - .answer = brcm_answer, //Channel is locked - .read = brcm_read, //Channel is locked - .write = brcm_write, //Channel is locked - .send_digit_begin = brcm_senddigit_begin, //Channel is NOT locked - .getRtpStats = brcm_getRtpStats, //Channel is NOT locked - .send_digit_end = brcm_senddigit_end, //Channel is NOT locked - .indicate = brcm_indicate, //Channel is locked - .transfer = brcm_transfer, // Channel is locked - .devicestate = brcm_devicestate // Channel is NOT locked -}; - -static struct brcm_channel_tech fxs_tech = { - .signal_ringing = brcm_signal_ringing, - .signal_ringing_callerid_pending = brcm_signal_ringing_callerid_pending, - .signal_callerid = brcm_signal_callerid, - .stop_ringing = brcm_stop_ringing, - .stop_ringing_callerid_pending = brcm_stop_ringing_callerid_pending, - .release = NULL, -}; - -/* Tries to lock 10 timees, then gives up */ -/* static int pvt_trylock(struct brcm_pvt *pvt, const char *reason) */ -/* { */ -/* int i = 10; */ -/* while (i--) { */ -/* if (!ast_mutex_trylock(&pvt->lock)) { */ -/* ast_debug(9, "----> Successfully locked pvt port %d - reason %s\n", pvt->line_id, reason); */ -/* return 1; */ -/* } */ -/* } */ -/* ast_debug(9, "----> Failed to lock port %d - %s\n", pvt->line_id, reason); */ -/* return 0; */ -/* } */ - -#ifdef BRCM_LOCK_DEBUG -static int pvt_lock(struct brcm_pvt *pvt, const char *reason) -{ - ast_debug(9, "----> Trying to lock port %d - %s\n", pvt->line_id, reason); - ast_mutex_lock(&pvt->lock); - ast_debug(9, "----> Locked pvt port %d - reason %s\n", pvt->line_id, reason); - return 1; -} - - -static int pvt_lock_silent(struct brcm_pvt *pvt) -{ - ast_mutex_lock(&pvt->lock); - return 1; -} - - -static int pvt_unlock(struct brcm_pvt *pvt) -{ - ast_mutex_unlock(&pvt->lock); - ast_debug(10, "----> Unlocking pvt port %d\n", pvt->line_id); - return 1; -} - -static int pvt_unlock_silent(struct brcm_pvt *pvt) -{ - ast_mutex_unlock(&pvt->lock); - return 1; -} - -#else -#define pvt_lock(pvt, reason) ast_mutex_lock(&(pvt)->lock) -#define pvt_lock_silent(pvt) ast_mutex_lock(&(pvt)->lock) -#define pvt_unlock(pvt) ast_mutex_unlock(&(pvt)->lock) -#define pvt_unlock_silent(pvt) ast_mutex_unlock(&(pvt)->lock) -#endif - -// Get call_id state based on the call_id value -static callid_state get_callid_state(int call_id) { - if (call_id <= CALLID_INVALID) - return CALLID_INVALID; - else if (call_id == CALLID_OBTAINING) - return CALLID_OBTAINING; - else - return CALLID_ESTABLISHED; -} - -// Get the endptmngr ubus address. It's dynamically updated -// by another thread without locking. -static uint32_t get_ubus_endpt_id(int log) { - if (!endpt_id) { - if (ubus_lookup_id(ctx, endpt_ubus_path, &endpt_id) != UBUS_STATUS_OK) { - if (log) - ast_log(LOG_ERROR, "Failed to look up endptmngr's UBUS path\n"); - return 0; - } - } - - return endpt_id; -} - -// Callback for: a ubus call (invocation) has replied with some data -static void ubus_call_answer(struct ubus_request *req, int type, struct blob_attr *msg) -{ - ast_verbose("%s() from thread %d\n", __func__, ast_get_tid()); -} - -// Callback for: a ubus call (invocation) has finished -static void ubus_call_complete(struct ubus_request *req, int ret) -{ - ast_verbose("%s() from thread %d\n", __func__, ast_get_tid()); - free(req); -} - -static void endpt_signal(int line, char *signal, char *state, char *data) { - struct blob_buf bb; - struct ubus_request *req; - int res; - - //ast_debug(5, "line = %d, signal = %s, state = %s, data = %s\n", line, signal, state, data); - - if (endpt_id) { - memset(&bb, 0, sizeof(bb)); - blob_buf_init(&bb, 0); - - blobmsg_add_u32(&bb, "line", line); - blobmsg_add_string(&bb, "signal", signal); - blobmsg_add_string(&bb, "state", state); - if (data) - blobmsg_add_string(&bb, "data", data); - - req = calloc(1, sizeof(struct ubus_request)); - if (!req) return; - - ast_verbose("thread %d: ubus call endpt signal\n", ast_get_tid()); - res = ubus_invoke_async(ctx, endpt_id, "signal", bb.head, req); - - if(res != UBUS_STATUS_OK) { - ast_log(LOG_ERROR, "Error invoking method: %s %d\n", "signal", res); - free(req); - return; - } - - req->data_cb = ubus_call_answer; - req->complete_cb = ubus_call_complete; - req->priv = NULL; - ubus_complete_request_async(ctx, req); - } -} - -static int brcm_send_ubus_event(char *ev_name, int line) -{ - struct blob_buf blob; - struct ubus_context *ubusctx; - int res = 0; - - ubusctx = ubus_connect(NULL); - if (!ubusctx) { - return -1; - } - - memset(&blob, 0, sizeof(blob)); - if(blob_buf_init(&blob, 0)) { - ubus_free(ubusctx); - return -1; - } - - blobmsg_add_u32(&blob, "id", line); - blobmsg_add_string(&blob, "event", ev_name); - - if (ubus_send_event(ubusctx, broadcast_path, blob.head) != UBUS_STATUS_OK) { - ast_log(LOG_NOTICE,"Error sending ubus message %s\n", ev_name); - res = -1; - } - - ubus_free(ubusctx); - blob_buf_free(&blob); - - return res; -} - -static void endpt_connection(int line, int id, char *action) { - struct blob_buf bb; - struct ubus_request *req; - int res; - - if (endpt_id) { - memset(&bb, 0, sizeof(bb)); - blob_buf_init(&bb, 0); - - blobmsg_add_u32(&bb, "line", line); - blobmsg_add_u32(&bb, "id", id); - blobmsg_add_string(&bb, "action", action); - - req = calloc(1, sizeof(struct ubus_request)); - if (!req) return; - ast_verbose("thread %d: ubus call endpt connection\n", ast_get_tid()); - res = ubus_invoke_async(ctx, endpt_id, "connection", bb.head, req); - - if(res != UBUS_STATUS_OK) { - ast_log(LOG_ERROR, "Error invoking method: %s %d\n", "connection", res); - free(req); - return; - } - - req->data_cb = ubus_call_answer; - req->complete_cb = ubus_call_complete; - req->priv = NULL; - ubus_complete_request_async(ctx, req); - } -} - -static int brcm_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen) -{ - struct brcm_subchannel *sub = ast_channel_tech_pvt(ast); - struct ast_bridge_channel *play_bridge_channel; - struct ast_bridge *myBridge; - struct ast_frame astFrame; - int res = 0; - - ast_debug(2, "Channel %s gets an indication, condition = %d\n", ast_channel_name(ast), condition); - - pvt_lock(sub->parent, "indicate"); - //ast_mutex_lock(&sub->parent->lock); - switch(condition) { - case AST_CONTROL_UNHOLD: - brcm_stop_dialtone(sub->parent); - - // Play a beep when unholding. - ast_channel_lock(ast); - play_bridge_channel = ast_channel_get_bridge_channel(ast); - ast_channel_unlock(ast); - ast_bridge_channel_queue_playfile(play_bridge_channel, NULL, "beep", NULL); - - sub->channel_state = INCALL; - - // Tell all participants to re-sync RTP stream. - myBridge = ast_channel_internal_bridge(ast); - memset(&astFrame, 0, sizeof astFrame); - astFrame.frametype = AST_FRAME_CONTROL; - astFrame.subclass.integer = AST_CONTROL_SRCUPDATE; - ast_bridge_queue_everyone_else(myBridge, NULL, &astFrame); - break; - - case AST_CONTROL_UPDATE_RTP_PEER: - case AST_CONTROL_SRCUPDATE: - case AST_CONTROL_SRCCHANGE: - sub->codec = -1; - if (sub->channel_state == RINGBACK) - endpt_signal(sub->parent->line_id, "ringback", "off", NULL); - break; - case AST_CONTROL_RINGING: - ast_debug(4, "Got AST_CONTROL_RINGING on %s, sub->codec = %d\n", ast_channel_name(ast), sub->codec); - sub->channel_state = RINGBACK; - // Play local ringback tone only if there is no incoming media packet - if (sub->codec == -1) - endpt_signal(sub->parent->line_id, "ringback", "on", NULL); - if (sub->owner && (get_callid_state(sub->call_id) != CALLID_ESTABLISHED)) { - sub->call_id = ast_channel_callid(sub->owner); - endpt_connection(sub->parent->line_id, sub->call_id, "update"); - } - break; - case AST_CONTROL_UNHOLD_FOR_TRANSFER: - if (sub->channel_state == TRANSFERING) { - struct ast_channel *bridged_chan = ast_channel_bridge_peer(sub->owner); - if (bridged_chan) { - ast_debug(4, "Start unattended transfer to [%s] on channel %s\n", sub->blind_xfer_target, ast_channel_name(bridged_chan)); - ast_channel_lock(bridged_chan); - if (!ast_test_flag(ast_channel_flags(bridged_chan), AST_FLAG_ZOMBIE) && - !ast_check_hangup(bridged_chan)) { - if (ast_channel_tech(bridged_chan)->transfer) { - res = ast_channel_tech(bridged_chan)->transfer(bridged_chan, (const char *)sub->blind_xfer_target); - if (res != 0) - ast_log(LOG_ERROR, "ast_transfer() failed\n"); - } else - ast_log(LOG_ERROR, "ast_transfer() is not supported on the peer channel\n"); - } - ast_channel_unlock(bridged_chan); - } else { - ast_log(LOG_ERROR, "can't get the peer channel, unattended call transfer will not be proceeded\n"); - } - - } - break; - case AST_CONTROL_TRANSFER: - res = -1; - if (datalen != sizeof(enum ast_control_transfer)) { - ast_log(LOG_ERROR, "Invalid datalen for AST_CONTROL_TRANSFER. Expected %d, got %d\n", (int) sizeof(enum ast_control_transfer), (int) datalen); - } else { - enum ast_control_transfer *message = (enum ast_control_transfer *) data; - struct brcm_subchannel* peer_sub; - peer_sub = brcm_subchannel_get_peer(sub); - if (peer_sub->channel_state == TRANSFERING) - brcm_finish_transfer(ast, peer_sub, *message); - else - brcm_finish_transfer(ast, sub, *message); - } - break; - case AST_CONTROL_CONGESTION: - ast_debug(4, "Got CONGESTION on %s\n", ast_channel_name(ast)); - /* The other end is out of network resources */ - if (ast_channel_state(ast) != AST_STATE_UP) { - /* If state is UP, we can't do anything */ - ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV); - brcm_hangup(ast); - break; - } - res = -1; - break; - case AST_CONTROL_CONNECTED_LINE: - res = -1; - ast_debug(4, "Got CONNECTED LINE UPDATE on %s\n", ast_channel_name(ast)); - /* Update caller IDs on display - dect ? */ - struct brcm_subchannel *sub_peer = brcm_subchannel_get_peer(sub); - if ((sub->connection_id != -1) && (sub_peer->connection_id != -1)) { - struct ast_bridge *bridge = ast_channel_internal_bridge(sub->owner); - struct ast_bridge_channel *bridge_channel; - if(bridge){ - // Find out which end party of call conference hangs up - AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { - if (strstr(ast_channel_name(bridge_channel->chan), "PJSIP") != NULL) { - if (sub->call_id == ast_channel_callid(bridge_channel->chan)) { - endpt_connection(sub_peer->parent->line_id, sub_peer->call_id, "release"); - sub_peer->channel_state = ONHOOK; - sub_peer->call_id = CALLID_INVALID; - } - else if(sub_peer->call_id == ast_channel_callid(bridge_channel->chan)) { - endpt_connection(sub->parent->line_id, sub->call_id, "release"); - sub->channel_state = ONHOOK; - sub->call_id = CALLID_INVALID; - } - } - } - sub->conference_initiator = 0; - free(sub->conference_id); - sub->conference_id = NULL; - - res = 0; - } - } - if (sub->owner && (get_callid_state(sub->call_id) != CALLID_ESTABLISHED)) { - sub->call_id = ast_channel_callid(sub->owner); - endpt_connection(sub->parent->line_id, sub->call_id, "update"); - res = 0; - } - break; - - case AST_CONTROL_BUSY: - ast_debug(4, "Got BUSY on %s\n", ast_channel_name(ast)); - /* The other end is busy */ - if (ast_channel_state(ast) != AST_STATE_UP) { - /* XXX We should play a busy tone here!! */ - ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV); - brcm_hangup(ast); - break; - } - res = -1; - break; - case AST_CONTROL_PROGRESS: - ast_debug(4, "Got AST_CONTROL_PROGRESS on %s\n", ast_channel_name(ast)); - /* Early media is coming our way */ - /* What do we do with that? */ - res = -1; - break; - case AST_CONTROL_NORMAL_DIALTONE: - brcm_dialtone_set(sub->parent, DIALTONE_ON); - break; - case AST_CONTROL_SPECIAL_DIALTONE: - brcm_dialtone_set(sub->parent, DIALTONE_SPECIAL_CONDITION); - break; - case AST_CONTROL_PVT_CAUSE_CODE: { - const struct ast_control_pvt_cause_code *cause_code = data; - int ast_cause = cause_code->ast_cause; - ast_debug(2, "AST_CONTROL_PVT_CAUSE_CODE = %d, chan_name = %s\n", ast_cause, cause_code->chan_name); - - switch (ast_cause) { - case AST_CAUSE_NO_USER_RESPONSE: - case AST_CAUSE_NO_ANSWER: - case AST_CAUSE_CALL_REJECTED: - endpt_signal(sub->parent->line_id, "ringback", "off", NULL); - if (ast_channel_state(ast) != AST_STATE_UP) { - endpt_signal(sub->parent->line_id, "congestion", "on", NULL); - strncpy(sub->parent->extensionCallStatus, "Disconnected", CALL_STATUS_MAX_LEN); - break; - } - res = -1; - break; - case AST_CAUSE_NORMAL_CLEARING: - // This is just fine. - break; - case AST_CAUSE_USER_BUSY: - endpt_signal(sub->parent->line_id, "ringback", "off", NULL); - if (ast_channel_state(ast) != AST_STATE_UP) { - /* XXX We should play a busy tone here!! */ - sub->channel_state = CALLENDED; - endpt_signal(sub->parent->line_id, "busy", "on", NULL); - strncpy(sub->parent->extensionCallStatus, "Disconnected", CALL_STATUS_MAX_LEN); - break; - } - res = -1; - case AST_CAUSE_NETWORK_OUT_OF_ORDER: - brcm_stop_dialtone(sub->parent); // stop any dialtone exist if disconnection happened due to network/server - break; - default: - ast_debug(1, "Don't know how to handle cause code %d\n", ast_cause); - break; - } - } - break; - default: - res = -1; - ast_debug(1, "Don't know how to indicate condition %d\n", condition); - break; - } - pvt_unlock(sub->parent); - return res; -} - -static int brcm_getRtpStats(struct ast_channel *ast) -{ - struct brcm_subchannel *sub; - - sub = ast_channel_tech_pvt(ast); - if (!sub) { - ast_log(LOG_ERROR, "Failed to get peer subchannel\n"); - return -1; - } - - pvt_lock(sub->parent, "brcm_getRtpStats"); - if (sub->parent) { - if (endpt_get_rtp_stats(sub->parent->line_id)) { - ast_log(LOG_WARNING, "Unable to get RTP statistics\n"); - } - ast_channel_rtpStats_set(ast, &sub->rtp_stats); - } - pvt_unlock(sub->parent); - - return 0; -} - -static int brcm_transfer(struct ast_channel *ast, const char *newdest) -{ - struct brcm_pvt *pvt; - struct brcm_subchannel *subchan_active, *subchan_inactive; - struct ast_channel *chan_new; - char ext[strlen(newdest) + 1], *replaces; - - ast_debug(1, "Transfer the existing call to [%s]\n", newdest); - - // Get the other subchannel - subchan_active = ast_channel_tech_pvt(ast); - if (!subchan_active || !(pvt = subchan_active->parent) || - !(subchan_inactive = brcm_subchannel_get_peer(subchan_active)) || - !brcm_subchannel_is_idle(subchan_inactive)) { - return -1; - } - - strncpy(ext, newdest, sizeof(ext)); - replaces = strcasestr(ext, "?Replaces="); - if (replaces) { - *replaces = '\0'; - replaces += strlen("?Replaces="); - } - - if (!subchan_inactive->connection_init) { - subchan_inactive->connection_id = ast_atomic_fetchadd_int((int *)¤t_connection_id, +1); - brcm_create_connection(subchan_inactive); - } - - chan_new = brcm_new(subchan_inactive, AST_STATE_DOWN, ext, pvt->context, NULL, NULL, NULL); - if (!chan_new) { - ast_log(LOG_ERROR, "couldn't create channel\n"); - return -1; - } - - if (replaces) - pbx_builtin_setvar_helper(chan_new, "SIPTRANSFER_REPLACES", replaces); - - ast_setstate(chan_new, AST_STATE_RING); - subchan_inactive->channel_state = TRANSFERING; - - if (ast_pbx_start(chan_new)) { - ast_log(LOG_WARNING, "Unable to start PBX on %s\n", ast_channel_name(chan_new)); - ast_hangup(chan_new); - return -1; - } - - return 0; -} - -static int brcm_finish_transfer(struct ast_channel *owner, struct brcm_subchannel *p, int result) -{ - struct brcm_subchannel* peer_sub; - /* - * We have received the result of a transfer operation. - * This could be: - * - Result of a Transfer-On-Hangup (Remote Transfer), in which case - * we should hangup the subchannel, no matter the result - * - Result of a R4 Attended Transfer (Remote Transfer), in which case - * we should wait for hangup on both subchannels, or resume calls if failed - * Hangup should be received immediately, but we start a timer to hangup - * everything ourselves just to be sure. - * - Probably nothing else - the built-in transfer should never let this - * control frame propagate to here - */ - if (p->channel_state != TRANSFERING) { - ast_log(LOG_WARNING, "Received AST_CONTROL_TRANSFER while in state %s\n", state2str(p->channel_state)); - return -1; - } - - peer_sub = brcm_subchannel_get_peer(p); - if (!peer_sub) { - ast_log(LOG_ERROR, "Failed to get peer subchannel\n"); - return -1; - } - - if (brcm_subchannel_is_idle(peer_sub)) { - // In the case of Transfer-On-Hangup peer sub should be a idle - if (result == AST_TRANSFER_SUCCESS) { - ast_log(LOG_NOTICE, "Remote transfer completed successfully, hanging up\n"); - } - else { - ast_log(LOG_NOTICE, "Remote transfer failed, hanging up\n"); - } - ast_queue_control(owner, AST_CONTROL_HANGUP); - p->channel_state = CALLENDED; - } else if (peer_sub->channel_state == ONHOLD) { - // In the case of R4 transfer peer sub should be on hold - if (result == AST_TRANSFER_SUCCESS) { - ast_log(LOG_NOTICE, "Remote transfer completed successfully, wait for remote hangup\n"); - p->r4_hangup_timer_id = ast_sched_add(sched, r4hanguptimeout, r4hanguptimeout_cb, p); - } else { - //Do nothing. Let calls be up as they were before R4 was attempted (first call on hold, second call active) - ast_log(LOG_NOTICE, "Remote transfer failed\n"); - p->channel_state = INCALL; - } - } else { - ast_log(LOG_WARNING, "AST_CONTROL_TRANSFER received in unexpected state\n"); - return -1; - } - - return 0; -} - -/*! \brief Incoming DTMF begin event */ -static int brcm_senddigit_begin(struct ast_channel *ast, char digit) -{ - struct brcm_subchannel *sub; - char signal[10]; - - //ast_debug(5, "DTMF send begin: %c\n", digit); - - sub = ast_channel_tech_pvt(ast); - pvt_lock(sub->parent, "DTMF senddigit_begin"); - - snprintf(signal, sizeof(signal), "dtmf%c", digit); - endpt_signal(sub->parent->line_id, signal, "on", NULL); - - pvt_unlock(sub->parent); - return 0; -} - -/*! \brief Incoming DTMF end */ -static int brcm_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration) -{ - struct brcm_subchannel *sub; - char signal[10]; - - //ast_debug(5, "DTMF send end: %c, %d ms\n", digit, duration); - - sub = ast_channel_tech_pvt(ast); - pvt_lock(sub->parent, "DTMF senddigit_end"); - - snprintf(signal, sizeof(signal), "dtmf%c", digit); - endpt_signal(sub->parent->line_id, signal, "off", NULL); - - pvt_unlock(sub->parent); - return 0; -} - -/* - * This function is called by _ast_device_state() to get the state of the device - * - * @param[in] device_number the number of device in the format of 0/0, i.e. line_id/connection_id - * - * @return enum ast_device_state - */ -static int brcm_devicestate(const char *device_number) -{ - char *sep = NULL; - int chan_id, connection_id, sub_chan; - struct brcm_pvt *pvt; - - if (!device_number || strlen(device_number) < 3 || (sep = strchr(device_number, '/')) == NULL) { - goto __ret; - } - - chan_id = atoi(device_number); - connection_id = atoi(sep + 1); - if (chan_id < 0 || chan_id >= num_endpoints || connection_id < 0) { - goto __ret; - } - - pvt = brcm_get_pvt_from_lineid(iflist, chan_id); - if (!pvt) { - goto __ret; - } - - for (sub_chan = 0; sub_chan < NUM_SUBCHANNELS; sub_chan++) { - if (pvt->sub[sub_chan]->connection_id == connection_id) - break; - } - if (sub_chan >= NUM_SUBCHANNELS) { - goto __ret; - } - - ast_debug(1, "the state of sub_channel %d/%d is %s(%u)\n", chan_id, sub_chan, - state2str(pvt->sub[sub_chan]->channel_state), pvt->sub[sub_chan]->channel_state); - - switch (pvt->sub[sub_chan]->channel_state) { - case ONHOOK: return AST_DEVICE_NOT_INUSE; - case OFFHOOK: return AST_DEVICE_INUSE; - case DIALING: return AST_DEVICE_INUSE; - case CALLING: return AST_DEVICE_INUSE; - case INCALL: return AST_DEVICE_INUSE; - case ANSWER: return AST_DEVICE_INUSE; - case CALLENDED: return AST_DEVICE_NOT_INUSE; - case RINGING: return AST_DEVICE_RINGINUSE; - case CALLWAITING: return AST_DEVICE_INUSE; - case ONHOLD: return AST_DEVICE_ONHOLD; - case TRANSFERING: return AST_DEVICE_INUSE; - case RINGBACK: return AST_DEVICE_INUSE; - case AWAITONHOOK: return AST_DEVICE_UNAVAILABLE; - case ALLOCATED: return AST_DEVICE_NOT_INUSE; - default: return AST_DEVICE_UNKNOWN; - } - -__ret: - ast_debug(1, "invalid device_number %s\n", device_number); - return AST_DEVICE_UNKNOWN; -} - -static int brcm_call(struct ast_channel *chan, const char *dest, int timeout) -{ - struct brcm_pvt *p; - struct brcm_subchannel *sub; - struct brcm_subchannel *sub_peer; - - struct timeval UtcTime = ast_tvnow(); - struct ast_tm tm; - sub = ast_channel_tech_pvt(chan); - - ast_debug(1, "line %d\n", sub->parent->line_id); - ast_localtime(&UtcTime, &tm, NULL); - - if ((ast_channel_state(chan) != AST_STATE_DOWN) && (ast_channel_state(chan) != AST_STATE_RESERVED)) { - ast_log(LOG_WARNING, "brcm_call called on %s, neither down nor reserved\n", ast_channel_name(chan)); - return -1; - } - - //ast_mutex_lock(&sub->parent->lock); - pvt_lock(sub->parent, "brcm_call"); - - p = sub->parent; - sub_peer = brcm_subchannel_get_peer(sub); - if (!channel_config[p->line_id].enabled) { - ast_debug(1, "tel_line %d disabled!\n", p->line_id); - ast_channel_hangupcause_set(chan, AST_CAUSE_CALL_REJECTED); - ast_queue_control(chan, AST_CONTROL_BUSY); - brcm_send_ubus_event("CALL_REJECTED",p->line_id); - } - else if (channel_config[p->line_id].do_not_disturb) { - ast_debug(1, "Do not disturbed\n"); - ast_channel_hangupcause_set(chan, AST_CAUSE_USER_BUSY); - brcm_send_ubus_event("USER_BUSY",p->line_id); - ast_queue_control(chan, AST_CONTROL_BUSY); - } - else if (brcm_in_call(p) && // a call is established - is_call_waiting_enabled(p->context)) { // call waiting enabled - ast_debug(1, "Call waiting\n"); - sub->channel_state = CALLWAITING; - brcm_signal_callwaiting(p); - int cwtimeout_ms = cwtimeout * 1000; - int cwBeep_ms = cwBeep * 1000; - sub->cw_timer_id = ast_sched_add(sched, cwtimeout_ms, cwtimeout_cb, sub); - sub->cwBeep_timer_id = ast_sched_add(sched, cwBeep_ms, cwbeep_cb, sub); - ast_setstate(chan, AST_STATE_RINGING_CW); - ast_queue_control(chan, AST_CONTROL_RINGING_CW); - } - else if (!brcm_subchannel_is_idle(sub_peer)) { - ast_debug(1, "Line is busy\n"); - ast_channel_hangupcause_set(chan, AST_CAUSE_USER_BUSY); - ast_queue_control(chan, AST_CONTROL_BUSY); - /*When call comes but the sub_peer is not idle,move to old ONHOOK state*/ - if(sub->channel_state == ALLOCATED) - sub->channel_state = ONHOOK; - brcm_send_ubus_event("USER_BUSY",p->line_id); - - struct brcm_pvt *tmp = p; - struct brcm_subchannel *s = NULL; - int con_id = -1; - - while(tmp) { - s = brcm_get_active_subchannel(tmp); - con_id = s->connection_id; - // for local call we need to find the other party and change its subchannel state to CALLENDED - // so the extensionCallStatus is properly set to Disconnected in brcm_answer() - if (con_id > 0 && (strncmp(tmp->extensionCallStatus, "Dialing", strlen(tmp->extensionCallStatus)) == 0)) { - s->channel_state = CALLENDED; - break; - } - tmp = brcm_get_next_pvt(tmp); - } - } - else { - ast_debug(1, "Not call waiting\n"); - /*We can move status to Alerting ,since we are ringing now */ - strncpy(p->extensionCallStatus, "Alerting", CALL_STATUS_MAX_LEN); - sub->channel_state = RINGING; - if (!channel_config[p->line_id].calleridenable) { - p->tech->signal_ringing(p); - } else { - p->tech->signal_ringing_callerid_pending(p); - p->tech->signal_callerid(chan, sub); - } - - if (has_call_in_sip_client(p->context)) { - sub->cw_timer_id = ast_sched_add(sched, cwtimeout*1000, cwtimeout_cb, sub); - if (is_call_waiting_enabled(p->context)) { - ast_setstate(chan, AST_STATE_RINGING_CW); - ast_queue_control(chan, AST_CONTROL_RINGING_CW); - } else { - ast_setstate(chan, AST_STATE_RINGING); - ast_queue_control(chan, AST_CONTROL_RINGING); - } - } else { - ast_setstate(chan, AST_STATE_RINGING); - ast_queue_control(chan, AST_CONTROL_RINGING); - } - - brcm_send_ubus_event("RINGING",p->line_id); - } - //ast_mutex_unlock(&sub->parent->lock); - pvt_unlock(sub->parent); - - return 0; -} - -static int brcm_hangup(struct ast_channel *ast) -{ - struct brcm_pvt *p; - struct brcm_subchannel *sub = ast_channel_tech_pvt(ast), *sub_peer; - struct ast_channel *peer_owner; - int conf_timer_removed = 0; - - if (!sub) { - ast_log(LOG_WARNING, "Asked to hangup channel not connected\n"); - return 0; - } - - p = sub->parent; - pvt_lock(p, "TELCHAN hangup"); - sub_peer = brcm_subchannel_get_peer(sub); - - ast_debug(1, "ast=%s line_id=%d connection_id=%d dialtone=%s channel_state=%s peer_state=%s\n", - ast_channel_name(ast), p->line_id, sub->connection_id, dialtone_map[p->dialtone].str, - state2str(sub->channel_state), state2str(sub_peer->channel_state)); - /* If call is not connected (INCALL) or is dialing but Subchannel is busy, move to Disconnected state */ - if ((sub->channel_state!= ONHOOK && sub_peer->channel_state != OFFHOOK)) - { - //Ignore state change if we are INCALL - if(sub->channel_state != INCALL && sub_peer->channel_state != INCALL) - { - /*Cases where other party doesnt picksup call ,after timeout move it to idle status */ - if(sub->channel_state == RINGING && sub_peer->channel_state == ONHOOK) - strncpy(p->extensionCallStatus, "Idle", CALL_STATUS_MAX_LEN); - else - strncpy(p->extensionCallStatus, "Disconnected", CALL_STATUS_MAX_LEN); - } - } - - if (sub_peer->conf_timer_id != -1) { - conf_timer_removed = 1; - if (ast_sched_del(sched, sub_peer->conf_timer_id)) { - ast_log(LOG_WARNING, "Failed to remove scheduled conference setup timer\n"); - } - sub_peer->conf_timer_id = -1; - } - - if (sub->conf_timer_id != -1){ - conf_timer_removed = 1; - if (ast_sched_del(sched, sub->conf_timer_id)) { - ast_log(LOG_WARNING, "Failed to remove scheduled conference setup timer\n"); - } - sub->conf_timer_id = -1; - } - - if (sub->channel_state == CALLWAITING) { - brcm_stop_callwaiting(p); - } else if (sub_peer->channel_state == ONHOLD && - sub_peer->onhold_hangup_timer_id != -1 && sub->channel_state == RINGING) { - ast_debug(2, "There is a forgotten onhold call, not releasing channel\n"); - endpt_signal(sub->parent->line_id, "ringback", "off", NULL); - } else if (sub->channel_state == RINGING || sub->onhold_hangup_timer_id != -1) { - //Stop ringing if other end hungup before we answered - channel_settings *s = &channel_config[p->line_id]; - if (!s->calleridenable) { - p->tech->stop_ringing(p); - } else { - p->tech->stop_ringing_callerid_pending(p); - } - } else if (brcm_subchannel_is_idle(sub_peer) && p->tech->release) { - // No active subchannel left, release - ast_debug(1, "Release the call since the peer sub-channel is also idle\n"); - p->tech->release(p); - } - - if (sub->cw_timer_id != -1) { - if (ast_sched_del(sched, sub->cw_timer_id)) { - ast_log(LOG_WARNING, "Failed to remove scheduled call waiting timer\n"); - } - sub->cw_timer_id = -1; - } - - if (sub->cwBeep_timer_id != -1) { - if (ast_sched_del(sched, sub->cwBeep_timer_id)) { - ast_log(LOG_WARNING, "Failed to remove scheduled call waiting beep timer\n"); - } - sub->cwBeep_timer_id = -1; - } - - if(sub->r4_hangup_timer_id != -1) { - if (ast_sched_del(sched, sub->r4_hangup_timer_id)) { - ast_log(LOG_WARNING, "Failed to remove scheduled r4 hangup timer\n"); - } - sub->r4_hangup_timer_id = -1; - } - - if(sub->onhold_hangup_timer_id != -1) { - if (ast_sched_del(sched, sub->onhold_hangup_timer_id)) { - ast_log(LOG_WARNING, "Failed to remove scheduled onhold hangup timer\n"); - } - sub->onhold_hangup_timer_id = -1; - } - ast_setstate(ast, AST_STATE_DOWN); - - p->lastformat = NULL; - p->lastinput = NULL; - p->hf_detected = 0; - - /** - * Howler tone is played using a media file on an active channel - * Treated like a normal call but we want to preserve the channel state - * Additional check for ONHOOK channel state so that handle_dialtone_timeout() - * isn't called again when going on-hook - */ - if (p->dialtone != DIALTONE_HOWLER && sub->channel_state != ONHOOK) { - /*In case the call ends before connecting ,move the party to ONHOOK state Since its already on ONHOOK , - we never get on ONHOOK event again*/ - if (sub->channel_state == RINGING || sub->channel_state == ONHOLD) - sub->channel_state = ONHOOK; - else - sub->channel_state = CALLENDED; - ast_debug(6, "Setting channel state to %s\n", state2str(sub->channel_state)); - } - - if (sub->conference_initiator && brcm_in_conference(p)) { - /* Switch still active call leg out of conference mode */ - brcm_stop_conference(sub); - brcm_stop_conference(sub_peer); - } - - if (sub_peer->channel_state == INCALL && conf_timer_removed == 1) { - ast_log(LOG_NOTICE,"In case some party hangs up ,Unhold the other parties \n"); - pvt_lock(sub_peer->parent, "Brcm Hangup"); - if(sub_peer->owner) - ast_channel_ref(sub_peer->owner); - peer_owner = sub_peer->owner; - pvt_unlock(sub_peer->parent); - brcm_unmute_connection(sub_peer); - ast_queue_unhold(peer_owner); - sub_peer->channel_state = INCALL; - } - - memset(p->ext, 0, sizeof(p->ext)); - sub->owner = NULL; - sub->conference_initiator = 0; - free(sub->conference_id); - sub->conference_id = NULL; - ast_module_unref(ast_module_info->self); - ast_verb(3, "Hungup '%s'\n", ast_channel_name(ast)); - ast_channel_tech_pvt_set(ast, NULL); - brcm_close_connection(sub); - - /* Check for channel state before dial tone timeout */ - if(sub->channel_state == CALLENDED && sub_peer->channel_state != INCALL) { - /* If we hangup but not playing howler, start playing timeout tones */ - p->dialtone = DIALTONE_ON; - handle_dialtone_timeout(p); - } - - if (sub_peer->cwBeep_timer_id != -1) { - if (ast_sched_del(sched, sub_peer->cwBeep_timer_id)) { - ast_log(LOG_WARNING, "Failed to remove scheduled call waiting beep timer\n"); - } - sub_peer->cwBeep_timer_id = -1; - } - - pvt_unlock(p); - return 0; -} - -static int brcm_answer(struct ast_channel *ast) -{ - struct brcm_subchannel *sub = ast_channel_tech_pvt(ast); - struct brcm_pvt *pvt = sub->parent; - - ast_debug(1, "brcm_answer(%s)\n", ast_channel_name(ast)); - pvt_lock(pvt, "TELCHAN answer"); - - /* For attended call transfer, if the transfer target has accepted the call initiated by - * us as the tranferee, we can terminate the original call with the transferor */ - if (sub->channel_state == TRANSFERING) { - struct brcm_subchannel *peer = brcm_subchannel_get_peer(sub); - - if (peer->owner) { - ast_debug(1, "Transferee terminates the original call with the transferor\n"); - ast_queue_control(peer->owner, AST_CONTROL_HANGUP); - peer->owner = NULL; - peer->channel_state = CALLENDED; - brcm_close_connection(peer); - } - } - - if (ast_channel_state(ast) != AST_STATE_UP) { - ast_setstate(ast, AST_STATE_UP); - ast_debug(2, "brcm_answer(%s) set state to up\n", ast_channel_name(ast)); - } - ast_channel_rings_set(ast, 0); - - /** - * Howler tone is played using a media file on an active channel - * Treated like a normal call but we want to preserve the channel_state - */ - if(pvt->dialtone != DIALTONE_HOWLER) { - if(sub->channel_state != CALLENDED)/*Dont change the channel state if call is terminated */ - sub->channel_state = INCALL; - } - endpt_signal(pvt->line_id, "answer", "on", NULL); - brcm_send_ubus_event("RINGING_OFF",pvt->line_id); - endpt_signal(pvt->line_id, "ringback", "off", NULL); - - if(sub->channel_state != CALLENDED) - strncpy(pvt->extensionCallStatus, "Connected", CALL_STATUS_MAX_LEN); - else - strncpy(pvt->extensionCallStatus, "Disconnected", CALL_STATUS_MAX_LEN); - - pvt_unlock(pvt); - - return 0; -} - -/* -* Map RTP data header value to a codec name -*/ -static char* brcm_get_codec_string(int id) { - switch (id) { - case PCMA: return "alaw"; break; - case PCMU: return "ulaw"; break; - case G723: return "g723.1"; break; - case G726: return "g726"; break; - case G729: return "g729"; break; - case G722: return "g722"; break; - case CN: return "cn"; break; - case -1: return "none set"; break; - default: return "unknown id"; break; - } -} - -static int brcm_classify_rtp_packet(int id) { - - //ast_debug(5, "RTP Packet ID = %d\n", id); - - switch (id) { - case PCMU: return BRCM_AUDIO; - case G726: return BRCM_AUDIO; - case G723: return BRCM_AUDIO; - case PCMA: return BRCM_AUDIO; - case G729: return BRCM_AUDIO; - case G722: return BRCM_AUDIO; - case CN: return BRCM_AUDIO; - case RTCP_SR: return BRCM_RTCP_SR; - case RTCP_RR: return BRCM_RTCP_RR; - default: - ast_verbose("Unknown rtp packet id %d\n", id); - return BRCM_UNKNOWN; - } -} - -static int map_ast_codec_id_to_rtp(const struct ast_format *astcodec) -{ - if (ast_format_cmp(astcodec, ast_format_alaw) == AST_FORMAT_CMP_EQUAL) { - return PCMA; - } else if (ast_format_cmp(astcodec, ast_format_ulaw) == AST_FORMAT_CMP_EQUAL) { - return PCMU; - } else if (ast_format_cmp(astcodec, ast_format_g722) == AST_FORMAT_CMP_EQUAL) { - return G722; - } else if (ast_format_cmp(astcodec, ast_format_g723) == AST_FORMAT_CMP_EQUAL) { - return G723; - } else if (ast_format_cmp(astcodec, ast_format_g729) == AST_FORMAT_CMP_EQUAL) { - return G729; - } else if (ast_format_cmp(astcodec, ast_format_g726) == AST_FORMAT_CMP_EQUAL) { - return G726; - } else { - ast_verbose("Unknown asterisk format/codec id\n"); - return PCMA; - } -} - - -static struct ast_frame *brcm_read(struct ast_channel *ast) -{ - return &ast_null_frame; -} - -/* Handle stream events on audio_bus. Parses raw stream and calls - registered packet handlers. */ -static void audio_rx_stream_handler(pe_stream_t *stream __attribute__((unused)), pe_event_t *event __attribute__((unused))) { - if (pe_bus_receive(audio_rx_bus, event) < 0) { - printf("audio_bus rx buffer full"); - return; // Drop packets if we can't cope the pace - } - - pe_bus_dispatch(audio_rx_bus); -} - -static int brcm_write(struct ast_channel *ast, struct ast_frame *frame) -{ - struct brcm_subchannel *sub = ast_channel_tech_pvt(ast); - int packet_size; - audio_packet_t *ap; - - if (ast_channel_state(ast) != AST_STATE_UP && ast_channel_state(ast) != AST_STATE_RING) { - /* Silently ignore packets until channel is up */ - ast_debug(5, "error: channel not up\n"); - return 0; - } - - /* Ignore if on hold */ - if (sub->channel_state == ONHOLD) { - return 0; - } - if (frame->frametype == AST_FRAME_CNG) { - /*packet_size = sizeof(audio_packet_t) - 1 + RTP_HEADER_SIZE + frame->datalen; - CNG paket has only 1 data for the noise lvl, datalen=1*/ - packet_size = sizeof(audio_packet_t) + RTP_HEADER_SIZE ; - ap = calloc(1, packet_size); - if (!ap) { - ast_log(LOG_ERROR, "Out of memory\n"); - return -1; - } - - ap->line = sub->parent->line_id; - ap->cnx_id = sub->parent->line_id; - ap->rtp_size = RTP_HEADER_SIZE + 1; - - /* copy frame data to audio packet */ - /* level = 127 - (level & 0x7f); */ - ap->rtp[12]=127 - (frame->subclass.integer & 0x7f); - - //ast_mutex_lock(&sub->parent->lock); - pvt_lock(sub->parent, "TELCHAN write frame"); - - /* generate the rtp header */ - brcm_generate_rtp_packet(sub, ap->rtp, CN, 0, 0, frame->seqno); - - /* set rtp id sent to endpoint */ - sub->codec = CN; - - //ast_mutex_unlock(&sub->parent->lock); - pvt_unlock(sub->parent); - pe_bus_send(audio_tx_bus, (uint8_t *)ap, packet_size); - free(ap); - } - - if(frame->frametype == AST_FRAME_VOICE) { - packet_size = sizeof(audio_packet_t) - 1 + RTP_HEADER_SIZE + frame->datalen; - ap = calloc(1, packet_size); - if (!ap) { - ast_log(LOG_ERROR, "Out of memory\n"); - return -1; - } - - ap->line = sub->parent->line_id; - ap->cnx_id = sub->parent->line_id; - ap->rtp_size = RTP_HEADER_SIZE + frame->datalen; - - /* copy frame data to audio packet */ - memcpy(ap->rtp + RTP_HEADER_SIZE, frame->data.ptr, frame->datalen); - - //ast_mutex_lock(&sub->parent->lock); - pvt_lock(sub->parent, "TELCHAN write frame"); - - /* generate the rtp header */ - brcm_generate_rtp_packet(sub, ap->rtp, map_ast_codec_id_to_rtp(frame->subclass.format), 0, 0, frame->seqno); - - /* set rtp id sent to endpoint */ - sub->codec = map_ast_codec_id_to_rtp(frame->subclass.format); - - //ast_mutex_unlock(&sub->parent->lock); - pvt_unlock(sub->parent); - - pe_bus_send(audio_tx_bus, (uint8_t *)ap, packet_size); - free(ap); - } - - if(frame->frametype == AST_FRAME_RTCP) { - packet_size = sizeof(audio_packet_t) - 1 + frame->datalen; - ap = calloc(1, packet_size); - if (!ap) { - ast_log(LOG_ERROR, "Out of memory\n"); - return -1; - } - - ap->line = sub->parent->line_id; - ap->cnx_id = sub->parent->line_id; - ap->rtp_size = frame->datalen; - - /* copy frame data to audio packet */ - memcpy(ap->rtp, frame->data.ptr, frame->datalen); - - brcm_interpret_rtcp_packet(sub, ap->rtp, ap->rtp_size); - - pe_bus_send(audio_tx_bus, (uint8_t *)ap, packet_size); - free(ap); - } - return 0; -} - -static void brcm_reset_dtmf_buffer(struct brcm_pvt *p) -{ - memset(p->dtmfbuf, 0, sizeof(p->dtmfbuf)); - p->dtmf_len = 0; - p->dtmf_first = -1; - p->dtmfbuf[p->dtmf_len] = '\0'; -} - -static char *state2str(enum brcm_channel_state state) -{ - switch (state) { - case ONHOOK: return "ONHOOK"; - case OFFHOOK: return "OFFHOOK"; - case DIALING: return "DIALING"; - case CALLING: return "CALLING"; - case INCALL: return "INCALL"; - case ANSWER: return "ANSWER"; - case CALLENDED: return "CALLENDED"; - case RINGING: return "RINGING"; - case CALLWAITING: return "CALLWAITING"; - case ONHOLD: return "ONHOLD"; - case TRANSFERING: return "TRANSFERING"; - case RINGBACK: return "RINGBACK"; - case AWAITONHOOK: return "AWAITONHOOK"; - case ALLOCATED: return "ALLOCATED"; - default: return "UNKNOWN"; - } -} - -static int brcm_subchannel_is_idle(const struct brcm_subchannel *sub) -{ - if (sub->channel_state == ONHOOK || sub->channel_state == CALLENDED) { - return 1; - } - return 0; -} - -struct brcm_subchannel *brcm_subchannel_get_peer(const struct brcm_subchannel *sub) -{ - struct brcm_subchannel *peer_sub; - peer_sub = (sub->parent->sub[0] == sub) ? sub->parent->sub[1] : sub->parent->sub[0]; - return peer_sub; -} - -/* Tell endpoint to play country specific dialtone. */ -static int brcm_signal_dialtone(struct brcm_pvt *p) { - - ast_verbose("Setting dialtone to %s\n", dialtone_map[p->dialtone].str); - switch (p->dialtone) { - case DIALTONE_OFF: - case DIALTONE_MWI_OFF: - endpt_signal(p->line_id, "dial", "off", NULL); // this will turn off any tones that is playing. Act as play dial-off. - break; - case DIALTONE_ON: - endpt_signal(p->line_id, "dial", "on", NULL); - break; - case DIALTONE_CONGESTION: - endpt_signal(p->line_id, "congestion", "on", NULL); - break; - case DIALTONE_SPECIAL_CONDITION: - endpt_signal(p->line_id, "stutter", "on", NULL); - break; - case DIALTONE_UNOBTAINABLE: - endpt_signal(p->line_id, "unobtainable", "on", NULL); - break; - case DIALTONE_HOWLER: - ast_debug(9, "Trigger howler tone from Asterisk\n"); - brcm_signal_howler(p); - break; - default: - ast_log(LOG_ERROR, "Requested to signal unknown dialtone\n"); - return -1; - } - return 0; -} - -static void brcm_signal_howler(struct brcm_pvt *p) { - struct brcm_subchannel *sub = brcm_get_active_subchannel(p); - - if(!sub) { - ast_debug(9, "Unable to find active subchannel\n"); - return; - } - - /* Start the pbx */ - if (!sub->connection_init) { - sub->connection_id = ast_atomic_fetchadd_int((int *)¤t_connection_id, +1); - brcm_create_connection(sub); - } - - /* Create a new channel to context 'howler' handled by extensions.conf */ - brcm_new(sub, AST_STATE_RING, "0", "howler", NULL, NULL, NULL); -} - -int brcm_stop_dialtone(struct brcm_pvt *p) { - p->dialtone = DIALTONE_OFF; - brcm_signal_dialtone(p); - brcm_send_ubus_event("DIALTONE_OFF",p->line_id); - return 0; -} - -static struct ast_channel *brcm_new(struct brcm_subchannel *subchan, int state, const char *ext, const char *context, - const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, - struct ast_format_cap *format) -{ - struct ast_channel *chan = NULL; - struct ast_format *fmt; - struct ast_format_cap *caps; - - caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); - if (!caps) { - return NULL; - } - - chan = ast_channel_alloc(1, state, subchan->parent->cid_num, subchan->parent->cid_name, "", ext, context, - assignedids, requestor, 0, "TELCHAN/%d/%d", subchan->parent->line_id, subchan->connection_id); - if (chan) { - ast_channel_tech_set(chan, cur_tech); - - ast_format_cap_append_from_cap(caps, default_cap, AST_MEDIA_TYPE_UNKNOWN); - ast_channel_nativeformats_set(chan, caps); - ao2_ref(caps, -1); - fmt = ast_format_cap_get_format(ast_channel_nativeformats(chan), 0); - - channel_settings *s = &channel_config[subchan->parent->line_id]; - - /* set codecs */ - ast_channel_set_writeformat(chan, fmt); - ast_channel_set_rawwriteformat(chan, fmt); - ast_channel_set_readformat(chan, fmt); - ast_channel_set_rawreadformat(chan, fmt); - - /* no need to call ast_setstate: the channel_alloc already did its job */ - if (state == AST_STATE_RING) - ast_channel_rings_set(chan, 1); - ast_channel_tech_pvt_set(chan, subchan); - /* Don't use ast_set_callerid() here because it will - * generate a NewCallerID event before the NewChannel event */ - if (!ast_strlen_zero(subchan->parent->cid_num)) { - ast_channel_caller(chan)->ani.number.valid = 1; - ast_channel_caller(chan)->ani.number.str = ast_strdup(subchan->parent->cid_num); - } - - ast_channel_caller(chan)->id.number.presentation = s->anonymouscallenable ? AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED : AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED; - ast_channel_caller(chan)->id.name.presentation = s->calleridnameenable; - - //Setup jitter buffer - ast_jb_configure(chan, &global_jbconf); - subchan->owner = chan; - - ast_module_ref(ast_module_info->self); - - if (state != AST_STATE_DOWN) { - if (ast_pbx_start(chan)) { - ast_log(LOG_WARNING, "Unable to start PBX on %s\n", ast_channel_name(chan)); - ast_hangup(chan); - return NULL; - } - } - - ast_channel_unlock(chan); - } else - ast_log(LOG_WARNING, "Unable to allocate channel structure\n"); - - return chan; -} - - -static struct brcm_pvt* brcm_get_next_pvt(struct brcm_pvt *p) { - if (p && p->next) - return p->next; - else - return NULL; -} - -struct brcm_pvt* brcm_get_pvt_from_lineid(struct brcm_pvt *p, int line_id) -{ - struct brcm_pvt *tmp = p; - if (p && p->line_id == line_id) return p; - tmp = brcm_get_next_pvt(tmp); - - while(tmp) { - if (tmp->line_id == line_id) return tmp; - tmp = brcm_get_next_pvt(tmp); - } - return NULL; -} - -struct brcm_subchannel* brcm_get_active_subchannel(const struct brcm_pvt *p) -{ - struct brcm_subchannel *sub = NULL; - int i; - - if(!p) - return NULL; - - for (i=0; i<NUM_SUBCHANNELS; i++) { - switch (p->sub[i]->channel_state) { - case INCALL: - case DIALING: - case CALLING: - case OFFHOOK: - case AWAITONHOOK: - case RINGING: - case TRANSFERING: - case RINGBACK: - sub = p->sub[i]; - return sub; - case CALLWAITING: - case ONHOLD: - break; - case ONHOOK: - case ANSWER: - case CALLENDED: - if (!sub) { - sub = p->sub[i]; - } - break; - default: - ast_log(LOG_WARNING, "Unhandled channel state %d\n", p->sub[i]->channel_state); - break; - } - } - - ast_assert(sub != NULL); - return sub; -} - -static struct brcm_subchannel *brcm_get_onhold_subchannel(const struct brcm_pvt *p) -{ - struct brcm_subchannel *sub; - int i; - for(i=0; i<NUM_SUBCHANNELS; i++) { - sub = p->sub[i]; - if (sub->channel_state == ONHOLD) { - return sub; - } - } - return NULL; -} - -/*Set up a Conference call on press of flash hook once timer expires */ -static int setup_conference_call_cb( const void *data) -{ - struct brcm_subchannel *sub_peer; - struct ast_channel *peer_owner = NULL; - - ast_log(LOG_NOTICE ,"Set up a conference call\n"); - struct brcm_pvt *p = (struct brcm_pvt *) data; - struct brcm_subchannel *sub = brcm_get_active_subchannel(p); - sub_peer = brcm_subchannel_get_peer(sub); - pvt_lock(sub->parent, "setup_conference_call_cb"); - if (sub_peer->owner) { - ast_channel_ref(sub_peer->owner); - peer_owner = sub_peer->owner; - } - - pvt_unlock(sub->parent); - /* Setup conference if no DTMF is pressed after flash ,i.e we are not waiting for DTMF now*/ - if((sub->conf_timer_id != -1) || (sub_peer->conf_timer_id != -1)) - { - ast_log(LOG_NOTICE,"Valid flags to start a Conference Call\n"); - brcm_create_conference(p); - if (peer_owner) { - sub_peer->channel_state = INCALL; - } - sub->channel_state = INCALL; - sub->conf_timer_id = -1; - sub_peer->conf_timer_id = -1; - } - - brcm_send_ubus_event("UNHOLD",sub->parent->line_id); - brcm_send_ubus_event("CONFERENCE_START",sub->parent->line_id); - if (peer_owner) { - ast_channel_unref(peer_owner); - } - return 0; -} - -/* Hangup incoming call after call waiting times out */ -static int cwtimeout_cb(const void *data) -{ - struct brcm_subchannel *sub; - struct ast_channel *owner = NULL; - - - sub = (struct brcm_subchannel *) data; - ast_debug(2, "No response to call waiting, hanging up, sub->channel_state: %d\n", sub->channel_state); - //ast_mutex_lock(&sub->parent->lock); - pvt_lock(sub->parent, "Cwtimeout callback"); - if (sub->cwBeep_timer_id != -1) { - if (ast_sched_del(sched, sub->cwBeep_timer_id)) { - ast_log(LOG_WARNING, "Failed to remove scheduled call waiting beep timer\n"); - } - sub->cwBeep_timer_id = -1; - } - sub->cw_timer_id = -1; - if (sub->owner) { - ast_channel_ref(sub->owner); - owner = sub->owner; - } - //ast_mutex_unlock(&sub->parent->lock); - if (sub->channel_state == CALLWAITING) - brcm_stop_callwaiting(sub->parent); - else if (sub->channel_state == RINGING) - brcm_stop_ringing(sub->parent); - pvt_unlock(sub->parent); - - if (owner) { - ast_channel_lock(owner); - ast_channel_hangupcause_set(owner, AST_CAUSE_USER_BUSY); - ast_queue_control(owner, AST_CONTROL_BUSY); - ast_channel_unlock(owner); - ast_channel_unref(owner); - } - - return 0; -} - -/* Play beep during call waiting */ -static int cwbeep_cb(const void *data) -{ - struct brcm_subchannel *sub = (struct brcm_subchannel *) data; - int cwBeep_ms = cwBeep * 1000; - - pvt_lock(sub->parent, "cwbeep_cb callback"); - if (sub->parent) { - brcm_signal_callwaiting(sub->parent); - } - pvt_unlock(sub->parent); - - sub->cwBeep_timer_id = ast_sched_add(sched, cwBeep_ms, cwbeep_cb, sub); - - return 0; -} - -/* Hangup calls if not done by remote after R4 transfer */ -static int r4hanguptimeout_cb(const void *data) -{ - struct brcm_subchannel *sub; - struct brcm_subchannel *peer_sub; - - struct ast_channel *sub_owner = NULL; - struct ast_channel *peer_sub_owner = NULL; - - ast_debug(2, "No hangup from remote after remote transfer using R4, hanging up\n"); - - sub = (struct brcm_subchannel *) data; - peer_sub = brcm_subchannel_get_peer(sub); - - //ast_mutex_lock(&sub->parent->lock); - pvt_lock(sub->parent, "r4hanguptimeout callback"); - - sub->r4_hangup_timer_id = -1; - peer_sub->channel_state = CALLENDED; - sub->channel_state = CALLENDED; - - if (sub->owner) { - ast_channel_ref(sub->owner); - sub_owner = sub->owner; - } - if (peer_sub->owner) { - ast_channel_ref(peer_sub->owner); - peer_sub_owner = peer_sub->owner; - } - //ast_mutex_unlock(&sub->parent->lock); - pvt_unlock(sub->parent); - - if (sub_owner) { - ast_queue_control(sub_owner, AST_CONTROL_HANGUP); - ast_channel_unref(sub_owner); - } - - if (peer_sub_owner) { - ast_queue_control(peer_sub_owner, AST_CONTROL_HANGUP); - ast_channel_unref(peer_sub_owner); - } - - return 0; -} - -/* Hangup call onhold if user does not pick up after reminder ringing */ -static int onholdhanguptimeout_cb(const void *data) -{ - struct brcm_subchannel *sub; - struct ast_channel *sub_owner = NULL; - - ast_debug(2, "No pickup after reminder ringing for call on hold, hanging up\n"); - sub = (struct brcm_subchannel *) data; - - //ast_mutex_lock(&sub->parent->lock); - pvt_lock(sub->parent, "onholdhanguptimeout callback"); - - sub->onhold_hangup_timer_id = -1; - - if (sub->owner) { - ast_channel_ref(sub->owner); - sub_owner = sub->owner; - } - //ast_mutex_unlock(&sub->parent->lock); - pvt_unlock(sub->parent); - - if (sub_owner) { - ast_queue_control(sub_owner, AST_CONTROL_HANGUP); - ast_channel_unref(sub_owner); - } - - return 0; -} - -/* - * Helper function that tells asterisk to start a call on the provided pvt/sub/context - * using the content of the dtmf buffer. - */ -static void brcm_start_calling(struct brcm_subchannel *sub, char* context) -{ - sub->channel_state = CALLING; - ast_copy_string(sub->parent->ext, sub->parent->dtmfbuf, sizeof(sub->parent->ext)); - - ast_debug(1, "Calling %s@%s\n", sub->parent->ext, context); - - /* Reset the dtmf buffer */ - brcm_reset_dtmf_buffer(sub->parent); - - /* Reset hook flash state */ - sub->parent->hf_detected = 0; - - /* Start the pbx */ - if (!sub->connection_init) { - sub->connection_id = ast_atomic_fetchadd_int((int *)¤t_connection_id, +1); - brcm_create_connection(sub); - } - - /* Changed state from AST_STATE_UP to AST_STATE_RING ito get the brcm_answer callback - * which is needed for call waiting. */ - brcm_new(sub, AST_STATE_RING, sub->parent->ext, context, NULL, NULL, NULL); -} - -/* - * Start calling if we have a (partial) match in asterisks dialplan after an interdigit timeout. - * Called on scheduler thread. - */ -static int handle_interdigit_timeout(const void *data) -{ - ast_debug(9, "Interdigit timeout\n"); - struct brcm_pvt *p = (struct brcm_pvt *) data; - //ast_mutex_lock(&p->lock); - pvt_lock(p, "interdigit callback"); - p->interdigit_timer_id = -1; - struct brcm_subchannel *sub = brcm_get_active_subchannel(p); - if (channel_config[p->line_id].minimumnumberdigits && strlen(p->dtmfbuf) >= channel_config[p->line_id].minimumnumberdigits){ - if (ast_exists_extension(NULL, p->context, p->dtmfbuf, 1, p->cid_num)) - { - //We have at least one matching extension in "normal" context, - //and interdigit timeout has passed, so have asterisk start calling. - //Asterisk will select the best matching extension if there are more than one possibility. - ast_debug(9, "Interdigit timeout, extension(s) matching %s found\n", p->dtmfbuf); - brcm_start_calling(sub, p->context); - } - } - //if no limit on minimum digits check extension exits - if(!channel_config[p->line_id].minimumnumberdigits){ - if (ast_exists_extension(NULL, p->context, p->dtmfbuf, 1, p->cid_num)) - { - //We have at least one matching extension in "normal" context, - //and interdigit timeout has passed, so have asterisk start calling. - //Asterisk will select the best matching extension if there are more than one possibility. - ast_debug(9, "Interdigit timeout, extension(s) matching %s found\n", p->dtmfbuf); - brcm_start_calling(sub, p->context); - } - } - //ast_mutex_unlock(&p->lock); - pvt_unlock(p); - return 0; -} - -/* - * Start autodialing if we have an autodial extension. - * Called on scheduler thread. - */ -static int handle_autodial_timeout(const void *data) -{ - ast_debug(9, "Autodial timeout\n"); - struct brcm_pvt *p = (struct brcm_pvt *) data; - pvt_lock(p, "autodial timeout"); - //ast_mutex_lock(&p->lock); - p->autodial_timer_id = -1; - struct brcm_subchannel *sub = brcm_get_active_subchannel(p); - channel_settings *s = &channel_config[p->line_id]; - - if (ast_exists_extension(NULL, p->context, s->autodial_ext, 1, p->cid_num)) - { - brcm_stop_dialtone(p); - ast_copy_string(p->dtmfbuf, s->autodial_ext, sizeof(p->dtmfbuf)); - ast_debug(9, "Autodialing extension: %s\n", p->dtmfbuf); - brcm_start_calling(sub, p->context); - } - //ast_mutex_unlock(&p->lock); - pvt_unlock(p); - return 0; -} - -/* - * Dialtone expired, play congestion tone - * Called on scheduler thread. - */ -static int handle_dialtone_timeout(const void *data) -{ - struct brcm_pvt *p = (struct brcm_pvt *) data; - - pvt_lock(p, "dialtone timeout"); - strncpy(p->extensionCallStatus, "Disconnected", CALL_STATUS_MAX_LEN); - //ast_mutex_lock(&p->lock); - p->dialtone_timeout_timer_id = -1; - - struct brcm_subchannel *sub = brcm_get_active_subchannel(p); - - ast_debug(9, "Dialtone timeout, sub->channel_state: %s\n", state2str(sub->channel_state)); - - if (sub && (sub->channel_state == OFFHOOK || sub->channel_state == AWAITONHOOK || sub->channel_state == CALLENDED)) { - /* Enter state where nothing else other than ONHOOK is accepted and play series of tones */ - sub->channel_state = AWAITONHOOK; - channel_settings *s = &channel_config[p->line_id]; - - switch (p->dialtone) { - case DIALTONE_ON: - case DIALTONE_SPECIAL_CONDITION: - case DIALTONE_CONGESTION: - case DIALTONE_MWI_OFF: - // Include all 4 dial tones available for asterisk.set1.mwi_dialtone_state - p->dialtone = DIALTONE_UNOBTAINABLE; - p->dialtone_timeout_timer_id = ast_sched_add(sched, s->offhook_nu_timeoutmsec, handle_dialtone_timeout, p); - break; - case DIALTONE_UNOBTAINABLE: - p->dialtone = DIALTONE_OFF; - p->dialtone_timeout_timer_id = ast_sched_add(sched, s->offhook_silence_timeoutmsec, handle_dialtone_timeout, p); - break; - case DIALTONE_OFF: - p->dialtone = DIALTONE_HOWLER; - break; - default: - p->dialtone = DIALTONE_OFF; - ast_log(LOG_ERROR, "Invalid dialtone timeout state\n"); - break; - } - brcm_signal_dialtone(p); - } - - //ast_mutex_unlock(&p->lock); - pvt_unlock(p); - return 0; -} - -/* - * Start calling if we have a match in asterisks dialplan. - * Called after each new DTMF event, from monitor_events thread, - * with the required locks already held. - */ -void handle_dtmf_calling(struct brcm_subchannel *sub) -{ - struct brcm_pvt *p = sub->parent; - char termination_digit; - int dtmfbuf_len = strlen(p->dtmfbuf); - char dtmf_last_char = p->dtmfbuf[(dtmfbuf_len - 1)]; - char dtmf_one_before_last_char = p->dtmfbuf[(dtmfbuf_len > 1 ? dtmfbuf_len - 2 : 0)]; - - termination_digit = channel_config[p->line_id].terminationdigit?channel_config[p->line_id].terminationdigit:0x23; - - if (ast_exists_extension(NULL, p->context_direct, p->dtmfbuf, 1, p->cid_num) && !ast_matchmore_extension(NULL, p->context_direct, p->dtmfbuf, 1, p->cid_num)) - { - //We have a full match in the "direct" context, so have asterisk place a call immediately - ast_debug(9, "Direct extension matching %s found\n", p->dtmfbuf); - brcm_start_calling(sub, p->context_direct); - } - - - else if (ast_exists_extension(NULL, p->context, p->dtmfbuf, 1, p->cid_num) && dtmf_last_char == termination_digit - && dtmf_one_before_last_char != 0x2A && feature_access_code_match(p->dtmfbuf) != 1) { - if(channel_config[p->line_id].minimumnumberdigits) - { - //Minimum dtmf limit is checked before starting a call.If limit is not reached,ignore - //We have a match in the "normal" context, and user ended the dialling sequence with a # or temination digit set in dialplan, - //so have asterisk place a call immediately if sequence is not partially matching a feature access code - ast_log(LOG_NOTICE, "Termination key %c is pressed during dialing, extension %s found\n",termination_digit, p->dtmfbuf); - if(strlen(p->dtmfbuf) >= channel_config[p->line_id].minimumnumberdigits) - brcm_start_calling(sub, p->context); - else - ast_log(LOG_ERROR, "Not reached the minimum digits to start a call!! \n"); - } - else - { - //No minimum limit, Start calling if we encounter a terminatingdigit - //We have a match in the "normal" context, and user ended the dialling sequence with a # or temination digit set in dialplan, - //so have asterisk place a call immediately if sequence is not partially matching a feature access code - ast_log(LOG_NOTICE, "No minimum digit limit set start a call right away \n"); - brcm_start_calling(sub, p->context); - } - - } - else if (ast_exists_extension(NULL, p->context, p->dtmfbuf, 1, p->cid_num) && !ast_matchmore_extension(NULL, p->context, p->dtmfbuf, 1, p->cid_num)) - { - //We have a full match in the "normal" context, so have asterisk place a call immediately, - //since no more digits can be added to the number - //(this is unlikely to happen since there is probably a "catch-all" extension) - ast_debug(9, "Unique extension matching %s found\n", p->dtmfbuf); - brcm_start_calling(sub, p->context); - } - else if (!ast_matchmore_extension(NULL, p->context, p->dtmfbuf, 1, p->cid_num)) - { - //Full match has been handled in the upper conditions - //Number become invalid if no ast_matchmore_extension - //Set dialtone to Unobtainable, and awaiting on hook - ast_debug(9, "number not matching any extensions, awaiting on hook\n"); - p->dialtone = DIALTONE_UNOBTAINABLE; - sub->channel_state = AWAITONHOOK; - p->dialtone_timeout_timer_id = ast_sched_add(sched, channel_config[p->line_id].offhook_nu_timeoutmsec, handle_dialtone_timeout, p); - brcm_signal_dialtone(p); - } - else { - if (channel_config[p->line_id].minimumnumberdigits && strlen(p->dtmfbuf) >= channel_config[p->line_id].minimumnumberdigits ){ - int interdigitopenmsec = channel_config[p->line_id].interdigitopenmsec; - if (interdigitopenmsec){ - ast_debug(9,"Scheduling interdigitopenmsec timeout of %d msec\n", interdigitopenmsec); - p->interdigit_timer_id = ast_sched_add(sched, interdigitopenmsec, handle_interdigit_timeout, p); - } - else{ - int timeoutmsec = channel_config[p->line_id].timeoutmsec; - ast_debug(9,"Interdigitopenmsec not found !!Using timeoutsec timeout of %d msec\n", timeoutmsec); - p->interdigit_timer_id = ast_sched_add(sched, timeoutmsec, handle_interdigit_timeout, p); - } - } - else { - //No matches. We schedule a (new) interdigit timeout to occur - int timeoutmsec = channel_config[p->line_id].timeoutmsec; - ast_debug(9, "Scheduling interdigit timeout of %d msec\n", timeoutmsec); - p->interdigit_timer_id = ast_sched_add(sched, timeoutmsec, handle_interdigit_timeout, p); - } - } -} - -static void handle_Rnumber_etsi(struct brcm_subchannel *sub, struct brcm_subchannel *sub_peer, - struct ast_channel *owner, struct ast_channel *peer_owner) -{ - struct brcm_pvt *p = sub->parent; - - switch (p->dtmf_first) { - /* Force busy on waiting call or hang up call on hold */ - case '0': - if (sub->channel_state == INCALL && sub_peer->channel_state == CALLWAITING) { - ast_debug(2, "Sending busy to waiting call\n"); - - /* Immediately send busy next time someone calls us during this call */ - //sub->cw_rejected = 1; - - if (ast_sched_del(sched, sub_peer->cw_timer_id)) { - ast_log(LOG_WARNING, "Failed to remove scheduled call waiting timer\n"); - } - sub_peer->cw_timer_id = -1; - - ast_queue_control(peer_owner, AST_CONTROL_BUSY); - ast_indicate(owner, AST_CONTROL_UNHOLD); - } else if (sub->channel_state == INCALL && sub_peer->channel_state == ONHOLD) { - ast_debug(2, "Hanging up call on hold\n"); - - sub_peer = brcm_get_onhold_subchannel(p); - - ast_queue_control(peer_owner, AST_CONTROL_HANGUP); - sub_peer->channel_state = CALLENDED; - brcm_close_connection(sub_peer); - } - break; - /* Hangup current call and answer waiting call */ - case '1': - if (sub->channel_state == INCALL && (sub_peer->channel_state == CALLWAITING || sub_peer->channel_state == ONHOLD)) { - - /* Close connection and hangup active subchannel */ - if (owner) { - ast_queue_control(owner, AST_CONTROL_HANGUP); - } - sub->channel_state = CALLENDED; - brcm_close_connection(sub); - - if (sub_peer->channel_state == CALLWAITING) { - ast_log(LOG_WARNING, "R1 call waiting\n"); - /* Stop call waiting tone on current call */ - brcm_stop_callwaiting(p); - - if (ast_sched_del(sched, sub_peer->cw_timer_id)) { - ast_log(LOG_WARNING, "Failed to remove scheduled call waiting timer\n"); - } - sub_peer->cw_timer_id = -1; - - /* Pick up call waiting */ - if (!sub_peer->connection_init) { - ast_debug(9, "create_connection()\n"); - brcm_create_connection(sub_peer); - } - if (peer_owner) { - ast_queue_control(peer_owner, AST_CONTROL_ANSWER); - sub_peer->channel_state = INCALL; - } - } else if (sub_peer->channel_state == ONHOLD) { - ast_log(LOG_WARNING, "R1 Unholding\n"); - - /* Unhold inactive subchannel */ - if (peer_owner) { - brcm_unmute_connection(sub_peer); - ast_queue_unhold(peer_owner); - sub_peer->channel_state = INCALL; - } - } - } - break; - /* Answer waiting call and put other call on hold (switch calls) */ - case '2': - if (sub->channel_state == INCALL && (sub_peer->channel_state == CALLWAITING || sub_peer->channel_state == ONHOLD)) { - - brcm_mute_connection(sub); - if (owner) { - ast_queue_hold(owner, NULL); - } - - if (sub_peer->channel_state == CALLWAITING) { - ast_log(LOG_WARNING, "R2 Call waiting\n"); - - /* Stop call waiting tone on current call */ - brcm_stop_callwaiting(p); - - /* Cancel timer */ - if (ast_sched_del(sched, sub_peer->cw_timer_id)) { - ast_log(LOG_WARNING, "Failed to remove scheduled call waiting timer\n"); - } - sub_peer->cw_timer_id = -1; - - if (ast_sched_del(sched, sub_peer->cwBeep_timer_id)) { - ast_log(LOG_WARNING, "Failed to remove scheduled call waiting beep timer\n"); - } - sub_peer->cwBeep_timer_id = -1; - - /* Pick up call waiting */ - if (!sub_peer->connection_init) { - ast_debug(9, "create_connection()\n"); - brcm_create_connection(sub_peer); - } - if (peer_owner) { - ast_queue_control(peer_owner, AST_CONTROL_ANSWER); - sub_peer->channel_state = INCALL; - } - } else if (sub_peer->channel_state == ONHOLD) { - ast_log(LOG_WARNING, "R2 on hold\n"); - - /* Unhold inactive subchannel */ - if (peer_owner) { - brcm_unmute_connection(sub_peer); - ast_queue_unhold(peer_owner); - sub_peer->channel_state = INCALL; - } - } - - sub->channel_state = ONHOLD; - } - break; - /* Connect waiting call to existing call to create 3-way */ - case '3': - if (sub->channel_state == INCALL && sub_peer->channel_state == ONHOLD) { - ast_debug(2, "DTMF3 after HF\n"); - brcm_create_conference(p); - } - break; - /* Remote transfer held call to active call */ - case '4': - brcm_attended_call_transfer(sub, sub_peer, owner, peer_owner); - break; - case '5': - brcm_unattended_call_transfer(sub, sub_peer, owner, peer_owner); - break; - default: - ast_log(LOG_NOTICE, "Unhandled DTMF %c\n", p->dtmfbuf[0]); - break; - } -} - -static void handle_Rnumber_uk(struct brcm_subchannel *sub, struct brcm_subchannel *sub_peer, - struct ast_channel *owner, struct ast_channel *peer_owner) -{ - struct brcm_pvt *p = sub->parent; - - switch (p->dtmf_first) { - case '4': - brcm_attended_call_transfer(sub, sub_peer, owner, peer_owner); - break; - case '5': - brcm_unattended_call_transfer(sub, sub_peer, owner, peer_owner); - break; - default: - ast_log(LOG_ERROR, "Unhandled DTMF %c\n", p->dtmfbuf[0]); - break; - } -} - -static void handle_Rkey_uk(struct brcm_subchannel *sub, struct brcm_subchannel *sub_peer, - struct ast_channel *owner, struct ast_channel *peer_owner) -{ - struct brcm_pvt *p = sub->parent; - - if (sub->channel_state == INCALL && (sub_peer->channel_state == CALLWAITING || sub_peer->channel_state == ONHOLD)) { - brcm_mute_connection(sub); - if (sub_peer->channel_state == CALLWAITING) { - if (owner) { - ast_queue_hold(owner, NULL); - } - ast_log(LOG_NOTICE, " R in Call waiting\n"); - /* Cancel timers */ - if (ast_sched_del(sched, sub_peer->cw_timer_id)) { - ast_log(LOG_WARNING, "Failed to remove scheduled call waiting timer\n"); - } - sub_peer->cw_timer_id = -1; - if (ast_sched_del(sched, sub_peer->cwBeep_timer_id)) { - ast_log(LOG_WARNING, "Failed to remove scheduled call waiting beep timer\n"); - } - sub_peer->cwBeep_timer_id = -1; - /* Pick up call waiting */ - if (!sub_peer->connection_init) { - ast_debug(9, "create_connection()\n"); - brcm_create_connection(sub_peer); - } - if (peer_owner) { - ast_queue_control(peer_owner, AST_CONTROL_ANSWER); - sub_peer->channel_state = INCALL; - sub_peer->call_direction = INCOMING_CALL; - sub->channel_state = ONHOLD; - } - } else if (sub_peer->channel_state == ONHOLD) { - ast_log(LOG_NOTICE, "R on hold, line: %d action: %s\n", - p->line_id, sub->call_direction == INCOMING_CALL ? "Toggle" : "3way Conference"); - if (sub->call_direction == OUTGOING_CALL) { - sub->channel_state = INCALL; - sub->conf_timer_id = ast_sched_add(sched, dtmf_wait_timer, setup_conference_call_cb, p); - } else if (sub->call_direction == INCOMING_CALL){ - brcm_unmute_connection(sub_peer); - ast_queue_unhold(peer_owner); - sub_peer->channel_state = INCALL; - - brcm_mute_connection(sub); - ast_queue_hold(owner, NULL); - sub->channel_state = ONHOLD; - } - } - } else if ((sub->channel_state == DIALING || sub->channel_state == OFFHOOK || sub->channel_state == RINGBACK) && - sub_peer->channel_state != ONHOLD) { - ast_log(LOG_NOTICE, "R while offhook/dialing\n"); - brcm_cancel_dialing_timeouts(p); - brcm_reset_dtmf_buffer(p); - p->hf_detected = 0; - } -} -/* - * Perform actions for hook flash. - * Preconditions: One subchannel should be in CALLWAITING or ONHOLD, - * One subchannel should be in INCALL. - * channel locks are held - * brcm_pvt->lock is held - */ -static void handle_hookflash(struct brcm_subchannel *sub, struct brcm_subchannel *sub_peer, - struct ast_channel *owner, struct ast_channel *peer_owner) -{ - struct brcm_pvt *p = sub->parent; - struct ast_bridge *bridge; - struct ast_bridge_channel *bridge_channel; - - ast_log(LOG_NOTICE, "Hook Flash detected for phone line %d, p->dtmf_first = %d, flashSpec: %d\r\n", sub->parent->line_id, p->dtmf_first, - channel_config[p->line_id].flashSpec); - - if (owner) { - bridge = ast_channel_internal_bridge(owner); - } - else if (peer_owner) { - bridge = ast_channel_internal_bridge(peer_owner); - } else { - p->hf_detected = 0; - return; - } - - if (sub->channel_state == INCALL){ - if (bridge && bridge->num_active > max_sessions_per_extension) { - ast_log(LOG_WARNING, "Max call per extension limit exceeded [%d/%d]\n", bridge->num_active, max_sessions_per_extension); - p->hf_detected = 0; - return; - } - } else if (sub->channel_state == CALLING && sub_peer->channel_state != ONHOLD){ - //ignore the flash hook sending from endptmngr for DECT when got 183 from IVR system and interacting through DTMF - p->hf_detected = 0; - return; - } - - if (p->dtmf_first < 0) { - ast_log(LOG_NOTICE, "sub->channel_state = %s, sub_peer->channel_state = %s\n", state2str(sub->channel_state), state2str(sub_peer->channel_state)); - /* If current subchannel is in call and peer subchannel is idle, provide dialtone */ - if (sub->channel_state == INCALL && (sub_peer->channel_state == ONHOOK || sub_peer->channel_state == CALLENDED)) { - ast_debug(2, "R while in call and idle peer subchannel\n"); - - brcm_cancel_dialing_timeouts(p); - brcm_reset_dtmf_buffer(p); - p->hf_detected = 0; - - /* Put current call on hold */ - if (owner) { - if(bridge){ - AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { - ast_indicate(bridge_channel->chan, AST_CONTROL_HOLD); - } - } - - brcm_send_ubus_event("CALL_HOLD",p->line_id); - brcm_mute_connection(sub); - sub->channel_state = ONHOLD; - ast_queue_hold(owner, NULL); - } - - /* Provide new line */ - p->dialtone = DIALTONE_ON; - brcm_signal_dialtone(p); - sub_peer->channel_state = OFFHOOK; - sub_peer->call_direction = OUTGOING_CALL; - /* If offhook/dialing/calling and peer subchannel is on hold, switch call */ - } else if ((sub->channel_state == DIALING || - sub->channel_state == OFFHOOK || - sub->channel_state == AWAITONHOOK || - sub->channel_state == CALLING ) - && sub_peer->channel_state == ONHOLD) { - - ast_debug(2, "R while offhook/dialing and peer subchannel on hold\n"); - - brcm_cancel_dialing_timeouts(p); - brcm_reset_dtmf_buffer(p); - p->hf_detected = 0; - - if (sub->channel_state == OFFHOOK || sub->channel_state == AWAITONHOOK) { - brcm_stop_dialtone(p); - } - sub->channel_state = ONHOOK; - - /* Hang up current */ - if(owner) { - ast_queue_control(owner, AST_CONTROL_HANGUP); - } - - /* Pick up old */ - if (peer_owner) { - if(bridge){ - AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { - ast_indicate(bridge_channel->chan, AST_CONTROL_UNHOLD); - ast_log(LOG_NOTICE,"Pick up old unhold\n"); - } - } - brcm_send_ubus_event("CALL_UNHOLD",p->line_id); - brcm_unmute_connection(sub_peer); - ast_queue_unhold(peer_owner); - sub_peer->channel_state = INCALL; - } - /* Switch back to old call (remote hung up) */ - } else if ((sub->channel_state == ONHOOK || sub->channel_state == CALLENDED) && sub_peer->channel_state == ONHOLD) { - ast_debug(2, "R when idle and peer subchannel on hold\n"); - - brcm_cancel_dialing_timeouts(p); - p->hf_detected = 0; - if(sub->channel_state == RINGBACK) { - brcm_reset_dtmf_buffer(p); - brcm_stop_dialtone(p); - } - - /* Hang up current */ - if (owner) { - ast_queue_control(owner, AST_CONTROL_HANGUP); - } - - /* Pick up old */ - if (peer_owner) { - brcm_unmute_connection(sub_peer); - ast_queue_unhold(peer_owner); - brcm_send_ubus_event("CALL_UNHOLD",p->line_id); - sub_peer->channel_state = INCALL; - } - } else if (sub->channel_state == RINGBACK && sub_peer->channel_state == ONHOLD) { - ast_debug(4, "R during ringback\n"); - sub->channel_state = INCALL; - sub->conf_timer_id = ast_sched_add(sched, dtmf_wait_timer, setup_conference_call_cb, p); - } - - if (channel_config[p->line_id].flashSpec == FLASH_SPEC_UK) - handle_Rkey_uk(sub, sub_peer, owner, peer_owner); - } - - if (p->dtmf_first > 0) { - if (channel_config[p->line_id].flashSpec == FLASH_SPEC_ETSI) - handle_Rnumber_etsi(sub, sub_peer, owner, peer_owner); - else - handle_Rnumber_uk(sub, sub_peer, owner, peer_owner); - } - brcm_reset_dtmf_buffer(p); -} - -static void handle_dect_event(struct brcm_subchannel *sub, struct brcm_subchannel *sub_peer, - struct ast_channel *owner, struct ast_channel *peer_owner, enum LINE_EVENT event) -{ - struct brcm_pvt *p = sub->parent; - - ast_log(LOG_NOTICE, "%s sub->channel_state = %d, sub_peer->channel_state = %d\n", __func__, sub->channel_state, sub_peer->channel_state); - if (event == EVENT_JOIN) { - ast_log(LOG_NOTICE, "Join detected for phone line %d\n", sub->parent->line_id); - - // Start 3way call conference - if (sub->channel_state == INCALL && sub_peer->channel_state == ONHOLD) - sub->conf_timer_id = ast_sched_add(sched, dtmf_wait_timer, setup_conference_call_cb, p); - } - else if (event == EVENT_SWITCH) { - ast_log(LOG_NOTICE, "Switch detected for phone line %d\n", sub->parent->line_id); - // Toggle calls - if (sub->channel_state == INCALL && sub_peer->channel_state == ONHOLD) { - brcm_unmute_connection(sub_peer); - ast_queue_unhold(peer_owner); - sub_peer->channel_state = INCALL; - - brcm_mute_connection(sub); - ast_queue_hold(owner, NULL); - sub->channel_state = ONHOLD; - } - } - else if (event == EVENT_RELEASE) { - ast_log(LOG_NOTICE, "EVENT_RELEASE detected for phone line %d\n", sub->parent->line_id); - if (sub->channel_state == INCALL && sub_peer->channel_state != CALLWAITING) { - ast_log(LOG_ERROR, "Hanging up call\n"); - ast_queue_control(owner, AST_CONTROL_HANGUP); - } else if ((sub->channel_state == RINGBACK || sub->channel_state == CALLENDED) && sub_peer->channel_state == ONHOLD) { - endpt_signal(sub->parent->line_id, "ringback", "off", NULL); - ast_queue_control(owner, AST_CONTROL_HANGUP); - brcm_unmute_connection(sub_peer); - ast_queue_unhold(peer_owner); - sub_peer->channel_state = INCALL; - } else if (sub->channel_state == AWAITONHOOK && sub_peer->channel_state == ONHOLD) { - sub->channel_state = ONHOOK; - brcm_unmute_connection(sub_peer); - ast_queue_unhold(peer_owner); - sub_peer->channel_state = INCALL; - } - } - brcm_reset_dtmf_buffer(p); -} - -static void handle_dtmf(enum LINE_EVENT event, - struct brcm_subchannel *sub, struct brcm_subchannel *sub_peer, - struct ast_channel *owner, struct ast_channel *peer_owner) -{ - struct brcm_pvt *p; - const DTMF_CHARNAME_MAP *dtmfMap = dtmf_to_charname; - struct timeval tim; - - /* Lookup event to find corresponding DTMF */ - while (dtmfMap->event != event) { - dtmfMap++; - if (dtmfMap->event == EVENT_LAST) { - /* DTMF not found. Should not be reached. */ - ast_log(LOG_WARNING, "Failed to handle DTMF. Event not found.\n"); - return; - } - } - - char dtmf_button = dtmfMap->c; - gettimeofday(&tim, NULL); - p = sub->parent; - - if (p->dtmf_first < 0) { - p->dtmf_first = dtmf_button; - ast_debug(9,"Pressed DTMF %s\n", dtmfMap->name); - if (owner && brcm_should_relay_dtmf(sub)) { - // INCALL - send_outgoing_dtmf(owner, dtmf_button, AST_FRAME_DTMF_BEGIN); - } - - /* Do not send AST_FRAME_DTMF_BEGIN to allow DSP-generated tone to pass through */ - } - else if (p->dtmf_first == dtmf_button) { - ast_log(LOG_NOTICE,"Released DTMF %s\n", dtmfMap->name); - //ast_log(LOG_DTMF, "Detected DTMF %c, line %d\n", dtmfMap->c, sub->parent->line_id); - - if (p->hf_detected) { - ast_debug(2, "DTMF after HF\n"); - p->hf_detected = 0; - if (sub->channel_state == INCALL && - (brcm_in_callwaiting(p) || brcm_in_onhold(p) || brcm_in_conference(p))) { - handle_hookflash(sub, sub_peer, owner, peer_owner); - } else { - /* HF while not in a call doesn't make sense */ - ast_debug(2, "DTMF after HF while not in call. \ - state: %d, \ - callwaiting: %d, \ - onhold: %d, \ - conference: %d\n", - sub->channel_state, - brcm_in_callwaiting(p), - brcm_in_onhold(p), - brcm_in_conference(p)); - } - } else { - //ast_debug(5,"DTMF %s in state %s.\n", dtmfMap->name, state2str(sub->channel_state)); - p->dtmfbuf[p->dtmf_len] = dtmf_button; - p->dtmf_len++; - p->dtmfbuf[p->dtmf_len] = '\0'; - p->dtmf_first = -1; - if (sub->channel_state == OFFHOOK) { - sub->channel_state = DIALING; - //ast_debug(5,"Set to state %s.\n", state2str(sub->channel_state)); - } - else if (sub->channel_state != INCALL) { - struct ast_frame f = { 0, }; - f.subclass.integer = dtmf_button; - f.src = "TELCHAN"; - f.frametype = AST_FRAME_DTMF_END; - - //ast_debug(5,"DTMF %s not in call.\n", dtmfMap->name); - - if (owner) { - //ast_debug(5,"DTMF %s queuing frame.\n", dtmfMap->name); - ast_queue_frame(owner, &f); - } - } else { - if (owner) { - // INCALL - send_outgoing_dtmf(owner, dtmf_button, AST_FRAME_DTMF_END); - } - } - } - } - else { - p->dtmf_first = -1; - } -} - -static void send_outgoing_dtmf(struct ast_channel *owner, char dtmf_button, int frametype) { - struct ast_frame fr; - - if (!owner) return; - - if (frametype != AST_FRAME_DTMF_BEGIN && - frametype != AST_FRAME_DTMF_END) { - return; - } - - if (ast_channel_state(owner) != AST_STATE_UP && - ast_channel_state(owner) != AST_STATE_RING) { - return; - } - - memset(&fr, 0, sizeof fr); - ast_debug(2, "Sending DTMF %c %s\n", dtmf_button, ast_channel_name(owner)); - fr.src = brcm_tech.type; - fr.frametype = frametype; - fr.subclass.integer = dtmf_button; - - ast_queue_frame(owner, &fr); -} - -/* Handle audio packets from endptmngr. */ -static void audio_packet_handler(pe_packet_t *p) { - struct brcm_subchannel *sub; - int rtp_packet_type = BRCM_UNKNOWN, drop_frame = 0; - struct ast_frame fr = {0}; - audio_packet_t *ap = (audio_packet_t *)p->data; - struct brcm_pvt *pvt; - - fr.src = "TELCHAN"; - rtp_packet_type = brcm_classify_rtp_packet(ap->rtp[1]); - pvt = brcm_get_pvt_from_lineid(iflist, ap->line); - sub = brcm_get_active_subchannel(pvt); - if (!pvt || !sub) { - ast_log(LOG_ERROR, "Failed to find subchannel for %s/%d/%d\n", - fr.src, ap->line, ap->cnx_id); - endpt_connection(ap->line, ap->cnx_id, "destroy"); // Request line close - return; - } - - //pvt_lock(sub->parent, "brcm monitor packets"); - //ast_mutex_lock(&sub->parent->lock); - struct ast_channel *owner = NULL; - if (sub->owner) { - ast_channel_ref(sub->owner); - owner = sub->owner; - } - - /* We seem to get packets from DSP even if connection is muted (perhaps muting only affects packet callback). - * Drop packets if subchannel is on hold. */ - /* Handle rtp packet according to classification */ - if (sub->channel_state != ONHOLD && rtp_packet_type == BRCM_AUDIO && - (ap->rtp[0] & 0x80) && ap->rtp_size) { - fr.frametype = AST_FRAME_VOICE; - fr.offset = 0; - fr.data.ptr = ap->rtp + 12; - fr.datalen = ap->rtp_size - 12; - - switch (ap->rtp[1]) { - case PCMU: - fr.subclass.format = ast_format_ulaw; - fr.samples = 160; - break; - case PCMA: - fr.subclass.format = ast_format_alaw; - fr.samples = 160; - break; - case G726: - fr.subclass.format = ast_format_g726; - fr.samples = 160; //for 20 ms frame size - break; - case G723: - fr.subclass.format = ast_format_g723; - fr.samples = 240; - break; - case G729: - fr.subclass.format = ast_format_g729; - fr.samples = 80; //for 10 ms frame size - break; - case G722: - fr.subclass.format = ast_format_g722; - fr.samples = 160; - break; - case CN: - fr.frametype = AST_FRAME_CNG; - fr.subclass.integer = ap->rtp[12]; - break; - default: - ast_log(LOG_WARNING, "Unknown rtp codec id [%d], %d\n", ap->rtp[1], p->data[1]); - return; - } - } else if (rtp_packet_type == BRCM_RTCP_SR || rtp_packet_type == BRCM_RTCP_RR) { - fr.frametype = AST_FRAME_RTCP; - fr.data.ptr = ap->rtp; - fr.datalen = ap->rtp_size; - fr.subclass.integer = (rtp_packet_type == BRCM_RTCP_SR ? RTCP_SR : RTCP_RR); - } else { - //ast_debug(5, "Dropping RTP frame of type %d.\n", rtp_packet_type); - drop_frame=1; - //pvt_unlock(sub->parent); - } - //ast_mutex_unlock(&sub->parent->lock); - //pvt_unlock(sub->parent); - - if (owner) { - if (!drop_frame && (ast_channel_state(owner) == AST_STATE_UP || ast_channel_state(owner) == AST_STATE_RING)) { - ast_queue_frame(owner, &fr); - } - ast_channel_unref(owner); - } -} - -static void brcm_cancel_dialing_timeouts(struct brcm_pvt *p) -{ - //If we have interdigit timeout, cancel it - if (p->interdigit_timer_id > 0) { - p->interdigit_timer_id = ast_sched_del(sched, p->interdigit_timer_id); - } - - //If we have a autodial timeout, cancel it - if (p->autodial_timer_id > 0) { - p->autodial_timer_id = ast_sched_del(sched, p->autodial_timer_id); - } - - //If we have a dialtone timeout, cancel it - if (p->dialtone_timeout_timer_id > 0) { - p->dialtone_timeout_timer_id = ast_sched_del(sched, p->dialtone_timeout_timer_id); - } -} - -static int brcm_should_relay_dtmf(const struct brcm_subchannel *sub) -{ - if (sub->channel_state == INCALL && sub->parent->hf_detected == 0) { - return 1; - } - return 0; -} - -/* - * Call a CLI command as specfied by cmd. The output is directed to a temp file and then - * each line of output is passed to the check_output callback function. The data parameter - * is also passed to the callback and can be used by the callback function to store any - * data required to help process the output. - * - * This function returns 1 if the check_output callback function returns 1 otherwise it - * will return 0 once all output has been processed. - */ -static int call_cli_command(const char *cmd, int (*check_output)(const char *, const char *data), const char *data) -{ - int res = 0; - char template[] = "/tmp/ast-brcm-XXXXXX"; /* template for temporary file */ - char line[160]; - int fd_temp = -1; - FILE *fp = NULL; - - if ((fd_temp = mkstemp(template)) < 0) { - ast_log(LOG_ERROR, "Failed to create temporary file for command \"%s\": %s\n", - cmd, strerror(errno)); - return res; - } - - if (ast_cli_command(fd_temp, cmd) == 0) { - close(fd_temp); - fd_temp = -1; - - fp = fopen(template, "r"); - if (fp) { - while (fgets(line, sizeof(line), fp) != NULL) { - if ((*check_output)(line, data)) { - res = 1; - break; - } - } - } - } else { - ast_log(LOG_ERROR, "Execution CLI \"%s\" failed\n", cmd); - } - - if (fd_temp >= 0) - close(fd_temp); - if (fp) - fclose(fp); - unlink(template); - return res; -} - -static int check_voicemail_messages_waiting(const char *line, const char *data) -{ - return (strstr(line, "messages_waiting") != NULL && strstr(line, "yes") != NULL); -} - -static int check_voicemail_dialtone(const char *line, const char *data) -{ - int ret = 1; - - if (strstr(line, "mwi_dialtone_state") != NULL && strstr(line, "off") != NULL) - mwi_dialtone_state = DIALTONE_MWI_OFF; - else if (strstr(line, "mwi_dialtone_state") != NULL && strstr(line, "congestion") != NULL) - mwi_dialtone_state = DIALTONE_CONGESTION; - else if (strstr(line, "mwi_dialtone_state") != NULL && strstr(line, "special") != NULL) - mwi_dialtone_state = DIALTONE_SPECIAL_CONDITION; - else { - mwi_dialtone_state = DIALTONE_ON; - ret = 0; - } - - return ret; -} - -static int voicemail_messages_waiting(const char *sip_account) -{ - char cmd[32]; - sprintf(cmd, "pjsip show endpoint %s", sip_account); - if (call_cli_command(cmd, &check_voicemail_messages_waiting, NULL)) - return call_cli_command(cmd, &check_voicemail_dialtone, NULL); - else - return 0; -} - -static int check_endpoint_cw_enabled(const char *line, const char *data) -{ - return (strstr(line, "call_waiting_enabled") != NULL && strstr(line, "true") != NULL); - -} - -static int check_endpoint_state_in_use(const char *line, const char *data) -{ - return (strstr(line, "Endpoint:") != NULL && strstr(line, "In use") != NULL); - -} - -static int is_call_waiting_enabled(const char *sip_account) -{ - char cmd[32]; - // do not check pjsip endpoint configuration if it is internal call - if (strcmp(sip_account, "local_extensions") == 0) - return 0; - sprintf(cmd, "pjsip show endpoint %s", sip_account); - return call_cli_command(cmd, &check_endpoint_cw_enabled, NULL); -} -static int has_call_in_sip_client(const char *sip_account) -{ - char cmd[32]; - // do not check pjsip endpoint configuration if it is internal call - if (strcmp(sip_account, "local_extensions") == 0) - return 0; - sprintf(cmd, "pjsip show endpoint %s", sip_account); - return call_cli_command(cmd, &check_endpoint_state_in_use, NULL); -} - -static int check_is_sip_account_registered(const char *line, const char *sip_account) -{ - /* The example output of the command is shown below. - * Some extra spaces are removed since the line is too long. - * sip0:5060 N 1313@10.0.2. 285 Registered Mon, 17 Jun 2019 17:12:58 */ - return ((strstr(line, sip_account) != NULL) && (strstr(line, "Registered") != NULL)); -} - -static int is_sip_account_registered(const char *sip_account) -{ - const char *cmd = "pjsip show registrations"; - return call_cli_command(cmd, &check_is_sip_account_registered, sip_account); -} - -static int check_endpoint_status(const char *line, const char *sip_account) -{ - int ret = 1; - - if((strstr(line, sip_account) != NULL) && (strstr(line, "Not in use") != NULL)) { - strcpy(lineCallStatus, "Idle\0"); - } else if((strstr(line, sip_account) != NULL) && (strstr(line, "In use") != NULL)) { - strcpy(lineCallStatus, "Connected\0"); - } else if((strstr(line, sip_account) != NULL) && (strstr(line, "Busy") != NULL)) { - strcpy(lineCallStatus, "Disconnected\0"); - } else if((strstr(line, sip_account) != NULL) && (strstr(line, "Ringing") != NULL)) { - strcpy(lineCallStatus, "Dialing\0"); - } else if((strstr(line, sip_account) != NULL) && (strstr(line, "Ring+Inuse") != NULL)) { - strcpy(lineCallStatus, "Dialing\0"); - } else if((strstr(line, sip_account) != NULL) && (strstr(line, "On Hold") != NULL)) { - strcpy(lineCallStatus, "Connected\0"); - } else if((strstr(line, sip_account) != NULL) && (strstr(line, "Alerting") != NULL)) { - strcpy(lineCallStatus, "Alerting\0"); - } else { - strcpy(lineCallStatus, "Idle\0"); - ret = 0; - } - return ret; -} - -static int getLineState(const char *sip_account, char *lineCallStatus) -{ - char cmd[32]; - sprintf(cmd, "pjsip show endpoint %s", sip_account); - - return call_cli_command(cmd, &check_endpoint_status, sip_account); -} - -static void brcm_process_event(struct endpt_event *ev) -{ - struct brcm_pvt *p = NULL; - struct brcm_subchannel *sub = NULL; - struct brcm_subchannel *sub_peer = NULL; - struct ast_channel *owner = NULL; - struct ast_channel *peer_owner = NULL; - - ast_debug(3, "Received event %s from line %d\n", ev->name, ev->line); - - // Ignore events when the telline is not enabled - if (!channel_config[ev->line].enabled) { - ast_debug(3, "Line %d disabled, ignore event %s!\n", ev->line, ev->name); - return; - } - - p = brcm_get_pvt_from_lineid(iflist, ev->line); - if (!p) { - ast_debug(3, "No pvt with the correct line_id %d found!\n", ev->line); - return; - } - - pvt_lock(p, "brcm monitor events"); - sub = brcm_get_active_subchannel(p); - sub_peer = brcm_subchannel_get_peer(sub); - if (!sub || !sub_peer) { - ast_debug(3, "sub-channels are not found\n"); - pvt_unlock(p); - return; - } - if (sub->owner) { - ast_channel_ref(sub->owner); - owner = sub->owner; - } - if (sub_peer->owner) { - ast_channel_ref(sub_peer->owner); - peer_owner = sub_peer->owner; - } - pvt_unlock(p); - - ast_debug(3, "call state:%s, peer call state:%s\n", state2str(sub->channel_state), - state2str(sub_peer->channel_state)); - - /* Get locks in correct order */ - if (owner && peer_owner) { - if (owner < peer_owner) { - ast_channel_lock(owner); - ast_channel_lock(peer_owner); - } else { - ast_channel_lock(peer_owner); - ast_channel_lock(owner); - } - } else if (owner) { - ast_channel_lock(owner); - } else if (peer_owner) { - ast_channel_lock(peer_owner); - } - - pvt_lock(p, "brcm monitor events"); - if (sub) { - switch (ev->event) { - case EVENT_OFFHOOK: { - if (!strlen(p->extensionCallStatus) || sub->channel_state == ONHOOK) - strncpy(p->extensionCallStatus, "Dialing", CALL_STATUS_MAX_LEN); - else if (sub->channel_state == RINGING) - strncpy(p->extensionCallStatus, "Connected", CALL_STATUS_MAX_LEN); - - /* Reset the dtmf buffer */ - memset(p->dtmfbuf, 0, sizeof(p->dtmfbuf)); - p->dtmf_len = 0; - p->dtmf_first = -1; - p->dtmfbuf[p->dtmf_len] = '\0'; - sub->channel_state = OFFHOOK; - - if (owner) { - if (!sub->connection_init) { - ast_debug(9, "create_connection()\n"); - brcm_stop_dialtone(p); - brcm_create_connection(sub); - } - - if (sub->cw_timer_id > -1) { - /* Picking up during reminder ringing for call waiting */ - if (ast_sched_del(sched, sub->cw_timer_id) < 0) - ast_debug(3, "Error deleting timer\n"); - sub->cw_timer_id = -1; - } - - if (sub->cwBeep_timer_id > -1) { - /* Picking up during reminder ringing for call waiting */ - if (ast_sched_del(sched, sub->cwBeep_timer_id) < 0) - ast_debug(3, "Error deleting timer\n"); - sub->cwBeep_timer_id = -1; - } - - sub->channel_state = INCALL; - ast_queue_control(owner, AST_CONTROL_ANSWER); - brcm_send_ubus_event("ANSWERED_CALL",p->line_id); - } else if (sub_peer->channel_state == ONHOLD) { - /* Picking up during reminder ringing for call on hold */ - if (ast_sched_del(sched, sub_peer->onhold_hangup_timer_id) < 0) { - ast_debug(3, "Error deleting timer\n"); - } - sub_peer->onhold_hangup_timer_id = -1; - sub->channel_state = CALLENDED; - sub_peer->channel_state = INCALL; - ast_queue_control(peer_owner, AST_CONTROL_ANSWER); - brcm_unmute_connection(sub_peer); - endpt_connection(sub_peer->parent->line_id, sub_peer->call_id, "create"); - ast_queue_unhold(peer_owner); - brcm_send_ubus_event("CALL_UNHOLD", p->line_id); - } else if (sub->channel_state == OFFHOOK) { - /* EVENT_OFFHOOK changed endpoint state to OFFHOOK, apply dialtone */ - if ( p->context[0] != '\0' && is_sip_account_registered(p->context)) { - ast_debug(9, "Resetting dial tones.\n"); - channel_settings *s = &channel_config[p->line_id]; - p->dialtone = voicemail_messages_waiting(p->context) ? mwi_dialtone_state : s->mwi_enabled ? DIALTONE_SPECIAL_CONDITION: DIALTONE_ON; - brcm_signal_dialtone(p); - brcm_send_ubus_event("DIALTONE_ON", p->line_id); - - if (strlen(s->autodial_ext)) { - /* Schedule autodial timeout if autodial extension is set */ - p->autodial_timer_id = ast_sched_add(sched, s->autodial_timeoutmsec, handle_autodial_timeout, p); - } else { - /* No autodial, schedule dialtone timeout */ - ast_verbose("Scheduling dialtone timeout in %dms\n", s->dialtone_timeoutmsec); - p->dialtone_timeout_timer_id = ast_sched_add(sched, s->dialtone_timeoutmsec, handle_dialtone_timeout, p); - } - } else { - ast_debug(9, "OFFHOOK but SIP account not registered\n"); - } - } - break; - } - case EVENT_ONHOOK: { - strncpy(p->extensionCallStatus, "Idle", CALL_STATUS_MAX_LEN); - if (sub->channel_state == OFFHOOK || sub->channel_state == AWAITONHOOK) { - /* Received EVENT_ONHOOK in state OFFHOOK/AWAITONHOOK, stop dial/congestion tone */ - brcm_stop_dialtone(p); - } else if (sub->channel_state == RINGBACK) { - /* Outgoing unanswered call - rtp stats need to be collected */ - brcm_getRtpStats(owner); - } - - sub->channel_state = ONHOOK; - brcm_cancel_dialing_timeouts(p); - p->hf_detected = 0; - - if (sub_peer->channel_state == CALLENDED) - sub_peer->channel_state = ONHOOK; - - /* Reset the dtmf buffer */ - memset(p->dtmfbuf, 0, sizeof(p->dtmfbuf)); - p->dtmf_len = 0; - p->dtmf_first = -1; - p->dtmfbuf[p->dtmf_len] = '\0'; - - if (owner) { - ast_queue_control(owner, AST_CONTROL_HANGUP); - } - - if (sub_peer->channel_state == CALLWAITING) { - /* Remind user of waiting call */ - sub_peer->channel_state = RINGING; - p->tech->signal_ringing(p); - brcm_send_ubus_event("RINGING",p->line_id); - } else if (sub_peer->channel_state == ONHOLD) { - /* Remind user of call on hold */ - sub_peer->onhold_hangup_timer_id = ast_sched_add(sched, onholdhanguptimeout * 1000, onholdhanguptimeout_cb, sub_peer); - ast_debug(2, "Forgotten call on hold for %s!\n", ast_channel_name(peer_owner)); - endpt_connection(sub_peer->parent->line_id, sub_peer->call_id, "destroy"); - usleep(500000); - p->tech->signal_ringing_callerid_pending(p); - p->tech->signal_callerid(sub_peer->owner, sub_peer); - sub->channel_state = RINGING; - ast_queue_control(peer_owner, AST_CONTROL_RINGING); - brcm_send_ubus_event("RINGING",p->line_id); - } else if (peer_owner && sub_peer->channel_state != TRANSFERING) { - /* Hangup peer subchannels in call or on hold */ - ast_debug(2, "Hanging up call (not transfering)\n"); - ast_queue_control(peer_owner, AST_CONTROL_HANGUP); - } - break; - } - case EVENT_DTMF0: - case EVENT_DTMF1: - case EVENT_DTMF2: - case EVENT_DTMF3: - case EVENT_DTMF4: - case EVENT_DTMF5: - case EVENT_DTMF6: - case EVENT_DTMF7: - case EVENT_DTMF8: - case EVENT_DTMF9: - case EVENT_DTMFA: - case EVENT_DTMFB: - case EVENT_DTMFC: - case EVENT_DTMFD: - case EVENT_DTMFS: - case EVENT_DTMFH: - { - brcm_cancel_dialing_timeouts(p); - - unsigned int old_state = sub->channel_state; - handle_dtmf(ev->event, sub, sub_peer, owner, peer_owner); - if (sub->channel_state == DIALING && old_state != sub->channel_state) { - /* DTMF event took channel state to DIALING. Stop dial tone. */ - ast_debug(2, "Dialing. Stop dialtone.\n"); - brcm_stop_dialtone(p); - } - - if (sub->channel_state == DIALING) { - ast_debug(2, "Handle DTMF calling\n"); - handle_dtmf_calling(sub); - } - break; - } - - case EVENT_FLASH: - p->hf_detected = 1; - handle_hookflash(sub, sub_peer, owner, peer_owner); - break; - - case EVENT_CALL_REJECT: - if (sub->channel_state == RINGING || (sub->channel_state == INCALL && sub_peer->channel_state == CALLWAITING)) { - const char *linkedid = ast_channel_linkedid(owner); - const char *peer_linkedid; - struct brcm_pvt *pvt = iflist; - int i; - - if (peer_owner) - peer_linkedid = ast_channel_linkedid(peer_owner); - - /* Release all requested channels for the same incoming call when one channel rejects, e.g. from DECT handset */ - while (pvt) { - pvt_lock(pvt, "lock pvt for call reject"); - - for (i = 0; i < NUM_SUBCHANNELS; i++) { - struct brcm_subchannel *sc = pvt->sub[i]; - if ((sc->channel_state == RINGING || sc->channel_state == CALLWAITING) && sc->owner) { - const char *link = ast_channel_linkedid(sc->owner); - - if (sub->channel_state == RINGING && (link == linkedid || strcmp(link, linkedid) == 0)) { - ast_debug(2, "Hang up the unanswered call on channel %s since %s has rejected\n", - ast_channel_name(sc->owner), ast_channel_name(owner)); - - ast_channel_hangupcause_set(sc->owner, AST_CAUSE_CALL_REJECTED); - ast_queue_control(sc->owner, AST_CONTROL_HANGUP); - } else if (sub->channel_state == INCALL && (link == peer_linkedid || strcmp(link, peer_linkedid) == 0)) { - ast_debug(2, "Hang up the unanswered call on channel %s since %s has rejected\n", - ast_channel_name(sc->owner), ast_channel_name(peer_owner)); - - ast_channel_hangupcause_set(sc->owner, AST_CAUSE_CALL_REJECTED); - ast_queue_control(sc->owner, AST_CONTROL_HANGUP); - } - } - } - - pvt_unlock(pvt); - pvt = brcm_get_next_pvt(pvt); - } - - brcm_send_ubus_event("CALL_REJECTED", ev->line); - } else if (sub->channel_state == CALLENDED && owner) { - ast_queue_control(owner, AST_CONTROL_HANGUP); - } - break; - case EVENT_DECT_UNAVAILABLE: - ast_channel_hangupcause_set(owner, AST_CAUSE_USER_BUSY); - ast_queue_control(owner, AST_CONTROL_BUSY); - case EVENT_SWITCH: - case EVENT_JOIN: - case EVENT_RELEASE: - ast_debug(1, "EVENT %d from dect received\n", ev->event); - handle_dect_event(sub, sub_peer, owner, peer_owner, ev->event); - break; - default: - ast_debug(1, "Ignore event %s\n", ev->name); - break; - } - } - pvt_unlock(p); - - if (owner) { - ast_channel_unlock(owner); - ast_channel_unref(owner); - } - if (peer_owner) { - ast_channel_unlock(peer_owner); - ast_channel_unref(peer_owner); - } -} - -/* Load settings for each line */ -static void brcm_initialize_pvt(struct brcm_pvt *p) -{ - channel_settings *s = &channel_config[p->line_id]; - - ast_copy_string(p->language, s->language, sizeof(p->language)); - ast_copy_string(p->context, s->context, sizeof(p->context)); - ast_copy_string(p->context_direct, s->context_direct, sizeof(p->context_direct)); - ast_copy_string(p->cid_num, s->cid_num, sizeof(p->cid_num)); - ast_copy_string(p->cid_name, s->cid_name, sizeof(p->cid_name)); -} - -static struct brcm_pvt *brcm_allocate_pvt(void) -{ - /* Make a brcm_pvt structure for this interface */ - struct brcm_pvt *tmp; - - tmp = ast_calloc(1, sizeof(*tmp)); - if (tmp) { - struct brcm_subchannel *sub; - int i; - - for (i=0; i<NUM_SUBCHANNELS; i++) { - sub = ast_calloc(1, sizeof(*sub)); - if (sub) { - sub->id = i; - sub->call_id = CALLID_INVALID; - sub->owner = NULL; - sub->connection_id = -1; - sub->connection_init = 0; - sub->channel_state = ONHOOK; - sub->time_stamp = 0; - sub->ssrc = 0; - sub->codec = -1; - sub->parent = tmp; - sub->cw_timer_id = -1; - sub->cwBeep_timer_id = -1; - sub->conf_timer_id = -1; - sub->r4_hangup_timer_id = -1; - sub->onhold_hangup_timer_id = -1; - sub->period = 20; // 20 ms - sub->conference_initiator = 0; - tmp->sub[i] = sub; - sub->jitter_count = 0; - sub->farEndInterrivalJitter = 0; - sub->blind_xfer_target[0] = '\0'; - ast_debug(2, "subchannel created\n"); - } else { - ast_log(LOG_ERROR, "no subchannel created\n"); - } - } - tmp->line_id = -1; - tmp->dtmf_len = 0; - tmp->dtmf_first = -1; - tmp->lastformat = NULL; - tmp->lastinput = NULL; - memset(tmp->ext, 0, sizeof(tmp->ext)); - tmp->next = NULL; - tmp->dialtone = DIALTONE_UNKNOWN; - tmp->interdigit_timer_id = -1; - tmp->autodial_timer_id = -1; - ast_mutex_init(&tmp->lock); - tmp->tech = &fxs_tech; - } - return tmp; -} - - -static void brcm_create_pvts(struct brcm_pvt *p, int mode) { - int i; - struct brcm_pvt *tmp = iflist; - struct brcm_pvt *tmp_next; - - for (i=0; i<num_endpoints ; i++) { - tmp_next = brcm_allocate_pvt(); - if (tmp == NULL) { - iflist = tmp_next; //First loop round, set iflist to point at first pvt - tmp = tmp_next; - tmp->next = NULL; - } else { - tmp->next = tmp_next; - tmp_next->next = NULL; - tmp = tmp_next; - } - } -} - -static void brcm_assign_line_id(struct brcm_pvt *p) -{ - struct brcm_pvt *tmp = p; - int i; - /* Assign line_id's */ - for (i=0 ; i<num_endpoints ; i++) { - tmp->line_id = i; - brcm_initialize_pvt(tmp); - int j; - for (j=0; j<NUM_SUBCHANNELS; j++) { - tmp->sub[j]->channel_state = ONHOOK; - } - tmp = tmp->next; - } -} - -static int brcm_in_dialing(const struct brcm_pvt *p) -{ - int i; - for (i=0; i<NUM_SUBCHANNELS; i++) { - if (p->sub[i]->channel_state == DIALING) { - return 1; - } - } - - return 0; -} - -static int brcm_in_ringback(const struct brcm_pvt *p) -{ - int i; - for (i=0; i<NUM_SUBCHANNELS; i++) { - if (p->sub[i]->channel_state == RINGBACK) { - return 1; - } - } - - return 0; -} - -static int brcm_in_call(const struct brcm_pvt *p) -{ - int i; - for (i=0; i<NUM_SUBCHANNELS; i++) { - if (p->sub[i]->channel_state == INCALL) { - return 1; - } - } - - return 0; -} - -static int brcm_in_callwaiting(const struct brcm_pvt *p) -{ - int i; - for (i=0; i<NUM_SUBCHANNELS; i++) { - if (p->sub[i]->channel_state == CALLWAITING) { - return 1; - } - } - - return 0; -} - -static int brcm_in_transferring(const struct brcm_pvt *p) -{ - int i; - for (i=0; i<NUM_SUBCHANNELS; i++) { - if (p->sub[i]->channel_state == TRANSFERING) { - return 1; - } - } - - return 0; -} - -static int brcm_in_onhold(const struct brcm_pvt *p) -{ - int i; - for (i=0; i<NUM_SUBCHANNELS; i++) { - if (p->sub[i]->channel_state == ONHOLD) { - return 1; - } - } - - return 0; -} - -static int brcm_in_conference(const struct brcm_pvt *p) -{ - return (p->sub[0]->channel_state == INCALL && - p->sub[1]->channel_state == INCALL) || - p->sub[0]->conference_initiator || - p->sub[1]->conference_initiator; -} - -/* - * Return idle subchannel - */ -struct brcm_subchannel *brcm_get_idle_subchannel(const struct brcm_pvt *p) -{ - int i; - for (i=0; i<NUM_SUBCHANNELS; i++) { - if (p->sub[i]->channel_state == ONHOOK || p->sub[i]->channel_state == CALLENDED) { - return p->sub[i]; - } - } - return NULL; -} - -static struct ast_channel *brcm_request(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *dest, int *cause) -{ - struct brcm_pvt *p; - struct brcm_subchannel *sub = NULL; - struct ast_channel *tmp = NULL; - struct ast_str * buf = ast_str_create(256); - int line_id = -1; - - ast_debug(3, "requestor=%p(%s), dest=[%s], type=[%s]\n", requestor, requestor ? ast_channel_name(requestor) : "", - dest ? dest : "", type ? type : ""); - - ast_format_cap_get_names(cap, &buf); - ast_debug(1, "Asked to create a channel with formats: %s\n", ast_str_buffer(buf)); - - /* Search for an unowned channel */ - if (ast_mutex_lock(&iflock)) { - ast_log(LOG_ERROR, "Unable to lock interface list???\n"); - return NULL; - } - - - /* Get line id */ - line_id = atoi((char*)dest); - //ast_debug(1, "brcm_request = %s, line_id=%d, format %x\n", (char*) data, line_id, (unsigned int) format); - - /* Map id to the correct pvt */ - p = brcm_get_pvt_from_lineid(iflist, line_id); - - /* If the id doesn't exist (p==NULL) use 0 as default */ - if (!p) { - ast_log(LOG_ERROR, "Port id %s not found using default 0 instead.\n", (char*) dest); - p = iflist; - } - - pvt_lock(p, "brcm request"); - //ast_mutex_lock(&p->lock); - - sub = brcm_get_idle_subchannel(p); - - /* Check that the request has an allowed format */ - if (!(ast_format_cap_has_type(cap, AST_MEDIA_TYPE_AUDIO))) { - struct ast_str *codec_buf = ast_str_alloca(64); - ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%s'\n", ast_format_cap_get_names(cap, &codec_buf)); - } else if (sub) { - sub->channel_state = ALLOCATED; - sub->connection_id = ast_atomic_fetchadd_int((int *)¤t_connection_id, +1); - tmp = brcm_new(sub, AST_STATE_DOWN, sub->parent->ext, sub->parent->context, assignedids, requestor ? requestor : NULL, cap); - } else { - *cause = AST_CAUSE_BUSY; - ast_log(LOG_NOTICE, "Idle subchannel not found\n"); - } - - pvt_unlock(p); - ast_mutex_unlock(&iflock); - - if (tmp) - ast_debug(3, "Created a new ast_channel %s, uniqid:%s, linkedid:%s\n", ast_channel_name(tmp), ast_channel_uniqueid(tmp), - ast_channel_linkedid(tmp)); - return tmp; -} - - -static void brcm_lock_pvts(void) -{ - struct brcm_pvt *p = iflist; - while(p) { - pvt_lock(p, "brcm lock pvts"); - //ast_mutex_lock(&p->lock); - p = brcm_get_next_pvt(p); - } -} - -static void brcm_unlock_pvts(void) -{ - struct brcm_pvt *p = iflist; - while(p) { - pvt_unlock(p); - //ast_mutex_unlock(&p->lock); - p = brcm_get_next_pvt(p); - } -} - -static void brcm_show_subchannels(struct ast_cli_args *a, struct brcm_pvt *p) -{ - struct brcm_subchannel *sub; - - /* Output status for sub channels */ - int i; - for (i=0; i<NUM_SUBCHANNELS; i++) { - sub = p->sub[i]; - ast_cli(a->fd, "Subchannel: %d\n", sub->id); - ast_cli(a->fd, " Connection id : %d\n", sub->connection_id); - - ast_cli(a->fd, " Owner : %p\n", sub->owner); - ast_cli(a->fd, " Channel state : %s\n", state2str(sub->channel_state)); - ast_cli(a->fd, " Connection init : %d\n", sub->connection_init); - ast_cli(a->fd, " Codec used : %s\n", brcm_get_codec_string(sub->codec)); - ast_cli(a->fd, " RTP SSRC : %d\n", sub->ssrc); - ast_cli(a->fd, " RTP timestamp : %d\n", sub->time_stamp); - ast_cli(a->fd, " CW Timer id : %d\n", sub->cw_timer_id); - ast_cli(a->fd, " CW Beep Timer id : %d\n", sub->cwBeep_timer_id); - ast_cli(a->fd, " R4 Hangup Timer id : %d\n", sub->r4_hangup_timer_id); - ast_cli(a->fd, " Conference initiator: %d\n", sub->conference_initiator); - ast_cli(a->fd, " Onhold Hangup Timer id: %d\n", sub->onhold_hangup_timer_id); - } -} - -static void brcm_show_pvts(struct ast_cli_args *a) -{ - struct brcm_pvt *p = iflist; - int i = 0; - - while(p) { - pvt_lock(p, "brcm show pvts"); - //ast_mutex_lock(&p->lock); - ast_cli(a->fd, "\nPvt nr: %d\n",i); - ast_cli(a->fd, "Line id : %d\n", p->line_id); - ast_cli(a->fd, "Pvt next ptr : 0x%x\n", (unsigned int) p->next); - ast_cli(a->fd, "DTMF buffer : %s\n", p->dtmfbuf); - ast_cli(a->fd, "Default context : %s\n", p->context); - ast_cli(a->fd, "Direct context : %s\n", p->context_direct); - channel_settings* s = &channel_config[p->line_id]; - - ast_cli(a->fd, "Ringsignal : %s\n", s->ringsignal ? "on" : "off"); - ast_cli(a->fd, "Dialout msecs : %d\n", s->timeoutmsec); - ast_cli(a->fd, "Autodial extension : %s\n", s->autodial_ext); - ast_cli(a->fd, "Autodial msecs : %d\n", s->autodial_timeoutmsec); - ast_cli(a->fd, "Dialt. timeout msecs: %d\n", s->dialtone_timeoutmsec); - - ast_cli(a->fd, "Ast JitterBuf impl : %s\n", global_jbconf.impl); - ast_cli(a->fd, "Ast JitterBuf max : %ld\n", global_jbconf.max_size); - ast_cli(a->fd, "Do not disturb : %s\n", s->do_not_disturb ? "on" : "off"); - ast_cli(a->fd, "CLIR : %s\n", s->anonymouscallenable ? "on" : "off"); - - ast_cli(a->fd, "Dialtone : "); - const DIALTONE_MAP *dialtone = dialtone_map; - while (dialtone->state != DIALTONE_LAST) { - if (dialtone->state == p->dialtone) { - break; - } - dialtone++; - } - ast_cli(a->fd, "%s\n", dialtone->str); - ast_cli(a->fd, "Dialtone Timer id : %d\n", p->dialtone_timeout_timer_id); - - /* Print status for subchannels */ - brcm_show_subchannels(a, p); - - ast_cli(a->fd, "\n"); - - i++; - pvt_unlock(p); - //ast_mutex_unlock(&p->lock); - p = brcm_get_next_pvt(p); - } -} - -/*! \brief CLI for showing brcm status. - * This is a new-style CLI handler so a single function contains - * the prototype for the function, the 'generator' to produce multiple - * entries in case it is required, and the actual handler for the command. - */ - -static char *brcm_show_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) -{ - char buffer[AST_MAX_EXTENSION]; - - if (cmd == CLI_INIT) { - e->command = "telephony show status"; - e->usage = - "Usage: telephony show status\n" - " Shows the current chan_brcm status.\n"; - return NULL; - } else if (cmd == CLI_GENERATE) - return NULL; - - /* print chan brcm status information */ - ast_cli(a->fd, "Channel version: %s\n\n", CHANNEL_VERSION); - ast_cli(a->fd, "Number of endpoints: %d\n", num_endpoints); - ast_cli(a->fd, "FAC list : %s\n", feature_access_code_string(buffer, AST_MAX_EXTENSION)); - - /* print status for individual pvts */ - brcm_show_pvts(a); - - return CLI_SUCCESS; -} - -/*! \brief CLI for showing brcm dialtone status. */ -static char *brcm_show_dialtone_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) -{ - if (cmd == CLI_INIT) { - e->command = "brcm show dialtone status"; - e->usage = - "Usage: brcm show dialtone status\n" - " Shows the current chan_brcm dialtone status.\n"; - return NULL; - } - else if (cmd == CLI_GENERATE) { - return NULL; - } - - struct brcm_pvt *p = iflist; - int i = 0; - - ast_cli(a->fd, "Pvt nr\tDialtone\n\n"); - while(p) { - const DIALTONE_MAP *dialtone = dialtone_map; - while (dialtone->state != DIALTONE_LAST) { - if (dialtone->state == p->dialtone) { - break; - } - dialtone++; - } - ast_cli(a->fd, "%d\t%s\n", i, dialtone->str); - - i++; - p = brcm_get_next_pvt(p); - } - - return CLI_SUCCESS; -} - -/*! \brief CLI for reloading brcm config. */ -static char *brcm_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) -{ - struct ast_config *cfg = NULL; - if (cmd == CLI_INIT) { - e->command = "telephony reload"; - e->usage = - "Usage: telephony reload\n" - " Reload chan_brcm configuration.\n"; - return NULL; - } else if (cmd == CLI_GENERATE) { - return NULL; - } - - ast_mutex_lock(&iflock); - - /* Acquire locks for all pvt:s to prevent nasty things from happening */ - brcm_lock_pvts(); - - feature_access_code_clear(); - - /* Reload configuration */ - if (load_common_settings(&cfg)) { - brcm_unlock_pvts(); - ast_mutex_unlock(&iflock); - return CLI_FAILURE; - } - - load_settings(cfg); - struct brcm_pvt *p = iflist; - while(p) { - brcm_initialize_pvt(p); - p = brcm_get_next_pvt(p); - } - - brcm_unlock_pvts(); - ast_mutex_unlock(&iflock); - - ast_verbose("TELCHAN reload done\n"); - - return CLI_SUCCESS; -} - -static char *brcm_set_parameters_on_off(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) -{ - int on_off = 0; - - if (cmd == CLI_INIT) { - e->command = "brcm set {echocancel|ringsignal} {on|off}"; - e->usage = - "Usage: brcm set {echocancel|ringsignal} {on|off} PvtNr\n" - " echocancel, echocancel mode.\n" - " ringsignal, ring signal mode.\n" - " PvtNr, the Pvt to modify.\n"; - return NULL; - } else if (cmd == CLI_GENERATE) { - return NULL; - } - - if (a->argc <= 4) { - return CLI_SHOWUSAGE; //Too few arguments - } - - int pvt_id = atoi(a->argv[4]); - if (pvt_id >= num_endpoints || pvt_id < 0) { - return CLI_SHOWUSAGE; - } - channel_settings *s = &channel_config[pvt_id]; - - if (!strcasecmp(a->argv[3], "on")) { - on_off = 1; - } else { - on_off = 0; - } - - if (!strcasecmp(a->argv[2], "ringsignal")) { - s->ringsignal = on_off; - } - return CLI_SUCCESS; -} - -static char *brcm_set_parameters_value(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) -{ - if (cmd == CLI_INIT) { - e->command = "brcm set dialout_msecs"; - e->usage = - "Usage: brcm set dialout_msecs 4000 PvtNr\n" - " dialout_msecs, dialout delay in msecs.\n" - " PvtNr, the Pvt to modify.\n"; - return NULL; - } else if (cmd == CLI_GENERATE) - return NULL; - - if (a->argc <= 4) - return CLI_SHOWUSAGE; - - int pvt_id = atoi(a->argv[4]); - if (pvt_id >= num_endpoints || pvt_id < 0) { - return CLI_SHOWUSAGE; - } - channel_settings *s = &channel_config[pvt_id]; - - s->timeoutmsec = atoi(a->argv[3]); - - return CLI_SUCCESS; -} - -static char *brcm_set_autodial_extension(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) -{ - struct brcm_pvt *p; - - if (cmd == CLI_INIT) { - e->command = "brcm set autodial"; - e->usage = - "Usage: brcm set autodial 0 1234\n" - " brcm set autodial 0 \"\"\n" - " autodial, extension to autodial on of hook.\n"; - return NULL; - } else if (cmd == CLI_GENERATE) - return NULL; - - if (a->argc <= 4) - return CLI_SHOWUSAGE; - -// ast_verbose("%d %s",(a->argv[3][0] -'0'), a->argv[4]); - - p = iflist; - while(p) { - if (p->line_id == (a->argv[3][0]-'0')) { - channel_settings *s = &channel_config[p->line_id]; - ast_copy_string(s->autodial_ext, a->argv[4], sizeof(s->autodial_ext)); - break; - } - p = brcm_get_next_pvt(p); - } - - return CLI_SUCCESS; -} - - -/*! \brief Channel CLI commands definition */ -static struct ast_cli_entry cli_brcm[] = { - AST_CLI_DEFINE(brcm_show_status, "Show chan_brcm status"), - AST_CLI_DEFINE(brcm_show_dialtone_status, "Show chan_brcm dialtone status"), - AST_CLI_DEFINE(brcm_set_parameters_on_off, "Set chan_brcm parameters"), - AST_CLI_DEFINE(brcm_set_parameters_value, "Set chan_brcm dialout msecs"), - AST_CLI_DEFINE(brcm_set_autodial_extension, "Set chan_brcm autodial extension"), - AST_CLI_DEFINE(brcm_reload, "Reload chan_brcm configuration"), -}; - - -static int unload_module(void) -{ - struct brcm_pvt *p, *pl; - - //ast_sched_dump(sched); - - /* First, take us out of the channel loop */ - if (cur_tech) - ast_channel_unregister(cur_tech); - if (!ast_mutex_lock(&iflock)) { - /* Hangup all interfaces if they have an owner */ - p = iflist; - while(p) { - int i; - pvt_lock(p, "brcm unload module"); - //ast_mutex_lock(&p->lock); - for (i=0; i<NUM_SUBCHANNELS; i++) { - struct ast_channel *owner = p->sub[i]->owner; - if (owner) { - ast_channel_ref(owner); - ast_mutex_unlock(&p->lock); - ast_softhangup(owner, AST_SOFTHANGUP_APPUNLOAD); - ast_channel_unref(owner); - ast_mutex_lock(&p->lock); - } - } - free(registration_change_sub); - registration_change_sub = NULL; - pvt_unlock(p); - //ast_mutex_unlock(&p->lock); - p = p->next; - } - iflist = NULL; - ast_mutex_unlock(&iflock); - } else { - ast_log(LOG_WARNING, "Unable to lock the monitor\n"); - return -1; - } - - if (!ast_mutex_lock(&monlock)) { - if (ubus_thread != AST_PTHREADT_NULL && ubus_thread != AST_PTHREADT_STOP) { - ast_verbose("Stopping ubus thread...\n"); - pthread_kill(ubus_thread, SIGUSR2); - pthread_join(ubus_thread, NULL); - ubus_thread = AST_PTHREADT_STOP; - ast_verbose("ubus thread is stopped\n"); - } - ast_mutex_unlock(&monlock); - } else { - ast_log(LOG_WARNING, "Unable to lock the monitor\n"); - return -1; - } - - if (!ast_mutex_lock(&iflock)) { - /* Destroy all the interfaces and free their memory */ - p = iflist; - while(p) { - /* Close the socket, assuming it's real */ - pl = p; - p = p->next; - /* Free associated memory */ - ast_free(pl); - } - iflist = NULL; - ast_mutex_unlock(&iflock); - } else { - ast_log(LOG_WARNING, "Unable to lock the monitor\n"); - return -1; - } - - /* Unregister CLI commands */ - ast_cli_unregister_multiple(cli_brcm, ARRAY_LEN(cli_brcm)); - - feature_access_code_clear(); - ast_sched_context_destroy(sched); - - return 0; -} - -/* - * Create a channel_settings struct with default values. - */ -static channel_settings channel_settings_create(void) -{ - channel_settings line_conf = (channel_settings){ - .enabled = 1, - .language = "", - .cid_num = "", - .cid_name = "", - .context_direct = "default-direct", - .context = "default", - .autodial_ext = "", - .ringsignal = 1, - .timeoutmsec = 4000, - .autodial_timeoutmsec = 60000, - .period = 20, - .hangup_xfer = 0, - .dialtone_timeoutmsec = 20000, - .offhook_nu_timeoutmsec = 60000, - .offhook_silence_timeoutmsec = 180000, - .do_not_disturb = 0, - .calleridenable = 0, //clip - .calleridnameenable = 0, //cnip - .anonymouscallenable = 0, //clir - .flashSpec = 0, - }; - return line_conf; -} - -/* - * Load config file settings into the specified channel_settings struct. - * Can be called multiple times in order to load from multiple ast_variables. - */ -static void channel_settings_load(channel_settings *channel_config, struct ast_variable *v) -{ - while(v) { - if (!strcasecmp(v->name, "enabled")) { - channel_config->enabled = ast_true(v->value)?1:0; - } else if (!strcasecmp(v->name, "language")) { - ast_copy_string(channel_config->language, v->value, sizeof(channel_config->language)); - } else if (!strcasecmp(v->name, "callerid")) { - ast_callerid_split(v->value, channel_config->cid_name, sizeof(channel_config->cid_name), channel_config->cid_num, sizeof(channel_config->cid_num)); - } else if (!strcasecmp(v->name, "context")) { - ast_copy_string(channel_config->context, v->value, sizeof(channel_config->context)); - } else if (!strcasecmp(v->name, "context_direct")) { - ast_copy_string(channel_config->context_direct, v->value, sizeof(channel_config->context_direct)); - } else if (!strcasecmp(v->name, "autodial")) { - ast_copy_string(channel_config->autodial_ext, v->value, sizeof(channel_config->autodial_ext)); - } else if (!strcasecmp(v->name, "ringsignal")) { - channel_config->ringsignal = ast_true(v->value)?1:0; - } else if (!strcasecmp(v->name, "dialoutmsec")) { - channel_config->timeoutmsec = atoi(v->value); - } else if (!strcasecmp(v->name, "interdigitopenmsec")) { - channel_config->interdigitopenmsec = atoi(v->value); - } else if (!strcasecmp(v->name, "minimumnumberdigits")) { - channel_config->minimumnumberdigits = atoi(v->value); - } else if (!strcasecmp(v->name, "terminationdigit")) { - channel_config->terminationdigit =v->value[0]; - } else if (!strcasecmp(v->name, "autodial_timeoutmsec")) { - channel_config->autodial_timeoutmsec = atoi(v->value); - } else if (!strcasecmp(v->name, "dialtone_timeoutmsec")) { - channel_config->dialtone_timeoutmsec = atoi(v->value); - } else if (!strcasecmp(v->name, "offhook_nu_timeoutmsec")) { - channel_config->offhook_nu_timeoutmsec = atoi(v->value); - } else if (!strcasecmp(v->name, "offhook_silence_timeoutmsec")) { - channel_config->offhook_silence_timeoutmsec = atoi(v->value); - } - else if (!strcasecmp(v->name, "hangup_xfer")) { - channel_config->hangup_xfer = ast_true(v->value)?1:0; - } - else if (!strcasecmp(v->name, "do_not_disturb")) { - channel_config->do_not_disturb = ast_true(v->value)?1:0; - } - else if (!strcasecmp(v->name, "calleridenable")) { - channel_config->calleridenable = ast_true(v->value)?1:0; - } - else if (!strcasecmp(v->name, "calleridnameenable")) { - channel_config->calleridnameenable = ast_true(v->value)?1:0; - } - else if (!strcasecmp(v->name, "anonymouscallenable")) { - channel_config->anonymouscallenable = ast_true(v->value)?1:0; - } - else if (!strcasecmp(v->name, "flash_spec")) { - if (!strcasecmp(v->value, "etsi")) - channel_config->flashSpec = FLASH_SPEC_ETSI; - else - channel_config->flashSpec = FLASH_SPEC_UK; - } - else if (!strcasecmp(v->name, "mwi_enabled")) { - channel_config->mwi_enabled = ast_true(v->value)?1:0; - } - v = v->next; - } -} - -static int load_common_settings(struct ast_config **cfg) -{ - struct ast_flags config_flags = { 0 }; - - /* Set default values */ - hold_target_before_refer = 1; - - if ((*cfg = ast_config_load(config, config_flags)) == CONFIG_STATUS_FILEINVALID) { - ast_log(LOG_ERROR, "Config file %s is in an invalid format. Aborting.\n", config); - return AST_MODULE_LOAD_DECLINE; - } - - /* We *must* have a config file otherwise stop immediately */ - if (!(*cfg)) { - ast_log(LOG_ERROR, "Unable to load config %s\n", config); - return AST_MODULE_LOAD_DECLINE; - } - - /* Load jitterbuffer defaults, Copy the default jb config over global_jbconf */ - memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf)); - - /* Load global settings */ - struct ast_variable *v; - v = ast_variable_browse(*cfg, "default"); - - while(v) { - if (!ast_jb_read_conf(&global_jbconf, v->name, v->value)) { - ast_debug(2, "Loaded jitterbuffer settings '%s'\n", v->value); - v = v->next; - continue; - } - - if (!strcasecmp(v->name, "cwtimeout")) { - cwtimeout = atoi(v->value); - if (cwtimeout > 60 || cwtimeout < 0) { - cwtimeout = DEFAULT_CALL_WAITING_TIMEOUT; - ast_log(LOG_WARNING, "Incorrect cwtimeout '%s', defaulting to '%d'\n", v->value, cwtimeout); - } - } else if (!strcasecmp(v->name, "r4hanguptimeout")) { - r4hanguptimeout = atoi(v->value); - if (r4hanguptimeout > 30000 || r4hanguptimeout < 0) { - r4hanguptimeout = DEFAULT_R4_HANGUP_TIMEOUT; - ast_log(LOG_WARNING, "Incorrect r4hanguptimeout '%s', defaulting to '%d'\n", - v->value, r4hanguptimeout); - } - } else if (!strcasecmp(v->name, "onholdhanguptimeout")) { - onholdhanguptimeout = atoi(v->value); - if (onholdhanguptimeout > 60 || onholdhanguptimeout < 0) { - onholdhanguptimeout = DEFAULT_ONHOLD_HANGUP_TIMEOUT; - ast_log(LOG_WARNING, "Incorrect onholdhanguptimeout '%s', defaulting to '%d'\n", - v->value, onholdhanguptimeout); - } - } else if (!strcasecmp(v->name, "hold_target_before_refer")) { - if (!strcasecmp(v->value, "no")) { - ast_debug(1, "The transfer target will not be put on-hold before sending REFER\n"); - hold_target_before_refer = 0; - } - } else if (!strcasecmp(v->name, "featureaccesscodes")) { - char *tok; - - if (ast_strlen_zero(v->value)) { - ast_debug(1, "No value given for featureaccesscodes on line %d\n", v->lineno); - } - else { - tok = strtok(ast_strdupa(v->value), ","); - while (tok) { - char *code = ast_strdupa(tok); - code = ast_strip(code); - - feature_access_code_add(code); - - tok = strtok(NULL, ","); - } - } - } - - v = v->next; - } - - return 0; -} - -static void load_settings(struct ast_config *cfg) -{ - struct ast_variable *v; - - // Load settings fore all channels / endpoints - for (int i = 0; i < num_endpoints; i++) { - // Create and init a new settings struct - channel_config[i] = channel_settings_create(); - // Load default settings - v = ast_variable_browse(cfg, "default"); - channel_settings_load(&channel_config[i], v); - // Load per line specific settings - char config_section[64]; - snprintf(config_section, 64, "extension%d", i); - v = ast_variable_browse(cfg, config_section); - if (!v) { - ast_log(LOG_WARNING, "Unable to load endpoint specific config (missing config section?): %s\n", config_section); - } - channel_settings_load(&channel_config[i], v); - } -} - -enum { - NUM_ENDPOINTS, - NUM_FXO_ENDPOINTS, - NUM_FXS_ENDPOINTS, - NUM_DECT_ENDPOINTS, - __MAX_ENDPOINTS, -}; - -static const struct blobmsg_policy endpt_count_policy[__MAX_ENDPOINTS] = { - [NUM_ENDPOINTS] = { .name = "num_endpoints", .type = BLOBMSG_TYPE_INT32 }, - [NUM_FXO_ENDPOINTS] = { .name = "num_fxo_endpoints", .type = BLOBMSG_TYPE_INT32 }, - [NUM_FXS_ENDPOINTS] = { .name = "num_fxs_endpoints", .type = BLOBMSG_TYPE_INT32 }, - [NUM_DECT_ENDPOINTS] = { .name = "num_dect_endpoints", .type = BLOBMSG_TYPE_INT32 }, -}; - -enum { - RTP_STATS_LINE_ID, - RTP_STATS_LOCAL_BURST_DENSITY, - RTP_STATS_REMOTE_BURST_DENSITY, - RTP_STATS_LOCAL_BURST_DURATION, - RTP_STATS_REMOTE_BURST_DURATION, - RTP_STATS_LOCAL_GAP_DENSITY, - RTP_STATS_REMOTE_GAP_DENSITY, - RTP_STATS_LOCAL_GAP_DURATION, - RTP_STATS_REMOTE_GAP_DURATION, - RTP_STATS_LOCAL_JB_RATE, - RTP_STATS_REMOTE_JB_RATE, - RTP_STATS_LOCAL_JB_MAX, - RTP_STATS_REMOTE_JB_MAX, - RTP_STATS_LOCAL_JB_NOMINAL, - RTP_STATS_REMOTE_JB_NOMINAL, - RTP_STATS_LOCAL_JB_ABS_MAX, - RTP_STATS_REMOTE_JB_ABS_MAX, - RTP_STATS_DISCARDED, - RTP_STATS_LOST, - RTP_STATS_RX_PKTS, - RTP_STATS_TX_PKTS, - RTP_STATS_JB_AVG, - RTP_STATS_JITTER, - RTP_STATS_LOSS_RATE, - RTP_STATS_MAX_JITTER, - RTP_STATS_AVERAGE_ROUND_TRIP_DELAY, - __MAX_RTP_STATS, -}; - -static const struct blobmsg_policy endpt_rtp_stats_policy[__MAX_RTP_STATS] = { - [RTP_STATS_LINE_ID] = { .name = "lineId", .type = BLOBMSG_TYPE_INT16 }, - [RTP_STATS_LOCAL_BURST_DENSITY] = { .name = "localBurstDensity", .type = BLOBMSG_TYPE_INT16 }, - [RTP_STATS_REMOTE_BURST_DENSITY] = { .name = "remoteBurstDensity", .type = BLOBMSG_TYPE_INT16 }, - [RTP_STATS_LOCAL_BURST_DURATION] = { .name = "localBurstDuration", .type = BLOBMSG_TYPE_INT16 }, - [RTP_STATS_REMOTE_BURST_DURATION] = { .name = "remoteBurstDuration", .type = BLOBMSG_TYPE_INT16 }, - [RTP_STATS_LOCAL_GAP_DENSITY] = { .name = "localGapDensity", .type = BLOBMSG_TYPE_INT16 }, - [RTP_STATS_REMOTE_GAP_DENSITY] = { .name = "remoteGapDensity", .type = BLOBMSG_TYPE_INT16 }, - [RTP_STATS_LOCAL_GAP_DURATION] = { .name = "localGapDuration", .type = BLOBMSG_TYPE_INT16 }, - [RTP_STATS_REMOTE_GAP_DURATION] = { .name = "remoteGapDuration", .type = BLOBMSG_TYPE_INT16 }, - [RTP_STATS_LOCAL_JB_RATE] = { .name = "localJbRate", .type = BLOBMSG_TYPE_INT16 }, - [RTP_STATS_REMOTE_JB_RATE] = { .name = "remoteJbRate", .type = BLOBMSG_TYPE_INT16 }, - [RTP_STATS_LOCAL_JB_MAX] = { .name = "localJbMax", .type = BLOBMSG_TYPE_INT16 }, - [RTP_STATS_REMOTE_JB_MAX] = { .name = "remoteJbMax", .type = BLOBMSG_TYPE_INT16 }, - [RTP_STATS_LOCAL_JB_NOMINAL] = { .name = "localJbNominal", .type = BLOBMSG_TYPE_INT16 }, - [RTP_STATS_REMOTE_JB_NOMINAL] = { .name = "remoteJbNominal", .type = BLOBMSG_TYPE_INT16 }, - [RTP_STATS_LOCAL_JB_ABS_MAX] = { .name = "localJbAbsMax", .type = BLOBMSG_TYPE_INT16 }, - [RTP_STATS_REMOTE_JB_ABS_MAX] = { .name = "remoteJbAbsMax", .type = BLOBMSG_TYPE_INT16 }, - [RTP_STATS_DISCARDED] = { .name = "discarded", .type = BLOBMSG_TYPE_INT32 }, - [RTP_STATS_LOST] = { .name = "lost", .type = BLOBMSG_TYPE_INT32 }, - [RTP_STATS_RX_PKTS] = { .name = "rxpkts", .type = BLOBMSG_TYPE_INT32 }, - [RTP_STATS_TX_PKTS] = { .name = "txpkts", .type = BLOBMSG_TYPE_INT32 }, - [RTP_STATS_JB_AVG] = { .name = "jbAvg", .type = BLOBMSG_TYPE_INT16 }, - [RTP_STATS_JITTER] = { .name = "jitter", .type = BLOBMSG_TYPE_INT32 }, - [RTP_STATS_LOSS_RATE] = { .name = "uLossRate", .type = BLOBMSG_TYPE_INT16 }, - [RTP_STATS_MAX_JITTER] = { .name = "maxJitter", .type = BLOBMSG_TYPE_INT32 }, - [RTP_STATS_AVERAGE_ROUND_TRIP_DELAY] = { .name = "averageRoundTripDelay", .type = BLOBMSG_TYPE_INT32 }, -}; - -// Reception of event -// { "ubus.object.add": {"id":123, "path":"foo"} } -static void ubus_event_new_obj(struct ubus_context *ctx __attribute__((unused)), struct ubus_event_handler *ev __attribute__((unused)), - const char *type, struct blob_attr *blob) -{ - struct blob_attr *keys[ARRAY_SIZE(new_obj_policy)]; - const char *objPath; - uint32_t objId; - - // Tokenize message key/value paris into an array - if (blobmsg_parse(new_obj_policy, ARRAY_SIZE(new_obj_policy), - keys, blob_data(blob), blob_len(blob))) { - return; - } - - if (!type || !keys[OBJ_ID] || !keys[OBJ_PATH]) - return; // Did we get all arguments we need? - - objId = blobmsg_get_u32(keys[OBJ_ID]); - objPath = blobmsg_get_string(keys[OBJ_PATH]); - - if(strcmp(type, ubusStrObjAdd) == 0) { // Object added to global context - if(strcmp(objPath, endpt_ubus_path) == 0) { - ast_log(LOG_DEBUG, "the UBUS object %s has been added\n", endpt_ubus_path); - endpt_id = objId; - } - } else if(strcmp(type, ubusStrObjRm) == 0) { // Object removed from global context - if(strcmp(objPath, endpt_ubus_path) == 0) { - ast_log(LOG_DEBUG, "the UBUS object %s has been removed\n", endpt_ubus_path); - endpt_id = 0; - } - } -} - -static void endpt_get_count_cb(struct ubus_request *req, - int type, struct blob_attr *msg) { - struct blob_attr *tb[__MAX_ENDPOINTS]; - - blobmsg_parse(endpt_count_policy, __MAX_ENDPOINTS, tb, blob_data(msg), blob_len(msg)); - - if (tb[NUM_FXS_ENDPOINTS]) - num_fxs_endpoints = blobmsg_get_u32(tb[NUM_FXS_ENDPOINTS]); - if (tb[NUM_FXO_ENDPOINTS]) - num_fxo_endpoints = blobmsg_get_u32(tb[NUM_FXO_ENDPOINTS]); - if (tb[NUM_DECT_ENDPOINTS]) - num_dect_endpoints = blobmsg_get_u32(tb[NUM_DECT_ENDPOINTS]); - num_endpoints = num_fxs_endpoints + num_dect_endpoints; -} - -static int endpt_get_count(void) { - struct blob_buf bb; - - endpt_id = get_ubus_endpt_id(0); - if(!endpt_id) - return -1; - - memset(&bb, 0, sizeof(bb)); - blob_buf_init(&bb, 0); - blobmsg_add_u8(&bb, "effective", 0); - - return (ubus_invoke(ctx, endpt_id, "count", bb.head, - endpt_get_count_cb, NULL, 2000) == UBUS_STATUS_OK ? 0 : -1); -} - -static void ubus_call_answer_rtp_stats(struct ubus_request *req, int type, struct blob_attr *msg) { - struct blob_attr *tb[__MAX_RTP_STATS]; - uint16_t lineId = 0; - struct brcm_pvt *p = NULL; - struct brcm_subchannel *sub = NULL; - - ast_log(LOG_DEBUG, "thread %d: got answer from endptmngr on rtp_stats ubus call.\n", ast_get_tid()); - blobmsg_parse(endpt_rtp_stats_policy, __MAX_RTP_STATS, tb, blob_data(msg), blob_len(msg)); - - if (tb[RTP_STATS_LINE_ID]) - lineId = blobmsg_get_u16(tb[RTP_STATS_LINE_ID]); - - p = brcm_get_pvt_from_lineid(iflist, lineId); - if (!p) { - ast_log(LOG_ERROR, "No pvt with the line_id %d found!\n", lineId); - return; - } - - sub = brcm_get_active_subchannel(p); - if (!sub) { - ast_log(LOG_ERROR, "No active subchannel to write rtp stats!\n"); - return; - } - - if (tb[RTP_STATS_LOCAL_BURST_DENSITY]) - sub->rtp_stats.localBurstDensity = blobmsg_get_u16(tb[RTP_STATS_LOCAL_BURST_DENSITY]); - if (tb[RTP_STATS_REMOTE_BURST_DENSITY]) - sub->rtp_stats.remoteBurstDensity = blobmsg_get_u16(tb[RTP_STATS_REMOTE_BURST_DENSITY]); - if (tb[RTP_STATS_LOCAL_BURST_DURATION]) - sub->rtp_stats.localBurstDuration = blobmsg_get_u16(tb[RTP_STATS_LOCAL_BURST_DURATION]); - if (tb[RTP_STATS_REMOTE_BURST_DURATION]) - sub->rtp_stats.remoteBurstDuration = blobmsg_get_u16(tb[RTP_STATS_REMOTE_BURST_DURATION]); - if (tb[RTP_STATS_LOCAL_GAP_DENSITY]) - sub->rtp_stats.localGapDensity = blobmsg_get_u16(tb[RTP_STATS_LOCAL_GAP_DENSITY]); - if (tb[RTP_STATS_REMOTE_GAP_DENSITY]) - sub->rtp_stats.remoteGapDensity = blobmsg_get_u16(tb[RTP_STATS_REMOTE_GAP_DENSITY]); - if (tb[RTP_STATS_LOCAL_GAP_DURATION]) - sub->rtp_stats.localGapDuration = blobmsg_get_u16(tb[RTP_STATS_LOCAL_GAP_DURATION]); - if (tb[RTP_STATS_REMOTE_GAP_DURATION]) - sub->rtp_stats.remoteGapDuration = blobmsg_get_u16(tb[RTP_STATS_REMOTE_GAP_DURATION]); - if (tb[RTP_STATS_LOCAL_JB_RATE]) - sub->rtp_stats.localJbRate = blobmsg_get_u16(tb[RTP_STATS_LOCAL_JB_RATE]); - if (tb[RTP_STATS_REMOTE_JB_RATE]) - sub->rtp_stats.remoteJbRate = blobmsg_get_u16(tb[RTP_STATS_REMOTE_JB_RATE]); - if (tb[RTP_STATS_LOCAL_JB_MAX]) - sub->rtp_stats.localJbMax = blobmsg_get_u16(tb[RTP_STATS_LOCAL_JB_MAX]); - if (tb[RTP_STATS_REMOTE_JB_MAX]) - sub->rtp_stats.remoteJbMax = blobmsg_get_u16(tb[RTP_STATS_REMOTE_JB_MAX]); - if (tb[RTP_STATS_LOCAL_JB_NOMINAL]) - sub->rtp_stats.localJbNominal = blobmsg_get_u16(tb[RTP_STATS_LOCAL_JB_NOMINAL]); - if (tb[RTP_STATS_REMOTE_JB_NOMINAL]) - sub->rtp_stats.remoteJbNominal = blobmsg_get_u16(tb[RTP_STATS_REMOTE_JB_NOMINAL]); - if (tb[RTP_STATS_LOCAL_JB_ABS_MAX]) - sub->rtp_stats.localJbAbsMax = blobmsg_get_u16(tb[RTP_STATS_LOCAL_JB_ABS_MAX]); - if (tb[RTP_STATS_REMOTE_JB_ABS_MAX]) - sub->rtp_stats.remoteJbAbsMax = blobmsg_get_u16(tb[RTP_STATS_REMOTE_JB_ABS_MAX]); - if (tb[RTP_STATS_DISCARDED]) - sub->rtp_stats.discarded = blobmsg_get_u32(tb[RTP_STATS_DISCARDED]); - if (tb[RTP_STATS_LOST]) - sub->rtp_stats.lost = blobmsg_get_u32(tb[RTP_STATS_LOST]); - if (tb[RTP_STATS_RX_PKTS]) - sub->rtp_stats.rxpkts = blobmsg_get_u32(tb[RTP_STATS_RX_PKTS]); - if (tb[RTP_STATS_TX_PKTS]) - sub->rtp_stats.txpkts = blobmsg_get_u32(tb[RTP_STATS_TX_PKTS]); - if (tb[RTP_STATS_JB_AVG]) - sub->rtp_stats.jbAvg = blobmsg_get_u16(tb[RTP_STATS_JB_AVG]); - if (tb[RTP_STATS_JITTER]) - sub->rtp_stats.jitter = blobmsg_get_u32(tb[RTP_STATS_JITTER]); - if (tb[RTP_STATS_LOSS_RATE]) - sub->rtp_stats.uLossRate = blobmsg_get_u16(tb[RTP_STATS_LOSS_RATE]); - if (tb[RTP_STATS_MAX_JITTER]) - sub->rtp_stats.maxJitter = blobmsg_get_u32(tb[RTP_STATS_MAX_JITTER]); - if (tb[RTP_STATS_AVERAGE_ROUND_TRIP_DELAY]) - sub->rtp_stats.averageRoundTripDelay = blobmsg_get_u32(tb[RTP_STATS_AVERAGE_ROUND_TRIP_DELAY]); - - sub->rtp_stats.averageFarEndInterarrivalJitter = sub->jitter_count ? (sub->farEndInterrivalJitter / sub->jitter_count) : 0; - - ast_log(LOG_DEBUG, "RTP stats received:\nlocalBurstDensity: %d, remoteBurstDensity: %d, " - "localBurstDuration: %d, remoteBurstDuration: %d, localGapDensity: %d, " - "remoteGapDensity: %d, localGapDuration: %d, remoteGapDuration: %d, " - "localJbRate: %d, remoteJbRate: %d, localJbMax: %d, remoteJbMax: %d, " - "localJbNominal: %d remoteJbNominal: %d, localJbAbsMax: %d, " - "remoteJbAbsMax: %d, discarded: %d, lost: %d, rxpkts: %d, txpkts: %d, " - "jbAvg: %d, jitter: %d, uLossRate: %d, maxJitter: %d, averageRoundTripDelay: %d, " - "averageFarEndInterarrivalJitter: %d\n", - sub->rtp_stats.localBurstDensity, sub->rtp_stats.remoteBurstDensity, sub->rtp_stats.localBurstDuration, - sub->rtp_stats.remoteBurstDuration, sub->rtp_stats.localGapDensity, sub->rtp_stats.remoteGapDensity, - sub->rtp_stats.localGapDuration, sub->rtp_stats.remoteGapDuration, sub->rtp_stats.localJbRate, - sub->rtp_stats.remoteJbRate, sub->rtp_stats.localJbMax, sub->rtp_stats.remoteJbMax, sub->rtp_stats.localJbNominal, - sub->rtp_stats.remoteJbNominal, sub->rtp_stats.localJbAbsMax, sub->rtp_stats.remoteJbAbsMax, sub->rtp_stats.discarded, - sub->rtp_stats.lost, sub->rtp_stats.rxpkts, sub->rtp_stats.txpkts, sub->rtp_stats.jbAvg, sub->rtp_stats.jitter, - sub->rtp_stats.uLossRate, sub->rtp_stats.maxJitter, sub->rtp_stats.averageRoundTripDelay, sub->rtp_stats.averageFarEndInterarrivalJitter); -} - -static int endpt_get_rtp_stats(int line) { - struct ubus_context *local_ctx; - struct blob_buf bb; - int ret; - struct brcm_pvt *p = NULL; - struct brcm_subchannel *sub = NULL; - - /* - * Reset rtp_stats first because ubus_call_answer_rtp_stats() will not be called if "ubus call endpt rtp_stats" fails, - * e.g. an unanswered incoming call on which the connection is not created. In this case, all RTP statistics counters - * shall be zeros. - */ - p = brcm_get_pvt_from_lineid(iflist, line); - if (!p) { - ast_log(LOG_ERROR, "No pvt with the line %d found!\n", line); - return -1; - } - - sub = brcm_get_active_subchannel(p); - if (!sub) { - ast_log(LOG_ERROR, "No active subchannel to get rtp stats!\n"); - return -1; - } - memset(&sub->rtp_stats, 0, sizeof(sub->rtp_stats)); - - if (!endpt_id) { - return -1; - } - - local_ctx = ubus_connect(NULL); - if (!local_ctx) { - ast_log(LOG_ERROR, "%s(): ubus_connect() failed\n", __func__); - return -1; - } - - memset(&bb, 0, sizeof(bb)); - blob_buf_init(&bb, 0); - - blobmsg_add_u32(&bb, "line", line); - blobmsg_add_u8(&bb, "reset", 1); // always reset RTP stats after get them - - ast_log(LOG_DEBUG, "thread %d: ubus call endpt rtp_stats \"{'line':%d,'reset':true}\"", ast_get_tid(), line); - ret = ubus_invoke(local_ctx, endpt_id, "rtp_stats", bb.head, ubus_call_answer_rtp_stats, NULL, 500); - - blob_buf_free(&bb); - ubus_free(local_ctx); - - if (ret != UBUS_STATUS_OK) { - ast_log(LOG_DEBUG, "ubus_invoke for rtp_stats failed with return value %d\n", ret); - return -1; - } - - return 0; -} - -// Reception of RPC call -// ubus call asterisk event '{ "line" : 1, "event" : "EVENT_DTMF0" }' -static int asterisk_event(struct ubus_context *ctx, struct ubus_object *obj, - struct ubus_request_data *req, const char *method, - struct blob_attr *msg) -{ - struct blob_attr *tb[__EVENT_MAX]; - char *event_str; - struct endpt_event *ev; - int line; - - blobmsg_parse(asterisk_event_policy, __EVENT_MAX, - tb, blob_data(msg), blob_len(msg)); - - if (!tb[EVENT_LINE_ID] || !tb[EVENT_TYPE]) - return UBUS_STATUS_INVALID_ARGUMENT; - - line = blobmsg_get_u32(tb[EVENT_LINE_ID]); - - event_str = blobmsg_get_string(tb[EVENT_TYPE]); - - /* Check if event is valid */ - for (ev = event_map; ev->event != EVENT_LAST; ev++) { - if (strncmp(ev->name, event_str, strlen(ev->name)) == 0) - break; - } - if (ev->event == EVENT_LAST) { - ast_log(LOG_DEBUG, "Unknown event: %s\n", event_str); - return UBUS_STATUS_INVALID_ARGUMENT; - } - ev->line = line; - - ast_log(LOG_DEBUG, "event: %s, line: %d\n", ev->name, ev->line); - if(iflist && cur_tech) - brcm_process_event(ev); - - return UBUS_STATUS_OK; -} - -// Reception of RPC call -// ubus call asterisk call_status '{ "extension" : X }' or ubus call asterisk call_status '{ "line" : X }' -static int asterisk_call_status(struct ubus_context *ctx, struct ubus_object *obj, - struct ubus_request_data *req, const char *method, - struct blob_attr *msg) -{ - struct blob_attr *tb[__EVENT_MAX]; - enum ubus_msg_status res = UBUS_STATUS_UNKNOWN_ERROR; - struct blob_buf blob; - int extension = -1; - int line = -1; - char sipAccount[6]; - - blobmsg_parse(asterisk_call_status_policy, __CALL_STATUS_MAX, - tb, blob_data(msg), blob_len(msg)); - memset(&blob, 0, sizeof(blob)); - - if(blob_buf_init(&blob, 0)) - return UBUS_STATUS_UNKNOWN_ERROR; - - if (tb[CALL_STATUS_EXTENSION_ID]) { - extension = blobmsg_get_u32(tb[CALL_STATUS_EXTENSION_ID]); - struct brcm_pvt *p = iflist; - - while(p) { - if (extension == p->line_id) { - blobmsg_add_u32(&blob, "extension", p->line_id); - blobmsg_add_string(&blob, "call_status", strlen(p->extensionCallStatus) ? p->extensionCallStatus : "Idle"); - res = UBUS_STATUS_OK; - break; - } - p = brcm_get_next_pvt(p); - } - } else if (tb[CALL_STATUS_LINE_ID]) { - line = blobmsg_get_u32(tb[CALL_STATUS_LINE_ID]); - if (line >=0) { - snprintf(sipAccount, sizeof(sipAccount), "sip%d", line); - getLineState(sipAccount, lineCallStatus); - blobmsg_add_u32(&blob, "line", line); - blobmsg_add_string(&blob, "call_status", strlen(lineCallStatus) ? lineCallStatus : "Idle"); - res = UBUS_STATUS_OK; - } else { - return UBUS_STATUS_INVALID_ARGUMENT; - } - } else { - return UBUS_STATUS_INVALID_ARGUMENT; - } - - ubus_send_reply(ctx, req, blob.head); - blob_buf_free(&blob); - return res; -} - -static struct ubus_method asterisk_methods[] = { - UBUS_METHOD("event", asterisk_event, asterisk_event_policy), - UBUS_METHOD("call_status", asterisk_call_status, asterisk_call_status_policy), -}; - -static struct ubus_object_type asterisk_obj_type = UBUS_OBJECT_TYPE("asterisk", asterisk_methods); - -static struct ubus_object asterisk_obj = { - .name = "asterisk", - .type = &asterisk_obj_type, - .methods = asterisk_methods, - .n_methods = ARRAY_SIZE(asterisk_methods), -}; - -/* Process ubus events by ubus stack */ -static void ubus_stream_handler(pe_stream_t *stream __attribute__((unused)), pe_event_t *event __attribute__((unused))) { - ubus_handle_event(ctx); -} - -static int ubus_init(void) { - pe_stream_t *ubus_stream; - - base = pe_base_new(); - if (base < 0) - exit_failure("pe_base_new\n"); - - ctx = ubus_connect(NULL); - if (!ctx) return -1; - - /* Register event handlers (not calls) for: - * { "ubus.object.add": {"id":123, "path":"foo"} } */ - memset(&ObjAddListener, 0, sizeof(ObjAddListener)); - ObjAddListener.cb = ubus_event_new_obj; - if(ubus_register_event_handler(ctx, &ObjAddListener, - ubusStrObjAdd) != UBUS_STATUS_OK) { - printf("Error registering ubus event handler %s", ubusStrObjAdd); - return -1; - } - - memset(&ObjRmListener, 0, sizeof(ObjRmListener)); - ObjRmListener.cb = ubus_event_new_obj; - if(ubus_register_event_handler(ctx, &ObjRmListener, - ubusStrObjRm) != UBUS_STATUS_OK) { - printf("Error registering ubus event handler %s", ubusStrObjRm); - return -1; - } - - /* Lookup path to Endptmngr AFTER registration of ubus object - * event handler above. It's no error if lookup fails. */ - if(ubus_lookup_id(ctx, endpt_ubus_path, - (uint32_t*) &endpt_id) != UBUS_STATUS_OK) { - endpt_id = 0; - } - - if (ubus_add_object(ctx, &asterisk_obj) != UBUS_STATUS_OK) { - printf("Failed to register asterisk object"); - return -1; - } - - /* Listen for data on ubus socket in our main event loop. */ - ubus_stream = pe_stream_new(ctx->sock.fd); - pe_stream_add_handler(ubus_stream, 0, ubus_stream_handler); - pe_base_add_stream(base, ubus_stream); - - return 0; -} - -// pthread wrapper for lib picoevent dispatcher -static void *pe_base_run(void *unused) { - int delay; - - ast_verbose("thread %d started\n", ast_get_tid()); - for (delay = 0; delay < 5 && (!iflist || !cur_tech); delay++) - sleep(1); - pe_base_dispatch(base); - - return NULL; -} - -static int load_module(void) -{ - struct ast_config *cfg; - int result, try; - - ast_verbose("thread %d is loading the module...\n", ast_get_tid()); - - if (!(default_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) { - return AST_MODULE_LOAD_DECLINE; - } - - if (!(brcm_tech.capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) { - ao2_ref(default_cap, -1); - return AST_MODULE_LOAD_DECLINE; - } - - ast_format_cap_append_by_type(brcm_tech.capabilities, AST_MEDIA_TYPE_AUDIO); - ast_format_cap_append(default_cap, ast_format_alaw, 0); - - // Init UBUS and wait for endptmngr to start - if(ubus_init()) goto err; - - /* Create audio fifos. Both ends open in R/W mode to become - * independent of daemon startup order and peer restart - * recovery. However, later each end use unidirectional flow. */ - result = mkfifo(audio_tx_str, 0666); - if ((result == -1) && (errno != EEXIST)) exit_failure("Failed to create tx pipe"); - audio_tx_fd = open(audio_tx_str, O_RDWR|O_LARGEFILE|O_NONBLOCK); - if(audio_tx_fd == -1) exit_failure("Failed to open tx pipe"); - audio_tx_bus = pe_bus_new(audio_tx_fd); - if (!audio_tx_bus) exit_failure("Failed to create audio_tx bus"); - result = mkfifo(audio_rx_str, 0666); - if ((result == -1) && (errno != EEXIST)) exit_failure("Failed to create rx pipe"); - audio_rx_fd = open(audio_rx_str, O_RDWR|O_LARGEFILE|O_NONBLOCK); - if(audio_rx_fd == -1) exit_failure("Failed to open rx pipe"); - audio_rx_stream = pe_stream_new(audio_rx_fd); - pe_stream_add_handler(audio_rx_stream, PE_MAX_EVENT_SIZE, audio_rx_stream_handler); - pe_base_add_stream(base, audio_rx_stream); - - audio_rx_bus = pe_bus_new(-1); - if (!audio_rx_bus) exit_failure("Failed to create audio_rx bus"); - pe_bus_add_handler(audio_rx_bus, audio_packet_handler); - - /* endpt_get_count() invokes synchronous ubus call and MUST be called before creating the ubus thread. - * Otherwise it may cause the process crash or hang */ - for (try = 0; try < 20 && num_endpoints == -1; try++) { - if(endpt_get_count()) { - ast_log(LOG_DEBUG, "Waiting for endptmngr...\n"); - sleep(1); - } - } - if (num_endpoints == -1) { - ast_log(LOG_ERROR, "endptmngr is not up and running.\n"); - goto err; - } - - /* Run pe_base_dispatch in separate thread. */ - if (ast_pthread_create_background(&ubus_thread, NULL, pe_base_run, NULL) < 0) { - ast_log(LOG_ERROR, "Unable to start ubus thread.\n"); - goto err; - } - - registration_change_sub = calloc(num_endpoints, sizeof(struct stasis_subscription *)); - - /* Setup scheduler thread */ - if (!(sched = ast_sched_context_create())) { - ast_log(LOG_ERROR, "Unable to create scheduler thread/context. Aborting.\n"); - goto err; - } - - if (ast_mutex_lock(&iflock)) { - /* It's a little silly to lock it, but we mind as well just to be sure */ - ast_log(LOG_ERROR, "Unable to lock interface list???\n"); - goto err; - } - - /* Load settings file and read default section */ - if ((result = load_common_settings(&cfg)) != 0) { - goto err; - } - - load_settings(cfg); - brcm_create_pvts(iflist, 0); - brcm_assign_line_id(iflist); - - ast_mutex_unlock(&iflock); - - /* Make sure we can register our channel */ - cur_tech = (struct ast_channel_tech *) &brcm_tech; - if (ast_channel_register(cur_tech)) { - ast_log(LOG_ERROR, "Unable to register channel class 'Brcm'\n"); - ast_config_destroy(cfg); - unload_module(); - goto err; - } - - /* Register all channel CLI functions */ - ast_cli_register_multiple(cli_brcm, ARRAY_LEN(cli_brcm)); - ast_config_destroy(cfg); - - if (ast_sched_start_thread(sched)) { - ast_sched_context_destroy(sched); - sched = NULL; - goto err; - } - - ast_debug(3, "The module is loaded successfully\n"); - return AST_MODULE_LOAD_SUCCESS; - - err: - ao2_ref(default_cap, -1); - return AST_MODULE_LOAD_FAILURE; -} - -static int brcm_signal_callwaiting(const struct brcm_pvt *p) -{ - endpt_signal(p->line_id, "callwt", "on", NULL); - brcm_send_ubus_event("CALLWAITING",p->line_id); - return 0; -} - -static int brcm_stop_callwaiting(const struct brcm_pvt *p) -{ - endpt_signal(p->line_id, "callwt", "off", NULL); - brcm_send_ubus_event("CALLWAITING_STOPPED", p->line_id); - return 0; -} - -static int brcm_signal_ringing(struct brcm_pvt *p) -{ - if (channel_config[p->line_id].ringsignal) { - endpt_signal(p->line_id, "ringing", "on", NULL); - } - return 0; -} - - -static int brcm_stop_ringing(struct brcm_pvt *p) -{ - ast_log(LOG_ERROR, "brcm_stop_ringing()\n"); - if (channel_config[p->line_id].ringsignal) { - endpt_signal(p->line_id, "ringing", "off", NULL); - } - - return 0; -} - -/* Prepare endpoint for ringing. Caller ID signal pending. */ -static int brcm_signal_ringing_callerid_pending(struct brcm_pvt *p) -{ - if (channel_config[p->line_id].ringsignal) { - endpt_signal(p->line_id, "callid_ringing", "on", NULL); - } - - return 0; -} - -static int brcm_stop_ringing_callerid_pending(struct brcm_pvt *p) -{ - if (channel_config[p->line_id].ringsignal) { - endpt_signal(p->line_id, "callid_ringing", "off", NULL); - } - - return 0; -} - -/* - * Send caller id message to endpoint. - * MMDDHHMM, number, name - * 'O' in number or name => not available - * 'P' in number or name => presentation not allowed - */ -static int brcm_signal_callerid(struct ast_channel *chan, struct brcm_subchannel *sub) -{ - if (channel_config[sub->parent->line_id].ringsignal) { - CLID_STRING clid_string; - struct timeval utc_time; - struct ast_tm local_time; - char number[CLID_MAX_NUMBER] = ""; - char name[CLID_MAX_NAME] = ""; - - /* Add datetime to caller id string, format: MMDDHHMM */ - utc_time = ast_tvnow(); - ast_localtime(&utc_time, &local_time, NULL); - sprintf(clid_string.date, - "%02d%02d%02d%02d, ", - local_time.tm_mon + 1, - local_time.tm_mday, - local_time.tm_hour, - local_time.tm_min); - - /* Get connected line identity if valid and presentation is allowed */ - if (chan) { - if ((ast_party_id_presentation(&ast_channel_connected(chan)->id) & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED) { // Caller id presentation is allowed - // Caller name is valid - if (ast_channel_connected(chan)->id.name.valid) { - strncpy(name, ast_channel_connected(chan)->id.name.str, sizeof(name) - 1); - } - - // Caller number is valid - if (ast_channel_connected(chan)->id.number.valid) { - strncpy(number, ast_channel_connected(chan)->id.number.str, sizeof(number) - 1); - - // If anonymous/unsubscribed number we marked caller name as private "P" - if (strcasecmp("anonymous", ast_channel_connected(chan)->id.number.str) == 0 || - strcasecmp("unsubscribed", ast_channel_connected(chan)->id.number.str) == 0) { - strcpy(number, "P"); - strcpy(name, "P"); - } else if (strcasecmp("unavailable",ast_channel_connected(chan)->id.number.str) ==0 ) { - // If unavailable number we mark caller name as not available "O" - strcpy(number, "O"); - strcpy(name, "O"); - } - } else { - strcpy(number, "O"); - } - } else { - /* Caller id presentation is not allowed */ - strcpy(number, "P"); - strcpy(name, "P"); - } - } else { - // Name and number not available. - strcpy(number, "O"); - strcpy(name, "O"); - } - - /* Add number and name to caller id string, format: number,"name" */ - int str_length = 0; - strncpy(&clid_string.number_name[str_length], number, CLID_MAX_NUMBER); - str_length = strlen(number); - clid_string.number_name[str_length++] = ','; - clid_string.number_name[str_length++] = '"'; - strncpy(&clid_string.number_name[str_length], name, CLID_MAX_NAME); - str_length = strlen(clid_string.number_name); - clid_string.number_name[str_length++] = '"'; - clid_string.number_name[str_length++] = '\0'; - - ast_debug(2, "CLIP info: caller number [%s], caller name [%s], final string [%s]\n", number, name, (char *)&clid_string); - endpt_signal(sub->parent->line_id, "callid", "on", (char *)&clid_string); - return 0; - } - - return 0; -} - -static int brcm_create_connection(struct brcm_subchannel *sub) { - if (!sub->connection_init) { - /* generate random nr for rtp header */ - sub->ssrc = rand(); - - ast_debug(1, "Creating virtual Asterisk connection for pvt line_id=%i connection_id=%d\n", sub->parent->line_id, sub->connection_id); - sub->connection_init = 1; - sub->jitter_count = 0; - sub->farEndInterrivalJitter = 0; - - if (sub->owner) { - sub->call_id = ast_channel_callid(sub->owner); - } else { - sub->call_id = CALLID_OBTAINING; - } - - if(!brcm_in_onhold(sub->parent) && !brcm_in_call(sub->parent)) { // Is there another connection already? - ast_debug(1, "Creating real endpoint connection for pvt line_id=%i, connection_id: %d, call_id: %d\n", sub->parent->line_id, sub->connection_id, sub->call_id); - endpt_connection(sub->parent->line_id, sub->call_id, "create"); - } else if (get_callid_state(sub->call_id) == CALLID_ESTABLISHED) { - ast_debug(1, "Updating real endpoint connection for pvt line_id=%i, connection_id: %d, call_id: %d\n", sub->parent->line_id, sub->connection_id, sub->call_id); - endpt_connection(sub->parent->line_id, sub->call_id, "update"); - } - } - - return 0; -} - -static int brcm_mute_connection(struct brcm_subchannel *sub) -{ - /* Workaround for AA. Unmuting is not working. Throw away packets in packets thread instead */ - return 0; -} - -static int brcm_unmute_connection(struct brcm_subchannel *sub) -{ - /* Workaround for AA. Unmuting is not working. Throw away packets in packets thread instead */ - return 0; -} - -/* Put all subchannels in conferencing mode */ -static int brcm_create_conference(struct brcm_pvt *p) -{ - struct brcm_subchannel *second, *onhold; - struct ast_bridge *onholdBridge, *secondBridge; - struct ast_channel *chanToKick[1]; - struct ast_frame astFrame; - int res; - - memset(&astFrame, 0, sizeof(astFrame)); - astFrame.src = "TELCHAN"; - // Second call from initiator. - second = brcm_get_active_subchannel(p); - if(!second || !second->owner) return -1; - // Second bridge. Initiator + second remote call. - secondBridge = ast_channel_internal_bridge(second->owner); - if(!secondBridge) return -1; - - // First call from initiator (is onhold). - onhold = brcm_get_onhold_subchannel(p); - if(!onhold || !onhold->owner) return -1; - - ast_log(LOG_NOTICE, "Starting conference for pvt line_id=%i connection_id=%d\n", - onhold->parent->line_id, onhold->connection_id); - - /* First bridge. Initiator + first (active but - * waiting in background) remote call. */ - onholdBridge = ast_channel_internal_bridge(onhold->owner); - if(!onholdBridge) return -1; - - /* Put second initiator call onhold and unhold the first initiator - * call. The other way around would be better, but for some reason - * it doesn't work... If doing so, the second call get one way - * audio only. */ - second->conference_initiator = 0; - second->conference_id = strdup(secondBridge->uniqueid); - brcm_mute_connection(second); - ast_queue_hold(second->owner, NULL); - second->channel_state = ONHOLD; - onhold->conference_id = strdup(onholdBridge->uniqueid); - onhold->conference_initiator = 1; - brcm_unmute_connection(onhold); - ast_queue_unhold(second->owner); - onhold->channel_state = INCALL; - sched_yield(); - - // Move second call into first bridge and wait for it to finish. - chanToKick[0] = second->owner; - - pvt_lock(second->parent, "moving call to first bridge"); - if(onholdBridge && secondBridge) - res = ast_bridge_merge(onholdBridge, secondBridge, 0, chanToKick, 1); - while(ast_bridge_find_by_id(second->conference_id)) sched_yield(); - pvt_unlock(second->parent); - - // SIP calls need unhold sent to the bridge as well. - astFrame.frametype = AST_FRAME_CONTROL; - astFrame.subclass.integer = AST_CONTROL_UNHOLD; - ast_bridge_queue_everyone_else(onholdBridge, NULL, &astFrame); - - ast_log(LOG_NOTICE,"Conference started \n"); - return res; -} - -static int brcm_stop_conference(struct brcm_subchannel *p) -{ - struct ast_bridge *confBridge; - struct brcm_pvt *pvt; - - pvt = p->parent; - if (p->connection_init && p->owner) { - ast_debug(1, "Ending conference for pvt line_id=%i connection_id=%d\n", - p->parent->line_id, p->connection_id); - - // Force end of the conference if it's still active. - confBridge = ast_bridge_find_by_id(p->conference_id); - if(!confBridge) return -1; - - if(ao2_ref(confBridge, +1) >= 0 && confBridge->uniqueid && - confBridge->technology) { - ast_bridge_destroy(confBridge, AST_CAUSE_NORMAL_CLEARING); - } - ao2_ref(confBridge, -1); - } - brcm_send_ubus_event("CONFERENCE_STOPPED",pvt->line_id); - return 0; -} - -static void brcm_attended_call_transfer(struct brcm_subchannel *sub, struct brcm_subchannel *sub_peer, - struct ast_channel *owner, struct ast_channel *peer_owner) -{ - struct brcm_pvt *p = sub->parent; - - if( channel_config[p->line_id].flashSpec == FLASH_SPEC_ETSI || - channel_config[p->line_id].flashSpec == FLASH_SPEC_UK && ((sub_peer->conf_timer_id != -1) || (sub->conf_timer_id != -1))) { - ast_log(LOG_NOTICE ,"Setting up attended call transfer \n"); - if(sub_peer->conf_timer_id != -1) - if (ast_sched_del(sched, sub_peer->conf_timer_id)) { - ast_log(LOG_WARNING, "Failed to remove scheduled conference setup timer\n"); - } - sub_peer->conf_timer_id = -1; - if(sub->conf_timer_id != -1) - if (ast_sched_del(sched, sub->conf_timer_id)) { - ast_log(LOG_WARNING, "Failed to remove scheduled conference setup timer\n"); - } - sub->conf_timer_id = -1; - - if (sub->channel_state == INCALL && sub_peer->channel_state == ONHOLD && - owner && peer_owner) { - struct ast_channel *bridged_chan_inactive = ast_channel_bridge_peer(peer_owner); - struct ast_channel *bridged_chan_active = ast_channel_bridge_peer(owner); - char dest[AST_CHANNEL_NAME * 2] = { '\0', }; - - if (bridged_chan_inactive && bridged_chan_active) { - // Hold the transfer target by default unless being configured no - if (hold_target_before_refer) - ast_queue_hold(owner, NULL); - - // Start the transfer by sending REFER to the transferee - snprintf(dest, sizeof(dest) - 1, "%s?Replaces=%s", - ast_channel_exten(owner), ast_channel_name(bridged_chan_active)); - ast_debug(1, "Start transfer to [%s] on channel %s\n", dest, ast_channel_name(bridged_chan_active)); - int res = -1; - ast_channel_lock(bridged_chan_inactive); - if (!ast_test_flag(ast_channel_flags(bridged_chan_inactive), AST_FLAG_ZOMBIE) && - !ast_check_hangup(bridged_chan_inactive)) { - if (ast_channel_tech(bridged_chan_inactive)->transfer) { - res = ast_channel_tech(bridged_chan_inactive)->transfer(bridged_chan_inactive, dest); - if (res == 0) - res = 1; - } else - res = 0; - } - ast_channel_unlock(bridged_chan_inactive); - if (res < 0) { - ast_log(LOG_ERROR, "ast_transfer() failed\n"); - } else if (res == 0) { - ast_log(LOG_ERROR, "ast_transfer() is not supported on the peer channel\n"); - } else { - sub->channel_state = TRANSFERING; - } - } else { - ast_log(LOG_ERROR, "can't get the peer channel\n"); - } - } - } - else - ast_log(LOG_ERROR,"Late R4 ,Ignoring since conference should be set up by now \n"); -} - -static void brcm_unattended_call_transfer(struct brcm_subchannel *sub, struct brcm_subchannel *sub_peer, - struct ast_channel *owner, struct ast_channel *peer_owner) -{ - struct brcm_pvt *p = sub->parent; - - ast_log(LOG_ERROR ,"flashSpec sub_peer->conf_timer_id: %d, sub->conf_timer_id: %d\n", sub_peer->conf_timer_id, sub->conf_timer_id); - if((sub_peer->conf_timer_id != -1) || (sub->conf_timer_id != -1)) { - ast_log(LOG_NOTICE ,"Setting up unattended call transfer \n"); - if(sub->conf_timer_id != -1) { - if (ast_sched_del(sched, sub->conf_timer_id)) - ast_log(LOG_ERROR, "Failed to remove scheduled conference setup timer\n"); - ast_debug(4, "sub->conf_timer_id deleted\n"); - } - sub->conf_timer_id = -1; - if(sub_peer->conf_timer_id != -1) { - if (ast_sched_del(sched, sub_peer->conf_timer_id)) - ast_log(LOG_ERROR, "Failed to remove scheduled conference setup timer\n"); - ast_debug(4, "sub_peer->conf_timer_id deleted\n"); - } - sub_peer->conf_timer_id = -1; - - channel_settings *s = &channel_config[p->line_id]; - if (!s->calleridenable) { - p->tech->stop_ringing(p); - } else { - p->tech->stop_ringing_callerid_pending(p); - } - - // unattended call transfer: cancel the the call to transfer target - if (sub->owner && sub_peer->owner) { - strncpy(sub_peer->blind_xfer_target, ast_channel_exten(owner), sizeof(sub->blind_xfer_target) - 1); - ast_queue_control(sub->owner, AST_CONTROL_HANGUP); - ast_queue_unhold(sub_peer->owner); - sub_peer->channel_state = TRANSFERING; - } - } - else - ast_log(LOG_ERROR,"Late R5 ,Ignoring since conference should be set up by now \n"); -} - -static int brcm_close_connection(struct brcm_subchannel *sub) -{ - struct brcm_pvt *p = sub->parent; - - if (sub->connection_init) { - if (!brcm_in_onhold(p) && !brcm_in_call(p) && !brcm_in_dialing(p) && !brcm_in_ringback(p) && - !brcm_in_callwaiting(p) && !brcm_in_transferring(p)) { // Does the line have another call? - ast_debug(1, "Closing real endpoint connection line_id: %d, connection_id=%d, call_id: %d\n", p->line_id, sub->connection_id, sub->call_id); - endpt_connection(p->line_id, sub->call_id, "destroy"); - } else { - ast_debug(1, "Releasing connection for pvt line_id=%i connection_id=%d, call_id: %d\n", - sub->parent->line_id, sub->connection_id, sub->call_id); - endpt_connection(p->line_id, sub->call_id, "release"); - } - sub->connection_init = 0; - sub->codec = -1; - sub->call_id = CALLID_INVALID; - ast_debug(1, "Virtual Asterisk connection %d/%d destroyed\n", p->line_id, sub->connection_id); - } - - return 0; -} - - -/* Generate rtp payload, 12 bytes of header and 160 bytes of ulaw payload */ -static void brcm_generate_rtp_packet(struct brcm_subchannel *sub, uint8_t *packet_buf, int type, int marker, int dtmf_timestamp, int seqno) { - unsigned short* packet_buf16 = (unsigned short*)packet_buf; - unsigned int* packet_buf32 = (unsigned int*)packet_buf; - - //Generate the rtp header, packet is zero from the start, that fact is used - packet_buf[0] |= 0x80; //Set version 2 of header - //Padding 0 - //Extension 0 - //CSRC count 0 - packet_buf[1] = type; - packet_buf[1] |= marker?0x80:0x00; - packet_buf16[1] = htons(seqno); //Add sequence number - packet_buf32[1] = htonl(sub->time_stamp); //Add timestamp - sub->time_stamp += sub->period*8; - packet_buf32[2] = sub->ssrc; //Random SSRC -} - -static void brcm_interpret_rtcp_packet(struct brcm_subchannel *p, uint8_t *rtcp_frame, uint32_t rtcp_size) { - unsigned int *rtcp_word = (unsigned int *)(rtcp_frame); // 32 bits packet - unsigned int packetwords = rtcp_size / 4; // number of packets - unsigned int position = 0; - unsigned int pt; - - while (position < packetwords) { - unsigned int temp_word = ntohl(rtcp_word[position]); - pt = (temp_word >> 16) & 0xFF; - switch (pt) { - case RTCP_SR: - p->jitter_count++; - p->farEndInterrivalJitter += ntohl(rtcp_word[position+10]); - /* Intentional fall through */ - case RTCP_RR: - case RTCP_SDES: - case RTCP_XR: - //replacing ssrc - rtcp_word[position + 1] = p->ssrc; - break; - default: - break; - } - position++; - } -} - -static void brcm_dialtone_set(struct brcm_pvt *p, dialtone_state state) -{ - ast_debug(3, "Old dialtone: %s, new dialtone: %s\n", - dialtone_map[p->dialtone].str, dialtone_map[state].str); - - if (state != p->dialtone) { - ast_debug(2, "Changing dialtone for pvt %d from '%s' to '%s'\n", - p->line_id, - dialtone_map[p->dialtone].str, - dialtone_map[state].str); - p->dialtone = state; - brcm_signal_dialtone(p); - } -} - -static const char *feature_access_code_string(char *buffer, unsigned int buffer_length) -{ - struct feature_access_code *current; - - if (AST_LIST_EMPTY(&feature_access_codes)) { - strncpy(buffer, "(empty)", buffer_length); - return buffer; - } - - buffer[0] = '\0'; - int write_length = 0; - AST_LIST_TRAVERSE(&feature_access_codes, current, list) { - int rv = snprintf(buffer + write_length, buffer_length - write_length, "%s ", current->code); - if (rv <= 0) { - break; - } - write_length += rv; - } - - return buffer; -} - -static int feature_access_code_add(const char *code) -{ - struct feature_access_code *fac; - - if (ast_strlen_zero(code)) { - ast_log(LOG_WARNING, "Zero length FAC\n"); - return 1; - } - - if (!(fac = ast_calloc(1, sizeof(*fac)))) { - ast_log(LOG_WARNING, "FAC alloc failed\n"); - return 1; - } - - ast_copy_string(fac->code, code, sizeof(fac->code)); - ast_log(LOG_DEBUG, "Adding FAC: [%s]\n", fac->code); - - AST_LIST_INSERT_TAIL(&feature_access_codes, fac, list); - return 0; -} - -static int feature_access_code_clear(void) -{ - struct feature_access_code *fac; - - while ((fac = AST_LIST_REMOVE_HEAD(&feature_access_codes, list))) { - ast_free(fac); - } - return 0; -} - -static int feature_access_code_match(char *sequence) -{ - struct feature_access_code *current; - int retval = -1; - - AST_LIST_TRAVERSE(&feature_access_codes, current, list) { - char *seq = sequence; - char *fac = current->code; - - int res = -1; - for (; *seq && *fac; seq++, fac++) { - if (*fac == '.') { - /* Perfect match */ - return 0; - } - else if (*seq == *fac) { - /* Partial match */ - res = 1; - } - else { - /* No match */ - res = -1; - break; - } - } - - if (res == 1 && *seq == *fac) { - /* Perfect match */ - return 0; - } - - if (res != -1) { - retval = res; - } - } - - return retval; -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Broadcom SLIC channel"); diff --git a/channels/chan_brcm.h b/channels/chan_brcm.h deleted file mode 100644 index c592e0a246..0000000000 --- a/channels/chan_brcm.h +++ /dev/null @@ -1,286 +0,0 @@ -/* Inteno AB, Stockholm, Sweden - * Channel for Broadcom FXS ports - */ - -#ifndef CHAN_BRCM_H -#define CHAN_BRCM_H - -/* Change this value when needed */ -#define CHANNEL_VERSION "1.2" - -#define DEFAULT_CALLER_ID "Unknown" -#define PHONE_MAX_BUF 480 -#define CALL_STATUS_MAX_LEN 14 - -#define TIMEMSEC 1000 - -#define PCMU 0 -#define G726 2 -#define G723 4 -#define PCMA 8 -#define G729 18 -#define G722 9 -#define DTMF_PAYLOAD 101 -#define DTMF 128 -#define RTCP_SR 200 -#define RTCP_RR 201 -#define RTCP_SDES 202 -#define RTCP_XR 207 - -#define CN 13 - -#define NOT_INITIALIZED -1 -//#define EPSTATUS_DRIVER_ERROR -1 -#define MAX_NUM_LINEID 30 -#define PACKET_BUFFER_SIZE 1024 -#define NUM_SUBCHANNELS 2 - -#define BEGIN 0 -#define CONT 1 -#define END 2 - -enum brcm_channel_state { - ONHOOK, - OFFHOOK, - DIALING, - CALLING, - INCALL, - ANSWER, - CALLENDED, - RINGING, - CALLWAITING, - ONHOLD, - TRANSFERING, - RINGBACK, - AWAITONHOOK, - ALLOCATED, -}; - -enum LINE_EVENT { // Events from low level line (endpoint etc.) - EVENT_DTMF0, - EVENT_DTMF1, - EVENT_DTMF2, - EVENT_DTMF3, - EVENT_DTMF4, - EVENT_DTMF5, - EVENT_DTMF6, - EVENT_DTMF7, - EVENT_DTMF8, - EVENT_DTMF9, - EVENT_DTMFA, - EVENT_DTMFB, - EVENT_DTMFC, - EVENT_DTMFD, - EVENT_DTMFH, - EVENT_DTMFS, - EVENT_OFFHOOK, - EVENT_ONHOOK, - EVENT_FLASH, - EVENT_CALL_REJECT, - EVENT_DECT_UNAVAILABLE, - EVENT_SWITCH, - EVENT_JOIN, - EVENT_RELEASE, - EVENT_LAST, -}; - -enum endpoint_type { - FXS, - FXO, - DECT, -}; - -enum CALL_DIRECTION { - INCOMING_CALL, - OUTGOING_CALL, -}; - -typedef enum dialtone_state { - DIALTONE_OFF = 0, - DIALTONE_ON, - DIALTONE_CONGESTION, - DIALTONE_SPECIAL_CONDITION, - DIALTONE_UNOBTAINABLE, - DIALTONE_HOWLER, - DIALTONE_UNKNOWN, - DIALTONE_MWI_OFF, - DIALTONE_LAST, -} dialtone_state; - -typedef enum callid_state { - CALLID_INVALID = -1, - CALLID_OBTAINING = 0, - CALLID_ESTABLISHED = 1 -} callid_state; - -struct brcm_subchannel { - int id; - int call_id; /* The call_id of connection assigned by pjsip */ - struct ast_channel *owner; /* Channel we belong to, possibly NULL */ - int connection_id; /* Current connection id, may be -1 */ - enum brcm_channel_state channel_state; /* Channel states */ - enum CALL_DIRECTION call_direction; // Direction of call for the subchannel : 0 = incoming, 1 = outgoing - unsigned int connection_init; /* State for endpoint id connection initialization */ - struct ast_frame fr; /* Frame */ - unsigned int time_stamp; /* Endpoint RTP time stamp state */ - unsigned int period; /* Endpoint RTP period */ - unsigned int ssrc; /* Endpoint RTP synchronization source */ - int codec; /* Used codec */ - struct brcm_pvt *parent; /* brcm_line owning this subchannel */ - int cw_timer_id; /* Current call waiting timer id, -1 if no active timer */ - int cwBeep_timer_id; /* Current call waiting beep timer id, -1 if no active timer */ - int r4_hangup_timer_id; /* Current R4 hangup timer id, -1 if no active timer */ - int onhold_hangup_timer_id; /* Current onhold hangup timer id, -1 if no active timer */ - int conference_initiator; /* True if this subchannel is the original leg in a 3-way conference */ - char *conference_id; /* uuid of the conference initiated by this subchannel */ - int conf_timer_id; /* Current conference call timer id, -1 if no active timer */ - rtp_statistics rtp_stats; /* RTP statistics for currently hanging up channel */ - unsigned int jitter_count; - unsigned long int farEndInterrivalJitter; - char blind_xfer_target[32]; /* Transfer target for unattended call transfer */ -}; - - -struct brcm_channel_tech { - int (* signal_ringing)(struct brcm_pvt *p); - int (* signal_ringing_callerid_pending)(struct brcm_pvt *p); - int (* signal_callerid)(struct ast_channel *chan, struct brcm_subchannel *s); - int (* stop_ringing)(struct brcm_pvt *p); - int (* stop_ringing_callerid_pending)(struct brcm_pvt *p); - int (* release)(struct brcm_pvt *p); -}; - - -struct brcm_pvt { - ast_mutex_t lock; - int fd; /* Raw file descriptor for this device */ - int line_id; /* Maps to the correct port */ - char dtmfbuf[AST_MAX_EXTENSION];/* DTMF buffer per channel */ - int dtmf_len; /* Length of DTMF buffer */ - int dtmf_first; /* DTMF control state, button pushes generate 2 events, one on button down and one on button up */ - struct ast_format_cap * lastformat; /* Last output format */ - struct ast_format_cap * lastinput; /* Last input format */ - struct brcm_pvt *next; /* Next channel in list */ - char offset[AST_FRIENDLY_OFFSET]; - char buf[PHONE_MAX_BUF]; /* Static buffer for reading frames */ - char context_direct[AST_MAX_EXTENSION]; - char context[AST_MAX_EXTENSION]; - char obuf[PHONE_MAX_BUF * 2]; - char ext[AST_MAX_EXTENSION]; - char language[MAX_LANGUAGE]; - char cid_num[AST_MAX_EXTENSION]; - char cid_name[AST_MAX_EXTENSION]; - char extensionCallStatus[CALL_STATUS_MAX_LEN]; - - struct brcm_subchannel *sub[NUM_SUBCHANNELS]; /* List of sub-channels, needed for callwaiting and 3-way support */ - int hf_detected; /* Hook flash detected */ - dialtone_state dialtone; /* Set by manager command */ - struct brcm_channel_tech *tech; - - int interdigit_timer_id; /* Id of timer that tracks interdigit timeout */ - int autodial_timer_id; /* Id of timer that tracks autodial timeout */ - int dialtone_timeout_timer_id; /* Id of timer that tracks dialtone timeout */ - int onhold_hangup_timer_id; /* Id of timer that tracks onhold hangup timeout */ -}; - -enum rtp_type { - BRCM_UNKNOWN, - BRCM_AUDIO, - BRCM_RTCP_SR, - BRCM_RTCP_RR -}; - - - -/* Mapping of DTMF to char/name/intval */ -typedef struct DTMF_CHARNAME_MAP -{ - enum LINE_EVENT event; - char name[12]; - char c; - int i; -} DTMF_CHARNAME_MAP; - - -typedef enum flash_spec { - FLASH_SPEC_UK = 0, - FLASH_SPEC_ETSI, -} flash_spec; - -typedef struct DIALTONE_MAP -{ - dialtone_state state; - char str[24]; -} DIALTONE_MAP; - -static const DIALTONE_MAP dialtone_map[] = -{ - {DIALTONE_OFF, "off"}, - {DIALTONE_ON, "on"}, - {DIALTONE_CONGESTION, "congestion"}, - {DIALTONE_SPECIAL_CONDITION, "special"}, - {DIALTONE_UNOBTAINABLE, "number unobtainable"}, - {DIALTONE_HOWLER, "howler"}, - {DIALTONE_UNKNOWN, "unknown"}, - {DIALTONE_LAST, "-"}, -}; - -/* Struct for individual endpoint settings */ -typedef struct { - int enabled; - char language[MAX_LANGUAGE]; - char cid_num[AST_MAX_EXTENSION]; - char cid_name[AST_MAX_EXTENSION]; - char context_direct[AST_MAX_EXTENSION]; //Context that will be checked for exact matches - char context[AST_MAX_EXTENSION]; //Default context for dialtone mode - char autodial_ext[AST_MAX_EXTENSION]; - int autodial_timeoutmsec; - int ringsignal; - int timeoutmsec; - int interdigitopenmsec; - int minimumnumberdigits; - char terminationdigit; - int period; - int hangup_xfer; - int dialtone_timeoutmsec; - int offhook_nu_timeoutmsec; - int offhook_silence_timeoutmsec; - int do_not_disturb; - int anonymouscallenable; - int calleridenable; - int calleridnameenable; - flash_spec flashSpec; - int mwi_enabled; -} channel_settings; - - -/* Caller ID */ -#define CLID_MAX_DATE 10 -#define CLID_MAX_NUMBER 21 -#define CLID_MAX_NAME 16 -typedef struct CLID_STRING -{ - char date[CLID_MAX_DATE]; - char number_name[CLID_MAX_NUMBER + CLID_MAX_NAME + 4]; // 4 = comma, quotation marks and null terminator -} CLID_STRING; - - -/* Global jitterbuffer configuration - by default, jb is disabled */ -static struct ast_jb_conf default_jbconf = -{ - .flags = 0, - .max_size = -1, - .resync_threshold = -1, - .impl = "", - .target_extra = -1, -}; - - -#define DEFAULT_CALL_WAITING_TIMEOUT 20 // In seconds -#define DEFAULT_CALL_WAITING_BEEP_FREQ 5 // In seconds - -#define DEFAULT_R4_HANGUP_TIMEOUT 5000 // In milliseconds -#define DEFAULT_ONHOLD_HANGUP_TIMEOUT 20 // In seconds - - -#endif /* CHAN_BRCM_H */ diff --git a/configs/brcm.conf.sample b/configs/brcm.conf.sample deleted file mode 100644 index fb80497c1a..0000000000 --- a/configs/brcm.conf.sample +++ /dev/null @@ -1,50 +0,0 @@ -; -; Linux Telephony Interface -; -; Configuration file -; -[interfaces] -; -; Select a mode, either the phone jack provides dialtone, reads digits, -; then starts PBX with the given extension (dialtone mode), or -; immediately provides the PBX without reading any digits or providing -; any dialtone (this is the immediate mode, the default). Also, you -; can set the mode to "fxo" if you have a linejack to make it operate -; properly. -; -mode=immediate -;mode=dialtone -;mode=fxo -; -; You can decide which format to use by default, "g723.1" or "slinear". -; XXX Be careful, sometimes the card causes kernel panics when running -; in signed linear mode for some reason... XXX -; -;format=slinear -format=g723.1 -; -; And set the echo cancellation to "off", "low", "medium", and "high". -; This is not supported on all phones. -; -echocancel=medium -; -; You can optionally use VAD/CNG silence supression -; -;silencesupression=yes -; -; List all devices we can use. Contexts may also be specified -; -;context=local -; -; You can set txgain and rxgain for each device in the same way as context. -; If you want to change default gain value (1.0 =~ 100%) for device, simple -; add txgain or rxgain line before device line. But rememeber, if you change -; volume all cards listed below will be affected by these values. You can -; use float values (1.0, 0.5, 2.0) or percentage values (100%, 150%, 50%). -; -;txgain=100% -;rxgain=1.0 -device => /dev/bcmendpoint0 - - - diff --git a/include/asterisk/cdr.h b/include/asterisk/cdr.h index 32509bdd2f..d421e36a56 100644 --- a/include/asterisk/cdr.h +++ b/include/asterisk/cdr.h @@ -271,7 +271,7 @@ struct ast_cdr_config { } batch_settings; }; -/*! \brief Struct for rtp statistics get from brcm */ +/*! \brief Struct for rtp statistics get from voicemngr */ typedef struct { uint16_t localBurstDensity; uint16_t remoteBurstDensity; diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 60b03244c7..2843de513f 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -597,7 +597,7 @@ typedef struct { const char *value; } ast_chan_write_info_t; -/*! \brief Struct for rtp statistics get from brcm */ +/*! \brief Struct for rtp statistics get from voicemngr */ typedef struct { uint16_t localBurstDensity; uint16_t remoteBurstDensity; -- GitLab