Index: channels/chan_skinny.c
===================================================================
--- channels/chan_skinny.c	(revision 89394)
+++ channels/chan_skinny.c	(working copy)
@@ -69,6 +69,7 @@
 #include "asterisk/abstract_jb.h"
 #include "asterisk/threadstorage.h"
 #include "asterisk/devicestate.h"
+#include "asterisk/indications.h"
 
 /*************************************
  * Skinny/Asterisk Protocol Settings *
@@ -152,6 +153,9 @@
 AST_THREADSTORAGE(device2str_threadbuf);
 #define DEVICE2STR_BUFSIZE   15
 
+AST_THREADSTORAGE(tone2str_threadbuf);
+#define TONE2STR_BUFSIZE   15
+
 AST_THREADSTORAGE(control2str_threadbuf);
 #define CONTROL2STR_BUFSIZE   100
 
@@ -1189,6 +1193,8 @@
 	int lastlineinstance;
 	int lastcallreference;
 	int capability;
+	int earlyrtp;
+	struct ind_tone_zone *tonezone;
 	struct sockaddr_in addr;
 	struct in_addr ourip;
 	struct skinny_line *lines;
@@ -1830,6 +1836,33 @@
 	transmit_response(s, req);
 }
 
+static char *tone2str(int type)
+{
+	char *tmp;
+
+	switch (type) {
+	case SKINNY_SILENCE:
+		return "silence";
+	case SKINNY_DIALTONE:
+		return "dial";
+	case SKINNY_BUSYTONE:
+		return "busy";
+	case SKINNY_ALERT:
+		return "ring";
+	case SKINNY_REORDER:
+		return "congestion";
+	case SKINNY_CALLWAITTONE:
+		return "callwaiting";
+	case SKINNY_NOTONE:
+		return "notone";
+	default:
+		if (!(tmp = ast_threadstorage_get(&tone2str_threadbuf, TONE2STR_BUFSIZE)))
+			return "unknown";
+		snprintf(tmp, TONE2STR_BUFSIZE, "UNKNOWN-%d", type);
+		return tmp;
+	}
+}
+
 static void transmit_tone(struct skinnysession *s, int tone, int instance, int reference)
 {
 	struct skinny_req *req;
@@ -1838,7 +1871,7 @@
 		/* This is bad, mmm'kay? */
 		return;
 	}
-
+	
 	if (tone > 0) {
 		if (!(req = req_alloc(sizeof(struct start_tone_message), START_TONE_MESSAGE)))
 			return;
@@ -1858,6 +1891,26 @@
 	transmit_response(s, req);
 }
 
+static void skinny_play_tone(struct skinny_subchannel *sub, int tone) //, int instance, int reference)
+{
+	const struct ind_tone_zone_sound *ts = NULL;
+
+	if (sub->rtp) {		/* We have RTP, so we need to indicate inband */
+		if (tone == SKINNY_SILENCE || tone == SKINNY_NOTONE) {
+			ast_playtones_stop(sub->owner);
+		} else {
+			ts = ast_get_indication_tone(sub->parent->parent->tonezone, tone2str(tone));
+			ast_playtones_start(sub->owner,0,ts->data, 1);
+		}
+		if (skinnydebug)
+			ast_verbose("Playing tone '%s' inband to '%s'.\n", tone2str(tone), sub->parent->parent->name);
+	} else {			/* No RTP so have the device play the tones */
+		if (skinnydebug)
+			ast_verbose("Transmitting tone '%s' to '%s'.\n", tone2str(tone), sub->parent->parent->name);
+		transmit_tone(sub->parent->parent->session, tone, sub->parent->instance, sub->callid);
+	}
+}
+
 static void transmit_selectsoftkeys(struct skinnysession *s, int instance, int callid, int softkey)
 {
 	struct skinny_req *req;
@@ -2762,6 +2815,8 @@
 		d->lastlineinstance = 1;
 		d->capability = default_capability;
 		d->prefs = default_prefs;
+		d->earlyrtp = 1;
+		d->tonezone = ast_get_indication_zone("-1");
 		if (!ast_strlen_zero(vmexten))
 			ast_copy_string(device_vmexten, vmexten, sizeof(device_vmexten));
 		while(v) {
@@ -2788,6 +2843,10 @@
 				ast_parse_allow_disallow(&d->prefs, &d->capability, v->value, 0);
 			} else if (!strcasecmp(v->name, "version")) {
 				ast_copy_string(d->version_id, v->value, sizeof(d->version_id));
+                        } else if (!strcasecmp(v->name, "earlyrtp")) {
+                                d->earlyrtp = ast_true(v->value);
+                        } else if (!strcasecmp(v->name, "tonezone")) {
+                                d->tonezone = ast_get_indication_zone(v->value);
 			} else if (!strcasecmp(v->name, "canreinvite")) {
 				canreinvite = ast_true(v->value);
 			} else if (!strcasecmp(v->name, "nat")) {
@@ -2998,7 +3057,6 @@
 	struct skinny_subchannel *sub = c->tech_pvt;
 	struct skinny_line *l = sub->parent;
 	struct skinny_device *d = l->parent;
-	struct skinnysession *s = d->session;
 	int res = 0;
 
 	ast_copy_string(l->lastnumberdialed, c->exten, sizeof(l->lastnumberdialed));
@@ -3007,10 +3065,13 @@
 		l->hidecallerid ? "" : l->cid_name,
 		c->cid.cid_ani ? NULL : l->cid_num);
 	ast_setstate(c, AST_STATE_RING);
+	if (d->earlyrtp && !sub->rtp) {
+		start_rtp(sub);
+	}
 	res = ast_pbx_run(c);
 	if (res) {
 		ast_log(LOG_WARNING, "PBX exited non-zero\n");
-		transmit_tone(s, SKINNY_REORDER, l->instance, sub->callid);
+		skinny_play_tone(sub, SKINNY_REORDER);
 	}
 	return NULL;
 }
@@ -3021,7 +3082,6 @@
 	struct skinny_subchannel *sub = c->tech_pvt;
 	struct skinny_line *l = sub->parent;
 	struct skinny_device *d = l->parent;
-	struct skinnysession *s = d->session;
 	int len = 0;
 	int timeout = firstdigittimeout;
 	int res = 0;
@@ -3048,7 +3108,7 @@
 		len = strlen(d->exten);
 
 		if (!ast_ignore_pattern(c->context, d->exten)) {
-			transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
+			skinny_play_tone(sub, SKINNY_SILENCE);
 		}
 		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)) {
@@ -3057,7 +3117,7 @@
 					ast_copy_string(l->call_forward, d->exten, sizeof(l->call_forward));
 					ast_verb(3, "Setting call forward to '%s' on channel %s\n",
 							l->call_forward, c->name);
-					transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid);
+					skinny_play_tone(sub, SKINNY_DIALTONE);
 					if (res) {
 						break;
 					}
@@ -3065,7 +3125,7 @@
 					ast_indicate(c, -1);
  					ast_safe_sleep(c, 1000);
 					memset(d->exten, 0, sizeof(d->exten));
-					transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid);
+					skinny_play_tone(sub, SKINNY_DIALTONE);
 					len = 0;
 					getforward = 0;
 				} else {
@@ -3083,7 +3143,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, l->instance, sub->callid);
+			skinny_play_tone(sub, SKINNY_REORDER);
 			if (sub->owner && sub->owner->_state != AST_STATE_UP) {
 				ast_indicate(c, -1);
 				ast_hangup(c);
@@ -3093,7 +3153,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, l->instance, sub->callid);
+			skinny_play_tone(sub, SKINNY_REORDER);
 			/* hang out for 3 seconds to let congestion play */
 			ast_safe_sleep(c, 3000);
 			break;
@@ -3189,7 +3249,7 @@
 			transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_OFF);
 			transmit_speaker_mode(s, SKINNY_SPEAKEROFF);
 		} else if ((l->type = TYPE_LINE) && (l->hookstate == SKINNY_ONHOOK)) {
-			transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
+			skinny_play_tone(sub, SKINNY_SILENCE);
 			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);
@@ -3230,7 +3290,7 @@
 		ast_setstate(ast, AST_STATE_UP);
 	}
 
-	transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
+	skinny_play_tone(sub, SKINNY_SILENCE);
 	/* order matters here...
 	   for some reason, transmit_callinfo must be before transmit_callstate,
 	   or you won't get keypad messages in some situations. */
@@ -3353,7 +3413,7 @@
 	int tmp;
 	/* not right */
 	sprintf(tmp, "%d", digit);
-	transmit_tone(d->session, digit, l->instance, sub->callid);
+	skinny_play_tone(sub, digit);
 #endif
 	return -1; /* Stop inband indications */
 }
@@ -3428,7 +3488,7 @@
 	case AST_CONTROL_RINGING:
 		if (ast->_state != AST_STATE_UP) {
 			if (!sub->progress) {
-				transmit_tone(s, SKINNY_ALERT, l->instance, sub->callid);
+				skinny_play_tone(sub, SKINNY_ALERT);
 				transmit_callstate(s, l->instance, SKINNY_RINGOUT, sub->callid);
 				transmit_dialednumber(s, exten, l->instance, sub->callid);
 				transmit_displaypromptstatus(s, "Ring Out", 0, l->instance, sub->callid);
@@ -3440,7 +3500,7 @@
 		return -1;
 	case AST_CONTROL_BUSY:
 		if (ast->_state != AST_STATE_UP) {
-			transmit_tone(s, SKINNY_BUSYTONE, l->instance, sub->callid);
+			skinny_play_tone(sub, SKINNY_BUSYTONE);
 			transmit_callstate(s, l->instance, SKINNY_BUSY, sub->callid);
 			sub->alreadygone = 1;
 			ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
@@ -3449,7 +3509,7 @@
 		return -1;
 	case AST_CONTROL_CONGESTION:
 		if (ast->_state != AST_STATE_UP) {
-			transmit_tone(s, SKINNY_REORDER, l->instance, sub->callid);
+			skinny_play_tone(sub, SKINNY_REORDER);
 			transmit_callstate(s, l->instance, SKINNY_CONGESTION, sub->callid);
 			sub->alreadygone = 1;
 			ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
@@ -3458,7 +3518,7 @@
 		return -1;
 	case AST_CONTROL_PROGRESS:
 		if ((ast->_state != AST_STATE_UP) && !sub->progress && !sub->outgoing) {
-			transmit_tone(s, SKINNY_ALERT, l->instance, sub->callid);
+			skinny_play_tone(sub, SKINNY_ALERT);
 			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, exten, exten, l->instance, sub->callid, 2); /* 2 = outgoing from phone */
@@ -3466,8 +3526,8 @@
 			break;
 		}
 		return -1;
-	case -1:
-		transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
+	case -1:  /* STOP_TONE */
+		skinny_play_tone(sub, SKINNY_SILENCE);
 		break;
 	case AST_CONTROL_HOLD:
 		ast_moh_start(ast, data, l->mohinterpret);
@@ -3830,11 +3890,11 @@
 			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);
+			skinny_play_tone(sub, SKINNY_DIALTONE);
 			transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_RINGOUT);
 
 			if (!ast_ignore_pattern(c->context, l->lastnumberdialed)) {
-				transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
+				skinny_play_tone(sub, SKINNY_SILENCE);
 			}
 			ast_copy_string(c->exten, l->lastnumberdialed, sizeof(c->exten));
 			if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
@@ -3871,11 +3931,11 @@
 			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);
+			skinny_play_tone(sub, SKINNY_DIALTONE);
 			transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_RINGOUT);
 
 			if (!ast_ignore_pattern(c->context, sd->exten)) {
-				transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
+				skinny_play_tone(sub, SKINNY_SILENCE);
 			}
 			if (ast_exists_extension(c, c->context, sd->exten, 1, l->cid_num)) {
 				ast_copy_string(c->exten, sd->exten, sizeof(c->exten));
@@ -3941,11 +4001,11 @@
 				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);
+			skinny_play_tone(sub, SKINNY_DIALTONE);
 			transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_RINGOUT);
 
 			if (!ast_ignore_pattern(c->context, vmexten)) {
-				transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
+				skinny_play_tone(sub, SKINNY_SILENCE);
 			}
 
 			if (ast_exists_extension(c, c->context, l->vmexten, 1, l->cid_num)) {
@@ -4019,7 +4079,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, l->instance, sub->callid);
+			skinny_play_tone(sub, SKINNY_SILENCE);
 			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);
@@ -4036,7 +4096,7 @@
 					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);
+					skinny_play_tone(sub, SKINNY_DIALTONE);
 					transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_OFFHOOK);
 
 					/* start the switch thread */
@@ -4099,7 +4159,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, l->instance, sub->callid);
+		skinny_play_tone(sub, SKINNY_SILENCE);
 		transmit_callstate(s, l->instance, SKINNY_CONNECTED, sub->callid);
 		transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_CONNECTED);
 		start_rtp(sub);
@@ -4115,7 +4175,10 @@
 				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 (d->earlyrtp && !sub->rtp) {
+					start_rtp(sub);
+				}
+				skinny_play_tone(sub, SKINNY_DIALTONE);
 				transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_OFFHOOK);
 
 				/* start the switch thread */
@@ -4600,10 +4663,10 @@
 		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);
+		skinny_play_tone(sub, SKINNY_DIALTONE);
 
 		if (!ast_ignore_pattern(c->context, req->data.enbloccallmessage.calledParty)) {
-			transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
+			skinny_play_tone(sub, SKINNY_SILENCE);
 		}
 		ast_copy_string(c->exten, req->data.enbloccallmessage.calledParty, sizeof(c->exten));
 		if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
@@ -4717,11 +4780,11 @@
 			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);
+			skinny_play_tone(sub, SKINNY_DIALTONE);
 			transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_RINGOUT);
 
 			if (!ast_ignore_pattern(c->context, l->lastnumberdialed)) {
-				transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
+				skinny_play_tone(sub, SKINNY_SILENCE);
 			}
 			ast_copy_string(c->exten, l->lastnumberdialed, sizeof(c->exten));
 			if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
@@ -4758,7 +4821,7 @@
 			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);
+			skinny_play_tone(sub, SKINNY_DIALTONE);
 			transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_OFFHOOK);
 
 			/* start the switch thread */
@@ -4877,7 +4940,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, l->instance, sub->callid);
+			skinny_play_tone(sub, SKINNY_SILENCE);
 			transmit_callstate(s, l->instance, SKINNY_CONNECTED, sub->callid);
 			transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_CONNECTED);
 			start_rtp(sub);
Index: configs/skinny.conf.sample
===================================================================
--- configs/skinny.conf.sample	(revision 89394)
+++ configs/skinny.conf.sample	(working copy)
@@ -55,6 +55,14 @@
 ;jblog = no                  ; Enables jitterbuffer frame logging. Defaults to "no".
 ;-----------------------------------------------------------------------------------
 
+;----------------------------------- DEVICE OPTIONS --------------------------------
+;earlyrtp=1                  ; whether audio signalling should be provided by asterisk
+                             ; (earlyrtp=1) or device generated (earlyrtp=0). 
+                             ; defaults to earlyrtp=1
+;tonezone=                   ; country code for indication tones. Really need earlyrtp
+                             ; set as indications are only used after RTP started. 
+                             ; defaults to unset
+;-----------------------------------------------------------------------------------
 
 ; Typical config for 12SP+
 ;[florian]
