implement transfer functionality in chan_skinny
(closes issue #9939) Reported by: wedhorn Patches: transfer_v6.diff uploaded by wedhorn (license 30) chan_skinny-transfer-trunk-v10.txt uploaded by DEA (license 3) chan_skinny-transfer-trunk-v12.txt uploaded by mvanbaak (license 7) Tested by: DEA, wedhorn, mvanbaak git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@125096 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
parent
41e496980b
commit
9ba79e3b68
|
@ -67,6 +67,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
#include "asterisk/threadstorage.h"
|
||||
#include "asterisk/devicestate.h"
|
||||
#include "asterisk/event.h"
|
||||
#include "asterisk/indications.h"
|
||||
|
||||
/*************************************
|
||||
* Skinny/Asterisk Protocol Settings *
|
||||
|
@ -202,14 +203,14 @@ struct stimulus_message {
|
|||
|
||||
#define OFFHOOK_MESSAGE 0x0006
|
||||
struct offhook_message {
|
||||
uint32_t unknown1;
|
||||
uint32_t unknown2;
|
||||
uint32_t instance;
|
||||
uint32_t reference;
|
||||
};
|
||||
|
||||
#define ONHOOK_MESSAGE 0x0007
|
||||
struct onhook_message {
|
||||
uint32_t unknown1;
|
||||
uint32_t unknown2;
|
||||
uint32_t instance;
|
||||
uint32_t reference;
|
||||
};
|
||||
|
||||
#define CAPABILITIES_RES_MESSAGE 0x0010
|
||||
|
@ -797,6 +798,7 @@ static const uint8_t soft_key_default_ringout[] = {
|
|||
static const uint8_t soft_key_default_offhookwithfeat[] = {
|
||||
SOFTKEY_REDIAL,
|
||||
SOFTKEY_ENDCALL,
|
||||
SOFTKEY_TRNSFER,
|
||||
};
|
||||
|
||||
static const uint8_t soft_key_default_unknown[] = {
|
||||
|
@ -861,6 +863,7 @@ struct display_prompt_status_message {
|
|||
char promptMessage[32];
|
||||
uint32_t lineInstance;
|
||||
uint32_t callReference;
|
||||
uint32_t space[3];
|
||||
};
|
||||
|
||||
#define CLEAR_PROMPT_MESSAGE 0x0113
|
||||
|
@ -1139,8 +1142,12 @@ struct skinny_subchannel {
|
|||
int nat;
|
||||
int outgoing;
|
||||
int alreadygone;
|
||||
int blindxfer;
|
||||
int xferor;
|
||||
|
||||
|
||||
struct skinny_subchannel *next;
|
||||
struct skinny_subchannel *related;
|
||||
struct skinny_line *parent;
|
||||
};
|
||||
|
||||
|
@ -1197,6 +1204,7 @@ struct skinny_line {
|
|||
|
||||
struct ast_codec_pref prefs;
|
||||
struct skinny_subchannel *sub;
|
||||
struct skinny_subchannel *activesub;
|
||||
struct skinny_line *next;
|
||||
struct skinny_device *parent;
|
||||
struct ast_variable *chanvars; /*!< Channel variables to set for inbound call */
|
||||
|
@ -1245,6 +1253,7 @@ static struct skinny_device {
|
|||
struct ast_ha *ha;
|
||||
struct skinnysession *session;
|
||||
struct skinny_device *next;
|
||||
struct skinny_line *activeline;
|
||||
} *devices = NULL;
|
||||
|
||||
struct skinny_paging_device {
|
||||
|
@ -1298,6 +1307,7 @@ static const struct ast_channel_tech skinny_tech = {
|
|||
};
|
||||
|
||||
static int skinny_extensionstate_cb(char *context, char* exten, int state, void *data);
|
||||
static int skinny_transfer(struct skinny_subchannel *sub);
|
||||
|
||||
static void *get_button_template(struct skinnysession *s, struct button_definition_template *btn)
|
||||
{
|
||||
|
@ -2096,6 +2106,54 @@ static void transmit_dialednumber(struct skinnysession *s, const char *text, int
|
|||
transmit_response(s, req);
|
||||
}
|
||||
|
||||
static void transmit_closereceivechannel(struct skinnysession *s, struct skinny_subchannel *sub)
|
||||
{
|
||||
struct skinny_req *req;
|
||||
|
||||
if (!(req = req_alloc(sizeof(struct close_receive_channel_message), CLOSE_RECEIVE_CHANNEL_MESSAGE)))
|
||||
return;
|
||||
|
||||
req->data.closereceivechannel.conferenceId = htolel(0);
|
||||
req->data.closereceivechannel.partyId = htolel(sub->callid);
|
||||
transmit_response(s, req);
|
||||
}
|
||||
|
||||
static void transmit_stopmediatransmission(struct skinnysession *s, struct skinny_subchannel *sub)
|
||||
{
|
||||
struct skinny_req *req;
|
||||
|
||||
if (!(req = req_alloc(sizeof(struct stop_media_transmission_message), STOP_MEDIA_TRANSMISSION_MESSAGE)))
|
||||
return;
|
||||
|
||||
req->data.stopmedia.conferenceId = htolel(0);
|
||||
req->data.stopmedia.passThruPartyId = htolel(sub->callid);
|
||||
transmit_response(s, req);
|
||||
}
|
||||
|
||||
static void transmit_activatecallplane(struct skinnysession *s, struct skinny_line *l)
|
||||
{
|
||||
struct skinny_req *req;
|
||||
|
||||
if (!(req = req_alloc(sizeof(struct activate_call_plane_message), ACTIVATE_CALL_PLANE_MESSAGE)))
|
||||
return;
|
||||
|
||||
req->data.activatecallplane.lineInstance = htolel(l->instance);
|
||||
transmit_response(s, req);
|
||||
}
|
||||
|
||||
static void transmit_callstateonly(struct skinnysession *s, struct skinny_subchannel *sub, int state)
|
||||
{
|
||||
struct skinny_req *req;
|
||||
|
||||
if (!(req = req_alloc(sizeof(struct call_state_message), CALL_STATE_MESSAGE)))
|
||||
return;
|
||||
|
||||
req->data.callstate.callState = htolel(state);
|
||||
req->data.callstate.lineInstance = htolel(sub->parent->instance);
|
||||
req->data.callstate.callReference = htolel(sub->callid);
|
||||
transmit_response(s, req);
|
||||
}
|
||||
|
||||
static void transmit_callstate(struct skinnysession *s, int instance, int state, unsigned callid)
|
||||
{
|
||||
struct skinny_req *req;
|
||||
|
@ -3160,6 +3218,9 @@ static struct skinny_device *build_device(const char *cat, struct ast_variable *
|
|||
l->nat = nat;
|
||||
l->canreinvite = canreinvite;
|
||||
|
||||
if (!d->lines) {
|
||||
d->activeline = l;
|
||||
}
|
||||
l->next = d->lines;
|
||||
d->lines = l;
|
||||
}
|
||||
|
@ -3394,7 +3455,7 @@ static int skinny_call(struct ast_channel *ast, char *dest, int timeout)
|
|||
break;
|
||||
}
|
||||
|
||||
transmit_callstate(s, l->instance, SKINNY_RINGIN, sub->callid);
|
||||
transmit_callstateonly(s, sub, SKINNY_RINGIN);
|
||||
transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_RINGIN);
|
||||
transmit_displaypromptstatus(s, "Ring-In", 0, l->instance, sub->callid);
|
||||
transmit_callinfo(s, ast->cid.cid_name, ast->cid.cid_num, l->cid_name, l->cid_num, l->instance, sub->callid, 1);
|
||||
|
@ -3409,7 +3470,7 @@ static int skinny_call(struct ast_channel *ast, char *dest, int timeout)
|
|||
|
||||
static int skinny_hangup(struct ast_channel *ast)
|
||||
{
|
||||
struct skinny_subchannel *sub = ast->tech_pvt;
|
||||
struct skinny_subchannel *sub = ast->tech_pvt, *tmpsub;
|
||||
struct skinny_line *l;
|
||||
struct skinny_device *d;
|
||||
struct skinnysession *s;
|
||||
|
@ -3421,22 +3482,88 @@ static int skinny_hangup(struct ast_channel *ast)
|
|||
l = sub->parent;
|
||||
d = l->parent;
|
||||
s = d->session;
|
||||
if (skinnydebug)
|
||||
ast_verb(1, "skinny_hangup(%s) on %s@%s\n", ast->name, l->name, d->name);
|
||||
|
||||
if (l->sub == sub) {
|
||||
l->sub = l->sub->next;
|
||||
} else {
|
||||
tmpsub = l->sub;
|
||||
while (tmpsub->next) {
|
||||
if (tmpsub->next == sub) {
|
||||
tmpsub->next = tmpsub->next->next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (d->registered) {
|
||||
if ((l->type = TYPE_LINE) && (l->hookstate == SKINNY_OFFHOOK)) {
|
||||
/* Ignoring l->type, doesn't seem relevant and previous code
|
||||
assigned rather than tested, ie always true */
|
||||
if (l->sub) {
|
||||
if (sub->related) {
|
||||
sub->related->related = NULL;
|
||||
|
||||
}
|
||||
if (sub == l->activesub) { /* we are killing the active sub, but there are other subs on the line*/
|
||||
if (sub->related) {
|
||||
l->activesub = sub->related;
|
||||
} else {
|
||||
if (sub->next) {
|
||||
l->activesub = sub->next;
|
||||
} else {
|
||||
l->activesub = l->sub;
|
||||
}
|
||||
}
|
||||
transmit_activatecallplane(s, l);
|
||||
transmit_closereceivechannel(s,sub);
|
||||
transmit_stopmediatransmission(s,sub);
|
||||
transmit_tone(s, SKINNY_CALLWAITTONE, l->instance, l->activesub->callid);
|
||||
transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK);
|
||||
} else { /* we are killing a background sub on the line with other subs*/
|
||||
if (l->sub->next) {
|
||||
transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK);
|
||||
} else {
|
||||
transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
|
||||
}
|
||||
}
|
||||
} else { /* no more subs on line so make idle */
|
||||
|
||||
l->hookstate = SKINNY_ONHOOK;
|
||||
transmit_callstate(s, l->instance, SKINNY_ONHOOK, sub->callid);
|
||||
l->activesub = NULL;
|
||||
transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_OFF);
|
||||
transmit_speaker_mode(s, SKINNY_SPEAKEROFF);
|
||||
if (sub->parent == d->activeline) {
|
||||
transmit_activatecallplane(s, l);
|
||||
transmit_closereceivechannel(s,sub);
|
||||
transmit_stopmediatransmission(s,sub);
|
||||
transmit_speaker_mode(s, SKINNY_SPEAKEROFF);
|
||||
transmit_ringer_mode(s, SKINNY_RING_OFF);
|
||||
/* we should check to see if we can start the ringer if another line is ringing */
|
||||
}
|
||||
}
|
||||
|
||||
/* if ((l->type = TYPE_LINE) && (l->hookstate == SKINNY_OFFHOOK)) {
|
||||
transmit_callstate(s, l->instance, SKINNY_ONHOOK, sub->callid);
|
||||
if (onlysub){
|
||||
if (skinnydebug)
|
||||
ast_debug(1, "skinny_hangup(%s) on %s@%s is not the only call on this device\n", ast->name, l->name, d->name);
|
||||
l->hookstate = SKINNY_ONHOOK;
|
||||
transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_OFF);
|
||||
transmit_speaker_mode(s, SKINNY_SPEAKEROFF);
|
||||
transmit_ringer_mode(s, SKINNY_RING_OFF);
|
||||
} else {
|
||||
transmit_ringer_mode(s, SKINNY_RING_OFF);
|
||||
if (skinnydebug)
|
||||
ast_debug(1, "skinny_hangup(%s) on %s@%s \n", ast->name, l->name, d->name);
|
||||
}
|
||||
/ends
|
||||
|
||||
} else if ((l->type = TYPE_LINE) && (l->hookstate == SKINNY_ONHOOK)) {
|
||||
transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
|
||||
transmit_callstate(s, l->instance, SKINNY_ONHOOK, sub->callid);
|
||||
transmit_ringer_mode(s, SKINNY_RING_OFF);
|
||||
transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_OFF);
|
||||
do_housekeeping(s);
|
||||
}
|
||||
} */
|
||||
}
|
||||
ast_mutex_lock(&sub->lock);
|
||||
sub->owner = NULL;
|
||||
|
@ -3462,6 +3589,15 @@ static int skinny_answer(struct ast_channel *ast)
|
|||
|
||||
ast_copy_string(exten, S_OR(ast->macroexten, ast->exten), sizeof(exten));
|
||||
|
||||
if (sub->blindxfer) {
|
||||
if (skinnydebug)
|
||||
ast_debug(1, "skinny_answer(%s) on %s@%s-%d with BlindXFER, transferring\n",
|
||||
ast->name, l->name, d->name, sub->callid);
|
||||
ast_setstate(ast, AST_STATE_UP);
|
||||
skinny_transfer(sub);
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub->cxmode = SKINNY_CX_SENDRECV;
|
||||
if (!sub->rtp) {
|
||||
start_rtp(sub);
|
||||
|
@ -3477,10 +3613,11 @@ static int skinny_answer(struct ast_channel *ast)
|
|||
for some reason, transmit_callinfo must be before transmit_callstate,
|
||||
or you won't get keypad messages in some situations. */
|
||||
transmit_callinfo(s, ast->cid.cid_name, ast->cid.cid_num, exten, exten, l->instance, sub->callid, 2);
|
||||
transmit_callstate(s, l->instance, SKINNY_CONNECTED, sub->callid);
|
||||
transmit_callstateonly(s, sub, SKINNY_CONNECTED);
|
||||
transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_CONNECTED);
|
||||
transmit_dialednumber(s, exten, l->instance, sub->callid);
|
||||
transmit_displaypromptstatus(s, "Connected", 0, l->instance, sub->callid);
|
||||
l->activesub = sub;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -3678,6 +3815,68 @@ static char *control2str(int ind) {
|
|||
}
|
||||
}
|
||||
|
||||
static int skinny_transfer(struct skinny_subchannel *sub)
|
||||
{
|
||||
struct skinny_subchannel *xferor; /* the sub doing the transferring */
|
||||
struct skinny_subchannel *xferee; /* the sub being transferred */
|
||||
const struct ind_tone_zone_sound *ts = NULL;
|
||||
|
||||
if (ast_bridged_channel(sub->owner) || ast_bridged_channel(sub->related->owner)) {
|
||||
if (sub->xferor) {
|
||||
xferor = sub;
|
||||
xferee = sub->related;
|
||||
} else {
|
||||
xferor = sub;
|
||||
xferee = sub->related;
|
||||
}
|
||||
|
||||
if (skinnydebug) {
|
||||
ast_debug(1, "Transferee channels (local/remote): %s and %s\n",
|
||||
xferee->owner->name, ast_bridged_channel(xferee->owner)?ast_bridged_channel(xferee->owner)->name:"");
|
||||
ast_debug(1, "Transferor channels (local/remote): %s and %s\n",
|
||||
xferor->owner->name, ast_bridged_channel(xferor->owner)?ast_bridged_channel(xferor->owner)->name:"");
|
||||
}
|
||||
if (ast_bridged_channel(xferor->owner)) {
|
||||
if (ast_bridged_channel(xferee->owner)) {
|
||||
ast_queue_control(xferee->owner, AST_CONTROL_UNHOLD);
|
||||
}
|
||||
if (xferor->owner->_state == AST_STATE_RING) {
|
||||
/* play ringing inband */
|
||||
ts = ast_get_indication_tone(xferor->owner->zone, "ring");
|
||||
ast_playtones_start(xferor->owner,0,ts->data, 1);
|
||||
}
|
||||
if (skinnydebug)
|
||||
ast_debug(1, "Transfer Masquerading %s to %s\n",
|
||||
xferee->owner->name, ast_bridged_channel(xferor->owner)?ast_bridged_channel(xferor->owner)->name:"");
|
||||
if (ast_channel_masquerade(xferee->owner, ast_bridged_channel(xferor->owner))) {
|
||||
ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
|
||||
ast_bridged_channel(xferor->owner)->name, xferee->owner->name);
|
||||
return -1;
|
||||
}
|
||||
} else if (ast_bridged_channel(xferee->owner)) {
|
||||
ast_queue_control(xferee->owner, AST_CONTROL_UNHOLD);
|
||||
if (xferor->owner->_state == AST_STATE_RING) {
|
||||
/* play ringing inband */
|
||||
ts = ast_get_indication_tone(xferor->owner->zone, "ring");
|
||||
ast_playtones_start(xferor->owner,0,ts->data, 1);
|
||||
}
|
||||
if (skinnydebug)
|
||||
ast_debug(1, "Transfer Masquerading %s to %s\n",
|
||||
xferor->owner->name, ast_bridged_channel(xferee->owner)?ast_bridged_channel(xferee->owner)->name:"");
|
||||
if (ast_channel_masquerade(xferor->owner, ast_bridged_channel(xferee->owner))) {
|
||||
ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
|
||||
ast_bridged_channel(xferee->owner)->name, xferor->owner->name);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
if (option_debug)
|
||||
ast_log(LOG_DEBUG, "Neither %s nor %s are in a bridge, nothing to transfer\n",
|
||||
xferor->owner->name, xferee->owner->name);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, size_t datalen)
|
||||
{
|
||||
|
@ -3698,12 +3897,18 @@ static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, s
|
|||
ast_verb(3, "Asked to indicate '%s' condition on channel %s\n", control2str(ind), ast->name);
|
||||
switch(ind) {
|
||||
case AST_CONTROL_RINGING:
|
||||
if (sub->blindxfer) {
|
||||
if (skinnydebug)
|
||||
ast_debug(1, "Channel %s set up for Blind Xfer, so Xfer rather than ring device\n", ast->name);
|
||||
skinny_transfer(sub);
|
||||
break;
|
||||
}
|
||||
if (ast->_state != AST_STATE_UP) {
|
||||
if (!sub->progress) {
|
||||
if (!d->earlyrtp) {
|
||||
transmit_tone(s, SKINNY_ALERT, l->instance, sub->callid);
|
||||
}
|
||||
transmit_callstate(s, l->instance, SKINNY_RINGOUT, sub->callid);
|
||||
transmit_callstateonly(s, sub, SKINNY_RINGOUT);
|
||||
transmit_dialednumber(s, exten, l->instance, sub->callid);
|
||||
transmit_displaypromptstatus(s, "Ring Out", 0, l->instance, sub->callid);
|
||||
transmit_callinfo(s, ast->cid.cid_name, ast->cid.cid_num, exten, exten, l->instance, sub->callid, 2); /* 2 = outgoing from phone */
|
||||
|
@ -3719,7 +3924,7 @@ static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, s
|
|||
if (!d->earlyrtp) {
|
||||
transmit_tone(s, SKINNY_BUSYTONE, l->instance, sub->callid);
|
||||
}
|
||||
transmit_callstate(s, l->instance, SKINNY_BUSY, sub->callid);
|
||||
transmit_callstateonly(s, sub, SKINNY_BUSY);
|
||||
sub->alreadygone = 1;
|
||||
ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
|
||||
if (!d->earlyrtp) {
|
||||
|
@ -3732,7 +3937,7 @@ static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, s
|
|||
if (!d->earlyrtp) {
|
||||
transmit_tone(s, SKINNY_REORDER, l->instance, sub->callid);
|
||||
}
|
||||
transmit_callstate(s, l->instance, SKINNY_CONGESTION, sub->callid);
|
||||
transmit_callstateonly(s, sub, SKINNY_CONGESTION);
|
||||
sub->alreadygone = 1;
|
||||
ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
|
||||
if (!d->earlyrtp) {
|
||||
|
@ -3745,7 +3950,7 @@ static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, s
|
|||
if (!d->earlyrtp) {
|
||||
transmit_tone(s, SKINNY_ALERT, l->instance, sub->callid);
|
||||
}
|
||||
transmit_callstate(s, l->instance, SKINNY_PROGRESS, sub->callid);
|
||||
transmit_callstateonly(s, sub, SKINNY_PROGRESS);
|
||||
transmit_displaypromptstatus(s, "Call Progress", 0, l->instance, sub->callid);
|
||||
transmit_callinfo(s, ast->cid.cid_name, ast->cid.cid_num, exten, exten, l->instance, sub->callid, 2); /* 2 = outgoing from phone */
|
||||
sub->progress = 1;
|
||||
|
@ -3805,9 +4010,14 @@ static struct ast_channel *skinny_new(struct skinny_line *l, int state)
|
|||
sub->nat = l->nat;
|
||||
sub->parent = l;
|
||||
sub->onhold = 0;
|
||||
sub->blindxfer = 0;
|
||||
sub->xferor = 0;
|
||||
sub->related = NULL;
|
||||
|
||||
|
||||
sub->next = l->sub;
|
||||
l->sub = sub;
|
||||
l->activesub = sub;
|
||||
}
|
||||
tmp->tech = &skinny_tech;
|
||||
tmp->tech_pvt = sub;
|
||||
|
@ -3880,7 +4090,6 @@ static int skinny_hold(struct skinny_subchannel *sub)
|
|||
struct skinny_line *l = sub->parent;
|
||||
struct skinny_device *d = l->parent;
|
||||
struct skinnysession *s = d->session;
|
||||
struct skinny_req *req;
|
||||
|
||||
/* Don't try to hold a channel that doesn't exist */
|
||||
if (!sub || !sub->owner)
|
||||
|
@ -3894,26 +4103,11 @@ static int skinny_hold(struct skinny_subchannel *sub)
|
|||
S_OR(l->mohsuggest, NULL),
|
||||
!ast_strlen_zero(l->mohsuggest) ? strlen(l->mohsuggest) + 1 : 0);
|
||||
|
||||
if (!(req = req_alloc(sizeof(struct activate_call_plane_message), ACTIVATE_CALL_PLANE_MESSAGE)))
|
||||
return 0;
|
||||
|
||||
req->data.activatecallplane.lineInstance = htolel(l->instance);
|
||||
transmit_response(s, req);
|
||||
|
||||
if (!(req = req_alloc(sizeof(struct close_receive_channel_message), CLOSE_RECEIVE_CHANNEL_MESSAGE)))
|
||||
return 0;
|
||||
|
||||
req->data.closereceivechannel.conferenceId = htolel(sub->callid);
|
||||
req->data.closereceivechannel.partyId = htolel(sub->callid);
|
||||
transmit_response(s, req);
|
||||
|
||||
if (!(req = req_alloc(sizeof(struct stop_media_transmission_message), STOP_MEDIA_TRANSMISSION_MESSAGE)))
|
||||
return 0;
|
||||
|
||||
req->data.stopmedia.conferenceId = htolel(sub->callid);
|
||||
req->data.stopmedia.passThruPartyId = htolel(sub->callid);
|
||||
transmit_response(s, req);
|
||||
transmit_activatecallplane(s, l);
|
||||
transmit_closereceivechannel(s,sub);
|
||||
transmit_stopmediatransmission(s,sub);
|
||||
|
||||
transmit_callstateonly(s, sub, SKINNY_HOLD);
|
||||
transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_WINK);
|
||||
sub->onhold = 1;
|
||||
return 1;
|
||||
|
@ -3924,7 +4118,6 @@ static int skinny_unhold(struct skinny_subchannel *sub)
|
|||
struct skinny_line *l = sub->parent;
|
||||
struct skinny_device *d = l->parent;
|
||||
struct skinnysession *s = d->session;
|
||||
struct skinny_req *req;
|
||||
|
||||
/* Don't try to unhold a channel that doesn't exist */
|
||||
if (!sub || !sub->owner)
|
||||
|
@ -3936,18 +4129,98 @@ static int skinny_unhold(struct skinny_subchannel *sub)
|
|||
|
||||
ast_queue_control(sub->owner, AST_CONTROL_UNHOLD);
|
||||
|
||||
if (!(req = req_alloc(sizeof(struct activate_call_plane_message), ACTIVATE_CALL_PLANE_MESSAGE)))
|
||||
return 0;
|
||||
|
||||
req->data.activatecallplane.lineInstance = htolel(l->instance);
|
||||
transmit_response(s, req);
|
||||
transmit_activatecallplane(s, l);
|
||||
|
||||
transmit_connect(s, sub);
|
||||
transmit_callstateonly(s, sub, SKINNY_CONNECTED);
|
||||
transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
|
||||
l->hookstate = SKINNY_OFFHOOK;
|
||||
sub->onhold = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int handle_hold_button(struct skinny_subchannel *sub)
|
||||
{
|
||||
if (!sub)
|
||||
return -1;
|
||||
if (sub->related) {
|
||||
skinny_hold(sub);
|
||||
skinny_unhold(sub->related);
|
||||
sub->parent->activesub = sub->related;
|
||||
} else {
|
||||
if (sub->onhold) {
|
||||
skinny_unhold(sub);
|
||||
transmit_selectsoftkeys(sub->parent->parent->session, sub->parent->instance, sub->callid, KEYDEF_CONNECTED);
|
||||
} else {
|
||||
skinny_hold(sub);
|
||||
transmit_selectsoftkeys(sub->parent->parent->session, sub->parent->instance, sub->callid, KEYDEF_ONHOLD);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int handle_transfer_button(struct skinny_subchannel *sub)
|
||||
{
|
||||
struct skinny_line *l = sub->parent;
|
||||
struct skinny_device *d = l->parent;
|
||||
struct skinnysession *s = d->session;
|
||||
struct skinny_subchannel *newsub;
|
||||
struct ast_channel *c;
|
||||
pthread_t t;
|
||||
|
||||
if (!sub) {
|
||||
ast_verbose("Transfer: No subchannel to transfer\n");
|
||||
return -1;
|
||||
}
|
||||
if (!sub->related) {
|
||||
/* Another sub has not been created so this must be first XFER press */
|
||||
if (!sub->onhold) {
|
||||
skinny_hold(sub);
|
||||
}
|
||||
c = skinny_new(l, AST_STATE_DOWN);
|
||||
if (c) {
|
||||
newsub = c->tech_pvt;
|
||||
/* point the sub and newsub at each other so we know they are related */
|
||||
newsub->related = sub;
|
||||
sub->related = newsub;
|
||||
newsub->xferor = 1;
|
||||
l->activesub = newsub;
|
||||
transmit_callstate(s, l->instance, SKINNY_OFFHOOK, newsub->callid);
|
||||
if (skinnydebug)
|
||||
ast_debug(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
|
||||
transmit_displaymessage(s, NULL, l->instance, newsub->callid); /* clear display */
|
||||
transmit_tone(s, SKINNY_DIALTONE, l->instance, newsub->callid);
|
||||
transmit_selectsoftkeys(s, l->instance, newsub->callid, KEYDEF_OFFHOOKWITHFEAT);
|
||||
/* start the switch thread */
|
||||
if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
|
||||
ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
|
||||
ast_hangup(c);
|
||||
}
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
|
||||
}
|
||||
} else {
|
||||
/* We already have a related sub so we can either complete XFER or go into BLINDXFER (or cancel BLINDXFER */
|
||||
if (sub->blindxfer) {
|
||||
/* toggle blindxfer off */
|
||||
sub->blindxfer = 0;
|
||||
sub->related->blindxfer = 0;
|
||||
/* we really need some indications */
|
||||
} else {
|
||||
/* We were doing attended transfer */
|
||||
if (sub->owner->_state == AST_STATE_DOWN || sub->related->owner->_state == AST_STATE_DOWN) {
|
||||
/* one of the subs so we cant transfer yet, toggle blindxfer on */
|
||||
sub->blindxfer = 1;
|
||||
sub->related->blindxfer = 1;
|
||||
} else {
|
||||
/* big assumption we have two channels, lets transfer */
|
||||
skinny_transfer(sub);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_keep_alive_message(struct skinny_req *req, struct skinnysession *s)
|
||||
{
|
||||
if (!(req = req_alloc(0, KEEP_ALIVE_ACK_MESSAGE)))
|
||||
|
@ -4084,7 +4357,8 @@ static int handle_keypad_button_message(struct skinny_req *req, struct skinnyses
|
|||
if (lineInstance && callReference)
|
||||
sub = find_subchannel_by_instance_reference(d, lineInstance, callReference);
|
||||
else
|
||||
sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
|
||||
sub = d->activeline->activesub;
|
||||
//sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
|
||||
|
||||
if (!sub)
|
||||
return 0;
|
||||
|
@ -4234,20 +4508,15 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
|
|||
case STIMULUS_HOLD:
|
||||
if (skinnydebug)
|
||||
ast_verb(1, "Received Stimulus: Hold(%d/%d)\n", instance, callreference);
|
||||
|
||||
if (!sub)
|
||||
break;
|
||||
|
||||
if (sub->onhold) {
|
||||
skinny_unhold(sub);
|
||||
} else {
|
||||
skinny_hold(sub);
|
||||
}
|
||||
handle_hold_button(sub);
|
||||
break;
|
||||
case STIMULUS_TRANSFER:
|
||||
if (skinnydebug)
|
||||
ast_verb(1, "Received Stimulus: Transfer(%d/%d)\n", instance, callreference);
|
||||
/* XXX figure out how to transfer */
|
||||
if (l->transfer)
|
||||
handle_transfer_button(sub);
|
||||
else
|
||||
transmit_displaynotify(s, "Transfer disabled", 10);
|
||||
break;
|
||||
case STIMULUS_CONFERENCE:
|
||||
if (skinnydebug)
|
||||
|
@ -4406,6 +4675,8 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
|
|||
return 0;
|
||||
}
|
||||
|
||||
d->activeline = l;
|
||||
|
||||
/* turn the speaker on */
|
||||
transmit_speaker_mode(s, SKINNY_SPEAKERON);
|
||||
transmit_ringer_mode(s, SKINNY_RING_OFF);
|
||||
|
@ -4420,7 +4691,7 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
|
|||
ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
|
||||
transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid);
|
||||
transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
|
||||
transmit_callstate(s, l->instance, SKINNY_CONNECTED, sub->callid);
|
||||
transmit_callstateonly(s, sub, SKINNY_CONNECTED);
|
||||
transmit_displaypromptstatus(s, "Connected", 0, l->instance, sub->callid);
|
||||
transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_CONNECTED);
|
||||
start_rtp(sub);
|
||||
|
@ -4466,22 +4737,40 @@ static int handle_offhook_message(struct skinny_req *req, struct skinnysession *
|
|||
struct skinny_line *l;
|
||||
struct skinny_subchannel *sub;
|
||||
struct ast_channel *c;
|
||||
struct skinny_line *tmp;
|
||||
pthread_t t;
|
||||
int unknown1;
|
||||
int unknown2;
|
||||
int instance;
|
||||
int reference;
|
||||
|
||||
unknown1 = letohl(req->data.offhook.unknown1);
|
||||
unknown2 = letohl(req->data.offhook.unknown2);
|
||||
/* if any line on a device is offhook, than the device must be offhook,
|
||||
unless we have shared lines CCM seems that it would never get here,
|
||||
but asterisk does, so we may need to do more work. Ugly, we should
|
||||
probably move hookstate from line to device, afterall, it's actually
|
||||
a device that changes hookstates */
|
||||
|
||||
sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
|
||||
|
||||
if (!sub) {
|
||||
l = find_line_by_instance(d, d->lastlineinstance);
|
||||
if (!l) {
|
||||
for (tmp = d->lines; tmp; tmp = tmp->next) {
|
||||
if (tmp->hookstate == SKINNY_OFFHOOK) {
|
||||
ast_verbose(VERBOSE_PREFIX_3 "Got offhook message when device (%s@%s) already offhook\n", tmp->name, d->name);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
instance = letohl(req->data.offhook.instance);
|
||||
reference = letohl(req->data.offhook.reference);
|
||||
|
||||
if (instance) {
|
||||
sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
|
||||
if (!sub) {
|
||||
l = find_line_by_instance(d, d->lastlineinstance);
|
||||
if (!l) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
l = sub->parent;
|
||||
}
|
||||
} else {
|
||||
l = sub->parent;
|
||||
l = d->activeline;
|
||||
sub = l->activesub;
|
||||
}
|
||||
|
||||
transmit_ringer_mode(s, SKINNY_RING_OFF);
|
||||
|
@ -4498,7 +4787,7 @@ static int handle_offhook_message(struct skinny_req *req, struct skinnysession *
|
|||
if (sub && sub->outgoing) {
|
||||
/* We're answering a ringing call */
|
||||
ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
|
||||
transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid);
|
||||
transmit_callstateonly(s, sub, SKINNY_CONNECTED);
|
||||
transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
|
||||
transmit_callstate(s, l->instance, SKINNY_CONNECTED, sub->callid);
|
||||
transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_CONNECTED);
|
||||
|
@ -4535,25 +4824,29 @@ static int handle_onhook_message(struct skinny_req *req, struct skinnysession *s
|
|||
{
|
||||
struct skinny_device *d = s->device;
|
||||
struct skinny_line *l;
|
||||
struct skinny_subchannel *sub;
|
||||
int unknown1;
|
||||
int unknown2;
|
||||
struct skinny_subchannel *sub, *tmpsub;
|
||||
int instance;
|
||||
int reference;
|
||||
int onlysub = 0;
|
||||
|
||||
unknown1 = letohl(req->data.onhook.unknown1);
|
||||
unknown2 = letohl(req->data.onhook.unknown2);
|
||||
instance = letohl(req->data.onhook.instance);
|
||||
reference = letohl(req->data.onhook.reference);
|
||||
|
||||
sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
|
||||
|
||||
if (!sub) {
|
||||
return 0;
|
||||
if (instance && reference) {
|
||||
sub = find_subchannel_by_instance_reference(d, instance, reference);
|
||||
if (!sub) {
|
||||
return 0;
|
||||
}
|
||||
l = sub->parent;
|
||||
} else {
|
||||
l = d->activeline;
|
||||
sub = l->activesub;
|
||||
}
|
||||
l = sub->parent;
|
||||
|
||||
if (l->hookstate == SKINNY_ONHOOK) {
|
||||
/* Something else already put us back on hook */
|
||||
return 0;
|
||||
}
|
||||
l->hookstate = SKINNY_ONHOOK;
|
||||
|
||||
ast_device_state_changed("Skinny/%s@%s", l->name, d->name);
|
||||
|
||||
|
@ -4561,28 +4854,39 @@ static int handle_onhook_message(struct skinny_req *req, struct skinnysession *s
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (!l->sub->next) {
|
||||
onlysub = 1;
|
||||
} else {
|
||||
tmpsub = l->sub;
|
||||
while (tmpsub->next){
|
||||
if ((sub == tmpsub->next) && sub->next) {
|
||||
tmpsub->next = sub->next;
|
||||
break;
|
||||
}
|
||||
tmpsub = tmpsub->next;
|
||||
}
|
||||
}
|
||||
|
||||
sub->cxmode = SKINNY_CX_RECVONLY;
|
||||
if (onlysub || sub->xferor){ /* is this the only call to this device? */
|
||||
l->hookstate = SKINNY_ONHOOK;
|
||||
if (skinnydebug)
|
||||
ast_debug(1, "Skinny %s@%s-%d went on hook\n", l->name, d->name, reference);
|
||||
}
|
||||
|
||||
transmit_callstate(s, l->instance, l->hookstate, sub->callid);
|
||||
if (skinnydebug)
|
||||
ast_verb(1, "Skinny %s@%s went on hook\n", l->name, d->name);
|
||||
if (l->transfer && (sub->owner && sub->next && sub->next->owner) && ((!sub->outgoing) || (sub->next && !sub->next->outgoing))) {
|
||||
if (l->transfer && sub->xferor && sub->owner->_state >= AST_STATE_RING) {
|
||||
/* We're allowed to transfer, we have two active calls and
|
||||
we made at least one of the calls. Let's try and transfer */
|
||||
|
||||
#if 0
|
||||
if ((res = attempt_transfer(p)) < 0) {
|
||||
if (sub->next && sub->next->owner) {
|
||||
sub->next->alreadygone = 1;
|
||||
ast_queue_hangup(sub->next->owner);
|
||||
}
|
||||
} else if (res) {
|
||||
ast_log(LOG_WARNING, "Transfer attempt failed\n");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
handle_transfer_button(sub);
|
||||
} else {
|
||||
/* Hangup the current call */
|
||||
/* If there is another active call, skinny_hangup will ring the phone with the other call */
|
||||
if (sub->xferor && sub->related){
|
||||
sub->related->related = NULL;
|
||||
sub->related->blindxfer = 0;
|
||||
}
|
||||
|
||||
if (sub->owner) {
|
||||
sub->alreadygone = 1;
|
||||
ast_queue_hangup(sub->owner);
|
||||
|
@ -5171,20 +5475,16 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
|
|||
case SOFTKEY_HOLD:
|
||||
if (skinnydebug)
|
||||
ast_verb(1, "Received Softkey Event: Hold(%d/%d)\n", instance, callreference);
|
||||
|
||||
if (sub) {
|
||||
if (sub->onhold) {
|
||||
skinny_unhold(sub);
|
||||
} else {
|
||||
skinny_hold(sub);
|
||||
}
|
||||
}
|
||||
|
||||
handle_hold_button(sub);
|
||||
break;
|
||||
case SOFTKEY_TRNSFER:
|
||||
if (skinnydebug)
|
||||
ast_verb(1, "Received Softkey Event: Transfer(%d/%d)\n", instance, callreference);
|
||||
/* XXX figure out how to transfer */
|
||||
if (l->transfer)
|
||||
handle_transfer_button(sub);
|
||||
else
|
||||
transmit_displaynotify(s, "Transfer disabled", 10);
|
||||
|
||||
break;
|
||||
case SOFTKEY_DND:
|
||||
if (skinnydebug)
|
||||
|
@ -5269,29 +5569,44 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
|
|||
break;
|
||||
}
|
||||
if (sub) {
|
||||
int onlysub = 0;
|
||||
struct skinny_subchannel *tmpsub;
|
||||
|
||||
if (!l->sub->next) {
|
||||
onlysub = 1;
|
||||
} else {
|
||||
tmpsub = l->sub;
|
||||
while (tmpsub->next){
|
||||
if ((sub == tmpsub->next) && sub->next) {
|
||||
tmpsub->next = sub->next;
|
||||
break;
|
||||
}
|
||||
tmpsub = tmpsub->next;
|
||||
}
|
||||
}
|
||||
|
||||
sub->cxmode = SKINNY_CX_RECVONLY;
|
||||
l->hookstate = SKINNY_ONHOOK;
|
||||
if (onlysub || sub->xferor){ /*Are there other calls to this device */
|
||||
l->hookstate = SKINNY_ONHOOK;
|
||||
if (skinnydebug)
|
||||
ast_debug(1, "Skinny %s@%s-%d went on hook\n", l->name, d->name, callreference);
|
||||
}
|
||||
|
||||
transmit_callstate(s, l->instance, l->hookstate, sub->callid);
|
||||
if (skinnydebug)
|
||||
ast_verb(1, "Skinny %s@%s went on hook\n", l->name, d->name);
|
||||
if (l->transfer && (sub->owner && sub->next && sub->next->owner) && ((!sub->outgoing) || (sub->next && !sub->next->outgoing))) {
|
||||
if (l->transfer && sub->xferor && sub->owner->_state >= AST_STATE_RING) {
|
||||
/* We're allowed to transfer, we have two active calls and
|
||||
we made at least one of the calls. Let's try and transfer */
|
||||
|
||||
#if 0
|
||||
if ((res = attempt_transfer(p)) < 0) {
|
||||
if (sub->next && sub->next->owner) {
|
||||
sub->next->alreadygone = 1;
|
||||
ast_queue_hangup(sub->next->owner);
|
||||
}
|
||||
} else if (res) {
|
||||
ast_log(LOG_WARNING, "Transfer attempt failed\n");
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
handle_transfer_button(sub);
|
||||
} else {
|
||||
/* Hangup the current call */
|
||||
/* If there is another active call, skinny_hangup will ring the phone with the other call */
|
||||
if (sub->xferor && sub->related){
|
||||
sub->related->related = NULL;
|
||||
sub->related->blindxfer = 0;
|
||||
}
|
||||
|
||||
if (sub->owner) {
|
||||
sub->alreadygone = 1;
|
||||
ast_queue_hangup(sub->owner);
|
||||
|
@ -5308,6 +5623,17 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
|
|||
case SOFTKEY_RESUME:
|
||||
if (skinnydebug)
|
||||
ast_verb(1, "Received Softkey Event: Resume(%d/%d)\n", instance, callreference);
|
||||
|
||||
if (sub) {
|
||||
if (sub->onhold) {
|
||||
skinny_unhold(sub);
|
||||
transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_CONNECTED);
|
||||
} else {
|
||||
skinny_hold(sub);
|
||||
transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_ONHOLD);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case SOFTKEY_ANSWER:
|
||||
if (skinnydebug)
|
||||
|
@ -5323,7 +5649,7 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
|
|||
ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
|
||||
transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid);
|
||||
transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
|
||||
transmit_callstate(s, l->instance, SKINNY_CONNECTED, sub->callid);
|
||||
transmit_callstateonly(s, sub, SKINNY_CONNECTED);
|
||||
transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_CONNECTED);
|
||||
start_rtp(sub);
|
||||
ast_setstate(sub->owner, AST_STATE_UP);
|
||||
|
@ -5454,9 +5780,13 @@ static int handle_message(struct skinny_req *req, struct skinnysession *s)
|
|||
lineInstance = letohl(req->data.keypad.lineInstance);
|
||||
callReference = letohl(req->data.keypad.callReference);
|
||||
|
||||
sub = find_subchannel_by_instance_reference(d, lineInstance, callReference);
|
||||
if (lineInstance) {
|
||||
sub = find_subchannel_by_instance_reference(d, lineInstance, callReference);
|
||||
} else {
|
||||
sub = d->activeline->activesub;
|
||||
}
|
||||
|
||||
if (sub && (sub->owner && sub->owner->_state < AST_STATE_UP)) {
|
||||
if (sub && ((sub->owner && sub->owner->_state < AST_STATE_UP) || sub->onhold)) {
|
||||
char dgt;
|
||||
int digit = letohl(req->data.keypad.button);
|
||||
|
||||
|
|
Loading…
Reference in New Issue