Index: channels/chan_skinny.c
===================================================================
--- channels/chan_skinny.c	(revision 73819)
+++ 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 *
@@ -170,6 +171,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;
@@ -179,14 +186,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
@@ -261,12 +268,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
@@ -553,6 +563,7 @@
 
 static const uint8_t soft_key_default_onhook[] = {
 	SOFTKEY_REDIAL,
+	SOFTKEY_NEWCALL,
 	SOFTKEY_CFWDALL,
 	SOFTKEY_CFWDBUSY,
 	SOFTKEY_GPICKUP,
@@ -608,15 +619,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[] = {
@@ -681,6 +691,7 @@
 	char promptMessage[32];
 	uint32_t lineInstance;
 	uint32_t callReference;
+	uint32_t space[3];
 };
 
 #define CLEAR_PROMPT_MESSAGE  0x0113
@@ -721,6 +732,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;
@@ -748,6 +760,7 @@
 	struct display_notify_message displaynotify;
 	struct dialed_number_message dialednumber;
 	struct soft_key_event_message softkeyeventmessage;
+	struct enbloc_call_message enbloccallmessage;
 };
 
 /* packet composition */
@@ -940,8 +953,11 @@
 	int nat;
 	int outgoing;
 	int alreadygone;
+	int blindxfer;
+	int xferor;
 
 	struct skinny_subchannel *next;
+	struct skinny_subchannel *related;
 	struct skinny_line *parent;
 };
 
@@ -991,6 +1007,7 @@
 
 	struct ast_codec_pref prefs;
 	struct skinny_subchannel *sub;
+	struct skinny_subchannel *activesub;
 	struct skinny_line *next;
 	struct skinny_device *parent;
 };
@@ -1037,6 +1054,7 @@
 	struct ast_ha *ha;
 	struct skinnysession *session;
 	struct skinny_device *next;
+	struct skinny_line *activeline;
 } *devices = NULL;
 
 struct skinny_paging_device {
@@ -1090,6 +1108,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)
 {
@@ -1589,6 +1608,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;
@@ -1596,9 +1663,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);
@@ -1680,7 +1744,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;
 
@@ -1692,14 +1756,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);
 }
 
@@ -1755,14 +1821,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_PROMPT_MESSAGE)))
 			return;
 
+		req->data.clearpromptstatus.lineInstance = instance;
+		req->data.clearpromptstatus.callReference = reference;
+
 		if (skinnydebug)
 			ast_verbose("Clearing Display\n");
 	} else {
@@ -1797,17 +1866,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);
 }
 
@@ -2385,6 +2465,9 @@
 					l->hookstate = SKINNY_ONHOOK;
 					l->nat = nat;
 
+					if (!d->lines) {
+						d->activeline = l;
+					}
 					l->next = d->lines;
 					d->lines = l;
 				}
@@ -2469,7 +2552,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;
 }
@@ -2489,7 +2572,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) {
@@ -2501,6 +2584,7 @@
 				 res = 0;
 				 break;
 			}
+
 		res = 1;
 		}
 
@@ -2508,7 +2592,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)) {
@@ -2518,7 +2602,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;
 					}
@@ -2526,7 +2610,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 {
@@ -2544,7 +2628,7 @@
 		} else if (res == 0) {
 			ast_debug(1, "Not enough digits (%s) (and no ambiguous match)...\n", d->exten);
 			memset(d->exten, 0, sizeof(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);
@@ -2554,7 +2638,7 @@
 			   ((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 : "<Unknown Caller>", c->context);
 			memset(d->exten, 0, sizeof(d->exten));
-			transmit_tone(s, SKINNY_REORDER);
+			transmit_tone(s, SKINNY_REORDER, l->instance, sub->callid);
 			/* hang out for 3 seconds to let congestion play */
 			ast_safe_sleep(c, 3000);
 			break;
@@ -2613,7 +2697,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);
@@ -2628,10 +2712,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");
@@ -2640,23 +2725,97 @@
 	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 (d->registered) {
-		if ((l->type = TYPE_LINE) && (l->hookstate == SKINNY_OFFHOOK)) {
+/*	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 (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) { 
+		/* 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;
+			l->activesub = NULL;
+			transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_OFF);
+			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);
-			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);
+				transmit_ringer_mode(s, SKINNY_RING_OFF);
+			} else {
+				transmit_ringer_mode(s, SKINNY_RING_OFF);
+				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);
-		}
+		} */
 	}
 	ast_mutex_lock(&sub->lock);
 	sub->owner = NULL;
@@ -2668,6 +2827,7 @@
 		sub->rtp = NULL;
 	}
 	ast_mutex_unlock(&sub->lock);
+	//l->activesub = NULL;
 	return 0;
 }
 
@@ -2679,6 +2839,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);
@@ -2689,14 +2858,15 @@
 		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. */
 	transmit_callinfo(s, ast->cid.cid_name, ast->cid.cid_num, ast->exten, ast->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_displaypromptstatus(s, "Connected", 0, l->instance, sub->callid);
+	l->activesub = sub;
 	return res;
 }
 
@@ -2812,7 +2982,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 */
 }
@@ -2865,6 +3035,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)
 {
@@ -2877,10 +3109,16 @@
 		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_callstate(s, l->instance, SKINNY_RINGOUT, sub->callid);
+				transmit_tone(s, SKINNY_ALERT, l->instance, sub->callid);
+				transmit_callstateonly(s, sub, SKINNY_RINGOUT);
 				transmit_dialednumber(s, ast->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, ast->exten, ast->exten, l->instance, sub->callid, 2); /* 2 = outgoing from phone */
@@ -2891,8 +3129,8 @@
 		return -1;
 	case AST_CONTROL_BUSY:
 		if (ast->_state != AST_STATE_UP) {
-			transmit_tone(s, SKINNY_BUSYTONE);
-			transmit_callstate(s, l->instance, SKINNY_BUSY, sub->callid);
+			transmit_tone(s, SKINNY_BUSYTONE, l->instance, sub->callid);
+			transmit_callstateonly(s, sub, SKINNY_BUSY);
 			sub->alreadygone = 1;
 			ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
 			break;
@@ -2900,8 +3138,8 @@
 		return -1;
 	case AST_CONTROL_CONGESTION:
 		if (ast->_state != AST_STATE_UP) {
-			transmit_tone(s, SKINNY_REORDER);
-			transmit_callstate(s, l->instance, SKINNY_CONGESTION, sub->callid);
+			transmit_tone(s, SKINNY_REORDER, l->instance, sub->callid);
+			transmit_callstateonly(s, sub, SKINNY_CONGESTION);
 			sub->alreadygone = 1;
 			ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
 			break;
@@ -2909,8 +3147,8 @@
 		return -1;
 	case AST_CONTROL_PROGRESS:
 		if ((ast->_state != AST_STATE_UP) && !sub->progress && !sub->outgoing) {
-			transmit_tone(s, SKINNY_ALERT);
-			transmit_callstate(s, l->instance, SKINNY_PROGRESS, sub->callid);
+			transmit_tone(s, SKINNY_ALERT, l->instance, 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, ast->exten, ast->exten, l->instance, sub->callid, 2); /* 2 = outgoing from phone */
 			sub->progress = 1;
@@ -2918,7 +3156,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);
@@ -2962,9 +3200,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;
@@ -3026,36 +3268,20 @@
 	struct skinny_line *l = sub->parent;
 	struct skinny_device *d = l->parent;
 	struct skinnysession *s = d->session;
-	struct skinny_req *req;
 
 	/* 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),
 		!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(0);
-	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(0);
-	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;
@@ -3066,26 +3292,104 @@
 	struct skinny_line *l = sub->parent;
 	struct skinny_device *d = l->parent;
 	struct skinnysession *s = d->session;
-	struct skinny_req *req;
 
 	/* 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);
 
-	if (!(req = req_alloc(sizeof(struct activate_call_plane_message), ACTIVATE_CALL_PLANE_MESSAGE)))
-		return 0;
+	transmit_activatecallplane(s, l);
 
-	req->data.activatecallplane.lineInstance = htolel(l->instance);
-	transmit_response(s, req);
-
 	transmit_connect(s, sub);
+	transmit_callstateonly(s, sub, SKINNY_CONNECTED);
 	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)))
@@ -3184,12 +3488,15 @@
 	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;
 
 	l = sub->parent;
+	/* sub = l->activesub; */
+	
 	if (sub->owner) {
 		if (sub->owner->_state == 0) {
 			f.frametype = AST_FRAME_DTMF_BEGIN;
@@ -3234,7 +3541,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) {
@@ -3244,11 +3551,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);
@@ -3261,15 +3578,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)) {
@@ -3305,11 +3619,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));
@@ -3327,26 +3642,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:
@@ -3376,11 +3682,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)) {
@@ -3406,7 +3713,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);
@@ -3443,6 +3750,8 @@
 		if (!l) {
 			return 0;
 		}
+		
+		d->activeline = l;
 
 		/* turn the speaker on */
 		transmit_speaker_mode(s, SKINNY_SPEAKERON);
@@ -3457,8 +3766,8 @@
 			/* 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_callstate(s, l->instance, SKINNY_CONNECTED, sub->callid);
+			transmit_tone(s, SKINNY_SILENCE, l->instance, 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);
@@ -3473,8 +3782,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 */
@@ -3504,24 +3813,39 @@
 	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);
-
-	sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
-
-	if (!sub) {
-		l = find_line_by_instance(d, d->lastlineinstance);
-		if (!l) {
+	/* 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, instance, reference);
+		if (!sub) {
+			l = find_line_by_instance(d, instance);
+			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);
 	l->hookstate = SKINNY_OFFHOOK;
 
@@ -3537,8 +3861,8 @@
 		/* 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_callstate(s, l->instance, SKINNY_CONNECTED, sub->callid);
+		transmit_tone(s, SKINNY_SILENCE, l->instance, 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);
@@ -3552,8 +3876,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 */
@@ -3573,36 +3897,59 @@
 {
 	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);
-
+	
 	if (sub->onhold) {
 		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 */
@@ -3656,7 +4003,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;
@@ -3779,7 +4127,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++;
@@ -3792,7 +4141,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++;
@@ -3810,7 +4160,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++;
@@ -3823,7 +4174,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++;
@@ -3831,7 +4183,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++;
@@ -3848,7 +4201,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++;
@@ -3864,7 +4218,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++;
@@ -3877,7 +4232,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++;
@@ -3986,7 +4342,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));
@@ -4001,6 +4357,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;
@@ -4022,6 +4428,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);
 				}
 			}
 		}
@@ -4058,6 +4465,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);
@@ -4073,8 +4482,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 {
@@ -4092,15 +4509,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)) {
@@ -4111,12 +4525,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);
@@ -4130,14 +4546,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 */
@@ -4149,29 +4566,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);
@@ -4188,30 +4596,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 */
@@ -4245,11 +4672,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);
@@ -4260,8 +4697,8 @@
 			/* 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_callstate(s, l->instance, SKINNY_CONNECTED, sub->callid);
+			transmit_tone(s, SKINNY_SILENCE, l->instance, 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);
@@ -4269,39 +4706,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);
@@ -4376,10 +4813,14 @@
 
 		lineInstance = letohl(req->data.keypad.lineInstance);
 		callReference = letohl(req->data.keypad.callReference);
+	
+		if (lineInstance) {
+			sub = find_subchannel_by_instance_reference(d, lineInstance, callReference);
+		} else {
+			sub = d->activeline->activesub;
+		}
 
-		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);
 	
@@ -4407,6 +4848,9 @@
 			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;
@@ -4429,8 +4873,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:
