--- chan_skinny.c	2006-07-25 07:45:58.000000000 +1000
+++ chan_skinny.c.hold4	2006-07-27 20:07:12.000000000 +1000
@@ -888,6 +888,7 @@
 	/* time_t lastouttime; */			/* Unused */
 	int progress;
 	int ringing;
+	int onhold;
 	/* int lastout; */				/* Unused */
 	int cxmode;
 	int nat;
@@ -2542,6 +2543,7 @@
 			sub->cxmode = SKINNY_CX_INACTIVE;
 			sub->nat = l->nat;
 			sub->parent = l;
+			sub->onhold = 0;
 
 			sub->next = l->sub;
 			l->sub = sub;
@@ -2718,6 +2720,77 @@
 	return 1;
 }
 
+static int skinny_hold(struct skinny_subchannel *sub)
+{
+	struct skinny_line *l = sub->parent;
+	struct skinnysession *s = l->parent->session;
+	skinny_req *req;
+	
+	if (skinnydebug)
+		ast_verbose("Putting on Hold(%d)\n", l->instance);
+	ast_queue_control(sub->owner, AST_CONTROL_HOLD);
+	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 = 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 = 0;
+	req->data.stopmedia.passThruPartyId = htolel(sub->callid);
+	transmit_response(s, req);
+	transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK);
+	sub->onhold = 1;
+	return 1;
+}	
+
+static int skinny_unhold(struct skinny_subchannel *sub)
+{
+	struct skinny_line *l = sub->parent;
+	struct skinny_device *d = l->parent;
+	struct skinnysession *s = d->session;
+	skinny_req *req;
+
+	if (skinnydebug)
+		ast_verbose("Taking off Hold(%d)\n", l->instance);
+	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);
+	if (!(req = req_alloc(sizeof(struct open_receive_channel_message), OPEN_RECEIVE_CHANNEL_MESSAGE)))
+		return 0;
+	req->data.openreceivechannel.conferenceId = 0;
+	req->data.openreceivechannel.partyId = htolel(sub->callid);
+	req->data.openreceivechannel.packets = htolel(20);
+	req->data.openreceivechannel.capability = htolel(convert_cap(l->capability));
+	req->data.openreceivechannel.echo = 0;
+	req->data.openreceivechannel.bitrate = 0;
+	transmit_response(s, req);
+	struct sockaddr_in us;
+	ast_rtp_get_us(sub->rtp, &us);
+	if (!(req = req_alloc(sizeof(start_media_transmission_message), START_MEDIA_TRANSMISSION_MESSAGE)))
+		return -1;
+	req->data.startmedia.conferenceId = 0;
+	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));
+	req->data.startmedia.packetSize = htolel(20);
+	req->data.startmedia.payloadType = htolel(convert_cap(l->capability));
+	req->data.startmedia.qualifier.precedence = htolel(127);
+	req->data.startmedia.qualifier.vad = 0;
+	req->data.startmedia.qualifier.packets = 0;
+	req->data.startmedia.qualifier.bitRate = 0;
+	transmit_response(s, req);
+	transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
+	sub->onhold = 0;
+	return 1;
+}
+	
 static int handle_stimulus_message(skinny_req *req, struct skinnysession *s)
 {
 	struct skinny_device *d = s->device;
@@ -2816,9 +2889,13 @@
 #endif
 		break;
 	case STIMULUS_HOLD:
-		/* start moh? set RTP to 0.0.0.0? */
 		if (skinnydebug)
 			ast_verbose("Received Stimulus: Hold(%d)\n", instance);
+		if (sub->onhold) {
+			skinny_unhold(sub);
+		} else {
+			skinny_hold(sub);
+		}
 		break;
 	case STIMULUS_TRANSFER:
 		if (skinnydebug)
@@ -2961,6 +3038,12 @@
 		l = sub->parent;
 	}
 
+	if (sub && sub->onhold) {
+		transmit_ringer_mode(s, SKINNY_RING_OFF);
+		l->hookstate = SKINNY_OFFHOOK;
+		return 1;
+	}
+
 	transmit_ringer_mode(s, SKINNY_RING_OFF);
 	transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
 	l->hookstate = SKINNY_OFFHOOK;
@@ -3016,8 +3099,12 @@
 
 	if (!sub) {
 		return 0;
-	} else {
-		l = sub->parent;
+	}
+	l = sub->parent;
+
+	if (sub->onhold) {
+		l->hookstate = SKINNY_ONHOOK;
+		return 0;
 	}
 
 	if (l->hookstate == SKINNY_ONHOOK) {
@@ -3484,9 +3571,13 @@
 		}
 		break;
 	case SOFTKEY_HOLD:
-		/* start moh? set RTP to 0.0.0.0? */
 		if (skinnydebug)
 			ast_verbose("Received Softkey Event: Hold(%d)\n", instance);
+		if (sub->onhold) {
+			skinny_unhold(sub);
+		} else {
+			skinny_hold(sub);
+		}
 		break;
 	case SOFTKEY_TRNSFER:
 		if (skinnydebug)
