[asterisk-commits] russell: branch russell/console_devices r99223 - in /team/russell/console_dev...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Tue Jan 22 10:39:03 CST 2008


Author: russell
Date: Sat Jan 19 22:31:57 2008
New Revision: 99223

URL: http://svn.digium.com/view/asterisk?view=rev&rev=99223
Log:
This big set of changes is my first pass at multiple device support.  I haven't
tested it, but it compiles, so it obviously works.  :)

Modified:
    team/russell/console_devices/channels/chan_console.c
    team/russell/console_devices/configs/console.conf.sample

Change Statistics:
 team/russell/console_devices/channels/chan_console.c     |  496 +++++++---
 team/russell/console_devices/configs/console.conf.sample |   24 
 2 files changed, 405 insertions(+), 115 deletions(-)

Modified: team/russell/console_devices/channels/chan_console.c
URL: http://svn.digium.com/view/asterisk/team/russell/console_devices/channels/chan_console.c?view=diff&rev=99223&r1=99222&r2=99223
==============================================================================
--- team/russell/console_devices/channels/chan_console.c (original)
+++ team/russell/console_devices/channels/chan_console.c Sat Jan 19 22:31:57 2008
@@ -1,7 +1,7 @@
 /*
  * Asterisk -- An open source telephony toolkit.
  *
- * Copyright (C) 2006 - 2007, Digium, Inc.
+ * Copyright (C) 2006 - 2008, Digium, Inc.
  *
  * Russell Bryant <russell at digium.com>
  *
@@ -68,6 +68,7 @@
 #include "asterisk/cli.h"
 #include "asterisk/musiconhold.h"
 #include "asterisk/callerid.h"
+#include "asterisk/astobj2.h"
 
 /*! 
  * \brief The sample rate to request from PortAudio 
@@ -126,6 +127,8 @@
 	AST_DECLARE_STRING_FIELDS(
 		/*! Name of the device */
 		AST_STRING_FIELD(name);
+		AST_STRING_FIELD(input_device);
+		AST_STRING_FIELD(output_device);
 		/*! Default context for outgoing calls */
 		AST_STRING_FIELD(context);
 		/*! Default extension for outgoing calls */
@@ -157,14 +160,19 @@
 	unsigned int autoanswer:1;
 	/*! Ignore context in the console dial CLI command */
 	unsigned int overridecontext:1;
-	/*! Lock to protect data in this struct */
-	ast_mutex_t __lock;
+	/*! Set during a reload so that we know to destroy this if it is no longer
+	 *  in the configuration file. */
+	unsigned int destroy:1;
 	/*! ID for the stream monitor thread */
 	pthread_t thread;
-} console_pvt = {
-	.__lock = AST_MUTEX_INIT_VALUE,
-	.thread = AST_PTHREADT_NULL,
-};
+} globals;
+
+AST_MUTEX_DEFINE_STATIC(globals_lock);
+
+static struct ao2_container *pvts;
+#define NUM_PVT_BUCKETS 7
+
+static struct console_pvt *active_pvt;
 
 /*! 
  * \brief Global jitterbuffer configuration 
@@ -218,10 +226,32 @@
 };
 
 /*! \brief lock a console_pvt struct */
-#define console_pvt_lock(pvt) ast_mutex_lock(&(pvt)->__lock)
+#define console_pvt_lock(pvt) ao2_lock(pvt)
 
 /*! \brief unlock a console_pvt struct */
-#define console_pvt_unlock(pvt) ast_mutex_unlock(&(pvt)->__lock)
+#define console_pvt_unlock(pvt) ao2_unlock(pvt)
+
+static inline struct console_pvt *ref_pvt(struct console_pvt *pvt)
+{
+	if (pvt)
+		ao2_ref(pvt, +1);
+	return pvt;
+}
+
+static inline struct console_pvt *unref_pvt(struct console_pvt *pvt)
+{
+	ao2_ref(pvt, -1);
+	return NULL;
+}
+
+static struct console_pvt *find_pvt(const char *name)
+{
+	struct console_pvt tmp_pvt = {
+		.name = name,
+	};
+
+	return ao2_find(pvts, &tmp_pvt, OBJ_POINTER);
+}
 
 /*!
  * \brief Stream monitor thread 
@@ -259,6 +289,67 @@
 	return NULL;
 }
 
+static int open_stream(struct console_pvt *pvt)
+{
+	int res = paInternalError;
+
+	if (!strcasecmp(pvt->input_device, "default") && 
+		!strcasecmp(pvt->output_device, "default")) {
+		res = Pa_OpenDefaultStream(&pvt->stream, INPUT_CHANNELS, OUTPUT_CHANNELS, 
+			paInt16, SAMPLE_RATE, NUM_SAMPLES, NULL, NULL);
+	} else {
+		PaStreamParameters input_params = { 
+			.channelCount = 1,
+			.sampleFormat = paInt16,
+			.suggestedLatency = (1.0 / 50.0), /* 20 ms */
+			.device = paNoDevice,
+		};
+		PaStreamParameters output_params = { 
+			.channelCount = 1, 
+			.sampleFormat = paInt16,
+			.suggestedLatency = (1.0 / 50.0), /* 20 ms */
+			.device = paNoDevice,
+		};
+		PaDeviceIndex index, num_devices, def_input, def_output;
+
+		if (!(num_devices = Pa_GetDeviceCount()))
+			return res;
+
+		def_input = Pa_GetDefaultInputDevice();
+		def_output = Pa_GetDefaultOutputDevice();
+
+		for (index = 0; 
+			index < num_devices && input_params.device == paNoDevice 
+				&& output_params.device == paNoDevice; 
+			index++) 
+		{
+			const PaDeviceInfo *dev = Pa_GetDeviceInfo(index);
+
+			if (dev->maxInputChannels) {
+				if ( (index == def_input && !strcasecmp(pvt->input_device, "default")) ||
+					!strcasecmp(pvt->input_device, dev->name) )
+					input_params.device = index;
+			}
+
+			if (dev->maxOutputChannels) {
+				if ( (index == def_output && !strcasecmp(pvt->output_device, "default")) ||
+					!strcasecmp(pvt->output_device, dev->name) )
+					output_params.device = index;
+			}
+		}
+
+		if (input_params.device == paNoDevice)
+			ast_log(LOG_ERROR, "No input device found for console device '%s'\n", pvt->name);
+		if (output_params.device == paNoDevice)
+			ast_log(LOG_ERROR, "No output device found for console device '%s'\n", pvt->name);
+
+		res = Pa_OpenStream(&pvt->stream, &input_params, &output_params,
+			SAMPLE_RATE, NUM_SAMPLES, paNoFlag, NULL, NULL);
+	}
+
+	return res;
+}
+
 static int start_stream(struct console_pvt *pvt)
 {
 	PaError res;
@@ -272,8 +363,7 @@
 	pvt->streamstate = 1;
 	ast_debug(1, "Starting stream\n");
 
-	res = Pa_OpenDefaultStream(&pvt->stream, INPUT_CHANNELS, OUTPUT_CHANNELS, 
-		paInt16, SAMPLE_RATE, NUM_SAMPLES, NULL, NULL);
+	res = open_stream(pvt);
 	if (res != paNoError) {
 		ast_log(LOG_WARNING, "Failed to open default audio device - (%d) %s\n",
 			res, Pa_GetErrorText(res));
@@ -335,7 +425,7 @@
 	chan->nativeformats = AST_FORMAT_SLINEAR16;
 	chan->readformat = AST_FORMAT_SLINEAR16;
 	chan->writeformat = AST_FORMAT_SLINEAR16;
-	chan->tech_pvt = pvt;
+	chan->tech_pvt = ref_pvt(pvt);
 
 	pvt->owner = chan;
 
@@ -359,19 +449,24 @@
 static struct ast_channel *console_request(const char *type, int format, void *data, int *cause)
 {
 	int oldformat = format;
-	struct ast_channel *chan;
-	struct console_pvt *pvt = &console_pvt;
+	struct ast_channel *chan = NULL;
+	struct console_pvt *pvt;
+
+	if (!(pvt = find_pvt(data))) {
+		ast_log(LOG_ERROR, "Console device '%s' not found\n", (char *) data);
+		return NULL;
+	}
 
 	format &= SUPPORTED_FORMATS;
 	if (!format) {
 		ast_log(LOG_NOTICE, "Channel requested with unsupported format(s): '%d'\n", oldformat);
-		return NULL;
+		goto return_unref;
 	}
 
 	if (pvt->owner) {
 		ast_log(LOG_NOTICE, "Console channel already active!\n");
 		*cause = AST_CAUSE_BUSY;
-		return NULL;
+		goto return_unref;
 	}
 
 	console_pvt_lock(pvt);
@@ -381,6 +476,9 @@
 	if (!chan)
 		ast_log(LOG_WARNING, "Unable to create new Console channel!\n");
 
+return_unref:
+	unref_pvt(pvt);
+
 	return chan;
 }
 
@@ -408,22 +506,22 @@
 
 static int console_hangup(struct ast_channel *c)
 {
-	struct console_pvt *pvt = &console_pvt;
+	struct console_pvt *pvt = c->tech_pvt;
 
 	ast_verb(1, V_BEGIN "Hangup on Console" V_END);
 
 	pvt->hookstate = 0;
-	c->tech_pvt = NULL;
 	pvt->owner = NULL;
-
 	stop_stream(pvt);
 
+	c->tech_pvt = unref_pvt(pvt);
+
 	return 0;
 }
 
 static int console_answer(struct ast_channel *c)
 {
-	struct console_pvt *pvt = &console_pvt;
+	struct console_pvt *pvt = c->tech_pvt;
 
 	ast_verb(1, V_BEGIN "Call from Console has been Answered" V_END);
 
@@ -462,7 +560,7 @@
 static int console_call(struct ast_channel *c, char *dest, int timeout)
 {
 	struct ast_frame f = { 0, };
-	struct console_pvt *pvt = &console_pvt;
+	struct console_pvt *pvt = c->tech_pvt;
 
 	ast_verb(1, V_BEGIN "Call to device '%s' on console from '%s' <%s>" V_END,
 		dest, c->cid.cid_name, c->cid.cid_num);
@@ -491,7 +589,7 @@
 
 static int console_write(struct ast_channel *chan, struct ast_frame *f)
 {
-	struct console_pvt *pvt = &console_pvt;
+	struct console_pvt *pvt = chan->tech_pvt;
 
 	Pa_WriteStream(pvt->stream, f->data, f->samples);
 
@@ -534,7 +632,7 @@
 
 static int console_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
 {
-	struct console_pvt *pvt = &console_pvt;
+	struct console_pvt *pvt = newchan->tech_pvt;
 
 	pvt->owner = newchan;
 
@@ -578,7 +676,8 @@
 static char *cli_console_autoanswer(struct ast_cli_entry *e, int cmd, 
 	struct ast_cli_args *a)
 {
-	struct console_pvt *pvt = &console_pvt;
+	struct console_pvt *pvt = ref_pvt(active_pvt);
+	char *res = CLI_SUCCESS;
 
 	switch (cmd) {
 	case CLI_INIT:
@@ -594,18 +693,20 @@
 		return NULL;
 	}
 
+	if (!pvt) {
+		ast_cli(a->fd, "No console device is set as active.\n");
+		return CLI_FAILURE;
+	}
+
 	if (a->argc == e->args - 1) {
 		ast_cli(a->fd, "Auto answer is %s.\n", pvt->autoanswer ? "on" : "off");
+		unref_pvt(pvt);
 		return CLI_SUCCESS;
 	}
 
-	if (a->argc != e->args)
+	if (a->argc != e->args) {
+		unref_pvt(pvt);
 		return CLI_SHOWUSAGE;
-
-	if (!pvt) {
-		ast_log(LOG_WARNING, "Cannot find device %s (should not happen!)\n",
-			pvt->name);
-		return CLI_FAILURE;
 	}
 
 	if (!strcasecmp(a->argv[e->args-1], "on"))
@@ -613,7 +714,9 @@
 	else if (!strcasecmp(a->argv[e->args - 1], "off"))
 		pvt->autoanswer = 0;
 	else
-		return CLI_SHOWUSAGE;
+		res = CLI_SHOWUSAGE;
+
+	unref_pvt(pvt);
 
 	return CLI_SUCCESS;
 }
@@ -621,7 +724,7 @@
 static char *cli_console_flash(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 	struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_FLASH };
-	struct console_pvt *pvt = &console_pvt;
+	struct console_pvt *pvt = ref_pvt(active_pvt);
 
 	if (cmd == CLI_INIT) {
 		e->command = "console flash";
@@ -632,17 +735,25 @@
 	} else if (cmd == CLI_GENERATE)
 		return NULL;
 
+	if (!pvt) {
+		ast_cli(a->fd, "No console device is set as active\n");
+		return CLI_FAILURE;
+	}
+
 	if (a->argc != e->args)
 		return CLI_SHOWUSAGE;
 
 	if (!pvt->owner) {
 		ast_cli(a->fd, "No call to flash\n");
+		unref_pvt(pvt);
 		return CLI_FAILURE;
 	}
 
 	pvt->hookstate = 0;
 
 	ast_queue_frame(pvt->owner, &f);
+
+	unref_pvt(pvt);
 
 	return CLI_SUCCESS;
 }
@@ -651,7 +762,7 @@
 {
 	char *s = NULL;
 	const char *mye = NULL, *myc = NULL; 
-	struct console_pvt *pvt = &console_pvt;
+	struct console_pvt *pvt = ref_pvt(active_pvt);
 
 	if (cmd == CLI_INIT) {
 		e->command = "console dial";
@@ -662,6 +773,11 @@
 	} else if (cmd == CLI_GENERATE)
 		return NULL;
 
+	if (!pvt) {
+		ast_cli(a->fd, "No console device is currently set as active\n");
+		return CLI_FAILURE;
+	}
+	
 	if (a->argc > e->args + 1)
 		return CLI_SHOWUSAGE;
 
@@ -671,6 +787,7 @@
 
 		if (a->argc == e->args) {	/* argument is mandatory here */
 			ast_cli(a->fd, "Already in a call. You can only dial digits until you hangup.\n");
+			unref_pvt(pvt);
 			return CLI_FAILURE;
 		}
 		s = a->argv[e->args];
@@ -679,6 +796,7 @@
 			f.subclass = s[i];
 			ast_queue_frame(pvt->owner, &f);
 		}
+		unref_pvt(pvt);
 		return CLI_SUCCESS;
 	}
 
@@ -709,12 +827,14 @@
 	if (s)
 		free(s);
 
+	unref_pvt(pvt);
+
 	return CLI_SUCCESS;
 }
 
 static char *cli_console_hangup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-	struct console_pvt *pvt = &console_pvt;
+	struct console_pvt *pvt = ref_pvt(active_pvt);
 
 	if (cmd == CLI_INIT) {
 		e->command = "console hangup";
@@ -725,11 +845,17 @@
 	} else if (cmd == CLI_GENERATE)
 		return NULL;
 
+	if (!pvt) {
+		ast_cli(a->fd, "No console device is set as active\n");
+		return CLI_FAILURE;
+	}
+	
 	if (a->argc != e->args)
 		return CLI_SHOWUSAGE;
 
 	if (!pvt->owner && !pvt->hookstate) {
 		ast_cli(a->fd, "No call to hang up\n");
+		unref_pvt(pvt);
 		return CLI_FAILURE;
 	}
 
@@ -737,14 +863,17 @@
 	if (pvt->owner)
 		ast_queue_hangup(pvt->owner);
 
+	unref_pvt(pvt);
+
 	return CLI_SUCCESS;
 }
 
 static char *cli_console_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 	char *s;
-	struct console_pvt *pvt = &console_pvt;
-	
+	struct console_pvt *pvt = ref_pvt(active_pvt);
+	char *res = CLI_SUCCESS;
+
 	if (cmd == CLI_INIT) {
 		e->command = "console {mute|unmute}";
 		e->usage =
@@ -753,6 +882,11 @@
 		return NULL;
 	} else if (cmd == CLI_GENERATE)
 		return NULL;
+
+	if (!pvt) {
+		ast_cli(a->fd, "No console device is set as active\n");
+		return CLI_FAILURE;
+	}
 
 	if (a->argc != e->args)
 		return CLI_SHOWUSAGE;
@@ -763,22 +897,24 @@
 	else if (!strcasecmp(s, "unmute"))
 		pvt->muted = 0;
 	else
-		return CLI_SHOWUSAGE;
+		res = CLI_SHOWUSAGE;
 
 	ast_verb(1, V_BEGIN "The Console is now %s" V_END, 
 		pvt->muted ? "Muted" : "Unmuted");
 
-	return CLI_SUCCESS;
-}
-
-static char *cli_list_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+	unref_pvt(pvt);
+
+	return res;
+}
+
+static char *cli_list_available(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 	PaDeviceIndex index, num, def_input, def_output;
 
 	if (cmd == CLI_INIT) {
-		e->command = "console list devices";
+		e->command = "console list available";
 		e->usage =
-			"Usage: console list devices\n"
+			"Usage: console list available\n"
 			"       List all available devices.\n";
 		return NULL;
 	} else if (cmd == CLI_GENERATE)
@@ -825,7 +961,7 @@
 static char *cli_console_answer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 	struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
-	struct console_pvt *pvt = &console_pvt;
+	struct console_pvt *pvt = ref_pvt(active_pvt);
 
 	switch (cmd) {
 	case CLI_INIT:
@@ -839,11 +975,19 @@
 		return NULL;	/* no completion */
 	}
 
-	if (a->argc != e->args)
+	if (!pvt) {
+		ast_cli(a->fd, "No console device is set as active\n");
+		return CLI_FAILURE;
+	}
+
+	if (a->argc != e->args) {
+		unref_pvt(pvt);
 		return CLI_SHOWUSAGE;
+	}
 
 	if (!pvt->owner) {
 		ast_cli(a->fd, "No one is calling us\n");
+		unref_pvt(pvt);
 		return CLI_FAILURE;
 	}
 
@@ -852,6 +996,8 @@
 	ast_indicate(pvt->owner, -1);
 
 	ast_queue_frame(pvt->owner, &f);
+
+	unref_pvt(pvt);
 
 	return CLI_SUCCESS;
 }
@@ -865,7 +1011,7 @@
 static char *cli_console_sendtext(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 	char buf[TEXT_SIZE];
-	struct console_pvt *pvt = &console_pvt;
+	struct console_pvt *pvt = ref_pvt(active_pvt);
 	struct ast_frame f = {
 		.frametype = AST_FRAME_TEXT,
 		.data = buf,
@@ -882,23 +1028,35 @@
 	} else if (cmd == CLI_GENERATE)
 		return NULL;
 
-	if (a->argc < e->args + 1)
+	if (!pvt) {
+		ast_cli(a->fd, "No console device is set as active\n");
+		return CLI_FAILURE;
+	}
+
+	if (a->argc < e->args + 1) {
+		unref_pvt(pvt);
 		return CLI_SHOWUSAGE;
+	}
 
 	if (!pvt->owner) {
 		ast_cli(a->fd, "Not in a call\n");
+		unref_pvt(pvt);
 		return CLI_FAILURE;
 	}
 
 	ast_join(buf, sizeof(buf) - 1, a->argv + e->args);
-	if (ast_strlen_zero(buf))
+	if (ast_strlen_zero(buf)) {
+		unref_pvt(pvt);
 		return CLI_SHOWUSAGE;
+	}
 
 	len = strlen(buf);
 	buf[len] = '\n';
 	f.datalen = len + 1;
 
 	ast_queue_frame(pvt->owner, &f);
+
+	unref_pvt(pvt);
 
 	return CLI_SUCCESS;
 }
@@ -911,7 +1069,7 @@
 	AST_CLI_DEFINE(cli_console_sendtext,   "Send text to a connected party"),
 	AST_CLI_DEFINE(cli_console_flash,      "Send a flash to the connected party"),
 	AST_CLI_DEFINE(cli_console_autoanswer, "Turn autoanswer on or off"),
-	AST_CLI_DEFINE(cli_list_devices,       "List available devices"),
+	AST_CLI_DEFINE(cli_list_available,     "List available devices"),
 };
 
 /*!
@@ -919,24 +1077,33 @@
  *
  * \note This function expects the pvt lock to be held.
  */
-static void set_pvt_defaults(struct console_pvt *pvt, int reload)
-{
-	if (!reload) {
-		/* This should be changed for multiple device support.  Right now,
-		 * there is no way to change the name of a device.  The default
-		 * input and output sound devices are the only ones supported. */
-		ast_string_field_set(pvt, name, "default");
-	}
-
-	ast_string_field_set(pvt, mohinterpret, "default");
-	ast_string_field_set(pvt, context, "default");
-	ast_string_field_set(pvt, exten, "s");
-	ast_string_field_set(pvt, language, "");
-	ast_string_field_set(pvt, cid_num, "");
-	ast_string_field_set(pvt, cid_name, "");
-
-	pvt->overridecontext = 0;
-	pvt->autoanswer = 0;
+static void set_pvt_defaults(struct console_pvt *pvt)
+{
+	if (pvt == &globals) {
+		ast_string_field_set(pvt, mohinterpret, "default");
+		ast_string_field_set(pvt, context, "default");
+		ast_string_field_set(pvt, exten, "s");
+		ast_string_field_set(pvt, language, "");
+		ast_string_field_set(pvt, cid_num, "");
+		ast_string_field_set(pvt, cid_name, "");
+	
+		pvt->overridecontext = 0;
+		pvt->autoanswer = 0;
+	} else {
+		ast_mutex_lock(&globals_lock);
+
+		ast_string_field_set(pvt, mohinterpret, globals.mohinterpret);
+		ast_string_field_set(pvt, context, globals.context);
+		ast_string_field_set(pvt, exten, globals.exten);
+		ast_string_field_set(pvt, language, globals.language);
+		ast_string_field_set(pvt, cid_num, globals.cid_num);
+		ast_string_field_set(pvt, cid_name, globals.cid_name);
+
+		pvt->overridecontext = globals.overridecontext;
+		pvt->autoanswer = globals.autoanswer;
+
+		ast_mutex_unlock(&globals_lock);
+	}
 }
 
 static void store_callerid(struct console_pvt *pvt, const char *value)
@@ -951,6 +1118,19 @@
 	ast_string_field_set(pvt, cid_num, cid_num);
 }
 
+static void set_active(struct console_pvt *pvt, const char *value)
+{
+	if (pvt == &globals) {
+		ast_log(LOG_ERROR, "active is only valid as a per-device setting\n");
+		return;
+	}
+
+	if (active_pvt)
+		unref_pvt(active_pvt);
+	
+	active_pvt = ref_pvt(pvt);
+}
+
 /*!
  * \brief Store a configuration parameter in a pvt struct
  *
@@ -958,7 +1138,7 @@
  */
 static void store_config_core(struct console_pvt *pvt, const char *var, const char *value)
 {
-	if (!ast_jb_read_conf(&global_jbconf, var, value))
+	if (pvt == &globals && !ast_jb_read_conf(&global_jbconf, var, value))
 		return;
 
 	CV_START(var, value);
@@ -970,10 +1150,85 @@
 	CV_F("callerid", store_callerid(pvt, value));
 	CV_BOOL("overridecontext", pvt->overridecontext);
 	CV_BOOL("autoanswer", pvt->autoanswer);
+
+	if (pvt != &globals) {
+		CV_F("active", set_active(pvt, value))
+		CV_STRFIELD("input_device", pvt, input_device);
+		CV_STRFIELD("output_device", pvt, output_device);
+	}
+
+	ast_log(LOG_WARNING, "Unknown option '%s'\n", var);
+
+	CV_END;
+}
+
+static void pvt_destructor(void *obj)
+{
+	struct console_pvt *pvt = obj;
+
+	ast_string_field_free_memory(pvt);
+}
+
+static int init_pvt(struct console_pvt *pvt)
+{
+	pvt->thread = AST_PTHREADT_NULL;
+
+	if (ast_string_field_init(pvt, 32))
+		return -1;
 	
-	ast_log(LOG_WARNING, "Unknown option '%s'\n", var);
-
-	CV_END;
+	return 0;
+}
+
+static void build_device(struct ast_config *cfg, const char *name)
+{
+	struct ast_variable *v;
+	struct console_pvt *pvt;
+	int new;
+
+	if ((pvt = find_pvt(name))) {
+		console_pvt_lock(pvt);
+		set_pvt_defaults(pvt);
+		pvt->destroy = 0;
+	} else {
+		if (!(pvt = ao2_alloc(sizeof(*pvt), pvt_destructor)))
+			return;
+		init_pvt(pvt);
+		set_pvt_defaults(pvt);
+		new = 1;
+	}
+
+	for (v = ast_variable_browse(cfg, name); v; v = v->next)
+		store_config_core(pvt, v->name, v->value);
+
+	if (new)
+		ao2_link(pvts, pvt);
+	else
+		console_pvt_unlock(pvt);
+	
+	unref_pvt(pvt);
+}
+
+static int pvt_mark_destroy_cb(void *obj, void *arg, int flags)
+{
+	struct console_pvt *pvt = obj;
+	pvt->destroy = 1;
+	return 0;
+}
+
+static void destroy_pvts(void)
+{
+	struct ao2_iterator i;
+	struct console_pvt *pvt;
+
+	i = ao2_iterator_init(pvts, 0);
+	while ((pvt = ao2_iterator_next(&i))) {
+		if (pvt->destroy) {
+			ao2_unlink(pvts, pvt);
+			if (active_pvt == pvt)
+				active_pvt = unref_pvt(pvt);
+		}
+		unref_pvt(pvt);
+	}
 }
 
 /*!
@@ -986,67 +1241,77 @@
 {
 	struct ast_config *cfg;
 	struct ast_variable *v;
-	struct console_pvt *pvt = &console_pvt;
 	struct ast_flags config_flags = { 0 };
-	int res = -1;
+	char *context = NULL;
 
 	/* default values */
 	memcpy(&global_jbconf, &default_jbconf, sizeof(global_jbconf));
-
-	console_pvt_lock(pvt);
-
-	set_pvt_defaults(pvt, reload);
+	ast_mutex_lock(&globals_lock);
+	set_pvt_defaults(&globals);
+	ast_mutex_unlock(&globals_lock);
 
 	if (!(cfg = ast_config_load(config_file, config_flags))) {
 		ast_log(LOG_NOTICE, "Unable to open configuration file %s!\n", config_file);
-		goto return_unlock;
-	}
-
+		return -1;
+	}
+	
+	ao2_callback(pvts, 0, pvt_mark_destroy_cb, NULL);
+
+	ast_mutex_lock(&globals_lock);
 	for (v = ast_variable_browse(cfg, "general"); v; v = v->next)
-		store_config_core(pvt, v->name, v->value);
+		store_config_core(&globals, v->name, v->value);
+	ast_mutex_unlock(&globals_lock);
+
+	while ((context = ast_category_browse(cfg, context)))
+		build_device(cfg, context);
 
 	ast_config_destroy(cfg);
 
-	res = 0;
-
-return_unlock:
-	console_pvt_unlock(pvt);
-	return res;
-}
-
-static int init_pvt(struct console_pvt *pvt)
-{
-	if (ast_string_field_init(pvt, 32))
-		return -1;
-	
-	if (ast_mutex_init(&pvt->__lock)) {
-		ast_log(LOG_ERROR, "Failed to initialize mutex\n");
-		return -1;
-	}
+	destroy_pvts();
 
 	return 0;
 }
 
-static void destroy_pvt(struct console_pvt *pvt)
-{
-	ast_string_field_free_memory(pvt);
-	
-	ast_mutex_destroy(&pvt->__lock);
+static int pvt_hash_cb(const void *obj, const int flags)
+{
+	const struct console_pvt *pvt = obj;
+
+	return ast_str_hash(pvt->name);
+}
+
+static int pvt_cmp_cb(void *obj, void *arg, int flags)
+{
+	struct console_pvt *pvt = obj, *pvt2 = arg;
+
+	return !strcasecmp(pvt->name, pvt2->name) ? CMP_MATCH : 0;
+}
+
+static void stop_streams(void)
+{
+	struct console_pvt *pvt;
+	struct ao2_iterator i;
+
+	i = ao2_iterator_init(pvts, 0);
+	while ((pvt = ao2_iterator_next(&i))) {
+		if (pvt->hookstate)
+			stop_stream(pvt);
+		unref_pvt(pvt);
+	}
 }
 
 static int unload_module(void)
 {
-	struct console_pvt *pvt = &console_pvt;
-
-	if (pvt->hookstate)
-		stop_stream(pvt);
-
-	Pa_Terminate();
-
 	ast_channel_unregister(&console_tech);
 	ast_cli_unregister_multiple(cli_console, ARRAY_LEN(cli_console));
 
-	destroy_pvt(pvt);
+	stop_streams();
+
+	Pa_Terminate();
+
+	/* Will unref all the pvts so they will get destroyed, too */
+	ao2_ref(pvts, -1);
+
+	pvt_destructor(&globals);
 
 	return 0;
 }
@@ -1054,9 +1319,10 @@
 static int load_module(void)
 {
 	PaError res;
-	struct console_pvt *pvt = &console_pvt;
-
-	if (init_pvt(pvt))
+
+	init_pvt(&globals);
+
+	if (!(pvts = ao2_container_alloc(NUM_PVT_BUCKETS, pvt_hash_cb, pvt_cmp_cb)))
 		goto return_error;
 
 	if (load_config(0))
@@ -1086,7 +1352,9 @@
 return_error_pa_init:
 	Pa_Terminate();
 return_error:
-	destroy_pvt(pvt);
+	if (pvts)
+		ao2_ref(pvts, -1);
+	pvt_destructor(&globals);
 
 	return AST_MODULE_LOAD_DECLINE;
 }

Modified: team/russell/console_devices/configs/console.conf.sample
URL: http://svn.digium.com/view/asterisk/team/russell/console_devices/configs/console.conf.sample?view=diff&rev=99223&r1=99222&r2=99223
==============================================================================
--- team/russell/console_devices/configs/console.conf.sample (original)
+++ team/russell/console_devices/configs/console.conf.sample Sat Jan 19 22:31:57 2008
@@ -33,7 +33,7 @@
 ; to dial SIP, IAX and other extensions which use the '@' character.
 ; The default is "no".
 ;
-;overridecontext = no	; if 'no', the last @ will start the context
+;overridecontext = no    ; if 'no', the last @ will start the context
                         ; if 'yes' the whole string is an extension.
 
 
@@ -66,3 +66,25 @@
 
 ; jblog = no                  ; Enables jitterbuffer frame logging. Defaults to "no".
 ;-----------------------------------------------------------------------------------
+
+
+;
+; Any configuration context defined beyond the [general] section configures
+; specific devices for use.
+;
+
+[default]
+input_device = default       ; When configuring an input device and output device,
+output_device = default      ; use the name that you see when you run the "console
+                             ; list devices" CLI command.  If you say "default", the
+                             ; system default input and output devices will be used.
+autoanswer = no
+context = default
+extension = s
+callerid = MyName Here <(256) 428-6000>
+language = en
+overridecontext = no
+mohinterpret = default
+active = yes                 ; This option should only be set for one console.
+                             ; It means that it is the active console to be
+                             ; used from the Asterisk CLI.




More information about the asterisk-commits mailing list