Index: channels/chan_skinny.c =================================================================== --- channels/chan_skinny.c (revision 69594) +++ channels/chan_skinny.c (working copy) @@ -74,6 +74,7 @@ #include "asterisk/abstract_jb.h" #include "asterisk/threadstorage.h" #include "asterisk/devicestate.h" +#include "asterisk/indications.h" /************************************* * Skinny/Asterisk Protocol Settings * @@ -169,6 +170,12 @@ uint32_t callReference; }; + +#define ENBLOC_CALL_MESSAGE 0x0004 +struct enbloc_call_message { + char calledParty[24]; +}; + #define STIMULUS_MESSAGE 0x0005 struct stimulus_message { uint32_t stimulus; @@ -178,14 +185,14 @@ #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 @@ -260,12 +267,15 @@ #define START_TONE_MESSAGE 0x0082 struct start_tone_message { uint32_t tone; - uint32_t space[3]; + uint32_t space; + uint32_t instance; + uint32_t reference; }; #define STOP_TONE_MESSAGE 0x0083 struct stop_tone_message { - uint32_t space[2]; + uint32_t instance; + uint32_t reference; }; #define SET_RINGER_MESSAGE 0x0085 @@ -353,7 +363,7 @@ struct line_stat_res_message { uint32_t lineNumber; char lineDirNumber[24]; - char lineDisplayName[24]; + char lineDisplayName[42]; uint32_t space[15]; }; @@ -474,14 +484,12 @@ uint32_t capability; uint32_t echo; uint32_t bitrate; - uint32_t space[16]; }; #define CLOSE_RECEIVE_CHANNEL_MESSAGE 0x0106 struct close_receive_channel_message { uint32_t conferenceId; uint32_t partyId; - uint32_t space[2]; }; #define SOFT_KEY_TEMPLATE_RES_MESSAGE 0x0108 @@ -552,6 +560,7 @@ static const uint8_t soft_key_default_onhook[] = { SOFTKEY_REDIAL, + SOFTKEY_NEWCALL, SOFTKEY_CFWDALL, SOFTKEY_CFWDBUSY, SOFTKEY_GPICKUP, @@ -607,15 +616,14 @@ }; static const uint8_t soft_key_default_ringout[] = { + SOFTKEY_NONE, SOFTKEY_ENDCALL, - SOFTKEY_TRNSFER, - SOFTKEY_CFWDALL, - SOFTKEY_CFWDBUSY, }; static const uint8_t soft_key_default_offhookwithfeat[] = { SOFTKEY_REDIAL, SOFTKEY_ENDCALL, + SOFTKEY_TRNSFER, }; static const uint8_t soft_key_default_unknown[] = { @@ -671,7 +679,6 @@ uint32_t callState; uint32_t lineInstance; uint32_t callReference; - uint32_t space[3]; }; #define DISPLAY_PROMPT_STATUS_MESSAGE 0x0112 @@ -680,6 +687,7 @@ char promptMessage[32]; uint32_t lineInstance; uint32_t callReference; + uint32_t space[3]; }; #define CLEAR_PROMPT_MESSAGE 0x0113 @@ -720,6 +728,7 @@ struct clear_prompt_message clearpromptstatus; struct definetimedate_message definetimedate; struct start_tone_message starttone; + struct stop_tone_message stoptone; struct speed_dial_stat_res_message speeddial; struct line_state_req_message line; struct line_stat_res_message linestat; @@ -747,6 +756,7 @@ struct display_notify_message displaynotify; struct dialed_number_message dialednumber; struct soft_key_event_message softkeyeventmessage; + struct enbloc_call_message enbloccallmessage; }; /* packet composition */ @@ -938,15 +948,18 @@ int nat; int outgoing; int alreadygone; + int blindxfer; + int xferor; struct skinny_subchannel *next; + struct skinny_subchannel *related; struct skinny_line *parent; }; struct skinny_line { ast_mutex_t lock; char name[80]; - char label[24]; /* Label that shows next to the line buttons */ + char label[42]; /* Label that shows next to the line buttons */ char accountcode[AST_MAX_ACCOUNT_CODE]; char exten[AST_MAX_EXTENSION]; /* Extension where to start */ char context[AST_MAX_CONTEXT]; @@ -987,6 +1000,7 @@ struct ast_codec_pref prefs; struct skinny_subchannel *sub; + struct skinny_subchannel *activesub; struct skinny_line *next; struct skinny_device *parent; }; @@ -1086,6 +1100,7 @@ }; 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) { @@ -1518,9 +1533,6 @@ if (!(req = req_alloc(sizeof(struct call_state_message), CALL_STATE_MESSAGE))) return; - if (state == SKINNY_ONHOOK) { - transmit_speaker_mode(s, SKINNY_SPEAKEROFF); - } req->data.callstate.callState = htolel(state); req->data.callstate.lineInstance = htolel(instance); req->data.callstate.callReference = htolel(callid); @@ -1602,7 +1614,7 @@ transmit_response(s, req); } -static void transmit_tone(struct skinnysession *s, int tone) +static void transmit_tone(struct skinnysession *s, int tone, int instance, int reference) { struct skinny_req *req; @@ -1614,14 +1626,16 @@ if (tone > 0) { if (!(req = req_alloc(sizeof(struct start_tone_message), START_TONE_MESSAGE))) return; + req->data.starttone.tone = htolel(tone); + req->data.starttone.instance = htolel(instance); + req->data.starttone.reference = htolel(reference); } else { if (!(req = req_alloc(sizeof(struct stop_tone_message), STOP_TONE_MESSAGE))) return; + req->data.stoptone.instance = htolel(instance); + req->data.stoptone.reference = htolel(reference); } - if (tone > 0) { - req->data.starttone.tone = htolel(tone); - } transmit_response(s, req); } @@ -1677,14 +1691,17 @@ transmit_response(s, req); } -static void transmit_displaymessage(struct skinnysession *s, const char *text) +static void transmit_displaymessage(struct skinnysession *s, const char *text, int instance, int reference) { struct skinny_req *req; if (text == 0) { - if (!(req = req_alloc(0, CLEAR_DISPLAY_MESSAGE))) + if (!(req = req_alloc(sizeof(struct clear_prompt_message), CLEAR_DISPLAY_MESSAGE))) return; + req->data.clearpromptstatus.lineInstance = instance; + req->data.clearpromptstatus.callReference = reference; + if (skinnydebug) ast_verbose("Clearing Display\n"); } else { @@ -1719,17 +1736,28 @@ { struct skinny_req *req; - if (!(req = req_alloc(sizeof(struct display_prompt_status_message), DISPLAY_PROMPT_STATUS_MESSAGE))) - return; + if (text == 0) { + if (!(req = req_alloc(sizeof(struct clear_prompt_message), CLEAR_PROMPT_MESSAGE))) + return; - ast_copy_string(req->data.displaypromptstatus.promptMessage, text, sizeof(req->data.displaypromptstatus.promptMessage)); - req->data.displaypromptstatus.messageTimeout = htolel(t); - req->data.displaypromptstatus.lineInstance = htolel(instance); - req->data.displaypromptstatus.callReference = htolel(callid); + req->data.clearpromptstatus.lineInstance = htolel(instance); + req->data.clearpromptstatus.callReference = htolel(callid); - if (skinnydebug) - ast_verbose("Displaying Prompt Status '%s'\n", text); + if (skinnydebug) + ast_verbose("Clearing Prompt\n"); + } else { + if (!(req = req_alloc(sizeof(struct display_prompt_status_message), DISPLAY_PROMPT_STATUS_MESSAGE))) + return; + ast_copy_string(req->data.displaypromptstatus.promptMessage, text, sizeof(req->data.displaypromptstatus.promptMessage)); + req->data.displaypromptstatus.messageTimeout = htolel(t); + req->data.displaypromptstatus.lineInstance = htolel(instance); + req->data.displaypromptstatus.callReference = htolel(callid); + + if (skinnydebug) + ast_verbose("Displaying Prompt Status '%s'\n", text); + } + transmit_response(s, req); } @@ -2389,7 +2417,7 @@ res = ast_pbx_run(c); if (res) { ast_log(LOG_WARNING, "PBX exited non-zero\n"); - transmit_tone(s, SKINNY_REORDER); + transmit_tone(s, SKINNY_REORDER, l->instance, sub->callid); } return NULL; } @@ -2409,7 +2437,7 @@ if (option_verbose > 2) ast_verbose( VERBOSE_PREFIX_3 "Starting simple switch on '%s@%s'\n", l->name, d->name); - + len = strlen(d->exten); while (len < AST_MAX_EXTENSION-1) { @@ -2421,6 +2449,7 @@ res = 0; break; } + res = 1; } @@ -2428,7 +2457,7 @@ len = strlen(d->exten); if (!ast_ignore_pattern(c->context, d->exten)) { - transmit_tone(s, SKINNY_SILENCE); + transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid); } if (ast_exists_extension(c, c->context, d->exten, 1, l->cid_num)) { if (!res || !ast_matchmore_extension(c, c->context, d->exten, 1, l->cid_num)) { @@ -2438,7 +2467,7 @@ if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Setting call forward to '%s' on channel %s\n", l->call_forward, c->name); - transmit_tone(s, SKINNY_DIALTONE); + transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid); if (res) { break; } @@ -2446,7 +2475,7 @@ ast_indicate(c, -1); ast_safe_sleep(c, 1000); memset(d->exten, 0, sizeof(d->exten)); - transmit_tone(s, SKINNY_DIALTONE); + transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid); len = 0; getforward = 0; } else { @@ -2463,7 +2492,7 @@ } } else if (res == 0) { ast_debug(1, "Not enough digits (%s) (and no ambiguous match)...\n", d->exten); - transmit_tone(s, SKINNY_REORDER); + transmit_tone(s, SKINNY_REORDER, l->instance, sub->callid); if (sub->owner && sub->owner->_state != AST_STATE_UP) { ast_indicate(c, -1); ast_hangup(c); @@ -2472,7 +2501,8 @@ } else if (!ast_canmatch_extension(c, c->context, d->exten, 1, c->cid.cid_num) && ((d->exten[0] != '*') || (!ast_strlen_zero(d->exten) > 2))) { ast_log(LOG_WARNING, "Can't match [%s] from '%s' in context %s\n", d->exten, c->cid.cid_num ? c->cid.cid_num : "", c->context); - transmit_tone(s, SKINNY_REORDER); + memset(d->exten, 0, sizeof(d->exten)); + transmit_tone(s, SKINNY_REORDER, l->instance, sub->callid); /* hang out for 3 seconds to let congestion play */ ast_safe_sleep(c, 3000); break; @@ -2545,10 +2575,11 @@ 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; + int onlysub = 0; if (!sub) { ast_debug(1, "Asked to hangup channel not connected\n"); @@ -2557,20 +2588,39 @@ l = sub->parent; d = l->parent; s = d->session; - if (skinnydebug) - ast_verbose("skinny_hangup(%s) on %s@%s\n", ast->name, l->name, d->name); + 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; + } + } + + if (d->registered) { if ((l->type = TYPE_LINE) && (l->hookstate == SKINNY_OFFHOOK)) { - l->hookstate = SKINNY_ONHOOK; transmit_callstate(s, l->instance, SKINNY_ONHOOK, sub->callid); - transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_OFF); - transmit_speaker_mode(s, SKINNY_SPEAKEROFF); + if (onlysub){ + if (skinnydebug) + ast_verbose("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); + } else { + if (skinnydebug) + ast_verbose("skinny_hangup(%s) on %s@%s \n", ast->name, l->name, d->name); + } } else if ((l->type = TYPE_LINE) && (l->hookstate == SKINNY_ONHOOK)) { transmit_callstate(s, l->instance, SKINNY_ONHOOK, sub->callid); transmit_speaker_mode(s, SKINNY_SPEAKEROFF); transmit_ringer_mode(s, SKINNY_RING_OFF); - transmit_tone(s, SKINNY_SILENCE); + transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid); transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_OFF); do_housekeeping(s); } @@ -2585,6 +2635,7 @@ sub->rtp = NULL; } ast_mutex_unlock(&sub->lock); + l->activesub = NULL; return 0; } @@ -2596,6 +2647,15 @@ struct skinny_device *d = l->parent; struct skinnysession *s = d->session; + if (sub->blindxfer) { + if (skinnydebug) + ast_verbose("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); @@ -2606,7 +2666,7 @@ ast_setstate(ast, AST_STATE_UP); } - transmit_tone(s, SKINNY_SILENCE); + transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid); /* order matters here... for some reason, transmit_callinfo must be before transmit_callstate, or you won't get keypad messages in some situations. */ @@ -2614,6 +2674,7 @@ transmit_callstate(s, l->instance, SKINNY_CONNECTED, sub->callid); transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_CONNECTED); transmit_displaypromptstatus(s, "Connected", 0, l->instance, sub->callid); + l->activesub = sub; return res; } @@ -2729,7 +2790,7 @@ int tmp; /* not right */ sprintf(tmp, "%d", digit); - transmit_tone(d->session, digit); + transmit_tone(d->session, digit, l->instance, sub->callid); #endif return -1; /* Stop inband indications */ } @@ -2782,6 +2843,68 @@ } } +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_verbose(VERBOSE_PREFIX_3 "Transferee channels (local/remote): %s and %s\n", + xferee->owner->name, ast_bridged_channel(xferee->owner)?ast_bridged_channel(xferee->owner)->name:""); + ast_verbose(VERBOSE_PREFIX_3 "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_verbose(VERBOSE_PREFIX_3 "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_verbose(VERBOSE_PREFIX_3 "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) { @@ -2794,9 +2917,15 @@ ast_verbose(VERBOSE_PREFIX_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_verbose(VERBOSE_PREFIX_3 "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) { - transmit_tone(s, SKINNY_ALERT); + transmit_tone(s, SKINNY_ALERT, l->instance, sub->callid); transmit_callstate(s, l->instance, SKINNY_RINGOUT, sub->callid); transmit_dialednumber(s, ast->exten, l->instance, sub->callid); transmit_displaypromptstatus(s, "Ring Out", 0, l->instance, sub->callid); @@ -2808,7 +2937,7 @@ return -1; case AST_CONTROL_BUSY: if (ast->_state != AST_STATE_UP) { - transmit_tone(s, SKINNY_BUSYTONE); + transmit_tone(s, SKINNY_BUSYTONE, l->instance, sub->callid); transmit_callstate(s, l->instance, SKINNY_BUSY, sub->callid); sub->alreadygone = 1; ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV); @@ -2817,7 +2946,7 @@ return -1; case AST_CONTROL_CONGESTION: if (ast->_state != AST_STATE_UP) { - transmit_tone(s, SKINNY_REORDER); + transmit_tone(s, SKINNY_REORDER, l->instance, sub->callid); transmit_callstate(s, l->instance, SKINNY_CONGESTION, sub->callid); sub->alreadygone = 1; ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV); @@ -2826,7 +2955,7 @@ return -1; case AST_CONTROL_PROGRESS: if ((ast->_state != AST_STATE_UP) && !sub->progress && !sub->outgoing) { - transmit_tone(s, SKINNY_ALERT); + transmit_tone(s, SKINNY_ALERT, l->instance, sub->callid); transmit_callstate(s, l->instance, SKINNY_PROGRESS, sub->callid); transmit_displaypromptstatus(s, "Call Progress", 0, l->instance, sub->callid); transmit_callinfo(s, ast->cid.cid_name, ast->cid.cid_num, ast->exten, ast->exten, l->instance, sub->callid, 2); /* 2 = outgoing from phone */ @@ -2835,7 +2964,7 @@ } return -1; case -1: - transmit_tone(s, SKINNY_SILENCE); + transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid); break; case AST_CONTROL_HOLD: ast_moh_start(ast, data, l->mohinterpret); @@ -2879,9 +3008,13 @@ 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; @@ -2947,7 +3080,7 @@ /* Channel needs to be put on hold */ if (skinnydebug) - ast_verbose("Putting on Hold(%d)\n", l->instance); + ast_verbose(VERBOSE_PREFIX_3 "Putting on Hold (%s)\n", sub->owner->name); ast_queue_control_data(sub->owner, AST_CONTROL_HOLD, S_OR(l->mohsuggest, NULL), @@ -2973,6 +3106,7 @@ req->data.stopmedia.passThruPartyId = htolel(sub->callid); transmit_response(s, req); + transmit_callstate(s, l->instance, SKINNY_HOLD, sub->callid); transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_WINK); sub->onhold = 1; return 1; @@ -2987,7 +3121,7 @@ /* Channel is on hold, so we will unhold */ if (skinnydebug) - ast_verbose("Taking off Hold(%d)\n", l->instance); + ast_verbose(VERBOSE_PREFIX_3 "Taking off Hold (%s)\n", sub->owner->name); ast_queue_control(sub->owner, AST_CONTROL_UNHOLD); @@ -2998,11 +3132,94 @@ transmit_response(s, req); transmit_connect(s, sub); + transmit_callstate(s, l->instance, SKINNY_CONNECTED, sub->callid); transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON); 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_verbose("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))) @@ -3107,6 +3324,8 @@ return 0; l = sub->parent; + /* sub = l->activesub; */ + if (sub->owner) { if (sub->owner->_state == 0) { f.frametype = AST_FRAME_DTMF_BEGIN; @@ -3151,7 +3370,7 @@ ast_verbose("unknown1 in handle_stimulus_message is '%d'\n", unknown1); sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference); - + if (!sub) { l = find_line_by_instance(d, d->lastlineinstance); if (!l) { @@ -3161,11 +3380,21 @@ l = sub->parent; } + sub = l->activesub; + switch(event) { case STIMULUS_REDIAL: if (skinnydebug) ast_verbose("Received Stimulus: Redial(%d)\n", instance); + if (ast_strlen_zero(l->lastnumberdialed)) { + ast_log(LOG_WARNING, "Attempted redial, but no previously dialed number found.\n"); + l->hookstate = SKINNY_ONHOOK; + transmit_speaker_mode(s, SKINNY_SPEAKEROFF); + transmit_callstate(s, l->instance, SKINNY_ONHOOK, instance); + break; + } + c = skinny_new(l, AST_STATE_DOWN); if (!c) { ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name); @@ -3178,15 +3407,12 @@ } if (skinnydebug) ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name); - transmit_displaymessage(s, NULL); /* clear display */ - transmit_tone(s, SKINNY_DIALTONE); + transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */ + transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid); + transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_RINGOUT); - if (ast_strlen_zero(l->lastnumberdialed)) { - ast_log(LOG_WARNING, "Attempted redial, but no previously dialed number found.\n"); - return 0; - } if (!ast_ignore_pattern(c->context, l->lastnumberdialed)) { - transmit_tone(s, SKINNY_SILENCE); + transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid); } ast_copy_string(c->exten, l->lastnumberdialed, sizeof(c->exten)); if (ast_pthread_create(&t, NULL, skinny_newcall, c)) { @@ -3221,11 +3447,12 @@ } if (skinnydebug) ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name); - transmit_displaymessage(s, NULL); /* clear display */ - transmit_tone(s, SKINNY_DIALTONE); + transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */ + transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid); + transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_RINGOUT); if (!ast_ignore_pattern(c->context, sd->exten)) { - transmit_tone(s, SKINNY_SILENCE); + transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid); } if (ast_exists_extension(c, c->context, sd->exten, 1, l->cid_num)) { ast_copy_string(c->exten, sd->exten, sizeof(c->exten)); @@ -3242,26 +3469,17 @@ case STIMULUS_HOLD: if (skinnydebug) ast_verbose("Received Stimulus: Hold(%d)\n", instance); - - if (!sub) - break; - - if (sub->onhold) { - skinny_unhold(sub); - } else { - skinny_hold(sub); - } + handle_hold_button(sub); break; case STIMULUS_TRANSFER: if (skinnydebug) ast_verbose("Received Stimulus: Transfer(%d)\n", instance); - transmit_tone(s, SKINNY_DIALTONE); - /* XXX figure out how to transfer */ + handle_transfer_button(sub); break; case STIMULUS_CONFERENCE: if (skinnydebug) ast_verbose("Received Stimulus: Conference(%d)\n", instance); - transmit_tone(s, SKINNY_DIALTONE); + transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid); /* XXX determine the best way to pull off a conference. Meetme? */ break; case STIMULUS_VOICEMAIL: @@ -3291,11 +3509,12 @@ if (skinnydebug) ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name); - transmit_displaymessage(s, NULL); /* clear display */ - transmit_tone(s, SKINNY_DIALTONE); + transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */ + transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid); + transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_RINGOUT); if (!ast_ignore_pattern(c->context, vmexten)) { - transmit_tone(s, SKINNY_SILENCE); + transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid); } if (ast_exists_extension(c, c->context, l->vmexten, 1, l->cid_num)) { @@ -3321,7 +3540,7 @@ /* Because it's the same thing. */ /* Do not disturb */ - transmit_tone(s, SKINNY_DIALTONE); + transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid); if (l->dnd != 0){ if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Disabling DND on %s@%s\n", l->name, d->name); @@ -3372,7 +3591,7 @@ /* We're answering a ringing call */ ast_queue_control(sub->owner, AST_CONTROL_ANSWER); transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid); - transmit_tone(s, SKINNY_SILENCE); + transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid); transmit_callstate(s, l->instance, SKINNY_CONNECTED, sub->callid); transmit_displaypromptstatus(s, "Connected", 0, l->instance, sub->callid); transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_CONNECTED); @@ -3388,8 +3607,8 @@ transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid); if (skinnydebug) ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name); - transmit_displaymessage(s, NULL); /* clear display */ - transmit_tone(s, SKINNY_DIALTONE); + transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */ + transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid); transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_OFFHOOK); /* start the switch thread */ @@ -3420,16 +3639,16 @@ struct skinny_subchannel *sub; struct ast_channel *c; pthread_t t; - int unknown1; - int unknown2; + int instance; + int reference; - unknown1 = letohl(req->data.offhook.unknown1); - unknown2 = letohl(req->data.offhook.unknown2); + instance = letohl(req->data.offhook.instance); + reference = letohl(req->data.offhook.reference); - sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference); + sub = find_subchannel_by_instance_reference(d, instance, reference); if (!sub) { - l = find_line_by_instance(d, d->lastlineinstance); + l = find_line_by_instance(d, instance); if (!l) { return 0; } @@ -3452,7 +3671,7 @@ /* We're answering a ringing call */ ast_queue_control(sub->owner, AST_CONTROL_ANSWER); transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid); - transmit_tone(s, SKINNY_SILENCE); + 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); start_rtp(sub); @@ -3467,8 +3686,8 @@ transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid); if (skinnydebug) ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name); - transmit_displaymessage(s, NULL); /* clear display */ - transmit_tone(s, SKINNY_DIALTONE); + transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */ + transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid); transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_OFFHOOK); /* start the switch thread */ @@ -3488,14 +3707,15 @@ { 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); + sub = find_subchannel_by_instance_reference(d, instance, reference); if (!sub) { return 0; @@ -3506,7 +3726,6 @@ /* 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); @@ -3514,10 +3733,29 @@ 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){ /* is this the only call to this device? */ + l->hookstate = SKINNY_ONHOOK; + if (skinnydebug) + ast_verbose("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_verbose("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))) { /* We're allowed to transfer, we have two active calls and we made at least one of the calls. Let's try and transfer */ @@ -3571,7 +3809,8 @@ } d->capability &= codecs; - ast_verbose("Device capability set to '%d'\n", d->capability); + if (skinnydebug) + ast_verbose("Device capability set to '%d'\n", d->capability); for (l = d->lines; l; l = l->next) { ast_mutex_lock(&l->lock); l->capability = d->capability; @@ -3694,7 +3933,8 @@ for (l = d->lines; l; l = l->next) { if (l->instance == lineInstance) { - ast_verbose("Adding button: %d, %d\n", BT_LINE, lineInstance); + if (skinnydebug) + ast_verbose("Adding button: %d, %d\n", BT_LINE, lineInstance); req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE; req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance); lineInstance++; @@ -3707,7 +3947,8 @@ if (!btnSet) { for (sd = d->speeddials; sd; sd = sd->next) { if (sd->isHint && sd->instance == lineInstance) { - ast_verbose("Adding button: %d, %d\n", BT_LINE, lineInstance); + if (skinnydebug) + ast_verbose("Adding button: %d, %d\n", BT_LINE, lineInstance); req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE; req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance); lineInstance++; @@ -3725,7 +3966,8 @@ for (l = d->lines; l; l = l->next) { if (l->instance == lineInstance) { - ast_verbose("Adding button: %d, %d\n", BT_LINE, lineInstance); + if (skinnydebug) + ast_verbose("Adding button: %d, %d\n", BT_LINE, lineInstance); req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE; req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance); lineInstance++; @@ -3738,7 +3980,8 @@ if (!btnSet) { for (sd = d->speeddials; sd; sd = sd->next) { if (sd->isHint && sd->instance == lineInstance) { - ast_verbose("Adding button: %d, %d\n", BT_LINE, lineInstance); + if (skinnydebug) + ast_verbose("Adding button: %d, %d\n", BT_LINE, lineInstance); req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE; req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance); lineInstance++; @@ -3746,7 +3989,8 @@ btnSet = 1; break; } else if (!sd->isHint && sd->instance == speeddialInstance) { - ast_verbose("Adding button: %d, %d\n", BT_SPEEDDIAL, speeddialInstance); + if (skinnydebug) + ast_verbose("Adding button: %d, %d\n", BT_SPEEDDIAL, speeddialInstance); req->data.buttontemplate.definition[i].buttonDefinition = BT_SPEEDDIAL; req->data.buttontemplate.definition[i].instanceNumber = htolel(speeddialInstance); speeddialInstance++; @@ -3763,7 +4007,8 @@ for (l = d->lines; l; l = l->next) { if (l->instance == lineInstance) { - ast_verbose("Adding button: %d, %d\n", BT_LINE, lineInstance); + if (skinnydebug) + ast_verbose("Adding button: %d, %d\n", BT_LINE, lineInstance); req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE; req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance); lineInstance++; @@ -3779,7 +4024,8 @@ for (sd = d->speeddials; sd; sd = sd->next) { if (!sd->isHint && sd->instance == speeddialInstance) { - ast_verbose("Adding button: %d, %d\n", BT_SPEEDDIAL, speeddialInstance); + if (skinnydebug) + ast_verbose("Adding button: %d, %d\n", BT_SPEEDDIAL, speeddialInstance); req->data.buttontemplate.definition[i].buttonDefinition = BT_SPEEDDIAL; req->data.buttontemplate.definition[i].instanceNumber = htolel(speeddialInstance - 1); speeddialInstance++; @@ -3792,7 +4038,8 @@ case BT_NONE: break; default: - ast_verbose("Adding button: %d, %d\n", btn[i].buttonDefinition, 0); + if (skinnydebug) + ast_verbose("Adding button: %d, %d\n", btn[i].buttonDefinition, 0); req->data.buttontemplate.definition[i].buttonDefinition = htolel(btn[i].buttonDefinition); req->data.buttontemplate.definition[i].instanceNumber = htolel(0); buttonCount++; @@ -3901,7 +4148,7 @@ if (skinnydebug) ast_verbose("Setting payloadType to '%d' (%d ms)\n", fmt.bits, fmt.cur_ms); - req->data.startmedia.conferenceId = htolel(0); + req->data.startmedia.conferenceId = htolel(sub->callid); req->data.startmedia.passThruPartyId = htolel(sub->callid); req->data.startmedia.remoteIp = htolel(d->ourip.s_addr); req->data.startmedia.remotePort = htolel(ntohs(us.sin_port)); @@ -3916,6 +4163,56 @@ return 1; } +static int handle_enbloc_call_message(struct skinny_req *req, struct skinnysession *s) +{ + struct skinny_device *d = s->device; + struct skinny_line *l; + struct skinny_subchannel *sub = NULL; + struct ast_channel *c; + pthread_t t; + + if (skinnydebug) + ast_verbose("Received Enbloc Call: %s\n", req->data.enbloccallmessage.calledParty); + + 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; + } + + c = skinny_new(l, AST_STATE_DOWN); + + if(!c) { + ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name); + } else { + l->hookstate = SKINNY_OFFHOOK; + + sub = c->tech_pvt; + transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid); + if (skinnydebug) + ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name); + transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */ + transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid); + + if (!ast_ignore_pattern(c->context, req->data.enbloccallmessage.calledParty)) { + transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid); + } + ast_copy_string(c->exten, req->data.enbloccallmessage.calledParty, sizeof(c->exten)); + if (ast_pthread_create(&t, NULL, skinny_newcall, c)) { + ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno)); + ast_hangup(c); + } + } + + return 1; +} + + static int handle_soft_key_set_req_message(struct skinny_req *req, struct skinnysession *s) { int i; @@ -3937,6 +4234,7 @@ for (i = 0; i < (sizeof(soft_key_template_default) / sizeof(struct soft_key_template_definition)); i++) { if (defaults[y] == i+1) { req->data.softkeysets.softKeySetDefinition[softkeymode->mode].softKeyTemplateIndex[y] = htolel(i+1); + req->data.softkeysets.softKeySetDefinition[softkeymode->mode].softKeyInfoIndex[y] = htolel(i+301); } } } @@ -3973,6 +4271,8 @@ l = find_line_by_instance(d, d->lastlineinstance); } + /* sub = l->activesub; */ /*Not needed with proper call tracking? */ + if (!l) { if (skinnydebug) ast_verbose("Received Softkey Event: %d(%d)\n", event, instance); @@ -3988,8 +4288,16 @@ break; case SOFTKEY_REDIAL: if (skinnydebug) - ast_verbose("Received Softkey Event: Redial(%d)\n", instance); + ast_verbose("Received Softkey Event: Redial(%d/%d)\n", instance, reference); + if (ast_strlen_zero(l->lastnumberdialed)) { + ast_log(LOG_WARNING, "Attempted redial, but no previously dialed number found.\n"); + l->hookstate = SKINNY_ONHOOK; + transmit_speaker_mode(s, SKINNY_SPEAKEROFF); + transmit_callstate(s, l->instance, SKINNY_ONHOOK, instance); + break; + } + if (!sub || !sub->owner) { c = skinny_new(l, AST_STATE_DOWN); } else { @@ -4007,15 +4315,12 @@ } if (skinnydebug) ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name); - transmit_displaymessage(s, NULL); /* clear display */ - transmit_tone(s, SKINNY_DIALTONE); + transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */ + transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid); + transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_RINGOUT); - if (ast_strlen_zero(l->lastnumberdialed)) { - ast_log(LOG_WARNING, "Attempted redial, but no previously dialed number found.\n"); - break; - } if (!ast_ignore_pattern(c->context, l->lastnumberdialed)) { - transmit_tone(s, SKINNY_SILENCE); + transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid); } ast_copy_string(c->exten, l->lastnumberdialed, sizeof(c->exten)); if (ast_pthread_create(&t, NULL, skinny_newcall, c)) { @@ -4026,12 +4331,14 @@ break; case SOFTKEY_NEWCALL: /* Actually the DIAL softkey */ if (skinnydebug) - ast_verbose("Received Softkey Event: New Call(%d)\n", instance); + ast_verbose("Received Softkey Event: New Call(%d/%d)\n", instance, reference); if (!sub || !sub->owner) { c = skinny_new(l, AST_STATE_DOWN); } else { - c = sub->owner; + if (!sub->onhold) + skinny_hold(sub); + c = skinny_new(l, AST_STATE_DOWN); } /* transmit_ringer_mode(s,SKINNY_RING_OFF); @@ -4045,14 +4352,15 @@ sub = c->tech_pvt; if (l->hookstate == SKINNY_ONHOOK) { l->hookstate = SKINNY_OFFHOOK; - transmit_speaker_mode(s, SKINNY_SPEAKERON); - transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid); } + transmit_speaker_mode(s, SKINNY_SPEAKERON); + transmit_callstate(s, l->instance, l->hookstate, sub->callid); + if (skinnydebug) ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name); - transmit_displaymessage(s, NULL); /* clear display */ - transmit_tone(s, SKINNY_DIALTONE); + transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */ + transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid); transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_OFFHOOK); /* start the switch thread */ @@ -4064,29 +4372,20 @@ break; case SOFTKEY_HOLD: if (skinnydebug) - ast_verbose("Received Softkey Event: Hold(%d)\n", instance); - - if (sub) { - if (sub->onhold) { - skinny_unhold(sub); - } else { - skinny_hold(sub); - } - } - + ast_verbose("Received Softkey Event: Hold(%d/%d)\n", instance, reference); + handle_hold_button(sub); break; case SOFTKEY_TRNSFER: if (skinnydebug) - ast_verbose("Received Softkey Event: Transfer(%d)\n", instance); - transmit_tone(s, SKINNY_DIALTONE); - /* XXX figure out how to transfer */ + ast_verbose("Received Softkey Event: Transfer(%d/%d)\n", instance, reference); + handle_transfer_button(sub); break; case SOFTKEY_CFWDALL: if (skinnydebug) - ast_verbose("Received Softkey Event: Forward All(%d)\n", instance); + ast_verbose("Received Softkey Event: Forward All(%d/%d)\n", instance, reference); /* Do not disturb */ - transmit_tone(s, SKINNY_DIALTONE); + transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid); if (l->dnd != 0){ if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Disabling DND on %s@%s\n", l->name, d->name); @@ -4103,30 +4402,49 @@ break; case SOFTKEY_CFWDBUSY: if (skinnydebug) - ast_verbose("Received Softkey Event: Forward Busy (%d)\n", instance); + ast_verbose("Received Softkey Event: Forward Busy (%d/%d)\n", instance, reference); break; case SOFTKEY_CFWDNOANSWER: if (skinnydebug) - ast_verbose("Received Softkey Event: Forward No Answer (%d)\n", instance); + ast_verbose("Received Softkey Event: Forward No Answer (%d/%d)\n", instance, reference); break; case SOFTKEY_BKSPC: if (skinnydebug) - ast_verbose("Received Softkey Event: Backspace(%d)\n", instance); + ast_verbose("Received Softkey Event: Backspace(%d/%d)\n", instance, reference); break; case SOFTKEY_ENDCALL: if (skinnydebug) - ast_verbose("Received Softkey Event: End Call(%d)\n", instance); + ast_verbose("Received Softkey Event: End Call(%d/%d)\n", instance, reference); if (l->hookstate == SKINNY_ONHOOK) { /* Something else already put us back on hook */ 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){ /*Are there other calls to this device */ + l->hookstate = SKINNY_ONHOOK; + if (skinnydebug) + ast_verbose("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_verbose("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))) { /* We're allowed to transfer, we have two active calls and we made at least one of the calls. Let's try and transfer */ @@ -4160,11 +4478,21 @@ break; case SOFTKEY_RESUME: if (skinnydebug) - ast_verbose("Received Softkey Event: Resume(%d)\n", instance); + ast_verbose("Received Softkey Event: Resume(%d/%d)\n", instance, reference); + + 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) - ast_verbose("Received Softkey Event: Answer(%d)\n", instance); + ast_verbose("Received Softkey Event: Answer(%d/%d)\n", instance, reference); transmit_ringer_mode(s,SKINNY_RING_OFF); transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON); @@ -4175,7 +4503,7 @@ /* We're answering a ringing call */ ast_queue_control(sub->owner, AST_CONTROL_ANSWER); transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid); - transmit_tone(s, SKINNY_SILENCE); + 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); start_rtp(sub); @@ -4184,39 +4512,39 @@ break; case SOFTKEY_INFO: if (skinnydebug) - ast_verbose("Received Softkey Event: Info(%d)\n", instance); + ast_verbose("Received Softkey Event: Info(%d/%d)\n", instance, reference); break; case SOFTKEY_CONFRN: if (skinnydebug) - ast_verbose("Received Softkey Event: Transfer(%d)\n", instance); - transmit_tone(s, SKINNY_DIALTONE); + ast_verbose("Received Softkey Event: Transfer(%d/%d)\n", instance, reference); + transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid); /* XXX determine the best way to pull off a conference. Meetme? */ break; case SOFTKEY_PARK: if (skinnydebug) - ast_verbose("Received Softkey Event: Park Call(%d)\n", instance); + ast_verbose("Received Softkey Event: Park Call(%d/%d)\n", instance, reference); /* XXX Park the call */ break; case SOFTKEY_JOIN: if (skinnydebug) - ast_verbose("Received Softkey Event: Join(%d)\n", instance); + ast_verbose("Received Softkey Event: Join(%d/%d)\n", instance, reference); break; case SOFTKEY_MEETME: /* XXX How is this different from CONFRN? */ if (skinnydebug) - ast_verbose("Received Softkey Event: Meetme(%d)\n", instance); + ast_verbose("Received Softkey Event: Meetme(%d/%d)\n", instance, reference); break; case SOFTKEY_PICKUP: if (skinnydebug) - ast_verbose("Received Softkey Event: None(%d)\n", instance); + ast_verbose("Received Softkey Event: None(%d/%d)\n", instance, reference); break; case SOFTKEY_GPICKUP: if (skinnydebug) - ast_verbose("Received Softkey Event: Group Pickup (%d)\n", instance); + ast_verbose("Received Softkey Event: Group Pickup (%d/%d)\n", instance, reference); break; default: if (skinnydebug) - ast_verbose("Received unknown Softkey Event: %d(%d)\n", event, instance); + ast_verbose("Received unknown Softkey Event: %d(%d/%d)\n", event, instance, reference); break; } ast_device_state_changed("Skinny/%s@%s", l->name, d->name); @@ -4293,7 +4621,7 @@ sub = find_subchannel_by_instance_reference(d, lineInstance, callReference); - if (sub && (sub->owner->_state < AST_STATE_UP)) { + if (sub && ((sub->owner->_state < AST_STATE_UP) || sub->onhold)) { char dgt; int digit = letohl(req->data.keypad.button); @@ -4320,6 +4648,9 @@ } else res = handle_keypad_button_message(req, s); break; + case ENBLOC_CALL_MESSAGE: + res = handle_enbloc_call_message(req, s); + break; case STIMULUS_MESSAGE: res = handle_stimulus_message(req, s); break; @@ -4342,8 +4673,6 @@ res = handle_speed_dial_stat_req_message(req, s); break; case LINE_STATE_REQ_MESSAGE: - if (skinnydebug) - ast_verbose("Received LineStatRequest\n"); res = handle_line_state_req_message(req, s); break; case TIME_DATE_REQ_MESSAGE: