[svn-commits] mvanbaak: branch mvanbaak/skinny-tranfer r122428 - /team/mvanbaak/skinny-tran...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Thu Jun 12 16:52:23 CDT 2008


Author: mvanbaak
Date: Thu Jun 12 16:52:23 2008
New Revision: 122428

URL: http://svn.digium.com/view/asterisk?view=rev&rev=122428
Log:
pull in patch from mantis.
Thanks to wedhorn and DEA for working on this.

Modified:
    team/mvanbaak/skinny-tranfer/channels/chan_skinny.c

Modified: team/mvanbaak/skinny-tranfer/channels/chan_skinny.c
URL: http://svn.digium.com/view/asterisk/team/mvanbaak/skinny-tranfer/channels/chan_skinny.c?view=diff&rev=122428&r1=122427&r2=122428
==============================================================================
--- team/mvanbaak/skinny-tranfer/channels/chan_skinny.c (original)
+++ team/mvanbaak/skinny-tranfer/channels/chan_skinny.c Thu Jun 12 16:52:23 2008
@@ -67,6 +67,7 @@
 #include "asterisk/threadstorage.h"
 #include "asterisk/devicestate.h"
 #include "asterisk/event.h"
+#include "asterisk/indications.h"
 
 /*************************************
  * Skinny/Asterisk Protocol Settings *
@@ -202,14 +203,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
@@ -797,6 +798,7 @@
 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 @@
 	char promptMessage[32];
 	uint32_t lineInstance;
 	uint32_t callReference;
+	uint32_t space[3];
 };
 
 #define CLEAR_PROMPT_MESSAGE  0x0113
@@ -1139,8 +1142,12 @@
 	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 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 @@
 	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 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 @@
 	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;
@@ -3156,6 +3214,9 @@
 					l->nat = nat;
 					l->canreinvite = canreinvite;
 
+					if (!d->lines) {
+						d->activeline = l;
+					}
 					l->next = d->lines;
 					d->lines = l;
 				}
@@ -3390,7 +3451,7 @@
 		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);
@@ -3405,7 +3466,7 @@
 
 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;
@@ -3417,22 +3478,88 @@
 	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;
@@ -3458,6 +3585,15 @@
 
 	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);
@@ -3473,10 +3609,11 @@
 	   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;
 }
 
@@ -3674,6 +3811,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_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)
 {
@@ -3694,12 +3893,18 @@
 		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 */
@@ -3715,7 +3920,7 @@
 			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) {
@@ -3728,7 +3933,7 @@
 			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) {
@@ -3741,7 +3946,7 @@
 			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;
@@ -3801,9 +4006,14 @@
 			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;
@@ -3876,7 +4086,6 @@
 	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)
@@ -3890,26 +4099,11 @@
 		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;
@@ -3920,7 +4114,6 @@
 	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)
@@ -3932,16 +4125,96 @@
 
 	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)
@@ -4080,7 +4353,8 @@
 	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;
@@ -4230,20 +4504,15 @@
 	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)
@@ -4386,6 +4655,8 @@
 			return 0;
 		}
 
+		d->activeline = l;
+
 		/* turn the speaker on */
 		transmit_speaker_mode(s, SKINNY_SPEAKERON);
 		transmit_ringer_mode(s, SKINNY_RING_OFF);
@@ -4400,7 +4671,7 @@
 			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);
@@ -4446,22 +4717,40 @@
 	struct skinny_line *l;
 	struct skinny_subchannel *sub;
 	struct ast_channel *c;
+	struct skinny_line *tmp;
 	pthread_t t;
-	int unknown1;
-	int unknown2;
-
-	unknown1 = letohl(req->data.offhook.unknown1);
-	unknown2 = letohl(req->data.offhook.unknown2);
-
-	sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
-
-	if (!sub) {
-		l = find_line_by_instance(d, d->lastlineinstance);
-		if (!l) {
+	int instance;
+	int reference;
+
+	/* 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 */
+
+	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);
@@ -4478,7 +4767,7 @@
 	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);
@@ -4515,25 +4804,29 @@
 {
 	struct skinny_device *d = s->device;
 	struct skinny_line *l;
-	struct skinny_subchannel *sub;
-	int unknown1;
-	int unknown2;
-
-	unknown1 = letohl(req->data.onhook.unknown1);
-	unknown2 = letohl(req->data.onhook.unknown2);
-
-	sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
-
-	if (!sub) {
-		return 0;
-	}
-	l = sub->parent;
+	struct skinny_subchannel *sub, *tmpsub;
+	int instance;
+	int reference;
+	int onlysub = 0;
+
+	instance = letohl(req->data.onhook.instance);
+	reference = letohl(req->data.onhook.reference);
+
+	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;
+	}
 
 	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);
 
@@ -4541,28 +4834,39 @@
 		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);
@@ -5151,20 +5455,16 @@
 	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)
@@ -5249,29 +5549,44 @@
 			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);
@@ -5288,6 +5603,17 @@
 	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)
@@ -5303,7 +5629,7 @@
 			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);
@@ -5418,9 +5744,13 @@
 		lineInstance = letohl(req->data.keypad.lineInstance);
 		callReference = letohl(req->data.keypad.callReference);
 
-		sub = find_subchannel_by_instance_reference(d, lineInstance, callReference);
-
-		if (sub && (sub->owner && sub->owner->_state <  AST_STATE_UP)) {
+		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) || sub->onhold)) {
 			char dgt;
 			int digit = letohl(req->data.keypad.button);
 




More information about the svn-commits mailing list