menuselect: Remove ineffective weak attribute detection.
[asterisk/asterisk.git] / channels / chan_console.c
index 830e890..a24a6c8 100644 (file)
@@ -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@digium.com>
  *
  * 
  * \ingroup channel_drivers
  *
+ * Portaudio http://www.portaudio.com/
+ *
+ * To install portaudio v19 from svn, check it out using the following command:
+ *  - svn co https://www.portaudio.com/repos/portaudio/branches/v19-devel
+ *
  * \note Since this works with any audio system that libportaudio supports,
  * including ALSA and OSS, this may someday deprecate chan_alsa and chan_oss.
  * However, before that can be done, it needs to *at least* have all of the
  * in at least one of the other console channel drivers that are not yet
  * implemented here are:
  *
- * - Multiple device support
- *   - with "active" CLI command
  * - Set Auto-answer from the dialplan
  * - transfer CLI command
  * - boost CLI command and .conf option
  * - console_video support
  */
 
+/*! \li \ref chan_console.c uses the configuration file \ref console.conf
+ * \addtogroup configuration_file
+ */
+
+/*! \page console.conf console.conf
+ * \verbinclude console.conf.sample
+ */
+
 /*** MODULEINFO
        <depend>portaudio</depend>
+       <support_level>extended</support_level>
  ***/
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <sys/signal.h>  /* SIGURG */
+#include <signal.h>  /* SIGURG */
 
 #include <portaudio.h>
 
@@ -63,6 +73,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/cli.h"
 #include "asterisk/musiconhold.h"
 #include "asterisk/callerid.h"
+#include "asterisk/astobj2.h"
+#include "asterisk/stasis_channels.h"
+#include "asterisk/format_cache.h"
 
 /*! 
  * \brief The sample rate to request from PortAudio 
@@ -97,13 +110,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  */
 #define TEXT_SIZE      256
 
-#ifndef MIN
-#define MIN(a,b) ((a) < (b) ? (a) : (b))
-#endif
-#ifndef MAX
-#define MAX(a,b) ((a) > (b) ? (a) : (b))
-#endif
-
 /*! \brief Dance, Kirby, Dance! @{ */
 #define V_BEGIN " --- <(\"<) --- "
 #define V_END   " --- (>\")> ---\n"
@@ -121,6 +127,8 @@ static struct console_pvt {
        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 */
@@ -135,6 +143,8 @@ static struct console_pvt {
                AST_STRING_FIELD(mohinterpret);
                /*! Default language */
                AST_STRING_FIELD(language);
+               /*! Default parkinglot */
+               AST_STRING_FIELD(parkinglot);
        );
        /*! Current channel for this device */
        struct ast_channel *owner;
@@ -152,53 +162,55 @@ static struct console_pvt {
        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;
+AST_RWLOCK_DEFINE_STATIC(active_lock);
 
 /*! 
  * \brief Global jitterbuffer configuration 
  *
  * \note Disabled by default.
+ * \note Values shown here match the defaults shown in console.conf.sample
  */
 static struct ast_jb_conf default_jbconf = {
        .flags = 0,
-       .max_size = -1,
-       .resync_threshold = -1,
-       .impl = ""
+       .max_size = 200,
+       .resync_threshold = 1000,
+       .impl = "fixed",
+       .target_extra = 40,
 };
 static struct ast_jb_conf global_jbconf;
 
 /*! Channel Technology Callbacks @{ */
-static struct ast_channel *console_request(const char *type, int format, 
-       void *data, int *cause);
+static struct ast_channel *console_request(const char *type, struct ast_format_cap *cap,
+       const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause);
 static int console_digit_begin(struct ast_channel *c, char digit);
 static int console_digit_end(struct ast_channel *c, char digit, unsigned int duration);
 static int console_text(struct ast_channel *c, const char *text);
 static int console_hangup(struct ast_channel *c);
 static int console_answer(struct ast_channel *c);
 static struct ast_frame *console_read(struct ast_channel *chan);
-static int console_call(struct ast_channel *c, char *dest, int timeout);
+static int console_call(struct ast_channel *c, const char *dest, int timeout);
 static int console_write(struct ast_channel *chan, struct ast_frame *f);
 static int console_indicate(struct ast_channel *chan, int cond, 
        const void *data, size_t datalen);
 static int console_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
 /*! @} */
 
-/*!
- * \brief Formats natively supported by this module.
- */
-#define SUPPORTED_FORMATS ( AST_FORMAT_SLINEAR16 )
-
-static const struct ast_channel_tech console_tech = {
+static struct ast_channel_tech console_tech = {
        .type = "Console",
        .description = "Console Channel Driver",
-       .capabilities = SUPPORTED_FORMATS,
        .requester = console_request,
        .send_digit_begin = console_digit_begin,
        .send_digit_end = console_digit_end,
@@ -213,10 +225,32 @@ static const struct ast_channel_tech console_tech = {
 };
 
 /*! \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 
@@ -235,9 +269,9 @@ static void *stream_monitor(void *data)
        PaError res;
        struct ast_frame f = {
                .frametype = AST_FRAME_VOICE,
-               .subclass = AST_FORMAT_SLINEAR16,
+               .subclass.format = ast_format_slin16,
                .src = "console_stream_monitor",
-               .data = buf,
+               .data.ptr = buf,
                .datalen = sizeof(buf),
                .samples = sizeof(buf) / sizeof(int16_t),
        };
@@ -247,6 +281,10 @@ static void *stream_monitor(void *data)
                res = Pa_ReadStream(pvt->stream, buf, sizeof(buf) / sizeof(int16_t));
                pthread_testcancel();
 
+               if (!pvt->owner) {
+                       return NULL;
+               }
+
                if (res == paNoError)
                        ast_queue_frame(pvt->owner, &f);
        }
@@ -254,6 +292,67 @@ static void *stream_monitor(void *data)
        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 idx, num_devices, def_input, def_output;
+
+               if (!(num_devices = Pa_GetDeviceCount()))
+                       return res;
+
+               def_input = Pa_GetDefaultInputDevice();
+               def_output = Pa_GetDefaultOutputDevice();
+
+               for (idx = 0; 
+                       idx < num_devices && (input_params.device == paNoDevice 
+                               || output_params.device == paNoDevice); 
+                       idx++) 
+               {
+                       const PaDeviceInfo *dev = Pa_GetDeviceInfo(idx);
+
+                       if (dev->maxInputChannels) {
+                               if ( (idx == def_input && !strcasecmp(pvt->input_device, "default")) ||
+                                       !strcasecmp(pvt->input_device, dev->name) )
+                                       input_params.device = idx;
+                       }
+
+                       if (dev->maxOutputChannels) {
+                               if ( (idx == def_output && !strcasecmp(pvt->output_device, "default")) ||
+                                       !strcasecmp(pvt->output_device, dev->name) )
+                                       output_params.device = idx;
+                       }
+               }
+
+               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;
@@ -261,16 +360,18 @@ static int start_stream(struct console_pvt *pvt)
 
        console_pvt_lock(pvt);
 
-       if (pvt->streamstate)
+       /* It is possible for console_hangup to be called before the
+        * stream is started, if this is the case pvt->owner will be NULL
+        * and start_stream should be aborted. */
+       if (pvt->streamstate || !pvt->owner)
                goto return_unlock;
 
        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",
+               ast_log(LOG_WARNING, "Failed to open stream - (%d) %s\n",
                        res, Pa_GetErrorText(res));
                ret_val = -1;
                goto return_unlock;
@@ -297,7 +398,7 @@ return_unlock:
 
 static int stop_stream(struct console_pvt *pvt)
 {
-       if (!pvt->streamstate)
+       if (!pvt->streamstate || pvt->thread == AST_PTHREADT_NULL)
                return 0;
 
        pthread_cancel(pvt->thread);
@@ -317,31 +418,45 @@ static int stop_stream(struct console_pvt *pvt)
 /*!
  * \note Called with the pvt struct locked
  */
-static struct ast_channel *console_new(struct console_pvt *pvt, const char *ext, const char *ctx, int state)
+static struct ast_channel *console_new(struct console_pvt *pvt, const char *ext, const char *ctx, int state, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor)
 {
+       struct ast_format_cap *caps;
        struct ast_channel *chan;
 
+       caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+       if (!caps) {
+               return NULL;
+       }
+
        if (!(chan = ast_channel_alloc(1, state, pvt->cid_num, pvt->cid_name, NULL, 
-               ext, ctx, 0, "Console/%s", pvt->name))) {
+               ext, ctx, assignedids, requestor, 0, "Console/%s", pvt->name))) {
+               ao2_ref(caps, -1);
                return NULL;
        }
 
-       chan->tech = &console_tech;
-       chan->nativeformats = AST_FORMAT_SLINEAR16;
-       chan->readformat = AST_FORMAT_SLINEAR16;
-       chan->writeformat = AST_FORMAT_SLINEAR16;
-       chan->tech_pvt = pvt;
+       ast_channel_stage_snapshot(chan);
+
+       ast_channel_tech_set(chan, &console_tech);
+       ast_channel_set_readformat(chan, ast_format_slin16);
+       ast_channel_set_writeformat(chan, ast_format_slin16);
+       ast_format_cap_append(caps, ast_format_slin16, 0);
+       ast_channel_nativeformats_set(chan, caps);
+       ao2_ref(caps, -1);
+       ast_channel_tech_pvt_set(chan, ref_pvt(pvt));
 
        pvt->owner = chan;
 
        if (!ast_strlen_zero(pvt->language))
-               ast_string_field_set(chan, language, pvt->language);
+               ast_channel_language_set(chan, pvt->language);
 
        ast_jb_configure(chan, &global_jbconf);
 
+       ast_channel_stage_snapshot_done(chan);
+       ast_channel_unlock(chan);
+
        if (state != AST_STATE_DOWN) {
                if (ast_pbx_start(chan)) {
-                       chan->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
+                       ast_channel_hangupcause_set(chan, AST_CAUSE_SWITCH_CONGESTION);
                        ast_hangup(chan);
                        chan = NULL;
                } else
@@ -351,31 +466,39 @@ static struct ast_channel *console_new(struct console_pvt *pvt, const char *ext,
        return chan;
 }
 
-static struct ast_channel *console_request(const char *type, int format, void *data, int *cause)
+static struct ast_channel *console_request(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *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;
 
-       format &= SUPPORTED_FORMATS;
-       if (!format) {
-               ast_log(LOG_NOTICE, "Channel requested with unsupported format(s): '%d'\n", oldformat);
+       if (!(pvt = find_pvt(data))) {
+               ast_log(LOG_ERROR, "Console device '%s' not found\n", data);
                return NULL;
        }
 
+       if (!(ast_format_cap_iscompatible(cap, console_tech.capabilities))) {
+               struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+               ast_log(LOG_NOTICE, "Channel requested with unsupported format(s): '%s'\n",
+                       ast_format_cap_get_names(cap, &cap_buf));
+               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);
-       chan = console_new(pvt, NULL, NULL, AST_STATE_DOWN);
+       chan = console_new(pvt, NULL, NULL, AST_STATE_DOWN, assignedids, requestor);
        console_pvt_unlock(pvt);
 
        if (!chan)
                ast_log(LOG_WARNING, "Unable to create new Console channel!\n");
 
+return_unref:
+       unref_pvt(pvt);
+
        return chan;
 }
 
@@ -403,22 +526,22 @@ static int console_text(struct ast_channel *c, const char *text)
 
 static int console_hangup(struct ast_channel *c)
 {
-       struct console_pvt *pvt = &console_pvt;
+       struct console_pvt *pvt = ast_channel_tech_pvt(c);
 
        ast_verb(1, V_BEGIN "Hangup on Console" V_END);
 
        pvt->hookstate = 0;
-       c->tech_pvt = NULL;
        pvt->owner = NULL;
-
        stop_stream(pvt);
 
+       ast_channel_tech_pvt_set(c, unref_pvt(pvt));
+
        return 0;
 }
 
 static int console_answer(struct ast_channel *c)
 {
-       struct console_pvt *pvt = &console_pvt;
+       struct console_pvt *pvt = ast_channel_tech_pvt(c);
 
        ast_verb(1, V_BEGIN "Call from Console has been Answered" V_END);
 
@@ -454,59 +577,63 @@ static struct ast_frame *console_read(struct ast_channel *chan)
        return &ast_null_frame;
 }
 
-static int console_call(struct ast_channel *c, char *dest, int timeout)
+static int console_call(struct ast_channel *c, const char *dest, int timeout)
 {
-       struct ast_frame f = { 0, };
-       struct console_pvt *pvt = &console_pvt;
+       struct console_pvt *pvt = ast_channel_tech_pvt(c);
+       enum ast_control_frame_type ctrl;
 
        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);
+               dest,
+               S_COR(ast_channel_caller(c)->id.name.valid, ast_channel_caller(c)->id.name.str, ""),
+               S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, ""));
 
        console_pvt_lock(pvt);
 
        if (pvt->autoanswer) {
-               ast_verb(1, V_BEGIN "Auto-answered" V_END);
                pvt->hookstate = 1;
-               f.frametype = AST_FRAME_CONTROL;
-               f.subclass = AST_CONTROL_ANSWER;
+               console_pvt_unlock(pvt);
+               ast_verb(1, V_BEGIN "Auto-answered" V_END);
+               ctrl = AST_CONTROL_ANSWER;
        } else {
+               console_pvt_unlock(pvt);
                ast_verb(1, V_BEGIN "Type 'console answer' to answer, or use the 'autoanswer' option "
                                "for future calls" V_END);
-               f.frametype = AST_FRAME_CONTROL;
-               f.subclass = AST_CONTROL_RINGING;
+               ctrl = AST_CONTROL_RINGING;
+               ast_indicate(c, AST_CONTROL_RINGING);
        }
 
-       console_pvt_unlock(pvt);
-
-       ast_queue_frame(c, &f);
+       ast_queue_control(c, ctrl);
 
        return start_stream(pvt);
 }
 
 static int console_write(struct ast_channel *chan, struct ast_frame *f)
 {
-       struct console_pvt *pvt = &console_pvt;
+       struct console_pvt *pvt = ast_channel_tech_pvt(chan);
 
-       Pa_WriteStream(pvt->stream, f->data, f->samples);
+       Pa_WriteStream(pvt->stream, f->data.ptr, f->samples);
 
        return 0;
 }
 
 static int console_indicate(struct ast_channel *chan, int cond, const void *data, size_t datalen)
 {
-       struct console_pvt *pvt = chan->tech_pvt;
+       struct console_pvt *pvt = ast_channel_tech_pvt(chan);
        int res = 0;
 
        switch (cond) {
        case AST_CONTROL_BUSY:
        case AST_CONTROL_CONGESTION:
        case AST_CONTROL_RINGING:
+       case AST_CONTROL_INCOMPLETE:
+       case AST_CONTROL_PVT_CAUSE_CODE:
+       case -1:
                res = -1;  /* Ask for inband indications */
                break;
        case AST_CONTROL_PROGRESS:
        case AST_CONTROL_PROCEEDING:
        case AST_CONTROL_VIDUPDATE:
-       case -1:
+       case AST_CONTROL_SRCUPDATE:
                break;
        case AST_CONTROL_HOLD:
                ast_verb(1, V_BEGIN "Console Has Been Placed on Hold" V_END);
@@ -518,7 +645,7 @@ static int console_indicate(struct ast_channel *chan, int cond, const void *data
                break;
        default:
                ast_log(LOG_WARNING, "Don't know how to display condition %d on %s\n", 
-                       cond, chan->name);
+                       cond, ast_channel_name(chan));
                /* The core will play inband indications for us if appropriate */
                res = -1;
        }
@@ -528,7 +655,7 @@ static int console_indicate(struct ast_channel *chan, int cond, const void *data
 
 static int console_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
 {
-       struct console_pvt *pvt = &console_pvt;
+       struct console_pvt *pvt = ast_channel_tech_pvt(newchan);
 
        pvt->owner = newchan;
 
@@ -569,16 +696,28 @@ static char *ast_ext_ctx(struct console_pvt *pvt, const char *src, char **ext, c
        return *ext;
 }
 
+static struct console_pvt *get_active_pvt(void)
+{
+       struct console_pvt *pvt;
+
+       ast_rwlock_rdlock(&active_lock);
+       pvt = ref_pvt(active_pvt);      
+       ast_rwlock_unlock(&active_lock);
+
+       return pvt;
+}
+
 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;
+       char *res = CLI_SUCCESS;
 
        switch (cmd) {
        case CLI_INIT:
-               e->command = "console set autoanswer [on|off]";
+               e->command = "console {set|show} autoanswer [on|off]";
                e->usage =
-                       "Usage: console set autoanswer [on|off]\n"
+                       "Usage: console {set|show} autoanswer [on|off]\n"
                        "       Enables or disables autoanswer feature.  If used without\n"
                        "       argument, displays the current on/off status of autoanswer.\n"
                        "       The default value of autoanswer is in 'oss.conf'.\n";
@@ -588,18 +727,21 @@ static char *cli_console_autoanswer(struct ast_cli_entry *e, int cmd,
                return NULL;
        }
 
+       pvt = get_active_pvt();
+       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"))
@@ -607,15 +749,16 @@ static char *cli_console_autoanswer(struct ast_cli_entry *e, int cmd,
        else if (!strcasecmp(a->argv[e->args - 1], "off"))
                pvt->autoanswer = 0;
        else
-               return CLI_SHOWUSAGE;
+               res = CLI_SHOWUSAGE;
 
-       return CLI_SUCCESS;
+       unref_pvt(pvt);
+
+       return res;
 }
 
 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;
 
        if (cmd == CLI_INIT) {
                e->command = "console flash";
@@ -623,20 +766,31 @@ static char *cli_console_flash(struct ast_cli_entry *e, int cmd, struct ast_cli_
                        "Usage: console flash\n"
                        "       Flashes the call currently placed on the console.\n";
                return NULL;
-       } else if (cmd == CLI_GENERATE)
+       } else if (cmd == CLI_GENERATE) {
                return NULL;
+       }
 
-       if (a->argc != e->args)
+       if (a->argc != e->args) {
                return CLI_SHOWUSAGE;
+       }
+
+       pvt = get_active_pvt();
+       if (!pvt) {
+               ast_cli(a->fd, "No console device is set as active\n");
+               return CLI_FAILURE;
+       }
 
        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);
+       ast_queue_control(pvt->owner, AST_CONTROL_FLASH);
+
+       unref_pvt(pvt);
 
        return CLI_SUCCESS;
 }
@@ -645,7 +799,7 @@ static char *cli_console_dial(struct ast_cli_entry *e, int cmd, struct ast_cli_a
 {
        char *s = NULL;
        const char *mye = NULL, *myc = NULL; 
-       struct console_pvt *pvt = &console_pvt;
+       struct console_pvt *pvt;
 
        if (cmd == CLI_INIT) {
                e->command = "console dial";
@@ -653,26 +807,37 @@ static char *cli_console_dial(struct ast_cli_entry *e, int cmd, struct ast_cli_a
                        "Usage: console dial [extension[@context]]\n"
                        "       Dials a given extension (and context if specified)\n";
                return NULL;
-       } else if (cmd == CLI_GENERATE)
+       } else if (cmd == CLI_GENERATE) {
                return NULL;
+       }
 
-       if (a->argc > e->args + 1)
+       if (a->argc > e->args + 1) {
                return CLI_SHOWUSAGE;
+       }
+
+       pvt = get_active_pvt();
+       if (!pvt) {
+               ast_cli(a->fd, "No console device is currently set as active\n");
+               return CLI_FAILURE;
+       }
 
        if (pvt->owner) {       /* already in a call */
                int i;
-               struct ast_frame f = { AST_FRAME_DTMF, 0 };
+               struct ast_frame f = { AST_FRAME_DTMF };
+               const char *s;
 
                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];
                /* send the string one char at a time */
                for (i = 0; i < strlen(s); i++) {
-                       f.subclass = s[i];
+                       f.subclass.integer = s[i];
                        ast_queue_frame(pvt->owner, &f);
                }
+               unref_pvt(pvt);
                return CLI_SUCCESS;
        }
 
@@ -695,20 +860,21 @@ static char *cli_console_dial(struct ast_cli_entry *e, int cmd, struct ast_cli_a
        if (ast_exists_extension(NULL, myc, mye, 1, NULL)) {
                console_pvt_lock(pvt);
                pvt->hookstate = 1;
-               console_new(pvt, mye, myc, AST_STATE_RINGING);
+               console_new(pvt, mye, myc, AST_STATE_RINGING, NULL, NULL);
                console_pvt_unlock(pvt);
        } else
                ast_cli(a->fd, "No such extension '%s' in context '%s'\n", mye, myc);
 
-       if (s)
-               free(s);
+       ast_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;
 
        if (cmd == CLI_INIT) {
                e->command = "console hangup";
@@ -716,14 +882,23 @@ static char *cli_console_hangup(struct ast_cli_entry *e, int cmd, struct ast_cli
                        "Usage: console hangup\n"
                        "       Hangs up any call currently placed on the console.\n";
                return NULL;
-       } else if (cmd == CLI_GENERATE)
+       } else if (cmd == CLI_GENERATE) {
                return NULL;
+       }
 
-       if (a->argc != e->args)
+       if (a->argc != e->args) {
                return CLI_SHOWUSAGE;
+       }
+
+       pvt = get_active_pvt();
+       if (!pvt) {
+               ast_cli(a->fd, "No console device is set as active\n");
+               return CLI_FAILURE;
+       }
 
        if (!pvt->owner && !pvt->hookstate) {
                ast_cli(a->fd, "No call to hang up\n");
+               unref_pvt(pvt);
                return CLI_FAILURE;
        }
 
@@ -731,25 +906,36 @@ static char *cli_console_hangup(struct ast_cli_entry *e, int cmd, struct ast_cli
        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;
-       
+       const char *s;
+       struct console_pvt *pvt;
+       char *res = CLI_SUCCESS;
+
        if (cmd == CLI_INIT) {
                e->command = "console {mute|unmute}";
                e->usage =
                        "Usage: console {mute|unmute}\n"
                        "       Mute/unmute the microphone.\n";
                return NULL;
-       } else if (cmd == CLI_GENERATE)
+       } else if (cmd == CLI_GENERATE) {
                return NULL;
+       }
 
-       if (a->argc != e->args)
+       if (a->argc != e->args) {
                return CLI_SHOWUSAGE;
+       }
+
+       pvt = get_active_pvt();
+       if (!pvt) {
+               ast_cli(a->fd, "No console device is set as active\n");
+               return CLI_FAILURE;
+       }
 
        s = a->argv[e->args-1];
        if (!strcasecmp(s, "mute"))
@@ -757,22 +943,24 @@ static char *cli_console_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_a
        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;
+       unref_pvt(pvt);
+
+       return res;
 }
 
-static char *cli_list_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+static char *cli_list_available(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-       PaDeviceIndex index, num, def_input, def_output;
+       PaDeviceIndex idx, 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)
@@ -781,7 +969,11 @@ static char *cli_list_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_a
        if (a->argc != e->args)
                return CLI_SHOWUSAGE;
 
-       ast_cli(a->fd, "Available Devices:\n---------------------------------\n");
+       ast_cli(a->fd, "\n"
+                   "=============================================================\n"
+                   "=== Available Devices =======================================\n"
+                   "=============================================================\n"
+                   "===\n");
 
        num = Pa_GetDeviceCount();
        if (!num) {
@@ -791,27 +983,88 @@ static char *cli_list_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_a
 
        def_input = Pa_GetDefaultInputDevice();
        def_output = Pa_GetDefaultOutputDevice();
-       for (index = 0; index < num; index++) {
-               const PaDeviceInfo *dev = Pa_GetDeviceInfo(index);
+       for (idx = 0; idx < num; idx++) {
+               const PaDeviceInfo *dev = Pa_GetDeviceInfo(idx);
                if (!dev)
                        continue;
-               ast_cli(a->fd, "Device Name: %s\n", dev->name);
-               if (index == def_input)
-                       ast_cli(a->fd, "    ---> Default Input Device\n");
-               if (index == def_output)
-                       ast_cli(a->fd, "    ---> Default Output Device\n");
+               ast_cli(a->fd, "=== ---------------------------------------------------------\n"
+                              "=== Device Name: %s\n", dev->name);
+               if (dev->maxInputChannels)
+                       ast_cli(a->fd, "=== ---> %sInput Device\n", (idx == def_input) ? "Default " : "");
+               if (dev->maxOutputChannels)
+                       ast_cli(a->fd, "=== ---> %sOutput Device\n", (idx == def_output) ? "Default " : "");
+               ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
        }
 
+       ast_cli(a->fd, "=============================================================\n\n");
+
        return CLI_SUCCESS;
 }
 
+static char *cli_list_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       struct ao2_iterator i;
+       struct console_pvt *pvt;
+
+       if (cmd == CLI_INIT) {
+               e->command = "console list devices";
+               e->usage =
+                       "Usage: console list devices\n"
+                       "       List all configured devices.\n";
+               return NULL;
+       } else if (cmd == CLI_GENERATE)
+               return NULL;
+
+       if (a->argc != e->args)
+               return CLI_SHOWUSAGE;
+
+       ast_cli(a->fd, "\n"
+                   "=============================================================\n"
+                   "=== Configured Devices ======================================\n"
+                   "=============================================================\n"
+                   "===\n");
+
+       i = ao2_iterator_init(pvts, 0);
+       while ((pvt = ao2_iterator_next(&i))) {
+               console_pvt_lock(pvt);
+
+               ast_cli(a->fd, "=== ---------------------------------------------------------\n"
+                              "=== Device Name: %s\n"
+                              "=== ---> Active:           %s\n"
+                              "=== ---> Input Device:     %s\n"
+                              "=== ---> Output Device:    %s\n"
+                              "=== ---> Context:          %s\n"
+                              "=== ---> Extension:        %s\n"
+                              "=== ---> CallerID Num:     %s\n"
+                              "=== ---> CallerID Name:    %s\n"
+                              "=== ---> MOH Interpret:    %s\n"
+                              "=== ---> Language:         %s\n"
+                              "=== ---> Parkinglot:       %s\n"
+                              "=== ---> Muted:            %s\n"
+                              "=== ---> Auto-Answer:      %s\n"
+                              "=== ---> Override Context: %s\n"
+                              "=== ---------------------------------------------------------\n===\n",
+                       pvt->name, (pvt == active_pvt) ? "Yes" : "No",
+                       pvt->input_device, pvt->output_device, pvt->context,
+                       pvt->exten, pvt->cid_num, pvt->cid_name, pvt->mohinterpret,
+                       pvt->language, pvt->parkinglot, pvt->muted ? "Yes" : "No", pvt->autoanswer ? "Yes" : "No",
+                       pvt->overridecontext ? "Yes" : "No");
+
+               console_pvt_unlock(pvt);
+               unref_pvt(pvt);
+       }
+       ao2_iterator_destroy(&i);
+
+       ast_cli(a->fd, "=============================================================\n\n");
+
+       return CLI_SUCCESS;
+}
 /*!
  * \brief answer command from the console
  */
 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;
 
        switch (cmd) {
        case CLI_INIT:
@@ -825,16 +1078,30 @@ static char *cli_console_answer(struct ast_cli_entry *e, int cmd, struct ast_cli
                return NULL;    /* no completion */
        }
 
-       if (a->argc != e->args)
+       pvt = get_active_pvt();
+       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;
        }
 
        pvt->hookstate = 1;
-       ast_queue_frame(pvt->owner, &f);
+
+       ast_indicate(pvt->owner, -1);
+
+       ast_queue_control(pvt->owner, AST_CONTROL_ANSWER);
+
+       unref_pvt(pvt);
 
        return CLI_SUCCESS;
 }
@@ -848,10 +1115,10 @@ static char *cli_console_answer(struct ast_cli_entry *e, int cmd, struct ast_cli
 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;
        struct ast_frame f = {
                .frametype = AST_FRAME_TEXT,
-               .data = buf,
+               .data.ptr = buf,
                .src = "console_send_text",
        };
        int len;
@@ -862,20 +1129,32 @@ static char *cli_console_sendtext(struct ast_cli_entry *e, int cmd, struct ast_c
                        "Usage: console send text <message>\n"
                        "       Sends a text message for display on the remote terminal.\n";
                return NULL;
-       } else if (cmd == CLI_GENERATE)
+       } else if (cmd == CLI_GENERATE) {
                return NULL;
+       }
+
+       pvt = get_active_pvt();
+       if (!pvt) {
+               ast_cli(a->fd, "No console device is set as active\n");
+               return CLI_FAILURE;
+       }
 
-       if (a->argc < e->args + 1)
+       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';
@@ -883,6 +1162,90 @@ static char *cli_console_sendtext(struct ast_cli_entry *e, int cmd, struct ast_c
 
        ast_queue_frame(pvt->owner, &f);
 
+       unref_pvt(pvt);
+
+       return CLI_SUCCESS;
+}
+
+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 (!ast_true(value))
+               return;
+
+       ast_rwlock_wrlock(&active_lock);
+       if (active_pvt)
+               unref_pvt(active_pvt);
+       active_pvt = ref_pvt(pvt);
+       ast_rwlock_unlock(&active_lock);
+}
+
+static char *cli_console_active(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       struct console_pvt *pvt;
+
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "console {set|show} active";
+               e->usage =
+                       "Usage: console {set|show} active [<device>]\n"
+                       "       Set or show the active console device for the Asterisk CLI.\n";
+               return NULL;
+       case CLI_GENERATE:
+               if (a->pos == e->args) {
+                       struct ao2_iterator i;
+                       int x = 0;
+                       char *res = NULL;
+                       i = ao2_iterator_init(pvts, 0);
+                       while ((pvt = ao2_iterator_next(&i))) {
+                               if (++x > a->n && !strncasecmp(pvt->name, a->word, strlen(a->word)))
+                                       res = ast_strdup(pvt->name);
+                               unref_pvt(pvt);
+                               if (res) {
+                                       ao2_iterator_destroy(&i);
+                                       return res;
+                               }
+                       }
+                       ao2_iterator_destroy(&i);
+               }
+               return NULL;
+       }
+
+       if (a->argc < e->args)
+               return CLI_SHOWUSAGE;
+
+       if (a->argc == 3) {
+               pvt = get_active_pvt();
+
+               if (!pvt)
+                       ast_cli(a->fd, "No device is currently set as the active console device.\n");
+               else {
+                       console_pvt_lock(pvt);
+                       ast_cli(a->fd, "The active console device is '%s'.\n", pvt->name);
+                       console_pvt_unlock(pvt);
+                       pvt = unref_pvt(pvt);
+               }
+
+               return CLI_SUCCESS;
+       }
+
+       if (!(pvt = find_pvt(a->argv[e->args - 1]))) {
+               ast_cli(a->fd, "Could not find a device called '%s'.\n", a->argv[e->args]);
+               return CLI_FAILURE;
+       }
+
+       set_active(pvt, "yes");
+
+       console_pvt_lock(pvt);
+       ast_cli(a->fd, "The active console device has been set to '%s'\n", pvt->name);
+       console_pvt_unlock(pvt);
+
+       unref_pvt(pvt);
+
        return CLI_SUCCESS;
 }
 
@@ -894,7 +1257,9 @@ static struct ast_cli_entry cli_console[] = {
        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"),
+       AST_CLI_DEFINE(cli_list_devices,       "List configured devices"),
+       AST_CLI_DEFINE(cli_console_active,     "View or Set the active console device"),
 };
 
 /*!
@@ -902,24 +1267,35 @@ static struct ast_cli_entry cli_console[] = {
  *
  * \note This function expects the pvt lock to be held.
  */
-static void set_pvt_defaults(struct console_pvt *pvt, int reload)
+static void set_pvt_defaults(struct console_pvt *pvt)
 {
-       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");
-       }
+       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, "");
+               ast_string_field_set(pvt, parkinglot, "");
+       
+               pvt->overridecontext = 0;
+               pvt->autoanswer = 0;
+       } else {
+               ast_mutex_lock(&globals_lock);
 
-       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, "");
+               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);
+               ast_string_field_set(pvt, parkinglot, globals.parkinglot);
 
-       pvt->overridecontext = 0;
-       pvt->autoanswer = 0;
+               pvt->overridecontext = globals.overridecontext;
+               pvt->autoanswer = globals.autoanswer;
+
+               ast_mutex_unlock(&globals_lock);
+       }
 }
 
 static void store_callerid(struct console_pvt *pvt, const char *value)
@@ -941,7 +1317,7 @@ static void store_callerid(struct console_pvt *pvt, const char *value)
  */
 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);
@@ -953,93 +1329,208 @@ static void store_config_core(struct console_pvt *pvt, const char *var, const ch
        CV_F("callerid", store_callerid(pvt, value));
        CV_BOOL("overridecontext", pvt->overridecontext);
        CV_BOOL("autoanswer", pvt->autoanswer);
-       
+       CV_STRFIELD("parkinglot", pvt, parkinglot);
+
+       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, const char *name)
+{
+       pvt->thread = AST_PTHREADT_NULL;
+
+       if (ast_string_field_init(pvt, 32))
+               return -1;
+
+       ast_string_field_set(pvt, name, S_OR(name, ""));
+
+       return 0;
+}
+
+static void build_device(struct ast_config *cfg, const char *name)
+{
+       struct ast_variable *v;
+       struct console_pvt *pvt;
+       int new = 0;
+
+       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, name);
+               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);
+                       ast_rwlock_wrlock(&active_lock);
+                       if (active_pvt == pvt)
+                               active_pvt = unref_pvt(pvt);
+                       ast_rwlock_unlock(&active_lock);
+               }
+               unref_pvt(pvt);
+       }
+       ao2_iterator_destroy(&i);
+}
+
 /*!
  * \brief Load the configuration
  * \param reload if this was called due to a reload
- * \retval 0 succcess
+ * \retval 0 success
  * \retval -1 failure
  */
 static int load_config(int reload)
 {
        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;
+       } else if (cfg == CONFIG_STATUS_FILEINVALID) {
+               ast_log(LOG_NOTICE, "Config file %s has an invalid format\n", config_file);
+               return -1;
        }
+       
+       ao2_callback(pvts, OBJ_NODATA, 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))) {
+               if (strcasecmp(context, "general"))
+                       build_device(cfg, context);
+       }
 
        ast_config_destroy(cfg);
 
-       res = 0;
+       destroy_pvts();
 
-return_unlock:
-       console_pvt_unlock(pvt);
-       return res;
+       return 0;
 }
 
-static int init_pvt(struct console_pvt *pvt)
+static int pvt_hash_cb(const void *obj, const int flags)
 {
-       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;
-       }
+       const struct console_pvt *pvt = obj;
 
-       return 0;
+       return ast_str_case_hash(pvt->name);
 }
 
-static void destroy_pvt(struct console_pvt *pvt)
+static int pvt_cmp_cb(void *obj, void *arg, int flags)
 {
-       ast_string_field_free_memory(pvt);
-       
-       ast_mutex_destroy(&pvt->__lock);
+       struct console_pvt *pvt = obj, *pvt2 = arg;
+
+       return !strcasecmp(pvt->name, pvt2->name) ? CMP_MATCH | CMP_STOP : 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);
+       }
+       ao2_iterator_destroy(&i);
 }
 
 static int unload_module(void)
 {
-       struct console_pvt *pvt = &console_pvt;
+       ao2_ref(console_tech.capabilities, -1);
+       console_tech.capabilities = NULL;
+       ast_channel_unregister(&console_tech);
+       ast_cli_unregister_multiple(cli_console, ARRAY_LEN(cli_console));
 
-       if (pvt->hookstate)
-               stop_stream(pvt);
+       stop_streams();
 
        Pa_Terminate();
 
-       ast_channel_unregister(&console_tech);
-       ast_cli_unregister_multiple(cli_console, ARRAY_LEN(cli_console));
+       /* Will unref all the pvts so they will get destroyed, too */
+       ao2_ref(pvts, -1);
 
-       destroy_pvt(pvt);
+       pvt_destructor(&globals);
 
        return 0;
 }
 
+/*!
+ * \brief Load the module
+ *
+ * Module loading including tests for configuration or dependencies.
+ * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
+ * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
+ * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the 
+ * configuration file or other non-critical problem return 
+ * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
+ */
 static int load_module(void)
 {
        PaError res;
-       struct console_pvt *pvt = &console_pvt;
 
-       if (init_pvt(pvt))
+       if (!(console_tech.capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
+               return AST_MODULE_LOAD_DECLINE;
+       }
+       ast_format_cap_append(console_tech.capabilities, ast_format_slin16, 0);
+
+       init_pvt(&globals, NULL);
+
+       if (!(pvts = ao2_container_alloc(NUM_PVT_BUCKETS, pvt_hash_cb, pvt_cmp_cb)))
                goto return_error;
 
        if (load_config(0))
@@ -1069,7 +1560,12 @@ return_error_chan_reg:
 return_error_pa_init:
        Pa_Terminate();
 return_error:
-       destroy_pvt(pvt);
+       if (pvts)
+               ao2_ref(pvts, -1);
+       pvts = NULL;
+       ao2_ref(console_tech.capabilities, -1);
+       console_tech.capabilities = NULL;
+       pvt_destructor(&globals);
 
        return AST_MODULE_LOAD_DECLINE;
 }
@@ -1079,8 +1575,10 @@ static int reload(void)
        return load_config(1);
 }
 
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Console Channel Driver",
-               .load = load_module,
-               .unload = unload_module,
-               .reload = reload,
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Console Channel Driver",
+       .support_level = AST_MODULE_SUPPORT_EXTENDED,
+       .load = load_module,
+       .unload = unload_module,
+       .reload = reload,
+       .load_pri = AST_MODPRI_CHANNEL_DRIVER,
 );