From 3d63833bd6c869b7efa383e8dea14be1a6eff998 Mon Sep 17 00:00:00 2001 From: Richard Mudgett Date: Tue, 21 May 2013 18:00:22 +0000 Subject: Merge in the bridge_construction branch to make the system use the Bridging API. Breaks many things until they can be reworked. A partial list: chan_agent chan_dahdi, chan_misdn, chan_iax2 native bridging app_queue COLP updates DTMF attended transfers Protocol attended transfers git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@389378 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- main/core_unreal.c | 855 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 855 insertions(+) create mode 100644 main/core_unreal.c (limited to 'main/core_unreal.c') diff --git a/main/core_unreal.c b/main/core_unreal.c new file mode 100644 index 000000000..d5e588111 --- /dev/null +++ b/main/core_unreal.c @@ -0,0 +1,855 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013 Digium, Inc. + * + * Richard Mudgett + * + * 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 + * \brief Unreal channel derivatives framework for channel drivers like local channels. + * + * \author Richard Mudgett + * + * See Also: + * \arg \ref AstCREDITS + */ + +/*** MODULEINFO + core + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/causes.h" +#include "asterisk/channel.h" +#include "asterisk/pbx.h" +#include "asterisk/musiconhold.h" +#include "asterisk/astobj2.h" +#include "asterisk/bridging.h" +#include "asterisk/core_unreal.h" + +static unsigned int name_sequence = 0; + +void ast_unreal_lock_all(struct ast_unreal_pvt *p, struct ast_channel **outchan, struct ast_channel **outowner) +{ + struct ast_channel *chan = NULL; + struct ast_channel *owner = NULL; + + ao2_lock(p); + for (;;) { + if (p->chan) { + chan = p->chan; + ast_channel_ref(chan); + } + if (p->owner) { + owner = p->owner; + ast_channel_ref(owner); + } + ao2_unlock(p); + + /* if we don't have both channels, then this is very easy */ + if (!owner || !chan) { + if (owner) { + ast_channel_lock(owner); + } else if(chan) { + ast_channel_lock(chan); + } + } else { + /* lock both channels first, then get the pvt lock */ + ast_channel_lock_both(chan, owner); + } + ao2_lock(p); + + /* Now that we have all the locks, validate that nothing changed */ + if (p->owner != owner || p->chan != chan) { + if (owner) { + ast_channel_unlock(owner); + owner = ast_channel_unref(owner); + } + if (chan) { + ast_channel_unlock(chan); + chan = ast_channel_unref(chan); + } + continue; + } + + break; + } + *outowner = p->owner; + *outchan = p->chan; +} + +/* Called with ast locked */ +int ast_unreal_setoption(struct ast_channel *ast, int option, void *data, int datalen) +{ + int res = 0; + struct ast_unreal_pvt *p; + struct ast_channel *otherchan = NULL; + ast_chan_write_info_t *write_info; + + if (option != AST_OPTION_CHANNEL_WRITE) { + return -1; + } + + write_info = data; + + if (write_info->version != AST_CHAN_WRITE_INFO_T_VERSION) { + ast_log(LOG_ERROR, "The chan_write_info_t type has changed, and this channel hasn't been updated!\n"); + return -1; + } + + if (!strcmp(write_info->function, "CHANNEL") + && !strncasecmp(write_info->data, "hangup_handler_", 15)) { + /* Block CHANNEL(hangup_handler_xxx) writes to the other unreal channel. */ + return 0; + } + + /* get the tech pvt */ + if (!(p = ast_channel_tech_pvt(ast))) { + return -1; + } + ao2_ref(p, 1); + ast_channel_unlock(ast); /* Held when called, unlock before locking another channel */ + + /* get the channel we are supposed to write to */ + ao2_lock(p); + otherchan = (write_info->chan == p->owner) ? p->chan : p->owner; + if (!otherchan || otherchan == write_info->chan) { + res = -1; + otherchan = NULL; + ao2_unlock(p); + goto setoption_cleanup; + } + ast_channel_ref(otherchan); + + /* clear the pvt lock before grabbing the channel */ + ao2_unlock(p); + + ast_channel_lock(otherchan); + res = write_info->write_fn(otherchan, write_info->function, write_info->data, write_info->value); + ast_channel_unlock(otherchan); + +setoption_cleanup: + ao2_ref(p, -1); + if (otherchan) { + ast_channel_unref(otherchan); + } + ast_channel_lock(ast); /* Lock back before we leave */ + return res; +} + +/* Called with ast locked */ +int ast_unreal_queryoption(struct ast_channel *ast, int option, void *data, int *datalen) +{ + struct ast_unreal_pvt *p; + struct ast_channel *peer; + struct ast_channel *other; + int res = 0; + + if (option != AST_OPTION_T38_STATE) { + /* AST_OPTION_T38_STATE is the only supported option at this time */ + return -1; + } + + /* for some reason the channel is not locked in channel.c when this function is called */ + if (!(p = ast_channel_tech_pvt(ast))) { + return -1; + } + + ao2_lock(p); + other = AST_UNREAL_IS_OUTBOUND(ast, p) ? p->owner : p->chan; + if (!other) { + ao2_unlock(p); + return -1; + } + ast_channel_ref(other); + ao2_unlock(p); + ast_channel_unlock(ast); /* Held when called, unlock before locking another channel */ + + peer = ast_channel_bridge_peer(other); + if (peer) { + res = ast_channel_queryoption(peer, option, data, datalen, 0); + ast_channel_unref(peer); + } + ast_channel_unref(other); + ast_channel_lock(ast); /* Lock back before we leave */ + + return res; +} + +/*! + * \brief queue a frame onto either the p->owner or p->chan + * + * \note the ast_unreal_pvt MUST have it's ref count bumped before entering this function and + * decremented after this function is called. This is a side effect of the deadlock + * avoidance that is necessary to lock 2 channels and a tech_pvt. Without a ref counted + * ast_unreal_pvt, it is impossible to guarantee it will not be destroyed by another thread + * during deadlock avoidance. + */ +static int unreal_queue_frame(struct ast_unreal_pvt *p, int isoutbound, struct ast_frame *f, + struct ast_channel *us, int us_locked) +{ + struct ast_channel *other; + + /* Recalculate outbound channel */ + other = isoutbound ? p->owner : p->chan; + if (!other) { + return 0; + } + + /* do not queue frame if generator is on both unreal channels */ + if (us && ast_channel_generator(us) && ast_channel_generator(other)) { + return 0; + } + + /* grab a ref on the channel before unlocking the pvt, + * other can not go away from us now regardless of locking */ + ast_channel_ref(other); + if (us && us_locked) { + ast_channel_unlock(us); + } + ao2_unlock(p); + + if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_RINGING) { + ast_setstate(other, AST_STATE_RINGING); + } + ast_queue_frame(other, f); + + other = ast_channel_unref(other); + if (us && us_locked) { + ast_channel_lock(us); + } + ao2_lock(p); + + return 0; +} + +int ast_unreal_answer(struct ast_channel *ast) +{ + struct ast_unreal_pvt *p = ast_channel_tech_pvt(ast); + int isoutbound; + int res = -1; + + if (!p) { + return -1; + } + + ao2_ref(p, 1); + ao2_lock(p); + isoutbound = AST_UNREAL_IS_OUTBOUND(ast, p); + if (isoutbound) { + /* Pass along answer since somebody answered us */ + struct ast_frame answer = { AST_FRAME_CONTROL, { AST_CONTROL_ANSWER } }; + + res = unreal_queue_frame(p, isoutbound, &answer, ast, 1); + } else { + ast_log(LOG_WARNING, "Huh? %s is being asked to answer?\n", + ast_channel_name(ast)); + } + ao2_unlock(p); + ao2_ref(p, -1); + return res; +} + +/*! + * \internal + * \brief Check and optimize out the unreal channels between bridges. + * \since 12.0.0 + * + * \param ast Channel writing a frame into the unreal channels. + * \param p Unreal channel private. + * + * \note It is assumed that ast is locked. + * \note It is assumed that p is locked. + * + * \retval 0 if unreal channels were not optimized out. + * \retval non-zero if unreal channels were optimized out. + */ +static int got_optimized_out(struct ast_channel *ast, struct ast_unreal_pvt *p) +{ + /* Do a few conditional checks early on just to see if this optimization is possible */ + if (ast_test_flag(p, AST_UNREAL_NO_OPTIMIZATION) || !p->chan || !p->owner) { + return 0; + } + if (ast == p->owner) { + return ast_bridge_unreal_optimized_out(p->owner, p->chan); + } + if (ast == p->chan) { + return ast_bridge_unreal_optimized_out(p->chan, p->owner); + } + /* ast is not valid to optimize. */ + return 0; +} + +struct ast_frame *ast_unreal_read(struct ast_channel *ast) +{ + return &ast_null_frame; +} + +int ast_unreal_write(struct ast_channel *ast, struct ast_frame *f) +{ + struct ast_unreal_pvt *p = ast_channel_tech_pvt(ast); + int res = -1; + + if (!p) { + return -1; + } + + /* Just queue for delivery to the other side */ + ao2_ref(p, 1); + ao2_lock(p); + switch (f->frametype) { + case AST_FRAME_VOICE: + case AST_FRAME_VIDEO: + if (got_optimized_out(ast, p)) { + break; + } + /* fall through */ + default: + res = unreal_queue_frame(p, AST_UNREAL_IS_OUTBOUND(ast, p), f, ast, 1); + break; + } + ao2_unlock(p); + ao2_ref(p, -1); + + return res; +} + +int ast_unreal_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) +{ + struct ast_unreal_pvt *p = ast_channel_tech_pvt(newchan); + struct ast_bridge *bridge_owner; + struct ast_bridge *bridge_chan; + + if (!p) { + return -1; + } + + ao2_lock(p); + + if ((p->owner != oldchan) && (p->chan != oldchan)) { + ast_log(LOG_WARNING, "Old channel %p wasn't %p or %p\n", oldchan, p->owner, p->chan); + ao2_unlock(p); + return -1; + } + if (p->owner == oldchan) { + p->owner = newchan; + } else { + p->chan = newchan; + } + + if (ast_check_hangup(newchan) || !p->owner || !p->chan) { + ao2_unlock(p); + return 0; + } + + /* Do not let a masquerade cause an unreal channel to be bridged to itself! */ + bridge_owner = ast_channel_internal_bridge(p->owner); + bridge_chan = ast_channel_internal_bridge(p->chan); + if (bridge_owner && bridge_owner == bridge_chan) { + ast_log(LOG_WARNING, "You can not bridge an unreal channel (%s) to itself!\n", + ast_channel_name(newchan)); + ao2_unlock(p); + ast_queue_hangup(newchan); + return -1; + } + + ao2_unlock(p); + return 0; +} + +/*! + * \internal + * \brief Queue up a frame representing the indication as a control frame. + * \since 12.0.0 + * + * \param p Unreal private structure. + * \param ast Channel indicating the condition. + * \param condition What is being indicated. + * \param data Extra data. + * \param datalen Length of extra data. + * + * \retval 0 on success. + * \retval AST_T38_REQUEST_PARMS if successful and condition is AST_CONTROL_T38_PARAMETERS. + * \retval -1 on error. + */ +static int unreal_queue_indicate(struct ast_unreal_pvt *p, struct ast_channel *ast, int condition, const void *data, size_t datalen) +{ + int res = 0; + int isoutbound; + + ao2_lock(p); + /* + * Block -1 stop tones events if we are to be optimized out. We + * don't need a flurry of these events on an unreal channel chain + * when initially connected to slow the optimization process. + */ + if (0 <= condition || ast_test_flag(p, AST_UNREAL_NO_OPTIMIZATION)) { + struct ast_frame f = { + .frametype = AST_FRAME_CONTROL, + .subclass.integer = condition, + .data.ptr = (void *) data, + .datalen = datalen, + }; + + isoutbound = AST_UNREAL_IS_OUTBOUND(ast, p); + res = unreal_queue_frame(p, isoutbound, &f, ast, 1); + if (!res + && condition == AST_CONTROL_T38_PARAMETERS + && datalen == sizeof(struct ast_control_t38_parameters)) { + const struct ast_control_t38_parameters *parameters = data; + + if (parameters->request_response == AST_T38_REQUEST_PARMS) { + res = AST_T38_REQUEST_PARMS; + } + } + } else { + ast_debug(4, "Blocked indication %d\n", condition); + } + ao2_unlock(p); + + return res; +} + +/*! + * \internal + * \brief Handle COLP and redirecting conditions. + * \since 12.0.0 + * + * \param p Unreal private structure. + * \param ast Channel indicating the condition. + * \param condition What is being indicated. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int unreal_colp_redirect_indicate(struct ast_unreal_pvt *p, struct ast_channel *ast, int condition) +{ + struct ast_channel *this_channel; + struct ast_channel *the_other_channel; + int isoutbound; + int res = 0; + + /* + * A connected line update frame may only contain a partial + * amount of data, such as just a source, or just a ton, and not + * the full amount of information. However, the collected + * information is all stored in the outgoing channel's + * connectedline structure, so when receiving a connected line + * update on an outgoing unreal channel, we need to transmit the + * collected connected line information instead of whatever + * happens to be in this control frame. The same applies for + * redirecting information, which is why it is handled here as + * well. + */ + ao2_lock(p); + isoutbound = AST_UNREAL_IS_OUTBOUND(ast, p); + if (isoutbound) { + this_channel = p->chan; + the_other_channel = p->owner; + } else { + this_channel = p->owner; + the_other_channel = p->chan; + } + if (the_other_channel) { + unsigned char frame_data[1024]; + struct ast_frame f = { + .frametype = AST_FRAME_CONTROL, + .subclass.integer = condition, + .data.ptr = frame_data, + }; + + if (condition == AST_CONTROL_CONNECTED_LINE) { + ast_connected_line_copy_to_caller(ast_channel_caller(the_other_channel), + ast_channel_connected(this_channel)); + f.datalen = ast_connected_line_build_data(frame_data, sizeof(frame_data), + ast_channel_connected(this_channel), NULL); + } else { + f.datalen = ast_redirecting_build_data(frame_data, sizeof(frame_data), + ast_channel_redirecting(this_channel), NULL); + } + res = unreal_queue_frame(p, isoutbound, &f, ast, 1); + } + ao2_unlock(p); + + return res; +} + +int ast_unreal_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen) +{ + struct ast_unreal_pvt *p = ast_channel_tech_pvt(ast); + int res = 0; + + if (!p) { + return -1; + } + + ao2_ref(p, 1); /* ref for unreal_queue_frame */ + + switch (condition) { + case AST_CONTROL_CONNECTED_LINE: + case AST_CONTROL_REDIRECTING: + res = unreal_colp_redirect_indicate(p, ast, condition); + break; + case AST_CONTROL_HOLD: + if (ast_test_flag(p, AST_UNREAL_MOH_INTERCEPT)) { + ast_moh_start(ast, data, NULL); + break; + } + res = unreal_queue_indicate(p, ast, condition, data, datalen); + break; + case AST_CONTROL_UNHOLD: + if (ast_test_flag(p, AST_UNREAL_MOH_INTERCEPT)) { + ast_moh_stop(ast); + break; + } + res = unreal_queue_indicate(p, ast, condition, data, datalen); + break; + default: + res = unreal_queue_indicate(p, ast, condition, data, datalen); + break; + } + + ao2_ref(p, -1); + return res; +} + +int ast_unreal_digit_begin(struct ast_channel *ast, char digit) +{ + struct ast_unreal_pvt *p = ast_channel_tech_pvt(ast); + int res = -1; + struct ast_frame f = { AST_FRAME_DTMF_BEGIN, }; + int isoutbound; + + if (!p) { + return -1; + } + + ao2_ref(p, 1); /* ref for unreal_queue_frame */ + ao2_lock(p); + isoutbound = AST_UNREAL_IS_OUTBOUND(ast, p); + f.subclass.integer = digit; + res = unreal_queue_frame(p, isoutbound, &f, ast, 0); + ao2_unlock(p); + ao2_ref(p, -1); + + return res; +} + +int ast_unreal_digit_end(struct ast_channel *ast, char digit, unsigned int duration) +{ + struct ast_unreal_pvt *p = ast_channel_tech_pvt(ast); + int res = -1; + struct ast_frame f = { AST_FRAME_DTMF_END, }; + int isoutbound; + + if (!p) { + return -1; + } + + ao2_ref(p, 1); /* ref for unreal_queue_frame */ + ao2_lock(p); + isoutbound = AST_UNREAL_IS_OUTBOUND(ast, p); + f.subclass.integer = digit; + f.len = duration; + res = unreal_queue_frame(p, isoutbound, &f, ast, 0); + ao2_unlock(p); + ao2_ref(p, -1); + + return res; +} + +int ast_unreal_sendtext(struct ast_channel *ast, const char *text) +{ + struct ast_unreal_pvt *p = ast_channel_tech_pvt(ast); + int res = -1; + struct ast_frame f = { AST_FRAME_TEXT, }; + int isoutbound; + + if (!p) { + return -1; + } + + ao2_ref(p, 1); /* ref for unreal_queue_frame */ + ao2_lock(p); + isoutbound = AST_UNREAL_IS_OUTBOUND(ast, p); + f.data.ptr = (char *) text; + f.datalen = strlen(text) + 1; + res = unreal_queue_frame(p, isoutbound, &f, ast, 0); + ao2_unlock(p); + ao2_ref(p, -1); + return res; +} + +int ast_unreal_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen) +{ + struct ast_unreal_pvt *p = ast_channel_tech_pvt(ast); + int res = -1; + struct ast_frame f = { AST_FRAME_HTML, }; + int isoutbound; + + if (!p) { + return -1; + } + + ao2_ref(p, 1); /* ref for unreal_queue_frame */ + ao2_lock(p); + isoutbound = AST_UNREAL_IS_OUTBOUND(ast, p); + f.subclass.integer = subclass; + f.data.ptr = (char *)data; + f.datalen = datalen; + res = unreal_queue_frame(p, isoutbound, &f, ast, 0); + ao2_unlock(p); + ao2_ref(p, -1); + + return res; +} + +void ast_unreal_call_setup(struct ast_channel *semi1, struct ast_channel *semi2) +{ + struct ast_var_t *varptr; + struct ast_var_t *clone_var; + + /* + * Note that cid_num and cid_name aren't passed in the + * ast_channel_alloc calls in ast_unreal_new_channels(). It's + * done here instead. + */ + ast_party_redirecting_copy(ast_channel_redirecting(semi2), ast_channel_redirecting(semi1)); + + ast_party_dialed_copy(ast_channel_dialed(semi2), ast_channel_dialed(semi1)); + + ast_connected_line_copy_to_caller(ast_channel_caller(semi2), ast_channel_connected(semi1)); + ast_connected_line_copy_from_caller(ast_channel_connected(semi2), ast_channel_caller(semi1)); + + ast_channel_language_set(semi2, ast_channel_language(semi1)); + ast_channel_accountcode_set(semi2, ast_channel_accountcode(semi1)); + ast_channel_musicclass_set(semi2, ast_channel_musicclass(semi1)); + + ast_channel_cc_params_init(semi2, ast_channel_get_cc_config_params(semi1)); + + /* + * Make sure we inherit the AST_CAUSE_ANSWERED_ELSEWHERE if it's + * set on the queue/dial call request in the dialplan. + */ + if (ast_channel_hangupcause(semi1) == AST_CAUSE_ANSWERED_ELSEWHERE) { + ast_channel_hangupcause_set(semi2, AST_CAUSE_ANSWERED_ELSEWHERE); + } + + /* + * Copy the channel variables from the semi1 channel to the + * outgoing channel. + * + * Note that due to certain assumptions, they MUST be in the + * same order. + */ + AST_LIST_TRAVERSE(ast_channel_varshead(semi1), varptr, entries) { + clone_var = ast_var_assign(varptr->name, varptr->value); + if (clone_var) { + AST_LIST_INSERT_TAIL(ast_channel_varshead(semi2), clone_var, entries); + } + } + ast_channel_datastore_inherit(semi1, semi2); +} + +int ast_unreal_hangup(struct ast_unreal_pvt *p, struct ast_channel *ast) +{ + int hangup_chan = 0; + int res = 0; + int cause; + struct ast_channel *owner = NULL; + struct ast_channel *chan = NULL; + + /* the pvt isn't going anywhere, it has a ref */ + ast_channel_unlock(ast); + + /* lock everything */ + ast_unreal_lock_all(p, &chan, &owner); + + if (ast != chan && ast != owner) { + res = -1; + goto unreal_hangup_cleanup; + } + + cause = ast_channel_hangupcause(ast); + + if (ast == p->chan) { + /* Outgoing side is hanging up. */ + ast_clear_flag(p, AST_UNREAL_CARETAKER_THREAD); + p->chan = NULL; + if (p->owner) { + const char *status = pbx_builtin_getvar_helper(p->chan, "DIALSTATUS"); + + if (status) { + ast_channel_hangupcause_set(p->owner, cause); + pbx_builtin_setvar_helper(p->owner, "CHANLOCALSTATUS", status); + } + ast_queue_hangup_with_cause(p->owner, cause); + } + } else { + /* Owner side is hanging up. */ + p->owner = NULL; + if (p->chan) { + if (cause == AST_CAUSE_ANSWERED_ELSEWHERE) { + ast_channel_hangupcause_set(p->chan, AST_CAUSE_ANSWERED_ELSEWHERE); + ast_debug(2, "%s has AST_CAUSE_ANSWERED_ELSEWHERE set.\n", + ast_channel_name(p->chan)); + } + if (!ast_test_flag(p, AST_UNREAL_CARETAKER_THREAD)) { + /* + * Need to actually hangup p->chan since nothing else is taking + * care of it. + */ + hangup_chan = 1; + } else { + ast_queue_hangup_with_cause(p->chan, cause); + } + } + } + + /* this is one of our locked channels, doesn't matter which */ + ast_channel_tech_pvt_set(ast, NULL); + ao2_ref(p, -1); + +unreal_hangup_cleanup: + ao2_unlock(p); + if (owner) { + ast_channel_unlock(owner); + ast_channel_unref(owner); + } + if (chan) { + ast_channel_unlock(chan); + if (hangup_chan) { + ast_hangup(chan); + } + ast_channel_unref(chan); + } + + /* leave with the channel locked that came in */ + ast_channel_lock(ast); + + return res; +} + +void ast_unreal_destructor(void *vdoomed) +{ + struct ast_unreal_pvt *doomed = vdoomed; + + doomed->reqcap = ast_format_cap_destroy(doomed->reqcap); +} + +struct ast_unreal_pvt *ast_unreal_alloc(size_t size, ao2_destructor_fn destructor, struct ast_format_cap *cap) +{ + struct ast_unreal_pvt *unreal; + + static const struct ast_jb_conf jb_conf = { + .flags = 0, + .max_size = -1, + .resync_threshold = -1, + .impl = "", + .target_extra = -1, + }; + + unreal = ao2_alloc(size, destructor); + if (!unreal) { + return NULL; + } + unreal->reqcap = ast_format_cap_dup(cap); + if (!unreal->reqcap) { + ao2_ref(unreal, -1); + return NULL; + } + + memcpy(&unreal->jb_conf, &jb_conf, sizeof(unreal->jb_conf)); + + return unreal; +} + +struct ast_channel *ast_unreal_new_channels(struct ast_unreal_pvt *p, + const struct ast_channel_tech *tech, int semi1_state, int semi2_state, + const char *exten, const char *context, const struct ast_channel *requestor, + struct ast_callid *callid) +{ + struct ast_channel *owner; + struct ast_channel *chan; + const char *linkedid = requestor ? ast_channel_linkedid(requestor) : NULL; + struct ast_format fmt; + int generated_seqno = ast_atomic_fetchadd_int((int *) &name_sequence, +1); + + /* + * Allocate two new Asterisk channels + * + * Make sure that the ;2 channel gets the same linkedid as ;1. + * You can't pass linkedid to both allocations since if linkedid + * isn't set, then each channel will generate its own linkedid. + */ + if (!(owner = ast_channel_alloc(1, semi1_state, NULL, NULL, NULL, + exten, context, linkedid, 0, + "%s/%s-%08x;1", tech->type, p->name, generated_seqno)) + || !(chan = ast_channel_alloc(1, semi2_state, NULL, NULL, NULL, + exten, context, ast_channel_linkedid(owner), 0, + "%s/%s-%08x;2", tech->type, p->name, generated_seqno))) { + if (owner) { + owner = ast_channel_release(owner); + } + ast_log(LOG_WARNING, "Unable to allocate channel structure(s)\n"); + return NULL; + } + + if (callid) { + ast_channel_callid_set(owner, callid); + ast_channel_callid_set(chan, callid); + } + + ast_channel_tech_set(owner, tech); + ast_channel_tech_set(chan, tech); + ast_channel_tech_pvt_set(owner, p); + ast_channel_tech_pvt_set(chan, p); + + ast_format_cap_copy(ast_channel_nativeformats(owner), p->reqcap); + ast_format_cap_copy(ast_channel_nativeformats(chan), p->reqcap); + + /* Determine our read/write format and set it on each channel */ + ast_best_codec(p->reqcap, &fmt); + ast_format_copy(ast_channel_writeformat(owner), &fmt); + ast_format_copy(ast_channel_writeformat(chan), &fmt); + ast_format_copy(ast_channel_rawwriteformat(owner), &fmt); + ast_format_copy(ast_channel_rawwriteformat(chan), &fmt); + ast_format_copy(ast_channel_readformat(owner), &fmt); + ast_format_copy(ast_channel_readformat(chan), &fmt); + ast_format_copy(ast_channel_rawreadformat(owner), &fmt); + ast_format_copy(ast_channel_rawreadformat(chan), &fmt); + + ast_set_flag(ast_channel_flags(owner), AST_FLAG_DISABLE_DEVSTATE_CACHE); + ast_set_flag(ast_channel_flags(chan), AST_FLAG_DISABLE_DEVSTATE_CACHE); + + ast_jb_configure(owner, &p->jb_conf); + + if (ast_channel_cc_params_init(owner, requestor + ? ast_channel_get_cc_config_params((struct ast_channel *) requestor) : NULL)) { + ast_channel_release(owner); + ast_channel_release(chan); + return NULL; + } + + /* Give the private a ref for each channel. */ + ao2_ref(p, +2); + p->owner = owner; + p->chan = chan; + + return owner; +} -- cgit v1.2.3