[asterisk-commits] branch north/chan_skinny-fixup r33517 - in /team/north/chan_skinny-fixup: cha...

asterisk-commits at lists.digium.com asterisk-commits at lists.digium.com
Sun Jun 11 13:58:00 MST 2006


Author: north
Date: Sun Jun 11 15:57:59 2006
New Revision: 33517

URL: http://svn.digium.com/view/asterisk?rev=33517&view=rev
Log:
config file updates

many many changes..
re-added support for subchannels
general restructuring
new items in some of the structs
added "skinny reset <device>" support

a bunch more that I've since forgot

Modified:
    team/north/chan_skinny-fixup/channels/chan_skinny.c
    team/north/chan_skinny-fixup/configs/skinny.conf.sample

Modified: team/north/chan_skinny-fixup/channels/chan_skinny.c
URL: http://svn.digium.com/view/asterisk/team/north/chan_skinny-fixup/channels/chan_skinny.c?rev=33517&r1=33516&r2=33517&view=diff
==============================================================================
--- team/north/chan_skinny-fixup/channels/chan_skinny.c (original)
+++ team/north/chan_skinny-fixup/channels/chan_skinny.c Sun Jun 11 15:57:59 2006
@@ -70,6 +70,7 @@
 #include "asterisk/utils.h"
 #include "asterisk/dsp.h"
 #include "asterisk/stringfields.h"
+#include "asterisk/astobj.h"
 #include "asterisk/abstract_jb.h"
 
 /*************************************
@@ -145,16 +146,28 @@
 #define KEYPAD_BUTTON_MESSAGE 0x0003
 typedef struct keypad_button_message {
 	int button;
+	int lineInstance;
+	int callReference;
 } keypad_button_message;
 
 #define STIMULUS_MESSAGE 0x0005
 typedef struct stimulus_message {
 	int stimulus;
 	int stimulusInstance;
+	int unknown1;
 } stimulus_message;
 
 #define OFFHOOK_MESSAGE 0x0006
+typedef struct offhook_message {
+	int unknown1;
+	int unknown2;
+} offhook_message;
+
 #define ONHOOK_MESSAGE 0x0007
+typedef struct onhook_message {
+	int unknown1;
+	int unknown2;
+} onhook_message;
 
 #define CAPABILITIES_RES_MESSAGE 0x0010
 typedef struct station_capabilities {
@@ -187,8 +200,6 @@
 #define SERVER_REQUEST_MESSAGE 0x0012
 
 #define ALARM_MESSAGE 0x0020
-/* XXX We never actually use this.  Maybe in the future we can.
-   I only added it for testing. */
 typedef struct alarm_message {
 	uint32_t alarmSeverity;
 	char displayMessage[80];
@@ -237,6 +248,8 @@
 #define SET_RINGER_MESSAGE 0x0085
 typedef struct set_ringer_message {
 	int ringerMode;
+	int unknown1; /* See notes in transmit_ringer_mode */
+	int unknown2;
 } set_ringer_message;
 
 #define SET_LAMP_MESSAGE 0x0086
@@ -408,6 +421,11 @@
 	int serverListenPort[5];
 	int serverIpAddr[5];
 } server_res_message;
+
+#define RESET_MESSAGE 0x009F
+typedef struct reset_message {
+	uint32_t resetType;
+} reset_message;
 
 #define KEEP_ALIVE_ACK_MESSAGE 0x0100
 
@@ -661,6 +679,7 @@
 	soft_key_set_res_message softkeysets;
 	soft_key_template_res_message softkeytemplate;
 	server_res_message serverres;
+	reset_message reset;
 	set_lamp_message setlamp;
 	set_ringer_message setringer;
 	call_state_message callstate;
@@ -668,6 +687,8 @@
 	select_soft_keys_message selectsoftkey;
 	activate_call_plane_message activatecallplane;
 	stimulus_message stimulus;
+	offhook_message offhook;
+	onhook_message onhook;
 	set_speaker_message setspeaker;
 	call_info_message callinfo;
 	start_media_transmission_message startmedia;
@@ -852,6 +873,25 @@
 
 /* How long to wait for an extra digit, if there is an ambiguous match */
 static int matchdigittimeout = 3000;
+
+struct skinny_subchannel {
+	ast_mutex_t lock;
+	struct ast_channel *owner;
+	struct ast_rtp *rtp;
+	struct ast_rtp *vrtp;
+	unsigned int callid;
+	/* time_t lastouttime; */			/* Unused */
+	int progress;
+	int ringing;
+	/* int lastout; */				/* Unused */
+	int cxmode;
+	int nat;
+	int outgoing;
+	int alreadygone;
+
+	struct skinny_subchannel *next;
+	struct skinny_line *parent;
+};
 
 struct skinny_line {
 	ast_mutex_t lock;
@@ -888,21 +928,12 @@
 	int capability;
 	int nonCodecCapability;
 	int onhooktime;
-	int msgstate;		/* voicemail message state */
+	int msgstate;					/* voicemail message state */
 	int immediate;
 	int hookstate;
-	int progress;
-	unsigned int callid;
-	struct ast_channel *owner;
-	struct ast_rtp *rtp;
-	time_t lastouttime;
-	int ringing;
-	int lastout;
-	int cxmode;
 	int nat;
-	int outgoing;
-	int alreadygone;
-
+
+	struct skinny_subchannel *sub;
 	struct skinny_line *next;
 	struct skinny_device *parent;
 };
@@ -932,7 +963,8 @@
 	char version_id[16];
 	int type;
 	int registered;
-//	char model[10];
+	int lastlineinstance;
+	int lastcallreference;
 	struct sockaddr_in addr;
 	struct in_addr ourip;
 	struct skinny_line *lines;
@@ -1170,6 +1202,54 @@
 	return NULL;
 }
 
+/* It's quicker/easier to find the subchannel when we know the instance number too */
+static struct skinny_subchannel *find_subchannel_by_instance_reference(struct skinny_device *d, int instance, int reference)
+{
+	struct skinny_line *l = find_line_by_instance(d, instance);
+	struct skinny_subchannel *sub;
+
+	if (!l) {
+		return NULL;
+	}
+
+	for (sub = l->sub; sub; sub = sub->next) {
+		if (sub->callid == reference)
+			break;
+	}
+
+	if (!sub) {
+		ast_log(LOG_WARNING, "Could not find subchannel with reference '%d' on '%s'\n", reference, d->name);
+	}
+	return sub;
+}
+
+/* Find the subchannel when we only have the callid - this shouldn't happen often */
+static struct skinny_subchannel *find_subchannel_by_reference(struct skinny_device *d, int reference)
+{
+	struct skinny_line *l;
+	struct skinny_subchannel *sub = NULL;
+
+	ast_log(LOG_WARNING, "find_subchannel_by_reference: %d\n", reference);
+
+	for (l = d->lines; l; l = l->next) {
+		for (sub = l->sub; sub; sub = sub->next) {
+			if (sub->callid == reference)
+				break;
+		}
+		if (sub)
+			break;
+	}
+
+	if (!l) {
+		ast_log(LOG_WARNING, "Could not find any lines that contained a subchannel with reference '%d' on device '%s'\n", reference, d->name);
+	} else {
+		if (!sub) {
+			ast_log(LOG_WARNING, "Could not find subchannel with reference '%d' on '%s@%s'\n", reference, l->name, d->name);
+		}
+	}
+	return sub;
+}
+
 static struct skinny_speeddial *find_speeddial_by_instance(struct skinny_device *d, int instance)
 {
 	struct skinny_speeddial *sd;
@@ -1249,21 +1329,21 @@
 		if (!(req = req_alloc(sizeof(struct activate_call_plane_message), ACTIVATE_CALL_PLANE_MESSAGE)))
 			return;
 
-		req->data.activatecallplane.lineInstance = 0;
+		req->data.activatecallplane.lineInstance = htolel(instance);
 		transmit_response(s, req);
 
 		if (!(req = req_alloc(sizeof(struct close_receive_channel_message), CLOSE_RECEIVE_CHANNEL_MESSAGE)))
 			return;
 
 		req->data.closereceivechannel.conferenceId = 0;
-		req->data.closereceivechannel.partyId = 0;
+		req->data.closereceivechannel.partyId = htolel(callid);
 		transmit_response(s, req);
 
 		if (!(req = req_alloc(sizeof(struct stop_media_transmission_message), STOP_MEDIA_TRANSMISSION_MESSAGE)))
 			return;
 
 		req->data.stopmedia.conferenceId = 0;
-		req->data.stopmedia.passThruPartyId = 0;
+		req->data.stopmedia.passThruPartyId = htolel(callid);
 		transmit_response(s, req);
 	}
 }
@@ -1293,16 +1373,16 @@
 	transmit_response(s, req);
 }
 
-static void transmit_connect(struct skinnysession *s)
+static void transmit_connect(struct skinnysession *s, struct skinny_subchannel *sub)
 {
 	skinny_req *req;
-	struct skinny_line *l = s->device->lines;
+	struct skinny_line *l = sub->parent;
 
 	if (!(req = req_alloc(sizeof(struct open_receive_channel_message), OPEN_RECEIVE_CHANNEL_MESSAGE)))
 		return;
 
 	req->data.openreceivechannel.conferenceId = 0;
-	req->data.openreceivechannel.partyId = 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;
@@ -1313,10 +1393,10 @@
 static void transmit_tone(struct skinnysession *s, int tone)
 {
 	skinny_req *req;
+	struct skinny_device *d = s->device;
 
 	if (tone == SKINNY_NOTONE) {
-		if (skinnydebug)
-			ast_verbose("I almost sent a SKINNY_NOTONE to device '%s'.  I think this causes some devices to reset.\n", s->device->name);
+		/* This is bad, mmm'kay? */
 		return;
 	}
 
@@ -1330,8 +1410,6 @@
 
 	if (tone > 0) {
 		req->data.starttone.tone = htolel(tone);
-	} else {
-//		req->len = htolel(4);
 	}
 	transmit_response(s, req);
 }
@@ -1373,10 +1451,18 @@
 	if (!(req = req_alloc(sizeof(struct set_ringer_message), SET_RINGER_MESSAGE)))
 		return;
 
-	/* XXX I don't know why, but my phone reboots unless I set this. */
-	req->len = htolel(0);
-
 	req->data.setringer.ringerMode = htolel(mode);
+	/* XXX okay, I don't quite know what this is, but here's what happens (on a 7960).
+	   Note: The phone will always show as ringing on the display.
+
+	   1: phone will audibly ring over and over
+	   2: phone will audibly ring only once
+	   any other value, will NOT cause the phone to audibly ring
+	*/
+	req->data.setringer.unknown1 = htolel(1);
+	/* XXX the value here doesn't seem to change anything.  Must be higher than 0.
+	   Perhaps a packet capture can shed some light on this. */
+	req->data.setringer.unknown2 = htolel(1);
 	transmit_response(s, req);
 }
 
@@ -1459,7 +1545,8 @@
 {
 	int new;
 	int old;
-	struct skinny_line *l = s->device->lines;
+	struct skinny_device *d = s->device;
+	struct skinny_line *l = d->lines;
 
 	transmit_displaymessage(s, NULL);
 
@@ -1467,10 +1554,10 @@
 	while (l) {
 		if (has_voicemail(l)) {
 			if (skinnydebug)
-				ast_verbose("Checking for voicemail Skinny %s@%s\n", l->name, l->parent->name);
+				ast_verbose("Checking for voicemail Skinny %s@%s\n", l->name, d->name);
 			ast_app_inboxcount(l->mailbox, &new, &old);
 			if (skinnydebug)
-				ast_verbose("Skinny %s@%s has voicemail!\n", l->name, l->parent->name);
+				ast_verbose("Skinny %s@%s has voicemail!\n", l->name, d->name);
 			transmit_lamp_indication(s, STIMULUS_VOICEMAIL, l->instance, l->mwiblink?SKINNY_LAMP_BLINK:SKINNY_LAMP_ON);
 		} else {
 			transmit_lamp_indication(s, STIMULUS_VOICEMAIL, l->instance, SKINNY_LAMP_OFF);
@@ -1485,25 +1572,30 @@
 /* Yes, it can.  Currently 7985 and Cisco VT Advantage do video. */
 static struct ast_rtp *skinny_get_vrtp_peer(struct ast_channel *chan)
 {
+	struct skinny_subchannel *sub;
+	sub = chan->tech_pvt;
+	if (sub && sub->vrtp) {
+		return sub->vrtp;
+	}
 	return NULL;
 }
 
 static struct ast_rtp *skinny_get_rtp_peer(struct ast_channel *chan)
 {
-	struct skinny_line *l;
-	l = chan->tech_pvt;
-	if (l && l->rtp) {
-		return l->rtp;
+	struct skinny_subchannel *sub;
+	sub = chan->tech_pvt;
+	if (sub && sub->rtp) {
+		return sub->rtp;
 	}
 	return NULL;
 }
 
 static int skinny_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, int codecs, int nat_active)
 {
-	struct skinny_line *l;
-	l = chan->tech_pvt;
-	if (l) {
-		/* transmit_modify_with_sdp(l, rtp); @@FIXME@@ if needed */
+	struct skinny_subchannel *sub;
+	sub = chan->tech_pvt;
+	if (sub) {
+		/* transmit_modify_with_sdp(sub, rtp); @@FIXME@@ if needed */
 		return 0;
 	}
 	return -1;
@@ -1533,6 +1625,60 @@
 	}
 	skinnydebug = 0;
 	ast_cli(fd, "Skinny Debugging Disabled\n");
+	return RESULT_SUCCESS;
+}
+
+static char *complete_skinny_reset(const char *line, const char *word, int pos, int state)
+{
+	struct skinny_device *d;
+
+	char *result = NULL;
+	int wordlen = strlen(word);
+	int which = 0;
+
+	if (pos == 2) {
+		for (d = devices; d && !result; d = d->next) {
+			if (!strncasecmp(word, d->id, wordlen) && ++which > state)
+				result = ast_strdup(d->id);
+		}
+	}
+
+	return result;
+}
+
+static int skinny_reset_device(int fd, int argc, char *argv[])
+{
+	struct skinny_device *d;
+	skinny_req *req;
+
+	if (argc < 3 || argc > 4) {
+		return RESULT_SHOWUSAGE;
+	}
+	ast_mutex_lock(&devicelock);
+
+	for (d = devices; d; d = d->next) {
+		int fullrestart = 0;
+		if (!strcasecmp(argv[2], d->id) || !strcasecmp(argv[2], "all")) {
+			if (!(d->session))
+				continue;
+
+			if (!(req = req_alloc(sizeof(struct reset_message), RESET_MESSAGE)))
+				continue;
+
+			if (argc == 4 && !strcasecmp(argv[3], "restart"))
+				fullrestart = 1;
+
+			if (fullrestart)
+				req->data.reset.resetType = 2;
+			else
+				req->data.reset.resetType = 1;
+
+			if (option_verbose > 2)
+				ast_verbose(VERBOSE_PREFIX_3 "%s device %s.\n", (fullrestart) ? "Restarting" : "Resetting", d->id);
+			transmit_response(d->session, req);
+		}
+	}
+	ast_mutex_unlock(&devicelock);
 	return RESULT_SUCCESS;
 }
 
@@ -1581,22 +1727,40 @@
 	ast_mutex_lock(&devicelock);
 	d = devices;
 
+#if 0
 	ast_cli(fd, "Device Name          Instance Name                 Label                O R\n");
 	ast_cli(fd, "-------------------- -------- -------------------- -------------------- - -\n");
 	while(d) {
 		l = d->lines;
 		while (l) {
 			ast_cli(fd, "%-20s %8d %-20s %-20s %c %c\n",
-				l->parent->name,
+				d->name,
 				l->instance,
 				l->name,
 				l->label,
-				l->owner?'Y':'N',
-				l->rtp?'Y':'N');
+				sub->owner?'Y':'N',
+				sub->rtp?'Y':'N');
 			l = l->next;
 		}
 		d = d->next;
 	}
+#else
+	ast_cli(fd, "Device Name          Instance Name                 Label               \n");
+	ast_cli(fd, "-------------------- -------- -------------------- --------------------\n");
+	while(d) {
+		l = d->lines;
+		while (l) {
+			ast_cli(fd, "%-20s %8d %-20s %-20s\n",
+				d->name,
+				l->instance,
+				l->name,
+				l->label);
+			l = l->next;
+		}
+		d = d->next;
+	}
+#endif
+
 	ast_mutex_unlock(&devicelock);
 	return RESULT_SUCCESS;
 }
@@ -1617,6 +1781,10 @@
 "Usage: skinny no debug\n"
 "       Disables dumping of Skinny packets for debugging purposes\n";
 
+static char reset_usage[] =
+"Usage: skinny reset <DeviceId|all> [restart]\n"
+"       Causes a Skinny device to reset itself, optionally with a full restart\n";
+
 static struct ast_cli_entry cli_show_devices =
 	{ { "skinny", "show", "devices", NULL }, skinny_show_devices, "Show defined Skinny devices", show_devices_usage };
 
@@ -1628,6 +1796,9 @@
 
 static struct ast_cli_entry cli_no_debug =
 	{ { "skinny", "no", "debug", NULL }, skinny_no_debug, "Disable Skinny debugging", no_debug_usage };
+
+static struct ast_cli_entry cli_reset_device =
+	{ { "skinny", "reset", NULL }, skinny_reset_device, "Reset Skinny device(s)", reset_usage, complete_skinny_reset };
 
 #if 0
 static struct skinny_paging_device *build_paging_device(char *cat, struct ast_variable *v)
@@ -1644,12 +1815,14 @@
 	struct skinny_addon *a;
 	int lineInstance = 1;
 	int speeddialInstance = 1;
-	int y=0;
-
-	d = malloc(sizeof(struct skinny_device));
-	if (d) {
-		memset(d, 0, sizeof(struct skinny_device));
+	int y = 0;
+
+	d = ast_calloc(1, sizeof(struct skinny_device));
+	if (!d) {
+		return NULL;
+	} else {
 		ast_copy_string(d->name, cat, sizeof(d->name));
+		d->lastlineinstance = 1;
 		while(v) {
 			if (!strcasecmp(v->name, "host")) {
 				if (ast_get_ip(&d->addr, v->value)) {
@@ -1668,8 +1841,6 @@
 				ast_copy_string(d->version_id, v->value, sizeof(d->version_id));
 			} else if (!strcasecmp(v->name, "nat")) {
 				nat = ast_true(v->value);
-//			} else if (!strcasecmp(v->name, "model")) {
-//				ast_copy_string(d->model, v->value, sizeof(d->model));
 			} else if (!strcasecmp(v->name, "callerid")) {
 				if (!strcasecmp(v->value, "asreceived")) {
 					cid_num[0] = '\0';
@@ -1715,9 +1886,17 @@
 			} else if (!strcasecmp(v->name, "speeddial")) {
 				sd = malloc(sizeof(struct skinny_speeddial));
 				if (sd) {
+					char *stringp, *exten, *label;
+					stringp = v->value;
+					exten = strsep(&stringp, ",");
+					label = strsep(&stringp, ",");
 					memset(sd, 0, sizeof(struct skinny_speeddial));
 					ast_mutex_init(&sd->lock);
-					ast_copy_string(sd->exten, v->value, sizeof(sd->exten));
+					ast_copy_string(sd->exten, exten, sizeof(sd->exten));
+					if (label)
+						ast_copy_string(sd->label, label, sizeof(sd->label));
+					else
+						ast_copy_string(sd->label, exten, sizeof(sd->label));
 					sd->instance = speeddialInstance++;
 
 					sd->next = d->speeddials;
@@ -1782,10 +1961,6 @@
 					l->instance = lineInstance++;
 					/* ASSUME we're onhook at this point */
 					l->hookstate = SKINNY_ONHOOK;
-					/* Make a call*ID */
-					l->callid = callnums;
-					callnums++;
-					l->cxmode = SKINNY_CX_INACTIVE;
 					l->nat = nat;
 
 					l->next = d->lines;
@@ -1844,27 +2019,45 @@
 	return 1;
 }
 
-static void start_rtp(struct skinny_line *l)
-{
-	ast_mutex_lock(&l->lock);
+static int skinny_unregister(skinny_req *req, struct skinnysession *s)
+{
+	struct skinny_device *d;
+
+	d = s->device;
+
+	if (d) {
+		d->session = NULL;
+		d->registered = 0;
+	}
+
+	return -1; /* main loop will destroy the session */
+}
+
+static void start_rtp(struct skinny_subchannel *sub)
+{
+	struct skinny_line *l = sub->parent;
+	struct skinny_device *d = l->parent;
+	ast_mutex_lock(&sub->lock);
 	/* Allocate the RTP */
-	l->rtp = ast_rtp_new(sched, io, 1, 0);
-	if (l->rtp && l->owner) {
-		l->owner->fds[0] = ast_rtp_fd(l->rtp);
-	}
-	if (l->rtp) {
-		ast_rtp_setnat(l->rtp, l->nat);
+	sub->rtp = ast_rtp_new(sched, io, 1, 0);
+	if (sub->rtp && sub->owner) {
+		sub->owner->fds[0] = ast_rtp_fd(sub->rtp);
+	}
+	if (sub->rtp) {
+		ast_rtp_setnat(sub->rtp, l->nat);
 	}
 	/* Create the RTP connection */
-	transmit_connect(l->parent->session);
- 	ast_mutex_unlock(&l->lock);
+	transmit_connect(d->session, sub);
+ 	ast_mutex_unlock(&sub->lock);
 }
 
 static void *skinny_ss(void *data)
 {
 	struct ast_channel *chan = data;
-	struct skinny_line *l = chan->tech_pvt;
-	struct skinnysession *s = l->parent->session;
+	struct skinny_subchannel *sub = chan->tech_pvt;
+	struct skinny_line *l = sub->parent;
+	struct skinny_device *d = l->parent;
+	struct skinnysession *s = d->session;
 	char exten[AST_MAX_EXTENSION] = "";
 	int len = 0;
 	int timeout = firstdigittimeout;
@@ -1872,13 +2065,13 @@
 	int getforward=0;
 
 	if (option_verbose > 2)
-		ast_verbose( VERBOSE_PREFIX_3 "Starting simple switch on '%s@%s'\n", l->name, l->parent->name);
+		ast_verbose( VERBOSE_PREFIX_3 "Starting simple switch on '%s@%s'\n", l->name, d->name);
 	while (len < AST_MAX_EXTENSION-1) {
 		res = ast_waitfordigit(chan, timeout);
 		timeout = 0;
 		if (res < 0) {
 			if (skinnydebug)
-				ast_verbose("Skinny(%s@%s): waitfordigit returned < 0\n", l->name, l->parent->name);
+				ast_verbose("Skinny(%s@%s): waitfordigit returned < 0\n", l->name, d->name);
 			ast_indicate(chan, -1);
 			ast_hangup(chan);
 			return NULL;
@@ -1935,122 +2128,6 @@
 			transmit_tone(s, SKINNY_REORDER);
 			ast_hangup(chan);
 			return NULL;
-		} else if (!strcmp(exten,ast_pickup_ext())) {
-			/* Scan all channels and see if any there
-			 * ringing channels with that have call groups
-			 * that equal this channels pickup group
-			 */
-			if (ast_pickup_call(chan)) {
-				ast_log(LOG_WARNING, "No call pickup possible...\n");
-				transmit_tone(s, SKINNY_REORDER);
-			}
-			ast_hangup(chan);
-			return NULL;
-		} else if (!strcmp(exten, ast_parking_ext()) &&
-			l->next && l->next->owner &&
-			ast_bridged_channel(l->next->owner)) {
-			/* This is a three way call, the main call being a real channel,
-			   and we're parking the first call. */
-			ast_masq_park_call(ast_bridged_channel(l->next->owner), chan, 0, NULL);
-			if (option_verbose > 2)
-				ast_verbose(VERBOSE_PREFIX_3 "Parking call to '%s'\n", chan->name);
-			break;
-		} else if (!ast_strlen_zero(l->lastcallerid) && !strcmp(exten, "*60")) {
-			if (option_verbose > 2)
-				ast_verbose(VERBOSE_PREFIX_3 "Blacklisting number %s\n", l->lastcallerid);
-			res = ast_db_put("blacklist", l->lastcallerid, "1");
-			if (!res) {
-				transmit_tone(s, SKINNY_DIALTONE);
-				memset(exten, 0, sizeof(exten));
-				len = 0;
-			}
-		} else if (!l->hidecallerid && !strcmp(exten, "*67")) {
-			if (option_verbose > 2)
-				ast_verbose(VERBOSE_PREFIX_3 "Disabling Caller*ID on %s\n", chan->name);
-			/* Disable Caller*ID if enabled */
-			l->hidecallerid = 1;
-			if (chan->cid.cid_num) {
-				free(chan->cid.cid_num);
-			}
-			chan->cid.cid_num = NULL;
-			if (chan->cid.cid_name) {
-				free(chan->cid.cid_name);
-			}
-			chan->cid.cid_name = NULL;
-			transmit_tone(s, SKINNY_DIALTONE);
-			len = 0;
-			memset(exten, 0, sizeof(exten));
-			timeout = firstdigittimeout;
-		} else if (l->callreturn && !strcmp(exten, "*69")) {
-			res = 0;
-			if (!ast_strlen_zero(l->lastcallerid)) {
-				res = ast_say_digit_str(chan, l->lastcallerid, "", chan->language);
-			}
-			if (!res) {
-				transmit_tone(s, SKINNY_DIALTONE);
-			}
-			break;
-		} else if (l->callwaiting && !strcmp(exten, "*70")) {
-			if (option_verbose > 2)
-				ast_verbose(VERBOSE_PREFIX_3 "Disabling call waiting on %s\n", chan->name);
-			/* Disable call waiting if enabled */
-			l->callwaiting = 0;
-			transmit_tone(s, SKINNY_DIALTONE);
-			len = 0;
-			memset(exten, 0, sizeof(exten));
-			timeout = firstdigittimeout;
-		} else if (l->cancallforward && !strcmp(exten, "*72")) {
-			transmit_tone(s, SKINNY_DIALTONE);
-			getforward = 1;
-			memset(exten, 0, sizeof(exten));
-			len = 0;
-		} else if (l->cancallforward && !strcmp(exten, "*73")) {
-			if (option_verbose > 2)
-				ast_verbose(VERBOSE_PREFIX_3 "Cancelling call forwarding on channel %s\n", chan->name);
-			transmit_tone(s, SKINNY_DIALTONE);
-			memset(l->call_forward, 0, sizeof(l->call_forward));
-			getforward = 0;
-			memset(exten, 0, sizeof(exten));
-			len = 0;
-		} else if (!strcmp(exten, "*78")) {
-			/* Do not disturb */
-			if (option_verbose > 2)
-				ast_verbose(VERBOSE_PREFIX_3 "Enabled DND on channel %s\n", chan->name);
-			transmit_tone(s, SKINNY_DIALTONE);
-			l->dnd = 1;
-			getforward = 0;
-			memset(exten, 0, sizeof(exten));
-			len = 0;
-		} else if (!strcmp(exten, "*79")) {
-			/* Do not disturb */
-			if (option_verbose > 2)
-				ast_verbose(VERBOSE_PREFIX_3 "Disabled DND on channel %s\n", chan->name);
-			transmit_tone(s, SKINNY_DIALTONE);
-			l->dnd = 0;
-			getforward = 0;
-			memset(exten, 0, sizeof(exten));
-			len = 0;
-		} else if (l->hidecallerid && !strcmp(exten, "*82")) {
-			if (option_verbose > 2)
-				ast_verbose(VERBOSE_PREFIX_3 "Enabling Caller*ID on %s\n", chan->name);
-			/* Enable Caller*ID if enabled */
-			l->hidecallerid = 0;
-			if (chan->cid.cid_num) {
-				free(chan->cid.cid_num);
-			}
-			if (!ast_strlen_zero(l->cid_num)) {
-				chan->cid.cid_num = strdup(l->cid_num);
-			}
-			if (chan->cid.cid_name) {
-				free(chan->cid.cid_name);
-			}
-			if (!ast_strlen_zero(l->cid_name)) {
-				chan->cid.cid_name = strdup(l->cid_name);
-			}
-			transmit_tone(s, SKINNY_DIALTONE);
-			len = 0;
-			memset(exten, 0, sizeof(exten));
-			timeout = firstdigittimeout;
 		} else if (!ast_canmatch_extension(chan, chan->context, exten, 1, chan->cid.cid_num) &&
 			   ((exten[0] != '*') || (!ast_strlen_zero(exten) > 2))) {
 			ast_log(LOG_WARNING, "Can't match [%s] from '%s' in context %s\n", exten, chan->cid.cid_num ? chan->cid.cid_num : "<Unknown Caller>", chan->context);
@@ -2076,13 +2153,12 @@
 {
 	int res = 0;
 	int tone = 0;
-	struct skinny_line *l;
-	struct skinnysession *session;
-
-	l = ast->tech_pvt;
-	session = l->parent->session;
-
-	if (!l->parent->registered) {
+	struct skinny_subchannel *sub = ast->tech_pvt;
+	struct skinny_line *l = sub->parent;
+	struct skinny_device *d = l->parent;
+	struct skinnysession *session = d->session;
+
+	if (!d->registered) {
 		ast_log(LOG_ERROR, "Device not registered, cannot call %s\n", dest);
 		return -1;
 	}
@@ -2116,37 +2192,42 @@
 	transmit_ringer_mode(session, SKINNY_RING_INSIDE);
 
 	transmit_tone(session, tone);
-	transmit_callstate(session, l->instance, SKINNY_RINGIN, l->callid);
-	transmit_displaypromptstatus(session, "Ring-In", 0, l->instance, l->callid);
-	transmit_callinfo(session, ast->cid.cid_name, ast->cid.cid_num, l->cid_name, l->cid_num, l->instance, l->callid, 1);
-	transmit_selectsoftkeys(session, l->instance, l->callid, KEYDEF_RINGIN);
+	transmit_callstate(session, l->instance, SKINNY_RINGIN, sub->callid);
+	transmit_displaypromptstatus(session, "Ring-In", 0, l->instance, sub->callid);
+	transmit_callinfo(session, ast->cid.cid_name, ast->cid.cid_num, l->cid_name, l->cid_num, l->instance, sub->callid, 1);
+	transmit_selectsoftkeys(session, l->instance, sub->callid, KEYDEF_RINGIN);
 
 	ast_setstate(ast, AST_STATE_RINGING);
 	ast_queue_control(ast, AST_CONTROL_RINGING);
-	l->outgoing = 1;
+	sub->outgoing = 1;
 	return res;
 }
 
 static int skinny_hangup(struct ast_channel *ast)
 {
-	struct skinny_line *l = ast->tech_pvt;
-	struct skinnysession *s = l->parent->session;
-
-	if (skinnydebug)
-		ast_verbose("skinny_hangup(%s) on %s@%s\n", ast->name, l->name, l->parent->name);
-	if (!ast->tech_pvt) {
+	struct skinny_subchannel *sub = ast->tech_pvt;
+	struct skinny_line *l;
+	struct skinny_device *d;
+	struct skinnysession *s;
+
+	if (!sub) {
 		ast_log(LOG_DEBUG, "Asked to hangup channel not connected\n");
 		return 0;
 	}
-
-	if (l->parent->registered) {
+	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)) {
 			l->hookstate = SKINNY_ONHOOK;
-			transmit_callstate(s, l->instance, SKINNY_ONHOOK, l->callid);
+			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);
 		} else if ((l->type = TYPE_LINE) && (l->hookstate == SKINNY_ONHOOK)) {
-			transmit_callstate(s, l->instance, SKINNY_ONHOOK, l->callid);
+			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);
@@ -2154,53 +2235,57 @@
 			do_housekeeping(s);
 		}
 	}
-	ast_mutex_lock(&l->lock);
-	l->owner = NULL;
+	ast_mutex_lock(&sub->lock);
+	sub->owner = NULL;
 	ast->tech_pvt = NULL;
-	l->alreadygone = 0;
-	l->outgoing = 0;
-	if (l->rtp) {
-		ast_rtp_destroy(l->rtp);
-		l->rtp = NULL;
-	}
-	ast_mutex_unlock(&l->lock);
+	sub->alreadygone = 0;
+	sub->outgoing = 0;
+	if (sub->rtp) {
+		ast_rtp_destroy(sub->rtp);
+		sub->rtp = NULL;
+	}
+	ast_mutex_unlock(&sub->lock);
 	return 0;
 }
 
 static int skinny_answer(struct ast_channel *ast)
 {
 	int res = 0;
-	struct skinny_line *l = ast->tech_pvt;
-	struct skinnysession *s = l->parent->session;
-
-	l->cxmode = SKINNY_CX_SENDRECV;
-	if (!l->rtp) {
-		start_rtp(l);
+	struct skinny_subchannel *sub = ast->tech_pvt;
+	struct skinny_line *l = sub->parent;
+	struct skinny_device *d = l->parent;
+	struct skinnysession *s = d->session;
+
+	sub->cxmode = SKINNY_CX_SENDRECV;
+	if (!sub->rtp) {
+		start_rtp(sub);
 	}
 	if (skinnydebug)
-		ast_verbose("skinny_answer(%s) on %s@%s-%d\n", ast->name, l->name, l->parent->name, l->callid);
+		ast_verbose("skinny_answer(%s) on %s@%s-%d\n", ast->name, l->name, d->name, sub->callid);
 	if (ast->_state != AST_STATE_UP) {
 		ast_setstate(ast, AST_STATE_UP);
 	}
 	transmit_tone(s, SKINNY_NOTONE);
-	transmit_callstate(s, l->instance, SKINNY_CONNECTED, l->callid);
-	transmit_displaypromptstatus(s, "Connected", 0, l->instance, l->callid);
+	transmit_callstate(s, l->instance, SKINNY_CONNECTED, sub->callid);
+	transmit_displaypromptstatus(s, "Connected", 0, l->instance, sub->callid);
 	return res;
 }
 
-static struct ast_frame *skinny_rtp_read(struct skinny_line *l)
-{
-	/* Retrieve audio/etc from channel.  Assumes l->lock is already held. */
+/* Retrieve audio/etc from channel.  Assumes sub->lock is already held. */
+static struct ast_frame *skinny_rtp_read(struct skinny_subchannel *sub)
+{
+	struct ast_channel *ast = sub->owner;
 	struct ast_frame *f;
-	f = ast_rtp_read(l->rtp);
-	if (l->owner) {
+
+	f = ast_rtp_read(sub->rtp);
+	if (ast) {
 		/* We already hold the channel lock */
 		if (f->frametype == AST_FRAME_VOICE) {
-			if (f->subclass != l->owner->nativeformats) {
+			if (f->subclass != ast->nativeformats) {
 				ast_log(LOG_DEBUG, "Oooh, format changed to %d\n", f->subclass);
-				l->owner->nativeformats = f->subclass;
-				ast_set_read_format(l->owner, l->owner->readformat);
-				ast_set_write_format(l->owner, l->owner->writeformat);
+				ast->nativeformats = f->subclass;
+				ast_set_read_format(ast, ast->readformat);
+				ast_set_write_format(ast, ast->writeformat);
 			}
 		}
 	}
@@ -2210,16 +2295,16 @@
 static struct ast_frame *skinny_read(struct ast_channel *ast)
 {
 	struct ast_frame *fr;
-	struct skinny_line *l = ast->tech_pvt;
-	ast_mutex_lock(&l->lock);
-	fr = skinny_rtp_read(l);
-	ast_mutex_unlock(&l->lock);
+	struct skinny_subchannel *sub = ast->tech_pvt;
+	ast_mutex_lock(&sub->lock);
+	fr = skinny_rtp_read(sub);
+	ast_mutex_unlock(&sub->lock);
 	return fr;
 }
 
 static int skinny_write(struct ast_channel *ast, struct ast_frame *frame)
 {
-	struct skinny_line *l = ast->tech_pvt;
+	struct skinny_subchannel *sub = ast->tech_pvt;
 	int res = 0;
 	if (frame->frametype != AST_FRAME_VOICE) {
 		if (frame->frametype == AST_FRAME_IMAGE) {
@@ -2235,36 +2320,38 @@
 			return -1;
 		}
 	}
-	if (l) {
-		ast_mutex_lock(&l->lock);
-		if (l->rtp) {
-			res = ast_rtp_write(l->rtp, frame);
-		}
-		ast_mutex_unlock(&l->lock);
+	if (sub) {
+		ast_mutex_lock(&sub->lock);
+		if (sub->rtp) {
+			res = ast_rtp_write(sub->rtp, frame);
+		}
+		ast_mutex_unlock(&sub->lock);
 	}
 	return res;
 }
 
 static int skinny_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
 {
-	struct skinny_line *l = newchan->tech_pvt;
+	struct skinny_subchannel *sub = newchan->tech_pvt;
 	ast_log(LOG_NOTICE, "skinny_fixup(%s, %s)\n", oldchan->name, newchan->name);
-	if (l->owner != oldchan) {
-		ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, l->owner);
+	if (sub->owner != oldchan) {
+		ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, sub->owner);
 		return -1;
 	}
-	l->owner = newchan;
+	sub->owner = newchan;
 	return 0;
 }
 
 static int skinny_senddigit(struct ast_channel *ast, char digit)
 {
 #if 0
-	struct skinny_line *l = ast->tech_pvt;
+	struct skinny_subchannel *sub = ast->tech_pvt;
+	struct skinny_line *l = sub->parent;
+	struct skinny_device *d = l->parent;
 	int tmp;
 	/* not right */
 	sprintf(tmp, "%d", digit);
-	transmit_tone(l->parent->session, digit);
+	transmit_tone(d->session, digit);
 #endif
 	return -1;
 }
@@ -2317,21 +2404,23 @@
 
 static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, size_t datalen)
 {
-	struct skinny_line *l = ast->tech_pvt;
-	struct skinnysession *s = l->parent->session;
+	struct skinny_subchannel *sub = ast->tech_pvt;
+	struct skinny_line *l = sub->parent;
+	struct skinny_device *d = l->parent;
+	struct skinnysession *s = d->session;
 
 	if (skinnydebug)
 		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 (ast->_state != AST_STATE_UP) {
-			if (!l->progress) {
+			if (!sub->progress) {
 				transmit_tone(s, SKINNY_ALERT);
-				transmit_callstate(s, l->instance, SKINNY_RINGOUT, l->callid);
-				transmit_dialednumber(s, ast->exten, l->instance, l->callid);
-				transmit_displaypromptstatus(s, "Ring Out", 0, l->instance, l->callid);
-				transmit_callinfo(s, ast->cid.cid_name, ast->cid.cid_num, ast->exten, ast->exten, l->instance, l->callid, 2); /* 2 = outgoing from phone */
-				l->ringing = 1;
+				transmit_callstate(s, l->instance, SKINNY_RINGOUT, sub->callid);
+				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 */
+				sub->ringing = 1;
 				break;
 			}
 		}
@@ -2339,8 +2428,8 @@
 	case AST_CONTROL_BUSY:
 		if (ast->_state != AST_STATE_UP) {
 			transmit_tone(s, SKINNY_BUSYTONE);
-			transmit_callstate(s, l->instance, SKINNY_BUSY, l->callid);
-			l->alreadygone = 1;
+			transmit_callstate(s, l->instance, SKINNY_BUSY, sub->callid);
+			sub->alreadygone = 1;
 			ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
 			break;
 		}
@@ -2348,19 +2437,19 @@
 	case AST_CONTROL_CONGESTION:
 		if (ast->_state != AST_STATE_UP) {
 			transmit_tone(s, SKINNY_REORDER);
-			transmit_callstate(s, l->instance, SKINNY_CONGESTION, l->callid);
-			l->alreadygone = 1;
+			transmit_callstate(s, l->instance, SKINNY_CONGESTION, sub->callid);
+			sub->alreadygone = 1;
 			ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
 			break;
 		}
 		return -1;
 	case AST_CONTROL_PROGRESS:
-		if ((ast->_state != AST_STATE_UP) && !l->progress && !l->outgoing) {
+		if ((ast->_state != AST_STATE_UP) && !sub->progress && !sub->outgoing) {
 			transmit_tone(s, SKINNY_ALERT);
-			transmit_callstate(s, l->instance, SKINNY_PROGRESS, l->callid);
-			transmit_displaypromptstatus(s, "Call Progress", 0, l->instance, l->callid);
-			transmit_callinfo(s, ast->cid.cid_name, ast->cid.cid_num, ast->exten, ast->exten, l->instance, l->callid, 2); /* 2 = outgoing from phone */
-			l->progress = 1;
+			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, ast->exten, ast->exten, l->instance, sub->callid, 2); /* 2 = outgoing from phone */
+			sub->progress = 1;
 			break;
 		}
 		return -1;
@@ -2379,19 +2468,44 @@
 static struct ast_channel *skinny_new(struct skinny_line *l, int state)
 {
 	struct ast_channel *tmp;
+	struct skinny_subchannel *sub;
+	struct skinny_device *d = l->parent;
 	int fmt;
+
 	tmp = ast_channel_alloc(1);
-	if (tmp) {
+	if (!tmp) {
+		ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
+		return NULL;
+	} else {
+		sub = ast_calloc(1, sizeof(struct skinny_subchannel));
+		if (!sub) {
+			ast_log(LOG_WARNING, "Unable to allocate Skinny subchannel\n");
+			return NULL;
+		} else {
+			ast_mutex_init(&sub->lock);
+
+			sub->owner = tmp;
+			sub->callid = callnums++;
+			d->lastlineinstance = l->instance;
+			d->lastcallreference = sub->callid;
+			sub->cxmode = SKINNY_CX_INACTIVE;
+			sub->nat = l->nat;
+			sub->parent = l;
+
+			sub->next = l->sub;
+			l->sub = sub;
+		}
 		tmp->tech = &skinny_tech;
+		tmp->tech_pvt = sub;
 		tmp->nativeformats = l->capability;
 		if (!tmp->nativeformats)
 			tmp->nativeformats = capability;
 		fmt = ast_best_codec(tmp->nativeformats);
 		if (skinnydebug)
 			ast_verbose("skinny_new: tmp->nativeformats=%d fmt=%d\n", tmp->nativeformats, fmt);
-		ast_string_field_build(tmp, name, "Skinny/%s@%s-%d", l->name, l->parent->name, l->callid);
-		if (l->rtp) {
-			tmp->fds[0] = ast_rtp_fd(l->rtp);
+		ast_string_field_build(tmp, name, "Skinny/%s@%s-%d", l->name, d->name, sub->callid);
+		if (sub->rtp) {
+			tmp->fds[0] = ast_rtp_fd(sub->rtp);
 		}
 		ast_setstate(tmp, state);
 		if (state == AST_STATE_RING) {
@@ -2401,14 +2515,13 @@
 		tmp->rawwriteformat = fmt;
 		tmp->readformat = fmt;
 		tmp->rawreadformat = fmt;
-		tmp->tech_pvt = l;
 		if (!ast_strlen_zero(l->language))
 			ast_string_field_set(tmp, language, l->language);
 		if (!ast_strlen_zero(l->accountcode))
 			ast_string_field_set(tmp, accountcode, l->accountcode);
 		if (l->amaflags)
 			tmp->amaflags = l->amaflags;
-		l->owner = tmp;
+
 		ast_mutex_lock(&usecnt_lock);
 		usecnt++;
 		ast_mutex_unlock(&usecnt_lock);
@@ -2417,7 +2530,7 @@
 		tmp->pickupgroup = l->pickupgroup;
 		ast_string_field_set(tmp, call_forward, l->call_forward);
 		ast_copy_string(tmp->context, l->context, sizeof(tmp->context));
-		ast_copy_string(tmp->exten,l->exten, sizeof(tmp->exten));
+		ast_copy_string(tmp->exten, l->exten, sizeof(tmp->exten));
 		tmp->cid.cid_num = ast_strdup(l->cid_num);
 		tmp->cid.cid_name = ast_strdup(l->cid_name);
 		tmp->priority = 1;
@@ -2432,10 +2545,8 @@
 		}
 
 		/* Configure the new channel jb */
-		if (tmp && l && l->rtp)
+		if (tmp && sub->rtp)
 			ast_jb_configure(tmp, &global_jbconf);
-	} else {
-		ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
 	}
 	return tmp;
 }
@@ -2455,10 +2566,11 @@
 	char name[16];
 	int res;
 
+	memcpy(&name, req->data.reg.name, sizeof(name));
+
 	res = skinny_register(req, s);
 	if (!res) {
-		ast_log(LOG_ERROR, "Rejecting Device %s: Device not found\n", req->data.reg.name);
-		memcpy(&name, req->data.reg.name, sizeof(req->data.reg.name));
+		ast_log(LOG_ERROR, "Rejecting Device %s: Device not found\n", name);
 		if (!(req = req_alloc(sizeof(register_rej_message), REGISTER_REJ_MESSAGE)))
 			return -1;
 
@@ -2467,7 +2579,7 @@
 		return 0;
 	}
 	if (option_verbose > 2)
-		ast_verbose(VERBOSE_PREFIX_3 "Device '%s' successfully registered\n", s->device->name);
+		ast_verbose(VERBOSE_PREFIX_3 "Device '%s' successfully registered\n", name);
 
 	if (!(req = req_alloc(sizeof(register_ack_message), REGISTER_ACK_MESSAGE)))
 		return -1;
@@ -2499,12 +2611,17 @@
 
 static int handle_keypad_button_message(skinny_req *req, struct skinnysession *s)
 {
+	struct skinny_subchannel *sub = NULL;
 	struct skinny_line *l;
 	struct ast_frame f = { 0, };
 	char d;
 	int digit;
+	int lineInstance;
+	int callReference;
 
 	digit = letohl(req->data.keypad.button);
+	lineInstance = letohl(req->data.keypad.lineInstance);
+	callReference = letohl(req->data.keypad.callReference);
 	f.frametype = AST_FRAME_DTMF;
 	if (digit == 14) {
 		d = '*';
@@ -2525,12 +2642,20 @@
 	}
 	f.subclass = d;
 	f.src = "skinny";
-	l = s->device->lines;
-	if (l->owner) {
+
+	if (lineInstance && callReference)
+		sub = find_subchannel_by_instance_reference(s->device, lineInstance, callReference);
+
+	if (!sub)
+		return 0;
+
+	l = sub->parent;
+	if (sub->owner) {
 		/* XXX MUST queue this frame to all lines in threeway call if threeway call is active */
-		ast_queue_frame(l->owner, &f);
-		if (l->next && l->owner) {
-			ast_queue_frame(l->owner, &f);
+		ast_queue_frame(sub->owner, &f);
+		/* XXX This seriously needs to be fixed */
+		if (sub->next && sub->next->owner) {
+			ast_queue_frame(sub->next->owner, &f);
 		}
 	} else {
 		if (skinnydebug)
@@ -2541,16 +2666,33 @@
 
 static int handle_stimulus_message(skinny_req *req, struct skinnysession *s)
 {
+	struct skinny_device *d = s->device;
 	struct skinny_line *l;
+	struct skinny_subchannel *sub;
 	struct skinny_speeddial *sd;
 	struct ast_channel *c;
 	pthread_t t;
 	int event;
 	int instance;
+	int unknown1;
 	int res = 0;
 
 	event = letohl(req->data.stimulus.stimulus);
 	instance = letohl(req->data.stimulus.stimulusInstance);
+	unknown1 = letohl(req->data.stimulus.unknown1); /* No clue.. */
+	if (skinnydebug)
+		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) {
+			return 0;
+		}
+	} else {
+		l = sub->parent;
+	}
 
 	switch(event) {
 	case STIMULUS_REDIAL:
@@ -2560,16 +2702,17 @@
 		if (skinnydebug)
 			ast_verbose("Received Stimulus: Redial(%d)\n", instance);
 
-		l = s->device->lines;
-
-		transmit_callstate(s, l->instance, SKINNY_OFFHOOK, l->callid);
-		if (skinnydebug)
-			ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, s->device->name);
-		transmit_displaymessage(s, NULL); /* clear display */
-		transmit_tone(s, SKINNY_DIALTONE);
-
+#if 0
 		c = skinny_new(l, AST_STATE_DOWN);
 		if(c) {
+			sub = c->tech_pvt;
+			l = sub->parent;
+			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);
+
 			if (ast_strlen_zero(l->lastnumberdialed)) {
 				ast_log(LOG_WARNING, "Attempted redial, but no previously dialed number found.\n");
 				return 0;
@@ -2596,27 +2739,29 @@
 				}
 			}
 		} else {
-			ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, s->device->name);
-		}

[... 817 lines stripped ...]


More information about the asterisk-commits mailing list