Index: channels/chan_skinny.c
===================================================================
--- channels/chan_skinny.c	(revision 382285)
+++ channels/chan_skinny.c	(working copy)
@@ -919,6 +919,7 @@
 	SOFTKEY_NEWCALL,
 	SOFTKEY_CFWDALL,
 	SOFTKEY_CFWDBUSY,
+	SOFTKEY_CFWDNOANSWER,
 	SOFTKEY_DND,
 	SOFTKEY_GPICKUP,
 	/*SOFTKEY_CONFRN,*/
@@ -931,6 +932,7 @@
 	SOFTKEY_PARK,
 	SOFTKEY_CFWDALL,
 	SOFTKEY_CFWDBUSY,
+	SOFTKEY_CFWDNOANSWER,
 };
 
 static const uint8_t soft_key_default_onhold[] = {
@@ -951,6 +953,7 @@
 	SOFTKEY_ENDCALL,
 	SOFTKEY_CFWDALL,
 	SOFTKEY_CFWDBUSY,
+	SOFTKEY_CFWDNOANSWER,
 	SOFTKEY_GPICKUP,
 };
 
@@ -961,6 +964,7 @@
 	SOFTKEY_PARK,
 	SOFTKEY_CFWDALL,
 	SOFTKEY_CFWDBUSY,
+	SOFTKEY_CFWDNOANSWER,
 };
 
 static const uint8_t soft_key_default_dadfd[] = {
@@ -1382,6 +1386,7 @@
 	int aa_beep;
 	int aa_mute;
 	int dialer_sched;
+	int cfwd_sched;
 	char *origtonum;
 	char *origtoname;
 
@@ -4905,6 +4910,17 @@
 	return 0;
 }
 
+static int skinny_cfwd_cb(const void *data)
+{
+	struct skinny_subchannel *sub = (struct skinny_subchannel *)data;
+	struct skinny_line *l = sub->line;
+	sub->cfwd_sched = 0;
+	SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - CFWDNOANS to %s.\n", sub->callid, l->call_forward_noanswer);
+	ast_channel_call_forward_set(sub->owner, l->call_forward_noanswer);
+	ast_queue_control(sub->owner, AST_CONTROL_REDIRECTING);
+	return 0;
+}
+
 static int skinny_call(struct ast_channel *ast, const char *dest, int timeout)
 {
 	int res = 0;
@@ -5408,6 +5424,7 @@
 			sub->calldirection = direction;
 			sub->aa_sched = 0;
 			sub->dialer_sched = 0;
+			sub->cfwd_sched = 0;
 
 			if (subline) {
 				sub->subline = subline;
@@ -5457,13 +5474,17 @@
 		ast_channel_named_callgroups_set(tmp, l->named_callgroups);
 		ast_channel_named_pickupgroups_set(tmp, l->named_pickupgroups);
 
-		/* XXX Need to figure out how to handle CFwdNoAnswer */
 		if (l->cfwdtype & SKINNY_CFWD_ALL) {
+			SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - CFWDALL to %s.\n", sub->callid, l->call_forward_all);
 			ast_channel_call_forward_set(tmp, l->call_forward_all);
 		} else if (l->cfwdtype & SKINNY_CFWD_BUSY) {
 			if (get_devicestate(l) != AST_DEVICE_NOT_INUSE) {
+				SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - CFWDBUSY to %s.\n", sub->callid, l->call_forward_busy);
 				ast_channel_call_forward_set(tmp, l->call_forward_busy);
 			}
+		} else if (l->cfwdtype & SKINNY_CFWD_NOANSWER) {
+			SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - CFWDNOANS Scheduling for %d seconds.\n", sub->callid, 20);
+			sub->cfwd_sched = skinny_sched_add(20000, skinny_cfwd_cb, sub);
 		}
 
 		if (subline) {
@@ -5563,6 +5584,19 @@
 		sub->aa_mute = 0;
 	}
 
+	if (sub->cfwd_sched) {
+		if (state == SUBSTATE_CONNECTED) {
+			if (skinny_sched_del(sub->cfwd_sched, sub)) {
+				SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - trying to change state from %s to %s, but already forwarded because no answer.\n",
+					sub->callid, substate2str(sub->substate), substate2str(actualstate));
+				return;
+			}
+			sub->cfwd_sched = 0;
+		} else if (state == SUBSTATE_ONHOOK) {
+			skinny_sched_del(sub->cfwd_sched, sub);
+		}
+	}
+
 	if ((state == SUBSTATE_RINGIN) && ((d->hookstate == SKINNY_OFFHOOK) || (AST_LIST_NEXT(AST_LIST_FIRST(&l->sub), list)))) {
 		actualstate = SUBSTATE_CALLWAIT;
 	}
@@ -5692,7 +5726,6 @@
 		sub->callid, substate2str(sub->substate), substate2str(actualstate));
 
 	if (actualstate == sub->substate) {
-		transmit_callstate(d, l->instance, sub->callid, SKINNY_HOLD);
 		return;
 	}
 
@@ -6034,7 +6067,7 @@
 		struct skinny_device *d = l->device;
 
 		// FIXME: needs some love and remove sleeps
-		SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - Set callforward to %s\n", sub->callid, exten);
+		SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - Set callforward(%d) to %s\n", sub->callid, l->getforward, exten);
 		set_callforwards(l, sub->exten, l->getforward);
 		transmit_start_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
 		transmit_lamp_indication(d, STIMULUS_FORWARDALL, 1, SKINNY_LAMP_ON);
@@ -7100,7 +7133,6 @@
 		SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_CFWDNOANSWER from %s, inst %d, callref %d\n",
 			d->name, instance, callreference);
 
-#if 0 /* Not sure how to handle this yet */
 		if (!sub || !sub->owner) {
 			c = skinny_new(l, NULL, AST_STATE_DOWN, NULL, SKINNY_OUTGOING);
 		} else {
@@ -7110,11 +7142,10 @@
 		if (!c) {
 			ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
 		} else {
-			sub = c->tech_pvt;
+			sub = ast_channel_tech_pvt(c);
 			l->activesub = sub;
 			handle_callforward_button(sub, SKINNY_CFWD_NOANSWER);
 		}
-#endif
 		break;
 	case SOFTKEY_BKSPC:
 		SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_BKSPC from %s, inst %d, callref %d\n",
